dhcpv4: DHCP pool size is off-by-one
[project/odhcpd.git] / src / dhcpv4.c
index 3fb59885b09fe631693965ec8a63df29e0486201..679b7931d2e35e0ceb35c3ece50f1f05ed59dc79 100644 (file)
@@ -51,7 +51,8 @@ static void handle_dhcpv4(void *addr, void *data, size_t len,
 static struct dhcp_assignment* dhcpv4_lease(struct interface *iface,
                enum dhcpv4_msg msg, const uint8_t *mac, const uint32_t reqaddr,
                uint32_t *leasetime, const char *hostname, const size_t hostname_len,
-               const bool accept_fr_nonce, bool *incl_fr_opt, uint32_t *fr_serverid);
+               const bool accept_fr_nonce, bool *incl_fr_opt, uint32_t *fr_serverid,
+               const char *reqopts, const size_t reqopts_len);
 
 static struct netevent_handler dhcpv4_netevent_handler = { .cb = dhcpv4_netevent_cb, };
 static struct uloop_timeout valid_until_timeout = {.cb = valid_until_cb};
@@ -262,18 +263,21 @@ static int setup_dhcpv4_addresses(struct interface *iface)
        end = start = iface->dhcpv4_local.s_addr & iface->dhcpv4_mask.s_addr;
 
        /* Auto allocate ranges */
-       if (ntohl(iface->dhcpv4_mask.s_addr) <= 0xffffff00) {
+       if (ntohl(iface->dhcpv4_mask.s_addr) <= 0xffffff00) {           /* /24, 150 of 256, [100..249] */
                iface->dhcpv4_start_ip.s_addr = start | htonl(100);
-               iface->dhcpv4_end_ip.s_addr = end | htonl(250);
-       } else if (ntohl(iface->dhcpv4_mask.s_addr) <= 0xffffffc0) {
+               iface->dhcpv4_end_ip.s_addr = end | htonl(100 + 150 - 1);
+       } else if (ntohl(iface->dhcpv4_mask.s_addr) <= 0xffffff80) {    /* /25, 100 of 128, [20..119] */
+               iface->dhcpv4_start_ip.s_addr = start | htonl(20);
+               iface->dhcpv4_end_ip.s_addr = end | htonl(20 + 100 - 1);
+       } else if (ntohl(iface->dhcpv4_mask.s_addr) <= 0xffffffc0) {    /* /26, 50 of 64, [10..59] */
                iface->dhcpv4_start_ip.s_addr = start | htonl(10);
-               iface->dhcpv4_end_ip.s_addr = end | htonl(60);
-       } else if (ntohl(iface->dhcpv4_mask.s_addr) <= 0xffffffe0) {
+               iface->dhcpv4_end_ip.s_addr = end | htonl(10 + 50 - 1);
+       } else if (ntohl(iface->dhcpv4_mask.s_addr) <= 0xffffffe0) {    /* /27, 20 of 32, [10..29] */
                iface->dhcpv4_start_ip.s_addr = start | htonl(10);
-               iface->dhcpv4_end_ip.s_addr = end | htonl(30);
-       } else {
+               iface->dhcpv4_end_ip.s_addr = end | htonl(10 + 20 - 1);
+       } else {                                                        /* /28, 10 of 16, [3..12] */
                iface->dhcpv4_start_ip.s_addr = start | htonl(3);
-               iface->dhcpv4_end_ip.s_addr = end | htonl(12);
+               iface->dhcpv4_end_ip.s_addr = end | htonl(3 + 10 - 1);
        }
 
        return 0;
@@ -634,7 +638,9 @@ static void handle_dhcpv4(void *addr, void *data, size_t len,
        uint32_t reqaddr = INADDR_ANY;
        uint32_t leasetime = 0;
        size_t hostname_len = 0;
+       size_t reqopts_len = 0;
        char hostname[256];
+       char reqopts[256];
        bool accept_fr_nonce = false;
        bool incl_fr_opt = false;
 
@@ -644,7 +650,11 @@ static void handle_dhcpv4(void *addr, void *data, size_t len,
        dhcpv4_for_each_option(start, end, opt) {
                if (opt->type == DHCPV4_OPT_MESSAGE && opt->len == 1)
                        reqmsg = opt->data[0];
-               else if (opt->type == DHCPV4_OPT_HOSTNAME && opt->len > 0) {
+               else if (opt->type == DHCPV4_OPT_REQOPTS && opt->len > 0) {
+                       reqopts_len = opt->len;
+                       memcpy(reqopts, opt->data, reqopts_len);
+                       reqopts[reqopts_len] = 0;
+               } else if (opt->type == DHCPV4_OPT_HOSTNAME && opt->len > 0) {
                        hostname_len = opt->len;
                        memcpy(hostname, opt->data, hostname_len);
                        hostname[hostname_len] = 0;
@@ -685,7 +695,8 @@ static void handle_dhcpv4(void *addr, void *data, size_t len,
        if (reqmsg != DHCPV4_MSG_INFORM)
                a = dhcpv4_lease(iface, reqmsg, req->chaddr, reqaddr,
                                 &leasetime, hostname, hostname_len,
-                                accept_fr_nonce, &incl_fr_opt, &fr_serverid);
+                                accept_fr_nonce, &incl_fr_opt, &fr_serverid,
+                                reqopts, reqopts_len);
 
        if (!a) {
                if (reqmsg == DHCPV4_MSG_REQUEST)
@@ -997,7 +1008,7 @@ static struct dhcp_assignment*
 dhcpv4_lease(struct interface *iface, enum dhcpv4_msg msg, const uint8_t *mac,
             const uint32_t reqaddr, uint32_t *leasetime, const char *hostname,
             const size_t hostname_len, const bool accept_fr_nonce, bool *incl_fr_opt,
-            uint32_t *fr_serverid)
+            uint32_t *fr_serverid, const char* reqopts, const size_t reqopts_len)
 {
        struct dhcp_assignment *a = find_assignment_by_hwaddr(iface, mac);
        struct lease *l = config_find_lease_by_mac(mac);
@@ -1089,6 +1100,14 @@ dhcpv4_lease(struct interface *iface, enum dhcpv4_msg msg, const uint8_t *mac,
                                        }
                                }
 
+                               if (reqopts_len > 0) {
+                                       a->reqopts = realloc(a->reqopts, reqopts_len + 1);
+                                       if (a->reqopts) {
+                                               memcpy(a->reqopts, reqopts, reqopts_len);
+                                               a->reqopts[reqopts_len] = 0;
+                                       }
+                               }
+
                                if (!(a->flags & OAF_BOUND)) {
                                        a->accept_fr_nonce = accept_fr_nonce;
                                        *incl_fr_opt = accept_fr_nonce;