]> git.ipfire.org Git - thirdparty/dhcpcd.git/commitdiff
Rework the ARP code so that we can have multiple ARP states.
authorRoy Marples <roy@marples.name>
Thu, 23 Oct 2014 19:07:35 +0000 (19:07 +0000)
committerRoy Marples <roy@marples.name>
Thu, 23 Oct 2014 19:07:35 +0000 (19:07 +0000)
Split the ARP resolution code to dhcp or ipv4ll where it belongs.
This allows us to probe IPv4LL while still DISCOVERing a DHCP lease.

arp.c
arp.h
dhcp.c
dhcp.h
dhcpcd.c
dhcpcd.conf.5.in
if-options.h
ipv4.c
ipv4.h
ipv4ll.c

diff --git a/arp.c b/arp.c
index 1782d37c1276ae006399cb61ddc738a8a9367fc8..929e032df605022b44e424a5abd66f3f40362a2f 100644 (file)
--- a/arp.c
+++ b/arp.c
@@ -39,7 +39,7 @@
 #include <syslog.h>
 #include <unistd.h>
 
-#define ELOOP_QUEUE 2
+#define ELOOP_QUEUE 5
 #include "config.h"
 #include "arp.h"
 #include "ipv4.h"
@@ -94,33 +94,6 @@ eexit:
        return -1;
 }
 
-static void
-arp_failure(struct interface *ifp)
-{
-       const struct dhcp_state *state = D_CSTATE(ifp);
-
-       /* If we failed without a magic cookie then we need to try
-        * and defend our IPv4LL address. */
-       if ((state->offer != NULL &&
-           state->offer->cookie != htonl(MAGIC_COOKIE)) ||
-           (state->new != NULL &&
-           state->new->cookie != htonl(MAGIC_COOKIE)))
-       {
-               ipv4ll_handle_failure(ifp);
-               return;
-       }
-
-       unlink(state->leasefile);
-       if (!state->lease.frominfo)
-               dhcp_decline(ifp);
-       eloop_timeout_delete(ifp->ctx->eloop, NULL, ifp);
-       if (state->lease.frominfo)
-               dhcpcd_startinterface(ifp);
-       else
-               eloop_timeout_add_sec(ifp->ctx->eloop,
-                   DHCP_ARP_FAIL, dhcpcd_startinterface, ifp);
-}
-
 static void
 arp_packet(void *arg)
 {
@@ -128,15 +101,11 @@ arp_packet(void *arg)
        const struct interface *ifn;
        uint8_t arp_buffer[ARP_LEN];
        struct arphdr ar;
-       uint32_t reply_s;
-       uint32_t reply_t;
-       uint8_t *hw_s, *hw_t;
+       struct arp_msg arm;
        ssize_t bytes;
        struct dhcp_state *state;
-       struct if_options *opts = ifp->options;
-       const char *hwaddr;
-       struct in_addr ina, fail;
-       char hwbuf[HWADDR_LEN * 3];
+       struct arp_state *astate, *astaten;
+       unsigned char *hw_s, *hw_t;
        int flags;
 
        state = D_STATE(ifp);
@@ -154,10 +123,13 @@ arp_packet(void *arg)
                if ((size_t)bytes < sizeof(ar))
                        continue;
                memcpy(&ar, arp_buffer, sizeof(ar));
+               /* Families must match */
+               if (ar.ar_hrd != htons(ifp->family))
+                       continue;
                /* Protocol must be IP. */
                if (ar.ar_pro != htons(ETHERTYPE_IP))
                        continue;
-               if (ar.ar_pln != sizeof(reply_s))
+               if (ar.ar_pln != sizeof(arm.sip.s_addr))
                        continue;
                /* Only these types are recognised */
                if (ar.ar_op != htons(ARPOP_REPLY) &&
@@ -178,79 +150,25 @@ arp_packet(void *arg)
                }
                if (ifn)
                        continue;
-               /* Copy out the IP addresses */
-               memcpy(&reply_s, hw_s + ar.ar_hln, ar.ar_pln);
-               memcpy(&reply_t, hw_t + ar.ar_hln, ar.ar_pln);
-
-               /* Check for arping */
-               if (state->arping_index &&
-                   state->arping_index <= opts->arping_len &&
-                   (reply_s == opts->arping[state->arping_index - 1] ||
-                   (reply_s == 0 &&
-                   reply_t == opts->arping[state->arping_index - 1])))
-               {
-                       ina.s_addr = reply_s;
-                       hwaddr = hwaddr_ntoa((unsigned char *)hw_s,
-                           (size_t)ar.ar_hln, hwbuf, sizeof(hwbuf));
-                       syslog(LOG_INFO,
-                           "%s: found %s on hardware address %s",
-                           ifp->name, inet_ntoa(ina), hwaddr);
-                       if (dhcpcd_selectprofile(ifp, hwaddr) == -1 &&
-                           dhcpcd_selectprofile(ifp, inet_ntoa(ina)) == -1)
-                       {
-                               state->probes = 0;
-                               /* We didn't find a profile for this
-                                * address or hwaddr, so move to the next
-                                * arping profile */
-                               if (state->arping_index <
-                                   ifp->options->arping_len)
-                               {
-                                       arp_probe(ifp);
-                                       return;
-                               }
-                       }
-                       dhcp_close(ifp);
-                       eloop_timeout_delete(ifp->ctx->eloop, NULL, ifp);
-                       dhcpcd_startinterface(ifp);
-                       return;
-               }
-
-               fail.s_addr = 0;
-               /* RFC 2131 3.1.5, Client-server interaction
-                * RFC 3927 2.2.1, Probe Conflict Detection */
-               if (state->offer &&
-                   (reply_s == state->offer->yiaddr ||
-                   (reply_s == 0 && reply_t == state->offer->yiaddr)))
-                       fail.s_addr = state->offer->yiaddr;
-
-               /* RFC 3927 2.5, Conflict Defense */
-               if (IN_LINKLOCAL(htonl(state->addr.s_addr)) &&
-                   reply_s == state->addr.s_addr)
-                       fail.s_addr = state->addr.s_addr;
-
-               if (fail.s_addr) {
-                       state->fail = fail;
-                       syslog(LOG_ERR, "%s: hardware address %s claims %s",
-                           ifp->name,
-                           hwaddr_ntoa((unsigned char *)hw_s,
-                               (size_t)ar.ar_hln, hwbuf, sizeof(hwbuf)),
-                           inet_ntoa(state->fail));
-                       errno = EEXIST;
-                       arp_failure(ifp);
-                       return;
+               /* Copy out the HW and IP addresses */
+               memcpy(&arm.sha, hw_s, ar.ar_hln);
+               memcpy(&arm.sip.s_addr, hw_s + ar.ar_hln, ar.ar_pln);
+               memcpy(&arm.tha, hw_t, ar.ar_hln);
+               memcpy(&arm.tip.s_addr, hw_t + ar.ar_hln, ar.ar_pln);
+
+               /* Run the conflicts */
+               TAILQ_FOREACH_SAFE(astate, &state->arp_states, next, astaten) {
+                       astate->conflicted_cb(astate, &arm);
                }
        }
 }
 
