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