1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
3 Copyright © 2014 Intel Corporation. All rights reserved.
7 #include <netinet/icmp6.h>
9 #include <linux/if_arp.h>
13 #include "event-util.h"
14 #include "missing_network.h"
15 #include "networkd-address-generation.h"
16 #include "networkd-address.h"
17 #include "networkd-dhcp6.h"
18 #include "networkd-manager.h"
19 #include "networkd-ndisc.h"
20 #include "networkd-queue.h"
21 #include "networkd-route.h"
22 #include "networkd-state-file.h"
23 #include "networkd-sysctl.h"
24 #include "string-table.h"
25 #include "string-util.h"
27 #include "sysctl-util.h"
29 #define NDISC_DNSSL_MAX 64U
30 #define NDISC_RDNSS_MAX 64U
31 /* Not defined in the RFC, but let's set an upper limit to make not consume much memory.
32 * This should be safe as typically there should be at most 1 portal per network. */
33 #define NDISC_CAPTIVE_PORTAL_MAX 64U
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
38 bool link_ndisc_enabled(Link
*link
) {
41 if (!socket_ipv6_is_supported())
44 if (link
->flags
& IFF_LOOPBACK
)
47 if (link
->iftype
== ARPHRD_CAN
)
53 if (!link_may_have_ipv6ll(link
, /* check_multicast = */ true))
56 if (link
->network
->ndisc
>= 0)
57 return link
->network
->ndisc
;
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
);
64 /* Otherwise, defaults to true. */
68 void network_adjust_ndisc(Network
*network
) {
71 if (!FLAGS_SET(network
->link_local
, ADDRESS_FAMILY_IPV6
)) {
72 if (network
->ndisc
> 0)
73 log_warning("%s: IPv6AcceptRA= is enabled but IPv6 link-local addressing is disabled or not supported. "
74 "Disabling IPv6AcceptRA=.", network
->filename
);
75 network
->ndisc
= false;
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
);
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
);
88 static int ndisc_check_ready(Link
*link
);
90 static int ndisc_address_ready_callback(Address
*address
) {
94 assert(address
->link
);
96 SET_FOREACH(a
, address
->link
->addresses
)
97 if (a
->source
== NETWORK_CONFIG_SOURCE_NDISC
)
100 return ndisc_check_ready(address
->link
);
103 static int ndisc_check_ready(Link
*link
) {
104 bool found
= false, ready
= false;
109 if (link
->ndisc_messages
> 0) {
110 log_link_debug(link
, "%s(): SLAAC addresses and routes are not set.", __func__
);
114 SET_FOREACH(address
, link
->addresses
) {
115 if (address
->source
!= NETWORK_CONFIG_SOURCE_NDISC
)
120 if (address_is_ready(address
)) {
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
;
131 log_link_debug(link
, "%s(): no SLAAC address is ready.", __func__
);
135 link
->ndisc_configured
= true;
136 log_link_debug(link
, "SLAAC addresses and routes set.");
138 link_check_ready(link
);
142 static int ndisc_route_handler(sd_netlink
*rtnl
, sd_netlink_message
*m
, Request
*req
, Link
*link
, Route
*route
) {
147 r
= route_configure_handler_internal(rtnl
, m
, link
, route
, "Could not set NDisc route");
151 r
= ndisc_check_ready(link
);
153 link_enter_failed(link
);
158 static void ndisc_set_route_priority(Link
*link
, Route
*route
) {
162 if (route
->priority_set
)
163 return; /* explicitly configured. */
165 switch (route
->pref
) {
166 case SD_NDISC_PREFERENCE_LOW
:
167 route
->priority
= link
->network
->ndisc_route_metric_low
;
169 case SD_NDISC_PREFERENCE_MEDIUM
:
170 route
->priority
= link
->network
->ndisc_route_metric_medium
;
172 case SD_NDISC_PREFERENCE_HIGH
:
173 route
->priority
= link
->network
->ndisc_route_metric_high
;
176 assert_not_reached();
180 static int ndisc_request_route(Route
*route
, Link
*link
, sd_ndisc_router
*rt
) {
181 struct in6_addr router
;
182 uint8_t hop_limit
= 0;
189 assert(link
->manager
);
190 assert(link
->network
);
193 r
= sd_ndisc_router_get_sender_address(rt
, &router
);
197 if (link
->network
->ndisc_use_mtu
) {
198 r
= sd_ndisc_router_get_mtu(rt
, &mtu
);
199 if (r
< 0 && r
!= -ENODATA
)
200 return log_link_warning_errno(link
, r
, "Failed to get MTU from RA: %m");
203 if (link
->network
->ndisc_use_hop_limit
) {
204 r
= sd_ndisc_router_get_hop_limit(rt
, &hop_limit
);
205 if (r
< 0 && r
!= -ENODATA
)
206 return log_link_warning_errno(link
, r
, "Failed to get hop limit from RA: %m");
209 route
->source
= NETWORK_CONFIG_SOURCE_NDISC
;
210 route
->provider
.in6
= router
;
211 if (!route
->table_set
)
212 route
->table
= link_get_ndisc_route_table(link
);
213 if (!route
->protocol_set
)
214 route
->protocol
= RTPROT_RA
;
215 r
= route_metric_set(&route
->metric
, RTAX_MTU
, mtu
);
218 r
= route_metric_set(&route
->metric
, RTAX_HOPLIMIT
, hop_limit
);
221 r
= route_metric_set(&route
->metric
, RTAX_QUICKACK
, link
->network
->ndisc_quickack
);
225 r
= route_adjust_nexthops(route
, link
);
229 uint8_t pref
, pref_original
= route
->pref
;
230 FOREACH_ARGUMENT(pref
, SD_NDISC_PREFERENCE_LOW
, SD_NDISC_PREFERENCE_MEDIUM
, SD_NDISC_PREFERENCE_HIGH
) {
234 /* If the preference is specified by the user config (that is, for semi-static routes),
235 * rather than RA, then only search conflicting routes that have the same preference. */
236 if (route
->pref_set
&& pref
!= pref_original
)
240 ndisc_set_route_priority(link
, route
);
242 /* Note, here do not call route_remove_and_cancel() with 'route' directly, otherwise
243 * existing route(s) may be removed needlessly. */
245 if (route_get(link
->manager
, route
, &existing
) >= 0) {
246 /* Found an existing route that may conflict with this route. */
247 if (!route_can_update(existing
, route
)) {
248 log_link_debug(link
, "Found an existing route that conflicts with new route based on a received RA, removing.");
249 r
= route_remove_and_cancel(existing
, link
->manager
);
255 if (route_get_request(link
->manager
, route
, &req
) >= 0) {
256 existing
= ASSERT_PTR(req
->userdata
);
257 if (!route_can_update(existing
, route
)) {
258 log_link_debug(link
, "Found a pending route request that conflicts with new request based on a received RA, cancelling.");
259 r
= route_remove_and_cancel(existing
, link
->manager
);
266 /* The preference (and priority) may be changed in the above loop. Restore it. */
267 route
->pref
= pref_original
;
268 ndisc_set_route_priority(link
, route
);
270 is_new
= route_get(link
->manager
, route
, NULL
) < 0;
272 r
= link_request_route(link
, route
, &link
->ndisc_messages
, ndisc_route_handler
);
276 link
->ndisc_configured
= false;
281 static int ndisc_remove_route(Route
*route
, Link
*link
) {
286 assert(link
->manager
);
288 ndisc_set_route_priority(link
, route
);
290 if (!route
->table_set
)
291 route
->table
= link_get_ndisc_route_table(link
);
293 r
= route_adjust_nexthops(route
, link
);
297 if (route
->pref_set
) {
298 ndisc_set_route_priority(link
, route
);
299 return route_remove_and_cancel(route
, link
->manager
);
303 FOREACH_ARGUMENT(pref
, SD_NDISC_PREFERENCE_LOW
, SD_NDISC_PREFERENCE_MEDIUM
, SD_NDISC_PREFERENCE_HIGH
) {
305 ndisc_set_route_priority(link
, route
);
306 r
= route_remove_and_cancel(route
, link
->manager
);
314 static int ndisc_address_handler(sd_netlink
*rtnl
, sd_netlink_message
*m
, Request
*req
, Link
*link
, Address
*address
) {
319 r
= address_configure_handler_internal(rtnl
, m
, link
, "Could not set NDisc address");
323 r
= ndisc_check_ready(link
);
325 link_enter_failed(link
);
330 static int ndisc_request_address(Address
*address
, Link
*link
, sd_ndisc_router
*rt
) {
331 struct in6_addr router
;
339 r
= sd_ndisc_router_get_sender_address(rt
, &router
);
343 address
->source
= NETWORK_CONFIG_SOURCE_NDISC
;
344 address
->provider
.in6
= router
;
346 r
= free_and_strdup_warn(&address
->netlabel
, link
->network
->ndisc_netlabel
);
351 if (address_get_harder(link
, address
, &existing
) < 0)
353 else if (address_can_update(existing
, address
))
355 else if (existing
->source
== NETWORK_CONFIG_SOURCE_DHCP6
) {
356 /* SLAAC address is preferred over DHCPv6 address. */
357 log_link_debug(link
, "Conflicting DHCPv6 address %s exists, removing.",
358 IN_ADDR_PREFIX_TO_STRING(existing
->family
, &existing
->in_addr
, existing
->prefixlen
));
359 r
= address_remove(existing
, link
);
365 /* Conflicting static address is configured?? */
366 log_link_debug(link
, "Conflicting address %s exists, ignoring request.",
367 IN_ADDR_PREFIX_TO_STRING(existing
->family
, &existing
->in_addr
, existing
->prefixlen
));
371 r
= link_request_address(link
, address
, &link
->ndisc_messages
,
372 ndisc_address_handler
, NULL
);
376 link
->ndisc_configured
= false;
381 static int ndisc_redirect_route_new(sd_ndisc_redirect
*rd
, Route
**ret
) {
382 _cleanup_(route_unrefp
) Route
*route
= NULL
;
383 struct in6_addr gateway
, destination
;
389 r
= sd_ndisc_redirect_get_target_address(rd
, &gateway
);
393 r
= sd_ndisc_redirect_get_destination_address(rd
, &destination
);
397 r
= route_new(&route
);
401 route
->family
= AF_INET6
;
402 if (!in6_addr_equal(&gateway
, &destination
)) {
403 route
->nexthop
.gw
.in6
= gateway
;
404 route
->nexthop
.family
= AF_INET6
;
406 route
->dst
.in6
= destination
;
407 route
->dst_prefixlen
= 128;
409 *ret
= TAKE_PTR(route
);
413 static int ndisc_request_redirect_route(Link
*link
, sd_ndisc_redirect
*rd
) {
414 struct in6_addr router
, sender
;
418 assert(link
->ndisc_default_router
);
421 r
= sd_ndisc_router_get_sender_address(link
->ndisc_default_router
, &router
);
425 r
= sd_ndisc_redirect_get_sender_address(rd
, &sender
);
429 if (!in6_addr_equal(&sender
, &router
))
432 _cleanup_(route_unrefp
) Route
*route
= NULL
;
433 r
= ndisc_redirect_route_new(rd
, &route
);
437 route
->protocol
= RTPROT_REDIRECT
;
438 route
->protocol_set
= true; /* To make ndisc_request_route() not override the protocol. */
440 /* Redirect message does not have the lifetime, let's use the lifetime of the default router, and
441 * update the lifetime of the redirect route every time when we receive RA. */
442 return ndisc_request_route(route
, link
, link
->ndisc_default_router
);
445 static int ndisc_remove_redirect_route(Link
*link
, sd_ndisc_redirect
*rd
) {
446 _cleanup_(route_unrefp
) Route
*route
= NULL
;
452 r
= ndisc_redirect_route_new(rd
, &route
);
456 return ndisc_remove_route(route
, link
);
459 static void ndisc_redirect_hash_func(const sd_ndisc_redirect
*x
, struct siphash
*state
) {
460 struct in6_addr dest
= {};
465 (void) sd_ndisc_redirect_get_destination_address((sd_ndisc_redirect
*) x
, &dest
);
467 siphash24_compress_typesafe(dest
, state
);
470 static int ndisc_redirect_compare_func(const sd_ndisc_redirect
*x
, const sd_ndisc_redirect
*y
) {
471 struct in6_addr dest_x
= {}, dest_y
= {};
476 (void) sd_ndisc_redirect_get_destination_address((sd_ndisc_redirect
*) x
, &dest_x
);
477 (void) sd_ndisc_redirect_get_destination_address((sd_ndisc_redirect
*) y
, &dest_y
);
479 return memcmp(&dest_x
, &dest_y
, sizeof(dest_x
));
482 DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(
483 ndisc_redirect_hash_ops
,
485 ndisc_redirect_hash_func
,
486 ndisc_redirect_compare_func
,
487 sd_ndisc_redirect_unref
);
489 static int ndisc_redirect_equal(sd_ndisc_redirect
*x
, sd_ndisc_redirect
*y
) {
490 struct in6_addr a
, b
;
496 r
= sd_ndisc_redirect_get_destination_address(x
, &a
);
500 r
= sd_ndisc_redirect_get_destination_address(y
, &b
);
504 if (!in6_addr_equal(&a
, &b
))
507 r
= sd_ndisc_redirect_get_target_address(x
, &a
);
511 r
= sd_ndisc_redirect_get_target_address(y
, &b
);
515 return in6_addr_equal(&a
, &b
);
518 static int ndisc_redirect_drop_conflict(Link
*link
, sd_ndisc_redirect
*rd
) {
519 _cleanup_(sd_ndisc_redirect_unrefp
) sd_ndisc_redirect
*existing
= NULL
;
525 existing
= set_remove(link
->ndisc_redirects
, rd
);
529 r
= ndisc_redirect_equal(rd
, existing
);
533 return ndisc_remove_redirect_route(link
, existing
);
536 static int ndisc_redirect_handler(Link
*link
, sd_ndisc_redirect
*rd
) {
537 struct in6_addr router
, sender
;
538 usec_t lifetime_usec
, now_usec
;
542 assert(link
->network
);
545 if (!link
->network
->ndisc_use_redirect
)
548 /* Ignore all Redirect messages from non-default router. */
550 if (!link
->ndisc_default_router
)
553 r
= sd_ndisc_router_get_lifetime_timestamp(link
->ndisc_default_router
, CLOCK_BOOTTIME
, &lifetime_usec
);
557 r
= sd_event_now(link
->manager
->event
, CLOCK_BOOTTIME
, &now_usec
);
561 if (lifetime_usec
<= now_usec
)
562 return 0; /* The default router is outdated. Ignore the redirect message. */
564 r
= sd_ndisc_router_get_sender_address(link
->ndisc_default_router
, &router
);
568 r
= sd_ndisc_redirect_get_sender_address(rd
, &sender
);
572 if (!in6_addr_equal(&sender
, &router
))
573 return 0; /* The redirect message is sent from a non-default router. */
575 /* OK, the Redirect message is sent from the current default router. */
577 r
= ndisc_redirect_drop_conflict(link
, rd
);
581 r
= set_ensure_put(&link
->ndisc_redirects
, &ndisc_redirect_hash_ops
, rd
);
585 sd_ndisc_redirect_ref(rd
);
587 return ndisc_request_redirect_route(link
, rd
);
590 static int ndisc_router_update_redirect(Link
*link
) {
595 /* Reconfigure redirect routes to update their lifetime. */
597 sd_ndisc_redirect
*rd
;
598 SET_FOREACH(rd
, link
->ndisc_redirects
) {
599 r
= ndisc_request_redirect_route(link
, rd
);
601 RET_GATHER(ret
, log_link_warning_errno(link
, r
, "Failed to update lifetime of the Redirect route: %m"));
607 static int ndisc_drop_redirect(Link
*link
, const struct in6_addr
*router
, bool remove
) {
612 /* If the router is purged, then drop the redirect routes configured with the Redirect message sent
618 sd_ndisc_redirect
*rd
;
619 SET_FOREACH(rd
, link
->ndisc_redirects
) {
620 struct in6_addr sender
;
622 r
= sd_ndisc_redirect_get_sender_address(rd
, &sender
);
626 if (!in6_addr_equal(&sender
, router
))
630 r
= ndisc_remove_redirect_route(link
, rd
);
635 sd_ndisc_redirect_unref(set_remove(link
->ndisc_redirects
, rd
));
641 static int ndisc_update_redirect_sender(Link
*link
, const struct in6_addr
*original_address
, const struct in6_addr
*current_address
) {
645 assert(original_address
);
646 assert(current_address
);
648 sd_ndisc_redirect
*rd
;
649 SET_FOREACH(rd
, link
->ndisc_redirects
) {
650 struct in6_addr sender
;
652 r
= sd_ndisc_redirect_get_sender_address(rd
, &sender
);
656 if (!in6_addr_equal(&sender
, original_address
))
659 r
= sd_ndisc_redirect_set_sender_address(rd
, current_address
);
667 static int ndisc_router_drop_default(Link
*link
, sd_ndisc_router
*rt
) {
668 _cleanup_(route_unrefp
) Route
*route
= NULL
;
669 struct in6_addr gateway
;
673 assert(link
->network
);
676 r
= sd_ndisc_router_get_sender_address(rt
, &gateway
);
678 return log_link_warning_errno(link
, r
, "Failed to get router address from RA: %m");
680 r
= route_new(&route
);
684 route
->family
= AF_INET6
;
685 route
->nexthop
.family
= AF_INET6
;
686 route
->nexthop
.gw
.in6
= gateway
;
688 r
= ndisc_remove_route(route
, link
);
690 return log_link_warning_errno(link
, r
, "Failed to remove the default gateway configured by RA: %m");
693 HASHMAP_FOREACH(route_gw
, link
->network
->routes_by_section
) {
694 _cleanup_(route_unrefp
) Route
*tmp
= NULL
;
696 if (!route_gw
->gateway_from_dhcp_or_ra
)
699 if (route_gw
->nexthop
.family
!= AF_INET6
)
702 r
= route_dup(route_gw
, NULL
, &tmp
);
706 tmp
->nexthop
.gw
.in6
= gateway
;
708 r
= ndisc_remove_route(tmp
, link
);
710 return log_link_warning_errno(link
, r
, "Could not remove semi-static gateway: %m");
716 static int ndisc_router_process_default(Link
*link
, sd_ndisc_router
*rt
) {
717 usec_t lifetime_usec
;
718 struct in6_addr gateway
;
723 assert(link
->network
);
726 /* If the router lifetime is zero, the router should not be used as the default gateway. */
727 r
= sd_ndisc_router_get_lifetime(rt
, NULL
);
731 return ndisc_router_drop_default(link
, rt
);
733 if (!link
->network
->ndisc_use_gateway
&&
734 hashmap_isempty(link
->network
->routes_by_section
))
737 r
= sd_ndisc_router_get_lifetime_timestamp(rt
, CLOCK_BOOTTIME
, &lifetime_usec
);
739 return log_link_warning_errno(link
, r
, "Failed to get gateway lifetime from RA: %m");
741 r
= sd_ndisc_router_get_sender_address(rt
, &gateway
);
743 return log_link_warning_errno(link
, r
, "Failed to get gateway address from RA: %m");
745 r
= sd_ndisc_router_get_preference(rt
, &preference
);
747 return log_link_warning_errno(link
, r
, "Failed to get router preference from RA: %m");
749 if (link
->network
->ndisc_use_gateway
) {
750 _cleanup_(route_unrefp
) Route
*route
= NULL
;
752 r
= route_new(&route
);
756 route
->family
= AF_INET6
;
757 route
->pref
= preference
;
758 route
->nexthop
.family
= AF_INET6
;
759 route
->nexthop
.gw
.in6
= gateway
;
760 route
->lifetime_usec
= lifetime_usec
;
762 r
= ndisc_request_route(route
, link
, rt
);
764 return log_link_warning_errno(link
, r
, "Could not request default route: %m");
768 HASHMAP_FOREACH(route_gw
, link
->network
->routes_by_section
) {
769 _cleanup_(route_unrefp
) Route
*route
= NULL
;
771 if (!route_gw
->gateway_from_dhcp_or_ra
)
774 if (route_gw
->nexthop
.family
!= AF_INET6
)
777 r
= route_dup(route_gw
, NULL
, &route
);
781 route
->nexthop
.gw
.in6
= gateway
;
782 if (!route
->pref_set
)
783 route
->pref
= preference
;
784 route
->lifetime_usec
= lifetime_usec
;
786 r
= ndisc_request_route(route
, link
, rt
);
788 return log_link_warning_errno(link
, r
, "Could not request gateway: %m");
794 static int update_default_router_address(Link
*link
, const struct in6_addr
*original_address
, const struct in6_addr
*current_address
) {
799 assert(original_address
);
800 assert(current_address
);
802 if (!link
->ndisc_default_router
)
805 r
= sd_ndisc_router_get_sender_address(link
->ndisc_default_router
, &a
);
809 if (!in6_addr_equal(&a
, original_address
))
812 return sd_ndisc_router_set_sender_address(link
->ndisc_default_router
, current_address
);
815 static int drop_default_router(Link
*link
, const struct in6_addr
*router
, usec_t timestamp_usec
) {
816 usec_t lifetime_usec
;
821 if (!link
->ndisc_default_router
)
827 r
= sd_ndisc_router_get_sender_address(link
->ndisc_default_router
, &a
);
831 if (!in6_addr_equal(&a
, router
))
835 r
= sd_ndisc_router_get_lifetime_timestamp(link
->ndisc_default_router
, CLOCK_BOOTTIME
, &lifetime_usec
);
839 if (lifetime_usec
> timestamp_usec
)
842 link
->ndisc_default_router
= sd_ndisc_router_unref(link
->ndisc_default_router
);
846 static int accept_default_router(sd_ndisc_router
*new_router
, sd_ndisc_router
*existing_router
) {
847 usec_t lifetime_usec
;
848 struct in6_addr a
, b
;
854 r
= sd_ndisc_router_get_lifetime(new_router
, &lifetime_usec
);
858 if (lifetime_usec
== 0)
859 return false; /* Received a new RA about revoking the router, ignoring. */
861 if (!existing_router
)
864 /* lifetime of the existing router is already checked in ndisc_drop_outdated(). */
866 r
= sd_ndisc_router_get_sender_address(new_router
, &a
);
870 r
= sd_ndisc_router_get_sender_address(existing_router
, &b
);
874 if (in6_addr_equal(&a
, &b
))
875 return true; /* Received a new RA from the remembered router. Replace the remembered RA. */
877 r
= sd_ndisc_router_get_preference(new_router
, &p
);
881 r
= sd_ndisc_router_get_preference(existing_router
, &q
);
888 if (p
== SD_NDISC_PREFERENCE_HIGH
)
891 if (p
== SD_NDISC_PREFERENCE_MEDIUM
&& q
== SD_NDISC_PREFERENCE_LOW
)
897 static int ndisc_remember_default_router(Link
*link
, sd_ndisc_router
*rt
) {
903 r
= accept_default_router(rt
, link
->ndisc_default_router
);
907 sd_ndisc_router_ref(rt
);
908 sd_ndisc_router_unref(link
->ndisc_default_router
);
909 link
->ndisc_default_router
= rt
;
911 return 1; /* The received router advertisement is from the default router. */
914 static int ndisc_router_process_reachable_time(Link
*link
, sd_ndisc_router
*rt
) {
915 usec_t reachable_time
, msec
;
919 assert(link
->network
);
922 if (!link
->network
->ndisc_use_reachable_time
)
925 /* Ignore the reachable time field of the RA header if the lifetime is zero. */
926 r
= sd_ndisc_router_get_lifetime(rt
, NULL
);
930 r
= sd_ndisc_router_get_reachable_time(rt
, &reachable_time
);
932 return log_link_warning_errno(link
, r
, "Failed to get reachable time from RA: %m");
934 /* 0 is the unspecified value and must not be set (see RFC4861, 6.3.4) */
935 if (!timestamp_is_set(reachable_time
))
938 msec
= DIV_ROUND_UP(reachable_time
, USEC_PER_MSEC
);
939 if (msec
<= 0 || msec
> UINT32_MAX
) {
940 log_link_debug(link
, "Failed to get reachable time from RA - out of range (%"PRIu64
"), ignoring", msec
);
944 /* Set the reachable time for Neighbor Solicitations. */
945 r
= sysctl_write_ip_neighbor_property_uint32(AF_INET6
, link
->ifname
, "base_reachable_time_ms", (uint32_t) msec
);
947 log_link_warning_errno(link
, r
, "Failed to apply neighbor reachable time (%"PRIu64
"), ignoring: %m", msec
);
952 static int ndisc_router_process_retransmission_time(Link
*link
, sd_ndisc_router
*rt
) {
953 usec_t retrans_time
, msec
;
957 assert(link
->network
);
960 if (!link
->network
->ndisc_use_retransmission_time
)
963 /* Ignore the retransmission time field of the RA header if the lifetime is zero. */
964 r
= sd_ndisc_router_get_lifetime(rt
, NULL
);
968 r
= sd_ndisc_router_get_retransmission_time(rt
, &retrans_time
);
970 return log_link_warning_errno(link
, r
, "Failed to get retransmission time from RA: %m");
972 /* 0 is the unspecified value and must not be set (see RFC4861, 6.3.4) */
973 if (!timestamp_is_set(retrans_time
))
976 msec
= DIV_ROUND_UP(retrans_time
, USEC_PER_MSEC
);
977 if (msec
<= 0 || msec
> UINT32_MAX
) {
978 log_link_debug(link
, "Failed to get retransmission time from RA - out of range (%"PRIu64
"), ignoring", msec
);
982 /* Set the retransmission time for Neighbor Solicitations. */
983 r
= sysctl_write_ip_neighbor_property_uint32(AF_INET6
, link
->ifname
, "retrans_time_ms", (uint32_t) msec
);
985 log_link_warning_errno(link
, r
, "Failed to apply neighbor retransmission time (%"PRIu64
"), ignoring: %m", msec
);
990 static int ndisc_router_process_hop_limit(Link
*link
, sd_ndisc_router
*rt
) {
995 assert(link
->network
);
998 if (!link
->network
->ndisc_use_hop_limit
)
1001 /* Ignore the hop limit field of the RA header if the lifetime is zero. */
1002 r
= sd_ndisc_router_get_lifetime(rt
, NULL
);
1006 r
= sd_ndisc_router_get_hop_limit(rt
, &hop_limit
);
1008 return log_link_warning_errno(link
, r
, "Failed to get hop limit from RA: %m");
1010 /* 0 is the unspecified value and must not be set (see RFC4861, 6.3.4):
1012 * A Router Advertisement field (e.g., Cur Hop Limit, Reachable Time, and Retrans Timer) may contain
1013 * a value denoting that it is unspecified. In such cases, the parameter should be ignored and the
1014 * host should continue using whatever value it is already using. In particular, a host MUST NOT
1015 * interpret the unspecified value as meaning change back to the default value that was in use before
1016 * the first Router Advertisement was received.
1018 * If the received Cur Hop Limit value is non-zero, the host SHOULD set
1019 * its CurHopLimit variable to the received value.*/
1023 r
= sysctl_write_ip_property_uint32(AF_INET6
, link
->ifname
, "hop_limit", (uint32_t) hop_limit
);
1025 log_link_warning_errno(link
, r
, "Failed to apply hop_limit (%u), ignoring: %m", hop_limit
);
1030 static int ndisc_router_process_mtu(Link
*link
, sd_ndisc_router
*rt
) {
1035 assert(link
->network
);
1038 if (!link
->network
->ndisc_use_mtu
)
1041 /* Ignore the MTU option if the lifetime is zero. */
1042 r
= sd_ndisc_router_get_lifetime(rt
, NULL
);
1046 r
= sd_ndisc_router_get_mtu(rt
, &mtu
);
1050 return log_link_warning_errno(link
, r
, "Failed to get MTU from RA: %m");
1052 link
->ndisc_mtu
= mtu
;
1054 r
= link_set_ipv6_mtu(link
, LOG_DEBUG
);
1056 log_link_warning_errno(link
, r
, "Failed to apply IPv6 MTU (%"PRIu32
"), ignoring: %m", mtu
);
1061 static int ndisc_router_process_autonomous_prefix(Link
*link
, sd_ndisc_router
*rt
) {
1062 usec_t lifetime_valid_usec
, lifetime_preferred_usec
;
1063 _cleanup_set_free_ Set
*addresses
= NULL
;
1064 struct in6_addr prefix
, *a
;
1069 assert(link
->network
);
1072 if (!link
->network
->ndisc_use_autonomous_prefix
)
1075 r
= sd_ndisc_router_prefix_get_address(rt
, &prefix
);
1077 return log_link_warning_errno(link
, r
, "Failed to get prefix address: %m");
1079 r
= sd_ndisc_router_prefix_get_prefixlen(rt
, &prefixlen
);
1081 return log_link_warning_errno(link
, r
, "Failed to get prefix length: %m");
1083 /* ndisc_generate_addresses() below requires the prefix length <= 64. */
1084 if (prefixlen
> 64) {
1085 log_link_debug(link
, "Prefix is longer than 64, ignoring autonomous prefix %s.",
1086 IN6_ADDR_PREFIX_TO_STRING(&prefix
, prefixlen
));
1090 r
= sd_ndisc_router_prefix_get_valid_lifetime_timestamp(rt
, CLOCK_BOOTTIME
, &lifetime_valid_usec
);
1092 return log_link_warning_errno(link
, r
, "Failed to get prefix valid lifetime: %m");
1094 r
= sd_ndisc_router_prefix_get_preferred_lifetime_timestamp(rt
, CLOCK_BOOTTIME
, &lifetime_preferred_usec
);
1096 return log_link_warning_errno(link
, r
, "Failed to get prefix preferred lifetime: %m");
1098 /* The preferred lifetime is never greater than the valid lifetime */
1099 if (lifetime_preferred_usec
> lifetime_valid_usec
)
1102 r
= ndisc_generate_addresses(link
, &prefix
, prefixlen
, &addresses
);
1104 return log_link_warning_errno(link
, r
, "Failed to generate SLAAC addresses: %m");
1106 SET_FOREACH(a
, addresses
) {
1107 _cleanup_(address_unrefp
) Address
*address
= NULL
;
1109 r
= address_new(&address
);
1113 address
->family
= AF_INET6
;
1114 address
->in_addr
.in6
= *a
;
1115 address
->prefixlen
= prefixlen
;
1116 address
->flags
= IFA_F_NOPREFIXROUTE
|IFA_F_MANAGETEMPADDR
;
1117 address
->lifetime_valid_usec
= lifetime_valid_usec
;
1118 address
->lifetime_preferred_usec
= lifetime_preferred_usec
;
1120 /* draft-ietf-6man-slaac-renum-07 section 4.2
1121 * https://datatracker.ietf.org/doc/html/draft-ietf-6man-slaac-renum-07#section-4.2
1123 * If the advertised prefix is equal to the prefix of an address configured by stateless
1124 * autoconfiguration in the list, the valid lifetime and the preferred lifetime of the
1125 * address should be updated by processing the Valid Lifetime and the Preferred Lifetime
1126 * (respectively) in the received advertisement. */
1127 if (lifetime_valid_usec
== 0) {
1128 r
= address_remove_and_cancel(address
, link
);
1130 return log_link_warning_errno(link
, r
, "Could not remove SLAAC address: %m");
1132 r
= ndisc_request_address(address
, link
, rt
);
1134 return log_link_warning_errno(link
, r
, "Could not request SLAAC address: %m");
1141 static int ndisc_router_process_onlink_prefix(Link
*link
, sd_ndisc_router
*rt
) {
1142 _cleanup_(route_unrefp
) Route
*route
= NULL
;
1143 uint8_t prefixlen
, preference
;
1144 usec_t lifetime_usec
;
1145 struct in6_addr prefix
;
1149 assert(link
->network
);
1152 if (!link
->network
->ndisc_use_onlink_prefix
)
1155 r
= sd_ndisc_router_prefix_get_valid_lifetime_timestamp(rt
, CLOCK_BOOTTIME
, &lifetime_usec
);
1157 return log_link_warning_errno(link
, r
, "Failed to get prefix lifetime: %m");
1159 r
= sd_ndisc_router_prefix_get_address(rt
, &prefix
);
1161 return log_link_warning_errno(link
, r
, "Failed to get prefix address: %m");
1163 r
= sd_ndisc_router_prefix_get_prefixlen(rt
, &prefixlen
);
1165 return log_link_warning_errno(link
, r
, "Failed to get prefix length: %m");
1167 /* Prefix Information option does not have preference, hence we use the 'main' preference here */
1168 r
= sd_ndisc_router_get_preference(rt
, &preference
);
1170 return log_link_warning_errno(link
, r
, "Failed to get router preference from RA: %m");
1172 r
= route_new(&route
);
1176 route
->family
= AF_INET6
;
1177 route
->dst
.in6
= prefix
;
1178 route
->dst_prefixlen
= prefixlen
;
1179 route
->pref
= preference
;
1180 route
->lifetime_usec
= lifetime_usec
;
1182 r
= ndisc_request_route(route
, link
, rt
);
1184 return log_link_warning_errno(link
, r
, "Could not request prefix route: %m");
1189 static int ndisc_router_drop_onlink_prefix(Link
*link
, sd_ndisc_router
*rt
) {
1190 _cleanup_(route_unrefp
) Route
*route
= NULL
;
1192 struct in6_addr prefix
;
1193 usec_t lifetime_usec
;
1197 assert(link
->network
);
1200 /* RFC 4861 section 6.3.4.
1201 * Note, however, that a Prefix Information option with the on-link flag set to zero conveys no
1202 * information concerning on-link determination and MUST NOT be interpreted to mean that addresses
1203 * covered by the prefix are off-link. The only way to cancel a previous on-link indication is to
1204 * advertise that prefix with the L-bit set and the Lifetime set to zero. */
1206 if (!link
->network
->ndisc_use_onlink_prefix
)
1209 r
= sd_ndisc_router_prefix_get_valid_lifetime(rt
, &lifetime_usec
);
1211 return log_link_warning_errno(link
, r
, "Failed to get prefix lifetime: %m");
1213 if (lifetime_usec
!= 0)
1216 r
= sd_ndisc_router_prefix_get_address(rt
, &prefix
);
1218 return log_link_warning_errno(link
, r
, "Failed to get prefix address: %m");
1220 r
= sd_ndisc_router_prefix_get_prefixlen(rt
, &prefixlen
);
1222 return log_link_warning_errno(link
, r
, "Failed to get prefix length: %m");
1224 r
= route_new(&route
);
1228 route
->family
= AF_INET6
;
1229 route
->dst
.in6
= prefix
;
1230 route
->dst_prefixlen
= prefixlen
;
1232 r
= ndisc_remove_route(route
, link
);
1234 return log_link_warning_errno(link
, r
, "Could not remove prefix route: %m");
1239 static int ndisc_router_process_prefix(Link
*link
, sd_ndisc_router
*rt
) {
1240 uint8_t flags
, prefixlen
;
1245 assert(link
->network
);
1248 r
= sd_ndisc_router_prefix_get_address(rt
, &a
);
1250 return log_link_warning_errno(link
, r
, "Failed to get prefix address: %m");
1252 /* RFC 4861 Section 4.6.2:
1253 * A router SHOULD NOT send a prefix option for the link-local prefix and a host SHOULD ignore such
1254 * a prefix option. */
1255 if (in6_addr_is_link_local(&a
)) {
1256 log_link_debug(link
, "Received link-local prefix, ignoring prefix.");
1260 r
= sd_ndisc_router_prefix_get_prefixlen(rt
, &prefixlen
);
1262 return log_link_warning_errno(link
, r
, "Failed to get prefix length: %m");
1264 if (in6_prefix_is_filtered(&a
, prefixlen
, link
->network
->ndisc_allow_listed_prefix
, link
->network
->ndisc_deny_listed_prefix
)) {
1266 log_link_debug(link
, "Prefix '%s' is %s, ignoring",
1267 !set_isempty(link
->network
->ndisc_allow_listed_prefix
) ? "not in allow list"
1269 IN6_ADDR_PREFIX_TO_STRING(&a
, prefixlen
));
1273 r
= sd_ndisc_router_prefix_get_flags(rt
, &flags
);
1275 return log_link_warning_errno(link
, r
, "Failed to get RA prefix flags: %m");
1277 if (FLAGS_SET(flags
, ND_OPT_PI_FLAG_ONLINK
))
1278 r
= ndisc_router_process_onlink_prefix(link
, rt
);
1280 r
= ndisc_router_drop_onlink_prefix(link
, rt
);
1284 if (FLAGS_SET(flags
, ND_OPT_PI_FLAG_AUTO
)) {
1285 r
= ndisc_router_process_autonomous_prefix(link
, rt
);
1293 static int ndisc_router_process_route(Link
*link
, sd_ndisc_router
*rt
) {
1294 _cleanup_(route_unrefp
) Route
*route
= NULL
;
1295 uint8_t preference
, prefixlen
;
1296 struct in6_addr gateway
, dst
;
1297 usec_t lifetime_usec
;
1302 if (!link
->network
->ndisc_use_route_prefix
)
1305 r
= sd_ndisc_router_route_get_lifetime_timestamp(rt
, CLOCK_BOOTTIME
, &lifetime_usec
);
1307 return log_link_warning_errno(link
, r
, "Failed to get route lifetime from RA: %m");
1309 r
= sd_ndisc_router_route_get_address(rt
, &dst
);
1311 return log_link_warning_errno(link
, r
, "Failed to get route destination address: %m");
1313 r
= sd_ndisc_router_route_get_prefixlen(rt
, &prefixlen
);
1315 return log_link_warning_errno(link
, r
, "Failed to get route prefix length: %m");
1317 if (in6_prefix_is_filtered(&dst
, prefixlen
,
1318 link
->network
->ndisc_allow_listed_route_prefix
,
1319 link
->network
->ndisc_deny_listed_route_prefix
)) {
1322 log_link_debug(link
, "Route prefix %s is %s, ignoring",
1323 !set_isempty(link
->network
->ndisc_allow_listed_route_prefix
) ? "not in allow list"
1325 IN6_ADDR_PREFIX_TO_STRING(&dst
, prefixlen
));
1329 r
= sd_ndisc_router_get_sender_address(rt
, &gateway
);
1331 return log_link_warning_errno(link
, r
, "Failed to get gateway address from RA: %m");
1333 if (link_get_ipv6_address(link
, &gateway
, 0, NULL
) >= 0) {
1335 log_link_debug(link
, "Advertised route gateway %s is local to the link, ignoring route",
1336 IN6_ADDR_TO_STRING(&gateway
));
1340 r
= sd_ndisc_router_route_get_preference(rt
, &preference
);
1341 if (r
== -EOPNOTSUPP
) {
1342 log_link_debug_errno(link
, r
, "Received route prefix with unsupported preference, ignoring: %m");
1346 return log_link_warning_errno(link
, r
, "Failed to get router preference from RA: %m");
1348 r
= route_new(&route
);
1352 route
->family
= AF_INET6
;
1353 route
->pref
= preference
;
1354 route
->nexthop
.gw
.in6
= gateway
;
1355 route
->nexthop
.family
= AF_INET6
;
1356 route
->dst
.in6
= dst
;
1357 route
->dst_prefixlen
= prefixlen
;
1358 route
->lifetime_usec
= lifetime_usec
;
1360 if (lifetime_usec
!= 0) {
1361 r
= ndisc_request_route(route
, link
, rt
);
1363 return log_link_warning_errno(link
, r
, "Could not request additional route: %m");
1365 r
= ndisc_remove_route(route
, link
);
1367 return log_link_warning_errno(link
, r
, "Could not remove additional route with zero lifetime: %m");
1373 static void ndisc_rdnss_hash_func(const NDiscRDNSS
*x
, struct siphash
*state
) {
1374 siphash24_compress_typesafe(x
->address
, state
);
1377 static int ndisc_rdnss_compare_func(const NDiscRDNSS
*a
, const NDiscRDNSS
*b
) {
1378 return memcmp(&a
->address
, &b
->address
, sizeof(a
->address
));
1381 DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(
1382 ndisc_rdnss_hash_ops
,
1384 ndisc_rdnss_hash_func
,
1385 ndisc_rdnss_compare_func
,
1388 static int ndisc_router_process_rdnss(Link
*link
, sd_ndisc_router
*rt
) {
1389 usec_t lifetime_usec
;
1390 const struct in6_addr
*a
;
1391 struct in6_addr router
;
1392 bool updated
= false, logged_about_too_many
= false;
1396 assert(link
->network
);
1399 if (!link
->network
->ndisc_use_dns
)
1402 r
= sd_ndisc_router_get_sender_address(rt
, &router
);
1404 return log_link_warning_errno(link
, r
, "Failed to get router address from RA: %m");
1406 r
= sd_ndisc_router_rdnss_get_lifetime_timestamp(rt
, CLOCK_BOOTTIME
, &lifetime_usec
);
1408 return log_link_warning_errno(link
, r
, "Failed to get RDNSS lifetime: %m");
1410 n
= sd_ndisc_router_rdnss_get_addresses(rt
, &a
);
1412 return log_link_warning_errno(link
, n
, "Failed to get RDNSS addresses: %m");
1414 for (int j
= 0; j
< n
; j
++) {
1415 _cleanup_free_ NDiscRDNSS
*x
= NULL
;
1416 NDiscRDNSS
*rdnss
, d
= {
1420 if (lifetime_usec
== 0) {
1421 /* The entry is outdated. */
1422 free(set_remove(link
->ndisc_rdnss
, &d
));
1427 rdnss
= set_get(link
->ndisc_rdnss
, &d
);
1429 rdnss
->router
= router
;
1430 rdnss
->lifetime_usec
= lifetime_usec
;
1434 if (set_size(link
->ndisc_rdnss
) >= NDISC_RDNSS_MAX
) {
1435 if (!logged_about_too_many
)
1436 log_link_warning(link
, "Too many RDNSS records per link. Only first %u records will be used.", NDISC_RDNSS_MAX
);
1437 logged_about_too_many
= true;
1441 x
= new(NDiscRDNSS
, 1);
1448 .lifetime_usec
= lifetime_usec
,
1451 r
= set_ensure_consume(&link
->ndisc_rdnss
, &ndisc_rdnss_hash_ops
, TAKE_PTR(x
));
1465 static void ndisc_dnssl_hash_func(const NDiscDNSSL
*x
, struct siphash
*state
) {
1466 siphash24_compress_string(NDISC_DNSSL_DOMAIN(x
), state
);
1469 static int ndisc_dnssl_compare_func(const NDiscDNSSL
*a
, const NDiscDNSSL
*b
) {
1470 return strcmp(NDISC_DNSSL_DOMAIN(a
), NDISC_DNSSL_DOMAIN(b
));
1473 DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(
1474 ndisc_dnssl_hash_ops
,
1476 ndisc_dnssl_hash_func
,
1477 ndisc_dnssl_compare_func
,
1480 static int ndisc_router_process_dnssl(Link
*link
, sd_ndisc_router
*rt
) {
1482 usec_t lifetime_usec
;
1483 struct in6_addr router
;
1484 bool updated
= false, logged_about_too_many
= false;
1488 assert(link
->network
);
1491 if (link
->network
->ndisc_use_domains
== DHCP_USE_DOMAINS_NO
)
1494 r
= sd_ndisc_router_get_sender_address(rt
, &router
);
1496 return log_link_warning_errno(link
, r
, "Failed to get router address from RA: %m");
1498 r
= sd_ndisc_router_dnssl_get_lifetime_timestamp(rt
, CLOCK_BOOTTIME
, &lifetime_usec
);
1500 return log_link_warning_errno(link
, r
, "Failed to get DNSSL lifetime: %m");
1502 r
= sd_ndisc_router_dnssl_get_domains(rt
, &l
);
1504 return log_link_warning_errno(link
, r
, "Failed to get DNSSL addresses: %m");
1506 STRV_FOREACH(j
, l
) {
1507 _cleanup_free_ NDiscDNSSL
*s
= NULL
;
1510 s
= malloc0(ALIGN(sizeof(NDiscDNSSL
)) + strlen(*j
) + 1);
1514 strcpy(NDISC_DNSSL_DOMAIN(s
), *j
);
1516 if (lifetime_usec
== 0) {
1517 /* The entry is outdated. */
1518 free(set_remove(link
->ndisc_dnssl
, s
));
1523 dnssl
= set_get(link
->ndisc_dnssl
, s
);
1525 dnssl
->router
= router
;
1526 dnssl
->lifetime_usec
= lifetime_usec
;
1530 if (set_size(link
->ndisc_dnssl
) >= NDISC_DNSSL_MAX
) {
1531 if (!logged_about_too_many
)
1532 log_link_warning(link
, "Too many DNSSL records per link. Only first %u records will be used.", NDISC_DNSSL_MAX
);
1533 logged_about_too_many
= true;
1538 s
->lifetime_usec
= lifetime_usec
;
1540 r
= set_ensure_consume(&link
->ndisc_dnssl
, &ndisc_dnssl_hash_ops
, TAKE_PTR(s
));
1554 static NDiscCaptivePortal
* ndisc_captive_portal_free(NDiscCaptivePortal
*x
) {
1558 free(x
->captive_portal
);
1562 DEFINE_TRIVIAL_CLEANUP_FUNC(NDiscCaptivePortal
*, ndisc_captive_portal_free
);
1564 static void ndisc_captive_portal_hash_func(const NDiscCaptivePortal
*x
, struct siphash
*state
) {
1566 siphash24_compress_string(x
->captive_portal
, state
);
1569 static int ndisc_captive_portal_compare_func(const NDiscCaptivePortal
*a
, const NDiscCaptivePortal
*b
) {
1572 return strcmp_ptr(a
->captive_portal
, b
->captive_portal
);
1575 DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(
1576 ndisc_captive_portal_hash_ops
,
1578 ndisc_captive_portal_hash_func
,
1579 ndisc_captive_portal_compare_func
,
1580 ndisc_captive_portal_free
);
1582 static int ndisc_router_process_captive_portal(Link
*link
, sd_ndisc_router
*rt
) {
1583 _cleanup_(ndisc_captive_portal_freep
) NDiscCaptivePortal
*new_entry
= NULL
;
1584 _cleanup_free_
char *captive_portal
= NULL
;
1586 usec_t lifetime_usec
;
1587 NDiscCaptivePortal
*exist
;
1588 struct in6_addr router
;
1592 assert(link
->network
);
1595 if (!link
->network
->ndisc_use_captive_portal
)
1598 r
= sd_ndisc_router_get_sender_address(rt
, &router
);
1600 return log_link_warning_errno(link
, r
, "Failed to get router address from RA: %m");
1602 /* RFC 4861 section 4.2. states that the lifetime in the message header should be used only for the
1603 * default gateway, but the captive portal option does not have a lifetime field, hence, we use the
1604 * main lifetime for the portal. */
1605 r
= sd_ndisc_router_get_lifetime_timestamp(rt
, CLOCK_BOOTTIME
, &lifetime_usec
);
1607 return log_link_warning_errno(link
, r
, "Failed to get lifetime of RA message: %m");
1609 r
= sd_ndisc_router_get_captive_portal(rt
, &uri
);
1611 return log_link_warning_errno(link
, r
, "Failed to get captive portal from RA: %m");
1613 captive_portal
= strdup(uri
);
1614 if (!captive_portal
)
1617 if (lifetime_usec
== 0) {
1618 /* Drop the portal with zero lifetime. */
1619 ndisc_captive_portal_free(set_remove(link
->ndisc_captive_portals
,
1620 &(const NDiscCaptivePortal
) {
1621 .captive_portal
= captive_portal
,
1626 exist
= set_get(link
->ndisc_captive_portals
,
1627 &(const NDiscCaptivePortal
) {
1628 .captive_portal
= captive_portal
,
1631 /* update existing entry */
1632 exist
->router
= router
;
1633 exist
->lifetime_usec
= lifetime_usec
;
1637 if (set_size(link
->ndisc_captive_portals
) >= NDISC_CAPTIVE_PORTAL_MAX
) {
1638 NDiscCaptivePortal
*c
, *target
= NULL
;
1640 /* Find the portal who has the minimal lifetime and drop it to store new one. */
1641 SET_FOREACH(c
, link
->ndisc_captive_portals
)
1642 if (!target
|| c
->lifetime_usec
< target
->lifetime_usec
)
1646 assert(set_remove(link
->ndisc_captive_portals
, target
) == target
);
1647 ndisc_captive_portal_free(target
);
1650 new_entry
= new(NDiscCaptivePortal
, 1);
1654 *new_entry
= (NDiscCaptivePortal
) {
1656 .lifetime_usec
= lifetime_usec
,
1657 .captive_portal
= TAKE_PTR(captive_portal
),
1660 r
= set_ensure_put(&link
->ndisc_captive_portals
, &ndisc_captive_portal_hash_ops
, new_entry
);
1664 TAKE_PTR(new_entry
);
1670 static void ndisc_pref64_hash_func(const NDiscPREF64
*x
, struct siphash
*state
) {
1673 siphash24_compress_typesafe(x
->prefix_len
, state
);
1674 siphash24_compress_typesafe(x
->prefix
, state
);
1677 static int ndisc_pref64_compare_func(const NDiscPREF64
*a
, const NDiscPREF64
*b
) {
1683 r
= CMP(a
->prefix_len
, b
->prefix_len
);
1687 return memcmp(&a
->prefix
, &b
->prefix
, sizeof(a
->prefix
));
1690 DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(
1691 ndisc_pref64_hash_ops
,
1693 ndisc_pref64_hash_func
,
1694 ndisc_pref64_compare_func
,
1697 static int ndisc_router_process_pref64(Link
*link
, sd_ndisc_router
*rt
) {
1698 _cleanup_free_ NDiscPREF64
*new_entry
= NULL
;
1699 usec_t lifetime_usec
;
1700 struct in6_addr a
, router
;
1706 assert(link
->network
);
1709 if (!link
->network
->ndisc_use_pref64
)
1712 r
= sd_ndisc_router_get_sender_address(rt
, &router
);
1714 return log_link_warning_errno(link
, r
, "Failed to get router address from RA: %m");
1716 r
= sd_ndisc_router_prefix64_get_prefix(rt
, &a
);
1718 return log_link_warning_errno(link
, r
, "Failed to get pref64 prefix: %m");
1720 r
= sd_ndisc_router_prefix64_get_prefixlen(rt
, &prefix_len
);
1722 return log_link_warning_errno(link
, r
, "Failed to get pref64 prefix length: %m");
1724 r
= sd_ndisc_router_prefix64_get_lifetime_timestamp(rt
, CLOCK_BOOTTIME
, &lifetime_usec
);
1726 return log_link_warning_errno(link
, r
, "Failed to get pref64 prefix lifetime: %m");
1728 if (lifetime_usec
== 0) {
1729 free(set_remove(link
->ndisc_pref64
,
1732 .prefix_len
= prefix_len
1737 exist
= set_get(link
->ndisc_pref64
,
1740 .prefix_len
= prefix_len
1743 /* update existing entry */
1744 exist
->router
= router
;
1745 exist
->lifetime_usec
= lifetime_usec
;
1749 if (set_size(link
->ndisc_pref64
) >= NDISC_PREF64_MAX
) {
1750 log_link_debug(link
, "Too many PREF64 records received. Only first %u records will be used.", NDISC_PREF64_MAX
);
1754 new_entry
= new(NDiscPREF64
, 1);
1758 *new_entry
= (NDiscPREF64
) {
1760 .lifetime_usec
= lifetime_usec
,
1762 .prefix_len
= prefix_len
,
1765 r
= set_ensure_put(&link
->ndisc_pref64
, &ndisc_pref64_hash_ops
, new_entry
);
1770 TAKE_PTR(new_entry
);
1775 static int ndisc_router_process_options(Link
*link
, sd_ndisc_router
*rt
) {
1776 size_t n_captive_portal
= 0;
1780 assert(link
->network
);
1783 for (r
= sd_ndisc_router_option_rewind(rt
); ; r
= sd_ndisc_router_option_next(rt
)) {
1787 return log_link_warning_errno(link
, r
, "Failed to iterate through options: %m");
1788 if (r
== 0) /* EOF */
1791 r
= sd_ndisc_router_option_get_type(rt
, &type
);
1793 return log_link_warning_errno(link
, r
, "Failed to get RA option type: %m");
1796 case SD_NDISC_OPTION_PREFIX_INFORMATION
:
1797 r
= ndisc_router_process_prefix(link
, rt
);
1800 case SD_NDISC_OPTION_ROUTE_INFORMATION
:
1801 r
= ndisc_router_process_route(link
, rt
);
1804 case SD_NDISC_OPTION_RDNSS
:
1805 r
= ndisc_router_process_rdnss(link
, rt
);
1808 case SD_NDISC_OPTION_DNSSL
:
1809 r
= ndisc_router_process_dnssl(link
, rt
);
1811 case SD_NDISC_OPTION_CAPTIVE_PORTAL
:
1812 if (n_captive_portal
> 0) {
1813 if (n_captive_portal
== 1)
1814 log_link_notice(link
, "Received RA with multiple captive portals, only using the first one.");
1819 r
= ndisc_router_process_captive_portal(link
, rt
);
1823 case SD_NDISC_OPTION_PREF64
:
1824 r
= ndisc_router_process_pref64(link
, rt
);
1827 if (r
< 0 && r
!= -EBADMSG
)
1832 static int ndisc_drop_outdated(Link
*link
, const struct in6_addr
*router
, usec_t timestamp_usec
) {
1833 bool updated
= false;
1836 NDiscCaptivePortal
*cp
;
1843 assert(link
->manager
);
1845 /* If an address or friends is already assigned, but not valid anymore, then refuse to update it,
1846 * and let's immediately remove it.
1847 * See RFC4862, section 5.5.3.e. But the following logic is deviated from RFC4862 by honoring all
1848 * valid lifetimes to improve the reaction of SLAAC to renumbering events.
1849 * See draft-ietf-6man-slaac-renum-02, section 4.2. */
1851 r
= drop_default_router(link
, router
, timestamp_usec
);
1853 RET_GATHER(ret
, log_link_warning_errno(link
, r
, "Failed to drop outdated default router, ignoring: %m"));
1855 r
= ndisc_drop_redirect(link
, router
, /* remove = */ false);
1857 RET_GATHER(ret
, log_link_warning_errno(link
, r
, "Failed to drop outdated Redirect messages, ignoring: %m"));
1859 SET_FOREACH(route
, link
->manager
->routes
) {
1860 if (route
->source
!= NETWORK_CONFIG_SOURCE_NDISC
)
1863 if (route
->nexthop
.ifindex
!= link
->ifindex
)
1866 if (route
->lifetime_usec
> timestamp_usec
)
1867 continue; /* the route is still valid */
1869 if (router
&& !in6_addr_equal(&route
->provider
.in6
, router
))
1872 r
= route_remove_and_cancel(route
, link
->manager
);
1874 RET_GATHER(ret
, log_link_warning_errno(link
, r
, "Failed to remove outdated SLAAC route, ignoring: %m"));
1877 SET_FOREACH(address
, link
->addresses
) {
1878 if (address
->source
!= NETWORK_CONFIG_SOURCE_NDISC
)
1881 if (address
->lifetime_valid_usec
> timestamp_usec
)
1882 continue; /* the address is still valid */
1884 if (router
&& !in6_addr_equal(&address
->provider
.in6
, router
))
1887 r
= address_remove_and_cancel(address
, link
);
1889 RET_GATHER(ret
, log_link_warning_errno(link
, r
, "Failed to remove outdated SLAAC address, ignoring: %m"));
1892 SET_FOREACH(rdnss
, link
->ndisc_rdnss
) {
1893 if (rdnss
->lifetime_usec
> timestamp_usec
)
1894 continue; /* the DNS server is still valid */
1896 if (router
&& !in6_addr_equal(&rdnss
->router
, router
))
1899 free(set_remove(link
->ndisc_rdnss
, rdnss
));
1903 SET_FOREACH(dnssl
, link
->ndisc_dnssl
) {
1904 if (dnssl
->lifetime_usec
> timestamp_usec
)
1905 continue; /* the DNS domain is still valid */
1907 if (router
&& !in6_addr_equal(&dnssl
->router
, router
))
1910 free(set_remove(link
->ndisc_dnssl
, dnssl
));
1914 SET_FOREACH(cp
, link
->ndisc_captive_portals
) {
1915 if (cp
->lifetime_usec
> timestamp_usec
)
1916 continue; /* the captive portal is still valid */
1918 if (router
&& !in6_addr_equal(&cp
->router
, router
))
1921 ndisc_captive_portal_free(set_remove(link
->ndisc_captive_portals
, cp
));
1925 SET_FOREACH(p64
, link
->ndisc_pref64
) {
1926 if (p64
->lifetime_usec
> timestamp_usec
)
1927 continue; /* the pref64 prefix is still valid */
1929 if (router
&& !in6_addr_equal(&p64
->router
, router
))
1932 free(set_remove(link
->ndisc_pref64
, p64
));
1933 /* The pref64 prefix is not exported through the state file, hence it is not necessary to set
1934 * the 'updated' flag. */
1943 static int ndisc_setup_expire(Link
*link
);
1945 static int ndisc_expire_handler(sd_event_source
*s
, uint64_t usec
, void *userdata
) {
1946 Link
*link
= ASSERT_PTR(userdata
);
1949 assert(link
->manager
);
1951 assert_se(sd_event_now(link
->manager
->event
, CLOCK_BOOTTIME
, &now_usec
) >= 0);
1953 (void) ndisc_drop_outdated(link
, /* router = */ NULL
, now_usec
);
1954 (void) ndisc_setup_expire(link
);
1958 static int ndisc_setup_expire(Link
*link
) {
1959 usec_t lifetime_usec
= USEC_INFINITY
;
1960 NDiscCaptivePortal
*cp
;
1969 assert(link
->manager
);
1971 SET_FOREACH(route
, link
->manager
->routes
) {
1972 if (route
->source
!= NETWORK_CONFIG_SOURCE_NDISC
)
1975 if (route
->nexthop
.ifindex
!= link
->ifindex
)
1978 if (!route_exists(route
))
1981 lifetime_usec
= MIN(lifetime_usec
, route
->lifetime_usec
);
1984 SET_FOREACH(address
, link
->addresses
) {
1985 if (address
->source
!= NETWORK_CONFIG_SOURCE_NDISC
)
1988 if (!address_exists(address
))
1991 lifetime_usec
= MIN(lifetime_usec
, address
->lifetime_valid_usec
);
1994 SET_FOREACH(rdnss
, link
->ndisc_rdnss
)
1995 lifetime_usec
= MIN(lifetime_usec
, rdnss
->lifetime_usec
);
1997 SET_FOREACH(dnssl
, link
->ndisc_dnssl
)
1998 lifetime_usec
= MIN(lifetime_usec
, dnssl
->lifetime_usec
);
2000 SET_FOREACH(cp
, link
->ndisc_captive_portals
)
2001 lifetime_usec
= MIN(lifetime_usec
, cp
->lifetime_usec
);
2003 SET_FOREACH(p64
, link
->ndisc_pref64
)
2004 lifetime_usec
= MIN(lifetime_usec
, p64
->lifetime_usec
);
2006 if (lifetime_usec
== USEC_INFINITY
)
2009 r
= event_reset_time(link
->manager
->event
, &link
->ndisc_expire
, CLOCK_BOOTTIME
,
2010 lifetime_usec
, 0, ndisc_expire_handler
, link
, 0, "ndisc-expiration", true);
2012 return log_link_warning_errno(link
, r
, "Failed to update expiration timer for ndisc: %m");
2017 static int ndisc_start_dhcp6_client(Link
*link
, sd_ndisc_router
*rt
) {
2021 assert(link
->network
);
2023 /* Do not start DHCPv6 client if the router lifetime is zero, as the message sent as a signal of
2024 * that the router is e.g. shutting down, revoked, etc,. */
2025 r
= sd_ndisc_router_get_lifetime(rt
, NULL
);
2029 switch (link
->network
->ndisc_start_dhcp6_client
) {
2030 case IPV6_ACCEPT_RA_START_DHCP6_CLIENT_NO
:
2033 case IPV6_ACCEPT_RA_START_DHCP6_CLIENT_YES
: {
2036 r
= sd_ndisc_router_get_flags(rt
, &flags
);
2038 return log_link_warning_errno(link
, r
, "Failed to get RA flags: %m");
2040 if ((flags
& (ND_RA_FLAG_MANAGED
| ND_RA_FLAG_OTHER
)) == 0)
2043 /* (re)start DHCPv6 client in stateful or stateless mode according to RA flags.
2044 * Note, if both "managed" and "other configuration" bits are set, then ignore
2045 * "other configuration" bit. See RFC 4861. */
2046 r
= dhcp6_start_on_ra(link
, !(flags
& ND_RA_FLAG_MANAGED
));
2049 case IPV6_ACCEPT_RA_START_DHCP6_CLIENT_ALWAYS
:
2050 /* When IPv6AcceptRA.DHCPv6Client=always, start dhcp6 client in solicit mode
2051 * even if the router flags have neither M nor O flags. */
2052 r
= dhcp6_start_on_ra(link
, /* information_request = */ false);
2056 assert_not_reached();
2060 return log_link_warning_errno(link
, r
, "Could not acquire DHCPv6 lease on NDisc request: %m");
2062 log_link_debug(link
, "Acquiring DHCPv6 lease on NDisc request");
2066 static int ndisc_router_handler(Link
*link
, sd_ndisc_router
*rt
) {
2067 struct in6_addr router
;
2068 usec_t timestamp_usec
;
2073 assert(link
->network
);
2074 assert(link
->manager
);
2077 r
= sd_ndisc_router_get_sender_address(rt
, &router
);
2078 if (r
== -ENODATA
) {
2079 log_link_debug(link
, "Received RA without router address, ignoring.");
2083 return log_link_warning_errno(link
, r
, "Failed to get router address from RA: %m");
2085 if (in6_prefix_is_filtered(&router
, 128, link
->network
->ndisc_allow_listed_router
, link
->network
->ndisc_deny_listed_router
)) {
2086 if (DEBUG_LOGGING
) {
2087 if (!set_isempty(link
->network
->ndisc_allow_listed_router
))
2088 log_link_debug(link
, "Router %s is not in allow list, ignoring.", IN6_ADDR_TO_STRING(&router
));
2090 log_link_debug(link
, "Router %s is in deny list, ignoring.", IN6_ADDR_TO_STRING(&router
));
2095 r
= sd_ndisc_router_get_timestamp(rt
, CLOCK_BOOTTIME
, ×tamp_usec
);
2096 if (r
== -ENODATA
) {
2097 log_link_debug(link
, "Received RA without timestamp, ignoring.");
2103 r
= ndisc_drop_outdated(link
, /* router = */ NULL
, timestamp_usec
);
2107 r
= ndisc_remember_default_router(link
, rt
);
2112 r
= ndisc_start_dhcp6_client(link
, rt
);
2116 r
= ndisc_router_process_default(link
, rt
);
2120 r
= ndisc_router_process_reachable_time(link
, rt
);
2124 r
= ndisc_router_process_retransmission_time(link
, rt
);
2128 r
= ndisc_router_process_hop_limit(link
, rt
);
2132 r
= ndisc_router_process_mtu(link
, rt
);
2136 r
= ndisc_router_process_options(link
, rt
);
2140 r
= ndisc_setup_expire(link
);
2145 r
= ndisc_router_update_redirect(link
);
2149 } else if (sd_ndisc_router_get_lifetime(rt
, NULL
) <= 0) {
2150 r
= ndisc_drop_redirect(link
, &router
, /* remove = */ true);
2155 if (link
->ndisc_messages
== 0)
2156 link
->ndisc_configured
= true;
2158 log_link_debug(link
, "Setting SLAAC addresses and router.");
2160 if (!link
->ndisc_configured
)
2161 link_set_state(link
, LINK_STATE_CONFIGURING
);
2163 link_check_ready(link
);
2167 static int ndisc_neighbor_handle_non_router_message(Link
*link
, sd_ndisc_neighbor
*na
) {
2168 struct in6_addr address
;
2174 /* Received Neighbor Advertisement message without Router flag. The node might have been a router,
2175 * and now it is not. Let's drop all configurations based on RAs sent from the node. */
2177 r
= sd_ndisc_neighbor_get_target_address(na
, &address
);
2183 (void) ndisc_drop_outdated(link
, /* router = */ &address
, /* timestamp_usec = */ USEC_INFINITY
);
2187 static int ndisc_neighbor_handle_router_message(Link
*link
, sd_ndisc_neighbor
*na
) {
2188 struct in6_addr current_address
, original_address
;
2192 assert(link
->manager
);
2195 /* Received Neighbor Advertisement message with Router flag. If the router address is changed, update
2196 * the provider field of configurations. */
2198 r
= sd_ndisc_neighbor_get_sender_address(na
, ¤t_address
);
2204 r
= sd_ndisc_neighbor_get_target_address(na
, &original_address
);
2210 if (in6_addr_equal(¤t_address
, &original_address
))
2211 return 0; /* the router address is not changed */
2213 r
= update_default_router_address(link
, &original_address
, ¤t_address
);
2217 r
= ndisc_update_redirect_sender(link
, &original_address
, ¤t_address
);
2222 SET_FOREACH(route
, link
->manager
->routes
) {
2223 if (route
->source
!= NETWORK_CONFIG_SOURCE_NDISC
)
2226 if (route
->nexthop
.ifindex
!= link
->ifindex
)
2229 if (!in6_addr_equal(&route
->provider
.in6
, &original_address
))
2232 route
->provider
.in6
= current_address
;
2236 SET_FOREACH(address
, link
->addresses
) {
2237 if (address
->source
!= NETWORK_CONFIG_SOURCE_NDISC
)
2240 if (!in6_addr_equal(&address
->provider
.in6
, &original_address
))
2243 address
->provider
.in6
= current_address
;
2247 SET_FOREACH(rdnss
, link
->ndisc_rdnss
) {
2248 if (!in6_addr_equal(&rdnss
->router
, &original_address
))
2251 rdnss
->router
= current_address
;
2255 SET_FOREACH(dnssl
, link
->ndisc_dnssl
) {
2256 if (!in6_addr_equal(&dnssl
->router
, &original_address
))
2259 dnssl
->router
= current_address
;
2262 NDiscCaptivePortal
*cp
;
2263 SET_FOREACH(cp
, link
->ndisc_captive_portals
) {
2264 if (!in6_addr_equal(&cp
->router
, &original_address
))
2267 cp
->router
= current_address
;
2271 SET_FOREACH(p64
, link
->ndisc_pref64
) {
2272 if (!in6_addr_equal(&p64
->router
, &original_address
))
2275 p64
->router
= current_address
;
2281 static int ndisc_neighbor_handler(Link
*link
, sd_ndisc_neighbor
*na
) {
2287 r
= sd_ndisc_neighbor_is_router(na
);
2291 r
= ndisc_neighbor_handle_non_router_message(link
, na
);
2293 r
= ndisc_neighbor_handle_router_message(link
, na
);
2300 static void ndisc_handler(sd_ndisc
*nd
, sd_ndisc_event_t event
, void *message
, void *userdata
) {
2301 Link
*link
= ASSERT_PTR(userdata
);
2304 if (IN_SET(link
->state
, LINK_STATE_FAILED
, LINK_STATE_LINGER
))
2309 case SD_NDISC_EVENT_ROUTER
:
2310 r
= ndisc_router_handler(link
, ASSERT_PTR(message
));
2311 if (r
< 0 && r
!= -EBADMSG
) {
2312 link_enter_failed(link
);
2317 case SD_NDISC_EVENT_NEIGHBOR
:
2318 r
= ndisc_neighbor_handler(link
, ASSERT_PTR(message
));
2319 if (r
< 0 && r
!= -EBADMSG
) {
2320 link_enter_failed(link
);
2325 case SD_NDISC_EVENT_REDIRECT
:
2326 r
= ndisc_redirect_handler(link
, ASSERT_PTR(message
));
2327 if (r
< 0 && r
!= -EBADMSG
) {
2328 log_link_warning_errno(link
, r
, "Failed to process Redirect message: %m");
2329 link_enter_failed(link
);
2334 case SD_NDISC_EVENT_TIMEOUT
:
2335 log_link_debug(link
, "NDisc handler get timeout event");
2336 if (link
->ndisc_messages
== 0) {
2337 link
->ndisc_configured
= true;
2338 link_check_ready(link
);
2343 log_link_debug(link
, "Received unsupported NDisc event, ignoring.");
2347 static int ndisc_configure(Link
*link
) {
2352 if (!link_ndisc_enabled(link
))
2356 return -EBUSY
; /* Already configured. */
2358 r
= sd_ndisc_new(&link
->ndisc
);
2362 r
= sd_ndisc_attach_event(link
->ndisc
, link
->manager
->event
, 0);
2366 if (link
->hw_addr
.length
== ETH_ALEN
) {
2367 r
= sd_ndisc_set_mac(link
->ndisc
, &link
->hw_addr
.ether
);
2372 r
= sd_ndisc_set_ifindex(link
->ndisc
, link
->ifindex
);
2376 r
= sd_ndisc_set_callback(link
->ndisc
, ndisc_handler
, link
);
2383 int ndisc_start(Link
*link
) {
2388 if (!link
->ndisc
|| !link
->dhcp6_client
)
2391 if (!link_has_carrier(link
))
2394 if (in6_addr_is_null(&link
->ipv6ll_address
))
2397 r
= sd_ndisc_set_link_local_address(link
->ndisc
, &link
->ipv6ll_address
);
2401 log_link_debug(link
, "Discovering IPv6 routers");
2403 r
= sd_ndisc_start(link
->ndisc
);
2410 static int ndisc_process_request(Request
*req
, Link
*link
, void *userdata
) {
2415 if (!link_is_ready_to_configure(link
, /* allow_unmanaged = */ false))
2418 r
= ndisc_configure(link
);
2420 return log_link_warning_errno(link
, r
, "Failed to configure IPv6 Router Discovery: %m");
2422 r
= ndisc_start(link
);
2424 return log_link_warning_errno(link
, r
, "Failed to start IPv6 Router Discovery: %m");
2426 log_link_debug(link
, "IPv6 Router Discovery is configured%s.",
2427 r
> 0 ? " and started" : "");
2431 int link_request_ndisc(Link
*link
) {
2436 if (!link_ndisc_enabled(link
))
2442 r
= link_queue_request(link
, REQUEST_TYPE_NDISC
, ndisc_process_request
, NULL
);
2444 return log_link_warning_errno(link
, r
, "Failed to request configuring of the IPv6 Router Discovery: %m");
2446 log_link_debug(link
, "Requested configuring of the IPv6 Router Discovery.");
2450 int ndisc_stop(Link
*link
) {
2453 link
->ndisc_expire
= sd_event_source_disable_unref(link
->ndisc_expire
);
2455 return sd_ndisc_stop(link
->ndisc
);
2459 void ndisc_flush(Link
*link
) {
2462 /* Remove all addresses, routes, RDNSS, DNSSL, and Captive Portal entries, without exception. */
2463 (void) ndisc_drop_outdated(link
, /* router = */ NULL
, /* timestamp_usec = */ USEC_INFINITY
);
2465 link
->ndisc_rdnss
= set_free(link
->ndisc_rdnss
);
2466 link
->ndisc_dnssl
= set_free(link
->ndisc_dnssl
);
2467 link
->ndisc_captive_portals
= set_free(link
->ndisc_captive_portals
);
2468 link
->ndisc_pref64
= set_free(link
->ndisc_pref64
);
2469 link
->ndisc_redirects
= set_free(link
->ndisc_redirects
);
2470 link
->ndisc_mtu
= 0;
2473 static const char* const ndisc_start_dhcp6_client_table
[_IPV6_ACCEPT_RA_START_DHCP6_CLIENT_MAX
] = {
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",
2479 DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING_WITH_BOOLEAN(ndisc_start_dhcp6_client
, IPv6AcceptRAStartDHCP6Client
, IPV6_ACCEPT_RA_START_DHCP6_CLIENT_YES
);
2481 DEFINE_CONFIG_PARSE_ENUM(config_parse_ndisc_use_domains
, dhcp_use_domains
, DHCPUseDomains
,
2482 "Failed to parse UseDomains= setting");
2483 DEFINE_CONFIG_PARSE_ENUM(config_parse_ndisc_start_dhcp6_client
, ndisc_start_dhcp6_client
, IPv6AcceptRAStartDHCP6Client
,
2484 "Failed to parse DHCPv6Client= setting");