From 7a911e57cb99dc71720c2ecccbfa05d79c2be37d Mon Sep 17 00:00:00 2001 From: Roy Marples Date: Fri, 6 Dec 2013 17:47:53 +0000 Subject: [PATCH] Add support for RFC3925 Vendor-Identifying Vendor Options --- dhcp-common.c | 46 +++++++++----- dhcp-common.h | 12 +++- dhcp.c | 67 ++++++++++++++++++-- dhcp.h | 2 + dhcp6.c | 133 +++++++++++++++++++--------------------- dhcp6.h | 3 +- dhcpcd-definitions.conf | 27 +++++++- dhcpcd.8.in | 8 +-- dhcpcd.c | 3 + dhcpcd.conf.5.in | 23 ++++++- if-options.c | 124 ++++++++++++++++++++++++++++++------- if-options.h | 10 +++ 12 files changed, 335 insertions(+), 123 deletions(-) diff --git a/dhcp-common.c b/dhcp-common.c index 8ff27315..96b8a741 100644 --- a/dhcp-common.c +++ b/dhcp-common.c @@ -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; + + 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, +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 @@ dhcp_envoption1(char **env, const char *prefix, 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; diff --git a/dhcp-common.h b/dhcp-common.h index fbd7f8a2..d9ccea9c 100644 --- a/dhcp-common.h +++ b/dhcp-common.h @@ -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 @@ struct dhcp_opt { 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_string(char *, ssize_t, int, const uint8_t *); 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 *); diff --git a/dhcp.c b/dhcp.c index 2939e874..1c87a9f5 100644 --- a/dhcp.c +++ b/dhcp.c @@ -684,6 +684,7 @@ make_message(struct dhcp_message **message, 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 @@ make_message(struct dhcp_message **message, 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 @@ make_message(struct dhcp_message **message, (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 @@ make_message(struct dhcp_message **message, *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 @@ dhcp_getoverride(const struct if_options *ifo, uint16_t o) } 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 @@ dhcp_env(char **env, const char *prefix, const struct dhcp_message *dhcp, 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 @@ dhcp_env(char **env, const char *prefix, const struct dhcp_message *dhcp, 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 @@ dhcp_env(char **env, const char *prefix, const struct dhcp_message *dhcp, 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; diff --git a/dhcp.h b/dhcp.h index 8f17c814..8aed9584 100644 --- a/dhcp.h +++ b/dhcp.h @@ -105,6 +105,8 @@ enum DHO { 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 */ diff --git a/dhcp6.c b/dhcp6.c index 380996be..bcd31ce8 100644 --- a/dhcp6.c +++ b/dhcp6.c @@ -116,6 +116,8 @@ const struct dhcp_compat dhcp_compats[] = { { 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 @@ dhcp6_init(void) 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 + size_t vlen, i; + const struct vivco *vivco; + 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; + } -#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 + 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); - 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); + 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, 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 @@ dhcp6_findoption(int code, const uint8_t *d, ssize_t len) } 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 @@ dhcp6_getoption(int *os, int *code, int *len, const uint8_t *od, int ol, } 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 @@ dhcp6_makemessage(struct interface *ifp) 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 @@ dhcp6_makemessage(struct interface *ifp) 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 @@ dhcp6_env(char **env, const char *prefix, const struct interface *ifp, { 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 @@ dhcp6_env(char **env, const char *prefix, const struct interface *ifp, 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 @@ dhcp6_env(char **env, const char *prefix, const struct interface *ifp, 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 @@ dhcp6_env(char **env, const char *prefix, const struct interface *ifp, 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); diff --git a/dhcp6.h b/dhcp6.h index 737e705c..120500c2 100644 --- a/dhcp6.h +++ b/dhcp6.h @@ -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 diff --git a/dhcpcd-definitions.conf b/dhcpcd-definitions.conf index 9df58903..b9e388a4 100644 --- a/dhcpcd-definitions.conf +++ b/dhcpcd-definitions.conf @@ -70,6 +70,8 @@ define 56 string dhcp_message 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 @@ define 119 domain domain_search # 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 @@ embed string message 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 @@ define6 56 encap ntp_server 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 diff --git a/dhcpcd.8.in b/dhcpcd.8.in index 3117c946..dec2cbc2 100644 --- a/dhcpcd.8.in +++ b/dhcpcd.8.in @@ -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 @@ running on the .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 diff --git a/dhcpcd.c b/dhcpcd.c index 193c9a1a..95976486 100644 --- a/dhcpcd.c +++ b/dhcpcd.c @@ -159,6 +159,9 @@ free_globals(void) 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 diff --git a/dhcpcd.conf.5.in b/dhcpcd.conf.5.in index 12b33279..9dacbb9b 100644 --- a/dhcpcd.conf.5.in +++ b/dhcpcd.conf.5.in @@ -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 @@ For example 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 @@ exported to .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 diff --git a/if-options.c b/if-options.c index e85fe11a..bd596e25 100644 --- a/if-options.c +++ b/if-options.c @@ -78,6 +78,8 @@ unsigned long long options = 0; #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 @@ const struct option cf_options[] = { {"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 @@ parse_string_hwaddr(char *sbuf, ssize_t slen, const char *str, int clid) /* 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 @@ parse_string_hwaddr(char *sbuf, ssize_t slen, const char *str, int clid) 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 @@ parse_string_hwaddr(char *sbuf, ssize_t slen, const char *str, int clid) 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 @@ parse_string_hwaddr(char *sbuf, ssize_t slen, const char *str, int clid) 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 @@ parse_string_hwaddr(char *sbuf, ssize_t slen, const char *str, int clid) 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 @@ static int 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 @@ parse_option(struct if_options *ifo, int opt, const char *arg) 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 @@ parse_option(struct if_options *ifo, int opt, const char *arg) 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 @@ parse_option(struct if_options *ifo, int opt, const char *arg) /* 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 @@ parse_option(struct if_options *ifo, int opt, const char *arg) 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 @@ parse_option(struct if_options *ifo, int opt, const char *arg) 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 @@ parse_option(struct if_options *ifo, int opt, const char *arg) 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 @@ read_config(const char *file, #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 @@ free_options(struct if_options *ifo) 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 diff --git a/if-options.h b/if-options.h index 9c7a3dc5..ad594e5c 100644 --- a/if-options.h +++ b/if-options.h @@ -116,6 +116,11 @@ struct if_ia { #endif }; +struct vivco { + uint16_t len; + uint8_t *data; +}; + struct if_options { uint8_t iaid[4]; int metric; @@ -165,6 +170,11 @@ struct if_options { 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; -- 2.47.3