[systemd-devel] [PATCH 22/28] dhcp: Send DHCP Request to acquire an IP address

Patrik Flykt patrik.flykt at linux.intel.com
Wed Nov 13 13:22:50 PST 2013


Create and send a DHCP Request message reusing already existing parts
of the code. This causes factoring out IP and UDP header creation and
moving next timeout calculation to be done every time in the timer
callback function independent of DHCP state. Also add an exponential
part to the timer calculation, bail out if there are errors while
resending the DHCP message for the sixth or more times.
---
 src/dhcp/client.c |  162 +++++++++++++++++++++++++++++++++++++++--------------
 1 file changed, 120 insertions(+), 42 deletions(-)

diff --git a/src/dhcp/client.c b/src/dhcp/client.c
index 84d5c25..4b316dc 100644
--- a/src/dhcp/client.c
+++ b/src/dhcp/client.c
@@ -56,6 +56,7 @@ struct DHCPClient {
         struct ether_addr mac_addr;
         uint32_t xid;
         uint64_t start_time;
+        unsigned int attempt;
         DHCPLease *lease;
 };
 
@@ -171,6 +172,8 @@ static int client_stop(DHCPClient *client, int error)
                 client->timeout_resend =
                         sd_event_source_unref(client->timeout_resend);
 
+        client->attempt = 1;
+
         switch (client->state) {
 
         case DHCP_STATE_INIT:
@@ -268,6 +271,28 @@ static uint16_t client_checksum(void *buf, int len)
         return ~((sum & 0xffff) + (sum >> 16));
 }
 
+static void client_append_ip_headers(DHCPPacket *packet, uint16_t len)
+{
+        packet->ip.version = IPVERSION;
+        packet->ip.ihl = DHCP_IP_SIZE / 4;
+        packet->ip.tot_len = htons(len);
+
+        packet->ip.protocol = IPPROTO_UDP;
+        packet->ip.saddr = INADDR_ANY;
+        packet->ip.daddr = INADDR_BROADCAST;
+
+        packet->udp.source = htons(DHCP_PORT_CLIENT);
+        packet->udp.dest = htons(DHCP_PORT_SERVER);
+        packet->udp.len = htons(len - DHCP_IP_SIZE);
+
+        packet->ip.check = packet->udp.len;
+        packet->udp.check = client_checksum(&packet->ip.ttl, len - 8);
+
+        packet->ip.ttl = IPDEFTTL;
+        packet->ip.check = 0;
+        packet->ip.check = client_checksum(&packet->ip, DHCP_IP_SIZE);
+}
+
 static int client_send_discover(DHCPClient *client, uint16_t secs)
 {
         int err = 0;
@@ -300,32 +325,61 @@ static int client_send_discover(DHCPClient *client, uint16_t secs)
         if (err < 0)
                 goto error;
 
-        discover->ip.version = IPVERSION;
-        discover->ip.ihl = sizeof(discover->ip) >> 2;
-        discover->ip.tot_len = htons(len);
+        client_append_ip_headers(discover, len);
 
-        discover->ip.protocol = IPPROTO_UDP;
-        discover->ip.saddr = INADDR_ANY;
-        discover->ip.daddr = INADDR_BROADCAST;
+        err = __dhcp_network_send_raw_socket(client->fd, &client->link,
+                                             discover, len);
+
+error:
+        free(discover);
 
-        discover->udp.source = htons(DHCP_PORT_CLIENT);
-        discover->udp.dest = htons(DHCP_PORT_SERVER);
-        discover->udp.len = htons(len - sizeof(discover->ip));
+        return err;
+}
+
+static int client_send_request(DHCPClient *client, uint16_t secs)
+{
+        DHCPPacket *request;
+        int optlen, len, err;
+        uint8_t *opt;
+
+        optlen = DHCP_CLIENT_MIN_OPTIONS_SIZE;
+        len = DHCP_MESSAGE_SIZE + optlen;
+
+        request = malloc0(len);
+
+        if (!request)
+                return -ENOBUFS;
+
+        err = client_packet_init(client, DHCP_REQUEST, &request->dhcp, secs,
+                                 &opt, &optlen);
+        if (err < 0)
+                goto error;
+
+        if (client->state == DHCP_STATE_REQUESTING) {
+                err = __dhcp_option_append(&opt, &optlen,
+                                           DHCP_OPTION_REQUESTED_IP_ADDRESS,
+                                           4, &client->lease->address.s_addr);
+                if (err < 0)
+                        goto error;
+
+                err = __dhcp_option_append(&opt, &optlen,
+                                           DHCP_OPTION_SERVER_IDENTIFIER,
+                                           4, &client->lease->server_address.s_addr);
+                if (err < 0)
+                        goto error;
+        }
 
-        discover->ip.check = discover->udp.len;
-        discover->udp.check = client_checksum(&discover->ip.ttl,
-                                              len - 8);
+        err = __dhcp_option_append(&opt, &optlen, DHCP_OPTION_END, 0, NULL);
+        if (err < 0)
+                goto error;
 
-        discover->ip.ttl = IPDEFTTL;
-        discover->ip.check = 0;
-        discover->ip.check = client_checksum(&discover->ip,
-                                             sizeof(discover->ip));
+        client_append_ip_headers(request, len);
 
         err = __dhcp_network_send_raw_socket(client->fd, &client->link,
-                                             discover, len);
+                                             request, len);
 
 error:
