]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/basic/in-addr-util.c
core,journald: use quoted commandlines
[thirdparty/systemd.git] / src / basic / in-addr-util.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <arpa/inet.h>
4 #include <endian.h>
5 #include <errno.h>
6 #include <net/if.h>
7 #include <stdint.h>
8 #include <stdio.h>
9 #include <stdlib.h>
10
11 #include "alloc-util.h"
12 #include "errno-util.h"
13 #include "in-addr-util.h"
14 #include "macro.h"
15 #include "parse-util.h"
16 #include "random-util.h"
17 #include "string-util.h"
18 #include "strxcpyx.h"
19 #include "util.h"
20
21 bool in4_addr_is_null(const struct in_addr *a) {
22 assert(a);
23
24 return a->s_addr == 0;
25 }
26
27 bool in6_addr_is_null(const struct in6_addr *a) {
28 assert(a);
29
30 return IN6_IS_ADDR_UNSPECIFIED(a);
31 }
32
33 int in_addr_is_null(int family, const union in_addr_union *u) {
34 assert(u);
35
36 if (family == AF_INET)
37 return in4_addr_is_null(&u->in);
38
39 if (family == AF_INET6)
40 return in6_addr_is_null(&u->in6);
41
42 return -EAFNOSUPPORT;
43 }
44
45 bool in4_addr_is_link_local(const struct in_addr *a) {
46 assert(a);
47
48 return (be32toh(a->s_addr) & UINT32_C(0xFFFF0000)) == (UINT32_C(169) << 24 | UINT32_C(254) << 16);
49 }
50
51 bool in6_addr_is_link_local(const struct in6_addr *a) {
52 assert(a);
53
54 return IN6_IS_ADDR_LINKLOCAL(a);
55 }
56
57 int in_addr_is_link_local(int family, const union in_addr_union *u) {
58 assert(u);
59
60 if (family == AF_INET)
61 return in4_addr_is_link_local(&u->in);
62
63 if (family == AF_INET6)
64 return in6_addr_is_link_local(&u->in6);
65
66 return -EAFNOSUPPORT;
67 }
68
69 bool in6_addr_is_link_local_all_nodes(const struct in6_addr *a) {
70 assert(a);
71
72 /* ff02::1 */
73 return be32toh(a->s6_addr32[0]) == UINT32_C(0xff020000) &&
74 a->s6_addr32[1] == 0 &&
75 a->s6_addr32[2] == 0 &&
76 be32toh(a->s6_addr32[3]) == UINT32_C(0x00000001);
77 }
78
79 int in_addr_is_multicast(int family, const union in_addr_union *u) {
80 assert(u);
81
82 if (family == AF_INET)
83 return IN_MULTICAST(be32toh(u->in.s_addr));
84
85 if (family == AF_INET6)
86 return IN6_IS_ADDR_MULTICAST(&u->in6);
87
88 return -EAFNOSUPPORT;
89 }
90
91 bool in4_addr_is_local_multicast(const struct in_addr *a) {
92 assert(a);
93
94 return (be32toh(a->s_addr) & UINT32_C(0xffffff00)) == UINT32_C(0xe0000000);
95 }
96
97 bool in4_addr_is_localhost(const struct in_addr *a) {
98 assert(a);
99
100 /* All of 127.x.x.x is localhost. */
101 return (be32toh(a->s_addr) & UINT32_C(0xFF000000)) == UINT32_C(127) << 24;
102 }
103
104 bool in4_addr_is_non_local(const struct in_addr *a) {
105 /* Whether the address is not null and not localhost.
106 *
107 * As such, it is suitable to configure as DNS/NTP server from DHCP. */
108 return !in4_addr_is_null(a) &&
109 !in4_addr_is_localhost(a);
110 }
111
112 int in_addr_is_localhost(int family, const union in_addr_union *u) {
113 assert(u);
114
115 if (family == AF_INET)
116 return in4_addr_is_localhost(&u->in);
117
118 if (family == AF_INET6)
119 return IN6_IS_ADDR_LOOPBACK(&u->in6);
120
121 return -EAFNOSUPPORT;
122 }
123
124 bool in4_addr_equal(const struct in_addr *a, const struct in_addr *b) {
125 assert(a);
126 assert(b);
127
128 return a->s_addr == b->s_addr;
129 }
130
131 bool in6_addr_equal(const struct in6_addr *a, const struct in6_addr *b) {
132 assert(a);
133 assert(b);
134
135 return IN6_ARE_ADDR_EQUAL(a, b);
136 }
137
138 int in_addr_equal(int family, const union in_addr_union *a, const union in_addr_union *b) {
139 assert(a);
140 assert(b);
141
142 if (family == AF_INET)
143 return in4_addr_equal(&a->in, &b->in);
144
145 if (family == AF_INET6)
146 return in6_addr_equal(&a->in6, &b->in6);
147
148 return -EAFNOSUPPORT;
149 }
150
151 int in_addr_prefix_intersect(
152 int family,
153 const union in_addr_union *a,
154 unsigned aprefixlen,
155 const union in_addr_union *b,
156 unsigned bprefixlen) {
157
158 unsigned m;
159
160 assert(a);
161 assert(b);
162
163 /* Checks whether there are any addresses that are in both
164 * networks */
165
166 m = MIN(aprefixlen, bprefixlen);
167
168 if (family == AF_INET) {
169 uint32_t x, nm;
170
171 x = be32toh(a->in.s_addr ^ b->in.s_addr);
172 nm = (m == 0) ? 0 : 0xFFFFFFFFUL << (32 - m);
173
174 return (x & nm) == 0;
175 }
176
177 if (family == AF_INET6) {
178 unsigned i;
179
180 if (m > 128)
181 m = 128;
182
183 for (i = 0; i < 16; i++) {
184 uint8_t x, nm;
185
186 x = a->in6.s6_addr[i] ^ b->in6.s6_addr[i];
187
188 if (m < 8)
189 nm = 0xFF << (8 - m);
190 else
191 nm = 0xFF;
192
193 if ((x & nm) != 0)
194 return 0;
195
196 if (m > 8)
197 m -= 8;
198 else
199 m = 0;
200 }
201
202 return 1;
203 }
204
205 return -EAFNOSUPPORT;
206 }
207
208 int in_addr_prefix_next(int family, union in_addr_union *u, unsigned prefixlen) {
209 assert(u);
210
211 /* Increases the network part of an address by one. Returns 0 if that succeeds, or -ERANGE if
212 * this overflows. */
213
214 return in_addr_prefix_nth(family, u, prefixlen, 1);
215 }
216
217 /*
218 * Calculates the nth prefix of size prefixlen starting from the address denoted by u.
219 *
220 * On success 0 will be returned and the calculated prefix will be available in
221 * u. In case the calculation cannot be performed (invalid prefix length,
222 * overflows would occur) -ERANGE is returned. If the address family given isn't
223 * supported -EAFNOSUPPORT will be returned.
224 *
225 * Examples:
226 * - in_addr_prefix_nth(AF_INET, 192.168.0.0, 24, 2), returns 0, writes 192.168.2.0 to u
227 * - in_addr_prefix_nth(AF_INET, 192.168.0.0, 24, 0), returns 0, no data written
228 * - in_addr_prefix_nth(AF_INET, 255.255.255.0, 24, 1), returns -ERANGE, no data written
229 * - in_addr_prefix_nth(AF_INET, 255.255.255.0, 0, 1), returns -ERANGE, no data written
230 * - in_addr_prefix_nth(AF_INET6, 2001:db8, 64, 0xff00) returns 0, writes 2001:0db8:0000:ff00:: to u
231 */
232 int in_addr_prefix_nth(int family, union in_addr_union *u, unsigned prefixlen, uint64_t nth) {
233 assert(u);
234
235 if (prefixlen <= 0)
236 return -ERANGE;
237
238 if (family == AF_INET) {
239 uint32_t c, n, t;
240
241 if (prefixlen > 32)
242 return -ERANGE;
243
244 c = be32toh(u->in.s_addr);
245
246 t = nth << (32 - prefixlen);
247
248 /* Check for wrap */
249 if (c > UINT32_MAX - t)
250 return -ERANGE;
251
252 n = c + t;
253
254 n &= UINT32_C(0xFFFFFFFF) << (32 - prefixlen);
255 u->in.s_addr = htobe32(n);
256 return 0;
257 }
258
259 if (family == AF_INET6) {
260 bool overflow = false;
261
262 if (prefixlen > 128)
263 return -ERANGE;
264
265 for (unsigned i = 16; i > 0; i--) {
266 unsigned t, j = i - 1, p = j * 8;
267
268 if (p >= prefixlen) {
269 u->in6.s6_addr[j] = 0;
270 continue;
271 }
272
273 if (prefixlen - p < 8) {
274 u->in6.s6_addr[j] &= 0xff << (8 - (prefixlen - p));
275 t = u->in6.s6_addr[j] + ((nth & 0xff) << (8 - (prefixlen - p)));
276 nth >>= prefixlen - p;
277 } else {
278 t = u->in6.s6_addr[j] + (nth & 0xff) + overflow;
279 nth >>= 8;
280 }
281
282 overflow = t > UINT8_MAX;
283 u->in6.s6_addr[j] = (uint8_t) (t & 0xff);
284 }
285
286 if (overflow || nth != 0)
287 return -ERANGE;
288
289 return 0;
290 }
291
292 return -EAFNOSUPPORT;
293 }
294
295 int in_addr_random_prefix(
296 int family,
297 union in_addr_union *u,
298 unsigned prefixlen_fixed_part,
299 unsigned prefixlen) {
300
301 assert(u);
302
303 /* Random network part of an address by one. */
304
305 if (prefixlen <= 0)
306 return 0;
307
308 if (family == AF_INET) {
309 uint32_t c, n;
310
311 if (prefixlen_fixed_part > 32)
312 prefixlen_fixed_part = 32;
313 if (prefixlen > 32)
314 prefixlen = 32;
315 if (prefixlen_fixed_part >= prefixlen)
316 return -EINVAL;
317
318 c = be32toh(u->in.s_addr);
319 c &= ((UINT32_C(1) << prefixlen_fixed_part) - 1) << (32 - prefixlen_fixed_part);
320
321 random_bytes(&n, sizeof(n));
322 n &= ((UINT32_C(1) << (prefixlen - prefixlen_fixed_part)) - 1) << (32 - prefixlen);
323
324 u->in.s_addr = htobe32(n | c);
325 return 1;
326 }
327
328 if (family == AF_INET6) {
329 struct in6_addr n;
330 unsigned i, j;
331
332 if (prefixlen_fixed_part > 128)
333 prefixlen_fixed_part = 128;
334 if (prefixlen > 128)
335 prefixlen = 128;
336 if (prefixlen_fixed_part >= prefixlen)
337 return -EINVAL;
338
339 random_bytes(&n, sizeof(n));
340
341 for (i = 0; i < 16; i++) {
342 uint8_t mask_fixed_part = 0, mask = 0;
343
344 if (i < (prefixlen_fixed_part + 7) / 8) {
345 if (i < prefixlen_fixed_part / 8)
346 mask_fixed_part = 0xffu;
347 else {
348 j = prefixlen_fixed_part % 8;
349 mask_fixed_part = ((UINT8_C(1) << (j + 1)) - 1) << (8 - j);
350 }
351 }
352
353 if (i < (prefixlen + 7) / 8) {
354 if (i < prefixlen / 8)
355 mask = 0xffu ^ mask_fixed_part;
356 else {
357 j = prefixlen % 8;
358 mask = (((UINT8_C(1) << (j + 1)) - 1) << (8 - j)) ^ mask_fixed_part;
359 }
360 }
361
362 u->in6.s6_addr[i] &= mask_fixed_part;
363 u->in6.s6_addr[i] |= n.s6_addr[i] & mask;
364 }
365
366 return 1;
367 }
368
369 return -EAFNOSUPPORT;
370 }
371
372 int in_addr_prefix_range(
373 int family,
374 const union in_addr_union *in,
375 unsigned prefixlen,
376 union in_addr_union *ret_start,
377 union in_addr_union *ret_end) {
378
379 union in_addr_union start, end;
380 int r;
381
382 assert(in);
383
384 if (!IN_SET(family, AF_INET, AF_INET6))
385 return -EAFNOSUPPORT;
386
387 if (ret_start) {
388 start = *in;
389 r = in_addr_prefix_nth(family, &start, prefixlen, 0);
390 if (r < 0)
391 return r;
392 }
393
394 if (ret_end) {
395 end = *in;
396 r = in_addr_prefix_nth(family, &end, prefixlen, 1);
397 if (r < 0)
398 return r;
399 }
400
401 if (ret_start)
402 *ret_start = start;
403 if (ret_end)
404 *ret_end = end;
405
406 return 0;
407 }
408
409 int in_addr_to_string(int family, const union in_addr_union *u, char **ret) {
410 _cleanup_free_ char *x = NULL;
411 size_t l;
412
413 assert(u);
414 assert(ret);
415
416 if (family == AF_INET)
417 l = INET_ADDRSTRLEN;
418 else if (family == AF_INET6)
419 l = INET6_ADDRSTRLEN;
420 else
421 return -EAFNOSUPPORT;
422
423 x = new(char, l);
424 if (!x)
425 return -ENOMEM;
426
427 errno = 0;
428 if (!inet_ntop(family, u, x, l))
429 return errno_or_else(EINVAL);
430
431 *ret = TAKE_PTR(x);
432 return 0;
433 }
434
435 int in_addr_prefix_to_string(int family, const union in_addr_union *u, unsigned prefixlen, char **ret) {
436 _cleanup_free_ char *x = NULL;
437 char *p;
438 size_t l;
439
440 assert(u);
441 assert(ret);
442
443 if (family == AF_INET)
444 l = INET_ADDRSTRLEN + 3;
445 else if (family == AF_INET6)
446 l = INET6_ADDRSTRLEN + 4;
447 else
448 return -EAFNOSUPPORT;
449
450 if (prefixlen > FAMILY_ADDRESS_SIZE(family) * 8)
451 return -EINVAL;
452
453 x = new(char, l);
454 if (!x)
455 return -ENOMEM;
456
457 errno = 0;
458 if (!inet_ntop(family, u, x, l))
459 return errno_or_else(EINVAL);
460
461 p = x + strlen(x);
462 l -= strlen(x);
463 (void) strpcpyf(&p, l, "/%u", prefixlen);
464
465 *ret = TAKE_PTR(x);
466 return 0;
467 }
468
469 int in_addr_port_ifindex_name_to_string(int family, const union in_addr_union *u, uint16_t port, int ifindex, const char *server_name, char **ret) {
470 _cleanup_free_ char *ip_str = NULL, *x = NULL;
471 int r;
472
473 assert(IN_SET(family, AF_INET, AF_INET6));
474 assert(u);
475 assert(ret);
476
477 /* Much like in_addr_to_string(), but optionally appends the zone interface index to the address, to properly
478 * handle IPv6 link-local addresses. */
479
480 r = in_addr_to_string(family, u, &ip_str);
481 if (r < 0)
482 return r;
483
484 if (family == AF_INET6) {
485 r = in_addr_is_link_local(family, u);
486 if (r < 0)
487 return r;
488 if (r == 0)
489 ifindex = 0;
490 } else
491 ifindex = 0; /* For IPv4 address, ifindex is always ignored. */
492
493 if (port == 0 && ifindex == 0 && isempty(server_name)) {
494 *ret = TAKE_PTR(ip_str);
495 return 0;
496 }
497
498 const char *separator = isempty(server_name) ? "" : "#";
499 server_name = strempty(server_name);
500
501 if (port > 0) {
502 if (family == AF_INET6) {
503 if (ifindex > 0)
504 r = asprintf(&x, "[%s]:%"PRIu16"%%%i%s%s", ip_str, port, ifindex, separator, server_name);
505 else
506 r = asprintf(&x, "[%s]:%"PRIu16"%s%s", ip_str, port, separator, server_name);
507 } else
508 r = asprintf(&x, "%s:%"PRIu16"%s%s", ip_str, port, separator, server_name);
509 } else {
510 if (ifindex > 0)
511 r = asprintf(&x, "%s%%%i%s%s", ip_str, ifindex, separator, server_name);
512 else {
513 x = strjoin(ip_str, separator, server_name);
514 r = x ? 0 : -ENOMEM;
515 }
516 }
517 if (r < 0)
518 return -ENOMEM;
519
520 *ret = TAKE_PTR(x);
521 return 0;
522 }
523
524 int in_addr_from_string(int family, const char *s, union in_addr_union *ret) {
525 union in_addr_union buffer;
526 assert(s);
527
528 if (!IN_SET(family, AF_INET, AF_INET6))
529 return -EAFNOSUPPORT;
530
531 errno = 0;
532 if (inet_pton(family, s, ret ?: &buffer) <= 0)
533 return errno_or_else(EINVAL);
534
535 return 0;
536 }
537
538 int in_addr_from_string_auto(const char *s, int *ret_family, union in_addr_union *ret) {
539 int r;
540
541 assert(s);
542
543 r = in_addr_from_string(AF_INET, s, ret);
544 if (r >= 0) {
545 if (ret_family)
546 *ret_family = AF_INET;
547 return 0;
548 }
549
550 r = in_addr_from_string(AF_INET6, s, ret);
551 if (r >= 0) {
552 if (ret_family)
553 *ret_family = AF_INET6;
554 return 0;
555 }
556
557 return -EINVAL;
558 }
559
560 unsigned char in4_addr_netmask_to_prefixlen(const struct in_addr *addr) {
561 assert(addr);
562
563 return 32U - u32ctz(be32toh(addr->s_addr));
564 }
565
566 struct in_addr* in4_addr_prefixlen_to_netmask(struct in_addr *addr, unsigned char prefixlen) {
567 assert(addr);
568 assert(prefixlen <= 32);
569
570 /* Shifting beyond 32 is not defined, handle this specially. */
571 if (prefixlen == 0)
572 addr->s_addr = 0;
573 else
574 addr->s_addr = htobe32((0xffffffff << (32 - prefixlen)) & 0xffffffff);
575
576 return addr;
577 }
578
579 int in4_addr_default_prefixlen(const struct in_addr *addr, unsigned char *prefixlen) {
580 uint8_t msb_octet = *(uint8_t*) addr;
581
582 /* addr may not be aligned, so make sure we only access it byte-wise */
583
584 assert(addr);
585 assert(prefixlen);
586
587 if (msb_octet < 128)
588 /* class A, leading bits: 0 */
589 *prefixlen = 8;
590 else if (msb_octet < 192)
591 /* class B, leading bits 10 */
592 *prefixlen = 16;
593 else if (msb_octet < 224)
594 /* class C, leading bits 110 */
595 *prefixlen = 24;
596 else
597 /* class D or E, no default prefixlen */
598 return -ERANGE;
599
600 return 0;
601 }
602
603 int in4_addr_default_subnet_mask(const struct in_addr *addr, struct in_addr *mask) {
604 unsigned char prefixlen;
605 int r;
606
607 assert(addr);
608 assert(mask);
609
610 r = in4_addr_default_prefixlen(addr, &prefixlen);
611 if (r < 0)
612 return r;
613
614 in4_addr_prefixlen_to_netmask(mask, prefixlen);
615 return 0;
616 }
617
618 int in_addr_mask(int family, union in_addr_union *addr, unsigned char prefixlen) {
619 assert(addr);
620
621 if (family == AF_INET) {
622 struct in_addr mask;
623
624 if (!in4_addr_prefixlen_to_netmask(&mask, prefixlen))
625 return -EINVAL;
626
627 addr->in.s_addr &= mask.s_addr;
628 return 0;
629 }
630
631 if (family == AF_INET6) {
632 unsigned i;
633
634 for (i = 0; i < 16; i++) {
635 uint8_t mask;
636
637 if (prefixlen >= 8) {
638 mask = 0xFF;
639 prefixlen -= 8;
640 } else {
641 mask = 0xFF << (8 - prefixlen);
642 prefixlen = 0;
643 }
644
645 addr->in6.s6_addr[i] &= mask;
646 }
647
648 return 0;
649 }
650
651 return -EAFNOSUPPORT;
652 }
653
654 int in_addr_prefix_covers(int family,
655 const union in_addr_union *prefix,
656 unsigned char prefixlen,
657 const union in_addr_union *address) {
658
659 union in_addr_union masked_prefix, masked_address;
660 int r;
661
662 assert(prefix);
663 assert(address);
664
665 masked_prefix = *prefix;
666 r = in_addr_mask(family, &masked_prefix, prefixlen);
667 if (r < 0)
668 return r;
669
670 masked_address = *address;
671 r = in_addr_mask(family, &masked_address, prefixlen);
672 if (r < 0)
673 return r;
674
675 return in_addr_equal(family, &masked_prefix, &masked_address);
676 }
677
678 int in_addr_parse_prefixlen(int family, const char *p, unsigned char *ret) {
679 uint8_t u;
680 int r;
681
682 if (!IN_SET(family, AF_INET, AF_INET6))
683 return -EAFNOSUPPORT;
684
685 r = safe_atou8(p, &u);
686 if (r < 0)
687 return r;
688
689 if (u > FAMILY_ADDRESS_SIZE(family) * 8)
690 return -ERANGE;
691
692 *ret = u;
693 return 0;
694 }
695
696 int in_addr_prefix_from_string(
697 const char *p,
698 int family,
699 union in_addr_union *ret_prefix,
700 unsigned char *ret_prefixlen) {
701
702 _cleanup_free_ char *str = NULL;
703 union in_addr_union buffer;
704 const char *e, *l;
705 unsigned char k;
706 int r;
707
708 assert(p);
709
710 if (!IN_SET(family, AF_INET, AF_INET6))
711 return -EAFNOSUPPORT;
712
713 e = strchr(p, '/');
714 if (e) {
715 str = strndup(p, e - p);
716 if (!str)
717 return -ENOMEM;
718
719 l = str;
720 } else
721 l = p;
722
723 r = in_addr_from_string(family, l, &buffer);
724 if (r < 0)
725 return r;
726
727 if (e) {
728 r = in_addr_parse_prefixlen(family, e+1, &k);
729 if (r < 0)
730 return r;
731 } else
732 k = FAMILY_ADDRESS_SIZE(family) * 8;
733
734 if (ret_prefix)
735 *ret_prefix = buffer;
736 if (ret_prefixlen)
737 *ret_prefixlen = k;
738
739 return 0;
740 }
741
742 int in_addr_prefix_from_string_auto_internal(
743 const char *p,
744 InAddrPrefixLenMode mode,
745 int *ret_family,
746 union in_addr_union *ret_prefix,
747 unsigned char *ret_prefixlen) {
748
749 _cleanup_free_ char *str = NULL;
750 union in_addr_union buffer;
751 const char *e, *l;
752 unsigned char k;
753 int family, r;
754
755 assert(p);
756
757 e = strchr(p, '/');
758 if (e) {
759 str = strndup(p, e - p);
760 if (!str)
761 return -ENOMEM;
762
763 l = str;
764 } else
765 l = p;
766
767 r = in_addr_from_string_auto(l, &family, &buffer);
768 if (r < 0)
769 return r;
770
771 if (e) {
772 r = in_addr_parse_prefixlen(family, e+1, &k);
773 if (r < 0)
774 return r;
775 } else
776 switch (mode) {
777 case PREFIXLEN_FULL:
778 k = FAMILY_ADDRESS_SIZE(family) * 8;
779 break;
780 case PREFIXLEN_REFUSE:
781 return -ENOANO; /* To distinguish this error from others. */
782 case PREFIXLEN_LEGACY:
783 if (family == AF_INET) {
784 r = in4_addr_default_prefixlen(&buffer.in, &k);
785 if (r < 0)
786 return r;
787 } else
788 k = 0;
789 break;
790 default:
791 assert_not_reached("Invalid prefixlen mode");
792 }
793
794 if (ret_family)
795 *ret_family = family;
796 if (ret_prefix)
797 *ret_prefix = buffer;
798 if (ret_prefixlen)
799 *ret_prefixlen = k;
800
801 return 0;
802
803 }
804
805 static void in_addr_data_hash_func(const struct in_addr_data *a, struct siphash *state) {
806 siphash24_compress(&a->family, sizeof(a->family), state);
807 siphash24_compress(&a->address, FAMILY_ADDRESS_SIZE(a->family), state);
808 }
809
810 static int in_addr_data_compare_func(const struct in_addr_data *x, const struct in_addr_data *y) {
811 int r;
812
813 r = CMP(x->family, y->family);
814 if (r != 0)
815 return r;
816
817 return memcmp(&x->address, &y->address, FAMILY_ADDRESS_SIZE(x->family));
818 }
819
820 DEFINE_HASH_OPS(in_addr_data_hash_ops, struct in_addr_data, in_addr_data_hash_func, in_addr_data_compare_func);
821
822 void in6_addr_hash_func(const struct in6_addr *addr, struct siphash *state) {
823 assert(addr);
824
825 siphash24_compress(addr, sizeof(*addr), state);
826 }
827
828 int in6_addr_compare_func(const struct in6_addr *a, const struct in6_addr *b) {
829 return memcmp(a, b, sizeof(*a));
830 }
831
832 DEFINE_HASH_OPS(in6_addr_hash_ops, struct in6_addr, in6_addr_hash_func, in6_addr_compare_func);