mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Louis Kotze <loukot@gmail.com>
To: Johannes Berg <johannes@sipsolutions.net>
Cc: linux-wireless@vger.kernel.org, linux-kernel@vger.kernel.org,
	loukot@gmail.com
Subject: [PATCH v3 1/2] wifi: cfg80211: say why the auth/assoc BSS lookup failed
Date: Wed, 22 Jul 2026 09:07:33 +0200	[thread overview]
Message-ID: <20260722070734.3612581-2-loukot@gmail.com> (raw)
In-Reply-To: <20260722070734.3612581-1-loukot@gmail.com>

The BSS lookup for an authentication or 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 association path, and as a bare -ENOENT with no
message at all on the authentication and non-MLO association paths.

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 a fresh scan could have helped. 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.

Let __cfg80211_get_bss() take an optional extack and record, during
the same bss_lock walk that fails the lookup, whether any matching
entry was rejected for being expired or for not being usable for the
requested use, and set a distinct message for each case (and a
combined one when different entries were rejected for different
reasons). Reorder the checks in the walk so that an entry's identity
(type, privacy, channel, BSSID/SSID) is established before the
usability checks; this doesn't change which entry is returned since
an entry is only used when all checks pass.

Also give the -EINVAL paths in nl80211_assoc_bss() proper messages
while at it, and keep pointing the bad_attr at the failing link on
the MLO path there; the message for that case is already set by the
lookup itself.

Signed-off-by: Louis Kotze <loukot@gmail.com>
---
v3: use GENL_SET_ERR_MSG for the -EINVAL messages; comment the bare
NL_SET_BAD_ATTR (the lookup already set the specific message);
comment the check ordering in the walk; combined message when
matching entries were rejected for different reasons; drop the
"scan again" instruction; minimal diff at the nl80211_associate()
call site.

v2: capture the reason inside __cfg80211_get_bss() during the single
bss_lock walk (per Johannes' feedback on v1); cover the auth path;
KUnit test added as patch 2.

 include/net/cfg80211.h    |  7 +++++--
 net/wireless/nl80211.c    | 27 +++++++++++++++---------
 net/wireless/scan.c       | 44 ++++++++++++++++++++++++++++++++-------
 net/wireless/tests/scan.c |  2 +-
 4 files changed, 59 insertions(+), 21 deletions(-)

diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index b8e9fbb89e69..15c08b24502f 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -8424,6 +8424,8 @@ cfg80211_inform_bss(struct wiphy *wiphy,
  * @bss_type: type of BSS, see &enum ieee80211_bss_type
  * @privacy: privacy filter, see &enum ieee80211_privacy
  * @use_for: indicates which use is intended
+ * @extack: (optional) extack that is filled with the reason when no
+ *	usable entry was found; may be %NULL
  *
  * Return: Reference-counted BSS on success. %NULL on error.
  */
@@ -8433,7 +8435,8 @@ struct cfg80211_bss *__cfg80211_get_bss(struct wiphy *wiphy,
 					const u8 *ssid, size_t ssid_len,
 					enum ieee80211_bss_type bss_type,
 					enum ieee80211_privacy privacy,
-					u32 use_for);
+					u32 use_for,
+					struct netlink_ext_ack *extack);
 
 /**
  * cfg80211_get_bss - get a BSS reference
@@ -8457,7 +8460,7 @@ cfg80211_get_bss(struct wiphy *wiphy, struct ieee80211_channel *channel,
 {
 	return __cfg80211_get_bss(wiphy, channel, bssid, ssid, ssid_len,
 				  bss_type, privacy,
-				  NL80211_BSS_USE_FOR_NORMAL);
+				  NL80211_BSS_USE_FOR_NORMAL, NULL);
 }
 
 static inline struct cfg80211_bss *
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index d962b5944533..ac0c0da45241 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -12890,9 +12890,11 @@ static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info)
 			return -EINVAL;
 	}
 
-	req.bss = cfg80211_get_bss(&rdev->wiphy, chan, bssid, ssid, ssid_len,
-				   IEEE80211_BSS_TYPE_ESS,
-				   IEEE80211_PRIVACY_ANY);
+	req.bss = __cfg80211_get_bss(&rdev->wiphy, chan, bssid, ssid, ssid_len,
+				     IEEE80211_BSS_TYPE_ESS,
+				     IEEE80211_PRIVACY_ANY,
+				     NL80211_BSS_USE_FOR_NORMAL,
+				     info->extack);
 	if (!req.bss)
 		return -ENOENT;
 
@@ -13037,6 +13039,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)
@@ -13046,8 +13049,10 @@ static struct cfg80211_bss *nl80211_assoc_bss(struct cfg80211_registered_device
 	const u8 *bssid;
 	u32 freq, use_for = 0;
 
-	if (!attrs[NL80211_ATTR_MAC] || !attrs[NL80211_ATTR_WIPHY_FREQ])
+	if (!attrs[NL80211_ATTR_MAC] || !attrs[NL80211_ATTR_WIPHY_FREQ]) {
+		GENL_SET_ERR_MSG(info, "BSSID or frequency missing");
 		return ERR_PTR(-EINVAL);
+	}
 
 	bssid = nla_data(attrs[NL80211_ATTR_MAC]);
 
@@ -13056,8 +13061,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) {
+		GENL_SET_ERR_MSG(info, "invalid or disabled channel");
 		return ERR_PTR(-EINVAL);
+	}
 
 	if (assoc_link_id >= 0)
 		use_for = NL80211_BSS_USE_FOR_MLD_LINK;
@@ -13068,7 +13075,7 @@ static struct cfg80211_bss *nl80211_assoc_bss(struct cfg80211_registered_device
 				 ssid, ssid_len,
 				 IEEE80211_BSS_TYPE_ESS,
 				 IEEE80211_PRIVACY_ANY,
-				 use_for);
+				 use_for, info->extack);
 	if (!bss)
 		return ERR_PTR(-ENOENT);
 
@@ -13107,13 +13114,13 @@ 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");
+			/* the BSS lookup set the specific message already */
+			NL_SET_BAD_ATTR(info->extack, link);
 			return err;
 		}
 
