]> git.ipfire.org Git - thirdparty/dhcpcd.git/commitdiff
Add support for RFC3925 Vendor-Identifying Vendor Options
authorRoy Marples <roy@marples.name>
Fri, 6 Dec 2013 17:47:53 +0000 (17:47 +0000)
committerRoy Marples <roy@marples.name>
Fri, 6 Dec 2013 17:47:53 +0000 (17:47 +0000)
12 files changed:
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

index 8ff273156392c754e79019d2322e34bd26629b28..96b8a7411eebb6e4a6e05100d74846ecfd55171c 100644 (file)
 #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;
 
index fbd7f8a2f035a4760e99287f5c65fbf00b5fcfc6..d9ccea9c09706732d97c2f47fc2eadab92b4fde6 100644 (file)
@@ -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 2939e874c223fde4424a88b916c218876118e128..1c87a9f504ecb3ec2473b0cdc8a96fcd957f31f3 100644 (file)
--- 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 8f17c814624f9f2429c11fa10e35336ec7b5ad06..8aed958424d614d8a83ddbeb69c410811a63c34d 100644 (file)
--- 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 380996bec202a0877d546402036101ba663fb7ae..bcd31ce8abb32b440a462905f90313cabdc094f7 100644 (file)
--- 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 737e705c778936d4886e5a110805202741f706da..120500c2f1afd4def315b5170cb6a3b90cb3b17e 100644 (file)
--- 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
index 9df5890318a88c0c1f11d6a8162b3b344e8b72d5..b9e388a457cb0deae695ea80b2916acad09be1e0 100644 (file)
@@ -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
index 3117c946a8ce758cc58aa75d183c2644e79d7e6a..dec2cbc2a44b8d3a7e3106fb145a67cf5f6f04d2 100644 (file)
@@ -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
index 193c9a1add76f23e5d0f682f2f78450436ed7097..959764864d255e8fb8552b477b3c7317efa1e35d 100644 (file)
--- 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
index 12b33279c0c5a12758902d3a219e0721abbb5074..9dacbb9bbfcb98da59be82197c7b2ad2014944e3 100644 (file)
@@ -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
index e85fe11a6d12ca37da8fbb7b4c2beedaaf8ae736..bd596e255a1239aa09e4f30f2f171ff600ee0449 100644 (file)
@@ -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
index 9c7a3dc5ee19a10d168e8c7dbb40ffb8d5ab20d8..ad594e5cba41fcaac434033b489c8fa8d4da52a6 100644 (file)
@@ -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;