Mercurial > hg > dhcpcd
changeset 2186:b4d8f5bb2fe4 draft
Fix encapsulated options.
IA_NA, IA_TA and IA_PD can now be expressed fully in our config :)
| author | Roy Marples <roy@marples.name> |
|---|---|
| date | Tue, 03 Dec 2013 17:55:40 +0000 |
| parents | 7f9e761f857c |
| children | 9c8b8f4c11f4 |
| files | dhcp-common.c dhcp-common.h dhcp.c dhcp6.c dhcpcd-definitions.conf dhcpcd.conf.5.in if-options.c |
| diffstat | 7 files changed, 351 insertions(+), 204 deletions(-) [+] |
line wrap: on
line diff
--- a/dhcp-common.c Mon Dec 02 20:45:19 2013 +0000 +++ b/dhcp-common.c Tue Dec 03 17:55:40 2013 +0000 @@ -286,6 +286,8 @@ return bytes; } +#define ADDRSZ 4 +#define ADDR6SZ 16 static size_t dhcp_optlen(const struct dhcp_opt *opt, size_t dl) { @@ -306,9 +308,15 @@ } if ((opt->type & (ADDRIPV4 | ARRAY)) == (ADDRIPV4 | ARRAY)) { - if (dl < sizeof(uint32_t)) + if (dl < ADDRSZ) return 0; - return dl - (dl % sizeof(uint32_t)); + return dl - (dl % ADDRSZ); + } + + if ((opt->type & (ADDRIPV6 | ARRAY)) == (ADDRIPV6 | ARRAY)) { + if (dl < ADDR6SZ) + return 0; + return dl - (dl % ADDR6SZ); } sz = 0; @@ -318,6 +326,8 @@ sz = sizeof(uint16_t); else if (opt->type & UINT8) sz = sizeof(uint8_t); + else if (opt->type & ADDRIPV6) + sz = ADDR6SZ; else /* If we don't know the size, assume it's valid */ return dl; @@ -451,12 +461,12 @@ len--; } if (type & UINT8) { - l = snprintf(s, len, "%d", *data); + l = snprintf(s, len, "%u", *data); data++; } else if (type & UINT16) { memcpy(&u16, data, sizeof(u16)); u16 = ntohs(u16); - l = snprintf(s, len, "%d", u16); + l = snprintf(s, len, "%u", u16); data += sizeof(u16); } else if (type & SINT16) { memcpy(&s16, data, sizeof(s16)); @@ -466,7 +476,7 @@ } else if (type & UINT32) { memcpy(&u32, data, sizeof(u32)); u32 = ntohl(u32); - l = snprintf(s, len, "%d", u32); + l = snprintf(s, len, "%u", u32); data += sizeof(u32); } else if (type & SINT32) { memcpy(&s32, data, sizeof(s32)); @@ -502,9 +512,9 @@ } static ssize_t -dhcp_envoption1(char **env, const char *prefix, const char *famprefix, - const char *ifname, const struct dhcp_opt *opt, - const uint8_t *od, int ol) +dhcp_envoption1(char **env, const char *prefix, + const struct dhcp_opt *opt, int vname, const uint8_t *od, int ol, + const char *ifname) { ssize_t len; size_t e; @@ -515,59 +525,79 @@ len = print_option(NULL, 0, opt->type, ol, od, ifname); if (len < 0) return 0; - e = strlen(prefix) + strlen(famprefix) + strlen(opt->v.var) + len + 4; + if (vname) + e = strlen(opt->v.var); + else + e = 0; + e += strlen(prefix) + len + 4; v = val = *env = malloc(e); if (v == NULL) { syslog(LOG_ERR, "%s: %m", __func__); return 0; } - v += snprintf(val, e, "%s%s_%s=", prefix, famprefix, opt->v.var); + if (vname) + v += snprintf(val, e, "%s_%s=", prefix, opt->v.var); + else + v += snprintf(val, e, "%s=", prefix); if (len != 0) print_option(v, len, opt->type, ol, od, ifname); return len; } ssize_t -dhcp_envoption(char **env, const char *prefix, const char *famprefix, - const char *ifname, const struct dhcp_opt *opt, - const uint8_t *(*dgetopt)(int *, int, const uint8_t *, int), +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 *od, int ol) { ssize_t e, n; size_t i; - const uint8_t *ed; - int el; - const struct dhcp_opt *eopt; - char *eprefix; + int eoc; + const uint8_t *eod; + int eos, eol, ov; + struct dhcp_opt *eopt, *oopt; + char *pfx; const char *p; /* If no embedded or encapsulated options, it's easy */ if (opt->embopts_len == 0 && opt->encopts_len == 0) { if (env) - dhcp_envoption1(&env[0], prefix, famprefix, ifname, - opt, od, ol); + dhcp_envoption1(&env[0], prefix, opt, 1, od, ol, + ifname); return 1; } /* Create a new prefix based on the option */ if (env) { - e = strlen(famprefix) + strlen(opt->v.var) + 2; - eprefix = malloc(e); - if (eprefix == NULL) { + if (opt->type & INDEX) { + if (opt->index > 999) { + errno = ENOBUFS; + syslog(LOG_ERR, "%s: %m", __func__); + return 0; + } + } + e = strlen(prefix) + strlen(opt->v.var) + 2 + + (opt->type & INDEX ? 3 : 0); + pfx = malloc(e); + if (pfx == NULL) { syslog(LOG_ERR, "%s: %m", __func__); return 0; } - snprintf(eprefix, e, "%s_%s", famprefix, opt->v.var); - } - /* Silence bogus gcc warning */ - else - eprefix = NULL; + if (opt->type & INDEX) + snprintf(pfx, e, "%s_%s%d", prefix, + opt->v.var, ++opt->index); + else + snprintf(pfx, e, "%s_%s", prefix, opt->v.var); + p = pfx; + } else + p = pfx = NULL; /* Embedded options are always processed first as that * is a fixed layout */ n = 0; - for (i = 0; i < opt->embopts_len; i++) { - eopt = &opt->embopts[i]; + + for (i = 0, eopt = opt->embopts; i < opt->embopts_len; i++, eopt++) { e = dhcp_optlen(eopt, ol); if (e == 0) /* Report error? */ @@ -576,37 +606,71 @@ /* Use the option prefix if the embedded option * name is different. * This avoids new_fqdn_fqdn which would be silly. */ - if (strcmp(opt->v.var, eopt->v.var) == 0) - p = famprefix; - else - p = eprefix; - dhcp_envoption1(&env[n], prefix, p, ifname, - eopt, od, e); + ov = strcmp(opt->v.var, eopt->v.var); + dhcp_envoption1(&env[n], p, eopt, ov, od, e, ifname); } n++; od += e; ol -= e; } - /* Now find our encapsulated option in what's left */ - for (i = 0; i < opt->encopts_len; i++) { - eopt = &opt->encopts[i]; - if ((ed = dgetopt(&el, eopt->option, od, ol))) { - if (env) { - if (strcmp(opt->v.var, eopt->v.var) == 0) - p = famprefix; - else - p = eprefix; - dhcp_envoption1(&env[n], prefix, p, - ifname, eopt, ed, el); + /* Enumerate our encapsulated options */ + if (opt->encopts_len && ol > 0) { + /* Zero any option indexes + * We assume that referenced encapsulated options are NEVER + * recursive as the index order could break. */ + for (i = 0, eopt = opt->encopts; + i < opt->encopts_len; + i++, eopt++) + { + eoc = opt->option; + if (eopt->type & OPTION) { + dgetopt(NULL, &eoc, NULL, NULL, 0, &oopt); + if (oopt) + oopt->index = 0; } - n++; + } + + while ((eod = dgetopt(&eos, &eoc, &eol, od, ol, &oopt))) { + for (i = 0, eopt = opt->encopts; + i < opt->encopts_len; + i++, eopt++) + { + if (eopt->option == eoc) { + if (eopt->type & OPTION) { + if (oopt == NULL) + /* Report error? */ + continue; + eopt = oopt; + } + n += dhcp_envoption( + env == NULL ? NULL : &env[n], p, + ifname, eopt, + dgetopt, eod, eol); + break; + } + } + od += eos + eol; + ol -= eos + eol; } } if (env) - free(eprefix); + free(pfx); /* Return number of options found */ return n; } + +void +dhcp_zero_index(struct dhcp_opt *opt) +{ + size_t i; + struct dhcp_opt *o; + + opt->index = 0; + for (i = 0, o = opt->embopts; i < opt->embopts_len; i++, opt++) + dhcp_zero_index(o); + for (i = 0, o = opt->encopts; i < opt->encopts_len; i++, opt++) + dhcp_zero_index(o); +}
--- a/dhcp-common.h Mon Dec 02 20:45:19 2013 +0000 +++ b/dhcp-common.h Tue Dec 03 17:55:40 2013 +0000 @@ -60,6 +60,8 @@ #define NOREQ (1 << 18) #define EMBED (1 << 19) #define ENCAP (1 << 20) +#define INDEX (1 << 21) +#define OPTION (1 << 22) struct dhcp_opt { uint16_t option; @@ -74,6 +76,8 @@ const char *var; } v; + int index; /* Index counter for many instances of the same option */ + /* Embedded options. * The option code is irrelevant here. */ struct dhcp_opt *embopts; @@ -95,9 +99,10 @@ 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 *, const char *, - const struct dhcp_opt *, - const uint8_t *(*)(int *, int, const uint8_t *, int), - const uint8_t *, int); +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 *od, int ol); +void dhcp_zero_index(struct dhcp_opt *); #endif
--- a/dhcp.c Mon Dec 02 20:45:19 2013 +0000 +++ b/dhcp.c Tue Dec 03 17:55:40 2013 +0000 @@ -985,23 +985,34 @@ } static const uint8_t * -dhcp_getoption(int *len, int option, const uint8_t *od, int ol) +dhcp_getoption(int *os, int *code, int *len, const uint8_t *od, int ol, + struct dhcp_opt **oopt) { - const uint8_t *o; - uint8_t l; + size_t i; + struct dhcp_opt *opt; - while (ol > 0) { - o = od++; - l = *od++; - if (l > ol) - /* Report malformed data? */ + if (od) { + if (ol < 0) { + errno = EINVAL; return NULL; - if (*o == option) { - *len = l; - return od; + } + *os = 2; /* code + len */ + *code = (int)*od++; + *len = (int)*od++; + if (*len < 0 || *len > ol) { + errno = EINVAL; + return NULL; } } - return NULL; + + for (i = 0, opt = dhcp_opts; i < dhcp_opts_len; i++, opt++) { + if (opt->option == *code) { + *oopt = opt; + break; + } + } + + return od; } ssize_t @@ -1014,12 +1025,12 @@ struct in_addr addr; struct in_addr net; struct in_addr brd; - const struct dhcp_opt *opt; + struct dhcp_opt *opt; ssize_t e = 0; char **ep; char cidr[4]; uint8_t overl = 0; - size_t oi; + size_t i; ifo = ifp->options; get_option_uint8(&overl, dhcp, DHO_OPTIONSOVERLOADED); @@ -1031,9 +1042,9 @@ e++; if (*dhcp->servername && !(overl & 2)) e++; - for (oi = 0, opt = dhcp_opts; - oi < dhcp_opts_len; - oi++, opt++) + for (i = 0, opt = dhcp_opts; + i < dhcp_opts_len; + i++, opt++) { if (has_option_mask(ifo->nomask, opt->option)) continue; @@ -1042,19 +1053,19 @@ p = get_option(dhcp, opt->option, &pl); if (!p) continue; - e += dhcp_envoption(NULL, prefix, "", ifp->name, + e += dhcp_envoption(NULL, NULL, ifp->name, opt, dhcp_getoption, p, pl); } - for (oi = 0, opt = ifo->dhcp_override; - oi < ifo->dhcp_override_len; - oi++, opt++) + for (i = 0, opt = ifo->dhcp_override; + i < ifo->dhcp_override_len; + i++, opt++) { if (has_option_mask(ifo->nomask, opt->option)) continue; p = get_option(dhcp, opt->option, &pl); if (!p) continue; - e += dhcp_envoption(NULL, prefix, "", ifp->name, + e += dhcp_envoption(NULL, NULL, ifp->name, opt, dhcp_getoption, p, pl); } return e; @@ -1087,27 +1098,37 @@ setvar(&ep, prefix, "server_name", (const char *)dhcp->servername); - for (oi = 0, opt = dhcp_opts; - oi < dhcp_opts_len; - oi++, opt++) + /* Zero our indexes */ + if (env) { + for (i = 0, opt = dhcp_opts; i < dhcp_opts_len; i++, opt++) + dhcp_zero_index(opt); + for (i = 0, opt = ifp->options->dhcp_override; + i < ifp->options->dhcp_override_len; + i++, opt++) + dhcp_zero_index(opt); + } + + for (i = 0, opt = dhcp_opts; + i < dhcp_opts_len; + i++, opt++) { if (has_option_mask(ifo->nomask, opt->option)) continue; if (dhcp_getoverride(ifo, opt->option)) continue; if ((p = get_option(dhcp, opt->option, &pl))) - ep += dhcp_envoption(ep, prefix, "", ifp->name, + ep += dhcp_envoption(ep, prefix, ifp->name, opt, dhcp_getoption, p, pl); } - for (oi = 0, opt = ifo->dhcp_override; - oi < ifo->dhcp_override_len; - oi++, opt++) + for (i = 0, opt = ifo->dhcp_override; + i < ifo->dhcp_override_len; + i++, opt++) { if (has_option_mask(ifo->nomask, opt->option)) continue; if ((p = get_option(dhcp, opt->option, &pl))) - ep += dhcp_envoption(ep, prefix, "", ifp->name, + ep += dhcp_envoption(ep, prefix, ifp->name, opt, dhcp_getoption, p, pl); }
--- a/dhcp6.c Mon Dec 02 20:45:19 2013 +0000 +++ b/dhcp6.c Tue Dec 03 17:55:40 2013 +0000 @@ -286,15 +286,39 @@ } static const uint8_t * -dhcp6_getoption(int *len, int option, const uint8_t *od, int ol) +dhcp6_getoption(int *os, int *code, int *len, const uint8_t *od, int ol, + struct dhcp_opt **oopt) { const struct dhcp6_option *o; + size_t i; + struct dhcp_opt *opt; - o = dhcp6_findoption(option, od, ol); - if (o == NULL) - return NULL; - *len = ntohs(o->len); - return D6_COPTION_DATA(o); + if (od) { + *os = sizeof(*o); + if (ol < *os) { + errno = EINVAL; + return NULL; + } + o = (const struct dhcp6_option *)od; + *len = ntohs(o->len); + if (*len < 0 || *len > ol) { + errno = EINVAL; + return NULL; + } + *code = ntohs(o->code); + } else + o = NULL; + + for (i = 0, opt = dhcp6_opts; i < dhcp6_opts_len; i++, opt++) { + if (opt->option == *code) { + *oopt = opt; + break; + } + } + + if (o) + return D6_COPTION_DATA(o); + return NULL; } static const struct dhcp6_option * @@ -2542,36 +2566,20 @@ continue; ipv6_handleifa_addrs(cmd, &state->addrs, addr, flags); } -} -static const struct dhcp_opt * -dhcp6_getoverride(const struct if_options *ifo, uint16_t o) -{ - size_t i; - const struct dhcp_opt *opt; - - for (i = 0, opt = ifo->dhcp6_override; - i < ifo->dhcp6_override_len; - i++, opt++) - { - if (opt->option == o) - return opt; - } - return NULL; } ssize_t dhcp6_env(char **env, const char *prefix, const struct interface *ifp, - const struct dhcp6_message *m, ssize_t mlen) + const struct dhcp6_message *m, ssize_t len) { const struct dhcp6_state *state; const struct if_options *ifo; - const struct dhcp_opt *opt; + struct dhcp_opt *opt; const struct dhcp6_option *o; - size_t e, n, oi; - uint16_t ol; - const uint8_t *od; - char **ep, *v, *val; + size_t i, n; + uint16_t ol, oc; + char **ep, *v, *val, *pfx; const struct ipv6_addr *ap; state = D6_CSTATE(ifp); @@ -2579,40 +2587,80 @@ ep = env; ifo = ifp->options; - for (oi = 0, opt = dhcp6_opts; - oi < dhcp6_opts_len; - oi++, opt++) - { - if (has_option_mask(ifo->nomask, opt->option)) - continue; - if (dhcp6_getoverride(ifo, opt->option)) - continue; + /* Zero our indexes */ + if (env) { + for (i = 0, opt = dhcp6_opts; i < dhcp6_opts_len; i++, opt++) + dhcp_zero_index(opt); + for (i = 0, opt = ifp->options->dhcp6_override; + i < ifp->options->dhcp6_override_len; + i++, opt++) + dhcp_zero_index(opt); + i = strlen(prefix) + strlen("_dhcp6") + 1; + pfx = malloc(i); + if (pfx == NULL) { + syslog(LOG_ERR, "%s: %m", __func__); + return 0; + } + snprintf(pfx, i, "%s_dhcp6", prefix); + } else + pfx = NULL; - o = dhcp6_getmoption(opt->option, m, mlen); - if (o == NULL) - continue; + /* Unlike DHCP, DHCPv6 options *may* occur more than once. + * There is also no provision for option concatenation unlike DHCP. */ + for (o = D6_CFIRST_OPTION(m); + len > (ssize_t)sizeof(*o); + o = D6_CNEXT_OPTION(o)) + { ol = ntohs(o->len); - od = D6_COPTION_DATA(o); - n += dhcp_envoption(env == NULL ? NULL : &env[n], - prefix, "_dhcp6", ifp->name, opt, dhcp6_getoption, od, ol); + len -= sizeof(*o) + ol; + if (len < 0) { + errno = EINVAL; + break; + } + oc = ntohs(o->code); + if (has_option_mask(ifo->nomask6, oc)) + continue; + for (i = 0, opt = ifo->dhcp6_override; + i < ifo->dhcp6_override_len; + i++, opt++) + if (opt->option == oc) + break; + if (opt == NULL) { + for (i = 0, opt = dhcp6_opts; + i < dhcp6_opts_len; + i++, opt++) + if (opt->option == oc) + break; + } + if (opt) { + n += dhcp_envoption(env == NULL ? NULL : &env[n], + pfx, ifp->name, + opt, dhcp6_getoption, D6_COPTION_DATA(o), ol); + } } + free(pfx); + /* It is tempting to remove this section. + * However, we need it at least for Delegated Prefixes + * (they don't have a DHCPv6 message to parse to get the addressses) + * and it's easier for shell scripts to see which addresses have + * been added */ if (TAILQ_FIRST(&state->addrs)) { if (env == NULL) n++; else { if (ifo->ia_type == D6_OPTION_IA_PD) { - e = strlen(prefix) + + i = strlen(prefix) + strlen("_dhcp6_prefix="); TAILQ_FOREACH(ap, &state->addrs, next) { - e += strlen(ap->saddr) + 1; + i += strlen(ap->saddr) + 1; } - v = val = *ep++ = malloc(e); + v = val = *ep++ = malloc(i); if (v == NULL) { syslog(LOG_ERR, "%s: %m", __func__); return -1; } - v += snprintf(val, e, "%s_dhcp6_prefix=", + v += snprintf(val, i, "%s_dhcp6_prefix=", prefix); TAILQ_FOREACH(ap, &state->addrs, next) { strcpy(v, ap->saddr); @@ -2621,17 +2669,17 @@ } *--v = '\0'; } else { - e = strlen(prefix) + + i = strlen(prefix) + strlen("_dhcp6_ip_address="); TAILQ_FOREACH(ap, &state->addrs, next) { - e += strlen(ap->saddr) + 1; + i += strlen(ap->saddr) + 1; } - v = val = *ep++ = malloc(e); + v = val = *ep++ = malloc(i); if (v == NULL) { syslog(LOG_ERR, "%s: %m", __func__); return -1; } - v += snprintf(val, e, "%s_dhcp6_ip_address=", + v += snprintf(val, i, "%s_dhcp6_ip_address=", prefix); TAILQ_FOREACH(ap, &state->addrs, next) { strcpy(v, ap->saddr); @@ -2643,20 +2691,5 @@ } } - for (oi = 0, opt = ifo->dhcp6_override; - oi < ifo->dhcp6_override_len; - oi++, opt++) - { - if (has_option_mask(ifo->nomask, opt->option)) - continue; - o = dhcp6_getmoption(opt->option, m, mlen); - if (o == NULL) - continue; - ol = ntohs(o->len); - od = D6_COPTION_DATA(o); - n += dhcp_envoption(env == NULL ? NULL : &env[n], - prefix, "_dhcp6", ifp->name, opt, dhcp6_getoption, od, ol); - } - return n; }
--- a/dhcpcd-definitions.conf Mon Dec 02 20:45:19 2013 +0000 +++ b/dhcpcd-definitions.conf Tue Dec 03 17:55:40 2013 +0000 @@ -134,41 +134,23 @@ define6 1 binhex client_id define6 2 binhex server_id -# DHCPv6 addresses. -# These are currently handled internally by dhcpcd(8) and only -# the addresses are exposed to dhcpcd-run-hooks(8). -# When they can be expressed properly they will be uncommented. -# many signifies that it could occur more than once and be index. -# option signifies that the specified option can be encapsulated. -# For example: -# ia_na_count=1 -# ia_na_1_iaid="00112233" -# ia_na_1_t1=3600 -# ia_na_1_t2=7200 -# ia_na_1_ia_addr_count=1 -# ia_na_1_ia_addr_1_addr="dead:beef" -# ia_na_1_ia_addr_1_pltime="3600" -# ia_na_1_ia_addr_1_pltime="7200" -# ia_na_1_ia_addr_1_status_code=0 -# ia_na_1_ia_addr_1_status_code_message="OK" -# ia_na_1_code=0 -# ia_na_1_status_code_message="OK" -# -#define6 3 embed many ia_na -#embed uint32 iaid -#embed uint32 t1 -#embed uint32 t2 -#encap option 5 -#encap option 13 -#define6 4 embed many ia_ta -#embed uint32 iaid -#encap option 5 -#encap option 13 -#define6 5 embed many ia_addr -#embed ip6address addr -#embed uint32 pltime -#embed uint32 vltime -#encap option 13 +define6 3 norequest index embed ia_na +embed binhex:4 iaid +embed uint32 t1 +embed uint32 t2 +encap 5 option +encap 13 option + +define6 4 norequest index embed ia_ta +embed uint32 iaid +encap 5 option +encap 13 option + +define6 5 norequest index embed ia_addr +embed ip6address ia_addr +embed uint32 pltime +embed uint32 vltime +encap 13 option define6 6 array uint16 option_request define6 7 byte preference @@ -203,6 +185,19 @@ define6 23 array ip6address name_servers define6 24 domain domain_search +# DHCPv6 Prefix Options, RFC6603 +define6 25 norequest index embed ia_pd +embed binhex:4 iaid +embed uint32 t1 +embed uint32 t2 +encap 26 option + +define 26 index embed prefix +embed uint32 pltime +embed uint32 vltime +embed ip6address prefix +encap 13 option + # DHCPv6 Network Information Service Options, RFC3898 define6 27 array ip6address nis_servers define6 28 array ip6address nisp_servers
--- a/dhcpcd.conf.5.in Mon Dec 02 20:45:19 2013 +0000 +++ b/dhcpcd.conf.5.in Tue Dec 03 17:55:40 2013 +0000 @@ -22,7 +22,7 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.Dd December 2, 2013 +.Dd December 3, 2013 .Dt DHCPCD.CONF 5 .Os .Sh NAME @@ -535,6 +535,8 @@ configuration .It Ic norequest This option cannot be requested, regardless of user configuration +.It Ic index +The option can appear more than once and will be indexed. .It Ic array The option data is split into a space seperated array, each element being the same type. @@ -571,6 +573,8 @@ Contains embedded options (implies encap as well) .It Ic encap Contains encapsulated options (implies embed as well) +.It Ic option +References an option from the global definition .El .Ss Example definition .D1 # DHCP option 81, Fully Qualified Domain Name, RFC4702
--- a/if-options.c Mon Dec 02 20:45:19 2013 +0000 +++ b/if-options.c Tue Dec 03 17:55:40 2013 +0000 @@ -482,6 +482,7 @@ /* Pointer to last defined option */ static struct dhcp_opt *ldop; +static struct dhcp_opt *edop; void free_dhcp_opt_embenc(struct dhcp_opt *opt) @@ -1212,17 +1213,23 @@ dop = &ifo->dhcp6_override; dop_len = &ifo->dhcp6_override_len; } - ldop = NULL; + edop = ldop = NULL; /* FALLTHROUGH */ case O_EMBED: if (dop == NULL) { - if (ldop == NULL) { - syslog(LOG_ERR, "embed must be after a define"); + if (edop) { + dop = &edop->embopts; + dop_len = &edop->embopts_len; + } else if (ldop) { + dop = &ldop->embopts; + dop_len = &ldop->embopts_len; + } else { + syslog(LOG_ERR, + "embed must be after a define or encap"); return -1; } - dop = &ldop->embopts; - dop_len = &ldop->embopts_len; } + /* FALLTHROUGH */ case O_ENCAP: if (dop == NULL) { if (ldop == NULL) { @@ -1281,6 +1288,16 @@ } *fp++ = '\0'; } + if (strcasecmp(arg, "index") == 0) { + t |= INDEX; + arg = strskipwhite(fp); + fp = strwhite(arg); + if (fp == NULL) { + syslog(LOG_ERR, "incomplete index type"); + return -1; + } + *fp++ = '\0'; + } if (strcasecmp(arg, "array") == 0) { t |= ARRAY; arg = strskipwhite(fp); @@ -1314,15 +1331,17 @@ else if (strcasecmp(arg, "binhex") == 0) t |= BINHEX; else if (strcasecmp(arg, "embed") == 0) - t = EMBED; + t |= EMBED; else if (strcasecmp(arg, "encap") == 0) - t = ENCAP; + t |= ENCAP; else if (strcasecmp(arg, "rfc3361") ==0) - t = STRING | RFC3361; + t |= STRING | RFC3361; else if (strcasecmp(arg, "rfc3442") ==0) - t = RFC3442; + t |= STRING | RFC3442; else if (strcasecmp(arg, "rfc5969") == 0) - t = RFC5969; + t |= STRING | RFC5969; + else if (strcasecmp(arg, "option") == 0) + t |= OPTION; else { syslog(LOG_ERR, "unknown type: %s", arg); return -1; @@ -1338,18 +1357,22 @@ } /* variable */ if (!fp) { - syslog(LOG_ERR, - "type %s requires a variable name", arg); - return -1; - } - arg = strskipwhite(fp); - fp = strwhite(arg); - if (fp) - *fp++ = '\0'; - np = strdup(arg); - if (np == NULL) { - syslog(LOG_ERR, "%s: %m", __func__); - return -1; + if (!(t & OPTION)) { + syslog(LOG_ERR, + "type %s requires a variable name", arg); + return -1; + } + np = NULL; + } else { + arg = strskipwhite(fp); + fp = strwhite(arg); + if (fp) + *fp++ = '\0'; + np = strdup(arg); + if (np == NULL) { + syslog(LOG_ERR, "%s: %m", __func__); + return -1; + } } if (opt == O_EMBED) dl = *dop_len; @@ -1383,6 +1406,8 @@ /* Save the define for embed and encap options */ if (opt == O_DEFINE || opt == O_DEFINE6) ldop = ndop; + else if (opt == O_ENCAP) + edop = ndop; break; default: return 0;
