diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
index 43558a0e46..6fd9416eed 100644
--- a/src/ap/ieee802_11.c
+++ b/src/ap/ieee802_11.c
@@ -3968,15 +3968,33 @@ static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
 
 		wpa_ie -= 2;
 		wpa_ie_len += 2;
-		if (sta->wpa_sm == NULL)
+		if (!sta->wpa_sm) {
+#ifdef CONFIG_IEEE80211BE
+			struct mld_info *info = &sta->mld_info;
+#endif /* CONFIG_IEEE80211BE */
+
 			sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth,
 							sta->addr,
 							p2p_dev_addr);
-		if (sta->wpa_sm == NULL) {
-			wpa_printf(MSG_WARNING, "Failed to initialize WPA "
-				   "state machine");
-			return WLAN_STATUS_UNSPECIFIED_FAILURE;
+
+			if (!sta->wpa_sm) {
+				wpa_printf(MSG_WARNING,
+					   "Failed to initialize WPA SM");
+				return WLAN_STATUS_UNSPECIFIED_FAILURE;
+			}
+
+#ifdef CONFIG_IEEE80211BE
+			if (info->mld_sta) {
+				wpa_printf(MSG_DEBUG, "MLD: used for WPA");
+
+				wpa_auth_set_ml_info(sta->wpa_sm,
+						     hapd->mld_addr,
+						     sta->mld_assoc_link_id,
+						     info);
+			}
+#endif /* CONFIG_IEEE80211BE */
 		}
+
 		wpa_auth_set_auth_alg(sta->wpa_sm, sta->auth_alg);
 		res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm,
 					  hapd->iface->freq,
diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c
index 12b49704b9..f661585611 100644
--- a/src/ap/wpa_auth.c
+++ b/src/ap/wpa_auth.c
@@ -33,6 +33,7 @@
 #include "pmksa_cache_auth.h"
 #include "wpa_auth_i.h"
 #include "wpa_auth_ie.h"
+#include "sta_info.h"
 
 #define STATE_MACHINE_DATA struct wpa_state_machine
 #define STATE_MACHINE_DEBUG_PREFIX "WPA"
@@ -695,6 +696,7 @@ wpa_auth_sta_init(struct wpa_authenticator *wpa_auth, const u8 *addr,
 	sm->wpa_auth = wpa_auth;
 	sm->group = wpa_auth->group;
 	wpa_group_get(sm->wpa_auth, sm->group);
+	sm->mld_assoc_link_id = -1;
 
 	return sm;
 }
@@ -788,6 +790,11 @@ static void wpa_free_sta_sm(struct wpa_state_machine *sm)
 #ifdef CONFIG_DPP2
 	wpabuf_clear_free(sm->dpp_z);
 #endif /* CONFIG_DPP2 */
+	os_memset(sm->own_mld_addr, 0, sizeof(sm->own_mld_addr));
+	os_memset(sm->peer_mld_addr, 0, sizeof(sm->peer_mld_addr));
+	sm->mld_assoc_link_id = -1;
+	os_memset(sm->mld_links, 0, sizeof(sm->mld_links));
+
 	bin_clear_free(sm, sizeof(*sm));
 }
 
@@ -3589,6 +3596,29 @@ static u8 * replace_ie(const char *name, const u8 *old_buf, size_t *len, u8 eid,
 }
 #endif /* CONFIG_TESTING_OPTIONS */
 
+#ifdef CONFIG_IEEE80211BE
+
+void wpa_auth_ml_get_rsn_info(struct wpa_authenticator *a,
+			      struct wpa_auth_ml_link_rsn_info *info)
+{
+	info->rsn_ies = a->wpa_ie;
+	info->rsn_ies_len = a->wpa_ie_len;
+
+	wpa_printf(MSG_DEBUG,
+		   "WPA_AUTH: MLD: RSN info: link_id=%u, rsn_ies_len=%u",
+		   info->link_id, info->rsn_ies_len);
+}
+
+
+static void wpa_auth_get_ml_rsn_info(struct wpa_authenticator *wpa_auth,
+				     struct wpa_auth_ml_rsn_info *info)
+{
+	if (!wpa_auth->cb->get_ml_rsn_info)
+		return;
+
+	wpa_auth->cb->get_ml_rsn_info(wpa_auth->cb_ctx, info);
+}
+#endif /* CONFIG_IEEE80211BE */
 
 SM_STATE(WPA_PTK, PTKINITNEGOTIATING)
 {
@@ -6026,3 +6056,84 @@ void wpa_auth_sta_radius_psk_resp(struct wpa_state_machine *sm, bool success)
 
 	eloop_register_timeout(0, 0, wpa_sm_call_step, sm, NULL);
 }
