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