diff mbox series

[03/12] MLD STA: Add support to fetch per-link beacon WPA/RSN/RSNX IEs into wpa_sm

Message ID 20220825055311.3327147-4-quic_vjakkam@quicinc.com
State Changes Requested
Headers show
Series MLD STA: Add support for four-way handshake and SAE external authentication | expand

Commit Message

Veerendranath Jakkam Aug. 25, 2022, 5:53 a.m. UTC
wpa_sm needs per-link beacon RSN and RSNX IEs for MLO KDE validation.
Thus, Add required APIs to parse and set AP link WPA/RSN/RSNX IEs to
wpa_sm.

Signed-off-by: Veerendranath Jakkam <quic_vjakkam@quicinc.com>
---
 src/rsn_supp/wpa.c                            | 103 +++++++++++++-----
 src/rsn_supp/wpa.h                            |  22 ++--
 src/rsn_supp/wpa_i.h                          |   8 ++
 tests/fuzzing/eapol-key-supp/eapol-key-supp.c |   2 +-
 wpa_supplicant/events.c                       |  66 ++++++++---
 wpa_supplicant/ibss_rsn.c                     |   4 +-
 wpa_supplicant/wpa_supplicant.c               |  19 ++--
 wpa_supplicant/wpas_glue.c                    | 103 +++++++++++++++---
 8 files changed, 247 insertions(+), 80 deletions(-)

Comments

Peer, Ilan Sept. 13, 2022, 7:22 a.m. UTC | #1
Hi,


> -----Original Message-----
> From: Hostap <hostap-bounces@lists.infradead.org> On Behalf Of
> Veerendranath Jakkam
> Sent: Thursday, August 25, 2022 08:53
> To: hostap@lists.infradead.org
> Cc: quic_vjakkam@quicinc.com
> Subject: [PATCH 03/12] MLD STA: Add support to fetch per-link beacon
> WPA/RSN/RSNX IEs into wpa_sm
> 
> wpa_sm needs per-link beacon RSN and RSNX IEs for MLO KDE validation.
> Thus, Add required APIs to parse and set AP link WPA/RSN/RSNX IEs to
> wpa_sm.
> 

As noted in the previous patch, I think that the RSN/RSNXE elements can be configured during the ML information setup
to the WPA SM, as they should be already be known at this stage (as before the connection setup they should already be
known to derive the common AKM suite, cipher suites etc.).

Regards,

Ilan.

> Signed-off-by: Veerendranath Jakkam <quic_vjakkam@quicinc.com>
> ---
>  src/rsn_supp/wpa.c                            | 103 +++++++++++++-----
>  src/rsn_supp/wpa.h                            |  22 ++--
>  src/rsn_supp/wpa_i.h                          |   8 ++
>  tests/fuzzing/eapol-key-supp/eapol-key-supp.c |   2 +-
>  wpa_supplicant/events.c                       |  66 ++++++++---
>  wpa_supplicant/ibss_rsn.c                     |   4 +-
>  wpa_supplicant/wpa_supplicant.c               |  19 ++--
>  wpa_supplicant/wpas_glue.c                    | 103 +++++++++++++++---
>  8 files changed, 247 insertions(+), 80 deletions(-)
> 
> diff --git a/src/rsn_supp/wpa.c b/src/rsn_supp/wpa.c index
> a28d49225..f3965ca50 100644
> --- a/src/rsn_supp/wpa.c
> +++ b/src/rsn_supp/wpa.c
> @@ -3716,6 +3716,7 @@ int wpa_sm_set_assoc_rsnxe(struct wpa_sm *sm,
> const u8 *ie, size_t len)
>  /**
>   * wpa_sm_set_ap_wpa_ie - Set AP WPA IE from Beacon/ProbeResp
>   * @sm: Pointer to WPA state machine data from wpa_sm_init()
> + * @link_id: MLO link ID to set specific link or -1 to set default
>   * @ie: Pointer to IE data (starting from id)
>   * @len: IE length
>   * Returns: 0 on success, -1 on failure @@ -3723,24 +3724,40 @@ int
> wpa_sm_set_assoc_rsnxe(struct wpa_sm *sm, const u8 *ie, size_t len)
>   * Inform WPA state machine about the WPA IE used in Beacon / Probe
> Response
>   * frame.
>   */
> -int wpa_sm_set_ap_wpa_ie(struct wpa_sm *sm, const u8 *ie, size_t len)
> +int wpa_sm_set_ap_wpa_ie(struct wpa_sm *sm, int link_id, const u8 *ie,
> +			 size_t len)

