]> git.ipfire.org Git - thirdparty/dhcpcd.git/commitdiff
Re-write the client state engine around a simple timer instead of complex loops....
authorRoy Marples <roy@marples.name>
Sun, 8 Jun 2008 20:07:54 +0000 (20:07 +0000)
committerRoy Marples <roy@marples.name>
Sun, 8 Jun 2008 20:07:54 +0000 (20:07 +0000)
bpf.c
client.c
dhcpcd.c
lpf.c
net.c
net.h

diff --git a/bpf.c b/bpf.c
index 37a92f3d205f2581183d1754bb612c38d24a9131..cf7cb8f288da724bade2355547bd149d6141e65d 100644 (file)
--- a/bpf.c
+++ b/bpf.c
@@ -52,6 +52,7 @@ int
 open_socket(struct interface *iface, int protocol)
 {
        int fd = -1;
+       int *fdp;
        struct ifreq ifr;
        int buf_len = 0;
        struct bpf_version pv;
@@ -106,22 +107,24 @@ open_socket(struct interface *iface, int protocol)
 #endif
 
        /* Install the DHCP filter */
+#ifdef ENABLE_ARP
        if (protocol == ETHERTYPE_ARP) {
                pf.bf_insns = UNCONST(arp_bpf_filter);
                pf.bf_len = arp_bpf_filter_len;
-       } else {
+               fdp = &iface->arp_fd;
+       } else
+#endif
+       {
                pf.bf_insns = UNCONST(dhcp_bpf_filter);
                pf.bf_len = dhcp_bpf_filter_len;
+               fdp = &iface->fd;
        }
        if (ioctl(fd, BIOCSETF, &pf) == -1)
                goto eexit;
-
-       if (iface->fd != -1)
-               close(iface->fd);
-
        close_on_exec(fd);
-       iface->fd = fd;
-
+       if (*fdp != -1)
+               close(*fdp);
+       *fdp = fd;
        return fd;
 
 eexit:
@@ -132,7 +135,7 @@ eexit:
 }
 
 ssize_t
