]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
AP: MLO: Process Multi-Link element from (Re)Association Request frame
authorAndrei Otcheretianski <andrei.otcheretianski@intel.com>
Mon, 22 May 2023 19:33:45 +0000 (22:33 +0300)
committerJouni Malinen <j@w1.fi>
Thu, 8 Jun 2023 17:31:20 +0000 (20:31 +0300)
Implement processing of the Multi-Link element in the (Re)Association
Request frame, including processing of the Per-STA Profile subelement.

After handling the basic parsing of the element and extracting the
information about the requested links, handle the link specific
processing for each link:

- Find the interface with the corresponding link ID.
- Process the station profile in the interface.
- Prepare the Per-STA Profile subelement to be included in the
  Multi-Link element in the (Re)Association Response frame.

Signed-off-by: Ilan Peer <ilan.peer@intel.com>
Signed-off-by: Andrei Otcheretianski <andrei.otcheretianski@intel.com>
src/ap/ieee802_11.c
src/ap/ieee802_11.h
src/ap/ieee802_11_eht.c
src/ap/sta_info.c

index 175f3ed027c24ce7451a05dfacff3b90a5be801a..335fa471d4465c4882a83a7e37d043d8403835ce 100644 (file)
@@ -83,6 +83,8 @@ static void pasn_fils_auth_resp(struct hostapd_data *hapd,
 static void handle_auth(struct hostapd_data *hapd,
                        const struct ieee80211_mgmt *mgmt, size_t len,
                        int rssi, int from_queue);
+static int add_associated_sta(struct hostapd_data *hapd,
+                             struct sta_info *sta, int reassoc);
 
 
 u8 * hostapd_eid_multi_ap(struct hostapd_data *hapd, u8 *eid)
@@ -3788,7 +3790,8 @@ static bool check_sa_query(struct hostapd_data *hapd, struct sta_info *sta,
 
 static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
                             const u8 *ies, size_t ies_len,
-                            struct ieee802_11_elems *elems, int reassoc)
+                            struct ieee802_11_elems *elems, int reassoc,
+                            bool link)
 {
        int resp;
        const u8 *wpa_ie;
@@ -3890,6 +3893,12 @@ static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
                                          elems->eht_capabilities_len);
                if (resp != WLAN_STATUS_SUCCESS)
                        return resp;
+
+               if (!link) {
+                       resp = hostapd_process_ml_assoc_req(hapd, elems, sta);
+                       if (resp != WLAN_STATUS_SUCCESS)
+                               return resp;
+               }
        }
 #endif /* CONFIG_IEEE80211BE */
 
@@ -4249,7 +4258,255 @@ static int check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
                return WLAN_STATUS_UNSPECIFIED_FAILURE;
        }
 
