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:
Kanchanapally, Vidyullatha 2016-04-21 16:59:56 +05:30 committed by Jouni Malinen
parent 49ae6ddbca
commit 2f195639ec
3 changed files with 151 additions and 26 deletions

View file

@ -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 */

View file

@ -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 */

View file

@ -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;