-/* $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 $ */
/*-
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 *);
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;
* 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;
}
/*
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,
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;
-/* $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 $ */
/*-
}
#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,
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;
}
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 */