diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h index a24e61fbe..805a4024f 100644 --- a/src/common/ieee802_11_defs.h +++ b/src/common/ieee802_11_defs.h @@ -2700,9 +2700,17 @@ struct eht_ml_probe_req_common_info { 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 */ diff --git a/wpa_supplicant/bss.c b/wpa_supplicant/bss.c index 47c7094c8..5ada34c4f 100644 --- a/wpa_supplicant/bss.c +++ b/wpa_supplicant/bss.c @@ -1708,3 +1708,83 @@ out: 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; +} diff --git a/wpa_supplicant/bss.h b/wpa_supplicant/bss.h index 8a45814e4..39dad868e 100644 --- a/wpa_supplicant/bss.h +++ b/wpa_supplicant/bss.h @@ -215,5 +215,7 @@ int wpa_bss_parse_basic_ml_element(struct wpa_supplicant *wpa_s, 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 */ diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c index ccdb63598..dec98f867 100644 --- a/wpa_supplicant/events.c +++ b/wpa_supplicant/events.c @@ -1177,6 +1177,26 @@ static void owe_trans_ssid(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, } +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; @@ -1579,6 +1599,13 @@ skip_assoc_disallow: 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; } @@ -1856,7 +1883,7 @@ static int wpa_supplicant_connect_ml_missing(struct wpa_supplicant *wpa_s, 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))) @@ -1867,6 +1894,12 @@ static int wpa_supplicant_connect_ml_missing(struct wpa_supplicant *wpa_s, &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); diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c index bb04652f5..ab64d467b 100644 --- a/wpa_supplicant/sme.c +++ b/wpa_supplicant/sme.c @@ -541,6 +541,15 @@ out: } +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) @@ -639,6 +648,7 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s, 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 ||