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