Not sure it is valid to have WPA elements with MLD. If it is indeed not needed, this change is not needed.

Regards,

Ilan.
diff mbox series

Patch

diff --git a/src/rsn_supp/wpa.c b/src/rsn_supp/wpa.c
index a28d49225..f3965ca50 100644
--- a/src/rsn_supp/wpa.c
+++ b/src/rsn_supp/wpa.c
@@ -3716,6 +3716,7 @@  int wpa_sm_set_assoc_rsnxe(struct wpa_sm *sm, const u8 *ie, size_t len)
 /**
  * wpa_sm_set_ap_wpa_ie - Set AP WPA IE from Beacon/ProbeResp
  * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @link_id: MLO link ID to set specific link or -1 to set default
  * @ie: Pointer to IE data (starting from id)
  * @len: IE length
  * Returns: 0 on success, -1 on failure
@@ -3723,24 +3724,40 @@  int wpa_sm_set_assoc_rsnxe(struct wpa_sm *sm, const u8 *ie, size_t len)
  * Inform WPA state machine about the WPA IE used in Beacon / Probe Response
  * frame.
  */
-int wpa_sm_set_ap_wpa_ie(struct wpa_sm *sm, const u8 *ie, size_t len)
+int wpa_sm_set_ap_wpa_ie(struct wpa_sm *sm, int link_id, const u8 *ie,
+			 size_t len)
 {
-	if (sm == NULL)
+	u8 **ap_wpa_ie;
+	size_t *ap_wpa_ie_len;
+
+	if (!sm)
 		return -1;
 
-	os_free(sm->ap_wpa_ie);
-	if (ie == NULL || len == 0) {
+	if (link_id == -1) {
+		ap_wpa_ie = &sm->ap_wpa_ie;
+		ap_wpa_ie_len = &sm->ap_wpa_ie_len;
+	} else {
+		if (link_id < 0 || link_id >= MAX_NUM_MLD_LINKS)
+			return -1;
+
 		wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
-			"WPA: clearing AP WPA IE");
-		sm->ap_wpa_ie = NULL;
-		sm->ap_wpa_ie_len = 0;
+			"WPA: set AP WPA IE for link ID %d", link_id);
+		ap_wpa_ie = &sm->links[link_id].ap_wpa_ie;
+		ap_wpa_ie_len = &sm->links[link_id].ap_wpa_ie_len;
+	}
+
+	os_free(*ap_wpa_ie);
+	if (ie == NULL || len == 0) {
+		wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: clearing AP WPA IE");
+		*ap_wpa_ie = NULL;
+		*ap_wpa_ie_len = 0;
 	} else {
 		wpa_hexdump(MSG_DEBUG, "WPA: set AP WPA IE", ie, len);
-		sm->ap_wpa_ie = os_memdup(ie, len);
-		if (sm->ap_wpa_ie == NULL)
+		*ap_wpa_ie = os_memdup(ie, len);
+		if (*ap_wpa_ie == NULL)
 			return -1;
 
-		sm->ap_wpa_ie_len = len;
+		*ap_wpa_ie_len = len;
 	}
 
 	return 0;
@@ -3750,6 +3767,7 @@  int wpa_sm_set_ap_wpa_ie(struct wpa_sm *sm, const u8 *ie, size_t len)
 /**
  * wpa_sm_set_ap_rsn_ie - Set AP RSN IE from Beacon/ProbeResp
  * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @link_id: MLO link ID to set specific link or -1 to set default
  * @ie: Pointer to IE data (starting from id)
  * @len: IE length
  * Returns: 0 on success, -1 on failure
@@ -3757,24 +3775,41 @@  int wpa_sm_set_ap_wpa_ie(struct wpa_sm *sm, const u8 *ie, size_t len)
  * Inform WPA state machine about the RSN IE used in Beacon / Probe Response
  * frame.
  */
