iwm switches the current BSS channel for each input frame.
The reason for this is that with most of our drivers the
net80211 stack zaps through all channels one-by-one during
scans, and hence expects the BSS channel to match the
currently scanned channel.
However, iwm offloads scans to the firmware. The firmware returns
frames from various channels and indicates which channel a frame was
received on. iwm uses this information to switch the BSS channel
accordingly, which keeps the existing net80211 scan logic working.
However, ieee80211_input() may have a significant side-effect
which iwm currently ignores.
When management frames are exchanged after a scan, the input code
path in net802211 may decide to switch to an arbitrary BSS.
This means iwm should only reset the BSS channel if we're still
in the same BSS (identified by MAC address of the AP) after the
frame has been processed.
Other drivers which offload scans to firmware might need a similar
fix, e.g. iwm(4) and bwfm(4). I'm leaving those for later.
The reason this didn't show up as a problem so far is that we
only ever select and try a single AP after a scan, i.e. ic_bss
currently remains unchanged once we've entered AUTH state, until
we somehow re-enter INIT or SCAN state.
But this will change with an upcoming diff that fixes net80211
behaviour for APs which perform band-steering.
Index: dev/pci/if_iwm.c
===================================================================
RCS file: /cvs/src/sys/dev/pci/if_iwm.c,v
retrieving revision 1.230
diff -u -p -r1.230 if_iwm.c
--- dev/pci/if_iwm.c 23 May 2018 17:49:20 -0000 1.230
+++ dev/pci/if_iwm.c 11 Aug 2018 10:52:43 -0000
@@ -3438,6 +3438,7 @@ iwm_rx_frame(struct iwm_softc *sc, struc
struct iwm_rx_phy_info *phy_info;
int device_timestamp;
int rssi, chanidx;
+ uint8_t saved_bssid[IEEE80211_ADDR_LEN] = { 0 };
phy_info = &sc->sc_last_phy_info;
if (__predict_false(phy_info->cfg_phy_cnt > 20))
@@ -3465,6 +3466,7 @@ iwm_rx_frame(struct iwm_softc *sc, struc
* Record the current channel so we can restore it later.
*/
bss_chan = ni->ni_chan;
+ IEEE80211_ADDR_COPY(&saved_bssid, ni->ni_macaddr);
}
ni->ni_chan = &ic->ic_channels[chanidx];
@@ -3529,7 +3531,11 @@ iwm_rx_frame(struct iwm_softc *sc, struc
}
#endif
ieee80211_input(IC2IFP(ic), m, ni, &rxi);
- if (ni == ic->ic_bss)
+ /*
+ * ieee80211_input() might have changed our BSS.
+ * Restore ic_bss's channel if we are still in the same BSS.
+ */
+ if (ni == ic->ic_bss && IEEE80211_ADDR_EQ(saved_bssid, ni->ni_macaddr))
ni->ni_chan = bss_chan;
ieee80211_release_node(ic, ni);