]> git.ipfire.org Git - thirdparty/dhcpcd.git/commitdiff
Fix using multiple enterprise IDs with vendclass (Option 124 DHCP / Option 16 DHCPv6)
authorStipe <stipe.poljak.ext@ericsson.com>
Sat, 18 Jan 2025 11:46:20 +0000 (12:46 +0100)
committerGitHub <noreply@github.com>
Sat, 18 Jan 2025 11:46:20 +0000 (11:46 +0000)
Fixes #328

src/dhcp.c
src/dhcp6.c
src/if-options.c
src/if-options.h

index 9e23ab620189a6c63bf2d222e8159889a630679a..bc87c760e2b8d9d293bb43d4adb91a26513f9a01 100644 (file)
@@ -809,7 +809,6 @@ make_message(struct bootp **bootpm, const struct interface *ifp, uint8_t type)
        const struct dhcp_lease *lease = &state->lease;
        char hbuf[HOSTNAME_MAX_LEN + 1];
        const char *hostname;
-       const struct vivco *vivco;
        int mtu;
 #ifdef AUTH
        uint8_t *auth, auth_len;
@@ -1138,35 +1137,35 @@ make_message(struct bootp **bootpm, const struct interface *ifp, uint8_t type)
                       p += ifo->mudurl[0] + 1;
                }
 
