]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
AP MLD: Process TX status for Link Reconfiguration Response frame
authorPooventhiran G <quic_pooventh@quicinc.com>
Mon, 16 Jun 2025 11:18:59 +0000 (16:48 +0530)
committerJouni Malinen <j@w1.fi>
Wed, 18 Jun 2025 09:14:47 +0000 (12:14 +0300)
When a Link Reconfiguration Response frame is ACK-ed, the AP MLD can
complete reconfiguration operations. Process the accepted del-link
requests, and if the link that is removed is the association link,
assign a new (soft) association link to maintain ML setup and APIs.

Reviewed-by: Rohan Dutta <quic_drohan@quicinc.com>
Signed-off-by: Pooventhiran G <quic_pooventh@quicinc.com>
src/ap/drv_callbacks.c
src/ap/hostapd.h
src/ap/ieee802_11.c
src/ap/ieee802_11.h
src/ap/ieee802_11_eht.c
src/ap/wpa_auth.c
src/ap/wpa_auth.h

index b54bb4f9e3c256ab9471a0acf0005d02455117d5..aebd52c0c7456789b501a8806284c47eca15ddd6 100644 (file)
@@ -1023,9 +1023,8 @@ static void hostapd_remove_sta(struct hostapd_data *hapd, struct sta_info *sta)
 
 
 #ifdef CONFIG_IEEE80211BE
-static void hostapd_notif_disassoc_mld(struct hostapd_data *assoc_hapd,
-                                      struct sta_info *sta,
-                                      const u8 *addr)
+void hostapd_notif_disassoc_mld(struct hostapd_data *assoc_hapd,
+                               struct sta_info *sta, const u8 *addr)
 {
        unsigned int link_id, i;
        struct hostapd_data *tmp_hapd;
index c96db5065862dc3876dc0110ed7912c84944c520..f263a6cb7ff267655901896f64c22654651357d0 100644 (file)
@@ -818,6 +818,8 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
                        const u8 *req_ie, size_t req_ielen, const u8 *resp_ie,
                        size_t resp_ielen, const u8 *link_addr, int reassoc);
 void hostapd_notif_disassoc(struct hostapd_data *hapd, const u8 *addr);
+void hostapd_notif_disassoc_mld(struct hostapd_data *assoc_hapd,
+                               struct sta_info *sta, const u8 *addr);
 void hostapd_event_sta_low_ack(struct hostapd_data *hapd, const u8 *addr);
 void hostapd_event_connect_failed_reason(struct hostapd_data *hapd,
                                         const u8 *addr, int reason_code);
index 4df866d30d9c6d48a6cc51baa993689be9f4bfd4..744c0b131bfdca057831b7601611cef3e98745ab 100644 (file)
@@ -7175,6 +7175,19 @@ static void handle_action_cb(struct hostapd_data *hapd,
        }
 #endif /* CONFIG_HS20 */
 
+#ifdef CONFIG_IEEE80211BE
+       /* Frame header (24B) + Category (1B) + Action code (1B) +
+        * Dialog token (1B) + Count (1B) + Status list (count * 3B)
+        */
+       if (len >= IEEE80211_HDRLEN + 3 + 1 + 3 &&
+           mgmt->u.action.category == WLAN_ACTION_PROTECTED_EHT &&
+           mgmt->u.action.u.link_reconf_resp.action ==
+           WLAN_PROT_EHT_LINK_RECONFIG_RESPONSE) {
+               hostapd_link_reconf_resp_tx_status(hapd, sta, mgmt, len, ok);
+               return;
+       }
+#endif /* CONFIG_IEEE80211BE */
+
 #ifndef CONFIG_NO_RRM
        if (len < 24 + 5 + sizeof(*report))
                return;
index 509416e448c72addf0b9bb294fc1bb0e560780e3..c3c92b7a1988c1564f0a8b402b8b1f3a23c1bf76 100644 (file)
@@ -315,5 +315,9 @@ void ieee80211_ml_build_assoc_resp(struct hostapd_data *hapd,
 void ieee802_11_rx_protected_eht_action(struct hostapd_data *hapd,
                                        const struct ieee80211_mgmt *mgmt,
                                        size_t len);
+void hostapd_link_reconf_resp_tx_status(struct hostapd_data *hapd,
+                                       struct sta_info *sta,
+                                       const struct ieee80211_mgmt *mgmt,
+                                       size_t len, int ok);
 
 #endif /* IEEE802_11_H */
index d85478c5888bf055b4773ee9e900cd04dad6a862..e7f0b4c034bec8471a6156aea482f7c64a66de2d 100644 (file)
@@ -1532,6 +1532,182 @@ void ml_deinit_link_reconf_req(struct link_reconf_req_list **req_list_ptr)
 }
 
 
+void hostapd_link_reconf_resp_tx_status(struct hostapd_data *hapd,
+                                       struct sta_info *sta,
+                                       const struct ieee80211_mgmt *mgmt,
+                                       size_t len, int ok)
+{
+       u8 dialog_token = mgmt->u.action.u.link_reconf_resp.dialog_token;
+       struct hostapd_data *assoc_hapd, *lhapd, *other_hapd;
+       struct sta_info *assoc_sta, *lsta, *other_sta;
+       struct link_reconf_req_list *req_list;
+       struct link_reconf_req_info *info;
+       uint8_t link_id;
+
+       wpa_printf(MSG_DEBUG,
+                  "MLD: Link Reconf Response TX status - dialog token=%u ok=%d",
+                  dialog_token, ok);
+
+       assoc_sta = hostapd_ml_get_assoc_sta(hapd, sta, &assoc_hapd);
+       if (!assoc_sta) {
+               wpa_printf(MSG_INFO, "MLD: Assoc STA not found for " MACSTR,
+                          MAC2STR(mgmt->da));
+               return;
+       }
+
+       if (!assoc_sta->reconf_req) {
+               wpa_printf(MSG_DEBUG,
+                          "MLD: Unexpected Link Reconf Request TX status");
+               return;
+       }
+
+       req_list = assoc_sta->reconf_req;
+
+       if (!ether_addr_equal(mgmt->da, req_list->sta_mld_addr)) {
+               wpa_printf(MSG_DEBUG,
+                          "MLD: Link Reconfiguration Response TX status from wrong STA");
+               return;
+       }
+
+       if (dialog_token != req_list->dialog_token) {
+               wpa_printf(MSG_DEBUG,
+                          "MLD: Link Reconfiguration session expired for %u",
+                          dialog_token);
+               return;
+       }
+
+       if (!ok) {
+               wpa_printf(MSG_INFO,
+                          "MLD: Link Reconf Response ack failed for " MACSTR
+                          "; revert link additions",
+                          MAC2STR(mgmt->da));
+
+               dl_list_for_each(info, &req_list->del_req,
+                                struct link_reconf_req_info, list) {
+                       if (info->status != WLAN_STATUS_SUCCESS)
+                               continue;
+
+                       lhapd = NULL;
+                       lsta = NULL;
+                       lhapd = hostapd_mld_get_link_bss(hapd, info->link_id);
+                       if (lhapd)
+                               lsta = ap_get_sta(lhapd,
+                                                 req_list->sta_mld_addr);
+
+                       if (lsta)
+                               ap_free_sta(lhapd, lsta);
+               }
+               goto exit;
+       }
+
+       if (dl_list_empty(&req_list->del_req))
+               goto exit;
+
+       dl_list_for_each(info, &req_list->del_req, struct link_reconf_req_info,
+                        list) {
+               if (info->status != WLAN_STATUS_SUCCESS)
+                       continue;
+
+               link_id = info->link_id;
+               lhapd = hostapd_mld_get_link_bss(hapd, link_id);
+               if (!lhapd) {
+                       wpa_printf(MSG_INFO,
+                                  "MLD: Link (%u) hapd cannot be NULL",
+                                  link_id);
+                       continue;
+               }
+
+               lsta = ap_get_sta(lhapd, mgmt->da);
+               if (!lsta) {
+                       wpa_printf(MSG_INFO,
+                                  "MLD: Link (%u) STA cannot be NULL",
+                                  link_id);
+                       continue;
+               }
+
+               /* Reassign assoc_sta to the link with lowest link ID */
+               if (!hostapd_sta_is_link_sta(lhapd, lsta) &&
+                   lsta == assoc_sta) {
+                       struct mld_info *mld_info = &assoc_sta->mld_info;
+                       int i;
+
+                       for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
+                               if (i == assoc_sta->mld_assoc_link_id ||
+                                   !mld_info->links[i].valid ||
+                                   req_list->links_del_ok & BIT(i)) {
+                                       continue;
+                               }
+                               break;
+                       }
+
+                       if (i == MAX_NUM_MLD_LINKS) {
+                               wpa_printf(MSG_INFO,
+                                          "MLD: No new assoc STA could be found; disconnect STA");
+                               hostapd_notif_disassoc_mld(assoc_hapd, sta,
+                                                          sta->addr);
+                               goto exit;
+                       }
+                       wpa_printf(MSG_DEBUG, "MLD: New assoc link=%d", i);
+
+                       /* Reset wpa_auth and assoc link ID */
+                       for_each_mld_link(other_hapd, lhapd) {
+                               other_sta = ap_get_sta(other_hapd, mgmt->da);
+                               if (other_sta)
+                                       other_sta->mld_assoc_link_id = i;
+                       }
+
+                       /* Reset reconfig request queue which will be freed
+                        * at the end */
+                       assoc_sta->reconf_req = NULL;
+
+                       /* assoc_sta switched */
+                       assoc_sta = hostapd_ml_get_assoc_sta(lhapd, lsta,
+                                                            &assoc_hapd);
+
+                       /* assoc_sta cannot be NULL since both AP and STA are
+                        * MLD and new valid assoc_sta is already found */
+                       if (!assoc_sta)
+                               goto exit;
+
+                       if (assoc_hapd == lhapd) {
+                               wpa_printf(MSG_ERROR,
+                                          "MLD: assoc_hapd is not updated; please check");
+                               goto exit;
+                       }
+
+                       assoc_sta->reconf_req = req_list;
+                       wpa_reset_assoc_sm_info(assoc_sta->wpa_sm,
+                                               assoc_hapd->wpa_auth, i);
+               }
+
+               /* Free as a link STA */
+               ap_free_sta(lhapd, lsta);
+
+               for_each_mld_link(other_hapd, lhapd) {
+                       struct mld_link_info *link;
+
+                       other_sta = ap_get_sta(other_hapd, mgmt->da);
+                       if (!other_sta)
+                               continue;
+
+                       link = &other_sta->mld_info.links[link_id];
+                       os_free(link->resp_sta_profile);
+                       link->resp_sta_profile = NULL;
+                       link->resp_sta_profile_len = 0;
+                       link->valid = false;
+               }
+               wpa_auth_set_ml_info(assoc_sta->wpa_sm,
+                                    assoc_sta->mld_assoc_link_id,
+                                    &assoc_sta->mld_info);
+       }
+
+exit:
+       ml_deinit_link_reconf_req(&req_list);
+       if (assoc_sta && assoc_sta->reconf_req)
+               assoc_sta->reconf_req = NULL;
+}
+
+
 static bool recover_from_zero_links(u16 *links_del_ok, u8 *recovery_link)
 {
        u8 pos = 0;
index 62c8c3c53d14f9e149d7672c8e2aaf4bb1586132..333467ce8f323ab038a88429cb9512005730e576 100644 (file)
@@ -7681,3 +7681,14 @@ bool wpa_auth_sm_known_sta_identification(struct wpa_state_machine *sm,
 
        return true;
 }
+
+
+void wpa_reset_assoc_sm_info(struct wpa_state_machine *assoc_sm,
+                            struct wpa_authenticator *wpa_auth,
+                            u8 mld_assoc_link_id)
+{
+#ifdef CONFIG_IEEE80211BE
+       assoc_sm->wpa_auth = wpa_auth;
+       assoc_sm->mld_assoc_link_id = mld_assoc_link_id;
+#endif /* CONFIG_IEEE80211BE */
+}
index 84c83c3f15fa1ef6cb00b907bcd4b3ebfac7e4b7..176ed2c44ca65fd241ccaf5ddbed8ea6aadd115f 100644 (file)
@@ -690,6 +690,9 @@ void wpa_release_link_auth_ref(struct wpa_state_machine *sm, u8 link_id,
 size_t wpa_auth_ml_group_kdes_len(struct wpa_state_machine *sm, u16 req_links);
 u8 * wpa_auth_ml_group_kdes(struct wpa_state_machine *sm, u8 *pos,
                            u16 req_links);
+void wpa_reset_assoc_sm_info(struct wpa_state_machine *assoc_sm,
+                            struct wpa_authenticator *wpa_auth,
+                            u8 mld_assoc_link_id);
 
 #define for_each_sm_auth(sm, link_id) \
        for (link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++)       \