]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/network/networkd-ndisc.c
tree-wide: drop redundant _cleanup_ macros (#8810)
[thirdparty/systemd.git] / src / network / networkd-ndisc.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2 /***
3 This file is part of systemd.
4
5 Copyright (C) 2014 Intel Corporation. All rights reserved.
6 ***/
7
8 #include <netinet/icmp6.h>
9 #include <arpa/inet.h>
10
11 #include "sd-ndisc.h"
12
13 #include "networkd-ndisc.h"
14 #include "networkd-route.h"
15
16 #define NDISC_DNSSL_MAX 64U
17 #define NDISC_RDNSS_MAX 64U
18 #define NDISC_PREFIX_LFT_MIN 7200U
19
20 static int ndisc_netlink_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) {
21 _cleanup_(link_unrefp) Link *link = userdata;
22 int r;
23
24 assert(link);
25 assert(link->ndisc_messages > 0);
26
27 link->ndisc_messages--;
28
29 r = sd_netlink_message_get_errno(m);
30 if (r < 0 && r != -EEXIST)
31 log_link_error_errno(link, r, "Could not set NDisc route or address: %m");
32
33 if (link->ndisc_messages == 0) {
34 link->ndisc_configured = true;
35 link_check_ready(link);
36 }
37
38 return 1;
39 }
40
41 static void ndisc_router_process_default(Link *link, sd_ndisc_router *rt) {
42 _cleanup_(route_freep) Route *route = NULL;
43 struct in6_addr gateway;
44 uint16_t lifetime;
45 unsigned preference;
46 uint32_t mtu;
47 usec_t time_now;
48 int r;
49 Address *address;
50 Iterator i;
51
52 assert(link);
53 assert(rt);
54
55 r = sd_ndisc_router_get_lifetime(rt, &lifetime);
56 if (r < 0) {
57 log_link_warning_errno(link, r, "Failed to get gateway address from RA: %m");
58 return;
59 }
60 if (lifetime == 0) /* not a default router */
61 return;
62
63 r = sd_ndisc_router_get_address(rt, &gateway);
64 if (r < 0) {
65 log_link_warning_errno(link, r, "Failed to get gateway address from RA: %m");
66 return;
67 }
68
69 SET_FOREACH(address, link->addresses, i) {
70 if (!memcmp(&gateway, &address->in_addr.in6,
71 sizeof(address->in_addr.in6))) {
72 char buffer[INET6_ADDRSTRLEN];
73
74 log_link_debug(link, "No NDisc route added, gateway %s matches local address",
75 inet_ntop(AF_INET6,
76 &address->in_addr.in6,
77 buffer, sizeof(buffer)));
78 return;
79 }
80 }
81
82 SET_FOREACH(address, link->addresses_foreign, i) {
83 if (!memcmp(&gateway, &address->in_addr.in6,
84 sizeof(address->in_addr.in6))) {
85 char buffer[INET6_ADDRSTRLEN];
86
87 log_link_debug(link, "No NDisc route added, gateway %s matches local address",
88 inet_ntop(AF_INET6,
89 &address->in_addr.in6,
90 buffer, sizeof(buffer)));
91 return;
92 }
93 }
94
95 r = sd_ndisc_router_get_preference(rt, &preference);
96 if (r < 0) {
97 log_link_warning_errno(link, r, "Failed to get default router preference from RA: %m");
98 return;
99 }
100
101 r = sd_ndisc_router_get_timestamp(rt, clock_boottime_or_monotonic(), &time_now);
102 if (r < 0) {
103 log_link_warning_errno(link, r, "Failed to get RA timestamp: %m");
104 return;
105 }
106
107 r = sd_ndisc_router_get_mtu(rt, &mtu);
108 if (r == -ENODATA)
109 mtu = 0;
110 else if (r < 0) {
111 log_link_warning_errno(link, r, "Failed to get default router MTU from RA: %m");
112 return;
113 }
114
115 r = route_new(&route);
116 if (r < 0) {
117 log_link_error_errno(link, r, "Could not allocate route: %m");
118 return;
119 }
120
121 route->family = AF_INET6;
122 route->table = link->network->ipv6_accept_ra_route_table;
123 route->priority = link->network->dhcp_route_metric;
124 route->protocol = RTPROT_RA;
125 route->pref = preference;
126 route->gw.in6 = gateway;
127 route->lifetime = time_now + lifetime * USEC_PER_SEC;
128 route->mtu = mtu;
129
130 r = route_configure(route, link, ndisc_netlink_handler);
131 if (r < 0) {
132 log_link_warning_errno(link, r, "Could not set default route: %m");
133 link_enter_failed(link);
134 return;
135 }
136
137 link->ndisc_messages++;
138 }
139
140 static void ndisc_router_process_autonomous_prefix(Link *link, sd_ndisc_router *rt) {
141 _cleanup_(address_freep) Address *address = NULL;
142 Address *existing_address;
143 uint32_t lifetime_valid, lifetime_preferred, lifetime_remaining;
144 usec_t time_now;
145 unsigned prefixlen;
146 int r;
147
148 assert(link);
149 assert(rt);
150
151 r = sd_ndisc_router_get_timestamp(rt, clock_boottime_or_monotonic(), &time_now);
152 if (r < 0) {
153 log_link_warning_errno(link, r, "Failed to get RA timestamp: %m");
154 return;
155 }
156
157 r = sd_ndisc_router_prefix_get_prefixlen(rt, &prefixlen);
158 if (r < 0) {
159 log_link_error_errno(link, r, "Failed to get prefix length: %m");
160 return;
161 }
162
163 r = sd_ndisc_router_prefix_get_valid_lifetime(rt, &lifetime_valid);
164 if (r < 0) {
165 log_link_error_errno(link, r, "Failed to get prefix valid lifetime: %m");
166 return;
167 }
168
169 r = sd_ndisc_router_prefix_get_preferred_lifetime(rt, &lifetime_preferred);
170 if (r < 0) {
171 log_link_error_errno(link, r, "Failed to get prefix preferred lifetime: %m");
172 return;
173 }
174
175 /* The preferred lifetime is never greater than the valid lifetime */
176 if (lifetime_preferred > lifetime_valid)
177 return;
178
179 r = address_new(&address);
180 if (r < 0) {
181 log_link_error_errno(link, r, "Could not allocate address: %m");
182 return;
183 }
184
185 address->family = AF_INET6;
186 r = sd_ndisc_router_prefix_get_address(rt, &address->in_addr.in6);
187 if (r < 0) {
188 log_link_error_errno(link, r, "Failed to get prefix address: %m");
189 return;
190 }
191
192 if (in_addr_is_null(AF_INET6, (const union in_addr_union *) &link->network->ipv6_token) == 0)
193 memcpy(((char *)&address->in_addr.in6) + 8, ((char *)&link->network->ipv6_token) + 8, 8);
194 else {
195 /* see RFC4291 section 2.5.1 */
196 address->in_addr.in6.s6_addr[8] = link->mac.ether_addr_octet[0];
197 address->in_addr.in6.s6_addr[8] ^= 1 << 1;
198 address->in_addr.in6.s6_addr[9] = link->mac.ether_addr_octet[1];
199 address->in_addr.in6.s6_addr[10] = link->mac.ether_addr_octet[2];
200 address->in_addr.in6.s6_addr[11] = 0xff;
201 address->in_addr.in6.s6_addr[12] = 0xfe;
202 address->in_addr.in6.s6_addr[13] = link->mac.ether_addr_octet[3];
203 address->in_addr.in6.s6_addr[14] = link->mac.ether_addr_octet[4];
204 address->in_addr.in6.s6_addr[15] = link->mac.ether_addr_octet[5];
205 }
206 address->prefixlen = prefixlen;
207 address->flags = IFA_F_NOPREFIXROUTE|IFA_F_MANAGETEMPADDR;
208 address->cinfo.ifa_prefered = lifetime_preferred;
209
210 /* see RFC4862 section 5.5.3.e */
211 r = address_get(link, address->family, &address->in_addr, address->prefixlen, &existing_address);
212 if (r > 0) {
213 lifetime_remaining = existing_address->cinfo.tstamp / 100 + existing_address->cinfo.ifa_valid - time_now / USEC_PER_SEC;
214 if (lifetime_valid > NDISC_PREFIX_LFT_MIN || lifetime_valid > lifetime_remaining)
215 address->cinfo.ifa_valid = lifetime_valid;
216 else if (lifetime_remaining <= NDISC_PREFIX_LFT_MIN)
217 address->cinfo.ifa_valid = lifetime_remaining;
218 else
219 address->cinfo.ifa_valid = NDISC_PREFIX_LFT_MIN;
220 } else if (lifetime_valid > 0)
221 address->cinfo.ifa_valid = lifetime_valid;
222 else
223 return; /* see RFC4862 section 5.5.3.d */
224
225 if (address->cinfo.ifa_valid == 0)
226 return;
227
228 r = address_configure(address, link, ndisc_netlink_handler, true);
229 if (r < 0) {
230 log_link_warning_errno(link, r, "Could not set SLAAC address: %m");
231 link_enter_failed(link);
232 return;
233 }
234
235 link->ndisc_messages++;
236 }
237
238 static void ndisc_router_process_onlink_prefix(Link *link, sd_ndisc_router *rt) {
239 _cleanup_(route_freep) Route *route = NULL;
240 usec_t time_now;
241 uint32_t lifetime;
242 unsigned prefixlen;
243 int r;
244
245 assert(link);
246 assert(rt);
247
248 r = sd_ndisc_router_get_timestamp(rt, clock_boottime_or_monotonic(), &time_now);
249 if (r < 0) {
250 log_link_warning_errno(link, r, "Failed to get RA timestamp: %m");
251 return;
252 }
253
254 r = sd_ndisc_router_prefix_get_prefixlen(rt, &prefixlen);
255 if (r < 0) {
256 log_link_error_errno(link, r, "Failed to get prefix length: %m");
257 return;
258 }
259
260 r = sd_ndisc_router_prefix_get_valid_lifetime(rt, &lifetime);
261 if (r < 0) {
262 log_link_error_errno(link, r, "Failed to get prefix lifetime: %m");
263 return;
264 }
265
266 r = route_new(&route);
267 if (r < 0) {
268 log_link_error_errno(link, r, "Could not allocate route: %m");
269 return;
270 }
271
272 route->family = AF_INET6;
273 route->table = link->network->ipv6_accept_ra_route_table;
274 route->priority = link->network->dhcp_route_metric;
275 route->protocol = RTPROT_RA;
276 route->flags = RTM_F_PREFIX;
277 route->dst_prefixlen = prefixlen;
278 route->lifetime = time_now + lifetime * USEC_PER_SEC;
279
280 r = sd_ndisc_router_prefix_get_address(rt, &route->dst.in6);
281 if (r < 0) {
282 log_link_error_errno(link, r, "Failed to get prefix address: %m");
283 return;
284 }
285
286 r = route_configure(route, link, ndisc_netlink_handler);
287 if (r < 0) {
288 log_link_warning_errno(link, r, "Could not set prefix route: %m");
289 link_enter_failed(link);
290 return;
291 }
292
293 link->ndisc_messages++;
294 }
295
296 static void ndisc_router_process_route(Link *link, sd_ndisc_router *rt) {
297 _cleanup_(route_freep) Route *route = NULL;
298 struct in6_addr gateway;
299 uint32_t lifetime;
300 unsigned preference, prefixlen;
301 usec_t time_now;
302 int r;
303
304 assert(link);
305
306 r = sd_ndisc_router_route_get_lifetime(rt, &lifetime);
307 if (r < 0) {
308 log_link_warning_errno(link, r, "Failed to get gateway address from RA: %m");
309 return;
310 }
311 if (lifetime == 0)
312 return;
313
314 r = sd_ndisc_router_get_address(rt, &gateway);
315 if (r < 0) {
316 log_link_warning_errno(link, r, "Failed to get gateway address from RA: %m");
317 return;
318 }
319
320 r = sd_ndisc_router_route_get_prefixlen(rt, &prefixlen);
321 if (r < 0) {
322 log_link_warning_errno(link, r, "Failed to get route prefix length: %m");
323 return;
324 }
325
326 r = sd_ndisc_router_route_get_preference(rt, &preference);
327 if (r < 0) {
328 log_link_warning_errno(link, r, "Failed to get default router preference from RA: %m");
329 return;
330 }
331
332 r = sd_ndisc_router_get_timestamp(rt, clock_boottime_or_monotonic(), &time_now);
333 if (r < 0) {
334 log_link_warning_errno(link, r, "Failed to get RA timestamp: %m");
335 return;
336 }
337
338 r = route_new(&route);
339 if (r < 0) {
340 log_link_error_errno(link, r, "Could not allocate route: %m");
341 return;
342 }
343
344 route->family = AF_INET6;
345 route->table = link->network->ipv6_accept_ra_route_table;
346 route->protocol = RTPROT_RA;
347 route->pref = preference;
348 route->gw.in6 = gateway;
349 route->dst_prefixlen = prefixlen;
350 route->lifetime = time_now + lifetime * USEC_PER_SEC;
351
352 r = sd_ndisc_router_route_get_address(rt, &route->dst.in6);
353 if (r < 0) {
354 log_link_error_errno(link, r, "Failed to get route address: %m");
355 return;
356 }
357
358 r = route_configure(route, link, ndisc_netlink_handler);
359 if (r < 0) {
360 log_link_warning_errno(link, r, "Could not set additional route: %m");
361 link_enter_failed(link);
362 return;
363 }
364
365 link->ndisc_messages++;
366 }
367
368 static void ndisc_rdnss_hash_func(const void *p, struct siphash *state) {
369 const NDiscRDNSS *x = p;
370
371 siphash24_compress(&x->address, sizeof(x->address), state);
372 }
373
374 static int ndisc_rdnss_compare_func(const void *_a, const void *_b) {
375 const NDiscRDNSS *a = _a, *b = _b;
376
377 return memcmp(&a->address, &b->address, sizeof(a->address));
378 }
379
380 static const struct hash_ops ndisc_rdnss_hash_ops = {
381 .hash = ndisc_rdnss_hash_func,
382 .compare = ndisc_rdnss_compare_func
383 };
384
385 static void ndisc_router_process_rdnss(Link *link, sd_ndisc_router *rt) {
386 uint32_t lifetime;
387 const struct in6_addr *a;
388 usec_t time_now;
389 int i, n, r;
390
391 assert(link);
392 assert(rt);
393
394 r = sd_ndisc_router_get_timestamp(rt, clock_boottime_or_monotonic(), &time_now);
395 if (r < 0) {
396 log_link_warning_errno(link, r, "Failed to get RA timestamp: %m");
397 return;
398 }
399
400 r = sd_ndisc_router_rdnss_get_lifetime(rt, &lifetime);
401 if (r < 0) {
402 log_link_warning_errno(link, r, "Failed to get RDNSS lifetime: %m");
403 return;
404 }
405
406 n = sd_ndisc_router_rdnss_get_addresses(rt, &a);
407 if (n < 0) {
408 log_link_warning_errno(link, n, "Failed to get RDNSS addresses: %m");
409 return;
410 }
411
412 for (i = 0; i < n; i++) {
413 NDiscRDNSS d = {
414 .address = a[i]
415 }, *x;
416
417 if (lifetime == 0) {
418 (void) set_remove(link->ndisc_rdnss, &d);
419 link_dirty(link);
420 continue;
421 }
422
423 x = set_get(link->ndisc_rdnss, &d);
424 if (x) {
425 x->valid_until = time_now + lifetime * USEC_PER_SEC;
426 continue;
427 }
428
429 ndisc_vacuum(link);
430
431 if (set_size(link->ndisc_rdnss) >= NDISC_RDNSS_MAX) {
432 log_link_warning(link, "Too many RDNSS records per link, ignoring.");
433 continue;
434 }
435
436 r = set_ensure_allocated(&link->ndisc_rdnss, &ndisc_rdnss_hash_ops);
437 if (r < 0) {
438 log_oom();
439 return;
440 }
441
442 x = new0(NDiscRDNSS, 1);
443 if (!x) {
444 log_oom();
445 return;
446 }
447
448 x->address = a[i];
449 x->valid_until = time_now + lifetime * USEC_PER_SEC;
450
451 r = set_put(link->ndisc_rdnss, x);
452 if (r < 0) {
453 free(x);
454 log_oom();
455 return;
456 }
457
458 assert(r > 0);
459 link_dirty(link);
460 }
461 }
462
463 static void ndisc_dnssl_hash_func(const void *p, struct siphash *state) {
464 const NDiscDNSSL *x = p;
465
466 siphash24_compress(NDISC_DNSSL_DOMAIN(x), strlen(NDISC_DNSSL_DOMAIN(x)), state);
467 }
468
469 static int ndisc_dnssl_compare_func(const void *_a, const void *_b) {
470 const NDiscDNSSL *a = _a, *b = _b;
471
472 return strcmp(NDISC_DNSSL_DOMAIN(a), NDISC_DNSSL_DOMAIN(b));
473 }
474
475 static const struct hash_ops ndisc_dnssl_hash_ops = {
476 .hash = ndisc_dnssl_hash_func,
477 .compare = ndisc_dnssl_compare_func
478 };
479
480 static void ndisc_router_process_dnssl(Link *link, sd_ndisc_router *rt) {
481 _cleanup_strv_free_ char **l = NULL;
482 uint32_t lifetime;
483 usec_t time_now;
484 char **i;
485 int r;
486
487 assert(link);
488 assert(rt);
489
490 r = sd_ndisc_router_get_timestamp(rt, clock_boottime_or_monotonic(), &time_now);
491 if (r < 0) {
492 log_link_warning_errno(link, r, "Failed to get RA timestamp: %m");
493 return;
494 }
495
496 r = sd_ndisc_router_dnssl_get_lifetime(rt, &lifetime);
497 if (r < 0) {
498 log_link_warning_errno(link, r, "Failed to get RDNSS lifetime: %m");
499 return;
500 }
501
502 r = sd_ndisc_router_dnssl_get_domains(rt, &l);
503 if (r < 0) {
504 log_link_warning_errno(link, r, "Failed to get RDNSS addresses: %m");
505 return;
506 }
507
508 STRV_FOREACH(i, l) {
509 _cleanup_free_ NDiscDNSSL *s;
510 NDiscDNSSL *x;
511
512 s = malloc0(ALIGN(sizeof(NDiscDNSSL)) + strlen(*i) + 1);
513 if (!s) {
514 log_oom();
515 return;
516 }
517
518 strcpy(NDISC_DNSSL_DOMAIN(s), *i);
519
520 if (lifetime == 0) {
521 (void) set_remove(link->ndisc_dnssl, s);
522 link_dirty(link);
523 continue;
524 }
525
526 x = set_get(link->ndisc_dnssl, s);
527 if (x) {
528 x->valid_until = time_now + lifetime * USEC_PER_SEC;
529 continue;
530 }
531
532 ndisc_vacuum(link);
533
534 if (set_size(link->ndisc_dnssl) >= NDISC_DNSSL_MAX) {
535 log_link_warning(link, "Too many DNSSL records per link, ignoring.");
536 continue;
537 }
538
539 r = set_ensure_allocated(&link->ndisc_dnssl, &ndisc_dnssl_hash_ops);
540 if (r < 0) {
541 log_oom();
542 return;
543 }
544
545 s->valid_until = time_now + lifetime * USEC_PER_SEC;
546
547 r = set_put(link->ndisc_dnssl, s);
548 if (r < 0) {
549 log_oom();
550 return;
551 }
552
553 s = NULL;
554 assert(r > 0);
555 link_dirty(link);
556 }
557 }
558
559 static void ndisc_router_process_options(Link *link, sd_ndisc_router *rt) {
560 int r;
561
562 assert(link);
563 assert(rt);
564
565 r = sd_ndisc_router_option_rewind(rt);
566 for (;;) {
567 uint8_t type;
568
569 if (r < 0) {
570 log_link_warning_errno(link, r, "Failed to iterate through options: %m");
571 return;
572 }
573 if (r == 0) /* EOF */
574 break;
575
576 r = sd_ndisc_router_option_get_type(rt, &type);
577 if (r < 0) {
578 log_link_warning_errno(link, r, "Failed to get RA option type: %m");
579 return;
580 }
581
582 switch (type) {
583
584 case SD_NDISC_OPTION_PREFIX_INFORMATION: {
585 uint8_t flags;
586
587 r = sd_ndisc_router_prefix_get_flags(rt, &flags);
588 if (r < 0) {
589 log_link_warning_errno(link, r, "Failed to get RA prefix flags: %m");
590 return;
591 }
592
593 if (flags & ND_OPT_PI_FLAG_ONLINK)
594 ndisc_router_process_onlink_prefix(link, rt);
595 if (flags & ND_OPT_PI_FLAG_AUTO)
596 ndisc_router_process_autonomous_prefix(link, rt);
597
598 break;
599 }
600
601 case SD_NDISC_OPTION_ROUTE_INFORMATION:
602 ndisc_router_process_route(link, rt);
603 break;
604
605 case SD_NDISC_OPTION_RDNSS:
606 if (link->network->ipv6_accept_ra_use_dns)
607 ndisc_router_process_rdnss(link, rt);
608 break;
609
610 case SD_NDISC_OPTION_DNSSL:
611 if (link->network->ipv6_accept_ra_use_dns)
612 ndisc_router_process_dnssl(link, rt);
613 break;
614 }
615
616 r = sd_ndisc_router_option_next(rt);
617 }
618 }
619
620 static void ndisc_router_handler(Link *link, sd_ndisc_router *rt) {
621 uint64_t flags;
622 int r;
623
624 assert(link);
625 assert(link->network);
626 assert(link->manager);
627 assert(rt);
628
629 r = sd_ndisc_router_get_flags(rt, &flags);
630 if (r < 0) {
631 log_link_warning_errno(link, r, "Failed to get RA flags: %m");
632 return;
633 }
634
635 if (flags & (ND_RA_FLAG_MANAGED | ND_RA_FLAG_OTHER)) {
636 /* (re)start DHCPv6 client in stateful or stateless mode according to RA flags */
637 r = dhcp6_request_address(link, !(flags & ND_RA_FLAG_MANAGED));
638 if (r < 0 && r != -EBUSY)
639 log_link_warning_errno(link, r, "Could not acquire DHCPv6 lease on NDisc request: %m");
640 else
641 log_link_debug(link, "Acquiring DHCPv6 lease on NDisc request");
642 }
643
644 ndisc_router_process_default(link, rt);
645 ndisc_router_process_options(link, rt);
646 }
647
648 static void ndisc_handler(sd_ndisc *nd, sd_ndisc_event event, sd_ndisc_router *rt, void *userdata) {
649 Link *link = userdata;
650
651 assert(link);
652
653 if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
654 return;
655
656 switch (event) {
657
658 case SD_NDISC_EVENT_ROUTER:
659 ndisc_router_handler(link, rt);
660 break;
661
662 case SD_NDISC_EVENT_TIMEOUT:
663 link->ndisc_configured = true;
664 link_check_ready(link);
665
666 break;
667 default:
668 log_link_warning(link, "IPv6 Neighbor Discovery unknown event: %d", event);
669 }
670 }
671
672 int ndisc_configure(Link *link) {
673 int r;
674
675 assert(link);
676
677 r = sd_ndisc_new(&link->ndisc);
678 if (r < 0)
679 return r;
680
681 r = sd_ndisc_attach_event(link->ndisc, NULL, 0);
682 if (r < 0)
683 return r;
684
685 r = sd_ndisc_set_mac(link->ndisc, &link->mac);
686 if (r < 0)
687 return r;
688
689 r = sd_ndisc_set_ifindex(link->ndisc, link->ifindex);
690 if (r < 0)
691 return r;
692
693 r = sd_ndisc_set_callback(link->ndisc, ndisc_handler, link);
694 if (r < 0)
695 return r;
696
697 return 0;
698 }
699
700 void ndisc_vacuum(Link *link) {
701 NDiscRDNSS *r;
702 NDiscDNSSL *d;
703 Iterator i;
704 usec_t time_now;
705
706 assert(link);
707
708 /* Removes all RDNSS and DNSSL entries whose validity time has passed */
709
710 time_now = now(clock_boottime_or_monotonic());
711
712 SET_FOREACH(r, link->ndisc_rdnss, i)
713 if (r->valid_until < time_now) {
714 free(set_remove(link->ndisc_rdnss, r));
715 link_dirty(link);
716 }
717
718 SET_FOREACH(d, link->ndisc_dnssl, i)
719 if (d->valid_until < time_now) {
720 free(set_remove(link->ndisc_dnssl, d));
721 link_dirty(link);
722 }
723 }
724
725 void ndisc_flush(Link *link) {
726 assert(link);
727
728 /* Removes all RDNSS and DNSSL entries, without exception */
729
730 link->ndisc_rdnss = set_free_free(link->ndisc_rdnss);
731 link->ndisc_dnssl = set_free_free(link->ndisc_dnssl);
732 }