]> git.ipfire.org Git - thirdparty/dhcpcd.git/commitdiff
Remove IPv4 and DHCP4 specific setup from dhcpcd.c and move into ipv4.c
authorRoy Marples <roy@marples.name>
Sat, 2 Feb 2013 14:05:55 +0000 (14:05 +0000)
committerRoy Marples <roy@marples.name>
Sat, 2 Feb 2013 14:05:55 +0000 (14:05 +0000)
and dhcp.c
Split configure.c into script.c and move the rest into dhcp.c

This starts the goal of making the base of dhcpcd protocol agnostic and
working towards building IPv4 and/or IPv6 code only to reduce size and
allow growing any future new protocol easier.

20 files changed:
Makefile
arp.c
arp.h
bind.c [deleted file]
dhcp-common.c [new file with mode: 0644]
dhcp-common.h [new file with mode: 0644]
dhcp.c
dhcp.h
dhcp6.c
dhcpcd.c
dhcpcd.h
if-linux.c
ipv4.c [moved from configure.c with 53% similarity]
ipv4.h [moved from bind.h with 81% similarity]
ipv4ll.c
ipv6.c
ipv6ns.c
ipv6rs.c
script.c [new file with mode: 0644]
script.h [moved from configure.h with 81% similarity]

index 0a478804831dea659674ff85ca48646154d5776d..b811d92528db00068c66caf1ca1e04f86de80199 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,8 +1,10 @@
 # dhcpcd Makefile
 
 PROG=          dhcpcd
-SRCS=          arp.c bind.c common.c control.c dhcp.c dhcpcd.c duid.c eloop.c
-SRCS+=         configure.c if-options.c if-pref.c ipv4ll.c net.c signals.c
+SRCS=          arp.c common.c control.c dhcpcd.c duid.c eloop.c
+SRCS+=         if-options.c if-pref.c net.c script.c signals.c
+SRCS+=         dhcp-common.c
+SRCS+=         ipv4.c dhcp.c ipv4ll.c
 SRCS+=         ipv6.c ipv6rs.c ipv6ns.c dhcp6.c
 
 CFLAGS?=       -O2
diff --git a/arp.c b/arp.c
index 6c35594acd570c6fc33ecc32f29b0eba050630ee..652a2a7f6799f9a39a8b218984a88539a3087dbe 100644 (file)
--- a/arp.c
+++ b/arp.c
@@ -1,6 +1,6 @@
 /* 
  * dhcpcd - DHCP client daemon
- * Copyright (c) 2006-2012 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2013 Roy Marples <roy@marples.name>
  * All rights reserved
 
  * Redistribution and use in source and binary forms, with or without
@@ -33,8 +33,9 @@
 #include <unistd.h>
 
 #include "arp.h"
-#include "bind.h"
+#include "ipv4.h"
 #include "common.h"
+#include "dhcp.h"
 #include "dhcpcd.h"
 #include "eloop.h"
 #include "if-options.h"
@@ -45,7 +46,7 @@
        (sizeof(struct arphdr) + (2 * sizeof(uint32_t)) + (2 * HWADDR_LEN))
 
 static int
-send_arp(const struct interface *ifp, int op, in_addr_t sip, in_addr_t tip)
+arp_send(const struct interface *ifp, int op, in_addr_t sip, in_addr_t tip)
 {
        uint8_t arp_buffer[ARP_LEN];
        struct arphdr ar;
@@ -76,7 +77,7 @@ send_arp(const struct interface *ifp, int op, in_addr_t sip, in_addr_t tip)
 }
 
 static void
-handle_arp_failure(struct interface *ifp)
+arp_failure(struct interface *ifp)
 {
 
        /* If we failed without a magic cookie then we need to try
@@ -92,8 +93,8 @@ handle_arp_failure(struct interface *ifp)
 
        unlink(ifp->leasefile);
        if (!ifp->state->lease.frominfo)
-               send_decline(ifp);
-       close_sockets(ifp);
+               dhcp_decline(ifp);
+       dhcp_close(ifp);
        eloop_timeout_delete(NULL, ifp);
        if (ifp->state->lease.frominfo)
                start_interface(ifp);
@@ -102,7 +103,7 @@ handle_arp_failure(struct interface *ifp)
 }
 
 static void
-handle_arp_packet(void *arg)
+arp_packet(void *arg)
 {
        struct interface *ifp = arg;
        uint8_t arp_buffer[ARP_LEN];
@@ -166,7 +167,7 @@ handle_arp_packet(void *arg)
                        if (select_profile(ifp, hwaddr) == -1 &&
                            errno == ENOENT)
                                select_profile(ifp, inet_ntoa(ina));
-                       close_sockets(ifp);
+                       dhcp_close(ifp);
                        eloop_timeout_delete(NULL, ifp);
                        start_interface(ifp);
                        return;
@@ -191,14 +192,14 @@ handle_arp_packet(void *arg)
                                (size_t)ar.ar_hln),
                            inet_ntoa(state->fail));
                        errno = EEXIST;
-                       handle_arp_failure(ifp);
+                       arp_failure(ifp);
                        return;
                }
        }
 }
 
 void
-send_arp_announce(void *arg)
+arp_announce(void *arg)
 {
        struct interface *ifp = arg;
        struct if_state *state = ifp->state;
@@ -208,7 +209,7 @@ send_arp_announce(void *arg)
                return;
        if (ifp->arp_fd == -1) {
                open_socket(ifp, ETHERTYPE_ARP);
-               eloop_event_add(ifp->arp_fd, handle_arp_packet, ifp);
+               eloop_event_add(ifp->arp_fd, arp_packet, ifp);
        }
        if (++state->claims < ANNOUNCE_NUM)     
                syslog(LOG_DEBUG,
@@ -219,11 +220,11 @@ send_arp_announce(void *arg)
                syslog(LOG_DEBUG,
                    "%s: sending ARP announce (%d of %d)",
                    ifp->name, state->claims, ANNOUNCE_NUM);
-       if (send_arp(ifp, ARPOP_REQUEST,
+       if (arp_send(ifp, ARPOP_REQUEST,
                state->new->yiaddr, state->new->yiaddr) == -1)
                syslog(LOG_ERR, "send_arp: %m");
        if (state->claims < ANNOUNCE_NUM) {
-               eloop_timeout_add_sec(ANNOUNCE_WAIT, send_arp_announce, ifp);
+               eloop_timeout_add_sec(ANNOUNCE_WAIT, arp_announce, ifp);
                return;
        }
        if (state->new->cookie != htonl(MAGIC_COOKIE)) {
@@ -236,7 +237,7 @@ send_arp_announce(void *arg)
                tv.tv_sec = state->interval - DHCP_RAND_MIN;
                tv.tv_usec = arc4random() % (DHCP_RAND_MAX_U - DHCP_RAND_MIN_U);
                timernorm(&tv);
-               eloop_timeout_add_tv(&tv, start_discover, ifp);
+               eloop_timeout_add_tv(&tv, dhcp_discover, ifp);
        } else {
                eloop_event_delete(ifp->arp_fd);
                close(ifp->arp_fd);
@@ -245,7 +246,7 @@ send_arp_announce(void *arg)
 }
 
 void
-send_arp_probe(void *arg)
+arp_probe(void *arg)
 {
        struct interface *ifp = arg;
        struct if_state *state = ifp->state;
@@ -266,7 +267,7 @@ send_arp_probe(void *arg)
 
        if (ifp->arp_fd == -1) {
                open_socket(ifp, ETHERTYPE_ARP);
-               eloop_event_add(ifp->arp_fd, handle_arp_packet, ifp);
+               eloop_event_add(ifp->arp_fd, arp_packet, ifp);
        }
        if (state->probes == 0) {
                if (arping)
@@ -280,32 +281,32 @@ send_arp_probe(void *arg)
                tv.tv_sec = PROBE_MIN;
                tv.tv_usec = arc4random() % (PROBE_MAX_U - PROBE_MIN_U);
                timernorm(&tv);
-               eloop_timeout_add_tv(&tv, send_arp_probe, ifp);
+               eloop_timeout_add_tv(&tv, arp_probe, ifp);
        } else {
                tv.tv_sec = ANNOUNCE_WAIT;
                tv.tv_usec = 0;
                if (arping) {
                        state->probes = 0;
                        if (++state->arping_index < state->options->arping_len)
-                               eloop_timeout_add_tv(&tv, send_arp_probe, ifp);
+                               eloop_timeout_add_tv(&tv, arp_probe, ifp);
                        else
                                eloop_timeout_add_tv(&tv, start_interface, ifp);
                } else
-                       eloop_timeout_add_tv(&tv, bind_interface, ifp);
+                       eloop_timeout_add_tv(&tv, dhcp_bind, ifp);
        }
        syslog(LOG_DEBUG,
            "%s: sending ARP probe (%d of %d), next in %0.2f seconds",
            ifp->name, state->probes ? state->probes : PROBE_NUM, PROBE_NUM,
            timeval_to_double(&tv));
-       if (send_arp(ifp, ARPOP_REQUEST, 0, addr.s_addr) == -1)
+       if (arp_send(ifp, ARPOP_REQUEST, 0, addr.s_addr) == -1)
                syslog(LOG_ERR, "send_arp: %m");
 }
 
 void
-start_arping(struct interface *ifp)
+arp_start(struct interface *ifp)
 {
 
        ifp->state->probes = 0;
        ifp->state->arping_index = 0;
-       send_arp_probe(ifp);
+       arp_probe(ifp);
 }
diff --git a/arp.h b/arp.h
index b97c38bdc08f46b1465bdd94a5f40d524f61aa26..3274bbb35bbcf52978ca225bd120357ee4e0d965 100644 (file)
--- a/arp.h
+++ b/arp.h
@@ -1,6 +1,6 @@
 /* 
  * dhcpcd - DHCP client daemon
- * Copyright (c) 2006-2008 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2013 Roy Marples <roy@marples.name>
  * All rights reserved
 
  * Redistribution and use in source and binary forms, with or without
@@ -43,7 +43,7 @@
 
 #include "dhcpcd.h"
 
-void send_arp_announce(void *);
-void send_arp_probe(void *);
-void start_arping(struct interface *);
+void arp_announce(void *);
+void arp_probe(void *);
+void arp_start(struct interface *);
 #endif
diff --git a/bind.c b/bind.c
deleted file mode 100644 (file)
index f622892..0000000
--- a/bind.c
+++ /dev/null
@@ -1,231 +0,0 @@
-/* 
- * dhcpcd - DHCP client daemon
- * Copyright (c) 2006-2012 Roy Marples <roy@marples.name>
- * All rights reserved
-
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#include <sys/param.h>
-#include <sys/time.h>
-
-#include <fcntl.h>
-#ifdef BSD
-#  include <paths.h>
-#endif
-#include <signal.h>
-#include <stdlib.h>
-#include <syslog.h>
-#include <unistd.h>
-
-#include "arp.h"
-#include "bind.h"
-#include "common.h"
-#include "configure.h"
-#include "dhcpcd.h"
-#include "eloop.h"
-#include "if-options.h"
-#include "net.h"
-#include "signals.h"
-
-#ifndef _PATH_DEVNULL
-#  define _PATH_DEVNULL "/dev/null"
-#endif
-
-/* We do things after aquiring the lease, so ensure we have enough time for them */
-#define DHCP_MIN_LEASE 20
-
-#ifndef THERE_IS_NO_FORK
-pid_t
-daemonise(void)
-{
-       pid_t pid;
-       char buf = '\0';
-       int sidpipe[2], fd;
-
-       eloop_timeout_delete(handle_exit_timeout, NULL);
-       if (options & DHCPCD_DAEMONISED || !(options & DHCPCD_DAEMONISE))
-               return 0;
-       /* Setup a signal pipe so parent knows when to exit. */
-       if (pipe(sidpipe) == -1) {
-               syslog(LOG_ERR, "pipe: %m");
-               return -1;
-       }
-       syslog(LOG_DEBUG, "forking to background");
-       switch (pid = fork()) {
-       case -1:
-               syslog(LOG_ERR, "fork: %m");
-               exit(EXIT_FAILURE);
-               /* NOTREACHED */
-       case 0:
-               setsid();
-               /* Notify parent it's safe to exit as we've detached. */
-               close(sidpipe[0]);
-               if (write(sidpipe[1], &buf, 1) == -1)
-                       syslog(LOG_ERR, "failed to notify parent: %m");
-               close(sidpipe[1]);
-               if ((fd = open(_PATH_DEVNULL, O_RDWR, 0)) != -1) {
-                       dup2(fd, STDIN_FILENO);
-                       dup2(fd, STDOUT_FILENO);
-                       dup2(fd, STDERR_FILENO);
-                       close(fd);
-               }
-               break;
-       default:
-               /* Wait for child to detach */
-               close(sidpipe[1]);
-               if (read(sidpipe[0], &buf, 1) == -1)
-                       syslog(LOG_ERR, "failed to read child: %m");
-               close(sidpipe[0]);
-               break;
-       }
-       /* Done with the fd now */
-       if (pid != 0) {
-               syslog(LOG_INFO, "forked to background, child pid %d",pid);
-               writepid(pidfd, pid);
-               close(pidfd);
-               pidfd = -1;
-               options |= DHCPCD_FORKED;
-               exit(EXIT_SUCCESS);
-       }
-       options |= DHCPCD_DAEMONISED;
-       return pid;
-}
-#endif
-
-void
-bind_interface(void *arg)
-{
-       struct interface *iface = arg;
-       struct if_state *state = iface->state;
-       struct if_options *ifo = state->options;
-       struct dhcp_lease *lease = &state->lease;
-       struct timeval tv;
-
-       /* We're binding an address now - ensure that sockets are closed */
-       close_sockets(iface);
-       state->reason = NULL;
-       if (clock_monotonic)
-               get_monotonic(&lease->boundtime);
-       state->xid = 0;
-       free(state->old);
-       state->old = state->new;
-       state->new = state->offer;
-       state->offer = NULL;
-       get_lease(lease, state->new);
-       if (ifo->options & DHCPCD_STATIC) {
-               syslog(LOG_INFO, "%s: using static address %s",
-                   iface->name, inet_ntoa(lease->addr));
-               lease->leasetime = ~0U;
-               lease->net.s_addr = ifo->req_mask.s_addr;
-               state->reason = "STATIC";
-       } else if (state->new->cookie != htonl(MAGIC_COOKIE)) {
-               syslog(LOG_INFO, "%s: using IPv4LL address %s",
-                   iface->name, inet_ntoa(lease->addr));
-               lease->leasetime = ~0U;
-               state->reason = "IPV4LL";
-       } else if (ifo->options & DHCPCD_INFORM) {
-               if (ifo->req_addr.s_addr != 0)
-                       lease->addr.s_addr = ifo->req_addr.s_addr;
-               else
-                       lease->addr.s_addr = iface->addr.s_addr;
-               syslog(LOG_INFO, "%s: received approval for %s", iface->name,
-                   inet_ntoa(lease->addr));
-               lease->leasetime = ~0U;
-               state->reason = "INFORM";
-       } else {
-               if (gettimeofday(&tv, NULL) == 0)
-                       lease->leasedfrom = tv.tv_sec;
-               else if (lease->frominfo)
-                       state->reason = "TIMEOUT";
-               if (lease->leasetime == ~0U) {
-                       lease->renewaltime =
-                           lease->rebindtime =
-                           lease->leasetime;
-                       syslog(LOG_INFO, "%s: leased %s for infinity",
-                           iface->name, inet_ntoa(lease->addr));
-               } else {
-                       if (lease->leasetime < DHCP_MIN_LEASE) {
-                               syslog(LOG_WARNING,
-                                   "%s: minimum lease is %d seconds",
-                                   iface->name, DHCP_MIN_LEASE);
-                               lease->leasetime = DHCP_MIN_LEASE;
-                       }
-                       if (lease->rebindtime == 0)
-                               lease->rebindtime = lease->leasetime * T2;
-                       else if (lease->rebindtime >= lease->leasetime) {
-                               lease->rebindtime = lease->leasetime * T2;
-                               syslog(LOG_ERR,
-                                   "%s: rebind time greater than lease "
-                                   "time, forcing to %u seconds",
-                                   iface->name, lease->rebindtime);
-                       }
-                       if (lease->renewaltime == 0)
-                               lease->renewaltime = lease->leasetime * T1;
-                       else if (lease->renewaltime > lease->rebindtime) {
-                               lease->renewaltime = lease->leasetime * T1;
-                               syslog(LOG_ERR,
-                                   "%s: renewal time greater than rebind "
-                                   "time, forcing to %u seconds",
-                                   iface->name, lease->renewaltime);
-                       }
-                       syslog(LOG_INFO,
-                           "%s: leased %s for %u seconds", iface->name,
-                           inet_ntoa(lease->addr), lease->leasetime);
-               }
-       }
-       if (options & DHCPCD_TEST) {
-               state->reason = "TEST";
-               run_script(iface);
-               exit(EXIT_SUCCESS);
-       }
-       if (state->reason == NULL) {
-               if (state->old) {
-                       if (state->old->yiaddr == state->new->yiaddr &&
-                           lease->server.s_addr)
-                               state->reason = "RENEW";
-                       else
-                               state->reason = "REBIND";
-               } else if (state->state == DHS_REBOOT)
-                       state->reason = "REBOOT";
-               else
-                       state->reason = "BOUND";
-       }
-       if (lease->leasetime == ~0U)
-               lease->renewaltime = lease->rebindtime = lease->leasetime;
-       else {
-               eloop_timeout_add_sec(lease->renewaltime, start_renew, iface);
-               eloop_timeout_add_sec(lease->rebindtime, start_rebind, iface);
-               eloop_timeout_add_sec(lease->leasetime, start_expire, iface);
-               syslog(LOG_DEBUG,
-                   "%s: renew in %u seconds, rebind in %u seconds",
-                   iface->name, lease->renewaltime, lease->rebindtime);
-       }
-       ifo->options &= ~ DHCPCD_CSR_WARNED;
-       configure(iface);
-       daemonise();
-       state->state = DHS_BOUND;
-       if (ifo->options & DHCPCD_ARP) {
-               state->claims = 0;
-               send_arp_announce(iface);
-       }
-}
diff --git a/dhcp-common.c b/dhcp-common.c
new file mode 100644 (file)
index 0000000..be2d0fa
--- /dev/null
@@ -0,0 +1,382 @@
+/* 
+ * dhcpcd - DHCP client daemon
+ * Copyright (c) 2006-2012 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 <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include "config.h"
+#include "common.h"
+#include "dhcp-common.h"
+#include "dhcp.h"
+
+int make_option_mask(const struct dhcp_opt *dopts,
+    uint8_t *mask, const char *opts, int add)
+{
+       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;
+               for (opt = dopts; opt->option; opt++) {
+                       if (!opt->var)
+                               continue;
+                       match = 0;
+                       if (strcmp(opt->var, token) == 0)
+                               match = 1;
+                       else {
+                               errno = 0;
+                               n = strtol(token, &t, 0);
+                               if (errno == 0 && !*t)
+                                       if (opt->option == n)
+                                               match = 1;
+                       }
+                       if (match) {
+                               if (add == 2 && !(opt->type & ADDRIPV4)) {
+                                       free(o);
+                                       errno = EINVAL;
+                                       return -1;
+                               }
+                               if (add == 1 || add == 2)
+                                       add_option_mask(mask,
+                                           opt->option);
+                               else
+                                       del_option_mask(mask,
+                                           opt->option);
+                               break;
+                       }
+               }
+               if (!opt->option) {
+                       free(o);
+                       errno = ENOENT;
+                       return -1;
+               }
+       }
+       free(o);
+       return 0;
+}
+
+/* Decode an RFC3397 DNS search order option into a space
+ * separated string. Returns length of string (including
+ * terminating zero) or zero on error. out may be NULL
+ * to just determine output length. */
+ssize_t
+decode_rfc3397(char *out, ssize_t len, int pl, const uint8_t *p)
+{
+       const char *start;
+       ssize_t start_len;
+       const uint8_t *r, *q = p;
+       int count = 0, l, hops;
+       uint8_t ltype;
+
+       start = out;
+       start_len = len;
+       while (q - p < pl) {
+               r = NULL;
+               hops = 0;
+               /* We check we are inside our length again incase
+                * the data is NOT terminated correctly. */
+               while ((l = *q++) && q - p < pl) {
+                       ltype = l & 0xc0;
+                       if (ltype == 0x80 || ltype == 0x40)
+                               return 0;
+                       else if (ltype == 0xc0) { /* pointer */
+                               l = (l & 0x3f) << 8;
+                               l |= *q++;
+                               /* save source of first jump. */
+                               if (!r)
+                                       r = q;
+                               hops++;
+                               if (hops > 255)
+                                       return 0;
+                               q = p + l;
+                               if (q - p >= pl)
+                                       return 0;
+                       } else {
+                               /* straightforward name segment, add with '.' */
+                               count += l + 1;
+                               if (out) {
+                                       if ((ssize_t)l + 1 > len) {
+                                               errno = ENOBUFS;
+                                               return -1;
+                                       }
+                                       memcpy(out, q, l);
+                                       out += l;
+                                       *out++ = '.';
+                                       len -= l;
+                                       len--;
+                               }
+                               q += l;
+                       }
+               }
+               /* change last dot to space */
+               if (out && out != start)
+                       *(out - 1) = ' ';
+               if (r)
+                       q = r;
+       }
+
+       /* change last space to zero terminator */
+       if (out) {
+               if (out != start)
+                       *(out - 1) = '\0';
+               else if (start_len > 0)
+                       *out = '\0';
+       }
+
+       return count;  
+}
+
+ssize_t
+print_string(char *s, ssize_t len, int dl, const uint8_t *data)
+{
+       uint8_t c;
+       const uint8_t *e, *p;
+       ssize_t bytes = 0;
+       ssize_t r;
+
+       e = data + dl;
+       while (data < e) {
+               c = *data++;
+               if (c == '\0') {
+                       /* If rest is all NULL, skip it. */
+                       for (p = data; p < e; p++)
+                               if (*p != '\0')
+                                       break;
+                       if (p == e)
+                               break;
+               }
+               if (!isascii(c) || !isprint(c)) {
+                       if (s) {
+                               if (len < 5) {
+                                       errno = ENOBUFS;
+                                       return -1;
+                               }
+                               r = snprintf(s, len, "\\%03o", c);
+                               len -= r;
+                               bytes += r;
+                               s += r;
+                       } else
+                               bytes += 4;
+                       continue;
+               }
+               switch (c) {
+               case '"':  /* FALLTHROUGH */
+               case '\'': /* FALLTHROUGH */
+               case '$':  /* FALLTHROUGH */
+               case '`':  /* FALLTHROUGH */
+               case '\\': /* FALLTHROUGH */
+               case '|':  /* FALLTHROUGH */
+               case '&':
+                       if (s) {
+                               if (len < 3) {
+                                       errno = ENOBUFS;
+                                       return -1;
+                               }
+                               *s++ = '\\';
+                               len--;
+                       }
+                       bytes++;
+                       break;
+               }
+               if (s) {
+                       *s++ = c;
+                       len--;
+               }
+               bytes++;
+       }
+
+       /* NULL */
+       if (s)
+               *s = '\0';
+       bytes++;
+       return bytes;
+}
+
+ssize_t
+print_option(char *s, ssize_t len, int type, int dl, const uint8_t *data,
+    const char *ifname)
+{
+       const uint8_t *e, *t;
+       uint16_t u16;
+       int16_t s16;
+       uint32_t u32;
+       int32_t s32;
+       struct in_addr addr;
+       ssize_t bytes = 0;
+       ssize_t l;
+       char *tmp;
+
+       if (type & RFC3397) {
+               l = decode_rfc3397(NULL, 0, dl, data);
+               if (l < 1)
+                       return l;
+               tmp = xmalloc(l);
+               decode_rfc3397(tmp, l, dl, data);
+               l = print_string(s, len, l - 1, (uint8_t *)tmp);
+               free(tmp);
+               return l;
+       }
+
+       if (type & RFC3361) {
+               if ((tmp = decode_rfc3361(dl, data)) == NULL)
+                       return -1;
+               l = strlen(tmp);
+               l = print_string(s, len, l - 1, (uint8_t *)tmp);
+               free(tmp);
+               return l;
+       }
+
+       if (type & RFC3442)
+               return decode_rfc3442(s, len, dl, data);
+
+       if (type & RFC5969)
+               return decode_rfc5969(s, len, dl, data);
+
+       if (type & STRING) {
+               /* Some DHCP servers return NULL strings */
+               if (*data == '\0')
+                       return 0;
+               return print_string(s, len, dl, data);
+       }
+
+       /* DHCPv6 status code */
+       if (type & SCODE && dl >= (int)sizeof(u16)) {
+               if (s) {
+                       memcpy(&u16, data, sizeof(u16));
+                       u16 = ntohs(u16);
+                       l = snprintf(s, len, "%d ", u16);
+                       len -= l;
+               } else
+                       l = 7;
+               data += sizeof(u16);
+               dl -= sizeof(u16);
+               if (dl)
+                       l += print_option(s, len, STRING, dl, data, ifname);
+               return l;
+       }
+
+       if (!s) {
+               if (type & UINT8)
+                       l = 3;
+               else if (type & UINT16) {
+                       l = 5;
+                       dl /= 2;
+               } else if (type & SINT16) {
+                       l = 6;
+                       dl /= 2;
+               } else if (type & UINT32) {
+                       l = 10;
+                       dl /= 4;
+               } else if (type & SINT32) {
+                       l = 11;
+                       dl /= 4;
+               } else if (type & ADDRIPV4) {
+                       l = 16;
+                       dl /= 4;
+               } else if (type & ADDRIPV6) {
+                       e = data + dl;
+                       l = 0;
+                       while (data < e) {
+                               if (l)
+                                       l++; /* space */
+                               dl = ipv6_printaddr(NULL, 0, data, ifname);
+                               if (dl != -1)
+                                       l += dl;
+                               data += 16;
+                       }
+                       return l + 1;
+               } else if (type & BINHEX) {
+                       l = 2;
+               } else {
+                       errno = EINVAL;
+                       return -1;
+               }
+               return (l + 1) * dl;
+       }
+
+       t = data;
+       e = data + dl;
+       while (data < e) {
+               if (data != t && type != BINHEX) {
+                       *s++ = ' ';
+                       bytes++;
+                       len--;
+               }
+               if (type & UINT8) {
+                       l = snprintf(s, len, "%d", *data);
+                       data++;
+               } else if (type & UINT16) {
+                       memcpy(&u16, data, sizeof(u16));
+                       u16 = ntohs(u16);
+                       l = snprintf(s, len, "%d", u16);
+                       data += sizeof(u16);
+               } else if (type & SINT16) {
+                       memcpy(&s16, data, sizeof(s16));
+                       s16 = ntohs(s16);
+                       l = snprintf(s, len, "%d", s16);
+                       data += sizeof(s16);
+               } else if (type & UINT32) {
+                       memcpy(&u32, data, sizeof(u32));
+                       u32 = ntohl(u32);
+                       l = snprintf(s, len, "%d", u32);
+                       data += sizeof(u32);
+               } else if (type & SINT32) {
+                       memcpy(&s32, data, sizeof(s32));
+                       s32 = ntohl(s32);
+                       l = snprintf(s, len, "%d", s32);
+                       data += sizeof(s32);
+               } else if (type & ADDRIPV4) {
+                       memcpy(&addr.s_addr, data, sizeof(addr.s_addr));
+                       l = snprintf(s, len, "%s", inet_ntoa(addr));
+                       data += sizeof(addr.s_addr);
+               } else if (type & ADDRIPV6) {
+                       dl = ipv6_printaddr(s, len, data, ifname);
+                       if (dl != -1)
+                               l = dl;
+                       else
+                               l = 0;  
+                       data += 16;
+               } else if (type & BINHEX) {
+                       l = snprintf(s, len, "%.2x", data[0]);
+                       data++; 
+               } else
+                       l = 0;
+               len -= l;
+               bytes += l;
+               s += l;
+       }
+
+       return bytes;
+}
diff --git a/dhcp-common.h b/dhcp-common.h
new file mode 100644 (file)
index 0000000..48dcc14
--- /dev/null
@@ -0,0 +1,74 @@
+/* 
+ * dhcpcd - DHCP client daemon
+ * Copyright (c) 2006-2012 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 DHCPCOMMON_H
+#define DHCPCOMMON_H
+
+#include <arpa/inet.h>
+#include <netinet/in.h>
+
+#include <stdint.h>
+
+#include "common.h"
+
+/* Max MTU - defines dhcp option length */
+#define MTU_MAX             1500
+#define MTU_MIN             576
+
+#define REQUEST                (1 << 0)
+#define UINT8          (1 << 1)
+#define UINT16         (1 << 2)
+#define SINT16         (1 << 3)
+#define UINT32         (1 << 4)
+#define SINT32         (1 << 5)
+#define ADDRIPV4       (1 << 6)
+#define STRING         (1 << 7)
+#define PAIR           (1 << 8)
+#define ARRAY          (1 << 9)
+#define RFC3361                (1 << 10)
+#define RFC3397                (1 << 11)
+#define RFC3442                (1 << 12)
+#define RFC5969                (1 << 13)
+#define ADDRIPV6       (1 << 14)
+#define BINHEX         (1 << 15)
+#define SCODE          (1 << 16)
+
+struct dhcp_opt {
+       uint16_t option;
+       int type;
+       const char *var;
+};
+
+#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(const struct dhcp_opt *,uint8_t *, const char *, int);
+ssize_t decode_rfc3397(char *, ssize_t, int, const uint8_t *);
+ssize_t print_string(char *, ssize_t, int, const uint8_t *);
+ssize_t print_option(char *, ssize_t, int, int, const uint8_t *, const char *);
+
+#endif
diff --git a/dhcp.c b/dhcp.c
index 385bfb44158d4c51d8625998034d55424e1ee07b..bd78caf2cf431e87678dbcbe68561e65a91d387a 100644 (file)
--- a/dhcp.c
+++ b/dhcp.c
@@ -1,6 +1,6 @@
 /* 
  * dhcpcd - DHCP client daemon
- * Copyright (c) 2006-2012 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2013 Roy Marples <roy@marples.name>
  * All rights reserved
 
  * Redistribution and use in source and binary forms, with or without
  * SUCH DAMAGE.
  */
 
