]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/basic/in-addr-util.c
exit-status: drop EXIT_MAKE_STARTER
[thirdparty/systemd.git] / src / basic / in-addr-util.c
CommitLineData
3b653205
LP
1/***
2 This file is part of systemd.
3
4 Copyright 2014 Lennart Poettering
5
6 systemd is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
10
11 systemd is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public License
17 along with systemd; If not, see <http://www.gnu.org/licenses/>.
18***/
19
20#include <arpa/inet.h>
11c3a366
TA
21#include <endian.h>
22#include <errno.h>
2817157b 23#include <net/if.h>
11c3a366
TA
24#include <stdint.h>
25#include <stdlib.h>
3b653205 26
b5efdb8a 27#include "alloc-util.h"
3b653205 28#include "in-addr-util.h"
11c3a366 29#include "macro.h"
2817157b 30#include "parse-util.h"
11c3a366 31#include "util.h"
3b653205 32
34380032 33bool in4_addr_is_null(const struct in_addr *a) {
fdedbe26 34 assert(a);
34380032 35
fdedbe26 36 return a->s_addr == 0;
34380032
LP
37}
38
af93291c 39int in_addr_is_null(int family, const union in_addr_union *u) {
3b653205
LP
40 assert(u);
41
42 if (family == AF_INET)
34380032 43 return in4_addr_is_null(&u->in);
3b653205
LP
44
45 if (family == AF_INET6)
fdedbe26 46 return IN6_IS_ADDR_UNSPECIFIED(&u->in6);
3b653205
LP
47
48 return -EAFNOSUPPORT;
49}
50
fdedbe26
LP
51bool in4_addr_is_link_local(const struct in_addr *a) {
52 assert(a);
53
54 return (be32toh(a->s_addr) & UINT32_C(0xFFFF0000)) == (UINT32_C(169) << 24 | UINT32_C(254) << 16);
55}
56
af93291c
LP
57int in_addr_is_link_local(int family, const union in_addr_union *u) {
58 assert(u);
59
60 if (family == AF_INET)
fdedbe26 61 return in4_addr_is_link_local(&u->in);
af93291c
LP
62
63 if (family == AF_INET6)
64 return IN6_IS_ADDR_LINKLOCAL(&u->in6);
65
66 return -EAFNOSUPPORT;
67}
3b653205 68
85257f48
SS
69int in_addr_is_multicast(int family, const union in_addr_union *u) {
70 assert(u);
71
72 if (family == AF_INET)
73 return IN_MULTICAST(be32toh(u->in.s_addr));
74
75 if (family == AF_INET6)
76 return IN6_IS_ADDR_MULTICAST(&u->in6);
77
78 return -EAFNOSUPPORT;
79}
80
fdedbe26
LP
81bool in4_addr_is_localhost(const struct in_addr *a) {
82 assert(a);
83
84 /* All of 127.x.x.x is localhost. */
85 return (be32toh(a->s_addr) & UINT32_C(0xFF000000)) == UINT32_C(127) << 24;
86}
87
d830ebbd
LP
88int in_addr_is_localhost(int family, const union in_addr_union *u) {
89 assert(u);
90
91 if (family == AF_INET)
fdedbe26 92 return in4_addr_is_localhost(&u->in);
d830ebbd 93
db15affc 94 if (family == AF_INET6)
d830ebbd
LP
95 return IN6_IS_ADDR_LOOPBACK(&u->in6);
96
97 return -EAFNOSUPPORT;
98}
99
623a4c97 100int in_addr_equal(int family, const union in_addr_union *a, const union in_addr_union *b) {
3b653205
LP
101 assert(a);
102 assert(b);
103
104 if (family == AF_INET)
105 return a->in.s_addr == b->in.s_addr;
106
107 if (family == AF_INET6)
108 return
109 a->in6.s6_addr32[0] == b->in6.s6_addr32[0] &&
110 a->in6.s6_addr32[1] == b->in6.s6_addr32[1] &&
111 a->in6.s6_addr32[2] == b->in6.s6_addr32[2] &&
112 a->in6.s6_addr32[3] == b->in6.s6_addr32[3];
113
114 return -EAFNOSUPPORT;
115}
116
117int in_addr_prefix_intersect(
0dd25fb9 118 int family,
3b653205
LP
119 const union in_addr_union *a,
120 unsigned aprefixlen,
121 const union in_addr_union *b,
122 unsigned bprefixlen) {
123
124 unsigned m;
125
126 assert(a);
127 assert(b);
128
129 /* Checks whether there are any addresses that are in both
130 * networks */
131
132 m = MIN(aprefixlen, bprefixlen);
133
134 if (family == AF_INET) {
135 uint32_t x, nm;
136
137 x = be32toh(a->in.s_addr ^ b->in.s_addr);
138 nm = (m == 0) ? 0 : 0xFFFFFFFFUL << (32 - m);
139
140 return (x & nm) == 0;
141 }
142
143 if (family == AF_INET6) {
144 unsigned i;
145
146 if (m > 128)
147 m = 128;
148
149 for (i = 0; i < 16; i++) {
150 uint8_t x, nm;
151
152 x = a->in6.s6_addr[i] ^ b->in6.s6_addr[i];
153
154 if (m < 8)
155 nm = 0xFF << (8 - m);
156 else
157 nm = 0xFF;
158
159 if ((x & nm) != 0)
160 return 0;
161
162 if (m > 8)
163 m -= 8;
164 else
165 m = 0;
166 }
167
168 return 1;
169 }
170
171 return -EAFNOSUPPORT;
172}
173
0dd25fb9 174int in_addr_prefix_next(int family, union in_addr_union *u, unsigned prefixlen) {
3b653205
LP
175 assert(u);
176
177 /* Increases the network part of an address by one. Returns
178 * positive it that succeeds, or 0 if this overflows. */
179
180 if (prefixlen <= 0)
181 return 0;
182
183 if (family == AF_INET) {
184 uint32_t c, n;
185
186 if (prefixlen > 32)
187 prefixlen = 32;
188
189 c = be32toh(u->in.s_addr);
190 n = c + (1UL << (32 - prefixlen));
191 if (n < c)
192 return 0;
193 n &= 0xFFFFFFFFUL << (32 - prefixlen);
194
195 u->in.s_addr = htobe32(n);
196 return 1;
197 }
198
199 if (family == AF_INET6) {
200 struct in6_addr add = {}, result;
201 uint8_t overflow = 0;
202 unsigned i;
203
204 if (prefixlen > 128)
205 prefixlen = 128;
206
207 /* First calculate what we have to add */
208 add.s6_addr[(prefixlen-1) / 8] = 1 << (7 - (prefixlen-1) % 8);
209
210 for (i = 16; i > 0; i--) {
211 unsigned j = i - 1;
212
213 result.s6_addr[j] = u->in6.s6_addr[j] + add.s6_addr[j] + overflow;
214 overflow = (result.s6_addr[j] < u->in6.s6_addr[j]);
215 }
216
217 if (overflow)
218 return 0;
219
220 u->in6 = result;
221 return 1;
222 }
223
224 return -EAFNOSUPPORT;
225}
226
0dd25fb9 227int in_addr_to_string(int family, const union in_addr_union *u, char **ret) {
3b653205
LP
228 char *x;
229 size_t l;
230
231 assert(u);
232 assert(ret);
233
234 if (family == AF_INET)
235 l = INET_ADDRSTRLEN;
236 else if (family == AF_INET6)
237 l = INET6_ADDRSTRLEN;
238 else
239 return -EAFNOSUPPORT;
240
241 x = new(char, l);
242 if (!x)
243 return -ENOMEM;
244
245 errno = 0;
246 if (!inet_ntop(family, u, x, l)) {
247 free(x);
f5e5c28f 248 return errno > 0 ? -errno : -EINVAL;
3b653205
LP
249 }
250
251 *ret = x;
252 return 0;
253}
254
2817157b
LP
255int in_addr_ifindex_to_string(int family, const union in_addr_union *u, int ifindex, char **ret) {
256 size_t l;
257 char *x;
258 int r;
259
260 assert(u);
261 assert(ret);
262
263 /* Much like in_addr_to_string(), but optionally appends the zone interface index to the address, to properly
264 * handle IPv6 link-local addresses. */
265
266 if (family != AF_INET6)
267 goto fallback;
268 if (ifindex <= 0)
269 goto fallback;
270
271 r = in_addr_is_link_local(family, u);
272 if (r < 0)
273 return r;
274 if (r == 0)
275 goto fallback;
276
277 l = INET6_ADDRSTRLEN + 1 + DECIMAL_STR_MAX(ifindex) + 1;
278 x = new(char, l);
279 if (!x)
280 return -ENOMEM;
281
282 errno = 0;
283 if (!inet_ntop(family, u, x, l)) {
284 free(x);
285 return errno > 0 ? -errno : -EINVAL;
286 }
287
288 sprintf(strchr(x, 0), "%%%i", ifindex);
289 *ret = x;
290
291 return 0;
292
293fallback:
294 return in_addr_to_string(family, u, ret);
295}
296
0dd25fb9 297int in_addr_from_string(int family, const char *s, union in_addr_union *ret) {
fd18634d 298 union in_addr_union buffer;
3b653205 299 assert(s);
3b653205
LP
300
301 if (!IN_SET(family, AF_INET, AF_INET6))
302 return -EAFNOSUPPORT;
303
304 errno = 0;
fd18634d 305 if (inet_pton(family, s, ret ?: &buffer) <= 0)
f5e5c28f 306 return errno > 0 ? -errno : -EINVAL;
3b653205
LP
307
308 return 0;
309}
74b2466e 310
0dd25fb9 311int in_addr_from_string_auto(const char *s, int *family, union in_addr_union *ret) {
74b2466e
LP
312 int r;
313
314 assert(s);
74b2466e
LP
315
316 r = in_addr_from_string(AF_INET, s, ret);
317 if (r >= 0) {
fd18634d
LP
318 if (family)
319 *family = AF_INET;
74b2466e
LP
320 return 0;
321 }
322
323 r = in_addr_from_string(AF_INET6, s, ret);
324 if (r >= 0) {
fd18634d
LP
325 if (family)
326 *family = AF_INET6;
74b2466e
LP
327 return 0;
328 }
329
330 return -EINVAL;
331}
44e7b949 332
2817157b
LP
333int in_addr_ifindex_from_string_auto(const char *s, int *family, union in_addr_union *ret, int *ifindex) {
334 const char *suffix;
335 int r, ifi = 0;
336
337 assert(s);
338 assert(family);
339 assert(ret);
340
341 /* Similar to in_addr_from_string_auto() but also parses an optionally appended IPv6 zone suffix ("scope id")
342 * if one is found. */
343
344 suffix = strchr(s, '%');
345 if (suffix) {
346
347 if (ifindex) {
348 /* If we shall return the interface index, try to parse it */
349 r = parse_ifindex(suffix + 1, &ifi);
350 if (r < 0) {
351 unsigned u;
352
353 u = if_nametoindex(suffix + 1);
354 if (u <= 0)
355 return -errno;
356
357 ifi = (int) u;
358 }
359 }
360
361 s = strndupa(s, suffix - s);
362 }
363
364 r = in_addr_from_string_auto(s, family, ret);
365 if (r < 0)
366 return r;
367
368 if (ifindex)
369 *ifindex = ifi;
370
371 return r;
372}
373
76917807 374unsigned char in_addr_netmask_to_prefixlen(const struct in_addr *addr) {
44e7b949
LP
375 assert(addr);
376
377 return 32 - u32ctz(be32toh(addr->s_addr));
378}
df40eee8 379
76917807
LP
380struct in_addr* in_addr_prefixlen_to_netmask(struct in_addr *addr, unsigned char prefixlen) {
381 assert(addr);
382 assert(prefixlen <= 32);
383
384 /* Shifting beyond 32 is not defined, handle this specially. */
385 if (prefixlen == 0)
386 addr->s_addr = 0;
387 else
388 addr->s_addr = htobe32((0xffffffff << (32 - prefixlen)) & 0xffffffff);
389
390 return addr;
391}
392
df40eee8 393int in_addr_default_prefixlen(const struct in_addr *addr, unsigned char *prefixlen) {
1caa12d0
TG
394 uint8_t msb_octet = *(uint8_t*) addr;
395
396 /* addr may not be aligned, so make sure we only access it byte-wise */
df40eee8
TG
397
398 assert(addr);
df40eee8
TG
399 assert(prefixlen);
400
1caa12d0 401 if (msb_octet < 128)
df40eee8
TG
402 /* class A, leading bits: 0 */
403 *prefixlen = 8;
1caa12d0 404 else if (msb_octet < 192)
df40eee8
TG
405 /* class B, leading bits 10 */
406 *prefixlen = 16;
1caa12d0 407 else if (msb_octet < 224)
df40eee8
TG
408 /* class C, leading bits 110 */
409 *prefixlen = 24;
410 else
411 /* class D or E, no default prefixlen */
412 return -ERANGE;
413
414 return 0;
415}
416
417int in_addr_default_subnet_mask(const struct in_addr *addr, struct in_addr *mask) {
418 unsigned char prefixlen;
419 int r;
420
421 assert(addr);
422 assert(mask);
423
424 r = in_addr_default_prefixlen(addr, &prefixlen);
425 if (r < 0)
426 return r;
427
76917807 428 in_addr_prefixlen_to_netmask(mask, prefixlen);
df40eee8
TG
429 return 0;
430}
5a8bcb67
LP
431
432int in_addr_mask(int family, union in_addr_union *addr, unsigned char prefixlen) {
433 assert(addr);
434
435 if (family == AF_INET) {
436 struct in_addr mask;
437
438 if (!in_addr_prefixlen_to_netmask(&mask, prefixlen))
439 return -EINVAL;
440
441 addr->in.s_addr &= mask.s_addr;
442 return 0;
443 }
444
445 if (family == AF_INET6) {
446 unsigned i;
447
448 for (i = 0; i < 16; i++) {
449 uint8_t mask;
450
451 if (prefixlen >= 8) {
452 mask = 0xFF;
453 prefixlen -= 8;
454 } else {
455 mask = 0xFF << (8 - prefixlen);
456 prefixlen = 0;
457 }
458
459 addr->in6.s6_addr[i] &= mask;
460 }
461
462 return 0;
463 }
464
465 return -EAFNOSUPPORT;
466}
f7bf1abe
SS
467
468int in_addr_prefix_from_string(const char *p, int family, union in_addr_union *ret_prefix, uint8_t *ret_prefixlen) {
469 union in_addr_union buffer;
470 const char *e, *l;
471 uint8_t k;
472 int r;
473
474 assert(p);
475
476 if (!IN_SET(family, AF_INET, AF_INET6))
477 return -EAFNOSUPPORT;
478
479 e = strchr(p, '/');
480 if (e)
481 l = strndupa(p, e - p);
482 else
483 l = p;
484
485 r = in_addr_from_string(family, l, &buffer);
486 if (r < 0)
487 return r;
488
489 k = FAMILY_ADDRESS_SIZE(family) * 8;
490
491 if (e) {
492 uint8_t n;
493
494 r = safe_atou8(e + 1, &n);
495 if (r < 0)
496 return r;
497
498 if (n > k)
499 return -ERANGE;
500
501 k = n;
502 }
503
504 *ret_prefix = buffer;
505 *ret_prefixlen = k;
506
507 return 0;
508}