xref: /minix3/external/bsd/dhcpcd/dist/dhcpcd.c (revision 9f20bfa6c4c442e2e798d91b11c2a5f8d6833a41)
1*9f20bfa6SDavid van Moolenbroek #include <sys/cdefs.h>
2*9f20bfa6SDavid van Moolenbroek  __RCSID("$NetBSD: dhcpcd.c,v 1.28 2015/09/04 12:25:01 roy Exp $");
3*9f20bfa6SDavid van Moolenbroek 
4*9f20bfa6SDavid van Moolenbroek /*
5*9f20bfa6SDavid van Moolenbroek  * dhcpcd - DHCP client daemon
6*9f20bfa6SDavid van Moolenbroek  * Copyright (c) 2006-2015 Roy Marples <roy@marples.name>
7*9f20bfa6SDavid van Moolenbroek  * All rights reserved
8*9f20bfa6SDavid van Moolenbroek 
9*9f20bfa6SDavid van Moolenbroek  * Redistribution and use in source and binary forms, with or without
10*9f20bfa6SDavid van Moolenbroek  * modification, are permitted provided that the following conditions
11*9f20bfa6SDavid van Moolenbroek  * are met:
12*9f20bfa6SDavid van Moolenbroek  * 1. Redistributions of source code must retain the above copyright
13*9f20bfa6SDavid van Moolenbroek  *    notice, this list of conditions and the following disclaimer.
14*9f20bfa6SDavid van Moolenbroek  * 2. Redistributions in binary form must reproduce the above copyright
15*9f20bfa6SDavid van Moolenbroek  *    notice, this list of conditions and the following disclaimer in the
16*9f20bfa6SDavid van Moolenbroek  *    documentation and/or other materials provided with the distribution.
17*9f20bfa6SDavid van Moolenbroek  *
18*9f20bfa6SDavid van Moolenbroek  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19*9f20bfa6SDavid van Moolenbroek  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20*9f20bfa6SDavid van Moolenbroek  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21*9f20bfa6SDavid van Moolenbroek  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22*9f20bfa6SDavid van Moolenbroek  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23*9f20bfa6SDavid van Moolenbroek  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24*9f20bfa6SDavid van Moolenbroek  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25*9f20bfa6SDavid van Moolenbroek  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26*9f20bfa6SDavid van Moolenbroek  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27*9f20bfa6SDavid van Moolenbroek  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28*9f20bfa6SDavid van Moolenbroek  * SUCH DAMAGE.
29*9f20bfa6SDavid van Moolenbroek  */
30*9f20bfa6SDavid van Moolenbroek 
31*9f20bfa6SDavid van Moolenbroek const char dhcpcd_copyright[] = "Copyright (c) 2006-2015 Roy Marples";
32*9f20bfa6SDavid van Moolenbroek 
33*9f20bfa6SDavid van Moolenbroek #define _WITH_DPRINTF /* Stop FreeBSD bitching */
34*9f20bfa6SDavid van Moolenbroek 
35*9f20bfa6SDavid van Moolenbroek #include <sys/file.h>
36*9f20bfa6SDavid van Moolenbroek #include <sys/socket.h>
37*9f20bfa6SDavid van Moolenbroek #include <sys/stat.h>
38*9f20bfa6SDavid van Moolenbroek #include <sys/time.h>
39*9f20bfa6SDavid van Moolenbroek #include <sys/types.h>
40*9f20bfa6SDavid van Moolenbroek #include <sys/uio.h>
41*9f20bfa6SDavid van Moolenbroek 
42*9f20bfa6SDavid van Moolenbroek #include <ctype.h>
43*9f20bfa6SDavid van Moolenbroek #include <errno.h>
44*9f20bfa6SDavid van Moolenbroek #include <fcntl.h>
45*9f20bfa6SDavid van Moolenbroek #include <getopt.h>
46*9f20bfa6SDavid van Moolenbroek #include <limits.h>
47*9f20bfa6SDavid van Moolenbroek #include <paths.h>
48*9f20bfa6SDavid van Moolenbroek #include <signal.h>
49*9f20bfa6SDavid van Moolenbroek #include <stdio.h>
50*9f20bfa6SDavid van Moolenbroek #include <stdlib.h>
51*9f20bfa6SDavid van Moolenbroek #include <string.h>
52*9f20bfa6SDavid van Moolenbroek #include <unistd.h>
53*9f20bfa6SDavid van Moolenbroek #include <time.h>
54*9f20bfa6SDavid van Moolenbroek 
55*9f20bfa6SDavid van Moolenbroek #include "config.h"
56*9f20bfa6SDavid van Moolenbroek #include "arp.h"
57*9f20bfa6SDavid van Moolenbroek #include "common.h"
58*9f20bfa6SDavid van Moolenbroek #include "control.h"
59*9f20bfa6SDavid van Moolenbroek #include "dev.h"
60*9f20bfa6SDavid van Moolenbroek #include "dhcpcd.h"
61*9f20bfa6SDavid van Moolenbroek #include "dhcp6.h"
62*9f20bfa6SDavid van Moolenbroek #include "duid.h"
63*9f20bfa6SDavid van Moolenbroek #include "eloop.h"
64*9f20bfa6SDavid van Moolenbroek #include "if.h"
65*9f20bfa6SDavid van Moolenbroek #include "if-options.h"
66*9f20bfa6SDavid van Moolenbroek #include "ipv4.h"
67*9f20bfa6SDavid van Moolenbroek #include "ipv4ll.h"
68*9f20bfa6SDavid van Moolenbroek #include "ipv6.h"
69*9f20bfa6SDavid van Moolenbroek #include "ipv6nd.h"
70*9f20bfa6SDavid van Moolenbroek #include "script.h"
71*9f20bfa6SDavid van Moolenbroek 
72*9f20bfa6SDavid van Moolenbroek #ifdef USE_SIGNALS
73*9f20bfa6SDavid van Moolenbroek const int dhcpcd_signals[] = {
74*9f20bfa6SDavid van Moolenbroek 	SIGTERM,
75*9f20bfa6SDavid van Moolenbroek 	SIGINT,
76*9f20bfa6SDavid van Moolenbroek 	SIGALRM,
77*9f20bfa6SDavid van Moolenbroek 	SIGHUP,
78*9f20bfa6SDavid van Moolenbroek 	SIGUSR1,
79*9f20bfa6SDavid van Moolenbroek 	SIGUSR2,
80*9f20bfa6SDavid van Moolenbroek 	SIGPIPE
81*9f20bfa6SDavid van Moolenbroek };
82*9f20bfa6SDavid van Moolenbroek const size_t dhcpcd_signals_len = __arraycount(dhcpcd_signals);
83*9f20bfa6SDavid van Moolenbroek #endif
84*9f20bfa6SDavid van Moolenbroek 
85*9f20bfa6SDavid van Moolenbroek #if defined(USE_SIGNALS) || !defined(THERE_IS_NO_FORK)
86*9f20bfa6SDavid van Moolenbroek static pid_t
read_pid(const char * pidfile)87*9f20bfa6SDavid van Moolenbroek read_pid(const char *pidfile)
88*9f20bfa6SDavid van Moolenbroek {
89*9f20bfa6SDavid van Moolenbroek 	FILE *fp;
90*9f20bfa6SDavid van Moolenbroek 	pid_t pid;
91*9f20bfa6SDavid van Moolenbroek 
92*9f20bfa6SDavid van Moolenbroek 	if ((fp = fopen(pidfile, "r")) == NULL) {
93*9f20bfa6SDavid van Moolenbroek 		errno = ENOENT;
94*9f20bfa6SDavid van Moolenbroek 		return 0;
95*9f20bfa6SDavid van Moolenbroek 	}
96*9f20bfa6SDavid van Moolenbroek 	if (fscanf(fp, "%d", &pid) != 1)
97*9f20bfa6SDavid van Moolenbroek 		pid = 0;
98*9f20bfa6SDavid van Moolenbroek 	fclose(fp);
99*9f20bfa6SDavid van Moolenbroek 	return pid;
100*9f20bfa6SDavid van Moolenbroek }
101*9f20bfa6SDavid van Moolenbroek 
102*9f20bfa6SDavid van Moolenbroek static int
write_pid(int fd,pid_t pid)103*9f20bfa6SDavid van Moolenbroek write_pid(int fd, pid_t pid)
104*9f20bfa6SDavid van Moolenbroek {
105*9f20bfa6SDavid van Moolenbroek 
106*9f20bfa6SDavid van Moolenbroek 	if (ftruncate(fd, (off_t)0) == -1)
107*9f20bfa6SDavid van Moolenbroek 		return -1;
108*9f20bfa6SDavid van Moolenbroek 	lseek(fd, (off_t)0, SEEK_SET);
109*9f20bfa6SDavid van Moolenbroek 	return dprintf(fd, "%d\n", (int)pid);
110*9f20bfa6SDavid van Moolenbroek }
111*9f20bfa6SDavid van Moolenbroek #endif
112*9f20bfa6SDavid van Moolenbroek 
113*9f20bfa6SDavid van Moolenbroek static void
usage(void)114*9f20bfa6SDavid van Moolenbroek usage(void)
115*9f20bfa6SDavid van Moolenbroek {
116*9f20bfa6SDavid van Moolenbroek 
117*9f20bfa6SDavid van Moolenbroek printf("usage: "PACKAGE"\t[-46ABbDdEGgHJKkLnpqTVw]\n"
118*9f20bfa6SDavid van Moolenbroek 	"\t\t[-C, --nohook hook] [-c, --script script]\n"
119*9f20bfa6SDavid van Moolenbroek 	"\t\t[-e, --env value] [-F, --fqdn FQDN] [-f, --config file]\n"
120*9f20bfa6SDavid van Moolenbroek 	"\t\t[-h, --hostname hostname] [-I, --clientid clientid]\n"
121*9f20bfa6SDavid van Moolenbroek 	"\t\t[-i, --vendorclassid vendorclassid] [-l, --leasetime seconds]\n"
122*9f20bfa6SDavid van Moolenbroek 	"\t\t[-m, --metric metric] [-O, --nooption option]\n"
123*9f20bfa6SDavid van Moolenbroek 	"\t\t[-o, --option option] [-Q, --require option]\n"
124*9f20bfa6SDavid van Moolenbroek 	"\t\t[-r, --request address] [-S, --static value]\n"
125*9f20bfa6SDavid van Moolenbroek 	"\t\t[-s, --inform address[/cidr]] [-t, --timeout seconds]\n"
126*9f20bfa6SDavid van Moolenbroek 	"\t\t[-u, --userclass class] [-v, --vendor code, value]\n"
127*9f20bfa6SDavid van Moolenbroek 	"\t\t[-W, --whitelist address[/cidr]] [-y, --reboot seconds]\n"
128*9f20bfa6SDavid van Moolenbroek 	"\t\t[-X, --blacklist address[/cidr]] [-Z, --denyinterfaces pattern]\n"
129*9f20bfa6SDavid van Moolenbroek 	"\t\t[-z, --allowinterfaces pattern] [interface] [...]\n"
130*9f20bfa6SDavid van Moolenbroek 	"       "PACKAGE"\t-k, --release [interface]\n"
131*9f20bfa6SDavid van Moolenbroek 	"       "PACKAGE"\t-U, --dumplease interface\n"
132*9f20bfa6SDavid van Moolenbroek 	"       "PACKAGE"\t--version\n"
133*9f20bfa6SDavid van Moolenbroek 	"       "PACKAGE"\t-x, --exit [interface]\n");
134*9f20bfa6SDavid van Moolenbroek }
135*9f20bfa6SDavid van Moolenbroek 
136*9f20bfa6SDavid van Moolenbroek static void
free_globals(struct dhcpcd_ctx * ctx)137*9f20bfa6SDavid van Moolenbroek free_globals(struct dhcpcd_ctx *ctx)
138*9f20bfa6SDavid van Moolenbroek {
139*9f20bfa6SDavid van Moolenbroek 	struct dhcp_opt *opt;
140*9f20bfa6SDavid van Moolenbroek 
141*9f20bfa6SDavid van Moolenbroek 	if (ctx->ifac) {
142*9f20bfa6SDavid van Moolenbroek 		for (; ctx->ifac > 0; ctx->ifac--)
143*9f20bfa6SDavid van Moolenbroek 			free(ctx->ifav[ctx->ifac - 1]);
144*9f20bfa6SDavid van Moolenbroek 		free(ctx->ifav);
145*9f20bfa6SDavid van Moolenbroek 		ctx->ifav = NULL;
146*9f20bfa6SDavid van Moolenbroek 	}
147*9f20bfa6SDavid van Moolenbroek 	if (ctx->ifdc) {
148*9f20bfa6SDavid van Moolenbroek 		for (; ctx->ifdc > 0; ctx->ifdc--)
149*9f20bfa6SDavid van Moolenbroek 			free(ctx->ifdv[ctx->ifdc - 1]);
150*9f20bfa6SDavid van Moolenbroek 		free(ctx->ifdv);
151*9f20bfa6SDavid van Moolenbroek 		ctx->ifdv = NULL;
152*9f20bfa6SDavid van Moolenbroek 	}
153*9f20bfa6SDavid van Moolenbroek 	if (ctx->ifcc) {
154*9f20bfa6SDavid van Moolenbroek 		for (; ctx->ifcc > 0; ctx->ifcc--)
155*9f20bfa6SDavid van Moolenbroek 			free(ctx->ifcv[ctx->ifcc - 1]);
156*9f20bfa6SDavid van Moolenbroek 		free(ctx->ifcv);
157*9f20bfa6SDavid van Moolenbroek 		ctx->ifcv = NULL;
158*9f20bfa6SDavid van Moolenbroek 	}
159*9f20bfa6SDavid van Moolenbroek 
160*9f20bfa6SDavid van Moolenbroek #ifdef INET
161*9f20bfa6SDavid van Moolenbroek 	if (ctx->dhcp_opts) {
162*9f20bfa6SDavid van Moolenbroek 		for (opt = ctx->dhcp_opts;
163*9f20bfa6SDavid van Moolenbroek 		    ctx->dhcp_opts_len > 0;
164*9f20bfa6SDavid van Moolenbroek 		    opt++, ctx->dhcp_opts_len--)
165*9f20bfa6SDavid van Moolenbroek 			free_dhcp_opt_embenc(opt);
166*9f20bfa6SDavid van Moolenbroek 		free(ctx->dhcp_opts);
167*9f20bfa6SDavid van Moolenbroek 		ctx->dhcp_opts = NULL;
168*9f20bfa6SDavid van Moolenbroek 	}
169*9f20bfa6SDavid van Moolenbroek #endif
170*9f20bfa6SDavid van Moolenbroek #ifdef INET6
171*9f20bfa6SDavid van Moolenbroek 	if (ctx->nd_opts) {
172*9f20bfa6SDavid van Moolenbroek 		for (opt = ctx->nd_opts;
173*9f20bfa6SDavid van Moolenbroek 		    ctx->nd_opts_len > 0;
174*9f20bfa6SDavid van Moolenbroek 		    opt++, ctx->nd_opts_len--)
175*9f20bfa6SDavid van Moolenbroek 			free_dhcp_opt_embenc(opt);
176*9f20bfa6SDavid van Moolenbroek 		free(ctx->nd_opts);
177*9f20bfa6SDavid van Moolenbroek 		ctx->nd_opts = NULL;
178*9f20bfa6SDavid van Moolenbroek 	}
179*9f20bfa6SDavid van Moolenbroek 	if (ctx->dhcp6_opts) {
180*9f20bfa6SDavid van Moolenbroek 		for (opt = ctx->dhcp6_opts;
181*9f20bfa6SDavid van Moolenbroek 		    ctx->dhcp6_opts_len > 0;
182*9f20bfa6SDavid van Moolenbroek 		    opt++, ctx->dhcp6_opts_len--)
183*9f20bfa6SDavid van Moolenbroek 			free_dhcp_opt_embenc(opt);
184*9f20bfa6SDavid van Moolenbroek 		free(ctx->dhcp6_opts);
185*9f20bfa6SDavid van Moolenbroek 		ctx->dhcp6_opts = NULL;
186*9f20bfa6SDavid van Moolenbroek 	}
187*9f20bfa6SDavid van Moolenbroek #endif
188*9f20bfa6SDavid van Moolenbroek 	if (ctx->vivso) {
189*9f20bfa6SDavid van Moolenbroek 		for (opt = ctx->vivso;
190*9f20bfa6SDavid van Moolenbroek 		    ctx->vivso_len > 0;
191*9f20bfa6SDavid van Moolenbroek 		    opt++, ctx->vivso_len--)
192*9f20bfa6SDavid van Moolenbroek 			free_dhcp_opt_embenc(opt);
193*9f20bfa6SDavid van Moolenbroek 		free(ctx->vivso);
194*9f20bfa6SDavid van Moolenbroek 		ctx->vivso = NULL;
195*9f20bfa6SDavid van Moolenbroek 	}
196*9f20bfa6SDavid van Moolenbroek }
197*9f20bfa6SDavid van Moolenbroek 
198*9f20bfa6SDavid van Moolenbroek static void
handle_exit_timeout(void * arg)199*9f20bfa6SDavid van Moolenbroek handle_exit_timeout(void *arg)
200*9f20bfa6SDavid van Moolenbroek {
201*9f20bfa6SDavid van Moolenbroek 	struct dhcpcd_ctx *ctx;
202*9f20bfa6SDavid van Moolenbroek 
203*9f20bfa6SDavid van Moolenbroek 	ctx = arg;
204*9f20bfa6SDavid van Moolenbroek 	logger(ctx, LOG_ERR, "timed out");
205*9f20bfa6SDavid van Moolenbroek 	if (!(ctx->options & DHCPCD_MASTER)) {
206*9f20bfa6SDavid van Moolenbroek 		eloop_exit(ctx->eloop, EXIT_FAILURE);
207*9f20bfa6SDavid van Moolenbroek 		return;
208*9f20bfa6SDavid van Moolenbroek 	}
209*9f20bfa6SDavid van Moolenbroek 	ctx->options |= DHCPCD_NOWAITIP;
210*9f20bfa6SDavid van Moolenbroek 	dhcpcd_daemonise(ctx);
211*9f20bfa6SDavid van Moolenbroek }
212*9f20bfa6SDavid van Moolenbroek 
213*9f20bfa6SDavid van Moolenbroek static const char *
dhcpcd_af(int af)214*9f20bfa6SDavid van Moolenbroek dhcpcd_af(int af)
215*9f20bfa6SDavid van Moolenbroek {
216*9f20bfa6SDavid van Moolenbroek 
217*9f20bfa6SDavid van Moolenbroek 	switch (af) {
218*9f20bfa6SDavid van Moolenbroek 	case AF_UNSPEC:
219*9f20bfa6SDavid van Moolenbroek 		return "IP";
220*9f20bfa6SDavid van Moolenbroek 	case AF_INET:
221*9f20bfa6SDavid van Moolenbroek 		return "IPv4";
222*9f20bfa6SDavid van Moolenbroek 	case AF_INET6:
223*9f20bfa6SDavid van Moolenbroek 		return "IPv6";
224*9f20bfa6SDavid van Moolenbroek 	default:
225*9f20bfa6SDavid van Moolenbroek 		return NULL;
226*9f20bfa6SDavid van Moolenbroek 	}
227*9f20bfa6SDavid van Moolenbroek }
228*9f20bfa6SDavid van Moolenbroek 
229*9f20bfa6SDavid van Moolenbroek int
dhcpcd_ifafwaiting(const struct interface * ifp)230*9f20bfa6SDavid van Moolenbroek dhcpcd_ifafwaiting(const struct interface *ifp)
231*9f20bfa6SDavid van Moolenbroek {
232*9f20bfa6SDavid van Moolenbroek 	unsigned long long opts;
233*9f20bfa6SDavid van Moolenbroek 
234*9f20bfa6SDavid van Moolenbroek 	opts = ifp->options->options;
235*9f20bfa6SDavid van Moolenbroek 	if (opts & DHCPCD_WAITIP4 && !ipv4_hasaddr(ifp))
236*9f20bfa6SDavid van Moolenbroek 		return AF_INET;
237*9f20bfa6SDavid van Moolenbroek 	if (opts & DHCPCD_WAITIP6 && !ipv6_hasaddr(ifp))
238*9f20bfa6SDavid van Moolenbroek 		return AF_INET6;
239*9f20bfa6SDavid van Moolenbroek 	if (opts & DHCPCD_WAITIP &&
240*9f20bfa6SDavid van Moolenbroek 	    !(opts & (DHCPCD_WAITIP4 | DHCPCD_WAITIP6)) &&
241*9f20bfa6SDavid van Moolenbroek 	    !ipv4_hasaddr(ifp) && !ipv6_hasaddr(ifp))
242*9f20bfa6SDavid van Moolenbroek 		return AF_UNSPEC;
243*9f20bfa6SDavid van Moolenbroek 	return AF_MAX;
244*9f20bfa6SDavid van Moolenbroek }
245*9f20bfa6SDavid van Moolenbroek 
246*9f20bfa6SDavid van Moolenbroek int
dhcpcd_afwaiting(const struct dhcpcd_ctx * ctx)247*9f20bfa6SDavid van Moolenbroek dhcpcd_afwaiting(const struct dhcpcd_ctx *ctx)
248*9f20bfa6SDavid van Moolenbroek {
249*9f20bfa6SDavid van Moolenbroek 	unsigned long long opts;
250*9f20bfa6SDavid van Moolenbroek 	const struct interface *ifp;
251*9f20bfa6SDavid van Moolenbroek 	int af;
252*9f20bfa6SDavid van Moolenbroek 
253*9f20bfa6SDavid van Moolenbroek 	if (!(ctx->options & DHCPCD_WAITOPTS))
254*9f20bfa6SDavid van Moolenbroek 		return AF_MAX;
255*9f20bfa6SDavid van Moolenbroek 
256*9f20bfa6SDavid van Moolenbroek 	opts = ctx->options;
257*9f20bfa6SDavid van Moolenbroek 	TAILQ_FOREACH(ifp, ctx->ifaces, next) {
258*9f20bfa6SDavid van Moolenbroek 		if (opts & (DHCPCD_WAITIP | DHCPCD_WAITIP4) &&
259*9f20bfa6SDavid van Moolenbroek 		    ipv4_hasaddr(ifp))
260*9f20bfa6SDavid van Moolenbroek 			opts &= ~(DHCPCD_WAITIP | DHCPCD_WAITIP4);
261*9f20bfa6SDavid van Moolenbroek 		if (opts & (DHCPCD_WAITIP | DHCPCD_WAITIP6) &&
262*9f20bfa6SDavid van Moolenbroek 		    ipv6_hasaddr(ifp))
263*9f20bfa6SDavid van Moolenbroek 			opts &= ~(DHCPCD_WAITIP | DHCPCD_WAITIP6);
264*9f20bfa6SDavid van Moolenbroek 		if (!(opts & DHCPCD_WAITOPTS))
265*9f20bfa6SDavid van Moolenbroek 			break;
266*9f20bfa6SDavid van Moolenbroek 	}
267*9f20bfa6SDavid van Moolenbroek 	if (opts & DHCPCD_WAITIP)
268*9f20bfa6SDavid van Moolenbroek 		af = AF_UNSPEC;
269*9f20bfa6SDavid van Moolenbroek 	else if (opts & DHCPCD_WAITIP4)
270*9f20bfa6SDavid van Moolenbroek 		af = AF_INET;
271*9f20bfa6SDavid van Moolenbroek 	else if (opts & DHCPCD_WAITIP6)
272*9f20bfa6SDavid van Moolenbroek 		af = AF_INET6;
273*9f20bfa6SDavid van Moolenbroek 	else
274*9f20bfa6SDavid van Moolenbroek 		return AF_MAX;
275*9f20bfa6SDavid van Moolenbroek 	return af;
276*9f20bfa6SDavid van Moolenbroek }
277*9f20bfa6SDavid van Moolenbroek 
278*9f20bfa6SDavid van Moolenbroek static int
dhcpcd_ipwaited(struct dhcpcd_ctx * ctx)279*9f20bfa6SDavid van Moolenbroek dhcpcd_ipwaited(struct dhcpcd_ctx *ctx)
280*9f20bfa6SDavid van Moolenbroek {
281*9f20bfa6SDavid van Moolenbroek 	struct interface *ifp;
282*9f20bfa6SDavid van Moolenbroek 	int af;
283*9f20bfa6SDavid van Moolenbroek 
284*9f20bfa6SDavid van Moolenbroek 	TAILQ_FOREACH(ifp, ctx->ifaces, next) {
285*9f20bfa6SDavid van Moolenbroek 		if ((af = dhcpcd_ifafwaiting(ifp)) != AF_MAX) {
286*9f20bfa6SDavid van Moolenbroek 			logger(ctx, LOG_DEBUG,
287*9f20bfa6SDavid van Moolenbroek 			    "%s: waiting for an %s address",
288*9f20bfa6SDavid van Moolenbroek 			    ifp->name, dhcpcd_af(af));
289*9f20bfa6SDavid van Moolenbroek 			return 0;
290*9f20bfa6SDavid van Moolenbroek 		}
291*9f20bfa6SDavid van Moolenbroek 	}
292*9f20bfa6SDavid van Moolenbroek 
293*9f20bfa6SDavid van Moolenbroek 	if ((af = dhcpcd_afwaiting(ctx)) != AF_MAX) {
294*9f20bfa6SDavid van Moolenbroek 		logger(ctx, LOG_DEBUG,
295*9f20bfa6SDavid van Moolenbroek 		    "waiting for an %s address",
296*9f20bfa6SDavid van Moolenbroek 		    dhcpcd_af(af));
297*9f20bfa6SDavid van Moolenbroek 		return 0;
298*9f20bfa6SDavid van Moolenbroek 	}
299*9f20bfa6SDavid van Moolenbroek 
300*9f20bfa6SDavid van Moolenbroek 	return 1;
301*9f20bfa6SDavid van Moolenbroek }
302*9f20bfa6SDavid van Moolenbroek 
303*9f20bfa6SDavid van Moolenbroek /* Returns the pid of the child, otherwise 0. */
304*9f20bfa6SDavid van Moolenbroek pid_t
dhcpcd_daemonise(struct dhcpcd_ctx * ctx)305*9f20bfa6SDavid van Moolenbroek dhcpcd_daemonise(struct dhcpcd_ctx *ctx)
306*9f20bfa6SDavid van Moolenbroek {
307*9f20bfa6SDavid van Moolenbroek #ifdef THERE_IS_NO_FORK
308*9f20bfa6SDavid van Moolenbroek 	eloop_timeout_delete(ctx->eloop, handle_exit_timeout, ctx);
309*9f20bfa6SDavid van Moolenbroek 	errno = ENOSYS;
310*9f20bfa6SDavid van Moolenbroek 	return 0;
311*9f20bfa6SDavid van Moolenbroek #else
312*9f20bfa6SDavid van Moolenbroek 	pid_t pid;
313*9f20bfa6SDavid van Moolenbroek 	char buf = '\0';
314*9f20bfa6SDavid van Moolenbroek 	int sidpipe[2], fd;
315*9f20bfa6SDavid van Moolenbroek 
316*9f20bfa6SDavid van Moolenbroek 	if (ctx->options & DHCPCD_DAEMONISE &&
317*9f20bfa6SDavid van Moolenbroek 	    !(ctx->options & (DHCPCD_DAEMONISED | DHCPCD_NOWAITIP)))
318*9f20bfa6SDavid van Moolenbroek 	{
319*9f20bfa6SDavid van Moolenbroek 		if (!dhcpcd_ipwaited(ctx))
320*9f20bfa6SDavid van Moolenbroek 			return 0;
321*9f20bfa6SDavid van Moolenbroek 	}
322*9f20bfa6SDavid van Moolenbroek 
323*9f20bfa6SDavid van Moolenbroek 	eloop_timeout_delete(ctx->eloop, handle_exit_timeout, ctx);
324*9f20bfa6SDavid van Moolenbroek 	if (ctx->options & DHCPCD_DAEMONISED ||
325*9f20bfa6SDavid van Moolenbroek 	    !(ctx->options & DHCPCD_DAEMONISE))
326*9f20bfa6SDavid van Moolenbroek 		return 0;
327*9f20bfa6SDavid van Moolenbroek 	/* Setup a signal pipe so parent knows when to exit. */
328*9f20bfa6SDavid van Moolenbroek 	if (pipe(sidpipe) == -1) {
329*9f20bfa6SDavid van Moolenbroek 		logger(ctx, LOG_ERR, "pipe: %m");
330*9f20bfa6SDavid van Moolenbroek 		return 0;
331*9f20bfa6SDavid van Moolenbroek 	}
332*9f20bfa6SDavid van Moolenbroek 	logger(ctx, LOG_DEBUG, "forking to background");
333*9f20bfa6SDavid van Moolenbroek 	switch (pid = fork()) {
334*9f20bfa6SDavid van Moolenbroek 	case -1:
335*9f20bfa6SDavid van Moolenbroek 		logger(ctx, LOG_ERR, "fork: %m");
336*9f20bfa6SDavid van Moolenbroek 		return 0;
337*9f20bfa6SDavid van Moolenbroek 	case 0:
338*9f20bfa6SDavid van Moolenbroek 		setsid();
339*9f20bfa6SDavid van Moolenbroek 		/* Some polling methods don't survive after forking,
340*9f20bfa6SDavid van Moolenbroek 		 * so ensure we can requeue all our events. */
341*9f20bfa6SDavid van Moolenbroek 		if (eloop_requeue(ctx->eloop) == -1) {
342*9f20bfa6SDavid van Moolenbroek 			logger(ctx, LOG_ERR, "eloop_requeue: %m");
343*9f20bfa6SDavid van Moolenbroek 			eloop_exit(ctx->eloop, EXIT_FAILURE);
344*9f20bfa6SDavid van Moolenbroek 		}
345*9f20bfa6SDavid van Moolenbroek 		/* Notify parent it's safe to exit as we've detached. */
346*9f20bfa6SDavid van Moolenbroek 		close(sidpipe[0]);
347*9f20bfa6SDavid van Moolenbroek 		if (write(sidpipe[1], &buf, 1) == -1)
348*9f20bfa6SDavid van Moolenbroek 			logger(ctx, LOG_ERR, "failed to notify parent: %m");
349*9f20bfa6SDavid van Moolenbroek 		close(sidpipe[1]);
350*9f20bfa6SDavid van Moolenbroek 		if ((fd = open(_PATH_DEVNULL, O_RDWR, 0)) != -1) {
351*9f20bfa6SDavid van Moolenbroek 			dup2(fd, STDIN_FILENO);
352*9f20bfa6SDavid van Moolenbroek 			dup2(fd, STDOUT_FILENO);
353*9f20bfa6SDavid van Moolenbroek 			dup2(fd, STDERR_FILENO);
354*9f20bfa6SDavid van Moolenbroek 			close(fd);
355*9f20bfa6SDavid van Moolenbroek 		}
356*9f20bfa6SDavid van Moolenbroek 		break;
357*9f20bfa6SDavid van Moolenbroek 	default:
358*9f20bfa6SDavid van Moolenbroek 		/* Wait for child to detach */
359*9f20bfa6SDavid van Moolenbroek 		close(sidpipe[1]);
360*9f20bfa6SDavid van Moolenbroek 		if (read(sidpipe[0], &buf, 1) == -1)
361*9f20bfa6SDavid van Moolenbroek 			logger(ctx, LOG_ERR, "failed to read child: %m");
362*9f20bfa6SDavid van Moolenbroek 		close(sidpipe[0]);
363*9f20bfa6SDavid van Moolenbroek 		break;
364*9f20bfa6SDavid van Moolenbroek 	}
365*9f20bfa6SDavid van Moolenbroek 	/* Done with the fd now */
366*9f20bfa6SDavid van Moolenbroek 	if (pid != 0) {
367*9f20bfa6SDavid van Moolenbroek 		logger(ctx, LOG_INFO, "forked to background, child pid %d", pid);
368*9f20bfa6SDavid van Moolenbroek 		write_pid(ctx->pid_fd, pid);
369*9f20bfa6SDavid van Moolenbroek 		close(ctx->pid_fd);
370*9f20bfa6SDavid van Moolenbroek 		ctx->pid_fd = -1;
371*9f20bfa6SDavid van Moolenbroek 		ctx->options |= DHCPCD_FORKED;
372*9f20bfa6SDavid van Moolenbroek 		eloop_exit(ctx->eloop, EXIT_SUCCESS);
373*9f20bfa6SDavid van Moolenbroek 		return pid;
374*9f20bfa6SDavid van Moolenbroek 	}
375*9f20bfa6SDavid van Moolenbroek 	ctx->options |= DHCPCD_DAEMONISED;
376*9f20bfa6SDavid van Moolenbroek 	return pid;
377*9f20bfa6SDavid van Moolenbroek #endif
378*9f20bfa6SDavid van Moolenbroek }
379*9f20bfa6SDavid van Moolenbroek 
380*9f20bfa6SDavid van Moolenbroek static void
dhcpcd_drop(struct interface * ifp,int stop)381*9f20bfa6SDavid van Moolenbroek dhcpcd_drop(struct interface *ifp, int stop)
382*9f20bfa6SDavid van Moolenbroek {
383*9f20bfa6SDavid van Moolenbroek 
384*9f20bfa6SDavid van Moolenbroek 	dhcp6_drop(ifp, stop ? NULL : "EXPIRE6");
385*9f20bfa6SDavid van Moolenbroek 	ipv6nd_drop(ifp);
386*9f20bfa6SDavid van Moolenbroek 	ipv6_drop(ifp);
387*9f20bfa6SDavid van Moolenbroek 	ipv4ll_drop(ifp);
388*9f20bfa6SDavid van Moolenbroek 	dhcp_drop(ifp, stop ? "STOP" : "EXPIRE");
389*9f20bfa6SDavid van Moolenbroek 	arp_close(ifp);
390*9f20bfa6SDavid van Moolenbroek }
391*9f20bfa6SDavid van Moolenbroek 
392*9f20bfa6SDavid van Moolenbroek static void
stop_interface(struct interface * ifp)393*9f20bfa6SDavid van Moolenbroek stop_interface(struct interface *ifp)
394*9f20bfa6SDavid van Moolenbroek {
395*9f20bfa6SDavid van Moolenbroek 	struct dhcpcd_ctx *ctx;
396*9f20bfa6SDavid van Moolenbroek 
397*9f20bfa6SDavid van Moolenbroek 	ctx = ifp->ctx;
398*9f20bfa6SDavid van Moolenbroek 	logger(ctx, LOG_INFO, "%s: removing interface", ifp->name);
399*9f20bfa6SDavid van Moolenbroek 	ifp->options->options |= DHCPCD_STOPPING;
400*9f20bfa6SDavid van Moolenbroek 
401*9f20bfa6SDavid van Moolenbroek 	dhcpcd_drop(ifp, 1);
402*9f20bfa6SDavid van Moolenbroek 	if (ifp->options->options & DHCPCD_DEPARTED)
403*9f20bfa6SDavid van Moolenbroek 		script_runreason(ifp, "DEPARTED");
404*9f20bfa6SDavid van Moolenbroek 	else
405*9f20bfa6SDavid van Moolenbroek 		script_runreason(ifp, "STOPPED");
406*9f20bfa6SDavid van Moolenbroek 
407*9f20bfa6SDavid van Moolenbroek 	/* Delete all timeouts for the interfaces */
408*9f20bfa6SDavid van Moolenbroek 	eloop_q_timeout_delete(ctx->eloop, 0, NULL, ifp);
409*9f20bfa6SDavid van Moolenbroek 
410*9f20bfa6SDavid van Moolenbroek 	/* Remove the interface from our list */
411*9f20bfa6SDavid van Moolenbroek 	TAILQ_REMOVE(ifp->ctx->ifaces, ifp, next);
412*9f20bfa6SDavid van Moolenbroek 	if_free(ifp);
413*9f20bfa6SDavid van Moolenbroek 
414*9f20bfa6SDavid van Moolenbroek 	if (!(ctx->options & (DHCPCD_MASTER | DHCPCD_TEST)))
415*9f20bfa6SDavid van Moolenbroek 		eloop_exit(ctx->eloop, EXIT_FAILURE);
416*9f20bfa6SDavid van Moolenbroek }
417*9f20bfa6SDavid van Moolenbroek 
418*9f20bfa6SDavid van Moolenbroek static void
configure_interface1(struct interface * ifp)419*9f20bfa6SDavid van Moolenbroek configure_interface1(struct interface *ifp)
420*9f20bfa6SDavid van Moolenbroek {
421*9f20bfa6SDavid van Moolenbroek 	struct if_options *ifo = ifp->options;
422*9f20bfa6SDavid van Moolenbroek 	int ra_global, ra_iface;
423*9f20bfa6SDavid van Moolenbroek #ifdef INET6
424*9f20bfa6SDavid van Moolenbroek 	size_t i;
425*9f20bfa6SDavid van Moolenbroek #endif
426*9f20bfa6SDavid van Moolenbroek 
427*9f20bfa6SDavid van Moolenbroek 	/* Do any platform specific configuration */
428*9f20bfa6SDavid van Moolenbroek 	if_conf(ifp);
429*9f20bfa6SDavid van Moolenbroek 
430*9f20bfa6SDavid van Moolenbroek 	/* If we want to release a lease, we can't really persist the
431*9f20bfa6SDavid van Moolenbroek 	 * address either. */
432*9f20bfa6SDavid van Moolenbroek 	if (ifo->options & DHCPCD_RELEASE)
433*9f20bfa6SDavid van Moolenbroek 		ifo->options &= ~DHCPCD_PERSISTENT;
434*9f20bfa6SDavid van Moolenbroek 
435*9f20bfa6SDavid van Moolenbroek 	if (ifp->flags & IFF_POINTOPOINT && !(ifo->options & DHCPCD_INFORM))
436*9f20bfa6SDavid van Moolenbroek 		ifo->options |= DHCPCD_STATIC;
437*9f20bfa6SDavid van Moolenbroek 	if (ifp->flags & IFF_NOARP ||
438*9f20bfa6SDavid van Moolenbroek 	    ifo->options & (DHCPCD_INFORM | DHCPCD_STATIC))
439*9f20bfa6SDavid van Moolenbroek 		ifo->options &= ~DHCPCD_IPV4LL;
440*9f20bfa6SDavid van Moolenbroek 	if (ifp->flags & (IFF_POINTOPOINT | IFF_LOOPBACK) ||
441*9f20bfa6SDavid van Moolenbroek 	    !(ifp->flags & IFF_MULTICAST))
442*9f20bfa6SDavid van Moolenbroek 		ifo->options &= ~DHCPCD_IPV6RS;
443*9f20bfa6SDavid van Moolenbroek 
444*9f20bfa6SDavid van Moolenbroek 	if (ifo->metric != -1)
445*9f20bfa6SDavid van Moolenbroek 		ifp->metric = (unsigned int)ifo->metric;
446*9f20bfa6SDavid van Moolenbroek 
447*9f20bfa6SDavid van Moolenbroek 	if (!(ifo->options & DHCPCD_IPV4))
448*9f20bfa6SDavid van Moolenbroek 		ifo->options &= ~(DHCPCD_DHCP | DHCPCD_IPV4LL | DHCPCD_WAITIP4);
449*9f20bfa6SDavid van Moolenbroek 
450*9f20bfa6SDavid van Moolenbroek 	if (!(ifo->options & DHCPCD_IPV6))
451*9f20bfa6SDavid van Moolenbroek 		ifo->options &=
452*9f20bfa6SDavid van Moolenbroek 		    ~(DHCPCD_IPV6RS | DHCPCD_DHCP6 | DHCPCD_WAITIP6);
453*9f20bfa6SDavid van Moolenbroek 
454*9f20bfa6SDavid van Moolenbroek 	if (ifo->options & DHCPCD_SLAACPRIVATE &&
455*9f20bfa6SDavid van Moolenbroek 	    !(ifp->ctx->options & (DHCPCD_DUMPLEASE | DHCPCD_TEST)))
456*9f20bfa6SDavid van Moolenbroek 		ifo->options |= DHCPCD_IPV6RA_OWN;
457*9f20bfa6SDavid van Moolenbroek 
458*9f20bfa6SDavid van Moolenbroek 	/* We want to disable kernel interface RA as early as possible. */
459*9f20bfa6SDavid van Moolenbroek 	if (ifo->options & DHCPCD_IPV6RS &&
460*9f20bfa6SDavid van Moolenbroek 	    !(ifp->ctx->options & DHCPCD_DUMPLEASE))
461*9f20bfa6SDavid van Moolenbroek 	{
462*9f20bfa6SDavid van Moolenbroek 		/* If not doing any DHCP, disable the RDNSS requirement. */
463*9f20bfa6SDavid van Moolenbroek 		if (!(ifo->options & (DHCPCD_DHCP | DHCPCD_DHCP6)))
464*9f20bfa6SDavid van Moolenbroek 			ifo->options &= ~DHCPCD_IPV6RA_REQRDNSS;
465*9f20bfa6SDavid van Moolenbroek 		ra_global = if_checkipv6(ifp->ctx, NULL,
466*9f20bfa6SDavid van Moolenbroek 		    ifp->ctx->options & DHCPCD_IPV6RA_OWN ? 1 : 0);
467*9f20bfa6SDavid van Moolenbroek 		ra_iface = if_checkipv6(ifp->ctx, ifp,
468*9f20bfa6SDavid van Moolenbroek 		    ifp->options->options & DHCPCD_IPV6RA_OWN ? 1 : 0);
469*9f20bfa6SDavid van Moolenbroek 		if (ra_global == -1 || ra_iface == -1)
470*9f20bfa6SDavid van Moolenbroek 			ifo->options &= ~DHCPCD_IPV6RS;
471*9f20bfa6SDavid van Moolenbroek 		else if (ra_iface == 0 &&
472*9f20bfa6SDavid van Moolenbroek 		    !(ifp->ctx->options & DHCPCD_TEST))
473*9f20bfa6SDavid van Moolenbroek 			ifo->options |= DHCPCD_IPV6RA_OWN;
474*9f20bfa6SDavid van Moolenbroek 	}
475*9f20bfa6SDavid van Moolenbroek 
476*9f20bfa6SDavid van Moolenbroek 	/* If we haven't specified a ClientID and our hardware address
477*9f20bfa6SDavid van Moolenbroek 	 * length is greater than DHCP_CHADDR_LEN then we enforce a ClientID
478*9f20bfa6SDavid van Moolenbroek 	 * of the hardware address family and the hardware address.
479*9f20bfa6SDavid van Moolenbroek 	 * If there is no hardware address and no ClientID set,
480*9f20bfa6SDavid van Moolenbroek 	 * force a DUID based ClientID. */
481*9f20bfa6SDavid van Moolenbroek 	if (ifp->hwlen > DHCP_CHADDR_LEN)
482*9f20bfa6SDavid van Moolenbroek 		ifo->options |= DHCPCD_CLIENTID;
483*9f20bfa6SDavid van Moolenbroek 	else if (ifp->hwlen == 0 && !(ifo->options & DHCPCD_CLIENTID))
484*9f20bfa6SDavid van Moolenbroek 		ifo->options |= DHCPCD_CLIENTID | DHCPCD_DUID;
485*9f20bfa6SDavid van Moolenbroek 
486*9f20bfa6SDavid van Moolenbroek 	/* Firewire and InfiniBand interfaces require ClientID and
487*9f20bfa6SDavid van Moolenbroek 	 * the broadcast option being set. */
488*9f20bfa6SDavid van Moolenbroek 	switch (ifp->family) {
489*9f20bfa6SDavid van Moolenbroek 	case ARPHRD_IEEE1394:	/* FALLTHROUGH */
490*9f20bfa6SDavid van Moolenbroek 	case ARPHRD_INFINIBAND:
491*9f20bfa6SDavid van Moolenbroek 		ifo->options |= DHCPCD_CLIENTID | DHCPCD_BROADCAST;
492*9f20bfa6SDavid van Moolenbroek 		break;
493*9f20bfa6SDavid van Moolenbroek 	}
494*9f20bfa6SDavid van Moolenbroek 
495*9f20bfa6SDavid van Moolenbroek 	if (!(ifo->options & DHCPCD_IAID)) {
496*9f20bfa6SDavid van Moolenbroek 		/*
497*9f20bfa6SDavid van Moolenbroek 		 * An IAID is for identifying a unqiue interface within
498*9f20bfa6SDavid van Moolenbroek 		 * the client. It is 4 bytes long. Working out a default
499*9f20bfa6SDavid van Moolenbroek 		 * value is problematic.
500*9f20bfa6SDavid van Moolenbroek 		 *
501*9f20bfa6SDavid van Moolenbroek 		 * Interface name and number are not stable
502*9f20bfa6SDavid van Moolenbroek 		 * between different OS's. Some OS's also cannot make
503*9f20bfa6SDavid van Moolenbroek 		 * up their mind what the interface should be called
504*9f20bfa6SDavid van Moolenbroek 		 * (yes, udev, I'm looking at you).
505*9f20bfa6SDavid van Moolenbroek 		 * Also, the name could be longer than 4 bytes.
506*9f20bfa6SDavid van Moolenbroek 		 * Also, with pluggable interfaces the name and index
507*9f20bfa6SDavid van Moolenbroek 		 * could easily get swapped per actual interface.
508*9f20bfa6SDavid van Moolenbroek 		 *
509*9f20bfa6SDavid van Moolenbroek 		 * The MAC address is 6 bytes long, the final 3
510*9f20bfa6SDavid van Moolenbroek 		 * being unique to the manufacturer and the initial 3
511*9f20bfa6SDavid van Moolenbroek 		 * being unique to the organisation which makes it.
512*9f20bfa6SDavid van Moolenbroek 		 * We could use the last 4 bytes of the MAC address
513*9f20bfa6SDavid van Moolenbroek 		 * as the IAID as it's the most stable part given the
514*9f20bfa6SDavid van Moolenbroek 		 * above, but equally it's not guaranteed to be
515*9f20bfa6SDavid van Moolenbroek 		 * unique.
516*9f20bfa6SDavid van Moolenbroek 		 *
517*9f20bfa6SDavid van Moolenbroek 		 * Given the above, and our need to reliably work
518*9f20bfa6SDavid van Moolenbroek 		 * between reboots without persitent storage,
519*9f20bfa6SDavid van Moolenbroek 		 * generating the IAID from the MAC address is the only
520*9f20bfa6SDavid van Moolenbroek 		 * logical default.
521*9f20bfa6SDavid van Moolenbroek 		 *
522*9f20bfa6SDavid van Moolenbroek 		 * dhclient uses the last 4 bytes of the MAC address.
523*9f20bfa6SDavid van Moolenbroek 		 * dibbler uses an increamenting counter.
524*9f20bfa6SDavid van Moolenbroek 		 * wide-dhcpv6 uses 0 or a configured value.
525*9f20bfa6SDavid van Moolenbroek 		 * odhcp6c uses 1.
526*9f20bfa6SDavid van Moolenbroek 		 * Windows 7 uses the first 3 bytes of the MAC address
527*9f20bfa6SDavid van Moolenbroek 		 * and an unknown byte.
528*9f20bfa6SDavid van Moolenbroek 		 * dhcpcd-6.1.0 and earlier used the interface name,
529*9f20bfa6SDavid van Moolenbroek 		 * falling back to interface index if name > 4.
530*9f20bfa6SDavid van Moolenbroek 		 */
531*9f20bfa6SDavid van Moolenbroek 		if (ifp->hwlen >= sizeof(ifo->iaid))
532*9f20bfa6SDavid van Moolenbroek 			memcpy(ifo->iaid,
533*9f20bfa6SDavid van Moolenbroek 			    ifp->hwaddr + ifp->hwlen - sizeof(ifo->iaid),
534*9f20bfa6SDavid van Moolenbroek 			    sizeof(ifo->iaid));
535*9f20bfa6SDavid van Moolenbroek 		else {
536*9f20bfa6SDavid van Moolenbroek 			uint32_t len;
537*9f20bfa6SDavid van Moolenbroek 
538*9f20bfa6SDavid van Moolenbroek 			len = (uint32_t)strlen(ifp->name);
539*9f20bfa6SDavid van Moolenbroek 			if (len <= sizeof(ifo->iaid)) {
540*9f20bfa6SDavid van Moolenbroek 				memcpy(ifo->iaid, ifp->name, len);
541*9f20bfa6SDavid van Moolenbroek 				if (len < sizeof(ifo->iaid))
542*9f20bfa6SDavid van Moolenbroek 					memset(ifo->iaid + len, 0,
543*9f20bfa6SDavid van Moolenbroek 					    sizeof(ifo->iaid) - len);
544*9f20bfa6SDavid van Moolenbroek 			} else {
545*9f20bfa6SDavid van Moolenbroek 				/* IAID is the same size as a uint32_t */
546*9f20bfa6SDavid van Moolenbroek 				len = htonl(ifp->index);
547*9f20bfa6SDavid van Moolenbroek 				memcpy(ifo->iaid, &len, sizeof(len));
548*9f20bfa6SDavid van Moolenbroek 			}
549*9f20bfa6SDavid van Moolenbroek 		}
550*9f20bfa6SDavid van Moolenbroek 		ifo->options |= DHCPCD_IAID;
551*9f20bfa6SDavid van Moolenbroek 	}
552*9f20bfa6SDavid van Moolenbroek 
553*9f20bfa6SDavid van Moolenbroek #ifdef INET6
554*9f20bfa6SDavid van Moolenbroek 	if (ifo->ia_len == 0 && ifo->options & DHCPCD_IPV6 &&
555*9f20bfa6SDavid van Moolenbroek 	    ifp->name[0] != '\0')
556*9f20bfa6SDavid van Moolenbroek 	{
557*9f20bfa6SDavid van Moolenbroek 		ifo->ia = malloc(sizeof(*ifo->ia));
558*9f20bfa6SDavid van Moolenbroek 		if (ifo->ia == NULL)
559*9f20bfa6SDavid van Moolenbroek 			logger(ifp->ctx, LOG_ERR, "%s: %m", __func__);
560*9f20bfa6SDavid van Moolenbroek 		else {
561*9f20bfa6SDavid van Moolenbroek 			ifo->ia_len = 1;
562*9f20bfa6SDavid van Moolenbroek 			ifo->ia->ia_type = D6_OPTION_IA_NA;
563*9f20bfa6SDavid van Moolenbroek 			memcpy(ifo->ia->iaid, ifo->iaid, sizeof(ifo->iaid));
564*9f20bfa6SDavid van Moolenbroek 			memset(&ifo->ia->addr, 0, sizeof(ifo->ia->addr));
565*9f20bfa6SDavid van Moolenbroek 			ifo->ia->sla = NULL;
566*9f20bfa6SDavid van Moolenbroek 			ifo->ia->sla_len = 0;
567*9f20bfa6SDavid van Moolenbroek 		}
568*9f20bfa6SDavid van Moolenbroek 	} else {
569*9f20bfa6SDavid van Moolenbroek 		for (i = 0; i < ifo->ia_len; i++) {
570*9f20bfa6SDavid van Moolenbroek 			if (!ifo->ia[i].iaid_set) {
571*9f20bfa6SDavid van Moolenbroek 				memcpy(&ifo->ia[i].iaid, ifo->iaid,
572*9f20bfa6SDavid van Moolenbroek 				    sizeof(ifo->ia[i].iaid));
573*9f20bfa6SDavid van Moolenbroek 				ifo->ia[i].iaid_set = 1;
574*9f20bfa6SDavid van Moolenbroek 			}
575*9f20bfa6SDavid van Moolenbroek 		}
576*9f20bfa6SDavid van Moolenbroek 	}
577*9f20bfa6SDavid van Moolenbroek #endif
578*9f20bfa6SDavid van Moolenbroek }
579*9f20bfa6SDavid van Moolenbroek 
580*9f20bfa6SDavid van Moolenbroek int
dhcpcd_selectprofile(struct interface * ifp,const char * profile)581*9f20bfa6SDavid van Moolenbroek dhcpcd_selectprofile(struct interface *ifp, const char *profile)
582*9f20bfa6SDavid van Moolenbroek {
583*9f20bfa6SDavid van Moolenbroek 	struct if_options *ifo;
584*9f20bfa6SDavid van Moolenbroek 	char pssid[PROFILE_LEN];
585*9f20bfa6SDavid van Moolenbroek 
586*9f20bfa6SDavid van Moolenbroek 	if (ifp->ssid_len) {
587*9f20bfa6SDavid van Moolenbroek 		ssize_t r;
588*9f20bfa6SDavid van Moolenbroek 
589*9f20bfa6SDavid van Moolenbroek 		r = print_string(pssid, sizeof(pssid), ESCSTRING,
590*9f20bfa6SDavid van Moolenbroek 		    ifp->ssid, ifp->ssid_len);
591*9f20bfa6SDavid van Moolenbroek 		if (r == -1) {
592*9f20bfa6SDavid van Moolenbroek 			logger(ifp->ctx, LOG_ERR,
593*9f20bfa6SDavid van Moolenbroek 			    "%s: %s: %m", ifp->name, __func__);
594*9f20bfa6SDavid van Moolenbroek 			pssid[0] = '\0';
595*9f20bfa6SDavid van Moolenbroek 		}
596*9f20bfa6SDavid van Moolenbroek 	} else
597*9f20bfa6SDavid van Moolenbroek 		pssid[0] = '\0';
598*9f20bfa6SDavid van Moolenbroek 	ifo = read_config(ifp->ctx, ifp->name, pssid, profile);
599*9f20bfa6SDavid van Moolenbroek 	if (ifo == NULL) {
600*9f20bfa6SDavid van Moolenbroek 		logger(ifp->ctx, LOG_DEBUG, "%s: no profile %s",
601*9f20bfa6SDavid van Moolenbroek 		    ifp->name, profile);
602*9f20bfa6SDavid van Moolenbroek 		return -1;
603*9f20bfa6SDavid van Moolenbroek 	}
604*9f20bfa6SDavid van Moolenbroek 	if (profile != NULL) {
605*9f20bfa6SDavid van Moolenbroek 		strlcpy(ifp->profile, profile, sizeof(ifp->profile));
606*9f20bfa6SDavid van Moolenbroek 		logger(ifp->ctx, LOG_INFO, "%s: selected profile %s",
607*9f20bfa6SDavid van Moolenbroek 		    ifp->name, profile);
608*9f20bfa6SDavid van Moolenbroek 	} else
609*9f20bfa6SDavid van Moolenbroek 		*ifp->profile = '\0';
610*9f20bfa6SDavid van Moolenbroek 
611*9f20bfa6SDavid van Moolenbroek 	free_options(ifp->options);
612*9f20bfa6SDavid van Moolenbroek 	ifp->options = ifo;
613*9f20bfa6SDavid van Moolenbroek 	if (profile)
614*9f20bfa6SDavid van Moolenbroek 		configure_interface1(ifp);
615*9f20bfa6SDavid van Moolenbroek 	return 1;
616*9f20bfa6SDavid van Moolenbroek }
617*9f20bfa6SDavid van Moolenbroek 
618*9f20bfa6SDavid van Moolenbroek static void
configure_interface(struct interface * ifp,int argc,char ** argv,unsigned long long options)619*9f20bfa6SDavid van Moolenbroek configure_interface(struct interface *ifp, int argc, char **argv,
620*9f20bfa6SDavid van Moolenbroek     unsigned long long options)
621*9f20bfa6SDavid van Moolenbroek {
622*9f20bfa6SDavid van Moolenbroek 	time_t old;
623*9f20bfa6SDavid van Moolenbroek 
624*9f20bfa6SDavid van Moolenbroek 	old = ifp->options ? ifp->options->mtime : 0;
625*9f20bfa6SDavid van Moolenbroek 	dhcpcd_selectprofile(ifp, NULL);
626*9f20bfa6SDavid van Moolenbroek 	add_options(ifp->ctx, ifp->name, ifp->options, argc, argv);
627*9f20bfa6SDavid van Moolenbroek 	ifp->options->options |= options;
628*9f20bfa6SDavid van Moolenbroek 	configure_interface1(ifp);
629*9f20bfa6SDavid van Moolenbroek 
630*9f20bfa6SDavid van Moolenbroek 	/* If the mtime has changed drop any old lease */
631*9f20bfa6SDavid van Moolenbroek 	if (ifp->options && old != 0 && ifp->options->mtime != old) {
632*9f20bfa6SDavid van Moolenbroek 		logger(ifp->ctx, LOG_WARNING,
633*9f20bfa6SDavid van Moolenbroek 		    "%s: confile file changed, expiring leases", ifp->name);
634*9f20bfa6SDavid van Moolenbroek 		dhcpcd_drop(ifp, 0);
635*9f20bfa6SDavid van Moolenbroek 	}
636*9f20bfa6SDavid van Moolenbroek }
637*9f20bfa6SDavid van Moolenbroek 
638*9f20bfa6SDavid van Moolenbroek static void
dhcpcd_pollup(void * arg)639*9f20bfa6SDavid van Moolenbroek dhcpcd_pollup(void *arg)
640*9f20bfa6SDavid van Moolenbroek {
641*9f20bfa6SDavid van Moolenbroek 	struct interface *ifp = arg;
642*9f20bfa6SDavid van Moolenbroek 	int carrier;
643*9f20bfa6SDavid van Moolenbroek 
644*9f20bfa6SDavid van Moolenbroek 	carrier = if_carrier(ifp); /* will set ifp->flags */
645*9f20bfa6SDavid van Moolenbroek 	if (carrier == LINK_UP && !(ifp->flags & IFF_UP)) {
646*9f20bfa6SDavid van Moolenbroek 		struct timespec tv;
647*9f20bfa6SDavid van Moolenbroek 
648*9f20bfa6SDavid van Moolenbroek 		tv.tv_sec = 0;
649*9f20bfa6SDavid van Moolenbroek 		tv.tv_nsec = IF_POLL_UP * NSEC_PER_MSEC;
650*9f20bfa6SDavid van Moolenbroek 		eloop_timeout_add_tv(ifp->ctx->eloop, &tv, dhcpcd_pollup, ifp);
651*9f20bfa6SDavid van Moolenbroek 		return;
652*9f20bfa6SDavid van Moolenbroek 	}
653*9f20bfa6SDavid van Moolenbroek 
654*9f20bfa6SDavid van Moolenbroek 	dhcpcd_handlecarrier(ifp->ctx, carrier, ifp->flags, ifp->name);
655*9f20bfa6SDavid van Moolenbroek }
656*9f20bfa6SDavid van Moolenbroek 
657*9f20bfa6SDavid van Moolenbroek void
dhcpcd_handlecarrier(struct dhcpcd_ctx * ctx,int carrier,unsigned int flags,const char * ifname)658*9f20bfa6SDavid van Moolenbroek dhcpcd_handlecarrier(struct dhcpcd_ctx *ctx, int carrier, unsigned int flags,
659*9f20bfa6SDavid van Moolenbroek     const char *ifname)
660*9f20bfa6SDavid van Moolenbroek {
661*9f20bfa6SDavid van Moolenbroek 	struct interface *ifp;
662*9f20bfa6SDavid van Moolenbroek 
663*9f20bfa6SDavid van Moolenbroek 	ifp = if_find(ctx->ifaces, ifname);
664*9f20bfa6SDavid van Moolenbroek 	if (ifp == NULL || !(ifp->options->options & DHCPCD_LINK))
665*9f20bfa6SDavid van Moolenbroek 		return;
666*9f20bfa6SDavid van Moolenbroek 
667*9f20bfa6SDavid van Moolenbroek 	switch(carrier) {
668*9f20bfa6SDavid van Moolenbroek 	case LINK_UNKNOWN:
669*9f20bfa6SDavid van Moolenbroek 		carrier = if_carrier(ifp); /* will set ifp->flags */
670*9f20bfa6SDavid van Moolenbroek 		break;
671*9f20bfa6SDavid van Moolenbroek 	case LINK_UP:
672*9f20bfa6SDavid van Moolenbroek 		/* we have a carrier! Still need to check for IFF_UP */
673*9f20bfa6SDavid van Moolenbroek 		if (flags & IFF_UP)
674*9f20bfa6SDavid van Moolenbroek 			ifp->flags = flags;
675*9f20bfa6SDavid van Moolenbroek 		else {
676*9f20bfa6SDavid van Moolenbroek 			/* So we need to poll for IFF_UP as there is no
677*9f20bfa6SDavid van Moolenbroek 			 * kernel notification when it's set. */
678*9f20bfa6SDavid van Moolenbroek 			dhcpcd_pollup(ifp);
679*9f20bfa6SDavid van Moolenbroek 			return;
680*9f20bfa6SDavid van Moolenbroek 		}
681*9f20bfa6SDavid van Moolenbroek 		break;
682*9f20bfa6SDavid van Moolenbroek 	default:
683*9f20bfa6SDavid van Moolenbroek 		ifp->flags = flags;
684*9f20bfa6SDavid van Moolenbroek 	}
685*9f20bfa6SDavid van Moolenbroek 
686*9f20bfa6SDavid van Moolenbroek 	/* If we here, we don't need to poll for IFF_UP any longer
687*9f20bfa6SDavid van Moolenbroek 	 * if generated by a kernel event. */
688*9f20bfa6SDavid van Moolenbroek 	eloop_timeout_delete(ifp->ctx->eloop, dhcpcd_pollup, ifp);
689*9f20bfa6SDavid van Moolenbroek 
690*9f20bfa6SDavid van Moolenbroek 	if (carrier == LINK_UNKNOWN) {
691*9f20bfa6SDavid van Moolenbroek 		if (errno != ENOTTY) /* For example a PPP link on BSD */
692*9f20bfa6SDavid van Moolenbroek 			logger(ctx, LOG_ERR, "%s: carrier_status: %m", ifname);
693*9f20bfa6SDavid van Moolenbroek 	} else if (carrier == LINK_DOWN || (ifp->flags & IFF_UP) == 0) {
694*9f20bfa6SDavid van Moolenbroek 		if (ifp->carrier != LINK_DOWN) {
695*9f20bfa6SDavid van Moolenbroek 			if (ifp->carrier == LINK_UP)
696*9f20bfa6SDavid van Moolenbroek 				logger(ctx, LOG_INFO, "%s: carrier lost",
697*9f20bfa6SDavid van Moolenbroek 				    ifp->name);
698*9f20bfa6SDavid van Moolenbroek 			ifp->carrier = LINK_DOWN;
699*9f20bfa6SDavid van Moolenbroek 			script_runreason(ifp, "NOCARRIER");
700*9f20bfa6SDavid van Moolenbroek #ifdef NOCARRIER_PRESERVE_IP
701*9f20bfa6SDavid van Moolenbroek 			arp_close(ifp);
702*9f20bfa6SDavid van Moolenbroek 			dhcp_abort(ifp);
703*9f20bfa6SDavid van Moolenbroek 			if_sortinterfaces(ctx);
704*9f20bfa6SDavid van Moolenbroek 			ipv4_preferanother(ifp);
705*9f20bfa6SDavid van Moolenbroek 			ipv6nd_expire(ifp, 0);
706*9f20bfa6SDavid van Moolenbroek #else
707*9f20bfa6SDavid van Moolenbroek 			dhcpcd_drop(ifp, 0);
708*9f20bfa6SDavid van Moolenbroek #endif
709*9f20bfa6SDavid van Moolenbroek 		}
710*9f20bfa6SDavid van Moolenbroek 	} else if (carrier == LINK_UP && ifp->flags & IFF_UP) {
711*9f20bfa6SDavid van Moolenbroek 		if (ifp->carrier != LINK_UP) {
712*9f20bfa6SDavid van Moolenbroek 			logger(ctx, LOG_INFO, "%s: carrier acquired",
713*9f20bfa6SDavid van Moolenbroek 			    ifp->name);
714*9f20bfa6SDavid van Moolenbroek 			ifp->carrier = LINK_UP;
715*9f20bfa6SDavid van Moolenbroek #if !defined(__linux__) && !defined(__NetBSD__)
716*9f20bfa6SDavid van Moolenbroek 			/* BSD does not emit RTM_NEWADDR or RTM_CHGADDR when the
717*9f20bfa6SDavid van Moolenbroek 			 * hardware address changes so we have to go
718*9f20bfa6SDavid van Moolenbroek 			 * through the disovery process to work it out. */
719*9f20bfa6SDavid van Moolenbroek 			dhcpcd_handleinterface(ctx, 0, ifp->name);
720*9f20bfa6SDavid van Moolenbroek #endif
721*9f20bfa6SDavid van Moolenbroek 			if (ifp->wireless) {
722*9f20bfa6SDavid van Moolenbroek 				uint8_t ossid[IF_SSIDSIZE];
723*9f20bfa6SDavid van Moolenbroek #ifdef NOCARRIER_PRESERVE_IP
724*9f20bfa6SDavid van Moolenbroek 				size_t olen;
725*9f20bfa6SDavid van Moolenbroek 
726*9f20bfa6SDavid van Moolenbroek 				olen = ifp->ssid_len;
727*9f20bfa6SDavid van Moolenbroek #endif
728*9f20bfa6SDavid van Moolenbroek 				memcpy(ossid, ifp->ssid, ifp->ssid_len);
729*9f20bfa6SDavid van Moolenbroek 				if_getssid(ifp);
730*9f20bfa6SDavid van Moolenbroek #ifdef NOCARRIER_PRESERVE_IP
731*9f20bfa6SDavid van Moolenbroek 				/* If we changed SSID network, drop leases */
732*9f20bfa6SDavid van Moolenbroek 				if (ifp->ssid_len != olen ||
733*9f20bfa6SDavid van Moolenbroek 				    memcmp(ifp->ssid, ossid, ifp->ssid_len))
734*9f20bfa6SDavid van Moolenbroek 					dhcpcd_drop(ifp, 0);
735*9f20bfa6SDavid van Moolenbroek #endif
736*9f20bfa6SDavid van Moolenbroek 			}
737*9f20bfa6SDavid van Moolenbroek 			dhcpcd_initstate(ifp, 0);
738*9f20bfa6SDavid van Moolenbroek 			script_runreason(ifp, "CARRIER");
739*9f20bfa6SDavid van Moolenbroek #ifdef NOCARRIER_PRESERVE_IP
740*9f20bfa6SDavid van Moolenbroek 			/* Set any IPv6 Routers we remembered to expire
741*9f20bfa6SDavid van Moolenbroek 			 * faster than they would normally as we
742*9f20bfa6SDavid van Moolenbroek 			 * maybe on a new network. */
743*9f20bfa6SDavid van Moolenbroek 			ipv6nd_expire(ifp, RTR_CARRIER_EXPIRE);
744*9f20bfa6SDavid van Moolenbroek #endif
745*9f20bfa6SDavid van Moolenbroek 			/* RFC4941 Section 3.5 */
746*9f20bfa6SDavid van Moolenbroek 			if (ifp->options->options & DHCPCD_IPV6RA_OWN)
747*9f20bfa6SDavid van Moolenbroek 				ipv6_gentempifid(ifp);
748*9f20bfa6SDavid van Moolenbroek 			dhcpcd_startinterface(ifp);
749*9f20bfa6SDavid van Moolenbroek 		}
750*9f20bfa6SDavid van Moolenbroek 	}
751*9f20bfa6SDavid van Moolenbroek }
752*9f20bfa6SDavid van Moolenbroek 
753*9f20bfa6SDavid van Moolenbroek static void
warn_iaid_conflict(struct interface * ifp,uint8_t * iaid)754*9f20bfa6SDavid van Moolenbroek warn_iaid_conflict(struct interface *ifp, uint8_t *iaid)
755*9f20bfa6SDavid van Moolenbroek {
756*9f20bfa6SDavid van Moolenbroek 	struct interface *ifn;
757*9f20bfa6SDavid van Moolenbroek 	size_t i;
758*9f20bfa6SDavid van Moolenbroek 
759*9f20bfa6SDavid van Moolenbroek 	TAILQ_FOREACH(ifn, ifp->ctx->ifaces, next) {
760*9f20bfa6SDavid van Moolenbroek 		if (ifn == ifp)
761*9f20bfa6SDavid van Moolenbroek 			continue;
762*9f20bfa6SDavid van Moolenbroek 		if (memcmp(ifn->options->iaid, iaid,
763*9f20bfa6SDavid van Moolenbroek 		    sizeof(ifn->options->iaid)) == 0)
764*9f20bfa6SDavid van Moolenbroek 			break;
765*9f20bfa6SDavid van Moolenbroek 		for (i = 0; i < ifn->options->ia_len; i++) {
766*9f20bfa6SDavid van Moolenbroek 			if (memcmp(&ifn->options->ia[i].iaid, iaid,
767*9f20bfa6SDavid van Moolenbroek 			    sizeof(ifn->options->ia[i].iaid)) == 0)
768*9f20bfa6SDavid van Moolenbroek 				break;
769*9f20bfa6SDavid van Moolenbroek 		}
770*9f20bfa6SDavid van Moolenbroek 	}
771*9f20bfa6SDavid van Moolenbroek 
772*9f20bfa6SDavid van Moolenbroek 	/* This is only a problem if the interfaces are on the same network. */
773*9f20bfa6SDavid van Moolenbroek 	if (ifn)
774*9f20bfa6SDavid van Moolenbroek 		logger(ifp->ctx, LOG_ERR,
775*9f20bfa6SDavid van Moolenbroek 		    "%s: IAID conflicts with one assigned to %s",
776*9f20bfa6SDavid van Moolenbroek 		    ifp->name, ifn->name);
777*9f20bfa6SDavid van Moolenbroek }
778*9f20bfa6SDavid van Moolenbroek 
779*9f20bfa6SDavid van Moolenbroek static void
pre_start(struct interface * ifp)780*9f20bfa6SDavid van Moolenbroek pre_start(struct interface *ifp)
781*9f20bfa6SDavid van Moolenbroek {
782*9f20bfa6SDavid van Moolenbroek 
783*9f20bfa6SDavid van Moolenbroek 	/* Add our link-local address before upping the interface
784*9f20bfa6SDavid van Moolenbroek 	 * so our RFC7217 address beats the hwaddr based one.
785*9f20bfa6SDavid van Moolenbroek 	 * This is also a safety check incase it was ripped out
786*9f20bfa6SDavid van Moolenbroek 	 * from under us. */
787*9f20bfa6SDavid van Moolenbroek 	if (ifp->options->options & DHCPCD_IPV6 && ipv6_start(ifp) == -1) {
788*9f20bfa6SDavid van Moolenbroek 		logger(ifp->ctx, LOG_ERR, "%s: ipv6_start: %m", ifp->name);
789*9f20bfa6SDavid van Moolenbroek 		ifp->options->options &= ~DHCPCD_IPV6;
790*9f20bfa6SDavid van Moolenbroek 	}
791*9f20bfa6SDavid van Moolenbroek }
792*9f20bfa6SDavid van Moolenbroek 
793*9f20bfa6SDavid van Moolenbroek void
dhcpcd_startinterface(void * arg)794*9f20bfa6SDavid van Moolenbroek dhcpcd_startinterface(void *arg)
795*9f20bfa6SDavid van Moolenbroek {
796*9f20bfa6SDavid van Moolenbroek 	struct interface *ifp = arg;
797*9f20bfa6SDavid van Moolenbroek 	struct if_options *ifo = ifp->options;
798*9f20bfa6SDavid van Moolenbroek 	size_t i;
799*9f20bfa6SDavid van Moolenbroek 	char buf[DUID_LEN * 3];
800*9f20bfa6SDavid van Moolenbroek 	int carrier;
801*9f20bfa6SDavid van Moolenbroek 	struct timespec tv;
802*9f20bfa6SDavid van Moolenbroek 
803*9f20bfa6SDavid van Moolenbroek 	if (ifo->options & DHCPCD_LINK) {
804*9f20bfa6SDavid van Moolenbroek 		switch (ifp->carrier) {
805*9f20bfa6SDavid van Moolenbroek 		case LINK_UP:
806*9f20bfa6SDavid van Moolenbroek 			break;
807*9f20bfa6SDavid van Moolenbroek 		case LINK_DOWN:
808*9f20bfa6SDavid van Moolenbroek 			logger(ifp->ctx, LOG_INFO, "%s: waiting for carrier",
809*9f20bfa6SDavid van Moolenbroek 			    ifp->name);
810*9f20bfa6SDavid van Moolenbroek 			return;
811*9f20bfa6SDavid van Moolenbroek 		case LINK_UNKNOWN:
812*9f20bfa6SDavid van Moolenbroek 			/* No media state available.
813*9f20bfa6SDavid van Moolenbroek 			 * Loop until both IFF_UP and IFF_RUNNING are set */
814*9f20bfa6SDavid van Moolenbroek 			if ((carrier = if_carrier(ifp)) == LINK_UNKNOWN) {
815*9f20bfa6SDavid van Moolenbroek 				tv.tv_sec = 0;
816*9f20bfa6SDavid van Moolenbroek 				tv.tv_nsec = IF_POLL_UP * NSEC_PER_MSEC;
817*9f20bfa6SDavid van Moolenbroek 				eloop_timeout_add_tv(ifp->ctx->eloop,
818*9f20bfa6SDavid van Moolenbroek 				    &tv, dhcpcd_startinterface, ifp);
819*9f20bfa6SDavid van Moolenbroek 			} else
820*9f20bfa6SDavid van Moolenbroek 				dhcpcd_handlecarrier(ifp->ctx, carrier,
821*9f20bfa6SDavid van Moolenbroek 				    ifp->flags, ifp->name);
822*9f20bfa6SDavid van Moolenbroek 			return;
823*9f20bfa6SDavid van Moolenbroek 		}
824*9f20bfa6SDavid van Moolenbroek 	}
825*9f20bfa6SDavid van Moolenbroek 
826*9f20bfa6SDavid van Moolenbroek 	if (ifo->options & (DHCPCD_DUID | DHCPCD_IPV6)) {
827*9f20bfa6SDavid van Moolenbroek 		/* Report client DUID */
828*9f20bfa6SDavid van Moolenbroek 		if (ifp->ctx->duid == NULL) {
829*9f20bfa6SDavid van Moolenbroek 			if (duid_init(ifp) == 0)
830*9f20bfa6SDavid van Moolenbroek 				return;
831*9f20bfa6SDavid van Moolenbroek 			logger(ifp->ctx, LOG_INFO, "DUID %s",
832*9f20bfa6SDavid van Moolenbroek 			    hwaddr_ntoa(ifp->ctx->duid,
833*9f20bfa6SDavid van Moolenbroek 			    ifp->ctx->duid_len,
834*9f20bfa6SDavid van Moolenbroek 			    buf, sizeof(buf)));
835*9f20bfa6SDavid van Moolenbroek 		}
836*9f20bfa6SDavid van Moolenbroek 	}
837*9f20bfa6SDavid van Moolenbroek 
838*9f20bfa6SDavid van Moolenbroek 	if (ifo->options & (DHCPCD_DUID | DHCPCD_IPV6)) {
839*9f20bfa6SDavid van Moolenbroek 		/* Report IAIDs */
840*9f20bfa6SDavid van Moolenbroek 		logger(ifp->ctx, LOG_INFO, "%s: IAID %s", ifp->name,
841*9f20bfa6SDavid van Moolenbroek 		    hwaddr_ntoa(ifo->iaid, sizeof(ifo->iaid),
842*9f20bfa6SDavid van Moolenbroek 		    buf, sizeof(buf)));
843*9f20bfa6SDavid van Moolenbroek 		warn_iaid_conflict(ifp, ifo->iaid);
844*9f20bfa6SDavid van Moolenbroek 		for (i = 0; i < ifo->ia_len; i++) {
845*9f20bfa6SDavid van Moolenbroek 			if (memcmp(ifo->iaid, ifo->ia[i].iaid,
846*9f20bfa6SDavid van Moolenbroek 			    sizeof(ifo->iaid)))
847*9f20bfa6SDavid van Moolenbroek 			{
848*9f20bfa6SDavid van Moolenbroek 				logger(ifp->ctx, LOG_INFO, "%s: IAID %s",
849*9f20bfa6SDavid van Moolenbroek 				    ifp->name, hwaddr_ntoa(ifo->ia[i].iaid,
850*9f20bfa6SDavid van Moolenbroek 				    sizeof(ifo->ia[i].iaid),
851*9f20bfa6SDavid van Moolenbroek 				    buf, sizeof(buf)));
852*9f20bfa6SDavid van Moolenbroek 				warn_iaid_conflict(ifp, ifo->ia[i].iaid);
853*9f20bfa6SDavid van Moolenbroek 			}
854*9f20bfa6SDavid van Moolenbroek 		}
855*9f20bfa6SDavid van Moolenbroek 	}
856*9f20bfa6SDavid van Moolenbroek 
857*9f20bfa6SDavid van Moolenbroek 	if (ifo->options & DHCPCD_IPV6) {
858*9f20bfa6SDavid van Moolenbroek 		if (ifo->options & DHCPCD_IPV6RS &&
859*9f20bfa6SDavid van Moolenbroek 		    !(ifo->options & DHCPCD_INFORM))
860*9f20bfa6SDavid van Moolenbroek 			ipv6nd_startrs(ifp);
861*9f20bfa6SDavid van Moolenbroek 
862*9f20bfa6SDavid van Moolenbroek 		if (ifo->options & DHCPCD_DHCP6)
863*9f20bfa6SDavid van Moolenbroek 			dhcp6_find_delegates(ifp);
864*9f20bfa6SDavid van Moolenbroek 
865*9f20bfa6SDavid van Moolenbroek 		if (!(ifo->options & DHCPCD_IPV6RS) ||
866*9f20bfa6SDavid van Moolenbroek 		    ifo->options & DHCPCD_IA_FORCED)
867*9f20bfa6SDavid van Moolenbroek 		{
868*9f20bfa6SDavid van Moolenbroek 			ssize_t nolease;
869*9f20bfa6SDavid van Moolenbroek 
870*9f20bfa6SDavid van Moolenbroek 			if (ifo->options & DHCPCD_IA_FORCED)
871*9f20bfa6SDavid van Moolenbroek 				nolease = dhcp6_start(ifp, DH6S_INIT);
872*9f20bfa6SDavid van Moolenbroek 			else {
873*9f20bfa6SDavid van Moolenbroek 				nolease = 0;
874*9f20bfa6SDavid van Moolenbroek 				/* Enabling the below doesn't really make
875*9f20bfa6SDavid van Moolenbroek 				 * sense as there is currently no standard
876*9f20bfa6SDavid van Moolenbroek 				 * to push routes via DHCPv6.
877*9f20bfa6SDavid van Moolenbroek 				 * (There is an expired working draft,
878*9f20bfa6SDavid van Moolenbroek 				 * maybe abandoned?)
879*9f20bfa6SDavid van Moolenbroek 				 * You can also get it to work by forcing
880*9f20bfa6SDavid van Moolenbroek 				 * an IA as shown above. */
881*9f20bfa6SDavid van Moolenbroek #if 0
882*9f20bfa6SDavid van Moolenbroek 				/* With no RS or delegates we might
883*9f20bfa6SDavid van Moolenbroek 				 * as well try and solicit a DHCPv6 address */
884*9f20bfa6SDavid van Moolenbroek 				if (nolease == 0)
885*9f20bfa6SDavid van Moolenbroek 					nolease = dhcp6_start(ifp, DH6S_INIT);
886*9f20bfa6SDavid van Moolenbroek #endif
887*9f20bfa6SDavid van Moolenbroek 			}
888*9f20bfa6SDavid van Moolenbroek 			if (nolease == -1)
889*9f20bfa6SDavid van Moolenbroek 			        logger(ifp->ctx, LOG_ERR,
890*9f20bfa6SDavid van Moolenbroek 				    "%s: dhcp6_start: %m", ifp->name);
891*9f20bfa6SDavid van Moolenbroek 		}
892*9f20bfa6SDavid van Moolenbroek 	}
893*9f20bfa6SDavid van Moolenbroek 
894*9f20bfa6SDavid van Moolenbroek 	if (ifo->options & DHCPCD_IPV4) {
895*9f20bfa6SDavid van Moolenbroek 		/* Ensure we have an IPv4 state before starting DHCP */
896*9f20bfa6SDavid van Moolenbroek 		if (ipv4_getstate(ifp) != NULL)
897*9f20bfa6SDavid van Moolenbroek 			dhcp_start(ifp);
898*9f20bfa6SDavid van Moolenbroek 	}
899*9f20bfa6SDavid van Moolenbroek }
900*9f20bfa6SDavid van Moolenbroek 
901*9f20bfa6SDavid van Moolenbroek static void
dhcpcd_prestartinterface(void * arg)902*9f20bfa6SDavid van Moolenbroek dhcpcd_prestartinterface(void *arg)
903*9f20bfa6SDavid van Moolenbroek {
904*9f20bfa6SDavid van Moolenbroek 	struct interface *ifp = arg;
905*9f20bfa6SDavid van Moolenbroek 
906*9f20bfa6SDavid van Moolenbroek 	pre_start(ifp);
907*9f20bfa6SDavid van Moolenbroek 	if ((!(ifp->ctx->options & DHCPCD_MASTER) ||
908*9f20bfa6SDavid van Moolenbroek 	    ifp->options->options & DHCPCD_IF_UP) &&
909*9f20bfa6SDavid van Moolenbroek 	    if_up(ifp) == -1)
910*9f20bfa6SDavid van Moolenbroek 		logger(ifp->ctx, LOG_ERR, "%s: if_up: %m", ifp->name);
911*9f20bfa6SDavid van Moolenbroek 
912*9f20bfa6SDavid van Moolenbroek 	if (ifp->options->options & DHCPCD_LINK &&
913*9f20bfa6SDavid van Moolenbroek 	    ifp->carrier == LINK_UNKNOWN)
914*9f20bfa6SDavid van Moolenbroek 	{
915*9f20bfa6SDavid van Moolenbroek 		int carrier;
916*9f20bfa6SDavid van Moolenbroek 
917*9f20bfa6SDavid van Moolenbroek 		if ((carrier = if_carrier(ifp)) != LINK_UNKNOWN) {
918*9f20bfa6SDavid van Moolenbroek 			dhcpcd_handlecarrier(ifp->ctx, carrier,
919*9f20bfa6SDavid van Moolenbroek 			    ifp->flags, ifp->name);
920*9f20bfa6SDavid van Moolenbroek 			return;
921*9f20bfa6SDavid van Moolenbroek 		}
922*9f20bfa6SDavid van Moolenbroek 		logger(ifp->ctx, LOG_INFO,
923*9f20bfa6SDavid van Moolenbroek 		    "%s: unknown carrier, waiting for interface flags",
924*9f20bfa6SDavid van Moolenbroek 		    ifp->name);
925*9f20bfa6SDavid van Moolenbroek 	}
926*9f20bfa6SDavid van Moolenbroek 
927*9f20bfa6SDavid van Moolenbroek 	dhcpcd_startinterface(ifp);
928*9f20bfa6SDavid van Moolenbroek }
929*9f20bfa6SDavid van Moolenbroek 
930*9f20bfa6SDavid van Moolenbroek static void
handle_link(void * arg)931*9f20bfa6SDavid van Moolenbroek handle_link(void *arg)
932*9f20bfa6SDavid van Moolenbroek {
933*9f20bfa6SDavid van Moolenbroek 	struct dhcpcd_ctx *ctx;
934*9f20bfa6SDavid van Moolenbroek 
935*9f20bfa6SDavid van Moolenbroek 	ctx = arg;
936*9f20bfa6SDavid van Moolenbroek 	if (if_managelink(ctx) == -1) {
937*9f20bfa6SDavid van Moolenbroek 		logger(ctx, LOG_ERR, "if_managelink: %m");
938*9f20bfa6SDavid van Moolenbroek 		eloop_event_delete(ctx->eloop, ctx->link_fd);
939*9f20bfa6SDavid van Moolenbroek 		close(ctx->link_fd);
940*9f20bfa6SDavid van Moolenbroek 		ctx->link_fd = -1;
941*9f20bfa6SDavid van Moolenbroek 	}
942*9f20bfa6SDavid van Moolenbroek }
943*9f20bfa6SDavid van Moolenbroek 
944*9f20bfa6SDavid van Moolenbroek static void
dhcpcd_initstate1(struct interface * ifp,int argc,char ** argv,unsigned long long options)945*9f20bfa6SDavid van Moolenbroek dhcpcd_initstate1(struct interface *ifp, int argc, char **argv,
946*9f20bfa6SDavid van Moolenbroek     unsigned long long options)
947*9f20bfa6SDavid van Moolenbroek {
948*9f20bfa6SDavid van Moolenbroek 	struct if_options *ifo;
949*9f20bfa6SDavid van Moolenbroek 
950*9f20bfa6SDavid van Moolenbroek 	configure_interface(ifp, argc, argv, options);
951*9f20bfa6SDavid van Moolenbroek 	ifo = ifp->options;
952*9f20bfa6SDavid van Moolenbroek 
953*9f20bfa6SDavid van Moolenbroek 	if (ifo->options & DHCPCD_IPV4 && ipv4_init(ifp->ctx) == -1) {
954*9f20bfa6SDavid van Moolenbroek 		logger(ifp->ctx, LOG_ERR, "ipv4_init: %m");
955*9f20bfa6SDavid van Moolenbroek 		ifo->options &= ~DHCPCD_IPV4;
956*9f20bfa6SDavid van Moolenbroek 	}
957*9f20bfa6SDavid van Moolenbroek 	if (ifo->options & DHCPCD_IPV6 && ipv6_init(ifp->ctx) == NULL) {
958*9f20bfa6SDavid van Moolenbroek 		logger(ifp->ctx, LOG_ERR, "ipv6_init: %m");
959*9f20bfa6SDavid van Moolenbroek 		ifo->options &= ~DHCPCD_IPV6RS;
960*9f20bfa6SDavid van Moolenbroek 	}
961*9f20bfa6SDavid van Moolenbroek 
962*9f20bfa6SDavid van Moolenbroek 	/* Add our link-local address before upping the interface
963*9f20bfa6SDavid van Moolenbroek 	 * so our RFC7217 address beats the hwaddr based one.
964*9f20bfa6SDavid van Moolenbroek 	 * This needs to happen before PREINIT incase a hook script
965*9f20bfa6SDavid van Moolenbroek 	 * inadvertently ups the interface. */
966*9f20bfa6SDavid van Moolenbroek 	if (ifo->options & DHCPCD_IPV6 && ipv6_start(ifp) == -1) {
967*9f20bfa6SDavid van Moolenbroek 		logger(ifp->ctx, LOG_ERR, "%s: ipv6_start: %m", ifp->name);
968*9f20bfa6SDavid van Moolenbroek 		ifo->options &= ~DHCPCD_IPV6;
969*9f20bfa6SDavid van Moolenbroek 	}
970*9f20bfa6SDavid van Moolenbroek }
971*9f20bfa6SDavid van Moolenbroek 
972*9f20bfa6SDavid van Moolenbroek void
dhcpcd_initstate(struct interface * ifp,unsigned long long options)973*9f20bfa6SDavid van Moolenbroek dhcpcd_initstate(struct interface *ifp, unsigned long long options)
974*9f20bfa6SDavid van Moolenbroek {
975*9f20bfa6SDavid van Moolenbroek 
976*9f20bfa6SDavid van Moolenbroek 	dhcpcd_initstate1(ifp, ifp->ctx->argc, ifp->ctx->argv, options);
977*9f20bfa6SDavid van Moolenbroek }
978*9f20bfa6SDavid van Moolenbroek 
979*9f20bfa6SDavid van Moolenbroek static void
run_preinit(struct interface * ifp)980*9f20bfa6SDavid van Moolenbroek run_preinit(struct interface *ifp)
981*9f20bfa6SDavid van Moolenbroek {
982*9f20bfa6SDavid van Moolenbroek 
983*9f20bfa6SDavid van Moolenbroek 	pre_start(ifp);
984*9f20bfa6SDavid van Moolenbroek 	if (ifp->ctx->options & DHCPCD_TEST)
985*9f20bfa6SDavid van Moolenbroek 		return;
986*9f20bfa6SDavid van Moolenbroek 
987*9f20bfa6SDavid van Moolenbroek 	script_runreason(ifp, "PREINIT");
988*9f20bfa6SDavid van Moolenbroek 
989*9f20bfa6SDavid van Moolenbroek 	if (ifp->options->options & DHCPCD_LINK && ifp->carrier != LINK_UNKNOWN)
990*9f20bfa6SDavid van Moolenbroek 		script_runreason(ifp,
991*9f20bfa6SDavid van Moolenbroek 		    ifp->carrier == LINK_UP ? "CARRIER" : "NOCARRIER");
992*9f20bfa6SDavid van Moolenbroek }
993*9f20bfa6SDavid van Moolenbroek 
994*9f20bfa6SDavid van Moolenbroek int
dhcpcd_handleinterface(void * arg,int action,const char * ifname)995*9f20bfa6SDavid van Moolenbroek dhcpcd_handleinterface(void *arg, int action, const char *ifname)
996*9f20bfa6SDavid van Moolenbroek {
997*9f20bfa6SDavid van Moolenbroek 	struct dhcpcd_ctx *ctx;
998*9f20bfa6SDavid van Moolenbroek 	struct if_head *ifs;
999*9f20bfa6SDavid van Moolenbroek 	struct interface *ifp, *iff, *ifn;
1000*9f20bfa6SDavid van Moolenbroek 	const char * const argv[] = { ifname };
1001*9f20bfa6SDavid van Moolenbroek 	int i;
1002*9f20bfa6SDavid van Moolenbroek 
1003*9f20bfa6SDavid van Moolenbroek 	ctx = arg;
1004*9f20bfa6SDavid van Moolenbroek 	if (action == -1) {
1005*9f20bfa6SDavid van Moolenbroek 		ifp = if_find(ctx->ifaces, ifname);
1006*9f20bfa6SDavid van Moolenbroek 		if (ifp == NULL) {
1007*9f20bfa6SDavid van Moolenbroek 			errno = ESRCH;
1008*9f20bfa6SDavid van Moolenbroek 			return -1;
1009*9f20bfa6SDavid van Moolenbroek 		}
1010*9f20bfa6SDavid van Moolenbroek 		logger(ctx, LOG_DEBUG, "%s: interface departed", ifp->name);
1011*9f20bfa6SDavid van Moolenbroek 		ifp->options->options |= DHCPCD_DEPARTED;
1012*9f20bfa6SDavid van Moolenbroek 		stop_interface(ifp);
1013*9f20bfa6SDavid van Moolenbroek 		return 0;
1014*9f20bfa6SDavid van Moolenbroek 	}
1015*9f20bfa6SDavid van Moolenbroek 
1016*9f20bfa6SDavid van Moolenbroek 	/* If running off an interface list, check it's in it. */
1017*9f20bfa6SDavid van Moolenbroek 	if (ctx->ifc && action != 2) {
1018*9f20bfa6SDavid van Moolenbroek 		for (i = 0; i < ctx->ifc; i++)
1019*9f20bfa6SDavid van Moolenbroek 			if (strcmp(ctx->ifv[i], ifname) == 0)
1020*9f20bfa6SDavid van Moolenbroek 				break;
1021*9f20bfa6SDavid van Moolenbroek 		if (i >= ctx->ifc)
1022*9f20bfa6SDavid van Moolenbroek 			return 0;
1023*9f20bfa6SDavid van Moolenbroek 	}
1024*9f20bfa6SDavid van Moolenbroek 
1025*9f20bfa6SDavid van Moolenbroek 	i = -1;
1026*9f20bfa6SDavid van Moolenbroek 	ifs = if_discover(ctx, -1, UNCONST(argv));
1027*9f20bfa6SDavid van Moolenbroek 	if (ifs == NULL) {
1028*9f20bfa6SDavid van Moolenbroek 		logger(ctx, LOG_ERR, "%s: if_discover: %m", __func__);
1029*9f20bfa6SDavid van Moolenbroek 		return -1;
1030*9f20bfa6SDavid van Moolenbroek 	}
1031*9f20bfa6SDavid van Moolenbroek 	TAILQ_FOREACH_SAFE(ifp, ifs, next, ifn) {
1032*9f20bfa6SDavid van Moolenbroek 		if (strcmp(ifp->name, ifname) != 0)
1033*9f20bfa6SDavid van Moolenbroek 			continue;
1034*9f20bfa6SDavid van Moolenbroek 		i = 0;
1035*9f20bfa6SDavid van Moolenbroek 		/* Check if we already have the interface */
1036*9f20bfa6SDavid van Moolenbroek 		iff = if_find(ctx->ifaces, ifp->name);
1037*9f20bfa6SDavid van Moolenbroek 		if (iff) {
1038*9f20bfa6SDavid van Moolenbroek 			logger(ctx, LOG_DEBUG, "%s: interface updated", iff->name);
1039*9f20bfa6SDavid van Moolenbroek 			/* The flags and hwaddr could have changed */
1040*9f20bfa6SDavid van Moolenbroek 			iff->flags = ifp->flags;
1041*9f20bfa6SDavid van Moolenbroek 			iff->hwlen = ifp->hwlen;
1042*9f20bfa6SDavid van Moolenbroek 			if (ifp->hwlen != 0)
1043*9f20bfa6SDavid van Moolenbroek 				memcpy(iff->hwaddr, ifp->hwaddr, iff->hwlen);
1044*9f20bfa6SDavid van Moolenbroek 		} else {
1045*9f20bfa6SDavid van Moolenbroek 			logger(ctx, LOG_DEBUG, "%s: interface added", ifp->name);
1046*9f20bfa6SDavid van Moolenbroek 			TAILQ_REMOVE(ifs, ifp, next);
1047*9f20bfa6SDavid van Moolenbroek 			TAILQ_INSERT_TAIL(ctx->ifaces, ifp, next);
1048*9f20bfa6SDavid van Moolenbroek 			dhcpcd_initstate(ifp, 0);
1049*9f20bfa6SDavid van Moolenbroek 			run_preinit(ifp);
1050*9f20bfa6SDavid van Moolenbroek 			iff = ifp;
1051*9f20bfa6SDavid van Moolenbroek 		}
1052*9f20bfa6SDavid van Moolenbroek 		if (action > 0)
1053*9f20bfa6SDavid van Moolenbroek 			dhcpcd_prestartinterface(iff);
1054*9f20bfa6SDavid van Moolenbroek 	}
1055*9f20bfa6SDavid van Moolenbroek 
1056*9f20bfa6SDavid van Moolenbroek 	/* Free our discovered list */
1057*9f20bfa6SDavid van Moolenbroek 	while ((ifp = TAILQ_FIRST(ifs))) {
1058*9f20bfa6SDavid van Moolenbroek 		TAILQ_REMOVE(ifs, ifp, next);
1059*9f20bfa6SDavid van Moolenbroek 		if_free(ifp);
1060*9f20bfa6SDavid van Moolenbroek 	}
1061*9f20bfa6SDavid van Moolenbroek 	free(ifs);
1062*9f20bfa6SDavid van Moolenbroek 
1063*9f20bfa6SDavid van Moolenbroek 	if (i == -1)
1064*9f20bfa6SDavid van Moolenbroek 		errno = ENOENT;
1065*9f20bfa6SDavid van Moolenbroek 	return i;
1066*9f20bfa6SDavid van Moolenbroek }
1067*9f20bfa6SDavid van Moolenbroek 
1068*9f20bfa6SDavid van Moolenbroek void
dhcpcd_handlehwaddr(struct dhcpcd_ctx * ctx,const char * ifname,const uint8_t * hwaddr,uint8_t hwlen)1069*9f20bfa6SDavid van Moolenbroek dhcpcd_handlehwaddr(struct dhcpcd_ctx *ctx, const char *ifname,
1070*9f20bfa6SDavid van Moolenbroek     const uint8_t *hwaddr, uint8_t hwlen)
1071*9f20bfa6SDavid van Moolenbroek {
1072*9f20bfa6SDavid van Moolenbroek 	struct interface *ifp;
1073*9f20bfa6SDavid van Moolenbroek 	char buf[sizeof(ifp->hwaddr) * 3];
1074*9f20bfa6SDavid van Moolenbroek 
1075*9f20bfa6SDavid van Moolenbroek 	ifp = if_find(ctx->ifaces, ifname);
1076*9f20bfa6SDavid van Moolenbroek 	if (ifp == NULL)
1077*9f20bfa6SDavid van Moolenbroek 		return;
1078*9f20bfa6SDavid van Moolenbroek 
1079*9f20bfa6SDavid van Moolenbroek 	if (hwlen > sizeof(ifp->hwaddr)) {
1080*9f20bfa6SDavid van Moolenbroek 		errno = ENOBUFS;
1081*9f20bfa6SDavid van Moolenbroek 		logger(ctx, LOG_ERR, "%s: %s: %m", ifp->name, __func__);
1082*9f20bfa6SDavid van Moolenbroek 		return;
1083*9f20bfa6SDavid van Moolenbroek 	}
1084*9f20bfa6SDavid van Moolenbroek 
1085*9f20bfa6SDavid van Moolenbroek 	if (ifp->hwlen == hwlen && memcmp(ifp->hwaddr, hwaddr, hwlen) == 0)
1086*9f20bfa6SDavid van Moolenbroek 		return;
1087*9f20bfa6SDavid van Moolenbroek 
1088*9f20bfa6SDavid van Moolenbroek 	logger(ctx, LOG_INFO, "%s: new hardware address: %s", ifp->name,
1089*9f20bfa6SDavid van Moolenbroek 	    hwaddr_ntoa(hwaddr, hwlen, buf, sizeof(buf)));
1090*9f20bfa6SDavid van Moolenbroek 	ifp->hwlen = hwlen;
1091*9f20bfa6SDavid van Moolenbroek 	memcpy(ifp->hwaddr, hwaddr, hwlen);
1092*9f20bfa6SDavid van Moolenbroek }
1093*9f20bfa6SDavid van Moolenbroek 
1094*9f20bfa6SDavid van Moolenbroek static void
if_reboot(struct interface * ifp,int argc,char ** argv)1095*9f20bfa6SDavid van Moolenbroek if_reboot(struct interface *ifp, int argc, char **argv)
1096*9f20bfa6SDavid van Moolenbroek {
1097*9f20bfa6SDavid van Moolenbroek 	unsigned long long oldopts;
1098*9f20bfa6SDavid van Moolenbroek 
1099*9f20bfa6SDavid van Moolenbroek 	oldopts = ifp->options->options;
1100*9f20bfa6SDavid van Moolenbroek 	script_runreason(ifp, "RECONFIGURE");
1101*9f20bfa6SDavid van Moolenbroek 	dhcpcd_initstate1(ifp, argc, argv, 0);
1102*9f20bfa6SDavid van Moolenbroek 	dhcp_reboot_newopts(ifp, oldopts);
1103*9f20bfa6SDavid van Moolenbroek 	dhcp6_reboot(ifp);
1104*9f20bfa6SDavid van Moolenbroek 	dhcpcd_prestartinterface(ifp);
1105*9f20bfa6SDavid van Moolenbroek }
1106*9f20bfa6SDavid van Moolenbroek 
1107*9f20bfa6SDavid van Moolenbroek static void
reload_config(struct dhcpcd_ctx * ctx)1108*9f20bfa6SDavid van Moolenbroek reload_config(struct dhcpcd_ctx *ctx)
1109*9f20bfa6SDavid van Moolenbroek {
1110*9f20bfa6SDavid van Moolenbroek 	struct if_options *ifo;
1111*9f20bfa6SDavid van Moolenbroek 
1112*9f20bfa6SDavid van Moolenbroek 	free_globals(ctx);
1113*9f20bfa6SDavid van Moolenbroek 	ifo = read_config(ctx, NULL, NULL, NULL);
1114*9f20bfa6SDavid van Moolenbroek 	add_options(ctx, NULL, ifo, ctx->argc, ctx->argv);
1115*9f20bfa6SDavid van Moolenbroek 	/* We need to preserve these two options. */
1116*9f20bfa6SDavid van Moolenbroek 	if (ctx->options & DHCPCD_MASTER)
1117*9f20bfa6SDavid van Moolenbroek 		ifo->options |= DHCPCD_MASTER;
1118*9f20bfa6SDavid van Moolenbroek 	if (ctx->options & DHCPCD_DAEMONISED)
1119*9f20bfa6SDavid van Moolenbroek 		ifo->options |= DHCPCD_DAEMONISED;
1120*9f20bfa6SDavid van Moolenbroek 	ctx->options = ifo->options;
1121*9f20bfa6SDavid van Moolenbroek 	free_options(ifo);
1122*9f20bfa6SDavid van Moolenbroek }
1123*9f20bfa6SDavid van Moolenbroek 
1124*9f20bfa6SDavid van Moolenbroek static void
reconf_reboot(struct dhcpcd_ctx * ctx,int action,int argc,char ** argv,int oi)1125*9f20bfa6SDavid van Moolenbroek reconf_reboot(struct dhcpcd_ctx *ctx, int action, int argc, char **argv, int oi)
1126*9f20bfa6SDavid van Moolenbroek {
1127*9f20bfa6SDavid van Moolenbroek 	struct if_head *ifs;
1128*9f20bfa6SDavid van Moolenbroek 	struct interface *ifn, *ifp;
1129*9f20bfa6SDavid van Moolenbroek 
1130*9f20bfa6SDavid van Moolenbroek 	ifs = if_discover(ctx, argc - oi, argv + oi);
1131*9f20bfa6SDavid van Moolenbroek 	if (ifs == NULL) {
1132*9f20bfa6SDavid van Moolenbroek 		logger(ctx, LOG_ERR, "%s: if_discover: %m", __func__);
1133*9f20bfa6SDavid van Moolenbroek 		return;
1134*9f20bfa6SDavid van Moolenbroek 	}
1135*9f20bfa6SDavid van Moolenbroek 
1136*9f20bfa6SDavid van Moolenbroek 	while ((ifp = TAILQ_FIRST(ifs))) {
1137*9f20bfa6SDavid van Moolenbroek 		TAILQ_REMOVE(ifs, ifp, next);
1138*9f20bfa6SDavid van Moolenbroek 		ifn = if_find(ctx->ifaces, ifp->name);
1139*9f20bfa6SDavid van Moolenbroek 		if (ifn) {
1140*9f20bfa6SDavid van Moolenbroek 			if (action)
1141*9f20bfa6SDavid van Moolenbroek 				if_reboot(ifn, argc, argv);
1142*9f20bfa6SDavid van Moolenbroek 			else
1143*9f20bfa6SDavid van Moolenbroek 				ipv4_applyaddr(ifn);
1144*9f20bfa6SDavid van Moolenbroek 			if_free(ifp);
1145*9f20bfa6SDavid van Moolenbroek 		} else {
1146*9f20bfa6SDavid van Moolenbroek 			TAILQ_INSERT_TAIL(ctx->ifaces, ifp, next);
1147*9f20bfa6SDavid van Moolenbroek 			dhcpcd_initstate1(ifp, argc, argv, 0);
1148*9f20bfa6SDavid van Moolenbroek 			run_preinit(ifp);
1149*9f20bfa6SDavid van Moolenbroek 			dhcpcd_prestartinterface(ifp);
1150*9f20bfa6SDavid van Moolenbroek 		}
1151*9f20bfa6SDavid van Moolenbroek 	}
1152*9f20bfa6SDavid van Moolenbroek 	free(ifs);
1153*9f20bfa6SDavid van Moolenbroek }
1154*9f20bfa6SDavid van Moolenbroek 
1155*9f20bfa6SDavid van Moolenbroek static void
stop_all_interfaces(struct dhcpcd_ctx * ctx,int do_release)1156*9f20bfa6SDavid van Moolenbroek stop_all_interfaces(struct dhcpcd_ctx *ctx, int do_release)
1157*9f20bfa6SDavid van Moolenbroek {
1158*9f20bfa6SDavid van Moolenbroek 	struct interface *ifp;
1159*9f20bfa6SDavid van Moolenbroek 
1160*9f20bfa6SDavid van Moolenbroek 	/* Drop the last interface first */
1161*9f20bfa6SDavid van Moolenbroek 	while ((ifp = TAILQ_LAST(ctx->ifaces, if_head)) != NULL) {
1162*9f20bfa6SDavid van Moolenbroek 		if (do_release) {
1163*9f20bfa6SDavid van Moolenbroek 			ifp->options->options |= DHCPCD_RELEASE;
1164*9f20bfa6SDavid van Moolenbroek 			ifp->options->options &= ~DHCPCD_PERSISTENT;
1165*9f20bfa6SDavid van Moolenbroek 		}
1166*9f20bfa6SDavid van Moolenbroek 		ifp->options->options |= DHCPCD_EXITING;
1167*9f20bfa6SDavid van Moolenbroek 		stop_interface(ifp);
1168*9f20bfa6SDavid van Moolenbroek 	}
1169*9f20bfa6SDavid van Moolenbroek }
1170*9f20bfa6SDavid van Moolenbroek 
1171*9f20bfa6SDavid van Moolenbroek #ifdef USE_SIGNALS
1172*9f20bfa6SDavid van Moolenbroek #define sigmsg "received %s, %s"
1173*9f20bfa6SDavid van Moolenbroek static void
signal_cb(int sig,void * arg)1174*9f20bfa6SDavid van Moolenbroek signal_cb(int sig, void *arg)
1175*9f20bfa6SDavid van Moolenbroek {
1176*9f20bfa6SDavid van Moolenbroek 	struct dhcpcd_ctx *ctx = arg;
1177*9f20bfa6SDavid van Moolenbroek 	struct interface *ifp;
1178*9f20bfa6SDavid van Moolenbroek 	int do_release, exit_code;
1179*9f20bfa6SDavid van Moolenbroek 
1180*9f20bfa6SDavid van Moolenbroek 	do_release = 0;
1181*9f20bfa6SDavid van Moolenbroek 	exit_code = EXIT_FAILURE;
1182*9f20bfa6SDavid van Moolenbroek 	switch (sig) {
1183*9f20bfa6SDavid van Moolenbroek 	case SIGINT:
1184*9f20bfa6SDavid van Moolenbroek 		logger(ctx, LOG_INFO, sigmsg, "SIGINT", "stopping");
1185*9f20bfa6SDavid van Moolenbroek 		break;
1186*9f20bfa6SDavid van Moolenbroek 	case SIGTERM:
1187*9f20bfa6SDavid van Moolenbroek 		logger(ctx, LOG_INFO, sigmsg, "SIGTERM", "stopping");
1188*9f20bfa6SDavid van Moolenbroek 		exit_code = EXIT_SUCCESS;
1189*9f20bfa6SDavid van Moolenbroek 		break;
1190*9f20bfa6SDavid van Moolenbroek 	case SIGALRM:
1191*9f20bfa6SDavid van Moolenbroek 		logger(ctx, LOG_INFO, sigmsg, "SIGALRM", "releasing");
1192*9f20bfa6SDavid van Moolenbroek 		do_release = 1;
1193*9f20bfa6SDavid van Moolenbroek 		exit_code = EXIT_SUCCESS;
1194*9f20bfa6SDavid van Moolenbroek 		break;
1195*9f20bfa6SDavid van Moolenbroek 	case SIGHUP:
1196*9f20bfa6SDavid van Moolenbroek 		logger(ctx, LOG_INFO, sigmsg, "SIGHUP", "rebinding");
1197*9f20bfa6SDavid van Moolenbroek 		reload_config(ctx);
1198*9f20bfa6SDavid van Moolenbroek 		/* Preserve any options passed on the commandline
1199*9f20bfa6SDavid van Moolenbroek 		 * when we were started. */
1200*9f20bfa6SDavid van Moolenbroek 		reconf_reboot(ctx, 1, ctx->argc, ctx->argv,
1201*9f20bfa6SDavid van Moolenbroek 		    ctx->argc - ctx->ifc);
1202*9f20bfa6SDavid van Moolenbroek 		return;
1203*9f20bfa6SDavid van Moolenbroek 	case SIGUSR1:
1204*9f20bfa6SDavid van Moolenbroek 		logger(ctx, LOG_INFO, sigmsg, "SIGUSR1", "reconfiguring");
1205*9f20bfa6SDavid van Moolenbroek 		TAILQ_FOREACH(ifp, ctx->ifaces, next) {
1206*9f20bfa6SDavid van Moolenbroek 			ipv4_applyaddr(ifp);
1207*9f20bfa6SDavid van Moolenbroek 		}
1208*9f20bfa6SDavid van Moolenbroek 		return;
1209*9f20bfa6SDavid van Moolenbroek 	case SIGUSR2:
1210*9f20bfa6SDavid van Moolenbroek 		logger_close(ctx);
1211*9f20bfa6SDavid van Moolenbroek 		logger_open(ctx);
1212*9f20bfa6SDavid van Moolenbroek 		logger(ctx, LOG_INFO, sigmsg, "SIGUSR2", "reopened logfile");
1213*9f20bfa6SDavid van Moolenbroek 		return;
1214*9f20bfa6SDavid van Moolenbroek 	case SIGPIPE:
1215*9f20bfa6SDavid van Moolenbroek 		logger(ctx, LOG_WARNING, "received SIGPIPE");
1216*9f20bfa6SDavid van Moolenbroek 		return;
1217*9f20bfa6SDavid van Moolenbroek 	default:
1218*9f20bfa6SDavid van Moolenbroek 		logger(ctx, LOG_ERR,
1219*9f20bfa6SDavid van Moolenbroek 		    "received signal %d, "
1220*9f20bfa6SDavid van Moolenbroek 		    "but don't know what to do with it",
1221*9f20bfa6SDavid van Moolenbroek 		    sig);
1222*9f20bfa6SDavid van Moolenbroek 		return;
1223*9f20bfa6SDavid van Moolenbroek 	}
1224*9f20bfa6SDavid van Moolenbroek 
1225*9f20bfa6SDavid van Moolenbroek 	if (!(ctx->options & DHCPCD_TEST))
1226*9f20bfa6SDavid van Moolenbroek 		stop_all_interfaces(ctx, do_release);
1227*9f20bfa6SDavid van Moolenbroek 	eloop_exit(ctx->eloop, exit_code);
1228*9f20bfa6SDavid van Moolenbroek }
1229*9f20bfa6SDavid van Moolenbroek #endif
1230*9f20bfa6SDavid van Moolenbroek 
1231*9f20bfa6SDavid van Moolenbroek static void
dhcpcd_getinterfaces(void * arg)1232*9f20bfa6SDavid van Moolenbroek dhcpcd_getinterfaces(void *arg)
1233*9f20bfa6SDavid van Moolenbroek {
1234*9f20bfa6SDavid van Moolenbroek 	struct fd_list *fd = arg;
1235*9f20bfa6SDavid van Moolenbroek 	struct interface *ifp;
1236*9f20bfa6SDavid van Moolenbroek 	size_t len;
1237*9f20bfa6SDavid van Moolenbroek 
1238*9f20bfa6SDavid van Moolenbroek 	len = 0;
1239*9f20bfa6SDavid van Moolenbroek 	TAILQ_FOREACH(ifp, fd->ctx->ifaces, next) {
1240*9f20bfa6SDavid van Moolenbroek 		len++;
1241*9f20bfa6SDavid van Moolenbroek 		if (D_STATE_RUNNING(ifp))
1242*9f20bfa6SDavid van Moolenbroek 			len++;
1243*9f20bfa6SDavid van Moolenbroek 		if (IPV4LL_STATE_RUNNING(ifp))
1244*9f20bfa6SDavid van Moolenbroek 			len++;
1245*9f20bfa6SDavid van Moolenbroek 		if (RS_STATE_RUNNING(ifp))
1246*9f20bfa6SDavid van Moolenbroek 			len++;
1247*9f20bfa6SDavid van Moolenbroek 		if (D6_STATE_RUNNING(ifp))
1248*9f20bfa6SDavid van Moolenbroek 			len++;
1249*9f20bfa6SDavid van Moolenbroek 	}
1250*9f20bfa6SDavid van Moolenbroek 	if (write(fd->fd, &len, sizeof(len)) != sizeof(len))
1251*9f20bfa6SDavid van Moolenbroek 		return;
1252*9f20bfa6SDavid van Moolenbroek 	eloop_event_remove_writecb(fd->ctx->eloop, fd->fd);
1253*9f20bfa6SDavid van Moolenbroek 	TAILQ_FOREACH(ifp, fd->ctx->ifaces, next) {
1254*9f20bfa6SDavid van Moolenbroek 		if (send_interface(fd, ifp) == -1)
1255*9f20bfa6SDavid van Moolenbroek 			logger(ifp->ctx, LOG_ERR,
1256*9f20bfa6SDavid van Moolenbroek 			    "send_interface %d: %m", fd->fd);
1257*9f20bfa6SDavid van Moolenbroek 	}
1258*9f20bfa6SDavid van Moolenbroek }
1259*9f20bfa6SDavid van Moolenbroek 
1260*9f20bfa6SDavid van Moolenbroek int
dhcpcd_handleargs(struct dhcpcd_ctx * ctx,struct fd_list * fd,int argc,char ** argv)1261*9f20bfa6SDavid van Moolenbroek dhcpcd_handleargs(struct dhcpcd_ctx *ctx, struct fd_list *fd,
1262*9f20bfa6SDavid van Moolenbroek     int argc, char **argv)
1263*9f20bfa6SDavid van Moolenbroek {
1264*9f20bfa6SDavid van Moolenbroek 	struct interface *ifp;
1265*9f20bfa6SDavid van Moolenbroek 	int do_exit = 0, do_release = 0, do_reboot = 0;
1266*9f20bfa6SDavid van Moolenbroek 	int opt, oi = 0;
1267*9f20bfa6SDavid van Moolenbroek 	size_t len, l;
1268*9f20bfa6SDavid van Moolenbroek 	char *tmp, *p;
1269*9f20bfa6SDavid van Moolenbroek 
1270*9f20bfa6SDavid van Moolenbroek 	/* Special commands for our control socket
1271*9f20bfa6SDavid van Moolenbroek 	 * as the other end should be blocking until it gets the
1272*9f20bfa6SDavid van Moolenbroek 	 * expected reply we should be safely able just to change the
1273*9f20bfa6SDavid van Moolenbroek 	 * write callback on the fd */
1274*9f20bfa6SDavid van Moolenbroek 	if (strcmp(*argv, "--version") == 0) {
1275*9f20bfa6SDavid van Moolenbroek 		return control_queue(fd, UNCONST(VERSION),
1276*9f20bfa6SDavid van Moolenbroek 		    strlen(VERSION) + 1, 0);
1277*9f20bfa6SDavid van Moolenbroek 	} else if (strcmp(*argv, "--getconfigfile") == 0) {
1278*9f20bfa6SDavid van Moolenbroek 		return control_queue(fd, UNCONST(fd->ctx->cffile),
1279*9f20bfa6SDavid van Moolenbroek 		    strlen(fd->ctx->cffile) + 1, 0);
1280*9f20bfa6SDavid van Moolenbroek 	} else if (strcmp(*argv, "--getinterfaces") == 0) {
1281*9f20bfa6SDavid van Moolenbroek 		eloop_event_add(fd->ctx->eloop, fd->fd, NULL, NULL,
1282*9f20bfa6SDavid van Moolenbroek 		    dhcpcd_getinterfaces, fd);
1283*9f20bfa6SDavid van Moolenbroek 		return 0;
1284*9f20bfa6SDavid van Moolenbroek 	} else if (strcmp(*argv, "--listen") == 0) {
1285*9f20bfa6SDavid van Moolenbroek 		fd->flags |= FD_LISTEN;
1286*9f20bfa6SDavid van Moolenbroek 		return 0;
1287*9f20bfa6SDavid van Moolenbroek 	}
1288*9f20bfa6SDavid van Moolenbroek 
1289*9f20bfa6SDavid van Moolenbroek 	/* Only priviledged users can control dhcpcd via the socket. */
1290*9f20bfa6SDavid van Moolenbroek 	if (fd->flags & FD_UNPRIV) {
1291*9f20bfa6SDavid van Moolenbroek 		errno = EPERM;
1292*9f20bfa6SDavid van Moolenbroek 		return -1;
1293*9f20bfa6SDavid van Moolenbroek 	}
1294*9f20bfa6SDavid van Moolenbroek 
1295*9f20bfa6SDavid van Moolenbroek 	/* Log the command */
1296*9f20bfa6SDavid van Moolenbroek 	len = 1;
1297*9f20bfa6SDavid van Moolenbroek 	for (opt = 0; opt < argc; opt++)
1298*9f20bfa6SDavid van Moolenbroek 		len += strlen(argv[opt]) + 1;
1299*9f20bfa6SDavid van Moolenbroek 	tmp = malloc(len);
1300*9f20bfa6SDavid van Moolenbroek 	if (tmp == NULL)
1301*9f20bfa6SDavid van Moolenbroek 		return -1;
1302*9f20bfa6SDavid van Moolenbroek 	p = tmp;
1303*9f20bfa6SDavid van Moolenbroek 	for (opt = 0; opt < argc; opt++) {
1304*9f20bfa6SDavid van Moolenbroek 		l = strlen(argv[opt]);
1305*9f20bfa6SDavid van Moolenbroek 		strlcpy(p, argv[opt], len);
1306*9f20bfa6SDavid van Moolenbroek 		len -= l + 1;
1307*9f20bfa6SDavid van Moolenbroek 		p += l;
1308*9f20bfa6SDavid van Moolenbroek 		*p++ = ' ';
1309*9f20bfa6SDavid van Moolenbroek 	}
1310*9f20bfa6SDavid van Moolenbroek 	*--p = '\0';
1311*9f20bfa6SDavid van Moolenbroek 	logger(ctx, LOG_INFO, "control command: %s", tmp);
1312*9f20bfa6SDavid van Moolenbroek 	free(tmp);
1313*9f20bfa6SDavid van Moolenbroek 
1314*9f20bfa6SDavid van Moolenbroek 	optind = 0;
1315*9f20bfa6SDavid van Moolenbroek 	while ((opt = getopt_long(argc, argv, IF_OPTS, cf_options, &oi)) != -1)
1316*9f20bfa6SDavid van Moolenbroek 	{
1317*9f20bfa6SDavid van Moolenbroek 		switch (opt) {
1318*9f20bfa6SDavid van Moolenbroek 		case 'g':
1319*9f20bfa6SDavid van Moolenbroek 			/* Assumed if below not set */
1320*9f20bfa6SDavid van Moolenbroek 			break;
1321*9f20bfa6SDavid van Moolenbroek 		case 'k':
1322*9f20bfa6SDavid van Moolenbroek 			do_release = 1;
1323*9f20bfa6SDavid van Moolenbroek 			break;
1324*9f20bfa6SDavid van Moolenbroek 		case 'n':
1325*9f20bfa6SDavid van Moolenbroek 			do_reboot = 1;
1326*9f20bfa6SDavid van Moolenbroek 			break;
1327*9f20bfa6SDavid van Moolenbroek 		case 'x':
1328*9f20bfa6SDavid van Moolenbroek 			do_exit = 1;
1329*9f20bfa6SDavid van Moolenbroek 			break;
1330*9f20bfa6SDavid van Moolenbroek 		}
1331*9f20bfa6SDavid van Moolenbroek 	}
1332*9f20bfa6SDavid van Moolenbroek 
1333*9f20bfa6SDavid van Moolenbroek 	if (do_release || do_exit) {
1334*9f20bfa6SDavid van Moolenbroek 		if (optind == argc) {
1335*9f20bfa6SDavid van Moolenbroek 			stop_all_interfaces(ctx, do_release);
1336*9f20bfa6SDavid van Moolenbroek 			eloop_exit(ctx->eloop, EXIT_SUCCESS);
1337*9f20bfa6SDavid van Moolenbroek 			return 0;
1338*9f20bfa6SDavid van Moolenbroek 		}
1339*9f20bfa6SDavid van Moolenbroek 		for (oi = optind; oi < argc; oi++) {
1340*9f20bfa6SDavid van Moolenbroek 			if ((ifp = if_find(ctx->ifaces, argv[oi])) == NULL)
1341*9f20bfa6SDavid van Moolenbroek 				continue;
1342*9f20bfa6SDavid van Moolenbroek 			if (do_release) {
1343*9f20bfa6SDavid van Moolenbroek 				ifp->options->options |= DHCPCD_RELEASE;
1344*9f20bfa6SDavid van Moolenbroek 				ifp->options->options &= ~DHCPCD_PERSISTENT;
1345*9f20bfa6SDavid van Moolenbroek 			}
1346*9f20bfa6SDavid van Moolenbroek 			ifp->options->options |= DHCPCD_EXITING;
1347*9f20bfa6SDavid van Moolenbroek 			stop_interface(ifp);
1348*9f20bfa6SDavid van Moolenbroek 		}
1349*9f20bfa6SDavid van Moolenbroek 		return 0;
1350*9f20bfa6SDavid van Moolenbroek 	}
1351*9f20bfa6SDavid van Moolenbroek 
1352*9f20bfa6SDavid van Moolenbroek 	reload_config(ctx);
1353*9f20bfa6SDavid van Moolenbroek 	/* XXX: Respect initial commandline options? */
1354*9f20bfa6SDavid van Moolenbroek 	reconf_reboot(ctx, do_reboot, argc, argv, optind - 1);
1355*9f20bfa6SDavid van Moolenbroek 	return 0;
1356*9f20bfa6SDavid van Moolenbroek }
1357*9f20bfa6SDavid van Moolenbroek 
1358*9f20bfa6SDavid van Moolenbroek int
main(int argc,char ** argv)1359*9f20bfa6SDavid van Moolenbroek main(int argc, char **argv)
1360*9f20bfa6SDavid van Moolenbroek {
1361*9f20bfa6SDavid van Moolenbroek 	struct dhcpcd_ctx ctx;
1362*9f20bfa6SDavid van Moolenbroek 	struct if_options *ifo;
1363*9f20bfa6SDavid van Moolenbroek 	struct interface *ifp;
1364*9f20bfa6SDavid van Moolenbroek 	uint16_t family = 0;
1365*9f20bfa6SDavid van Moolenbroek 	int opt, oi = 0, i;
1366*9f20bfa6SDavid van Moolenbroek 	time_t t;
1367*9f20bfa6SDavid van Moolenbroek 	ssize_t len;
1368*9f20bfa6SDavid van Moolenbroek #if defined(USE_SIGNALS) || !defined(THERE_IS_NO_FORK)
1369*9f20bfa6SDavid van Moolenbroek 	pid_t pid;
1370*9f20bfa6SDavid van Moolenbroek #endif
1371*9f20bfa6SDavid van Moolenbroek #ifdef USE_SIGNALS
1372*9f20bfa6SDavid van Moolenbroek 	int sig = 0;
1373*9f20bfa6SDavid van Moolenbroek 	const char *siga = NULL;
1374*9f20bfa6SDavid van Moolenbroek #endif
1375*9f20bfa6SDavid van Moolenbroek 
1376*9f20bfa6SDavid van Moolenbroek 	/* Test for --help and --version */
1377*9f20bfa6SDavid van Moolenbroek 	if (argc > 1) {
1378*9f20bfa6SDavid van Moolenbroek 		if (strcmp(argv[1], "--help") == 0) {
1379*9f20bfa6SDavid van Moolenbroek 			usage();
1380*9f20bfa6SDavid van Moolenbroek 			return EXIT_SUCCESS;
1381*9f20bfa6SDavid van Moolenbroek 		} else if (strcmp(argv[1], "--version") == 0) {
1382*9f20bfa6SDavid van Moolenbroek 			printf(""PACKAGE" "VERSION"\n%s\n", dhcpcd_copyright);
1383*9f20bfa6SDavid van Moolenbroek 			return EXIT_SUCCESS;
1384*9f20bfa6SDavid van Moolenbroek 		}
1385*9f20bfa6SDavid van Moolenbroek 	}
1386*9f20bfa6SDavid van Moolenbroek 
1387*9f20bfa6SDavid van Moolenbroek 	memset(&ctx, 0, sizeof(ctx));
1388*9f20bfa6SDavid van Moolenbroek 	closefrom(3);
1389*9f20bfa6SDavid van Moolenbroek 
1390*9f20bfa6SDavid van Moolenbroek 	ctx.log_fd = -1;
1391*9f20bfa6SDavid van Moolenbroek 	logger_open(&ctx);
1392*9f20bfa6SDavid van Moolenbroek 	logger_mask(&ctx, LOG_UPTO(LOG_INFO));
1393*9f20bfa6SDavid van Moolenbroek 
1394*9f20bfa6SDavid van Moolenbroek 	ifo = NULL;
1395*9f20bfa6SDavid van Moolenbroek 	ctx.cffile = CONFIG;
1396*9f20bfa6SDavid van Moolenbroek 	ctx.pid_fd = ctx.control_fd = ctx.control_unpriv_fd = ctx.link_fd = -1;
1397*9f20bfa6SDavid van Moolenbroek 	ctx.pf_inet_fd = -1;
1398*9f20bfa6SDavid van Moolenbroek #if defined(INET6) && defined(BSD)
1399*9f20bfa6SDavid van Moolenbroek 	ctx.pf_inet6_fd = -1;
1400*9f20bfa6SDavid van Moolenbroek #endif
1401*9f20bfa6SDavid van Moolenbroek #ifdef IFLR_ACTIVE
1402*9f20bfa6SDavid van Moolenbroek 	ctx.pf_link_fd = -1;
1403*9f20bfa6SDavid van Moolenbroek #endif
1404*9f20bfa6SDavid van Moolenbroek 
1405*9f20bfa6SDavid van Moolenbroek 	TAILQ_INIT(&ctx.control_fds);
1406*9f20bfa6SDavid van Moolenbroek #ifdef PLUGIN_DEV
1407*9f20bfa6SDavid van Moolenbroek 	ctx.dev_fd = -1;
1408*9f20bfa6SDavid van Moolenbroek #endif
1409*9f20bfa6SDavid van Moolenbroek #ifdef INET
1410*9f20bfa6SDavid van Moolenbroek 	ctx.udp_fd = -1;
1411*9f20bfa6SDavid van Moolenbroek #endif
1412*9f20bfa6SDavid van Moolenbroek 	i = 0;
1413*9f20bfa6SDavid van Moolenbroek 	while ((opt = getopt_long(argc, argv, IF_OPTS, cf_options, &oi)) != -1)
1414*9f20bfa6SDavid van Moolenbroek 	{
1415*9f20bfa6SDavid van Moolenbroek 		switch (opt) {
1416*9f20bfa6SDavid van Moolenbroek 		case '4':
1417*9f20bfa6SDavid van Moolenbroek 			family = AF_INET;
1418*9f20bfa6SDavid van Moolenbroek 			break;
1419*9f20bfa6SDavid van Moolenbroek 		case '6':
1420*9f20bfa6SDavid van Moolenbroek 			family = AF_INET6;
1421*9f20bfa6SDavid van Moolenbroek 			break;
1422*9f20bfa6SDavid van Moolenbroek 		case 'f':
1423*9f20bfa6SDavid van Moolenbroek 			ctx.cffile = optarg;
1424*9f20bfa6SDavid van Moolenbroek 			break;
1425*9f20bfa6SDavid van Moolenbroek #ifdef USE_SIGNALS
1426*9f20bfa6SDavid van Moolenbroek 		case 'g':
1427*9f20bfa6SDavid van Moolenbroek 			sig = SIGUSR1;
1428*9f20bfa6SDavid van Moolenbroek 			siga = "USR1";
1429*9f20bfa6SDavid van Moolenbroek 			break;
1430*9f20bfa6SDavid van Moolenbroek 		case 'j':
1431*9f20bfa6SDavid van Moolenbroek 			ctx.logfile = strdup(optarg);
1432*9f20bfa6SDavid van Moolenbroek 			logger_close(&ctx);
1433*9f20bfa6SDavid van Moolenbroek 			logger_open(&ctx);
1434*9f20bfa6SDavid van Moolenbroek 			break;
1435*9f20bfa6SDavid van Moolenbroek 		case 'k':
1436*9f20bfa6SDavid van Moolenbroek 			sig = SIGALRM;
1437*9f20bfa6SDavid van Moolenbroek 			siga = "ARLM";
1438*9f20bfa6SDavid van Moolenbroek 			break;
1439*9f20bfa6SDavid van Moolenbroek 		case 'n':
1440*9f20bfa6SDavid van Moolenbroek 			sig = SIGHUP;
1441*9f20bfa6SDavid van Moolenbroek 			siga = "HUP";
1442*9f20bfa6SDavid van Moolenbroek 			break;
1443*9f20bfa6SDavid van Moolenbroek 		case 'x':
1444*9f20bfa6SDavid van Moolenbroek 			sig = SIGTERM;
1445*9f20bfa6SDavid van Moolenbroek 			siga = "TERM";;
1446*9f20bfa6SDavid van Moolenbroek 			break;
1447*9f20bfa6SDavid van Moolenbroek #endif
1448*9f20bfa6SDavid van Moolenbroek 		case 'T':
1449*9f20bfa6SDavid van Moolenbroek 			i = 1;
1450*9f20bfa6SDavid van Moolenbroek 			break;
1451*9f20bfa6SDavid van Moolenbroek 		case 'U':
1452*9f20bfa6SDavid van Moolenbroek 			i = 3;
1453*9f20bfa6SDavid van Moolenbroek 			break;
1454*9f20bfa6SDavid van Moolenbroek 		case 'V':
1455*9f20bfa6SDavid van Moolenbroek 			i = 2;
1456*9f20bfa6SDavid van Moolenbroek 			break;
1457*9f20bfa6SDavid van Moolenbroek 		case '?':
1458*9f20bfa6SDavid van Moolenbroek 			usage();
1459*9f20bfa6SDavid van Moolenbroek 			goto exit_failure;
1460*9f20bfa6SDavid van Moolenbroek 		}
1461*9f20bfa6SDavid van Moolenbroek 	}
1462*9f20bfa6SDavid van Moolenbroek 
1463*9f20bfa6SDavid van Moolenbroek 	ctx.argv = argv;
1464*9f20bfa6SDavid van Moolenbroek 	ctx.argc = argc;
1465*9f20bfa6SDavid van Moolenbroek 	ctx.ifc = argc - optind;
1466*9f20bfa6SDavid van Moolenbroek 	ctx.ifv = argv + optind;
1467*9f20bfa6SDavid van Moolenbroek 
1468*9f20bfa6SDavid van Moolenbroek 	ifo = read_config(&ctx, NULL, NULL, NULL);
1469*9f20bfa6SDavid van Moolenbroek 	if (ifo == NULL)
1470*9f20bfa6SDavid van Moolenbroek 		goto exit_failure;
1471*9f20bfa6SDavid van Moolenbroek 	opt = add_options(&ctx, NULL, ifo, argc, argv);
1472*9f20bfa6SDavid van Moolenbroek 	if (opt != 1) {
1473*9f20bfa6SDavid van Moolenbroek 		if (opt == 0)
1474*9f20bfa6SDavid van Moolenbroek 			usage();
1475*9f20bfa6SDavid van Moolenbroek 		goto exit_failure;
1476*9f20bfa6SDavid van Moolenbroek 	}
1477*9f20bfa6SDavid van Moolenbroek 	if (i == 2) {
1478*9f20bfa6SDavid van Moolenbroek 		printf("Interface options:\n");
1479*9f20bfa6SDavid van Moolenbroek 		if (optind == argc - 1) {
1480*9f20bfa6SDavid van Moolenbroek 			free_options(ifo);
1481*9f20bfa6SDavid van Moolenbroek 			ifo = read_config(&ctx, argv[optind], NULL, NULL);
1482*9f20bfa6SDavid van Moolenbroek 			if (ifo == NULL)
1483*9f20bfa6SDavid van Moolenbroek 				goto exit_failure;
1484*9f20bfa6SDavid van Moolenbroek 			add_options(&ctx, NULL, ifo, argc, argv);
1485*9f20bfa6SDavid van Moolenbroek 		}
1486*9f20bfa6SDavid van Moolenbroek 		if_printoptions();
1487*9f20bfa6SDavid van Moolenbroek #ifdef INET
1488*9f20bfa6SDavid van Moolenbroek 		if (family == 0 || family == AF_INET) {
1489*9f20bfa6SDavid van Moolenbroek 			printf("\nDHCPv4 options:\n");
1490*9f20bfa6SDavid van Moolenbroek 			dhcp_printoptions(&ctx,
1491*9f20bfa6SDavid van Moolenbroek 			    ifo->dhcp_override, ifo->dhcp_override_len);
1492*9f20bfa6SDavid van Moolenbroek 		}
1493*9f20bfa6SDavid van Moolenbroek #endif
1494*9f20bfa6SDavid van Moolenbroek #ifdef INET6
1495*9f20bfa6SDavid van Moolenbroek 		if (family == 0 || family == AF_INET6) {
1496*9f20bfa6SDavid van Moolenbroek 			printf("\nND options:\n");
1497*9f20bfa6SDavid van Moolenbroek 			ipv6nd_printoptions(&ctx,
1498*9f20bfa6SDavid van Moolenbroek 			    ifo->nd_override, ifo->nd_override_len);
1499*9f20bfa6SDavid van Moolenbroek 			printf("\nDHCPv6 options:\n");
1500*9f20bfa6SDavid van Moolenbroek 			dhcp6_printoptions(&ctx,
1501*9f20bfa6SDavid van Moolenbroek 			    ifo->dhcp6_override, ifo->dhcp6_override_len);
1502*9f20bfa6SDavid van Moolenbroek 		}
1503*9f20bfa6SDavid van Moolenbroek #endif
1504*9f20bfa6SDavid van Moolenbroek 		goto exit_success;
1505*9f20bfa6SDavid van Moolenbroek 	}
1506*9f20bfa6SDavid van Moolenbroek 	ctx.options = ifo->options;
1507*9f20bfa6SDavid van Moolenbroek 	if (i != 0) {
1508*9f20bfa6SDavid van Moolenbroek 		if (i == 1)
1509*9f20bfa6SDavid van Moolenbroek 			ctx.options |= DHCPCD_TEST;
1510*9f20bfa6SDavid van Moolenbroek 		else
1511*9f20bfa6SDavid van Moolenbroek 			ctx.options |= DHCPCD_DUMPLEASE;
1512*9f20bfa6SDavid van Moolenbroek 		ctx.options |= DHCPCD_PERSISTENT;
1513*9f20bfa6SDavid van Moolenbroek 		ctx.options &= ~DHCPCD_DAEMONISE;
1514*9f20bfa6SDavid van Moolenbroek 	}
1515*9f20bfa6SDavid van Moolenbroek 
1516*9f20bfa6SDavid van Moolenbroek #ifdef THERE_IS_NO_FORK
1517*9f20bfa6SDavid van Moolenbroek 	ctx.options &= ~DHCPCD_DAEMONISE;
1518*9f20bfa6SDavid van Moolenbroek #endif
1519*9f20bfa6SDavid van Moolenbroek 
1520*9f20bfa6SDavid van Moolenbroek 	if (ctx.options & DHCPCD_DEBUG)
1521*9f20bfa6SDavid van Moolenbroek 		logger_mask(&ctx, LOG_UPTO(LOG_DEBUG));
1522*9f20bfa6SDavid van Moolenbroek 	if (ctx.options & DHCPCD_QUIET) {
1523*9f20bfa6SDavid van Moolenbroek 		i = open(_PATH_DEVNULL, O_RDWR);
1524*9f20bfa6SDavid van Moolenbroek 		if (i == -1)
1525*9f20bfa6SDavid van Moolenbroek 			logger(&ctx, LOG_ERR, "%s: open: %m", __func__);
1526*9f20bfa6SDavid van Moolenbroek 		else {
1527*9f20bfa6SDavid van Moolenbroek 			dup2(i, STDERR_FILENO);
1528*9f20bfa6SDavid van Moolenbroek 			close(i);
1529*9f20bfa6SDavid van Moolenbroek 		}
1530*9f20bfa6SDavid van Moolenbroek 	}
1531*9f20bfa6SDavid van Moolenbroek 
1532*9f20bfa6SDavid van Moolenbroek 	if (!(ctx.options & (DHCPCD_TEST | DHCPCD_DUMPLEASE))) {
1533*9f20bfa6SDavid van Moolenbroek 		/* If we have any other args, we should run as a single dhcpcd
1534*9f20bfa6SDavid van Moolenbroek 		 *  instance for that interface. */
1535*9f20bfa6SDavid van Moolenbroek 		if (optind == argc - 1 && !(ctx.options & DHCPCD_MASTER)) {
1536*9f20bfa6SDavid van Moolenbroek 			const char *per;
1537*9f20bfa6SDavid van Moolenbroek 
1538*9f20bfa6SDavid van Moolenbroek 			if (strlen(argv[optind]) > IF_NAMESIZE) {
1539*9f20bfa6SDavid van Moolenbroek 				logger(&ctx, LOG_ERR,
1540*9f20bfa6SDavid van Moolenbroek 				    "%s: interface name too long",
1541*9f20bfa6SDavid van Moolenbroek 				    argv[optind]);
1542*9f20bfa6SDavid van Moolenbroek 				goto exit_failure;
1543*9f20bfa6SDavid van Moolenbroek 			}
1544*9f20bfa6SDavid van Moolenbroek 			/* Allow a dhcpcd interface per address family */
1545*9f20bfa6SDavid van Moolenbroek 			switch(family) {
1546*9f20bfa6SDavid van Moolenbroek 			case AF_INET:
1547*9f20bfa6SDavid van Moolenbroek 				per = "-4";
1548*9f20bfa6SDavid van Moolenbroek 				break;
1549*9f20bfa6SDavid van Moolenbroek 			case AF_INET6:
1550*9f20bfa6SDavid van Moolenbroek 				per = "-6";
1551*9f20bfa6SDavid van Moolenbroek 				break;
1552*9f20bfa6SDavid van Moolenbroek 			default:
1553*9f20bfa6SDavid van Moolenbroek 				per = "";
1554*9f20bfa6SDavid van Moolenbroek 			}
1555*9f20bfa6SDavid van Moolenbroek 			snprintf(ctx.pidfile, sizeof(ctx.pidfile),
1556*9f20bfa6SDavid van Moolenbroek 			    PIDFILE, "-", argv[optind], per);
1557*9f20bfa6SDavid van Moolenbroek 		} else {
1558*9f20bfa6SDavid van Moolenbroek 			snprintf(ctx.pidfile, sizeof(ctx.pidfile),
1559*9f20bfa6SDavid van Moolenbroek 			    PIDFILE, "", "", "");
1560*9f20bfa6SDavid van Moolenbroek 			ctx.options |= DHCPCD_MASTER;
1561*9f20bfa6SDavid van Moolenbroek 		}
1562*9f20bfa6SDavid van Moolenbroek 	}
1563*9f20bfa6SDavid van Moolenbroek 
1564*9f20bfa6SDavid van Moolenbroek 	if (chdir("/") == -1)
1565*9f20bfa6SDavid van Moolenbroek 		logger(&ctx, LOG_ERR, "chdir `/': %m");
1566*9f20bfa6SDavid van Moolenbroek 
1567*9f20bfa6SDavid van Moolenbroek 	/* Freeing allocated addresses from dumping leases can trigger
1568*9f20bfa6SDavid van Moolenbroek 	 * eloop removals as well, so init here. */
1569*9f20bfa6SDavid van Moolenbroek 	if ((ctx.eloop = eloop_new()) == NULL) {
1570*9f20bfa6SDavid van Moolenbroek 		logger(&ctx, LOG_ERR, "%s: eloop_init: %m", __func__);
1571*9f20bfa6SDavid van Moolenbroek 		goto exit_failure;
1572*9f20bfa6SDavid van Moolenbroek 	}
1573*9f20bfa6SDavid van Moolenbroek 
1574*9f20bfa6SDavid van Moolenbroek 	if (ctx.options & DHCPCD_DUMPLEASE) {
1575*9f20bfa6SDavid van Moolenbroek 		if (optind != argc - 1) {
1576*9f20bfa6SDavid van Moolenbroek 			logger(&ctx, LOG_ERR,
1577*9f20bfa6SDavid van Moolenbroek 			    "dumplease requires an interface");
1578*9f20bfa6SDavid van Moolenbroek 			goto exit_failure;
1579*9f20bfa6SDavid van Moolenbroek 		}
1580*9f20bfa6SDavid van Moolenbroek 		i = 0;
1581*9f20bfa6SDavid van Moolenbroek 		/* We need to try and find the interface so we can
1582*9f20bfa6SDavid van Moolenbroek 		 * load the hardware address to compare automated IAID */
1583*9f20bfa6SDavid van Moolenbroek 		ctx.ifaces = if_discover(&ctx, 1, argv + optind);
1584*9f20bfa6SDavid van Moolenbroek 		if (ctx.ifaces == NULL) {
1585*9f20bfa6SDavid van Moolenbroek 			logger(&ctx, LOG_ERR, "if_discover: %m");
1586*9f20bfa6SDavid van Moolenbroek 			goto exit_failure;
1587*9f20bfa6SDavid van Moolenbroek 		}
1588*9f20bfa6SDavid van Moolenbroek 		ifp = TAILQ_FIRST(ctx.ifaces);
1589*9f20bfa6SDavid van Moolenbroek 		if (ifp == NULL) {
1590*9f20bfa6SDavid van Moolenbroek 			ifp = calloc(1, sizeof(*ifp));
1591*9f20bfa6SDavid van Moolenbroek 			if (ifp == NULL) {
1592*9f20bfa6SDavid van Moolenbroek 				logger(&ctx, LOG_ERR, "%s: %m", __func__);
1593*9f20bfa6SDavid van Moolenbroek 				goto exit_failure;
1594*9f20bfa6SDavid van Moolenbroek 			}
1595*9f20bfa6SDavid van Moolenbroek 			strlcpy(ctx.pidfile, argv[optind], sizeof(ctx.pidfile));
1596*9f20bfa6SDavid van Moolenbroek 			ifp->ctx = &ctx;
1597*9f20bfa6SDavid van Moolenbroek 			TAILQ_INSERT_HEAD(ctx.ifaces, ifp, next);
1598*9f20bfa6SDavid van Moolenbroek 			if (family == 0) {
1599*9f20bfa6SDavid van Moolenbroek 				if (ctx.pidfile[strlen(ctx.pidfile) - 1] == '6')
1600*9f20bfa6SDavid van Moolenbroek 					family = AF_INET6;
1601*9f20bfa6SDavid van Moolenbroek 				else
1602*9f20bfa6SDavid van Moolenbroek 					family = AF_INET;
1603*9f20bfa6SDavid van Moolenbroek 			}
1604*9f20bfa6SDavid van Moolenbroek 		}
1605*9f20bfa6SDavid van Moolenbroek 		configure_interface(ifp, ctx.argc, ctx.argv, 0);
1606*9f20bfa6SDavid van Moolenbroek 		if (family == 0 || family == AF_INET) {
1607*9f20bfa6SDavid van Moolenbroek 			if (dhcp_dump(ifp) == -1)
1608*9f20bfa6SDavid van Moolenbroek 				i = 1;
1609*9f20bfa6SDavid van Moolenbroek 		}
1610*9f20bfa6SDavid van Moolenbroek 		if (family == 0 || family == AF_INET6) {
1611*9f20bfa6SDavid van Moolenbroek 			if (dhcp6_dump(ifp) == -1)
1612*9f20bfa6SDavid van Moolenbroek 				i = 1;
1613*9f20bfa6SDavid van Moolenbroek 		}
1614*9f20bfa6SDavid van Moolenbroek 		if (i == -1)
1615*9f20bfa6SDavid van Moolenbroek 			goto exit_failure;
1616*9f20bfa6SDavid van Moolenbroek 		goto exit_success;
1617*9f20bfa6SDavid van Moolenbroek 	}
1618*9f20bfa6SDavid van Moolenbroek 
1619*9f20bfa6SDavid van Moolenbroek #ifdef USE_SIGNALS
1620*9f20bfa6SDavid van Moolenbroek 	if (!(ctx.options & DHCPCD_TEST) &&
1621*9f20bfa6SDavid van Moolenbroek 	    (sig == 0 || ctx.ifc != 0))
1622*9f20bfa6SDavid van Moolenbroek 	{
1623*9f20bfa6SDavid van Moolenbroek #endif
1624*9f20bfa6SDavid van Moolenbroek 		if (ctx.options & DHCPCD_MASTER)
1625*9f20bfa6SDavid van Moolenbroek 			i = -1;
1626*9f20bfa6SDavid van Moolenbroek 		else
1627*9f20bfa6SDavid van Moolenbroek 			i = control_open(&ctx, argv[optind]);
1628*9f20bfa6SDavid van Moolenbroek 		if (i == -1)
1629*9f20bfa6SDavid van Moolenbroek 			i = control_open(&ctx, NULL);
1630*9f20bfa6SDavid van Moolenbroek 		if (i != -1) {
1631*9f20bfa6SDavid van Moolenbroek 			logger(&ctx, LOG_INFO,
1632*9f20bfa6SDavid van Moolenbroek 			    "sending commands to master dhcpcd process");
1633*9f20bfa6SDavid van Moolenbroek 			len = control_send(&ctx, argc, argv);
1634*9f20bfa6SDavid van Moolenbroek 			control_close(&ctx);
1635*9f20bfa6SDavid van Moolenbroek 			if (len > 0) {
1636*9f20bfa6SDavid van Moolenbroek 				logger(&ctx, LOG_DEBUG, "send OK");
1637*9f20bfa6SDavid van Moolenbroek 				goto exit_success;
1638*9f20bfa6SDavid van Moolenbroek 			} else {
1639*9f20bfa6SDavid van Moolenbroek 				logger(&ctx, LOG_ERR,
1640*9f20bfa6SDavid van Moolenbroek 				    "failed to send commands");
1641*9f20bfa6SDavid van Moolenbroek 				goto exit_failure;
1642*9f20bfa6SDavid van Moolenbroek 			}
1643*9f20bfa6SDavid van Moolenbroek 		} else {
1644*9f20bfa6SDavid van Moolenbroek 			if (errno != ENOENT)
1645*9f20bfa6SDavid van Moolenbroek 				logger(&ctx, LOG_ERR, "control_open: %m");
1646*9f20bfa6SDavid van Moolenbroek 		}
1647*9f20bfa6SDavid van Moolenbroek #ifdef USE_SIGNALS
1648*9f20bfa6SDavid van Moolenbroek 	}
1649*9f20bfa6SDavid van Moolenbroek #endif
1650*9f20bfa6SDavid van Moolenbroek 
1651*9f20bfa6SDavid van Moolenbroek #ifdef USE_SIGNALS
1652*9f20bfa6SDavid van Moolenbroek 	if (sig != 0) {
1653*9f20bfa6SDavid van Moolenbroek 		pid = read_pid(ctx.pidfile);
1654*9f20bfa6SDavid van Moolenbroek 		if (pid != 0)
1655*9f20bfa6SDavid van Moolenbroek 			logger(&ctx, LOG_INFO, "sending signal %s to pid %d",
1656*9f20bfa6SDavid van Moolenbroek 			    siga, pid);
1657*9f20bfa6SDavid van Moolenbroek 		if (pid == 0 || kill(pid, sig) != 0) {
1658*9f20bfa6SDavid van Moolenbroek 			if (sig != SIGHUP && errno != EPERM)
1659*9f20bfa6SDavid van Moolenbroek 				logger(&ctx, LOG_ERR, ""PACKAGE" not running");
1660*9f20bfa6SDavid van Moolenbroek 			if (pid != 0 && errno != ESRCH) {
1661*9f20bfa6SDavid van Moolenbroek 				logger(&ctx, LOG_ERR, "kill: %m");
1662*9f20bfa6SDavid van Moolenbroek 				goto exit_failure;
1663*9f20bfa6SDavid van Moolenbroek 			}
1664*9f20bfa6SDavid van Moolenbroek 			unlink(ctx.pidfile);
1665*9f20bfa6SDavid van Moolenbroek 			if (sig != SIGHUP)
1666*9f20bfa6SDavid van Moolenbroek 				goto exit_failure;
1667*9f20bfa6SDavid van Moolenbroek 		} else {
1668*9f20bfa6SDavid van Moolenbroek 			struct timespec ts;
1669*9f20bfa6SDavid van Moolenbroek 
1670*9f20bfa6SDavid van Moolenbroek 			if (sig == SIGHUP || sig == SIGUSR1)
1671*9f20bfa6SDavid van Moolenbroek 				goto exit_success;
1672*9f20bfa6SDavid van Moolenbroek 			/* Spin until it exits */
1673*9f20bfa6SDavid van Moolenbroek 			logger(&ctx, LOG_INFO,
1674*9f20bfa6SDavid van Moolenbroek 			    "waiting for pid %d to exit", pid);
1675*9f20bfa6SDavid van Moolenbroek 			ts.tv_sec = 0;
1676*9f20bfa6SDavid van Moolenbroek 			ts.tv_nsec = 100000000; /* 10th of a second */
1677*9f20bfa6SDavid van Moolenbroek 			for(i = 0; i < 100; i++) {
1678*9f20bfa6SDavid van Moolenbroek 				nanosleep(&ts, NULL);
1679*9f20bfa6SDavid van Moolenbroek 				if (read_pid(ctx.pidfile) == 0)
1680*9f20bfa6SDavid van Moolenbroek 					goto exit_success;
1681*9f20bfa6SDavid van Moolenbroek 			}
1682*9f20bfa6SDavid van Moolenbroek 			logger(&ctx, LOG_ERR, "pid %d failed to exit", pid);
1683*9f20bfa6SDavid van Moolenbroek 			goto exit_failure;
1684*9f20bfa6SDavid van Moolenbroek 		}
1685*9f20bfa6SDavid van Moolenbroek 	}
1686*9f20bfa6SDavid van Moolenbroek 
1687*9f20bfa6SDavid van Moolenbroek 	if (!(ctx.options & DHCPCD_TEST)) {
1688*9f20bfa6SDavid van Moolenbroek 		if ((pid = read_pid(ctx.pidfile)) > 0 &&
1689*9f20bfa6SDavid van Moolenbroek 		    kill(pid, 0) == 0)
1690*9f20bfa6SDavid van Moolenbroek 		{
1691*9f20bfa6SDavid van Moolenbroek 			logger(&ctx, LOG_ERR, ""PACKAGE
1692*9f20bfa6SDavid van Moolenbroek 			    " already running on pid %d (%s)",
1693*9f20bfa6SDavid van Moolenbroek 			    pid, ctx.pidfile);
1694*9f20bfa6SDavid van Moolenbroek 			goto exit_failure;
1695*9f20bfa6SDavid van Moolenbroek 		}
1696*9f20bfa6SDavid van Moolenbroek 
1697*9f20bfa6SDavid van Moolenbroek 		/* Ensure we have the needed directories */
1698*9f20bfa6SDavid van Moolenbroek 		if (mkdir(RUNDIR, 0755) == -1 && errno != EEXIST)
1699*9f20bfa6SDavid van Moolenbroek 			logger(&ctx, LOG_ERR, "mkdir `%s': %m", RUNDIR);
1700*9f20bfa6SDavid van Moolenbroek 		if (mkdir(DBDIR, 0755) == -1 && errno != EEXIST)
1701*9f20bfa6SDavid van Moolenbroek 			logger(&ctx, LOG_ERR, "mkdir `%s': %m", DBDIR);
1702*9f20bfa6SDavid van Moolenbroek 
1703*9f20bfa6SDavid van Moolenbroek 		opt = O_WRONLY | O_CREAT | O_NONBLOCK;
1704*9f20bfa6SDavid van Moolenbroek #ifdef O_CLOEXEC
1705*9f20bfa6SDavid van Moolenbroek 		opt |= O_CLOEXEC;
1706*9f20bfa6SDavid van Moolenbroek #endif
1707*9f20bfa6SDavid van Moolenbroek 		ctx.pid_fd = open(ctx.pidfile, opt, 0664);
1708*9f20bfa6SDavid van Moolenbroek 		if (ctx.pid_fd == -1)
1709*9f20bfa6SDavid van Moolenbroek 			logger(&ctx, LOG_ERR, "open `%s': %m", ctx.pidfile);
1710*9f20bfa6SDavid van Moolenbroek 		else {
1711*9f20bfa6SDavid van Moolenbroek #ifdef LOCK_EX
1712*9f20bfa6SDavid van Moolenbroek 			/* Lock the file so that only one instance of dhcpcd
1713*9f20bfa6SDavid van Moolenbroek 			 * runs on an interface */
1714*9f20bfa6SDavid van Moolenbroek 			if (flock(ctx.pid_fd, LOCK_EX | LOCK_NB) == -1) {
1715*9f20bfa6SDavid van Moolenbroek 				logger(&ctx, LOG_ERR, "flock `%s': %m", ctx.pidfile);
1716*9f20bfa6SDavid van Moolenbroek 				close(ctx.pid_fd);
1717*9f20bfa6SDavid van Moolenbroek 				ctx.pid_fd = -1;
1718*9f20bfa6SDavid van Moolenbroek 				goto exit_failure;
1719*9f20bfa6SDavid van Moolenbroek 			}
1720*9f20bfa6SDavid van Moolenbroek #endif
1721*9f20bfa6SDavid van Moolenbroek #ifndef O_CLOEXEC
1722*9f20bfa6SDavid van Moolenbroek 			if (fcntl(ctx.pid_fd, F_GETFD, &opt) == -1 ||
1723*9f20bfa6SDavid van Moolenbroek 			    fcntl(ctx.pid_fd, F_SETFD, opt | FD_CLOEXEC) == -1)
1724*9f20bfa6SDavid van Moolenbroek 			{
1725*9f20bfa6SDavid van Moolenbroek 				logger(&ctx, LOG_ERR, "fcntl: %m");
1726*9f20bfa6SDavid van Moolenbroek 				close(ctx.pid_fd);
1727*9f20bfa6SDavid van Moolenbroek 				ctx.pid_fd = -1;
1728*9f20bfa6SDavid van Moolenbroek 				goto exit_failure;
1729*9f20bfa6SDavid van Moolenbroek 			}
1730*9f20bfa6SDavid van Moolenbroek #endif
1731*9f20bfa6SDavid van Moolenbroek 			write_pid(ctx.pid_fd, getpid());
1732*9f20bfa6SDavid van Moolenbroek 		}
1733*9f20bfa6SDavid van Moolenbroek 	}
1734*9f20bfa6SDavid van Moolenbroek 
1735*9f20bfa6SDavid van Moolenbroek 	if (ctx.options & DHCPCD_MASTER) {
1736*9f20bfa6SDavid van Moolenbroek 		if (control_start(&ctx, NULL) == -1)
1737*9f20bfa6SDavid van Moolenbroek 			logger(&ctx, LOG_ERR, "control_start: %m");
1738*9f20bfa6SDavid van Moolenbroek 	}
1739*9f20bfa6SDavid van Moolenbroek #else
1740*9f20bfa6SDavid van Moolenbroek 	if (control_start(&ctx,
1741*9f20bfa6SDavid van Moolenbroek 	    ctx.options & DHCPCD_MASTER ? NULL : argv[optind]) == -1)
1742*9f20bfa6SDavid van Moolenbroek 	{
1743*9f20bfa6SDavid van Moolenbroek 		logger(&ctx, LOG_ERR, "control_start: %m");
1744*9f20bfa6SDavid van Moolenbroek 		goto exit_failure;
1745*9f20bfa6SDavid van Moolenbroek 	}
1746*9f20bfa6SDavid van Moolenbroek #endif
1747*9f20bfa6SDavid van Moolenbroek 
1748*9f20bfa6SDavid van Moolenbroek 	logger(&ctx, LOG_DEBUG, PACKAGE "-" VERSION " starting");
1749*9f20bfa6SDavid van Moolenbroek 	ctx.options |= DHCPCD_STARTED;
1750*9f20bfa6SDavid van Moolenbroek #ifdef USE_SIGNALS
1751*9f20bfa6SDavid van Moolenbroek 	if (eloop_signal_set_cb(ctx.eloop,
1752*9f20bfa6SDavid van Moolenbroek 	    dhcpcd_signals, dhcpcd_signals_len,
1753*9f20bfa6SDavid van Moolenbroek 	    signal_cb, &ctx) == -1)
1754*9f20bfa6SDavid van Moolenbroek 	{
1755*9f20bfa6SDavid van Moolenbroek 		logger(&ctx, LOG_ERR, "eloop_signal_mask: %m");
1756*9f20bfa6SDavid van Moolenbroek 		goto exit_failure;
1757*9f20bfa6SDavid van Moolenbroek 	}
1758*9f20bfa6SDavid van Moolenbroek 	/* Save signal mask, block and redirect signals to our handler */
1759*9f20bfa6SDavid van Moolenbroek 	if (eloop_signal_mask(ctx.eloop, &ctx.sigset) == -1) {
1760*9f20bfa6SDavid van Moolenbroek 		logger(&ctx, LOG_ERR, "eloop_signal_mask: %m");
1761*9f20bfa6SDavid van Moolenbroek 		goto exit_failure;
1762*9f20bfa6SDavid van Moolenbroek 	}
1763*9f20bfa6SDavid van Moolenbroek #endif
1764*9f20bfa6SDavid van Moolenbroek 
1765*9f20bfa6SDavid van Moolenbroek 	/* When running dhcpcd against a single interface, we need to retain
1766*9f20bfa6SDavid van Moolenbroek 	 * the old behaviour of waiting for an IP address */
1767*9f20bfa6SDavid van Moolenbroek 	if (ctx.ifc == 1 && !(ctx.options & DHCPCD_BACKGROUND))
1768*9f20bfa6SDavid van Moolenbroek 		ctx.options |= DHCPCD_WAITIP;
1769*9f20bfa6SDavid van Moolenbroek 
1770*9f20bfa6SDavid van Moolenbroek 	/* Open our persistent sockets. */
1771*9f20bfa6SDavid van Moolenbroek 	if (if_opensockets(&ctx) == -1) {
1772*9f20bfa6SDavid van Moolenbroek 		logger(&ctx, LOG_ERR, "if_opensockets: %m");
1773*9f20bfa6SDavid van Moolenbroek 		goto exit_failure;
1774*9f20bfa6SDavid van Moolenbroek 	}
1775*9f20bfa6SDavid van Moolenbroek 	eloop_event_add(ctx.eloop, ctx.link_fd, handle_link, &ctx, NULL, NULL);
1776*9f20bfa6SDavid van Moolenbroek 
1777*9f20bfa6SDavid van Moolenbroek 	/* Start any dev listening plugin which may want to
1778*9f20bfa6SDavid van Moolenbroek 	 * change the interface name provided by the kernel */
1779*9f20bfa6SDavid van Moolenbroek 	if ((ctx.options & (DHCPCD_MASTER | DHCPCD_DEV)) ==
1780*9f20bfa6SDavid van Moolenbroek 	    (DHCPCD_MASTER | DHCPCD_DEV))
1781*9f20bfa6SDavid van Moolenbroek 		dev_start(&ctx);
1782*9f20bfa6SDavid van Moolenbroek 
1783*9f20bfa6SDavid van Moolenbroek 	ctx.ifaces = if_discover(&ctx, ctx.ifc, ctx.ifv);
1784*9f20bfa6SDavid van Moolenbroek 	if (ctx.ifaces == NULL) {
1785*9f20bfa6SDavid van Moolenbroek 		logger(&ctx, LOG_ERR, "if_discover: %m");
1786*9f20bfa6SDavid van Moolenbroek 		goto exit_failure;
1787*9f20bfa6SDavid van Moolenbroek 	}
1788*9f20bfa6SDavid van Moolenbroek 	for (i = 0; i < ctx.ifc; i++) {
1789*9f20bfa6SDavid van Moolenbroek 		if (if_find(ctx.ifaces, ctx.ifv[i]) == NULL)
1790*9f20bfa6SDavid van Moolenbroek 			logger(&ctx, LOG_ERR,
1791*9f20bfa6SDavid van Moolenbroek 			    "%s: interface not found or invalid",
1792*9f20bfa6SDavid van Moolenbroek 			    ctx.ifv[i]);
1793*9f20bfa6SDavid van Moolenbroek 	}
1794*9f20bfa6SDavid van Moolenbroek 	if (TAILQ_FIRST(ctx.ifaces) == NULL) {
1795*9f20bfa6SDavid van Moolenbroek 		if (ctx.ifc == 0)
1796*9f20bfa6SDavid van Moolenbroek 			logger(&ctx, LOG_ERR, "no valid interfaces found");
1797*9f20bfa6SDavid van Moolenbroek 		else
1798*9f20bfa6SDavid van Moolenbroek 			goto exit_failure;
1799*9f20bfa6SDavid van Moolenbroek 		if (!(ctx.options & DHCPCD_LINK)) {
1800*9f20bfa6SDavid van Moolenbroek 			logger(&ctx, LOG_ERR,
1801*9f20bfa6SDavid van Moolenbroek 			    "aborting as link detection is disabled");
1802*9f20bfa6SDavid van Moolenbroek 			goto exit_failure;
1803*9f20bfa6SDavid van Moolenbroek 		}
1804*9f20bfa6SDavid van Moolenbroek 	}
1805*9f20bfa6SDavid van Moolenbroek 
1806*9f20bfa6SDavid van Moolenbroek 	TAILQ_FOREACH(ifp, ctx.ifaces, next) {
1807*9f20bfa6SDavid van Moolenbroek 		dhcpcd_initstate1(ifp, argc, argv, 0);
1808*9f20bfa6SDavid van Moolenbroek 	}
1809*9f20bfa6SDavid van Moolenbroek 
1810*9f20bfa6SDavid van Moolenbroek 	if (ctx.options & DHCPCD_BACKGROUND && dhcpcd_daemonise(&ctx))
1811*9f20bfa6SDavid van Moolenbroek 		goto exit_success;
1812*9f20bfa6SDavid van Moolenbroek 
1813*9f20bfa6SDavid van Moolenbroek 	opt = 0;
1814*9f20bfa6SDavid van Moolenbroek 	TAILQ_FOREACH(ifp, ctx.ifaces, next) {
1815*9f20bfa6SDavid van Moolenbroek 		run_preinit(ifp);
1816*9f20bfa6SDavid van Moolenbroek 		if (ifp->carrier != LINK_DOWN)
1817*9f20bfa6SDavid van Moolenbroek 			opt = 1;
1818*9f20bfa6SDavid van Moolenbroek 	}
1819*9f20bfa6SDavid van Moolenbroek 
1820*9f20bfa6SDavid van Moolenbroek 	if (!(ctx.options & DHCPCD_BACKGROUND)) {
1821*9f20bfa6SDavid van Moolenbroek 		if (ctx.options & DHCPCD_MASTER)
1822*9f20bfa6SDavid van Moolenbroek 			t = ifo->timeout;
1823*9f20bfa6SDavid van Moolenbroek 		else if ((ifp = TAILQ_FIRST(ctx.ifaces)))
1824*9f20bfa6SDavid van Moolenbroek 			t = ifp->options->timeout;
1825*9f20bfa6SDavid van Moolenbroek 		else
1826*9f20bfa6SDavid van Moolenbroek 			t = 0;
1827*9f20bfa6SDavid van Moolenbroek 		if (opt == 0 &&
1828*9f20bfa6SDavid van Moolenbroek 		    ctx.options & DHCPCD_LINK &&
1829*9f20bfa6SDavid van Moolenbroek 		    !(ctx.options & DHCPCD_WAITIP))
1830*9f20bfa6SDavid van Moolenbroek 		{
1831*9f20bfa6SDavid van Moolenbroek 			logger(&ctx, LOG_WARNING,
1832*9f20bfa6SDavid van Moolenbroek 			    "no interfaces have a carrier");
1833*9f20bfa6SDavid van Moolenbroek 			if (dhcpcd_daemonise(&ctx))
1834*9f20bfa6SDavid van Moolenbroek 				goto exit_success;
1835*9f20bfa6SDavid van Moolenbroek 		} else if (t > 0 &&
1836*9f20bfa6SDavid van Moolenbroek 		    /* Test mode removes the daemonise bit, so check for both */
1837*9f20bfa6SDavid van Moolenbroek 		    ctx.options & (DHCPCD_DAEMONISE | DHCPCD_TEST))
1838*9f20bfa6SDavid van Moolenbroek 		{
1839*9f20bfa6SDavid van Moolenbroek 			eloop_timeout_add_sec(ctx.eloop, t,
1840*9f20bfa6SDavid van Moolenbroek 			    handle_exit_timeout, &ctx);
1841*9f20bfa6SDavid van Moolenbroek 		}
1842*9f20bfa6SDavid van Moolenbroek 	}
1843*9f20bfa6SDavid van Moolenbroek 	free_options(ifo);
1844*9f20bfa6SDavid van Moolenbroek 	ifo = NULL;
1845*9f20bfa6SDavid van Moolenbroek 
1846*9f20bfa6SDavid van Moolenbroek 	if_sortinterfaces(&ctx);
1847*9f20bfa6SDavid van Moolenbroek 	TAILQ_FOREACH(ifp, ctx.ifaces, next) {
1848*9f20bfa6SDavid van Moolenbroek 		eloop_timeout_add_sec(ctx.eloop, 0,
1849*9f20bfa6SDavid van Moolenbroek 		    dhcpcd_prestartinterface, ifp);
1850*9f20bfa6SDavid van Moolenbroek 	}
1851*9f20bfa6SDavid van Moolenbroek 
1852*9f20bfa6SDavid van Moolenbroek 	i = eloop_start(ctx.eloop, &ctx.sigset);
1853*9f20bfa6SDavid van Moolenbroek 	if (i < 0) {
1854*9f20bfa6SDavid van Moolenbroek 		syslog(LOG_ERR, "eloop_start: %m");
1855*9f20bfa6SDavid van Moolenbroek 		goto exit_failure;
1856*9f20bfa6SDavid van Moolenbroek 	}
1857*9f20bfa6SDavid van Moolenbroek 	goto exit1;
1858*9f20bfa6SDavid van Moolenbroek 
1859*9f20bfa6SDavid van Moolenbroek exit_success:
1860*9f20bfa6SDavid van Moolenbroek 	i = EXIT_SUCCESS;
1861*9f20bfa6SDavid van Moolenbroek 	goto exit1;
1862*9f20bfa6SDavid van Moolenbroek 
1863*9f20bfa6SDavid van Moolenbroek exit_failure:
1864*9f20bfa6SDavid van Moolenbroek 	i = EXIT_FAILURE;
1865*9f20bfa6SDavid van Moolenbroek 
1866*9f20bfa6SDavid van Moolenbroek exit1:
1867*9f20bfa6SDavid van Moolenbroek 	/* Free memory and close fd's */
1868*9f20bfa6SDavid van Moolenbroek 	if (ctx.ifaces) {
1869*9f20bfa6SDavid van Moolenbroek 		while ((ifp = TAILQ_FIRST(ctx.ifaces))) {
1870*9f20bfa6SDavid van Moolenbroek 			TAILQ_REMOVE(ctx.ifaces, ifp, next);
1871*9f20bfa6SDavid van Moolenbroek 			if_free(ifp);
1872*9f20bfa6SDavid van Moolenbroek 		}
1873*9f20bfa6SDavid van Moolenbroek 		free(ctx.ifaces);
1874*9f20bfa6SDavid van Moolenbroek 	}
1875*9f20bfa6SDavid van Moolenbroek 	free(ctx.duid);
1876*9f20bfa6SDavid van Moolenbroek 	if (ctx.link_fd != -1) {
1877*9f20bfa6SDavid van Moolenbroek 		eloop_event_delete(ctx.eloop, ctx.link_fd);
1878*9f20bfa6SDavid van Moolenbroek 		close(ctx.link_fd);
1879*9f20bfa6SDavid van Moolenbroek 	}
1880*9f20bfa6SDavid van Moolenbroek 	if (ctx.pf_inet_fd != -1)
1881*9f20bfa6SDavid van Moolenbroek 		close(ctx.pf_inet_fd);
1882*9f20bfa6SDavid van Moolenbroek #if defined(INET6) && defined(BSD)
1883*9f20bfa6SDavid van Moolenbroek 	if (ctx.pf_inet6_fd != -1)
1884*9f20bfa6SDavid van Moolenbroek 		close(ctx.pf_inet6_fd);
1885*9f20bfa6SDavid van Moolenbroek #endif
1886*9f20bfa6SDavid van Moolenbroek #ifdef IFLR_ACTIVE
1887*9f20bfa6SDavid van Moolenbroek 	if (ctx.pf_link_fd != -1)
1888*9f20bfa6SDavid van Moolenbroek 		close(ctx.pf_link_fd);
1889*9f20bfa6SDavid van Moolenbroek #endif
1890*9f20bfa6SDavid van Moolenbroek 
1891*9f20bfa6SDavid van Moolenbroek 
1892*9f20bfa6SDavid van Moolenbroek 	free_options(ifo);
1893*9f20bfa6SDavid van Moolenbroek 	free_globals(&ctx);
1894*9f20bfa6SDavid van Moolenbroek 	ipv4_ctxfree(&ctx);
1895*9f20bfa6SDavid van Moolenbroek 	ipv6_ctxfree(&ctx);
1896*9f20bfa6SDavid van Moolenbroek 	dev_stop(&ctx);
1897*9f20bfa6SDavid van Moolenbroek 	if (control_stop(&ctx) == -1)
1898*9f20bfa6SDavid van Moolenbroek 		logger(&ctx, LOG_ERR, "control_stop: %m:");
1899*9f20bfa6SDavid van Moolenbroek 	if (ctx.pid_fd != -1) {
1900*9f20bfa6SDavid van Moolenbroek 		close(ctx.pid_fd);
1901*9f20bfa6SDavid van Moolenbroek 		unlink(ctx.pidfile);
1902*9f20bfa6SDavid van Moolenbroek 	}
1903*9f20bfa6SDavid van Moolenbroek 	eloop_free(ctx.eloop);
1904*9f20bfa6SDavid van Moolenbroek 
1905*9f20bfa6SDavid van Moolenbroek 	if (ctx.options & DHCPCD_STARTED && !(ctx.options & DHCPCD_FORKED))
1906*9f20bfa6SDavid van Moolenbroek 		logger(&ctx, LOG_INFO, PACKAGE " exited");
1907*9f20bfa6SDavid van Moolenbroek 	logger_close(&ctx);
1908*9f20bfa6SDavid van Moolenbroek 	free(ctx.logfile);
1909*9f20bfa6SDavid van Moolenbroek 	return i;
1910*9f20bfa6SDavid van Moolenbroek }
1911