]> git.ipfire.org Git - thirdparty/dhcpcd.git/commitdiff
Add an event loop.
authorRoy Marples <roy@marples.name>
Tue, 2 Sep 2008 13:28:11 +0000 (13:28 +0000)
committerRoy Marples <roy@marples.name>
Tue, 2 Sep 2008 13:28:11 +0000 (13:28 +0000)
Split client.c into smaller files and functions and
recode around the event loop.
Add multiple interface support using the new event loop.
Document changes and outstanding bugs.

35 files changed:
Makefile
README
arp.c [new file with mode: 0644]
arp.h [new file with mode: 0644]
bind.c [new file with mode: 0644]
bind.h [new file with mode: 0644]
client.c [deleted file]
common.c
common.h
config.h
configure.c
configure.h
dhcp.c
dhcp.h
dhcpcd.8.in
dhcpcd.c
dhcpcd.h
dhcpf.h [new file with mode: 0644]
duid.c [new file with mode: 0644]
duid.h [moved from client.h with 92% similarity]
eloop.c [new file with mode: 0644]
eloop.h [new file with mode: 0644]
if-bsd.c
if-linux.c
if-options.c [new file with mode: 0644]
if-options.h [new file with mode: 0644]
ipv4ll.c [new file with mode: 0644]
ipv4ll.h [new file with mode: 0644]
logger.c
logger.h
mk/prog.mk
net.c
net.h
signals.c
signals.h

index fdb566f06af49323012fae394228b19a64b00912..58ca6ba0fcf25afbb0a7adf32a9c1e344b2cd879 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -3,8 +3,9 @@
 # Copyright 2008 Roy Marples <roy@marples.name>
 
 PROG=          dhcpcd
-SRCS=          common.c dhcp.c dhcpcd.c logger.c net.c signals.c
-SRCS+=         configure.c client.c
+SRCS=          arp.c bind.c common.c dhcp.c dhcpcd.c duid.c eloop.c
+SRCS+=         if-options.c ipv4ll.c logger.c net.c signals.c
+SRCS+=         configure.c
 SRCS+=         ${SRC_IF} ${SRC_PF}
 
 LIBEXECDIR?=   ${PREFIX}/libexec
diff --git a/README b/README
index 50294fe4561156844253165b893b93fac12473cf..8c6dd6f2ffee6bca6216679b441dce3b4152c229 100644 (file)
--- a/README
+++ b/README
@@ -43,21 +43,8 @@ make HOOKSCRIPTS=50-ntp install
 
 Compatibility
 -------------
-If you require compatibility with dhcpcd-3 and older style variables,
-you can install 50-dhcpcd-compat into the directory $LIBEXECDIR/dhcpcd-hooks
-We don't install this by default.
-You should also add -DCMDLINE_COMPAT to your CPPFLAGS if you need to be fully
-commandline compatible with prior versions.
-
-dhcpcd-3 enabled DUID support by default - this has changed in dhcpcd-4.
-You can enable it via the --duid, -D command line option or by using the
-duid directive in dhcpcd.conf.
-If CMDLINE_COMPAT is defined the we renable DUID support by default IF
-the dhcpcd.duid file exits. This keeps the clients working as they were,
-which is good.
-
-dhcpcd-4 is NOT fully commandline compatible with dhcpcd-2 and older and
-changes the meaning of some options.
+dhcpcd-4.1 is only fully command line compatible with dhcpcd-4.0
+For compatibility with older versions, use dhcpcd-4.0
 
 
 ChangeLog