-void
-arp_announce(void *arg)
+static void
+arp_open(struct interface *ifp)
 {
-       struct interface *ifp = arg;
-       struct dhcp_state *state = D_STATE(ifp);
-       struct timeval tv;
+       struct dhcp_state *state;
 
-       if (state->new == NULL)
-               return;
+       state = D_STATE(ifp);
        if (state->arp_fd == -1) {
                state->arp_fd = if_openrawsocket(ifp, ETHERTYPE_ARP);
                if (state->arp_fd == -1) {
@@ -260,131 +178,132 @@ arp_announce(void *arg)
                eloop_event_add(ifp->ctx->eloop, state->arp_fd,
                    arp_packet, ifp, NULL, NULL);
        }
-       if (++state->claims < ANNOUNCE_NUM)
+}
+
+static void
+arp_announced(void *arg)
+{
+       struct arp_state *astate = arg;
+
+       if (astate->announced_cb) {
+               astate->announced_cb(astate);
+               return;
+       }
+
+       /* Nothing more to do, so free us */
+       arp_free(astate);
+}
+
+static void
+arp_announce1(void *arg)
+{
+       struct arp_state *astate = arg;
+       struct interface *ifp = astate->iface;
+
+       if (++astate->claims < ANNOUNCE_NUM)
                syslog(LOG_DEBUG,
                    "%s: sending ARP announce (%d of %d), "
                    "next in %d.0 seconds",
-                   ifp->name, state->claims, ANNOUNCE_NUM, ANNOUNCE_WAIT);
+                   ifp->name, astate->claims, ANNOUNCE_NUM, ANNOUNCE_WAIT);
        else
                syslog(LOG_DEBUG,
                    "%s: sending ARP announce (%d of %d)",
-                   ifp->name, state->claims, ANNOUNCE_NUM);
+                   ifp->name, astate->claims, ANNOUNCE_NUM);
        if (arp_send(ifp, ARPOP_REQUEST,
-               state->new->yiaddr, state->new->yiaddr) == -1)
+               astate->addr.s_addr, astate->addr.s_addr) == -1)
                syslog(LOG_ERR, "send_arp: %m");
-       if (state->claims < ANNOUNCE_NUM) {
-               eloop_timeout_add_sec(ifp->ctx->eloop,
-                   ANNOUNCE_WAIT, arp_announce, ifp);
-               return;
-       }
-       if (state->new->cookie != htonl(MAGIC_COOKIE)) {
-               /* Reset the conflict counter when we finish announcing. */
-               eloop_timeout_add_sec(ifp->ctx->eloop,
-                   ANNOUNCE_WAIT, ipv4ll_claimed, ifp);
-               /* Check if doing DHCP */
-               if (!(ifp->options->options & DHCPCD_DHCP))
-                       return;
-               /* We should pretend to be at the end
-                * of the DHCP negotation cycle unless we rebooted */
-               if (state->interval)
-                       state->interval = 64 + DHCP_RAND_MIN;
-               else
-                       state->interval = ANNOUNCE_WAIT;
-               state->probes = 0;
-               state->claims = 0;
-               tv.tv_sec = state->interval;
-               tv.tv_usec = (suseconds_t)arc4random_uniform(
-                   (DHCP_RAND_MAX - DHCP_RAND_MIN) * 1000000);
-               timernorm(&tv);
-               eloop_timeout_add_tv(ifp->ctx->eloop, &tv, dhcp_discover, ifp);
-       } else {
-               eloop_event_delete(ifp->ctx->eloop, state->arp_fd, 0);
-               close(state->arp_fd);
-               state->arp_fd = -1;
-       }
+       eloop_timeout_add_sec(ifp->ctx->eloop, ANNOUNCE_WAIT,
+           astate->claims < ANNOUNCE_NUM ? arp_announce1 : arp_announced,
+           astate);
 }
 
 void
-arp_probe(void *arg)
+arp_announce(struct arp_state *astate)
 {
-       struct interface *ifp = arg;
-       struct dhcp_state *state = D_STATE(ifp);
-       struct in_addr addr;
-       struct timeval tv;
-       int arping = 0;
 
-       if (state->arp_fd == -1) {
-               state->arp_fd = if_openrawsocket(ifp, ETHERTYPE_ARP);
-               if (state->arp_fd == -1) {
-                       syslog(LOG_ERR, "%s: %s: %m", __func__, ifp->name);
-                       return;
-               }
-               eloop_event_add(ifp->ctx->eloop,
-                   state->arp_fd, arp_packet, ifp, NULL, NULL);
-       }
+       arp_open(astate->iface);
+       astate->claims = 0;
+       arp_announce1(astate);
+}
 
