Fix typo in ubus integration
[project/odhcpd.git] / src / dhcpv4.c
index 49b75f4..9746aa2 100644 (file)
@@ -20,6 +20,7 @@
 #include <stddef.h>
 #include <stdlib.h>
 #include <resolv.h>
+#include <limits.h>
 #include <net/if.h>
 #include <net/if_arp.h>
 #include <netinet/ip.h>
 
 
 static void handle_dhcpv4(void *addr, void *data, size_t len,
-               struct interface *iface);
+               struct interface *iface, void *dest_addr);
 static struct dhcpv4_assignment* dhcpv4_lease(struct interface *iface,
                enum dhcpv4_msg msg, const uint8_t *mac, struct in_addr reqaddr,
                const char *hostname);
 
-// Magic option for hnet internal (4B enterprise ID, 1B data-len, 1B subopt-code, 1B subopt-len)
-static uint8_t hnet_internal_data[7] = {0x00, 0x00, 0x76, 0xfe, 2, 1, 0};
-
 // Create socket and register events
 int init_dhcpv4(void)
 {
@@ -122,16 +120,22 @@ int setup_dhcpv4_interface(struct interface *iface, bool enable)
                                                (addr.s_addr & mask.s_addr);
                                iface->dhcpv4_end.s_addr = htonl(end) |
                                                (addr.s_addr & mask.s_addr);
-                       } else if (ntohl(mask.s_addr) <= 0xffffffc0) {
+                       } else if (ntohl(mask.s_addr) <= 0xfffffff0) {
                                start = addr.s_addr & mask.s_addr;
                                end = addr.s_addr & mask.s_addr;
 
                                if (ntohl(mask.s_addr) <= 0xffffff00) {
                                        iface->dhcpv4_start.s_addr = start | htonl(100);
                                        iface->dhcpv4_end.s_addr = end | htonl(250);
-                               } else {
+                               } else if (ntohl(mask.s_addr) <= 0xffffffc0) {
+                                       iface->dhcpv4_start.s_addr = start | htonl(10);
+                                       iface->dhcpv4_end.s_addr = end | htonl(60);
+                               } else if (ntohl(mask.s_addr) <= 0xffffffe0) {
                                        iface->dhcpv4_start.s_addr = start | htonl(10);
-                                       iface->dhcpv4_end.s_addr = end | htonl(59);
+                                       iface->dhcpv4_end.s_addr = end | htonl(30);
+                               } else {
+                                       iface->dhcpv4_start.s_addr = start | htonl(3);
+                                       iface->dhcpv4_end.s_addr = end | htonl(12);
                                }
                        }
 
@@ -152,17 +156,22 @@ int setup_dhcpv4_interface(struct interface *iface, bool enable)
                        a->addr = ntohl(lease->ipaddr.s_addr);
                        memcpy(a->hwaddr, lease->mac.ether_addr_octet, sizeof(a->hwaddr));
                        memcpy(a->hostname, lease->hostname, hostlen);
+                       a->valid_until = LONG_MAX;
 
                        // Assign to all interfaces
                        struct dhcpv4_assignment *c;
                        list_for_each_entry(c, &iface->dhcpv4_assignments, head) {
                                if (c->addr > a->addr) {
                                        list_add_tail(&a->head, &c->head);
+                                       break;
                                } else if (c->addr == a->addr) {
                                        // Already an assignment with that number
                                        break;
                                }
                        }
+                       if (&c->head == &iface->dhcpv4_assignments) {
+                               list_add(&a->head, &iface->dhcpv4_assignments);
+                       }
 
                        if (!a->head.next)
                                free(a);