-int wpa_sm_set_ap_rsn_ie(struct wpa_sm *sm, const u8 *ie, size_t len)
+int wpa_sm_set_ap_rsn_ie(struct wpa_sm *sm, int link_id, const u8 *ie,
+			 size_t len)
 {
-	if (sm == NULL)
+	u8 **ap_rsn_ie;
+	size_t *ap_rsn_ie_len;
+
+	if (!sm)
 		return -1;
 
-	os_free(sm->ap_rsn_ie);
+	if (link_id == -1) {
+		ap_rsn_ie = &sm->ap_rsn_ie;
+		ap_rsn_ie_len = &sm->ap_rsn_ie_len;
+	} else {
+		if (link_id < 0 || link_id >= MAX_NUM_MLD_LINKS)
+			return -1;
+
+		wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+			"WPA: set AP RSN IE for link ID %d", link_id);
+		ap_rsn_ie = &sm->links[link_id].ap_rsn_ie;
+		ap_rsn_ie_len = &sm->links[link_id].ap_rsn_ie_len;
+	}
+
+	os_free(*ap_rsn_ie);
 	if (ie == NULL || len == 0) {
 		wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
 			"WPA: clearing AP RSN IE");
-		sm->ap_rsn_ie = NULL;
-		sm->ap_rsn_ie_len = 0;
+		*ap_rsn_ie = NULL;
+		*ap_rsn_ie_len = 0;
 	} else {
 		wpa_hexdump(MSG_DEBUG, "WPA: set AP RSN IE", ie, len);
-		sm->ap_rsn_ie = os_memdup(ie, len);
-		if (sm->ap_rsn_ie == NULL)
+		*ap_rsn_ie = os_memdup(ie, len);
+		if (*ap_rsn_ie == NULL)
 			return -1;
 
-		sm->ap_rsn_ie_len = len;
+		*ap_rsn_ie_len = len;
 	}
 
 	return 0;
@@ -3784,6 +3819,7 @@  int wpa_sm_set_ap_rsn_ie(struct wpa_sm *sm, const u8 *ie, size_t len)
 /**
  * wpa_sm_set_ap_rsnxe - Set AP RSNXE from Beacon/ProbeResp
  * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @link_id: MLO link ID to set specific link or -1 to set default
  * @ie: Pointer to IE data (starting from id)
  * @len: IE length
  * Returns: 0 on success, -1 on failure
@@ -3791,23 +3827,40 @@  int wpa_sm_set_ap_rsn_ie(struct wpa_sm *sm, const u8 *ie, size_t len)
  * Inform WPA state machine about the RSNXE used in Beacon / Probe Response
  * frame.
  */
-int wpa_sm_set_ap_rsnxe(struct wpa_sm *sm, const u8 *ie, size_t len)
+int wpa_sm_set_ap_rsnxe(struct wpa_sm *sm, int link_id, const u8 *ie,
+			size_t len)
 {
+	u8 **ap_rsnxe;
+	size_t *ap_rsnxe_len;
+
 	if (!sm)
 		return -1;
 
-	os_free(sm->ap_rsnxe);
+	if (link_id == -1) {
+		ap_rsnxe = &sm->ap_rsnxe;
+		ap_rsnxe_len = &sm->ap_rsnxe_len;
+	} else {
+		if (link_id < 0 || link_id >= MAX_NUM_MLD_LINKS)
+			return -1;
+
+		wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+			"WPA: set AP RSNXE IE for link ID %d", link_id);
+		ap_rsnxe = &sm->links[link_id].ap_rsnxe;
+		ap_rsnxe_len = &sm->links[link_id].ap_rsnxe_len;
+	}
+
+	os_free(*ap_rsnxe);
 	if (!ie || len == 0) {
 		wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: clearing AP RSNXE");
-		sm->ap_rsnxe = NULL;
-		sm->ap_rsnxe_len = 0;
+		*ap_rsnxe = NULL;
+		*ap_rsnxe_len = 0;
 	} else {
 		wpa_hexdump(MSG_DEBUG, "WPA: set AP RSNXE", ie, len);
-		sm->ap_rsnxe = os_memdup(ie, len);
-		if (!sm->ap_rsnxe)
+		*ap_rsnxe = os_memdup(ie, len);
+		if (*ap_rsnxe == NULL)
 			return -1;
 
-		sm->ap_rsnxe_len = len;
+		*ap_rsnxe_len = len;
 	}
 
 	return 0;
