}
-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;
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);
}
+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;
}
+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,
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;
} 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);
}