From patchwork Wed Sep 18 12:39:10 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Benjamin Berg X-Patchwork-Id: 1986793 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=pass (2048-bit key; secure) header.d=lists.infradead.org header.i=@lists.infradead.org header.a=rsa-sha256 header.s=bombadil.20210309 header.b=R8j91icS; dkim=fail reason="signature verification failed" (2048-bit key; secure) header.d=sipsolutions.net header.i=@sipsolutions.net header.a=rsa-sha256 header.s=mail header.b=deNDwvw7; dkim-atps=neutral Authentication-Results: legolas.ozlabs.org; spf=none (no SPF record) smtp.mailfrom=lists.infradead.org (client-ip=2607:7c80:54:3::133; helo=bombadil.infradead.org; envelope-from=hostap-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org; receiver=patchwork.ozlabs.org) Received: from bombadil.infradead.org (bombadil.infradead.org [IPv6:2607:7c80:54:3::133]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (secp384r1) server-digest SHA384) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4X7yvH2KWZz1y2j for ; Wed, 18 Sep 2024 22:41:29 +1000 (AEST) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20210309; h=Sender: Content-Transfer-Encoding:Content-Type:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:MIME-Version:Message-ID:Date:Subject:Cc :To:From:Reply-To:Content-ID:Content-Description:Resent-Date:Resent-From: Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:In-Reply-To:References: List-Owner; bh=s+cVh8LgS151iuUg3bv+VRtgAQozasQx7uUC3vzT23g=; b=R8j91icSChVhoO 8HxVVDzVucUVrioTYfLucnUwa8+XqwVRty7NZbUcxsdhhi5hY2hnxUPUCeTV/+CyWU51Hx+jO28/6 3Ge476mcwFUyt+xW0fK+w+kMg0kzNLx7Cg01+1Od7QvLpteyYPfo4AH3AyCZTGujSGhPxbhgWu94f F/hBWrDgttMFck8xXuqy1pzqZXMRcJ/O+KT01nKT1cv2TQTylTngbqwc11pVkcIK8TEwZAjMemYHO M6k3fMwWKzk47KGsR3PX05tDojgcDJd4uX8ODlwu1oCpztifPU5zyx7hDHv/Iudbpjn/rpxDvB/Ah rbl5oa47CnB2ImmkTgkQ==; Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.98 #2 (Red Hat Linux)) id 1sqtzR-00000008Fei-3Uyz; Wed, 18 Sep 2024 12:41:01 +0000 Received: from s3.sipsolutions.net ([2a01:4f8:242:246e::2] helo=sipsolutions.net) by bombadil.infradead.org with esmtps (Exim 4.98 #2 (Red Hat Linux)) id 1sqtzN-00000008Fdj-2y72 for hostap@lists.infradead.org; Wed, 18 Sep 2024 12:40:59 +0000 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=sipsolutions.net; s=mail; h=Content-Transfer-Encoding:MIME-Version: Message-ID:Date:Subject:Cc:To:From:Content-Type:Sender:Reply-To:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-To:Resent-Cc: Resent-Message-ID:In-Reply-To:References; bh=vxJhVD0qiybaMUiS4sxzp09Abk6SkqH1Kf52jzcp3o8=; t=1726663255; x=1727872855; b=deNDwvw7tj0H823d66nxkvELDkhUa2HXU7E8NVV/GAfS3W0B7HkH/M1hSaXmBXgb55XQHWRY4fR /65eBC8zF9/R0k3VyZ/BHgKGqKDlP2w8oK4+aGvNtFsnIc4547sViLIKjQORFyqt1L3llmKuRgkYj cyVpCnznlwKlmpNJCdyEsBgcnQidcnC26h7C3VF9TNM4dxtYgm7Zu45S97JsM3uuVQkY5i6bGaR6e G8J8EwJs4TsYbO8ZMVXWehh1CXSsdgKZ4PSdx10rl9eHcu2ZmyP/JPAhB2eVQuxfjWql2F2XRyoLV l2cQFrK7bwViq1Lm8e4FEFEza88luDiud6gw==; Received: by sipsolutions.net with esmtpsa (TLS1.3:ECDHE_X25519__RSA_PSS_RSAE_SHA256__AES_256_GCM:256) (Exim 4.97) (envelope-from ) id 1sqtzG-00000000jC2-3CAh; Wed, 18 Sep 2024 14:40:51 +0200 From: Benjamin Berg To: hostap@lists.infradead.org Cc: Benjamin Berg Subject: [PATCH v2 1/2] WNM: Move driver MBO transition rejection into wnm_is_bss_excluded Date: Wed, 18 Sep 2024 14:39:10 +0200 Message-ID: <20240918123911.120326-1-benjamin@sipsolutions.net> X-Mailer: git-send-email 2.46.0 MIME-Version: 1.0 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20240918_054058_092812_31286485 X-CRM114-Status: GOOD ( 33.27 ) X-Spam-Score: -2.1 (--) X-Spam-Report: Spam detection software, running on the system "bombadil.infradead.org", has NOT identified this incoming email as spam. The original message has been attached to this so you can view it or label similar future email. If you have any questions, see the administrator of that system for details. Content preview: From: Benjamin Berg Change the logic a bit to not directly use the result of the wpa_drv_get_bss_trans_status call and instead use the same selection logic as usual but taking into account the driver rejections. Content analysis details: (-2.1 points, 5.0 required) pts rule name description ---- ---------------------- -------------------------------------------------- -0.0 SPF_PASS SPF: sender matches SPF record -0.0 SPF_HELO_PASS SPF: HELO matches SPF record 0.1 DKIM_SIGNED Message has a DKIM or DK signature, not necessarily valid -0.1 DKIM_VALID Message has at least one valid DKIM or DK signature -0.1 DKIM_VALID_EF Message has a valid DKIM or DK signature from envelope-from domain -0.1 DKIM_VALID_AU Message has a valid DKIM or DK signature from author's domain -1.9 BAYES_00 BODY: Bayes spam probability is 0 to 1% [score: 0.0000] X-BeenThere: hostap@lists.infradead.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: "Hostap" Errors-To: hostap-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org From: Benjamin Berg Change the logic a bit to not directly use the result of the wpa_drv_get_bss_trans_status call and instead use the same selection logic as usual but taking into account the driver rejections. This changes the logic in minor ways. The main change is that this aligns the ordering of BSSs to be identical in all cases. More precisely, we'll select the best BSS as found by find_better_target. Beyond that, it also means that in the case of a non-abridged BTM request we'll also consider candidates that were found through the scan and not in the neighbor report. In this case, the driver will not have a chance to reject them. Signed-off-by: Benjamin Berg --- v2: - Fix code when CONFIG_MBO is disabled - Add tests to verify the behaviour --- src/drivers/driver_nl80211.c | 49 +++++++++++++ src/utils/os_unix.c | 2 +- tests/hwsim/test_wnm.py | 55 ++++++++++++++ wpa_supplicant/wnm_sta.c | 137 +++++++++++++++++------------------ wpa_supplicant/wnm_sta.h | 2 +- 5 files changed, 172 insertions(+), 73 deletions(-) diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c index 1356d325d..e4465c6a4 100644 --- a/src/drivers/driver_nl80211.c +++ b/src/drivers/driver_nl80211.c @@ -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, diff --git a/src/utils/os_unix.c b/src/utils/os_unix.c index 679f3a0dc..cabfd87c0 100644 --- a/src/utils/os_unix.c +++ b/src/utils/os_unix.c @@ -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) { diff --git a/tests/hwsim/test_wnm.py b/tests/hwsim/test_wnm.py index bf4243a46..cdb04f324 100644 --- a/tests/hwsim/test_wnm.py +++ b/tests/hwsim/test_wnm.py @@ -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) diff --git a/wpa_supplicant/wnm_sta.c b/wpa_supplicant/wnm_sta.c index 662f6089e..3638cd543 100644 --- a/wpa_supplicant/wnm_sta.c +++ b/wpa_supplicant/wnm_sta.c @@ -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; } diff --git a/wpa_supplicant/wnm_sta.h b/wpa_supplicant/wnm_sta.h index 235a838fa..5994027a4 100644 --- a/wpa_supplicant/wnm_sta.h +++ b/wpa_supplicant/wnm_sta.h @@ -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;