linux-wireless.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [RFC PATCH 0/2] wifi: detect stuck ECSA
@ 2024-01-18 11:16 Johannes Berg
  2024-01-18 11:16 ` [RFC PATCH 1/2] wifi: cfg80211: detect stuck ECSA element in probe resp Johannes Berg
                   ` (2 more replies)
  0 siblings, 3 replies; 4+ messages in thread
From: Johannes Berg @ 2024-01-18 11:16 UTC (permalink / raw)
  To: linux-wireless; +Cc: coldolt

This is a pretty specific workaround for what's going on with the Asus
AP codolt has (btw, could you actually mention the specific model and
maybe firmware version, just so we have a record of it?). But I've also
asked around internally and while we had encountered this with Windows
(too bad we didn't check before, sorry!), no other case is known.

Could you try these patches? I didn't (yet) attempt to reproduce this
scenario.

Sending as RFC to see if this addresses the issue - I will probably
refine the logic a bit, e.g. if there's no quiet and the channel is
the same, we might still want to connect? Not sure entirely.

johannes


^ permalink raw reply	[flat|nested] 4+ messages in thread

* [RFC PATCH 1/2] wifi: cfg80211: detect stuck ECSA element in probe resp
  2024-01-18 11:16 [RFC PATCH 0/2] wifi: detect stuck ECSA Johannes Berg
@ 2024-01-18 11:16 ` Johannes Berg
  2024-01-18 11:16 ` [RFC PATCH 2/2] wifi: mac80211: improve CSA/ECSA connection refusal Johannes Berg
  2024-01-20 14:56 ` [RFC PATCH 0/2] wifi: detect stuck ECSA coldolt
  2 siblings, 0 replies; 4+ messages in thread
From: Johannes Berg @ 2024-01-18 11:16 UTC (permalink / raw)
  To: linux-wireless; +Cc: coldolt, Johannes Berg

From: Johannes Berg <johannes.berg@intel.com>

We recently added some validation that we don't try to
connect to an AP that is currently in a channel switch
process, since that might want the channel to be quiet
or we might not be able to connect in time to hear the
switching in a beacon. This was in commit c09c4f31998b
("wifi: mac80211: don't connect to an AP while it's in
a CSA process").

However, we promptly got a report that this caused new
connection failures, and it turns out that the AP that
we now cannot connect to is permanently advertising an
extended channel switch announcement, even with quiet.

As a first step, attempt to detect that we're dealing
with such a situation, so mac80211 can use this later.

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
---
 include/net/cfg80211.h |  4 ++++
 net/wireless/scan.c    | 48 +++++++++++++++++++++++++++++++++++++++---
 2 files changed, 49 insertions(+), 3 deletions(-)

diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index 4ecfb06c413d..8f2c48761833 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -2865,6 +2865,8 @@ struct cfg80211_bss_ies {
  *	own the beacon_ies, but they're just pointers to the ones from the
  *	@hidden_beacon_bss struct)
  * @proberesp_ies: the information elements from the last Probe Response frame
+ * @proberesp_ecsa_stuck: ECSA element is stuck in the Probe Response frame,
+ *	cannot rely on it having valid data
  * @hidden_beacon_bss: in case this BSS struct represents a probe response from
  *	a BSS that hides the SSID in its beacon, this points to the BSS struct
  *	that holds the beacon data. @beacon_ies is still valid, of course, and
@@ -2900,6 +2902,8 @@ struct cfg80211_bss {
 	u8 chains;
 	s8 chain_signal[IEEE80211_MAX_CHAINS];
 
+	u8 proberesp_ecsa_stuck:1;
+
 	u8 bssid_index;
 	u8 max_bssid_indicator;
 
diff --git a/net/wireless/scan.c b/net/wireless/scan.c
index 9e5ccffd6868..dc018f661f2a 100644
--- a/net/wireless/scan.c
+++ b/net/wireless/scan.c
@@ -5,7 +5,7 @@
  * Copyright 2008 Johannes Berg <johannes@sipsolutions.net>
  * Copyright 2013-2014  Intel Mobile Communications GmbH
  * Copyright 2016	Intel Deutschland GmbH
- * Copyright (C) 2018-2023 Intel Corporation
+ * Copyright (C) 2018-2024 Intel Corporation
  */
 #include <linux/kernel.h>
 #include <linux/slab.h>
@@ -1725,6 +1725,46 @@ static void cfg80211_update_hidden_bsses(struct cfg80211_internal_bss *known,
 	}
 }
 
+static void cfg80211_check_stuck_ecsa(struct cfg80211_registered_device *rdev,
+				      struct cfg80211_internal_bss *known,
+				      const struct cfg80211_bss_ies *old,
+				      const struct cfg80211_bss_ies *bcn)
+{
+	const struct cfg80211_bss_ies *new;
+	const struct element *elem_new, *elem_old;
+
+	if (!old || known->pub.proberesp_ecsa_stuck)
+		return;
+
+	elem_old = cfg80211_find_elem(WLAN_EID_EXT_CHANSWITCH_ANN,
+				      old->data, old->len);
+	if (!elem_old)
+		return;
+
+	new = rcu_dereference_protected(known->pub.proberesp_ies,
+					lockdep_is_held(&rdev->bss_lock));
+	elem_new = cfg80211_find_elem(WLAN_EID_EXT_CHANSWITCH_ANN,
+				      new->data, new->len);
+	if (!elem_new)
+		return;
+
+	if (!bcn)
+		bcn = rcu_dereference_protected(known->pub.beacon_ies,
+						lockdep_is_held(&rdev->bss_lock));
+	if (bcn &&
+	    cfg80211_find_elem(WLAN_EID_EXT_CHANSWITCH_ANN,
+			       bcn->data, bcn->len))
+		return;
+
+	if (elem_new->datalen != elem_old->datalen)
+		return;
+	if (elem_new->datalen < sizeof(struct ieee80211_ext_chansw_ie))
+		return;
+	if (memcmp(elem_new->data, elem_old->data, elem_new->datalen))
+		return;
+	known->pub.proberesp_ecsa_stuck = 1;
+}
+
 static bool
 cfg80211_update_known_bss(struct cfg80211_registered_device *rdev,
 			  struct cfg80211_internal_bss *known,
@@ -1733,7 +1773,6 @@ cfg80211_update_known_bss(struct cfg80211_registered_device *rdev,
 {
 	lockdep_assert_held(&rdev->bss_lock);
 
-	/* Update IEs */
 	if (rcu_access_pointer(new->pub.proberesp_ies)) {
 		const struct cfg80211_bss_ies *old;
 
@@ -1744,8 +1783,11 @@ cfg80211_update_known_bss(struct cfg80211_registered_device *rdev,
 		/* Override possible earlier Beacon frame IEs */
 		rcu_assign_pointer(known->pub.ies,
 				   new->pub.proberesp_ies);
-		if (old)
+		if (old) {
+			cfg80211_check_stuck_ecsa(rdev, known, old,
+						  rcu_access_pointer(new->pub.beacon_ies));
 			kfree_rcu((struct cfg80211_bss_ies *)old, rcu_head);
+		}
 	} else if (rcu_access_pointer(new->pub.beacon_ies)) {
 		const struct cfg80211_bss_ies *old;
 
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* [RFC PATCH 2/2] wifi: mac80211: improve CSA/ECSA connection refusal
  2024-01-18 11:16 [RFC PATCH 0/2] wifi: detect stuck ECSA Johannes Berg
  2024-01-18 11:16 ` [RFC PATCH 1/2] wifi: cfg80211: detect stuck ECSA element in probe resp Johannes Berg
@ 2024-01-18 11:16 ` Johannes Berg
  2024-01-20 14:56 ` [RFC PATCH 0/2] wifi: detect stuck ECSA coldolt
  2 siblings, 0 replies; 4+ messages in thread
From: Johannes Berg @ 2024-01-18 11:16 UTC (permalink / raw)
  To: linux-wireless; +Cc: coldolt, Johannes Berg

From: Johannes Berg <johannes.berg@intel.com>

As mentioned in the previous commit, we pretty quickly found
that some APs have ECSA elements stuck in their probe response,
so using that to not attempt to connect while CSA is happening
we never connect to such an AP.

Improve this situation by checking more carefully and ignoring
the ECSA if cfg80211 has previously detected the ECSA element
being stuck in the probe response.

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
---
 net/mac80211/mlme.c | 98 ++++++++++++++++++++++++++++++++-------------
 1 file changed, 70 insertions(+), 28 deletions(-)

diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index c8998cf01b7a..3ac48423c441 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -7257,6 +7257,68 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata,
 	return err;
 }
 
