diff mbox series

RFC: Perform active scanning on secondary interface (wpa_supplicant)

Message ID CO2PR0801MB2296C7CF591EE8616EE273B2BB6B9@CO2PR0801MB2296.namprd08.prod.outlook.com
State RFC
Headers show
Series RFC: Perform active scanning on secondary interface (wpa_supplicant) | expand

Commit Message

Isaacs, Jeff (EXT - US/Murray Hill) March 16, 2021, 3 p.m. UTC
WiFi clients using wpa_supplicant maintain a table of available BSSs for each interface managed. Entries in this table are populated by performing active scanning on each interface. Entries not seen for a certain number of scans or a certain amount of time expire and are removed from the table. When the RSSI of an active connection drops too low and the quality of the connection is very poor, the interface performs a scan, which further degrades the connection quality.

In environments with high density networks, mobile clients can get caught in this cycle of letting the connection quality get very bad, then scanning, then reconnecting to a better AP. In the wpa_cli, there is the roam command, which allows clients to move to another AP in the same SSID. The roam command requires the BSSID to be in the BSS table of the interface performing the command. This means that the interface still needs to perform disruptive scanning to roam to other APs.

In applications where high throughput and low latency are required, roaming between access points without disruption is not possible. To achieve this, the interface would need to be constantly scanning for a better AP, which decreases throughput and increase latency. If a client has multiple WiFi interfaces, one interface could be designated as the secondary scanning interface and be used to perform periodic scanning in small intervals (every 5 seconds). The results of scans on the secondary interface could be made available to the primary interface. This would allow wpa_cli roam to be used to its full potential and open the door to seamless roaming in enterprise and industrial environments. The mechanism that decides when to execute handoffs is application based and would be up to the user to implement using the CLI.

I have attached a patch that introduces a global config parameter "is_secondary_scan_iface" that can be set for each interface. When an interface performs a scan, if the is_secondary_scan_iface is set to 1, the results from the scan are used to update the BSS tables of all other interfaces whose is_secondary_scan_iface parameter is set to 0.

To test this feature, you'll need two WiFi adapters. Add is_secondary_scan_iface=1 to the config file of the secondary interface. Startup wpa_supplicant using the two interfaces. Flush the BSS table of the primary interface, then verify that it is empty. Next, perform a scan on the secondary interface. You'll see that the results of this scan are populated in the BSS table of the primary interface.

An obvious limitation of this feature as it is implemented right now would be lack of capabilities matching. For instance, if the secondary scan interface can see 5GHz but the primary interface cannot, those results should not be made available to the primary interface.

I am interested in your comments, concerns, and questions.

Thank you,
Jeffrey Isaacs
diff mbox series

Patch

From 5f8e010965377c28ede4d09ef8cb4b321d69769f Mon Sep 17 00:00:00 2001
From: Jeffrey Isaacs <jeff.isaacs.ext@nokia.com>
Date: Mon, 15 Mar 2021 17:02:25 -0400
Subject: [PATCH 1/2] wpa_supplicant: Add is_secondary_scan_iface to wpa_config

This is the first in a series of commits that will implement
offloading active scanning for available access points to a
secondary interface. The motivation behind this is that
performing active scans is disruptive to the WiFi connection.
In mission critical applications, or when a constant high
throughput is required, roaming between APs can result in
unacceptable performance. When a client's rssi drops too low,
or all the BSS table entries expire, the interface performs
an active scan to find the next available AP.

The new parameter is_secondary_scan_iface will be specified in
the interface config file. When set to 1, scan results from that
interface will be used to update the BSS tables in all interfaces
whose is_secondary_scan_iface is set to 0. This allows the
primary interface to roam between BSSs without having to take time
to scan.

This parameter does not affect any other parameters. It would make
sense that if a secondary interface is specified, the frequency
of scans performed on that interface should be increased to fully
leverage this feature.

Signed-off-by: Jeffrey Isaacs <jeff.isaacs.ext@nokia.com>
---
 wpa_supplicant/config.c      |  2 ++
 wpa_supplicant/config.h      | 12 ++++++++++++
 wpa_supplicant/config_file.c |  3 +++
 3 files changed, 17 insertions(+)

diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c
index ce5c80d02..1935e097d 100644
--- a/wpa_supplicant/config.c
+++ b/wpa_supplicant/config.c
@@ -4378,6 +4378,7 @@  struct wpa_config * wpa_config_alloc_empty(const char *ctrl_interface,
 	config->cert_in_cb = DEFAULT_CERT_IN_CB;
 	config->wpa_rsc_relaxation = DEFAULT_WPA_RSC_RELAXATION;
 	config->extended_key_id = DEFAULT_EXTENDED_KEY_ID;
+	config->is_secondary_scan_iface = DEFAULT_IS_SECONDARY_SCAN_IFACE;
 
 #ifdef CONFIG_MBO
 	config->mbo_cell_capa = DEFAULT_MBO_CELL_CAPA;
@@ -5210,6 +5211,7 @@  static const struct global_parse_data global_fields[] = {
 	{ INT_RANGE(force_kdk_derivation, 0, 1), 0 },
 #endif /* CONFIG_TESTING_OPTIONS */
 #endif /* CONFIG_PASN */
+	{ INT(is_secondary_scan_iface), 0 },
 };
 
 #undef FUNC
diff --git a/wpa_supplicant/config.h b/wpa_supplicant/config.h
index aac4a9dff..b10beaba4 100644
--- a/wpa_supplicant/config.h
+++ b/wpa_supplicant/config.h
@@ -46,6 +46,7 @@ 
 #define DEFAULT_OCE_SUPPORT OCE_STA
 #define DEFAULT_EXTENDED_KEY_ID 0
 #define DEFAULT_SCAN_RES_VALID_FOR_CONNECT 5
+#define DEFAULT_IS_SECONDARY_SCAN_IFACE 0
 
 #include "config_ssid.h"
 #include "wps/wps.h"
@@ -1650,6 +1651,17 @@  struct wpa_config {
 	int force_kdk_derivation;
 #endif /* CONFIG_TESTING_OPTIONS */
 #endif /* CONFIG_PASN*/
+
+	/**
+	 * is_secondary_scan_iface - Enable dedicated scan interface
+	 *
+	 * 0 = Scan results are not shared with other interfaces
+	 * 1 = Scan results from this interface are used to update
+	 	   the BSS table of each other interface
+	 *
+	 * By default, scan results are not shared
+	 */
+	int is_secondary_scan_iface;
 };
 
 
diff --git a/wpa_supplicant/config_file.c b/wpa_supplicant/config_file.c
index a535e3f08..2f86263b8 100644
--- a/wpa_supplicant/config_file.c
+++ b/wpa_supplicant/config_file.c
@@ -1530,6 +1530,9 @@  static void wpa_config_write_global(FILE *f, struct wpa_config *config)
 	if (config->wowlan_disconnect_on_deinit)
 		fprintf(f, "wowlan_disconnect_on_deinit=%d\n",
 			config->wowlan_disconnect_on_deinit);
+	if (config->is_secondary_scan_iface)
+		fprintf(f, "is_secondary_scan_iface=%d\n",
+			config->is_secondary_scan_iface);
 }
 
 #endif /* CONFIG_NO_CONFIG_WRITE */
-- 
2.17.1


From cbf7388a3cb99f67c240d1b41f9edef6d685732f Mon Sep 17 00:00:00 2001
From: Jeffrey Isaacs <jeff.isaacs.ext@nokia.com>
Date: Mon, 15 Mar 2021 17:36:56 -0400
Subject: [PATCH 2/2] wpa_supplicant: Implement active scanning from secondary
 interface

This commit implements the changes required for the
is_secondary_scan_iface parameter. When the parameter is set for a
given interface, when scan results are available on that interface,
wpa_bss_update is called using those results on all other interfaces
that have is_secondary_scan_interface set to 0.

Signed-off-by: Jeffrey Isaacs <jeff.isaacs.ext@nokia.com>
---
 wpa_supplicant/bss.c        | 20 +++++++++++++++++++-
 wpa_supplicant/bss.h        |  3 ++-
 wpa_supplicant/ctrl_iface.c |  2 +-
 wpa_supplicant/scan.c       |  2 +-
 wpa_supplicant/wnm_sta.c    |  2 +-
 5 files changed, 24 insertions(+), 5 deletions(-)

