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