]> git.ipfire.org Git - thirdparty/dhcpcd.git/commitdiff
Add support for user defined options with embedded and encapsulated options.
authorRoy Marples <roy@marples.name>
Wed, 27 Nov 2013 20:21:17 +0000 (20:21 +0000)
committerRoy Marples <roy@marples.name>
Wed, 27 Nov 2013 20:21:17 +0000 (20:21 +0000)
Install dhcpcd-embedded.conf to LIBEXECDIR which is read before dhcpcd.conf.

The idea is that this dhcpcd-embedded.conf is maintained only by me to support
RFC DHCP/DHCPv6 options that require known embedded or ncapsulated options
which are not easily added to our option structures in dhcp.c and dhcp6.c.
Eventually we may move the whole structure here so that a smaller binary
is produced and the definitions are easier to maintain.

Makefile
configure
defs.h
dhcp-common.c
dhcp-common.h
dhcp.c
dhcp6.c
dhcpcd-embedded.conf [new file with mode: 0644]
dhcpcd.conf.5.in
if-options.c
if-options.h

index a66bf1f36b9387ff701797c89aeb5cafc16719a6..003fb5d08a8fe24df871d68bdf96d3ea64bde672 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -86,12 +86,13 @@ _proginstall: ${PROG}
        ${INSTALL} -m ${BINMODE} ${PROG} ${DESTDIR}${SBINDIR}
        ${INSTALL} -d ${DESTDIR}${DBDIR}
 
-proginstall: _proginstall
-       for x in ${SUBDIRS}; do cd $$x; ${MAKE} $@; cd ..; done
-
 _scriptsinstall: ${SCRIPTS}
        ${INSTALL} -d ${DESTDIR}${SCRIPTSDIR}
        ${INSTALL} -m ${BINMODE} ${SCRIPTS} ${DESTDIR}${SCRIPTSDIR}
+       ${INSTALL} -m ${CONFMODE} dhcpcd-embedded.conf ${DESTDIR}${SCRIPTSDIR}
+
+proginstall: _proginstall _scriptsinstall
+       for x in ${SUBDIRS}; do cd $$x; ${MAKE} $@; cd ..; done
 
 _maninstall: ${MAN5} ${MAN8}
        ${INSTALL} -d ${DESTDIR}${MANDIR}/man5
index e420bf7eec32db6d5099825f0dc6a9dc77509837..f268becd1fa0dfa316e48d3a8149771410fdf1cd 100755 (executable)
--- a/configure
+++ b/configure
@@ -300,7 +300,7 @@ fi
 if [ "$DEBUG" != no -a "$DEBUG" != false ]; then
        echo "Adding debugging CFLAGS"
        cat <<EOF >>$CONFIG_MK
-CFLAGS+=       -Wall -Wextra -Wimplicit -Wshadow -Wformat=2
+CFLAGS+=       -ggdb -Wall -Wextra -Wimplicit -Wshadow -Wformat=2
 CFLAGS+=       -Wmissing-prototypes -Wmissing-declarations
 CFLAGS+=       -Wmissing-noreturn -Wmissing-format-attribute
 CFLAGS+=       -Wredundant-decls  -Wnested-externs
diff --git a/defs.h b/defs.h
index f4906072d0cc3b9e23bfb3b7f7bc17b19efd1bf6..73cf4d817cbf596acdd2a22b883e863512ceb8ea 100644 (file)
--- a/defs.h
+++ b/defs.h
 #ifndef CONFIG
 # define CONFIG                        SYSCONFDIR "/" PACKAGE ".conf"
 #endif
+#ifndef EMBEDDED_CONFIG
+# define EMBEDDED_CONFIG       LIBEXECDIR "/" PACKAGE "-embedded.conf"
+#endif
 #ifndef SCRIPT
 # define SCRIPT                        LIBEXECDIR "/" PACKAGE "-run-hooks"
+#endif
 #ifndef DEVDIR
 # define DEVDIR                        LIBDIR "/" PACKAGE "/dev"
 #endif
-#endif
 #ifndef DUID
 # define DUID                  SYSCONFDIR "/" PACKAGE ".duid"
 #endif
index 3f28d24a9d9520d67a5ea9ed657c611ed50e4d22..3e6ea0b0b488d350da38791dc252246df3c36931 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
+
 int make_option_mask(const struct dhcp_opt *dopts,
     uint8_t *mask, const char *opts, int add)
 {
@@ -52,10 +61,10 @@ int make_option_mask(const struct dhcp_opt *dopts,
                if (*token == '\0')
                        continue;
                for (opt = dopts; opt->option; opt++) {
-                       if (!opt->var)
+                       if (!opt->v.var)
                                continue;
                        match = 0;
-                       if (strcmp(opt->var, token) == 0)
+                       if (strcmp(opt->v.var, token) == 0)
                                match = 1;
                        else {
                                errno = 0;
@@ -89,6 +98,50 @@ int make_option_mask(const struct dhcp_opt *dopts,
        return 0;
 }
 
+int
+dhcp_getaddr(struct in_addr *a, const uint8_t *p, size_t pl)
+{
+
+       if (!p || pl < sizeof(a->s_addr))
+               return -1;
+       memcpy(&a->s_addr, p, sizeof(a->s_addr));
+       return 0;
+}
+
+int
+dhcp_getuint32(uint32_t *i, const uint8_t *p, size_t pl)
+{
+       uint32_t d;
+
+       if (!p || pl < sizeof(d))
+               return -1;
+       memcpy(&d, p, sizeof(d));
+       *i = ntohl(d);
+       return 0;
+}
+
+int
+dhcp_getuint16(uint16_t *i, const uint8_t *p, size_t pl)
+{
+       uint16_t d;
+
+       if (!p || pl < sizeof(d))
+               return -1;
+       memcpy(&d, p, sizeof(d));
+       *i = ntohs(d);
+       return 0;
+}
+
+int
+dhcp_getuint8(uint8_t *i, const uint8_t *p, __unused size_t pl)
+{
+
+       if (!p)
+               return -1;
+       *i = *(p);
+       return 0;
+}
+
 size_t
 encode_rfc1035(const char *src, uint8_t *dst)
 {
@@ -278,6 +331,36 @@ print_string(char *s, ssize_t len, int dl, const uint8_t *data)
        return bytes;
 }
 
+static size_t
+dhcp_optlen(int type, size_t dl)
+{
+       size_t sz;
+
+       if (dl == 0)
+               return 0;
+
+       if (type == 0 || type & (STRING | RFC3442 | RFC5969))
+               return dl;
+
+       if ((type & (ADDRIPV4 | ARRAY)) == (ADDRIPV4 | ARRAY)) {
+               if (dl < sizeof(uint32_t))
+                       return 0;
+               return dl - (dl % sizeof(uint32_t));
+       }
+
+       sz = 0;
+       if (type & (UINT32 | ADDRIPV4))
+               sz = sizeof(uint32_t);
+       else if (type & UINT16)
+               sz = sizeof(uint16_t);
+       else if (type & UINT8)
+               sz = sizeof(uint8_t);
+       else
+               /* If we don't know the size, assume it's valid */
+               return dl;
+       return (dl < sz ? 0 : sz);
+}
+
 ssize_t
 print_option(char *s, ssize_t len, int type, int dl, const uint8_t *data,
     const char *ifname)
@@ -454,3 +537,79 @@ print_option(char *s, ssize_t len, int type, int dl, const uint8_t *data,
 
        return bytes;
 }
+
+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)
+{
+       ssize_t len;
+       size_t e;
+       char *v, *val;
+
+       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;
+       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 (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),
+    const uint8_t *od, int ol)
+{
+       ssize_t e, n;
+       size_t i;
+       const uint8_t *ed;
+       int el;
+       const struct dhcp_opt *eopt;
+
+       /* 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);
+               return 1;
+       }
+
+       /* 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];
+               e = dhcp_optlen(eopt->type, ol);
+               if (e == 0)
+                       /* Report error? */
+                       return 0;
+               if (env)
+                       dhcp_envoption1(&env[n], prefix, famprefix, ifname,
+                           eopt, od, e);
+               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)
+                               dhcp_envoption1(&env[n], prefix,
+                                   famprefix, ifname, eopt, ed, el);
+                       n++;
+               }
+       }
+
+       /* Return number of options found */
+       return n;
+}
index 3a30b25267af29613cd3188dab3370c120267d17..dd35075400ddbcf21e53a8894bc2d47d5ce60665 100644 (file)
 #define SCODE          (1 << 16)
 #define FLAG           (1 << 17)
 #define NOREQ          (1 << 18)
