MBO: Parse MBO IE in BSS Transition Management Request frames

Add parsing of MBO IE in BSS Transition Management Request frames. If
the MBO IE includes the association retry delay attribute, do not try to
reconnect to the current BSS until the delay time is over.

If the MBO IE includes the cellular data connection preference attribute
or the transition rejection reason attribute, send a message to upper
layers with the data.

Signed-off-by: David Spinadel <david.spinadel@intel.com>
Signed-off-by: Avraham Stern <avraham.stern@intel.com>
This commit is contained in:
Avraham Stern 2016-02-15 16:53:36 +02:00 committed by Jouni Malinen
parent 5e57ba2505
commit dd5999084e
6 changed files with 231 additions and 1 deletions

View file

@ -270,6 +270,12 @@ extern "C" {
/* BSS Transition Management Response frame received */ /* BSS Transition Management Response frame received */
#define BSS_TM_RESP "BSS-TM-RESP " #define BSS_TM_RESP "BSS-TM-RESP "
/* MBO IE with cellular data connection preference received */
#define MBO_CELL_PREFERENCE "MBO-CELL-PREFERENCE "
/* BSS Transition Management Request received with MBO transition reason */
#define MBO_TRANSITION_REASON "MBO-TRANSITION-REASON "
/* BSS command information masks */ /* BSS command information masks */
#define WPA_BSS_MASK_ALL 0xFFFDFFFF #define WPA_BSS_MASK_ALL 0xFFFDFFFF

View file

@ -1076,6 +1076,12 @@ static struct wpa_ssid * wpa_scan_res_match(struct wpa_supplicant *wpa_s,
assoc_disallow[2]); assoc_disallow[2]);
continue; continue;
} }
if (wpa_is_bss_tmp_disallowed(wpa_s, bss->bssid)) {
wpa_dbg(wpa_s, MSG_DEBUG,
" skip - MBO retry delay has not passed yet");
continue;
}
#endif /* CONFIG_MBO */ #endif /* CONFIG_MBO */
/* Matching configuration found */ /* Matching configuration found */
@ -2541,7 +2547,8 @@ static void wpa_supplicant_event_disassoc_finish(struct wpa_supplicant *wpa_s,
!disallowed_bssid(wpa_s, fast_reconnect->bssid) && !disallowed_bssid(wpa_s, fast_reconnect->bssid) &&
!disallowed_ssid(wpa_s, fast_reconnect->ssid, !disallowed_ssid(wpa_s, fast_reconnect->ssid,
fast_reconnect->ssid_len) && fast_reconnect->ssid_len) &&
!wpas_temp_disabled(wpa_s, fast_reconnect_ssid)) { !wpas_temp_disabled(wpa_s, fast_reconnect_ssid) &&
!wpa_is_bss_tmp_disallowed(wpa_s, fast_reconnect->bssid)) {
#ifndef CONFIG_NO_SCAN_PROCESSING #ifndef CONFIG_NO_SCAN_PROCESSING
wpa_dbg(wpa_s, MSG_DEBUG, "Try to reconnect to the same BSS"); wpa_dbg(wpa_s, MSG_DEBUG, "Try to reconnect to the same BSS");
if (wpa_supplicant_connect(wpa_s, fast_reconnect, if (wpa_supplicant_connect(wpa_s, fast_reconnect,

View file

@ -620,3 +620,99 @@ int wpas_mbo_supp_op_class_ie(struct wpa_supplicant *wpa_s, int freq, u8 *pos,
wpabuf_free(buf); wpabuf_free(buf);
return res; return res;
} }
void wpas_mbo_ie_trans_req(struct wpa_supplicant *wpa_s, const u8 *mbo_ie,
size_t len)
{
const u8 *pos, *cell_pref = NULL, *reason = NULL;
u8 id, elen;
u16 disallowed_sec = 0;
if (len <= 4 || WPA_GET_BE24(mbo_ie) != OUI_WFA ||
mbo_ie[3] != MBO_OUI_TYPE)
return;
pos = mbo_ie + 4;
len -= 4;
while (len >= 2) {
id = *pos++;
elen = *pos++;
len -= 2;
if (elen > len)
goto fail;
switch (id) {
case MBO_ATTR_ID_CELL_DATA_PREF:
if (elen != 1)
goto fail;
if (wpa_s->conf->mbo_cell_capa ==
MBO_CELL_CAPA_AVAILABLE)
cell_pref = pos;
else
wpa_printf(MSG_DEBUG,
"MBO: Station does not support Cellular data connection");
break;
case MBO_ATTR_ID_TRANSITION_REASON:
if (elen != 1)
goto fail;
reason = pos;
break;
case MBO_ATTR_ID_ASSOC_RETRY_DELAY:
if (elen != 2)
goto fail;
if (wpa_s->wnm_mode &
WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED) {
wpa_printf(MSG_DEBUG,
"MBO: Unexpected association retry delay, BSS is terminating");
goto fail;
} else if (wpa_s->wnm_mode &
WNM_BSS_TM_REQ_DISASSOC_IMMINENT) {
disallowed_sec = WPA_GET_LE16(pos);
} else {
wpa_printf(MSG_DEBUG,
"MBO: Association retry delay attribute not in disassoc imminent mode");
}
break;
case MBO_ATTR_ID_AP_CAPA_IND:
case MBO_ATTR_ID_NON_PREF_CHAN_REPORT:
case MBO_ATTR_ID_CELL_DATA_CAPA:
case MBO_ATTR_ID_ASSOC_DISALLOW:
case MBO_ATTR_ID_TRANSITION_REJECT_REASON:
wpa_printf(MSG_DEBUG,
"MBO: Attribute %d should not be included in BTM Request frame",
id);
break;
default:
wpa_printf(MSG_DEBUG, "MBO: Unknown attribute id %u",
id);
return;
}
pos += elen;
len -= elen;
}
if (cell_pref)
wpa_msg(wpa_s, MSG_INFO, MBO_CELL_PREFERENCE "preference=%u",
*cell_pref);
if (reason)
wpa_msg(wpa_s, MSG_INFO, MBO_TRANSITION_REASON "reason=%u",
*reason);
if (disallowed_sec && wpa_s->current_bss)
wpa_bss_tmp_disallow(wpa_s, wpa_s->current_bss->bssid,
disallowed_sec);
return;
fail:
wpa_printf(MSG_DEBUG, "MBO IE parsing failed (id=%u len=%u left=%zu)",
id, elen, len);
}

View file

@ -546,6 +546,14 @@ compare_scan_neighbor_results(struct wpa_supplicant *wpa_s)
continue; continue;
} }
if (wpa_is_bss_tmp_disallowed(wpa_s, target->bssid)) {
wpa_printf(MSG_DEBUG,
"MBO: Candidate BSS " MACSTR
" retry delay is not over yet",
MAC2STR(nei->bssid));
continue;
}
if (target->level < bss->level && target->level < -80) { if (target->level < bss->level && target->level < -80) {
wpa_printf(MSG_DEBUG, "Candidate BSS " MACSTR wpa_printf(MSG_DEBUG, "Candidate BSS " MACSTR
" (pref %d) does not have sufficient signal level (%d)", " (pref %d) does not have sufficient signal level (%d)",
@ -821,6 +829,9 @@ static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s,
{ {
unsigned int beacon_int; unsigned int beacon_int;
u8 valid_int; u8 valid_int;
#ifdef CONFIG_MBO
const u8 *vendor;
#endif /* CONFIG_MBO */
if (end - pos < 5) if (end - pos < 5)
return; return;
@ -881,6 +892,12 @@ static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s,
} }
} }
#ifdef CONFIG_MBO
vendor = get_ie(pos, end - pos, WLAN_EID_VENDOR_SPECIFIC);
if (vendor)
wpas_mbo_ie_trans_req(wpa_s, vendor + 2, vendor[1]);
#endif /* CONFIG_MBO */
if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_PREF_CAND_LIST_INCLUDED) { if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_PREF_CAND_LIST_INCLUDED) {
unsigned int valid_ms; unsigned int valid_ms;

View file

@ -397,6 +397,18 @@ void free_hw_features(struct wpa_supplicant *wpa_s)
} }
static void free_bss_tmp_disallowed(struct wpa_supplicant *wpa_s)
{
struct wpa_bss_tmp_disallowed *bss, *prev;
dl_list_for_each_safe(bss, prev, &wpa_s->bss_tmp_disallowed,
struct wpa_bss_tmp_disallowed, list) {
dl_list_del(&bss->list);
os_free(bss);
}
}
static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s) static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s)
{ {
int i; int i;
@ -555,6 +567,8 @@ static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s)
os_free(wpa_s->non_pref_chan); os_free(wpa_s->non_pref_chan);
wpa_s->non_pref_chan = NULL; wpa_s->non_pref_chan = NULL;
#endif /* CONFIG_MBO */ #endif /* CONFIG_MBO */
free_bss_tmp_disallowed(wpa_s);
} }
@ -3497,6 +3511,8 @@ wpa_supplicant_alloc(struct wpa_supplicant *parent)
wpa_s->parent = parent ? parent : wpa_s; wpa_s->parent = parent ? parent : wpa_s;
wpa_s->sched_scanning = 0; wpa_s->sched_scanning = 0;
dl_list_init(&wpa_s->bss_tmp_disallowed);
return wpa_s; return wpa_s;
} }
@ -6308,3 +6324,73 @@ struct hostapd_hw_modes * get_mode(struct hostapd_hw_modes *modes,
return NULL; return NULL;
} }
static struct
wpa_bss_tmp_disallowed * wpas_get_disallowed_bss(struct wpa_supplicant *wpa_s,
const u8 *bssid)
{
struct wpa_bss_tmp_disallowed *bss;
dl_list_for_each(bss, &wpa_s->bss_tmp_disallowed,
struct wpa_bss_tmp_disallowed, list) {
if (os_memcmp(bssid, bss->bssid, ETH_ALEN) == 0)
return bss;
}
return NULL;
}
void wpa_bss_tmp_disallow(struct wpa_supplicant *wpa_s, const u8 *bssid,
unsigned int sec)
{
struct wpa_bss_tmp_disallowed *bss;
struct os_reltime until;
os_get_reltime(&until);
until.sec += sec;
bss = wpas_get_disallowed_bss(wpa_s, bssid);
if (bss) {
bss->disallowed_until = until;
return;
}
bss = os_malloc(sizeof(*bss));
if (!bss) {
wpa_printf(MSG_DEBUG,
"Failed to allocate memory for temp disallow BSS");
return;
}
bss->disallowed_until = until;
os_memcpy(bss->bssid, bssid, ETH_ALEN);
dl_list_add(&wpa_s->bss_tmp_disallowed, &bss->list);
}
int wpa_is_bss_tmp_disallowed(struct wpa_supplicant *wpa_s, const u8 *bssid)
{
struct wpa_bss_tmp_disallowed *bss;
struct os_reltime now, age;
os_get_reltime(&now);
bss = wpas_get_disallowed_bss(wpa_s, bssid);
if (!bss)
return 0;
if (os_reltime_before(&now, &bss->disallowed_until)) {
os_reltime_sub(&bss->disallowed_until, &now, &age);
wpa_printf(MSG_DEBUG,
"BSS " MACSTR " disabled for %ld.%0ld seconds",
MAC2STR(bss->bssid), age.sec, age.usec);
return 1;
}
/* This BSS is not disallowed anymore */
dl_list_del(&bss->list);
os_free(bss);
return 0;
}