diff --git a/src/rsn_supp/wpa.h b/src/rsn_supp/wpa.h
index e6eef0c99..a56802b0a 100644
--- a/src/rsn_supp/wpa.h
+++ b/src/rsn_supp/wpa.h
@@ -93,6 +93,7 @@  struct wpa_sm_ctx {
 	void (*transition_disable)(void *ctx, u8 bitmap);
 	void (*store_ptk)(void *ctx, u8 *addr, int cipher,
 			  u32 life_time, const struct wpa_ptk *ptk);
+	int (*get_link_beacon_ie)(void *ctx, u8 link_id);
 };
 
 
@@ -165,9 +166,12 @@  int wpa_sm_set_assoc_wpa_ie_default(struct wpa_sm *sm, u8 *wpa_ie,
 int wpa_sm_set_assoc_rsnxe_default(struct wpa_sm *sm, u8 *rsnxe,
 				   size_t *rsnxe_len);
 int wpa_sm_set_assoc_rsnxe(struct wpa_sm *sm, const u8 *ie, size_t len);
-int wpa_sm_set_ap_wpa_ie(struct wpa_sm *sm, const u8 *ie, size_t len);
-int wpa_sm_set_ap_rsn_ie(struct wpa_sm *sm, const u8 *ie, size_t len);
-int wpa_sm_set_ap_rsnxe(struct wpa_sm *sm, const u8 *ie, size_t len);
+int wpa_sm_set_ap_wpa_ie(struct wpa_sm *sm, int link_id, const u8 *ie,
+			 size_t len);
+int wpa_sm_set_ap_rsn_ie(struct wpa_sm *sm, int link_id, const u8 *ie,
+			 size_t len);
+int wpa_sm_set_ap_rsnxe(struct wpa_sm *sm, int link_id, const u8 *ie,
+			size_t len);
 int wpa_sm_get_mib(struct wpa_sm *sm, char *buf, size_t buflen);
 
 int wpa_sm_set_param(struct wpa_sm *sm, enum wpa_sm_conf_params param,
@@ -299,20 +303,20 @@  static inline int wpa_sm_set_assoc_wpa_ie_default(struct wpa_sm *sm,
 	return -1;
 }
 
-static inline int wpa_sm_set_ap_wpa_ie(struct wpa_sm *sm, const u8 *ie,
-				       size_t len)
+static inline int wpa_sm_set_ap_wpa_ie(struct wpa_sm *sm, int link_id,
+				       const u8 *ie, size_t len)
 {
 	return -1;
 }
 
-static inline int wpa_sm_set_ap_rsn_ie(struct wpa_sm *sm, const u8 *ie,
-				       size_t len)
+static inline int wpa_sm_set_ap_rsn_ie(struct wpa_sm *sm, int link_id,
+				       const u8 *ie, size_t len)
 {
 	return -1;
 }
 
-static inline int wpa_sm_set_ap_rsnxe(struct wpa_sm *sm, const u8 *ie,
-				      size_t len)
+static inline int wpa_sm_set_ap_rsnxe(struct wpa_sm *sm, int link_id,
+				      const u8 *ie, size_t len)
 {
 	return -1;
 }
diff --git a/src/rsn_supp/wpa_i.h b/src/rsn_supp/wpa_i.h
index 62b85cb2e..8bf0f28d8 100644
--- a/src/rsn_supp/wpa_i.h
+++ b/src/rsn_supp/wpa_i.h
@@ -224,6 +224,8 @@  struct wpa_sm {
 	struct {
 		u8 addr[ETH_ALEN];
 		u8 bssid[ETH_ALEN];
+		u8 *ap_wpa_ie, *ap_rsn_ie, *ap_rsnxe;
+		size_t ap_wpa_ie_len, ap_rsn_ie_len, ap_rsnxe_len;
 	} links[MAX_NUM_MLD_LINKS];
 };
 
@@ -288,6 +290,12 @@  static inline int wpa_sm_get_beacon_ie(struct wpa_sm *sm)
 	return sm->ctx->get_beacon_ie(sm->ctx->ctx);
 }
 
+static inline int wpa_sm_get_link_beacon_ie(struct wpa_sm *sm, u8 link_id)
+{
+	WPA_ASSERT(sm->ctx->get_link_beacon_ie);
+	return sm->ctx->get_link_beacon_ie(sm->ctx->ctx, link_id);
+}
+
 static inline void wpa_sm_cancel_auth_timeout(struct wpa_sm *sm)
 {
 	WPA_ASSERT(sm->ctx->cancel_auth_timeout);
diff --git a/tests/fuzzing/eapol-key-supp/eapol-key-supp.c b/tests/fuzzing/eapol-key-supp/eapol-key-supp.c
index 0c7189571..a86efd376 100644
--- a/tests/fuzzing/eapol-key-supp/eapol-key-supp.c
+++ b/tests/fuzzing/eapol-key-supp/eapol-key-supp.c
@@ -168,7 +168,7 @@  static int supp_get_beacon_ie(void *ctx)
 	ie = wpa->wpa1 ? wpaie : rsne;
 	if (ie[0] == WLAN_EID_RSN)
 		return wpa_sm_set_ap_rsn_ie(wpa->supp, ie, 2 + ie[1]);
-	return wpa_sm_set_ap_wpa_ie(wpa->supp, ie, 2 + ie[1]);
+	return wpa_sm_set_ap_wpa_ie(wpa->supp, -1, ie, 2 + ie[1]);
 }
 
 
diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c
index db4de316f..5f13d4674 100644
--- a/wpa_supplicant/events.c
+++ b/wpa_supplicant/events.c
@@ -3223,27 +3223,27 @@  no_pfs:
 		    p[0] == WLAN_EID_VENDOR_SPECIFIC && p[1] >= 6 &&
 		    os_memcmp(&p[2], "\x00\x50\xF2\x01\x01\x00", 6) == 0) {
 			wpa_found = 1;
-			wpa_sm_set_ap_wpa_ie(wpa_s->wpa, p, len);
+			wpa_sm_set_ap_wpa_ie(wpa_s->wpa, -1, p, len);
 		}
 
 		if (!rsn_found &&
 		    p[0] == WLAN_EID_RSN && p[1] >= 2) {
 			rsn_found = 1;
-			wpa_sm_set_ap_rsn_ie(wpa_s->wpa, p, len);
+			wpa_sm_set_ap_rsn_ie(wpa_s->wpa, -1, p, len);
 		}
 
 		if (p[0] == WLAN_EID_RSNX && p[1] >= 1)
