]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/basic/in-addr-util.c
Merge commit 'pr/2036^^'
[thirdparty/systemd.git] / src / basic / in-addr-util.c
CommitLineData
3b653205
LP
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
6 Copyright 2014 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
22#include <arpa/inet.h>
23
b5efdb8a 24#include "alloc-util.h"
3b653205
LP
25#include "in-addr-util.h"
26
af93291c 27int in_addr_is_null(int family, const union in_addr_union *u) {
3b653205
LP
28 assert(u);
29
30 if (family == AF_INET)
31 return u->in.s_addr == 0;
32
33 if (family == AF_INET6)
34 return
35 u->in6.s6_addr32[0] == 0 &&
36 u->in6.s6_addr32[1] == 0 &&
37 u->in6.s6_addr32[2] == 0 &&
38 u->in6.s6_addr32[3] == 0;
39
40 return -EAFNOSUPPORT;
41}
42
af93291c
LP
43int in_addr_is_link_local(int family, const union in_addr_union *u) {
44 assert(u);
45
46 if (family == AF_INET)
d830ebbd 47 return (be32toh(u->in.s_addr) & UINT32_C(0xFFFF0000)) == (UINT32_C(169) << 24 | UINT32_C(254) << 16);
af93291c
LP
48
49 if (family == AF_INET6)
50 return IN6_IS_ADDR_LINKLOCAL(&u->in6);
51
52 return -EAFNOSUPPORT;
53}
3b653205 54
d830ebbd
LP
55int in_addr_is_localhost(int family, const union in_addr_union *u) {
56 assert(u);
57
58 if (family == AF_INET)
59 /* All of 127.x.x.x is localhost. */
60 return (be32toh(u->in.s_addr) & UINT32_C(0xFF000000)) == UINT32_C(127) << 24;
61
62 if (family == AF_INET)
63 return IN6_IS_ADDR_LOOPBACK(&u->in6);
64
65 return -EAFNOSUPPORT;
66}
67
623a4c97 68int in_addr_equal(int family, const union in_addr_union *a, const union in_addr_union *b) {
3b653205
LP
69 assert(a);
70 assert(b);
71
72 if (family == AF_INET)
73 return a->in.s_addr == b->in.s_addr;
74
75 if (family == AF_INET6)
76 return
77 a->in6.s6_addr32[0] == b->in6.s6_addr32[0] &&
78 a->in6.s6_addr32[1] == b->in6.s6_addr32[1] &&
79 a->in6.s6_addr32[2] == b->in6.s6_addr32[2] &&
80 a->in6.s6_addr32[3] == b->in6.s6_addr32[3];
81
82 return -EAFNOSUPPORT;
83}
84
85int in_addr_prefix_intersect(
0dd25fb9 86 int family,
3b653205
LP
87 const union in_addr_union *a,
88 unsigned aprefixlen,
89 const union in_addr_union *b,
90 unsigned bprefixlen) {
91
92 unsigned m;
93
94 assert(a);
95 assert(b);
96
97 /* Checks whether there are any addresses that are in both
98 * networks */
99
100 m = MIN(aprefixlen, bprefixlen);
101
102 if (family == AF_INET) {
103 uint32_t x, nm;
104
105 x = be32toh(a->in.s_addr ^ b->in.s_addr);
106 nm = (m == 0) ? 0 : 0xFFFFFFFFUL << (32 - m);
107
108 return (x & nm) == 0;
109 }
110
111 if (family == AF_INET6) {
112 unsigned i;
113
114 if (m > 128)
115 m = 128;
116
117 for (i = 0; i < 16; i++) {
118 uint8_t x, nm;
119
120 x = a->in6.s6_addr[i] ^ b->in6.s6_addr[i];
121
122 if (m < 8)
123 nm = 0xFF << (8 - m);
124 else
125 nm = 0xFF;
126
127 if ((x & nm) != 0)
128 return 0;
129
130 if (m > 8)
131 m -= 8;
132 else
133 m = 0;
134 }
135
136 return 1;
137 }
138
139 return -EAFNOSUPPORT;
140}
141
0dd25fb9 142int in_addr_prefix_next(int family, union in_addr_union *u, unsigned prefixlen) {
3b653205
LP
143 assert(u);
144
145 /* Increases the network part of an address by one. Returns
146 * positive it that succeeds, or 0 if this overflows. */
147
148 if (prefixlen <= 0)
149 return 0;
150
151 if (family == AF_INET) {
152 uint32_t c, n;
153
154 if (prefixlen > 32)
155 prefixlen = 32;
156
157 c = be32toh(u->in.s_addr);
158 n = c + (1UL << (32 - prefixlen));
159 if (n < c)
160 return 0;
161 n &= 0xFFFFFFFFUL << (32 - prefixlen);
162
163 u->in.s_addr = htobe32(n);
164 return 1;
165 }
166
167 if (family == AF_INET6) {
168 struct in6_addr add = {}, result;
169 uint8_t overflow = 0;
170 unsigned i;
171
172 if (prefixlen > 128)
173 prefixlen = 128;
174
175 /* First calculate what we have to add */
176 add.s6_addr[(prefixlen-1) / 8] = 1 << (7 - (prefixlen-1) % 8);
177
178 for (i = 16; i > 0; i--) {
179 unsigned j = i - 1;
180
181 result.s6_addr[j] = u->in6.s6_addr[j] + add.s6_addr[j] + overflow;
182 overflow = (result.s6_addr[j] < u->in6.s6_addr[j]);
183 }
184
185 if (overflow)
186 return 0;
187
188 u->in6 = result;
189 return 1;
190 }
191
192 return -EAFNOSUPPORT;
193}
194
0dd25fb9 195int in_addr_to_string(int family, const union in_addr_union *u, char **ret) {
3b653205
LP
196 char *x;
197 size_t l;
198
199 assert(u);
200 assert(ret);
201
202 if (family == AF_INET)
203 l = INET_ADDRSTRLEN;
204 else if (family == AF_INET6)
205 l = INET6_ADDRSTRLEN;
206 else
207 return -EAFNOSUPPORT;
208
209 x = new(char, l);
210 if (!x)
211 return -ENOMEM;
212
213 errno = 0;
214 if (!inet_ntop(family, u, x, l)) {
215 free(x);
216 return errno ? -errno : -EINVAL;
217 }
218
219 *ret = x;
220 return 0;
221}
222
0dd25fb9 223int in_addr_from_string(int family, const char *s, union in_addr_union *ret) {
3b653205
LP
224
225 assert(s);
226 assert(ret);
227
228 if (!IN_SET(family, AF_INET, AF_INET6))
229 return -EAFNOSUPPORT;
230
231 errno = 0;
232 if (inet_pton(family, s, ret) <= 0)
233 return errno ? -errno : -EINVAL;
234
235 return 0;
236}
74b2466e 237
0dd25fb9 238int in_addr_from_string_auto(const char *s, int *family, union in_addr_union *ret) {
74b2466e
LP
239 int r;
240
241 assert(s);
242 assert(family);
243 assert(ret);
244
245 r = in_addr_from_string(AF_INET, s, ret);
246 if (r >= 0) {
247 *family = AF_INET;
248 return 0;
249 }
250
251 r = in_addr_from_string(AF_INET6, s, ret);
252 if (r >= 0) {
253 *family = AF_INET6;
254 return 0;
255 }
256
257 return -EINVAL;
258}
44e7b949 259
76917807 260unsigned char in_addr_netmask_to_prefixlen(const struct in_addr *addr) {
44e7b949
LP
261 assert(addr);
262
263 return 32 - u32ctz(be32toh(addr->s_addr));
264}
df40eee8 265
76917807
LP
266struct in_addr* in_addr_prefixlen_to_netmask(struct in_addr *addr, unsigned char prefixlen) {
267 assert(addr);
268 assert(prefixlen <= 32);
269
270 /* Shifting beyond 32 is not defined, handle this specially. */
271 if (prefixlen == 0)
272 addr->s_addr = 0;
273 else
274 addr->s_addr = htobe32((0xffffffff << (32 - prefixlen)) & 0xffffffff);
275
276 return addr;
277}
278
df40eee8 279int in_addr_default_prefixlen(const struct in_addr *addr, unsigned char *prefixlen) {
1caa12d0
TG
280 uint8_t msb_octet = *(uint8_t*) addr;
281
282 /* addr may not be aligned, so make sure we only access it byte-wise */
df40eee8
TG
283
284 assert(addr);
df40eee8
TG
285 assert(prefixlen);
286
1caa12d0 287 if (msb_octet < 128)
df40eee8
TG
288 /* class A, leading bits: 0 */
289 *prefixlen = 8;
1caa12d0 290 else if (msb_octet < 192)
df40eee8
TG
291 /* class B, leading bits 10 */
292 *prefixlen = 16;
1caa12d0 293 else if (msb_octet < 224)
df40eee8
TG
294 /* class C, leading bits 110 */
295 *prefixlen = 24;
296 else
297 /* class D or E, no default prefixlen */
298 return -ERANGE;
299
300 return 0;
301}
302
303int in_addr_default_subnet_mask(const struct in_addr *addr, struct in_addr *mask) {
304 unsigned char prefixlen;
305 int r;
306
307 assert(addr);
308 assert(mask);
309
310 r = in_addr_default_prefixlen(addr, &prefixlen);
311 if (r < 0)
312 return r;
313
76917807 314 in_addr_prefixlen_to_netmask(mask, prefixlen);
df40eee8
TG
315 return 0;
316}
5a8bcb67
LP
317
318int in_addr_mask(int family, union in_addr_union *addr, unsigned char prefixlen) {
319 assert(addr);
320
321 if (family == AF_INET) {
322 struct in_addr mask;
323
324 if (!in_addr_prefixlen_to_netmask(&mask, prefixlen))
325 return -EINVAL;
326
327 addr->in.s_addr &= mask.s_addr;
328 return 0;
329 }
330
331 if (family == AF_INET6) {
332 unsigned i;
333
334 for (i = 0; i < 16; i++) {
335 uint8_t mask;
336
337 if (prefixlen >= 8) {
338 mask = 0xFF;
339 prefixlen -= 8;
340 } else {
341 mask = 0xFF << (8 - prefixlen);
342 prefixlen = 0;
343 }
344
345 addr->in6.s6_addr[i] &= mask;
346 }
347
348 return 0;
349 }
350
351 return -EAFNOSUPPORT;
352}