Home | History | Annotate | Download | only in dhcpcd
      1 /*
      2  * dhcpcd - DHCP client daemon
      3  * Copyright (c) 2006-2010 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/param.h>
     29 #include <sys/time.h>
     30 
     31 #include <fcntl.h>
     32 #ifdef BSD
     33 #  include <paths.h>
     34 #endif
     35 #include <signal.h>
     36 #include <stdlib.h>
     37 #include <syslog.h>
     38 #include <unistd.h>
     39 
     40 #include "arp.h"
     41 #include "bind.h"
     42 #include "common.h"
     43 #include "configure.h"
     44 #include "dhcpcd.h"
     45 #include "eloop.h"
     46 #include "if-options.h"
     47 #include "net.h"
     48 #include "signals.h"
     49 
     50 #ifndef _PATH_DEVNULL
     51 #  define _PATH_DEVNULL "/dev/null"
     52 #endif
     53 
     54 /* We do things after aquiring the lease, so ensure we have enough time for them */
     55 #define DHCP_MIN_LEASE 20
     56 
     57 #ifndef THERE_IS_NO_FORK
     58 pid_t
     59 daemonise(void)
     60 {
     61 	pid_t pid;
     62 	sigset_t full;
     63 	sigset_t old;
     64 	char buf = '\0';
     65 	int sidpipe[2], fd;
     66 
     67 	if (options & DHCPCD_DAEMONISED || !(options & DHCPCD_DAEMONISE))
     68 		return 0;
     69 	sigfillset(&full);
     70 	sigprocmask(SIG_SETMASK, &full, &old);
     71 	/* Setup a signal pipe so parent knows when to exit. */
     72 	if (pipe(sidpipe) == -1) {
     73 		syslog(LOG_ERR, "pipe: %m");
     74 		return -1;
     75 	}
     76 	syslog(LOG_DEBUG, "forking to background");
     77 	switch (pid = fork()) {
     78 	case -1:
     79 		syslog(LOG_ERR, "fork: %m");
     80 		exit(EXIT_FAILURE);
     81 		/* NOTREACHED */
     82 	case 0:
     83 		setsid();
     84 		/* Notify parent it's safe to exit as we've detached. */
     85 		close(sidpipe[0]);
     86 		if (write(sidpipe[1], &buf, 1) == -1)
     87 			syslog(LOG_ERR, "failed to notify parent: %m");
     88 		close(sidpipe[1]);
     89 		if ((fd = open(_PATH_DEVNULL, O_RDWR, 0)) != -1) {
     90 			dup2(fd, STDIN_FILENO);
     91 			dup2(fd, STDOUT_FILENO);
     92 			dup2(fd, STDERR_FILENO);
     93 			if (fd > STDERR_FILENO)
     94 				close(fd);
     95 		}
     96 		break;
     97 	default:
     98 		signal_reset();
     99 		/* Wait for child to detach */
    100 		close(sidpipe[1]);
    101 		if (read(sidpipe[0], &buf, 1) == -1)
    102 			syslog(LOG_ERR, "failed to read child: %m");
    103 		close(sidpipe[0]);
    104 		break;
    105 	}
    106 	/* Done with the fd now */
    107 	if (pid != 0) {
    108 		syslog(LOG_INFO, "forked to background, child pid %d",pid);
    109 		writepid(pidfd, pid);
    110 		close(pidfd);
    111 		pidfd = -1;
    112 		exit(EXIT_SUCCESS);
    113 	}
    114 	options |= DHCPCD_DAEMONISED;
    115 	sigprocmask(SIG_SETMASK, &old, NULL);
    116 	return pid;
    117 }
    118 #endif
    119 
    120 void
    121 bind_interface(void *arg)
    122 {
    123 	struct interface *iface = arg;
    124 	struct if_state *state = iface->state;
    125 	struct if_options *ifo = state->options;
    126 	struct dhcp_lease *lease = &state->lease;
    127 	struct timeval tv;
    128 
    129 	/* We're binding an address now - ensure that sockets are closed */
    130 	close_sockets(iface);
    131 	state->reason = NULL;
    132 	delete_timeout(handle_exit_timeout, NULL);
    133 	if (clock_monotonic)
    134 		get_monotonic(&lease->boundtime);
    135 	state->xid = 0;
    136 	free(state->old);
    137 	state->old = state->new;
    138 	state->new = state->offer;
    139 	state->offer = NULL;
    140 	get_lease(lease, state->new);
    141 	if (ifo->options & DHCPCD_STATIC) {
    142 		syslog(LOG_INFO, "%s: using static address %s",
    143 		    iface->name, inet_ntoa(lease->addr));
    144 		lease->leasetime = ~0U;
    145 		lease->net.s_addr = ifo->req_mask.s_addr;
    146 		state->reason = "STATIC";
    147 	} else if (state->new->cookie != htonl(MAGIC_COOKIE)) {
    148 		syslog(LOG_INFO, "%s: using IPv4LL address %s",
    149 		    iface->name, inet_ntoa(lease->addr));
    150 		lease->leasetime = ~0U;
    151 		state->reason = "IPV4LL";
    152 	} else if (ifo->options & DHCPCD_INFORM) {
    153 		if (ifo->req_addr.s_addr != 0)
    154 			lease->addr.s_addr = ifo->req_addr.s_addr;
    155 		else
    156 			lease->addr.s_addr = iface->addr.s_addr;
    157 		syslog(LOG_INFO, "%s: received approval for %s", iface->name,
    158 		    inet_ntoa(lease->addr));
    159 		lease->leasetime = ~0U;
    160 		state->reason = "INFORM";
    161 	} else {
    162 		if (gettimeofday(&tv, NULL) == 0)
    163 			lease->leasedfrom = tv.tv_sec;
    164 		else if (lease->frominfo)
    165 			state->reason = "TIMEOUT";
    166 		if (lease->leasetime == ~0U) {
    167 			lease->renewaltime =
    168 			    lease->rebindtime =
    169 			    lease->leasetime;
    170 			syslog(LOG_INFO, "%s: leased %s for infinity",
    171 			    iface->name, inet_ntoa(lease->addr));
    172 		} else {
    173 			if (lease->leasetime < DHCP_MIN_LEASE) {
    174 				syslog(LOG_WARNING,
    175 				    "%s: minimum lease is %d seconds",
    176 				    iface->name, DHCP_MIN_LEASE);
    177 				lease->leasetime = DHCP_MIN_LEASE;
    178 			}
    179 			if (lease->rebindtime == 0)
    180 				lease->rebindtime = lease->leasetime * T2;
    181 			else if (lease->rebindtime >= lease->leasetime) {
    182 				lease->rebindtime = lease->leasetime * T2;
    183 				syslog(LOG_ERR,
    184 				    "%s: rebind time greater than lease "
    185 				    "time, forcing to %u seconds",
    186 				    iface->name, lease->rebindtime);
    187 			}
    188 			if (lease->renewaltime == 0)
    189 				lease->renewaltime = lease->leasetime * T1;
    190 			else if (lease->renewaltime > lease->rebindtime) {
    191 				lease->renewaltime = lease->leasetime * T1;
    192 				syslog(LOG_ERR,
    193 				    "%s: renewal time greater than rebind "
    194 				    "time, forcing to %u seconds",
    195 				    iface->name, lease->renewaltime);
    196 			}
    197 			syslog(LOG_INFO,
    198 			    "%s: leased %s for %u seconds", iface->name,
    199 			    inet_ntoa(lease->addr), lease->leasetime);
    200 		}
    201 	}
    202 	if (options & DHCPCD_TEST) {
    203 		state->reason = "TEST";
    204 		run_script(iface);
    205 		exit(EXIT_SUCCESS);
    206 	}
    207 	if (state->reason == NULL) {
    208 		if (state->old) {
    209 			if (state->old->yiaddr == state->new->yiaddr &&
    210 			    lease->server.s_addr)
    211 				state->reason = "RENEW";
    212 			else
    213 				state->reason = "REBIND";
    214 		} else if (state->state == DHS_REBOOT)
    215 			state->reason = "REBOOT";
    216 		else
    217 			state->reason = "BOUND";
    218 	}
    219 	if (lease->leasetime == ~0U)
    220 		lease->renewaltime = lease->rebindtime = lease->leasetime;
    221 	else {
    222 		add_timeout_sec(lease->renewaltime, start_renew, iface);
    223 		add_timeout_sec(lease->rebindtime, start_rebind, iface);
    224 		add_timeout_sec(lease->leasetime, start_expire, iface);
    225 	}
    226 	ifo->options &= ~ DHCPCD_CSR_WARNED;
    227 	configure(iface);
    228 	daemonise();
    229 	state->state = DHS_BOUND;
    230 	if (ifo->options & DHCPCD_ARP) {
    231 		state->claims = 0;
    232 		send_arp_announce(iface);
    233 	}
    234 }
    235