1/*
2 * dhcpcd - DHCP client daemon
3 * Copyright (c) 2006-2010 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/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
58pid_t
59daemonise(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
120void
121bind_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