diff --git a/arp.c b/arp.c
new file mode 100644 (file)
index 0000000..71431dc
--- /dev/null
+++ b/arp.c
@@ -0,0 +1,204 @@
+/* 
+ * 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 <errno.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "arp.h"
+#include "bind.h"
+#include "common.h"
+#include "dhcpcd.h"
+#include "eloop.h"
+#include "if-options.h"
+#include "ipv4ll.h"
+#include "logger.h"
+#include "net.h"
+
+static void
+handle_arp_failure(struct interface *iface)
+{
+       if (IN_LINKLOCAL(htonl(iface->state->fail.s_addr))) {
+               handle_ipv4ll_failure(iface);
+               return;
+       }
+       send_decline(iface);
+       close_sockets(iface);
+       add_timeout_sec(DHCP_ARP_FAIL, start_interface, iface);
+}
+
+void
+handle_arp_packet(struct interface *iface)
+{
+       struct arphdr reply;
+       uint32_t reply_s;
+       uint32_t reply_t;
+       uint8_t arp_reply[sizeof(reply) + 2 * sizeof(reply_s) + 2 * HWADDR_LEN];
+       uint8_t *hw_s, *hw_t;
+       ssize_t bytes;
+       struct if_state *state = iface->state;
+
+       state->fail.s_addr = 0;
+       for(;;) {
+               bytes = get_raw_packet(iface, ETHERTYPE_ARP,
+                                      arp_reply, sizeof(arp_reply));
+               if (bytes == 0 || bytes == -1)
+                       return;
+               /* We must have a full ARP header */
+               if ((size_t)bytes < sizeof(reply))
+                       continue;
+               memcpy(&reply, arp_reply, sizeof(reply));
+               /* Protocol must be IP. */
+               if (reply.ar_pro != htons(ETHERTYPE_IP))
+                       continue;
+               if (reply.ar_pln != sizeof(reply_s))
+                       continue;
+               /* Only these types are recognised */
+               if (reply.ar_op != htons(ARPOP_REPLY) &&
+                   reply.ar_op != htons(ARPOP_REQUEST))
+                       continue;
+
+               /* Get pointers to the hardware addreses */
+               hw_s = arp_reply + sizeof(reply);
+               hw_t = hw_s + reply.ar_hln + reply.ar_pln;
+               /* Ensure we got all the data */
+               if ((hw_t + reply.ar_hln + reply.ar_pln) - arp_reply > bytes)
+                       continue;
+               /* Ignore messages from ourself */
+               if (reply.ar_hln == iface->hwlen &&
+                   memcmp(hw_s, iface->hwaddr, iface->hwlen) == 0)
+                       continue;
+               /* Copy out the IP addresses */
+               memcpy(&reply_s, hw_s + reply.ar_hln, reply.ar_pln);
+               memcpy(&reply_t, hw_t + reply.ar_hln, reply.ar_pln);
+
+               /* Check for conflict */
+               if (state->offer && 
+                   (reply_s == state->offer->yiaddr ||
+                    (reply_s == 0 && reply_t == state->offer->yiaddr)))
+                       state->fail.s_addr = state->offer->yiaddr;
+
+               /* Handle IPv4LL conflicts */
+               if (IN_LINKLOCAL(htonl(iface->addr.s_addr)) &&
+                   (reply_s == iface->addr.s_addr ||
+                    (reply_s == 0 && reply_t == iface->addr.s_addr)))
+                       state->fail.s_addr = iface->addr.s_addr;
+
+               if (state->fail.s_addr) {
+                       logger(LOG_ERR, "%s: hardware address %s claims %s",
+                              iface->name,
+                              hwaddr_ntoa((unsigned char *)hw_s,
+                                          (size_t)reply.ar_hln),
+                              inet_ntoa(state->fail));
+                       errno = EEXIST;
+                       handle_arp_failure(iface);
+                       return;
+               }
+       }
+}
+
+void
+send_arp_announce(struct interface *iface)
+{
+       struct if_state *state = iface->state;
+       struct timeval tv;
+
+       if (iface->arp_fd == -1) {
+               open_socket(iface, ETHERTYPE_ARP);
+               add_event(iface->arp_fd, handle_arp_packet, iface);
+       }
+       if (++state->claims < ANNOUNCE_NUM)     
+               logger(LOG_DEBUG,
+                      "%s: sending ARP announce (%d of %d), "
+                      "next in %d.00 seconds",
+                      iface->name, state->claims, ANNOUNCE_NUM, ANNOUNCE_WAIT);
+       else
+               logger(LOG_DEBUG,
+                      "%s: sending ARP announce (%d of %d)",
+                      iface->name, state->claims, ANNOUNCE_NUM);
+       if (send_arp(iface, ARPOP_REQUEST,
+                    state->new->yiaddr, state->new->yiaddr) == -1)
+               logger(LOG_ERR, "send_arp: %s", strerror(errno));
+       if (state->claims < ANNOUNCE_NUM) {
+               add_timeout_sec(ANNOUNCE_WAIT, send_arp_announce, iface);
+               return;
+       }
+       if (IN_LINKLOCAL(htonl(state->new->yiaddr))) {
+               /* We should pretend to be at the end
+                * of the DHCP negotation cycle */
+               state->interval = 64;
+               state->probes = 0;
+               state->claims = 0;
+               tv.tv_sec = state->interval - DHCP_RAND_MIN;
+               tv.tv_usec = arc4random() % (DHCP_RAND_MAX_U - DHCP_RAND_MIN_U);
+               tv.tv_sec = 3; /* test easier */
+               timernorm(&tv);
+               add_timeout_tv(&tv, start_discover, iface);
+       } else {
+               delete_event(iface->arp_fd);
+               close(iface->arp_fd);
+               iface->arp_fd = -1;
+       }
+}
+
+void
+send_arp_probe(struct interface *iface)
+{
+       struct if_state *state = iface->state;
+       struct in_addr addr;
+       struct timeval tv;
+
+       if (iface->arp_fd == -1) {
+               open_socket(iface, ETHERTYPE_ARP);
+               add_event(iface->arp_fd, handle_arp_packet, iface);
+       }
+       if (state->probes == 0) {
+               addr.s_addr = state->offer->yiaddr;
+               logger(LOG_INFO, "%s: checking %s is available"
+                               " on attached networks",
+                               iface->name, inet_ntoa(addr));
+       }
+       if (++state->probes < PROBE_NUM) {
+               tv.tv_sec = PROBE_MIN;
+               tv.tv_usec = arc4random() % (PROBE_MAX_U - PROBE_MIN_U);
+               timernorm(&tv);
+               add_timeout_tv(&tv, send_arp_probe, iface);
+       } else {
+               tv.tv_sec = ANNOUNCE_WAIT;
+               tv.tv_usec = 0;
+               if (IN_LINKLOCAL(htonl(state->offer->yiaddr)))
+                       add_timeout_tv(&tv, bind_interface, iface);
+               else
+                       add_timeout_tv(&tv, send_request, iface);
+       }
+       logger(LOG_DEBUG,
+               "%s: sending ARP probe (%d of %d), next in %0.2f seconds",
+               iface->name, state->probes, PROBE_NUM,  timeval_to_double(&tv));
+       if (send_arp(iface, ARPOP_REQUEST, 0, state->offer->yiaddr) == -1)
+               logger(LOG_ERR, "send_arp: %s", strerror(errno));
+}
diff --git a/arp.h b/arp.h
new file mode 100644 (file)
index 0000000..3cfe55a
--- /dev/null
+++ b/arp.h
@@ -0,0 +1,49 @@
+/* 
+ * 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.
+ */
+
+#ifndef ARP_H
+#define ARP_H
+
+#include "dhcpcd.h"
+
+/* These are for IPV4LL, RFC 3927.
+ * We put them here as we use the timings for all ARP foo. */
+#define PROBE_WAIT              1
+#define PROBE_NUM               3
+#define PROBE_MIN               1
+#define PROBE_MAX               2
+#define ANNOUNCE_WAIT           2
+#define ANNOUNCE_NUM            2
+#define ANNOUNCE_INTERVAL       2
+#define MAX_CONFLICTS          10
+#define RATE_LIMIT_INTERVAL    60
+#define DEFEND_INTERVAL                10
+
+void send_arp_announce(struct interface *);
+void send_arp_probe(struct interface *);
+void handle_arp_packet(struct interface *);
+#endif
diff --git a/bind.c b/bind.c
new file mode 100644 (file)
index 0000000..452738c
--- /dev/null
+++ b/bind.c
@@ -0,0 +1,189 @@
+/* 
+ * 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 <errno.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "arp.h"
+#include "bind.h"
+#include "common.h"
+#include "configure.h"
+#include "dhcpcd.h"
+#include "dhcpf.h"
+#include "eloop.h"
+#include "if-options.h"
+#include "logger.h"
+#include "net.h"
+#include "signals.h"
+
+static int daemonised = 0;
+int can_daemonise = 1;
+
+#ifndef THERE_IS_NO_FORK
+pid_t
+daemonise(void)
+{
+       pid_t pid;
+       sigset_t full;
+       sigset_t old;
+       char buf = '\0';
+       int sidpipe[2];
+
+       if (daemonised || !can_daemonise)
+               return 0;
+       sigfillset(&full);
+       sigprocmask(SIG_SETMASK, &full, &old);
+       /* Setup a signal pipe so parent knows when to exit. */
+       if (pipe(sidpipe) == -1) {
+               logger(LOG_ERR, "pipe: %s", strerror(errno));
+               return -1;
+       }
+       logger(LOG_INFO, "forking to background");
+       switch (pid = fork()) {
+               case -1:
+                       logger(LOG_ERR, "fork: %s", strerror(errno));
+                       exit(EXIT_FAILURE);
+                       /* NOTREACHED */
+               case 0:
+                       setsid();
+                       /* Notify parent it's safe to exit as we've detached. */
+                       close(sidpipe[0]);
+                       write(sidpipe[1], &buf, 1);
+                       close(sidpipe[1]);
+                       close_fds();
+                       break;
+               default:
+                       signal_reset();
+                       /* Wait for child to detach */
+                       close(sidpipe[1]);
+                       read(sidpipe[0], &buf, 1);
+                       close(sidpipe[0]);
+                       break;
+       }
+       /* Done with the fd now */
+       if (pid != 0) {
+               writepid(pidfd, pid);
+               close(pidfd);
+               pidfd = -1;
+               exit(EXIT_SUCCESS);
+       }
+       sigprocmask(SIG_SETMASK, &old, NULL);
+       return pid;
+}
+#endif
+
+void bind_interface(struct interface *iface)
+{
+       struct if_state *state = iface->state;
+       struct if_options *ifo = state->options;
+       struct dhcp_lease *lease = &state->lease;
+       struct timeval tv;
+       const char *reason = NULL;
+
+       delete_timeout(handle_exit_timeout, NULL);
+       if (clock_monotonic)
+               get_monotonic(&lease->boundtime);
+       state->state = DHS_BOUND;
+       state->xid = 0;
+       free(state->old);
+       state->old = state->new;
+       state->new = state->offer;
+       state->offer = NULL;
+       get_lease(lease, state->new);
+       if (IN_LINKLOCAL(htonl(state->new->yiaddr))) {
+               logger(LOG_INFO, "%s: using IPv4LL address %s",
+                      iface->name, inet_ntoa(lease->addr));
+               lease->leasetime = ~0U;
+               reason = "IPV4LL";
+       } else if (ifo->options & DHCPCD_INFORM) {
+               if (ifo->request_address.s_addr != 0)
+                       lease->addr.s_addr = ifo->request_address.s_addr;
+               else
+                       lease->addr.s_addr = iface->addr.s_addr;
+               logger(LOG_INFO, "%s: received approval for %s", iface->name,
+                      inet_ntoa(lease->addr));
+               lease->leasetime = ~0U;
+               reason = "INFORM";
+       } else {
+               if (gettimeofday(&tv, NULL) == 0)
+                       lease->leasedfrom = tv.tv_sec;
+               if (lease->frominfo)
+                       reason = "TIMEOUT";
+               if (lease->leasetime == ~0U) {
+                       lease->renewaltime = lease->rebindtime = lease->leasetime;
+                       logger(LOG_INFO, "%s: leased %s for infinity",
+                              iface->name, inet_ntoa(lease->addr));
+               } else {
+                       if (lease->rebindtime >= lease->leasetime) {
+                               lease->rebindtime = lease->leasetime * T2;
+                               logger(LOG_ERR,
+                                      "%s: rebind time greater than lease "
+                                      "time, forcing to %u seconds",
+                                      iface->name, lease->rebindtime);
+                       }
+                       if (lease->renewaltime > lease->rebindtime) {
+                               lease->renewaltime = lease->leasetime * T1;
+                               logger(LOG_ERR,
+                                      "%s: renewal time greater than rebind "
+                                      "time, forcing to %u seconds",
+                                      iface->name, lease->renewaltime);
+                       }
+                       if (!lease->renewaltime)
+                               lease->renewaltime = lease->leasetime * T1;
+                       if (!lease->rebindtime)
+                               lease->rebindtime = lease->leasetime * T2;
+                       logger(LOG_INFO,
+                              "%s: leased %s for %u seconds", iface->name,
+                              inet_ntoa(lease->addr), lease->leasetime);
+               }
+       }
+       if (!reason) {
+               if (state->old) {
+                       if (state->old->yiaddr == state->new->yiaddr &&
+                           lease->server.s_addr)
+                               reason = "RENEW";
+                       else
+                               reason = "REBIND";
+               } else
+                       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);
+       }
+       configure(iface, reason);
+       daemonise();
+       if (ifo->options & DHCPCD_ARP) {
+               state->claims = 0;
+               send_arp_announce(iface);
+       }
+}
diff --git a/bind.h b/bind.h
new file mode 100644 (file)
index 0000000..5aaabf7
--- /dev/null
+++ b/bind.h
@@ -0,0 +1,41 @@
+/* 
+ * 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.
+ */
+
+#ifndef BIND_H
+#define BIND_H
+
+#include "config.h"
+#include "dhcpcd.h"
+#ifdef THERE_IS_NO_FORK
+#define daemonise() {}
+#else
+pid_t daemonise(void);
+#endif
+
+extern int can_daemonise;
+void bind_interface(struct interface *);
+#endif
diff --git a/client.c b/client.c
deleted file mode 100644 (file)
index c34d318..0000000
--- a/client.c
+++ /dev/null
@@ -1,1839 +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/stat.h>
-#include <sys/time.h>
-#include <sys/types.h>
-#include <arpa/inet.h>
-
-#ifdef __linux__
-# include <netinet/ether.h>
-#endif
-
-#include <errno.h>
-#include <poll.h>
-#include <signal.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <time.h>
-#include <unistd.h>
-
-#include "config.h"
-#include "common.h"
-#include "client.h"
-#include "configure.h"
-#include "dhcp.h"
-#include "dhcpcd.h"
-#include "net.h"
-#include "logger.h"
-#include "signals.h"
-
-#define IPV4LL_LEASETIME       2
-
-/* Some platforms don't define INFTIM */
-#ifndef INFTIM
-# define INFTIM                 -1
-#endif
-
-#define STATE_INIT              0
-#define STATE_DISCOVERING      1
-#define STATE_REQUESTING        2
-#define STATE_BOUND             3
-#define STATE_RENEWING          4
-#define STATE_REBINDING         5
-#define STATE_REBOOT            6
-#define STATE_RENEW_REQUESTED   7
-#define STATE_INIT_IPV4LL      8
-#define STATE_PROBING          9
-#define STATE_ANNOUNCING       10
-
-/* Constants taken from RFC 2131. */
-#define T1                     0.5
-#define T2                     0.875
-#define DHCP_BASE              4
-#define DHCP_MAX               64
-#define DHCP_RAND_MIN          -1
-#define DHCP_RAND_MAX          1
-#define DHCP_ARP_FAIL          10
-
-/* We should define a maximum for the NAK exponential backoff */ 
-#define NAKOFF_MAX              60
-
-#define SOCKET_CLOSED           0
-#define SOCKET_OPEN             1
-
-/* These are for IPV4LL, RFC 3927. */
-#define PROBE_WAIT              1
-#define PROBE_NUM               3
-#define PROBE_MIN               1
-#define PROBE_MAX               2
-#define ANNOUNCE_WAIT           2
-/* BSD systems always do a grauitous ARP when assigning an address,
- * so we can do one less announce. */
-#ifdef BSD
-# define ANNOUNCE_NUM           1
-#else
-# define ANNOUNCE_NUM           2
-#endif
-#define ANNOUNCE_INTERVAL       2
-#define MAX_CONFLICTS          10
-#define RATE_LIMIT_INTERVAL    60
-#define DEFEND_INTERVAL                10
-
-
-/* number of usecs in a second. */
-#define USECS_SECOND           1000000
-/* As we use timevals, we should use the usec part for
- * greater randomisation. */
-#define DHCP_RAND_MIN_U                DHCP_RAND_MIN * USECS_SECOND
-#define DHCP_RAND_MAX_U                DHCP_RAND_MAX * USECS_SECOND
-#define PROBE_MIN_U            PROBE_MIN * USECS_SECOND
-#define PROBE_MAX_U            PROBE_MAX * USECS_SECOND
-
-#define timernorm(tvp)                                         \
-       do {                                                    \
-               while ((tvp)->tv_usec >= 1000000) {             \
-                       (tvp)->tv_sec++;                        \
-                       (tvp)->tv_usec -= 1000000;              \
-               }                                               \
-       } while (0 /* CONSTCOND */);
-
-#define timerneg(tvp)  ((tvp)->tv_sec < 0 || (tvp)->tv_usec < 0)
-
-struct if_state {
-       int options;
-       struct interface *interface;
-       struct dhcp_message *offer;
-       struct dhcp_message *new;
-       struct dhcp_message *old;
-       struct dhcp_lease lease;
-       struct timeval timeout;
-       struct timeval stop;
-       struct timeval exit;
-       int state;
-       int messages;
-       time_t nakoff;
-       uint32_t xid;
-       int socket;
-       int *pid_fd;
-       int signal_fd;
-       int carrier;
-       int probes;
-       int claims;
-       int conflicts;
-       time_t defend;
-       struct in_addr fail;
-};
-
-#define LINK_UP        1
-#define LINK_UNKNOWN   0
-#define LINK_DOWN      -1
-
-struct dhcp_op {
-        uint8_t value;
-        const char *name;
-};
-
-static const struct dhcp_op const dhcp_ops[] = {
-       { DHCP_DISCOVER, "DHCP_DISCOVER" },
-       { DHCP_OFFER,    "DHCP_OFFER" },
-       { DHCP_REQUEST,  "DHCP_REQUEST" },
-       { DHCP_DECLINE,  "DHCP_DECLINE" },
-       { DHCP_ACK,      "DHCP_ACK" },
-       { DHCP_NAK,      "DHCP_NAK" },
-       { DHCP_RELEASE,  "DHCP_RELEASE" },
-       { DHCP_INFORM,   "DHCP_INFORM" },
-       { 0, NULL }
-};
-
-static const char *
-get_dhcp_op(uint8_t type)
-{
-       const struct dhcp_op *d;
-
-       for (d = dhcp_ops; d->name; d++)
-               if (d->value == type)
-                       return d->name;
-       return NULL;
-}
-
-#ifdef THERE_IS_NO_FORK
-#define daemonise(a,b) 0
-#else
-static int
-daemonise(struct if_state *state, const struct options *options)
-{
-       pid_t pid;
-       sigset_t full;
-       sigset_t old;
-       char buf = '\0';
-       int sidpipe[2];
-
-       if (state->options & DHCPCD_DAEMONISED ||
-           !(options->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) {
-               logger(LOG_ERR,"pipe: %s", strerror(errno));
-               return -1;
-       }
-
-       logger(LOG_DEBUG, "forking to background");
-       switch (pid = fork()) {
-               case -1:
-                       logger(LOG_ERR, "fork: %s", strerror(errno));
-                       exit(EXIT_FAILURE);
-                       /* NOTREACHED */
-               case 0:
-                       setsid();
-                       /* Notify parent it's safe to exit as we've detached. */
-                       close(sidpipe[0]);
-                       write(sidpipe[1], &buf, 1);
-                       close(sidpipe[1]);
-                       close_fds();
-                       break;
-               default:
-                       /* Reset signals as we're the parent about to exit. */
-                       signal_reset();
-                       /* Wait for child to detach */
-                       close(sidpipe[1]);
-                       read(sidpipe[0], &buf, 1);
-                       close(sidpipe[0]);
-                       break;
-       }
-
-       /* Done with the fd now */
-       if (pid != 0) {
-               writepid(*state->pid_fd, pid);
-               close(*state->pid_fd);
-               *state->pid_fd = -1;
-       }
-
-       sigprocmask(SIG_SETMASK, &old, NULL);
-       if (pid == 0) {
-               state->options |= DHCPCD_DAEMONISED;
-               timerclear(&state->exit);
-               return 0;
-       }
-       state->options |= DHCPCD_PERSISTENT | DHCPCD_FORKED;
-       return -1;
-}
-#endif
-
-#define THIRTY_YEARS_IN_SECONDS    946707779
-static size_t
-get_duid(unsigned char *duid, const struct interface *iface)
-{
-       FILE *f;
-       uint16_t type = 0;
-       uint16_t hw = 0;
-       uint32_t ul;
-       time_t t;
-       int x = 0;
-       unsigned char *p = duid;
-       size_t len = 0, l = 0;
-       char *buffer = NULL, *line, *option;
-
-       /* If we already have a DUID then use it as it's never supposed
-        * to change once we have one even if the interfaces do */
-       if ((f = fopen(DUID, "r"))) {
-               while ((get_line(&buffer, &len, f))) {
-                       line = buffer;
-                       while ((option = strsep(&line, " \t")))
-                               if (*option != '\0')
-                                       break;
-                       if (!option || *option == '\0' || *option == '#')
-                               continue;
-                       l = hwaddr_aton(NULL, option);
-                       if (l && l <= DUID_LEN) {
-                               hwaddr_aton(duid, option);
-                               break;
-                       }
-                       l = 0;
-               }
-               fclose(f);
-               free(buffer);
-               if (l)
-                       return l;
-       } else {
-               if (errno != ENOENT)
-                       return 0;
-       }
-
-       /* No file? OK, lets make one based on our interface */
-       if (!(f = fopen(DUID, "w")))
-               return 0;
-       type = htons(1); /* DUI-D-LLT */
-       memcpy(p, &type, 2);
-       p += 2;
-       hw = htons(iface->family);
-       memcpy(p, &hw, 2);
-       p += 2;
-       /* time returns seconds from jan 1 1970, but DUID-LLT is
-        * seconds from jan 1 2000 modulo 2^32 */
-       t = time(NULL) - THIRTY_YEARS_IN_SECONDS;
-       ul = htonl(t & 0xffffffff);
-       memcpy(p, &ul, 4);
-       p += 4;
-       /* Finally, add the MAC address of the interface */
-       memcpy(p, iface->hwaddr, iface->hwlen);
-       p += iface->hwlen;
-       len = p - duid;
-       x = fprintf(f, "%s\n", hwaddr_ntoa(duid, len));
-       fclose(f);
-       /* Failed to write the duid? scrub it, we cannot use it */
-       if (x < 1) {
-               len = 0;
-               unlink(DUID);
-       }
-       return len;
-}
-
-static struct dhcp_message*
-ipv4ll_get_dhcp(uint32_t old_addr)
-{
-       uint32_t u32;
-       struct dhcp_message *dhcp;
-       uint8_t *p;
-
-       dhcp = xzalloc(sizeof(*dhcp));
-       /* Put some LL options in */
-       p = dhcp->options;
-       *p++ = DHO_SUBNETMASK;
-       *p++ = sizeof(u32);
-       u32 = htonl(LINKLOCAL_MASK);
-       memcpy(p, &u32, sizeof(u32));
-       p += sizeof(u32);
-       *p++ = DHO_BROADCAST;
-       *p++ = sizeof(u32);
-       u32 = htonl(LINKLOCAL_BRDC);
-       memcpy(p, &u32, sizeof(u32));
-       p += sizeof(u32);
-       *p++ = DHO_END;
-
-       for (;;) {
-               dhcp->yiaddr = htonl(LINKLOCAL_ADDR |
-                                    (((uint32_t)abs((int)arc4random())
-                                      % 0xFD00) + 0x0100));
-               if (dhcp->yiaddr != old_addr &&
-                   IN_LINKLOCAL(ntohl(dhcp->yiaddr)))
-                       break;
-       }
-       return dhcp;
-}
-
-static double
-timeval_to_double(struct timeval *tv)
-{
-       return tv->tv_sec * 1.0 + tv->tv_usec * 1.0e-6;
-}
-
-static void
-get_lease(struct dhcp_lease *lease, const struct dhcp_message *dhcp)
-{
-       time_t t;
-
-       lease->frominfo = 0;
-       lease->addr.s_addr = dhcp->yiaddr;
-
-       if (get_option_addr(&lease->net.s_addr, dhcp, DHO_SUBNETMASK) == -1)
-               lease->net.s_addr = get_netmask(dhcp->yiaddr);
-       if (get_option_uint32(&lease->leasetime, dhcp, DHO_LEASETIME) == 0) {
-               /* Ensure that we can use the lease */
-               t = 0;
-               if (t + (time_t)lease->leasetime < t) {
-                       logger(LOG_WARNING, "lease of %u would overflow, "
-                              "treating as infinite", lease->leasetime);
-                       lease->leasetime = ~0U; /* Infinite lease */
-               }
-       } else
-               lease->leasetime = DEFAULT_LEASETIME;
-       if (get_option_uint32(&lease->renewaltime, dhcp, DHO_RENEWALTIME) != 0)
-               lease->renewaltime = 0;
-       if (get_option_uint32(&lease->rebindtime, dhcp, DHO_REBINDTIME) != 0)
-               lease->rebindtime = 0;
-}
-
-static int
-get_old_lease(struct if_state *state)
-{
-       struct interface *iface = state->interface;
-       struct dhcp_lease *lease = &state->lease;
-       struct dhcp_message *dhcp = NULL;
-       struct timeval tv;
-       unsigned int offset = 0;
-       struct stat sb;
-
-       if (stat(iface->leasefile, &sb) == -1) {
-               if (errno != ENOENT)
-                       logger(LOG_ERR, "stat: %s", strerror(errno));
-               goto eexit;
-       }
-       if (!IN_LINKLOCAL(ntohl(iface->addr.s_addr)))
-               logger(LOG_INFO, "trying to use old lease in `%s'",
-                      iface->leasefile);
-       if ((dhcp = read_lease(iface)) == NULL) {
-               logger(LOG_INFO, "read_lease: %s", strerror(errno));
-               goto eexit;
-       }
-       get_lease(&state->lease, dhcp);
-       lease->frominfo = 1;
-       lease->leasedfrom = sb.st_mtime;
-
-       /* Vitaly important we remove the server information here */
-       state->lease.server.s_addr = 0;
-       dhcp->servername[0] = '\0';
-
-       if (!IN_LINKLOCAL(ntohl(dhcp->yiaddr))) {
-               if (!(state->options & DHCPCD_LASTLEASE))
-                       goto eexit;
-
-               /* Ensure that we can still use the lease */
-               if (gettimeofday(&tv, NULL) == -1) {
-                       logger(LOG_ERR, "gettimeofday: %s", strerror(errno));
-                       goto eexit;
-               }
-
-               offset = tv.tv_sec - lease->leasedfrom;
-               if (lease->leasedfrom &&
-                   tv.tv_sec - lease->leasedfrom > (time_t)lease->leasetime)
-               {
-                       logger(LOG_ERR, "lease expired %u seconds ago",
-                              offset + lease->leasetime);
-                       /* Persistent interfaces should still try and use the
-                        * lease if we can't contact a DHCP server.
-                        * We just set the timeout to 1 second. */
-                       if (state->options & DHCPCD_PERSISTENT)
-                               offset = lease->renewaltime - 1;
-                       else
-                               goto eexit;
-               }
-       }
-
-       if (lease->leasedfrom == 0)
-               offset = 0;
-       iface->start_uptime = uptime();
-       state->timeout.tv_sec = lease->renewaltime - offset;
-       free(state->old);
-       state->old = state->new;
-       state->new = NULL;
-       state->offer = dhcp;
-       return 0;
-
-eexit:
-       lease->addr.s_addr = 0;
-       free(dhcp);
-       return -1;
-}
-
-static int
-client_setup(struct if_state *state, const struct options *options)
-{
-       struct interface *iface = state->interface;
-       struct dhcp_lease *lease = &state->lease;
-       struct in_addr addr;
-       struct timeval tv;
-       size_t len = 0;
-       unsigned char *duid = NULL;
-       uint32_t ul;
-
-       state->state = STATE_INIT;
-       state->nakoff = 1;
-       state->options = options->options;
-       timerclear(&tv);
-
-       if (options->request_address.s_addr == 0 &&
-           (options->options & DHCPCD_INFORM ||
-            options->options & DHCPCD_REQUEST ||
-            (options->options & DHCPCD_DAEMONISED &&
-             !(options->options & DHCPCD_BACKGROUND))))
-       {
-               if (get_old_lease(state) != 0)
-                       return -1;
-               timerclear(&state->timeout);
-
-               if (!(options->options & DHCPCD_DAEMONISED) &&
-                   IN_LINKLOCAL(ntohl(lease->addr.s_addr)))
-               {
-                       logger(LOG_ERR, "cannot request a link local address");
-                       return -1;
-               }
-       } else {
-               lease->addr.s_addr = options->request_address.s_addr;
-               lease->net.s_addr = options->request_netmask.s_addr;
-       }
-
-       if (options->options & DHCPCD_REQUEST &&
-           state->options & DHCPCD_ARP &&
-           !state->offer)
-       {
-               state->offer = xzalloc(sizeof(*state->offer));
-               state->offer->yiaddr = options->request_address.s_addr;
-               state->state = STATE_PROBING;
-               state->xid = arc4random();
-       }
-
-       /* If INFORMing, ensure the interface has the address */
-       if (state->options & DHCPCD_INFORM &&
-           has_address(iface->name, &lease->addr, &lease->net) < 1)
-       {
-               addr.s_addr = lease->addr.s_addr | ~lease->net.s_addr;
-               logger(LOG_DEBUG, "adding IP address %s/%d",
-                      inet_ntoa(lease->addr), inet_ntocidr(lease->net));
-               if (add_address(iface->name, &lease->addr,
-                               &lease->net, &addr) == -1)
-               {
-                       logger(LOG_ERR, "add_address: %s", strerror(errno));
-                       return -1;
-               }
-               iface->addr.s_addr = lease->addr.s_addr;
-               iface->net.s_addr = lease->net.s_addr;
-       }
-
-       if (*options->clientid) {
-               iface->clientid = xmalloc(options->clientid[0] + 1);
-               memcpy(iface->clientid,
-                      options->clientid, options->clientid[0] + 1);
-       } else if (options->options & DHCPCD_CLIENTID) {
-               if (options->options & DHCPCD_DUID) {
-                       duid = xmalloc(DUID_LEN);
-                       if ((len = get_duid(duid, iface)) == 0)
-                               logger(LOG_ERR, "get_duid: %s",
-                                      strerror(errno));
-               }
-
-               if (len > 0) {
-                       logger(LOG_DEBUG, "DUID = %s",
-                              hwaddr_ntoa(duid, len));
-
-                       iface->clientid = xmalloc(len + 6);
-                       iface->clientid[0] = len + 5;
-                       iface->clientid[1] = 255; /* RFC 4361 */
-
-                       /* IAID is 4 bytes, so if the iface name is 4 bytes
-                        * or less, use it */
-                       ul = strlen(iface->name);
-                       if (ul < 5) {
-                               memcpy(iface->clientid + 2, iface->name, ul);
-                               if (ul < 4)
-                                       memset(iface->clientid + 2 + ul,
-                                              0, 4 - ul);
-                       } else {
-                               /* Name isn't 4 bytes, so use the index */
-                               ul = htonl(if_nametoindex(iface->name));
-                               memcpy(iface->clientid + 2, &ul, 4);
-                       }
-
-                       memcpy(iface->clientid + 6, duid, len);
-                       free(duid);
-               }
-               if (len == 0) {
-                       len = iface->hwlen + 1;
-                       iface->clientid = xmalloc(len + 1);
-                       iface->clientid[0] = len;
-                       iface->clientid[1] = iface->family;
-                       memcpy(iface->clientid + 2, iface->hwaddr, iface->hwlen);
-               }
-       }
-
-       if (state->options & DHCPCD_LINK) {
-               open_link_socket(iface);
-               switch (carrier_status(iface->name)) {
-               case 0:
-                       state->carrier = LINK_DOWN;
-                       break;
-               case 1:
-                       state->carrier = LINK_UP;
-                       break;
-               default:
-                       state->carrier = LINK_UNKNOWN;
-               }
-       }
-
-       if (options->timeout > 0 &&
-           !(state->options & DHCPCD_DAEMONISED))
-       {
-               if (state->options & DHCPCD_IPV4LL) {
-                       state->stop.tv_sec = options->timeout;
-                       if (!(state->options & DHCPCD_BACKGROUND))
-                               state->exit.tv_sec = state->stop.tv_sec + 10;
-               } else if (!(state->options & DHCPCD_BACKGROUND))
-                       state->exit.tv_sec = options->timeout;
-       }
-       return 0;
-}
-
-static int 
-do_socket(struct if_state *state, int mode)
-{
-       if (state->interface->raw_fd != -1) {
-               close(state->interface->raw_fd);
-               state->interface->raw_fd = -1;
-       }
-       if (mode == SOCKET_CLOSED) {
-               if (state->interface->udp_fd != -1) {
-                       close(state->interface->udp_fd);
-                       state->interface->udp_fd = -1;
-               }
-               if (state->interface->arp_fd != -1) {
-                       close(state->interface->arp_fd);
-                       state->interface->arp_fd = -1;
-               }
-       }
-
-       /* Always have the UDP socket open to avoid the kernel sending
-        * ICMP unreachable messages. */
-       /* For systems without SO_BINDTODEVICE, (ie BSD ones) we may get an
-        * error or EADDRINUSE when binding to INADDR_ANY as another dhcpcd
-        * instance could be running.
-        * Oddly enough, we don't care about this as the socket is there
-        * just to please the kernel - we don't care for reading from it. */
-       if (mode == SOCKET_OPEN &&
-           state->interface->udp_fd == -1 &&
-           open_udp_socket(state->interface) == -1 &&
-           (errno != EADDRINUSE || state->interface->addr.s_addr != 0))
-               logger(LOG_ERR, "open_udp_socket: %s", strerror(errno));
-
-       if (mode == SOCKET_OPEN) 
-               if (open_socket(state->interface, ETHERTYPE_IP) == -1) {
-                       logger(LOG_ERR, "open_socket: %s", strerror(errno));
-                       return -1;
-               }
-       state->socket = mode;
-       return 0;
-}
-
-static ssize_t
-send_message(struct if_state *state, int type, const struct options *options)
-{
-       struct dhcp_message *dhcp;
-       uint8_t *udp;
-       ssize_t len, r;
-       struct in_addr from, to;
-       in_addr_t a = 0;
-
-       if (state->carrier == LINK_DOWN)
-               return 0;
-       if (type == DHCP_RELEASE)
-               logger(LOG_DEBUG, "sending %s with xid 0x%x",
-                      get_dhcp_op(type), state->xid);
-       else
-               logger(LOG_DEBUG,
-                      "sending %s with xid 0x%x, next in %0.2f seconds",
-                      get_dhcp_op(type), state->xid,
-                      timeval_to_double(&state->timeout));
-       state->messages++;
-       if (state->messages < 0)
-               state->messages = INT_MAX;
-       /* If we couldn't open a UDP port for our IP address
-        * then we cannot renew.
-        * This could happen if our IP was pulled out from underneath us. */
-       if (state->interface->udp_fd == -1) {
-               a = state->interface->addr.s_addr;
-               state->interface->addr.s_addr = 0;
-       }
-       len = make_message(&dhcp, state->interface, &state->lease, state->xid,
-                          type, options);
-       if (state->interface->udp_fd == -1)
-               state->interface->addr.s_addr = a;
-       from.s_addr = dhcp->ciaddr;
-       if (from.s_addr)
-               to.s_addr = state->lease.server.s_addr;
-       else
-               to.s_addr = 0;
-       if (to.s_addr && to.s_addr != INADDR_BROADCAST) {
-               r = send_packet(state->interface, to, (uint8_t *)dhcp, len);
-               if (r == -1)
-                       logger(LOG_ERR, "send_packet: %s", strerror(errno));
-       } else {
-               len = make_udp_packet(&udp, (uint8_t *)dhcp, len, from, to);
-               r = send_raw_packet(state->interface, ETHERTYPE_IP, udp, len);
-               free(udp);
-               if (r == -1)
-                       logger(LOG_ERR, "send_raw_packet: %s", strerror(errno));
-       }
-       free(dhcp);
-       /* Failed to send the packet? Return to the init state */
-       if (r == -1) {
-               state->state = STATE_INIT;
-               timerclear(&state->timeout);
-               timerclear(&state->stop);
-               do_socket(state, SOCKET_CLOSED);
-       }
-       return r;
-}
-
-static void
-drop_config(struct if_state *state, const char *reason,
-           const struct options *options)
-{
-       if (state->new || strcmp(reason, "FAIL") == 0) {
-               configure(state->interface, reason, NULL, state->new,
-                         &state->lease, options, 0);
-               free(state->old);
-               state->old = NULL;
-               free(state->new);
-               state->new = NULL;
-       }
-       state->lease.addr.s_addr = 0;
-}
-
-static void
-reduce_timers(struct if_state *state, const struct timeval *tv)
-{
-       if (timerisset(&state->exit)) {
-               timersub(&state->exit, tv, &state->exit);
-               if (!timerisset(&state->exit))
-                       state->exit.tv_sec = -1;
-       }
-       if (timerisset(&state->stop)) {
-               timersub(&state->stop, tv, &state->stop);
-               if (!timerisset(&state->stop))
-                       state->stop.tv_sec = -1;
-       }
-       if (timerisset(&state->timeout)) {
-               timersub(&state->timeout, tv, &state->timeout);
-               if (!timerisset(&state->timeout))
-                       state->timeout.tv_sec = -1;
-       }
-}
-
-static struct timeval *
-get_lowest_timer(struct if_state *state)
-{
-       struct timeval *ref = NULL;
-       
-       if (timerisset(&state->exit))
-               ref = &state->exit;
-       if (timerisset(&state->stop)) {
-               if (!ref || timercmp(&state->stop, ref, <))
-                       ref = &state->stop;
-       }
-       if (timerisset(&state->timeout)) {
-               if (!ref || timercmp(&state->timeout, ref, <))
-                       ref = &state->timeout;
-       }
-       return ref;
-}
-
-static int
-wait_for_fd(struct if_state *state, int *fd)
-{
-       struct pollfd fds[4]; /* signal, link, raw, arp */
-       struct interface *iface = state->interface;
-       int i, r, nfds = 0, msecs = -1;
-       struct timeval start, stop, diff, *ref;
-       static int lastinf = 0;
-
-       /* Ensure that we haven't already timed out */
-       ref = get_lowest_timer(state);
-       if (ref && timerneg(ref))
-               return 0;
-
-       /* We always listen to signals */
-       fds[nfds].fd = state->signal_fd;
-       fds[nfds].events = POLLIN;
-       nfds++;
-       /* And links */
-       if (iface->link_fd != -1) {
-               fds[nfds].fd = iface->link_fd;
-               fds[nfds].events = POLLIN;
-               nfds++;
-       }
-
-       if (state->lease.leasetime == ~0U &&
-           state->state == STATE_BOUND)
-       {
-               if (!lastinf) {
-                       logger(LOG_DEBUG, "waiting for infinity");
-                       lastinf = 1;
-               }
-               ref = NULL;
-       } else if (state->carrier == LINK_DOWN && !ref) {
-               if (!lastinf) {
-                       logger(LOG_DEBUG, "waiting for carrier");
-                       lastinf = 1;
-               }
-               if (timerisset(&state->exit))
-                       ref = &state->exit;
-               else
-                       ref = NULL;
-       } else {
-               if (iface->raw_fd != -1) {
-                       fds[nfds].fd = iface->raw_fd;
-                       fds[nfds].events = POLLIN;
-                       nfds++;
-               }
-               if (iface->arp_fd != -1) {
-                       fds[nfds].fd = iface->arp_fd;
-                       fds[nfds].events = POLLIN;
-                       nfds++;
-               }
-       }
-
-       /* Wait and then reduce the timers.
-        * If we reduce a timer to zero, set it negative to indicate timeout.
-        * We cannot reliably use select as there is no guarantee we will
-        * actually wait the whole time if greater than 31 days according
-        * to POSIX. So we loop on poll if needed as it's limitation of
-        * INT_MAX milliseconds is known. */
-       for (;;) {
-               get_monotonic(&start);
-               if (ref) {
-                       lastinf = 0;
-                       if (ref->tv_sec > INT_MAX / 1000 ||
-                           (ref->tv_sec == INT_MAX / 1000 &&
-                            (ref->tv_usec + 999) / 1000 > INT_MAX % 1000))
-                               msecs = INT_MAX;
-                       else
-                               msecs = ref->tv_sec * 1000 +
-                                       (ref->tv_usec + 999) / 1000;
-               } else
-                       msecs = -1;
-               r = poll(fds, nfds, msecs);
-               get_monotonic(&stop);
-               timersub(&stop, &start, &diff);
-               reduce_timers(state, &diff);
-               if (r == -1) {
-                       if (errno != EINTR)
-                               logger(LOG_ERR, "poll: %s", strerror(errno));
-                       return -1;
-               }
-               if (r)
-                       break;
-               /* We should not have an infinite timeout if we get here */
-               if (timerneg(ref))
-                       return 0;
-       }
-
-       /* We configured our array in the order we should deal with them */
-       for (i = 0; i < nfds; i++)
-               if (fds[i].revents & POLLIN) {
-                       *fd = fds[i].fd;
-                       return r;
-               }
-       return r;
-}
-
-static int
-handle_signal(int sig, struct if_state *state,  const struct options *options)
-{
-       struct dhcp_lease *lease = &state->lease;
-
-       switch (sig) {
-       case SIGINT:
-               logger(LOG_INFO, "received SIGINT, stopping");
-               if (!(state->options & DHCPCD_PERSISTENT))
-                       drop_config(state, "STOP", options);
-               return -1;
-       case SIGTERM:
-               logger(LOG_INFO, "received SIGTERM, stopping");
-               if (!(state->options & DHCPCD_PERSISTENT))
-                       drop_config(state, "STOP", options);
-               return -1;
-       case SIGALRM:
-               logger(LOG_INFO, "received SIGALRM, renewing lease");
-               do_socket(state, SOCKET_CLOSED);
-               state->state = STATE_RENEW_REQUESTED;
-               timerclear(&state->timeout);
-               timerclear(&state->stop);
-               return 1;
-       case SIGHUP:
-               logger(LOG_INFO, "received SIGHUP, releasing lease");
-               if (lease->addr.s_addr &&
-                   !IN_LINKLOCAL(ntohl(lease->addr.s_addr)))
-               {
-                       do_socket(state, SOCKET_OPEN);
-                       state->xid = arc4random();
-                       send_message(state, DHCP_RELEASE, options);
-                       do_socket(state, SOCKET_CLOSED);
-               }
-               drop_config(state, "RELEASE", options);
-               return -1;
-       default:
-               logger (LOG_ERR,
-                       "received signal %d, but don't know what to do with it",
-                       sig);
-       }
-
-       return 0;
-}
-
-static int bind_dhcp(struct if_state *state, const struct options *options)
-{
-       struct interface *iface = state->interface;
-       struct dhcp_lease *lease = &state->lease;
-       const char *reason = NULL;
-       struct timeval start, stop, diff;
-       int retval;
-
-       free(state->old);
-       state->old = state->new;
-       state->new = state->offer;
-       state->offer = NULL;
-       state->messages = 0;
-       state->conflicts = 0;
-       state->defend = 0;
-       timerclear(&state->exit);
-       if (clock_monotonic)
-               get_monotonic(&lease->boundtime);
-
-       if (options->options & DHCPCD_INFORM) {
-               if (options->request_address.s_addr != 0)
-                       lease->addr.s_addr = options->request_address.s_addr;
-               else
-                       lease->addr.s_addr = iface->addr.s_addr;
-               logger(LOG_INFO, "received approval for %s",
-                      inet_ntoa(lease->addr));
-               state->state = STATE_BOUND;
-               state->lease.leasetime = ~0U;
-               timerclear(&state->stop);
-               reason = "INFORM";
-       } else if (IN_LINKLOCAL(htonl(state->new->yiaddr))) {
-               get_lease(lease, state->new);
-               logger(LOG_INFO, "using IPv4LL address %s",
-                      inet_ntoa(lease->addr));
-               state->state = STATE_INIT;
-               timerclear(&state->timeout);
-               reason = "IPV4LL";
-       } else {
-               if (gettimeofday(&start, NULL) == 0)
-                       lease->leasedfrom = start.tv_sec;
-
-               get_lease(lease, state->new);
-               if (lease->frominfo)
-                       reason = "TIMEOUT";
-
-               if (lease->leasetime == ~0U) {
-                       lease->renewaltime = lease->rebindtime = lease->leasetime;
-                       logger(LOG_INFO, "leased %s for infinity",
-                              inet_ntoa(lease->addr));
-                       state->state = STATE_BOUND;
-                       timerclear(&state->stop);
-               } else {
-                       if (lease->rebindtime >= lease->leasetime) {
-                               lease->rebindtime = lease->leasetime * T2;
-                               logger(LOG_ERR,
-                                      "rebind time greater than lease "
-                                      "time, forcing to %u seconds",
-                                      lease->rebindtime);
-                       }
-                       if (lease->renewaltime > lease->rebindtime) {
-                               lease->renewaltime = lease->leasetime * T1;
-                               logger(LOG_ERR,
-                                      "renewal time greater than rebind time, "
-                                      "forcing to %u seconds",
-                                      lease->renewaltime);
-                       }
-                       if (!lease->renewaltime)
-                               lease->renewaltime = lease->leasetime * T1;
-                       if (!lease->rebindtime)
-                               lease->rebindtime = lease->leasetime * T2;
-                       logger(LOG_INFO,
-                              "leased %s for %u seconds",
-                              inet_ntoa(lease->addr), lease->leasetime);
-                       state->stop.tv_sec = lease->renewaltime;
-                       state->stop.tv_usec = 0;
-               }
-               state->state = STATE_BOUND;
-       }
-
-       state->xid = 0;
-       timerclear(&state->timeout);
-       if (!reason) {
-               if (state->old) {
-                       if (state->old->yiaddr == state->new->yiaddr &&
-                           lease->server.s_addr)
-                               reason = "RENEW";
-                       else
-                               reason = "REBIND";
-               } else
-                       reason = "BOUND";
-       }
-       /* If we have a monotonic clock we can safely substract the
-        * script execution time from our timers.
-        * Otherwise we can't as the script may update the real time. */
-       if (clock_monotonic)
-               get_monotonic(&start);
-       retval = configure(iface, reason, state->new, state->old,
-                          &state->lease, options, 1);
-       if (clock_monotonic) {
-               get_monotonic(&stop);
-               timersub(&stop, &start, &diff);
-               reduce_timers(state, &diff);
-       }
-       if (retval != 0)
-               return -1;
-       return daemonise(state, options);
-}
-
-static int
-handle_timeout_fail(struct if_state *state, const struct options *options)
-{
-       struct dhcp_lease *lease = &state->lease;
-       struct interface *iface = state->interface;
-       int gotlease = -1;
-       const char *reason = NULL;
-
-       timerclear(&state->stop);
-       timerclear(&state->exit);
-       if (state->state != STATE_DISCOVERING)
-               state->messages = 0;
-
-       switch (state->state) {
-       case STATE_INIT:        /* FALLTHROUGH */
-       case STATE_DISCOVERING: /* FALLTHROUGH */
-       case STATE_REQUESTING:
-               if (IN_LINKLOCAL(ntohl(iface->addr.s_addr))) {
-                       if (!(state->options & DHCPCD_DAEMONISED))
-                               logger(LOG_ERR, "timed out");
-               } else {
-                       if (iface->addr.s_addr != 0 &&
-                           !(state->options & DHCPCD_INFORM))
-                               logger(LOG_ERR, "lost lease");
-                       else if (state->carrier != LINK_DOWN || 
-                               !(state->options & DHCPCD_DAEMONISED)) 
-                               logger(LOG_ERR, "timed out");
-               }
-               do_socket(state, SOCKET_CLOSED);
-               if (state->options & DHCPCD_INFORM ||
-                   state->options & DHCPCD_TEST)
-                       return -1;
-
-               if (state->carrier != LINK_DOWN &&
-                   (state->options & DHCPCD_IPV4LL ||
-                    state->options & DHCPCD_LASTLEASE))
-                       gotlease = get_old_lease(state);
-
-               if (state->carrier != LINK_DOWN &&
-                   state->options & DHCPCD_IPV4LL &&
-                   gotlease != 0)
-               {
-                       logger(LOG_INFO, "probing for an IPV4LL address");
-                       free(state->offer);
-                       state->offer = ipv4ll_get_dhcp(0);
-                       gotlease = 0;
-               }
-
-               if (gotlease == 0 &&
-                   state->offer->yiaddr != iface->addr.s_addr)
-               {
-                       state->state = STATE_PROBING;
-                       state->claims = 0;
-                       state->probes = 0;
-                       if (iface->addr.s_addr)
-                               state->conflicts = 0;
-                       return 1;
-               }
-
-               if (gotlease == 0)
-                       return bind_dhcp(state, options);
-
-               if (iface->addr.s_addr)
-                       reason = "EXPIRE";
-               else
-                       reason = "FAIL";
-               drop_config(state, reason, options);
-               if (!(state->options & DHCPCD_DAEMONISED) &&
-                   (state->options & DHCPCD_DAEMONISE))
-                       return -1;
-               state->state = STATE_RENEW_REQUESTED;
-               return 1;
-       case STATE_BOUND:
-               logger(LOG_INFO, "renewing lease of %s",inet_ntoa(lease->addr));
-               if (state->carrier != LINK_DOWN)
-                       do_socket(state, SOCKET_OPEN);
-               state->xid = arc4random();
-               state->state = STATE_RENEWING;
-               state->stop.tv_sec = lease->rebindtime - lease->renewaltime;
-               break;
-       case STATE_RENEWING:
-               logger(LOG_ERR, "failed to renew, attempting to rebind");
-               state->state = STATE_REBINDING;
-               if (lease->server.s_addr == 0)
-                       state->stop.tv_sec = options->timeout;
-               else
-                       state->stop.tv_sec = lease->rebindtime - \
-                                            lease->renewaltime;
-               lease->server.s_addr = 0;
-               break;
-       case STATE_REBINDING:
-               logger(LOG_ERR, "failed to rebind");
-               reason = "EXPIRE";
-               drop_config(state, reason, options);
-               state->state = STATE_INIT;
-               break;
-       case STATE_PROBING:    /* FALLTHROUGH */
-       case STATE_ANNOUNCING:
-               /* We should have lost carrier here and exit timer went */
-               logger(LOG_ERR, "timed out");
-               return -1;
-       default:
-               logger(LOG_DEBUG, "handle_timeout_failed: invalid state %d",
-                      state->state);
-       }
-
-       /* This effectively falls through into the handle_timeout funtion */
-       return 1;
-}
-
-static int
-handle_timeout(struct if_state *state, const struct options *options)
-{
-       struct dhcp_lease *lease = &state->lease;
-       struct interface *iface = state->interface;
-       int i = 0;
-       struct in_addr addr;
-       struct timeval tv;
-
-       timerclear(&state->timeout);
-       if (timerneg(&state->exit))
-               return handle_timeout_fail(state, options);
-
-       if (state->state == STATE_RENEW_REQUESTED &&
-           IN_LINKLOCAL(ntohl(lease->addr.s_addr)))
-       {
-               state->state = STATE_PROBING;
-               free(state->offer);
-               state->offer = read_lease(state->interface);
-               state->probes = 0;
-               state->claims = 0;
-       }
-       switch (state->state) {
-       case STATE_INIT_IPV4LL:
-               state->state = STATE_PROBING;
-               free(state->offer);
-               state->offer = ipv4ll_get_dhcp(0);
-               state->probes = 0;
-               state->claims = 0;
-               /* FALLTHROUGH */
-       case STATE_PROBING:
-               if (iface->arp_fd == -1)
-                       open_socket(iface, ETHERTYPE_ARP);
-               if (state->probes < PROBE_NUM) {
-                       if (state->probes == 0) {
-                               addr.s_addr = state->offer->yiaddr;
-                               logger(LOG_INFO, "checking %s is available"
-                                      " on attached networks",
-                                      inet_ntoa(addr));
-                       }
-                       state->probes++;
-                       if (state->probes < PROBE_NUM) {
-                               state->timeout.tv_sec = PROBE_MIN;
-                               state->timeout.tv_usec = arc4random() %
-                                       (PROBE_MAX_U - PROBE_MIN_U);
-                               timernorm(&state->timeout);
-                       } else {
-                               state->timeout.tv_sec = ANNOUNCE_WAIT;
-                               state->timeout.tv_usec = 0;
-                       }
-                       logger(LOG_DEBUG,
-                              "sending ARP probe (%d of %d), next in %0.2f seconds",
-                              state->probes, PROBE_NUM,
-                              timeval_to_double(&state->timeout));
-                       if (send_arp(iface, ARPOP_REQUEST, 0,
-                                    state->offer->yiaddr) == -1)
-                       {
-                               logger(LOG_ERR, "send_arp: %s", strerror(errno));
-                               return -1;
-                       }
-                       return 0;
-               } else {
-                       /* We've waited for ANNOUNCE_WAIT after the final probe
-                        * so the address is now ours */
-                       if (IN_LINKLOCAL(htonl(state->offer->yiaddr))) {
-                               i = bind_dhcp(state, options);
-                               state->state = STATE_ANNOUNCING;
-                               state->timeout.tv_sec = ANNOUNCE_INTERVAL;
-                               state->timeout.tv_usec = 0;
-                               return i;
-                       }
-                       state->state = STATE_REQUESTING;
-               }
-               break;
-       case STATE_ANNOUNCING:
-               if (iface->arp_fd == -1)
-                       open_socket(iface, ETHERTYPE_ARP);
-               if (state->claims < ANNOUNCE_NUM) {
-                       state->claims++;
-                       if (state->claims < ANNOUNCE_NUM) {
-                               state->timeout.tv_sec = ANNOUNCE_INTERVAL;
-                               state->timeout.tv_usec = 0;
-                               logger(LOG_DEBUG,
-                                      "sending ARP announce (%d of %d),"
-                                      " next in %0.2f seconds",
-                                      state->claims, ANNOUNCE_NUM,
-                                      timeval_to_double(&state->timeout));
-                       } else
-                               logger(LOG_DEBUG,
-                                      "sending ARP announce (%d of %d)",
-                                      state->claims, ANNOUNCE_NUM);
-                       i = send_arp(iface, ARPOP_REQUEST,
-                                    state->new->yiaddr, state->new->yiaddr);
-                       if (i == -1) {
-                               logger(LOG_ERR, "send_arp: %s", strerror(errno));
-                               return -1;
-                       }
-               }
-               if (state->claims < ANNOUNCE_NUM)
-                       return 0;
-               if (IN_LINKLOCAL(htonl(state->new->yiaddr))) {
-                       /* We should pretend to be at the end
-                        * of the DHCP negotation cycle */
-                       state->state = STATE_INIT;
-                       state->messages = DHCP_MAX / DHCP_BASE;
-                       state->probes = 0;
-                       state->claims = 0;
-                       timerclear(&state->stop);
-                       goto dhcp_timeout;
-               } else {
-                       state->state = STATE_BOUND;
-                       close(iface->arp_fd);
-                       iface->arp_fd = -1;
-                       if (lease->leasetime != ~0U) {
-                               state->stop.tv_sec = lease->renewaltime;
-                               state->stop.tv_usec = 0;
-                               if (clock_monotonic) {
-                                       get_monotonic(&tv);
-                                       timersub(&tv, &lease->boundtime, &tv);
-                                       timersub(&state->stop, &tv, &state->stop);
-                               } else {
-                                       state->stop.tv_sec -=
-                                               (ANNOUNCE_INTERVAL * ANNOUNCE_NUM);
-                               }
-                               logger(LOG_DEBUG, "renew in %ld seconds",
-                                      (long int)state->stop.tv_sec);
-                       }
-               }
-               return 0;
-       }
-
-       if (timerneg(&state->stop))
-               return handle_timeout_fail(state, options);
-
-       switch (state->state) {
-       case STATE_BOUND: /* FALLTHROUGH */
-       case STATE_RENEW_REQUESTED:
-               timerclear(&state->stop);
-               /* FALLTHROUGH */
-       case STATE_INIT:
-               do_socket(state, SOCKET_OPEN);
-               state->xid = arc4random();
-               iface->start_uptime = uptime();
-               break;
-       }
-
-       switch(state->state) {
-       case STATE_RENEW_REQUESTED:
-               /* If a renew was requested (ie, didn't timeout) we actually
-                * enter the REBIND state so that we broadcast to all servers.
-                * We need to do this for when we change networks. */
-               lease->server.s_addr = 0;
-               state->messages = 0;
-               if (lease->addr.s_addr && !(state->options & DHCPCD_INFORM)) {
-                       logger(LOG_INFO, "rebinding lease of %s",
-                              inet_ntoa(lease->addr));
-                       state->state = STATE_REBINDING;
-                       state->stop.tv_sec = options->timeout;
-                       state->stop.tv_usec = 0;
-                       break;
-               }
-               /* FALLTHROUGH */
-       case STATE_INIT:
-               if (state->carrier == LINK_DOWN)
-                       return 0;
-               if (lease->addr.s_addr == 0 ||
-                   IN_LINKLOCAL(ntohl(iface->addr.s_addr)))
-               {
-                       logger(LOG_INFO, "broadcasting for a lease");
-                       state->state = STATE_DISCOVERING;
-               } else if (state->options & DHCPCD_INFORM) {
-                       logger(LOG_INFO, "broadcasting inform for %s",
-                              inet_ntoa(lease->addr));
-                       state->state = STATE_REQUESTING;
-               } else {
-                       logger(LOG_INFO, "broadcasting for a lease of %s",
-                              inet_ntoa(lease->addr));
-                       state->state = STATE_REQUESTING;
-               }
-               if (!lease->addr.s_addr && !timerisset(&state->stop)) {
-                       state->stop.tv_sec = DHCP_MAX + DHCP_RAND_MIN;
-                       state->stop.tv_usec = arc4random() % (DHCP_RAND_MAX_U - DHCP_RAND_MIN_U);
-                       timernorm(&state->stop);
-               }
-               break;
-       }
-
-dhcp_timeout:
-       if (state->carrier == LINK_DOWN) {
-               timerclear(&state->timeout);
-               return 0;
-       }
-       state->timeout.tv_sec = DHCP_BASE;
-       for (i = 0; i < state->messages; i++) {
-               state->timeout.tv_sec *= 2;
-               if (state->timeout.tv_sec > DHCP_MAX) {
-                       state->timeout.tv_sec = DHCP_MAX;
-                       break;
-               }
-       }
-       state->timeout.tv_sec += DHCP_RAND_MIN;
-       state->timeout.tv_usec = arc4random() %
-               (DHCP_RAND_MAX_U - DHCP_RAND_MIN_U);
-       timernorm(&state->timeout);
-
-       /* We send the message here so that the timeout is reported */
-       switch (state->state) {
-       case STATE_DISCOVERING:
-               send_message(state, DHCP_DISCOVER, options);
-               break;
-       case STATE_REQUESTING:
-               if (state->options & DHCPCD_INFORM) {
-                       send_message(state, DHCP_INFORM, options);
-                       break;
-               }
-               /* FALLTHROUGH */
-       case STATE_RENEWING:   /* FALLTHROUGH */
-       case STATE_REBINDING:
-               if (iface->raw_fd == -1)
-                       do_socket(state, SOCKET_OPEN);
-               send_message(state, DHCP_REQUEST, options);
-               break;
-       }
-
-       return 0;
-}
-
-static void
-log_dhcp(int lvl, const char *msg, const struct dhcp_message *dhcp)
-{
-       char *a;
-       struct in_addr addr;
-       int r;
-
-       if (strcmp(msg, "NAK:") == 0)
-               a = get_option_string(dhcp, DHO_MESSAGE);
-       else {
-               addr.s_addr = dhcp->yiaddr;
-               a = xstrdup(inet_ntoa(addr));
-       }
-       r = get_option_addr(&addr.s_addr, dhcp, DHO_SERVERID);
-       if (dhcp->servername[0] && r == 0)
-               logger(lvl, "%s %s from %s `%s'", msg, a,
-                      inet_ntoa(addr), dhcp->servername);
-       else if (r == 0)
-               logger(lvl, "%s %s from %s", msg, a, inet_ntoa(addr));
-       else
-               logger(lvl, "%s %s", msg, a);
-       free(a);
-}
-
-static int
-handle_dhcp(struct if_state *state, struct dhcp_message **dhcpp,
-           const struct options *options)
-{
-       struct dhcp_message *dhcp = *dhcpp;
-       struct interface *iface = state->interface;
-       struct dhcp_lease *lease = &state->lease;
-       uint8_t type, tmp;
-       struct in_addr addr;
-       size_t i;
-       int r;
-
-       /* reset the message counter */
-       state->messages = 0;
-
-       /* We have to have DHCP type to work */
-       if (get_option_uint8(&type, dhcp, DHO_MESSAGETYPE) == -1) {
-               log_dhcp(LOG_ERR, "no DHCP type in", dhcp);
-               return 0;
-       }
-
-       /* Ensure that it's not from a blacklisted server.
-        * We should expand this to check IP and/or hardware address
-        * at the packet level. */
-       if (options->blacklist_len != 0 &&
-           get_option_addr(&addr.s_addr, dhcp, DHO_SERVERID) == 0)
-       {
-               for (i = 0; i < options->blacklist_len; i++) {
-                       if (options->blacklist[i] != addr.s_addr)
-                               continue;
-                       if (dhcp->servername[0])
-                               logger(LOG_WARNING,
-                                      "ignoring blacklisted server %s `%s'",
-                                       inet_ntoa(addr), dhcp->servername);
-                       else
-                               logger(LOG_WARNING,
-                                      "ignoring blacklisted server %s",
-                                      inet_ntoa(addr));
-                       return 0;
-               }
-       }
-
-       /* We should restart on a NAK */
-       if (type == DHCP_NAK) {
-               log_dhcp(LOG_WARNING, "NAK:", dhcp);
-               drop_config(state, "EXPIRE", options);
-               do_socket(state, SOCKET_CLOSED);
-               state->state = STATE_INIT;
-               /* If we constantly get NAKS then we should slowly back off */
-               if (state->nakoff == 0) {
-                       state->nakoff = 1;
-                       timerclear(&state->timeout);
-               } else {
-                       state->timeout.tv_sec = state->nakoff;
-                       state->timeout.tv_usec = 0;
-                       state->nakoff *= 2;
-                       if (state->nakoff > NAKOFF_MAX)
-                               state->nakoff = NAKOFF_MAX;
-               } 
-               return 0;
-       }
-
-       /* No NAK, so reset the backoff */
-       state->nakoff = 1;
-
-       /* Ensure that all required options are present */
-       for (i = 1; i < 255; i++) {
-               if (has_option_mask(options->requiremask, i) &&
-                   get_option_uint8(&tmp, dhcp, i) != 0)
-               {
-                       log_dhcp(LOG_WARNING, "reject", dhcp);
-                       return 0;
-               }
-       }
-
-       if (type == DHCP_OFFER && state->state == STATE_DISCOVERING) {
-               lease->addr.s_addr = dhcp->yiaddr;
-               get_option_addr(&lease->server.s_addr, dhcp, DHO_SERVERID);
-               log_dhcp(LOG_INFO, "offered", dhcp);
-               if (state->options & DHCPCD_TEST) {
-                       run_script(options, iface->name, "TEST", dhcp, NULL);
-                       /* Fake the fact we forked so we return 0 to userland */
-                       state->options |= DHCPCD_FORKED;
-                       return -1;
-               }
-               free(state->offer);
-               state->offer = dhcp;
-               *dhcpp = NULL;
-               timerclear(&state->timeout);
-               if (state->options & DHCPCD_ARP &&
-                   iface->addr.s_addr != state->offer->yiaddr)
-               {
-                       /* If the interface already has the address configured
-                        * then we can't ARP for duplicate detection. */
-                       addr.s_addr = state->offer->yiaddr;
-                       if (!has_address(iface->name, &addr, NULL)) {
-                               state->state = STATE_PROBING;
-                               state->claims = 0;
-                               state->probes = 0;
-                               state->conflicts = 0;
-                               timerclear(&state->stop);
-                               return 1;
-                       }
-               }
-               state->state = STATE_REQUESTING;
-               return 1;
-       }
-
-       if (type == DHCP_OFFER) {
-               log_dhcp(LOG_INFO, "ignoring offer of", dhcp);
-               return 0;
-       }
-
-       /* We should only be dealing with acks */
-       if (type != DHCP_ACK) {
-               log_dhcp(LOG_ERR, "not ACK or OFFER", dhcp);
-               return 0;
-       }
-           
-       switch (state->state) {
-       case STATE_RENEW_REQUESTED:
-       case STATE_REQUESTING:
-       case STATE_RENEWING:
-       case STATE_REBINDING:
-               if (!(state->options & DHCPCD_INFORM)) {
-                       get_option_addr(&lease->server.s_addr,
-                                       dhcp, DHO_SERVERID);
-                       log_dhcp(LOG_INFO, "acknowledged", dhcp);
-               }
-               free(state->offer);
-               state->offer = dhcp;
-               *dhcpp = NULL;
-               break;
-       default:
-               logger(LOG_ERR, "wrong state %d", state->state);
-       }
-
-       do_socket(state, SOCKET_CLOSED);
-       r = bind_dhcp(state, options);
-       if (!(state->options & DHCPCD_ARP)) {
-               if (!(state->options & DHCPCD_INFORM))
-                       logger(LOG_DEBUG, "renew in %ld seconds",
-                              (long int)state->stop.tv_sec);
-               return r;
-       }
-       state->state = STATE_ANNOUNCING;
-       if (state->options & DHCPCD_FORKED)
-               return r;
-       return 1;
-}
-
-static int
-handle_dhcp_packet(struct if_state *state, const struct options *options)
-{
-       uint8_t *packet;
-       struct interface *iface = state->interface;
-       struct dhcp_message *dhcp = NULL;
-       const uint8_t *pp;
-       uint8_t *p;
-       ssize_t bytes;
-       int retval = -1;
-
-       /* 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. */
-       packet = xmalloc(udp_dhcp_len);
-       for(;;) {
-               bytes = get_raw_packet(iface, ETHERTYPE_IP,
-                                      packet, udp_dhcp_len);
-               if (bytes == 0) {
-                       retval = 0;
-                       break;
-               }
-               if (bytes == -1)
-                       break;
-               if (valid_udp_packet(packet) == -1)
-                       continue;
-               bytes = get_udp_data(&pp, packet);
-               if ((size_t)bytes > sizeof(*dhcp)) {
-                       logger(LOG_ERR, "packet greater than DHCP size");
-                       continue;
-               }
-               if (!dhcp)
-                       dhcp = xmalloc(sizeof(*dhcp));
-               memcpy(dhcp, pp, bytes);
-               if (dhcp->cookie != htonl(MAGIC_COOKIE)) {
-                       logger(LOG_DEBUG, "bogus cookie, ignoring");
-                       continue;
-               }
-               /* Ensure it's the right transaction */
-               if (state->xid != dhcp->xid) {
-                       logger(LOG_DEBUG,
-                              "ignoring packet with xid 0x%x as"
-                              " it's not ours (0x%x)",
-                              dhcp->xid, state->xid);
-                       continue;
-               }
-               /* Ensure packet is for us */
-               if (iface->hwlen <= sizeof(dhcp->chaddr) &&
-                   memcmp(dhcp->chaddr, iface->hwaddr, iface->hwlen))
-               {
-                       logger(LOG_DEBUG, "xid 0x%x is not for our hwaddr %s",
-                              dhcp->xid,
-                              hwaddr_ntoa(dhcp->chaddr, sizeof(dhcp->chaddr)));
-                       continue;
-               }
-               /* We should ensure that the packet is terminated correctly
-                * if we have space for the terminator */
-               if ((size_t)bytes < sizeof(struct dhcp_message)) {
-                       p = (uint8_t *)dhcp + bytes - 1;
-                       while (p > dhcp->options && *p == DHO_PAD)
-                               p--;
-                       if (*p != DHO_END)
-                               *++p = DHO_END;
-               }
-               retval = handle_dhcp(state, &dhcp, options);
-               if (retval == 0 && state->options & DHCPCD_TEST)
-                       state->options |= DHCPCD_FORKED;
-               break;
-       }
-
-       free(packet);
-       free(dhcp);
-       return retval;
-}
-
-static int
-handle_arp_packet(struct if_state *state)
-{
-       struct arphdr reply;
-       uint32_t reply_s;
-       uint32_t reply_t;
-       uint8_t arp_reply[sizeof(reply) + 2 * sizeof(reply_s) + 2 * HWADDR_LEN];
-       uint8_t *hw_s, *hw_t;
-       ssize_t bytes;
-       struct interface *iface = state->interface;
-
-       state->fail.s_addr = 0;
-       for(;;) {
-               bytes = get_raw_packet(iface, ETHERTYPE_ARP,
-                                      arp_reply, sizeof(arp_reply));
-               if (bytes == 0 || bytes == -1)
-                       return (int)bytes;
-               /* We must have a full ARP header */
-               if ((size_t)bytes < sizeof(reply))
-                       continue;
-               memcpy(&reply, arp_reply, sizeof(reply));
-               /* Protocol must be IP. */
-               if (reply.ar_pro != htons(ETHERTYPE_IP))
-                       continue;
-               if (reply.ar_pln != sizeof(reply_s))
-                       continue;
-               /* Only these types are recognised */
-               if (reply.ar_op != htons(ARPOP_REPLY) &&
-                   reply.ar_op != htons(ARPOP_REQUEST))
-                       continue;
-
-               /* Get pointers to the hardware addreses */
-               hw_s = arp_reply + sizeof(reply);
-               hw_t = hw_s + reply.ar_hln + reply.ar_pln;
-               /* Ensure we got all the data */
-               if ((hw_t + reply.ar_hln + reply.ar_pln) - arp_reply > bytes)
-                       continue;
-               /* Ignore messages from ourself */
-               if (reply.ar_hln == iface->hwlen &&
-                   memcmp(hw_s, iface->hwaddr, iface->hwlen) == 0)
-                       continue;
-               /* Copy out the IP addresses */
-               memcpy(&reply_s, hw_s + reply.ar_hln, reply.ar_pln);
-               memcpy(&reply_t, hw_t + reply.ar_hln, reply.ar_pln);
-
-               /* Check for conflict */
-               if (state->offer && 
-                   (reply_s == state->offer->yiaddr ||
-                    (reply_s == 0 && reply_t == state->offer->yiaddr)))
-                       state->fail.s_addr = state->offer->yiaddr;
-
-               /* Handle IPv4LL conflicts */
-               if (IN_LINKLOCAL(htonl(iface->addr.s_addr)) &&
-                   (reply_s == iface->addr.s_addr ||
-                    (reply_s == 0 && reply_t == iface->addr.s_addr)))
-                       state->fail.s_addr = iface->addr.s_addr;
-
-               if (state->fail.s_addr) {
-                       logger(LOG_ERR, "hardware address %s claims %s",
-                              hwaddr_ntoa((unsigned char *)hw_s,
-                                          (size_t)reply.ar_hln),
-                              inet_ntoa(state->fail));
-                       errno = EEXIST;
-                       return -1;
-               }
-       }
-}
-
-static int
-handle_arp_fail(struct if_state *state, const struct options *options)
-{
-       time_t up;
-       int cookie = state->offer->cookie;
-
-       if (!IN_LINKLOCAL(htonl(state->fail.s_addr))) {
-               state->state = STATE_INIT;
-               free(state->offer);
-               state->offer = NULL;
-               state->lease.addr.s_addr = 0;
-               if (!cookie)
-                       return 1;
-               state->timeout.tv_sec = DHCP_ARP_FAIL;
-               state->timeout.tv_usec = 0;
-               do_socket(state, SOCKET_OPEN);
-               send_message(state, DHCP_DECLINE, options);
-               do_socket(state, SOCKET_CLOSED);
-               return 0;
-       }
-
-       if (state->fail.s_addr == state->interface->addr.s_addr) {
-               if (state->state == STATE_PROBING)
-                       /* This should only happen when SIGALRM or
-                        * link when down/up and we have a conflict. */
-                       drop_config(state, "EXPIRE", options);
-               else {
-                       up = uptime();
-                       if (state->defend + DEFEND_INTERVAL > up) {
-                               drop_config(state, "EXPIRE", options);
-                               state->conflicts = -1;
-                               /* drop through to set conflicts to 0 */
-                       } else {
-                               state->defend = up;
-                               return 0;
-                       }
-               }
-       }
-       do_socket(state, SOCKET_CLOSED);
-       state->conflicts++;
-       timerclear(&state->stop);
-       if (state->conflicts > MAX_CONFLICTS) {
-               logger(LOG_ERR, "failed to obtain an IPv4LL address");
-               state->state = STATE_INIT;
-               timerclear(&state->timeout);
-               if (!(state->options & DHCPCD_DAEMONISED) &&
-                   (state->options & DHCPCD_DAEMONISE))
-                       return -1;
-               return 1;
-       }
-       state->state = STATE_INIT_IPV4LL;
-       state->timeout.tv_sec = PROBE_WAIT;
-       state->timeout.tv_usec = 0;
-       return 0;
-}
-
-static int
-handle_link(struct if_state *state)
-{
-       int retval;
-
-       retval = link_changed(state->interface);
-       if (retval == -1) {
-               logger(LOG_ERR, "link_changed: %s", strerror(errno));
-               return -1;
-       }
-       if (retval == 0)
-               return 0;
-
-       timerclear(&state->timeout);
-       switch (carrier_status(state->interface->name)) {
-       case -1:
-               logger(LOG_ERR, "carrier_status: %s", strerror(errno));
-               return -1;
-       case 0:
-               if (state->carrier != LINK_DOWN) {
-                       logger(LOG_INFO, "carrier lost");
-                       state->carrier = LINK_DOWN;
-                       do_socket(state, SOCKET_CLOSED);
-                       if (state->state != STATE_BOUND)
-                               timerclear(&state->stop);
-               }
-               break;
-       default:
-               if (state->carrier != LINK_UP) {
-                       logger(LOG_INFO, "carrier acquired");
-                       state->state = STATE_RENEW_REQUESTED;
-                       state->carrier = LINK_UP;
-                       timerclear(&state->stop);
-                       return 1;
-               }
-               break;
-       }
-       return 0;
-}
-
-int
-dhcp_run(const struct options *options, int *pid_fd)
-{
-       struct interface *iface;
-       struct if_state *state = NULL;
-       int fd = -1, r = 0, sig;
-
-       iface = read_interface(options->interface, options->metric);
-       if (!iface) {
-               logger(LOG_ERR, "read_interface: %s", strerror(errno));
-               goto eexit;
-       }
-       logger(LOG_DEBUG, "hardware address = %s",
-              hwaddr_ntoa(iface->hwaddr, iface->hwlen));
-       state = xzalloc(sizeof(*state));
-       state->pid_fd = pid_fd;
-       state->interface = iface;
-       if (!(options->options & DHCPCD_TEST))
-               run_script(options, iface->name, "PREINIT", NULL, NULL);
-
-       if (client_setup(state, options) == -1)
-               goto eexit;
-       if (signal_init() == -1)
-               goto eexit;
-       if (signal_setup() == -1)
-               goto eexit;
-       state->signal_fd = signal_fd();
-
-       if (state->options & DHCPCD_BACKGROUND &&
-           !(state->options & DHCPCD_DAEMONISED))
-               if (daemonise(state, options) == -1)
-                       goto eexit;
-
-       if (state->carrier == LINK_DOWN)
-               logger(LOG_INFO, "waiting for carrier");
-
-       for (;;) {
-               if (r == 0)
-                       r = handle_timeout(state, options);
-               else if (r > 0) {
-                       if (fd == state->signal_fd) {
-                               if ((sig = signal_read()) != -1)
-                                       r = handle_signal(sig, state, options);
-                       } else if (fd == iface->link_fd)
-                               r = handle_link(state);
-                       else if (fd == iface->raw_fd)
-                               r = handle_dhcp_packet(state, options);
-                       else if (fd == iface->arp_fd) {
-                               if ((r = handle_arp_packet(state)) == -1)
-                                       r = handle_arp_fail(state, options);
-                       } else
-                               r = 0;
-               }
-               if (r == -1)
-                       break;
-               if (r == 0) {
-                       fd = -1;
-                       r = wait_for_fd(state, &fd);
-                       if (r == -1 && errno == EINTR) {
-                               r = 1;
-                               fd = state->signal_fd;
-                       }
-               } else
-                       r = 0;
-       }
-
-eexit:
-       if (iface) {
-               do_socket(state, SOCKET_CLOSED);
-               if (iface->link_fd != -1)
-                   close(iface->link_fd);
-               free_routes(iface->routes);
-               free(iface->clientid);
-               free(iface->buffer);
-               free(iface);
-       }
-
-       if (state) {
-               if (state->options & DHCPCD_FORKED)
-                       r = 0;
-               if (state->options & DHCPCD_DAEMONISED)
-                       unlink(options->pidfile);
-               free(state->offer);
-               free(state->new);
-               free(state->old);
-               free(state);
-       }
-
-       return r;
-}
index d90c7d2f2eada4120e688a32d9a0e948f08ed7d1..efcdd80a4bd531b97cb8603a260a11bd4567d2d0 100644 (file)
--- a/common.c
+++ b/common.c
@@ -284,7 +284,7 @@ xmalloc(size_t s)
 
        if (value)
                return value;
-       logger(LOG_ERR, "memory exhausted");
+       logger(LOG_ERR, "memory exhausted (xalloc %zu bytes)", s);
        exit (EXIT_FAILURE);
        /* NOTREACHED */
 }
