• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * dhcpcd - DHCP client daemon
3  * Copyright (c) 2006-2012 Roy Marples <roy@marples.name>
4  * All rights reserved
5 
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  */
27 
28 #include <sys/stat.h>
29 #include <sys/uio.h>
30 #include <sys/wait.h>
31 
32 #include <netinet/in.h>
33 #include <arpa/inet.h>
34 
35 #include <ctype.h>
36 #include <errno.h>
37 #include <signal.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <syslog.h>
41 #include <unistd.h>
42 
43 #include "config.h"
44 #include "common.h"
45 #include "configure.h"
46 #include "dhcp.h"
47 #include "if-options.h"
48 #include "if-pref.h"
49 #include "ipv6rs.h"
50 #include "net.h"
51 #include "signals.h"
52 
53 #define DEFAULT_PATH	"PATH=/usr/bin:/usr/sbin:/bin:/sbin"
54 
55 /* Some systems have route metrics */
56 #ifndef HAVE_ROUTE_METRIC
57 # ifdef __linux__
58 #  define HAVE_ROUTE_METRIC 1
59 # endif
60 # ifndef HAVE_ROUTE_METRIC
61 #  define HAVE_ROUTE_METRIC 0
62 # endif
63 #endif
64 
65 static struct rt *routes;
66 
67 static int
exec_script(char * const * argv,char * const * env)68 exec_script(char *const *argv, char *const *env)
69 {
70 	pid_t pid;
71 	sigset_t full;
72 	sigset_t old;
73 
74 	/* OK, we need to block signals */
75 	sigfillset(&full);
76 	sigprocmask(SIG_SETMASK, &full, &old);
77 	signal_reset();
78 
79 	switch (pid = vfork()) {
80 	case -1:
81 		syslog(LOG_ERR, "vfork: %m");
82 		break;
83 	case 0:
84 		sigprocmask(SIG_SETMASK, &old, NULL);
85 		execve(argv[0], argv, env);
86 		syslog(LOG_ERR, "%s: %m", argv[0]);
87 		_exit(127);
88 		/* NOTREACHED */
89 	}
90 
91 	/* Restore our signals */
92 	signal_setup();
93 	sigprocmask(SIG_SETMASK, &old, NULL);
94 	return pid;
95 }
96 
97 static char *
make_var(const char * prefix,const char * var)98 make_var(const char *prefix, const char *var)
99 {
100 	size_t len;
101 	char *v;
102 
103 	len = strlen(prefix) + strlen(var) + 2;
104 	v = xmalloc(len);
105 	snprintf(v, len, "%s_%s", prefix, var);
106 	return v;
107 }
108 
109 
110 static void
append_config(char *** env,ssize_t * len,const char * prefix,const char * const * config)111 append_config(char ***env, ssize_t *len,
112     const char *prefix, const char *const *config)
113 {
114 	ssize_t i, j, e1;
115 	char **ne, *eq;
116 
117 	if (config == NULL)
118 		return;
119 
120 	ne = *env;
121 	for (i = 0; config[i] != NULL; i++) {
122 		eq = strchr(config[i], '=');
123 		e1 = eq - config[i] + 1;
124 		for (j = 0; j < *len; j++) {
125 			if (strncmp(ne[j] + strlen(prefix) + 1,
126 				config[i], e1) == 0)
127 			{
128 				free(ne[j]);
129 				ne[j] = make_var(prefix, config[i]);
130 				break;
131 			}
132 		}
133 		if (j == *len) {
134 			j++;
135 			ne = xrealloc(ne, sizeof(char *) * (j + 1));
136 			ne[j - 1] = make_var(prefix, config[i]);
137 			*len = j;
138 		}
139 	}
140 	*env = ne;
141 }
142 
143 static size_t
arraytostr(const char * const * argv,char ** s)144 arraytostr(const char *const *argv, char **s)
145 {
146 	const char *const *ap;
147 	char *p;
148 	size_t len, l;
149 
150 	len = 0;
151 	ap = argv;
152 	while (*ap)
153 		len += strlen(*ap++) + 1;
154 	*s = p = xmalloc(len);
155 	ap = argv;
156 	while (*ap) {
157 		l = strlen(*ap) + 1;
158 		memcpy(p, *ap, l);
159 		p += l;
160 		ap++;
161 	}
162 	return len;
163 }
164 
165 static ssize_t
make_env(const struct interface * iface,const char * reason,char *** argv)166 make_env(const struct interface *iface, const char *reason, char ***argv)
167 {
168 	char **env, *p;
169 	ssize_t e, elen, l;
170 	const struct if_options *ifo = iface->state->options;
171 	const struct interface *ifp;
172 	int dhcp, ra;
173 
174 	dhcp = ra = 0;
175 	if (strcmp(reason, "ROUTERADVERT") == 0)
176 		ra = 1;
177 	else
178 		dhcp = 1;
179 
180 	/* When dumping the lease, we only want to report interface and
181 	   reason - the other interface variables are meaningless */
182 	if (options & DHCPCD_DUMPLEASE)
183 		elen = 2;
184 	else
185 		elen = 10;
186 
187 	/* Make our env */
188 	env = xmalloc(sizeof(char *) * (elen + 1));
189 	e = strlen("interface") + strlen(iface->name) + 2;
190 	env[0] = xmalloc(e);
191 	snprintf(env[0], e, "interface=%s", iface->name);
192 	e = strlen("reason") + strlen(reason) + 2;
193 	env[1] = xmalloc(e);
194 	snprintf(env[1], e, "reason=%s", reason);
195 	if (options & DHCPCD_DUMPLEASE)
196 		goto dumplease;
197 
198  	e = 20;
199 	env[2] = xmalloc(e);
200 	snprintf(env[2], e, "pid=%d", getpid());
201 	env[3] = xmalloc(e);
202 	snprintf(env[3], e, "ifmetric=%d", iface->metric);
203 	env[4] = xmalloc(e);
204 	snprintf(env[4], e, "ifwireless=%d", iface->wireless);
205 	env[5] = xmalloc(e);
206 	snprintf(env[5], e, "ifflags=%u", iface->flags);
207 	env[6] = xmalloc(e);
208 	snprintf(env[6], e, "ifmtu=%d", get_mtu(iface->name));
209 	l = e = strlen("interface_order=");
210 	for (ifp = ifaces; ifp; ifp = ifp->next)
211 		e += strlen(ifp->name) + 1;
212 	p = env[7] = xmalloc(e);
213 	strlcpy(p, "interface_order=", e);
214 	e -= l;
215 	p += l;
216 	for (ifp = ifaces; ifp; ifp = ifp->next) {
217 		l = strlcpy(p, ifp->name, e);
218 		p += l;
219 		e -= l;
220 		*p++ = ' ';
221 		e--;
222 	}
223 	*--p = '\0';
224 	if ((dhcp && iface->state->new) || (ra && iface->ras)) {
225 		env[8] = strdup("if_up=true");
226 		env[9] = strdup("if_down=false");
227 	} else {
228 		env[8] = strdup("if_up=false");
229 		env[9] = strdup("if_down=true");
230 	}
231 	if (*iface->state->profile) {
232 		e = strlen("profile=") + strlen(iface->state->profile) + 2;
233 		env[elen] = xmalloc(e);
234 		snprintf(env[elen++], e, "profile=%s", iface->state->profile);
235 	}
236 	if (iface->wireless) {
237 		e = strlen("new_ssid=") + strlen(iface->ssid) + 2;
238 		if (iface->state->new != NULL ||
239 		    strcmp(iface->state->reason, "CARRIER") == 0)
240 		{
241 			env = xrealloc(env, sizeof(char *) * (elen + 2));
242 			env[elen] = xmalloc(e);
243 			snprintf(env[elen++], e, "new_ssid=%s", iface->ssid);
244 		}
245 		if (iface->state->old != NULL ||
246 		    strcmp(iface->state->reason, "NOCARRIER") == 0)
247 		{
248 			env = xrealloc(env, sizeof(char *) * (elen + 2));
249 			env[elen] = xmalloc(e);
250 			snprintf(env[elen++], e, "old_ssid=%s", iface->ssid);
251 		}
252 	}
253 	if (dhcp && iface->state->old) {
254 		e = configure_env(NULL, NULL, iface->state->old, ifo);
255 		if (e > 0) {
256 			env = xrealloc(env, sizeof(char *) * (elen + e + 1));
257 			elen += configure_env(env + elen, "old",
258 			    iface->state->old, ifo);
259 		}
260 		append_config(&env, &elen, "old",
261 		    (const char *const *)ifo->config);
262 	}
263 
264 dumplease:
265 	if (dhcp && iface->state->new) {
266 		e = configure_env(NULL, NULL, iface->state->new, ifo);
267 		if (e > 0) {
268 			env = xrealloc(env, sizeof(char *) * (elen + e + 1));
269 			elen += configure_env(env + elen, "new",
270 			    iface->state->new, ifo);
271 		}
272 		append_config(&env, &elen, "new",
273 		    (const char *const *)ifo->config);
274 	}
275 	if (ra) {
276 		e = ipv6rs_env(NULL, NULL, iface);
277 		if (e > 0) {
278 			env = xrealloc(env, sizeof(char *) * (elen + e + 1));
279 			elen += ipv6rs_env(env + elen, NULL, iface);
280 		}
281 	}
282 
283 	/* Add our base environment */
284 	if (ifo->environ) {
285 		e = 0;
286 		while (ifo->environ[e++])
287 			;
288 		env = xrealloc(env, sizeof(char *) * (elen + e + 1));
289 		e = 0;
290 		while (ifo->environ[e]) {
291 			env[elen + e] = xstrdup(ifo->environ[e]);
292 			e++;
293 		}
294 		elen += e;
295 	}
296 	env[elen] = '\0';
297 
298 	*argv = env;
299 	return elen;
300 }
301 
302 static int
send_interface1(int fd,const struct interface * iface,const char * reason)303 send_interface1(int fd, const struct interface *iface, const char *reason)
304 {
305 	char **env, **ep, *s;
306 	ssize_t elen;
307 	struct iovec iov[2];
308 	int retval;
309 
310 	retval = 0;
311 	make_env(iface, reason, &env);
312 	elen = arraytostr((const char *const *)env, &s);
313 	iov[0].iov_base = &elen;
314 	iov[0].iov_len = sizeof(ssize_t);
315 	iov[1].iov_base = s;
316 	iov[1].iov_len = elen;
317 	retval = writev(fd, iov, 2);
318 	ep = env;
319 	while (*ep)
320 		free(*ep++);
321 	free(env);
322 	free(s);
323 	return retval;
324 }
325 
326 int
send_interface(int fd,const struct interface * iface)327 send_interface(int fd, const struct interface *iface)
328 {
329 	int retval = 0;
330 	if (send_interface1(fd, iface, iface->state->reason) == -1)
331 		retval = -1;
332 	if (iface->ras) {
333 		if (send_interface1(fd, iface, "ROUTERADVERT") == -1)
334 			retval = -1;
335 	}
336 	return retval;
337 }
338 
339 int
run_script_reason(const struct interface * iface,const char * reason)340 run_script_reason(const struct interface *iface, const char *reason)
341 {
342 	char *const argv[2] = { UNCONST(iface->state->options->script), NULL };
343 	char **env = NULL, **ep;
344 	char *path, *bigenv;
345 	ssize_t e, elen = 0;
346 	pid_t pid;
347 	int status = 0;
348 	const struct fd_list *fd;
349 	struct iovec iov[2];
350 
351 	if (iface->state->options->script == NULL ||
352 	    iface->state->options->script[0] == '\0' ||
353 	    strcmp(iface->state->options->script, "/dev/null") == 0)
354 		return 0;
355 
356 	if (reason == NULL)
357 		reason = iface->state->reason;
358 	syslog(LOG_DEBUG, "%s: executing `%s', reason %s",
359 	    iface->name, argv[0], reason);
360 
361 	/* Make our env */
362 	elen = make_env(iface, reason, &env);
363 	env = xrealloc(env, sizeof(char *) * (elen + 2));
364 	/* Add path to it */
365 	path = getenv("PATH");
366 	if (path) {
367 		e = strlen("PATH") + strlen(path) + 2;
368 		env[elen] = xmalloc(e);
369 		snprintf(env[elen], e, "PATH=%s", path);
370 	} else
371 		env[elen] = xstrdup(DEFAULT_PATH);
372 	env[++elen] = '\0';
373 
374 	pid = exec_script(argv, env);
375 	if (pid == -1)
376 		status = -1;
377 	else if (pid != 0) {
378 		/* Wait for the script to finish */
379 		while (waitpid(pid, &status, 0) == -1) {
380 			if (errno != EINTR) {
381 				syslog(LOG_ERR, "waitpid: %m");
382 				status = -1;
383 				break;
384 			}
385 		}
386 	}
387 
388 	/* Send to our listeners */
389 	bigenv = NULL;
390 	for (fd = fds; fd != NULL; fd = fd->next) {
391 		if (fd->listener) {
392 			if (bigenv == NULL) {
393 				elen = arraytostr((const char *const *)env,
394 				    &bigenv);
395 				iov[0].iov_base = &elen;
396 				iov[0].iov_len = sizeof(ssize_t);
397 				iov[1].iov_base = bigenv;
398 				iov[1].iov_len = elen;
399 			}
400 			if (writev(fd->fd, iov, 2) == -1)
401 				syslog(LOG_ERR, "writev: %m");
402 		}
403 	}
404 	free(bigenv);
405 
406 	/* Cleanup */
407 	ep = env;
408 	while (*ep)
409 		free(*ep++);
410 	free(env);
411 	return status;
412 }
413 
414 static struct rt *
find_route(struct rt * rts,const struct rt * r,struct rt ** lrt,const struct rt * srt)415 find_route(struct rt *rts, const struct rt *r, struct rt **lrt,
416     const struct rt *srt)
417 {
418 	struct rt *rt;
419 
420 	if (lrt)
421 		*lrt = NULL;
422 	for (rt = rts; rt; rt = rt->next) {
423 		if (rt->dest.s_addr == r->dest.s_addr &&
424 #if HAVE_ROUTE_METRIC
425 		    (srt || (!rt->iface ||
426 			rt->iface->metric == r->iface->metric)) &&
427 #endif
428                     (!srt || srt != rt) &&
429 		    rt->net.s_addr == r->net.s_addr)
430 			return rt;
431 		if (lrt)
432 			*lrt = rt;
433 	}
434 	return NULL;
435 }
436 
437 static void
desc_route(const char * cmd,const struct rt * rt)438 desc_route(const char *cmd, const struct rt *rt)
439 {
440 	char addr[sizeof("000.000.000.000") + 1];
441 	const char *ifname = rt->iface->name;
442 
443 	strlcpy(addr, inet_ntoa(rt->dest), sizeof(addr));
444 	if (rt->gate.s_addr == INADDR_ANY)
445 		syslog(LOG_DEBUG, "%s: %s route to %s/%d", ifname, cmd,
446 		    addr, inet_ntocidr(rt->net));
447 	else if (rt->gate.s_addr == rt->dest.s_addr &&
448 	    rt->net.s_addr == INADDR_BROADCAST)
449 		syslog(LOG_DEBUG, "%s: %s host route to %s", ifname, cmd,
450 		    addr);
451 	else if (rt->dest.s_addr == INADDR_ANY && rt->net.s_addr == INADDR_ANY)
452 		syslog(LOG_DEBUG, "%s: %s default route via %s", ifname, cmd,
453 		    inet_ntoa(rt->gate));
454 	else
455 		syslog(LOG_DEBUG, "%s: %s route to %s/%d via %s", ifname, cmd,
456 		    addr, inet_ntocidr(rt->net), inet_ntoa(rt->gate));
457 }
458 
459 /* If something other than dhcpcd removes a route,
460  * we need to remove it from our internal table. */
461 int
route_deleted(const struct rt * rt)462 route_deleted(const struct rt *rt)
463 {
464 	struct rt *f, *l;
465 
466 	f = find_route(routes, rt, &l, NULL);
467 	if (f == NULL)
468 		return 0;
469 	desc_route("removing", f);
470 	if (l)
471 		l->next = f->next;
472 	else
473 		routes = f->next;
474 	free(f);
475 	return 1;
476 }
477 
478 static int
n_route(struct rt * rt)479 n_route(struct rt *rt)
480 {
481 	/* Don't set default routes if not asked to */
482 	if (rt->dest.s_addr == 0 &&
483 	    rt->net.s_addr == 0 &&
484 	    !(rt->iface->state->options->options & DHCPCD_GATEWAY))
485 		return -1;
486 
487 	desc_route("adding", rt);
488 	if (!add_route(rt))
489 		return 0;
490 	if (errno == EEXIST) {
491 		/* Pretend we added the subnet route */
492 		if (rt->dest.s_addr ==
493 		    (rt->iface->addr.s_addr & rt->iface->net.s_addr) &&
494 		    rt->net.s_addr == rt->iface->net.s_addr &&
495 		    rt->gate.s_addr == 0)
496 			return 0;
497 		else
498 			return -1;
499 	}
500 	syslog(LOG_ERR, "%s: add_route: %m", rt->iface->name);
501 	return -1;
502 }
503 
504 static int
c_route(struct rt * ort,struct rt * nrt)505 c_route(struct rt *ort, struct rt *nrt)
506 {
507 	/* Don't set default routes if not asked to */
508 	if (nrt->dest.s_addr == 0 &&
509 	    nrt->net.s_addr == 0 &&
510 	    !(nrt->iface->state->options->options & DHCPCD_GATEWAY))
511 		return -1;
512 
513 	desc_route("changing", nrt);
514 	/* We delete and add the route so that we can change metric.
515 	 * This also has the nice side effect of flushing ARP entries so
516 	 * we don't have to do that manually. */
517 	del_route(ort);
518 	if (!add_route(nrt))
519 		return 0;
520 	syslog(LOG_ERR, "%s: add_route: %m", nrt->iface->name);
521 	return -1;
522 }
523 
524 static int
d_route(struct rt * rt)525 d_route(struct rt *rt)
526 {
527 	int retval;
528 
529 	desc_route("deleting", rt);
530 	retval = del_route(rt);
531 	if (retval != 0 && errno != ENOENT && errno != ESRCH)
532 		syslog(LOG_ERR,"%s: del_route: %m", rt->iface->name);
533 	return retval;
534 }
535 
536 static struct rt *
get_subnet_route(struct dhcp_message * dhcp)537 get_subnet_route(struct dhcp_message *dhcp)
538 {
539 	in_addr_t addr;
540 	struct in_addr net;
541 	struct rt *rt;
542 
543 	addr = dhcp->yiaddr;
544 	if (addr == 0)
545 		addr = dhcp->ciaddr;
546 	/* Ensure we have all the needed values */
547 	if (get_option_addr(&net, dhcp, DHO_SUBNETMASK) == -1)
548 		net.s_addr = get_netmask(addr);
549 	if (net.s_addr == INADDR_BROADCAST || net.s_addr == INADDR_ANY)
550 		return NULL;
551 	rt = malloc(sizeof(*rt));
552 	rt->dest.s_addr = addr & net.s_addr;
553 	rt->net.s_addr = net.s_addr;
554 	rt->gate.s_addr = 0;
555 	return rt;
556 }
557 
558 static struct rt *
add_subnet_route(struct rt * rt,const struct interface * iface)559 add_subnet_route(struct rt *rt, const struct interface *iface)
560 {
561 	struct rt *r;
562 
563 	if (iface->net.s_addr == INADDR_BROADCAST ||
564 	    iface->net.s_addr == INADDR_ANY ||
565 	    (iface->state->options->options &
566 	     (DHCPCD_INFORM | DHCPCD_STATIC) &&
567 	     iface->state->options->req_addr.s_addr == INADDR_ANY))
568 		return rt;
569 
570 	r = xmalloc(sizeof(*r));
571 	r->dest.s_addr = iface->addr.s_addr & iface->net.s_addr;
572 	r->net.s_addr = iface->net.s_addr;
573 	r->gate.s_addr = 0;
574 	r->next = rt;
575 	return r;
576 }
577 
578 static struct rt *
get_routes(const struct interface * iface)579 get_routes(const struct interface *iface)
580 {
581 	struct rt *rt, *nrt = NULL, *r = NULL;
582 
583 	if (iface->state->options->routes != NULL) {
584 		for (rt = iface->state->options->routes;
585 		     rt != NULL;
586 		     rt = rt->next)
587 		{
588 			if (rt->gate.s_addr == 0)
589 				break;
590 			if (r == NULL)
591 				r = nrt = xmalloc(sizeof(*r));
592 			else {
593 				r->next = xmalloc(sizeof(*r));
594 				r = r->next;
595 			}
596 			memcpy(r, rt, sizeof(*r));
597 			r->next = NULL;
598 		}
599 		return nrt;
600 	}
601 
602 	return get_option_routes(iface->state->new,
603 	    iface->name, &iface->state->options->options);
604 }
605 
606 /* Some DHCP servers add set host routes by setting the gateway
607  * to the assinged IP address. This differs from our notion of a host route
608  * where the gateway is the destination address, so we fix it. */
609 static struct rt *
massage_host_routes(struct rt * rt,const struct interface * iface)610 massage_host_routes(struct rt *rt, const struct interface *iface)
611 {
612 	struct rt *r;
613 
614 	for (r = rt; r; r = r->next)
615 		if (r->gate.s_addr == iface->addr.s_addr &&
616 		    r->net.s_addr == INADDR_BROADCAST)
617 			r->gate.s_addr = r->dest.s_addr;
618 	return rt;
619 }
620 
621 static struct rt *
add_destination_route(struct rt * rt,const struct interface * iface)622 add_destination_route(struct rt *rt, const struct interface *iface)
623 {
624 	struct rt *r;
625 
626 	if (!(iface->flags & IFF_POINTOPOINT) ||
627 	    !has_option_mask(iface->state->options->dstmask, DHO_ROUTER))
628 		return rt;
629 	r = xmalloc(sizeof(*r));
630 	r->dest.s_addr = INADDR_ANY;
631 	r->net.s_addr = INADDR_ANY;
632 	r->gate.s_addr = iface->dst.s_addr;
633 	r->next = rt;
634 	return r;
635 }
636 
637 /* We should check to ensure the routers are on the same subnet
638  * OR supply a host route. If not, warn and add a host route. */
639 static struct rt *
add_router_host_route(struct rt * rt,const struct interface * ifp)640 add_router_host_route(struct rt *rt, const struct interface *ifp)
641 {
642 	struct rt *rtp, *rtl, *rtn;
643 	const char *cp, *cp2, *cp3, *cplim;
644 
645 	for (rtp = rt, rtl = NULL; rtp; rtl = rtp, rtp = rtp->next) {
646 		if (rtp->dest.s_addr != INADDR_ANY)
647 			continue;
648 		/* Scan for a route to match */
649 		for (rtn = rt; rtn != rtp; rtn = rtn->next) {
650 			/* match host */
651 			if (rtn->dest.s_addr == rtp->gate.s_addr)
652 				break;
653 			/* match subnet */
654 			cp = (const char *)&rtp->gate.s_addr;
655 			cp2 = (const char *)&rtn->dest.s_addr;
656 			cp3 = (const char *)&rtn->net.s_addr;
657 			cplim = cp3 + sizeof(rtn->net.s_addr);
658 			while (cp3 < cplim) {
659 				if ((*cp++ ^ *cp2++) & *cp3++)
660 					break;
661 			}
662 			if (cp3 == cplim)
663 				break;
664 		}
665 		if (rtn != rtp)
666 			continue;
667 		if (ifp->flags & IFF_NOARP) {
668 			syslog(LOG_WARNING,
669 			    "%s: forcing router %s through interface",
670 			    ifp->name, inet_ntoa(rtp->gate));
671 			rtp->gate.s_addr = 0;
672 			continue;
673 		}
674 		syslog(LOG_WARNING, "%s: router %s requires a host route",
675 		    ifp->name, inet_ntoa(rtp->gate));
676 		rtn = xmalloc(sizeof(*rtn));
677 		rtn->dest.s_addr = rtp->gate.s_addr;
678 		rtn->net.s_addr = INADDR_BROADCAST;
679 		rtn->gate.s_addr = rtp->gate.s_addr;
680 		rtn->next = rtp;
681 		if (rtl == NULL)
682 			rt = rtn;
683 		else
684 			rtl->next = rtn;
685 	}
686 	return rt;
687 }
688 
689 void
build_routes(void)690 build_routes(void)
691 {
692 	struct rt *nrs = NULL, *dnr, *or, *rt, *rtn, *rtl, *lrt = NULL;
693 	const struct interface *ifp;
694 
695 	if (avoid_routes) return;
696 
697 	for (ifp = ifaces; ifp; ifp = ifp->next) {
698 		if (ifp->state->new == NULL)
699 			continue;
700 		dnr = get_routes(ifp);
701 		dnr = massage_host_routes(dnr, ifp);
702 		dnr = add_subnet_route(dnr, ifp);
703 		dnr = add_router_host_route(dnr, ifp);
704 		dnr = add_destination_route(dnr, ifp);
705 		for (rt = dnr; rt && (rtn = rt->next, 1); lrt = rt, rt = rtn) {
706 			rt->iface = ifp;
707 			rt->metric = ifp->metric;
708 			/* Is this route already in our table? */
709 			if ((find_route(nrs, rt, NULL, NULL)) != NULL)
710 				continue;
711 			rt->src.s_addr = ifp->addr.s_addr;
712 			/* Do we already manage it? */
713 			if ((or = find_route(routes, rt, &rtl, NULL))) {
714 				if (or->iface != ifp ||
715 				    or->src.s_addr != ifp->addr.s_addr ||
716 				    rt->gate.s_addr != or->gate.s_addr ||
717 				    rt->metric != or->metric)
718 				{
719 					if (c_route(or, rt) != 0)
720 						continue;
721 				}
722 				if (rtl != NULL)
723 					rtl->next = or->next;
724 				else
725 					routes = or->next;
726 				free(or);
727 			} else {
728 				if (n_route(rt) != 0)
729 					continue;
730 			}
731 			if (dnr == rt)
732 				dnr = rtn;
733 			else if (lrt)
734 				lrt->next = rtn;
735 			rt->next = nrs;
736 			nrs = rt;
737 			rt = lrt; /* When we loop this makes lrt correct */
738 		}
739 		free_routes(dnr);
740 	}
741 
742 	/* Remove old routes we used to manage */
743 	for (rt = routes; rt; rt = rt->next) {
744 		if (find_route(nrs, rt, NULL, NULL) == NULL)
745 			d_route(rt);
746 	}
747 
748 	free_routes(routes);
749 	routes = nrs;
750 }
751 
752 static int
delete_address(struct interface * iface)753 delete_address(struct interface *iface)
754 {
755 	int retval;
756 	struct if_options *ifo;
757 
758 	ifo = iface->state->options;
759 	if (ifo->options & DHCPCD_INFORM ||
760 	    (ifo->options & DHCPCD_STATIC && ifo->req_addr.s_addr == 0))
761 		return 0;
762 	syslog(LOG_DEBUG, "%s: deleting IP address %s/%d",
763 	    iface->name,
764 	    inet_ntoa(iface->addr),
765 	    inet_ntocidr(iface->net));
766 	retval = del_address(iface, &iface->addr, &iface->net);
767 	if (retval == -1 && errno != EADDRNOTAVAIL)
768 		syslog(LOG_ERR, "del_address: %m");
769 	iface->addr.s_addr = 0;
770 	iface->net.s_addr = 0;
771 	return retval;
772 }
773 
774 int
configure(struct interface * iface)775 configure(struct interface *iface)
776 {
777 	struct dhcp_message *dhcp = iface->state->new;
778 	struct dhcp_lease *lease = &iface->state->lease;
779 	struct if_options *ifo = iface->state->options;
780 	struct rt *rt;
781 
782 	/* As we are now adjusting an interface, we need to ensure
783 	 * we have them in the right order for routing and configuration. */
784 	sort_interfaces();
785 
786 	if (dhcp == NULL) {
787 		if (!(ifo->options & DHCPCD_PERSISTENT)) {
788 			build_routes();
789 			if (iface->addr.s_addr != 0)
790 				delete_address(iface);
791 			run_script(iface);
792 		}
793 		return 0;
794 	}
795 
796 	/* This also changes netmask */
797 	if (!(ifo->options & DHCPCD_INFORM) ||
798 	    !has_address(iface->name, &lease->addr, &lease->net))
799 	{
800 		syslog(LOG_DEBUG, "%s: adding IP address %s/%d",
801 		    iface->name, inet_ntoa(lease->addr),
802 		    inet_ntocidr(lease->net));
803 		if (add_address(iface,
804 			&lease->addr, &lease->net, &lease->brd) == -1 &&
805 		    errno != EEXIST)
806 		{
807 			syslog(LOG_ERR, "add_address: %m");
808 			return -1;
809 		}
810 	}
811 
812 	/* Now delete the old address if different */
813 	if (iface->addr.s_addr != lease->addr.s_addr &&
814 	    iface->addr.s_addr != 0)
815 		delete_address(iface);
816 
817 	iface->addr.s_addr = lease->addr.s_addr;
818 	iface->net.s_addr = lease->net.s_addr;
819 
820 	if (!avoid_routes) {
821 		/* We need to delete the subnet route to have our metric or
822 		 * prefer the interface. */
823 		rt = get_subnet_route(dhcp);
824 		if (rt != NULL) {
825 			rt->iface = iface;
826 			rt->metric = 0;
827 			if (!find_route(routes, rt, NULL, NULL))
828 				del_route(rt);
829 			free(rt);
830 		}
831 
832 		build_routes();
833 	}
834 
835 	if (!iface->state->lease.frominfo &&
836 	    !(ifo->options & (DHCPCD_INFORM | DHCPCD_STATIC)))
837 		if (write_lease(iface, dhcp) == -1)
838 			syslog(LOG_ERR, "write_lease: %m");
839 	run_script(iface);
840 	return 0;
841 }
842