@@ -172,7 +181,7 @@ int setup_dhcpv4_interface(struct interface *iface, bool enable)
                struct dhcpv4_assignment *a, *n;
                list_for_each_entry_safe(a, n, &iface->dhcpv4_assignments, head) {
                        if ((htonl(a->addr) & smask->sin_addr.s_addr) !=
-                                       (saddr->sin_addr.s_addr & smask->sin_addr.s_addr)) {
+                                       (iface->dhcpv4_start.s_addr & smask->sin_addr.s_addr)) {
                                list_del(&a->head);
                                free(a);
                        }
@@ -216,7 +225,7 @@ static void dhcpv4_put(struct dhcpv4_message *msg, uint8_t **cookie,
 
 // Simple DHCPv6-server for information requests
 static void handle_dhcpv4(void *addr, void *data, size_t len,
-               struct interface *iface)
+               struct interface *iface, _unused void *dest_addr)
 {
        if (!iface->dhcpv4)
                return;
@@ -296,10 +305,13 @@ static void handle_dhcpv4(void *addr, void *data, size_t len,
                } else if (opt->type == DHCPV4_OPT_SERVERID && opt->len == 4) {
                        if (memcmp(opt->data, &ifaddr.sin_addr, 4))
                                return;
-               } else if (opt->type == DHCPV4_OPT_VENDOR_SPECIFIC_INFORMATION &&
-                               opt->len == sizeof(hnet_internal_data)) {
-                       if (!memcmp(opt->data, hnet_internal_data, sizeof(hnet_internal_data)))
-                               return; // Ignoring hnet internal routers
+               } else if (iface->filter_class && opt->type == DHCPV4_OPT_USER_CLASS) {
+                       uint8_t *c = opt->data, *cend = &opt->data[opt->len];
+                       for (; c < cend && &c[*c] < cend; c = &c[1 + *c]) {
+                               size_t elen = strlen(iface->filter_class);
+                               if (*c == elen && !memcmp(&c[1], iface->filter_class, elen))
+                                       return; // Ignore from homenet
+                       }
                }
        }
 
@@ -319,6 +331,10 @@ static void handle_dhcpv4(void *addr, void *data, size_t len,
                        return;
        } else if (reqmsg == DHCPV4_MSG_DISCOVER) {
                msg = DHCPV4_MSG_OFFER;
+       } else if (reqmsg == DHCPV4_MSG_REQUEST && reqaddr.s_addr &&
+                       reqaddr.s_addr != htonl(lease->addr)) {
+               msg = DHCPV4_MSG_NAK;
+               lease = NULL;
        }
 
        if (reqmsg == DHCPV4_MSG_DECLINE || reqmsg == DHCPV4_MSG_RELEASE)
@@ -356,14 +372,16 @@ static void handle_dhcpv4(void *addr, void *data, size_t len,
                dhcpv4_put(&reply, &cookie, DHCPV4_OPT_MTU, 2, &mtu);
        }
 
-       if (iface->search) {
-               char b[256];
-               if (dn_expand(iface->search, iface->search + iface->search_len,
-                               iface->search, b, sizeof(b)) > 0)
-                       dhcpv4_put(&reply, &cookie, DHCPV4_OPT_DOMAIN, strlen(b), b);
+       if (iface->search && iface->search_len <= 255) {
+               dhcpv4_put(&reply, &cookie, DHCPV4_OPT_SEARCH_DOMAIN,
+                               iface->search_len, iface->search);
        } else if (!res_init() && _res.dnsrch[0] && _res.dnsrch[0][0]) {
-               dhcpv4_put(&reply, &cookie, DHCPV4_OPT_DOMAIN,
-                               strlen(_res.dnsrch[0]), _res.dnsrch[0]);
+               uint8_t search_buf[256];
+               int len = dn_comp(_res.dnsrch[0], search_buf,
+                                               sizeof(search_buf), NULL, NULL);
+               if (len > 0)
+                       dhcpv4_put(&reply, &cookie, DHCPV4_OPT_SEARCH_DOMAIN,
+                                       len, search_buf);
        }
 
        dhcpv4_put(&reply, &cookie, DHCPV4_OPT_ROUTER, 4, &ifaddr.sin_addr);
@@ -387,7 +405,7 @@ static void handle_dhcpv4(void *addr, void *data, size_t len,
                dest.sin_addr = req->ciaddr;
                dest.sin_port = htons(DHCPV4_CLIENT_PORT);
        } else if ((ntohs(req->flags) & DHCPV4_FLAG_BROADCAST) ||
-                       req->hlen != reply.hlen) {
+                       req->hlen != reply.hlen || !reply.yiaddr.s_addr) {
                dest.sin_addr.s_addr = INADDR_BROADCAST;
                dest.sin_port = htons(DHCPV4_CLIENT_PORT);
        } else {
@@ -463,9 +481,10 @@ static struct dhcpv4_assignment* dhcpv4_lease(struct interface *iface,
 
        struct dhcpv4_assignment *c, *n, *a = NULL;
        list_for_each_entry_safe(c, n, &iface->dhcpv4_assignments, head) {
-               if (c->addr == raddr && !memcmp(c->hwaddr, mac, 6)) {
+               if (!memcmp(c->hwaddr, mac, 6)) {
                        a = c;
-                       break;
+                       if (c->addr == raddr)
+                               break;
                } else if (c->valid_until < now) {
                        list_del(&c->head);
                        free(c);
@@ -511,6 +530,9 @@ static struct dhcpv4_assignment* dhcpv4_lease(struct interface *iface,
                        a = NULL;
                }
 
+               if (a)
+                       update_state = true;
+
                if (assigned && a)
                        lease = a;
        } else if (msg == DHCPV4_MSG_RELEASE) {