-       return __check_assoc_ies(hapd, sta, ies, ies_len, &elems, reassoc);
+       return __check_assoc_ies(hapd, sta, ies, ies_len, &elems, reassoc,
+                                false);
+}
+
+
+#ifdef CONFIG_IEEE80211BE
+
+static size_t ieee80211_ml_build_assoc_resp(struct hostapd_data *hapd,
+                                           u16 status_code,
+                                           u8 *buf, size_t buflen)
+{
+       u8 *p = buf;
+
+       /* Capability Info */
+       WPA_PUT_LE16(p, hostapd_own_capab_info(hapd));
+       p += 2;
+
+       /* Status Code */
+       WPA_PUT_LE16(p, status_code);
+       p += 2;
+
+       if (status_code != WLAN_STATUS_SUCCESS)
+               return p - buf;
+
+       /* AID is not included */
+       p = hostapd_eid_supp_rates(hapd, p);
+       p = hostapd_eid_ext_supp_rates(hapd, p);
+       p = hostapd_eid_rm_enabled_capab(hapd, p, buf + buflen - p);
+       p = hostapd_eid_ht_capabilities(hapd, p);
+       p = hostapd_eid_ht_operation(hapd, p);
+
+       if (hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac) {
+               p = hostapd_eid_vht_capabilities(hapd, p, 0);
+               p = hostapd_eid_vht_operation(hapd, p);
+       }
+
+       if (hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax) {
+               p = hostapd_eid_he_capab(hapd, p, IEEE80211_MODE_AP);
+               p = hostapd_eid_he_operation(hapd, p);
+               p = hostapd_eid_spatial_reuse(hapd, p);
+               p = hostapd_eid_he_mu_edca_parameter_set(hapd, p);
+               p = hostapd_eid_he_6ghz_band_cap(hapd, p);
+               if (hapd->iconf->ieee80211be && !hapd->conf->disable_11be) {
+                       p = hostapd_eid_eht_capab(hapd, p, IEEE80211_MODE_AP);
+                       p = hostapd_eid_eht_operation(hapd, p);
+               }
+       }
+
+       p = hostapd_eid_ext_capab(hapd, p, false);
+       p = hostapd_eid_mbo(hapd, p, buf + buflen - p);
+       p = hostapd_eid_wmm(hapd, p);
+
+       if (hapd->conf->assocresp_elements &&
+           (size_t) (buf + buflen - p) >=
+           wpabuf_len(hapd->conf->assocresp_elements)) {
+               os_memcpy(p, wpabuf_head(hapd->conf->assocresp_elements),
+                         wpabuf_len(hapd->conf->assocresp_elements));
+               p += wpabuf_len(hapd->conf->assocresp_elements);
+       }
+
+       return p - buf;
+}
+
+
+static void ieee80211_ml_process_link(struct hostapd_data *hapd,
+                                     struct sta_info *origin_sta,
+                                     struct mld_link_info *link,
+                                     const u8 *ies, size_t ies_len,
+                                     bool reassoc)
+{
+       struct ieee802_11_elems elems;
+       struct wpabuf *mlbuf = NULL;
+       struct sta_info *sta = NULL;
+       u16 status = WLAN_STATUS_SUCCESS;
+
+       wpa_printf(MSG_DEBUG, "MLD: link: link_id=%u, peer=" MACSTR,
+                  hapd->mld_link_id, MAC2STR(link->peer_addr));
+
+       if (ieee802_11_parse_elems(ies, ies_len, &elems, 1) == ParseFailed) {
+               wpa_printf(MSG_DEBUG, "MLD: link: Element parsing failed");
+               status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+               goto out;
+       }
+
+       sta = ap_get_sta(hapd, origin_sta->addr);
+       if (sta) {
+               wpa_printf(MSG_INFO, "MLD: link: Station already exists");
+               status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+               sta = NULL;
+               goto out;
+       }
+
+       sta = ap_sta_add(hapd, origin_sta->addr);
+       if (!sta) {
+               wpa_printf(MSG_DEBUG, "MLD: link: ap_sta_add() failed");
+               status = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
+               goto out;
+       }
+
+       mlbuf = ieee802_11_defrag_mle(&elems, MULTI_LINK_CONTROL_TYPE_BASIC);
+       if (!mlbuf)
+               goto out;
+
+       if (ieee802_11_parse_link_assoc_req(ies, ies_len, &elems, mlbuf,
+                                           hapd->mld_link_id, true) ==
+           ParseFailed) {
+               wpa_printf(MSG_DEBUG,
+                          "MLD: link: Failed to parse association request Multi-Link element");
+               status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+               goto out;
+       }
+
+       sta->flags |= origin_sta->flags | WLAN_STA_ASSOC_REQ_OK;
+       status = __check_assoc_ies(hapd, sta, NULL, 0, &elems, reassoc, true);
+       if (status != WLAN_STATUS_SUCCESS) {
+               wpa_printf(MSG_DEBUG, "MLD: link: Element check failed");
+               goto out;
+       }
+
+       sta->mld_info.mld_sta = true;
+       sta->mld_assoc_link_id = origin_sta->mld_assoc_link_id;
+
+       os_memcpy(&sta->mld_info, &origin_sta->mld_info, sizeof(sta->mld_info));
+
+       /*
+        * Get the AID from the station on which the association was performed,
+        * and mark it as used.
+        */
+       sta->aid = origin_sta->aid;
+       if (sta->aid == 0) {
+               wpa_printf(MSG_DEBUG, "MLD: link: No AID assigned");
+               status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+               goto out;
+       }
+       hapd->sta_aid[(sta->aid - 1) / 32] |= BIT((sta->aid - 1) % 32);
+       sta->listen_interval = origin_sta->listen_interval;
+       update_ht_state(hapd, sta);
+
+       /* RSN Authenticator should always be the one on the original station */
+       wpa_auth_sta_deinit(sta->wpa_sm);
+       sta->wpa_sm = NULL;
+
+       /*
+        * Do not initialize the EAPOL state machine.
+        * TODO: Maybe it is needed?
+        */
+       sta->eapol_sm = NULL;
+
+       wpa_printf(MSG_DEBUG, "MLD: link=%u, association OK (aid=%u)",
+                  hapd->mld_link_id, sta->aid);
+
+       /*
+        * Get RSNE and RSNXE for the current BSS as they are required by the
+        * Authenticator.
+        */
+       link->rsne = hostapd_wpa_ie(hapd, WLAN_EID_RSN);
+       link->rsnxe = hostapd_wpa_ie(hapd, WLAN_EID_RSNX);
+
+       sta->flags |= WLAN_STA_AUTH | WLAN_STA_ASSOC_REQ_OK;
+
+       /* TODO: What other processing is required? */
+
+       if (add_associated_sta(hapd, sta, reassoc))
+               status = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
+out:
+       wpabuf_free(mlbuf);
+       link->status = status;
+
+       wpa_printf(MSG_DEBUG, "MLD: link: status=%u", status);
+       if (sta && status != WLAN_STATUS_SUCCESS)
+               ap_free_sta(hapd, sta);
+
+       link->resp_sta_profile_len =
+               ieee80211_ml_build_assoc_resp(hapd, link->status,
+                                             link->resp_sta_profile,
+                                             sizeof(link->resp_sta_profile));
+}
+
+
+static bool hostapd_is_mld_ap(struct hostapd_data *hapd)
+{
+       if (!hapd->conf->mld_ap)
+               return false;
+
+       if (!hapd->iface || !hapd->iface->interfaces ||
+           hapd->iface->interfaces->count <= 1)
+               return false;
+
+       return true;
+}
+
+#endif /* CONFIG_IEEE80211BE */
+
+
+static void hostapd_process_assoc_ml_info(struct hostapd_data *hapd,
+                                         struct sta_info *sta,
+                                         const u8 *ies, size_t ies_len,
+                                         bool reassoc)
+{
+#ifdef CONFIG_IEEE80211BE
+       unsigned int i, j;
+
+       if (!hostapd_is_mld_ap(hapd))
+               return;
+
+       /*
+        * This is not really needed, but make the interaction with the RSN
+        * Authenticator more consistent
+        */
+       sta->mld_info.links[hapd->mld_link_id].rsne =
+               hostapd_wpa_ie(hapd, WLAN_EID_RSN);
+       sta->mld_info.links[hapd->mld_link_id].rsnxe =
+               hostapd_wpa_ie(hapd, WLAN_EID_RSNX);
+
+       for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
+               struct hostapd_iface *iface = NULL;
+               struct mld_link_info *link = &sta->mld_info.links[i];
+
+               if (!link->valid)
+                       continue;
+
+               for (j = 0; j < hapd->iface->interfaces->count; j++) {
+                       iface = hapd->iface->interfaces->iface[j];
+
+                       if (hapd->iface == iface)
+                               continue;
+
+                       if (iface->bss[0]->conf->mld_ap &&
+                           hapd->conf->mld_id == iface->bss[0]->conf->mld_id &&
+                           i == iface->bss[0]->mld_link_id)
+                               break;
+               }
+
+               if (!iface || j == hapd->iface->interfaces->count) {
+                       wpa_printf(MSG_DEBUG,
+                                  "MLD: No link match for link_id=%u", i);
+
+                       link->status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+                       link->resp_sta_profile_len =
+                               ieee80211_ml_build_assoc_resp(
+                                       hapd, link->status,
+                                       link->resp_sta_profile,
+                                       sizeof(link->resp_sta_profile));
+               } else {
+                       ieee80211_ml_process_link(iface->bss[0], sta, link,
+                                                 ies, ies_len, reassoc);
+               }
+       }
+#endif /* CONFIG_IEEE80211BE */
 }
 
 
