]> git.ipfire.org Git - thirdparty/dhcpcd.git/commitdiff
Linux: Support wireless IP roaming
authorRoy Marples <roy@marples.name>
Sat, 12 Dec 2020 13:12:26 +0000 (13:12 +0000)
committerRoy Marples <roy@marples.name>
Sat, 12 Dec 2020 13:20:19 +0000 (13:20 +0000)
This is achieved by checking that the interface is wireless,
IFF_UP and IFF_LOWER_UP are present, but IFF_RUNNING is missing.

This gives exactly the same support as modern NetBSD when carrier
loss is detected, but without the address verifications when the
carrier comes back as that needs to be handled in the kernel.

While IP setup is maintained, other configuration data is discarded.
Note that this should be improved in the future.

Thanks to Boris Krasnovskiy <borkra@gmail.com> for helping with this.

src/dhcpcd.c
src/if-bsd.c
src/if-linux.c
src/if-sun.c
src/if.h
src/ipv6nd.c

index 306d1d00f0a20df6ac8d0ea1476c7fadda86cb56..68c826721bd30c4522a367358031c6168938cece 100644 (file)
@@ -695,35 +695,82 @@ dhcpcd_reportssid(struct interface *ifp)
        loginfox("%s: connected to Access Point: %s", ifp->name, pssid);
 }
 
+static void
+dhcpcd_nocarrier_roaming(struct interface *ifp)
+{
+
+       loginfox("%s: carrier lost - roaming", ifp->name);
+
+       /*
+        * XXX We should pass something like NOCARRIER_ROAMING
+        * and set if_up=true; ifdown=false; so that the hook scripts
+        * can make a decision to keep or discard the interface information.
+        *
+        * Currently they discard it (no carrier after all) which is
+        * generally fine as new connections won't work and current
+        * connections try to chug along as best as.
+        * dhcpcd has been doing this since NetBSD-7 at least.
+        *
+        * However, for slow roaming this is poor for say web browsing
+        * as new lookups will fail quickly giving a poor user experience.
+        * We should improve this, but the hooks will require some work first
+        * as we need to introduce a mechanism to sort interfaces by
+        * carrier > roaming > nocarrier. Then the hooks know in which
+        * order to apply their data, if at all.
+        * This probably should be a user toggle.
+        */
+       script_runreason(ifp, "NOCARRIER");
+
+#ifdef ARP
+       arp_drop(ifp);
+#endif
+#ifdef INET
+       dhcp_abort(ifp);
+#endif
+#ifdef DHCP6
+       dhcp6_abort(ifp);
+#endif
+}
+
 void
 dhcpcd_handlecarrier(struct interface *ifp, int carrier, unsigned int flags)
 {
        bool was_link_up = if_is_link_up(ifp);
+       bool was_roaming = if_roaming(ifp);
 
        ifp->carrier = carrier;
        ifp->flags = flags;
 
        if (!if_is_link_up(ifp)) {
-               if (!was_link_up || !ifp->active)
+               if (!ifp->active || (!was_link_up && !was_roaming))
                        return;
-               loginfox("%s: carrier lost", ifp->name);
-               script_runreason(ifp, "NOCARRIER");
+
+               /*
+                * If the interface is roaming (generally on wireless)
+                * then while we are not up, we are not down either.
+                * Preserve the network state until we either disconnect
+                * or re-connect.
+                */
+               if (if_roaming(ifp)) {
+                       dhcpcd_nocarrier_roaming(ifp);
+                       return;
+               }
+
 #ifdef NOCARRIER_PRESERVE_IP
                if (ifp->flags & IFF_UP &&
                    !(ifp->options->options & DHCPCD_ANONYMOUS))
                {
-#ifdef ARP
-                       arp_drop(ifp);
-#endif
-#ifdef INET
-                       dhcp_abort(ifp);
-#endif
-#ifdef DHCP6
-                       dhcp6_abort(ifp);
-#endif
-               } else
+                       /* This OS supports the roaming concept on any
+                        * interface. */
+                       dhcpcd_nocarrier_roaming(ifp);
+                       return;
+               }
 #endif
-                       dhcpcd_drop(ifp, 0);
+
+               loginfox("%s: carrier lost", ifp->name);
+               script_runreason(ifp, "NOCARRIER");
+               dhcpcd_drop(ifp, 0);
+
                if (ifp->options->options & DHCPCD_ANONYMOUS) {
                        bool is_up = ifp->flags & IFF_UP;
 
@@ -734,6 +781,7 @@ dhcpcd_handlecarrier(struct interface *ifp, int carrier, unsigned int flags)
                        if (is_up)
                                if_up(ifp);
                }
+
                return;
        }
 
