@@ -270,6 +270,12 @@ extern "C" {
/* BSS Transition Management Response frame received */
#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 */
#define WPA_BSS_MASK_ALL 0xFFFDFFFF
@@ -1076,6 +1076,12 @@ static struct wpa_ssid * wpa_scan_res_match(struct wpa_supplicant *wpa_s,
assoc_disallow[2]);
continue;
}
+
+ if (wpa_is_bss_tmp_disallowed(wpa_s, bss->bssid)) {
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ " skip - MBO Retry delay didn't pass yet");
+ continue;
+ }
#endif /* CONFIG_MBO */
/* 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_ssid(wpa_s, fast_reconnect->ssid,
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
wpa_dbg(wpa_s, MSG_DEBUG, "Try to reconnect to the same BSS");
if (wpa_supplicant_connect(wpa_s, fast_reconnect,
@@ -618,3 +618,99 @@ int wpas_mbo_supp_op_class_ie(struct wpa_supplicant *wpa_s, int freq, u8 *pos,
wpabuf_free(buf);
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 assoc 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: Assoc retry delay attribute not in disassoc imminent mode");
+ }
+
+ break;
+ case MBO_ATTR_ID_CAPA_IND:
+ case MBO_ATTR_ID_NP_CHAN_REPORT:
+ case MBO_ATTR_ID_CELL_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_bss_tmp_disallow(wpa_s, wpa_s->current_bss->bssid,
+ disallowed_sec);
+
+ return;
+fail:
+ wpa_printf(MSG_DEBUG, "MBO IE parse failed (id=%u len=%u left=%zu)", id,
+ elen, len);
+}
@@ -546,6 +546,13 @@ compare_scan_neighbor_results(struct wpa_supplicant *wpa_s)
continue;
}
+ if (wpa_is_bss_tmp_disallowed(wpa_s, target->bssid)) {
+ wpa_printf(MSG_DEBUG,
+ "MBO: Candidate BSS " MACSTR " retry delay isn't over yet",
+ MAC2STR(nei->bssid));
+ continue;
+ }
+
if (target->level < bss->level && target->level < -80) {
wpa_printf(MSG_DEBUG, "Candidate BSS " MACSTR
" (pref %d) does not have sufficient signal level (%d)",
@@ -821,6 +828,9 @@ static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s,
{
unsigned int beacon_int;
u8 valid_int;
+#ifdef CONFIG_MBO
+ const u8 *vendor;
+#endif
if (end - pos < 5)
return;
@@ -881,6 +891,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) {
unsigned int valid_ms;
@@ -945,7 +961,8 @@ static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s,
wpa_supplicant_req_scan(wpa_s, 0, 0);
} else if (reply) {
enum bss_trans_mgmt_status_code status;
- if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT)
+ if ((wpa_s->wnm_mode & WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT) ||
+ (wpa_s->wnm_mode & WNM_BSS_TM_REQ_DISASSOC_IMMINENT))
status = WNM_BSS_TM_ACCEPT;
else {
wpa_msg(wpa_s, MSG_INFO, "WNM: BSS Transition Management Request did not include candidates");
@@ -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)
{
int i;
@@ -554,6 +566,8 @@ static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s)
wpa_s->np_chan_num = 0;
os_free(wpa_s->np_chan);
#endif /* CONFIG_MBO */
+
+ free_bss_tmp_disallowed(wpa_s);
}
@@ -3490,6 +3504,8 @@ wpa_supplicant_alloc(struct wpa_supplicant *parent)
wpa_s->parent = parent ? parent : wpa_s;
wpa_s->sched_scanning = 0;
+ dl_list_init(&wpa_s->bss_tmp_disallowed);
+
return wpa_s;
}
@@ -6302,3 +6318,72 @@ struct hostapd_hw_modes *get_mode(struct hostapd_hw_modes *modes,
return NULL;
}
+
+
+static struct
+wpa_bss_tmp_disallowed *wpas_get_disallowed_bss(struct wpa_supplicant *wpa_s,
+ 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))
+ return bss;
+ }
+
+ return NULL;
+}
+
+
+void wpa_bss_tmp_disallow(struct wpa_supplicant *wpa_s, u8 *bssid, u16 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, u8 *bssid)
+{
+ struct wpa_bss_tmp_disallowed *bss;
+ struct os_reltime now;
+
+ os_get_reltime(&now);
+
+ bss = wpas_get_disallowed_bss(wpa_s, bssid);
+ if (!bss)
+ return 0;
+
+ if (os_reltime_before(&now, &bss->disallowed_until)) {
+ wpa_printf(MSG_DEBUG,
+ "BSS " MACSTR " disabled until %ld.%ld, now=%ld.%ld",
+ MAC2STR(bss->bssid), bss->disallowed_until.sec,
+ bss->disallowed_until.usec, now.sec, now.usec);
+ return 1;
+ }
+
+ /* This BSS is not disallowed anymore */
+ dl_list_del(&bss->list);
+ os_free(bss);
+ return 0;
+}
@@ -434,6 +434,12 @@ struct icon_entry {
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
*
@@ -1029,6 +1035,12 @@ struct wpa_supplicant {
size_t np_chan_num;
u8 mbo_wnm_token;
#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_np_chan(struct wpa_supplicant *wpa_s, const char *np_chan);
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,
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
@@ -1232,4 +1246,7 @@ 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,
u16 num_modes,
enum hostapd_hw_mode mode);
+
+void wpa_bss_tmp_disallow(struct wpa_supplicant *wpa_s, u8 *bssid, u16 sec);
+int wpa_is_bss_tmp_disallowed(struct wpa_supplicant *wpa_s, u8 *bssid);
#endif /* WPA_SUPPLICANT_I_H */