@@ -305,7 +305,7 @@ xrealloc(void *ptr, size_t s)
 
        if (value)
                return (value);
-       logger(LOG_ERR, "memory exhausted");
+       logger(LOG_ERR, "memory exhausted (xrealloc %zu bytes)", s);
        exit(EXIT_FAILURE);
        /* NOTREACHED */
 }
@@ -321,7 +321,7 @@ xstrdup(const char *str)
        if ((value = strdup(str)))
                return value;
 
-       logger(LOG_ERR, "memory exhausted");
+       logger(LOG_ERR, "memory exhausted (xstrdup)");
        exit(EXIT_FAILURE);
        /* NOTREACHED */
 }
index 25226636d3b71c896c2af08d7d480e9de2941fff..a9715d635796360c7efce37453be36ebfcd44e72 100644 (file)
--- a/common.h
+++ b/common.h
 
 #define UNCONST(a)             ((void *)(unsigned long)(const void *)(a))
 
+#define timeval_to_double(tv) ((tv)->tv_sec * 1.0 + (tv)->tv_usec * 1.0e-6)
+#define timernorm(tvp)                                         \
+       do {                                                    \
+               while ((tvp)->tv_usec >= 1000000) {             \
+                       (tvp)->tv_sec++;                        \
+                       (tvp)->tv_usec -= 1000000;              \
+               }                                               \
+       } while (0 /* CONSTCOND */);
+
 #if __GNUC__ > 2 || defined(__INTEL_COMPILER)
 # define _unused __attribute__((__unused__))
 #else
index 922203944c6bfe631955764ef4993e9e5df6b623..801c89cd442bb6c2c2c768e7d75a3bfe4dafba77 100644 (file)
--- a/config.h
+++ b/config.h
@@ -28,7 +28,7 @@
 #define CONFIG_H
 
 #define PACKAGE                        "dhcpcd"
-#define VERSION                        "4.0.0"
+#define VERSION                        "4.0.99"
 
 /*
  * By default we don't add a local link route if we got a routeable address.
@@ -69,7 +69,7 @@
 # define LEASEFILE             DBDIR "/" PACKAGE "-%s.lease"
 #endif
 #ifndef PIDFILE
-# define PIDFILE               RUNDIR "/" PACKAGE "-%s.pid"
+# define PIDFILE               RUNDIR "/" PACKAGE "%s%s.pid"
 #endif
 
 #endif
index dad3bce6d19c5397716c9ee3c92fbf7d577fba7b..6baf4042677c856720673d3c70b6d1afcbcf8041 100644 (file)
 #include "config.h"
 #include "common.h"
 #include "configure.h"
-#include "dhcp.h"
-#include "dhcpcd.h"
+#include "dhcpf.h"
+#include "if-options.h"
 #include "logger.h"
 #include "net.h"
 #include "signals.h"
 
 #define DEFAULT_PATH   "PATH=/usr/bin:/usr/sbin:/bin:/sbin"
 
-
 static int
 exec_script(char *const *argv, char *const *env)
 {
@@ -80,18 +79,18 @@ exec_script(char *const *argv, char *const *env)
 }
 
 int
-run_script(const struct options *options, const char *iface,
-           const char *reason,
-           const struct dhcp_message *dhcpn, const struct dhcp_message *dhcpo)
+run_script(const struct interface *iface, const char *reason)
 {
-       char *const argv[2] = { UNCONST(options->script), NULL };
+       char *const argv[2] = { UNCONST(iface->state->options->script), NULL };
        char **env = NULL, **ep;
        char *path;
        ssize_t e, elen;
        pid_t pid;
        int status = 0;
+       const struct if_options *ifo = iface->state->options;
 
-       logger(LOG_DEBUG, "executing `%s', reason %s", options->script, reason);
+       logger(LOG_DEBUG, "%s: executing `%s', reason %s",
+              iface->name, argv[0], reason);
 
        /* Make our env */
        elen = 5;
@@ -103,9 +102,9 @@ run_script(const struct options *options, const char *iface,
                snprintf(env[0], e, "PATH=%s", path);
        } else
                env[0] = xstrdup(DEFAULT_PATH);
-       e = strlen("interface") + strlen(iface) + 2;
+       e = strlen("interface") + strlen(iface->name) + 2;
        env[1] = xmalloc(e);
-       snprintf(env[1], e, "interface=%s", iface);
+       snprintf(env[1], e, "interface=%s", iface->name);
        e = strlen("reason") + strlen(reason) + 2;
        env[2] = xmalloc(e);
        snprintf(env[2], e, "reason=%s", reason);