+#include <arpa/inet.h>
+#include <net/route.h>
+#include <sys/stat.h>
+
+#ifdef __linux__
+#  include <asm/types.h> /* for systems with broken headers */
+#  include <linux/rtnetlink.h>
+#endif
+
 #include <ctype.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <syslog.h>
 #include <unistd.h>
 
+#include "arp.h"
 #include "config.h"
 #include "common.h"
 #include "dhcp.h"
+#include "dhcp-common.h"
+#include "eloop.h"
+#include "ipv4.h"
+#include "ipv4ll.h"
+#include "script.h"
+
+#define DAD            "Duplicate address detected"
+#define DHCP_MIN_LEASE 20
 
-#define DAD    "Duplicate address detected"
+static uint8_t *packet;
 
 /* Our aggregate option buffer.
  * We ONLY use this when options are split, which for most purposes is
  * practically never. See RFC3396 for details. */
 static uint8_t *opt_buffer;
 
+#define IPV4A          ADDRIPV4 | ARRAY
+#define IPV4R          ADDRIPV4 | REQUEST
+
+/* We should define a maximum for the NAK exponential backoff */ 
+#define NAKOFF_MAX              60
+
+/* Wait N nanoseconds between sending a RELEASE and dropping the address.
+ * This gives the kernel enough time to actually send it. */
+#define RELEASE_DELAY_S                0
+#define RELEASE_DELAY_NS       10000000
+
+struct dhcp_op {
+       uint8_t value;
+       const char *name;
+};
+
+static const struct dhcp_op dhcp_ops[] = {
+       { DHCP_DISCOVER, "DISCOVER" },
+       { DHCP_OFFER,    "OFFER" },
+       { DHCP_REQUEST,  "REQUEST" },
+       { DHCP_DECLINE,  "DECLINE" },
+       { DHCP_ACK,      "ACK" },
+       { DHCP_NAK,      "NAK" },
+       { DHCP_RELEASE,  "RELEASE" },
+       { DHCP_INFORM,   "INFORM" },
+       { 0, NULL }
+};
+
 const struct dhcp_opt const dhcp_opts[] = {
-       { 1,    IPV4 | REQUEST, "subnet_mask" },
+       { 1,    ADDRIPV4 | REQUEST,     "subnet_mask" },
                /* RFC 3442 states that the CSR has to come before all other
                 * routes. For completeness, we also specify static routes,
                 * then routers. */
        { 121,  RFC3442,        "classless_static_routes" },
        { 249,  RFC3442,        "ms_classless_static_routes" },
-       { 33,   IPV4 | ARRAY | REQUEST, "static_routes" },
-       { 3,    IPV4 | ARRAY | REQUEST, "routers" },
+       { 33,   IPV4A | REQUEST,        "static_routes" },
+       { 3,    IPV4A | REQUEST,        "routers" },
        { 2,    UINT32,         "time_offset" },
-       { 4,    IPV4 | ARRAY,   "time_servers" },
-       { 5,    IPV4 | ARRAY,   "ien116_name_servers" },
-       { 6,    IPV4 | ARRAY,   "domain_name_servers" },
-       { 7,    IPV4 | ARRAY,   "log_servers" },
-       { 8,    IPV4 | ARRAY,   "cookie_servers" },
-       { 9,    IPV4 | ARRAY,   "lpr_servers" },
-       { 10,   IPV4 | ARRAY,   "impress_servers" },
-       { 11,   IPV4 | ARRAY,   "resource_location_servers" },
+       { 4,    IPV4A,          "time_servers" },
+       { 5,    IPV4A,          "ien116_name_servers" },
+       { 6,    IPV4A,          "domain_name_servers" },
+       { 7,    IPV4A,          "log_servers" },
+       { 8,    IPV4A,          "cookie_servers" },
+       { 9,    IPV4A,          "lpr_servers" },
+       { 10,   IPV4A,          "impress_servers" },
+       { 11,   IPV4A,          "resource_location_servers" },
        { 12,   STRING,         "host_name" },
        { 13,   UINT16,         "boot_size" },
        { 14,   STRING,         "merit_dump" },
        { 15,   STRING,         "domain_name" },
-       { 16,   IPV4,           "swap_server" },
+       { 16,   ADDRIPV4,       "swap_server" },
        { 17,   STRING,         "root_path" },
        { 18,   STRING,         "extensions_path" },
        { 19,   UINT8,          "ip_forwarding" },
        { 20,   UINT8,          "non_local_source_routing" },
-       { 21,   IPV4 | ARRAY,   "policy_filter" },
+       { 21,   IPV4A,  "policy_filter" },
        { 22,   SINT16,         "max_dgram_reassembly" },
        { 23,   UINT16,         "default_ip_ttl" },
        { 24,   UINT32,         "path_mtu_aging_timeout" },
        { 25,   UINT16 | ARRAY, "path_mtu_plateau_table" },
        { 26,   UINT16,         "interface_mtu" },
        { 27,   UINT8,          "all_subnets_local" },
-       { 28,   IPV4 | REQUEST, "broadcast_address" },
+       { 28,   ADDRIPV4 | REQUEST,     "broadcast_address" },
        { 29,   UINT8,          "perform_mask_discovery" },
        { 30,   UINT8,          "mask_supplier" },
        { 31,   UINT8,          "router_discovery" },
-       { 32,   IPV4,           "router_solicitation_address" },
+       { 32,   ADDRIPV4,       "router_solicitation_address" },
        { 34,   UINT8,          "trailer_encapsulation" },
        { 35,   UINT32,         "arp_cache_timeout" },
        { 36,   UINT16,         "ieee802_3_encapsulation" },
@@ -90,52 +136,52 @@ const struct dhcp_opt const dhcp_opts[] = {
        { 38,   UINT32,         "tcp_keepalive_interval" },
        { 39,   UINT8,          "tcp_keepalive_garbage" },
        { 40,   STRING,         "nis_domain" },
-       { 41,   IPV4 | ARRAY,   "nis_servers" },
-       { 42,   IPV4 | ARRAY,   "ntp_servers" },
+       { 41,   IPV4A,          "nis_servers" },
+       { 42,   IPV4A,          "ntp_servers" },
        { 43,   STRING,         "vendor_encapsulated_options" },
-       { 44,   IPV4 | ARRAY,   "netbios_name_servers" },
-       { 45,   IPV4,           "netbios_dd_server" },
+       { 44,   IPV4A,          "netbios_name_servers" },
+       { 45,   ADDRIPV4,       "netbios_dd_server" },
        { 46,   UINT8,          "netbios_node_type" },
        { 47,   STRING,         "netbios_scope" },
-       { 48,   IPV4 | ARRAY,   "font_servers" },
-       { 49,   IPV4 | ARRAY,   "x_display_manager" },
-       { 50,   IPV4,           "dhcp_requested_address" },
+       { 48,   IPV4A,          "font_servers" },
+       { 49,   IPV4A,          "x_display_manager" },
+       { 50,   ADDRIPV4,       "dhcp_requested_address" },
        { 51,   UINT32 | REQUEST,       "dhcp_lease_time" },
        { 52,   UINT8,          "dhcp_option_overload" },
        { 53,   UINT8,          "dhcp_message_type" },
-       { 54,   IPV4,           "dhcp_server_identifier" },
+       { 54,   ADDRIPV4,       "dhcp_server_identifier" },
        { 55,   UINT8 | ARRAY,  "dhcp_parameter_request_list" },
        { 56,   STRING,         "dhcp_message" },
        { 57,   UINT16,         "dhcp_max_message_size" },
        { 58,   UINT32 | REQUEST,       "dhcp_renewal_time" },
        { 59,   UINT32 | REQUEST,       "dhcp_rebinding_time" },
        { 64,   STRING,         "nisplus_domain" },
-       { 65,   IPV4 | ARRAY,   "nisplus_servers" },
+       { 65,   IPV4A,          "nisplus_servers" },
        { 66,   STRING,         "tftp_server_name" },
        { 67,   STRING,         "bootfile_name" },
-       { 68,   IPV4 | ARRAY,   "mobile_ip_home_agent" },
-       { 69,   IPV4 | ARRAY,   "smtp_server" },
-       { 70,   IPV4 | ARRAY,   "pop_server" },
-       { 71,   IPV4 | ARRAY,   "nntp_server" },
-       { 72,   IPV4 | ARRAY,   "www_server" },
-       { 73,   IPV4 | ARRAY,   "finger_server" },
-       { 74,   IPV4 | ARRAY,   "irc_server" },
-       { 75,   IPV4 | ARRAY,   "streettalk_server" },
-       { 76,   IPV4 | ARRAY,   "streettalk_directory_assistance_server" },
+       { 68,   IPV4A,          "mobile_ip_home_agent" },
+       { 69,   IPV4A,          "smtp_server" },
+       { 70,   IPV4A,          "pop_server" },
+       { 71,   IPV4A,          "nntp_server" },
+       { 72,   IPV4A,          "www_server" },
+       { 73,   IPV4A,          "finger_server" },
+       { 74,   IPV4A,          "irc_server" },
+       { 75,   IPV4A,          "streettalk_server" },
+       { 76,   IPV4A,          "streettalk_directory_assistance_server" },
        { 77,   STRING,         "user_class" },
        { 81,   STRING | RFC3397,       "fqdn_name" },
-       { 85,   IPV4 | ARRAY,   "nds_servers" },
+       { 85,   IPV4A,          "nds_servers" },
        { 86,   STRING,         "nds_tree_name" },
        { 87,   STRING,         "nds_context" },
        { 88,   STRING | RFC3397,       "bcms_controller_names" },
-       { 89,   IPV4 | ARRAY,   "bcms_controller_address" },
+       { 89,   IPV4A,          "bcms_controller_address" },
        { 91,   UINT32,         "client_last_transaction_time" },
-       { 92,   IPV4 | ARRAY,   "associated_ip" },
+       { 92,   IPV4A,          "associated_ip" },
        { 98,   STRING,         "uap_servers" },
-       { 112,  IPV4 | ARRAY,   "netinfo_server_address" },
+       { 112,  IPV4A,          "netinfo_server_address" },
        { 113,  STRING,         "netinfo_server_tag" },
        { 114,  STRING,         "default_url" },
-       { 118,  IPV4,           "subnet_selection" },
+       { 118,  ADDRIPV4,       "subnet_selection" },
        { 119,  STRING | RFC3397,       "domain_search" },
        { 120,  STRING | RFC3361,       "sip_server" },
        { 212,  RFC5969,        "sixrd" },
@@ -151,6 +197,8 @@ static const char *dhcp_params[] = {
        NULL
 };
 
+static int dhcp_open(struct interface *);
+
 void
 print_options(void)
 {
@@ -165,55 +213,6 @@ print_options(void)
                        printf("%03d %s\n", opt->option, opt->var);
 }
 
-int make_option_mask(const struct dhcp_opt *dopts,
-    uint8_t *mask, const char *opts, int add)
-{
-       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;
-               for (opt = dopts; opt->option; opt++) {
-                       if (!opt->var)
-                               continue;
-                       match = 0;
-                       if (strcmp(opt->var, token) == 0)
-                               match = 1;
-                       else {
-                               errno = 0;
-                               n = strtol(token, &t, 0);
-                               if (errno == 0 && !*t)
-                                       if (opt->option == n)
-                                               match = 1;
-                       }
-                       if (match) {
-                               if (add == 2 && !(opt->type & IPV4)) {
-                                       free(o);
-                                       errno = EINVAL;
-                                       return -1;
-                               }
-                               if (add == 1 || add == 2)
-                                       add_option_mask(mask,
-                                           opt->option);
-                               else
-                                       del_option_mask(mask,
-                                           opt->option);
-                               break;
-                       }
-               }
-               if (!opt->option) {
-                       free(o);
-                       errno = ENOENT;
-                       return -1;
-               }
-       }
-       free(o);
-       return 0;
-}
-
 static int
 validate_length(uint8_t option, int dl, int *type)
 {
@@ -234,14 +233,14 @@ validate_length(uint8_t option, int dl, int *type)
                    opt->type & (STRING | RFC3442 | RFC5969))
                        return dl;
 
-               if (opt->type & IPV4 && opt->type & ARRAY) {
+               if (opt->type & ADDRIPV4 && opt->type & ARRAY) {
                        if (dl < (int)sizeof(uint32_t))
                                return -1;
                        return dl - (dl % sizeof(uint32_t));
                }
 
                sz = 0;
-               if (opt->type & (UINT32 | IPV4))
+               if (opt->type & (UINT32 | ADDRIPV4))
                        sz = sizeof(uint32_t);
                if (opt->type & UINT16)
                        sz = sizeof(uint16_t);
@@ -394,78 +393,7 @@ get_option_uint8(uint8_t *i, const struct dhcp_message *dhcp, uint8_t option)
        return 0;
 }
 
-/* Decode an RFC3397 DNS search order option into a space
- * separated string. Returns length of string (including
- * terminating zero) or zero on error. out may be NULL
- * to just determine output length. */
 ssize_t
-decode_rfc3397(char *out, ssize_t len, int pl, const uint8_t *p)
-{
-       const char *start;
-       ssize_t start_len;
-       const uint8_t *r, *q = p;
-       int count = 0, l, hops;
-       uint8_t ltype;
-
-       start = out;
-       start_len = len;
-       while (q - p < pl) {
-               r = NULL;
-               hops = 0;
-               /* We check we are inside our length again incase
-                * the data is NOT terminated correctly. */
-               while ((l = *q++) && q - p < pl) {
-                       ltype = l & 0xc0;
-                       if (ltype == 0x80 || ltype == 0x40)
-                               return 0;
-                       else if (ltype == 0xc0) { /* pointer */
-                               l = (l & 0x3f) << 8;
-                               l |= *q++;
-                               /* save source of first jump. */
-                               if (!r)
-                                       r = q;
-                               hops++;
-                               if (hops > 255)
-                                       return 0;
-                               q = p + l;
-                               if (q - p >= pl)
-                                       return 0;
-                       } else {
-                               /* straightforward name segment, add with '.' */
-                               count += l + 1;
-                               if (out) {
-                                       if ((ssize_t)l + 1 > len) {
-                                               errno = ENOBUFS;
-                                               return -1;
-                                       }
-                                       memcpy(out, q, l);
-                                       out += l;
-                                       *out++ = '.';
-                                       len -= l;
-                                       len--;
-                               }
-                               q += l;
-                       }
-               }
-               /* change last dot to space */
-               if (out && out != start)
-                       *(out - 1) = ' ';
-               if (r)
-                       q = r;
-       }
-
-       /* change last space to zero terminator */
-       if (out) {
-               if (out != start)
-                       *(out - 1) = '\0';
-               else if (start_len > 0)
-                       *out = '\0';
-       }
-
-       return count;  
-}
-
-static ssize_t
 decode_rfc3442(char *out, ssize_t len, int pl, const uint8_t *p)
 {
        const uint8_t *e;
@@ -571,7 +499,7 @@ decode_rfc3442_rt(int dl, const uint8_t *data)
        return routes;
 }
 
-static char *
+char *
 decode_rfc3361(int dl, const uint8_t *data)
 {
        uint8_t enc;
@@ -621,7 +549,7 @@ decode_rfc3361(int dl, const uint8_t *data)
 /* Decode an RFC5969 6rd order option into a space
  * separated string. Returns length of string (including
  * terminating zero) or zero on error. */
-static ssize_t
+ssize_t
 decode_rfc5969(char *out, ssize_t len, int pl, const uint8_t *p)
 {
        uint8_t ipv4masklen, ipv6prefixlen;
@@ -1164,232 +1092,6 @@ read_lease(const struct interface *iface)
        return dhcp;
 }
 
-ssize_t
-print_string(char *s, ssize_t len, int dl, const uint8_t *data)
-{
-       uint8_t c;
-       const uint8_t *e, *p;
-       ssize_t bytes = 0;
-       ssize_t r;
-
-       e = data + dl;
-       while (data < e) {
-               c = *data++;
-               if (c == '\0') {
-                       /* If rest is all NULL, skip it. */
-                       for (p = data; p < e; p++)
-                               if (*p != '\0')
-                                       break;
-                       if (p == e)
-                               break;
-               }
-               if (!isascii(c) || !isprint(c)) {
-                       if (s) {
-                               if (len < 5) {
-                                       errno = ENOBUFS;
-                                       return -1;
-                               }
-                               r = snprintf(s, len, "\\%03o", c);
-                               len -= r;
-                               bytes += r;
-                               s += r;
-                       } else
-                               bytes += 4;
-                       continue;
-               }
-               switch (c) {
-               case '"':  /* FALLTHROUGH */
-               case '\'': /* FALLTHROUGH */
-               case '$':  /* FALLTHROUGH */
-               case '`':  /* FALLTHROUGH */
-               case '\\': /* FALLTHROUGH */
-               case '|':  /* FALLTHROUGH */
-               case '&':
-                       if (s) {
-                               if (len < 3) {
-                                       errno = ENOBUFS;
-                                       return -1;
-                               }
-                               *s++ = '\\';
-                               len--;
-                       }
-                       bytes++;
-                       break;
-               }
-               if (s) {
-                       *s++ = c;
-                       len--;
-               }
-               bytes++;
-       }
-
-       /* NULL */
-       if (s)
-               *s = '\0';
-       bytes++;
-       return bytes;
-}
-
-ssize_t
-print_option(char *s, ssize_t len, int type, int dl, const uint8_t *data,
-    const char *ifname)
-{
-       const uint8_t *e, *t;
-       uint16_t u16;
-       int16_t s16;
-       uint32_t u32;
-       int32_t s32;
-       struct in_addr addr;
-       ssize_t bytes = 0;
-       ssize_t l;
-       char *tmp;
-
-       if (type & RFC3397) {
-               l = decode_rfc3397(NULL, 0, dl, data);
-               if (l < 1)
-                       return l;
-               tmp = xmalloc(l);
-               decode_rfc3397(tmp, l, dl, data);
-               l = print_string(s, len, l - 1, (uint8_t *)tmp);
-               free(tmp);
-               return l;
-       }
-
-       if (type & RFC3361) {
-               if ((tmp = decode_rfc3361(dl, data)) == NULL)
-                       return -1;
-               l = strlen(tmp);
-               l = print_string(s, len, l - 1, (uint8_t *)tmp);
-               free(tmp);
-               return l;
-       }
-
-       if (type & RFC3442)
-               return decode_rfc3442(s, len, dl, data);
-
-       if (type & RFC5969)
-               return decode_rfc5969(s, len, dl, data);
-
-       if (type & STRING) {
-               /* Some DHCP servers return NULL strings */
-               if (*data == '\0')
-                       return 0;
-               return print_string(s, len, dl, data);
-       }
-
-       /* DHCPv6 status code */
-       if (type & SCODE && dl >= (int)sizeof(u16)) {
-               if (s) {
-                       memcpy(&u16, data, sizeof(u16));
-                       u16 = ntohs(u16);
-                       l = snprintf(s, len, "%d ", u16);
-                       len -= l;
-               } else
-                       l = 7;
-               data += sizeof(u16);
-               dl -= sizeof(u16);
-               if (dl)
-                       l += print_option(s, len, STRING, dl, data, ifname);
-               return l;
-       }
-
-       if (type & IPV6) {
-       }
-
-       if (!s) {
-               if (type & UINT8)
-                       l = 3;
-               else if (type & UINT16) {
-                       l = 5;
-                       dl /= 2;
-               } else if (type & SINT16) {
-                       l = 6;
-                       dl /= 2;
-               } else if (type & UINT32) {
-                       l = 10;
-                       dl /= 4;
-               } else if (type & SINT32) {
-                       l = 11;
-                       dl /= 4;
-               } else if (type & IPV4) {
-                       l = 16;
-                       dl /= 4;
-               } else if (type & IPV6) {
-                       e = data + dl;
-                       l = 0;
-                       while (data < e) {
-                               if (l)
-                                       l++; /* space */
-                               dl = ipv6_printaddr(NULL, 0, data, ifname);
-                               if (dl != -1)
-                                       l += dl;
-                               data += 16;
-                       }
-                       return l + 1;
-               } else if (type & BINHEX) {
-                       l = 2;
-               } else {
-                       errno = EINVAL;
-                       return -1;
-               }
-               return (l + 1) * dl;
-       }
-
-       t = data;
-       e = data + dl;
-       while (data < e) {
-               if (data != t && type != BINHEX) {
-                       *s++ = ' ';
-                       bytes++;
-                       len--;
-               }
-               if (type & UINT8) {
-                       l = snprintf(s, len, "%d", *data);
-                       data++;
-               } else if (type & UINT16) {
-                       memcpy(&u16, data, sizeof(u16));
-                       u16 = ntohs(u16);
-                       l = snprintf(s, len, "%d", u16);
-                       data += sizeof(u16);
-               } else if (type & SINT16) {
-                       memcpy(&s16, data, sizeof(s16));
-                       s16 = ntohs(s16);
-                       l = snprintf(s, len, "%d", s16);
-                       data += sizeof(s16);
-               } else if (type & UINT32) {
-                       memcpy(&u32, data, sizeof(u32));
-                       u32 = ntohl(u32);
-                       l = snprintf(s, len, "%d", u32);
-                       data += sizeof(u32);
-               } else if (type & SINT32) {
-                       memcpy(&s32, data, sizeof(s32));
-                       s32 = ntohl(s32);
-                       l = snprintf(s, len, "%d", s32);
-                       data += sizeof(s32);
-               } else if (type & IPV4) {
-                       memcpy(&addr.s_addr, data, sizeof(addr.s_addr));
-                       l = snprintf(s, len, "%s", inet_ntoa(addr));
-                       data += sizeof(addr.s_addr);
-               } else if (type & IPV6) {
-                       dl = ipv6_printaddr(s, len, data, ifname);
-                       if (dl != -1)
-                               l = dl;
-                       else
-                               l = 0;  
-                       data += 16;
-               } else if (type & BINHEX) {
-                       l = snprintf(s, len, "%.2x", data[0]);
-                       data++; 
-               } else
-                       l = 0;
-               len -= l;
-               bytes += l;
-               s += l;
-       }
-
-       return bytes;
-}
-
 ssize_t
 configure_env(char **env, const char *prefix, const struct dhcp_message *dhcp,
     const struct interface *ifp)
@@ -1509,3 +1211,1000 @@ get_lease(struct dhcp_lease *lease, const struct dhcp_message *dhcp)
        if (get_option_addr(&lease->server, dhcp, DHO_SERVERID) != 0)
                lease->server.s_addr = INADDR_ANY;
 }
+
+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;
+}
+
+static void
+dhcp_fallback(void *arg)
+{
+       struct interface *iface;
+
+       iface = (struct interface *)arg;
+       select_profile(iface, iface->state->options->fallback);
+       start_interface(iface);
+}
+
+uint32_t
+dhcp_xid(const struct interface *ifp)
+{
+       uint32_t xid;
+
+       if (ifp->state->options->options & DHCPCD_XID_HWADDR &&
+           ifp->hwlen >= sizeof(xid)) 
+               /* The lower bits are probably more unique on the network */
+               memcpy(&xid, (ifp->hwaddr + ifp->hwlen) - sizeof(xid),
+                   sizeof(xid));
+       else
+               xid = arc4random();
+
+       return xid;
+}
+
+void
+dhcp_close(struct interface *iface)
+{
+
+       if (iface->arp_fd != -1) {
+               eloop_event_delete(iface->arp_fd);
+               close(iface->arp_fd);
+               iface->arp_fd = -1;
+       }
+       if (iface->raw_fd != -1) {
+               eloop_event_delete(iface->raw_fd);
+               close(iface->raw_fd);
+               iface->raw_fd = -1;
+       }
+       if (iface->udp_fd != -1) {
+               /* we don't listen to events on the udp */
+               close(iface->udp_fd);
+               iface->udp_fd = -1;
+       }
+}
+
+static void
+send_message(struct interface *iface, int type,
+    void (*callback)(void *))
+{
+       struct if_state *state = iface->state;
+       struct if_options *ifo = state->options;
+       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 (!callback)
+               syslog(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);
+               timernorm(&tv);
+               syslog(LOG_DEBUG,
+                   "%s: sending %s (xid 0x%x), next in %0.2f seconds",
+                   iface->name, get_dhcp_op(type), state->xid,
+                   timeval_to_double(&tv));
+       }
+
+       /* Ensure sockets are open. */
+       if (dhcp_open(iface) == -1) {
+               if (!(options & DHCPCD_TEST))
+                       dhcp_drop(iface, "FAIL");
+               return;
+       }
+
+       /* 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.
+        * Also, we should not unicast from a BOOTP lease. */
+       if (iface->udp_fd == -1 ||
+           (!(ifo->options & DHCPCD_INFORM) && is_bootp(iface->state->new)))
+       {
+               a = iface->addr.s_addr;
+               iface->addr.s_addr = 0;
+       }
+       len = make_message(&dhcp, iface, type);
+       if (a)
+               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) {
+                       syslog(LOG_ERR, "%s: send_packet: %m", iface->name);
+                       dhcp_close(iface);
+               }
+       } else {
+               len = make_udp_packet(&udp, (uint8_t *)dhcp, len, from, to);
+               r = send_raw_packet(iface, ETHERTYPE_IP, udp, len);
+               free(udp);
+               /* If we failed to send a raw packet this normally means
+                * we don't have the ability to work beneath the IP layer
+                * for this interface.
+                * As such we remove it from consideration without actually
+                * stopping the interface. */
+               if (r == -1) {
+                       syslog(LOG_ERR, "%s: send_raw_packet: %m", iface->name);
+                       if (!(options & DHCPCD_TEST))
+                               dhcp_drop(iface, "FAIL");
+                       dhcp_close(iface);
+                       eloop_timeout_delete(NULL, iface);
+                       callback = NULL;
+               }
+       }
+       free(dhcp);
+
+       /* Even if we fail to send a packet we should continue as we are
+        * as our failure timeouts will change out codepath when needed. */
+       if (callback)
+               eloop_timeout_add_tv(&tv, callback, iface);
+}
+
+static void
+send_inform(void *arg)
+{
+
+       send_message((struct interface *)arg, DHCP_INFORM, send_inform);
+}
+
+static void
+send_discover(void *arg)
+{
+
+       send_message((struct interface *)arg, DHCP_DISCOVER, send_discover);
+}
+
+static void
+send_request(void *arg)
+{
+
+       send_message((struct interface *)arg, DHCP_REQUEST, send_request);
+}
+
+static void
+send_renew(void *arg)
+{
+
+       send_message((struct interface *)arg, DHCP_REQUEST, send_renew);
+}
+
+static void
+send_rebind(void *arg)
+{
+
+       send_message((struct interface *)arg, DHCP_REQUEST, send_rebind);
+}
+
+void
+dhcp_discover(void *arg)
+{
+       struct interface *iface = arg;
+       struct if_options *ifo = iface->state->options;
+       int timeout = ifo->timeout;
+
+       /* If we're rebooting and we're not daemonised then we need
+        * to shorten the normal timeout to ensure we try correctly
+        * for a fallback or IPv4LL address. */
+       if (iface->state->state == DHS_REBOOT &&
+           !(options & DHCPCD_DAEMONISED))
+       {
+               timeout -= ifo->reboot;
+               if (timeout <= 0)
+                       timeout = 2;
+       }
+
+       iface->state->state = DHS_DISCOVER;
+       iface->state->xid = dhcp_xid(iface);
+       eloop_timeout_delete(NULL, iface);
+       if (ifo->fallback)
+               eloop_timeout_add_sec(timeout, dhcp_fallback, iface);
+       else if (ifo->options & DHCPCD_IPV4LL &&
+           !IN_LINKLOCAL(htonl(iface->addr.s_addr)))
+       {
+               if (IN_LINKLOCAL(htonl(iface->state->fail.s_addr)))
+                       eloop_timeout_add_sec(RATE_LIMIT_INTERVAL,
+                           ipv4ll_start, iface);
+               else
+                       eloop_timeout_add_sec(timeout, ipv4ll_start, iface);
+       }
+       if (ifo->options & DHCPCD_REQUEST)
+               syslog(LOG_INFO, "%s: broadcasting for a lease (requesting %s)",
+                   iface->name, inet_ntoa(ifo->req_addr));
+       else
+               syslog(LOG_INFO, "%s: broadcasting for a lease", iface->name);
+       send_discover(iface);
+}
+
+static void
+dhcp_request(void *arg)
+{
+       struct interface *iface = arg;
+
+       iface->state->state = DHS_REQUEST;
+       send_request(iface);
+}
+
+static void
+dhcp_expire(void *arg)
+{
+       struct interface *iface = arg;
+
+       iface->state->interval = 0;
+       if (iface->addr.s_addr == 0) {
+               /* We failed to reboot, so enter discovery. */
+               iface->state->lease.addr.s_addr = 0;
+               dhcp_discover(iface);
+               return;
+       }
+
+       syslog(LOG_ERR, "%s: lease expired", iface->name);
+       eloop_timeout_delete(NULL, iface);
+       dhcp_drop(iface, "EXPIRE");
+       unlink(iface->leasefile);
+       if (iface->carrier != LINK_DOWN)
+               start_interface(iface);
+}
+
+void
+dhcp_release(struct interface *iface)
+{
+       struct timespec ts;
+
+       if (iface->state->new != NULL &&
+           iface->state->new->cookie == htonl(MAGIC_COOKIE))
+       {
+               syslog(LOG_INFO, "%s: releasing lease of %s",
+                   iface->name, inet_ntoa(iface->state->lease.addr));
+               iface->state->xid = dhcp_xid(iface);
+               send_message(iface, DHCP_RELEASE, NULL);
+               /* Give the packet a chance to go before dropping the ip */
+               ts.tv_sec = RELEASE_DELAY_S;
+               ts.tv_nsec = RELEASE_DELAY_NS;
+               nanosleep(&ts, NULL);
+               dhcp_drop(iface, "RELEASE");
+       }
+       unlink(iface->leasefile);
+}
+
+void
+dhcp_decline(struct interface *ifp)
+{
+
+       send_message(ifp, DHCP_DECLINE, NULL);
+}
+
+static void
+dhcp_renew(void *arg)
+{
+       struct interface *iface = arg;
+       struct dhcp_lease *lease = &iface->state->lease;
+
+       syslog(LOG_INFO, "%s: renewing lease of %s",
+           iface->name, inet_ntoa(lease->addr));
+       syslog(LOG_DEBUG, "%s: rebind in %u seconds, expire in %u seconds",
+           iface->name, lease->rebindtime - lease->renewaltime,
+           lease->leasetime - lease->renewaltime);
+       iface->state->state = DHS_RENEW;
+       iface->state->xid = dhcp_xid(iface);
+       send_renew(iface);
+}
+
+static void
+dhcp_rebind(void *arg)
+{
+       struct interface *iface = arg;
+       struct dhcp_lease *lease = &iface->state->lease;
+
+       syslog(LOG_ERR, "%s: failed to renew, attempting to rebind",
+           iface->name);
+       syslog(LOG_DEBUG, "%s: expire in %u seconds",
+           iface->name, lease->leasetime - lease->rebindtime);
+       iface->state->state = DHS_REBIND;
+       eloop_timeout_delete(send_renew, iface);
+       iface->state->lease.server.s_addr = 0;
+       send_rebind(iface);
+}
+
+void
+dhcp_bind(void *arg)
+{
+       struct interface *iface = arg;
+       struct if_state *state = iface->state;
+       struct if_options *ifo = state->options;
+       struct dhcp_lease *lease = &state->lease;
+       struct timeval tv;
+
+       /* We're binding an address now - ensure that sockets are closed */
+       dhcp_close(iface);
+       state->reason = NULL;
+       if (clock_monotonic)
+               get_monotonic(&lease->boundtime);
+       state->xid = 0;
+       free(state->old);
+       state->old = state->new;
+       state->new = state->offer;
+       state->offer = NULL;
+       get_lease(lease, state->new);
+       if (ifo->options & DHCPCD_STATIC) {
+               syslog(LOG_INFO, "%s: using static address %s",
+                   iface->name, inet_ntoa(lease->addr));
+               lease->leasetime = ~0U;
+               lease->net.s_addr = ifo->req_mask.s_addr;
+               state->reason = "STATIC";
+       } else if (state->new->cookie != htonl(MAGIC_COOKIE)) {
+               syslog(LOG_INFO, "%s: using IPv4LL address %s",
+                   iface->name, inet_ntoa(lease->addr));
+               lease->leasetime = ~0U;
+               state->reason = "IPV4LL";
+       } else if (ifo->options & DHCPCD_INFORM) {
+               if (ifo->req_addr.s_addr != 0)
+                       lease->addr.s_addr = ifo->req_addr.s_addr;
+               else
+                       lease->addr.s_addr = iface->addr.s_addr;
+               syslog(LOG_INFO, "%s: received approval for %s", iface->name,
+                   inet_ntoa(lease->addr));
+               lease->leasetime = ~0U;
+               state->reason = "INFORM";
+       } else {
+               if (gettimeofday(&tv, NULL) == 0)
+                       lease->leasedfrom = tv.tv_sec;
+               else if (lease->frominfo)
+                       state->reason = "TIMEOUT";
+               if (lease->leasetime == ~0U) {
+                       lease->renewaltime =
+                           lease->rebindtime =
+                           lease->leasetime;
+                       syslog(LOG_INFO, "%s: leased %s for infinity",
+                           iface->name, inet_ntoa(lease->addr));
+               } else {
+                       if (lease->leasetime < DHCP_MIN_LEASE) {
+                               syslog(LOG_WARNING,
+                                   "%s: minimum lease is %d seconds",
+                                   iface->name, DHCP_MIN_LEASE);
+                               lease->leasetime = DHCP_MIN_LEASE;
+                       }
+                       if (lease->rebindtime == 0)
+                               lease->rebindtime = lease->leasetime * T2;
+                       else if (lease->rebindtime >= lease->leasetime) {
+                               lease->rebindtime = lease->leasetime * T2;
+                               syslog(LOG_ERR,
+                                   "%s: rebind time greater than lease "
+                                   "time, forcing to %u seconds",
+                                   iface->name, lease->rebindtime);
+                       }
+                       if (lease->renewaltime == 0)
+                               lease->renewaltime = lease->leasetime * T1;
+                       else if (lease->renewaltime > lease->rebindtime) {
+                               lease->renewaltime = lease->leasetime * T1;
+                               syslog(LOG_ERR,
+                                   "%s: renewal time greater than rebind "
+                                   "time, forcing to %u seconds",
+                                   iface->name, lease->renewaltime);
+                       }
+                       syslog(LOG_INFO,
+                           "%s: leased %s for %u seconds", iface->name,
+                           inet_ntoa(lease->addr), lease->leasetime);
+               }
+       }
+       if (options & DHCPCD_TEST) {
+               state->reason = "TEST";
+               script_run(iface);
+               exit(EXIT_SUCCESS);
+       }
+       if (state->reason == NULL) {
+               if (state->old) {
+                       if (state->old->yiaddr == state->new->yiaddr &&
+                           lease->server.s_addr)
+                               state->reason = "RENEW";
+                       else
+                               state->reason = "REBIND";
+               } else if (state->state == DHS_REBOOT)
+                       state->reason = "REBOOT";
+               else
+                       state->reason = "BOUND";
+       }
+       if (lease->leasetime == ~0U)
+               lease->renewaltime = lease->rebindtime = lease->leasetime;
+       else {
+               eloop_timeout_add_sec(lease->renewaltime, dhcp_renew, iface);
+               eloop_timeout_add_sec(lease->rebindtime, dhcp_rebind, iface);
+               eloop_timeout_add_sec(lease->leasetime, dhcp_expire, iface);
+               syslog(LOG_DEBUG,
+                   "%s: renew in %u seconds, rebind in %u seconds",
+                   iface->name, lease->renewaltime, lease->rebindtime);
+       }
+       ifo->options &= ~ DHCPCD_CSR_WARNED;
+       ipv4_applyaddr(iface);
+       daemonise();
+       state->state = DHS_BOUND;
+       if (ifo->options & DHCPCD_ARP) {
+               state->claims = 0;
+               arp_announce(iface);
+       }
+}
+
+
+static void
+dhcp_timeout(void *arg)
+{
+       struct interface *iface = arg;
+
+       dhcp_bind(iface);
+       iface->state->interval = 0;
+       dhcp_discover(iface);
+}
+
+struct dhcp_message *
+dhcp_message_new(struct in_addr *addr, struct in_addr *mask)
+{
+       struct dhcp_message *dhcp;
+       uint8_t *p;
+
+       dhcp = xzalloc(sizeof(*dhcp));
+       dhcp->yiaddr = addr->s_addr;
+       p = dhcp->options;
+       if (mask && mask->s_addr != INADDR_ANY) {
+               *p++ = DHO_SUBNETMASK;
+               *p++ = sizeof(mask->s_addr);
+               memcpy(p, &mask->s_addr, sizeof(mask->s_addr));
+               p+= sizeof(mask->s_addr);
+       }
+       *p++ = DHO_END;
+       return dhcp;
+}
+
+static int
+handle_3rdparty(struct interface *iface)
+{
+       struct if_options *ifo;
+       struct in_addr addr, net, dst;
+       
+       ifo = iface->state->options;
+       if (ifo->req_addr.s_addr != INADDR_ANY)
+               return 0;
+
+       if (get_address(iface->name, &addr, &net, &dst) == 1)
+               ipv4_handleifa(RTM_NEWADDR, iface->name, &addr, &net, &dst);
+       else {
+               syslog(LOG_INFO,
+                   "%s: waiting for 3rd party to configure IP address",
+                   iface->name);
+               iface->state->reason = "3RDPARTY";
+               script_run(iface);
+       }
+       return 1;
+}
+
+static void
+dhcp_static(struct interface *iface)
+{
+       struct if_options *ifo;
+
+       if (handle_3rdparty(iface))
+               return;
+       ifo = iface->state->options;
+       iface->state->offer =
+           dhcp_message_new(&ifo->req_addr, &ifo->req_mask);
+       eloop_timeout_delete(NULL, iface);
+       dhcp_bind(iface);
+}
+
+void
+dhcp_inform(struct interface *iface)
+{
+
+       if (handle_3rdparty(iface))
+               return;
+
+       if (options & DHCPCD_TEST) {
+               iface->addr.s_addr = iface->state->options->req_addr.s_addr;
+               iface->net.s_addr = iface->state->options->req_mask.s_addr;
+       } else {
+               iface->state->options->options |= DHCPCD_STATIC;
+               dhcp_static(iface);
+       }
+
+       iface->state->state = DHS_INFORM;
+       iface->state->xid = dhcp_xid(iface);
+       send_inform(iface);
+}
+
+static void
+dhcp_reboot(struct interface *iface)
+{
+       struct if_options *ifo = iface->state->options;
+
+       if (ifo->options & DHCPCD_LINK && iface->carrier == LINK_DOWN) {
+               syslog(LOG_INFO, "%s: waiting for carrier", iface->name);
+               return;
+       }
+       if (ifo->options & DHCPCD_STATIC) {
+               dhcp_static(iface);
+               return;
+       }
+       if (ifo->reboot == 0 || iface->state->offer == NULL) {
+               dhcp_discover(iface);
+               return;
+       }
+       if (ifo->options & DHCPCD_INFORM) {
+               syslog(LOG_INFO, "%s: informing address of %s",
+                   iface->name, inet_ntoa(iface->state->lease.addr));
+       } else if (iface->state->offer->cookie == 0) {
+               if (ifo->options & DHCPCD_IPV4LL) {
+                       iface->state->claims = 0;
+                       arp_announce(iface);
+               } else
+                       dhcp_discover(iface);
+               return;
+       } else {
+               syslog(LOG_INFO, "%s: rebinding lease of %s",
+                   iface->name, inet_ntoa(iface->state->lease.addr));
+       }
+       iface->state->state = DHS_REBOOT;
+       iface->state->xid = dhcp_xid(iface);
+       iface->state->lease.server.s_addr = 0;
+       eloop_timeout_delete(NULL, iface);
+       if (ifo->fallback)
+               eloop_timeout_add_sec(ifo->reboot, dhcp_fallback, iface);
+       else if (ifo->options & DHCPCD_LASTLEASE &&
+           iface->state->lease.frominfo)
+               eloop_timeout_add_sec(ifo->reboot, dhcp_timeout, iface);
+       else if (!(ifo->options & DHCPCD_INFORM &&
+           options & (DHCPCD_MASTER | DHCPCD_DAEMONISED)))
+               eloop_timeout_add_sec(ifo->reboot, dhcp_expire, iface);
+       /* Don't bother ARP checking as the server could NAK us first. */
+       if (ifo->options & DHCPCD_INFORM)
+               dhcp_inform(iface);
+       else
+               dhcp_request(iface);
+}
+
+
+void
+dhcp_drop(struct interface *iface, const char *reason)
+{
+
+       eloop_timeouts_delete(iface, dhcp_expire, NULL);
+       free(iface->state->old);
+       iface->state->old = iface->state->new;
+       iface->state->new = NULL;
+       iface->state->reason = reason;
+       ipv4_applyaddr(iface);
+       free(iface->state->old);
+       iface->state->old = NULL;
+       iface->state->lease.addr.s_addr = 0;
+}
+
+static void
+log_dhcp(int lvl, const char *msg,
+    const struct interface *iface, const struct dhcp_message *dhcp,
+    const struct in_addr *from)
+{
+       const char *tfrom;
+       char *a;
+       struct in_addr addr;
+       int r;
+
+       if (strcmp(msg, "NAK:") == 0)
+               a = get_option_string(dhcp, DHO_MESSAGE);
+       else if (dhcp->yiaddr != 0) {
+               addr.s_addr = dhcp->yiaddr;
+               a = xstrdup(inet_ntoa(addr));
+       } else
+               a = NULL;
+
+       tfrom = "from";
+       r = get_option_addr(&addr, dhcp, DHO_SERVERID);
+       if (dhcp->servername[0] && r == 0)
+               syslog(lvl, "%s: %s %s %s %s `%s'", iface->name, msg, a,
+                   tfrom, inet_ntoa(addr), dhcp->servername);
+       else {
+               if (r != 0) {
+                       tfrom = "via";
+                       addr = *from;
+               }
+               if (a == NULL)
+                       syslog(lvl, "%s: %s %s %s",
+                           iface->name, msg, tfrom, inet_ntoa(addr));
+               else
+                       syslog(lvl, "%s: %s %s %s %s",
+                           iface->name, msg, a, tfrom, inet_ntoa(addr));
+       }
+       free(a);
+}
+
+static int
+blacklisted_ip(const struct if_options *ifo, in_addr_t addr)
+{
+       size_t i;
+       
+       for (i = 0; i < ifo->blacklist_len; i += 2)
+               if (ifo->blacklist[i] == (addr & ifo->blacklist[i + 1]))
+                       return 1;
+       return 0;
+}
+
+static int
+whitelisted_ip(const struct if_options *ifo, in_addr_t addr)
+{
+       size_t i;
+
+       if (ifo->whitelist_len == 0)
+               return -1;
+       for (i = 0; i < ifo->whitelist_len; i += 2)
+               if (ifo->whitelist[i] == (addr & ifo->whitelist[i + 1]))
+                       return 1;
+       return 0;
+}
+
+static void
+dhcp_handle(struct interface *iface, struct dhcp_message **dhcpp,
+    const struct in_addr *from)
+{
+       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;
+
+       /* reset the message counter */
+       state->interval = 0;
+
+       /* We may have found a BOOTP server */
+       if (get_option_uint8(&type, dhcp, DHO_MESSAGETYPE) == -1) 
+               type = 0;
+
+       if (type == DHCP_NAK) {
+               /* For NAK, only check if we require the ServerID */
+               if (has_option_mask(ifo->requiremask, DHO_SERVERID) &&
+                   get_option_addr(&addr, dhcp, DHO_SERVERID) == -1)
+               {
+                       log_dhcp(LOG_WARNING, "reject NAK", iface, dhcp, from);
+                       return;
+               }
+               /* We should restart on a NAK */
+               log_dhcp(LOG_WARNING, "NAK:", iface, dhcp, from);
+               if (!(options & DHCPCD_TEST)) {
+                       dhcp_drop(iface, "NAK");
+                       unlink(iface->leasefile);
+               }
+               dhcp_close(iface);
+               /* If we constantly get NAKS then we should slowly back off */
+               eloop_timeout_add_sec(state->nakoff, start_interface, iface);
+               if (state->nakoff == 0)
+                       state->nakoff = 1;
+               else {
+                       state->nakoff *= 2;
+                       if (state->nakoff > NAKOFF_MAX)
+                               state->nakoff = NAKOFF_MAX;
+               }
+               return;
+       }
+
+       /* 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)
+               {
+                       /* If we are bootp, then ignore the need for serverid.
+                        * To ignore bootp, require dhcp_message_type instead. */
+                       if (type == 0 && i == DHO_SERVERID)
+                               continue;
+                       log_dhcp(LOG_WARNING, "reject DHCP", iface, dhcp, from);
+                       return;
+               }
+       }
+
+       /* Ensure that the address offered is valid */
+       if ((type == 0 || type == DHCP_OFFER || type == DHCP_ACK) &&
+           (dhcp->ciaddr == INADDR_ANY || dhcp->ciaddr == INADDR_BROADCAST) &&
+           (dhcp->yiaddr == INADDR_ANY || dhcp->yiaddr == INADDR_BROADCAST))
+       {
+               log_dhcp(LOG_WARNING, "reject invalid address",
+                   iface, dhcp, from);
+               return;
+       }
+
+       /* No NAK, so reset the backoff */
+       state->nakoff = 0;
+
+       if ((type == 0 || type == DHCP_OFFER) &&
+           state->state == DHS_DISCOVER)
+       {
+               lease->frominfo = 0;
+               lease->addr.s_addr = dhcp->yiaddr;
+               lease->cookie = dhcp->cookie;
+               if (type == 0 ||
+                   get_option_addr(&lease->server, dhcp, DHO_SERVERID) != 0)
+                       lease->server.s_addr = INADDR_ANY;
+               log_dhcp(LOG_INFO, "offered", iface, dhcp, from);
+               free(state->offer);
+               state->offer = dhcp;
+               *dhcpp = NULL;
+               if (options & DHCPCD_TEST) {
+                       free(state->old);
+                       state->old = state->new;
+                       state->new = state->offer;
+                       state->offer = NULL;
+                       state->reason = "TEST";
+                       script_run(iface);
+                       exit(EXIT_SUCCESS);
+               }
+               eloop_timeout_delete(send_discover, iface);
+               /* We don't request BOOTP addresses */
+               if (type) {
+                       /* We used to ARP check here, but that seems to be in
+                        * violation of RFC2131 where it only describes
+                        * DECLINE after REQUEST.
+                        * It also seems that some MS DHCP servers actually
+                        * ignore DECLINE if no REQUEST, ie we decline a
+                        * DISCOVER. */
+                       dhcp_request(iface);
+                       return;
+               }
+       }
+
+       if (type) {
+               if (type == DHCP_OFFER) {
+                       log_dhcp(LOG_INFO, "ignoring offer of",
+                           iface, dhcp, from);
+                       return;
+               }
+
+               /* We should only be dealing with acks */
+               if (type != DHCP_ACK) {
+                       log_dhcp(LOG_ERR, "not ACK or OFFER",
+                           iface, dhcp, from);
+                       return;
+               }
+
+               if (!(ifo->options & DHCPCD_INFORM))
+                       log_dhcp(LOG_INFO, "acknowledged", iface, dhcp, from);
+       }
+
+       /* BOOTP could have already assigned this above, so check we still
+        * have a pointer. */
+       if (*dhcpp) {
+               free(state->offer);
+               state->offer = dhcp;
+               *dhcpp = NULL;
+       }
+
+       lease->frominfo = 0;
+       eloop_timeout_delete(NULL, iface);
+
+       /* We now have an offer, so close the DHCP sockets.
+        * This allows us to safely ARP when broken DHCP servers send an ACK
+        * follows by an invalid NAK. */
+       dhcp_close(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) != 1) {
+                       state->claims = 0;
+                       state->probes = 0;
+                       state->conflicts = 0;
+                       state->state = DHS_PROBE;
+                       arp_probe(iface);
+                       return;
+               }
+       }
+
+       dhcp_bind(iface);
+}
+
+static void
+dhcp_handlepacket(void *arg)
+{
+       struct interface *iface = arg;
+       struct dhcp_message *dhcp = NULL;
+       const uint8_t *pp;
+       ssize_t bytes;
+       struct in_addr from;
+       int i, partialcsum = 0;
+
+       /* We loop through until our buffer is empty.
+        * The benefit is that if we get >1 DHCP packet in our buffer and
+        * the first one fails for any reason, we can use the next. */
+       if (packet == NULL)
+               packet = xmalloc(udp_dhcp_len);
+       for(;;) {
+               bytes = get_raw_packet(iface, ETHERTYPE_IP,
+                   packet, udp_dhcp_len, &partialcsum);
+               if (bytes == 0 || bytes == -1)
+                       break;
+               if (valid_udp_packet(packet, bytes, &from, partialcsum) == -1) {
+                       syslog(LOG_ERR, "%s: invalid UDP packet from %s",
+                           iface->name, inet_ntoa(from));
+                       continue;
+               }
+               i = whitelisted_ip(iface->state->options, from.s_addr);
+               if (i == 0) {
+                       syslog(LOG_WARNING,
+                           "%s: non whitelisted DHCP packet from %s",
+                           iface->name, inet_ntoa(from));
+                       continue;
+               } else if (i != 1 &&
+                   blacklisted_ip(iface->state->options, from.s_addr) == 1)
+               {
+                       syslog(LOG_WARNING,
+                           "%s: blacklisted DHCP packet from %s",
+                           iface->name, inet_ntoa(from));
+                       continue;
+               }
+               if (iface->flags & IFF_POINTOPOINT &&
+                   iface->dst.s_addr != from.s_addr)
+               {
+                       syslog(LOG_WARNING,
+                           "%s: server %s is not destination",
+                           iface->name, inet_ntoa(from));
+               }
+               bytes = get_udp_data(&pp, packet);
+               if ((size_t)bytes > sizeof(*dhcp)) {
+                       syslog(LOG_ERR,
+                           "%s: packet greater than DHCP size from %s",
+                           iface->name, inet_ntoa(from));
+                       continue;
+               }
+               if (!dhcp)
+                       dhcp = xzalloc(sizeof(*dhcp));
+               memcpy(dhcp, pp, bytes);
+               if (dhcp->cookie != htonl(MAGIC_COOKIE)) {
+                       syslog(LOG_DEBUG, "%s: bogus cookie from %s",
+                           iface->name, inet_ntoa(from));
+                       continue;
+               }
+               /* Ensure it's the right transaction */
+               if (iface->state->xid != ntohl(dhcp->xid)) {
+                       syslog(LOG_DEBUG,
+                           "%s: wrong xid 0x%x (expecting 0x%x) from %s",
+                           iface->name, ntohl(dhcp->xid), iface->state->xid,
+                           inet_ntoa(from));
+                       continue;
+               }
+               /* Ensure packet is for us */
+               if (iface->hwlen <= sizeof(dhcp->chaddr) &&
+                   memcmp(dhcp->chaddr, iface->hwaddr, iface->hwlen))
+               {
+                       syslog(LOG_DEBUG, "%s: xid 0x%x is not for hwaddr %s",
+                           iface->name, ntohl(dhcp->xid),
+                           hwaddr_ntoa(dhcp->chaddr, sizeof(dhcp->chaddr)));
+                       continue;
+               }
+               dhcp_handle(iface, &dhcp, &from);
+               if (iface->raw_fd == -1)
+                       break;
+       }
+       free(packet);
+       packet = NULL;
+       free(dhcp);
+}
+
+static int
+dhcp_open(struct interface *ifp)
+{
+       int r = 0;
+
+       if (ifp->raw_fd == -1) {
+               if ((r = open_socket(ifp, ETHERTYPE_IP)) == -1)
+                       syslog(LOG_ERR, "%s: %s: %m", __func__, ifp->name);
+               else
+                       eloop_event_add(ifp->raw_fd, dhcp_handlepacket, ifp);
+       }
+       if (ifp->udp_fd == -1 &&
+           ifp->addr.s_addr != 0 &&
+           ifp->state->new != NULL &&
+           (ifp->state->new->cookie == htonl(MAGIC_COOKIE) ||
+           ifp->state->options->options & DHCPCD_INFORM))
+       {
+               if (open_udp_socket(ifp) == -1 && errno != EADDRINUSE) {
+                       syslog(LOG_ERR, "%s: open_udp_socket: %m", ifp->name);
+                       r = -1;
+               }
+       }
+       return r;
+}
+
+void
+dhcp_start(struct interface *ifp)
+{
+       struct if_options *ifo = ifp->state->options;
+       struct stat st;
+       struct timeval now;
+       uint32_t l;
+       int nolease;
+
+       if (!(ifo->options & DHCPCD_IPV4))
+               return;
+
+       if (ifo->options & DHCPCD_STATIC) {
+               dhcp_static(ifp);
+               return;
+       }
+
+       if (!dhcp_open(ifp))
+               return;
+
+       if (ifo->options & DHCPCD_INFORM) {
+               dhcp_inform(ifp);
+               return;
+       }
+       if (ifp->hwlen == 0 && ifo->clientid[0] == '\0') {
+               syslog(LOG_WARNING, "%s: needs a clientid to configure",
+                   ifp->name);
+               dhcp_drop(ifp, "FAIL");
+               dhcp_close(ifp);
+               eloop_timeout_delete(NULL, ifp);
+               return;
+       }
+       /* We don't want to read the old lease if we NAK an old test */
+       nolease = ifp->state->offer && options & DHCPCD_TEST;
+       if (!nolease)
+               ifp->state->offer = read_lease(ifp);
+       if (ifp->state->offer) {
+               get_lease(&ifp->state->lease, ifp->state->offer);
+               ifp->state->lease.frominfo = 1;
+               if (ifp->state->offer->cookie == 0) {
+                       if (ifp->state->offer->yiaddr ==
+                           ifp->addr.s_addr)
+                       {
+                               free(ifp->state->offer);
+                               ifp->state->offer = NULL;
+                       }
+               } else if (ifp->state->lease.leasetime != ~0U &&
+                   stat(ifp->leasefile, &st) == 0)
+               {
+                       /* Offset lease times and check expiry */
+                       gettimeofday(&now, NULL);
+                       if ((time_t)ifp->state->lease.leasetime <
+                           now.tv_sec - st.st_mtime)
+                       {
+                               syslog(LOG_DEBUG,
+                                   "%s: discarding expired lease",
+                                   ifp->name);
+                               free(ifp->state->offer);
+                               ifp->state->offer = NULL;
+                               ifp->state->lease.addr.s_addr = 0;
+                       } else {
+                               l = now.tv_sec - st.st_mtime;
+                               ifp->state->lease.leasetime -= l;
+                               ifp->state->lease.renewaltime -= l;
+                               ifp->state->lease.rebindtime -= l;
+                       }
+               }
+       }
+       if (ifp->state->offer == NULL)
+               dhcp_discover(ifp);
+       else if (ifp->state->offer->cookie == 0 &&
+           ifp->state->options->options & DHCPCD_IPV4LL)
+               ipv4ll_start(ifp);
+       else
+               dhcp_reboot(ifp);
+}
+
diff --git a/dhcp.h b/dhcp.h
index c0ddaa92323667fbabbce31f244a85b2180d84f5..f83722bb2c768869bce8c1f6ceac79a6b5e83898 100644 (file)
--- a/dhcp.h
+++ b/dhcp.h
@@ -1,6 +1,6 @@
 /* 
  * dhcpcd - DHCP client daemon
- * Copyright (c) 2006-2012 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2013 Roy Marples <roy@marples.name>
  * All rights reserved
 
  * Redistribution and use in source and binary forms, with or without
 #include <stdint.h>
 
 #include "common.h"
-
-/* Max MTU - defines dhcp option length */
-#define MTU_MAX             1500
-#define MTU_MIN             576
+#include "dhcp-common.h"
 
 /* UDP port numbers for DHCP */
 #define DHCP_SERVER_PORT    67