-       if (state->arping_index < ifp->options->arping_len) {
-               addr.s_addr = ifp->options->arping[state->arping_index];
-               arping = 1;
-       } else if (state->offer) {
-               if (state->offer->yiaddr)
-                       addr.s_addr = state->offer->yiaddr;
-               else
-                       addr.s_addr = state->offer->ciaddr;
-       } else
-               addr.s_addr = state->addr.s_addr;
-
-       if (state->probes == 0) {
-               if (arping)
-                       syslog(LOG_DEBUG, "%s: searching for %s",
-                           ifp->name, inet_ntoa(addr));
-               else
-                       syslog(LOG_DEBUG, "%s: checking for %s",
-                           ifp->name, inet_ntoa(addr));
-       }
-       if (++state->probes < PROBE_NUM) {
+static void
+arp_probed(void *arg)
+{
+       struct arp_state *astate = arg;
+
+       astate->probed_cb(astate);
+}
+
+static void
+arp_probe1(void *arg)
+{
+       struct arp_state *astate = arg;
+       struct interface *ifp = astate->iface;
+       struct timeval tv;
+
+       if (++astate->probes < PROBE_NUM) {
                tv.tv_sec = PROBE_MIN;
                tv.tv_usec = (suseconds_t)arc4random_uniform(
                    (PROBE_MAX - PROBE_MIN) * 1000000);
                timernorm(&tv);
-               eloop_timeout_add_tv(ifp->ctx->eloop, &tv, arp_probe, ifp);
+               eloop_timeout_add_tv(ifp->ctx->eloop, &tv, arp_probe1, astate);
        } else {
                tv.tv_sec = ANNOUNCE_WAIT;
                tv.tv_usec = 0;
-               if (arping) {
-                       state->probes = 0;
-                       if (++state->arping_index < ifp->options->arping_len)
-                               eloop_timeout_add_tv(ifp->ctx->eloop,
-                                   &tv, arp_probe, ifp);
-                       else
-                               eloop_timeout_add_tv(ifp->ctx->eloop,
-                                   &tv, dhcpcd_startinterface, ifp);
-               } else
-                       eloop_timeout_add_tv(ifp->ctx->eloop,
-                           &tv, dhcp_bind, ifp);
+               eloop_timeout_add_tv(ifp->ctx->eloop, &tv, arp_probed, astate);
        }
        syslog(LOG_DEBUG,
            "%s: sending ARP probe (%d of %d), next in %0.1f seconds",
-           ifp->name, state->probes ? state->probes : PROBE_NUM, PROBE_NUM,
+           ifp->name, astate->probes ? astate->probes : PROBE_NUM, PROBE_NUM,
            timeval_to_double(&tv));
-       if (arp_send(ifp, ARPOP_REQUEST, 0, addr.s_addr) == -1)
+       if (arp_send(ifp, ARPOP_REQUEST, 0, astate->addr.s_addr) == -1)
                syslog(LOG_ERR, "send_arp: %m");
 }
 
 void
-arp_start(struct interface *ifp)
+arp_probe(struct arp_state *astate)
 {
-       struct dhcp_state *state = D_STATE(ifp);
 
-       state->probes = 0;
-       state->arping_index = 0;
-       arp_probe(ifp);
+       arp_open(astate->iface);
+       astate->probes = 0;
+       syslog(LOG_DEBUG, "%s: probing for %s",
+           astate->iface->name, inet_ntoa(astate->addr));
+       arp_probe1(astate);
+}
+
+
+struct arp_state *
+arp_new(struct interface *ifp) {
+       struct arp_state *astate;
+       struct dhcp_state *state;
+
+       astate = calloc(1, sizeof(*astate));
+       if (astate == NULL) {
+               syslog(LOG_ERR, "%s: %s: %m", ifp->name, __func__);
+               return NULL;
+       }
+
+       astate->iface = ifp;
+       state = D_STATE(ifp);
+       TAILQ_INSERT_TAIL(&state->arp_states, astate, next);
+       return astate;
+}
+
+void
+arp_free(struct arp_state *astate)
+{
+       struct dhcp_state *state;
+
+       state = D_STATE(astate->iface);
+       TAILQ_REMOVE(&state->arp_states, astate, next);
+       free(astate);
 }
 
 void
 arp_close(struct interface *ifp)
 {
        struct dhcp_state *state = D_STATE(ifp);
+       struct arp_state *astate;
 
        if (state == NULL)
                return;
@@ -394,5 +313,10 @@ arp_close(struct interface *ifp)
                close(state->arp_fd);
                state->arp_fd = -1;
        }
-}
 
+       while ((astate = TAILQ_FIRST(&state->arp_states))) {
+               TAILQ_REMOVE(&state->arp_states, astate, next);
+               eloop_timeout_delete(ifp->ctx->eloop, NULL, astate);
+               free(astate);
+       }
+}
diff --git a/arp.h b/arp.h
index 22ab72ea79fc02762c83590391b96c625b3e2443..a87dd4f453f8727b556138d5be3489b3d8c449f5 100644 (file)
--- a/arp.h
+++ b/arp.h
 
 #include "dhcpcd.h"
 
-void arp_announce(void *);
-void arp_probe(void *);
-void arp_start(struct interface *);
+struct arp_msg {
+       uint16_t op;
+       unsigned char sha[HWADDR_LEN];
+       struct in_addr sip;
+       unsigned char tha[HWADDR_LEN];
+       struct in_addr tip;
+};
+
+struct arp_state {
+       TAILQ_ENTRY(arp_state) next;
+       struct interface *iface;
+
+       void (*probed_cb)(struct arp_state *);
+       void (*announced_cb)(struct arp_state *);
+       void (*conflicted_cb)(struct arp_state *, const struct arp_msg *);
+
+       struct in_addr addr;
+       int probes;
+       int claims;
+};
+TAILQ_HEAD(arp_statehead, arp_state);
+
+void arp_announce(struct arp_state *);
+void arp_probe(struct arp_state *);
+struct arp_state *arp_new(struct interface *);
+void arp_free(struct arp_state *);
 void arp_close(struct interface *);
 #endif
diff --git a/dhcp.c b/dhcp.c
index 81ca1d84d169f1e94c23c835b7570ed005bec77b..560f4e2c25f9c892615945c7f4ba8fa161c38069 100644 (file)
--- a/dhcp.c
+++ b/dhcp.c
@@ -1415,11 +1415,7 @@ dhcp_close(struct interface *ifp)
        if (state == NULL)
                return;
 
-       if (state->arp_fd != -1) {
-               eloop_event_delete(ifp->ctx->eloop, state->arp_fd, 0);
-               close(state->arp_fd);
-               state->arp_fd = -1;
-       }
+       arp_close(ifp);
        if (state->raw_fd != -1) {
                eloop_event_delete(ifp->ctx->eloop, state->raw_fd, 0);
                close(state->raw_fd);
@@ -1735,16 +1731,13 @@ dhcp_discover(void *arg)
        struct if_options *ifo = ifp->options;
        time_t 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 (state->state == DHS_REBOOT &&
-           !(ifp->ctx->options & DHCPCD_DAEMONISED))
-       {
+       /* If we're rebooting then we need to shorten the normal timeout
+        * to ensure we try for a fallback or IPv4LL address. */
+       if (state->state == DHS_REBOOT) {
                if (ifo->reboot >= timeout)
                        timeout = 2;
                else
-                       timeout -= ifo->reboot;
+                       timeout = ifo->reboot;
        }
 
        state->state = DHS_DISCOVER;
@@ -1755,12 +1748,8 @@ dhcp_discover(void *arg)
                    timeout, dhcp_fallback, ifp);
        else if (ifo->options & DHCPCD_IPV4LL &&
            !IN_LINKLOCAL(htonl(state->addr.s_addr)))
-       {
-               if (IN_LINKLOCAL(htonl(state->fail.s_addr)))
-                       timeout = RATE_LIMIT_INTERVAL;
                eloop_timeout_add_sec(ifp->ctx->eloop,
                    timeout, ipv4ll_start, ifp);
-       }
        if (ifo->options & DHCPCD_REQUEST)
                syslog(LOG_INFO, "%s: soliciting a DHCP lease (requesting %s)",
                    ifp->name, inet_ntoa(ifo->req_addr));
@@ -1819,6 +1808,13 @@ dhcp_renew(void *arg)
        send_renew(ifp);
 }
 
