On Wed, Nov 03, 2021 at 02:47:39PM +0100, Stefan Sperling wrote:
> If channel load is not a reliable indicator I would hope vendors are at
> least able to reliably count and report the number of associated stations?
Here is new patch which makes the following changes relative to the
previous version:
Only use BSS load to resolve a tie between APs which meet a minimum RSSI
threshold. Otherwise we keep relying on RSSI as the only indicator.
I hope this will avoid APs which are too far away to be usable.
Ignore channel load values advertised by APs, since they are unreliable
according to research done by hostapd developers. Only use the number
of associated stations as the indicator of BSS load.
ok?
diff 8c2348c4b5d36dddcf1de4644b5bb2c5dd715299
bc115f3279aa8e472b2d369c2a15d792574a27a1
blob - 888a39c43dd0bbe0629998a8bb1e24a66466c092
blob + 5bd87ce70a265cd354e29b105cfe48ead88b93a6
--- sys/net80211/ieee80211_input.c
+++ sys/net80211/ieee80211_input.c
@@ -1606,7 +1606,7 @@ ieee80211_recv_probe_resp(struct ieee80211com *ic, str
const struct ieee80211_frame *wh;
const u_int8_t *frm, *efrm;
const u_int8_t *tstamp, *ssid, *rates, *xrates, *edcaie, *wmmie;
- const u_int8_t *rsnie, *wpaie, *htcaps, *htop;
+ const u_int8_t *rsnie, *wpaie, *htcaps, *htop, *bssload;
u_int16_t capinfo, bintval;
u_int8_t chan, bchan, erp, dtim_count, dtim_period;
int is_new;
@@ -1647,7 +1647,7 @@ ieee80211_recv_probe_resp(struct ieee80211com *ic, str
capinfo = LE_READ_2(frm); frm += 2;
ssid = rates = xrates = edcaie = wmmie = rsnie = wpaie = NULL;
- htcaps = htop = NULL;
+ htcaps = htop = bssload = NULL;
bchan = ieee80211_chan2ieee(ic, ic->ic_bss->ni_chan);
chan = bchan;
erp = 0;
@@ -1712,6 +1712,13 @@ ieee80211_recv_probe_resp(struct ieee80211com *ic, str
wmmie = frm;
}
break;
+ case IEEE80211_ELEMID_QBSS_LOAD:
+ if (frm[1] < 5) {
+ ic->ic_stats.is_rx_elem_toosmall++;
+ break;
+ }
+ bssload = frm;
+ break;
}
frm += 2 + frm[1];
}
@@ -1975,6 +1982,18 @@ ieee80211_recv_probe_resp(struct ieee80211com *ic, str
ni->ni_rsncaps = wpa.rsn_caps;
}
}
+
+ if (bssload != NULL) {
+ ni->ni_stacnt = bssload[2] | (bssload[3] << 8);
+ ni->ni_chanutil = (bssload[4] * 100) / 255;
+ ni->ni_admcap = (bssload[5] | (bssload[6] << 8)) / 32;
+ ni->ni_flags |= IEEE80211_NODE_BSSLOAD;
+ } else {
+ ni->ni_stacnt = 0;
+ ni->ni_chanutil = 0;
+ ni->ni_admcap = 0;
+ ni->ni_flags &= ~IEEE80211_NODE_BSSLOAD;
+ }
}
if (ssid[1] != 0 && ni->ni_essid[0] == '\0') {
blob - 2fe4984654e67cc638ea1f7b3e48cbeeae24ecca
blob + d0537167f455b4fc3aa514e3c6cee79a2f4d8e59
--- sys/net80211/ieee80211_node.c
+++ sys/net80211/ieee80211_node.c
@@ -74,6 +74,8 @@ void ieee80211_setup_node(struct ieee80211com *, struc
const u_int8_t *);
struct ieee80211_node *ieee80211_alloc_node_helper(struct ieee80211com *);
void ieee80211_node_switch_bss(struct ieee80211com *, struct ieee80211_node *);
+int ieee80211_node_prefer_bss(struct ieee80211com *, struct ieee80211_node *,
+ 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 *);
@@ -1286,6 +1288,45 @@ ieee80211_node_join_bss(struct ieee80211com *ic, struc
}
}
+int
+ieee80211_node_prefer_bss(struct ieee80211com *ic,
+ struct ieee80211_node *selbs, struct ieee80211_node *ni)
+{
+ uint8_t min_2ghz_rssi, min_5ghz_rssi, min_ni_rssi, min_selbs_rssi;
+
+ if (selbs == NULL)
+ return 1;
+
+ if (ic->ic_max_rssi) {
+ min_2ghz_rssi = IEEE80211_RSSI_THRES_RATIO_2GHZ;
+ min_5ghz_rssi = IEEE80211_RSSI_THRES_RATIO_5GHZ;
+ } else {
+ min_2ghz_rssi = (uint8_t)IEEE80211_RSSI_THRES_2GHZ;
+ min_5ghz_rssi = (uint8_t)IEEE80211_RSSI_THRES_5GHZ;
+ }
+ min_ni_rssi = IEEE80211_IS_CHAN_5GHZ(ni->ni_chan) ?
+ min_5ghz_rssi : min_2ghz_rssi;
+ min_selbs_rssi = IEEE80211_IS_CHAN_5GHZ(selbs->ni_chan) ?
+ min_5ghz_rssi : min_2ghz_rssi;
+
+ if ((ni->ni_flags & IEEE80211_NODE_BSSLOAD) &&
+ (selbs->ni_flags & IEEE80211_NODE_BSSLOAD) &&
+ ni->ni_rssi > min_ni_rssi && selbs->ni_rssi > min_selbs_rssi) {
+ /*
+ * Channel load advertised in the QBSS IE varies a
+ * lot between vendor implementations. We only check
+ * the number of associated stations because of this.
+ */
+ if (ni->ni_stacnt < selbs->ni_stacnt)
+ return 1;
+ }
+
+ if (ni->ni_rssi > selbs->ni_rssi)
+ return 1;
+
+ return 0;
+}
+
struct ieee80211_node *
ieee80211_node_choose_bss(struct ieee80211com *ic, int bgscan,
struct ieee80211_node **curbs)
@@ -1317,12 +1358,12 @@ ieee80211_node_choose_bss(struct ieee80211com *ic, int
if (ic->ic_caps & IEEE80211_C_SCANALLBAND) {
if (IEEE80211_IS_CHAN_2GHZ(ni->ni_chan) &&
- (selbs2 == NULL || ni->ni_rssi > selbs2->ni_rssi))
+ ieee80211_node_prefer_bss(ic, selbs2, ni))
selbs2 = ni;
else if (IEEE80211_IS_CHAN_5GHZ(ni->ni_chan) &&
- (selbs5 == NULL || ni->ni_rssi > selbs5->ni_rssi))
+ ieee80211_node_prefer_bss(ic, selbs5, ni))
selbs5 = ni;
- } else if (selbs == NULL || ni->ni_rssi > selbs->ni_rssi)
+ } else if (ieee80211_node_prefer_bss(ic, selbs, ni))
selbs = ni;
}
@@ -1338,9 +1379,12 @@ ieee80211_node_choose_bss(struct ieee80211com *ic, int
*/
if (selbs5 && selbs5->ni_rssi > min_5ghz_rssi)
selbs = selbs5;
- else if (selbs5 && selbs2)
- selbs = (selbs5->ni_rssi >= selbs2->ni_rssi ? selbs5 : selbs2);
- else if (selbs2)
+ else if (selbs5 && selbs2) {
+ if (ieee80211_node_prefer_bss(ic, selbs5, selbs2))
+ selbs = selbs5;
+ else
+ selbs = selbs2;
+ } else if (selbs2)
selbs = selbs2;
else if (selbs5)
selbs = selbs5;
blob - d8fce4ca1f545d4f5d85fbf0ecd32e6959ad3003
blob + 14b5fbede6f1b9b2b6a1f048f5241a4ded9ec880
--- sys/net80211/ieee80211_node.h
+++ sys/net80211/ieee80211_node.h
@@ -287,6 +287,11 @@ struct ieee80211_node {
struct ieee80211_channel *ni_chan;
u_int8_t ni_erp; /* 11g only */
+ /* bss load information (valid if IEEE80211_NODE_BSSLOAD flag is set) */
+ uint16_t ni_stacnt; /* number of associated stations */
+ uint8_t ni_chanutil; /* channel utilization */
+ uint16_t ni_admcap; /* admission capacity */
+
/* DTIM and contention free period (CFP) */
u_int8_t ni_dtimcount;
u_int8_t ni_dtimperiod;
@@ -406,6 +411,7 @@ struct ieee80211_node {
#define IEEE80211_NODE_HT_SGI40 0x8000 /* SGI on 40 MHz
negotiated */
#define IEEE80211_NODE_VHT 0x10000 /* VHT negotiated */
#define IEEE80211_NODE_HTCAP 0x20000 /* claims to support HT */
+#define IEEE80211_NODE_BSSLOAD 0x40000 /* reports BSS load info */
/* If not NULL, this function gets called when ni_refcnt hits zero. */
void (*ni_unref_cb)(struct ieee80211com *,