]> git.ipfire.org Git - thirdparty/dhcpcd.git/commitdiff
IPv4LL: Pick a different address at conflict
authorRoy Marples <roy@marples.name>
Tue, 8 Oct 2019 10:29:15 +0000 (11:29 +0100)
committerRoy Marples <roy@marples.name>
Tue, 8 Oct 2019 10:29:15 +0000 (11:29 +0100)
The start function will be called at carrier up and may rightly
pick the last assigned address. So, we need to ensure we pick the
next address we want to try at any conflict.

If any conflict, cancel any ARP state.

src/ipv4ll.c

index 09d1166f50b4cf5e00d7076407b6275c0176f7cc..42a6a78416a7f9ecef5ce7e1d35eff3117fb1c6f 100644 (file)
@@ -198,12 +198,16 @@ ipv4ll_not_found(struct interface *ifp)
        struct ipv4_addr *ia;
 #ifdef KERNEL_RFC5227
        struct arp_state *astate;
+       bool new_addr;
 #endif
 
        state = IPV4LL_STATE(ifp);
        assert(state != NULL);
 
        ia = ipv4_iffindaddr(ifp, &state->pickedaddr, &inaddr_llmask);
+#ifdef KERNEL_RFC5227
+       new_addr = ia == NULL;
+#endif
 #ifdef IN_IFF_NOTREADY
        if (ia == NULL || ia->addr_flags & IN_IFF_NOTREADY)
 #endif
@@ -233,11 +237,13 @@ test:
        }
        rt_build(ifp->ctx, AF_INET);
 #ifdef KERNEL_RFC5227
-       astate = arp_new(ifp, &ia->addr);
-       if (astate != NULL) {
-               astate->announced_cb = ipv4ll_announced_arp;
-               astate->free_cb = ipv4ll_arpfree;
-               arp_announce(astate);
+       if (!new_addr) {
+               astate = arp_new(ifp, &ia->addr);
+               if (astate != NULL) {
+                       astate->announced_cb = ipv4ll_announced_arp;
+                       astate->free_cb = ipv4ll_arpfree;
+                       arp_announce(astate);
+               }
        }
 #else
        arp_announce(state->arp);
@@ -266,7 +272,7 @@ ipv4ll_found(struct interface *ifp)
        if (++state->conflicts == MAX_CONFLICTS)
                logerrx("%s: failed to acquire an IPv4LL address",
                    ifp->name);
-       state->pickedaddr.s_addr = INADDR_ANY;
+       state->pickedaddr.s_addr = ipv4ll_pickaddr(ifp);
        eloop_timeout_add_sec(ifp->ctx->eloop,
            state->conflicts >= MAX_CONFLICTS ?
            RATE_LIMIT_INTERVAL : PROBE_WAIT,
@@ -278,11 +284,14 @@ ipv4ll_defend_failed(struct interface *ifp)
 {
        struct ipv4ll_state *state = IPV4LL_STATE(ifp);
 
+       if (state->arp != NULL)
+               arp_cancel(state->arp);
        ipv4_deladdr(state->addr, 1);
        state->down = true;
        state->addr = NULL;
        rt_build(ifp->ctx, AF_INET);
        script_runreason(ifp, "IPV4LL");
+       state->pickedaddr.s_addr = ipv4ll_pickaddr(ifp);
        ipv4ll_start1(ifp, state->arp);
 }
 
@@ -576,7 +585,12 @@ ipv4ll_handleifa(int cmd, struct ipv4_addr *ia, pid_t pid)
                ipv4ll_not_found(ifp);
        else if (ia->addr_flags & IN_IFF_DUPLICATED) {
                logerrx("%s: DAD detected %s", ifp->name, ia->saddr);
+#ifdef KERNEL_RFC5227
+               arp_freeaddr(ifp, &ia->addr);
+#endif
                ipv4_deladdr(ia, 1);
+               state->addr = NULL;
+               rt_build(ifp->ctx, AF_INET);
                ipv4ll_found(ifp);
                return NULL;
        }