mirror of
https://github.com/rsmarples/dhcpcd.git
synced 2024-11-24 10:35:03 +08:00
232 lines
6.8 KiB
C
232 lines
6.8 KiB
C
/*
|
|
* dhcpcd - DHCP client daemon
|
|
* Copyright (c) 2006-2012 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;
|
|
char buf = '\0';
|
|
int sidpipe[2], fd;
|
|
|
|
eloop_timeout_delete(handle_exit_timeout, NULL);
|
|
if (options & DHCPCD_DAEMONISED || !(options & DHCPCD_DAEMONISE))
|
|
return 0;
|
|
/* 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);
|
|
close(fd);
|
|
}
|
|
break;
|
|
default:
|
|
/* 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;
|
|
options |= DHCPCD_FORKED;
|
|
exit(EXIT_SUCCESS);
|
|
}
|
|
options |= DHCPCD_DAEMONISED;
|
|
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;
|
|
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 {
|
|
eloop_timeout_add_sec(lease->renewaltime, start_renew, iface);
|
|
eloop_timeout_add_sec(lease->rebindtime, start_rebind, iface);
|
|
eloop_timeout_add_sec(lease->leasetime, start_expire, iface);
|
|
syslog(LOG_DEBUG,
|
|
"%s: renew in %u seconds, rebind in %u seconds",
|
|
iface->name, lease->renewaltime, lease->rebindtime);
|
|
}
|
|
ifo->options &= ~ DHCPCD_CSR_WARNED;
|
|
configure(iface);
|
|
daemonise();
|
|
state->state = DHS_BOUND;
|
|
if (ifo->options & DHCPCD_ARP) {
|
|
state->claims = 0;
|
|
send_arp_announce(iface);
|
|
}
|
|
}
|