From 11355a122d3171fdde1dea255116d3e77ccf2c54 Mon Sep 17 00:00:00 2001 From: David Su Date: Tue, 26 Jan 2021 14:26:13 -0800 Subject: [PATCH] Reset external_scan_running on interface deletion Currently, the external_scan_running flag is not reset when an interface is removed. Thus, if a connection attempt is made on another iface, it will fail due to wpa_supplicant incorrectly assuming the radio is still busy due to the ongoing scan. To fix this, convert external_scan_running to a pointer to the interface that started the scan. If this interface is removed, also reset the pointer to NULL so that other operations may continue on this radio. Test: 1. Start scan on wlan0 2. Remove wlan0 3. Can connect to a network on wlan1 Signed-off-by: David Su --- wpa_supplicant/events.c | 6 +++--- wpa_supplicant/p2p_supplicant.c | 2 +- wpa_supplicant/wpa_supplicant.c | 10 +++++++--- wpa_supplicant/wpa_supplicant_i.h | 13 ++++++++++++- 4 files changed, 23 insertions(+), 8 deletions(-) diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c index 48757dbd1..199829bcf 100644 --- a/wpa_supplicant/events.c +++ b/wpa_supplicant/events.c @@ -4792,7 +4792,7 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, } } else { wpa_dbg(wpa_s, MSG_DEBUG, "External program started a scan"); - wpa_s->radio->external_scan_running = 1; + wpa_s->radio->external_scan_req_interface = wpa_s; wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_SCAN_STARTED); } break; @@ -4800,7 +4800,7 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) { wpa_s->scan_res_handler = NULL; wpa_s->own_scan_running = 0; - wpa_s->radio->external_scan_running = 0; + wpa_s->radio->external_scan_req_interface = NULL; wpa_s->last_scan_req = NORMAL_SCAN_REQ; break; } @@ -4820,7 +4820,7 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, if (!(data && data->scan_info.external_scan)) wpa_s->own_scan_running = 0; if (data && data->scan_info.nl_scan_event) - wpa_s->radio->external_scan_running = 0; + wpa_s->radio->external_scan_req_interface = NULL; radio_work_check_next(wpa_s); break; #endif /* CONFIG_NO_SCAN_PROCESSING */ diff --git a/wpa_supplicant/p2p_supplicant.c b/wpa_supplicant/p2p_supplicant.c index a1eaaf128..62a3ea691 100644 --- a/wpa_supplicant/p2p_supplicant.c +++ b/wpa_supplicant/p2p_supplicant.c @@ -247,7 +247,7 @@ static void wpas_p2p_scan_res_handled(struct wpa_supplicant *wpa_s) unsigned int delay = wpas_p2p_search_delay(wpa_s); /* In case of concurrent P2P and external scans, delay P2P search. */ - if (wpa_s->radio->external_scan_running) { + if (external_scan_running(wpa_s->radio)) { delay = wpa_s->conf->p2p_search_delay; wpa_printf(MSG_DEBUG, "P2P: Delay next P2P search by %d ms to let externally triggered scan complete", diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c index a7e74344c..281fe11be 100644 --- a/wpa_supplicant/wpa_supplicant.c +++ b/wpa_supplicant/wpa_supplicant.c @@ -5868,7 +5868,7 @@ static struct wpa_radio_work * radio_work_get_next_work(struct wpa_radio *radio) dl_list_for_each(tmp, &radio->work, struct wpa_radio_work, list) { if (os_strcmp(tmp->type, "scan") == 0 && - radio->external_scan_running && + external_scan_running(radio) && (((struct wpa_driver_scan_params *) tmp->ctx)->only_new_results || tmp->wpa_s->clear_driver_scan_cache)) @@ -5924,7 +5924,7 @@ static struct wpa_radio_work * radio_work_get_next_work(struct wpa_radio *radio) * rejected by kernel. */ if (os_strcmp(tmp->type, "scan") == 0 && - radio->external_scan_running && + external_scan_running(radio) && (((struct wpa_driver_scan_params *) tmp->ctx)->only_new_results || tmp->wpa_s->clear_driver_scan_cache)) @@ -5963,7 +5963,7 @@ static void radio_start_next_work(void *eloop_ctx, void *timeout_ctx) if (work->started) return; /* already started and still in progress */ - if (wpa_s && wpa_s->radio->external_scan_running) { + if (wpa_s && external_scan_running(wpa_s->radio)) { wpa_printf(MSG_DEBUG, "Delay radio work start until externally triggered scan completes"); return; } @@ -6059,6 +6059,10 @@ static void radio_remove_interface(struct wpa_supplicant *wpa_s) wpa_s->ifname, radio->name); dl_list_del(&wpa_s->radio_list); radio_remove_works(wpa_s, NULL, 0); + /* If the interface that triggered the external scan was removed, the + * external scan is no longer running. */ + if (wpa_s == radio->external_scan_req_interface) + radio->external_scan_req_interface = NULL; wpa_s->radio = NULL; if (!dl_list_empty(&radio->ifaces)) return; /* Interfaces remain for this radio */ diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h index 0ce7447fe..f70f3c853 100644 --- a/wpa_supplicant/wpa_supplicant_i.h +++ b/wpa_supplicant/wpa_supplicant_i.h @@ -332,12 +332,23 @@ struct wpa_global { struct wpa_radio { char name[16]; /* from driver_ops get_radio_name() or empty if not * available */ - unsigned int external_scan_running:1; + /** NULL if no external scan running. */ + struct wpa_supplicant *external_scan_req_interface; unsigned int num_active_works; struct dl_list ifaces; /* struct wpa_supplicant::radio_list entries */ struct dl_list work; /* struct wpa_radio_work::list entries */ }; +/** + * Checks whether an external scan is running on a given radio. + * @radio: Pointer to radio struct + * Returns: true if an external scan is running, false otherwise. + */ +static inline bool external_scan_running(struct wpa_radio *radio) +{ + return radio && radio->external_scan_req_interface; +} + #define MAX_ACTIVE_WORKS 2