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