+#define EMBED          (1 << 19)
+#define ENCAP          (1 << 20)
 
 struct dhcp_opt {
        uint16_t option;
        int type;
-       const char *var;
+
+       /* This union allows us to define a global static list of
+        * variable names which we don't free and a list of user defined
+        * options which we do free. */
+       union {
+               char *dvar;
+               const char *var;
+       } v;
+
+       /* Embedded options.
+        * The option code is irrelevant here. */
+       struct dhcp_opt *embopts;
+       size_t embopts_len;
+
+       /* Encapsulated options */
+       struct dhcp_opt *encopts;
+       size_t encopts_len;
 };
 
 #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)))
-int make_option_mask(const struct dhcp_opt *,uint8_t *, const char *, int);
+#define has_option_mask(var, val) (var[val >>3] & (1 << (val & 7)))
+int make_option_mask(const struct dhcp_opt *, uint8_t *, const char *, int);
+
+int dhcp_getaddr(struct in_addr *, const uint8_t *, size_t);
+int dhcp_getuint32(uint32_t *, const uint8_t *, size_t);
+int dhcp_getuint16(uint16_t *, const uint8_t *, size_t);
+int dhcp_getuint8(uint8_t *, const uint8_t *, size_t);
+
 size_t encode_rfc1035(const char *src, uint8_t *dst);
 ssize_t decode_rfc3397(char *, ssize_t, int, const uint8_t *);
 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);
+
 #endif
diff --git a/dhcp.c b/dhcp.c
index bd8fdc0c8979f87bee2ec654758cda4340808d41..18c3f66e10aa48502d14ae67d91176bc7f1bc62d 100644 (file)
--- a/dhcp.c
+++ b/dhcp.c
@@ -104,106 +104,108 @@ static const struct dhcp_op dhcp_ops[] = {
        { 0, NULL }
 };
 
