* [PATCH] wifi: nl80211: say why the association BSS lookup failed
@ 2026-07-11 4:34 Louis Kotze
0 siblings, 0 replies; only message in thread
From: Louis Kotze @ 2026-07-11 4:34 UTC (permalink / raw)
To: Johannes Berg; +Cc: linux-wireless, linux-kernel, loukot
The BSS lookup for an association request can fail for three distinct
reasons: cfg80211 has no scan entry at all for the BSSID/channel, an
entry exists but is older than IEEE80211_SCAN_RESULT_EXPIRE (and not
held), or a fresh entry exists but its use_for flags do not allow
this use. All three currently surface as the same generic extack
message "Error fetching BSS for link" on the MLO path, and as a bare
-ENOENT with no message on the non-MLO path.
Since wpa_supplicant logs the extack message verbatim ("nl80211:
kernel reports: ..."), that message is often the only diagnostic a
user sees when an MLO association degrades to fewer links, and it
does not say whether rescanning would help. In practice the expired
case is common for MLO partner links: 6 GHz is passive-scan in many
regulatory domains, so the partner-link entry is routinely stale by
the time userspace requests the association even though the link is
perfectly usable.
Add a cfg80211 helper that classifies, on the failure path only, why
matching entries were rejected, and set a distinct extack message
for each case in nl80211_assoc_bss(). Also give the -EINVAL paths
there proper messages while at it, and keep pointing the bad_attr at
the failing link.
Signed-off-by: Louis Kotze <loukot@gmail.com>
---
Compile-tested (W=1 clean) and exercised on a UML/hwsim rig: an MLO
reassociation whose partner-link entry had aged out now gets
"nl80211: kernel reports: BSS not found in scan results" in the
wpa_supplicant debug log instead of the generic message.
net/wireless/core.h | 12 +++++++++++
net/wireless/nl80211.c | 37 +++++++++++++++++++++++--------
net/wireless/scan.c | 49 ++++++++++++++++++++++++++++++++++++++++++
3 files changed, 89 insertions(+), 9 deletions(-)
diff --git a/net/wireless/core.h b/net/wireless/core.h
index df47ed6208a5..a3778c6e07e4 100644
--- a/net/wireless/core.h
+++ b/net/wireless/core.h
@@ -332,6 +332,18 @@ void ieee80211_set_bitrate_flags(struct wiphy *wiphy);
void cfg80211_bss_expire(struct cfg80211_registered_device *rdev);
void cfg80211_bss_age(struct cfg80211_registered_device *rdev,
unsigned long age_secs);
+
+/* why __cfg80211_get_bss() found no usable BSS entry */
+#define CFG80211_BSS_MISS_EXPIRED BIT(0)
+#define CFG80211_BSS_MISS_USE_FOR BIT(1)
+
+u32 cfg80211_get_bss_miss_reasons(struct wiphy *wiphy,
+ struct ieee80211_channel *channel,
+ const u8 *bssid,
+ const u8 *ssid, size_t ssid_len,
+ enum ieee80211_bss_type bss_type,
+ enum ieee80211_privacy privacy,
+ u32 use_for);
void cfg80211_update_assoc_bss_entry(struct wireless_dev *wdev,
unsigned int link,
struct ieee80211_channel *channel);
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 242071ad10d6..a68390ae6d2c 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -13036,6 +13036,7 @@ static int nl80211_crypto_settings(struct cfg80211_registered_device *rdev,
}
static struct cfg80211_bss *nl80211_assoc_bss(struct cfg80211_registered_device *rdev,
+ struct genl_info *info,
const u8 *ssid, int ssid_len,
struct nlattr **attrs,
int assoc_link_id, int link_id)
@@ -13043,10 +13044,12 @@ static struct cfg80211_bss *nl80211_assoc_bss(struct cfg80211_registered_device
struct ieee80211_channel *chan;
struct cfg80211_bss *bss;
const u8 *bssid;
- u32 freq, use_for = 0;
+ u32 freq, miss, use_for = 0;
- if (!attrs[NL80211_ATTR_MAC] || !attrs[NL80211_ATTR_WIPHY_FREQ])
+ if (!attrs[NL80211_ATTR_MAC] || !attrs[NL80211_ATTR_WIPHY_FREQ]) {
+ NL_SET_ERR_MSG(info->extack, "BSSID or frequency missing");
return ERR_PTR(-EINVAL);
+ }
bssid = nla_data(attrs[NL80211_ATTR_MAC]);
@@ -13055,8 +13058,10 @@ static struct cfg80211_bss *nl80211_assoc_bss(struct cfg80211_registered_device
freq += nla_get_u32(attrs[NL80211_ATTR_WIPHY_FREQ_OFFSET]);
chan = nl80211_get_valid_chan(&rdev->wiphy, freq);
- if (!chan)
+ if (!chan) {
+ NL_SET_ERR_MSG(info->extack, "invalid or disabled channel");
return ERR_PTR(-EINVAL);
+ }
if (assoc_link_id >= 0)
use_for = NL80211_BSS_USE_FOR_MLD_LINK;
@@ -13068,8 +13073,23 @@ static struct cfg80211_bss *nl80211_assoc_bss(struct cfg80211_registered_device
IEEE80211_BSS_TYPE_ESS,
IEEE80211_PRIVACY_ANY,
use_for);
- if (!bss)
+ if (!bss) {
+ miss = cfg80211_get_bss_miss_reasons(&rdev->wiphy, chan,
+ bssid, ssid, ssid_len,
+ IEEE80211_BSS_TYPE_ESS,
+ IEEE80211_PRIVACY_ANY,
+ use_for);
+ if (miss & CFG80211_BSS_MISS_USE_FOR)
+ NL_SET_ERR_MSG(info->extack,
+ "BSS cannot be used for this association");
+ else if (miss & CFG80211_BSS_MISS_EXPIRED)
+ NL_SET_ERR_MSG(info->extack,
+ "BSS entry is expired, scan again");
+ else
+ NL_SET_ERR_MSG(info->extack,
+ "BSS not found in scan results");
return ERR_PTR(-ENOENT);
+ }
return bss;
}
@@ -13106,13 +13126,12 @@ static int nl80211_process_links(struct cfg80211_registered_device *rdev,
return -EINVAL;
}
links[link_id].bss =
- nl80211_assoc_bss(rdev, ssid, ssid_len, attrs,
+ nl80211_assoc_bss(rdev, info, ssid, ssid_len, attrs,
assoc_link_id, link_id);
if (IS_ERR(links[link_id].bss)) {
err = PTR_ERR(links[link_id].bss);
links[link_id].bss = NULL;
- NL_SET_ERR_MSG_ATTR(info->extack, link,
- "Error fetching BSS for link");
+ NL_SET_BAD_ATTR(info->extack, link);
return err;
}
@@ -13328,8 +13347,8 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info)
if (req.link_id >= 0)
return -EINVAL;
- req.bss = nl80211_assoc_bss(rdev, ssid, ssid_len, info->attrs,
- -1, -1);
+ req.bss = nl80211_assoc_bss(rdev, info, ssid, ssid_len,
+ info->attrs, -1, -1);
if (IS_ERR(req.bss))
return PTR_ERR(req.bss);
ap_addr = req.bss->bssid;
diff --git a/net/wireless/scan.c b/net/wireless/scan.c
index 05b7dc6b766c..f0e13b178b74 100644
--- a/net/wireless/scan.c
+++ b/net/wireless/scan.c
@@ -1655,6 +1655,55 @@ struct cfg80211_bss *__cfg80211_get_bss(struct wiphy *wiphy,
}
EXPORT_SYMBOL(__cfg80211_get_bss);
+/*
+ * Report why __cfg80211_get_bss() with the same arguments found no
+ * usable entry: matching entries exist but are expired, or are not
+ * usable for the requested use. Returns 0 if no entry matches at all.
+ */
+u32 cfg80211_get_bss_miss_reasons(struct wiphy *wiphy,
+ struct ieee80211_channel *channel,
+ const u8 *bssid,
+ const u8 *ssid, size_t ssid_len,
+ enum ieee80211_bss_type bss_type,
+ enum ieee80211_privacy privacy,
+ u32 use_for)
+{
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
+ struct cfg80211_internal_bss *bss;
+ unsigned long now = jiffies;
+ u32 reasons = 0;
+ int bss_privacy;
+
+ spin_lock_bh(&rdev->bss_lock);
+
+ list_for_each_entry(bss, &rdev->bss_list, list) {
+ if (!cfg80211_bss_type_match(bss->pub.capability,
+ bss->pub.channel->band, bss_type))
+ continue;
+
+ bss_privacy = (bss->pub.capability & WLAN_CAPABILITY_PRIVACY);
+ if ((privacy == IEEE80211_PRIVACY_ON && !bss_privacy) ||
+ (privacy == IEEE80211_PRIVACY_OFF && bss_privacy))
+ continue;
+ if (channel && bss->pub.channel != channel)
+ continue;
+ if (!is_valid_ether_addr(bss->pub.bssid))
+ continue;
+ if (!is_bss(&bss->pub, bssid, ssid, ssid_len))
+ continue;
+
+ if (time_after(now, bss->ts + IEEE80211_SCAN_RESULT_EXPIRE) &&
+ !atomic_read(&bss->hold))
+ reasons |= CFG80211_BSS_MISS_EXPIRED;
+ else if ((bss->pub.use_for & use_for) != use_for)
+ reasons |= CFG80211_BSS_MISS_USE_FOR;
+ }
+
+ spin_unlock_bh(&rdev->bss_lock);
+
+ return reasons;
+}
+
static bool rb_insert_bss(struct cfg80211_registered_device *rdev,
struct cfg80211_internal_bss *bss)
{
base-commit: ac798f757d6475dc6fee2ec899980d6740714596
--
2.55.0
^ permalink raw reply related [flat|nested] only message in thread
only message in thread, other threads:[~2026-07-11 4:34 UTC | newest]
Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-11 4:34 [PATCH] wifi: nl80211: say why the association BSS lookup failed Louis Kotze
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox