[PATCH 3/9] BSS: Set valid_links for all links and return usable links

Benjamin Berg benjamin at sipsolutions.net
Wed Jun 18 05:35:25 PDT 2025


From: Benjamin Berg <benjamin.berg at intel.com>

This commit is a preparation to better define valid_links in the bss
structure and move parsing to wpa_bss_update. Before this, the value
of valid_links would depend on whether a neighbor is known and if an
SSID struct was passed to the parser.

With this change, valid_links is purely defined on whether there is an
entry in the RNR for the corresponding link.

Signed-off-by: Benjamin Berg <benjamin.berg at intel.com>
---
 tests/test-bss.c        |  4 +--
 wpa_supplicant/bss.c    | 62 ++++++++++++++++++++---------------------
 wpa_supplicant/bss.h    |  4 +--
 wpa_supplicant/events.c | 14 ++++------
 wpa_supplicant/sme.c    | 21 ++++++++++----
 5 files changed, 56 insertions(+), 49 deletions(-)

diff --git a/tests/test-bss.c b/tests/test-bss.c
index 0b82bd6e9d..10bcd17787 100644
--- a/tests/test-bss.c
+++ b/tests/test-bss.c
@@ -88,8 +88,8 @@ void test_parse_basic_ml(struct wpa_supplicant *wpa_s, u8 mld_id,
 					     &missing_links, NULL,
 					     &nontransmitted);
 
-	ASSERT_CMP_INT(ret, ==, 0);
-	ASSERT_CMP_INT(bss.bss.valid_links, ==, 1);
+	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);
 }
