changeset 2194:e65989e20e61 draft

Add support for RFC3925 Vendor-Identifying Vendor Options
author Roy Marples <roy@marples.name>
date Fri, 06 Dec 2013 17:47:53 +0000
parents 5a75bc2628f5
children 97118e44508a
files dhcp-common.c dhcp-common.h dhcp.c dhcp.h dhcp6.c dhcp6.h dhcpcd-definitions.conf dhcpcd.8.in dhcpcd.c dhcpcd.conf.5.in if-options.c if-options.h
diffstat 12 files changed, 337 insertions(+), 125 deletions(-) [+]
line wrap: on
line diff
--- a/dhcp-common.c	Fri Dec 06 17:47:52 2013 +0000
+++ b/dhcp-common.c	Fri Dec 06 17:47:53 2013 +0000
@@ -38,21 +38,39 @@
 #include "dhcp-common.h"
 #include "dhcp.h"
 
-#ifdef INET
-struct dhcp_opt *dhcp_override = NULL;
-size_t dhcp_override_len = 0;
-#endif
-#ifdef INET6
-struct dhcp_opt *dhcp6_override = NULL;
-size_t dhcp6_override_len = 0;
-#endif
+/* DHCP Enterprise options, RFC3925 */
+struct dhcp_opt *vivso = NULL;
+size_t vivso_len = 0;
+
+struct dhcp_opt *
+vivso_find(uint16_t iana_en, const void *arg)
+{
+	const struct interface *ifp = arg;
+	size_t i;
+	struct dhcp_opt *opt;
 
-int make_option_mask(const struct dhcp_opt *dopts, size_t dopts_len,
+	if (arg) {
+		ifp = arg;
+		for (i = 0, opt = ifp->options->vivso_override;
+		    i < ifp->options->vivso_override_len;
+		    i++, opt++)
+			if (opt->option == iana_en)
+				return opt;
+	}
+	for (i = 0, opt = vivso; i < vivso_len; i++, opt++)
+		if (opt->option == iana_en)
+			return opt;
+	return NULL;
+}
+
+int
+make_option_mask(const struct dhcp_opt *dopts, size_t dopts_len,
     uint8_t *mask, const char *opts, int add)
 {
 	char *token, *o, *p, *t;
 	const struct dhcp_opt *opt;
-	int match, n;
+	int match;
+	unsigned int n;
 	size_t i;
 
 	o = p = strdup(opts);
@@ -535,15 +553,15 @@
 ssize_t
 dhcp_envoption(char **env, const char *prefix,
     const char *ifname, struct dhcp_opt *opt,
-    const uint8_t *(*dgetopt)(int *, int *, int *, const uint8_t *, int,
-        struct dhcp_opt **),
+    const uint8_t *(*dgetopt)(unsigned int *, unsigned int *, unsigned int *,
+    const uint8_t *, unsigned int, struct dhcp_opt **),
     const uint8_t *od, int ol)
 {
 	ssize_t e, n;
 	size_t i;
-	int eoc;
+	unsigned int eoc, eos, eol;
 	const uint8_t *eod;
-	int eos, eol, ov;
+	int ov;
 	struct dhcp_opt *eopt, *oopt;
 	char *pfx;
 
--- a/dhcp-common.h	Fri Dec 06 17:47:52 2013 +0000
+++ b/dhcp-common.h	Fri Dec 06 17:47:53 2013 +0000
@@ -62,7 +62,7 @@
 #define OPTION		(1 << 20)
 
 struct dhcp_opt {
-	uint16_t option;
+	uint32_t option; /* Also used for IANA Enterpise Number */
 	int type;
 	int len;
 	char *var;
@@ -79,6 +79,12 @@
 	size_t encopts_len;
 };
 
+/* DHCP Vendor-Identifying Vendor Options, RFC3925 */
+extern struct dhcp_opt *vivso;
+extern size_t vivso_len;
+
+struct dhcp_opt *vivso_find(uint16_t, const void *);
+
 #define add_option_mask(var, val) (var[val >> 3] |= 1 << (val & 7))
 #define del_option_mask(var, val) (var[val >> 3] &= ~(1 << (val & 7)))
 #define has_option_mask(var, val) (var[val >>3] & (1 << (val & 7)))
@@ -91,8 +97,8 @@
 ssize_t print_option(char *, ssize_t, int, int, const uint8_t *, const char *);
 
 ssize_t dhcp_envoption(char **, const char *, const char *, struct dhcp_opt *,
-    const uint8_t *(*dgetopt)(int *, int *, int *, const uint8_t *, int,
-        struct dhcp_opt **),
+    const uint8_t *(*dgetopt)(unsigned int *, unsigned int *, unsigned int *,
+    const uint8_t *, unsigned int, struct dhcp_opt **),
     const uint8_t *od, int ol);
 void dhcp_zero_index(struct dhcp_opt *);
 
--- a/dhcp.c	Fri Dec 06 17:47:52 2013 +0000
+++ b/dhcp.c	Fri Dec 06 17:47:53 2013 +0000
@@ -684,6 +684,7 @@
 	const struct dhcp_lease *lease = &state->lease;
 	time_t up = uptime() - state->start_uptime;
 	const char *hostname;
+	const struct vivco *vivco;
 
 	dhcp = calloc(1, sizeof (*dhcp));
 	if (dhcp == NULL)
@@ -864,6 +865,37 @@
 			p += ifo->vendor[0] + 1;
 		}
 
+		if (ifo->vivco_len) {
+			*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++)
+			{
+				len = (p - m) + vivco->len + 1;
+				if (len > sizeof(*dhcp))
+					goto toobig;
+				if (vivco->len + 2 + *lp > 255) {
+					syslog(LOG_ERR,
+					    "%s: VIVCO option too big",
+					    iface->name);
+					free(dhcp);
+					return -1;
+				}
+				*p++ = (uint8_t)vivco->len;
+				memcpy(p, vivco->data, vivco->len);
+				p += vivco->len;
+				*lp += (uint8_t)vivco->len + 1;
+			}
+		}
+
+		len = (p - m) + 3;
+		if (len > sizeof(*dhcp))
+			goto toobig;
 		*p++ = DHO_PARAMETERREQUESTLIST;
 		n_params = p;
 		*p++ = 0;
@@ -877,6 +909,9 @@
 			    (opt->option == DHO_RENEWALTIME ||
 				opt->option == DHO_REBINDTIME))
 				continue;
