diff mbox series

[v2] wpa_supplicant: Add an option to disable SNR capping

Message ID 20240324172116.99904-1-Chaitanya.Tata@nordicsemi.no
State New
Headers show
Series [v2] wpa_supplicant: Add an option to disable SNR capping | expand

Commit Message

Krishna Chaitanya March 24, 2024, 5:21 p.m. UTC
When selecting a network the default behaviour of WPA supplicant is to
prefer higher throughput, it does this by capping the SNR.

But in certain environments, reliability is important over throughput
and choosing a lower SNR (thought it is greater than "Great SNR") might
be sub-optimal.

Introduce a configuration option to choose between the two options
(throughput or reliability).

Signed-off-by: Chaitanya Tata <Chaitanya.Tata@nordicsemi.no>
---
v2: Move to runtime configuration
---
 tests/hwsim/test_wpas_config.py    |  3 ++-
 wpa_supplicant/config.c            |  1 +
 wpa_supplicant/config.h            |  8 ++++++++
 wpa_supplicant/config_file.c       |  2 ++
 wpa_supplicant/p2p_supplicant.c    |  1 +
 wpa_supplicant/scan.c              | 16 ++++++++++++++--
 wpa_supplicant/scan.h              |  4 ++++
 wpa_supplicant/wpa_cli.c           |  4 +++-
 wpa_supplicant/wpa_supplicant.conf |  9 +++++++++
 9 files changed, 44 insertions(+), 4 deletions(-)
diff mbox series

Patch

diff --git a/tests/hwsim/test_wpas_config.py b/tests/hwsim/test_wpas_config.py
index 2b6ab2ac0..decbdce4e 100644
--- a/tests/hwsim/test_wpas_config.py
+++ b/tests/hwsim/test_wpas_config.py
@@ -129,7 +129,8 @@  config_checks = [("ap_scan", "0"),
                  ("p2p_device_random_mac_addr", "1"),
                  ("p2p_device_persistent_mac_addr", "02:12:34:56:78:9a"),
                  ("p2p_interface_random_mac_addr", "1"),
-                 ("openssl_ciphers", "DEFAULT")]
+                 ("openssl_ciphers", "DEFAULT"),
+                 ("nw_sel_strategy", "1")]
 
 def supported_param(capa, field):
     mesh_params = ["user_mpm", "max_peer_links", "mesh_max_inactivity"]
diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c
index 2c756136c..80544a698 100644
--- a/wpa_supplicant/config.c
+++ b/wpa_supplicant/config.c
@@ -5589,6 +5589,7 @@  static const struct global_parse_data global_fields[] = {
 	{ FUNC(mld_connect_bssid_pref), 0 },
 #endif /* CONFIG_TESTING_OPTIONS */
 	{ INT_RANGE(ft_prepend_pmkid, 0, 1), CFG_CHANGED_FT_PREPEND_PMKID },
+	{ INT(nw_sel_strategy), 0 },
 	/* NOTE: When adding new parameters here, add_interface() in
 	 * wpa_supplicant/dbus_new_introspect.c may need to be modified to
 	 * increase the size of the iface->xml buffer. */
diff --git a/wpa_supplicant/config.h b/wpa_supplicant/config.h
index 8981305c2..38b4e920e 100644
--- a/wpa_supplicant/config.h
+++ b/wpa_supplicant/config.h
@@ -1801,6 +1801,14 @@  struct wpa_config {
 
 	int mld_force_single_link;
 #endif /* CONFIG_TESTING_OPTIONS */
+
+	/**
+	 * nw_sel_strategy - Network selection strategy for scan results
+	 *
+	 * 0 = use the network with higher band preference
+	 * 1 = use the network with highest SNR (no cap)
+	 */
+	int nw_sel_strategy;
 };
 
 
diff --git a/wpa_supplicant/config_file.c b/wpa_supplicant/config_file.c
index 1a2c0c9be..3130e6ba7 100644
--- a/wpa_supplicant/config_file.c
+++ b/wpa_supplicant/config_file.c
@@ -1625,6 +1625,8 @@  static void wpa_config_write_global(FILE *f, struct wpa_config *config)
 #endif /* CONFIG_TESTING_OPTIONS */
 	if (config->ft_prepend_pmkid)
 		fprintf(f, "ft_prepend_pmkid=%d", config->ft_prepend_pmkid);
+	if (config->nw_sel_strategy)
+		fprintf(f, "nw_sel_strategy=%d\n", config->nw_sel_strategy);
 }
 
 #endif /* CONFIG_NO_CONFIG_WRITE */
diff --git a/wpa_supplicant/p2p_supplicant.c b/wpa_supplicant/p2p_supplicant.c
index 70025f1e4..472fe0ae8 100644
--- a/wpa_supplicant/p2p_supplicant.c
+++ b/wpa_supplicant/p2p_supplicant.c
@@ -2217,6 +2217,7 @@  do {                                    \
 	d->go_venue_group = s->go_venue_group;
 	d->go_venue_type = s->go_venue_type;
 	d->p2p_add_cli_chan = s->p2p_add_cli_chan;
+	d->nw_sel_strategy = s->nw_sel_strategy;
 }
 
 
diff --git a/wpa_supplicant/scan.c b/wpa_supplicant/scan.c
index 2db4d8b91..f171c7af4 100644
--- a/wpa_supplicant/scan.c
+++ b/wpa_supplicant/scan.c
@@ -24,6 +24,9 @@ 
 #include "scan.h"
 #include "mesh.h"
 