@@ -114,26 +111,6 @@ enum DHO {
        DHO_END                    = 255
 };
 
-#define REQUEST        (1 << 0)
-#define UINT8  (1 << 1)
-#define UINT16 (1 << 2)
-#define SINT16 (1 << 3)
-#define UINT32 (1 << 4)
-#define SINT32 (1 << 5)
-#define IPV4   (1 << 6)
-#define STRING (1 << 7)
-#define PAIR   (1 << 8)
-#define ARRAY  (1 << 9)
-#define RFC3361        (1 << 10)
-#define RFC3397        (1 << 11)
-#define RFC3442 (1 << 12)
-#define RFC5969 (1 << 13)
-#define IPV6   (1 << 14)
-#define BINHEX (1 << 15)
-#define SCODE  (1 << 16)
-
-#define IPV4R  IPV4 | REQUEST
-
 /* FQDN values - lsnybble used in flags
  * hsnybble to create order
  * and to allow 0x00 to mean disable
@@ -193,18 +170,12 @@ struct dhcp_lease {
 #include "if-options.h"
 #include "net.h"
 
-struct dhcp_opt {
-       uint16_t option;
-       int type;
-       const char *var;
-};
-
 extern const struct dhcp_opt const dhcp_opts[];
 
-#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(const struct dhcp_opt *,uint8_t *, const char *, int);
+char *decode_rfc3361(int dl, const uint8_t *data);
+ssize_t decode_rfc3442(char *out, ssize_t len, int pl, const uint8_t *p);
+ssize_t decode_rfc5969(char *out, ssize_t len, int pl, const uint8_t *p);
+
 void print_options(void);
 char *get_option_string(const struct dhcp_message *, uint8_t);
 int get_option_addr(struct in_addr *, const struct dhcp_message *, uint8_t);
@@ -215,12 +186,12 @@ int get_option_uint8(uint8_t *, const struct dhcp_message *, uint8_t);
            !IN_LINKLOCAL(htonl((m)->yiaddr)) &&                        \
            get_option_uint8(NULL, m, DHO_MESSAGETYPE) == -1)
 struct rt *get_option_routes(struct interface *, const struct dhcp_message *);
-ssize_t decode_rfc3397(char *, ssize_t, int, const uint8_t *);
-ssize_t print_string(char *, ssize_t, int, const uint8_t *);
-ssize_t print_option(char *, ssize_t, int, int, const uint8_t *, const char *);
 ssize_t configure_env(char **, const char *, const struct dhcp_message *,
     const struct interface *);
 
+uint32_t dhcp_xid(const struct interface *);
+struct dhcp_message *dhcp_message_new(struct in_addr *addr,
+    struct in_addr *mask);
 int dhcp_message_add_addr(struct dhcp_message *, uint8_t, struct in_addr);
 ssize_t make_message(struct dhcp_message **, const struct interface *,
     uint8_t);
@@ -230,4 +201,14 @@ 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 *);
 
+void dhcp_drop(struct interface *, const char *);
+void dhcp_start(struct interface *);
+void dhcp_stop(struct interface *);
+void dhcp_decline(struct interface *);
+void dhcp_discover(void *);
+void dhcp_inform(struct interface *);
+void dhcp_release(struct interface *);
+void dhcp_bind(void *);
+void dhcp_close(struct interface *);
+
 #endif
diff --git a/dhcp6.c b/dhcp6.c
index 100cfe49f7acce3d9d62208261353955c02c8ada..26760550ea95041558af77e410889e818ebbaa6e 100644 (file)
--- a/dhcp6.c
+++ b/dhcp6.c
@@ -1,6 +1,6 @@
 /* 
  * dhcpcd - DHCP client daemon
- * Copyright (c) 2006-2012 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2013 Roy Marples <roy@marples.name>
  * All rights reserved
 
  * Redistribution and use in source and binary forms, with or without
 
 #define ELOOP_QUEUE 2
 
-#include "bind.h"
 #include "config.h"
 #include "common.h"
-#include "configure.h"
 #include "dhcp.h"
 #include "dhcp6.h"
 #include "duid.h"
 #include "eloop.h"
 #include "ipv6rs.h"
 #include "platform.h"
+#include "script.h"
 
 #ifndef __UNCONST
 #define __UNCONST(a) ((void *)(unsigned long)(const void *)(a))
@@ -95,26 +94,27 @@ static const struct dhcp6_op dhcp6_ops[] = {
        { 0, NULL }
 };
 
+#define IPV6A  ADDRIPV6 | ARRAY
 const struct dhcp_opt const dhcp6_opts[] = {
        { D6_OPTION_CLIENTID,           BINHEX,         "client_id" },
        { D6_OPTION_SERVERID,           BINHEX,         "server_id" },
-       { D6_OPTION_IA_ADDR,            IPV6 | ARRAY,   "ia_addr" },
+       { D6_OPTION_IA_ADDR,            IPV6A,          "ia_addr" },
        { D6_OPTION_PREFERENCE,         UINT8,          "preference" },
        { D6_OPTION_RAPID_COMMIT,       0,              "rapid_commit" },
-       { D6_OPTION_UNICAST,            IPV6,           "unicast" },
+       { D6_OPTION_UNICAST,            ADDRIPV6,       "unicast" },
        { D6_OPTION_STATUS_CODE,        SCODE,          "status_code" },
        { D6_OPTION_SIP_SERVERS_NAME,   RFC3397,        "sip_servers_names" },
-       { D6_OPTION_SIP_SERVERS_ADDRESS,IPV6 | ARRAY,   "sip_servers_addresses" },
-       { D6_OPTION_DNS_SERVERS,        IPV6 | ARRAY,   "name_servers" },
+       { D6_OPTION_SIP_SERVERS_ADDRESS,IPV6A,  "sip_servers_addresses" },
+       { D6_OPTION_DNS_SERVERS,        IPV6A,          "name_servers" },
        { D6_OPTION_DOMAIN_LIST,        RFC3397,        "domain_search" },
-       { D6_OPTION_NIS_SERVERS,        IPV6 | ARRAY,   "nis_servers" },
-       { D6_OPTION_NISP_SERVERS,       IPV6 | ARRAY,   "nisp_servers" },
+       { D6_OPTION_NIS_SERVERS,        IPV6A,          "nis_servers" },
+       { D6_OPTION_NISP_SERVERS,       IPV6A,          "nisp_servers" },
        { D6_OPTION_NIS_DOMAIN_NAME,    RFC3397,        "nis_domain_name" },
        { D6_OPTION_NISP_DOMAIN_NAME,   RFC3397,        "nisp_domain_name" },
-       { D6_OPTION_SNTP_SERVERS,       IPV6 | ARRAY,   "sntp_servers" },
+       { D6_OPTION_SNTP_SERVERS,       IPV6A,          "sntp_servers" },
        { D6_OPTION_INFO_REFRESH_TIME,  UINT32,         "info_refresh_time" },
        { D6_OPTION_BCMS_SERVER_D,      RFC3397,        "bcms_server_d" },
-       { D6_OPTION_BCMS_SERVER_A,      IPV6 | ARRAY,   "bcms_server_a" },
+       { D6_OPTION_BCMS_SERVER_A,      IPV6A,          "bcms_server_a" },
        { 0, 0, NULL }
 };
 
@@ -875,7 +875,7 @@ dhcp6_startexpire(void *arg)
 
        syslog(LOG_ERR, "%s: DHCPv6 lease expired", ifp->name);
        dhcp6_freedrop_addrs(ifp, 1);
-       run_script_reason(ifp, "EXPIRE6");
+       script_runreason(ifp, "EXPIRE6");
        state = D6_CSTATE(ifp);
        unlink(state->leasefile);
        dhcp6_startdiscover(ifp);
@@ -1390,7 +1390,7 @@ recv:
                dhcp6_writelease(ifp);
        }
 
-       run_script_reason(ifp, options & DHCPCD_TEST ? "TEST" : reason);
+       script_runreason(ifp, options & DHCPCD_TEST ? "TEST" : reason);
        if (options & DHCPCD_TEST ||
            (ifp->state->options->options & DHCPCD_INFORM &&
            !(options & DHCPCD_MASTER)))
@@ -1527,7 +1527,7 @@ dhcp6_freedrop(struct interface *ifp, int drop, const char *reason)
                if (drop && state->new) {
                        if (reason == NULL)
                                reason = "STOP6";
-                       run_script_reason(ifp, reason);
+                       script_runreason(ifp, reason);
                }
                free(state->send);
                free(state->recv);
index 27665e3a36ac229a37439fa315040e08d5c88e8a..ebbb300722e54a50672c635dbca7ff2c28c78da9 100644 (file)
--- a/dhcpcd.c
+++ b/dhcpcd.c
@@ -1,6 +1,6 @@
 /* 
  * dhcpcd - DHCP client daemon
- * Copyright (c) 2006-2012 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2013 Roy Marples <roy@marples.name>
  * All rights reserved
 
  * Redistribution and use in source and binary forms, with or without
@@ -25,7 +25,7 @@
  * SUCH DAMAGE.
  */
 