@@ -113,30 +112,32 @@ run_script(const struct options *options, const char *iface,
        env[3] = xmalloc(e);
        snprintf(env[3], e, "pid=%d", getpid());
        env[4] = xmalloc(e);
-       snprintf(env[4], e, "metric=%d", options->metric);
-       if (dhcpo) {
-               e = configure_env(NULL, NULL, dhcpo, options);
+       snprintf(env[4], e, "metric=%d", iface->metric);
+       if (iface->state->old) {
+               e = configure_env(NULL, NULL, iface->state->old, ifo);
                if (e > 0) {
                        env = xrealloc(env, sizeof(char *) * (elen + e + 1));
-                       elen += configure_env(env + elen, "old", dhcpo, options);
+                       elen += configure_env(env + elen, "old",
+                                       iface->state->old, ifo);
                }
        }
-       if (dhcpn) {
-               e = configure_env(NULL, NULL, dhcpn, options);
+       if (iface->state->new) {
+               e = configure_env(NULL, NULL, iface->state->new, ifo);
                if (e > 0) {
                        env = xrealloc(env, sizeof(char *) * (elen + e + 1));
-                       elen += configure_env(env + elen, "new", dhcpn, options);
+                       elen += configure_env(env + elen, "new",
+                                       iface->state->new, ifo);
                }
        }
        /* Add our base environment */
-       if (options->environ) {
+       if (ifo->environ) {
                e = 0;
-               while (options->environ[e++])
+               while (ifo->environ[e++])
                        ;
                env = xrealloc(env, sizeof(char *) * (elen + e + 1));
                e = 0;
-               while (options->environ[e]) {
-                       env[elen + e] = xstrdup(options->environ[e]);
+               while (ifo->environ[e]) {
+                       env[elen + e] = xstrdup(ifo->environ[e]);
                        e++;
                }
                elen += e;
@@ -181,13 +182,13 @@ reverse_routes(struct rt *routes)
 }
 
 static int
-delete_route(const char *iface, struct rt *rt, int metric)
+delete_route(const struct interface *iface, struct rt *rt, int metric)
 {
        char *addr;
        int retval;
 
        addr = xstrdup(inet_ntoa(rt->dest));
-       logger(LOG_DEBUG, "deleting route %s/%d via %s",
+       logger(LOG_DEBUG, "%s: deleting route %s/%d via %s", iface->name,
               addr, inet_ntocidr(rt->net), inet_ntoa(rt->gate));
        free(addr);
        retval = del_route(iface, &rt->dest, &rt->net, &rt->gate, metric);
@@ -206,7 +207,7 @@ delete_routes(struct interface *iface, int metric)
        rt = reverse_routes(iface->routes);
        while (rt) {
                rtn = rt->next;
-               retval += delete_route(iface->name, rt, metric);
+               retval += delete_route(iface, rt, metric);
                free(rt);
                rt = rtn;
        }
@@ -229,9 +230,9 @@ in_routes(const struct rt *routes, const struct rt *rt)
 }
 
 static int
-configure_routes(struct interface *iface, const struct dhcp_message *dhcp,
-                const struct options *options)
+configure_routes(struct interface *iface, const struct dhcp_message *dhcp)
 {
+       const struct if_options *ifo = iface->state->options;
        struct rt *rt, *ort;
        struct rt *rtn = NULL, *nr = NULL;
        int remember;
@@ -241,7 +242,7 @@ configure_routes(struct interface *iface, const struct dhcp_message *dhcp,
        ort = get_option_routes(dhcp);
 
 #ifdef IPV4LL_ALWAYSROUTE
-       if (options->options & DHCPCD_IPV4LL &&
+       if (ifo->options & DHCPCD_IPV4LL &&
            IN_PRIVATE(ntohl(dhcp->yiaddr)))
        {
                for (rt = ort; rt; rt = rt->next) {
@@ -272,22 +273,22 @@ configure_routes(struct interface *iface, const struct dhcp_message *dhcp,
        iface->routes = reverse_routes(iface->routes);
        for (rt = iface->routes; rt; rt = rt->next)
                if (in_routes(ort, rt) != 0)
-                       delete_route(iface->name, rt, options->metric);
+                       delete_route(iface, rt, iface->metric);
 
        for (rt = ort; rt; rt = rt->next) {
                /* Don't set default routes if not asked to */
                if (rt->dest.s_addr == 0 &&
                    rt->net.s_addr == 0 &&
-                   !(options->options & DHCPCD_GATEWAY))
+                   !(ifo->options & DHCPCD_GATEWAY))
                        continue;
 
                addr = xstrdup(inet_ntoa(rt->dest));
-               logger(LOG_DEBUG, "adding route to %s/%d via %s",
-                       addr, inet_ntocidr(rt->net), inet_ntoa(rt->gate));
+               logger(LOG_DEBUG, "%s: adding route to %s/%d via %s",
+                      iface->name, addr,
+                      inet_ntocidr(rt->net), inet_ntoa(rt->gate));
                free(addr);
-               remember = add_route(iface->name, &rt->dest,
-                                    &rt->net, &rt->gate,
-                                    options->metric);
+               remember = add_route(iface, &rt->dest,
+                                    &rt->net, &rt->gate, iface->metric);
                retval += remember;
 
                /* If we failed to add the route, we may have already added it
@@ -324,10 +325,11 @@ static int
 delete_address(struct interface *iface)
 {
        int retval;
-       logger(LOG_DEBUG, "deleting IP address %s/%d",
+       logger(LOG_DEBUG, "%s: deleting IP address %s/%d",
+              iface->name,
               inet_ntoa(iface->addr),
               inet_ntocidr(iface->net));
-       retval = del_address(iface->name, &iface->addr, &iface->net);
+       retval = del_address(iface, &iface->addr, &iface->net);
        if (retval == -1 && errno != EADDRNOTAVAIL) 
                logger(LOG_ERR, "del_address: %s", strerror(errno));
        iface->addr.s_addr = 0;
@@ -336,11 +338,9 @@ delete_address(struct interface *iface)
 }
 
 int
-configure(struct interface *iface, const char *reason,
-         const struct dhcp_message *dhcp, const struct dhcp_message *old,
-         const struct dhcp_lease *lease, const struct options *options,
-         int up)
+configure(struct interface *iface, const char *reason)
 {
+       struct dhcp_message *dhcp = iface->state->new;
        struct in_addr addr;
        struct in_addr net;
        struct in_addr brd;
@@ -350,37 +350,32 @@ configure(struct interface *iface, const char *reason,
 #endif
 
        /* Grab our IP config */
-       if (dhcp == NULL)
-               up = 0;
-       else {
+       if (dhcp) {
                addr.s_addr = dhcp->yiaddr;
                if (addr.s_addr == 0)
-                       addr.s_addr = lease->addr.s_addr;
+                       addr.s_addr = iface->state->lease.addr.s_addr;
                /* Ensure we have all the needed values */
                if (get_option_addr(&net.s_addr, dhcp, DHO_SUBNETMASK) == -1)
                        net.s_addr = get_netmask(addr.s_addr);
                if (get_option_addr(&brd.s_addr, dhcp, DHO_BROADCAST) == -1)
                        brd.s_addr = addr.s_addr | ~net.s_addr;
-       }
-
-       /* If we aren't up, then reset the interface as much as we can */
-       if (!up) {
+       } else {
                /* Only reset things if we had set them before */
                if (iface->addr.s_addr != 0) {
-                       delete_routes(iface, options->metric);
+                       delete_routes(iface, iface->metric);
                        delete_address(iface);
                }
 
-               run_script(options, iface->name, reason, NULL, old);
+               run_script(iface, reason);
                return 0;
        }
 
        /* This also changes netmask */
-       if (!(options->options & DHCPCD_INFORM) ||
+       if (!(iface->state->options->options & DHCPCD_INFORM) ||
            !has_address(iface->name, &addr, &net)) {
-               logger(LOG_DEBUG, "adding IP address %s/%d",
-                      inet_ntoa(addr), inet_ntocidr(net));
-               if (add_address(iface->name, &addr, &net, &brd) == -1 &&
+               logger(LOG_DEBUG, "%s: adding IP address %s/%d",
+                      iface->name, inet_ntoa(addr), inet_ntocidr(net));
+               if (add_address(iface, &addr, &net, &brd) == -1 &&
                    errno != EEXIST)
                {
                        logger(LOG_ERR, "add_address: %s", strerror(errno));
@@ -395,26 +390,25 @@ configure(struct interface *iface, const char *reason,
 
 #ifdef __linux__
        /* On linux, we need to change the subnet route to have our metric. */
-       if (iface->addr.s_addr != lease->addr.s_addr &&
-           options->metric > 0 && net.s_addr != INADDR_BROADCAST)
+       if (iface->addr.s_addr != iface->state->lease.addr.s_addr &&
+           iface->metric > 0 &&
+           net.s_addr != INADDR_BROADCAST)
        {
                dest.s_addr = addr.s_addr & net.s_addr;
                gate.s_addr = 0;
-               add_route(iface->name, &dest, &net, &gate, options->metric);
-               del_route(iface->name, &dest, &net, &gate, 0);
+               add_route(iface, &dest, &net, &gate, iface->metric);
+               del_route(iface, &dest, &net, &gate, 0);
        }
 #endif
 
-       configure_routes(iface, dhcp, options);
-       up = (iface->addr.s_addr != addr.s_addr ||
-             iface->net.s_addr != net.s_addr);
+       configure_routes(iface, dhcp);
        iface->addr.s_addr = addr.s_addr;
        iface->net.s_addr = net.s_addr;
 
-       if (!lease->frominfo)
+       if (!iface->state->lease.frominfo)
                if (write_lease(iface, dhcp) == -1)
                        logger(LOG_ERR, "write_lease: %s", strerror(errno));
 
-       run_script(options, iface->name, reason, dhcp, old);
+       run_script(iface, reason);
        return 0;
 }
index fe065db6d93271b6fbc95bfbf6bae4538a41ea28..b5ffc7e0210252b1e9c740568fa1f2cec04c432d 100644 (file)
 #ifndef DHCPCONFIG_H
 #define DHCPCONFIG_H
 
-#include "dhcpcd.h"
-#include "dhcp.h"
 #include "net.h"
 
-int run_script(const struct options *, const char *, const char *,
-              const struct dhcp_message *, const struct dhcp_message *);
-int configure(struct interface *, const char *,
-             const struct dhcp_message *, const struct dhcp_message *,
-             const struct dhcp_lease *, const struct options *, int);
+int run_script(const struct interface *, const char *);
+int configure(struct interface *, const char *);
 
 #endif
diff --git a/dhcp.c b/dhcp.c
index a9ef632cc5684d8642528e871fc7d7f8df60fb6a..0fb1f63f0e17fe6f0877c20dbfb7d93da7fa99e6 100644 (file)
--- a/dhcp.c
+++ b/dhcp.c
@@ -35,6 +35,7 @@
 #include "config.h"
 #include "common.h"
 #include "dhcp.h"
+#include "dhcpf.h"
 
 #define REQUEST        (1 << 0)
 #define UINT8  (1 << 1)
@@ -166,12 +167,13 @@ print_options(void)
                        printf("%03d %s\n", opt->option, opt->var);
 }
 
-int make_option_mask(uint8_t *mask, char **opts, int add)
+int make_option_mask(uint8_t *mask, const char *opts, int add)
 {
-       char *token, *p = *opts, *t;
+       char *token, *o, *p, *t;
        const struct dhcp_opt *opt;
        int match, n;
 
+       o = p = xstrdup(opts);
        while ((token = strsep(&p, ", "))) {
                if (*token == '\0')
                        continue;
@@ -199,11 +201,12 @@ int make_option_mask(uint8_t *mask, char **opts, int add)
                        }
                }
                if (!opt->option) {
-                       *opts = token;
+                       free(o);
                        errno = ENOENT;
                        return -1;
                }
        }
+       free(o);
        return 0;
 }
 
@@ -745,8 +748,8 @@ encode_rfc1035(const char *src, uint8_t *dst, size_t len)
 
 ssize_t
 make_message(struct dhcp_message **message,
-            const struct interface *iface, const struct dhcp_lease *lease,
-            uint32_t xid, uint8_t type, const struct options *options)
+            const struct interface *iface,
+            uint8_t type)
 {
        struct dhcp_message *dhcp;
        uint8_t *m, *lp, *p;
@@ -755,6 +758,8 @@ make_message(struct dhcp_message **message,
        uint32_t ul;
        uint16_t sz;
        const struct dhcp_opt *opt;
+       const struct if_options *ifo = iface->state->options;
+       const struct dhcp_lease *lease = &iface->state->lease;
 
        dhcp = xzalloc(sizeof (*dhcp));
        m = (uint8_t *)dhcp;
@@ -795,7 +800,7 @@ make_message(struct dhcp_message **message,
                dhcp->secs = htons((uint16_t)UINT16_MAX);
        else
                dhcp->secs = htons(up);
-       dhcp->xid = xid;
+       dhcp->xid = iface->state->xid;
        dhcp->cookie = htonl(MAGIC_COOKIE);
 
        *p++ = DHO_MESSAGETYPE; 
@@ -822,17 +827,17 @@ make_message(struct dhcp_message **message,
        }
 
        if (type != DHCP_DECLINE && type != DHCP_RELEASE) {
-               if (options->userclass[0]) {
+               if (ifo->userclass[0]) {
                        *p++ = DHO_USERCLASS;
-                       memcpy(p, options->userclass, options->userclass[0] + 1);
-                       p += options->userclass[0] + 1;
+                       memcpy(p, ifo->userclass, ifo->userclass[0] + 1);
+                       p += ifo->userclass[0] + 1;
                }
 
-               if (options->vendorclassid[0]) {
+               if (ifo->vendorclassid[0]) {
                        *p++ = DHO_VENDORCLASSID;
-                       memcpy(p, options->vendorclassid,
-                              options->vendorclassid[0] + 1);
-                       p += options->vendorclassid[0] + 1;
+                       memcpy(p, ifo->vendorclassid,
+                              ifo->vendorclassid[0] + 1);
+                       p += ifo->vendorclassid[0] + 1;
                }
        }
 
@@ -854,10 +859,10 @@ make_message(struct dhcp_message **message,
                }
 #undef PUTADDR
 
-               if (options->leasetime != 0) {
+               if (ifo->leasetime != 0) {
                        *p++ = DHO_LEASETIME;
                        *p++ = 4;
-                       ul = htonl(options->leasetime);
+                       ul = htonl(ifo->leasetime);
                        memcpy(p, &ul, 4);
                        p += 4;
                }
@@ -867,12 +872,12 @@ make_message(struct dhcp_message **message,
            type == DHCP_INFORM ||
            type == DHCP_REQUEST)
        {
-               if (options->hostname[0]) {
+               if (ifo->hostname[0]) {
                        *p++ = DHO_HOSTNAME;
-                       memcpy(p, options->hostname, options->hostname[0] + 1);
-                       p += options->hostname[0] + 1;
+                       memcpy(p, ifo->hostname, ifo->hostname[0] + 1);
+                       p += ifo->hostname[0] + 1;
                }
-               if (options->fqdn != FQDN_DISABLE) {
+               if (ifo->fqdn != FQDN_DISABLE) {
                        /* IETF DHC-FQDN option (81), RFC4702 */
                        *p++ = DHO_FQDN;
                        lp = p;
@@ -887,20 +892,20 @@ make_message(struct dhcp_message **message,
                         * N: 1 => Client requests Server to not
                         *         update DNS
                         */
-                       *p++ = (options->fqdn & 0x09) | 0x04;
+                       *p++ = (ifo->fqdn & 0x09) | 0x04;
                        *p++ = 0; /* from server for PTR RR */
                        *p++ = 0; /* from server for A RR if S=1 */
-                       ul = encode_rfc1035(options->hostname + 1, p,
-                                       options->hostname[0]);
+                       ul = encode_rfc1035(ifo->hostname + 1, p,
+                                           ifo->hostname[0]);
                        *lp += ul;
                        p += ul;
                }
 
                /* vendor is already encoded correctly, so just add it */
-               if (options->vendor[0]) {
+               if (ifo->vendor[0]) {
                        *p++ = DHO_VENDOR;
-                       memcpy(p, options->vendor, options->vendor[0] + 1);
-                       p += options->vendor[0] + 1;
+                       memcpy(p, ifo->vendor, ifo->vendor[0] + 1);
+                       p += ifo->vendor[0] + 1;
                }
 
                *p++ = DHO_PARAMETERREQUESTLIST;
@@ -908,7 +913,7 @@ make_message(struct dhcp_message **message,
                *p++ = 0;
                for (opt = dhcp_opts; opt->option; opt++) {
                        if (!(opt->type & REQUEST || 
-                             has_option_mask(options->requestmask, opt->option)))
+                             has_option_mask(ifo->requestmask, opt->option)))
                                continue;
                        switch (opt->option) {
                        case DHO_RENEWALTIME:   /* FALLTHROUGH */
@@ -1155,7 +1160,7 @@ setvar(char ***e, const char *prefix, const char *var, const char *value)
 
 ssize_t
 configure_env(char **env, const char *prefix, const struct dhcp_message *dhcp,
-             const struct options *options)
+             const struct if_options *ifo)
 {
        unsigned int i;
        const uint8_t *p;
@@ -1176,7 +1181,7 @@ configure_env(char **env, const char *prefix, const struct dhcp_message *dhcp,
                for (opt = dhcp_opts; opt->option; opt++) {
                        if (!opt->var)
                                continue;
-                       if (has_option_mask(options->nomask, opt->option))
+                       if (has_option_mask(ifo->nomask, opt->option))
                                continue;
                        if (get_option_raw(dhcp, opt->option))
                                e++;
@@ -1219,7 +1224,7 @@ configure_env(char **env, const char *prefix, const struct dhcp_message *dhcp,
        for (opt = dhcp_opts; opt->option; opt++) {
                if (!opt->var)
                        continue;
-               if (has_option_mask(options->nomask, opt->option))
+               if (has_option_mask(ifo->nomask, opt->option))
                        continue;
                val = NULL;
                p = get_option(dhcp, opt->option, &pl, NULL);
@@ -1242,3 +1247,25 @@ configure_env(char **env, const char *prefix, const struct dhcp_message *dhcp,
 
        return ep - env;
 }
+
+void
+get_lease(struct dhcp_lease *lease, const struct dhcp_message *dhcp)
+{
+       time_t t;
+
+       lease->frominfo = 0;
+       lease->addr.s_addr = dhcp->yiaddr;
+       if (get_option_addr(&lease->net.s_addr, dhcp, DHO_SUBNETMASK) == -1)
+               lease->net.s_addr = get_netmask(dhcp->yiaddr);
+       if (get_option_uint32(&lease->leasetime, dhcp, DHO_LEASETIME) == 0) {
+               /* Ensure that we can use the lease */
+               t = 0;
+               if (t + (time_t)lease->leasetime < t)
+                       lease->leasetime = ~0U; /* Infinite lease */
+       } else
+               lease->leasetime = DEFAULT_LEASETIME;
+       if (get_option_uint32(&lease->renewaltime, dhcp, DHO_RENEWALTIME) != 0)
+               lease->renewaltime = 0;
+       if (get_option_uint32(&lease->rebindtime, dhcp, DHO_REBINDTIME) != 0)
+               lease->rebindtime = 0;
+}
diff --git a/dhcp.h b/dhcp.h
index e584452c2a02240c3a02ccf8839515582daf16e1..0f6053447db8b00df25e44456d22d022b3f1dd26 100644 (file)
--- a/dhcp.h
+++ b/dhcp.h
 #define DHCP_H
 
 #include <arpa/inet.h>
+#include <netinet/in.h>
 
 #include <stdint.h>
 
-#include "config.h"
-#include "dhcpcd.h"
-#include "net.h"
-
 /* Max MTU - defines dhcp option length */
 #define MTU_MAX             1500
 #define MTU_MIN             576
 #define DHCP_RELEASE        7
 #define DHCP_INFORM         8
 
+/* Constants taken from RFC 2131. */
+#define T1                     0.5
+#define T2                     0.875
+#define DHCP_BASE              4
+#define DHCP_MAX               64
+#define DHCP_RAND_MIN          -1
+#define DHCP_RAND_MAX          1
+#define DHCP_ARP_FAIL          10
+
+/* number of usecs in a second. */
+#define USECS_SECOND           1000000
+/* As we use timevals, we should use the usec part for
+ * greater randomisation. */
+#define DHCP_RAND_MIN_U                DHCP_RAND_MIN * USECS_SECOND
+#define DHCP_RAND_MAX_U                DHCP_RAND_MAX * USECS_SECOND
+#define PROBE_MIN_U            PROBE_MIN * USECS_SECOND
+#define PROBE_MAX_U            PROBE_MAX * USECS_SECOND
+
 /* DHCP options */
 enum DHO
 {
@@ -154,25 +169,4 @@ struct dhcp_lease {
        uint8_t frominfo;
 };
 
-#define add_option_mask(var, val) (var[val >> 3] |= 1 << (val & 7))
-#define del_option_mask(var, val) (var[val >> 3] &= ~(1 << (val & 7)))
-#define has_option_mask(var, val) (var[val >> 3] & (1 << (val & 7)))
-int make_option_mask(uint8_t *, char **, int);
-void print_options(void);
-char *get_option_string(const struct dhcp_message *, uint8_t);
-int get_option_addr(uint32_t *, const struct dhcp_message *, uint8_t);
-int get_option_uint32(uint32_t *, const struct dhcp_message *, uint8_t);
-int get_option_uint16(uint16_t *, const struct dhcp_message *, uint8_t);
-int get_option_uint8(uint8_t *, const struct dhcp_message *, uint8_t);
-struct rt *get_option_routes(const struct dhcp_message *);
-ssize_t configure_env(char **, const char *, const struct dhcp_message *,
-                     const struct options *);
-
-ssize_t make_message(struct dhcp_message **,
-                       const struct interface *, const struct dhcp_lease *,
-                       uint32_t, uint8_t, const struct options *);
-int valid_dhcp_packet(unsigned char *);
-
-ssize_t write_lease(const struct interface *, const struct dhcp_message *);
-struct dhcp_message *read_lease(const struct interface *iface);
 #endif
index 6c82d3fbb0414f91a31cf596caf39a0b5017e845..68d7f9509f9416892ac6c184b2eca52f0f00a237 100644 (file)
@@ -22,7 +22,7 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.Dd August 20, 2008
+.Dd September 1, 2008
 .Dt DHCPCD 8 SMM
 .Sh NAME
 .Nm dhcpcd
 .Op Fl O , -nooption Ar option
 .Op Fl Q , -require Ar option
 .Op Fl X , -blacklist Ar address
-.Ar interface
+.Op interface
+.Op ...
 .Nm
 .Fl k , -release
-.Ar interface
+.Op interface
 .Nm
 .Fl x , -exit
-.Ar interface
+.Op interface
 .Sh DESCRIPTION
 .Nm
 is an implementation of the DHCP client specified in
@@ -97,6 +98,14 @@ installed which always defeats IPv4LL probing.
 To disable this behaviour, you can use the
 .Fl L , -noipv4ll
 option.
+.Ss Multiple interfaces
+.Nm
+can be run per interface or as a single instance to manage all interfaces.
+If a list of interfaces are given on the command line, then
+.Nm
+only works with those interfaces.
+If no interfaces are given then we detect all available interfaces and
+attempt to configure all of them.
 .Ss Hooking into DHCP events
 .Nm
 runs
@@ -427,4 +436,12 @@ RFC 3442, RFC 3927, RFC 4361, RFC 4390, RFC 4702.
 .Sh AUTHORS
 .An Roy Marples <roy@marples.name>
 .Sh BUGS
+You cannot release a lease - this will be fixed before the first release.
+.Pp
+You cannot dynamically add or remove interfaces either manually or detected.
+.Pp
+One of the goals for one instance managing multiple interfaces is more
+intelligent route and configuration management.
+This has not yet been done.
+.Pp
 Please report them to http://bugs.marples.name
index 0219b02fa784f6839f0616da4d8ca398a6594e2e..c450217e0c612dbaa47cb613315577b000441cac 100644 (file)
--- a/dhcpcd.c
+++ b/dhcpcd.c
@@ -36,6 +36,7 @@ const char copyright[] = "Copyright (c) 2006-2008 Roy Marples";
 #include <ctype.h>
 #include <errno.h>
 #include <getopt.h>
+#include <limits.h>
 #include <paths.h>
 #include <signal.h>
 #include <stdio.h>
@@ -44,97 +45,61 @@ const char copyright[] = "Copyright (c) 2006-2008 Roy Marples";
 #include <unistd.h>
 #include <time.h>
 
+#include "arp.h"
+#include "bind.h"
 #include "config.h"
-#include "client.h"
+#include "common.h"
+#include "configure.h"
 #include "dhcpcd.h"
-#include "dhcp.h"
-#include "net.h"
+#include "dhcpf.h"
+#include "duid.h"
+#include "eloop.h"
+#include "if-options.h"
+#include "ipv4ll.h"
 #include "logger.h"
+#include "net.h"
+#include "signals.h"
 
-/* Don't set any optional arguments here so we retain POSIX
- * compatibility with getopt */
-#define OPTS "bc:df:h:i:kl:m:no:pqr:s:t:u:v:xABC:DEF:GI:KLO:Q:TVX:"
-
-static int doversion = 0;
-static int dohelp = 0;
-static const struct option longopts[] = {
-       {"background",    no_argument,        NULL, 'b'},
-       {"script",        required_argument,  NULL, 'c'},
-       {"debug",         no_argument,        NULL, 'd'},
-       {"config",        required_argument,  NULL, 'f'},
-       {"hostname",      optional_argument,  NULL, 'h'},
-       {"vendorclassid", optional_argument,  NULL, 'i'},
-       {"release",       no_argument,        NULL, 'k'},
-       {"leasetime",     required_argument,  NULL, 'l'},
-       {"metric",        required_argument,  NULL, 'm'},
-       {"rebind",        no_argument,        NULL, 'n'},
-       {"option",        required_argument,  NULL, 'o'},
-       {"persistent",    no_argument,        NULL, 'p'},
-       {"quiet",         no_argument,        NULL, 'q'},
-       {"request",       optional_argument,  NULL, 'r'},
-       {"inform",        optional_argument,  NULL, 's'},
-       {"timeout",       required_argument,  NULL, 't'},
-       {"userclass",     required_argument,  NULL, 'u'},
-       {"vendor",        required_argument,  NULL, 'v'},
-       {"exit",          no_argument,        NULL, 'x'},
-       {"noarp",         no_argument,        NULL, 'A'},
-       {"nobackground",  no_argument,        NULL, 'B'},
-       {"nohook",        required_argument,  NULL, 'C'},
-       {"duid",          no_argument,        NULL, 'D'},
-       {"lastlease",     no_argument,        NULL, 'E'},
-       {"fqdn",          optional_argument,  NULL, 'F'},
-       {"nogateway",     no_argument,        NULL, 'G'},
-       {"clientid",      optional_argument,  NULL, 'I'},
-       {"nolink",        no_argument,        NULL, 'K'},
-       {"noipv4ll",      no_argument,        NULL, 'L'},
-       {"nooption",      optional_argument,  NULL, 'O'},
-       {"require",       required_argument,  NULL, 'Q'},
-       {"test",          no_argument,        NULL, 'T'},
-       {"variables",     no_argument,        NULL, 'V'},
-       {"blacklist",     required_argument,  NULL, 'X'},
-       {"help",          no_argument,        &dohelp, 1},
-       {"version",       no_argument,        &doversion, 1},
-#ifdef CMDLINE_COMPAT
-       {"classid",       optional_argument,  NULL, 'i'},
-       {"renew",         no_argument,        NULL, 'n'},
-       {"nohostname",    no_argument,        NULL, 'H'},
-       {"nomtu",         no_argument,        NULL, 'M'},
-       {"nontp",         no_argument,        NULL, 'N'},
-       {"nodns",         no_argument,        NULL, 'R'},
-       {"msscr",         no_argument,        NULL, 'S'},
-       {"nonis",         no_argument,        NULL, 'Y'},
-#endif
-       {NULL,          0,                  NULL, '\0'}
-};
+/* We should define a maximum for the NAK exponential backoff */ 
+#define NAKOFF_MAX              60
 
-#ifdef CMDLINE_COMPAT
-# define EXTRA_OPTS "HMNRSY"
-#endif
+int pidfd = -1;
+static int linkfd = -1;
+static char cffile[PATH_MAX];
+static char pidfile[PATH_MAX] = { '\0' };
+static struct interface *ifaces = NULL;
 
-#ifndef EXTRA_OPTS
-# define EXTRA_OPTS
-#endif
 
-static int
-atoint(const char *s)
-{
-       char *t;
-       long n;
+struct dhcp_op {
+       uint8_t value;
+       const char *name;
+};
 
-       errno = 0;
-       n = strtol(s, &t, 0);
-       if ((errno != 0 && n == 0) || s == t ||
-           (errno == ERANGE && (n == LONG_MAX || n == LONG_MIN)))
-       {
-               logger(LOG_ERR, "`%s' out of range", s);
-               return -1;
-       }
+static const struct dhcp_op const dhcp_ops[] = {
+       { DHCP_DISCOVER, "DHCP_DISCOVER" },
+       { DHCP_OFFER,    "DHCP_OFFER" },
+       { DHCP_REQUEST,  "DHCP_REQUEST" },
+       { DHCP_DECLINE,  "DHCP_DECLINE" },
+       { DHCP_ACK,      "DHCP_ACK" },
+       { DHCP_NAK,      "DHCP_NAK" },
+       { DHCP_RELEASE,  "DHCP_RELEASE" },
+       { DHCP_INFORM,   "DHCP_INFORM" },
+       { 0, NULL }
+};
+
+static const char *
+get_dhcp_op(uint8_t type)
+{
+       const struct dhcp_op *d;
 
-       return (int)n;
+       for (d = dhcp_ops; d->name; d++)
+               if (d->value == type)
+                       return d->name;
+       return NULL;
 }
 
 static pid_t
-read_pid(const char *pidfile)
+read_pid(void)
 {
        FILE *fp;
        pid_t pid = 0;
@@ -159,721 +124,709 @@ usage(void)
               "              [-I clientID] [-C hookscript] [-Q option] [-X ipaddr] <interface>\n");
 }
 
-static char * 
-add_environ(struct options *options, const char *value, int uniq)
+static void
+cleanup(void)
 {
-       char **newlist;
-       char **lst = options->environ;
-       size_t i = 0, l, lv;
-       char *match = NULL, *p;
-
-       match = xstrdup(value);
-       p = strchr(match, '=');
-       if (p)
-               *p++ = '\0';
-       l = strlen(match);
-
-       while (lst && lst[i]) {
-               if (match && strncmp(lst[i], match, l) == 0) {
-                       if (uniq) {
-                               free(lst[i]);
-                               lst[i] = xstrdup(value);
-                       } else {
-                               /* Append a space and the value to it */
-                               l = strlen(lst[i]);
-                               lv = strlen(p);
-                               lst[i] = xrealloc(lst[i], l + lv + 2);
-                               lst[i][l] = ' ';
-                               memcpy(lst[i] + l + 1, p, lv);
-                               lst[i][l + lv + 1] = '\0';
-                       }
-                       free(match);
-                       return lst[i];
-               }
-               i++;
+#ifdef DEBUG_MEMORY
+       struct interface *iface;
+
+       while (ifaces) {
+               iface = ifaces;
+               ifaces = iface->next;
+               free_interface(iface);
        }
+#endif
 
-       newlist = xrealloc(lst, sizeof(char *) * (i + 2));
-       newlist[i] = xstrdup(value);
-       newlist[i + 1] = NULL;
-       options->environ = newlist;
-       free(match);
-       return newlist[i];
+       if (linkfd != -1)
+               close(linkfd);
+       if (pidfd > -1) {
+               close(pidfd);
+               unlink(pidfile);
+       }
+}
+
+void
+handle_exit_timeout(_unused struct interface *iface)
+{
+       logger(LOG_ERR, "timed out");
+       exit(EXIT_FAILURE);
+}
+
+void
+drop_config(struct interface *iface, const char *reason)
+{
+       if (iface->state->new || strcmp(reason, "FAIL") == 0) {
+               free(iface->state->new);
+               iface->state->new = NULL;
+               configure(iface, reason);
+               free(iface->state->old);
+               iface->state->old = NULL;
+       }
+       iface->state->lease.addr.s_addr = 0;
+}
+
+void
+close_sockets(struct interface *iface)
+{
+       if (iface->arp_fd != -1) {
+               delete_event(iface->arp_fd);
+               close(iface->arp_fd);
+               iface->arp_fd = -1;
+       }
+       if (iface->raw_fd != -1) {
+               delete_event(iface->raw_fd);
+               close(iface->raw_fd);
+               iface->raw_fd = -1;
+       }
+       if (iface->udp_fd != -1) {
+               close(iface->udp_fd);
+               iface->udp_fd = -1;
+       }
 }
 
-#define parse_string(buf, len, arg) parse_string_hwaddr(buf, len, arg, 0)
-static ssize_t
-parse_string_hwaddr(char *sbuf, ssize_t slen, char *str, int clid)
+static void
+send_message(struct interface *iface, int type,
+            void (*function)(struct interface *))
 {
-       ssize_t l;
-       char *p;
-       int i;
-       char c[4];
-
-       /* If surrounded by quotes then it's a string */
-       if (*str == '"') {
-               str++;
-               l = strlen(str);
-               p = str + l - 1;
-               if (*p == '"')
-                       *p = '\0';
+       struct if_state *state = iface->state;
+       struct dhcp_message *dhcp;
+       uint8_t *udp;
+       ssize_t len, r;
+       struct in_addr from, to;
+       in_addr_t a = 0;
+       struct timeval tv;
+
+       if (!function)
+               logger(LOG_DEBUG, "%s: sending %s with xid 0x%x",
+                      iface->name, get_dhcp_op(type), state->xid);
+       else {
+               if (state->interval == 0)
+                       state->interval = 4;
+               else {
+                       state->interval *= 2;
+                       if (state->interval > 64)
+                               state->interval = 64;
+               }
+               tv.tv_sec = state->interval + DHCP_RAND_MIN;
+               tv.tv_usec = arc4random() % (DHCP_RAND_MAX_U - DHCP_RAND_MIN_U);
+               logger(LOG_DEBUG,
+                      "%s: sending %s with xid 0x%x, next in %0.2f seconds",
+                      iface->name, get_dhcp_op(type), state->xid,
+                      timeval_to_double(&tv));
+       }
+       /* If we couldn't open a UDP port for our IP address
+        * then we cannot renew.
+        * This could happen if our IP was pulled out from underneath us. */
+       if (iface->udp_fd == -1) {
+               a = iface->addr.s_addr;
+               iface->addr.s_addr = 0;
+       }
+       len = make_message(&dhcp, iface, type);
+       if (iface->udp_fd == -1)
+               iface->addr.s_addr = a;
+       from.s_addr = dhcp->ciaddr;
+       if (from.s_addr)
+               to.s_addr = state->lease.server.s_addr;
+       else
+               to.s_addr = 0;
+       if (to.s_addr && to.s_addr != INADDR_BROADCAST) {
+               r = send_packet(iface, to, (uint8_t *)dhcp, len);
+               if (r == -1)
+                       logger(LOG_ERR, "%s: send_packet: %s",
+                              iface->name, strerror(errno));
        } else {
-               l = hwaddr_aton(NULL, str);
-               if (l > 1) {
-                       if (l > slen) {
-                               errno = ENOBUFS;
-                               return -1;
-                       }
-                       hwaddr_aton((uint8_t *)sbuf, str);
-                       return l;
-               }
+               len = make_udp_packet(&udp, (uint8_t *)dhcp, len, from, to);
+               r = send_raw_packet(iface, ETHERTYPE_IP, udp, len);
+               free(udp);
+               if (r == -1)
+                       logger(LOG_ERR, "%s: send_raw_packet: %s",
+                              iface->name, strerror(errno));
        }
+       free(dhcp);
+       if (function)
+               add_timeout_tv(&tv, function, iface);
+}
+
+static void
+send_discover(struct interface *iface)
+{
+       send_message(iface, DHCP_DISCOVER, send_discover);
+}
+
+void
+send_request(struct interface *iface)
+{
+       send_message(iface, DHCP_REQUEST, send_request);
+}
 
-       /* Process escapes */
-       l = 0;
-       /* If processing a string on the clientid, first byte should be
-        * 0 to indicate a non hardware type */
-       if (clid) {
-               *sbuf++ = 0;
-               l++;
+static void
+send_renew(struct interface *iface)
+{
+       send_message(iface, DHCP_REQUEST, send_renew);
+}
+
+void
+start_renew(struct interface *iface)
+{
+       logger(LOG_INFO, "%s: renewing lease of %s",
+                       iface->name, inet_ntoa(iface->state->lease.addr));
+       iface->state->state = DHS_RENEWING;
+       iface->state->xid = arc4random();
+       send_renew(iface);
+}
+
+static void
+send_rebind(struct interface *iface)
+{
+       send_message(iface, DHCP_REQUEST, send_rebind);
+}
+
+void
+start_rebind(struct interface *iface)
+{
+       logger(LOG_ERR, "%s: failed to renew, attmepting to rebind",
+                       iface->name);
+       iface->state->state = DHS_REBINDING;
+       delete_timeout(send_renew, iface);
+       iface->state->lease.server.s_addr = 0;
+       send_rebind(iface);
+}
+
+void
+start_expire(struct interface *iface)
+{
+       int ll = IN_LINKLOCAL(htonl(iface->state->lease.addr.s_addr));
+
+       logger(LOG_ERR, "%s: lease expired", iface->name);
+       delete_timeout(NULL, iface);
+       drop_config(iface, "EXPIRE");
+       iface->state->interval = 0;
+       if (iface->state->carrier != LINK_DOWN) {
+               if (ll)
+                       start_interface(iface);
+               else
+                       start_ipv4ll(iface);
        }
-       c[3] = '\0';
-       while (*str) {
-               if (++l > slen) {
-                       errno = ENOBUFS;
-                       return -1;
-               }
-               if (*str == '\\') {
-                       str++;
-                       switch(*str++) {
-                       case '\0':
-                               break;
-                       case 'b':
-                               *sbuf++ = '\b';
-                               break;
-                       case 'n':
-                               *sbuf++ = '\n';
-                               break;
-                       case 'r':
-                               *sbuf++ = '\r';
-                               break;
-                       case 't':
-                               *sbuf++ = '\t';
-                               break;
-                       case 'x':
-                               /* Grab a hex code */
-                               c[1] = '\0';
-                               for (i = 0; i < 2; i++) {
-                                       if (isxdigit((unsigned char)*str) == 0)
-                                               break;
-                                       c[i] = *str++;
-                               }
-                               if (c[1] != '\0') {
-                                       c[2] = '\0';
-                                       *sbuf++ = strtol(c, NULL, 16);
-                               } else
-                                       l--;
-                               break;
-                       case '0':
-                               /* Grab an octal code */
-                               c[2] = '\0';
-                               for (i = 0; i < 3; i++) {
-                                       if (*str < '0' || *str > '7')
-                                               break;
-                                       c[i] = *str++;
-                               }
-                               if (c[2] != '\0') {
-                                       i = strtol(c, NULL, 8);
-                                       if (i > 255)
-                                               i = 255;
-                                       *sbuf ++= i;
-                               } else
-                                       l--;
-                               break;
-                       default:
-                               *sbuf++ = *str++;
-                       }
-               } else
-                       *sbuf++ = *str++;
+}
+
+void
+send_decline(struct interface *iface)
+{
+       send_message(iface, DHCP_DECLINE, NULL);
+}
+
+static void
+log_dhcp(int lvl, const char *msg,
+        const struct interface *iface, const struct dhcp_message *dhcp)
+{
+       char *a;
+       struct in_addr addr;
+       int r;
+
+       if (strcmp(msg, "NAK:") == 0)
+               a = get_option_string(dhcp, DHO_MESSAGE);
+       else {
+               addr.s_addr = dhcp->yiaddr;
+               a = xstrdup(inet_ntoa(addr));
        }
-       return l;
+       r = get_option_addr(&addr.s_addr, dhcp, DHO_SERVERID);
+       if (dhcp->servername[0] && r == 0)
+               logger(lvl, "%s: %s %s from %s `%s'", iface->name, msg, a,
+                      inet_ntoa(addr), dhcp->servername);
+       else if (r == 0)
+               logger(lvl, "%s: %s %s from %s",
+                      iface->name, msg, a, inet_ntoa(addr));
+       else
+               logger(lvl, "%s: %s %s", iface->name, msg, a);
+       free(a);
 }
 
-static int
-parse_option(int opt, char *oarg, struct options *options)
+static void
+handle_dhcp(struct interface *iface, struct dhcp_message **dhcpp)
 {
-       int i;
-       char *p;
-       ssize_t s;
+       struct if_state *state = iface->state;
+       struct if_options *ifo = state->options;
+       struct dhcp_message *dhcp = *dhcpp;
+       struct dhcp_lease *lease = &state->lease;
+       uint8_t type, tmp;
        struct in_addr addr;
+       size_t i;
 
-       switch(opt) {
-       case 'b':
-               options->options |= DHCPCD_BACKGROUND;
-               break;
-       case 'c':
-               strlcpy(options->script, oarg, sizeof(options->script));
-               break;
-       case 'h':
-               if (oarg)
-                       s = parse_string(options->hostname + 1,
-                                        HOSTNAME_MAX_LEN, oarg);
-               else
-                       s = 0;
-               if (s == -1) {
-                       logger(LOG_ERR, "hostname: %s", strerror(errno));
-                       return -1;
-               }
-               if (s != 0 && options->hostname[1] == '.') {
-                       logger(LOG_ERR, "hostname cannot begin with a .");
-                       return -1;
-               }
-               options->hostname[0] = (uint8_t)s;
-               break;
-       case 'i':
-               if (oarg)
-                       s = parse_string((char *)options->vendorclassid + 1,
-                                        VENDORCLASSID_MAX_LEN, oarg);
-               else
-                       s = 0;
-               if (s == -1) {
-                       logger(LOG_ERR, "vendorclassid: %s", strerror(errno));
-                       return -1;
-               }
-               *options->vendorclassid = (uint8_t)s;
-               break;
-       case 'l':
-               if (*oarg == '-') {
-                       logger(LOG_ERR,
-                              "leasetime must be a positive value");
-                       return -1;
-               }
-               errno = 0;
-               options->leasetime = (uint32_t)strtol(oarg, NULL, 0);
-               if (errno == EINVAL || errno == ERANGE) {
-                       logger(LOG_ERR, "`%s' out of range", oarg);
-                       return -1;
-               }
-               break;
-       case 'm':
-               options->metric = atoint(oarg);
-               if (options->metric < 0) {
-                       logger(LOG_ERR, "metric must be a positive value");
-                       return -1;
+       /* reset the message counter */
+       state->interval = 0;
+
+       /* We have to have DHCP type to work */
+       if (get_option_uint8(&type, dhcp, DHO_MESSAGETYPE) == -1) {
+               log_dhcp(LOG_ERR, "no DHCP type in", iface, dhcp);
+               return;
+       }
+
+       /* Ensure that it's not from a blacklisted server.
+        * We should expand this to check IP and/or hardware address
+        * at the packet level. */
+       if (ifo->blacklist_len != 0 &&
+           get_option_addr(&addr.s_addr, dhcp, DHO_SERVERID) == 0)
+       {
+               for (i = 0; i < ifo->blacklist_len; i++) {
+                       if (ifo->blacklist[i] != addr.s_addr)
+                               continue;
+                       if (dhcp->servername[0])
+                               logger(LOG_WARNING,
+                                      "%s: ignoring blacklisted server %s `%s'",
+                                       iface->name,
+                                       inet_ntoa(addr), dhcp->servername);
+                       else
+                               logger(LOG_WARNING,
+                                      "%s: ignoring blacklisted server %s",
+                                      iface->name, inet_ntoa(addr));
+                       return;
                }
-               break;
-       case 'o':
-               if (make_option_mask(options->requestmask, &oarg, 1) != 0) {
-                       logger(LOG_ERR, "unknown option `%s'", oarg);
-                       return -1;
+       }
+
+       /* We should restart on a NAK */
+       if (type == DHCP_NAK) {
+               log_dhcp(LOG_WARNING, "NAK:", iface, dhcp);
+               drop_config(iface, "EXPIRE");
+               delete_event(iface->raw_fd);
+               close(iface->raw_fd);
+               iface->raw_fd = -1;
+               close(iface->udp_fd);
+               iface->udp_fd = -1;
+               /* If we constantly get NAKS then we should slowly back off */
+               add_timeout_sec(state->nakoff, start_interface, iface);
+               state->nakoff *= 2;
+               if (state->nakoff > NAKOFF_MAX)
+                       state->nakoff = NAKOFF_MAX;
+               return;
+       }
+
+       /* No NAK, so reset the backoff */
+       state->nakoff = 1;
+
+       /* Ensure that all required options are present */
+       for (i = 1; i < 255; i++) {
+               if (has_option_mask(ifo->requiremask, i) &&
+                   get_option_uint8(&tmp, dhcp, i) != 0)
+               {
+                       log_dhcp(LOG_WARNING, "reject", iface, dhcp);
+                       return;
                }
-               break;
-       case 'p':
-               options->options |= DHCPCD_PERSISTENT;
-               break;
-       case 'q':
-               setloglevel(LOG_WARNING);
-               break;
-       case 's':
-               options->options |= DHCPCD_INFORM;
-               options->options |= DHCPCD_PERSISTENT;
-               options->options &= ~DHCPCD_ARP;
-               if (!oarg || *oarg == '\0') {
-                       options->request_address.s_addr = 0;
-                       break;
-               } else {
-                       if ((p = strchr(oarg, '/'))) {
-                               /* nullify the slash, so the -r option
-                                * can read the address */
-                               *p++ = '\0';
-                               if (sscanf(p, "%d", &i) != 1 ||
-                                   inet_cidrtoaddr(i, &options->request_netmask) != 0)
-                               {
-                                       logger(LOG_ERR,
-                                              "`%s' is not a valid CIDR",
-                                              p);
-                                       return -1;
-                               }
+       }
+
+       if (type == DHCP_OFFER && state->state == DHS_DISCOVERING) {
+               lease->addr.s_addr = dhcp->yiaddr;
+               get_option_addr(&lease->server.s_addr, dhcp, DHO_SERVERID);
+               log_dhcp(LOG_INFO, "offered", iface, dhcp);
+               if (ifo->options & DHCPCD_TEST) {
+                       run_script(iface, "TEST");
+                       exit(EXIT_SUCCESS);
+               }
+               free(state->offer);
+               state->offer = dhcp;
+               *dhcpp = NULL;
+               delete_timeout(send_discover, iface);
+               if (ifo->options & DHCPCD_ARP &&
+                   iface->addr.s_addr != state->offer->yiaddr)
+               {
+                       /* If the interface already has the address configured
+                        * then we can't ARP for duplicate detection. */
+                       addr.s_addr = state->offer->yiaddr;
+                       if (!has_address(iface->name, &addr, NULL)) {
+                               state->state = DHS_PROBING;
+                               state->claims = 0;
+                               state->probes = 0;
+                               state->conflicts = 0;
+                               send_arp_probe(iface);
+                               return;
                        }
                }
-               /* FALLTHROUGH */
-       case 'r':
-               if (!(options->options & DHCPCD_INFORM))
-                       options->options |= DHCPCD_REQUEST;
-               if (oarg && !inet_aton(oarg, &options->request_address)) {
-                       logger(LOG_ERR, "`%s' is not a valid IP address",
-                              oarg);
-                       return -1;
-               }
-               break;
-       case 't':
-               options->timeout = atoint(oarg);
-               if (options->timeout < 0) {
-                       logger (LOG_ERR, "timeout must be a positive value");
-                       return -1;
-               }
-               break;
-       case 'u':
-               s = USERCLASS_MAX_LEN - options->userclass[0] - 1;
-               s = parse_string((char *)options->userclass + options->userclass[0] + 2,
-                                s, oarg);
-               if (s == -1) {
-                       logger(LOG_ERR, "userclass: %s", strerror(errno));
-                       return -1;
-               }
-               if (s != 0) {
-                       options->userclass[options->userclass[0] + 1] = s;
-                       options->userclass[0] += s + 1;
-               }
-               break;
-       case 'v':
-               p = strchr(oarg, ',');
-               if (!p || !p[1]) {
-                       logger(LOG_ERR, "invalid vendor format");
-                       return -1;
-               }
-               *p = '\0';
-               i = atoint(oarg);
-               oarg = p + 1;
-               if (i < 1 || i > 254) {
-                       logger(LOG_ERR, "vendor option should be between"
-                                       " 1 and 254 inclusive");
-                       return -1;
-               }
-               s = VENDOR_MAX_LEN - options->vendor[0] - 2;
-               if (inet_aton(oarg, &addr) == 1) {
-                       if (s < 6) {
-                               s = -1;
-                               errno = ENOBUFS;
-                       } else
-                               memcpy(options->vendor + options->vendor[0] + 3,
-                                      &addr.s_addr, sizeof(addr.s_addr));
-               } else {
-                       s = parse_string((char *)options->vendor + options->vendor[0] + 3,
-                                        s, oarg);
-               }
-               if (s == -1) {
-                       logger(LOG_ERR, "vendor: %s", strerror(errno));
-                       return -1;
-               }
-               if (s != 0) {
-                       options->vendor[options->vendor[0] + 1] = i;
-                       options->vendor[options->vendor[0] + 2] = s;
-                       options->vendor[0] += s + 2;
-               }
-               break;
-       case 'A':
-               options->options &= ~DHCPCD_ARP;
-               /* IPv4LL requires ARP */
-               options->options &= ~DHCPCD_IPV4LL;
-               break;
-       case 'B':
-               options->options &= ~DHCPCD_DAEMONISE;
-               break;
-       case 'C':
-               /* Commas to spaces for shell */
-               while ((p = strchr(oarg, ',')))
-                       *p = ' ';
-               s = strlen("skip_hooks=") + strlen(oarg) + 1;
-               p = xmalloc(sizeof(char) * s);
-               snprintf(p, s, "skip_hooks=%s", oarg);
-               add_environ(options, p, 0);
-               free(p);
-               break;
-       case 'D':
-               options->options |= DHCPCD_DUID;
-               break;
-       case 'E':
-               options->options |= DHCPCD_LASTLEASE;
-               break;
-       case 'F':
-               if (!oarg) {
-                       options->fqdn = FQDN_BOTH;
+               state->state = DHS_REQUESTING;
+               send_request(iface);
+               return;
+       }
+
+       if (type == DHCP_OFFER) {
+               log_dhcp(LOG_INFO, "ignoring offer of", iface, dhcp);
+               return;
+       }
+
+       /* We should only be dealing with acks */
+       if (type != DHCP_ACK) {
+               log_dhcp(LOG_ERR, "not ACK or OFFER", iface, dhcp);
+               return;
+       }
+
+       if (!(ifo->options & DHCPCD_INFORM))
+               log_dhcp(LOG_INFO, "acknowledged", iface, dhcp);
+       close_sockets(iface);
+       free(state->offer);
+       state->offer = dhcp;
+       *dhcpp = NULL;
+       /* Delete all timeouts for this interface. */
+       delete_timeout(NULL, iface);
+       bind_interface(iface);
+}
+
+static void
+handle_dhcp_packet(struct interface *iface)
+{
+       uint8_t *packet;
+       struct dhcp_message *dhcp = NULL;
+       const uint8_t *pp;
+       uint8_t *p;
+       ssize_t bytes;
+
+       /* 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. */
+       packet = xmalloc(udp_dhcp_len);
+       for(;;) {
+               bytes = get_raw_packet(iface, ETHERTYPE_IP,
+                                      packet, udp_dhcp_len);
+               if (bytes == 0 || bytes == -1)
                        break;
+               if (valid_udp_packet(packet) == -1)
+                       continue;
+               bytes = get_udp_data(&pp, packet);
+               if ((size_t)bytes > sizeof(*dhcp)) {
+                       logger(LOG_ERR, "%s: packet greater than DHCP size",
+                              iface->name);
+                       continue;
                }
-               if (strcmp(oarg, "none") == 0)
-                       options->fqdn = FQDN_NONE;
-               else if (strcmp(oarg, "ptr") == 0)
-                       options->fqdn = FQDN_PTR;
-               else if (strcmp(oarg, "both") == 0)
-                       options->fqdn = FQDN_BOTH;
-               else if (strcmp(oarg, "disable") == 0)
-                       options->fqdn = FQDN_DISABLE;
-               else {
-                       logger(LOG_ERR, "invalid value `%s' for FQDN",
-                              oarg);
-                       return -1;
-               }
-               break;
-       case 'G':
-               options->options &= ~DHCPCD_GATEWAY;
-               break;
-       case 'I':
-               /* Strings have a type of 0 */;
-               options->clientid[1] = 0;
-               if (oarg)
-                       s = parse_string_hwaddr((char *)options->clientid + 1,
-                                               CLIENTID_MAX_LEN, oarg, 1);
-               else
-                       s = 0;
-               if (s == -1) {
-                       logger(LOG_ERR, "clientid: %s", strerror(errno));
-                       return -1;
-               }
-               options->clientid[0] = (uint8_t)s;
-               if (s == 0) {
-                       options->options &= ~DHCPCD_DUID;
-                       options->options &= ~DHCPCD_CLIENTID;
+               if (!dhcp)
+                       dhcp = xmalloc(sizeof(*dhcp));
+               memcpy(dhcp, pp, bytes);
+               if (dhcp->cookie != htonl(MAGIC_COOKIE)) {
+                       logger(LOG_DEBUG, "%s: bogus cookie, ignoring",
+                              iface->name);
+                       continue;
                }
-               break;
-       case 'K':
-               options->options &= ~DHCPCD_LINK;
-               break;
-       case 'L':
-               options->options &= ~DHCPCD_IPV4LL;
-               break;
-       case 'O':
-               if (make_option_mask(options->requestmask, &oarg, -1) != 0 ||
-                   make_option_mask(options->requiremask, &oarg, -1) != 0 ||
-                   make_option_mask(options->nomask, &oarg, 1) != 0)
-               {
-                       logger(LOG_ERR, "unknown option `%s'", oarg);
-                       return -1;
+               /* Ensure it's the right transaction */
+               if (iface->state->xid != dhcp->xid) {
+                       logger(LOG_DEBUG,
+                              "%s: ignoring packet with xid 0x%x as"
+                              " it's not ours (0x%x)",
+                              iface->name, dhcp->xid, iface->state->xid);
+                       continue;
                }
-               break;
-       case 'Q':
-               if (make_option_mask(options->requiremask, &oarg, 1) != 0 ||
-                   make_option_mask(options->requestmask, &oarg, 1) != 0)
+               /* Ensure packet is for us */
+               if (iface->hwlen <= sizeof(dhcp->chaddr) &&
+                   memcmp(dhcp->chaddr, iface->hwaddr, iface->hwlen))
                {
-                       logger(LOG_ERR, "unknown option `%s'", oarg);
-                       return -1;
-               }
-               break;
-       case 'X':
-               if (!inet_aton(oarg, &addr)) {
-                       logger(LOG_ERR, "`%s' is not a valid IP address",
-                              oarg);
-                       return -1;
+                       logger(LOG_DEBUG, "%s: xid 0x%x is not for our hwaddr %s",
+                              iface->name, dhcp->xid,
+                              hwaddr_ntoa(dhcp->chaddr, sizeof(dhcp->chaddr)));
+                       continue;
                }
-               options->blacklist = xrealloc(options->blacklist,
-                   sizeof(in_addr_t) * (options->blacklist_len + 1));
-               options->blacklist[options->blacklist_len] = addr.s_addr;
-               options->blacklist_len++;
-               break;
-       default:
-               return 0;
+               /* We should ensure that the packet is terminated correctly
+                * if we have space for the terminator */
+               if ((size_t)bytes < sizeof(struct dhcp_message)) {
+                       p = (uint8_t *)dhcp + bytes - 1;
+                       while (p > dhcp->options && *p == DHO_PAD)
+                               p--;
+                       if (*p != DHO_END)
+                               *++p = DHO_END;
+               }
+               handle_dhcp(iface, &dhcp);
+               if (iface->raw_fd == -1)
+                       break;
        }
-
-       return 1;
+       free(packet);
+       free(dhcp);
 }
 
-static int
-parse_config_line(const char *opt, char *line, struct options *options)
+static void
+open_sockets(struct interface *iface)
 {
-       unsigned int i;
+       if (iface->udp_fd != -1)
+               close(iface->udp_fd);
+       if (open_udp_socket(iface) == -1 &&
+           (errno != EADDRINUSE || iface->addr.s_addr != 0))
+               logger(LOG_ERR, "open_udp_socket: %s", strerror(errno));
+       if (iface->raw_fd != -1)
+               delete_event(iface->raw_fd);
+       if (open_socket(iface, ETHERTYPE_IP) == -1)
+               logger(LOG_ERR, "open_socket: %s", strerror(errno));
+       if (iface->raw_fd != -1)
+               add_event(iface->raw_fd, handle_dhcp_packet, iface);
+}
 
-       for (i = 0; i < sizeof(longopts) / sizeof(longopts[0]); i++) {
-               if (!longopts[i].name ||
-                   strcmp(longopts[i].name, opt) != 0)
-                       continue;
+static void
+handle_link(struct interface *iface)
+{
+       int retval;
 
-               if (longopts[i].has_arg == required_argument && !line) {
-                       fprintf(stderr,
-                               PACKAGE ": option requires an argument -- %s\n",
-                               opt);
-                       return -1;
+       retval = link_changed(linkfd, ifaces);
+       if (retval == -1) {
+               logger(LOG_ERR, "link_changed: %s", strerror(errno));
+               return;
+       }
+       if (retval == 0)
+               return;
+       for (iface = ifaces; iface; iface = iface->next) {
+               if (iface->state->options->options & DHCPCD_LINK) {
+                       switch (carrier_status(iface->name)) {
+                       case -1:
+                               logger(LOG_ERR, "carrier_status: %s",
+                                      strerror(errno));
+                               break;
+                       case 0:
+                               if (iface->state->carrier != LINK_DOWN) {
+                                       iface->state->carrier = LINK_DOWN;
+                                       logger(LOG_INFO, "%s: carrier lost",
+                                              iface->name);
+                                       close_sockets(iface);
+                                       delete_timeouts(iface, start_expire, NULL);
+                               }
+                               break;
+                       default:
+                               if (iface->state->carrier != LINK_UP) {
+                                       iface->state->carrier = LINK_UP;
+                                       logger(LOG_INFO, "%s: carrier acquired",
+                                              iface->name);
+                                       start_interface(iface);
+                               }
+                               break;
+                       }
                }
+       }
+}
 
-               return parse_option(longopts[i].val, line, options);
+void
+start_discover(struct interface *iface)
+{
+       struct if_options *ifo = iface->state->options;
+
+       iface->state->state = DHS_DISCOVERING;
+       iface->state->xid = arc4random();
+       open_sockets(iface);
+       delete_timeout(NULL, iface);
+       if (ifo->options & DHCPCD_IPV4LL &&
+           !IN_LINKLOCAL(htonl(iface->addr.s_addr)))
+       {
+               if (IN_LINKLOCAL(htonl(iface->state->fail.s_addr)))
+                       add_timeout_sec(RATE_LIMIT_INTERVAL, start_ipv4ll, iface);
+               else
+                       add_timeout_sec(ifo->timeout, start_ipv4ll, iface);
        }
+       logger(LOG_INFO, "%s: broadcasting for a lease", iface->name);
+       send_discover(iface);
+}
+
 
-       fprintf(stderr, PACKAGE ": unknown option -- %s\n", opt);
-       return -1;
+void
+start_reboot(struct interface *iface)
+{
+       struct if_options *ifo = iface->state->options;
+
+       logger(LOG_INFO, "%s: rebinding lease of %s",
+                       iface->name, inet_ntoa(iface->state->lease.addr));
+       iface->state->state = DHS_REBINDING;
+       iface->state->xid = arc4random();
+       iface->state->lease.server.s_addr = 0;
+       delete_timeout(NULL, iface);
+       add_timeout_sec(ifo->timeout, start_expire, iface);
+       open_sockets(iface);
+       send_rebind(iface);
 }
 
-int
-main(int argc, char **argv)
+void
+start_interface(struct interface *iface)
 {
-       struct options *options;
-       int opt;
-       int option_index = 0;
-       char *prefix;
-       pid_t pid;
-       int debug = 0;
-       int i, r;
-       int pid_fd = -1;
-       int sig = 0;
-       int retval = EXIT_FAILURE;
-       char *line, *option, *p, *buffer = NULL;
-       size_t len = 0;
-       FILE *f;
-       char *cf = NULL;
-       char *intf = NULL;
-       struct timespec ts;
+       iface->start_uptime = uptime();
+       if (!iface->state->lease.addr.s_addr)
+               start_discover(iface);
+       else if (IN_LINKLOCAL(htonl(iface->state->lease.addr.s_addr)))
+               start_ipv4ll(iface);
+       else
+               start_reboot(iface);
+}
 
-       closefrom(3);
-       /* Saves calling fflush(stream) in the logger */
-       setlinebuf(stdout);
-       openlog(PACKAGE, LOG_PID, LOG_LOCAL0);
-       setlogprefix(PACKAGE ": ");
-
-       options = xzalloc(sizeof(*options));
-       options->options |= DHCPCD_CLIENTID | DHCPCD_GATEWAY | DHCPCD_DAEMONISE;
-       options->options |= DHCPCD_ARP | DHCPCD_IPV4LL | DHCPCD_LINK;
-       options->timeout = DEFAULT_TIMEOUT;
-       strlcpy(options->script, SCRIPT, sizeof(options->script));
-
-       options->vendorclassid[0] = snprintf((char *)options->vendorclassid + 1,
-                                            VENDORCLASSID_MAX_LEN,
-                                            "%s %s", PACKAGE, VERSION);
-
-#ifdef CMDLINE_COMPAT
-       add_option_mask(options->requestmask, DHO_DNSSERVER);
-       add_option_mask(options->requestmask, DHO_DNSDOMAIN);
-       add_option_mask(options->requestmask, DHO_DNSSEARCH);
-       add_option_mask(options->requestmask, DHO_NISSERVER);
-       add_option_mask(options->requestmask, DHO_NISDOMAIN);
-       add_option_mask(options->requestmask, DHO_NTPSERVER);
-
-       /* If the duid file exists, then enable duid by default
-        * This means we don't break existing clients that easily :) */
-       if ((f = fopen(DUID, "r"))) {
-               options->options |= DHCPCD_DUID;
-               fclose(f);
+static void
+init_state(struct interface *iface, int argc, char **argv)
+{
+       struct if_state *ifs;
+       struct if_options *ifo;
+       uint8_t *duid;
+       size_t len = 0, ifl;
+
+       if (iface->state) {
+               ifs = iface->state;
+               free_options(ifs->options);
+       } else
+               ifs = iface->state = xzalloc(sizeof(*ifs));
+
+       ifs->state = DHS_INIT;
+       ifs->nakoff = 1;
+       ifo = ifs->options = read_config(cffile, iface->name);
+       add_options(ifo, argc, argv);
+
+       if (ifo->metric)
+               iface->metric = ifo->metric;
+
+       if (*ifo->clientid) {
+               iface->clientid = xmalloc(ifo->clientid[0] + 1);
+               memcpy(iface->clientid, ifo->clientid, ifo->clientid[0] + 1);
+       } else if (ifo->options & DHCPCD_CLIENTID) {
+               if (ifo->options & DHCPCD_DUID) {
+                       duid = xmalloc(DUID_LEN);
+                       if ((len = get_duid(duid, iface)) == 0)
+                               logger(LOG_ERR, "get_duid: %s", strerror(errno));
+               }
+               if (len > 0) {
+                       iface->clientid = xmalloc(len + 6);
+                       iface->clientid[0] = len + 5;
+                       iface->clientid[1] = 255; /* RFC 4361 */
+                       ifl = strlen(iface->name);
+                       if (ifl < 5) {
+                               memcpy(iface->clientid + 2, iface->name, ifl);
+                               if (ifl < 4)
+                                       memset(iface->clientid + 2 + ifl,
+                                                       0, 4 - ifl);
+                       } else {
+                               ifl = htonl(if_nametoindex(iface->name));
+                               memcpy(iface->clientid + 2, &ifl, 4);
+                       }
+               } else if (len == 0) {
+                       len = iface->hwlen + 1;
+                       iface->clientid = xmalloc(len + 1);
+                       iface->clientid[0] = len;
+                       iface->clientid[1] = iface->family;
+                       memcpy(iface->clientid + 2, iface->hwaddr, iface->hwlen);
+               }
        }
-#endif
 
-       gethostname(options->hostname + 1, sizeof(options->hostname));
-       if (strcmp(options->hostname + 1, "(none)") == 0 ||
-           strcmp(options->hostname + 1, "localhost") == 0)
-               options->hostname[1] = '\0';
-       *options->hostname = strlen(options->hostname + 1);
+       if (!(ifo->options & DHCPCD_TEST))
+               run_script(iface, "PREINIT");
 
-       while ((opt = getopt_long(argc, argv, OPTS EXTRA_OPTS,
-                                 longopts, &option_index)) != -1)
-       {
-               switch (opt) {
+       if (ifo->options & DHCPCD_LINK) {
+               switch (carrier_status(iface->name)) {
                case 0:
-                       if (longopts[option_index].flag)
-                               break;
-                       logger(LOG_ERR, "option `%s' should set a flag",
-                              longopts[option_index].name);
-                       goto abort;
-               case 'f':
-                       cf = optarg;
+                       ifs->carrier = LINK_DOWN;
                        break;
-               case 'V':
-                       print_options();
-                       goto abort;
-               case '?':
-                       usage();
-                       goto abort;
+               case 1:
+                       ifs->carrier = LINK_UP;
+                       break;
+               default:
+                       ifs->carrier = LINK_UNKNOWN;
                }
        }
 
-       if (doversion)
-               printf(""PACKAGE" "VERSION"\n%s\n", copyright);
+       if (ifs->carrier == LINK_DOWN)
+               logger(LOG_INFO, "%s: waiting for carrier", iface->name);
+       else
+               start_interface(iface);
+}
 
-       if (dohelp)
-               usage();
+static void
+handle_signal(struct interface *iface)
+{
+       int sig = signal_read();
 
-       if (optind < argc) {
-               if (strlen(argv[optind]) >= IF_NAMESIZE) {
-                       logger(LOG_ERR,
-                              "`%s' too long for an interface name (max=%d)",
-                              argv[optind], IF_NAMESIZE);
-                       goto abort;
-               }
-               strlcpy(options->interface, argv[optind],
-                       sizeof(options->interface));
-       } else {
-               /* If only version was requested then exit now */
-               if (doversion || dohelp) {
-                       retval = 0;
-                       goto abort;
-               }
+       switch (sig) {
+       case SIGINT:
+               logger(LOG_INFO, "received SIGINT, stopping");
+               break;
+       case SIGTERM:
+               logger(LOG_INFO, "received SIGTERM, stopping");
+               break;
+       default:
+               logger (LOG_ERR,
+                       "received signal %d, but don't know what to do with it",
+                       sig);
+               return;
+       }
 
-               logger(LOG_ERR, "no interface specified");
-               goto abort;
+       for (iface = ifaces; iface; iface = iface->next) {
+               if (!iface->state)
+                       continue;
+               if (!(iface->state->options->options & DHCPCD_PERSISTENT))
+                       drop_config(iface, "STOP");
        }
+       exit(EXIT_FAILURE);
+}
 
-       /* Parse our options file */
-       f = fopen(cf ? cf : CONFIG, "r");
-       if (f) {
-               r = 1;
-               while ((get_line(&buffer, &len, f))) {
-                       line = buffer;
-                       while ((option = strsep(&line, " \t")))
-                               if (*option != '\0')
-                                       break;
-                       if (!option || *option == '\0' || *option == '#')
-                               continue;
-                       /* Trim leading whitespace */
-                       if (line) {
-                               while (*line != '\0' && (*line == ' ' || *line == '\t'))
-                                       line++;
-                       }
-                       /* Trim trailing whitespace */
-                       if (line && *line) {
-                               p = line + strlen(line) - 1;
-                               while (p != line &&
-                                      (*p == ' ' || *p == '\t') &&
-                                      *(p - 1) != '\\')
-                                       *p-- = '\0';
-                       }
-                       if (strcmp(option, "interface") == 0) {
-                               free(intf);
-                               intf = xstrdup(line);
-                               continue;
-                       }
-                       /* If we're in an interface block don't use these
-                        * options unless it's for us */
-                       if (intf && strcmp(intf, options->interface) != 0)
-                               continue;
-                       r = parse_config_line(option, line, options);
-                       if (r != 1)
-                               break;
-               }
-               free(buffer);
-               free(intf);
-               fclose(f);
-               if (r == 0)
+int
+main(int argc, char **argv)
+{
+       struct if_options *ifo;
+       struct interface *iface;
+       int opt, oi = 0, test = 0, signal_fd, sig = 0, i;
+       pid_t pid;
+       struct timespec ts;
+
+       closefrom(3);
+       /* Saves calling fflush(stream) in the logger */
+       setlinebuf(stdout);
+       openlog(PACKAGE, LOG_PID, LOG_LOCAL0);
+       strncpy(cffile, CONFIG, sizeof(cffile));
+
+       /* Test for --help and --version */
+       if (argc > 1) {
+               if (strcmp(argv[1], "--help") == 0) {
                        usage();
-               if (r != 1)
-                       goto abort;
-       } else {
-               if (errno != ENOENT || cf) {
-                       logger(LOG_ERR, "fopen `%s': %s", cf ? cf : CONFIG,
-                              strerror(errno));
-                       goto abort;
+                       exit(EXIT_SUCCESS);
+               } else if (strcmp(argv[1], "--version") == 0) {
+                       printf(""PACKAGE" "VERSION"\n%s\n", copyright);
+                       exit(EXIT_SUCCESS);
                }
        }
 
-       optind = 0;
-       while ((opt = getopt_long(argc, argv, OPTS EXTRA_OPTS,
-                                 longopts, &option_index)) != -1)
+       while ((opt = getopt_long(argc, argv, IF_OPTS, cf_options, &oi)) != -1)
        {
                switch (opt) {
                case 'd':
-                       debug++;
-                       switch (debug) {
-                       case 1:
-                               setloglevel(LOG_DEBUG);
-                               break;
-                       case 2:
-                               options->options &= ~DHCPCD_DAEMONISE;
-                               break;
-                       }
+                       setloglevel(LOG_DEBUG);
                        break;
                case 'f':
-                       break;
-               case 'k':
-                       sig = SIGHUP;
-                       break;
-               case 'n':
-                       sig = SIGALRM;
+                       strlcpy(cffile, optarg, sizeof(cffile));
                        break;
                case 'x':
                        sig = SIGTERM;
                        break;
                case 'T':
-                       options->options |= DHCPCD_TEST | DHCPCD_PERSISTENT;
-                       break;
-#ifdef CMDLINE_COMPAT
-               case 'H': /* FALLTHROUGH */
-               case 'M':
-                       del_option_mask(options->requestmask, DHO_MTU);
-                       break;
-               case 'N':
-                       del_option_mask(options->requestmask, DHO_NTPSERVER);
+                       test = 1;
                        break;
-               case 'R':
-                       del_option_mask(options->requestmask, DHO_DNSSERVER);
-                       del_option_mask(options->requestmask, DHO_DNSDOMAIN);
-                       del_option_mask(options->requestmask, DHO_DNSSEARCH);
-                       break;
-               case 'S':
-                       add_option_mask(options->requestmask, DHO_MSCSR);
-                       break;
-               case 'Y':
-                       del_option_mask(options->requestmask, DHO_NISSERVER);
-                       del_option_mask(options->requestmask, DHO_NISDOMAIN);
-                       break;
-#endif
-               default:
-                       i = parse_option(opt, optarg, options);
-                       if (i == 1)
-                               break;
-                       if (i == 0)
-                               usage();
-                       goto abort;
+               case 'V':
+                       print_options();
+                       exit(EXIT_SUCCESS);
+               case '?':
+                       usage();
+                       exit(EXIT_FAILURE);
                }
        }
 
+       ifo = read_config(cffile, NULL);
+       opt = add_options(ifo, argc, argv);
+       if (opt != 1) {
+               if (opt == 0)
+                       usage();
+               exit(EXIT_FAILURE);
+       }
+       if (test)
+               ifo->options |= DHCPCD_TEST | DHCPCD_PERSISTENT;
 #ifdef THERE_IS_NO_FORK
-       options->options &= ~DHCPCD_DAEMONISE;
+       ifo->options &= ~DHCPCD_DAEMONISE;
 #endif
 
+       /* If we have any other args, we should run as a single dhcpcd instance
+        * for that interface. */
+       if (optind == argc - 1)
+               snprintf(pidfile, sizeof(pidfile), PIDFILE, "-", argv[optind]);
+       else
+               snprintf(pidfile, sizeof(pidfile), PIDFILE, "", "");
+
        if (geteuid())
                logger(LOG_WARNING, PACKAGE " will not work correctly unless"
                       " run as root");
 
-       if (options->options & DHCPCD_TEST) {
-               if (options->options & DHCPCD_REQUEST ||
-                   options->options & DHCPCD_INFORM) {
-                       logger(LOG_ERR,
-                              "cannot test with --inform or --request");
-                       goto abort;
-               }
-
-               if (options->options & DHCPCD_LASTLEASE) {
-                       logger(LOG_ERR, "cannot test with --lastlease");
-                       goto abort;
-               }
-
-               if (sig != 0) {
-                       logger(LOG_ERR,
-                              "cannot test with --release or --renew");
-                       goto abort;
-               }
-       }
-
-       prefix = xmalloc(sizeof(char) * (IF_NAMESIZE + 3));
-       snprintf(prefix, IF_NAMESIZE, "%s: ", options->interface);
-       setlogprefix(prefix);
-       snprintf(options->pidfile, sizeof(options->pidfile), PIDFILE,
-                options->interface);
-       free(prefix);
-
-       if (options->request_address.s_addr == 0 &&
-           (options->options & DHCPCD_INFORM ||
-            options->options & DHCPCD_REQUEST))
-       {
-               errno = 0;
-               if (get_address(options->interface,
-                               &options->request_address,
-                               &options->request_netmask) != 1)
-               {
-                       if (errno)
-                               logger(LOG_ERR, "get_address: %s",
-                                      strerror(errno));
-                       else
-                               logger(LOG_ERR, "no existing address");
-                       goto abort;
-               }
-       }
-       if (IN_LINKLOCAL(ntohl(options->request_address.s_addr))) {
-               logger(LOG_ERR,
-                      "you are not allowed to request a link local address");
-               goto abort;
-       }
-
        chdir("/");
        umask(022);
+       atexit(cleanup);
 
-       if (sig != 0 && !(options->options & DHCPCD_DAEMONISED)) {
+       if (sig != 0) {
                i = -1;
-               pid = read_pid(options->pidfile);
+               pid = read_pid();
                if (pid != 0)
                        logger(LOG_INFO, "sending signal %d to pid %d",
                               sig, pid);
@@ -881,89 +834,84 @@ main(int argc, char **argv)
                if (!pid || (i = kill(pid, sig))) {
                        if (sig != SIGALRM)
                                logger(LOG_ERR, ""PACKAGE" not running");
-                       unlink(options->pidfile);
-               }
-               if (i == 0) {
-                       if (sig == SIGALRM) {
-                               retval = EXIT_SUCCESS;
-                               goto abort;
-                       }
-                       /* Spin until it exits */
-                       logger(LOG_INFO, "waiting for pid %d to exit", pid);
-                       ts.tv_sec = 0;
-                       ts.tv_nsec = 100000000; /* 10th of a second */
-                       for(i = 0; i < 100; i++) {
-                               nanosleep(&ts, NULL);
-                               if (read_pid(options->pidfile) == 0) {
-                                       retval = EXIT_SUCCESS;
-                                       break;
-                               }
-                       }
-                       if (retval != EXIT_SUCCESS)
-                               logger(LOG_ERR, "pid %d failed to exit", pid);
-                       goto abort;
-               }
-               if (sig != SIGALRM)
-                       goto abort;     
+                       unlink(pidfile);
+                       exit(EXIT_FAILURE);
+               }
+               /* Spin until it exits */
+               logger(LOG_INFO, "waiting for pid %d to exit", pid);
+               ts.tv_sec = 0;
+               ts.tv_nsec = 100000000; /* 10th of a second */
+               for(i = 0; i < 100; i++) {
+                       nanosleep(&ts, NULL);
+                       if (read_pid() == 0)
+                               exit(EXIT_SUCCESS);
+               }
+               logger(LOG_ERR, "pid %d failed to exit", pid);
+               exit(EXIT_FAILURE);
        }
 
-       if (!(options->options & DHCPCD_TEST) &&
-           !(options->options & DHCPCD_DAEMONISED))
-       {
-               if ((pid = read_pid(options->pidfile)) > 0 &&
+       if (!(ifo->options & DHCPCD_TEST)) {
+               if ((pid = read_pid()) > 0 &&
                    kill(pid, 0) == 0)
                {
                        logger(LOG_ERR, ""PACKAGE
                               " already running on pid %d (%s)",
-                              pid, options->pidfile);
-                       goto abort;
+                              pid, pidfile);
+                       exit(EXIT_FAILURE);
                }
 
-               pid_fd = open(options->pidfile,
-                            O_WRONLY | O_CREAT | O_NONBLOCK, 0664);
-               if (pid_fd == -1) {
+               pidfd = open(pidfile, O_WRONLY | O_CREAT | O_NONBLOCK, 0664);
+               if (pidfd == -1) {
                        logger(LOG_ERR, "open `%s': %s",
-                              options->pidfile, strerror(errno));
-                       goto abort;
+                              pidfile, strerror(errno));
+                       exit(EXIT_FAILURE);
                }
-
                /* Lock the file so that only one instance of dhcpcd runs
                 * on an interface */
-               if (flock(pid_fd, LOCK_EX | LOCK_NB) == -1) {
+               if (flock(pidfd, LOCK_EX | LOCK_NB) == -1) {
                        logger(LOG_ERR, "flock `%s': %s",
-                              options->pidfile, strerror(errno));
-                       goto abort;
+                              pidfile, strerror(errno));
+                       exit(EXIT_FAILURE);
                }
-
-               if (set_cloexec(pid_fd) == -1)
-                       goto abort;
-               writepid(pid_fd, getpid());
-               logger(LOG_INFO, PACKAGE " " VERSION " starting");
+               if (set_cloexec(pidfd) == -1)
+                       exit(EXIT_FAILURE);
+               writepid(pidfd, getpid());
        }
 
-       /* Terminate the encapsulated options */
-       if (options->vendor[0]) {
-               options->vendor[0]++;
-               options->vendor[options->vendor[0]] = DHO_END;
-       }
+       logger(LOG_INFO, PACKAGE " " VERSION " starting");
 
-       if (dhcp_run(options, &pid_fd) == 0)
-               retval = EXIT_SUCCESS;
+       if ((signal_fd =signal_init()) == -1)
+               exit(EXIT_FAILURE);
+       if (signal_setup() == -1)
+               exit(EXIT_FAILURE);
+       add_event(signal_fd, handle_signal, NULL);
 
-abort:
-       /* If we didn't daemonise then we need to punt the pidfile now */
-       if (pid_fd > -1) {
-               close(pid_fd);
-               unlink(options->pidfile);
+       if (ifo->options & DHCPCD_LINK) {
+               linkfd = open_link_socket();
+               if (linkfd == -1)
+                       logger(LOG_ERR, "open_link_socket: %s",
+                                       strerror(errno));
+               else
+                       add_event(linkfd, handle_link, NULL);
        }
-       if (options->environ) {
-               len = 0;
-               while (options->environ[len])
-                       free(options->environ[len++]);
-               free(options->environ);
+
+       if (!(ifo->options & DHCPCD_DAEMONISE))
+               can_daemonise = 0;
+       if (can_daemonise && !(ifo->options & DHCPCD_BACKGROUND)) {
+               oi = ifo->timeout;
+               if (ifo->options & DHCPCD_IPV4LL)
+                       oi += 10;
+               add_timeout_sec(oi, handle_exit_timeout, NULL);
        }
-       free(options->blacklist);
-       free(options);
-       exit(retval);
+       free_options(ifo);
+
+       argc -= optind;
+       argv += optind;
+       ifaces = discover_interfaces(argc, argv);
+       argc += optind;
+       argv -= optind;
+       for (iface = ifaces; iface; iface = iface->next)
+               init_state(iface, argc, argv);
+       start_eloop();
        /* NOTREACHED */
 }
index 1cd2b5d5ffac36d4ebbcec1d996e30fd512ae2fd..061a1d1b8e834e456cce70507aab4da0175e3462 100644 (file)
--- a/dhcpcd.h
+++ b/dhcpcd.h
 #ifndef DHCPCD_H
 #define DHCPCD_H
 
-#include <sys/param.h>
-#include <sys/socket.h>
-
 #include <net/if.h>
-#include <netinet/in.h>
 
 #include <limits.h>
 
-#include "common.h"
-
-#define DEFAULT_TIMEOUT                30
-#define DEFAULT_LEASETIME      3600    /* 1 hour */
-
-#define HOSTNAME_MAX_LEN       250     /* 255 - 3 (FQDN) - 2 (DNS enc) */
-#define VENDORCLASSID_MAX_LEN  48
-#define CLIENTID_MAX_LEN       48
-#define USERCLASS_MAX_LEN      255
-#define VENDOR_MAX_LEN         255
-
-#define DHCPCD_ARP             (1 << 0)
-#define DHCPCD_DOMAIN          (1 << 2)
-#define DHCPCD_GATEWAY         (1 << 3)
-#define DHCPCD_LASTLEASE       (1 << 7)
-#define DHCPCD_INFORM          (1 << 8)
-#define DHCPCD_REQUEST         (1 << 9)
-#define DHCPCD_IPV4LL          (1 << 10)
-#define DHCPCD_DUID            (1 << 11)
-#define DHCPCD_PERSISTENT      (1 << 12)
-#define DHCPCD_DAEMONISE       (1 << 14)
-#define DHCPCD_DAEMONISED      (1 << 15)
-#define DHCPCD_TEST            (1 << 16)
-#define DHCPCD_FORKED          (1 << 17)
-#define DHCPCD_HOSTNAME                (1 << 18)
-#define DHCPCD_CLIENTID                (1 << 19)
-#define DHCPCD_LINK            (1 << 20)
-#define DHCPCD_BACKGROUND      (1 << 21)
-
-struct options {
-       char interface[IF_NAMESIZE];
+#include "dhcp.h"
+
+#define HWADDR_LEN 20
+
+enum DHS {
+       DHS_INIT,
+       DHS_DISCOVERING,
+       DHS_REQUESTING,
+       DHS_BOUND,
+       DHS_RENEWING,
+       DHS_REBINDING,
+       DHS_REBOOT,
+       DHS_RENEW_REQUESTED,
+       DHS_INIT_IPV4LL,
+       DHS_PROBING,
+       DHS_ANNOUNCING
+};
+
+#define LINK_UP        1
+#define LINK_UNKNOWN   0
+#define LINK_DOWN      -1
+
+struct if_state {
+       enum DHS state;
+       struct if_options *options;
+       struct dhcp_message *sent;
+       struct dhcp_message *offer;
+       struct dhcp_message *new;
+       struct dhcp_message *old;
+       struct dhcp_lease lease;
+       time_t interval;
+       time_t nakoff;
+       uint32_t xid;
+       int socket;
+       int carrier;
+       int probes;
+       int claims;
+       int conflicts;
+       time_t defend;
+       struct in_addr fail;
+};
+
+struct interface
+{
+       char name[IF_NAMESIZE];
+       struct if_state *state;
+
+       sa_family_t family;
+       unsigned char hwaddr[HWADDR_LEN];
+       size_t hwlen;
        int metric;
-       uint8_t requestmask[256 / 8];
-       uint8_t requiremask[256 / 8];
-       uint8_t nomask[256 / 8];
-       uint32_t leasetime;
-       time_t timeout;
-       int options;
-
-       struct in_addr request_address;
-       struct in_addr request_netmask;
-
-       char **environ;
-       char script[PATH_MAX];
-       char pidfile[PATH_MAX];
-
-       char hostname[HOSTNAME_MAX_LEN + 1];
-       int fqdn;
-       uint8_t vendorclassid[VENDORCLASSID_MAX_LEN + 1];
-       char clientid[CLIENTID_MAX_LEN + 1];
-       uint8_t userclass[USERCLASS_MAX_LEN + 1];
-       uint8_t vendor[VENDOR_MAX_LEN + 1];
-
-       size_t blacklist_len;
-       in_addr_t *blacklist;
+       int arpable;
+
+       int raw_fd;
+       int udp_fd;
+       int arp_fd;
+       size_t buffer_size, buffer_len, buffer_pos;
+       unsigned char *buffer;
+
+       struct in_addr addr;
+       struct in_addr net;
+       struct rt *routes;
+
+       char leasefile[PATH_MAX];
+       time_t start_uptime;
+
+       unsigned char *clientid;
+
+       struct interface *next;
 };
+
+extern int pidfd;
+void handle_exit_timeout(struct interface *);
+void send_request(struct interface *);
+void start_interface(struct interface *);
+void start_discover(struct interface *);
+void start_renew(struct interface *);
+void start_rebind(struct interface *);
+void start_reboot(struct interface *);
+void start_expire(struct interface *);
+void send_decline(struct interface *);
+void close_sockets(struct interface *);
+void drop_config(struct interface *, const char *);
+
 #endif
diff --git a/dhcpf.h b/dhcpf.h
new file mode 100644 (file)
index 0000000..eb09633
--- /dev/null
+++ b/dhcpf.h
@@ -0,0 +1,55 @@
+/* 
+ * 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.
+ */
+
+#ifndef DHCPF_H
+#define DHCPF_H
+
+#include "dhcp.h"
+#include "if-options.h"
+#include "net.h"
+
+#define add_option_mask(var, val) (var[val >> 3] |= 1 << (val & 7))
+#define del_option_mask(var, val) (var[val >> 3] &= ~(1 << (val & 7)))
+#define has_option_mask(var, val) (var[val >> 3] & (1 << (val & 7)))
+int make_option_mask(uint8_t *, const char *, int);
+void print_options(void);
+char *get_option_string(const struct dhcp_message *, uint8_t);
+int get_option_addr(uint32_t *, const struct dhcp_message *, uint8_t);
+int get_option_uint32(uint32_t *, const struct dhcp_message *, uint8_t);
+int get_option_uint16(uint16_t *, const struct dhcp_message *, uint8_t);
+int get_option_uint8(uint8_t *, const struct dhcp_message *, uint8_t);
+struct rt *get_option_routes(const struct dhcp_message *);
+ssize_t configure_env(char **, const char *, const struct dhcp_message *,
+                     const struct if_options *);
+
+ssize_t make_message(struct dhcp_message **, const struct interface *, uint8_t);
+int valid_dhcp_packet(unsigned char *);
+
+ssize_t write_lease(const struct interface *, const struct dhcp_message *);
+struct dhcp_message *read_lease(const struct interface *);
+void get_lease(struct dhcp_lease *, const struct dhcp_message *);
+#endif
diff --git a/duid.c b/duid.c
new file mode 100644 (file)
index 0000000..8b19550
--- /dev/null
+++ b/duid.c
@@ -0,0 +1,106 @@
+/* 
+ * 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.
+ */
+
+#define THIRTY_YEARS_IN_SECONDS    946707779
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "common.h"
+#include "duid.h"
+#include "net.h"
+
+size_t
+get_duid(unsigned char *duid, const struct interface *iface)
+{
+       FILE *f;
+       uint16_t type = 0;
+       uint16_t hw = 0;
+       uint32_t ul;
+       time_t t;
+       int x = 0;
+       unsigned char *p = duid;
+       size_t len = 0, l = 0;
+       char *buffer = NULL, *line, *option;
+
+       /* If we already have a DUID then use it as it's never supposed
+        * to change once we have one even if the interfaces do */
+       if ((f = fopen(DUID, "r"))) {
+               while ((get_line(&buffer, &len, f))) {
+                       line = buffer;
+                       while ((option = strsep(&line, " \t")))
+                               if (*option != '\0')
+                                       break;
+                       if (!option || *option == '\0' || *option == '#')
+                               continue;
+                       l = hwaddr_aton(NULL, option);
+                       if (l && l <= DUID_LEN) {
+                               hwaddr_aton(duid, option);
+                               break;
+                       }
+                       l = 0;
+               }
+               fclose(f);
+               free(buffer);
+               if (l)
+                       return l;
+       } else {
+               if (errno != ENOENT)
+                       return 0;
+       }
+
+       /* No file? OK, lets make one based on our interface */
+       if (!(f = fopen(DUID, "w")))
+               return 0;
+       type = htons(1); /* DUI-D-LLT */
+       memcpy(p, &type, 2);
+       p += 2;
+       hw = htons(iface->family);
+       memcpy(p, &hw, 2);
+       p += 2;
+       /* time returns seconds from jan 1 1970, but DUID-LLT is
+        * seconds from jan 1 2000 modulo 2^32 */
+       t = time(NULL) - THIRTY_YEARS_IN_SECONDS;
+       ul = htonl(t & 0xffffffff);
+       memcpy(p, &ul, 4);
+       p += 4;
+       /* Finally, add the MAC address of the interface */
+       memcpy(p, iface->hwaddr, iface->hwlen);
+       p += iface->hwlen;
+       len = p - duid;
+       x = fprintf(f, "%s\n", hwaddr_ntoa(duid, len));
+       fclose(f);
+       /* Failed to write the duid? scrub it, we cannot use it */
+       if (x < 1) {
+               len = 0;
+               unlink(DUID);
+       }
+       return len;
+}
diff --git a/client.h b/duid.h
similarity index 92%
rename from client.h
rename to duid.h
index 35a5e37cf42e5f53506a4623274fbfe357be7729..cc2224e888564db4ac333f619c77966941fa257a 100644 (file)
--- a/client.h
+++ b/duid.h
  * SUCH DAMAGE.
  */
 
-#ifndef CLIENT_H
-#define CLIENT_H
+#ifndef DUID_H
+#define DUID_H
 
-#include "dhcpcd.h"
+#include "net.h"
 
-int dhcp_run(const struct options *, int *);
+size_t get_duid(unsigned char *duid, const struct interface *iface);
 
 #endif
diff --git a/eloop.c b/eloop.c
new file mode 100644 (file)
index 0000000..6391416
--- /dev/null
+++ b/eloop.c
@@ -0,0 +1,347 @@
+/* 
+ * 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 <errno.h>
+#include <limits.h>
+#include <poll.h>
+#include <stdarg.h>
+#include <stdlib.h>
+
+#include "common.h"
+#include "eloop.h"
+#include "logger.h"
+#include "net.h"
+
+static struct timeval now = {0, 0};
+
+static struct event {
+       int fd;
+       void (*callback)(struct interface *);
+       struct interface *iface;
+       struct event *next;
+} *events = NULL;
+static struct event *free_events = NULL;
+
+static struct timeout {
+       struct timeval when;
+       void (*callback)(struct interface *);
+       struct interface *iface;
+       struct timeout *next;
+} *timeouts = NULL;
+static struct timeout *free_timeouts = NULL;
+
+static struct pollfd *fds = NULL;
+static size_t fds_len = 0;
+
+#ifdef DEBUG_MEMORY
+/* Define this to free all malloced memory.
+ * Normally we don't do this as the OS will do it for us at exit,
+ * but it's handy for debugging other leaks in valgrind. */
+static void
+cleanup(void)
+{
+       struct event *e;
+       struct timeout *t;
+
+       while (events) {
+               e = events->next;
+               free(events);
+               events = e;
+       }
+       while (free_events) {
+               e = free_events->next;
+               free(free_events);
+               free_events = e;
+       }
+       while (timeouts) {
+               t = timeouts->next;
+               free(timeouts);
+               timeouts = t;
+       }
+       while (free_timeouts) {
+               t = free_timeouts->next;
+               free(free_timeouts);
+               free_timeouts = t;
+       }
+       free(fds);
+}
+#endif
+
+void
+add_event(int fd, void (*callback)(struct interface *), struct interface *iface)
+{
+       struct event *e, *last = NULL;
+
+       /* We should only have one callback monitoring the fd */
+       for (e = events; e; e = e->next) {
+               if (e->fd == fd) {
+                       e->callback = callback;
+                       e->iface = iface;
+                       return;
+               }
+               last = e;
+       }
+
+       /* Allocate a new event if no free ones already allocated */
+       if (free_events) {
+               e = free_events;
+               free_events = e->next;
+       } else
+               e = xmalloc(sizeof(*e));
+       e->fd = fd;
+       e->callback = callback;
+       e->iface = iface;
+       e->next = NULL;
+       if (last)
+               last->next = e;
+       else
+               events = e;
+}
+
+void
+delete_event(int fd)
+{
+       struct event *e, *last = NULL;
+
+       for (e = events; e; e = e->next) {
+               if (e->fd == fd) {
+                       if (last)
+                               last->next = e->next;
+                       else
+                               events = e->next;
+                       e->next = free_events;
+                       free_events = e;
+                       break;
+               }
+               last = e;
+       }
+}
+
+void
+add_timeout_tv(const struct timeval *when,
+              void (*callback)(struct interface *), struct interface *iface)
+{
+       struct timeval w;
+       struct timeout *t, *tt = NULL;
+
+       get_monotonic(&now);
+       timeradd(&now, when, &w);
+
+       /* Remove existing timeout if present */
+       for (t = timeouts; t; t = t->next) {
+               if (t->callback == callback && t->iface == iface) {
+                       if (tt)
+                               tt->next = t->next;
+                       else
+                               timeouts = t->next;
+                       break;
+               }
+               tt = t;
+       }
+
+       if (!t) {
+               /* No existing, so allocate or grab one from the free pool */
+               if (free_timeouts) {
+                       t = free_timeouts;
+                       free_timeouts = t->next;
+               } else
+                       t = xmalloc(sizeof(*t));
+       }
+
+       t->when.tv_sec = w.tv_sec;
+       t->when.tv_usec = w.tv_usec;
+       t->callback = callback;
+       t->iface = iface;
+
+       /* The timeout list should be in chronological order,
+        * soonest first.
+        * This is the easiest algorithm - check the head, then middle
+        * and finally the end. */
+       if (!timeouts || timercmp(&t->when, &timeouts->when, <)) {
+               t->next = timeouts;
+               timeouts = t;
+               return;
+       } 
+       for (tt = timeouts; tt->next; tt = tt->next)
+               if (timercmp(&t->when, &tt->next->when, <)) {
+                       t->next = tt->next;
+                       tt->next = t;
+                       return;
+               }
+       tt->next = t;
+       t->next = NULL;
+}
+
+void
+add_timeout_sec(time_t when,
+               void (*callback)(struct interface *), struct interface *iface)
+{
+       struct timeval tv;
+
+       tv.tv_sec = when;
+       tv.tv_usec = 0;
+       add_timeout_tv(&tv, callback, iface);
+}
+
+/* This deletes all timeouts for the interface EXCEPT for ones with the
+ * callbacks given. Handy for deleting everything apart from the expire
+ * timeout. */
+void
+delete_timeouts(struct interface *iface,
+               void (*callback)(struct interface *), ...)
+{
+       struct timeout *t, *tt, *last = NULL;
+       va_list va;
+       void (*f)(struct interface *);
+
+       for (t = timeouts; t && (tt = t->next, 1); t = tt) {
+               if (t->iface == iface && t->callback != callback) {
+                       va_start(va, callback);
+                       while ((f = va_arg(va, void (*)(struct interface *))))
+                               if (f == t->callback)
+                                       break;
+                       va_end(va);
+                       if (!f) {
+                               if (last)
+                                       last->next = t->next;
+                               else
+                                       timeouts = t->next;
+                               t->next = free_timeouts;
+                               free_timeouts = t;
+                               continue;
+                       }
+               }
+       }
+}
+
+void
+delete_timeout(void (*callback)(struct interface *), struct interface *iface)
+{
+       struct timeout *t, *tt, *last = NULL;
+
+       for (t = timeouts; t && (tt = t->next, 1); t = tt) {
+               if (t->iface == iface &&
+                   (!callback || t->callback == callback))
+               {
+                       if (last)
+                               last->next = t->next;
+                       else
+                               timeouts = t->next;
+                       t->next = free_timeouts;
+                       free_timeouts = t;
+                       continue;
+               }
+               last = t;
+       }
+}
+
+void
+start_eloop(void)
+{
+       int msecs, n;
+       nfds_t nfds, i;
+       struct event *e;
+       struct timeout *t;
+       struct timeval tv;
+
+#ifdef DEBUG_MEMORY
+       atexit(cleanup);
+#endif
+
+       for (;;) {
+               /* Run all timeouts first.
+                * When we have one that has not yet occured,
+                * calculate milliseconds until it does for use in poll. */
+               if (timeouts) {
+                       if (timercmp(&now, &timeouts->when, >)) {
+                               t = timeouts;
+                               timeouts = timeouts->next;
+                               t->callback(t->iface);
+                               t->next = free_timeouts;
+                               free_timeouts = t;
+                               continue;
+                       }
+                       timersub(&timeouts->when, &now, &tv);
+                       if (tv.tv_sec > INT_MAX / 1000 ||
+                           (tv.tv_sec == INT_MAX / 1000 &&
+                            (tv.tv_usec + 999) / 1000 > INT_MAX % 1000))
+                               msecs = INT_MAX;
+                       else
+                               msecs = tv.tv_sec * 1000 +
+                                       (tv.tv_usec + 999) / 1000;
+               } else
+                       /* No timeouts, so wait forever. */
+                       msecs = -1;
+
+               /* Allocate memory for our pollfds as and when needed.
+                * We don't bother shrinking it. */
+               nfds = 0;
+               for (e = events; e; e = e->next)
+                       nfds++;
+               if (msecs == -1 && nfds == 0) {
+                       logger(LOG_ERR, "nothing to do");
+                       exit(EXIT_FAILURE);
+               }
+               if (nfds > fds_len) {
+                       free(fds);
+                       fds = xmalloc(sizeof(*fds) * nfds);
+                       fds_len = nfds;
+               }
+               nfds = 0;
+               for (e = events; e; e = e->next) {
+                       fds[nfds].fd = e->fd;
+                       fds[nfds].events = POLLIN;
+                       fds[nfds].revents = 0;
+                       nfds++;
+               }
+               n = poll(fds, nfds, msecs);
+               if (n == -1) {
+                       if (errno == EAGAIN || errno == EINTR) {
+                               get_monotonic(&now);
+                               continue;
+                       }
+                       logger(LOG_ERR, "poll: %s", strerror(errno));
+                       exit(EXIT_FAILURE);
+               }
+
+               /* Get the now time and process any triggered events. */
+               get_monotonic(&now);
+               if (n == 0)
+                       continue;
+               for (i = 0; i < nfds; i++) {
+                       if (!(fds[i].revents & (POLLIN | POLLHUP)))
+                               continue;
+                       for (e = events; e; e = e->next) {
+                               if (e->fd == fds[i].fd) {
+                                       e->callback(e->iface);
+                                       break;
+                               }
+                       }
+               }
+       }
+}
diff --git a/eloop.h b/eloop.h
new file mode 100644 (file)
index 0000000..18e64ee
--- /dev/null
+++ b/eloop.h
@@ -0,0 +1,44 @@
+/* 
+ * 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.
+ */
+
+#ifndef ELOOP_H
+#define ELOOP_H
+
+#include <time.h>
+
+#include "dhcpcd.h"
+
+void add_event(int fd, void (*)(struct interface *), struct interface *);
+void delete_event(int fd);
+void add_timeout_sec(time_t, void (*)(struct interface *), struct interface *);
+void add_timeout_tv(const struct timeval *,
+                   void (*)(struct interface *), struct interface *);
+void delete_timeout(void (*)(struct interface *), struct interface *);
+void delete_timeouts(struct interface *, void (*)(struct interface *), ...);
+void start_eloop(void);
+
+#endif
index bbf1a95d0a82a3108ad530bf76189ed7c4f94694..d44bf4e2e2a070a21fa6cd314de06a51390a272e 100644 (file)
--- a/if-bsd.c
+++ b/if-bsd.c
@@ -58,7 +58,7 @@
 #endif
 
 int
-if_address(const char *ifname, const struct in_addr *address,
+if_address(const struct interface *iface, const struct in_addr *address,
           const struct in_addr *netmask, const struct in_addr *broadcast,
           int action)
 {
@@ -74,7 +74,7 @@ if_address(const char *ifname, const struct in_addr *address,
                return -1;
 
        memset(&ifa, 0, sizeof(ifa));
-       strlcpy(ifa.ifra_name, ifname, sizeof(ifa.ifra_name));
+       strlcpy(ifa.ifra_name, iface->name, sizeof(ifa.ifra_name));
 
 #define ADDADDR(_var, _addr) \
        _s.sa = &_var; \
@@ -98,7 +98,7 @@ if_address(const char *ifname, const struct in_addr *address,
 }
 
 int
-if_route(const char *ifname, const struct in_addr *destination,
+if_route(const struct interface *iface, const struct in_addr *destination,
         const struct in_addr *netmask, const struct in_addr *gateway,
         _unused int metric, int action)
 {
@@ -120,8 +120,6 @@ if_route(const char *ifname, const struct in_addr *destination,
        } rtm;
        char *bp = rtm.buffer;
        size_t l;
-       unsigned char *hwaddr;
-       size_t hwlen = 0;
        int retval = 0;
 
        if ((s = socket(PF_ROUTE, SOCK_RAW, 0)) == -1)
@@ -156,21 +154,18 @@ if_route(const char *ifname, const struct in_addr *destination,
 
        if (gateway->s_addr == INADDR_ANY) {
                /* Make us a link layer socket */
-               hwaddr = xmalloc(sizeof(unsigned char) * HWADDR_LEN);
-               do_interface(ifname, hwaddr, &hwlen, NULL, 0, 0);
                memset(&su, 0, sizeof(su));
                su.sdl.sdl_len = sizeof(su.sdl);
                su.sdl.sdl_family = AF_LINK;
-               su.sdl.sdl_nlen = strlen(ifname);
-               memcpy(&su.sdl.sdl_data, ifname, (size_t)su.sdl.sdl_nlen);
-               su.sdl.sdl_alen = hwlen;
+               su.sdl.sdl_nlen = strlen(iface->name);
+               memcpy(&su.sdl.sdl_data, iface->name, (size_t)su.sdl.sdl_nlen);
+               su.sdl.sdl_alen = iface->hwlen;
                memcpy(((unsigned char *)&su.sdl.sdl_data) + su.sdl.sdl_nlen,
-                      hwaddr, (size_t)su.sdl.sdl_alen);
+                      iface->hwaddr, (size_t)su.sdl.sdl_alen);
 
                l = SA_SIZE(&(su.sa));
                memcpy(bp, &su, l);
                bp += l;
-               free(hwaddr);
        } else {
                rtm.hdr.rtm_flags |= RTF_GATEWAY;
                ADDADDR(gateway);
@@ -187,34 +182,28 @@ if_route(const char *ifname, const struct in_addr *destination,
 }
 
 int
-open_link_socket(struct interface *iface)
+open_link_socket(void)
 {
        int fd;
 
        fd = socket(PF_ROUTE, SOCK_RAW, 0);
-       if (fd == -1)
-               return -1;
-       set_cloexec(fd);
-       if (iface->link_fd != -1)
-               close(iface->link_fd);
-       iface->link_fd = fd;
-       return 0;
+       if (fd != -1)
+               set_cloexec(fd);
+       return fd;
 }
 
 #define BUFFER_LEN     2048
 int
-link_changed(struct interface *iface)
+link_changed(int fd, const struct interface *ifaces)
 {
        char buffer[2048], *p;
        ssize_t bytes;
        struct rt_msghdr *rtm;
        struct if_msghdr *ifm;
-       int i;
+       const struct interface *iface;
 
-       if ((i = if_nametoindex(iface->name)) == -1)
-               return -1;
        for (;;) {
-               bytes = recv(iface->link_fd, buffer, BUFFER_LEN, MSG_DONTWAIT);
+               bytes = recv(fd, buffer, BUFFER_LEN, MSG_DONTWAIT);
                if (bytes == -1) {
                        if (errno == EAGAIN)
                                return 0;
@@ -230,12 +219,19 @@ link_changed(struct interface *iface)
                        if (rtm->rtm_type != RTM_IFINFO)
                                continue;
                        ifm = (struct if_msghdr *)p;
-                       if (ifm->ifm_index != i)
-                               continue;
-
-                       /* Link changed */
-                       return 1;
+                       for (iface = ifaces; iface; iface = iface->next)
+                               if (ifm->ifm_index == if_nametoindex(iface->name))
+                                       return 1;
                }
        }
        return 0;
 }
+
+struct interface *
+discover_interfaces(int argc, char * const *argv)
+{
+       struct interface *ifaces = NULL;
+
+       do_interface(NULL, &ifaces, argc, argv, NULL, NULL, 2);
+       return ifaces;
+}
index afb0ea6416fde8424870896b96ffd9a3e4fab714..45b5b2b77ed8519757e06261f145f619a1eef8a0 100644 (file)
@@ -40,6 +40,7 @@
 #include <netpacket/packet.h>
 
 #include <errno.h>
+#include <ctype.h>
 #include <stddef.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -59,7 +60,7 @@
 #define BUFFERLEN 256
 
 int
-open_link_socket(struct interface *iface)
+open_link_socket(void)
 {
        int fd;
        struct sockaddr_nl nl;
@@ -72,16 +73,13 @@ open_link_socket(struct interface *iface)
        if (bind(fd, (struct sockaddr *)&nl, sizeof(nl)) == -1)
                return -1;
        set_cloexec(fd);
-       if (iface->link_fd != -1)
-               close(iface->link_fd);
-       iface->link_fd = fd;
-       return 0;
+       return fd;
 }
 
 static int
 get_netlink(int fd, int flags,
-           int (*callback)(struct nlmsghdr *, const char *),
-           const char *ifname)
+           int (*callback)(struct nlmsghdr *, const struct interface *),
+           const struct interface *iface)
 {
        char *buffer = NULL;
        ssize_t bytes;
@@ -104,7 +102,7 @@ get_netlink(int fd, int flags,
                     NLMSG_OK(nlm, (size_t)bytes);
                     nlm = NLMSG_NEXT(nlm, bytes))
                {
-                       r = callback(nlm, ifname);
+                       r = callback(nlm, iface);
                        if (r != 0)
                                goto eexit;
                }
@@ -116,7 +114,7 @@ eexit:
 }
 
 static int
-err_netlink(struct nlmsghdr *nlm, _unused const char *ifname)
+err_netlink(struct nlmsghdr *nlm, _unused const struct interface *iface)
 {
        struct nlmsgerr *err;
        int l;
@@ -136,12 +134,13 @@ err_netlink(struct nlmsghdr *nlm, _unused const char *ifname)
 }
 
 static int
-link_netlink(struct nlmsghdr *nlm, const char *ifname)
+link_netlink(struct nlmsghdr *nlm, const struct interface *ifaces)
 {
        int len;
        struct rtattr *rta;
        struct ifinfomsg *ifi;
        char ifn[IF_NAMESIZE + 1];
+       const struct interface *iface;
 
        if (nlm->nlmsg_type != RTM_NEWLINK && nlm->nlmsg_type != RTM_DELLINK)
                return 0;
@@ -171,16 +170,16 @@ link_netlink(struct nlmsghdr *nlm, const char *ifname)
                rta = RTA_NEXT(rta, len);
        }
 
-       if (strncmp(ifname, ifn, sizeof(ifn)) == 0)
-               return 1;
+       for (iface = ifaces; iface; iface = iface->next)
+               if (strcmp(iface->name, ifn) == 0)
+                       return 1;
        return 0;
 }
 
 int
-link_changed(struct interface *iface)
+link_changed(int fd, const struct interface *iface)
 {
-       return get_netlink(iface->link_fd, MSG_DONTWAIT,
-                          &link_netlink, iface->name);
+       return get_netlink(fd, MSG_DONTWAIT, &link_netlink, iface);
 }
 
 static int
@@ -279,7 +278,7 @@ struct nlmr
 };
 
 int
-if_address(const char *ifname,
+if_address(const struct interface *iface,
           const struct in_addr *address, const struct in_addr *netmask,
           const struct in_addr *broadcast, int action)
 {
@@ -294,7 +293,7 @@ if_address(const char *ifname,
                nlm->hdr.nlmsg_type = RTM_NEWADDR;
        } else
                nlm->hdr.nlmsg_type = RTM_DELADDR;
-       if (!(nlm->ifa.ifa_index = if_nametoindex(ifname))) {
+       if (!(nlm->ifa.ifa_index = if_nametoindex(iface->name))) {
                free(nlm);
                errno = ENODEV;
                return -1;
@@ -303,7 +302,7 @@ if_address(const char *ifname,
        nlm->ifa.ifa_prefixlen = inet_ntocidr(*netmask);
        /* This creates the aliased interface */
        add_attr_l(&nlm->hdr, sizeof(*nlm), IFA_LABEL,
-                  ifname, strlen(ifname) + 1);
+                  iface->name, strlen(iface->name) + 1);
        add_attr_l(&nlm->hdr, sizeof(*nlm), IFA_LOCAL,
                   &address->s_addr, sizeof(address->s_addr));
        if (action >= 0)
@@ -317,7 +316,7 @@ if_address(const char *ifname,
 }
 
 int
-if_route(const char *ifname,
+if_route(const struct interface *iface,
         const struct in_addr *destination, const struct in_addr *netmask,
         const struct in_addr *gateway, int metric, int action)
 {
@@ -326,7 +325,7 @@ if_route(const char *ifname,
        int retval = 0;
 
 
-       if (!(ifindex = if_nametoindex(ifname))) {
+       if (!(ifindex = if_nametoindex(iface->name))) {
                errno = ENODEV;
                return -1;
        }
@@ -370,3 +369,49 @@ if_route(const char *ifname,
        free(nlm);
        return retval;
 }
+
+struct interface *
+discover_interfaces(int argc, char * const *argv)
+{
+       FILE *f;
+       char *buffer = NULL, *p;
+       size_t len = 0, ln = 0, n;
+       int i, s;
+       struct interface *ifaces = NULL, *ifp, *ifl;
+
+       if ((f = fopen("/proc/net/dev", "r"))) {
+               while (get_line(&buffer, &len, f)) {
+                       if (++ln < 2)
+                               continue;
+                       p = buffer;
+                       while (isspace((unsigned int)*p))
+                               p++;
+                       n = strcspn(p, ": \t");
+                       p[n]= '\0';
+                       ifl = NULL;
+                       for (ifp = ifaces; ifp; ifp = ifp->next) {
+                               if (strcmp(ifp->name, p) == 0)
+                                       break;
+                               ifl = ifp;
+                       }
+                       if (ifp)
+                               continue;
+                       if (argc > 0) {
+                               for (i = 0; i < argc; i++)
+                                       if (strcmp(argv[i], p) == 0)
+                                               break;
+                               if (i == argc)
+                                       continue;
+                       }
+                       if (ifp = init_interface(p)) {
+                               if (ifl)
+                                       ifl->next =ifp; 
+                               else
+                                       ifaces = ifp;
+                       }
+               }
+               fclose(f);
+               free(buffer);
+       }
+       return ifaces;
+}
diff --git a/if-options.c b/if-options.c
new file mode 100644 (file)
index 0000000..836bf8d
--- /dev/null
@@ -0,0 +1,653 @@
+/* 
+ * 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/types.h>
+
+#include <arpa/inet.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <getopt.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <time.h>
+
+#include "common.h"
+#include "config.h"
+#include "dhcpf.h"
+#include "if-options.h"
+#include "logger.h"
+#include "net.h"
+
+/* Don't set any optional arguments here so we retain POSIX
+ * compatibility with getopt */
+#define OPTS "bc:df:h:i:kl:m:no:pqr:s:t:u:v:xABC:DEF:GI:KLO:Q:TVX:"
+
+const struct option cf_options[] = {
+       {"background",    no_argument,        NULL, 'b'},
+       {"script",        required_argument,  NULL, 'c'},
+       {"debug",         no_argument,        NULL, 'd'},
+       {"config",        required_argument,  NULL, 'f'},
+       {"hostname",      optional_argument,  NULL, 'h'},
+       {"vendorclassid", optional_argument,  NULL, 'i'},
+       {"release",       no_argument,        NULL, 'k'},
+       {"leasetime",     required_argument,  NULL, 'l'},
+       {"metric",        required_argument,  NULL, 'm'},
+       {"rebind",        no_argument,        NULL, 'n'},
+       {"option",        required_argument,  NULL, 'o'},
+       {"persistent",    no_argument,        NULL, 'p'},
+       {"quiet",         no_argument,        NULL, 'q'},
+       {"request",       optional_argument,  NULL, 'r'},
+       {"inform",        optional_argument,  NULL, 's'},
+       {"timeout",       required_argument,  NULL, 't'},
+       {"userclass",     required_argument,  NULL, 'u'},
+       {"vendor",        required_argument,  NULL, 'v'},
+       {"exit",          no_argument,        NULL, 'x'},
+       {"noarp",         no_argument,        NULL, 'A'},
+       {"nobackground",  no_argument,        NULL, 'B'},
+       {"nohook",        required_argument,  NULL, 'C'},
+       {"duid",          no_argument,        NULL, 'D'},
+       {"lastlease",     no_argument,        NULL, 'E'},
+       {"fqdn",          optional_argument,  NULL, 'F'},
+       {"nogateway",     no_argument,        NULL, 'G'},
+       {"clientid",      optional_argument,  NULL, 'I'},
+       {"nolink",        no_argument,        NULL, 'K'},
+       {"noipv4ll",      no_argument,        NULL, 'L'},
+       {"nooption",      optional_argument,  NULL, 'O'},
+       {"require",       required_argument,  NULL, 'Q'},
+       {"test",          no_argument,        NULL, 'T'},
+       {"variables",     no_argument,        NULL, 'V'},
+       {"blacklist",     required_argument,  NULL, 'X'},
+       {NULL,            0,                  NULL, '\0'}
+};
+
+static int
+atoint(const char *s)
+{
+       char *t;
+       long n;
+
+       errno = 0;
+       n = strtol(s, &t, 0);
+       if ((errno != 0 && n == 0) || s == t ||
+           (errno == ERANGE && (n == LONG_MAX || n == LONG_MIN)))
+       {
+               logger(LOG_ERR, "`%s' out of range", s);
+               return -1;
+       }
+
+       return (int)n;
+}
+
+static char * 
+add_environ(struct if_options *ifo, const char *value, int uniq)
+{
+       char **newlist;
+       char **lst = ifo->environ;
+       size_t i = 0, l, lv;
+       char *match = NULL, *p;
+
+       match = xstrdup(value);
+       p = strchr(match, '=');
+       if (p)
+               *p++ = '\0';
+       l = strlen(match);
+
+       while (lst && lst[i]) {
+               if (match && strncmp(lst[i], match, l) == 0) {
+                       if (uniq) {
+                               free(lst[i]);
+                               lst[i] = xstrdup(value);
+                       } else {
+                               /* Append a space and the value to it */
+                               l = strlen(lst[i]);
+                               lv = strlen(p);
+                               lst[i] = xrealloc(lst[i], l + lv + 2);
+                               lst[i][l] = ' ';
+                               memcpy(lst[i] + l + 1, p, lv);
+                               lst[i][l + lv + 1] = '\0';
+                       }
+                       free(match);
+                       return lst[i];
+               }
+               i++;
+       }
+
+       newlist = xrealloc(lst, sizeof(char *) * (i + 2));
+       newlist[i] = xstrdup(value);
+       newlist[i + 1] = NULL;
+       ifo->environ = newlist;
+       free(match);
+       return newlist[i];
+}
+
+#define parse_string(buf, len, arg) parse_string_hwaddr(buf, len, arg, 0)
+static ssize_t
+parse_string_hwaddr(char *sbuf, ssize_t slen, const char *str, int clid)
+{
+       ssize_t l;
+       const char *p;
+       int i, punt_last = 0;
+       char c[4];
+
+       /* If surrounded by quotes then it's a string */
+       if (*str == '"') {
+               str++;
+               l = strlen(str);
+               p = str + l - 1;
+               if (*p == '"')
+                       punt_last = 1;
+       } else {
+               l = hwaddr_aton(NULL, str);
+               if (l > 1) {
+                       if (l > slen) {
+                               errno = ENOBUFS;
+                               return -1;
+                       }
+                       hwaddr_aton((uint8_t *)sbuf, str);
+                       return l;
+               }
+       }
+
+       /* Process escapes */
+       l = 0;
+       /* If processing a string on the clientid, first byte should be
+        * 0 to indicate a non hardware type */
+       if (clid) {
+               *sbuf++ = 0;
+               l++;
+       }
+       c[3] = '\0';
+       while (*str) {
+               if (++l > slen) {
+                       errno = ENOBUFS;
+                       return -1;
+               }
+               if (*str == '\\') {
+                       str++;
+                       switch(*str++) {
+                       case '\0':
+                               break;
+                       case 'b':
+                               *sbuf++ = '\b';
+                               break;
+                       case 'n':
+                               *sbuf++ = '\n';
+                               break;
+                       case 'r':
+                               *sbuf++ = '\r';
+                               break;
+                       case 't':
+                               *sbuf++ = '\t';
+                               break;
+                       case 'x':
+                               /* Grab a hex code */
+                               c[1] = '\0';
+                               for (i = 0; i < 2; i++) {
+                                       if (isxdigit((unsigned char)*str) == 0)
+                                               break;
+                                       c[i] = *str++;
+                               }
+                               if (c[1] != '\0') {
+                                       c[2] = '\0';
+                                       *sbuf++ = strtol(c, NULL, 16);
+                               } else
+                                       l--;
+                               break;
+                       case '0':
+                               /* Grab an octal code */
+                               c[2] = '\0';
+                               for (i = 0; i < 3; i++) {
+                                       if (*str < '0' || *str > '7')
+                                               break;
+                                       c[i] = *str++;
+                               }
+                               if (c[2] != '\0') {
+                                       i = strtol(c, NULL, 8);
+                                       if (i > 255)
+                                               i = 255;
+                                       *sbuf ++= i;
+                               } else
+                                       l--;
+                               break;
+                       default:
+                               *sbuf++ = *str++;
+                       }
+               } else
+                       *sbuf++ = *str++;
+       }
+       if (punt_last)
+               *--sbuf = '\0';
+       return l;
+}
+
+static int
+parse_option(struct if_options *ifo, int opt, const char *arg)
+{
+       int i;
+       char *p;
+       ssize_t s;
+       struct in_addr addr;
+
+       switch(opt) {
+       case 'b':
+               ifo->options |= DHCPCD_BACKGROUND;
+               break;
+       case 'c':
+               strlcpy(ifo->script, arg, sizeof(ifo->script));
+               break;
+       case 'd':
+               break;
+       case 'h':
+               if (arg)
+                       s = parse_string(ifo->hostname + 1,
+                                        HOSTNAME_MAX_LEN, arg);
+               else
+                       s = 0;
+               if (s == -1) {
+                       logger(LOG_ERR, "hostname: %s", strerror(errno));
+                       return -1;
+               }
+               if (s != 0 && ifo->hostname[1] == '.') {
+                       logger(LOG_ERR, "hostname cannot begin with a .");
+                       return -1;
+               }
+               ifo->hostname[0] = (uint8_t)s;
+               break;
+       case 'i':
+               if (arg)
+                       s = parse_string((char *)ifo->vendorclassid + 1,
+                                        VENDORCLASSID_MAX_LEN, arg);
+               else
+                       s = 0;
+               if (s == -1) {
+                       logger(LOG_ERR, "vendorclassid: %s", strerror(errno));
+                       return -1;
+               }
+               *ifo->vendorclassid = (uint8_t)s;
+               break;
+       case 'l':
+               if (*arg == '-') {
+                       logger(LOG_ERR,
+                              "leasetime must be a positive value");
+                       return -1;
+               }
+               errno = 0;
+               ifo->leasetime = (uint32_t)strtol(arg, NULL, 0);
+               if (errno == EINVAL || errno == ERANGE) {
+                       logger(LOG_ERR, "`%s' out of range", arg);
+                       return -1;
+               }
+               break;
+       case 'm':
+               ifo->metric = atoint(arg);
+               if (ifo->metric < 0) {
+                       logger(LOG_ERR, "metric must be a positive value");
+                       return -1;
+               }
+               break;
+       case 'o':
+               if (make_option_mask(ifo->requestmask, arg, 1) != 0) {
+                       logger(LOG_ERR, "unknown option `%s'", arg);
+                       return -1;
+               }
+               break;
+       case 'p':
+               ifo->options |= DHCPCD_PERSISTENT;
+               break;
+       case 'q':
+               setloglevel(LOG_WARNING);
+               break;
+       case 's':
+               ifo->options |= DHCPCD_INFORM;
+               ifo->options |= DHCPCD_PERSISTENT;
+               ifo->options &= ~DHCPCD_ARP;
+               if (!arg || *arg == '\0') {
+                       ifo->request_address.s_addr = 0;
+                       break;
+               } else {
+                       if ((p = strchr(arg, '/'))) {
+                               /* nullify the slash, so the -r option
+                                * can read the address */
+                               *p++ = '\0';
+                               if (sscanf(p, "%d", &i) != 1 ||
+                                   inet_cidrtoaddr(i, &ifo->request_netmask) != 0)
+                               {
+                                       logger(LOG_ERR,
+                                              "`%s' is not a valid CIDR",
+                                              p);
+                                       return -1;
+                               }
+                       }
+               }
+               /* FALLTHROUGH */
+       case 'r':
+               if (!(ifo->options & DHCPCD_INFORM))
+                       ifo->options |= DHCPCD_REQUEST;
+               if (arg && !inet_aton(arg, &ifo->request_address)) {
+                       logger(LOG_ERR, "`%s' is not a valid IP address",
+                              arg);
+                       return -1;
+               }
+               break;
+       case 't':
+               ifo->timeout = atoint(arg);
+               if (ifo->timeout < 0) {
+                       logger (LOG_ERR, "timeout must be a positive value");
+                       return -1;
+               }
+               break;
+       case 'u':
+               s = USERCLASS_MAX_LEN - ifo->userclass[0] - 1;
+               s = parse_string((char *)ifo->userclass + ifo->userclass[0] + 2,
+                                s, arg);
+               if (s == -1) {
+                       logger(LOG_ERR, "userclass: %s", strerror(errno));
+                       return -1;
+               }
+               if (s != 0) {
+                       ifo->userclass[ifo->userclass[0] + 1] = s;
+                       ifo->userclass[0] += s + 1;
+               }
+               break;
+       case 'v':
+               p = strchr(arg, ',');
+               if (!p || !p[1]) {
+                       logger(LOG_ERR, "invalid vendor format");
+                       return -1;
+               }
+               *p = '\0';
+               i = atoint(arg);
+               arg = p + 1;
+               if (i < 1 || i > 254) {
+                       logger(LOG_ERR, "vendor option should be between"
+                                       " 1 and 254 inclusive");
+                       return -1;
+               }
+               s = VENDOR_MAX_LEN - ifo->vendor[0] - 2;
+               if (inet_aton(arg, &addr) == 1) {
+                       if (s < 6) {
+                               s = -1;
+                               errno = ENOBUFS;
+                       } else
+                               memcpy(ifo->vendor + ifo->vendor[0] + 3,
+                                      &addr.s_addr, sizeof(addr.s_addr));
+               } else {
+                       s = parse_string((char *)ifo->vendor + ifo->vendor[0] + 3,
+                                        s, arg);
+               }
+               if (s == -1) {
+                       logger(LOG_ERR, "vendor: %s", strerror(errno));
+                       return -1;
+               }
+               if (s != 0) {
+                       ifo->vendor[ifo->vendor[0] + 1] = i;
+                       ifo->vendor[ifo->vendor[0] + 2] = s;
+                       ifo->vendor[0] += s + 2;
+               }
+               break;
+       case 'x':
+               break;
+       case 'A':
+               ifo->options &= ~DHCPCD_ARP;
+               /* IPv4LL requires ARP */
+               ifo->options &= ~DHCPCD_IPV4LL;
+               break;
+       case 'B':
+               ifo->options &= ~DHCPCD_DAEMONISE;
+               break;
+       case 'C':
+               /* Commas to spaces for shell */
+               while ((p = strchr(arg, ',')))
+                       *p = ' ';
+               s = strlen("skip_hooks=") + strlen(arg) + 1;
+               p = xmalloc(sizeof(char) * s);
+               snprintf(p, s, "skip_hooks=%s", arg);
+               add_environ(ifo, p, 0);
+               free(p);
+               break;
+       case 'D':
+               ifo->options |= DHCPCD_DUID;
+               break;
+       case 'E':
+               ifo->options |= DHCPCD_LASTLEASE;
+               break;
+       case 'F':
+               if (!arg) {
+                       ifo->fqdn = FQDN_BOTH;
+                       break;
+               }
+               if (strcmp(arg, "none") == 0)
+                       ifo->fqdn = FQDN_NONE;
+               else if (strcmp(arg, "ptr") == 0)
+                       ifo->fqdn = FQDN_PTR;
+               else if (strcmp(arg, "both") == 0)
+                       ifo->fqdn = FQDN_BOTH;
+               else if (strcmp(arg, "disable") == 0)
+                       ifo->fqdn = FQDN_DISABLE;
+               else {
+                       logger(LOG_ERR, "invalid value `%s' for FQDN", arg);
+                       return -1;
+               }
+               break;
+       case 'G':
+               ifo->options &= ~DHCPCD_GATEWAY;
+               break;
+       case 'I':
+               /* Strings have a type of 0 */;
+               ifo->clientid[1] = 0;
+               if (arg)
+                       s = parse_string_hwaddr((char *)ifo->clientid + 1,
+                                               CLIENTID_MAX_LEN, arg, 1);
+               else
+                       s = 0;
+               if (s == -1) {
+                       logger(LOG_ERR, "clientid: %s", strerror(errno));
+                       return -1;
+               }
+               ifo->clientid[0] = (uint8_t)s;
+               if (s == 0) {
+                       ifo->options &= ~DHCPCD_DUID;
+                       ifo->options &= ~DHCPCD_CLIENTID;
+               }
+               break;
+       case 'K':
+               ifo->options &= ~DHCPCD_LINK;
+               break;
+       case 'L':
+               ifo->options &= ~DHCPCD_IPV4LL;
+               break;
+       case 'O':
+               if (make_option_mask(ifo->requestmask, arg, -1) != 0 ||
+                   make_option_mask(ifo->requiremask, arg, -1) != 0 ||
+                   make_option_mask(ifo->nomask, arg, 1) != 0)
+               {
+                       logger(LOG_ERR, "unknown option `%s'", arg);
+                       return -1;
+               }
+               break;
+       case 'Q':
+               if (make_option_mask(ifo->requiremask, arg, 1) != 0 ||
+                   make_option_mask(ifo->requestmask, arg, 1) != 0)
+               {
+                       logger(LOG_ERR, "unknown option `%s'", arg);
+                       return -1;
+               }
+               break;
+       case 'X':
+               if (!inet_aton(arg, &addr)) {
+                       logger(LOG_ERR, "`%s' is not a valid IP address",
+                              arg);
+                       return -1;
+               }
+               ifo->blacklist = xrealloc(ifo->blacklist,
+                   sizeof(in_addr_t) * (ifo->blacklist_len + 1));
+               ifo->blacklist[ifo->blacklist_len] = addr.s_addr;
+               ifo->blacklist_len++;
+               break;
+       default:
+               return 0;
+       }
+
+       return 1;
+}
+
+static int
+parse_config_line(struct if_options *ifo, const char *opt, char *line)
+{
+       unsigned int i;
+
+       for (i = 0; i < sizeof(cf_options) / sizeof(cf_options[0]); i++) {
+               if (!cf_options[i].name ||
+                   strcmp(cf_options[i].name, opt) != 0)
+                       continue;
+
+               if (cf_options[i].has_arg == required_argument && !line) {
+                       fprintf(stderr,
+                               PACKAGE ": option requires an argument -- %s\n",
+                               opt);
+                       return -1;
+               }
+
+               return parse_option(ifo, cf_options[i].val, line);
+       }
+
+       fprintf(stderr, PACKAGE ": unknown option -- %s\n", opt);
+       return -1;
+}
+
+struct if_options *
+read_config(const char *file, const char *ifname)
+{
+       struct if_options *ifo;
+       FILE *f;
+       size_t len = 0;
+       char *line, *option, *p, *buffer = NULL;
+       int skip = 0;
+
+       /* Seed our default options */
+       ifo = xzalloc(sizeof(*ifo));
+       ifo->options |= DHCPCD_CLIENTID | DHCPCD_GATEWAY | DHCPCD_DAEMONISE;
+       ifo->options |= DHCPCD_ARP | DHCPCD_IPV4LL | DHCPCD_LINK;
+       ifo->timeout = DEFAULT_TIMEOUT;
+       gethostname(ifo->hostname + 1, sizeof(ifo->hostname));
+       if (strcmp(ifo->hostname + 1, "(none)") == 0 ||
+           strcmp(ifo->hostname + 1, "localhost") == 0)
+               ifo->hostname[1] = '\0';
+       *ifo->hostname = strlen(ifo->hostname + 1);
+       strlcpy(ifo->script, SCRIPT, sizeof(ifo->script));
+       ifo->vendorclassid[0] = snprintf((char *)ifo->vendorclassid + 1,
+                                            VENDORCLASSID_MAX_LEN,
+                                            "%s %s", PACKAGE, VERSION);
+
+       /* Parse our options file */
+       f = fopen(file, "r");
+       if (!f)
+               return ifo;
+
+       while ((get_line(&buffer, &len, f))) {
+               line = buffer;
+               while ((option = strsep(&line, " \t")))
+                       if (*option != '\0')
+                               break;
+               if (!option || *option == '\0' || *option == '#')
+                       continue;
+               /* Trim leading whitespace */
+               if (line) {
+                       while (*line != '\0' && (*line == ' ' || *line == '\t'))
+                               line++;
+               }
+               /* Trim trailing whitespace */
+               if (line && *line) {
+                       p = line + strlen(line) - 1;
+                       while (p != line &&
+                                       (*p == ' ' || *p == '\t') &&
+                                       *(p - 1) != '\\')
+                               *p-- = '\0';
+               }
+               /* Start of an interface block, skip if not ours */
+               if (strcmp(option, "interface") == 0) {
+                       if (ifname && line && strcmp(line, ifname) == 0)
+                               skip = 0;
+                       else
+                               skip = 1;
+                       continue;
+               }
+               if (skip)
+                       continue;
+               if (parse_config_line(ifo, option, line) != 1) {
+                       break;
+               }
+       }
+       free(buffer);
+       fclose(f);
+
+       /* Terminate the encapsulated options */
+       if (ifo->vendor[0]) {
+               ifo->vendor[0]++;
+               ifo->vendor[ifo->vendor[0]] = DHO_END;
+       }
+       return ifo;
+}
+
+int
+add_options(struct if_options *ifo, int argc, char **argv)
+{
+       int oi, opt, r = 1;
+
+       optind = 0;
+       while ((opt = getopt_long(argc, argv, IF_OPTS, cf_options, &oi)) != -1)
+       {
+               r = parse_option(ifo, opt, optarg);
+               if (r != 1)
+                       break;
+       }
+       /* Terminate the encapsulated options */
+       if (r == 1 && ifo->vendor[0]) {
+               ifo->vendor[0]++;
+               ifo->vendor[ifo->vendor[0]] = DHO_END;
+       }
+       return r;
+}
+
+void
+free_options(struct if_options *ifo)
+{
+       size_t i;
+
+       if (ifo->environ) {
+               i = 0;
+               while (ifo->environ[i])
+                       free(ifo->environ[i++]);
+               free(ifo->environ);
+       }
+       free(ifo->blacklist);
+       free(ifo);
+}
diff --git a/if-options.h b/if-options.h
new file mode 100644 (file)
index 0000000..154aef2
--- /dev/null
@@ -0,0 +1,100 @@
+/* 
+ * 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.
+ */
+
+#ifndef IF_OPTIONS_H
+#define IF_OPTIONS_H
+
+#include <net/if.h>
+#include <netinet/in.h>
+
+#include <getopt.h>
+#include <limits.h>
+
+/* Don't set any optional arguments here so we retain POSIX
+ *  * compatibility with getopt */
+#define IF_OPTS "bc:df:h:i:kl:m:no:pqr:s:t:u:v:xABC:DEF:GI:KLO:Q:TVX:"
+
+#define DEFAULT_TIMEOUT                30
+#define DEFAULT_LEASETIME      3600    /* 1 hour */
+
+#define HOSTNAME_MAX_LEN       250     /* 255 - 3 (FQDN) - 2 (DNS enc) */
+#define VENDORCLASSID_MAX_LEN  48
+#define CLIENTID_MAX_LEN       48
+#define USERCLASS_MAX_LEN      255
+#define VENDOR_MAX_LEN         255
+
+#define DHCPCD_ARP             (1 << 0)
+#define DHCPCD_DOMAIN          (1 << 2)
+#define DHCPCD_GATEWAY         (1 << 3)
+#define DHCPCD_LASTLEASE       (1 << 7)
+#define DHCPCD_INFORM          (1 << 8)
+#define DHCPCD_REQUEST         (1 << 9)
+#define DHCPCD_IPV4LL          (1 << 10)
+#define DHCPCD_DUID            (1 << 11)
+#define DHCPCD_PERSISTENT      (1 << 12)
+#define DHCPCD_DAEMONISE       (1 << 14)
+#define DHCPCD_DAEMONISED      (1 << 15)
+#define DHCPCD_TEST            (1 << 16)
+#define DHCPCD_FORKED          (1 << 17)
+#define DHCPCD_HOSTNAME                (1 << 18)
+#define DHCPCD_CLIENTID                (1 << 19)
+#define DHCPCD_LINK            (1 << 20)
+#define DHCPCD_BACKGROUND      (1 << 21)
+
+extern const struct option cf_options[];
+
+struct if_options {
+       int metric;
+       uint8_t requestmask[256 / 8];
+       uint8_t requiremask[256 / 8];
+       uint8_t nomask[256 / 8];
+       uint32_t leasetime;
+       time_t timeout;
+       int options;
+
+       struct in_addr request_address;
+       struct in_addr request_netmask;
+
+       char **environ;
+       char script[PATH_MAX];
+
+       char hostname[HOSTNAME_MAX_LEN + 1];
+       int fqdn;
+       uint8_t vendorclassid[VENDORCLASSID_MAX_LEN + 1];
+       char clientid[CLIENTID_MAX_LEN + 1];
+       uint8_t userclass[USERCLASS_MAX_LEN + 1];
+       uint8_t vendor[VENDOR_MAX_LEN + 1];
+
+       size_t blacklist_len;
+       in_addr_t *blacklist;
+};
+
+struct if_options *read_config(const char *, const char *);
+int add_options(struct if_options *, int, char **);
+void free_options(struct if_options *);
+
+#endif
diff --git a/ipv4ll.c b/ipv4ll.c
new file mode 100644 (file)
index 0000000..3e8786f
--- /dev/null
+++ b/ipv4ll.c
@@ -0,0 +1,124 @@
+/* 
+ * 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 <errno.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "arp.h"
+#include "common.h"
+#include "dhcpcd.h"
+#include "eloop.h"
+#include "if-options.h"
+#include "ipv4ll.h"
+#include "logger.h"
+#include "net.h"
+
+static struct dhcp_message*
+make_ipv4ll_lease(uint32_t old_addr)
+{
+       uint32_t u32;
+       struct dhcp_message *dhcp;
+       uint8_t *p;
+
+       dhcp = xzalloc(sizeof(*dhcp));
+       /* Put some LL options in */
+       p = dhcp->options;
+       *p++ = DHO_SUBNETMASK;
+       *p++ = sizeof(u32);
+       u32 = htonl(LINKLOCAL_MASK);
+       memcpy(p, &u32, sizeof(u32));
+       p += sizeof(u32);
+       *p++ = DHO_BROADCAST;
+       *p++ = sizeof(u32);
+       u32 = htonl(LINKLOCAL_BRDC);
+       memcpy(p, &u32, sizeof(u32));
+       p += sizeof(u32);
+       *p++ = DHO_END;
+
+       for (;;) {
+               dhcp->yiaddr = htonl(LINKLOCAL_ADDR |
+                                    (((uint32_t)abs((int)arc4random())
+                                      % 0xFD00) + 0x0100));
+               if (dhcp->yiaddr != old_addr &&
+                   IN_LINKLOCAL(ntohl(dhcp->yiaddr)))
+                       break;
+       }
+       return dhcp;
+}
+
+void
+start_ipv4ll(struct interface *iface)
+{
+       iface->state->probes = 0;
+       iface->state->claims = 0;
+       if (iface->addr.s_addr) {
+               iface->state->conflicts = 0;
+               if (IN_LINKLOCAL(htonl(iface->addr.s_addr))) {
+                       send_arp_announce(iface);
+                       return;
+               }
+       }
+
+       logger(LOG_INFO, "%s: probing for an IPv4LL address", iface->name);
+       delete_timeout(NULL, iface);
+       iface->state->state = DHS_PROBING;
+       free(iface->state->offer);
+       iface->state->offer = make_ipv4ll_lease(0);
+       send_arp_probe(iface);
+}
+
+void
+handle_ipv4ll_failure(struct interface *iface)
+{
+       time_t up;
+
+       if (iface->state->fail.s_addr == iface->state->lease.addr.s_addr) {
+               if (iface->state->state == DHS_PROBING)
+                       drop_config(iface, "EXPIRE");
+               else {
+                       up = uptime();
+                       if (iface->state->defend + DEFEND_INTERVAL > up) {
+                               drop_config(iface, "EXPIRE");
+                               iface->state->conflicts = -1;
+                       } else {
+                               iface->state->defend = up;
+                               return;
+                       }
+               }
+       }
+
+       close_sockets(iface);
+       if (++iface->state->conflicts > MAX_CONFLICTS) {
+               logger(LOG_ERR, "%s: failed to acquire an IPv4LL address",
+                               iface->name);
+               iface->state->interval = RATE_LIMIT_INTERVAL / 2;
+               start_discover(iface);
+       } else
+               add_timeout_sec(PROBE_WAIT, start_ipv4ll, iface);
+}
diff --git a/ipv4ll.h b/ipv4ll.h
new file mode 100644 (file)
index 0000000..82f8d59
--- /dev/null
+++ b/ipv4ll.h
@@ -0,0 +1,35 @@
+/* 
+ * 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.
+ */
+
+#ifndef IPV4LL_H
+#define IPV4LL_H
+
+#include "net.h"
+
+void start_ipv4ll(struct interface *);
+void handle_ipv4ll_failure(struct interface *);
+#endif
index 15c6cf7b350dcd336a3177c39e4c12b518a48a4e..3e138d7cba153d63193591d1e0580be265914943 100644 (file)
--- a/logger.c
+++ b/logger.c
@@ -36,7 +36,6 @@
 #include "logger.h"
 
 static int loglevel = LOG_INFO;
-static char logprefix[12] = {0};
 
 void
 setloglevel(int level)
@@ -44,46 +43,20 @@ setloglevel(int level)
        loglevel = level;
 }
 
-void
-setlogprefix(const char *prefix)
-{
-       strlcpy(logprefix, prefix, sizeof(logprefix));
-}
-
 void
 logger(int level, const char *fmt, ...)
 {
        va_list p, p2;
        FILE *f = stderr;
-       size_t len, fmt2len;
-       char *fmt2, *pf;
 
        va_start(p, fmt);
        va_copy(p2, p);
-
        if (level <= LOG_ERR || level <= loglevel) {
-               fprintf(f, "%s", logprefix);
                vfprintf(f, fmt, p);
                fputc('\n', f);
        }
-
-       if (level < LOG_DEBUG || level <= loglevel) {
-               len = strlen(logprefix);
-               fmt2len = strlen(fmt) + len + 1;
-               fmt2 = pf = malloc(sizeof(char) * fmt2len);
-               if (fmt2) {
-                       strlcpy(pf, logprefix, fmt2len);
-                       pf += len;
-                       strlcpy(pf, fmt, fmt2len - len);
-                       vsyslog(level, fmt2, p2);
-                       free(fmt2);
-               } else {
-                       vsyslog(level, fmt, p2);
-                       syslog(LOG_ERR, "logger: memory exhausted");
-                       exit(EXIT_FAILURE);
-               }
-       }
-
+       if (level < LOG_DEBUG || level <= loglevel)
+               vsyslog(level, fmt, p2);
        va_end(p2);
        va_end(p);
 }
index 8ac76b62f4f8c77ca4e1fb342312b781b47d6eb1..13f53728d6b1d494e6c5747b80c7982940680694 100644 (file)
--- a/logger.h
+++ b/logger.h
@@ -37,7 +37,6 @@
 #include <syslog.h>
 
 void setloglevel(int);
-void setlogprefix(const char *);
 void logger(int, const char *, ...) _PRINTF_LIKE (2, 3);
 
 #endif
index d970b2d647f7aed4e36918d7fc33df3dfd865c9e..b6e3efc169fd52218234de1ce7642b548cc31e37 100644 (file)
@@ -36,14 +36,6 @@ all: ${PROG} ${SCRIPTS} _man
 ${PROG}: ${OBJS}
        ${CC} ${LDFLAGS} -o $@ ${OBJS} ${LDADD}
 
-# We could save about 600 bytes by building it like this
-# instead of the more traditional method above
-small: ${SRCS}
-       echo "" > _${PROG}.c
-       for src in ${SRCS}; do echo "#include \"$$src\"" >> _${PROG}.c; done
-       ${CC} ${CFLAGS} ${CPPFLAGS} -c _${PROG}.c -o _${PROG}.o
-       ${CC} ${LDFLAGS} -o ${PROG} _${PROG}.o ${LDADD}
-
 _proginstall: ${PROG}
        ${INSTALL} -d ${DESTDIR}${BINDIR}
        ${INSTALL} -m ${BINMODE} ${PROG} ${DESTDIR}${BINDIR}
diff --git a/net.c b/net.c
index fa5af103750660ee417d988b8e946df06ab1ba56..10612961adfbc234b1a5f694069fdf1706a373c8 100644 (file)
--- a/net.c
+++ b/net.c
@@ -35,6 +35,7 @@
 #include <arpa/inet.h>
 #include <netinet/in_systm.h>
 #ifdef __linux__
+#include <linux/wireless.h>
 #include <netinet/ether.h>
 #include <netpacket/packet.h>
 #endif
@@ -63,6 +64,7 @@
 #include "common.h"
 #include "dhcp.h"
 #include "logger.h"
+#include "if-options.h"
 #include "net.h"
 #include "signals.h"
 
@@ -176,10 +178,119 @@ hwaddr_aton(unsigned char *buffer, const char *addr)
        return len;
 }
 
+struct interface *
+init_interface(const char *ifname)
+{
+       int s, arpable;
+       struct ifreq ifr;
+       struct interface *iface = NULL;
+#ifdef SIOCGIWNAME
+       struct iwreq iwr;
+#endif
+
+       memset(&ifr, 0, sizeof(ifr));
+       strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+
+       if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
+               return NULL;
+
+       strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+       if (ioctl(s, SIOCGIFFLAGS, &ifr) == -1)
+               goto eexit;
+       if (ifr.ifr_flags & IFF_LOOPBACK || ifr.ifr_flags & IFF_POINTOPOINT)
+               goto eexit;
+       arpable = !(ifr.ifr_flags & IFF_NOARP);
+
+       iface = xzalloc(sizeof(*iface));
+       strlcpy(iface->name, ifname, sizeof(iface->name));
+       iface->metric = 100 + if_nametoindex(iface->name);
+
+#ifdef SIOCGIFHWADDR
+       strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+       if (ioctl(s, SIOCGIFHWADDR, &ifr) == -1)
+               goto eexit;
+
+       switch (ifr.ifr_hwaddr.sa_family) {
+       case ARPHRD_ETHER:
+       case ARPHRD_IEEE802:
+               iface->hwlen = ETHER_ADDR_LEN;
+               break;
+       case ARPHRD_IEEE1394:
+               iface->hwlen = EUI64_ADDR_LEN;
+       case ARPHRD_INFINIBAND:
+               iface->hwlen = INFINIBAND_ADDR_LEN;
+               break;
+       default:
+               logger(LOG_ERR, "%s: unsupported media family", iface->name);
+               goto eexit;
+       }
+       memcpy(iface->hwaddr, ifr.ifr_hwaddr.sa_data, iface->hwlen);
+       iface->family = ifr.ifr_hwaddr.sa_family;
+               
+#else
+       iface->family = ARPHRD_ETHER;
+#endif
+
+#ifdef SIOCGIWNAME
+       memset(&iwr, 0, sizeof(iwr));
+       strlcpy(iwr.ifr_name, ifname, sizeof(iwr.ifr_name));
+       /* Check for wireless */
+       if (ioctl(s, SIOCGIWNAME, &iwr) != -1)
+               iface->metric += 100;
+#endif
+
+       strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+       if (ioctl(s, SIOCGIFMTU, &ifr) == -1)
+               goto eexit;
+
+       /* Ensure that the MTU is big enough for DHCP */
+       if (ifr.ifr_mtu < MTU_MIN) {
+               ifr.ifr_mtu = MTU_MIN;
+               strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+               if (ioctl(s, SIOCSIFMTU, &ifr) == -1)
+                       goto eexit;
+       }
+
+       if (up_interface(ifname) != 0)
+               goto eexit;
+
+       snprintf(iface->leasefile, PATH_MAX, LEASEFILE, ifname);
+       iface->arpable = arpable;
+
+       /* 0 is a valid fd, so init to -1 */
+       iface->raw_fd = -1;
+       iface->udp_fd = -1;
+       iface->arp_fd = -1;
+       close(s);
+       return iface;
+
+eexit:
+       free(iface);
+       close(s);
+       return NULL;
+}
+
+void
+free_interface(struct interface *iface)
+{
+       if (!iface)
+               return;
+       if (iface->state) {
+               free_options(iface->state->options);
+               free(iface->state->old);
+               free(iface->state->new);
+               free(iface->state->offer);
+               free(iface->state);
+       }
+       free(iface->clientid);
+       free(iface);
+}
+
 int
 do_interface(const char *ifname,
-            _unused unsigned char *hwaddr, _unused size_t *hwlen,
-            struct in_addr *addr, struct in_addr *net, int get)
+            _unused struct interface **ifaces,
+            _unused int argc, _unused char * const *argv,
+            struct in_addr *addr, struct in_addr *net, int act)
 {
        int s;
        struct ifconf ifc;
@@ -195,7 +306,9 @@ do_interface(const char *ifname,
        struct ifreq *ifr;
        struct sockaddr_in netmask;
 #ifdef AF_LINK
+       int n;
        struct sockaddr_dl *sdl;
+       struct interface *ifp, *ifl = NULL, *ifn = NULL;
 #endif
 
        if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
@@ -237,29 +350,49 @@ do_interface(const char *ifname,
 #endif
                        p += sizeof(*ifr);
 
-               if (strcmp(ifname, ifr->ifr_name) != 0)
+               if (ifname && strcmp(ifname, ifr->ifr_name) != 0)
                        continue;
 
                found = 1;
 
 #ifdef AF_LINK
-               if (hwaddr && hwlen && ifr->ifr_addr.sa_family == AF_LINK) {
+               /* Interface discovery for BSD's */
+               if (act == 2 && ifr->ifr_addr.sa_family == AF_LINK) {
+                       for (ifp = *ifaces; ifp; ifp = ifp->next) {
+                               ifl = ifp;
+                               if (strcmp(ifp->name, ifr->ifr_name) == 0)
+                                       break;
+                       }
+                       if (ifp)
+                               continue;
+                       if (argc > 0) {
+                               for (n = 0; n < argc; n++)
+                                       if (strcmp(ifr->ifr_name, argv[n]) == 0)
+                                               break;
+                               if (n == argc)
+                                       continue;
+                       }
+                       ifn = init_interface(ifr->ifr_name);
+                       if (!ifn)
+                               continue;
+                       if (ifl)
+                               ifp = ifl->next = ifn;
+                       else
+                               ifp = *ifaces = ifn;
                        sdl = xmalloc(ifr->ifr_addr.sa_len);
                        memcpy(sdl, &ifr->ifr_addr, ifr->ifr_addr.sa_len);
-                       *hwlen = sdl->sdl_alen;
-                       memcpy(hwaddr, LLADDR(sdl), *hwlen);
+                       ifp->hwlen = sdl->sdl_alen;
+                       memcpy(ifp->hwaddr, LLADDR(sdl), sdl->sdl_alen);
                        free(sdl);
-                       retval = 1;
-                       break;
                }
 #endif
 
-               if (ifr->ifr_addr.sa_family == AF_INET) {
+               if (ifr->ifr_addr.sa_family == AF_INET && addr) {
                        memcpy(&address, &ifr->ifr_addr, sizeof(address));
                        if (ioctl(s, SIOCGIFNETMASK, ifr) == -1)
                                continue;
                        memcpy(&netmask, &ifr->ifr_addr, sizeof(netmask));
-                       if (get) {
+                       if (act == 1) {
                                addr->s_addr = address.sin_addr.s_addr;
                                net->s_addr = netmask.sin_addr.s_addr;
                                retval = 1;
@@ -361,90 +494,6 @@ carrier_status(const char *ifname)
        return retval;
 }
 
-struct interface *
-read_interface(const char *ifname, _unused int metric)
-{
-       int s;
-       struct ifreq ifr;
-       struct interface *iface = NULL;
-       unsigned char *hwaddr = NULL;
-       size_t hwlen = 0;
-       sa_family_t family = 0;
-
-       memset(&ifr, 0, sizeof(ifr));
-       strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
-
-       if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
-               return NULL;
-
-#ifdef __linux__
-       strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
-       if (ioctl(s, SIOCGIFHWADDR, &ifr) == -1)
-               goto eexit;
-
-       switch (ifr.ifr_hwaddr.sa_family) {
-       case ARPHRD_ETHER:
-       case ARPHRD_IEEE802:
-               hwlen = ETHER_ADDR_LEN;
-               break;
-       case ARPHRD_IEEE1394:
-               hwlen = EUI64_ADDR_LEN;
-       case ARPHRD_INFINIBAND:
-               hwlen = INFINIBAND_ADDR_LEN;
-               break;
-       }
-
-       hwaddr = xmalloc(sizeof(unsigned char) * HWADDR_LEN);
-       memcpy(hwaddr, ifr.ifr_hwaddr.sa_data, hwlen);
-       family = ifr.ifr_hwaddr.sa_family;
-#else
-       ifr.ifr_metric = metric;
-       strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
-       if (ioctl(s, SIOCSIFMETRIC, &ifr) == -1)
-               goto eexit;
-
-       hwaddr = xmalloc(sizeof(unsigned char) * HWADDR_LEN);
-       if (do_interface(ifname, hwaddr, &hwlen, NULL, NULL, 0) != 1)
-               goto eexit;
-
-       family = ARPHRD_ETHER;
-#endif
-
-       strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
-       if (ioctl(s, SIOCGIFMTU, &ifr) == -1)
-               goto eexit;
-
-       /* Ensure that the MTU is big enough for DHCP */
-       if (ifr.ifr_mtu < MTU_MIN) {
-               ifr.ifr_mtu = MTU_MIN;
-               strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
-               if (ioctl(s, SIOCSIFMTU, &ifr) == -1)
-                       goto eexit;
-       }
-
-       if (up_interface(ifname) != 0)
-               goto eexit;
-
-       iface = xzalloc(sizeof(*iface));
-       strlcpy(iface->name, ifname, IF_NAMESIZE);
-       snprintf(iface->leasefile, PATH_MAX, LEASEFILE, ifname);
-       memcpy(&iface->hwaddr, hwaddr, hwlen);
-       iface->hwlen = hwlen;
-
-       iface->family = family;
-       iface->arpable = !(ifr.ifr_flags & (IFF_NOARP | IFF_LOOPBACK));
-
-       /* 0 is a valid fd, so init to -1 */
-       iface->raw_fd = -1;
-       iface->udp_fd = -1;
-       iface->arp_fd = -1;
-       iface->link_fd = -1;
-
-eexit:
-       close(s);
-       free(hwaddr);
-       return iface;
-}
 
 int
 do_mtu(const char *ifname, short int mtu)
diff --git a/net.h b/net.h
index 3ade0257e0843d82198448d6e338d08f9fae6281..fd96f692c55299565b9df435064a3ecff33f3b45 100644 (file)
--- a/net.h
+++ b/net.h
@@ -39,6 +39,8 @@
 #include <limits.h>
 
 #include "config.h"
+#include "dhcp.h"
+#include "dhcpcd.h"
 
 #ifndef DUID_LEN
 #  define DUID_LEN                     128 + 2
@@ -57,7 +59,6 @@
 #  define ARPHRD_INFINIBAND            32
 #endif
 
-#define HWADDR_LEN                     20
 
 /* Work out if we have a private address or not
  * 10/8
@@ -93,36 +94,13 @@ struct rt {
        struct rt *next;
 };
 
-struct interface
-{
-       char name[IF_NAMESIZE];
-       sa_family_t family;
-       unsigned char hwaddr[HWADDR_LEN];
-       size_t hwlen;
-       int arpable;
-
-       int raw_fd;
-       int udp_fd;
-       int arp_fd;
-       int link_fd;
-       size_t buffer_size, buffer_len, buffer_pos;
-       unsigned char *buffer;
-
-       struct in_addr addr;
-       struct in_addr net;
-       struct rt *routes;
-
-       char leasefile[PATH_MAX];
-       time_t start_uptime;
-
-       unsigned char *clientid;
-};
-
 uint32_t get_netmask(uint32_t);
 char *hwaddr_ntoa(const unsigned char *, size_t);
 size_t hwaddr_aton(unsigned char *, const char *);
 
-struct interface *read_interface(const char *, int);
+struct interface *init_interface(const char *);
+struct interface *discover_interfaces(int, char * const *);
+void free_interface(struct interface *);
 int do_mtu(const char *, short int);
 #define get_mtu(iface) do_mtu(iface, 0)
 #define set_mtu(iface, mtu) do_mtu(iface, mtu)
@@ -131,27 +109,29 @@ int inet_ntocidr(struct in_addr);
 int inet_cidrtoaddr(int, struct in_addr *);
 
 int up_interface(const char *);
-int do_interface(const char *, unsigned char *, size_t *,
+int do_interface(const char *, struct interface **,
+                int argc, char * const *argv,
                 struct in_addr *, struct in_addr *, int);
-int if_address(const char *, const struct in_addr *, const struct in_addr *,
+int if_address(const struct interface *,
+              const struct in_addr *, const struct in_addr *,
               const struct in_addr *, int);
-#define add_address(ifname, addr, net, brd) \
-       if_address(ifname, addr, net, brd, 1)
-#define del_address(ifname, addr, net) \
-       if_address(ifname, addr, net, NULL, -1)
-#define has_address(ifname, addr, net) \
-       do_interface(ifname, NULL, NULL, addr, net, 0)
-#define get_address(ifname, addr, net) \
-       do_interface(ifname, NULL, NULL, addr, net, 1)
-
-int if_route(const char *, const struct in_addr *, const struct in_addr *,
-            const struct in_addr *, int, int);
-#define add_route(ifname, dest, mask, gate, metric) \
-       if_route(ifname, dest, mask, gate, metric, 1)
-#define change_route(ifname, dest, mask, gate, metric) \
-       if_route(ifname, dest, mask, gate, metric, 0)
-#define del_route(ifname, dest, mask, gate, metric) \
-       if_route(ifname, dest, mask, gate, metric, -1)
+#define add_address(iface, addr, net, brd) \
+       if_address(iface, addr, net, brd, 1)
+#define del_address(iface, addr, net) \
+       if_address(iface, addr, net, NULL, -1)
+#define has_address(iface, addr, net) \
+       do_interface(iface, NULL, 0, NULL, addr, net, 0)
+#define get_address(iface, addr, net) \
+       do_interface(iface, NULL, 0, NULL, addr, net, 1)
+
+int if_route(const struct interface *, const struct in_addr *,
+            const struct in_addr *, const struct in_addr *, int, int);
+#define add_route(iface, dest, mask, gate, metric) \
+       if_route(iface, dest, mask, gate, metric, 1)
+#define change_route(iface, dest, mask, gate, metric) \
+       if_route(iface, dest, mask, gate, metric, 0)
+#define del_route(iface, dest, mask, gate, metric) \
+       if_route(iface, dest, mask, gate, metric, -1)
 void free_routes(struct rt *);
 
 int open_udp_socket(struct interface *);
@@ -170,7 +150,7 @@ ssize_t get_raw_packet(struct interface *, int, void *, ssize_t);
 
 int send_arp(const struct interface *, int, in_addr_t, in_addr_t);
 
-int open_link_socket(struct interface *);
-int link_changed(struct interface *);
+int open_link_socket(void);
+int link_changed(int, const struct interface *);
 int carrier_status(const char *);
 #endif
index 58679d63410b43d35ecd7d8dcf7f9a8806ad3cdb..d98a2dcc973eb57bdef05cf35b48710430fc17ad 100644 (file)
--- a/signals.c
+++ b/signals.c
@@ -55,12 +55,6 @@ signal_handler(int sig)
        errno = serrno;
 }
 
-int
-signal_fd(void)
-{
-       return (signal_pipe[0]);
-}
-
 /* Read a signal from the signal pipe. Returns 0 if there is
  * no signal, -1 on error (and sets errno appropriately), and
  * your signal on success */
@@ -93,7 +87,7 @@ signal_init(void)
                return -1;
        if (set_cloexec(signal_pipe[1]) == -1)
                return -1;
-       return 0;
+       return signal_pipe[0];
 }
 
 static int
@@ -123,3 +117,4 @@ signal_reset(void)
 {
        return signal_handle(SIG_DFL);
 }
+
index f784a68a886d41cb77d47fe2880af5e438099c6d..76b13f5280a6f467e59121027b1fb9abf5ec00f8 100644 (file)
--- a/signals.h
+++ b/signals.h
@@ -31,7 +31,6 @@
 int signal_init(void);
 int signal_setup(void);
 int signal_reset(void);
-int signal_fd(void);
 int signal_read(void);
 
 #endif