+/* qsort_r is non-standard, so, use qsort and a global variable */
+static int nw_sel_strategy;
+
 static struct wpabuf * wpa_supplicant_extra_ies(struct wpa_supplicant *wpa_s);
 
 
@@ -2376,6 +2379,7 @@  static int wpa_scan_result_compar(const void *a, const void *b)
 	int snr_a, snr_b, snr_a_full, snr_b_full;
 	size_t ies_len;
 	const u8 *rsne_a, *rsne_b;
+	unsigned short snr_cap;
 
 	/* WPA/WPA2 support preferred */
 	wpa_a = wpa_scan_get_vendor_ie(wa, WPA_IE_VENDOR_TYPE) != NULL ||
@@ -2396,6 +2400,12 @@  static int wpa_scan_result_compar(const void *a, const void *b)
 	    (wb->caps & IEEE80211_CAP_PRIVACY) == 0)
 		return -1;
 
+	if (nw_sel_strategy == 1) {
+		snr_cap = DISABLE_SNR_CAP;
+	} else {
+		snr_cap = GREAT_SNR;
+	}
+
 	if (wa->flags & wb->flags & WPA_SCAN_LEVEL_DBM) {
 		/*
 		 * The scan result estimates SNR over 20 MHz, while Data frames
@@ -2406,12 +2416,12 @@  static int wpa_scan_result_compar(const void *a, const void *b)
 		snr_a_full = wpas_adjust_snr_by_chanwidth((const u8 *) (wa + 1),
 							  ies_len, wa->max_cw,
 							  wa->snr);
-		snr_a = MIN(snr_a_full, GREAT_SNR);
+		snr_a = MIN(snr_a_full, snr_cap);
 		ies_len = wb->ie_len ? wb->ie_len : wb->beacon_ie_len;
 		snr_b_full = wpas_adjust_snr_by_chanwidth((const u8 *) (wb + 1),
 							  ies_len, wb->max_cw,
 							  wb->snr);
-		snr_b = MIN(snr_b_full, GREAT_SNR);
+		snr_b = MIN(snr_b_full, snr_cap);
 	} else {
 		/* Level is not in dBm, so we can't calculate
 		 * SNR. Just use raw level (units unknown). */
@@ -3182,6 +3192,8 @@  wpa_supplicant_get_scan_results(struct wpa_supplicant *wpa_s,
 	size_t i;
 	int (*compar)(const void *, const void *) = wpa_scan_result_compar;
 
+	nw_sel_strategy = wpa_s->conf->nw_sel_strategy;
+
 	scan_res = wpa_drv_get_scan_results(wpa_s, bssid);
 	if (scan_res == NULL) {
 		wpa_dbg(wpa_s, MSG_DEBUG, "Failed to get scan results");
diff --git a/wpa_supplicant/scan.h b/wpa_supplicant/scan.h
index d4c06c1ae..13b771a1e 100644
--- a/wpa_supplicant/scan.h
+++ b/wpa_supplicant/scan.h
@@ -30,6 +30,10 @@ 
  */
 #define GREAT_SNR 25
 
+/* Disabled capping of SNR for network selection */
+#define DISABLE_SNR_CAP 999
+
+
 /*
  * IEEE Sts 802.11ax-2021, 9.4.2.161 (Transmit Power Envelope element) indicates
  * no max TX power limit if Maximum Transmit Power field is 63.5 dBm.
diff --git a/wpa_supplicant/wpa_cli.c b/wpa_supplicant/wpa_cli.c
index 60f85624f..f71a37abf 100644
--- a/wpa_supplicant/wpa_cli.c
+++ b/wpa_supplicant/wpa_cli.c
@@ -517,6 +517,7 @@  static char ** wpa_cli_complete_set(const char *str, int pos)
 #endif /* CONFIG_TESTING_OPTIONS */
 		"relative_rssi", "relative_band_adjust",
 		"extended_key_id",
+		"nw_sel_strategy"
 	};
 	int i, num_fields = ARRAY_SIZE(fields);
 
@@ -617,7 +618,8 @@  static char ** wpa_cli_complete_get(const char *str, int pos)
 		"tdls_external_control", "osu_dir", "wowlan_triggers",
 		"p2p_search_delay", "mac_addr", "rand_addr_lifetime",
 		"preassoc_mac_addr", "key_mgmt_offload", "passive_scan",
-		"reassoc_same_bss_optim", "extended_key_id"
+		"reassoc_same_bss_optim", "extended_key_id",
+		"nw_sel_strategy"
 	};
 	int i, num_fields = ARRAY_SIZE(fields);
 
diff --git a/wpa_supplicant/wpa_supplicant.conf b/wpa_supplicant/wpa_supplicant.conf
index 2e36845f1..f65ab57ee 100644
--- a/wpa_supplicant/wpa_supplicant.conf
+++ b/wpa_supplicant/wpa_supplicant.conf
@@ -132,6 +132,15 @@  ap_scan=1
 # 1:  Do passive scans.
 #passive_scan=0
 
+# Network selection strategy
+#
+# 0 : Prefer the network with the higher band to get the best throughput.
+#     Cap the SNR value for comparing two networks to 25, and use the
+#     network with the higher band to get the best throughput.
+# 1 : Prefer the network with the highest SNR (
+#     Use the network with the highest SNR value without any capping.
+nw_sel_strategy=0
+
 # MPM residency
 # By default, wpa_supplicant implements the mesh peering manager (MPM) for an
 # open mesh. However, if the driver can implement the MPM, you may set this to