blob: 4e7cbcd31e7ec80f65b86b6790fc3c659da6ad4d [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 Welted12eab92017-08-02 19:49:47 +020082/*! Determine if two in46_addr are equal or not
83 * \returns 1 in case they are equal; 0 otherwise */
84int in46a_equal(const struct in46_addr *a, const struct in46_addr *b)
85{
86 if (a->len == b->len && !memcmp(&a->v6, &b->v6, a->len))
87 return 1;
88 else
89 return 0;
90}
91
Harald Welte365f8fa2017-08-08 18:09:36 +020092/*! Determine if two in46_addr prefix are equal or not
93 * The prefix length is determined by the shortest of the prefixes of a and b
94 * \returns 1 in case the common prefix are equal; 0 otherwise */
95int in46a_prefix_equal(const struct in46_addr *a, const struct in46_addr *b)
96{
97 unsigned int len;
98 if (a->len > b->len)
99 len = b->len;
100 else
101 len = a->len;
102
103 if (!memcmp(&a->v6, &b->v6, len))
104 return 1;
105 else
106 return 0;
107}
108
Harald Welted12eab92017-08-02 19:49:47 +0200109/*! Match if IPv6 addr1 + addr2 are within same \a mask */
110static int ipv6_within_mask(const struct in6_addr *addr1, const struct in6_addr *addr2,
111 const struct in6_addr *mask)
112{
113 struct in6_addr masked = *addr2;
114#if defined(__linux__)
115 masked.s6_addr32[0] &= mask->s6_addr32[0];
116 masked.s6_addr32[1] &= mask->s6_addr32[1];
117 masked.s6_addr32[2] &= mask->s6_addr32[2];
118 masked.s6_addr32[3] &= mask->s6_addr32[3];
119#else
120 masked.__u6_addr.__u6_addr32[0] &= mask->__u6_addr.__u6_addr32[0];
121 masked.__u6_addr.__u6_addr32[1] &= mask->__u6_addr.__u6_addr32[1];
122 masked.__u6_addr.__u6_addr32[2] &= mask->__u6_addr.__u6_addr32[2];
123 masked.__u6_addr.__u6_addr32[3] &= mask->__u6_addr.__u6_addr32[3];
124#endif
125 if (!memcmp(addr1, &masked, sizeof(struct in6_addr)))
126 return 1;
127 else
128 return 0;
129}
130
131/*! Create an IPv6 netmask from the given prefix length */
132static void create_ipv6_netmask(struct in6_addr *netmask, int prefixlen)
133{
134 uint32_t *p_netmask;
135 memset(netmask, 0, sizeof(struct in6_addr));
136 if (prefixlen < 0)
137 prefixlen = 0;
138 else if (128 < prefixlen)
139 prefixlen = 128;
140
141#if defined(__linux__)
142 p_netmask = &netmask->s6_addr32[0];
143#else
144 p_netmask = &netmask->__u6_addr.__u6_addr32[0];
145#endif
146 while (32 < prefixlen) {
147 *p_netmask = 0xffffffff;
148 p_netmask++;
149 prefixlen -= 32;
150 }
151 if (prefixlen != 0) {
152 *p_netmask = htonl(0xFFFFFFFF << (32 - prefixlen));
153 }
154}
155
156/*! Determine if given \a addr is within given \a net + \a prefixlen
157 * Builds the netmask from \a net + \a prefixlen and matches it to \a addr
158 * \returns 1 in case of a match, 0 otherwise */
159int in46a_within_mask(const struct in46_addr *addr, const struct in46_addr *net, size_t prefixlen)
160{
161 struct in_addr netmask;
162 struct in6_addr netmask6;
163
164 if (addr->len != net->len)
165 return 0;
166
167 switch (addr->len) {
168 case 4:
169 netmask.s_addr = htonl(0xFFFFFFFF << (32 - prefixlen));
170 if ((addr->v4.s_addr & netmask.s_addr) == net->v4.s_addr)
171 return 1;
172 else
173 return 0;
174 case 16:
175 create_ipv6_netmask(&netmask6, prefixlen);
176 return ipv6_within_mask(&addr->v6, &net->v6, &netmask6);
177 default:
Harald Welte72a38b52017-08-09 21:58:12 +0200178 OSMO_ASSERT(0);
Harald Welted12eab92017-08-02 19:49:47 +0200179 return 0;
180 }
181}
Harald Weltea0d281d2017-08-02 21:48:16 +0200182
183/*! Convert given PDP End User Address to in46_addr
184 * \returns 0 on success; negative on error */
185int in46a_to_eua(const struct in46_addr *src, struct ul66_t *eua)
186{
187 switch (src->len) {
188 case 4:
189 eua->l = 6;
190 eua->v[0] = 0xf1; /* IETF */
191 eua->v[1] = 0x21; /* IPv4 */
192 memcpy(&eua->v[2], &src->v4, 4); /* Copy a 4 byte address */
193 break;
Harald Welted4d6e092017-08-08 18:10:43 +0200194 case 8:
Harald Weltea0d281d2017-08-02 21:48:16 +0200195 case 16:
196 eua->l = 18;
197 eua->v[0] = 0xf1; /* IETF */
198 eua->v[1] = 0x57; /* IPv6 */
199 memcpy(&eua->v[2], &src->v6, 16); /* Copy a 16 byte address */
200 break;
201 default:
Harald Welte72a38b52017-08-09 21:58:12 +0200202 OSMO_ASSERT(0);
Harald Weltea0d281d2017-08-02 21:48:16 +0200203 return -1;
204 }
205 return 0;
206}
207
208/*! Convert given in46_addr to PDP End User Address
209 * \returns 0 on success; negative on error */
210int in46a_from_eua(const struct ul66_t *eua, struct in46_addr *dst)
211{
212 if (eua->l < 2)
213 goto default_to_dyn_v4;
214
215 if (eua->v[0] != 0xf1)
216 return -1;
217
218 switch (eua->v[1]) {
219 case 0x21:
220 dst->len = 4;
221 if (eua->l >= 6)
222 memcpy(&dst->v4, &eua->v[2], 4); /* Copy a 4 byte address */
223 else
224 dst->v4.s_addr = 0;
225 break;
226 case 0x57:
227 dst->len = 16;
228 if (eua->l >= 18)
229 memcpy(&dst->v6, &eua->v[2], 16); /* Copy a 16 byte address */
230 else
231 memset(&dst->v6, 0, 16);
232 break;
233 default:
234 return -1;
235 }
236 return 0;
237
238default_to_dyn_v4:
239 /* assume dynamic IPv4 by default */
240 dst->len = 4;
241 dst->v4.s_addr = 0;
242 return 0;
243}