blob: 17853777ae83050b0f8165ed15e7f03768e63126 [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
14#include <sys/types.h>
15#include <netinet/in.h>
16#include <sys/socket.h>
17#include <arpa/inet.h>
18#include <netdb.h>
19#include <stdlib.h>
20#include <string.h>
21
22/*! Return the address family of given \reff in46_addr argument */
23int in46a_to_af(const struct in46_addr *in)
24{
25 switch (in->len) {
26 case 4:
27 return AF_INET;
Harald Welted4d6e092017-08-08 18:10:43 +020028 case 8:
Harald Welted12eab92017-08-02 19:49:47 +020029 case 16:
30 return AF_INET6;
31 default:
32 return -1;
33 }
34}
35
36/*! Convert \ref in46_addr to sockaddr_storage */
37int in46a_to_sas(struct sockaddr_storage *out, const struct in46_addr *in)
38{
39 struct sockaddr_in *sin = (struct sockaddr_in *)out;
40 struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)out;
41
42 switch (in->len) {
43 case 4:
44 sin->sin_family = AF_INET;
45 sin->sin_addr = in->v4;
46 break;
47 case 16:
48 sin6->sin6_family = AF_INET;
49 sin6->sin6_addr = in->v6;
50 break;
51 default:
52 return -1;
53 }
54
55 return 0;
56}
57
58/*! Convenience wrapper around inet_ntop() for \ref in46_addr */
59const char *in46a_ntop(const struct in46_addr *in, char *dst, socklen_t dst_size)
60{
61 int af = in46a_to_af(in);
62 if (af < 0)
63 return NULL;
64
65 return inet_ntop(af, (const void *) &in->v4, dst, dst_size);
66}
67
68/*! Determine if two in46_addr are equal or not
69 * \returns 1 in case they are equal; 0 otherwise */
70int in46a_equal(const struct in46_addr *a, const struct in46_addr *b)
71{
72 if (a->len == b->len && !memcmp(&a->v6, &b->v6, a->len))
73 return 1;
74 else
75 return 0;
76}
77
Harald Welte365f8fa2017-08-08 18:09:36 +020078/*! Determine if two in46_addr prefix are equal or not
79 * The prefix length is determined by the shortest of the prefixes of a and b
80 * \returns 1 in case the common prefix are equal; 0 otherwise */
81int in46a_prefix_equal(const struct in46_addr *a, const struct in46_addr *b)
82{
83 unsigned int len;
84 if (a->len > b->len)
85 len = b->len;
86 else
87 len = a->len;
88
89 if (!memcmp(&a->v6, &b->v6, len))
90 return 1;
91 else
92 return 0;
93}
94
Harald Welted12eab92017-08-02 19:49:47 +020095/*! Match if IPv6 addr1 + addr2 are within same \a mask */
96static int ipv6_within_mask(const struct in6_addr *addr1, const struct in6_addr *addr2,
97 const struct in6_addr *mask)
98{
99 struct in6_addr masked = *addr2;
100#if defined(__linux__)
101 masked.s6_addr32[0] &= mask->s6_addr32[0];
102 masked.s6_addr32[1] &= mask->s6_addr32[1];
103 masked.s6_addr32[2] &= mask->s6_addr32[2];
104 masked.s6_addr32[3] &= mask->s6_addr32[3];
105#else
106 masked.__u6_addr.__u6_addr32[0] &= mask->__u6_addr.__u6_addr32[0];
107 masked.__u6_addr.__u6_addr32[1] &= mask->__u6_addr.__u6_addr32[1];
108 masked.__u6_addr.__u6_addr32[2] &= mask->__u6_addr.__u6_addr32[2];
109 masked.__u6_addr.__u6_addr32[3] &= mask->__u6_addr.__u6_addr32[3];
110#endif
111 if (!memcmp(addr1, &masked, sizeof(struct in6_addr)))
112 return 1;
113 else
114 return 0;
115}
116
117/*! Create an IPv6 netmask from the given prefix length */
118static void create_ipv6_netmask(struct in6_addr *netmask, int prefixlen)
119{
120 uint32_t *p_netmask;
121 memset(netmask, 0, sizeof(struct in6_addr));
122 if (prefixlen < 0)
123 prefixlen = 0;
124 else if (128 < prefixlen)
125 prefixlen = 128;
126
127#if defined(__linux__)
128 p_netmask = &netmask->s6_addr32[0];
129#else
130 p_netmask = &netmask->__u6_addr.__u6_addr32[0];
131#endif
132 while (32 < prefixlen) {
133 *p_netmask = 0xffffffff;
134 p_netmask++;
135 prefixlen -= 32;
136 }
137 if (prefixlen != 0) {
138 *p_netmask = htonl(0xFFFFFFFF << (32 - prefixlen));
139 }
140}
141
142/*! Determine if given \a addr is within given \a net + \a prefixlen
143 * Builds the netmask from \a net + \a prefixlen and matches it to \a addr
144 * \returns 1 in case of a match, 0 otherwise */
145int in46a_within_mask(const struct in46_addr *addr, const struct in46_addr *net, size_t prefixlen)
146{
147 struct in_addr netmask;
148 struct in6_addr netmask6;
149
150 if (addr->len != net->len)
151 return 0;
152
153 switch (addr->len) {
154 case 4:
155 netmask.s_addr = htonl(0xFFFFFFFF << (32 - prefixlen));
156 if ((addr->v4.s_addr & netmask.s_addr) == net->v4.s_addr)
157 return 1;
158 else
159 return 0;
160 case 16:
161 create_ipv6_netmask(&netmask6, prefixlen);
162 return ipv6_within_mask(&addr->v6, &net->v6, &netmask6);
163 default:
164 return 0;
165 }
166}
Harald Weltea0d281d2017-08-02 21:48:16 +0200167
168/*! Convert given PDP End User Address to in46_addr
169 * \returns 0 on success; negative on error */
170int in46a_to_eua(const struct in46_addr *src, struct ul66_t *eua)
171{
172 switch (src->len) {
173 case 4:
174 eua->l = 6;
175 eua->v[0] = 0xf1; /* IETF */
176 eua->v[1] = 0x21; /* IPv4 */
177 memcpy(&eua->v[2], &src->v4, 4); /* Copy a 4 byte address */
178 break;
Harald Welted4d6e092017-08-08 18:10:43 +0200179 case 8:
Harald Weltea0d281d2017-08-02 21:48:16 +0200180 case 16:
181 eua->l = 18;
182 eua->v[0] = 0xf1; /* IETF */
183 eua->v[1] = 0x57; /* IPv6 */
184 memcpy(&eua->v[2], &src->v6, 16); /* Copy a 16 byte address */
185 break;
186 default:
187 return -1;
188 }
189 return 0;
190}
191
192/*! Convert given in46_addr to PDP End User Address
193 * \returns 0 on success; negative on error */
194int in46a_from_eua(const struct ul66_t *eua, struct in46_addr *dst)
195{
196 if (eua->l < 2)
197 goto default_to_dyn_v4;
198
199 if (eua->v[0] != 0xf1)
200 return -1;
201
202 switch (eua->v[1]) {
203 case 0x21:
204 dst->len = 4;
205 if (eua->l >= 6)
206 memcpy(&dst->v4, &eua->v[2], 4); /* Copy a 4 byte address */
207 else
208 dst->v4.s_addr = 0;
209 break;
210 case 0x57:
211 dst->len = 16;
212 if (eua->l >= 18)
213 memcpy(&dst->v6, &eua->v[2], 16); /* Copy a 16 byte address */
214 else
215 memset(&dst->v6, 0, 16);
216 break;
217 default:
218 return -1;
219 }
220 return 0;
221
222default_to_dyn_v4:
223 /* assume dynamic IPv4 by default */
224 dst->len = 4;
225 dst->v4.s_addr = 0;
226 return 0;
227}