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