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 <dysu@google.com>
This commit is contained in:
David Su 2021-01-26 14:26:13 -08:00 committed by Jouni Malinen
parent 630b1fdba8
commit 11355a122d
4 changed files with 23 additions and 8 deletions

View file

@ -4792,7 +4792,7 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
} }
} else { } else {
wpa_dbg(wpa_s, MSG_DEBUG, "External program started a scan"); 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); wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_SCAN_STARTED);
} }
break; break;
@ -4800,7 +4800,7 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) { if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) {
wpa_s->scan_res_handler = NULL; wpa_s->scan_res_handler = NULL;
wpa_s->own_scan_running = 0; 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; wpa_s->last_scan_req = NORMAL_SCAN_REQ;
break; break;
} }
@ -4820,7 +4820,7 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
if (!(data && data->scan_info.external_scan)) if (!(data && data->scan_info.external_scan))
wpa_s->own_scan_running = 0; wpa_s->own_scan_running = 0;
if (data && data->scan_info.nl_scan_event) 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); radio_work_check_next(wpa_s);
break; break;
#endif /* CONFIG_NO_SCAN_PROCESSING */ #endif /* CONFIG_NO_SCAN_PROCESSING */

View file

@ -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); unsigned int delay = wpas_p2p_search_delay(wpa_s);
/* In case of concurrent P2P and external scans, delay P2P search. */ /* 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; delay = wpa_s->conf->p2p_search_delay;
wpa_printf(MSG_DEBUG, wpa_printf(MSG_DEBUG,
"P2P: Delay next P2P search by %d ms to let externally triggered scan complete", "P2P: Delay next P2P search by %d ms to let externally triggered scan complete",

View file

@ -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, dl_list_for_each(tmp, &radio->work, struct wpa_radio_work,
list) { list) {
if (os_strcmp(tmp->type, "scan") == 0 && if (os_strcmp(tmp->type, "scan") == 0 &&
radio->external_scan_running && external_scan_running(radio) &&
(((struct wpa_driver_scan_params *) (((struct wpa_driver_scan_params *)
tmp->ctx)->only_new_results || tmp->ctx)->only_new_results ||
tmp->wpa_s->clear_driver_scan_cache)) 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. * rejected by kernel.
*/ */
if (os_strcmp(tmp->type, "scan") == 0 && if (os_strcmp(tmp->type, "scan") == 0 &&
radio->external_scan_running && external_scan_running(radio) &&
(((struct wpa_driver_scan_params *) (((struct wpa_driver_scan_params *)
tmp->ctx)->only_new_results || tmp->ctx)->only_new_results ||
tmp->wpa_s->clear_driver_scan_cache)) 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) if (work->started)
return; /* already started and still in progress */ 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"); wpa_printf(MSG_DEBUG, "Delay radio work start until externally triggered scan completes");
return; return;
} }
@ -6059,6 +6059,10 @@ static void radio_remove_interface(struct wpa_supplicant *wpa_s)
wpa_s->ifname, radio->name); wpa_s->ifname, radio->name);
dl_list_del(&wpa_s->radio_list); dl_list_del(&wpa_s->radio_list);
radio_remove_works(wpa_s, NULL, 0); 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; wpa_s->radio = NULL;
if (!dl_list_empty(&radio->ifaces)) if (!dl_list_empty(&radio->ifaces))
return; /* Interfaces remain for this radio */ return; /* Interfaces remain for this radio */

View file

@ -332,12 +332,23 @@ struct wpa_global {
struct wpa_radio { struct wpa_radio {
char name[16]; /* from driver_ops get_radio_name() or empty if not char name[16]; /* from driver_ops get_radio_name() or empty if not
* available */ * 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; unsigned int num_active_works;
struct dl_list ifaces; /* struct wpa_supplicant::radio_list entries */ struct dl_list ifaces; /* struct wpa_supplicant::radio_list entries */
struct dl_list work; /* struct wpa_radio_work::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 #define MAX_ACTIVE_WORKS 2