diff --git a/wpa_supplicant/bss.c b/wpa_supplicant/bss.c
index 711dc7db26..b8fe141935 100644
--- a/wpa_supplicant/bss.c
+++ b/wpa_supplicant/bss.c
@@ -1639,7 +1639,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 *missing,
+			     size_t len, u16 *seen, u16 *usable, u16 *missing,
 			     struct wpa_ssid *ssid)
 {
 	const u8 *pos, *end;
@@ -1678,6 +1678,7 @@ wpa_bss_parse_ml_rnr_ap_info(struct wpa_supplicant *wpa_s,
 				   "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,
@@ -1690,6 +1691,15 @@ wpa_bss_parse_ml_rnr_ap_info(struct wpa_supplicant *wpa_s,
 			wpa_printf(MSG_DEBUG, "MLD: mld ID=%u, link ID=%u",
 				   *mld_params, link_id);
 
+			bss->valid_links |= BIT(link_id);
+			l = &bss->mld_links[link_id];
+			os_memcpy(l->bssid, pos + 1, ETH_ALEN);
+			l->disabled = mld_params[2] &
+				RNR_TBTT_INFO_MLD_PARAM2_LINK_DISABLED;
+			l->freq = ieee80211_chan_to_freq(NULL,
+							 ap_info->op_class,
+							 ap_info->channel);
+
 			if (!neigh_bss) {
 				*missing |= BIT(link_id);
 			} else if ((!ssid ||
@@ -1697,14 +1707,8 @@ wpa_bss_parse_ml_rnr_ap_info(struct wpa_supplicant *wpa_s,
 						       ssid, 1, 0, true)) &&
 				   !wpa_bssid_ignore_is_listed(
 					   wpa_s, neigh_bss->bssid)) {
-				struct mld_link *l;
-
-				bss->valid_links |= BIT(link_id);
-				l = &bss->mld_links[link_id];
-				os_memcpy(l->bssid, pos + 1, ETH_ALEN);
 				l->freq = neigh_bss->freq;
-				l->disabled = mld_params[2] &
-					RNR_TBTT_INFO_MLD_PARAM2_LINK_DISABLED;
+				*usable |= BIT(link_id);
 			}
 		}
 	}
@@ -1797,7 +1801,7 @@ wpa_bss_validate_rsne_ml(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
  * @ssid: Target SSID (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
+ * 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
  * information stored in the wpa_supplicant data to fill in information for
@@ -1809,14 +1813,14 @@ 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.
  */
-int wpa_bss_parse_basic_ml_element(struct wpa_supplicant *wpa_s,
+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)
 {
 	struct ieee802_11_elems elems;
-	struct wpabuf *mlbuf;
+	struct wpabuf *mlbuf = NULL;
 	const struct element *elem;
 	size_t ml_ie_len;
 	const struct ieee80211_eht_ml *eht_ml;
@@ -1833,23 +1837,22 @@ int wpa_bss_parse_basic_ml_element(struct wpa_supplicant *wpa_s,
 		BASIC_MULTI_LINK_CTRL_PRES_BSS_PARAM_CH_COUNT |
 		BASIC_MULTI_LINK_CTRL_PRES_MLD_CAPA;
 	u16 missing = 0;
-	u16 seen;
+	u16 seen, usable = 0;
 	const u8 *ies_pos = wpa_bss_ie_ptr(bss);
 	size_t ies_len = bss->ie_len ? bss->ie_len : bss->beacon_ie_len;
-	int ret = -1, rsne_type, key_mgmt;
+	int rsne_type, key_mgmt;
 	struct mld_link *l;
-	u16 valid_links;
 
 	if (ieee802_11_parse_elems(ies_pos, ies_len, &elems, 1) ==
 	    ParseFailed) {
 		wpa_dbg(wpa_s, MSG_DEBUG, "MLD: Failed to parse elements");
-		return ret;
+		goto out;
 	}
 
 	mlbuf = ieee802_11_defrag(elems.basic_mle, elems.basic_mle_len, true);
 	if (!mlbuf) {
 		wpa_dbg(wpa_s, MSG_DEBUG, "MLD: No Multi-Link element");
-		return ret;
+		goto out;
 	}
 
 	ml_ie_len = wpabuf_len(mlbuf);
@@ -1920,7 +1923,8 @@ int wpa_bss_parse_basic_ml_element(struct wpa_supplicant *wpa_s,
 	link_id = ml_basic_common_info->variable[0] & EHT_ML_LINK_ID_MSK;
 
 	bss->mld_link_id = link_id;
-	seen = bss->valid_links = BIT(link_id);
+	bss->valid_links = BIT(link_id);
+	usable = seen = bss->valid_links;
 
 	l = &bss->mld_links[link_id];
 	os_memcpy(l->bssid, bss->bssid, ETH_ALEN);
@@ -1995,18 +1999,15 @@ int wpa_bss_parse_basic_ml_element(struct wpa_supplicant *wpa_s,
 
 			wpa_bss_parse_ml_rnr_ap_info(wpa_s, bss, ap_mld_id,
 						     ap_info, ap_info_len,
-						     &seen, &missing, ssid);
+						     &seen, &usable, &missing,
+						     ssid);
 
 			ap_info_pos += ap_info_len;
 			len -= ap_info_len;
 		}
 	}
 
-	wpa_printf(MSG_DEBUG, "MLD: valid_links=%04hx (unresolved: 0x%04hx)",
-		   bss->valid_links, missing);
-
-	valid_links = bss->valid_links;
-	for_each_link(bss->valid_links, i) {
+	for_each_link(usable, i) {
 		struct wpa_bss *neigh_bss;
 		int neigh_key_mgmt;
 
@@ -2032,16 +2033,13 @@ int wpa_bss_parse_basic_ml_element(struct wpa_supplicant *wpa_s,
 			wpa_printf(MSG_DEBUG,
 				   "MLD: Discard link %u due to RSN parameter mismatch",
 				   i);
-			valid_links &= ~BIT(i);
+			usable &= ~BIT(i);
 			continue;
 		}
 	}
 
-	if (valid_links != bss->valid_links) {
-		wpa_printf(MSG_DEBUG, "MLD: Updated valid links=%04hx",
-			   valid_links);
-		bss->valid_links = valid_links;
-	}
+	wpa_printf(MSG_DEBUG, "MLD: valid_links=0x%04hx usable=0x%04hx (unresolved: 0x%04hx)",
+		   bss->valid_links, usable, missing);
 
 	for_each_link(bss->valid_links, i) {
 		wpa_printf(MSG_DEBUG, "MLD: link=%u, bssid=" MACSTR,
@@ -2051,11 +2049,13 @@ int wpa_bss_parse_basic_ml_element(struct wpa_supplicant *wpa_s,
 	if (missing_links)
 		*missing_links = missing;
 
+	wpabuf_free(mlbuf);
+	return usable;
 
-	ret = 0;
 out:
+	bss->valid_links = 0;
 	wpabuf_free(mlbuf);
-	return ret;
+	return 0;
 }
 
 
diff --git a/wpa_supplicant/bss.h b/wpa_supplicant/bss.h
index fd560a9a8d..f01accc50b 100644
--- a/wpa_supplicant/bss.h
+++ b/wpa_supplicant/bss.h
@@ -126,7 +126,7 @@ struct wpa_bss {
 	/** Link ID of this affiliated AP of the AP MLD */
 	u8 mld_link_id;
 
-	/** An array of MLD links */
+	/** An array of MLD links, any link found in the RNR is "valid" */
 	u16 valid_links;
 	struct mld_link {
 		u8 bssid[ETH_ALEN];
@@ -217,7 +217,7 @@ void calculate_update_time(const struct os_reltime *fetch_time,
 			   unsigned int age_ms,
 			   struct os_reltime *update_time);
 
-int wpa_bss_parse_basic_ml_element(struct wpa_supplicant *wpa_s,
+u16 wpa_bss_parse_basic_ml_element(struct wpa_supplicant *wpa_s,
 				   struct wpa_bss *bss,
 				   u16 *missing_links,
 				   struct wpa_ssid *ssid,
diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c
index fe9e358f5d..b8db56921d 100644
--- a/wpa_supplicant/events.c
+++ b/wpa_supplicant/events.c
@@ -1176,9 +1176,7 @@ 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, NULL))
-		return true;
-
+	wpa_bss_parse_basic_ml_element(wpa_s, bss, NULL, NULL, NULL);
 	if (!bss->valid_links)
 		return true;
 
@@ -1888,17 +1886,17 @@ static int wpa_supplicant_connect_ml_missing(struct wpa_supplicant *wpa_s,
 					     struct wpa_ssid *ssid)
 {
 	int *freqs;
-	u16 missing_links = 0, removed_links;
+	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;
 
-	if (wpa_bss_parse_basic_ml_element(wpa_s, selected,
-					   &missing_links, ssid,
-					   &nontransmitted) ||
-	    !missing_links)
+	usable_links = wpa_bss_parse_basic_ml_element(wpa_s, selected,
+						      &missing_links, ssid,
+						      &nontransmitted);
+	if (!usable_links || !missing_links)
 		return 0;
 
 	removed_links = wpa_bss_parse_reconf_ml_element(wpa_s, selected);
diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c
index d32d315766..50b3d635df 100644
--- a/wpa_supplicant/sme.c
+++ b/wpa_supplicant/sme.c
@@ -526,13 +526,24 @@ static int wpas_sme_ml_auth(struct wpa_supplicant *wpa_s,
 static void wpas_sme_set_mlo_links(struct wpa_supplicant *wpa_s,
 				   struct wpa_bss *bss, struct wpa_ssid *ssid)
 {
+	u16 usable_links;
 	u8 i;
 
+	wpas_reset_mlo_info(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);
+	if (!usable_links)
+		return;
+
 	os_memcpy(wpa_s->ap_mld_addr, bss->mld_addr, ETH_ALEN);
 	wpa_s->valid_links = 0;
 	wpa_s->mlo_assoc_link_id = bss->mld_link_id;
 
-	for_each_link(bss->valid_links, i) {
+	for_each_link(usable_links, i) {
 		const u8 *bssid = bss->mld_links[i].bssid;
 
 		wpa_s->valid_links |= BIT(i);
@@ -613,12 +624,10 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s,
 
 	os_memset(&params, 0, sizeof(params));
 
-	if ((wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_MLO) &&
-	    !wpa_bss_parse_basic_ml_element(wpa_s, bss, NULL,
-					    ssid, NULL) &&
-	    bss->valid_links) {
+	wpas_sme_set_mlo_links(wpa_s, bss, ssid);
+
+	if (wpa_s->valid_links) {
 		wpa_printf(MSG_DEBUG, "MLD: In authentication");
-		wpas_sme_set_mlo_links(wpa_s, bss, ssid);
 
 #ifdef CONFIG_TESTING_OPTIONS
 		bss = wpas_ml_connect_pref(wpa_s, bss, ssid);
-- 
2.49.0




More information about the Hostap mailing list