+static void
+dhcp_arp_announced(struct arp_state *astate)
+{
+
+       arp_close(astate->iface);
+}
+
 static void
 dhcp_rebind(void *arg)
 {
@@ -1838,9 +1834,8 @@ dhcp_rebind(void *arg)
 }
 
 void
-dhcp_bind(void *arg)
+dhcp_bind(struct interface *ifp, struct arp_state *astate)
 {
-       struct interface *ifp = arg;
        struct dhcp_state *state = D_STATE(ifp);
        struct if_options *ifo = ifp->options;
        struct dhcp_lease *lease = &state->lease;
@@ -1850,7 +1845,6 @@ dhcp_bind(void *arg)
        if (state->state == DHS_BOUND)
                goto applyaddr;
        state->reason = NULL;
-       state->xid = 0;
        free(state->old);
        state->old = state->new;
        state->new = state->offer;
@@ -1963,18 +1957,21 @@ dhcp_bind(void *arg)
 
 applyaddr:
        ipv4_applyaddr(ifp);
-       if (dhcpcd_daemonise(ifp->ctx) == 0) {
-               if (!ipv4ll) {
-                       /* We bound a non IPv4LL address so reset the
-                        * conflict counter */
-                       state->conflicts = 0;
+       if (dhcpcd_daemonise(ifp->ctx))
+               return;
+       if (ifo->options & DHCPCD_ARP) {
+               if (state->added) {
+                       if (astate == NULL) {
+                               /* We don't care about what happens
+                                * to the ARP announcement */
+                               astate = arp_new(ifp);
+                               astate->announced_cb =
+                                   dhcp_arp_announced;
+                       }
+                       if (astate)
+                               arp_announce(astate);
+               } else if (!ipv4ll)
                        arp_close(ifp);
-               }
-               if (ifo->options & DHCPCD_ARP) {
-                       state->claims = 0;
-                       if (state->added)
-                               arp_announce(ifp);
-               }
        }
 }
 
@@ -1984,7 +1981,7 @@ dhcp_timeout(void *arg)
        struct interface *ifp = arg;
        struct dhcp_state *state = D_STATE(ifp);
 
-       dhcp_bind(ifp);
+       dhcp_bind(ifp, NULL);
        state->interval = 0;
        dhcp_discover(ifp);
 }
@@ -2030,7 +2027,7 @@ dhcp_static(struct interface *ifp)
        state->offer = dhcp_message_new(&ifo->req_addr, &ifo->req_mask);
        if (state->offer) {
                eloop_timeout_delete(ifp->ctx->eloop, NULL, ifp);
-               dhcp_bind(ifp);
+               dhcp_bind(ifp, NULL);
        }
 }
 
@@ -2066,7 +2063,7 @@ dhcp_inform(struct interface *ifp)
                            dhcp_message_new(&ifo->req_addr, &ifo->req_mask);
                if (state->offer) {
                        ifo->options |= DHCPCD_STATIC;
-                       dhcp_bind(ifp);
+                       dhcp_bind(ifp, NULL);
                        ifo->options &= ~DHCPCD_STATIC;
                }
        }
@@ -2103,6 +2100,7 @@ dhcp_reboot(struct interface *ifp)
        if (state == NULL)
                return;
        ifo = ifp->options;
+       state->state = DHS_REBOOT;
        state->interval = 0;
 
        if (ifo->options & DHCPCD_LINK && ifp->carrier == LINK_DOWN) {
@@ -2121,18 +2119,11 @@ dhcp_reboot(struct interface *ifp)
                syslog(LOG_INFO, "%s: informing address of %s",
                    ifp->name, inet_ntoa(state->lease.addr));
        } else if (state->offer->cookie == 0) {
-               if (ifo->options & DHCPCD_IPV4LL) {
-                       state->claims = 0;
-                       if (state->added)
-                               arp_announce(ifp);
-               } else
-                       dhcp_discover(ifp);
                return;
        } else {
                syslog(LOG_INFO, "%s: rebinding lease of %s",
                    ifp->name, inet_ntoa(state->lease.addr));
        }
-       state->state = DHS_REBOOT;
        state->xid = dhcp_xid(ifp);
        state->lease.server.s_addr = 0;
        eloop_timeout_delete(ifp->ctx->eloop, NULL, ifp);
@@ -2172,7 +2163,6 @@ dhcp_drop(struct interface *ifp, const char *reason)
                return;
        dhcp_auth_reset(&state->auth);
        dhcp_close(ifp);
