Drop stale AP nodes from the client list when disconnecting. Fixes some reassoc issues. Signed-off-by: Felix Fietkau <nbd@openwrt.org> --- a/net80211/ieee80211_proto.c +++ b/net80211/ieee80211_proto.c @@ -1348,7 +1348,7 @@ __ieee80211_newstate(struct ieee80211vap IEEE80211_SEND_MGMT(ni, IEEE80211_FC0_SUBTYPE_DISASSOC, IEEE80211_REASON_ASSOC_LEAVE); - ieee80211_sta_leave(ni); + ieee80211_node_leave(ni); break; case IEEE80211_M_HOSTAP: ieee80211_iterate_nodes(&ic->ic_sta, @@ -1358,12 +1358,14 @@ __ieee80211_newstate(struct ieee80211vap break; } goto reset; + case IEEE80211_S_AUTH: case IEEE80211_S_ASSOC: switch (vap->iv_opmode) { case IEEE80211_M_STA: IEEE80211_SEND_MGMT(ni, IEEE80211_FC0_SUBTYPE_DEAUTH, IEEE80211_REASON_AUTH_LEAVE); + ieee80211_node_leave(ni); break; case IEEE80211_M_HOSTAP: ieee80211_iterate_nodes(&ic->ic_sta, @@ -1376,7 +1378,6 @@ __ieee80211_newstate(struct ieee80211vap case IEEE80211_S_SCAN: ieee80211_cancel_scan(vap); goto reset; - case IEEE80211_S_AUTH: reset: ieee80211_reset_bss(vap); break; @@ -1429,10 +1430,12 @@ __ieee80211_newstate(struct ieee80211vap IEEE80211_SCAN_FOREVER, vap->iv_des_nssid, vap->iv_des_ssid, NULL); + else + ieee80211_node_leave(vap->iv_bss); break; case IEEE80211_S_RUN: /* beacon miss */ if (vap->iv_opmode == IEEE80211_M_STA) { - ieee80211_sta_leave(ni); + ieee80211_node_leave(ni); vap->iv_flags &= ~IEEE80211_F_SIBSS; /* XXX */ if (ic->ic_roaming == IEEE80211_ROAMING_AUTO) ieee80211_check_scan(vap, @@ -1511,7 +1514,7 @@ __ieee80211_newstate(struct ieee80211vap IEEE80211_FC0_SUBTYPE_ASSOC_REQ, 0); break; case IEEE80211_S_RUN: - ieee80211_sta_leave(ni); + ieee80211_node_leave(ni); if (ic->ic_roaming == IEEE80211_ROAMING_AUTO) { /* NB: caller specifies ASSOC/REASSOC by arg */ IEEE80211_SEND_MGMT(ni, arg ? @@ -1779,6 +1782,7 @@ ieee80211_newstate(struct ieee80211vap * ieee80211_state_name[nstate], ieee80211_state_name[dstate]); + ieee80211_update_link_status(vap, nstate, ostate); switch (nstate) { case IEEE80211_S_AUTH: case IEEE80211_S_ASSOC: --- a/net80211/ieee80211_linux.c +++ b/net80211/ieee80211_linux.c @@ -233,33 +233,59 @@ ieee80211_vlan_vdetach(struct ieee80211v } void -ieee80211_notify_node_join(struct ieee80211_node *ni, int newassoc) +ieee80211_update_link_status(struct ieee80211vap *vap, int nstate, int ostate) { - struct ieee80211vap *vap = ni->ni_vap; struct net_device *dev = vap->iv_dev; union iwreq_data wreq; + int active; + + if (vap->iv_opmode != IEEE80211_M_STA) + return; + + if (ostate == nstate) + return; + + if (nstate == IEEE80211_S_RUN) + active = 1; + else if ((ostate >= IEEE80211_S_AUTH) && (nstate < ostate)) + active = 0; + else + return; + + if (active && !vap->iv_bss) + return; + + memset(&wreq, 0, sizeof(wreq)); + wreq.ap_addr.sa_family = ARPHRD_ETHER; - if (ni == vap->iv_bss) { - if (newassoc) - netif_carrier_on(dev); - memset(&wreq, 0, sizeof(wreq)); + if (active) { + //netif_carrier_on(vap->iv_dev); IEEE80211_ADDR_COPY(wreq.addr.sa_data, vap->iv_bssid); - wreq.addr.sa_family = ARPHRD_ETHER; -#ifdef ATH_SUPERG_XR - if (vap->iv_xrvap && vap->iv_flags & IEEE80211_F_XR) - dev = vap->iv_xrvap->iv_dev; -#endif - wireless_send_event(dev, SIOCGIWAP, &wreq, NULL); } else { - memset(&wreq, 0, sizeof(wreq)); - IEEE80211_ADDR_COPY(wreq.addr.sa_data, ni->ni_macaddr); - wreq.addr.sa_family = ARPHRD_ETHER; + //netif_carrier_off(vap->iv_dev); + memset(wreq.ap_addr.sa_data, 0, ETHER_ADDR_LEN); + } + wireless_send_event(dev, SIOCGIWAP, &wreq, NULL); +} + +void +ieee80211_notify_node_join(struct ieee80211_node *ni, int newassoc) +{ + struct ieee80211vap *vap = ni->ni_vap; + struct net_device *dev = vap->iv_dev; + union iwreq_data wreq; + + if (ni == vap->iv_bss) + return; + + memset(&wreq, 0, sizeof(wreq)); + IEEE80211_ADDR_COPY(wreq.addr.sa_data, ni->ni_macaddr); + wreq.addr.sa_family = ARPHRD_ETHER; #ifdef ATH_SUPERG_XR - if (vap->iv_xrvap && vap->iv_flags & IEEE80211_F_XR) - dev = vap->iv_xrvap->iv_dev; + if (vap->iv_xrvap && vap->iv_flags & IEEE80211_F_XR) + dev = vap->iv_xrvap->iv_dev; #endif - wireless_send_event(dev, IWEVREGISTERED, &wreq, NULL); - } + wireless_send_event(dev, IWEVREGISTERED, &wreq, NULL); } void @@ -269,18 +295,14 @@ ieee80211_notify_node_leave(struct ieee8 struct net_device *dev = vap->iv_dev; union iwreq_data wreq; - if (ni == vap->iv_bss) { - netif_carrier_off(dev); - memset(wreq.ap_addr.sa_data, 0, ETHER_ADDR_LEN); - wreq.ap_addr.sa_family = ARPHRD_ETHER; - wireless_send_event(dev, SIOCGIWAP, &wreq, NULL); - } else { - /* fire off wireless event station leaving */ - memset(&wreq, 0, sizeof(wreq)); - IEEE80211_ADDR_COPY(wreq.addr.sa_data, ni->ni_macaddr); - wreq.addr.sa_family = ARPHRD_ETHER; - wireless_send_event(dev, IWEVEXPIRED, &wreq, NULL); - } + if (ni == vap->iv_bss) + return; + + /* fire off wireless event station leaving */ + memset(&wreq, 0, sizeof(wreq)); + IEEE80211_ADDR_COPY(wreq.addr.sa_data, ni->ni_macaddr); + wreq.addr.sa_family = ARPHRD_ETHER; + wireless_send_event(dev, IWEVEXPIRED, &wreq, NULL); } void --- a/net80211/ieee80211_node.c +++ b/net80211/ieee80211_node.c @@ -2332,6 +2332,7 @@ ieee80211_node_leave(struct ieee80211_no count_suppchans(ic, ni, -1); IEEE80211_UNLOCK_IRQ(ic); +done: /* * Cleanup station state. In particular clear various * state that might otherwise be reused if the node @@ -2339,7 +2340,7 @@ ieee80211_node_leave(struct ieee80211_no * (and memory is reclaimed). */ ieee80211_sta_leave(ni); -done: + /* Run a cleanup */ #ifdef IEEE80211_DEBUG_REFCNT ic->ic_node_cleanup_debug(ni, __func__, __LINE__); --- a/net80211/ieee80211_node.h +++ b/net80211/ieee80211_node.h @@ -60,7 +60,7 @@ #define IEEE80211_INACT_PROBE (30/IEEE80211_INACT_WAIT) /* probe */ #define IEEE80211_INACT_SCAN (300/IEEE80211_INACT_WAIT) /* scanned */ -#define IEEE80211_TRANS_WAIT 5 /* mgt frame tx timer (secs) */ +#define IEEE80211_TRANS_WAIT 300 /* mgt frame tx timer (msecs) */ #define IEEE80211_NODE_HASHSIZE 32 /* simple hash is enough for variation of macaddr */ --- a/net80211/ieee80211_output.c +++ b/net80211/ieee80211_output.c @@ -2141,7 +2141,7 @@ ieee80211_send_mgmt(struct ieee80211_nod ieee80211_mgmt_output(ieee80211_ref_node(ni), skb, type); if (timer) - mod_timer(&vap->iv_mgtsend, jiffies + timer * HZ); + mod_timer(&vap->iv_mgtsend, jiffies + msecs_to_jiffies(timer)); return 0; bad: return ret; --- a/net80211/ieee80211_wireless.c +++ b/net80211/ieee80211_wireless.c @@ -514,8 +514,9 @@ ieee80211_ioctl_siwap(struct net_device vap->iv_flags |= IEEE80211_F_DESBSSID; IEEE80211_ADDR_COPY(vap->iv_des_bssid, &ap_addr->sa_data); - if (IS_UP_AUTO(vap)) + if (IS_UP(vap->iv_dev)) { ieee80211_new_state(vap, IEEE80211_S_SCAN, 0); + } } return 0; } --- a/net80211/ieee80211_linux.h +++ b/net80211/ieee80211_linux.h @@ -643,6 +643,7 @@ void ieee80211_vlan_vdetach(struct ieee8 #define free_netdev(dev) kfree(dev) #endif +void ieee80211_update_link_status(struct ieee80211vap *vap, int nstate, int ostate); void ieee80211_ioctl_vattach(struct ieee80211vap *); void ieee80211_ioctl_vdetach(struct ieee80211vap *); struct ifreq;