MBO: Add support for transition reject reason code

Add support for rejecting a BSS transition request using MBO reject
reason codes. A candidate is selected or rejected based on whether it is
found acceptable by both wpa_supplicant and the driver. Also accept any
candidate meeting a certain threshold if disassoc imminent is set in BTM
Request frame.

Signed-off-by: Jouni Malinen <jouni@qca.qualcomm.com>
This commit is contained in:
Kanchanapally, Vidyullatha 2017-03-06 17:16:00 +05:30 committed by Jouni Malinen
parent 3ab484928a
commit af8bc24da3
7 changed files with 204 additions and 32 deletions

View file

@ -3742,6 +3742,8 @@ struct wpa_config * wpa_config_alloc_empty(const char *ctrl_interface,
#ifdef CONFIG_MBO #ifdef CONFIG_MBO
config->mbo_cell_capa = DEFAULT_MBO_CELL_CAPA; config->mbo_cell_capa = DEFAULT_MBO_CELL_CAPA;
config->disassoc_imminent_rssi_threshold =
DEFAULT_DISASSOC_IMMINENT_RSSI_THRESHOLD;
#endif /* CONFIG_MBO */ #endif /* CONFIG_MBO */
if (ctrl_interface) if (ctrl_interface)
@ -4453,6 +4455,7 @@ static const struct global_parse_data global_fields[] = {
{ STR(non_pref_chan), 0 }, { STR(non_pref_chan), 0 },
{ INT_RANGE(mbo_cell_capa, MBO_CELL_CAPA_AVAILABLE, { INT_RANGE(mbo_cell_capa, MBO_CELL_CAPA_AVAILABLE,
MBO_CELL_CAPA_NOT_SUPPORTED), 0 }, MBO_CELL_CAPA_NOT_SUPPORTED), 0 },
{ INT_RANGE(disassoc_imminent_rssi_threshold, -120, 0), 0 },
#endif /*CONFIG_MBO */ #endif /*CONFIG_MBO */
{ INT(gas_address3), 0 }, { INT(gas_address3), 0 },
{ INT_RANGE(ftm_responder, 0, 1), 0 }, { INT_RANGE(ftm_responder, 0, 1), 0 },

View file

@ -41,6 +41,7 @@
#define DEFAULT_P2P_GO_CTWINDOW 0 #define DEFAULT_P2P_GO_CTWINDOW 0
#define DEFAULT_WPA_RSC_RELAXATION 1 #define DEFAULT_WPA_RSC_RELAXATION 1
#define DEFAULT_MBO_CELL_CAPA MBO_CELL_CAPA_NOT_SUPPORTED #define DEFAULT_MBO_CELL_CAPA MBO_CELL_CAPA_NOT_SUPPORTED
#define DEFAULT_DISASSOC_IMMINENT_RSSI_THRESHOLD -75
#include "config_ssid.h" #include "config_ssid.h"
#include "wps/wps.h" #include "wps/wps.h"
@ -1291,6 +1292,12 @@ struct wpa_config {
* mbo_cell_capa - Cellular capabilities for MBO * mbo_cell_capa - Cellular capabilities for MBO
*/ */
enum mbo_cellular_capa mbo_cell_capa; enum mbo_cellular_capa mbo_cell_capa;
/**
* disassoc_imminent_rssi_threshold - RSSI threshold of candidate AP
* when disassociation imminent is set.
*/
int disassoc_imminent_rssi_threshold;
#endif /* CONFIG_MBO */ #endif /* CONFIG_MBO */
/** /**

View file

@ -1394,6 +1394,10 @@ static void wpa_config_write_global(FILE *f, struct wpa_config *config)
fprintf(f, "non_pref_chan=%s\n", config->non_pref_chan); fprintf(f, "non_pref_chan=%s\n", config->non_pref_chan);
if (config->mbo_cell_capa != DEFAULT_MBO_CELL_CAPA) if (config->mbo_cell_capa != DEFAULT_MBO_CELL_CAPA)
fprintf(f, "mbo_cell_capa=%u\n", config->mbo_cell_capa); fprintf(f, "mbo_cell_capa=%u\n", config->mbo_cell_capa);
if (config->disassoc_imminent_rssi_threshold !=
DEFAULT_DISASSOC_IMMINENT_RSSI_THRESHOLD)
fprintf(f, "disassoc_imminent_rssi_threshold=%d\n",
config->disassoc_imminent_rssi_threshold);
#endif /* CONFIG_MBO */ #endif /* CONFIG_MBO */
if (config->gas_address3) if (config->gas_address3)

View file

@ -382,7 +382,7 @@ void wpas_mbo_scan_ie(struct wpa_supplicant *wpa_s, struct wpabuf *ie)
void wpas_mbo_ie_trans_req(struct wpa_supplicant *wpa_s, const u8 *mbo_ie, void wpas_mbo_ie_trans_req(struct wpa_supplicant *wpa_s, const u8 *mbo_ie,
size_t len) size_t len)
{ {
const u8 *pos, *cell_pref = NULL, *reason = NULL; const u8 *pos, *cell_pref = NULL;
u8 id, elen; u8 id, elen;
u16 disallowed_sec = 0; u16 disallowed_sec = 0;
@ -417,7 +417,8 @@ void wpas_mbo_ie_trans_req(struct wpa_supplicant *wpa_s, const u8 *mbo_ie,
if (elen != 1) if (elen != 1)
goto fail; goto fail;
reason = pos; wpa_s->wnm_mbo_trans_reason_present = 1;
wpa_s->wnm_mbo_transition_reason = *pos;
break; break;
case MBO_ATTR_ID_ASSOC_RETRY_DELAY: case MBO_ATTR_ID_ASSOC_RETRY_DELAY:
if (elen != 2) if (elen != 2)
@ -460,9 +461,9 @@ void wpas_mbo_ie_trans_req(struct wpa_supplicant *wpa_s, const u8 *mbo_ie,
wpa_msg(wpa_s, MSG_INFO, MBO_CELL_PREFERENCE "preference=%u", wpa_msg(wpa_s, MSG_INFO, MBO_CELL_PREFERENCE "preference=%u",
*cell_pref); *cell_pref);
if (reason) if (wpa_s->wnm_mbo_trans_reason_present)
wpa_msg(wpa_s, MSG_INFO, MBO_TRANSITION_REASON "reason=%u", wpa_msg(wpa_s, MSG_INFO, MBO_TRANSITION_REASON "reason=%u",
*reason); wpa_s->wnm_mbo_transition_reason);
if (disallowed_sec && wpa_s->current_bss) if (disallowed_sec && wpa_s->current_bss)
wpa_bss_tmp_disallow(wpa_s, wpa_s->current_bss->bssid, wpa_bss_tmp_disallow(wpa_s, wpa_s->current_bss->bssid,

View file

@ -13,6 +13,7 @@
#include "common/ieee802_11_common.h" #include "common/ieee802_11_common.h"
#include "common/wpa_ctrl.h" #include "common/wpa_ctrl.h"
#include "rsn_supp/wpa.h" #include "rsn_supp/wpa.h"
#include "config.h"
#include "wpa_supplicant_i.h" #include "wpa_supplicant_i.h"
#include "driver_i.h" #include "driver_i.h"
#include "scan.h" #include "scan.h"
@ -499,10 +500,127 @@ static void wnm_parse_neighbor_report(struct wpa_supplicant *wpa_s,
} }
static struct wpa_bss * static void wnm_clear_acceptable(struct wpa_supplicant *wpa_s)
compare_scan_neighbor_results(struct wpa_supplicant *wpa_s, os_time_t age_secs)
{ {
unsigned int i;
for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++)
wpa_s->wnm_neighbor_report_elements[i].acceptable = 0;
}
static struct wpa_bss * get_first_acceptable(struct wpa_supplicant *wpa_s)
{
unsigned int i;
struct neighbor_report *nei;
for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) {
nei = &wpa_s->wnm_neighbor_report_elements[i];
if (nei->acceptable)
return wpa_bss_get_bssid(wpa_s, nei->bssid);
}
return NULL;
}
#ifdef CONFIG_MBO
static struct wpa_bss *
get_mbo_transition_candidate(struct wpa_supplicant *wpa_s,
enum mbo_transition_reject_reason *reason)
{
struct wpa_bss *target = NULL;
struct wpa_bss_trans_info params;
struct wpa_bss_candidate_info *info = NULL;
struct neighbor_report *nei = wpa_s->wnm_neighbor_report_elements;
u8 *first_candidate_bssid = NULL, *pos;
unsigned int i;
params.mbo_transition_reason = wpa_s->wnm_mbo_transition_reason;
params.n_candidates = 0;
params.bssid = os_calloc(wpa_s->wnm_num_neighbor_report, ETH_ALEN);
if (!params.bssid)
return NULL;
pos = params.bssid;
for (i = 0; i < wpa_s->wnm_num_neighbor_report; nei++, i++) {
if (nei->is_first)
first_candidate_bssid = nei->bssid;
if (!nei->acceptable)
continue;
os_memcpy(pos, nei->bssid, ETH_ALEN);
pos += ETH_ALEN;
params.n_candidates++;
}
if (!params.n_candidates)
goto end;
info = wpa_drv_get_bss_trans_status(wpa_s, &params);
if (!info) {
/* If failed to get candidate BSS transition status from driver,
* get the first acceptable candidate from wpa_supplicant.
*/
target = wpa_bss_get_bssid(wpa_s, params.bssid);
goto end;
}
/* Get the first acceptable candidate from driver */
for (i = 0; i < info->num; i++) {
if (info->candidates[i].is_accept) {
target = wpa_bss_get_bssid(wpa_s,
info->candidates[i].bssid);
goto end;
}
}
/* If Disassociation Imminent is set and driver rejects all the
* candidate select first acceptable candidate which has
* rssi > disassoc_imminent_rssi_threshold
*/
if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_DISASSOC_IMMINENT) {
for (i = 0; i < info->num; i++) {
target = wpa_bss_get_bssid(wpa_s,
info->candidates[i].bssid);
if (target->level <
wpa_s->conf->disassoc_imminent_rssi_threshold)
continue;
goto end;
}
}
/* While sending BTM reject use reason code of the first candidate
* received in BTM request frame
*/
if (reason) {
for (i = 0; i < info->num; i++) {
if (first_candidate_bssid &&
os_memcmp(first_candidate_bssid,
info->candidates[i].bssid, ETH_ALEN) == 0)
{
*reason = info->candidates[i].reject_reason;
break;
}
}
}
target = NULL;
end:
os_free(params.bssid);
if (info) {
os_free(info->candidates);
os_free(info);
}
return target;
}
#endif /* CONFIG_MBO */
static struct wpa_bss *
compare_scan_neighbor_results(struct wpa_supplicant *wpa_s, os_time_t age_secs,
enum mbo_transition_reject_reason *reason)
{
u8 i; u8 i;
struct wpa_bss *bss = wpa_s->current_bss; struct wpa_bss *bss = wpa_s->current_bss;
struct wpa_bss *target; struct wpa_bss *target;
@ -513,6 +631,8 @@ compare_scan_neighbor_results(struct wpa_supplicant *wpa_s, os_time_t age_secs)
wpa_printf(MSG_DEBUG, "WNM: Current BSS " MACSTR " RSSI %d", wpa_printf(MSG_DEBUG, "WNM: Current BSS " MACSTR " RSSI %d",
MAC2STR(wpa_s->bssid), bss->level); MAC2STR(wpa_s->bssid), bss->level);
wnm_clear_acceptable(wpa_s);
for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) { for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) {
struct neighbor_report *nei; struct neighbor_report *nei;
@ -589,14 +709,26 @@ compare_scan_neighbor_results(struct wpa_supplicant *wpa_s, os_time_t age_secs)
continue; continue;
} }
nei->acceptable = 1;
}
#ifdef CONFIG_MBO
if (wpa_s->wnm_mbo_trans_reason_present)
target = get_mbo_transition_candidate(wpa_s, reason);
else
target = get_first_acceptable(wpa_s);
#else /* CONFIG_MBO */
target = get_first_acceptable(wpa_s);
#endif /* CONFIG_MBO */
if (target) {
wpa_printf(MSG_DEBUG, wpa_printf(MSG_DEBUG,
"WNM: Found an acceptable preferred transition candidate BSS " "WNM: Found an acceptable preferred transition candidate BSS "
MACSTR " (RSSI %d)", MACSTR " (RSSI %d)",
MAC2STR(nei->bssid), target->level); MAC2STR(target->bssid), target->level);
return target;
} }
return NULL; return target;
} }
@ -780,17 +912,19 @@ static int wnm_add_cand_list(struct wpa_supplicant *wpa_s, u8 *buf, size_t len)
static void wnm_send_bss_transition_mgmt_resp( static void wnm_send_bss_transition_mgmt_resp(
struct wpa_supplicant *wpa_s, u8 dialog_token, struct wpa_supplicant *wpa_s, u8 dialog_token,
enum bss_trans_mgmt_status_code status, u8 delay, enum bss_trans_mgmt_status_code status,
const u8 *target_bssid) enum mbo_transition_reject_reason reason,
u8 delay, const u8 *target_bssid)
{ {
u8 buf[2000], *pos; u8 buf[2000], *pos;
struct ieee80211_mgmt *mgmt; struct ieee80211_mgmt *mgmt;
size_t len; size_t len;
int res; int res;
wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Response " wpa_printf(MSG_DEBUG,
"to " MACSTR " dialog_token=%u status=%u delay=%d", "WNM: Send BSS Transition Management Response to " MACSTR
MAC2STR(wpa_s->bssid), dialog_token, status, delay); " dialog_token=%u status=%u reason=%u delay=%d",
MAC2STR(wpa_s->bssid), dialog_token, status, reason, delay);
if (!wpa_s->current_bss) { if (!wpa_s->current_bss) {
wpa_printf(MSG_DEBUG, wpa_printf(MSG_DEBUG,
"WNM: Current BSS not known - drop response"); "WNM: Current BSS not known - drop response");
@ -827,10 +961,10 @@ static void wnm_send_bss_transition_mgmt_resp(
pos += wnm_add_cand_list(wpa_s, pos, buf + sizeof(buf) - pos); pos += wnm_add_cand_list(wpa_s, pos, buf + sizeof(buf) - pos);
#ifdef CONFIG_MBO #ifdef CONFIG_MBO
if (status != WNM_BSS_TM_ACCEPT) { if (status != WNM_BSS_TM_ACCEPT &&
wpa_bss_get_vendor_ie(wpa_s->current_bss, MBO_IE_VENDOR_TYPE)) {
pos += wpas_mbo_ie_bss_trans_reject( pos += wpas_mbo_ie_bss_trans_reject(
wpa_s, pos, buf + sizeof(buf) - pos, wpa_s, pos, buf + sizeof(buf) - pos, reason);
MBO_TRANSITION_REJECT_REASON_UNSPECIFIED);
} }
#endif /* CONFIG_MBO */ #endif /* CONFIG_MBO */
@ -861,10 +995,9 @@ static void wnm_bss_tm_connect(struct wpa_supplicant *wpa_s,
wpa_s->wnm_reply = 0; wpa_s->wnm_reply = 0;
wpa_printf(MSG_DEBUG, wpa_printf(MSG_DEBUG,
"WNM: Sending successful BSS Transition Management Response"); "WNM: Sending successful BSS Transition Management Response");
wnm_send_bss_transition_mgmt_resp(wpa_s, wnm_send_bss_transition_mgmt_resp(
wpa_s->wnm_dialog_token, wpa_s, wpa_s->wnm_dialog_token, WNM_BSS_TM_ACCEPT,
WNM_BSS_TM_ACCEPT, MBO_TRANSITION_REASON_UNSPECIFIED, 0, bss->bssid);
0, bss->bssid);
} }
if (bss == wpa_s->current_bss) { if (bss == wpa_s->current_bss) {
@ -886,6 +1019,8 @@ int wnm_scan_process(struct wpa_supplicant *wpa_s, int reply_on_fail)
struct wpa_bss *bss; struct wpa_bss *bss;
struct wpa_ssid *ssid = wpa_s->current_ssid; struct wpa_ssid *ssid = wpa_s->current_ssid;
enum bss_trans_mgmt_status_code status = WNM_BSS_TM_REJECT_UNSPECIFIED; enum bss_trans_mgmt_status_code status = WNM_BSS_TM_REJECT_UNSPECIFIED;
enum mbo_transition_reject_reason reason =
MBO_TRANSITION_REJECT_REASON_UNSPECIFIED;
if (!wpa_s->wnm_neighbor_report_elements) if (!wpa_s->wnm_neighbor_report_elements)
return 0; return 0;
@ -907,7 +1042,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, 0); bss = compare_scan_neighbor_results(wpa_s, 0, &reason);
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;
@ -928,7 +1063,7 @@ send_bss_resp_fail:
wpa_s->wnm_reply = 0; wpa_s->wnm_reply = 0;
wnm_send_bss_transition_mgmt_resp(wpa_s, wnm_send_bss_transition_mgmt_resp(wpa_s,
wpa_s->wnm_dialog_token, wpa_s->wnm_dialog_token,
status, 0, NULL); status, reason, 0, NULL);
} }
wnm_deallocate_memory(wpa_s); wnm_deallocate_memory(wpa_s);
@ -1116,7 +1251,7 @@ static int wnm_fetch_scan_results(struct wpa_supplicant *wpa_s)
return 0; return 0;
} }
bss = compare_scan_neighbor_results(wpa_s, WNM_SCAN_RESULT_AGE); bss = compare_scan_neighbor_results(wpa_s, WNM_SCAN_RESULT_AGE, NULL);
if (!bss) { if (!bss) {
wpa_dbg(wpa_s, MSG_DEBUG, wpa_dbg(wpa_s, MSG_DEBUG,
"WNM: Comparison of scan results against transition candidates did not find matches"); "WNM: Comparison of scan results against transition candidates did not find matches");
@ -1142,6 +1277,11 @@ static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s,
if (end - pos < 5) if (end - pos < 5)
return; return;
#ifdef CONFIG_MBO
wpa_s->wnm_mbo_trans_reason_present = 0;
wpa_s->wnm_mbo_transition_reason = 0;
#endif /* CONFIG_MBO */
if (wpa_s->current_bss) if (wpa_s->current_bss)
beacon_int = wpa_s->current_bss->beacon_int; beacon_int = wpa_s->current_bss->beacon_int;
else else
@ -1164,10 +1304,10 @@ static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s,
wpa_printf(MSG_INFO, wpa_printf(MSG_INFO,
"WNM: Testing - reject BSS Transition Management Request: reject_btm_req_reason=%d", "WNM: Testing - reject BSS Transition Management Request: reject_btm_req_reason=%d",
wpa_s->reject_btm_req_reason); wpa_s->reject_btm_req_reason);
wnm_send_bss_transition_mgmt_resp(wpa_s, wnm_send_bss_transition_mgmt_resp(
wpa_s->wnm_dialog_token, wpa_s, wpa_s->wnm_dialog_token,
wpa_s->reject_btm_req_reason, wpa_s->reject_btm_req_reason,
0, NULL); MBO_TRANSITION_REASON_UNSPECIFIED, 0, NULL);
return; return;
} }
#endif /* CONFIG_MBO && CONFIG_TESTING_OPTIONS */ #endif /* CONFIG_MBO && CONFIG_TESTING_OPTIONS */
@ -1246,6 +1386,15 @@ static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s,
wpa_s->wnm_num_neighbor_report]; wpa_s->wnm_num_neighbor_report];
wnm_parse_neighbor_report(wpa_s, pos, len, rep); wnm_parse_neighbor_report(wpa_s, pos, len, rep);
wpa_s->wnm_num_neighbor_report++; wpa_s->wnm_num_neighbor_report++;
#ifdef CONFIG_MBO
if (wpa_s->wnm_mbo_trans_reason_present &&
wpa_s->wnm_num_neighbor_report == 1) {
rep->is_first = 1;
wpa_printf(MSG_DEBUG,
"WNM: First transition candidate is "
MACSTR, MAC2STR(rep->bssid));
}
#endif /* CONFIG_MBO */
} }
pos += len; pos += len;
@ -1257,7 +1406,7 @@ static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s,
wnm_send_bss_transition_mgmt_resp( wnm_send_bss_transition_mgmt_resp(
wpa_s, wpa_s->wnm_dialog_token, wpa_s, wpa_s->wnm_dialog_token,
WNM_BSS_TM_REJECT_NO_SUITABLE_CANDIDATES, WNM_BSS_TM_REJECT_NO_SUITABLE_CANDIDATES,
0, NULL); MBO_TRANSITION_REASON_UNSPECIFIED, 0, NULL);
return; return;
} }
@ -1320,9 +1469,9 @@ static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s,
wpa_msg(wpa_s, MSG_INFO, "WNM: BSS Transition Management Request did not include candidates"); wpa_msg(wpa_s, MSG_INFO, "WNM: BSS Transition Management Request did not include candidates");
status = WNM_BSS_TM_REJECT_UNSPECIFIED; status = WNM_BSS_TM_REJECT_UNSPECIFIED;
} }
wnm_send_bss_transition_mgmt_resp(wpa_s, wnm_send_bss_transition_mgmt_resp(
wpa_s->wnm_dialog_token, wpa_s, wpa_s->wnm_dialog_token, status,
status, 0, NULL); MBO_TRANSITION_REASON_UNSPECIFIED, 0, NULL);
} }
} }

