[PATCH v2] Pass more capabilities of MLO link to framework

Bruno.Kremp at sony.com Bruno.Kremp at sony.com
Fri Dec 19 02:25:46 PST 2025


Pass Channel Bandwidth and Maximum Number of Spatial Stream 
of MLO link to framework, the information is necessary to
calculate the theoretical maximum speed for MLO link.

Signed-off-by: Bruno Kremp <bruno.kremp at sony.com>
---
 src/common/ieee802_11_common.c    | 55 +++++++++++++++++++++++++++----
 src/common/ieee802_11_common.h    |  9 +++--
 wpa_supplicant/events.c           | 51 ++++++++++++++++++++++++++++
 wpa_supplicant/wpa_supplicant_i.h |  3 ++
 4 files changed, 108 insertions(+), 10 deletions(-)

diff --git a/src/common/ieee802_11_common.c b/src/common/ieee802_11_common.c
index 5d1e02f81..c0ae42591 100644
--- a/src/common/ieee802_11_common.c
+++ b/src/common/ieee802_11_common.c
@@ -1001,9 +1001,10 @@ void ieee802_11_elems_clear_ext_ids(struct ieee802_11_elems *elems,
 }
 
 
-ParseRes ieee802_11_parse_link_assoc_req(struct ieee802_11_elems *elems,
-					 struct wpabuf *mlbuf,
-					 u8 link_id, bool show_errors)
+static ParseRes ieee802_11_parse_link_profile(struct ieee802_11_elems *elems,
+					      struct wpabuf *mlbuf,
+					      u8 link_id, bool show_errors,
+					      bool is_assoc_resp)
 {
 	const struct ieee80211_eht_ml *ml;
 	const u8 *pos;
@@ -1042,7 +1043,8 @@ ParseRes ieee802_11_parse_link_assoc_req(struct ieee802_11_elems *elems,
 				   "MLD: Failed to parse MLE subelem");
 			goto out;
 		}
