[PATCH 4/9] BSS: Parse ML information when updating the BSS
Benjamin Berg
benjamin at sipsolutions.net
Wed Jun 18 05:35:26 PDT 2025
From: Benjamin Berg <benjamin.berg at intel.com>
There is no point in re-parsing the ML element all the time if the
relevant information is being cached in the BSS anyway. As such, move
the parsing part to be done when adding/updating the BSS and add a
separate function to retrieve the links that are usable or missing in
the BSS cache.
Overall, this is not going to save work, but it means that the
multi-link information in the BSS is always up-to-date and also
detangles the RNR/ML parsing from the later runtime checks whether the
links are valid for the selected SSID.
Signed-off-by: Benjamin Berg <benjamin.berg at intel.com>
---
tests/test-bss.c | 10 +-
wpa_supplicant/bss.c | 282 +++++++++++++++++++---------------------
wpa_supplicant/bss.h | 13 +-
wpa_supplicant/events.c | 9 +-
wpa_supplicant/sme.c | 3 +-
5 files changed, 150 insertions(+), 167 deletions(-)
diff --git a/tests/test-bss.c b/tests/test-bss.c
index 10bcd17787..058cc1fad7 100644
--- a/tests/test-bss.c
+++ b/tests/test-bss.c
@@ -62,9 +62,8 @@ void test_parse_basic_ml(struct wpa_supplicant *wpa_s, u8 mld_id,
u8 ies[sizeof(rnr_ie) + sizeof(ml_ie_mld_id) +
sizeof(mbssid_idx_ie)];
} bss;
- u16 missing_links;
+ u16 missing_links = 0;
u8 ret;
- bool nontransmitted;
memcpy(bss.bss.ies, rnr_ie, sizeof(rnr_ie));
bss.bss.ie_len = sizeof(rnr_ie);
@@ -84,14 +83,13 @@ void test_parse_basic_ml(struct wpa_supplicant *wpa_s, u8 mld_id,
bss.bss.ie_len += sizeof(mbssid_idx_ie);
}
- ret = wpa_bss_parse_basic_ml_element(wpa_s, &bss.bss,
- &missing_links, NULL,
- &nontransmitted);
+ wpa_bss_parse_basic_ml_element(wpa_s, &bss.bss);
+ ret = wpa_bss_get_usable_links(wpa_s, &bss.bss, NULL, &missing_links);
ASSERT_CMP_INT(ret, ==, 1);
ASSERT_CMP_INT(bss.bss.valid_links, ==, 3);
ASSERT_CMP_INT(missing_links, ==, 0x0002);
- ASSERT_CMP_INT(nontransmitted, ==, mbssid_idx > 0);
+ ASSERT_CMP_INT(bss.bss.mld_bss_non_transmitted, ==, mbssid_idx > 0);
}
#define RUN_TEST(func, ...) do { \
diff --git a/wpa_supplicant/bss.c b/wpa_supplicant/bss.c
index b8fe141935..b5b6305f13 100644
--- a/wpa_supplicant/bss.c
+++ b/wpa_supplicant/bss.c
@@ -590,10 +590,8 @@ static struct wpa_bss * wpa_bss_add(struct wpa_supplicant *wpa_s,
{
struct wpa_bss *bss;
char extra[100];
- const u8 *ml_ie;
char *pos, *end;
int ret = 0;
- const u8 *mld_addr;
bss = os_zalloc(sizeof(*bss) + res->ie_len + res->beacon_ie_len);
if (bss == NULL)
@@ -608,13 +606,7 @@ static struct wpa_bss * wpa_bss_add(struct wpa_supplicant *wpa_s,
os_memcpy(bss->ies, res + 1, res->ie_len + res->beacon_ie_len);
wpa_bss_set_hessid(bss);
- os_memset(bss->mld_addr, 0, ETH_ALEN);
- ml_ie = wpa_scan_get_ml_ie(res, MULTI_LINK_CONTROL_TYPE_BASIC);
- if (ml_ie) {
- mld_addr = get_basic_mle_mld_addr(&ml_ie[3], ml_ie[1] - 1);
- if (mld_addr)
- os_memcpy(bss->mld_addr, mld_addr, ETH_ALEN);
- }
+ wpa_bss_parse_basic_ml_element(wpa_s, bss);
if (wpa_s->num_bss + 1 > wpa_s->conf->bss_max_count &&
wpa_bss_remove_oldest(wpa_s) != 0) {
@@ -905,17 +897,9 @@ wpa_bss_update(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
dl_list_add(prev, &bss->list_id);
}
if (changes & WPA_BSS_IES_CHANGED_FLAG) {
- const u8 *ml_ie, *mld_addr;
-
wpa_bss_set_hessid(bss);
- os_memset(bss->mld_addr, 0, ETH_ALEN);
- ml_ie = wpa_scan_get_ml_ie(res, MULTI_LINK_CONTROL_TYPE_BASIC);
- if (ml_ie) {
- mld_addr = get_basic_mle_mld_addr(&ml_ie[3],
- ml_ie[1] - 1);
- if (mld_addr)
- os_memcpy(bss->mld_addr, mld_addr, ETH_ALEN);
- }
+
+ wpa_bss_parse_basic_ml_element(wpa_s, bss);
}
dl_list_add_tail(&wpa_s->bss, &bss->list);
@@ -1639,8 +1623,7 @@ static void
wpa_bss_parse_ml_rnr_ap_info(struct wpa_supplicant *wpa_s,
struct wpa_bss *bss, u8 ap_mld_id,
const struct ieee80211_neighbor_ap_info *ap_info,
- size_t len, u16 *seen, u16 *usable, u16 *missing,
- struct wpa_ssid *ssid)
+ size_t len, u16 *seen)
{
const u8 *pos, *end;
const u8 *mld_params;
@@ -1677,16 +1660,8 @@ wpa_bss_parse_ml_rnr_ap_info(struct wpa_supplicant *wpa_s,
wpa_printf(MSG_DEBUG,
"MLD: Reported link not part of MLD");
} else if (!(BIT(link_id) & *seen)) {
- struct wpa_bss *neigh_bss;
struct mld_link *l;
- if (ssid && ssid->ssid_len)
- neigh_bss = wpa_bss_get(wpa_s, pos + 1,
- ssid->ssid,
- ssid->ssid_len);
- else
- neigh_bss = wpa_bss_get_bssid(wpa_s, pos + 1);
-
*seen |= BIT(link_id);
wpa_printf(MSG_DEBUG, "MLD: mld ID=%u, link ID=%u",
*mld_params, link_id);
@@ -1699,17 +1674,6 @@ wpa_bss_parse_ml_rnr_ap_info(struct wpa_supplicant *wpa_s,
l->freq = ieee80211_chan_to_freq(NULL,
ap_info->op_class,
ap_info->channel);
-
- if (!neigh_bss) {
- *missing |= BIT(link_id);
- } else if ((!ssid ||
- wpa_scan_res_match(wpa_s, 0, neigh_bss,
- ssid, 1, 0, true)) &&
- !wpa_bssid_ignore_is_listed(
- wpa_s, neigh_bss->bssid)) {
- l->freq = neigh_bss->freq;
- *usable |= BIT(link_id);
- }
}
}
}
@@ -1792,15 +1756,93 @@ wpa_bss_validate_rsne_ml(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
/**
- * wpa_bss_parse_basic_ml_element - Parse the Basic Multi-Link element
+ * wpa_bss_get_usable_links - Retrieve the usable links of the AP MLD
* @wpa_s: Pointer to wpa_supplicant data
* @bss: BSS table entry
- * @link_info: Array to store link information (or %NULL),
- * should be initialized and #MAX_NUM_MLD_LINKS elements long
- * @missing_links: Result bitmask of links that were not discovered (or %NULL)
* @ssid: Target SSID (or %NULL)
- * @nontransmitted: Out parameter denoting whether the BSSID is nontransmitted
- * (or %NULL)
+ * @missing_links: Result bitmask of links that were not discovered (or %NULL)
+ * Returns: bitmap of links that are usable, or 0 for non-MLD or failure
+ *
+ * Validate each link of the MLD to verify that it is compatible and connection
+ * to each of the links is allowed.
+ */
+u16 wpa_bss_get_usable_links(struct wpa_supplicant *wpa_s,
+ struct wpa_bss *bss,
+ struct wpa_ssid *ssid,
+ u16 *missing_links)
+{
+ int rsne_type, key_mgmt;
+ u16 usable_links = 0;
+ u8 link_id;
+
+ if (!bss->valid_links)
+ return 0;
+
+ rsne_type = -1;
+ if (ssid &&
+ !wpa_bss_validate_rsne_ml(wpa_s, ssid, bss, &key_mgmt,
+ &rsne_type)) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "MLD: No valid key management");
+ return 0;
+ }
+
+ usable_links = BIT(bss->mld_link_id);
+
+ for_each_link(bss->valid_links, link_id) {
+ struct wpa_bss *neigh_bss;
+
+ if (link_id == bss->mld_link_id)
+ continue;
+
+ if (ssid && ssid->ssid_len)
+ neigh_bss = wpa_bss_get(wpa_s,
+ bss->mld_links[link_id].bssid,
+ ssid->ssid,
+ ssid->ssid_len);
+ else
+ neigh_bss = wpa_bss_get_bssid(wpa_s,
+ bss->mld_links[link_id].bssid);
+
+ if (!neigh_bss) {
+ if (missing_links)
+ *missing_links |= BIT(link_id);
+ continue;
+ }
+
+ if (ssid) {
+ int neigh_key_mgmt;
+
+ /* As per IEEE P802.11be/D7.0, 12.6.2 (RSNA selection), all APs
+ * affiliated with an AP MLD shall advertise at least one common
+ * AKM suite selector in the AKM Suite List field of an RSNE or
+ * RSNXE. Discard links that do not have compatibility
+ * configuration with the association link.
+ */
+ if (!wpa_bss_validate_rsne_ml(wpa_s, ssid, neigh_bss,
+ &neigh_key_mgmt, &rsne_type) ||
+ !(key_mgmt & neigh_key_mgmt)) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: Discard link %u due to RSN parameter mismatch",
+ link_id);
+ continue;
+ }
+ }
+
+ if ((!ssid ||
+ wpa_scan_res_match(wpa_s, 0, neigh_bss, ssid, 1, 0, true)) &&
+ !wpa_bssid_ignore_is_listed(wpa_s, neigh_bss->bssid)) {
+ usable_links |= BIT(link_id);
+ }
+ }
+
+ return usable_links;
+}
+
+
+/**
+ * wpa_bss_parse_basic_ml_element - Parse the Basic Multi-Link element
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @bss: BSS table entry
* Returns: Usable links bitmask, or 0 for non-MLD or parsing failures
*
* Parses the Basic Multi-Link element of the BSS into @link_info using the scan
@@ -1813,11 +1855,8 @@ wpa_bss_validate_rsne_ml(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
* AP MLD ID should not be included in an ML Probe Request sent to its BSSID,
* otherwise it should be included and set to zero.
*/
-u16 wpa_bss_parse_basic_ml_element(struct wpa_supplicant *wpa_s,
- struct wpa_bss *bss,
- u16 *missing_links,
- struct wpa_ssid *ssid,
- bool *nontransmitted)
+void wpa_bss_parse_basic_ml_element(struct wpa_supplicant *wpa_s,
+ struct wpa_bss *bss)
{
struct ieee802_11_elems elems;
struct wpabuf *mlbuf = NULL;
@@ -1825,6 +1864,7 @@ u16 wpa_bss_parse_basic_ml_element(struct wpa_supplicant *wpa_s,
size_t ml_ie_len;
const struct ieee80211_eht_ml *eht_ml;
const struct eht_ml_basic_common_info *ml_basic_common_info;
+ const u8 *mbssid_idx_elem;
u8 i, pos, link_id, ap_mld_id;
const u16 control_mask =
MULTI_LINK_CONTROL_TYPE_MASK |
@@ -1836,11 +1876,9 @@ u16 wpa_bss_parse_basic_ml_element(struct wpa_supplicant *wpa_s,
BASIC_MULTI_LINK_CTRL_PRES_LINK_ID |
BASIC_MULTI_LINK_CTRL_PRES_BSS_PARAM_CH_COUNT |
BASIC_MULTI_LINK_CTRL_PRES_MLD_CAPA;
- u16 missing = 0;
- u16 seen, usable = 0;
+ u16 seen;
const u8 *ies_pos = wpa_bss_ie_ptr(bss);
size_t ies_len = bss->ie_len ? bss->ie_len : bss->beacon_ie_len;
- int rsne_type, key_mgmt;
struct mld_link *l;
if (ieee802_11_parse_elems(ies_pos, ies_len, &elems, 1) ==
@@ -1857,14 +1895,6 @@ u16 wpa_bss_parse_basic_ml_element(struct wpa_supplicant *wpa_s,
ml_ie_len = wpabuf_len(mlbuf);
- rsne_type = -1;
- if (ssid &&
- !wpa_bss_validate_rsne_ml(wpa_s, ssid, bss, &key_mgmt,
- &rsne_type)) {
- wpa_dbg(wpa_s, MSG_DEBUG, "MLD: No valid key management");
- goto out;
- }
-
/*
* for ext ID + 2 control + common info len
*/
@@ -1922,59 +1952,51 @@ u16 wpa_bss_parse_basic_ml_element(struct wpa_supplicant *wpa_s,
link_id = ml_basic_common_info->variable[0] & EHT_ML_LINK_ID_MSK;
+ os_memcpy(bss->mld_addr, ml_basic_common_info->mld_addr, ETH_ALEN);
+
bss->mld_link_id = link_id;
bss->valid_links = BIT(link_id);
- usable = seen = bss->valid_links;
+ seen = bss->valid_links;
l = &bss->mld_links[link_id];
os_memcpy(l->bssid, bss->bssid, ETH_ALEN);
l->freq = bss->freq;
+ bss->mld_bss_non_transmitted = false;
- /* Read Multiple BSSID Index for *nontransmitted and AP MLD ID */
- if (nontransmitted || !(le_to_host16(eht_ml->ml_control) &
- BASIC_MULTI_LINK_CTRL_PRES_AP_MLD_ID)) {
- const u8 *mbssid_idx_elem;
-
- /*
- * We should be able to rely on the Multiple BSSID Index element
- * to be included if the BSS is nontransmitted. Both if it was
- * extracted from a beacon and if it came from an ML probe
- * response (i.e. not listed in Draft P802.11be_D4.1, 35.3.3.4).
- *
- * Note that the AP MLD ID and the Multiple-BSSID Index will be
- * identical if the information was reported by the
- * corresponding transmitting AP (P802.11be_D4.1, 9.4.2.169.2).
- * As an AP MLD ID will not be explicitly provided we need to
- * rely on the Multiple-BSSID Index. This is generally the case
- * when the BSS information was read from a
- * Multiple-BSSID Element.
- *
- * The alternative scenario is a BSS discovered using a
- * Multi-Link Probe Response. In that case, we can still
- * determine whether the BSS is nontransmitted or not using the
- * Multiple BSSID Index Element. However, the AP MLD ID may be
- * different inside the ML Probe Response and the driver also
- * needs to deal with this during inheritance.
- *
- * We assume the driver either
- * - includes the appropriate AP MLD ID in the ML Element
- * it generates (see above), or
- * - rewrites the RNR so that the AP MLD ID matches the
- * Multiple-BSSID Index.
- */
- mbssid_idx_elem = wpa_bss_get_ie(bss,
- WLAN_EID_MULTIPLE_BSSID_INDEX);
- if (mbssid_idx_elem && mbssid_idx_elem[1] >= 1) {
- if (!(le_to_host16(eht_ml->ml_control) &
- BASIC_MULTI_LINK_CTRL_PRES_AP_MLD_ID))
- ap_mld_id = mbssid_idx_elem[2];
- if (nontransmitted)
- *nontransmitted = !!mbssid_idx_elem[2];
- } else {
- if (nontransmitted)
- *nontransmitted = false;
- }
+ /*
+ * We should be able to rely on the Multiple BSSID Index element
+ * to be included if the BSS is nontransmitted. Both if it was
+ * extracted from a beacon and if it came from an ML probe
+ * response (i.e. not listed in Draft P802.11be_D4.1, 35.3.3.4).
+ *
+ * Note that the AP MLD ID and the Multiple-BSSID Index will be
+ * identical if the information was reported by the
+ * corresponding transmitting AP (P802.11be_D4.1, 9.4.2.169.2).
+ * As an AP MLD ID will not be explicitly provided we need to
+ * rely on the Multiple-BSSID Index. This is generally the case
+ * when the BSS information was read from a
+ * Multiple-BSSID Element.
+ *
+ * The alternative scenario is a BSS discovered using a
+ * Multi-Link Probe Response. In that case, we can still
+ * determine whether the BSS is nontransmitted or not using the
+ * Multiple BSSID Index Element. However, the AP MLD ID may be
+ * different inside the ML Probe Response and the driver also
+ * needs to deal with this during inheritance.
+ *
+ * We assume the driver either
+ * - includes the appropriate AP MLD ID in the ML Element
+ * it generates (see above), or
+ * - rewrites the RNR so that the AP MLD ID matches the
+ * Multiple-BSSID Index.
+ */
+ mbssid_idx_elem = wpa_bss_get_ie(bss, WLAN_EID_MULTIPLE_BSSID_INDEX);
+ if (mbssid_idx_elem && mbssid_idx_elem[1] >= 1) {
+ if (!(le_to_host16(eht_ml->ml_control) &
+ BASIC_MULTI_LINK_CTRL_PRES_AP_MLD_ID))
+ ap_mld_id = mbssid_idx_elem[2];
+ bss->mld_bss_non_transmitted = !!mbssid_idx_elem[2];
}
for_each_element_id(elem, WLAN_EID_REDUCED_NEIGHBOR_REPORT,
@@ -1998,64 +2020,28 @@ u16 wpa_bss_parse_basic_ml_element(struct wpa_supplicant *wpa_s,
goto out;
wpa_bss_parse_ml_rnr_ap_info(wpa_s, bss, ap_mld_id,
- ap_info, ap_info_len,
- &seen, &usable, &missing,
- ssid);
+ ap_info, len, &seen);
ap_info_pos += ap_info_len;
len -= ap_info_len;
}
}
- for_each_link(usable, i) {
- struct wpa_bss *neigh_bss;
- int neigh_key_mgmt;
-
- if (!ssid)
- break;
-
- if (i == link_id)
- continue;
-
- neigh_bss = wpa_bss_get_bssid(wpa_s, bss->mld_links[i].bssid);
- if (!neigh_bss) /* cannot be NULL at this point */
- continue;
-
- /* As per IEEE P802.11be/D7.0, 12.6.2 (RSNA selection), all APs
- * affiliated with an AP MLD shall advertise at least one common
- * AKM suite selector in the AKM Suite List field of an RSNE or
- * RSNXE. Discard links that do not have compatibility
- * configuration with the association link.
- */
- if (!wpa_bss_validate_rsne_ml(wpa_s, ssid, neigh_bss,
- &neigh_key_mgmt, &rsne_type) ||
- !(key_mgmt & neigh_key_mgmt)) {
- wpa_printf(MSG_DEBUG,
- "MLD: Discard link %u due to RSN parameter mismatch",
- i);
- usable &= ~BIT(i);
- continue;
- }
- }
-
- wpa_printf(MSG_DEBUG, "MLD: valid_links=0x%04hx usable=0x%04hx (unresolved: 0x%04hx)",
- bss->valid_links, usable, missing);
+ wpa_printf(MSG_DEBUG, "MLD: valid_links=0x%04hx",
+ bss->valid_links);
for_each_link(bss->valid_links, i) {
wpa_printf(MSG_DEBUG, "MLD: link=%u, bssid=" MACSTR,
i, MAC2STR(bss->mld_links[i].bssid));
}
- if (missing_links)
- *missing_links = missing;
-
wpabuf_free(mlbuf);
- return usable;
+ return;
out:
+ os_memset(bss->mld_addr, 0, ETH_ALEN);
bss->valid_links = 0;
wpabuf_free(mlbuf);
- return 0;
}
diff --git a/wpa_supplicant/bss.h b/wpa_supplicant/bss.h
index f01accc50b..4bd752fda2 100644
--- a/wpa_supplicant/bss.h
+++ b/wpa_supplicant/bss.h
@@ -125,6 +125,8 @@ struct wpa_bss {
u8 mld_addr[ETH_ALEN];
/** Link ID of this affiliated AP of the AP MLD */
u8 mld_link_id;
+ /** For MLD, denotes if BSS is non-TX; useful for ML probe */
+ bool mld_bss_non_transmitted;
/** An array of MLD links, any link found in the RNR is "valid" */
u16 valid_links;
@@ -217,11 +219,12 @@ void calculate_update_time(const struct os_reltime *fetch_time,
unsigned int age_ms,
struct os_reltime *update_time);
-u16 wpa_bss_parse_basic_ml_element(struct wpa_supplicant *wpa_s,
- struct wpa_bss *bss,
- u16 *missing_links,
- struct wpa_ssid *ssid,
- bool *nontransmitted);
+u16 wpa_bss_get_usable_links(struct wpa_supplicant *wpa_s,
+ struct wpa_bss *bss,
+ struct wpa_ssid *ssid,
+ u16 *missing_links);
+void wpa_bss_parse_basic_ml_element(struct wpa_supplicant *wpa_s,
+ struct wpa_bss *bss);
u16 wpa_bss_parse_reconf_ml_element(struct wpa_supplicant *wpa_s,
struct wpa_bss *bss);
diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c
index b8db56921d..a411ea8f37 100644
--- a/wpa_supplicant/events.c
+++ b/wpa_supplicant/events.c
@@ -1176,7 +1176,6 @@ static bool wpas_valid_ml_bss(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
{
u16 removed_links;
- wpa_bss_parse_basic_ml_element(wpa_s, bss, NULL, NULL, NULL);
if (!bss->valid_links)
return true;
@@ -1887,15 +1886,13 @@ static int wpa_supplicant_connect_ml_missing(struct wpa_supplicant *wpa_s,
{
int *freqs;
u16 missing_links = 0, removed_links, usable_links;
- bool nontransmitted;
if (!((wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_MLO) &&
(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME)))
return 0;
- usable_links = wpa_bss_parse_basic_ml_element(wpa_s, selected,
- &missing_links, ssid,
- &nontransmitted);
+ usable_links = wpa_bss_get_usable_links(wpa_s, selected, NULL,
+ &missing_links);
if (!usable_links || !missing_links)
return 0;
@@ -1935,7 +1932,7 @@ static int wpa_supplicant_connect_ml_missing(struct wpa_supplicant *wpa_s,
* In case the ML probe requested is intended to retrieve information
* from a non-transmitted BSS, the AP MLD ID should not be included.
*/
- if (nontransmitted)
+ if (selected->mld_bss_non_transmitted)
wpa_s->ml_probe_mld_id = -1;
else
wpa_s->ml_probe_mld_id = 0;
diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c
index 50b3d635df..55dcf41efc 100644
--- a/wpa_supplicant/sme.c
+++ b/wpa_supplicant/sme.c
@@ -534,8 +534,7 @@ static void wpas_sme_set_mlo_links(struct wpa_supplicant *wpa_s,
if (!(wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_MLO))
return;
- usable_links = wpa_bss_parse_basic_ml_element(wpa_s, bss, NULL,
- ssid, NULL);
+ usable_links = wpa_bss_get_usable_links(wpa_s, bss, ssid, NULL);
if (!usable_links)
return;
--
2.49.0
More information about the Hostap
mailing list