Skip to content

Commit 58498b2

Browse files
authored
Fix using multiple enterprise IDs with vendclass (Option 124 DHCP / Option 16 DHCPv6)
Fixes #328
1 parent 7b94a2e commit 58498b2

File tree

4 files changed

+69
-56
lines changed

4 files changed

+69
-56
lines changed

src/dhcp.c

Lines changed: 23 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -809,7 +809,6 @@ make_message(struct bootp **bootpm, const struct interface *ifp, uint8_t type)
809809
const struct dhcp_lease *lease = &state->lease;
810810
char hbuf[HOSTNAME_MAX_LEN + 1];
811811
const char *hostname;
812-
const struct vivco *vivco;
813812
int mtu;
814813
#ifdef AUTH
815814
uint8_t *auth, auth_len;
@@ -1138,35 +1137,35 @@ make_message(struct bootp **bootpm, const struct interface *ifp, uint8_t type)
11381137
p += ifo->mudurl[0] + 1;
11391138
}
11401139

1140+
#ifndef SMALL
11411141
if (ifo->vivco_len &&
11421142
!has_option_mask(ifo->nomask, DHO_VIVCO))
11431143
{
1144-
AREA_CHECK(sizeof(ul));
1145-
*p++ = DHO_VIVCO;
1146-
lp = p++;
1147-
*lp = sizeof(ul);
1148-
ul = htonl(ifo->vivco_en);
1149-
memcpy(p, &ul, sizeof(ul));
1150-
p += sizeof(ul);
1151-
for (i = 0, vivco = ifo->vivco;
1152-
i < ifo->vivco_len;
1153-
i++, vivco++)
1154-
{
1155-
AREA_FIT(vivco->len);
1156-
if (vivco->len + 2 + *lp > 255) {
1157-
logerrx("%s: VIVCO option too big",
1158-
ifp->name);
1159-
free(bootp);
1160-
return -1;
1161-
}
1162-
*p++ = (uint8_t)vivco->len;
1163-
memcpy(p, vivco->data, vivco->len);
1164-
p += vivco->len;
1144+
struct vivco *vivco = ifo->vivco;
1145+
size_t vlen = ifo->vivco_len;
1146+
struct rfc3396_ctx rctx = {
1147+
.code = DHO_VIVCO,
1148+
.buf = &p,
1149+
.buflen = AREA_LEFT,
1150+
};
1151+
1152+
for (; vlen > 0; vivco++, vlen--) {
1153+
ul = htonl(vivco->en);
1154+
if (rfc3396_write(&rctx, &ul, sizeof(ul)) == -1)
1155+
goto toobig;
1156+
lp = rfc3396_zero(&rctx);
1157+
if (lp == NULL)
1158+
goto toobig;
1159+
if (rfc3396_write_byte(&rctx,
1160+
(uint8_t)vivco->len) == -1)
1161+
goto toobig;
1162+
if (rfc3396_write(&rctx,
1163+
vivco->data, vivco->len) == -1)
1164+
goto toobig;
11651165
*lp = (uint8_t)(*lp + vivco->len + 1);
11661166
}
11671167
}
1168-
1169-
#ifndef SMALL
1168+
11701169
if (ifo->vsio_len &&
11711170
!has_option_mask(ifo->nomask, DHO_VIVSO))
11721171
{

src/dhcp6.c

Lines changed: 28 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -276,29 +276,28 @@ dhcp6_makeuser(void *data, const struct interface *ifp)
276276
return sizeof(o) + olen;
277277
}
278278

279+
#ifndef SMALL
280+
/* DHCPv6 Option 16 (Vendor Class Option) */
279281
static size_t
280282
dhcp6_makevendor(void *data, const struct interface *ifp)
281283
{
282284
const struct if_options *ifo;
283-
size_t len, vlen, i;
285+
size_t len = 0, optlen, vlen, i;
284286
uint8_t *p;
285287
const struct vivco *vivco;
286288
struct dhcp6_option o;
287289

288290
ifo = ifp->options;
289-
len = sizeof(uint32_t); /* IANA PEN */
290-
if (ifo->vivco_en) {
291-
vlen = 0;
291+
if (ifo->vivco_len > 0) {
292292
for (i = 0, vivco = ifo->vivco;
293293
i < ifo->vivco_len;
294294
i++, vivco++)
295-
vlen += sizeof(uint16_t) + vivco->len;
296-
len += vlen;
295+
len += sizeof(o) + sizeof(uint32_t) + sizeof(uint16_t) + vivco->len;
297296
} else if (ifo->vendorclassid[0] != '\0') {
298297
/* dhcpcd owns DHCPCD_IANA_PEN.
299298
* If you need your own string, get your own IANA PEN. */
300299
vlen = strlen(ifp->ctx->vendor);
301-
len += sizeof(uint16_t) + vlen;
300+
len += sizeof(o) + sizeof(uint32_t) + sizeof(uint16_t) + vlen;
302301
} else
303302
return 0;
304303

@@ -312,37 +311,42 @@ dhcp6_makevendor(void *data, const struct interface *ifp)
312311
uint16_t hvlen;
313312

314313
p = data;
315-
o.code = htons(D6_OPTION_VENDOR_CLASS);
316-
o.len = htons((uint16_t)len);
317-
memcpy(p, &o, sizeof(o));
318-
p += sizeof(o);
319-
pen = htonl(ifo->vivco_en ? ifo->vivco_en : DHCPCD_IANA_PEN);
320-
memcpy(p, &pen, sizeof(pen));
321-
p += sizeof(pen);
322314

323-
if (ifo->vivco_en) {
315+
if (ifo->vivco_len > 0) {
324316
for (i = 0, vivco = ifo->vivco;
325317
i < ifo->vivco_len;
326-
i++, vivco++)
327-
{
318+
i++, vivco++) {
319+
optlen = sizeof(uint32_t) + sizeof(uint16_t) + vivco->len;
320+
o.code = htons(D6_OPTION_VENDOR_CLASS);
321+
o.len = htons((uint16_t)optlen);
322+
memcpy(p, &o, sizeof(o));
323+
p += sizeof(o);
324+
pen = htonl(vivco->en);
325+
memcpy(p, &pen, sizeof(pen));
326+
p += sizeof(pen);
328327
hvlen = htons((uint16_t)vivco->len);
329328
memcpy(p, &hvlen, sizeof(hvlen));
330329
p += sizeof(hvlen);
331330
memcpy(p, vivco->data, vivco->len);
332331
p += vivco->len;
333332
}
334333
} else if (ifo->vendorclassid[0] != '\0') {
334+
o.code = htons(D6_OPTION_VENDOR_CLASS);
335+
o.len = htons((uint16_t)len);
336+
memcpy(p, &o, sizeof(o));
337+
p += sizeof(o);
338+
pen = htonl(DHCPCD_IANA_PEN);
339+
memcpy(p, &pen, sizeof(pen));
340+
p += sizeof(pen);
335341
hvlen = htons((uint16_t)vlen);
336342
memcpy(p, &hvlen, sizeof(hvlen));
337343
p += sizeof(hvlen);
338344
memcpy(p, ifp->ctx->vendor, vlen);
339345
}
340346
}
341-
342-
return sizeof(o) + len;
347+
return len;
343348
}
344349

345-
#ifndef SMALL
346350
/* DHCPv6 Option 17 (Vendor-Specific Information Option) */
347351
static size_t
348352
dhcp6_makevendoropts(void *data, const struct interface *ifp)
@@ -875,10 +879,10 @@ dhcp6_makemessage(struct interface *ifp)
875879

876880
if (!has_option_mask(ifo->nomask6, D6_OPTION_USER_CLASS))
877881
len += dhcp6_makeuser(NULL, ifp);
878-
if (!has_option_mask(ifo->nomask6, D6_OPTION_VENDOR_CLASS))
879-
len += dhcp6_makevendor(NULL, ifp);
880882

881883
#ifndef SMALL
884+
if (!has_option_mask(ifo->nomask6, D6_OPTION_VENDOR_CLASS))
885+
len += dhcp6_makevendor(NULL, ifp);
882886
if (!has_option_mask(ifo->nomask6, D6_OPTION_VENDOR_OPTS))
883887
len += dhcp6_makevendoropts(NULL, ifp);
884888
#endif
@@ -1199,10 +1203,10 @@ dhcp6_makemessage(struct interface *ifp)
11991203

12001204
if (!has_option_mask(ifo->nomask6, D6_OPTION_USER_CLASS))
12011205
p += dhcp6_makeuser(p, ifp);
1202-
if (!has_option_mask(ifo->nomask6, D6_OPTION_VENDOR_CLASS))
1203-
p += dhcp6_makevendor(p, ifp);
12041206

12051207
#ifndef SMALL
1208+
if (!has_option_mask(ifo->nomask6, D6_OPTION_VENDOR_CLASS))
1209+
p += dhcp6_makevendor(p, ifp);
12061210
if (!has_option_mask(ifo->nomask6, D6_OPTION_VENDOR_OPTS))
12071211
p += dhcp6_makevendoropts(p, ifp);
12081212
#endif

src/if-options.c

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -656,6 +656,7 @@ parse_option(struct dhcpcd_ctx *ctx, const char *ifname, struct if_options *ifo,
656656
struct dhcp_opt **dop, *ndop;
657657
size_t *dop_len, dl, odl;
658658
struct vivco *vivco;
659+
const struct vivco *vivco_endp = ifo->vivco + ifo->vivco_len;
659660
struct group *grp;
660661
#ifdef AUTH
661662
struct token *token;
@@ -2111,6 +2112,10 @@ parse_option(struct dhcpcd_ctx *ctx, const char *ifname, struct if_options *ifo,
21112112
break;
21122113
case O_VENDCLASS:
21132114
ARG_REQUIRED;
2115+
#ifdef SMALL
2116+
logwarnx("%s: vendor options not compiled in", ifname);
2117+
return -1;
2118+
#else
21142119
fp = strwhite(arg);
21152120
if (fp)
21162121
*fp++ = '\0';
@@ -2119,6 +2124,12 @@ parse_option(struct dhcpcd_ctx *ctx, const char *ifname, struct if_options *ifo,
21192124
logerrx("invalid code: %s", arg);
21202125
return -1;
21212126
}
2127+
for (vivco = ifo->vivco; vivco != vivco_endp; vivco++) {
2128+
if (vivco->en == (uint32_t)u) {
2129+
logerrx("vendor class option for enterprise number %u already defined", vivco->en);
2130+
return -1;
2131+
}
2132+
}
21222133
fp = strskipwhite(fp);
21232134
if (fp) {
21242135
s = parse_string(NULL, 0, fp);
@@ -2149,11 +2160,12 @@ parse_option(struct dhcpcd_ctx *ctx, const char *ifname, struct if_options *ifo,
21492160
return -1;
21502161
}
21512162
ifo->vivco = vivco;
2152-
ifo->vivco_en = (uint32_t)u;
21532163
vivco = &ifo->vivco[ifo->vivco_len++];
2164+
vivco->en = (uint32_t)u;
21542165
vivco->len = dl;
21552166
vivco->data = (uint8_t *)np;
21562167
break;
2168+
#endif
21572169
case O_AUTHPROTOCOL:
21582170
ARG_REQUIRED;
21592171
#ifdef AUTH
@@ -2994,12 +3006,12 @@ free_options(struct dhcpcd_ctx *ctx, struct if_options *ifo)
29943006
opt++, ifo->dhcp6_override_len--)
29953007
free_dhcp_opt_embenc(opt);
29963008
free(ifo->dhcp6_override);
3009+
#ifndef SMALL
29973010
for (vo = ifo->vivco;
29983011
ifo->vivco_len > 0;
29993012
vo++, ifo->vivco_len--)
30003013
free(vo->data);
30013014
free(ifo->vivco);
3002-
#ifndef SMALL
30033015
for (vsio = ifo->vsio;
30043016
ifo->vsio_len > 0;
30053017
vsio++, ifo->vsio_len--)

src/if-options.h

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,6 @@
6161
#define USERCLASS_MAX_LEN 255
6262
#define VENDOR_MAX_LEN 255
6363
#define MUDURL_MAX_LEN 255
64-
#define ENTERPRISE_NUMS_MAX_LEN 255
6564

6665
#define DHCPCD_ARP (1ULL << 0)
6766
#define DHCPCD_RELEASE (1ULL << 1)
@@ -220,12 +219,12 @@ struct if_ia {
220219
#endif
221220
};
222221

222+
#ifndef SMALL
223223
struct vivco {
224+
uint32_t en;
224225
size_t len;
225226
uint8_t *data;
226227
};
227-
228-
#ifndef SMALL
229228
struct vsio_so {
230229
uint16_t opt;
231230
uint16_t len;
@@ -303,13 +302,12 @@ struct if_options {
303302
size_t nd_override_len;
304303
struct dhcp_opt *dhcp6_override;
305304
size_t dhcp6_override_len;
306-
uint32_t vivco_en;
307-
struct vivco *vivco;
308-
size_t vivco_len;
309305
struct dhcp_opt *vivso_override;
310306
size_t vivso_override_len;
311307

312308
#ifndef SMALL
309+
size_t vivco_len;
310+
struct vivco *vivco;
313311
size_t vsio_len;
314312
struct vsio *vsio;
315313
size_t vsio6_len;

0 commit comments

Comments
 (0)