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 *,