summaryrefslogtreecommitdiffstats
path: root/workspace/networkDiscovery/dhcpcd/client.c
diff options
context:
space:
mode:
Diffstat (limited to 'workspace/networkDiscovery/dhcpcd/client.c')
-rw-r--r--workspace/networkDiscovery/dhcpcd/client.c1136
1 files changed, 0 insertions, 1136 deletions
diff --git a/workspace/networkDiscovery/dhcpcd/client.c b/workspace/networkDiscovery/dhcpcd/client.c
deleted file mode 100644
index 8ce2721..0000000
--- a/workspace/networkDiscovery/dhcpcd/client.c
+++ /dev/null
@@ -1,1136 +0,0 @@
-/*
- * dhcpcd - DHCP client daemon
- * Copyright 2006-2008 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/time.h>
-#include <sys/types.h>
-#include <arpa/inet.h>
-#ifdef __linux__
-# include <netinet/ether.h>
-#endif
-#include <ctype.h>
-#include <errno.h>
-#include <poll.h>
-#include <signal.h>
-#include <stdbool.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <time.h>
-#include <unistd.h>
-
-#include "config.h"
-#include "common.h"
-#ifdef ENABLE_ARP
-# include "arp.h"
-#endif
-#include "client.h"
-#include "configure.h"
-#include "dhcp.h"
-#include "dhcpcd.h"
-#include "info.h"
-#include "interface.h"
-#ifdef ENABLE_IPV4LL
-# include "ipv4ll.h"
-#endif
-#include "logger.h"
-#include "signal.h"
-#include "socket.h"
-
-#ifdef ENABLE_DUID
-# include "duid.h"
-#endif
-
-#ifdef ENABLE_INFO
-# include "info.h"
-#endif
-
-#ifdef THERE_IS_NO_FORK
-# ifndef ENABLE_INFO
- # error "Non MMU requires ENABLE_INFO to work"
-# endif
-#endif
-
-/* Some platforms don't define INFTIM */
-#ifndef INFTIM
-# define INFTIM -1
-#endif
-
-/* This is out mini timeout.
- * Basically we resend the last request every TIMEOUT_MINI seconds. */
-#define TIMEOUT_MINI 3
-/* Except for an infinite timeout. We keep adding TIMEOUT_MINI to
- * ourself until TIMEOUT_MINI_INF is reached. */
-#define TIMEOUT_MINI_INF 60
-
-#define STATE_INIT 0
-#define STATE_REQUESTING 1
-#define STATE_BOUND 2
-#define STATE_RENEWING 3
-#define STATE_REBINDING 4
-#define STATE_REBOOT 5
-#define STATE_RENEW_REQUESTED 6
-#define STATE_RELEASED 7
-
-/* We should define a maximum for the NAK exponential backoff */
-#define NAKOFF_MAX 60
-
-#define SOCKET_CLOSED 0
-#define SOCKET_OPEN 1
-
-/* Indexes for pollfds */
-#define POLLFD_SIGNAL 0
-#define POLLFD_IFACE 1
-
-typedef struct _state {
- int *pidfd;
- bool forked;
- int state;
- uint32_t xid;
- dhcp_t *dhcp;
- int socket;
- interface_t *interface;
- time_t start;
- time_t last_sent;
- time_t last_type;
- long timeout;
- time_t nakoff;
- bool daemonised;
- bool persistent;
- unsigned char *buffer;
- size_t buffer_len;
- size_t buffer_pos;
-} state_t;
-
-static pid_t daemonise (int *pidfd)
-{
- pid_t pid;
- sigset_t full;
- sigset_t old;
-#ifdef THERE_IS_NO_FORK
- char **argv;
- int i;
-#endif
-
- sigfillset (&full);
- sigprocmask (SIG_SETMASK, &full, &old);
-
-#ifndef THERE_IS_NO_FORK
- logger (LOG_DEBUG, "forking to background");
- switch (pid = fork()) {
- case -1:
- logger (LOG_ERR, "fork: %s", strerror (errno));
- exit (EXIT_FAILURE);
- /* NOT REACHED */
- case 0:
- setsid ();
- close_fds ();
- break;
- default:
- /* Reset our signals as we're the parent about to exit. */
- signal_reset ();
- break;
- }
-#else
- logger (LOG_INFO, "forking to background");
-
- /* We need to add --daemonise to our options */
- argv = xmalloc (sizeof (char *) * (dhcpcd_argc + 4));
- argv[0] = dhcpcd;
- for (i = 1; i < dhcpcd_argc; i++)
- argv[i] = dhcpcd_argv[i];
- argv[i] = (char *) "--daemonised";
- if (dhcpcd_skiproutes) {
- argv[++i] = (char *) "--skiproutes";
- argv[++i] = dhcpcd_skiproutes;
- }
- argv[i + 1] = NULL;
-
- switch (pid = vfork ()) {
- case -1:
- logger (LOG_ERR, "vfork: %s", strerror (errno));
- _exit (EXIT_FAILURE);
- case 0:
- signal_reset ();
- sigprocmask (SIG_SETMASK, &old, NULL);
- execvp (dhcpcd, argv);
- logger (LOG_ERR, "execl `%s': %s", dhcpcd,
- strerror (errno));
- _exit (EXIT_FAILURE);
- }
-
- free (argv);
-#endif
-
- /* Done with the fd now */
- if (pid != 0) {
- writepid (*pidfd, pid);
- close (*pidfd);
- *pidfd = -1;
-
- }
-
- sigprocmask (SIG_SETMASK, &old, NULL);
- return (pid);
-}
-
-#ifdef ENABLE_INFO
-static bool get_old_lease (state_t *state, const options_t *options)
-{
- interface_t *iface = state->interface;
- dhcp_t *dhcp = state->dhcp;
- struct timeval tv;
- unsigned int offset = 0;
-
- if (! IN_LINKLOCAL (ntohl (iface->previous_address.s_addr)))
- logger (LOG_INFO, "trying to use old lease in `%s'",
- iface->infofile);
- if (! read_info (iface, dhcp))
- return (false);
-
- /* Vitaly important we remove the server information here */
- memset (&dhcp->serveraddress, 0, sizeof (dhcp->serveraddress));
- memset (dhcp->servername, 0, sizeof (dhcp->servername));
-
-#ifdef ENABLE_ARP
- /* Check that no-one is using the address */
- if ((options->dolastlease ||
- (IN_LINKLOCAL (ntohl (dhcp->address.s_addr)) &&
- (! options->doipv4ll ||
- arp_claim (iface, dhcp->address)))))
- {
- memset (&dhcp->address, 0, sizeof (dhcp->address));
- memset (&dhcp->netmask, 0, sizeof (dhcp->netmask));
- memset (&dhcp->broadcast, 0, sizeof (dhcp->broadcast));
- return (false);
- }
-
- /* Ok, lets use this */
- if (IN_LINKLOCAL (dhcp->address.s_addr))
- return (true);
-#endif
-
- /* Ensure that we can still use the lease */
- if (gettimeofday (&tv, NULL) == -1) {
- logger (LOG_ERR, "gettimeofday: %s", strerror (errno));
- return (false);
- }
-
- offset = tv.tv_sec - dhcp->leasedfrom;
- if (dhcp->leasedfrom &&
- tv.tv_sec - dhcp->leasedfrom > dhcp->leasetime)
- {
- logger (LOG_ERR, "lease expired %u seconds ago",
- offset + dhcp->leasetime);
- return (false);
- }
-
- if (dhcp->leasedfrom == 0)
- offset = 0;
- state->timeout = dhcp->renewaltime - offset;
- iface->start_uptime = uptime ();
- return (true);
-}
-#endif
-
-#ifdef THERE_IS_NO_FORK
-static void remove_skiproutes (dhcp_t *dhcp, interface_t *iface)
-{
- int i = -1;
- route_t *route;
- route_t *newroute;
-
- free_route (iface->previous_routes);
- iface->previous_routes = NULL;
-
- NSTAILQ_FOREACH (route, dhcp->routes, entries) {
- i++;
-
- /* Check that we did add this route or not */
- if (dhcpcd_skiproutes) {
- char *sk = xstrdup (dhcpcd_skiproutes);
- char *skp = sk;
- char *token;
- bool found = false;
-
- while ((token = strsep (&skp, ","))) {
- if (isdigit (*token) && atoi (token) == i) {
- found = true;
- break;
- }
- }
- free (sk);
- if (found)
- continue;
- }
-
- if (! iface->previous_routes) {
- iface->previous_routes = xmalloc (sizeof (*iface->previous_routes));
- STAILQ_INIT (iface->previous_routes);
- }
-
- newroute = xmalloc (sizeof (*newroute));
- memcpy (newroute, route, sizeof (*newroute));
- STAILQ_INSERT_TAIL (iface->previous_routes, newroute, entries);
- }
-
- /* We no longer need this argument */
- free (dhcpcd_skiproutes);
- dhcpcd_skiproutes = NULL;
-}
-#endif
-
-static bool client_setup (state_t *state, const options_t *options)
-{
- dhcp_t *dhcp = state->dhcp;
- interface_t *iface = state->interface;
-
- state->state = STATE_INIT;
- state->last_type = DHCP_DISCOVER;
- state->nakoff = 1;
- state->daemonised = options->daemonised;
- state->persistent = options->persistent;
-
- if (options->request_address.s_addr == 0 &&
- (options->doinform || options->dorequest || options->daemonised))
- {
-#ifdef ENABLE_INFO
- if (! get_old_lease (state, options))
-#endif
- {
- free (dhcp);
- return (false);
- }
- state->timeout = 0;
-
- if (! options->daemonised &&
- IN_LINKLOCAL (ntohl (dhcp->address.s_addr)))
- {
- logger (LOG_ERR, "cannot request a link local address");
- return (false);
- }
-#ifdef THERE_IS_NO_FORK
- if (options->daemonised) {
- state->state = STATE_BOUND;
- state->timeout = dhcp->renewaltime;
- iface->previous_address = dhcp->address;
- iface->previous_netmask = dhcp->netmask;
- remove_skiproutes (dhcp, iface);
- }
-#endif
-
- } else {
- dhcp->address = options->request_address;
- dhcp->netmask = options->request_netmask;
- if (dhcp->netmask.s_addr == 0)
- dhcp->netmask.s_addr = get_netmask (dhcp->address.s_addr);
- dhcp->broadcast.s_addr = dhcp->address.s_addr |
- ~dhcp->netmask.s_addr;
- }
-
- /* Remove all existing addresses.
- * After all, we ARE a DHCP client whose job it is to configure the
- * interface. We only do this on start, so persistent addresses
- * can be added afterwards by the user if needed. */
- if (! options->test && ! options->daemonised) {
- if (! options->doinform) {
- flush_addresses (iface->name);
- } else {
- /* The inform address HAS to be configured for it to
- * work with most DHCP servers */
- if (options->doinform &&
- has_address (iface->name, dhcp->address) < 1)
- {
- add_address (iface->name, dhcp->address,
- dhcp->netmask, dhcp->broadcast);
- iface->previous_address = dhcp->address;
- iface->previous_netmask = dhcp->netmask;
- }
- }
- }
-
- if (*options->clientid) {
- /* Attempt to see if the ClientID is a hardware address */
- iface->clientid_len = hwaddr_aton (NULL, options->clientid);
- if (iface->clientid_len) {
- iface->clientid = xmalloc (iface->clientid_len);
- hwaddr_aton (iface->clientid, options->clientid);
- } else {
- /* Nope, so mark it as-is */
- iface->clientid_len = strlen (options->clientid) + 1;
- iface->clientid = xmalloc (iface->clientid_len);
- *iface->clientid = '\0';
- memcpy (iface->clientid + 1,
- options->clientid, iface->clientid_len - 1);
- }
- } else {
-#ifdef ENABLE_DUID
- unsigned char *duid = NULL;
- size_t duid_len = 0;
-
- if (options->doduid) {
- duid = xmalloc (DUID_LEN);
- duid_len = get_duid (duid, iface);
- }
-
- if (duid_len > 0) {
- logger (LOG_INFO, "DUID = %s", hwaddr_ntoa (duid, duid_len));
-
- iface->clientid_len = duid_len + 5;
- iface->clientid = xmalloc (iface->clientid_len);
- *iface->clientid = 255; /* RFC 4361 */
-
- /* IAID is 4 bytes, so if the iface name is 4 bytes use it */
- if (strlen (iface->name) == 4) {
- memcpy (iface->clientid + 1, iface->name, 4);
- } else {
- /* Name isn't 4 bytes, so use the index */
- uint32_t ul = htonl (if_nametoindex (iface->name));
- memcpy (iface->clientid + 1, &ul, 4);
- }
-
- memcpy (iface->clientid + 5, duid, duid_len);
- free (duid);
- } else {
-#else
- {
-#endif
- iface->clientid_len = iface->hwlen + 1;
- iface->clientid = xmalloc (iface->clientid_len);
- *iface->clientid = iface->family;
- memcpy (iface->clientid + 1, iface->hwaddr, iface->hwlen);
- }
- }
-
- return (true);
-}
-
-static bool do_socket (state_t *state, int mode)
-{
- if (state->interface->fd >= 0)
- close (state->interface->fd);
-#ifdef __linux
- if (mode == SOCKET_CLOSED && state->interface->listen_fd >= 0) {
- close (state->interface->listen_fd);
- state->interface->listen_fd = -1;
- }
-#endif
-
- state->interface->fd = -1;
- if (mode == SOCKET_OPEN)
- if (open_socket (state->interface, ETHERTYPE_IP) == -1)
- return (false);
- state->socket = mode;
- return (true);
-}
-
-static bool _send_message (state_t *state, int type, const options_t *options)
-{
- ssize_t retval;
-
- state->last_type = type;
- state->last_sent = uptime ();
- retval = send_message (state->interface, state->dhcp, state->xid,
- type, options);
- return (retval == -1 ? false : true);
-}
-
-static void drop_config (state_t *state, const options_t *options)
-{
- if (! state->persistent)
- configure (options, state->interface, state->dhcp, false);
-
- free_dhcp (state->dhcp);
- memset (state->dhcp, 0, sizeof (*state->dhcp));
-}
-
-static int wait_for_packet (struct pollfd *fds, state_t *state,
- const options_t *options)
-{
- dhcp_t *dhcp = state->dhcp;
- interface_t *iface = state->interface;
- int timeout = 0;
- int retval = 0;
-
- if (! (state->timeout > 0 ||
- (options->timeout == 0 &&
- (state->state != STATE_INIT || state->xid)))) {
- /* We need to zero our signal fd, otherwise we will block
- * trying to read a signal. */
- fds[POLLFD_SIGNAL].revents = 0;
- return (0);
- }
-
- fds[POLLFD_IFACE].fd = iface->fd;
-
- if ((options->timeout == 0 && state->xid) ||
- (dhcp->leasetime == (unsigned) -1 &&
- state->state == STATE_BOUND))
- {
- logger (LOG_DEBUG, "waiting for infinity");
- while (retval == 0) {
- if (iface->fd == -1)
- retval = poll (fds, 1, INFTIM);
- else {
- /* Slow down our requests */
- if (timeout < TIMEOUT_MINI_INF)
- timeout += TIMEOUT_MINI;
- else if (timeout > TIMEOUT_MINI_INF)
- timeout = TIMEOUT_MINI_INF;
-
- retval = poll (fds, 2, timeout * 1000);
- if (retval == -1 && errno == EINTR) {
- /* If interupted, continue as normal as
- * the signal will be delivered down
- * the pipe */
- retval = 0;
- continue;
- }
- if (retval == 0)
- _send_message (state, state->last_type,
- options);
- }
- }
-
- return (retval);
- }
-
- /* Resend our message if we're getting loads of packets.
- * As we use BPF or LPF, we shouldn't hit this as much, but it's
- * still nice to have. */
- if (iface->fd > -1 && uptime () - state->last_sent >= TIMEOUT_MINI)
- _send_message (state, state->last_type, options);
-
- logger (LOG_DEBUG, "waiting for %ld seconds",
- (unsigned long) state->timeout);
- /* If we're waiting for a reply, then we re-send the last
- * DHCP request periodically in-case of a bad line */
- retval = 0;
- while (state->timeout > 0 && retval == 0) {
- if (iface->fd == -1)
- timeout = (int) state->timeout;
- else {
- timeout = TIMEOUT_MINI;
- if (state->timeout < timeout)
- timeout = (int) state->timeout;
- }
- timeout *= 1000;
- state->start = uptime ();
- retval = poll (fds, iface->fd == -1 ? 1 : 2, timeout);
- state->timeout -= uptime () - state->start;
- if (retval == -1 && errno == EINTR) {
- /* If interupted, continue as normal as the signal
- * will be delivered down the pipe */
- retval = 0;
- continue;
- }
- if (retval == 0 && iface->fd != -1 && state->timeout > 0)
- _send_message (state, state->last_type, options);
- }
-
- return (retval);
-}
-
-static bool handle_signal (int sig, state_t *state, const options_t *options)
-{
- switch (sig) {
- case SIGINT:
- logger (LOG_INFO, "received SIGINT, stopping");
- return (false);
- case SIGTERM:
- logger (LOG_INFO, "received SIGTERM, stopping");
- return (false);
-
- case SIGALRM:
- logger (LOG_INFO, "received SIGALRM, renewing lease");
- switch (state->state) {
- case STATE_BOUND:
- case STATE_RENEWING:
- case STATE_REBINDING:
- state->state = STATE_RENEW_REQUESTED;
- break;
- case STATE_RENEW_REQUESTED:
- case STATE_REQUESTING:
- case STATE_RELEASED:
- state->state = STATE_INIT;
- break;
- }
- state->timeout = 0;
- state->xid = 0;
- return (true);
-
- case SIGHUP:
- if (state->state != STATE_BOUND &&
- state->state != STATE_RENEWING &&
- state->state != STATE_REBINDING)
- {
- logger (LOG_ERR,
- "received SIGHUP, but we no have lease to release");
- return (false);
- }
-
- logger (LOG_INFO, "received SIGHUP, releasing lease");
- if (! IN_LINKLOCAL (ntohl (state->dhcp->address.s_addr))) {
- do_socket (state, SOCKET_OPEN);
- state->xid = (uint32_t) random ();
- if ((open_socket (state->interface, false)) >= 0)
- _send_message (state, DHCP_RELEASE, options);
- do_socket (state, SOCKET_CLOSED);
- }
- unlink (state->interface->infofile);
- return (false);
-
- default:
- logger (LOG_ERR,
- "received signal %d, but don't know what to do with it",
- sig);
- }
-
- return (false);
-}
-
-static int handle_timeout (state_t *state, const options_t *options)
-{
- dhcp_t *dhcp = state->dhcp;
- interface_t *iface = state->interface;
-
- /* No NAK, so reset the backoff */
- state->nakoff = 1;
-
- if (state->state == STATE_INIT && state->xid != 0) {
- if (iface->previous_address.s_addr != 0 &&
- ! IN_LINKLOCAL (ntohl (iface->previous_address.s_addr)) &&
- ! options->doinform)
- {
- logger (LOG_ERR, "lost lease");
- if (! options->persistent)
- drop_config (state, options);
- } else if (! IN_LINKLOCAL (ntohl (iface->previous_address.s_addr)))
- logger (LOG_ERR, "timed out");
-
- do_socket (state, SOCKET_CLOSED);
- free_dhcp (dhcp);
- memset (dhcp, 0, sizeof (*dhcp));
-
-#ifdef ENABLE_INFO
- if (! options->test &&
- (options->doipv4ll || options->dolastlease))
- {
- errno = 0;
- if (! get_old_lease (state, options))
- {
- if (errno == EINTR)
- return (0);
- if (options->dolastlease)
- return (-1);
- free_dhcp (dhcp);
- memset (dhcp, 0, sizeof (*dhcp));
- } else if (errno == EINTR)
- return (0);
- }
-#endif
-
-#ifdef ENABLE_IPV4LL
- if (! options->test && options->doipv4ll &&
- (! dhcp->address.s_addr ||
- (! IN_LINKLOCAL (ntohl (dhcp->address.s_addr)) &&
- ! options->dolastlease)))
- {
- logger (LOG_INFO, "probing for an IPV4LL address");
- free_dhcp (dhcp);
- memset (dhcp, 0, sizeof (*dhcp));
- if (ipv4ll_get_address (iface, dhcp) == -1) {
- if (! state->daemonised)
- return (-1);
-
- /* start over */
- state->xid = 0;
- return (0);
- }
- state->timeout = dhcp->renewaltime;
- }
-#endif
-
-#if defined (ENABLE_INFO) || defined (ENABLE_IPV4LL)
- if (dhcp->address.s_addr) {
- if (! state->daemonised &&
- IN_LINKLOCAL (ntohl (dhcp->address.s_addr)))
- logger (LOG_WARNING, "using IPV4LL address %s",
- inet_ntoa (dhcp->address));
- if (configure (options, iface, dhcp, true) == -1 &&
- ! state->daemonised)
- return (-1);
-
- state->state = STATE_BOUND;
- if (! state->daemonised && options->daemonise) {
- switch (daemonise (state->pidfd)) {
- case -1:
- return (-1);
- case 0:
- state->daemonised = true;
- return (0);
- default:
- state->persistent = true;
- state->forked = true;
- return (-1);
- }
- }
-
- state->timeout = dhcp->renewaltime;
- state->xid = 0;
- return (0);
- }
-#endif
-
- if (! state->daemonised)
- return (-1);
- }
-
- switch (state->state) {
- case STATE_INIT:
- state->xid = (uint32_t) random ();
- do_socket (state, SOCKET_OPEN);
- state->timeout = options->timeout;
- iface->start_uptime = uptime ();
- if (dhcp->address.s_addr == 0) {
- if (! IN_LINKLOCAL (ntohl (iface->previous_address.s_addr)))
- logger (LOG_INFO, "broadcasting for a lease");
- _send_message (state, DHCP_DISCOVER, options);
- } else if (options->doinform) {
- logger (LOG_INFO, "broadcasting inform for %s",
- inet_ntoa (dhcp->address));
- _send_message (state, DHCP_INFORM, options);
- state->state = STATE_REQUESTING;
- } else {
- logger (LOG_INFO, "broadcasting for a lease of %s",
- inet_ntoa (dhcp->address));
- _send_message (state, DHCP_REQUEST, options);
- state->state = STATE_REQUESTING;
- }
-
- break;
- case STATE_BOUND:
- case STATE_RENEW_REQUESTED:
- if (IN_LINKLOCAL (ntohl (dhcp->address.s_addr))) {
- memset (&dhcp->address, 0, sizeof (dhcp->address));
- state->state = STATE_INIT;
- state->xid = 0;
- break;
- }
- state->state = STATE_RENEWING;
- state->xid = (uint32_t) random ();
- /* FALLTHROUGH */
- case STATE_RENEWING:
- iface->start_uptime = uptime ();
- logger (LOG_INFO, "renewing lease of %s", inet_ntoa
- (dhcp->address));
- do_socket (state, SOCKET_OPEN);
- _send_message (state, DHCP_REQUEST, options);
- state->timeout = dhcp->rebindtime - dhcp->renewaltime;
- state->state = STATE_REBINDING;
- break;
- case STATE_REBINDING:
- logger (LOG_ERR, "lost lease, attemping to rebind");
- memset (&dhcp->address, 0, sizeof (dhcp->address));
- do_socket (state, SOCKET_OPEN);
- if (state->xid == 0)
- state->xid = (uint32_t) random ();
- dhcp->serveraddress.s_addr = 0;
- _send_message (state, DHCP_REQUEST, options);
- state->timeout = dhcp->leasetime - dhcp->rebindtime;
- state->state = STATE_REQUESTING;
- break;
- case STATE_REQUESTING:
- state->state = STATE_INIT;
- do_socket (state, SOCKET_CLOSED);
- state->timeout = 0;
- break;
-
- case STATE_RELEASED:
- dhcp->leasetime = 0;
- break;
- }
-
- return (0);
-}
-
-
-static int handle_dhcp (state_t *state, int type, const options_t *options)
-{
- struct timespec ts;
- interface_t *iface = state->interface;
- dhcp_t *dhcp = state->dhcp;
-
- /* We should restart on a NAK */
- if (type == DHCP_NAK) {
- logger (LOG_INFO, "received NAK: %s", dhcp->message);
- state->state = STATE_INIT;
- state->timeout = 0;
- state->xid = 0;
- free_dhcp (dhcp);
- memset (dhcp, 0, sizeof (*dhcp));
-
- /* If we constantly get NAKS then we should slowly back off */
- if (state->nakoff > 0) {
- logger (LOG_DEBUG, "sleeping for %ld seconds",
- (long) state->nakoff);
- ts.tv_sec = state->nakoff;
- ts.tv_nsec = 0;
- state->nakoff *= 2;
- if (state->nakoff > NAKOFF_MAX)
- state->nakoff = NAKOFF_MAX;
- nanosleep (&ts, NULL);
- }
-
- return (0);
- }
-
- /* No NAK, so reset the backoff */
- state->nakoff = 1;
-
- if (type == DHCP_OFFER && state->state == STATE_INIT) {
- char *addr = strdup (inet_ntoa (dhcp->address));
- if (dhcp->servername[0])
- logger (LOG_INFO, "offered %s from %s `%s'",
- addr, inet_ntoa (dhcp->serveraddress),
- dhcp->servername);
- else
- logger (LOG_INFO, "offered %s from %s",
- addr, inet_ntoa (dhcp->serveraddress));
- free (addr);
-
-#ifdef ENABLE_INFO
- if (options->test) {
- write_info (iface, dhcp, options, false);
- errno = 0;
- return (-1);
- }
-#endif
-
- _send_message (state, DHCP_REQUEST, options);
- state->state = STATE_REQUESTING;
-
- return (0);
- }
-
- if (type == DHCP_OFFER) {
- logger (LOG_INFO, "got subsequent offer of %s, ignoring ",
- inet_ntoa (dhcp->address));
- return (0);
- }
-
- /* We should only be dealing with acks */
- if (type != DHCP_ACK) {
- logger (LOG_ERR, "%d not an ACK or OFFER", type);
- return (0);
- }
-
- switch (state->state) {
- case STATE_RENEW_REQUESTED:
- case STATE_REQUESTING:
- case STATE_RENEWING:
- case STATE_REBINDING:
- break;
- default:
- logger (LOG_ERR, "wrong state %d", state->state);
- }
-
- do_socket (state, SOCKET_CLOSED);
-
-#ifdef ENABLE_ARP
- if (options->doarp && iface->previous_address.s_addr !=
- dhcp->address.s_addr)
- {
- errno = 0;
- if (arp_claim (iface, dhcp->address)) {
- do_socket (state, SOCKET_OPEN);
- _send_message (state, DHCP_DECLINE, options);
- do_socket (state, SOCKET_CLOSED);
-
- free_dhcp (dhcp);
- memset (dhcp, 0, sizeof (*dhcp));
- state->xid = 0;
- state->timeout = 0;
- state->state = STATE_INIT;
-
- /* RFC 2131 says that we should wait for 10 seconds
- * before doing anything else */
- logger (LOG_INFO, "sleeping for 10 seconds");
- ts.tv_sec = 10;
- ts.tv_nsec = 0;
- nanosleep (&ts, NULL);
- return (0);
- } else if (errno == EINTR)
- return (0);
- }
-#endif
-
- if (options->doinform) {
- if (options->request_address.s_addr != 0)
- dhcp->address = options->request_address;
- else
- dhcp->address = iface->previous_address;
-
- logger (LOG_INFO, "received approval for %s",
- inet_ntoa (dhcp->address));
- if (iface->previous_netmask.s_addr != dhcp->netmask.s_addr) {
- add_address (iface->name, dhcp->address,
- dhcp->netmask, dhcp->broadcast);
- iface->previous_netmask.s_addr = dhcp->netmask.s_addr;
- }
- state->timeout = options->leasetime;
- if (state->timeout == 0)
- state->timeout = DEFAULT_LEASETIME;
- state->state = STATE_INIT;
- } else if (dhcp->leasetime == (unsigned) -1) {
- dhcp->renewaltime = dhcp->rebindtime = dhcp->leasetime;
- state->timeout = 1; /* So we wait for infinity */
- logger (LOG_INFO, "leased %s for infinity",
- inet_ntoa (dhcp->address));
- state->state = STATE_BOUND;
- } else {
- if (! dhcp->leasetime) {
- dhcp->leasetime = DEFAULT_LEASETIME;
- logger(LOG_INFO,
- "no lease time supplied, assuming %d seconds",
- dhcp->leasetime);
- }
- logger (LOG_INFO, "leased %s for %u seconds",
- inet_ntoa (dhcp->address), dhcp->leasetime);
-
- if (dhcp->rebindtime >= dhcp->leasetime) {
- dhcp->rebindtime = (dhcp->leasetime * 0.875);
- logger (LOG_ERR,
- "rebind time greater than lease "
- "time, forcing to %u seconds",
- dhcp->rebindtime);
- }
-
- if (dhcp->renewaltime > dhcp->rebindtime) {
- dhcp->renewaltime = (dhcp->leasetime * 0.5);
- logger (LOG_ERR,
- "renewal time greater than rebind time, "
- "forcing to %u seconds",
- dhcp->renewaltime);
- }
-
- if (! dhcp->renewaltime) {
- dhcp->renewaltime = (dhcp->leasetime * 0.5);
- logger (LOG_INFO,
- "no renewal time supplied, assuming %d seconds",
- dhcp->renewaltime);
- } else
- logger (LOG_DEBUG, "renew in %u seconds",
- dhcp->renewaltime);
-
- if (! dhcp->rebindtime) {
- dhcp->rebindtime = (dhcp->leasetime * 0.875);
- logger (LOG_INFO,
- "no rebind time supplied, assuming %d seconds",
- dhcp->rebindtime);
- } else
- logger (LOG_DEBUG, "rebind in %u seconds",
- dhcp->rebindtime);
-
- state->timeout = dhcp->renewaltime;
- state->state = STATE_BOUND;
- }
-
- state->xid = 0;
-
- if (configure (options, iface, dhcp, true) == -1 &&
- ! state->daemonised)
- return (-1);
-
- if (! state->daemonised && options->daemonise) {
- switch (daemonise (state->pidfd)) {
- case 0:
- state->daemonised = true;
- return (0);
- case -1:
- return (-1);
- default:
- state->persistent = true;
- state->forked = true;
- return (-1);
- }
- }
-
- return (0);
-}
-
-static int handle_packet (state_t *state, const options_t *options)
-{
- interface_t *iface = state->interface;
- bool valid = false;
- int type;
- struct dhcp_t *new_dhcp;
- dhcpmessage_t message;
-
- /* Allocate our buffer space for BPF.
- * We cannot do this until we have opened our socket as we don't
- * know how much of a buffer we need until then. */
- if (! state->buffer)
- state->buffer = xmalloc (iface->buffer_length);
- state->buffer_len = iface->buffer_length;
- state->buffer_pos = 0;
-
- /* We loop through until our buffer is empty.
- * The benefit is that if we get >1 DHCP packet in our buffer and
- * the first one fails for any reason, we can use the next. */
-
- memset (&message, 0, sizeof (message));
- new_dhcp = xmalloc (sizeof (*new_dhcp));
-
- do {
- if (get_packet (iface, (unsigned char *) &message,
- state->buffer,
- &state->buffer_len, &state->buffer_pos) == -1)
- break;
-
- if (state->xid != message.xid) {
- logger (LOG_DEBUG,
- "ignoring packet with xid 0x%x as it's not ours (0x%x)",
- message.xid, state->xid);
- continue;
- }
-
- logger (LOG_DEBUG, "got a packet with xid 0x%x", message.xid);
- memset (new_dhcp, 0, sizeof (*new_dhcp));
- type = parse_dhcpmessage (new_dhcp, &message);
- if (type == -1) {
- logger (LOG_ERR, "failed to parse packet");
- free_dhcp (new_dhcp);
- /* We don't abort on this, so return zero */
- return (0);
- }
-
- /* If we got here then the DHCP packet is valid and appears to
- * be for us, so let's clear the buffer as we don't care about
- * any more DHCP packets at this point. */
- valid = true;
- break;
- } while (state->buffer_pos != 0);
-
- /* No packets for us, so wait until we get one */
- if (! valid) {
- free (new_dhcp);
- return (0);
- }
-
- /* new_dhcp is now our master DHCP message */
- free_dhcp (state->dhcp);
- free (state->dhcp);
- state->dhcp = new_dhcp;
- new_dhcp = NULL;
-
- return (handle_dhcp (state, type, options));
-}
-
-int dhcp_run (const options_t *options, int *pidfd)
-{
- interface_t *iface;
- state_t *state = NULL;
- struct pollfd fds[] = {
- { -1, POLLIN, 0 },
- { -1, POLLIN, 0 }
- };
- int retval = -1;
- int sig;
-
- if (! options)
- return (-1);
-
- iface = read_interface (options->interface, options->metric);
- if (! iface)
- goto eexit;
-
- state = xzalloc (sizeof (*state));
- state->dhcp = xzalloc (sizeof (*state->dhcp));
- state->pidfd = pidfd;
- state->interface = iface;
-
- if (! client_setup (state, options))
- goto eexit;
-
- if (signal_init () == -1)
- goto eexit;
- if (signal_setup () == -1)
- goto eexit;
-
- fds[POLLFD_SIGNAL].fd = signal_fd ();
-
- for (;;) {
- retval = wait_for_packet (fds, state, options);
-
- /* We should always handle our signals first */
- if ((sig = (signal_read (&fds[POLLFD_SIGNAL]))) != -1) {
- if (handle_signal (sig, state, options))
- retval = 0;
- else
- retval = -1;
- } else if (retval == 0)
- retval = handle_timeout (state, options);
- else if (retval > 0 &&
- state->socket != SOCKET_CLOSED &&
- fds[POLLFD_IFACE].revents & POLLIN)
- retval = handle_packet (state, options);
- else if (retval == -1 && errno == EINTR) {
- /* The interupt will be handled above */
- retval = 0;
- } else {
- logger (LOG_ERR, "poll: %s", strerror (errno));
- retval = -1;
- }
-
- if (retval != 0)
- break;
- }
-
-eexit:
- if (iface) {
- do_socket (state, SOCKET_CLOSED);
- drop_config (state, options);
- free_route (iface->previous_routes);
- free (iface->clientid);
- free (iface);
- }
-
- if (state) {
- if (state->forked)
- retval = 0;
-
- if (state->daemonised)
- unlink (options->pidfile);
-
- free_dhcp (state->dhcp);
- free (state->dhcp);
- free (state->buffer);
- free (state);
- }
-
- return (retval);
-}