@@ -5175,6 +5432,9 @@ static void handle_assoc(struct hostapd_data *hapd,
         *    issues with processing other non-Data Class 3 frames during this
         *    window.
         */
+       if (resp == WLAN_STATUS_SUCCESS)
+               hostapd_process_assoc_ml_info(hapd, sta, pos, left, reassoc);
+
        if (resp == WLAN_STATUS_SUCCESS && sta &&
            add_associated_sta(hapd, sta, reassoc))
                resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
index 8428813587b994f530a8271b3d1d1a6948803835..4d207699bdfad8687de3afb81938a7c184f4771b 100644 (file)
@@ -19,6 +19,7 @@ struct ieee80211_mgmt;
 struct radius_sta;
 enum ieee80211_op_mode;
 enum oper_chan_width;
+struct ieee802_11_elems;
 
 int ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len,
                    struct hostapd_frame_info *fi);
@@ -90,6 +91,9 @@ struct wpabuf * hostapd_ml_auth_resp(struct hostapd_data *hapd);
 const u8 * hostapd_process_ml_auth(struct hostapd_data *hapd,
                                   const struct ieee80211_mgmt *mgmt,
                                   size_t len);
+u16 hostapd_process_ml_assoc_req(struct hostapd_data *hapd,
+                                struct ieee802_11_elems *elems,
+                                struct sta_info *sta);
 int hostapd_get_aid(struct hostapd_data *hapd, struct sta_info *sta);
 u16 copy_sta_ht_capab(struct hostapd_data *hapd, struct sta_info *sta,
                      const u8 *ht_capab);
