]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
Determine NSS and bandwidth for each setup link on association event
authorBruno.Kremp@sony.com <Bruno.Kremp@sony.com>
Fri, 19 Dec 2025 10:25:46 +0000 (10:25 +0000)
committerJouni Malinen <j@w1.fi>
Sat, 24 Jan 2026 17:44:15 +0000 (19:44 +0200)
Parse the MLE from the (Re)Association Request and Response frames to
get the channel bandwidth and maximum number of spatial streams for each
setup link of an ML association.

Signed-off-by: Bruno Kremp <bruno.kremp@sony.com>
src/common/ieee802_11_common.c
src/common/ieee802_11_common.h
wpa_supplicant/events.c
wpa_supplicant/wpa_supplicant_i.h

index 387c417a6eb925a00f8dafc7ebc12af3fd5203c1..74ad41d3591970361f276dfc12b61cb79222d98c 100644 (file)
@@ -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;
@@ -1102,6 +1103,28 @@ 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) {
+                       u16 status_code;
+
+                       if (sub_elem_len < 2) {
+                               if (show_errors)
+                                       wpa_printf(MSG_DEBUG,
+                                                  "MLD: missing status code");
+                               goto out;
+                       }
+
+                       status_code = WPA_GET_LE16(pos);
+                       if (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);
@@ -1161,6 +1184,26 @@ 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;
index 09bd0675a1b2593c6ca31b42d51973e0317e2812..b1eba593bbe65c2aaff92eb99c6404ad4284e472 100644 (file)
@@ -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);
index e74099eecf2426e6c5eeafb4408a11bbf4ab183d..4ad6c5138d9ec8559bba8d35e74f6917a1074d83 100644 (file)
@@ -3445,6 +3445,53 @@ static int wpa_supplicant_use_own_rsne_params(struct wpa_supplicant *wpa_s,
 }
 
 
+static void wpas_parse_connection_info_link(struct wpa_supplicant *wpa_s,
+                                           int i,
+                                           struct ieee802_11_elems *req_elems,
+                                           struct ieee802_11_elems *resp_elems,
+                                           struct wpabuf *req_mlbuf,
+                                           struct wpabuf *resp_mlbuf)
+{
+       struct ieee802_11_elems req_persta_elems = *req_elems;
+       struct ieee802_11_elems resp_persta_elems = *resp_elems;
+       struct supported_chan_width sta_cw;
+       enum chan_width ap_cw;
+
+       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;
+               return;
+       }
+
+       wpa_s->links[i].max_nss_rx =
+               MIN(get_max_nss_capability(&req_persta_elems, true),
+                   get_max_nss_capability(&resp_persta_elems, false));
+
+       wpa_s->links[i].max_nss_tx =
+               MIN(get_max_nss_capability(&req_persta_elems, false),
+                   get_max_nss_capability(&resp_persta_elems, true));
+
+       sta_cw = get_supported_channel_width(&req_persta_elems);
+       ap_cw = 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_cw, sta_cw);
+       } else if (wpa_s->connection_ht) {
+               wpa_s->links[i].channel_bandwidth = (ap_cw == CHAN_WIDTH_40) ?
+                       CHAN_WIDTH_40 : CHAN_WIDTH_20;
+       } else {
+               wpa_s->links[i].channel_bandwidth = CHAN_WIDTH_20;
+       }
+}
+
+
 static void wpas_parse_connection_info(struct wpa_supplicant *wpa_s,
                                       unsigned int freq,
                                       const u8 *req_ies, size_t req_ies_len,
@@ -3454,6 +3501,7 @@ static void wpas_parse_connection_info(struct wpa_supplicant *wpa_s,
        int max_nss_rx_req, max_nss_rx_resp, max_nss_tx_req, max_nss_tx_resp;
        struct supported_chan_width sta_supported_chan_width;
        enum chan_width ap_operation_chan_width;
+       struct wpabuf *req_mlbuf, *resp_mlbuf;
 
        wpa_s->connection_set = 0;
 
@@ -3502,6 +3550,21 @@ static void wpas_parse_connection_info(struct wpa_supplicant *wpa_s,
        } else {
                wpa_s->connection_channel_bandwidth = CHAN_WIDTH_20;
        }
+
+       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) {
+               int i;
+
+               for_each_link(wpa_s->valid_links, i)
+                       wpas_parse_connection_info_link(wpa_s, i,
+                                                       &req_elems, &resp_elems,
+                                                       req_mlbuf, resp_mlbuf);
+       }
+       wpabuf_free(req_mlbuf);
+       wpabuf_free(resp_mlbuf);
 }
 
 
index 8c784304d8f53e7b49a558b70e8fe6a7abf93b94..968c777d7eaf3f90ecbf5560fee8ba2aaab455e9 100644 (file)
@@ -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;