Many of our drivers hardcode multicast frames to 1 Mbit/s on 2 GHz.
This assumes 11b interop, which is mandatory for all 2GHz PHYs and
thus generally works.

However, access points may modify the networks' basic rate set such
that it differs from the mandatory PHY rate set. (Standard laywers may
wish to consult sections such as "9.7.5.3 Rate selection for other group
addressed data and management frames" in 802.11-2012.)

This can be used to exclude 11b clients from a network, such that
the network appears as a "pure 11g" network (no 11b frames allowed).

It seems nowadays this has become the default for some vendor APs,
who then ship with an 11b compat mode labeled "legacy" for standard
compliance.

We should be more diligent in following the APs basic rate set.
The following diff introduces net80211 helpers for this and makes use
of them in iwn(4). Other drivers will need to be fixed over time.

ok?

Index: dev/pci/if_iwn.c
===================================================================
RCS file: /cvs/src/sys/dev/pci/if_iwn.c,v
retrieving revision 1.186
diff -u -p -r1.186 if_iwn.c
--- dev/pci/if_iwn.c    26 Apr 2017 07:53:17 -0000      1.186
+++ dev/pci/if_iwn.c    30 May 2017 08:29:24 -0000
@@ -2896,8 +2896,7 @@ iwn_tx(struct iwn_softc *sc, struct mbuf
        /* Choose a TX rate index. */
        if (IEEE80211_IS_MULTICAST(wh->i_addr1) ||
            type != IEEE80211_FC0_TYPE_DATA)
-               ridx = (IEEE80211_IS_CHAN_5GHZ(ni->ni_chan)) ?
-                   IWN_RIDX_OFDM6 : IWN_RIDX_CCK1;
+               ridx = wn->ridx[ieee80211_max_basic_rate(ic)];
        else if (ic->ic_fixed_mcs != -1)
                ridx = sc->fixed_ridx;
        else if (ic->ic_fixed_rate != -1)
@@ -3430,6 +3429,7 @@ iwn5000_add_node(struct iwn_softc *sc, s
 int
 iwn_set_link_quality(struct iwn_softc *sc, struct ieee80211_node *ni)
 {
+       struct ieee80211com *ic = &sc->sc_ic;
        struct iwn_node *wn = (void *)ni;
        struct ieee80211_rateset *rs = &ni->ni_rates;
        struct iwn_cmd_link_quality linkq;
@@ -3466,11 +3466,8 @@ iwn_set_link_quality(struct iwn_softc *s
                                break;
                }
 
-               /* Fill the rest with the lowest legacy rate. */
-               if (IEEE80211_IS_CHAN_5GHZ(ni->ni_chan))
-                       rinfo = &iwn_rates[IWN_RIDX_OFDM6];
-               else
-                       rinfo = &iwn_rates[IWN_RIDX_CCK1];
+               /* Fill the rest with the lowest basic rate. */
+               rinfo = &iwn_rates[ieee80211_min_basic_rate(ic)];
                while (i < IWN_MAX_TX_RETRIES) {
                        linkq.retry[i].plcp = rinfo->plcp;
                        linkq.retry[i].rflags = rinfo->flags;
@@ -4562,8 +4559,7 @@ iwn_config(struct iwn_softc *sc)
                return error;
        }
 
-       ridx = (sc->sc_ic.ic_curmode == IEEE80211_MODE_11A) ?
-           IWN_RIDX_OFDM6 : IWN_RIDX_CCK1;
+       ridx = ieee80211_max_basic_rate(ic);
        if ((error = iwn_add_broadcast_node(sc, 0, ridx)) != 0) {
                printf("%s: could not add broadcast node\n",
                    sc->sc_dev.dv_xname);
@@ -4898,8 +4894,7 @@ iwn_auth(struct iwn_softc *sc)
         * Reconfiguring RXON clears the firmware nodes table so we must
         * add the broadcast node again.
         */
-       ridx = IEEE80211_IS_CHAN_5GHZ(ni->ni_chan) ?
-           IWN_RIDX_OFDM6 : IWN_RIDX_CCK1;
+       ridx = ieee80211_max_basic_rate(ic);
        if ((error = iwn_add_broadcast_node(sc, 1, ridx)) != 0) {
                printf("%s: could not add broadcast node\n",
                    sc->sc_dev.dv_xname);
Index: net80211/ieee80211.c
===================================================================
RCS file: /cvs/src/sys/net80211/ieee80211.c,v
retrieving revision 1.58
diff -u -p -r1.58 ieee80211.c
--- net80211/ieee80211.c        13 Jan 2016 14:33:07 -0000      1.58
+++ net80211/ieee80211.c        30 May 2017 08:38:14 -0000
@@ -712,6 +712,43 @@ ieee80211_setbasicrates(struct ieee80211
        }
 }
 
+int
+ieee80211_min_basic_rate(struct ieee80211com *ic)
+{
+       struct ieee80211_rateset *rs = &ic->ic_bss->ni_rates;
+       int i;
+
+       for (i = 0; i < rs->rs_nrates; i++) {
+               if (rs->rs_rates[i] & IEEE80211_RATE_BASIC)
+                       return i;
+       }
+
+       return 0;
+}
+
+int
+ieee80211_max_basic_rate(struct ieee80211com *ic)
+{
+       struct ieee80211_rateset *rs = &ic->ic_bss->ni_rates;
+       int i, best, rval, best_rval;
+
+       /* Defaults to 1 Mbit/s on 2GHz and 6 Mbit/s on 5GHz. */
+       best = 0;
+       best_rval = (rs->rs_rates[best] & IEEE80211_RATE_VAL);
+
+       for (i = 0; i < rs->rs_nrates; i++) {
+               if ((rs->rs_rates[i] & IEEE80211_RATE_BASIC) == 0)
+                       continue;
+               rval = (rs->rs_rates[i] & IEEE80211_RATE_VAL);
+               if (rval > best_rval) {
+                       best_rval = rval;
+                       best = i;
+               }
+       }
+
+       return best;
+}
+
 /*
  * Set the current phy mode and recalculate the active channel
  * set based on the available channels for this mode.  Also
Index: net80211/ieee80211_var.h
===================================================================
RCS file: /cvs/src/sys/net80211/ieee80211_var.h,v
retrieving revision 1.77
diff -u -p -r1.77 ieee80211_var.h
--- net80211/ieee80211_var.h    2 Feb 2017 16:47:53 -0000       1.77
+++ net80211/ieee80211_var.h    30 May 2017 08:28:46 -0000
@@ -402,6 +402,8 @@ u_int       ieee80211_mhz2ieee(u_int, u_int);
 u_int  ieee80211_chan2ieee(struct ieee80211com *,
                const struct ieee80211_channel *);
 u_int  ieee80211_ieee2mhz(u_int, u_int);
+int    ieee80211_min_basic_rate(struct ieee80211com *);
+int    ieee80211_max_basic_rate(struct ieee80211com *);
 int    ieee80211_setmode(struct ieee80211com *, enum ieee80211_phymode);
 enum ieee80211_phymode ieee80211_next_mode(struct ifnet *);
 enum ieee80211_phymode ieee80211_chan2mode(struct ieee80211com *,

Reply via email to