9ecd5b8
From 72dec8120893e76d51e430b4ee5a263e885d0974 Mon Sep 17 00:00:00 2001
9ecd5b8
From: Dan Williams <dcbw@redhat.com>
9ecd5b8
Date: Tue, 4 Nov 2014 11:20:43 -0600
9ecd5b8
Subject: [PATCH] sd-dhcp-client: fix REBOOT state handling
9ecd5b8
9ecd5b8
client->secs wasn't getting set in the REBOOT state, causing
9ecd5b8
an assertion.  REBOOT should work the same way as INIT, per
9ecd5b8
RFC 2131:
9ecd5b8
9ecd5b8
 secs   2  Filled in by client, seconds elapsed since client
9ecd5b8
           began address acquisition or renewal process.
9ecd5b8
9ecd5b8
REBOOT is necessary because some DHCP servers (eg on
9ecd5b8
home routers) do not hand back the same IP address unless the
9ecd5b8
'ciaddr' field is filled with that address, which DISCOVER
9ecd5b8
cannot do per the RFCs.  This leads to multiple leases
9ecd5b8
on machine reboot or DHCP client restart.
9ecd5b8
9ecd5b8
(cherry picked from commit d8d74ef06fa0ccf39084a6177e70e4c2297cca57)
9ecd5b8
---
9ecd5b8
 src/libsystemd-network/sd-dhcp-client.c | 31 +++++++++++++------------------
9ecd5b8
 1 file changed, 13 insertions(+), 18 deletions(-)
9ecd5b8
9ecd5b8
diff --git a/src/libsystemd-network/sd-dhcp-client.c b/src/libsystemd-network/sd-dhcp-client.c
9ecd5b8
index 1f7f238ca0..9986b5257b 100644
9ecd5b8
--- a/src/libsystemd-network/sd-dhcp-client.c
9ecd5b8
+++ b/src/libsystemd-network/sd-dhcp-client.c
9ecd5b8
@@ -68,7 +68,6 @@ struct sd_dhcp_client {
9ecd5b8
         uint32_t mtu;
9ecd5b8
         uint32_t xid;
9ecd5b8
         usec_t start_time;
9ecd5b8
-        uint16_t secs;
9ecd5b8
         unsigned int attempt;
9ecd5b8
         usec_t request_sent;
9ecd5b8
         sd_event_source *timeout_t1;
9ecd5b8
@@ -321,10 +320,12 @@ static int client_message_init(sd_dhcp_client *client, DHCPPacket **ret,
9ecd5b8
         _cleanup_free_ DHCPPacket *packet;
9ecd5b8
         size_t optlen, optoffset, size;
9ecd5b8
         be16_t max_size;
9ecd5b8
+        usec_t time_now;
9ecd5b8
+        uint16_t secs;
9ecd5b8
         int r;
9ecd5b8
 
9ecd5b8
         assert(client);
9ecd5b8
-        assert(client->secs);
9ecd5b8
+        assert(client->start_time);
9ecd5b8
         assert(ret);
9ecd5b8
         assert(_optlen);
9ecd5b8
         assert(_optoffset);
9ecd5b8
@@ -344,7 +345,15 @@ static int client_message_init(sd_dhcp_client *client, DHCPPacket **ret,
9ecd5b8
 
9ecd5b8
         /* Although 'secs' field is a SHOULD in RFC 2131, certain DHCP servers
9ecd5b8
            refuse to issue an DHCP lease if 'secs' is set to zero */
9ecd5b8
-        packet->dhcp.secs = htobe16(client->secs);
9ecd5b8
+        r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now);
9ecd5b8
+        if (r < 0)
9ecd5b8
+                return r;
9ecd5b8
+        assert(time_now >= client->start_time);
9ecd5b8
+
9ecd5b8
+        /* seconds between sending first and last DISCOVER
9ecd5b8
+         * must always be strictly positive to deal with broken servers */
9ecd5b8
+        secs = ((time_now - client->start_time) / USEC_PER_SEC) ? : 1;
9ecd5b8
+        packet->dhcp.secs = htobe16(secs);
9ecd5b8
 
9ecd5b8
         /* RFC2132 section 4.1
9ecd5b8
            A client that cannot receive unicast IP datagrams until its protocol
9ecd5b8
@@ -441,24 +450,12 @@ static int dhcp_client_send_raw(sd_dhcp_client *client, DHCPPacket *packet,
9ecd5b8
 static int client_send_discover(sd_dhcp_client *client) {
9ecd5b8
         _cleanup_free_ DHCPPacket *discover = NULL;
9ecd5b8
         size_t optoffset, optlen;
9ecd5b8
-        usec_t time_now;
9ecd5b8
         int r;
9ecd5b8
 
9ecd5b8
         assert(client);
9ecd5b8
         assert(client->state == DHCP_STATE_INIT ||
9ecd5b8
                client->state == DHCP_STATE_SELECTING);
9ecd5b8
 
9ecd5b8
-        /* See RFC2131 section 4.4.1 */
9ecd5b8
-
9ecd5b8
-        r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now);
9ecd5b8
-        if (r < 0)
9ecd5b8
-                return r;
9ecd5b8
-        assert(time_now >= client->start_time);
9ecd5b8
-
9ecd5b8
-        /* seconds between sending first and last DISCOVER
9ecd5b8
-         * must always be strictly positive to deal with broken servers */
9ecd5b8
-        client->secs = ((time_now - client->start_time) / USEC_PER_SEC) ? : 1;
9ecd5b8
-
9ecd5b8
         r = client_message_init(client, &discover, DHCP_DISCOVER,
9ecd5b8
                                 &optlen, &optoffset);
9ecd5b8
         if (r < 0)
9ecd5b8
@@ -875,10 +872,8 @@ static int client_start(sd_dhcp_client *client) {
9ecd5b8
         }
9ecd5b8
         client->fd = r;
9ecd5b8
 
9ecd5b8
-        if (client->state == DHCP_STATE_INIT) {
9ecd5b8
+        if (client->state == DHCP_STATE_INIT || client->state == DHCP_STATE_INIT_REBOOT)
9ecd5b8
                 client->start_time = now(clock_boottime_or_monotonic());
9ecd5b8
-                client->secs = 0;
9ecd5b8
-        }
9ecd5b8
 
9ecd5b8
         return client_initialize_events(client, client_receive_message_raw);
9ecd5b8
 }