-			wpa_sm_set_ap_rsnxe(wpa_s->wpa, p, len);
+			wpa_sm_set_ap_rsnxe(wpa_s->wpa, -1, p, len);
 
 		l -= len;
 		p += len;
 	}
 
 	if (!wpa_found && data->assoc_info.beacon_ies)
-		wpa_sm_set_ap_wpa_ie(wpa_s->wpa, NULL, 0);
+		wpa_sm_set_ap_wpa_ie(wpa_s->wpa, -1, NULL, 0);
 	if (!rsn_found && data->assoc_info.beacon_ies) {
-		wpa_sm_set_ap_rsn_ie(wpa_s->wpa, NULL, 0);
-		wpa_sm_set_ap_rsnxe(wpa_s->wpa, NULL, 0);
+		wpa_sm_set_ap_rsn_ie(wpa_s->wpa, -1, NULL, 0);
+		wpa_sm_set_ap_rsnxe(wpa_s->wpa, -1, NULL, 0);
 	}
 	if (wpa_found || rsn_found)
 		wpa_s->ap_ies_from_associnfo = 1;
@@ -3265,26 +3265,23 @@  no_pfs:
 }
 
 
-static int wpa_supplicant_assoc_update_ie(struct wpa_supplicant *wpa_s)
+static int wpa_supplicant_update_link_ie(struct wpa_supplicant *wpa_s,
+					 int link_id, const struct wpa_bss *bss)
 {
 	const u8 *bss_wpa = NULL, *bss_rsn = NULL, *bss_rsnx = NULL;
 
-	if (!wpa_s->current_bss || !wpa_s->current_ssid)
+	if (!bss)
 		return -1;
 
-	if (!wpa_key_mgmt_wpa_any(wpa_s->current_ssid->key_mgmt))
-		return 0;
-
-	bss_wpa = wpa_bss_get_vendor_ie(wpa_s->current_bss,
-					WPA_IE_VENDOR_TYPE);
-	bss_rsn = wpa_bss_get_ie(wpa_s->current_bss, WLAN_EID_RSN);
-	bss_rsnx = wpa_bss_get_ie(wpa_s->current_bss, WLAN_EID_RSNX);
+	bss_wpa = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE);
+	bss_rsn = wpa_bss_get_ie(bss, WLAN_EID_RSN);
+	bss_rsnx = wpa_bss_get_ie(bss, WLAN_EID_RSNX);
 