-
+		if ((size_t) num_frag_subelems * 2 > len)
+			goto out;
 		len -= num_frag_subelems * 2;
 
 		wpa_printf(MSG_DEBUG,
@@ -1101,6 +1103,26 @@ ParseRes ieee802_11_parse_link_assoc_req(struct ieee802_11_elems *elems,
 		pos += 2;
 		sub_elem_len -= 2;
 
+		/* For association response, check status code */
+		if (is_assoc_resp) {
+			if (sub_elem_len < 2) {
+				if (show_errors)
+					wpa_printf(MSG_DEBUG,
+						   "MLD: missing status code");
+				goto out;
+			}
+
+			le16 status_code = WPA_GET_LE16(pos);
+			if (le_to_host16(status_code) != WLAN_STATUS_SUCCESS) {
+				wpa_printf(MSG_DEBUG,
+					   "MLD: status code %u", status_code);
+				goto out;
+			}
+
+			pos += 2;
+			sub_elem_len -= 2;
+		}
+
 		/* Handle non-inheritance */
 		non_inherit = get_ie_ext(pos, sub_elem_len,
 					 WLAN_EID_EXT_NON_INHERITANCE);
@@ -1160,6 +1182,24 @@ out:
 }
 
 
+ParseRes ieee802_11_parse_link_assoc_req(struct ieee802_11_elems *elems,
+					 struct wpabuf *mlbuf,
+					 u8 link_id, bool show_errors)
+{
+	return ieee802_11_parse_link_profile(elems, mlbuf, link_id,
+					     show_errors, false);
+}
+
+ParseRes ieee802_11_parse_link_assoc_resp(struct ieee802_11_elems *elems,
+				  struct wpabuf *mlbuf,
+					  u8 link_id, bool show_errors)
+{
+	/* ieee802_11_defrag_mle_subelem() handles subelement defragmentation
+	 * in-place within mlbuf */
+	return ieee802_11_parse_link_profile(elems, mlbuf, link_id,
+					     show_errors, true);
+}
+
 int ieee802_11_ie_count(const u8 *ies, size_t ies_len)
 {
 	const struct element *elem;
@@ -3486,9 +3526,9 @@ struct wpabuf * ieee802_11_defrag(const u8 *data, size_t len, bool ext_elem)
  * of data, see IEEE Std 802.11be-2024, Figure 35-4 - Per-STA Profile subelement
  * fragmentation within a fragmented Multi-Link element.
  */
-ssize_t ieee802_11_defrag_mle_subelem(struct wpabuf *mlbuf,
-				      const u8 *parent_subelem,
-				      size_t *defrag_len)
+int ieee802_11_defrag_mle_subelem(struct wpabuf *mlbuf,
+				     const u8 *parent_subelem,
+				     size_t *defrag_len)
 {
 	u8 *buf, *pos, *end;
 	size_t len, subelem_len;
@@ -3524,6 +3564,7 @@ ssize_t ieee802_11_defrag_mle_subelem(struct wpabuf *mlbuf,
 			return -1;
 
 		os_memmove(pos, pos + 2, end - (pos + 2));
+		end -= 2;
 		pos += elen - 2;
 		subelem_len += elen - 2;
 
diff --git a/src/common/ieee802_11_common.h b/src/common/ieee802_11_common.h
index 09bd0675a..f63379a7f 100644
--- a/src/common/ieee802_11_common.h
+++ b/src/common/ieee802_11_common.h
@@ -217,6 +217,9 @@ void ieee802_11_elems_clear_ext_ids(struct ieee802_11_elems *elems,
 ParseRes ieee802_11_parse_link_assoc_req(struct ieee802_11_elems *elems,
 					 struct wpabuf *mlbuf,
 					 u8 link_id, bool show_errors);
+ParseRes ieee802_11_parse_link_assoc_resp(struct ieee802_11_elems *elems,
+					  struct wpabuf *mlbuf,
+					  u8 link_id, bool show_errors);
 int ieee802_11_ie_count(const u8 *ies, size_t ies_len);
 struct wpabuf * ieee802_11_vendor_ie_concat(const u8 *ies, size_t ies_len,
 					    u32 oui_type);
@@ -382,9 +385,9 @@ int ieee802_edmg_is_allowed(struct ieee80211_edmg_config allowed,
 			    struct ieee80211_edmg_config requested);
 
 struct wpabuf * ieee802_11_defrag(const u8 *data, size_t len, bool ext_elem);
-ssize_t ieee802_11_defrag_mle_subelem(struct wpabuf *mlbuf,
-				      const u8 *parent_subelem,
-				      size_t *defrag_len);
+int ieee802_11_defrag_mle_subelem(struct wpabuf *mlbuf,
+				     const u8 *parent_subelem,
+				     size_t *defrag_len);
 const u8 * get_ml_ie(const u8 *ies, size_t len, u8 type);
 const u8 * get_basic_mle_mld_addr(const u8 *buf, size_t len);
 const u8 * get_basic_mle_eml_capa(const u8 *buf, size_t len);
diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c
index d831557b3..7a6c0ddf0 100644
--- a/wpa_supplicant/events.c
+++ b/wpa_supplicant/events.c
@@ -3501,6 +3501,57 @@ static void wpas_parse_connection_info(struct wpa_supplicant *wpa_s,
 	} else {
 		wpa_s->connection_channel_bandwidth = CHAN_WIDTH_20;
 	}
+	
+	struct wpabuf *req_mlbuf = NULL, *resp_mlbuf = NULL;
+
+	req_mlbuf = ieee802_11_defrag(req_elems.basic_mle, req_elems.basic_mle_len, true);
+	resp_mlbuf = ieee802_11_defrag(resp_elems.basic_mle, resp_elems.basic_mle_len, true);
+
+	if (req_mlbuf && resp_mlbuf) {
+		for (int i = 0; i < MAX_NUM_MLD_LINKS; i++) {
+			if (!(wpa_s->valid_links & BIT(i)))
+				continue;
+
+			struct ieee802_11_elems req_persta_elems = req_elems;
+			struct ieee802_11_elems resp_persta_elems = resp_elems;
+
+			if (ieee802_11_parse_link_assoc_req(&req_persta_elems, req_mlbuf, i, true) == ParseFailed ||
+				ieee802_11_parse_link_assoc_resp(&resp_persta_elems, resp_mlbuf, i, true) == ParseFailed) {
+				wpa_s->links[i].max_nss_rx = wpa_s->connection_max_nss_rx;
+				wpa_s->links[i].max_nss_tx = wpa_s->connection_max_nss_tx;
+				wpa_s->links[i].channel_bandwidth = wpa_s->connection_channel_bandwidth;
+				continue;
+			}
+
+			wpa_s->links[i].max_nss_rx =
+				get_max_nss_capability(&req_persta_elems, 1) > get_max_nss_capability(&resp_persta_elems, 1)
+				? get_max_nss_capability(&resp_persta_elems, 1)
+				: get_max_nss_capability(&req_persta_elems, 1);
+
+			wpa_s->links[i].max_nss_tx =
+				get_max_nss_capability(&req_persta_elems, 0) > get_max_nss_capability(&resp_persta_elems, 0)
+				? get_max_nss_capability(&resp_persta_elems, 0)
+				: get_max_nss_capability(&req_persta_elems, 0);
+
+			struct supported_chan_width sta_link_supported_chan_width =
+				get_supported_channel_width(&req_persta_elems);
+			enum chan_width ap_link_operation_chan_width =
+				get_operation_channel_width(&resp_persta_elems);
+
+			if (wpa_s->connection_vht || wpa_s->connection_he || wpa_s->connection_eht) {
+				wpa_s->links[i].channel_bandwidth =
+					get_sta_operation_chan_width(ap_link_operation_chan_width,
+												 sta_link_supported_chan_width);
+			} else if (wpa_s->connection_ht) {
+				wpa_s->links[i].channel_bandwidth =
+					(ap_link_operation_chan_width == CHAN_WIDTH_40) ? CHAN_WIDTH_40 : CHAN_WIDTH_20;
+			} else {
+				wpa_s->links[i].channel_bandwidth = CHAN_WIDTH_20;
+			}
+		}
+		wpabuf_free(req_mlbuf);
+		wpabuf_free(resp_mlbuf);
+	}
 }
 
 
diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h
index 4379b053c..485cf6825 100644
--- a/wpa_supplicant/wpa_supplicant_i.h
+++ b/wpa_supplicant/wpa_supplicant_i.h
@@ -758,6 +758,9 @@ struct wpa_supplicant {
 		struct wpa_bss *bss;
 		bool disabled;
 		struct wpabuf *ies;
+		unsigned int max_nss_rx:4;
+		unsigned int max_nss_tx:4;
+		enum chan_width channel_bandwidth;
 	} links[MAX_NUM_MLD_LINKS];
 	u8 *last_con_fail_realm;
 	size_t last_con_fail_realm_len;
-- 
2.50.1




More information about the Hostap mailing list