Fix prefix detection for hybrid mode
[project/odhcpd.git] / src / dhcpv6-ia.c
index 3d9fe83..2e93bd0 100644 (file)
@@ -15,7 +15,7 @@
 #include "odhcpd.h"
 #include "dhcpv6.h"
 #include "dhcpv4.h"
-#include "md5.h"
+#include "libubox/md5.h"
 
 #include <time.h>
 #include <errno.h>
 static void update(struct interface *iface);
 static void reconf_timer(struct uloop_timeout *event);
 static struct uloop_timeout reconf_event = {.cb = reconf_timer};
-static int socket_fd = -1;
 static uint32_t serial = 0;
+static uint8_t statemd5[16];
 
 
-int dhcpv6_ia_init(int dhcpv6_socket)
+int dhcpv6_ia_init(void)
 {
-       socket_fd = dhcpv6_socket;
        uloop_timeout_set(&reconf_event, 2000);
        return 0;
 }
@@ -63,6 +62,11 @@ int setup_dhcpv6_ia_interface(struct interface *iface, bool enable)
 
                if (list_empty(&iface->ia_assignments)) {
                        struct dhcpv6_assignment *border = calloc(1, sizeof(*border));
+                       if (!border) {
+                               syslog(LOG_ERR, "Calloc failed for border on interface %s", iface->ifname);
+                               return -1;
+                       }
+                       
                        border->length = 64;
                        list_add(&border->head, &iface->ia_assignments);
                }
