]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
WNM: Wait for BTM response TX status before roaming
authorAvraham Stern <avraham.stern@intel.com>
Tue, 20 Feb 2024 13:18:18 +0000 (14:18 +0100)
committerJouni Malinen <j@w1.fi>
Sat, 2 Mar 2024 18:52:17 +0000 (20:52 +0200)
When accepting a BSS transition request there is a race between
sending the response and roaming to the target AP. As a result,
the response may not be sent because the station deauthenticated
from the AP before the response was actually sent.

To make sure the BSS transition response is sent, start roaming only
after the TX status is received for the BSS transition response.

Signed-off-by: Avraham Stern <avraham.stern@intel.com>
Signed-off-by: Benjamin Berg <benjamin.berg@intel.com>
wpa_supplicant/bss.c
wpa_supplicant/events.c
wpa_supplicant/wnm_sta.c
wpa_supplicant/wnm_sta.h
wpa_supplicant/wpa_supplicant.c
wpa_supplicant/wpa_supplicant_i.h

index 9e34135e126a7d1e196ba020f8806741844fb30a..289035310231bc8b3024abf606bb2f2df67862ed 100644 (file)
@@ -400,6 +400,11 @@ static int wpa_bss_in_use(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
        if (bss == wpa_s->ml_connect_probe_bss)
                return 1;
 
+#ifdef CONFIG_WNM
+       if (bss == wpa_s->wnm_target_bss)
+               return 1;
+#endif /* CONFIG_WNM */
+
        if (wpa_s->current_bss &&
            (bss->ssid_len != wpa_s->current_bss->ssid_len ||
             os_memcmp(bss->ssid, wpa_s->current_bss->ssid,
index d61512124c3b4e8c640f7adea6712680be034771..d18f4362867dc55dcc377022f03d5dc980f376d9 100644 (file)
@@ -6140,6 +6140,13 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
                        " type=%d stype=%d",
                        MAC2STR(data->tx_status.dst),
                        data->tx_status.type, data->tx_status.stype);
+#ifdef CONFIG_WNM
+               if (data->tx_status.type == WLAN_FC_TYPE_MGMT &&
+                   data->tx_status.stype == WLAN_FC_STYPE_ACTION &&
+                   wnm_btm_resp_tx_status(wpa_s, data->tx_status.data,
+                                          data->tx_status.data_len) == 0)
+                       break;
+#endif /* CONFIG_WNM */
 #ifdef CONFIG_PASN
                if (data->tx_status.type == WLAN_FC_TYPE_MGMT &&
                    data->tx_status.stype == WLAN_FC_STYPE_AUTH &&
index 652c6d0e7f5b5cffb6c881f1076e174e88b2f96d..a883feabb5f8695cf9062a1b08edb691307e6dc9 100644 (file)
@@ -1045,7 +1045,7 @@ static void wnm_add_cand_list(struct wpa_supplicant *wpa_s, struct wpabuf **buf)
 
 #define BTM_RESP_MIN_SIZE      5 + ETH_ALEN
 
-static void wnm_send_bss_transition_mgmt_resp(
+static int wnm_send_bss_transition_mgmt_resp(
        struct wpa_supplicant *wpa_s, u8 dialog_token,
        enum bss_trans_mgmt_status_code status,
        enum mbo_transition_reject_reason reason,
@@ -1061,14 +1061,14 @@ static void wnm_send_bss_transition_mgmt_resp(
        if (!wpa_s->current_bss) {
                wpa_printf(MSG_DEBUG,
                           "WNM: Current BSS not known - drop response");
-               return;
+               return -1;
        }
 
        buf = wpabuf_alloc(BTM_RESP_MIN_SIZE);
        if (!buf) {
                wpa_printf(MSG_DEBUG,
                           "WNM: Failed to allocate memory for BTM response");
-               return;
+               return -1;
        }
 
        wpa_s->bss_tm_status = status;
@@ -1106,7 +1106,7 @@ static void wnm_send_bss_transition_mgmt_resp(
                                wpabuf_free(buf);
                                wpa_printf(MSG_DEBUG,
                                           "WNM: Failed to allocate memory for MBO IE");
-                               return;
+                               return -1;
                        }
 
                        wpabuf_put_data(buf, mbo, ret);
@@ -1123,6 +1123,8 @@ static void wnm_send_bss_transition_mgmt_resp(
        }
 
        wpabuf_free(buf);
+
+       return res;
 }
 
 
@@ -1141,12 +1143,19 @@ static void wnm_bss_tm_connect(struct wpa_supplicant *wpa_s,
        /* Send the BSS Management Response - Accept */
        if (wpa_s->wnm_reply) {
                wpa_s->wnm_reply = 0;
+               wpa_s->wnm_target_bss = bss;
                wpa_printf(MSG_DEBUG,
                           "WNM: Sending successful BSS Transition Management Response");
-               wnm_send_bss_transition_mgmt_resp(
-                       wpa_s, wpa_s->wnm_dialog_token, WNM_BSS_TM_ACCEPT,
-                       MBO_TRANSITION_REJECT_REASON_UNSPECIFIED, 0,
-                       bss->bssid);
+
+               /* This function will be called again from the TX handler to
+                * start the actual reassociation after this response has been
+                * delivered to the current AP. */
+               if (wnm_send_bss_transition_mgmt_resp(
+                           wpa_s, wpa_s->wnm_dialog_token,
+                           WNM_BSS_TM_ACCEPT,
+                           MBO_TRANSITION_REJECT_REASON_UNSPECIFIED, 0,
+                           bss->bssid) >= 0)
+                       return;
        }
 
        if (bss == wpa_s->current_bss) {
@@ -1634,6 +1643,39 @@ static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s,
 }
 
 
+int wnm_btm_resp_tx_status(struct wpa_supplicant *wpa_s, const u8 *data,
+                          size_t data_len)
+{
+       const struct ieee80211_mgmt *frame =
+               (const struct ieee80211_mgmt *) data;
+
+       if (data_len <
+           IEEE80211_HDRLEN + sizeof(frame->u.action.u.bss_tm_resp) ||
+           frame->u.action.category != WLAN_ACTION_WNM ||
+           frame->u.action.u.bss_tm_resp.action != WNM_BSS_TRANS_MGMT_RESP ||
+           frame->u.action.u.bss_tm_resp.status_code != WNM_BSS_TM_ACCEPT)
+               return -1;
+
+       /*
+        * If disassoc imminent bit was set in the request, the response may
+        * indicate accept even if no candidate was found, so bail out here.
+        */
+       if (!wpa_s->wnm_target_bss) {
+               wpa_printf(MSG_DEBUG, "WNM: Target BSS is not set");
+               return 0;
+       }
+
+       if (!wpa_s->current_ssid)
+               return 0;
+
+       wnm_bss_tm_connect(wpa_s, wpa_s->wnm_target_bss, wpa_s->current_ssid,
+                          0);
+
+       wpa_s->wnm_target_bss = NULL;
+       return 0;
+}
+
+
 #define BTM_QUERY_MIN_SIZE     4
 
 int wnm_send_bss_transition_mgmt_query(struct wpa_supplicant *wpa_s,
index 2eaa2964fafafb8fd667ebeb8a69f8c255d1fc49..08662f4336fb62ebf920bc83374765e9d8bc5b32 100644 (file)
@@ -72,6 +72,8 @@ void wnm_set_coloc_intf_elems(struct wpa_supplicant *wpa_s,
                              struct wpabuf *elems);
 bool wnm_is_bss_excluded(struct wpa_supplicant *wpa_s, struct wpa_bss *bss);
 
+int wnm_btm_resp_tx_status(struct wpa_supplicant *wpa_s, const u8 *data,
+                          size_t data_len);
 
 #ifdef CONFIG_WNM
 
index c19ab810a72739233e5f48ccc1a8efadb8e525d7..679ff2012564ba4c268a85f77c291780b71d934a 100644 (file)
@@ -2503,6 +2503,7 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s,
 #endif /* CONFIG_NO_WMM_AC */
 #ifdef CONFIG_WNM
        wpa_s->wnm_mode = 0;
+       wpa_s->wnm_target_bss = NULL;
 #endif /* CONFIG_WNM */
        wpa_s->reassoc_same_bss = 0;
        wpa_s->reassoc_same_ess = 0;
index 7a35df950f6c500eb3eafd510b78ea61997a3bd8..513fcaadcdee5f5bac2565e75973742588d7b546 100644 (file)
@@ -1310,6 +1310,7 @@ struct wpa_supplicant {
        struct neighbor_report *wnm_neighbor_report_elements;
        struct os_reltime wnm_cand_valid_until;
        u8 wnm_cand_from_bss[ETH_ALEN];
+       struct wpa_bss *wnm_target_bss;
        enum bss_trans_mgmt_status_code bss_tm_status;
        bool bss_trans_mgmt_in_progress;
        struct wpabuf *coloc_intf_elems;