wpa_supplicant keeps a blacklist of BSSes in order to prevent repeated associations to problematic APs*. Currently, this blacklist is completely cleared whenever we successfully connect to any AP. This causes problematic behaviour when in the presence of both a bad AP and a good AP. The device can repeatedly attempt to roam to the bad AP because it is clearing the blacklist every time it connects to the good AP. This results in the connection constantly ping-ponging between the APs, leaving the user stuck without connection. Instead of clearing the blacklist, implement timeout functionality which allows association attempts to blacklisted APs after some time has passed. Each time a BSS would be added to the blacklist, increase the duration of this timeout exponentially, up to a cap of 1800 seconds. This means that the device will no longer be able to immediately attempt to roam back to a bad AP whenever it succesfully connects to any other AP. Other details: The algorithm for building up the blacklist count and timeout duration on a given AP has been designed to be minimally obtrusive. Starting with a fresh blacklist, the device may attempt to connect to a problematic AP no more than 6 times in any ~45 minute period. Once an AP has reached a blacklist count >= 6, the device may attempt to connect to it no more than once every 30 minutes. The goal of these limits is to find an ideal balance between minimizing connection attempts to bad APs while still trying them out occasionally to see if the problems have stopped. The only exception to the above limits is that the blacklist is still completely cleared whenever there are no APs available in a scan. This means that if all nearby APs have been blacklisted, all APs will be completely exonerated regardless of their blacklist counts or how close their blacklist entries are to expiring. When all nearby APs have been blacklisted we know that every nearby AP is in some way problematic. Once we know that every AP is causing problems, it doesn't really make sense to sort them beyond that because the blacklist count and timeout durations don't neccesarily reflect the degree to which an AP is problematic (i.e. they can be manipulated by external factors such as the user physically moving around). Instead, its best to restart the blacklist and let the normal roaming algorithm take over to maximize our chance of getting the best possible connection quality. As stated above, the time-based blacklisting algorithm is designed to be minimally obtrusive to user experience, so occasionally restarting the process is not too impactful on the user. *problematic AP: rejects new clients, frequently de-auths clients, very poor connection quality, etc. BUG=chromium:1040974, chromium:1051374 TEST=network_WiFi_BadAPAssocAttempts Run test and check debugging logs. Blacklist timeout behaves as expected. Device can reconnect to blacklisted BSS after the blacklist has expired. Run other roaming/connectivity tests to check for regressions. Run suite:wifi_matfunc: "Total PASS: 180/180 (100%)" TEST=`./run-build-tests.sh` TEST=Emerge and deploy hostap-test on board Betty to cros_vm instance `tast -verbose run -var=network.HostapHwsim.runArgs='-f module_tests' localhost:9222 network.HostapHwsim.full` `tast -verbose run -var=network.HostapHwsim.runArgs='-f ap_open ap_roam wpas_ctrl' localhost:9222 network.HostapHwsim.full` `tast -verbose run localhost:9222 network.HostapHwsim.full` tast -verbose run localhost:9222 network.HostapHwsim.sanity` No new hwsim test failures are introduced by this change. Signed-off-by: Kevin Lund <kglund@xxxxxxxxxx> Change-Id: I48cedd19d2fd3e8fcbcf5bc6fd84274fcf01f219 --- wpa_supplicant/blacklist.c | 45 ++++++++++++++++++++++++++++++--- wpa_supplicant/blacklist.h | 8 ++++++ wpa_supplicant/events.c | 12 ++++----- wpa_supplicant/wpa_supplicant.c | 3 +-- 4 files changed, 56 insertions(+), 12 deletions(-) diff --git a/wpa_supplicant/blacklist.c b/wpa_supplicant/blacklist.c index 1274a5d24..74d6b0bcd 100644 --- a/wpa_supplicant/blacklist.c +++ b/wpa_supplicant/blacklist.c @@ -56,16 +56,30 @@ struct wpa_blacklist * wpa_blacklist_get(struct wpa_supplicant *wpa_s, int wpa_blacklist_add(struct wpa_supplicant *wpa_s, const u8 *bssid) { struct wpa_blacklist *e; + struct os_reltime now; if (wpa_s == NULL || bssid == NULL) return -1; e = wpa_blacklist_get(wpa_s, bssid); + os_get_reltime(&now); if (e) { + e->blacklist_start = now; e->count++; + if (e->count > 5) { + e->timeout_secs = 1800; + } else if (e->count == 5) { + e->timeout_secs = 600; + } else if (e->count == 4) { + e->timeout_secs = 120; + } else if (e->count == 3) { + e->timeout_secs = 60; + } else { + e->timeout_secs = 10; + } wpa_printf(MSG_INFO, "BSSID " MACSTR " blacklist count " - "incremented to %d", - MAC2STR(bssid), e->count); + "incremented to %d, blacklisting for %d seconds", + MAC2STR(bssid), e->count, e->timeout_secs); return e->count; } @@ -74,10 +88,12 @@ int wpa_blacklist_add(struct wpa_supplicant *wpa_s, const u8 *bssid) return -1; os_memcpy(e->bssid, bssid, ETH_ALEN); e->count = 1; + e->timeout_secs = 10; + e->blacklist_start = now; e->next = wpa_s->blacklist; wpa_s->blacklist = e; - wpa_printf(MSG_DEBUG, "Added BSSID " MACSTR " into blacklist", - MAC2STR(bssid)); + wpa_printf(MSG_DEBUG, "Added BSSID " MACSTR " into blacklist, blacklisting " + "for %d seconds", MAC2STR(bssid), e->timeout_secs); return e->count; } @@ -116,6 +132,27 @@ int wpa_blacklist_del(struct wpa_supplicant *wpa_s, const u8 *bssid) } +/** + * wpa_blacklist_is_blacklisted - Check the blacklist status of a BSSID + * @wpa_s: Pointer to wpa_supplicant data + * @bssid: BSSID to be checked + * Returns: count if BSSID is currently considered to be blacklisted, 0 otherwise + */ +int wpa_blacklist_is_blacklisted(struct wpa_supplicant *wpa_s, const u8 *bssid) +{ + struct wpa_blacklist *e; + struct os_reltime now; + + e = wpa_blacklist_get(wpa_s, bssid); + if (!e) + return 0; + os_get_reltime(&now); + if (os_reltime_expired(&now, &e->blacklist_start, e->timeout_secs)) + return 0; + return e->count; +} + + /** * wpa_blacklist_clear - Clear the blacklist of all entries * @wpa_s: Pointer to wpa_supplicant data diff --git a/wpa_supplicant/blacklist.h b/wpa_supplicant/blacklist.h index ae06986f3..92990fb6d 100644 --- a/wpa_supplicant/blacklist.h +++ b/wpa_supplicant/blacklist.h @@ -13,12 +13,20 @@ struct wpa_blacklist { struct wpa_blacklist *next; u8 bssid[ETH_ALEN]; int count; + /* Time of most recent blacklist event. */ + struct os_reltime blacklist_start; + /* + * Number of seconds after blacklist_start that the entry will be considered + * blacklisted. + */ + int timeout_secs; }; struct wpa_blacklist * wpa_blacklist_get(struct wpa_supplicant *wpa_s, const u8 *bssid); int wpa_blacklist_add(struct wpa_supplicant *wpa_s, const u8 *bssid); int wpa_blacklist_del(struct wpa_supplicant *wpa_s, const u8 *bssid); +int wpa_blacklist_is_blacklisted(struct wpa_supplicant *wpa_s, const u8 *bssid); void wpa_blacklist_clear(struct wpa_supplicant *wpa_s); #endif /* BLACKLIST_H */ diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c index 3300781aa..55e21810a 100644 --- a/wpa_supplicant/events.c +++ b/wpa_supplicant/events.c @@ -1015,7 +1015,6 @@ struct wpa_ssid * wpa_scan_res_match(struct wpa_supplicant *wpa_s, { u8 wpa_ie_len, rsn_ie_len; int wpa; - struct wpa_blacklist *e; const u8 *ie; struct wpa_ssid *ssid; int osen, rsn_osen = 0; @@ -1025,6 +1024,7 @@ struct wpa_ssid * wpa_scan_res_match(struct wpa_supplicant *wpa_s, const u8 *match_ssid; size_t match_ssid_len; struct wpa_ie_data data; + int blacklist_count; ie = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE); wpa_ie_len = ie ? ie[1] : 0; @@ -1053,8 +1053,8 @@ struct wpa_ssid * wpa_scan_res_match(struct wpa_supplicant *wpa_s, osen ? " osen=1" : ""); } - e = wpa_blacklist_get(wpa_s, bss->bssid); - if (e) { + blacklist_count = wpa_blacklist_is_blacklisted(wpa_s, bss->bssid); + if (blacklist_count) { int limit = 1; if (wpa_supplicant_enabled_networks(wpa_s) == 1) { /* @@ -1067,11 +1067,11 @@ struct wpa_ssid * wpa_scan_res_match(struct wpa_supplicant *wpa_s, */ limit = 0; } - if (e->count > limit) { + if (blacklist_count > limit) { if (debug_print) { wpa_dbg(wpa_s, MSG_DEBUG, " skip - blacklisted (count=%d limit=%d)", - e->count, limit); + blacklist_count, limit); } return NULL; } @@ -1121,7 +1121,7 @@ struct wpa_ssid * wpa_scan_res_match(struct wpa_supplicant *wpa_s, } #ifdef CONFIG_WPS - if ((ssid->key_mgmt & WPA_KEY_MGMT_WPS) && e && e->count > 0) { + if ((ssid->key_mgmt & WPA_KEY_MGMT_WPS) && blacklist_count) { if (debug_print) wpa_dbg(wpa_s, MSG_DEBUG, " skip - blacklisted (WPS)"); diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c index 2528fe336..8478096e9 100644 --- a/wpa_supplicant/wpa_supplicant.c +++ b/wpa_supplicant/wpa_supplicant.c @@ -926,7 +926,6 @@ void wpa_supplicant_set_state(struct wpa_supplicant *wpa_s, fils_hlp_sent ? " FILS_HLP_SENT" : ""); #endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */ wpas_clear_temp_disabled(wpa_s, ssid, 1); - wpa_blacklist_clear(wpa_s); wpa_s->consecutive_conn_failures = 0; wpa_s->new_connection = 0; wpa_drv_set_operstate(wpa_s, 1); @@ -6672,7 +6671,7 @@ static int * get_bss_freqs_in_ess(struct wpa_supplicant *wpa_s) continue; if (bss->ssid_len == cbss->ssid_len && os_memcmp(bss->ssid, cbss->ssid, bss->ssid_len) == 0 && - wpa_blacklist_get(wpa_s, bss->bssid) == NULL) { + !wpa_blacklist_is_blacklisted(wpa_s, bss->bssid)) { add_freq(freqs, &num_freqs, bss->freq); if (num_freqs == max_freqs) break; -- 2.26.2.761.g0e0b3e54be-goog _______________________________________________ Hostap mailing list Hostap@xxxxxxxxxxxxxxxxxxx http://lists.infradead.org/mailman/listinfo/hostap