+
+
+void wpa_auth_set_ml_info(struct wpa_state_machine *sm,
+			  const u8 *mld_addr, u8 mld_assoc_link_id,
+			  struct mld_info *info)
+{
+#ifdef CONFIG_IEEE80211BE
+	struct wpa_auth_ml_rsn_info ml_rsn_info;
+	u32 link_id, i;
+
+	if (!info)
+		return;
+
+	os_memset(sm->mld_links, 0, sizeof(sm->mld_links));
+
+	wpa_auth_logger(sm->wpa_auth, wpa_auth_get_spa(sm), LOGGER_DEBUG,
+			"MLD: initialization");
+
+	os_memcpy(sm->own_mld_addr, mld_addr, ETH_ALEN);
+	os_memcpy(sm->peer_mld_addr, info->common_info.mld_addr, ETH_ALEN);
+
+	sm->mld_assoc_link_id = mld_assoc_link_id;
+
+	os_memset(&ml_rsn_info, 0, sizeof(ml_rsn_info));
+
+	for (i = 0, link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
+		struct mld_link_info *link = &info->links[link_id];
+		struct mld_link *sm_link = &sm->mld_links[link_id];
+
+		sm_link->valid = link->valid;
+
+		if (!link->valid)
+			continue;
+
+		os_memcpy(sm_link->peer_addr, link->peer_addr, ETH_ALEN);
+		os_memcpy(sm_link->own_addr, link->local_addr, ETH_ALEN);
+
+		wpa_printf(MSG_DEBUG,
+			   "WPA_AUTH: MLD: id=%u, addr=" MACSTR " peer=" MACSTR,
+			   link_id,
+			   MAC2STR(sm_link->own_addr),
+			   MAC2STR(sm_link->peer_addr));
+
+		if (link_id != mld_assoc_link_id)
+			sm->n_mld_affiliated_links++;
+
+		ml_rsn_info.links[i++].link_id = link_id;
+	}
+
+	ml_rsn_info.n_mld_links = i;
+
+	wpa_auth_get_ml_rsn_info(sm->wpa_auth, &ml_rsn_info);
+
+	for (i = 0; i < ml_rsn_info.n_mld_links; i++) {
+		u8 link_id = ml_rsn_info.links[i].link_id;
+		struct mld_link *sm_link = &sm->mld_links[link_id];
+		const u8 *rsn_ies;
+		u8 rsn_ies_len;
+
+		rsn_ies = ml_rsn_info.links[i].rsn_ies;
+		rsn_ies_len = ml_rsn_info.links[i].rsn_ies_len;
+
+		/* This should not really happen */
+		if (!rsn_ies || rsn_ies_len < 2 || rsn_ies[0] != WLAN_EID_RSN) {
+			wpa_printf(MSG_DEBUG,
+				   "WPA_AUTH: MLD: Invalid RSN element");
+			continue;
+		}
+
+		sm_link->rsne = rsn_ies;
+		sm_link->rsne_len = rsn_ies[1] + 2;
+
+		if (rsn_ies[1] + 2UL + 2UL < rsn_ies_len &&
+		    rsn_ies[rsn_ies[1] + 2] == WLAN_EID_RSNX) {
+			sm_link->rsnxe = rsn_ies + 2 + rsn_ies[1];
+			sm_link->rsnxe_len = sm_link->rsnxe[1] + 2;
+		}
+	}
+
+#endif /* CONFIG_IEEE80211BE */
+}
diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h
index eed016accb..fbd9d84f8b 100644
--- a/src/ap/wpa_auth.h
+++ b/src/ap/wpa_auth.h
@@ -15,6 +15,7 @@
 #include "common/ieee802_11_defs.h"
 
 struct vlan_description;
+struct mld_info;
 
 #define MAX_OWN_IE_OVERRIDE 256
 
@@ -288,6 +289,40 @@ typedef enum {
 	WPA_EAPOL_keyDone, WPA_EAPOL_inc_EapolFramesTx
 } wpa_eapol_variable;
 
