]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
network: implement RFC4039 DHCP Rapid Commit
authorRonan Pigott <ronan@rjp.ie>
Tue, 24 Oct 2023 18:01:32 +0000 (11:01 -0700)
committerRonan Pigott <ronan@rjp.ie>
Thu, 26 Oct 2023 22:26:50 +0000 (15:26 -0700)
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.

NEWS
man/systemd.network.xml
src/libsystemd-network/dhcp-lease-internal.h
src/libsystemd-network/fuzz-dhcp-client.c
src/libsystemd-network/sd-dhcp-client.c
src/libsystemd-network/sd-dhcp-lease.c
src/network/networkd-dhcp4.c
src/network/networkd-network-gperf.gperf
src/network/networkd-network.c
src/network/networkd-network.h
src/systemd/sd-dhcp-client.h

diff --git a/NEWS b/NEWS
index 9767421f59e298994c6527d78e92340a629f0630..62cc4698dc7770a5560fe2ae952391bec5039bd5 100644 (file)
--- 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
index d939c29f716a5e227e4a165daaf0b16d5872a8e8..684a54e08bb7681bea43baed2371b447c195bf12 100644 (file)
@@ -2219,6 +2219,21 @@ NFTSet=prefix:netdev:filter:eth_ipv4_prefix</programlisting>
         </listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><varname>RapidCommit=</varname></term>
+        <listitem>
+          <para>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
+          <ulink url="https://tools.ietf.org/html/rfc4039">RFC 4039</ulink> for details.
+          Defaults to true.</para>
+
+          <xi:include href="version-info.xml" xpointer="v255"/>
+        </listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><varname>Anonymize=</varname></term>
         <listitem>
index ce1342e9fddf66a95918e38fc8577493cf2296c9..0b72813733bf5a89cecbb116747696d287f472bd 100644 (file)
@@ -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;
index 3e98b683eca4f0907eee670f8f8601b0dff09c13..cbdeb42eff816b2a50e796c86111c4b16105cc27 100644 (file)
@@ -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);
 
index 87d18269e414f39108b2900d8e23a091989d6d60..2a65d6d0ea5a8cfd0226aa02ff65635888eb7ab4 100644 (file)
@@ -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)
index e0e8e81750526978beb4df11786acde905d20bde..1442746900605764e2e90bd54d3dd2bca88f0c3b 100644 (file)
@@ -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)
index 5ad8128da7258cc74f15e6b426c2f177d6f4f219..031507b66307cc59a65a12f1200e224071e7b17b 100644 (file)
@@ -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,
index 02fda412439e3840af633db7c8c65a197c5ac3c4..b87ae006f135a94a28f6e27b0de6a602225d35a1 100644 (file)
@@ -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
index 3de4ea8dec31a47a5a3243fe6c0990bc81fea3bc..4a38bc88ae1896ac0f77eabfa5123708d5f70e44 100644 (file)
@@ -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,
index 1091ce289c1b134a878f56bb99c2986120921e4a..e0891f9c186cec95012cb09137db84a14df7c2a9 100644 (file)
@@ -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;
index 0996aeeb881a8a345c4245c3007ac4da3e9afb7c..3a8abc82ed4fcf0b957b046eebaba55c45d23c3f 100644 (file)
@@ -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,