@@ -13965,6 +13965,52 @@ static int nl80211_do_acs(void *priv, struct drv_acs_params *params)
}
+#if defined(CONFIG_MBO) && defined(WPA_TRACE_BFD) && defined(CONFIG_TESTING_OPTIONS)
+static struct wpa_bss_candidate_info *
+nl80211_get_bss_transition_status(void *priv, struct wpa_bss_trans_info *params)
+{
+ struct wpa_bss_candidate_info *info;
+ int i;
+
+ /* This only exists for testing purposes, disable unless requested */
+ if (!TEST_FAIL_TAG("simulate"))
+ return NULL;
+
+ info = os_zalloc(sizeof(*info));
+ if (!info)
+ return NULL;
+
+ info->candidates = os_calloc(params->n_candidates,
+ sizeof(*info->candidates));
+ if (!info->candidates) {
+ os_free(info);
+ return NULL;
+ }
+
+ info->num = params->n_candidates;
+ for (i = 0; i < params->n_candidates; i++) {
+ char bssid_str[ETH_ALEN * 3];
+ os_memcpy(info->candidates[i].bssid,
+ ¶ms->bssid[i * ETH_ALEN], ETH_ALEN);
+
+ os_snprintf(bssid_str, sizeof(bssid_str), MACSTR,
+ MAC2STR(info->candidates[i].bssid));
+
+ if (TEST_FAIL_TAG(bssid_str)) {
+ info->candidates[i].is_accept = 0;
+ info->candidates[i].reject_reason =
+ MBO_TRANSITION_REJECT_REASON_FRAME_LOSS;
+ } else {
+ info->candidates[i].is_accept = 1;
+ info->candidates[i].reject_reason = 0;
+ }
+ }
+
+ return info;
+}
+#endif /* defined(CONFIG_MBO) && defined(WPA_TRACE_BFD) && defined(CONFIG_TESTING_OPTIONS) */
+
+
static int nl80211_write_to_file(const char *name, unsigned int val)
{
int fd, len;
@@ -14612,6 +14658,9 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
.nan_cancel_subscribe = nl80211_nan_cancel_subscribe,
#endif /* CONFIG_NAN_USD */
#endif /* CONFIG_DRIVER_NL80211_QCA */
+#if defined(CONFIG_MBO) && defined(WPA_TRACE_BFD) && defined(CONFIG_TESTING_OPTIONS)
+ .get_bss_transition_status = nl80211_get_bss_transition_status,
+#endif /* defined(CONFIG_MBO) && defined(WPA_TRACE_BFD) && defined(CONFIG_TESTING_OPTIONS) */
.do_acs = nl80211_do_acs,
.configure_data_frame_filters = nl80211_configure_data_frame_filters,
.get_ext_capab = nl80211_get_ext_capab,
@@ -543,7 +543,7 @@ void * os_memdup(const void *src, size_t len)
struct wpa_trace_test_fail {
unsigned int fail_after;
char pattern[256];
-} wpa_trace_test_fail[5][2];
+} wpa_trace_test_fail[5][4];
int testing_test_fail(const char *tag, bool is_alloc)
{
@@ -1024,6 +1024,61 @@ def test_wnm_bss_tm(dev, apdev):
if ev is not None:
raise Exception("Unexpected scan started")
+ # The following two tests excercise the MBO target querying to the driver
+ dev[0].flush_scan_cache()
+ logger.info("BTM request with candidate list and all are valid, roams because MBO is enabled and driver rejects current")
+ with fail_test(dev[0], 1, "simulate;nl80211_get_bss_transition_status",
+ 1, apdev[0]['bssid'] + ";nl80211_get_bss_transition_status",
+ # Second time post-scan
+ 1, "simulate;nl80211_get_bss_transition_status",
+ 1, apdev[0]['bssid'] + ";nl80211_get_bss_transition_status"):
+ if "OK" not in hapd.request("BSS_TM_REQ " + addr + " pref=1 abridged=1 mbo=3:0:1 valid_int=255 neighbor=" + apdev[0]['bssid'] + ",0x0000,81,1,7,0301ff neighbor=" + apdev[1]['bssid'] + ",0x0000,115,36,7,0301ff"):
+ raise Exception("BSS_TM_REQ command failed")
+ ev = hapd.wait_event(['BSS-TM-RESP'], timeout=10)
+ if ev is None:
+ raise Exception("No BSS Transition Management Response")
+ if "status_code=0" not in ev:
+ raise Exception("BSS transition request was not accepted: " + ev)
+ if "target_bssid=" + apdev[1]['bssid'] not in ev:
+ raise Exception("Unexpected target BSS: " + ev)
+ # This scans only one frequency
+ scan_ev = dev[0].wait_event(["CTRL-EVENT-SCAN-STARTED"], timeout=0)
+ if scan_ev is None:
+ raise Exception("Expected scan started")
+ dev[0].wait_connected(timeout=15, error="No reassociation seen")
+ if apdev[1]['bssid'] not in ev:
+ raise Exception("Unexpected reassociation target: " + ev)
+ ev = dev[0].wait_event(["CTRL-EVENT-SCAN-STARTED"], timeout=0.1)
+ if ev is not None:
+ raise Exception("Unexpected scan started")
+
+ dev[0].flush_scan_cache()
+ logger.info("BTM request with candidate list forcing other AP throug disassoc imminent, driver does MBO reject, but still roams")
+ with fail_test(dev[0], 1, "simulate;nl80211_get_bss_transition_status",
+ 1, apdev[0]['bssid'] + ";nl80211_get_bss_transition_status",
+ # And a second time post-scan
+ 1, "simulate;nl80211_get_bss_transition_status",
+ 1, apdev[0]['bssid'] + ";nl80211_get_bss_transition_status"):
+ if "OK" not in hapd2.request("BSS_TM_REQ " + addr + " disassoc_imminent=1 pref=1 abridged=1 mbo=3:5:1 valid_int=255 neighbor=" + apdev[0]['bssid'] + ",0x0000,81,1,7,0301ff"):
+ raise Exception("BSS_TM_REQ command failed")
+ ev = hapd2.wait_event(['BSS-TM-RESP'], timeout=10)
+ if ev is None:
+ raise Exception("No BSS Transition Management Response")
+ if "status_code=0" not in ev:
+ raise Exception("BSS transition request was not accepted: " + ev)
+ if "target_bssid=" + apdev[0]['bssid'] not in ev:
+ raise Exception("Unexpected target BSS: " + ev)
+ # This scans only one frequency
+ scan_ev = dev[0].wait_event(["CTRL-EVENT-SCAN-STARTED"], timeout=0)
+ if scan_ev is None:
+ raise Exception("Expected scan started")
+ dev[0].wait_connected(timeout=15, error="No reassociation seen")
+ if apdev[0]['bssid'] not in ev:
+ raise Exception("Unexpected reassociation target: " + ev)
+ ev = dev[0].wait_event(["CTRL-EVENT-SCAN-STARTED"], timeout=0.1)
+ if ev is not None:
+ raise Exception("Unexpected scan started")
+
finally:
clear_regdom_state(dev, hapd, hapd2)
@@ -614,38 +614,37 @@ static void wnm_parse_neighbor_report(struct wpa_supplicant *wpa_s,
}
-static void wnm_clear_acceptable(struct wpa_supplicant *wpa_s)
-{
- unsigned int i;
-
- for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++)
- wpa_s->wnm_neighbor_report_elements[i].acceptable = 0;
-}
-
-#ifdef CONFIG_MBO
-static struct wpa_bss *
-get_mbo_transition_candidate(struct wpa_supplicant *wpa_s,
+static void
+fetch_drv_mbo_candidate_info(struct wpa_supplicant *wpa_s,
enum mbo_transition_reject_reason *reason)
{
- struct wpa_bss *target = NULL;
+#ifdef CONFIG_MBO
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;
+ struct neighbor_report *nei;
+ u8 *pos;
unsigned int i;
+ if (!wpa_s->wnm_mbo_trans_reason_present)
+ return;
+
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;
+ return;
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)
+ for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) {
+ nei = &wpa_s->wnm_neighbor_report_elements[i];
+
+ nei->drv_mbo_reject = 0;
+
+ if (nei->preference_present && nei->preference == 0)
continue;
+
+ /* Should we query BSSIDs that we reject for other reasons? */
+
os_memcpy(pos, nei->bssid, ETH_ALEN);
pos += ETH_ALEN;
params.n_candidates++;
@@ -655,64 +654,37 @@ get_mbo_transition_candidate(struct wpa_supplicant *wpa_s,
goto end;
info = wpa_drv_get_bss_trans_status(wpa_s, ¶ms);
- 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);
+ if (!info)
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;
- }
- }
+ int j;
- /* 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 &&
- (target->level <
- wpa_s->conf->disassoc_imminent_rssi_threshold))
+ for (j = 0; j < wpa_s->wnm_num_neighbor_report; nei++, j++) {
+ nei = &wpa_s->wnm_neighbor_report_elements[j];
+
+ if (!ether_addr_equal(info->candidates[i].bssid,
+ nei->bssid))
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 &&
- ether_addr_equal(first_candidate_bssid,
- info->candidates[i].bssid)) {
+ nei->drv_mbo_reject = !info->candidates[i].is_accept;
+
+ /* Use the reject reason from "first" candidate */
+ if (nei->is_first && nei->drv_mbo_reject)
*reason = info->candidates[i].reject_reason;
- break;
- }
+
+ 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 * find_better_target(struct wpa_bss *a,
@@ -754,8 +726,6 @@ compare_scan_neighbor_results(struct wpa_supplicant *wpa_s,
wpa_printf(MSG_DEBUG, "WNM: Current BSS " MACSTR " RSSI %d",
MAC2STR(wpa_s->bssid), bss->level);
- wnm_clear_acceptable(wpa_s);
-
for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) {
struct neighbor_report *nei;
@@ -795,21 +765,12 @@ compare_scan_neighbor_results(struct wpa_supplicant *wpa_s,
continue;
}
- nei->acceptable = 1;
-
best_target = find_better_target(target, best_target);
if (target == bss)
bss_in_list = bss;
}
-#ifdef CONFIG_MBO
- if (wpa_s->wnm_mbo_trans_reason_present)
- target = get_mbo_transition_candidate(wpa_s, reason);
- else
- target = best_target;
-#else /* CONFIG_MBO */
target = best_target;
-#endif /* CONFIG_MBO */
if (!target)
return NULL;
@@ -1178,8 +1139,39 @@ int wnm_scan_process(struct wpa_supplicant *wpa_s, bool pre_scan_check)
wpa_s->wnm_transition_scan = false;
+ /* Fetch MBO transition candidate rejection information from driver */
+ fetch_drv_mbo_candidate_info(wpa_s, &reason);
+
/* Compare the Neighbor Report and scan results */
bss = compare_scan_neighbor_results(wpa_s, &reason);
+#ifdef CONFIG_MBO
+ if (!bss && wpa_s->wnm_mbo_trans_reason_present &&
+ wpa_s->wnm_mode & WNM_BSS_TM_REQ_DISASSOC_IMMINENT) {
+ int i;
+
+ /*
+ * We didn't find any candidate, the driver had a say about
+ * which targets to reject and disassociation is immiment.
+ *
+ * We should still try to roam, so retry after ignoring the
+ * driver reject for any BSS that has an RSSI better than
+ * disassoc_imminent_rssi_threshold.
+ */
+ for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) {
+ struct neighbor_report *nei;
+
+ nei = &wpa_s->wnm_neighbor_report_elements[i];
+
+ bss = wpa_bss_get_bssid(wpa_s, nei->bssid);
+
+ if (bss->level >
+ wpa_s->conf->disassoc_imminent_rssi_threshold)
+ nei->drv_mbo_reject = 0;
+ }
+
+ bss = compare_scan_neighbor_results(wpa_s, &reason);
+ }
+#endif /* CONFIG_MBO */
/*
* If this is a pre-scan check, returning 0 will trigger a scan and
@@ -2118,6 +2110,9 @@ bool wnm_is_bss_excluded(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
if (nei->preference_present && nei->preference == 0)
return true;
+ if (nei->drv_mbo_reject)
+ return true;
+
break;
}
@@ -44,9 +44,9 @@ struct neighbor_report {
unsigned int rm_capab_present:1;
unsigned int bearing_present:1;
unsigned int bss_term_present:1;
- unsigned int acceptable:1;
#ifdef CONFIG_MBO
unsigned int is_first:1;
+ unsigned int drv_mbo_reject:1;
#endif /* CONFIG_MBO */
struct measurement_pilot *meas_pilot;
struct multiple_bssid *mul_bssid;