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