diff --git a/man/systemd.network.xml b/man/systemd.network.xml index b6bf993adc..41a58b36eb 100644 --- a/man/systemd.network.xml +++ b/man/systemd.network.xml @@ -2089,28 +2089,16 @@ Table=1234 - - ForceDHCPv6PDOtherInformation= - - Takes a boolean that enforces DHCPv6 stateful mode when the 'Other information' bit is set in - Router Advertisement messages. By default setting only the 'O' bit in Router Advertisements - makes DHCPv6 request network information in a stateless manner using a two-message Information - Request and Information Reply message exchange. - RFC 7084, requirement WPD-4, updates - this behavior for a Customer Edge router so that stateful DHCPv6 Prefix Delegation is also - requested when only the 'O' bit is set in Router Advertisements. This option enables such a CE - behavior as it is impossible to automatically distinguish the intention of the 'O' bit otherwise. - By default this option is set to false, enable it if no prefixes are delegated when the device - should be acting as a CE router. - - - WithoutRA= - Allows DHCPv6 client to start without router advertisements's managed or other address - configuration flag. Takes one of solicit or - information-request. Defaults to unset. + Allows DHCPv6 client to start without router advertisements's managed or other + address configuration flag. Takes one of no, solicit + or information-request. When this is not specified and + UplinkInterface=:self is specified, then solicit is + implied. Otherwise, defaults to no, and the DHCPv6 client will be + started when an RA is received. See also DHCPv6Client= setting in the + [IPv6AcceptRA] section. @@ -2128,9 +2116,10 @@ Table=1234 Specifies the name or the index of the uplink interface, or one of the special values :self and :auto. When :self, the - interface itself is considered the uplink interface. When :auto, the first - link which acquired prefixes to be delegated from the DHCPv6 server is selected. Defaults to - :auto. + interface itself is considered the uplink interface, and + WithoutRA=solicit is implied if the setting is not explicitly specified. + When :auto, the first link which acquired prefixes to be delegated from + the DHCPv6 server is selected. Defaults to :auto. @@ -2382,11 +2371,13 @@ Token=prefixstable:2002:da8:1:: DHCPv6Client= - Takes a boolean, or the special value always. When true or - always, the DHCPv6 client will be started when the RA has the managed or - other information flag. If set to always, the DHCPv6 client will also be - started in managed mode when neither managed nor other information flag is set in the RA. - Defaults to true. + Takes a boolean, or the special value always. When true, the + DHCPv6 client will be started when the RA has the managed or other information flag. If set + to always, the DHCPv6 client will be started in managed mode when an RA + is received, even if neither managed nor other information flag is set in the RA. This will + be ignored when WithoutRA= in the [DHCPv6] section is enabled, or + UplinkInterface=:self in the [DHCPv6PrefixDelegation] section is + specified. Defaults to true. diff --git a/src/network/networkd-dhcp6.c b/src/network/networkd-dhcp6.c index 52ff5eb3aa..3cc457a02d 100644 --- a/src/network/networkd-dhcp6.c +++ b/src/network/networkd-dhcp6.c @@ -58,6 +58,25 @@ static int dhcp6_pd_resolve_uplink(Link *link, Link **ret) { return -ENOENT; } +static DHCP6ClientStartMode link_get_dhcp6_client_start_mode(Link *link) { + Link *uplink; + + assert(link); + + if (!link->network) + return DHCP6_CLIENT_START_MODE_NO; + + /* When WithoutRA= is explicitly specified, then honor it. */ + if (link->network->dhcp6_client_start_mode >= 0) + return link->network->dhcp6_client_start_mode; + + if (dhcp6_pd_resolve_uplink(link, &uplink) < 0) + return DHCP6_CLIENT_START_MODE_NO; + + /* When this interface itself is an uplink interface, then start dhcp6 client in managed mode */ + return uplink == link ? DHCP6_CLIENT_START_MODE_SOLICIT : DHCP6_CLIENT_START_MODE_NO; +} + static bool dhcp6_lease_has_pd_prefix(sd_dhcp6_lease *lease) { uint32_t lifetime_preferred_sec, lifetime_valid_sec; struct in6_addr pd_prefix; @@ -1311,40 +1330,31 @@ static void dhcp6_handler(sd_dhcp6_client *client, int event, void *userdata) { } } -int dhcp6_request_information(Link *link, int ir) { - int r, inf_req, pd; - bool running; +int dhcp6_start_on_ra(Link *link, bool information_request) { + int r; assert(link); assert(link->dhcp6_client); assert(link->network); assert(in6_addr_is_link_local(&link->ipv6ll_address)); + if (link_get_dhcp6_client_start_mode(link) != DHCP6_CLIENT_START_MODE_NO) + /* When WithoutRA= is specified, then the DHCPv6 client should be already runnging in + * the requested mode. Hence, ignore the requests by RA. */ + return 0; + r = sd_dhcp6_client_is_running(link->dhcp6_client); if (r < 0) return r; - running = r; - r = sd_dhcp6_client_get_prefix_delegation(link->dhcp6_client, &pd); - if (r < 0) - return r; + if (r > 0) { + int inf_req; - if (pd && ir && link->network->dhcp6_force_pd_other_information) { - log_link_debug(link, "Enabling managed mode to request DHCPv6 PD with 'Other Information' set"); - - r = sd_dhcp6_client_set_address_request(link->dhcp6_client, false); - if (r < 0) - return r; - - ir = false; - } - - if (running) { r = sd_dhcp6_client_get_information_request(link->dhcp6_client, &inf_req); if (r < 0) return r; - if (inf_req == ir) + if (inf_req == information_request) return 0; r = sd_dhcp6_client_stop(link->dhcp6_client); @@ -1356,7 +1366,7 @@ int dhcp6_request_information(Link *link, int ir) { return r; } - r = sd_dhcp6_client_set_information_request(link->dhcp6_client, ir); + r = sd_dhcp6_client_set_information_request(link->dhcp6_client, information_request); if (r < 0) return r; @@ -1368,9 +1378,11 @@ int dhcp6_request_information(Link *link, int ir) { } int dhcp6_start(Link *link) { + DHCP6ClientStartMode start_mode; int r; assert(link); + assert(link->network); if (!link->dhcp6_client) return 0; @@ -1381,7 +1393,7 @@ int dhcp6_start(Link *link) { if (!link_has_carrier(link)) return 0; - if (link->network->dhcp6_without_ra == DHCP6_CLIENT_START_MODE_NO) + if (sd_dhcp6_client_is_running(link->dhcp6_client) > 0) return 0; if (!in6_addr_is_link_local(&link->ipv6ll_address)) { @@ -1389,10 +1401,20 @@ int dhcp6_start(Link *link) { return 0; } - if (sd_dhcp6_client_is_running(link->dhcp6_client) > 0) + r = sd_dhcp6_client_set_local_address(link->dhcp6_client, &link->ipv6ll_address); + if (r < 0) + return r; + + start_mode = link_get_dhcp6_client_start_mode(link); + if (start_mode == DHCP6_CLIENT_START_MODE_NO) return 0; - r = dhcp6_request_information(link, link->network->dhcp6_without_ra == DHCP6_CLIENT_START_MODE_INFORMATION_REQUEST); + r = sd_dhcp6_client_set_information_request(link->dhcp6_client, + start_mode == DHCP6_CLIENT_START_MODE_INFORMATION_REQUEST); + if (r < 0) + return r; + + r = sd_dhcp6_client_start(link->dhcp6_client); if (r < 0) return r; diff --git a/src/network/networkd-dhcp6.h b/src/network/networkd-dhcp6.h index a08d03b614..4a522aebcf 100644 --- a/src/network/networkd-dhcp6.h +++ b/src/network/networkd-dhcp6.h @@ -21,7 +21,7 @@ bool link_dhcp6_pd_is_enabled(Link *link); int dhcp6_pd_remove(Link *link, bool only_marked); int dhcp6_update_mac(Link *link); int dhcp6_start(Link *link); -int dhcp6_request_information(Link *link, int ir); +int dhcp6_start_on_ra(Link *link, bool information_request); int dhcp6_request_prefix_delegation(Link *link); int request_process_dhcp6_client(Request *req); diff --git a/src/network/networkd-ndisc.c b/src/network/networkd-ndisc.c index d7b79f7416..5f831b58a4 100644 --- a/src/network/networkd-ndisc.c +++ b/src/network/networkd-ndisc.c @@ -906,13 +906,13 @@ static int ndisc_start_dhcp6_client(Link *link, sd_ndisc_router *rt) { /* (re)start DHCPv6 client in stateful or stateless mode according to RA flags. * Note, if both managed and other information bits are set, then ignore other * information bit. See RFC 4861. */ - r = dhcp6_request_information(link, !(flags & ND_RA_FLAG_MANAGED)); + r = dhcp6_start_on_ra(link, !(flags & ND_RA_FLAG_MANAGED)); break; } case IPV6_ACCEPT_RA_START_DHCP6_CLIENT_ALWAYS: /* When IPv6AcceptRA.DHCPv6Client=always, start dhcp6 client in managed mode * even if the router flags have neither M nor O flags. */ - r = dhcp6_request_information(link, false); + r = dhcp6_start_on_ra(link, /* information_request = */ false); break; default: diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf index ccbf02b99d..6631f5bc3e 100644 --- a/src/network/networkd-network-gperf.gperf +++ b/src/network/networkd-network-gperf.gperf @@ -248,9 +248,8 @@ DHCPv6.RequestOptions, config_parse_dhcp_request_options, DHCPv6.UserClass, config_parse_dhcp_user_or_vendor_class, AF_INET6, offsetof(Network, dhcp6_user_class) DHCPv6.VendorClass, config_parse_dhcp_user_or_vendor_class, AF_INET6, offsetof(Network, dhcp6_vendor_class) DHCPv6.SendVendorOption, config_parse_dhcp_send_option, AF_INET6, offsetof(Network, dhcp6_client_send_vendor_options) -DHCPv6.ForceDHCPv6PDOtherInformation, config_parse_bool, 0, offsetof(Network, dhcp6_force_pd_other_information) DHCPv6.PrefixDelegationHint, config_parse_dhcp6_pd_prefix_hint, 0, 0 -DHCPv6.WithoutRA, config_parse_dhcp6_client_start_mode, 0, offsetof(Network, dhcp6_without_ra) +DHCPv6.WithoutRA, config_parse_dhcp6_client_start_mode, 0, offsetof(Network, dhcp6_client_start_mode) DHCPv6.SendOption, config_parse_dhcp_send_option, AF_INET6, offsetof(Network, dhcp6_client_send_options) DHCPv6.IAID, config_parse_iaid, AF_INET6, 0 DHCPv6.DUIDType, config_parse_duid_type, 0, offsetof(Network, dhcp6_duid) @@ -530,11 +529,12 @@ DHCP.RouteTable, config_parse_dhcp_or_ra_route_table DHCP.UseTimezone, config_parse_bool, 0, offsetof(Network, dhcp_use_timezone) DHCP.ListenPort, config_parse_uint16, 0, offsetof(Network, dhcp_client_port) DHCP.RapidCommit, config_parse_warn_compat, DISABLED_LEGACY, 0 -DHCP.ForceDHCPv6PDOtherInformation, config_parse_bool, 0, offsetof(Network, dhcp6_force_pd_other_information) +DHCP.ForceDHCPv6PDOtherInformation, config_parse_warn_compat, DISABLED_LEGACY, 0 DHCPv4.UseDomainName, config_parse_dhcp_use_domains, AF_INET, 0 DHCPv4.CriticalConnection, config_parse_tristate, 0, offsetof(Network, dhcp_critical) DHCPv6.RouteMetric, config_parse_dhcp_or_ra_route_metric, AF_INET6, 0 DHCPv6.RapidCommit, config_parse_warn_compat, DISABLED_LEGACY, 0 +DHCPv6.ForceDHCPv6PDOtherInformation, config_parse_warn_compat, DISABLED_LEGACY, 0 IPv6AcceptRA.DenyList, config_parse_in_addr_prefixes, AF_INET6, offsetof(Network, ndisc_deny_listed_prefix) IPv6AcceptRA.BlackList, config_parse_in_addr_prefixes, AF_INET6, offsetof(Network, ndisc_deny_listed_prefix) TrafficControlQueueingDiscipline.Parent, config_parse_qdisc_parent, _QDISC_KIND_INVALID, 0 diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c index c3c792eb49..5469cb4560 100644 --- a/src/network/networkd-network.c +++ b/src/network/networkd-network.c @@ -405,6 +405,7 @@ int network_load_one(Manager *manager, OrderedHashmap **networks, const char *fi .dhcp6_use_hostname = true, .dhcp6_use_ntp = true, .dhcp6_duid.type = _DUID_TYPE_INVALID, + .dhcp6_client_start_mode = _DHCP6_CLIENT_START_MODE_INVALID, .dhcp6_pd = -1, .dhcp6_pd_announce = true, diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h index bd9758c899..d10119f58c 100644 --- a/src/network/networkd-network.h +++ b/src/network/networkd-network.h @@ -185,12 +185,10 @@ struct Network { char *dhcp6_mudurl; char **dhcp6_user_class; char **dhcp6_vendor_class; - DHCP6ClientStartMode dhcp6_without_ra; + DHCP6ClientStartMode dhcp6_client_start_mode; OrderedHashmap *dhcp6_client_send_options; OrderedHashmap *dhcp6_client_send_vendor_options; Set *dhcp6_request_options; - /* Start DHCPv6 PD also when 'O' RA flag is set, see RFC 7084, WPD-4 */ - bool dhcp6_force_pd_other_information; /* DHCP Server Support */ bool dhcp_server;