-const char copyright[] = "Copyright (c) 2006-2012 Roy Marples";
+const char copyright[] = "Copyright (c) 2006-2013 Roy Marples";
 
 #include <sys/file.h>
 #include <sys/socket.h>
@@ -35,14 +35,6 @@ const char copyright[] = "Copyright (c) 2006-2012 Roy Marples";
 #include <sys/uio.h>
 #include <sys/utsname.h>
 
-#include <arpa/inet.h>
-#include <net/route.h>
-
-#ifdef __linux__
-#  include <asm/types.h> /* for systems with broken headers */
-#  include <linux/rtnetlink.h>
-#endif
-
 #include <ctype.h>
 #include <errno.h>
 #include <getopt.h>
@@ -57,10 +49,8 @@ const char copyright[] = "Copyright (c) 2006-2012 Roy Marples";
 #include <time.h>
 
 #include "arp.h"
-#include "bind.h"
 #include "config.h"
 #include "common.h"
-#include "configure.h"
 #include "control.h"
 #include "dhcpcd.h"
 #include "dhcp6.h"
@@ -68,12 +58,14 @@ const char copyright[] = "Copyright (c) 2006-2012 Roy Marples";
 #include "eloop.h"
 #include "if-options.h"
 #include "if-pref.h"
+#include "ipv4.h"
 #include "ipv4ll.h"
 #include "ipv6.h"
 #include "ipv6ns.h"
 #include "ipv6rs.h"
 #include "net.h"
 #include "platform.h"
