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