[systemd-devel] [PATCH v2 23/26] dhcp: Process DHCP Ack/Nak message

Patrik Flykt patrik.flykt at linux.intel.com
Sun Nov 24 23:13:39 PST 2013


Process a DHCP Ack/Nak in much the same way as an DHCP Offer. Factor
out header verification and process options sent. Add notification
functionality with discrete values for the outcome of the DHCP Ack/
Nak processing.
---
v2: - previous 24/28
    - replace 'err' with 'r' and fix fd as suggested
    - same thing with _cleanup_free_ for a lease as in the previous one,
      usage of the allocation depends on success/failure
    - rename DHCP_EVENT_NAK to DHCP_EVENT_NO_LEASE so it's easier to
      understand

 src/dhcp/client.c            |  136 ++++++++++++++++++++++++++++++++++--------
 src/systemd/sd-dhcp-client.h |    6 ++
 2 files changed, 117 insertions(+), 25 deletions(-)

diff --git a/src/dhcp/client.c b/src/dhcp/client.c
index 1c6602c..bee1a3a 100644
--- a/src/dhcp/client.c
+++ b/src/dhcp/client.c
@@ -136,6 +136,11 @@ int sd_dhcp_client_set_mac(DHCPClient *client, const struct ether_addr *addr)
         return 0;
 }
 
