[RFC PATCH v1 03/13] FT: Include MLO link data in MIC calculation

Jeff Hansen x at jeffhansen.com
Fri Sep 11 12:20:01 PDT 2026


Cover the non-AP MLD link addresses in FT request MICs. Reconstruct the
per-link RSNE and RSNXE sequence and cover the AP link BSSIDs when
validating FT reassociation response MICs.

Signed-off-by: Jeff Hansen <x at jeffhansen.com>
---
 src/rsn_supp/wpa_ft.c | 96 ++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 90 insertions(+), 6 deletions(-)

diff --git a/src/rsn_supp/wpa_ft.c b/src/rsn_supp/wpa_ft.c
index fb46fa2f4..b9d151736 100644
--- a/src/rsn_supp/wpa_ft.c
+++ b/src/rsn_supp/wpa_ft.c
@@ -220,6 +220,13 @@ static u8 * wpa_ft_gen_req_ies(struct wpa_sm *sm, size_t *len,
 	int rsnxe_used;
 	int res;
 	u8 mic_control;
+	struct wpabuf *extra = NULL;
+	u16 mlo_links = 0;
+
+#ifdef CONFIG_IEEE80211BE
+	mlo_links = sm->mlo.req_links ? sm->mlo.req_links :
+		sm->mlo.valid_links;
+#endif /* CONFIG_IEEE80211BE */
 
 	sm->ft_completed = 0;
 	sm->ft_reassoc_completed = 0;
@@ -469,18 +476,36 @@ static u8 * wpa_ft_gen_req_ies(struct wpa_sm *sm, size_t *len,
 		*elem_count = 3 + ieee802_11_ie_count(ric_ies, ric_ies_len);
 		if (rsnxe_len)
 			*elem_count += 1;
+#ifdef CONFIG_IEEE80211BE
+		if (mlo_links) {
+			int link_id;
+
+			extra = wpabuf_alloc(MAX_NUM_MLD_LINKS * ETH_ALEN);
+			if (!extra) {
+				os_free(buf);
+				return NULL;
+			}
+
+			for_each_link(mlo_links, link_id)
+				wpabuf_put_data(extra,
+						 sm->mlo.links[link_id].addr,
+						 ETH_ALEN);
+		}
+#endif /* CONFIG_IEEE80211BE */
 		if (wpa_ft_mic(sm->key_mgmt, kck, kck_len,
 			       sm->own_addr, target_ap, 5,
 			       ((u8 *) mdie) - 2, 2 + sizeof(*mdie),
 			       ftie_pos, 2 + *ftie_len,
 			       (u8 *) rsnie, 2 + rsnie->len, ric_ies,
 			       ric_ies_len, rsnxe_len ? rsnxe : NULL, rsnxe_len,
-			       NULL,
+			       extra,
 			       fte_mic) < 0) {
 			wpa_printf(MSG_INFO, "FT: Failed to calculate MIC");
+			wpabuf_free(extra);
 			os_free(buf);
 			return NULL;
 		}
+		wpabuf_free(extra);
 	}
 
 	*len = pos - buf;
@@ -1036,6 +1061,13 @@ int wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies,
 	int own_rsnxe_used;
 	size_t mic_len;
 	int ret = -1;
+	struct wpabuf *extra = NULL, *rsne = NULL, *rsnxe = NULL;
+	u16 mlo_links = 0;
+
+#ifdef CONFIG_IEEE80211BE
+	mlo_links = sm->mlo.req_links ? sm->mlo.req_links :
+		sm->mlo.valid_links;
+#endif /* CONFIG_IEEE80211BE */
 
 	os_memset(&parse, 0, sizeof(parse));
 
@@ -1138,9 +1170,54 @@ int wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies,
 	}
 
 	count = 3;