-       arp_close(ifp);
        if (ifp->options->options & DHCPCD_RELEASE) {
                unlink(state->leasefile);
                if (ifp->carrier != LINK_DOWN &&
@@ -2299,6 +2289,118 @@ whitelisted_ip(const struct if_options *ifo, in_addr_t addr)
        return 0;
 }
 
+static void
+dhcp_arp_probed(struct arp_state *astate)
+{
+       struct dhcp_state *state;
+       struct if_options *ifo;
+
+       /* We didn't find a profile for this
+        * address or hwaddr, so move to the next
+        * arping profile */
+       state = D_STATE(astate->iface);
+       ifo = astate->iface->options;
+       if (state->arping_index < ifo->arping_len) {
+               if (++state->arping_index < ifo->arping_len) {
+                       astate->addr.s_addr =
+                           ifo->arping[state->arping_index - 1];
+                       arp_probe(astate);
+               }
+               dhcpcd_startinterface(astate->iface);
+               return;
+       }
+       dhcp_close(astate->iface);
+       eloop_timeout_delete(astate->iface->ctx->eloop, NULL, astate->iface);
+       dhcp_bind(astate->iface, astate);
+}
+
+static void
+dhcp_arp_conflicted(struct arp_state *astate, const struct arp_msg *amsg)
+{
+       struct dhcp_state *state;
+       struct if_options *ifo;
+
+       state = D_STATE(astate->iface);
+       ifo = astate->iface->options;
+       if (state->arping_index &&
+           state->arping_index <= ifo->arping_len &&
+           (amsg->sip.s_addr == ifo->arping[state->arping_index - 1] ||
+           (amsg->sip.s_addr == 0 &&
+           amsg->tip.s_addr == ifo->arping[state->arping_index - 1])))
+       {
+               struct in_addr addr;
+               char buf[HWADDR_LEN * 3];
+
+               addr.s_addr = ifo->arping[state->arping_index - 1];
+               syslog(LOG_INFO,
+                   "%s: found %s on hardware address %s",
+                   astate->iface->name, inet_ntoa(addr),
+                   hwaddr_ntoa(amsg->sha, astate->iface->hwlen,
+                   buf, sizeof(buf)));
+               if (dhcpcd_selectprofile(astate->iface, buf) == -1 &&
+                   dhcpcd_selectprofile(astate->iface, inet_ntoa(addr)) == -1)
+               {
+                       /* We didn't find a profile for this
+                        * address or hwaddr, so move to the next
+                        * arping profile */
+                       dhcp_arp_probed(astate);
+                       return;
+               }
+               dhcp_close(astate->iface);
+               eloop_timeout_delete(astate->iface->ctx->eloop, NULL,
+                   astate->iface);
+               dhcpcd_startinterface(astate->iface);
+       }
+
+       if (state->offer == NULL)
+               return;
+
+       /* RFC 2131 3.1.5, Client-server interaction */
+       if (amsg->sip.s_addr == state->offer->yiaddr ||
+           (amsg->sip.s_addr == 0 && amsg->tip.s_addr == state->offer->yiaddr))
+       {
+               struct in_addr fail;
+               char buf[HWADDR_LEN * 3];
+
+               fail.s_addr = state->offer->yiaddr;
+               syslog(LOG_ERR, "%s: hardware address %s claims %s",
+                   astate->iface->name,
+                   hwaddr_ntoa(amsg->sha, astate->iface->hwlen,
+                   buf, sizeof(buf)),
+                   inet_ntoa(fail));
+
+               arp_free(astate);
+               unlink(state->leasefile);
+               if (!state->lease.frominfo)
+                       dhcp_decline(astate->iface);
+               eloop_timeout_delete(astate->iface->ctx->eloop, NULL,
+                   astate->iface);
+               if (state->lease.frominfo)
+                       dhcpcd_startinterface(astate->iface);
+               else
+                       eloop_timeout_add_sec(astate->iface->ctx->eloop,
+                           DHCP_ARP_FAIL,
+                           dhcpcd_startinterface, astate->iface);
+       }
+}
+
+void
+dhcp_probe(struct interface *ifp)
+{
+       const struct dhcp_state *state;
+       struct arp_state *astate;
+
+       astate = arp_new(ifp);
+       if (astate) {
+               state = D_CSTATE(ifp);
+               astate->addr = state->addr;
+               astate->probed_cb = dhcp_arp_probed;
+               astate->conflicted_cb = dhcp_arp_conflicted;
+               astate->announced_cb = dhcp_arp_announced;
+               arp_probe(astate);
+       }
+}
+
 static void
 dhcp_handledhcp(struct interface *iface, struct dhcp_message **dhcpp,
     const struct in_addr *from)
@@ -2581,15 +2683,20 @@ dhcp_handledhcp(struct interface *iface, struct dhcp_message **dhcpp,
                 * then we can't ARP for duplicate detection. */
                addr.s_addr = state->offer->yiaddr;
                if (!ipv4_iffindaddr(iface, &addr, NULL)) {
-                       state->claims = 0;
-                       state->probes = 0;
-                       state->conflicts = 0;
-                       arp_probe(iface);
+                       struct arp_state *astate;
+
+                       astate = arp_new(iface);
+                       if (astate) {
+                               astate->addr = addr;
+                               astate->probed_cb = dhcp_arp_probed;
+                               astate->conflicted_cb = dhcp_arp_conflicted;
+                               arp_probe(astate);
+                       }
                        return;
                }
        }
 
-       dhcp_bind(iface);
+       dhcp_bind(iface, NULL);
 }
 
 static size_t
@@ -2794,6 +2901,8 @@ dhcp_dump(struct interface *ifp)
        ifp->if_data[IF_DATA_DHCP] = state = calloc(1, sizeof(*state));
        if (state == NULL)
                goto eexit;
+       state->raw_fd = state->arp_fd = -1;
+       TAILQ_INIT(&state->arp_states);
        snprintf(state->leasefile, sizeof(state->leasefile),
            LEASEFILE, ifp->name);
        state->new = read_lease(ifp);
@@ -2820,6 +2929,7 @@ dhcp_free(struct interface *ifp)
        struct dhcp_state *state = D_STATE(ifp);
        struct dhcpcd_ctx *ctx;
 
+       dhcp_close(ifp);
        if (state) {
                free(state->old);
                free(state->new);
@@ -2869,6 +2979,7 @@ dhcp_init(struct interface *ifp)
                        return -1;
                /* 0 is a valid fd, so init to -1 */
                state->raw_fd = state->arp_fd = -1;
+               TAILQ_INIT(&state->arp_states);
        }
 
        state->state = DHS_INIT;
@@ -2973,7 +3084,14 @@ dhcp_start1(void *arg)
        state->offer = NULL;
 
        if (state->arping_index < ifo->arping_len) {
-               arp_start(ifp);
+               struct arp_state *astate;
+
+               astate = arp_new(ifp);
+               if (astate) {
+                       astate->probed_cb = dhcp_arp_probed;
+                       astate->conflicted_cb = dhcp_arp_conflicted;
+                       dhcp_arp_probed(astate);
+               }
                return;
        }
 
@@ -3042,9 +3160,13 @@ dhcp_start1(void *arg)
                return;
        }
 
