From: Roy Marples Date: Tue, 8 Oct 2019 10:29:15 +0000 (+0100) Subject: IPv4LL: Pick a different address at conflict X-Git-Tag: v8.1.0~19 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=079a79506561768bb07022b69332fe2f47fde4f7;p=thirdparty%2Fdhcpcd.git IPv4LL: Pick a different address at conflict 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. --- diff --git a/src/ipv4ll.c b/src/ipv4ll.c index 09d1166f..42a6a784 100644 --- a/src/ipv4ll.c +++ b/src/ipv4ll.c @@ -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; }