]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
sd-dhcp-client: several fixlets for sending RELEASE or DECLINE
authorYu Watanabe <watanabe.yu+github@gmail.com>
Mon, 9 Mar 2026 06:18:24 +0000 (15:18 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Tue, 10 Mar 2026 23:45:24 +0000 (08:45 +0900)
- Extract common logic to client_send_release_or_decline().
- Do not send DECLINE message on BOOTP protocol.
- Drop redundant assignment of chaddr, as it is already set by
  client_message_init() -> dhcp_message_init().
- Do not assign acquired address in ciaddr field of DECLINE message,
  but use Requested IP Address option.
- Broadcast DECLINE message, rather than unicast.
- Set server identifier in both cases.

Fixes #39299.

src/libsystemd-network/sd-dhcp-client.c

index 8c373146f61850f2dcc7dd95e71ca1342761d4cd..4b1cdd0b8635215088dc12f6ae56a121a90cfa02 100644 (file)
@@ -876,7 +876,7 @@ static int client_message_init(
            MAY contain the Parameter Request List option. */
         /* NOTE: in case that there would be an option to do not send
          * any PRL at all, the size should be checked before sending */
-        if (!set_isempty(client->req_opts) && type != DHCP_RELEASE) {
+        if (!set_isempty(client->req_opts) && IN_SET(type, DHCP_DISCOVER, DHCP_REQUEST)) {
                 _cleanup_free_ uint8_t *opts = NULL;
                 size_t n_opts, i = 0;
                 void *val;
@@ -2356,70 +2356,93 @@ int sd_dhcp_client_start(sd_dhcp_client *client) {
         return r;
 }
 
-static int client_send_release(sd_dhcp_client *client) {
-        _cleanup_free_ DHCPPacket *release = NULL;
-        size_t optoffset, optlen;
+static int client_send_release_or_decline(sd_dhcp_client *client, uint8_t type) {
         int r;
 
-        assert(client);
+        assert(IN_SET(type, DHCP_RELEASE, DHCP_DECLINE));
 
-        if (!client->send_release)
-                return 0; /* disabled */
+        if (!sd_dhcp_client_is_running(client) || !client->lease || client->bootp)
+                return 0; /* there is nothing to release or decline */
 
-        if (!client->lease || client->bootp)
-                return 0; /* there is nothing to be released */
+        const char *name = type == DHCP_RELEASE ? "RELEASE" : "DECLINE";
 
-        r = client_message_init(client, DHCP_RELEASE, &release, &optlen, &optoffset);
+        _cleanup_free_ DHCPPacket *packet = NULL;
+        size_t optoffset, optlen;
+        r = client_message_init(client, type, &packet, &optlen, &optoffset);
         if (r < 0)
-                return r;
+                return log_dhcp_client_errno(client, r, "Failed to initialize DHCP %s message: %m", name);
 
-        /* Fill up release IP and MAC */
-        release->dhcp.ciaddr = client->lease->address;
-        memcpy(&release->dhcp.chaddr, client->hw_addr.bytes, client->hw_addr.length);
+        /* See RFC 2131, Table 5 */
+        switch (type) {
+        case DHCP_RELEASE:
+                /* On release, the acquired address must be set in ciaddr. */
+                packet->dhcp.ciaddr = client->lease->address;
+                break;
 
-        r = dhcp_option_append(&release->dhcp, optlen, &optoffset, 0,
-                               SD_DHCP_OPTION_END, 0, NULL);
+        case DHCP_DECLINE:
+                /* On decline, the acquired address must be set in Requested IP Address option. */
+                r = dhcp_option_append(&packet->dhcp, optlen, &optoffset, /* overload= */ 0,
+                                       SD_DHCP_OPTION_REQUESTED_IP_ADDRESS,
+                                       4, &client->lease->address);
+                if (r < 0)
+                        return log_dhcp_client_errno(
+                                        client, r,
+                                        "Failed to append Requested IP Address option to DHCP %s message: %m",
+                                        name);
+                break;
+
+        default:
+                assert_not_reached();
+        }
+
+        /* In both cases, the server identifier must be set. */
+        r = dhcp_option_append(&packet->dhcp, optlen, &optoffset, /* overload= */ 0,
+                               SD_DHCP_OPTION_SERVER_IDENTIFIER,
+                               4, &client->lease->server_address);
         if (r < 0)
-                return r;
+                return log_dhcp_client_errno(
+                                client, r,
+                                "Failed to append Server Identifier option to DHCP %s message: %m",
+                                name);
 
-        r = dhcp_network_send_udp_socket(client->fd,
-                                         client->lease->server_address,
-                                         client->server_port,
-                                         &release->dhcp,
-                                         sizeof(DHCPMessage) + optoffset);
+        r = dhcp_option_append(&packet->dhcp, optlen, &optoffset, /* overload= */ 0,
+                               SD_DHCP_OPTION_END, /* optlen= */ 0, /* optval= */ NULL);
         if (r < 0)
-                return r;
+                return log_dhcp_client_errno(
+                                client, r,
+                                "Failed to finalize DHCP %s message: %m",
+                                name);
+
+        switch (type) {
+        case DHCP_RELEASE:
+                r = dhcp_network_send_udp_socket(
+                                client->fd,
+                                client->lease->server_address,
+                                client->server_port,
+                                &packet->dhcp,
+                                sizeof(DHCPMessage) + optoffset);
+                break;
+        case DHCP_DECLINE:
+                r = dhcp_client_send_raw(client, packet, sizeof(DHCPPacket) + optoffset);
+                break;
+        default:
+                assert_not_reached();
+        }
+        if (r < 0)
+                return log_dhcp_client_errno(
+                                client, r,
+                                "Failed to send DHCP %s message: %m",
+                                name);
 
-        log_dhcp_client(client, "RELEASE");
-        return 0;
+        log_dhcp_client(client, "%s", name);
+        return 1; /* sent */
 }
 
 int sd_dhcp_client_send_decline(sd_dhcp_client *client) {
-        _cleanup_free_ DHCPPacket *release = NULL;
-        size_t optoffset, optlen;
         int r;
 
-        if (!sd_dhcp_client_is_running(client) || !client->lease)
-                return 0; /* do nothing */
-
-        r = client_message_init(client, DHCP_DECLINE, &release, &optlen, &optoffset);
-        if (r < 0)
-                return r;
-
-        release->dhcp.ciaddr = client->lease->address;
-        memcpy(&release->dhcp.chaddr, client->hw_addr.bytes, client->hw_addr.length);
-
-        r = dhcp_option_append(&release->dhcp, optlen, &optoffset, 0,
-                               SD_DHCP_OPTION_END, 0, NULL);
-        if (r < 0)
-                return r;
-
-        r = dhcp_network_send_udp_socket(client->fd,
-                                         client->lease->server_address,
-                                         client->server_port,
-                                         &release->dhcp,
-                                         sizeof(DHCPMessage) + optoffset);
-        if (r < 0)
+        r = client_send_release_or_decline(client, DHCP_DECLINE);
+        if (r <= 0)
                 return r;
 
         log_dhcp_client(client, "DECLINE");
@@ -2434,20 +2457,15 @@ int sd_dhcp_client_send_decline(sd_dhcp_client *client) {
 }
 
 int sd_dhcp_client_stop(sd_dhcp_client *client) {
-        int r;
-
         if (!client)
                 return 0;
 
         DHCP_CLIENT_DONT_DESTROY(client);
 
-        r = client_send_release(client);
-        if (r < 0)
-                log_dhcp_client_errno(client, r,
-                                      "Failed to send DHCP release message, ignoring: %m");
+        if (client->send_release)
+                (void) client_send_release_or_decline(client, DHCP_RELEASE);
 
         client_stop(client, SD_DHCP_CLIENT_EVENT_STOP);
-
         return 0;
 }