derive ipv6 static lease from ipv4 if no hostid is given
[project/odhcpd.git] / src / dhcpv6-ia.c
index 3d9fe83..e5a7c0b 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>
@@ -63,6 +63,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 +79,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));
@@ -541,10 +557,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 +581,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 +606,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 +615,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 +632,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 +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, &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 +888,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 +904,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 +969,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);