+#define O(a, b, c) {.option = (a), .type = (b), .v.var = (c) }
 const struct dhcp_opt dhcp_opts[] = {
-       { 1,    ADDRIPV4 | REQUEST,     "subnet_mask" },
+       O(1,    ADDRIPV4 | REQUEST,     "subnet_mask"),
                /* RFC 3442 states that the CSR has to come before all other
                 * routes. For completeness, we also specify static routes,
                 * then routers. */
-       { 121,  RFC3442,        "classless_static_routes" },
-       { 249,  RFC3442,        "ms_classless_static_routes" },
-       { 33,   IPV4A | REQUEST,        "static_routes" },
-       { 3,    IPV4A | REQUEST,        "routers" },
-       { 2,    UINT32,         "time_offset" },
-       { 4,    IPV4A,          "time_servers" },
-       { 5,    IPV4A,          "ien116_name_servers" },
-       { 6,    IPV4A,          "domain_name_servers" },
-       { 7,    IPV4A,          "log_servers" },
-       { 8,    IPV4A,          "cookie_servers" },
-       { 9,    IPV4A,          "lpr_servers" },
-       { 10,   IPV4A,          "impress_servers" },
-       { 11,   IPV4A,          "resource_location_servers" },
-       { 12,   STRING,         "host_name" },
-       { 13,   UINT16,         "boot_size" },
-       { 14,   STRING,         "merit_dump" },
-       { 15,   STRING,         "domain_name" },
-       { 16,   ADDRIPV4,       "swap_server" },
-       { 17,   STRING,         "root_path" },
-       { 18,   STRING,         "extensions_path" },
-       { 19,   UINT8,          "ip_forwarding" },
-       { 20,   UINT8,          "non_local_source_routing" },
-       { 21,   IPV4A,  "policy_filter" },
-       { 22,   SINT16,         "max_dgram_reassembly" },
-       { 23,   UINT16,         "default_ip_ttl" },
-       { 24,   UINT32,         "path_mtu_aging_timeout" },
-       { 25,   UINT16 | ARRAY, "path_mtu_plateau_table" },
-       { 26,   UINT16,         "interface_mtu" },
-       { 27,   UINT8,          "all_subnets_local" },
-       { 28,   ADDRIPV4 | REQUEST,     "broadcast_address" },
-       { 29,   UINT8,          "perform_mask_discovery" },
-       { 30,   UINT8,          "mask_supplier" },
-       { 31,   UINT8,          "router_discovery" },
-       { 32,   ADDRIPV4,       "router_solicitation_address" },
-       { 34,   UINT8,          "trailer_encapsulation" },
-       { 35,   UINT32,         "arp_cache_timeout" },
-       { 36,   UINT16,         "ieee802_3_encapsulation" },
-       { 37,   UINT8,          "default_tcp_ttl" },
-       { 38,   UINT32,         "tcp_keepalive_interval" },
-       { 39,   UINT8,          "tcp_keepalive_garbage" },
-       { 40,   STRING,         "nis_domain" },
-       { 41,   IPV4A,          "nis_servers" },
-       { 42,   IPV4A,          "ntp_servers" },
-       { 43,   STRING,         "vendor_encapsulated_options" },
-       { 44,   IPV4A,          "netbios_name_servers" },
-       { 45,   ADDRIPV4,       "netbios_dd_server" },
-       { 46,   UINT8,          "netbios_node_type" },
-       { 47,   STRING,         "netbios_scope" },
-       { 48,   IPV4A,          "font_servers" },
-       { 49,   IPV4A,          "x_display_manager" },
-       { 50,   ADDRIPV4,       "dhcp_requested_address" },
-       { 51,   UINT32 | REQUEST,       "dhcp_lease_time" },
-       { 52,   UINT8,          "dhcp_option_overload" },
-       { 53,   UINT8,          "dhcp_message_type" },
-       { 54,   ADDRIPV4,       "dhcp_server_identifier" },
-       { 55,   UINT8 | ARRAY,  "dhcp_parameter_request_list" },
-       { 56,   STRING,         "dhcp_message" },
-       { 57,   UINT16,         "dhcp_max_message_size" },
-       { 58,   UINT32 | REQUEST,       "dhcp_renewal_time" },
-       { 59,   UINT32 | REQUEST,       "dhcp_rebinding_time" },
-       { 64,   STRING,         "nisplus_domain" },
-       { 65,   IPV4A,          "nisplus_servers" },
-       { 66,   STRING,         "tftp_server_name" },
-       { 67,   STRING,         "bootfile_name" },
-       { 68,   IPV4A,          "mobile_ip_home_agent" },
-       { 69,   IPV4A,          "smtp_server" },
-       { 70,   IPV4A,          "pop_server" },
-       { 71,   IPV4A,          "nntp_server" },
-       { 72,   IPV4A,          "www_server" },
-       { 73,   IPV4A,          "finger_server" },
-       { 74,   IPV4A,          "irc_server" },
-       { 75,   IPV4A,          "streettalk_server" },
-       { 76,   IPV4A,          "streettalk_directory_assistance_server" },
-       { 77,   STRING,         "user_class" },
-       { 80,   FLAG | NOREQ,   "rapid_commit" },
-       { 81,   STRING | RFC3397,       "fqdn" },
-       { 85,   IPV4A,          "nds_servers" },
-       { 86,   STRING,         "nds_tree_name" },
-       { 87,   STRING,         "nds_context" },
-       { 88,   STRING | RFC3397,       "bcms_controller_names" },
-       { 89,   IPV4A,          "bcms_controller_address" },
-       { 91,   UINT32,         "client_last_transaction_time" },
-       { 92,   IPV4A,          "associated_ip" },
-       { 98,   STRING,         "uap_servers" },
-       { 100,  STRING,         "posix_timezone" },
-       { 101,  STRING,         "tzdb_timezone" },
-       { 112,  IPV4A,          "netinfo_server_address" },
-       { 113,  STRING,         "netinfo_server_tag" },
-       { 114,  STRING,         "default_url" },
-       { 118,  ADDRIPV4,       "subnet_selection" },
-       { 119,  STRING | RFC3397,       "domain_search" },
-       { 120,  STRING | RFC3361,       "sip_server" },
-       { 212,  RFC5969,        "sixrd" },
-       { 0, 0, NULL }
+       O(121,  RFC3442,        "classless_static_routes"),
+       O(249,  RFC3442,        "ms_classless_static_routes"),
+       O(33,   IPV4A | REQUEST,        "static_routes"),
+       O(3,    IPV4A | REQUEST,        "routers"),
+       O(2,    UINT32,         "time_offset"),
+       O(4,    IPV4A,          "time_servers"),
+       O(5,    IPV4A,          "ien116_name_servers"),
+       O(6,    IPV4A,          "domain_name_servers"),
+       O(7,    IPV4A,          "log_servers"),
+       O(8,    IPV4A,          "cookie_servers"),
+       O(9,    IPV4A,          "lpr_servers"),
+       O(10,   IPV4A,          "impress_servers"),
+       O(11,   IPV4A,          "resource_location_servers"),
+       O(12,   STRING,         "host_name"),
+       O(13,   UINT16,         "boot_size"),
+       O(14,   STRING,         "merit_dump"),
+       O(15,   STRING,         "domain_name"),
+       O(16,   ADDRIPV4,       "swap_server"),
+       O(17,   STRING,         "root_path"),
+       O(18,   STRING,         "extensions_path"),
+       O(19,   UINT8,          "ip_forwarding"),
+       O(20,   UINT8,          "non_local_source_routing"),
+       O(21,   IPV4A,  "policy_filter"),
+       O(22,   SINT16,         "max_dgram_reassembly"),
+       O(23,   UINT16,         "default_ip_ttl"),
+       O(24,   UINT32,         "path_mtu_aging_timeout"),
+       O(25,   UINT16 | ARRAY, "path_mtu_plateau_table"),
+       O(26,   UINT16,         "interface_mtu"),
+       O(27,   UINT8,          "all_subnets_local"),
+       O(28,   ADDRIPV4 | REQUEST,     "broadcast_address"),
+       O(29,   UINT8,          "perform_mask_discovery"),
+       O(30,   UINT8,          "mask_supplier"),
+       O(31,   UINT8,          "router_discovery"),
+       O(32,   ADDRIPV4,       "router_solicitation_address"),
+       O(34,   UINT8,          "trailer_encapsulation"),
+       O(35,   UINT32,         "arp_cache_timeout"),
+       O(36,   UINT16,         "ieee802_3_encapsulation"),
+       O(37,   UINT8,          "default_tcp_ttl"),
+       O(38,   UINT32,         "tcp_keepalive_interval"),
+       O(39,   UINT8,          "tcp_keepalive_garbage"),
+       O(40,   STRING,         "nis_domain"),
+       O(41,   IPV4A,          "nis_servers"),
+       O(42,   IPV4A,          "ntp_servers"),
+       O(43,   STRING,         "vendor_encapsulated_options"),
+       O(44,   IPV4A,          "netbios_name_servers"),
+       O(45,   ADDRIPV4,       "netbios_dd_server"),
+       O(46,   UINT8,          "netbios_node_type"),
+       O(47,   STRING,         "netbios_scope"),
+       O(48,   IPV4A,          "font_servers"),
+       O(49,   IPV4A,          "x_display_manager"),
+       O(50,   ADDRIPV4,       "dhcp_requested_address"),
+       O(51,   UINT32 | REQUEST,       "dhcp_lease_time"),
+       O(52,   UINT8,          "dhcp_option_overload"),
+       O(53,   UINT8,          "dhcp_message_type"),
+       O(54,   ADDRIPV4,       "dhcp_server_identifier"),
+       O(55,   UINT8 | ARRAY,  "dhcp_parameter_request_list"),
+       O(56,   STRING,         "dhcp_message"),
+       O(57,   UINT16,         "dhcp_max_message_size"),
+       O(58,   UINT32 | REQUEST,       "dhcp_renewal_time"),
+       O(59,   UINT32 | REQUEST,       "dhcp_rebinding_time"),
+       O(64,   STRING,         "nisplus_domain"),
+       O(65,   IPV4A,          "nisplus_servers"),
+       O(66,   STRING,         "tftp_server_name"),
+       O(67,   STRING,         "bootfile_name"),
+       O(68,   IPV4A,          "mobile_ip_home_agent"),
+       O(69,   IPV4A,          "smtp_server"),
+       O(70,   IPV4A,          "pop_server"),
+       O(71,   IPV4A,          "nntp_server"),
+       O(72,   IPV4A,          "www_server"),
+       O(73,   IPV4A,          "finger_server"),
+       O(74,   IPV4A,          "irc_server"),
+       O(75,   IPV4A,          "streettalk_server"),
+       O(76,   IPV4A,          "streettalk_directory_assistance_server"),
+       O(77,   STRING,         "user_class"),
+       O(80,   FLAG | NOREQ,   "rapid_commit"),
+       O(81,   STRING | RFC3397,       "fqdn"),
+       O(85,   IPV4A,          "nds_servers"),
+       O(86,   STRING,         "nds_tree_name"),
+       O(87,   STRING,         "nds_context"),
+       O(88,   STRING | RFC3397,       "bcms_controller_names"),
+       O(89,   IPV4A,          "bcms_controller_address"),
+       O(91,   UINT32,         "client_last_transaction_time"),
+       O(92,   IPV4A,          "associated_ip"),
+       O(98,   STRING,         "uap_servers"),
+       O(100,  STRING,         "posix_timezone"),
+       O(101,  STRING,         "tzdb_timezone"),
+       O(112,  IPV4A,          "netinfo_server_address"),
+       O(113,  STRING,         "netinfo_server_tag"),
+       O(114,  STRING,         "default_url"),
+       O(118,  ADDRIPV4,       "subnet_selection"),
+       O(119,  STRING | RFC3397,       "domain_search"),
+       O(120,  STRING | RFC3361,       "sip_server"),
+       O(212,  RFC5969,        "sixrd"),
+       O(0, 0, NULL)
 };
