Home | History | Annotate | Download | only in dhcpcd
      1 /*
      2  * dhcpcd - DHCP client daemon
      3  * Copyright 2006-2008 Roy Marples <roy (at) 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/wait.h>
     30 
     31 #include <netinet/in.h>
     32 #include <arpa/inet.h>
     33 
     34 #include <ctype.h>
     35 #include <errno.h>
     36 #include <signal.h>
     37 #include <stdlib.h>
     38 #include <unistd.h>
     39 
     40 #include "config.h"
     41 #include "common.h"
     42 #include "configure.h"
     43 #include "dhcp.h"
     44 #include "dhcpcd.h"
     45 #include "logger.h"
     46 #include "net.h"
     47 #include "signals.h"
     48 
     49 #define DEFAULT_PATH	"PATH=/usr/bin:/usr/sbin:/bin:/sbin"
     50 
     51 
     52 static int
     53 exec_script(char *const *argv, char *const *env)
     54 {
     55 	pid_t pid;
     56 	sigset_t full;
     57 	sigset_t old;
     58 
     59 	/* OK, we need to block signals */
     60 	sigfillset(&full);
     61 	sigprocmask(SIG_SETMASK, &full, &old);
     62 	signal_reset();
     63 
     64 	switch (pid = vfork()) {
     65 	case -1:
     66 		logger(LOG_ERR, "vfork: %s", strerror(errno));
     67 		break;
     68 	case 0:
     69 		sigprocmask(SIG_SETMASK, &old, NULL);
     70 		execve(argv[0], argv, env);
     71 		logger(LOG_ERR, "%s: %s", argv[0], strerror(errno));
     72 		_exit(127);
     73 		/* NOTREACHED */
     74 	}
     75 
     76 	/* Restore our signals */
     77 	signal_setup();
     78 	sigprocmask(SIG_SETMASK, &old, NULL);
     79 	return pid;
     80 }
     81 
     82 int
     83 run_script(const struct options *options, const char *iface,
     84            const char *reason,
     85            const struct dhcp_message *dhcpn, const struct dhcp_message *dhcpo)
     86 {
     87 	char *const argv[2] = { UNCONST(options->script), NULL };
     88 	char **env = NULL, **ep;
     89 	char *path;
     90 	ssize_t e, elen;
     91 	pid_t pid;
     92 	int status = 0;
     93 
     94 	logger(LOG_DEBUG, "executing `%s', reason %s", options->script, reason);
     95 
     96 	/* Make our env */
     97 	elen = 5;
     98 	env = xmalloc(sizeof(char *) * (elen + 1));
     99 	path = getenv("PATH");
    100 	if (path) {
    101 		e = strlen("PATH") + strlen(path) + 2;
    102 		env[0] = xmalloc(e);
    103 		snprintf(env[0], e, "PATH=%s", path);
    104 	} else
    105 		env[0] = xstrdup(DEFAULT_PATH);
    106 	e = strlen("interface") + strlen(iface) + 2;
    107 	env[1] = xmalloc(e);
    108 	snprintf(env[1], e, "interface=%s", iface);
    109 	e = strlen("reason") + strlen(reason) + 2;
    110 	env[2] = xmalloc(e);
    111 	snprintf(env[2], e, "reason=%s", reason);
    112 	e = 20;
    113 	env[3] = xmalloc(e);
    114 	snprintf(env[3], e, "pid=%d", getpid());
    115 	env[4] = xmalloc(e);
    116 	snprintf(env[4], e, "metric=%d", options->metric);
    117 	if (dhcpo) {
    118 		e = configure_env(NULL, NULL, dhcpo, options);
    119 		if (e > 0) {
    120 			env = xrealloc(env, sizeof(char *) * (elen + e + 1));
    121 			elen += configure_env(env + elen, "old", dhcpo, options);
    122 		}
    123 	}
    124 	if (dhcpn) {
    125 		e = configure_env(NULL, NULL, dhcpn, options);
    126 		if (e > 0) {
    127 			env = xrealloc(env, sizeof(char *) * (elen + e + 1));
    128 			elen += configure_env(env + elen, "new", dhcpn, options);
    129 		}
    130 	}
    131 	/* Add our base environment */
    132 	if (options->environ) {
    133 		e = 0;
    134 		while (options->environ[e++])
    135 			;
    136 		env = xrealloc(env, sizeof(char *) * (elen + e + 1));
    137 		e = 0;
    138 		while (options->environ[e]) {
    139 			env[elen + e] = xstrdup(options->environ[e]);
    140 			e++;
    141 		}
    142 		elen += e;
    143 	}
    144 	env[elen] = '\0';
    145 
    146 	pid = exec_script(argv, env);
    147 	if (pid == -1)
    148 		status = -1;
    149 	else if (pid != 0) {
    150 		/* Wait for the script to finish */
    151 		while (waitpid(pid, &status, 0) == -1) {
    152 			if (errno != EINTR) {
    153 				logger(LOG_ERR, "waitpid: %s", strerror(errno));
    154 				status = -1;
    155 				break;
    156 			}
    157 		}
    158 	}
    159 
    160 	/* Cleanup */
    161 	ep = env;
    162 	while (*ep)
    163 		free(*ep++);
    164 	free(env);
    165 	return status;
    166 }
    167 
    168 static struct rt *
    169 reverse_routes(struct rt *routes)
    170 {
    171 	struct rt *rt;
    172 	struct rt *rtn = NULL;
    173 
    174 	while (routes) {
    175 		rt = routes->next;
    176 		routes->next = rtn;
    177 		rtn = routes;
    178 		routes = rt;
    179 	}
    180 	return rtn;
    181 }
    182 
    183 static int
    184 delete_route(const struct interface *iface, struct rt *rt, int metric)
    185 {
    186 	char *addr;
    187 	int retval;
    188 
    189 	addr = xstrdup(inet_ntoa(rt->dest));
    190 	logger(LOG_DEBUG, "deleting route %s/%d via %s",
    191 	       addr, inet_ntocidr(rt->net), inet_ntoa(rt->gate));
    192 	free(addr);
    193 	retval = del_route(iface, &rt->dest, &rt->net, &rt->gate, metric);
    194 	if (retval != 0 && errno != ENOENT && errno != ESRCH)
    195 		logger(LOG_ERR," del_route: %s", strerror(errno));
    196 	return retval;
    197 }
    198 
    199 static int
    200 delete_routes(struct interface *iface, int metric)
    201 {
    202 	struct rt *rt;
    203 	struct rt *rtn;
    204 	int retval = 0;
    205 
    206 	rt = reverse_routes(iface->routes);
    207 	while (rt) {
    208 		rtn = rt->next;
    209 		retval += delete_route(iface, rt, metric);
    210 		free(rt);
    211 		rt = rtn;
    212 	}
    213 	iface->routes = NULL;
    214 
    215 	return retval;
    216 }
    217 
    218 static int
    219 in_routes(const struct rt *routes, const struct rt *rt)
    220 {
    221 	while (routes) {
    222 		if (routes->dest.s_addr == rt->dest.s_addr &&
    223 				routes->net.s_addr == rt->net.s_addr &&
    224 				routes->gate.s_addr == rt->gate.s_addr)
    225 			return 0;
    226 		routes = routes->next;
    227 	}
    228 	return -1;
    229 }
    230 
    231 static int
    232 configure_routes(struct interface *iface, const struct dhcp_message *dhcp,
    233 		 const struct options *options)
    234 {
    235 	struct rt *rt, *ort;
    236 	struct rt *rtn = NULL, *nr = NULL;
    237 	int remember;
    238 	int retval = 0;
    239 	char *addr;
    240 
    241 	ort = get_option_routes(dhcp);
    242 
    243 #ifdef IPV4LL_ALWAYSROUTE
    244 	if (options->options & DHCPCD_IPV4LL &&
    245 	    IN_PRIVATE(ntohl(dhcp->yiaddr)))
    246 	{
    247 		for (rt = ort; rt; rt = rt->next) {
    248 			/* Check if we have already got a link locale route
    249 			 * dished out by the DHCP server */
    250 			if (rt->dest.s_addr == htonl(LINKLOCAL_ADDR) &&
    251 			    rt->net.s_addr == htonl(LINKLOCAL_MASK))
    252 				break;
    253 			rtn = rt;
    254 		}
    255 
    256 		if (!rt) {
    257 			rt = xmalloc(sizeof(*rt));
    258 			rt->dest.s_addr = htonl(LINKLOCAL_ADDR);
    259 			rt->net.s_addr = htonl(LINKLOCAL_MASK);
    260 			rt->gate.s_addr = 0;
    261 			rt->next = NULL;
    262 			if (rtn)
    263 				rtn->next = rt;
    264 			else
    265 				ort = rt;
    266 		}
    267 	}
    268 #endif
    269 
    270 	/* Now remove old routes we no longer use.
    271  	 * We should do this in reverse order. */
    272 	iface->routes = reverse_routes(iface->routes);
    273 	for (rt = iface->routes; rt; rt = rt->next)
    274 		if (in_routes(ort, rt) != 0)
    275 			delete_route(iface, rt, options->metric);
    276 
    277 	for (rt = ort; rt; rt = rt->next) {
    278 		/* Don't set default routes if not asked to */
    279 		if (rt->dest.s_addr == 0 &&
    280 		    rt->net.s_addr == 0 &&
    281 		    !(options->options & DHCPCD_GATEWAY))
    282 			continue;
    283 
    284 		addr = xstrdup(inet_ntoa(rt->dest));
    285 		logger(LOG_DEBUG, "adding route to %s/%d via %s",
    286 			addr, inet_ntocidr(rt->net), inet_ntoa(rt->gate));
    287 		free(addr);
    288 		remember = add_route(iface, &rt->dest,
    289 				     &rt->net, &rt->gate,
    290 				     options->metric);
    291 		retval += remember;
    292 
    293 		/* If we failed to add the route, we may have already added it
    294 		   ourselves. If so, remember it again. */
    295 		if (remember < 0) {
    296 			if (errno != EEXIST)
    297 				logger(LOG_ERR, "add_route: %s",
    298 				       strerror(errno));
    299 			if (in_routes(iface->routes, rt) == 0)
    300 				remember = 1;
    301 		}
    302 
    303 		/* This login is split from above due to the #ifdef below */
    304 		if (remember >= 0) {
    305 			if (nr) {
    306 				rtn->next = xmalloc(sizeof(*rtn));
    307 				rtn = rtn->next;
    308 			} else {
    309 				nr = rtn = xmalloc(sizeof(*rtn));
    310 			}
    311 			rtn->dest.s_addr = rt->dest.s_addr;
    312 			rtn->net.s_addr = rt->net.s_addr;
    313 			rtn->gate.s_addr = rt->gate.s_addr;
    314 			rtn->next = NULL;
    315 		}
    316 	}
    317 	free_routes(ort);
    318 	free_routes(iface->routes);
    319 	iface->routes = nr;
    320 	return retval;
    321 }
    322 
    323 static int
    324 delete_address(struct interface *iface)
    325 {
    326 	int retval;
    327 	logger(LOG_DEBUG, "deleting IP address %s/%d",
    328 	       inet_ntoa(iface->addr),
    329 	       inet_ntocidr(iface->net));
    330 	retval = del_address(iface->name, &iface->addr, &iface->net);
    331 	if (retval == -1 && errno != EADDRNOTAVAIL)
    332 		logger(LOG_ERR, "del_address: %s", strerror(errno));
    333 	iface->addr.s_addr = 0;
    334 	iface->net.s_addr = 0;
    335 	return retval;
    336 }
    337 
    338 int
    339 configure(struct interface *iface, const char *reason,
    340 	  const struct dhcp_message *dhcp, const struct dhcp_message *old,
    341 	  const struct dhcp_lease *lease, const struct options *options,
    342 	  int up)
    343 {
    344 	struct in_addr addr;
    345 	struct in_addr net;
    346 	struct in_addr brd;
    347 #ifdef __linux__
    348 	struct in_addr dest;
    349 	struct in_addr gate;
    350 #endif
    351 
    352 	/* Grab our IP config */
    353 	if (dhcp == NULL)
    354 		up = 0;
    355 	else {
    356 		addr.s_addr = dhcp->yiaddr;
    357 		if (addr.s_addr == 0)
    358 			addr.s_addr = lease->addr.s_addr;
    359 		/* Ensure we have all the needed values */
    360 		if (get_option_addr(&net, dhcp, DHO_SUBNETMASK) == -1)
    361 			net.s_addr = get_netmask(addr.s_addr);
    362 		if (get_option_addr(&brd, dhcp, DHO_BROADCAST) == -1)
    363 			brd.s_addr = addr.s_addr | ~net.s_addr;
    364 	}
    365 
    366 	/* If we aren't up, then reset the interface as much as we can */
    367 	if (!up) {
    368 		/* Only reset things if we had set them before */
    369 		if (iface->addr.s_addr != 0) {
    370 			delete_routes(iface, options->metric);
    371 			delete_address(iface);
    372 		}
    373 
    374 		run_script(options, iface->name, reason, NULL, old);
    375 		return 0;
    376 	}
    377 
    378 	/* This also changes netmask */
    379 	if (!(options->options & DHCPCD_INFORM) ||
    380 	    !has_address(iface->name, &addr, &net)) {
    381 		logger(LOG_DEBUG, "adding IP address %s/%d",
    382 		       inet_ntoa(addr), inet_ntocidr(net));
    383 		if (add_address(iface->name, &addr, &net, &brd) == -1 &&
    384 		    errno != EEXIST)
    385 		{
    386 			logger(LOG_ERR, "add_address: %s", strerror(errno));
    387 			return -1;
    388 		}
    389 	}
    390 
    391 	/* Now delete the old address if different */
    392 	if (iface->addr.s_addr != addr.s_addr &&
    393 	    iface->addr.s_addr != 0)
    394 		delete_address(iface);
    395 
    396 #ifdef __linux__
    397 	/* On linux, we need to change the subnet route to have our metric. */
    398 	if (iface->addr.s_addr != lease->addr.s_addr &&
    399 	    options->metric > 0 && net.s_addr != INADDR_BROADCAST)
    400 	{
    401 		dest.s_addr = addr.s_addr & net.s_addr;
    402 		gate.s_addr = 0;
    403 		add_route(iface, &dest, &net, &gate, options->metric);
    404 		del_route(iface, &dest, &net, &gate, 0);
    405 	}
    406 #endif
    407 
    408 	iface->addr.s_addr = addr.s_addr;
    409 	iface->net.s_addr = net.s_addr;
    410 	configure_routes(iface, dhcp, options);
    411 	if (!lease->frominfo)
    412 		if (write_lease(iface, dhcp) == -1)
    413 			logger(LOG_ERR, "write_lease: %s", strerror(errno));
    414 	run_script(options, iface->name, reason, dhcp, old);
    415 	return 0;
    416 }
    417