+			len = (p - m) + 2;
+			if (len > sizeof(*dhcp))
+				goto toobig;
 			*p++ = opt->option;
 		}
 		*n_params = p - n_params - 1;
@@ -893,6 +928,11 @@
 
 	*message = dhcp;
 	return p - m;
+
+toobig:
+	syslog(LOG_ERR, "%s: DHCP messge too big", iface->name);
+	free(dhcp);
+	return -1;
 }
 
 ssize_t
@@ -985,21 +1025,21 @@
 }
 
 static const uint8_t *
-dhcp_getoption(int *os, int *code, int *len, const uint8_t *od, int ol,
-   struct dhcp_opt **oopt)
+dhcp_getoption(unsigned int *os, unsigned int *code, unsigned int *len,
+    const uint8_t *od, unsigned int ol, struct dhcp_opt **oopt)
 {
 	size_t i;
 	struct dhcp_opt *opt;
 
 	if (od) {
-		if (ol < 0) {
+		if (ol < 2) {
 			errno = EINVAL;
 			return NULL;
 		}
 		*os = 2; /* code + len */
 		*code = (int)*od++;
 		*len = (int)*od++;
-		if (*len < 0 || *len > ol) {
+		if (*len > ol) {
 			errno = EINVAL;
 			return NULL;
 		}
@@ -1025,12 +1065,13 @@
 	struct in_addr addr;
 	struct in_addr net;
 	struct in_addr brd;
-	struct dhcp_opt *opt;
+	struct dhcp_opt *opt, *vo;
 	ssize_t e = 0;
 	char **ep;
 	char cidr[4];
 	uint8_t overl = 0;
 	size_t i;
+	uint32_t en;
 
 	ifo = ifp->options;
 	get_option_uint8(&overl, dhcp, DHO_OPTIONSOVERLOADED);
@@ -1106,6 +1147,8 @@
 		    i < ifp->options->dhcp_override_len;
 		    i++, opt++)
 			dhcp_zero_index(opt);
+		for (i = 0, opt = vivso; i < vivso_len; i++, opt++)
+			dhcp_zero_index(opt);
 	}
 
 	for (i = 0, opt = dhcp_opts;
@@ -1119,6 +1162,20 @@
 		if ((p = get_option(dhcp, opt->option, &pl)))
 			ep += dhcp_envoption(ep, prefix, ifp->name,
 			    opt, dhcp_getoption, p, pl);
+		/* Grab the Vendor-Identifying Vendor Options, RFC 3925 */
+		if (opt->option == DHO_VIVSO && pl > (int)sizeof(uint32_t)) {
+			memcpy(&en, p, sizeof(en));
+			en = ntohl(en);
+			vo = vivso_find(en, ifp);
+			if (vo) {
+				/* Skip over en + total size */
+				p += sizeof(en) + 1;
+				pl -= sizeof(en) + 1;
+				ep += dhcp_envoption(ep, prefix, ifp->name,
+				    vo, dhcp_getoption, p, pl);
+				printf ("%p\n", ep);
+			}
+		}
 	}
 
 	for (i = 0, opt = ifo->dhcp_override;
--- a/dhcp.h	Fri Dec 06 17:47:52 2013 +0000
+++ b/dhcp.h	Fri Dec 06 17:47:53 2013 +0000
@@ -105,6 +105,8 @@
 	DHO_USERCLASS              = 77,  /* RFC 3004 */
 	DHO_RAPIDCOMMIT            = 80,  /* RFC 4039 */
 	DHO_FQDN                   = 81,
+	DHO_VIVCO                  = 124, /* RFC 3925 */
+	DHO_VIVSO                  = 125, /* RFC 3925 */
 	DHO_DNSSEARCH              = 119, /* RFC 3397 */
 	DHO_CSR                    = 121, /* RFC 3442 */
 	DHO_SIXRD                  = 212, /* RFC 5969 */
--- a/dhcp6.c	Fri Dec 06 17:47:52 2013 +0000
+++ b/dhcp6.c	Fri Dec 06 17:47:53 2013 +0000
@@ -116,6 +116,8 @@
 	{ DHO_NTPSERVER,	D6_OPTION_SNTP_SERVERS },
 	{ DHO_RAPIDCOMMIT,	D6_OPTION_RAPID_COMMIT },
 	{ DHO_FQDN,		D6_OPTION_FQDN },
+	{ DHO_VIVCO,		D6_OPTION_VENDOR_CLASS },
+	{ DHO_VIVSO,		D6_OPTION_VENDOR_OPTS },
 	{ DHO_DNSSEARCH,	D6_OPTION_DOMAIN_LIST },
 	{ 0, 0 }
 };
@@ -178,89 +180,63 @@
 	return 0;
 }
 
-#ifdef DHCPCD_IANA_PEN
 static size_t
-dhcp6_makevendor(struct dhcp6_option *o)
+dhcp6_makevendor(struct dhcp6_option *o, const struct interface *ifp)
 {
+	const struct if_options *ifo;
 	size_t len;
 	uint8_t *p;
 	uint16_t u16;
 	uint32_t u32;
-	size_t vlen;
-#ifdef VENDOR_SPLIT
-	const char *platform;
-	size_t plen, unl, url, uml, pl;
-	struct utsname utn;
-#endif
-
-	len = sizeof(uint32_t); /* IANA PEN */
+	size_t vlen, i;
+	const struct vivco *vivco;
 
-#ifdef VENDOR_SPLIT
-	plen = strlen(PACKAGE);
-	vlen = strlen(VERSION);
-	len += sizeof(uint16_t) + plen + 1 + vlen;
-	if (uname(&utn) == 0) {
-		unl = strlen(utn.sysname);
-		url = strlen(utn.release);
-		uml = strlen(utn.machine);
-		platform = hardware_platform();
-		pl = strlen(platform);
-		len += sizeof(uint16_t) + unl + 1 + url;
-		len += sizeof(uint16_t) + uml;
-		len += sizeof(uint16_t) + pl;
-	} else
-		unl = 0;
-#else
-	vlen = strlen(vendor);
-	len += sizeof(uint16_t) + vlen;
-#endif
+	ifo = ifp->options;
+	len = sizeof(uint32_t); /* IANA PEN */
+	if (ifo->vivco_en) {
+		for (i = 0, vivco = ifo->vivco;
+		    i < ifo->vivco_len;
+		    i++, vivco++)
+			len += sizeof(uint16_t) + vivco->len;
+		vlen = 0; /* silence bogus gcc warning */
+	} else {
+		vlen = strlen(vendor);
+		len += sizeof(uint16_t) + vlen;
+	}
+
+	if (len > UINT16_MAX) {
+		syslog(LOG_ERR, "%s: DHCPv6 Vendor Class too big", ifp->name);
+		return 0;
+	}
 
 	if (o) {
-		o->code = htons(D6_OPTION_VENDOR);
+		o->code = htons(D6_OPTION_VENDOR_CLASS);
 		o->len = htons(len);
 		p = D6_OPTION_DATA(o);
-		u32 = htonl(DHCPCD_IANA_PEN);
+		u32 = htonl(ifo->vivco_en ? ifo->vivco_en : DHCPCD_IANA_PEN);
 		memcpy(p, &u32, sizeof(u32));
 		p += sizeof(u32);
-#ifdef VENDOR_SPLIT
-		u16 = htons(plen + 1 + vlen);
-		memcpy(p, &u16, sizeof(u16));
-		p += sizeof(u16);
-		memcpy(p, PACKAGE, plen);
-		p += plen;
-		*p++ = '-';
-		memcpy(p, VERSION, vlen);
-		p += vlen;
-		if (unl > 0) {
-			u16 = htons(unl + 1 + url);
+		if (ifo->vivco_en) {
+			for (i = 0, vivco = ifo->vivco;
+			    i < ifo->vivco_len;
+			    i++, vivco++)
+			{
+				u16 = htons(vivco->len);
+				memcpy(p, &u16, sizeof(u16));
+				p += sizeof(u16);
+				memcpy(p, vivco->data, vivco->len);
+				p += vivco->len;
+			}
+		} else {
+			u16 = htons(vlen);
 			memcpy(p, &u16, sizeof(u16));
 			p += sizeof(u16);
-			memcpy(p, utn.sysname, unl);
-			p += unl;
-			*p++ = '-';
-			memcpy(p, utn.release, url);
-			p += url;
-			u16 = htons(uml);
-			memcpy(p, &u16, sizeof(u16));
-			p += sizeof(u16);
-			memcpy(p, utn.machine, uml);
-			p += uml;
-			u16 = htons(pl);
-			memcpy(p, &u16, sizeof(u16));
-			p += sizeof(u16);
-			memcpy(p, platform, pl);
+			memcpy(p, vendor, vlen);
 		}
-#else
-		u16 = htons(vlen);
-		memcpy(p, &u16, sizeof(u16));
-		p += sizeof(u16);
-		memcpy(p, vendor, vlen);
-#endif
 	}
 
 	return len;
 }
-#endif
 
 static const struct dhcp6_option *
 dhcp6_findoption(int code, const uint8_t *d, ssize_t len)
@@ -286,8 +262,8 @@
 }
 
 static const uint8_t *
-dhcp6_getoption(int *os, int *code, int *len, const uint8_t *od, int ol,
-    struct dhcp_opt **oopt)
+dhcp6_getoption(unsigned int *os, unsigned int *code, unsigned int *len,
+    const uint8_t *od, unsigned int ol, struct dhcp_opt **oopt)
 {
 	const struct dhcp6_option *o;
 	size_t i;
@@ -301,7 +277,7 @@
 		}
 		o = (const struct dhcp6_option *)od;
 		*len = ntohs(o->len);
-		if (*len < 0 || *len > ol) {
+		if (*len > ol) {
 			errno = EINVAL;
 			return NULL;
 		}
@@ -436,9 +412,7 @@
 	len += sizeof(*state->send);
 	len += sizeof(*o) + duid_len;
 	len += sizeof(*o) + sizeof(uint16_t); /* elapsed */
-#ifdef DHCPCD_IANA_PEN
-	len += sizeof(*o) + dhcp6_makevendor(NULL);
-#endif
+	len += sizeof(*o) + dhcp6_makevendor(NULL, ifp);
 
 	/* IA */
 	m = NULL;
@@ -562,10 +536,8 @@
 	p = D6_OPTION_DATA(o);
 	memset(p, 0, sizeof(u16));
 
-#ifdef DHCPCD_IANA_PEN
 	o = D6_NEXT_OPTION(o);
-	dhcp6_makevendor(o);
-#endif
+	dhcp6_makevendor(o, ifp);
 
 	if (state->state == DH6S_DISCOVER &&
 	    !(options & DHCPCD_TEST) &&
@@ -2579,12 +2551,13 @@
 {
 	const struct dhcp6_state *state;
 	const struct if_options *ifo;
-	struct dhcp_opt *opt;
+	struct dhcp_opt *opt, *vo;
 	const struct dhcp6_option *o;
 	size_t i, n;
 	uint16_t ol, oc;
 	char *v, *val, *pfx;
 	const struct ipv6_addr *ap;
+	uint32_t en;
 
 	state = D6_CSTATE(ifp);
 	n = 0;
@@ -2598,6 +2571,8 @@
 		    i < ifp->options->dhcp6_override_len;
 		    i++, opt++)
 			dhcp_zero_index(opt);
+		for (i = 0, opt = vivso; i < vivso_len; i++, opt++)
+			dhcp_zero_index(opt);
 		i = strlen(prefix) + strlen("_dhcp6") + 1;
 		pfx = malloc(i);
 		if (pfx == NULL) {
@@ -2628,6 +2603,15 @@
 		    i++, opt++)
 			if (opt->option == oc)
 				break;
+		if (i == ifo->dhcp6_override_len &&
+		    oc == D6_OPTION_VENDOR_OPTS &&
+		    ol > sizeof(en))
+		{
+			memcpy(&en, D6_COPTION_DATA(o), sizeof(en));
+			en = ntohl(en);
+			vo = vivso_find(en, ifp);
+		} else
+			vo = NULL;
 		if (i == ifo->dhcp6_override_len) {
 			for (i = 0, opt = dhcp6_opts;
 			    i < dhcp6_opts_len;
@@ -2642,6 +2626,13 @@
 			    pfx, ifp->name,
 			    opt, dhcp6_getoption, D6_COPTION_DATA(o), ol);
 		}
+		if (vo) {
+			n += dhcp_envoption(env == NULL ? NULL : &env[n],
+			    pfx, ifp->name,
+			    vo, dhcp6_getoption,
+			    D6_COPTION_DATA(o) + sizeof(en),
+			    ol - sizeof(en));
+		}
 	}
 	free(pfx);
 
--- a/dhcp6.h	Fri Dec 06 17:47:52 2013 +0000
+++ b/dhcp6.h	Fri Dec 06 17:47:53 2013 +0000
@@ -64,7 +64,8 @@
 #define D6_OPTION_UNICAST		12
 #define D6_OPTION_STATUS_CODE		13
 #define D6_OPTION_RAPID_COMMIT		14
-#define D6_OPTION_VENDOR		16
+#define D6_OPTION_VENDOR_CLASS		16
+#define D6_OPTION_VENDOR_OPTS		17
 #define D6_OPTION_SIP_SERVERS_NAME	21
 #define D6_OPTION_SIP_SERVERS_ADDRESS	22
 #define D6_OPTION_DNS_SERVERS		23
--- a/dhcpcd-definitions.conf	Fri Dec 06 17:47:52 2013 +0000
+++ b/dhcpcd-definitions.conf	Fri Dec 06 17:47:53 2013 +0000
@@ -70,6 +70,8 @@
 define 57	uint16			dhcp_max_message_size
 define 58	request uint32		dhcp_renewal_time
 define 59	request uint32		dhcp_rebinding_time
+define 60	binhex			vendor_class_identifier
+define 61	binhex			dhcp_client_identifier
 define 64	string			nisplus_domain
 define 65	array ipaddress		nisplus_servers
 define 66	string			tftp_server_name
@@ -126,6 +128,14 @@
 # DHCP Session Initiated Protocol Servers, RFC3361
 define 120	rfc3361			sip_server
 
+# DHCP Vendor-Identifying Vendor Options, RFC3925
+define 124	binhex			vivco
+define 125	embed			vivso
+embed		uint32			enterprise_number
+# Vendor options are shared between DHCP/DHCPv6
+# Their code is matched to the enterprise number defined above
+# see the end of this file for an example
+
 # DHCP IPv6 Rapid Deployment on IPv4 Infrastructures, RFC5969
 define 212	rfc5969			sixrd
 
@@ -171,8 +181,14 @@
 
 define6 14	norequest flag		rapid_commit
 define6 15	binhex			user_class
-define6 16	binhex			vendor_class
-define6 17	binhex			vendor_options
+
+define6 16	binhex			vivco
+define6 17	embed			vivso
+embed		uint32			enterprise_number
+# Vendor options are shared between DHCP/DHCPv6
+# Their code is matched to the enterprise number defined above
+# See the end of this file for an example
+
 define6 18	binhex			interface_id
 define6 19	byte			reconfigure_msg
 define6 20	flag			reconfigure_accept
@@ -228,3 +244,10 @@
 encap 1		ip6address		addr
 encap 2		ip6address		mcast_addr
 encap 3		ip6address		fqdn
+
+##############################################################################
+# Vendor-Identifying Vendor Options
+# An example:
+#vendopt 12345	encap			frobozzco
+#encap 1	string			maze_location
+#encap 2	byte			grue_probability
--- a/dhcpcd.8.in	Fri Dec 06 17:47:52 2013 +0000
+++ b/dhcpcd.8.in	Fri Dec 06 17:47:53 2013 +0000
@@ -22,7 +22,7 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.Dd November 15, 2013
+.Dd December 6, 2013
 .Dt DHCPCD 8
 .Os
 .Sh NAME
@@ -648,9 +648,9 @@
 .Xr resolvconf 8
 .Sh STANDARDS
 RFC\ 951 RFC\ 1534 RFC\ 2131, RFC\ 2132, RFC\ 2855, RFC\ 3004, RFC\ 3315,
-RFC\ 3361, RFC\ 3633, RFC\ 3396, RFC\ 3397, RFC\ 3442, RFC\ 3927, RFC\ 4039,
-RFC\ 4075, RFC\ 4242, RFC\ 4361, RFC\ 4390, RFC\ 4702, RFC\ 4704, RFC\ 4861,
-RFC\ 4833, RFC\ 5227, RFC\ 5942, RFC\ 5969, RFC\ 6106.
+RFC\ 3361, RFC\ 3633, RFC\ 3396, RFC\ 3397, RFC\ 3442, RFC\ 3925, RFC\ 3927,
+RFC\ 4039, RFC\ 4075, RFC\ 4242, RFC\ 4361, RFC\ 4390, RFC\ 4702, RFC\ 4704,
+RFC\ 4861, RFC\ 4833, RFC\ 5227, RFC\ 5942, RFC\ 5969, RFC\ 6106.
 .Sh AUTHORS
 .An Roy Marples Aq Mt roy@marples.name
 .Sh BUGS
--- a/dhcpcd.c	Fri Dec 06 17:47:52 2013 +0000
+++ b/dhcpcd.c	Fri Dec 06 17:47:53 2013 +0000
@@ -159,6 +159,9 @@
 		free_dhcp_opt_embenc(opt);
 	free(dhcp6_opts);
 #endif
+	for (n = 0, opt = vivso; n < vivso_len; n++, opt++)
+		free_dhcp_opt_embenc(opt);
+	free(vivso);
 }
 
 static void
--- a/dhcpcd.conf.5.in	Fri Dec 06 17:47:52 2013 +0000
+++ b/dhcpcd.conf.5.in	Fri Dec 06 17:47:53 2013 +0000
@@ -22,7 +22,7 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.Dd December 3, 2013
+.Dd December 6, 2013
 .Dt DHCPCD.CONF 5
 .Os
 .Sh NAME
@@ -454,6 +454,16 @@
 If not set then none is sent.
 Some badly configured DHCP servers reject unknown vendorclassids.
 To work around it, try and impersonate Windows by using the MSFT vendorclassid.
+.It Ic vendclass Ar en Ar data
+Add the Vendor Indetifying Vendor Class with the IANA assigned Enterprise
+Number
+.Ar en
+with the
+.Ar data .
+This option can be set more than once to add more data, but the behaviour,
+as per
+.Xr RFC 3925
+is undefined if the Enterprise Number differs.
 .It Ic waitip Op 4 | 6
 Wait for an address to be assigned before forking to the background.
 4 means wait for an IPv4 address to be assigned.
@@ -505,6 +515,17 @@
 .Xr dhcpcd-run-hooks 8 ,
 with a prefix of
 .Va _dhcp6 .
+.It Ic vendopt Ar code Ar type Ar variable
+Defines the Vendor-Identifying Vendor Options.
+The
+.Ar code
+is the IANA Enterprise Number which will unqiuely describe the encapsulated
+options.
+.Ar type
+is normally
+.Ar encap .
+.Ar variable
+names the Vendor option to be exported.
 .It Ic embed Ar type Ar variable
 Defines an embedded variable within the defined option.
 The length is determined by the
--- a/if-options.c	Fri Dec 06 17:47:52 2013 +0000
+++ b/if-options.c	Fri Dec 06 17:47:53 2013 +0000
@@ -78,6 +78,8 @@
 #define O_DEFINE6		O_BASE + 20
 #define O_EMBED			O_BASE + 21
 #define O_ENCAP			O_BASE + 22
+#define O_VENDOPT		O_BASE + 23
+#define O_VENDCLASS		O_BASE + 24
 
 char *dev_load;
 
@@ -140,17 +142,19 @@
 	{"noipv4",          no_argument,       NULL, O_NOIPV4},
 	{"noipv6",          no_argument,       NULL, O_NOIPV6},
 	{"noalias",         no_argument,       NULL, O_NOALIAS},
-	{"iaid",            no_argument,       NULL, O_IAID},
+	{"iaid",            required_argument, NULL, O_IAID},
 	{"ia_na",           no_argument,       NULL, O_IA_NA},
 	{"ia_ta",           no_argument,       NULL, O_IA_TA},
 	{"ia_pd",           no_argument,       NULL, O_IA_PD},
 	{"hostname_short",  no_argument,       NULL, O_HOSTNAME_SHORT},
 	{"dev",             required_argument, NULL, O_DEV},
 	{"nodev",           no_argument,       NULL, O_NODEV},
-	{"define",          no_argument,       NULL, O_DEFINE},
-	{"define6",         no_argument,       NULL, O_DEFINE6},
-	{"embed",           no_argument,       NULL, O_EMBED},
-	{"encap",           no_argument,       NULL, O_ENCAP},
+	{"define",          required_argument, NULL, O_DEFINE},
+	{"define6",         required_argument, NULL, O_DEFINE6},
+	{"embed",           required_argument, NULL, O_EMBED},
+	{"encap",           required_argument, NULL, O_ENCAP},
+	{"vendopt",         required_argument, NULL, O_VENDOPT},
+	{"vendclass",       required_argument, NULL, O_VENDCLASS},
 	{NULL,              0,                 NULL, '\0'}
 };
 
