[PATCH] Pass more capabilities of MLO link to framework
Bruno.Kremp at sony.com
Bruno.Kremp at sony.com
Fri Oct 3 00:44:55 PDT 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 | 207 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
src/common/ieee802_11_common.h | 4 ++++
wpa_supplicant/events.c | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
wpa_supplicant/wpa_supplicant_i.h | 3 +++
4 files changed, 278 insertions(+)
diff --git a/src/common/ieee802_11_common.c b/src/common/ieee802_11_common.c
index ae7a7647a..206139916 100644
--- a/src/common/ieee802_11_common.c
+++ b/src/common/ieee802_11_common.c
@@ -1146,6 +1146,171 @@ out:
return res;
}
+ParseRes ieee802_11_parse_link_assoc_resp(struct ieee802_11_elems *elems,
+ struct wpabuf *mlbuf, struct wpabuf **sub_elem_buf,
+ u8 link_id, bool show_errors)
+{
+ const struct ieee80211_eht_ml *ml;
+ const u8 *pos;
+ ParseRes res = ParseFailed;
+
+ pos = wpabuf_head(mlbuf);
+ size_t len = wpabuf_len(mlbuf);
+
+ /* Must have control and common info length */
+ if (len < sizeof(*ml) + 1 || len < sizeof(*ml) + pos[sizeof(*ml)])
+ goto out;
+
+ ml = (const struct ieee80211_eht_ml *) pos;
+
+ /* As we are interested with the Per-STA profile, ignore other types */
+ if ((le_to_host16(ml->ml_control) & MULTI_LINK_CONTROL_TYPE_MASK) !=
+ MULTI_LINK_CONTROL_TYPE_BASIC)
+ goto out;
+
+ /* Skip the common info */
+ len -= sizeof(*ml) + pos[sizeof(*ml)];
+ pos += sizeof(*ml) + pos[sizeof(*ml)];
+
+ while (len > 2) {
+ size_t sub_elem_len = *(pos + 1), frag_len = sub_elem_len;
+ const u8 *start = pos;
+ size_t sta_info_len;
+ u16 link_info_control;
+ const u8 *non_inherit;
+
+ wpa_printf(MSG_DEBUG,
+ "MLD: sub element: len=%zu, sub_elem_len=%zu",
+ len, sub_elem_len);
+
+ if (2 + sub_elem_len > len) {
+ if (show_errors)
+ wpa_printf(MSG_DEBUG,
+ "MLD: error: len=%zu, sub_elem_len=%zu",
+ len, sub_elem_len);
+ goto out;
+ }
+
+ if (*pos != 0) {
+ pos += 2 + sub_elem_len;
+ len -= 2 + sub_elem_len;
+ continue;
+ }
+
+ if (sub_elem_len < 3) {
+ if (show_errors)
+ wpa_printf(MSG_DEBUG,
+ "MLD: error: sub_elem_len=%zu < 5",
+ sub_elem_len);
+ goto out;
+ }
+
+ link_info_control = WPA_GET_LE16(pos + 2);
+
+ if ((link_info_control & BASIC_MLE_STA_CTRL_LINK_ID_MASK) != link_id) {
+ pos += 2 + sub_elem_len;
+ len -= 2 + sub_elem_len;
+ continue;
+ }
+
+ sta_info_len = *(pos + 4);
+ if (sub_elem_len < sta_info_len + 3 || sta_info_len < 1) {
+ if (show_errors)
+ wpa_printf(MSG_DEBUG,
+ "MLD: error: sub_elem_len=%zu, sta_info_len=%zu",
+ sub_elem_len, sta_info_len);
+ goto out;
+ }
+
+ pos += sta_info_len + 4;
+ sub_elem_len -= sta_info_len + 2;
+
+ if (sub_elem_len < 2) {
+ if (show_errors)
+ wpa_printf(MSG_DEBUG,
+ "MLD: missing capability info");
+ goto out;
+ }
+
+ pos += 2;
+ sub_elem_len -= 2;
+
+ 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;
+
+ if (frag_len == 255) {
+ *sub_elem_buf = ieee802_11_defrag_per_sta_data(start, len);
+ if (!*sub_elem_buf)
+ {
+ goto out;
+ }
+ sub_elem_len = wpabuf_size(*sub_elem_buf) - 2 - (frag_len - sub_elem_len);
+ pos = (u8 *)wpabuf_head(*sub_elem_buf) + (pos - start);
+ }
+
+ /* Handle non-inheritance */
+ non_inherit = get_ie_ext(pos, sub_elem_len,
+ WLAN_EID_EXT_NON_INHERITANCE);
+ if (non_inherit && non_inherit[1] > 1) {
+ u8 non_inherit_len = non_inherit[1] - 1;
+
+ /*
+ * Do not include the Non-Inheritance element when
+ * parsing below. It should be the last element in the
+ * subelement.
+ */
+ if (3U + non_inherit_len > sub_elem_len)
+ goto out;
+ sub_elem_len -= 3 + non_inherit_len;
+
+ /* Skip the ID, length and extension ID */
+ non_inherit += 3;
+
+ if (non_inherit_len < 1UL + non_inherit[0]) {
+ if (show_errors)
+ wpa_printf(MSG_DEBUG,
+ "MLD: Invalid inheritance");
+ goto out;
+ }
+
+ ieee802_11_elems_clear_ids(elems, &non_inherit[1],
+ non_inherit[0]);
+
+ non_inherit_len -= 1 + non_inherit[0];
+ non_inherit += 1 + non_inherit[0];
+
+ if (non_inherit_len < 1UL + non_inherit[0]) {
+ if (show_errors)
+ wpa_printf(MSG_DEBUG,
+ "MLD: Invalid inheritance");
+ goto out;
+ }
+
+ ieee802_11_elems_clear_ext_ids(elems, &non_inherit[1],
+ non_inherit[0]);
+ }
+
+ wpa_printf(MSG_DEBUG, "MLD: link: sub_elem_len=%zu",
+ sub_elem_len);
+
+ if (sub_elem_len)
+ res = __ieee802_11_parse_elems(pos, sub_elem_len,
+ elems, show_errors);
+ else
+ res = ParseOK;
+ break;
+ }
+
+out:
+ return res;
+}
int ieee802_11_ie_count(const u8 *ies, size_t ies_len)
{
@@ -3458,6 +3623,48 @@ struct wpabuf * ieee802_11_defrag(const u8 *data, size_t len, bool ext_elem)
return buf;
}
+struct wpabuf * ieee802_11_defrag_per_sta_data(const u8 *data, size_t len)
+{
+ struct wpabuf *buf;
+ const u8 *pos, *end = data + len;
+
+ // subEID(1 oct) + length(1 oct) + max length of subelement(255 oct)
+ const size_t min_defrag_len = 258;
+
+ if (!data || !len)
+ return NULL;
+
+ if (len < min_defrag_len) {
+ return wpabuf_alloc_copy(data, len);
+ }
+
+ buf = wpabuf_alloc_copy(data, min_defrag_len - 1);
+ if (!buf)
+ return NULL;
+
+ pos = &data[min_defrag_len - 1];
+ len -= min_defrag_len - 1;
+ while (len > 2 && pos[0] == (int)EHT_ML_SUB_ELEM_FRAGMENT && pos[1]) {
+ int ret;
+ size_t elen = 2 + pos[1];
+
+ if (elen > (size_t) (end - pos) || elen > len)
+ break;
+ ret = wpabuf_resize(&buf, pos[1]);
+ if (ret < 0) {
+ wpabuf_free(buf);
+ return NULL;
+ }
+
+ /* Copy only the fragment data (without the SubEID and length) */
+ wpabuf_put_data(buf, &pos[2], pos[1]);
+ pos += elen;
+ len -= elen;
+ }
+
+ return buf;
+}
+
/**
* ieee802_11_defrag_mle_subelem - Defragment Multi-Link element subelements
diff --git a/src/common/ieee802_11_common.h b/src/common/ieee802_11_common.h
index 5f52b573c..bcca51f6a 100644
--- a/src/common/ieee802_11_common.h
+++ b/src/common/ieee802_11_common.h
@@ -215,6 +215,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, struct wpabuf **sub_elem_buf,
+ 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);
@@ -383,6 +386,7 @@ struct wpabuf * ieee802_11_defrag(const u8 *data, size_t len, bool ext_elem);
size_t ieee802_11_defrag_mle_subelem(struct wpabuf *mlbuf,
const u8 *parent_subelem,
size_t *defrag_len);
+struct wpabuf * ieee802_11_defrag_per_sta_data(const u8 *data, size_t 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);
diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c
index 42a52899d..319e46bfd 100644
--- a/wpa_supplicant/events.c
+++ b/wpa_supplicant/events.c
@@ -3525,6 +3525,70 @@ static int wpa_supplicant_event_associnfo(struct wpa_supplicant *wpa_s,
resp_elems.eht_capabilities;
if (req_elems.rrm_enabled)
wpa_s->rrm.rrm_used = 1;
+
+ 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,
+ resp_persta_elems = resp_elems;
+ struct wpabuf *sub_elem_buf = NULL;
+
+ 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,
+ &sub_elem_buf, 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;
+ if (sub_elem_buf)
+ wpabuf_free(sub_elem_buf);
+ 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;
+ }
+ if (sub_elem_buf)
+ wpabuf_free(sub_elem_buf);
+ }
+ 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 e7675a4ab..16c797c83 100644
--- a/wpa_supplicant/wpa_supplicant_i.h
+++ b/wpa_supplicant/wpa_supplicant_i.h
@@ -742,6 +742,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;
More information about the Hostap
mailing list