+#ifndef SMALL
                if (ifo->vivco_len &&
                    !has_option_mask(ifo->nomask, DHO_VIVCO))
                {
-                       AREA_CHECK(sizeof(ul));
-                       *p++ = DHO_VIVCO;
-                       lp = p++;
-                       *lp = sizeof(ul);
-                       ul = htonl(ifo->vivco_en);
-                       memcpy(p, &ul, sizeof(ul));
-                       p += sizeof(ul);
-                       for (i = 0, vivco = ifo->vivco;
-                           i < ifo->vivco_len;
-                           i++, vivco++)
-                       {
-                               AREA_FIT(vivco->len);
-                               if (vivco->len + 2 + *lp > 255) {
-                                       logerrx("%s: VIVCO option too big",
-                                           ifp->name);
-                                       free(bootp);
-                                       return -1;
-                               }
-                               *p++ = (uint8_t)vivco->len;
-                               memcpy(p, vivco->data, vivco->len);
-                               p += vivco->len;
+                       struct vivco *vivco = ifo->vivco;
+                       size_t vlen = ifo->vivco_len;
+                       struct rfc3396_ctx rctx = {
+                               .code = DHO_VIVCO,
+                               .buf = &p,
+                               .buflen = AREA_LEFT,
+                       };
+
+                       for (; vlen > 0; vivco++, vlen--) {
+                               ul = htonl(vivco->en);
+                               if (rfc3396_write(&rctx, &ul, sizeof(ul)) == -1)
+                                       goto toobig;
+                               lp = rfc3396_zero(&rctx);
+                               if (lp == NULL)
+                                       goto toobig;
+                               if (rfc3396_write_byte(&rctx,
+                                   (uint8_t)vivco->len) == -1)
+                                       goto toobig;
+                               if (rfc3396_write(&rctx,
+                                   vivco->data, vivco->len) == -1)
+                                       goto toobig;
                                *lp = (uint8_t)(*lp + vivco->len + 1);
                        }
                }
-
-#ifndef SMALL
+               
                if (ifo->vsio_len &&
                    !has_option_mask(ifo->nomask, DHO_VIVSO))
                {
index ca076e4ede42bc5d2b48bfcfcaf63c36e9835385..ea0bff53641ff538ae746cce4a32039ff57a0948 100644 (file)
@@ -276,29 +276,28 @@ dhcp6_makeuser(void *data, const struct interface *ifp)
        return sizeof(o) + olen;
 }
 
+#ifndef SMALL
+/* DHCPv6 Option 16 (Vendor Class Option) */
 static size_t
 dhcp6_makevendor(void *data, const struct interface *ifp)
 {
        const struct if_options *ifo;
-       size_t len, vlen, i;
+       size_t len = 0, optlen, vlen, i;
        uint8_t *p;
        const struct vivco *vivco;
        struct dhcp6_option o;
 
        ifo = ifp->options;
-       len = sizeof(uint32_t); /* IANA PEN */
-       if (ifo->vivco_en) {
-               vlen = 0;
+       if (ifo->vivco_len > 0) {
                for (i = 0, vivco = ifo->vivco;
                    i < ifo->vivco_len;
                    i++, vivco++)
-                       vlen += sizeof(uint16_t) + vivco->len;
-               len += vlen;
+                       len += sizeof(o) + sizeof(uint32_t) + sizeof(uint16_t) + vivco->len;
        } else if (ifo->vendorclassid[0] != '\0') {
                /* dhcpcd owns DHCPCD_IANA_PEN.
                 * If you need your own string, get your own IANA PEN. */
                vlen = strlen(ifp->ctx->vendor);
-               len += sizeof(uint16_t) + vlen;
+               len += sizeof(o) + sizeof(uint32_t) + sizeof(uint16_t) + vlen;
        } else
                return 0;
 
@@ -312,19 +311,19 @@ dhcp6_makevendor(void *data, const struct interface *ifp)
                uint16_t hvlen;
 
                p = data;
-               o.code = htons(D6_OPTION_VENDOR_CLASS);
-               o.len = htons((uint16_t)len);
-               memcpy(p, &o, sizeof(o));
-               p += sizeof(o);
-               pen = htonl(ifo->vivco_en ? ifo->vivco_en : DHCPCD_IANA_PEN);
-               memcpy(p, &pen, sizeof(pen));
-               p += sizeof(pen);
 
-               if (ifo->vivco_en) {
+               if (ifo->vivco_len > 0) {
                        for (i = 0, vivco = ifo->vivco;
                            i < ifo->vivco_len;
-                           i++, vivco++)
-                       {
+                           i++, vivco++) {
+                               optlen = sizeof(uint32_t) + sizeof(uint16_t) + vivco->len;
+                               o.code = htons(D6_OPTION_VENDOR_CLASS);
+                               o.len = htons((uint16_t)optlen);
+                               memcpy(p, &o, sizeof(o));
+                               p += sizeof(o);
+                               pen = htonl(vivco->en);
+                               memcpy(p, &pen, sizeof(pen));
+                               p += sizeof(pen);
                                hvlen = htons((uint16_t)vivco->len);
                                memcpy(p, &hvlen, sizeof(hvlen));
                                p += sizeof(hvlen);
@@ -332,17 +331,22 @@ dhcp6_makevendor(void *data, const struct interface *ifp)
                                p += vivco->len;
                        }
                } else if (ifo->vendorclassid[0] != '\0') {
+                       o.code = htons(D6_OPTION_VENDOR_CLASS);
+                       o.len = htons((uint16_t)len);
+                       memcpy(p, &o, sizeof(o));
+                       p += sizeof(o);
+                       pen = htonl(DHCPCD_IANA_PEN);
+                       memcpy(p, &pen, sizeof(pen));
+                       p += sizeof(pen);
                        hvlen = htons((uint16_t)vlen);
                        memcpy(p, &hvlen, sizeof(hvlen));
                        p += sizeof(hvlen);
                        memcpy(p, ifp->ctx->vendor, vlen);
                }
        }
-
-       return sizeof(o) + len;
+       return len;
 }
 
-#ifndef SMALL
 /* DHCPv6 Option 17 (Vendor-Specific Information Option) */
 static size_t
 dhcp6_makevendoropts(void *data, const struct interface *ifp)
@@ -875,10 +879,10 @@ dhcp6_makemessage(struct interface *ifp)
 
        if (!has_option_mask(ifo->nomask6, D6_OPTION_USER_CLASS))
                len += dhcp6_makeuser(NULL, ifp);
-       if (!has_option_mask(ifo->nomask6, D6_OPTION_VENDOR_CLASS))
-               len += dhcp6_makevendor(NULL, ifp);
 
 #ifndef SMALL
+       if (!has_option_mask(ifo->nomask6, D6_OPTION_VENDOR_CLASS))
+               len += dhcp6_makevendor(NULL, ifp);
        if (!has_option_mask(ifo->nomask6, D6_OPTION_VENDOR_OPTS))
                len += dhcp6_makevendoropts(NULL, ifp);
 #endif
@@ -1199,10 +1203,10 @@ dhcp6_makemessage(struct interface *ifp)
 
        if (!has_option_mask(ifo->nomask6, D6_OPTION_USER_CLASS))
                p += dhcp6_makeuser(p, ifp);
-       if (!has_option_mask(ifo->nomask6, D6_OPTION_VENDOR_CLASS))
-               p += dhcp6_makevendor(p, ifp);
 
 #ifndef SMALL
+       if (!has_option_mask(ifo->nomask6, D6_OPTION_VENDOR_CLASS))
+               p += dhcp6_makevendor(p, ifp);
        if (!has_option_mask(ifo->nomask6, D6_OPTION_VENDOR_OPTS))
                p += dhcp6_makevendoropts(p, ifp);
 #endif
index c50f65a1c5389706b00f336b337bc6925cb56ad5..54ae590b4a5948d506431a92451d2a5c59080e2c 100644 (file)
@@ -656,6 +656,7 @@ parse_option(struct dhcpcd_ctx *ctx, const char *ifname, struct if_options *ifo,
        struct dhcp_opt **dop, *ndop;
        size_t *dop_len, dl, odl;
        struct vivco *vivco;
+       const struct vivco *vivco_endp = ifo->vivco + ifo->vivco_len;
        struct group *grp;
 #ifdef AUTH
        struct token *token;
@@ -2111,6 +2112,10 @@ err_sla:
                break;
        case O_VENDCLASS:
                ARG_REQUIRED;
+#ifdef SMALL
+                       logwarnx("%s: vendor options not compiled in", ifname);
+                       return -1;
+#else
                fp = strwhite(arg);
                if (fp)
                        *fp++ = '\0';
@@ -2119,6 +2124,12 @@ err_sla:
                        logerrx("invalid code: %s", arg);
                        return -1;
                }
+               for (vivco = ifo->vivco; vivco != vivco_endp; vivco++) {
+                       if (vivco->en == (uint32_t)u) {
+                               logerrx("vendor class option for enterprise number %u already defined", vivco->en);
+                               return -1;
+                       }
+               }
                fp = strskipwhite(fp);
                if (fp) {
                        s = parse_string(NULL, 0, fp);
@@ -2149,11 +2160,12 @@ err_sla:
                        return -1;
                }
                ifo->vivco = vivco;
-               ifo->vivco_en = (uint32_t)u;
                vivco = &ifo->vivco[ifo->vivco_len++];
+               vivco->en = (uint32_t)u;
                vivco->len = dl;
                vivco->data = (uint8_t *)np;
                break;
+#endif
        case O_AUTHPROTOCOL:
                ARG_REQUIRED;
 #ifdef AUTH
@@ -2994,12 +3006,12 @@ free_options(struct dhcpcd_ctx *ctx, struct if_options *ifo)
            opt++, ifo->dhcp6_override_len--)
                free_dhcp_opt_embenc(opt);
        free(ifo->dhcp6_override);
+#ifndef SMALL
        for (vo = ifo->vivco;
            ifo->vivco_len > 0;
            vo++, ifo->vivco_len--)
                free(vo->data);
        free(ifo->vivco);
-#ifndef SMALL
        for (vsio = ifo->vsio;
            ifo->vsio_len > 0;
            vsio++, ifo->vsio_len--)
index f3b9c17aa5e71e0befcfe514944506510c5fb22f..c892e75d5f8c33158a15283544fe2ad14d37cf51 100644 (file)
@@ -61,7 +61,6 @@
 #define USERCLASS_MAX_LEN      255
 #define VENDOR_MAX_LEN         255
 #define        MUDURL_MAX_LEN          255
-#define ENTERPRISE_NUMS_MAX_LEN        255
 
 #define DHCPCD_ARP                     (1ULL << 0)
 #define DHCPCD_RELEASE                 (1ULL << 1)
@@ -220,12 +219,12 @@ struct if_ia {
 #endif
 };
 
+#ifndef SMALL
 struct vivco {
+       uint32_t en;
        size_t len;
        uint8_t *data;
 };
-
-#ifndef SMALL
 struct vsio_so {
        uint16_t opt;
        uint16_t len;
@@ -303,13 +302,12 @@ struct if_options {
        size_t nd_override_len;
        struct dhcp_opt *dhcp6_override;
        size_t dhcp6_override_len;
-       uint32_t vivco_en;
-       struct vivco *vivco;
-       size_t vivco_len;
        struct dhcp_opt *vivso_override;
        size_t vivso_override_len;
 
 #ifndef SMALL
+       size_t vivco_len;
+       struct vivco *vivco;
        size_t vsio_len;
        struct vsio *vsio;
        size_t vsio6_len;