@@ -74,9 +78,20 @@ int setup_dhcpv6_ia_interface(struct interface *iface, bool enable)
                list_for_each_entry(lease, &leases, head) {
                        // Construct entry
                        struct dhcpv6_assignment *a = calloc(1, sizeof(*a) + lease->duid_len);
+                       if (!a) {
+                               syslog(LOG_ERR, "Calloc failed for static lease assignment on interface %s",
+                                       iface->ifname);
+                               return -1;
+                       }
+
                        a->clid_len = lease->duid_len;
                        a->length = 128;
-                       a->assigned = lease->hostid;
+                       if (lease->hostid) {
+                               a->assigned = lease->hostid;
+                       } else {
+                               uint32_t i4a = ntohl(lease->ipaddr.s_addr) & 0xff;
+                               a->assigned = ((i4a / 100) << 8) | (((i4a % 100) / 10) << 4) | (i4a % 10);
+                       }
                        odhcpd_urandom(a->key, sizeof(a->key));
                        memcpy(a->clid_data, lease->duid, a->clid_len);
                        memcpy(a->mac, lease->mac.ether_addr_octet, sizeof(a->mac));
@@ -167,12 +182,15 @@ static int send_reconf(struct interface *iface, struct dhcpv6_assignment *assign
        md5_hash(reconf_msg.auth.key, 16, &md5);
        md5_end(reconf_msg.auth.key, &md5);
 
-       return odhcpd_send(socket_fd, &assign->peer, &iov, 1, iface);
+       return odhcpd_send(iface->dhcpv6_event.uloop.fd, &assign->peer, &iov, 1, iface);
 }
 
 
 void dhcpv6_write_statefile(void)
 {
+       md5_ctx_t md5;
+       md5_begin(&md5);
+
        if (config.dhcp_statefile) {
                time_t now = odhcpd_time(), wall_time = time(NULL);
                int fd = open(config.dhcp_statefile, O_CREAT | O_WRONLY | O_CLOEXEC, 0644);
@@ -224,8 +242,18 @@ void dhcpv6_write_statefile(void)
                                                        addr.s6_addr32[1] |= htonl(c->assigned);
                                                inet_ntop(AF_INET6, &addr, ipbuf, sizeof(ipbuf) - 1);
 
-                                               if (c->length == 128 && c->hostname && i == 0)
-                                                       fprintf(fp, "%s\t%s\n", ipbuf, c->hostname);
+                                               if (c->length == 128 && c->hostname && i == 0) {
+                                                       fputs(ipbuf, fp);
+
+                                                       char b[256];
+                                                       if (dn_expand(iface->search, iface->search + iface->search_len,
+                                                                       iface->search, b, sizeof(b)) > 0)
+                                                               fprintf(fp, "\t%s.%s", c->hostname, b);
+
+                                                       fprintf(fp, "\t%s\n", c->hostname);
+                                                       md5_hash(ipbuf, strlen(ipbuf), &md5);
+                                                       md5_hash(c->hostname, strlen(c->hostname), &md5);
+                                               }
 
                                                l += snprintf(leasebuf + l, sizeof(leasebuf) - l, "%s/%hhu ", ipbuf, c->length);
                                        }
@@ -253,8 +281,18 @@ void dhcpv6_write_statefile(void)
                                        struct in_addr addr = {htonl(c->addr)};
                                        inet_ntop(AF_INET, &addr, ipbuf, sizeof(ipbuf) - 1);
 
-                                       if (c->hostname[0])
-                                               fprintf(fp, "%s\t%s\n", ipbuf, c->hostname);
+                                       if (c->hostname[0]) {
+                                               fputs(ipbuf, fp);
+
+                                               char b[256];
+                                               if (dn_expand(iface->search, iface->search + iface->search_len,
+                                                               iface->search, b, sizeof(b)) > 0)
+                                                       fprintf(fp, "\t%s.%s", c->hostname, b);
+
+                                               fprintf(fp, "\t%s\n", c->hostname);
+                                               md5_hash(ipbuf, strlen(ipbuf), &md5);
+                                               md5_hash(c->hostname, strlen(c->hostname), &md5);
+                                       }
 
                                        l += snprintf(leasebuf + l, sizeof(leasebuf) - l, "%s/32 ", ipbuf);
                                        leasebuf[l - 1] = '\n';
@@ -266,7 +304,11 @@ void dhcpv6_write_statefile(void)
                fclose(fp);
        }
 
-       if (config.dhcp_cb) {
+       uint8_t newmd5[16];
+       md5_end(newmd5, &md5);
+
+       if (config.dhcp_cb && memcmp(newmd5, statemd5, sizeof(newmd5))) {
+               memcpy(statemd5, newmd5, sizeof(statemd5));
                char *argv[2] = {config.dhcp_cb, NULL};
                if (!vfork()) {
                        execv(argv[0], argv);
@@ -541,10 +583,6 @@ static size_t append_reply(uint8_t *buf, size_t buflen, uint16_t status,
                if (a) {
                        uint32_t pref = 3600;
                        uint32_t valid = 3600;
-                       bool have_non_ula = false;
-                       for (size_t i = 0; i < iface->ia_addr_len; ++i)
-                               if ((iface->ia_addr[i].addr.s6_addr[0] & 0xfe) != 0xfc)
-                                       have_non_ula = true;
 
                        for (size_t i = 0; i < iface->ia_addr_len; ++i) {
                                bool match = true;
@@ -569,12 +607,6 @@ static size_t append_reply(uint8_t *buf, size_t buflen, uint16_t status,
                                                iface->ia_addr[i].preferred <= (uint32_t)now)
                                        continue;
 
-                               // ULA-deprecation compatibility workaround
-                               if ((iface->ia_addr[i].addr.s6_addr[0] & 0xfe) == 0xfc &&
-                                               a->length == 128 && have_non_ula &&
-                                               iface->deprecate_ula_if_public_avail)
-                                       continue;
-
                                if (prefix_pref > 86400)
                                        prefix_pref = 86400;
 
@@ -600,7 +632,7 @@ static size_t append_reply(uint8_t *buf, size_t buflen, uint16_t status,
                                                .addr = iface->ia_addr[i].addr
                                        };
                                        p.addr.s6_addr32[1] |= htonl(a->assigned);
-                                       size_t entrlen = sizeof(p);
+                                       size_t entrlen = sizeof(p) - 4;
 
 #ifdef DHCPV6_OPT_PREFIX_CLASS
                                        if (iface->ia_addr[i].has_class) {
@@ -609,14 +641,14 @@ static size_t append_reply(uint8_t *buf, size_t buflen, uint16_t status,
                                        }
 #endif
 
-                                       if (datalen + entrlen > buflen || a->assigned == 0)
+                                       if (datalen + entrlen + 4 > buflen || a->assigned == 0)
                                                continue;
 
                                        memcpy(buf + datalen, &p, sizeof(p));
 #ifdef DHCPV6_OPT_PREFIX_CLASS
                                        memcpy(buf + datalen + sizeof(p), &pclass, sizeof(pclass));
 #endif
-                                       datalen += entrlen;
+                                       datalen += entrlen + 4;
                                } else {
                                        struct dhcpv6_ia_addr n = {
                                                .type = htons(DHCPV6_OPT_IA_ADDR),
@@ -626,7 +658,7 @@ static size_t append_reply(uint8_t *buf, size_t buflen, uint16_t status,
                                                .valid = htonl(prefix_valid)
                                        };
                                        n.addr.s6_addr32[3] = htonl(a->assigned);
-                                       size_t entrlen = sizeof(n);
+                                       size_t entrlen = sizeof(n) - 4;
 
 #ifdef DHCPV6_OPT_PREFIX_CLASS
                                        if (iface->ia_addr[i].has_class) {
@@ -635,14 +667,14 @@ static size_t append_reply(uint8_t *buf, size_t buflen, uint16_t status,
                                        }
 #endif
 
-                                       if (datalen + entrlen > buflen || a->assigned == 0)
+                                       if (datalen + entrlen + 4 > buflen || a->assigned == 0)
                                                continue;
 
                                        memcpy(buf + datalen, &n, sizeof(n));
 #ifdef DHCPV6_OPT_PREFIX_CLASS
                                        memcpy(buf + datalen + sizeof(n), &pclass, sizeof(pclass));
 #endif
-                                       datalen += entrlen;
+                                       datalen += entrlen + 4;
                                }
 
                                // Calculate T1 / T2 based on non-deprecated addresses
@@ -882,6 +914,11 @@ size_t dhcpv6_handle_ia(uint8_t *buf, size_t buflen, struct interface *iface,
                                a->peer = *addr;
                                a->reconf_cnt = 0;
                                a->reconf_sent = 0;
+                               a->all_class = class_oro;
+                               a->classes_cnt = classes_cnt;
+                               a->classes = realloc(a->classes, classes_cnt * sizeof(uint16_t));
+                               if (a->classes)
+                                       memcpy(a->classes, classes, classes_cnt * sizeof(uint16_t));
                                break;
                        }
                }
@@ -893,26 +930,31 @@ size_t dhcpv6_handle_ia(uint8_t *buf, size_t buflen, struct interface *iface,
 
                        if (!a && !iface->no_dynamic_dhcp) { // Create new binding
                                a = calloc(1, sizeof(*a) + clid_len);
-                               a->clid_len = clid_len;
-                               a->iaid = ia->iaid;
-                               a->length = reqlen;
-                               a->peer = *addr;
-                               a->assigned = reqhint;
-                               a->all_class = class_oro;
-                               a->classes_cnt = classes_cnt;
-                               a->classes = malloc(classes_cnt * sizeof(uint16_t));
-                               memcpy(a->classes, classes, classes_cnt * sizeof(uint16_t));
+                               if (a) {
+                                       a->clid_len = clid_len;
+                                       a->iaid = ia->iaid;
+                                       a->length = reqlen;
+                                       a->peer = *addr;
+                                       a->assigned = reqhint;
+                                       a->all_class = class_oro;
+                                       a->classes_cnt = classes_cnt;
+                                       if (classes_cnt) {
+                                               a->classes = malloc(classes_cnt * sizeof(uint16_t));
+                                               if (a->classes)
+                                                       memcpy(a->classes, classes, classes_cnt * sizeof(uint16_t));
+                                       }
 
-                               if (first)
-                                       memcpy(a->key, first->key, sizeof(a->key));
-                               else
-                                       odhcpd_urandom(a->key, sizeof(a->key));
-                               memcpy(a->clid_data, clid_data, clid_len);
+                                       if (first)
+                                               memcpy(a->key, first->key, sizeof(a->key));
+                                       else
+                                               odhcpd_urandom(a->key, sizeof(a->key));
+                                       memcpy(a->clid_data, clid_data, clid_len);
 
-                               if (is_pd)
-                                       while (!(assigned = assign_pd(iface, a)) && ++a->length <= 64);
-                               else
-                                       assigned = assign_na(iface, a);
+                                       if (is_pd)
+                                               while (!(assigned = assign_pd(iface, a)) && ++a->length <= 64);
+                                       else
+                                               assigned = assign_na(iface, a);
+                               }
                        }
 
                        if (!assigned || iface->ia_addr_len == 0) { // Set error status
@@ -953,8 +995,10 @@ size_t dhcpv6_handle_ia(uint8_t *buf, size_t buflen, struct interface *iface,
                        } else if (assigned && hdr->msg_type == DHCPV6_MSG_REQUEST) {
                                if (hostname_len > 0) {
                                        a->hostname = realloc(a->hostname, hostname_len + 1);
-                                       memcpy(a->hostname, hostname, hostname_len);
-                                       a->hostname[hostname_len] = 0;
+                                       if (a->hostname) {
+                                               memcpy(a->hostname, hostname, hostname_len);
+                                               a->hostname[hostname_len] = 0;
+                                       }
                                }
                                a->accept_reconf = accept_reconf;
                                apply_lease(iface, a, true);