+static bool ieee80211_mgd_csa_present(struct ieee80211_sub_if_data *sdata,
+				      const struct cfg80211_bss_ies *ies,
+				      bool ignore_ecsa)
+{
+	const struct element *csa_elem, *ecsa_elem;
+	struct ieee80211_channel_sw_ie *csa = NULL;
+	struct ieee80211_ext_chansw_ie *ecsa = NULL;
+
+	if (!ies)
+		return false;
+
+	csa_elem = cfg80211_find_elem(WLAN_EID_CHANNEL_SWITCH,
+				      ies->data, ies->len);
+	if (csa_elem && csa_elem->datalen == sizeof(*csa))
+		csa = (void *)csa_elem->data;
+
+	ecsa_elem = cfg80211_find_elem(WLAN_EID_EXT_CHANSWITCH_ANN,
+				       ies->data, ies->len);
+	if (ecsa_elem && ecsa_elem->datalen == sizeof(*ecsa))
+		ecsa = (void *)ecsa_elem->data;
+
+	if (csa && csa->count == 0)
+		csa = NULL;
+
+	if (ecsa && ecsa->count == 0)
+		ecsa = NULL;
+
+	if (ignore_ecsa && ecsa) {
+		sdata_info(sdata,
+			   "Ignoring ECSA in probe response - was considered stuck!\n");
+		return csa;
+	}
+
+	return csa || ecsa;
+}
+
+static bool ieee80211_mgd_csa_in_process(struct ieee80211_sub_if_data *sdata,
+					 struct cfg80211_bss *bss)
+{
+	bool ret;
+
+	rcu_read_lock();
+	if (ieee80211_mgd_csa_present(sdata,
+				      rcu_dereference(bss->beacon_ies),
+				      false)) {
+		ret = true;
+		goto out;
+	}
+
+	if (ieee80211_mgd_csa_present(sdata,
+				      rcu_dereference(bss->proberesp_ies),
+				      bss->proberesp_ecsa_stuck)) {
+		ret = true;
+		goto out;
+	}
+
+	ret = false;
+out:
+	rcu_read_unlock();
+	return ret;
+}
+
 /* config hooks */
 int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata,
 		       struct cfg80211_auth_request *req)
