From c0a58f445475bfb91775adef445addd73d85b34e Mon Sep 17 00:00:00 2001 From: spoljak-ent Date: Thu, 21 Nov 2024 07:57:37 +0000 Subject: [PATCH] Fix using multiple enterprise IDs (Option 124 DHCP / 17 DHCP6) (#328) --- src/dhcp.c | 41 ++++++++++++++++++++++------------------- src/dhcp6.c | 41 ++++++++++++++++++++++------------------- src/if-options.c | 9 ++++++++- src/if-options.h | 3 +-- 4 files changed, 53 insertions(+), 41 deletions(-) diff --git a/src/dhcp.c b/src/dhcp.c index 11c69bc1..1966afee 100644 --- a/src/dhcp.c +++ b/src/dhcp.c @@ -808,7 +808,7 @@ 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; + const struct vivco *vivco, *vivco_endp = ifo->vivco + ifo->vivco_len;; int mtu; #ifdef AUTH uint8_t *auth, auth_len; @@ -1142,26 +1142,29 @@ make_message(struct bootp **bootpm, const struct interface *ifp, uint8_t type) { 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; + size_t totallen = 0; + uint8_t datalen, datalenopt; + for (vivco = ifo->vivco; vivco != vivco_endp; vivco++) + totallen += sizeof(uint32_t) + 2 * sizeof(uint8_t) + vivco->len; + if (totallen > UINT8_MAX) { + logerrx("%s: VIVCO option too big", + ifp->name); + free(bootp); + return -1; + } + *p++ = (uint8_t)totallen; + for (vivco = ifo->vivco; vivco != vivco_endp; vivco++) { + ul = htonl(vivco->en); + memcpy(p, &ul, sizeof(ul)); + p += sizeof(ul); + datalen = (uint8_t)(sizeof(uint8_t) + vivco->len); + memcpy(p, &datalen, sizeof(datalen)); + p += sizeof(datalen); // OK do tu + datalenopt = (uint8_t)(vivco->len); + memcpy(p, &datalenopt, sizeof(datalenopt)); + p += sizeof(datalenopt); memcpy(p, vivco->data, vivco->len); p += vivco->len; - *lp = (uint8_t)(*lp + vivco->len + 1); } } diff --git a/src/dhcp6.c b/src/dhcp6.c index ca076e4e..48557d1b 100644 --- a/src/dhcp6.c +++ b/src/dhcp6.c @@ -280,25 +280,22 @@ 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 +309,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,14 +329,20 @@ 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 diff --git a/src/if-options.c b/src/if-options.c index c50f65a1..817f2b89 100644 --- a/src/if-options.c +++ b/src/if-options.c @@ -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; @@ -2119,6 +2120,12 @@ parse_option(struct dhcpcd_ctx *ctx, const char *ifname, struct if_options *ifo, logerrx("invalid code: %s", arg); return -1; } + for (vivco = ifo->vivco; vivco != vivco_endp; vivco++) { + if (vivco->en == (uint32_t)u) { + logerrx("only one vendor class option per enterprise number"); + return -1; + } + } fp = strskipwhite(fp); if (fp) { s = parse_string(NULL, 0, fp); @@ -2149,8 +2156,8 @@ parse_option(struct dhcpcd_ctx *ctx, const char *ifname, struct if_options *ifo, 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; diff --git a/src/if-options.h b/src/if-options.h index f3b9c17a..5fc57ec3 100644 --- a/src/if-options.h +++ b/src/if-options.h @@ -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) @@ -221,6 +220,7 @@ struct if_ia { }; struct vivco { + uint32_t en; size_t len; uint8_t *data; }; @@ -303,7 +303,6 @@ 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;