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,
|
void filter_scan_res(struct wpa_supplicant *wpa_s,
|
||||||
struct wpa_scan_results *res)
|
struct wpa_scan_results *res)
|
||||||
{
|
{
|
||||||
size_t i, j;
|
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_2GHZ (-89)
|
||||||
#define DEFAULT_NOISE_FLOOR_5GHZ (-92)
|
#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) {
|
if (res->flags & WPA_SCAN_NOISE_INVALID) {
|
||||||
res->noise = IS_5GHZ(res->freq) ?
|
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,
|
void scan_est_throughput(struct wpa_supplicant *wpa_s,
|
||||||
struct wpa_scan_res *res)
|
struct wpa_scan_res *res)
|
||||||
{
|
{
|
||||||
enum local_hw_capab capab = wpa_s->hw_capab;
|
enum local_hw_capab capab = wpa_s->hw_capab;
|
||||||
int rate; /* max legacy rate in 500 kb/s units */
|
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,
|
unsigned int type, const u8 *addr,
|
||||||
const u8 *mask);
|
const u8 *mask);
|
||||||
int wpas_abort_ongoing_scan(struct wpa_supplicant *wpa_s);
|
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 */
|
#endif /* SCAN_H */
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
#define MAX_TFS_IE_LEN 1024
|
#define MAX_TFS_IE_LEN 1024
|
||||||
#define WNM_MAX_NEIGHBOR_REPORT 10
|
#define WNM_MAX_NEIGHBOR_REPORT 10
|
||||||
|
|
||||||
|
#define WNM_SCAN_RESULT_AGE 2 /* 2 seconds */
|
||||||
|
|
||||||
/* get the TFS IE from driver */
|
/* get the TFS IE from driver */
|
||||||
static int ieee80211_11_get_tfs_ie(struct wpa_supplicant *wpa_s, u8 *buf,
|
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 *
|
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;
|
u8 i;
|
||||||
|
@ -532,6 +533,19 @@ compare_scan_neighbor_results(struct wpa_supplicant *wpa_s)
|
||||||
continue;
|
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 ||
|
if (bss->ssid_len != target->ssid_len ||
|
||||||
os_memcmp(bss->ssid, target->ssid, bss->ssid_len) != 0) {
|
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)
|
int wnm_scan_process(struct wpa_supplicant *wpa_s, int reply_on_fail)
|
||||||
{
|
{
|
||||||
struct wpa_bss *bss;
|
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)
|
if (!wpa_s->wnm_neighbor_report_elements)
|
||||||
return 0;
|
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,
|
if (os_reltime_before(&wpa_s->wnm_cand_valid_until,
|
||||||
&wpa_s->scan_trigger_time)) {
|
&wpa_s->scan_trigger_time)) {
|
||||||
wpa_printf(MSG_DEBUG, "WNM: Previously stored BSS transition candidate list is not valid anymore - drop it");
|
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 */
|
/* Compare the Neighbor Report and scan results */
|
||||||
bss = compare_scan_neighbor_results(wpa_s);
|
bss = compare_scan_neighbor_results(wpa_s, 0);
|
||||||
if (!bss) {
|
if (!bss) {
|
||||||
wpa_printf(MSG_DEBUG, "WNM: No BSS transition candidate match found");
|
wpa_printf(MSG_DEBUG, "WNM: No BSS transition candidate match found");
|
||||||
status = WNM_BSS_TM_REJECT_NO_SUITABLE_CANDIDATES;
|
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 */
|
/* Associate to the network */
|
||||||
/* Send the BSS Management Response - Accept */
|
wnm_bss_tm_connect(wpa_s, bss, ssid, 1);
|
||||||
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);
|
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
send_bss_resp_fail:
|
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,
|
static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s,
|
||||||
const u8 *pos, const u8 *end,
|
const u8 *pos, const u8 *end,
|
||||||
int reply)
|
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;
|
wpa_s->wnm_cand_valid_until.usec %= 1000000;
|
||||||
os_memcpy(wpa_s->wnm_cand_from_bss, wpa_s->bssid, ETH_ALEN);
|
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) {
|
if (wpa_s->last_scan_res_used > 0) {
|
||||||
struct os_reltime now;
|
struct os_reltime now;
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue