git: d9f59799fc3e - main - LinuxKPI: 802.11: rework sta state machine compatibility
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Tue, 22 Mar 2022 18:52:52 UTC
The branch main has been updated by bz: URL: https://cgit.FreeBSD.org/src/commit/?id=d9f59799fc3e7940c47aa674c25994e640eae45e commit d9f59799fc3e7940c47aa674c25994e640eae45e Author: Bjoern A. Zeeb <bz@FreeBSD.org> AuthorDate: 2022-03-22 18:34:13 +0000 Commit: Bjoern A. Zeeb <bz@FreeBSD.org> CommitDate: 2022-03-22 18:51:43 +0000 LinuxKPI: 802.11: rework sta state machine compatibility Rework the state machine parts for various reasons: (1) to add sta tracing to be able to better follow ni and lsta state (2) factor out/implement lkpi_lsta_remove() to unlink the lsta and free the ni reference. (3) avoid calling lkpi_disassoc() when you would think you should as changing BSS_CHANGED_ASSOC setting vif->bss_conf.assoc to false triggers a sta removal from firmware in iwlwifi which then triggers follow-up errors. I do not understand why they use flags and state and ?? in parallel (too many options and ways to do things?). (4) when "roaming" (or being disassoc/deauth) from an AP both net80211 and apparently so mac80211 re-start with a new node/sta. This results in us losing one or the other state in the compat layer or not updating firmware appropriately. To resolve this make use of (a) the newly introduced (*iv_update_bss)() and (b) always tear a station down to "State 1" (INIT/SCAN/pre-AUTH) and only if needed re-create the new one (if we go to AUTH). A slightly earlier version has survived a night of wpa_supplicant and hostapd fighting each other over disassoc and deauth and re-associating/authorizing. While there update a few comments and typos and do a few minor auxiliary changes which are hard or not worth to extract. Sponsored by: The FreeBSD Foundation MFC after: 3 days --- sys/compat/linuxkpi/common/src/linux_80211.c | 574 ++++++++++++++++++--- sys/compat/linuxkpi/common/src/linux_80211.h | 6 +- .../linuxkpi/common/src/linux_80211_macops.c | 4 +- 3 files changed, 502 insertions(+), 82 deletions(-) diff --git a/sys/compat/linuxkpi/common/src/linux_80211.c b/sys/compat/linuxkpi/common/src/linux_80211.c index 5721ff1127ee..1d3e6de375dc 100644 --- a/sys/compat/linuxkpi/common/src/linux_80211.c +++ b/sys/compat/linuxkpi/common/src/linux_80211.c @@ -95,6 +95,7 @@ SYSCTL_INT(_compat_linuxkpi, OID_AUTO, debug_80211, CTLFLAG_RWTUN, #define D80211_TRACE_RX_BEACONS 0x4000 #define D80211_TRACEX (D80211_TRACE_TX|D80211_TRACE_RX) #define D80211_TRACEX_DUMP (D80211_TRACE_TX_DUMP|D80211_TRACE_RX_DUMP) +#define D80211_TRACE_STA 0x10000 #define UNIMPLEMENTED if (debug_80211 & D80211_TODO) \ printf("XXX-TODO %s:%d: UNIMPLEMENTED\n", __func__, __LINE__) #define TRACEOK() if (debug_80211 & D80211_TRACEOK) \ @@ -139,6 +140,42 @@ static struct lkpi_sta *lkpi_find_lsta_by_ni(struct lkpi_vif *, static void lkpi_80211_txq_task(void *, int); static void lkpi_ieee80211_free_skb_mbuf(void *); +static void +lkpi_dump_lsta(struct lkpi_sta *lsta, const char *_f, int _l) +{ + + if ((debug_80211 & D80211_TRACE_STA) == 0) + return; + if (lsta == NULL) + return; + + printf("%s:%d lsta %p ni %p sta %p\n", + _f, _l, lsta, lsta->ni, &lsta->sta); + if (lsta->ni != NULL) + ieee80211_dump_node(NULL, lsta->ni); + printf("\ttxq_task txq len %d mtx\n", mbufq_len(&lsta->txq)); + printf("\tkc %p state %d added_to_drv %d in_mgd %d\n", + lsta->kc, lsta->state, lsta->added_to_drv, lsta->in_mgd); +} + +static void +lkpi_lsta_remove(struct lkpi_sta *lsta, struct lkpi_vif *lvif) +{ + struct ieee80211_node *ni; + + ni = lsta->ni; + + LKPI_80211_LVIF_LOCK(lvif); + TAILQ_REMOVE(&lvif->lsta_head, lsta, lsta_entry); + LKPI_80211_LVIF_UNLOCK(lvif); + + lsta->ni = NULL; + ni->ni_drv_data = NULL; + ieee80211_free_node(ni); + + IMPROVE("free lsta here? We won't have a pointer to it from the node anymore."); +} + static struct lkpi_sta * lkpi_lsta_alloc(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN], struct ieee80211_hw *hw, struct ieee80211_node *ni) @@ -765,6 +802,10 @@ lkpi_disassoc(struct ieee80211_sta *sta, struct ieee80211_vif *vif, vif->bss_conf.assoc = false; vif->bss_conf.aid = 0; changed |= BSS_CHANGED_ASSOC; + /* + * This will remove the sta from firmware for iwlwifi. + * So confusing that they use state and flags and ... ^%$%#%$^. + */ IMPROVE(); hw = LHW_TO_HW(lhw); lkpi_80211_mo_bss_info_changed(hw, vif, &vif->bss_conf, @@ -845,6 +886,8 @@ lkpi_sta_scan_to_auth(struct ieee80211vap *vap, enum ieee80211_state nstate, int lvif = VAP_TO_LVIF(vap); vif = LVIF_TO_VIF(lvif); + ni = ieee80211_ref_node(vap->iv_bss); + IEEE80211_UNLOCK(vap->iv_ic); /* Add chanctx (or if exists, change it). */ @@ -872,7 +915,6 @@ lkpi_sta_scan_to_auth(struct ieee80211vap *vap, enum ieee80211_state nstate, int conf->min_def.center_freq2 = 0; IMPROVE("currently 20_NOHT only"); - ni = NULL; error = 0; if (vif->chanctx_conf != NULL) { changed = IEEE80211_CHANCTX_CHANGE_MIN_WIDTH; @@ -905,8 +947,6 @@ lkpi_sta_scan_to_auth(struct ieee80211vap *vap, enum ieee80211_state nstate, int } IMPROVE("update radiotap chan fields too"); - ni = ieee80211_ref_node(vap->iv_bss); - /* Set bss info (bss_info_changed). */ bss_changed = 0; IEEE80211_ADDR_COPY(vif->bss_conf.bssid, ni->ni_bssid); @@ -926,8 +966,29 @@ lkpi_sta_scan_to_auth(struct ieee80211vap *vap, enum ieee80211_state nstate, int IMPROVE("bss info: not all needs to come now and rates are missing"); lkpi_80211_mo_bss_info_changed(hw, vif, &vif->bss_conf, bss_changed); + /* + * This is a bandaid for now. If we went through (*iv_update_bss)() + * and then removed the lsta we end up here without a lsta and have + * to manually allocate and link it in as lkpi_ic_node_alloc()/init() + * would normally do. + * XXX-BZ I do not like this but currently we have no good way of + * intercepting the bss swap and state changes and packets going out + * workflow so live with this. It is a compat layer after all. + */ + if (ni->ni_drv_data == NULL) { + lsta = lkpi_lsta_alloc(vap, ni->ni_macaddr, hw, ni); + if (lsta == NULL) { + error = ENOMEM; + goto out; + } + lsta->ni = ieee80211_ref_node(ni); + LKPI_80211_LVIF_LOCK(lvif); + TAILQ_INSERT_TAIL(&lvif->lsta_head, lsta, lsta_entry); + LKPI_80211_LVIF_UNLOCK(lvif); + } else { + lsta = ni->ni_drv_data; + } /* Add (or adjust) sta and change state (from NOTEXIST) to NONE. */ - lsta = ni->ni_drv_data; KASSERT(lsta != NULL, ("%s: ni %p lsta is NULL\n", __func__, ni)); KASSERT(lsta->state == IEEE80211_STA_NOTEXIST, ("%s: lsta %p state not " "NOTEXIST: %#x\n", __func__, lsta, lsta->state)); @@ -946,6 +1007,8 @@ lkpi_sta_scan_to_auth(struct ieee80211vap *vap, enum ieee80211_state nstate, int * possibly prepare the queue in the driver to be ready for the 1st * packet; lkpi_80211_txq_tx_one() still has a workaround as there * is no guarantee or way to check. + * XXX-BZ and by now we know that this does not work on all drivers + * for all queues. */ lkpi_wake_tx_queues(hw, sta, false, false); @@ -1032,33 +1095,22 @@ lkpi_sta_auth_to_scan(struct ieee80211vap *vap, enum ieee80211_state nstate, int /* Keep ni around. */ ni = ieee80211_ref_node(vap->iv_bss); - - IEEE80211_UNLOCK(vap->iv_ic); lsta = ni->ni_drv_data; sta = LSTA_TO_STA(lsta); - /* flush, drop. */ - lkpi_80211_mo_flush(hw, vif, nitems(sta->txq), true); - - IEEE80211_LOCK(vap->iv_ic); - - /* Call iv_newstate first so we get potential deauth packet out. */ - error = lvif->iv_newstate(vap, nstate, arg); - if (error != 0) - goto outni; + lkpi_dump_lsta(lsta, __func__, __LINE__); IEEE80211_UNLOCK(vap->iv_ic); + /* flush, drop. */ + lkpi_80211_mo_flush(hw, vif, nitems(sta->txq), true); + /* Wake tx queues to get packet(s) out. */ lkpi_wake_tx_queues(hw, sta, true, true); /* flush, no drop */ lkpi_80211_mo_flush(hw, vif, nitems(sta->txq), false); - /* Take the station and chan ctx down again. */ - - IMPROVE("event callback with failure?"); - /* End mgd_complete_tx. */ if (lsta->in_mgd) { memset(&prep_tx_info, 0, sizeof(prep_tx_info)); @@ -1067,18 +1119,18 @@ lkpi_sta_auth_to_scan(struct ieee80211vap *vap, enum ieee80211_state nstate, int lsta->in_mgd = false; } -#ifdef __not_yet__ /* sync_rx_queues */ lkpi_80211_mo_sync_rx_queues(hw); /* sta_pre_rcu_remove */ lkpi_80211_mo_sta_pre_rcu_remove(hw, vif, sta); -#endif + + /* Take the station down. */ /* Adjust sta and change state (from NONE) to NOTEXIST. */ KASSERT(lsta != NULL, ("%s: ni %p lsta is NULL\n", __func__, ni)); KASSERT(lsta->state == IEEE80211_STA_NONE, ("%s: lsta %p state not " - "NONE: %#x\n", __func__, lsta, lsta->state)); + "NONE: %#x, nstate %d arg %d\n", __func__, lsta, lsta->state, nstate, arg)); error = lkpi_80211_mo_sta_state(hw, vif, sta, IEEE80211_STA_NOTEXIST); if (error != 0) { IMPROVE("do we need to undo the chan ctx?"); @@ -1088,8 +1140,13 @@ lkpi_sta_auth_to_scan(struct ieee80211vap *vap, enum ieee80211_state nstate, int lsta->added_to_drv = false; /* mo manages. */ #endif - IMPROVE("Any bss_info changes to announce?"); + lkpi_dump_lsta(lsta, __func__, __LINE__); + + lkpi_lsta_remove(lsta, lvif); + + /* conf_tx */ + /* Take the chan ctx down. */ if (vif->chanctx_conf != NULL) { struct ieee80211_chanctx_conf *conf; @@ -1103,12 +1160,8 @@ lkpi_sta_auth_to_scan(struct ieee80211vap *vap, enum ieee80211_state nstate, int free(conf, M_LKPI80211); } - /* No need to start a scan; ic_scan_start should do. */ - - error = EALREADY; out: IEEE80211_LOCK(vap->iv_ic); -outni: if (ni != NULL) ieee80211_free_node(ni); return (error); @@ -1248,7 +1301,7 @@ lkpi_sta_a_to_a(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) } static int -lkpi_sta_assoc_to_auth(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) +_lkpi_sta_assoc_to_down(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) { struct lkpi_hw *lhw; struct ieee80211_hw *hw; @@ -1258,6 +1311,7 @@ lkpi_sta_assoc_to_auth(struct ieee80211vap *vap, enum ieee80211_state nstate, in struct lkpi_sta *lsta; struct ieee80211_sta *sta; struct ieee80211_prep_tx_info prep_tx_info; + enum ieee80211_bss_changed bss_changed; int error; lhw = vap->iv_ic->ic_softc; @@ -1267,11 +1321,42 @@ lkpi_sta_assoc_to_auth(struct ieee80211vap *vap, enum ieee80211_state nstate, in /* Keep ni around. */ ni = ieee80211_ref_node(vap->iv_bss); - - IEEE80211_UNLOCK(vap->iv_ic); lsta = ni->ni_drv_data; sta = LSTA_TO_STA(lsta); + lkpi_dump_lsta(lsta, __func__, __LINE__); + + IEEE80211_UNLOCK(vap->iv_ic); + + /* flush, drop. */ + lkpi_80211_mo_flush(hw, vif, nitems(sta->txq), true); + + IMPROVE("What are the proper conditions for DEAUTH_NEED_MGD_TX_PREP?"); + if (ieee80211_hw_check(hw, DEAUTH_NEED_MGD_TX_PREP) && + !lsta->in_mgd) { + memset(&prep_tx_info, 0, sizeof(prep_tx_info)); + prep_tx_info.duration = PREP_TX_INFO_DURATION; + lkpi_80211_mo_mgd_prepare_tx(hw, vif, &prep_tx_info); + lsta->in_mgd = true; + } + + IEEE80211_LOCK(vap->iv_ic); + + /* Call iv_newstate first so we get potential DISASSOC packet out. */ + error = lvif->iv_newstate(vap, nstate, arg); + if (error != 0) + goto outni; + + IEEE80211_UNLOCK(vap->iv_ic); + + lkpi_dump_lsta(lsta, __func__, __LINE__); + + /* Wake tx queues to get packet(s) out. */ + lkpi_wake_tx_queues(hw, sta, true, true); + + /* flush, no drop */ + lkpi_80211_mo_flush(hw, vif, nitems(sta->txq), false); + /* End mgd_complete_tx. */ if (lsta->in_mgd) { memset(&prep_tx_info, 0, sizeof(prep_tx_info)); @@ -1280,6 +1365,14 @@ lkpi_sta_assoc_to_auth(struct ieee80211vap *vap, enum ieee80211_state nstate, in lsta->in_mgd = false; } + /* sync_rx_queues */ + lkpi_80211_mo_sync_rx_queues(hw); + + /* sta_pre_rcu_remove */ + lkpi_80211_mo_sta_pre_rcu_remove(hw, vif, sta); + + /* Take the station down. */ + /* Update sta and change state (from AUTH) to NONE. */ KASSERT(lsta != NULL, ("%s: ni %p lsta is NULL\n", __func__, ni)); KASSERT(lsta->state == IEEE80211_STA_AUTH, ("%s: lsta %p state not " @@ -1288,23 +1381,84 @@ lkpi_sta_assoc_to_auth(struct ieee80211vap *vap, enum ieee80211_state nstate, in if (error != 0) goto out; - IMPROVE("anything else?"); + lkpi_dump_lsta(lsta, __func__, __LINE__); + + /* Adjust sta and change state (from NONE) to NOTEXIST. */ + KASSERT(lsta != NULL, ("%s: ni %p lsta is NULL\n", __func__, ni)); + KASSERT(lsta->state == IEEE80211_STA_NONE, ("%s: lsta %p state not " + "NONE: %#x, nstate %d arg %d\n", __func__, lsta, lsta->state, nstate, arg)); + error = lkpi_80211_mo_sta_state(hw, vif, sta, IEEE80211_STA_NOTEXIST); + if (error != 0) { + IMPROVE("do we need to undo the chan ctx?"); + goto out; + } +#if 0 + lsta->added_to_drv = false; /* mo manages. */ +#endif + + lkpi_dump_lsta(lsta, __func__, __LINE__); + + /* Update bss info (bss_info_changed) (assoc, aid, ..). */ + /* We need to do this now, can only do after sta is IEEE80211_STA_NOTEXIST. */ + lkpi_disassoc(sta, vif, lhw); + + IMPROVE("Any bss_info changes to announce?"); + bss_changed = 0; + vif->bss_conf.qos = 0; + bss_changed |= BSS_CHANGED_QOS; + vif->bss_conf.ssid_len = 0; + memset(vif->bss_conf.ssid, '\0', sizeof(vif->bss_conf.ssid)); + bss_changed |= BSS_CHANGED_BSSID; + lkpi_80211_mo_bss_info_changed(hw, vif, &vif->bss_conf, bss_changed); + + lkpi_lsta_remove(lsta, lvif); + + /* conf_tx */ + /* Take the chan ctx down. */ + if (vif->chanctx_conf != NULL) { + struct ieee80211_chanctx_conf *conf; + + conf = vif->chanctx_conf; + /* Remove vif context. */ + lkpi_80211_mo_unassign_vif_chanctx(hw, vif, &vif->chanctx_conf); + /* NB: vif->chanctx_conf is NULL now. */ + + /* Remove chan ctx. */ + lkpi_80211_mo_remove_chanctx(hw, conf); + free(conf, M_LKPI80211); + } + + error = EALREADY; out: IEEE80211_LOCK(vap->iv_ic); +outni: if (ni != NULL) ieee80211_free_node(ni); return (error); } +static int +lkpi_sta_assoc_to_auth(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) +{ + int error; + + error = _lkpi_sta_assoc_to_down(vap, nstate, arg); + if (error != 0 && error != EALREADY) + return (error); + + /* At this point iv_bss is long a new node! */ + + error |= lkpi_sta_scan_to_auth(vap, nstate, 0); + return (error); +} + static int lkpi_sta_assoc_to_scan(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) { int error; - error = lkpi_sta_assoc_to_auth(vap, nstate, arg); - if (error == 0) - error = lkpi_sta_auth_to_scan(vap, nstate, arg); + error = _lkpi_sta_assoc_to_down(vap, nstate, arg); return (error); } @@ -1313,9 +1467,7 @@ lkpi_sta_assoc_to_init(struct ieee80211vap *vap, enum ieee80211_state nstate, in { int error; - error = lkpi_sta_assoc_to_scan(vap, nstate, arg); - if (error == 0) - error = lkpi_sta_scan_to_init(vap, nstate, arg); + error = _lkpi_sta_assoc_to_down(vap, nstate, arg); return (error); } @@ -1472,6 +1624,10 @@ lkpi_sta_run_to_assoc(struct ieee80211vap *vap, enum ieee80211_state nstate, int struct ieee80211_node *ni; struct lkpi_sta *lsta; struct ieee80211_sta *sta; + struct ieee80211_prep_tx_info prep_tx_info; +#if 0 + enum ieee80211_bss_changed bss_changed; +#endif int error; lhw = vap->iv_ic->ic_softc; @@ -1481,11 +1637,60 @@ lkpi_sta_run_to_assoc(struct ieee80211vap *vap, enum ieee80211_state nstate, int /* Keep ni around. */ ni = ieee80211_ref_node(vap->iv_bss); - - IEEE80211_UNLOCK(vap->iv_ic); lsta = ni->ni_drv_data; sta = LSTA_TO_STA(lsta); + lkpi_dump_lsta(lsta, __func__, __LINE__); + + IEEE80211_UNLOCK(vap->iv_ic); + + /* flush, drop. */ + lkpi_80211_mo_flush(hw, vif, nitems(sta->txq), true); + + IMPROVE("What are the proper conditions for DEAUTH_NEED_MGD_TX_PREP?"); + if (ieee80211_hw_check(hw, DEAUTH_NEED_MGD_TX_PREP) && + !lsta->in_mgd) { + memset(&prep_tx_info, 0, sizeof(prep_tx_info)); + prep_tx_info.duration = PREP_TX_INFO_DURATION; + lkpi_80211_mo_mgd_prepare_tx(hw, vif, &prep_tx_info); + lsta->in_mgd = true; + } + + IEEE80211_LOCK(vap->iv_ic); + + /* Call iv_newstate first so we get potential DISASSOC packet out. */ + error = lvif->iv_newstate(vap, nstate, arg); + if (error != 0) + goto outni; + + IEEE80211_UNLOCK(vap->iv_ic); + + lkpi_dump_lsta(lsta, __func__, __LINE__); + + /* Wake tx queues to get packet(s) out. */ + lkpi_wake_tx_queues(hw, sta, true, true); + + /* flush, no drop */ + lkpi_80211_mo_flush(hw, vif, nitems(sta->txq), false); + + /* End mgd_complete_tx. */ + if (lsta->in_mgd) { + memset(&prep_tx_info, 0, sizeof(prep_tx_info)); + prep_tx_info.success = false; + lkpi_80211_mo_mgd_complete_tx(hw, vif, &prep_tx_info); + lsta->in_mgd = false; + } + +#if 0 + /* sync_rx_queues */ + lkpi_80211_mo_sync_rx_queues(hw); + + /* sta_pre_rcu_remove */ + lkpi_80211_mo_sta_pre_rcu_remove(hw, vif, sta); +#endif + + /* Take the station down. */ + /* Adjust sta and change state (from AUTHORIZED) to ASSOC. */ KASSERT(lsta != NULL, ("%s: ni %p lsta is NULL\n", __func__, ni)); KASSERT(lsta->state == IEEE80211_STA_AUTHORIZED, ("%s: lsta %p state not " @@ -1494,61 +1699,217 @@ lkpi_sta_run_to_assoc(struct ieee80211vap *vap, enum ieee80211_state nstate, int if (error != 0) goto out; - /* Update bss info (bss_info_changed) (assoc, aid, ..). */ - lkpi_disassoc(sta, vif, lhw); + lkpi_dump_lsta(lsta, __func__, __LINE__); /* Update sta_state (ASSOC to AUTH). */ KASSERT(lsta != NULL, ("%s: ni %p lsta is NULL\n", __func__, ni)); KASSERT(lsta->state == IEEE80211_STA_ASSOC, ("%s: lsta %p state not " "ASSOC: %#x\n", __func__, lsta, lsta->state)); - sta = LSTA_TO_STA(lsta); - sta->aid = 0; error = lkpi_80211_mo_sta_state(hw, vif, sta, IEEE80211_STA_AUTH); if (error != 0) goto out; - IMPROVE("if ASSOC is final state, prep_tx_info?"); + lkpi_dump_lsta(lsta, __func__, __LINE__); +#if 0 + /* Update bss info (bss_info_changed) (assoc, aid, ..). */ + lkpi_disassoc(sta, vif, lhw); +#endif + + error = EALREADY; out: IEEE80211_LOCK(vap->iv_ic); +outni: if (ni != NULL) ieee80211_free_node(ni); return (error); } static int -lkpi_sta_run_to_auth(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) +lkpi_sta_run_to_init(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) { + struct lkpi_hw *lhw; + struct ieee80211_hw *hw; + struct lkpi_vif *lvif; + struct ieee80211_vif *vif; + struct ieee80211_node *ni; + struct lkpi_sta *lsta; + struct ieee80211_sta *sta; + struct ieee80211_prep_tx_info prep_tx_info; + enum ieee80211_bss_changed bss_changed; int error; - error = lkpi_sta_run_to_assoc(vap, nstate, arg); - if (error == 0) - error = lkpi_sta_assoc_to_auth(vap, nstate, arg); + lhw = vap->iv_ic->ic_softc; + hw = LHW_TO_HW(lhw); + lvif = VAP_TO_LVIF(vap); + vif = LVIF_TO_VIF(lvif); + + /* Keep ni around. */ + ni = ieee80211_ref_node(vap->iv_bss); + lsta = ni->ni_drv_data; + sta = LSTA_TO_STA(lsta); + + lkpi_dump_lsta(lsta, __func__, __LINE__); + + IEEE80211_UNLOCK(vap->iv_ic); + + /* flush, drop. */ + lkpi_80211_mo_flush(hw, vif, nitems(sta->txq), true); + + IMPROVE("What are the proper conditions for DEAUTH_NEED_MGD_TX_PREP?"); + if (ieee80211_hw_check(hw, DEAUTH_NEED_MGD_TX_PREP) && + !lsta->in_mgd) { + memset(&prep_tx_info, 0, sizeof(prep_tx_info)); + prep_tx_info.duration = PREP_TX_INFO_DURATION; + lkpi_80211_mo_mgd_prepare_tx(hw, vif, &prep_tx_info); + lsta->in_mgd = true; + } + + IEEE80211_LOCK(vap->iv_ic); + + /* Call iv_newstate first so we get potential DISASSOC packet out. */ + error = lvif->iv_newstate(vap, nstate, arg); + if (error != 0) + goto outni; + + IEEE80211_UNLOCK(vap->iv_ic); + + lkpi_dump_lsta(lsta, __func__, __LINE__); + + /* Wake tx queues to get packet(s) out. */ + lkpi_wake_tx_queues(hw, sta, true, true); + + /* flush, no drop */ + lkpi_80211_mo_flush(hw, vif, nitems(sta->txq), false); + + /* End mgd_complete_tx. */ + if (lsta->in_mgd) { + memset(&prep_tx_info, 0, sizeof(prep_tx_info)); + prep_tx_info.success = false; + lkpi_80211_mo_mgd_complete_tx(hw, vif, &prep_tx_info); + lsta->in_mgd = false; + } + + /* sync_rx_queues */ + lkpi_80211_mo_sync_rx_queues(hw); + + /* sta_pre_rcu_remove */ + lkpi_80211_mo_sta_pre_rcu_remove(hw, vif, sta); + + /* Take the station down. */ + + /* Adjust sta and change state (from AUTHORIZED) to ASSOC. */ + KASSERT(lsta != NULL, ("%s: ni %p lsta is NULL\n", __func__, ni)); + KASSERT(lsta->state == IEEE80211_STA_AUTHORIZED, ("%s: lsta %p state not " + "AUTHORIZED: %#x\n", __func__, lsta, lsta->state)); + error = lkpi_80211_mo_sta_state(hw, vif, sta, IEEE80211_STA_ASSOC); + if (error != 0) + goto out; + + lkpi_dump_lsta(lsta, __func__, __LINE__); + + /* Update sta_state (ASSOC to AUTH). */ + KASSERT(lsta != NULL, ("%s: ni %p lsta is NULL\n", __func__, ni)); + KASSERT(lsta->state == IEEE80211_STA_ASSOC, ("%s: lsta %p state not " + "ASSOC: %#x\n", __func__, lsta, lsta->state)); + error = lkpi_80211_mo_sta_state(hw, vif, sta, IEEE80211_STA_AUTH); + if (error != 0) + goto out; + + lkpi_dump_lsta(lsta, __func__, __LINE__); + + /* Update sta and change state (from AUTH) to NONE. */ + KASSERT(lsta != NULL, ("%s: ni %p lsta is NULL\n", __func__, ni)); + KASSERT(lsta->state == IEEE80211_STA_AUTH, ("%s: lsta %p state not " + "AUTH: %#x\n", __func__, lsta, lsta->state)); + error = lkpi_80211_mo_sta_state(hw, vif, sta, IEEE80211_STA_NONE); + if (error != 0) + goto out; + + lkpi_dump_lsta(lsta, __func__, __LINE__); + + /* Adjust sta and change state (from NONE) to NOTEXIST. */ + KASSERT(lsta != NULL, ("%s: ni %p lsta is NULL\n", __func__, ni)); + KASSERT(lsta->state == IEEE80211_STA_NONE, ("%s: lsta %p state not " + "NONE: %#x, nstate %d arg %d\n", __func__, lsta, lsta->state, nstate, arg)); + error = lkpi_80211_mo_sta_state(hw, vif, sta, IEEE80211_STA_NOTEXIST); + if (error != 0) { + IMPROVE("do we need to undo the chan ctx?"); + goto out; + } +#if 0 + lsta->added_to_drv = false; /* mo manages. */ +#endif + + lkpi_dump_lsta(lsta, __func__, __LINE__); + + /* Update bss info (bss_info_changed) (assoc, aid, ..). */ + /* + * One would expect this to happen when going off AUTHORIZED. + * See comment there; removes the sta from fw. + */ + lkpi_disassoc(sta, vif, lhw); + + IMPROVE("Any bss_info changes to announce?"); + bss_changed = 0; + vif->bss_conf.qos = 0; + bss_changed |= BSS_CHANGED_QOS; + vif->bss_conf.ssid_len = 0; + memset(vif->bss_conf.ssid, '\0', sizeof(vif->bss_conf.ssid)); + bss_changed |= BSS_CHANGED_BSSID; + lkpi_80211_mo_bss_info_changed(hw, vif, &vif->bss_conf, bss_changed); + + lkpi_lsta_remove(lsta, lvif); + + /* conf_tx */ + + /* Take the chan ctx down. */ + if (vif->chanctx_conf != NULL) { + struct ieee80211_chanctx_conf *conf; + + conf = vif->chanctx_conf; + /* Remove vif context. */ + lkpi_80211_mo_unassign_vif_chanctx(hw, vif, &vif->chanctx_conf); + /* NB: vif->chanctx_conf is NULL now. */ + + /* Remove chan ctx. */ + lkpi_80211_mo_remove_chanctx(hw, conf); + free(conf, M_LKPI80211); + } + + error = EALREADY; +out: + IEEE80211_LOCK(vap->iv_ic); +outni: + if (ni != NULL) + ieee80211_free_node(ni); return (error); } static int lkpi_sta_run_to_scan(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) { - int error; - error = lkpi_sta_run_to_auth(vap, nstate, arg); - if (error == 0) - error = lkpi_sta_auth_to_scan(vap, nstate, arg); - return (error); + return (lkpi_sta_run_to_init(vap, nstate, arg)); } static int -lkpi_sta_run_to_init(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) +lkpi_sta_run_to_auth(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) { int error; - error = lkpi_sta_run_to_scan(vap, nstate, arg); - if (error == 0) - error = lkpi_sta_scan_to_init(vap, nstate, arg); + error = lkpi_sta_run_to_init(vap, nstate, arg); + if (error != 0 && error != EALREADY) + return (error); + + /* At this point iv_bss is long a new node! */ + + error |= lkpi_sta_scan_to_auth(vap, nstate, 0); return (error); } +/* -------------------------------------------------------------------------- */ + /* * The matches the documented state changes in net80211::sta_newstate(). * XXX (1) without CSA and SLEEP yet, * XXX (2) not all unhandled cases @@ -1563,24 +1924,24 @@ struct fsm_state { { IEEE80211_S_INIT, IEEE80211_S_INIT, lkpi_sta_state_do_nada }, { IEEE80211_S_SCAN, IEEE80211_S_INIT, lkpi_sta_state_do_nada }, /* scan_to_init */ { IEEE80211_S_AUTH, IEEE80211_S_INIT, lkpi_sta_auth_to_init }, /* not explicitly in sta_newstate() */ - { IEEE80211_S_ASSOC, IEEE80211_S_INIT, lkpi_sta_assoc_to_init }, - { IEEE80211_S_RUN, IEEE80211_S_INIT, lkpi_sta_run_to_init }, + { IEEE80211_S_ASSOC, IEEE80211_S_INIT, lkpi_sta_assoc_to_init }, /* Send DEAUTH. */ + { IEEE80211_S_RUN, IEEE80211_S_INIT, lkpi_sta_run_to_init }, /* Send DISASSOC. */ { IEEE80211_S_INIT, IEEE80211_S_SCAN, lkpi_sta_state_do_nada }, { IEEE80211_S_SCAN, IEEE80211_S_SCAN, lkpi_sta_state_do_nada }, { IEEE80211_S_AUTH, IEEE80211_S_SCAN, lkpi_sta_auth_to_scan }, { IEEE80211_S_ASSOC, IEEE80211_S_SCAN, lkpi_sta_assoc_to_scan }, - { IEEE80211_S_RUN, IEEE80211_S_SCAN, lkpi_sta_run_to_scan }, + { IEEE80211_S_RUN, IEEE80211_S_SCAN, lkpi_sta_run_to_scan }, /* Beacon miss. */ - { IEEE80211_S_INIT, IEEE80211_S_AUTH, lkpi_sta_scan_to_auth }, - { IEEE80211_S_SCAN, IEEE80211_S_AUTH, lkpi_sta_scan_to_auth }, - { IEEE80211_S_AUTH, IEEE80211_S_AUTH, lkpi_sta_a_to_a }, - { IEEE80211_S_ASSOC, IEEE80211_S_AUTH, lkpi_sta_assoc_to_auth }, - { IEEE80211_S_RUN, IEEE80211_S_AUTH, lkpi_sta_run_to_auth }, + { IEEE80211_S_INIT, IEEE80211_S_AUTH, lkpi_sta_scan_to_auth }, /* Send AUTH. */ + { IEEE80211_S_SCAN, IEEE80211_S_AUTH, lkpi_sta_scan_to_auth }, /* Send AUTH. */ + { IEEE80211_S_AUTH, IEEE80211_S_AUTH, lkpi_sta_a_to_a }, /* Send ?AUTH. */ + { IEEE80211_S_ASSOC, IEEE80211_S_AUTH, lkpi_sta_assoc_to_auth }, /* Send ?AUTH. */ + { IEEE80211_S_RUN, IEEE80211_S_AUTH, lkpi_sta_run_to_auth }, /* Send ?AUTH. */ - { IEEE80211_S_AUTH, IEEE80211_S_ASSOC, lkpi_sta_auth_to_assoc }, - { IEEE80211_S_ASSOC, IEEE80211_S_ASSOC, lkpi_sta_a_to_a }, - { IEEE80211_S_RUN, IEEE80211_S_ASSOC, lkpi_sta_run_to_assoc }, + { IEEE80211_S_AUTH, IEEE80211_S_ASSOC, lkpi_sta_auth_to_assoc }, /* Send ASSOCREQ. */ + { IEEE80211_S_ASSOC, IEEE80211_S_ASSOC, lkpi_sta_a_to_a }, /* Send ASSOCREQ. */ + { IEEE80211_S_RUN, IEEE80211_S_ASSOC, lkpi_sta_run_to_assoc }, /* Send ASSOCREQ/REASSOCREQ. */ { IEEE80211_S_AUTH, IEEE80211_S_RUN, lkpi_sta_auth_to_run }, { IEEE80211_S_ASSOC, IEEE80211_S_RUN, lkpi_sta_assoc_to_run }, @@ -1630,6 +1991,11 @@ lkpi_iv_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) error = 0; for (; s->handler != NULL; s++) { if (ostate == s->ostate && nstate == s->nstate) { + if (debug_80211 & D80211_TRACE) + ic_printf(vap->iv_ic, "%s: new state %d (%s) ->" + " %d (%s): arg %d.\n", __func__, + ostate, ieee80211_state_name[ostate], + nstate, ieee80211_state_name[nstate], arg); error = s->handler(vap, nstate, arg); break; } @@ -1637,7 +2003,7 @@ lkpi_iv_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) IEEE80211_LOCK_ASSERT(vap->iv_ic); if (s->handler == NULL) { - IMPROVE("thurn this into a KASSERT\n"); + IMPROVE("turn this into a KASSERT\n"); ic_printf(vap->iv_ic, "%s: unsupported state transition " "%d (%s) -> %d (%s)\n", __func__, ostate, ieee80211_state_name[ostate], @@ -1646,12 +2012,11 @@ lkpi_iv_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) } if (error == EALREADY) { - IMPROVE("make this a debug log later"); - ic_printf(vap->iv_ic, "%s: error %d during state transition " - "%d (%s) -> %d (%s): iv_newstate already handled.\n", - __func__, error, - ostate, ieee80211_state_name[ostate], - nstate, ieee80211_state_name[nstate]); + if (debug_80211 & D80211_TRACE) + ic_printf(vap->iv_ic, "%s: state transition %d (%s) -> " + "%d (%s): iv_newstate already handled: %d.\n", + __func__, ostate, ieee80211_state_name[ostate], + nstate, ieee80211_state_name[nstate], error); return (0); } @@ -1665,7 +2030,8 @@ lkpi_iv_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) } if (debug_80211 & D80211_TRACE) - ic_printf(vap->iv_ic, "%s:%d: vap %p nstate %#x arg %#x calling net80211 parent\n", + ic_printf(vap->iv_ic, "%s:%d: vap %p nstate %#x arg %#x " + "calling net80211 parent\n", __func__, __LINE__, vap, nstate, arg); return (lvif->iv_newstate(vap, nstate, arg)); @@ -1673,6 +2039,53 @@ lkpi_iv_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) /* -------------------------------------------------------------------------- */ +/* + * We overload (*iv_update_bss) as otherwise we have cases in, e.g., + * net80211::ieee80211_sta_join1() where vap->iv_bss gets replaced by a + * new node without us knowing and thus our ni/lsta are out of sync. + */ +static struct ieee80211_node * +lkpi_iv_update_bss(struct ieee80211vap *vap, struct ieee80211_node *ni) +{ + struct lkpi_vif *lvif; + struct ieee80211_node *obss; + struct lkpi_sta *lsta; + + lvif = VAP_TO_LVIF(vap); + obss = vap->iv_bss; + + /* Nothing to copy from. Just return. */ + if (obss == NULL || obss->ni_drv_data == NULL) + goto out; + + /* Nothing to copy to. Just return. */ + IMPROVE("clearing the obss might still be needed?"); + if (ni == NULL) + goto out; + + /* Nothing changed? panic? */ + if (obss == ni) + goto out; + + if (debug_80211 & D80211_TRACE) + ic_printf(vap->iv_ic, "%s: obss %p ni_drv_data %p " + "ni %p ni_drv_data %p\n", __func__, + obss, (obss != NULL) ? obss->ni_drv_data : NULL, + ni, (ni != NULL) ? ni->ni_drv_data : NULL); + + lsta = obss->ni_drv_data; + obss->ni_drv_data = ni->ni_drv_data; + ni->ni_drv_data = lsta; + if (lsta != NULL) + lsta->ni = ni; + lsta = obss->ni_drv_data; + if (lsta != NULL) + lsta->ni = obss; + +out: + return (lvif->iv_update_bss(vap, ni)); +} + static int lkpi_ic_wme_update(struct ieee80211com *ic) { @@ -1840,6 +2253,8 @@ lkpi_ic_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], /* Override with LinuxKPI method so we can drive mac80211/cfg80211. */ lvif->iv_newstate = vap->iv_newstate; vap->iv_newstate = lkpi_iv_newstate; + lvif->iv_update_bss = vap->iv_update_bss; + vap->iv_update_bss = lkpi_iv_update_bss; /* Key management. */ if (lhw->ops->set_key != NULL) { @@ -2361,6 +2776,8 @@ lkpi_ic_node_free(struct ieee80211_node *ni) ic = ni->ni_ic; lhw = ic->ic_softc; lsta = ni->ni_drv_data; + if (lsta == NULL) + goto out; /* XXX-BZ free resources, ... */ IMPROVE(); @@ -2380,10 +2797,11 @@ lkpi_ic_node_free(struct ieee80211_node *ni) /* remove ref from lsta node... */ + /* Free lsta. */ + +out: if (lhw->ic_node_free != NULL) lhw->ic_node_free(ni); - - /* Free lsta. */ } static int diff --git a/sys/compat/linuxkpi/common/src/linux_80211.h b/sys/compat/linuxkpi/common/src/linux_80211.h index 9538a1284201..fabc90e07a87 100644 --- a/sys/compat/linuxkpi/common/src/linux_80211.h +++ b/sys/compat/linuxkpi/common/src/linux_80211.h @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2020-2021 The FreeBSD Foundation + * Copyright (c) 2020-2022 The FreeBSD Foundation * Copyright (c) 2020-2021 Bjoern A. Zeeb * * This software was developed by Björn Zeeb under sponsorship from @@ -96,7 +96,7 @@ struct lkpi_sta { struct ieee80211_key_conf *kc; enum ieee80211_sta_state state; bool added_to_drv; /* Driver knows; i.e. we called ...(). */ - bool in_mgd; + bool in_mgd; /* XXX-BZ should this be per-vif? */ /* Must be last! */ struct ieee80211_sta sta __aligned(CACHE_LINE_SIZE); @@ -114,6 +114,8 @@ struct lkpi_vif { /* Other local stuff. */ int (*iv_newstate)(struct ieee80211vap *, enum ieee80211_state, int); + struct ieee80211_node * (*iv_update_bss)(struct ieee80211vap *, + struct ieee80211_node *); TAILQ_HEAD(, lkpi_sta) lsta_head; bool added_to_drv; /* Driver knows; i.e. we called add_interface(). */ diff --git a/sys/compat/linuxkpi/common/src/linux_80211_macops.c b/sys/compat/linuxkpi/common/src/linux_80211_macops.c index 0004967f350e..68e9ca47634b 100644 --- a/sys/compat/linuxkpi/common/src/linux_80211_macops.c +++ b/sys/compat/linuxkpi/common/src/linux_80211_macops.c @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2021 The FreeBSD Foundation + * Copyright (c) 2021-2022 The FreeBSD Foundation * * This software was developed by Björn Zeeb under sponsorship from * the FreeBSD Foundation. @@ -272,7 +272,7 @@ lkpi_80211_mo_configure_filter(struct ieee80211_hw *hw, unsigned int changed_fla /* * So far we only called sta_{add,remove} as an alternative to sta_state. - * Let's keep the implementation simpler and hid sta_{add,remove} under the + * Let's keep the implementation simpler and hide sta_{add,remove} under the * hood here calling them if state_state is not available from mo_sta_state. */ static int