From 40dc231e714b0fcbf9ec09eb02aeab6d6154b58c Mon Sep 17 00:00:00 2001 From: stsp Date: Mon, 13 Aug 2018 15:19:52 +0000 Subject: [PATCH] Add support for band-steering access points to net80211. Some access points have a feature called "band steering" where they will try to push clients from 2 GHz channels to 5 GHz channels. If a client sends probe-requests on both 2 GHz and 5GHz channels, and then attempts to authenticate on a 2 GHz channel, such APs will deny authentication and hope that the client will come back on a 5 GHz channel. So if we fail to AUTH for any reason, and if there is a different AP with the same ESSID that we haven't tried yet, try that AP next. Keep trying until no APs are left, and only then continue scanning. APs with support for this feature were provided by Mischa Peters. ok phessler@ mpi@ --- sys/net80211/ieee80211_node.c | 135 +++++++++++++++++++-------------- sys/net80211/ieee80211_node.h | 5 +- sys/net80211/ieee80211_proto.c | 53 ++++++++++++- 3 files changed, 132 insertions(+), 61 deletions(-) diff --git a/sys/net80211/ieee80211_node.c b/sys/net80211/ieee80211_node.c index 6fcbf9334c8..bd64aea32b7 100644 --- a/sys/net80211/ieee80211_node.c +++ b/sys/net80211/ieee80211_node.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ieee80211_node.c,v 1.140 2018/08/11 10:58:39 stsp Exp $ */ +/* $OpenBSD: ieee80211_node.c,v 1.141 2018/08/13 15:19:52 stsp Exp $ */ /* $NetBSD: ieee80211_node.c,v 1.14 2004/05/09 09:18:47 dyoung Exp $ */ /*- @@ -76,7 +76,6 @@ void ieee80211_ba_del(struct ieee80211_node *); struct ieee80211_node *ieee80211_alloc_node_helper(struct ieee80211com *); void ieee80211_node_cleanup(struct ieee80211com *, struct ieee80211_node *); void ieee80211_node_switch_bss(struct ieee80211com *, struct ieee80211_node *); -void ieee80211_node_join_bss(struct ieee80211com *, struct ieee80211_node *); void ieee80211_needs_auth(struct ieee80211com *, struct ieee80211_node *); #ifndef IEEE80211_STA_ONLY void ieee80211_node_join_ht(struct ieee80211com *, struct ieee80211_node *); @@ -978,6 +977,9 @@ ieee80211_node_join_bss(struct ieee80211com *ic, struct ieee80211_node *selbs) int bgscan = ((ic->ic_flags & IEEE80211_F_BGSCAN) && ic->ic_opmode == IEEE80211_M_STA && ic->ic_state == IEEE80211_S_RUN); + int auth_next = (ic->ic_opmode == IEEE80211_M_STA && + ic->ic_state == IEEE80211_S_AUTH); + int mgt = -1; timeout_del(&ic->ic_bgscan_timeout); ic->ic_flags &= ~IEEE80211_F_BGSCAN; @@ -988,9 +990,80 @@ ieee80211_node_join_bss(struct ieee80211com *ic, struct ieee80211_node *selbs) * ieee80211_new_state() try to re-auth and thus send * an AUTH frame to our newly selected AP. */ - ieee80211_new_state(ic, IEEE80211_S_AUTH, - bgscan ? IEEE80211_FC0_SUBTYPE_DEAUTH : -1); + if (bgscan) + mgt = IEEE80211_FC0_SUBTYPE_DEAUTH; + /* + * If we are trying another AP after the previous one + * failed (state transition AUTH->AUTH), ensure that + * ieee80211_new_state() tries to send another auth frame. + */ + else if (auth_next) + mgt = IEEE80211_FC0_SUBTYPE_AUTH; + + ieee80211_new_state(ic, IEEE80211_S_AUTH, mgt); + } +} + +struct ieee80211_node * +ieee80211_node_choose_bss(struct ieee80211com *ic, int bgscan, + struct ieee80211_node **curbs) +{ + struct ieee80211_node *ni, *nextbs, *selbs = NULL, + *selbs2 = NULL, *selbs5 = NULL; + uint8_t min_5ghz_rssi; + + ni = RBT_MIN(ieee80211_tree, &ic->ic_tree); + + for (; ni != NULL; ni = nextbs) { + nextbs = RBT_NEXT(ieee80211_tree, ni); + if (ni->ni_fails) { + /* + * The configuration of the access points may change + * during my scan. So delete the entry for the AP + * and retry to associate if there is another beacon. + */ + if (ni->ni_fails++ > 2) + ieee80211_free_node(ic, ni); + continue; + } + + if (curbs && ieee80211_node_cmp(ic->ic_bss, ni) == 0) + *curbs = ni; + + if (ieee80211_match_bss(ic, ni) != 0) + continue; + + if (ic->ic_caps & IEEE80211_C_SCANALLBAND) { + if (IEEE80211_IS_CHAN_2GHZ(ni->ni_chan) && + (selbs2 == NULL || ni->ni_rssi > selbs2->ni_rssi)) + selbs2 = ni; + else if (IEEE80211_IS_CHAN_5GHZ(ni->ni_chan) && + (selbs5 == NULL || ni->ni_rssi > selbs5->ni_rssi)) + selbs5 = ni; + } else if (selbs == NULL || ni->ni_rssi > selbs->ni_rssi) + selbs = ni; } + + if (ic->ic_max_rssi) + min_5ghz_rssi = IEEE80211_RSSI_THRES_RATIO_5GHZ; + else + min_5ghz_rssi = (uint8_t)IEEE80211_RSSI_THRES_5GHZ; + + /* + * 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. + */ + 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) + selbs = selbs2; + else if (selbs5) + selbs = selbs5; + + return selbs; } /* @@ -1000,12 +1073,10 @@ void ieee80211_end_scan(struct ifnet *ifp) { struct ieee80211com *ic = (void *)ifp; - struct ieee80211_node *ni, *nextbs, *selbs = NULL, *curbs = NULL, - *selbs2 = NULL, *selbs5 = NULL; + struct ieee80211_node *ni, *selbs = NULL, *curbs = NULL; int bgscan = ((ic->ic_flags & IEEE80211_F_BGSCAN) && ic->ic_opmode == IEEE80211_M_STA && ic->ic_state == IEEE80211_S_RUN); - uint8_t min_5ghz_rssi; if (ifp->if_flags & IFF_DEBUG) printf("%s: end %s scan\n", ifp->if_xname, @@ -1083,55 +1154,7 @@ ieee80211_end_scan(struct ifnet *ifp) if (!bgscan && ic->ic_opmode == IEEE80211_M_STA) ieee80211_match_ess(ic); - for (; ni != NULL; ni = nextbs) { - nextbs = RBT_NEXT(ieee80211_tree, ni); - if (ni->ni_fails) { - /* - * The configuration of the access points may change - * during my scan. So delete the entry for the AP - * and retry to associate if there is another beacon. - */ - if (ni->ni_fails++ > 2) - ieee80211_free_node(ic, ni); - continue; - } - - if (bgscan && ieee80211_node_cmp(ic->ic_bss, ni) == 0) - curbs = ni; - - if (ieee80211_match_bss(ic, ni) != 0) - continue; - - if (ic->ic_caps & IEEE80211_C_SCANALLBAND) { - if (IEEE80211_IS_CHAN_2GHZ(ni->ni_chan) && - (selbs2 == NULL || ni->ni_rssi > selbs2->ni_rssi)) - selbs2 = ni; - else if (IEEE80211_IS_CHAN_5GHZ(ni->ni_chan) && - (selbs5 == NULL || ni->ni_rssi > selbs5->ni_rssi)) - selbs5 = ni; - } else if (selbs == NULL || ni->ni_rssi > selbs->ni_rssi) - selbs = ni; - } - - if (ic->ic_max_rssi) - min_5ghz_rssi = IEEE80211_RSSI_THRES_RATIO_5GHZ; - else - min_5ghz_rssi = (uint8_t)IEEE80211_RSSI_THRES_5GHZ; - - /* - * 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. - */ - 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) - selbs = selbs2; - else if (selbs5) - selbs = selbs5; - + selbs = ieee80211_node_choose_bss(ic, bgscan, &curbs); if (bgscan) { struct ieee80211_node_switch_bss_arg *arg; diff --git a/sys/net80211/ieee80211_node.h b/sys/net80211/ieee80211_node.h index dd07921bc7e..334efcdf5b1 100644 --- a/sys/net80211/ieee80211_node.h +++ b/sys/net80211/ieee80211_node.h @@ -1,4 +1,4 @@ -/* $OpenBSD: ieee80211_node.h,v 1.76 2018/08/07 18:13:14 stsp Exp $ */ +/* $OpenBSD: ieee80211_node.h,v 1.77 2018/08/13 15:19:52 stsp Exp $ */ /* $NetBSD: ieee80211_node.h,v 1.9 2004/04/30 22:57:32 dyoung Exp $ */ /*- @@ -416,6 +416,9 @@ void ieee80211_node_leave(struct ieee80211com *, struct ieee80211_node *); int ieee80211_match_bss(struct ieee80211com *, struct ieee80211_node *); +struct ieee80211_node *ieee80211_node_choose_bss(struct ieee80211com *, int, + struct ieee80211_node **); +void ieee80211_node_join_bss(struct ieee80211com *, struct ieee80211_node *); void ieee80211_create_ibss(struct ieee80211com* , struct ieee80211_channel *); void ieee80211_notify_dtim(struct ieee80211com *); diff --git a/sys/net80211/ieee80211_proto.c b/sys/net80211/ieee80211_proto.c index 51f1d0cfcc5..c1832f7f60b 100644 --- a/sys/net80211/ieee80211_proto.c +++ b/sys/net80211/ieee80211_proto.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ieee80211_proto.c,v 1.88 2018/08/06 14:28:13 stsp Exp $ */ +/* $OpenBSD: ieee80211_proto.c,v 1.89 2018/08/13 15:19:52 stsp Exp $ */ /* $NetBSD: ieee80211_proto.c,v 1.8 2004/04/30 23:58:20 dyoung Exp $ */ /*- @@ -733,6 +733,47 @@ ieee80211_auth_open_confirm(struct ieee80211com *ic, } #endif +void +ieee80211_try_another_bss(struct ieee80211com *ic) +{ + struct ieee80211_node *curbs, *selbs; + struct ifnet *ifp = &ic->ic_if; + + /* Don't select our current AP again. */ + curbs = ieee80211_find_node(ic, ic->ic_bss->ni_macaddr); + if (curbs) { + curbs->ni_fails++; + ieee80211_node_newstate(curbs, IEEE80211_STA_CACHE); + } + + /* Try a different AP from the same ESS if available. */ + if (ic->ic_caps & IEEE80211_C_SCANALLBAND) { + /* + * Make sure we will consider APs on all bands during + * access point selection in ieee80211_node_choose_bss(). + * During multi-band scans, our previous AP may be trying + * to steer us onto another band by denying authentication. + */ + ieee80211_setmode(ic, IEEE80211_MODE_AUTO); + } + selbs = ieee80211_node_choose_bss(ic, 0, NULL); + if (selbs == NULL) + return; + + /* Should not happen but seriously, don't try the same AP again. */ + if (memcmp(selbs->ni_macaddr, ic->ic_bss->ni_macaddr, + IEEE80211_NWID_LEN) == 0) + return; + + if (ifp->if_flags & IFF_DEBUG) + printf("%s: trying AP %s on channel %d instead\n", + ifp->if_xname, ether_sprintf(selbs->ni_macaddr), + ieee80211_chan2ieee(ic, selbs->ni_chan)); + + /* Triggers an AUTH->AUTH transition, avoiding another SCAN. */ + ieee80211_node_join_bss(ic, selbs); +} + void ieee80211_auth_open(struct ieee80211com *ic, const struct ieee80211_frame *wh, struct ieee80211_node *ni, struct ieee80211_rxinfo *rxi, u_int16_t seq, @@ -821,6 +862,8 @@ ieee80211_auth_open(struct ieee80211com *ic, const struct ieee80211_frame *wh, ether_sprintf((u_int8_t *)wh->i_addr3)); if (ni != ic->ic_bss) ni->ni_fails++; + else + ieee80211_try_another_bss(ic); ic->ic_stats.is_rx_auth_fail++; return; } @@ -1025,9 +1068,11 @@ justcleanup: case IEEE80211_S_ASSOC: switch (mgt) { case IEEE80211_FC0_SUBTYPE_AUTH: - /* ??? */ - IEEE80211_SEND_MGMT(ic, ni, - IEEE80211_FC0_SUBTYPE_AUTH, 2); + if (ic->ic_opmode == IEEE80211_M_STA) { + IEEE80211_SEND_MGMT(ic, ni, + IEEE80211_FC0_SUBTYPE_AUTH, + IEEE80211_AUTH_OPEN_REQUEST); + } break; case IEEE80211_FC0_SUBTYPE_DEAUTH: /* ignore and retry scan on timeout */ -- 2.20.1