-send_raw_packet(const struct interface *iface, int type,
+send_raw_packet(const struct interface *iface, int protocol,
                const void *data, ssize_t len)
 {
        struct iovec iov[2];
@@ -140,7 +143,7 @@ send_raw_packet(const struct interface *iface, int type,
 
        memset(&hw, 0, sizeof(hw));
        memset(&hw.ether_dhost, 0xff, ETHER_ADDR_LEN);
-       hw.ether_type = htons(type);
+       hw.ether_type = htons(protocol);
        iov[0].iov_base = &hw;
        iov[0].iov_len = sizeof(hw);
        iov[1].iov_base = UNCONST(data);
@@ -150,19 +153,27 @@ send_raw_packet(const struct interface *iface, int type,
 }
 
 /* BPF requires that we read the entire buffer.
- * So we pass the buffer in the API so we can loop on >1 dhcp packet. */
+ * So we pass the buffer in the API so we can loop on >1 packet. */
 ssize_t
-get_packet(struct interface *iface, void *data, ssize_t len)
+get_raw_packet(struct interface *iface, _unused int protocol,
+              void *data, ssize_t len)
 {
+       int fd;
        struct bpf_hdr packet;
        struct ether_header hw;
        ssize_t bytes;
-       const unsigned char *payload, *d;
+       const unsigned char *payload;
+
+#ifdef ENABLE_ARP
+       if (procotol == ETHERTYPE_ARP)
+               fd = iface->arp_fd;
+       else
+#endif
+               fd = iface->fd;
 
        for (;;) {
                if (iface->buffer_len == 0) {
-                       bytes = read(iface->fd, iface->buffer,
-                                    iface->buffer_size);
+                       bytes = read(fd, iface->buffer, iface->buffer_size);
                        if (bytes == -1)
                                return errno == EAGAIN ? 0 : -1;
                        else if ((size_t)bytes < sizeof(packet))
@@ -178,20 +189,11 @@ get_packet(struct interface *iface, void *data, ssize_t len)
                if (iface->buffer_pos + packet.bh_caplen + packet.bh_hdrlen >
                    iface->buffer_len)
                        goto next; /* Packet beyond buffer, drop. */
-               memcpy(&hw, iface->buffer + packet.bh_hdrlen, sizeof(hw));
                payload = iface->buffer + packet.bh_hdrlen + sizeof(hw);
-               if (hw.ether_type == htons(ETHERTYPE_ARP)) {
-                       bytes = packet.bh_caplen - sizeof(hw);
-                       if (bytes > len)
-                               bytes = len;
-                       memcpy(data, payload, bytes);
-               } else if (valid_udp_packet(payload) >= 0) {
-                       bytes = get_udp_data(&d, payload);
-                       if (bytes > len)
-                               bytes = len;
-                       memcpy(data, d, bytes);
-               } else
-                       bytes = -1;
+               bytes = packet.bh_caplen - sizeof(hw);
+               if (bytes > len)
+                       bytes = len;
+               memcpy(data, payload, bytes);
 next:
                iface->buffer_pos += BPF_WORDALIGN(packet.bh_hdrlen +
                                                   packet.bh_caplen);
index d2cf24c5a451bd6fa1eb4504947fa96b46f96c60..31d33f243df4b4a7e5808c3640fd5c762bbf7616 100644 (file)
--- a/client.c
+++ b/client.c
 # define INFTIM                 -1
 #endif
 
-/* This is out mini timeout.
- * Basically we resend the last request every TIMEOUT_MINI seconds. */
-#define TIMEOUT_MINI            3
-/* Except for an infinite timeout. We keep adding TIMEOUT_MINI to
- * ourself until TIMEOUT_MINI_INF is reached. */
-#define TIMEOUT_MINI_INF        60
-
 #define STATE_INIT              0
-#define STATE_REQUESTING        1
-#define STATE_BOUND             2
-#define STATE_RENEWING          3
-#define STATE_REBINDING         4
-#define STATE_REBOOT            5
-#define STATE_RENEW_REQUESTED   6
-#define STATE_RELEASED          7
+#define STATE_DISCOVERING      1
+#define STATE_REQUESTING        2
+#define STATE_BOUND             3
+#define STATE_RENEWING          4
+#define STATE_REBINDING         5
+#define STATE_REBOOT            6
+#define STATE_RENEW_REQUESTED   7
+#define STATE_PROBING          8
+#define STATE_ANNOUNCING       9
+
+/* Constants taken from RFC 2131. 
+ * We multiply some numbers by 1000 so they are suitable for use in poll(). */
+#define T1                     0.5
+#define T2                     0.875
+#define DHCP_BASE              4 * 1000
+#define DHCP_RAND_MIN          -1 * 1000
+#define DHCP_RAND_MAX          1 * 1000
+#define DHCP_MAX               64 * 1000
 
 /* We should define a maximum for the NAK exponential backoff */ 
 #define NAKOFF_MAX              60
 /* Indexes for pollfds */
 #define POLLFD_SIGNAL           0
 #define POLLFD_IFACE            1 
+#define POLLFD_ARP             2
+
+/* These are really for IPV4LL, RFC 3927.
+ * We multiply some numbers by 1000 so they are suitable for use in poll(). */
+#define PROBE_WAIT              1 * 1000
+#define PROBE_NUM               3
+#define PROBE_MIN               1 * 1000
+#define PROBE_MAX               2 * 1000
+#define ANNOUNCE_WAIT           2 * 1000
+#define ANNOUNCE_NUM            2
+#define ANNOUNCE_INTERVAL       2 * 1000
+#define MAX_CONFLICTS          10
+#define RATE_LIMIT_INTERVAL    60
+#define DEFEND_INTERVAL                10
 
 struct if_state {
        int options;
        struct interface *interface;
-       struct dhcp_message *dhcp;
-       struct dhcp_message *old_dhcp;
+       struct dhcp_message *offer;
+       struct dhcp_message *new;
+       struct dhcp_message *old;
        struct dhcp_lease lease;
-       time_t start;
-       time_t last_sent;
-       time_t last_type;
+       struct timeval start;
+       struct timeval stop;
        int state;
+       int messages;
        long timeout;
        time_t nakoff;
        uint32_t xid;
        int socket;
-       int *pidfd;
+       int *pid_fd;
+       int signal_fd;
+#ifdef ENABLE_ARP
+       int probes;
+       int claims;
+       int conflicts;
+       time_t defend;
+       struct in_addr fail;
+#endif
 };
 
 struct dhcp_op {
@@ -173,7 +200,9 @@ daemonise(struct if_state *state, const struct options *options)
                case 0:
                        setsid();
                        /* Notify parent it's safe to exit as we've detached. */
+                       close(sidpipe[0]);
                        write(sidpipe[1], &buf, 1);
+                       close(sidpipe[1]);
                        close_fds();
                        break;
                default:
@@ -218,9 +247,9 @@ daemonise(struct if_state *state, const struct options *options)
 
        /* Done with the fd now */
        if (pid != 0) {
-               writepid(*state->pidfd, pid);
-               close(*state->pidfd);
-               *state->pidfd = -1;
+               writepid(*state->pid_fd, pid);
+               close(*state->pid_fd);
+               *state->pid_fd = -1;
        }
 
        sigprocmask(SIG_SETMASK, &old, NULL);
@@ -235,7 +264,6 @@ daemonise(struct if_state *state, const struct options *options)
        return -1;
 }
 
-
 #ifdef ENABLE_DUID
 #define THIRTY_YEARS_IN_SECONDS    946707779
 static size_t
@@ -280,74 +308,64 @@ get_duid(unsigned char *duid, const struct interface *iface)
        /* No file? OK, lets make one based on our interface */
        if (!(f = fopen(DUID, "w")))
                return 0;
-
        type = htons(1); /* DUI-D-LLT */
        memcpy(p, &type, 2);
        p += 2;
-
        hw = htons(iface->family);
        memcpy(p, &hw, 2);
        p += 2;
-
        /* time returns seconds from jan 1 1970, but DUID-LLT is
         * seconds from jan 1 2000 modulo 2^32 */
        t = time(NULL) - THIRTY_YEARS_IN_SECONDS;
        ul = htonl(t & 0xffffffff);
        memcpy(p, &ul, 4);
        p += 4;
-
        /* Finally, add the MAC address of the interface */
        memcpy(p, iface->hwaddr, iface->hwlen);
        p += iface->hwlen;
-
        len = p - duid;
-
        x = fprintf(f, "%s\n", hwaddr_ntoa(duid, len));
        fclose(f);
-
        /* Failed to write the duid? scrub it, we cannot use it */
        if (x < 1) {
                len = 0;
                unlink(DUID);
        }
-
        return len;
 }
 #endif
 
 #ifdef ENABLE_IPV4LL
-static void
-ipv4ll_lease(struct dhcp_lease *lease)
+static struct dhcp_message*
+ipv4ll_get_dhcp(uint32_t old_addr)
 {
-       lease->leasetime = IPV4LL_LEASETIME;
-       lease->renewaltime = lease->leasetime * 0.5;
-       lease->rebindtime = lease->leasetime * 0.875;
-}
+       uint32_t u32;
+       struct dhcp_message *dhcp;
+       uint8_t *p;
 
-static int
-ipv4ll_get_address(struct interface *iface, struct dhcp_lease *lease) {
-       struct in_addr addr;
-       struct in_addr old;
-       int conflicts = 0;
+       dhcp = xzalloc(sizeof(*dhcp));
+       /* Put some LL options in */
+       p = dhcp->options;
+       *p++ = DHCP_SUBNETMASK;
+       *p += sizeof(u32);
+       u32 = LINKLOCAL_MASK;
+       memcpy(p, &u32, sizeof(u32));
+       p += sizeof(u32);
+       *p++ = DHCP_BROADCAST;
+       *p += sizeof(u32);
+       u32 = LINKLOCAL_BRDC;
+       memcpy(p, &u32, sizeof(u32));
+       p += sizeof(u32);
+       *p++ = DHCP_END;
 
-       old.s_addr = 0;
        for (;;) {
-               addr.s_addr = htonl(LINKLOCAL_ADDR |
-                                   (((uint32_t)abs((int)arc4random())
-                                     % 0xFD00) + 0x0100));
-               if (addr.s_addr == old.s_addr)
-                       continue;
-               old.s_addr = addr.s_addr;
-               if (!arp_claim(iface, addr))
+               dhcp->yiaddr = htonl(LINKLOCAL_ADDR |
+                                    (((uint32_t)abs((int)arc4random())
+                                      % 0xFD00) + 0x0100));
+               if (dhcp->yiaddr != old_addr)
                        break;
-               if (errno != EEXIST)
-                       return -1;
-               if (++conflicts >= MAX_CONFLICTS)
-                       return -1;
        }
-       lease->addr.s_addr = addr.s_addr;
-       ipv4ll_lease(lease);
-       return 0;
+       return dhcp;
 }
 #endif
 
@@ -357,14 +375,6 @@ get_lease(struct dhcp_lease *lease, const struct dhcp_message *dhcp)
        lease->frominfo = 0;
        lease->addr.s_addr = dhcp->yiaddr;
 
-#ifdef ENABLE_IPV4LL
-       if (IN_LINKLOCAL(ntohl(dhcp->yiaddr))) {
-               lease->net.s_addr = LINKLOCAL_MASK;
-               ipv4ll_lease(lease);
-               return;
-       }
-#endif
-
        if (get_option_addr(&lease->net.s_addr, dhcp, DHCP_SUBNETMASK) == -1)
                lease->net.s_addr = get_netmask(dhcp->yiaddr);
        if (get_option_uint32(&lease->leasetime, dhcp, DHCP_LEASETIME) != 0)
@@ -376,7 +386,7 @@ get_lease(struct dhcp_lease *lease, const struct dhcp_message *dhcp)
 }
 
 static int
-get_old_lease(struct if_state *state, const struct options *options)
+get_old_lease(struct if_state *state)
 {
        struct interface *iface = state->interface;
        struct dhcp_lease *lease = &state->lease;
@@ -405,61 +415,42 @@ get_old_lease(struct if_state *state, const struct options *options)
        state->lease.server.s_addr = 0;
        dhcp->servername[0] = '\0';
 
-#ifdef ENABLE_ARP
-       /* Check that no-one is using the address */
-       if (options->options & DHCPCD_ARP &&
-           (options->options & DHCPCD_LASTLEASE ||
-            (options->options & DHCPCD_IPV4LL &&
-             IN_LINKLOCAL(ntohl(dhcp->yiaddr))))) {
-               if (arp_claim(iface, lease->addr) == -1)
-                       goto eexit;
-       }
-
-       /* Ok, lets use this */
-       if (IN_LINKLOCAL(ntohl(dhcp->yiaddr))) {
-               if (options->options & DHCPCD_IPV4LL) {
-                       free(state->old_dhcp);
-                       state->old_dhcp = state->dhcp;
-                       state->dhcp = dhcp;
-                       return 0;
-               }
-               goto eexit;
-       }
-#endif
-
+       if (!IN_LINKLOCAL(ntohl(dhcp->yiaddr))) {
 #ifndef THERE_IS_NO_FORK
-       if (!(state->options & DHCPCD_LASTLEASE))
-               goto eexit;
+               if (!(state->options & DHCPCD_LASTLEASE))
+                       goto eexit;
 #endif
 
-       /* Ensure that we can still use the lease */
-       if (gettimeofday(&tv, NULL) == -1) {
-               logger(LOG_ERR, "gettimeofday: %s", strerror(errno));
-               goto eexit;
-       }
-
-       offset = tv.tv_sec - lease->leasedfrom;
-       if (lease->leasedfrom &&
-           tv.tv_sec - lease->leasedfrom > lease->leasetime)
-       {
-               logger(LOG_ERR, "lease expired %u seconds ago",
-                      offset + lease->leasetime);
-               /* Persistent interfaces should still try and use the lease
-                * if we can't contact a DHCP server. We just set the timeout
-                * to 1 second. */
-               if (state->options & DHCPCD_PERSISTENT)
-                       offset = lease->renewaltime - 1;
-               else
+               /* Ensure that we can still use the lease */
+               if (gettimeofday(&tv, NULL) == -1) {
+                       logger(LOG_ERR, "gettimeofday: %s", strerror(errno));
                        goto eexit;
+               }
+
+               offset = tv.tv_sec - lease->leasedfrom;
+               if (lease->leasedfrom &&
+                   tv.tv_sec - lease->leasedfrom > lease->leasetime)
+               {
+                       logger(LOG_ERR, "lease expired %u seconds ago",
+                              offset + lease->leasetime);
+                       /* Persistent interfaces should still try and use the
+                        * lease if we can't contact a DHCP server.
+                        * We just set the timeout to 1 second. */
+                       if (state->options & DHCPCD_PERSISTENT)
+                               offset = lease->renewaltime - 1;
+                       else
+                               goto eexit;
+               }
        }
 
        if (lease->leasedfrom == 0)
                offset = 0;
        state->timeout = lease->renewaltime - offset;
        iface->start_uptime = uptime();
-       free(state->old_dhcp);
-       state->old_dhcp = state->dhcp;
-       state->dhcp = dhcp;
+       free(state->old);
+       state->old = state->new;
+       state->new = NULL;
+       state->offer = dhcp;
        return 0;
 
 eexit:
@@ -482,7 +473,6 @@ client_setup(struct if_state *state, const struct options *options)
 #endif
 
        state->state = STATE_INIT;
-       state->last_type = DHCP_DISCOVER;
        state->nakoff = 1;
        state->options = options->options;
 
@@ -491,7 +481,7 @@ client_setup(struct if_state *state, const struct options *options)
             options->options & DHCPCD_REQUEST ||
             options->options & DHCPCD_DAEMONISED))
        {
-               if (get_old_lease(state, options) != 0)
+               if (get_old_lease(state) != 0)
                        return -1;
                state->timeout = 0;
 
@@ -659,8 +649,7 @@ send_message(struct if_state *state, int type, const struct options *options)
 
        logger(LOG_DEBUG, "sending %s with xid 0x%x",
               get_dhcp_op(type), state->xid);
-       state->last_type = type;
-       state->last_sent = uptime();
+       state->messages++;
        len = make_message(&dhcp, state->interface, &state->lease, state->xid,
                           type, options);
        from.s_addr = dhcp->ciaddr;
@@ -686,100 +675,76 @@ send_message(struct if_state *state, int type, const struct options *options)
 static void
 drop_config(struct if_state *state, const char *reason, const struct options *options)
 {
-       configure(state->interface, reason, NULL, state->dhcp,
+       configure(state->interface, reason, NULL, state->new,
                  &state->lease, options, 0);
-       free(state->old_dhcp);
-       state->old_dhcp = NULL;
-       free(state->dhcp);
-       state->dhcp = NULL;
+       free(state->old);
+       state->old = NULL;
+       free(state->new);
+       state->new = NULL;
 
        state->lease.addr.s_addr = 0;
 }
 
 static int
-wait_for_packet(struct pollfd *fds, struct if_state *state,
-               const struct options *options)
+wait_for_packet(struct if_state *state)
 {
-       struct dhcp_lease *lease = &state->lease;
-       struct interface *iface = state->interface;
-       int timeout = 0;
-       int retval = 0;
-
-       if (!(state->timeout > 0 ||
-             (options->timeout == 0 &&
-              (state->state != STATE_INIT || state->xid)))) {
-               /* We need to zero our signal fd, otherwise we will block
-                * trying to read a signal. */
-               fds[POLLFD_SIGNAL].revents = 0;
-               return 0;
-       }
+       struct pollfd fds[3]; /* iface, arp, signal */
+       int retval, timeout, nfds = 0;
+       time_t start, now;
+       struct timeval n, a, b;
 
-       fds[POLLFD_IFACE].fd = iface->fd;
+       /* We always listen to signals */
+       fds[nfds].fd = state->signal_fd;
+       fds[nfds].events = POLLIN;
+       nfds++;
 
-       if ((options->timeout == 0 && state->xid) ||
-           (lease->leasetime == ~0U &&
-            state->state == STATE_BOUND))
-       {
+       if (state->lease.leasetime == ~0U && state->state == STATE_BOUND) {
                logger(LOG_DEBUG, "waiting for infinity");
-               while (retval == 0)     {
-                       if (iface->fd == -1)
-                               retval = poll(fds, 1, INFTIM);
-                       else {
-                               /* Slow down our requests */
-                               if (timeout < TIMEOUT_MINI_INF)
-                                       timeout += TIMEOUT_MINI;
-                               else if (timeout > TIMEOUT_MINI_INF)
-                                       timeout = TIMEOUT_MINI_INF;
-
-                               retval = poll(fds, 2, timeout * 1000);
-                               if (retval == -1 && errno == EINTR) {
-                                       /* If interupted, continue as normal as
-                                        * the signal will be delivered down
-                                        * the pipe */
-                                       retval = 0;
-                                       continue;
-                               }
-                               if (retval == 0)
-                                       send_message(state, state->last_type,
-                                                    options);
+               timeout = INFTIM;
+       } else {
+               timeout = state->timeout;
+               if (state->stop.tv_sec || state->stop.tv_usec) {
+                       get_time(&n);
+                       a.tv_sec = state->timeout / 1000;
+                       a.tv_usec = (state->timeout % 1000) * 1000;
+                       timeradd(&n, &a, &b);
+                       if (timercmp(&state->stop, &b, <)) {
+                               timersub(&state->stop, &n, &b);
+                               timeout = b.tv_sec * 1000 + b.tv_usec / 1000;
                        }
                }
 
-               return retval;
-       }
-
-       /* Resend our message if we're getting loads of packets.
-        * As we use BPF or LPF, we shouldn't hit this as much, but it's
-        * still nice to have. */
-       if (iface->fd > -1 && uptime() - state->last_sent >= TIMEOUT_MINI)
-               send_message(state, state->last_type, options);
-
-       logger(LOG_DEBUG, "waiting for %lu seconds", state->timeout);
-       /* If we're waiting for a reply, then we re-send the last
-        * DHCP request periodically in-case of a bad line */
-       retval = 0;
-       while (state->timeout > 0 && retval == 0) {
-               if (iface->fd == -1)
-                       timeout = (int)state->timeout;
-               else {
-                       timeout = TIMEOUT_MINI;
-                       if (state->timeout < timeout)
-                               timeout = (int)state->timeout;
+               if (timeout == 0)
+                       return 0;
+               if (state->interface->fd != -1) {
+                       fds[nfds].fd = state->interface->fd;
+                       fds[nfds].events = POLLIN;
+                       nfds++;
                }
-               timeout *= 1000;
-               state->start = uptime();
-               retval = poll(fds, iface->fd == -1 ? 1 : 2, timeout);
-               state->timeout -= uptime() - state->start;
-               if (retval == -1 && errno == EINTR) {
-                       /* If interupted, continue as normal as the signal
-                        * will be delivered down the pipe */
-                       retval = 0;
-                       continue;
+#ifdef ENABLE_ARP
+               if (state->interface->arp_fd != -1) {
+                       fds[nfds].fd = state->interface->arp_fd;
+                       fds[nfds].events = POLLIN;
+                       nfds++;
                }
-               if (retval == 0 && iface->fd != -1 && state->timeout > 0)
-                       send_message(state, state->last_type, options);
+#endif
+               logger(LOG_DEBUG, "waiting for %0.3f seconds",
+                      (float)timeout / 1000);
        }
 
+       start = uptime();
+       retval = poll(fds, nfds, timeout);
+       if (timeout != INFTIM) {
+               now = uptime();
+               state->timeout -= now - start;
+               if (state->timeout < 0)
+                       state->timeout = 0;
+       }
+       if (retval == -1) {
+               if (errno == EINTR)
+                       return 0;
+               logger(LOG_ERR, "poll: %s", strerror(errno));
+       }
        return retval;
 }
 
@@ -804,16 +769,16 @@ handle_signal(int sig, struct if_state *state,  const struct options *options)
                case STATE_BOUND:
                case STATE_RENEWING:
                case STATE_REBINDING:
+               case STATE_ANNOUNCING:
                        state->state = STATE_RENEW_REQUESTED;
                        break;
                case STATE_RENEW_REQUESTED:
                case STATE_REQUESTING:
-               case STATE_RELEASED:
                        state->state = STATE_INIT;
                        break;
                }
+               timerclear(&state->stop);
                state->timeout = 0;
-               state->xid = 0;
                return 0;
 
        case SIGHUP:
@@ -845,18 +810,134 @@ handle_signal(int sig, struct if_state *state,  const struct options *options)
        return -1;
 }
 
+static int bind_dhcp(struct if_state *state, const struct options *options)
+{
+       struct interface *iface = state->interface;
+       struct dhcp_lease *lease = &state->lease;
+       const char *reason = NULL;
+       struct timeval tv;
+       int retval;
+
+       free(state->old);
+       state->old = state->new;
+       state->new = state->offer;
+       state->offer = NULL;
+#ifdef ENABLE_ARP
+       state->conflicts = 0;
+       state->defend = 0;
+#endif
+
+       if (options->options & DHCPCD_INFORM) {
+               if (options->request_address.s_addr != 0)
+                       lease->addr.s_addr = options->request_address.s_addr;
+               else
+                       lease->addr.s_addr = iface->addr.s_addr;
+
+               logger(LOG_INFO, "received approval for %s",
+                      inet_ntoa(lease->addr));
+               state->timeout = options->leasetime;
+               if (state->timeout == 0)
+                       state->timeout = DEFAULT_LEASETIME;
+               state->state = STATE_INIT;
+               reason = "INFORM";
+       } else if (IN_LINKLOCAL(htonl(lease->addr.s_addr))) {
+               if (lease->addr.s_addr != iface->addr.s_addr)
+                       logger(LOG_INFO, "using IPv4LL address %s",
+                              inet_ntoa(lease->addr));
+               state->state = STATE_INIT;
+               state->timeout = 0;
+               reason = "IPV4LL";
+       } else {
+               if (gettimeofday(&tv, NULL) == 0)
+                       lease->leasedfrom = tv.tv_sec;
+
+               get_lease(lease, state->new);
+               if (lease->frominfo)
+                       reason = "TIMEOUT";
+
+               if (lease->leasetime == ~0U) {
+                       lease->renewaltime = lease->rebindtime = lease->leasetime;
+                       state->timeout = 1; /* So we wait for infinity */
+                       logger(LOG_INFO, "leased %s for infinity",
+                              inet_ntoa(lease->addr));
+                       state->state = STATE_BOUND;
+               } else {
+                       logger(LOG_INFO, "leased %s for %u seconds",
+                              inet_ntoa(lease->addr), lease->leasetime);
+
+                       if (lease->rebindtime >= lease->leasetime) {
+                               lease->rebindtime = (lease->leasetime * T2);
+                               logger(LOG_ERR,
+                                      "rebind time greater than lease "
+                                      "time, forcing to %u seconds",
+                                      lease->rebindtime);
+                       }
+
+                       if (lease->renewaltime > lease->rebindtime) {
+                               lease->renewaltime = (lease->leasetime * T1);
+                               logger(LOG_ERR,
+                                      "renewal time greater than rebind time, "
+                                      "forcing to %u seconds",
+                                      lease->renewaltime);
+                       }
+
+                       if (!lease->renewaltime) {
+                               lease->renewaltime = (lease->leasetime * T1);
+                               logger(LOG_INFO,
+                                      "no renewal time supplied, assuming %d seconds",
+                                      lease->renewaltime);
+                       } else
+                               logger(LOG_DEBUG, "renew in %u seconds",
+                                      lease->renewaltime);
+
+                       if (!lease->rebindtime) {
+                               lease->rebindtime = (lease->leasetime * T2);
+                               logger(LOG_INFO,
+                                      "no rebind time supplied, assuming %d seconds",
+                                      lease->rebindtime);
+                       } else
+                               logger(LOG_DEBUG, "rebind in %u seconds",
+                                      lease->rebindtime);
+
+                       state->timeout = lease->renewaltime * 1000;
+               }
+               state->state = STATE_BOUND;
+       }
+
+       state->xid = 0;
+       timerclear(&state->stop);
+       if (!reason) {
+               if (state->old) {
+                       if (state->old->yiaddr == state->new->yiaddr &&
+                           lease->server.s_addr)
+                               reason = "RENEW";
+                       else
+                               reason = "REBIND";
+               } else
+                       reason = "BOUND";
+       }
+       retval = configure(iface, reason, state->new, state->old,
+                          &state->lease, options, 1);
+       if (retval != 0)
+               return -1;
+       return daemonise(state, options);
+}
+
 static int
-handle_timeout(struct if_state *state, const struct options *options)
+handle_timeout_fail(struct if_state *state, const struct options *options)
 {
        struct dhcp_lease *lease = &state->lease;
        struct interface *iface = state->interface;
        int gotlease = -1;
        const char *reason = NULL;
+       struct timeval tv;
 
-       /* No NAK, so reset the backoff */
-       state->nakoff = 1;
+       timerclear(&tv);
 
-       if (state->state == STATE_INIT && state->xid != 0) {
+       switch (state->state) {
+       case STATE_DISCOVERING:
+               /* FALLTHROUGH */
+       case STATE_REQUESTING:
                if (IN_LINKLOCAL(ntohl(iface->addr.s_addr))) {
                        if (!(state->options & DHCPCD_DAEMONISED))
                                logger(LOG_ERR, "timed out");
@@ -867,86 +948,157 @@ handle_timeout(struct if_state *state, const struct options *options)
                        else
                                logger(LOG_ERR, "timed out");
                }
-
                do_socket(state, SOCKET_CLOSED);
-
                if (options->options & DHCPCD_INFORM ||
                    options->options & DHCPCD_TEST)
                        return -1;
 
                if (state->options & DHCPCD_IPV4LL ||
                    state->options & DHCPCD_LASTLEASE)
-               {
-                       errno = 0;
-                       gotlease = get_old_lease(state, options);
-                       if (gotlease == 0) {
-                               if (!(state->options & DHCPCD_DAEMONISED))
-                                       reason = "REBOOT";
-                       } else if (errno == EINTR)
-                               return 0;
-               }
+                       gotlease = get_old_lease(state);
 
 #ifdef ENABLE_IPV4LL
                if (state->options & DHCPCD_IPV4LL && gotlease != 0) {
                        logger(LOG_INFO, "probing for an IPV4LL address");
-                       gotlease = ipv4ll_get_address(iface, lease);
-                       if (gotlease != 0) {
-                               if (errno == EINTR)
-                                       return 0;
-                               logger(LOG_ERR, "failed to find a free IPV4LL"
-                                               " address");
-                       } else {
-                               free(state->old_dhcp);
-                               state->old_dhcp = state->dhcp;
-                               state->dhcp = xmalloc(sizeof(*state->dhcp));
-                               memset(state->dhcp, 0, sizeof(*state->dhcp));
-                               state->dhcp->yiaddr = lease->addr.s_addr;
-                               state->dhcp->options[0] = DHCP_END;
-                               state->lease.frominfo = 0;
-                               reason = "IPV4LL";
-                       }
+                       free(state->offer);
+                       state->offer = ipv4ll_get_dhcp(0);
+                       gotlease = 0;
                }
 #endif
 
-               if (gotlease != 0) {
-                       if (state->dhcp && !IN_LINKLOCAL(state->dhcp->yiaddr))
-                               reason = "EXPIRE";
-                       if (!reason)
-                               reason = "FAIL";
-                       drop_config(state, reason, options);
-                       if (!(state->options & DHCPCD_DAEMONISED))
-                               return -1;
+#ifdef ENABLE_ARP
+               if (gotlease == 0 &&
+                   state->offer->yiaddr != iface->addr.s_addr)
+               {
+                       state->state = STATE_PROBING;
+                       state->timeout = 0;
+                       state->claims = 0;
+                       state->probes = 0;
+                       state->conflicts = 0;
+                       timerclear(&state->stop);
+                       return 0;
+               }
+#endif
+
+               if (gotlease == 0)
+                       return bind_dhcp(state, options);
+
+               if (state->new && !IN_LINKLOCAL(state->new->yiaddr))
+                       reason = "EXPIRE";
+               else
+                       reason = "FAIL";
+               drop_config(state, reason, options);
+               if (!(state->options & DHCPCD_DAEMONISED))
+                       return -1;
+               break;
+       case STATE_RENEWING:
+               logger(LOG_ERR, "failed to renew, attempting to rebind");
+               lease->addr.s_addr = 0;
+               state->state = STATE_REBINDING;
+               tv.tv_sec = lease->rebindtime - lease->renewaltime;
+               break;
+       case STATE_REBINDING:
+               logger(LOG_ERR, "failed to rebind, attempting to discover");
+               state->state = STATE_INIT;
+               break;
+       default:
+               logger(LOG_DEBUG, "handle_timeout_failed: invalid state %d",
+                      state->state);
+       }
+
+       get_time(&state->start);
+       tv.tv_usec = 0;
+       if (tv.tv_sec != 0)
+               timeradd(&state->start, &tv, &state->stop);
+
+       /* This effectively falls through into the handle_timeout funtion */
+       state->timeout = 0;
+       state->messages = 0;
+       return 0;
+}
+
+static int
+handle_timeout(struct if_state *state, const struct options *options)
+{
+       struct dhcp_lease *lease = &state->lease;
+       struct interface *iface = state->interface;
+       int i;
+       struct timeval tv;
+
+#ifdef ENABLE_ARP
+       switch (state->state) {
+       case STATE_PROBING:
+               timerclear(&state->stop);
+               if (iface->arp_fd == -1)
+                       open_socket(iface, ETHERTYPE_ARP);
+               if (state->probes < PROBE_NUM) {
+                       state->probes++;
+                       logger(LOG_DEBUG, "sending ARP probe #%d",
+                              state->probes);
+                       if (state->probes < PROBE_NUM) 
+                               state->timeout = (arc4random() %
+                                                 (PROBE_MAX - PROBE_MIN)) + PROBE_MIN;
+                       else
+                               state->timeout = ANNOUNCE_WAIT;
+                       send_arp(iface, ARPOP_REQUEST, 0, state->offer->yiaddr);
+                       return 0;
+               } else {
+                       /* We've waited for ANNOUNCE_WAIT after the final probe
+                        * so the address is now ours */
+                       i = bind_dhcp(state, options);
+                       state->state = STATE_ANNOUNCING;
+                       state->timeout = ANNOUNCE_INTERVAL;
+                       return i;
+               }
+       case STATE_ANNOUNCING:
+               timerclear(&state->stop);
+               if (state->claims < ANNOUNCE_NUM) {
+                       state->claims++;
+                       logger(LOG_DEBUG, "sending ARP announce #%d",
+                              state->claims);
+                       send_arp(iface, ARPOP_REQUEST,
+                                state->new->yiaddr, state->new->yiaddr);
+                       if (state->claims < ANNOUNCE_NUM)
+                               state->timeout = ANNOUNCE_INTERVAL;
+                       else if (IN_LINKLOCAL(htonl(lease->addr.s_addr))) {
+                               state->state = STATE_INIT;
+                               state->timeout = 0;
+                       } else {
+                               state->state = STATE_BOUND;
+                               state->timeout = lease->renewaltime * 1000 -
+                                       (ANNOUNCE_INTERVAL * ANNOUNCE_NUM);
+                               close(iface->arp_fd);
+                               iface->arp_fd = -1;
+                       }
                }
-               if (!(state->options & DHCPCD_DAEMONISED) &&
-                   IN_LINKLOCAL(ntohl(lease->addr.s_addr)))
-                       logger(LOG_WARNING, "using IPV4LL address %s",
-                              inet_ntoa(lease->addr));
-               state->timeout = lease->renewaltime;
-               state->xid = 0;
-               state->state = STATE_BOUND;
-               if (!reason)
-                       reason = "TIMEOUT";
-               if (configure(iface, reason,
-                             state->dhcp, state->old_dhcp,
-                             lease, options, 1) == 0)
-                       if (daemonise(state, options) == -1)
-                               return -1;
                return 0;
        }
+#endif
+
+       if (timerisset(&state->stop)) {
+               get_time(&tv);
+               if (timercmp(&tv, &state->stop, >))
+                       return handle_timeout_fail(state, options);
+       }
 
        switch (state->state) {
        case STATE_INIT:
-               state->xid = arc4random();
                do_socket(state, SOCKET_OPEN);
-               state->timeout = options->timeout;
-               iface->start_uptime = uptime ();
-               if (lease->addr.s_addr == 0) {
-#ifdef ENABLE_IPV4LL
-                       if (IN_LINKLOCAL(ntohl(iface->addr.s_addr)))
-                               state->timeout = DEFEND_INTERVAL;
-#endif
+               state->xid = arc4random();
+               state->messages = 0;
+               iface->start_uptime = uptime();
+               if (!IN_LINKLOCAL(htonl(iface->addr.s_addr))) {
+                       get_time(&state->start);
+                       tv.tv_sec = options->timeout;
+                       tv.tv_usec = 0;
+                       timeradd(&state->start, &tv, &state->stop);
+               }
+               if (lease->addr.s_addr == 0 ||
+                   IN_LINKLOCAL(ntohl(iface->addr.s_addr)))
+               {
                        logger(LOG_INFO, "broadcasting for a lease");
-                       send_message (state, DHCP_DISCOVER, options);
+                       send_message(state, DHCP_DISCOVER, options);
+                       state->state = STATE_DISCOVERING;
                } else if (state->options & DHCPCD_INFORM) {
                        logger(LOG_INFO, "broadcasting inform for %s",
                               inet_ntoa(lease->addr));
@@ -958,48 +1110,55 @@ handle_timeout(struct if_state *state, const struct options *options)
                        send_message(state, DHCP_REQUEST, options);
                        state->state = STATE_REQUESTING;
                }
-
                break;
-       case STATE_BOUND:
+       case STATE_DISCOVERING:
+               send_message(state, DHCP_DISCOVER, options);
+               break;
        case STATE_RENEW_REQUESTED:
+               do_socket(state, SOCKET_OPEN);
+               state->xid = arc4random();
+               state->messages = 0;
+               iface->start_uptime = uptime();
+               /* FALLTHROUGH */
+       case STATE_REQUESTING:
+               send_message(state, DHCP_REQUEST, options);
+               break;
+       case STATE_BOUND:
+               do_socket(state, SOCKET_OPEN);
                if (IN_LINKLOCAL(ntohl(lease->addr.s_addr))) {
                        lease->addr.s_addr = 0;
                        state->state = STATE_INIT;
                        state->xid = 0;
                        break;
                }
+               logger(LOG_INFO, "renewing lease of %s",inet_ntoa(lease->addr));
                state->state = STATE_RENEWING;
                state->xid = arc4random();
+               state->messages = 0;
+               state->nakoff = 1;
+               iface->start_uptime = uptime();
+               get_time(&state->start);
+               tv.tv_sec = lease->rebindtime - lease->renewaltime;
+               tv.tv_usec = 0;
+               timeradd(&state->start, &tv, &state->stop);
                /* FALLTHROUGH */
        case STATE_RENEWING:
-               iface->start_uptime = uptime();
-               logger(LOG_INFO, "renewing lease of %s",inet_ntoa(lease->addr));
-               do_socket(state, SOCKET_OPEN);
-               send_message(state, DHCP_REQUEST, options);
-               state->timeout = lease->rebindtime - lease->renewaltime;
-               state->state = STATE_REBINDING;
-               break;
+               /* FALLTHROUGH */
        case STATE_REBINDING:
-               logger(LOG_ERR, "lost lease, attemping to rebind");
-               lease->addr.s_addr = 0;
-               do_socket(state, SOCKET_OPEN);
-               if (state->xid == 0)
-                       state->xid = arc4random();
-               lease->server.s_addr = 0;
                send_message(state, DHCP_REQUEST, options);
-               state->timeout = lease->leasetime - lease->rebindtime;
-               state->state = STATE_REQUESTING;
-               break;
-       case STATE_REQUESTING:
-               state->state = STATE_INIT;
-               do_socket(state, SOCKET_CLOSED);
-               state->timeout = 0;
-               break;
-       case STATE_RELEASED:
-               lease->leasetime = 0;
                break;
        }
 
+       state->timeout = DHCP_BASE;
+       for (i = 1; i < state->messages; i++) {
+               state->timeout *= 2;
+               if (state->timeout > DHCP_MAX) {
+                       state->timeout = DHCP_MAX;
+                       break;
+               }
+       }
+       state->timeout += (arc4random() % (DHCP_RAND_MAX - DHCP_RAND_MIN)) +
+               DHCP_RAND_MIN;
        return 0;
 }
 
@@ -1014,9 +1173,7 @@ handle_dhcp(struct if_state *state, struct dhcp_message **dhcpp,
        char *addr;
        struct in_addr saddr;
        uint8_t type;
-       struct timeval tv;
        int r;
-       const char *reason = NULL;
 
        if (get_option_uint8(&type, dhcp, DHCP_MESSAGETYPE) == -1) {
                logger(LOG_ERR, "no DHCP type in message");
@@ -1030,8 +1187,6 @@ handle_dhcp(struct if_state *state, struct dhcp_message **dhcpp,
                free(addr);
                state->state = STATE_INIT;
                state->timeout = 0;
-               state->xid = 0;
-               lease->addr.s_addr = 0;
 
                /* If we constantly get NAKS then we should slowly back off */
                if (state->nakoff > 0) {
@@ -1051,7 +1206,7 @@ handle_dhcp(struct if_state *state, struct dhcp_message **dhcpp,
        /* No NAK, so reset the backoff */
        state->nakoff = 1;
 
-       if (type == DHCP_OFFER && state->state == STATE_INIT) {
+       if (type == DHCP_OFFER && state->state == STATE_DISCOVERING) {
                lease->addr.s_addr = dhcp->yiaddr;
                addr = xstrdup(inet_ntoa(lease->addr));
                r = get_option_addr(&lease->server.s_addr, dhcp, DHCP_SERVERID);
@@ -1073,9 +1228,8 @@ handle_dhcp(struct if_state *state, struct dhcp_message **dhcpp,
                }
 
                free(dhcp);
-               send_message(state, DHCP_REQUEST, options);
                state->state = STATE_REQUESTING;
-
+               state->timeout = 0;
                return 0;
        }
 
@@ -1099,151 +1253,69 @@ handle_dhcp(struct if_state *state, struct dhcp_message **dhcpp,
        case STATE_REQUESTING:
        case STATE_RENEWING:
        case STATE_REBINDING:
+               saddr.s_addr = dhcp->yiaddr;
+               logger(LOG_INFO, "lease of %s acknowledged",
+                      inet_ntoa(saddr));
                break;
        default:
                logger(LOG_ERR, "wrong state %d", state->state);
        }
 
        do_socket(state, SOCKET_CLOSED);
+       free(state->offer);
+       state->offer = dhcp;
+       *dhcpp = NULL;
 
 #ifdef ENABLE_ARP
-       if (options->options & DHCPCD_ARP &&
-           iface->addr.s_addr != lease->addr.s_addr)
+       if (state->options & DHCPCD_ARP &&
+           iface->addr.s_addr != state->offer->yiaddr)
        {
-               if (arp_claim(iface, lease->addr) && errno != EINTR) {
-                       free(dhcp);
-                       do_socket(state, SOCKET_OPEN);
-                       send_message(state, DHCP_DECLINE, options);
-                       do_socket(state, SOCKET_CLOSED);
-
-                       state->xid = 0;
-                       state->timeout = 0;
-                       state->state = STATE_INIT;
-                       state->lease.addr.s_addr = 0;
-
-                       /* RFC 2131 says that we should wait for 10 seconds
-                        * before doing anything else */
-                       logger(LOG_INFO, "sleeping for 10 seconds");
-                       ts.tv_sec = 10;
-                       ts.tv_nsec = 0;
-                       nanosleep(&ts, NULL);
-                       return 0;
-               } else if (errno == EINTR) {
-                       free(dhcp);
-                       return 0;
-               }
+               state->state = STATE_PROBING;
+               state->timeout = 0;
+               state->claims = 0;
+               state->probes = 0;
+               state->conflicts = 0;
+               timerclear(&state->stop);
+               return 0;
        }
 #endif
 
-       free(state->old_dhcp);
-       state->old_dhcp = state->dhcp;
-       state->dhcp = dhcp;
-       *dhcpp = NULL;
-
-       if (options->options & DHCPCD_INFORM) {
-               if (options->request_address.s_addr != 0)
-                       lease->addr.s_addr = options->request_address.s_addr;
-               else
-                       lease->addr.s_addr = iface->addr.s_addr;
-
-               logger(LOG_INFO, "received approval for %s",
-                      inet_ntoa(lease->addr));
-               state->timeout = options->leasetime;
-               if (state->timeout == 0)
-                       state->timeout = DEFAULT_LEASETIME;
-               state->state = STATE_INIT;
-               reason = "INFORM";
-       } else {
-               if (gettimeofday(&tv, NULL) == 0)
-                       lease->leasedfrom = tv.tv_sec;
-
-               get_lease(lease, dhcp);
-
-               if (lease->leasetime == ~0U) {
-                       lease->renewaltime = lease->rebindtime = lease->leasetime;
-                       state->timeout = 1; /* So we wait for infinity */
-                       logger(LOG_INFO, "leased %s for infinity",
-                              inet_ntoa(lease->addr));
-                       state->state = STATE_BOUND;
-               } else {
-                       logger(LOG_INFO, "leased %s for %u seconds",
-                              inet_ntoa(lease->addr), lease->leasetime);
-
-                       if (lease->rebindtime >= lease->leasetime) {
-                               lease->rebindtime = (lease->leasetime * 0.875);
-                               logger(LOG_ERR,
-                                      "rebind time greater than lease "
-                                      "time, forcing to %u seconds",
-                                      lease->rebindtime);
-                       }
-
-                       if (lease->renewaltime > lease->rebindtime) {
-                               lease->renewaltime = (lease->leasetime * 0.5);
-                               logger(LOG_ERR,
-                                      "renewal time greater than rebind time, "
-                                      "forcing to %u seconds",
-                                      lease->renewaltime);
-                       }
-
-                       if (!lease->renewaltime) {
-                               lease->renewaltime = (lease->leasetime * 0.5);
-                               logger(LOG_INFO,
-                                      "no renewal time supplied, assuming %d seconds",
-                                      lease->renewaltime);
-                       } else
-                               logger(LOG_DEBUG, "renew in %u seconds",
-                                      lease->renewaltime);
-
-                       if (!lease->rebindtime) {
-                               lease->rebindtime = (lease->leasetime * 0.875);
-                               logger(LOG_INFO,
-                                      "no rebind time supplied, assuming %d seconds",
-                                      lease->rebindtime);
-                       } else
-                               logger(LOG_DEBUG, "rebind in %u seconds",
-                                      lease->rebindtime);
-
-                       state->timeout = lease->renewaltime;
-               }
-
-               state->state = STATE_BOUND;
-       }
-
-       state->xid = 0;
-       if (!reason) {
-               if (state->old_dhcp) {
-                       if (state->old_dhcp->yiaddr == dhcp->yiaddr &&
-                           lease->server.s_addr)
-                               reason = "RENEW";
-                       else
-                               reason = "REBIND";
-               } else
-                       reason = "BOUND";
-       }       
-       r = configure(iface, reason, dhcp, state->old_dhcp,
-                     &state->lease, options, 1);
-       if (r != 0)
-               return -1;
-       return daemonise(state, options);
+       return bind_dhcp(state, options);               
 }
 
 static int
-handle_packet(struct if_state *state, const struct options *options)
+handle_dhcp_packet(struct if_state *state, const struct options *options)
 {
+       uint8_t *packet;
        struct interface *iface = state->interface;
        struct dhcp_message *dhcp;
+       const uint8_t *pp;
        uint8_t *p;
        ssize_t bytes;
+       int retval = -1;
 
        /* We loop through until our buffer is empty.
         * The benefit is that if we get >1 DHCP packet in our buffer and
         * the first one fails for any reason, we can use the next. */
+       packet = xmalloc(udp_dhcp_len);
        dhcp = xmalloc(sizeof(*dhcp));
        for(;;) {
-               memset(dhcp, 0, sizeof(*dhcp));
-               bytes = get_packet(iface, dhcp, sizeof(*dhcp));
-               if (bytes == -1 || bytes == 0)
+               bytes = get_raw_packet(iface, ETHERTYPE_IP,
+                                      packet, udp_dhcp_len);
+               if (bytes == 0) {
+                       retval = 0;
                        break;
+               }
+               if (bytes == -1)
+                       break;
+               if (valid_udp_packet(packet) == -1)
+                       continue;
+               bytes = get_udp_data(&pp, packet);
+               if ((size_t)bytes > sizeof(*dhcp)) {
+                       logger(LOG_ERR, "packet greater than DHCP size");
+                       continue;
+               }
+               memcpy(dhcp, pp, bytes);
                if (dhcp->cookie != htonl(MAGIC_COOKIE)) {
                        logger(LOG_DEBUG, "bogus cookie, ignoring");
                        continue;
@@ -1264,6 +1336,7 @@ handle_packet(struct if_state *state, const struct options *options)
                        if (*p != DHCP_END)
                                *++p = DHCP_END;
                }
+               free(packet);
                if (handle_dhcp(state, &dhcp, options) == 0) {
                        if (state->options & DHCPCD_TEST)
                                return -1;
@@ -1273,19 +1346,143 @@ handle_packet(struct if_state *state, const struct options *options)
                        return -1;
        }
 
+       free(packet);
        free(dhcp);
-       return -1;
+       return retval;
+}
+
+#ifdef ENABLE_ARP
+static int
+handle_arp_packet(struct if_state *state)
+{
+       struct arphdr reply;
+       uint32_t reply_s;
+       uint32_t reply_t;
+       uint8_t arp_reply[sizeof(reply) + 2 * sizeof(reply_s) + 2 * HWADDR_LEN];
+       uint8_t *hw_s, *hw_t;
+       ssize_t bytes;
+       struct interface *iface = state->interface;
+
+       state->fail.s_addr = 0;
+
+       for(;;) {
+               bytes = get_raw_packet(iface, ETHERTYPE_ARP,
+                                      arp_reply, sizeof(arp_reply));
+               if (bytes == 0 || bytes == -1)
+                       return (int)bytes;
+               /* We must have a full ARP header */
+               if ((size_t)bytes < sizeof(reply))
+                       continue;
+               memcpy(&reply, arp_reply, sizeof(reply));
+               /* Protocol must be IP. */
+               if (reply.ar_pro != htons(ETHERTYPE_IP))
+                       continue;
+               if (reply.ar_pln != sizeof(reply_s))
+                       continue;
+               /* Only these types are recognised */
+               if (reply.ar_op != htons(ARPOP_REPLY) &&
+                   reply.ar_op != htons(ARPOP_REQUEST))
+                       continue;
+
+               /* Get pointers to the hardware addreses */
+               hw_s = arp_reply + sizeof(reply);
+               hw_t = hw_s + reply.ar_hln + reply.ar_pln;
+               /* Ensure we got all the data */
+               if ((hw_t + reply.ar_hln + reply.ar_pln) - arp_reply > bytes)
+                       continue;
+               /* Copy out the IP addresses */
+               memcpy(&reply_s, hw_s + reply.ar_hln, reply.ar_pln);
+               memcpy(&reply_t, hw_t + reply.ar_hln, reply.ar_pln);
+
+               /* Check for conflict */
+               if (state->offer && 
+                   (reply_s == state->offer->yiaddr ||
+                    (reply_t == state->offer->yiaddr &&
+                     reply.ar_op == htons(ARPOP_REQUEST) &&
+                     (iface->hwlen != reply.ar_hln ||
+                      memcmp(hw_s, iface->hwaddr, iface->hwlen) != 0))))
+                       state->fail.s_addr = state->offer->yiaddr;
+
+               /* Handle IPv4LL conflicts */
+               if (IN_LINKLOCAL(htonl(iface->addr.s_addr)) &&
+                   (reply_s == iface->addr.s_addr ||
+                    (reply_t == iface->addr.s_addr &&
+                     reply.ar_op == htons(ARPOP_REQUEST) &&
+                     (iface->hwlen != reply.ar_hln ||
+                      memcmp(hw_s, iface->hwaddr, iface->hwlen) != 0))))
+                       state->fail.s_addr = iface->addr.s_addr;
+
+               if (state->fail.s_addr) {
+                       logger(LOG_ERR, "hardware address %s claims %s",
+                              hwaddr_ntoa((unsigned char *)hw_s,
+                                          (size_t)reply.ar_hln),
+                              inet_ntoa(state->fail));
+                       errno = EEXIST;
+                       return -1;
+               }
+       }
 }
 
+static int
+handle_arp_fail(struct if_state *state, const struct options *options)
+{
+       struct timespec ts;
+       time_t up;
+
+       if (IN_LINKLOCAL(htonl(state->fail.s_addr))) {
+               if (state->fail.s_addr == state->interface->addr.s_addr) {
+                       up = uptime();
+                       if (state->defend + DEFEND_INTERVAL > up) {
+                               drop_config(state, "FAIL", options);
+                               state->state = STATE_PROBING;
+                               state->timeout = 0;
+                               state->claims = 0;
+                               state->probes = 0;
+                               state->conflicts = 0;
+                               timerclear(&state->stop);
+                       } else
+                               state->defend = up;
+                       return 0;
+               }
+
+               timerclear(&state->stop);
+               state->conflicts++;
+               state->timeout = 0;
+               state->claims = 0;
+               state->probes = 0;
+               state->state = STATE_PROBING;
+               free(state->offer);
+               if (state->conflicts > MAX_CONFLICTS) {
+                       /* RFC 3927 says we should rate limit */
+                       logger(LOG_INFO, "sleeping for %d seconds",
+                              RATE_LIMIT_INTERVAL);
+                       ts.tv_sec = RATE_LIMIT_INTERVAL;
+                       ts.tv_nsec = 0;
+                       nanosleep(&ts, NULL);
+               }
+               state->offer = ipv4ll_get_dhcp(0);
+               return 0;
+       }
+
+       do_socket(state, SOCKET_OPEN);
+       send_message(state, DHCP_DECLINE, options);
+       state->timeout = 0;
+       state->state = STATE_INIT;
+       /* RFC 2131 says that we should wait for 10 seconds
+        * before doing anything else */
+       logger(LOG_INFO, "sleeping for 10 seconds");
+       ts.tv_sec = 10;
+       ts.tv_nsec = 0;
+       nanosleep(&ts, NULL);
+       return 0;
+}
+#endif
+
 int
-dhcp_run(const struct options *options, int *pidfd)
+dhcp_run(const struct options *options, int *pid_fd)
 {
        struct interface *iface;
        struct if_state *state = NULL;
-       struct pollfd fds[] = {
-               { -1, POLLIN, 0 },
-               { -1, POLLIN, 0 }
-       };
        int retval = -1;
        int sig;
 
@@ -1299,7 +1496,7 @@ dhcp_run(const struct options *options, int *pidfd)
               hwaddr_ntoa(iface->hwaddr, iface->hwlen));
 
        state = xzalloc(sizeof(*state));
-       state->pidfd = pidfd;
+       state->pid_fd = pid_fd;
        state->interface = iface;
 
        if (client_setup(state, options) == -1)
@@ -1309,25 +1506,32 @@ dhcp_run(const struct options *options, int *pidfd)
        if (signal_setup() == -1)
                goto eexit;
 
-       fds[POLLFD_SIGNAL].fd = signal_fd();
+       state->signal_fd = signal_fd();
+
        for (;;) {
-               retval = wait_for_packet(fds, state, options);
+               retval = wait_for_packet(state);
 
                /* We should always handle our signals first */
-               if ((sig = (signal_read(&fds[POLLFD_SIGNAL]))) != -1) {
+               if ((sig = (signal_read(state->signal_fd))) != -1) {
                        retval = handle_signal(sig, state, options);
                } else if (retval == 0)
                        retval = handle_timeout(state, options);
-               else if (retval > 0 &&
-                        state->socket != SOCKET_CLOSED &&
-                        fds[POLLFD_IFACE].revents & POLLIN)
-                       retval = handle_packet(state, options);
-               else if (retval == -1 && errno == EINTR) {
-                       /* The interupt will be handled above */
-                       retval = 0;
-               } else {
-                       logger(LOG_ERR, "poll: %s", strerror(errno));
-                       retval = -1;
+               else if (retval == -1) {
+                       if (errno == EINTR)
+                               /* The interupt will be handled above */
+                               retval = 0;
+               } else if (retval > 0) {
+                       if (fd_hasdata(state->interface->fd) == 1)
+                               retval = handle_dhcp_packet(state, options);
+#ifdef ENABLE_ARP
+                       else if (fd_hasdata(state->interface->arp_fd) == 1) {
+                               retval = handle_arp_packet(state);
+                               if (retval == -1)
+                                       retval = handle_arp_fail(state, options);
+                       }
+#endif
+                       else
+                               retval = 0;
                }
 
                if (retval != 0)
@@ -1348,8 +1552,9 @@ eexit:
                        retval = 0;
                if (state->options & DHCPCD_DAEMONISED)
                        unlink(options->pidfile);
-               free(state->dhcp);
-               free(state->old_dhcp);
+               free(state->offer);
+               free(state->new);
+               free(state->old);
                free(state);
        }
 
index 172eb88b44847b53073b875b9b08dec6a083ed4d..1860ce397cb5ba7615158d0f03e466fc88c03d8e 100644 (file)
--- a/dhcpcd.c
+++ b/dhcpcd.c
@@ -433,7 +433,7 @@ main(int argc, char **argv)
        pid_t pid;
        int debug = 0;
        int i, r;
-       int pidfd = -1;
+       int pid_fd = -1;
        int sig = 0;
        int retval = EXIT_FAILURE;
        char *line, *option, *p, *lp, *buffer = NULL;
@@ -810,9 +810,9 @@ main(int argc, char **argv)
                        goto abort;
                }
 
-               pidfd = open(options->pidfile,
+               pid_fd = open(options->pidfile,
                             O_WRONLY | O_CREAT | O_NONBLOCK, 0664);
-               if (pidfd == -1) {
+               if (pid_fd == -1) {
                        logger(LOG_ERR, "open `%s': %s",
                               options->pidfile, strerror(errno));
                        goto abort;
@@ -820,24 +820,24 @@ main(int argc, char **argv)
 
                /* Lock the file so that only one instance of dhcpcd runs
                 * on an interface */
-               if (flock(pidfd, LOCK_EX | LOCK_NB) == -1) {
+               if (flock(pid_fd, LOCK_EX | LOCK_NB) == -1) {
                        logger(LOG_ERR, "flock `%s': %s",
                               options->pidfile, strerror(errno));
                        goto abort;
                }
 
-               close_on_exec(pidfd);
-               writepid(pidfd, getpid());
+               close_on_exec(pid_fd);
+               writepid(pid_fd, getpid());
                logger(LOG_INFO, PACKAGE " " VERSION " starting");
        }
 
-       if (dhcp_run(options, &pidfd) == 0)
+       if (dhcp_run(options, &pid_fd) == 0)
                retval = EXIT_SUCCESS;
 
 abort:
        /* If we didn't daemonise then we need to punt the pidfile now */
-       if (pidfd > -1) {
-               close(pidfd);
+       if (pid_fd > -1) {
+               close(pid_fd);
                unlink(options->pidfile);
        }
        if (options->environ) {
diff --git a/lpf.c b/lpf.c
index 87e6460a441907f7310319ff79996e3805c73d8d..c507378f5738f969b4aadba8d05bac97a89536c0 100644 (file)
--- a/lpf.c
+++ b/lpf.c
@@ -57,9 +57,6 @@
 #include "net.h"
 #include "bpf-filter.h"
 
-/* A suitably large buffer for all transactions. */
-#define BUFFER_LENGTH 4096
-
 /* Broadcast address for IPoIB */
 static const uint8_t ipv4_bcast_addr[] = {
        0x00, 0xff, 0xff, 0xff,
@@ -79,6 +76,7 @@ open_socket(struct interface *iface, int protocol)
        } su;
        struct sock_fprog pf;
        int flags;
+       int *fd;
 
        if ((s = socket(PF_PACKET, SOCK_DGRAM, htons(protocol))) == -1)
                return -1;
@@ -92,10 +90,13 @@ open_socket(struct interface *iface, int protocol)
        }
        /* Install the DHCP filter */
        memset(&pf, 0, sizeof(pf));
+#ifdef ENABLE_ARP
        if (protocol == ETHERTYPE_ARP) {
                pf.filter = UNCONST(arp_bpf_filter);
                pf.len = arp_bpf_filter_len;
-       } else {
+       } else
+#endif
+       {
                pf.filter = UNCONST(dhcp_bpf_filter);
                pf.len = dhcp_bpf_filter_len;
        }
@@ -108,15 +109,15 @@ open_socket(struct interface *iface, int protocol)
                goto eexit;
        if (close_on_exec(s) == -1)
                goto eexit;
-       if (iface->fd > -1)
-               close(iface->fd);
-       iface->fd = s;
-       iface->socket_protocol = protocol;
-       if (iface->buffer == NULL) {
-               iface->buffer_size = BUFFER_LENGTH;
-               iface->buffer = xmalloc(iface->buffer_size);
-               iface->buffer_len = iface->buffer_pos = 0;
-       }
+#ifdef ENABLE_ARP
+       if (protocol == ETHERTYPE_ARP)
+               fd = &iface->arp_fd;
+       else
+#endif
+               fd = &iface->fd;
+       if (*fd != -1)
+               close(*fd);
+       *fd = s;
        return s;
 
 eexit:
@@ -125,7 +126,7 @@ eexit:
 }
 
 ssize_t
-send_raw_packet(const struct interface *iface, int type,
+send_raw_packet(const struct interface *iface, int protocol,
                const void *data, ssize_t len)
 {
        union sockunion {
@@ -133,10 +134,11 @@ send_raw_packet(const struct interface *iface, int type,
                struct sockaddr_ll sll;
                struct sockaddr_storage ss;
        } su;
+       int fd;
 
        memset(&su, 0, sizeof(su));
        su.sll.sll_family = AF_PACKET;
-       su.sll.sll_protocol = htons(type);
+       su.sll.sll_protocol = htons(protocol);
        if (!(su.sll.sll_ifindex = if_nametoindex(iface->name))) {
                errno = ENOENT;
                return -1;
@@ -148,30 +150,31 @@ send_raw_packet(const struct interface *iface, int type,
                       &ipv4_bcast_addr, sizeof(ipv4_bcast_addr));
        else
                memset(&su.sll.sll_addr, 0xff, iface->hwlen);
+#ifdef ENABLE_ARP
+       if (protocol == ETHERTYPE_ARP)
+               fd = iface->arp_fd;
+       else
+#endif
+               fd = iface->fd;
 
-       return sendto(iface->fd, data, len, 0, &su.sa, sizeof(su));
+       return sendto(fd, data, len, 0, &su.sa, sizeof(su));
 }
 
 ssize_t
-get_packet(struct interface *iface, void *data, ssize_t len)
+get_raw_packet(struct interface *iface, _unused int protocol,
+              void *data, ssize_t len)
 {
        ssize_t bytes;
-       const uint8_t *p;
+       int fd;
 
-       bytes = read(iface->fd, iface->buffer, iface->buffer_size);
+#ifdef ENABLE_ARP
+       if (protocol == ETHERTYPE_ARP)
+               fd = iface->arp_fd;
+       else
+#endif
+               fd = iface->fd;
+       bytes = read(fd, data, len);
        if (bytes == -1)
                return errno == EAGAIN ? 0 : -1;
-
-       /* If it's an ARP reply, then just send it back */
-       if (iface->socket_protocol == ETHERTYPE_ARP) {
-               p = iface->buffer;
-       } else {
-               if (valid_udp_packet(iface->buffer) != 0)
-                       return -1;
-               bytes = get_udp_data(&p, iface->buffer);
-       }
-       if (bytes > len)
-               bytes = len;
-       memcpy(data, p, bytes);
        return bytes;
 }
diff --git a/net.c b/net.c
index 1a05f91e6ae5b2d5c7dfaddba807866e87735d76..aa530ba2726acd7f9b9588dca340953a212d7d94 100644 (file)
--- a/net.c
+++ b/net.c
@@ -367,6 +367,9 @@ read_interface(const char *ifname, _unused int metric)
        /* 0 is a valid fd, so init to -1 */
        iface->fd = -1;
        iface->udp_fd = -1;
+#ifdef ENABLE_ARP
+       iface->arp_fd = -1;
+#endif
 
 eexit:
        close(s);
@@ -464,6 +467,7 @@ struct udp_dhcp_packet
        struct udphdr udp;
        struct dhcp_message dhcp;
 };
+const size_t udp_dhcp_len = sizeof(struct udp_dhcp_packet);
 
 static uint16_t
 checksum(const void *data, uint16_t len)
@@ -591,9 +595,8 @@ valid_udp_packet(const uint8_t *data)
 }
 
 #ifdef ENABLE_ARP
-static int
-send_arp(const struct interface *iface, int op, struct in_addr sip,
-        const unsigned char *taddr, struct in_addr tip)
+int
+send_arp(const struct interface *iface, int op, in_addr_t sip, in_addr_t tip)
 {
        struct arphdr *arp;
        size_t arpsize;
@@ -602,7 +605,7 @@ send_arp(const struct interface *iface, int op, struct in_addr sip,
 
        arpsize = sizeof(*arp) + 2 * iface->hwlen + 2 *sizeof(sip);
 
-       arp = xzalloc(arpsize);
+       arp = xmalloc(arpsize);
        arp->ar_hrd = htons(iface->family);
        arp->ar_pro = htons(ETHERTYPE_IP);
        arp->ar_hln = iface->hwlen;
@@ -614,199 +617,15 @@ send_arp(const struct interface *iface, int op, struct in_addr sip,
        p += iface->hwlen;
        memcpy(p, &sip, sizeof(sip));
        p += sizeof(sip);
-
-       if (taddr != NULL)
-               memcpy(p, taddr, iface->hwlen);
-       else
-               memset(p, 0, iface->hwlen);
+       /* ARP requests should ignore this, but we fill with 0xff
+        * for broadcast. */
+       memset(p, 0xff, iface->hwlen);
        p += iface->hwlen;
        memcpy(p, &tip, sizeof(tip));
 
        retval = send_raw_packet(iface, ETHERTYPE_ARP, arp, arpsize);
-       if (retval == -1)
-               logger(LOG_ERR,"send_packet: %s", strerror(errno));
        free(arp);
        return retval;
 }
-
-int
-arp_claim(struct interface *iface, struct in_addr address)
-{
-       struct arphdr reply;
-       struct in_addr reply_s;
-       struct in_addr reply_t;
-       uint8_t arp_reply[sizeof(reply) + 2 * sizeof(reply_s) + 2 * HWADDR_LEN];
-       uint8_t *hw_s, *hw_t;
-       long timeout;
-       int retval = -1;
-       int nprobes = 0;
-       int nclaims = 0;
-       struct in_addr null_addr;
-       struct pollfd fds[] = {
-               { -1, POLLIN, 0 },
-               { -1, POLLIN, 0 }
-       };
-       int bytes;
-       int s;
-       struct timeval stopat;
-       struct timeval now;
-
-       if (!iface->arpable) {
-               logger(LOG_DEBUG, "interface `%s' is not ARPable", iface->name);
-               return 0;
-       }
-
-       if (!IN_LINKLOCAL(ntohl(iface->addr.s_addr)) || 
-           !IN_LINKLOCAL(ntohl(address.s_addr)))
-               logger(LOG_INFO,
-                      "checking %s is available on attached networks",
-                      inet_ntoa(address));
-
-       timeout = arc4random() % PROBE_WAIT;
-       if (get_time(&stopat) != 0)
-               return -1;
-       stopat.tv_usec += timeout * 1000;
-
-       if (open_socket(iface, ETHERTYPE_ARP) == -1) {
-               logger (LOG_ERR, "open_socket: %s", strerror(errno));
-               return -1;
-       }
-
-       fds[0].fd = signal_fd();
-       fds[1].fd = iface->fd;
-       memset(&null_addr, 0, sizeof(null_addr));
-
-       for (;;) {
-               /* Only poll if we have a timeout */
-               if (timeout > 0) {
-                       /* Obey IPV4LL timings, but make us faster for
-                        * routeable addresses */
-                       if (!IN_LINKLOCAL(htonl(address.s_addr)))
-                               timeout /= 6;
-                       s = poll(fds, 2, timeout);
-                       if (s == -1) {
-                               if (errno != EINTR)
-                                       logger(LOG_ERR, "poll: `%s'",
-                                              strerror(errno));
-                               break;
-                       }
-               } else
-                       s = 0;
-
-               /* Timed out */
-               if (s == 0) {
-                       if (nprobes < PROBE_NUM) {
-                               nprobes++;
-                               timeout = (arc4random() %
-                                          (PROBE_MAX - PROBE_MIN)) +
-                                         PROBE_MIN;
-                               logger(LOG_DEBUG, "sending ARP probe #%d",
-                                      nprobes);
-                               if (send_arp(iface, ARPOP_REQUEST,
-                                            null_addr, NULL,
-                                            address) == -1)
-                                       break;
-
-                               /* IEEE1394 cannot set ARP target address
-                                * according to RFC2734 */
-                               if (nprobes >= PROBE_NUM &&
-                                   iface->family == ARPHRD_IEEE1394)
-                                       nclaims = ANNOUNCE_NUM;
-                       } else if (nclaims < ANNOUNCE_NUM) {
-                               nclaims++;
-                               timeout = ANNOUNCE_INTERVAL;
-                               /* Kernel will send the last ARP when we add
-                                * the address. */
-                               if (nclaims < ANNOUNCE_NUM) {
-                                       logger(LOG_DEBUG,
-                                              "sending ARP claim #%d",
-                                              nclaims);
-                                       if (send_arp(iface, ARPOP_REQUEST,
-                                                    address, iface->hwaddr,
-                                                    address) == -1)
-                                               break;
-                               }
-                       } else {
-                               /* No replies, so done */
-                               retval = 0;
-                               break;
-                       }
-
-                       /* Setup our stop time */
-                       if (get_time(&stopat) != 0)
-                               break;
-                       stopat.tv_usec += timeout * 1000;
-                       continue;
-               }
-
-               /* We maybe ARP flooded, so check our time */
-               if (get_time(&now) != 0)
-                       break;
-               if (timercmp(&now, &stopat, >)) {
-                       timeout = 0;
-                       continue;
-               }
-
-               /* Check if signalled */
-               if ((fds[0].revents & POLLIN)) {
-                       errno = EINTR;
-                       return -1;
-               }
-
-               if (!(fds[1].revents & POLLIN))
-                       continue;
-               for(;;) {
-                       memset(arp_reply, 0, sizeof(arp_reply));
-                       bytes = get_packet(iface,
-                                          arp_reply, sizeof(arp_reply));
-                       if (bytes == -1 || bytes == 0)
-                               break;
-
-                       /* We must have a full ARP header */
-                       if ((size_t)bytes < sizeof(reply))
-                               continue;
-                       memcpy(&reply, arp_reply, sizeof(reply));
-                       /* Protocol must be IP. */
-                       if (reply.ar_pro != htons(ETHERTYPE_IP))
-                               continue;
-                       if (reply.ar_pln != sizeof(reply_s))
-                               continue;
-                       /* Only these types are recognised */
-                       if (reply.ar_op != htons(ARPOP_REPLY) &&
-                           reply.ar_op != htons(ARPOP_REQUEST))
-                               continue;
-
-                       /* Get pointers to the hardware addreses */
-                       hw_s = arp_reply + sizeof(reply);
-                       hw_t = hw_s + reply.ar_hln + reply.ar_pln;
-                       /* Ensure we got all the data */
-                       if ((hw_t + reply.ar_hln + reply.ar_pln) - arp_reply > bytes)
-                               continue;
-                       /* Copy out the IP addresses */
-                       memcpy(&reply_s, hw_s + reply.ar_hln, reply.ar_pln);
-                       memcpy(&reply_t, hw_t + reply.ar_hln, reply.ar_pln);
-
-                       /* Check for conflict */
-                       if (reply_s.s_addr == address.s_addr ||
-                           (reply_t.s_addr == address.s_addr &&
-                            reply.ar_op == htons(ARPOP_REQUEST) &&
-                            (iface->hwlen != reply.ar_hln ||
-                             memcmp(hw_s, iface->hwaddr, iface->hwlen) != 0)))
-                       {
-                               logger(LOG_ERR, "hardware address %s claims %s",
-                                      hwaddr_ntoa((unsigned char *)hw_s,
-                                                  (size_t)reply.ar_hln),
-                                      inet_ntoa(address));
-                               errno = EEXIST;
-                               retval = -1;
-                               goto eexit;
-                       }
-               }
-       }
-
-eexit:
-       close(iface->fd);
-       iface->fd = -1;
-       return retval;
-}
 #endif
+
diff --git a/net.h b/net.h
index b8b6cb6c88ab6105afbf7bae3ac790e7eee14193..aef09e331ad3631beda39171e13856f157674936 100644 (file)
--- a/net.h
+++ b/net.h
@@ -107,17 +107,15 @@ struct interface
        int udp_fd;
        size_t buffer_size, buffer_len, buffer_pos;
        unsigned char *buffer;
-
-#ifdef __linux__
-       int socket_protocol;
+#ifdef ENABLE_ARP
+       int arp_fd;
 #endif
 
-       char leasefile[PATH_MAX];
-
        struct in_addr addr;
        struct in_addr net;
        struct rt *routes;
 
+       char leasefile[PATH_MAX];
        time_t start_uptime;
 
        unsigned char *clientid;
@@ -158,6 +156,7 @@ int if_route(const char *, const struct in_addr *, const struct in_addr *,
 void free_routes(struct rt *);
 
 int open_udp_socket(struct interface *);
+const size_t udp_dhcp_len;
 ssize_t make_udp_packet(uint8_t **, const uint8_t *, size_t,
                        struct in_addr, struct in_addr);
 ssize_t get_udp_data(const uint8_t **, const uint8_t *);
@@ -168,21 +167,9 @@ ssize_t send_packet(const struct interface *, struct in_addr,
                    const uint8_t *, ssize_t);
 ssize_t send_raw_packet(const struct interface *, int,
                        const void *, ssize_t);
-ssize_t get_packet(struct interface *, void *, ssize_t);
+ssize_t get_raw_packet(struct interface *, int, void *, ssize_t);
 
 #ifdef ENABLE_ARP
-/* These are really for IPV4LL, RFC 3927.
- * We multiply some numbers by 1000 so they are suitable for use in poll(). */
-#define PROBE_WAIT              1 * 1000
-#define PROBE_NUM               3
-#define PROBE_MIN               1 * 1000
-#define PROBE_MAX               2 * 1000
-#define ANNOUNCE_WAIT           2 * 1000
-#define ANNOUNCE_NUM            2
-#define ANNOUNCE_INTERVAL       2 * 1000
-#define MAX_CONFLICTS          10
-#define RATE_LIMIT_INTERVAL    60
-#define DEFEND_INTERVAL                10
-int arp_claim(struct interface *, struct in_addr);
+int send_arp(const struct interface *, int, in_addr_t, in_addr_t);
 #endif
 #endif