Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix using multiple enterprise IDs with vendclass (Option 124 DHCP / Option 16 DHCPv6) (#328) #408

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 23 additions & 24 deletions src/dhcp.c
Original file line number Diff line number Diff line change
Expand Up @@ -808,7 +808,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;
Expand Down Expand Up @@ -1137,35 +1136,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))
{
Expand Down
52 changes: 28 additions & 24 deletions src/dhcp6.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -312,37 +311,42 @@ 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);
memcpy(p, vivco->data, vivco->len);
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)
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
16 changes: 14 additions & 2 deletions src/if-options.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -2111,6 +2112,10 @@ parse_option(struct dhcpcd_ctx *ctx, const char *ifname, struct if_options *ifo,
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';
Expand All @@ -2119,6 +2124,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("vendor class option for enterprise number %u already defined", vivco->en);
return -1;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe a better text would be "vendor class option for enterprise number %u already defined".
We might also want to consider do we want to have a different option sent for the same enterprise number based on interface or profile name? If so, we might just want to silently override it instead.

Thoughts?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, it is better regarding the text.
Regarding the different option, I was going along with RFC 3925 that we should have only one enterprise number among all instances of this option.
How would this work with the silent override?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would just free what was there and put new data in?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for the confusion, I meant about having a different option sent for the same enterprise number based on interface or profile name. Haven't worked with multiple profiles/interfaces tbh.

}
}
fp = strskipwhite(fp);
if (fp) {
s = parse_string(NULL, 0, fp);
Expand Down Expand Up @@ -2149,11 +2160,12 @@ 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;
#endif
case O_AUTHPROTOCOL:
ARG_REQUIRED;
#ifdef AUTH
Expand Down Expand Up @@ -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--)
Expand Down
10 changes: 4 additions & 6 deletions src/if-options.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down
Loading