On Sat, Mar 19, 2022 at 05:32:48PM +0100, Stefan Sperling wrote:
> The net80211 input routine expects that ic->ic_bss->ni_chan will always
> correspond to the channel which is currently being scanned. This dates
> back to older devices which are manually tuned to the next channel by the
> driver during SCAN->SCAN state transitions. And this must of course be
> kept working for these drivers.
> 
> However, this approach is very awkward for drivers which scan across a
> whole range of channels in firmware. Right now such drivers have an ugly
> workaround around in place which changes the ni_chan variable for each
> received frame.
> 
> This patch introduces a channel number field in the Rx info struct which
> can be used to indicate the hardware channel number on which a frame was
> received. If this field is set, net80211 will use it instead of using the
> current channel of ic_bss.
> 
> Works for me on iwm(4).
> Additional tests on iwn, iwm, iwx, and bwfm would be good.

jmc@ managed to trigger a bug where the interface ended up in 11ac
mode on a 2GHz channel. Should be fixed in this version of the diff.

diff 72ccef0dd6ed210ccf409e8bd0918949ccb70238 
2b44274faa697c674c40e5a1ba5ca818cdf5caf4
blob - 15e712c92ee21ac76262d534d58b7f5548341107
blob + ef8ea9876a4f77106aa42033fdff7f8f2bb53d4c
--- sys/dev/ic/bwfm.c
+++ sys/dev/ic/bwfm.c
@@ -2703,8 +2703,6 @@ bwfm_scan_node(struct bwfm_softc *sc, struct bwfm_bss_
        struct ieee80211_frame *wh;
        struct ieee80211_node *ni;
        struct ieee80211_rxinfo rxi;
-       struct ieee80211_channel *bss_chan;
-       uint8_t saved_bssid[IEEE80211_ADDR_LEN] = { 0 };
        struct mbuf *m;
        uint32_t pktlen, ieslen;
        uint16_t iesoff;
@@ -2739,28 +2737,14 @@ bwfm_scan_node(struct bwfm_softc *sc, struct bwfm_bss_
        /* Finalize mbuf. */
        m->m_pkthdr.len = m->m_len = pktlen;
        ni = ieee80211_find_rxnode(ic, wh);
-       if (ni == ic->ic_bss) {
-               /*
-                * We may switch ic_bss's channel during scans.
-                * Record the current channel so we can restore it later.
-                */
-               bss_chan = ni->ni_chan;
-               IEEE80211_ADDR_COPY(&saved_bssid, ni->ni_macaddr);
-       }
        /* Channel mask equals IEEE80211_CHAN_MAX */
        chanidx = bwfm_spec2chan(sc, letoh32(bss->chanspec));
-       ni->ni_chan = &ic->ic_channels[chanidx];
        /* Supply RSSI */
        rxi.rxi_flags = 0;
        rxi.rxi_rssi = (int16_t)letoh16(bss->rssi);
        rxi.rxi_tstamp = 0;
+       rxi.rxi_chan = chanidx;
        ieee80211_input(ifp, m, ni, &rxi);
-       /*
-        * 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;
        /* Node is no longer needed. */
        ieee80211_release_node(ic, ni);
 }
blob - c7115ed57c8f3367983d438ade529d39a8cd2872
blob + 984ac6f8913c3f576a724e13dbaf9371d14b3d9a
--- sys/dev/pci/if_iwm.c
+++ sys/dev/pci/if_iwm.c
@@ -4775,24 +4775,12 @@ iwm_rx_frame(struct iwm_softc *sc, struct mbuf *m, int
        struct ifnet *ifp = IC2IFP(ic);
        struct ieee80211_frame *wh;
        struct ieee80211_node *ni;
-       struct ieee80211_channel *bss_chan;
-       uint8_t saved_bssid[IEEE80211_ADDR_LEN] = { 0 };
 
        if (chanidx < 0 || chanidx >= nitems(ic->ic_channels))  
                chanidx = ieee80211_chan2ieee(ic, ic->ic_ibss_chan);
 
        wh = mtod(m, struct ieee80211_frame *);
        ni = ieee80211_find_rxnode(ic, wh);
-       if (ni == ic->ic_bss) {
-               /* 
-                * We may switch ic_bss's channel during scans.
-                * 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];
-
        if ((rxi->rxi_flags & IEEE80211_RXI_HWDEC) &&
            iwm_ccmp_decap(sc, m, ni, rxi) != 0) {
                ifp->if_ierrors++;
@@ -4856,12 +4844,6 @@ iwm_rx_frame(struct iwm_softc *sc, struct mbuf *m, int
        }
 #endif
        ieee80211_inputm(IC2IFP(ic), m, ni, rxi, ml);
-       /*
-        * ieee80211_inputm() 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);
 }
 
@@ -4935,6 +4917,7 @@ iwm_rx_mpdu(struct iwm_softc *sc, struct mbuf *m, void
 
        rxi.rxi_rssi = rssi;
        rxi.rxi_tstamp = device_timestamp;
+       rxi.rxi_chan = chanidx;
 
        iwm_rx_frame(sc, m, chanidx, rx_pkt_status,
            (phy_flags & IWM_PHY_INFO_FLAG_SHPREAMBLE),
@@ -5465,6 +5448,7 @@ iwm_rx_mpdu_mq(struct iwm_softc *sc, struct mbuf *m, v
 
        rxi.rxi_rssi = rssi;
        rxi.rxi_tstamp = le64toh(desc->v1.tsf_on_air_rise);
+       rxi.rxi_chan = chanidx;
 
        if (iwm_rx_reorder(sc, m, chanidx, desc,
            (phy_info & IWM_RX_MPDU_PHY_SHORT_PREAMBLE),
blob - dd629227807ce8697b1a9da6374a3be68279a6e7
blob + c284dc7d27a91d3b05972122edd37f7f8f58910b
--- sys/dev/pci/if_iwn.c
+++ sys/dev/pci/if_iwn.c
@@ -2004,8 +2004,6 @@ iwn_rx_done(struct iwn_softc *sc, struct iwn_rx_desc *
        struct ieee80211_frame *wh;
        struct ieee80211_rxinfo rxi;
        struct ieee80211_node *ni;
-       struct ieee80211_channel *bss_chan = NULL;
-       uint8_t saved_bssid[IEEE80211_ADDR_LEN] = { 0 };
        struct mbuf *m, *m1;
        struct iwn_rx_stat *stat;
        caddr_t head;
@@ -2174,17 +2172,6 @@ iwn_rx_done(struct iwn_softc *sc, struct iwn_rx_desc *
        if (chan > IEEE80211_CHAN_MAX)
                chan = IEEE80211_CHAN_MAX;
 
-       /* Fix current channel. */
-       if (ni == ic->ic_bss) {
-               /*
-                * We may switch ic_bss's channel during scans.
-                * 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[chan];
-
 #if NBPFILTER > 0
        if (sc->sc_drvbpf != NULL) {
                struct iwn_rx_radiotap_header *tap = &sc->sc_rxtap;
@@ -2232,15 +2219,9 @@ iwn_rx_done(struct iwn_softc *sc, struct iwn_rx_desc *
        /* Send the frame to the 802.11 layer. */
        rxi.rxi_rssi = rssi;
        rxi.rxi_tstamp = 0;     /* unused */
+       rxi.rxi_chan = chan;
        ieee80211_inputm(ifp, m, ni, &rxi, ml);
 
-       /*
-        * ieee80211_inputm() 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;
-
        /* Node is no longer needed. */
        ieee80211_release_node(ic, ni);
 }
blob - ba54430a5bbe62eea3749d62d72ee14d97ffb11e
blob + d63c8ed78396db023402012a22f12635e51fbcb6
--- sys/dev/pci/if_iwx.c
+++ sys/dev/pci/if_iwx.c
@@ -3992,24 +3992,12 @@ iwx_rx_frame(struct iwx_softc *sc, struct mbuf *m, int
        struct ifnet *ifp = IC2IFP(ic);
        struct ieee80211_frame *wh;
        struct ieee80211_node *ni;
-       struct ieee80211_channel *bss_chan;
-       uint8_t saved_bssid[IEEE80211_ADDR_LEN] = { 0 };
 
        if (chanidx < 0 || chanidx >= nitems(ic->ic_channels))  
                chanidx = ieee80211_chan2ieee(ic, ic->ic_ibss_chan);
 
        wh = mtod(m, struct ieee80211_frame *);
        ni = ieee80211_find_rxnode(ic, wh);
-       if (ni == ic->ic_bss) {
-               /* 
-                * We may switch ic_bss's channel during scans.
-                * 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];
-
        if ((rxi->rxi_flags & IEEE80211_RXI_HWDEC) &&
            iwx_ccmp_decap(sc, m, ni, rxi) != 0) {
                ifp->if_ierrors++;
@@ -4073,12 +4061,6 @@ iwx_rx_frame(struct iwx_softc *sc, struct mbuf *m, int
        }
 #endif
        ieee80211_inputm(IC2IFP(ic), m, ni, rxi, ml);
-       /*
-        * ieee80211_inputm() 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);
 }
 
@@ -4593,6 +4575,7 @@ iwx_rx_mpdu_mq(struct iwx_softc *sc, struct mbuf *m, v
 
        rxi.rxi_rssi = rssi;
        rxi.rxi_tstamp = le64toh(desc->v1.tsf_on_air_rise);
+       rxi.rxi_chan = chanidx;
 
        if (iwx_rx_reorder(sc, m, chanidx, desc,
            (phy_info & IWX_RX_MPDU_PHY_SHORT_PREAMBLE),
blob - 29e79454bd5f94785ab0c1f39a0e8e61b997aae2
blob + f9064eaba21945f89a07b33857cde41c004d6dfe
--- sys/net80211/ieee80211_input.c
+++ sys/net80211/ieee80211_input.c
@@ -1648,7 +1648,10 @@ ieee80211_recv_probe_resp(struct ieee80211com *ic, str
 
        ssid = rates = xrates = edcaie = wmmie = rsnie = wpaie = tim = NULL;
        htcaps = htop = vhtcaps = vhtop = NULL;
-       bchan = ieee80211_chan2ieee(ic, ic->ic_bss->ni_chan);
+       if (rxi->rxi_chan)
+               bchan = rxi->rxi_chan;
+       else
+               bchan = ieee80211_chan2ieee(ic, ic->ic_bss->ni_chan);
        chan = bchan;
        erp = 0;
        while (frm + 2 <= efrm) {
@@ -1744,9 +1747,10 @@ ieee80211_recv_probe_resp(struct ieee80211com *ic, str
                ic->ic_stats.is_rx_badchan++;
                return;
        }
-       if ((ic->ic_state != IEEE80211_S_SCAN ||
+       if ((rxi->rxi_chan != 0 && chan != rxi->rxi_chan) ||
+           ((ic->ic_state != IEEE80211_S_SCAN ||
             !(ic->ic_caps & IEEE80211_C_SCANALL)) &&
-           chan != bchan) {
+           chan != bchan)) {
                /*
                 * Frame was received on a channel different from the
                 * one indicated in the DS params element id;
@@ -1783,11 +1787,13 @@ ieee80211_recv_probe_resp(struct ieee80211com *ic, str
        } else
                is_new = 0;
 
+       ni->ni_chan = &ic->ic_channels[chan];
+
        if (htcaps)
                ieee80211_setup_htcaps(ni, htcaps + 2, htcaps[1]);
        if (htop && !ieee80211_setup_htop(ni, htop + 2, htop[1], 1))
                htop = NULL; /* invalid HTOP */
-       if (htcaps && vhtcaps && IEEE80211_IS_CHAN_5GHZ(ic->ic_bss->ni_chan)) {
+       if (htcaps && vhtcaps && IEEE80211_IS_CHAN_5GHZ(ni->ni_chan)) {
                ieee80211_setup_vhtcaps(ni, vhtcaps + 2, vhtcaps[1]);
                if (vhtop && !ieee80211_setup_vhtop(ni, vhtop + 2, vhtop[1], 1))
                        vhtop = NULL; /* invalid VHTOP */
@@ -1984,8 +1990,6 @@ ieee80211_recv_probe_resp(struct ieee80211com *ic, str
                memcpy(ni->ni_essid, &ssid[2], ssid[1]);
        }
        IEEE80211_ADDR_COPY(ni->ni_bssid, wh->i_addr3);
-       /* XXX validate channel # */
-       ni->ni_chan = &ic->ic_channels[chan];
        if (ic->ic_state == IEEE80211_S_SCAN &&
            IEEE80211_IS_CHAN_5GHZ(ni->ni_chan)) {
                /*
@@ -2645,7 +2649,7 @@ ieee80211_recv_assoc_resp(struct ieee80211com *ic, str
                ieee80211_setup_htop(ni, htop + 2, htop[1], 0);
        ieee80211_ht_negotiate(ic, ni);
 
-       if (htcaps && vhtcaps && IEEE80211_IS_CHAN_5GHZ(ic->ic_bss->ni_chan)) {
+       if (htcaps && vhtcaps && IEEE80211_IS_CHAN_5GHZ(ni->ni_chan)) {
                ieee80211_setup_vhtcaps(ni, vhtcaps + 2, vhtcaps[1]);
                if (vhtop && !ieee80211_setup_vhtop(ni, vhtop + 2, vhtop[1], 1))
                        vhtop = NULL; /* invalid VHTOP */
blob - d03d25669b60a63ffd168bb9884b80883c480e49
blob + a520f65f6904627038b8237518a16aaef64b6563
--- sys/net80211/ieee80211_node.h
+++ sys/net80211/ieee80211_node.h
@@ -192,6 +192,7 @@ struct ieee80211_rxinfo {
        u_int32_t               rxi_flags;
        u_int32_t               rxi_tstamp;
        int                     rxi_rssi;
+       uint8_t                 rxi_chan;
 };
 #define IEEE80211_RXI_HWDEC            0x00000001
 #define IEEE80211_RXI_AMPDU_DONE       0x00000002

Reply via email to