diff --git a/wpa_supplicant/bss.c b/wpa_supplicant/bss.c
index e13783ce1..56121436e 100644
--- a/wpa_supplicant/bss.c
+++ b/wpa_supplicant/bss.c
@@ -747,8 +747,26 @@  void wpa_bss_update_start(struct wpa_supplicant *wpa_s)
  */
 void wpa_bss_update_scan_res(struct wpa_supplicant *wpa_s,
 			     struct wpa_scan_res *res,
-			     struct os_reltime *fetch_time)
+			     struct os_reltime *fetch_time,
+			     int secondary_scan)
 {
+	struct wpa_supplicant *iface;
+	iface = wpa_s->global->ifaces;
+
+	while (iface && !secondary_scan) {
+		if (wpa_s->conf->is_secondary_scan_iface && !iface->conf->is_secondary_scan_iface) {
+			wpa_bss_update_scan_res(iface, res, fetch_time, 1);
+		}
+		if (iface->p2pdev == wpa_s)
+			iface->p2pdev = iface->parent;
+		if (iface == wpa_s || iface->parent != wpa_s) {
+			iface = iface->next;
+			continue;
+		}
+		iface = iface->next;
+	}
+
+
 	const u8 *ssid, *p2p, *mesh;
 	struct wpa_bss *bss;
 
diff --git a/wpa_supplicant/bss.h b/wpa_supplicant/bss.h
index 4078b9b9d..eb34b425d 100644
--- a/wpa_supplicant/bss.h
+++ b/wpa_supplicant/bss.h
@@ -135,7 +135,8 @@  void notify_bss_changes(struct wpa_supplicant *wpa_s, u32 changes,
 void wpa_bss_update_start(struct wpa_supplicant *wpa_s);
 void wpa_bss_update_scan_res(struct wpa_supplicant *wpa_s,
 			     struct wpa_scan_res *res,
-			     struct os_reltime *fetch_time);
+			     struct os_reltime *fetch_time,
+			     int secondary_scan);
 void wpa_bss_remove(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
 		    const char *reason);
 void wpa_bss_update_end(struct wpa_supplicant *wpa_s, struct scan_info *info,
diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c
index c05175822..2c3cf2601 100644
--- a/wpa_supplicant/ctrl_iface.c
+++ b/wpa_supplicant/ctrl_iface.c
@@ -9291,7 +9291,7 @@  static int wpas_ctrl_iface_driver_scan_res(struct wpa_supplicant *wpa_s,
 	}
 
 	os_get_reltime(&now);
-	wpa_bss_update_scan_res(wpa_s, res, &now);
+	wpa_bss_update_scan_res(wpa_s, res, &now, 0);
 	ret = 0;
 fail:
 	os_free(res);
diff --git a/wpa_supplicant/scan.c b/wpa_supplicant/scan.c
index c53474dae..8684d1c97 100644
--- a/wpa_supplicant/scan.c
+++ b/wpa_supplicant/scan.c
@@ -2509,7 +2509,7 @@  wpa_supplicant_get_scan_results(struct wpa_supplicant *wpa_s,
 	wpa_bss_update_start(wpa_s);
 	for (i = 0; i < scan_res->num; i++)
 		wpa_bss_update_scan_res(wpa_s, scan_res->res[i],
-					&scan_res->fetch_time);
+					&scan_res->fetch_time, 0);
 	wpa_bss_update_end(wpa_s, info, new_scan);
 
 	return scan_res;
diff --git a/wpa_supplicant/wnm_sta.c b/wpa_supplicant/wnm_sta.c
index c4c6651d4..c9ebcaca8 100644
--- a/wpa_supplicant/wnm_sta.c
+++ b/wpa_supplicant/wnm_sta.c
@@ -1354,7 +1354,7 @@  static int wnm_fetch_scan_results(struct wpa_supplicant *wpa_s)
 			scan_snr(res);
 			scan_est_throughput(wpa_s, res);
 			wpa_bss_update_scan_res(wpa_s, res,
-						&scan_res->fetch_time);
+						&scan_res->fetch_time, 0);
 		}
 	}
 
-- 
2.17.1