Complete managed PD/CER-ID support
authorSteven Barth <steven@midlink.org>
Tue, 1 Apr 2014 09:19:31 +0000 (11:19 +0200)
committerSteven Barth <steven@midlink.org>
Tue, 1 Apr 2014 09:19:31 +0000 (11:19 +0200)
CMakeLists.txt
src/config.c
src/dhcpv6-ia.c
src/dhcpv6.c
src/dhcpv6.h
src/odhcpd.c
src/odhcpd.h

index 2eca68e61144b7a0b08656e716d6baed2bf24fb7..dd750b3e441af4fe5c3b4a383763e9c39bc5415b 100644 (file)
@@ -14,6 +14,10 @@ if(${EXT_PREFIX_CLASS})
        add_definitions(-DEXT_PREFIX_CLASS=${EXT_PREFIX_CLASS})
 endif(${EXT_PREFIX_CLASS})
 
+if (${EXT_CER_ID})
+       add_definitions(-DEXT_CER_ID=${EXT_CER_ID})
+endif(${EXT_CER_ID})
+
 if(${UBUS})
        add_definitions(-DWITH_UBUS)
        set(EXT_SRC ${EXT_SRC} src/ubus.c)
index 7f51585fdb848d2b4b85377e19068bb3ef896b02..a392af7b3fcce34002d4facd72cd8f0c8f25da96 100644 (file)
@@ -36,6 +36,8 @@ enum {
        IFACE_ATTR_RA_MANAGEMENT,
        IFACE_ATTR_RA_OFFLINK,
        IFACE_ATTR_RA_PREFERENCE,
+       IFACE_ATTR_PD_MANAGER,
+       IFACE_ATTR_PD_CER,
        IFACE_ATTR_NDPROXY_ROUTING,
        IFACE_ATTR_NDPROXY_SLAVE,
        IFACE_ATTR_NDPROXY_STATIC,
@@ -59,6 +61,8 @@ static const struct blobmsg_policy iface_attrs[IFACE_ATTR_MAX] = {
        [IFACE_ATTR_NDP] = { .name = "ndp", .type = BLOBMSG_TYPE_STRING },
        [IFACE_ATTR_DNS] = { .name = "dns", .type = BLOBMSG_TYPE_ARRAY },
        [IFACE_ATTR_DOMAIN] = { .name = "domain", .type = BLOBMSG_TYPE_ARRAY },
+       [IFACE_ATTR_PD_MANAGER] = { .name = "pd_manager", .type = BLOBMSG_TYPE_STRING },
+       [IFACE_ATTR_PD_CER] = { .name = "pd_cer", .type = BLOBMSG_TYPE_STRING },
        [IFACE_ATTR_RA_DEFAULT] = { .name = "ra_default", .type = BLOBMSG_TYPE_INT32 },
        [IFACE_ATTR_RA_MANAGEMENT] = { .name = "ra_management", .type = BLOBMSG_TYPE_INT32 },
        [IFACE_ATTR_RA_OFFLINK] = { .name = "ra_offlink", .type = BLOBMSG_TYPE_BOOL },
@@ -473,6 +477,14 @@ int config_parse_interface(void *data, size_t len, const char *name, bool overwr
                        goto err;
        }
 
+       if ((c = tb[IFACE_ATTR_PD_MANAGER]))
+               strncpy(iface->dhcpv6_pd_manager, blobmsg_get_string(c),
+                               sizeof(iface->dhcpv6_pd_manager) - 1);
+
+       if ((c = tb[IFACE_ATTR_PD_CER]) &&
+                       inet_pton(AF_INET6, blobmsg_get_string(c), &iface->dhcpv6_pd_cer) < 1)
+               goto err;
+
        if ((c = tb[IFACE_ATTR_NDPROXY_ROUTING]))
                iface->learn_routes = blobmsg_get_bool(c);
        else if (overwrite)
index 593b5bd5064ee704657ed80971b444b923878f52..a7fbd12830753c70552afe0192ffc40e34abe978 100644 (file)
@@ -50,10 +50,13 @@ int dhcpv6_ia_init(void)
 void free_dhcpv6_assignment(struct dhcpv6_assignment *c)
 {
        if (c->managed_sock.fd.registered) {
-               close(c->managed_sock.fd.fd);
                ustream_free(&c->managed_sock.stream);
+               close(c->managed_sock.fd.fd);
        }
 
+       if (c->head.next)
+               list_del(&c->head);
+
        free(c->managed);
        free(c->hostname);
        free(c->classes);
@@ -67,7 +70,6 @@ int setup_dhcpv6_ia_interface(struct interface *iface, bool enable)
                struct dhcpv6_assignment *c;
                while (!list_empty(&iface->ia_assignments)) {
                        c = list_first_entry(&iface->ia_assignments, struct dhcpv6_assignment, head);
-                       list_del(&c->head);
                        free_dhcpv6_assignment(c);
                }
        }
@@ -401,8 +403,22 @@ static void managed_handle_pd_data(struct ustream *s, _unused int bytes_new)
                        if (sscanf(x, "%u", &n->valid) < 1)
                                continue;
 
-                       n->preferred += now;
-                       n->valid += now;
+                       if (n->preferred > n->valid)
+                               continue;
+
+                       if (UINT32_MAX - now < n->preferred)
+                               n->preferred = UINT32_MAX;
+                       else
+                               n->preferred += now;
+
+                       if (UINT32_MAX - now < n->valid)
+                               n->valid = UINT32_MAX;
+                       else
+                               n->valid += now;
+
+                       n->has_class = false;
+                       n->class = 0;
+                       n->dprefix = 0;
 
                        ++c->managed_size;
                }
@@ -410,10 +426,8 @@ static void managed_handle_pd_data(struct ustream *s, _unused int bytes_new)
                ustream_consume(s, end - data);
        }
 
