blob: 4b5fd64d0f961ace74432374783a2795891e0274 [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{
Harald Welte33520b42017-08-12 14:54:28 +020065 int af;
66
67 if (!in || in->len == 0) {
68 strncpy(dst, "UNDEFINED", dst_size);
69 return dst;
70 }
71
72 af = in46a_to_af(in);
Harald Welted12eab92017-08-02 19:49:47 +020073 if (af < 0)
74 return NULL;
75
76 return inet_ntop(af, (const void *) &in->v4, dst, dst_size);
77}
78
Harald Welteb62983d2017-08-12 12:46:17 +020079/* like inet_ntoa() */
80const char *in46a_ntoa(const struct in46_addr *in46)
81{
82 static char addrstr_buf[256];
83 if (in46a_ntop(in46, addrstr_buf, sizeof(addrstr_buf)) < 0)
84 return "INVALID";
85 else
86 return addrstr_buf;
87}
88
Harald Welte7fc86942017-08-12 12:55:48 +020089const char *in46p_ntoa(const struct in46_prefix *in46p)
90{
91 static char addrstr_buf[256];
92 snprintf(addrstr_buf, sizeof(addrstr_buf), "%s/%u", in46a_ntoa(&in46p->addr), in46p->prefixlen);
93 return addrstr_buf;
94}
95
Harald Welted12eab92017-08-02 19:49:47 +020096/*! Determine if two in46_addr are equal or not
97 * \returns 1 in case they are equal; 0 otherwise */
98int in46a_equal(const struct in46_addr *a, const struct in46_addr *b)
99{
100 if (a->len == b->len && !memcmp(&a->v6, &b->v6, a->len))
101 return 1;
102 else
103 return 0;
104}
105
Harald Welte365f8fa2017-08-08 18:09:36 +0200106/*! Determine if two in46_addr prefix are equal or not
107 * The prefix length is determined by the shortest of the prefixes of a and b
108 * \returns 1 in case the common prefix are equal; 0 otherwise */
109int in46a_prefix_equal(const struct in46_addr *a, const struct in46_addr *b)
110{
111 unsigned int len;
112 if (a->len > b->len)
113 len = b->len;
114 else
115 len = a->len;
116
117 if (!memcmp(&a->v6, &b->v6, len))
118 return 1;
119 else
120 return 0;
121}
122
Harald Welted12eab92017-08-02 19:49:47 +0200123/*! Match if IPv6 addr1 + addr2 are within same \a mask */
124static int ipv6_within_mask(const struct in6_addr *addr1, const struct in6_addr *addr2,
125 const struct in6_addr *mask)
126{
127 struct in6_addr masked = *addr2;
128#if defined(__linux__)
129 masked.s6_addr32[0] &= mask->s6_addr32[0];
130 masked.s6_addr32[1] &= mask->s6_addr32[1];
131 masked.s6_addr32[2] &= mask->s6_addr32[2];
132 masked.s6_addr32[3] &= mask->s6_addr32[3];
133#else
134 masked.__u6_addr.__u6_addr32[0] &= mask->__u6_addr.__u6_addr32[0];
135 masked.__u6_addr.__u6_addr32[1] &= mask->__u6_addr.__u6_addr32[1];
136 masked.__u6_addr.__u6_addr32[2] &= mask->__u6_addr.__u6_addr32[2];
137 masked.__u6_addr.__u6_addr32[3] &= mask->__u6_addr.__u6_addr32[3];
138#endif
139 if (!memcmp(addr1, &masked, sizeof(struct in6_addr)))
140 return 1;
141 else
142 return 0;
143}
144
145/*! Create an IPv6 netmask from the given prefix length */
146static void create_ipv6_netmask(struct in6_addr *netmask, int prefixlen)
147{
148 uint32_t *p_netmask;
149 memset(netmask, 0, sizeof(struct in6_addr));
150 if (prefixlen < 0)
151 prefixlen = 0;
152 else if (128 < prefixlen)
153 prefixlen = 128;
154
155#if defined(__linux__)
156 p_netmask = &netmask->s6_addr32[0];
157#else
158 p_netmask = &netmask->__u6_addr.__u6_addr32[0];
159#endif
160 while (32 < prefixlen) {
161 *p_netmask = 0xffffffff;
162 p_netmask++;
163 prefixlen -= 32;
164 }
165 if (prefixlen != 0) {
166 *p_netmask = htonl(0xFFFFFFFF << (32 - prefixlen));
167 }
168}
169
170/*! Determine if given \a addr is within given \a net + \a prefixlen
171 * Builds the netmask from \a net + \a prefixlen and matches it to \a addr
172 * \returns 1 in case of a match, 0 otherwise */
173int in46a_within_mask(const struct in46_addr *addr, const struct in46_addr *net, size_t prefixlen)
174{
175 struct in_addr netmask;
176 struct in6_addr netmask6;
177
178 if (addr->len != net->len)
179 return 0;
180
181 switch (addr->len) {
182 case 4:
183 netmask.s_addr = htonl(0xFFFFFFFF << (32 - prefixlen));
184 if ((addr->v4.s_addr & netmask.s_addr) == net->v4.s_addr)
185 return 1;
186 else
187 return 0;
188 case 16:
189 create_ipv6_netmask(&netmask6, prefixlen);
190 return ipv6_within_mask(&addr->v6, &net->v6, &netmask6);
191 default:
Harald Welte72a38b52017-08-09 21:58:12 +0200192 OSMO_ASSERT(0);
Harald Welted12eab92017-08-02 19:49:47 +0200193 return 0;
194 }
195}
Harald Weltea0d281d2017-08-02 21:48:16 +0200196
197/*! Convert given PDP End User Address to in46_addr
198 * \returns 0 on success; negative on error */
199int in46a_to_eua(const struct in46_addr *src, struct ul66_t *eua)
200{
201 switch (src->len) {
202 case 4:
203 eua->l = 6;
204 eua->v[0] = 0xf1; /* IETF */
205 eua->v[1] = 0x21; /* IPv4 */
206 memcpy(&eua->v[2], &src->v4, 4); /* Copy a 4 byte address */
207 break;
Harald Welted4d6e092017-08-08 18:10:43 +0200208 case 8:
Harald Weltea0d281d2017-08-02 21:48:16 +0200209 case 16:
210 eua->l = 18;
211 eua->v[0] = 0xf1; /* IETF */
212 eua->v[1] = 0x57; /* IPv6 */
213 memcpy(&eua->v[2], &src->v6, 16); /* Copy a 16 byte address */
214 break;
215 default:
Harald Welte72a38b52017-08-09 21:58:12 +0200216 OSMO_ASSERT(0);
Harald Weltea0d281d2017-08-02 21:48:16 +0200217 return -1;
218 }
219 return 0;
220}
221
222/*! Convert given in46_addr to PDP End User Address
223 * \returns 0 on success; negative on error */
224int in46a_from_eua(const struct ul66_t *eua, struct in46_addr *dst)
225{
226 if (eua->l < 2)
227 goto default_to_dyn_v4;
228
229 if (eua->v[0] != 0xf1)
230 return -1;
231
232 switch (eua->v[1]) {
233 case 0x21:
234 dst->len = 4;
235 if (eua->l >= 6)
236 memcpy(&dst->v4, &eua->v[2], 4); /* Copy a 4 byte address */
237 else
238 dst->v4.s_addr = 0;
239 break;
240 case 0x57:
241 dst->len = 16;
242 if (eua->l >= 18)
243 memcpy(&dst->v6, &eua->v[2], 16); /* Copy a 16 byte address */
244 else
245 memset(&dst->v6, 0, 16);
246 break;
247 default:
248 return -1;
249 }
250 return 0;
251
252default_to_dyn_v4:
253 /* assume dynamic IPv4 by default */
254 dst->len = 4;
255 dst->v4.s_addr = 0;
256 return 0;
257}