From: Roy Marples Date: Thu, 17 Jan 2019 11:44:08 +0000 (+0000) Subject: IPv4LL: Preserve address when carrier drops X-Git-Tag: v7.1.0~3 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=466fddaeedcdd6fd23b0b33f260243867bcb11bd;p=thirdparty%2Fdhcpcd.git IPv4LL: Preserve address when carrier drops Apple Conformance testing now requires this. However, if associated SSID changes on wireless networks, any remembered address is reset. --- diff --git a/src/dhcpcd.c b/src/dhcpcd.c index 6c46820b..616a2ee0 100644 --- a/src/dhcpcd.c +++ b/src/dhcpcd.c @@ -759,12 +759,16 @@ dhcpcd_handlecarrier(struct dhcpcd_ctx *ctx, int carrier, unsigned int flags, #endif memcpy(ossid, ifp->ssid, ifp->ssid_len); if_getssid(ifp); -#ifdef NOCARRIER_PRESERVE_IP + /* If we changed SSID network, drop leases */ if (ifp->ssid_len != olen || memcmp(ifp->ssid, ossid, ifp->ssid_len)) + { +#ifdef NOCARRIER_PRESERVE_IP dhcpcd_drop(ifp, 0); #endif + ipv4ll_reset(ifp); + } } dhcpcd_initstate(ifp, 0); script_runreason(ifp, "CARRIER"); diff --git a/src/ipv4ll.c b/src/ipv4ll.c index 103bce0a..4d59fff2 100644 --- a/src/ipv4ll.c +++ b/src/ipv4ll.c @@ -318,7 +318,8 @@ ipv4ll_conflicted(struct arp_state *astate, const struct arp_msg *amsg) if (++state->conflicts == MAX_CONFLICTS) logerr("%s: failed to acquire an IPv4LL address", ifp->name); - astate->addr.s_addr = ipv4ll_pickaddr(astate); + state->pickedaddr.s_addr = ipv4ll_pickaddr(astate); + astate->addr = state->pickedaddr; eloop_timeout_add_sec(ifp->ctx->eloop, state->conflicts >= MAX_CONFLICTS ? RATE_LIMIT_INTERVAL : PROBE_WAIT, @@ -331,7 +332,7 @@ ipv4ll_arpfree(struct arp_state *astate) struct ipv4ll_state *state; state = IPV4LL_STATE(astate->iface); - if (state->arp == astate) + if (state != NULL && state->arp == astate) state->arp = NULL; } @@ -353,14 +354,11 @@ ipv4ll_start(void *arg) } } - if (state->arp != NULL) - return; - /* RFC 3927 Section 2.1 states that the random number generator * SHOULD be seeded with a value derived from persistent information * such as the IEEE 802 MAC address so that it usually picks * the same address without persistent storage. */ - if (state->conflicts == 0) { + if (!state->seeded) { unsigned int seed; char *orig; @@ -380,8 +378,11 @@ ipv4ll_start(void *arg) /* Set back the original state until we need the seeded one. */ setstate(ifp->ctx->randomstate); + state->seeded = true; } + if (state->arp != NULL) + return; if ((astate = arp_new(ifp, NULL)) == NULL) return; @@ -391,8 +392,15 @@ ipv4ll_start(void *arg) astate->conflicted_cb = ipv4ll_conflicted; astate->free_cb = ipv4ll_arpfree; + /* Find the previosuly used address. */ + if (state->pickedaddr.s_addr != INADDR_ANY) + ia = ipv4_iffindaddr(ifp, &state->pickedaddr, NULL); + else + ia = NULL; + /* Find an existing IPv4LL address and ensure we can work with it. */ - ia = ipv4_iffindlladdr(ifp); + if (ia == NULL) + ia = ipv4_iffindlladdr(ifp); #ifdef IN_IFF_TENTATIVE if (ia != NULL && ia->addr_flags & IN_IFF_DUPLICATED) { @@ -402,7 +410,7 @@ ipv4ll_start(void *arg) #endif if (ia != NULL) { - astate->addr = ia->addr; + state->pickedaddr = astate->addr = ia->addr; #ifdef IN_IFF_TENTATIVE if (ia->addr_flags & (IN_IFF_TENTATIVE | IN_IFF_DETACHED)) { loginfox("%s: waiting for DAD to complete on %s", @@ -416,7 +424,9 @@ ipv4ll_start(void *arg) } loginfox("%s: probing for an IPv4LL address", ifp->name); - astate->addr.s_addr = ipv4ll_pickaddr(astate); + if (state->pickedaddr.s_addr == INADDR_ANY) + state->pickedaddr.s_addr = ipv4ll_pickaddr(astate); + astate->addr = state->pickedaddr; #ifdef IN_IFF_TENTATIVE ipv4ll_probed(astate); #else @@ -424,56 +434,83 @@ ipv4ll_start(void *arg) #endif } +static void +ipv4ll_freearp(struct interface *ifp) +{ + struct ipv4ll_state *state; + + state = IPV4LL_STATE(ifp); + if (state == NULL || state->arp == NULL) + return; + + eloop_timeout_delete(ifp->ctx->eloop, NULL, state->arp); + arp_free(state->arp); + state->arp = NULL; +} + void -ipv4ll_freedrop(struct interface *ifp, int drop) +ipv4ll_drop(struct interface *ifp) { struct ipv4ll_state *state; - int dropped; + bool dropped = false; + struct ipv4_state *istate; assert(ifp != NULL); - state = IPV4LL_STATE(ifp); - dropped = 0; - /* Free ARP state first because ipv4_deladdr might also ... */ - if (state && state->arp) { - eloop_timeout_delete(ifp->ctx->eloop, NULL, state->arp); - arp_free(state->arp); - state->arp = NULL; - } + ipv4ll_freearp(ifp); - if (drop && (ifp->options->options & DHCPCD_NODROP) != DHCPCD_NODROP) { - struct ipv4_state *istate; +#ifndef IN_IFF_TENATIVE + if ((ifp->options->options & DHCPCD_NODROP) == DHCPCD_NODROP) +#endif + return; - if (state && state->addr != NULL) { - ipv4_deladdr(state->addr, 1); - state->addr = NULL; - dropped = 1; - } + state = IPV4LL_STATE(ifp); + if (state && state->addr != NULL) { + ipv4_deladdr(state->addr, 1); + state->addr = NULL; + dropped = true; + } - /* Free any other link local addresses that might exist. */ - if ((istate = IPV4_STATE(ifp)) != NULL) { - struct ipv4_addr *ia, *ian; + /* Free any other link local addresses that might exist. */ + if ((istate = IPV4_STATE(ifp)) != NULL) { + struct ipv4_addr *ia, *ian; - TAILQ_FOREACH_SAFE(ia, &istate->addrs, next, ian) { - if (IN_LINKLOCAL(ntohl(ia->addr.s_addr))) { - ipv4_deladdr(ia, 0); - dropped = 1; - } + TAILQ_FOREACH_SAFE(ia, &istate->addrs, next, ian) { + if (IN_LINKLOCAL(ntohl(ia->addr.s_addr))) { + ipv4_deladdr(ia, 0); + dropped = true; } } } - if (state) { - free(state); - ifp->if_data[IF_DATA_IPV4LL] = NULL; - - if (dropped) { - rt_build(ifp->ctx, AF_INET); - script_runreason(ifp, "IPV4LL"); - } + if (dropped) { + rt_build(ifp->ctx, AF_INET); + script_runreason(ifp, "IPV4LL"); } } +void +ipv4ll_reset(struct interface *ifp) +{ + struct ipv4ll_state *state = IPV4LL_STATE(ifp); + + if (state == NULL) + return; + state->pickedaddr.s_addr = INADDR_ANY; + state->seeded = false; +} + +void +ipv4ll_free(struct interface *ifp) +{ + + assert(ifp != NULL); + + ipv4ll_freearp(ifp); + free(IPV4LL_STATE(ifp)); + ifp->if_data[IF_DATA_IPV4LL] = NULL; +} + /* This may cause issues in BSD systems, where running as a single dhcpcd * daemon would solve this issue easily. */ #ifdef HAVE_ROUTE_METRIC diff --git a/src/ipv4ll.h b/src/ipv4ll.h index 6ead6ea5..95a6a524 100644 --- a/src/ipv4ll.h +++ b/src/ipv4ll.h @@ -40,11 +40,13 @@ #include "arp.h" struct ipv4ll_state { + struct in_addr pickedaddr; struct ipv4_addr *addr; struct arp_state *arp; unsigned int conflicts; struct timespec defend; char randomstate[128]; + bool seeded; uint8_t down; }; @@ -66,9 +68,9 @@ void ipv4ll_handle_failure(void *); int ipv4ll_recvrt(int, const struct rt *); #endif -#define ipv4ll_free(ifp) ipv4ll_freedrop((ifp), 0); -#define ipv4ll_drop(ifp) ipv4ll_freedrop((ifp), 1); -void ipv4ll_freedrop(struct interface *, int); +void ipv4ll_reset(struct interface *); +void ipv4ll_drop(struct interface *); +void ipv4ll_free(struct interface *); #else #define IPV4LL_STATE_RUNNING(ifp) (0) #define ipv4ll_subnetroute(route, ifp) (0)