public inbox for iwd@lists.linux.dev
 help / color / mirror / Atom feed
From: James Prestwood <prestwoj@gmail.com>
To: iwd@lists.linux.dev
Cc: James Prestwood <prestwoj@gmail.com>
Subject: [PATCH] station: improve zero oper class neighbor report workaround
Date: Wed, 31 Jul 2024 07:47:17 -0700	[thread overview]
Message-ID: <20240731144717.473941-1-prestwoj@gmail.com> (raw)

The workaround for Cisco APs reporting an operating class of zero
is still a bug that remains in Cisco equipment. This is made even
worse with the introduction of 6GHz where the channel numbers
overlap with both 2.4 and 5GHz bands. This makes it impossible to
definitively choose a frequency given only a channel number.

To improve this workaround and cover the 6GHz band we can calculate
a frequency for each band and see what is successful. Then append
each frequency we get to the list. This will result in more
frequencies scanned, but this tradeoff is better than potentially
avoiding a roam to 6GHz or high order 5ghz channel numbers.
---
 src/station.c | 145 ++++++++++++++++++++++++++++----------------------
 1 file changed, 82 insertions(+), 63 deletions(-)

diff --git a/src/station.c b/src/station.c
index db069981..e373b03b 100644
--- a/src/station.c
+++ b/src/station.c
@@ -1821,36 +1821,87 @@ static void station_roam_timeout_rearm(struct station *station, int seconds);
 static int station_roam_scan(struct station *station,
 				struct scan_freq_set *freq_set);
 
