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