@@ -7265,7 +7327,6 @@ int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata,
 	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
 	struct ieee80211_mgd_auth_data *auth_data;
 	struct ieee80211_link_data *link;
-	const struct element *csa_elem, *ecsa_elem;
 	u16 auth_alg;
 	int err;
 	bool cont_auth;
@@ -7308,21 +7369,10 @@ int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata,
 	if (ifmgd->assoc_data)
 		return -EBUSY;
 
-	rcu_read_lock();
-	csa_elem = ieee80211_bss_get_elem(req->bss, WLAN_EID_CHANNEL_SWITCH);
-	ecsa_elem = ieee80211_bss_get_elem(req->bss,
-					   WLAN_EID_EXT_CHANSWITCH_ANN);
-	if ((csa_elem &&
-	     csa_elem->datalen == sizeof(struct ieee80211_channel_sw_ie) &&
-	     ((struct ieee80211_channel_sw_ie *)csa_elem->data)->count != 0) ||
-	    (ecsa_elem &&
-	     ecsa_elem->datalen == sizeof(struct ieee80211_ext_chansw_ie) &&
-	     ((struct ieee80211_ext_chansw_ie *)ecsa_elem->data)->count != 0)) {
-		rcu_read_unlock();
+	if (ieee80211_mgd_csa_in_process(sdata, req->bss)) {
 		sdata_info(sdata, "AP is in CSA process, reject auth\n");
 		return -EINVAL;
 	}
-	rcu_read_unlock();
 
 	auth_data = kzalloc(sizeof(*auth_data) + req->auth_data_len +
 			    req->ie_len, GFP_KERNEL);
@@ -7631,7 +7681,7 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
 	struct ieee80211_local *local = sdata->local;
 	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
 	struct ieee80211_mgd_assoc_data *assoc_data;
-	const struct element *ssid_elem, *csa_elem, *ecsa_elem;
+	const struct element *ssid_elem;
 	struct ieee80211_vif_cfg *vif_cfg = &sdata->vif.cfg;
 	ieee80211_conn_flags_t conn_flags = 0;
 	struct ieee80211_link_data *link;
@@ -7654,6 +7704,12 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
 
 	cbss = req->link_id < 0 ? req->bss : req->links[req->link_id].bss;
 
+	if (ieee80211_mgd_csa_in_process(sdata, cbss)) {
+		sdata_info(sdata, "AP is in CSA process, reject assoc\n");
+		kfree(assoc_data);
+		return -EINVAL;
+	}
+
 	rcu_read_lock();
 	ssid_elem = ieee80211_bss_get_elem(cbss, WLAN_EID_SSID);
 	if (!ssid_elem || ssid_elem->datalen > sizeof(assoc_data->ssid)) {
@@ -7662,20 +7718,6 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
 		return -EINVAL;
 	}
 
-	csa_elem = ieee80211_bss_get_elem(cbss, WLAN_EID_CHANNEL_SWITCH);
-	ecsa_elem = ieee80211_bss_get_elem(cbss, WLAN_EID_EXT_CHANSWITCH_ANN);
-	if ((csa_elem &&
-	     csa_elem->datalen == sizeof(struct ieee80211_channel_sw_ie) &&
-	     ((struct ieee80211_channel_sw_ie *)csa_elem->data)->count != 0) ||
-	    (ecsa_elem &&
-	     ecsa_elem->datalen == sizeof(struct ieee80211_ext_chansw_ie) &&
-	     ((struct ieee80211_ext_chansw_ie *)ecsa_elem->data)->count != 0)) {
-		sdata_info(sdata, "AP is in CSA process, reject assoc\n");
-		rcu_read_unlock();
-		kfree(assoc_data);
-		return -EINVAL;
-	}
-
 	memcpy(assoc_data->ssid, ssid_elem->data, ssid_elem->datalen);
 	assoc_data->ssid_len = ssid_elem->datalen;
 	memcpy(vif_cfg->ssid, assoc_data->ssid, assoc_data->ssid_len);
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* Re: [RFC PATCH 0/2] wifi: detect stuck ECSA
  2024-01-18 11:16 [RFC PATCH 0/2] wifi: detect stuck ECSA Johannes Berg
  2024-01-18 11:16 ` [RFC PATCH 1/2] wifi: cfg80211: detect stuck ECSA element in probe resp Johannes Berg
  2024-01-18 11:16 ` [RFC PATCH 2/2] wifi: mac80211: improve CSA/ECSA connection refusal Johannes Berg
@ 2024-01-20 14:56 ` coldolt
  2 siblings, 0 replies; 4+ messages in thread
From: coldolt @ 2024-01-20 14:56 UTC (permalink / raw)
  To: Johannes Berg; +Cc: linux-wireless

The AP is an Asus RT-AC53, firmware 3.0.0.4.380_10760-g21a5898

I tried those patches on 6.7.0, with them it works and connects fine to the AP.

^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2024-01-20 14:56 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-01-18 11:16 [RFC PATCH 0/2] wifi: detect stuck ECSA Johannes Berg
2024-01-18 11:16 ` [RFC PATCH 1/2] wifi: cfg80211: detect stuck ECSA element in probe resp Johannes Berg
2024-01-18 11:16 ` [RFC PATCH 2/2] wifi: mac80211: improve CSA/ECSA connection refusal Johannes Berg
2024-01-20 14:56 ` [RFC PATCH 0/2] wifi: detect stuck ECSA coldolt

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).