[PATCH 1/9] BSS: use correct AP MLD ID
Benjamin Berg
benjamin at sipsolutions.net
Wed Jun 18 05:35:23 PDT 2025
From: Benjamin Berg <benjamin.berg at intel.com>
We need to be more careful about the ID used when resolving the RNR and
doing an ML probe request. The thing to keep in mind is that the AP MLD
ID is local to the reported beacon and can change depending on who is
reporting the information. On the other hand, the Multiple BSSID-Index
is static.
As such, the AP MLD ID that is local to the information should be used
for parsing the RNR. This AP MLD ID can simply be retrieved from the
Multi-Link Element. However, if an AP MLD ID is not included, then it
must be assumed to be identical to the Multiple-BSSID Index Element.
Note that an AP MLD ID will only ever be included if the underlying code
that does the inheritance chooses to include it in the Multi-Link
Element that it inserts. This code may also rewrite the RNR entries to
ensure they match the expected value and leave out the AP MLD ID in the
Multi-Link Element.
In contrast, for the purpose of sending a Multi-Link Probe Request we
need to know whether or not the AP is transmitting. For this information
we must purely rely on the Multiple-BSSID Index Element.
Fixes: de5e01010cb2 ("wpa_supplicant: Support ML probe request")
Signed-off-by: Benjamin Berg <benjamin.berg at intel.com>
Reviewed-by: Andrei Otcheretianski <andrei.otcheretianski at intel.com>
---
tests/test-bss.c | 67 ++++++++++++++------
wpa_supplicant/bss.c | 132 +++++++++++++++++++++++++++++-----------
wpa_supplicant/bss.h | 2 +-
wpa_supplicant/events.c | 6 +-
4 files changed, 151 insertions(+), 56 deletions(-)
diff --git a/tests/test-bss.c b/tests/test-bss.c
index 97af7e4a1e..3c72f10106 100644
--- a/tests/test-bss.c
+++ b/tests/test-bss.c
@@ -17,55 +17,82 @@
#define ASSERT_CMP_INT(a, cmp, b) { \
ssize_t __a = (a); ssize_t __b = (b); \
if (!(__a cmp __b)) { \
- wpa_printf(MSG_ERROR, "Assertion failed: %ld %s %ld", \
- __a, #cmp, __b); \
+ wpa_printf(MSG_ERROR, \
+ "Assertion failed: %ld %s %ld (%s %s %s)", \
+ __a, #cmp, __b, #a, #cmp, #b); \
abort(); \
} \
}
-void test_parse_basic_ml(struct wpa_supplicant *wpa_s, u8 mld_id)
+void test_parse_basic_ml(struct wpa_supplicant *wpa_s, u8 mld_id,
+ int mbssid_idx)
{
- const u8 mld_ie[] = {
+ u8 params_link = RNR_BSS_PARAM_SAME_SSID;
+ u8 params_bss = RNR_BSS_PARAM_SAME_SSID |
+ ((mbssid_idx >= 0) ?
+ RNR_BSS_PARAM_MULTIPLE_BSSID : 0) |
+ ((mbssid_idx == 0) ?
+ RNR_BSS_PARAM_TRANSMITTED_BSSID : 0);
+ const u8 rnr_ie[] = {
/* RNR */
WLAN_EID_REDUCED_NEIGHBOR_REPORT, 40,
0x00, 0x10, 0x51, 0x01, 0xff, 0x00, 0x11, 0x22,
- 0x33, 0x44, 0x01, 0x68, 0x05, 0x2d, 0xa6, 0x42,
+ 0x33, 0x44, 0x01, 0x68, 0x05, 0x2d, 0xa6, params_bss,
0xfe, mld_id, 0x10, 0x00, 0x00, 0x10, 0x51, 0x06,
0xff, 0x00, 0x11, 0x22, 0x33, 0x44, 0x02, 0x68,
- 0x05, 0x2d, 0xa6, 0x42, 0xfe, mld_id, 0x11, 0x00,
+ 0x05, 0x2d, 0xa6, params_link, 0xfe, mld_id, 0x11, 0x00,
+ };
+ const u8 ml_ie[] = {
/* basic ML */
WLAN_EID_EXTENSION, 1 + 15, WLAN_EID_EXT_MULTI_LINK,
0xb0, 0x01, 0x0d, 0x02, 0x00, 0x00, 0x00, 0x07,
0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
};
+ const u8 ml_ie_mld_id[] = {
+ /* basic ML */
+ WLAN_EID_EXTENSION, 1 + 16, WLAN_EID_EXT_MULTI_LINK,
+ 0xb0, 0x03, 0x0e, 0x02, 0x00, 0x00, 0x00, 0x07,
+ 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, mld_id
+ };
const u8 mbssid_idx_ie[] = {
- WLAN_EID_MULTIPLE_BSSID_INDEX, 1, mld_id,
+ WLAN_EID_MULTIPLE_BSSID_INDEX, 1, mbssid_idx,
};
struct {
struct wpa_bss bss;
- u8 ies[sizeof(mld_ie) + sizeof(mbssid_idx_ie)];
+ u8 ies[sizeof(rnr_ie) + sizeof(ml_ie_mld_id) +
+ sizeof(mbssid_idx_ie)];
} bss;
u8 ap_mld_addr[ETH_ALEN];
u16 missing_links;
u8 ret;
- u8 ap_mld_id;
+ bool nontransmitted;
- memcpy(bss.bss.ies, mld_ie, sizeof(mld_ie));
- bss.bss.ie_len = sizeof(mld_ie);
+ memcpy(bss.bss.ies, rnr_ie, sizeof(rnr_ie));
+ bss.bss.ie_len = sizeof(rnr_ie);
+
+ if (mld_id == (mbssid_idx < 0 ? 0 : mbssid_idx)) {
+ memcpy(bss.bss.ies + bss.bss.ie_len, ml_ie, sizeof(ml_ie));
+ bss.bss.ie_len += sizeof(ml_ie);
+ } else {
+ memcpy(bss.bss.ies + bss.bss.ie_len, ml_ie_mld_id,
+ sizeof(ml_ie_mld_id));
+ bss.bss.ie_len += sizeof(ml_ie_mld_id);
+ }
- if (mld_id > 0) {
- memcpy(bss.bss.ies + sizeof(mld_ie), mbssid_idx_ie,
+ if (mbssid_idx > 0) {
+ memcpy(bss.bss.ies + bss.bss.ie_len, mbssid_idx_ie,
sizeof(mbssid_idx_ie));
bss.bss.ie_len += sizeof(mbssid_idx_ie);
}
ret = wpa_bss_parse_basic_ml_element(wpa_s, &bss.bss, ap_mld_addr,
- &missing_links, NULL, &ap_mld_id);
+ &missing_links, NULL,
+ &nontransmitted);
ASSERT_CMP_INT(ret, ==, 0);
ASSERT_CMP_INT(bss.bss.valid_links, ==, 1);
ASSERT_CMP_INT(missing_links, ==, 0x0002);
- ASSERT_CMP_INT(ap_mld_id, ==, mld_id);
+ ASSERT_CMP_INT(nontransmitted, ==, mbssid_idx > 0);
}
#define RUN_TEST(func, ...) do { \
@@ -89,8 +116,14 @@ int main(void)
wpa_s = wpa_supplicant_add_iface(global, &iface, NULL);
assert(wpa_s);
- RUN_TEST(test_parse_basic_ml, 0);
- RUN_TEST(test_parse_basic_ml, 1);
+ RUN_TEST(test_parse_basic_ml, 0, -1);
+ RUN_TEST(test_parse_basic_ml, 1, -1);
+ RUN_TEST(test_parse_basic_ml, 0, 0);
+ RUN_TEST(test_parse_basic_ml, 1, 0);
+ RUN_TEST(test_parse_basic_ml, 0, 1);
+ RUN_TEST(test_parse_basic_ml, 1, 1);
+ RUN_TEST(test_parse_basic_ml, 2, 0);
+ RUN_TEST(test_parse_basic_ml, 2, 1);
return 0;
}
diff --git a/wpa_supplicant/bss.c b/wpa_supplicant/bss.c
index 58adaf7449..fa5323468d 100644
--- a/wpa_supplicant/bss.c
+++ b/wpa_supplicant/bss.c
@@ -1637,7 +1637,7 @@ int wpa_bss_ext_capab(const struct wpa_bss *bss, unsigned int capab)
static void
wpa_bss_parse_ml_rnr_ap_info(struct wpa_supplicant *wpa_s,
- struct wpa_bss *bss, u8 mbssid_idx,
+ struct wpa_bss *bss, u8 ap_mld_id,
const struct ieee80211_neighbor_ap_info *ap_info,
size_t len, u16 *seen, u16 *missing,
struct wpa_ssid *ssid)
@@ -1673,7 +1673,7 @@ wpa_bss_parse_ml_rnr_ap_info(struct wpa_supplicant *wpa_s,
if (link_id >= MAX_NUM_MLD_LINKS)
continue;
- if (*mld_params != mbssid_idx) {
+ if (*mld_params != ap_mld_id) {
wpa_printf(MSG_DEBUG,
"MLD: Reported link not part of MLD");
} else if (!(BIT(link_id) & *seen)) {
@@ -1796,29 +1796,34 @@ wpa_bss_validate_rsne_ml(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
* 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)
- * @ap_mld_id: On return would hold the corresponding AP MLD ID (or %NULL)
+ * @nontransmitted: Out parameter denoting whether the BSSID is nontransmitted
+ * (or %NULL)
* Returns: 0 on success or -1 for non-MLD or parsing failures
*
* Parses the Basic Multi-Link element of the BSS into @link_info using the scan
* information stored in the wpa_supplicant data to fill in information for
* links where possible. The @missing_links out parameter will contain any links
* for which no corresponding BSS was found.
+ *
+ * The @nontransmitted out parameter can be used to create a well formatted ML
+ * Probe Request for the AP. If this out parameter is set to %true, then the
+ * 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.
*/
int wpa_bss_parse_basic_ml_element(struct wpa_supplicant *wpa_s,
struct wpa_bss *bss,
u8 *ap_mld_addr,
u16 *missing_links,
struct wpa_ssid *ssid,
- u8 *ap_mld_id)
+ bool *nontransmitted)
{
struct ieee802_11_elems elems;
struct wpabuf *mlbuf;
const struct element *elem;
- u8 mbssid_idx = 0;
size_t ml_ie_len;
const struct ieee80211_eht_ml *eht_ml;
const struct eht_ml_basic_common_info *ml_basic_common_info;
- u8 i, link_id;
+ u8 i, pos, link_id, ap_mld_id;
const u16 control_mask =
MULTI_LINK_CONTROL_TYPE_MASK |
BASIC_MULTI_LINK_CTRL_PRES_LINK_ID |
@@ -1860,10 +1865,9 @@ int wpa_bss_parse_basic_ml_element(struct wpa_supplicant *wpa_s,
}
/*
- * for ext ID + 2 control + common info len + MLD address +
- * link info
+ * for ext ID + 2 control + common info len
*/
- if (ml_ie_len < 2UL + 1UL + ETH_ALEN + 1UL)
+ if (ml_ie_len < sizeof(*eht_ml) + sizeof(*ml_basic_common_info))
goto out;
eht_ml = (const struct ieee80211_eht_ml *) wpabuf_head(mlbuf);
@@ -1878,8 +1882,41 @@ int wpa_bss_parse_basic_ml_element(struct wpa_supplicant *wpa_s,
ml_basic_common_info =
(const struct eht_ml_basic_common_info *) eht_ml->variable;
- /* Common info length should be valid */
- if (ml_basic_common_info->len < ETH_ALEN + 1UL)
+ if (ml_ie_len < sizeof(*eht_ml) + ml_basic_common_info->len)
+ goto out;
+
+ /* Minimum Common info length to be valid */
+ if (ml_basic_common_info->len <
+ sizeof(*ml_basic_common_info) + 1 + 1 + 2)
+ goto out;
+
+ /* LINK_ID, BSS_PARAM_CH_COUNT, MLD_CAPA (see control/control_mask) */
+ link_id = ml_basic_common_info->variable[0] & EHT_ML_LINK_ID_MSK;
+ pos = 1 + 1 + 2;
+
+ if (le_to_host16(eht_ml->ml_control) &
+ BASIC_MULTI_LINK_CTRL_PRES_MSD_INFO)
+ pos += 2;
+
+ if (le_to_host16(eht_ml->ml_control) &
+ BASIC_MULTI_LINK_CTRL_PRES_EML_CAPA)
+ pos += 2;
+
+ /* AP MLD ID from ML-Element if present (see comment below) */
+ if (le_to_host16(eht_ml->ml_control) &
+ BASIC_MULTI_LINK_CTRL_PRES_AP_MLD_ID) {
+ if (ml_basic_common_info->len <
+ sizeof(*ml_basic_common_info) + pos + 1)
+ goto out;
+
+ ap_mld_id = ml_basic_common_info->variable[pos];
+
+ pos += 1;
+ } else {
+ ap_mld_id = 0;
+ }
+
+ if (ml_basic_common_info->len < sizeof(*ml_basic_common_info) + pos)
goto out;
/* Get the MLD address and MLD link ID */
@@ -1897,30 +1934,57 @@ int wpa_bss_parse_basic_ml_element(struct wpa_supplicant *wpa_s,
l->freq = bss->freq;
- /*
- * The AP MLD ID in the RNR corresponds to the MBSSID index, see
- * IEEE P802.11be/D4.0, 9.4.2.169.2 (Neighbor AP Information field).
- *
- * For the transmitting BSSID it is clear that both the MBSSID index
- * and the AP MLD ID in the RNR are zero.
- *
- * For nontransmitted BSSIDs we will have a BSS generated from the
- * MBSSID element(s) using inheritance rules. Included in the elements
- * is the MBSSID Index Element. The RNR is copied from the Beacon/Probe
- * Response frame that was send by the transmitting BSSID. As such, the
- * reported AP MLD ID in the RNR will match the value in the MBSSID
- * Index Element.
- */
- elem = (const struct element *)
- wpa_bss_get_ie(bss, WLAN_EID_MULTIPLE_BSSID_INDEX);
- if (elem && elem->datalen >= 1)
- mbssid_idx = elem->data[0];
+ /* 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;
+ }
+ }
for_each_element_id(elem, WLAN_EID_REDUCED_NEIGHBOR_REPORT,
wpa_bss_ie_ptr(bss),
bss->ie_len ? bss->ie_len : bss->beacon_ie_len) {
const struct ieee80211_neighbor_ap_info *ap_info;
- const u8 *pos = elem->data;
+ const u8 *ap_info_pos = elem->data;
size_t len = elem->datalen;
/* RNR IE may contain more than one Neighbor AP Info */
@@ -1929,18 +1993,18 @@ int wpa_bss_parse_basic_ml_element(struct wpa_supplicant *wpa_s,
u8 count;
ap_info = (const struct ieee80211_neighbor_ap_info *)
- pos;
+ ap_info_pos;
count = RNR_TBTT_INFO_COUNT_VAL(ap_info->tbtt_info_hdr) + 1;
ap_info_len += count * ap_info->tbtt_info_len;
if (ap_info_len > len)
goto out;
- wpa_bss_parse_ml_rnr_ap_info(wpa_s, bss, mbssid_idx,
+ wpa_bss_parse_ml_rnr_ap_info(wpa_s, bss, ap_mld_id,
ap_info, ap_info_len,
&seen, &missing, ssid);
- pos += ap_info_len;
+ ap_info_pos += ap_info_len;
len -= ap_info_len;
}
}
@@ -1994,8 +2058,6 @@ int wpa_bss_parse_basic_ml_element(struct wpa_supplicant *wpa_s,
if (missing_links)
*missing_links = missing;
- if (ap_mld_id)
- *ap_mld_id = mbssid_idx;
ret = 0;
out:
diff --git a/wpa_supplicant/bss.h b/wpa_supplicant/bss.h
index 74ef3c8587..b75ae06b7a 100644
--- a/wpa_supplicant/bss.h
+++ b/wpa_supplicant/bss.h
@@ -222,7 +222,7 @@ int wpa_bss_parse_basic_ml_element(struct wpa_supplicant *wpa_s,
u8 *ap_mld_addr,
u16 *missing_links,
struct wpa_ssid *ssid,
- u8 *ap_mld_id);
+ bool *nontransmitted);
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 00b733a1c8..39a4e0b226 100644
--- a/wpa_supplicant/events.c
+++ b/wpa_supplicant/events.c
@@ -1889,7 +1889,7 @@ static int wpa_supplicant_connect_ml_missing(struct wpa_supplicant *wpa_s,
{
int *freqs;
u16 missing_links = 0, removed_links;
- u8 ap_mld_id;
+ bool nontransmitted;
if (!((wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_MLO) &&
(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME)))
@@ -1897,7 +1897,7 @@ static int wpa_supplicant_connect_ml_missing(struct wpa_supplicant *wpa_s,
if (wpa_bss_parse_basic_ml_element(wpa_s, selected, NULL,
&missing_links, ssid,
- &ap_mld_id) ||
+ &nontransmitted) ||
!missing_links)
return 0;
@@ -1937,7 +1937,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 (ap_mld_id)
+ if (nontransmitted)
wpa_s->ml_probe_mld_id = -1;
else
wpa_s->ml_probe_mld_id = 0;
--
2.49.0
More information about the Hostap
mailing list