From 786bf0e50e69a063a953e4c0717519c37c42ec21 Mon Sep 17 00:00:00 2001 From: Roy Marples Date: Tue, 8 Oct 2019 11:29:15 +0100 Subject: [PATCH] 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. --- src/ipv4ll.c | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) 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; } -- 2.47.2