+#include "script.h"
 #include "signals.h"
 
 /* We should define a maximum for the NAK exponential backoff */ 
@@ -98,42 +90,11 @@ static char *cffile;
 static struct if_options *if_options;
 static char *pidfile;
 static int linkfd = -1, ipv6rsfd = -1, ipv6nsfd = -1;
-static uint8_t *packet;
 static char **ifv;
 static int ifc;
 static char **margv;
 static int margc;
 
-struct dhcp_op {
-       uint8_t value;
-       const char *name;
-};
-
-static const struct dhcp_op dhcp_ops[] = {
-       { DHCP_DISCOVER, "DISCOVER" },
-       { DHCP_OFFER,    "OFFER" },
-       { DHCP_REQUEST,  "REQUEST" },
-       { DHCP_DECLINE,  "DECLINE" },
-       { DHCP_ACK,      "ACK" },
-       { DHCP_NAK,      "NAK" },
-       { DHCP_RELEASE,  "RELEASE" },
-       { DHCP_INFORM,   "INFORM" },
-       { 0, NULL }
-};
-
-static void send_release(struct interface *);
-
-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;
-}
-
 static pid_t
 read_pid(void)
 {
@@ -216,7 +177,7 @@ cleanup(void)
 }
 
 /* ARGSUSED */
-void
+static void
 handle_exit_timeout(_unused void *arg)
 {
        int timeout;
@@ -235,17 +196,64 @@ handle_exit_timeout(_unused void *arg)
        eloop_timeout_add_sec(timeout, handle_exit_timeout, NULL);
 }
 
-void
-drop_dhcp(struct interface *iface, const char *reason)
+pid_t
+daemonise(void)
 {
-       free(iface->state->old);
-       iface->state->old = iface->state->new;
-       iface->state->new = NULL;
-       iface->state->reason = reason;
-       configure(iface);
-       free(iface->state->old);
-       iface->state->old = NULL;
-       iface->state->lease.addr.s_addr = 0;
+#ifdef THERE_IS_NO_FORK
+       return -1;
+#else
+       pid_t pid;
+       char buf = '\0';
+       int sidpipe[2], fd;
+
+       eloop_timeout_delete(handle_exit_timeout, NULL);
+       if (options & DHCPCD_DAEMONISED || !(options & DHCPCD_DAEMONISE))
+               return 0;
+       /* Setup a signal pipe so parent knows when to exit. */
+       if (pipe(sidpipe) == -1) {
+               syslog(LOG_ERR, "pipe: %m");
+               return -1;
+       }
+       syslog(LOG_DEBUG, "forking to background");
+       switch (pid = fork()) {
+       case -1:
+               syslog(LOG_ERR, "fork: %m");
+               exit(EXIT_FAILURE);
+               /* NOTREACHED */
+       case 0:
+               setsid();
+               /* Notify parent it's safe to exit as we've detached. */
+               close(sidpipe[0]);
+               if (write(sidpipe[1], &buf, 1) == -1)
+                       syslog(LOG_ERR, "failed to notify parent: %m");
+               close(sidpipe[1]);
+               if ((fd = open(_PATH_DEVNULL, O_RDWR, 0)) != -1) {
+                       dup2(fd, STDIN_FILENO);
+                       dup2(fd, STDOUT_FILENO);
+                       dup2(fd, STDERR_FILENO);
+                       close(fd);
+               }
+               break;
+       default:
+               /* Wait for child to detach */
+               close(sidpipe[1]);
+               if (read(sidpipe[0], &buf, 1) == -1)
+                       syslog(LOG_ERR, "failed to read child: %m");
+               close(sidpipe[0]);
+               break;
+       }
+       /* Done with the fd now */
+       if (pid != 0) {
+               syslog(LOG_INFO, "forked to background, child pid %d",pid);
+               writepid(pidfd, pid);
+               close(pidfd);
+               pidfd = -1;
+               options |= DHCPCD_FORKED;
+               exit(EXIT_SUCCESS);
+       }
+       options |= DHCPCD_DAEMONISED;
+       return pid;
+#endif
 }
 
 struct interface *
@@ -279,509 +287,15 @@ stop_interface(struct interface *iface)
 
        dhcp6_drop(iface, NULL);
        ipv6rs_drop(iface);
-       if (strcmp(iface->state->reason, "RELEASE") != 0)
-               drop_dhcp(iface, "STOP");
-       close_sockets(iface);
+//     if (strcmp(iface->state->reason, "RELEASE") != 0)
+               dhcp_drop(iface, "STOP");
+       dhcp_close(iface);
        eloop_timeout_delete(NULL, iface);
        free_interface(ifp);
        if (!(options & (DHCPCD_MASTER | DHCPCD_TEST)))
                exit(EXIT_FAILURE);
 }
 
-static uint32_t
-dhcp_xid(struct interface *iface)
-{
-       uint32_t xid;
-
-       if (iface->state->options->options & DHCPCD_XID_HWADDR &&
-           iface->hwlen >= sizeof(xid)) 
-               /* The lower bits are probably more unique on the network */
-               memcpy(&xid, (iface->hwaddr + iface->hwlen) - sizeof(xid),
-                   sizeof(xid));
-       else
-               xid = arc4random();
-
-       return xid;
-}
-
-static void
-send_message(struct interface *iface, int type,
-    void (*callback)(void *))
-{
-       struct if_state *state = iface->state;
-       struct if_options *ifo = state->options;
-       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 (!callback)
-               syslog(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);
-               timernorm(&tv);
-               syslog(LOG_DEBUG,
-                   "%s: sending %s (xid 0x%x), next in %0.2f seconds",
-                   iface->name, get_dhcp_op(type), state->xid,
-                   timeval_to_double(&tv));
-       }
-
-       /* Ensure sockets are open. */
-       if (open_sockets(iface) == -1) {
-               if (!(options & DHCPCD_TEST))
-                       drop_dhcp(iface, "FAIL");
-               return;
-       }
-
-       /* 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.
-        * Also, we should not unicast from a BOOTP lease. */
-       if (iface->udp_fd == -1 ||
-           (!(ifo->options & DHCPCD_INFORM) && is_bootp(iface->state->new)))
-       {
-               a = iface->addr.s_addr;
-               iface->addr.s_addr = 0;
-       }
-       len = make_message(&dhcp, iface, type);
-       if (a)
-               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) {
-                       syslog(LOG_ERR, "%s: send_packet: %m", iface->name);
-                       close_sockets(iface);
-               }
-       } else {
-               len = make_udp_packet(&udp, (uint8_t *)dhcp, len, from, to);
-               r = send_raw_packet(iface, ETHERTYPE_IP, udp, len);
-               free(udp);
-               /* If we failed to send a raw packet this normally means
-                * we don't have the ability to work beneath the IP layer
-                * for this interface.
-                * As such we remove it from consideration without actually
-                * stopping the interface. */
-               if (r == -1) {
-                       syslog(LOG_ERR, "%s: send_raw_packet: %m", iface->name);
-                       if (!(options & DHCPCD_TEST))
-                               drop_dhcp(iface, "FAIL");
-                       close_sockets(iface);
-                       eloop_timeout_delete(NULL, iface);
-                       callback = NULL;
-               }
-       }
-       free(dhcp);
-
-       /* Even if we fail to send a packet we should continue as we are
-        * as our failure timeouts will change out codepath when needed. */
-       if (callback)
-               eloop_timeout_add_tv(&tv, callback, iface);
-}
-
-static void
-send_inform(void *arg)
-{
-       send_message((struct interface *)arg, DHCP_INFORM, send_inform);
-}
-
-static void
-send_discover(void *arg)
-{
-       send_message((struct interface *)arg, DHCP_DISCOVER, send_discover);
-}
-
-static void
-send_request(void *arg)
-{
-       send_message((struct interface *)arg, DHCP_REQUEST, send_request);
-}
-
-static void
-send_renew(void *arg)
-{
-       send_message((struct interface *)arg, DHCP_REQUEST, send_renew);
-}
-
-static void
-send_rebind(void *arg)
-{
-       send_message((struct interface *)arg, DHCP_REQUEST, send_rebind);
-}
-
-void
-start_expire(void *arg)
-{
-       struct interface *iface = arg;
-
-       iface->state->interval = 0;
-       if (iface->addr.s_addr == 0) {
-               /* We failed to reboot, so enter discovery. */
-               iface->state->lease.addr.s_addr = 0;
-               start_discover(iface);
-               return;
-       }
-
-       syslog(LOG_ERR, "%s: lease expired", iface->name);
-       eloop_timeout_delete(NULL, iface);
-       drop_dhcp(iface, "EXPIRE");
-       unlink(iface->leasefile);
-       if (iface->carrier != LINK_DOWN)
-               start_interface(iface);
-}
-
-static void
-log_dhcp(int lvl, const char *msg,
-    const struct interface *iface, const struct dhcp_message *dhcp,
-    const struct in_addr *from)
-{
-       const char *tfrom;
-       char *a;
-       struct in_addr addr;
-       int r;
-
-       if (strcmp(msg, "NAK:") == 0)
-               a = get_option_string(dhcp, DHO_MESSAGE);
-       else if (dhcp->yiaddr != 0) {
-               addr.s_addr = dhcp->yiaddr;
-               a = xstrdup(inet_ntoa(addr));
-       } else
-               a = NULL;
-
-       tfrom = "from";
-       r = get_option_addr(&addr, dhcp, DHO_SERVERID);
-       if (dhcp->servername[0] && r == 0)
-               syslog(lvl, "%s: %s %s %s %s `%s'", iface->name, msg, a,
-                   tfrom, inet_ntoa(addr), dhcp->servername);
-       else {
-               if (r != 0) {
-                       tfrom = "via";
-                       addr = *from;
-               }
-               if (a == NULL)
-                       syslog(lvl, "%s: %s %s %s",
-                           iface->name, msg, tfrom, inet_ntoa(addr));
-               else
-                       syslog(lvl, "%s: %s %s %s %s",
-                           iface->name, msg, a, tfrom, inet_ntoa(addr));
-       }
-       free(a);
-}
-
-static int
-blacklisted_ip(const struct if_options *ifo, in_addr_t addr)
-{
-       size_t i;
-       
-       for (i = 0; i < ifo->blacklist_len; i += 2)
-               if (ifo->blacklist[i] == (addr & ifo->blacklist[i + 1]))
-                       return 1;
-       return 0;
-}
-
-static int
-whitelisted_ip(const struct if_options *ifo, in_addr_t addr)
-{
-       size_t i;
-
-       if (ifo->whitelist_len == 0)
-               return -1;
-       for (i = 0; i < ifo->whitelist_len; i += 2)
-               if (ifo->whitelist[i] == (addr & ifo->whitelist[i + 1]))
-                       return 1;
-       return 0;
-}
-
-static void
-handle_dhcp(struct interface *iface, struct dhcp_message **dhcpp, const struct in_addr *from)
-{
-       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;
-
-       /* reset the message counter */
-       state->interval = 0;
-
-       /* We may have found a BOOTP server */
-       if (get_option_uint8(&type, dhcp, DHO_MESSAGETYPE) == -1) 
-               type = 0;
-
-       if (type == DHCP_NAK) {
-               /* For NAK, only check if we require the ServerID */
-               if (has_option_mask(ifo->requiremask, DHO_SERVERID) &&
-                   get_option_addr(&addr, dhcp, DHO_SERVERID) == -1)
-               {
-                       log_dhcp(LOG_WARNING, "reject NAK", iface, dhcp, from);
-                       return;
-               }
-               /* We should restart on a NAK */
-               log_dhcp(LOG_WARNING, "NAK:", iface, dhcp, from);
-               if (!(options & DHCPCD_TEST)) {
-                       drop_dhcp(iface, "NAK");
-                       unlink(iface->leasefile);
-               }
-               close_sockets(iface);
-               /* If we constantly get NAKS then we should slowly back off */
-               eloop_timeout_add_sec(state->nakoff, start_interface, iface);
-               if (state->nakoff == 0)
-                       state->nakoff = 1;
-               else {
-                       state->nakoff *= 2;
-                       if (state->nakoff > NAKOFF_MAX)
-                               state->nakoff = NAKOFF_MAX;
-               }
-               return;
-       }
-
-       /* 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)
-               {
-                       /* If we are bootp, then ignore the need for serverid.
-                        * To ignore bootp, require dhcp_message_type instead. */
-                       if (type == 0 && i == DHO_SERVERID)
-                               continue;
-                       log_dhcp(LOG_WARNING, "reject DHCP", iface, dhcp, from);
-                       return;
-               }
-       }
-
-       /* Ensure that the address offered is valid */
-       if ((type == 0 || type == DHCP_OFFER || type == DHCP_ACK) &&
-           (dhcp->ciaddr == INADDR_ANY || dhcp->ciaddr == INADDR_BROADCAST) &&
-           (dhcp->yiaddr == INADDR_ANY || dhcp->yiaddr == INADDR_BROADCAST))
-       {
-               log_dhcp(LOG_WARNING, "reject invalid address",
-                   iface, dhcp, from);
-               return;
-       }
-
-       /* No NAK, so reset the backoff */
-       state->nakoff = 0;
-
-       if ((type == 0 || type == DHCP_OFFER) &&
-           state->state == DHS_DISCOVER)
-       {
-               lease->frominfo = 0;
-               lease->addr.s_addr = dhcp->yiaddr;
-               lease->cookie = dhcp->cookie;
-               if (type == 0 ||
-                   get_option_addr(&lease->server, dhcp, DHO_SERVERID) != 0)
-                       lease->server.s_addr = INADDR_ANY;
-               log_dhcp(LOG_INFO, "offered", iface, dhcp, from);
-               free(state->offer);
-               state->offer = dhcp;
-               *dhcpp = NULL;
-               if (options & DHCPCD_TEST) {
-                       free(state->old);
-                       state->old = state->new;
-                       state->new = state->offer;
-                       state->offer = NULL;
-                       state->reason = "TEST";
-                       run_script(iface);
-                       exit(EXIT_SUCCESS);
-               }
-               eloop_timeout_delete(send_discover, iface);
-               /* We don't request BOOTP addresses */
-               if (type) {
-                       /* We used to ARP check here, but that seems to be in
-                        * violation of RFC2131 where it only describes
-                        * DECLINE after REQUEST.
-                        * It also seems that some MS DHCP servers actually
-                        * ignore DECLINE if no REQUEST, ie we decline a
-                        * DISCOVER. */
-                       start_request(iface);
-                       return;
-               }
-       }
-
-       if (type) {
-               if (type == DHCP_OFFER) {
-                       log_dhcp(LOG_INFO, "ignoring offer of",
-                           iface, dhcp, from);
-                       return;
-               }
-
-               /* We should only be dealing with acks */
-               if (type != DHCP_ACK) {
-                       log_dhcp(LOG_ERR, "not ACK or OFFER",
-                           iface, dhcp, from);
-                       return;
-               }
-
-               if (!(ifo->options & DHCPCD_INFORM))
-                       log_dhcp(LOG_INFO, "acknowledged", iface, dhcp, from);
-       }
-
-       /* BOOTP could have already assigned this above, so check we still
-        * have a pointer. */
-       if (*dhcpp) {
-               free(state->offer);
-               state->offer = dhcp;
-               *dhcpp = NULL;
-       }
-
-       lease->frominfo = 0;
-       eloop_timeout_delete(NULL, iface);
-
-       /* We now have an offer, so close the DHCP sockets.
-        * This allows us to safely ARP when broken DHCP servers send an ACK
-        * follows by an invalid NAK. */
-       close_sockets(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) != 1) {
-                       state->claims = 0;
-                       state->probes = 0;
-                       state->conflicts = 0;
-                       state->state = DHS_PROBE;
-                       send_arp_probe(iface);
-                       return;
-               }
-       }
-
-       bind_interface(iface);
-}
-
-static void
-handle_dhcp_packet(void *arg)
-{
-       struct interface *iface = arg;
-       struct dhcp_message *dhcp = NULL;
-       const uint8_t *pp;
-       ssize_t bytes;
-       struct in_addr from;
-       int i, partialcsum = 0;
-
-       /* We loop through until our buffer is empty.
-        * The benefit is that if we get >1 DHCP packet in our buffer and
-        * the first one fails for any reason, we can use the next. */
-       if (packet == NULL)
-               packet = xmalloc(udp_dhcp_len);
-       for(;;) {
-               bytes = get_raw_packet(iface, ETHERTYPE_IP,
-                   packet, udp_dhcp_len, &partialcsum);
-               if (bytes == 0 || bytes == -1)
-                       break;
-               if (valid_udp_packet(packet, bytes, &from, partialcsum) == -1) {
-                       syslog(LOG_ERR, "%s: invalid UDP packet from %s",
-                           iface->name, inet_ntoa(from));
-                       continue;
-               }
-               i = whitelisted_ip(iface->state->options, from.s_addr);
-               if (i == 0) {
-                       syslog(LOG_WARNING,
-                           "%s: non whitelisted DHCP packet from %s",
-                           iface->name, inet_ntoa(from));
-                       continue;
-               } else if (i != 1 &&
-                   blacklisted_ip(iface->state->options, from.s_addr) == 1)
-               {
-                       syslog(LOG_WARNING,
-                           "%s: blacklisted DHCP packet from %s",
-                           iface->name, inet_ntoa(from));
-                       continue;
-               }
-               if (iface->flags & IFF_POINTOPOINT &&
-                   iface->dst.s_addr != from.s_addr)
-               {
-                       syslog(LOG_WARNING,
-                           "%s: server %s is not destination",
-                           iface->name, inet_ntoa(from));
-               }
-               bytes = get_udp_data(&pp, packet);
-               if ((size_t)bytes > sizeof(*dhcp)) {
-                       syslog(LOG_ERR,
-                           "%s: packet greater than DHCP size from %s",
-                           iface->name, inet_ntoa(from));
-                       continue;
-               }
-               if (!dhcp)
-                       dhcp = xzalloc(sizeof(*dhcp));
-               memcpy(dhcp, pp, bytes);
-               if (dhcp->cookie != htonl(MAGIC_COOKIE)) {
-                       syslog(LOG_DEBUG, "%s: bogus cookie from %s",
-                           iface->name, inet_ntoa(from));
-                       continue;
-               }
-               /* Ensure it's the right transaction */
-               if (iface->state->xid != ntohl(dhcp->xid)) {
-                       syslog(LOG_DEBUG,
-                           "%s: wrong xid 0x%x (expecting 0x%x) from %s",
-                           iface->name, ntohl(dhcp->xid), iface->state->xid,
-                           inet_ntoa(from));
-                       continue;
-               }
-               /* Ensure packet is for us */
-               if (iface->hwlen <= sizeof(dhcp->chaddr) &&
-                   memcmp(dhcp->chaddr, iface->hwaddr, iface->hwlen))
-               {
-                       syslog(LOG_DEBUG, "%s: xid 0x%x is not for hwaddr %s",
-                           iface->name, ntohl(dhcp->xid),
-                           hwaddr_ntoa(dhcp->chaddr, sizeof(dhcp->chaddr)));
-                       continue;
-               }
-               handle_dhcp(iface, &dhcp, &from);
-               if (iface->raw_fd == -1)
-                       break;
-       }
-       free(packet);
-       packet = NULL;
-       free(dhcp);
-}
-
-static void
-send_release(struct interface *iface)
-{
-       struct timespec ts;
-
-       if (iface->state->new != NULL &&
-           iface->state->new->cookie == htonl(MAGIC_COOKIE))
-       {
-               syslog(LOG_INFO, "%s: releasing lease of %s",
-                   iface->name, inet_ntoa(iface->state->lease.addr));
-               iface->state->xid = dhcp_xid(iface);
-               send_message(iface, DHCP_RELEASE, NULL);
-               /* Give the packet a chance to go before dropping the ip */
-               ts.tv_sec = RELEASE_DELAY_S;
-               ts.tv_nsec = RELEASE_DELAY_NS;
-               nanosleep(&ts, NULL);
-               drop_dhcp(iface, "RELEASE");
-       }
-       unlink(iface->leasefile);
-}
-
-void
-send_decline(struct interface *iface)
-{
-       send_message(iface, DHCP_DECLINE, NULL);
-}
-
 static void
 configure_interface1(struct interface *iface)
 {
@@ -905,16 +419,6 @@ exit:
        return ret;
 }
 
