diff --git a/man/dnsmasq.8 b/man/dnsmasq.8 index 5b01e63..57c9b29 100644 --- a/man/dnsmasq.8 +++ b/man/dnsmasq.8 @@ -1372,11 +1372,13 @@ described in RFC 3442. IPv6 options are specified using the \fBoption6:\fP keyword, followed by the option number or option name. The IPv6 option name space is disjoint from the IPv4 option name space. IPv6 addresses -in options must be bracketed with square brackets, eg. -\fB --dhcp-option=option6:ntp-server,[1234::56]\fP. +in options must be bracketed with square brackets, eg. \fB --dhcp-option=option6:ntp-server,[1234::56]\fP. For IPv6, [::] means "the global address of the machine running dnsmasq", whilst [fd00::] is replaced with the -ULA, if it exists, and [fe80::] with the link-local address. +ULA, if it exists, and [fe80::] with the link-local address. The vendorclass option +is special for DHCPv6, since it requires an enterprise number which must appear as a decimal number +before the class(es). eg. \fB --dhcp-option=option6:vendor-class,343,HTTPClient\fP + Be careful: data-type suitability for the option number sent is not checked. It is quite possible to persuade dnsmasq to generate illegal DHCP packets with diff --git a/src/dhcp-common.c b/src/dhcp-common.c index 6b8824a..fa92381 100644 --- a/src/dhcp-common.c +++ b/src/dhcp-common.c @@ -734,7 +734,7 @@ static const struct opttab_t opttab6[] = { { "status", 13, OT_INTERNAL }, { "rapid-commit", 14, OT_INTERNAL }, { "user-class", 15, OT_INTERNAL | OT_CSTRING }, - { "vendor-class", 16, OT_INTERNAL | OT_CSTRING }, + { "vendor-class", 16, OT_DHCP6_VENDOR }, { "vendor-opts", 17, OT_INTERNAL }, { "sip-server-domain", 21, OT_RFC1035_NAME }, { "sip-server", 22, OT_ADDR_LIST }, @@ -909,6 +909,37 @@ char *option_string(int prot, unsigned int opt, unsigned char *val, int opt_len, buf[j++] = ','; } } + else if ((ot[o].size & OT_DHCP6_VENDOR)) + { + unsigned int enterprise; + unsigned char *p = &val[0]; + + if (opt_len >= 4) + { + GETLONG(enterprise, p); + snprintf(buf, buf_len, "%u", enterprise); + j = strlen(buf); + i = 4; + while (i + 2 <= opt_len) + { + int k, len; + p = &val[i]; + GETSHORT(len, p); + if (i + 2 + len > opt_len) + break; + if (j < buf_len - 1) + buf[j++] = ','; + for (k = 0; k < len && j < buf_len - 1; k++) + { + char c = *p++; + if (isprint((unsigned char)c)) + buf[j++] = c; + } + buf[j] = 0; + i += len + 2; + } + } + } #endif else if ((ot[o].size & (OT_DEC | OT_TIME)) && opt_len != 0) { diff --git a/src/dnsmasq.h b/src/dnsmasq.h index ebb8d15..88171f9 100644 --- a/src/dnsmasq.h +++ b/src/dnsmasq.h @@ -834,6 +834,7 @@ struct frec { #define OT_CSTRING 0x0800 #define OT_DEC 0x0400 #define OT_TIME 0x0200 +#define OT_DHCP6_VENDOR 0x0100 /* actions in the daemon->helper RPC */ #define ACTION_DEL 1 diff --git a/src/option.c b/src/option.c index 274732f..a11a341 100644 --- a/src/option.c +++ b/src/option.c @@ -1902,6 +1902,51 @@ static int parse_dhcp_opt(char *errstr, char *arg, int flags) comma = split(arg); } + new->val = newp; + new->len = p - newp; + } + else if (comma && (opt_len & OT_DHCP6_VENDOR)) + { + /* First arg is Enterprise ID (4 bytes) + subsequent are length fields (2 bytes each) + string */ + int i, commas = 1; + unsigned char *p, *newp; + + for (i = 0; comma[i]; i++) + if (comma[i] == ',') + commas++; + + newp = opt_malloc(strlen(comma)+(2*commas)+4); + p = newp; + arg = comma; + comma = split(arg); + + if (arg && *arg) + { + unsigned int enterprise = atoi(arg); + PUTLONG(enterprise, p); + } + else + goto_err(_("missing enterprise ID in dhcp-option")); + + arg = comma; + comma = split(arg); + + if (!arg || !*arg) + goto_err(_("missing vendor class in dhcp-option")); + + while (arg && *arg) + { + u16 len = strlen(arg); + unhide_metas(arg); + PUTSHORT(len, p); + memcpy(p, arg, len); + p += len; + + arg = comma; + comma = split(arg); + } + new->val = newp; new->len = p - newp; }