[PATCH 2/3] MLD: Treat stale per-link scan entries as missing links

Louis Kotze loukot at gmail.com
Thu Jul 9 12:10:32 PDT 2026


SME-based association with an AP MLD requires the driver to have a
sufficiently recent scan entry for every link included in the
association request. With nl80211-based drivers, cfg80211 rejects the
association if the BSS entry for any requested link is older than 30
seconds (IEEE80211_SCAN_RESULT_EXPIRE) and the attempt fails before any
frame goes out:

nl80211: kernel reports: Error fetching BSS for link
wlan0: nl80211: MLME command failed (assoc): ret=-2 (No such file or directory)

wpa_supplicant already handles missing per-link information by sending
an ML probe request before association
(wpa_supplicant_connect_ml_missing()). However, a link was considered
missing only if its BSS entry was absent from the local BSS table,
which keeps entries much longer (bss_expiration_age, 180 seconds by
default) than the kernel accepts them for association. In addition, the
entries for the links of the current AP MLD are not refreshed while
associated, so on reassociation or roaming the affiliated link entries
can be arbitrarily old.

As a result, an association attempt where a partner link was last seen
more than 30 seconds earlier included that link based on the stale
local entry, failed in the kernel with -ENOENT, and ended up
ignore-listing the partner link BSSID for 10 seconds. The following
attempt then came up without that link, i.e., the connection silently
degraded to a single link. Idle associations hit this on essentially
every reassociation, and a 6 GHz affiliated link makes recovery harder
since passive scanning does not reliably refresh its entry.

Fix this by considering an affiliated link with a scan entry older than
25 seconds to be missing in wpa_bss_get_usable_links(), leaving some
margin below the kernel limit to cover the time needed for
authentication. This makes wpa_supplicant_connect_ml_missing() use the
existing ML probe request mechanism to refresh the information before
association. Should the entry still be stale when setting up the
association links (e.g., if the AP does not respond to the ML probe
request), the link is now excluded from the association request instead
of causing the whole association attempt to fail.

Signed-off-by: Louis Kotze <loukot at gmail.com>
---
 wpa_supplicant/bss.c | 28 ++++++++++++++++++++++++++++
 1 file changed, 28 insertions(+)

diff --git a/wpa_supplicant/bss.c b/wpa_supplicant/bss.c
index 4c0d818ce..5740453df 100644
--- a/wpa_supplicant/bss.c
+++ b/wpa_supplicant/bss.c
@@ -1782,6 +1782,20 @@ wpa_bss_validate_rsne_ml(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
 }
 
 
+/*
+ * SME-based association with an AP MLD requires the driver to have a
+ * sufficiently recent scan entry for every requested link. With nl80211-based
+ * drivers, cfg80211 rejects the association if the BSS entry for any link is
+ * older than 30 seconds (IEEE80211_SCAN_RESULT_EXPIRE). Since the local BSS
+ * table keeps entries considerably longer than that (bss_expiration_age,
+ * 180 seconds by default) and does not refresh entries for the links of the
+ * current AP MLD while associated, consider an affiliated link with an older
+ * scan entry to be missing so that an ML probe request gets used to refresh
+ * the information before association. Leave some margin below the kernel
+ * limit to cover the time needed for authentication.
+ */
+#define WPA_BSS_ML_LINK_FRESH_AGE 25 /* seconds */
+
 /**
  * wpa_bss_get_usable_links - Retrieve the usable links of the AP MLD
  * @wpa_s: Pointer to wpa_supplicant data
@@ -1797,6 +1811,7 @@ u16 wpa_bss_get_usable_links(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
 			     struct wpa_ssid *ssid, u16 *missing_links)
 {
 	struct wpa_ie_data rsne;
+	struct os_reltime now;
 	int rsne_type;
 	u16 usable_links = 0;
 	u8 link_id;
@@ -1804,6 +1819,8 @@ u16 wpa_bss_get_usable_links(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
 	if (!bss->valid_links)
 		return 0;
 
+	os_get_reltime(&now);
+
 	rsne_type = -1;
 	os_memset(&rsne, 0, sizeof(rsne));
 	if (ssid &&
@@ -1836,6 +1853,17 @@ u16 wpa_bss_get_usable_links(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
 			continue;
 		}
 
+		if (os_reltime_expired(&now, &neigh_bss->last_update,
+				       WPA_BSS_ML_LINK_FRESH_AGE)) {
+			wpa_dbg(wpa_s, MSG_DEBUG,
+				"MLD: Scan entry for neighbor " MACSTR
+				" is too old for association; consider link %u missing",
+				MAC2STR(neigh_bss->bssid), link_id);
+			if (missing_links)
+				*missing_links |= BIT(link_id);
+			continue;
+		}
+
 		/* Check that the affiliated links are for the same AP MLD and
 		 * the information matches */
 		if (!neigh_bss->valid_links) {
-- 
2.55.0




More information about the Hostap mailing list