[PATCH v2 17/44] AP: MLO: Process EHT ML element from association request

Andrei Otcheretianski andrei.otcheretianski at intel.com
Mon May 22 12:33:45 PDT 2023


Implement processing of the ML element in the association
request, including processing of the per station profile.

After handling the basic parsing of the element, 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 station profile to be included in the
  ML element in the association response.

Signed-off-by: Ilan Peer <ilan.peer at intel.com>
Signed-off-by: Andrei Otcheretianski <andrei.otcheretianski at intel.com>
---
 src/ap/ieee802_11.c     | 265 +++++++++++++++++++++++++++++++++++-
 src/ap/ieee802_11.h     |   4 +
 src/ap/ieee802_11_eht.c | 292 ++++++++++++++++++++++++++++++++++++++++
 src/ap/sta_info.c       |  15 +++
 4 files changed, 573 insertions(+), 3 deletions(-)

diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
index 1cc42ea83c..a7dccbc444 100644
--- a/src/ap/ieee802_11.c
+++ b/src/ap/ieee802_11.c
@@ -83,7 +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)
 {
@@ -3785,7 +3786,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;
@@ -3887,6 +3889,14 @@ 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 */
 
@@ -4246,7 +4256,253 @@ 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)
+		goto out;
+
+	/* 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);
+	}
+
+out:
+	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,
+				      int reassoc)
+{
+	struct ieee802_11_elems elems;
+	struct wpabuf *mlbuf = NULL;
+	struct sta_info *sta = NULL;
+	u16 status = WLAN_STATUS_SUCCESS;
+
+	wpa_printf(MSG_INFO, "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: failed parsing");
+		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 (ParseOK != ieee802_11_parse_link_assoc_req(ies, ies_len, &elems,
+						       mlbuf,
+						       hapd->mld_link_id,
+						       true)) {
+		wpa_printf(MSG_DEBUG, "MLD: link: assoc_req parse failed");
+		status = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
+		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 preformed,
+	 * and mark it as used.
+	 */
+	sta->aid = origin_sta->aid;
+	hapd->sta_aid[(sta->aid - 1) / 32] |= BIT((sta->aid - 1) % 32);
+	sta->listen_interval = origin_sta->listen_interval;
+	update_ht_state(hapd, sta);
+
+	/* WPA 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 RSN and RSNXE elements 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,
+					  int reassoc)
+{
+#ifdef CONFIG_IEEE80211BE
+	u8 i, j;
+
+	if (!hostapd_is_mld_ap(hapd))
+		return;
+
+	/*
+	 * This is not really needed, but make the interaction with the WPA
+	 * 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 */
 }
 
 
@@ -5174,6 +5430,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;
diff --git a/src/ap/ieee802_11.h b/src/ap/ieee802_11.h
index 0c0ddd631c..2eb40a33ce 100644
--- a/src/ap/ieee802_11.h
+++ b/src/ap/ieee802_11.h
@@ -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);
diff --git a/src/ap/ieee802_11_eht.c b/src/ap/ieee802_11_eht.c
index cd7ca95c2e..12a053fa92 100644
--- a/src/ap/ieee802_11_eht.c
+++ b/src/ap/ieee802_11_eht.c
@@ -809,3 +809,295 @@ 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 an MLD station");
+		return 0;
+	}
+
+	/*
+	 * Iterate over the link negotiated in the association request and
+	 * validate that they are indeed valid links in the local MLD AP.
+	 * 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 WPA 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 = ieee802_11_defrag_mle(elems,
+						 MULTI_LINK_CONTROL_TYPE_BASIC);
+	struct ieee80211_eht_ml *ml;
+	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;
+	u8 *pos;
+	int ret = -1;
+	u16 ml_control;
+
+	if (!mlbuf)
+		return WLAN_STATUS_SUCCESS;
+
+	ml = (struct ieee80211_eht_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 address must always be present */
+	common_info_len = 7;
+
+	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 = (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", 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);
+
+
+	/* get the MLD Address */
+	if (os_memcmp(info->common_info.mld_addr, common_info->mld_addr,
+		      ETH_ALEN)) {
+		wpa_printf(MSG_DEBUG, "MLD: MLD address mismatch with auth");
+		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 == WLAN_EID_VENDOR_SPECIFIC) {
+			wpa_printf(MSG_DEBUG,
+				   "MLD: skip vendor specific subelement");
+
+			pos += 2 + sub_elem_len;
+			ml_len -= 2 + sub_elem_len;
+			continue;
+		} else if (*pos != 0) {
+			wpa_printf(MSG_DEBUG, "MLD: invalid subelement ID=%u",
+				   *pos);
+			goto out;
+		}
+
+		/* skip the subelement ID and the length */
+		pos += 2;
+		ml_len -= 2;
+
+		/* get the station control field */
+		control = WPA_GET_LE16(pos);
+		link_info = &info->links[control & EHT_PER_STA_CTRL_LINK_ID_MSK];
+		pos += 2;
+		ml_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)) {
+			wpa_printf(MSG_DEBUG, "MLD: invalid sta info len");
+			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 + 2;
+
+		wpa_printf(MSG_DEBUG, "MLD: STA profile len=%zu",
+			   sub_elem_len);
+
+		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,
+			   "link ctrl=0x%x, " MACSTR ", nstr bm 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;
+}
diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c
index edfda2abf1..61116cff96 100644
--- a/src/ap/sta_info.c
+++ b/src/ap/sta_info.c
@@ -298,7 +298,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)
@@ -863,7 +871,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;
-- 
2.38.1




More information about the Hostap mailing list