+struct wpa_auth_ml_rsn_info {
+	u8 n_mld_links;
+
+	struct wpa_auth_ml_link_rsn_info {
+		u8 link_id;
+		const u8 *rsn_ies;
+		u8 rsn_ies_len;
+	} links[MAX_NUM_MLD_LINKS];
+};
+
+struct wpa_auth_ml_key_info {
+	u8 n_mld_links;
+	bool mgmt_frame_prot;
+	bool beacon_prot;
+
+	struct wpa_auth_ml_link_key_info {
+		u8 link_id;
+
+		u8 gtkidx;
+		u8 gtk_len;
+		u8 pn[6];
+		const u8 *gtk;
+
+		u8 igtkidx;
+		u8 igtk_len;
+		const u8 *igtk;
+		u8 ipn[6];
+
+		u8 bigtkidx;
+		const u8 *bigtk;
+		u8 bipn[6];
+	} links[MAX_NUM_MLD_LINKS];
+};
+
 struct wpa_auth_callbacks {
 	void (*logger)(void *ctx, const u8 *addr, logger_level level,
 		       const char *txt);
@@ -355,6 +390,9 @@ struct wpa_auth_callbacks {
 	int (*set_ltf_keyseed)(void *ctx, const u8 *addr, const u8 *ltf_keyseed,
 			       size_t ltf_keyseed_len);
 #endif /* CONFIG_PASN */
+#ifdef CONFIG_IEEE80211BE
+	int (*get_ml_rsn_info)(void *ctx, struct wpa_auth_ml_rsn_info *info);
+#endif /* CONFIG_IEEE80211BE */
 };
 
 struct wpa_authenticator * wpa_init(const u8 *addr,
@@ -593,4 +631,10 @@ void wpa_auth_set_ocv_override_freq(struct wpa_authenticator *wpa_auth,
 
 void wpa_auth_sta_radius_psk_resp(struct wpa_state_machine *sm, bool success);
 
+void wpa_auth_set_ml_info(struct wpa_state_machine *sm,
+			  const u8 *mld_addr,
+			  u8 mld_assoc_link_id,
+			  struct mld_info *info);
+void wpa_auth_ml_get_rsn_info(struct wpa_authenticator *a,
+			      struct wpa_auth_ml_link_rsn_info *info);
 #endif /* WPA_AUTH_H */
diff --git a/src/ap/wpa_auth_glue.c b/src/ap/wpa_auth_glue.c
index 8517f6954a..236f2f6001 100644
--- a/src/ap/wpa_auth_glue.c
+++ b/src/ap/wpa_auth_glue.c
@@ -1496,6 +1496,49 @@ static int hostapd_set_ltf_keyseed(void *ctx, const u8 *peer_addr,
 }
 #endif /* CONFIG_PASN */
 
+#ifdef CONFIG_IEEE80211BE
+
+static int hostapd_wpa_auth_get_ml_rsn_info(void *ctx,
+					    struct wpa_auth_ml_rsn_info *info)
+{
+	struct hostapd_data *hapd = ctx;
+	u8 i, j;
+
+	wpa_printf(MSG_DEBUG, "WPA_AUTH: MLD: get RSN info CB: n_mld_links=%u",
+		   info->n_mld_links);
+
+	if (!hapd->conf->mld_ap || !hapd->iface || !hapd->iface->interfaces)
+		return -1;
+
+	for (i = 0; i < info->n_mld_links; i++) {
+		u8 link_id = info->links[i].link_id;
+
+		wpa_printf(MSG_DEBUG,
+			   "WPA_AUTH: MLD: get link RSN CB: link_id=%u",
+			   link_id);
+
+		for (j = 0; j < hapd->iface->interfaces->count; j++) {
+			struct hostapd_iface *iface =
+				hapd->iface->interfaces->iface[j];
+
+			if (!iface->bss[0]->conf->mld_ap ||
+			    hapd->conf->mld_id != iface->bss[0]->conf->mld_id ||
+			    link_id != iface->bss[0]->mld_link_id)
+				continue;
+
+			wpa_auth_ml_get_rsn_info(iface->bss[0]->wpa_auth,
+						 &info->links[i]);
+			break;
+		}
+
+		if (j == hapd->iface->interfaces->count)
+			wpa_printf(MSG_DEBUG,
+				   "WPA_AUTH: MLD: link=%u not found", link_id);
+	}
+
+	return 0;
+}
+#endif /* CONFIG_IEEE80211BE */
 
 int hostapd_setup_wpa(struct hostapd_data *hapd)
 {
@@ -1546,6 +1589,9 @@ int hostapd_setup_wpa(struct hostapd_data *hapd)
 #ifdef CONFIG_PASN
 		.set_ltf_keyseed = hostapd_set_ltf_keyseed,
 #endif /* CONFIG_PASN */
+#ifdef CONFIG_IEEE80211BE
+		.get_ml_rsn_info = hostapd_wpa_auth_get_ml_rsn_info,
+#endif /* CONFIG_IEEE80211BE */
 	};
 	const u8 *wpa_ie;
 	size_t wpa_ie_len;
diff --git a/src/ap/wpa_auth_i.h b/src/ap/wpa_auth_i.h
index d401550c7a..e0f2d6fa4b 100644
--- a/src/ap/wpa_auth_i.h
+++ b/src/ap/wpa_auth_i.h
@@ -172,6 +172,22 @@ struct wpa_state_machine {
 	void *eapol_status_cb_ctx1;
 	void *eapol_status_cb_ctx2;
 #endif /* CONFIG_TESTING_OPTIONS */
+
+	u8 own_mld_addr[ETH_ALEN];
+	u8 peer_mld_addr[ETH_ALEN];
+	s8 mld_assoc_link_id;
+	u8 n_mld_affiliated_links;
+
+	struct mld_link {
+		bool valid;
+		u8 peer_addr[ETH_ALEN];
+		u8 own_addr[ETH_ALEN];
+
+		const u8 *rsne;
+		const u8 *rsnxe;
+		u8 rsne_len;
+		u8 rsnxe_len;
+	} mld_links[MAX_NUM_MLD_LINKS];
 };
 
 