@@ -13329,7 +13336,7 @@ 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,
+		req.bss = nl80211_assoc_bss(rdev, info, ssid, ssid_len, info->attrs,
 					    -1, -1);
 		if (IS_ERR(req.bss))
 			return PTR_ERR(req.bss);
diff --git a/net/wireless/scan.c b/net/wireless/scan.c
index e62b7dd2b7c2..90a4f285654f 100644
--- a/net/wireless/scan.c
+++ b/net/wireless/scan.c
@@ -1609,10 +1609,12 @@ struct cfg80211_bss *__cfg80211_get_bss(struct wiphy *wiphy,
 					const u8 *ssid, size_t ssid_len,
 					enum ieee80211_bss_type bss_type,
 					enum ieee80211_privacy privacy,
-					u32 use_for)
+					u32 use_for,
+					struct netlink_ext_ack *extack)
 {
 	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
 	struct cfg80211_internal_bss *bss, *res = NULL;
+	bool expired = false, unusable = false;
 	unsigned long now = jiffies;
 	int bss_privacy;
 
@@ -1634,22 +1636,48 @@ struct cfg80211_bss *__cfg80211_get_bss(struct wiphy *wiphy,
 			continue;
 		if (!is_valid_ether_addr(bss->pub.bssid))
 			continue;
-		if ((bss->pub.use_for & use_for) != use_for)
+		if (!is_bss(&bss->pub, bssid, ssid, ssid_len))
 			continue;
+
+		/*
+		 * The identity checks above must all come first so that
+		 * the expired/unusable classification below only ever
+		 * applies to entries that actually match the request.
+		 */
+
 		/* Don't get expired BSS structs */
 		if (time_after(now, bss->ts + IEEE80211_SCAN_RESULT_EXPIRE) &&
-		    !atomic_read(&bss->hold))
+		    !atomic_read(&bss->hold)) {
+			expired = true;
+			continue;
+		}
+
+		if ((bss->pub.use_for & use_for) != use_for) {
+			unusable = true;
 			continue;
-		if (is_bss(&bss->pub, bssid, ssid, ssid_len)) {
-			res = bss;
-			bss_ref_get(rdev, res);
-			break;
 		}
+
+		res = bss;
+		bss_ref_get(rdev, res);
+		break;
 	}
 
 	spin_unlock_bh(&rdev->bss_lock);
-	if (!res)
+	if (!res) {
+		if (expired && unusable)
+			NL_SET_ERR_MSG(extack,
+				       "BSS entries are expired or cannot be used for the requested operation");
+		else if (unusable)
+			NL_SET_ERR_MSG(extack,
+				       "BSS cannot be used for the requested operation");
+		else if (expired)
+			NL_SET_ERR_MSG(extack,
+				       "BSS entry in scan results is expired");
+		else
+			NL_SET_ERR_MSG(extack,
+				       "BSS not found in scan results");
 		return NULL;
+	}
 	trace_cfg80211_return_bss(&res->pub);
 	return &res->pub;
 }
diff --git a/net/wireless/tests/scan.c b/net/wireless/tests/scan.c
index b1a9c1466d6c..2fc717317ac3 100644
--- a/net/wireless/tests/scan.c
+++ b/net/wireless/tests/scan.c
@@ -617,7 +617,7 @@ static void test_inform_bss_ml_sta(struct kunit *test)
 	link_bss = __cfg80211_get_bss(wiphy, NULL, sta_prof.bssid, NULL, 0,
 				      IEEE80211_BSS_TYPE_ANY,
 				      IEEE80211_PRIVACY_ANY,
-				      0);
+				      0, NULL);
 	KUNIT_ASSERT_NOT_NULL(test, link_bss);
 	KUNIT_EXPECT_EQ(test, link_bss->signal, 0);
 	KUNIT_EXPECT_EQ(test, link_bss->beacon_interval,
-- 
2.55.0


  reply	other threads:[~2026-07-22  7:07 UTC|newest]

Thread overview: 10+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-07-11  4:34 [PATCH] wifi: nl80211: say why the association " Louis Kotze
2026-07-21 15:31 ` Johannes Berg
2026-07-21 18:11   ` [PATCH v2 0/2] wifi: cfg80211: say why the auth/assoc " Louis Kotze
2026-07-21 18:11     ` [PATCH v2 1/2] " Louis Kotze
2026-07-21 22:10       ` Johannes Berg
2026-07-22  7:07         ` Louis Kotze
2026-07-22  7:07         ` [PATCH v3 0/2] " Louis Kotze
2026-07-22  7:07           ` Louis Kotze [this message]
2026-07-22  7:07           ` [PATCH v3 2/2] wifi: cfg80211: tests: check BSS lookup failure reasons Louis Kotze
2026-07-21 18:11     ` [PATCH v2 " Louis Kotze

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260722070734.3612581-2-loukot@gmail.com \
    --to=loukot@gmail.com \
    --cc=johannes@sipsolutions.net \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-wireless@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®