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