@@ -1790,6 +1790,7 @@ u16 wpa_bss_get_usable_links(struct wpa_supplicant *wpa_s,
for_each_link(bss->valid_links, link_id) {
struct wpa_bss *neigh_bss;
+ u16 ext_mld_capa_mask;
if (link_id == bss->mld_link_id)
continue;
@@ -1809,6 +1810,55 @@ u16 wpa_bss_get_usable_links(struct wpa_supplicant *wpa_s,
continue;
}
+ /* Check that the links is MLD and the information matches */
+ if (!neigh_bss->valid_links) {
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "MLD: neighbor without Multi-Link support");
+ continue;
+ }
+
+ if (neigh_bss->mld_link_id != link_id) {
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "MLD: neighbor has unexpected link ID");
+ continue;
+ }
+
+ if (!ether_addr_equal(bss->mld_addr, neigh_bss->mld_addr)) {
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "MLD: neighbor has a different MLD addr");
+ continue;
+ }
+
+ if (bss->mld_capa != neigh_bss->mld_capa) {
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "MLD: neighbors MLD Capabilities are not matching");
+ continue;
+ }
+
+ if (bss->eml_capa != neigh_bss->eml_capa) {
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "MLD: neighbors EML Capabilities are not matching");
+ continue;
+ }
+
+ /*
+ * Check well-defined values in Extended MLD Capabilities.
+ * In particular the Recommended Max Simultaneous Links
+ * subfield may change over time and is reserved depending on
+ * the frame that it is carried in.
+ * See IEEE P802.11be/D7.0, Table 9-417o.
+ */
+ ext_mld_capa_mask =
+ EHT_ML_EXT_MLD_CAPA_OP_PARAM_UPDATE |
+ EHT_ML_EXT_MLD_CAPA_NSTR_UPDATE |
+ EHT_ML_EXT_MLD_CAPA_EMLSR_ENA_ONE_LINK;
+ if ((bss->ext_mld_capa & ext_mld_capa_mask) !=
+ (neigh_bss->ext_mld_capa & ext_mld_capa_mask)) {
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "MLD: neighbors Extended MLD Capabilities are not matching");
+ continue;
+ }
+
if (ssid) {
int neigh_key_mgmt;
@@ -1921,17 +1971,25 @@ void wpa_bss_parse_basic_ml_element(struct wpa_supplicant *wpa_s,
sizeof(*ml_basic_common_info) + 1 + 1 + 2)
goto out;
- /* LINK_ID, BSS_PARAM_CH_COUNT, MLD_CAPA (see control/control_mask) */
+ /* LINK_ID, BSS_PARAM_CH_COUNT (see control/control_mask) */
link_id = ml_basic_common_info->variable[0] & EHT_ML_LINK_ID_MSK;
- pos = 1 + 1 + 2;
+ pos = 1 + 1;
if (le_to_host16(eht_ml->ml_control) &
BASIC_MULTI_LINK_CTRL_PRES_MSD_INFO)
pos += 2;
+ bss->eml_capa = 0;
if (le_to_host16(eht_ml->ml_control) &
- BASIC_MULTI_LINK_CTRL_PRES_EML_CAPA)
+ BASIC_MULTI_LINK_CTRL_PRES_EML_CAPA) {
+ bss->eml_capa =
+ WPA_GET_LE16(&ml_basic_common_info->variable[pos]);
pos += 2;
+ }
+
+ /* MLD_CAPA (always present, see control/control_mask) */
+ bss->mld_capa = WPA_GET_LE16(&ml_basic_common_info->variable[pos]);
+ pos += 2;
/* AP MLD ID from ML-Element if present (see comment below) */
if (le_to_host16(eht_ml->ml_control) &
@@ -1947,6 +2005,20 @@ void wpa_bss_parse_basic_ml_element(struct wpa_supplicant *wpa_s,
ap_mld_id = 0;
}
+ /* Extended MLD Capabilities */
+ bss->ext_mld_capa = 0;
+ if (le_to_host16(eht_ml->ml_control) &
+ BASIC_MULTI_LINK_CTRL_PRES_EXT_MLD_CAP) {
+ if (ml_basic_common_info->len <
+ sizeof(*ml_basic_common_info) + pos + 2)
+ goto out;
+
+ bss->ext_mld_capa =
+ WPA_GET_LE16(&ml_basic_common_info->variable[pos]);
+
+ pos += 2;
+ }
+
if (ml_basic_common_info->len < sizeof(*ml_basic_common_info) + pos)
goto out;
@@ -127,6 +127,12 @@ struct wpa_bss {
u8 mld_link_id;
/** For MLD, denotes if BSS is non-TX; useful for ML probe */
bool mld_bss_non_transmitted;
+ /** MLD Capabilities and Operations */
+ u16 mld_capa;
+ /** Extended MLD Capabilities and Operations */
+ u16 ext_mld_capa;
+ /** EML Capabilities */
+ u16 eml_capa;
/** An array of MLD links, any link found in the RNR is "valid" */
u16 valid_links;