Download raw body.
iwx: roaming fix for MA/Bz devices and PMF
> Date: Tue, 3 Feb 2026 17:01:14 +0100
> From: Stefan Sperling <stsp@stsp.name>
>
> Tests of this patch while using roaming on any iwx(4) device would be
> much appreciated.
>
> Bz device firmware throws a fatal firmware error if we attempt to remove
> the firmware AP station before removing associated crypto keys. We currently
> rely on firmware to implicitly remove keys along with the station and this
> will no longer work on Bz.
> The same might be true for MA devices, not sure. I cannot test them.
Tested this diff on:
iwx0 at pci0 dev 20 function 3 "Intel Wi-Fi 6 AX210" rev 0x20, msix
iwx0: hw rev 0x440, fw 83.d24e06ed.0, pnvm 285b3568, address dc:45:46:e7:d5:33
This is FW version 83, so it must be MA ;).
Doesn't seem to make a difference here. But I'm not roaming between
access points.
On the bright side, it seems this device works a lot better now. But
I'm also using a different access point now. Will do a bit more
testing before declaring that MA now work.
> Also improve roaming behaviour when PMF is enabled. We must send the deauth
> frame to the old AP properly encrypted, so send it before keys get removed.
>
> Thanks to Johannes Berg for deciphering firmware SYSASSERT code 0x0000251B.
>
> M sys/dev/pci/if_iwx.c | 56+ 3-
> M sys/net80211/ieee80211_node.c | 0+ 1-
> M sys/net80211/ieee80211_node.h | 1+ 0-
>
> 3 files changed, 57 insertions(+), 4 deletions(-)
>
> commit - d2b520ec61a7e83d4019845aa16dddcae54a25b4
> commit + d8efa19ebc99e3df6b75c7977d2708263746ca57
> blob - 5889c86b6502beae028604eb0d51cca7f59b6b07
> blob + b635a2b55989d3076d60629e301534c684262611
> --- sys/dev/pci/if_iwx.c
> +++ sys/dev/pci/if_iwx.c
> @@ -481,6 +481,7 @@ int iwx_send_temp_report_ths_cmd(struct iwx_softc *);
> int iwx_init_hw(struct iwx_softc *);
> int iwx_init(struct ifnet *);
> void iwx_start(struct ifnet *);
> +void iwx_mfp_leave(struct iwx_softc *);
> void iwx_stop(struct ifnet *);
> void iwx_watchdog(struct ifnet *);
> int iwx_ioctl(struct ifnet *, u_long, caddr_t);
> @@ -7982,6 +7983,43 @@ iwx_bgscan_done_task(void *arg)
> }
>
> /*
> + * Remove installed crypto keys while we still have access to them.
> + * Once iwx_newstate() is entered ic_bss will already contain
> + * information about our next AP.
> + */
> + if (ic->ic_flags & IEEE80211_F_RSNON) {
> + struct ieee80211_key *k;
> +
> + if (ic->ic_bss->ni_flags & IEEE80211_NODE_MFP)
> + iwx_mfp_leave(sc);
> +
> + if (in->in_flags & IWX_NODE_FLAG_HAVE_PAIRWISE_KEY)
> + (*ic->ic_delete_key)(ic, ni, &ni->ni_pairwise_key);
> +
> + if ((in->in_flags & IWX_NODE_FLAG_HAVE_GROUP_KEY) &&
> + (ic->ic_def_txkey == 1 || ic->ic_def_txkey == 2)) {
> + k = &ic->ic_nw_keys[ic->ic_def_txkey];
> + if ((k->k_flags & IEEE80211_KEY_GROUP) &&
> + k->k_cipher == IEEE80211_CIPHER_CCMP)
> + (*ic->ic_delete_key)(ic, ni, k);
> + }
> +
> + if ((in->in_flags & IWX_NODE_FLAG_HAVE_INTEGRITY_GROUP_KEY) &&
> + (ic->ic_igtk_kid == 4 || ic->ic_igtk_kid == 5)) {
> +
> + k = &ic->ic_nw_keys[ic->ic_igtk_kid];
> + if (k->k_flags & IEEE80211_KEY_IGTK)
> + (*ic->ic_delete_key)(ic, ni, k);
> + }
> +
> + ni->ni_port_valid = 0;
> + ni->ni_flags &= ~IEEE80211_NODE_TXRXPROT;
> + ni->ni_flags &= ~IEEE80211_NODE_TXMGMTPROT;
> + ni->ni_flags &= ~IEEE80211_NODE_RXMGMTPROT;
> + ni->ni_rsn_supp_state = RSNA_SUPP_INITIALIZE;
> + }
> +
> + /*
> * Tx queues have been flushed and Tx agg has been stopped.
> * Allow roaming to proceed.
> */
> @@ -7989,7 +8027,11 @@ iwx_bgscan_done_task(void *arg)
> ni->ni_unref_arg_size = sc->bgscan_unref_arg_size;
> sc->bgscan_unref_arg = NULL;
> sc->bgscan_unref_arg_size = 0;
> - ieee80211_node_tx_stopped(ic, &in->in_ni);
> + if (ni->ni_flags & IEEE80211_NODE_MFP) {
> + /* Deauth frame already sent. */
> + ieee80211_node_switch_bss(ic, &in->in_ni);
> + } else
> + ieee80211_node_tx_stopped(ic, &in->in_ni);
> done:
> if (err) {
> free(sc->bgscan_unref_arg, M_DEVBUF, sc->bgscan_unref_arg_size);
> @@ -8844,6 +8886,13 @@ iwx_mld_set_sta_key_cmd(struct iwx_softc *sc, int sta_
> return err;
> }
>
> + if (!remove_key) {
> + if (k->k_flags & IEEE80211_KEY_GROUP)
> + ic->ic_def_txkey = k->k_id;
> + if (k->k_flags & IEEE80211_KEY_IGTK)
> + ic->ic_igtk_kid = k->k_id;
> + }
> +
> return 0;
> }
>
> @@ -8890,6 +8939,9 @@ iwx_add_sta_key_cmd(struct iwx_softc *sc, int sta_id,
> return err;
> }
>
> + if (k->k_flags & IEEE80211_KEY_GROUP)
> + ic->ic_def_txkey = k->k_id;
> +
> return 0;
> }
>
> @@ -8990,9 +9042,10 @@ iwx_add_sta_key(struct iwx_softc *sc, int sta_id, stru
> if (k->k_flags & IEEE80211_KEY_IGTK) {
> in->in_flags |= IWX_NODE_FLAG_HAVE_INTEGRITY_GROUP_KEY;
> ic->ic_igtk_kid = k->k_id;
> - } else if (k->k_flags & IEEE80211_KEY_GROUP)
> + } else if (k->k_flags & IEEE80211_KEY_GROUP) {
> in->in_flags |= IWX_NODE_FLAG_HAVE_GROUP_KEY;
> - else
> + ic->ic_def_txkey = k->k_id;
> + } else
> in->in_flags |= IWX_NODE_FLAG_HAVE_PAIRWISE_KEY;
>
> if ((in->in_flags & want_keymask) == want_keymask) {
> blob - 6a6d141a064a5746eb967c37b1543069ce238048
> blob + 7ea1edd5828a9f48fe870dd191c6f24ae9a0e199
> --- sys/net80211/ieee80211_node.c
> +++ sys/net80211/ieee80211_node.c
> @@ -75,7 +75,6 @@ void ieee80211_setup_node(struct ieee80211com *, struc
> struct ieee80211_node *ieee80211_alloc_node_helper(struct ieee80211com *);
> void ieee80211_node_free_unref_cb(struct ieee80211_node *);
> void ieee80211_node_tx_flushed(struct ieee80211com *, struct ieee80211_node *);
> -void ieee80211_node_switch_bss(struct ieee80211com *, struct ieee80211_node *);
> void ieee80211_node_addba_request(struct ieee80211_node *, int);
> void ieee80211_node_addba_request_ac_be_to(void *);
> void ieee80211_node_addba_request_ac_bk_to(void *);
> blob - 8a6a267cf1c06fc0ba5e14a9b30e50052a22e48b
> blob + db4d49ac605aa07d08bc78700a82d59215f91e81
> --- sys/net80211/ieee80211_node.h
> +++ sys/net80211/ieee80211_node.h
> @@ -656,6 +656,7 @@ void ieee80211_node_join(struct ieee80211com *,
> void ieee80211_node_leave(struct ieee80211com *,
> struct ieee80211_node *);
> int ieee80211_match_bss(struct ieee80211com *, struct ieee80211_node *, int);
> +void ieee80211_node_switch_bss(struct ieee80211com *, struct ieee80211_node *);
> void ieee80211_node_tx_stopped(struct ieee80211com *, struct ieee80211_node *);
> struct ieee80211_node *ieee80211_node_choose_bss(struct ieee80211com *, int,
> struct ieee80211_node **);
>
>
iwx: roaming fix for MA/Bz devices and PMF