[systemd-devel] [PATCH 21/28] dhcp: Handle received DHCP Offer message

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


Create a function for handling the full IP, UDP and DHCP packet
and tie it to the main loop. Verify IP and UDP headers and checksum.
Creat a new lease structure with using the values supplied in the
DHCP message. Free the lease structure when client is stopped.

Split out socket handling into a creation and a sending part. As a
result modify the test code.
---
 src/dhcp/client.c           |  207 ++++++++++++++++++++++++++++++++++++++++++-
 src/dhcp/internal.h         |    4 +
 src/dhcp/network.c          |   33 +++----
 src/dhcp/protocol.h         |    6 ++
 src/dhcp/test-dhcp-client.c |   21 ++++-
 5 files changed, 253 insertions(+), 18 deletions(-)

diff --git a/src/dhcp/client.c b/src/dhcp/client.c
index cb3d41b..84d5c25 100644
--- a/src/dhcp/client.c
+++ b/src/dhcp/client.c
@@ -32,17 +32,31 @@
 
 #define DHCP_CLIENT_MIN_OPTIONS_SIZE            312
 
+struct DHCPLease {
+        uint32_t lifetime;
+        struct in_addr address;
+        struct in_addr server_address;
+        struct in_addr subnet_mask;
+        struct in_addr router;
+};
+
+typedef struct DHCPLease DHCPLease;
+
 struct DHCPClient {
         DHCPState state;
         sd_event *event;
         sd_event_source *timeout_resend;
         int index;
+        int fd;
+        struct sockaddr_ll link;
+        sd_event_source *receive_message;
         uint8_t *req_opts;
         int req_opts_size;
         struct in_addr *last_addr;
         struct ether_addr mac_addr;
         uint32_t xid;
         uint64_t start_time;
+        DHCPLease *lease;
 };
 
 static uint8_t default_req_opts[] = {
@@ -145,6 +159,14 @@ static int client_stop(DHCPClient *client, int error)
             client->state == DHCP_STATE_INIT_REBOOT)
                 return -EALREADY;
 
+        if (client->fd > 0)
+                close(client->fd);
+        client->fd = 0;
+
+        if (client->receive_message)
+                client->receive_message =
+                        sd_event_source_unref(client->receive_message);
+
         if (client->timeout_resend)
                 client->timeout_resend =
                         sd_event_source_unref(client->timeout_resend);
@@ -154,6 +176,7 @@ static int client_stop(DHCPClient *client, int error)
         case DHCP_STATE_INIT:
         case DHCP_STATE_SELECTING:
 
+                client->start_time = 0;
                 client->state = DHCP_STATE_INIT;
                 break;
 
@@ -167,6 +190,11 @@ static int client_stop(DHCPClient *client, int error)
                 break;
         }
 
+        if (client->lease) {
+                free(client->lease);
+                client->lease = NULL;
+        }
+
         return 0;
 }
 
@@ -293,7 +321,8 @@ static int client_send_discover(DHCPClient *client, uint16_t secs)
         discover->ip.check = client_checksum(&discover->ip,
                                              sizeof(discover->ip));
 
-        err = __dhcp_network_send_raw_packet(client->index, discover, len);
+        err = __dhcp_network_send_raw_socket(client->fd, &client->link,
+                                             discover, len);
 
 error:
         free(discover);
@@ -350,6 +379,168 @@ error:
         return 0;
 }
 
