sd-dhcp-server: add basic message parsing
authorTom Gundersen <teg@jklm.no>
Sat, 24 May 2014 20:14:32 +0000 (22:14 +0200)
committerTom Gundersen <teg@jklm.no>
Fri, 13 Jun 2014 14:53:13 +0000 (16:53 +0200)
Parse the maximum message size the client can accept and the client id, falling back to
sane defaults if they are not set.

src/libsystemd-network/dhcp-server-internal.h
src/libsystemd-network/sd-dhcp-server.c

index 63883fa..3d49cba 100644 (file)
@@ -40,6 +40,21 @@ struct sd_dhcp_server {
         int index;
 };
 
+typedef struct DHCPClientId {
+        size_t length;
+        uint8_t *data;
+} DHCPClientId;
+
+typedef struct DHCPRequest {
+        /* received message */
+        DHCPMessage *message;
+
+        /* options */
+        DHCPClientId client_id;
+        size_t max_optlen;
+        be32_t server_id;
+} DHCPRequest;
+
 DEFINE_TRIVIAL_CLEANUP_FUNC(sd_dhcp_server*, sd_dhcp_server_unref);
 #define _cleanup_dhcp_server_unref_ _cleanup_(sd_dhcp_server_unrefp)
 
index 57fb09a..b1f2fa0 100644 (file)
@@ -113,9 +113,84 @@ int sd_dhcp_server_stop(sd_dhcp_server *server) {
         return 0;
 }
 
+static int parse_request(uint8_t code, uint8_t len, const uint8_t *option,
+                         void *user_data) {
+        DHCPRequest *req = user_data;
+
+        assert(req);
+
+        switch(code) {
+        case DHCP_OPTION_SERVER_IDENTIFIER:
+                if (len == 4)
+                        req->server_id = *(be32_t*)option;
+
+                break;
+        case DHCP_OPTION_CLIENT_IDENTIFIER:
+                if (len >= 2) {
+                        uint8_t *data;
+
+                        data = memdup(option, len);
+                        if (!data)
+                                return -ENOMEM;
+
+                        free(req->client_id.data);
+                        req->client_id.data = data;
+                        req->client_id.length = len;
+                }
+
+                break;
+        case DHCP_OPTION_MAXIMUM_MESSAGE_SIZE:
+                if (len == 2)
+                        req->max_optlen = be16toh(*(be16_t*)option) -
+                                          - sizeof(DHCPPacket);
+
+                break;
+        }
+
+        return 0;
+}
+
+static void dhcp_request_free(DHCPRequest *req) {
+        if (!req)
+                return;
+
+        free(req->client_id.data);
+        free(req);
+}
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(DHCPRequest*, dhcp_request_free);
+#define _cleanup_dhcp_request_free_ _cleanup_(dhcp_request_freep)
+
+static int ensure_sane_request(DHCPRequest *req, DHCPMessage *message) {
+        assert(req);
+        assert(message);
+
+        req->message = message;
+
+        /* set client id based on mac address if client did not send an explicit one */
+        if (!req->client_id.data) {
+                uint8_t *data;
+
+                data = new0(uint8_t, ETH_ALEN + 1);
+                if (!data)
+                        return -ENOMEM;
+
+                req->client_id.length = ETH_ALEN + 1;
+                req->client_id.data = data;
+                req->client_id.data[0] = 0x01;
+                memcpy(&req->client_id.data[1], &message->chaddr, ETH_ALEN);
+        }
+
+        if (req->max_optlen < DHCP_MIN_OPTIONS_SIZE)
+                req->max_optlen = DHCP_MIN_OPTIONS_SIZE;
+
+        return 0;
+}
+
 int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message,
                                size_t length) {
-        int type;
+        _cleanup_dhcp_request_free_ DHCPRequest *req = NULL;
+        int type, r;
 
         assert(server);
         assert(message);
@@ -125,10 +200,19 @@ int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message,
             message->hlen != ETHER_ADDR_LEN)
                 return 0;
 
-        type = dhcp_option_parse(message, length, NULL, NULL);
+        req = new0(DHCPRequest, 1);
+        if (!req)
+                return -ENOMEM;
+
+        type = dhcp_option_parse(message, length, parse_request, req);
         if (type < 0)
                 return 0;
 
+        r = ensure_sane_request(req, message);
+        if (r < 0)
+                /* this only fails on critical errors */
+                return r;
+
         log_dhcp_server(server, "received message of type %d", type);
 
         return 1;