+#ifdef CONFIG_IEEE80211BE
+	if (mlo_links) {
+		int link_id;
+
+		count = 2; /* MDE and FTE */
+		extra = wpabuf_alloc(MAX_NUM_MLD_LINKS * ETH_ALEN);
+		rsne = wpabuf_alloc(MAX_NUM_MLD_LINKS *
+				    (2 + 255 + 2 + PMKID_LEN));
+		rsnxe = wpabuf_alloc(MAX_NUM_MLD_LINKS * 257);
+		if (!extra || !rsne || !rsnxe)
+			goto fail;
+
+		for_each_link(mlo_links, link_id) {
+			struct wpa_sm_link *link = &sm->mlo.links[link_id];
+			u8 rsne_buf[2 + 255 + 2 + PMKID_LEN];
+			size_t rsne_len = link->ap_rsne_len;
+
+			if (!link->ap_rsne || !rsne_len ||
+			    rsne_len > 2 + 255) {
+				wpa_printf(MSG_DEBUG,
+					   "FT: Invalid AP RSNE for MLO link %d",
+					   link_id);
+				goto fail;
+			}
+
+			os_memcpy(rsne_buf, link->ap_rsne, rsne_len);
+			if (wpa_insert_pmkid(rsne_buf, &rsne_len,
+					     sm->pmk_r1_name, true) < 0) {
+				wpa_printf(MSG_DEBUG,
+					   "FT: Failed to add PMKR1Name for MLO link %d",
+					   link_id);
+				goto fail;
+			}
+			wpabuf_put_data(rsne, rsne_buf, rsne_len);
+			count++;
+
+			if (link->ap_rsnxe && link->ap_rsnxe_len) {
+				wpabuf_put_data(rsnxe, link->ap_rsnxe,
+						 link->ap_rsnxe_len);
+				count++;
+			}
+			wpabuf_put_data(extra, link->bssid, ETH_ALEN);
+		}
+	}
+#endif /* CONFIG_IEEE80211BE */
 	if (parse.ric)
 		count += ieee802_11_ie_count(parse.ric, parse.ric_len);
-	if (parse.rsnxe)
+	if (parse.rsnxe && !mlo_links)
 		count++;
 	if (parse.fte_elem_count != count) {
 		wpa_printf(MSG_DEBUG, "FT: Unexpected IE count in MIC "
@@ -1160,11 +1237,15 @@ int wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies,
 	if (wpa_ft_mic(sm->key_mgmt, kck, kck_len, sm->own_addr, src_addr, 6,
 		       parse.mdie - 2, parse.mdie_len + 2,
 		       parse.ftie - 2, parse.ftie_len + 2,
-		       parse.rsn - 2, parse.rsn_len + 2,
+		       mlo_links ? wpabuf_head(rsne) : parse.rsn - 2,
+		       mlo_links ? wpabuf_len(rsne) : parse.rsn_len + 2,
 		       parse.ric, parse.ric_len,
-		       parse.rsnxe ? parse.rsnxe - 2 : NULL,
-		       parse.rsnxe ? parse.rsnxe_len + 2 : 0,
-		       NULL,
+		       mlo_links ?
+		       (wpabuf_len(rsnxe) ? wpabuf_head(rsnxe) : NULL) :
+		       (parse.rsnxe ? parse.rsnxe - 2 : NULL),
+		       mlo_links ? wpabuf_len(rsnxe) :
+		       (parse.rsnxe ? parse.rsnxe_len + 2 : 0),
+		       extra,
 		       mic) < 0) {
 		wpa_printf(MSG_DEBUG, "FT: Failed to calculate MIC");
 		goto fail;
@@ -1278,6 +1359,9 @@ int wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies,
 
 	ret = 0;
 fail:
+	wpabuf_free(extra);
+	wpabuf_free(rsne);
+	wpabuf_free(rsnxe);
 	wpa_ft_parse_ies_free(&parse);
 	return ret;
 }
-- 
2.53.0




More information about the Hostap mailing list