-       if (first && c->managed_size == 0) {
-               list_del(&c->head);
+       if (first && c->managed_size == 0)
                free_dhcpv6_assignment(c);
-       }
 }
 
 
@@ -436,10 +450,14 @@ static bool assign_pd(struct interface *iface, struct dhcpv6_assignment *assign)
        if (iface->dhcpv6_pd_manager[0]) {
                int fd = usock(USOCK_UNIX | USOCK_TCP, iface->dhcpv6_pd_manager, NULL);
                if (fd >= 0) {
+                       char iaidbuf[298];
+                       odhcpd_hexlify(iaidbuf, assign->clid_data, assign->clid_len);
+
                        assign->managed_sock.stream.notify_read = managed_handle_pd_data;
                        assign->managed_sock.stream.notify_state = managed_handle_pd_done;
                        ustream_fd_init(&assign->managed_sock, fd);
-                       ustream_printf(&assign->managed_sock.stream, "::/%d,0,0\n\n", assign->length);
+                       ustream_printf(&assign->managed_sock.stream, "%s,%x\n::/%d,0,0\n\n",
+                                       iaidbuf, assign->iaid, assign->length);
                        ustream_write_pending(&assign->managed_sock.stream);
                        assign->managed_size = -1;
                        list_add(&assign->head, &iface->ia_assignments);
@@ -693,6 +711,7 @@ static size_t append_reply(uint8_t *buf, size_t buflen, uint16_t status,
 
                        struct odhcpd_ipaddr *addrs = (a->managed) ? a->managed : iface->ia_addr;
                        size_t addrlen = (a->managed) ? (size_t)a->managed_size : iface->ia_addr_len;
+
                        for (size_t i = 0; i < addrlen; ++i) {
                                bool match = true;
                                if (addrs[i].has_class) {
@@ -737,7 +756,7 @@ static size_t append_reply(uint8_t *buf, size_t buflen, uint16_t status,
                                                .len = htons(sizeof(p) - 4),
                                                .preferred = htonl(prefix_pref),
                                                .valid = htonl(prefix_valid),
-                                               .prefix = (a->managed_size) ? a->managed_size : a->length,
+                                               .prefix = (a->managed_size) ? addrs[i].prefix : a->length,
                                                .addr = addrs[i].addr
                                        };
                                        p.addr.s6_addr32[1] |= htonl(a->assigned);
@@ -751,7 +770,8 @@ static size_t append_reply(uint8_t *buf, size_t buflen, uint16_t status,
                                        }
 #endif
 
-                                       if (datalen + entrlen + 4 > buflen || a->assigned == 0)
+                                       if (datalen + entrlen + 4 > buflen ||
+                                                       (a->assigned == 0 && a->managed_size == 0))
                                                continue;
 
                                        memcpy(buf + datalen, &p, sizeof(p));
@@ -886,7 +906,7 @@ static size_t append_reply(uint8_t *buf, size_t buflen, uint16_t status,
 }
 
 
-size_t dhcpv6_handle_ia(uint8_t *buf, size_t buflen, struct interface *iface,
+ssize_t dhcpv6_handle_ia(uint8_t *buf, size_t buflen, struct interface *iface,
                const struct sockaddr_in6 *addr, const void *data, const uint8_t *end)
 {
        time_t now = odhcpd_time();
@@ -935,7 +955,6 @@ size_t dhcpv6_handle_ia(uint8_t *buf, size_t buflen, struct interface *iface,
 
        update(iface);
        bool update_state = false;
-       bool managed_pd_out = false;
 
        struct dhcpv6_assignment *first = NULL;
        dhcpv6_for_each_option(start, end, otype, olen, odata) {
@@ -1040,9 +1059,7 @@ size_t dhcpv6_handle_ia(uint8_t *buf, size_t buflen, struct interface *iface,
                // Generic message handling
                uint16_t status = DHCPV6_STATUS_OK;
                if (a && a->managed_size < 0) {
-                       managed_pd_out = true;
-                       status = DHCPV6_STATUS_NOTONLINK;
-                       ia_response_len = append_reply(buf, buflen, status, ia, NULL, iface, true);
+                       return -1;
                } else if (hdr->msg_type == DHCPV6_MSG_SOLICIT || hdr->msg_type == DHCPV6_MSG_REQUEST) {
                        bool assigned = !!a;
 
@@ -1070,12 +1087,12 @@ size_t dhcpv6_handle_ia(uint8_t *buf, size_t buflen, struct interface *iface,
 
                                        if (is_pd)
                                                while (!(assigned = assign_pd(iface, a)) &&
-                                                               ++a->length <= 64 && !a->managed_size);
+                                                               !a->managed_size && ++a->length <= 64);
                                        else
                                                assigned = assign_na(iface, a);
 
                                        if (a->managed_size && !assigned)
-                                               managed_pd_out = true;
+                                               return -1;
                                }
                        }
 
@@ -1168,14 +1185,6 @@ size_t dhcpv6_handle_ia(uint8_t *buf, size_t buflen, struct interface *iface,
                buf[4] = 0;
                buf[5] = DHCPV6_STATUS_OK;
                response_len += 6;
-       } else if (managed_pd_out && response_len + 6 < buflen) {
-               buf[0] = 0;
-               buf[1] = DHCPV6_OPT_STATUS;
-               buf[2] = 0;
-               buf[3] = 2;
-               buf[4] = 0;
-               buf[5] = DHCPV6_STATUS_NOADDRSAVAIL;
-               response_len += 6;
        }
 
        if (update_state)
index 3fa2acfec3ac741106bd494e8c72853ed9e24d38..62f30e23f749694808e47b84702ad48d504ce18c 100644 (file)
@@ -101,7 +101,7 @@ int setup_dhcpv6_interface(struct interface *iface, bool enable)
 
 
 static void handle_nested_message(uint8_t *data, size_t len,
-               uint8_t **opts, uint8_t **end, struct iovec iov[6])
+               uint8_t **opts, uint8_t **end, struct iovec iov[9])
 {
        struct dhcpv6_relay_header *hdr = (struct dhcpv6_relay_header*)data;
        if (iov[0].iov_base == NULL) {
@@ -124,8 +124,8 @@ static void handle_nested_message(uint8_t *data, size_t len,
        uint8_t *odata;
        dhcpv6_for_each_option(hdr->options, data + len, otype, olen, odata) {
                if (otype == DHCPV6_OPT_RELAY_MSG) {
-                       iov[7].iov_base = odata + olen;
-                       iov[7].iov_len = (((uint8_t*)iov[0].iov_base) + iov[0].iov_len)
+                       iov[8].iov_base = odata + olen;
+                       iov[8].iov_len = (((uint8_t*)iov[0].iov_base) + iov[0].iov_len)
                                        - (odata + olen);
                        handle_nested_message(odata, olen, opts, end, iov);
                        return;
@@ -244,6 +244,14 @@ static void handle_client_request(void *addr, void *data, size_t len,
        } search = {htons(DHCPV6_OPT_DNS_DOMAIN), htons(search_len)};
 
 
+       struct dhcpv6_cer_id cerid = {
+#ifdef EXT_CER_ID
+               .type = htons(EXT_CER_ID),
+#endif
+               .len = htons(36),
+               .addr = iface->dhcpv6_pd_cer,
+       };
+
 
        uint8_t pdbuf[512];
        struct iovec iov[] = {{NULL, 0},
@@ -253,6 +261,7 @@ static void handle_client_request(void *addr, void *data, size_t len,
                        {&search, (search_len) ? sizeof(search) : 0},
                        {search_domain, search_len},
                        {pdbuf, 0},
+                       {&cerid, 0},
                        {NULL, 0}};
 
        uint8_t *opts = (uint8_t*)&hdr[1], *opts_end = (uint8_t*)data + len;
@@ -290,19 +299,35 @@ static void handle_client_request(void *addr, void *data, size_t len,
                                if (((((size_t)c[0]) << 8) | c[1]) == elen && !memcmp(&c[2], excluded_class, elen))
                                        return; // Ignore from homenet
                        }
+               } else if (otype == DHCPV6_OPT_IA_PD) {
+#ifdef EXT_CER_ID
+                       iov[7].iov_len = sizeof(cerid);
+
+                       if (IN6_IS_ADDR_UNSPECIFIED(&cerid.addr)) {
+                               struct odhcpd_ipaddr addrs[32];
+                               ssize_t len = odhcpd_get_interface_addresses(0, addrs,
+                                               sizeof(addrs) / sizeof(*addrs));
+
+                               for (ssize_t i = 0; i < len; ++i)
+                                       if (IN6_IS_ADDR_UNSPECIFIED(&cerid.addr)
+                                                       || memcmp(&addrs[i].addr, &cerid.addr, sizeof(cerid.addr)) < 0)
+                                               cerid.addr = addrs[i].addr;
+                       }
+#endif
                }
        }
 
        if (opts[-4] != DHCPV6_MSG_INFORMATION_REQUEST) {
-               iov[6].iov_len = dhcpv6_handle_ia(pdbuf, sizeof(pdbuf), iface, addr, &opts[-4], opts_end);
-               if (iov[6].iov_len == 0 && opts[-4] == DHCPV6_MSG_REBIND)
+               ssize_t ialen = dhcpv6_handle_ia(pdbuf, sizeof(pdbuf), iface, addr, &opts[-4], opts_end);
+               iov[6].iov_len = ialen;
+               if (ialen < 0 || (ialen == 0 && opts[-4] == DHCPV6_MSG_REBIND))
                        return;
        }
 
        if (iov[0].iov_len > 0) // Update length
                update_nested_message(data, len, iov[1].iov_len + iov[2].iov_len +
                                iov[3].iov_len + iov[4].iov_len + iov[5].iov_len +
-                               iov[6].iov_len - (4 + opts_end - opts));
+                               iov[6].iov_len + iov[7].iov_len - (4 + opts_end - opts));
 
        odhcpd_send(iface->dhcpv6_event.uloop.fd, addr, iov, ARRAY_SIZE(iov), iface);
 }
index 936a350f89b10c2c7265e3a97b6cea05966677c3..dbde88a040b0b8fc3bb468728f43c0d372e528d3 100644 (file)
@@ -164,6 +164,15 @@ struct dhcpv6_assignment {
        uint8_t clid_data[];
 };
 
+struct dhcpv6_cer_id {
+       uint16_t type;
+       uint16_t len;
+       uint16_t reserved;
+       uint16_t auth_type;
+       uint8_t auth[16];
+       struct in6_addr addr;
+};
+
 
 
 #define dhcpv6_for_each_option(start, end, otype, olen, odata)\
@@ -173,7 +182,7 @@ struct dhcpv6_assignment {
                _o += 4 + (_o[2] << 8 | _o[3]))
 
 int dhcpv6_init_ia(struct interface *iface, int socket);
-size_t dhcpv6_handle_ia(uint8_t *buf, size_t buflen, struct interface *iface,
+ssize_t dhcpv6_handle_ia(uint8_t *buf, size_t buflen, struct interface *iface,
                const struct sockaddr_in6 *addr, const void *data, const uint8_t *end);
 int dhcpv6_ia_init(void);
 int setup_dhcpv6_ia_interface(struct interface *iface, bool enable);
index 25a6047b5d7cdb231829795d9d814575f5158ea3..bf9f16d8932845b454569572be627df78ccac6d7 100644 (file)
@@ -217,7 +217,7 @@ ssize_t odhcpd_get_interface_addresses(int ifindex,
 
                struct ifaddrmsg *ifa = NLMSG_DATA(nhm);
                if (ifa->ifa_scope != RT_SCOPE_UNIVERSE ||
-                               ifa->ifa_index != (unsigned)ifindex)
+                               (ifindex && ifa->ifa_index != (unsigned)ifindex))
                        continue;
 
                struct rtattr *rta = (struct rtattr*)&ifa[1];
index d0373f56291841a0b178df15fc6a65835e4d8ec9..cf349388a36194852e0c7bf0c5fe49909a80014b 100644 (file)
@@ -122,6 +122,7 @@ struct interface {
 
        // Managed PD
        char dhcpv6_pd_manager[128];
+       struct in6_addr dhcpv6_pd_cer;
 
        // Services
        enum odhcpd_mode ra;