/* * 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); } }