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
This commit is contained in:
Bjoern A. Zeeb 2022-03-22 18:34:13 +00:00
parent fa8f007de9
commit d9f59799fc
3 changed files with 513 additions and 93 deletions

View File

@ -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,60 +1699,216 @@ 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_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;
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)
{
return (lkpi_sta_run_to_init(vap, nstate, arg));
}
static int
lkpi_sta_run_to_auth(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
{
int error;
error = lkpi_sta_run_to_assoc(vap, nstate, arg);
if (error == 0)
error = lkpi_sta_assoc_to_auth(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);
}
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);
}
static int
lkpi_sta_run_to_init(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);
return (error);
}
/* -------------------------------------------------------------------------- */
/*
* The matches the documented state changes in net80211::sta_newstate().
@ -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

View File

@ -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(). */

View File

@ -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