diff --git a/man/systemd.network.xml b/man/systemd.network.xml index 2d3bb66e395..76a924cec73 100644 --- a/man/systemd.network.xml +++ b/man/systemd.network.xml @@ -2147,6 +2147,19 @@ Table=1234 + + RapidCommit= + + Takes a boolean. The DHCPv6 client can obtain configuration parameters from a DHCPv6 server + through a rapid two-message exchange (solicit and reply). When the rapid commit option is set by + both the DHCPv6 client and the DHCPv6 server, the two-message exchange is used. Otherwise, the + four-message exchange (solicit, advertise, request, and reply) is used. The two-message exchange + provides faster client configuration. See + RFC 3315 for details. + Defaults to true, and the two-message exchange will be used if the server support it. + + + diff --git a/src/libsystemd-network/dhcp6-internal.h b/src/libsystemd-network/dhcp6-internal.h index 176391ebecd..65f6cb057f4 100644 --- a/src/libsystemd-network/dhcp6-internal.h +++ b/src/libsystemd-network/dhcp6-internal.h @@ -71,6 +71,7 @@ struct sd_dhcp6_client { char **vendor_class; OrderedHashmap *extra_options; OrderedSet *vendor_options; + bool rapid_commit; struct sd_dhcp6_lease *lease; diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c index 4ca51591065..3de10cc1995 100644 --- a/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/libsystemd-network/sd-dhcp6-client.c @@ -491,6 +491,14 @@ int dhcp6_client_set_transaction_id(sd_dhcp6_client *client, uint32_t transactio return 0; } +int sd_dhcp6_client_set_rapid_commit(sd_dhcp6_client *client, int enable) { + assert_return(client, -EINVAL); + assert_return(!sd_dhcp6_client_is_running(client), -EBUSY); + + client->rapid_commit = enable; + return 0; +} + int sd_dhcp6_client_get_lease(sd_dhcp6_client *client, sd_dhcp6_lease **ret) { assert_return(client, -EINVAL); @@ -714,9 +722,11 @@ int dhcp6_client_send_message(sd_dhcp6_client *client) { break; case DHCP6_STATE_SOLICITATION: - r = dhcp6_option_append(&opt, &optlen, SD_DHCP6_OPTION_RAPID_COMMIT, 0, NULL); - if (r < 0) - return r; + if (client->rapid_commit) { + r = dhcp6_option_append(&opt, &optlen, SD_DHCP6_OPTION_RAPID_COMMIT, 0, NULL); + if (r < 0) + return r; + } r = client_append_common_options_in_managed_mode(client, &opt, &optlen, &client->ia_na, &client->ia_pd); @@ -1160,6 +1170,10 @@ static int client_process_advertise_or_rapid_commit_reply( if (message->type == DHCP6_MESSAGE_REPLY) { bool rapid_commit; + if (!client->rapid_commit) + return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), + "Received unexpected reply message, even we sent a solicit message without the rapid commit option, ignoring."); + r = dhcp6_lease_get_rapid_commit(lease, &rapid_commit); if (r < 0) return r; @@ -1467,6 +1481,7 @@ int sd_dhcp6_client_new(sd_dhcp6_client **ret) { .ifindex = -1, .request_ia = DHCP6_REQUEST_IA_NA | DHCP6_REQUEST_IA_PD, .fd = -1, + .rapid_commit = true, }; *ret = TAKE_PTR(client); diff --git a/src/network/networkd-dhcp6.c b/src/network/networkd-dhcp6.c index d6f2c2cca34..7f9f4e12b99 100644 --- a/src/network/networkd-dhcp6.c +++ b/src/network/networkd-dhcp6.c @@ -695,6 +695,12 @@ static int dhcp6_configure(Link *link) { return log_link_debug_errno(link, r, "DHCPv6 CLIENT: Failed to set prefix delegation hint: %m"); } + r = sd_dhcp6_client_set_rapid_commit(client, link->network->dhcp6_use_rapid_commit); + if (r < 0) + return log_link_debug_errno(link, r, + "DHCPv6 CLIENT: Failed to %s rapid commit: %m", + enable_disable(link->network->dhcp6_use_rapid_commit)); + link->dhcp6_client = TAKE_PTR(client); return 0; diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf index 13d521e37a2..a4f038681a4 100644 --- a/src/network/networkd-network-gperf.gperf +++ b/src/network/networkd-network-gperf.gperf @@ -262,6 +262,7 @@ DHCPv6.SendOption, config_parse_dhcp_send_option, DHCPv6.IAID, config_parse_iaid, AF_INET6, 0 DHCPv6.DUIDType, config_parse_duid_type, 0, offsetof(Network, dhcp6_duid) DHCPv6.DUIDRawData, config_parse_duid_rawdata, 0, offsetof(Network, dhcp6_duid) +DHCPv6.RapidCommit, config_parse_bool, 0, offsetof(Network, dhcp6_use_rapid_commit) IPv6AcceptRA.UseGateway, config_parse_bool, 0, offsetof(Network, ipv6_accept_ra_use_gateway) IPv6AcceptRA.UseRoutePrefix, config_parse_bool, 0, offsetof(Network, ipv6_accept_ra_use_route_prefix) IPv6AcceptRA.UseAutonomousPrefix, config_parse_bool, 0, offsetof(Network, ipv6_accept_ra_use_autonomous_prefix) @@ -555,12 +556,11 @@ DHCP.RouteMetric, config_parse_dhcp_or_ra_route_metri DHCP.RouteTable, config_parse_dhcp_or_ra_route_table, AF_INET, 0 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.RapidCommit, config_parse_bool, 0, offsetof(Network, dhcp6_use_rapid_commit) 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 DHCPv6PrefixDelegation.SubnetId, config_parse_dhcp_pd_subnet_id, 0, offsetof(Network, dhcp_pd_subnet_id) DHCPv6PrefixDelegation.Announce, config_parse_bool, 0, offsetof(Network, dhcp_pd_announce) diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c index e090a78023e..4faa43b3279 100644 --- a/src/network/networkd-network.c +++ b/src/network/networkd-network.c @@ -414,6 +414,7 @@ int network_load_one(Manager *manager, OrderedHashmap **networks, const char *fi .dhcp6_use_dns = true, .dhcp6_use_hostname = true, .dhcp6_use_ntp = true, + .dhcp6_use_rapid_commit = true, .dhcp6_duid.type = _DUID_TYPE_INVALID, .dhcp6_client_start_mode = _DHCP6_CLIENT_START_MODE_INVALID, diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h index c653124a9c6..3df7eee555e 100644 --- a/src/network/networkd-network.h +++ b/src/network/networkd-network.h @@ -164,6 +164,7 @@ struct Network { bool dhcp6_use_hostname; bool dhcp6_use_ntp; bool dhcp6_use_ntp_set; + bool dhcp6_use_rapid_commit; DHCPUseDomains dhcp6_use_domains; bool dhcp6_use_domains_set; uint32_t dhcp6_iaid; diff --git a/src/systemd/sd-dhcp6-client.h b/src/systemd/sd-dhcp6-client.h index 7fe60c356c8..2c66c51b78c 100644 --- a/src/systemd/sd-dhcp6-client.h +++ b/src/systemd/sd-dhcp6-client.h @@ -262,6 +262,7 @@ int sd_dhcp6_client_set_address_request(sd_dhcp6_client *client, int request); int sd_dhcp6_client_add_vendor_option(sd_dhcp6_client *client, sd_dhcp6_option *v); +int sd_dhcp6_client_set_rapid_commit(sd_dhcp6_client *client, int enable); int sd_dhcp6_client_get_lease( sd_dhcp6_client *client, diff --git a/test/test-network/systemd-networkd-tests.py b/test/test-network/systemd-networkd-tests.py index df8267c62c3..ccde067af93 100755 --- a/test/test-network/systemd-networkd-tests.py +++ b/test/test-network/systemd-networkd-tests.py @@ -4165,6 +4165,44 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities): print(output) self.assertRegex(output, 'token :: dev veth99') + print('## dnsmasq log') + output = read_dnsmasq_log_file() + print(output) + self.assertIn('DHCPSOLICIT(veth-peer)', output) + self.assertNotIn('DHCPADVERTISE(veth-peer)', output) + self.assertNotIn('DHCPREQUEST(veth-peer)', output) + self.assertIn('DHCPREPLY(veth-peer)', output) + self.assertIn('sent size: 0 option: 14 rapid-commit', output) + + with open(os.path.join(network_unit_dir, '25-dhcp-client-ipv6-only.network'), mode='a', encoding='utf-8') as f: + f.write('\n[DHCPv6]\nRapidCommit=no\n') + + stop_dnsmasq() + start_dnsmasq() + + networkctl_reload() + self.wait_online(['veth99:routable', 'veth-peer:routable']) + + # checking address + output = check_output('ip address show dev veth99 scope global') + print(output) + self.assertRegex(output, r'inet6 2600::[0-9a-f:]*/128 scope global dynamic noprefixroute') + self.assertNotIn('192.168.5', output) + + # checking semi-static route + output = check_output('ip -6 route list dev veth99 2001:1234:5:9fff:ff:ff:ff:ff') + print(output) + self.assertRegex(output, 'via fe80::1034:56ff:fe78:9abd') + + print('## dnsmasq log') + output = read_dnsmasq_log_file() + print(output) + self.assertIn('DHCPSOLICIT(veth-peer)', output) + self.assertIn('DHCPADVERTISE(veth-peer)', output) + self.assertIn('DHCPREQUEST(veth-peer)', output) + self.assertIn('DHCPREPLY(veth-peer)', output) + self.assertNotIn('rapid-commit', output) + def test_dhcp_client_ipv4_only(self): copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-ipv4-only.network')