diff options
Diffstat (limited to 'bind.c')
-rw-r--r-- | bind.c | 234 |
1 files changed, 234 insertions, 0 deletions
@@ -0,0 +1,234 @@ +/* + * 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); + } +} |