]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/network/networkd-ndisc.c
network: IPv6 Compliance RFC4862: Address Lifetime Expiry (Hosts Only) [v6LC.3.2.2]
[thirdparty/systemd.git] / src / network / networkd-ndisc.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
a13c50e7 2/***
810adae9 3 Copyright © 2014 Intel Corporation. All rights reserved.
a13c50e7
TG
4***/
5
23f53b99 6#include <arpa/inet.h>
062c020f
YW
7#include <netinet/icmp6.h>
8#include <linux/if.h>
f4ef1c19 9#include <linux/if_arp.h>
a13c50e7 10
a13c50e7
TG
11#include "sd-ndisc.h"
12
77302468 13#include "event-util.h"
d909e4af 14#include "missing_network.h"
f09a4747 15#include "networkd-address-generation.h"
093e3533 16#include "networkd-address.h"
ca5ad760 17#include "networkd-dhcp6.h"
73854ba1 18#include "networkd-manager.h"
1e7a0e21 19#include "networkd-ndisc.h"
76c5a0f2 20#include "networkd-queue.h"
3b6a3bde 21#include "networkd-route.h"
3b5a4fc6 22#include "networkd-state-file.h"
3976c430 23#include "networkd-sysctl.h"
ac24e418 24#include "string-table.h"
5f506a55 25#include "string-util.h"
51517f9e 26#include "strv.h"
91750028 27#include "sysctl-util.h"
1e7a0e21
LP
28
29#define NDISC_DNSSL_MAX 64U
30#define NDISC_RDNSS_MAX 64U
4df16cd0 31/* Not defined in the RFC, but let's set an upper limit to make not consume much memory.
bf943a9d
YW
32 * This should be safe as typically there should be at most 1 portal per network. */
33#define NDISC_CAPTIVE_PORTAL_MAX 64U
4df16cd0
YW
34/* Neither defined in the RFC. Just for safety. Otherwise, malformed messages can make clients trigger OOM.
35 * Not sure if the threshold is high enough. Let's adjust later if not. */
36#define NDISC_PREF64_MAX 64U
fe307276 37
52672db3 38bool link_ndisc_enabled(Link *link) {
062c020f
YW
39 assert(link);
40
41 if (!socket_ipv6_is_supported())
42 return false;
43
44 if (link->flags & IFF_LOOPBACK)
45 return false;
46
f4ef1c19
YW
47 if (link->iftype == ARPHRD_CAN)
48 return false;
49
062c020f
YW
50 if (!link->network)
51 return false;
52
bd7e0a3f 53 if (!link_may_have_ipv6ll(link, /* check_multicast = */ true))
062c020f
YW
54 return false;
55
769f9744 56 /* Honor explicitly specified value. */
52672db3
YW
57 if (link->network->ndisc >= 0)
58 return link->network->ndisc;
3976c430 59
769f9744
YW
60 /* Disable if RADV is enabled. */
61 if (link_radv_enabled(link))
62 return false;
63
3976c430
YW
64 /* Accept RAs if IPv6 forwarding is disabled, and ignore RAs if IPv6 forwarding is enabled. */
65 int t = link_get_ip_forwarding(link, AF_INET6);
66 if (t >= 0)
67 return !t;
68
69 /* Otherwise, defaults to true. */
70 return true;
3773eb54
YW
71}
72
52672db3 73void network_adjust_ndisc(Network *network) {
3773eb54
YW
74 assert(network);
75
76 if (!FLAGS_SET(network->link_local, ADDRESS_FAMILY_IPV6)) {
52672db3 77 if (network->ndisc > 0)
f81ac115 78 log_warning("%s: IPv6AcceptRA= is enabled but IPv6 link-local addressing is disabled or not supported. "
3773eb54 79 "Disabling IPv6AcceptRA=.", network->filename);
52672db3 80 network->ndisc = false;
3773eb54
YW
81 }
82
75d26411
YW
83 /* When RouterAllowList=, PrefixAllowList= or RouteAllowList= are specified, then
84 * RouterDenyList=, PrefixDenyList= or RouteDenyList= are ignored, respectively. */
85 if (!set_isempty(network->ndisc_allow_listed_router))
86 network->ndisc_deny_listed_router = set_free_free(network->ndisc_deny_listed_router);
de6b6ff8
SS
87 if (!set_isempty(network->ndisc_allow_listed_prefix))
88 network->ndisc_deny_listed_prefix = set_free_free(network->ndisc_deny_listed_prefix);
89 if (!set_isempty(network->ndisc_allow_listed_route_prefix))
90 network->ndisc_deny_listed_route_prefix = set_free_free(network->ndisc_deny_listed_route_prefix);
062c020f
YW
91}
92
3b6a3bde 93static int ndisc_check_ready(Link *link);
50550722 94
3b6a3bde
YW
95static int ndisc_address_ready_callback(Address *address) {
96 Address *a;
50550722 97
3b6a3bde
YW
98 assert(address);
99 assert(address->link);
50550722 100
3b6a3bde
YW
101 SET_FOREACH(a, address->link->addresses)
102 if (a->source == NETWORK_CONFIG_SOURCE_NDISC)
103 a->callback = NULL;
50550722 104
3b6a3bde
YW
105 return ndisc_check_ready(address->link);
106}
50550722 107
3b6a3bde
YW
108static int ndisc_check_ready(Link *link) {
109 bool found = false, ready = false;
110 Address *address;
50550722 111
3b6a3bde 112 assert(link);
50550722 113
3b6a3bde
YW
114 if (link->ndisc_messages > 0) {
115 log_link_debug(link, "%s(): SLAAC addresses and routes are not set.", __func__);
116 return 0;
117 }
50550722 118
3b6a3bde
YW
119 SET_FOREACH(address, link->addresses) {
120 if (address->source != NETWORK_CONFIG_SOURCE_NDISC)
121 continue;
50550722 122
3b6a3bde 123 found = true;
50550722 124
3b6a3bde
YW
125 if (address_is_ready(address)) {
126 ready = true;
127 break;
50550722 128 }
3b6a3bde 129 }
50550722 130
3b6a3bde
YW
131 if (found && !ready) {
132 SET_FOREACH(address, link->addresses)
133 if (address->source == NETWORK_CONFIG_SOURCE_NDISC)
134 address->callback = ndisc_address_ready_callback;
50550722 135
3b6a3bde
YW
136 log_link_debug(link, "%s(): no SLAAC address is ready.", __func__);
137 return 0;
50550722
YW
138 }
139
3b6a3bde
YW
140 link->ndisc_configured = true;
141 log_link_debug(link, "SLAAC addresses and routes set.");
50550722 142
3b6a3bde
YW
143 link_check_ready(link);
144 return 0;
50550722
YW
145}
146
80d62d4f 147static int ndisc_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Request *req, Link *link, Route *route) {
3b015d40
TG
148 int r;
149
150 assert(link);
3b015d40 151
2c0b49ba 152 r = route_configure_handler_internal(rtnl, m, link, route, "Could not set NDisc route");
5a07fa9d
YW
153 if (r <= 0)
154 return r;
3b015d40 155
3b6a3bde 156 r = ndisc_check_ready(link);
50550722 157 if (r < 0)
3b6a3bde 158 link_enter_failed(link);
76c5a0f2 159
3b6a3bde 160 return 1;
76c5a0f2
YW
161}
162
6f812d28
YW
163static void ndisc_set_route_priority(Link *link, Route *route) {
164 assert(link);
165 assert(route);
166
167 if (route->priority_set)
168 return; /* explicitly configured. */
169
170 switch (route->pref) {
171 case SD_NDISC_PREFERENCE_LOW:
52672db3 172 route->priority = link->network->ndisc_route_metric_low;
6f812d28
YW
173 break;
174 case SD_NDISC_PREFERENCE_MEDIUM:
52672db3 175 route->priority = link->network->ndisc_route_metric_medium;
6f812d28
YW
176 break;
177 case SD_NDISC_PREFERENCE_HIGH:
52672db3 178 route->priority = link->network->ndisc_route_metric_high;
6f812d28
YW
179 break;
180 default:
181 assert_not_reached();
182 }
183}
184
33cab1d4 185static int ndisc_request_route(Route *route, Link *link) {
76c5a0f2
YW
186 int r;
187
188 assert(route);
189 assert(link);
8d01e44c 190 assert(link->manager);
f95fb199 191 assert(link->network);
3b6a3bde
YW
192
193 route->source = NETWORK_CONFIG_SOURCE_NDISC;
33cab1d4 194
429dc05a 195 if (!route->table_set)
52672db3 196 route->table = link_get_ndisc_route_table(link);
2e73aa50 197
52672db3 198 r = route_metric_set(&route->metric, RTAX_QUICKACK, link->network->ndisc_quickack);
ebf4fa1e
YW
199 if (r < 0)
200 return r;
f141b2c0 201
2f542fc3
YW
202 r = route_adjust_nexthops(route, link);
203 if (r < 0)
204 return r;
205
972f1d17
YW
206 uint8_t pref, pref_original = route->pref;
207 FOREACH_ARGUMENT(pref, SD_NDISC_PREFERENCE_LOW, SD_NDISC_PREFERENCE_MEDIUM, SD_NDISC_PREFERENCE_HIGH) {
208 Route *existing;
209 Request *req;
210
211 /* If the preference is specified by the user config (that is, for semi-static routes),
212 * rather than RA, then only search conflicting routes that have the same preference. */
213 if (route->pref_set && pref != pref_original)
214 continue;
215
216 route->pref = pref;
217 ndisc_set_route_priority(link, route);
218
219 /* Note, here do not call route_remove_and_cancel() with 'route' directly, otherwise
220 * existing route(s) may be removed needlessly. */
221
222 if (route_get(link->manager, route, &existing) >= 0) {
223 /* Found an existing route that may conflict with this route. */
224 if (!route_can_update(existing, route)) {
225 log_link_debug(link, "Found an existing route that conflicts with new route based on a received RA, removing.");
226 r = route_remove_and_cancel(existing, link->manager);
227 if (r < 0)
228 return r;
229 }
230 }
231
232 if (route_get_request(link->manager, route, &req) >= 0) {
233 existing = ASSERT_PTR(req->userdata);
234 if (!route_can_update(existing, route)) {
235 log_link_debug(link, "Found a pending route request that conflicts with new request based on a received RA, cancelling.");
236 r = route_remove_and_cancel(existing, link->manager);
237 if (r < 0)
238 return r;
239 }
240 }
241 }
242
243 /* The preference (and priority) may be changed in the above loop. Restore it. */
244 route->pref = pref_original;
245 ndisc_set_route_priority(link, route);
246
33cab1d4 247 bool is_new = route_get(link->manager, route, NULL) < 0;
d9a95033 248
5a18697d 249 r = link_request_route(link, route, &link->ndisc_messages, ndisc_route_handler);
d9a95033
YW
250 if (r < 0)
251 return r;
252 if (r > 0 && is_new)
3b6a3bde 253 link->ndisc_configured = false;
50550722 254
d9a95033 255 return 0;
50550722
YW
256}
257
33cab1d4
YW
258static int ndisc_request_router_route(Route *route, Link *link, sd_ndisc_router *rt) {
259 int r;
260
261 assert(route);
262 assert(link);
263 assert(rt);
264
265 r = sd_ndisc_router_get_sender_address(rt, &route->provider.in6);
266 if (r < 0)
267 return r;
268
269 if (!route->protocol_set)
270 route->protocol = RTPROT_RA;
271
272 return ndisc_request_route(route, link);
273}
274
479d3e19
YW
275static int ndisc_remove_route(Route *route, Link *link) {
276 int r;
277
278 assert(route);
279 assert(link);
280 assert(link->manager);
281
282 ndisc_set_route_priority(link, route);
283
284 if (!route->table_set)
52672db3 285 route->table = link_get_ndisc_route_table(link);
479d3e19
YW
286
287 r = route_adjust_nexthops(route, link);
288 if (r < 0)
289 return r;
290
291 if (route->pref_set) {
292 ndisc_set_route_priority(link, route);
293 return route_remove_and_cancel(route, link->manager);
294 }
295
296 uint8_t pref;
297 FOREACH_ARGUMENT(pref, SD_NDISC_PREFERENCE_LOW, SD_NDISC_PREFERENCE_MEDIUM, SD_NDISC_PREFERENCE_HIGH) {
298 route->pref = pref;
299 ndisc_set_route_priority(link, route);
300 r = route_remove_and_cancel(route, link->manager);
301 if (r < 0)
302 return r;
303 }
304
305 return 0;
306}
307
80d62d4f 308static int ndisc_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Request *req, Link *link, Address *address) {
73854ba1
YW
309 int r;
310
311 assert(link);
73854ba1 312
5a07fa9d
YW
313 r = address_configure_handler_internal(rtnl, m, link, "Could not set NDisc address");
314 if (r <= 0)
315 return r;
73854ba1 316
3b6a3bde 317 r = ndisc_check_ready(link);
50550722 318 if (r < 0)
3b6a3bde 319 link_enter_failed(link);
69203fba 320
3b6a3bde 321 return 1;
69203fba
YW
322}
323
2d4c9c42 324static int ndisc_request_address(Address *address, Link *link) {
d9a95033 325 bool is_new;
76c5a0f2
YW
326 int r;
327
328 assert(address);
329 assert(link);
76c5a0f2 330
2d4c9c42 331 address->source = NETWORK_CONFIG_SOURCE_NDISC;
76c5a0f2 332
4b3590c3
TM
333 r = free_and_strdup_warn(&address->netlabel, link->network->ndisc_netlabel);
334 if (r < 0)
335 return r;
336
e720ad88
YW
337 Address *existing;
338 if (address_get_harder(link, address, &existing) < 0)
339 is_new = true;
340 else if (address_can_update(existing, address))
341 is_new = false;
342 else if (existing->source == NETWORK_CONFIG_SOURCE_DHCP6) {
343 /* SLAAC address is preferred over DHCPv6 address. */
344 log_link_debug(link, "Conflicting DHCPv6 address %s exists, removing.",
345 IN_ADDR_PREFIX_TO_STRING(existing->family, &existing->in_addr, existing->prefixlen));
346 r = address_remove(existing, link);
347 if (r < 0)
348 return r;
349
350 is_new = true;
351 } else {
352 /* Conflicting static address is configured?? */
353 log_link_debug(link, "Conflicting address %s exists, ignoring request.",
354 IN_ADDR_PREFIX_TO_STRING(existing->family, &existing->in_addr, existing->prefixlen));
355 return 0;
356 }
d9a95033 357
f60e6558 358 r = link_request_address(link, address, &link->ndisc_messages,
d9a95033
YW
359 ndisc_address_handler, NULL);
360 if (r < 0)
361 return r;
362 if (r > 0 && is_new)
3b6a3bde 363 link->ndisc_configured = false;
3b6a3bde 364
d9a95033 365 return 0;
76c5a0f2
YW
366}
367
e14679ff
YW
368int ndisc_reconfigure_address(Address *address, Link *link) {
369 int r;
370
371 assert(address);
372 assert(address->source == NETWORK_CONFIG_SOURCE_NDISC);
373 assert(link);
374
375 r = regenerate_address(address, link);
376 if (r <= 0)
377 return r;
378
2d4c9c42 379 r = ndisc_request_address(address, link);
e14679ff
YW
380 if (r < 0)
381 return r;
382
383 if (!link->ndisc_configured)
384 link_set_state(link, LINK_STATE_CONFIGURING);
385
386 link_check_ready(link);
387 return 0;
388}
389
6df00594
YW
390static int ndisc_redirect_route_new(sd_ndisc_redirect *rd, Route **ret) {
391 _cleanup_(route_unrefp) Route *route = NULL;
392 struct in6_addr gateway, destination;
393 int r;
394
395 assert(rd);
396 assert(ret);
397
398 r = sd_ndisc_redirect_get_target_address(rd, &gateway);
399 if (r < 0)
400 return r;
401
402 r = sd_ndisc_redirect_get_destination_address(rd, &destination);
403 if (r < 0)
404 return r;
405
406 r = route_new(&route);
407 if (r < 0)
408 return r;
409
410 route->family = AF_INET6;
411 if (!in6_addr_equal(&gateway, &destination)) {
412 route->nexthop.gw.in6 = gateway;
413 route->nexthop.family = AF_INET6;
414 }
415 route->dst.in6 = destination;
416 route->dst_prefixlen = 128;
33cab1d4
YW
417 route->protocol = RTPROT_REDIRECT;
418
419 r = sd_ndisc_redirect_get_sender_address(rd, &route->provider.in6);
420 if (r < 0)
421 return r;
6df00594
YW
422
423 *ret = TAKE_PTR(route);
424 return 0;
425}
426
6df00594
YW
427static int ndisc_remove_redirect_route(Link *link, sd_ndisc_redirect *rd) {
428 _cleanup_(route_unrefp) Route *route = NULL;
429 int r;
430
431 assert(link);
432 assert(rd);
433
434 r = ndisc_redirect_route_new(rd, &route);
435 if (r < 0)
436 return r;
437
438 return ndisc_remove_route(route, link);
439}
440
441static void ndisc_redirect_hash_func(const sd_ndisc_redirect *x, struct siphash *state) {
442 struct in6_addr dest = {};
443
444 assert(x);
445 assert(state);
446
447 (void) sd_ndisc_redirect_get_destination_address((sd_ndisc_redirect*) x, &dest);
448
449 siphash24_compress_typesafe(dest, state);
450}
451
452static int ndisc_redirect_compare_func(const sd_ndisc_redirect *x, const sd_ndisc_redirect *y) {
453 struct in6_addr dest_x = {}, dest_y = {};
454
455 assert(x);
456 assert(y);
457
458 (void) sd_ndisc_redirect_get_destination_address((sd_ndisc_redirect*) x, &dest_x);
459 (void) sd_ndisc_redirect_get_destination_address((sd_ndisc_redirect*) y, &dest_y);
460
461 return memcmp(&dest_x, &dest_y, sizeof(dest_x));
462}
463
464DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(
465 ndisc_redirect_hash_ops,
466 sd_ndisc_redirect,
467 ndisc_redirect_hash_func,
468 ndisc_redirect_compare_func,
469 sd_ndisc_redirect_unref);
470
471static int ndisc_redirect_equal(sd_ndisc_redirect *x, sd_ndisc_redirect *y) {
472 struct in6_addr a, b;
473 int r;
474
475 assert(x);
476 assert(y);
477
478 r = sd_ndisc_redirect_get_destination_address(x, &a);
479 if (r < 0)
480 return r;
481
482 r = sd_ndisc_redirect_get_destination_address(y, &b);
483 if (r < 0)
484 return r;
485
486 if (!in6_addr_equal(&a, &b))
487 return false;
488
489 r = sd_ndisc_redirect_get_target_address(x, &a);
490 if (r < 0)
491 return r;
492
493 r = sd_ndisc_redirect_get_target_address(y, &b);
494 if (r < 0)
495 return r;
496
497 return in6_addr_equal(&a, &b);
498}
499
500static int ndisc_redirect_drop_conflict(Link *link, sd_ndisc_redirect *rd) {
501 _cleanup_(sd_ndisc_redirect_unrefp) sd_ndisc_redirect *existing = NULL;
502 int r;
503
504 assert(link);
505 assert(rd);
506
507 existing = set_remove(link->ndisc_redirects, rd);
508 if (!existing)
509 return 0;
510
511 r = ndisc_redirect_equal(rd, existing);
512 if (r != 0)
513 return r;
514
515 return ndisc_remove_redirect_route(link, existing);
516}
517
f7681475 518static int ndisc_redirect_verify_sender(Link *link, sd_ndisc_redirect *rd) {
9944629e 519 sd_ndisc_redirect *existing;
6df00594 520 struct in6_addr router, sender;
6df00594
YW
521 int r;
522
523 assert(link);
6df00594
YW
524 assert(rd);
525
9944629e
YW
526 /* RFC 4861 section 8.1
527 * The IP source address of the Redirect is the same as the current first-hop router for the specified
528 * ICMP Destination Address. */
6df00594 529
9944629e 530 r = sd_ndisc_redirect_get_sender_address(rd, &sender);
6df00594
YW
531 if (r < 0)
532 return r;
533
9944629e
YW
534 existing = set_get(link->ndisc_redirects, rd);
535 if (existing) {
536 struct in6_addr target, dest;
6df00594 537
9944629e 538 /* If we have received Redirect message for the host, the sender must be the previous target. */
6df00594 539
9944629e
YW
540 r = sd_ndisc_redirect_get_target_address(existing, &target);
541 if (r < 0)
542 return r;
6df00594 543
9944629e
YW
544 if (in6_addr_equal(&sender, &target))
545 return true;
546
547 /* If the existing redirect route is on-link, that is, the destination and target address are
548 * equivalent, then also accept Redirect message from the current default router. This is not
549 * mentioned by the RFC, but without this, we cannot update on-link redirect route. */
550 r = sd_ndisc_redirect_get_destination_address(existing, &dest);
551 if (r < 0)
552 return r;
553
554 if (!in6_addr_equal(&dest, &target))
555 return false;
556 }
557
558 if (!link->ndisc_default_router)
559 return false;
560
561 r = sd_ndisc_router_get_sender_address(link->ndisc_default_router, &router);
6df00594
YW
562 if (r < 0)
563 return r;
564
f7681475
YW
565 /* The sender must be the default router. */
566 return in6_addr_equal(&sender, &router);
567}
568
569static int ndisc_redirect_handler(Link *link, sd_ndisc_redirect *rd) {
570 int r;
571
572 assert(link);
573 assert(link->network);
574 assert(rd);
6df00594 575
f7681475
YW
576 if (!link->network->ndisc_use_redirect)
577 return 0;
578
579 r = ndisc_redirect_verify_sender(link, rd);
580 if (r <= 0)
581 return r;
6df00594 582
d9688518 583 /* First, drop conflicting redirect route, if exists. */
6df00594
YW
584 r = ndisc_redirect_drop_conflict(link, rd);
585 if (r < 0)
586 return r;
587
d9688518 588 /* Then, remember the received message. */
6df00594
YW
589 r = set_ensure_put(&link->ndisc_redirects, &ndisc_redirect_hash_ops, rd);
590 if (r < 0)
591 return r;
592
593 sd_ndisc_redirect_ref(rd);
594
d9688518
YW
595 /* Finally, request the corresponding route. */
596 _cleanup_(route_unrefp) Route *route = NULL;
597 r = ndisc_redirect_route_new(rd, &route);
598 if (r < 0)
599 return r;
600
601 return ndisc_request_route(route, link);
6df00594
YW
602}
603
9944629e
YW
604static int ndisc_drop_redirect(Link *link, const struct in6_addr *router) {
605 int r, ret = 0;
6df00594
YW
606
607 assert(link);
608
6df00594
YW
609 sd_ndisc_redirect *rd;
610 SET_FOREACH(rd, link->ndisc_redirects) {
9944629e
YW
611 if (router) {
612 struct in6_addr target;
6df00594 613
9944629e 614 r = sd_ndisc_redirect_get_target_address(rd, &target);
6df00594
YW
615 if (r < 0)
616 return r;
9944629e
YW
617
618 if (!in6_addr_equal(&target, router))
619 continue;
6df00594
YW
620 }
621
9944629e
YW
622 r = ndisc_remove_redirect_route(link, rd);
623 if (r < 0)
624 RET_GATHER(ret, log_link_warning_errno(link, r, "Failed to remove redirect route, ignoring: %m"));
625
6df00594
YW
626 sd_ndisc_redirect_unref(set_remove(link->ndisc_redirects, rd));
627 }
628
9944629e 629 return ret;
6df00594
YW
630}
631
632static int ndisc_update_redirect_sender(Link *link, const struct in6_addr *original_address, const struct in6_addr *current_address) {
633 int r;
634
635 assert(link);
636 assert(original_address);
637 assert(current_address);
638
639 sd_ndisc_redirect *rd;
640 SET_FOREACH(rd, link->ndisc_redirects) {
641 struct in6_addr sender;
642
643 r = sd_ndisc_redirect_get_sender_address(rd, &sender);
644 if (r < 0)
645 return r;
646
647 if (!in6_addr_equal(&sender, original_address))
648 continue;
649
650 r = sd_ndisc_redirect_set_sender_address(rd, current_address);
651 if (r < 0)
652 return r;
653 }
654
655 return 0;
656}
657
479d3e19
YW
658static int ndisc_router_drop_default(Link *link, sd_ndisc_router *rt) {
659 _cleanup_(route_unrefp) Route *route = NULL;
660 struct in6_addr gateway;
661 int r;
662
663 assert(link);
664 assert(link->network);
665 assert(rt);
666
9ca04752 667 r = sd_ndisc_router_get_sender_address(rt, &gateway);
479d3e19
YW
668 if (r < 0)
669 return log_link_warning_errno(link, r, "Failed to get router address from RA: %m");
670
671 r = route_new(&route);
672 if (r < 0)
673 return log_oom();
674
675 route->family = AF_INET6;
676 route->nexthop.family = AF_INET6;
677 route->nexthop.gw.in6 = gateway;
678
679 r = ndisc_remove_route(route, link);
680 if (r < 0)
681 return log_link_warning_errno(link, r, "Failed to remove the default gateway configured by RA: %m");
682
683 Route *route_gw;
684 HASHMAP_FOREACH(route_gw, link->network->routes_by_section) {
685 _cleanup_(route_unrefp) Route *tmp = NULL;
686
687 if (!route_gw->gateway_from_dhcp_or_ra)
688 continue;
689
690 if (route_gw->nexthop.family != AF_INET6)
691 continue;
692
693 r = route_dup(route_gw, NULL, &tmp);
694 if (r < 0)
695 return r;
696
697 tmp->nexthop.gw.in6 = gateway;
698
699 r = ndisc_remove_route(tmp, link);
700 if (r < 0)
701 return log_link_warning_errno(link, r, "Could not remove semi-static gateway: %m");
702 }
703
704 return 0;
705}
706
d5017c84 707static int ndisc_router_process_default(Link *link, sd_ndisc_router *rt) {
6197db53 708 usec_t lifetime_usec;
b8ce3b44 709 struct in6_addr gateway;
9ca04752 710 uint8_t preference;
13e8a49a 711 int r;
3b015d40 712
3b015d40 713 assert(link);
610c0db1 714 assert(link->network);
1e7a0e21 715 assert(rt);
3b015d40 716
479d3e19
YW
717 /* If the router lifetime is zero, the router should not be used as the default gateway. */
718 r = sd_ndisc_router_get_lifetime(rt, NULL);
719 if (r < 0)
720 return r;
721 if (r == 0)
722 return ndisc_router_drop_default(link, rt);
723
52672db3 724 if (!link->network->ndisc_use_gateway &&
610c0db1
YW
725 hashmap_isempty(link->network->routes_by_section))
726 return 0;
727
6197db53 728 r = sd_ndisc_router_get_lifetime_timestamp(rt, CLOCK_BOOTTIME, &lifetime_usec);
d5017c84 729 if (r < 0)
2c5bca17 730 return log_link_warning_errno(link, r, "Failed to get gateway lifetime from RA: %m");
d5017c84 731
9ca04752 732 r = sd_ndisc_router_get_sender_address(rt, &gateway);
d5017c84 733 if (r < 0)
2c5bca17 734 return log_link_warning_errno(link, r, "Failed to get gateway address from RA: %m");
1e7a0e21
LP
735
736 r = sd_ndisc_router_get_preference(rt, &preference);
d5017c84 737 if (r < 0)
34acdf90 738 return log_link_warning_errno(link, r, "Failed to get router preference from RA: %m");
1e7a0e21 739
52672db3 740 if (link->network->ndisc_use_gateway) {
74c301b9 741 _cleanup_(route_unrefp) Route *route = NULL;
1e7a0e21 742
610c0db1
YW
743 r = route_new(&route);
744 if (r < 0)
745 return log_oom();
1e7a0e21 746
610c0db1
YW
747 route->family = AF_INET6;
748 route->pref = preference;
054b8c28
YW
749 route->nexthop.family = AF_INET6;
750 route->nexthop.gw.in6 = gateway;
610c0db1 751 route->lifetime_usec = lifetime_usec;
610c0db1 752
33cab1d4 753 r = ndisc_request_router_route(route, link, rt);
610c0db1 754 if (r < 0)
2c5bca17 755 return log_link_warning_errno(link, r, "Could not request default route: %m");
610c0db1 756 }
d5017c84 757
1985c54f 758 Route *route_gw;
2a54a044 759 HASHMAP_FOREACH(route_gw, link->network->routes_by_section) {
74c301b9 760 _cleanup_(route_unrefp) Route *route = NULL;
610c0db1 761
1a3a6309 762 if (!route_gw->gateway_from_dhcp_or_ra)
1985c54f
YW
763 continue;
764
054b8c28 765 if (route_gw->nexthop.family != AF_INET6)
1985c54f
YW
766 continue;
767
413ea20a 768 r = route_dup(route_gw, NULL, &route);
76c5a0f2
YW
769 if (r < 0)
770 return r;
771
054b8c28 772 route->nexthop.gw.in6 = gateway;
76c5a0f2
YW
773 if (!route->pref_set)
774 route->pref = preference;
91fc5135 775 route->lifetime_usec = lifetime_usec;
76c5a0f2 776
33cab1d4 777 r = ndisc_request_router_route(route, link, rt);
13e8a49a 778 if (r < 0)
2c5bca17 779 return log_link_warning_errno(link, r, "Could not request gateway: %m");
1985c54f
YW
780 }
781
0f9a2b80
YW
782 return 0;
783}
91750028 784
b03d7291
YW
785static int update_default_router_address(Link *link, const struct in6_addr *original_address, const struct in6_addr *current_address) {
786 struct in6_addr a;
787 int r;
788
789 assert(link);
790 assert(original_address);
791 assert(current_address);
792
793 if (!link->ndisc_default_router)
794 return 0;
795
796 r = sd_ndisc_router_get_sender_address(link->ndisc_default_router, &a);
797 if (r < 0)
798 return r;
799
800 if (!in6_addr_equal(&a, original_address))
801 return 0;
802
803 return sd_ndisc_router_set_sender_address(link->ndisc_default_router, current_address);
804}
805
806static int drop_default_router(Link *link, const struct in6_addr *router, usec_t timestamp_usec) {
807 usec_t lifetime_usec;
808 int r;
809
810 assert(link);
811
812 if (!link->ndisc_default_router)
813 return 0;
814
815 if (router) {
816 struct in6_addr a;
817
818 r = sd_ndisc_router_get_sender_address(link->ndisc_default_router, &a);
819 if (r < 0)
820 return r;
821
822 if (!in6_addr_equal(&a, router))
823 return 0;
824 }
825
826 r = sd_ndisc_router_get_lifetime_timestamp(link->ndisc_default_router, CLOCK_BOOTTIME, &lifetime_usec);
827 if (r < 0)
828 return r;
829
830 if (lifetime_usec > timestamp_usec)
831 return 0;
832
833 link->ndisc_default_router = sd_ndisc_router_unref(link->ndisc_default_router);
834 return 0;
835}
836
837static int accept_default_router(sd_ndisc_router *new_router, sd_ndisc_router *existing_router) {
838 usec_t lifetime_usec;
839 struct in6_addr a, b;
840 uint8_t p, q;
841 int r;
842
843 assert(new_router);
844
845 r = sd_ndisc_router_get_lifetime(new_router, &lifetime_usec);
846 if (r < 0)
847 return r;
848
849 if (lifetime_usec == 0)
850 return false; /* Received a new RA about revoking the router, ignoring. */
851
852 if (!existing_router)
853 return true;
854
855 /* lifetime of the existing router is already checked in ndisc_drop_outdated(). */
856
857 r = sd_ndisc_router_get_sender_address(new_router, &a);
858 if (r < 0)
859 return r;
860
861 r = sd_ndisc_router_get_sender_address(existing_router, &b);
862 if (r < 0)
863 return r;
864
865 if (in6_addr_equal(&a, &b))
866 return true; /* Received a new RA from the remembered router. Replace the remembered RA. */
867
868 r = sd_ndisc_router_get_preference(new_router, &p);
869 if (r < 0)
870 return r;
871
872 r = sd_ndisc_router_get_preference(existing_router, &q);
873 if (r < 0)
874 return r;
875
876 if (p == q)
877 return true;
878
879 if (p == SD_NDISC_PREFERENCE_HIGH)
880 return true;
881
882 if (p == SD_NDISC_PREFERENCE_MEDIUM && q == SD_NDISC_PREFERENCE_LOW)
883 return true;
884
885 return false;
886}
887
888static int ndisc_remember_default_router(Link *link, sd_ndisc_router *rt) {
889 int r;
890
891 assert(link);
892 assert(rt);
893
894 r = accept_default_router(rt, link->ndisc_default_router);
895 if (r <= 0)
896 return r;
897
898 sd_ndisc_router_ref(rt);
899 sd_ndisc_router_unref(link->ndisc_default_router);
900 link->ndisc_default_router = rt;
901
902 return 1; /* The received router advertisement is from the default router. */
903}
904
1452d497
YW
905static int ndisc_router_process_reachable_time(Link *link, sd_ndisc_router *rt) {
906 usec_t reachable_time, msec;
907 int r;
908
909 assert(link);
910 assert(link->network);
911 assert(rt);
912
52672db3 913 if (!link->network->ndisc_use_reachable_time)
1452d497
YW
914 return 0;
915
1452d497
YW
916 r = sd_ndisc_router_get_reachable_time(rt, &reachable_time);
917 if (r < 0)
918 return log_link_warning_errno(link, r, "Failed to get reachable time from RA: %m");
919
920 /* 0 is the unspecified value and must not be set (see RFC4861, 6.3.4) */
921 if (!timestamp_is_set(reachable_time))
922 return 0;
923
924 msec = DIV_ROUND_UP(reachable_time, USEC_PER_MSEC);
925 if (msec <= 0 || msec > UINT32_MAX) {
926 log_link_debug(link, "Failed to get reachable time from RA - out of range (%"PRIu64"), ignoring", msec);
927 return 0;
928 }
929
930 /* Set the reachable time for Neighbor Solicitations. */
931 r = sysctl_write_ip_neighbor_property_uint32(AF_INET6, link->ifname, "base_reachable_time_ms", (uint32_t) msec);
932 if (r < 0)
933 log_link_warning_errno(link, r, "Failed to apply neighbor reachable time (%"PRIu64"), ignoring: %m", msec);
934
935 return 0;
936}
937
d4c8de21
MM
938static int ndisc_router_process_retransmission_time(Link *link, sd_ndisc_router *rt) {
939 usec_t retrans_time, msec;
940 int r;
941
942 assert(link);
943 assert(link->network);
944 assert(rt);
945
52672db3 946 if (!link->network->ndisc_use_retransmission_time)
d4c8de21
MM
947 return 0;
948
949 r = sd_ndisc_router_get_retransmission_time(rt, &retrans_time);
b409ac6c
YW
950 if (r < 0)
951 return log_link_warning_errno(link, r, "Failed to get retransmission time from RA: %m");
d4c8de21
MM
952
953 /* 0 is the unspecified value and must not be set (see RFC4861, 6.3.4) */
954 if (!timestamp_is_set(retrans_time))
955 return 0;
956
957 msec = DIV_ROUND_UP(retrans_time, USEC_PER_MSEC);
958 if (msec <= 0 || msec > UINT32_MAX) {
959 log_link_debug(link, "Failed to get retransmission time from RA - out of range (%"PRIu64"), ignoring", msec);
960 return 0;
961 }
962
6a8026e8 963 /* Set the retransmission time for Neighbor Solicitations. */
d4c8de21
MM
964 r = sysctl_write_ip_neighbor_property_uint32(AF_INET6, link->ifname, "retrans_time_ms", (uint32_t) msec);
965 if (r < 0)
ffef01ac 966 log_link_warning_errno(link, r, "Failed to apply neighbor retransmission time (%"PRIu64"), ignoring: %m", msec);
d4c8de21
MM
967
968 return 0;
969}
970
b15ed2be
MM
971static int ndisc_router_process_hop_limit(Link *link, sd_ndisc_router *rt) {
972 uint8_t hop_limit;
973 int r;
974
975 assert(link);
976 assert(link->network);
977 assert(rt);
978
52672db3 979 if (!link->network->ndisc_use_hop_limit)
b15ed2be
MM
980 return 0;
981
982 r = sd_ndisc_router_get_hop_limit(rt, &hop_limit);
983 if (r < 0)
984 return log_link_warning_errno(link, r, "Failed to get hop limit from RA: %m");
985
986 /* 0 is the unspecified value and must not be set (see RFC4861, 6.3.4):
987 *
988 * A Router Advertisement field (e.g., Cur Hop Limit, Reachable Time, and Retrans Timer) may contain
989 * a value denoting that it is unspecified. In such cases, the parameter should be ignored and the
990 * host should continue using whatever value it is already using. In particular, a host MUST NOT
991 * interpret the unspecified value as meaning change back to the default value that was in use before
992 * the first Router Advertisement was received.
993 *
994 * If the received Cur Hop Limit value is non-zero, the host SHOULD set
995 * its CurHopLimit variable to the received value.*/
996 if (hop_limit <= 0)
997 return 0;
998
999 r = sysctl_write_ip_property_uint32(AF_INET6, link->ifname, "hop_limit", (uint32_t) hop_limit);
1000 if (r < 0)
1001 log_link_warning_errno(link, r, "Failed to apply hop_limit (%u), ignoring: %m", hop_limit);
1002
1003 return 0;
1004}
1005
8c9ef90b
YW
1006static int ndisc_router_process_mtu(Link *link, sd_ndisc_router *rt) {
1007 uint32_t mtu;
1008 int r;
1009
1010 assert(link);
1011 assert(link->network);
1012 assert(rt);
1013
1014 if (!link->network->ndisc_use_mtu)
1015 return 0;
1016
8c9ef90b
YW
1017 r = sd_ndisc_router_get_mtu(rt, &mtu);
1018 if (r == -ENODATA)
1019 return 0;
1020 if (r < 0)
1021 return log_link_warning_errno(link, r, "Failed to get MTU from RA: %m");
1022
1023 link->ndisc_mtu = mtu;
1024
1025 r = link_set_ipv6_mtu(link, LOG_DEBUG);
1026 if (r < 0)
1027 log_link_warning_errno(link, r, "Failed to apply IPv6 MTU (%"PRIu32"), ignoring: %m", mtu);
1028
1029 return 0;
1030}
1031
e42a7480
YW
1032static int ndisc_address_set_lifetime(Address *address, Link *link, sd_ndisc_router *rt) {
1033 Address *existing;
1034 usec_t t;
1035 int r;
1036
1037 assert(address);
1038 assert(link);
1039 assert(rt);
1040
1041 /* This is mostly based on RFC 4862 section 5.5.3 (e). However, the definition of 'RemainingLifetime'
4dbcda1c 1042 * is ambiguous, and there is no clear explanation when the address is not assigned yet. If we assume
e42a7480
YW
1043 * that 'RemainingLifetime' is zero in that case, then IPv6 Core Conformance test [v6LC.3.2.5 Part C]
1044 * fails. So, in such case, we skip the conditions about 'RemainingLifetime'. */
1045
1046 r = sd_ndisc_router_prefix_get_valid_lifetime_timestamp(rt, CLOCK_BOOTTIME, &address->lifetime_valid_usec);
1047 if (r < 0)
1048 return r;
1049
1050 r = sd_ndisc_router_prefix_get_preferred_lifetime_timestamp(rt, CLOCK_BOOTTIME, &address->lifetime_preferred_usec);
1051 if (r < 0)
1052 return r;
1053
1054 /* RFC 4862 section 5.5.3 (e)
1055 * 1. If the received Valid Lifetime is greater than 2 hours or greater than RemainingLifetime,
1056 * set the valid lifetime of the corresponding address to the advertised Valid Lifetime. */
1057 r = sd_ndisc_router_prefix_get_valid_lifetime(rt, &t);
1058 if (r < 0)
1059 return r;
1060
1061 if (t > 2 * USEC_PER_HOUR)
1062 return 0;
1063
68adffed
MM
1064 if (address_get(link, address, &existing) < 0 || existing->source != NETWORK_CONFIG_SOURCE_NDISC)
1065 return 0;
1066
1067 if (address->lifetime_valid_usec > existing->lifetime_valid_usec)
1068 return 0;
1069
1070 /* 2. If RemainingLifetime is less than or equal to 2 hours, ignore the Prefix Information option
1071 * with regards to the valid lifetime, unless the Router Advertisement from which this option was
1072 * obtained has been authenticated (e.g., via Secure Neighbor Discovery [RFC3971]). If the Router
1073 * Advertisement was authenticated, the valid lifetime of the corresponding address should be set
1074 * to the Valid Lifetime in the received option.
1075 *
1076 * Currently, authentication is not supported. So check the lifetime of the existing address. */
e42a7480
YW
1077 r = sd_ndisc_router_get_timestamp(rt, CLOCK_BOOTTIME, &t);
1078 if (r < 0)
1079 return r;
1080
68adffed
MM
1081 if (existing->lifetime_valid_usec <= usec_add(t, 2 * USEC_PER_HOUR)) {
1082 address->lifetime_valid_usec = existing->lifetime_valid_usec;
1083 return 0;
e42a7480
YW
1084 }
1085
1086 /* 3. Otherwise, reset the valid lifetime of the corresponding address to 2 hours. */
1087 address->lifetime_valid_usec = usec_add(t, 2 * USEC_PER_HOUR);
1088 return 0;
1089}
1090
d5017c84 1091static int ndisc_router_process_autonomous_prefix(Link *link, sd_ndisc_router *rt) {
6197db53 1092 usec_t lifetime_valid_usec, lifetime_preferred_usec;
2d4c9c42 1093 struct in6_addr prefix, router;
9ca04752 1094 uint8_t prefixlen;
1e7a0e21
LP
1095 int r;
1096
1097 assert(link);
fbdda4bb 1098 assert(link->network);
1e7a0e21
LP
1099 assert(rt);
1100
52672db3 1101 if (!link->network->ndisc_use_autonomous_prefix)
fbdda4bb
YW
1102 return 0;
1103
2d4c9c42
YW
1104 r = sd_ndisc_router_get_sender_address(rt, &router);
1105 if (r < 0)
1106 return log_link_warning_errno(link, r, "Failed to get router address: %m");
1107
151b8ea3
YW
1108 r = sd_ndisc_router_prefix_get_address(rt, &prefix);
1109 if (r < 0)
2c5bca17 1110 return log_link_warning_errno(link, r, "Failed to get prefix address: %m");
151b8ea3 1111
1e7a0e21 1112 r = sd_ndisc_router_prefix_get_prefixlen(rt, &prefixlen);
d5017c84 1113 if (r < 0)
2c5bca17 1114 return log_link_warning_errno(link, r, "Failed to get prefix length: %m");
1e7a0e21 1115
868bd1aa 1116 /* ndisc_generate_addresses() below requires the prefix length <= 64. */
151b8ea3 1117 if (prefixlen > 64) {
c71384a9
ZJS
1118 log_link_debug(link, "Prefix is longer than 64, ignoring autonomous prefix %s.",
1119 IN6_ADDR_PREFIX_TO_STRING(&prefix, prefixlen));
151b8ea3
YW
1120 return 0;
1121 }
1122
e42a7480 1123 r = sd_ndisc_router_prefix_get_valid_lifetime(rt, &lifetime_valid_usec);
d5017c84 1124 if (r < 0)
2c5bca17 1125 return log_link_warning_errno(link, r, "Failed to get prefix valid lifetime: %m");
1e7a0e21 1126
e42a7480 1127 r = sd_ndisc_router_prefix_get_preferred_lifetime(rt, &lifetime_preferred_usec);
d5017c84 1128 if (r < 0)
2c5bca17 1129 return log_link_warning_errno(link, r, "Failed to get prefix preferred lifetime: %m");
3b015d40 1130
e42a7480
YW
1131 /* RFC 4862 section 5.5.3 (c)
1132 * If the preferred lifetime is greater than the valid lifetime, silently ignore the Prefix
1133 * Information option. */
6197db53 1134 if (lifetime_preferred_usec > lifetime_valid_usec)
d5017c84 1135 return 0;
92bdc3ff 1136
e700e482
YW
1137 _cleanup_hashmap_free_ Hashmap *tokens_by_address = NULL;
1138 r = ndisc_generate_addresses(link, &prefix, prefixlen, &tokens_by_address);
5f506a55 1139 if (r < 0)
2c5bca17 1140 return log_link_warning_errno(link, r, "Failed to generate SLAAC addresses: %m");
c24c83dc 1141
e700e482
YW
1142 IPv6Token *token;
1143 struct in6_addr *a;
1144 HASHMAP_FOREACH_KEY(token, a, tokens_by_address) {
ebd96906 1145 _cleanup_(address_unrefp) Address *address = NULL;
13e8a49a 1146
76c5a0f2
YW
1147 r = address_new(&address);
1148 if (r < 0)
1149 return log_oom();
1150
2d4c9c42 1151 address->provider.in6 = router;
76c5a0f2 1152 address->family = AF_INET6;
25db3aea 1153 address->in_addr.in6 = *a;
76c5a0f2
YW
1154 address->prefixlen = prefixlen;
1155 address->flags = IFA_F_NOPREFIXROUTE|IFA_F_MANAGETEMPADDR;
e700e482 1156 address->token = ipv6_token_ref(token);
fe841414 1157
e42a7480
YW
1158 r = ndisc_address_set_lifetime(address, link, rt);
1159 if (r < 0)
1160 return log_link_warning_errno(link, r, "Failed to set lifetime of SLAAC address: %m");
1161
1162 assert(address->lifetime_preferred_usec <= address->lifetime_valid_usec);
1163
1164 r = ndisc_request_address(address, link);
1165 if (r < 0)
1166 return log_link_warning_errno(link, r, "Could not request SLAAC address: %m");
3b015d40 1167 }
d5017c84
YW
1168
1169 return 0;
3b015d40
TG
1170}
1171
d5017c84 1172static int ndisc_router_process_onlink_prefix(Link *link, sd_ndisc_router *rt) {
74c301b9 1173 _cleanup_(route_unrefp) Route *route = NULL;
9ca04752 1174 uint8_t prefixlen, preference;
6197db53 1175 usec_t lifetime_usec;
167c7ae5 1176 struct in6_addr prefix;
3b015d40
TG
1177 int r;
1178
3b015d40 1179 assert(link);
fbdda4bb 1180 assert(link->network);
1e7a0e21 1181 assert(rt);
3b015d40 1182
52672db3 1183 if (!link->network->ndisc_use_onlink_prefix)
fbdda4bb
YW
1184 return 0;
1185
6197db53 1186 r = sd_ndisc_router_prefix_get_valid_lifetime_timestamp(rt, CLOCK_BOOTTIME, &lifetime_usec);
09845af5 1187 if (r < 0)
2c5bca17 1188 return log_link_warning_errno(link, r, "Failed to get prefix lifetime: %m");
09845af5 1189
167c7ae5
YW
1190 r = sd_ndisc_router_prefix_get_address(rt, &prefix);
1191 if (r < 0)
2c5bca17 1192 return log_link_warning_errno(link, r, "Failed to get prefix address: %m");
167c7ae5 1193
1e7a0e21 1194 r = sd_ndisc_router_prefix_get_prefixlen(rt, &prefixlen);
d5017c84 1195 if (r < 0)
2c5bca17 1196 return log_link_warning_errno(link, r, "Failed to get prefix length: %m");
1e7a0e21 1197
f44eebd1
SS
1198 /* Prefix Information option does not have preference, hence we use the 'main' preference here */
1199 r = sd_ndisc_router_get_preference(rt, &preference);
1200 if (r < 0)
a8b0b848 1201 return log_link_warning_errno(link, r, "Failed to get router preference from RA: %m");
f44eebd1 1202
3b015d40 1203 r = route_new(&route);
d5017c84 1204 if (r < 0)
13e8a49a 1205 return log_oom();
3b015d40 1206
3b015d40 1207 route->family = AF_INET6;
167c7ae5 1208 route->dst.in6 = prefix;
3b015d40 1209 route->dst_prefixlen = prefixlen;
f44eebd1 1210 route->pref = preference;
6197db53 1211 route->lifetime_usec = lifetime_usec;
3b015d40 1212
b2633622
YW
1213 /* RFC 4861 section 6.3.4:
1214 * - If the prefix is not already present in the Prefix List, and the Prefix Information option's
1215 * Valid Lifetime field is non-zero, create a new entry for the prefix and initialize its
1216 * invalidation timer to the Valid Lifetime value in the Prefix Information option.
1217 *
1218 * - If the prefix is already present in the host's Prefix List as the result of a previously
1219 * received advertisement, reset its invalidation timer to the Valid Lifetime value in the Prefix
1220 * Information option. If the new Lifetime value is zero, time-out the prefix immediately. */
1221 if (lifetime_usec == 0) {
1222 r = ndisc_remove_route(route, link);
1223 if (r < 0)
1224 return log_link_warning_errno(link, r, "Failed to remove prefix route: %m");
1225 } else {
1226 r = ndisc_request_router_route(route, link, rt);
1227 if (r < 0)
1228 return log_link_warning_errno(link, r, "Failed to request prefix route: %m");
1229 }
d5017c84
YW
1230
1231 return 0;
3b015d40
TG
1232}
1233
fbdda4bb 1234static int ndisc_router_process_prefix(Link *link, sd_ndisc_router *rt) {
9ca04752 1235 uint8_t flags, prefixlen;
fbdda4bb 1236 struct in6_addr a;
fbdda4bb
YW
1237 int r;
1238
1239 assert(link);
1240 assert(link->network);
1241 assert(rt);
1242
1243 r = sd_ndisc_router_prefix_get_address(rt, &a);
1244 if (r < 0)
2c5bca17 1245 return log_link_warning_errno(link, r, "Failed to get prefix address: %m");
fbdda4bb 1246
a115c60e
YW
1247 /* RFC 4861 Section 4.6.2:
1248 * A router SHOULD NOT send a prefix option for the link-local prefix and a host SHOULD ignore such
1249 * a prefix option. */
1250 if (in6_addr_is_link_local(&a)) {
479d3e19 1251 log_link_debug(link, "Received link-local prefix, ignoring prefix.");
a115c60e
YW
1252 return 0;
1253 }
1254
fbdda4bb
YW
1255 r = sd_ndisc_router_prefix_get_prefixlen(rt, &prefixlen);
1256 if (r < 0)
2c5bca17 1257 return log_link_warning_errno(link, r, "Failed to get prefix length: %m");
fbdda4bb
YW
1258
1259 if (in6_prefix_is_filtered(&a, prefixlen, link->network->ndisc_allow_listed_prefix, link->network->ndisc_deny_listed_prefix)) {
c71384a9
ZJS
1260 if (DEBUG_LOGGING)
1261 log_link_debug(link, "Prefix '%s' is %s, ignoring",
1262 !set_isempty(link->network->ndisc_allow_listed_prefix) ? "not in allow list"
1263 : "in deny list",
1264 IN6_ADDR_PREFIX_TO_STRING(&a, prefixlen));
fbdda4bb
YW
1265 return 0;
1266 }
1267
1268 r = sd_ndisc_router_prefix_get_flags(rt, &flags);
1269 if (r < 0)
2c5bca17 1270 return log_link_warning_errno(link, r, "Failed to get RA prefix flags: %m");
fbdda4bb 1271
9f368d9e 1272 if (FLAGS_SET(flags, ND_OPT_PI_FLAG_ONLINK)) {
fbdda4bb 1273 r = ndisc_router_process_onlink_prefix(link, rt);
9f368d9e
YW
1274 if (r < 0)
1275 return r;
1276 }
fbdda4bb
YW
1277
1278 if (FLAGS_SET(flags, ND_OPT_PI_FLAG_AUTO)) {
1279 r = ndisc_router_process_autonomous_prefix(link, rt);
1280 if (r < 0)
1281 return r;
1282 }
1283
1284 return 0;
1285}
1286
d5017c84 1287static int ndisc_router_process_route(Link *link, sd_ndisc_router *rt) {
74c301b9 1288 _cleanup_(route_unrefp) Route *route = NULL;
9ca04752 1289 uint8_t preference, prefixlen;
91fc5135 1290 struct in6_addr gateway, dst;
6197db53 1291 usec_t lifetime_usec;
7a695d8e 1292 int r;
a13c50e7
TG
1293
1294 assert(link);
a13c50e7 1295
52672db3 1296 if (!link->network->ndisc_use_route_prefix)
610c0db1
YW
1297 return 0;
1298
6197db53 1299 r = sd_ndisc_router_route_get_lifetime_timestamp(rt, CLOCK_BOOTTIME, &lifetime_usec);
d5017c84 1300 if (r < 0)
2c5bca17 1301 return log_link_warning_errno(link, r, "Failed to get route lifetime from RA: %m");
d5017c84 1302
b8ce3b44 1303 r = sd_ndisc_router_route_get_address(rt, &dst);
d5017c84 1304 if (r < 0)
2c5bca17 1305 return log_link_warning_errno(link, r, "Failed to get route destination address: %m");
3b015d40 1306
c995fa02
YW
1307 r = sd_ndisc_router_route_get_prefixlen(rt, &prefixlen);
1308 if (r < 0)
2c5bca17 1309 return log_link_warning_errno(link, r, "Failed to get route prefix length: %m");
c995fa02 1310
c71384a9
ZJS
1311 if (in6_prefix_is_filtered(&dst, prefixlen,
1312 link->network->ndisc_allow_listed_route_prefix,
1313 link->network->ndisc_deny_listed_route_prefix)) {
16c89e64 1314
c71384a9
ZJS
1315 if (DEBUG_LOGGING)
1316 log_link_debug(link, "Route prefix %s is %s, ignoring",
1317 !set_isempty(link->network->ndisc_allow_listed_route_prefix) ? "not in allow list"
1318 : "in deny list",
1319 IN6_ADDR_PREFIX_TO_STRING(&dst, prefixlen));
16c89e64
DP
1320 return 0;
1321 }
1322
9ca04752 1323 r = sd_ndisc_router_get_sender_address(rt, &gateway);
19e334bd 1324 if (r < 0)
2c5bca17 1325 return log_link_warning_errno(link, r, "Failed to get gateway address from RA: %m");
19e334bd 1326
5d003031 1327 if (link_get_ipv6_address(link, &gateway, 0, NULL) >= 0) {
84dbb3fd
ZJS
1328 if (DEBUG_LOGGING)
1329 log_link_debug(link, "Advertised route gateway %s is local to the link, ignoring route",
1330 IN6_ADDR_TO_STRING(&gateway));
22101916
DP
1331 return 0;
1332 }
1333
1e7a0e21 1334 r = sd_ndisc_router_route_get_preference(rt, &preference);
4b68f708 1335 if (r == -EOPNOTSUPP) {
3912d49d
SS
1336 log_link_debug_errno(link, r, "Received route prefix with unsupported preference, ignoring: %m");
1337 return 0;
1338 }
d5017c84 1339 if (r < 0)
34acdf90 1340 return log_link_warning_errno(link, r, "Failed to get router preference from RA: %m");
1e7a0e21 1341
3b015d40 1342 r = route_new(&route);
d5017c84 1343 if (r < 0)
13e8a49a 1344 return log_oom();
3b015d40 1345
3b015d40 1346 route->family = AF_INET6;
1e7a0e21 1347 route->pref = preference;
054b8c28
YW
1348 route->nexthop.gw.in6 = gateway;
1349 route->nexthop.family = AF_INET6;
b8ce3b44 1350 route->dst.in6 = dst;
1e7a0e21 1351 route->dst_prefixlen = prefixlen;
6197db53 1352 route->lifetime_usec = lifetime_usec;
3b015d40 1353
b3ea82af 1354 if (lifetime_usec != 0) {
33cab1d4 1355 r = ndisc_request_router_route(route, link, rt);
b3ea82af
YW
1356 if (r < 0)
1357 return log_link_warning_errno(link, r, "Could not request additional route: %m");
1358 } else {
1359 r = ndisc_remove_route(route, link);
1360 if (r < 0)
1361 return log_link_warning_errno(link, r, "Could not remove additional route with zero lifetime: %m");
1362 }
d5017c84
YW
1363
1364 return 0;
9d96e6c3 1365}
a13c50e7 1366
7a08d314 1367static void ndisc_rdnss_hash_func(const NDiscRDNSS *x, struct siphash *state) {
c01a5c05 1368 siphash24_compress_typesafe(x->address, state);
1e7a0e21
LP
1369}
1370
7a08d314 1371static int ndisc_rdnss_compare_func(const NDiscRDNSS *a, const NDiscRDNSS *b) {
1e7a0e21
LP
1372 return memcmp(&a->address, &b->address, sizeof(a->address));
1373}
1374
b0b97766
YW
1375DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(
1376 ndisc_rdnss_hash_ops,
1377 NDiscRDNSS,
1378 ndisc_rdnss_hash_func,
1379 ndisc_rdnss_compare_func,
1380 free);
1e7a0e21 1381
d5017c84 1382static int ndisc_router_process_rdnss(Link *link, sd_ndisc_router *rt) {
6197db53 1383 usec_t lifetime_usec;
1e7a0e21 1384 const struct in6_addr *a;
50550722 1385 struct in6_addr router;
8aba7b83 1386 bool updated = false, logged_about_too_many = false;
13e8a49a 1387 int n, r;
1e7a0e21
LP
1388
1389 assert(link);
ad0b2df6 1390 assert(link->network);
1e7a0e21
LP
1391 assert(rt);
1392
9646ffe2 1393 if (!link_get_use_dns(link, NETWORK_CONFIG_SOURCE_NDISC))
ad0b2df6
YW
1394 return 0;
1395
9ca04752 1396 r = sd_ndisc_router_get_sender_address(rt, &router);
50550722 1397 if (r < 0)
2c5bca17 1398 return log_link_warning_errno(link, r, "Failed to get router address from RA: %m");
50550722 1399
6197db53 1400 r = sd_ndisc_router_rdnss_get_lifetime_timestamp(rt, CLOCK_BOOTTIME, &lifetime_usec);
d5017c84 1401 if (r < 0)
2c5bca17 1402 return log_link_warning_errno(link, r, "Failed to get RDNSS lifetime: %m");
1e7a0e21
LP
1403
1404 n = sd_ndisc_router_rdnss_get_addresses(rt, &a);
d5017c84 1405 if (n < 0)
2c5bca17 1406 return log_link_warning_errno(link, n, "Failed to get RDNSS addresses: %m");
1e7a0e21 1407
b0b97766
YW
1408 for (int j = 0; j < n; j++) {
1409 _cleanup_free_ NDiscRDNSS *x = NULL;
3b6a3bde 1410 NDiscRDNSS *rdnss, d = {
b0b97766
YW
1411 .address = a[j],
1412 };
1e7a0e21 1413
af2aea8b
YW
1414 if (lifetime_usec == 0) {
1415 /* The entry is outdated. */
1416 free(set_remove(link->ndisc_rdnss, &d));
1417 updated = true;
1418 continue;
1419 }
1420
b0b97766
YW
1421 rdnss = set_get(link->ndisc_rdnss, &d);
1422 if (rdnss) {
50550722 1423 rdnss->router = router;
03ccc4b4 1424 rdnss->lifetime_usec = lifetime_usec;
1e7a0e21
LP
1425 continue;
1426 }
1427
8aba7b83
YW
1428 if (set_size(link->ndisc_rdnss) >= NDISC_RDNSS_MAX) {
1429 if (!logged_about_too_many)
1430 log_link_warning(link, "Too many RDNSS records per link. Only first %u records will be used.", NDISC_RDNSS_MAX);
1431 logged_about_too_many = true;
1432 continue;
1433 }
1434
d5017c84
YW
1435 x = new(NDiscRDNSS, 1);
1436 if (!x)
1437 return log_oom();
1e7a0e21 1438
d5017c84 1439 *x = (NDiscRDNSS) {
b0b97766 1440 .address = a[j],
50550722 1441 .router = router,
03ccc4b4 1442 .lifetime_usec = lifetime_usec,
d5017c84 1443 };
1e7a0e21 1444
35e601d4 1445 r = set_ensure_consume(&link->ndisc_rdnss, &ndisc_rdnss_hash_ops, TAKE_PTR(x));
d5017c84
YW
1446 if (r < 0)
1447 return log_oom();
1e7a0e21 1448 assert(r > 0);
9092113d
YW
1449
1450 updated = true;
1e7a0e21 1451 }
d5017c84 1452
9092113d
YW
1453 if (updated)
1454 link_dirty(link);
1455
d5017c84 1456 return 0;
1e7a0e21
LP
1457}
1458
7a08d314 1459static void ndisc_dnssl_hash_func(const NDiscDNSSL *x, struct siphash *state) {
f281fc1e 1460 siphash24_compress_string(NDISC_DNSSL_DOMAIN(x), state);
1e7a0e21
LP
1461}
1462
7a08d314 1463static int ndisc_dnssl_compare_func(const NDiscDNSSL *a, const NDiscDNSSL *b) {
1e7a0e21
LP
1464 return strcmp(NDISC_DNSSL_DOMAIN(a), NDISC_DNSSL_DOMAIN(b));
1465}
1466
b0b97766
YW
1467DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(
1468 ndisc_dnssl_hash_ops,
1469 NDiscDNSSL,
1470 ndisc_dnssl_hash_func,
1471 ndisc_dnssl_compare_func,
1472 free);
1e7a0e21 1473
13e8a49a 1474static int ndisc_router_process_dnssl(Link *link, sd_ndisc_router *rt) {
9ca04752 1475 char **l;
6197db53 1476 usec_t lifetime_usec;
50550722 1477 struct in6_addr router;
8aba7b83 1478 bool updated = false, logged_about_too_many = false;
1e7a0e21
LP
1479 int r;
1480
1481 assert(link);
ad0b2df6 1482 assert(link->network);
1e7a0e21
LP
1483 assert(rt);
1484
7a169cb4 1485 if (link_get_use_domains(link, NETWORK_CONFIG_SOURCE_NDISC) <= 0)
ad0b2df6
YW
1486 return 0;
1487
9ca04752 1488 r = sd_ndisc_router_get_sender_address(rt, &router);
50550722 1489 if (r < 0)
2c5bca17 1490 return log_link_warning_errno(link, r, "Failed to get router address from RA: %m");
50550722 1491
6197db53 1492 r = sd_ndisc_router_dnssl_get_lifetime_timestamp(rt, CLOCK_BOOTTIME, &lifetime_usec);
13e8a49a 1493 if (r < 0)
2c5bca17 1494 return log_link_warning_errno(link, r, "Failed to get DNSSL lifetime: %m");
1e7a0e21
LP
1495
1496 r = sd_ndisc_router_dnssl_get_domains(rt, &l);
13e8a49a 1497 if (r < 0)
2c5bca17 1498 return log_link_warning_errno(link, r, "Failed to get DNSSL addresses: %m");
1e7a0e21 1499
b0b97766
YW
1500 STRV_FOREACH(j, l) {
1501 _cleanup_free_ NDiscDNSSL *s = NULL;
3b6a3bde 1502 NDiscDNSSL *dnssl;
1e7a0e21 1503
b0b97766
YW
1504 s = malloc0(ALIGN(sizeof(NDiscDNSSL)) + strlen(*j) + 1);
1505 if (!s)
1506 return log_oom();
1e7a0e21 1507
b0b97766 1508 strcpy(NDISC_DNSSL_DOMAIN(s), *j);
1e7a0e21 1509
af2aea8b
YW
1510 if (lifetime_usec == 0) {
1511 /* The entry is outdated. */
1512 free(set_remove(link->ndisc_dnssl, s));
1513 updated = true;
1514 continue;
1515 }
1516
b0b97766
YW
1517 dnssl = set_get(link->ndisc_dnssl, s);
1518 if (dnssl) {
50550722 1519 dnssl->router = router;
03ccc4b4 1520 dnssl->lifetime_usec = lifetime_usec;
1e7a0e21
LP
1521 continue;
1522 }
1523
8aba7b83
YW
1524 if (set_size(link->ndisc_dnssl) >= NDISC_DNSSL_MAX) {
1525 if (!logged_about_too_many)
1526 log_link_warning(link, "Too many DNSSL records per link. Only first %u records will be used.", NDISC_DNSSL_MAX);
1527 logged_about_too_many = true;
1528 continue;
1529 }
1530
50550722 1531 s->router = router;
03ccc4b4 1532 s->lifetime_usec = lifetime_usec;
1e7a0e21 1533
35e601d4 1534 r = set_ensure_consume(&link->ndisc_dnssl, &ndisc_dnssl_hash_ops, TAKE_PTR(s));
13e8a49a
YW
1535 if (r < 0)
1536 return log_oom();
1e7a0e21 1537 assert(r > 0);
9092113d
YW
1538
1539 updated = true;
1e7a0e21 1540 }
13e8a49a 1541
9092113d
YW
1542 if (updated)
1543 link_dirty(link);
1544
13e8a49a 1545 return 0;
1e7a0e21
LP
1546}
1547
64de00c4
YW
1548static NDiscCaptivePortal* ndisc_captive_portal_free(NDiscCaptivePortal *x) {
1549 if (!x)
1550 return NULL;
1551
1552 free(x->captive_portal);
1553 return mfree(x);
1554}
1555
1556DEFINE_TRIVIAL_CLEANUP_FUNC(NDiscCaptivePortal*, ndisc_captive_portal_free);
1557
1558static void ndisc_captive_portal_hash_func(const NDiscCaptivePortal *x, struct siphash *state) {
1559 assert(x);
1560 siphash24_compress_string(x->captive_portal, state);
1561}
1562
1563static int ndisc_captive_portal_compare_func(const NDiscCaptivePortal *a, const NDiscCaptivePortal *b) {
1564 assert(a);
1565 assert(b);
1566 return strcmp_ptr(a->captive_portal, b->captive_portal);
1567}
1568
1569DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(
1570 ndisc_captive_portal_hash_ops,
1571 NDiscCaptivePortal,
1572 ndisc_captive_portal_hash_func,
1573 ndisc_captive_portal_compare_func,
535134bc 1574 ndisc_captive_portal_free);
64de00c4
YW
1575
1576static int ndisc_router_process_captive_portal(Link *link, sd_ndisc_router *rt) {
1577 _cleanup_(ndisc_captive_portal_freep) NDiscCaptivePortal *new_entry = NULL;
1578 _cleanup_free_ char *captive_portal = NULL;
9ca04752 1579 const char *uri;
6197db53 1580 usec_t lifetime_usec;
64de00c4
YW
1581 NDiscCaptivePortal *exist;
1582 struct in6_addr router;
64de00c4
YW
1583 int r;
1584
1585 assert(link);
1586 assert(link->network);
1587 assert(rt);
1588
52672db3 1589 if (!link->network->ndisc_use_captive_portal)
64de00c4
YW
1590 return 0;
1591
9ca04752 1592 r = sd_ndisc_router_get_sender_address(rt, &router);
64de00c4
YW
1593 if (r < 0)
1594 return log_link_warning_errno(link, r, "Failed to get router address from RA: %m");
1595
218f3738
YW
1596 /* RFC 4861 section 4.2. states that the lifetime in the message header should be used only for the
1597 * default gateway, but the captive portal option does not have a lifetime field, hence, we use the
1598 * main lifetime for the portal. */
6197db53 1599 r = sd_ndisc_router_get_lifetime_timestamp(rt, CLOCK_BOOTTIME, &lifetime_usec);
64de00c4
YW
1600 if (r < 0)
1601 return log_link_warning_errno(link, r, "Failed to get lifetime of RA message: %m");
1602
9ca04752 1603 r = sd_ndisc_router_get_captive_portal(rt, &uri);
64de00c4
YW
1604 if (r < 0)
1605 return log_link_warning_errno(link, r, "Failed to get captive portal from RA: %m");
1606
9ca04752
YW
1607 captive_portal = strdup(uri);
1608 if (!captive_portal)
1609 return log_oom();
64de00c4 1610
218f3738
YW
1611 if (lifetime_usec == 0) {
1612 /* Drop the portal with zero lifetime. */
1613 ndisc_captive_portal_free(set_remove(link->ndisc_captive_portals,
9ca04752 1614 &(const NDiscCaptivePortal) {
218f3738
YW
1615 .captive_portal = captive_portal,
1616 }));
1617 return 0;
1618 }
1619
1620 exist = set_get(link->ndisc_captive_portals,
9ca04752 1621 &(const NDiscCaptivePortal) {
218f3738
YW
1622 .captive_portal = captive_portal,
1623 });
64de00c4
YW
1624 if (exist) {
1625 /* update existing entry */
1626 exist->router = router;
1627 exist->lifetime_usec = lifetime_usec;
6df82d12 1628 return 1;
64de00c4
YW
1629 }
1630
bf943a9d
YW
1631 if (set_size(link->ndisc_captive_portals) >= NDISC_CAPTIVE_PORTAL_MAX) {
1632 NDiscCaptivePortal *c, *target = NULL;
1633
1634 /* Find the portal who has the minimal lifetime and drop it to store new one. */
1635 SET_FOREACH(c, link->ndisc_captive_portals)
1636 if (!target || c->lifetime_usec < target->lifetime_usec)
1637 target = c;
1638
1639 assert(target);
1640 assert(set_remove(link->ndisc_captive_portals, target) == target);
1641 ndisc_captive_portal_free(target);
1642 }
1643
64de00c4
YW
1644 new_entry = new(NDiscCaptivePortal, 1);
1645 if (!new_entry)
1646 return log_oom();
1647
1648 *new_entry = (NDiscCaptivePortal) {
1649 .router = router,
1650 .lifetime_usec = lifetime_usec,
1651 .captive_portal = TAKE_PTR(captive_portal),
1652 };
1653
1654 r = set_ensure_put(&link->ndisc_captive_portals, &ndisc_captive_portal_hash_ops, new_entry);
1655 if (r < 0)
1656 return log_oom();
1657 assert(r > 0);
1658 TAKE_PTR(new_entry);
1659
1660 link_dirty(link);
6df82d12 1661 return 1;
64de00c4
YW
1662}
1663
6e8f5e4c
SS
1664static void ndisc_pref64_hash_func(const NDiscPREF64 *x, struct siphash *state) {
1665 assert(x);
1666
c01a5c05
YW
1667 siphash24_compress_typesafe(x->prefix_len, state);
1668 siphash24_compress_typesafe(x->prefix, state);
6e8f5e4c
SS
1669}
1670
1671static int ndisc_pref64_compare_func(const NDiscPREF64 *a, const NDiscPREF64 *b) {
1672 int r;
1673
1674 assert(a);
1675 assert(b);
1676
1677 r = CMP(a->prefix_len, b->prefix_len);
1678 if (r != 0)
1679 return r;
1680
1681 return memcmp(&a->prefix, &b->prefix, sizeof(a->prefix));
1682}
1683
1684DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(
1685 ndisc_pref64_hash_ops,
1686 NDiscPREF64,
1687 ndisc_pref64_hash_func,
1688 ndisc_pref64_compare_func,
1689 mfree);
1690
1691static int ndisc_router_process_pref64(Link *link, sd_ndisc_router *rt) {
1692 _cleanup_free_ NDiscPREF64 *new_entry = NULL;
6197db53 1693 usec_t lifetime_usec;
6e8f5e4c 1694 struct in6_addr a, router;
9ca04752 1695 uint8_t prefix_len;
6e8f5e4c
SS
1696 NDiscPREF64 *exist;
1697 int r;
1698
1699 assert(link);
1700 assert(link->network);
1701 assert(rt);
1702
52672db3 1703 if (!link->network->ndisc_use_pref64)
6e8f5e4c
SS
1704 return 0;
1705
9ca04752 1706 r = sd_ndisc_router_get_sender_address(rt, &router);
6e8f5e4c
SS
1707 if (r < 0)
1708 return log_link_warning_errno(link, r, "Failed to get router address from RA: %m");
1709
1710 r = sd_ndisc_router_prefix64_get_prefix(rt, &a);
1711 if (r < 0)
1712 return log_link_warning_errno(link, r, "Failed to get pref64 prefix: %m");
1713
1714 r = sd_ndisc_router_prefix64_get_prefixlen(rt, &prefix_len);
1715 if (r < 0)
1716 return log_link_warning_errno(link, r, "Failed to get pref64 prefix length: %m");
1717
6197db53 1718 r = sd_ndisc_router_prefix64_get_lifetime_timestamp(rt, CLOCK_BOOTTIME, &lifetime_usec);
6e8f5e4c
SS
1719 if (r < 0)
1720 return log_link_warning_errno(link, r, "Failed to get pref64 prefix lifetime: %m");
1721
6e8f5e4c
SS
1722 if (lifetime_usec == 0) {
1723 free(set_remove(link->ndisc_pref64,
1724 &(NDiscPREF64) {
1725 .prefix = a,
1726 .prefix_len = prefix_len
1727 }));
1728 return 0;
1729 }
1730
1731 exist = set_get(link->ndisc_pref64,
1732 &(NDiscPREF64) {
1733 .prefix = a,
1734 .prefix_len = prefix_len
1735 });
1736 if (exist) {
1737 /* update existing entry */
1738 exist->router = router;
1739 exist->lifetime_usec = lifetime_usec;
1740 return 0;
1741 }
1742
4df16cd0
YW
1743 if (set_size(link->ndisc_pref64) >= NDISC_PREF64_MAX) {
1744 log_link_debug(link, "Too many PREF64 records received. Only first %u records will be used.", NDISC_PREF64_MAX);
1745 return 0;
1746 }
1747
6e8f5e4c
SS
1748 new_entry = new(NDiscPREF64, 1);
1749 if (!new_entry)
1750 return log_oom();
1751
1752 *new_entry = (NDiscPREF64) {
1753 .router = router,
1754 .lifetime_usec = lifetime_usec,
1755 .prefix = a,
1756 .prefix_len = prefix_len,
1757 };
1758
1759 r = set_ensure_put(&link->ndisc_pref64, &ndisc_pref64_hash_ops, new_entry);
1760 if (r < 0)
1761 return log_oom();
1762
1763 assert(r > 0);
1764 TAKE_PTR(new_entry);
1765
1766 return 0;
1767}
1768
e8c9b5b0 1769static int ndisc_router_process_options(Link *link, sd_ndisc_router *rt) {
6df82d12 1770 size_t n_captive_portal = 0;
fbdda4bb
YW
1771 int r;
1772
1e7a0e21 1773 assert(link);
55d3fdcf 1774 assert(link->network);
1e7a0e21
LP
1775 assert(rt);
1776
fbdda4bb 1777 for (r = sd_ndisc_router_option_rewind(rt); ; r = sd_ndisc_router_option_next(rt)) {
1e7a0e21
LP
1778 uint8_t type;
1779
e8c9b5b0 1780 if (r < 0)
2c5bca17 1781 return log_link_warning_errno(link, r, "Failed to iterate through options: %m");
1e7a0e21 1782 if (r == 0) /* EOF */
13e8a49a 1783 return 0;
1e7a0e21
LP
1784
1785 r = sd_ndisc_router_option_get_type(rt, &type);
e8c9b5b0 1786 if (r < 0)
2c5bca17 1787 return log_link_warning_errno(link, r, "Failed to get RA option type: %m");
1e7a0e21
LP
1788
1789 switch (type) {
fbdda4bb
YW
1790 case SD_NDISC_OPTION_PREFIX_INFORMATION:
1791 r = ndisc_router_process_prefix(link, rt);
1e7a0e21 1792 break;
1e7a0e21
LP
1793
1794 case SD_NDISC_OPTION_ROUTE_INFORMATION:
13e8a49a 1795 r = ndisc_router_process_route(link, rt);
1e7a0e21
LP
1796 break;
1797
1798 case SD_NDISC_OPTION_RDNSS:
ad0b2df6 1799 r = ndisc_router_process_rdnss(link, rt);
1e7a0e21
LP
1800 break;
1801
1802 case SD_NDISC_OPTION_DNSSL:
ad0b2df6 1803 r = ndisc_router_process_dnssl(link, rt);
1e7a0e21 1804 break;
9747955d 1805 case SD_NDISC_OPTION_CAPTIVE_PORTAL:
6df82d12
YW
1806 if (n_captive_portal > 0) {
1807 if (n_captive_portal == 1)
1808 log_link_notice(link, "Received RA with multiple captive portals, only using the first one.");
1809
1810 n_captive_portal++;
1811 continue;
1812 }
9747955d 1813 r = ndisc_router_process_captive_portal(link, rt);
6df82d12
YW
1814 if (r > 0)
1815 n_captive_portal++;
9747955d 1816 break;
6e8f5e4c
SS
1817 case SD_NDISC_OPTION_PREF64:
1818 r = ndisc_router_process_pref64(link, rt);
1819 break;
1e7a0e21 1820 }
5908d864
YW
1821 if (r < 0 && r != -EBADMSG)
1822 return r;
1e7a0e21
LP
1823 }
1824}
1825
87a33c07 1826static int ndisc_drop_outdated(Link *link, const struct in6_addr *router, usec_t timestamp_usec) {
94e6d37c
YW
1827 bool updated = false;
1828 NDiscDNSSL *dnssl;
1829 NDiscRDNSS *rdnss;
64de00c4 1830 NDiscCaptivePortal *cp;
fabea9c0 1831 NDiscPREF64 *p64;
94e6d37c
YW
1832 Address *address;
1833 Route *route;
372acaad 1834 int r, ret = 0;
94e6d37c
YW
1835
1836 assert(link);
8d01e44c 1837 assert(link->manager);
94e6d37c
YW
1838
1839 /* If an address or friends is already assigned, but not valid anymore, then refuse to update it,
1840 * and let's immediately remove it.
1841 * See RFC4862, section 5.5.3.e. But the following logic is deviated from RFC4862 by honoring all
1842 * valid lifetimes to improve the reaction of SLAAC to renumbering events.
1843 * See draft-ietf-6man-slaac-renum-02, section 4.2. */
1844
b03d7291
YW
1845 r = drop_default_router(link, router, timestamp_usec);
1846 if (r < 0)
1847 RET_GATHER(ret, log_link_warning_errno(link, r, "Failed to drop outdated default router, ignoring: %m"));
1848
8d01e44c 1849 SET_FOREACH(route, link->manager->routes) {
94e6d37c
YW
1850 if (route->source != NETWORK_CONFIG_SOURCE_NDISC)
1851 continue;
1852
8d01e44c
YW
1853 if (route->nexthop.ifindex != link->ifindex)
1854 continue;
1855
9944629e
YW
1856 if (route->protocol == RTPROT_REDIRECT)
1857 continue; /* redirect route will be dropped by ndisc_drop_redirect(). */
1858
c75d1196 1859 if (route->lifetime_usec > timestamp_usec)
94e6d37c
YW
1860 continue; /* the route is still valid */
1861
87a33c07
YW
1862 if (router && !in6_addr_equal(&route->provider.in6, router))
1863 continue;
1864
3caed9ea 1865 r = route_remove_and_cancel(route, link->manager);
372acaad
YW
1866 if (r < 0)
1867 RET_GATHER(ret, log_link_warning_errno(link, r, "Failed to remove outdated SLAAC route, ignoring: %m"));
94e6d37c
YW
1868 }
1869
1870 SET_FOREACH(address, link->addresses) {
1871 if (address->source != NETWORK_CONFIG_SOURCE_NDISC)
1872 continue;
1873
c75d1196 1874 if (address->lifetime_valid_usec > timestamp_usec)
94e6d37c
YW
1875 continue; /* the address is still valid */
1876
87a33c07
YW
1877 if (router && !in6_addr_equal(&address->provider.in6, router))
1878 continue;
1879
f22b586a 1880 r = address_remove_and_cancel(address, link);
372acaad
YW
1881 if (r < 0)
1882 RET_GATHER(ret, log_link_warning_errno(link, r, "Failed to remove outdated SLAAC address, ignoring: %m"));
94e6d37c
YW
1883 }
1884
1885 SET_FOREACH(rdnss, link->ndisc_rdnss) {
c75d1196 1886 if (rdnss->lifetime_usec > timestamp_usec)
94e6d37c
YW
1887 continue; /* the DNS server is still valid */
1888
87a33c07
YW
1889 if (router && !in6_addr_equal(&rdnss->router, router))
1890 continue;
1891
94e6d37c
YW
1892 free(set_remove(link->ndisc_rdnss, rdnss));
1893 updated = true;
1894 }
1895
1896 SET_FOREACH(dnssl, link->ndisc_dnssl) {
c75d1196 1897 if (dnssl->lifetime_usec > timestamp_usec)
94e6d37c
YW
1898 continue; /* the DNS domain is still valid */
1899
87a33c07
YW
1900 if (router && !in6_addr_equal(&dnssl->router, router))
1901 continue;
1902
94e6d37c
YW
1903 free(set_remove(link->ndisc_dnssl, dnssl));
1904 updated = true;
1905 }
1906
64de00c4 1907 SET_FOREACH(cp, link->ndisc_captive_portals) {
c75d1196 1908 if (cp->lifetime_usec > timestamp_usec)
64de00c4
YW
1909 continue; /* the captive portal is still valid */
1910
87a33c07
YW
1911 if (router && !in6_addr_equal(&cp->router, router))
1912 continue;
1913
75a91226 1914 ndisc_captive_portal_free(set_remove(link->ndisc_captive_portals, cp));
64de00c4
YW
1915 updated = true;
1916 }
1917
fabea9c0 1918 SET_FOREACH(p64, link->ndisc_pref64) {
c75d1196 1919 if (p64->lifetime_usec > timestamp_usec)
fabea9c0
YW
1920 continue; /* the pref64 prefix is still valid */
1921
87a33c07
YW
1922 if (router && !in6_addr_equal(&p64->router, router))
1923 continue;
1924
fabea9c0
YW
1925 free(set_remove(link->ndisc_pref64, p64));
1926 /* The pref64 prefix is not exported through the state file, hence it is not necessary to set
1927 * the 'updated' flag. */
1928 }
1929
94e6d37c
YW
1930 if (updated)
1931 link_dirty(link);
1932
372acaad 1933 return ret;
94e6d37c
YW
1934}
1935
77302468
YW
1936static int ndisc_setup_expire(Link *link);
1937
1938static int ndisc_expire_handler(sd_event_source *s, uint64_t usec, void *userdata) {
1939 Link *link = ASSERT_PTR(userdata);
1940 usec_t now_usec;
1941
1942 assert(link->manager);
1943
1944 assert_se(sd_event_now(link->manager->event, CLOCK_BOOTTIME, &now_usec) >= 0);
1945
87a33c07 1946 (void) ndisc_drop_outdated(link, /* router = */ NULL, now_usec);
77302468
YW
1947 (void) ndisc_setup_expire(link);
1948 return 0;
1949}
1950
1951static int ndisc_setup_expire(Link *link) {
1952 usec_t lifetime_usec = USEC_INFINITY;
64de00c4 1953 NDiscCaptivePortal *cp;
77302468
YW
1954 NDiscDNSSL *dnssl;
1955 NDiscRDNSS *rdnss;
fabea9c0 1956 NDiscPREF64 *p64;
77302468
YW
1957 Address *address;
1958 Route *route;
1959 int r;
1960
1961 assert(link);
1962 assert(link->manager);
1963
8d01e44c 1964 SET_FOREACH(route, link->manager->routes) {
77302468
YW
1965 if (route->source != NETWORK_CONFIG_SOURCE_NDISC)
1966 continue;
1967
8d01e44c
YW
1968 if (route->nexthop.ifindex != link->ifindex)
1969 continue;
1970
77302468
YW
1971 if (!route_exists(route))
1972 continue;
1973
1974 lifetime_usec = MIN(lifetime_usec, route->lifetime_usec);
1975 }
1976
1977 SET_FOREACH(address, link->addresses) {
1978 if (address->source != NETWORK_CONFIG_SOURCE_NDISC)
1979 continue;
1980
1981 if (!address_exists(address))
1982 continue;
1983
1984 lifetime_usec = MIN(lifetime_usec, address->lifetime_valid_usec);
1985 }
1986
1987 SET_FOREACH(rdnss, link->ndisc_rdnss)
1988 lifetime_usec = MIN(lifetime_usec, rdnss->lifetime_usec);
1989
1990 SET_FOREACH(dnssl, link->ndisc_dnssl)
1991 lifetime_usec = MIN(lifetime_usec, dnssl->lifetime_usec);
1992
64de00c4
YW
1993 SET_FOREACH(cp, link->ndisc_captive_portals)
1994 lifetime_usec = MIN(lifetime_usec, cp->lifetime_usec);
1995
fabea9c0
YW
1996 SET_FOREACH(p64, link->ndisc_pref64)
1997 lifetime_usec = MIN(lifetime_usec, p64->lifetime_usec);
1998
77302468
YW
1999 if (lifetime_usec == USEC_INFINITY)
2000 return 0;
2001
2002 r = event_reset_time(link->manager->event, &link->ndisc_expire, CLOCK_BOOTTIME,
2003 lifetime_usec, 0, ndisc_expire_handler, link, 0, "ndisc-expiration", true);
2004 if (r < 0)
2005 return log_link_warning_errno(link, r, "Failed to update expiration timer for ndisc: %m");
2006
2007 return 0;
2008}
2009
928112a4
YW
2010static int ndisc_start_dhcp6_client(Link *link, sd_ndisc_router *rt) {
2011 int r;
2012
2013 assert(link);
2014 assert(link->network);
2015
ffef01ac
YW
2016 /* Do not start DHCPv6 client if the router lifetime is zero, as the message sent as a signal of
2017 * that the router is e.g. shutting down, revoked, etc,. */
2018 r = sd_ndisc_router_get_lifetime(rt, NULL);
2019 if (r <= 0)
2020 return r;
2021
52672db3 2022 switch (link->network->ndisc_start_dhcp6_client) {
928112a4
YW
2023 case IPV6_ACCEPT_RA_START_DHCP6_CLIENT_NO:
2024 return 0;
2025
2026 case IPV6_ACCEPT_RA_START_DHCP6_CLIENT_YES: {
2027 uint64_t flags;
2028
2029 r = sd_ndisc_router_get_flags(rt, &flags);
2030 if (r < 0)
2031 return log_link_warning_errno(link, r, "Failed to get RA flags: %m");
2032
2033 if ((flags & (ND_RA_FLAG_MANAGED | ND_RA_FLAG_OTHER)) == 0)
2034 return 0;
2035
2036 /* (re)start DHCPv6 client in stateful or stateless mode according to RA flags.
0bcc6557
AH
2037 * Note, if both "managed" and "other configuration" bits are set, then ignore
2038 * "other configuration" bit. See RFC 4861. */
fac19a21 2039 r = dhcp6_start_on_ra(link, !(flags & ND_RA_FLAG_MANAGED));
928112a4
YW
2040 break;
2041 }
2042 case IPV6_ACCEPT_RA_START_DHCP6_CLIENT_ALWAYS:
0bcc6557 2043 /* When IPv6AcceptRA.DHCPv6Client=always, start dhcp6 client in solicit mode
928112a4 2044 * even if the router flags have neither M nor O flags. */
fac19a21 2045 r = dhcp6_start_on_ra(link, /* information_request = */ false);
928112a4
YW
2046 break;
2047
2048 default:
2049 assert_not_reached();
2050 }
2051
2052 if (r < 0)
2c5bca17 2053 return log_link_warning_errno(link, r, "Could not acquire DHCPv6 lease on NDisc request: %m");
928112a4
YW
2054
2055 log_link_debug(link, "Acquiring DHCPv6 lease on NDisc request");
2056 return 0;
2057}
2058
d5017c84 2059static int ndisc_router_handler(Link *link, sd_ndisc_router *rt) {
b8ce3b44 2060 struct in6_addr router;
94e6d37c 2061 usec_t timestamp_usec;
86e2be7b 2062 int r;
1e7a0e21
LP
2063
2064 assert(link);
2065 assert(link->network);
2066 assert(link->manager);
2067 assert(rt);
2068
9ca04752 2069 r = sd_ndisc_router_get_sender_address(rt, &router);
5908d864
YW
2070 if (r == -ENODATA) {
2071 log_link_debug(link, "Received RA without router address, ignoring.");
2072 return 0;
2073 }
75d26411 2074 if (r < 0)
2c5bca17 2075 return log_link_warning_errno(link, r, "Failed to get router address from RA: %m");
75d26411 2076
c995fa02 2077 if (in6_prefix_is_filtered(&router, 128, link->network->ndisc_allow_listed_router, link->network->ndisc_deny_listed_router)) {
75d26411 2078 if (DEBUG_LOGGING) {
75d26411 2079 if (!set_isempty(link->network->ndisc_allow_listed_router))
84dbb3fd 2080 log_link_debug(link, "Router %s is not in allow list, ignoring.", IN6_ADDR_TO_STRING(&router));
75d26411 2081 else
84dbb3fd 2082 log_link_debug(link, "Router %s is in deny list, ignoring.", IN6_ADDR_TO_STRING(&router));
75d26411
YW
2083 }
2084 return 0;
2085 }
2086
94e6d37c 2087 r = sd_ndisc_router_get_timestamp(rt, CLOCK_BOOTTIME, &timestamp_usec);
5908d864
YW
2088 if (r == -ENODATA) {
2089 log_link_debug(link, "Received RA without timestamp, ignoring.");
2090 return 0;
2091 }
94e6d37c
YW
2092 if (r < 0)
2093 return r;
2094
87a33c07 2095 r = ndisc_drop_outdated(link, /* router = */ NULL, timestamp_usec);
94e6d37c 2096 if (r < 0)
2b4fca55 2097 return r;
94e6d37c 2098
b03d7291
YW
2099 r = ndisc_remember_default_router(link, rt);
2100 if (r < 0)
2101 return r;
2102
928112a4
YW
2103 r = ndisc_start_dhcp6_client(link, rt);
2104 if (r < 0)
2105 return r;
1e7a0e21 2106
13e8a49a
YW
2107 r = ndisc_router_process_default(link, rt);
2108 if (r < 0)
2109 return r;
fbdda4bb 2110
1452d497
YW
2111 r = ndisc_router_process_reachable_time(link, rt);
2112 if (r < 0)
2113 return r;
2114
d4c8de21
MM
2115 r = ndisc_router_process_retransmission_time(link, rt);
2116 if (r < 0)
2117 return r;
2118
b15ed2be
MM
2119 r = ndisc_router_process_hop_limit(link, rt);
2120 if (r < 0)
2121 return r;
2122
8c9ef90b
YW
2123 r = ndisc_router_process_mtu(link, rt);
2124 if (r < 0)
2125 return r;
2126
13e8a49a
YW
2127 r = ndisc_router_process_options(link, rt);
2128 if (r < 0)
2129 return r;
d5017c84 2130
77302468
YW
2131 r = ndisc_setup_expire(link);
2132 if (r < 0)
2133 return r;
2134
9944629e
YW
2135 if (sd_ndisc_router_get_lifetime(rt, NULL) <= 0)
2136 (void) ndisc_drop_redirect(link, &router);
6df00594 2137
2ccada8d 2138 if (link->ndisc_messages == 0)
3b6a3bde 2139 link->ndisc_configured = true;
2ccada8d 2140 else
3b6a3bde 2141 log_link_debug(link, "Setting SLAAC addresses and router.");
69203fba 2142
3b6a3bde 2143 if (!link->ndisc_configured)
69203fba
YW
2144 link_set_state(link, LINK_STATE_CONFIGURING);
2145
76c5a0f2 2146 link_check_ready(link);
69203fba 2147 return 0;
1e7a0e21
LP
2148}
2149
87a33c07
YW
2150static int ndisc_neighbor_handle_non_router_message(Link *link, sd_ndisc_neighbor *na) {
2151 struct in6_addr address;
2152 int r;
2153
2154 assert(link);
2155 assert(na);
2156
2157 /* Received Neighbor Advertisement message without Router flag. The node might have been a router,
2158 * and now it is not. Let's drop all configurations based on RAs sent from the node. */
2159
2160 r = sd_ndisc_neighbor_get_target_address(na, &address);
2161 if (r == -ENODATA)
2162 return 0;
2163 if (r < 0)
2164 return r;
2165
2166 (void) ndisc_drop_outdated(link, /* router = */ &address, /* timestamp_usec = */ USEC_INFINITY);
9944629e
YW
2167 (void) ndisc_drop_redirect(link, &address);
2168
87a33c07
YW
2169 return 0;
2170}
2171
2172static int ndisc_neighbor_handle_router_message(Link *link, sd_ndisc_neighbor *na) {
2173 struct in6_addr current_address, original_address;
2174 int r;
2175
2176 assert(link);
2177 assert(link->manager);
2178 assert(na);
2179
2180 /* Received Neighbor Advertisement message with Router flag. If the router address is changed, update
2181 * the provider field of configurations. */
2182
2183 r = sd_ndisc_neighbor_get_sender_address(na, &current_address);
2184 if (r == -ENODATA)
2185 return 0;
2186 if (r < 0)
2187 return r;
2188
2189 r = sd_ndisc_neighbor_get_target_address(na, &original_address);
2190 if (r == -ENODATA)
2191 return 0;
2192 if (r < 0)
2193 return r;
2194
2195 if (in6_addr_equal(&current_address, &original_address))
2196 return 0; /* the router address is not changed */
2197
b03d7291
YW
2198 r = update_default_router_address(link, &original_address, &current_address);
2199 if (r < 0)
2200 return r;
2201
6df00594
YW
2202 r = ndisc_update_redirect_sender(link, &original_address, &current_address);
2203 if (r < 0)
2204 return r;
2205
87a33c07
YW
2206 Route *route;
2207 SET_FOREACH(route, link->manager->routes) {
2208 if (route->source != NETWORK_CONFIG_SOURCE_NDISC)
2209 continue;
2210
2211 if (route->nexthop.ifindex != link->ifindex)
2212 continue;
2213
2214 if (!in6_addr_equal(&route->provider.in6, &original_address))
2215 continue;
2216
2217 route->provider.in6 = current_address;
2218 }
2219
2220 Address *address;
2221 SET_FOREACH(address, link->addresses) {
2222 if (address->source != NETWORK_CONFIG_SOURCE_NDISC)
2223 continue;
2224
2225 if (!in6_addr_equal(&address->provider.in6, &original_address))
2226 continue;
2227
2228 address->provider.in6 = current_address;
2229 }
2230
2231 NDiscRDNSS *rdnss;
2232 SET_FOREACH(rdnss, link->ndisc_rdnss) {
2233 if (!in6_addr_equal(&rdnss->router, &original_address))
2234 continue;
2235
2236 rdnss->router = current_address;
2237 }
2238
2239 NDiscDNSSL *dnssl;
2240 SET_FOREACH(dnssl, link->ndisc_dnssl) {
2241 if (!in6_addr_equal(&dnssl->router, &original_address))
2242 continue;
2243
2244 dnssl->router = current_address;
2245 }
2246
2247 NDiscCaptivePortal *cp;
2248 SET_FOREACH(cp, link->ndisc_captive_portals) {
2249 if (!in6_addr_equal(&cp->router, &original_address))
2250 continue;
2251
2252 cp->router = current_address;
2253 }
2254
2255 NDiscPREF64 *p64;
2256 SET_FOREACH(p64, link->ndisc_pref64) {
2257 if (!in6_addr_equal(&p64->router, &original_address))
2258 continue;
2259
2260 p64->router = current_address;
2261 }
2262
2263 return 0;
2264}
2265
2266static int ndisc_neighbor_handler(Link *link, sd_ndisc_neighbor *na) {
2267 int r;
2268
2269 assert(link);
2270 assert(na);
2271
2272 r = sd_ndisc_neighbor_is_router(na);
2273 if (r < 0)
2274 return r;
2275 if (r == 0)
2276 r = ndisc_neighbor_handle_non_router_message(link, na);
2277 else
2278 r = ndisc_neighbor_handle_router_message(link, na);
2279 if (r < 0)
2280 return r;
2281
2282 return 0;
2283}
2284
28eef158 2285static void ndisc_handler(sd_ndisc *nd, sd_ndisc_event_t event, void *message, void *userdata) {
99534007 2286 Link *link = ASSERT_PTR(userdata);
13e8a49a 2287 int r;
a13c50e7 2288
9d96e6c3
TG
2289 if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
2290 return;
a13c50e7 2291
9d96e6c3 2292 switch (event) {
1e7a0e21
LP
2293
2294 case SD_NDISC_EVENT_ROUTER:
28eef158 2295 r = ndisc_router_handler(link, ASSERT_PTR(message));
5908d864 2296 if (r < 0 && r != -EBADMSG) {
13e8a49a
YW
2297 link_enter_failed(link);
2298 return;
2299 }
1e7a0e21
LP
2300 break;
2301
87a33c07
YW
2302 case SD_NDISC_EVENT_NEIGHBOR:
2303 r = ndisc_neighbor_handler(link, ASSERT_PTR(message));
2304 if (r < 0 && r != -EBADMSG) {
2305 link_enter_failed(link);
2306 return;
2307 }
2308 break;
2309
6df00594
YW
2310 case SD_NDISC_EVENT_REDIRECT:
2311 r = ndisc_redirect_handler(link, ASSERT_PTR(message));
2312 if (r < 0 && r != -EBADMSG) {
2313 log_link_warning_errno(link, r, "Failed to process Redirect message: %m");
2314 link_enter_failed(link);
2315 return;
2316 }
2317 break;
2318
9d96e6c3 2319 case SD_NDISC_EVENT_TIMEOUT:
a8c10331 2320 log_link_debug(link, "NDisc handler get timeout event");
3b6a3bde
YW
2321 if (link->ndisc_messages == 0) {
2322 link->ndisc_configured = true;
3336e946
YW
2323 link_check_ready(link);
2324 }
9d96e6c3 2325 break;
28eef158 2326
9d96e6c3 2327 default:
28eef158 2328 log_link_debug(link, "Received unsupported NDisc event, ignoring.");
a13c50e7
TG
2329 }
2330}
2331
ba4c7184 2332static int ndisc_configure(Link *link) {
a13c50e7
TG
2333 int r;
2334
1e7a0e21
LP
2335 assert(link);
2336
52672db3 2337 if (!link_ndisc_enabled(link))
2ffd6d73 2338 return 0;
a13c50e7 2339
5c078687 2340 if (link->ndisc)
bc9e40c9 2341 return -EBUSY; /* Already configured. */
2ffd6d73 2342
5c078687
YW
2343 r = sd_ndisc_new(&link->ndisc);
2344 if (r < 0)
2345 return r;
2346
2347 r = sd_ndisc_attach_event(link->ndisc, link->manager->event, 0);
2348 if (r < 0)
2349 return r;
a13c50e7 2350
3be64aa4
YW
2351 if (link->hw_addr.length == ETH_ALEN) {
2352 r = sd_ndisc_set_mac(link->ndisc, &link->hw_addr.ether);
2353 if (r < 0)
2354 return r;
2355 }
a13c50e7 2356
1e7a0e21 2357 r = sd_ndisc_set_ifindex(link->ndisc, link->ifindex);
a13c50e7
TG
2358 if (r < 0)
2359 return r;
2360
1e7a0e21 2361 r = sd_ndisc_set_callback(link->ndisc, ndisc_handler, link);
a13c50e7
TG
2362 if (r < 0)
2363 return r;
2364
1e7a0e21
LP
2365 return 0;
2366}
2367
294f129b 2368int ndisc_start(Link *link) {
ba4c7184
YW
2369 int r;
2370
294f129b
YW
2371 assert(link);
2372
2373 if (!link->ndisc || !link->dhcp6_client)
2374 return 0;
2375
ccffa166
YW
2376 if (!link_has_carrier(link))
2377 return 0;
2378
3b6a3bde
YW
2379 if (in6_addr_is_null(&link->ipv6ll_address))
2380 return 0;
2381
25413fbf
YW
2382 r = sd_ndisc_set_link_local_address(link->ndisc, &link->ipv6ll_address);
2383 if (r < 0)
2384 return r;
2385
294f129b
YW
2386 log_link_debug(link, "Discovering IPv6 routers");
2387
ba4c7184
YW
2388 r = sd_ndisc_start(link->ndisc);
2389 if (r < 0)
2390 return r;
2391
2392 return 1;
2393}
2394
09d09207 2395static int ndisc_process_request(Request *req, Link *link, void *userdata) {
ba4c7184
YW
2396 int r;
2397
ff51134c 2398 assert(link);
ba4c7184 2399
4b482e8b 2400 if (!link_is_ready_to_configure(link, /* allow_unmanaged = */ false))
ba4c7184
YW
2401 return 0;
2402
ba4c7184
YW
2403 r = ndisc_configure(link);
2404 if (r < 0)
2405 return log_link_warning_errno(link, r, "Failed to configure IPv6 Router Discovery: %m");
2406
2407 r = ndisc_start(link);
2408 if (r < 0)
2409 return log_link_warning_errno(link, r, "Failed to start IPv6 Router Discovery: %m");
2410
2411 log_link_debug(link, "IPv6 Router Discovery is configured%s.",
2412 r > 0 ? " and started" : "");
ba4c7184
YW
2413 return 1;
2414}
2415
2416int link_request_ndisc(Link *link) {
2417 int r;
2418
2419 assert(link);
2420
52672db3 2421 if (!link_ndisc_enabled(link))
ba4c7184
YW
2422 return 0;
2423
2424 if (link->ndisc)
2425 return 0;
2426
09d09207 2427 r = link_queue_request(link, REQUEST_TYPE_NDISC, ndisc_process_request, NULL);
ba4c7184
YW
2428 if (r < 0)
2429 return log_link_warning_errno(link, r, "Failed to request configuring of the IPv6 Router Discovery: %m");
2430
2431 log_link_debug(link, "Requested configuring of the IPv6 Router Discovery.");
2432 return 0;
294f129b
YW
2433}
2434
77302468
YW
2435int ndisc_stop(Link *link) {
2436 assert(link);
2437
2438 link->ndisc_expire = sd_event_source_disable_unref(link->ndisc_expire);
2439
2440 return sd_ndisc_stop(link->ndisc);
2441}
2442
c69305ff
LP
2443void ndisc_flush(Link *link) {
2444 assert(link);
2445
a86763c7 2446 /* Remove all addresses, routes, RDNSS, DNSSL, and Captive Portal entries, without exception. */
87a33c07 2447 (void) ndisc_drop_outdated(link, /* router = */ NULL, /* timestamp_usec = */ USEC_INFINITY);
9944629e 2448 (void) ndisc_drop_redirect(link, /* router = */ NULL);
c69305ff 2449
b0b97766
YW
2450 link->ndisc_rdnss = set_free(link->ndisc_rdnss);
2451 link->ndisc_dnssl = set_free(link->ndisc_dnssl);
64de00c4 2452 link->ndisc_captive_portals = set_free(link->ndisc_captive_portals);
6e8f5e4c 2453 link->ndisc_pref64 = set_free(link->ndisc_pref64);
6df00594 2454 link->ndisc_redirects = set_free(link->ndisc_redirects);
8c9ef90b 2455 link->ndisc_mtu = 0;
c69305ff 2456}
e520ce64 2457
52672db3 2458static const char* const ndisc_start_dhcp6_client_table[_IPV6_ACCEPT_RA_START_DHCP6_CLIENT_MAX] = {
ac24e418
SS
2459 [IPV6_ACCEPT_RA_START_DHCP6_CLIENT_NO] = "no",
2460 [IPV6_ACCEPT_RA_START_DHCP6_CLIENT_ALWAYS] = "always",
2461 [IPV6_ACCEPT_RA_START_DHCP6_CLIENT_YES] = "yes",
2462};
2463
52672db3 2464DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING_WITH_BOOLEAN(ndisc_start_dhcp6_client, IPv6AcceptRAStartDHCP6Client, IPV6_ACCEPT_RA_START_DHCP6_CLIENT_YES);
3b6a3bde 2465
52672db3 2466DEFINE_CONFIG_PARSE_ENUM(config_parse_ndisc_start_dhcp6_client, ndisc_start_dhcp6_client, IPv6AcceptRAStartDHCP6Client,
3b6a3bde 2467 "Failed to parse DHCPv6Client= setting");