-static uint32_t station_freq_from_neighbor_report(const uint8_t *country,
-		struct ie_neighbor_report_info *info, enum band_freq *out_band)
+static bool is_freq_band_supported(struct station *station, uint32_t freq,
+					enum band_freq band)
+{
+	const struct band_freq_attrs *attr;
+
+	if (!(band & wiphy_get_supported_bands(station->wiphy)))
+		return false;
+
+	/* Skip if frequency is not supported or disabled */
+	attr = wiphy_get_frequency_info(station->wiphy, freq);
+	if (!attr || attr->disabled)
+		return false;
+
+	return true;
+}
+
+/*
+ * Some Cisco APs report all operating class values as 0 in the Neighbor
+ * Report Responses. Historically we could be very sure what band the
+ * channel was associated with since there was no overlap between 2.4
+ * and 5GHz. With the addition of 6GHz this becomes more difficult as
+ * the 6GHz channel range overlaps both 2.4 and 5ghz.
+ *
+ * To cover all 3 bands, calculate a frequency for each and see what sticks.
+ */
+static void station_parse_zero_oper_class(struct station *station,
+					struct ie_neighbor_report_info *info,
+					struct scan_freq_set *freq_set)
+{
+	uint32_t freq_2g;
+	uint32_t freq_5g;
+	uint32_t freq_6g;
+
+	freq_2g = band_channel_to_freq(info->channel_num, BAND_FREQ_2_4_GHZ);
+	freq_5g = band_channel_to_freq(info->channel_num, BAND_FREQ_5_GHZ);
+	freq_6g = band_channel_to_freq(info->channel_num, BAND_FREQ_6_GHZ);
+
+	if (!freq_2g && !freq_5g && !freq_6g) {
+		l_debug("Ignored: 0 oper class with an unusual "
+				"channel number %u", info->channel_num);
+		return;
+	}
+
+	if (freq_2g && is_freq_band_supported(station, freq_2g,
+						BAND_FREQ_2_4_GHZ)) {
+		l_debug("adding frequency %u from invalid oper class", freq_2g);
+		scan_freq_set_add(freq_set, freq_2g);
+	}
+
+	if (freq_5g && is_freq_band_supported(station, freq_5g,
+						BAND_FREQ_5_GHZ)) {
+		l_debug("adding frequency %u from invalid oper class", freq_5g);
+		scan_freq_set_add(freq_set, freq_5g);
+	}
+
+	if (freq_6g && is_freq_band_supported(station, freq_6g,
+						BAND_FREQ_6_GHZ)) {
+		l_debug("adding frequency %u from invalid oper class", freq_6g);
+		scan_freq_set_add(freq_set, freq_6g);
+	}
+}
+
+static void station_add_neighbor_report_freqs(struct station *station,
+					struct ie_neighbor_report_info *info,
+					struct scan_freq_set *freq_set)
 {
 	enum band_freq band;
 	uint32_t freq;
 
-	if (info->oper_class == 0) {
-		/*
-		 * Some Cisco APs report all operating class values as 0
-		 * in the Neighbor Report Responses.  Work around this by
-		 * using the most likely operating class for the channel
-		 * number as the 2.4GHz and 5GHz bands happen to mostly
-		 * use channels in two disjoint ranges.
-		 */
-		if (info->channel_num >= 1 && info->channel_num <= 14)
-			band = BAND_FREQ_2_4_GHZ;
-		else if (info->channel_num >= 36 && info->channel_num <= 169)
-			band = BAND_FREQ_5_GHZ;
-		else {
-			l_debug("Ignored: 0 oper class with an unusual "
-				"channel number %u", info->channel_num);
+	const uint8_t *country = station->connected_bss->cc_present ?
+					station->connected_bss->cc : NULL;
 
-			return 0;
-		}
+	if (info->oper_class == 0) {
+		station_parse_zero_oper_class(station, info, freq_set);
+		return;
 	} else {
 		band = band_oper_class_to_band(country, info->oper_class);
 		if (!band) {
 			l_debug("Ignored: unsupported oper class");
 
-			return 0;
+			return;
 		}
 	}
 
@@ -1858,13 +1909,14 @@ static uint32_t station_freq_from_neighbor_report(const uint8_t *country,
 	if (!freq) {
 		l_debug("Ignored: unsupported channel");
 
-		return 0;
+		return;
 	}
 
-	if (out_band)
-		*out_band = band;
+	/* Skip if the band/frequency is not supported */
+	if (!is_freq_band_supported(station, freq, band))
+		return;
 
-	return freq;
+	scan_freq_set_add(freq_set, freq);
 }
 
 static void parse_neighbor_report(struct station *station,
@@ -1873,9 +1925,8 @@ static void parse_neighbor_report(struct station *station,
 					struct scan_freq_set **set)
 {
 	struct ie_tlv_iter iter;
-	int count_md = 0, count_no_md = 0;
 	struct scan_freq_set *freq_set_md, *freq_set_no_md;
-	uint32_t current_freq = 0;
+	uint32_t current_freq = station->connected_bss->frequency;
 	struct handshake_state *hs = netdev_get_handshake(station->netdev);
 
 	freq_set_md = scan_freq_set_new();
@@ -1886,10 +1937,6 @@ static void parse_neighbor_report(struct station *station,
 	/* First see if any of the reports contain the MD bit set */
 	while (ie_tlv_iter_next(&iter)) {
 		struct ie_neighbor_report_info info;
-		uint32_t freq;
-		enum band_freq band;
-		const uint8_t *cc = NULL;
-		const struct band_freq_attrs *attr;
 
 		if (ie_tlv_iter_get_tag(&iter) != IE_TYPE_NEIGHBOR_REPORT)
 			continue;
@@ -1903,22 +1950,6 @@ static void parse_neighbor_report(struct station *station,
 				(int) info.channel_num, (int) info.oper_class,
 				info.md ? "MD set" : "MD not set");
 
-		if (station->connected_bss->cc_present)
-			cc = station->connected_bss->cc;
-
-		freq = station_freq_from_neighbor_report(cc, &info, &band);
-		if (!freq)
-			continue;
-
-		/* Skip if the band is not supported */
-		if (!(band & wiphy_get_supported_bands(station->wiphy)))
-			continue;
-
-		/* Skip if frequency is not supported or disabled */
-		attr = wiphy_get_frequency_info(station->wiphy, freq);
-		if (!attr || attr->disabled)
-			continue;
-
 		if (!memcmp(info.addr,
 				station->connected_bss->addr, ETH_ALEN)) {
 			/*
@@ -1927,26 +1958,14 @@ static void parse_neighbor_report(struct station *station,
 			 * its channel because it may still be the best ranked
 			 * or the only visible AP.
 			 */
-			current_freq = freq;
-
 			continue;
 		}
 
-		/* Add the frequency to one of the lists */
-		if (info.md && hs->mde) {
-			scan_freq_set_add(freq_set_md, freq);
-
-			count_md += 1;
-		} else {
-			scan_freq_set_add(freq_set_no_md, freq);
-
-			count_no_md += 1;
-		}
+		station_add_neighbor_report_freqs(station, &info,
+						info.md && hs->mde ?
+						freq_set_md : freq_set_no_md);
 	}
 
-	if (!current_freq)
-		current_freq = station->connected_bss->frequency;
-
 	/*
 	 * If there are neighbor reports with the MD bit set then the bit
 	 * is probably valid so scan only the frequencies of the neighbors
@@ -1968,11 +1987,11 @@ static void parse_neighbor_report(struct station *station,
 	 * automatically fall back to the non-FT candidates and then to
 	 * full scan.
 	 */
-	if (count_md) {
+	if (!scan_freq_set_isempty(freq_set_md)) {
 		scan_freq_set_add(freq_set_md, current_freq);
 		*set = freq_set_md;
 		scan_freq_set_free(freq_set_no_md);
-	} else if (count_no_md) {
+	} else if (!scan_freq_set_isempty(freq_set_no_md)) {
 		scan_freq_set_add(freq_set_no_md, current_freq);
 		*set = freq_set_no_md;
 		scan_freq_set_free(freq_set_md);
-- 
2.34.1


             reply	other threads:[~2024-07-31 14:47 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2024-07-31 14:47 James Prestwood [this message]
2024-08-05 14:06 ` [PATCH] station: improve zero oper class neighbor report workaround Denis Kenzior

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=20240731144717.473941-1-prestwoj@gmail.com \
    --to=prestwoj@gmail.com \
    --cc=iwd@lists.linux.dev \
    /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