From: Mark Kettenis Subject: Re: iwx: roaming fix for MA/Bz devices and PMF To: Stefan Sperling Cc: tech@openbsd.org Date: Thu, 05 Feb 2026 23:24:39 +0100 > Date: Tue, 3 Feb 2026 17:01:14 +0100 > From: Stefan Sperling > > 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 **); > >