/*
* dhcpcd - DHCP client daemon
* Copyright (c) 2006-2010 Roy Marples <roy@marples.name>
* All rights reserved
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/param.h>
#include <sys/time.h>
#include <fcntl.h>
#ifdef BSD
# include <paths.h>
#endif
#include <signal.h>
#include <stdlib.h>
#include <syslog.h>
#include <unistd.h>
#include "arp.h"
#include "bind.h"
#include "common.h"
#include "configure.h"
#include "dhcpcd.h"
#include "eloop.h"
#include "if-options.h"
#include "net.h"
#include "signals.h"
#ifndef _PATH_DEVNULL
# define _PATH_DEVNULL "/dev/null"
#endif
/* We do things after aquiring the lease, so ensure we have enough time for them */
#define DHCP_MIN_LEASE 20
#ifndef THERE_IS_NO_FORK
pid_t
daemonise(void)
{
pid_t pid;
sigset_t full;
sigset_t old;
char buf = '\0';
int sidpipe[2], fd;
if (options & DHCPCD_DAEMONISED || !(options & DHCPCD_DAEMONISE))
return 0;
sigfillset(&full);
sigprocmask(SIG_SETMASK, &full, &old);
/* Setup a signal pipe so parent knows when to exit. */
if (pipe(sidpipe) == -1) {
syslog(LOG_ERR, "pipe: %m");
return -1;
}
syslog(LOG_DEBUG, "forking to background");
switch (pid = fork()) {
case -1:
syslog(LOG_ERR, "fork: %m");
exit(EXIT_FAILURE);
/* NOTREACHED */
case 0:
setsid();
/* Notify parent it's safe to exit as we've detached. */
close(sidpipe[0]);
if (write(sidpipe[1], &buf, 1) == -1)
syslog(LOG_ERR, "failed to notify parent: %m");
close(sidpipe[1]);
if ((fd = open(_PATH_DEVNULL, O_RDWR, 0)) != -1) {
dup2(fd, STDIN_FILENO);
dup2(fd, STDOUT_FILENO);
dup2(fd, STDERR_FILENO);
if (fd > STDERR_FILENO)
close(fd);
}
break;
default:
signal_reset();
/* Wait for child to detach */
close(sidpipe[1]);
if (read(sidpipe[0], &buf, 1) == -1)
syslog(LOG_ERR, "failed to read child: %m");
close(sidpipe[0]);
break;
}
/* Done with the fd now */
if (pid != 0) {
syslog(LOG_INFO, "forked to background, child pid %d",pid);
writepid(pidfd, pid);
close(pidfd);
pidfd = -1;
exit(EXIT_SUCCESS);
}
options |= DHCPCD_DAEMONISED;
sigprocmask(SIG_SETMASK, &old, NULL);
return pid;
}
#endif
void
bind_interface(void *arg)
{
struct interface *iface = arg;
struct if_state *state = iface->state;
struct if_options *ifo = state->options;
struct dhcp_lease *lease = &state->lease;
struct timeval tv;
/* We're binding an address now - ensure that sockets are closed */
close_sockets(iface);
state->reason = NULL;
delete_timeout(handle_exit_timeout, NULL);
if (clock_monotonic)
get_monotonic(&lease->boundtime);
state->xid = 0;
free(state->old);
state->old = state->new;
state->new = state->offer;
state->offer = NULL;
get_lease(lease, state->new);
if (ifo->options & DHCPCD_STATIC) {
syslog(LOG_INFO, "%s: using static address %s",
iface->name, inet_ntoa(lease->addr));
lease->leasetime = ~0U;
lease->net.s_addr = ifo->req_mask.s_addr;
state->reason = "STATIC";
} else if (state->new->cookie != htonl(MAGIC_COOKIE)) {
syslog(LOG_INFO, "%s: using IPv4LL address %s",
iface->name, inet_ntoa(lease->addr));
lease->leasetime = ~0U;
state->reason = "IPV4LL";
} else if (ifo->options & DHCPCD_INFORM) {
if (ifo->req_addr.s_addr != 0)
lease->addr.s_addr = ifo->req_addr.s_addr;
else
lease->addr.s_addr = iface->addr.s_addr;
syslog(LOG_INFO, "%s: received approval for %s", iface->name,
inet_ntoa(lease->addr));
lease->leasetime = ~0U;
state->reason = "INFORM";
} else {
if (gettimeofday(&tv, NULL) == 0)
lease->leasedfrom = tv.tv_sec;
else if (lease->frominfo)
state->reason = "TIMEOUT";
if (lease->leasetime == ~0U) {
lease->renewaltime =
lease->rebindtime =
lease->leasetime;
syslog(LOG_INFO, "%s: leased %s for infinity",
iface->name, inet_ntoa(lease->addr));
} else {
if (lease->leasetime < DHCP_MIN_LEASE) {
syslog(LOG_WARNING,
"%s: minimum lease is %d seconds",
iface->name, DHCP_MIN_LEASE);
lease->leasetime = DHCP_MIN_LEASE;
}
if (lease->rebindtime == 0)
lease->rebindtime = lease->leasetime * T2;
else if (lease->rebindtime >= lease->leasetime) {
lease->rebindtime = lease->leasetime * T2;
syslog(LOG_ERR,
"%s: rebind time greater than lease "
"time, forcing to %u seconds",
iface->name, lease->rebindtime);
}
if (lease->renewaltime == 0)
lease->renewaltime = lease->leasetime * T1;
else if (lease->renewaltime > lease->rebindtime) {
lease->renewaltime = lease->leasetime * T1;
syslog(LOG_ERR,
"%s: renewal time greater than rebind "
"time, forcing to %u seconds",
iface->name, lease->renewaltime);
}
syslog(LOG_INFO,
"%s: leased %s for %u seconds", iface->name,
inet_ntoa(lease->addr), lease->leasetime);
}
}
if (options & DHCPCD_TEST) {
state->reason = "TEST";
run_script(iface);
exit(EXIT_SUCCESS);
}
if (state->reason == NULL) {
if (state->old) {
if (state->old->yiaddr == state->new->yiaddr &&
lease->server.s_addr)
state->reason = "RENEW";
else
state->reason = "REBIND";
} else if (state->state == DHS_REBOOT)
state->reason = "REBOOT";
else
state->reason = "BOUND";
}
if (lease->leasetime == ~0U)
lease->renewaltime = lease->rebindtime = lease->leasetime;
else {
add_timeout_sec(lease->renewaltime, start_renew, iface);
add_timeout_sec(lease->rebindtime, start_rebind, iface);
add_timeout_sec(lease->leasetime, start_expire, iface);
}
ifo->options &= ~ DHCPCD_CSR_WARNED;
configure(iface);
daemonise();
state->state = DHS_BOUND;
if (ifo->options & DHCPCD_ARP) {
state->claims = 0;
send_arp_announce(iface);
}
}