]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/network/networkd-ndisc.c
README: mention fq_codel
[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"
ac24e418 23#include "string-table.h"
5f506a55 24#include "string-util.h"
51517f9e 25#include "strv.h"
91750028 26#include "sysctl-util.h"
1e7a0e21
LP
27
28#define NDISC_DNSSL_MAX 64U
29#define NDISC_RDNSS_MAX 64U
4df16cd0 30/* Not defined in the RFC, but let's set an upper limit to make not consume much memory.
bf943a9d
YW
31 * This should be safe as typically there should be at most 1 portal per network. */
32#define NDISC_CAPTIVE_PORTAL_MAX 64U
4df16cd0
YW
33/* Neither defined in the RFC. Just for safety. Otherwise, malformed messages can make clients trigger OOM.
34 * Not sure if the threshold is high enough. Let's adjust later if not. */
35#define NDISC_PREF64_MAX 64U
fe307276 36
062c020f
YW
37bool link_ipv6_accept_ra_enabled(Link *link) {
38 assert(link);
39
40 if (!socket_ipv6_is_supported())
41 return false;
42
43 if (link->flags & IFF_LOOPBACK)
44 return false;
45
f4ef1c19
YW
46 if (link->iftype == ARPHRD_CAN)
47 return false;
48
062c020f
YW
49 if (!link->network)
50 return false;
51
bd7e0a3f 52 if (!link_may_have_ipv6ll(link, /* check_multicast = */ true))
062c020f
YW
53 return false;
54
3773eb54
YW
55 assert(link->network->ipv6_accept_ra >= 0);
56 return link->network->ipv6_accept_ra;
57}
58
59void network_adjust_ipv6_accept_ra(Network *network) {
60 assert(network);
61
62 if (!FLAGS_SET(network->link_local, ADDRESS_FAMILY_IPV6)) {
63 if (network->ipv6_accept_ra > 0)
f81ac115 64 log_warning("%s: IPv6AcceptRA= is enabled but IPv6 link-local addressing is disabled or not supported. "
3773eb54
YW
65 "Disabling IPv6AcceptRA=.", network->filename);
66 network->ipv6_accept_ra = false;
67 }
68
69 if (network->ipv6_accept_ra < 0)
062c020f 70 /* default to accept RA if ip_forward is disabled and ignore RA if ip_forward is enabled */
3773eb54 71 network->ipv6_accept_ra = !FLAGS_SET(network->ip_forward, ADDRESS_FAMILY_IPV6);
de6b6ff8 72
75d26411
YW
73 /* When RouterAllowList=, PrefixAllowList= or RouteAllowList= are specified, then
74 * RouterDenyList=, PrefixDenyList= or RouteDenyList= are ignored, respectively. */
75 if (!set_isempty(network->ndisc_allow_listed_router))
76 network->ndisc_deny_listed_router = set_free_free(network->ndisc_deny_listed_router);
de6b6ff8
SS
77 if (!set_isempty(network->ndisc_allow_listed_prefix))
78 network->ndisc_deny_listed_prefix = set_free_free(network->ndisc_deny_listed_prefix);
79 if (!set_isempty(network->ndisc_allow_listed_route_prefix))
80 network->ndisc_deny_listed_route_prefix = set_free_free(network->ndisc_deny_listed_route_prefix);
062c020f
YW
81}
82
3b6a3bde 83static int ndisc_check_ready(Link *link);
50550722 84
3b6a3bde
YW
85static int ndisc_address_ready_callback(Address *address) {
86 Address *a;
50550722 87
3b6a3bde
YW
88 assert(address);
89 assert(address->link);
50550722 90
3b6a3bde
YW
91 SET_FOREACH(a, address->link->addresses)
92 if (a->source == NETWORK_CONFIG_SOURCE_NDISC)
93 a->callback = NULL;
50550722 94
3b6a3bde
YW
95 return ndisc_check_ready(address->link);
96}
50550722 97
3b6a3bde
YW
98static int ndisc_check_ready(Link *link) {
99 bool found = false, ready = false;
100 Address *address;
50550722 101
3b6a3bde 102 assert(link);
50550722 103
3b6a3bde
YW
104 if (link->ndisc_messages > 0) {
105 log_link_debug(link, "%s(): SLAAC addresses and routes are not set.", __func__);
106 return 0;
107 }
50550722 108
3b6a3bde
YW
109 SET_FOREACH(address, link->addresses) {
110 if (address->source != NETWORK_CONFIG_SOURCE_NDISC)
111 continue;
50550722 112
3b6a3bde 113 found = true;
50550722 114
3b6a3bde
YW
115 if (address_is_ready(address)) {
116 ready = true;
117 break;
50550722 118 }
3b6a3bde 119 }
50550722 120
3b6a3bde
YW
121 if (found && !ready) {
122 SET_FOREACH(address, link->addresses)
123 if (address->source == NETWORK_CONFIG_SOURCE_NDISC)
124 address->callback = ndisc_address_ready_callback;
50550722 125
3b6a3bde
YW
126 log_link_debug(link, "%s(): no SLAAC address is ready.", __func__);
127 return 0;
50550722
YW
128 }
129
3b6a3bde
YW
130 link->ndisc_configured = true;
131 log_link_debug(link, "SLAAC addresses and routes set.");
50550722 132
3b6a3bde
YW
133 link_check_ready(link);
134 return 0;
50550722
YW
135}
136
80d62d4f 137static int ndisc_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Request *req, Link *link, Route *route) {
3b015d40
TG
138 int r;
139
140 assert(link);
3b015d40 141
2c0b49ba 142 r = route_configure_handler_internal(rtnl, m, link, route, "Could not set NDisc route");
5a07fa9d
YW
143 if (r <= 0)
144 return r;
3b015d40 145
3b6a3bde 146 r = ndisc_check_ready(link);
50550722 147 if (r < 0)
3b6a3bde 148 link_enter_failed(link);
76c5a0f2 149
3b6a3bde 150 return 1;
76c5a0f2
YW
151}
152
6f812d28
YW
153static void ndisc_set_route_priority(Link *link, Route *route) {
154 assert(link);
155 assert(route);
156
157 if (route->priority_set)
158 return; /* explicitly configured. */
159
160 switch (route->pref) {
161 case SD_NDISC_PREFERENCE_LOW:
162 route->priority = link->network->ipv6_accept_ra_route_metric_low;
163 break;
164 case SD_NDISC_PREFERENCE_MEDIUM:
165 route->priority = link->network->ipv6_accept_ra_route_metric_medium;
166 break;
167 case SD_NDISC_PREFERENCE_HIGH:
168 route->priority = link->network->ipv6_accept_ra_route_metric_high;
169 break;
170 default:
171 assert_not_reached();
172 }
173}
174
e217da13 175static int ndisc_request_route(Route *route, Link *link, sd_ndisc_router *rt) {
3b6a3bde 176 struct in6_addr router;
f141b2c0 177 uint8_t hop_limit = 0;
fc4a7f13 178 uint32_t mtu = 0;
d9a95033 179 bool is_new;
76c5a0f2
YW
180 int r;
181
182 assert(route);
183 assert(link);
8d01e44c 184 assert(link->manager);
f95fb199 185 assert(link->network);
76c5a0f2
YW
186 assert(rt);
187
3b6a3bde
YW
188 r = sd_ndisc_router_get_address(rt, &router);
189 if (r < 0)
190 return r;
191
fc4a7f13
SS
192 if (link->network->ipv6_accept_ra_use_mtu) {
193 r = sd_ndisc_router_get_mtu(rt, &mtu);
194 if (r < 0 && r != -ENODATA)
34acdf90 195 return log_link_warning_errno(link, r, "Failed to get MTU from RA: %m");
fc4a7f13
SS
196 }
197
39713b07 198 if (link->network->ipv6_accept_ra_use_hop_limit) {
f141b2c0
SS
199 r = sd_ndisc_router_get_hop_limit(rt, &hop_limit);
200 if (r < 0 && r != -ENODATA)
34acdf90 201 return log_link_warning_errno(link, r, "Failed to get hop limit from RA: %m");
93e583aa 202 }
f141b2c0 203
3b6a3bde
YW
204 route->source = NETWORK_CONFIG_SOURCE_NDISC;
205 route->provider.in6 = router;
429dc05a
YW
206 if (!route->table_set)
207 route->table = link_get_ipv6_accept_ra_route_table(link);
429dc05a
YW
208 if (!route->protocol_set)
209 route->protocol = RTPROT_RA;
ebf4fa1e
YW
210 r = route_metric_set(&route->metric, RTAX_MTU, mtu);
211 if (r < 0)
212 return r;
213 r = route_metric_set(&route->metric, RTAX_HOPLIMIT, hop_limit);
214 if (r < 0)
215 return r;
216 r = route_metric_set(&route->metric, RTAX_QUICKACK, link->network->ipv6_accept_ra_quickack);
217 if (r < 0)
218 return r;
f141b2c0 219
2f542fc3
YW
220 r = route_adjust_nexthops(route, link);
221 if (r < 0)
222 return r;
223
972f1d17
YW
224 uint8_t pref, pref_original = route->pref;
225 FOREACH_ARGUMENT(pref, SD_NDISC_PREFERENCE_LOW, SD_NDISC_PREFERENCE_MEDIUM, SD_NDISC_PREFERENCE_HIGH) {
226 Route *existing;
227 Request *req;
228
229 /* If the preference is specified by the user config (that is, for semi-static routes),
230 * rather than RA, then only search conflicting routes that have the same preference. */
231 if (route->pref_set && pref != pref_original)
232 continue;
233
234 route->pref = pref;
235 ndisc_set_route_priority(link, route);
236
237 /* Note, here do not call route_remove_and_cancel() with 'route' directly, otherwise
238 * existing route(s) may be removed needlessly. */
239
240 if (route_get(link->manager, route, &existing) >= 0) {
241 /* Found an existing route that may conflict with this route. */
242 if (!route_can_update(existing, route)) {
243 log_link_debug(link, "Found an existing route that conflicts with new route based on a received RA, removing.");
244 r = route_remove_and_cancel(existing, link->manager);
245 if (r < 0)
246 return r;
247 }
248 }
249
250 if (route_get_request(link->manager, route, &req) >= 0) {
251 existing = ASSERT_PTR(req->userdata);
252 if (!route_can_update(existing, route)) {
253 log_link_debug(link, "Found a pending route request that conflicts with new request based on a received RA, cancelling.");
254 r = route_remove_and_cancel(existing, link->manager);
255 if (r < 0)
256 return r;
257 }
258 }
259 }
260
261 /* The preference (and priority) may be changed in the above loop. Restore it. */
262 route->pref = pref_original;
263 ndisc_set_route_priority(link, route);
264
8d01e44c 265 is_new = route_get(link->manager, route, NULL) < 0;
d9a95033 266
5a18697d 267 r = link_request_route(link, route, &link->ndisc_messages, ndisc_route_handler);
d9a95033
YW
268 if (r < 0)
269 return r;
270 if (r > 0 && is_new)
3b6a3bde 271 link->ndisc_configured = false;
50550722 272
d9a95033 273 return 0;
50550722
YW
274}
275
479d3e19
YW
276static int ndisc_remove_route(Route *route, Link *link) {
277 int r;
278
279 assert(route);
280 assert(link);
281 assert(link->manager);
282
283 ndisc_set_route_priority(link, route);
284
285 if (!route->table_set)
286 route->table = link_get_ipv6_accept_ra_route_table(link);
287
288 r = route_adjust_nexthops(route, link);
289 if (r < 0)
290 return r;
291
292 if (route->pref_set) {
293 ndisc_set_route_priority(link, route);
294 return route_remove_and_cancel(route, link->manager);
295 }
296
297 uint8_t pref;
298 FOREACH_ARGUMENT(pref, SD_NDISC_PREFERENCE_LOW, SD_NDISC_PREFERENCE_MEDIUM, SD_NDISC_PREFERENCE_HIGH) {
299 route->pref = pref;
300 ndisc_set_route_priority(link, route);
301 r = route_remove_and_cancel(route, link->manager);
302 if (r < 0)
303 return r;
304 }
305
306 return 0;
307}
308
80d62d4f 309static int ndisc_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Request *req, Link *link, Address *address) {
73854ba1
YW
310 int r;
311
312 assert(link);
73854ba1 313
5a07fa9d
YW
314 r = address_configure_handler_internal(rtnl, m, link, "Could not set NDisc address");
315 if (r <= 0)
316 return r;
73854ba1 317
3b6a3bde 318 r = ndisc_check_ready(link);
50550722 319 if (r < 0)
3b6a3bde 320 link_enter_failed(link);
69203fba 321
3b6a3bde 322 return 1;
69203fba
YW
323}
324
9e79ef91 325static int ndisc_request_address(Address *address, Link *link, sd_ndisc_router *rt) {
3b6a3bde 326 struct in6_addr router;
d9a95033 327 bool is_new;
76c5a0f2
YW
328 int r;
329
330 assert(address);
331 assert(link);
332 assert(rt);
333
3b6a3bde
YW
334 r = sd_ndisc_router_get_address(rt, &router);
335 if (r < 0)
76c5a0f2
YW
336 return r;
337
3b6a3bde
YW
338 address->source = NETWORK_CONFIG_SOURCE_NDISC;
339 address->provider.in6 = router;
76c5a0f2 340
4b3590c3
TM
341 r = free_and_strdup_warn(&address->netlabel, link->network->ndisc_netlabel);
342 if (r < 0)
343 return r;
344
e720ad88
YW
345 Address *existing;
346 if (address_get_harder(link, address, &existing) < 0)
347 is_new = true;
348 else if (address_can_update(existing, address))
349 is_new = false;
350 else if (existing->source == NETWORK_CONFIG_SOURCE_DHCP6) {
351 /* SLAAC address is preferred over DHCPv6 address. */
352 log_link_debug(link, "Conflicting DHCPv6 address %s exists, removing.",
353 IN_ADDR_PREFIX_TO_STRING(existing->family, &existing->in_addr, existing->prefixlen));
354 r = address_remove(existing, link);
355 if (r < 0)
356 return r;
357
358 is_new = true;
359 } else {
360 /* Conflicting static address is configured?? */
361 log_link_debug(link, "Conflicting address %s exists, ignoring request.",
362 IN_ADDR_PREFIX_TO_STRING(existing->family, &existing->in_addr, existing->prefixlen));
363 return 0;
364 }
d9a95033 365
f60e6558 366 r = link_request_address(link, address, &link->ndisc_messages,
d9a95033
YW
367 ndisc_address_handler, NULL);
368 if (r < 0)
369 return r;
370 if (r > 0 && is_new)
3b6a3bde 371 link->ndisc_configured = false;
3b6a3bde 372
d9a95033 373 return 0;
76c5a0f2
YW
374}
375
479d3e19
YW
376static int ndisc_router_drop_default(Link *link, sd_ndisc_router *rt) {
377 _cleanup_(route_unrefp) Route *route = NULL;
378 struct in6_addr gateway;
379 int r;
380
381 assert(link);
382 assert(link->network);
383 assert(rt);
384
385 r = sd_ndisc_router_get_address(rt, &gateway);
386 if (r < 0)
387 return log_link_warning_errno(link, r, "Failed to get router address from RA: %m");
388
389 r = route_new(&route);
390 if (r < 0)
391 return log_oom();
392
393 route->family = AF_INET6;
394 route->nexthop.family = AF_INET6;
395 route->nexthop.gw.in6 = gateway;
396
397 r = ndisc_remove_route(route, link);
398 if (r < 0)
399 return log_link_warning_errno(link, r, "Failed to remove the default gateway configured by RA: %m");
400
401 Route *route_gw;
402 HASHMAP_FOREACH(route_gw, link->network->routes_by_section) {
403 _cleanup_(route_unrefp) Route *tmp = NULL;
404
405 if (!route_gw->gateway_from_dhcp_or_ra)
406 continue;
407
408 if (route_gw->nexthop.family != AF_INET6)
409 continue;
410
411 r = route_dup(route_gw, NULL, &tmp);
412 if (r < 0)
413 return r;
414
415 tmp->nexthop.gw.in6 = gateway;
416
417 r = ndisc_remove_route(tmp, link);
418 if (r < 0)
419 return log_link_warning_errno(link, r, "Could not remove semi-static gateway: %m");
420 }
421
422 return 0;
423}
424
d5017c84 425static int ndisc_router_process_default(Link *link, sd_ndisc_router *rt) {
6197db53 426 usec_t lifetime_usec;
b8ce3b44 427 struct in6_addr gateway;
1e7a0e21 428 unsigned preference;
13e8a49a 429 int r;
3b015d40 430
3b015d40 431 assert(link);
610c0db1 432 assert(link->network);
1e7a0e21 433 assert(rt);
3b015d40 434
479d3e19
YW
435 /* If the router lifetime is zero, the router should not be used as the default gateway. */
436 r = sd_ndisc_router_get_lifetime(rt, NULL);
437 if (r < 0)
438 return r;
439 if (r == 0)
440 return ndisc_router_drop_default(link, rt);
441
610c0db1
YW
442 if (!link->network->ipv6_accept_ra_use_gateway &&
443 hashmap_isempty(link->network->routes_by_section))
444 return 0;
445
6197db53 446 r = sd_ndisc_router_get_lifetime_timestamp(rt, CLOCK_BOOTTIME, &lifetime_usec);
d5017c84 447 if (r < 0)
2c5bca17 448 return log_link_warning_errno(link, r, "Failed to get gateway lifetime from RA: %m");
d5017c84 449
b8ce3b44 450 r = sd_ndisc_router_get_address(rt, &gateway);
d5017c84 451 if (r < 0)
2c5bca17 452 return log_link_warning_errno(link, r, "Failed to get gateway address from RA: %m");
1e7a0e21 453
5d003031 454 if (link_get_ipv6_address(link, &gateway, 0, NULL) >= 0) {
84dbb3fd 455 if (DEBUG_LOGGING)
5eec0a08 456 log_link_debug(link, "No NDisc route added, gateway %s matches local address",
84dbb3fd 457 IN6_ADDR_TO_STRING(&gateway));
5eec0a08 458 return 0;
ce2ea782 459 }
6d7c7615 460
1e7a0e21 461 r = sd_ndisc_router_get_preference(rt, &preference);
d5017c84 462 if (r < 0)
34acdf90 463 return log_link_warning_errno(link, r, "Failed to get router preference from RA: %m");
1e7a0e21 464
610c0db1 465 if (link->network->ipv6_accept_ra_use_gateway) {
74c301b9 466 _cleanup_(route_unrefp) Route *route = NULL;
1e7a0e21 467
610c0db1
YW
468 r = route_new(&route);
469 if (r < 0)
470 return log_oom();
1e7a0e21 471
610c0db1
YW
472 route->family = AF_INET6;
473 route->pref = preference;
054b8c28
YW
474 route->nexthop.family = AF_INET6;
475 route->nexthop.gw.in6 = gateway;
610c0db1 476 route->lifetime_usec = lifetime_usec;
610c0db1 477
e217da13 478 r = ndisc_request_route(route, link, rt);
610c0db1 479 if (r < 0)
2c5bca17 480 return log_link_warning_errno(link, r, "Could not request default route: %m");
610c0db1 481 }
d5017c84 482
1985c54f 483 Route *route_gw;
2a54a044 484 HASHMAP_FOREACH(route_gw, link->network->routes_by_section) {
74c301b9 485 _cleanup_(route_unrefp) Route *route = NULL;
610c0db1 486
1a3a6309 487 if (!route_gw->gateway_from_dhcp_or_ra)
1985c54f
YW
488 continue;
489
054b8c28 490 if (route_gw->nexthop.family != AF_INET6)
1985c54f
YW
491 continue;
492
413ea20a 493 r = route_dup(route_gw, NULL, &route);
76c5a0f2
YW
494 if (r < 0)
495 return r;
496
054b8c28 497 route->nexthop.gw.in6 = gateway;
76c5a0f2
YW
498 if (!route->pref_set)
499 route->pref = preference;
91fc5135 500 route->lifetime_usec = lifetime_usec;
76c5a0f2 501
e217da13 502 r = ndisc_request_route(route, link, rt);
13e8a49a 503 if (r < 0)
2c5bca17 504 return log_link_warning_errno(link, r, "Could not request gateway: %m");
1985c54f
YW
505 }
506
0f9a2b80
YW
507 return 0;
508}
91750028 509
0f9a2b80 510static int ndisc_router_process_icmp6_ratelimit(Link *link, sd_ndisc_router *rt) {
be89a76a 511 usec_t icmp6_ratelimit, msec;
0f9a2b80 512 int r;
91750028 513
0f9a2b80
YW
514 assert(link);
515 assert(link->network);
516 assert(rt);
91750028 517
0f9a2b80
YW
518 if (!link->network->ipv6_accept_ra_use_icmp6_ratelimit)
519 return 0;
520
ffef01ac
YW
521 /* Ignore the icmp6 ratelimit field of the RA header if the lifetime is zero. */
522 r = sd_ndisc_router_get_lifetime(rt, NULL);
523 if (r <= 0)
524 return r;
525
0f9a2b80 526 r = sd_ndisc_router_get_icmp6_ratelimit(rt, &icmp6_ratelimit);
b409ac6c
YW
527 if (r < 0)
528 return log_link_warning_errno(link, r, "Failed to get ICMP6 ratelimit from RA: %m");
91750028 529
be89a76a 530 /* We do not allow 0 here. */
6197db53 531 if (!timestamp_is_set(icmp6_ratelimit))
0f9a2b80
YW
532 return 0;
533
be89a76a
YW
534 msec = DIV_ROUND_UP(icmp6_ratelimit, USEC_PER_MSEC);
535 if (msec <= 0 || msec > INT_MAX)
536 return 0;
537
6197db53
YW
538 /* Limit the maximal rates for sending ICMPv6 packets. 0 to disable any limiting, otherwise the
539 * minimal space between responses in milliseconds. Default: 1000. */
be89a76a 540 r = sysctl_write_ip_property_int(AF_INET6, NULL, "icmp/ratelimit", (int) msec);
0f9a2b80
YW
541 if (r < 0)
542 log_link_warning_errno(link, r, "Failed to apply ICMP6 ratelimit, ignoring: %m");
543
d5017c84 544 return 0;
1e7a0e21
LP
545}
546
1452d497
YW
547static int ndisc_router_process_reachable_time(Link *link, sd_ndisc_router *rt) {
548 usec_t reachable_time, msec;
549 int r;
550
551 assert(link);
552 assert(link->network);
553 assert(rt);
554
555 if (!link->network->ipv6_accept_ra_use_reachable_time)
556 return 0;
557
558 /* Ignore the reachable time field of the RA header if the lifetime is zero. */
559 r = sd_ndisc_router_get_lifetime(rt, NULL);
560 if (r <= 0)
561 return r;
562
563 r = sd_ndisc_router_get_reachable_time(rt, &reachable_time);
564 if (r < 0)
565 return log_link_warning_errno(link, r, "Failed to get reachable time from RA: %m");
566
567 /* 0 is the unspecified value and must not be set (see RFC4861, 6.3.4) */
568 if (!timestamp_is_set(reachable_time))
569 return 0;
570
571 msec = DIV_ROUND_UP(reachable_time, USEC_PER_MSEC);
572 if (msec <= 0 || msec > UINT32_MAX) {
573 log_link_debug(link, "Failed to get reachable time from RA - out of range (%"PRIu64"), ignoring", msec);
574 return 0;
575 }
576
577 /* Set the reachable time for Neighbor Solicitations. */
578 r = sysctl_write_ip_neighbor_property_uint32(AF_INET6, link->ifname, "base_reachable_time_ms", (uint32_t) msec);
579 if (r < 0)
580 log_link_warning_errno(link, r, "Failed to apply neighbor reachable time (%"PRIu64"), ignoring: %m", msec);
581
582 return 0;
583}
584
d4c8de21
MM
585static int ndisc_router_process_retransmission_time(Link *link, sd_ndisc_router *rt) {
586 usec_t retrans_time, msec;
587 int r;
588
589 assert(link);
590 assert(link->network);
591 assert(rt);
592
593 if (!link->network->ipv6_accept_ra_use_retransmission_time)
594 return 0;
595
ffef01ac
YW
596 /* Ignore the retransmission time field of the RA header if the lifetime is zero. */
597 r = sd_ndisc_router_get_lifetime(rt, NULL);
598 if (r <= 0)
599 return r;
600
d4c8de21 601 r = sd_ndisc_router_get_retransmission_time(rt, &retrans_time);
b409ac6c
YW
602 if (r < 0)
603 return log_link_warning_errno(link, r, "Failed to get retransmission time from RA: %m");
d4c8de21
MM
604
605 /* 0 is the unspecified value and must not be set (see RFC4861, 6.3.4) */
606 if (!timestamp_is_set(retrans_time))
607 return 0;
608
609 msec = DIV_ROUND_UP(retrans_time, USEC_PER_MSEC);
610 if (msec <= 0 || msec > UINT32_MAX) {
611 log_link_debug(link, "Failed to get retransmission time from RA - out of range (%"PRIu64"), ignoring", msec);
612 return 0;
613 }
614
6a8026e8 615 /* Set the retransmission time for Neighbor Solicitations. */
d4c8de21
MM
616 r = sysctl_write_ip_neighbor_property_uint32(AF_INET6, link->ifname, "retrans_time_ms", (uint32_t) msec);
617 if (r < 0)
ffef01ac 618 log_link_warning_errno(link, r, "Failed to apply neighbor retransmission time (%"PRIu64"), ignoring: %m", msec);
d4c8de21
MM
619
620 return 0;
621}
622
b15ed2be
MM
623static int ndisc_router_process_hop_limit(Link *link, sd_ndisc_router *rt) {
624 uint8_t hop_limit;
625 int r;
626
627 assert(link);
628 assert(link->network);
629 assert(rt);
630
631 if (!link->network->ipv6_accept_ra_use_hop_limit)
632 return 0;
633
ffef01ac
YW
634 /* Ignore the hop limit field of the RA header if the lifetime is zero. */
635 r = sd_ndisc_router_get_lifetime(rt, NULL);
636 if (r <= 0)
637 return r;
638
b15ed2be
MM
639 r = sd_ndisc_router_get_hop_limit(rt, &hop_limit);
640 if (r < 0)
641 return log_link_warning_errno(link, r, "Failed to get hop limit from RA: %m");
642
643 /* 0 is the unspecified value and must not be set (see RFC4861, 6.3.4):
644 *
645 * A Router Advertisement field (e.g., Cur Hop Limit, Reachable Time, and Retrans Timer) may contain
646 * a value denoting that it is unspecified. In such cases, the parameter should be ignored and the
647 * host should continue using whatever value it is already using. In particular, a host MUST NOT
648 * interpret the unspecified value as meaning change back to the default value that was in use before
649 * the first Router Advertisement was received.
650 *
651 * If the received Cur Hop Limit value is non-zero, the host SHOULD set
652 * its CurHopLimit variable to the received value.*/
653 if (hop_limit <= 0)
654 return 0;
655
656 r = sysctl_write_ip_property_uint32(AF_INET6, link->ifname, "hop_limit", (uint32_t) hop_limit);
657 if (r < 0)
658 log_link_warning_errno(link, r, "Failed to apply hop_limit (%u), ignoring: %m", hop_limit);
659
660 return 0;
661}
662
d5017c84 663static int ndisc_router_process_autonomous_prefix(Link *link, sd_ndisc_router *rt) {
6197db53 664 usec_t lifetime_valid_usec, lifetime_preferred_usec;
d207581f 665 _cleanup_set_free_ Set *addresses = NULL;
151b8ea3 666 struct in6_addr prefix, *a;
1e7a0e21
LP
667 unsigned prefixlen;
668 int r;
669
670 assert(link);
fbdda4bb 671 assert(link->network);
1e7a0e21
LP
672 assert(rt);
673
fbdda4bb
YW
674 if (!link->network->ipv6_accept_ra_use_autonomous_prefix)
675 return 0;
676
151b8ea3
YW
677 r = sd_ndisc_router_prefix_get_address(rt, &prefix);
678 if (r < 0)
2c5bca17 679 return log_link_warning_errno(link, r, "Failed to get prefix address: %m");
151b8ea3 680
1e7a0e21 681 r = sd_ndisc_router_prefix_get_prefixlen(rt, &prefixlen);
d5017c84 682 if (r < 0)
2c5bca17 683 return log_link_warning_errno(link, r, "Failed to get prefix length: %m");
1e7a0e21 684
868bd1aa 685 /* ndisc_generate_addresses() below requires the prefix length <= 64. */
151b8ea3 686 if (prefixlen > 64) {
c71384a9
ZJS
687 log_link_debug(link, "Prefix is longer than 64, ignoring autonomous prefix %s.",
688 IN6_ADDR_PREFIX_TO_STRING(&prefix, prefixlen));
151b8ea3
YW
689 return 0;
690 }
691
6197db53 692 r = sd_ndisc_router_prefix_get_valid_lifetime_timestamp(rt, CLOCK_BOOTTIME, &lifetime_valid_usec);
d5017c84 693 if (r < 0)
2c5bca17 694 return log_link_warning_errno(link, r, "Failed to get prefix valid lifetime: %m");
1e7a0e21 695
6197db53 696 r = sd_ndisc_router_prefix_get_preferred_lifetime_timestamp(rt, CLOCK_BOOTTIME, &lifetime_preferred_usec);
d5017c84 697 if (r < 0)
2c5bca17 698 return log_link_warning_errno(link, r, "Failed to get prefix preferred lifetime: %m");
3b015d40 699
92bdc3ff 700 /* The preferred lifetime is never greater than the valid lifetime */
6197db53 701 if (lifetime_preferred_usec > lifetime_valid_usec)
d5017c84 702 return 0;
92bdc3ff 703
868bd1aa 704 r = ndisc_generate_addresses(link, &prefix, prefixlen, &addresses);
5f506a55 705 if (r < 0)
2c5bca17 706 return log_link_warning_errno(link, r, "Failed to generate SLAAC addresses: %m");
c24c83dc 707
90e74a66 708 SET_FOREACH(a, addresses) {
ebd96906 709 _cleanup_(address_unrefp) Address *address = NULL;
13e8a49a 710
76c5a0f2
YW
711 r = address_new(&address);
712 if (r < 0)
713 return log_oom();
714
715 address->family = AF_INET6;
25db3aea 716 address->in_addr.in6 = *a;
76c5a0f2
YW
717 address->prefixlen = prefixlen;
718 address->flags = IFA_F_NOPREFIXROUTE|IFA_F_MANAGETEMPADDR;
16bc8635
YW
719 address->lifetime_valid_usec = lifetime_valid_usec;
720 address->lifetime_preferred_usec = lifetime_preferred_usec;
fe841414 721
479d3e19
YW
722 /* draft-ietf-6man-slaac-renum-07 section 4.2
723 * https://datatracker.ietf.org/doc/html/draft-ietf-6man-slaac-renum-07#section-4.2
724 *
725 * If the advertised prefix is equal to the prefix of an address configured by stateless
726 * autoconfiguration in the list, the valid lifetime and the preferred lifetime of the
727 * address should be updated by processing the Valid Lifetime and the Preferred Lifetime
728 * (respectively) in the received advertisement. */
729 if (lifetime_valid_usec == 0) {
730 r = address_remove_and_cancel(address, link);
731 if (r < 0)
732 return log_link_warning_errno(link, r, "Could not remove SLAAC address: %m");
733 } else {
734 r = ndisc_request_address(address, link, rt);
735 if (r < 0)
736 return log_link_warning_errno(link, r, "Could not request SLAAC address: %m");
737 }
3b015d40 738 }
d5017c84
YW
739
740 return 0;
3b015d40
TG
741}
742
d5017c84 743static int ndisc_router_process_onlink_prefix(Link *link, sd_ndisc_router *rt) {
74c301b9 744 _cleanup_(route_unrefp) Route *route = NULL;
f44eebd1 745 unsigned prefixlen, preference;
6197db53 746 usec_t lifetime_usec;
167c7ae5 747 struct in6_addr prefix;
3b015d40
TG
748 int r;
749
3b015d40 750 assert(link);
fbdda4bb 751 assert(link->network);
1e7a0e21 752 assert(rt);
3b015d40 753
fbdda4bb
YW
754 if (!link->network->ipv6_accept_ra_use_onlink_prefix)
755 return 0;
756
6197db53 757 r = sd_ndisc_router_prefix_get_valid_lifetime_timestamp(rt, CLOCK_BOOTTIME, &lifetime_usec);
09845af5 758 if (r < 0)
2c5bca17 759 return log_link_warning_errno(link, r, "Failed to get prefix lifetime: %m");
09845af5 760
167c7ae5
YW
761 r = sd_ndisc_router_prefix_get_address(rt, &prefix);
762 if (r < 0)
2c5bca17 763 return log_link_warning_errno(link, r, "Failed to get prefix address: %m");
167c7ae5 764
1e7a0e21 765 r = sd_ndisc_router_prefix_get_prefixlen(rt, &prefixlen);
d5017c84 766 if (r < 0)
2c5bca17 767 return log_link_warning_errno(link, r, "Failed to get prefix length: %m");
1e7a0e21 768
f44eebd1
SS
769 /* Prefix Information option does not have preference, hence we use the 'main' preference here */
770 r = sd_ndisc_router_get_preference(rt, &preference);
771 if (r < 0)
a8b0b848 772 return log_link_warning_errno(link, r, "Failed to get router preference from RA: %m");
f44eebd1 773
3b015d40 774 r = route_new(&route);
d5017c84 775 if (r < 0)
13e8a49a 776 return log_oom();
3b015d40 777
3b015d40 778 route->family = AF_INET6;
167c7ae5 779 route->dst.in6 = prefix;
3b015d40 780 route->dst_prefixlen = prefixlen;
f44eebd1 781 route->pref = preference;
6197db53 782 route->lifetime_usec = lifetime_usec;
3b015d40 783
e217da13 784 r = ndisc_request_route(route, link, rt);
13e8a49a 785 if (r < 0)
2c5bca17 786 return log_link_warning_errno(link, r, "Could not request prefix route: %m");
d5017c84
YW
787
788 return 0;
3b015d40
TG
789}
790
155d7a2c
YW
791static int ndisc_router_drop_onlink_prefix(Link *link, sd_ndisc_router *rt) {
792 _cleanup_(route_unrefp) Route *route = NULL;
479d3e19 793 unsigned prefixlen;
155d7a2c
YW
794 struct in6_addr prefix;
795 usec_t lifetime_usec;
796 int r;
797
798 assert(link);
799 assert(link->network);
800 assert(rt);
801
802 /* RFC 4861 section 6.3.4.
803 * Note, however, that a Prefix Information option with the on-link flag set to zero conveys no
804 * information concerning on-link determination and MUST NOT be interpreted to mean that addresses
805 * covered by the prefix are off-link. The only way to cancel a previous on-link indication is to
806 * advertise that prefix with the L-bit set and the Lifetime set to zero. */
807
808 if (!link->network->ipv6_accept_ra_use_onlink_prefix)
809 return 0;
810
811 r = sd_ndisc_router_prefix_get_valid_lifetime(rt, &lifetime_usec);
812 if (r < 0)
813 return log_link_warning_errno(link, r, "Failed to get prefix lifetime: %m");
814
815 if (lifetime_usec != 0)
816 return 0;
817
818 r = sd_ndisc_router_prefix_get_address(rt, &prefix);
819 if (r < 0)
820 return log_link_warning_errno(link, r, "Failed to get prefix address: %m");
821
822 r = sd_ndisc_router_prefix_get_prefixlen(rt, &prefixlen);
823 if (r < 0)
824 return log_link_warning_errno(link, r, "Failed to get prefix length: %m");
825
155d7a2c
YW
826 r = route_new(&route);
827 if (r < 0)
828 return log_oom();
829
830 route->family = AF_INET6;
831 route->dst.in6 = prefix;
832 route->dst_prefixlen = prefixlen;
155d7a2c 833
479d3e19 834 r = ndisc_remove_route(route, link);
155d7a2c
YW
835 if (r < 0)
836 return log_link_warning_errno(link, r, "Could not remove prefix route: %m");
837
838 return 0;
839}
840
fbdda4bb
YW
841static int ndisc_router_process_prefix(Link *link, sd_ndisc_router *rt) {
842 unsigned prefixlen;
843 struct in6_addr a;
844 uint8_t flags;
845 int r;
846
847 assert(link);
848 assert(link->network);
849 assert(rt);
850
851 r = sd_ndisc_router_prefix_get_address(rt, &a);
852 if (r < 0)
2c5bca17 853 return log_link_warning_errno(link, r, "Failed to get prefix address: %m");
fbdda4bb 854
a115c60e
YW
855 /* RFC 4861 Section 4.6.2:
856 * A router SHOULD NOT send a prefix option for the link-local prefix and a host SHOULD ignore such
857 * a prefix option. */
858 if (in6_addr_is_link_local(&a)) {
479d3e19 859 log_link_debug(link, "Received link-local prefix, ignoring prefix.");
a115c60e
YW
860 return 0;
861 }
862
fbdda4bb
YW
863 r = sd_ndisc_router_prefix_get_prefixlen(rt, &prefixlen);
864 if (r < 0)
2c5bca17 865 return log_link_warning_errno(link, r, "Failed to get prefix length: %m");
fbdda4bb
YW
866
867 if (in6_prefix_is_filtered(&a, prefixlen, link->network->ndisc_allow_listed_prefix, link->network->ndisc_deny_listed_prefix)) {
c71384a9
ZJS
868 if (DEBUG_LOGGING)
869 log_link_debug(link, "Prefix '%s' is %s, ignoring",
870 !set_isempty(link->network->ndisc_allow_listed_prefix) ? "not in allow list"
871 : "in deny list",
872 IN6_ADDR_PREFIX_TO_STRING(&a, prefixlen));
fbdda4bb
YW
873 return 0;
874 }
875
876 r = sd_ndisc_router_prefix_get_flags(rt, &flags);
877 if (r < 0)
2c5bca17 878 return log_link_warning_errno(link, r, "Failed to get RA prefix flags: %m");
fbdda4bb 879
155d7a2c 880 if (FLAGS_SET(flags, ND_OPT_PI_FLAG_ONLINK))
fbdda4bb 881 r = ndisc_router_process_onlink_prefix(link, rt);
155d7a2c
YW
882 else
883 r = ndisc_router_drop_onlink_prefix(link, rt);
884 if (r < 0)
885 return r;
fbdda4bb
YW
886
887 if (FLAGS_SET(flags, ND_OPT_PI_FLAG_AUTO)) {
888 r = ndisc_router_process_autonomous_prefix(link, rt);
889 if (r < 0)
890 return r;
891 }
892
893 return 0;
894}
895
d5017c84 896static int ndisc_router_process_route(Link *link, sd_ndisc_router *rt) {
74c301b9 897 _cleanup_(route_unrefp) Route *route = NULL;
1e7a0e21 898 unsigned preference, prefixlen;
91fc5135 899 struct in6_addr gateway, dst;
6197db53 900 usec_t lifetime_usec;
7a695d8e 901 int r;
a13c50e7
TG
902
903 assert(link);
a13c50e7 904
610c0db1
YW
905 if (!link->network->ipv6_accept_ra_use_route_prefix)
906 return 0;
907
6197db53 908 r = sd_ndisc_router_route_get_lifetime_timestamp(rt, CLOCK_BOOTTIME, &lifetime_usec);
d5017c84 909 if (r < 0)
2c5bca17 910 return log_link_warning_errno(link, r, "Failed to get route lifetime from RA: %m");
d5017c84 911
b8ce3b44 912 r = sd_ndisc_router_route_get_address(rt, &dst);
d5017c84 913 if (r < 0)
2c5bca17 914 return log_link_warning_errno(link, r, "Failed to get route destination address: %m");
3b015d40 915
c995fa02
YW
916 r = sd_ndisc_router_route_get_prefixlen(rt, &prefixlen);
917 if (r < 0)
2c5bca17 918 return log_link_warning_errno(link, r, "Failed to get route prefix length: %m");
c995fa02 919
80bfc3b9
YW
920 if (in6_addr_is_null(&dst) && prefixlen == 0) {
921 log_link_debug(link, "Route prefix is ::/0, ignoring");
922 return 0;
923 }
924
c71384a9
ZJS
925 if (in6_prefix_is_filtered(&dst, prefixlen,
926 link->network->ndisc_allow_listed_route_prefix,
927 link->network->ndisc_deny_listed_route_prefix)) {
16c89e64 928
c71384a9
ZJS
929 if (DEBUG_LOGGING)
930 log_link_debug(link, "Route prefix %s is %s, ignoring",
931 !set_isempty(link->network->ndisc_allow_listed_route_prefix) ? "not in allow list"
932 : "in deny list",
933 IN6_ADDR_PREFIX_TO_STRING(&dst, prefixlen));
16c89e64
DP
934 return 0;
935 }
936
b8ce3b44 937 r = sd_ndisc_router_get_address(rt, &gateway);
19e334bd 938 if (r < 0)
2c5bca17 939 return log_link_warning_errno(link, r, "Failed to get gateway address from RA: %m");
19e334bd 940
5d003031 941 if (link_get_ipv6_address(link, &gateway, 0, NULL) >= 0) {
84dbb3fd
ZJS
942 if (DEBUG_LOGGING)
943 log_link_debug(link, "Advertised route gateway %s is local to the link, ignoring route",
944 IN6_ADDR_TO_STRING(&gateway));
22101916
DP
945 return 0;
946 }
947
1e7a0e21 948 r = sd_ndisc_router_route_get_preference(rt, &preference);
4b68f708 949 if (r == -EOPNOTSUPP) {
3912d49d
SS
950 log_link_debug_errno(link, r, "Received route prefix with unsupported preference, ignoring: %m");
951 return 0;
952 }
d5017c84 953 if (r < 0)
34acdf90 954 return log_link_warning_errno(link, r, "Failed to get router preference from RA: %m");
1e7a0e21 955
3b015d40 956 r = route_new(&route);
d5017c84 957 if (r < 0)
13e8a49a 958 return log_oom();
3b015d40 959
3b015d40 960 route->family = AF_INET6;
1e7a0e21 961 route->pref = preference;
054b8c28
YW
962 route->nexthop.gw.in6 = gateway;
963 route->nexthop.family = AF_INET6;
b8ce3b44 964 route->dst.in6 = dst;
1e7a0e21 965 route->dst_prefixlen = prefixlen;
6197db53 966 route->lifetime_usec = lifetime_usec;
3b015d40 967
e217da13 968 r = ndisc_request_route(route, link, rt);
13e8a49a 969 if (r < 0)
2c5bca17 970 return log_link_warning_errno(link, r, "Could not request additional route: %m");
d5017c84
YW
971
972 return 0;
9d96e6c3 973}
a13c50e7 974
7a08d314 975static void ndisc_rdnss_hash_func(const NDiscRDNSS *x, struct siphash *state) {
c01a5c05 976 siphash24_compress_typesafe(x->address, state);
1e7a0e21
LP
977}
978
7a08d314 979static int ndisc_rdnss_compare_func(const NDiscRDNSS *a, const NDiscRDNSS *b) {
1e7a0e21
LP
980 return memcmp(&a->address, &b->address, sizeof(a->address));
981}
982
b0b97766
YW
983DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(
984 ndisc_rdnss_hash_ops,
985 NDiscRDNSS,
986 ndisc_rdnss_hash_func,
987 ndisc_rdnss_compare_func,
988 free);
1e7a0e21 989
d5017c84 990static int ndisc_router_process_rdnss(Link *link, sd_ndisc_router *rt) {
6197db53 991 usec_t lifetime_usec;
1e7a0e21 992 const struct in6_addr *a;
50550722 993 struct in6_addr router;
8aba7b83 994 bool updated = false, logged_about_too_many = false;
13e8a49a 995 int n, r;
1e7a0e21
LP
996
997 assert(link);
ad0b2df6 998 assert(link->network);
1e7a0e21
LP
999 assert(rt);
1000
ad0b2df6
YW
1001 if (!link->network->ipv6_accept_ra_use_dns)
1002 return 0;
1003
50550722
YW
1004 r = sd_ndisc_router_get_address(rt, &router);
1005 if (r < 0)
2c5bca17 1006 return log_link_warning_errno(link, r, "Failed to get router address from RA: %m");
50550722 1007
6197db53 1008 r = sd_ndisc_router_rdnss_get_lifetime_timestamp(rt, CLOCK_BOOTTIME, &lifetime_usec);
d5017c84 1009 if (r < 0)
2c5bca17 1010 return log_link_warning_errno(link, r, "Failed to get RDNSS lifetime: %m");
1e7a0e21
LP
1011
1012 n = sd_ndisc_router_rdnss_get_addresses(rt, &a);
d5017c84 1013 if (n < 0)
2c5bca17 1014 return log_link_warning_errno(link, n, "Failed to get RDNSS addresses: %m");
1e7a0e21 1015
b0b97766
YW
1016 for (int j = 0; j < n; j++) {
1017 _cleanup_free_ NDiscRDNSS *x = NULL;
3b6a3bde 1018 NDiscRDNSS *rdnss, d = {
b0b97766
YW
1019 .address = a[j],
1020 };
1e7a0e21 1021
af2aea8b
YW
1022 if (lifetime_usec == 0) {
1023 /* The entry is outdated. */
1024 free(set_remove(link->ndisc_rdnss, &d));
1025 updated = true;
1026 continue;
1027 }
1028
b0b97766
YW
1029 rdnss = set_get(link->ndisc_rdnss, &d);
1030 if (rdnss) {
50550722 1031 rdnss->router = router;
03ccc4b4 1032 rdnss->lifetime_usec = lifetime_usec;
1e7a0e21
LP
1033 continue;
1034 }
1035
8aba7b83
YW
1036 if (set_size(link->ndisc_rdnss) >= NDISC_RDNSS_MAX) {
1037 if (!logged_about_too_many)
1038 log_link_warning(link, "Too many RDNSS records per link. Only first %u records will be used.", NDISC_RDNSS_MAX);
1039 logged_about_too_many = true;
1040 continue;
1041 }
1042
d5017c84
YW
1043 x = new(NDiscRDNSS, 1);
1044 if (!x)
1045 return log_oom();
1e7a0e21 1046
d5017c84 1047 *x = (NDiscRDNSS) {
b0b97766 1048 .address = a[j],
50550722 1049 .router = router,
03ccc4b4 1050 .lifetime_usec = lifetime_usec,
d5017c84 1051 };
1e7a0e21 1052
35e601d4 1053 r = set_ensure_consume(&link->ndisc_rdnss, &ndisc_rdnss_hash_ops, TAKE_PTR(x));
d5017c84
YW
1054 if (r < 0)
1055 return log_oom();
1e7a0e21 1056 assert(r > 0);
9092113d
YW
1057
1058 updated = true;
1e7a0e21 1059 }
d5017c84 1060
9092113d
YW
1061 if (updated)
1062 link_dirty(link);
1063
d5017c84 1064 return 0;
1e7a0e21
LP
1065}
1066
7a08d314 1067static void ndisc_dnssl_hash_func(const NDiscDNSSL *x, struct siphash *state) {
f281fc1e 1068 siphash24_compress_string(NDISC_DNSSL_DOMAIN(x), state);
1e7a0e21
LP
1069}
1070
7a08d314 1071static int ndisc_dnssl_compare_func(const NDiscDNSSL *a, const NDiscDNSSL *b) {
1e7a0e21
LP
1072 return strcmp(NDISC_DNSSL_DOMAIN(a), NDISC_DNSSL_DOMAIN(b));
1073}
1074
b0b97766
YW
1075DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(
1076 ndisc_dnssl_hash_ops,
1077 NDiscDNSSL,
1078 ndisc_dnssl_hash_func,
1079 ndisc_dnssl_compare_func,
1080 free);
1e7a0e21 1081
13e8a49a 1082static int ndisc_router_process_dnssl(Link *link, sd_ndisc_router *rt) {
1e7a0e21 1083 _cleanup_strv_free_ char **l = NULL;
6197db53 1084 usec_t lifetime_usec;
50550722 1085 struct in6_addr router;
8aba7b83 1086 bool updated = false, logged_about_too_many = false;
1e7a0e21
LP
1087 int r;
1088
1089 assert(link);
ad0b2df6 1090 assert(link->network);
1e7a0e21
LP
1091 assert(rt);
1092
ad0b2df6
YW
1093 if (link->network->ipv6_accept_ra_use_domains == DHCP_USE_DOMAINS_NO)
1094 return 0;
1095
50550722
YW
1096 r = sd_ndisc_router_get_address(rt, &router);
1097 if (r < 0)
2c5bca17 1098 return log_link_warning_errno(link, r, "Failed to get router address from RA: %m");
50550722 1099
6197db53 1100 r = sd_ndisc_router_dnssl_get_lifetime_timestamp(rt, CLOCK_BOOTTIME, &lifetime_usec);
13e8a49a 1101 if (r < 0)
2c5bca17 1102 return log_link_warning_errno(link, r, "Failed to get DNSSL lifetime: %m");
1e7a0e21
LP
1103
1104 r = sd_ndisc_router_dnssl_get_domains(rt, &l);
13e8a49a 1105 if (r < 0)
2c5bca17 1106 return log_link_warning_errno(link, r, "Failed to get DNSSL addresses: %m");
1e7a0e21 1107
b0b97766
YW
1108 STRV_FOREACH(j, l) {
1109 _cleanup_free_ NDiscDNSSL *s = NULL;
3b6a3bde 1110 NDiscDNSSL *dnssl;
1e7a0e21 1111
b0b97766
YW
1112 s = malloc0(ALIGN(sizeof(NDiscDNSSL)) + strlen(*j) + 1);
1113 if (!s)
1114 return log_oom();
1e7a0e21 1115
b0b97766 1116 strcpy(NDISC_DNSSL_DOMAIN(s), *j);
1e7a0e21 1117
af2aea8b
YW
1118 if (lifetime_usec == 0) {
1119 /* The entry is outdated. */
1120 free(set_remove(link->ndisc_dnssl, s));
1121 updated = true;
1122 continue;
1123 }
1124
b0b97766
YW
1125 dnssl = set_get(link->ndisc_dnssl, s);
1126 if (dnssl) {
50550722 1127 dnssl->router = router;
03ccc4b4 1128 dnssl->lifetime_usec = lifetime_usec;
1e7a0e21
LP
1129 continue;
1130 }
1131
8aba7b83
YW
1132 if (set_size(link->ndisc_dnssl) >= NDISC_DNSSL_MAX) {
1133 if (!logged_about_too_many)
1134 log_link_warning(link, "Too many DNSSL records per link. Only first %u records will be used.", NDISC_DNSSL_MAX);
1135 logged_about_too_many = true;
1136 continue;
1137 }
1138
50550722 1139 s->router = router;
03ccc4b4 1140 s->lifetime_usec = lifetime_usec;
1e7a0e21 1141
35e601d4 1142 r = set_ensure_consume(&link->ndisc_dnssl, &ndisc_dnssl_hash_ops, TAKE_PTR(s));
13e8a49a
YW
1143 if (r < 0)
1144 return log_oom();
1e7a0e21 1145 assert(r > 0);
9092113d
YW
1146
1147 updated = true;
1e7a0e21 1148 }
13e8a49a 1149
9092113d
YW
1150 if (updated)
1151 link_dirty(link);
1152
13e8a49a 1153 return 0;
1e7a0e21
LP
1154}
1155
64de00c4
YW
1156static NDiscCaptivePortal* ndisc_captive_portal_free(NDiscCaptivePortal *x) {
1157 if (!x)
1158 return NULL;
1159
1160 free(x->captive_portal);
1161 return mfree(x);
1162}
1163
1164DEFINE_TRIVIAL_CLEANUP_FUNC(NDiscCaptivePortal*, ndisc_captive_portal_free);
1165
1166static void ndisc_captive_portal_hash_func(const NDiscCaptivePortal *x, struct siphash *state) {
1167 assert(x);
1168 siphash24_compress_string(x->captive_portal, state);
1169}
1170
1171static int ndisc_captive_portal_compare_func(const NDiscCaptivePortal *a, const NDiscCaptivePortal *b) {
1172 assert(a);
1173 assert(b);
1174 return strcmp_ptr(a->captive_portal, b->captive_portal);
1175}
1176
1177DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(
1178 ndisc_captive_portal_hash_ops,
1179 NDiscCaptivePortal,
1180 ndisc_captive_portal_hash_func,
1181 ndisc_captive_portal_compare_func,
535134bc 1182 ndisc_captive_portal_free);
64de00c4
YW
1183
1184static int ndisc_router_process_captive_portal(Link *link, sd_ndisc_router *rt) {
1185 _cleanup_(ndisc_captive_portal_freep) NDiscCaptivePortal *new_entry = NULL;
1186 _cleanup_free_ char *captive_portal = NULL;
6197db53 1187 usec_t lifetime_usec;
64de00c4
YW
1188 NDiscCaptivePortal *exist;
1189 struct in6_addr router;
64de00c4
YW
1190 const char *uri;
1191 size_t len;
1192 int r;
1193
1194 assert(link);
1195 assert(link->network);
1196 assert(rt);
1197
1198 if (!link->network->ipv6_accept_ra_use_captive_portal)
1199 return 0;
1200
1201 r = sd_ndisc_router_get_address(rt, &router);
1202 if (r < 0)
1203 return log_link_warning_errno(link, r, "Failed to get router address from RA: %m");
1204
218f3738
YW
1205 /* RFC 4861 section 4.2. states that the lifetime in the message header should be used only for the
1206 * default gateway, but the captive portal option does not have a lifetime field, hence, we use the
1207 * main lifetime for the portal. */
6197db53 1208 r = sd_ndisc_router_get_lifetime_timestamp(rt, CLOCK_BOOTTIME, &lifetime_usec);
64de00c4
YW
1209 if (r < 0)
1210 return log_link_warning_errno(link, r, "Failed to get lifetime of RA message: %m");
1211
64de00c4
YW
1212 r = sd_ndisc_router_captive_portal_get_uri(rt, &uri, &len);
1213 if (r < 0)
1214 return log_link_warning_errno(link, r, "Failed to get captive portal from RA: %m");
1215
1216 if (len == 0)
6df82d12 1217 return log_link_warning_errno(link, SYNTHETIC_ERRNO(EBADMSG), "Received empty captive portal, ignoring.");
64de00c4
YW
1218
1219 r = make_cstring(uri, len, MAKE_CSTRING_REFUSE_TRAILING_NUL, &captive_portal);
1220 if (r < 0)
1221 return log_link_warning_errno(link, r, "Failed to convert captive portal URI: %m");
1222
1223 if (!in_charset(captive_portal, URI_VALID))
6df82d12 1224 return log_link_warning_errno(link, SYNTHETIC_ERRNO(EBADMSG), "Received invalid captive portal, ignoring.");
64de00c4 1225
218f3738
YW
1226 if (lifetime_usec == 0) {
1227 /* Drop the portal with zero lifetime. */
1228 ndisc_captive_portal_free(set_remove(link->ndisc_captive_portals,
1229 &(NDiscCaptivePortal) {
1230 .captive_portal = captive_portal,
1231 }));
1232 return 0;
1233 }
1234
1235 exist = set_get(link->ndisc_captive_portals,
1236 &(NDiscCaptivePortal) {
1237 .captive_portal = captive_portal,
1238 });
64de00c4
YW
1239 if (exist) {
1240 /* update existing entry */
1241 exist->router = router;
1242 exist->lifetime_usec = lifetime_usec;
6df82d12 1243 return 1;
64de00c4
YW
1244 }
1245
bf943a9d
YW
1246 if (set_size(link->ndisc_captive_portals) >= NDISC_CAPTIVE_PORTAL_MAX) {
1247 NDiscCaptivePortal *c, *target = NULL;
1248
1249 /* Find the portal who has the minimal lifetime and drop it to store new one. */
1250 SET_FOREACH(c, link->ndisc_captive_portals)
1251 if (!target || c->lifetime_usec < target->lifetime_usec)
1252 target = c;
1253
1254 assert(target);
1255 assert(set_remove(link->ndisc_captive_portals, target) == target);
1256 ndisc_captive_portal_free(target);
1257 }
1258
64de00c4
YW
1259 new_entry = new(NDiscCaptivePortal, 1);
1260 if (!new_entry)
1261 return log_oom();
1262
1263 *new_entry = (NDiscCaptivePortal) {
1264 .router = router,
1265 .lifetime_usec = lifetime_usec,
1266 .captive_portal = TAKE_PTR(captive_portal),
1267 };
1268
1269 r = set_ensure_put(&link->ndisc_captive_portals, &ndisc_captive_portal_hash_ops, new_entry);
1270 if (r < 0)
1271 return log_oom();
1272 assert(r > 0);
1273 TAKE_PTR(new_entry);
1274
1275 link_dirty(link);
6df82d12 1276 return 1;
64de00c4
YW
1277}
1278
6e8f5e4c
SS
1279static void ndisc_pref64_hash_func(const NDiscPREF64 *x, struct siphash *state) {
1280 assert(x);
1281
c01a5c05
YW
1282 siphash24_compress_typesafe(x->prefix_len, state);
1283 siphash24_compress_typesafe(x->prefix, state);
6e8f5e4c
SS
1284}
1285
1286static int ndisc_pref64_compare_func(const NDiscPREF64 *a, const NDiscPREF64 *b) {
1287 int r;
1288
1289 assert(a);
1290 assert(b);
1291
1292 r = CMP(a->prefix_len, b->prefix_len);
1293 if (r != 0)
1294 return r;
1295
1296 return memcmp(&a->prefix, &b->prefix, sizeof(a->prefix));
1297}
1298
1299DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(
1300 ndisc_pref64_hash_ops,
1301 NDiscPREF64,
1302 ndisc_pref64_hash_func,
1303 ndisc_pref64_compare_func,
1304 mfree);
1305
1306static int ndisc_router_process_pref64(Link *link, sd_ndisc_router *rt) {
1307 _cleanup_free_ NDiscPREF64 *new_entry = NULL;
6197db53 1308 usec_t lifetime_usec;
6e8f5e4c 1309 struct in6_addr a, router;
6e8f5e4c
SS
1310 unsigned prefix_len;
1311 NDiscPREF64 *exist;
1312 int r;
1313
1314 assert(link);
1315 assert(link->network);
1316 assert(rt);
1317
1318 if (!link->network->ipv6_accept_ra_use_pref64)
1319 return 0;
1320
1321 r = sd_ndisc_router_get_address(rt, &router);
1322 if (r < 0)
1323 return log_link_warning_errno(link, r, "Failed to get router address from RA: %m");
1324
1325 r = sd_ndisc_router_prefix64_get_prefix(rt, &a);
1326 if (r < 0)
1327 return log_link_warning_errno(link, r, "Failed to get pref64 prefix: %m");
1328
1329 r = sd_ndisc_router_prefix64_get_prefixlen(rt, &prefix_len);
1330 if (r < 0)
1331 return log_link_warning_errno(link, r, "Failed to get pref64 prefix length: %m");
1332
6197db53 1333 r = sd_ndisc_router_prefix64_get_lifetime_timestamp(rt, CLOCK_BOOTTIME, &lifetime_usec);
6e8f5e4c
SS
1334 if (r < 0)
1335 return log_link_warning_errno(link, r, "Failed to get pref64 prefix lifetime: %m");
1336
6e8f5e4c
SS
1337 if (lifetime_usec == 0) {
1338 free(set_remove(link->ndisc_pref64,
1339 &(NDiscPREF64) {
1340 .prefix = a,
1341 .prefix_len = prefix_len
1342 }));
1343 return 0;
1344 }
1345
1346 exist = set_get(link->ndisc_pref64,
1347 &(NDiscPREF64) {
1348 .prefix = a,
1349 .prefix_len = prefix_len
1350 });
1351 if (exist) {
1352 /* update existing entry */
1353 exist->router = router;
1354 exist->lifetime_usec = lifetime_usec;
1355 return 0;
1356 }
1357
4df16cd0
YW
1358 if (set_size(link->ndisc_pref64) >= NDISC_PREF64_MAX) {
1359 log_link_debug(link, "Too many PREF64 records received. Only first %u records will be used.", NDISC_PREF64_MAX);
1360 return 0;
1361 }
1362
6e8f5e4c
SS
1363 new_entry = new(NDiscPREF64, 1);
1364 if (!new_entry)
1365 return log_oom();
1366
1367 *new_entry = (NDiscPREF64) {
1368 .router = router,
1369 .lifetime_usec = lifetime_usec,
1370 .prefix = a,
1371 .prefix_len = prefix_len,
1372 };
1373
1374 r = set_ensure_put(&link->ndisc_pref64, &ndisc_pref64_hash_ops, new_entry);
1375 if (r < 0)
1376 return log_oom();
1377
1378 assert(r > 0);
1379 TAKE_PTR(new_entry);
1380
1381 return 0;
1382}
1383
e8c9b5b0 1384static int ndisc_router_process_options(Link *link, sd_ndisc_router *rt) {
6df82d12 1385 size_t n_captive_portal = 0;
fbdda4bb
YW
1386 int r;
1387
1e7a0e21 1388 assert(link);
55d3fdcf 1389 assert(link->network);
1e7a0e21
LP
1390 assert(rt);
1391
fbdda4bb 1392 for (r = sd_ndisc_router_option_rewind(rt); ; r = sd_ndisc_router_option_next(rt)) {
1e7a0e21
LP
1393 uint8_t type;
1394
e8c9b5b0 1395 if (r < 0)
2c5bca17 1396 return log_link_warning_errno(link, r, "Failed to iterate through options: %m");
1e7a0e21 1397 if (r == 0) /* EOF */
13e8a49a 1398 return 0;
1e7a0e21
LP
1399
1400 r = sd_ndisc_router_option_get_type(rt, &type);
e8c9b5b0 1401 if (r < 0)
2c5bca17 1402 return log_link_warning_errno(link, r, "Failed to get RA option type: %m");
1e7a0e21
LP
1403
1404 switch (type) {
fbdda4bb
YW
1405 case SD_NDISC_OPTION_PREFIX_INFORMATION:
1406 r = ndisc_router_process_prefix(link, rt);
1e7a0e21 1407 break;
1e7a0e21
LP
1408
1409 case SD_NDISC_OPTION_ROUTE_INFORMATION:
13e8a49a 1410 r = ndisc_router_process_route(link, rt);
1e7a0e21
LP
1411 break;
1412
1413 case SD_NDISC_OPTION_RDNSS:
ad0b2df6 1414 r = ndisc_router_process_rdnss(link, rt);
1e7a0e21
LP
1415 break;
1416
1417 case SD_NDISC_OPTION_DNSSL:
ad0b2df6 1418 r = ndisc_router_process_dnssl(link, rt);
1e7a0e21 1419 break;
9747955d 1420 case SD_NDISC_OPTION_CAPTIVE_PORTAL:
6df82d12
YW
1421 if (n_captive_portal > 0) {
1422 if (n_captive_portal == 1)
1423 log_link_notice(link, "Received RA with multiple captive portals, only using the first one.");
1424
1425 n_captive_portal++;
1426 continue;
1427 }
9747955d 1428 r = ndisc_router_process_captive_portal(link, rt);
6df82d12
YW
1429 if (r > 0)
1430 n_captive_portal++;
9747955d 1431 break;
6e8f5e4c
SS
1432 case SD_NDISC_OPTION_PREF64:
1433 r = ndisc_router_process_pref64(link, rt);
1434 break;
1e7a0e21 1435 }
5908d864
YW
1436 if (r < 0 && r != -EBADMSG)
1437 return r;
1e7a0e21
LP
1438 }
1439}
1440
2b4fca55 1441static int ndisc_drop_outdated(Link *link, usec_t timestamp_usec) {
94e6d37c
YW
1442 bool updated = false;
1443 NDiscDNSSL *dnssl;
1444 NDiscRDNSS *rdnss;
64de00c4 1445 NDiscCaptivePortal *cp;
fabea9c0 1446 NDiscPREF64 *p64;
94e6d37c
YW
1447 Address *address;
1448 Route *route;
372acaad 1449 int r, ret = 0;
94e6d37c
YW
1450
1451 assert(link);
8d01e44c 1452 assert(link->manager);
94e6d37c
YW
1453
1454 /* If an address or friends is already assigned, but not valid anymore, then refuse to update it,
1455 * and let's immediately remove it.
1456 * See RFC4862, section 5.5.3.e. But the following logic is deviated from RFC4862 by honoring all
1457 * valid lifetimes to improve the reaction of SLAAC to renumbering events.
1458 * See draft-ietf-6man-slaac-renum-02, section 4.2. */
1459
8d01e44c 1460 SET_FOREACH(route, link->manager->routes) {
94e6d37c
YW
1461 if (route->source != NETWORK_CONFIG_SOURCE_NDISC)
1462 continue;
1463
8d01e44c
YW
1464 if (route->nexthop.ifindex != link->ifindex)
1465 continue;
1466
94e6d37c
YW
1467 if (route->lifetime_usec >= timestamp_usec)
1468 continue; /* the route is still valid */
1469
3caed9ea 1470 r = route_remove_and_cancel(route, link->manager);
372acaad
YW
1471 if (r < 0)
1472 RET_GATHER(ret, log_link_warning_errno(link, r, "Failed to remove outdated SLAAC route, ignoring: %m"));
94e6d37c
YW
1473 }
1474
1475 SET_FOREACH(address, link->addresses) {
1476 if (address->source != NETWORK_CONFIG_SOURCE_NDISC)
1477 continue;
1478
1479 if (address->lifetime_valid_usec >= timestamp_usec)
1480 continue; /* the address is still valid */
1481
f22b586a 1482 r = address_remove_and_cancel(address, link);
372acaad
YW
1483 if (r < 0)
1484 RET_GATHER(ret, log_link_warning_errno(link, r, "Failed to remove outdated SLAAC address, ignoring: %m"));
94e6d37c
YW
1485 }
1486
1487 SET_FOREACH(rdnss, link->ndisc_rdnss) {
1488 if (rdnss->lifetime_usec >= timestamp_usec)
1489 continue; /* the DNS server is still valid */
1490
1491 free(set_remove(link->ndisc_rdnss, rdnss));
1492 updated = true;
1493 }
1494
1495 SET_FOREACH(dnssl, link->ndisc_dnssl) {
1496 if (dnssl->lifetime_usec >= timestamp_usec)
1497 continue; /* the DNS domain is still valid */
1498
1499 free(set_remove(link->ndisc_dnssl, dnssl));
1500 updated = true;
1501 }
1502
64de00c4
YW
1503 SET_FOREACH(cp, link->ndisc_captive_portals) {
1504 if (cp->lifetime_usec >= timestamp_usec)
1505 continue; /* the captive portal is still valid */
1506
75a91226 1507 ndisc_captive_portal_free(set_remove(link->ndisc_captive_portals, cp));
64de00c4
YW
1508 updated = true;
1509 }
1510
fabea9c0
YW
1511 SET_FOREACH(p64, link->ndisc_pref64) {
1512 if (p64->lifetime_usec >= timestamp_usec)
1513 continue; /* the pref64 prefix is still valid */
1514
1515 free(set_remove(link->ndisc_pref64, p64));
1516 /* The pref64 prefix is not exported through the state file, hence it is not necessary to set
1517 * the 'updated' flag. */
1518 }
1519
94e6d37c
YW
1520 if (updated)
1521 link_dirty(link);
1522
372acaad 1523 return ret;
94e6d37c
YW
1524}
1525
77302468
YW
1526static int ndisc_setup_expire(Link *link);
1527
1528static int ndisc_expire_handler(sd_event_source *s, uint64_t usec, void *userdata) {
1529 Link *link = ASSERT_PTR(userdata);
1530 usec_t now_usec;
1531
1532 assert(link->manager);
1533
1534 assert_se(sd_event_now(link->manager->event, CLOCK_BOOTTIME, &now_usec) >= 0);
1535
2b4fca55 1536 (void) ndisc_drop_outdated(link, now_usec);
77302468
YW
1537 (void) ndisc_setup_expire(link);
1538 return 0;
1539}
1540
1541static int ndisc_setup_expire(Link *link) {
1542 usec_t lifetime_usec = USEC_INFINITY;
64de00c4 1543 NDiscCaptivePortal *cp;
77302468
YW
1544 NDiscDNSSL *dnssl;
1545 NDiscRDNSS *rdnss;
fabea9c0 1546 NDiscPREF64 *p64;
77302468
YW
1547 Address *address;
1548 Route *route;
1549 int r;
1550
1551 assert(link);
1552 assert(link->manager);
1553
8d01e44c 1554 SET_FOREACH(route, link->manager->routes) {
77302468
YW
1555 if (route->source != NETWORK_CONFIG_SOURCE_NDISC)
1556 continue;
1557
8d01e44c
YW
1558 if (route->nexthop.ifindex != link->ifindex)
1559 continue;
1560
77302468
YW
1561 if (!route_exists(route))
1562 continue;
1563
1564 lifetime_usec = MIN(lifetime_usec, route->lifetime_usec);
1565 }
1566
1567 SET_FOREACH(address, link->addresses) {
1568 if (address->source != NETWORK_CONFIG_SOURCE_NDISC)
1569 continue;
1570
1571 if (!address_exists(address))
1572 continue;
1573
1574 lifetime_usec = MIN(lifetime_usec, address->lifetime_valid_usec);
1575 }
1576
1577 SET_FOREACH(rdnss, link->ndisc_rdnss)
1578 lifetime_usec = MIN(lifetime_usec, rdnss->lifetime_usec);
1579
1580 SET_FOREACH(dnssl, link->ndisc_dnssl)
1581 lifetime_usec = MIN(lifetime_usec, dnssl->lifetime_usec);
1582
64de00c4
YW
1583 SET_FOREACH(cp, link->ndisc_captive_portals)
1584 lifetime_usec = MIN(lifetime_usec, cp->lifetime_usec);
1585
fabea9c0
YW
1586 SET_FOREACH(p64, link->ndisc_pref64)
1587 lifetime_usec = MIN(lifetime_usec, p64->lifetime_usec);
1588
77302468
YW
1589 if (lifetime_usec == USEC_INFINITY)
1590 return 0;
1591
1592 r = event_reset_time(link->manager->event, &link->ndisc_expire, CLOCK_BOOTTIME,
1593 lifetime_usec, 0, ndisc_expire_handler, link, 0, "ndisc-expiration", true);
1594 if (r < 0)
1595 return log_link_warning_errno(link, r, "Failed to update expiration timer for ndisc: %m");
1596
1597 return 0;
1598}
1599
928112a4
YW
1600static int ndisc_start_dhcp6_client(Link *link, sd_ndisc_router *rt) {
1601 int r;
1602
1603 assert(link);
1604 assert(link->network);
1605
ffef01ac
YW
1606 /* Do not start DHCPv6 client if the router lifetime is zero, as the message sent as a signal of
1607 * that the router is e.g. shutting down, revoked, etc,. */
1608 r = sd_ndisc_router_get_lifetime(rt, NULL);
1609 if (r <= 0)
1610 return r;
1611
928112a4
YW
1612 switch (link->network->ipv6_accept_ra_start_dhcp6_client) {
1613 case IPV6_ACCEPT_RA_START_DHCP6_CLIENT_NO:
1614 return 0;
1615
1616 case IPV6_ACCEPT_RA_START_DHCP6_CLIENT_YES: {
1617 uint64_t flags;
1618
1619 r = sd_ndisc_router_get_flags(rt, &flags);
1620 if (r < 0)
1621 return log_link_warning_errno(link, r, "Failed to get RA flags: %m");
1622
1623 if ((flags & (ND_RA_FLAG_MANAGED | ND_RA_FLAG_OTHER)) == 0)
1624 return 0;
1625
1626 /* (re)start DHCPv6 client in stateful or stateless mode according to RA flags.
0bcc6557
AH
1627 * Note, if both "managed" and "other configuration" bits are set, then ignore
1628 * "other configuration" bit. See RFC 4861. */
fac19a21 1629 r = dhcp6_start_on_ra(link, !(flags & ND_RA_FLAG_MANAGED));
928112a4
YW
1630 break;
1631 }
1632 case IPV6_ACCEPT_RA_START_DHCP6_CLIENT_ALWAYS:
0bcc6557 1633 /* When IPv6AcceptRA.DHCPv6Client=always, start dhcp6 client in solicit mode
928112a4 1634 * even if the router flags have neither M nor O flags. */
fac19a21 1635 r = dhcp6_start_on_ra(link, /* information_request = */ false);
928112a4
YW
1636 break;
1637
1638 default:
1639 assert_not_reached();
1640 }
1641
1642 if (r < 0)
2c5bca17 1643 return log_link_warning_errno(link, r, "Could not acquire DHCPv6 lease on NDisc request: %m");
928112a4
YW
1644
1645 log_link_debug(link, "Acquiring DHCPv6 lease on NDisc request");
1646 return 0;
1647}
1648
d5017c84 1649static int ndisc_router_handler(Link *link, sd_ndisc_router *rt) {
b8ce3b44 1650 struct in6_addr router;
94e6d37c 1651 usec_t timestamp_usec;
86e2be7b 1652 int r;
1e7a0e21
LP
1653
1654 assert(link);
1655 assert(link->network);
1656 assert(link->manager);
1657 assert(rt);
1658
b8ce3b44 1659 r = sd_ndisc_router_get_address(rt, &router);
5908d864
YW
1660 if (r == -ENODATA) {
1661 log_link_debug(link, "Received RA without router address, ignoring.");
1662 return 0;
1663 }
75d26411 1664 if (r < 0)
2c5bca17 1665 return log_link_warning_errno(link, r, "Failed to get router address from RA: %m");
75d26411 1666
c995fa02 1667 if (in6_prefix_is_filtered(&router, 128, link->network->ndisc_allow_listed_router, link->network->ndisc_deny_listed_router)) {
75d26411 1668 if (DEBUG_LOGGING) {
75d26411 1669 if (!set_isempty(link->network->ndisc_allow_listed_router))
84dbb3fd 1670 log_link_debug(link, "Router %s is not in allow list, ignoring.", IN6_ADDR_TO_STRING(&router));
75d26411 1671 else
84dbb3fd 1672 log_link_debug(link, "Router %s is in deny list, ignoring.", IN6_ADDR_TO_STRING(&router));
75d26411
YW
1673 }
1674 return 0;
1675 }
1676
94e6d37c 1677 r = sd_ndisc_router_get_timestamp(rt, CLOCK_BOOTTIME, &timestamp_usec);
5908d864
YW
1678 if (r == -ENODATA) {
1679 log_link_debug(link, "Received RA without timestamp, ignoring.");
1680 return 0;
1681 }
94e6d37c
YW
1682 if (r < 0)
1683 return r;
1684
2b4fca55 1685 r = ndisc_drop_outdated(link, timestamp_usec);
94e6d37c 1686 if (r < 0)
2b4fca55 1687 return r;
94e6d37c 1688
928112a4
YW
1689 r = ndisc_start_dhcp6_client(link, rt);
1690 if (r < 0)
1691 return r;
1e7a0e21 1692
13e8a49a
YW
1693 r = ndisc_router_process_default(link, rt);
1694 if (r < 0)
1695 return r;
fbdda4bb 1696
0f9a2b80
YW
1697 r = ndisc_router_process_icmp6_ratelimit(link, rt);
1698 if (r < 0)
1699 return r;
1700
1452d497
YW
1701 r = ndisc_router_process_reachable_time(link, rt);
1702 if (r < 0)
1703 return r;
1704
d4c8de21
MM
1705 r = ndisc_router_process_retransmission_time(link, rt);
1706 if (r < 0)
1707 return r;
1708
b15ed2be
MM
1709 r = ndisc_router_process_hop_limit(link, rt);
1710 if (r < 0)
1711 return r;
1712
13e8a49a
YW
1713 r = ndisc_router_process_options(link, rt);
1714 if (r < 0)
1715 return r;
d5017c84 1716
77302468
YW
1717 r = ndisc_setup_expire(link);
1718 if (r < 0)
1719 return r;
1720
2ccada8d 1721 if (link->ndisc_messages == 0)
3b6a3bde 1722 link->ndisc_configured = true;
2ccada8d 1723 else
3b6a3bde 1724 log_link_debug(link, "Setting SLAAC addresses and router.");
69203fba 1725
3b6a3bde 1726 if (!link->ndisc_configured)
69203fba
YW
1727 link_set_state(link, LINK_STATE_CONFIGURING);
1728
76c5a0f2 1729 link_check_ready(link);
69203fba 1730 return 0;
1e7a0e21
LP
1731}
1732
2324fd3a 1733static void ndisc_handler(sd_ndisc *nd, sd_ndisc_event_t event, sd_ndisc_router *rt, void *userdata) {
99534007 1734 Link *link = ASSERT_PTR(userdata);
13e8a49a 1735 int r;
a13c50e7 1736
9d96e6c3
TG
1737 if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
1738 return;
a13c50e7 1739
9d96e6c3 1740 switch (event) {
1e7a0e21
LP
1741
1742 case SD_NDISC_EVENT_ROUTER:
13e8a49a 1743 r = ndisc_router_handler(link, rt);
5908d864 1744 if (r < 0 && r != -EBADMSG) {
13e8a49a
YW
1745 link_enter_failed(link);
1746 return;
1747 }
1e7a0e21
LP
1748 break;
1749
9d96e6c3 1750 case SD_NDISC_EVENT_TIMEOUT:
a8c10331 1751 log_link_debug(link, "NDisc handler get timeout event");
3b6a3bde
YW
1752 if (link->ndisc_messages == 0) {
1753 link->ndisc_configured = true;
3336e946
YW
1754 link_check_ready(link);
1755 }
9d96e6c3
TG
1756 break;
1757 default:
04499a70 1758 assert_not_reached();
a13c50e7
TG
1759 }
1760}
1761
ba4c7184 1762static int ndisc_configure(Link *link) {
a13c50e7
TG
1763 int r;
1764
1e7a0e21
LP
1765 assert(link);
1766
2ffd6d73
YW
1767 if (!link_ipv6_accept_ra_enabled(link))
1768 return 0;
a13c50e7 1769
5c078687 1770 if (link->ndisc)
bc9e40c9 1771 return -EBUSY; /* Already configured. */
2ffd6d73 1772
5c078687
YW
1773 r = sd_ndisc_new(&link->ndisc);
1774 if (r < 0)
1775 return r;
1776
1777 r = sd_ndisc_attach_event(link->ndisc, link->manager->event, 0);
1778 if (r < 0)
1779 return r;
a13c50e7 1780
3be64aa4
YW
1781 if (link->hw_addr.length == ETH_ALEN) {
1782 r = sd_ndisc_set_mac(link->ndisc, &link->hw_addr.ether);
1783 if (r < 0)
1784 return r;
1785 }
a13c50e7 1786
1e7a0e21 1787 r = sd_ndisc_set_ifindex(link->ndisc, link->ifindex);
a13c50e7
TG
1788 if (r < 0)
1789 return r;
1790
1e7a0e21 1791 r = sd_ndisc_set_callback(link->ndisc, ndisc_handler, link);
a13c50e7
TG
1792 if (r < 0)
1793 return r;
1794
1e7a0e21
LP
1795 return 0;
1796}
1797
294f129b 1798int ndisc_start(Link *link) {
ba4c7184
YW
1799 int r;
1800
294f129b
YW
1801 assert(link);
1802
1803 if (!link->ndisc || !link->dhcp6_client)
1804 return 0;
1805
ccffa166
YW
1806 if (!link_has_carrier(link))
1807 return 0;
1808
3b6a3bde
YW
1809 if (in6_addr_is_null(&link->ipv6ll_address))
1810 return 0;
1811
294f129b
YW
1812 log_link_debug(link, "Discovering IPv6 routers");
1813
ba4c7184
YW
1814 r = sd_ndisc_start(link->ndisc);
1815 if (r < 0)
1816 return r;
1817
1818 return 1;
1819}
1820
09d09207 1821static int ndisc_process_request(Request *req, Link *link, void *userdata) {
ba4c7184
YW
1822 int r;
1823
ff51134c 1824 assert(link);
ba4c7184 1825
4b482e8b 1826 if (!link_is_ready_to_configure(link, /* allow_unmanaged = */ false))
ba4c7184
YW
1827 return 0;
1828
ba4c7184
YW
1829 r = ndisc_configure(link);
1830 if (r < 0)
1831 return log_link_warning_errno(link, r, "Failed to configure IPv6 Router Discovery: %m");
1832
1833 r = ndisc_start(link);
1834 if (r < 0)
1835 return log_link_warning_errno(link, r, "Failed to start IPv6 Router Discovery: %m");
1836
1837 log_link_debug(link, "IPv6 Router Discovery is configured%s.",
1838 r > 0 ? " and started" : "");
ba4c7184
YW
1839 return 1;
1840}
1841
1842int link_request_ndisc(Link *link) {
1843 int r;
1844
1845 assert(link);
1846
1847 if (!link_ipv6_accept_ra_enabled(link))
1848 return 0;
1849
1850 if (link->ndisc)
1851 return 0;
1852
09d09207 1853 r = link_queue_request(link, REQUEST_TYPE_NDISC, ndisc_process_request, NULL);
ba4c7184
YW
1854 if (r < 0)
1855 return log_link_warning_errno(link, r, "Failed to request configuring of the IPv6 Router Discovery: %m");
1856
1857 log_link_debug(link, "Requested configuring of the IPv6 Router Discovery.");
1858 return 0;
294f129b
YW
1859}
1860
77302468
YW
1861int ndisc_stop(Link *link) {
1862 assert(link);
1863
1864 link->ndisc_expire = sd_event_source_disable_unref(link->ndisc_expire);
1865
1866 return sd_ndisc_stop(link->ndisc);
1867}
1868
1869
c69305ff
LP
1870void ndisc_flush(Link *link) {
1871 assert(link);
1872
a86763c7
YW
1873 /* Remove all addresses, routes, RDNSS, DNSSL, and Captive Portal entries, without exception. */
1874 (void) ndisc_drop_outdated(link, /* timestamp_usec = */ USEC_INFINITY);
c69305ff 1875
b0b97766
YW
1876 link->ndisc_rdnss = set_free(link->ndisc_rdnss);
1877 link->ndisc_dnssl = set_free(link->ndisc_dnssl);
64de00c4 1878 link->ndisc_captive_portals = set_free(link->ndisc_captive_portals);
6e8f5e4c 1879 link->ndisc_pref64 = set_free(link->ndisc_pref64);
c69305ff 1880}
e520ce64 1881
ac24e418
SS
1882static const char* const ipv6_accept_ra_start_dhcp6_client_table[_IPV6_ACCEPT_RA_START_DHCP6_CLIENT_MAX] = {
1883 [IPV6_ACCEPT_RA_START_DHCP6_CLIENT_NO] = "no",
1884 [IPV6_ACCEPT_RA_START_DHCP6_CLIENT_ALWAYS] = "always",
1885 [IPV6_ACCEPT_RA_START_DHCP6_CLIENT_YES] = "yes",
1886};
1887
3b6a3bde
YW
1888DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING_WITH_BOOLEAN(ipv6_accept_ra_start_dhcp6_client, IPv6AcceptRAStartDHCP6Client, IPV6_ACCEPT_RA_START_DHCP6_CLIENT_YES);
1889
1890DEFINE_CONFIG_PARSE_ENUM(config_parse_ipv6_accept_ra_use_domains, dhcp_use_domains, DHCPUseDomains,
1891 "Failed to parse UseDomains= setting");
1892DEFINE_CONFIG_PARSE_ENUM(config_parse_ipv6_accept_ra_start_dhcp6_client, ipv6_accept_ra_start_dhcp6_client, IPv6AcceptRAStartDHCP6Client,
1893 "Failed to parse DHCPv6Client= setting");