-	if (wpa_sm_set_ap_wpa_ie(wpa_s->wpa, bss_wpa,
+	if (wpa_sm_set_ap_wpa_ie(wpa_s->wpa, link_id, bss_wpa,
 				 bss_wpa ? 2 + bss_wpa[1] : 0) ||
-	    wpa_sm_set_ap_rsn_ie(wpa_s->wpa, bss_rsn,
+	    wpa_sm_set_ap_rsn_ie(wpa_s->wpa, link_id, bss_rsn,
 				 bss_rsn ? 2 + bss_rsn[1] : 0) ||
-	    wpa_sm_set_ap_rsnxe(wpa_s->wpa, bss_rsnx,
+	    wpa_sm_set_ap_rsnxe(wpa_s->wpa, link_id, bss_rsnx,
 				 bss_rsnx ? 2 + bss_rsnx[1] : 0))
 		return -1;
 
@@ -3292,6 +3289,39 @@  static int wpa_supplicant_assoc_update_ie(struct wpa_supplicant *wpa_s)
 }
 
 
+static int wpa_supplicant_assoc_update_ie(struct wpa_supplicant *wpa_s)
+{
+	int i;
+
+	if (!wpa_s->current_bss || !wpa_s->current_ssid)
+		return -1;
+
+	if (!wpa_key_mgmt_wpa_any(wpa_s->current_ssid->key_mgmt))
+		return 0;
+
+	if (wpa_supplicant_update_link_ie(wpa_s, -1, wpa_s->current_bss))
+		return -1;
+
+	if (!wpa_s->valid_links)
+		return 0;
+
+	for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
+		if (!(wpa_s->valid_links & BIT(i))) {
+			wpa_sm_set_ap_wpa_ie(wpa_s->wpa, i, NULL, 0);
+			wpa_sm_set_ap_rsn_ie(wpa_s->wpa, i, NULL, 0);
+			wpa_sm_set_ap_rsnxe(wpa_s->wpa, i, NULL, 0);
+			continue;
+		}
+
+		if (wpa_supplicant_update_link_ie(wpa_s, i,
+						  wpa_s->links[i].bss))
+			return -1;
+	}
+
+	return 0;
+}
+
+
 static void wpas_fst_update_mb_assoc(struct wpa_supplicant *wpa_s,
 				     union wpa_event_data *data)
 {
diff --git a/wpa_supplicant/ibss_rsn.c b/wpa_supplicant/ibss_rsn.c
index 874c2bf1d..459d7bd2d 100644
--- a/wpa_supplicant/ibss_rsn.c
+++ b/wpa_supplicant/ibss_rsn.c
@@ -117,8 +117,8 @@  static int supp_get_beacon_ie(void *ctx)
 
 	wpa_printf(MSG_DEBUG, "SUPP: %s", __func__);
 	/* TODO: get correct RSN IE */
-	wpa_sm_set_ap_rsnxe(peer->supp, NULL, 0);
-	return wpa_sm_set_ap_rsn_ie(peer->supp,
+	wpa_sm_set_ap_rsnxe(peer->supp, -1, NULL, 0);
+	return wpa_sm_set_ap_rsn_ie(peer->supp, -1,
 				    (u8 *) "\x30\x14\x01\x00"
 				    "\x00\x0f\xac\x04"
 				    "\x01\x00\x00\x0f\xac\x04"
diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c
index ba364324d..b0087328c 100644
--- a/wpa_supplicant/wpa_supplicant.c
+++ b/wpa_supplicant/wpa_supplicant.c
@@ -400,9 +400,7 @@  void wpa_supplicant_initiate_eapol(struct wpa_supplicant *wpa_s)
 void wpa_supplicant_set_non_wpa_policy(struct wpa_supplicant *wpa_s,
 				       struct wpa_ssid *ssid)
 {
-#ifdef CONFIG_WEP
 	int i;
-#endif /* CONFIG_WEP */
 
 	if (ssid->key_mgmt & WPA_KEY_MGMT_WPS)
 		wpa_s->key_mgmt = WPA_KEY_MGMT_WPS;
@@ -410,9 +408,14 @@  void wpa_supplicant_set_non_wpa_policy(struct wpa_supplicant *wpa_s,
 		wpa_s->key_mgmt = WPA_KEY_MGMT_IEEE8021X_NO_WPA;
 	else
 		wpa_s->key_mgmt = WPA_KEY_MGMT_NONE;
-	wpa_sm_set_ap_wpa_ie(wpa_s->wpa, NULL, 0);
-	wpa_sm_set_ap_rsn_ie(wpa_s->wpa, NULL, 0);
-	wpa_sm_set_ap_rsnxe(wpa_s->wpa, NULL, 0);
+	wpa_sm_set_ap_wpa_ie(wpa_s->wpa, -1, NULL, 0);
+	wpa_sm_set_ap_rsn_ie(wpa_s->wpa, -1, NULL, 0);
+	wpa_sm_set_ap_rsnxe(wpa_s->wpa, -1, NULL, 0);
+	for (int i = 0; i < MAX_NUM_MLD_LINKS; i++) {
+		wpa_sm_set_ap_wpa_ie(wpa_s->wpa, i, NULL, 0);
+		wpa_sm_set_ap_rsn_ie(wpa_s->wpa, i, NULL, 0);
+		wpa_sm_set_ap_rsnxe(wpa_s->wpa, i, NULL, 0);
+	}
 	wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, NULL, 0);
 	wpa_sm_set_assoc_rsnxe(wpa_s->wpa, NULL, 0);
 	wpa_s->rsnxe_len = 0;
@@ -1505,11 +1508,11 @@  int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s,
 			 !!(ssid->proto & (WPA_PROTO_RSN | WPA_PROTO_OSEN)));
 
 	if (bss || !wpa_s->ap_ies_from_associnfo) {
-		if (wpa_sm_set_ap_wpa_ie(wpa_s->wpa, bss_wpa,
+		if (wpa_sm_set_ap_wpa_ie(wpa_s->wpa, -1, bss_wpa,
 					 bss_wpa ? 2 + bss_wpa[1] : 0) ||
-		    wpa_sm_set_ap_rsn_ie(wpa_s->wpa, bss_rsn,
+		    wpa_sm_set_ap_rsn_ie(wpa_s->wpa, -1, bss_rsn,
 					 bss_rsn ? 2 + bss_rsn[1] : 0) ||
-		    wpa_sm_set_ap_rsnxe(wpa_s->wpa, bss_rsnx,
+		    wpa_sm_set_ap_rsnxe(wpa_s->wpa, -1, bss_rsnx,
 					bss_rsnx ? 2 + bss_rsnx[1] : 0))
 			return -1;
 	}
diff --git a/wpa_supplicant/wpas_glue.c b/wpa_supplicant/wpas_glue.c
index ccc72c4d6..2784fb096 100644
--- a/wpa_supplicant/wpas_glue.c
+++ b/wpa_supplicant/wpas_glue.c
@@ -390,15 +390,21 @@  static void wpa_supplicant_notify_eapol_done(void *ctx)
 
 #ifndef CONFIG_NO_WPA
 
-static int wpa_get_beacon_ie(struct wpa_supplicant *wpa_s)
+struct beacon_ies {
+	const u8 *wpa_ie, *rsn_ie, *rsnxe;
+	size_t wpa_ie_len, rsn_ie_len, rsnxe_len;
+};
+
+
+static int wpa_get_bssid_beacon_ie(struct wpa_supplicant *wpa_s,
+				   const u8 *bssid, struct beacon_ies *ies)
 {
-	int ret = 0;
 	struct wpa_bss *curr = NULL, *bss;
 	struct wpa_ssid *ssid = wpa_s->current_ssid;
 	const u8 *ie;
 
 	dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
-		if (os_memcmp(bss->bssid, wpa_s->bssid, ETH_ALEN) != 0)
+		if (os_memcmp(bss->bssid, bssid, ETH_ALEN) != 0)
 			continue;
 		if (ssid == NULL ||
 		    ((bss->ssid_len == ssid->ssid_len &&
@@ -416,23 +422,42 @@  static int wpa_get_beacon_ie(struct wpa_supplicant *wpa_s)
 #endif /* CONFIG_OWE */
 	}
 
-	if (curr) {
-		ie = wpa_bss_get_vendor_ie(curr, WPA_IE_VENDOR_TYPE);
-		if (wpa_sm_set_ap_wpa_ie(wpa_s->wpa, ie, ie ? 2 + ie[1] : 0))
-			ret = -1;
+	if (!curr)
+		return -1;
 
-		ie = wpa_bss_get_ie(curr, WLAN_EID_RSN);
-		if (wpa_sm_set_ap_rsn_ie(wpa_s->wpa, ie, ie ? 2 + ie[1] : 0))
-			ret = -1;
+	ie = wpa_bss_get_vendor_ie(curr, WPA_IE_VENDOR_TYPE);
+	ies->wpa_ie = ie;
+	ies->wpa_ie_len = ie ? 2 + ie[1] : 0;
 
-		ie = wpa_bss_get_ie(curr, WLAN_EID_RSNX);
-		if (wpa_sm_set_ap_rsnxe(wpa_s->wpa, ie, ie ? 2 + ie[1] : 0))
-			ret = -1;
-	} else {
-		ret = -1;
-	}
+	ie = wpa_bss_get_ie(curr, WLAN_EID_RSN);
+	ies->rsn_ie = ie;
+	ies->rsn_ie_len = ie ? 2 + ie[1] : 0;
 
-	return ret;
+	ie = wpa_bss_get_ie(curr, WLAN_EID_RSNX);
+	ies->rsnxe = ie;
+	ies->rsnxe_len = ie ? 2 + ie[1] : 0;
+
+	return 0;
+}
+
+
+static int wpa_get_beacon_ie(struct wpa_supplicant *wpa_s)
+{
+	struct beacon_ies ies;
+
+	if (wpa_get_bssid_beacon_ie(wpa_s, wpa_s->bssid, &ies))
+		return -1;
+
+	if (wpa_sm_set_ap_wpa_ie(wpa_s->wpa, -1, ies.wpa_ie, ies.wpa_ie_len))
+		return -1;
+
+	if (wpa_sm_set_ap_rsn_ie(wpa_s->wpa, -1, ies.rsn_ie, ies.rsn_ie_len))
+		return -1;
+
+	if (wpa_sm_set_ap_rsnxe(wpa_s->wpa, -1, ies.rsnxe, ies.rsnxe_len))
+		return -1;
+
+	return 0;
 }
 
 
@@ -452,6 +477,49 @@  static int wpa_supplicant_get_beacon_ie(void *ctx)
 }
 
 
+static int wpa_get_link_beacon_ie(struct wpa_supplicant *wpa_s, u8 link_id)
+{
+	struct beacon_ies ies;
+
+	if (!(wpa_s->valid_links & BIT(link_id)))
+		return -1;
+
+
+	if (wpa_get_bssid_beacon_ie(wpa_s, wpa_s->links[link_id].bssid, &ies))
+		return -1;
+
+	if (wpa_sm_set_ap_wpa_ie(wpa_s->wpa, link_id, ies.wpa_ie,
+				 ies.wpa_ie_len))
+		return -1;
+
+	if (wpa_sm_set_ap_rsn_ie(wpa_s->wpa, link_id, ies.rsn_ie,
+				 ies.rsn_ie_len))
+		return -1;
+
+	if (wpa_sm_set_ap_rsnxe(wpa_s->wpa, link_id, ies.rsnxe, ies.rsnxe_len))
+		return -1;
+
+	return 0;
+}
+
+
+static int wpa_supplicant_get_link_beacon_ie(void *ctx, u8 link_id)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+	if (wpa_get_link_beacon_ie(wpa_s, link_id) == 0) {
+		return 0;
+	}
+
+	/* No WPA/RSN IE found in the cached scan results. Try to get updated
+	 * scan results from the driver. */
+	if (wpa_supplicant_update_scan_results(wpa_s) < 0)
+		return -1;
+
+	return wpa_get_link_beacon_ie(wpa_s, link_id);
+}
+
+
+
 static u8 * _wpa_alloc_eapol(void *wpa_s, u8 type,
 			     const void *data, u16 data_len,
 			     size_t *msg_len, void **data_pos)
@@ -1412,6 +1480,7 @@  int wpa_supplicant_init_wpa(struct wpa_supplicant *wpa_s)
 	ctx->get_bssid = wpa_supplicant_get_bssid;
 	ctx->ether_send = _wpa_ether_send;
 	ctx->get_beacon_ie = wpa_supplicant_get_beacon_ie;
+	ctx->get_link_beacon_ie = wpa_supplicant_get_link_beacon_ie;
 	ctx->alloc_eapol = _wpa_alloc_eapol;
 	ctx->cancel_auth_timeout = _wpa_supplicant_cancel_auth_timeout;
 	ctx->add_pmkid = wpa_supplicant_add_pmkid;