View file

@ -43,6 +43,10 @@ struct neighbor_report {
unsigned int rm_capab_present:1; unsigned int rm_capab_present:1;
unsigned int bearing_present:1; unsigned int bearing_present:1;
unsigned int bss_term_present:1; unsigned int bss_term_present:1;
unsigned int acceptable:1;
#ifdef CONFIG_MBO
unsigned int is_first:1;
#endif /* CONFIG_MBO */
struct measurement_pilot *meas_pilot; struct measurement_pilot *meas_pilot;
struct multiple_bssid *mul_bssid; struct multiple_bssid *mul_bssid;
int freq; int freq;

View file

@ -1044,6 +1044,10 @@ struct wpa_supplicant {
struct neighbor_report *wnm_neighbor_report_elements; struct neighbor_report *wnm_neighbor_report_elements;
struct os_reltime wnm_cand_valid_until; struct os_reltime wnm_cand_valid_until;
u8 wnm_cand_from_bss[ETH_ALEN]; u8 wnm_cand_from_bss[ETH_ALEN];
#ifdef CONFIG_MBO
unsigned int wnm_mbo_trans_reason_present:1;
u8 wnm_mbo_transition_reason;
#endif /* CONFIG_MBO */
#endif /* CONFIG_WNM */ #endif /* CONFIG_WNM */
#ifdef CONFIG_TESTING_GET_GTK #ifdef CONFIG_TESTING_GET_GTK