@@ -272,12 +276,13 @@
 	/* If processing a string on the clientid, first byte should be
 	 * 0 to indicate a non hardware type */
 	if (clid && *str) {
-		*sbuf++ = 0;
+		if (sbuf)
+			*sbuf++ = 0;
 		l++;
 	}
 	c[3] = '\0';
 	while (*str) {
-		if (++l > slen) {
+		if (++l > slen && sbuf) {
 			errno = ENOBUFS;
 			return -1;
 		}
@@ -287,19 +292,23 @@
 			case '\0':
 				break;
 			case 'b':
-				*sbuf++ = '\b';
+				if (sbuf)
+					*sbuf++ = '\b';
 				str++;
 				break;
 			case 'n':
-				*sbuf++ = '\n';
+				if (sbuf)
+					*sbuf++ = '\n';
 				str++;
 				break;
 			case 'r':
-				*sbuf++ = '\r';
+				if (sbuf)
+					*sbuf++ = '\r';
 				str++;
 				break;
 			case 't':
-				*sbuf++ = '\t';
+				if (sbuf)
+					*sbuf++ = '\t';
 				str++;
 				break;
 			case 'x':
@@ -310,7 +319,7 @@
 						break;
 					c[i] = *str++;
 				}
-				if (c[1] != '\0') {
+				if (c[1] != '\0' && sbuf) {
 					c[2] = '\0';
 					*sbuf++ = strtol(c, NULL, 16);
 				} else
@@ -324,7 +333,7 @@
 						break;
 					c[i] = *str++;
 				}
-				if (c[2] != '\0') {
+				if (c[2] != '\0' && sbuf) {
 					i = strtol(c, NULL, 8);
 					if (i > 255)
 						i = 255;
@@ -333,13 +342,20 @@
 					l--;
 				break;
 			default:
-				*sbuf++ = *str++;
+				if (sbuf)
+					*sbuf++ = *str;
+				str++;
+				break;
 			}
-		} else
-			*sbuf++ = *str++;
+		} else {
+			if (sbuf)
+				*sbuf++ = *str;
+			str++;
+		}
 	}
 	if (punt_last) {
-		*--sbuf = '\0';
+		if (sbuf)
+			*--sbuf = '\0';
 		l--;
 	}
 	return l;
@@ -533,6 +549,7 @@
 parse_option(struct if_options *ifo, int opt, const char *arg)
 {
 	int i, l, t;
+	unsigned int u;
 	char *p = NULL, *fp, *np, **nconf;
 	ssize_t s;
 	struct in_addr addr, addr2;
@@ -542,6 +559,7 @@
 	uint8_t *request, *require, *no;
 	struct dhcp_opt **dop, *ndop;
 	size_t *dop_len, dl;
+	struct vivco *vivco;
 #ifdef INET6
 	size_t sl;
 	struct if_ia *ia;
@@ -1215,6 +1233,12 @@
 			dop = &ifo->dhcp6_override;
 			dop_len = &ifo->dhcp6_override_len;
 		}
+		/* FALLTHROUGH */
+	case O_VENDOPT:
+		if (dop == NULL) {
+			dop = &ifo->vivso_override;
+			dop_len = &ifo->vivso_override_len;
+		}
 		edop = ldop = NULL;
 		/* FALLTHROUGH */
 	case O_EMBED:
@@ -1246,7 +1270,7 @@
 
 		/* code */
 		if (opt == O_EMBED) /* Embedded options don't have codes */
-			i = 0;
+			u = 0;
 		else {
 			fp = strwhite(arg);
 			if (!fp) {
@@ -1254,8 +1278,12 @@
 				return -1;
 			}
 			*fp++ = '\0';
-			if ((i = atoint(arg)) == -1)
+			errno = 0;
+			u = strtoul(arg, &np, 0);
+			if (u > UINT32_MAX || errno != 0 || *np != '\0') {
+				syslog(LOG_ERR, "invalid code: %s", arg);
 				return -1;
+			}
 			arg = strskipwhite(fp);
 		}
 		/* type */