index 2c003e3bb3c9f9e0e69ccd214f11ac6588742acc..9a07f750111509520795447a4d4b77842366c354 100644 (file)
@@ -827,3 +827,313 @@ const u8 * hostapd_process_ml_auth(struct hostapd_data *hapd,
 
        return get_basic_mle_mld_addr(elems.basic_mle, elems.basic_mle_len);
 }
+
+
+static int hostapd_mld_validate_assoc_info(struct hostapd_data *hapd,
+                                          struct mld_info *info)
+{
+       u8 i, link_id;
+
+       if (!info->mld_sta) {
+               wpa_printf(MSG_DEBUG, "MLD: Not a non-AP MLD");
+               return 0;
+       }
+
+       /*
+        * Iterate over the links negotiated in the (Re)Association Request
+        * frame and validate that they are indeed valid links in the local AP
+        * MLD.
+        *
+        * While at it, also update the local address for the links in the
+        * mld_info, so it could be easily available for later flows, e.g., for
+        * the RSN Authenticator, etc.
+        */
+       for (link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
+               struct hostapd_data *other_hapd;
+
+               if (!info->links[link_id].valid)
+                       continue;
+
+               for (i = 0; i < hapd->iface->interfaces->count; i++) {
+                       other_hapd = hapd->iface->interfaces->iface[i]->bss[0];
+
+                       if (hapd == other_hapd)
+                               continue;
+
+                       if (other_hapd->conf->mld_ap &&
+                           other_hapd->conf->mld_id == hapd->conf->mld_id &&
+                           link_id == other_hapd->mld_link_id)
+                               break;
+               }
+
+               if (i == hapd->iface->interfaces->count &&
+                   link_id != hapd->mld_link_id) {
+                       wpa_printf(MSG_DEBUG, "MLD: Invalid link ID=%u",
+                                  link_id);
+                       return -1;
+               }
+
+               if (i < hapd->iface->interfaces->count)
+                       os_memcpy(info->links[link_id].local_addr,
+                                 other_hapd->own_addr,
+                                 ETH_ALEN);
+       }
+
+       return 0;
+}
+
+
+u16 hostapd_process_ml_assoc_req(struct hostapd_data *hapd,
+                                struct ieee802_11_elems *elems,
+                                struct sta_info *sta)
+{
+       struct wpabuf *mlbuf;
+       const struct ieee80211_eht_ml *ml;
+       const struct eht_ml_basic_common_info *common_info;
+       size_t ml_len, common_info_len;
+       struct mld_link_info *link_info;
+       struct mld_info *info = &sta->mld_info;
+       const u8 *pos;
+       int ret = -1;
+       u16 ml_control;
+
+       mlbuf = ieee802_11_defrag_mle(elems, MULTI_LINK_CONTROL_TYPE_BASIC);
+       if (!mlbuf)
+               return WLAN_STATUS_SUCCESS;
+
+       ml = wpabuf_head(mlbuf);
+       ml_len = wpabuf_len(mlbuf);
+
+       ml_control = le_to_host16(ml->ml_control);
+       if ((ml_control & MULTI_LINK_CONTROL_TYPE_MASK) !=
+           MULTI_LINK_CONTROL_TYPE_BASIC) {
+               wpa_printf(MSG_DEBUG, "MLD: Invalid ML type=%u",
+                          ml_control & MULTI_LINK_CONTROL_TYPE_MASK);
+               goto out;
+       }
+
+       /* Common Info length and MLD MAC address must always be present */
+       common_info_len = 1 + ETH_ALEN;
+
+       if (ml_control & BASIC_MULTI_LINK_CTRL_PRES_LINK_ID) {
+               wpa_printf(MSG_DEBUG, "MLD: Link ID info not expected");
+               goto out;
+       }
+
+       if (ml_control & BASIC_MULTI_LINK_CTRL_PRES_BSS_PARAM_CH_COUNT) {
+               wpa_printf(MSG_DEBUG, "MLD: BSS params change not expected");
+               goto out;
+       }
+
+       if (ml_control & BASIC_MULTI_LINK_CTRL_PRES_MSD_INFO) {
+               wpa_printf(MSG_DEBUG, "MLD: Sync delay not expected");
+               goto out;
+       }
+
+       if (ml_control & BASIC_MULTI_LINK_CTRL_PRES_EML_CAPA) {
+               common_info_len += 2;
+       } else {
+               wpa_printf(MSG_DEBUG, "MLD: EML capabilities not present");
+       }
+
+       if (ml_control & BASIC_MULTI_LINK_CTRL_PRES_MLD_CAPA) {
+               common_info_len += 2;
+
+       } else {
+               wpa_printf(MSG_DEBUG, "MLD: MLD capabilities not present");
+               goto out;
+       }
+
+       wpa_printf(MSG_DEBUG, "MLD: expected_common_info_len=%lu",
+                  common_info_len);
+
+       if (sizeof(*ml) + common_info_len > ml_len) {
+               wpa_printf(MSG_DEBUG, "MLD: Not enough bytes for common info");
+               goto out;
+       }
+
+       common_info = (const struct eht_ml_basic_common_info *) ml->variable;
+
+       /* Common information length includes the length octet */
+       if (common_info->len != common_info_len) {
+               wpa_printf(MSG_DEBUG,
+                          "MLD: Invalid common info len=%u (expected %zu)",
+                          common_info->len, common_info_len);
+               goto out;
+       }
+
+       pos = common_info->variable;
+
+       if (ml_control & BASIC_MULTI_LINK_CTRL_PRES_EML_CAPA) {
+               info->common_info.eml_capa = WPA_GET_LE16(pos);
+               pos += 2;
+       } else {
+               info->common_info.eml_capa = 0;
+       }
+
+       info->common_info.mld_capa = WPA_GET_LE16(pos);
+       pos += 2;
+
+       wpa_printf(MSG_DEBUG, "MLD: addr=" MACSTR ", eml=0x%x, mld=0x%x",
+                  MAC2STR(info->common_info.mld_addr),
+                  info->common_info.eml_capa, info->common_info.mld_capa);
+
+       /* Check the MLD MAC Address */
+       if (os_memcmp(info->common_info.mld_addr, common_info->mld_addr,
+                     ETH_ALEN) != 0) {
+               wpa_printf(MSG_DEBUG,
+                          "MLD: MLD address mismatch between authentication ("
+                          MACSTR ") and association (" MACSTR ")",
+                          MAC2STR(info->common_info.mld_addr),
+                          MAC2STR(common_info->mld_addr));
+               goto out;
+       }
+
+       info->links[hapd->mld_link_id].valid = true;
+
+       /* Parse the link info field */
+       ml_len -= sizeof(*ml) + common_info_len;
+
+       while (ml_len > 2) {
+               size_t sub_elem_len = *(pos + 1);
+               size_t sta_info_len;
+               u16 control;
+
+               wpa_printf(MSG_DEBUG, "MLD: sub element len=%zu",
+                          sub_elem_len);
+
+               if (2 + sub_elem_len > ml_len) {
+                       wpa_printf(MSG_DEBUG,
+                                  "MLD: Invalid link info len: %zu %zu",
+                                  2 + sub_elem_len, ml_len);
+                       goto out;
+               }
+
+               if (*pos == MULTI_LINK_SUB_ELEM_ID_VENDOR) {
+                       wpa_printf(MSG_DEBUG,
+                                  "MLD: Skip vendor specific subelement");
+
+                       pos += 2 + sub_elem_len;
+                       ml_len -= 2 + sub_elem_len;
+                       continue;
+               }
+
+               if (*pos != MULTI_LINK_SUB_ELEM_ID_PER_STA_PROFILE) {
+                       wpa_printf(MSG_DEBUG,
+                                  "MLD: Unexpected Multi-Link element subelement ID=%u",
+                                  *pos);
+                       goto out;
+               }
+
+               /* Skip the subelement ID and the length */
+               pos += 2;
+               ml_len -= 2;
+
+               /* Get the station control field */
+               if (sub_elem_len < 2) {
+                       wpa_printf(MSG_DEBUG,
+                                  "MLD: Too short Per-STA Profile subelement");
+                       goto out;
+               }
+               control = WPA_GET_LE16(pos);
+               link_info = &info->links[control &
+                                        EHT_PER_STA_CTRL_LINK_ID_MSK];
+               pos += 2;
+               ml_len -= 2;
+               sub_elem_len -= 2;
+
+               if (!(control & EHT_PER_STA_CTRL_COMPLETE_PROFILE_MSK)) {
+                       wpa_printf(MSG_DEBUG,
+                                  "MLD: Per-STA complete profile expected");
+                       goto out;
+               }
+
+               if (!(control & EHT_PER_STA_CTRL_MAC_ADDR_PRESENT_MSK)) {
+                       wpa_printf(MSG_DEBUG,
+                                  "MLD: Per-STA MAC address not present");
+                       goto out;
+               }
+
+               if ((control & (EHT_PER_STA_CTRL_BEACON_INTERVAL_PRESENT_MSK |
+                               EHT_PER_STA_CTRL_DTIM_INFO_PRESENT_MSK))) {
+                       wpa_printf(MSG_DEBUG,
+                                  "MLD: Beacon/DTIM interval not expected");
+                       goto out;
+               }
+
+               /* The length octet and the MAC address must be present */
+               sta_info_len = 1 + ETH_ALEN;
+
+               if (control & EHT_PER_STA_CTRL_NSTR_LINK_PAIR_PRESENT_MSK) {
+                       if (control & EHT_PER_STA_CTRL_NSTR_BM_SIZE_MSK)
+                               link_info->nstr_bitmap_len = 2;
+                       else
+                               link_info->nstr_bitmap_len = 1;
+               }
+
+               sta_info_len += link_info->nstr_bitmap_len;
+
+               if (sta_info_len > ml_len || sta_info_len != *pos ||
+                   sta_info_len > sub_elem_len) {
+                       wpa_printf(MSG_DEBUG, "MLD: Invalid STA Info length");
+                       goto out;
+               }
+
+               /* skip the length */
+               pos++;
+               ml_len--;
+
+               /* get the link address */
+               os_memcpy(link_info->peer_addr, pos, ETH_ALEN);
+               wpa_printf(MSG_DEBUG,
+                          "MLD: assoc: link id=%u, addr=" MACSTR,
+                          control & EHT_PER_STA_CTRL_LINK_ID_MSK,
+                          MAC2STR(link_info->peer_addr));
+
+               pos += ETH_ALEN;
+               ml_len -= ETH_ALEN;
+
+               /* Get the NSTR bitmap */
+               if (link_info->nstr_bitmap_len) {
+                       os_memcpy(link_info->nstr_bitmap, pos,
+                                 link_info->nstr_bitmap_len);
+                       pos += link_info->nstr_bitmap_len;
+                       ml_len -= link_info->nstr_bitmap_len;
+               }
+
+               sub_elem_len -= sta_info_len;
+
+               wpa_printf(MSG_DEBUG, "MLD: STA Profile len=%zu", sub_elem_len);
+               if (sub_elem_len > ml_len)
+                       goto out;
+
+               if (sub_elem_len > 2)
+                       link_info->capability = WPA_GET_LE16(pos);
+
+               pos += sub_elem_len;
+               ml_len -= sub_elem_len;
+
+               wpa_printf(MSG_DEBUG, "MLD: link ctrl=0x%x, " MACSTR
+                          ", nstr bitmap len=%lu",
+                          control, MAC2STR(link_info->peer_addr),
+                          link_info->nstr_bitmap_len);
+
+               link_info->valid = true;
+       }
+
+       if (ml_len) {
+               wpa_printf(MSG_DEBUG, "MLD: %zu bytes left after parsing. fail",
+                          ml_len);
+               goto out;
+       }
+
+       ret = hostapd_mld_validate_assoc_info(hapd, info);
+out:
+       wpabuf_free(mlbuf);
+       if (ret) {
+               os_memset(info, 0, sizeof(*info));
+               return WLAN_STATUS_UNSPECIFIED_FAILURE;
+       }
+
+       return WLAN_STATUS_SUCCESS;
+}
index 041c95961dd537570049f20e798d269e54b320bd..01d73cf1494c6935a6ce081895bd40c72a62244e 100644 (file)
@@ -301,7 +301,15 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta)
        sae_clear_retransmit_timer(hapd, sta);
 
        ieee802_1x_free_station(hapd, sta);
