xref: /dflybsd-src/contrib/dhcpcd/src/if-options.c (revision b2927f2b6ddfb64c8f3a45f76e21fb8e0fcbe11a)
18d36e1dfSRoy Marples /* SPDX-License-Identifier: BSD-2-Clause */
27827cba2SAaron LI /*
37827cba2SAaron LI  * dhcpcd - DHCP client daemon
46e63cc1fSRoy Marples  * Copyright (c) 2006-2020 Roy Marples <roy@marples.name>
57827cba2SAaron LI  * All rights reserved
67827cba2SAaron LI 
77827cba2SAaron LI  * Redistribution and use in source and binary forms, with or without
87827cba2SAaron LI  * modification, are permitted provided that the following conditions
97827cba2SAaron LI  * are met:
107827cba2SAaron LI  * 1. Redistributions of source code must retain the above copyright
117827cba2SAaron LI  *    notice, this list of conditions and the following disclaimer.
127827cba2SAaron LI  * 2. Redistributions in binary form must reproduce the above copyright
137827cba2SAaron LI  *    notice, this list of conditions and the following disclaimer in the
147827cba2SAaron LI  *    documentation and/or other materials provided with the distribution.
157827cba2SAaron LI  *
167827cba2SAaron LI  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
177827cba2SAaron LI  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
187827cba2SAaron LI  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
197827cba2SAaron LI  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
207827cba2SAaron LI  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
217827cba2SAaron LI  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
227827cba2SAaron LI  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
237827cba2SAaron LI  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
247827cba2SAaron LI  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
257827cba2SAaron LI  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
267827cba2SAaron LI  * SUCH DAMAGE.
277827cba2SAaron LI  */
287827cba2SAaron LI 
297827cba2SAaron LI #include <sys/param.h>
307827cba2SAaron LI #include <sys/types.h>
317827cba2SAaron LI 
327827cba2SAaron LI #include <arpa/inet.h>
337827cba2SAaron LI 
347827cba2SAaron LI #include <ctype.h>
357827cba2SAaron LI #include <errno.h>
367827cba2SAaron LI #include <getopt.h>
377827cba2SAaron LI #include <grp.h>
387827cba2SAaron LI #include <inttypes.h>
397827cba2SAaron LI #include <limits.h>
407827cba2SAaron LI #include <paths.h>
417827cba2SAaron LI #include <stdio.h>
427827cba2SAaron LI #include <stdlib.h>
437827cba2SAaron LI #include <string.h>
447827cba2SAaron LI #include <unistd.h>
457827cba2SAaron LI #include <time.h>
467827cba2SAaron LI 
477827cba2SAaron LI #include "config.h"
487827cba2SAaron LI #include "common.h"
497827cba2SAaron LI #include "dhcp.h"
507827cba2SAaron LI #include "dhcp6.h"
517827cba2SAaron LI #include "dhcpcd-embedded.h"
52a0d9933aSRoy Marples #include "duid.h"
537827cba2SAaron LI #include "if.h"
547827cba2SAaron LI #include "if-options.h"
557827cba2SAaron LI #include "ipv4.h"
567827cba2SAaron LI #include "logerr.h"
577827cba2SAaron LI #include "sa.h"
587827cba2SAaron LI 
59cc34ba0cSRoy Marples #define	IN_CONFIG_BLOCK(ifo)	((ifo)->options & DHCPCD_FORKED)
60cc34ba0cSRoy Marples #define	SET_CONFIG_BLOCK(ifo)	((ifo)->options |= DHCPCD_FORKED)
61cc34ba0cSRoy Marples #define	CLEAR_CONFIG_BLOCK(ifo)	((ifo)->options &= ~DHCPCD_FORKED)
62cc34ba0cSRoy Marples 
63b8b69544SRoy Marples static unsigned long long default_options;
64b8b69544SRoy Marples 
657827cba2SAaron LI const struct option cf_options[] = {
667827cba2SAaron LI 	{"background",      no_argument,       NULL, 'b'},
677827cba2SAaron LI 	{"script",          required_argument, NULL, 'c'},
687827cba2SAaron LI 	{"debug",           no_argument,       NULL, 'd'},
697827cba2SAaron LI 	{"env",             required_argument, NULL, 'e'},
707827cba2SAaron LI 	{"config",          required_argument, NULL, 'f'},
717827cba2SAaron LI 	{"reconfigure",     no_argument,       NULL, 'g'},
727827cba2SAaron LI 	{"hostname",        optional_argument, NULL, 'h'},
737827cba2SAaron LI 	{"vendorclassid",   optional_argument, NULL, 'i'},
747827cba2SAaron LI 	{"logfile",         required_argument, NULL, 'j'},
757827cba2SAaron LI 	{"release",         no_argument,       NULL, 'k'},
767827cba2SAaron LI 	{"leasetime",       required_argument, NULL, 'l'},
777827cba2SAaron LI 	{"metric",          required_argument, NULL, 'm'},
787827cba2SAaron LI 	{"rebind",          no_argument,       NULL, 'n'},
797827cba2SAaron LI 	{"option",          required_argument, NULL, 'o'},
807827cba2SAaron LI 	{"persistent",      no_argument,       NULL, 'p'},
817827cba2SAaron LI 	{"quiet",           no_argument,       NULL, 'q'},
827827cba2SAaron LI 	{"request",         optional_argument, NULL, 'r'},
837827cba2SAaron LI 	{"inform",          optional_argument, NULL, 's'},
847827cba2SAaron LI 	{"inform6",         optional_argument, NULL, O_INFORM6},
857827cba2SAaron LI 	{"timeout",         required_argument, NULL, 't'},
867827cba2SAaron LI 	{"userclass",       required_argument, NULL, 'u'},
876e63cc1fSRoy Marples #ifndef SMALL
886e63cc1fSRoy Marples 	{"msuserclass",     required_argument, NULL, O_MSUSERCLASS},
896e63cc1fSRoy Marples #endif
907827cba2SAaron LI 	{"vendor",          required_argument, NULL, 'v'},
917827cba2SAaron LI 	{"waitip",          optional_argument, NULL, 'w'},
927827cba2SAaron LI 	{"exit",            no_argument,       NULL, 'x'},
937827cba2SAaron LI 	{"allowinterfaces", required_argument, NULL, 'z'},
947827cba2SAaron LI 	{"reboot",          required_argument, NULL, 'y'},
957827cba2SAaron LI 	{"noarp",           no_argument,       NULL, 'A'},
967827cba2SAaron LI 	{"nobackground",    no_argument,       NULL, 'B'},
977827cba2SAaron LI 	{"nohook",          required_argument, NULL, 'C'},
98a0d9933aSRoy Marples 	{"duid",            optional_argument, NULL, 'D'},
997827cba2SAaron LI 	{"lastlease",       no_argument,       NULL, 'E'},
1007827cba2SAaron LI 	{"fqdn",            optional_argument, NULL, 'F'},
1017827cba2SAaron LI 	{"nogateway",       no_argument,       NULL, 'G'},
1027827cba2SAaron LI 	{"xidhwaddr",       no_argument,       NULL, 'H'},
1037827cba2SAaron LI 	{"clientid",        optional_argument, NULL, 'I'},
1047827cba2SAaron LI 	{"broadcast",       no_argument,       NULL, 'J'},
1057827cba2SAaron LI 	{"nolink",          no_argument,       NULL, 'K'},
1067827cba2SAaron LI 	{"noipv4ll",        no_argument,       NULL, 'L'},
1077827cba2SAaron LI 	{"master",          no_argument,       NULL, 'M'},
1087827cba2SAaron LI 	{"renew",           no_argument,       NULL, 'N'},
1097827cba2SAaron LI 	{"nooption",        required_argument, NULL, 'O'},
1107827cba2SAaron LI 	{"printpidfile",    no_argument,       NULL, 'P'},
1117827cba2SAaron LI 	{"require",         required_argument, NULL, 'Q'},
1127827cba2SAaron LI 	{"static",          required_argument, NULL, 'S'},
1137827cba2SAaron LI 	{"test",            no_argument,       NULL, 'T'},
1147827cba2SAaron LI 	{"dumplease",       no_argument,       NULL, 'U'},
1157827cba2SAaron LI 	{"variables",       no_argument,       NULL, 'V'},
1167827cba2SAaron LI 	{"whitelist",       required_argument, NULL, 'W'},
1177827cba2SAaron LI 	{"blacklist",       required_argument, NULL, 'X'},
1187827cba2SAaron LI 	{"denyinterfaces",  required_argument, NULL, 'Z'},
1197827cba2SAaron LI 	{"oneshot",         no_argument,       NULL, '1'},
1207827cba2SAaron LI 	{"ipv4only",        no_argument,       NULL, '4'},
1217827cba2SAaron LI 	{"ipv6only",        no_argument,       NULL, '6'},
1226e63cc1fSRoy Marples 	{"anonymous",       no_argument,       NULL, O_ANONYMOUS},
1237827cba2SAaron LI 	{"arping",          required_argument, NULL, O_ARPING},
1247827cba2SAaron LI 	{"destination",     required_argument, NULL, O_DESTINATION},
1257827cba2SAaron LI 	{"fallback",        required_argument, NULL, O_FALLBACK},
1267827cba2SAaron LI 	{"ipv6rs",          no_argument,       NULL, O_IPV6RS},
1277827cba2SAaron LI 	{"noipv6rs",        no_argument,       NULL, O_NOIPV6RS},
1287827cba2SAaron LI 	{"ipv6ra_autoconf", no_argument,       NULL, O_IPV6RA_AUTOCONF},
1297827cba2SAaron LI 	{"ipv6ra_noautoconf", no_argument,     NULL, O_IPV6RA_NOAUTOCONF},
1307827cba2SAaron LI 	{"ipv6ra_fork",     no_argument,       NULL, O_IPV6RA_FORK},
1317827cba2SAaron LI 	{"ipv4",            no_argument,       NULL, O_IPV4},
1327827cba2SAaron LI 	{"noipv4",          no_argument,       NULL, O_NOIPV4},
1337827cba2SAaron LI 	{"ipv6",            no_argument,       NULL, O_IPV6},
1347827cba2SAaron LI 	{"noipv6",          no_argument,       NULL, O_NOIPV6},
1357827cba2SAaron LI 	{"noalias",         no_argument,       NULL, O_NOALIAS},
1367827cba2SAaron LI 	{"iaid",            required_argument, NULL, O_IAID},
1377827cba2SAaron LI 	{"ia_na",           no_argument,       NULL, O_IA_NA},
1387827cba2SAaron LI 	{"ia_ta",           no_argument,       NULL, O_IA_TA},
1397827cba2SAaron LI 	{"ia_pd",           no_argument,       NULL, O_IA_PD},
1407827cba2SAaron LI 	{"hostname_short",  no_argument,       NULL, O_HOSTNAME_SHORT},
1417827cba2SAaron LI 	{"dev",             required_argument, NULL, O_DEV},
1427827cba2SAaron LI 	{"nodev",           no_argument,       NULL, O_NODEV},
1437827cba2SAaron LI 	{"define",          required_argument, NULL, O_DEFINE},
1447827cba2SAaron LI 	{"definend",        required_argument, NULL, O_DEFINEND},
1457827cba2SAaron LI 	{"define6",         required_argument, NULL, O_DEFINE6},
1467827cba2SAaron LI 	{"embed",           required_argument, NULL, O_EMBED},
1477827cba2SAaron LI 	{"encap",           required_argument, NULL, O_ENCAP},
1487827cba2SAaron LI 	{"vendopt",         required_argument, NULL, O_VENDOPT},
1497827cba2SAaron LI 	{"vendclass",       required_argument, NULL, O_VENDCLASS},
1507827cba2SAaron LI 	{"authprotocol",    required_argument, NULL, O_AUTHPROTOCOL},
1517827cba2SAaron LI 	{"authtoken",       required_argument, NULL, O_AUTHTOKEN},
1527827cba2SAaron LI 	{"noauthrequired",  no_argument,       NULL, O_AUTHNOTREQUIRED},
1537827cba2SAaron LI 	{"dhcp",            no_argument,       NULL, O_DHCP},
1547827cba2SAaron LI 	{"nodhcp",          no_argument,       NULL, O_NODHCP},
1557827cba2SAaron LI 	{"dhcp6",           no_argument,       NULL, O_DHCP6},
1567827cba2SAaron LI 	{"nodhcp6",         no_argument,       NULL, O_NODHCP6},
1577827cba2SAaron LI 	{"controlgroup",    required_argument, NULL, O_CONTROLGRP},
1587827cba2SAaron LI 	{"slaac",           required_argument, NULL, O_SLAAC},
1597827cba2SAaron LI 	{"gateway",         no_argument,       NULL, O_GATEWAY},
1607827cba2SAaron LI 	{"reject",          required_argument, NULL, O_REJECT},
1617827cba2SAaron LI 	{"bootp",           no_argument,       NULL, O_BOOTP},
1627827cba2SAaron LI 	{"nodelay",         no_argument,       NULL, O_NODELAY},
1637827cba2SAaron LI 	{"noup",            no_argument,       NULL, O_NOUP},
1647827cba2SAaron LI 	{"lastleaseextend", no_argument,       NULL, O_LASTLEASE_EXTEND},
1657827cba2SAaron LI 	{"inactive",        no_argument,       NULL, O_INACTIVE},
1667827cba2SAaron LI 	{"mudurl",          required_argument, NULL, O_MUDURL},
1678d36e1dfSRoy Marples 	{"link_rcvbuf",     required_argument, NULL, O_LINK_RCVBUF},
168*b2927f2bSRoy Marples 	{"configure",       no_argument,       NULL, O_CONFIGURE},
169*b2927f2bSRoy Marples 	{"noconfigure",     no_argument,       NULL, O_NOCONFIGURE},
1707827cba2SAaron LI 	{NULL,              0,                 NULL, '\0'}
1717827cba2SAaron LI };
1727827cba2SAaron LI 
1737827cba2SAaron LI static char *
1748d36e1dfSRoy Marples add_environ(char ***array, const char *value, int uniq)
1757827cba2SAaron LI {
1768d36e1dfSRoy Marples 	char **newlist, **list = *array;
1777827cba2SAaron LI 	size_t i = 0, l, lv;
1787827cba2SAaron LI 	char *match = NULL, *p, *n;
1797827cba2SAaron LI 
1807827cba2SAaron LI 	match = strdup(value);
1817827cba2SAaron LI 	if (match == NULL) {
1827827cba2SAaron LI 		logerr(__func__);
1837827cba2SAaron LI 		return NULL;
1847827cba2SAaron LI 	}
1857827cba2SAaron LI 	p = strchr(match, '=');
1867827cba2SAaron LI 	if (p == NULL) {
1877827cba2SAaron LI 		logerrx("%s: no assignment: %s", __func__, value);
1887827cba2SAaron LI 		free(match);
1897827cba2SAaron LI 		return NULL;
1907827cba2SAaron LI 	}
1917827cba2SAaron LI 	*p++ = '\0';
1927827cba2SAaron LI 	l = strlen(match);
1937827cba2SAaron LI 
1948d36e1dfSRoy Marples 	while (list && list[i]) {
1958d36e1dfSRoy Marples 		if (match && strncmp(list[i], match, l) == 0) {
1967827cba2SAaron LI 			if (uniq) {
1977827cba2SAaron LI 				n = strdup(value);
1987827cba2SAaron LI 				if (n == NULL) {
1997827cba2SAaron LI 					logerr(__func__);
2007827cba2SAaron LI 					free(match);
2017827cba2SAaron LI 					return NULL;
2027827cba2SAaron LI 				}
2038d36e1dfSRoy Marples 				free(list[i]);
2048d36e1dfSRoy Marples 				list[i] = n;
2057827cba2SAaron LI 			} else {
2067827cba2SAaron LI 				/* Append a space and the value to it */
2078d36e1dfSRoy Marples 				l = strlen(list[i]);
2087827cba2SAaron LI 				lv = strlen(p);
2098d36e1dfSRoy Marples 				n = realloc(list[i], l + lv + 2);
2107827cba2SAaron LI 				if (n == NULL) {
2117827cba2SAaron LI 					logerr(__func__);
2127827cba2SAaron LI 					free(match);
2137827cba2SAaron LI 					return NULL;
2147827cba2SAaron LI 				}
2158d36e1dfSRoy Marples 				list[i] = n;
2168d36e1dfSRoy Marples 				list[i][l] = ' ';
2178d36e1dfSRoy Marples 				memcpy(list[i] + l + 1, p, lv);
2188d36e1dfSRoy Marples 				list[i][l + lv + 1] = '\0';
2197827cba2SAaron LI 			}
2207827cba2SAaron LI 			free(match);
2218d36e1dfSRoy Marples 			return list[i];
2227827cba2SAaron LI 		}
2237827cba2SAaron LI 		i++;
2247827cba2SAaron LI 	}
2257827cba2SAaron LI 
2267827cba2SAaron LI 	free(match);
2277827cba2SAaron LI 	n = strdup(value);
2287827cba2SAaron LI 	if (n == NULL) {
2297827cba2SAaron LI 		logerr(__func__);
2307827cba2SAaron LI 		return NULL;
2317827cba2SAaron LI 	}
2328d36e1dfSRoy Marples 	newlist = reallocarray(list, i + 2, sizeof(char *));
2337827cba2SAaron LI 	if (newlist == NULL) {
2347827cba2SAaron LI 		logerr(__func__);
2357827cba2SAaron LI 		free(n);
2367827cba2SAaron LI 		return NULL;
2377827cba2SAaron LI 	}
2387827cba2SAaron LI 	newlist[i] = n;
2397827cba2SAaron LI 	newlist[i + 1] = NULL;
2408d36e1dfSRoy Marples 	*array = newlist;
2417827cba2SAaron LI 	return newlist[i];
2427827cba2SAaron LI }
2437827cba2SAaron LI 
2448d36e1dfSRoy Marples #define PARSE_STRING		0
2458d36e1dfSRoy Marples #define PARSE_STRING_NULL	1
2468d36e1dfSRoy Marples #define PARSE_HWADDR		2
2478d36e1dfSRoy Marples #define parse_string(a, b, c) parse_str((a), (b), (c), PARSE_STRING)
2486e63cc1fSRoy Marples #define parse_nstring(a, b, c) parse_str((a), (b), (c), PARSE_STRING_NULL)
2498d36e1dfSRoy Marples #define parse_hwaddr(a, b, c) parse_str((a), (b), (c), PARSE_HWADDR)
2507827cba2SAaron LI static ssize_t
2518d36e1dfSRoy Marples parse_str(char *sbuf, size_t slen, const char *str, int flags)
2527827cba2SAaron LI {
2537827cba2SAaron LI 	size_t l;
2548d36e1dfSRoy Marples 	const char *p, *end;
2558d36e1dfSRoy Marples 	int i;
2567827cba2SAaron LI 	char c[4], cmd;
2577827cba2SAaron LI 
2588d36e1dfSRoy Marples 	end = str + strlen(str);
2597827cba2SAaron LI 	/* If surrounded by quotes then it's a string */
2607827cba2SAaron LI 	if (*str == '"') {
2618d36e1dfSRoy Marples 		p = end - 1;
2628d36e1dfSRoy Marples 		if (*p == '"') {
2637827cba2SAaron LI 			str++;
2648d36e1dfSRoy Marples 			end = p;
2658d36e1dfSRoy Marples 		}
2667827cba2SAaron LI 	} else {
2677827cba2SAaron LI 		l = (size_t)hwaddr_aton(NULL, str);
2687827cba2SAaron LI 		if ((ssize_t) l != -1 && l > 1) {
2697827cba2SAaron LI 			if (l > slen) {
2707827cba2SAaron LI 				errno = ENOBUFS;
2717827cba2SAaron LI 				return -1;
2727827cba2SAaron LI 			}
2737827cba2SAaron LI 			hwaddr_aton((uint8_t *)sbuf, str);
2747827cba2SAaron LI 			return (ssize_t)l;
2757827cba2SAaron LI 		}
2767827cba2SAaron LI 	}
2777827cba2SAaron LI 
2787827cba2SAaron LI 	/* Process escapes */
2797827cba2SAaron LI 	l = 0;
2807827cba2SAaron LI 	/* If processing a string on the clientid, first byte should be
2817827cba2SAaron LI 	 * 0 to indicate a non hardware type */
2828d36e1dfSRoy Marples 	if (flags == PARSE_HWADDR && *str) {
2837827cba2SAaron LI 		if (sbuf)
2847827cba2SAaron LI 			*sbuf++ = 0;
2857827cba2SAaron LI 		l++;
2867827cba2SAaron LI 	}
2877827cba2SAaron LI 	c[3] = '\0';
2888d36e1dfSRoy Marples 	while (str < end) {
2897827cba2SAaron LI 		if (++l > slen && sbuf) {
2907827cba2SAaron LI 			errno = ENOBUFS;
2917827cba2SAaron LI 			return -1;
2927827cba2SAaron LI 		}
2937827cba2SAaron LI 		if (*str == '\\') {
2947827cba2SAaron LI 			str++;
2957827cba2SAaron LI 			switch((cmd = *str++)) {
2967827cba2SAaron LI 			case '\0':
2977827cba2SAaron LI 				str--;
2987827cba2SAaron LI 				break;
2997827cba2SAaron LI 			case 'b':
3007827cba2SAaron LI 				if (sbuf)
3017827cba2SAaron LI 					*sbuf++ = '\b';
3027827cba2SAaron LI 				break;
3037827cba2SAaron LI 			case 'n':
3047827cba2SAaron LI 				if (sbuf)
3057827cba2SAaron LI 					*sbuf++ = '\n';
3067827cba2SAaron LI 				break;
3077827cba2SAaron LI 			case 'r':
3087827cba2SAaron LI 				if (sbuf)
3097827cba2SAaron LI 					*sbuf++ = '\r';
3107827cba2SAaron LI 				break;
3117827cba2SAaron LI 			case 't':
3127827cba2SAaron LI 				if (sbuf)
3137827cba2SAaron LI 					*sbuf++ = '\t';
3147827cba2SAaron LI 				break;
3157827cba2SAaron LI 			case 'x':
3167827cba2SAaron LI 				/* Grab a hex code */
3177827cba2SAaron LI 				c[1] = '\0';
3187827cba2SAaron LI 				for (i = 0; i < 2; i++) {
3197827cba2SAaron LI 					if (isxdigit((unsigned char)*str) == 0)
3207827cba2SAaron LI 						break;
3217827cba2SAaron LI 					c[i] = *str++;
3227827cba2SAaron LI 				}
3237827cba2SAaron LI 				if (c[1] != '\0' && sbuf) {
3247827cba2SAaron LI 					c[2] = '\0';
3257827cba2SAaron LI 					*sbuf++ = (char)strtol(c, NULL, 16);
3267827cba2SAaron LI 				} else
3277827cba2SAaron LI 					l--;
3287827cba2SAaron LI 				break;
3297827cba2SAaron LI 			case '0':
3307827cba2SAaron LI 				/* Grab an octal code */
3317827cba2SAaron LI 				c[2] = '\0';
3327827cba2SAaron LI 				for (i = 0; i < 3; i++) {
3337827cba2SAaron LI 					if (*str < '0' || *str > '7')
3347827cba2SAaron LI 						break;
3357827cba2SAaron LI 					c[i] = *str++;
3367827cba2SAaron LI 				}
3377827cba2SAaron LI 				if (c[2] != '\0' && sbuf) {
3387827cba2SAaron LI 					i = (int)strtol(c, NULL, 8);
3397827cba2SAaron LI 					if (i > 255)
3407827cba2SAaron LI 						i = 255;
3417827cba2SAaron LI 					*sbuf ++= (char)i;
3427827cba2SAaron LI 				} else
3437827cba2SAaron LI 					l--;
3447827cba2SAaron LI 				break;
3457827cba2SAaron LI 			default:
3467827cba2SAaron LI 				if (sbuf)
3477827cba2SAaron LI 					*sbuf++ = cmd;
3487827cba2SAaron LI 				break;
3497827cba2SAaron LI 			}
3507827cba2SAaron LI 		} else {
3517827cba2SAaron LI 			if (sbuf)
3527827cba2SAaron LI 				*sbuf++ = *str;
3537827cba2SAaron LI 			str++;
3547827cba2SAaron LI 		}
3557827cba2SAaron LI 	}
356e0bc1ec6SRoy Marples 	if (flags == PARSE_STRING_NULL) {
357e0bc1ec6SRoy Marples 		l++;
358e0bc1ec6SRoy Marples 		if (sbuf != NULL) {
359e0bc1ec6SRoy Marples 			if (l > slen) {
360e0bc1ec6SRoy Marples 				errno = ENOBUFS;
361e0bc1ec6SRoy Marples 				return -1;
362e0bc1ec6SRoy Marples 			}
3638d36e1dfSRoy Marples 			*sbuf = '\0';
364e0bc1ec6SRoy Marples 		}
365e0bc1ec6SRoy Marples 	}
3667827cba2SAaron LI 	return (ssize_t)l;
3677827cba2SAaron LI }
3687827cba2SAaron LI 
3697827cba2SAaron LI static int
3707827cba2SAaron LI parse_iaid1(uint8_t *iaid, const char *arg, size_t len, int n)
3717827cba2SAaron LI {
3727827cba2SAaron LI 	int e;
3737827cba2SAaron LI 	uint32_t narg;
3747827cba2SAaron LI 	ssize_t s;
3757827cba2SAaron LI 
3767827cba2SAaron LI 	narg = (uint32_t)strtou(arg, NULL, 0, 0, UINT32_MAX, &e);
3777827cba2SAaron LI 	if (e == 0) {
3787827cba2SAaron LI 		if (n)
3797827cba2SAaron LI 			narg = htonl(narg);
3807827cba2SAaron LI 		memcpy(iaid, &narg, sizeof(narg));
3817827cba2SAaron LI 		return 0;
3827827cba2SAaron LI 	}
3837827cba2SAaron LI 
3847827cba2SAaron LI 	if ((s = parse_string((char *)iaid, len, arg)) < 1)
3857827cba2SAaron LI 		return -1;
3867827cba2SAaron LI 	if (s < 4)
3877827cba2SAaron LI 		iaid[3] = '\0';
3887827cba2SAaron LI 	if (s < 3)
3897827cba2SAaron LI 		iaid[2] = '\0';
3907827cba2SAaron LI 	if (s < 2)
3917827cba2SAaron LI 		iaid[1] = '\0';
3927827cba2SAaron LI 	return 0;
3937827cba2SAaron LI }
3947827cba2SAaron LI 
3957827cba2SAaron LI static int
3967827cba2SAaron LI parse_iaid(uint8_t *iaid, const char *arg, size_t len)
3977827cba2SAaron LI {
3987827cba2SAaron LI 
3997827cba2SAaron LI 	return parse_iaid1(iaid, arg, len, 1);
4007827cba2SAaron LI }
4017827cba2SAaron LI 
4027827cba2SAaron LI #ifdef AUTH
4037827cba2SAaron LI static int
4047827cba2SAaron LI parse_uint32(uint32_t *i, const char *arg)
4057827cba2SAaron LI {
4067827cba2SAaron LI 
4077827cba2SAaron LI 	return parse_iaid1((uint8_t *)i, arg, sizeof(uint32_t), 0);
4087827cba2SAaron LI }
4097827cba2SAaron LI #endif
4107827cba2SAaron LI 
4117827cba2SAaron LI static char **
4127827cba2SAaron LI splitv(int *argc, char **argv, const char *arg)
4137827cba2SAaron LI {
4147827cba2SAaron LI 	char **n, **v = argv;
4157827cba2SAaron LI 	char *o = strdup(arg), *p, *t, *nt;
4167827cba2SAaron LI 
4177827cba2SAaron LI 	if (o == NULL) {
4187827cba2SAaron LI 		logerr(__func__);
4197827cba2SAaron LI 		return v;
4207827cba2SAaron LI 	}
4217827cba2SAaron LI 	p = o;
4227827cba2SAaron LI 	while ((t = strsep(&p, ", "))) {
4237827cba2SAaron LI 		nt = strdup(t);
4247827cba2SAaron LI 		if (nt == NULL) {
4257827cba2SAaron LI 			logerr(__func__);
4267827cba2SAaron LI 			free(o);
4277827cba2SAaron LI 			return v;
4287827cba2SAaron LI 		}
4297827cba2SAaron LI 		n = reallocarray(v, (size_t)(*argc) + 1, sizeof(char *));
4307827cba2SAaron LI 		if (n == NULL) {
4317827cba2SAaron LI 			logerr(__func__);
4327827cba2SAaron LI 			free(o);
4337827cba2SAaron LI 			free(nt);
4347827cba2SAaron LI 			return v;
4357827cba2SAaron LI 		}
4367827cba2SAaron LI 		v = n;
4377827cba2SAaron LI 		v[(*argc)++] = nt;
4387827cba2SAaron LI 	}
4397827cba2SAaron LI 	free(o);
4407827cba2SAaron LI 	return v;
4417827cba2SAaron LI }
4427827cba2SAaron LI 
4437827cba2SAaron LI #ifdef INET
4447827cba2SAaron LI static int
4457827cba2SAaron LI parse_addr(struct in_addr *addr, struct in_addr *net, const char *arg)
4467827cba2SAaron LI {
4477827cba2SAaron LI 	char *p;
4487827cba2SAaron LI 
4497827cba2SAaron LI 	if (arg == NULL || *arg == '\0') {
4507827cba2SAaron LI 		if (addr != NULL)
4517827cba2SAaron LI 			addr->s_addr = 0;
4527827cba2SAaron LI 		if (net != NULL)
4537827cba2SAaron LI 			net->s_addr = 0;
4547827cba2SAaron LI 		return 0;
4557827cba2SAaron LI 	}
4567827cba2SAaron LI 	if ((p = strchr(arg, '/')) != NULL) {
4577827cba2SAaron LI 		int e;
4587827cba2SAaron LI 		intmax_t i;
4597827cba2SAaron LI 
4607827cba2SAaron LI 		*p++ = '\0';
4617827cba2SAaron LI 		i = strtoi(p, NULL, 10, 0, 32, &e);
4627827cba2SAaron LI 		if (e != 0 ||
4637827cba2SAaron LI 		    (net != NULL && inet_cidrtoaddr((int)i, net) != 0))
4647827cba2SAaron LI 		{
465a0d9933aSRoy Marples 			logerrx("invalid CIDR: %s", p);
4667827cba2SAaron LI 			return -1;
4677827cba2SAaron LI 		}
4687827cba2SAaron LI 	}
4697827cba2SAaron LI 
4707827cba2SAaron LI 	if (addr != NULL && inet_aton(arg, addr) == 0) {
471a0d9933aSRoy Marples 		logerrx("invalid IP address: %s", arg);
4727827cba2SAaron LI 		return -1;
4737827cba2SAaron LI 	}
4747827cba2SAaron LI 	if (p != NULL)
4757827cba2SAaron LI 		*--p = '/';
4767827cba2SAaron LI 	else if (net != NULL && addr != NULL)
4777827cba2SAaron LI 		net->s_addr = ipv4_getnetmask(addr->s_addr);
4787827cba2SAaron LI 	return 0;
4797827cba2SAaron LI }
4807827cba2SAaron LI #else
4817827cba2SAaron LI static int
4827827cba2SAaron LI parse_addr(__unused struct in_addr *addr, __unused struct in_addr *net,
4837827cba2SAaron LI     __unused const char *arg)
4847827cba2SAaron LI {
4857827cba2SAaron LI 
4867827cba2SAaron LI 	logerrx("No IPv4 support");
4877827cba2SAaron LI 	return -1;
4887827cba2SAaron LI }
4897827cba2SAaron LI #endif
4907827cba2SAaron LI 
4911b3b16a2SRoy Marples static void
4927827cba2SAaron LI set_option_space(struct dhcpcd_ctx *ctx,
4937827cba2SAaron LI     const char *arg,
4947827cba2SAaron LI     const struct dhcp_opt **d, size_t *dl,
4957827cba2SAaron LI     const struct dhcp_opt **od, size_t *odl,
4967827cba2SAaron LI     struct if_options *ifo,
4977827cba2SAaron LI     uint8_t *request[], uint8_t *require[], uint8_t *no[], uint8_t *reject[])
4987827cba2SAaron LI {
4997827cba2SAaron LI 
5007827cba2SAaron LI #if !defined(INET) && !defined(INET6)
5017827cba2SAaron LI 	UNUSED(ctx);
5027827cba2SAaron LI #endif
5037827cba2SAaron LI 
5047827cba2SAaron LI #ifdef INET6
5057827cba2SAaron LI 	if (strncmp(arg, "nd_", strlen("nd_")) == 0) {
5067827cba2SAaron LI 		*d = ctx->nd_opts;
5077827cba2SAaron LI 		*dl = ctx->nd_opts_len;
5087827cba2SAaron LI 		*od = ifo->nd_override;
5097827cba2SAaron LI 		*odl = ifo->nd_override_len;
5107827cba2SAaron LI 		*request = ifo->requestmasknd;
5117827cba2SAaron LI 		*require = ifo->requiremasknd;
5127827cba2SAaron LI 		*no = ifo->nomasknd;
5137827cba2SAaron LI 		*reject = ifo->rejectmasknd;
5141b3b16a2SRoy Marples 		return;
5157827cba2SAaron LI 	}
5167827cba2SAaron LI 
5178d36e1dfSRoy Marples #ifdef DHCP6
5187827cba2SAaron LI 	if (strncmp(arg, "dhcp6_", strlen("dhcp6_")) == 0) {
5197827cba2SAaron LI 		*d = ctx->dhcp6_opts;
5207827cba2SAaron LI 		*dl = ctx->dhcp6_opts_len;
5217827cba2SAaron LI 		*od = ifo->dhcp6_override;
5227827cba2SAaron LI 		*odl = ifo->dhcp6_override_len;
5237827cba2SAaron LI 		*request = ifo->requestmask6;
5247827cba2SAaron LI 		*require = ifo->requiremask6;
5257827cba2SAaron LI 		*no = ifo->nomask6;
5267827cba2SAaron LI 		*reject = ifo->rejectmask6;
5271b3b16a2SRoy Marples 		return;
5287827cba2SAaron LI 	}
5297827cba2SAaron LI #endif
530280986e4SRoy Marples #else
531280986e4SRoy Marples 	UNUSED(arg);
5328d36e1dfSRoy Marples #endif
5337827cba2SAaron LI 
5347827cba2SAaron LI #ifdef INET
5357827cba2SAaron LI 	*d = ctx->dhcp_opts;
5367827cba2SAaron LI 	*dl = ctx->dhcp_opts_len;
5377827cba2SAaron LI 	*od = ifo->dhcp_override;
5387827cba2SAaron LI 	*odl = ifo->dhcp_override_len;
5397827cba2SAaron LI #else
5407827cba2SAaron LI 	*d = NULL;
5417827cba2SAaron LI 	*dl = 0;
5427827cba2SAaron LI 	*od = NULL;
5437827cba2SAaron LI 	*odl = 0;
5447827cba2SAaron LI #endif
5457827cba2SAaron LI 	*request = ifo->requestmask;
5467827cba2SAaron LI 	*require = ifo->requiremask;
5477827cba2SAaron LI 	*no = ifo->nomask;
5487827cba2SAaron LI 	*reject = ifo->rejectmask;
5497827cba2SAaron LI }
5507827cba2SAaron LI 
5517827cba2SAaron LI void
5527827cba2SAaron LI free_dhcp_opt_embenc(struct dhcp_opt *opt)
5537827cba2SAaron LI {
5547827cba2SAaron LI 	size_t i;
5557827cba2SAaron LI 	struct dhcp_opt *o;
5567827cba2SAaron LI 
5577827cba2SAaron LI 	free(opt->var);
5587827cba2SAaron LI 
5597827cba2SAaron LI 	for (i = 0, o = opt->embopts; i < opt->embopts_len; i++, o++)
5607827cba2SAaron LI 		free_dhcp_opt_embenc(o);
5617827cba2SAaron LI 	free(opt->embopts);
5627827cba2SAaron LI 	opt->embopts_len = 0;
5637827cba2SAaron LI 	opt->embopts = NULL;
5647827cba2SAaron LI 
5657827cba2SAaron LI 	for (i = 0, o = opt->encopts; i < opt->encopts_len; i++, o++)
5667827cba2SAaron LI 		free_dhcp_opt_embenc(o);
5677827cba2SAaron LI 	free(opt->encopts);
5687827cba2SAaron LI 	opt->encopts_len = 0;
5697827cba2SAaron LI 	opt->encopts = NULL;
5707827cba2SAaron LI }
5717827cba2SAaron LI 
5727827cba2SAaron LI static char *
5737827cba2SAaron LI strwhite(const char *s)
5747827cba2SAaron LI {
5757827cba2SAaron LI 
5767827cba2SAaron LI 	if (s == NULL)
5777827cba2SAaron LI 		return NULL;
5787827cba2SAaron LI 	while (*s != ' ' && *s != '\t') {
5797827cba2SAaron LI 		if (*s == '\0')
5807827cba2SAaron LI 			return NULL;
5817827cba2SAaron LI 		s++;
5827827cba2SAaron LI 	}
5837827cba2SAaron LI 	return UNCONST(s);
5847827cba2SAaron LI }
5857827cba2SAaron LI 
5867827cba2SAaron LI static char *
5877827cba2SAaron LI strskipwhite(const char *s)
5887827cba2SAaron LI {
5897827cba2SAaron LI 
5907827cba2SAaron LI 	if (s == NULL || *s == '\0')
5917827cba2SAaron LI 		return NULL;
5927827cba2SAaron LI 	while (*s == ' ' || *s == '\t') {
5937827cba2SAaron LI 		s++;
5947827cba2SAaron LI 		if (*s == '\0')
5957827cba2SAaron LI 			return NULL;
5967827cba2SAaron LI 	}
5977827cba2SAaron LI 	return UNCONST(s);
5987827cba2SAaron LI }
5997827cba2SAaron LI 
6007827cba2SAaron LI #ifdef AUTH
6017827cba2SAaron LI /* Find the end pointer of a string. */
6027827cba2SAaron LI static char *
6037827cba2SAaron LI strend(const char *s)
6047827cba2SAaron LI {
6057827cba2SAaron LI 
6067827cba2SAaron LI 	s = strskipwhite(s);
6077827cba2SAaron LI 	if (s == NULL)
6087827cba2SAaron LI 		return NULL;
6097827cba2SAaron LI 	if (*s != '"')
6107827cba2SAaron LI 		return strchr(s, ' ');
6117827cba2SAaron LI 	s++;
6127827cba2SAaron LI 	for (; *s != '"' ; s++) {
6137827cba2SAaron LI 		if (*s == '\0')
6147827cba2SAaron LI 			return NULL;
6157827cba2SAaron LI 		if (*s == '\\') {
6167827cba2SAaron LI 			if (*(++s) == '\0')
6177827cba2SAaron LI 				return NULL;
6187827cba2SAaron LI 		}
6197827cba2SAaron LI 	}
6207827cba2SAaron LI 	return UNCONST(++s);
6217827cba2SAaron LI }
6227827cba2SAaron LI #endif
6237827cba2SAaron LI 
6247827cba2SAaron LI static int
6257827cba2SAaron LI parse_option(struct dhcpcd_ctx *ctx, const char *ifname, struct if_options *ifo,
6267827cba2SAaron LI     int opt, const char *arg, struct dhcp_opt **ldop, struct dhcp_opt **edop)
6277827cba2SAaron LI {
6287827cba2SAaron LI 	int e, i, t;
6297827cba2SAaron LI 	long l;
6307827cba2SAaron LI 	unsigned long u;
6318d36e1dfSRoy Marples 	char *p = NULL, *bp, *fp, *np;
6327827cba2SAaron LI 	ssize_t s;
6337827cba2SAaron LI 	struct in_addr addr, addr2;
6347827cba2SAaron LI 	in_addr_t *naddr;
6357827cba2SAaron LI 	struct rt *rt;
6367827cba2SAaron LI 	const struct dhcp_opt *d, *od;
6377827cba2SAaron LI 	uint8_t *request, *require, *no, *reject;
6387827cba2SAaron LI 	struct dhcp_opt **dop, *ndop;
6397827cba2SAaron LI 	size_t *dop_len, dl, odl;
6407827cba2SAaron LI 	struct vivco *vivco;
6417827cba2SAaron LI 	struct group *grp;
6427827cba2SAaron LI #ifdef AUTH
6437827cba2SAaron LI 	struct token *token;
6447827cba2SAaron LI #endif
6457827cba2SAaron LI #ifdef _REENTRANT
6467827cba2SAaron LI 	struct group grpbuf;
6477827cba2SAaron LI #endif
6487827cba2SAaron LI #ifdef DHCP6
6497827cba2SAaron LI 	size_t sl;
6507827cba2SAaron LI 	struct if_ia *ia;
6517827cba2SAaron LI 	uint8_t iaid[4];
6527827cba2SAaron LI #ifndef SMALL
6537827cba2SAaron LI 	struct if_sla *sla, *slap;
6547827cba2SAaron LI #endif
6557827cba2SAaron LI #endif
6567827cba2SAaron LI 
6577827cba2SAaron LI 	dop = NULL;
6587827cba2SAaron LI 	dop_len = NULL;
6597827cba2SAaron LI #ifdef INET6
6607827cba2SAaron LI 	i = 0;
6617827cba2SAaron LI #endif
6627827cba2SAaron LI 
6637827cba2SAaron LI /* Add a guard for static analysers.
6647827cba2SAaron LI  * This should not be needed really because of the argument_required option
6657827cba2SAaron LI  * in the options declaration above. */
6667827cba2SAaron LI #define ARG_REQUIRED if (arg == NULL) goto arg_required
6677827cba2SAaron LI 
6687827cba2SAaron LI 	switch(opt) {
6697827cba2SAaron LI 	case 'f': /* FALLTHROUGH */
6707827cba2SAaron LI 	case 'g': /* FALLTHROUGH */
6717827cba2SAaron LI 	case 'n': /* FALLTHROUGH */
6727827cba2SAaron LI 	case 'q': /* FALLTHROUGH */
6737827cba2SAaron LI 	case 'x': /* FALLTHROUGH */
6747827cba2SAaron LI 	case 'N': /* FALLTHROUGH */
6757827cba2SAaron LI 	case 'P': /* FALLTHROUGH */
6767827cba2SAaron LI 	case 'T': /* FALLTHROUGH */
6777827cba2SAaron LI 	case 'U': /* FALLTHROUGH */
6787827cba2SAaron LI 	case 'V': /* We need to handle non interface options */
6797827cba2SAaron LI 		break;
6807827cba2SAaron LI 	case 'b':
6817827cba2SAaron LI 		ifo->options |= DHCPCD_BACKGROUND;
6827827cba2SAaron LI 		break;
6837827cba2SAaron LI 	case 'c':
6847827cba2SAaron LI 		ARG_REQUIRED;
685cc34ba0cSRoy Marples 		if (IN_CONFIG_BLOCK(ifo)) {
686d4fb1e02SRoy Marples 			logerrx("%s: per interface scripts"
687d4fb1e02SRoy Marples 			    " are no longer supported",
688d4fb1e02SRoy Marples 			    ifname);
689d4fb1e02SRoy Marples 			return -1;
690d4fb1e02SRoy Marples 		}
691d4fb1e02SRoy Marples 		if (ctx->script != dhcpcd_default_script)
692d4fb1e02SRoy Marples 			free(ctx->script);
6936e63cc1fSRoy Marples 		s = parse_nstring(NULL, 0, arg);
6948d36e1dfSRoy Marples 		if (s == 0) {
695d4fb1e02SRoy Marples 			ctx->script = NULL;
6968d36e1dfSRoy Marples 			break;
6978d36e1dfSRoy Marples 		}
6988d36e1dfSRoy Marples 		dl = (size_t)s;
699d4fb1e02SRoy Marples 		if (s == -1 || (ctx->script = malloc(dl)) == NULL) {
700d4fb1e02SRoy Marples 			ctx->script = NULL;
7017827cba2SAaron LI 			logerr(__func__);
7028d36e1dfSRoy Marples 			return -1;
7038d36e1dfSRoy Marples 		}
704d4fb1e02SRoy Marples 		s = parse_nstring(ctx->script, dl, arg);
7058d36e1dfSRoy Marples 		if (s == -1 ||
706d4fb1e02SRoy Marples 		    ctx->script[0] == '\0' ||
707d4fb1e02SRoy Marples 		    strcmp(ctx->script, "/dev/null") == 0)
7088d36e1dfSRoy Marples 		{
709d4fb1e02SRoy Marples 			free(ctx->script);
710d4fb1e02SRoy Marples 			ctx->script = NULL;
7118d36e1dfSRoy Marples 		}
7127827cba2SAaron LI 		break;
7137827cba2SAaron LI 	case 'd':
7147827cba2SAaron LI 		ifo->options |= DHCPCD_DEBUG;
7157827cba2SAaron LI 		break;
7167827cba2SAaron LI 	case 'e':
7177827cba2SAaron LI 		ARG_REQUIRED;
7188d36e1dfSRoy Marples 		add_environ(&ifo->environ, arg, 1);
7197827cba2SAaron LI 		break;
7207827cba2SAaron LI 	case 'h':
7217827cba2SAaron LI 		if (!arg) {
7227827cba2SAaron LI 			ifo->options |= DHCPCD_HOSTNAME;
7237827cba2SAaron LI 			break;
7247827cba2SAaron LI 		}
7256e63cc1fSRoy Marples 		s = parse_nstring(ifo->hostname, sizeof(ifo->hostname), arg);
7267827cba2SAaron LI 		if (s == -1) {
7277827cba2SAaron LI 			logerr("%s: hostname", __func__);
7287827cba2SAaron LI 			return -1;
7297827cba2SAaron LI 		}
7307827cba2SAaron LI 		if (s != 0 && ifo->hostname[0] == '.') {
7317827cba2SAaron LI 			logerrx("hostname cannot begin with .");
7327827cba2SAaron LI 			return -1;
7337827cba2SAaron LI 		}
7347827cba2SAaron LI 		if (ifo->hostname[0] == '\0')
7357827cba2SAaron LI 			ifo->options &= ~DHCPCD_HOSTNAME;
7367827cba2SAaron LI 		else
7377827cba2SAaron LI 			ifo->options |= DHCPCD_HOSTNAME;
7387827cba2SAaron LI 		break;
7397827cba2SAaron LI 	case 'i':
7407827cba2SAaron LI 		if (arg)
7417827cba2SAaron LI 			s = parse_string((char *)ifo->vendorclassid + 1,
7427827cba2SAaron LI 			    VENDORCLASSID_MAX_LEN, arg);
7437827cba2SAaron LI 		else
7447827cba2SAaron LI 			s = 0;
7457827cba2SAaron LI 		if (s == -1) {
7467827cba2SAaron LI 			logerr("vendorclassid");
7477827cba2SAaron LI 			return -1;
7487827cba2SAaron LI 		}
7497827cba2SAaron LI 		*ifo->vendorclassid = (uint8_t)s;
7507827cba2SAaron LI 		break;
7517827cba2SAaron LI 	case 'j':
7527827cba2SAaron LI 		ARG_REQUIRED;
7537827cba2SAaron LI 		/* per interface logging is not supported
7547827cba2SAaron LI 		 * don't want to overide the commandline */
755cc34ba0cSRoy Marples 		if (!IN_CONFIG_BLOCK(ifo) && ctx->logfile == NULL) {
7567827cba2SAaron LI 			logclose();
7577827cba2SAaron LI 			ctx->logfile = strdup(arg);
7587827cba2SAaron LI 			logopen(ctx->logfile);
7597827cba2SAaron LI 		}
7607827cba2SAaron LI 		break;
7617827cba2SAaron LI 	case 'k':
7627827cba2SAaron LI 		ifo->options |= DHCPCD_RELEASE;
7637827cba2SAaron LI 		break;
7647827cba2SAaron LI 	case 'l':
7657827cba2SAaron LI 		ARG_REQUIRED;
766b8b69544SRoy Marples 		if (strcmp(arg, "-1") == 0) {
767b8b69544SRoy Marples 			ifo->leasetime = DHCP_INFINITE_LIFETIME;
768b8b69544SRoy Marples 			break;
769b8b69544SRoy Marples 		}
7707827cba2SAaron LI 		ifo->leasetime = (uint32_t)strtou(arg, NULL,
7717827cba2SAaron LI 		    0, 0, UINT32_MAX, &e);
7727827cba2SAaron LI 		if (e) {
7737827cba2SAaron LI 			logerrx("failed to convert leasetime %s", arg);
7747827cba2SAaron LI 			return -1;
7757827cba2SAaron LI 		}
7767827cba2SAaron LI 		break;
7777827cba2SAaron LI 	case 'm':
7787827cba2SAaron LI 		ARG_REQUIRED;
7797827cba2SAaron LI 		ifo->metric = (int)strtoi(arg, NULL, 0, 0, INT32_MAX, &e);
7807827cba2SAaron LI 		if (e) {
7817827cba2SAaron LI 			logerrx("failed to convert metric %s", arg);
7827827cba2SAaron LI 			return -1;
7837827cba2SAaron LI 		}
7847827cba2SAaron LI 		break;
7857827cba2SAaron LI 	case 'o':
7867827cba2SAaron LI 		ARG_REQUIRED;
787*b2927f2bSRoy Marples 		if (ctx->options & DHCPCD_PRINT_PIDFILE)
788*b2927f2bSRoy Marples 			break;
7891b3b16a2SRoy Marples 		set_option_space(ctx, arg, &d, &dl, &od, &odl, ifo,
7907827cba2SAaron LI 		    &request, &require, &no, &reject);
7917827cba2SAaron LI 		if (make_option_mask(d, dl, od, odl, request, arg, 1) != 0 ||
7927827cba2SAaron LI 		    make_option_mask(d, dl, od, odl, no, arg, -1) != 0 ||
7937827cba2SAaron LI 		    make_option_mask(d, dl, od, odl, reject, arg, -1) != 0)
7947827cba2SAaron LI 		{
795a0d9933aSRoy Marples 			logerrx("unknown option: %s", arg);
7967827cba2SAaron LI 			return -1;
7977827cba2SAaron LI 		}
7987827cba2SAaron LI 		break;
7997827cba2SAaron LI 	case O_REJECT:
8007827cba2SAaron LI 		ARG_REQUIRED;
801*b2927f2bSRoy Marples 		if (ctx->options & DHCPCD_PRINT_PIDFILE)
802*b2927f2bSRoy Marples 			break;
8031b3b16a2SRoy Marples 		set_option_space(ctx, arg, &d, &dl, &od, &odl, ifo,
8047827cba2SAaron LI 		    &request, &require, &no, &reject);
8057827cba2SAaron LI 		if (make_option_mask(d, dl, od, odl, reject, arg, 1) != 0 ||
8067827cba2SAaron LI 		    make_option_mask(d, dl, od, odl, request, arg, -1) != 0 ||
8077827cba2SAaron LI 		    make_option_mask(d, dl, od, odl, require, arg, -1) != 0)
8087827cba2SAaron LI 		{
809a0d9933aSRoy Marples 			logerrx("unknown option: %s", arg);
8107827cba2SAaron LI 			return -1;
8117827cba2SAaron LI 		}
8127827cba2SAaron LI 		break;
8137827cba2SAaron LI 	case 'p':
8147827cba2SAaron LI 		ifo->options |= DHCPCD_PERSISTENT;
8157827cba2SAaron LI 		break;
8167827cba2SAaron LI 	case 'r':
8177827cba2SAaron LI 		if (parse_addr(&ifo->req_addr, NULL, arg) != 0)
8187827cba2SAaron LI 			return -1;
8197827cba2SAaron LI 		ifo->options |= DHCPCD_REQUEST;
8207827cba2SAaron LI 		ifo->req_mask.s_addr = 0;
8217827cba2SAaron LI 		break;
8227827cba2SAaron LI 	case 's':
8237827cba2SAaron LI 		if (arg && *arg != '\0') {
8247827cba2SAaron LI 			/* Strip out a broadcast address */
8257827cba2SAaron LI 			p = strchr(arg, '/');
8267827cba2SAaron LI 			if (p != NULL) {
8277827cba2SAaron LI 				p = strchr(p + 1, '/');
8287827cba2SAaron LI 				if (p != NULL)
8297827cba2SAaron LI 					*p = '\0';
8307827cba2SAaron LI 			}
8317827cba2SAaron LI 			i = parse_addr(&ifo->req_addr, &ifo->req_mask, arg);
8327827cba2SAaron LI 			if (p != NULL) {
8337827cba2SAaron LI 				/* Ensure the original string is preserved */
8347827cba2SAaron LI 				*p++ = '/';
8357827cba2SAaron LI 				if (i == 0)
8367827cba2SAaron LI 					i = parse_addr(&ifo->req_brd, NULL, p);
8377827cba2SAaron LI 			}
8387827cba2SAaron LI 			if (i != 0)
8397827cba2SAaron LI 				return -1;
8407827cba2SAaron LI 		} else {
8417827cba2SAaron LI 			ifo->req_addr.s_addr = 0;
8427827cba2SAaron LI 			ifo->req_mask.s_addr = 0;
8437827cba2SAaron LI 		}
8447827cba2SAaron LI 		ifo->options |= DHCPCD_INFORM | DHCPCD_PERSISTENT;
8457827cba2SAaron LI 		ifo->options &= ~DHCPCD_STATIC;
8467827cba2SAaron LI 		break;
8477827cba2SAaron LI 	case O_INFORM6:
8487827cba2SAaron LI 		ifo->options |= DHCPCD_INFORM6;
8497827cba2SAaron LI 		break;
8507827cba2SAaron LI 	case 't':
8517827cba2SAaron LI 		ARG_REQUIRED;
8526e63cc1fSRoy Marples 		ifo->timeout = (uint32_t)strtou(arg, NULL, 0, 0, UINT32_MAX, &e);
8537827cba2SAaron LI 		if (e) {
8547827cba2SAaron LI 			logerrx("failed to convert timeout %s", arg);
8557827cba2SAaron LI 			return -1;
8567827cba2SAaron LI 		}
8577827cba2SAaron LI 		break;
8587827cba2SAaron LI 	case 'u':
8596e63cc1fSRoy Marples 		dl = sizeof(ifo->userclass) - ifo->userclass[0] - 1;
8607827cba2SAaron LI 		s = parse_string((char *)ifo->userclass +
8616e63cc1fSRoy Marples 		    ifo->userclass[0] + 2, dl, arg);
8627827cba2SAaron LI 		if (s == -1) {
8637827cba2SAaron LI 			logerr("userclass");
8647827cba2SAaron LI 			return -1;
8657827cba2SAaron LI 		}
8667827cba2SAaron LI 		if (s != 0) {
8677827cba2SAaron LI 			ifo->userclass[ifo->userclass[0] + 1] = (uint8_t)s;
8687827cba2SAaron LI 			ifo->userclass[0] = (uint8_t)(ifo->userclass[0] + s +1);
8697827cba2SAaron LI 		}
8707827cba2SAaron LI 		break;
8716e63cc1fSRoy Marples #ifndef SMALL
8726e63cc1fSRoy Marples 	case O_MSUSERCLASS:
8736e63cc1fSRoy Marples 		/* Some Microsoft DHCP servers expect userclass to be an
8746e63cc1fSRoy Marples 		 * opaque blob. This is not RFC 3004 compliant. */
8756e63cc1fSRoy Marples 		s = parse_string((char *)ifo->userclass + 1,
8766e63cc1fSRoy Marples 		    sizeof(ifo->userclass) - 1, arg);
8776e63cc1fSRoy Marples 		if (s == -1) {
8786e63cc1fSRoy Marples 			logerr("msuserclass");
8796e63cc1fSRoy Marples 			return -1;
8806e63cc1fSRoy Marples 		}
8816e63cc1fSRoy Marples 		ifo->userclass[0] = (uint8_t)s;
8826e63cc1fSRoy Marples 		break;
8836e63cc1fSRoy Marples #endif
8847827cba2SAaron LI 	case 'v':
8857827cba2SAaron LI 		ARG_REQUIRED;
8867827cba2SAaron LI 		p = strchr(arg, ',');
8877827cba2SAaron LI 		if (!p || !p[1]) {
8887827cba2SAaron LI 			logerrx("invalid vendor format: %s", arg);
8897827cba2SAaron LI 			return -1;
8907827cba2SAaron LI 		}
8917827cba2SAaron LI 
8927827cba2SAaron LI 		/* If vendor starts with , then it is not encapsulated */
8937827cba2SAaron LI 		if (p == arg) {
8947827cba2SAaron LI 			arg++;
8957827cba2SAaron LI 			s = parse_string((char *)ifo->vendor + 1,
8967827cba2SAaron LI 			    VENDOR_MAX_LEN, arg);
8977827cba2SAaron LI 			if (s == -1) {
8987827cba2SAaron LI 				logerr("vendor");
8997827cba2SAaron LI 				return -1;
9007827cba2SAaron LI 			}
9017827cba2SAaron LI 			ifo->vendor[0] = (uint8_t)s;
9027827cba2SAaron LI 			ifo->options |= DHCPCD_VENDORRAW;
9037827cba2SAaron LI 			break;
9047827cba2SAaron LI 		}
9057827cba2SAaron LI 
9067827cba2SAaron LI 		/* Encapsulated vendor options */
9077827cba2SAaron LI 		if (ifo->options & DHCPCD_VENDORRAW) {
9087827cba2SAaron LI 			ifo->options &= ~DHCPCD_VENDORRAW;
9097827cba2SAaron LI 			ifo->vendor[0] = 0;
9107827cba2SAaron LI 		}
9117827cba2SAaron LI 
9127827cba2SAaron LI 		/* Strip and preserve the comma */
9137827cba2SAaron LI 		*p = '\0';
9147827cba2SAaron LI 		i = (int)strtoi(arg, NULL, 0, 1, 254, &e);
9157827cba2SAaron LI 		*p = ',';
9167827cba2SAaron LI 		if (e) {
9177827cba2SAaron LI 			logerrx("vendor option should be between"
9187827cba2SAaron LI 			    " 1 and 254 inclusive");
9197827cba2SAaron LI 			return -1;
9207827cba2SAaron LI 		}
9217827cba2SAaron LI 
9227827cba2SAaron LI 		arg = p + 1;
9237827cba2SAaron LI 		s = VENDOR_MAX_LEN - ifo->vendor[0] - 2;
9247827cba2SAaron LI 		if (inet_aton(arg, &addr) == 1) {
9257827cba2SAaron LI 			if (s < 6) {
9267827cba2SAaron LI 				s = -1;
9277827cba2SAaron LI 				errno = ENOBUFS;
9287827cba2SAaron LI 			} else {
9297827cba2SAaron LI 				memcpy(ifo->vendor + ifo->vendor[0] + 3,
9307827cba2SAaron LI 				    &addr.s_addr, sizeof(addr.s_addr));
9317827cba2SAaron LI 				s = sizeof(addr.s_addr);
9327827cba2SAaron LI 			}
9337827cba2SAaron LI 		} else {
9347827cba2SAaron LI 			s = parse_string((char *)ifo->vendor +
9357827cba2SAaron LI 			    ifo->vendor[0] + 3, (size_t)s, arg);
9367827cba2SAaron LI 		}
9377827cba2SAaron LI 		if (s == -1) {
9387827cba2SAaron LI 			logerr("vendor");
9397827cba2SAaron LI 			return -1;
9407827cba2SAaron LI 		}
9417827cba2SAaron LI 		if (s != 0) {
9427827cba2SAaron LI 			ifo->vendor[ifo->vendor[0] + 1] = (uint8_t)i;
9437827cba2SAaron LI 			ifo->vendor[ifo->vendor[0] + 2] = (uint8_t)s;
9447827cba2SAaron LI 			ifo->vendor[0] = (uint8_t)(ifo->vendor[0] + s + 2);
9457827cba2SAaron LI 		}
9467827cba2SAaron LI 		break;
9477827cba2SAaron LI 	case 'w':
9487827cba2SAaron LI 		ifo->options |= DHCPCD_WAITIP;
9497827cba2SAaron LI 		if (arg != NULL && arg[0] != '\0') {
9507827cba2SAaron LI 			if (arg[0] == '4' || arg[1] == '4')
9517827cba2SAaron LI 				ifo->options |= DHCPCD_WAITIP4;
9527827cba2SAaron LI 			if (arg[0] == '6' || arg[1] == '6')
9537827cba2SAaron LI 				ifo->options |= DHCPCD_WAITIP6;
9547827cba2SAaron LI 		}
9557827cba2SAaron LI 		break;
9567827cba2SAaron LI 	case 'y':
9577827cba2SAaron LI 		ARG_REQUIRED;
9586e63cc1fSRoy Marples 		ifo->reboot = (uint32_t)strtou(arg, NULL, 0, 0, UINT32_MAX, &e);
9597827cba2SAaron LI 		if (e) {
9607827cba2SAaron LI 			logerr("failed to convert reboot %s", arg);
9617827cba2SAaron LI 			return -1;
9627827cba2SAaron LI 		}
9637827cba2SAaron LI 		break;
9647827cba2SAaron LI 	case 'z':
9657827cba2SAaron LI 		ARG_REQUIRED;
966cc34ba0cSRoy Marples 		if (!IN_CONFIG_BLOCK(ifo))
9677827cba2SAaron LI 			ctx->ifav = splitv(&ctx->ifac, ctx->ifav, arg);
9687827cba2SAaron LI 		break;
9697827cba2SAaron LI 	case 'A':
9707827cba2SAaron LI 		ifo->options &= ~DHCPCD_ARP;
9717827cba2SAaron LI 		/* IPv4LL requires ARP */
9727827cba2SAaron LI 		ifo->options &= ~DHCPCD_IPV4LL;
9737827cba2SAaron LI 		break;
9747827cba2SAaron LI 	case 'B':
9757827cba2SAaron LI 		ifo->options &= ~DHCPCD_DAEMONISE;
9767827cba2SAaron LI 		break;
9777827cba2SAaron LI 	case 'C':
9787827cba2SAaron LI 		ARG_REQUIRED;
9797827cba2SAaron LI 		/* Commas to spaces for shell */
9807827cba2SAaron LI 		while ((p = strchr(arg, ',')))
9817827cba2SAaron LI 			*p = ' ';
9827827cba2SAaron LI 		dl = strlen("skip_hooks=") + strlen(arg) + 1;
9837827cba2SAaron LI 		p = malloc(sizeof(char) * dl);
9847827cba2SAaron LI 		if (p == NULL) {
9857827cba2SAaron LI 			logerr(__func__);
9867827cba2SAaron LI 			return -1;
9877827cba2SAaron LI 		}
9887827cba2SAaron LI 		snprintf(p, dl, "skip_hooks=%s", arg);
9898d36e1dfSRoy Marples 		add_environ(&ifo->environ, p, 0);
9907827cba2SAaron LI 		free(p);
9917827cba2SAaron LI 		break;
9927827cba2SAaron LI 	case 'D':
9937827cba2SAaron LI 		ifo->options |= DHCPCD_CLIENTID | DHCPCD_DUID;
994a0d9933aSRoy Marples 		if (ifname != NULL) /* duid type only a global option */
995a0d9933aSRoy Marples 			break;
996a0d9933aSRoy Marples 		if (arg == NULL)
997a0d9933aSRoy Marples 			ctx->duid_type = DUID_DEFAULT;
998a0d9933aSRoy Marples 		else if (strcmp(arg, "ll") == 0)
999a0d9933aSRoy Marples 			ctx->duid_type = DUID_LL;
1000a0d9933aSRoy Marples 		else if (strcmp(arg, "llt") == 0)
1001a0d9933aSRoy Marples 			ctx->duid_type = DUID_LLT;
1002a0d9933aSRoy Marples 		else if (strcmp(arg, "uuid") == 0)
1003a0d9933aSRoy Marples 			ctx->duid_type = DUID_UUID;
1004a0d9933aSRoy Marples 		else {
1005a0d9933aSRoy Marples 			logwarnx("%s: invalid duid type", arg);
1006a0d9933aSRoy Marples 			ctx->duid_type = DUID_DEFAULT;
1007a0d9933aSRoy Marples 		}
10087827cba2SAaron LI 		break;
10097827cba2SAaron LI 	case 'E':
10107827cba2SAaron LI 		ifo->options |= DHCPCD_LASTLEASE;
10117827cba2SAaron LI 		break;
10127827cba2SAaron LI 	case 'F':
10137827cba2SAaron LI 		if (!arg) {
10147827cba2SAaron LI 			ifo->fqdn = FQDN_BOTH;
10157827cba2SAaron LI 			break;
10167827cba2SAaron LI 		}
10177827cba2SAaron LI 		if (strcmp(arg, "none") == 0)
10187827cba2SAaron LI 			ifo->fqdn = FQDN_NONE;
10197827cba2SAaron LI 		else if (strcmp(arg, "ptr") == 0)
10207827cba2SAaron LI 			ifo->fqdn = FQDN_PTR;
10217827cba2SAaron LI 		else if (strcmp(arg, "both") == 0)
10227827cba2SAaron LI 			ifo->fqdn = FQDN_BOTH;
10237827cba2SAaron LI 		else if (strcmp(arg, "disable") == 0)
10247827cba2SAaron LI 			ifo->fqdn = FQDN_DISABLE;
10257827cba2SAaron LI 		else {
1026a0d9933aSRoy Marples 			logerrx("invalid FQDN value: %s", arg);
10277827cba2SAaron LI 			return -1;
10287827cba2SAaron LI 		}
10297827cba2SAaron LI 		break;
10307827cba2SAaron LI 	case 'G':
10317827cba2SAaron LI 		ifo->options &= ~DHCPCD_GATEWAY;
10327827cba2SAaron LI 		break;
10337827cba2SAaron LI 	case 'H':
10347827cba2SAaron LI 		ifo->options |= DHCPCD_XID_HWADDR;
10357827cba2SAaron LI 		break;
10367827cba2SAaron LI 	case 'I':
10377827cba2SAaron LI 		/* Strings have a type of 0 */;
10387827cba2SAaron LI 		ifo->clientid[1] = 0;
10397827cba2SAaron LI 		if (arg)
10408d36e1dfSRoy Marples 			s = parse_hwaddr((char *)ifo->clientid + 1,
10418d36e1dfSRoy Marples 			    CLIENTID_MAX_LEN, arg);
10427827cba2SAaron LI 		else
10437827cba2SAaron LI 			s = 0;
10447827cba2SAaron LI 		if (s == -1) {
10457827cba2SAaron LI 			logerr("clientid");
10467827cba2SAaron LI 			return -1;
10477827cba2SAaron LI 		}
10487827cba2SAaron LI 		ifo->options |= DHCPCD_CLIENTID;
10497827cba2SAaron LI 		ifo->clientid[0] = (uint8_t)s;
1050b8b69544SRoy Marples 		ifo->options &= ~DHCPCD_DUID;
10517827cba2SAaron LI 		break;
10527827cba2SAaron LI 	case 'J':
10537827cba2SAaron LI 		ifo->options |= DHCPCD_BROADCAST;
10547827cba2SAaron LI 		break;
10557827cba2SAaron LI 	case 'K':
10567827cba2SAaron LI 		ifo->options &= ~DHCPCD_LINK;
10577827cba2SAaron LI 		break;
10587827cba2SAaron LI 	case 'L':
10597827cba2SAaron LI 		ifo->options &= ~DHCPCD_IPV4LL;
10607827cba2SAaron LI 		break;
10617827cba2SAaron LI 	case 'M':
10627827cba2SAaron LI 		ifo->options |= DHCPCD_MASTER;
10637827cba2SAaron LI 		break;
10647827cba2SAaron LI 	case 'O':
10657827cba2SAaron LI 		ARG_REQUIRED;
1066*b2927f2bSRoy Marples 		if (ctx->options & DHCPCD_PRINT_PIDFILE)
1067*b2927f2bSRoy Marples 			break;
10681b3b16a2SRoy Marples 		set_option_space(ctx, arg, &d, &dl, &od, &odl, ifo,
10697827cba2SAaron LI 		    &request, &require, &no, &reject);
10707827cba2SAaron LI 		if (make_option_mask(d, dl, od, odl, request, arg, -1) != 0 ||
10717827cba2SAaron LI 		    make_option_mask(d, dl, od, odl, require, arg, -1) != 0 ||
10727827cba2SAaron LI 		    make_option_mask(d, dl, od, odl, no, arg, 1) != 0)
10737827cba2SAaron LI 		{
1074a0d9933aSRoy Marples 			logerrx("unknown option: %s", arg);
10757827cba2SAaron LI 			return -1;
10767827cba2SAaron LI 		}
10777827cba2SAaron LI 		break;
10787827cba2SAaron LI 	case 'Q':
10797827cba2SAaron LI 		ARG_REQUIRED;
1080*b2927f2bSRoy Marples 		if (ctx->options & DHCPCD_PRINT_PIDFILE)
1081*b2927f2bSRoy Marples 			break;
10821b3b16a2SRoy Marples 		set_option_space(ctx, arg, &d, &dl, &od, &odl, ifo,
10837827cba2SAaron LI 		    &request, &require, &no, &reject);
10847827cba2SAaron LI 		if (make_option_mask(d, dl, od, odl, require, arg, 1) != 0 ||
10857827cba2SAaron LI 		    make_option_mask(d, dl, od, odl, request, arg, 1) != 0 ||
10867827cba2SAaron LI 		    make_option_mask(d, dl, od, odl, no, arg, -1) != 0 ||
10877827cba2SAaron LI 		    make_option_mask(d, dl, od, odl, reject, arg, -1) != 0)
10887827cba2SAaron LI 		{
1089a0d9933aSRoy Marples 			logerrx("unknown option: %s", arg);
10907827cba2SAaron LI 			return -1;
10917827cba2SAaron LI 		}
10927827cba2SAaron LI 		break;
10937827cba2SAaron LI 	case 'S':
10947827cba2SAaron LI 		ARG_REQUIRED;
10957827cba2SAaron LI 		p = strchr(arg, '=');
10967827cba2SAaron LI 		if (p == NULL) {
10977827cba2SAaron LI 			logerrx("static assignment required");
10987827cba2SAaron LI 			return -1;
10997827cba2SAaron LI 		}
11007827cba2SAaron LI 		p++;
11017827cba2SAaron LI 		if (strncmp(arg, "ip_address=", strlen("ip_address=")) == 0) {
11027827cba2SAaron LI 			if (parse_addr(&ifo->req_addr,
11037827cba2SAaron LI 			    ifo->req_mask.s_addr == 0 ? &ifo->req_mask : NULL,
11047827cba2SAaron LI 			    p) != 0)
11057827cba2SAaron LI 				return -1;
11067827cba2SAaron LI 
11077827cba2SAaron LI 			ifo->options |= DHCPCD_STATIC;
11087827cba2SAaron LI 			ifo->options &= ~DHCPCD_INFORM;
11097827cba2SAaron LI 		} else if (strncmp(arg, "subnet_mask=",
11107827cba2SAaron LI 		    strlen("subnet_mask=")) == 0)
11117827cba2SAaron LI 		{
11127827cba2SAaron LI 			if (parse_addr(&ifo->req_mask, NULL, p) != 0)
11137827cba2SAaron LI 				return -1;
11147827cba2SAaron LI 		} else if (strncmp(arg, "broadcast_address=",
11157827cba2SAaron LI 		    strlen("broadcast_address=")) == 0)
11167827cba2SAaron LI 		{
11177827cba2SAaron LI 			if (parse_addr(&ifo->req_brd, NULL, p) != 0)
11187827cba2SAaron LI 				return -1;
11197827cba2SAaron LI 		} else if (strncmp(arg, "routes=", strlen("routes=")) == 0 ||
11207827cba2SAaron LI 		    strncmp(arg, "static_routes=",
11217827cba2SAaron LI 		        strlen("static_routes=")) == 0 ||
11227827cba2SAaron LI 		    strncmp(arg, "classless_static_routes=",
11237827cba2SAaron LI 		        strlen("classless_static_routes=")) == 0 ||
11247827cba2SAaron LI 		    strncmp(arg, "ms_classless_static_routes=",
11257827cba2SAaron LI 		        strlen("ms_classless_static_routes=")) == 0)
11267827cba2SAaron LI 		{
11277827cba2SAaron LI 			struct in_addr addr3;
11287827cba2SAaron LI 
11297827cba2SAaron LI 			fp = np = strwhite(p);
11307827cba2SAaron LI 			if (np == NULL) {
11317827cba2SAaron LI 				logerrx("all routes need a gateway");
11327827cba2SAaron LI 				return -1;
11337827cba2SAaron LI 			}
11347827cba2SAaron LI 			*np++ = '\0';
11357827cba2SAaron LI 			np = strskipwhite(np);
11367827cba2SAaron LI 			if (parse_addr(&addr, &addr2, p) == -1 ||
11377827cba2SAaron LI 			    parse_addr(&addr3, NULL, np) == -1)
11387827cba2SAaron LI 			{
11397827cba2SAaron LI 				*fp = ' ';
11407827cba2SAaron LI 				return -1;
11417827cba2SAaron LI 			}
11427827cba2SAaron LI 			*fp = ' ';
11438d36e1dfSRoy Marples 			if ((rt = rt_new0(ctx)) == NULL)
11447827cba2SAaron LI 				return -1;
11457827cba2SAaron LI 			sa_in_init(&rt->rt_dest, &addr);
11467827cba2SAaron LI 			sa_in_init(&rt->rt_netmask, &addr2);
11477827cba2SAaron LI 			sa_in_init(&rt->rt_gateway, &addr3);
11488d36e1dfSRoy Marples 			if (rt_proto_add_ctx(&ifo->routes, rt, ctx))
11498d36e1dfSRoy Marples 				add_environ(&ifo->config, arg, 0);
11507827cba2SAaron LI 		} else if (strncmp(arg, "routers=", strlen("routers=")) == 0) {
11517827cba2SAaron LI 			if (parse_addr(&addr, NULL, p) == -1)
11527827cba2SAaron LI 				return -1;
11537827cba2SAaron LI 			if ((rt = rt_new0(ctx)) == NULL)
11547827cba2SAaron LI 				return -1;
11557827cba2SAaron LI 			addr2.s_addr = INADDR_ANY;
11567827cba2SAaron LI 			sa_in_init(&rt->rt_dest, &addr2);
11577827cba2SAaron LI 			sa_in_init(&rt->rt_netmask, &addr2);
11587827cba2SAaron LI 			sa_in_init(&rt->rt_gateway, &addr);
11598d36e1dfSRoy Marples 			if (rt_proto_add_ctx(&ifo->routes, rt, ctx))
11608d36e1dfSRoy Marples 				add_environ(&ifo->config, arg, 0);
11617827cba2SAaron LI 		} else if (strncmp(arg, "interface_mtu=",
11627827cba2SAaron LI 		    strlen("interface_mtu=")) == 0 ||
11637827cba2SAaron LI 		    strncmp(arg, "mtu=", strlen("mtu=")) == 0)
11647827cba2SAaron LI 		{
11657827cba2SAaron LI 			ifo->mtu = (unsigned int)strtou(p, NULL, 0,
11667827cba2SAaron LI 			    MTU_MIN, MTU_MAX, &e);
11677827cba2SAaron LI 			if (e) {
11687827cba2SAaron LI 				logerrx("invalid MTU %s", p);
11697827cba2SAaron LI 				return -1;
11707827cba2SAaron LI 			}
11717827cba2SAaron LI 		} else if (strncmp(arg, "ip6_address=", strlen("ip6_address=")) == 0) {
11727827cba2SAaron LI 			np = strchr(p, '/');
11737827cba2SAaron LI 			if (np)
11747827cba2SAaron LI 				*np++ = '\0';
11756e63cc1fSRoy Marples 			if ((i = inet_pton(AF_INET6, p, &ifo->req_addr6)) == 1) {
11767827cba2SAaron LI 				if (np) {
11777827cba2SAaron LI 					ifo->req_prefix_len = (uint8_t)strtou(np,
11787827cba2SAaron LI 					    NULL, 0, 0, 128, &e);
11797827cba2SAaron LI 					if (e) {
11807827cba2SAaron LI 						logerrx("%s: failed to "
11817827cba2SAaron LI 						    "convert prefix len",
11827827cba2SAaron LI 						    ifname);
11837827cba2SAaron LI 						return -1;
11847827cba2SAaron LI 					}
11857827cba2SAaron LI 				} else
11867827cba2SAaron LI 					ifo->req_prefix_len = 128;
11877827cba2SAaron LI 			}
11886e63cc1fSRoy Marples 			if (np)
11896e63cc1fSRoy Marples 				*(--np) = '\0';
11906e63cc1fSRoy Marples 			if (i != 1) {
11916e63cc1fSRoy Marples 				logerrx("invalid AF_INET6: %s", p);
11926e63cc1fSRoy Marples 				memset(&ifo->req_addr6, 0,
11936e63cc1fSRoy Marples 				    sizeof(ifo->req_addr6));
11946e63cc1fSRoy Marples 				return -1;
11956e63cc1fSRoy Marples 			}
11968d36e1dfSRoy Marples 		} else
11978d36e1dfSRoy Marples 			add_environ(&ifo->config, arg, 1);
11987827cba2SAaron LI 		break;
11997827cba2SAaron LI 	case 'W':
12007827cba2SAaron LI 		if (parse_addr(&addr, &addr2, arg) != 0)
12017827cba2SAaron LI 			return -1;
12027827cba2SAaron LI 		if (strchr(arg, '/') == NULL)
12037827cba2SAaron LI 			addr2.s_addr = INADDR_BROADCAST;
12047827cba2SAaron LI 		naddr = reallocarray(ifo->whitelist,
12057827cba2SAaron LI 		    ifo->whitelist_len + 2, sizeof(in_addr_t));
12067827cba2SAaron LI 		if (naddr == NULL) {
12077827cba2SAaron LI 			logerr(__func__);
12087827cba2SAaron LI 			return -1;
12097827cba2SAaron LI 		}
12107827cba2SAaron LI 		ifo->whitelist = naddr;
12117827cba2SAaron LI 		ifo->whitelist[ifo->whitelist_len++] = addr.s_addr;
12127827cba2SAaron LI 		ifo->whitelist[ifo->whitelist_len++] = addr2.s_addr;
12137827cba2SAaron LI 		break;
12147827cba2SAaron LI 	case 'X':
12157827cba2SAaron LI 		if (parse_addr(&addr, &addr2, arg) != 0)
12167827cba2SAaron LI 			return -1;
12177827cba2SAaron LI 		if (strchr(arg, '/') == NULL)
12187827cba2SAaron LI 			addr2.s_addr = INADDR_BROADCAST;
12197827cba2SAaron LI 		naddr = reallocarray(ifo->blacklist,
12207827cba2SAaron LI 		    ifo->blacklist_len + 2, sizeof(in_addr_t));
12217827cba2SAaron LI 		if (naddr == NULL) {
12227827cba2SAaron LI 			logerr(__func__);
12237827cba2SAaron LI 			return -1;
12247827cba2SAaron LI 		}
12257827cba2SAaron LI 		ifo->blacklist = naddr;
12267827cba2SAaron LI 		ifo->blacklist[ifo->blacklist_len++] = addr.s_addr;
12277827cba2SAaron LI 		ifo->blacklist[ifo->blacklist_len++] = addr2.s_addr;
12287827cba2SAaron LI 		break;
12297827cba2SAaron LI 	case 'Z':
12307827cba2SAaron LI 		ARG_REQUIRED;
1231cc34ba0cSRoy Marples 		if (!IN_CONFIG_BLOCK(ifo))
12327827cba2SAaron LI 			ctx->ifdv = splitv(&ctx->ifdc, ctx->ifdv, arg);
12337827cba2SAaron LI 		break;
12347827cba2SAaron LI 	case '1':
12357827cba2SAaron LI 		ifo->options |= DHCPCD_ONESHOT;
12367827cba2SAaron LI 		break;
12377827cba2SAaron LI 	case '4':
1238b8b69544SRoy Marples #ifdef INET
12397827cba2SAaron LI 		ifo->options &= ~DHCPCD_IPV6;
12407827cba2SAaron LI 		ifo->options |= DHCPCD_IPV4;
12417827cba2SAaron LI 		break;
1242b8b69544SRoy Marples #else
1243b8b69544SRoy Marples 		logerrx("INET has been compiled out");
1244b8b69544SRoy Marples 		return -1;
1245b8b69544SRoy Marples #endif
12467827cba2SAaron LI 	case '6':
1247b8b69544SRoy Marples #ifdef INET6
12487827cba2SAaron LI 		ifo->options &= ~DHCPCD_IPV4;
12497827cba2SAaron LI 		ifo->options |= DHCPCD_IPV6;
12507827cba2SAaron LI 		break;
1251b8b69544SRoy Marples #else
1252b8b69544SRoy Marples 		logerrx("INET6 has been compiled out");
1253b8b69544SRoy Marples 		return -1;
1254b8b69544SRoy Marples #endif
12557827cba2SAaron LI 	case O_IPV4:
12567827cba2SAaron LI 		ifo->options |= DHCPCD_IPV4;
12577827cba2SAaron LI 		break;
12587827cba2SAaron LI 	case O_NOIPV4:
12597827cba2SAaron LI 		ifo->options &= ~DHCPCD_IPV4;
12607827cba2SAaron LI 		break;
12617827cba2SAaron LI 	case O_IPV6:
12627827cba2SAaron LI 		ifo->options |= DHCPCD_IPV6;
12637827cba2SAaron LI 		break;
12647827cba2SAaron LI 	case O_NOIPV6:
12657827cba2SAaron LI 		ifo->options &= ~DHCPCD_IPV6;
12667827cba2SAaron LI 		break;
12676e63cc1fSRoy Marples 	case O_ANONYMOUS:
12686e63cc1fSRoy Marples 		ifo->options |= DHCPCD_ANONYMOUS;
12696e63cc1fSRoy Marples 		ifo->options &= ~DHCPCD_HOSTNAME;
12706e63cc1fSRoy Marples 		ifo->fqdn = FQDN_DISABLE;
12716e63cc1fSRoy Marples 
12726e63cc1fSRoy Marples 		/* Block everything */
12736e63cc1fSRoy Marples 		memset(ifo->nomask, 0xff, sizeof(ifo->nomask));
12746e63cc1fSRoy Marples 		memset(ifo->nomask6, 0xff, sizeof(ifo->nomask6));
12756e63cc1fSRoy Marples 
12766e63cc1fSRoy Marples 		/* Allow the bare minimum through */
1277280986e4SRoy Marples #ifdef INET
12786e63cc1fSRoy Marples 		del_option_mask(ifo->nomask, DHO_SUBNETMASK);
12796e63cc1fSRoy Marples 		del_option_mask(ifo->nomask, DHO_CSR);
12806e63cc1fSRoy Marples 		del_option_mask(ifo->nomask, DHO_ROUTER);
12816e63cc1fSRoy Marples 		del_option_mask(ifo->nomask, DHO_DNSSERVER);
12826e63cc1fSRoy Marples 		del_option_mask(ifo->nomask, DHO_DNSDOMAIN);
12836e63cc1fSRoy Marples 		del_option_mask(ifo->nomask, DHO_BROADCAST);
12846e63cc1fSRoy Marples 		del_option_mask(ifo->nomask, DHO_STATICROUTE);
12856e63cc1fSRoy Marples 		del_option_mask(ifo->nomask, DHO_SERVERID);
12866e63cc1fSRoy Marples 		del_option_mask(ifo->nomask, DHO_RENEWALTIME);
12876e63cc1fSRoy Marples 		del_option_mask(ifo->nomask, DHO_REBINDTIME);
12886e63cc1fSRoy Marples 		del_option_mask(ifo->nomask, DHO_DNSSEARCH);
1289280986e4SRoy Marples #endif
12906e63cc1fSRoy Marples 
1291d4fb1e02SRoy Marples #ifdef DHCP6
12926e63cc1fSRoy Marples 		del_option_mask(ifo->nomask6, D6_OPTION_DNS_SERVERS);
12936e63cc1fSRoy Marples 		del_option_mask(ifo->nomask6, D6_OPTION_DOMAIN_LIST);
12946e63cc1fSRoy Marples 		del_option_mask(ifo->nomask6, D6_OPTION_SOL_MAX_RT);
12956e63cc1fSRoy Marples 		del_option_mask(ifo->nomask6, D6_OPTION_INF_MAX_RT);
1296280986e4SRoy Marples #endif
12976e63cc1fSRoy Marples 
12986e63cc1fSRoy Marples 		break;
12997827cba2SAaron LI #ifdef INET
13007827cba2SAaron LI 	case O_ARPING:
13017827cba2SAaron LI 		while (arg != NULL) {
13027827cba2SAaron LI 			fp = strwhite(arg);
13037827cba2SAaron LI 			if (fp)
13047827cba2SAaron LI 				*fp++ = '\0';
13057827cba2SAaron LI 			if (parse_addr(&addr, NULL, arg) != 0)
13067827cba2SAaron LI 				return -1;
13077827cba2SAaron LI 			naddr = reallocarray(ifo->arping,
13087827cba2SAaron LI 			    (size_t)ifo->arping_len + 1, sizeof(in_addr_t));
13097827cba2SAaron LI 			if (naddr == NULL) {
13107827cba2SAaron LI 				logerr(__func__);
13117827cba2SAaron LI 				return -1;
13127827cba2SAaron LI 			}
13137827cba2SAaron LI 			ifo->arping = naddr;
13147827cba2SAaron LI 			ifo->arping[ifo->arping_len++] = addr.s_addr;
13157827cba2SAaron LI 			arg = strskipwhite(fp);
13167827cba2SAaron LI 		}
13177827cba2SAaron LI 		break;
13187827cba2SAaron LI 	case O_DESTINATION:
13197827cba2SAaron LI 		ARG_REQUIRED;
1320*b2927f2bSRoy Marples 		if (ctx->options & DHCPCD_PRINT_PIDFILE)
1321*b2927f2bSRoy Marples 			break;
13221b3b16a2SRoy Marples 		set_option_space(ctx, arg, &d, &dl, &od, &odl, ifo,
13237827cba2SAaron LI 		    &request, &require, &no, &reject);
13247827cba2SAaron LI 		if (make_option_mask(d, dl, od, odl,
13257827cba2SAaron LI 		    ifo->dstmask, arg, 2) != 0)
13267827cba2SAaron LI 		{
13277827cba2SAaron LI 			if (errno == EINVAL)
1328a0d9933aSRoy Marples 				logerrx("option does not take"
1329a0d9933aSRoy Marples 				    " an IPv4 address: %s", arg);
13307827cba2SAaron LI 			else
1331a0d9933aSRoy Marples 				logerrx("unknown option: %s", arg);
13327827cba2SAaron LI 			return -1;
13337827cba2SAaron LI 		}
13347827cba2SAaron LI 		break;
13357827cba2SAaron LI 	case O_FALLBACK:
13367827cba2SAaron LI 		ARG_REQUIRED;
13377827cba2SAaron LI 		free(ifo->fallback);
13387827cba2SAaron LI 		ifo->fallback = strdup(arg);
13397827cba2SAaron LI 		if (ifo->fallback == NULL) {
13407827cba2SAaron LI 			logerrx(__func__);
13417827cba2SAaron LI 			return -1;
13427827cba2SAaron LI 		}
13437827cba2SAaron LI 		break;
13447827cba2SAaron LI #endif
13457827cba2SAaron LI 	case O_IAID:
13467827cba2SAaron LI 		ARG_REQUIRED;
1347cc34ba0cSRoy Marples 		if (!IN_CONFIG_BLOCK(ifo)) {
13487827cba2SAaron LI 			logerrx("IAID must belong in an interface block");
13497827cba2SAaron LI 			return -1;
13507827cba2SAaron LI 		}
13517827cba2SAaron LI 		if (parse_iaid(ifo->iaid, arg, sizeof(ifo->iaid)) == -1) {
13527827cba2SAaron LI 			logerrx("invalid IAID %s", arg);
13537827cba2SAaron LI 			return -1;
13547827cba2SAaron LI 		}
13557827cba2SAaron LI 		ifo->options |= DHCPCD_IAID;
13567827cba2SAaron LI 		break;
13577827cba2SAaron LI 	case O_IPV6RS:
13587827cba2SAaron LI 		ifo->options |= DHCPCD_IPV6RS;
13597827cba2SAaron LI 		break;
13607827cba2SAaron LI 	case O_NOIPV6RS:
13617827cba2SAaron LI 		ifo->options &= ~DHCPCD_IPV6RS;
13627827cba2SAaron LI 		break;
13637827cba2SAaron LI 	case O_IPV6RA_FORK:
13647827cba2SAaron LI 		ifo->options &= ~DHCPCD_IPV6RA_REQRDNSS;
13657827cba2SAaron LI 		break;
13667827cba2SAaron LI 	case O_IPV6RA_AUTOCONF:
13677827cba2SAaron LI 		ifo->options |= DHCPCD_IPV6RA_AUTOCONF;
13687827cba2SAaron LI 		break;
13697827cba2SAaron LI 	case O_IPV6RA_NOAUTOCONF:
13707827cba2SAaron LI 		ifo->options &= ~DHCPCD_IPV6RA_AUTOCONF;
13717827cba2SAaron LI 		break;
13727827cba2SAaron LI 	case O_NOALIAS:
13737827cba2SAaron LI 		ifo->options |= DHCPCD_NOALIAS;
13747827cba2SAaron LI 		break;
13757827cba2SAaron LI #ifdef DHCP6
13767827cba2SAaron LI 	case O_IA_NA:
13777827cba2SAaron LI 		i = D6_OPTION_IA_NA;
13787827cba2SAaron LI 		/* FALLTHROUGH */
13797827cba2SAaron LI 	case O_IA_TA:
13807827cba2SAaron LI 		if (i == 0)
13817827cba2SAaron LI 			i = D6_OPTION_IA_TA;
13827827cba2SAaron LI 		/* FALLTHROUGH */
13837827cba2SAaron LI 	case O_IA_PD:
13847827cba2SAaron LI 		if (i == 0) {
13857827cba2SAaron LI #ifdef SMALL
13867827cba2SAaron LI 			logwarnx("%s: IA_PD not compiled in", ifname);
13877827cba2SAaron LI 			return -1;
13887827cba2SAaron LI #else
1389cc34ba0cSRoy Marples 			if (!IN_CONFIG_BLOCK(ifo)) {
13908d36e1dfSRoy Marples 				logerrx("IA PD must belong in an "
13917827cba2SAaron LI 				    "interface block");
13927827cba2SAaron LI 				return -1;
13937827cba2SAaron LI 			}
13947827cba2SAaron LI 			i = D6_OPTION_IA_PD;
13957827cba2SAaron LI #endif
13967827cba2SAaron LI 		}
1397cc34ba0cSRoy Marples 		if (!IN_CONFIG_BLOCK(ifo) && arg) {
13987827cba2SAaron LI 			logerrx("IA with IAID must belong in an "
13997827cba2SAaron LI 			    "interface block");
14007827cba2SAaron LI 			return -1;
14017827cba2SAaron LI 		}
14027827cba2SAaron LI 		ifo->options |= DHCPCD_IA_FORCED;
14037827cba2SAaron LI 		fp = strwhite(arg);
14047827cba2SAaron LI 		if (fp) {
14057827cba2SAaron LI 			*fp++ = '\0';
14067827cba2SAaron LI 			fp = strskipwhite(fp);
14077827cba2SAaron LI 		}
14087827cba2SAaron LI 		if (arg) {
14097827cba2SAaron LI 			p = strchr(arg, '/');
14107827cba2SAaron LI 			if (p)
14117827cba2SAaron LI 				*p++ = '\0';
14127827cba2SAaron LI 			if (parse_iaid(iaid, arg, sizeof(iaid)) == -1) {
14137827cba2SAaron LI 				logerr("invalid IAID: %s", arg);
14147827cba2SAaron LI 				return -1;
14157827cba2SAaron LI 			}
14167827cba2SAaron LI 		}
14177827cba2SAaron LI 		ia = NULL;
14187827cba2SAaron LI 		for (sl = 0; sl < ifo->ia_len; sl++) {
14197827cba2SAaron LI 			if ((arg == NULL && !ifo->ia[sl].iaid_set) ||
14207827cba2SAaron LI 			    (arg != NULL && ifo->ia[sl].iaid_set &&
14217827cba2SAaron LI 			    ifo->ia[sl].ia_type == (uint16_t)i &&
14227827cba2SAaron LI 			    ifo->ia[sl].iaid[0] == iaid[0] &&
14237827cba2SAaron LI 			    ifo->ia[sl].iaid[1] == iaid[1] &&
14247827cba2SAaron LI 			    ifo->ia[sl].iaid[2] == iaid[2] &&
14257827cba2SAaron LI 			    ifo->ia[sl].iaid[3] == iaid[3]))
14267827cba2SAaron LI 			{
14277827cba2SAaron LI 			        ia = &ifo->ia[sl];
14287827cba2SAaron LI 				break;
14297827cba2SAaron LI 			}
14307827cba2SAaron LI 		}
14317827cba2SAaron LI 		if (ia == NULL) {
14327827cba2SAaron LI 			ia = reallocarray(ifo->ia,
14337827cba2SAaron LI 			    ifo->ia_len + 1, sizeof(*ifo->ia));
14347827cba2SAaron LI 			if (ia == NULL) {
14357827cba2SAaron LI 				logerr(__func__);
14367827cba2SAaron LI 				return -1;
14377827cba2SAaron LI 			}
14387827cba2SAaron LI 			ifo->ia = ia;
14397827cba2SAaron LI 			ia = &ifo->ia[ifo->ia_len++];
14407827cba2SAaron LI 			ia->ia_type = (uint16_t)i;
14417827cba2SAaron LI 			if (arg) {
14427827cba2SAaron LI 				ia->iaid[0] = iaid[0];
14437827cba2SAaron LI 				ia->iaid[1] = iaid[1];
14447827cba2SAaron LI 				ia->iaid[2] = iaid[2];
14457827cba2SAaron LI 				ia->iaid[3] = iaid[3];
14467827cba2SAaron LI 				ia->iaid_set = 1;
14477827cba2SAaron LI 			} else
14487827cba2SAaron LI 				ia->iaid_set = 0;
14497827cba2SAaron LI 			if (!ia->iaid_set ||
14507827cba2SAaron LI 			    p == NULL ||
14517827cba2SAaron LI 			    ia->ia_type == D6_OPTION_IA_TA)
14527827cba2SAaron LI 			{
14537827cba2SAaron LI 				memset(&ia->addr, 0, sizeof(ia->addr));
14547827cba2SAaron LI 				ia->prefix_len = 0;
14557827cba2SAaron LI 			} else {
14567827cba2SAaron LI 				arg = p;
14577827cba2SAaron LI 				p = strchr(arg, '/');
14587827cba2SAaron LI 				if (p)
14597827cba2SAaron LI 					*p++ = '\0';
14606e63cc1fSRoy Marples 				if (inet_pton(AF_INET6, arg, &ia->addr) != 1) {
14616e63cc1fSRoy Marples 					logerrx("invalid AF_INET6: %s", arg);
14627827cba2SAaron LI 					memset(&ia->addr, 0, sizeof(ia->addr));
14637827cba2SAaron LI 				}
14647827cba2SAaron LI 				if (p && ia->ia_type == D6_OPTION_IA_PD) {
14657827cba2SAaron LI 					ia->prefix_len = (uint8_t)strtou(p,
14667827cba2SAaron LI 					    NULL, 0, 8, 120, &e);
14677827cba2SAaron LI 					if (e) {
14687827cba2SAaron LI 						logerrx("%s: failed to convert"
14697827cba2SAaron LI 						    " prefix len",
14707827cba2SAaron LI 						    p);
14717827cba2SAaron LI 						ia->prefix_len = 0;
14727827cba2SAaron LI 					}
14737827cba2SAaron LI 				}
14747827cba2SAaron LI 			}
14757827cba2SAaron LI #ifndef SMALL
14767827cba2SAaron LI 			ia->sla_max = 0;
14777827cba2SAaron LI 			ia->sla_len = 0;
14787827cba2SAaron LI 			ia->sla = NULL;
14797827cba2SAaron LI #endif
14807827cba2SAaron LI 		}
14818d36e1dfSRoy Marples 
14828d36e1dfSRoy Marples #ifdef SMALL
14838d36e1dfSRoy Marples 		break;
14848d36e1dfSRoy Marples #else
14857827cba2SAaron LI 		if (ia->ia_type != D6_OPTION_IA_PD)
14867827cba2SAaron LI 			break;
14878d36e1dfSRoy Marples 
14887827cba2SAaron LI 		for (p = fp; p; p = fp) {
14897827cba2SAaron LI 			fp = strwhite(p);
14907827cba2SAaron LI 			if (fp) {
14917827cba2SAaron LI 				*fp++ = '\0';
14927827cba2SAaron LI 				fp = strskipwhite(fp);
14937827cba2SAaron LI 			}
14947827cba2SAaron LI 			sla = reallocarray(ia->sla,
14957827cba2SAaron LI 			    ia->sla_len + 1, sizeof(*ia->sla));
14967827cba2SAaron LI 			if (sla == NULL) {
14977827cba2SAaron LI 				logerr(__func__);
14987827cba2SAaron LI 				return -1;
14997827cba2SAaron LI 			}
15007827cba2SAaron LI 			ia->sla = sla;
15017827cba2SAaron LI 			sla = &ia->sla[ia->sla_len++];
15027827cba2SAaron LI 			np = strchr(p, '/');
15037827cba2SAaron LI 			if (np)
15047827cba2SAaron LI 				*np++ = '\0';
15057827cba2SAaron LI 			if (strlcpy(sla->ifname, p,
15067827cba2SAaron LI 			    sizeof(sla->ifname)) >= sizeof(sla->ifname))
15077827cba2SAaron LI 			{
15087827cba2SAaron LI 				logerrx("%s: interface name too long", arg);
15097827cba2SAaron LI 				goto err_sla;
15107827cba2SAaron LI 			}
15117f8103cdSRoy Marples 			sla->sla_set = false;
15127827cba2SAaron LI 			sla->prefix_len = 0;
15137827cba2SAaron LI 			sla->suffix = 1;
15147827cba2SAaron LI 			p = np;
15157827cba2SAaron LI 			if (p) {
15167827cba2SAaron LI 				np = strchr(p, '/');
15177827cba2SAaron LI 				if (np)
15187827cba2SAaron LI 					*np++ = '\0';
15197827cba2SAaron LI 				if (*p != '\0') {
15207827cba2SAaron LI 					sla->sla = (uint32_t)strtou(p, NULL,
15217827cba2SAaron LI 					    0, 0, UINT32_MAX, &e);
15227f8103cdSRoy Marples 					sla->sla_set = true;
15237827cba2SAaron LI 					if (e) {
15247827cba2SAaron LI 						logerrx("%s: failed to convert "
15257827cba2SAaron LI 						    "sla",
15267827cba2SAaron LI 						    ifname);
15277827cba2SAaron LI 						goto err_sla;
15287827cba2SAaron LI 					}
15297827cba2SAaron LI 				}
15307827cba2SAaron LI 				p = np;
15317827cba2SAaron LI 			}
15327827cba2SAaron LI 			if (p) {
15337827cba2SAaron LI 				np = strchr(p, '/');
15347827cba2SAaron LI 				if (np)
15357827cba2SAaron LI 					*np++ = '\0';
15367827cba2SAaron LI 				if (*p != '\0') {
15377827cba2SAaron LI 					sla->prefix_len = (uint8_t)strtou(p,
15387827cba2SAaron LI 				    NULL, 0, 0, 120, &e);
15397827cba2SAaron LI 					if (e) {
15407827cba2SAaron LI 						logerrx("%s: failed to "
15417827cba2SAaron LI 						    "convert prefix len",
15427827cba2SAaron LI 						    ifname);
15437827cba2SAaron LI 						goto err_sla;
15447827cba2SAaron LI 					}
15457827cba2SAaron LI 				}
15467827cba2SAaron LI 				p = np;
15477827cba2SAaron LI 			}
15487827cba2SAaron LI 			if (p) {
15497827cba2SAaron LI 				np = strchr(p, '/');
15507827cba2SAaron LI 				if (np)
15517827cba2SAaron LI 					*np = '\0';
15527827cba2SAaron LI 				if (*p != '\0') {
15537827cba2SAaron LI 					sla->suffix = (uint64_t)strtou(p, NULL,
15547827cba2SAaron LI 					    0, 0, UINT64_MAX, &e);
15557827cba2SAaron LI 					if (e) {
15567827cba2SAaron LI 						logerrx("%s: failed to "
15577827cba2SAaron LI 						    "convert suffix",
15587827cba2SAaron LI 						    ifname);
15597827cba2SAaron LI 						goto err_sla;
15607827cba2SAaron LI 					}
15617827cba2SAaron LI 				}
15627827cba2SAaron LI 			}
15637827cba2SAaron LI 			/* Sanity check */
15647827cba2SAaron LI 			for (sl = 0; sl < ia->sla_len - 1; sl++) {
15657827cba2SAaron LI 				slap = &ia->sla[sl];
15667827cba2SAaron LI 				if (slap->sla_set != sla->sla_set) {
15677827cba2SAaron LI 					logerrx("%s: cannot mix automatic "
15687827cba2SAaron LI 					    "and fixed SLA",
15697827cba2SAaron LI 					    sla->ifname);
15707827cba2SAaron LI 					goto err_sla;
15717827cba2SAaron LI 				}
15727827cba2SAaron LI 				if (ia->prefix_len &&
15737827cba2SAaron LI 				    (sla->prefix_len == ia->prefix_len ||
15747827cba2SAaron LI 				    slap->prefix_len == ia->prefix_len))
15757827cba2SAaron LI 				{
15767827cba2SAaron LI 					logerrx("%s: cannot delegte the same"
15777827cba2SAaron LI 					    "prefix length more than once",
15787827cba2SAaron LI 					    sla->ifname);
15797827cba2SAaron LI 					goto err_sla;
15807827cba2SAaron LI 				}
15817f8103cdSRoy Marples 				if (!sla->sla_set &&
15827827cba2SAaron LI 				    strcmp(slap->ifname, sla->ifname) == 0)
15837827cba2SAaron LI 				{
15847827cba2SAaron LI 					logwarnx("%s: cannot specify the "
15857827cba2SAaron LI 					    "same interface twice with "
15867827cba2SAaron LI 					    "an automatic SLA",
15877827cba2SAaron LI 					    sla->ifname);
15887827cba2SAaron LI 					goto err_sla;
15897827cba2SAaron LI 				}
15907827cba2SAaron LI 				if (slap->sla_set && sla->sla_set &&
15917827cba2SAaron LI 				    slap->sla == sla->sla)
15927827cba2SAaron LI 				{
15937827cba2SAaron LI 					logerrx("%s: cannot"
15947827cba2SAaron LI 					    " assign the same SLA %u"
15957827cba2SAaron LI 					    " more than once",
15967827cba2SAaron LI 					    sla->ifname, sla->sla);
15977827cba2SAaron LI 					goto err_sla;
15987827cba2SAaron LI 				}
15997827cba2SAaron LI 			}
16007827cba2SAaron LI 			if (sla->sla_set && sla->sla > ia->sla_max)
16017827cba2SAaron LI 				ia->sla_max = sla->sla;
16027827cba2SAaron LI 		}
16037827cba2SAaron LI 		break;
16047827cba2SAaron LI err_sla:
16057827cba2SAaron LI 		ia->sla_len--;
16067827cba2SAaron LI 		return -1;
16077827cba2SAaron LI #endif
16087827cba2SAaron LI #endif
16097827cba2SAaron LI 	case O_HOSTNAME_SHORT:
16107827cba2SAaron LI 		ifo->options |= DHCPCD_HOSTNAME | DHCPCD_HOSTNAME_SHORT;
16117827cba2SAaron LI 		break;
16127827cba2SAaron LI 	case O_DEV:
16137827cba2SAaron LI 		ARG_REQUIRED;
16147827cba2SAaron LI #ifdef PLUGIN_DEV
16157827cba2SAaron LI 		if (ctx->dev_load)
16167827cba2SAaron LI 			free(ctx->dev_load);
16177827cba2SAaron LI 		ctx->dev_load = strdup(arg);
16187827cba2SAaron LI #endif
16197827cba2SAaron LI 		break;
16207827cba2SAaron LI 	case O_NODEV:
16217827cba2SAaron LI 		ifo->options &= ~DHCPCD_DEV;
16227827cba2SAaron LI 		break;
16237827cba2SAaron LI 	case O_DEFINE:
16247827cba2SAaron LI 		dop = &ifo->dhcp_override;
16257827cba2SAaron LI 		dop_len = &ifo->dhcp_override_len;
16267827cba2SAaron LI 		/* FALLTHROUGH */
16277827cba2SAaron LI 	case O_DEFINEND:
16287827cba2SAaron LI 		if (dop == NULL) {
16297827cba2SAaron LI 			dop = &ifo->nd_override;
16307827cba2SAaron LI 			dop_len = &ifo->nd_override_len;
16317827cba2SAaron LI 		}
16327827cba2SAaron LI 		/* FALLTHROUGH */
16337827cba2SAaron LI 	case O_DEFINE6:
16347827cba2SAaron LI 		if (dop == NULL) {
16357827cba2SAaron LI 			dop = &ifo->dhcp6_override;
16367827cba2SAaron LI 			dop_len = &ifo->dhcp6_override_len;
16377827cba2SAaron LI 		}
16387827cba2SAaron LI 		/* FALLTHROUGH */
16397827cba2SAaron LI 	case O_VENDOPT:
16407827cba2SAaron LI 		if (dop == NULL) {
16417827cba2SAaron LI 			dop = &ifo->vivso_override;
16427827cba2SAaron LI 			dop_len = &ifo->vivso_override_len;
16437827cba2SAaron LI 		}
16447827cba2SAaron LI 		*edop = *ldop = NULL;
16457827cba2SAaron LI 		/* FALLTHROUGH */
16467827cba2SAaron LI 	case O_EMBED:
16477827cba2SAaron LI 		if (dop == NULL) {
16487827cba2SAaron LI 			if (*edop) {
16497827cba2SAaron LI 				dop = &(*edop)->embopts;
16507827cba2SAaron LI 				dop_len = &(*edop)->embopts_len;
16517827cba2SAaron LI 			} else if (ldop) {
16527827cba2SAaron LI 				dop = &(*ldop)->embopts;
16537827cba2SAaron LI 				dop_len = &(*ldop)->embopts_len;
16547827cba2SAaron LI 			} else {
16557827cba2SAaron LI 				logerrx("embed must be after a define "
16567827cba2SAaron LI 				    "or encap");
16577827cba2SAaron LI 				return -1;
16587827cba2SAaron LI 			}
16597827cba2SAaron LI 		}
16607827cba2SAaron LI 		/* FALLTHROUGH */
16617827cba2SAaron LI 	case O_ENCAP:
16627827cba2SAaron LI 		ARG_REQUIRED;
16637827cba2SAaron LI 		if (dop == NULL) {
16647827cba2SAaron LI 			if (*ldop == NULL) {
16657827cba2SAaron LI 				logerrx("encap must be after a define");
16667827cba2SAaron LI 				return -1;
16677827cba2SAaron LI 			}
16687827cba2SAaron LI 			dop = &(*ldop)->encopts;
16697827cba2SAaron LI 			dop_len = &(*ldop)->encopts_len;
16707827cba2SAaron LI 		}
16717827cba2SAaron LI 
16727827cba2SAaron LI 		/* Shared code for define, define6, embed and encap */
16737827cba2SAaron LI 
16747827cba2SAaron LI 		/* code */
16757827cba2SAaron LI 		if (opt == O_EMBED) /* Embedded options don't have codes */
16767827cba2SAaron LI 			u = 0;
16777827cba2SAaron LI 		else {
16787827cba2SAaron LI 			fp = strwhite(arg);
16797827cba2SAaron LI 			if (fp == NULL) {
16807827cba2SAaron LI 				logerrx("invalid syntax: %s", arg);
16817827cba2SAaron LI 				return -1;
16827827cba2SAaron LI 			}
16837827cba2SAaron LI 			*fp++ = '\0';
16847827cba2SAaron LI 			u = (uint32_t)strtou(arg, NULL, 0, 0, UINT32_MAX, &e);
16857827cba2SAaron LI 			if (e) {
16867827cba2SAaron LI 				logerrx("invalid code: %s", arg);
16877827cba2SAaron LI 				return -1;
16887827cba2SAaron LI 			}
16897827cba2SAaron LI 			arg = strskipwhite(fp);
16907827cba2SAaron LI 			if (arg == NULL) {
16917827cba2SAaron LI 				logerrx("invalid syntax");
16927827cba2SAaron LI 				return -1;
16937827cba2SAaron LI 			}
16947827cba2SAaron LI 		}
16957827cba2SAaron LI 		/* type */
16967827cba2SAaron LI 		fp = strwhite(arg);
16977827cba2SAaron LI 		if (fp)
16987827cba2SAaron LI 			*fp++ = '\0';
16997827cba2SAaron LI 		np = strchr(arg, ':');
17007827cba2SAaron LI 		/* length */
17017827cba2SAaron LI 		if (np) {
17027827cba2SAaron LI 			*np++ = '\0';
17037827cba2SAaron LI 			bp = NULL; /* No bitflag */
17047827cba2SAaron LI 			l = (long)strtou(np, NULL, 0, 0, LONG_MAX, &e);
17057827cba2SAaron LI 			if (e) {
17067827cba2SAaron LI 				logerrx("failed to convert length");
17077827cba2SAaron LI 				return -1;
17087827cba2SAaron LI 			}
17097827cba2SAaron LI 		} else {
17107827cba2SAaron LI 			l = 0;
17117827cba2SAaron LI 			bp = strchr(arg, '='); /* bitflag assignment */
17127827cba2SAaron LI 			if (bp)
17137827cba2SAaron LI 				*bp++ = '\0';
17147827cba2SAaron LI 		}
17157827cba2SAaron LI 		t = 0;
17167827cba2SAaron LI 		if (strcasecmp(arg, "request") == 0) {
17177827cba2SAaron LI 			t |= OT_REQUEST;
17187827cba2SAaron LI 			arg = strskipwhite(fp);
17197827cba2SAaron LI 			fp = strwhite(arg);
17207827cba2SAaron LI 			if (fp == NULL) {
17217827cba2SAaron LI 				logerrx("incomplete request type");
17227827cba2SAaron LI 				return -1;
17237827cba2SAaron LI 			}
17247827cba2SAaron LI 			*fp++ = '\0';
17257827cba2SAaron LI 		} else if (strcasecmp(arg, "norequest") == 0) {
17267827cba2SAaron LI 			t |= OT_NOREQ;
17277827cba2SAaron LI 			arg = strskipwhite(fp);
17287827cba2SAaron LI 			fp = strwhite(arg);
17297827cba2SAaron LI 			if (fp == NULL) {
17307827cba2SAaron LI 				logerrx("incomplete request type");
17317827cba2SAaron LI 				return -1;
17327827cba2SAaron LI 			}
17337827cba2SAaron LI 			*fp++ = '\0';
17347827cba2SAaron LI 		}
17357827cba2SAaron LI 		if (strcasecmp(arg, "optional") == 0) {
17367827cba2SAaron LI 			t |= OT_OPTIONAL;
17377827cba2SAaron LI 			arg = strskipwhite(fp);
17387827cba2SAaron LI 			fp = strwhite(arg);
17397827cba2SAaron LI 			if (fp == NULL) {
17407827cba2SAaron LI 				logerrx("incomplete optional type");
17417827cba2SAaron LI 				return -1;
17427827cba2SAaron LI 			}
17437827cba2SAaron LI 			*fp++ = '\0';
17447827cba2SAaron LI 		}
17457827cba2SAaron LI 		if (strcasecmp(arg, "index") == 0) {
17467827cba2SAaron LI 			t |= OT_INDEX;
17477827cba2SAaron LI 			arg = strskipwhite(fp);
17487827cba2SAaron LI 			fp = strwhite(arg);
17497827cba2SAaron LI 			if (fp == NULL) {
17507827cba2SAaron LI 				logerrx("incomplete index type");
17517827cba2SAaron LI 				return -1;
17527827cba2SAaron LI 			}
17537827cba2SAaron LI 			*fp++ = '\0';
17547827cba2SAaron LI 		}
17557827cba2SAaron LI 		if (strcasecmp(arg, "array") == 0) {
17567827cba2SAaron LI 			t |= OT_ARRAY;
17577827cba2SAaron LI 			arg = strskipwhite(fp);
17587827cba2SAaron LI 			fp = strwhite(arg);
17597827cba2SAaron LI 			if (fp == NULL) {
17607827cba2SAaron LI 				logerrx("incomplete array type");
17617827cba2SAaron LI 				return -1;
17627827cba2SAaron LI 			}
17637827cba2SAaron LI 			*fp++ = '\0';
17647827cba2SAaron LI 		}
17657827cba2SAaron LI 		if (strcasecmp(arg, "ipaddress") == 0)
17667827cba2SAaron LI 			t |= OT_ADDRIPV4;
17677827cba2SAaron LI 		else if (strcasecmp(arg, "ip6address") == 0)
17687827cba2SAaron LI 			t |= OT_ADDRIPV6;
17697827cba2SAaron LI 		else if (strcasecmp(arg, "string") == 0)
17707827cba2SAaron LI 			t |= OT_STRING;
17717827cba2SAaron LI 		else if (strcasecmp(arg, "byte") == 0)
17727827cba2SAaron LI 			t |= OT_UINT8;
17737827cba2SAaron LI 		else if (strcasecmp(arg, "bitflags") == 0)
17747827cba2SAaron LI 			t |= OT_BITFLAG;
17757827cba2SAaron LI 		else if (strcasecmp(arg, "uint8") == 0)
17767827cba2SAaron LI 			t |= OT_UINT8;
17777827cba2SAaron LI 		else if (strcasecmp(arg, "int8") == 0)
17787827cba2SAaron LI 			t |= OT_INT8;
17797827cba2SAaron LI 		else if (strcasecmp(arg, "uint16") == 0)
17807827cba2SAaron LI 			t |= OT_UINT16;
17817827cba2SAaron LI 		else if (strcasecmp(arg, "int16") == 0)
17827827cba2SAaron LI 			t |= OT_INT16;
17837827cba2SAaron LI 		else if (strcasecmp(arg, "uint32") == 0)
17847827cba2SAaron LI 			t |= OT_UINT32;
17857827cba2SAaron LI 		else if (strcasecmp(arg, "int32") == 0)
17867827cba2SAaron LI 			t |= OT_INT32;
17877827cba2SAaron LI 		else if (strcasecmp(arg, "flag") == 0)
17887827cba2SAaron LI 			t |= OT_FLAG;
17897827cba2SAaron LI 		else if (strcasecmp(arg, "raw") == 0)
17907827cba2SAaron LI 			t |= OT_STRING | OT_RAW;
17917827cba2SAaron LI 		else if (strcasecmp(arg, "ascii") == 0)
17927827cba2SAaron LI 			t |= OT_STRING | OT_ASCII;
17937827cba2SAaron LI 		else if (strcasecmp(arg, "domain") == 0)
17947827cba2SAaron LI 			t |= OT_STRING | OT_DOMAIN | OT_RFC1035;
17957827cba2SAaron LI 		else if (strcasecmp(arg, "dname") == 0)
17967827cba2SAaron LI 			t |= OT_STRING | OT_DOMAIN;
17977827cba2SAaron LI 		else if (strcasecmp(arg, "binhex") == 0)
17987827cba2SAaron LI 			t |= OT_STRING | OT_BINHEX;
17997827cba2SAaron LI 		else if (strcasecmp(arg, "embed") == 0)
18007827cba2SAaron LI 			t |= OT_EMBED;
18017827cba2SAaron LI 		else if (strcasecmp(arg, "encap") == 0)
18027827cba2SAaron LI 			t |= OT_ENCAP;
18037827cba2SAaron LI 		else if (strcasecmp(arg, "rfc3361") ==0)
18047827cba2SAaron LI 			t |= OT_STRING | OT_RFC3361;
18057827cba2SAaron LI 		else if (strcasecmp(arg, "rfc3442") ==0)
18067827cba2SAaron LI 			t |= OT_STRING | OT_RFC3442;
18077827cba2SAaron LI 		else if (strcasecmp(arg, "option") == 0)
18087827cba2SAaron LI 			t |= OT_OPTION;
18097827cba2SAaron LI 		else {
18107827cba2SAaron LI 			logerrx("unknown type: %s", arg);
18117827cba2SAaron LI 			return -1;
18127827cba2SAaron LI 		}
18137827cba2SAaron LI 		if (l && !(t & (OT_STRING | OT_BINHEX))) {
1814a0d9933aSRoy Marples 			logwarnx("ignoring length for type: %s", arg);
18157827cba2SAaron LI 			l = 0;
18167827cba2SAaron LI 		}
18177827cba2SAaron LI 		if (t & OT_ARRAY && t & (OT_STRING | OT_BINHEX) &&
18187827cba2SAaron LI 		    !(t & (OT_RFC1035 | OT_DOMAIN)))
18197827cba2SAaron LI 		{
18207827cba2SAaron LI 			logwarnx("ignoring array for strings");
18217827cba2SAaron LI 			t &= ~OT_ARRAY;
18227827cba2SAaron LI 		}
18237827cba2SAaron LI 		if (t & OT_BITFLAG) {
18247827cba2SAaron LI 			if (bp == NULL)
18257827cba2SAaron LI 				logwarnx("missing bitflag assignment");
18267827cba2SAaron LI 		}
18277827cba2SAaron LI 		/* variable */
18287827cba2SAaron LI 		if (!fp) {
18297827cba2SAaron LI 			if (!(t & OT_OPTION)) {
18307827cba2SAaron LI 			        logerrx("type %s requires a variable name",
18317827cba2SAaron LI 				    arg);
18327827cba2SAaron LI 				return -1;
18337827cba2SAaron LI 			}
18347827cba2SAaron LI 			np = NULL;
18357827cba2SAaron LI 		} else {
18367827cba2SAaron LI 			arg = strskipwhite(fp);
18377827cba2SAaron LI 			fp = strwhite(arg);
18387827cba2SAaron LI 			if (fp)
18397827cba2SAaron LI 				*fp++ = '\0';
18407827cba2SAaron LI 			if (strcasecmp(arg, "reserved")) {
18417827cba2SAaron LI 				np = strdup(arg);
18427827cba2SAaron LI 				if (np == NULL) {
18437827cba2SAaron LI 					logerr(__func__);
18447827cba2SAaron LI 					return -1;
18457827cba2SAaron LI 				}
18467827cba2SAaron LI 			} else {
18477827cba2SAaron LI 				np = NULL;
18487827cba2SAaron LI 				t |= OT_RESERVED;
18497827cba2SAaron LI 			}
18507827cba2SAaron LI 		}
18517827cba2SAaron LI 		if (opt != O_EMBED) {
18527827cba2SAaron LI 			for (dl = 0, ndop = *dop; dl < *dop_len; dl++, ndop++)
18537827cba2SAaron LI 			{
18547827cba2SAaron LI 				/* type 0 seems freshly malloced struct
18557827cba2SAaron LI 				 * for us to use */
18567827cba2SAaron LI 				if (ndop->option == u || ndop->type == 0)
18577827cba2SAaron LI 					break;
18587827cba2SAaron LI 			}
18597827cba2SAaron LI 			if (dl == *dop_len)
18607827cba2SAaron LI 				ndop = NULL;
18617827cba2SAaron LI 		} else
18627827cba2SAaron LI 			ndop = NULL;
18637827cba2SAaron LI 		if (ndop == NULL) {
18647827cba2SAaron LI 			ndop = reallocarray(*dop, *dop_len + 1, sizeof(**dop));
18657827cba2SAaron LI 			if (ndop == NULL) {
18667827cba2SAaron LI 				logerr(__func__);
18677827cba2SAaron LI 				free(np);
18687827cba2SAaron LI 				return -1;
18697827cba2SAaron LI 			}
18707827cba2SAaron LI 			*dop = ndop;
18717827cba2SAaron LI 			ndop = &(*dop)[(*dop_len)++];
18727827cba2SAaron LI 			ndop->embopts = NULL;
18737827cba2SAaron LI 			ndop->embopts_len = 0;
18747827cba2SAaron LI 			ndop->encopts = NULL;
18757827cba2SAaron LI 			ndop->encopts_len = 0;
18767827cba2SAaron LI 		} else
18777827cba2SAaron LI 			free_dhcp_opt_embenc(ndop);
18787827cba2SAaron LI 		ndop->option = (uint32_t)u; /* could have been 0 */
18797827cba2SAaron LI 		ndop->type = t;
18807827cba2SAaron LI 		ndop->len = (size_t)l;
18817827cba2SAaron LI 		ndop->var = np;
18827827cba2SAaron LI 		if (bp) {
18837827cba2SAaron LI 			dl = strlen(bp);
18847827cba2SAaron LI 			memcpy(ndop->bitflags, bp, dl);
18857827cba2SAaron LI 			memset(ndop->bitflags + dl, 0,
18867827cba2SAaron LI 			    sizeof(ndop->bitflags) - dl);
18877827cba2SAaron LI 		} else
18887827cba2SAaron LI 			memset(ndop->bitflags, 0, sizeof(ndop->bitflags));
18897827cba2SAaron LI 		/* Save the define for embed and encap options */
18907827cba2SAaron LI 		switch (opt) {
18917827cba2SAaron LI 		case O_DEFINE:
18927827cba2SAaron LI 		case O_DEFINEND:
18937827cba2SAaron LI 		case O_DEFINE6:
18947827cba2SAaron LI 		case O_VENDOPT:
18957827cba2SAaron LI 			*ldop = ndop;
18967827cba2SAaron LI 			break;
18977827cba2SAaron LI 		case O_ENCAP:
18987827cba2SAaron LI 			*edop = ndop;
18997827cba2SAaron LI 			break;
19007827cba2SAaron LI 		}
19017827cba2SAaron LI 		break;
19027827cba2SAaron LI 	case O_VENDCLASS:
19037827cba2SAaron LI 		ARG_REQUIRED;
19047827cba2SAaron LI 		fp = strwhite(arg);
19057827cba2SAaron LI 		if (fp)
19067827cba2SAaron LI 			*fp++ = '\0';
19077827cba2SAaron LI 		u = (uint32_t)strtou(arg, NULL, 0, 0, UINT32_MAX, &e);
19087827cba2SAaron LI 		if (e) {
19097827cba2SAaron LI 			logerrx("invalid code: %s", arg);
19107827cba2SAaron LI 			return -1;
19117827cba2SAaron LI 		}
19127827cba2SAaron LI 		fp = strskipwhite(fp);
19137827cba2SAaron LI 		if (fp) {
19147827cba2SAaron LI 			s = parse_string(NULL, 0, fp);
19157827cba2SAaron LI 			if (s == -1) {
19167827cba2SAaron LI 				logerr(__func__);
19177827cba2SAaron LI 				return -1;
19187827cba2SAaron LI 			}
19197827cba2SAaron LI 			dl = (size_t)s;
19207827cba2SAaron LI 			if (dl + (sizeof(uint16_t) * 2) > UINT16_MAX) {
19217827cba2SAaron LI 				logerrx("vendor class is too big");
19227827cba2SAaron LI 				return -1;
19237827cba2SAaron LI 			}
19247827cba2SAaron LI 			np = malloc(dl);
19257827cba2SAaron LI 			if (np == NULL) {
19267827cba2SAaron LI 				logerr(__func__);
19277827cba2SAaron LI 				return -1;
19287827cba2SAaron LI 			}
19297827cba2SAaron LI 			parse_string(np, dl, fp);
19307827cba2SAaron LI 		} else {
19317827cba2SAaron LI 			dl = 0;
19327827cba2SAaron LI 			np = NULL;
19337827cba2SAaron LI 		}
19347827cba2SAaron LI 		vivco = reallocarray(ifo->vivco,
19357827cba2SAaron LI 		    ifo->vivco_len + 1, sizeof(*ifo->vivco));
19367827cba2SAaron LI 		if (vivco == NULL) {
19377827cba2SAaron LI 			logerr( __func__);
19388d36e1dfSRoy Marples 			free(np);
19397827cba2SAaron LI 			return -1;
19407827cba2SAaron LI 		}
19417827cba2SAaron LI 		ifo->vivco = vivco;
19427827cba2SAaron LI 		ifo->vivco_en = (uint32_t)u;
19437827cba2SAaron LI 		vivco = &ifo->vivco[ifo->vivco_len++];
19447827cba2SAaron LI 		vivco->len = dl;
19457827cba2SAaron LI 		vivco->data = (uint8_t *)np;
19467827cba2SAaron LI 		break;
19477827cba2SAaron LI 	case O_AUTHPROTOCOL:
19487827cba2SAaron LI 		ARG_REQUIRED;
19497827cba2SAaron LI #ifdef AUTH
19507827cba2SAaron LI 		fp = strwhite(arg);
19517827cba2SAaron LI 		if (fp)
19527827cba2SAaron LI 			*fp++ = '\0';
19537827cba2SAaron LI 		if (strcasecmp(arg, "token") == 0)
19547827cba2SAaron LI 			ifo->auth.protocol = AUTH_PROTO_TOKEN;
19557827cba2SAaron LI 		else if (strcasecmp(arg, "delayed") == 0)
19567827cba2SAaron LI 			ifo->auth.protocol = AUTH_PROTO_DELAYED;
19577827cba2SAaron LI 		else if (strcasecmp(arg, "delayedrealm") == 0)
19587827cba2SAaron LI 			ifo->auth.protocol = AUTH_PROTO_DELAYEDREALM;
19597827cba2SAaron LI 		else {
19607827cba2SAaron LI 			logerrx("%s: unsupported protocol", arg);
19617827cba2SAaron LI 			return -1;
19627827cba2SAaron LI 		}
19637827cba2SAaron LI 		arg = strskipwhite(fp);
19647827cba2SAaron LI 		fp = strwhite(arg);
19657827cba2SAaron LI 		if (arg == NULL) {
19667827cba2SAaron LI 			ifo->auth.options |= DHCPCD_AUTH_SEND;
19677827cba2SAaron LI 			if (ifo->auth.protocol == AUTH_PROTO_TOKEN)
19687827cba2SAaron LI 				ifo->auth.protocol = AUTH_ALG_NONE;
19697827cba2SAaron LI 			else
19707827cba2SAaron LI 				ifo->auth.algorithm = AUTH_ALG_HMAC_MD5;
19717827cba2SAaron LI 			ifo->auth.rdm = AUTH_RDM_MONOTONIC;
19727827cba2SAaron LI 			break;
19737827cba2SAaron LI 		}
19747827cba2SAaron LI 		if (fp)
19757827cba2SAaron LI 			*fp++ = '\0';
19767827cba2SAaron LI 		if (ifo->auth.protocol == AUTH_PROTO_TOKEN) {
19777827cba2SAaron LI 			np = strchr(arg, '/');
19787827cba2SAaron LI 			if (np) {
19797827cba2SAaron LI 				if (fp == NULL || np < fp)
19807827cba2SAaron LI 					*np++ = '\0';
19817827cba2SAaron LI 				else
19827827cba2SAaron LI 					np = NULL;
19837827cba2SAaron LI 			}
19847827cba2SAaron LI 			if (parse_uint32(&ifo->auth.token_snd_secretid,
19857827cba2SAaron LI 			    arg) == -1)
19867827cba2SAaron LI 				logerrx("%s: not a number", arg);
19877827cba2SAaron LI 			else
19887827cba2SAaron LI 				ifo->auth.token_rcv_secretid =
19897827cba2SAaron LI 				    ifo->auth.token_snd_secretid;
19907827cba2SAaron LI 			if (np &&
19917827cba2SAaron LI 			    parse_uint32(&ifo->auth.token_rcv_secretid,
19927827cba2SAaron LI 			    np) == -1)
19937827cba2SAaron LI 				logerrx("%s: not a number", arg);
19947827cba2SAaron LI 		} else {
19957827cba2SAaron LI 			if (strcasecmp(arg, "hmacmd5") == 0 ||
19967827cba2SAaron LI 			    strcasecmp(arg, "hmac-md5") == 0)
19977827cba2SAaron LI 				ifo->auth.algorithm = AUTH_ALG_HMAC_MD5;
19987827cba2SAaron LI 			else {
19997827cba2SAaron LI 				logerrx("%s: unsupported algorithm", arg);
20007827cba2SAaron LI 				return 1;
20017827cba2SAaron LI 			}
20027827cba2SAaron LI 		}
20037827cba2SAaron LI 		arg = fp;
20047827cba2SAaron LI 		if (arg == NULL) {
20057827cba2SAaron LI 			ifo->auth.options |= DHCPCD_AUTH_SEND;
20067827cba2SAaron LI 			ifo->auth.rdm = AUTH_RDM_MONOTONIC;
20077827cba2SAaron LI 			break;
20087827cba2SAaron LI 		}
20097827cba2SAaron LI 		if (strcasecmp(arg, "monocounter") == 0) {
20107827cba2SAaron LI 			ifo->auth.rdm = AUTH_RDM_MONOTONIC;
20117827cba2SAaron LI 			ifo->auth.options |= DHCPCD_AUTH_RDM_COUNTER;
20127827cba2SAaron LI 		} else if (strcasecmp(arg, "monotonic") ==0 ||
20137827cba2SAaron LI 		    strcasecmp(arg, "monotime") == 0)
20147827cba2SAaron LI 			ifo->auth.rdm = AUTH_RDM_MONOTONIC;
20157827cba2SAaron LI 		else {
20167827cba2SAaron LI 			logerrx("%s: unsupported RDM", arg);
20177827cba2SAaron LI 			return -1;
20187827cba2SAaron LI 		}
20197827cba2SAaron LI 		ifo->auth.options |= DHCPCD_AUTH_SEND;
20207827cba2SAaron LI 		break;
20217827cba2SAaron LI #else
20227827cba2SAaron LI 		logerrx("no authentication support");
20237827cba2SAaron LI 		return -1;
20247827cba2SAaron LI #endif
20257827cba2SAaron LI 	case O_AUTHTOKEN:
20267827cba2SAaron LI 		ARG_REQUIRED;
20277827cba2SAaron LI #ifdef AUTH
20287827cba2SAaron LI 		fp = strwhite(arg);
20297827cba2SAaron LI 		if (fp == NULL) {
20307827cba2SAaron LI 			logerrx("authtoken requires a realm");
20317827cba2SAaron LI 			return -1;
20327827cba2SAaron LI 		}
20337827cba2SAaron LI 		*fp++ = '\0';
20346e63cc1fSRoy Marples 		token = calloc(1, sizeof(*token));
20357827cba2SAaron LI 		if (token == NULL) {
20367827cba2SAaron LI 			logerr(__func__);
20377827cba2SAaron LI 			return -1;
20387827cba2SAaron LI 		}
20397827cba2SAaron LI 		if (parse_uint32(&token->secretid, arg) == -1) {
20407827cba2SAaron LI 			logerrx("%s: not a number", arg);
20416e63cc1fSRoy Marples 			goto invalid_token;
20427827cba2SAaron LI 		}
20437827cba2SAaron LI 		arg = fp;
20447827cba2SAaron LI 		fp = strend(arg);
20457827cba2SAaron LI 		if (fp == NULL) {
20467827cba2SAaron LI 			logerrx("authtoken requies an a key");
20476e63cc1fSRoy Marples 			goto invalid_token;
20487827cba2SAaron LI 		}
20497827cba2SAaron LI 		*fp++ = '\0';
20507827cba2SAaron LI 		s = parse_string(NULL, 0, arg);
20517827cba2SAaron LI 		if (s == -1) {
20527827cba2SAaron LI 			logerr("realm_len");
20536e63cc1fSRoy Marples 			goto invalid_token;
20547827cba2SAaron LI 		}
20556e63cc1fSRoy Marples 		if (s != 0) {
20567827cba2SAaron LI 			token->realm_len = (size_t)s;
20577827cba2SAaron LI 			token->realm = malloc(token->realm_len);
20587827cba2SAaron LI 			if (token->realm == NULL) {
20597827cba2SAaron LI 				logerr(__func__);
20606e63cc1fSRoy Marples 				goto invalid_token;
20617827cba2SAaron LI 			}
20627827cba2SAaron LI 			parse_string((char *)token->realm, token->realm_len,
20637827cba2SAaron LI 			    arg);
20647827cba2SAaron LI 		}
20657827cba2SAaron LI 		arg = fp;
20667827cba2SAaron LI 		fp = strend(arg);
20677827cba2SAaron LI 		if (fp == NULL) {
20688d36e1dfSRoy Marples 			logerrx("authtoken requies an expiry date");
20696e63cc1fSRoy Marples 			goto invalid_token;
20707827cba2SAaron LI 		}
20717827cba2SAaron LI 		*fp++ = '\0';
20727827cba2SAaron LI 		if (*arg == '"') {
20737827cba2SAaron LI 			arg++;
20747827cba2SAaron LI 			np = strchr(arg, '"');
20757827cba2SAaron LI 			if (np)
20767827cba2SAaron LI 				*np = '\0';
20777827cba2SAaron LI 		}
20787827cba2SAaron LI 		if (strcmp(arg, "0") == 0 || strcasecmp(arg, "forever") == 0)
20797827cba2SAaron LI 			token->expire =0;
20807827cba2SAaron LI 		else {
20817827cba2SAaron LI 			struct tm tm;
20827827cba2SAaron LI 
20837827cba2SAaron LI 			memset(&tm, 0, sizeof(tm));
20847827cba2SAaron LI 			if (strptime(arg, "%Y-%m-%d %H:%M", &tm) == NULL) {
20857827cba2SAaron LI 				logerrx("%s: invalid date time", arg);
20866e63cc1fSRoy Marples 				goto invalid_token;
20877827cba2SAaron LI 			}
20887827cba2SAaron LI 			if ((token->expire = mktime(&tm)) == (time_t)-1) {
20897827cba2SAaron LI 				logerr("%s: mktime", __func__);
20906e63cc1fSRoy Marples 				goto invalid_token;
20917827cba2SAaron LI 			}
20927827cba2SAaron LI 		}
20937827cba2SAaron LI 		arg = fp;
20947827cba2SAaron LI 		s = parse_string(NULL, 0, arg);
20957827cba2SAaron LI 		if (s == -1 || s == 0) {
20967827cba2SAaron LI 			if (s == -1)
20977827cba2SAaron LI 				logerr("token_len");
20987827cba2SAaron LI 			else
20997827cba2SAaron LI 				logerrx("authtoken needs a key");
21006e63cc1fSRoy Marples 			goto invalid_token;
21017827cba2SAaron LI 		}
21027827cba2SAaron LI 		token->key_len = (size_t)s;
21037827cba2SAaron LI 		token->key = malloc(token->key_len);
21046e63cc1fSRoy Marples 		if (token->key == NULL) {
21056e63cc1fSRoy Marples 			logerr(__func__);
21066e63cc1fSRoy Marples 			goto invalid_token;
21076e63cc1fSRoy Marples 		}
21087827cba2SAaron LI 		parse_string((char *)token->key, token->key_len, arg);
21097827cba2SAaron LI 		TAILQ_INSERT_TAIL(&ifo->auth.tokens, token, next);
21106e63cc1fSRoy Marples 		break;
21116e63cc1fSRoy Marples 
21126e63cc1fSRoy Marples invalid_token:
21136e63cc1fSRoy Marples 		free(token->realm);
21146e63cc1fSRoy Marples 		free(token);
21157827cba2SAaron LI #else
21167827cba2SAaron LI 		logerrx("no authentication support");
21177827cba2SAaron LI #endif
21186e63cc1fSRoy Marples 		return -1;
21197827cba2SAaron LI 	case O_AUTHNOTREQUIRED:
21207827cba2SAaron LI 		ifo->auth.options &= ~DHCPCD_AUTH_REQUIRE;
21217827cba2SAaron LI 		break;
21227827cba2SAaron LI 	case O_DHCP:
21238d36e1dfSRoy Marples 		ifo->options |= DHCPCD_DHCP | DHCPCD_WANTDHCP | DHCPCD_IPV4;
21247827cba2SAaron LI 		break;
21257827cba2SAaron LI 	case O_NODHCP:
21267827cba2SAaron LI 		ifo->options &= ~DHCPCD_DHCP;
21277827cba2SAaron LI 		break;
21287827cba2SAaron LI 	case O_DHCP6:
21297827cba2SAaron LI 		ifo->options |= DHCPCD_DHCP6 | DHCPCD_IPV6;
21307827cba2SAaron LI 		break;
21317827cba2SAaron LI 	case O_NODHCP6:
21327827cba2SAaron LI 		ifo->options &= ~DHCPCD_DHCP6;
21337827cba2SAaron LI 		break;
21347827cba2SAaron LI 	case O_CONTROLGRP:
21357827cba2SAaron LI 		ARG_REQUIRED;
2136b8b69544SRoy Marples #ifdef PRIVSEP
2137b8b69544SRoy Marples 		/* Control group is already set by this point.
2138b8b69544SRoy Marples 		 * We don't need to pledge getpw either with this. */
2139b8b69544SRoy Marples 		if (IN_PRIVSEP(ctx))
2140b8b69544SRoy Marples 			break;
2141b8b69544SRoy Marples #endif
21427827cba2SAaron LI #ifdef _REENTRANT
21437827cba2SAaron LI 		l = sysconf(_SC_GETGR_R_SIZE_MAX);
21447827cba2SAaron LI 		if (l == -1)
21457827cba2SAaron LI 			dl = 1024;
21467827cba2SAaron LI 		else
21477827cba2SAaron LI 			dl = (size_t)l;
21487827cba2SAaron LI 		p = malloc(dl);
21497827cba2SAaron LI 		if (p == NULL) {
21507827cba2SAaron LI 			logerr(__func__);
21517827cba2SAaron LI 			return -1;
21527827cba2SAaron LI 		}
2153d4fb1e02SRoy Marples 		while ((i = getgrnam_r(arg, &grpbuf, p, dl, &grp)) ==
21547827cba2SAaron LI 		    ERANGE)
21557827cba2SAaron LI 		{
21567827cba2SAaron LI 			size_t nl = dl * 2;
21577827cba2SAaron LI 			if (nl < dl) {
21587827cba2SAaron LI 				logerrx("control_group: out of buffer");
21597827cba2SAaron LI 				free(p);
21607827cba2SAaron LI 				return -1;
21617827cba2SAaron LI 			}
21627827cba2SAaron LI 			dl = nl;
21637827cba2SAaron LI 			np = realloc(p, dl);
21647827cba2SAaron LI 			if (np == NULL) {
21657827cba2SAaron LI 				logerr(__func__);
21667827cba2SAaron LI 				free(p);
21677827cba2SAaron LI 				return -1;
21687827cba2SAaron LI 			}
21697827cba2SAaron LI 			p = np;
21707827cba2SAaron LI 		}
21717827cba2SAaron LI 		if (i != 0) {
21727827cba2SAaron LI 			errno = i;
21737827cba2SAaron LI 			logerr("getgrnam_r");
21747827cba2SAaron LI 			free(p);
21757827cba2SAaron LI 			return -1;
21767827cba2SAaron LI 		}
21777827cba2SAaron LI 		if (grp == NULL) {
2178d4fb1e02SRoy Marples 			if (!ctx->control_group)
21797827cba2SAaron LI 				logerrx("controlgroup: %s: not found", arg);
21807827cba2SAaron LI 			free(p);
21817827cba2SAaron LI 			return -1;
21827827cba2SAaron LI 		}
21837827cba2SAaron LI 		ctx->control_group = grp->gr_gid;
21847827cba2SAaron LI 		free(p);
21857827cba2SAaron LI #else
21867827cba2SAaron LI 		grp = getgrnam(arg);
21877827cba2SAaron LI 		if (grp == NULL) {
2188d4fb1e02SRoy Marples 			if (!ctx->control_group)
21897827cba2SAaron LI 				logerrx("controlgroup: %s: not found", arg);
21907827cba2SAaron LI 			return -1;
21917827cba2SAaron LI 		}
21927827cba2SAaron LI 		ctx->control_group = grp->gr_gid;
21937827cba2SAaron LI #endif
21947827cba2SAaron LI 		break;
21957827cba2SAaron LI 	case O_GATEWAY:
21967827cba2SAaron LI 		ifo->options |= DHCPCD_GATEWAY;
21977827cba2SAaron LI 		break;
21987827cba2SAaron LI 	case O_NOUP:
21997827cba2SAaron LI 		ifo->options &= ~DHCPCD_IF_UP;
22007827cba2SAaron LI 		break;
22017827cba2SAaron LI 	case O_SLAAC:
22027827cba2SAaron LI 		ARG_REQUIRED;
22037a0236bfSRoy Marples 		np = strwhite(arg);
22047a0236bfSRoy Marples 		if (np != NULL) {
22057a0236bfSRoy Marples 			*np++ = '\0';
22067a0236bfSRoy Marples 			np = strskipwhite(np);
22077a0236bfSRoy Marples 		}
22087827cba2SAaron LI 		if (strcmp(arg, "private") == 0 ||
22097827cba2SAaron LI 		    strcmp(arg, "stableprivate") == 0 ||
22107827cba2SAaron LI 		    strcmp(arg, "stable") == 0)
22117827cba2SAaron LI 			ifo->options |= DHCPCD_SLAACPRIVATE;
22127827cba2SAaron LI 		else
22137827cba2SAaron LI 			ifo->options &= ~DHCPCD_SLAACPRIVATE;
22147a0236bfSRoy Marples 		if (np != NULL &&
22157a0236bfSRoy Marples 		    (strcmp(np, "temp") == 0 || strcmp(np, "temporary") == 0))
22167a0236bfSRoy Marples 			ifo->options |= DHCPCD_SLAACTEMP;
22177827cba2SAaron LI 		break;
22187827cba2SAaron LI 	case O_BOOTP:
22197827cba2SAaron LI 		ifo->options |= DHCPCD_BOOTP;
22207827cba2SAaron LI 		break;
22217827cba2SAaron LI 	case O_NODELAY:
22227827cba2SAaron LI 		ifo->options &= ~DHCPCD_INITIAL_DELAY;
22237827cba2SAaron LI 		break;
22247827cba2SAaron LI 	case O_LASTLEASE_EXTEND:
22257827cba2SAaron LI 		ifo->options |= DHCPCD_LASTLEASE | DHCPCD_LASTLEASE_EXTEND;
22267827cba2SAaron LI 		break;
22277827cba2SAaron LI 	case O_INACTIVE:
22287827cba2SAaron LI 		ifo->options |= DHCPCD_INACTIVE;
22297827cba2SAaron LI 		break;
22307827cba2SAaron LI 	case O_MUDURL:
22317827cba2SAaron LI 		ARG_REQUIRED;
22327827cba2SAaron LI 		s = parse_string((char *)ifo->mudurl + 1, MUDURL_MAX_LEN, arg);
22337827cba2SAaron LI 		if (s == -1) {
22347827cba2SAaron LI 			logerr("mudurl");
22357827cba2SAaron LI 			return -1;
22367827cba2SAaron LI 		}
22377827cba2SAaron LI 		*ifo->mudurl = (uint8_t)s;
22387827cba2SAaron LI 		break;
22398d36e1dfSRoy Marples 	case O_LINK_RCVBUF:
22408d36e1dfSRoy Marples #ifndef SMALL
22418d36e1dfSRoy Marples 		ARG_REQUIRED;
22428d36e1dfSRoy Marples 		ctx->link_rcvbuf = (int)strtoi(arg, NULL, 0, 0, INT32_MAX, &e);
22438d36e1dfSRoy Marples 		if (e) {
22448d36e1dfSRoy Marples 			logerrx("failed to convert link_rcvbuf %s", arg);
22458d36e1dfSRoy Marples 			return -1;
22468d36e1dfSRoy Marples 		}
22478d36e1dfSRoy Marples #endif
22488d36e1dfSRoy Marples 		break;
2249*b2927f2bSRoy Marples 	case O_CONFIGURE:
2250*b2927f2bSRoy Marples 		ifo->options |= DHCPCD_CONFIGURE;
2251*b2927f2bSRoy Marples 		break;
2252*b2927f2bSRoy Marples 	case O_NOCONFIGURE:
2253*b2927f2bSRoy Marples 		ifo->options &= ~DHCPCD_CONFIGURE;
2254*b2927f2bSRoy Marples 		break;
22557827cba2SAaron LI 	default:
22567827cba2SAaron LI 		return 0;
22577827cba2SAaron LI 	}
22587827cba2SAaron LI 
22597827cba2SAaron LI 	return 1;
22607827cba2SAaron LI 
22617827cba2SAaron LI #ifdef ARG_REQUIRED
22627827cba2SAaron LI arg_required:
22637827cba2SAaron LI 	logerrx("option %d requires an argument", opt);
22647827cba2SAaron LI 	return -1;
22657827cba2SAaron LI #undef ARG_REQUIRED
22667827cba2SAaron LI #endif
22677827cba2SAaron LI }
22687827cba2SAaron LI 
22697827cba2SAaron LI static int
22707827cba2SAaron LI parse_config_line(struct dhcpcd_ctx *ctx, const char *ifname,
22717827cba2SAaron LI     struct if_options *ifo, const char *opt, char *line,
22727827cba2SAaron LI     struct dhcp_opt **ldop, struct dhcp_opt **edop)
22737827cba2SAaron LI {
22747827cba2SAaron LI 	unsigned int i;
22757827cba2SAaron LI 
22767827cba2SAaron LI 	for (i = 0; i < sizeof(cf_options) / sizeof(cf_options[0]); i++) {
22777827cba2SAaron LI 		if (!cf_options[i].name ||
22787827cba2SAaron LI 		    strcmp(cf_options[i].name, opt) != 0)
22797827cba2SAaron LI 			continue;
22807827cba2SAaron LI 
22817827cba2SAaron LI 		if (cf_options[i].has_arg == required_argument && !line) {
22827827cba2SAaron LI 			logerrx("option requires an argument -- %s", opt);
22837827cba2SAaron LI 			return -1;
22847827cba2SAaron LI 		}
22857827cba2SAaron LI 
22867827cba2SAaron LI 		return parse_option(ctx, ifname, ifo, cf_options[i].val, line,
22877827cba2SAaron LI 		    ldop, edop);
22887827cba2SAaron LI 	}
22897827cba2SAaron LI 
2290*b2927f2bSRoy Marples 	if (!(ctx->options & DHCPCD_PRINT_PIDFILE))
22917827cba2SAaron LI 		logerrx("unknown option: %s", opt);
22927827cba2SAaron LI 	return -1;
22937827cba2SAaron LI }
22947827cba2SAaron LI 
22957827cba2SAaron LI static void
22967827cba2SAaron LI finish_config(struct if_options *ifo)
22977827cba2SAaron LI {
22987827cba2SAaron LI 
22997827cba2SAaron LI 	/* Terminate the encapsulated options */
23007827cba2SAaron LI 	if (ifo->vendor[0] && !(ifo->options & DHCPCD_VENDORRAW)) {
23017827cba2SAaron LI 		ifo->vendor[0]++;
23027827cba2SAaron LI 		ifo->vendor[ifo->vendor[0]] = DHO_END;
23037827cba2SAaron LI 		/* We are called twice.
23047827cba2SAaron LI 		 * This should be fixed, but in the meantime, this
23057827cba2SAaron LI 		 * guard should suffice */
23067827cba2SAaron LI 		ifo->options |= DHCPCD_VENDORRAW;
23077827cba2SAaron LI 	}
23086e63cc1fSRoy Marples 
23096e63cc1fSRoy Marples 	if (!(ifo->options & DHCPCD_ARP) ||
23106e63cc1fSRoy Marples 	    ifo->options & (DHCPCD_INFORM | DHCPCD_STATIC))
23116e63cc1fSRoy Marples 		ifo->options &= ~DHCPCD_IPV4LL;
23126e63cc1fSRoy Marples 
23136e63cc1fSRoy Marples 	if (!(ifo->options & DHCPCD_IPV4))
23146e63cc1fSRoy Marples 		ifo->options &= ~(DHCPCD_DHCP | DHCPCD_IPV4LL | DHCPCD_WAITIP4);
23156e63cc1fSRoy Marples 
23166e63cc1fSRoy Marples 	if (!(ifo->options & DHCPCD_IPV6))
23176e63cc1fSRoy Marples 		ifo->options &=
23186e63cc1fSRoy Marples 		    ~(DHCPCD_IPV6RS | DHCPCD_DHCP6 | DHCPCD_WAITIP6);
23196e63cc1fSRoy Marples 
23206e63cc1fSRoy Marples 	if (!(ifo->options & DHCPCD_IPV6RS))
23216e63cc1fSRoy Marples 		ifo->options &=
23226e63cc1fSRoy Marples 		    ~(DHCPCD_IPV6RA_AUTOCONF | DHCPCD_IPV6RA_REQRDNSS);
23237827cba2SAaron LI }
23247827cba2SAaron LI 
23257827cba2SAaron LI struct if_options *
23267827cba2SAaron LI default_config(struct dhcpcd_ctx *ctx)
23277827cba2SAaron LI {
23287827cba2SAaron LI 	struct if_options *ifo;
23297827cba2SAaron LI 
23307827cba2SAaron LI 	/* Seed our default options */
23317827cba2SAaron LI 	if ((ifo = calloc(1, sizeof(*ifo))) == NULL) {
23327827cba2SAaron LI 		logerr(__func__);
23337827cba2SAaron LI 		return NULL;
23347827cba2SAaron LI 	}
23357827cba2SAaron LI 	ifo->options |= DHCPCD_IF_UP | DHCPCD_LINK | DHCPCD_INITIAL_DELAY;
23367827cba2SAaron LI 	ifo->timeout = DEFAULT_TIMEOUT;
23377827cba2SAaron LI 	ifo->reboot = DEFAULT_REBOOT;
23387827cba2SAaron LI 	ifo->metric = -1;
23397827cba2SAaron LI 	ifo->auth.options |= DHCPCD_AUTH_REQUIRE;
23408d36e1dfSRoy Marples 	rb_tree_init(&ifo->routes, &rt_compare_list_ops);
23417827cba2SAaron LI #ifdef AUTH
23427827cba2SAaron LI 	TAILQ_INIT(&ifo->auth.tokens);
23437827cba2SAaron LI #endif
23447827cba2SAaron LI 
23457827cba2SAaron LI 	/* Inherit some global defaults */
23467827cba2SAaron LI 	if (ctx->options & DHCPCD_PERSISTENT)
23477827cba2SAaron LI 		ifo->options |= DHCPCD_PERSISTENT;
23487827cba2SAaron LI 	if (ctx->options & DHCPCD_SLAACPRIVATE)
23497827cba2SAaron LI 		ifo->options |= DHCPCD_SLAACPRIVATE;
23507827cba2SAaron LI 
23517827cba2SAaron LI 	return ifo;
23527827cba2SAaron LI }
23537827cba2SAaron LI 
23547827cba2SAaron LI struct if_options *
23557827cba2SAaron LI read_config(struct dhcpcd_ctx *ctx,
23567827cba2SAaron LI     const char *ifname, const char *ssid, const char *profile)
23577827cba2SAaron LI {
23587827cba2SAaron LI 	struct if_options *ifo;
2359d4fb1e02SRoy Marples 	char buf[UDPLEN_MAX], *bp; /* 64k max config file size */
2360d4fb1e02SRoy Marples 	char *line, *option, *p;
2361d4fb1e02SRoy Marples 	ssize_t buflen;
2362d4fb1e02SRoy Marples 	size_t vlen;
23637827cba2SAaron LI 	int skip, have_profile, new_block, had_block;
23647827cba2SAaron LI #if !defined(INET) || !defined(INET6)
23657827cba2SAaron LI 	size_t i;
23667827cba2SAaron LI 	struct dhcp_opt *opt;
23677827cba2SAaron LI #endif
23687827cba2SAaron LI 	struct dhcp_opt *ldop, *edop;
23697827cba2SAaron LI 
23707827cba2SAaron LI 	/* Seed our default options */
23717827cba2SAaron LI 	if ((ifo = default_config(ctx)) == NULL)
23727827cba2SAaron LI 		return NULL;
2373b8b69544SRoy Marples 	if (default_options == 0) {
2374*b2927f2bSRoy Marples 		default_options |= DHCPCD_DAEMONISE |
2375*b2927f2bSRoy Marples 			DHCPCD_CONFIGURE | DHCPCD_GATEWAY;
23767827cba2SAaron LI #ifdef INET
2377b8b69544SRoy Marples 		skip = socket(PF_INET, SOCK_DGRAM, 0);
2378b8b69544SRoy Marples 		if (skip != -1) {
2379b8b69544SRoy Marples 			close(skip);
2380b8b69544SRoy Marples 			default_options |= DHCPCD_IPV4 | DHCPCD_ARP |
2381b8b69544SRoy Marples 			    DHCPCD_DHCP | DHCPCD_IPV4LL;
2382b8b69544SRoy Marples 		}
23837827cba2SAaron LI #endif
23847827cba2SAaron LI #ifdef INET6
2385b8b69544SRoy Marples 		skip = socket(PF_INET6, SOCK_DGRAM, 0);
2386b8b69544SRoy Marples 		if (skip != -1) {
2387b8b69544SRoy Marples 			close(skip);
2388b8b69544SRoy Marples 			default_options |= DHCPCD_IPV6 | DHCPCD_IPV6RS |
2389b8b69544SRoy Marples 			    DHCPCD_IPV6RA_AUTOCONF | DHCPCD_IPV6RA_REQRDNSS |
2390b8b69544SRoy Marples 			    DHCPCD_DHCP6;
2391b8b69544SRoy Marples 		}
23927827cba2SAaron LI #endif
2393b8b69544SRoy Marples #ifdef PLUGIN_DEV
2394b8b69544SRoy Marples 		default_options |= DHCPCD_DEV;
2395b8b69544SRoy Marples #endif
2396b8b69544SRoy Marples 	}
2397b8b69544SRoy Marples 	ifo->options |= default_options;
23987827cba2SAaron LI 
2399cc34ba0cSRoy Marples 	CLEAR_CONFIG_BLOCK(ifo);
2400cc34ba0cSRoy Marples 
2401d4fb1e02SRoy Marples 	vlen = strlcpy((char *)ifo->vendorclassid + 1, ctx->vendor,
24027827cba2SAaron LI 	    sizeof(ifo->vendorclassid) - 1);
2403d4fb1e02SRoy Marples 	ifo->vendorclassid[0] = (uint8_t)(vlen > 255 ? 0 : vlen);
24047827cba2SAaron LI 
24058d36e1dfSRoy Marples 	/* Reset route order */
24068d36e1dfSRoy Marples 	ctx->rt_order = 0;
24078d36e1dfSRoy Marples 
24087827cba2SAaron LI 	/* Parse our embedded options file */
24097827cba2SAaron LI 	if (ifname == NULL && !(ctx->options & DHCPCD_PRINT_PIDFILE)) {
24107827cba2SAaron LI 		/* Space for initial estimates */
24117827cba2SAaron LI #if defined(INET) && defined(INITDEFINES)
24127827cba2SAaron LI 		ifo->dhcp_override =
24137827cba2SAaron LI 		    calloc(INITDEFINES, sizeof(*ifo->dhcp_override));
24147827cba2SAaron LI 		if (ifo->dhcp_override == NULL)
24157827cba2SAaron LI 			logerr(__func__);
24167827cba2SAaron LI 		else
24177827cba2SAaron LI 			ifo->dhcp_override_len = INITDEFINES;
24187827cba2SAaron LI #endif
24197827cba2SAaron LI 
24207827cba2SAaron LI #if defined(INET6) && defined(INITDEFINENDS)
24217827cba2SAaron LI 		ifo->nd_override =
24227827cba2SAaron LI 		    calloc(INITDEFINENDS, sizeof(*ifo->nd_override));
24237827cba2SAaron LI 		if (ifo->nd_override == NULL)
24247827cba2SAaron LI 			logerr(__func__);
24257827cba2SAaron LI 		else
24267827cba2SAaron LI 			ifo->nd_override_len = INITDEFINENDS;
24277827cba2SAaron LI #endif
24287827cba2SAaron LI #if defined(INET6) && defined(INITDEFINE6S)
24297827cba2SAaron LI 		ifo->dhcp6_override =
24307827cba2SAaron LI 		    calloc(INITDEFINE6S, sizeof(*ifo->dhcp6_override));
24317827cba2SAaron LI 		if (ifo->dhcp6_override == NULL)
24327827cba2SAaron LI 			logerr(__func__);
24337827cba2SAaron LI 		else
24347827cba2SAaron LI 			ifo->dhcp6_override_len = INITDEFINE6S;
24357827cba2SAaron LI #endif
24367827cba2SAaron LI 
24377827cba2SAaron LI 		/* Now load our embedded config */
24387827cba2SAaron LI #ifdef EMBEDDED_CONFIG
2439d4fb1e02SRoy Marples 		buflen = dhcp_readfile(ctx, EMBEDDED_CONFIG, buf, sizeof(buf));
2440d4fb1e02SRoy Marples 		if (buflen == -1) {
2441d4fb1e02SRoy Marples 			logerr("%s: %s", __func__, EMBEDDED_CONFIG);
2442d4fb1e02SRoy Marples 			return ifo;
2443d4fb1e02SRoy Marples 		}
2444d4fb1e02SRoy Marples 		if (buf[buflen - 1] != '\0') {
24457f8103cdSRoy Marples 			if ((size_t)buflen < sizeof(buf) - 1)
24467f8103cdSRoy Marples 				buflen++;
2447d4fb1e02SRoy Marples 			buf[buflen - 1] = '\0';
2448d4fb1e02SRoy Marples 		}
24497827cba2SAaron LI #else
2450d4fb1e02SRoy Marples 		buflen = (ssize_t)strlcpy(buf, dhcpcd_embedded_conf,
2451d4fb1e02SRoy Marples 		    sizeof(buf));
2452d4fb1e02SRoy Marples 		if ((size_t)buflen >= sizeof(buf)) {
2453d4fb1e02SRoy Marples 			logerrx("%s: embedded config too big", __func__);
2454d4fb1e02SRoy Marples 			return ifo;
24557827cba2SAaron LI 		}
2456d4fb1e02SRoy Marples 		/* Our embedded config is NULL terminated */
24577827cba2SAaron LI #endif
2458d4fb1e02SRoy Marples 		bp = buf;
2459d4fb1e02SRoy Marples 		while ((line = get_line(&bp, &buflen)) != NULL) {
24607827cba2SAaron LI 			option = strsep(&line, " \t");
24617827cba2SAaron LI 			if (line)
24627827cba2SAaron LI 				line = strskipwhite(line);
24637827cba2SAaron LI 			/* Trim trailing whitespace */
24647827cba2SAaron LI 			if (line) {
24657827cba2SAaron LI 				p = line + strlen(line) - 1;
24667827cba2SAaron LI 				while (p != line &&
24677827cba2SAaron LI 				    (*p == ' ' || *p == '\t') &&
24687827cba2SAaron LI 				    *(p - 1) != '\\')
24697827cba2SAaron LI 					*p-- = '\0';
24707827cba2SAaron LI 			}
24717827cba2SAaron LI 			parse_config_line(ctx, NULL, ifo, option, line,
24727827cba2SAaron LI 			    &ldop, &edop);
24737827cba2SAaron LI 		}
24747827cba2SAaron LI 
24757827cba2SAaron LI #ifdef INET
24767827cba2SAaron LI 		ctx->dhcp_opts = ifo->dhcp_override;
24777827cba2SAaron LI 		ctx->dhcp_opts_len = ifo->dhcp_override_len;
24787827cba2SAaron LI #else
24797827cba2SAaron LI 		for (i = 0, opt = ifo->dhcp_override;
24807827cba2SAaron LI 		    i < ifo->dhcp_override_len;
24817827cba2SAaron LI 		    i++, opt++)
24827827cba2SAaron LI 			free_dhcp_opt_embenc(opt);
24837827cba2SAaron LI 		free(ifo->dhcp_override);
24847827cba2SAaron LI #endif
24857827cba2SAaron LI 		ifo->dhcp_override = NULL;
24867827cba2SAaron LI 		ifo->dhcp_override_len = 0;
24877827cba2SAaron LI 
24887827cba2SAaron LI #ifdef INET6
24897827cba2SAaron LI 		ctx->nd_opts = ifo->nd_override;
24907827cba2SAaron LI 		ctx->nd_opts_len = ifo->nd_override_len;
24918d36e1dfSRoy Marples #ifdef DHCP6
24927827cba2SAaron LI 		ctx->dhcp6_opts = ifo->dhcp6_override;
24937827cba2SAaron LI 		ctx->dhcp6_opts_len = ifo->dhcp6_override_len;
24948d36e1dfSRoy Marples #endif
24957827cba2SAaron LI #else
24967827cba2SAaron LI 		for (i = 0, opt = ifo->nd_override;
24977827cba2SAaron LI 		    i < ifo->nd_override_len;
24987827cba2SAaron LI 		    i++, opt++)
24997827cba2SAaron LI 			free_dhcp_opt_embenc(opt);
25007827cba2SAaron LI 		free(ifo->nd_override);
25017827cba2SAaron LI 		for (i = 0, opt = ifo->dhcp6_override;
25027827cba2SAaron LI 		    i < ifo->dhcp6_override_len;
25037827cba2SAaron LI 		    i++, opt++)
25047827cba2SAaron LI 			free_dhcp_opt_embenc(opt);
25057827cba2SAaron LI 		free(ifo->dhcp6_override);
25067827cba2SAaron LI #endif
25077827cba2SAaron LI 		ifo->nd_override = NULL;
25087827cba2SAaron LI 		ifo->nd_override_len = 0;
25097827cba2SAaron LI 		ifo->dhcp6_override = NULL;
25107827cba2SAaron LI 		ifo->dhcp6_override_len = 0;
25117827cba2SAaron LI 
25127827cba2SAaron LI 		ctx->vivso = ifo->vivso_override;
25137827cba2SAaron LI 		ctx->vivso_len = ifo->vivso_override_len;
25147827cba2SAaron LI 		ifo->vivso_override = NULL;
25157827cba2SAaron LI 		ifo->vivso_override_len = 0;
25167827cba2SAaron LI 	}
25177827cba2SAaron LI 
25187827cba2SAaron LI 	/* Parse our options file */
2519d4fb1e02SRoy Marples 	buflen = dhcp_readfile(ctx, ctx->cffile, buf, sizeof(buf));
2520d4fb1e02SRoy Marples 	if (buflen == -1) {
25217827cba2SAaron LI 		/* dhcpcd can continue without it, but no DNS options
25227827cba2SAaron LI 		 * would be requested ... */
2523d4fb1e02SRoy Marples 		logerr("%s: %s", __func__, ctx->cffile);
25247827cba2SAaron LI 		return ifo;
25257827cba2SAaron LI 	}
2526d4fb1e02SRoy Marples 	if (buf[buflen - 1] != '\0') {
2527d4fb1e02SRoy Marples 		if ((size_t)buflen < sizeof(buf) - 1)
2528d4fb1e02SRoy Marples 			buflen++;
2529d4fb1e02SRoy Marples 		buf[buflen - 1] = '\0';
2530d4fb1e02SRoy Marples 	}
2531d4fb1e02SRoy Marples 	dhcp_filemtime(ctx, ctx->cffile, &ifo->mtime);
25327827cba2SAaron LI 
25337827cba2SAaron LI 	ldop = edop = NULL;
25347827cba2SAaron LI 	skip = have_profile = new_block = 0;
25357827cba2SAaron LI 	had_block = ifname == NULL ? 1 : 0;
2536d4fb1e02SRoy Marples 	bp = buf;
2537d4fb1e02SRoy Marples 	while ((line = get_line(&bp, &buflen)) != NULL) {
25387827cba2SAaron LI 		option = strsep(&line, " \t");
25397827cba2SAaron LI 		if (line)
25407827cba2SAaron LI 			line = strskipwhite(line);
25417827cba2SAaron LI 		/* Trim trailing whitespace */
25427827cba2SAaron LI 		if (line) {
25437827cba2SAaron LI 			p = line + strlen(line) - 1;
25447827cba2SAaron LI 			while (p != line &&
25457827cba2SAaron LI 			    (*p == ' ' || *p == '\t') &&
25467827cba2SAaron LI 			    *(p - 1) != '\\')
25477827cba2SAaron LI 				*p-- = '\0';
25487827cba2SAaron LI 		}
25497827cba2SAaron LI 		if (skip == 0 && new_block) {
25507827cba2SAaron LI 			had_block = 1;
25517827cba2SAaron LI 			new_block = 0;
25527827cba2SAaron LI 			ifo->options &= ~DHCPCD_WAITOPTS;
2553cc34ba0cSRoy Marples 			SET_CONFIG_BLOCK(ifo);
25547827cba2SAaron LI 		}
2555cc34ba0cSRoy Marples 
25567827cba2SAaron LI 		/* Start of an interface block, skip if not ours */
25577827cba2SAaron LI 		if (strcmp(option, "interface") == 0) {
25587827cba2SAaron LI 			char **n;
25597827cba2SAaron LI 
25607827cba2SAaron LI 			new_block = 1;
25617827cba2SAaron LI 			if (line == NULL) {
25627827cba2SAaron LI 				/* No interface given */
25637827cba2SAaron LI 				skip = 1;
25647827cba2SAaron LI 				continue;
25657827cba2SAaron LI 			}
25667827cba2SAaron LI 			if (ifname && strcmp(line, ifname) == 0)
25677827cba2SAaron LI 				skip = 0;
25687827cba2SAaron LI 			else
25697827cba2SAaron LI 				skip = 1;
25707827cba2SAaron LI 			if (ifname)
25717827cba2SAaron LI 				continue;
25727827cba2SAaron LI 
25737827cba2SAaron LI 			n = reallocarray(ctx->ifcv,
25747827cba2SAaron LI 			    (size_t)ctx->ifcc + 1, sizeof(char *));
25757827cba2SAaron LI 			if (n == NULL) {
25767827cba2SAaron LI 				logerr(__func__);
25777827cba2SAaron LI 				continue;
25787827cba2SAaron LI 			}
25797827cba2SAaron LI 			ctx->ifcv = n;
25807827cba2SAaron LI 			ctx->ifcv[ctx->ifcc] = strdup(line);
25817827cba2SAaron LI 			if (ctx->ifcv[ctx->ifcc] == NULL) {
25827827cba2SAaron LI 				logerr(__func__);
25837827cba2SAaron LI 				continue;
25847827cba2SAaron LI 			}
25857827cba2SAaron LI 			ctx->ifcc++;
25867827cba2SAaron LI 			continue;
25877827cba2SAaron LI 		}
25887827cba2SAaron LI 		/* Start of an ssid block, skip if not ours */
25897827cba2SAaron LI 		if (strcmp(option, "ssid") == 0) {
25907827cba2SAaron LI 			new_block = 1;
25917827cba2SAaron LI 			if (ssid && line && strcmp(line, ssid) == 0)
25927827cba2SAaron LI 				skip = 0;
25937827cba2SAaron LI 			else
25947827cba2SAaron LI 				skip = 1;
25957827cba2SAaron LI 			continue;
25967827cba2SAaron LI 		}
25977827cba2SAaron LI 		/* Start of a profile block, skip if not ours */
25987827cba2SAaron LI 		if (strcmp(option, "profile") == 0) {
25997827cba2SAaron LI 			new_block = 1;
26007827cba2SAaron LI 			if (profile && line && strcmp(line, profile) == 0) {
26017827cba2SAaron LI 				skip = 0;
26027827cba2SAaron LI 				have_profile = 1;
26037827cba2SAaron LI 			} else
26047827cba2SAaron LI 				skip = 1;
26057827cba2SAaron LI 			continue;
26067827cba2SAaron LI 		}
26077827cba2SAaron LI 		/* Skip arping if we have selected a profile but not parsing
26087827cba2SAaron LI 		 * one. */
26097827cba2SAaron LI 		if (profile && !have_profile && strcmp(option, "arping") == 0)
26107827cba2SAaron LI 			continue;
26117827cba2SAaron LI 		if (skip)
26127827cba2SAaron LI 			continue;
2613d4fb1e02SRoy Marples 
26147827cba2SAaron LI 		parse_config_line(ctx, ifname, ifo, option, line, &ldop, &edop);
26157827cba2SAaron LI 	}
26167827cba2SAaron LI 
26177827cba2SAaron LI 	if (profile && !have_profile) {
26187827cba2SAaron LI 		free_options(ctx, ifo);
26197827cba2SAaron LI 		errno = ENOENT;
26207827cba2SAaron LI 		return NULL;
26217827cba2SAaron LI 	}
26227827cba2SAaron LI 
26237827cba2SAaron LI 	if (!had_block)
26247827cba2SAaron LI 		ifo->options &= ~DHCPCD_WAITOPTS;
2625cc34ba0cSRoy Marples 	CLEAR_CONFIG_BLOCK(ifo);
26267827cba2SAaron LI 	finish_config(ifo);
26277827cba2SAaron LI 	return ifo;
26287827cba2SAaron LI }
26297827cba2SAaron LI 
26307827cba2SAaron LI int
26317827cba2SAaron LI add_options(struct dhcpcd_ctx *ctx, const char *ifname,
26327827cba2SAaron LI     struct if_options *ifo, int argc, char **argv)
26337827cba2SAaron LI {
26347827cba2SAaron LI 	int oi, opt, r;
26357827cba2SAaron LI 	unsigned long long wait_opts;
26367827cba2SAaron LI 
26377827cba2SAaron LI 	if (argc == 0)
26387827cba2SAaron LI 		return 1;
26397827cba2SAaron LI 
26407827cba2SAaron LI 	optind = 0;
26417827cba2SAaron LI 	r = 1;
26427827cba2SAaron LI 	/* Don't apply the command line wait options to each interface,
26437827cba2SAaron LI 	 * only use the dhcpcd.conf entry for that. */
26447827cba2SAaron LI 	if (ifname != NULL)
26457827cba2SAaron LI 		wait_opts = ifo->options & DHCPCD_WAITOPTS;
26467827cba2SAaron LI 	while ((opt = getopt_long(argc, argv,
26477827cba2SAaron LI 	    ctx->options & DHCPCD_PRINT_PIDFILE ? NOERR_IF_OPTS : IF_OPTS,
26487827cba2SAaron LI 	    cf_options, &oi)) != -1)
26497827cba2SAaron LI 	{
26507827cba2SAaron LI 		r = parse_option(ctx, ifname, ifo, opt, optarg, NULL, NULL);
26517827cba2SAaron LI 		if (r != 1)
26527827cba2SAaron LI 			break;
26537827cba2SAaron LI 	}
26547827cba2SAaron LI 	if (ifname != NULL) {
26557827cba2SAaron LI 		ifo->options &= ~DHCPCD_WAITOPTS;
26567827cba2SAaron LI 		ifo->options |= wait_opts;
26577827cba2SAaron LI 	}
26587827cba2SAaron LI 
26597827cba2SAaron LI 	finish_config(ifo);
26607827cba2SAaron LI 	return r;
26617827cba2SAaron LI }
26627827cba2SAaron LI 
26637827cba2SAaron LI void
26647827cba2SAaron LI free_options(struct dhcpcd_ctx *ctx, struct if_options *ifo)
26657827cba2SAaron LI {
26667827cba2SAaron LI 	size_t i;
26678d36e1dfSRoy Marples #ifdef RT_FREE_ROUTE_TABLE
26688d36e1dfSRoy Marples 	struct interface *ifp;
26698d36e1dfSRoy Marples 	struct rt *rt;
26708d36e1dfSRoy Marples #endif
26717827cba2SAaron LI 	struct dhcp_opt *opt;
26727827cba2SAaron LI 	struct vivco *vo;
26737827cba2SAaron LI #ifdef AUTH
26747827cba2SAaron LI 	struct token *token;
26757827cba2SAaron LI #endif
26767827cba2SAaron LI 
26778d36e1dfSRoy Marples 	if (ifo == NULL)
26788d36e1dfSRoy Marples 		return;
26798d36e1dfSRoy Marples 
26807827cba2SAaron LI 	if (ifo->environ) {
26817827cba2SAaron LI 		i = 0;
26827827cba2SAaron LI 		while (ifo->environ[i])
26837827cba2SAaron LI 			free(ifo->environ[i++]);
26847827cba2SAaron LI 		free(ifo->environ);
26857827cba2SAaron LI 	}
26867827cba2SAaron LI 	if (ifo->config) {
26877827cba2SAaron LI 		i = 0;
26887827cba2SAaron LI 		while (ifo->config[i])
26897827cba2SAaron LI 			free(ifo->config[i++]);
26907827cba2SAaron LI 		free(ifo->config);
26917827cba2SAaron LI 	}
26928d36e1dfSRoy Marples 
26938d36e1dfSRoy Marples #ifdef RT_FREE_ROUTE_TABLE
26948d36e1dfSRoy Marples 	/* Stupidly, we don't know the interface when creating the options.
26958d36e1dfSRoy Marples 	 * As such, make sure each route has one so they can goto the
26968d36e1dfSRoy Marples 	 * free list. */
26978d36e1dfSRoy Marples 	ifp = ctx->ifaces != NULL ? TAILQ_FIRST(ctx->ifaces) : NULL;
26988d36e1dfSRoy Marples 	if (ifp != NULL) {
26998d36e1dfSRoy Marples 		RB_TREE_FOREACH(rt, &ifo->routes) {
27008d36e1dfSRoy Marples 			if (rt->rt_ifp == NULL)
27018d36e1dfSRoy Marples 				rt->rt_ifp = ifp;
27028d36e1dfSRoy Marples 		}
27038d36e1dfSRoy Marples 	}
27048d36e1dfSRoy Marples #endif
27057827cba2SAaron LI 	rt_headclear0(ctx, &ifo->routes, AF_UNSPEC);
27068d36e1dfSRoy Marples 
27077827cba2SAaron LI 	free(ifo->arping);
27087827cba2SAaron LI 	free(ifo->blacklist);
27097827cba2SAaron LI 	free(ifo->fallback);
27107827cba2SAaron LI 
27117827cba2SAaron LI 	for (opt = ifo->dhcp_override;
27127827cba2SAaron LI 	    ifo->dhcp_override_len > 0;
27137827cba2SAaron LI 	    opt++, ifo->dhcp_override_len--)
27147827cba2SAaron LI 		free_dhcp_opt_embenc(opt);
27157827cba2SAaron LI 	free(ifo->dhcp_override);
27167827cba2SAaron LI 	for (opt = ifo->nd_override;
27177827cba2SAaron LI 	    ifo->nd_override_len > 0;
27187827cba2SAaron LI 	    opt++, ifo->nd_override_len--)
27197827cba2SAaron LI 		free_dhcp_opt_embenc(opt);
27207827cba2SAaron LI 	free(ifo->nd_override);
27217827cba2SAaron LI 	for (opt = ifo->dhcp6_override;
27227827cba2SAaron LI 	    ifo->dhcp6_override_len > 0;
27237827cba2SAaron LI 	    opt++, ifo->dhcp6_override_len--)
27247827cba2SAaron LI 		free_dhcp_opt_embenc(opt);
27257827cba2SAaron LI 	free(ifo->dhcp6_override);
27267827cba2SAaron LI 	for (vo = ifo->vivco;
27277827cba2SAaron LI 	    ifo->vivco_len > 0;
27287827cba2SAaron LI 	    vo++, ifo->vivco_len--)
27297827cba2SAaron LI 		free(vo->data);
27307827cba2SAaron LI 	free(ifo->vivco);
27317827cba2SAaron LI 	for (opt = ifo->vivso_override;
27327827cba2SAaron LI 	    ifo->vivso_override_len > 0;
27337827cba2SAaron LI 	    opt++, ifo->vivso_override_len--)
27347827cba2SAaron LI 		free_dhcp_opt_embenc(opt);
27357827cba2SAaron LI 	free(ifo->vivso_override);
27367827cba2SAaron LI 
27377827cba2SAaron LI #if defined(INET6) && !defined(SMALL)
27387827cba2SAaron LI 	for (; ifo->ia_len > 0; ifo->ia_len--)
27397827cba2SAaron LI 		free(ifo->ia[ifo->ia_len - 1].sla);
27407827cba2SAaron LI #endif
27417827cba2SAaron LI 	free(ifo->ia);
27427827cba2SAaron LI 
27437827cba2SAaron LI #ifdef AUTH
27447827cba2SAaron LI 	while ((token = TAILQ_FIRST(&ifo->auth.tokens))) {
27457827cba2SAaron LI 		TAILQ_REMOVE(&ifo->auth.tokens, token, next);
27467827cba2SAaron LI 		if (token->realm_len)
27477827cba2SAaron LI 			free(token->realm);
27487827cba2SAaron LI 		free(token->key);
27497827cba2SAaron LI 		free(token);
27507827cba2SAaron LI 	}
27517827cba2SAaron LI #endif
27527827cba2SAaron LI 	free(ifo);
27537827cba2SAaron LI }
2754