@@ -1382,7 +1410,7 @@
 				ndop = &(*dop)[dl];
 				/* type 0 seems freshly malloced struct
 				 * for us to use */
-				if (ndop->option == i || ndop->type == 0)
+				if (ndop->option == u || ndop->type == 0)
 					break;
 			}
 		}
@@ -1400,16 +1428,57 @@
 			ndop->encopts_len = 0;
 		} else
 			free_dhcp_opt_embenc(ndop);
-		ndop->option = i; /* could have been 0 */
+		ndop->option = u; /* could have been 0 */
 		ndop->type = t;
 		ndop->len = l;
 		ndop->var = np;
 		/* Save the define for embed and encap options */
-		if (opt == O_DEFINE || opt == O_DEFINE6)
+		if (opt == O_DEFINE || opt == O_DEFINE6 || opt == O_VENDOPT)
 			ldop = ndop;
 		else if (opt == O_ENCAP)
 			edop = ndop;
 		break;
+	case O_VENDCLASS:
+		fp = strwhite(arg);
+		if (fp)
+			*fp++ = '\0';
+		errno = 0;
+		u = strtoul(arg, &np, 0);
+		if (u > UINT32_MAX || errno != 0 || *np != '\0') {
+			syslog(LOG_ERR, "invalid code: %s", arg);
+			return -1;
+		}
+		if (fp) {
+			s = parse_string(NULL, 0, fp);
+			if (s == -1) {
+				syslog(LOG_ERR, "%s: %m", __func__);
+				return -1;
+			}
+			if (s + (sizeof(uint16_t) * 2) > UINT16_MAX) {
+				syslog(LOG_ERR, "vendor class is too big");
+				return -1;
+			}
+			np = malloc(s);
+			if (np == NULL) {
+				syslog(LOG_ERR, "%s: %m", __func__);
+				return -1;
+			}
+			parse_string(np, s, fp);
+		} else {
+			s = 0;
+			np = NULL;
+		}
+		vivco = realloc(ifo->vivco, ifo->vivco_len + 1);
+		if (vivco == NULL) {
+			syslog(LOG_ERR, "%s: %m", __func__);
+			return -1;
+		}
+		ifo->vivco = vivco;
+		ifo->vivco_en = u;
+		vivco = &ifo->vivco[ifo->vivco_len++];
+		vivco->len = s;
+		vivco->data = (uint8_t *)np;
+		break;
 	default:
 		return 0;
 	}