-static void
-start_fallback(void *arg)
-{
-       struct interface *iface;
-
-       iface = (struct interface *)arg;
-       select_profile(iface, iface->state->options->fallback);
-       start_interface(iface);
-}
-
 static void
 configure_interface(struct interface *iface, int argc, char **argv)
 {
@@ -954,11 +458,10 @@ handle_carrier(int action, int flags, const char *ifname)
                if (iface->carrier != LINK_DOWN) {
                        iface->carrier = LINK_DOWN;
                        syslog(LOG_INFO, "%s: carrier lost", iface->name);
-                       close_sockets(iface);
-                       eloop_timeouts_delete(iface, start_expire, NULL);
+                       dhcp_close(iface);
                        dhcp6_drop(iface, "EXPIRE6");
                        ipv6rs_drop(iface);
-                       drop_dhcp(iface, "NOCARRIER");
+                       dhcp_drop(iface, "NOCARRIER");
                }
        } else if (carrier == 1 && !(~iface->flags & IFF_UP)) {
                if (iface->carrier != LINK_UP) {
@@ -969,235 +472,17 @@ handle_carrier(int action, int flags, const char *ifname)
                        configure_interface(iface, margc, margv);
                        iface->state->interval = 0;
                        iface->state->reason = "CARRIER";
-                       run_script(iface);
+                       script_run(iface);
                        start_interface(iface);
                }
        }
 }
 
-void
-start_discover(void *arg)
-{
-       struct interface *iface = arg;
-       struct if_options *ifo = iface->state->options;
-       int timeout = ifo->timeout;
-
-       /* If we're rebooting and we're not daemonised then we need
-        * to shorten the normal timeout to ensure we try correctly
-        * for a fallback or IPv4LL address. */
-       if (iface->state->state == DHS_REBOOT &&
-           !(options & DHCPCD_DAEMONISED))
-       {
-               timeout -= ifo->reboot;
-               if (timeout <= 0)
-                       timeout = 2;
-       }
-
-       iface->state->state = DHS_DISCOVER;
-       iface->state->xid = dhcp_xid(iface);
-       eloop_timeout_delete(NULL, iface);
-       if (ifo->fallback)
-               eloop_timeout_add_sec(timeout, start_fallback, iface);
-       else if (ifo->options & DHCPCD_IPV4LL &&
-           !IN_LINKLOCAL(htonl(iface->addr.s_addr)))
-       {
-               if (IN_LINKLOCAL(htonl(iface->state->fail.s_addr)))
-                       eloop_timeout_add_sec(RATE_LIMIT_INTERVAL,
-                           ipv4ll_start, iface);
-               else
-                       eloop_timeout_add_sec(timeout, ipv4ll_start, iface);
-       }
-       if (ifo->options & DHCPCD_REQUEST)
-               syslog(LOG_INFO, "%s: broadcasting for a lease (requesting %s)",
-                   iface->name, inet_ntoa(ifo->req_addr));
-       else
-               syslog(LOG_INFO, "%s: broadcasting for a lease", iface->name);
-       send_discover(iface);
-}
-
-void
-start_request(void *arg)
-{
-       struct interface *iface = arg;
-
-       iface->state->state = DHS_REQUEST;
-       send_request(iface);
-}
-
-void
-start_renew(void *arg)
-{
-       struct interface *iface = arg;
-       struct dhcp_lease *lease = &iface->state->lease;
-
-       syslog(LOG_INFO, "%s: renewing lease of %s",
-           iface->name, inet_ntoa(lease->addr));
-       syslog(LOG_DEBUG, "%s: rebind in %u seconds, expire in %u seconds",
-           iface->name, lease->rebindtime - lease->renewaltime,
-           lease->leasetime - lease->renewaltime);
-       iface->state->state = DHS_RENEW;
-       iface->state->xid = dhcp_xid(iface);
-       send_renew(iface);
-}
-
-void
-start_rebind(void *arg)
-{
-       struct interface *iface = arg;
-       struct dhcp_lease *lease = &iface->state->lease;
-
-       syslog(LOG_ERR, "%s: failed to renew, attempting to rebind",
-           iface->name);
-       syslog(LOG_DEBUG, "%s: expire in %u seconds",
-           iface->name, lease->leasetime - lease->rebindtime);
-       iface->state->state = DHS_REBIND;
-       eloop_timeout_delete(send_renew, iface);
-       iface->state->lease.server.s_addr = 0;
-       send_rebind(iface);
-}
-
-static void
-start_timeout(void *arg)
-{
-       struct interface *iface = arg;
-
-       bind_interface(iface);
-       iface->state->interval = 0;
-       start_discover(iface);
-}
-
-static struct dhcp_message *
-dhcp_message_new(struct in_addr *addr, struct in_addr *mask)
-{
-       struct dhcp_message *dhcp;
-       uint8_t *p;
-
-       dhcp = xzalloc(sizeof(*dhcp));
-       dhcp->yiaddr = addr->s_addr;
-       p = dhcp->options;
-       if (mask && mask->s_addr != INADDR_ANY) {
-               *p++ = DHO_SUBNETMASK;
-               *p++ = sizeof(mask->s_addr);
-               memcpy(p, &mask->s_addr, sizeof(mask->s_addr));
-               p+= sizeof(mask->s_addr);
-       }
-       *p++ = DHO_END;
-       return dhcp;
-}
-
-static int
-handle_3rdparty(struct interface *iface)
-{
-       struct if_options *ifo;
-       struct in_addr addr, net, dst;
-       
-       ifo = iface->state->options;
-       if (ifo->req_addr.s_addr != INADDR_ANY)
-               return 0;
-
-       if (get_address(iface->name, &addr, &net, &dst) == 1)
-               handle_ifa(RTM_NEWADDR, iface->name, &addr, &net, &dst);
-       else {
-               syslog(LOG_INFO,
-                   "%s: waiting for 3rd party to configure IP address",
-                   iface->name);
-               iface->state->reason = "3RDPARTY";
-               run_script(iface);
-       }
-       return 1;
-}
-
-static void
-start_static(struct interface *iface)
-{
-       struct if_options *ifo;
-
-       if (handle_3rdparty(iface))
-               return;
-       ifo = iface->state->options;
-       iface->state->offer =
-           dhcp_message_new(&ifo->req_addr, &ifo->req_mask);
-       eloop_timeout_delete(NULL, iface);
-       bind_interface(iface);
-}
-
-static void
-start_inform(struct interface *iface)
-{
-       if (handle_3rdparty(iface))
-               return;
-
-       if (options & DHCPCD_TEST) {
-               iface->addr.s_addr = iface->state->options->req_addr.s_addr;
-               iface->net.s_addr = iface->state->options->req_mask.s_addr;
-       } else {
-               iface->state->options->options |= DHCPCD_STATIC;
-               start_static(iface);
-       }
-
-       iface->state->state = DHS_INFORM;
-       iface->state->xid = dhcp_xid(iface);
-       send_inform(iface);
-}
-
-void
-start_reboot(struct interface *iface)
-{
-       struct if_options *ifo = iface->state->options;
-
-       if (ifo->options & DHCPCD_LINK && iface->carrier == LINK_DOWN) {
-               syslog(LOG_INFO, "%s: waiting for carrier", iface->name);
-               return;
-       }
-       if (ifo->options & DHCPCD_STATIC) {
-               start_static(iface);
-               return;
-       }
-       if (ifo->reboot == 0 || iface->state->offer == NULL) {
-               start_discover(iface);
-               return;
-       }
-       if (ifo->options & DHCPCD_INFORM) {
-               syslog(LOG_INFO, "%s: informing address of %s",
-                   iface->name, inet_ntoa(iface->state->lease.addr));
-       } else if (iface->state->offer->cookie == 0) {
-               if (ifo->options & DHCPCD_IPV4LL) {
-                       iface->state->claims = 0;
-                       send_arp_announce(iface);
-               } else
-                       start_discover(iface);
-               return;
-       } else {
-               syslog(LOG_INFO, "%s: rebinding lease of %s",
-                   iface->name, inet_ntoa(iface->state->lease.addr));
-       }
-       iface->state->state = DHS_REBOOT;
-       iface->state->xid = dhcp_xid(iface);
-       iface->state->lease.server.s_addr = 0;
-       eloop_timeout_delete(NULL, iface);
-       if (ifo->fallback)
-               eloop_timeout_add_sec(ifo->reboot, start_fallback, iface);
-       else if (ifo->options & DHCPCD_LASTLEASE &&
-           iface->state->lease.frominfo)
-               eloop_timeout_add_sec(ifo->reboot, start_timeout, iface);
-       else if (!(ifo->options & DHCPCD_INFORM &&
-           options & (DHCPCD_MASTER | DHCPCD_DAEMONISED)))
-               eloop_timeout_add_sec(ifo->reboot, start_expire, iface);
-       /* Don't bother ARP checking as the server could NAK us first. */
-       if (ifo->options & DHCPCD_INFORM)
-               send_inform(iface);
-       else
-               send_request(iface);
-}
-
 void
 start_interface(void *arg)
 {
        struct interface *iface = arg;
        struct if_options *ifo = iface->state->options;
-       struct stat st;
-       struct timeval now;
-       uint32_t l;
        int nolease;
 
        handle_carrier(0, 0, iface->name);
@@ -1215,11 +500,7 @@ start_interface(void *arg)
                ipv6rs_start(iface);
 
        if (iface->state->arping_index < ifo->arping_len) {
-               start_arping(iface);
-               return;
-       }
-       if (ifo->options & DHCPCD_STATIC) {
-               start_static(iface);
+               arp_start(iface);
                return;
        }
 
@@ -1234,64 +515,8 @@ start_interface(void *arg)
                        syslog(LOG_ERR, "%s: dhcp6_start: %m", iface->name);
        }
 
-       if (!(ifo->options & DHCPCD_IPV4))
-               return;
-
-       if (ifo->options & DHCPCD_INFORM) {
-               start_inform(iface);
-               return;
-       }
-       if (iface->hwlen == 0 && ifo->clientid[0] == '\0') {
-               syslog(LOG_WARNING, "%s: needs a clientid to configure",
-                   iface->name);
-               drop_dhcp(iface, "FAIL");
-               close_sockets(iface);
-               eloop_timeout_delete(NULL, iface);
-               return;
-       }
-       /* We don't want to read the old lease if we NAK an old test */
-       nolease = iface->state->offer && options & DHCPCD_TEST;
-       if (!nolease)
-               iface->state->offer = read_lease(iface);
-       if (iface->state->offer) {
-               get_lease(&iface->state->lease, iface->state->offer);
-               iface->state->lease.frominfo = 1;
-               if (iface->state->offer->cookie == 0) {
-                       if (iface->state->offer->yiaddr ==
-                           iface->addr.s_addr)
-                       {
-                               free(iface->state->offer);
-                               iface->state->offer = NULL;
-                       }
-               } else if (iface->state->lease.leasetime != ~0U &&
-                   stat(iface->leasefile, &st) == 0)
-               {
-                       /* Offset lease times and check expiry */
-                       gettimeofday(&now, NULL);
-                       if ((time_t)iface->state->lease.leasetime <
-                           now.tv_sec - st.st_mtime)
-                       {
-                               syslog(LOG_DEBUG,
-                                   "%s: discarding expired lease",
-                                   iface->name);
-                               free(iface->state->offer);
-                               iface->state->offer = NULL;
-                               iface->state->lease.addr.s_addr = 0;
-                       } else {
-                               l = now.tv_sec - st.st_mtime;
-                               iface->state->lease.leasetime -= l;
-                               iface->state->lease.renewaltime -= l;
-                               iface->state->lease.rebindtime -= l;
-                       }
-               }
-       }
-       if (iface->state->offer == NULL)
-               start_discover(iface);
-       else if (iface->state->offer->cookie == 0 &&
-           iface->state->options->options & DHCPCD_IPV4LL)
-               ipv4ll_start(iface);
-       else
-               start_reboot(iface);
+       if (ifo->options & DHCPCD_IPV4)
+               dhcp_start(iface);
 }
 
 static void
@@ -1309,7 +534,7 @@ init_state(struct interface *iface, int argc, char **argv)
        ifs->nakoff = 0;
        configure_interface(iface, argc, argv);
        if (!(options & DHCPCD_TEST))
-               run_script(iface);
+               script_run(iface);
        /* We need to drop the leasefile so that start_interface
         * doesn't load it. */  
        if (ifs->options->options & DHCPCD_REQUEST)
@@ -1330,7 +555,7 @@ init_state(struct interface *iface, int argc, char **argv)
                        return;
                }
                if (!(options & DHCPCD_TEST))
-                       run_script(iface);
+                       script_run(iface);
        } else
                iface->carrier = LINK_UNKNOWN;
 }
@@ -1422,66 +647,11 @@ handle_hwaddr(const char *ifname, unsigned char *hwaddr, size_t hwlen)
 }
 #endif
 
-void
-handle_ifa(int type, const char *ifname,
-    struct in_addr *addr, struct in_addr *net, struct in_addr *dst)
-{
-       struct interface *ifp;
-       struct if_options *ifo;
-       int i;
-
-       if (addr->s_addr == INADDR_ANY)
-               return;
-       for (ifp = ifaces; ifp; ifp = ifp->next)
-               if (strcmp(ifp->name, ifname) == 0)
-                       break;
-       if (ifp == NULL)
-               return;
-
-       if (type == RTM_DELADDR) {
-               if (ifp->state->new &&
-                   ifp->state->new->yiaddr == addr->s_addr)
-                       syslog(LOG_INFO, "%s: removing IP address %s/%d",
-                           ifp->name, inet_ntoa(ifp->state->lease.addr),
-                           inet_ntocidr(ifp->state->lease.net));
-               return;
-       }
-
-       if (type != RTM_NEWADDR)
-               return;
-
-       ifo = ifp->state->options;
-       if ((ifo->options & (DHCPCD_INFORM | DHCPCD_STATIC)) == 0 ||
-           ifo->req_addr.s_addr != INADDR_ANY)
-               return;
-
-       free(ifp->state->old);
-       ifp->state->old = ifp->state->new;
-       ifp->state->new = dhcp_message_new(addr, net);
-       ifp->dst.s_addr = dst ? dst->s_addr : INADDR_ANY;
-       if (dst) {
-               for (i = 1; i < 255; i++)
-                       if (i != DHO_ROUTER && has_option_mask(ifo->dstmask,i))
-                               dhcp_message_add_addr(ifp->state->new, i, *dst);
-       }
-       ifp->state->reason = "STATIC";
-       build_routes();
-       run_script(ifp);
-       if (ifo->options & DHCPCD_INFORM) {
-               ifp->state->state = DHS_INFORM;
-               ifp->state->xid = dhcp_xid(ifp);
-               ifp->state->lease.server.s_addr =
-                   dst ? dst->s_addr : INADDR_ANY;
-               ifp->addr = *addr;
-               ifp->net = *net;
-               send_inform(ifp);
-       }
-}
-
 /* ARGSUSED */
 static void
 handle_link(_unused void *arg)
 {
+
        if (manage_link(linkfd) == -1)
                syslog(LOG_ERR, "manage_link: %m");
 }
@@ -1502,7 +672,7 @@ if_reboot(struct interface *iface, int argc, char **argv)
            (opt & (DHCPCD_INFORM | DHCPCD_STATIC) &&
                !(ifo->options & (DHCPCD_INFORM | DHCPCD_STATIC))))
        {
-               drop_dhcp(iface, "EXPIRE");
+               dhcp_drop(iface, "EXPIRE");
        } else {
                free(iface->state->offer);
                iface->state->offer = NULL;
@@ -1530,7 +700,7 @@ reconf_reboot(int action, int argc, char **argv, int oi)
                        if (action)
                                if_reboot(ifn, argc, argv);
                        else if (ifn->state->new)
-                               configure(ifn);
+                               ipv4_applyaddr(ifn);
                        free_interface(ifp);
                } else {
                        ifp->next = NULL;
@@ -1592,7 +762,7 @@ handle_signal(int sig)
                syslog(LOG_INFO, "received SIGUSR, reconfiguring");
                for (ifp = ifaces; ifp; ifp = ifp->next)
                        if (ifp->state->new)
-                               configure(ifp);
+                               ipv4_applyaddr(ifp);
                return;
        case SIGPIPE:
                syslog(LOG_WARNING, "received SIGPIPE");
@@ -1619,7 +789,7 @@ handle_signal(int sig)
                if (ifp->carrier != LINK_DOWN &&
                    (do_release ||
                        ifp->state->options->options & DHCPCD_RELEASE))
-                       send_release(ifp);
+                       dhcp_release(ifp);
                stop_interface(ifp);
        }
        exit(EXIT_FAILURE);
@@ -1755,7 +925,7 @@ handle_args(struct fd_list *fd, int argc, char **argv)
                                ifp->state->options->options |= DHCPCD_RELEASE;
                        if (ifp->state->options->options & DHCPCD_RELEASE &&
                            ifp->carrier != LINK_DOWN)
-                               send_release(ifp);
+                               dhcp_release(ifp);
                        stop_interface(ifp);
                }
                return 0;
@@ -1765,51 +935,6 @@ handle_args(struct fd_list *fd, int argc, char **argv)
        return 0;
 }
 
-int
-open_sockets(struct interface *iface)
-{
-       int r = 0;
-
-       if (iface->raw_fd == -1) {
-               if ((r = open_socket(iface, ETHERTYPE_IP)) == -1)
-                       syslog(LOG_ERR, "%s: open_socket: %m", iface->name);
-               else
-                       eloop_event_add(iface->raw_fd, handle_dhcp_packet, iface);
-       }
-       if (iface->udp_fd == -1 &&
-           iface->addr.s_addr != 0 &&
-           iface->state->new != NULL &&
-           (iface->state->new->cookie == htonl(MAGIC_COOKIE) ||
-           iface->state->options->options & DHCPCD_INFORM))
-       {
-               if (open_udp_socket(iface) == -1 && errno != EADDRINUSE) {
-                       syslog(LOG_ERR, "%s: open_udp_socket: %m", iface->name);
-                       r = -1;
-               }
-       }
-       return r;
-}
-
-void
-close_sockets(struct interface *iface)
-{
-       if (iface->arp_fd != -1) {
-               eloop_event_delete(iface->arp_fd);
-               close(iface->arp_fd);
-               iface->arp_fd = -1;
-       }
-       if (iface->raw_fd != -1) {
-               eloop_event_delete(iface->raw_fd);
-               close(iface->raw_fd);
-               iface->raw_fd = -1;
-       }
-       if (iface->udp_fd != -1) {
-               /* we don't listen to events on the udp */
-               close(iface->udp_fd);
-               iface->udp_fd = -1;
-       }
-}
-
 int
 main(int argc, char **argv)
 {
@@ -1967,7 +1092,7 @@ main(int argc, char **argv)
                        exit(EXIT_FAILURE);
                }
                iface->state->reason = "DUMP";
-               run_script(iface);
+               script_run(iface);
                exit(EXIT_SUCCESS);
        }
 
index bfcba8c4769cee2538c226ae19979980d36e1397..2e3899464a97a9c3a53780b12f7ef594a917310c 100644 (file)
--- a/dhcpcd.h
+++ b/dhcpcd.h
@@ -1,6 +1,6 @@
 /* 
  * dhcpcd - DHCP client daemon
- * Copyright (c) 2006-2012 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2013 Roy Marples <roy@marples.name>
  * All rights reserved
 
  * Redistribution and use in source and binary forms, with or without
@@ -128,27 +128,16 @@ extern int ifdc;
 extern char **ifdv;
 extern struct interface *ifaces;
 
+pid_t daemonise(void);
 struct interface *find_interface(const char *);
 int handle_args(struct fd_list *, int, char **);
 void handle_carrier(int, int, const char *);
 void handle_interface(int, const char *);
 void handle_hwaddr(const char *, unsigned char *, size_t);
-void handle_ifa(int, const char *,
-    struct in_addr *, struct in_addr *, struct in_addr *);
-void handle_exit_timeout(void *);
 void handle_signal(int);
-void start_interface(void *);
-void start_discover(void *);
-void start_request(void *);
-void start_renew(void *);
-void start_rebind(void *);
-void start_reboot(struct interface *);
-void start_expire(void *);
-void send_decline(struct interface *);
-int open_sockets(struct interface *);
-void close_sockets(struct interface *);
-void drop_dhcp(struct interface *, const char *);
 void drop_interface(struct interface *, const char *);
 int select_profile(struct interface *, const char *);
 
+void start_interface(void *);
+
 #endif
index f39507a4095d117eb6c7c2005a1ab3231c2ec7f2..4b21ff34a5bb5177a541500d293ad14b87f44634 100644 (file)
@@ -1,6 +1,6 @@
 /* 
  * dhcpcd - DHCP client daemon
- * Copyright (c) 2006-2011 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2013 Roy Marples <roy@marples.name>
  * All rights reserved
 
  * Redistribution and use in source and binary forms, with or without
@@ -57,8 +57,8 @@
 
 #include "config.h"
 #include "common.h"
-#include "configure.h"
 #include "dhcp.h"
+#include "ipv4.h"
 #include "ipv6.h"
 #include "net.h"
 
@@ -294,7 +294,7 @@ link_route(struct nlmsghdr *nlm)
        if (rt.iface != NULL) {
                if (metric == rt.iface->metric) {
                        inet_cidrtoaddr(rtm->rtm_dst_len, &rt.net);
-                       route_deleted(&rt);
+                       ipv4_routedeleted(&rt);
                }
        }
        return 1;
@@ -346,7 +346,7 @@ link_addr(struct nlmsghdr *nlm)
                }
                rta = RTA_NEXT(rta, len);
        }
-       handle_ifa(nlm->nlmsg_type, ifn, &addr, &net, &dest);
+       ipv4_handleifa(nlm->nlmsg_type, ifn, &addr, &net, &dest);
        return 1;
 }
 
similarity index 53%
rename from configure.c
rename to ipv4.c
index 01b0d861cb1b21a0d62b8ac067cc67f4c42f0c43..45b4e5d7b91a407e311b68473dc162aef164f7ae 100644 (file)
+++ b/ipv4.c
  * SUCH DAMAGE.
  */
 
-#include <sys/stat.h>
-#include <sys/uio.h>
-#include <sys/wait.h>
-
 #include <netinet/in.h>
 #include <arpa/inet.h>
 
+#ifdef __linux__
+#  include <asm/types.h> /* for systems with broken headers */
+#  include <linux/rtnetlink.h>
+#endif
+
 #include <ctype.h>
 #include <errno.h>
-#include <signal.h>
-/* We can't include spawn.h here because it may not exist.
- * config.h will pull it in, or our compat one. */
 #include <stdlib.h>
 #include <string.h>
 #include <syslog.h>
 
 #include "config.h"
 #include "common.h"
-#include "configure.h"
 #include "dhcp.h"
-#include "dhcp6.h"
 #include "if-options.h"
 #include "if-pref.h"
-#include "ipv6rs.h"
+#include "ipv4.h"
 #include "net.h"
-#include "signals.h"
-
-#define DEFAULT_PATH   "PATH=/usr/bin:/usr/sbin:/bin:/sbin"
+#include "script.h"
 
 static struct rt *routes;
 
-static const char *if_params[] = {
-       "interface",
-       "reason",
-       "pid",
-       "ifmetric",
-       "ifwireless",
-       "ifflags",
-       "ssid",
-       "profile",
-       "interface_order",
-       NULL
-};
-
-void
-if_printoptions(void)
-{
-       const char **p;
-
-       for (p = if_params; *p; p++)
-               printf(" -  %s\n", *p);
-}
-
-static int
-exec_script(char *const *argv, char *const *env)
-{
-       pid_t pid;
-       posix_spawnattr_t attr;
-       short flags;
-       sigset_t defsigs;
-       int i;
-
-       /* posix_spawn is a safe way of executing another image
-        * and changing signals back to how they should be. */
-       if (posix_spawnattr_init(&attr) == -1)
-               return -1;
-       flags = POSIX_SPAWN_SETSIGMASK | POSIX_SPAWN_SETSIGDEF;
-       posix_spawnattr_setflags(&attr, flags);
-       sigemptyset(&defsigs);
-       for (i = 0; i < handle_sigs[i]; i++)
-               sigaddset(&defsigs, handle_sigs[i]);
-       posix_spawnattr_setsigdefault(&attr, &defsigs);
-       posix_spawnattr_setsigmask(&attr, &dhcpcd_sigset);
-       errno = 0;
-       i = posix_spawn(&pid, argv[0], NULL, &attr, argv, env);
-       if (i) {
-               errno = i;
-               return -1;
-       }
-       return pid;
-}
-
-static char *
-make_var(const char *prefix, const char *var)
-{
-       size_t len;
-       char *v;
-
-       len = strlen(prefix) + strlen(var) + 2;
-       v = xmalloc(len);
-       snprintf(v, len, "%s_%s", prefix, var);
-       return v;
-}
-
-
-static void
-append_config(char ***env, ssize_t *len,
-    const char *prefix, const char *const *config)
-{
-       ssize_t i, j, e1;
-       char **ne, *eq;
-
-       if (config == NULL)
-               return;
-
-       ne = *env;
-       for (i = 0; config[i] != NULL; i++) {
-               eq = strchr(config[i], '=');
-               e1 = eq - config[i] + 1;
-               for (j = 0; j < *len; j++) {
-                       if (strncmp(ne[j] + strlen(prefix) + 1,
-                               config[i], e1) == 0)
-                       {
-                               free(ne[j]);
-                               ne[j] = make_var(prefix, config[i]);
-                               break;
-                       }
-               }
-               if (j == *len) {
-                       j++;
-                       ne = xrealloc(ne, sizeof(char *) * (j + 1));
-                       ne[j - 1] = make_var(prefix, config[i]);
-                       *len = j;
-               }
-       }
-       *env = ne;
-}
-
-static size_t
-arraytostr(const char *const *argv, char **s)
-{
-       const char *const *ap;
-       char *p;
-       size_t len, l;
-
-       len = 0;
-       ap = argv;
-       while (*ap)
-               len += strlen(*ap++) + 1;
-       *s = p = xmalloc(len);
-       ap = argv;
-       while (*ap) {
-               l = strlen(*ap) + 1;
-               memcpy(p, *ap, l);
-               p += l;
-               ap++;
-       }
-       return len;
-}
-
-static ssize_t
-make_env(const struct interface *iface, const char *reason, char ***argv)
-{
-       char **env, *p;
-       ssize_t e, elen, l;
-       const struct if_options *ifo = iface->state->options;
-       const struct interface *ifp;
-       int dhcp, dhcp6, ra;
-       const struct dhcp6_state *d6_state;
-
-       dhcp = dhcp6 = ra = 0;
-       d6_state = D6_STATE(iface);
-       if (strcmp(reason, "TEST") == 0) {
-               if (d6_state && d6_state->new)
-                       dhcp6 = 1;
-               else if (ipv6rs_has_ra(iface))
-                       ra = 1;
-               else
-                       dhcp = 1;
-       } else if (reason[strlen(reason) - 1] == '6')
-               dhcp6 = 1;
-       else if (strcmp(reason, "ROUTERADVERT") == 0)
-               ra = 1;
-       else
-               dhcp = 1;
-
-       /* When dumping the lease, we only want to report interface and
-          reason - the other interface variables are meaningless */
-       if (options & DHCPCD_DUMPLEASE)
-               elen = 2;
-       else
-               elen = 10;
-
-       /* Make our env */
-       env = xmalloc(sizeof(char *) * (elen + 1));
-       e = strlen("interface") + strlen(iface->name) + 2;
-       env[0] = xmalloc(e);
-       snprintf(env[0], e, "interface=%s", iface->name);
-       e = strlen("reason") + strlen(reason) + 2;
-       env[1] = xmalloc(e);
-       snprintf(env[1], e, "reason=%s", reason);
-       if (options & DHCPCD_DUMPLEASE)
-               goto dumplease;
-
-       e = 20;
-       env[2] = xmalloc(e);
-       snprintf(env[2], e, "pid=%d", getpid());
-       env[3] = xmalloc(e);
-       snprintf(env[3], e, "ifmetric=%d", iface->metric);
-       env[4] = xmalloc(e);
-       snprintf(env[4], e, "ifwireless=%d", iface->wireless);
-       env[5] = xmalloc(e);
-       snprintf(env[5], e, "ifflags=%u", iface->flags);
-       env[6] = xmalloc(e);
-       snprintf(env[6], e, "ifmtu=%d", get_mtu(iface->name));
-       l = e = strlen("interface_order=");
-       for (ifp = ifaces; ifp; ifp = ifp->next)
-               e += strlen(ifp->name) + 1;
-       p = env[7] = xmalloc(e);
-       strlcpy(p, "interface_order=", e);
-       e -= l;
-       p += l;
-       for (ifp = ifaces; ifp; ifp = ifp->next) {
-               l = strlcpy(p, ifp->name, e);
-               p += l;
-               e -= l;
-               *p++ = ' ';
-               e--;
-       }
-       *--p = '\0';
-       if (strcmp(reason, "TEST") == 0) {
-               env[8] = strdup("if_up=false");
-               env[9] = strdup("if_down=false");
-       } else if ((dhcp && iface->state->new) ||
-           (dhcp6 && d6_state->new) ||
-           (ra && ipv6rs_has_ra(iface)))
-       {
-               env[8] = strdup("if_up=true");
-               env[9] = strdup("if_down=false");
-       } else {
-               env[8] = strdup("if_up=false");
-               env[9] = strdup("if_down=true");
-       }
-       if (*iface->state->profile) {
-               e = strlen("profile=") + strlen(iface->state->profile) + 2;
-               env[elen] = xmalloc(e);
-               snprintf(env[elen++], e, "profile=%s", iface->state->profile);
-       }
-       if (iface->wireless) {
-               e = strlen("new_ssid=") + strlen(iface->ssid) + 2;
-               if (iface->state->new != NULL ||
-                   strcmp(iface->state->reason, "CARRIER") == 0)
-               {
-                       env = xrealloc(env, sizeof(char *) * (elen + 2));
-                       env[elen] = xmalloc(e);
-                       snprintf(env[elen++], e, "new_ssid=%s", iface->ssid);
-               }
-               if (iface->state->old != NULL ||
-                   strcmp(iface->state->reason, "NOCARRIER") == 0)
-               {
-                       env = xrealloc(env, sizeof(char *) * (elen + 2));
-                       env[elen] = xmalloc(e);
-                       snprintf(env[elen++], e, "old_ssid=%s", iface->ssid);
-               }
-       }
-       if (dhcp && iface->state->old) {
-               e = configure_env(NULL, NULL, iface->state->old, iface);
-               if (e > 0) {
-                       env = xrealloc(env, sizeof(char *) * (elen + e + 1));
-                       elen += configure_env(env + elen, "old",
-                           iface->state->old, iface);
-               }
-               append_config(&env, &elen, "old",
-                   (const char *const *)ifo->config);
-       }
-       if (dhcp6 && d6_state->old) {
-               e = dhcp6_env(NULL, NULL, iface,
-                   d6_state->old, d6_state->old_len);
-               if (e > 0) {
-                       env = xrealloc(env, sizeof(char *) * (elen + e + 1));
-                       elen += dhcp6_env(env + elen, "old", iface,
-                           d6_state->old, d6_state->old_len);
-               }
-       }
-
-dumplease:
-       if (dhcp && iface->state->new) {
-               e = configure_env(NULL, NULL, iface->state->new, iface);
-               if (e > 0) {
-                       env = xrealloc(env, sizeof(char *) * (elen + e + 1));
-                       elen += configure_env(env + elen, "new",
-                           iface->state->new, iface);
-               }
-               append_config(&env, &elen, "new",
-                   (const char *const *)ifo->config);
-       }
-       if (dhcp6 && d6_state->new) {
-               e = dhcp6_env(NULL, NULL, iface,
-                   d6_state->new, d6_state->new_len);
-               if (e > 0) {
-                       env = xrealloc(env, sizeof(char *) * (elen + e + 1));
-                       elen += dhcp6_env(env + elen, "new", iface,
-                           d6_state->new, d6_state->new_len);
-               }
-       }
-       if (ra) {
-               e = ipv6rs_env(NULL, NULL, iface);
-               if (e > 0) {
-                       env = xrealloc(env, sizeof(char *) * (elen + e + 1));
-                       elen += ipv6rs_env(env + elen, NULL, iface);
-               }
-       }
-
-       /* Add our base environment */
-       if (ifo->environ) {
-               e = 0;
-               while (ifo->environ[e++])
-                       ;
-               env = xrealloc(env, sizeof(char *) * (elen + e + 1));
-               e = 0;
-               while (ifo->environ[e]) {
-                       env[elen + e] = xstrdup(ifo->environ[e]);
-                       e++;
-               }
-               elen += e;
-       }
-       env[elen] = '\0';
-
-       *argv = env;
-       return elen;
-}
-
-static int
-send_interface1(int fd, const struct interface *iface, const char *reason)
-{
-       char **env, **ep, *s;
-       ssize_t elen;
-       struct iovec iov[2];
-       int retval;
-
-       make_env(iface, reason, &env);
-       elen = arraytostr((const char *const *)env, &s);
-       iov[0].iov_base = &elen;
-       iov[0].iov_len = sizeof(ssize_t);
-       iov[1].iov_base = s;
-       iov[1].iov_len = elen;
-       retval = writev(fd, iov, 2);
-       ep = env;
-       while (*ep)
-               free(*ep++);
-       free(env);
-       free(s);
-       return retval;
-}
-
-int
-send_interface(int fd, const struct interface *iface)
-{
-       int retval = 0;
-       if (send_interface1(fd, iface, iface->state->reason) == -1)
-               retval = -1;
-       if (ipv6rs_has_ra(iface)) {
-               if (send_interface1(fd, iface, "ROUTERADVERT") == -1)
-                       retval = -1;
-       }
-       if (D6_STATE_RUNNING(iface)) {
-               if (send_interface1(fd, iface, "INFORM6") == -1)
-                       retval = -1;
-       }
-       return retval;
-}
-
-int
-run_script_reason(const struct interface *iface, const char *reason)
-{
-       char *const argv[2] = { UNCONST(iface->state->options->script), NULL };
-       char **env = NULL, **ep;
-       char *path, *bigenv;
-       ssize_t e, elen = 0;
-       pid_t pid;
-       int status = 0;
-       const struct fd_list *fd;
-       struct iovec iov[2];
-
-       if (iface->state->options->script == NULL ||
-           iface->state->options->script[0] == '\0' ||
-           strcmp(iface->state->options->script, "/dev/null") == 0)
-               return 0;
-
-       if (reason == NULL)
-               reason = iface->state->reason;
-       syslog(LOG_DEBUG, "%s: executing `%s', reason %s",
-           iface->name, argv[0], reason);
-
-       /* Make our env */
-       elen = make_env(iface, reason, &env);
-       env = xrealloc(env, sizeof(char *) * (elen + 2));
-       /* Add path to it */
-       path = getenv("PATH");
-       if (path) {
-               e = strlen("PATH") + strlen(path) + 2;
-               env[elen] = xmalloc(e);
-               snprintf(env[elen], e, "PATH=%s", path);
-       } else
-               env[elen] = xstrdup(DEFAULT_PATH);
-       env[++elen] = '\0';
-
-       pid = exec_script(argv, env);
-       if (pid == -1)
-               syslog(LOG_ERR, "exec_script: %s: %m", argv[0]);
-       else if (pid != 0) {
-               /* Wait for the script to finish */
-               while (waitpid(pid, &status, 0) == -1) {
-                       if (errno != EINTR) {
-                               syslog(LOG_ERR, "waitpid: %m");
-                               status = 0;
-                               break;
-                       }
-               }
-               if (WIFEXITED(status)) {
-                       if (WEXITSTATUS(status))
-                               syslog(LOG_ERR,
-                                   "exec_script: %s: WEXITSTATUS %d",
-                                   argv[0], WEXITSTATUS(status));
-               } else if (WIFSIGNALED(status))
-                       syslog(LOG_ERR, "exec_sript: %s: %s",
-                           argv[0], strsignal(WTERMSIG(status)));
-       }
-
-       /* Send to our listeners */
-       bigenv = NULL;
-       for (fd = control_fds; fd != NULL; fd = fd->next) {
-               if (fd->listener) {
-                       if (bigenv == NULL) {
-                               elen = arraytostr((const char *const *)env,
-                                   &bigenv);
-                               iov[0].iov_base = &elen;
-                               iov[0].iov_len = sizeof(ssize_t);
-                               iov[1].iov_base = bigenv;
-                               iov[1].iov_len = elen;
-                       }
-                       if (writev(fd->fd, iov, 2) == -1)
-                               syslog(LOG_ERR, "writev: %m");
-               }
-       }
-       free(bigenv);
-
-       /* Cleanup */
-       ep = env;
-       while (*ep)
-               free(*ep++);
-       free(env);
-       return WEXITSTATUS(status);
-}
-
 static struct rt *
 find_route(struct rt *rts, const struct rt *r, struct rt **lrt,
     const struct rt *srt)
@@ -519,7 +99,7 @@ desc_route(const char *cmd, const struct rt *rt)
 /* If something other than dhcpcd removes a route,
  * we need to remove it from our internal table. */
 int
-route_deleted(const struct rt *rt)
+ipv4_routedeleted(const struct rt *rt)
 {
        struct rt *f, *l;
 
@@ -636,12 +216,12 @@ add_subnet_route(struct rt *rt, const struct interface *iface)
 }
 
 static struct rt *
-get_routes(struct interface *iface)
+get_routes(struct interface *ifp)
 {
        struct rt *rt, *nrt = NULL, *r = NULL;
 
-       if (iface->state->options->routes != NULL) {
-               for (rt = iface->state->options->routes;
+       if (ifp->state->options->routes != NULL) {
+               for (rt = ifp->state->options->routes;
                     rt != NULL;
                     rt = rt->next)
                {
@@ -659,7 +239,7 @@ get_routes(struct interface *iface)
                return nrt;
        }
 
-       return get_option_routes(iface, iface->state->new);
+       return get_option_routes(ifp, ifp->state->new);
 }
 
 /* Some DHCP servers add set host routes by setting the gateway
@@ -746,7 +326,7 @@ add_router_host_route(struct rt *rt, const struct interface *ifp)
 }
 
 void
-build_routes(void)
+ipv4_buildroutes(void)
 {
        struct rt *nrs = NULL, *dnr, *or, *rt, *rtn, *rtl, *lrt = NULL;
        struct interface *ifp;
@@ -830,9 +410,10 @@ delete_address(struct interface *iface)
        return retval;
 }
 
-int
-configure(struct interface *iface)
+void
+ipv4_applyaddr(void *arg)
 {
+       struct interface *iface = arg;
        struct dhcp_message *dhcp = iface->state->new;
        struct dhcp_lease *lease = &iface->state->lease;
        struct if_options *ifo = iface->state->options;
@@ -844,12 +425,12 @@ configure(struct interface *iface)
 
        if (dhcp == NULL) {
                if (!(ifo->options & DHCPCD_PERSISTENT)) {
-                       build_routes();
+                       ipv4_buildroutes();
                        if (iface->addr.s_addr != 0)
                                delete_address(iface);
-                       run_script(iface);
+                       script_run(iface);
                }
-               return 0;
+               return;
        }
 
        /* This also changes netmask */
@@ -863,8 +444,8 @@ configure(struct interface *iface)
                        &lease->addr, &lease->net, &lease->brd) == -1 &&
                    errno != EEXIST)
                {
-                       syslog(LOG_ERR, "add_address: %m");
-                       return -1;
+                       syslog(LOG_ERR, "%s: add_address: %m", __func__);
+                       return;
                }
        }
 
@@ -887,11 +468,66 @@ configure(struct interface *iface)
                free(rt);
        }
 
-       build_routes();
+       ipv4_buildroutes();
        if (!iface->state->lease.frominfo &&
            !(ifo->options & (DHCPCD_INFORM | DHCPCD_STATIC)))
                if (write_lease(iface, dhcp) == -1)
-                       syslog(LOG_ERR, "write_lease: %m");
-       run_script(iface);
-       return 0;
+                       syslog(LOG_ERR, "%s: write_lease: %m", __func__);
+       script_run(iface);
+}
+
+void
+ipv4_handleifa(int type, const char *ifname,
+    struct in_addr *addr, struct in_addr *net, struct in_addr *dst)
+{
+       struct interface *ifp;
+       struct if_options *ifo;
+       int i;
+
+       if (addr->s_addr == INADDR_ANY)
+               return;
+       for (ifp = ifaces; ifp; ifp = ifp->next)
+               if (strcmp(ifp->name, ifname) == 0)
+                       break;
+       if (ifp == NULL)
+               return;
+
+       if (type == RTM_DELADDR) {
+               if (ifp->state->new &&
+                   ifp->state->new->yiaddr == addr->s_addr)
+                       syslog(LOG_INFO, "%s: removing IP address %s/%d",
+                           ifp->name, inet_ntoa(ifp->state->lease.addr),
+                           inet_ntocidr(ifp->state->lease.net));
+               return;
+       }
+
+       if (type != RTM_NEWADDR)
+               return;
+
+       ifo = ifp->state->options;
+       if ((ifo->options & (DHCPCD_INFORM | DHCPCD_STATIC)) == 0 ||
+           ifo->req_addr.s_addr != INADDR_ANY)
+               return;
+
+       free(ifp->state->old);
+       ifp->state->old = ifp->state->new;
+       ifp->state->new = dhcp_message_new(addr, net);
+       ifp->dst.s_addr = dst ? dst->s_addr : INADDR_ANY;
+       if (dst) {
+               for (i = 1; i < 255; i++)
+                       if (i != DHO_ROUTER && has_option_mask(ifo->dstmask,i))
+                               dhcp_message_add_addr(ifp->state->new, i, *dst);
+       }
+       ifp->state->reason = "STATIC";
+       ipv4_buildroutes();
+       script_run(ifp);
+       if (ifo->options & DHCPCD_INFORM) {
+               ifp->state->state = DHS_INFORM;
+               ifp->state->xid = dhcp_xid(ifp);
+               ifp->state->lease.server.s_addr =
+                   dst ? dst->s_addr : INADDR_ANY;
+               ifp->addr = *addr;
+               ifp->net = *net;
+               dhcp_inform(ifp);
+       }
 }
diff --git a/bind.h b/ipv4.h
similarity index 81%
rename from bind.h
rename to ipv4.h
index 375a0f3cca6c7c435ae218c4f4fbcd7fd7158fad..a13f4478c0c0068e138e246f9202cd4e7809cc9c 100644 (file)
--- a/bind.h
+++ b/ipv4.h
@@ -1,6 +1,6 @@
 /* 
  * dhcpcd - DHCP client daemon
- * Copyright (c) 2006-2008 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2013 Roy Marples <roy@marples.name>
  * All rights reserved
 
  * Redistribution and use in source and binary forms, with or without
  * SUCH DAMAGE.
  */
 
-#ifndef BIND_H
-#define BIND_H
+#ifndef IPV4_H
+#define IPV4_H
 
-#include "config.h"
-#ifdef THERE_IS_NO_FORK
-# define daemonise() {}
-#else
-pid_t daemonise(void);
-#endif
+#include "net.h"
+
+void ipv4_buildroutes(void);
+void ipv4_applyaddr(void *);
+int ipv4_routedeleted(const struct rt *);
 