+static int client_notify(DHCPClient *client, int event)
+{
+        return 0;
+}
+
 static int client_stop(DHCPClient *client, int error)
 {
         assert_return(client, -EINVAL);
@@ -157,6 +162,7 @@ static int client_stop(DHCPClient *client, int error)
 
         case DHCP_STATE_INIT:
         case DHCP_STATE_SELECTING:
+        case DHCP_STATE_REQUESTING:
 
                 client->start_time = 0;
                 client->state = DHCP_STATE_INIT;
@@ -164,7 +170,6 @@ static int client_stop(DHCPClient *client, int error)
 
         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:
@@ -477,41 +482,52 @@ static int client_parse_offer(uint8_t code, uint8_t len, uint8_t *option,
         return 0;
 }
 
-static int client_receive_offer(DHCPClient *client,
-                                DHCPPacket *offer, int len)
+static int client_verify_headers(DHCPClient *client,
+                                 DHCPPacket *message, int len)
 {
         int hdrlen;
-        DHCPLease *lease;
 
         if (len < (DHCP_IP_UDP_SIZE + DHCP_MESSAGE_SIZE))
                 return -EINVAL;
 
-        hdrlen = offer->ip.ihl * 4;
-        if (hdrlen < 20 || hdrlen > len || client_checksum(&offer->ip,
+        hdrlen = message->ip.ihl * 4;
+        if (hdrlen < 20 || hdrlen > len || client_checksum(&message->ip,
                                                            hdrlen))
                 return -EINVAL;
 
-        offer->ip.check = offer->udp.len;
-        offer->ip.ttl = 0;
+        message->ip.check = message->udp.len;
+        message->ip.ttl = 0;
 
-        if (hdrlen + be16toh(offer->udp.len) > len ||
-            client_checksum(&offer->ip.ttl, be16toh(offer->udp.len) + 12))
+        if (hdrlen + be16toh(message->udp.len) > len ||
+            client_checksum(&message->ip.ttl, be16toh(message->udp.len) + 12))
                 return -EINVAL;
 
-        if (be16toh(offer->udp.source) != DHCP_PORT_SERVER ||
-            be16toh(offer->udp.dest) != DHCP_PORT_CLIENT)
+        if (be16toh(message->udp.source) != DHCP_PORT_SERVER ||
+            be16toh(message->udp.dest) != DHCP_PORT_CLIENT)
                 return -EINVAL;
 
-        if (offer->dhcp.op != BOOTREPLY)
+        if (message->dhcp.op != BOOTREPLY)
                 return -EINVAL;
 
-        if (be32toh(offer->dhcp.xid) != client->xid)
+        if (be32toh(message->dhcp.xid) != client->xid)
                 return -EINVAL;
 
-        if (memcmp(&offer->dhcp.chaddr[0], &client->mac_addr.ether_addr_octet,
+        if (memcmp(&message->dhcp.chaddr[0], &client->mac_addr.ether_addr_octet,
                     ETHER_ADDR_LEN))
                 return -EINVAL;
 
+        return 0;
+}
+
+static int client_receive_offer(DHCPClient *client, DHCPPacket *offer, int len)
+{
+        int err;
+        DHCPLease *lease;
+
+        err = client_verify_headers(client, offer, len);
+        if (err < 0)
+                return err;
+
         lease = new0(DHCPLease, 1);
         if (!lease)
                 return -ENOMEM;
@@ -539,11 +555,60 @@ error:
         return -ENOMSG;
 }
 
+static int client_receive_ack(DHCPClient *client, DHCPPacket *offer, int len)
+{
+        int r;
+        DHCPLease *lease;
+
+        r = client_verify_headers(client, offer, len);
+        if (r < 0)
+                return r;
+
+        lease = new0(DHCPLease, 1);
+        if (!lease)
+                return -ENOBUFS;
+
+        len = len - DHCP_IP_UDP_SIZE;
+        r = dhcp_option_parse(&offer->dhcp, len, client_parse_offer, lease);
+
+        if (r != DHCP_ACK)
+                goto error;
+
+        lease->address = offer->dhcp.yiaddr;
+
+        if (lease->address == INADDR_ANY ||
+            lease->server_address == INADDR_ANY ||
+            lease->subnet_mask == INADDR_ANY || lease->lifetime == 0) {
+                r = -ENOMSG;
+                goto error;
+        }
+
+        r = 0;
+        if (client->lease) {
+                if (client->lease->address != lease->address ||
+                    client->lease->subnet_mask != lease->subnet_mask ||
+                    client->lease->router != lease->router) {
+                        r = DHCP_EVENT_IP_CHANGE;
+                }
+
+                free(client->lease);
+        }
+
+        client->lease = lease;
+
+        return r;
+
+error:
+        free(lease);
+
+        return r;
+}
+
 static int client_receive_raw_message(sd_event_source *s, int fd,
                                       uint32_t revents, void *userdata)
 {
         DHCPClient *client = userdata;
-        int len, buflen, err = 0;
+        int len, buflen, r = 0;
         _cleanup_free_ uint8_t *buf;
         uint8_t tmp;
         DHCPPacket *message;
@@ -571,21 +636,42 @@ static int client_receive_raw_message(sd_event_source *s, int fd,
                         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)
+                        r = sd_event_add_monotonic(client->event,
+                                                   now(CLOCK_MONOTONIC), 0,
+                                                   client_timeout_resend,
+                                                   client,
+                                                   &client->timeout_resend);
+                        if (r < 0)
                                 goto error;
                 }
 
                 break;
 
+        case DHCP_STATE_REQUESTING:
+
+                r = client_receive_ack(client, message, len);
+                if (r == DHCP_EVENT_NO_LEASE)
+                        goto error;
+
+                if (r >= 0) {
+                        client->timeout_resend =
+                                sd_event_source_unref(client->timeout_resend);
+
+                        client->state = DHCP_STATE_BOUND;
+                        client->attempt = 1;
+
+                        client->last_addr = client->lease->address;
+
+                        client_notify(client, DHCP_EVENT_IP_ACQUIRE);
+
+                        close(client->fd);
+                        client->fd = -1;
+                }
+                break;
+
         case DHCP_STATE_INIT:
         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:
@@ -594,8 +680,8 @@ static int client_receive_raw_message(sd_event_source *s, int fd,
         }
 
 error:
-        if (err < 0)
-                return client_stop(client, err);
+        if (r < 0)
+                return client_stop(client, r);
 
         return 0;
 }
diff --git a/src/systemd/sd-dhcp-client.h b/src/systemd/sd-dhcp-client.h
index 177ccfd..3185552 100644
--- a/src/systemd/sd-dhcp-client.h
+++ b/src/systemd/sd-dhcp-client.h
@@ -26,6 +26,12 @@
 
 #include "sd-event.h"
 
+enum {
+        DHCP_EVENT_NO_LEASE                     = 1,
+        DHCP_EVENT_IP_ACQUIRE                   = 2,
+        DHCP_EVENT_IP_CHANGE                    = 3,
+};
+
 typedef struct DHCPClient DHCPClient;
 
 int sd_dhcp_client_set_request_option(DHCPClient *client, const uint8_t option);
-- 
1.7.10.4



More information about the systemd-devel mailing list