From: Ronan Pigott Date: Tue, 24 Oct 2023 18:01:32 +0000 (-0700) Subject: network: implement RFC4039 DHCP Rapid Commit X-Git-Tag: v255-rc1~112^2~1 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=808b65a08729caa268efd57c478285ee4912d5a3;p=thirdparty%2Fsystemd.git network: implement RFC4039 DHCP Rapid Commit This implements the DHCPv4 equivalent of the DHCPv6 Rapid Commit option, enabling a lease to be selected in an accelerated 2-message exchange instead of the typical 4-message exchange. --- diff --git a/NEWS b/NEWS index 9767421f59e..62cc4698dc7 100644 --- a/NEWS +++ b/NEWS @@ -73,6 +73,11 @@ CHANGES WITH 255 in spe: already use 'prefixstable' addresses with wireless networks, the stable address chosen will be changed by the update. + * The DHCPv4 client gained a RapidCommit option, default true, which + enables RFC4039 Rapid Commit behavior to obtain a lease in a + simplified 2-message exchange instead of the typical 4-message + exchange if also supported by the DHCP server. + Changes in systemd-analyze: * "systemd-analyze plot" has gained tooltips on each unit name with diff --git a/man/systemd.network.xml b/man/systemd.network.xml index d939c29f716..684a54e08bb 100644 --- a/man/systemd.network.xml +++ b/man/systemd.network.xml @@ -2219,6 +2219,21 @@ NFTSet=prefix:netdev:filter:eth_ipv4_prefix + + RapidCommit= + + Takes a boolean. The DHCPv4 client can obtain configuration parameters from a DHCPv4 server + through a rapid two-message exchange (discover and ack). When the rapid commit option is set by + both the DHCPv4 client and the DHCPv4 server, the two-message exchange is used. Otherwise, the + four-message exchange (discover, offer, request, and ack) is used. The two-message exchange + provides faster client configuration. See + RFC 4039 for details. + Defaults to true. + + + + + Anonymize= diff --git a/src/libsystemd-network/dhcp-lease-internal.h b/src/libsystemd-network/dhcp-lease-internal.h index ce1342e9fdd..0b72813733b 100644 --- a/src/libsystemd-network/dhcp-lease-internal.h +++ b/src/libsystemd-network/dhcp-lease-internal.h @@ -50,6 +50,8 @@ struct sd_dhcp_lease { struct in_addr *router; size_t router_size; + bool rapid_commit; + DHCPServerData servers[_SD_DHCP_LEASE_SERVER_TYPE_MAX]; struct sd_dhcp_route *static_routes; diff --git a/src/libsystemd-network/fuzz-dhcp-client.c b/src/libsystemd-network/fuzz-dhcp-client.c index 3e98b683eca..cbdeb42eff8 100644 --- a/src/libsystemd-network/fuzz-dhcp-client.c +++ b/src/libsystemd-network/fuzz-dhcp-client.c @@ -75,7 +75,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { client->xid = 2; client->state = DHCP_STATE_SELECTING; - (void) client_handle_offer(client, (DHCPMessage*) data, size); + (void) client_handle_offer_or_rapid_ack(client, (DHCPMessage*) data, size); assert_se(sd_dhcp_client_stop(client) >= 0); diff --git a/src/libsystemd-network/sd-dhcp-client.c b/src/libsystemd-network/sd-dhcp-client.c index 87d18269e41..2a65d6d0ea5 100644 --- a/src/libsystemd-network/sd-dhcp-client.c +++ b/src/libsystemd-network/sd-dhcp-client.c @@ -94,6 +94,7 @@ struct sd_dhcp_client { bool request_broadcast; Set *req_opts; bool anonymize; + bool rapid_commit; be32_t last_addr; struct hw_addr_data hw_addr; struct hw_addr_data bcast_addr; @@ -576,6 +577,13 @@ int sd_dhcp_client_set_iaid_duid_raw( return 0; } +int sd_dhcp_client_set_rapid_commit(sd_dhcp_client *client, bool rapid_commit) { + assert_return(client, -EINVAL); + + client->rapid_commit = !client->anonymize && rapid_commit; + return 0; +} + int sd_dhcp_client_set_hostname( sd_dhcp_client *client, const char *hostname) { @@ -1131,6 +1139,13 @@ static int client_send_discover(sd_dhcp_client *client) { return r; } + if (client->rapid_commit) { + r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0, + SD_DHCP_OPTION_RAPID_COMMIT, 0, NULL); + if (r < 0) + return r; + } + r = client_append_common_discover_request_options(client, discover, &optoffset, optlen); if (r < 0) return r; @@ -1359,6 +1374,9 @@ static int client_timeout_resend( if (r < 0 && client->attempt >= client->max_attempts) goto error; + if (client->rapid_commit) + client->request_sent = time_now; + break; case DHCP_STATE_INIT_REBOOT: @@ -1583,12 +1601,22 @@ static int client_parse_message( switch (client->state) { case DHCP_STATE_SELECTING: - if (r != DHCP_OFFER) + if (r == DHCP_ACK) { + if (!client->rapid_commit) + return log_dhcp_client_errno(client, SYNTHETIC_ERRNO(ENOMSG), + "received unexpected ACK, ignoring."); + if (!lease->rapid_commit) + return log_dhcp_client_errno(client, SYNTHETIC_ERRNO(ENOMSG), + "received rapid ACK without Rapid Commit option, ignoring."); + } else if (r == DHCP_OFFER) { + if (lease->rapid_commit) + return log_dhcp_client_errno(client, SYNTHETIC_ERRNO(ENOMSG), + "received OFFER with Rapid Commit option, ignoring"); + if (lease->lifetime == 0 && client->fallback_lease_lifetime > 0) + lease->lifetime = client->fallback_lease_lifetime; + } else return log_dhcp_client_errno(client, SYNTHETIC_ERRNO(ENOMSG), - "received message was not an OFFER, ignoring."); - - if (lease->lifetime == 0 && client->fallback_lease_lifetime > 0) - lease->lifetime = client->fallback_lease_lifetime; + "received unexpected message, ignoring."); break; @@ -1641,7 +1669,7 @@ static int client_parse_message( return 0; } -static int client_handle_offer(sd_dhcp_client *client, DHCPMessage *message, size_t len) { +static int client_handle_offer_or_rapid_ack(sd_dhcp_client *client, DHCPMessage *message, size_t len) { _cleanup_(sd_dhcp_lease_unrefp) sd_dhcp_lease *lease = NULL; int r; @@ -1654,6 +1682,11 @@ static int client_handle_offer(sd_dhcp_client *client, DHCPMessage *message, siz dhcp_lease_unref_and_replace(client->lease, lease); + if (client->lease->rapid_commit) { + log_dhcp_client(client, "ACK"); + return SD_DHCP_CLIENT_EVENT_IP_ACQUIRE; + } + if (client_notify(client, SD_DHCP_CLIENT_EVENT_SELECTING) < 0) return -ENOMSG; @@ -1955,6 +1988,27 @@ static int client_enter_bound(sd_dhcp_client *client, int notify_event) { return client_enter_bound_now(client, notify_event); } +static int client_restart(sd_dhcp_client *client) { + int r; + assert(client); + + client_notify(client, SD_DHCP_CLIENT_EVENT_EXPIRED); + + r = client_initialize(client); + if (r < 0) + return r; + + r = client_start_delayed(client); + if (r < 0) + return r; + + log_dhcp_client(client, "REBOOT in %s", FORMAT_TIMESPAN(client->start_delay, USEC_PER_SEC)); + + client->start_delay = CLAMP(client->start_delay * 2, + RESTART_AFTER_NAK_MIN_USEC, RESTART_AFTER_NAK_MAX_USEC); + return 0; +} + static int client_handle_message(sd_dhcp_client *client, DHCPMessage *message, int len) { DHCP_CLIENT_DONT_DESTROY(client); int r; @@ -1966,13 +2020,26 @@ static int client_handle_message(sd_dhcp_client *client, DHCPMessage *message, i switch (client->state) { case DHCP_STATE_SELECTING: - r = client_handle_offer(client, message, len); + r = client_handle_offer_or_rapid_ack(client, message, len); if (ERRNO_IS_NEG_RESOURCE(r)) goto error; + + if (r == -EADDRNOTAVAIL) { + /* got a rapid NAK, let's restart the client */ + r = client_restart(client); + if (r < 0) + goto error; + + return 0; + } if (r < 0) return 0; /* invalid message, let's ignore it */ - r = client_enter_requesting(client); + if (client->lease->rapid_commit) + /* got a succssful rapid commit */ + r = client_enter_bound(client, r); + else + r = client_enter_requesting(client); break; case DHCP_STATE_REBOOTING: @@ -1985,20 +2052,10 @@ static int client_handle_message(sd_dhcp_client *client, DHCPMessage *message, i goto error; if (r == -EADDRNOTAVAIL) { /* got a NAK, let's restart the client */ - client_notify(client, SD_DHCP_CLIENT_EVENT_EXPIRED); - - r = client_initialize(client); + r = client_restart(client); if (r < 0) goto error; - r = client_start_delayed(client); - if (r < 0) - goto error; - - log_dhcp_client(client, "REBOOT in %s", FORMAT_TIMESPAN(client->start_delay, USEC_PER_SEC)); - - client->start_delay = CLAMP(client->start_delay * 2, - RESTART_AFTER_NAK_MIN_USEC, RESTART_AFTER_NAK_MAX_USEC); return 0; } if (r < 0) diff --git a/src/libsystemd-network/sd-dhcp-lease.c b/src/libsystemd-network/sd-dhcp-lease.c index e0e8e817505..14427469006 100644 --- a/src/libsystemd-network/sd-dhcp-lease.c +++ b/src/libsystemd-network/sd-dhcp-lease.c @@ -735,6 +735,12 @@ int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void log_debug_errno(r, "Failed to parse router addresses, ignoring: %m"); break; + case SD_DHCP_OPTION_RAPID_COMMIT: + if (len > 0) + log_debug("Invalid DHCP Rapid Commit option, ignorning."); + lease->rapid_commit = true; + break; + case SD_DHCP_OPTION_DOMAIN_NAME_SERVER: r = lease_parse_in_addrs(option, len, &lease->servers[SD_DHCP_LEASE_DNS].addr, &lease->servers[SD_DHCP_LEASE_DNS].size); if (r < 0) diff --git a/src/network/networkd-dhcp4.c b/src/network/networkd-dhcp4.c index 5ad8128da72..031507b6630 100644 --- a/src/network/networkd-dhcp4.c +++ b/src/network/networkd-dhcp4.c @@ -1485,6 +1485,10 @@ static int dhcp4_configure(Link *link) { if (r < 0) return log_link_debug_errno(link, r, "DHCPv4 CLIENT: Failed to attach device: %m"); + r = sd_dhcp_client_set_rapid_commit(link->dhcp_client, link->network->dhcp_use_rapid_commit); + if (r < 0) + return log_link_debug_errno(link, r, "DHCPv4 CLIENT: Failed to set rapid commit: %m"); + r = sd_dhcp_client_set_mac(link->dhcp_client, link->hw_addr.bytes, link->bcast_addr.length > 0 ? link->bcast_addr.bytes : NULL, diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf index 02fda412439..b87ae006f13 100644 --- a/src/network/networkd-network-gperf.gperf +++ b/src/network/networkd-network-gperf.gperf @@ -261,6 +261,7 @@ DHCPv4.Use6RD, config_parse_bool, DHCPv4.IPv6OnlyMode, config_parse_tristate, 0, offsetof(Network, dhcp_ipv6_only_mode) DHCPv4.NetLabel, config_parse_string, CONFIG_PARSE_STRING_SAFE, offsetof(Network, dhcp_netlabel) DHCPv4.NFTSet, config_parse_nft_set, NFT_SET_PARSE_NETWORK, offsetof(Network, dhcp_nft_set_context) +DHCPv4.RapidCommit config_parse_bool, 0, offsetof(Network, dhcp_use_rapid_commit) DHCPv6.UseAddress, config_parse_bool, 0, offsetof(Network, dhcp6_use_address) DHCPv6.UseDelegatedPrefix, config_parse_bool, 0, offsetof(Network, dhcp6_use_pd_prefix) DHCPv6.UseDNS, config_parse_dhcp_use_dns, AF_INET6, 0 diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c index 3de4ea8dec3..4a38bc88ae1 100644 --- a/src/network/networkd-network.c +++ b/src/network/networkd-network.c @@ -396,6 +396,7 @@ int network_load_one(Manager *manager, OrderedHashmap **networks, const char *fi .dhcp_send_hostname = true, .dhcp_send_release = true, .dhcp_route_metric = DHCP_ROUTE_METRIC, + .dhcp_use_rapid_commit = true, .dhcp_client_identifier = _DHCP_CLIENT_ID_INVALID, .dhcp_route_table = RT_TABLE_MAIN, .dhcp_ip_service_type = -1, diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h index 1091ce289c1..e0891f9c186 100644 --- a/src/network/networkd-network.h +++ b/src/network/networkd-network.h @@ -140,6 +140,7 @@ struct Network { bool dhcp_send_hostname; int dhcp_broadcast; int dhcp_ipv6_only_mode; + bool dhcp_use_rapid_commit; bool dhcp_use_dns; bool dhcp_use_dns_set; bool dhcp_routes_to_dns; diff --git a/src/systemd/sd-dhcp-client.h b/src/systemd/sd-dhcp-client.h index 0996aeeb881..3a8abc82ed4 100644 --- a/src/systemd/sd-dhcp-client.h +++ b/src/systemd/sd-dhcp-client.h @@ -104,6 +104,9 @@ __extension__ int sd_dhcp_client_set_iaid_duid_raw( uint16_t duid_type, const uint8_t *duid, size_t duid_len); +__extension__ int sd_dhcp_client_set_rapid_commit( + sd_dhcp_client *client, + bool rapid_commit); int sd_dhcp_client_get_client_id( sd_dhcp_client *client, uint8_t *ret_type,