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.
${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
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
#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
#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)
{
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;
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)
{
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)
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;
+}
#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
{ 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",
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
#ifdef DEBUG_MEMORY
static void
-free_option_buffer(void)
+dhcp_cleanup(void)
{
free(packet);
opt_buffer = malloc(sizeof(*dhcp));
if (opt_buffer == NULL)
return NULL;
-#ifdef DEBUG_MEMORY
- atexit(free_option_buffer);
-#endif
}
if (!bp)
bp = opt_buffer;
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
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)
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;
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;
}
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;
/* 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);
if (state->raw_fd == -1)
break;
}
- free(packet);
- packet = NULL;
free(dhcp);
}
{
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);
};
#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;
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
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);
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;
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:
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;
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;
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;
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)
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)
{
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;
}
}
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;
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;
}
}
+static const struct dhcp_opt *
+dhcp6_getoverride(const struct if_options *ifo, uint16_t o)
+{
+ size_t i;
+ const struct dhcp_opt *opt;
+
+ for (i = 0, opt = ifo->dhcp6_override;
+ i < ifo->dhcp6_override_len;
+ i++, opt++)
+ {
+ if (opt->option == o)
+ return opt;
+ }
+ return NULL;
+}
+
ssize_t
dhcp6_env(char **env, const char *prefix, const struct interface *ifp,
const struct dhcp6_message *m, ssize_t mlen)
const struct 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) +
}
}
- 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;
}
--- /dev/null
+# 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
.\" 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
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 ,
#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;
{"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'}
};
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;
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;
struct if_sla *sla, *slap;
#endif
+ dop = NULL;
+ dop_len = NULL;
i = 0;
switch(opt) {
case 'f': /* FALLTHROUGH */
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;
}
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) {
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);
#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;