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