+
+#ifdef CONFIG_IEEE80211BE
+       if (!hapd->conf->mld_ap || !sta->mld_info.mld_sta ||
+           hapd->mld_link_id == sta->mld_assoc_link_id)
+               wpa_auth_sta_deinit(sta->wpa_sm);
+#else
        wpa_auth_sta_deinit(sta->wpa_sm);
+#endif /* CONFIG_IEEE80211BE */
+
        rsn_preauth_free_station(hapd, sta);
 #ifndef CONFIG_NO_RADIUS
        if (hapd->radius)
@@ -866,7 +874,14 @@ void ap_sta_disassociate(struct hostapd_data *hapd, struct sta_info *sta,
                               ap_handle_timer, hapd, sta);
        accounting_sta_stop(hapd, sta);
        ieee802_1x_free_station(hapd, sta);
+#ifdef CONFIG_IEEE80211BE
+       if (!hapd->conf->mld_ap ||
+           hapd->mld_link_id == sta->mld_assoc_link_id)
+               wpa_auth_sta_deinit(sta->wpa_sm);
+#else
        wpa_auth_sta_deinit(sta->wpa_sm);
+#endif /* CONFIG_IEEE80211BE */
+
        sta->wpa_sm = NULL;
 
        sta->disassoc_reason = reason;