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