@@ -1594,6 +1663,11 @@
 #endif
 		ifo->dhcp6_override = NULL;
 		ifo->dhcp6_override_len = 0;
+
+		vivso = ifo->vivso_override;
+		vivso_len = ifo->vivso_override_len;
+		ifo->vivso_override = NULL;
+		ifo->vivso_override_len = 0;
 	}
 
 	/* Parse our options file */
@@ -1710,6 +1784,12 @@
 		for (i = 0, opt = ifo->dhcp6_override;
 		    i < ifo->dhcp6_override_len;
 		    i++, opt++)
+			free_dhcp_opt_embenc(opt);
+		free(ifo->dhcp6_override);
+		for (i = 0, opt = ifo->vivso_override;
+		    i < ifo->vivso_override_len;
+		    i++, opt++)
+			free_dhcp_opt_embenc(opt);
 		free(ifo->dhcp6_override);
 
 #ifdef INET6
--- a/if-options.h	Fri Dec 06 17:47:52 2013 +0000
+++ b/if-options.h	Fri Dec 06 17:47:53 2013 +0000
@@ -116,6 +116,11 @@
 #endif
 };
 
+struct vivco {
+	uint16_t len;
+	uint8_t *data;
+};
+
 struct if_options {
 	uint8_t iaid[4];
 	int metric;
@@ -165,6 +170,11 @@
 	size_t dhcp_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;
 };
 
 extern unsigned long long options;