-       if (state->offer == NULL || state->offer->cookie == 0)
+       if (state->offer == NULL || state->offer->cookie == 0) {
+               /* If we don't have an address yet, enter the reboot
+                * state to ensure at least fallback in short order. */
+               if (state->addr.s_addr == INADDR_ANY)
+                       state->state = DHS_REBOOT;
                dhcp_discover(ifp);
-       else
+       else
                dhcp_reboot(ifp);
 }
 
diff --git a/dhcp.h b/dhcp.h
index 48e1bba0e772dbd8ab321c169b3ef97713c529da..2ebe4f9828c551fe0ed28bcbc3773cf8e2063eed 100644 (file)
--- a/dhcp.h
+++ b/dhcp.h
@@ -34,6 +34,7 @@
 #include <limits.h>
 #include <stdint.h>
 
+#include "arp.h"
 #include "auth.h"
 #include "dhcp-common.h"
 
@@ -213,17 +214,6 @@ struct dhcp_state {
        uint32_t xid;
        int socket;
 
-       /* ARP */
-       int probes;
-       int claims;
-       struct in_addr fail;
-       size_t arping_index;
-
-       /* IPv4LL */
-       char randomstate[128];
-       int conflicts;
-       time_t defend;
-
        int raw_fd;
        int arp_fd;
        size_t buffer_size, buffer_len, buffer_pos;
@@ -240,6 +230,15 @@ struct dhcp_state {
        unsigned char *clientid;
 
        struct authstate auth;
+       struct arp_statehead arp_states;
+
+       size_t arping_index;
+
+       struct arp_state *arp_ipv4ll;
+       unsigned int conflicts;
+       time_t defend;
+       char randomstate[128];
+       struct in_addr failed;
 };
 
 #define D_STATE(ifp)                                                          \
@@ -286,7 +285,8 @@ void dhcp_stop(struct interface *);
 void dhcp_decline(struct interface *);
 void dhcp_discover(void *);
 void dhcp_inform(struct interface *);
-void dhcp_bind(void *);
+void dhcp_probe(struct interface *);
+void dhcp_bind(struct interface *, struct arp_state *);
 void dhcp_reboot_newopts(struct interface *, unsigned long long);
 void dhcp_close(struct interface *);
 void dhcp_free(struct interface *);
index b62546f702a79d4596a6ddc743aca2fbf3c84b2a..8f9b56c9035205311acfa5aa43d6b8e8bd199d09 100644 (file)
--- a/dhcpcd.c
+++ b/dhcpcd.c
@@ -185,28 +185,15 @@ static void
 handle_exit_timeout(void *arg)
 {
        struct dhcpcd_ctx *ctx;
-       int timeout;
 
        ctx = arg;
        syslog(LOG_ERR, "timed out");
-       if (!(ctx->options & DHCPCD_IPV4) ||
-           !(ctx->options & DHCPCD_TIMEOUT_IPV4LL))
-       {
-               if (ctx->options & DHCPCD_MASTER) {
-                       /* We've timed out, so remove the waitip requirements.
-                        * If the user doesn't like this they can always set
-                        * an infinite timeout. */
-                       ctx->options &=
-                           ~(DHCPCD_WAITIP | DHCPCD_WAITIP4 | DHCPCD_WAITIP6);
-                       dhcpcd_daemonise(ctx);
-               } else
-                       eloop_exit(ctx->eloop, EXIT_FAILURE);
+       if (!ctx->options & DHCPCD_MASTER) {
+               eloop_exit(ctx->eloop, EXIT_FAILURE);
                return;
        }
-       ctx->options &= ~DHCPCD_TIMEOUT_IPV4LL;
-       timeout = (PROBE_NUM * PROBE_MAX) + (PROBE_WAIT * 2) + DHCP_MAX_DELAY;
-       syslog(LOG_WARNING, "allowing %d seconds for IPv4LL timeout", timeout);
-       eloop_timeout_add_sec(ctx->eloop, timeout, handle_exit_timeout, ctx);
+       ctx->options |= DHCPCD_NOWAITIP;
+       dhcpcd_daemonise(ctx);
 }
 
 int
@@ -257,7 +244,7 @@ dhcpcd_daemonise(struct dhcpcd_ctx *ctx)
        int sidpipe[2], fd;
 
        if (ctx->options & DHCPCD_DAEMONISE &&
-           !(ctx->options & DHCPCD_DAEMONISED))
+           !(ctx->options & (DHCPCD_DAEMONISED | DHCPCD_NOWAITIP)))
        {
                if (!dhcpcd_ipwaited(ctx))
                        return 0;
@@ -1709,8 +1696,6 @@ main(int argc, char **argv)
                        if (dhcpcd_daemonise(&ctx))
                                goto exit_success;
                } else if (t > 0) {
-                       if (ctx.options & DHCPCD_IPV4LL)
-                               ctx.options |= DHCPCD_TIMEOUT_IPV4LL;
                        eloop_timeout_add_sec(ctx.eloop, t,
                            handle_exit_timeout, &ctx);
                }
index fee1731875768b866d1d8f68a42d6a6442c611dd..53857854260fc4b8900dc47be33f2920dcca9bd4 100644 (file)
@@ -455,8 +455,9 @@ Suppress any dhcpcd output to the console, except for errors.
 .It Ic reboot Ar seconds
 Allow
 .Ar reboot
-seconds before moving to the DISCOVER phase if we have an old lease to use.
-The default is 5 seconds.
+seconds before moving to the DISCOVER phase if we have an old lease to use
+and moving from DISCOVER to IPv4LL if no reply.
+The default is 10 seconds.
 A setting of 0 seconds causes
 .Nm dhcpcd
 to skip the REBOOT phase and go straight into DISCOVER.
index 82e3e95ae657cbb6246ffb62f663f7c306e19183..63b659e5ac5df1a1664866f512396da0af9954ae 100644 (file)
@@ -63,6 +63,7 @@
 #define DHCPCD_LASTLEASE               (1ULL << 7)
 #define DHCPCD_INFORM                  (1ULL << 8)
 #define DHCPCD_REQUEST                 (1ULL << 9)
+
 #define DHCPCD_IPV4LL                  (1ULL << 10)
 #define DHCPCD_DUID                    (1ULL << 11)
 #define DHCPCD_PERSISTENT              (1ULL << 12)
@@ -76,7 +77,7 @@
 #define DHCPCD_QUIET                   (1ULL << 21)
 #define DHCPCD_BACKGROUND              (1ULL << 22)
 #define DHCPCD_VENDORRAW               (1ULL << 23)
-#define DHCPCD_TIMEOUT_IPV4LL          (1ULL << 24)
+#define DHCPCD_NOWAITIP                        (1ULL << 24) /* To force daemonise */
 #define DHCPCD_WAITIP                  (1ULL << 25)
 #define DHCPCD_SLAACPRIVATE            (1ULL << 26)
 #define DHCPCD_CSR_WARNED              (1ULL << 27)
diff --git a/ipv4.c b/ipv4.c
index 1f4a5d92d0f0526e8584f19a5a1aecfb0b09456f..7d2b99fc906475ff02606b937ae22f25c283229a 100644 (file)
--- a/ipv4.c
+++ b/ipv4.c
@@ -132,6 +132,22 @@ ipv4_iffindaddr(struct interface *ifp,
        return NULL;
 }
 
+struct ipv4_addr *
+ipv4_iffindlladdr(struct interface *ifp)
+{
+       struct ipv4_state *state;
+       struct ipv4_addr *ap;
+
+       state = IPV4_STATE(ifp);
+       if (state) {
+               TAILQ_FOREACH(ap, &state->addrs, next) {
+                       if (IN_LINKLOCAL(htonl(ap->addr.s_addr)))
+                               return ap;
+               }
+       }
+       return NULL;
+}
+
 struct ipv4_addr *
 ipv4_findaddr(struct dhcpcd_ctx *ctx, const struct in_addr *addr)
 {
@@ -767,10 +783,7 @@ ipv4_applyaddr(void *arg)
                                                if (ifn->options->options &
                                                    DHCPCD_ARP)
                                                {
-                                                       nstate->claims = 0;
-                                                       nstate->probes = 0;
-                                                       nstate->conflicts = 0;
-                                                       arp_probe(ifn);
+                                                       dhcp_bind(ifn, NULL);
                                                } else {
                                                        ipv4_addaddr(ifn,
                                                            &nstate->lease);
diff --git a/ipv4.h b/ipv4.h
index 5a4a78ed709c50a0ffff37be227c1ca98e03d85c..5dbfc9cd7e7ad984e529f383b1c17352a1b8ae81 100644 (file)
--- a/ipv4.h
+++ b/ipv4.h
@@ -72,6 +72,7 @@ int ipv4_routedeleted(struct dhcpcd_ctx *, const struct rt *);
 
 struct ipv4_addr *ipv4_iffindaddr(struct interface *,
     const struct in_addr *, const struct in_addr *);
+struct ipv4_addr *ipv4_iffindlladdr(struct interface *);
 struct ipv4_addr *ipv4_findaddr(struct dhcpcd_ctx *, const struct in_addr *);
 void ipv4_handleifa(struct dhcpcd_ctx *, int, struct if_head *, const char *,
     const struct in_addr *, const struct in_addr *, const struct in_addr *);
index 93a09661301cfd7fd69359e861edeb3936d0b7e2..2ce623c5c93274f84b26014404edba4c1d43ae16 100644 (file)
--- a/ipv4ll.c
+++ b/ipv4ll.c
@@ -32,7 +32,7 @@
 #include <syslog.h>
 #include <unistd.h>
 
-#define ELOOP_QUEUE 2
+#define ELOOP_QUEUE 6
 #include "config.h"
 #include "arp.h"
 #include "common.h"
@@ -70,11 +70,11 @@ ipv4ll_make_lease(uint32_t addr)
        return dhcp;
 }
 
-static struct dhcp_message *
-ipv4ll_find_lease(const struct interface *ifp)
+static in_addr_t
+ipv4ll_pick_addr(const struct arp_state *astate)
 {
-       uint32_t addr;
-       struct interface *ifp2;
+       in_addr_t addr;
+       struct interface *ifp;
        const struct dhcp_state *state;
 
        for (;;) {
@@ -83,32 +83,112 @@ ipv4ll_find_lease(const struct interface *ifp)
                 * See ipv4ll_start for why we don't use arc4_random. */
                addr = ntohl(LINKLOCAL_ADDR | ((random() % 0xFD00) + 0x0100));
 
-               state = D_CSTATE(ifp);
+               state = D_CSTATE(astate->iface);
                /* No point using a failed address */
-               if (addr == state->fail.s_addr)
+               if (addr == state->failed.s_addr)
                        continue;
 
                /* Ensure we don't have the address on another interface */
-               TAILQ_FOREACH(ifp2, ifp->ctx->ifaces, next) {
-                       state = D_CSTATE(ifp2);
+               TAILQ_FOREACH(ifp, astate->iface->ctx->ifaces, next) {
+                       state = D_CSTATE(ifp);
                        if (state && state->addr.s_addr == addr)
                                break;
                }
 
                /* Yay, this should be a unique and workable IPv4LL address */
-               if (ifp2 == NULL)
+               if (ifp == NULL)
                        break;
        }
-       return ipv4ll_make_lease(addr);
+       return addr;
 }
 
-void
-ipv4ll_claimed(void *arg)
+static void
+ipv4ll_probed(struct arp_state *astate)
 {
-       struct interface *ifp = arg;
-       struct dhcp_state *state = D_STATE(ifp);
+       struct dhcp_state *state = D_STATE(astate->iface);
+
+       free(state->offer);
+       state->offer = ipv4ll_make_lease(astate->addr.s_addr);
+       if (state->offer == NULL) {
+               syslog(LOG_ERR, "%s: %m", __func__);
+               return;
+       }
+       dhcp_bind(astate->iface, astate);
+}
+
+static void
+ipv4ll_announced(struct arp_state *astate)
+{
+       struct dhcp_state *state = D_STATE(astate->iface);
 
        state->conflicts = 0;
+       /* Need to keep the arp state so we can defend our IP. */
+}
+
+static void
+ipv4ll_probe(void *arg)
+{
+
+       arp_probe(arg);
+}
+
+static void
+ipv4ll_conflicted(struct arp_state *astate, const struct arp_msg *amsg)
+{
+       struct dhcp_state *state = D_STATE(astate->iface);
+       uint32_t fail;
+       char buf[HWADDR_LEN * 3];
+
+       if (state->offer == NULL)
+               return;
+
+       fail = 0;
+       /* RFC 3927 2.2.1, Probe Conflict Detection */
+       if (amsg->sip.s_addr == astate->addr.s_addr ||
+           (amsg->sip.s_addr == 0 && amsg->tip.s_addr == astate->addr.s_addr))
+               fail = astate->addr.s_addr;
+
+       /* RFC 3927 2.5, Conflict Defense */
+       if (IN_LINKLOCAL(htonl(state->addr.s_addr)) &&
+           amsg->sip.s_addr == state->addr.s_addr)
+               fail = state->addr.s_addr;
+
+       if (fail == 0)
+               return;
+
+       state->failed.s_addr = fail;
+       syslog(LOG_ERR, "%s: hardware address %s claims %s",
+           astate->iface->name,
+           hwaddr_ntoa(amsg->sha, astate->iface->hwlen, buf, sizeof(buf)),
+           inet_ntoa(state->failed));
+
+       if (state->failed.s_addr == state->addr.s_addr) {
+               time_t up;
+
+               /* RFC 3927 Section 2.5 */
+               up = uptime();
+               if (state->defend + DEFEND_INTERVAL > up) {
+                       syslog(LOG_WARNING,
+                           "%s: IPv4LL %d second defence failed for %s",
+                           astate->iface->name, DEFEND_INTERVAL,
+                           inet_ntoa(state->addr));
+                       dhcp_drop(astate->iface, "EXPIRE");
+               } else {
+                       syslog(LOG_DEBUG, "%s: defended IPv4LL address %s",
+                           astate->iface->name, inet_ntoa(state->addr));
+                       state->defend = up;
+                       return;
+               }
+       }
+
+       if (++state->conflicts == MAX_CONFLICTS)
+               syslog(LOG_ERR, "%s: failed to acquire an IPv4LL address",
+                   astate->iface->name);
+       astate->addr.s_addr = ipv4ll_pick_addr(astate);
+       eloop_timeout_add_sec(astate->iface->ctx->eloop,
+               state->conflicts > MAX_CONFLICTS ?
+               RATE_LIMIT_INTERVAL : PROBE_WAIT,
+               ipv4ll_probe, astate);
 }
 
 void
@@ -116,7 +196,11 @@ ipv4ll_start(void *arg)
 {
        struct interface *ifp = arg;
        struct dhcp_state *state = D_STATE(ifp);
-       uint32_t addr;
+       struct arp_state *astate;
+       struct ipv4_addr *ap;
+
+       if (state->arp_ipv4ll)
+               return;
 
        /* RFC 3927 Section 2.1 states that the random number generator
         * SHOULD be seeded with a value derived from persistent information
@@ -134,80 +218,40 @@ ipv4ll_start(void *arg)
                initstate(seed, state->randomstate, sizeof(state->randomstate));
        }
 
-       eloop_timeout_delete(ifp->ctx->eloop, NULL, ifp);
-       state->probes = 0;
-       state->claims = 0;
-       if (state->addr.s_addr) {
-               if (IN_LINKLOCAL(htonl(state->addr.s_addr))) {
-                       arp_announce(ifp);
-                       return;
-               }
+       if ((astate = arp_new(ifp)) == NULL)
+               return;
+
+       state->arp_ipv4ll = astate;
+       astate->probed_cb = ipv4ll_probed;
+       astate->announced_cb = ipv4ll_announced;
+       astate->conflicted_cb = ipv4ll_conflicted;
+
+       if (IN_LINKLOCAL(htonl(state->addr.s_addr))) {
+               astate->addr = state->addr;
+               arp_announce(astate);
+               return;
        }
 
-       if (state->offer == NULL)
-               addr = 0;
-       else {
-               addr = state->offer->yiaddr;
+       if (state->offer) {
+               astate->addr.s_addr = state->offer->yiaddr;
                free(state->offer);
+               ap = ipv4_iffindaddr(ifp, &astate->addr, NULL);
+       } else
+               ap = ipv4_iffindlladdr(ifp);
+       if (ap) {
+               astate->addr = ap->addr;
+               ipv4ll_probed(astate);
+               return;
        }
 
-       state->state = DHS_INIT_IPV4LL;
        setstate(state->randomstate);
        /* We maybe rebooting an IPv4LL address. */
-       if (!IN_LINKLOCAL(htonl(addr))) {
+       if (!IN_LINKLOCAL(htonl(astate->addr.s_addr))) {
                syslog(LOG_INFO, "%s: probing for an IPv4LL address",
                    ifp->name);
-               addr = 0;
-       }
-       if (addr == 0)
-               state->offer = ipv4ll_find_lease(ifp);
-       else
-               state->offer = ipv4ll_make_lease(addr);
-       if (state->offer == NULL) {
-               syslog(LOG_ERR, "%s: %m", __func__);
-               return;
-       }
-       state->lease.frominfo = 0;
-       arp_probe(ifp);
-}
-
-void
-ipv4ll_handle_failure(void *arg)
-{
-       struct interface *ifp = arg;
-       struct dhcp_state *state = D_STATE(ifp);
-       time_t up;
-
-       if (state->fail.s_addr == state->addr.s_addr) {
-               /* RFC 3927 Section 2.5 */
-               up = uptime();
-               if (state->defend + DEFEND_INTERVAL > up) {
-                       syslog(LOG_WARNING,
-                           "%s: IPv4LL %d second defence failed for %s",
-                           ifp->name, DEFEND_INTERVAL, inet_ntoa(state->addr));
-                       dhcp_drop(ifp, "EXPIRE");
-               } else {
-                       syslog(LOG_DEBUG, "%s: defended IPv4LL address %s",
-                           ifp->name, inet_ntoa(state->addr));
-                       state->defend = up;
-                       return;
-               }
-       }
-
-       free(state->offer);
-       state->offer = NULL;
-       eloop_timeout_delete(ifp->ctx->eloop, NULL, ifp);
-       if (++state->conflicts >= MAX_CONFLICTS) {
-               syslog(LOG_ERR, "%s: failed to acquire an IPv4LL address",
-                   ifp->name);
-               if (ifp->options->options & DHCPCD_DHCP) {
-                       state->interval = RATE_LIMIT_INTERVAL / 2;
-                       dhcp_discover(ifp);
-               } else
-                       eloop_timeout_add_sec(ifp->ctx->eloop,
-                           RATE_LIMIT_INTERVAL, ipv4ll_start, ifp);
-       } else {
-               eloop_timeout_add_sec(ifp->ctx->eloop,
-                   PROBE_WAIT, ipv4ll_start, ifp);
+               astate->addr.s_addr = INADDR_ANY;
        }
+       if (astate->addr.s_addr == INADDR_ANY)
+               astate->addr.s_addr = ipv4ll_pick_addr(astate);
+       arp_probe(astate);
 }