Some access points advertise BSS load information in beacons in
order to help clients make informed roaming decisions.

BSS load information includes the number of associated stations,
the channel utilization (this takes other networks on the same
channel into account), and current admission capacity (interesting
for clients which use QoS, which we do not).

We currently ignore BSS load information and only use RSSI to tell APs
apart. With this patch we store bss load information for APs during
scans if available, and take it into account when chosing an access point.

Unfortunately, I do not have a suitable AP available to test with.
tcpdump can be used to check whether an access point supports this
feature. Run this command while associated to the access point:
 tcpdump -n -i iwm0 -y IEEE802_11_RADIO -s 4096 -v

If the AP reports BSS load information you should see something like
this among the information printed by tcpdump:
  64 stations, 100% utilization, admission capacity 0us/s

It would help to get this patch tested in an environment where access
points advertise BSS load information, with a roaming test between
different access points.
I wrote down information about how roaming can be tested here:
https://marc.info/?l=openbsd-tech&m=163329420019842&w=2

Thanks!

diff 8c2348c4b5d36dddcf1de4644b5bb2c5dd715299 
187113184307352db702dff07a73873bbd1f087c
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 + f09c34dca9ffcfc1fca60c1db984c22d6f1c482d
--- sys/net80211/ieee80211_node.c
+++ sys/net80211/ieee80211_node.c
@@ -74,6 +74,7 @@ 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 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 +1287,27 @@ ieee80211_node_join_bss(struct ieee80211com *ic, struc
        }
 }
 
+int
+ieee80211_node_prefer_bss(struct ieee80211_node *selbs,
+    struct ieee80211_node *ni)
+{
+       if (selbs == NULL)
+               return 1;
+
+       if ((ni->ni_flags & IEEE80211_NODE_BSSLOAD) &&
+           (selbs->ni_flags & IEEE80211_NODE_BSSLOAD)) {
+               if (ni->ni_chanutil < selbs->ni_chanutil)
+                       return 1;
+               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 +1339,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(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(selbs5, ni))
                                selbs5 = ni;
-               } else if (selbs == NULL || ni->ni_rssi > selbs->ni_rssi)
+               } else if (ieee80211_node_prefer_bss(selbs, ni))
                        selbs = ni;
        }
 
@@ -1334,13 +1356,25 @@ ieee80211_node_choose_bss(struct ieee80211com *ic, int
        /*
         * Prefer a 5Ghz AP even if its RSSI is weaker than the best 2Ghz AP
         * (as long as it meets the minimum RSSI threshold) since the 5Ghz band
-        * is usually less saturated.
+        * is usually less saturated. If we found APs on both bands and BSS
+        * load information is available then use it to pick the least busy AP.
         */
        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 ((selbs5->ni_flags & IEEE80211_NODE_BSSLOAD) &&
+                   (selbs2->ni_flags & IEEE80211_NODE_BSSLOAD)) {
+                       if (selbs5->ni_chanutil > selbs2->ni_chanutil)
+                               selbs = selbs2;
+                       else if (selbs5->ni_stacnt > selbs2->ni_stacnt)
+                               selbs = selbs2;
+                       else
+                               selbs = selbs5;
+               } else {
+                       selbs = (selbs5->ni_rssi >= selbs2->ni_rssi ?
+                           selbs5 : 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 *,





Reply via email to