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 "string-table.h"
24 #include "string-util.h"
26 #include "sysctl-util.h"
28 #define NDISC_DNSSL_MAX 64U
29 #define NDISC_RDNSS_MAX 64U
30 /* Not defined in the RFC, but let's set an upper limit to make not consume much memory.
31 * This should be safe as typically there should be at most 1 portal per network. */
32 #define NDISC_CAPTIVE_PORTAL_MAX 64U
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
37 bool link_ipv6_accept_ra_enabled(Link
*link
) {
40 if (!socket_ipv6_is_supported())
43 if (link
->flags
& IFF_LOOPBACK
)
46 if (link
->iftype
== ARPHRD_CAN
)
52 if (!link_may_have_ipv6ll(link
, /* check_multicast = */ true))
55 assert(link
->network
->ipv6_accept_ra
>= 0);
56 return link
->network
->ipv6_accept_ra
;
59 void network_adjust_ipv6_accept_ra(Network
*network
) {
62 if (!FLAGS_SET(network
->link_local
, ADDRESS_FAMILY_IPV6
)) {
63 if (network
->ipv6_accept_ra
> 0)
64 log_warning("%s: IPv6AcceptRA= is enabled but IPv6 link-local addressing is disabled or not supported. "
65 "Disabling IPv6AcceptRA=.", network
->filename
);
66 network
->ipv6_accept_ra
= false;
69 if (network
->ipv6_accept_ra
< 0)
70 /* default to accept RA if ip_forward is disabled and ignore RA if ip_forward is enabled */
71 network
->ipv6_accept_ra
= !FLAGS_SET(network
->ip_forward
, ADDRESS_FAMILY_IPV6
);
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
);
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
);
83 static int ndisc_check_ready(Link
*link
);
85 static int ndisc_address_ready_callback(Address
*address
) {
89 assert(address
->link
);
91 SET_FOREACH(a
, address
->link
->addresses
)
92 if (a
->source
== NETWORK_CONFIG_SOURCE_NDISC
)
95 return ndisc_check_ready(address
->link
);
98 static int ndisc_check_ready(Link
*link
) {
99 bool found
= false, ready
= false;
104 if (link
->ndisc_messages
> 0) {
105 log_link_debug(link
, "%s(): SLAAC addresses and routes are not set.", __func__
);
109 SET_FOREACH(address
, link
->addresses
) {
110 if (address
->source
!= NETWORK_CONFIG_SOURCE_NDISC
)
115 if (address_is_ready(address
)) {
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
;
126 log_link_debug(link
, "%s(): no SLAAC address is ready.", __func__
);
130 link
->ndisc_configured
= true;
131 log_link_debug(link
, "SLAAC addresses and routes set.");
133 link_check_ready(link
);
137 static int ndisc_route_handler(sd_netlink
*rtnl
, sd_netlink_message
*m
, Request
*req
, Link
*link
, Route
*route
) {
142 r
= route_configure_handler_internal(rtnl
, m
, link
, route
, "Could not set NDisc route");
146 r
= ndisc_check_ready(link
);
148 link_enter_failed(link
);
153 static void ndisc_set_route_priority(Link
*link
, Route
*route
) {
157 if (route
->priority_set
)
158 return; /* explicitly configured. */
160 switch (route
->pref
) {
161 case SD_NDISC_PREFERENCE_LOW
:
162 route
->priority
= link
->network
->ipv6_accept_ra_route_metric_low
;
164 case SD_NDISC_PREFERENCE_MEDIUM
:
165 route
->priority
= link
->network
->ipv6_accept_ra_route_metric_medium
;
167 case SD_NDISC_PREFERENCE_HIGH
:
168 route
->priority
= link
->network
->ipv6_accept_ra_route_metric_high
;
171 assert_not_reached();
175 static int ndisc_request_route(Route
*route
, Link
*link
, sd_ndisc_router
*rt
) {
176 struct in6_addr router
;
177 uint8_t hop_limit
= 0;
184 assert(link
->network
);
187 r
= sd_ndisc_router_get_address(rt
, &router
);
191 if (link
->network
->ipv6_accept_ra_use_mtu
) {
192 r
= sd_ndisc_router_get_mtu(rt
, &mtu
);
193 if (r
< 0 && r
!= -ENODATA
)
194 return log_link_warning_errno(link
, r
, "Failed to get default router MTU from RA: %m");
197 if (link
->network
->ipv6_accept_ra_use_hop_limit
) {
198 r
= sd_ndisc_router_get_hop_limit(rt
, &hop_limit
);
199 if (r
< 0 && r
!= -ENODATA
)
200 return log_link_warning_errno(link
, r
, "Failed to get default router hop limit from RA: %m");
203 route
->source
= NETWORK_CONFIG_SOURCE_NDISC
;
204 route
->provider
.in6
= router
;
205 if (!route
->table_set
)
206 route
->table
= link_get_ipv6_accept_ra_route_table(link
);
207 ndisc_set_route_priority(link
, route
);
208 if (!route
->protocol_set
)
209 route
->protocol
= RTPROT_RA
;
210 r
= route_metric_set(&route
->metric
, RTAX_MTU
, mtu
);
213 r
= route_metric_set(&route
->metric
, RTAX_HOPLIMIT
, hop_limit
);
216 r
= route_metric_set(&route
->metric
, RTAX_QUICKACK
, link
->network
->ipv6_accept_ra_quickack
);
220 r
= route_adjust_nexthops(route
, link
);
224 is_new
= route_get(NULL
, link
, route
, NULL
) < 0;
226 r
= link_request_route(link
, route
, &link
->ndisc_messages
, ndisc_route_handler
);
230 link
->ndisc_configured
= false;
235 static int ndisc_address_handler(sd_netlink
*rtnl
, sd_netlink_message
*m
, Request
*req
, Link
*link
, Address
*address
) {
240 r
= address_configure_handler_internal(rtnl
, m
, link
, "Could not set NDisc address");
244 r
= ndisc_check_ready(link
);
246 link_enter_failed(link
);
251 static int ndisc_request_address(Address
*in
, Link
*link
, sd_ndisc_router
*rt
) {
252 _cleanup_(address_unrefp
) Address
*address
= in
;
253 struct in6_addr router
;
261 r
= sd_ndisc_router_get_address(rt
, &router
);
265 address
->source
= NETWORK_CONFIG_SOURCE_NDISC
;
266 address
->provider
.in6
= router
;
268 r
= free_and_strdup_warn(&address
->netlabel
, link
->network
->ndisc_netlabel
);
272 is_new
= address_get(link
, address
, NULL
) < 0;
274 r
= link_request_address(link
, address
, &link
->ndisc_messages
,
275 ndisc_address_handler
, NULL
);
279 link
->ndisc_configured
= false;
284 static int ndisc_router_process_default(Link
*link
, sd_ndisc_router
*rt
) {
285 usec_t lifetime_usec
;
286 struct in6_addr gateway
;
291 assert(link
->network
);
294 if (!link
->network
->ipv6_accept_ra_use_gateway
&&
295 hashmap_isempty(link
->network
->routes_by_section
))
298 r
= sd_ndisc_router_get_lifetime_timestamp(rt
, CLOCK_BOOTTIME
, &lifetime_usec
);
300 return log_link_warning_errno(link
, r
, "Failed to get gateway lifetime from RA: %m");
302 r
= sd_ndisc_router_get_address(rt
, &gateway
);
304 return log_link_warning_errno(link
, r
, "Failed to get gateway address from RA: %m");
306 if (link_get_ipv6_address(link
, &gateway
, 0, NULL
) >= 0) {
308 log_link_debug(link
, "No NDisc route added, gateway %s matches local address",
309 IN6_ADDR_TO_STRING(&gateway
));
313 r
= sd_ndisc_router_get_preference(rt
, &preference
);
315 return log_link_warning_errno(link
, r
, "Failed to get default router preference from RA: %m");
317 if (link
->network
->ipv6_accept_ra_use_gateway
) {
318 _cleanup_(route_freep
) Route
*route
= NULL
;
320 r
= route_new(&route
);
324 route
->family
= AF_INET6
;
325 route
->pref
= preference
;
326 route
->nexthop
.family
= AF_INET6
;
327 route
->nexthop
.gw
.in6
= gateway
;
328 route
->lifetime_usec
= lifetime_usec
;
330 r
= ndisc_request_route(route
, link
, rt
);
332 return log_link_warning_errno(link
, r
, "Could not request default route: %m");
336 HASHMAP_FOREACH(route_gw
, link
->network
->routes_by_section
) {
337 _cleanup_(route_freep
) Route
*route
= NULL
;
339 if (!route_gw
->gateway_from_dhcp_or_ra
)
342 if (route_gw
->nexthop
.family
!= AF_INET6
)
345 r
= route_dup(route_gw
, NULL
, &route
);
349 route
->nexthop
.gw
.in6
= gateway
;
350 if (!route
->pref_set
)
351 route
->pref
= preference
;
352 route
->lifetime_usec
= lifetime_usec
;
354 r
= ndisc_request_route(route
, link
, rt
);
356 return log_link_warning_errno(link
, r
, "Could not request gateway: %m");
362 static int ndisc_router_process_icmp6_ratelimit(Link
*link
, sd_ndisc_router
*rt
) {
363 usec_t icmp6_ratelimit
, msec
;
367 assert(link
->network
);
370 if (!link
->network
->ipv6_accept_ra_use_icmp6_ratelimit
)
373 r
= sd_ndisc_router_get_icmp6_ratelimit(rt
, &icmp6_ratelimit
);
375 log_link_debug(link
, "Failed to get ICMP6 ratelimit from RA, ignoring: %m");
379 /* We do not allow 0 here. */
380 if (!timestamp_is_set(icmp6_ratelimit
))
383 msec
= DIV_ROUND_UP(icmp6_ratelimit
, USEC_PER_MSEC
);
384 if (msec
<= 0 || msec
> INT_MAX
)
387 /* Limit the maximal rates for sending ICMPv6 packets. 0 to disable any limiting, otherwise the
388 * minimal space between responses in milliseconds. Default: 1000. */
389 r
= sysctl_write_ip_property_int(AF_INET6
, NULL
, "icmp/ratelimit", (int) msec
);
391 log_link_warning_errno(link
, r
, "Failed to apply ICMP6 ratelimit, ignoring: %m");
396 static int ndisc_router_process_retransmission_time(Link
*link
, sd_ndisc_router
*rt
) {
397 usec_t retrans_time
, msec
;
401 assert(link
->network
);
404 if (!link
->network
->ipv6_accept_ra_use_retransmission_time
)
407 r
= sd_ndisc_router_get_retransmission_time(rt
, &retrans_time
);
409 log_link_debug_errno(link
, r
, "Failed to get retransmission time from RA, ignoring: %m");
413 /* 0 is the unspecified value and must not be set (see RFC4861, 6.3.4) */
414 if (!timestamp_is_set(retrans_time
))
417 msec
= DIV_ROUND_UP(retrans_time
, USEC_PER_MSEC
);
418 if (msec
<= 0 || msec
> UINT32_MAX
) {
419 log_link_debug(link
, "Failed to get retransmission time from RA - out of range (%"PRIu64
"), ignoring", msec
);
423 /* Set the retransmission time for Neigbor Solicitations. */
424 r
= sysctl_write_ip_neighbor_property_uint32(AF_INET6
, link
->ifname
, "retrans_time_ms", (uint32_t) msec
);
426 log_link_warning_errno(
427 link
, r
, "Failed to apply neighbor retransmission time (%"PRIu64
"), ignoring: %m", msec
);
432 static int ndisc_router_process_autonomous_prefix(Link
*link
, sd_ndisc_router
*rt
) {
433 usec_t lifetime_valid_usec
, lifetime_preferred_usec
;
434 _cleanup_set_free_ Set
*addresses
= NULL
;
435 struct in6_addr prefix
, *a
;
440 assert(link
->network
);
443 if (!link
->network
->ipv6_accept_ra_use_autonomous_prefix
)
446 r
= sd_ndisc_router_prefix_get_address(rt
, &prefix
);
448 return log_link_warning_errno(link
, r
, "Failed to get prefix address: %m");
450 r
= sd_ndisc_router_prefix_get_prefixlen(rt
, &prefixlen
);
452 return log_link_warning_errno(link
, r
, "Failed to get prefix length: %m");
454 /* ndisc_generate_addresses() below requires the prefix length <= 64. */
455 if (prefixlen
> 64) {
456 log_link_debug(link
, "Prefix is longer than 64, ignoring autonomous prefix %s.",
457 IN6_ADDR_PREFIX_TO_STRING(&prefix
, prefixlen
));
461 r
= sd_ndisc_router_prefix_get_valid_lifetime_timestamp(rt
, CLOCK_BOOTTIME
, &lifetime_valid_usec
);
463 return log_link_warning_errno(link
, r
, "Failed to get prefix valid lifetime: %m");
465 r
= sd_ndisc_router_prefix_get_preferred_lifetime_timestamp(rt
, CLOCK_BOOTTIME
, &lifetime_preferred_usec
);
467 return log_link_warning_errno(link
, r
, "Failed to get prefix preferred lifetime: %m");
469 /* The preferred lifetime is never greater than the valid lifetime */
470 if (lifetime_preferred_usec
> lifetime_valid_usec
)
473 r
= ndisc_generate_addresses(link
, &prefix
, prefixlen
, &addresses
);
475 return log_link_warning_errno(link
, r
, "Failed to generate SLAAC addresses: %m");
477 SET_FOREACH(a
, addresses
) {
478 _cleanup_(address_unrefp
) Address
*address
= NULL
;
480 r
= address_new(&address
);
484 address
->family
= AF_INET6
;
485 address
->in_addr
.in6
= *a
;
486 address
->prefixlen
= prefixlen
;
487 address
->flags
= IFA_F_NOPREFIXROUTE
|IFA_F_MANAGETEMPADDR
;
488 address
->lifetime_valid_usec
= lifetime_valid_usec
;
489 address
->lifetime_preferred_usec
= lifetime_preferred_usec
;
491 r
= ndisc_request_address(TAKE_PTR(address
), link
, rt
);
493 return log_link_warning_errno(link
, r
, "Could not request SLAAC address: %m");
499 static int ndisc_router_process_onlink_prefix(Link
*link
, sd_ndisc_router
*rt
) {
500 _cleanup_(route_freep
) Route
*route
= NULL
;
501 unsigned prefixlen
, preference
;
502 usec_t lifetime_usec
;
503 struct in6_addr prefix
;
507 assert(link
->network
);
510 if (!link
->network
->ipv6_accept_ra_use_onlink_prefix
)
513 r
= sd_ndisc_router_prefix_get_valid_lifetime_timestamp(rt
, CLOCK_BOOTTIME
, &lifetime_usec
);
515 return log_link_warning_errno(link
, r
, "Failed to get prefix lifetime: %m");
517 r
= sd_ndisc_router_prefix_get_address(rt
, &prefix
);
519 return log_link_warning_errno(link
, r
, "Failed to get prefix address: %m");
521 r
= sd_ndisc_router_prefix_get_prefixlen(rt
, &prefixlen
);
523 return log_link_warning_errno(link
, r
, "Failed to get prefix length: %m");
525 /* Prefix Information option does not have preference, hence we use the 'main' preference here */
526 r
= sd_ndisc_router_get_preference(rt
, &preference
);
528 log_link_warning_errno(link
, r
, "Failed to get default router preference from RA: %m");
530 r
= route_new(&route
);
534 route
->family
= AF_INET6
;
535 route
->dst
.in6
= prefix
;
536 route
->dst_prefixlen
= prefixlen
;
537 route
->pref
= preference
;
538 route
->lifetime_usec
= lifetime_usec
;
540 r
= ndisc_request_route(route
, link
, rt
);
542 return log_link_warning_errno(link
, r
, "Could not request prefix route: %m");
547 static int ndisc_router_process_prefix(Link
*link
, sd_ndisc_router
*rt
) {
554 assert(link
->network
);
557 r
= sd_ndisc_router_prefix_get_address(rt
, &a
);
559 return log_link_warning_errno(link
, r
, "Failed to get prefix address: %m");
561 /* RFC 4861 Section 4.6.2:
562 * A router SHOULD NOT send a prefix option for the link-local prefix and a host SHOULD ignore such
563 * a prefix option. */
564 if (in6_addr_is_link_local(&a
)) {
565 log_link_debug(link
, "Received link-local prefix, ignoring autonomous prefix.");
569 r
= sd_ndisc_router_prefix_get_prefixlen(rt
, &prefixlen
);
571 return log_link_warning_errno(link
, r
, "Failed to get prefix length: %m");
573 if (in6_prefix_is_filtered(&a
, prefixlen
, link
->network
->ndisc_allow_listed_prefix
, link
->network
->ndisc_deny_listed_prefix
)) {
575 log_link_debug(link
, "Prefix '%s' is %s, ignoring",
576 !set_isempty(link
->network
->ndisc_allow_listed_prefix
) ? "not in allow list"
578 IN6_ADDR_PREFIX_TO_STRING(&a
, prefixlen
));
582 r
= sd_ndisc_router_prefix_get_flags(rt
, &flags
);
584 return log_link_warning_errno(link
, r
, "Failed to get RA prefix flags: %m");
586 if (FLAGS_SET(flags
, ND_OPT_PI_FLAG_ONLINK
)) {
587 r
= ndisc_router_process_onlink_prefix(link
, rt
);
592 if (FLAGS_SET(flags
, ND_OPT_PI_FLAG_AUTO
)) {
593 r
= ndisc_router_process_autonomous_prefix(link
, rt
);
601 static int ndisc_router_process_route(Link
*link
, sd_ndisc_router
*rt
) {
602 _cleanup_(route_freep
) Route
*route
= NULL
;
603 unsigned preference
, prefixlen
;
604 struct in6_addr gateway
, dst
;
605 usec_t lifetime_usec
;
610 if (!link
->network
->ipv6_accept_ra_use_route_prefix
)
613 r
= sd_ndisc_router_route_get_lifetime_timestamp(rt
, CLOCK_BOOTTIME
, &lifetime_usec
);
615 return log_link_warning_errno(link
, r
, "Failed to get route lifetime from RA: %m");
617 r
= sd_ndisc_router_route_get_address(rt
, &dst
);
619 return log_link_warning_errno(link
, r
, "Failed to get route destination address: %m");
621 r
= sd_ndisc_router_route_get_prefixlen(rt
, &prefixlen
);
623 return log_link_warning_errno(link
, r
, "Failed to get route prefix length: %m");
625 if (in6_addr_is_null(&dst
) && prefixlen
== 0) {
626 log_link_debug(link
, "Route prefix is ::/0, ignoring");
630 if (in6_prefix_is_filtered(&dst
, prefixlen
,
631 link
->network
->ndisc_allow_listed_route_prefix
,
632 link
->network
->ndisc_deny_listed_route_prefix
)) {
635 log_link_debug(link
, "Route prefix %s is %s, ignoring",
636 !set_isempty(link
->network
->ndisc_allow_listed_route_prefix
) ? "not in allow list"
638 IN6_ADDR_PREFIX_TO_STRING(&dst
, prefixlen
));
642 r
= sd_ndisc_router_get_address(rt
, &gateway
);
644 return log_link_warning_errno(link
, r
, "Failed to get gateway address from RA: %m");
646 if (link_get_ipv6_address(link
, &gateway
, 0, NULL
) >= 0) {
648 log_link_debug(link
, "Advertised route gateway %s is local to the link, ignoring route",
649 IN6_ADDR_TO_STRING(&gateway
));
653 r
= sd_ndisc_router_route_get_preference(rt
, &preference
);
654 if (r
== -EOPNOTSUPP
) {
655 log_link_debug_errno(link
, r
, "Received route prefix with unsupported preference, ignoring: %m");
659 return log_link_warning_errno(link
, r
, "Failed to get default router preference from RA: %m");
661 r
= route_new(&route
);
665 route
->family
= AF_INET6
;
666 route
->pref
= preference
;
667 route
->nexthop
.gw
.in6
= gateway
;
668 route
->nexthop
.family
= AF_INET6
;
669 route
->dst
.in6
= dst
;
670 route
->dst_prefixlen
= prefixlen
;
671 route
->lifetime_usec
= lifetime_usec
;
673 r
= ndisc_request_route(route
, link
, rt
);
675 return log_link_warning_errno(link
, r
, "Could not request additional route: %m");
680 static void ndisc_rdnss_hash_func(const NDiscRDNSS
*x
, struct siphash
*state
) {
681 siphash24_compress_typesafe(x
->address
, state
);
684 static int ndisc_rdnss_compare_func(const NDiscRDNSS
*a
, const NDiscRDNSS
*b
) {
685 return memcmp(&a
->address
, &b
->address
, sizeof(a
->address
));
688 DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(
689 ndisc_rdnss_hash_ops
,
691 ndisc_rdnss_hash_func
,
692 ndisc_rdnss_compare_func
,
695 static int ndisc_router_process_rdnss(Link
*link
, sd_ndisc_router
*rt
) {
696 usec_t lifetime_usec
;
697 const struct in6_addr
*a
;
698 struct in6_addr router
;
699 bool updated
= false, logged_about_too_many
= false;
703 assert(link
->network
);
706 if (!link
->network
->ipv6_accept_ra_use_dns
)
709 r
= sd_ndisc_router_get_address(rt
, &router
);
711 return log_link_warning_errno(link
, r
, "Failed to get router address from RA: %m");
713 r
= sd_ndisc_router_rdnss_get_lifetime_timestamp(rt
, CLOCK_BOOTTIME
, &lifetime_usec
);
715 return log_link_warning_errno(link
, r
, "Failed to get RDNSS lifetime: %m");
717 n
= sd_ndisc_router_rdnss_get_addresses(rt
, &a
);
719 return log_link_warning_errno(link
, n
, "Failed to get RDNSS addresses: %m");
721 for (int j
= 0; j
< n
; j
++) {
722 _cleanup_free_ NDiscRDNSS
*x
= NULL
;
723 NDiscRDNSS
*rdnss
, d
= {
727 if (lifetime_usec
== 0) {
728 /* The entry is outdated. */
729 free(set_remove(link
->ndisc_rdnss
, &d
));
734 rdnss
= set_get(link
->ndisc_rdnss
, &d
);
736 rdnss
->router
= router
;
737 rdnss
->lifetime_usec
= lifetime_usec
;
741 if (set_size(link
->ndisc_rdnss
) >= NDISC_RDNSS_MAX
) {
742 if (!logged_about_too_many
)
743 log_link_warning(link
, "Too many RDNSS records per link. Only first %u records will be used.", NDISC_RDNSS_MAX
);
744 logged_about_too_many
= true;
748 x
= new(NDiscRDNSS
, 1);
755 .lifetime_usec
= lifetime_usec
,
758 r
= set_ensure_consume(&link
->ndisc_rdnss
, &ndisc_rdnss_hash_ops
, TAKE_PTR(x
));
772 static void ndisc_dnssl_hash_func(const NDiscDNSSL
*x
, struct siphash
*state
) {
773 siphash24_compress_string(NDISC_DNSSL_DOMAIN(x
), state
);
776 static int ndisc_dnssl_compare_func(const NDiscDNSSL
*a
, const NDiscDNSSL
*b
) {
777 return strcmp(NDISC_DNSSL_DOMAIN(a
), NDISC_DNSSL_DOMAIN(b
));
780 DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(
781 ndisc_dnssl_hash_ops
,
783 ndisc_dnssl_hash_func
,
784 ndisc_dnssl_compare_func
,
787 static int ndisc_router_process_dnssl(Link
*link
, sd_ndisc_router
*rt
) {
788 _cleanup_strv_free_
char **l
= NULL
;
789 usec_t lifetime_usec
;
790 struct in6_addr router
;
791 bool updated
= false, logged_about_too_many
= false;
795 assert(link
->network
);
798 if (link
->network
->ipv6_accept_ra_use_domains
== DHCP_USE_DOMAINS_NO
)
801 r
= sd_ndisc_router_get_address(rt
, &router
);
803 return log_link_warning_errno(link
, r
, "Failed to get router address from RA: %m");
805 r
= sd_ndisc_router_dnssl_get_lifetime_timestamp(rt
, CLOCK_BOOTTIME
, &lifetime_usec
);
807 return log_link_warning_errno(link
, r
, "Failed to get DNSSL lifetime: %m");
809 r
= sd_ndisc_router_dnssl_get_domains(rt
, &l
);
811 return log_link_warning_errno(link
, r
, "Failed to get DNSSL addresses: %m");
814 _cleanup_free_ NDiscDNSSL
*s
= NULL
;
817 s
= malloc0(ALIGN(sizeof(NDiscDNSSL
)) + strlen(*j
) + 1);
821 strcpy(NDISC_DNSSL_DOMAIN(s
), *j
);
823 if (lifetime_usec
== 0) {
824 /* The entry is outdated. */
825 free(set_remove(link
->ndisc_dnssl
, s
));
830 dnssl
= set_get(link
->ndisc_dnssl
, s
);
832 dnssl
->router
= router
;
833 dnssl
->lifetime_usec
= lifetime_usec
;
837 if (set_size(link
->ndisc_dnssl
) >= NDISC_DNSSL_MAX
) {
838 if (!logged_about_too_many
)
839 log_link_warning(link
, "Too many DNSSL records per link. Only first %u records will be used.", NDISC_DNSSL_MAX
);
840 logged_about_too_many
= true;
845 s
->lifetime_usec
= lifetime_usec
;
847 r
= set_ensure_consume(&link
->ndisc_dnssl
, &ndisc_dnssl_hash_ops
, TAKE_PTR(s
));
861 static NDiscCaptivePortal
* ndisc_captive_portal_free(NDiscCaptivePortal
*x
) {
865 free(x
->captive_portal
);
869 DEFINE_TRIVIAL_CLEANUP_FUNC(NDiscCaptivePortal
*, ndisc_captive_portal_free
);
871 static void ndisc_captive_portal_hash_func(const NDiscCaptivePortal
*x
, struct siphash
*state
) {
873 siphash24_compress_string(x
->captive_portal
, state
);
876 static int ndisc_captive_portal_compare_func(const NDiscCaptivePortal
*a
, const NDiscCaptivePortal
*b
) {
879 return strcmp_ptr(a
->captive_portal
, b
->captive_portal
);
882 DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(
883 ndisc_captive_portal_hash_ops
,
885 ndisc_captive_portal_hash_func
,
886 ndisc_captive_portal_compare_func
,
887 ndisc_captive_portal_free
);
889 static int ndisc_router_process_captive_portal(Link
*link
, sd_ndisc_router
*rt
) {
890 _cleanup_(ndisc_captive_portal_freep
) NDiscCaptivePortal
*new_entry
= NULL
;
891 _cleanup_free_
char *captive_portal
= NULL
;
892 usec_t lifetime_usec
;
893 NDiscCaptivePortal
*exist
;
894 struct in6_addr router
;
900 assert(link
->network
);
903 if (!link
->network
->ipv6_accept_ra_use_captive_portal
)
906 r
= sd_ndisc_router_get_address(rt
, &router
);
908 return log_link_warning_errno(link
, r
, "Failed to get router address from RA: %m");
910 /* RFC 4861 section 4.2. states that the lifetime in the message header should be used only for the
911 * default gateway, but the captive portal option does not have a lifetime field, hence, we use the
912 * main lifetime for the portal. */
913 r
= sd_ndisc_router_get_lifetime_timestamp(rt
, CLOCK_BOOTTIME
, &lifetime_usec
);
915 return log_link_warning_errno(link
, r
, "Failed to get lifetime of RA message: %m");
917 r
= sd_ndisc_router_captive_portal_get_uri(rt
, &uri
, &len
);
919 return log_link_warning_errno(link
, r
, "Failed to get captive portal from RA: %m");
922 return log_link_warning_errno(link
, SYNTHETIC_ERRNO(EBADMSG
), "Received empty captive portal, ignoring.");
924 r
= make_cstring(uri
, len
, MAKE_CSTRING_REFUSE_TRAILING_NUL
, &captive_portal
);
926 return log_link_warning_errno(link
, r
, "Failed to convert captive portal URI: %m");
928 if (!in_charset(captive_portal
, URI_VALID
))
929 return log_link_warning_errno(link
, SYNTHETIC_ERRNO(EBADMSG
), "Received invalid captive portal, ignoring.");
931 if (lifetime_usec
== 0) {
932 /* Drop the portal with zero lifetime. */
933 ndisc_captive_portal_free(set_remove(link
->ndisc_captive_portals
,
934 &(NDiscCaptivePortal
) {
935 .captive_portal
= captive_portal
,
940 exist
= set_get(link
->ndisc_captive_portals
,
941 &(NDiscCaptivePortal
) {
942 .captive_portal
= captive_portal
,
945 /* update existing entry */
946 exist
->router
= router
;
947 exist
->lifetime_usec
= lifetime_usec
;
951 if (set_size(link
->ndisc_captive_portals
) >= NDISC_CAPTIVE_PORTAL_MAX
) {
952 NDiscCaptivePortal
*c
, *target
= NULL
;
954 /* Find the portal who has the minimal lifetime and drop it to store new one. */
955 SET_FOREACH(c
, link
->ndisc_captive_portals
)
956 if (!target
|| c
->lifetime_usec
< target
->lifetime_usec
)
960 assert(set_remove(link
->ndisc_captive_portals
, target
) == target
);
961 ndisc_captive_portal_free(target
);
964 new_entry
= new(NDiscCaptivePortal
, 1);
968 *new_entry
= (NDiscCaptivePortal
) {
970 .lifetime_usec
= lifetime_usec
,
971 .captive_portal
= TAKE_PTR(captive_portal
),
974 r
= set_ensure_put(&link
->ndisc_captive_portals
, &ndisc_captive_portal_hash_ops
, new_entry
);
984 static void ndisc_pref64_hash_func(const NDiscPREF64
*x
, struct siphash
*state
) {
987 siphash24_compress_typesafe(x
->prefix_len
, state
);
988 siphash24_compress_typesafe(x
->prefix
, state
);
991 static int ndisc_pref64_compare_func(const NDiscPREF64
*a
, const NDiscPREF64
*b
) {
997 r
= CMP(a
->prefix_len
, b
->prefix_len
);
1001 return memcmp(&a
->prefix
, &b
->prefix
, sizeof(a
->prefix
));
1004 DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(
1005 ndisc_pref64_hash_ops
,
1007 ndisc_pref64_hash_func
,
1008 ndisc_pref64_compare_func
,
1011 static int ndisc_router_process_pref64(Link
*link
, sd_ndisc_router
*rt
) {
1012 _cleanup_free_ NDiscPREF64
*new_entry
= NULL
;
1013 usec_t lifetime_usec
;
1014 struct in6_addr a
, router
;
1015 unsigned prefix_len
;
1020 assert(link
->network
);
1023 if (!link
->network
->ipv6_accept_ra_use_pref64
)
1026 r
= sd_ndisc_router_get_address(rt
, &router
);
1028 return log_link_warning_errno(link
, r
, "Failed to get router address from RA: %m");
1030 r
= sd_ndisc_router_prefix64_get_prefix(rt
, &a
);
1032 return log_link_warning_errno(link
, r
, "Failed to get pref64 prefix: %m");
1034 r
= sd_ndisc_router_prefix64_get_prefixlen(rt
, &prefix_len
);
1036 return log_link_warning_errno(link
, r
, "Failed to get pref64 prefix length: %m");
1038 r
= sd_ndisc_router_prefix64_get_lifetime_timestamp(rt
, CLOCK_BOOTTIME
, &lifetime_usec
);
1040 return log_link_warning_errno(link
, r
, "Failed to get pref64 prefix lifetime: %m");
1042 if (lifetime_usec
== 0) {
1043 free(set_remove(link
->ndisc_pref64
,
1046 .prefix_len
= prefix_len
1051 exist
= set_get(link
->ndisc_pref64
,
1054 .prefix_len
= prefix_len
1057 /* update existing entry */
1058 exist
->router
= router
;
1059 exist
->lifetime_usec
= lifetime_usec
;
1063 if (set_size(link
->ndisc_pref64
) >= NDISC_PREF64_MAX
) {
1064 log_link_debug(link
, "Too many PREF64 records received. Only first %u records will be used.", NDISC_PREF64_MAX
);
1068 new_entry
= new(NDiscPREF64
, 1);
1072 *new_entry
= (NDiscPREF64
) {
1074 .lifetime_usec
= lifetime_usec
,
1076 .prefix_len
= prefix_len
,
1079 r
= set_ensure_put(&link
->ndisc_pref64
, &ndisc_pref64_hash_ops
, new_entry
);
1084 TAKE_PTR(new_entry
);
1089 static int ndisc_router_process_options(Link
*link
, sd_ndisc_router
*rt
) {
1090 size_t n_captive_portal
= 0;
1094 assert(link
->network
);
1097 for (r
= sd_ndisc_router_option_rewind(rt
); ; r
= sd_ndisc_router_option_next(rt
)) {
1101 return log_link_warning_errno(link
, r
, "Failed to iterate through options: %m");
1102 if (r
== 0) /* EOF */
1105 r
= sd_ndisc_router_option_get_type(rt
, &type
);
1107 return log_link_warning_errno(link
, r
, "Failed to get RA option type: %m");
1110 case SD_NDISC_OPTION_PREFIX_INFORMATION
:
1111 r
= ndisc_router_process_prefix(link
, rt
);
1114 case SD_NDISC_OPTION_ROUTE_INFORMATION
:
1115 r
= ndisc_router_process_route(link
, rt
);
1118 case SD_NDISC_OPTION_RDNSS
:
1119 r
= ndisc_router_process_rdnss(link
, rt
);
1122 case SD_NDISC_OPTION_DNSSL
:
1123 r
= ndisc_router_process_dnssl(link
, rt
);
1125 case SD_NDISC_OPTION_CAPTIVE_PORTAL
:
1126 if (n_captive_portal
> 0) {
1127 if (n_captive_portal
== 1)
1128 log_link_notice(link
, "Received RA with multiple captive portals, only using the first one.");
1133 r
= ndisc_router_process_captive_portal(link
, rt
);
1137 case SD_NDISC_OPTION_PREF64
:
1138 r
= ndisc_router_process_pref64(link
, rt
);
1141 if (r
< 0 && r
!= -EBADMSG
)
1146 static int ndisc_drop_outdated(Link
*link
, usec_t timestamp_usec
) {
1147 bool updated
= false;
1150 NDiscCaptivePortal
*cp
;
1158 /* If an address or friends is already assigned, but not valid anymore, then refuse to update it,
1159 * and let's immediately remove it.
1160 * See RFC4862, section 5.5.3.e. But the following logic is deviated from RFC4862 by honoring all
1161 * valid lifetimes to improve the reaction of SLAAC to renumbering events.
1162 * See draft-ietf-6man-slaac-renum-02, section 4.2. */
1164 SET_FOREACH(route
, link
->routes
) {
1165 if (route
->source
!= NETWORK_CONFIG_SOURCE_NDISC
)
1168 if (route
->lifetime_usec
>= timestamp_usec
)
1169 continue; /* the route is still valid */
1171 r
= route_remove_and_drop(route
);
1173 RET_GATHER(ret
, log_link_warning_errno(link
, r
, "Failed to remove outdated SLAAC route, ignoring: %m"));
1176 SET_FOREACH(address
, link
->addresses
) {
1177 if (address
->source
!= NETWORK_CONFIG_SOURCE_NDISC
)
1180 if (address
->lifetime_valid_usec
>= timestamp_usec
)
1181 continue; /* the address is still valid */
1183 r
= address_remove_and_cancel(address
, link
);
1185 RET_GATHER(ret
, log_link_warning_errno(link
, r
, "Failed to remove outdated SLAAC address, ignoring: %m"));
1188 SET_FOREACH(rdnss
, link
->ndisc_rdnss
) {
1189 if (rdnss
->lifetime_usec
>= timestamp_usec
)
1190 continue; /* the DNS server is still valid */
1192 free(set_remove(link
->ndisc_rdnss
, rdnss
));
1196 SET_FOREACH(dnssl
, link
->ndisc_dnssl
) {
1197 if (dnssl
->lifetime_usec
>= timestamp_usec
)
1198 continue; /* the DNS domain is still valid */
1200 free(set_remove(link
->ndisc_dnssl
, dnssl
));
1204 SET_FOREACH(cp
, link
->ndisc_captive_portals
) {
1205 if (cp
->lifetime_usec
>= timestamp_usec
)
1206 continue; /* the captive portal is still valid */
1208 ndisc_captive_portal_free(set_remove(link
->ndisc_captive_portals
, cp
));
1212 SET_FOREACH(p64
, link
->ndisc_pref64
) {
1213 if (p64
->lifetime_usec
>= timestamp_usec
)
1214 continue; /* the pref64 prefix is still valid */
1216 free(set_remove(link
->ndisc_pref64
, p64
));
1217 /* The pref64 prefix is not exported through the state file, hence it is not necessary to set
1218 * the 'updated' flag. */
1227 static int ndisc_setup_expire(Link
*link
);
1229 static int ndisc_expire_handler(sd_event_source
*s
, uint64_t usec
, void *userdata
) {
1230 Link
*link
= ASSERT_PTR(userdata
);
1233 assert(link
->manager
);
1235 assert_se(sd_event_now(link
->manager
->event
, CLOCK_BOOTTIME
, &now_usec
) >= 0);
1237 (void) ndisc_drop_outdated(link
, now_usec
);
1238 (void) ndisc_setup_expire(link
);
1242 static int ndisc_setup_expire(Link
*link
) {
1243 usec_t lifetime_usec
= USEC_INFINITY
;
1244 NDiscCaptivePortal
*cp
;
1253 assert(link
->manager
);
1255 SET_FOREACH(route
, link
->routes
) {
1256 if (route
->source
!= NETWORK_CONFIG_SOURCE_NDISC
)
1259 if (!route_exists(route
))
1262 lifetime_usec
= MIN(lifetime_usec
, route
->lifetime_usec
);
1265 SET_FOREACH(address
, link
->addresses
) {
1266 if (address
->source
!= NETWORK_CONFIG_SOURCE_NDISC
)
1269 if (!address_exists(address
))
1272 lifetime_usec
= MIN(lifetime_usec
, address
->lifetime_valid_usec
);
1275 SET_FOREACH(rdnss
, link
->ndisc_rdnss
)
1276 lifetime_usec
= MIN(lifetime_usec
, rdnss
->lifetime_usec
);
1278 SET_FOREACH(dnssl
, link
->ndisc_dnssl
)
1279 lifetime_usec
= MIN(lifetime_usec
, dnssl
->lifetime_usec
);
1281 SET_FOREACH(cp
, link
->ndisc_captive_portals
)
1282 lifetime_usec
= MIN(lifetime_usec
, cp
->lifetime_usec
);
1284 SET_FOREACH(p64
, link
->ndisc_pref64
)
1285 lifetime_usec
= MIN(lifetime_usec
, p64
->lifetime_usec
);
1287 if (lifetime_usec
== USEC_INFINITY
)
1290 r
= event_reset_time(link
->manager
->event
, &link
->ndisc_expire
, CLOCK_BOOTTIME
,
1291 lifetime_usec
, 0, ndisc_expire_handler
, link
, 0, "ndisc-expiration", true);
1293 return log_link_warning_errno(link
, r
, "Failed to update expiration timer for ndisc: %m");
1298 static int ndisc_start_dhcp6_client(Link
*link
, sd_ndisc_router
*rt
) {
1302 assert(link
->network
);
1304 switch (link
->network
->ipv6_accept_ra_start_dhcp6_client
) {
1305 case IPV6_ACCEPT_RA_START_DHCP6_CLIENT_NO
:
1308 case IPV6_ACCEPT_RA_START_DHCP6_CLIENT_YES
: {
1311 r
= sd_ndisc_router_get_flags(rt
, &flags
);
1313 return log_link_warning_errno(link
, r
, "Failed to get RA flags: %m");
1315 if ((flags
& (ND_RA_FLAG_MANAGED
| ND_RA_FLAG_OTHER
)) == 0)
1318 /* (re)start DHCPv6 client in stateful or stateless mode according to RA flags.
1319 * Note, if both "managed" and "other configuration" bits are set, then ignore
1320 * "other configuration" bit. See RFC 4861. */
1321 r
= dhcp6_start_on_ra(link
, !(flags
& ND_RA_FLAG_MANAGED
));
1324 case IPV6_ACCEPT_RA_START_DHCP6_CLIENT_ALWAYS
:
1325 /* When IPv6AcceptRA.DHCPv6Client=always, start dhcp6 client in solicit mode
1326 * even if the router flags have neither M nor O flags. */
1327 r
= dhcp6_start_on_ra(link
, /* information_request = */ false);
1331 assert_not_reached();
1335 return log_link_warning_errno(link
, r
, "Could not acquire DHCPv6 lease on NDisc request: %m");
1337 log_link_debug(link
, "Acquiring DHCPv6 lease on NDisc request");
1341 static int ndisc_router_handler(Link
*link
, sd_ndisc_router
*rt
) {
1342 struct in6_addr router
;
1343 usec_t timestamp_usec
;
1347 assert(link
->network
);
1348 assert(link
->manager
);
1351 r
= sd_ndisc_router_get_address(rt
, &router
);
1352 if (r
== -ENODATA
) {
1353 log_link_debug(link
, "Received RA without router address, ignoring.");
1357 return log_link_warning_errno(link
, r
, "Failed to get router address from RA: %m");
1359 if (in6_prefix_is_filtered(&router
, 128, link
->network
->ndisc_allow_listed_router
, link
->network
->ndisc_deny_listed_router
)) {
1360 if (DEBUG_LOGGING
) {
1361 if (!set_isempty(link
->network
->ndisc_allow_listed_router
))
1362 log_link_debug(link
, "Router %s is not in allow list, ignoring.", IN6_ADDR_TO_STRING(&router
));
1364 log_link_debug(link
, "Router %s is in deny list, ignoring.", IN6_ADDR_TO_STRING(&router
));
1369 r
= sd_ndisc_router_get_timestamp(rt
, CLOCK_BOOTTIME
, ×tamp_usec
);
1370 if (r
== -ENODATA
) {
1371 log_link_debug(link
, "Received RA without timestamp, ignoring.");
1377 r
= ndisc_drop_outdated(link
, timestamp_usec
);
1381 r
= ndisc_start_dhcp6_client(link
, rt
);
1385 r
= ndisc_router_process_default(link
, rt
);
1389 r
= ndisc_router_process_icmp6_ratelimit(link
, rt
);
1393 r
= ndisc_router_process_retransmission_time(link
, rt
);
1397 r
= ndisc_router_process_options(link
, rt
);
1401 r
= ndisc_setup_expire(link
);
1405 if (link
->ndisc_messages
== 0)
1406 link
->ndisc_configured
= true;
1408 log_link_debug(link
, "Setting SLAAC addresses and router.");
1410 if (!link
->ndisc_configured
)
1411 link_set_state(link
, LINK_STATE_CONFIGURING
);
1413 link_check_ready(link
);
1417 static void ndisc_handler(sd_ndisc
*nd
, sd_ndisc_event_t event
, sd_ndisc_router
*rt
, void *userdata
) {
1418 Link
*link
= ASSERT_PTR(userdata
);
1421 if (IN_SET(link
->state
, LINK_STATE_FAILED
, LINK_STATE_LINGER
))
1426 case SD_NDISC_EVENT_ROUTER
:
1427 r
= ndisc_router_handler(link
, rt
);
1428 if (r
< 0 && r
!= -EBADMSG
) {
1429 link_enter_failed(link
);
1434 case SD_NDISC_EVENT_TIMEOUT
:
1435 log_link_debug(link
, "NDisc handler get timeout event");
1436 if (link
->ndisc_messages
== 0) {
1437 link
->ndisc_configured
= true;
1438 link_check_ready(link
);
1442 assert_not_reached();
1446 static int ndisc_configure(Link
*link
) {
1451 if (!link_ipv6_accept_ra_enabled(link
))
1455 return -EBUSY
; /* Already configured. */
1457 r
= sd_ndisc_new(&link
->ndisc
);
1461 r
= sd_ndisc_attach_event(link
->ndisc
, link
->manager
->event
, 0);
1465 if (link
->hw_addr
.length
== ETH_ALEN
) {
1466 r
= sd_ndisc_set_mac(link
->ndisc
, &link
->hw_addr
.ether
);
1471 r
= sd_ndisc_set_ifindex(link
->ndisc
, link
->ifindex
);
1475 r
= sd_ndisc_set_callback(link
->ndisc
, ndisc_handler
, link
);
1482 int ndisc_start(Link
*link
) {
1487 if (!link
->ndisc
|| !link
->dhcp6_client
)
1490 if (!link_has_carrier(link
))
1493 if (in6_addr_is_null(&link
->ipv6ll_address
))
1496 log_link_debug(link
, "Discovering IPv6 routers");
1498 r
= sd_ndisc_start(link
->ndisc
);
1505 static int ndisc_process_request(Request
*req
, Link
*link
, void *userdata
) {
1510 if (!link_is_ready_to_configure(link
, /* allow_unmanaged = */ false))
1513 r
= ndisc_configure(link
);
1515 return log_link_warning_errno(link
, r
, "Failed to configure IPv6 Router Discovery: %m");
1517 r
= ndisc_start(link
);
1519 return log_link_warning_errno(link
, r
, "Failed to start IPv6 Router Discovery: %m");
1521 log_link_debug(link
, "IPv6 Router Discovery is configured%s.",
1522 r
> 0 ? " and started" : "");
1526 int link_request_ndisc(Link
*link
) {
1531 if (!link_ipv6_accept_ra_enabled(link
))
1537 r
= link_queue_request(link
, REQUEST_TYPE_NDISC
, ndisc_process_request
, NULL
);
1539 return log_link_warning_errno(link
, r
, "Failed to request configuring of the IPv6 Router Discovery: %m");
1541 log_link_debug(link
, "Requested configuring of the IPv6 Router Discovery.");
1545 int ndisc_stop(Link
*link
) {
1548 link
->ndisc_expire
= sd_event_source_disable_unref(link
->ndisc_expire
);
1550 return sd_ndisc_stop(link
->ndisc
);
1554 void ndisc_flush(Link
*link
) {
1557 /* Remove all RDNSS, DNSSL, and Captive Portal entries, without exception. */
1559 link
->ndisc_rdnss
= set_free(link
->ndisc_rdnss
);
1560 link
->ndisc_dnssl
= set_free(link
->ndisc_dnssl
);
1561 link
->ndisc_captive_portals
= set_free(link
->ndisc_captive_portals
);
1562 link
->ndisc_pref64
= set_free(link
->ndisc_pref64
);
1565 static const char* const ipv6_accept_ra_start_dhcp6_client_table
[_IPV6_ACCEPT_RA_START_DHCP6_CLIENT_MAX
] = {
1566 [IPV6_ACCEPT_RA_START_DHCP6_CLIENT_NO
] = "no",
1567 [IPV6_ACCEPT_RA_START_DHCP6_CLIENT_ALWAYS
] = "always",
1568 [IPV6_ACCEPT_RA_START_DHCP6_CLIENT_YES
] = "yes",
1571 DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING_WITH_BOOLEAN(ipv6_accept_ra_start_dhcp6_client
, IPv6AcceptRAStartDHCP6Client
, IPV6_ACCEPT_RA_START_DHCP6_CLIENT_YES
);
1573 DEFINE_CONFIG_PARSE_ENUM(config_parse_ipv6_accept_ra_use_domains
, dhcp_use_domains
, DHCPUseDomains
,
1574 "Failed to parse UseDomains= setting");
1575 DEFINE_CONFIG_PARSE_ENUM(config_parse_ipv6_accept_ra_start_dhcp6_client
, ipv6_accept_ra_start_dhcp6_client
, IPv6AcceptRAStartDHCP6Client
,
1576 "Failed to parse DHCPv6Client= setting");