@@ -774,9 +822,7 @@ dhcpcd_handlecarrier(struct interface *ifp, int carrier, unsigned int flags)
                    memcmp(ifp->ssid, ossid, ifp->ssid_len)) && ifp->active)
                {
                        dhcpcd_reportssid(ifp);
-#ifdef NOCARRIER_PRESERVE_IP
                        dhcpcd_drop(ifp, 0);
-#endif
 #ifdef IPV4LL
                        ipv4ll_reset(ifp);
 #endif
@@ -788,17 +834,17 @@ dhcpcd_handlecarrier(struct interface *ifp, int carrier, unsigned int flags)
 
        dhcpcd_initstate(ifp, 0);
        script_runreason(ifp, "CARRIER");
+
 #ifdef INET6
-#ifdef NOCARRIER_PRESERVE_IP
        /* Set any IPv6 Routers we remembered to expire faster than they
         * would normally as we maybe on a new network. */
        ipv6nd_startexpire(ifp);
-#endif
 #ifdef IPV6_MANAGETEMPADDR
        /* RFC4941 Section 3.5 */
        ipv6_regentempaddrs(ifp);
 #endif
 #endif
+
        dhcpcd_startinterface(ifp);
 }
 
index 94f58b319d06fb56c68a4f2b1900044455a3268d..62e4a83c98ec9fa6e2e4de4ed20249b43e265a8e 100644 (file)
@@ -410,6 +410,13 @@ if_carrier(struct interface *ifp, const void *ifadata)
        return LINK_DOWN;
 }
 
+bool
+if_roaming(__unused struct interface *ifp)
+{
+
+       return false;
+}
+
 static void
 if_linkaddr(struct sockaddr_dl *sdl, const struct interface *ifp)
 {
index eaa5a4d69e47406077b147d01874f00867cedb65..69f75e12c0274a41348438fe58a686980ff65046 100644 (file)
@@ -515,6 +515,21 @@ if_carrier(struct interface *ifp, __unused const void *ifadata)
        return ifp->flags & IFF_RUNNING ? LINK_UP : LINK_DOWN;
 }
 
+bool
+if_roaming(struct interface *ifp)
+{
+
+#ifdef IFF_LOWER_UP
+       if (!ifp->wireless ||
+           ifp->flags & IFF_RUNNING ||
+           (ifp->flags & (IFF_UP | IFF_LOWER_UP)) != (IFF_UP | IFF_LOWER_UP))
+               return false;
+       return true;
+#else
+       return false;
+#endif
+}
+
 int
 if_getnetlink(struct dhcpcd_ctx *ctx, struct iovec *iov, int fd, int flags,
     int (*cb)(struct dhcpcd_ctx *, void *, struct nlmsghdr *), void *cbarg)
index 51c0cfd5ee2f79e36f72aefd0aa080434f8741be..019076e78ced3091712a1f5a03bf8e13520e0326 100644 (file)
@@ -245,6 +245,13 @@ err:
        return LINK_UNKNOWN;
 }
 
+bool
+if_roaming(__unused struct interface *ifp)
+{
+
+       return false;
+}
+
 int
 if_mtu_os(const struct interface *ifp)
 {
index 8474deb70d71a0093657383e98cd658a3dd3821f..9a2f262ac3db86d5987d32873df73b37f9a57b6d 100644 (file)
--- a/src/if.h
+++ b/src/if.h
@@ -161,6 +161,7 @@ int if_domtu(const struct interface *, short int);
 #define if_getmtu(ifp) if_domtu((ifp), 0)
 #define if_setmtu(ifp, mtu) if_domtu((ifp), (mtu))
 int if_carrier(struct interface *, const void *);
+bool if_roaming(struct interface *);
 
 #ifdef ALIAS_ADDR
 int if_makealias(char *, size_t, const char *, int);
index f0a79d5145b46e3f10da363b4a8d2b56e143aad4..b9ba57f476af84149086b398f20d399761807d8b 100644 (file)
@@ -1155,7 +1155,6 @@ ipv6nd_handlera(struct dhcpcd_ctx *ctx,
                return;
        }
 
-#ifdef NOCARRIER_PRESERVE_IP
        /*
         * Because we preserve RA's and expire them quickly after
         * carrier up, it's important to reset the kernels notion of
@@ -1168,7 +1167,6 @@ ipv6nd_handlera(struct dhcpcd_ctx *ctx,
        }
        if (rap != NULL && rap->willexpire)
                ipv6nd_applyra(ifp);
-#endif
 
        TAILQ_FOREACH(rap, ctx->ra_routers, next) {
                if (ifp == rap->iface &&