blob: 6864c644307e17005ecf98ef077304e26da3b544 [file] [log] [blame]
Harald Welted12eab92017-08-02 19:49:47 +02001/*
2 * IPv4/v6 address functions.
3 * Copyright (C) 2017 by Harald Welte <laforge@gnumonks.org>
4 *
5 * The contents of this file may be used under the terms of the GNU
6 * General Public License Version 2, provided that the above copyright
7 * notice and this permission notice is included in all copies or
8 * substantial portions of the software.
9 *
10 */
11
12#include "../lib/in46_addr.h"
13
Harald Welte72a38b52017-08-09 21:58:12 +020014#include <osmocom/core/utils.h>
15
Harald Welted12eab92017-08-02 19:49:47 +020016#include <sys/types.h>
17#include <netinet/in.h>
18#include <sys/socket.h>
19#include <arpa/inet.h>
20#include <netdb.h>
21#include <stdlib.h>
22#include <string.h>
23
24/*! Return the address family of given \reff in46_addr argument */
25int in46a_to_af(const struct in46_addr *in)
26{
27 switch (in->len) {
28 case 4:
29 return AF_INET;
Harald Welted4d6e092017-08-08 18:10:43 +020030 case 8:
Harald Welted12eab92017-08-02 19:49:47 +020031 case 16:
32 return AF_INET6;
33 default:
Harald Welte72a38b52017-08-09 21:58:12 +020034 OSMO_ASSERT(0);
Harald Welted12eab92017-08-02 19:49:47 +020035 return -1;
36 }
37}
38
39/*! Convert \ref in46_addr to sockaddr_storage */
40int in46a_to_sas(struct sockaddr_storage *out, const struct in46_addr *in)
41{
42 struct sockaddr_in *sin = (struct sockaddr_in *)out;
43 struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)out;
44
45 switch (in->len) {
46 case 4:
47 sin->sin_family = AF_INET;
48 sin->sin_addr = in->v4;
49 break;
50 case 16:
51 sin6->sin6_family = AF_INET;
52 sin6->sin6_addr = in->v6;
53 break;
54 default:
Harald Welte72a38b52017-08-09 21:58:12 +020055 OSMO_ASSERT(0);
Harald Welted12eab92017-08-02 19:49:47 +020056 return -1;
57 }
58
59 return 0;
60}
61
62/*! Convenience wrapper around inet_ntop() for \ref in46_addr */
63const char *in46a_ntop(const struct in46_addr *in, char *dst, socklen_t dst_size)
64{
65 int af = in46a_to_af(in);
66 if (af < 0)
67 return NULL;
68
69 return inet_ntop(af, (const void *) &in->v4, dst, dst_size);
70}
71
Harald Welteb62983d2017-08-12 12:46:17 +020072/* like inet_ntoa() */
73const char *in46a_ntoa(const struct in46_addr *in46)
74{
75 static char addrstr_buf[256];
76 if (in46a_ntop(in46, addrstr_buf, sizeof(addrstr_buf)) < 0)
77 return "INVALID";
78 else
79 return addrstr_buf;
80}
81
Harald Welte7fc86942017-08-12 12:55:48 +020082const char *in46p_ntoa(const struct in46_prefix *in46p)
83{
84 static char addrstr_buf[256];
85 snprintf(addrstr_buf, sizeof(addrstr_buf), "%s/%u", in46a_ntoa(&in46p->addr), in46p->prefixlen);
86 return addrstr_buf;
87}
88
Harald Welted12eab92017-08-02 19:49:47 +020089/*! Determine if two in46_addr are equal or not
90 * \returns 1 in case they are equal; 0 otherwise */
91int in46a_equal(const struct in46_addr *a, const struct in46_addr *b)
92{
93 if (a->len == b->len && !memcmp(&a->v6, &b->v6, a->len))
94 return 1;
95 else
96 return 0;
97}
98
Harald Welte365f8fa2017-08-08 18:09:36 +020099/*! Determine if two in46_addr prefix are equal or not
100 * The prefix length is determined by the shortest of the prefixes of a and b
101 * \returns 1 in case the common prefix are equal; 0 otherwise */
102int in46a_prefix_equal(const struct in46_addr *a, const struct in46_addr *b)
103{
104 unsigned int len;
105 if (a->len > b->len)
106 len = b->len;
107 else
108 len = a->len;
109
110 if (!memcmp(&a->v6, &b->v6, len))
111 return 1;
112 else
113 return 0;
114}
115
Harald Welted12eab92017-08-02 19:49:47 +0200116/*! Match if IPv6 addr1 + addr2 are within same \a mask */
117static int ipv6_within_mask(const struct in6_addr *addr1, const struct in6_addr *addr2,
118 const struct in6_addr *mask)
119{
120 struct in6_addr masked = *addr2;
121#if defined(__linux__)
122 masked.s6_addr32[0] &= mask->s6_addr32[0];
123 masked.s6_addr32[1] &= mask->s6_addr32[1];
124 masked.s6_addr32[2] &= mask->s6_addr32[2];
125 masked.s6_addr32[3] &= mask->s6_addr32[3];
126#else
127 masked.__u6_addr.__u6_addr32[0] &= mask->__u6_addr.__u6_addr32[0];
128 masked.__u6_addr.__u6_addr32[1] &= mask->__u6_addr.__u6_addr32[1];
129 masked.__u6_addr.__u6_addr32[2] &= mask->__u6_addr.__u6_addr32[2];
130 masked.__u6_addr.__u6_addr32[3] &= mask->__u6_addr.__u6_addr32[3];
131#endif
132 if (!memcmp(addr1, &masked, sizeof(struct in6_addr)))
133 return 1;
134 else
135 return 0;
136}
137
138/*! Create an IPv6 netmask from the given prefix length */
139static void create_ipv6_netmask(struct in6_addr *netmask, int prefixlen)
140{
141 uint32_t *p_netmask;
142 memset(netmask, 0, sizeof(struct in6_addr));
143 if (prefixlen < 0)
144 prefixlen = 0;
145 else if (128 < prefixlen)
146 prefixlen = 128;
147
148#if defined(__linux__)
149 p_netmask = &netmask->s6_addr32[0];
150#else
151 p_netmask = &netmask->__u6_addr.__u6_addr32[0];
152#endif
153 while (32 < prefixlen) {
154 *p_netmask = 0xffffffff;
155 p_netmask++;
156 prefixlen -= 32;
157 }
158 if (prefixlen != 0) {
159 *p_netmask = htonl(0xFFFFFFFF << (32 - prefixlen));
160 }
161}
162
163/*! Determine if given \a addr is within given \a net + \a prefixlen
164 * Builds the netmask from \a net + \a prefixlen and matches it to \a addr
165 * \returns 1 in case of a match, 0 otherwise */
166int in46a_within_mask(const struct in46_addr *addr, const struct in46_addr *net, size_t prefixlen)
167{
168 struct in_addr netmask;
169 struct in6_addr netmask6;
170
171 if (addr->len != net->len)
172 return 0;
173
174 switch (addr->len) {
175 case 4:
176 netmask.s_addr = htonl(0xFFFFFFFF << (32 - prefixlen));
177 if ((addr->v4.s_addr & netmask.s_addr) == net->v4.s_addr)
178 return 1;
179 else
180 return 0;
181 case 16:
182 create_ipv6_netmask(&netmask6, prefixlen);
183 return ipv6_within_mask(&addr->v6, &net->v6, &netmask6);
184 default:
Harald Welte72a38b52017-08-09 21:58:12 +0200185 OSMO_ASSERT(0);
Harald Welted12eab92017-08-02 19:49:47 +0200186 return 0;
187 }
188}
Harald Weltea0d281d2017-08-02 21:48:16 +0200189
190/*! Convert given PDP End User Address to in46_addr
191 * \returns 0 on success; negative on error */
192int in46a_to_eua(const struct in46_addr *src, struct ul66_t *eua)
193{
194 switch (src->len) {
195 case 4:
196 eua->l = 6;
197 eua->v[0] = 0xf1; /* IETF */
198 eua->v[1] = 0x21; /* IPv4 */
199 memcpy(&eua->v[2], &src->v4, 4); /* Copy a 4 byte address */
200 break;
Harald Welted4d6e092017-08-08 18:10:43 +0200201 case 8:
Harald Weltea0d281d2017-08-02 21:48:16 +0200202 case 16:
203 eua->l = 18;
204 eua->v[0] = 0xf1; /* IETF */
205 eua->v[1] = 0x57; /* IPv6 */
206 memcpy(&eua->v[2], &src->v6, 16); /* Copy a 16 byte address */
207 break;
208 default:
Harald Welte72a38b52017-08-09 21:58:12 +0200209 OSMO_ASSERT(0);
Harald Weltea0d281d2017-08-02 21:48:16 +0200210 return -1;
211 }
212 return 0;
213}
214
215/*! Convert given in46_addr to PDP End User Address
216 * \returns 0 on success; negative on error */
217int in46a_from_eua(const struct ul66_t *eua, struct in46_addr *dst)
218{
219 if (eua->l < 2)
220 goto default_to_dyn_v4;
221
222 if (eua->v[0] != 0xf1)
223 return -1;
224
225 switch (eua->v[1]) {
226 case 0x21:
227 dst->len = 4;
228 if (eua->l >= 6)
229 memcpy(&dst->v4, &eua->v[2], 4); /* Copy a 4 byte address */
230 else
231 dst->v4.s_addr = 0;
232 break;
233 case 0x57:
234 dst->len = 16;
235 if (eua->l >= 18)
236 memcpy(&dst->v6, &eua->v[2], 16); /* Copy a 16 byte address */
237 else
238 memset(&dst->v6, 0, 16);
239 break;
240 default:
241 return -1;
242 }
243 return 0;
244
245default_to_dyn_v4:
246 /* assume dynamic IPv4 by default */
247 dst->len = 4;
248 dst->v4.s_addr = 0;
249 return 0;
250}