]> git.ipfire.org Git - thirdparty/dhcpcd.git/commitdiff
IPv4LL: Preserve address when carrier drops
authorRoy Marples <roy@marples.name>
Thu, 17 Jan 2019 11:44:08 +0000 (11:44 +0000)
committerRoy Marples <roy@marples.name>
Thu, 17 Jan 2019 11:44:08 +0000 (11:44 +0000)
Apple Conformance testing now requires this.
However, if associated SSID changes on wireless networks,
any remembered address is reset.

src/dhcpcd.c
src/ipv4ll.c
src/ipv4ll.h

index 6c46820bd726f4d181e410c6cda606e18ca7508a..616a2ee05b4f0aaed9aaaf92add37dceb0cc2557 100644 (file)
@@ -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");
index 103bce0ae1b453b1d6b69c589a50ae41b5c09d0f..4d59fff29e08db8481b6b18fcad2f637f44b53a4 100644 (file)
@@ -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
index 6ead6ea5e9cf09f141dcef6d0d5c1756f95dfc54..95a6a524f98903f78ed5d906803dc72458768c9e 100644 (file)
 #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)