-        free(discover);
+        free(request);
 
         return err;
 }
@@ -338,32 +392,50 @@ static int client_timeout_resend(sd_event_source *s, uint64_t usec,
         uint16_t secs;
         int err = 0;
 
+        secs = (usec - client->start_time) / USEC_PER_SEC;
+
+        if (client->attempt < 64)
+                client->attempt *= 2;
+
+        next_timeout = usec + (client->attempt - 1) * USEC_PER_SEC +
+                (random_u() & 0x1fffff);
+
+        err = sd_event_add_monotonic(sd_event_get(s), next_timeout,
+                                     10 * USEC_PER_MSEC,
+                                     client_timeout_resend, client,
+                                     &client->timeout_resend);
+        if (err < 0)
+                goto error;
+
         switch (client->state) {
         case DHCP_STATE_INIT:
-        case DHCP_STATE_SELECTING:
+                err = client_send_discover(client, secs);
+                if (err >= 0) {
+                        client->state = DHCP_STATE_SELECTING;
+                        client->attempt = 1;
+                } else {
+                        if (client->attempt >= 64)
+                                goto error;
+                }
 
-                if (!client->start_time)
-                        client->start_time = usec;
+                break;
 
-                secs = (usec - client->start_time) / USEC_PER_SEC;
+        case DHCP_STATE_SELECTING:
+                err = client_send_discover(client, secs);
+                if (err < 0 && client->attempt >= 64)
+                        goto error;
 
-                next_timeout = usec + 2 * USEC_PER_SEC + (random() & 0x1fffff);
+                break;
 
-                err = sd_event_add_monotonic(sd_event_get(s), next_timeout,
-                                             10 * USEC_PER_MSEC,
-                                             client_timeout_resend, client,
-                                             &client->timeout_resend);
-                if (err < 0)
+        case DHCP_STATE_REQUESTING:
+                err = client_send_request(client, secs);
+                if (err < 0 && client->attempt >= 64)
                         goto error;
 
-                if (client_send_discover(client, secs) >= 0)
-                        client->state = DHCP_STATE_SELECTING;
-
                 break;
 
         case DHCP_STATE_INIT_REBOOT:
         case DHCP_STATE_REBOOTING:
-        case DHCP_STATE_REQUESTING:
         case DHCP_STATE_BOUND:
         case DHCP_STATE_RENEWING:
         case DHCP_STATE_REBINDING:
@@ -494,10 +566,8 @@ static int client_receive_raw_message(sd_event_source *s, int fd,
                 goto error;
 
         len = read(fd, buf, buflen);
-        if (len < 0) {
-                err = -errno;
+        if (len < 0)
                 goto error;
-        }
 
         message = (DHCPPacket *)buf;
 
@@ -505,16 +575,19 @@ static int client_receive_raw_message(sd_event_source *s, int fd,
         case DHCP_STATE_SELECTING:
 
                 if (client_receive_offer(client, message, len) >= 0) {
-
-                        close(client->fd);
-                        client->fd = 0;
-                        client->receive_message =
-                                sd_event_source_unref(client->receive_message);
-
                         client->timeout_resend =
                                 sd_event_source_unref(client->timeout_resend);
 
                         client->state = DHCP_STATE_REQUESTING;
+                        client->attempt = 1;
+
+                        err = sd_event_add_monotonic(client->event,
+                                                     now(CLOCK_MONOTONIC), 0,
+                                                     client_timeout_resend,
+                                                     client,
+                                                     &client->timeout_resend);
+                        if (err < 0)
+                                goto error;
                 }
 
                 break;
@@ -535,6 +608,9 @@ static int client_receive_raw_message(sd_event_source *s, int fd,
         return 0;
 
 error:
+        if (err < 0)
+                return client_stop(client, err);
+
         if (!err)
                 read(fd, buf, 1);
 
@@ -568,7 +644,8 @@ int dhcp_client_start(DHCPClient *client)
         if (err < 0)
                 goto error;
 
-        err = sd_event_add_monotonic(client->event, now(CLOCK_MONOTONIC), 0,
+        client->start_time = now(CLOCK_MONOTONIC);
+        err = sd_event_add_monotonic(client->event, client->start_time, 0,
                                      client_timeout_resend, client,
                                      &client->timeout_resend);
         if (err < 0)
@@ -602,6 +679,7 @@ DHCPClient *dhcp_client_new(sd_event *event)
         client->event = event;
         client->state = DHCP_STATE_INIT;
         client->index = -1;
+        client->attempt = 1;
 
         client->req_opts_size = sizeof(default_req_opts)
                 / sizeof(default_req_opts[0]);
-- 
1.7.10.4



More information about the systemd-devel mailing list