-void bind_interface(void *);
+void ipv4_handleifa(int, const char *,
+    struct in_addr *, struct in_addr *, struct in_addr *);
 #endif
index 402e158cdf1787d5c4bd7748af58518530ff14dc..f1d4b1223f30a72f6ed8e4efabb176077e998621 100644 (file)
--- a/ipv4ll.c
+++ b/ipv4ll.c
@@ -1,6 +1,6 @@
 /* 
  * dhcpcd - DHCP client daemon
- * Copyright (c) 2006-2012 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2013 Roy Marples <roy@marples.name>
  * All rights reserved
 
  * Redistribution and use in source and binary forms, with or without
@@ -94,7 +94,7 @@ ipv4ll_start(void *arg)
        if (ifp->addr.s_addr) {
                ifp->state->conflicts = 0;
                if (IN_LINKLOCAL(htonl(ifp->addr.s_addr))) {
-                       send_arp_announce(ifp);
+                       arp_announce(ifp);
                        return;
                }
        }
@@ -116,7 +116,7 @@ ipv4ll_start(void *arg)
        else
                ifp->state->offer = ipv4ll_make_lease(addr);
        ifp->state->lease.frominfo = 0;
-       send_arp_probe(ifp);
+       arp_probe(ifp);
 }
 
 void
@@ -131,7 +131,7 @@ ipv4ll_handle_failure(void *arg)
                        syslog(LOG_DEBUG,
                            "%s: IPv4LL %d second defence failed",
                            ifp->name, DEFEND_INTERVAL);
-                       drop_dhcp(ifp, "EXPIRE");
+                       dhcp_drop(ifp, "EXPIRE");
                        ifp->state->conflicts = -1;
                } else {
                        syslog(LOG_DEBUG, "%s: defended IPv4LL address",
@@ -141,7 +141,7 @@ ipv4ll_handle_failure(void *arg)
                }
        }
 
-       close_sockets(ifp);
+       dhcp_close(ifp);
        free(ifp->state->offer);
        ifp->state->offer = NULL;
        eloop_timeout_delete(NULL, ifp);
@@ -149,7 +149,7 @@ ipv4ll_handle_failure(void *arg)
                syslog(LOG_ERR, "%s: failed to acquire an IPv4LL address",
                    ifp->name);
                ifp->state->interval = RATE_LIMIT_INTERVAL / 2;
-               start_discover(ifp);
+               dhcp_discover(ifp);
        } else {
                eloop_timeout_add_sec(PROBE_WAIT, ipv4ll_start, ifp);
        }
diff --git a/ipv6.c b/ipv6.c
index 93d2e1cf3daeed51190c9c2f1053e92ff0e8e06c..ae6c5df342e327e2bfb783c87017b733b8d06a2b 100644 (file)
--- a/ipv6.c
+++ b/ipv6.c
@@ -1,6 +1,6 @@
 /* 
  * dhcpcd - DHCP client daemon
- * Copyright (c) 2006-2012 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2013 Roy Marples <roy@marples.name>
  * All rights reserved
 
  * Redistribution and use in source and binary forms, with or without
@@ -37,7 +37,6 @@
 #include <syslog.h>
 
 #include "common.h"
-#include "configure.h"
 #include "dhcpcd.h"
 #include "dhcp6.h"
 #include "ipv6.h"
index 02e7f82ef938b7894f670f493ad36b89987b1d1a..120ec2a1142009cd09506dedc8e19ec8ce1e5c71 100644 (file)
--- a/ipv6ns.c
+++ b/ipv6ns.c
@@ -1,6 +1,6 @@
 /* 
  * dhcpcd - DHCP client daemon
- * Copyright (c) 2006-2012 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2013 Roy Marples <roy@marples.name>
  * All rights reserved
 
  * Redistribution and use in source and binary forms, with or without
 
 #define ELOOP_QUEUE 1
 #include "common.h"
-#include "configure.h"
 #include "dhcpcd.h"
 #include "eloop.h"
 #include "ipv6.h"
 #include "ipv6ns.h"
+#include "script.h"
 
 #define MIN_RANDOM_FACTOR      (500 * 1000)    /* milliseconds in usecs */
 #define MAX_RANDOM_FACTOR      (1500 * 1000)   /* milliseconds in usecs */
@@ -169,7 +169,7 @@ ipv6ns_unreachable(void *arg)
            rap->iface->name, rap->sfrom);
        rap->expired = 1;
        ipv6_buildroutes();
-       run_script_reason(rap->iface, "ROUTERADVERT"); /* XXX not RA */
+       script_runreason(rap->iface, "ROUTERADVERT"); /* XXX not RA */
 }
 
 void
@@ -342,7 +342,7 @@ ipv6ns_handledata(_unused void *arg)
                    ifp->name, sfrom);
                rap->expired = 1;
                ipv6_buildroutes();
-               run_script_reason(ifp, "ROUTERADVERT");
+               script_runreason(ifp, "ROUTERADVERT");
                return;
        }
 
index 3b18750ac82584ef85049e09bbfe0a75f45f24fd..2d64ba98ff9cc02649c56cec325ecb87941e5570 100644 (file)
--- a/ipv6rs.c
+++ b/ipv6rs.c
@@ -1,6 +1,6 @@
 /* 
  * dhcpcd - DHCP client daemon
- * Copyright (c) 2006-2012 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2013 Roy Marples <roy@marples.name>
  * All rights reserved
 
  * Redistribution and use in source and binary forms, with or without
 #include <syslog.h>
 
 #define ELOOP_QUEUE 1
-#include "bind.h"
 #include "common.h"
-#include "configure.h"
 #include "dhcpcd.h"
 #include "dhcp6.h"
 #include "eloop.h"
 #include "ipv6.h"
 #include "ipv6ns.h"
 #include "ipv6rs.h"
+#include "script.h"
 
 /* Debugging Router Solicitations is a lot of spam, so disable it */
 //#define DEBUG_RS
@@ -756,13 +755,13 @@ ipv6rs_handledata(_unused void *arg)
        if (options & DHCPCD_IPV6RA_OWN && !(options & DHCPCD_TEST))
                ipv6_addaddrs(ifp, &rap->addrs);
        if (options & DHCPCD_TEST) {
-               run_script_reason(ifp, "TEST");
+               script_runreason(ifp, "TEST");
                goto handle_flag;
        }
        ipv6_buildroutes();
        /* We will get run by the expire function */
        if (rap->lifetime)
-               run_script_reason(ifp, "ROUTERADVERT");
+               script_runreason(ifp, "ROUTERADVERT");
 
        /* If we don't require RDNSS then set has_dns = 1 so we fork */
        if (!(ifp->state->options->options & DHCPCD_IPV6RA_REQRDNSS))
@@ -1070,7 +1069,7 @@ ipv6rs_expire(void *arg)
                eloop_timeout_add_tv(&next, ipv6rs_expire, ifp);
        if (expired) {
                ipv6_buildroutes();
-               run_script_reason(ifp, "ROUTERADVERT");
+               script_runreason(ifp, "ROUTERADVERT");
        }
 }
 
@@ -1125,6 +1124,6 @@ ipv6rs_drop(struct interface *ifp)
                                ipv6rs_drop_ra(rap);
                        }
                }
-               run_script_reason(ifp, "ROUTERADVERT");
+               script_runreason(ifp, "ROUTERADVERT");
        }
 }
diff --git a/script.c b/script.c
new file mode 100644 (file)
index 0000000..870d941
--- /dev/null
+++ b/script.c
@@ -0,0 +1,470 @@
+/* 
+ * dhcpcd - DHCP client daemon
+ * Copyright (c) 2006-2013 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/uio.h>
+#include <sys/wait.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <signal.h>
+/* We can't include spawn.h here because it may not exist.
+ * config.h will pull it in, or our compat one. */
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include "config.h"
+#include "common.h"
+#include "dhcp.h"
+#include "dhcp6.h"
+#include "if-options.h"
+#include "if-pref.h"
+#include "ipv6rs.h"
+#include "net.h"
+#include "script.h"
+#include "signals.h"
+
+#define DEFAULT_PATH   "PATH=/usr/bin:/usr/sbin:/bin:/sbin"
+
+static const char *if_params[] = {
+       "interface",
+       "reason",
+       "pid",
+       "ifmetric",
+       "ifwireless",
+       "ifflags",
+       "ssid",
+       "profile",
+       "interface_order",
+       NULL
+};
+
+void
+if_printoptions(void)
+{
+       const char **p;
+
+       for (p = if_params; *p; p++)
+               printf(" -  %s\n", *p);
+}
+
+static int
+exec_script(char *const *argv, char *const *env)
+{
+       pid_t pid;
+       posix_spawnattr_t attr;
+       short flags;
+       sigset_t defsigs;
+       int i;
+
+       /* posix_spawn is a safe way of executing another image
+        * and changing signals back to how they should be. */
+       if (posix_spawnattr_init(&attr) == -1)
+               return -1;
+       flags = POSIX_SPAWN_SETSIGMASK | POSIX_SPAWN_SETSIGDEF;
+       posix_spawnattr_setflags(&attr, flags);
+       sigemptyset(&defsigs);
+       for (i = 0; i < handle_sigs[i]; i++)
+               sigaddset(&defsigs, handle_sigs[i]);
+       posix_spawnattr_setsigdefault(&attr, &defsigs);
+       posix_spawnattr_setsigmask(&attr, &dhcpcd_sigset);
+       errno = 0;
+       i = posix_spawn(&pid, argv[0], NULL, &attr, argv, env);
+       if (i) {
+               errno = i;
+               return -1;
+       }
+       return pid;
+}
+
+static char *
+make_var(const char *prefix, const char *var)
+{
+       size_t len;
+       char *v;
+
+       len = strlen(prefix) + strlen(var) + 2;
+       v = xmalloc(len);
+       snprintf(v, len, "%s_%s", prefix, var);
+       return v;
+}
+
+
+static void
+append_config(char ***env, ssize_t *len,
+    const char *prefix, const char *const *config)
+{
+       ssize_t i, j, e1;
+       char **ne, *eq;
+
+       if (config == NULL)
+               return;
+
+       ne = *env;
+       for (i = 0; config[i] != NULL; i++) {
+               eq = strchr(config[i], '=');
+               e1 = eq - config[i] + 1;
+               for (j = 0; j < *len; j++) {
+                       if (strncmp(ne[j] + strlen(prefix) + 1,
+                               config[i], e1) == 0)
+                       {
+                               free(ne[j]);
+                               ne[j] = make_var(prefix, config[i]);
+                               break;
+                       }
+               }
+               if (j == *len) {
+                       j++;
+                       ne = xrealloc(ne, sizeof(char *) * (j + 1));
+                       ne[j - 1] = make_var(prefix, config[i]);
+                       *len = j;
+               }
+       }
+       *env = ne;
+}
+
+static size_t
+arraytostr(const char *const *argv, char **s)
+{
+       const char *const *ap;
+       char *p;
+       size_t len, l;
+
+       len = 0;
+       ap = argv;
+       while (*ap)
+               len += strlen(*ap++) + 1;
+       *s = p = xmalloc(len);
+       ap = argv;
+       while (*ap) {
+               l = strlen(*ap) + 1;
+               memcpy(p, *ap, l);
+               p += l;
+               ap++;
+       }
+       return len;
+}
+
+static ssize_t
+make_env(const struct interface *iface, const char *reason, char ***argv)
+{
+       char **env, *p;
+       ssize_t e, elen, l;
+       const struct if_options *ifo = iface->state->options;
+       const struct interface *ifp;
+       int dhcp, dhcp6, ra;
+       const struct dhcp6_state *d6_state;
+
+       dhcp = dhcp6 = ra = 0;
+       d6_state = D6_STATE(iface);
+       if (strcmp(reason, "TEST") == 0) {
+               if (d6_state && d6_state->new)
+                       dhcp6 = 1;
+               else if (ipv6rs_has_ra(iface))
+                       ra = 1;
+               else
+                       dhcp = 1;
+       } else if (reason[strlen(reason) - 1] == '6')
+               dhcp6 = 1;
+       else if (strcmp(reason, "ROUTERADVERT") == 0)
+               ra = 1;
+       else
+               dhcp = 1;
+
+       /* When dumping the lease, we only want to report interface and
+          reason - the other interface variables are meaningless */
+       if (options & DHCPCD_DUMPLEASE)
+               elen = 2;
+       else
+               elen = 10;
+
+       /* Make our env */
+       env = xmalloc(sizeof(char *) * (elen + 1));
+       e = strlen("interface") + strlen(iface->name) + 2;
+       env[0] = xmalloc(e);
+       snprintf(env[0], e, "interface=%s", iface->name);
+       e = strlen("reason") + strlen(reason) + 2;
+       env[1] = xmalloc(e);
+       snprintf(env[1], e, "reason=%s", reason);
+       if (options & DHCPCD_DUMPLEASE)
+               goto dumplease;
+
+       e = 20;
+       env[2] = xmalloc(e);
+       snprintf(env[2], e, "pid=%d", getpid());
+       env[3] = xmalloc(e);
+       snprintf(env[3], e, "ifmetric=%d", iface->metric);
+       env[4] = xmalloc(e);
+       snprintf(env[4], e, "ifwireless=%d", iface->wireless);
+       env[5] = xmalloc(e);
+       snprintf(env[5], e, "ifflags=%u", iface->flags);
+       env[6] = xmalloc(e);
+       snprintf(env[6], e, "ifmtu=%d", get_mtu(iface->name));
+       l = e = strlen("interface_order=");
+       for (ifp = ifaces; ifp; ifp = ifp->next)
+               e += strlen(ifp->name) + 1;
+       p = env[7] = xmalloc(e);
+       strlcpy(p, "interface_order=", e);
+       e -= l;
+       p += l;
+       for (ifp = ifaces; ifp; ifp = ifp->next) {
+               l = strlcpy(p, ifp->name, e);
+               p += l;
+               e -= l;
+               *p++ = ' ';
+               e--;
+       }
+       *--p = '\0';
+       if (strcmp(reason, "TEST") == 0) {
+               env[8] = strdup("if_up=false");
+               env[9] = strdup("if_down=false");
+       } else if ((dhcp && iface->state->new) ||
+           (dhcp6 && d6_state->new) ||
+           (ra && ipv6rs_has_ra(iface)))
+       {
+               env[8] = strdup("if_up=true");
+               env[9] = strdup("if_down=false");
+       } else {
+               env[8] = strdup("if_up=false");
+               env[9] = strdup("if_down=true");
+       }
+       if (*iface->state->profile) {
+               e = strlen("profile=") + strlen(iface->state->profile) + 2;
+               env[elen] = xmalloc(e);
+               snprintf(env[elen++], e, "profile=%s", iface->state->profile);
+       }
+       if (iface->wireless) {
+               e = strlen("new_ssid=") + strlen(iface->ssid) + 2;
+               if (iface->state->new != NULL ||
+                   strcmp(iface->state->reason, "CARRIER") == 0)
+               {
+                       env = xrealloc(env, sizeof(char *) * (elen + 2));
+                       env[elen] = xmalloc(e);
+                       snprintf(env[elen++], e, "new_ssid=%s", iface->ssid);
+               }
+               if (iface->state->old != NULL ||
+                   strcmp(iface->state->reason, "NOCARRIER") == 0)
+               {
+                       env = xrealloc(env, sizeof(char *) * (elen + 2));
+                       env[elen] = xmalloc(e);
+                       snprintf(env[elen++], e, "old_ssid=%s", iface->ssid);
+               }
+       }
+       if (dhcp && iface->state->old) {
+               e = configure_env(NULL, NULL, iface->state->old, iface);
+               if (e > 0) {
+                       env = xrealloc(env, sizeof(char *) * (elen + e + 1));
+                       elen += configure_env(env + elen, "old",
+                           iface->state->old, iface);
+               }
+               append_config(&env, &elen, "old",
+                   (const char *const *)ifo->config);
+       }
+       if (dhcp6 && d6_state->old) {
+               e = dhcp6_env(NULL, NULL, iface,
+                   d6_state->old, d6_state->old_len);
+               if (e > 0) {
+                       env = xrealloc(env, sizeof(char *) * (elen + e + 1));
+                       elen += dhcp6_env(env + elen, "old", iface,
+                           d6_state->old, d6_state->old_len);
+               }
+       }
+
+dumplease:
+       if (dhcp && iface->state->new) {
+               e = configure_env(NULL, NULL, iface->state->new, iface);
+               if (e > 0) {
+                       env = xrealloc(env, sizeof(char *) * (elen + e + 1));
+                       elen += configure_env(env + elen, "new",
+                           iface->state->new, iface);
+               }
+               append_config(&env, &elen, "new",
+                   (const char *const *)ifo->config);
+       }
+       if (dhcp6 && d6_state->new) {
+               e = dhcp6_env(NULL, NULL, iface,
+                   d6_state->new, d6_state->new_len);
+               if (e > 0) {
+                       env = xrealloc(env, sizeof(char *) * (elen + e + 1));
+                       elen += dhcp6_env(env + elen, "new", iface,
+                           d6_state->new, d6_state->new_len);
+               }
+       }
+       if (ra) {
+               e = ipv6rs_env(NULL, NULL, iface);
+               if (e > 0) {
+                       env = xrealloc(env, sizeof(char *) * (elen + e + 1));
+                       elen += ipv6rs_env(env + elen, NULL, iface);
+               }
+       }
+
+       /* Add our base environment */
+       if (ifo->environ) {
+               e = 0;
+               while (ifo->environ[e++])
+                       ;
+               env = xrealloc(env, sizeof(char *) * (elen + e + 1));
+               e = 0;
+               while (ifo->environ[e]) {
+                       env[elen + e] = xstrdup(ifo->environ[e]);
+                       e++;
+               }
+               elen += e;
+       }
+       env[elen] = '\0';
+
+       *argv = env;
+       return elen;
+}
+
+static int
+send_interface1(int fd, const struct interface *iface, const char *reason)
+{
+       char **env, **ep, *s;
+       ssize_t elen;
+       struct iovec iov[2];
+       int retval;
+
+       make_env(iface, reason, &env);
+       elen = arraytostr((const char *const *)env, &s);
+       iov[0].iov_base = &elen;
+       iov[0].iov_len = sizeof(ssize_t);
+       iov[1].iov_base = s;
+       iov[1].iov_len = elen;
+       retval = writev(fd, iov, 2);
+       ep = env;
+       while (*ep)
+               free(*ep++);
+       free(env);
+       free(s);
+       return retval;
+}
+
+int
+send_interface(int fd, const struct interface *iface)
+{
+       int retval = 0;
+       if (send_interface1(fd, iface, iface->state->reason) == -1)
+               retval = -1;
+       if (ipv6rs_has_ra(iface)) {
+               if (send_interface1(fd, iface, "ROUTERADVERT") == -1)
+                       retval = -1;
+       }
+       if (D6_STATE_RUNNING(iface)) {
+               if (send_interface1(fd, iface, "INFORM6") == -1)
+                       retval = -1;
+       }
+       return retval;
+}
+
+int
+script_runreason(const struct interface *iface, const char *reason)
+{
+       char *const argv[2] = { UNCONST(iface->state->options->script), NULL };
+       char **env = NULL, **ep;
+       char *path, *bigenv;
+       ssize_t e, elen = 0;
+       pid_t pid;
+       int status = 0;
+       const struct fd_list *fd;
+       struct iovec iov[2];
+
+       if (iface->state->options->script == NULL ||
+           iface->state->options->script[0] == '\0' ||
+           strcmp(iface->state->options->script, "/dev/null") == 0)
+               return 0;
+
+       if (reason == NULL)
+               reason = iface->state->reason;
+       syslog(LOG_DEBUG, "%s: executing `%s', reason %s",
+           iface->name, argv[0], reason);
+
+       /* Make our env */
+       elen = make_env(iface, reason, &env);
+       env = xrealloc(env, sizeof(char *) * (elen + 2));
+       /* Add path to it */
+       path = getenv("PATH");
+       if (path) {
+               e = strlen("PATH") + strlen(path) + 2;
+               env[elen] = xmalloc(e);
+               snprintf(env[elen], e, "PATH=%s", path);
+       } else
+               env[elen] = xstrdup(DEFAULT_PATH);
+       env[++elen] = '\0';
+
+       pid = exec_script(argv, env);
+       if (pid == -1)
+               syslog(LOG_ERR, "%s: %s: %m", __func__, argv[0]);
+       else if (pid != 0) {
+               /* Wait for the script to finish */
+               while (waitpid(pid, &status, 0) == -1) {
+                       if (errno != EINTR) {
+                               syslog(LOG_ERR, "waitpid: %m");
+                               status = 0;
+                               break;
+                       }
+               }
+               if (WIFEXITED(status)) {
+                       if (WEXITSTATUS(status))
+                               syslog(LOG_ERR,
+                                   "%s: %s: WEXITSTATUS %d",
+                                   __func__, argv[0], WEXITSTATUS(status));
+               } else if (WIFSIGNALED(status))
+                       syslog(LOG_ERR, "%s: %s: %s",
+                           __func__, argv[0], strsignal(WTERMSIG(status)));
+       }
+
+       /* Send to our listeners */
+       bigenv = NULL;
+       for (fd = control_fds; fd != NULL; fd = fd->next) {
+               if (fd->listener) {
+                       if (bigenv == NULL) {
+                               elen = arraytostr((const char *const *)env,
+                                   &bigenv);
+                               iov[0].iov_base = &elen;
+                               iov[0].iov_len = sizeof(ssize_t);
+                               iov[1].iov_base = bigenv;
+                               iov[1].iov_len = elen;
+                       }
+                       if (writev(fd->fd, iov, 2) == -1)
+                               syslog(LOG_ERR, "%s: writev: %m", __func__);
+               }
+       }
+       free(bigenv);
+
+       /* Cleanup */
+       ep = env;
+       while (*ep)
+               free(*ep++);
+       free(env);
+       return WEXITSTATUS(status);
+}
similarity index 81%
rename from configure.h
rename to script.h
index 5e5090fa60fd91b4b1350ebd9f015c40ad114ae2..50e142cc7a96ebefba2f5acc6ddd2c500cada4e7 100644 (file)
+++ b/script.h
@@ -1,6 +1,6 @@
 /* 
  * dhcpcd - DHCP client daemon
- * Copyright (c) 2006-2012 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2013 Roy Marples <roy@marples.name>
  * All rights reserved
 
  * Redistribution and use in source and binary forms, with or without
  * SUCH DAMAGE.
  */
 
-#ifndef DHCPCONFIG_H
-#define DHCPCONFIG_H
+#ifndef SCRIPT_H
+#define SCRIPT_H
 
 #include "net.h"
 
 void if_printoptions(void);
 int send_interface(int, const struct interface *);
-int run_script_reason(const struct interface *, const char *);
-void build_routes(void);
-int configure(struct interface *);
-int route_deleted(const struct rt *);
+int script_runreason(const struct interface *, const char *);
 
-#define run_script(ifp) run_script_reason(ifp, (ifp)->state->reason);
+#define script_run(ifp) script_runreason(ifp, (ifp)->state->reason);
 #endif