WNM: Fetch scan results before checking transition candidates
On receiving a WNM BSS Transition Management Request frame with a candidate list, fetch the latest scan results from the kernel to see if there are any recent scan results for the candidates and initiate a connection if found. This helps to avoid triggering a new scan in cases where a scan initiated by something else (e.g., an internal beacon measurement report functionality in a driver) has processed Beacon or Probe Response frames without wpa_supplicant having received a notification of such an update yet. Signed-off-by: Jouni Malinen <jouni@qca.qualcomm.com>
This commit is contained in:
parent
49ae6ddbca
commit
2f195639ec
3 changed files with 151 additions and 26 deletions
|
@ -1875,8 +1875,8 @@ int wpa_supplicant_filter_bssid_match(struct wpa_supplicant *wpa_s,
|
|||
}
|
||||
|
||||
|
||||
static void filter_scan_res(struct wpa_supplicant *wpa_s,
|
||||
struct wpa_scan_results *res)
|
||||
void filter_scan_res(struct wpa_supplicant *wpa_s,
|
||||
struct wpa_scan_results *res)
|
||||
{
|
||||
size_t i, j;
|
||||
|
||||
|
@ -1909,7 +1909,7 @@ static void filter_scan_res(struct wpa_supplicant *wpa_s,
|
|||
#define DEFAULT_NOISE_FLOOR_2GHZ (-89)
|
||||
#define DEFAULT_NOISE_FLOOR_5GHZ (-92)
|
||||
|
||||
static void scan_snr(struct wpa_scan_res *res)
|
||||
void scan_snr(struct wpa_scan_res *res)
|
||||
{
|
||||
if (res->flags & WPA_SCAN_NOISE_INVALID) {
|
||||
res->noise = IS_5GHZ(res->freq) ?
|
||||
|
@ -1993,8 +1993,8 @@ static unsigned int max_vht80_rate(int snr)
|
|||
}
|
||||
|
||||
|
||||
static void scan_est_throughput(struct wpa_supplicant *wpa_s,
|
||||
struct wpa_scan_res *res)
|
||||
void scan_est_throughput(struct wpa_supplicant *wpa_s,
|
||||
struct wpa_scan_res *res)
|
||||
{
|
||||
enum local_hw_capab capab = wpa_s->hw_capab;
|
||||
int rate; /* max legacy rate in 500 kb/s units */
|
||||
|
|
|
@ -51,5 +51,10 @@ int wpas_mac_addr_rand_scan_set(struct wpa_supplicant *wpa_s,
|
|||
unsigned int type, const u8 *addr,
|
||||
const u8 *mask);
|
||||
int wpas_abort_ongoing_scan(struct wpa_supplicant *wpa_s);
|
||||
void filter_scan_res(struct wpa_supplicant *wpa_s,
|
||||
struct wpa_scan_results *res);
|
||||
void scan_snr(struct wpa_scan_res *res);
|
||||
void scan_est_throughput(struct wpa_supplicant *wpa_s,
|
||||
struct wpa_scan_res *res);
|
||||
|
||||
#endif /* SCAN_H */
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#define MAX_TFS_IE_LEN 1024
|
||||
#define WNM_MAX_NEIGHBOR_REPORT 10
|
||||
|
||||
#define WNM_SCAN_RESULT_AGE 2 /* 2 seconds */
|
||||
|
||||
/* get the TFS IE from driver */
|
||||
static int ieee80211_11_get_tfs_ie(struct wpa_supplicant *wpa_s, u8 *buf,
|
||||
|
@ -499,7 +500,7 @@ static void wnm_parse_neighbor_report(struct wpa_supplicant *wpa_s,
|
|||
|
||||
|
||||
static struct wpa_bss *
|
||||
compare_scan_neighbor_results(struct wpa_supplicant *wpa_s)
|
||||
compare_scan_neighbor_results(struct wpa_supplicant *wpa_s, os_time_t age_secs)
|
||||
{
|
||||
|
||||
u8 i;
|
||||
|
@ -532,6 +533,19 @@ compare_scan_neighbor_results(struct wpa_supplicant *wpa_s)
|
|||
continue;
|
||||
}
|
||||
|
||||
if (age_secs) {
|
||||
struct os_reltime now;
|
||||
|
||||
if (os_get_reltime(&now) == 0 &&
|
||||
os_reltime_expired(&now, &target->last_update,
|
||||
age_secs)) {
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"Candidate BSS is more than %ld seconds old",
|
||||
age_secs);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (bss->ssid_len != target->ssid_len ||
|
||||
os_memcmp(bss->ssid, target->ssid, bss->ssid_len) != 0) {
|
||||
/*
|
||||
|
@ -832,6 +846,41 @@ static void wnm_send_bss_transition_mgmt_resp(
|
|||
}
|
||||
|
||||
|
||||
static void wnm_bss_tm_connect(struct wpa_supplicant *wpa_s,
|
||||
struct wpa_bss *bss, struct wpa_ssid *ssid,
|
||||
int after_new_scan)
|
||||
{
|
||||
wpa_dbg(wpa_s, MSG_DEBUG,
|
||||
"WNM: Transition to BSS " MACSTR
|
||||
" based on BSS Transition Management Request (old BSSID "
|
||||
MACSTR " after_new_scan=%d)",
|
||||
MAC2STR(bss->bssid), MAC2STR(wpa_s->bssid), after_new_scan);
|
||||
|
||||
/* Send the BSS Management Response - Accept */
|
||||
if (wpa_s->wnm_reply) {
|
||||
wpa_s->wnm_reply = 0;
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"WNM: Sending successful BSS Transition Management Response");
|
||||
wnm_send_bss_transition_mgmt_resp(wpa_s,
|
||||
wpa_s->wnm_dialog_token,
|
||||
WNM_BSS_TM_ACCEPT,
|
||||
0, bss->bssid);
|
||||
}
|
||||
|
||||
if (bss == wpa_s->current_bss) {
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"WNM: Already associated with the preferred candidate");
|
||||
wnm_deallocate_memory(wpa_s);
|
||||
return;
|
||||
}
|
||||
|
||||
wpa_s->reassociate = 1;
|
||||
wpa_printf(MSG_DEBUG, "WNM: Issuing connect");
|
||||
wpa_supplicant_connect(wpa_s, bss, ssid);
|
||||
wnm_deallocate_memory(wpa_s);
|
||||
}
|
||||
|
||||
|
||||
int wnm_scan_process(struct wpa_supplicant *wpa_s, int reply_on_fail)
|
||||
{
|
||||
struct wpa_bss *bss;
|
||||
|
@ -841,6 +890,8 @@ int wnm_scan_process(struct wpa_supplicant *wpa_s, int reply_on_fail)
|
|||
if (!wpa_s->wnm_neighbor_report_elements)
|
||||
return 0;
|
||||
|
||||
wpa_dbg(wpa_s, MSG_DEBUG,
|
||||
"WNM: Process scan results for BSS Transition Management");
|
||||
if (os_reltime_before(&wpa_s->wnm_cand_valid_until,
|
||||
&wpa_s->scan_trigger_time)) {
|
||||
wpa_printf(MSG_DEBUG, "WNM: Previously stored BSS transition candidate list is not valid anymore - drop it");
|
||||
|
@ -856,7 +907,7 @@ int wnm_scan_process(struct wpa_supplicant *wpa_s, int reply_on_fail)
|
|||
}
|
||||
|
||||
/* Compare the Neighbor Report and scan results */
|
||||
bss = compare_scan_neighbor_results(wpa_s);
|
||||
bss = compare_scan_neighbor_results(wpa_s, 0);
|
||||
if (!bss) {
|
||||
wpa_printf(MSG_DEBUG, "WNM: No BSS transition candidate match found");
|
||||
status = WNM_BSS_TM_REJECT_NO_SUITABLE_CANDIDATES;
|
||||
|
@ -864,25 +915,7 @@ int wnm_scan_process(struct wpa_supplicant *wpa_s, int reply_on_fail)
|
|||
}
|
||||
|
||||
/* Associate to the network */
|
||||
/* Send the BSS Management Response - Accept */
|
||||
if (wpa_s->wnm_reply) {
|
||||
wpa_s->wnm_reply = 0;
|
||||
wnm_send_bss_transition_mgmt_resp(wpa_s,
|
||||
wpa_s->wnm_dialog_token,
|
||||
WNM_BSS_TM_ACCEPT,
|
||||
0, bss->bssid);
|
||||
}
|
||||
|
||||
if (bss == wpa_s->current_bss) {
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"WNM: Already associated with the preferred candidate");
|
||||
wnm_deallocate_memory(wpa_s);
|
||||
return 1;
|
||||
}
|
||||
|
||||
wpa_s->reassociate = 1;
|
||||
wpa_supplicant_connect(wpa_s, bss, ssid);
|
||||
wnm_deallocate_memory(wpa_s);
|
||||
wnm_bss_tm_connect(wpa_s, bss, ssid, 1);
|
||||
return 1;
|
||||
|
||||
send_bss_resp_fail:
|
||||
|
@ -1023,6 +1056,79 @@ static void wnm_set_scan_freqs(struct wpa_supplicant *wpa_s)
|
|||
}
|
||||
|
||||
|
||||
static int wnm_fetch_scan_results(struct wpa_supplicant *wpa_s)
|
||||
{
|
||||
struct wpa_scan_results *scan_res;
|
||||
struct wpa_bss *bss;
|
||||
struct wpa_ssid *ssid = wpa_s->current_ssid;
|
||||
u8 i, found = 0;
|
||||
size_t j;
|
||||
|
||||
wpa_dbg(wpa_s, MSG_DEBUG,
|
||||
"WNM: Fetch current scan results from the driver for checking transition candidates");
|
||||
scan_res = wpa_drv_get_scan_results2(wpa_s);
|
||||
if (!scan_res) {
|
||||
wpa_dbg(wpa_s, MSG_DEBUG, "WNM: Failed to get scan results");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (scan_res->fetch_time.sec == 0)
|
||||
os_get_reltime(&scan_res->fetch_time);
|
||||
|
||||
filter_scan_res(wpa_s, scan_res);
|
||||
|
||||
for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) {
|
||||
struct neighbor_report *nei;
|
||||
|
||||
nei = &wpa_s->wnm_neighbor_report_elements[i];
|
||||
if (nei->preference_present && nei->preference == 0)
|
||||
continue;
|
||||
|
||||
for (j = 0; j < scan_res->num; j++) {
|
||||
struct wpa_scan_res *res;
|
||||
const u8 *ssid_ie;
|
||||
|
||||
res = scan_res->res[j];
|
||||
if (os_memcmp(nei->bssid, res->bssid, ETH_ALEN) != 0 ||
|
||||
res->age > WNM_SCAN_RESULT_AGE * 1000)
|
||||
continue;
|
||||
bss = wpa_s->current_bss;
|
||||
ssid_ie = wpa_scan_get_ie(res, WLAN_EID_SSID);
|
||||
if (bss && ssid_ie &&
|
||||
(bss->ssid_len != ssid_ie[1] ||
|
||||
os_memcmp(bss->ssid, ssid_ie + 2,
|
||||
bss->ssid_len) != 0))
|
||||
continue;
|
||||
|
||||
/* Potential candidate found */
|
||||
found = 1;
|
||||
scan_snr(res);
|
||||
scan_est_throughput(wpa_s, res);
|
||||
wpa_bss_update_scan_res(wpa_s, res,
|
||||
&scan_res->fetch_time);
|
||||
}
|
||||
}
|
||||
|
||||
wpa_scan_results_free(scan_res);
|
||||
if (!found) {
|
||||
wpa_dbg(wpa_s, MSG_DEBUG,
|
||||
"WNM: No transition candidate matches existing scan results");
|
||||
return 0;
|
||||
}
|
||||
|
||||
bss = compare_scan_neighbor_results(wpa_s, WNM_SCAN_RESULT_AGE);
|
||||
if (!bss) {
|
||||
wpa_dbg(wpa_s, MSG_DEBUG,
|
||||
"WNM: Comparison of scan results against transition candidates did not find matches");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Associate to the network */
|
||||
wnm_bss_tm_connect(wpa_s, bss, ssid, 0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s,
|
||||
const u8 *pos, const u8 *end,
|
||||
int reply)
|
||||
|
@ -1155,6 +1261,20 @@ static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s,
|
|||
wpa_s->wnm_cand_valid_until.usec %= 1000000;
|
||||
os_memcpy(wpa_s->wnm_cand_from_bss, wpa_s->bssid, ETH_ALEN);
|
||||
|
||||
/*
|
||||
* Fetch the latest scan results from the kernel and check for
|
||||
* candidates based on those results first. This can help in
|
||||
* finding more up-to-date information should the driver has
|
||||
* done some internal scanning operations after the last scan
|
||||
* result update in wpa_supplicant.
|
||||
*/
|
||||
if (wnm_fetch_scan_results(wpa_s) > 0)
|
||||
return;
|
||||
|
||||
/*
|
||||
* Try to use previously received scan results, if they are
|
||||
* recent enough to use for a connection.
|
||||
*/
|
||||
if (wpa_s->last_scan_res_used > 0) {
|
||||
struct os_reltime now;
|
||||
|
||||
|
|
Loading…
Reference in a new issue