# HG changeset patch # User Roy Marples # Date 1607778746 0 # Node ID e65d193a1960c446aace60a3ecf1af732c3ca47d # Parent 99bfd2eb77ab7f3faa7fc9c83744d6009b1c7370 Linux: Support wireless IP roaming 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 for helping with this. diff -r 99bfd2eb77ab -r e65d193a1960 src/dhcpcd.c --- a/src/dhcpcd.c Wed Dec 09 11:15:30 2020 +0000 +++ b/src/dhcpcd.c Sat Dec 12 13:12:26 2020 +0000 @@ -695,35 +695,82 @@ 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); + /* This OS supports the roaming concept on any + * interface. */ + dhcpcd_nocarrier_roaming(ifp); + return; + } #endif -#ifdef DHCP6 - dhcp6_abort(ifp); -#endif - } else -#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 @@ if (is_up) if_up(ifp); } + return; } @@ -774,9 +822,7 @@ 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_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); } diff -r 99bfd2eb77ab -r e65d193a1960 src/if-bsd.c --- a/src/if-bsd.c Wed Dec 09 11:15:30 2020 +0000 +++ b/src/if-bsd.c Sat Dec 12 13:12:26 2020 +0000 @@ -410,6 +410,13 @@ return LINK_DOWN; } +bool +if_roaming(__unused struct interface *ifp) +{ + + return false; +} + static void if_linkaddr(struct sockaddr_dl *sdl, const struct interface *ifp) { diff -r 99bfd2eb77ab -r e65d193a1960 src/if-linux.c --- a/src/if-linux.c Wed Dec 09 11:15:30 2020 +0000 +++ b/src/if-linux.c Sat Dec 12 13:12:26 2020 +0000 @@ -515,6 +515,21 @@ 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) diff -r 99bfd2eb77ab -r e65d193a1960 src/if-sun.c --- a/src/if-sun.c Wed Dec 09 11:15:30 2020 +0000 +++ b/src/if-sun.c Sat Dec 12 13:12:26 2020 +0000 @@ -245,6 +245,13 @@ return LINK_UNKNOWN; } +bool +if_roaming(__unused struct interface *ifp) +{ + + return false; +} + int if_mtu_os(const struct interface *ifp) { diff -r 99bfd2eb77ab -r e65d193a1960 src/if.h --- a/src/if.h Wed Dec 09 11:15:30 2020 +0000 +++ b/src/if.h Sat Dec 12 13:12:26 2020 +0000 @@ -161,6 +161,7 @@ #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); diff -r 99bfd2eb77ab -r e65d193a1960 src/ipv6nd.c --- a/src/ipv6nd.c Wed Dec 09 11:15:30 2020 +0000 +++ b/src/ipv6nd.c Sat Dec 12 13:12:26 2020 +0000 @@ -1155,7 +1155,6 @@ 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 @@ } if (rap != NULL && rap->willexpire) ipv6nd_applyra(ifp); -#endif TAILQ_FOREACH(rap, ctx->ra_routers, next) { if (ifp == rap->iface &&