+#undef O
 
 static const char *dhcp_params[] = {
        "ip_address",
@@ -234,8 +236,8 @@ dhcp_printoptions(void)
                printf("    %s\n", *p);
 
        for (opt = dhcp_opts; opt->option; opt++)
-               if (opt->var)
-                       printf("%03d %s\n", opt->option, opt->var);
+               if (opt->v.var)
+                       printf("%03d %s\n", opt->option, opt->v.var);
 }
 
 static int
@@ -283,7 +285,7 @@ validate_length(uint8_t option, int dl, int *type)
 
 #ifdef DEBUG_MEMORY
 static void
-free_option_buffer(void)
+dhcp_cleanup(void)
 {
 
        free(packet);
@@ -312,9 +314,6 @@ get_option(const struct dhcp_message *dhcp, uint8_t opt, int *len, int *type)
                                        opt_buffer = malloc(sizeof(*dhcp));
                                        if (opt_buffer == NULL)
                                                return NULL;
-#ifdef DEBUG_MEMORY
-                                       atexit(free_option_buffer);
-#endif
                                }
                                if (!bp)
                                        bp = opt_buffer;
@@ -375,50 +374,41 @@ int
 get_option_addr(struct in_addr *a, const struct dhcp_message *dhcp,
     uint8_t option)
 {
-       const uint8_t *p = get_option_raw(dhcp, option);
+       const uint8_t *p;
+       int len;
 
-       if (!p)
-               return -1;
-       memcpy(&a->s_addr, p, sizeof(a->s_addr));
-       return 0;
+       p = get_option(dhcp, option, &len, NULL);
+       return dhcp_getaddr(a, p, len);
 }
 
 int
 get_option_uint32(uint32_t *i, const struct dhcp_message *dhcp, uint8_t option)
 {
-       const uint8_t *p = get_option_raw(dhcp, option);
-       uint32_t d;
+       const uint8_t *p;
+       int len;
 
-       if (!p)
-               return -1;
-       memcpy(&d, p, sizeof(d));
-       *i = ntohl(d);
-       return 0;
+       p = get_option(dhcp, option, &len, NULL);
+       return dhcp_getuint32(i, p, len);
 }
 
 int
 get_option_uint16(uint16_t *i, const struct dhcp_message *dhcp, uint8_t option)
 {
-       const uint8_t *p = get_option_raw(dhcp, option);
-       uint16_t d;
+       const uint8_t *p;
+       int len;
 
-       if (!p)
-               return -1;
-       memcpy(&d, p, sizeof(d));
-       *i = ntohs(d);
-       return 0;
+       p = get_option(dhcp, option, &len, NULL);
+       return dhcp_getuint16(i, p, len);
 }
 
 int
 get_option_uint8(uint8_t *i, const struct dhcp_message *dhcp, uint8_t option)
 {
-       const uint8_t *p = get_option_raw(dhcp, option);
+       const uint8_t *p;
+       int len;
 
-       if (!p)
-               return -1;
-       if (i)
-               *i = *(p);
-       return 0;
+       p = get_option(dhcp, option, &len, NULL);
+       return dhcp_getuint8(i, p, len);
 }
 
 ssize_t
@@ -1135,6 +1125,43 @@ read_lease(const struct interface *ifp)
        return dhcp;
 }
 
+static const struct dhcp_opt *
+dhcp_getoverride(const struct if_options *ifo, uint16_t o)
+{
+       size_t i;
+       const struct dhcp_opt *opt;
+
+       for (i = 0, opt = ifo->dhcp_override;
+           i < ifo->dhcp_override_len;
+           i++, opt++)
+       {
+               if (opt->option == o)
+                       return opt;
+       }
+       return NULL;
+}
+
+
+static const uint8_t *
+dhcp_getoption(int *len, int option, const uint8_t *od, int ol)
+{
+       const uint8_t *o;
+       uint8_t l;
+
+       while (ol > 0) {
+               o = od++;
+               l = *od++;
+               if (l > ol)
+                       /* Report malformed data? */
+                       return NULL;
+               if (*o == option) {
+                       *len = l;
+                       return od;
+               }
+       }
+       return NULL;
+}
+
 ssize_t
 dhcp_env(char **env, const char *prefix, const struct dhcp_message *dhcp,
     const struct interface *ifp)
