u8 variable[];
} STRUCT_PACKED;
-/* IEEE P802.11be/D2.0, 9.4.2.312.4 - Reconfiguration Multi-Link element */
+/* IEEE P802.11be/D4.0, 9.4.2.312.4 - Reconfiguration Multi-Link element */
-#define EHT_ML_PRES_BM_RECONFIGURE_MLD_ADDRESS 0x0001
+#define RECONF_MULTI_LINK_CTRL_PRES_MLD_MAC_ADDR 0x0001
+
+#define EHT_PER_STA_RECONF_CTRL_LINK_ID_MSK 0x000f
+#define EHT_PER_STA_RECONF_CTRL_COMPLETE_PROFILE 0x0010
+#define EHT_PER_STA_RECONF_CTRL_MAC_ADDR 0x0020
+#define EHT_PER_STA_RECONF_CTRL_AP_REMOVAL_TIMER 0x0040
+#define EHT_PER_STA_RECONF_CTRL_OP_UPDATE_TYPE_MSK 0x0780
+#define EHT_PER_STA_RECONF_CTRL_OP_PARAMS 0x0800
+#define EHT_PER_STA_RECONF_CTRL_NSTR_BITMAP_SIZE 0x1000
/* IEEE P802.11be/D2.0, 9.4.2.312.1 - Multi-Link element / General */
wpabuf_free(mlbuf);
return ret;
}
+
+
+/*
+ * wpa_bss_parse_reconf_ml_element - Parse the Reconfiguration ML element
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @bss: BSS table entry
+ * Returns: The bitmap of links that are going to be removed
+ */
+u16 wpa_bss_parse_reconf_ml_element(struct wpa_supplicant *wpa_s,
+ struct wpa_bss *bss)
+{
+ struct ieee802_11_elems elems;
+ struct wpabuf *mlbuf;
+ const u8 *pos = wpa_bss_ie_ptr(bss);
+ size_t len = bss->ie_len ? bss->ie_len : bss->beacon_ie_len;
+ const struct ieee80211_eht_ml *ml;
+ u16 removed_links = 0;
+ u8 ml_common_len;
+
+ if (ieee802_11_parse_elems(pos, len, &elems, 1) == ParseFailed)
+ return 0;
+
+ if (!elems.reconf_mle || !elems.reconf_mle_len)
+ return 0;
+
+ mlbuf = ieee802_11_defrag_mle(&elems, MULTI_LINK_CONTROL_TYPE_RECONF);
+ if (!mlbuf)
+ return 0;
+
+ ml = (const struct ieee80211_eht_ml *) wpabuf_head(mlbuf);
+ len = wpabuf_len(mlbuf);
+
+ if (len < sizeof(*ml))
+ goto out;
+
+ ml_common_len = 1;
+ if (ml->ml_control & RECONF_MULTI_LINK_CTRL_PRES_MLD_MAC_ADDR)
+ ml_common_len += ETH_ALEN;
+
+ if (len < sizeof(*ml) + ml_common_len) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: Unexpected Reconfiguration ML element length: (%zu < %zu)",
+ len, sizeof(*ml) + ml_common_len);
+ goto out;
+ }
+
+ pos = ml->variable + ml_common_len;
+ len -= sizeof(*ml) + ml_common_len;
+
+ while (len >= 2 + sizeof(struct ieee80211_eht_per_sta_profile)) {
+ size_t sub_elem_len = *(pos + 1);
+
+ if (2 + sub_elem_len > len) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: Invalid link info len: %zu %zu",
+ 2 + sub_elem_len, len);
+ goto out;
+ }
+
+ if (*pos == EHT_ML_SUB_ELEM_PER_STA_PROFILE) {
+ const struct ieee80211_eht_per_sta_profile *sta_prof =
+ (const struct ieee80211_eht_per_sta_profile *)
+ (pos + 2);
+ u16 control = le_to_host16(sta_prof->sta_control);
+ u8 link_id;
+
+ link_id = control & EHT_PER_STA_RECONF_CTRL_LINK_ID_MSK;
+ removed_links |= BIT(link_id);
+ }
+
+ pos += 2 + sub_elem_len;
+ len -= 2 + sub_elem_len;
+ }
+
+ wpa_printf(MSG_DEBUG, "MLD: Reconfiguration: removed_links=0x%x",
+ removed_links);
+out:
+ wpabuf_free(mlbuf);
+ return removed_links;
+}
struct wpa_bss *bss,
u8 *ap_mld_addr,
u16 *missing_links);
+u16 wpa_bss_parse_reconf_ml_element(struct wpa_supplicant *wpa_s,
+ struct wpa_bss *bss);
#endif /* BSS_H */
}
+static bool wpas_valid_ml_bss(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
+
+{
+ u16 removed_links;
+
+ if (wpa_bss_parse_basic_ml_element(wpa_s, bss, NULL, NULL))
+ return true;
+
+ if (bss->n_mld_links == 0)
+ return true;
+
+ /* Check if the current BSS is going to be removed */
+ removed_links = wpa_bss_parse_reconf_ml_element(wpa_s, bss);
+ if (BIT(bss->mld_links[0].link_id) & removed_links)
+ return false;
+
+ return true;
+}
+
+
int disabled_freq(struct wpa_supplicant *wpa_s, int freq)
{
int i, j;
return false;
}
+ if (!wpas_valid_ml_bss(wpa_s, bss)) {
+ if (debug_print)
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ " skip - ML BSS going to be removed");
+ return false;
+ }
+
/* Matching configuration found */
return true;
}
struct wpa_ssid *ssid)
{
int *freqs;
- u16 missing_links = 0;
+ u16 missing_links = 0, removed_links;
if (!((wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_MLO) &&
(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME)))
&missing_links) || !missing_links)
return 0;
+ removed_links = wpa_bss_parse_reconf_ml_element(wpa_s, selected);
+ missing_links &= ~removed_links;
+
+ if (!missing_links)
+ return 0;
+
wpa_dbg(wpa_s, MSG_DEBUG,
"MLD: Doing an ML probe for missing links 0x%04x",
missing_links);
}
+static void wpas_ml_handle_removed_links(struct wpa_supplicant *wpa_s,
+ struct wpa_bss *bss)
+{
+ u16 removed_links = wpa_bss_parse_reconf_ml_element(wpa_s, bss);
+
+ wpa_s->valid_links &= ~removed_links;
+}
+
+
static void wpas_sme_ml_auth(struct wpa_supplicant *wpa_s,
union wpa_event_data *data,
int ie_offset)
params.mld = true;
params.mld_link_id = wpa_s->mlo_assoc_link_id;
params.ap_mld_addr = wpa_s->ap_mld_addr;
+ wpas_ml_handle_removed_links(wpa_s, bss);
}
if (wpa_s->sme.ssid_len != params.ssid_len ||