]> git.ipfire.org Git - thirdparty/dhcpcd.git/blame - if-options.c
Preserve vendor encapsulated options
[thirdparty/dhcpcd.git] / if-options.c
CommitLineData
8cc47ba2 1/*
fd05b7dc 2 * dhcpcd - DHCP client daemon
aae24feb 3 * Copyright (c) 2006-2013 Roy Marples <roy@marples.name>
fd05b7dc
RM
4 * All rights reserved
5
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 */
27
0d0c5f66 28#include <sys/param.h>
fd05b7dc
RM
29#include <sys/types.h>
30
31#include <arpa/inet.h>
32
33#include <ctype.h>
34#include <errno.h>
35#include <getopt.h>
0d0c5f66 36#include <limits.h>
fd05b7dc
RM
37#include <paths.h>
38#include <stdio.h>
39#include <stdlib.h>
40#include <string.h>
765fbf7d 41#include <syslog.h>
fd05b7dc
RM
42#include <unistd.h>
43#include <time.h>
44
fd05b7dc 45#include "config.h"
9f7780b0 46#include "common.h"
d7555c12
RM
47#include "dhcp.h"
48#include "dhcp6.h"
fd05b7dc 49#include "if-options.h"
e88c525f 50#include "ipv4.h"
793c4286 51#include "platform.h"
fd05b7dc 52
eebe9a18
RM
53unsigned long long options = 0;
54
4ca7460f
RM
55/* These options only make sense in the config file, so don't use any
56 valid short options for them */
eebe9a18
RM
57#define O_BASE MAX('z', 'Z') + 1
58#define O_ARPING O_BASE + 1
59#define O_FALLBACK O_BASE + 2
60#define O_DESTINATION O_BASE + 3
61#define O_IPV6RS O_BASE + 4
00ababe4
RM
62#define O_NOIPV6RS O_BASE + 5
63#define O_IPV6RA_FORK O_BASE + 6
eebe9a18
RM
64#define O_IPV6RA_OWN O_BASE + 7
65#define O_IPV6RA_OWN_D O_BASE + 8
7dab081f 66#define O_NOALIAS O_BASE + 9
00ababe4
RM
67#define O_IA_NA O_BASE + 10
68#define O_IA_TA O_BASE + 11
69#define O_IA_PD O_BASE + 12
d6a18654 70#define O_HOSTNAME_SHORT O_BASE + 13
413652c1
RM
71#define O_DEV O_BASE + 14
72#define O_NODEV O_BASE + 15
bb8051bf
RM
73#define O_NOIPV4 O_BASE + 16
74#define O_NOIPV6 O_BASE + 17
413652c1
RM
75
76char *dev_load;
4ca7460f 77
fd05b7dc 78const struct option cf_options[] = {
ba97e494
RM
79 {"background", no_argument, NULL, 'b'},
80 {"script", required_argument, NULL, 'c'},
81 {"debug", no_argument, NULL, 'd'},
6bfd88f1 82 {"env", required_argument, NULL, 'e'},
ba97e494 83 {"config", required_argument, NULL, 'f'},
6bfd88f1 84 {"reconfigure", no_argument, NULL, 'g'},
ba97e494
RM
85 {"hostname", optional_argument, NULL, 'h'},
86 {"vendorclassid", optional_argument, NULL, 'i'},
87 {"release", no_argument, NULL, 'k'},
88 {"leasetime", required_argument, NULL, 'l'},
89 {"metric", required_argument, NULL, 'm'},
90 {"rebind", no_argument, NULL, 'n'},
91 {"option", required_argument, NULL, 'o'},
92 {"persistent", no_argument, NULL, 'p'},
93 {"quiet", no_argument, NULL, 'q'},
94 {"request", optional_argument, NULL, 'r'},
95 {"inform", optional_argument, NULL, 's'},
96 {"timeout", required_argument, NULL, 't'},
97 {"userclass", required_argument, NULL, 'u'},
98 {"vendor", required_argument, NULL, 'v'},
7013b073 99 {"waitip", optional_argument, NULL, 'w'},
ba97e494 100 {"exit", no_argument, NULL, 'x'},
d3088c74 101 {"allowinterfaces", required_argument, NULL, 'z'},
a2a9a498 102 {"reboot", required_argument, NULL, 'y'},
ba97e494
RM
103 {"noarp", no_argument, NULL, 'A'},
104 {"nobackground", no_argument, NULL, 'B'},
105 {"nohook", required_argument, NULL, 'C'},
106 {"duid", no_argument, NULL, 'D'},
107 {"lastlease", no_argument, NULL, 'E'},
108 {"fqdn", optional_argument, NULL, 'F'},
109 {"nogateway", no_argument, NULL, 'G'},
00ababe4 110 {"xidhwaddr", no_argument, NULL, 'H'},
ba97e494 111 {"clientid", optional_argument, NULL, 'I'},
900b3da4 112 {"broadcast", no_argument, NULL, 'J'},
ba97e494
RM
113 {"nolink", no_argument, NULL, 'K'},
114 {"noipv4ll", no_argument, NULL, 'L'},
115 {"nooption", optional_argument, NULL, 'O'},
116 {"require", required_argument, NULL, 'Q'},
91a44b91 117 {"static", required_argument, NULL, 'S'},
ba97e494 118 {"test", no_argument, NULL, 'T'},
dc60cba4 119 {"dumplease", no_argument, NULL, 'U'},
ba97e494 120 {"variables", no_argument, NULL, 'V'},
bf80d526 121 {"whitelist", required_argument, NULL, 'W'},
ba97e494 122 {"blacklist", required_argument, NULL, 'X'},
d3088c74 123 {"denyinterfaces", required_argument, NULL, 'Z'},
4ca7460f 124 {"arping", required_argument, NULL, O_ARPING},
41c60e02 125 {"destination", required_argument, NULL, O_DESTINATION},
ff021b0b 126 {"fallback", required_argument, NULL, O_FALLBACK},
eebe9a18 127 {"ipv6rs", no_argument, NULL, O_IPV6RS},
91cd7324 128 {"noipv6rs", no_argument, NULL, O_NOIPV6RS},
eebe9a18
RM
129 {"ipv6ra_fork", no_argument, NULL, O_IPV6RA_FORK},
130 {"ipv6ra_own", no_argument, NULL, O_IPV6RA_OWN},
131 {"ipv6ra_own_default", no_argument, NULL, O_IPV6RA_OWN_D},
d7555c12
RM
132 {"ipv4only", no_argument, NULL, '4'},
133 {"ipv6only", no_argument, NULL, '6'},
bb8051bf
RM
134 {"noipv4", no_argument, NULL, O_NOIPV4},
135 {"noipv6", no_argument, NULL, O_NOIPV6},
7dab081f 136 {"noalias", no_argument, NULL, O_NOALIAS},
00ababe4
RM
137 {"ia_na", no_argument, NULL, O_IA_NA},
138 {"ia_ta", no_argument, NULL, O_IA_TA},
139 {"ia_pd", no_argument, NULL, O_IA_PD},
d6a18654 140 {"hostname_short", no_argument, NULL, O_HOSTNAME_SHORT},
413652c1
RM
141 {"dev", required_argument, NULL, O_DEV},
142 {"nodev", no_argument, NULL, O_NODEV},
ba97e494 143 {NULL, 0, NULL, '\0'}
fd05b7dc
RM
144};
145
146static int
147atoint(const char *s)
148{
149 char *t;
150 long n;
151
152 errno = 0;
153 n = strtol(s, &t, 0);
154 if ((errno != 0 && n == 0) || s == t ||
155 (errno == ERANGE && (n == LONG_MAX || n == LONG_MIN)))
156 {
367f7b11
RM
157 if (errno == 0)
158 errno = EINVAL;
765fbf7d 159 syslog(LOG_ERR, "`%s' out of range", s);
fd05b7dc
RM
160 return -1;
161 }
162
163 return (int)n;
164}
165
00ababe4 166static char *
fd05b7dc
RM
167add_environ(struct if_options *ifo, const char *value, int uniq)
168{
169 char **newlist;
170 char **lst = ifo->environ;
171 size_t i = 0, l, lv;
fa245a4d 172 char *match = NULL, *p, *n;
fd05b7dc 173
78369646
RM
174 match = strdup(value);
175 if (match == NULL) {
176 syslog(LOG_ERR, "%s: %m", __func__);
177 return NULL;
178 }
fd05b7dc
RM
179 p = strchr(match, '=');
180 if (p)
181 *p++ = '\0';
182 l = strlen(match);
183
184 while (lst && lst[i]) {
185 if (match && strncmp(lst[i], match, l) == 0) {
186 if (uniq) {
78369646
RM
187 n = strdup(value);
188 if (n == NULL) {
189 syslog(LOG_ERR, "%s: %m", __func__);
190 return NULL;
191 }
fd05b7dc 192 free(lst[i]);
78369646 193 lst[i] = n;
fd05b7dc
RM
194 } else {
195 /* Append a space and the value to it */
196 l = strlen(lst[i]);
197 lv = strlen(p);
fa245a4d
RM
198 n = realloc(lst[i], l + lv + 2);
199 if (n == NULL) {
200 syslog(LOG_ERR, "%s: %m", __func__);
201 return NULL;
202 }
203 lst[i] = n;
fd05b7dc
RM
204 lst[i][l] = ' ';
205 memcpy(lst[i] + l + 1, p, lv);
206 lst[i][l + lv + 1] = '\0';
207 }
208 free(match);
209 return lst[i];
210 }
211 i++;
212 }
213
78369646
RM
214 n = strdup(value);
215 if (n == NULL) {
216 syslog(LOG_ERR, "%s: %m", __func__);
217 return NULL;
218 }
fa245a4d
RM
219 newlist = realloc(lst, sizeof(char *) * (i + 2));
220 if (newlist == NULL) {
221 syslog(LOG_ERR, "%s: %m", __func__);
222 return NULL;
223 }
78369646 224 newlist[i] = n;
fd05b7dc
RM
225 newlist[i + 1] = NULL;
226 ifo->environ = newlist;
227 free(match);
228 return newlist[i];
229}
230
231#define parse_string(buf, len, arg) parse_string_hwaddr(buf, len, arg, 0)
232static ssize_t
233parse_string_hwaddr(char *sbuf, ssize_t slen, const char *str, int clid)
234{
235 ssize_t l;
236 const char *p;
237 int i, punt_last = 0;
238 char c[4];
239
240 /* If surrounded by quotes then it's a string */
241 if (*str == '"') {
242 str++;
243 l = strlen(str);
244 p = str + l - 1;
245 if (*p == '"')
246 punt_last = 1;
247 } else {
248 l = hwaddr_aton(NULL, str);
249 if (l > 1) {
250 if (l > slen) {
251 errno = ENOBUFS;
252 return -1;
253 }
254 hwaddr_aton((uint8_t *)sbuf, str);
255 return l;
256 }
257 }
258
259 /* Process escapes */
260 l = 0;
261 /* If processing a string on the clientid, first byte should be
262 * 0 to indicate a non hardware type */
9c9ad2f1 263 if (clid && *str) {
fd05b7dc
RM
264 *sbuf++ = 0;
265 l++;
266 }
267 c[3] = '\0';
268 while (*str) {
269 if (++l > slen) {
270 errno = ENOBUFS;
271 return -1;
272 }
273 if (*str == '\\') {
274 str++;
857576b4 275 switch(*str) {
fd05b7dc
RM
276 case '\0':
277 break;
278 case 'b':
279 *sbuf++ = '\b';
857576b4 280 str++;
fd05b7dc
RM
281 break;
282 case 'n':
283 *sbuf++ = '\n';
857576b4 284 str++;
fd05b7dc
RM
285 break;
286 case 'r':
287 *sbuf++ = '\r';
857576b4 288 str++;
fd05b7dc
RM
289 break;
290 case 't':
291 *sbuf++ = '\t';
857576b4 292 str++;
fd05b7dc
RM
293 break;
294 case 'x':
295 /* Grab a hex code */
296 c[1] = '\0';
297 for (i = 0; i < 2; i++) {
298 if (isxdigit((unsigned char)*str) == 0)
299 break;
300 c[i] = *str++;
301 }
302 if (c[1] != '\0') {
303 c[2] = '\0';
304 *sbuf++ = strtol(c, NULL, 16);
305 } else
306 l--;
307 break;
308 case '0':
309 /* Grab an octal code */
310 c[2] = '\0';
311 for (i = 0; i < 3; i++) {
312 if (*str < '0' || *str > '7')
313 break;
314 c[i] = *str++;
315 }
316 if (c[2] != '\0') {
317 i = strtol(c, NULL, 8);
318 if (i > 255)
319 i = 255;
320 *sbuf ++= i;
321 } else
322 l--;
323 break;
324 default:
325 *sbuf++ = *str++;
326 }
327 } else
328 *sbuf++ = *str++;
329 }
c35f63c8 330 if (punt_last) {
fd05b7dc 331 *--sbuf = '\0';
c35f63c8
RM
332 l--;
333 }
fd05b7dc
RM
334 return l;
335}
336
ba97e494
RM
337static char **
338splitv(int *argc, char **argv, const char *arg)
339{
fa245a4d 340 char **n, **v = argv;
78369646 341 char *o = strdup(arg), *p, *t, *nt;
ba97e494 342
78369646
RM
343 if (o == NULL) {
344 syslog(LOG_ERR, "%s: %m", __func__);
345 return v;
346 }
ba97e494
RM
347 p = o;
348 while ((t = strsep(&p, ", "))) {
fa245a4d
RM
349 nt = strdup(t);
350 if (nt == NULL) {
351 syslog(LOG_ERR, "%s: %m", __func__);
352 return NULL;
353 }
ba97e494 354 (*argc)++;
fa245a4d
RM
355 n = realloc(v, sizeof(char *) * ((*argc)));
356 if (n == NULL) {
357 syslog(LOG_ERR, "%s: %m", __func__);
358 return NULL;
359 }
360 v = n;
361 v[(*argc) - 1] = nt;
ba97e494
RM
362 }
363 free(o);
00ababe4 364 return v;
ba97e494
RM
365}
366
1f4c1525 367#ifdef INET
91a44b91
RM
368static int
369parse_addr(struct in_addr *addr, struct in_addr *net, const char *arg)
370{
371 char *p;
372 int i;
373
2f7cb97c
RM
374 if (arg == NULL || *arg == '\0') {
375 if (addr != NULL)
376 addr->s_addr = 0;
377 if (net != NULL)
378 net->s_addr = 0;
91a44b91 379 return 0;
2f7cb97c 380 }
91a44b91
RM
381 if ((p = strchr(arg, '/')) != NULL) {
382 *p++ = '\0';
383 if (net != NULL &&
384 (sscanf(p, "%d", &i) != 1 ||
eab2229c 385 inet_cidrtoaddr(i, net) != 0))
91a44b91
RM
386 {
387 syslog(LOG_ERR, "`%s' is not a valid CIDR", p);
388 return -1;
389 }
00ababe4 390 }
2f7cb97c 391
91a44b91
RM
392 if (addr != NULL && inet_aton(arg, addr) == 0) {
393 syslog(LOG_ERR, "`%s' is not a valid IP address", arg);
394 return -1;
395 }
2f7cb97c 396 if (p != NULL)
e095a6eb 397 *--p = '/';
2f7cb97c 398 else if (net != NULL)
e88c525f 399 net->s_addr = ipv4_getnetmask(addr->s_addr);
91a44b91 400 return 0;
1f4c1525 401}
aae24feb 402#else
1f4c1525
RM
403static int
404parse_addr(__unused struct in_addr *addr, __unused struct in_addr *net,
405 __unused const char *arg)
406{
407
aae24feb
RM
408 syslog(LOG_ERR, "No IPv4 support");
409 return -1;
91a44b91 410}
1f4c1525 411#endif
91a44b91 412
00ababe4 413static const char *
d7555c12
RM
414set_option_space(const char *arg, const struct dhcp_opt **d,
415 struct if_options *ifo,
416 uint8_t *request[], uint8_t *require[], uint8_t *no[])
417{
418
aae24feb 419#ifdef INET6
d7555c12
RM
420 if (strncmp(arg, "dhcp6_", strlen("dhcp6_")) == 0) {
421 *d = dhcp6_opts;
422 *request = ifo->requestmask6;
423 *require = ifo->requiremask6;
424 *no = ifo->nomask6;
425 return arg + strlen("dhcp6_");
426 }
aae24feb
RM
427#endif
428
429#ifdef INET
d7555c12 430 *d = dhcp_opts;
aae24feb
RM
431#else
432 *d = NULL;
433#endif
d7555c12
RM
434 *request = ifo->requestmask;
435 *require = ifo->requiremask;
436 *no = ifo->nomask;
437 return arg;
438}
439
fd05b7dc
RM
440static int
441parse_option(struct if_options *ifo, int opt, const char *arg)
442{
443 int i;
fa245a4d 444 char *p = NULL, *fp, *np, **nconf;
fd05b7dc 445 ssize_t s;
e095a6eb 446 struct in_addr addr, addr2;
fa245a4d 447 in_addr_t *naddr;
91a44b91 448 struct rt *rt;
a2258eb3 449 const struct dhcp_opt *d;
d7555c12 450 uint8_t *request, *require, *no;
1f4c1525
RM
451#ifdef INET6
452 long l;
367f7b11 453 uint32_t u32;
1f4c1525 454 size_t sl;
00ababe4
RM
455 struct if_iaid *iaid;
456 uint8_t _iaid[4];
5985c4e2 457 struct if_sla *sla, *slap;
1f4c1525 458#endif
fd05b7dc 459
00ababe4 460 i = 0;
fd05b7dc 461 switch(opt) {
5e2062a4 462 case 'f': /* FALLTHROUGH */
6bfd88f1 463 case 'g': /* FALLTHROUGH */
da166178
RM
464 case 'n': /* FALLTHROUGH */
465 case 'x': /* FALLTHROUGH */
dc60cba4
RM
466 case 'T': /* FALLTHROUGH */
467 case 'U': /* We need to handle non interface options */
fd05b7dc 468 break;
03c2c879
RM
469 case 'b':
470 ifo->options |= DHCPCD_BACKGROUND;
471 break;
fd05b7dc
RM
472 case 'c':
473 strlcpy(ifo->script, arg, sizeof(ifo->script));
474 break;
acb1cf88
RM
475 case 'd':
476 ifo->options |= DHCPCD_DEBUG;
477 break;
6bfd88f1
RM
478 case 'e':
479 add_environ(ifo, arg, 1);
480 break;
fd05b7dc 481 case 'h':
cc3c3560
RM
482 if (!arg) {
483 ifo->options |= DHCPCD_HOSTNAME;
484 break;
485 }
486 s = parse_string(ifo->hostname, HOSTNAME_MAX_LEN, arg);
487 if (s == -1) {
488 syslog(LOG_ERR, "hostname: %m");
489 return -1;
490 }
491 if (s != 0 && ifo->hostname[0] == '.') {
492 syslog(LOG_ERR, "hostname cannot begin with .");
493 return -1;
fd05b7dc 494 }
cc3c3560 495 ifo->hostname[s] = '\0';
ed913a59
RM
496 if (ifo->hostname[0] == '\0')
497 ifo->options &= ~DHCPCD_HOSTNAME;
498 else
499 ifo->options |= DHCPCD_HOSTNAME;
fd05b7dc
RM
500 break;
501 case 'i':
502 if (arg)
503 s = parse_string((char *)ifo->vendorclassid + 1,
eab2229c 504 VENDORCLASSID_MAX_LEN, arg);
fd05b7dc
RM
505 else
506 s = 0;
507 if (s == -1) {
765fbf7d 508 syslog(LOG_ERR, "vendorclassid: %m");
fd05b7dc
RM
509 return -1;
510 }
511 *ifo->vendorclassid = (uint8_t)s;
512 break;
2662d519
RM
513 case 'k':
514 ifo->options |= DHCPCD_RELEASE;
515 break;
fd05b7dc
RM
516 case 'l':
517 if (*arg == '-') {
765fbf7d 518 syslog(LOG_ERR,
eab2229c 519 "leasetime must be a positive value");
fd05b7dc
RM
520 return -1;
521 }
522 errno = 0;
523 ifo->leasetime = (uint32_t)strtol(arg, NULL, 0);
524 if (errno == EINVAL || errno == ERANGE) {
765fbf7d 525 syslog(LOG_ERR, "`%s' out of range", arg);
fd05b7dc
RM
526 return -1;
527 }
528 break;
529 case 'm':
530 ifo->metric = atoint(arg);
531 if (ifo->metric < 0) {
765fbf7d 532 syslog(LOG_ERR, "metric must be a positive value");
fd05b7dc
RM
533 return -1;
534 }
535 break;
536 case 'o':
d7555c12
RM
537 arg = set_option_space(arg, &d, ifo, &request, &require, &no);
538 if (make_option_mask(d, request, arg, 1) != 0) {
765fbf7d 539 syslog(LOG_ERR, "unknown option `%s'", arg);
fd05b7dc
RM
540 return -1;
541 }
542 break;
543 case 'p':
544 ifo->options |= DHCPCD_PERSISTENT;
545 break;
03c2c879
RM
546 case 'q':
547 ifo->options |= DHCPCD_QUIET;
548 break;
2f7cb97c 549 case 'r':
2f7cb97c
RM
550 if (parse_addr(&ifo->req_addr, NULL, arg) != 0)
551 return -1;
5b39d8f5 552 ifo->options |= DHCPCD_REQUEST;
2f7cb97c
RM
553 ifo->req_mask.s_addr = 0;
554 break;
fd05b7dc 555 case 's':
d7555c12
RM
556 if (ifo->options & DHCPCD_IPV6 &&
557 !(ifo->options & DHCPCD_IPV4))
558 {
559 ifo->options |= DHCPCD_INFORM;
560 break;
561 }
2f7cb97c
RM
562 if (arg && *arg != '\0') {
563 if (parse_addr(&ifo->req_addr, &ifo->req_mask,
eab2229c 564 arg) != 0)
91a44b91 565 return -1;
2f7cb97c
RM
566 } else {
567 ifo->req_addr.s_addr = 0;
568 ifo->req_mask.s_addr = 0;
fd05b7dc 569 }
5b39d8f5
RM
570 ifo->options |= DHCPCD_INFORM | DHCPCD_PERSISTENT;
571 ifo->options &= ~(DHCPCD_ARP | DHCPCD_STATIC);
91a44b91 572 break;
fd05b7dc
RM
573 case 't':
574 ifo->timeout = atoint(arg);
575 if (ifo->timeout < 0) {
a2a9a498 576 syslog(LOG_ERR, "timeout must be a positive value");
fd05b7dc
RM
577 return -1;
578 }
579 break;
580 case 'u':
581 s = USERCLASS_MAX_LEN - ifo->userclass[0] - 1;
eab2229c
RM
582 s = parse_string((char *)ifo->userclass +
583 ifo->userclass[0] + 2,
584 s, arg);
fd05b7dc 585 if (s == -1) {
765fbf7d 586 syslog(LOG_ERR, "userclass: %m");
fd05b7dc
RM
587 return -1;
588 }
589 if (s != 0) {
590 ifo->userclass[ifo->userclass[0] + 1] = s;
591 ifo->userclass[0] += s + 1;
592 }
593 break;
594 case 'v':
595 p = strchr(arg, ',');
596 if (!p || !p[1]) {
b357d09f 597 syslog(LOG_ERR, "invalid vendor format: %s", arg);
fd05b7dc
RM
598 return -1;
599 }
95d6dcfa
RM
600
601 /* If vendor starts with , then it is not encapsulated */
602 if (p == arg) {
603 arg++;
604 s = parse_string((char *)ifo->vendor + 1,
605 VENDOR_MAX_LEN, arg);
606 if (s == -1) {
607 syslog(LOG_ERR, "vendor: %m");
608 return -1;
609 }
610 ifo->vendor[0] = (uint8_t)s;
611 ifo->options |= DHCPCD_VENDORRAW;
612 break;
613 }
614
615 /* Encapsulated vendor options */
616 if (ifo->options & DHCPCD_VENDORRAW) {
617 ifo->options &= ~DHCPCD_VENDORRAW;
618 ifo->vendor[0] = 0;
619 }
620
b357d09f 621 /* No need to strip the comma */
fd05b7dc 622 i = atoint(arg);
fd05b7dc 623 if (i < 1 || i > 254) {
765fbf7d 624 syslog(LOG_ERR, "vendor option should be between"
eab2229c 625 " 1 and 254 inclusive");
fd05b7dc
RM
626 return -1;
627 }
b357d09f
RM
628
629 arg = p + 1;
fd05b7dc
RM
630 s = VENDOR_MAX_LEN - ifo->vendor[0] - 2;
631 if (inet_aton(arg, &addr) == 1) {
632 if (s < 6) {
633 s = -1;
634 errno = ENOBUFS;
635 } else
636 memcpy(ifo->vendor + ifo->vendor[0] + 3,
eab2229c 637 &addr.s_addr, sizeof(addr.s_addr));
fd05b7dc 638 } else {
eab2229c
RM
639 s = parse_string((char *)ifo->vendor +
640 ifo->vendor[0] + 3, s, arg);
fd05b7dc
RM
641 }
642 if (s == -1) {
765fbf7d 643 syslog(LOG_ERR, "vendor: %m");
fd05b7dc
RM
644 return -1;
645 }
646 if (s != 0) {
647 ifo->vendor[ifo->vendor[0] + 1] = i;
648 ifo->vendor[ifo->vendor[0] + 2] = s;
649 ifo->vendor[0] += s + 2;
650 }
651 break;
2a07a2af
RM
652 case 'w':
653 ifo->options |= DHCPCD_WAITIP;
7013b073
RM
654 if (arg != NULL && arg[0] != '\0') {
655 if (arg[0] == '4' || arg[1] == '4')
656 ifo->options |= DHCPCD_WAITIP4;
657 if (arg[0] == '6' || arg[1] == '6')
658 ifo->options |= DHCPCD_WAITIP6;
659 }
2a07a2af 660 break;
a2a9a498
RM
661 case 'y':
662 ifo->reboot = atoint(arg);
663 if (ifo->reboot < 0) {
664 syslog(LOG_ERR, "reboot must be a positive value");
665 return -1;
666 }
667 break;
d3088c74 668 case 'z':
378f8fa4 669 ifav = splitv(&ifac, ifav, arg);
d3088c74 670 break;
fd05b7dc
RM
671 case 'A':
672 ifo->options &= ~DHCPCD_ARP;
673 /* IPv4LL requires ARP */
674 ifo->options &= ~DHCPCD_IPV4LL;
675 break;
03c2c879
RM
676 case 'B':
677 ifo->options &= ~DHCPCD_DAEMONISE;
678 break;
fd05b7dc
RM
679 case 'C':
680 /* Commas to spaces for shell */
681 while ((p = strchr(arg, ',')))
682 *p = ' ';
683 s = strlen("skip_hooks=") + strlen(arg) + 1;
28382337
RM
684 p = malloc(sizeof(char) * s);
685 if (p == NULL) {
686 syslog(LOG_ERR, "%s: %m", __func__);
687 return -1;
688 }
fd05b7dc
RM
689 snprintf(p, s, "skip_hooks=%s", arg);
690 add_environ(ifo, p, 0);
691 free(p);
692 break;
693 case 'D':
c989b023 694 ifo->options |= DHCPCD_CLIENTID | DHCPCD_DUID;
fd05b7dc
RM
695 break;
696 case 'E':
697 ifo->options |= DHCPCD_LASTLEASE;
698 break;
699 case 'F':
700 if (!arg) {
701 ifo->fqdn = FQDN_BOTH;
702 break;
703 }
704 if (strcmp(arg, "none") == 0)
705 ifo->fqdn = FQDN_NONE;
706 else if (strcmp(arg, "ptr") == 0)
707 ifo->fqdn = FQDN_PTR;
708 else if (strcmp(arg, "both") == 0)
709 ifo->fqdn = FQDN_BOTH;
710 else if (strcmp(arg, "disable") == 0)
711 ifo->fqdn = FQDN_DISABLE;
712 else {
765fbf7d 713 syslog(LOG_ERR, "invalid value `%s' for FQDN", arg);
fd05b7dc
RM
714 return -1;
715 }
716 break;
717 case 'G':
718 ifo->options &= ~DHCPCD_GATEWAY;
719 break;
4242c9b3
RM
720 case 'H':
721 ifo->options |= DHCPCD_XID_HWADDR;
722 break;
fd05b7dc
RM
723 case 'I':
724 /* Strings have a type of 0 */;
725 ifo->clientid[1] = 0;
726 if (arg)
727 s = parse_string_hwaddr((char *)ifo->clientid + 1,
eab2229c 728 CLIENTID_MAX_LEN, arg, 1);
fd05b7dc
RM
729 else
730 s = 0;
731 if (s == -1) {
765fbf7d 732 syslog(LOG_ERR, "clientid: %m");
fd05b7dc
RM
733 return -1;
734 }
c989b023 735 ifo->options |= DHCPCD_CLIENTID;
fd05b7dc 736 ifo->clientid[0] = (uint8_t)s;
fd05b7dc 737 break;
900b3da4
RM
738 case 'J':
739 ifo->options |= DHCPCD_BROADCAST;
740 break;
fd05b7dc
RM
741 case 'K':
742 ifo->options &= ~DHCPCD_LINK;
743 break;
744 case 'L':
745 ifo->options &= ~DHCPCD_IPV4LL;
746 break;
747 case 'O':
d7555c12
RM
748 arg = set_option_space(arg, &d, ifo, &request, &require, &no);
749 if (make_option_mask(d, request, arg, -1) != 0 ||
750 make_option_mask(d, require, arg, -1) != 0 ||
751 make_option_mask(d, no, arg, 1) != 0)
fd05b7dc 752 {
765fbf7d 753 syslog(LOG_ERR, "unknown option `%s'", arg);
fd05b7dc
RM
754 return -1;
755 }
756 break;
757 case 'Q':
d7555c12
RM
758 arg = set_option_space(arg, &d, ifo, &request, &require, &no);
759 if (make_option_mask(d, require, arg, 1) != 0 ||
760 make_option_mask(d, request, arg, 1) != 0)
fd05b7dc 761 {
765fbf7d 762 syslog(LOG_ERR, "unknown option `%s'", arg);
fd05b7dc
RM
763 return -1;
764 }
765 break;
91a44b91
RM
766 case 'S':
767 p = strchr(arg, '=');
768 if (p == NULL) {
769 syslog(LOG_ERR, "static assignment required");
770 return -1;
771 }
772 p++;
773 if (strncmp(arg, "ip_address=", strlen("ip_address=")) == 0) {
776961cf
RM
774 if (parse_addr(&ifo->req_addr,
775 ifo->req_mask.s_addr == 0 ? &ifo->req_mask : NULL,
776 p) != 0)
91a44b91
RM
777 return -1;
778
779 ifo->options |= DHCPCD_STATIC;
fa8b2a7a 780 ifo->options &= ~DHCPCD_INFORM;
776961cf
RM
781 } else if (strncmp(arg, "subnet_mask=", strlen("subnet_mask=")) == 0) {
782 if (parse_addr(&ifo->req_mask, NULL, p) != 0)
783 return -1;
91a44b91 784 } else if (strncmp(arg, "routes=", strlen("routes=")) == 0 ||
eab2229c
RM
785 strncmp(arg, "static_routes=", strlen("static_routes=")) == 0 ||
786 strncmp(arg, "classless_static_routes=", strlen("classless_static_routes=")) == 0 ||
787 strncmp(arg, "ms_classless_static_routes=", strlen("ms_classless_static_routes=")) == 0)
91a44b91 788 {
332a5fe6 789 fp = np = strchr(p, ' ');
91a44b91
RM
790 if (np == NULL) {
791 syslog(LOG_ERR, "all routes need a gateway");
792 return -1;
793 }
794 *np++ = '\0';
795 while (*np == ' ')
796 np++;
797 if (ifo->routes == NULL) {
4c9c4b3e
RM
798 ifo->routes = malloc(sizeof(*ifo->routes));
799 if (ifo->routes == NULL) {
10e17e3f 800 syslog(LOG_ERR, "%s: %m", __func__);
10e17e3f
RM
801 return -1;
802 }
673e81e5 803 TAILQ_INIT(ifo->routes);
4c9c4b3e
RM
804 }
805 rt = malloc(sizeof(*rt));
806 if (rt == NULL) {
807 syslog(LOG_ERR, "%s: %m", __func__);
808 *fp = ' ';
809 return -1;
91a44b91 810 }
91a44b91
RM
811 if (parse_addr(&rt->dest, &rt->net, p) == -1 ||
812 parse_addr(&rt->gate, NULL, np) == -1)
332a5fe6 813 {
4c9c4b3e 814 free(rt);
332a5fe6 815 *fp = ' ';
91a44b91 816 return -1;
332a5fe6 817 }
4c9c4b3e 818 TAILQ_INSERT_TAIL(ifo->routes, rt, next);
332a5fe6 819 *fp = ' ';
91a44b91
RM
820 } else if (strncmp(arg, "routers=", strlen("routers=")) == 0) {
821 if (ifo->routes == NULL) {
4c9c4b3e
RM
822 ifo->routes = malloc(sizeof(*ifo->routes));
823 if (ifo->routes == NULL) {
10e17e3f
RM
824 syslog(LOG_ERR, "%s: %m", __func__);
825 return -1;
826 }
00ababe4 827 TAILQ_INIT(ifo->routes);
4c9c4b3e
RM
828 }
829 rt = malloc(sizeof(*rt));
830 if (rt == NULL) {
831 syslog(LOG_ERR, "%s: %m", __func__);
832 return -1;
91a44b91 833 }
1abffd5b
RM
834 rt->dest.s_addr = INADDR_ANY;
835 rt->net.s_addr = INADDR_ANY;
4c9c4b3e
RM
836 if (parse_addr(&rt->gate, NULL, p) == -1) {
837 free(rt);
91a44b91 838 return -1;
4c9c4b3e
RM
839 }
840 TAILQ_INSERT_TAIL(ifo->routes, rt, next);
91a44b91
RM
841 } else {
842 s = 0;
843 if (ifo->config != NULL) {
844 while (ifo->config[s] != NULL) {
eab2229c
RM
845 if (strncmp(ifo->config[s], arg,
846 p - arg) == 0)
847 {
78369646
RM
848 p = strdup(arg);
849 if (p == NULL) {
850 syslog(LOG_ERR,
851 "%s: %m", __func__);
852 return -1;
853 }
91a44b91 854 free(ifo->config[s]);
78369646 855 ifo->config[s] = p;
91a44b91
RM
856 return 1;
857 }
858 s++;
859 }
860 }
78369646 861 p = strdup(arg);
fa245a4d
RM
862 if (p == NULL) {
863 syslog(LOG_ERR, "%s: %m", __func__);
864 return -1;
865 }
78369646
RM
866 nconf = realloc(ifo->config, sizeof(char *) * (s + 2));
867 if (nconf == NULL) {
868 syslog(LOG_ERR, "%s: %m", __func__);
869 return -1;
870 }
fa245a4d 871 ifo->config = nconf;
78369646 872 ifo->config[s] = p;
91a44b91
RM
873 ifo->config[s + 1] = NULL;
874 }
875 break;
bf80d526
RM
876 case 'W':
877 if (parse_addr(&addr, &addr2, arg) != 0)
878 return -1;
879 if (strchr(arg, '/') == NULL)
880 addr2.s_addr = INADDR_BROADCAST;
fa245a4d 881 naddr = realloc(ifo->whitelist,
bf80d526 882 sizeof(in_addr_t) * (ifo->whitelist_len + 2));
fa245a4d
RM
883 if (naddr == NULL) {
884 syslog(LOG_ERR, "%s: %m", __func__);
885 return -1;
886 }
887 ifo->whitelist = naddr;
bf80d526
RM
888 ifo->whitelist[ifo->whitelist_len++] = addr.s_addr;
889 ifo->whitelist[ifo->whitelist_len++] = addr2.s_addr;
890 break;
fd05b7dc 891 case 'X':
e095a6eb 892 if (parse_addr(&addr, &addr2, arg) != 0)
fd05b7dc 893 return -1;
ce6b39df
RM
894 if (strchr(arg, '/') == NULL)
895 addr2.s_addr = INADDR_BROADCAST;
fa245a4d 896 naddr = realloc(ifo->blacklist,
e095a6eb 897 sizeof(in_addr_t) * (ifo->blacklist_len + 2));
fa245a4d
RM
898 if (naddr == NULL) {
899 syslog(LOG_ERR, "%s: %m", __func__);
900 return -1;
901 }
902 ifo->blacklist = naddr;
e095a6eb
RM
903 ifo->blacklist[ifo->blacklist_len++] = addr.s_addr;
904 ifo->blacklist[ifo->blacklist_len++] = addr2.s_addr;
fd05b7dc 905 break;
d3088c74 906 case 'Z':
378f8fa4 907 ifdv = splitv(&ifdc, ifdv, arg);
d3088c74 908 break;
d7555c12 909 case '4':
2f0addfd 910 ifo->options &= ~DHCPCD_IPV6;
d7555c12
RM
911 ifo->options |= DHCPCD_IPV4;
912 break;
913 case '6':
914 ifo->options &= ~DHCPCD_IPV4;
2f0addfd 915 ifo->options |= DHCPCD_IPV6;
d7555c12 916 break;
bb8051bf
RM
917 case O_NOIPV4:
918 ifo->options &= ~DHCPCD_IPV4;
919 break;
920 case O_NOIPV6:
921 ifo->options &= ~DHCPCD_IPV6;
922 break;
aae24feb 923#ifdef INET
4ca7460f
RM
924 case O_ARPING:
925 if (parse_addr(&addr, NULL, arg) != 0)
926 return -1;
fa245a4d 927 naddr = realloc(ifo->arping,
4ca7460f 928 sizeof(in_addr_t) * (ifo->arping_len + 1));
fa245a4d
RM
929 if (naddr == NULL) {
930 syslog(LOG_ERR, "%s: %m", __func__);
931 return -1;
932 }
933 ifo->arping = naddr;
4ca7460f
RM
934 ifo->arping[ifo->arping_len++] = addr.s_addr;
935 break;
41c60e02 936 case O_DESTINATION:
d7555c12 937 if (make_option_mask(dhcp_opts, ifo->dstmask, arg, 2) != 0) {
41c60e02
RM
938 if (errno == EINVAL)
939 syslog(LOG_ERR, "option `%s' does not take"
940 " an IPv4 address", arg);
941 else
942 syslog(LOG_ERR, "unknown option `%s'", arg);
943 return -1;
944 }
945 break;
ff021b0b
RM
946 case O_FALLBACK:
947 free(ifo->fallback);
78369646
RM
948 ifo->fallback = strdup(arg);
949 if (ifo->fallback == NULL) {
950 syslog(LOG_ERR, "%s: %m", __func__);
00ababe4 951 return -1;
78369646 952 }
ff021b0b 953 break;
aae24feb 954#endif
eebe9a18
RM
955 case O_IPV6RS:
956 ifo->options |= DHCPCD_IPV6RS;
957 break;
91cd7324 958 case O_NOIPV6RS:
61dd6cf9
RM
959 ifo->options &= ~DHCPCD_IPV6RS;
960 break;
eebe9a18 961 case O_IPV6RA_FORK:
61dd6cf9 962 ifo->options &= ~DHCPCD_IPV6RA_REQRDNSS;
91cd7324 963 break;
eebe9a18
RM
964 case O_IPV6RA_OWN:
965 ifo->options |= DHCPCD_IPV6RA_OWN;
966 break;
967 case O_IPV6RA_OWN_D:
968 ifo->options |= DHCPCD_IPV6RA_OWN_DEFAULT;
969 break;
7dab081f
RM
970 case O_NOALIAS:
971 ifo->options |= DHCPCD_NOALIAS;
972 break;
00ababe4
RM
973#ifdef INET6
974 case O_IA_NA:
975 i = D6_OPTION_IA_NA;
976 /* FALLTHROUGH */
977 case O_IA_TA:
978 if (i == 0)
979 i = D6_OPTION_IA_TA;
980 /* FALLTHROUGH */
981 case O_IA_PD:
982 if (i == 0)
983 i = D6_OPTION_IA_PD;
984 ifo->options |= DHCPCD_IA_FORCED;
985 if (ifo->ia_type != 0 && ifo->ia_type != i) {
986 syslog(LOG_ERR, "cannot specify a different IA type");
987 return -1;
988 }
989 ifo->ia_type = i;
990 if (arg == NULL)
991 break;
992 fp = strchr(arg, ' ');
eade34fd
RM
993 if (fp)
994 *fp++ = '\0';
367f7b11
RM
995 errno = 0;
996 l = strtol(arg, &np, 0);
7784695d
RM
997 if (l >= 0 && l <= (long)UINT32_MAX &&
998 errno == 0 && *np == '\0')
999 {
367f7b11
RM
1000 u32 = htonl(l);
1001 memcpy(&_iaid, &u32, sizeof(_iaid));
1002 goto got_iaid;
1003 }
00ababe4
RM
1004 if ((s = parse_string((char *)_iaid, sizeof(_iaid), arg)) < 1) {
1005 syslog(LOG_ERR, "%s: invalid IAID", arg);
1006 return -1;
1007 }
1008 if (s < 4)
1009 _iaid[3] = '\0';
1010 if (s < 3)
1011 _iaid[2] = '\0';
1012 if (s < 2)
1013 _iaid[1] = '\0';
367f7b11 1014got_iaid:
00ababe4
RM
1015 iaid = NULL;
1016 for (sl = 0; sl < ifo->iaid_len; sl++) {
1017 if (ifo->iaid[sl].iaid[0] == _iaid[0] &&
1018 ifo->iaid[sl].iaid[1] == _iaid[1] &&
1019 ifo->iaid[sl].iaid[2] == _iaid[2] &&
1020 ifo->iaid[sl].iaid[3] == _iaid[3])
1021 {
1022 iaid = &ifo->iaid[sl];
1023 break;
1024 }
1025 }
1026 if (iaid == NULL) {
1027 iaid = realloc(ifo->iaid,
1028 sizeof(*ifo->iaid) * (ifo->iaid_len + 1));
1029 if (iaid == NULL) {
1030 syslog(LOG_ERR, "%s: %m", __func__);
1031 return -1;
1032 }
1033 ifo->iaid = iaid;
1034 iaid = &ifo->iaid[ifo->iaid_len++];
1035 iaid->iaid[0] = _iaid[0];
1036 iaid->iaid[1] = _iaid[1];
1037 iaid->iaid[2] = _iaid[2];
1038 iaid->iaid[3] = _iaid[3];
1039 iaid->sla = NULL;
1040 iaid->sla_len = 0;
1041 }
7cece083
RM
1042 if (ifo->ia_type != D6_OPTION_IA_PD)
1043 break;
00ababe4
RM
1044 for (p = fp; p; p = fp) {
1045 fp = strchr(p, ' ');
1046 if (fp)
1047 *fp++ = '\0';
1048 sla = realloc(iaid->sla,
1049 sizeof(*iaid->sla) * (iaid->sla_len + 1));
1050 if (sla == NULL) {
1051 syslog(LOG_ERR, "%s: %m", __func__);
1052 return -1;
1053 }
1054 iaid->sla = sla;
1055 sla = &iaid->sla[iaid->sla_len++];
1056 np = strchr(p, '/');
1057 if (np)
1058 *np++ = '\0';
00ababe4
RM
1059 if (strlcpy(sla->ifname, p,
1060 sizeof(sla->ifname)) >= sizeof(sla->ifname))
1061 {
1062 syslog(LOG_ERR, "%s: interface name too long",
1063 arg);
1064 return -1;
1065 }
1066 p = np;
367f7b11
RM
1067 if (p) {
1068 np = strchr(p, '/');
1069 if (np)
1070 *np++ = '\0';
83919266
RM
1071 if (*p == '\0')
1072 sla->sla_set = 0;
1073 else {
1074 errno = 0;
1075 sla->sla = atoint(p);
1076 sla->sla_set = 1;
1077 if (errno)
1078 return -1;
1079 }
367f7b11
RM
1080 if (np) {
1081 sla->prefix_len = atoint(np);
1082 if (sla->prefix_len < 0 ||
1083 sla->prefix_len > 128)
1084 return -1;
1085 } else
1086 sla->prefix_len = 64;
5985c4e2
RM
1087 } else {
1088 sla->sla_set = 0;
1089 /* Sanity - check there are no more
1090 * unspecified SLA's */
1091 for (sl = 0; sl < iaid->sla_len - 1; sl++) {
1092 slap = &iaid->sla[sl];
1093 if (slap->sla_set == 0 &&
1094 strcmp(slap->ifname, sla->ifname)
1095 == 0)
1096 {
1097 syslog(LOG_WARNING,
1098 "%s: cannot specify the "
1099 "same interface twice with "
1100 "an automatic SLA",
1101 sla->ifname);
1102 iaid->sla_len--;
1103 break;
1104 }
1105 }
367f7b11 1106 }
00ababe4
RM
1107 }
1108 break;
413652c1 1109#endif
d6a18654 1110 case O_HOSTNAME_SHORT:
9c54b11b 1111 ifo->options |= DHCPCD_HOSTNAME | DHCPCD_HOSTNAME_SHORT;
d6a18654 1112 break;
413652c1
RM
1113 case O_DEV:
1114 if (dev_load)
1115 free(dev_load);
1116 dev_load = strdup(arg);
1117 break;
1118 case O_NODEV:
1119 ifo->options &= ~DHCPCD_DEV;
1120 break;
fd05b7dc
RM
1121 default:
1122 return 0;
1123 }
1124
1125 return 1;
1126}
1127
1128static int
1129parse_config_line(struct if_options *ifo, const char *opt, char *line)
1130{
1131 unsigned int i;
1132
1133 for (i = 0; i < sizeof(cf_options) / sizeof(cf_options[0]); i++) {
1134 if (!cf_options[i].name ||
1135 strcmp(cf_options[i].name, opt) != 0)
1136 continue;
1137
1138 if (cf_options[i].has_arg == required_argument && !line) {
1139 fprintf(stderr,
eab2229c
RM
1140 PACKAGE ": option requires an argument -- %s\n",
1141 opt);
fd05b7dc
RM
1142 return -1;
1143 }
1144
1145 return parse_option(ifo, cf_options[i].val, line);
1146 }
1147
1148 fprintf(stderr, PACKAGE ": unknown option -- %s\n", opt);
1149 return -1;
1150}
1151
741f46c6 1152static void
b0272a9d 1153finish_config(struct if_options *ifo)
741f46c6
RM
1154{
1155
1156 /* Terminate the encapsulated options */
1157 if (ifo->vendor[0] && !(ifo->options & DHCPCD_VENDORRAW)) {
1158 ifo->vendor[0]++;
1159 ifo->vendor[ifo->vendor[0]] = DHO_END;
1160 }
b0272a9d 1161}
741f46c6
RM
1162
1163#ifdef INET6
b0272a9d
RM
1164static void
1165finish_config6(struct if_options *ifo, const char *ifname)
1166{
1167
741f46c6
RM
1168 if (!(ifo->options & DHCPCD_IPV6))
1169 ifo->options &= ~DHCPCD_IPV6RS;
1170
1171 if (ifname && ifo->iaid_len == 0 && ifo->options & DHCPCD_IPV6) {
1172 ifo->iaid = malloc(sizeof(*ifo->iaid));
1173 if (ifo->iaid == NULL)
1174 syslog(LOG_ERR, "%s: %m", __func__);
1175 else {
1176 if (ifo->ia_type == 0)
1177 ifo->ia_type = D6_OPTION_IA_NA;
1178 ifo->iaid_len = strlen(ifname);
1179 if (ifo->iaid_len <= sizeof(ifo->iaid->iaid)) {
1180 strncpy((char *)ifo->iaid->iaid, ifname,
1181 sizeof(ifo->iaid->iaid));
1182 memset(ifo->iaid->iaid + ifo->iaid_len, 0,
1183 sizeof(ifo->iaid->iaid) -ifo->iaid_len);
1184 } else {
1185 uint32_t idx = if_nametoindex(ifname);
1186 memcpy(ifo->iaid->iaid, &idx, sizeof(idx));
1187 }
1188 ifo->iaid_len = 1;
1189 ifo->iaid->sla = NULL;
1190 ifo->iaid->sla_len = 0;
1191 }
a7386a6e 1192 }
741f46c6 1193}
b0272a9d 1194#endif
741f46c6 1195
fd05b7dc 1196struct if_options *
6f767217
RM
1197read_config(const char *file,
1198 const char *ifname, const char *ssid, const char *profile)
fd05b7dc
RM
1199{
1200 struct if_options *ifo;
1201 FILE *f;
27805e96 1202 char *line, *option, *p;
6f767217 1203 int skip = 0, have_profile = 0;
fd05b7dc
RM
1204
1205 /* Seed our default options */
10e17e3f
RM
1206 ifo = calloc(1, sizeof(*ifo));
1207 if (ifo == NULL) {
1208 syslog(LOG_ERR, "%s: %m", __func__);
1209 return NULL;
1210 }
aae24feb 1211 ifo->options |= DHCPCD_DAEMONISE | DHCPCD_LINK;
413652c1
RM
1212#ifdef PLUGIN_DEV
1213 ifo->options |= DHCPCD_DEV;
1214#endif
aae24feb 1215#ifdef INET
d7555c12 1216 ifo->options |= DHCPCD_IPV4 | DHCPCD_IPV4LL;
00ababe4 1217 ifo->options |= DHCPCD_GATEWAY | DHCPCD_ARP;
aae24feb
RM
1218#endif
1219#ifdef INET6
d7555c12 1220 ifo->options |= DHCPCD_IPV6 | DHCPCD_IPV6RS | DHCPCD_IPV6RA_REQRDNSS;
66fd5d67 1221 ifo->dadtransmits = ipv6_dadtransmits(ifname);
aae24feb 1222#endif
fd05b7dc 1223 ifo->timeout = DEFAULT_TIMEOUT;
a2a9a498 1224 ifo->reboot = DEFAULT_REBOOT;
f43e5853 1225 ifo->metric = -1;
ed913a59 1226 strlcpy(ifo->script, SCRIPT, sizeof(ifo->script));
793c4286 1227
27805e96
RM
1228 ifo->vendorclassid[0] = strlen(vendor);
1229 memcpy(ifo->vendorclassid + 1, vendor, ifo->vendorclassid[0]);
fd05b7dc
RM
1230
1231 /* Parse our options file */
9f7780b0 1232 f = fopen(file ? file : CONFIG, "r");
5e2062a4
RM
1233 if (f == NULL) {
1234 if (file != NULL)
1235 syslog(LOG_ERR, "fopen `%s': %m", file);
fd05b7dc 1236 return ifo;
5e2062a4 1237 }
fd05b7dc 1238
e1caa8db
RM
1239 while ((line = get_line(f))) {
1240 option = strsep(&line, " \t");
fd05b7dc
RM
1241 /* Trim trailing whitespace */
1242 if (line && *line) {
1243 p = line + strlen(line) - 1;
1244 while (p != line &&
eab2229c
RM
1245 (*p == ' ' || *p == '\t') &&
1246 *(p - 1) != '\\')
fd05b7dc
RM
1247 *p-- = '\0';
1248 }
1249 /* Start of an interface block, skip if not ours */
1250 if (strcmp(option, "interface") == 0) {
1251 if (ifname && line && strcmp(line, ifname) == 0)
1252 skip = 0;
1253 else
1254 skip = 1;
1255 continue;
c53cf4ef
RM
1256 }
1257 /* Start of an ssid block, skip if not ours */
1258 if (strcmp(option, "ssid") == 0) {
1259 if (ssid && line && strcmp(line, ssid) == 0)
1260 skip = 0;
1261 else
1262 skip = 1;
1263 continue;
fd05b7dc 1264 }
6f767217
RM
1265 /* Start of a profile block, skip if not ours */
1266 if (strcmp(option, "profile") == 0) {
1267 if (profile && line && strcmp(line, profile) == 0) {
1268 skip = 0;
1269 have_profile = 1;
1270 } else
1271 skip = 1;
1272 continue;
1273 }
fd05b7dc
RM
1274 if (skip)
1275 continue;
378f8fa4 1276 parse_config_line(ifo, option, line);
fd05b7dc 1277 }
fd05b7dc
RM
1278 fclose(f);
1279
6f767217
RM
1280 if (profile && !have_profile) {
1281 free_options(ifo);
1282 errno = ENOENT;
1283 ifo = NULL;
1284 }
1285
b0272a9d
RM
1286 finish_config(ifo);
1287#ifdef INET6
1288 finish_config6(ifo, ifname);
1289#endif
fd05b7dc
RM
1290 return ifo;
1291}
1292
1293int
1294add_options(struct if_options *ifo, int argc, char **argv)
1295{
29c0fd6f
RM
1296 int oi, opt, r;
1297
1298 if (argc == 0)
1299 return 1;
fd05b7dc
RM
1300
1301 optind = 0;
29c0fd6f 1302 r = 1;
fd05b7dc
RM
1303 while ((opt = getopt_long(argc, argv, IF_OPTS, cf_options, &oi)) != -1)
1304 {
1305 r = parse_option(ifo, opt, optarg);
1306 if (r != 1)
1307 break;
1308 }
741f46c6 1309
b0272a9d
RM
1310 finish_config(ifo);
1311#ifdef INET6
1312 finish_config6(ifo, NULL);
1313#endif
fd05b7dc
RM
1314 return r;
1315}
1316
1317void
1318free_options(struct if_options *ifo)
1319{
1320 size_t i;
1321
f43e5853
RM
1322 if (ifo) {
1323 if (ifo->environ) {
1324 i = 0;
1325 while (ifo->environ[i])
1326 free(ifo->environ[i++]);
1327 free(ifo->environ);
1328 }
91a44b91
RM
1329 if (ifo->config) {
1330 i = 0;
1331 while (ifo->config[i])
1332 free(ifo->config[i++]);
1333 free(ifo->config);
1334 }
e88c525f 1335 ipv4_freeroutes(ifo->routes);
ff021b0b 1336 free(ifo->arping);
f43e5853 1337 free(ifo->blacklist);
ff021b0b 1338 free(ifo->fallback);
00ababe4
RM
1339#ifdef INET6
1340 for (i = 0; i < ifo->iaid_len; i++)
1341 free(ifo->iaid[i].sla);
1342 free(ifo->iaid);
1343#endif
f43e5853 1344 free(ifo);
fd05b7dc 1345 }
fd05b7dc 1346}