@@ -1145,24 +1172,29 @@ dhcp_env(char **env, const char *prefix, const struct dhcp_message *dhcp,
        struct in_addr addr;
        struct in_addr net;
        struct in_addr brd;
-       char *val, *v;
        const struct dhcp_opt *opt;
-       ssize_t len, e = 0;
+       ssize_t e = 0;
        char **ep;
        char cidr[4];
        uint8_t overl = 0;
+       size_t oi;
 
        ifo = ifp->options;
        get_option_uint8(&overl, dhcp, DHO_OPTIONSOVERLOADED);
 
        if (!env) {
                for (opt = dhcp_opts; opt->option; opt++) {
-                       if (!opt->var)
+                       if (!opt->v.var)
                                continue;
                        if (has_option_mask(ifo->nomask, opt->option))
                                continue;
-                       if (get_option_raw(dhcp, opt->option))
-                               e++;
+                       if (dhcp_getoverride(ifo, opt->option))
+                               continue;
+                       p = get_option(dhcp, opt->option, &pl, NULL);
+                       if (!p)
+                               continue;
+                       e += dhcp_envoption(NULL, prefix, "", ifp->name,
+                           opt, dhcp_getoption, p, pl);
                }
                if (dhcp->yiaddr || dhcp->ciaddr)
                        e += 5;
@@ -1170,6 +1202,16 @@ dhcp_env(char **env, const char *prefix, const struct dhcp_message *dhcp,
                        e++;
                if (*dhcp->servername && !(overl & 2))
                        e++;
+               for (oi = 0, opt = ifo->dhcp_override;
+                   oi < ifo->dhcp_override_len;
+                   oi++, opt++)
+               {
+                       p = get_option(dhcp, opt->option, &pl, NULL);
+                       if (!p)
+                               continue;
+                       e += dhcp_envoption(NULL, prefix, "", ifp->name,
+                           opt, dhcp_getoption, p, pl);
+               }
                return e;
        }
 
@@ -1199,29 +1241,34 @@ dhcp_env(char **env, const char *prefix, const struct dhcp_message *dhcp,
                setvar(&ep, prefix, "server_name", (const char *)dhcp->servername);
 
        for (opt = dhcp_opts; opt->option; opt++) {
-               if (!opt->var)
+               if (!opt->v.var)
                        continue;
                if (has_option_mask(ifo->nomask, opt->option))
                        continue;
-               val = NULL;
+               if (dhcp_getoverride(ifo, opt->option))
+                       continue;
                p = get_option(dhcp, opt->option, &pl, NULL);
                if (!p)
                        continue;
-               /* We only want the FQDN name */
+               /* No override, which means it's not embedded, so just
+                * grab the FQDN itself */
                if (opt->option == DHO_FQDN) {
                        p += 3;
                        pl -= 3;
                }
-               len = print_option(NULL, 0, opt->type, pl, p, ifp->name);
-               if (len < 0)
-                       return -1;
-               e = strlen(prefix) + strlen(opt->var) + len + 4;
-               v = val = *ep++ = malloc(e);
-               if (v == NULL)
-                       return -1;
-               v += snprintf(val, e, "%s_%s=", prefix, opt->var);
-               if (len != 0)
-                       print_option(v, len, opt->type, pl, p, 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++)
+       {
+               if (has_option_mask(ifo->nomask, opt->option))
+                       continue;
+               if ((p = get_option(dhcp, opt->option, &pl, NULL)))
+                       ep += dhcp_envoption(ep, prefix, "", ifp->name,
+                           opt, dhcp_getoption, p, pl);
        }
 
        return ep - env;
@@ -2335,14 +2382,6 @@ dhcp_handlepacket(void *arg)
        /* We loop through until our buffer is empty.
         * The benefit is that if we get >1 DHCP packet in our buffer and
         * the first one fails for any reason, we can use the next. */
-       if (packet == NULL) {
-               packet = malloc(udp_dhcp_len);
-               if (packet == NULL) {
-                       syslog(LOG_ERR, "%s: %m", __func__);
-                       return;
-               }
-       }
-
        for(;;) {
                bytes = ipv4_getrawpacket(iface, ETHERTYPE_IP,
                    packet, udp_dhcp_len, &partialcsum);
@@ -2415,8 +2454,6 @@ dhcp_handlepacket(void *arg)
                if (state->raw_fd == -1)
                        break;
        }
-       free(packet);
-       packet = NULL;
        free(dhcp);
 }
 
@@ -2425,6 +2462,17 @@ dhcp_open(struct interface *ifp)
 {
        struct dhcp_state *state;
 
+       if (packet == NULL) {
+               packet = malloc(udp_dhcp_len);
+               if (packet == NULL) {
+                       syslog(LOG_ERR, "%s: %m", __func__);
+                       return -1;
+               }
+#ifdef DEBUG_MEMORY
+               atexit(dhcp_cleanup);
+#endif
+       }
+
        state = D_STATE(ifp);
        if (state->raw_fd == -1) {
                state->raw_fd = ipv4_opensocket(ifp, ETHERTYPE_IP);
diff --git a/dhcp6.c b/dhcp6.c
index fbdc8a461546dd93364c96c163e6cacfb9f9673e..3bc431832678a960824402bb0a0e3df1c6be42ae 100644 (file)
--- a/dhcp6.c
+++ b/dhcp6.c
@@ -101,31 +101,33 @@ static const struct dhcp6_op dhcp6_ops[] = {
 };
 
 #define IPV6A  ADDRIPV6 | ARRAY
+#define O(a, b, c) {.option = (a), .type = (b), .v.var = (c) }
 const struct dhcp_opt dhcp6_opts[] = {
-       { D6_OPTION_CLIENTID,           BINHEX,         "client_id" },
-       { D6_OPTION_SERVERID,           BINHEX,         "server_id" },
-       { D6_OPTION_IA_ADDR,            IPV6A,          "ia_addr" },
-       { D6_OPTION_PREFERENCE,         UINT8,          "preference" },
-       { D6_OPTION_UNICAST,            ADDRIPV6,       "unicast" },
-       { D6_OPTION_RAPID_COMMIT,       FLAG | NOREQ,   "rapid_commit" },
-       { D6_OPTION_STATUS_CODE,        SCODE,          "status_code" },
-       { D6_OPTION_SIP_SERVERS_NAME,   RFC3397,        "sip_servers_names" },
-       { D6_OPTION_SIP_SERVERS_ADDRESS,IPV6A,  "sip_servers_addresses" },
-       { D6_OPTION_DNS_SERVERS,        IPV6A,          "name_servers" },
-       { D6_OPTION_DOMAIN_LIST,        RFC3397,        "domain_search" },
-       { D6_OPTION_NIS_SERVERS,        IPV6A,          "nis_servers" },
-       { D6_OPTION_NISP_SERVERS,       IPV6A,          "nisp_servers" },
-       { D6_OPTION_NIS_DOMAIN_NAME,    RFC3397,        "nis_domain_name" },
-       { D6_OPTION_NISP_DOMAIN_NAME,   RFC3397,        "nisp_domain_name" },
-       { D6_OPTION_SNTP_SERVERS,       IPV6A,          "sntp_servers" },
-       { D6_OPTION_INFO_REFRESH_TIME,  UINT32,         "info_refresh_time" },
-       { D6_OPTION_BCMS_SERVER_D,      RFC3397,        "bcms_server_d" },
-       { D6_OPTION_BCMS_SERVER_A,      IPV6A,          "bcms_server_a" },
-       { D6_OPTION_FQDN,               RFC3397,        "fqdn" },
-       { D6_OPTION_POSIX_TIMEZONE,     STRING,         "posix_timezone" },
-       { D6_OPTION_TZDB_TIMEZONE,      STRING,         "tzdb_timezone" },
-       { 0, 0, NULL }
+       O(D6_OPTION_CLIENTID,           BINHEX,         "client_id"),
+       O(D6_OPTION_SERVERID,           BINHEX,         "server_id"),
+       O(D6_OPTION_IA_ADDR,            IPV6A,          "ia_addr"),
+       O(D6_OPTION_PREFERENCE,         UINT8,          "preference"),
+       O(D6_OPTION_UNICAST,            ADDRIPV6,       "unicast"),
+       O(D6_OPTION_RAPID_COMMIT,       FLAG | NOREQ,   "rapid_commit"),
+       O(D6_OPTION_STATUS_CODE,        SCODE,          "status_code"),
+       O(D6_OPTION_SIP_SERVERS_NAME,   RFC3397,        "sip_servers_names"),
+       O(D6_OPTION_SIP_SERVERS_ADDRESS,IPV6A,  "sip_servers_addresses"),
+       O(D6_OPTION_DNS_SERVERS,        IPV6A,          "name_servers"),
+       O(D6_OPTION_DOMAIN_LIST,        RFC3397,        "domain_search"),
+       O(D6_OPTION_NIS_SERVERS,        IPV6A,          "nis_servers"),
+       O(D6_OPTION_NISP_SERVERS,       IPV6A,          "nisp_servers"),
+       O(D6_OPTION_NIS_DOMAIN_NAME,    RFC3397,        "nis_domain_name"),
+       O(D6_OPTION_NISP_DOMAIN_NAME,   RFC3397,        "nisp_domain_name"),
+       O(D6_OPTION_SNTP_SERVERS,       IPV6A,          "sntp_servers"),
+       O(D6_OPTION_INFO_REFRESH_TIME,  UINT32,         "info_refresh_time"),
+       O(D6_OPTION_BCMS_SERVER_D,      RFC3397,        "bcms_server_d"),
+       O(D6_OPTION_BCMS_SERVER_A,      IPV6A,          "bcms_server_a"),
+       O(D6_OPTION_FQDN,               RFC3397,        "fqdn"),
+       O(D6_OPTION_POSIX_TIMEZONE,     STRING,         "posix_timezone"),
+       O(D6_OPTION_TZDB_TIMEZONE,      STRING,         "tzdb_timezone"),
+       O(0, 0, NULL)
 };
+#undef O
 
 struct dhcp_compat {
        uint8_t dhcp_opt;
@@ -161,8 +163,8 @@ dhcp6_printoptions(void)
        const struct dhcp_opt *opt;
 
        for (opt = dhcp6_opts; opt->option; opt++)
-               if (opt->var)
-                       printf("%05d %s\n", opt->option, opt->var);
+               if (opt->v.var)
+                       printf("%05d %s\n", opt->option, opt->v.var);
 }
 
 static int
@@ -309,8 +311,20 @@ dhcp6_findoption(int code, const uint8_t *d, ssize_t len)
        return NULL;
 }
 
+static const uint8_t *
+dhcp6_getoption(int *len, int option, const uint8_t *od, int ol)
+{
+       const struct dhcp6_option *o;
+
+       o = dhcp6_findoption(option, od, ol);
+       if (o == NULL)
+               return NULL;
+       *len = ntohs(o->len);
+       return D6_COPTION_DATA(o);
+}
+
 static const struct dhcp6_option *
-dhcp6_getoption(int code, const struct dhcp6_message *m, ssize_t len)
+dhcp6_getmoption(int code, const struct dhcp6_message *m, ssize_t len)
 {
 
        len -= sizeof(*m);
@@ -327,7 +341,7 @@ dhcp6_updateelapsed(struct interface *ifp, struct dhcp6_message *m, ssize_t len)
        time_t up;
        uint16_t u16;
 
-       co = dhcp6_getoption(D6_OPTION_ELAPSED, m, len);
+       co = dhcp6_getmoption(D6_OPTION_ELAPSED, m, len);
        if (co == NULL)
                return -1;
 
@@ -443,7 +457,7 @@ dhcp6_makemessage(struct interface *ifp)
                        m = state->new;
                        ml = state->new_len;
                }
-               si = dhcp6_getoption(D6_OPTION_SERVERID, m, ml);
+               si = dhcp6_getmoption(D6_OPTION_SERVERID, m, ml);
                len += sizeof(*si) + ntohs(si->len);
                /* FALLTHROUGH */
        case DH6S_REBIND:
@@ -499,7 +513,7 @@ dhcp6_makemessage(struct interface *ifp)
                break;
        case DH6S_REQUEST:
                state->send->type = DHCP6_REQUEST;
-               unicast = dhcp6_getoption(D6_OPTION_UNICAST, m, ml);
+               unicast = dhcp6_getmoption(D6_OPTION_UNICAST, m, ml);
                break;
        case DH6S_CONFIRM:
                state->send->type = DHCP6_CONFIRM;
@@ -509,14 +523,14 @@ dhcp6_makemessage(struct interface *ifp)
                break;
        case DH6S_RENEW:
                state->send->type = DHCP6_RENEW;
-               unicast = dhcp6_getoption(D6_OPTION_UNICAST, m, ml);
+               unicast = dhcp6_getmoption(D6_OPTION_UNICAST, m, ml);
                break;
        case DH6S_INFORM:
                state->send->type = DHCP6_INFORMATION_REQ;
                break;
        case DH6S_RELEASE:
                state->send->type = DHCP6_RELEASE;
-               unicast = dhcp6_getoption(D6_OPTION_UNICAST, m, ml);
+               unicast = dhcp6_getmoption(D6_OPTION_UNICAST, m, ml);
                break;
        default:
                errno = EINVAL;
@@ -1197,7 +1211,7 @@ dhcp6_checkstatusok(const struct interface *ifp,
        if (p)
                o = dhcp6_findoption(D6_OPTION_STATUS_CODE, p, len);
        else
-               o = dhcp6_getoption(D6_OPTION_STATUS_CODE, m, len);
+               o = dhcp6_getmoption(D6_OPTION_STATUS_CODE, m, len);
        if (o == NULL) {
                //syslog(LOG_DEBUG, "%s: no status", ifp->name);
                return 0;
@@ -1555,7 +1569,7 @@ dhcp6_validatelease(struct interface *ifp,
        const struct dhcp6_option *o;
 
        state = D6_STATE(ifp);
-       o = dhcp6_getoption(ifp->options->ia_type, m, len);
+       o = dhcp6_getmoption(ifp->options->ia_type, m, len);
        if (o == NULL) {
                if (sfrom &&
                    dhcp6_checkstatusok(ifp, m, NULL, len) != -1)
@@ -2047,13 +2061,13 @@ dhcp6_handledata(__unused void *arg)
                return;
        }
 
-       if (dhcp6_getoption(D6_OPTION_SERVERID, r, len) == NULL) {
+       if (dhcp6_getmoption(D6_OPTION_SERVERID, r, len) == NULL) {
                syslog(LOG_ERR, "%s: no DHCPv6 server ID from %s",
                    ifp->name, sfrom);
                return;
        }
 
-       o = dhcp6_getoption(D6_OPTION_CLIENTID, r, len);
+       o = dhcp6_getmoption(D6_OPTION_CLIENTID, r, len);
        if (o == NULL || ntohs(o->len) != duid_len ||
            memcmp(D6_COPTION_DATA(o), duid, duid_len) != 0)
        {
@@ -2065,11 +2079,11 @@ dhcp6_handledata(__unused void *arg)
        ifo = ifp->options;
        for (opt = dhcp6_opts; opt->option; opt++) {
                if (has_option_mask(ifo->requiremask6, opt->option) &&
-                   dhcp6_getoption(opt->option, r, len) == NULL)
+                   dhcp6_getmoption(opt->option, r, len) == NULL)
                {
                        syslog(LOG_WARNING,
                            "%s: reject DHCPv6 (no option %s) from %s",
-                           ifp->name, opt->var, sfrom);
+                           ifp->name, opt->v.var, sfrom);
                        return;
                }
        }
@@ -2080,7 +2094,7 @@ dhcp6_handledata(__unused void *arg)
                switch(state->state) {
                case DH6S_INFORM:
                        /* RFC4242 */
-                       o = dhcp6_getoption(D6_OPTION_INFO_REFRESH_TIME,
+                       o = dhcp6_getmoption(D6_OPTION_INFO_REFRESH_TIME,
                            r, len);
                        if (o == NULL || ntohs(o->len) != sizeof(u32))
                                state->renew = IRT_DEFAULT;
@@ -2107,7 +2121,7 @@ dhcp6_handledata(__unused void *arg)
                case DH6S_DISCOVER:
                        if (has_option_mask(ifo->requestmask6,
                            D6_OPTION_RAPID_COMMIT) &&
-                           dhcp6_getoption(D6_OPTION_RAPID_COMMIT, r, len))
+                           dhcp6_getmoption(D6_OPTION_RAPID_COMMIT, r, len))
                                state->state = DH6S_REQUEST;
                        else
                                op = NULL;
@@ -2556,6 +2570,22 @@ dhcp6_handleifa(int cmd, const char *ifname,
        }
 }
 
+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)
@@ -2564,53 +2594,41 @@ dhcp6_env(char **env, const char *prefix, const struct interface *ifp,
        const struct if_options *ifo;
        const struct dhcp_opt *opt;
        const struct dhcp6_option *o;
-       ssize_t len, e;
+       size_t e, n, oi;
        uint16_t ol;
        const uint8_t *od;
        char **ep, *v, *val;
        const struct ipv6_addr *ap;
 
        state = D6_CSTATE(ifp);
-       e = 0;
+       n = 0;
        ep = env;
        ifo = ifp->options;
        for (opt = dhcp6_opts; opt->option; opt++) {
-               if (!opt->var)
+               if (!opt->v.var)
                        continue;
                if (has_option_mask(ifo->nomask6, opt->option))
                        continue;
-               o = dhcp6_getoption(opt->option, m, mlen);
-               if (o == NULL)
+               if (dhcp6_getoverride(ifo, opt->option))
                        continue;
-               if (env == NULL) {
-                       e++;
+               o = dhcp6_getmoption(opt->option, m, mlen);
+               if (o == NULL)
                        continue;
-               }
                ol = ntohs(o->len);
                od = D6_COPTION_DATA(o);
-               /* We only want the FQDN name */
+               /* No override, which means it's not embedded, so just
+                * grab the FQDN itself */
                if (opt->option == D6_OPTION_FQDN) {
                        ol--;
                        od++;
                }
-               len = print_option(NULL, 0, opt->type, ol, od, ifp->name);
-               if (len < 0)
-                       return -1;
-               e = strlen(prefix) + 6 + strlen(opt->var) + len + 4;
-               v = val = *ep++ = malloc(e);
-               if (v == NULL) {
-                       syslog(LOG_ERR, "%s: %m", __func__);
-                       return -1;
-               }
-               v += snprintf(val, e, "%s_dhcp6_%s=", prefix, opt->var);
-               if (len != 0)
-                       print_option(v, len, opt->type, ol, od, ifp->name);
-
+               n += dhcp_envoption(env == NULL ? NULL : &env[n],
+                   prefix, "_dhcp6", ifp->name, opt, dhcp6_getoption, od, ol);
        }
 
        if (TAILQ_FIRST(&state->addrs)) {
                if (env == NULL)
-                       e++;
+                       n++;
                else {
                        if (ifo->ia_type == D6_OPTION_IA_PD) {
                                e = strlen(prefix) +
@@ -2654,7 +2672,20 @@ dhcp6_env(char **env, const char *prefix, const struct interface *ifp,
                }
        }
 
-       if (env == NULL)
-               return e;
-       return ep - env;
+       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;
 }
diff --git a/dhcpcd-embedded.conf b/dhcpcd-embedded.conf
new file mode 100644 (file)
index 0000000..eae33a2
--- /dev/null
@@ -0,0 +1,19 @@
+# Embedded option definitions for dhcpcd(8)
+
+# DHCP option 81, Fully Qualified Domain Name, RFC4702
+define 81 embed
+embed byte fqdn_flags
+embed byte fqdn_rcode1
+embed byte fqdn_rcode2
+embed domain fqdn
+
+# DHCPv6 option 39, Fully Qualified Domain Name, RFC4704
+define6 39 embed
+embed byte fqdn_flags
+embed domain fqdn
+
+# DHCPv6 option 56 NTP Server, RFC5908
+define6 56 encap
+encap 1 ip6address ntp_server_addr
+encap 2 ip6address ntp_mcast_addr
+encap 3 ip6address ntp_fqdn
index 709dd901af3c9c6955c0a83d33c4d1d1207d6b3d..32d548f0d37ff02cfb70590b8965e11837982ba6 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 November 27, 2013
 .Dt DHCPCD.CONF 5
 .Os
 .Sh NAME
@@ -468,6 +468,94 @@ will only fork to the background when all waiting conditions are satisfied.
 Use the last four bytes of the hardware address as the DHCP xid instead
 of a randomly generated number.
 .El
+.Ss Defining new options
+DHCP allows for the use of custom options.
+Each option needs to be started with the
+.Ic define
+or
+.Ic define6
+directive.
+This can optionally be followed by both
+.Ic embed
+or
+.Ic encap
+options.
+Both can be specified more than once and
+.Ic embed
+must come before
+.Ic encap .
+.Bl -tag -width indent
+.It Ic define Ar code Ar type Ar variable
+Defines the DHCP option
+.Ar code
+of
+.Ar type
+with a name of
+.Ar variable
+exported to
+.Xr dhcpcd-run-hooks 8 .
+.It Ic define6 Ar code Ar type Ar variable
+Defines the DHCPv6 option
+.Ar code
+of
+.Ar type
+with a name of
+.Ar variable
+exported to
+.Xr dhcpcd-run-hooks 8 ,
+with a prefix of
+.Va _dhcp6 .
+.It Ic embed Ar type Ar variable
+Defines an embedded variable within the defined option.
+The length is determined by the
+.Ar type .
+.It Ic encap Ar code Ar type Ar variable
+Defines an encapsulated variable within the defined option.
+The length is determined by the
+.Ar type .
+.El
+.Ss Types to define
+If a length is unspecified by the type, it consumes the rest of the option
+data.
+.Bl -tag -width indent
+.It Ic ipaddress
+An IPv4 address, 4 bytes
+.It Ic ip6address
+An IPv6 address, 16 bytes
+.It Ic string
+A shell escaped string (binary data escaped as octal)
+.It Ic byte
+A byte
+.It Ic int16
+A signed 16bit integer, 2 bytes
+.It Ic uint16
+An unsigned 16bit integer, 2 bytes
+.It Ic int32
+A signed 32bit integer, 4 bytes
+.It Ic uint32
+An unsigned 32bit integer, 4 bytes
+.It Ic domain
+A RFC 3397 encoded string
+.It Ic binhex
+Binary data expressed as hexadecimal
+.It Ic embed
+Contains embedded options (implies encap as well)
+.It Ic encap
+Contains encapsulated options (implies embed as well)
+.El
+.Ss Example definition
+.D1 # DHCP option 81, Fully Qualified Domain Name, RFC4702
+.D1 define 81 embed
+.D1 embed byte fqdn_flags
+.D1 embed byte fqdn_rcode1
+.D1 embed byte fqdn_rcode2
+.D1 embed domain fqdn
+.Pp
+.D1 # DHCP option 125, Vendor Specific Information Option, RFC3925
+.D1 define 125 encap
+.D1 embed uint32 vsio_enterprise_number
+.D1 # Options defined for the enterprise number
+.D1 encap 1 ipaddress vsio_ipaddress
 .Sh SEE ALSO
 .Xr fnmatch 3 ,
 .Xr if_nametoindex 3 ,
index effc6e54fa4088d5ba9eb845d6f5917b76db8795..78a2979ed231ec08ff61a27a71c5db4b28fdc76b 100644 (file)
@@ -73,6 +73,10 @@ unsigned long long options = 0;
 #define O_NOIPV4               O_BASE + 16
 #define O_NOIPV6               O_BASE + 17
 #define O_IAID                 O_BASE + 18
+#define O_DEFINE               O_BASE + 19
+#define O_DEFINE6              O_BASE + 20
+#define O_EMBED                        O_BASE + 21
+#define O_ENCAP                        O_BASE + 22
 
 char *dev_load;
 
@@ -142,6 +146,10 @@ const struct option cf_options[] = {
        {"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},
        {NULL,              0,                 NULL, '\0'}
 };
 
@@ -468,10 +476,33 @@ set_option_space(const char *arg, const struct dhcp_opt **d,
        return arg;
 }
 
+/* Pointer to last defined option */
+static struct dhcp_opt *ldop;
+
+static void
+free_dhcp_opt_embenc(struct dhcp_opt *opt)
+{
+       size_t i;
+
+       free(opt->v.dvar);
+
+       for (i = 0; i < opt->embopts_len; i++)
+               free(opt->embopts[i].v.dvar);
+       free(opt->embopts);
+       opt->embopts_len = 0;
+       opt->embopts = NULL;
+
+       for (i = 0; i < opt->encopts_len; i++)
+               free(opt->encopts[i].v.dvar);
+       free(opt->encopts);
+       opt->encopts_len = 0;
+       opt->encopts = NULL;
+}
+
 static int
 parse_option(struct if_options *ifo, int opt, const char *arg)
 {
-       int i;
+       int i, t;
        char *p = NULL, *fp, *np, **nconf;
        ssize_t s;
        struct in_addr addr, addr2;
@@ -479,6 +510,8 @@ parse_option(struct if_options *ifo, int opt, const char *arg)
        struct rt *rt;
        const struct dhcp_opt *d;
        uint8_t *request, *require, *no;
+       struct dhcp_opt **dop, *ndop;
+       size_t *dop_len, dl;
 #ifdef INET6
        size_t sl;
        struct if_ia *ia;
@@ -486,6 +519,8 @@ parse_option(struct if_options *ifo, int opt, const char *arg)
        struct if_sla *sla, *slap;
 #endif
 
+       dop = NULL;
+       dop_len = NULL;
        i = 0;
        switch(opt) {
        case 'f': /* FALLTHROUGH */
@@ -1135,6 +1170,134 @@ parse_option(struct if_options *ifo, int opt, const char *arg)
        case O_NODEV:
                ifo->options &= ~DHCPCD_DEV;
                break;
+       case O_DEFINE:
+               dop = &ifo->dhcp_override;
+               dop_len = &ifo->dhcp_override_len;
+               /* FALLTHROUGH */
+       case O_DEFINE6:
+               if (dop == NULL) {
+                       dop = &ifo->dhcp6_override;
+                       dop_len = &ifo->dhcp6_override_len;
+               }
+               ldop = NULL;
+               /* FALLTHROUGH */
+       case O_EMBED:
+               if (dop == NULL) {
+                       if (ldop == NULL) {
+                               syslog(LOG_ERR, "embed must be after a define");
+                               return -1;
+                       }
+                       dop = &ldop->embopts;
+                       dop_len = &ldop->embopts_len;
+               }
+       case O_ENCAP:
+               if (dop == NULL) {
+                       if (ldop == NULL) {
+                               syslog(LOG_ERR, "encap must be after a define");
+                               return -1;
+                       }
+                       dop = &ldop->encopts;
+                       dop_len = &ldop->encopts_len;
+               }
+
+               /* Shared code for define, define6, embed and encap */
+
+               /* code */
+               if (opt == O_EMBED) /* Embedded options don't have codes */
+                       i = 0;
+               else {
+                       fp = strchr(arg, ' ');
+                       if (!fp) {
+                               syslog(LOG_ERR, "invalid syntax: %s", arg);
+                               return -1;
+                       }
+                       *fp++ = '\0';
+                       if ((i = atoint(arg)) == -1)
+                               return -1;
+                       arg = fp;
+               }
+               /* type */
+               fp = strchr(arg, ' ');
+               if (fp)
+                       *fp++ = '\0';
+               if (strcasecmp(arg, "ipaddress") == 0)
+                       t = ADDRIPV4;
+               else if (strcasecmp(arg, "ip6address") == 0)
+                       t = ADDRIPV6;
+               else if (strcasecmp(arg, "string") == 0)
+                       t = STRING;
+               else if (strcasecmp(arg, "byte") == 0)
+                       t = UINT8;
+               else if (strcasecmp(arg, "uint16") == 0)
+                       t = UINT16;
+               else if (strcasecmp(arg, "int16") == 0)
+                       t = SINT16;
+               else if (strcasecmp(arg, "uint32") == 0)
+                       t = UINT32;
+               else if (strcasecmp(arg, "int32") == 0)
+                       t = SINT32;
+               else if (strcasecmp(arg, "domain") == 0)
+                       t = STRING | RFC3397;
+               else if (strcasecmp(arg, "binhex") == 0)
+                       t = BINHEX;
+               else if (strcasecmp(arg, "embed") == 0)
+                       t = EMBED;
+               else if (strcasecmp(arg, "encap") == 0)
+                       t = ENCAP;
+               else {
+                       syslog(LOG_ERR, "unknown type: %s", arg);
+                       return -1;
+               }
+               /* variable */
+               if (fp) {
+                       arg = fp;
+                       fp = strchr(arg, ' ');
+                       if (fp)
+                               *fp++ = '\0';
+                       np = strdup(arg);
+                       if (np == NULL) {
+                               syslog(LOG_ERR, "%s: %m", __func__);
+                               return -1;
+                       }
+               } else {
+                       if (t != EMBED && t != ENCAP) {
+                               syslog(LOG_ERR,
+                                   "type %s requires a variable name",
+                                   arg);
+                               return -1;
+                       }
+                       np = NULL;
+               }
+               if (opt == O_EMBED)
+                       dl = 0;
+               else {
+                       for (dl = 0; dl < *dop_len; dl++) {
+                               ndop = &(*dop)[dl];
+                               if (ndop->option == i)
+                                       break;
+                       }
+               }
+               if (dl <= *dop_len) {
+                       if ((ndop = realloc(*dop,
+                           sizeof(**dop) * ((*dop_len) + 1))) == NULL) {
+                               syslog(LOG_ERR, "%s: %m", __func__);
+                               return -1;
+                       }
+                       *dop = ndop;
+                       ndop = &(*dop)[(*dop_len)++];
+                       ndop->option = i;
+                       ndop->embopts = NULL;
+                       ndop->embopts_len = 0;
+                       ndop->encopts = NULL;
+                       ndop->encopts_len = 0;
+               } else
+                       free_dhcp_opt_embenc(ndop);
+               ndop->type = t;
+               ndop->v.dvar = np;
+               /* Save the define for embed and encap options */
+               if (opt == O_DEFINE || opt == O_DEFINE6)
+                       ldop = ndop;
+               break;
        default:
                return 0;
        }
@@ -1216,6 +1379,27 @@ read_config(const char *file,
        ifo->vendorclassid[0] = strlen(vendor);
        memcpy(ifo->vendorclassid + 1, vendor, ifo->vendorclassid[0]);
 
+       /* Parse our embedded options file */
+       f = fopen(EMBEDDED_CONFIG, "r");
+       if (f == NULL) {
+               if (errno != ENOENT)
+                       syslog(LOG_ERR, "fopen `%s': %m", file);
+       } else {
+               while ((line = get_line(f))) {
+                       option = strsep(&line, " \t");
+                       /* Trim trailing whitespace */
+                       if (line && *line) {
+                               p = line + strlen(line) - 1;
+                               while (p != line &&
+                                   (*p == ' ' || *p == '\t') &&
+                                   *(p - 1) != '\\')
+                                       *p-- = '\0';
+                       }
+                       parse_config_line(ifo, option, line);
+               }
+               fclose(f);
+       }
+
        /* Parse our options file */
        f = fopen(file ? file : CONFIG, "r");
        if (f == NULL) {
@@ -1318,6 +1502,14 @@ free_options(struct if_options *ifo)
                free(ifo->arping);
                free(ifo->blacklist);
                free(ifo->fallback);
+
+               for (i = 0; i < ifo->dhcp_override_len; i++)
+                       free_dhcp_opt_embenc(&ifo->dhcp_override[i]);
+               free(ifo->dhcp_override);
+               for (i = 0; i < ifo->dhcp6_override_len; i++)
+                       free_dhcp_opt_embenc(&ifo->dhcp6_override[i]);
+               free(ifo->dhcp6_override);
+
 #ifdef INET6
                for (i = 0; i < ifo->ia_len; i++)
                        free(ifo->ia[i].sla);
index e3189ad40b375d4095a1028e560f011242ba874c..57cbca7ffc310d1cc2e8822559b15f69847b8a4a 100644 (file)
@@ -160,6 +160,11 @@ struct if_options {
 #ifdef INET6
        int dadtransmits;
 #endif
+
+       struct dhcp_opt *dhcp_override;
+       size_t dhcp_override_len;
+       struct dhcp_opt *dhcp6_override;
+       size_t dhcp6_override_len;
 };
 
 extern unsigned long long options;