+static int client_parse_offer(uint8_t code, uint8_t len, uint8_t *option,
+                              void *user_data)
+{
+        DHCPLease *lease = user_data;
+        uint32_t val;
+
+        switch(code) {
+
+        case DHCP_OPTION_IP_ADDRESS_LEASE_TIME:
+                if (len == 4) {
+                        memcpy(&val, option, 4);
+                        lease->lifetime = ntohl(val);
+                }
+
+                break;
+
+        case DHCP_OPTION_SERVER_IDENTIFIER:
+                if (len >= 4)
+                        memcpy(&lease->server_address.s_addr, option, 4);
+
+                break;
+
+        case DHCP_OPTION_SUBNET_MASK:
+                if (len >= 4)
+                        memcpy(&lease->subnet_mask.s_addr, option, 4);
+
+                break;
+
+        case DHCP_OPTION_ROUTER:
+                if (len >= 4)
+                        memcpy(&lease->router.s_addr, option, 4);
+
+                break;
+        }
+
+        return 0;
+}
+
+static int client_receive_offer(DHCPClient *client,
+                                DHCPPacket *offer, 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))
+                return -EINVAL;
+
+        offer->ip.check = offer->udp.len;
+        offer->ip.ttl = 0;
+
+        if (hdrlen + ntohs(offer->udp.len) > len ||
+            client_checksum(&offer->ip.ttl, ntohs(offer->udp.len) + 12))
+                return -EINVAL;
+
+        if (ntohs(offer->udp.source) != DHCP_PORT_SERVER ||
+            ntohs(offer->udp.dest) != DHCP_PORT_CLIENT)
+                return -EINVAL;
+
+        if (offer->dhcp.op != BOOTREPLY)
+                return -EINVAL;
+
+        if (ntohl(offer->dhcp.xid) != client->xid)
+                return -EINVAL;
+
+        if (memcmp(&offer->dhcp.chaddr[0], &client->mac_addr.ether_addr_octet,
+                    ETHER_ADDR_LEN))
+                return -EINVAL;
+
+        lease = new0(DHCPLease, 1);
+        if (!lease)
+                return -ENOBUFS;
+
+        len = len - DHCP_IP_UDP_SIZE;
+        if (__dhcp_option_parse(&offer->dhcp, len, client_parse_offer,
+                                lease) != DHCP_OFFER)
+                goto error;
+
+        lease->address.s_addr = offer->dhcp.yiaddr;
+
+        if (lease->address.s_addr == INADDR_ANY ||
+            lease->server_address.s_addr == INADDR_ANY ||
+            lease->subnet_mask.s_addr == INADDR_ANY ||
+            lease->lifetime == 0)
+                goto error;
+
+        client->lease = lease;
+
+        return 0;
+
+error:
+        free(lease);
+
+        return -ENOMSG;
+}
+
+static int client_receive_raw_message(sd_event_source *s, int fd,
+                                      uint32_t revents, void *userdata)
+{
+        DHCPClient *client = userdata;
+        int err = 0;
+        int len, buflen;
+        uint8_t *buf;
+        DHCPPacket *message;
+
+        buflen = sizeof(DHCPPacket) + DHCP_CLIENT_MIN_OPTIONS_SIZE;
+        buf = malloc0(buflen);
+        if (!buf)
+                goto error;
+
+        len = read(fd, buf, buflen);
+        if (len < 0) {
+                err = -errno;
+                goto error;
+        }
+
+        message = (DHCPPacket *)buf;
+
+        switch (client->state) {
+        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;
+                }
+
+                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:
+
+                break;
+        }
+
+        free(buf);
+
+        return 0;
+
+error:
+        if (!err)
+                read(fd, buf, 1);
+
+        return 0;
+}
+
 int dhcp_client_start(DHCPClient *client)
 {
         int err;
@@ -363,6 +554,20 @@ int dhcp_client_start(DHCPClient *client)
 
         client->xid = random_u();
 
+        client->fd = __dhcp_network_bind_raw_socket(client->index,
+                                                    &client->link);
+
+        if (client->fd < 0) {
+                err = client->fd;
+                goto error;
+        }
+
+        err = sd_event_add_io(client->event, client->fd, EPOLLIN,
+                              client_receive_raw_message, client,
+                              &client->receive_message);
+        if (err < 0)
+                goto error;
+
         err = sd_event_add_monotonic(client->event, now(CLOCK_MONOTONIC), 0,
                                      client_timeout_resend, client,
                                      &client->timeout_resend);
diff --git a/src/dhcp/internal.h b/src/dhcp/internal.h
index 92bd2a1..07bd677 100644
--- a/src/dhcp/internal.h
+++ b/src/dhcp/internal.h
@@ -22,9 +22,13 @@
 ***/
 
 #include <stdint.h>
+#include <linux/if_packet.h>
 
 #include "protocol.h"
 
+int __dhcp_network_bind_raw_socket(int index, struct sockaddr_ll *link);
+int __dhcp_network_send_raw_socket(int s, struct sockaddr_ll *link,
+                                   void *packet, int len);
 int __dhcp_network_send_raw_packet(int index, void *packet, int len);
 
 int __dhcp_option_append(uint8_t **buf, int *buflen, uint8_t code,
diff --git a/src/dhcp/network.c b/src/dhcp/network.c
index 623251d..1b25f86 100644
--- a/src/dhcp/network.c
+++ b/src/dhcp/network.c
@@ -28,36 +28,39 @@
 
 #include "internal.h"
 
-int __dhcp_network_send_raw_packet(int index, void *packet, int len)
+int __dhcp_network_bind_raw_socket(int index, struct sockaddr_ll *link)
 {
         int err, s;
-        struct sockaddr_ll link;
-
-        err = 0;
 
         s = socket(AF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC, htons(ETH_P_IP));
         if (s < 0)
                 return -errno;
 
-        memset(&link, 0, sizeof(link));
+        memset(link, 0, sizeof(struct sockaddr_ll));
 
-        link.sll_family = AF_PACKET;
-        link.sll_protocol = htons(ETH_P_IP);
-        link.sll_ifindex =  index;
-        link.sll_halen = ETH_ALEN;
-        memset(&link.sll_addr, 0xff, ETH_ALEN);
+        link->sll_family = AF_PACKET;
+        link->sll_protocol = htons(ETH_P_IP);
+        link->sll_ifindex = index;
+        link->sll_halen = ETH_ALEN;
+        memset(link->sll_addr, 0xff, ETH_ALEN);
 
-        if (bind(s, (struct sockaddr *)&link, sizeof(link)) < 0) {
+        if (bind(s, (struct sockaddr *)link, sizeof(struct sockaddr_ll)) < 0) {
                 err = -errno;
                 close(s);
                 return err;
         }
 
-        if (sendto(s, packet, len, 0, (struct sockaddr *)&link,
-                   sizeof(link)) < 0)
-                err = -errno;
+        return s;
+}
 
-        close(s);
+int __dhcp_network_send_raw_socket(int s, struct sockaddr_ll *link,
+                                   void *packet, int len)
+{
+        int err = 0;
+
+        if (sendto(s, packet, len, 0, (struct sockaddr *)link,
+                   sizeof(struct sockaddr_ll)) < 0)
+                err = -errno;
 
         return err;
 }
diff --git a/src/dhcp/protocol.h b/src/dhcp/protocol.h
index a2c5216..014a6f8 100644
--- a/src/dhcp/protocol.h
+++ b/src/dhcp/protocol.h
@@ -52,6 +52,10 @@ struct DHCPPacket {
 
 typedef struct DHCPPacket DHCPPacket;
 
+#define DHCP_IP_SIZE            (int32_t)(sizeof(struct iphdr))
+#define DHCP_IP_UDP_SIZE        (int32_t)(sizeof(struct udphdr) + DHCP_IP_SIZE)
+#define DHCP_MESSAGE_SIZE       (int32_t)(sizeof(DHCPMessage))
+
 #define DHCP_PORT_SERVER                        67
 #define DHCP_PORT_CLIENT                        68
 
@@ -90,8 +94,10 @@ typedef enum DHCPState DHCPState;
 #define DHCP_OPTION_DOMAIN_NAME                 15
 #define DHCP_OPTION_NTP_SERVER                  42
 #define DHCP_OPTION_REQUESTED_IP_ADDRESS        50
+#define DHCP_OPTION_IP_ADDRESS_LEASE_TIME       51
 #define DHCP_OPTION_OVERLOAD                    52
 #define DHCP_OPTION_MESSAGE_TYPE                53
+#define DHCP_OPTION_SERVER_IDENTIFIER           54
 #define DHCP_OPTION_PARAMETER_REQUEST_LIST      55
 #define DHCP_OPTION_CLIENT_IDENTIFIER           61
 #define DHCP_OPTION_END                         255
diff --git a/src/dhcp/test-dhcp-client.c b/src/dhcp/test-dhcp-client.c
index 9cad04b..dc2420b 100644
--- a/src/dhcp/test-dhcp-client.c
+++ b/src/dhcp/test-dhcp-client.c
@@ -24,6 +24,9 @@
 #include <errno.h>
 #include <stdio.h>
 #include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <unistd.h>
 
 #include "protocol.h"
 #include "internal.h"
@@ -33,6 +36,8 @@ static struct ether_addr mac_addr = {
         .ether_addr_octet = {'A', 'B', 'C', '1', '2', '3'}
 };
 
+static int test_fd[2];
+
 static void test_request_basic(sd_event *e)
 {
         DHCPClient *client;
@@ -124,14 +129,15 @@ static int check_options(uint8_t code, uint8_t len, uint8_t *option,
         return 0;
 }
 
-int __dhcp_network_send_raw_packet(int index, void *packet, int len)
+int __dhcp_network_send_raw_socket(int s, struct sockaddr_ll *link,
+                                   void *packet, int len)
 {
         int size;
         DHCPPacket *discover;
         uint16_t ip_check, udp_check;
         int res;
 
-        assert(index == 42);
+        assert(s > 0);
         assert(packet);
 
         size = sizeof(DHCPPacket) + 4;
@@ -171,6 +177,14 @@ int __dhcp_network_send_raw_packet(int index, void *packet, int len)
         return 575;
 }
 
+int __dhcp_network_bind_raw_socket(int index, struct sockaddr_ll *link)
+{
+        if (socketpair(AF_UNIX, SOCK_STREAM, 0, test_fd) < 0)
+                return -errno;
+
+        return test_fd[0];
+}
+
 static void test_discover_message(sd_event *e)
 {
         DHCPClient *client;
@@ -187,6 +201,9 @@ static void test_discover_message(sd_event *e)
         res = dhcp_client_start(client);
 
         assert(res == 575 || res == 0);
+
+        close(test_fd[0]);
+        close(test_fd[1]);
 }
 
 int main(int argc, char *argv[])
-- 
1.7.10.4



More information about the systemd-devel mailing list