View file

@ -434,6 +434,12 @@ struct icon_entry {
size_t image_len; size_t image_len;
}; };
struct wpa_bss_tmp_disallowed {
struct dl_list list;
u8 bssid[ETH_ALEN];
struct os_reltime disallowed_until;
};
/** /**
* struct wpa_supplicant - Internal data for wpa_supplicant interface * struct wpa_supplicant - Internal data for wpa_supplicant interface
* *
@ -1029,6 +1035,12 @@ struct wpa_supplicant {
size_t non_pref_chan_num; size_t non_pref_chan_num;
u8 mbo_wnm_token; u8 mbo_wnm_token;
#endif /* CONFIG_MBO */ #endif /* CONFIG_MBO */
/*
* This should be under CONFIG_MBO, but it is left out to allow using
* the bss_temp_disallowed list for other purposes as well.
*/
struct dl_list bss_tmp_disallowed;
}; };
@ -1150,6 +1162,8 @@ int wpas_mbo_update_non_pref_chan(struct wpa_supplicant *wpa_s,
void wpas_mbo_scan_ie(struct wpa_supplicant *wpa_s, struct wpabuf *ie); void wpas_mbo_scan_ie(struct wpa_supplicant *wpa_s, struct wpabuf *ie);
int wpas_mbo_supp_op_class_ie(struct wpa_supplicant *wpa_s, int freq, u8 *pos, int wpas_mbo_supp_op_class_ie(struct wpa_supplicant *wpa_s, int freq, u8 *pos,
size_t len); size_t len);
void wpas_mbo_ie_trans_req(struct wpa_supplicant *wpa_s, const u8 *ie,
size_t len);
/** /**
* wpa_supplicant_ctrl_iface_ctrl_rsp_handle - Handle a control response * wpa_supplicant_ctrl_iface_ctrl_rsp_handle - Handle a control response
@ -1232,4 +1246,8 @@ int wpas_sched_scan_plans_set(struct wpa_supplicant *wpa_s, const char *cmd);
struct hostapd_hw_modes * get_mode(struct hostapd_hw_modes *modes, struct hostapd_hw_modes * get_mode(struct hostapd_hw_modes *modes,
u16 num_modes, enum hostapd_hw_mode mode); u16 num_modes, enum hostapd_hw_mode mode);
void wpa_bss_tmp_disallow(struct wpa_supplicant *wpa_s, const u8 *bssid,
unsigned int sec);
int wpa_is_bss_tmp_disallowed(struct wpa_supplicant *wpa_s, const u8 *bssid);
#endif /* WPA_SUPPLICANT_I_H */ #endif /* WPA_SUPPLICANT_I_H */