139beb93cSSam Leffler /*
239beb93cSSam Leffler * WPA Supplicant - Scanning
34bc52338SCy Schubert * Copyright (c) 2003-2019, Jouni Malinen <j@w1.fi>
439beb93cSSam Leffler *
5f05cddf9SRui Paulo * This software may be distributed under the terms of the BSD license.
6f05cddf9SRui Paulo * See README for more details.
739beb93cSSam Leffler */
839beb93cSSam Leffler
9e28a4053SRui Paulo #include "utils/includes.h"
1039beb93cSSam Leffler
11e28a4053SRui Paulo #include "utils/common.h"
12e28a4053SRui Paulo #include "utils/eloop.h"
13e28a4053SRui Paulo #include "common/ieee802_11_defs.h"
145b9c547cSRui Paulo #include "common/wpa_ctrl.h"
1539beb93cSSam Leffler #include "config.h"
1639beb93cSSam Leffler #include "wpa_supplicant_i.h"
17e28a4053SRui Paulo #include "driver_i.h"
1839beb93cSSam Leffler #include "wps_supplicant.h"
19f05cddf9SRui Paulo #include "p2p_supplicant.h"
20f05cddf9SRui Paulo #include "p2p/p2p.h"
21f05cddf9SRui Paulo #include "hs20_supplicant.h"
22e28a4053SRui Paulo #include "notify.h"
23e28a4053SRui Paulo #include "bss.h"
24e28a4053SRui Paulo #include "scan.h"
255b9c547cSRui Paulo #include "mesh.h"
2639beb93cSSam Leffler
27*a90b9d01SCy Schubert static struct wpabuf * wpa_supplicant_extra_ies(struct wpa_supplicant *wpa_s);
28*a90b9d01SCy Schubert
2939beb93cSSam Leffler
wpa_supplicant_gen_assoc_event(struct wpa_supplicant * wpa_s)3039beb93cSSam Leffler static void wpa_supplicant_gen_assoc_event(struct wpa_supplicant *wpa_s)
3139beb93cSSam Leffler {
3239beb93cSSam Leffler struct wpa_ssid *ssid;
3339beb93cSSam Leffler union wpa_event_data data;
3439beb93cSSam Leffler
3539beb93cSSam Leffler ssid = wpa_supplicant_get_ssid(wpa_s);
3639beb93cSSam Leffler if (ssid == NULL)
3739beb93cSSam Leffler return;
3839beb93cSSam Leffler
39e28a4053SRui Paulo if (wpa_s->current_ssid == NULL) {
4039beb93cSSam Leffler wpa_s->current_ssid = ssid;
41e28a4053SRui Paulo wpas_notify_network_changed(wpa_s);
42e28a4053SRui Paulo }
4339beb93cSSam Leffler wpa_supplicant_initiate_eapol(wpa_s);
44f05cddf9SRui Paulo wpa_dbg(wpa_s, MSG_DEBUG, "Already associated with a configured "
45f05cddf9SRui Paulo "network - generating associated event");
4639beb93cSSam Leffler os_memset(&data, 0, sizeof(data));
4739beb93cSSam Leffler wpa_supplicant_event(wpa_s, EVENT_ASSOC, &data);
4839beb93cSSam Leffler }
4939beb93cSSam Leffler
5039beb93cSSam Leffler
5139beb93cSSam Leffler #ifdef CONFIG_WPS
wpas_wps_in_use(struct wpa_supplicant * wpa_s,enum wps_request_type * req_type)52f05cddf9SRui Paulo static int wpas_wps_in_use(struct wpa_supplicant *wpa_s,
5339beb93cSSam Leffler enum wps_request_type *req_type)
5439beb93cSSam Leffler {
5539beb93cSSam Leffler struct wpa_ssid *ssid;
5639beb93cSSam Leffler int wps = 0;
5739beb93cSSam Leffler
58f05cddf9SRui Paulo for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
5939beb93cSSam Leffler if (!(ssid->key_mgmt & WPA_KEY_MGMT_WPS))
6039beb93cSSam Leffler continue;
6139beb93cSSam Leffler
6239beb93cSSam Leffler wps = 1;
6339beb93cSSam Leffler *req_type = wpas_wps_get_req_type(ssid);
64780fb4a2SCy Schubert if (ssid->eap.phase1 && os_strstr(ssid->eap.phase1, "pbc=1"))
6539beb93cSSam Leffler return 2;
6639beb93cSSam Leffler }
6739beb93cSSam Leffler
68f05cddf9SRui Paulo #ifdef CONFIG_P2P
69f05cddf9SRui Paulo if (!wpa_s->global->p2p_disabled && wpa_s->global->p2p &&
70f05cddf9SRui Paulo !wpa_s->conf->p2p_disabled) {
71f05cddf9SRui Paulo wpa_s->wps->dev.p2p = 1;
72f05cddf9SRui Paulo if (!wps) {
73f05cddf9SRui Paulo wps = 1;
74f05cddf9SRui Paulo *req_type = WPS_REQ_ENROLLEE_INFO;
75f05cddf9SRui Paulo }
76f05cddf9SRui Paulo }
77f05cddf9SRui Paulo #endif /* CONFIG_P2P */
78f05cddf9SRui Paulo
7939beb93cSSam Leffler return wps;
8039beb93cSSam Leffler }
8139beb93cSSam Leffler #endif /* CONFIG_WPS */
8239beb93cSSam Leffler
833157ba21SRui Paulo
wpa_setup_mac_addr_rand_params(struct wpa_driver_scan_params * params,const u8 * mac_addr)84c1d255d3SCy Schubert static int wpa_setup_mac_addr_rand_params(struct wpa_driver_scan_params *params,
85c1d255d3SCy Schubert const u8 *mac_addr)
86c1d255d3SCy Schubert {
87c1d255d3SCy Schubert u8 *tmp;
88c1d255d3SCy Schubert
89c1d255d3SCy Schubert if (params->mac_addr) {
90c1d255d3SCy Schubert params->mac_addr_mask = NULL;
91c1d255d3SCy Schubert os_free(params->mac_addr);
92c1d255d3SCy Schubert params->mac_addr = NULL;
93c1d255d3SCy Schubert }
94c1d255d3SCy Schubert
95c1d255d3SCy Schubert params->mac_addr_rand = 1;
96c1d255d3SCy Schubert
97c1d255d3SCy Schubert if (!mac_addr)
98c1d255d3SCy Schubert return 0;
99c1d255d3SCy Schubert
100c1d255d3SCy Schubert tmp = os_malloc(2 * ETH_ALEN);
101c1d255d3SCy Schubert if (!tmp)
102c1d255d3SCy Schubert return -1;
103c1d255d3SCy Schubert
104c1d255d3SCy Schubert os_memcpy(tmp, mac_addr, 2 * ETH_ALEN);
105c1d255d3SCy Schubert params->mac_addr = tmp;
106c1d255d3SCy Schubert params->mac_addr_mask = tmp + ETH_ALEN;
107c1d255d3SCy Schubert return 0;
108c1d255d3SCy Schubert }
109c1d255d3SCy Schubert
110c1d255d3SCy Schubert
111f05cddf9SRui Paulo /**
112f05cddf9SRui Paulo * wpa_supplicant_enabled_networks - Check whether there are enabled networks
113f05cddf9SRui Paulo * @wpa_s: Pointer to wpa_supplicant data
114f05cddf9SRui Paulo * Returns: 0 if no networks are enabled, >0 if networks are enabled
115f05cddf9SRui Paulo *
116f05cddf9SRui Paulo * This function is used to figure out whether any networks (or Interworking
117f05cddf9SRui Paulo * with enabled credentials and auto_interworking) are present in the current
118f05cddf9SRui Paulo * configuration.
119f05cddf9SRui Paulo */
wpa_supplicant_enabled_networks(struct wpa_supplicant * wpa_s)120f05cddf9SRui Paulo int wpa_supplicant_enabled_networks(struct wpa_supplicant *wpa_s)
1213157ba21SRui Paulo {
122f05cddf9SRui Paulo struct wpa_ssid *ssid = wpa_s->conf->ssid;
123f05cddf9SRui Paulo int count = 0, disabled = 0;
1245b9c547cSRui Paulo
1255b9c547cSRui Paulo if (wpa_s->p2p_mgmt)
1265b9c547cSRui Paulo return 0; /* no normal network profiles on p2p_mgmt interface */
1275b9c547cSRui Paulo
1283157ba21SRui Paulo while (ssid) {
129f05cddf9SRui Paulo if (!wpas_network_disabled(wpa_s, ssid))
130f05cddf9SRui Paulo count++;
131f05cddf9SRui Paulo else
132f05cddf9SRui Paulo disabled++;
1333157ba21SRui Paulo ssid = ssid->next;
1343157ba21SRui Paulo }
135f05cddf9SRui Paulo if (wpa_s->conf->cred && wpa_s->conf->interworking &&
136f05cddf9SRui Paulo wpa_s->conf->auto_interworking)
137f05cddf9SRui Paulo count++;
138f05cddf9SRui Paulo if (count == 0 && disabled > 0) {
139f05cddf9SRui Paulo wpa_dbg(wpa_s, MSG_DEBUG, "No enabled networks (%d disabled "
140f05cddf9SRui Paulo "networks)", disabled);
141f05cddf9SRui Paulo }
142f05cddf9SRui Paulo return count;
1433157ba21SRui Paulo }
1443157ba21SRui Paulo
1453157ba21SRui Paulo
wpa_supplicant_assoc_try(struct wpa_supplicant * wpa_s,struct wpa_ssid * ssid)146e28a4053SRui Paulo static void wpa_supplicant_assoc_try(struct wpa_supplicant *wpa_s,
147e28a4053SRui Paulo struct wpa_ssid *ssid)
148e28a4053SRui Paulo {
14985732ac8SCy Schubert int min_temp_disabled = 0;
15085732ac8SCy Schubert
151e28a4053SRui Paulo while (ssid) {
15285732ac8SCy Schubert if (!wpas_network_disabled(wpa_s, ssid)) {
15385732ac8SCy Schubert int temp_disabled = wpas_temp_disabled(wpa_s, ssid);
15485732ac8SCy Schubert
15585732ac8SCy Schubert if (temp_disabled <= 0)
156e28a4053SRui Paulo break;
15785732ac8SCy Schubert
15885732ac8SCy Schubert if (!min_temp_disabled ||
15985732ac8SCy Schubert temp_disabled < min_temp_disabled)
16085732ac8SCy Schubert min_temp_disabled = temp_disabled;
16185732ac8SCy Schubert }
162e28a4053SRui Paulo ssid = ssid->next;
163e28a4053SRui Paulo }
164e28a4053SRui Paulo
165e28a4053SRui Paulo /* ap_scan=2 mode - try to associate with each SSID. */
166e28a4053SRui Paulo if (ssid == NULL) {
167f05cddf9SRui Paulo wpa_dbg(wpa_s, MSG_DEBUG, "wpa_supplicant_assoc_try: Reached "
168e28a4053SRui Paulo "end of scan list - go back to beginning");
169e28a4053SRui Paulo wpa_s->prev_scan_ssid = WILDCARD_SSID_SCAN;
17085732ac8SCy Schubert wpa_supplicant_req_scan(wpa_s, min_temp_disabled, 0);
171e28a4053SRui Paulo return;
172e28a4053SRui Paulo }
173e28a4053SRui Paulo if (ssid->next) {
174e28a4053SRui Paulo /* Continue from the next SSID on the next attempt. */
175e28a4053SRui Paulo wpa_s->prev_scan_ssid = ssid;
176e28a4053SRui Paulo } else {
177e28a4053SRui Paulo /* Start from the beginning of the SSID list. */
178e28a4053SRui Paulo wpa_s->prev_scan_ssid = WILDCARD_SSID_SCAN;
179e28a4053SRui Paulo }
180e28a4053SRui Paulo wpa_supplicant_associate(wpa_s, NULL, ssid);
181e28a4053SRui Paulo }
182e28a4053SRui Paulo
183e28a4053SRui Paulo
wpas_trigger_scan_cb(struct wpa_radio_work * work,int deinit)1845b9c547cSRui Paulo static void wpas_trigger_scan_cb(struct wpa_radio_work *work, int deinit)
185e28a4053SRui Paulo {
1865b9c547cSRui Paulo struct wpa_supplicant *wpa_s = work->wpa_s;
1875b9c547cSRui Paulo struct wpa_driver_scan_params *params = work->ctx;
1885b9c547cSRui Paulo int ret;
189e28a4053SRui Paulo
1905b9c547cSRui Paulo if (deinit) {
1915b9c547cSRui Paulo if (!work->started) {
1925b9c547cSRui Paulo wpa_scan_free_params(params);
193e28a4053SRui Paulo return;
194e28a4053SRui Paulo }
1955b9c547cSRui Paulo wpa_supplicant_notify_scanning(wpa_s, 0);
1965b9c547cSRui Paulo wpas_notify_scan_done(wpa_s, 0);
1975b9c547cSRui Paulo wpa_s->scan_work = NULL;
198e28a4053SRui Paulo return;
199e28a4053SRui Paulo }
2005b9c547cSRui Paulo
201c1d255d3SCy Schubert if ((wpa_s->mac_addr_rand_enable & MAC_ADDR_RAND_SCAN) &&
202c1d255d3SCy Schubert wpa_s->wpa_state <= WPA_SCANNING)
203c1d255d3SCy Schubert wpa_setup_mac_addr_rand_params(params, wpa_s->mac_addr_scan);
204c1d255d3SCy Schubert
2055b9c547cSRui Paulo if (wpas_update_random_addr_disassoc(wpa_s) < 0) {
2065b9c547cSRui Paulo wpa_msg(wpa_s, MSG_INFO,
2075b9c547cSRui Paulo "Failed to assign random MAC address for a scan");
208780fb4a2SCy Schubert wpa_scan_free_params(params);
209780fb4a2SCy Schubert wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_SCAN_FAILED "ret=-1");
2105b9c547cSRui Paulo radio_work_done(work);
2115b9c547cSRui Paulo return;
212e28a4053SRui Paulo }
2135b9c547cSRui Paulo
2145b9c547cSRui Paulo wpa_supplicant_notify_scanning(wpa_s, 1);
2155b9c547cSRui Paulo
2165b9c547cSRui Paulo if (wpa_s->clear_driver_scan_cache) {
2175b9c547cSRui Paulo wpa_printf(MSG_DEBUG,
2185b9c547cSRui Paulo "Request driver to clear scan cache due to local BSS flush");
2195b9c547cSRui Paulo params->only_new_results = 1;
2205b9c547cSRui Paulo }
2215b9c547cSRui Paulo ret = wpa_drv_scan(wpa_s, params);
22285732ac8SCy Schubert /*
22385732ac8SCy Schubert * Store the obtained vendor scan cookie (if any) in wpa_s context.
22485732ac8SCy Schubert * The current design is to allow only one scan request on each
22585732ac8SCy Schubert * interface, hence having this scan cookie stored in wpa_s context is
22685732ac8SCy Schubert * fine for now.
22785732ac8SCy Schubert *
22885732ac8SCy Schubert * Revisit this logic if concurrent scan operations per interface
22985732ac8SCy Schubert * is supported.
23085732ac8SCy Schubert */
23185732ac8SCy Schubert if (ret == 0)
23285732ac8SCy Schubert wpa_s->curr_scan_cookie = params->scan_cookie;
2335b9c547cSRui Paulo wpa_scan_free_params(params);
2345b9c547cSRui Paulo work->ctx = NULL;
2355b9c547cSRui Paulo if (ret) {
23685732ac8SCy Schubert int retry = wpa_s->last_scan_req != MANUAL_SCAN_REQ &&
23785732ac8SCy Schubert !wpa_s->beacon_rep_data.token;
2385b9c547cSRui Paulo
2395b9c547cSRui Paulo if (wpa_s->disconnected)
2405b9c547cSRui Paulo retry = 0;
2415b9c547cSRui Paulo
242c1d255d3SCy Schubert /* do not retry if operation is not supported */
243c1d255d3SCy Schubert if (ret == -EOPNOTSUPP)
244c1d255d3SCy Schubert retry = 0;
245c1d255d3SCy Schubert
2465b9c547cSRui Paulo wpa_supplicant_notify_scanning(wpa_s, 0);
2475b9c547cSRui Paulo wpas_notify_scan_done(wpa_s, 0);
2485b9c547cSRui Paulo if (wpa_s->wpa_state == WPA_SCANNING)
2495b9c547cSRui Paulo wpa_supplicant_set_state(wpa_s,
2505b9c547cSRui Paulo wpa_s->scan_prev_wpa_state);
2515b9c547cSRui Paulo wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_SCAN_FAILED "ret=%d%s",
2525b9c547cSRui Paulo ret, retry ? " retry=1" : "");
2535b9c547cSRui Paulo radio_work_done(work);
2545b9c547cSRui Paulo
2555b9c547cSRui Paulo if (retry) {
2565b9c547cSRui Paulo /* Restore scan_req since we will try to scan again */
2575b9c547cSRui Paulo wpa_s->scan_req = wpa_s->last_scan_req;
2585b9c547cSRui Paulo wpa_supplicant_req_scan(wpa_s, 1, 0);
25985732ac8SCy Schubert } else if (wpa_s->scan_res_handler) {
26085732ac8SCy Schubert /* Clear the scan_res_handler */
26185732ac8SCy Schubert wpa_s->scan_res_handler = NULL;
2625b9c547cSRui Paulo }
26385732ac8SCy Schubert
264*a90b9d01SCy Schubert #ifndef CONFIG_NO_RRM
26585732ac8SCy Schubert if (wpa_s->beacon_rep_data.token)
26685732ac8SCy Schubert wpas_rrm_refuse_request(wpa_s);
267*a90b9d01SCy Schubert #endif /* CONFIG_NO_RRM */
26885732ac8SCy Schubert
2695b9c547cSRui Paulo return;
2705b9c547cSRui Paulo }
2715b9c547cSRui Paulo
2725b9c547cSRui Paulo os_get_reltime(&wpa_s->scan_trigger_time);
2735b9c547cSRui Paulo wpa_s->scan_runs++;
2745b9c547cSRui Paulo wpa_s->normal_scans++;
2755b9c547cSRui Paulo wpa_s->own_scan_requested = 1;
2765b9c547cSRui Paulo wpa_s->clear_driver_scan_cache = 0;
2775b9c547cSRui Paulo wpa_s->scan_work = work;
278e28a4053SRui Paulo }
279e28a4053SRui Paulo
280e28a4053SRui Paulo
281f05cddf9SRui Paulo /**
282f05cddf9SRui Paulo * wpa_supplicant_trigger_scan - Request driver to start a scan
283f05cddf9SRui Paulo * @wpa_s: Pointer to wpa_supplicant data
284f05cddf9SRui Paulo * @params: Scan parameters
285*a90b9d01SCy Schubert * @default_ies: Whether or not to use the default IEs in the Probe Request
286*a90b9d01SCy Schubert * frames. Note that this will free any existing IEs set in @params, so this
287*a90b9d01SCy Schubert * shouldn't be set if the IEs have already been set with
288*a90b9d01SCy Schubert * wpa_supplicant_extra_ies(). Otherwise, wpabuf_free() will lead to a
289*a90b9d01SCy Schubert * double-free.
290*a90b9d01SCy Schubert * @next: Whether or not to perform this scan as the next radio work
291f05cddf9SRui Paulo * Returns: 0 on success, -1 on failure
292f05cddf9SRui Paulo */
wpa_supplicant_trigger_scan(struct wpa_supplicant * wpa_s,struct wpa_driver_scan_params * params,bool default_ies,bool next)293e28a4053SRui Paulo int wpa_supplicant_trigger_scan(struct wpa_supplicant *wpa_s,
294*a90b9d01SCy Schubert struct wpa_driver_scan_params *params,
295*a90b9d01SCy Schubert bool default_ies, bool next)
296e28a4053SRui Paulo {
2975b9c547cSRui Paulo struct wpa_driver_scan_params *ctx;
298*a90b9d01SCy Schubert struct wpabuf *ies = NULL;
299e28a4053SRui Paulo
3005b9c547cSRui Paulo if (wpa_s->scan_work) {
3015b9c547cSRui Paulo wpa_dbg(wpa_s, MSG_INFO, "Reject scan trigger since one is already pending");
3025b9c547cSRui Paulo return -1;
303f05cddf9SRui Paulo }
304f05cddf9SRui Paulo
305*a90b9d01SCy Schubert if (default_ies) {
306*a90b9d01SCy Schubert if (params->extra_ies_len) {
307*a90b9d01SCy Schubert os_free((u8 *) params->extra_ies);
308*a90b9d01SCy Schubert params->extra_ies = NULL;
309*a90b9d01SCy Schubert params->extra_ies_len = 0;
310*a90b9d01SCy Schubert }
311*a90b9d01SCy Schubert ies = wpa_supplicant_extra_ies(wpa_s);
312*a90b9d01SCy Schubert if (ies) {
313*a90b9d01SCy Schubert params->extra_ies = wpabuf_head(ies);
314*a90b9d01SCy Schubert params->extra_ies_len = wpabuf_len(ies);
315*a90b9d01SCy Schubert }
316*a90b9d01SCy Schubert }
3175b9c547cSRui Paulo ctx = wpa_scan_clone_params(params);
318*a90b9d01SCy Schubert if (ies) {
319*a90b9d01SCy Schubert wpabuf_free(ies);
320*a90b9d01SCy Schubert params->extra_ies = NULL;
321*a90b9d01SCy Schubert params->extra_ies_len = 0;
322*a90b9d01SCy Schubert }
323*a90b9d01SCy Schubert wpa_s->last_scan_all_chan = !params->freqs;
324*a90b9d01SCy Schubert wpa_s->last_scan_non_coloc_6ghz = params->non_coloc_6ghz;
325*a90b9d01SCy Schubert
326*a90b9d01SCy Schubert if (wpa_s->crossed_6ghz_dom) {
327*a90b9d01SCy Schubert wpa_printf(MSG_DEBUG, "First scan after crossing 6 GHz domain");
328*a90b9d01SCy Schubert wpa_s->crossed_6ghz_dom = false;
329*a90b9d01SCy Schubert }
330*a90b9d01SCy Schubert
331780fb4a2SCy Schubert if (!ctx ||
332*a90b9d01SCy Schubert radio_add_work(wpa_s, 0, "scan", next, wpas_trigger_scan_cb,
333*a90b9d01SCy Schubert ctx) < 0) {
3345b9c547cSRui Paulo wpa_scan_free_params(ctx);
335780fb4a2SCy Schubert wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_SCAN_FAILED "ret=-1");
3365b9c547cSRui Paulo return -1;
3375b9c547cSRui Paulo }
3385b9c547cSRui Paulo
339*a90b9d01SCy Schubert wpa_s->wps_scan_done = false;
340*a90b9d01SCy Schubert
3415b9c547cSRui Paulo return 0;
342f05cddf9SRui Paulo }
343f05cddf9SRui Paulo
344f05cddf9SRui Paulo
345f05cddf9SRui Paulo static void
wpa_supplicant_delayed_sched_scan_timeout(void * eloop_ctx,void * timeout_ctx)346f05cddf9SRui Paulo wpa_supplicant_delayed_sched_scan_timeout(void *eloop_ctx, void *timeout_ctx)
347f05cddf9SRui Paulo {
348f05cddf9SRui Paulo struct wpa_supplicant *wpa_s = eloop_ctx;
349f05cddf9SRui Paulo
350f05cddf9SRui Paulo wpa_dbg(wpa_s, MSG_DEBUG, "Starting delayed sched scan");
351f05cddf9SRui Paulo
352f05cddf9SRui Paulo if (wpa_supplicant_req_sched_scan(wpa_s))
353f05cddf9SRui Paulo wpa_supplicant_req_scan(wpa_s, 0, 0);
354f05cddf9SRui Paulo }
355f05cddf9SRui Paulo
356f05cddf9SRui Paulo
357f05cddf9SRui Paulo static void
wpa_supplicant_sched_scan_timeout(void * eloop_ctx,void * timeout_ctx)358f05cddf9SRui Paulo wpa_supplicant_sched_scan_timeout(void *eloop_ctx, void *timeout_ctx)
359f05cddf9SRui Paulo {
360f05cddf9SRui Paulo struct wpa_supplicant *wpa_s = eloop_ctx;
361f05cddf9SRui Paulo
362f05cddf9SRui Paulo wpa_dbg(wpa_s, MSG_DEBUG, "Sched scan timeout - stopping it");
363f05cddf9SRui Paulo
364f05cddf9SRui Paulo wpa_s->sched_scan_timed_out = 1;
365f05cddf9SRui Paulo wpa_supplicant_cancel_sched_scan(wpa_s);
366f05cddf9SRui Paulo }
367f05cddf9SRui Paulo
368f05cddf9SRui Paulo
369780fb4a2SCy Schubert static int
wpa_supplicant_start_sched_scan(struct wpa_supplicant * wpa_s,struct wpa_driver_scan_params * params)370780fb4a2SCy Schubert wpa_supplicant_start_sched_scan(struct wpa_supplicant *wpa_s,
371780fb4a2SCy Schubert struct wpa_driver_scan_params *params)
372f05cddf9SRui Paulo {
373f05cddf9SRui Paulo int ret;
374f05cddf9SRui Paulo
375f05cddf9SRui Paulo wpa_supplicant_notify_scanning(wpa_s, 1);
376780fb4a2SCy Schubert ret = wpa_drv_sched_scan(wpa_s, params);
377f05cddf9SRui Paulo if (ret)
378f05cddf9SRui Paulo wpa_supplicant_notify_scanning(wpa_s, 0);
379f05cddf9SRui Paulo else
380f05cddf9SRui Paulo wpa_s->sched_scanning = 1;
381f05cddf9SRui Paulo
382f05cddf9SRui Paulo return ret;
383f05cddf9SRui Paulo }
384f05cddf9SRui Paulo
385f05cddf9SRui Paulo
wpa_supplicant_stop_sched_scan(struct wpa_supplicant * wpa_s)386780fb4a2SCy Schubert static int wpa_supplicant_stop_sched_scan(struct wpa_supplicant *wpa_s)
387f05cddf9SRui Paulo {
388f05cddf9SRui Paulo int ret;
389f05cddf9SRui Paulo
390f05cddf9SRui Paulo ret = wpa_drv_stop_sched_scan(wpa_s);
391f05cddf9SRui Paulo if (ret) {
392f05cddf9SRui Paulo wpa_dbg(wpa_s, MSG_DEBUG, "stopping sched_scan failed!");
393f05cddf9SRui Paulo /* TODO: what to do if stopping fails? */
394f05cddf9SRui Paulo return -1;
395f05cddf9SRui Paulo }
396e28a4053SRui Paulo
397e28a4053SRui Paulo return ret;
398e28a4053SRui Paulo }
399e28a4053SRui Paulo
400e28a4053SRui Paulo
401e28a4053SRui Paulo static struct wpa_driver_scan_filter *
wpa_supplicant_build_filter_ssids(struct wpa_config * conf,size_t * num_ssids)402e28a4053SRui Paulo wpa_supplicant_build_filter_ssids(struct wpa_config *conf, size_t *num_ssids)
403e28a4053SRui Paulo {
404e28a4053SRui Paulo struct wpa_driver_scan_filter *ssids;
405e28a4053SRui Paulo struct wpa_ssid *ssid;
406e28a4053SRui Paulo size_t count;
407e28a4053SRui Paulo
408e28a4053SRui Paulo *num_ssids = 0;
409e28a4053SRui Paulo if (!conf->filter_ssids)
410e28a4053SRui Paulo return NULL;
411e28a4053SRui Paulo
412e28a4053SRui Paulo for (count = 0, ssid = conf->ssid; ssid; ssid = ssid->next) {
413e28a4053SRui Paulo if (ssid->ssid && ssid->ssid_len)
414e28a4053SRui Paulo count++;
415e28a4053SRui Paulo }
416e28a4053SRui Paulo if (count == 0)
417e28a4053SRui Paulo return NULL;
4185b9c547cSRui Paulo ssids = os_calloc(count, sizeof(struct wpa_driver_scan_filter));
419e28a4053SRui Paulo if (ssids == NULL)
420e28a4053SRui Paulo return NULL;
421e28a4053SRui Paulo
422e28a4053SRui Paulo for (ssid = conf->ssid; ssid; ssid = ssid->next) {
423e28a4053SRui Paulo if (!ssid->ssid || !ssid->ssid_len)
424e28a4053SRui Paulo continue;
425e28a4053SRui Paulo os_memcpy(ssids[*num_ssids].ssid, ssid->ssid, ssid->ssid_len);
426e28a4053SRui Paulo ssids[*num_ssids].ssid_len = ssid->ssid_len;
427e28a4053SRui Paulo (*num_ssids)++;
428e28a4053SRui Paulo }
429e28a4053SRui Paulo
430e28a4053SRui Paulo return ssids;
431e28a4053SRui Paulo }
432e28a4053SRui Paulo
433e28a4053SRui Paulo
wpa_supplicant_optimize_freqs(struct wpa_supplicant * wpa_s,struct wpa_driver_scan_params * params)434f05cddf9SRui Paulo static void wpa_supplicant_optimize_freqs(
435f05cddf9SRui Paulo struct wpa_supplicant *wpa_s, struct wpa_driver_scan_params *params)
43639beb93cSSam Leffler {
437f05cddf9SRui Paulo #ifdef CONFIG_P2P
438f05cddf9SRui Paulo if (params->freqs == NULL && wpa_s->p2p_in_provisioning &&
439f05cddf9SRui Paulo wpa_s->go_params) {
440f05cddf9SRui Paulo /* Optimize provisioning state scan based on GO information */
441f05cddf9SRui Paulo if (wpa_s->p2p_in_provisioning < 5 &&
442f05cddf9SRui Paulo wpa_s->go_params->freq > 0) {
443f05cddf9SRui Paulo wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Scan only GO "
444f05cddf9SRui Paulo "preferred frequency %d MHz",
445f05cddf9SRui Paulo wpa_s->go_params->freq);
4465b9c547cSRui Paulo params->freqs = os_calloc(2, sizeof(int));
447f05cddf9SRui Paulo if (params->freqs)
448f05cddf9SRui Paulo params->freqs[0] = wpa_s->go_params->freq;
449f05cddf9SRui Paulo } else if (wpa_s->p2p_in_provisioning < 8 &&
450f05cddf9SRui Paulo wpa_s->go_params->freq_list[0]) {
451f05cddf9SRui Paulo wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Scan only common "
452f05cddf9SRui Paulo "channels");
453f05cddf9SRui Paulo int_array_concat(¶ms->freqs,
454f05cddf9SRui Paulo wpa_s->go_params->freq_list);
455f05cddf9SRui Paulo if (params->freqs)
456f05cddf9SRui Paulo int_array_sort_unique(params->freqs);
457f05cddf9SRui Paulo }
458f05cddf9SRui Paulo wpa_s->p2p_in_provisioning++;
459f05cddf9SRui Paulo }
4605b9c547cSRui Paulo
4615b9c547cSRui Paulo if (params->freqs == NULL && wpa_s->p2p_in_invitation) {
462*a90b9d01SCy Schubert struct wpa_ssid *ssid = wpa_s->current_ssid;
463*a90b9d01SCy Schubert
464*a90b9d01SCy Schubert /*
465*a90b9d01SCy Schubert * Perform a single-channel scan if the GO has already been
466*a90b9d01SCy Schubert * discovered on another non-P2P interface. Note that a scan
467*a90b9d01SCy Schubert * initiated by a P2P interface (e.g., the device interface)
468*a90b9d01SCy Schubert * should already have sufficient IEs and scan results will be
469*a90b9d01SCy Schubert * fetched on interface creation in that case.
470*a90b9d01SCy Schubert */
471*a90b9d01SCy Schubert if (wpa_s->p2p_in_invitation == 1 && ssid) {
472*a90b9d01SCy Schubert struct wpa_supplicant *ifs;
473*a90b9d01SCy Schubert struct wpa_bss *bss = NULL;
474*a90b9d01SCy Schubert const u8 *bssid = ssid->bssid_set ? ssid->bssid : NULL;
475*a90b9d01SCy Schubert
476*a90b9d01SCy Schubert dl_list_for_each(ifs, &wpa_s->radio->ifaces,
477*a90b9d01SCy Schubert struct wpa_supplicant, radio_list) {
478*a90b9d01SCy Schubert bss = wpa_bss_get(ifs, bssid, ssid->ssid,
479*a90b9d01SCy Schubert ssid->ssid_len);
480*a90b9d01SCy Schubert if (bss)
481*a90b9d01SCy Schubert break;
482*a90b9d01SCy Schubert }
483*a90b9d01SCy Schubert if (bss && !disabled_freq(wpa_s, bss->freq)) {
484*a90b9d01SCy Schubert params->freqs = os_calloc(2, sizeof(int));
485*a90b9d01SCy Schubert if (params->freqs) {
486*a90b9d01SCy Schubert wpa_dbg(wpa_s, MSG_DEBUG,
487*a90b9d01SCy Schubert "P2P: Scan only the known GO frequency %d MHz during invitation",
488*a90b9d01SCy Schubert bss->freq);
489*a90b9d01SCy Schubert params->freqs[0] = bss->freq;
490*a90b9d01SCy Schubert }
491*a90b9d01SCy Schubert }
492*a90b9d01SCy Schubert }
493*a90b9d01SCy Schubert
4945b9c547cSRui Paulo /*
4955b9c547cSRui Paulo * Optimize scan based on GO information during persistent
4965b9c547cSRui Paulo * group reinvocation
4975b9c547cSRui Paulo */
498*a90b9d01SCy Schubert if (!params->freqs && wpa_s->p2p_in_invitation < 5 &&
4995b9c547cSRui Paulo wpa_s->p2p_invite_go_freq > 0) {
500*a90b9d01SCy Schubert if (wpa_s->p2p_invite_go_freq == 2 ||
501*a90b9d01SCy Schubert wpa_s->p2p_invite_go_freq == 5) {
502*a90b9d01SCy Schubert enum hostapd_hw_mode mode;
503*a90b9d01SCy Schubert
504*a90b9d01SCy Schubert wpa_dbg(wpa_s, MSG_DEBUG,
505*a90b9d01SCy Schubert "P2P: Scan only GO preferred band %d GHz during invitation",
506*a90b9d01SCy Schubert wpa_s->p2p_invite_go_freq);
507*a90b9d01SCy Schubert
508*a90b9d01SCy Schubert if (!wpa_s->hw.modes)
509*a90b9d01SCy Schubert return;
510*a90b9d01SCy Schubert mode = wpa_s->p2p_invite_go_freq == 5 ?
511*a90b9d01SCy Schubert HOSTAPD_MODE_IEEE80211A :
512*a90b9d01SCy Schubert HOSTAPD_MODE_IEEE80211G;
513*a90b9d01SCy Schubert if (wpa_s->p2p_in_invitation <= 2)
514*a90b9d01SCy Schubert wpa_add_scan_freqs_list(wpa_s, mode,
515*a90b9d01SCy Schubert params, false,
516*a90b9d01SCy Schubert false, true);
517*a90b9d01SCy Schubert if (!params->freqs || params->freqs[0] == 0)
518*a90b9d01SCy Schubert wpa_add_scan_freqs_list(wpa_s, mode,
519*a90b9d01SCy Schubert params, false,
520*a90b9d01SCy Schubert false, false);
521*a90b9d01SCy Schubert } else {
522*a90b9d01SCy Schubert wpa_dbg(wpa_s, MSG_DEBUG,
523*a90b9d01SCy Schubert "P2P: Scan only GO preferred frequency %d MHz during invitation",
5245b9c547cSRui Paulo wpa_s->p2p_invite_go_freq);
5255b9c547cSRui Paulo params->freqs = os_calloc(2, sizeof(int));
5265b9c547cSRui Paulo if (params->freqs)
527*a90b9d01SCy Schubert params->freqs[0] =
528*a90b9d01SCy Schubert wpa_s->p2p_invite_go_freq;
529*a90b9d01SCy Schubert }
5305b9c547cSRui Paulo }
5315b9c547cSRui Paulo wpa_s->p2p_in_invitation++;
5325b9c547cSRui Paulo if (wpa_s->p2p_in_invitation > 20) {
5335b9c547cSRui Paulo /*
5345b9c547cSRui Paulo * This should not really happen since the variable is
5355b9c547cSRui Paulo * cleared on group removal, but if it does happen, make
5365b9c547cSRui Paulo * sure we do not get stuck in special invitation scan
5375b9c547cSRui Paulo * mode.
5385b9c547cSRui Paulo */
5395b9c547cSRui Paulo wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Clear p2p_in_invitation");
5405b9c547cSRui Paulo wpa_s->p2p_in_invitation = 0;
541*a90b9d01SCy Schubert wpa_s->p2p_retry_limit = 0;
5425b9c547cSRui Paulo }
5435b9c547cSRui Paulo }
544f05cddf9SRui Paulo #endif /* CONFIG_P2P */
545f05cddf9SRui Paulo
546f05cddf9SRui Paulo #ifdef CONFIG_WPS
547f05cddf9SRui Paulo if (params->freqs == NULL && wpa_s->after_wps && wpa_s->wps_freq) {
548f05cddf9SRui Paulo /*
549f05cddf9SRui Paulo * Optimize post-provisioning scan based on channel used
550f05cddf9SRui Paulo * during provisioning.
551f05cddf9SRui Paulo */
552f05cddf9SRui Paulo wpa_dbg(wpa_s, MSG_DEBUG, "WPS: Scan only frequency %u MHz "
553f05cddf9SRui Paulo "that was used during provisioning", wpa_s->wps_freq);
5545b9c547cSRui Paulo params->freqs = os_calloc(2, sizeof(int));
555f05cddf9SRui Paulo if (params->freqs)
556f05cddf9SRui Paulo params->freqs[0] = wpa_s->wps_freq;
557f05cddf9SRui Paulo wpa_s->after_wps--;
5585b9c547cSRui Paulo } else if (wpa_s->after_wps)
5595b9c547cSRui Paulo wpa_s->after_wps--;
560f05cddf9SRui Paulo
561f05cddf9SRui Paulo if (params->freqs == NULL && wpa_s->known_wps_freq && wpa_s->wps_freq)
562f05cddf9SRui Paulo {
563f05cddf9SRui Paulo /* Optimize provisioning scan based on already known channel */
564f05cddf9SRui Paulo wpa_dbg(wpa_s, MSG_DEBUG, "WPS: Scan only frequency %u MHz",
565f05cddf9SRui Paulo wpa_s->wps_freq);
5665b9c547cSRui Paulo params->freqs = os_calloc(2, sizeof(int));
567f05cddf9SRui Paulo if (params->freqs)
568f05cddf9SRui Paulo params->freqs[0] = wpa_s->wps_freq;
569f05cddf9SRui Paulo wpa_s->known_wps_freq = 0; /* only do this once */
570f05cddf9SRui Paulo }
571f05cddf9SRui Paulo #endif /* CONFIG_WPS */
572f05cddf9SRui Paulo }
573f05cddf9SRui Paulo
574f05cddf9SRui Paulo
575f05cddf9SRui Paulo #ifdef CONFIG_INTERWORKING
wpas_add_interworking_elements(struct wpa_supplicant * wpa_s,struct wpabuf * buf)576f05cddf9SRui Paulo static void wpas_add_interworking_elements(struct wpa_supplicant *wpa_s,
577f05cddf9SRui Paulo struct wpabuf *buf)
578f05cddf9SRui Paulo {
579f05cddf9SRui Paulo wpabuf_put_u8(buf, WLAN_EID_INTERWORKING);
580f05cddf9SRui Paulo wpabuf_put_u8(buf, is_zero_ether_addr(wpa_s->conf->hessid) ? 1 :
581f05cddf9SRui Paulo 1 + ETH_ALEN);
582f05cddf9SRui Paulo wpabuf_put_u8(buf, wpa_s->conf->access_network_type);
583f05cddf9SRui Paulo /* No Venue Info */
584f05cddf9SRui Paulo if (!is_zero_ether_addr(wpa_s->conf->hessid))
585f05cddf9SRui Paulo wpabuf_put_data(buf, wpa_s->conf->hessid, ETH_ALEN);
586f05cddf9SRui Paulo }
587f05cddf9SRui Paulo #endif /* CONFIG_INTERWORKING */
588f05cddf9SRui Paulo
589f05cddf9SRui Paulo
59085732ac8SCy Schubert #ifdef CONFIG_MBO
wpas_fils_req_param_add_max_channel(struct wpa_supplicant * wpa_s,struct wpabuf ** ie)59185732ac8SCy Schubert static void wpas_fils_req_param_add_max_channel(struct wpa_supplicant *wpa_s,
59285732ac8SCy Schubert struct wpabuf **ie)
59385732ac8SCy Schubert {
59485732ac8SCy Schubert if (wpabuf_resize(ie, 5)) {
59585732ac8SCy Schubert wpa_printf(MSG_DEBUG,
59685732ac8SCy Schubert "Failed to allocate space for FILS Request Parameters element");
59785732ac8SCy Schubert return;
59885732ac8SCy Schubert }
59985732ac8SCy Schubert
60085732ac8SCy Schubert /* FILS Request Parameters element */
60185732ac8SCy Schubert wpabuf_put_u8(*ie, WLAN_EID_EXTENSION);
60285732ac8SCy Schubert wpabuf_put_u8(*ie, 3); /* FILS Request attribute length */
60385732ac8SCy Schubert wpabuf_put_u8(*ie, WLAN_EID_EXT_FILS_REQ_PARAMS);
60485732ac8SCy Schubert /* Parameter control bitmap */
60585732ac8SCy Schubert wpabuf_put_u8(*ie, 0);
60685732ac8SCy Schubert /* Max Channel Time field - contains the value of MaxChannelTime
60785732ac8SCy Schubert * parameter of the MLME-SCAN.request primitive represented in units of
60885732ac8SCy Schubert * TUs, as an unsigned integer. A Max Channel Time field value of 255
60985732ac8SCy Schubert * is used to indicate any duration of more than 254 TUs, or an
61085732ac8SCy Schubert * unspecified or unknown duration. (IEEE Std 802.11ai-2016, 9.4.2.178)
61185732ac8SCy Schubert */
61285732ac8SCy Schubert wpabuf_put_u8(*ie, 255);
61385732ac8SCy Schubert }
61485732ac8SCy Schubert #endif /* CONFIG_MBO */
61585732ac8SCy Schubert
61685732ac8SCy Schubert
wpa_supplicant_set_default_scan_ies(struct wpa_supplicant * wpa_s)617780fb4a2SCy Schubert void wpa_supplicant_set_default_scan_ies(struct wpa_supplicant *wpa_s)
618780fb4a2SCy Schubert {
619780fb4a2SCy Schubert struct wpabuf *default_ies = NULL;
620780fb4a2SCy Schubert u8 ext_capab[18];
621c1d255d3SCy Schubert int ext_capab_len, frame_id;
622780fb4a2SCy Schubert enum wpa_driver_if_type type = WPA_IF_STATION;
623780fb4a2SCy Schubert
624780fb4a2SCy Schubert #ifdef CONFIG_P2P
625780fb4a2SCy Schubert if (wpa_s->p2p_group_interface == P2P_GROUP_INTERFACE_CLIENT)
626780fb4a2SCy Schubert type = WPA_IF_P2P_CLIENT;
627780fb4a2SCy Schubert #endif /* CONFIG_P2P */
628780fb4a2SCy Schubert
629780fb4a2SCy Schubert wpa_drv_get_ext_capa(wpa_s, type);
630780fb4a2SCy Schubert
631780fb4a2SCy Schubert ext_capab_len = wpas_build_ext_capab(wpa_s, ext_capab,
632*a90b9d01SCy Schubert sizeof(ext_capab), NULL);
633780fb4a2SCy Schubert if (ext_capab_len > 0 &&
634780fb4a2SCy Schubert wpabuf_resize(&default_ies, ext_capab_len) == 0)
635780fb4a2SCy Schubert wpabuf_put_data(default_ies, ext_capab, ext_capab_len);
636780fb4a2SCy Schubert
637780fb4a2SCy Schubert #ifdef CONFIG_MBO
63885732ac8SCy Schubert if (wpa_s->enable_oce & OCE_STA)
63985732ac8SCy Schubert wpas_fils_req_param_add_max_channel(wpa_s, &default_ies);
64085732ac8SCy Schubert /* Send MBO and OCE capabilities */
64185732ac8SCy Schubert if (wpabuf_resize(&default_ies, 12) == 0)
642780fb4a2SCy Schubert wpas_mbo_scan_ie(wpa_s, default_ies);
643780fb4a2SCy Schubert #endif /* CONFIG_MBO */
644780fb4a2SCy Schubert
645c1d255d3SCy Schubert if (type == WPA_IF_P2P_CLIENT)
646c1d255d3SCy Schubert frame_id = VENDOR_ELEM_PROBE_REQ_P2P;
647c1d255d3SCy Schubert else
648c1d255d3SCy Schubert frame_id = VENDOR_ELEM_PROBE_REQ;
649c1d255d3SCy Schubert
650c1d255d3SCy Schubert if (wpa_s->vendor_elem[frame_id]) {
651c1d255d3SCy Schubert size_t len;
652c1d255d3SCy Schubert
653c1d255d3SCy Schubert len = wpabuf_len(wpa_s->vendor_elem[frame_id]);
654c1d255d3SCy Schubert if (len > 0 && wpabuf_resize(&default_ies, len) == 0)
655c1d255d3SCy Schubert wpabuf_put_buf(default_ies,
656c1d255d3SCy Schubert wpa_s->vendor_elem[frame_id]);
657c1d255d3SCy Schubert }
658c1d255d3SCy Schubert
659780fb4a2SCy Schubert if (default_ies)
660780fb4a2SCy Schubert wpa_drv_set_default_scan_ies(wpa_s, wpabuf_head(default_ies),
661780fb4a2SCy Schubert wpabuf_len(default_ies));
662780fb4a2SCy Schubert wpabuf_free(default_ies);
663780fb4a2SCy Schubert }
664780fb4a2SCy Schubert
665780fb4a2SCy Schubert
wpa_supplicant_ml_probe_ie(int mld_id,u16 links)666*a90b9d01SCy Schubert static struct wpabuf * wpa_supplicant_ml_probe_ie(int mld_id, u16 links)
667*a90b9d01SCy Schubert {
668*a90b9d01SCy Schubert struct wpabuf *extra_ie;
669*a90b9d01SCy Schubert u16 control = MULTI_LINK_CONTROL_TYPE_PROBE_REQ;
670*a90b9d01SCy Schubert size_t len = 3 + 4 + 4 * MAX_NUM_MLD_LINKS;
671*a90b9d01SCy Schubert u8 link_id;
672*a90b9d01SCy Schubert u8 *len_pos;
673*a90b9d01SCy Schubert
674*a90b9d01SCy Schubert if (mld_id >= 0) {
675*a90b9d01SCy Schubert control |= EHT_ML_PRES_BM_PROBE_REQ_AP_MLD_ID;
676*a90b9d01SCy Schubert len++;
677*a90b9d01SCy Schubert }
678*a90b9d01SCy Schubert
679*a90b9d01SCy Schubert extra_ie = wpabuf_alloc(len);
680*a90b9d01SCy Schubert if (!extra_ie)
681*a90b9d01SCy Schubert return NULL;
682*a90b9d01SCy Schubert
683*a90b9d01SCy Schubert wpabuf_put_u8(extra_ie, WLAN_EID_EXTENSION);
684*a90b9d01SCy Schubert len_pos = wpabuf_put(extra_ie, 1);
685*a90b9d01SCy Schubert wpabuf_put_u8(extra_ie, WLAN_EID_EXT_MULTI_LINK);
686*a90b9d01SCy Schubert
687*a90b9d01SCy Schubert wpabuf_put_le16(extra_ie, control);
688*a90b9d01SCy Schubert
689*a90b9d01SCy Schubert /* common info length and MLD ID (if requested) */
690*a90b9d01SCy Schubert if (mld_id >= 0) {
691*a90b9d01SCy Schubert wpabuf_put_u8(extra_ie, 2);
692*a90b9d01SCy Schubert wpabuf_put_u8(extra_ie, mld_id);
693*a90b9d01SCy Schubert
694*a90b9d01SCy Schubert wpa_printf(MSG_DEBUG, "MLD: ML probe targeted at MLD ID %d",
695*a90b9d01SCy Schubert mld_id);
696*a90b9d01SCy Schubert } else {
697*a90b9d01SCy Schubert wpabuf_put_u8(extra_ie, 1);
698*a90b9d01SCy Schubert
699*a90b9d01SCy Schubert wpa_printf(MSG_DEBUG, "MLD: ML probe targeted at receiving AP");
700*a90b9d01SCy Schubert }
701*a90b9d01SCy Schubert
702*a90b9d01SCy Schubert if (!links)
703*a90b9d01SCy Schubert wpa_printf(MSG_DEBUG, "MLD: Probing all links");
704*a90b9d01SCy Schubert else
705*a90b9d01SCy Schubert wpa_printf(MSG_DEBUG, "MLD: Probing links 0x%04x", links);
706*a90b9d01SCy Schubert
707*a90b9d01SCy Schubert for_each_link(links, link_id) {
708*a90b9d01SCy Schubert wpabuf_put_u8(extra_ie, EHT_ML_SUB_ELEM_PER_STA_PROFILE);
709*a90b9d01SCy Schubert
710*a90b9d01SCy Schubert /* Subelement length includes only the control */
711*a90b9d01SCy Schubert wpabuf_put_u8(extra_ie, 2);
712*a90b9d01SCy Schubert
713*a90b9d01SCy Schubert control = link_id | EHT_PER_STA_CTRL_COMPLETE_PROFILE_MSK;
714*a90b9d01SCy Schubert
715*a90b9d01SCy Schubert wpabuf_put_le16(extra_ie, control);
716*a90b9d01SCy Schubert }
717*a90b9d01SCy Schubert
718*a90b9d01SCy Schubert *len_pos = (u8 *) wpabuf_put(extra_ie, 0) - len_pos - 1;
719*a90b9d01SCy Schubert
720*a90b9d01SCy Schubert return extra_ie;
721*a90b9d01SCy Schubert }
722*a90b9d01SCy Schubert
723*a90b9d01SCy Schubert
wpa_supplicant_extra_ies(struct wpa_supplicant * wpa_s)724f05cddf9SRui Paulo static struct wpabuf * wpa_supplicant_extra_ies(struct wpa_supplicant *wpa_s)
725f05cddf9SRui Paulo {
726f05cddf9SRui Paulo struct wpabuf *extra_ie = NULL;
727325151a3SRui Paulo u8 ext_capab[18];
728325151a3SRui Paulo int ext_capab_len;
72939beb93cSSam Leffler #ifdef CONFIG_WPS
730e28a4053SRui Paulo int wps = 0;
73139beb93cSSam Leffler enum wps_request_type req_type = WPS_REQ_ENROLLEE_INFO;
73239beb93cSSam Leffler #endif /* CONFIG_WPS */
733f05cddf9SRui Paulo
734*a90b9d01SCy Schubert if (!is_zero_ether_addr(wpa_s->ml_probe_bssid)) {
735*a90b9d01SCy Schubert extra_ie = wpa_supplicant_ml_probe_ie(wpa_s->ml_probe_mld_id,
736*a90b9d01SCy Schubert wpa_s->ml_probe_links);
737*a90b9d01SCy Schubert
738*a90b9d01SCy Schubert /* No other elements should be included in the probe request */
739*a90b9d01SCy Schubert wpa_printf(MSG_DEBUG, "MLD: Scan including only ML element");
740*a90b9d01SCy Schubert return extra_ie;
741*a90b9d01SCy Schubert }
742*a90b9d01SCy Schubert
743780fb4a2SCy Schubert #ifdef CONFIG_P2P
744780fb4a2SCy Schubert if (wpa_s->p2p_group_interface == P2P_GROUP_INTERFACE_CLIENT)
745780fb4a2SCy Schubert wpa_drv_get_ext_capa(wpa_s, WPA_IF_P2P_CLIENT);
746780fb4a2SCy Schubert else
747780fb4a2SCy Schubert #endif /* CONFIG_P2P */
748780fb4a2SCy Schubert wpa_drv_get_ext_capa(wpa_s, WPA_IF_STATION);
749780fb4a2SCy Schubert
750325151a3SRui Paulo ext_capab_len = wpas_build_ext_capab(wpa_s, ext_capab,
751*a90b9d01SCy Schubert sizeof(ext_capab), NULL);
752325151a3SRui Paulo if (ext_capab_len > 0 &&
753325151a3SRui Paulo wpabuf_resize(&extra_ie, ext_capab_len) == 0)
754325151a3SRui Paulo wpabuf_put_data(extra_ie, ext_capab, ext_capab_len);
755325151a3SRui Paulo
756f05cddf9SRui Paulo #ifdef CONFIG_INTERWORKING
757f05cddf9SRui Paulo if (wpa_s->conf->interworking &&
758f05cddf9SRui Paulo wpabuf_resize(&extra_ie, 100) == 0)
759f05cddf9SRui Paulo wpas_add_interworking_elements(wpa_s, extra_ie);
760f05cddf9SRui Paulo #endif /* CONFIG_INTERWORKING */
761f05cddf9SRui Paulo
76285732ac8SCy Schubert #ifdef CONFIG_MBO
76385732ac8SCy Schubert if (wpa_s->enable_oce & OCE_STA)
76485732ac8SCy Schubert wpas_fils_req_param_add_max_channel(wpa_s, &extra_ie);
76585732ac8SCy Schubert #endif /* CONFIG_MBO */
76685732ac8SCy Schubert
767f05cddf9SRui Paulo #ifdef CONFIG_WPS
768f05cddf9SRui Paulo wps = wpas_wps_in_use(wpa_s, &req_type);
769f05cddf9SRui Paulo
770f05cddf9SRui Paulo if (wps) {
771f05cddf9SRui Paulo struct wpabuf *wps_ie;
772f05cddf9SRui Paulo wps_ie = wps_build_probe_req_ie(wps == 2 ? DEV_PW_PUSHBUTTON :
773f05cddf9SRui Paulo DEV_PW_DEFAULT,
774f05cddf9SRui Paulo &wpa_s->wps->dev,
775f05cddf9SRui Paulo wpa_s->wps->uuid, req_type,
776f05cddf9SRui Paulo 0, NULL);
777f05cddf9SRui Paulo if (wps_ie) {
778f05cddf9SRui Paulo if (wpabuf_resize(&extra_ie, wpabuf_len(wps_ie)) == 0)
779f05cddf9SRui Paulo wpabuf_put_buf(extra_ie, wps_ie);
780f05cddf9SRui Paulo wpabuf_free(wps_ie);
781f05cddf9SRui Paulo }
782f05cddf9SRui Paulo }
783f05cddf9SRui Paulo
784f05cddf9SRui Paulo #ifdef CONFIG_P2P
785f05cddf9SRui Paulo if (wps) {
786f05cddf9SRui Paulo size_t ielen = p2p_scan_ie_buf_len(wpa_s->global->p2p);
787f05cddf9SRui Paulo if (wpabuf_resize(&extra_ie, ielen) == 0)
788f05cddf9SRui Paulo wpas_p2p_scan_ie(wpa_s, extra_ie);
789f05cddf9SRui Paulo }
790f05cddf9SRui Paulo #endif /* CONFIG_P2P */
791f05cddf9SRui Paulo
7925b9c547cSRui Paulo wpa_supplicant_mesh_add_scan_ie(wpa_s, &extra_ie);
7935b9c547cSRui Paulo
794f05cddf9SRui Paulo #endif /* CONFIG_WPS */
795f05cddf9SRui Paulo
7965b9c547cSRui Paulo #ifdef CONFIG_HS20
7974bc52338SCy Schubert if (wpa_s->conf->hs20 && wpabuf_resize(&extra_ie, 9) == 0)
7984bc52338SCy Schubert wpas_hs20_add_indication(extra_ie, -1, 0);
7995b9c547cSRui Paulo #endif /* CONFIG_HS20 */
8005b9c547cSRui Paulo
801325151a3SRui Paulo #ifdef CONFIG_FST
802325151a3SRui Paulo if (wpa_s->fst_ies &&
803325151a3SRui Paulo wpabuf_resize(&extra_ie, wpabuf_len(wpa_s->fst_ies)) == 0)
804325151a3SRui Paulo wpabuf_put_buf(extra_ie, wpa_s->fst_ies);
805325151a3SRui Paulo #endif /* CONFIG_FST */
806325151a3SRui Paulo
807780fb4a2SCy Schubert #ifdef CONFIG_MBO
80885732ac8SCy Schubert /* Send MBO and OCE capabilities */
80985732ac8SCy Schubert if (wpabuf_resize(&extra_ie, 12) == 0)
810780fb4a2SCy Schubert wpas_mbo_scan_ie(wpa_s, extra_ie);
811780fb4a2SCy Schubert #endif /* CONFIG_MBO */
812780fb4a2SCy Schubert
813780fb4a2SCy Schubert if (wpa_s->vendor_elem[VENDOR_ELEM_PROBE_REQ]) {
814780fb4a2SCy Schubert struct wpabuf *buf = wpa_s->vendor_elem[VENDOR_ELEM_PROBE_REQ];
815780fb4a2SCy Schubert
816780fb4a2SCy Schubert if (wpabuf_resize(&extra_ie, wpabuf_len(buf)) == 0)
817780fb4a2SCy Schubert wpabuf_put_buf(extra_ie, buf);
818780fb4a2SCy Schubert }
819780fb4a2SCy Schubert
820f05cddf9SRui Paulo return extra_ie;
821f05cddf9SRui Paulo }
822f05cddf9SRui Paulo
823f05cddf9SRui Paulo
824f05cddf9SRui Paulo #ifdef CONFIG_P2P
825f05cddf9SRui Paulo
826f05cddf9SRui Paulo /*
827f05cddf9SRui Paulo * Check whether there are any enabled networks or credentials that could be
828f05cddf9SRui Paulo * used for a non-P2P connection.
829f05cddf9SRui Paulo */
non_p2p_network_enabled(struct wpa_supplicant * wpa_s)830f05cddf9SRui Paulo static int non_p2p_network_enabled(struct wpa_supplicant *wpa_s)
831f05cddf9SRui Paulo {
832f05cddf9SRui Paulo struct wpa_ssid *ssid;
833f05cddf9SRui Paulo
834f05cddf9SRui Paulo for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
835f05cddf9SRui Paulo if (wpas_network_disabled(wpa_s, ssid))
836f05cddf9SRui Paulo continue;
837f05cddf9SRui Paulo if (!ssid->p2p_group)
838f05cddf9SRui Paulo return 1;
839f05cddf9SRui Paulo }
840f05cddf9SRui Paulo
841f05cddf9SRui Paulo if (wpa_s->conf->cred && wpa_s->conf->interworking &&
842f05cddf9SRui Paulo wpa_s->conf->auto_interworking)
843f05cddf9SRui Paulo return 1;
844f05cddf9SRui Paulo
845f05cddf9SRui Paulo return 0;
846f05cddf9SRui Paulo }
847f05cddf9SRui Paulo
848f05cddf9SRui Paulo #endif /* CONFIG_P2P */
849f05cddf9SRui Paulo
850f05cddf9SRui Paulo
wpa_add_scan_freqs_list(struct wpa_supplicant * wpa_s,enum hostapd_hw_mode band,struct wpa_driver_scan_params * params,bool is_6ghz,bool only_6ghz_psc,bool exclude_radar)851c1d255d3SCy Schubert int wpa_add_scan_freqs_list(struct wpa_supplicant *wpa_s,
8525b9c547cSRui Paulo enum hostapd_hw_mode band,
853*a90b9d01SCy Schubert struct wpa_driver_scan_params *params,
854*a90b9d01SCy Schubert bool is_6ghz, bool only_6ghz_psc,
855*a90b9d01SCy Schubert bool exclude_radar)
8565b9c547cSRui Paulo {
8575b9c547cSRui Paulo /* Include only supported channels for the specified band */
8585b9c547cSRui Paulo struct hostapd_hw_modes *mode;
859c1d255d3SCy Schubert int num_chans = 0;
860c1d255d3SCy Schubert int *freqs, i;
8615b9c547cSRui Paulo
862c1d255d3SCy Schubert mode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes, band, is_6ghz);
863*a90b9d01SCy Schubert if (!mode || !mode->num_channels)
864c1d255d3SCy Schubert return -1;
865c1d255d3SCy Schubert
866c1d255d3SCy Schubert if (params->freqs) {
867c1d255d3SCy Schubert while (params->freqs[num_chans])
868c1d255d3SCy Schubert num_chans++;
8695b9c547cSRui Paulo }
8705b9c547cSRui Paulo
871c1d255d3SCy Schubert freqs = os_realloc(params->freqs,
872c1d255d3SCy Schubert (num_chans + mode->num_channels + 1) * sizeof(int));
873c1d255d3SCy Schubert if (!freqs)
874c1d255d3SCy Schubert return -1;
875c1d255d3SCy Schubert
876c1d255d3SCy Schubert params->freqs = freqs;
877c1d255d3SCy Schubert for (i = 0; i < mode->num_channels; i++) {
8785b9c547cSRui Paulo if (mode->channels[i].flag & HOSTAPD_CHAN_DISABLED)
8795b9c547cSRui Paulo continue;
880*a90b9d01SCy Schubert if (exclude_radar &&
881*a90b9d01SCy Schubert (mode->channels[i].flag & HOSTAPD_CHAN_RADAR))
882*a90b9d01SCy Schubert continue;
883*a90b9d01SCy Schubert
884*a90b9d01SCy Schubert if (is_6ghz && only_6ghz_psc &&
885*a90b9d01SCy Schubert !is_6ghz_psc_frequency(mode->channels[i].freq))
886*a90b9d01SCy Schubert continue;
887*a90b9d01SCy Schubert
888c1d255d3SCy Schubert params->freqs[num_chans++] = mode->channels[i].freq;
8895b9c547cSRui Paulo }
890c1d255d3SCy Schubert params->freqs[num_chans] = 0;
891c1d255d3SCy Schubert
892c1d255d3SCy Schubert return 0;
8935b9c547cSRui Paulo }
8945b9c547cSRui Paulo
8955b9c547cSRui Paulo
wpa_setband_scan_freqs(struct wpa_supplicant * wpa_s,struct wpa_driver_scan_params * params)8965b9c547cSRui Paulo static void wpa_setband_scan_freqs(struct wpa_supplicant *wpa_s,
8975b9c547cSRui Paulo struct wpa_driver_scan_params *params)
8985b9c547cSRui Paulo {
8995b9c547cSRui Paulo if (wpa_s->hw.modes == NULL)
9005b9c547cSRui Paulo return; /* unknown what channels the driver supports */
9015b9c547cSRui Paulo if (params->freqs)
9025b9c547cSRui Paulo return; /* already using a limited channel set */
903c1d255d3SCy Schubert
904c1d255d3SCy Schubert if (wpa_s->setband_mask & WPA_SETBAND_5G)
905c1d255d3SCy Schubert wpa_add_scan_freqs_list(wpa_s, HOSTAPD_MODE_IEEE80211A, params,
906*a90b9d01SCy Schubert false, false, false);
907c1d255d3SCy Schubert if (wpa_s->setband_mask & WPA_SETBAND_2G)
908c1d255d3SCy Schubert wpa_add_scan_freqs_list(wpa_s, HOSTAPD_MODE_IEEE80211G, params,
909*a90b9d01SCy Schubert false, false, false);
910c1d255d3SCy Schubert if (wpa_s->setband_mask & WPA_SETBAND_6G)
911c1d255d3SCy Schubert wpa_add_scan_freqs_list(wpa_s, HOSTAPD_MODE_IEEE80211A, params,
912*a90b9d01SCy Schubert true, false, false);
9135b9c547cSRui Paulo }
9145b9c547cSRui Paulo
9155b9c547cSRui Paulo
wpa_add_scan_ssid(struct wpa_supplicant * wpa_s,struct wpa_driver_scan_params * params,size_t max_ssids,const u8 * ssid,size_t ssid_len)91685732ac8SCy Schubert static void wpa_add_scan_ssid(struct wpa_supplicant *wpa_s,
91785732ac8SCy Schubert struct wpa_driver_scan_params *params,
91885732ac8SCy Schubert size_t max_ssids, const u8 *ssid, size_t ssid_len)
91985732ac8SCy Schubert {
92085732ac8SCy Schubert unsigned int j;
92185732ac8SCy Schubert
92285732ac8SCy Schubert for (j = 0; j < params->num_ssids; j++) {
92385732ac8SCy Schubert if (params->ssids[j].ssid_len == ssid_len &&
92485732ac8SCy Schubert params->ssids[j].ssid &&
92585732ac8SCy Schubert os_memcmp(params->ssids[j].ssid, ssid, ssid_len) == 0)
92685732ac8SCy Schubert return; /* already in the list */
92785732ac8SCy Schubert }
92885732ac8SCy Schubert
92985732ac8SCy Schubert if (params->num_ssids + 1 > max_ssids) {
93085732ac8SCy Schubert wpa_printf(MSG_DEBUG, "Over max scan SSIDs for manual request");
93185732ac8SCy Schubert return;
93285732ac8SCy Schubert }
93385732ac8SCy Schubert
93485732ac8SCy Schubert wpa_printf(MSG_DEBUG, "Scan SSID (manual request): %s",
93585732ac8SCy Schubert wpa_ssid_txt(ssid, ssid_len));
93685732ac8SCy Schubert
93785732ac8SCy Schubert params->ssids[params->num_ssids].ssid = ssid;
93885732ac8SCy Schubert params->ssids[params->num_ssids].ssid_len = ssid_len;
93985732ac8SCy Schubert params->num_ssids++;
94085732ac8SCy Schubert }
94185732ac8SCy Schubert
94285732ac8SCy Schubert
wpa_add_owe_scan_ssid(struct wpa_supplicant * wpa_s,struct wpa_driver_scan_params * params,struct wpa_ssid * ssid,size_t max_ssids)94385732ac8SCy Schubert static void wpa_add_owe_scan_ssid(struct wpa_supplicant *wpa_s,
94485732ac8SCy Schubert struct wpa_driver_scan_params *params,
94585732ac8SCy Schubert struct wpa_ssid *ssid, size_t max_ssids)
94685732ac8SCy Schubert {
94785732ac8SCy Schubert #ifdef CONFIG_OWE
94885732ac8SCy Schubert struct wpa_bss *bss;
94985732ac8SCy Schubert
95085732ac8SCy Schubert if (!(ssid->key_mgmt & WPA_KEY_MGMT_OWE))
95185732ac8SCy Schubert return;
95285732ac8SCy Schubert
95385732ac8SCy Schubert wpa_printf(MSG_DEBUG, "OWE: Look for transition mode AP. ssid=%s",
95485732ac8SCy Schubert wpa_ssid_txt(ssid->ssid, ssid->ssid_len));
95585732ac8SCy Schubert
95685732ac8SCy Schubert dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
95785732ac8SCy Schubert const u8 *owe, *pos, *end;
95885732ac8SCy Schubert const u8 *owe_ssid;
95985732ac8SCy Schubert size_t owe_ssid_len;
96085732ac8SCy Schubert
96185732ac8SCy Schubert if (bss->ssid_len != ssid->ssid_len ||
96285732ac8SCy Schubert os_memcmp(bss->ssid, ssid->ssid, ssid->ssid_len) != 0)
96385732ac8SCy Schubert continue;
96485732ac8SCy Schubert
96585732ac8SCy Schubert owe = wpa_bss_get_vendor_ie(bss, OWE_IE_VENDOR_TYPE);
96685732ac8SCy Schubert if (!owe || owe[1] < 4)
96785732ac8SCy Schubert continue;
96885732ac8SCy Schubert
96985732ac8SCy Schubert pos = owe + 6;
97085732ac8SCy Schubert end = owe + 2 + owe[1];
97185732ac8SCy Schubert
97285732ac8SCy Schubert /* Must include BSSID and ssid_len */
97385732ac8SCy Schubert if (end - pos < ETH_ALEN + 1)
97485732ac8SCy Schubert return;
97585732ac8SCy Schubert
97685732ac8SCy Schubert /* Skip BSSID */
97785732ac8SCy Schubert pos += ETH_ALEN;
97885732ac8SCy Schubert owe_ssid_len = *pos++;
97985732ac8SCy Schubert owe_ssid = pos;
98085732ac8SCy Schubert
98185732ac8SCy Schubert if ((size_t) (end - pos) < owe_ssid_len ||
98285732ac8SCy Schubert owe_ssid_len > SSID_MAX_LEN)
98385732ac8SCy Schubert return;
98485732ac8SCy Schubert
98585732ac8SCy Schubert wpa_printf(MSG_DEBUG,
98685732ac8SCy Schubert "OWE: scan_ssids: transition mode OWE ssid=%s",
98785732ac8SCy Schubert wpa_ssid_txt(owe_ssid, owe_ssid_len));
98885732ac8SCy Schubert
98985732ac8SCy Schubert wpa_add_scan_ssid(wpa_s, params, max_ssids,
99085732ac8SCy Schubert owe_ssid, owe_ssid_len);
99185732ac8SCy Schubert return;
99285732ac8SCy Schubert }
99385732ac8SCy Schubert #endif /* CONFIG_OWE */
99485732ac8SCy Schubert }
99585732ac8SCy Schubert
99685732ac8SCy Schubert
wpa_set_scan_ssids(struct wpa_supplicant * wpa_s,struct wpa_driver_scan_params * params,size_t max_ssids)9975b9c547cSRui Paulo static void wpa_set_scan_ssids(struct wpa_supplicant *wpa_s,
9985b9c547cSRui Paulo struct wpa_driver_scan_params *params,
9995b9c547cSRui Paulo size_t max_ssids)
10005b9c547cSRui Paulo {
10015b9c547cSRui Paulo unsigned int i;
10025b9c547cSRui Paulo struct wpa_ssid *ssid;
10035b9c547cSRui Paulo
1004780fb4a2SCy Schubert /*
1005780fb4a2SCy Schubert * For devices with max_ssids greater than 1, leave the last slot empty
1006780fb4a2SCy Schubert * for adding the wildcard scan entry.
1007780fb4a2SCy Schubert */
1008780fb4a2SCy Schubert max_ssids = max_ssids > 1 ? max_ssids - 1 : max_ssids;
1009780fb4a2SCy Schubert
10105b9c547cSRui Paulo for (i = 0; i < wpa_s->scan_id_count; i++) {
10115b9c547cSRui Paulo ssid = wpa_config_get_network(wpa_s->conf, wpa_s->scan_id[i]);
101285732ac8SCy Schubert if (!ssid)
10135b9c547cSRui Paulo continue;
101485732ac8SCy Schubert if (ssid->scan_ssid)
101585732ac8SCy Schubert wpa_add_scan_ssid(wpa_s, params, max_ssids,
101685732ac8SCy Schubert ssid->ssid, ssid->ssid_len);
101785732ac8SCy Schubert /*
101885732ac8SCy Schubert * Also add the SSID of the OWE BSS, to allow discovery of
101985732ac8SCy Schubert * transition mode APs more quickly.
102085732ac8SCy Schubert */
102185732ac8SCy Schubert wpa_add_owe_scan_ssid(wpa_s, params, ssid, max_ssids);
10225b9c547cSRui Paulo }
10235b9c547cSRui Paulo
10245b9c547cSRui Paulo wpa_s->scan_id_count = 0;
10255b9c547cSRui Paulo }
10265b9c547cSRui Paulo
10275b9c547cSRui Paulo
wpa_set_ssids_from_scan_req(struct wpa_supplicant * wpa_s,struct wpa_driver_scan_params * params,size_t max_ssids)1028325151a3SRui Paulo static int wpa_set_ssids_from_scan_req(struct wpa_supplicant *wpa_s,
1029325151a3SRui Paulo struct wpa_driver_scan_params *params,
1030325151a3SRui Paulo size_t max_ssids)
1031325151a3SRui Paulo {
1032325151a3SRui Paulo unsigned int i;
1033325151a3SRui Paulo
1034325151a3SRui Paulo if (wpa_s->ssids_from_scan_req == NULL ||
1035325151a3SRui Paulo wpa_s->num_ssids_from_scan_req == 0)
1036325151a3SRui Paulo return 0;
1037325151a3SRui Paulo
1038325151a3SRui Paulo if (wpa_s->num_ssids_from_scan_req > max_ssids) {
1039325151a3SRui Paulo wpa_s->num_ssids_from_scan_req = max_ssids;
1040325151a3SRui Paulo wpa_printf(MSG_DEBUG, "Over max scan SSIDs from scan req: %u",
1041325151a3SRui Paulo (unsigned int) max_ssids);
1042325151a3SRui Paulo }
1043325151a3SRui Paulo
1044325151a3SRui Paulo for (i = 0; i < wpa_s->num_ssids_from_scan_req; i++) {
1045325151a3SRui Paulo params->ssids[i].ssid = wpa_s->ssids_from_scan_req[i].ssid;
1046325151a3SRui Paulo params->ssids[i].ssid_len =
1047325151a3SRui Paulo wpa_s->ssids_from_scan_req[i].ssid_len;
1048325151a3SRui Paulo wpa_hexdump_ascii(MSG_DEBUG, "specific SSID",
1049325151a3SRui Paulo params->ssids[i].ssid,
1050325151a3SRui Paulo params->ssids[i].ssid_len);
1051325151a3SRui Paulo }
1052325151a3SRui Paulo
1053325151a3SRui Paulo params->num_ssids = wpa_s->num_ssids_from_scan_req;
1054325151a3SRui Paulo wpa_s->num_ssids_from_scan_req = 0;
1055325151a3SRui Paulo return 1;
1056325151a3SRui Paulo }
1057325151a3SRui Paulo
1058325151a3SRui Paulo
wpa_supplicant_scan(void * eloop_ctx,void * timeout_ctx)1059f05cddf9SRui Paulo static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx)
1060f05cddf9SRui Paulo {
1061f05cddf9SRui Paulo struct wpa_supplicant *wpa_s = eloop_ctx;
1062f05cddf9SRui Paulo struct wpa_ssid *ssid;
10635b9c547cSRui Paulo int ret, p2p_in_prog;
1064f05cddf9SRui Paulo struct wpabuf *extra_ie = NULL;
1065e28a4053SRui Paulo struct wpa_driver_scan_params params;
1066f05cddf9SRui Paulo struct wpa_driver_scan_params *scan_params;
1067e28a4053SRui Paulo size_t max_ssids;
10685b9c547cSRui Paulo int connect_without_scan = 0;
10695b9c547cSRui Paulo
107085732ac8SCy Schubert wpa_s->ignore_post_flush_scan_res = 0;
107139beb93cSSam Leffler
1072f05cddf9SRui Paulo if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) {
1073f05cddf9SRui Paulo wpa_dbg(wpa_s, MSG_DEBUG, "Skip scan - interface disabled");
1074f05cddf9SRui Paulo return;
1075f05cddf9SRui Paulo }
1076f05cddf9SRui Paulo
1077f05cddf9SRui Paulo if (wpa_s->disconnected && wpa_s->scan_req == NORMAL_SCAN_REQ) {
1078f05cddf9SRui Paulo wpa_dbg(wpa_s, MSG_DEBUG, "Disconnected - do not scan");
10793157ba21SRui Paulo wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
108039beb93cSSam Leffler return;
10813157ba21SRui Paulo }
108239beb93cSSam Leffler
10835b9c547cSRui Paulo if (wpa_s->scanning) {
10845b9c547cSRui Paulo /*
10855b9c547cSRui Paulo * If we are already in scanning state, we shall reschedule the
10865b9c547cSRui Paulo * the incoming scan request.
10875b9c547cSRui Paulo */
10885b9c547cSRui Paulo wpa_dbg(wpa_s, MSG_DEBUG, "Already scanning - Reschedule the incoming scan req");
10895b9c547cSRui Paulo wpa_supplicant_req_scan(wpa_s, 1, 0);
10905b9c547cSRui Paulo return;
10915b9c547cSRui Paulo }
10925b9c547cSRui Paulo
1093f05cddf9SRui Paulo if (!wpa_supplicant_enabled_networks(wpa_s) &&
1094f05cddf9SRui Paulo wpa_s->scan_req == NORMAL_SCAN_REQ) {
1095f05cddf9SRui Paulo wpa_dbg(wpa_s, MSG_DEBUG, "No enabled networks - do not scan");
109639beb93cSSam Leffler wpa_supplicant_set_state(wpa_s, WPA_INACTIVE);
109739beb93cSSam Leffler return;
109839beb93cSSam Leffler }
109939beb93cSSam Leffler
110039beb93cSSam Leffler if (wpa_s->conf->ap_scan != 0 &&
1101e28a4053SRui Paulo (wpa_s->drv_flags & WPA_DRIVER_FLAGS_WIRED)) {
1102f05cddf9SRui Paulo wpa_dbg(wpa_s, MSG_DEBUG, "Using wired authentication - "
110339beb93cSSam Leffler "overriding ap_scan configuration");
110439beb93cSSam Leffler wpa_s->conf->ap_scan = 0;
1105e28a4053SRui Paulo wpas_notify_ap_scan_changed(wpa_s);
110639beb93cSSam Leffler }
110739beb93cSSam Leffler
110839beb93cSSam Leffler if (wpa_s->conf->ap_scan == 0) {
110939beb93cSSam Leffler wpa_supplicant_gen_assoc_event(wpa_s);
111039beb93cSSam Leffler return;
111139beb93cSSam Leffler }
111239beb93cSSam Leffler
11135b9c547cSRui Paulo ssid = NULL;
11145b9c547cSRui Paulo if (wpa_s->scan_req != MANUAL_SCAN_REQ &&
11155b9c547cSRui Paulo wpa_s->connect_without_scan) {
11165b9c547cSRui Paulo connect_without_scan = 1;
11175b9c547cSRui Paulo for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
11185b9c547cSRui Paulo if (ssid == wpa_s->connect_without_scan)
11195b9c547cSRui Paulo break;
11205b9c547cSRui Paulo }
11215b9c547cSRui Paulo }
11225b9c547cSRui Paulo
11235b9c547cSRui Paulo p2p_in_prog = wpas_p2p_in_progress(wpa_s);
11245b9c547cSRui Paulo if (p2p_in_prog && p2p_in_prog != 2 &&
11255b9c547cSRui Paulo (!ssid ||
11265b9c547cSRui Paulo (ssid->mode != WPAS_MODE_AP && ssid->mode != WPAS_MODE_P2P_GO))) {
11275b9c547cSRui Paulo wpa_dbg(wpa_s, MSG_DEBUG, "Delay station mode scan while P2P operation is in progress");
1128f05cddf9SRui Paulo wpa_supplicant_req_scan(wpa_s, 5, 0);
1129f05cddf9SRui Paulo return;
1130f05cddf9SRui Paulo }
1131f05cddf9SRui Paulo
113285732ac8SCy Schubert /*
113385732ac8SCy Schubert * Don't cancel the scan based on ongoing PNO; defer it. Some scans are
113485732ac8SCy Schubert * used for changing modes inside wpa_supplicant (roaming,
113585732ac8SCy Schubert * auto-reconnect, etc). Discarding the scan might hurt these processes.
113685732ac8SCy Schubert * The normal use case for PNO is to suspend the host immediately after
113785732ac8SCy Schubert * starting PNO, so the periodic 100 ms attempts to run the scan do not
113885732ac8SCy Schubert * normally happen in practice multiple times, i.e., this is simply
113985732ac8SCy Schubert * restarting scanning once the host is woken up and PNO stopped.
114085732ac8SCy Schubert */
114185732ac8SCy Schubert if (wpa_s->pno || wpa_s->pno_sched_pending) {
114285732ac8SCy Schubert wpa_dbg(wpa_s, MSG_DEBUG, "Defer scan - PNO is in progress");
114385732ac8SCy Schubert wpa_supplicant_req_scan(wpa_s, 0, 100000);
114485732ac8SCy Schubert return;
114585732ac8SCy Schubert }
114685732ac8SCy Schubert
1147f05cddf9SRui Paulo if (wpa_s->conf->ap_scan == 2)
1148e28a4053SRui Paulo max_ssids = 1;
1149e28a4053SRui Paulo else {
1150e28a4053SRui Paulo max_ssids = wpa_s->max_scan_ssids;
1151e28a4053SRui Paulo if (max_ssids > WPAS_MAX_SCAN_SSIDS)
1152e28a4053SRui Paulo max_ssids = WPAS_MAX_SCAN_SSIDS;
1153e28a4053SRui Paulo }
1154e28a4053SRui Paulo
11555b9c547cSRui Paulo wpa_s->last_scan_req = wpa_s->scan_req;
1156f05cddf9SRui Paulo wpa_s->scan_req = NORMAL_SCAN_REQ;
1157e28a4053SRui Paulo
11585b9c547cSRui Paulo if (connect_without_scan) {
11595b9c547cSRui Paulo wpa_s->connect_without_scan = NULL;
11605b9c547cSRui Paulo if (ssid) {
11615b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "Start a pre-selected network "
11625b9c547cSRui Paulo "without scan step");
11635b9c547cSRui Paulo wpa_supplicant_associate(wpa_s, NULL, ssid);
11645b9c547cSRui Paulo return;
11655b9c547cSRui Paulo }
11665b9c547cSRui Paulo }
11675b9c547cSRui Paulo
1168e28a4053SRui Paulo os_memset(¶ms, 0, sizeof(params));
1169e28a4053SRui Paulo
11705b9c547cSRui Paulo wpa_s->scan_prev_wpa_state = wpa_s->wpa_state;
117139beb93cSSam Leffler if (wpa_s->wpa_state == WPA_DISCONNECTED ||
117239beb93cSSam Leffler wpa_s->wpa_state == WPA_INACTIVE)
117339beb93cSSam Leffler wpa_supplicant_set_state(wpa_s, WPA_SCANNING);
117439beb93cSSam Leffler
1175f05cddf9SRui Paulo /*
1176f05cddf9SRui Paulo * If autoscan has set its own scanning parameters
1177f05cddf9SRui Paulo */
1178f05cddf9SRui Paulo if (wpa_s->autoscan_params != NULL) {
1179f05cddf9SRui Paulo scan_params = wpa_s->autoscan_params;
1180f05cddf9SRui Paulo goto scan;
1181f05cddf9SRui Paulo }
1182f05cddf9SRui Paulo
1183325151a3SRui Paulo if (wpa_s->last_scan_req == MANUAL_SCAN_REQ &&
1184325151a3SRui Paulo wpa_set_ssids_from_scan_req(wpa_s, ¶ms, max_ssids)) {
1185325151a3SRui Paulo wpa_printf(MSG_DEBUG, "Use specific SSIDs from SCAN command");
1186325151a3SRui Paulo goto ssid_list_set;
1187325151a3SRui Paulo }
1188325151a3SRui Paulo
1189f05cddf9SRui Paulo #ifdef CONFIG_P2P
1190f05cddf9SRui Paulo if ((wpa_s->p2p_in_provisioning || wpa_s->show_group_started) &&
11915b9c547cSRui Paulo wpa_s->go_params && !wpa_s->conf->passive_scan) {
11925b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "P2P: Use specific SSID for scan during P2P group formation (p2p_in_provisioning=%d show_group_started=%d)",
11935b9c547cSRui Paulo wpa_s->p2p_in_provisioning,
11945b9c547cSRui Paulo wpa_s->show_group_started);
1195f05cddf9SRui Paulo params.ssids[0].ssid = wpa_s->go_params->ssid;
1196f05cddf9SRui Paulo params.ssids[0].ssid_len = wpa_s->go_params->ssid_len;
1197f05cddf9SRui Paulo params.num_ssids = 1;
1198*a90b9d01SCy Schubert params.bssid = wpa_s->go_params->peer_interface_addr;
1199*a90b9d01SCy Schubert wpa_printf(MSG_DEBUG, "P2P: Use specific BSSID " MACSTR
1200*a90b9d01SCy Schubert " (peer interface address) for scan",
1201*a90b9d01SCy Schubert MAC2STR(params.bssid));
1202f05cddf9SRui Paulo goto ssid_list_set;
1203f05cddf9SRui Paulo }
12045b9c547cSRui Paulo
12055b9c547cSRui Paulo if (wpa_s->p2p_in_invitation) {
12065b9c547cSRui Paulo if (wpa_s->current_ssid) {
12075b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "P2P: Use specific SSID for scan during invitation");
12085b9c547cSRui Paulo params.ssids[0].ssid = wpa_s->current_ssid->ssid;
12095b9c547cSRui Paulo params.ssids[0].ssid_len =
12105b9c547cSRui Paulo wpa_s->current_ssid->ssid_len;
12115b9c547cSRui Paulo params.num_ssids = 1;
1212*a90b9d01SCy Schubert if (wpa_s->current_ssid->bssid_set) {
1213*a90b9d01SCy Schubert params.bssid = wpa_s->current_ssid->bssid;
1214*a90b9d01SCy Schubert wpa_printf(MSG_DEBUG, "P2P: Use specific BSSID "
1215*a90b9d01SCy Schubert MACSTR " for scan",
1216*a90b9d01SCy Schubert MAC2STR(params.bssid));
1217*a90b9d01SCy Schubert }
12185b9c547cSRui Paulo } else {
12195b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "P2P: No specific SSID known for scan during invitation");
12205b9c547cSRui Paulo }
12215b9c547cSRui Paulo goto ssid_list_set;
12225b9c547cSRui Paulo }
1223f05cddf9SRui Paulo #endif /* CONFIG_P2P */
1224f05cddf9SRui Paulo
1225e28a4053SRui Paulo /* Find the starting point from which to continue scanning */
122639beb93cSSam Leffler ssid = wpa_s->conf->ssid;
1227e28a4053SRui Paulo if (wpa_s->prev_scan_ssid != WILDCARD_SSID_SCAN) {
122839beb93cSSam Leffler while (ssid) {
122939beb93cSSam Leffler if (ssid == wpa_s->prev_scan_ssid) {
123039beb93cSSam Leffler ssid = ssid->next;
123139beb93cSSam Leffler break;
123239beb93cSSam Leffler }
123339beb93cSSam Leffler ssid = ssid->next;
123439beb93cSSam Leffler }
123539beb93cSSam Leffler }
123639beb93cSSam Leffler
12375b9c547cSRui Paulo if (wpa_s->last_scan_req != MANUAL_SCAN_REQ &&
1238325151a3SRui Paulo #ifdef CONFIG_AP
1239325151a3SRui Paulo !wpa_s->ap_iface &&
1240325151a3SRui Paulo #endif /* CONFIG_AP */
12415b9c547cSRui Paulo wpa_s->conf->ap_scan == 2) {
1242f05cddf9SRui Paulo wpa_s->connect_without_scan = NULL;
1243f05cddf9SRui Paulo wpa_s->prev_scan_wildcard = 0;
1244e28a4053SRui Paulo wpa_supplicant_assoc_try(wpa_s, ssid);
1245e28a4053SRui Paulo return;
1246e28a4053SRui Paulo } else if (wpa_s->conf->ap_scan == 2) {
124739beb93cSSam Leffler /*
1248e28a4053SRui Paulo * User-initiated scan request in ap_scan == 2; scan with
1249e28a4053SRui Paulo * wildcard SSID.
125039beb93cSSam Leffler */
1251e28a4053SRui Paulo ssid = NULL;
12525b9c547cSRui Paulo } else if (wpa_s->reattach && wpa_s->current_ssid != NULL) {
12535b9c547cSRui Paulo /*
12545b9c547cSRui Paulo * Perform single-channel single-SSID scan for
12555b9c547cSRui Paulo * reassociate-to-same-BSS operation.
12565b9c547cSRui Paulo */
12575b9c547cSRui Paulo /* Setup SSID */
12585b9c547cSRui Paulo ssid = wpa_s->current_ssid;
12595b9c547cSRui Paulo wpa_hexdump_ascii(MSG_DEBUG, "Scan SSID",
12605b9c547cSRui Paulo ssid->ssid, ssid->ssid_len);
12615b9c547cSRui Paulo params.ssids[0].ssid = ssid->ssid;
12625b9c547cSRui Paulo params.ssids[0].ssid_len = ssid->ssid_len;
12635b9c547cSRui Paulo params.num_ssids = 1;
12645b9c547cSRui Paulo
12655b9c547cSRui Paulo /*
12665b9c547cSRui Paulo * Allocate memory for frequency array, allocate one extra
12675b9c547cSRui Paulo * slot for the zero-terminator.
12685b9c547cSRui Paulo */
12695b9c547cSRui Paulo params.freqs = os_malloc(sizeof(int) * 2);
1270780fb4a2SCy Schubert if (params.freqs) {
12715b9c547cSRui Paulo params.freqs[0] = wpa_s->assoc_freq;
12725b9c547cSRui Paulo params.freqs[1] = 0;
1273780fb4a2SCy Schubert }
12745b9c547cSRui Paulo
12755b9c547cSRui Paulo /*
12765b9c547cSRui Paulo * Reset the reattach flag so that we fall back to full scan if
12775b9c547cSRui Paulo * this scan fails.
12785b9c547cSRui Paulo */
12795b9c547cSRui Paulo wpa_s->reattach = 0;
128039beb93cSSam Leffler } else {
1281e28a4053SRui Paulo struct wpa_ssid *start = ssid, *tssid;
1282e28a4053SRui Paulo int freqs_set = 0;
1283e28a4053SRui Paulo if (ssid == NULL && max_ssids > 1)
1284e28a4053SRui Paulo ssid = wpa_s->conf->ssid;
1285e28a4053SRui Paulo while (ssid) {
1286f05cddf9SRui Paulo if (!wpas_network_disabled(wpa_s, ssid) &&
1287f05cddf9SRui Paulo ssid->scan_ssid) {
128839beb93cSSam Leffler wpa_hexdump_ascii(MSG_DEBUG, "Scan SSID",
128939beb93cSSam Leffler ssid->ssid, ssid->ssid_len);
1290e28a4053SRui Paulo params.ssids[params.num_ssids].ssid =
1291e28a4053SRui Paulo ssid->ssid;
1292e28a4053SRui Paulo params.ssids[params.num_ssids].ssid_len =
1293e28a4053SRui Paulo ssid->ssid_len;
1294e28a4053SRui Paulo params.num_ssids++;
1295e28a4053SRui Paulo if (params.num_ssids + 1 >= max_ssids)
1296e28a4053SRui Paulo break;
1297e28a4053SRui Paulo }
129885732ac8SCy Schubert
129985732ac8SCy Schubert if (!wpas_network_disabled(wpa_s, ssid)) {
130085732ac8SCy Schubert /*
130185732ac8SCy Schubert * Also add the SSID of the OWE BSS, to allow
130285732ac8SCy Schubert * discovery of transition mode APs more
130385732ac8SCy Schubert * quickly.
130485732ac8SCy Schubert */
130585732ac8SCy Schubert wpa_add_owe_scan_ssid(wpa_s, ¶ms, ssid,
130685732ac8SCy Schubert max_ssids);
130785732ac8SCy Schubert }
130885732ac8SCy Schubert
1309e28a4053SRui Paulo ssid = ssid->next;
1310e28a4053SRui Paulo if (ssid == start)
1311e28a4053SRui Paulo break;
1312e28a4053SRui Paulo if (ssid == NULL && max_ssids > 1 &&
1313e28a4053SRui Paulo start != wpa_s->conf->ssid)
1314e28a4053SRui Paulo ssid = wpa_s->conf->ssid;
1315e28a4053SRui Paulo }
1316e28a4053SRui Paulo
13175b9c547cSRui Paulo if (wpa_s->scan_id_count &&
13185b9c547cSRui Paulo wpa_s->last_scan_req == MANUAL_SCAN_REQ)
13195b9c547cSRui Paulo wpa_set_scan_ssids(wpa_s, ¶ms, max_ssids);
13205b9c547cSRui Paulo
13215b9c547cSRui Paulo for (tssid = wpa_s->conf->ssid;
13225b9c547cSRui Paulo wpa_s->last_scan_req != MANUAL_SCAN_REQ && tssid;
13235b9c547cSRui Paulo tssid = tssid->next) {
1324f05cddf9SRui Paulo if (wpas_network_disabled(wpa_s, tssid))
1325e28a4053SRui Paulo continue;
1326c1d255d3SCy Schubert if (((params.freqs || !freqs_set) &&
1327c1d255d3SCy Schubert tssid->scan_freq) &&
1328c1d255d3SCy Schubert int_array_len(params.freqs) < 100) {
1329e28a4053SRui Paulo int_array_concat(¶ms.freqs,
1330e28a4053SRui Paulo tssid->scan_freq);
1331e28a4053SRui Paulo } else {
1332e28a4053SRui Paulo os_free(params.freqs);
1333e28a4053SRui Paulo params.freqs = NULL;
1334e28a4053SRui Paulo }
1335e28a4053SRui Paulo freqs_set = 1;
1336e28a4053SRui Paulo }
1337e28a4053SRui Paulo int_array_sort_unique(params.freqs);
1338e28a4053SRui Paulo }
1339e28a4053SRui Paulo
1340f05cddf9SRui Paulo if (ssid && max_ssids == 1) {
1341f05cddf9SRui Paulo /*
1342f05cddf9SRui Paulo * If the driver is limited to 1 SSID at a time interleave
1343f05cddf9SRui Paulo * wildcard SSID scans with specific SSID scans to avoid
1344f05cddf9SRui Paulo * waiting a long time for a wildcard scan.
1345f05cddf9SRui Paulo */
1346f05cddf9SRui Paulo if (!wpa_s->prev_scan_wildcard) {
1347f05cddf9SRui Paulo params.ssids[0].ssid = NULL;
1348f05cddf9SRui Paulo params.ssids[0].ssid_len = 0;
1349f05cddf9SRui Paulo wpa_s->prev_scan_wildcard = 1;
1350f05cddf9SRui Paulo wpa_dbg(wpa_s, MSG_DEBUG, "Starting AP scan for "
1351f05cddf9SRui Paulo "wildcard SSID (Interleave with specific)");
1352f05cddf9SRui Paulo } else {
135339beb93cSSam Leffler wpa_s->prev_scan_ssid = ssid;
1354f05cddf9SRui Paulo wpa_s->prev_scan_wildcard = 0;
1355f05cddf9SRui Paulo wpa_dbg(wpa_s, MSG_DEBUG,
1356f05cddf9SRui Paulo "Starting AP scan for specific SSID: %s",
1357f05cddf9SRui Paulo wpa_ssid_txt(ssid->ssid, ssid->ssid_len));
1358e28a4053SRui Paulo }
1359f05cddf9SRui Paulo } else if (ssid) {
1360f05cddf9SRui Paulo /* max_ssids > 1 */
1361f05cddf9SRui Paulo
1362f05cddf9SRui Paulo wpa_s->prev_scan_ssid = ssid;
1363f05cddf9SRui Paulo wpa_dbg(wpa_s, MSG_DEBUG, "Include wildcard SSID in "
1364f05cddf9SRui Paulo "the scan request");
1365f05cddf9SRui Paulo params.num_ssids++;
13665b9c547cSRui Paulo } else if (wpa_s->last_scan_req == MANUAL_SCAN_REQ &&
13675b9c547cSRui Paulo wpa_s->manual_scan_passive && params.num_ssids == 0) {
13685b9c547cSRui Paulo wpa_dbg(wpa_s, MSG_DEBUG, "Use passive scan based on manual request");
13695b9c547cSRui Paulo } else if (wpa_s->conf->passive_scan) {
13705b9c547cSRui Paulo wpa_dbg(wpa_s, MSG_DEBUG,
13715b9c547cSRui Paulo "Use passive scan based on configuration");
1372e28a4053SRui Paulo } else {
1373e28a4053SRui Paulo wpa_s->prev_scan_ssid = WILDCARD_SSID_SCAN;
1374e28a4053SRui Paulo params.num_ssids++;
1375f05cddf9SRui Paulo wpa_dbg(wpa_s, MSG_DEBUG, "Starting AP scan for wildcard "
1376f05cddf9SRui Paulo "SSID");
137739beb93cSSam Leffler }
137839beb93cSSam Leffler
1379325151a3SRui Paulo ssid_list_set:
1380f05cddf9SRui Paulo wpa_supplicant_optimize_freqs(wpa_s, ¶ms);
1381f05cddf9SRui Paulo extra_ie = wpa_supplicant_extra_ies(wpa_s);
1382e28a4053SRui Paulo
13835b9c547cSRui Paulo if (wpa_s->last_scan_req == MANUAL_SCAN_REQ &&
13845b9c547cSRui Paulo wpa_s->manual_scan_only_new) {
13855b9c547cSRui Paulo wpa_printf(MSG_DEBUG,
13865b9c547cSRui Paulo "Request driver to clear scan cache due to manual only_new=1 scan");
13875b9c547cSRui Paulo params.only_new_results = 1;
13885b9c547cSRui Paulo }
13895b9c547cSRui Paulo
13905b9c547cSRui Paulo if (wpa_s->last_scan_req == MANUAL_SCAN_REQ && params.freqs == NULL &&
13915b9c547cSRui Paulo wpa_s->manual_scan_freqs) {
13925b9c547cSRui Paulo wpa_dbg(wpa_s, MSG_DEBUG, "Limit manual scan to specified channels");
13935b9c547cSRui Paulo params.freqs = wpa_s->manual_scan_freqs;
13945b9c547cSRui Paulo wpa_s->manual_scan_freqs = NULL;
13955b9c547cSRui Paulo }
1396f05cddf9SRui Paulo
139785732ac8SCy Schubert if (params.freqs == NULL && wpa_s->select_network_scan_freqs) {
139885732ac8SCy Schubert wpa_dbg(wpa_s, MSG_DEBUG,
139985732ac8SCy Schubert "Limit select_network scan to specified channels");
140085732ac8SCy Schubert params.freqs = wpa_s->select_network_scan_freqs;
140185732ac8SCy Schubert wpa_s->select_network_scan_freqs = NULL;
140285732ac8SCy Schubert }
140385732ac8SCy Schubert
1404f05cddf9SRui Paulo if (params.freqs == NULL && wpa_s->next_scan_freqs) {
1405f05cddf9SRui Paulo wpa_dbg(wpa_s, MSG_DEBUG, "Optimize scan based on previously "
1406f05cddf9SRui Paulo "generated frequency list");
1407f05cddf9SRui Paulo params.freqs = wpa_s->next_scan_freqs;
1408f05cddf9SRui Paulo } else
1409f05cddf9SRui Paulo os_free(wpa_s->next_scan_freqs);
1410f05cddf9SRui Paulo wpa_s->next_scan_freqs = NULL;
14115b9c547cSRui Paulo wpa_setband_scan_freqs(wpa_s, ¶ms);
14125b9c547cSRui Paulo
14135b9c547cSRui Paulo /* See if user specified frequencies. If so, scan only those. */
1414c1d255d3SCy Schubert if (wpa_s->last_scan_req == INITIAL_SCAN_REQ &&
1415c1d255d3SCy Schubert wpa_s->conf->initial_freq_list && !params.freqs) {
1416c1d255d3SCy Schubert wpa_dbg(wpa_s, MSG_DEBUG,
1417c1d255d3SCy Schubert "Optimize scan based on conf->initial_freq_list");
1418c1d255d3SCy Schubert int_array_concat(¶ms.freqs, wpa_s->conf->initial_freq_list);
1419c1d255d3SCy Schubert } else if (wpa_s->conf->freq_list && !params.freqs) {
14205b9c547cSRui Paulo wpa_dbg(wpa_s, MSG_DEBUG,
14215b9c547cSRui Paulo "Optimize scan based on conf->freq_list");
14225b9c547cSRui Paulo int_array_concat(¶ms.freqs, wpa_s->conf->freq_list);
14235b9c547cSRui Paulo }
14245b9c547cSRui Paulo
14255b9c547cSRui Paulo /* Use current associated channel? */
14265b9c547cSRui Paulo if (wpa_s->conf->scan_cur_freq && !params.freqs) {
14275b9c547cSRui Paulo unsigned int num = wpa_s->num_multichan_concurrent;
14285b9c547cSRui Paulo
14295b9c547cSRui Paulo params.freqs = os_calloc(num + 1, sizeof(int));
14305b9c547cSRui Paulo if (params.freqs) {
1431*a90b9d01SCy Schubert num = get_shared_radio_freqs(wpa_s, params.freqs, num,
1432*a90b9d01SCy Schubert false);
14335b9c547cSRui Paulo if (num > 0) {
14345b9c547cSRui Paulo wpa_dbg(wpa_s, MSG_DEBUG, "Scan only the "
14355b9c547cSRui Paulo "current operating channels since "
14365b9c547cSRui Paulo "scan_cur_freq is enabled");
14375b9c547cSRui Paulo } else {
14385b9c547cSRui Paulo os_free(params.freqs);
14395b9c547cSRui Paulo params.freqs = NULL;
14405b9c547cSRui Paulo }
14415b9c547cSRui Paulo }
14425b9c547cSRui Paulo }
144339beb93cSSam Leffler
144485732ac8SCy Schubert #ifdef CONFIG_MBO
144585732ac8SCy Schubert if (wpa_s->enable_oce & OCE_STA)
144685732ac8SCy Schubert params.oce_scan = 1;
144785732ac8SCy Schubert #endif /* CONFIG_MBO */
144885732ac8SCy Schubert
1449e28a4053SRui Paulo params.filter_ssids = wpa_supplicant_build_filter_ssids(
1450e28a4053SRui Paulo wpa_s->conf, ¶ms.num_filter_ssids);
1451f05cddf9SRui Paulo if (extra_ie) {
1452f05cddf9SRui Paulo params.extra_ies = wpabuf_head(extra_ie);
1453f05cddf9SRui Paulo params.extra_ies_len = wpabuf_len(extra_ie);
1454f05cddf9SRui Paulo }
14553157ba21SRui Paulo
1456f05cddf9SRui Paulo #ifdef CONFIG_P2P
14575b9c547cSRui Paulo if (wpa_s->p2p_in_provisioning || wpa_s->p2p_in_invitation ||
1458f05cddf9SRui Paulo (wpa_s->show_group_started && wpa_s->go_params)) {
1459f05cddf9SRui Paulo /*
1460f05cddf9SRui Paulo * The interface may not yet be in P2P mode, so we have to
1461f05cddf9SRui Paulo * explicitly request P2P probe to disable CCK rates.
1462f05cddf9SRui Paulo */
1463f05cddf9SRui Paulo params.p2p_probe = 1;
1464f05cddf9SRui Paulo }
1465f05cddf9SRui Paulo #endif /* CONFIG_P2P */
146639beb93cSSam Leffler
146785732ac8SCy Schubert if ((wpa_s->mac_addr_rand_enable & MAC_ADDR_RAND_SCAN) &&
1468c1d255d3SCy Schubert wpa_s->wpa_state <= WPA_SCANNING)
1469c1d255d3SCy Schubert wpa_setup_mac_addr_rand_params(¶ms, wpa_s->mac_addr_scan);
14705b9c547cSRui Paulo
1471780fb4a2SCy Schubert if (!is_zero_ether_addr(wpa_s->next_scan_bssid)) {
1472780fb4a2SCy Schubert struct wpa_bss *bss;
1473780fb4a2SCy Schubert
1474780fb4a2SCy Schubert params.bssid = wpa_s->next_scan_bssid;
1475780fb4a2SCy Schubert bss = wpa_bss_get_bssid_latest(wpa_s, params.bssid);
1476c1d255d3SCy Schubert if (!wpa_s->next_scan_bssid_wildcard_ssid &&
1477c1d255d3SCy Schubert bss && bss->ssid_len && params.num_ssids == 1 &&
1478780fb4a2SCy Schubert params.ssids[0].ssid_len == 0) {
1479780fb4a2SCy Schubert params.ssids[0].ssid = bss->ssid;
1480780fb4a2SCy Schubert params.ssids[0].ssid_len = bss->ssid_len;
1481780fb4a2SCy Schubert wpa_dbg(wpa_s, MSG_DEBUG,
1482780fb4a2SCy Schubert "Scan a previously specified BSSID " MACSTR
1483780fb4a2SCy Schubert " and SSID %s",
1484780fb4a2SCy Schubert MAC2STR(params.bssid),
1485780fb4a2SCy Schubert wpa_ssid_txt(bss->ssid, bss->ssid_len));
1486780fb4a2SCy Schubert } else {
1487780fb4a2SCy Schubert wpa_dbg(wpa_s, MSG_DEBUG,
1488780fb4a2SCy Schubert "Scan a previously specified BSSID " MACSTR,
1489780fb4a2SCy Schubert MAC2STR(params.bssid));
1490780fb4a2SCy Schubert }
1491*a90b9d01SCy Schubert } else if (!is_zero_ether_addr(wpa_s->ml_probe_bssid)) {
1492*a90b9d01SCy Schubert wpa_printf(MSG_DEBUG, "Scanning for ML probe request");
1493*a90b9d01SCy Schubert params.bssid = wpa_s->ml_probe_bssid;
1494*a90b9d01SCy Schubert params.min_probe_req_content = true;
1495*a90b9d01SCy Schubert }
1496*a90b9d01SCy Schubert
1497*a90b9d01SCy Schubert
1498*a90b9d01SCy Schubert if (wpa_s->last_scan_req == MANUAL_SCAN_REQ &&
1499*a90b9d01SCy Schubert wpa_s->manual_non_coloc_6ghz) {
1500*a90b9d01SCy Schubert wpa_dbg(wpa_s, MSG_DEBUG, "Collocated 6 GHz logic is disabled");
1501*a90b9d01SCy Schubert params.non_coloc_6ghz = 1;
1502780fb4a2SCy Schubert }
1503780fb4a2SCy Schubert
1504f05cddf9SRui Paulo scan_params = ¶ms;
1505f05cddf9SRui Paulo
1506f05cddf9SRui Paulo scan:
1507f05cddf9SRui Paulo #ifdef CONFIG_P2P
1508f05cddf9SRui Paulo /*
1509f05cddf9SRui Paulo * If the driver does not support multi-channel concurrency and a
1510f05cddf9SRui Paulo * virtual interface that shares the same radio with the wpa_s interface
1511f05cddf9SRui Paulo * is operating there may not be need to scan other channels apart from
1512f05cddf9SRui Paulo * the current operating channel on the other virtual interface. Filter
1513f05cddf9SRui Paulo * out other channels in case we are trying to find a connection for a
1514f05cddf9SRui Paulo * station interface when we are not configured to prefer station
1515f05cddf9SRui Paulo * connection and a concurrent operation is already in process.
1516f05cddf9SRui Paulo */
15175b9c547cSRui Paulo if (wpa_s->scan_for_connection &&
15185b9c547cSRui Paulo wpa_s->last_scan_req == NORMAL_SCAN_REQ &&
1519f05cddf9SRui Paulo !scan_params->freqs && !params.freqs &&
1520f05cddf9SRui Paulo wpas_is_p2p_prioritized(wpa_s) &&
1521f05cddf9SRui Paulo wpa_s->p2p_group_interface == NOT_P2P_GROUP_INTERFACE &&
1522f05cddf9SRui Paulo non_p2p_network_enabled(wpa_s)) {
15235b9c547cSRui Paulo unsigned int num = wpa_s->num_multichan_concurrent;
15245b9c547cSRui Paulo
15255b9c547cSRui Paulo params.freqs = os_calloc(num + 1, sizeof(int));
15265b9c547cSRui Paulo if (params.freqs) {
1527*a90b9d01SCy Schubert /*
1528*a90b9d01SCy Schubert * Exclude the operating frequency of the current
1529*a90b9d01SCy Schubert * interface since we're looking to transition off of
1530*a90b9d01SCy Schubert * it.
1531*a90b9d01SCy Schubert */
1532*a90b9d01SCy Schubert num = get_shared_radio_freqs(wpa_s, params.freqs, num,
1533*a90b9d01SCy Schubert true);
15345b9c547cSRui Paulo if (num > 0 && num == wpa_s->num_multichan_concurrent) {
15355b9c547cSRui Paulo wpa_dbg(wpa_s, MSG_DEBUG, "Scan only the current operating channels since all channels are already used");
15365b9c547cSRui Paulo } else {
15375b9c547cSRui Paulo os_free(params.freqs);
15385b9c547cSRui Paulo params.freqs = NULL;
15395b9c547cSRui Paulo }
1540f05cddf9SRui Paulo }
1541f05cddf9SRui Paulo }
15424b72b91aSCy Schubert
1543*a90b9d01SCy Schubert if (!params.freqs && wpas_is_6ghz_supported(wpa_s, true) &&
1544*a90b9d01SCy Schubert (wpa_s->p2p_in_invitation || wpa_s->p2p_in_provisioning))
1545*a90b9d01SCy Schubert wpas_p2p_scan_freqs(wpa_s, ¶ms, true);
1546f05cddf9SRui Paulo #endif /* CONFIG_P2P */
1547f05cddf9SRui Paulo
1548*a90b9d01SCy Schubert ret = wpa_supplicant_trigger_scan(wpa_s, scan_params, false, false);
1549f05cddf9SRui Paulo
15505b9c547cSRui Paulo if (ret && wpa_s->last_scan_req == MANUAL_SCAN_REQ && params.freqs &&
15515b9c547cSRui Paulo !wpa_s->manual_scan_freqs) {
15525b9c547cSRui Paulo /* Restore manual_scan_freqs for the next attempt */
15535b9c547cSRui Paulo wpa_s->manual_scan_freqs = params.freqs;
15545b9c547cSRui Paulo params.freqs = NULL;
15555b9c547cSRui Paulo }
15565b9c547cSRui Paulo
1557f05cddf9SRui Paulo wpabuf_free(extra_ie);
1558e28a4053SRui Paulo os_free(params.freqs);
1559e28a4053SRui Paulo os_free(params.filter_ssids);
1560c1d255d3SCy Schubert os_free(params.mac_addr);
156139beb93cSSam Leffler
156239beb93cSSam Leffler if (ret) {
1563f05cddf9SRui Paulo wpa_msg(wpa_s, MSG_WARNING, "Failed to initiate AP scan");
15645b9c547cSRui Paulo if (wpa_s->scan_prev_wpa_state != wpa_s->wpa_state)
15655b9c547cSRui Paulo wpa_supplicant_set_state(wpa_s,
15665b9c547cSRui Paulo wpa_s->scan_prev_wpa_state);
1567f05cddf9SRui Paulo /* Restore scan_req since we will try to scan again */
15685b9c547cSRui Paulo wpa_s->scan_req = wpa_s->last_scan_req;
1569e28a4053SRui Paulo wpa_supplicant_req_scan(wpa_s, 1, 0);
1570f05cddf9SRui Paulo } else {
1571f05cddf9SRui Paulo wpa_s->scan_for_connection = 0;
15725b9c547cSRui Paulo #ifdef CONFIG_INTERWORKING
15735b9c547cSRui Paulo wpa_s->interworking_fast_assoc_tried = 0;
15745b9c547cSRui Paulo #endif /* CONFIG_INTERWORKING */
1575c1d255d3SCy Schubert wpa_s->next_scan_bssid_wildcard_ssid = 0;
1576780fb4a2SCy Schubert if (params.bssid)
1577780fb4a2SCy Schubert os_memset(wpa_s->next_scan_bssid, 0, ETH_ALEN);
1578e28a4053SRui Paulo }
1579*a90b9d01SCy Schubert
1580*a90b9d01SCy Schubert wpa_s->ml_probe_mld_id = -1;
1581*a90b9d01SCy Schubert wpa_s->ml_probe_links = 0;
1582*a90b9d01SCy Schubert os_memset(wpa_s->ml_probe_bssid, 0, sizeof(wpa_s->ml_probe_bssid));
158339beb93cSSam Leffler }
158439beb93cSSam Leffler
158539beb93cSSam Leffler
wpa_supplicant_update_scan_int(struct wpa_supplicant * wpa_s,int sec)15865b9c547cSRui Paulo void wpa_supplicant_update_scan_int(struct wpa_supplicant *wpa_s, int sec)
15875b9c547cSRui Paulo {
15885b9c547cSRui Paulo struct os_reltime remaining, new_int;
15895b9c547cSRui Paulo int cancelled;
15905b9c547cSRui Paulo
15915b9c547cSRui Paulo cancelled = eloop_cancel_timeout_one(wpa_supplicant_scan, wpa_s, NULL,
15925b9c547cSRui Paulo &remaining);
15935b9c547cSRui Paulo
15945b9c547cSRui Paulo new_int.sec = sec;
15955b9c547cSRui Paulo new_int.usec = 0;
15965b9c547cSRui Paulo if (cancelled && os_reltime_before(&remaining, &new_int)) {
15975b9c547cSRui Paulo new_int.sec = remaining.sec;
15985b9c547cSRui Paulo new_int.usec = remaining.usec;
15995b9c547cSRui Paulo }
16005b9c547cSRui Paulo
16015b9c547cSRui Paulo if (cancelled) {
16025b9c547cSRui Paulo eloop_register_timeout(new_int.sec, new_int.usec,
16035b9c547cSRui Paulo wpa_supplicant_scan, wpa_s, NULL);
16045b9c547cSRui Paulo }
16055b9c547cSRui Paulo wpa_s->scan_interval = sec;
16065b9c547cSRui Paulo }
16075b9c547cSRui Paulo
16085b9c547cSRui Paulo
160939beb93cSSam Leffler /**
161039beb93cSSam Leffler * wpa_supplicant_req_scan - Schedule a scan for neighboring access points
161139beb93cSSam Leffler * @wpa_s: Pointer to wpa_supplicant data
161239beb93cSSam Leffler * @sec: Number of seconds after which to scan
161339beb93cSSam Leffler * @usec: Number of microseconds after which to scan
161439beb93cSSam Leffler *
161539beb93cSSam Leffler * This function is used to schedule a scan for neighboring access points after
161639beb93cSSam Leffler * the specified time.
161739beb93cSSam Leffler */
wpa_supplicant_req_scan(struct wpa_supplicant * wpa_s,int sec,int usec)161839beb93cSSam Leffler void wpa_supplicant_req_scan(struct wpa_supplicant *wpa_s, int sec, int usec)
161939beb93cSSam Leffler {
16205b9c547cSRui Paulo int res;
162139beb93cSSam Leffler
16225b9c547cSRui Paulo if (wpa_s->p2p_mgmt) {
16235b9c547cSRui Paulo wpa_dbg(wpa_s, MSG_DEBUG,
16245b9c547cSRui Paulo "Ignore scan request (%d.%06d sec) on p2p_mgmt interface",
16255b9c547cSRui Paulo sec, usec);
162639beb93cSSam Leffler return;
162739beb93cSSam Leffler }
162839beb93cSSam Leffler
16295b9c547cSRui Paulo res = eloop_deplete_timeout(sec, usec, wpa_supplicant_scan, wpa_s,
16305b9c547cSRui Paulo NULL);
16315b9c547cSRui Paulo if (res == 1) {
16325b9c547cSRui Paulo wpa_dbg(wpa_s, MSG_DEBUG, "Rescheduling scan request: %d.%06d sec",
163339beb93cSSam Leffler sec, usec);
16345b9c547cSRui Paulo } else if (res == 0) {
16355b9c547cSRui Paulo wpa_dbg(wpa_s, MSG_DEBUG, "Ignore new scan request for %d.%06d sec since an earlier request is scheduled to trigger sooner",
16365b9c547cSRui Paulo sec, usec);
16375b9c547cSRui Paulo } else {
16385b9c547cSRui Paulo wpa_dbg(wpa_s, MSG_DEBUG, "Setting scan request: %d.%06d sec",
16395b9c547cSRui Paulo sec, usec);
164039beb93cSSam Leffler eloop_register_timeout(sec, usec, wpa_supplicant_scan, wpa_s, NULL);
164139beb93cSSam Leffler }
16425b9c547cSRui Paulo }
164339beb93cSSam Leffler
164439beb93cSSam Leffler
164539beb93cSSam Leffler /**
1646f05cddf9SRui Paulo * wpa_supplicant_delayed_sched_scan - Request a delayed scheduled scan
1647f05cddf9SRui Paulo * @wpa_s: Pointer to wpa_supplicant data
1648f05cddf9SRui Paulo * @sec: Number of seconds after which to scan
1649f05cddf9SRui Paulo * @usec: Number of microseconds after which to scan
1650f05cddf9SRui Paulo * Returns: 0 on success or -1 otherwise
1651f05cddf9SRui Paulo *
1652f05cddf9SRui Paulo * This function is used to schedule periodic scans for neighboring
1653f05cddf9SRui Paulo * access points after the specified time.
1654f05cddf9SRui Paulo */
wpa_supplicant_delayed_sched_scan(struct wpa_supplicant * wpa_s,int sec,int usec)1655f05cddf9SRui Paulo int wpa_supplicant_delayed_sched_scan(struct wpa_supplicant *wpa_s,
1656f05cddf9SRui Paulo int sec, int usec)
1657f05cddf9SRui Paulo {
1658f05cddf9SRui Paulo if (!wpa_s->sched_scan_supported)
1659f05cddf9SRui Paulo return -1;
1660f05cddf9SRui Paulo
1661f05cddf9SRui Paulo eloop_register_timeout(sec, usec,
1662f05cddf9SRui Paulo wpa_supplicant_delayed_sched_scan_timeout,
1663f05cddf9SRui Paulo wpa_s, NULL);
1664f05cddf9SRui Paulo
1665f05cddf9SRui Paulo return 0;
1666f05cddf9SRui Paulo }
1667f05cddf9SRui Paulo
1668f05cddf9SRui Paulo
166985732ac8SCy Schubert static void
wpa_scan_set_relative_rssi_params(struct wpa_supplicant * wpa_s,struct wpa_driver_scan_params * params)167085732ac8SCy Schubert wpa_scan_set_relative_rssi_params(struct wpa_supplicant *wpa_s,
167185732ac8SCy Schubert struct wpa_driver_scan_params *params)
167285732ac8SCy Schubert {
167385732ac8SCy Schubert if (wpa_s->wpa_state != WPA_COMPLETED ||
167485732ac8SCy Schubert !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SCHED_SCAN_RELATIVE_RSSI) ||
167585732ac8SCy Schubert wpa_s->srp.relative_rssi_set == 0)
167685732ac8SCy Schubert return;
167785732ac8SCy Schubert
167885732ac8SCy Schubert params->relative_rssi_set = 1;
167985732ac8SCy Schubert params->relative_rssi = wpa_s->srp.relative_rssi;
168085732ac8SCy Schubert
168185732ac8SCy Schubert if (wpa_s->srp.relative_adjust_rssi == 0)
168285732ac8SCy Schubert return;
168385732ac8SCy Schubert
168485732ac8SCy Schubert params->relative_adjust_band = wpa_s->srp.relative_adjust_band;
168585732ac8SCy Schubert params->relative_adjust_rssi = wpa_s->srp.relative_adjust_rssi;
168685732ac8SCy Schubert }
168785732ac8SCy Schubert
168885732ac8SCy Schubert
1689f05cddf9SRui Paulo /**
1690f05cddf9SRui Paulo * wpa_supplicant_req_sched_scan - Start a periodic scheduled scan
1691f05cddf9SRui Paulo * @wpa_s: Pointer to wpa_supplicant data
1692f05cddf9SRui Paulo * Returns: 0 is sched_scan was started or -1 otherwise
1693f05cddf9SRui Paulo *
1694f05cddf9SRui Paulo * This function is used to schedule periodic scans for neighboring
1695f05cddf9SRui Paulo * access points repeating the scan continuously.
1696f05cddf9SRui Paulo */
wpa_supplicant_req_sched_scan(struct wpa_supplicant * wpa_s)1697f05cddf9SRui Paulo int wpa_supplicant_req_sched_scan(struct wpa_supplicant *wpa_s)
1698f05cddf9SRui Paulo {
1699f05cddf9SRui Paulo struct wpa_driver_scan_params params;
1700f05cddf9SRui Paulo struct wpa_driver_scan_params *scan_params;
1701f05cddf9SRui Paulo enum wpa_states prev_state;
1702f05cddf9SRui Paulo struct wpa_ssid *ssid = NULL;
1703f05cddf9SRui Paulo struct wpabuf *extra_ie = NULL;
1704f05cddf9SRui Paulo int ret;
1705f05cddf9SRui Paulo unsigned int max_sched_scan_ssids;
1706f05cddf9SRui Paulo int wildcard = 0;
1707f05cddf9SRui Paulo int need_ssids;
1708780fb4a2SCy Schubert struct sched_scan_plan scan_plan;
1709f05cddf9SRui Paulo
1710f05cddf9SRui Paulo if (!wpa_s->sched_scan_supported)
1711f05cddf9SRui Paulo return -1;
1712f05cddf9SRui Paulo
1713f05cddf9SRui Paulo if (wpa_s->max_sched_scan_ssids > WPAS_MAX_SCAN_SSIDS)
1714f05cddf9SRui Paulo max_sched_scan_ssids = WPAS_MAX_SCAN_SSIDS;
1715f05cddf9SRui Paulo else
1716f05cddf9SRui Paulo max_sched_scan_ssids = wpa_s->max_sched_scan_ssids;
1717f05cddf9SRui Paulo if (max_sched_scan_ssids < 1 || wpa_s->conf->disable_scan_offload)
1718f05cddf9SRui Paulo return -1;
1719f05cddf9SRui Paulo
1720780fb4a2SCy Schubert wpa_s->sched_scan_stop_req = 0;
1721780fb4a2SCy Schubert
1722f05cddf9SRui Paulo if (wpa_s->sched_scanning) {
1723f05cddf9SRui Paulo wpa_dbg(wpa_s, MSG_DEBUG, "Already sched scanning");
1724f05cddf9SRui Paulo return 0;
1725f05cddf9SRui Paulo }
1726f05cddf9SRui Paulo
1727f05cddf9SRui Paulo need_ssids = 0;
1728f05cddf9SRui Paulo for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
1729f05cddf9SRui Paulo if (!wpas_network_disabled(wpa_s, ssid) && !ssid->scan_ssid) {
1730f05cddf9SRui Paulo /* Use wildcard SSID to find this network */
1731f05cddf9SRui Paulo wildcard = 1;
1732f05cddf9SRui Paulo } else if (!wpas_network_disabled(wpa_s, ssid) &&
1733f05cddf9SRui Paulo ssid->ssid_len)
1734f05cddf9SRui Paulo need_ssids++;
1735f05cddf9SRui Paulo
1736f05cddf9SRui Paulo #ifdef CONFIG_WPS
1737f05cddf9SRui Paulo if (!wpas_network_disabled(wpa_s, ssid) &&
1738f05cddf9SRui Paulo ssid->key_mgmt == WPA_KEY_MGMT_WPS) {
1739f05cddf9SRui Paulo /*
1740f05cddf9SRui Paulo * Normal scan is more reliable and faster for WPS
1741f05cddf9SRui Paulo * operations and since these are for short periods of
1742f05cddf9SRui Paulo * time, the benefit of trying to use sched_scan would
1743f05cddf9SRui Paulo * be limited.
1744f05cddf9SRui Paulo */
1745f05cddf9SRui Paulo wpa_dbg(wpa_s, MSG_DEBUG, "Use normal scan instead of "
1746f05cddf9SRui Paulo "sched_scan for WPS");
1747f05cddf9SRui Paulo return -1;
1748f05cddf9SRui Paulo }
1749f05cddf9SRui Paulo #endif /* CONFIG_WPS */
1750f05cddf9SRui Paulo }
1751f05cddf9SRui Paulo if (wildcard)
1752f05cddf9SRui Paulo need_ssids++;
1753f05cddf9SRui Paulo
1754f05cddf9SRui Paulo if (wpa_s->normal_scans < 3 &&
1755f05cddf9SRui Paulo (need_ssids <= wpa_s->max_scan_ssids ||
1756f05cddf9SRui Paulo wpa_s->max_scan_ssids >= (int) max_sched_scan_ssids)) {
1757f05cddf9SRui Paulo /*
1758f05cddf9SRui Paulo * When normal scan can speed up operations, use that for the
1759f05cddf9SRui Paulo * first operations before starting the sched_scan to allow
1760f05cddf9SRui Paulo * user space sleep more. We do this only if the normal scan
1761f05cddf9SRui Paulo * has functionality that is suitable for this or if the
1762f05cddf9SRui Paulo * sched_scan does not have better support for multiple SSIDs.
1763f05cddf9SRui Paulo */
1764f05cddf9SRui Paulo wpa_dbg(wpa_s, MSG_DEBUG, "Use normal scan instead of "
1765f05cddf9SRui Paulo "sched_scan for initial scans (normal_scans=%d)",
1766f05cddf9SRui Paulo wpa_s->normal_scans);
1767f05cddf9SRui Paulo return -1;
1768f05cddf9SRui Paulo }
1769f05cddf9SRui Paulo
1770f05cddf9SRui Paulo os_memset(¶ms, 0, sizeof(params));
1771f05cddf9SRui Paulo
1772f05cddf9SRui Paulo /* If we can't allocate space for the filters, we just don't filter */
17735b9c547cSRui Paulo params.filter_ssids = os_calloc(wpa_s->max_match_sets,
1774f05cddf9SRui Paulo sizeof(struct wpa_driver_scan_filter));
1775f05cddf9SRui Paulo
1776f05cddf9SRui Paulo prev_state = wpa_s->wpa_state;
1777f05cddf9SRui Paulo if (wpa_s->wpa_state == WPA_DISCONNECTED ||
1778f05cddf9SRui Paulo wpa_s->wpa_state == WPA_INACTIVE)
1779f05cddf9SRui Paulo wpa_supplicant_set_state(wpa_s, WPA_SCANNING);
1780f05cddf9SRui Paulo
1781f05cddf9SRui Paulo if (wpa_s->autoscan_params != NULL) {
1782f05cddf9SRui Paulo scan_params = wpa_s->autoscan_params;
1783f05cddf9SRui Paulo goto scan;
1784f05cddf9SRui Paulo }
1785f05cddf9SRui Paulo
1786f05cddf9SRui Paulo /* Find the starting point from which to continue scanning */
1787f05cddf9SRui Paulo ssid = wpa_s->conf->ssid;
1788f05cddf9SRui Paulo if (wpa_s->prev_sched_ssid) {
1789f05cddf9SRui Paulo while (ssid) {
1790f05cddf9SRui Paulo if (ssid == wpa_s->prev_sched_ssid) {
1791f05cddf9SRui Paulo ssid = ssid->next;
1792f05cddf9SRui Paulo break;
1793f05cddf9SRui Paulo }
1794f05cddf9SRui Paulo ssid = ssid->next;
1795f05cddf9SRui Paulo }
1796f05cddf9SRui Paulo }
1797f05cddf9SRui Paulo
1798f05cddf9SRui Paulo if (!ssid || !wpa_s->prev_sched_ssid) {
1799f05cddf9SRui Paulo wpa_dbg(wpa_s, MSG_DEBUG, "Beginning of SSID list");
1800f05cddf9SRui Paulo wpa_s->sched_scan_timeout = max_sched_scan_ssids * 2;
1801f05cddf9SRui Paulo wpa_s->first_sched_scan = 1;
1802f05cddf9SRui Paulo ssid = wpa_s->conf->ssid;
1803f05cddf9SRui Paulo wpa_s->prev_sched_ssid = ssid;
1804f05cddf9SRui Paulo }
1805f05cddf9SRui Paulo
1806f05cddf9SRui Paulo if (wildcard) {
1807f05cddf9SRui Paulo wpa_dbg(wpa_s, MSG_DEBUG, "Add wildcard SSID to sched_scan");
1808f05cddf9SRui Paulo params.num_ssids++;
1809f05cddf9SRui Paulo }
1810f05cddf9SRui Paulo
1811f05cddf9SRui Paulo while (ssid) {
1812f05cddf9SRui Paulo if (wpas_network_disabled(wpa_s, ssid))
1813f05cddf9SRui Paulo goto next;
1814f05cddf9SRui Paulo
1815f05cddf9SRui Paulo if (params.num_filter_ssids < wpa_s->max_match_sets &&
1816f05cddf9SRui Paulo params.filter_ssids && ssid->ssid && ssid->ssid_len) {
1817f05cddf9SRui Paulo wpa_dbg(wpa_s, MSG_DEBUG, "add to filter ssid: %s",
1818f05cddf9SRui Paulo wpa_ssid_txt(ssid->ssid, ssid->ssid_len));
1819f05cddf9SRui Paulo os_memcpy(params.filter_ssids[params.num_filter_ssids].ssid,
1820f05cddf9SRui Paulo ssid->ssid, ssid->ssid_len);
1821f05cddf9SRui Paulo params.filter_ssids[params.num_filter_ssids].ssid_len =
1822f05cddf9SRui Paulo ssid->ssid_len;
1823f05cddf9SRui Paulo params.num_filter_ssids++;
1824f05cddf9SRui Paulo } else if (params.filter_ssids && ssid->ssid && ssid->ssid_len)
1825f05cddf9SRui Paulo {
1826f05cddf9SRui Paulo wpa_dbg(wpa_s, MSG_DEBUG, "Not enough room for SSID "
1827f05cddf9SRui Paulo "filter for sched_scan - drop filter");
1828f05cddf9SRui Paulo os_free(params.filter_ssids);
1829f05cddf9SRui Paulo params.filter_ssids = NULL;
1830f05cddf9SRui Paulo params.num_filter_ssids = 0;
1831f05cddf9SRui Paulo }
1832f05cddf9SRui Paulo
1833f05cddf9SRui Paulo if (ssid->scan_ssid && ssid->ssid && ssid->ssid_len) {
1834f05cddf9SRui Paulo if (params.num_ssids == max_sched_scan_ssids)
1835f05cddf9SRui Paulo break; /* only room for broadcast SSID */
1836f05cddf9SRui Paulo wpa_dbg(wpa_s, MSG_DEBUG,
1837f05cddf9SRui Paulo "add to active scan ssid: %s",
1838f05cddf9SRui Paulo wpa_ssid_txt(ssid->ssid, ssid->ssid_len));
1839f05cddf9SRui Paulo params.ssids[params.num_ssids].ssid =
1840f05cddf9SRui Paulo ssid->ssid;
1841f05cddf9SRui Paulo params.ssids[params.num_ssids].ssid_len =
1842f05cddf9SRui Paulo ssid->ssid_len;
1843f05cddf9SRui Paulo params.num_ssids++;
1844f05cddf9SRui Paulo if (params.num_ssids >= max_sched_scan_ssids) {
1845f05cddf9SRui Paulo wpa_s->prev_sched_ssid = ssid;
1846f05cddf9SRui Paulo do {
1847f05cddf9SRui Paulo ssid = ssid->next;
1848f05cddf9SRui Paulo } while (ssid &&
1849f05cddf9SRui Paulo (wpas_network_disabled(wpa_s, ssid) ||
1850f05cddf9SRui Paulo !ssid->scan_ssid));
1851f05cddf9SRui Paulo break;
1852f05cddf9SRui Paulo }
1853f05cddf9SRui Paulo }
1854f05cddf9SRui Paulo
1855f05cddf9SRui Paulo next:
1856f05cddf9SRui Paulo wpa_s->prev_sched_ssid = ssid;
1857f05cddf9SRui Paulo ssid = ssid->next;
1858f05cddf9SRui Paulo }
1859f05cddf9SRui Paulo
1860f05cddf9SRui Paulo if (params.num_filter_ssids == 0) {
1861f05cddf9SRui Paulo os_free(params.filter_ssids);
1862f05cddf9SRui Paulo params.filter_ssids = NULL;
1863f05cddf9SRui Paulo }
1864f05cddf9SRui Paulo
1865f05cddf9SRui Paulo extra_ie = wpa_supplicant_extra_ies(wpa_s);
1866f05cddf9SRui Paulo if (extra_ie) {
1867f05cddf9SRui Paulo params.extra_ies = wpabuf_head(extra_ie);
1868f05cddf9SRui Paulo params.extra_ies_len = wpabuf_len(extra_ie);
1869f05cddf9SRui Paulo }
1870f05cddf9SRui Paulo
18715b9c547cSRui Paulo if (wpa_s->conf->filter_rssi)
18725b9c547cSRui Paulo params.filter_rssi = wpa_s->conf->filter_rssi;
18735b9c547cSRui Paulo
18745b9c547cSRui Paulo /* See if user specified frequencies. If so, scan only those. */
18755b9c547cSRui Paulo if (wpa_s->conf->freq_list && !params.freqs) {
18765b9c547cSRui Paulo wpa_dbg(wpa_s, MSG_DEBUG,
18775b9c547cSRui Paulo "Optimize scan based on conf->freq_list");
18785b9c547cSRui Paulo int_array_concat(¶ms.freqs, wpa_s->conf->freq_list);
18795b9c547cSRui Paulo }
18805b9c547cSRui Paulo
188185732ac8SCy Schubert #ifdef CONFIG_MBO
188285732ac8SCy Schubert if (wpa_s->enable_oce & OCE_STA)
188385732ac8SCy Schubert params.oce_scan = 1;
188485732ac8SCy Schubert #endif /* CONFIG_MBO */
188585732ac8SCy Schubert
1886f05cddf9SRui Paulo scan_params = ¶ms;
1887f05cddf9SRui Paulo
1888f05cddf9SRui Paulo scan:
1889780fb4a2SCy Schubert wpa_s->sched_scan_timed_out = 0;
1890780fb4a2SCy Schubert
1891780fb4a2SCy Schubert /*
1892780fb4a2SCy Schubert * We cannot support multiple scan plans if the scan request includes
1893780fb4a2SCy Schubert * too many SSID's, so in this case use only the last scan plan and make
1894780fb4a2SCy Schubert * it run infinitely. It will be stopped by the timeout.
1895780fb4a2SCy Schubert */
1896780fb4a2SCy Schubert if (wpa_s->sched_scan_plans_num == 1 ||
1897780fb4a2SCy Schubert (wpa_s->sched_scan_plans_num && !ssid && wpa_s->first_sched_scan)) {
1898780fb4a2SCy Schubert params.sched_scan_plans = wpa_s->sched_scan_plans;
1899780fb4a2SCy Schubert params.sched_scan_plans_num = wpa_s->sched_scan_plans_num;
1900780fb4a2SCy Schubert } else if (wpa_s->sched_scan_plans_num > 1) {
1901780fb4a2SCy Schubert wpa_dbg(wpa_s, MSG_DEBUG,
1902780fb4a2SCy Schubert "Too many SSIDs. Default to using single scheduled_scan plan");
1903780fb4a2SCy Schubert params.sched_scan_plans =
1904780fb4a2SCy Schubert &wpa_s->sched_scan_plans[wpa_s->sched_scan_plans_num -
1905780fb4a2SCy Schubert 1];
1906780fb4a2SCy Schubert params.sched_scan_plans_num = 1;
1907780fb4a2SCy Schubert } else {
1908780fb4a2SCy Schubert if (wpa_s->conf->sched_scan_interval)
1909780fb4a2SCy Schubert scan_plan.interval = wpa_s->conf->sched_scan_interval;
1910780fb4a2SCy Schubert else
1911780fb4a2SCy Schubert scan_plan.interval = 10;
1912780fb4a2SCy Schubert
1913780fb4a2SCy Schubert if (scan_plan.interval > wpa_s->max_sched_scan_plan_interval) {
1914780fb4a2SCy Schubert wpa_printf(MSG_WARNING,
1915780fb4a2SCy Schubert "Scan interval too long(%u), use the maximum allowed(%u)",
1916780fb4a2SCy Schubert scan_plan.interval,
1917780fb4a2SCy Schubert wpa_s->max_sched_scan_plan_interval);
1918780fb4a2SCy Schubert scan_plan.interval =
1919780fb4a2SCy Schubert wpa_s->max_sched_scan_plan_interval;
1920780fb4a2SCy Schubert }
1921780fb4a2SCy Schubert
1922780fb4a2SCy Schubert scan_plan.iterations = 0;
1923780fb4a2SCy Schubert params.sched_scan_plans = &scan_plan;
1924780fb4a2SCy Schubert params.sched_scan_plans_num = 1;
1925780fb4a2SCy Schubert }
1926780fb4a2SCy Schubert
192785732ac8SCy Schubert params.sched_scan_start_delay = wpa_s->conf->sched_scan_start_delay;
192885732ac8SCy Schubert
1929f05cddf9SRui Paulo if (ssid || !wpa_s->first_sched_scan) {
1930f05cddf9SRui Paulo wpa_dbg(wpa_s, MSG_DEBUG,
193185732ac8SCy Schubert "Starting sched scan after %u seconds: interval %u timeout %d",
193285732ac8SCy Schubert params.sched_scan_start_delay,
1933780fb4a2SCy Schubert params.sched_scan_plans[0].interval,
1934780fb4a2SCy Schubert wpa_s->sched_scan_timeout);
1935f05cddf9SRui Paulo } else {
193685732ac8SCy Schubert wpa_dbg(wpa_s, MSG_DEBUG,
193785732ac8SCy Schubert "Starting sched scan after %u seconds (no timeout)",
193885732ac8SCy Schubert params.sched_scan_start_delay);
1939f05cddf9SRui Paulo }
1940f05cddf9SRui Paulo
19415b9c547cSRui Paulo wpa_setband_scan_freqs(wpa_s, scan_params);
19425b9c547cSRui Paulo
194385732ac8SCy Schubert if ((wpa_s->mac_addr_rand_enable & MAC_ADDR_RAND_SCHED_SCAN) &&
1944c1d255d3SCy Schubert wpa_s->wpa_state <= WPA_SCANNING)
1945c1d255d3SCy Schubert wpa_setup_mac_addr_rand_params(¶ms,
1946c1d255d3SCy Schubert wpa_s->mac_addr_sched_scan);
19475b9c547cSRui Paulo
194885732ac8SCy Schubert wpa_scan_set_relative_rssi_params(wpa_s, scan_params);
194985732ac8SCy Schubert
1950780fb4a2SCy Schubert ret = wpa_supplicant_start_sched_scan(wpa_s, scan_params);
1951f05cddf9SRui Paulo wpabuf_free(extra_ie);
1952f05cddf9SRui Paulo os_free(params.filter_ssids);
1953c1d255d3SCy Schubert os_free(params.mac_addr);
1954f05cddf9SRui Paulo if (ret) {
1955f05cddf9SRui Paulo wpa_msg(wpa_s, MSG_WARNING, "Failed to initiate sched scan");
1956f05cddf9SRui Paulo if (prev_state != wpa_s->wpa_state)
1957f05cddf9SRui Paulo wpa_supplicant_set_state(wpa_s, prev_state);
1958f05cddf9SRui Paulo return ret;
1959f05cddf9SRui Paulo }
1960f05cddf9SRui Paulo
1961f05cddf9SRui Paulo /* If we have more SSIDs to scan, add a timeout so we scan them too */
1962f05cddf9SRui Paulo if (ssid || !wpa_s->first_sched_scan) {
1963f05cddf9SRui Paulo wpa_s->sched_scan_timed_out = 0;
1964f05cddf9SRui Paulo eloop_register_timeout(wpa_s->sched_scan_timeout, 0,
1965f05cddf9SRui Paulo wpa_supplicant_sched_scan_timeout,
1966f05cddf9SRui Paulo wpa_s, NULL);
1967f05cddf9SRui Paulo wpa_s->first_sched_scan = 0;
1968f05cddf9SRui Paulo wpa_s->sched_scan_timeout /= 2;
1969780fb4a2SCy Schubert params.sched_scan_plans[0].interval *= 2;
1970780fb4a2SCy Schubert if ((unsigned int) wpa_s->sched_scan_timeout <
1971780fb4a2SCy Schubert params.sched_scan_plans[0].interval ||
1972780fb4a2SCy Schubert params.sched_scan_plans[0].interval >
1973780fb4a2SCy Schubert wpa_s->max_sched_scan_plan_interval) {
1974780fb4a2SCy Schubert params.sched_scan_plans[0].interval = 10;
19755b9c547cSRui Paulo wpa_s->sched_scan_timeout = max_sched_scan_ssids * 2;
1976f05cddf9SRui Paulo }
19775b9c547cSRui Paulo }
19785b9c547cSRui Paulo
19795b9c547cSRui Paulo /* If there is no more ssids, start next time from the beginning */
19805b9c547cSRui Paulo if (!ssid)
19815b9c547cSRui Paulo wpa_s->prev_sched_ssid = NULL;
1982f05cddf9SRui Paulo
1983f05cddf9SRui Paulo return 0;
1984f05cddf9SRui Paulo }
1985f05cddf9SRui Paulo
1986f05cddf9SRui Paulo
1987f05cddf9SRui Paulo /**
198839beb93cSSam Leffler * wpa_supplicant_cancel_scan - Cancel a scheduled scan request
198939beb93cSSam Leffler * @wpa_s: Pointer to wpa_supplicant data
199039beb93cSSam Leffler *
199139beb93cSSam Leffler * This function is used to cancel a scan request scheduled with
199239beb93cSSam Leffler * wpa_supplicant_req_scan().
199339beb93cSSam Leffler */
wpa_supplicant_cancel_scan(struct wpa_supplicant * wpa_s)199439beb93cSSam Leffler void wpa_supplicant_cancel_scan(struct wpa_supplicant *wpa_s)
199539beb93cSSam Leffler {
1996f05cddf9SRui Paulo wpa_dbg(wpa_s, MSG_DEBUG, "Cancelling scan request");
199739beb93cSSam Leffler eloop_cancel_timeout(wpa_supplicant_scan, wpa_s, NULL);
199839beb93cSSam Leffler }
19993157ba21SRui Paulo
20003157ba21SRui Paulo
2001f05cddf9SRui Paulo /**
20025b9c547cSRui Paulo * wpa_supplicant_cancel_delayed_sched_scan - Stop a delayed scheduled scan
20035b9c547cSRui Paulo * @wpa_s: Pointer to wpa_supplicant data
20045b9c547cSRui Paulo *
20055b9c547cSRui Paulo * This function is used to stop a delayed scheduled scan.
20065b9c547cSRui Paulo */
wpa_supplicant_cancel_delayed_sched_scan(struct wpa_supplicant * wpa_s)20075b9c547cSRui Paulo void wpa_supplicant_cancel_delayed_sched_scan(struct wpa_supplicant *wpa_s)
20085b9c547cSRui Paulo {
20095b9c547cSRui Paulo if (!wpa_s->sched_scan_supported)
20105b9c547cSRui Paulo return;
20115b9c547cSRui Paulo
20125b9c547cSRui Paulo wpa_dbg(wpa_s, MSG_DEBUG, "Cancelling delayed sched scan");
20135b9c547cSRui Paulo eloop_cancel_timeout(wpa_supplicant_delayed_sched_scan_timeout,
20145b9c547cSRui Paulo wpa_s, NULL);
20155b9c547cSRui Paulo }
20165b9c547cSRui Paulo
20175b9c547cSRui Paulo
20185b9c547cSRui Paulo /**
2019f05cddf9SRui Paulo * wpa_supplicant_cancel_sched_scan - Stop running scheduled scans
2020f05cddf9SRui Paulo * @wpa_s: Pointer to wpa_supplicant data
2021f05cddf9SRui Paulo *
2022f05cddf9SRui Paulo * This function is used to stop a periodic scheduled scan.
2023f05cddf9SRui Paulo */
wpa_supplicant_cancel_sched_scan(struct wpa_supplicant * wpa_s)2024f05cddf9SRui Paulo void wpa_supplicant_cancel_sched_scan(struct wpa_supplicant *wpa_s)
2025f05cddf9SRui Paulo {
2026f05cddf9SRui Paulo if (!wpa_s->sched_scanning)
2027f05cddf9SRui Paulo return;
2028f05cddf9SRui Paulo
2029780fb4a2SCy Schubert if (wpa_s->sched_scanning)
2030780fb4a2SCy Schubert wpa_s->sched_scan_stop_req = 1;
2031780fb4a2SCy Schubert
2032f05cddf9SRui Paulo wpa_dbg(wpa_s, MSG_DEBUG, "Cancelling sched scan");
2033f05cddf9SRui Paulo eloop_cancel_timeout(wpa_supplicant_sched_scan_timeout, wpa_s, NULL);
2034f05cddf9SRui Paulo wpa_supplicant_stop_sched_scan(wpa_s);
2035f05cddf9SRui Paulo }
2036f05cddf9SRui Paulo
2037f05cddf9SRui Paulo
2038f05cddf9SRui Paulo /**
2039f05cddf9SRui Paulo * wpa_supplicant_notify_scanning - Indicate possible scan state change
2040f05cddf9SRui Paulo * @wpa_s: Pointer to wpa_supplicant data
2041f05cddf9SRui Paulo * @scanning: Whether scanning is currently in progress
2042f05cddf9SRui Paulo *
2043f05cddf9SRui Paulo * This function is to generate scanning notifycations. It is called whenever
2044f05cddf9SRui Paulo * there may have been a change in scanning (scan started, completed, stopped).
2045f05cddf9SRui Paulo * wpas_notify_scanning() is called whenever the scanning state changed from the
2046f05cddf9SRui Paulo * previously notified state.
2047f05cddf9SRui Paulo */
wpa_supplicant_notify_scanning(struct wpa_supplicant * wpa_s,int scanning)20483157ba21SRui Paulo void wpa_supplicant_notify_scanning(struct wpa_supplicant *wpa_s,
20493157ba21SRui Paulo int scanning)
20503157ba21SRui Paulo {
20513157ba21SRui Paulo if (wpa_s->scanning != scanning) {
20523157ba21SRui Paulo wpa_s->scanning = scanning;
2053e28a4053SRui Paulo wpas_notify_scanning(wpa_s);
20543157ba21SRui Paulo }
20553157ba21SRui Paulo }
20563157ba21SRui Paulo
2057e28a4053SRui Paulo
wpa_scan_get_max_rate(const struct wpa_scan_res * res)2058e28a4053SRui Paulo static int wpa_scan_get_max_rate(const struct wpa_scan_res *res)
2059e28a4053SRui Paulo {
2060e28a4053SRui Paulo int rate = 0;
2061e28a4053SRui Paulo const u8 *ie;
2062e28a4053SRui Paulo int i;
2063e28a4053SRui Paulo
2064e28a4053SRui Paulo ie = wpa_scan_get_ie(res, WLAN_EID_SUPP_RATES);
2065e28a4053SRui Paulo for (i = 0; ie && i < ie[1]; i++) {
2066e28a4053SRui Paulo if ((ie[i + 2] & 0x7f) > rate)
2067e28a4053SRui Paulo rate = ie[i + 2] & 0x7f;
2068e28a4053SRui Paulo }
2069e28a4053SRui Paulo
2070e28a4053SRui Paulo ie = wpa_scan_get_ie(res, WLAN_EID_EXT_SUPP_RATES);
2071e28a4053SRui Paulo for (i = 0; ie && i < ie[1]; i++) {
2072e28a4053SRui Paulo if ((ie[i + 2] & 0x7f) > rate)
2073e28a4053SRui Paulo rate = ie[i + 2] & 0x7f;
2074e28a4053SRui Paulo }
2075e28a4053SRui Paulo
2076e28a4053SRui Paulo return rate;
2077e28a4053SRui Paulo }
2078e28a4053SRui Paulo
2079e28a4053SRui Paulo
2080f05cddf9SRui Paulo /**
2081f05cddf9SRui Paulo * wpa_scan_get_ie - Fetch a specified information element from a scan result
2082f05cddf9SRui Paulo * @res: Scan result entry
2083f05cddf9SRui Paulo * @ie: Information element identitifier (WLAN_EID_*)
2084f05cddf9SRui Paulo * Returns: Pointer to the information element (id field) or %NULL if not found
2085f05cddf9SRui Paulo *
2086f05cddf9SRui Paulo * This function returns the first matching information element in the scan
2087f05cddf9SRui Paulo * result.
2088f05cddf9SRui Paulo */
wpa_scan_get_ie(const struct wpa_scan_res * res,u8 ie)2089e28a4053SRui Paulo const u8 * wpa_scan_get_ie(const struct wpa_scan_res *res, u8 ie)
2090e28a4053SRui Paulo {
209185732ac8SCy Schubert size_t ie_len = res->ie_len;
209285732ac8SCy Schubert
209385732ac8SCy Schubert /* Use the Beacon frame IEs if res->ie_len is not available */
209485732ac8SCy Schubert if (!ie_len)
209585732ac8SCy Schubert ie_len = res->beacon_ie_len;
209685732ac8SCy Schubert
209785732ac8SCy Schubert return get_ie((const u8 *) (res + 1), ie_len, ie);
2098e28a4053SRui Paulo }
2099e28a4053SRui Paulo
2100e28a4053SRui Paulo
wpa_scan_get_ml_ie(const struct wpa_scan_res * res,u8 type)2101*a90b9d01SCy Schubert const u8 * wpa_scan_get_ml_ie(const struct wpa_scan_res *res, u8 type)
2102*a90b9d01SCy Schubert {
2103*a90b9d01SCy Schubert size_t ie_len = res->ie_len;
2104*a90b9d01SCy Schubert
2105*a90b9d01SCy Schubert /* Use the Beacon frame IEs if res->ie_len is not available */
2106*a90b9d01SCy Schubert if (!ie_len)
2107*a90b9d01SCy Schubert ie_len = res->beacon_ie_len;
2108*a90b9d01SCy Schubert
2109*a90b9d01SCy Schubert return get_ml_ie((const u8 *) (res + 1), ie_len, type);
2110*a90b9d01SCy Schubert }
2111*a90b9d01SCy Schubert
2112*a90b9d01SCy Schubert
2113f05cddf9SRui Paulo /**
2114f05cddf9SRui Paulo * wpa_scan_get_vendor_ie - Fetch vendor information element from a scan result
2115f05cddf9SRui Paulo * @res: Scan result entry
2116f05cddf9SRui Paulo * @vendor_type: Vendor type (four octets starting the IE payload)
2117f05cddf9SRui Paulo * Returns: Pointer to the information element (id field) or %NULL if not found
2118f05cddf9SRui Paulo *
2119f05cddf9SRui Paulo * This function returns the first matching information element in the scan
2120f05cddf9SRui Paulo * result.
2121f05cddf9SRui Paulo */
wpa_scan_get_vendor_ie(const struct wpa_scan_res * res,u32 vendor_type)2122e28a4053SRui Paulo const u8 * wpa_scan_get_vendor_ie(const struct wpa_scan_res *res,
2123e28a4053SRui Paulo u32 vendor_type)
2124e28a4053SRui Paulo {
2125c1d255d3SCy Schubert const u8 *ies;
2126c1d255d3SCy Schubert const struct element *elem;
2127e28a4053SRui Paulo
2128c1d255d3SCy Schubert ies = (const u8 *) (res + 1);
2129e28a4053SRui Paulo
2130c1d255d3SCy Schubert for_each_element_id(elem, WLAN_EID_VENDOR_SPECIFIC, ies, res->ie_len) {
2131c1d255d3SCy Schubert if (elem->datalen >= 4 &&
2132c1d255d3SCy Schubert vendor_type == WPA_GET_BE32(elem->data))
2133c1d255d3SCy Schubert return &elem->id;
2134e28a4053SRui Paulo }
2135e28a4053SRui Paulo
2136e28a4053SRui Paulo return NULL;
2137e28a4053SRui Paulo }
2138e28a4053SRui Paulo
2139e28a4053SRui Paulo
2140f05cddf9SRui Paulo /**
21415b9c547cSRui Paulo * wpa_scan_get_vendor_ie_beacon - Fetch vendor information from a scan result
21425b9c547cSRui Paulo * @res: Scan result entry
21435b9c547cSRui Paulo * @vendor_type: Vendor type (four octets starting the IE payload)
21445b9c547cSRui Paulo * Returns: Pointer to the information element (id field) or %NULL if not found
21455b9c547cSRui Paulo *
21465b9c547cSRui Paulo * This function returns the first matching information element in the scan
21475b9c547cSRui Paulo * result.
21485b9c547cSRui Paulo *
21495b9c547cSRui Paulo * This function is like wpa_scan_get_vendor_ie(), but uses IE buffer only
21505b9c547cSRui Paulo * from Beacon frames instead of either Beacon or Probe Response frames.
21515b9c547cSRui Paulo */
wpa_scan_get_vendor_ie_beacon(const struct wpa_scan_res * res,u32 vendor_type)21525b9c547cSRui Paulo const u8 * wpa_scan_get_vendor_ie_beacon(const struct wpa_scan_res *res,
21535b9c547cSRui Paulo u32 vendor_type)
21545b9c547cSRui Paulo {
2155c1d255d3SCy Schubert const u8 *ies;
2156c1d255d3SCy Schubert const struct element *elem;
21575b9c547cSRui Paulo
21585b9c547cSRui Paulo if (res->beacon_ie_len == 0)
21595b9c547cSRui Paulo return NULL;
21605b9c547cSRui Paulo
2161c1d255d3SCy Schubert ies = (const u8 *) (res + 1);
2162c1d255d3SCy Schubert ies += res->ie_len;
21635b9c547cSRui Paulo
2164c1d255d3SCy Schubert for_each_element_id(elem, WLAN_EID_VENDOR_SPECIFIC, ies,
2165c1d255d3SCy Schubert res->beacon_ie_len) {
2166c1d255d3SCy Schubert if (elem->datalen >= 4 &&
2167c1d255d3SCy Schubert vendor_type == WPA_GET_BE32(elem->data))
2168c1d255d3SCy Schubert return &elem->id;
21695b9c547cSRui Paulo }
21705b9c547cSRui Paulo
21715b9c547cSRui Paulo return NULL;
21725b9c547cSRui Paulo }
21735b9c547cSRui Paulo
21745b9c547cSRui Paulo
21755b9c547cSRui Paulo /**
2176f05cddf9SRui Paulo * wpa_scan_get_vendor_ie_multi - Fetch vendor IE data from a scan result
2177f05cddf9SRui Paulo * @res: Scan result entry
2178f05cddf9SRui Paulo * @vendor_type: Vendor type (four octets starting the IE payload)
2179f05cddf9SRui Paulo * Returns: Pointer to the information element payload or %NULL if not found
2180f05cddf9SRui Paulo *
2181f05cddf9SRui Paulo * This function returns concatenated payload of possibly fragmented vendor
2182f05cddf9SRui Paulo * specific information elements in the scan result. The caller is responsible
2183f05cddf9SRui Paulo * for freeing the returned buffer.
2184f05cddf9SRui Paulo */
wpa_scan_get_vendor_ie_multi(const struct wpa_scan_res * res,u32 vendor_type)2185e28a4053SRui Paulo struct wpabuf * wpa_scan_get_vendor_ie_multi(const struct wpa_scan_res *res,
2186e28a4053SRui Paulo u32 vendor_type)
2187e28a4053SRui Paulo {
2188e28a4053SRui Paulo struct wpabuf *buf;
2189e28a4053SRui Paulo const u8 *end, *pos;
2190e28a4053SRui Paulo
2191e28a4053SRui Paulo buf = wpabuf_alloc(res->ie_len);
2192e28a4053SRui Paulo if (buf == NULL)
2193e28a4053SRui Paulo return NULL;
2194e28a4053SRui Paulo
2195e28a4053SRui Paulo pos = (const u8 *) (res + 1);
2196e28a4053SRui Paulo end = pos + res->ie_len;
2197e28a4053SRui Paulo
2198780fb4a2SCy Schubert while (end - pos > 1) {
2199c1d255d3SCy Schubert u8 ie, len;
2200c1d255d3SCy Schubert
2201c1d255d3SCy Schubert ie = pos[0];
2202c1d255d3SCy Schubert len = pos[1];
2203c1d255d3SCy Schubert if (len > end - pos - 2)
2204e28a4053SRui Paulo break;
2205c1d255d3SCy Schubert pos += 2;
2206c1d255d3SCy Schubert if (ie == WLAN_EID_VENDOR_SPECIFIC && len >= 4 &&
2207c1d255d3SCy Schubert vendor_type == WPA_GET_BE32(pos))
2208c1d255d3SCy Schubert wpabuf_put_data(buf, pos + 4, len - 4);
2209c1d255d3SCy Schubert pos += len;
2210e28a4053SRui Paulo }
2211e28a4053SRui Paulo
2212e28a4053SRui Paulo if (wpabuf_len(buf) == 0) {
2213e28a4053SRui Paulo wpabuf_free(buf);
2214e28a4053SRui Paulo buf = NULL;
2215e28a4053SRui Paulo }
2216e28a4053SRui Paulo
2217e28a4053SRui Paulo return buf;
2218e28a4053SRui Paulo }
2219e28a4053SRui Paulo
2220e28a4053SRui Paulo
wpas_channel_width_offset(enum chan_width cw)2221*a90b9d01SCy Schubert static int wpas_channel_width_offset(enum chan_width cw)
2222*a90b9d01SCy Schubert {
2223*a90b9d01SCy Schubert switch (cw) {
2224*a90b9d01SCy Schubert case CHAN_WIDTH_40:
2225*a90b9d01SCy Schubert return 1;
2226*a90b9d01SCy Schubert case CHAN_WIDTH_80:
2227*a90b9d01SCy Schubert return 2;
2228*a90b9d01SCy Schubert case CHAN_WIDTH_80P80:
2229*a90b9d01SCy Schubert case CHAN_WIDTH_160:
2230*a90b9d01SCy Schubert return 3;
2231*a90b9d01SCy Schubert case CHAN_WIDTH_320:
2232*a90b9d01SCy Schubert return 4;
2233*a90b9d01SCy Schubert default:
2234*a90b9d01SCy Schubert return 0;
2235*a90b9d01SCy Schubert }
2236*a90b9d01SCy Schubert }
2237*a90b9d01SCy Schubert
2238*a90b9d01SCy Schubert
2239*a90b9d01SCy Schubert /**
2240*a90b9d01SCy Schubert * wpas_channel_width_tx_pwr - Calculate the max transmit power at the channel
2241*a90b9d01SCy Schubert * width
2242*a90b9d01SCy Schubert * @ies: Information elements
2243*a90b9d01SCy Schubert * @ies_len: Length of elements
2244*a90b9d01SCy Schubert * @cw: The channel width
2245*a90b9d01SCy Schubert * Returns: The max transmit power at the channel width, TX_POWER_NO_CONSTRAINT
2246*a90b9d01SCy Schubert * if it is not constrained.
2247*a90b9d01SCy Schubert *
2248*a90b9d01SCy Schubert * This function is only used to estimate the actual signal RSSI when associated
2249*a90b9d01SCy Schubert * based on the beacon RSSI at the STA. Beacon frames are transmitted on 20 MHz
2250*a90b9d01SCy Schubert * channels, while the Data frames usually use higher channel width. Therefore
2251*a90b9d01SCy Schubert * their RSSIs may be different. Assuming there is a fixed gap between the TX
2252*a90b9d01SCy Schubert * power limit of the STA defined by the Transmit Power Envelope element and the
2253*a90b9d01SCy Schubert * TX power of the AP, the difference in the TX power of X MHz and Y MHz at the
2254*a90b9d01SCy Schubert * STA equals to the difference at the AP, and the difference in the signal RSSI
2255*a90b9d01SCy Schubert * at the STA. tx_pwr is a floating point number in the standard, but the error
2256*a90b9d01SCy Schubert * of casting to int is trivial in comparing two BSSes.
2257*a90b9d01SCy Schubert */
wpas_channel_width_tx_pwr(const u8 * ies,size_t ies_len,enum chan_width cw)2258*a90b9d01SCy Schubert static int wpas_channel_width_tx_pwr(const u8 *ies, size_t ies_len,
2259*a90b9d01SCy Schubert enum chan_width cw)
2260*a90b9d01SCy Schubert {
2261*a90b9d01SCy Schubert int offset = wpas_channel_width_offset(cw);
2262*a90b9d01SCy Schubert const struct element *elem;
2263*a90b9d01SCy Schubert int max_tx_power = TX_POWER_NO_CONSTRAINT, tx_pwr = 0;
2264*a90b9d01SCy Schubert
2265*a90b9d01SCy Schubert for_each_element_id(elem, WLAN_EID_TRANSMIT_POWER_ENVELOPE, ies,
2266*a90b9d01SCy Schubert ies_len) {
2267*a90b9d01SCy Schubert int max_tx_pwr_count;
2268*a90b9d01SCy Schubert enum max_tx_pwr_interpretation tx_pwr_intrpn;
2269*a90b9d01SCy Schubert enum reg_6g_client_type client_type;
2270*a90b9d01SCy Schubert
2271*a90b9d01SCy Schubert if (elem->datalen < 1)
2272*a90b9d01SCy Schubert continue;
2273*a90b9d01SCy Schubert
2274*a90b9d01SCy Schubert /*
2275*a90b9d01SCy Schubert * IEEE Std 802.11ax-2021, 9.4.2.161 (Transmit Power Envelope
2276*a90b9d01SCy Schubert * element) defines Maximum Transmit Power Count (B0-B2),
2277*a90b9d01SCy Schubert * Maximum Transmit Power Interpretation (B3-B5), and Maximum
2278*a90b9d01SCy Schubert * Transmit Power Category (B6-B7).
2279*a90b9d01SCy Schubert */
2280*a90b9d01SCy Schubert max_tx_pwr_count = elem->data[0] & 0x07;
2281*a90b9d01SCy Schubert tx_pwr_intrpn = (elem->data[0] >> 3) & 0x07;
2282*a90b9d01SCy Schubert client_type = (elem->data[0] >> 6) & 0x03;
2283*a90b9d01SCy Schubert
2284*a90b9d01SCy Schubert if (client_type != REG_DEFAULT_CLIENT)
2285*a90b9d01SCy Schubert continue;
2286*a90b9d01SCy Schubert
2287*a90b9d01SCy Schubert if (tx_pwr_intrpn == LOCAL_EIRP ||
2288*a90b9d01SCy Schubert tx_pwr_intrpn == REGULATORY_CLIENT_EIRP) {
2289*a90b9d01SCy Schubert int offs;
2290*a90b9d01SCy Schubert
2291*a90b9d01SCy Schubert max_tx_pwr_count = MIN(max_tx_pwr_count, 3);
2292*a90b9d01SCy Schubert offs = MIN(offset, max_tx_pwr_count) + 1;
2293*a90b9d01SCy Schubert if (elem->datalen <= offs)
2294*a90b9d01SCy Schubert continue;
2295*a90b9d01SCy Schubert tx_pwr = (signed char) elem->data[offs];
2296*a90b9d01SCy Schubert /*
2297*a90b9d01SCy Schubert * Maximum Transmit Power subfield is encoded as an
2298*a90b9d01SCy Schubert * 8-bit 2s complement signed integer in the range -64
2299*a90b9d01SCy Schubert * dBm to 63 dBm with a 0.5 dB step. 63.5 dBm means no
2300*a90b9d01SCy Schubert * local maximum transmit power constraint.
2301*a90b9d01SCy Schubert */
2302*a90b9d01SCy Schubert if (tx_pwr == 127)
2303*a90b9d01SCy Schubert continue;
2304*a90b9d01SCy Schubert tx_pwr /= 2;
2305*a90b9d01SCy Schubert max_tx_power = MIN(max_tx_power, tx_pwr);
2306*a90b9d01SCy Schubert } else if (tx_pwr_intrpn == LOCAL_EIRP_PSD ||
2307*a90b9d01SCy Schubert tx_pwr_intrpn == REGULATORY_CLIENT_EIRP_PSD) {
2308*a90b9d01SCy Schubert if (elem->datalen < 2)
2309*a90b9d01SCy Schubert continue;
2310*a90b9d01SCy Schubert
2311*a90b9d01SCy Schubert tx_pwr = (signed char) elem->data[1];
2312*a90b9d01SCy Schubert /*
2313*a90b9d01SCy Schubert * Maximum Transmit PSD subfield is encoded as an 8-bit
2314*a90b9d01SCy Schubert * 2s complement signed integer. -128 indicates that the
2315*a90b9d01SCy Schubert * corresponding 20 MHz channel cannot be used for
2316*a90b9d01SCy Schubert * transmission. +127 indicates that no maximum PSD
2317*a90b9d01SCy Schubert * limit is specified for the corresponding 20 MHz
2318*a90b9d01SCy Schubert * channel.
2319*a90b9d01SCy Schubert */
2320*a90b9d01SCy Schubert if (tx_pwr == 127 || tx_pwr == -128)
2321*a90b9d01SCy Schubert continue;
2322*a90b9d01SCy Schubert
2323*a90b9d01SCy Schubert /*
2324*a90b9d01SCy Schubert * The Maximum Transmit PSD subfield indicates the
2325*a90b9d01SCy Schubert * maximum transmit PSD for the 20 MHz channel. Suppose
2326*a90b9d01SCy Schubert * the PSD value is X dBm/MHz, the TX power of N MHz is
2327*a90b9d01SCy Schubert * X + 10*log10(N) = X + 10*log10(20) + 10*log10(N/20) =
2328*a90b9d01SCy Schubert * X + 13 + 3*log2(N/20)
2329*a90b9d01SCy Schubert */
2330*a90b9d01SCy Schubert tx_pwr = tx_pwr / 2 + 13 + offset * 3;
2331*a90b9d01SCy Schubert max_tx_power = MIN(max_tx_power, tx_pwr);
2332*a90b9d01SCy Schubert }
2333*a90b9d01SCy Schubert }
2334*a90b9d01SCy Schubert
2335*a90b9d01SCy Schubert return max_tx_power;
2336*a90b9d01SCy Schubert }
2337*a90b9d01SCy Schubert
2338*a90b9d01SCy Schubert
2339*a90b9d01SCy Schubert /**
2340*a90b9d01SCy Schubert * Estimate the RSSI bump of channel width |cw| with respect to 20 MHz channel.
2341*a90b9d01SCy Schubert * If the TX power has no constraint, it is unable to estimate the RSSI bump.
2342*a90b9d01SCy Schubert */
wpas_channel_width_rssi_bump(const u8 * ies,size_t ies_len,enum chan_width cw)2343*a90b9d01SCy Schubert int wpas_channel_width_rssi_bump(const u8 *ies, size_t ies_len,
2344*a90b9d01SCy Schubert enum chan_width cw)
2345*a90b9d01SCy Schubert {
2346*a90b9d01SCy Schubert int max_20mhz_tx_pwr = wpas_channel_width_tx_pwr(ies, ies_len,
2347*a90b9d01SCy Schubert CHAN_WIDTH_20);
2348*a90b9d01SCy Schubert int max_cw_tx_pwr = wpas_channel_width_tx_pwr(ies, ies_len, cw);
2349*a90b9d01SCy Schubert
2350*a90b9d01SCy Schubert return (max_20mhz_tx_pwr == TX_POWER_NO_CONSTRAINT ||
2351*a90b9d01SCy Schubert max_cw_tx_pwr == TX_POWER_NO_CONSTRAINT) ?
2352*a90b9d01SCy Schubert 0 : (max_cw_tx_pwr - max_20mhz_tx_pwr);
2353*a90b9d01SCy Schubert }
2354*a90b9d01SCy Schubert
2355*a90b9d01SCy Schubert
wpas_adjust_snr_by_chanwidth(const u8 * ies,size_t ies_len,enum chan_width max_cw,int snr)2356*a90b9d01SCy Schubert int wpas_adjust_snr_by_chanwidth(const u8 *ies, size_t ies_len,
2357*a90b9d01SCy Schubert enum chan_width max_cw, int snr)
2358*a90b9d01SCy Schubert {
2359*a90b9d01SCy Schubert int rssi_bump = wpas_channel_width_rssi_bump(ies, ies_len, max_cw);
2360*a90b9d01SCy Schubert /*
2361*a90b9d01SCy Schubert * The noise has uniform power spectral density (PSD) across the
2362*a90b9d01SCy Schubert * frequency band, its power is proportional to the channel width.
2363*a90b9d01SCy Schubert * Suppose the PSD of noise is X dBm/MHz, the noise power of N MHz is
2364*a90b9d01SCy Schubert * X + 10*log10(N), and the noise power bump with respect to 20 MHz is
2365*a90b9d01SCy Schubert * 10*log10(N) - 10*log10(20) = 10*log10(N/20) = 3*log2(N/20)
2366*a90b9d01SCy Schubert */
2367*a90b9d01SCy Schubert int noise_bump = 3 * wpas_channel_width_offset(max_cw);
2368*a90b9d01SCy Schubert
2369*a90b9d01SCy Schubert return snr + rssi_bump - noise_bump;
2370*a90b9d01SCy Schubert }
2371*a90b9d01SCy Schubert
2372*a90b9d01SCy Schubert
2373e28a4053SRui Paulo /* Compare function for sorting scan results. Return >0 if @b is considered
2374e28a4053SRui Paulo * better. */
wpa_scan_result_compar(const void * a,const void * b)2375e28a4053SRui Paulo static int wpa_scan_result_compar(const void *a, const void *b)
2376e28a4053SRui Paulo {
2377e28a4053SRui Paulo struct wpa_scan_res **_wa = (void *) a;
2378e28a4053SRui Paulo struct wpa_scan_res **_wb = (void *) b;
2379e28a4053SRui Paulo struct wpa_scan_res *wa = *_wa;
2380e28a4053SRui Paulo struct wpa_scan_res *wb = *_wb;
23815b9c547cSRui Paulo int wpa_a, wpa_b;
23825b9c547cSRui Paulo int snr_a, snr_b, snr_a_full, snr_b_full;
2383*a90b9d01SCy Schubert size_t ies_len;
2384*a90b9d01SCy Schubert const u8 *rsne_a, *rsne_b;
2385e28a4053SRui Paulo
2386e28a4053SRui Paulo /* WPA/WPA2 support preferred */
2387e28a4053SRui Paulo wpa_a = wpa_scan_get_vendor_ie(wa, WPA_IE_VENDOR_TYPE) != NULL ||
2388e28a4053SRui Paulo wpa_scan_get_ie(wa, WLAN_EID_RSN) != NULL;
2389e28a4053SRui Paulo wpa_b = wpa_scan_get_vendor_ie(wb, WPA_IE_VENDOR_TYPE) != NULL ||
2390e28a4053SRui Paulo wpa_scan_get_ie(wb, WLAN_EID_RSN) != NULL;
2391e28a4053SRui Paulo
2392e28a4053SRui Paulo if (wpa_b && !wpa_a)
2393e28a4053SRui Paulo return 1;
2394e28a4053SRui Paulo if (!wpa_b && wpa_a)
2395e28a4053SRui Paulo return -1;
2396e28a4053SRui Paulo
2397e28a4053SRui Paulo /* privacy support preferred */
2398e28a4053SRui Paulo if ((wa->caps & IEEE80211_CAP_PRIVACY) == 0 &&
2399e28a4053SRui Paulo (wb->caps & IEEE80211_CAP_PRIVACY))
2400e28a4053SRui Paulo return 1;
2401e28a4053SRui Paulo if ((wa->caps & IEEE80211_CAP_PRIVACY) &&
2402e28a4053SRui Paulo (wb->caps & IEEE80211_CAP_PRIVACY) == 0)
2403e28a4053SRui Paulo return -1;
2404e28a4053SRui Paulo
24055b9c547cSRui Paulo if (wa->flags & wb->flags & WPA_SCAN_LEVEL_DBM) {
2406*a90b9d01SCy Schubert /*
2407*a90b9d01SCy Schubert * The scan result estimates SNR over 20 MHz, while Data frames
2408*a90b9d01SCy Schubert * usually use wider channel width. The TX power and noise power
2409*a90b9d01SCy Schubert * are both affected by the channel width.
2410*a90b9d01SCy Schubert */
2411*a90b9d01SCy Schubert ies_len = wa->ie_len ? wa->ie_len : wa->beacon_ie_len;
2412*a90b9d01SCy Schubert snr_a_full = wpas_adjust_snr_by_chanwidth((const u8 *) (wa + 1),
2413*a90b9d01SCy Schubert ies_len, wa->max_cw,
2414*a90b9d01SCy Schubert wa->snr);
2415*a90b9d01SCy Schubert snr_a = MIN(snr_a_full, GREAT_SNR);
2416*a90b9d01SCy Schubert ies_len = wb->ie_len ? wb->ie_len : wb->beacon_ie_len;
2417*a90b9d01SCy Schubert snr_b_full = wpas_adjust_snr_by_chanwidth((const u8 *) (wb + 1),
2418*a90b9d01SCy Schubert ies_len, wb->max_cw,
2419*a90b9d01SCy Schubert wb->snr);
2420*a90b9d01SCy Schubert snr_b = MIN(snr_b_full, GREAT_SNR);
2421f05cddf9SRui Paulo } else {
24225b9c547cSRui Paulo /* Level is not in dBm, so we can't calculate
24235b9c547cSRui Paulo * SNR. Just use raw level (units unknown). */
24245b9c547cSRui Paulo snr_a = snr_a_full = wa->level;
24255b9c547cSRui Paulo snr_b = snr_b_full = wb->level;
2426f05cddf9SRui Paulo }
2427f05cddf9SRui Paulo
2428*a90b9d01SCy Schubert /* If SNR of a SAE BSS is good or at least as high as the PSK BSS,
2429*a90b9d01SCy Schubert * prefer SAE over PSK for mixed WPA3-Personal transition mode and
2430*a90b9d01SCy Schubert * WPA2-Personal deployments */
2431*a90b9d01SCy Schubert rsne_a = wpa_scan_get_ie(wa, WLAN_EID_RSN);
2432*a90b9d01SCy Schubert rsne_b = wpa_scan_get_ie(wb, WLAN_EID_RSN);
2433*a90b9d01SCy Schubert if (rsne_a && rsne_b) {
2434*a90b9d01SCy Schubert struct wpa_ie_data data;
2435*a90b9d01SCy Schubert bool psk_a = false, psk_b = false, sae_a = false, sae_b = false;
2436*a90b9d01SCy Schubert
2437*a90b9d01SCy Schubert if (wpa_parse_wpa_ie_rsn(rsne_a, 2 + rsne_a[1], &data) == 0) {
2438*a90b9d01SCy Schubert psk_a = wpa_key_mgmt_wpa_psk_no_sae(data.key_mgmt);
2439*a90b9d01SCy Schubert sae_a = wpa_key_mgmt_sae(data.key_mgmt);
2440*a90b9d01SCy Schubert }
2441*a90b9d01SCy Schubert if (wpa_parse_wpa_ie_rsn(rsne_b, 2 + rsne_b[1], &data) == 0) {
2442*a90b9d01SCy Schubert psk_b = wpa_key_mgmt_wpa_psk_no_sae(data.key_mgmt);
2443*a90b9d01SCy Schubert sae_b = wpa_key_mgmt_sae(data.key_mgmt);
2444*a90b9d01SCy Schubert }
2445*a90b9d01SCy Schubert
2446*a90b9d01SCy Schubert if (sae_a && !sae_b && psk_b &&
2447*a90b9d01SCy Schubert (snr_a >= GREAT_SNR || snr_a >= snr_b))
2448*a90b9d01SCy Schubert return -1;
2449*a90b9d01SCy Schubert if (sae_b && !sae_a && psk_a &&
2450*a90b9d01SCy Schubert (snr_b >= GREAT_SNR || snr_b >= snr_a))
2451*a90b9d01SCy Schubert return 1;
2452*a90b9d01SCy Schubert }
2453*a90b9d01SCy Schubert
2454c1d255d3SCy Schubert /* If SNR is close, decide by max rate or frequency band. For cases
2455c1d255d3SCy Schubert * involving the 6 GHz band, use the throughput estimate irrespective
2456c1d255d3SCy Schubert * of the SNR difference since the LPI/VLP rules may result in
2457c1d255d3SCy Schubert * significant differences in SNR for cases where the estimated
2458c1d255d3SCy Schubert * throughput can be considerably higher with the lower SNR. */
2459c1d255d3SCy Schubert if (snr_a && snr_b && (abs(snr_b - snr_a) < 7 ||
2460c1d255d3SCy Schubert is_6ghz_freq(wa->freq) ||
2461c1d255d3SCy Schubert is_6ghz_freq(wb->freq))) {
24625b9c547cSRui Paulo if (wa->est_throughput != wb->est_throughput)
24634bc52338SCy Schubert return (int) wb->est_throughput -
24644bc52338SCy Schubert (int) wa->est_throughput;
246585732ac8SCy Schubert }
246685732ac8SCy Schubert if ((snr_a && snr_b && abs(snr_b - snr_a) < 5) ||
246785732ac8SCy Schubert (wa->qual && wb->qual && abs(wb->qual - wa->qual) < 10)) {
2468c1d255d3SCy Schubert if (is_6ghz_freq(wa->freq) ^ is_6ghz_freq(wb->freq))
2469c1d255d3SCy Schubert return is_6ghz_freq(wa->freq) ? -1 : 1;
2470f05cddf9SRui Paulo if (IS_5GHZ(wa->freq) ^ IS_5GHZ(wb->freq))
2471f05cddf9SRui Paulo return IS_5GHZ(wa->freq) ? -1 : 1;
2472e28a4053SRui Paulo }
2473e28a4053SRui Paulo
2474f05cddf9SRui Paulo /* all things being equal, use SNR; if SNRs are
2475f05cddf9SRui Paulo * identical, use quality values since some drivers may only report
2476f05cddf9SRui Paulo * that value and leave the signal level zero */
24775b9c547cSRui Paulo if (snr_b_full == snr_a_full)
2478f05cddf9SRui Paulo return wb->qual - wa->qual;
24795b9c547cSRui Paulo return snr_b_full - snr_a_full;
2480f05cddf9SRui Paulo }
2481f05cddf9SRui Paulo
2482f05cddf9SRui Paulo
2483f05cddf9SRui Paulo #ifdef CONFIG_WPS
2484f05cddf9SRui Paulo /* Compare function for sorting scan results when searching a WPS AP for
2485f05cddf9SRui Paulo * provisioning. Return >0 if @b is considered better. */
wpa_scan_result_wps_compar(const void * a,const void * b)2486f05cddf9SRui Paulo static int wpa_scan_result_wps_compar(const void *a, const void *b)
2487f05cddf9SRui Paulo {
2488f05cddf9SRui Paulo struct wpa_scan_res **_wa = (void *) a;
2489f05cddf9SRui Paulo struct wpa_scan_res **_wb = (void *) b;
2490f05cddf9SRui Paulo struct wpa_scan_res *wa = *_wa;
2491f05cddf9SRui Paulo struct wpa_scan_res *wb = *_wb;
2492f05cddf9SRui Paulo int uses_wps_a, uses_wps_b;
2493f05cddf9SRui Paulo struct wpabuf *wps_a, *wps_b;
2494f05cddf9SRui Paulo int res;
2495f05cddf9SRui Paulo
2496f05cddf9SRui Paulo /* Optimization - check WPS IE existence before allocated memory and
2497f05cddf9SRui Paulo * doing full reassembly. */
2498f05cddf9SRui Paulo uses_wps_a = wpa_scan_get_vendor_ie(wa, WPS_IE_VENDOR_TYPE) != NULL;
2499f05cddf9SRui Paulo uses_wps_b = wpa_scan_get_vendor_ie(wb, WPS_IE_VENDOR_TYPE) != NULL;
2500f05cddf9SRui Paulo if (uses_wps_a && !uses_wps_b)
2501f05cddf9SRui Paulo return -1;
2502f05cddf9SRui Paulo if (!uses_wps_a && uses_wps_b)
2503f05cddf9SRui Paulo return 1;
2504f05cddf9SRui Paulo
2505f05cddf9SRui Paulo if (uses_wps_a && uses_wps_b) {
2506f05cddf9SRui Paulo wps_a = wpa_scan_get_vendor_ie_multi(wa, WPS_IE_VENDOR_TYPE);
2507f05cddf9SRui Paulo wps_b = wpa_scan_get_vendor_ie_multi(wb, WPS_IE_VENDOR_TYPE);
2508f05cddf9SRui Paulo res = wps_ap_priority_compar(wps_a, wps_b);
2509f05cddf9SRui Paulo wpabuf_free(wps_a);
2510f05cddf9SRui Paulo wpabuf_free(wps_b);
2511f05cddf9SRui Paulo if (res)
2512f05cddf9SRui Paulo return res;
2513f05cddf9SRui Paulo }
2514f05cddf9SRui Paulo
2515f05cddf9SRui Paulo /*
2516f05cddf9SRui Paulo * Do not use current AP security policy as a sorting criteria during
2517f05cddf9SRui Paulo * WPS provisioning step since the AP may get reconfigured at the
2518f05cddf9SRui Paulo * completion of provisioning.
2519f05cddf9SRui Paulo */
2520f05cddf9SRui Paulo
2521e28a4053SRui Paulo /* all things being equal, use signal level; if signal levels are
2522e28a4053SRui Paulo * identical, use quality values since some drivers may only report
2523e28a4053SRui Paulo * that value and leave the signal level zero */
2524e28a4053SRui Paulo if (wb->level == wa->level)
2525e28a4053SRui Paulo return wb->qual - wa->qual;
2526e28a4053SRui Paulo return wb->level - wa->level;
2527e28a4053SRui Paulo }
2528f05cddf9SRui Paulo #endif /* CONFIG_WPS */
2529f05cddf9SRui Paulo
2530f05cddf9SRui Paulo
dump_scan_res(struct wpa_scan_results * scan_res)2531f05cddf9SRui Paulo static void dump_scan_res(struct wpa_scan_results *scan_res)
2532f05cddf9SRui Paulo {
2533f05cddf9SRui Paulo #ifndef CONFIG_NO_STDOUT_DEBUG
2534f05cddf9SRui Paulo size_t i;
2535f05cddf9SRui Paulo
2536f05cddf9SRui Paulo if (scan_res->res == NULL || scan_res->num == 0)
2537f05cddf9SRui Paulo return;
2538f05cddf9SRui Paulo
2539f05cddf9SRui Paulo wpa_printf(MSG_EXCESSIVE, "Sorted scan results");
2540f05cddf9SRui Paulo
2541f05cddf9SRui Paulo for (i = 0; i < scan_res->num; i++) {
2542f05cddf9SRui Paulo struct wpa_scan_res *r = scan_res->res[i];
2543f05cddf9SRui Paulo u8 *pos;
2544*a90b9d01SCy Schubert const u8 *ssid_ie, *ssid = NULL;
2545*a90b9d01SCy Schubert size_t ssid_len = 0;
2546*a90b9d01SCy Schubert
2547*a90b9d01SCy Schubert ssid_ie = wpa_scan_get_ie(r, WLAN_EID_SSID);
2548*a90b9d01SCy Schubert if (ssid_ie) {
2549*a90b9d01SCy Schubert ssid = ssid_ie + 2;
2550*a90b9d01SCy Schubert ssid_len = ssid_ie[1];
2551*a90b9d01SCy Schubert }
2552*a90b9d01SCy Schubert
25535b9c547cSRui Paulo if (r->flags & WPA_SCAN_LEVEL_DBM) {
25545b9c547cSRui Paulo int noise_valid = !(r->flags & WPA_SCAN_NOISE_INVALID);
25555b9c547cSRui Paulo
2556*a90b9d01SCy Schubert wpa_printf(MSG_EXCESSIVE, MACSTR
2557*a90b9d01SCy Schubert " ssid=%s freq=%d qual=%d noise=%d%s level=%d snr=%d%s flags=0x%x age=%u est=%u",
2558*a90b9d01SCy Schubert MAC2STR(r->bssid),
2559*a90b9d01SCy Schubert wpa_ssid_txt(ssid, ssid_len),
2560*a90b9d01SCy Schubert r->freq, r->qual,
25615b9c547cSRui Paulo r->noise, noise_valid ? "" : "~", r->level,
25625b9c547cSRui Paulo r->snr, r->snr >= GREAT_SNR ? "*" : "",
25635b9c547cSRui Paulo r->flags,
25645b9c547cSRui Paulo r->age, r->est_throughput);
2565f05cddf9SRui Paulo } else {
2566*a90b9d01SCy Schubert wpa_printf(MSG_EXCESSIVE, MACSTR
2567*a90b9d01SCy Schubert " ssid=%s freq=%d qual=%d noise=%d level=%d flags=0x%x age=%u est=%u",
2568*a90b9d01SCy Schubert MAC2STR(r->bssid),
2569*a90b9d01SCy Schubert wpa_ssid_txt(ssid, ssid_len),
2570*a90b9d01SCy Schubert r->freq, r->qual,
25715b9c547cSRui Paulo r->noise, r->level, r->flags, r->age,
25725b9c547cSRui Paulo r->est_throughput);
2573f05cddf9SRui Paulo }
2574f05cddf9SRui Paulo pos = (u8 *) (r + 1);
2575f05cddf9SRui Paulo if (r->ie_len)
2576f05cddf9SRui Paulo wpa_hexdump(MSG_EXCESSIVE, "IEs", pos, r->ie_len);
2577f05cddf9SRui Paulo pos += r->ie_len;
2578f05cddf9SRui Paulo if (r->beacon_ie_len)
2579f05cddf9SRui Paulo wpa_hexdump(MSG_EXCESSIVE, "Beacon IEs",
2580f05cddf9SRui Paulo pos, r->beacon_ie_len);
2581f05cddf9SRui Paulo }
2582f05cddf9SRui Paulo #endif /* CONFIG_NO_STDOUT_DEBUG */
2583f05cddf9SRui Paulo }
2584f05cddf9SRui Paulo
2585f05cddf9SRui Paulo
2586f05cddf9SRui Paulo /**
2587f05cddf9SRui Paulo * wpa_supplicant_filter_bssid_match - Is the specified BSSID allowed
2588f05cddf9SRui Paulo * @wpa_s: Pointer to wpa_supplicant data
2589f05cddf9SRui Paulo * @bssid: BSSID to check
2590f05cddf9SRui Paulo * Returns: 0 if the BSSID is filtered or 1 if not
2591f05cddf9SRui Paulo *
2592f05cddf9SRui Paulo * This function is used to filter out specific BSSIDs from scan reslts mainly
2593f05cddf9SRui Paulo * for testing purposes (SET bssid_filter ctrl_iface command).
2594f05cddf9SRui Paulo */
wpa_supplicant_filter_bssid_match(struct wpa_supplicant * wpa_s,const u8 * bssid)2595f05cddf9SRui Paulo int wpa_supplicant_filter_bssid_match(struct wpa_supplicant *wpa_s,
2596f05cddf9SRui Paulo const u8 *bssid)
2597f05cddf9SRui Paulo {
2598f05cddf9SRui Paulo size_t i;
2599f05cddf9SRui Paulo
2600f05cddf9SRui Paulo if (wpa_s->bssid_filter == NULL)
2601f05cddf9SRui Paulo return 1;
2602f05cddf9SRui Paulo
2603f05cddf9SRui Paulo for (i = 0; i < wpa_s->bssid_filter_count; i++) {
2604*a90b9d01SCy Schubert if (ether_addr_equal(wpa_s->bssid_filter + i * ETH_ALEN, bssid))
2605f05cddf9SRui Paulo return 1;
2606f05cddf9SRui Paulo }
2607f05cddf9SRui Paulo
2608f05cddf9SRui Paulo return 0;
2609f05cddf9SRui Paulo }
2610f05cddf9SRui Paulo
2611f05cddf9SRui Paulo
filter_scan_res(struct wpa_supplicant * wpa_s,struct wpa_scan_results * res)2612*a90b9d01SCy Schubert static void filter_scan_res(struct wpa_supplicant *wpa_s,
2613f05cddf9SRui Paulo struct wpa_scan_results *res)
2614f05cddf9SRui Paulo {
2615f05cddf9SRui Paulo size_t i, j;
2616f05cddf9SRui Paulo
2617f05cddf9SRui Paulo if (wpa_s->bssid_filter == NULL)
2618f05cddf9SRui Paulo return;
2619f05cddf9SRui Paulo
2620f05cddf9SRui Paulo for (i = 0, j = 0; i < res->num; i++) {
2621f05cddf9SRui Paulo if (wpa_supplicant_filter_bssid_match(wpa_s,
2622f05cddf9SRui Paulo res->res[i]->bssid)) {
2623f05cddf9SRui Paulo res->res[j++] = res->res[i];
2624f05cddf9SRui Paulo } else {
2625f05cddf9SRui Paulo os_free(res->res[i]);
2626f05cddf9SRui Paulo res->res[i] = NULL;
2627f05cddf9SRui Paulo }
2628f05cddf9SRui Paulo }
2629f05cddf9SRui Paulo
2630f05cddf9SRui Paulo if (res->num != j) {
2631f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "Filtered out %d scan results",
2632f05cddf9SRui Paulo (int) (res->num - j));
2633f05cddf9SRui Paulo res->num = j;
2634f05cddf9SRui Paulo }
2635f05cddf9SRui Paulo }
2636e28a4053SRui Paulo
2637e28a4053SRui Paulo
scan_snr(struct wpa_scan_res * res)2638780fb4a2SCy Schubert void scan_snr(struct wpa_scan_res *res)
26395b9c547cSRui Paulo {
26405b9c547cSRui Paulo if (res->flags & WPA_SCAN_NOISE_INVALID) {
2641c1d255d3SCy Schubert res->noise = is_6ghz_freq(res->freq) ?
2642c1d255d3SCy Schubert DEFAULT_NOISE_FLOOR_6GHZ :
2643c1d255d3SCy Schubert (IS_5GHZ(res->freq) ?
2644c1d255d3SCy Schubert DEFAULT_NOISE_FLOOR_5GHZ : DEFAULT_NOISE_FLOOR_2GHZ);
26455b9c547cSRui Paulo }
26465b9c547cSRui Paulo
26475b9c547cSRui Paulo if (res->flags & WPA_SCAN_LEVEL_DBM) {
26485b9c547cSRui Paulo res->snr = res->level - res->noise;
26495b9c547cSRui Paulo } else {
26505b9c547cSRui Paulo /* Level is not in dBm, so we can't calculate
26515b9c547cSRui Paulo * SNR. Just use raw level (units unknown). */
26525b9c547cSRui Paulo res->snr = res->level;
26535b9c547cSRui Paulo }
26545b9c547cSRui Paulo }
26555b9c547cSRui Paulo
26565b9c547cSRui Paulo
2657c1d255d3SCy Schubert /* Minimum SNR required to achieve a certain bitrate. */
2658c1d255d3SCy Schubert struct minsnr_bitrate_entry {
2659c1d255d3SCy Schubert int minsnr;
2660c1d255d3SCy Schubert unsigned int bitrate; /* in Mbps */
2661c1d255d3SCy Schubert };
2662c1d255d3SCy Schubert
2663c1d255d3SCy Schubert /* VHT needs to be enabled in order to achieve MCS8 and MCS9 rates. */
2664c1d255d3SCy Schubert static const int vht_mcs = 8;
2665c1d255d3SCy Schubert
2666c1d255d3SCy Schubert static const struct minsnr_bitrate_entry vht20_table[] = {
2667c1d255d3SCy Schubert { 0, 0 },
2668c1d255d3SCy Schubert { 2, 6500 }, /* HT20 MCS0 */
2669c1d255d3SCy Schubert { 5, 13000 }, /* HT20 MCS1 */
2670c1d255d3SCy Schubert { 9, 19500 }, /* HT20 MCS2 */
2671c1d255d3SCy Schubert { 11, 26000 }, /* HT20 MCS3 */
2672c1d255d3SCy Schubert { 15, 39000 }, /* HT20 MCS4 */
2673c1d255d3SCy Schubert { 18, 52000 }, /* HT20 MCS5 */
2674c1d255d3SCy Schubert { 20, 58500 }, /* HT20 MCS6 */
2675c1d255d3SCy Schubert { 25, 65000 }, /* HT20 MCS7 */
2676c1d255d3SCy Schubert { 29, 78000 }, /* VHT20 MCS8 */
2677c1d255d3SCy Schubert { -1, 78000 } /* SNR > 29 */
2678c1d255d3SCy Schubert };
2679c1d255d3SCy Schubert
2680c1d255d3SCy Schubert static const struct minsnr_bitrate_entry vht40_table[] = {
2681c1d255d3SCy Schubert { 0, 0 },
2682c1d255d3SCy Schubert { 5, 13500 }, /* HT40 MCS0 */
2683c1d255d3SCy Schubert { 8, 27000 }, /* HT40 MCS1 */
2684c1d255d3SCy Schubert { 12, 40500 }, /* HT40 MCS2 */
2685c1d255d3SCy Schubert { 14, 54000 }, /* HT40 MCS3 */
2686c1d255d3SCy Schubert { 18, 81000 }, /* HT40 MCS4 */
2687c1d255d3SCy Schubert { 21, 108000 }, /* HT40 MCS5 */
2688c1d255d3SCy Schubert { 23, 121500 }, /* HT40 MCS6 */
2689c1d255d3SCy Schubert { 28, 135000 }, /* HT40 MCS7 */
2690c1d255d3SCy Schubert { 32, 162000 }, /* VHT40 MCS8 */
2691c1d255d3SCy Schubert { 34, 180000 }, /* VHT40 MCS9 */
2692c1d255d3SCy Schubert { -1, 180000 } /* SNR > 34 */
2693c1d255d3SCy Schubert };
2694c1d255d3SCy Schubert
2695c1d255d3SCy Schubert static const struct minsnr_bitrate_entry vht80_table[] = {
2696c1d255d3SCy Schubert { 0, 0 },
2697c1d255d3SCy Schubert { 8, 29300 }, /* VHT80 MCS0 */
2698c1d255d3SCy Schubert { 11, 58500 }, /* VHT80 MCS1 */
2699c1d255d3SCy Schubert { 15, 87800 }, /* VHT80 MCS2 */
2700c1d255d3SCy Schubert { 17, 117000 }, /* VHT80 MCS3 */
2701c1d255d3SCy Schubert { 21, 175500 }, /* VHT80 MCS4 */
2702c1d255d3SCy Schubert { 24, 234000 }, /* VHT80 MCS5 */
2703c1d255d3SCy Schubert { 26, 263300 }, /* VHT80 MCS6 */
2704c1d255d3SCy Schubert { 31, 292500 }, /* VHT80 MCS7 */
2705c1d255d3SCy Schubert { 35, 351000 }, /* VHT80 MCS8 */
2706c1d255d3SCy Schubert { 37, 390000 }, /* VHT80 MCS9 */
2707c1d255d3SCy Schubert { -1, 390000 } /* SNR > 37 */
2708c1d255d3SCy Schubert };
2709c1d255d3SCy Schubert
2710c1d255d3SCy Schubert
2711c1d255d3SCy Schubert static const struct minsnr_bitrate_entry vht160_table[] = {
2712c1d255d3SCy Schubert { 0, 0 },
2713c1d255d3SCy Schubert { 11, 58500 }, /* VHT160 MCS0 */
2714c1d255d3SCy Schubert { 14, 117000 }, /* VHT160 MCS1 */
2715c1d255d3SCy Schubert { 18, 175500 }, /* VHT160 MCS2 */
2716c1d255d3SCy Schubert { 20, 234000 }, /* VHT160 MCS3 */
2717c1d255d3SCy Schubert { 24, 351000 }, /* VHT160 MCS4 */
2718c1d255d3SCy Schubert { 27, 468000 }, /* VHT160 MCS5 */
2719c1d255d3SCy Schubert { 29, 526500 }, /* VHT160 MCS6 */
2720c1d255d3SCy Schubert { 34, 585000 }, /* VHT160 MCS7 */
2721c1d255d3SCy Schubert { 38, 702000 }, /* VHT160 MCS8 */
2722c1d255d3SCy Schubert { 40, 780000 }, /* VHT160 MCS9 */
2723c1d255d3SCy Schubert { -1, 780000 } /* SNR > 37 */
2724c1d255d3SCy Schubert };
2725c1d255d3SCy Schubert
2726*a90b9d01SCy Schubert /* EHT needs to be enabled in order to achieve MCS12 and MCS13 rates. */
2727*a90b9d01SCy Schubert #define EHT_MCS 12
2728c1d255d3SCy Schubert
2729c1d255d3SCy Schubert static const struct minsnr_bitrate_entry he20_table[] = {
2730c1d255d3SCy Schubert { 0, 0 },
2731c1d255d3SCy Schubert { 2, 8600 }, /* HE20 MCS0 */
2732c1d255d3SCy Schubert { 5, 17200 }, /* HE20 MCS1 */
2733c1d255d3SCy Schubert { 9, 25800 }, /* HE20 MCS2 */
2734c1d255d3SCy Schubert { 11, 34400 }, /* HE20 MCS3 */
2735c1d255d3SCy Schubert { 15, 51600 }, /* HE20 MCS4 */
2736c1d255d3SCy Schubert { 18, 68800 }, /* HE20 MCS5 */
2737c1d255d3SCy Schubert { 20, 77400 }, /* HE20 MCS6 */
2738c1d255d3SCy Schubert { 25, 86000 }, /* HE20 MCS7 */
2739c1d255d3SCy Schubert { 29, 103200 }, /* HE20 MCS8 */
2740c1d255d3SCy Schubert { 31, 114700 }, /* HE20 MCS9 */
2741c1d255d3SCy Schubert { 34, 129000 }, /* HE20 MCS10 */
2742c1d255d3SCy Schubert { 36, 143400 }, /* HE20 MCS11 */
2743*a90b9d01SCy Schubert { 39, 154900 }, /* EHT20 MCS12 */
2744*a90b9d01SCy Schubert { 42, 172100 }, /* EHT20 MCS13 */
2745*a90b9d01SCy Schubert { -1, 172100 } /* SNR > 42 */
2746c1d255d3SCy Schubert };
2747c1d255d3SCy Schubert
2748c1d255d3SCy Schubert static const struct minsnr_bitrate_entry he40_table[] = {
2749c1d255d3SCy Schubert { 0, 0 },
2750c1d255d3SCy Schubert { 5, 17200 }, /* HE40 MCS0 */
2751c1d255d3SCy Schubert { 8, 34400 }, /* HE40 MCS1 */
2752c1d255d3SCy Schubert { 12, 51600 }, /* HE40 MCS2 */
2753c1d255d3SCy Schubert { 14, 68800 }, /* HE40 MCS3 */
2754c1d255d3SCy Schubert { 18, 103200 }, /* HE40 MCS4 */
2755c1d255d3SCy Schubert { 21, 137600 }, /* HE40 MCS5 */
2756c1d255d3SCy Schubert { 23, 154900 }, /* HE40 MCS6 */
2757c1d255d3SCy Schubert { 28, 172100 }, /* HE40 MCS7 */
2758c1d255d3SCy Schubert { 32, 206500 }, /* HE40 MCS8 */
2759c1d255d3SCy Schubert { 34, 229400 }, /* HE40 MCS9 */
2760c1d255d3SCy Schubert { 37, 258100 }, /* HE40 MCS10 */
2761c1d255d3SCy Schubert { 39, 286800 }, /* HE40 MCS11 */
2762*a90b9d01SCy Schubert { 42, 309500 }, /* EHT40 MCS12 */
2763*a90b9d01SCy Schubert { 45, 344100 }, /* EHT40 MCS13 */
2764*a90b9d01SCy Schubert { -1, 344100 } /* SNR > 45 */
2765c1d255d3SCy Schubert };
2766c1d255d3SCy Schubert
2767c1d255d3SCy Schubert static const struct minsnr_bitrate_entry he80_table[] = {
2768c1d255d3SCy Schubert { 0, 0 },
2769c1d255d3SCy Schubert { 8, 36000 }, /* HE80 MCS0 */
2770c1d255d3SCy Schubert { 11, 72100 }, /* HE80 MCS1 */
2771c1d255d3SCy Schubert { 15, 108100 }, /* HE80 MCS2 */
2772c1d255d3SCy Schubert { 17, 144100 }, /* HE80 MCS3 */
2773c1d255d3SCy Schubert { 21, 216200 }, /* HE80 MCS4 */
2774c1d255d3SCy Schubert { 24, 288200 }, /* HE80 MCS5 */
2775c1d255d3SCy Schubert { 26, 324300 }, /* HE80 MCS6 */
2776c1d255d3SCy Schubert { 31, 360300 }, /* HE80 MCS7 */
2777c1d255d3SCy Schubert { 35, 432400 }, /* HE80 MCS8 */
2778c1d255d3SCy Schubert { 37, 480400 }, /* HE80 MCS9 */
2779c1d255d3SCy Schubert { 40, 540400 }, /* HE80 MCS10 */
2780c1d255d3SCy Schubert { 42, 600500 }, /* HE80 MCS11 */
2781*a90b9d01SCy Schubert { 45, 648500 }, /* EHT80 MCS12 */
2782*a90b9d01SCy Schubert { 48, 720600 }, /* EHT80 MCS13 */
2783*a90b9d01SCy Schubert { -1, 720600 } /* SNR > 48 */
2784c1d255d3SCy Schubert };
2785c1d255d3SCy Schubert
2786c1d255d3SCy Schubert
2787c1d255d3SCy Schubert static const struct minsnr_bitrate_entry he160_table[] = {
2788c1d255d3SCy Schubert { 0, 0 },
2789c1d255d3SCy Schubert { 11, 72100 }, /* HE160 MCS0 */
2790c1d255d3SCy Schubert { 14, 144100 }, /* HE160 MCS1 */
2791c1d255d3SCy Schubert { 18, 216200 }, /* HE160 MCS2 */
2792c1d255d3SCy Schubert { 20, 288200 }, /* HE160 MCS3 */
2793c1d255d3SCy Schubert { 24, 432400 }, /* HE160 MCS4 */
2794c1d255d3SCy Schubert { 27, 576500 }, /* HE160 MCS5 */
2795c1d255d3SCy Schubert { 29, 648500 }, /* HE160 MCS6 */
2796c1d255d3SCy Schubert { 34, 720600 }, /* HE160 MCS7 */
2797c1d255d3SCy Schubert { 38, 864700 }, /* HE160 MCS8 */
2798c1d255d3SCy Schubert { 40, 960800 }, /* HE160 MCS9 */
2799c1d255d3SCy Schubert { 43, 1080900 }, /* HE160 MCS10 */
2800c1d255d3SCy Schubert { 45, 1201000 }, /* HE160 MCS11 */
2801*a90b9d01SCy Schubert { 48, 1297100 }, /* EHT160 MCS12 */
2802*a90b9d01SCy Schubert { 51, 1441200 }, /* EHT160 MCS13 */
2803*a90b9d01SCy Schubert { -1, 1441200 } /* SNR > 51 */
2804c1d255d3SCy Schubert };
2805c1d255d3SCy Schubert
2806*a90b9d01SCy Schubert /* See IEEE P802.11be/D2.0, Table 36-86: EHT-MCSs for 4x996-tone RU, NSS,u = 1
2807*a90b9d01SCy Schubert */
2808*a90b9d01SCy Schubert static const struct minsnr_bitrate_entry eht320_table[] = {
2809*a90b9d01SCy Schubert { 0, 0 },
2810*a90b9d01SCy Schubert { 14, 144100 }, /* EHT320 MCS0 */
2811*a90b9d01SCy Schubert { 17, 288200 }, /* EHT320 MCS1 */
2812*a90b9d01SCy Schubert { 21, 432400 }, /* EHT320 MCS2 */
2813*a90b9d01SCy Schubert { 23, 576500 }, /* EHT320 MCS3 */
2814*a90b9d01SCy Schubert { 27, 864700 }, /* EHT320 MCS4 */
2815*a90b9d01SCy Schubert { 30, 1152900 }, /* EHT320 MCS5 */
2816*a90b9d01SCy Schubert { 32, 1297100 }, /* EHT320 MCS6 */
2817*a90b9d01SCy Schubert { 37, 1441200 }, /* EHT320 MCS7 */
2818*a90b9d01SCy Schubert { 41, 1729400 }, /* EHT320 MCS8 */
2819*a90b9d01SCy Schubert { 43, 1921500 }, /* EHT320 MCS9 */
2820*a90b9d01SCy Schubert { 46, 2161800 }, /* EHT320 MCS10 */
2821*a90b9d01SCy Schubert { 48, 2401900 }, /* EHT320 MCS11 */
2822*a90b9d01SCy Schubert { 51, 2594100 }, /* EHT320 MCS12 */
2823*a90b9d01SCy Schubert { 54, 2882400 }, /* EHT320 MCS13 */
2824*a90b9d01SCy Schubert { -1, 2882400 } /* SNR > 54 */
2825*a90b9d01SCy Schubert };
2826c1d255d3SCy Schubert
interpolate_rate(int snr,int snr0,int snr1,int rate0,int rate1)2827c1d255d3SCy Schubert static unsigned int interpolate_rate(int snr, int snr0, int snr1,
2828c1d255d3SCy Schubert int rate0, int rate1)
28295b9c547cSRui Paulo {
2830c1d255d3SCy Schubert return rate0 + (snr - snr0) * (rate1 - rate0) / (snr1 - snr0);
28315b9c547cSRui Paulo }
28325b9c547cSRui Paulo
28335b9c547cSRui Paulo
max_rate(const struct minsnr_bitrate_entry table[],int snr,bool vht)2834c1d255d3SCy Schubert static unsigned int max_rate(const struct minsnr_bitrate_entry table[],
2835c1d255d3SCy Schubert int snr, bool vht)
28365b9c547cSRui Paulo {
2837c1d255d3SCy Schubert const struct minsnr_bitrate_entry *prev, *entry = table;
2838c1d255d3SCy Schubert
2839c1d255d3SCy Schubert while ((entry->minsnr != -1) &&
2840c1d255d3SCy Schubert (snr >= entry->minsnr) &&
2841c1d255d3SCy Schubert (vht || entry - table <= vht_mcs))
2842c1d255d3SCy Schubert entry++;
2843c1d255d3SCy Schubert if (entry == table)
2844c1d255d3SCy Schubert return entry->bitrate;
2845c1d255d3SCy Schubert prev = entry - 1;
2846c1d255d3SCy Schubert if (entry->minsnr == -1 || (!vht && entry - table > vht_mcs))
2847c1d255d3SCy Schubert return prev->bitrate;
2848c1d255d3SCy Schubert return interpolate_rate(snr, prev->minsnr, entry->minsnr, prev->bitrate,
2849c1d255d3SCy Schubert entry->bitrate);
2850c1d255d3SCy Schubert }
2851c1d255d3SCy Schubert
2852c1d255d3SCy Schubert
max_ht20_rate(int snr,bool vht)2853c1d255d3SCy Schubert static unsigned int max_ht20_rate(int snr, bool vht)
2854c1d255d3SCy Schubert {
2855c1d255d3SCy Schubert return max_rate(vht20_table, snr, vht);
2856c1d255d3SCy Schubert }
2857c1d255d3SCy Schubert
2858c1d255d3SCy Schubert
max_ht40_rate(int snr,bool vht)2859c1d255d3SCy Schubert static unsigned int max_ht40_rate(int snr, bool vht)
2860c1d255d3SCy Schubert {
2861c1d255d3SCy Schubert return max_rate(vht40_table, snr, vht);
28625b9c547cSRui Paulo }
28635b9c547cSRui Paulo
28645b9c547cSRui Paulo
max_vht80_rate(int snr)28655b9c547cSRui Paulo static unsigned int max_vht80_rate(int snr)
28665b9c547cSRui Paulo {
2867c1d255d3SCy Schubert return max_rate(vht80_table, snr, 1);
28685b9c547cSRui Paulo }
28695b9c547cSRui Paulo
28705b9c547cSRui Paulo
max_vht160_rate(int snr)2871c1d255d3SCy Schubert static unsigned int max_vht160_rate(int snr)
28725b9c547cSRui Paulo {
2873c1d255d3SCy Schubert return max_rate(vht160_table, snr, 1);
2874c1d255d3SCy Schubert }
2875c1d255d3SCy Schubert
2876c1d255d3SCy Schubert
max_he_eht_rate(const struct minsnr_bitrate_entry table[],int snr,bool eht)2877*a90b9d01SCy Schubert static unsigned int max_he_eht_rate(const struct minsnr_bitrate_entry table[],
2878*a90b9d01SCy Schubert int snr, bool eht)
2879c1d255d3SCy Schubert {
2880c1d255d3SCy Schubert const struct minsnr_bitrate_entry *prev, *entry = table;
2881c1d255d3SCy Schubert
2882*a90b9d01SCy Schubert while (entry->minsnr != -1 && snr >= entry->minsnr &&
2883*a90b9d01SCy Schubert (eht || entry - table <= EHT_MCS))
2884c1d255d3SCy Schubert entry++;
2885c1d255d3SCy Schubert if (entry == table)
2886c1d255d3SCy Schubert return 0;
2887c1d255d3SCy Schubert prev = entry - 1;
2888*a90b9d01SCy Schubert if (entry->minsnr == -1 || (!eht && entry - table > EHT_MCS))
2889c1d255d3SCy Schubert return prev->bitrate;
2890c1d255d3SCy Schubert return interpolate_rate(snr, prev->minsnr, entry->minsnr,
2891c1d255d3SCy Schubert prev->bitrate, entry->bitrate);
2892c1d255d3SCy Schubert }
2893c1d255d3SCy Schubert
2894c1d255d3SCy Schubert
wpas_get_est_tpt(const struct wpa_supplicant * wpa_s,const u8 * ies,size_t ies_len,int rate,int snr,int freq,enum chan_width * max_cw)2895c1d255d3SCy Schubert unsigned int wpas_get_est_tpt(const struct wpa_supplicant *wpa_s,
2896c1d255d3SCy Schubert const u8 *ies, size_t ies_len, int rate,
2897*a90b9d01SCy Schubert int snr, int freq, enum chan_width *max_cw)
2898c1d255d3SCy Schubert {
2899c1d255d3SCy Schubert struct hostapd_hw_modes *hw_mode;
29005b9c547cSRui Paulo unsigned int est, tmp;
2901c1d255d3SCy Schubert const u8 *ie;
2902*a90b9d01SCy Schubert /*
2903*a90b9d01SCy Schubert * No need to apply a bump to the noise here because the
2904*a90b9d01SCy Schubert * minsnr_bitrate_entry tables are based on MCS tables where this has
2905*a90b9d01SCy Schubert * been taken into account.
2906*a90b9d01SCy Schubert */
2907*a90b9d01SCy Schubert int adjusted_snr;
2908*a90b9d01SCy Schubert bool ht40 = false, vht80 = false, vht160 = false;
29095b9c547cSRui Paulo
29105b9c547cSRui Paulo /* Limit based on estimated SNR */
29115b9c547cSRui Paulo if (rate > 1 * 2 && snr < 1)
29125b9c547cSRui Paulo rate = 1 * 2;
29135b9c547cSRui Paulo else if (rate > 2 * 2 && snr < 4)
29145b9c547cSRui Paulo rate = 2 * 2;
29155b9c547cSRui Paulo else if (rate > 6 * 2 && snr < 5)
29165b9c547cSRui Paulo rate = 6 * 2;
29175b9c547cSRui Paulo else if (rate > 9 * 2 && snr < 6)
29185b9c547cSRui Paulo rate = 9 * 2;
29195b9c547cSRui Paulo else if (rate > 12 * 2 && snr < 7)
29205b9c547cSRui Paulo rate = 12 * 2;
2921c1d255d3SCy Schubert else if (rate > 12 * 2 && snr < 8)
2922c1d255d3SCy Schubert rate = 14 * 2;
2923c1d255d3SCy Schubert else if (rate > 12 * 2 && snr < 9)
2924c1d255d3SCy Schubert rate = 16 * 2;
29255b9c547cSRui Paulo else if (rate > 18 * 2 && snr < 10)
29265b9c547cSRui Paulo rate = 18 * 2;
29275b9c547cSRui Paulo else if (rate > 24 * 2 && snr < 11)
29285b9c547cSRui Paulo rate = 24 * 2;
2929c1d255d3SCy Schubert else if (rate > 24 * 2 && snr < 12)
2930c1d255d3SCy Schubert rate = 27 * 2;
2931c1d255d3SCy Schubert else if (rate > 24 * 2 && snr < 13)
2932c1d255d3SCy Schubert rate = 30 * 2;
2933c1d255d3SCy Schubert else if (rate > 24 * 2 && snr < 14)
2934c1d255d3SCy Schubert rate = 33 * 2;
29355b9c547cSRui Paulo else if (rate > 36 * 2 && snr < 15)
29365b9c547cSRui Paulo rate = 36 * 2;
2937c1d255d3SCy Schubert else if (rate > 36 * 2 && snr < 16)
2938c1d255d3SCy Schubert rate = 39 * 2;
2939c1d255d3SCy Schubert else if (rate > 36 * 2 && snr < 17)
2940c1d255d3SCy Schubert rate = 42 * 2;
2941c1d255d3SCy Schubert else if (rate > 36 * 2 && snr < 18)
2942c1d255d3SCy Schubert rate = 45 * 2;
29435b9c547cSRui Paulo else if (rate > 48 * 2 && snr < 19)
29445b9c547cSRui Paulo rate = 48 * 2;
2945c1d255d3SCy Schubert else if (rate > 48 * 2 && snr < 20)
2946c1d255d3SCy Schubert rate = 51 * 2;
29475b9c547cSRui Paulo else if (rate > 54 * 2 && snr < 21)
29485b9c547cSRui Paulo rate = 54 * 2;
29495b9c547cSRui Paulo est = rate * 500;
29505b9c547cSRui Paulo
2951c1d255d3SCy Schubert hw_mode = get_mode_with_freq(wpa_s->hw.modes, wpa_s->hw.num_modes,
2952c1d255d3SCy Schubert freq);
2953c1d255d3SCy Schubert
2954c1d255d3SCy Schubert if (hw_mode && hw_mode->ht_capab) {
2955c1d255d3SCy Schubert ie = get_ie(ies, ies_len, WLAN_EID_HT_CAP);
29565b9c547cSRui Paulo if (ie) {
2957*a90b9d01SCy Schubert *max_cw = CHAN_WIDTH_20;
2958c1d255d3SCy Schubert tmp = max_ht20_rate(snr, false);
29595b9c547cSRui Paulo if (tmp > est)
29605b9c547cSRui Paulo est = tmp;
29615b9c547cSRui Paulo }
29625b9c547cSRui Paulo }
29635b9c547cSRui Paulo
2964*a90b9d01SCy Schubert ie = get_ie(ies, ies_len, WLAN_EID_HT_OPERATION);
2965*a90b9d01SCy Schubert if (ie && ie[1] >= 2 &&
2966*a90b9d01SCy Schubert (ie[3] & HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK))
2967*a90b9d01SCy Schubert ht40 = true;
2968*a90b9d01SCy Schubert
2969c1d255d3SCy Schubert if (hw_mode &&
2970c1d255d3SCy Schubert (hw_mode->ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)) {
2971*a90b9d01SCy Schubert if (ht40) {
2972*a90b9d01SCy Schubert *max_cw = CHAN_WIDTH_40;
2973*a90b9d01SCy Schubert adjusted_snr = snr +
2974*a90b9d01SCy Schubert wpas_channel_width_rssi_bump(ies, ies_len,
2975*a90b9d01SCy Schubert CHAN_WIDTH_40);
2976*a90b9d01SCy Schubert tmp = max_ht40_rate(adjusted_snr, false);
29775b9c547cSRui Paulo if (tmp > est)
29785b9c547cSRui Paulo est = tmp;
29795b9c547cSRui Paulo }
29805b9c547cSRui Paulo }
29815b9c547cSRui Paulo
2982*a90b9d01SCy Schubert /* Determine VHT BSS bandwidth based on IEEE Std 802.11-2020,
2983*a90b9d01SCy Schubert * Table 11-23 (VHT BSS bandwidth) */
2984c1d255d3SCy Schubert ie = get_ie(ies, ies_len, WLAN_EID_VHT_OPERATION);
2985c1d255d3SCy Schubert if (ie && ie[1] >= 3) {
2986c1d255d3SCy Schubert u8 cw = ie[2] & VHT_OPMODE_CHANNEL_WIDTH_MASK;
2987c1d255d3SCy Schubert u8 seg0 = ie[3];
2988c1d255d3SCy Schubert u8 seg1 = ie[4];
2989c1d255d3SCy Schubert
2990c1d255d3SCy Schubert if (cw)
2991c1d255d3SCy Schubert vht80 = true;
2992c1d255d3SCy Schubert if (cw == 2 ||
2993*a90b9d01SCy Schubert (cw == 3 && (seg1 > 0 && abs(seg1 - seg0) == 16)))
2994c1d255d3SCy Schubert vht160 = true;
2995c1d255d3SCy Schubert if (cw == 1 &&
2996c1d255d3SCy Schubert ((seg1 > 0 && abs(seg1 - seg0) == 8) ||
2997c1d255d3SCy Schubert (seg1 > 0 && abs(seg1 - seg0) == 16)))
2998c1d255d3SCy Schubert vht160 = true;
2999c1d255d3SCy Schubert }
3000c1d255d3SCy Schubert
3001*a90b9d01SCy Schubert if (hw_mode && hw_mode->vht_capab) {
3002*a90b9d01SCy Schubert /* Use +1 to assume VHT is always faster than HT */
3003*a90b9d01SCy Schubert ie = get_ie(ies, ies_len, WLAN_EID_VHT_CAP);
3004*a90b9d01SCy Schubert if (ie) {
3005*a90b9d01SCy Schubert if (*max_cw == CHAN_WIDTH_UNKNOWN)
3006*a90b9d01SCy Schubert *max_cw = CHAN_WIDTH_20;
3007*a90b9d01SCy Schubert tmp = max_ht20_rate(snr, true) + 1;
3008*a90b9d01SCy Schubert if (tmp > est)
3009*a90b9d01SCy Schubert est = tmp;
3010*a90b9d01SCy Schubert
3011*a90b9d01SCy Schubert if (ht40) {
3012*a90b9d01SCy Schubert *max_cw = CHAN_WIDTH_40;
3013*a90b9d01SCy Schubert adjusted_snr = snr +
3014*a90b9d01SCy Schubert wpas_channel_width_rssi_bump(
3015*a90b9d01SCy Schubert ies, ies_len, CHAN_WIDTH_40);
3016*a90b9d01SCy Schubert tmp = max_ht40_rate(adjusted_snr, true) + 1;
3017*a90b9d01SCy Schubert if (tmp > est)
3018*a90b9d01SCy Schubert est = tmp;
3019*a90b9d01SCy Schubert }
3020*a90b9d01SCy Schubert
3021c1d255d3SCy Schubert if (vht80) {
3022*a90b9d01SCy Schubert *max_cw = CHAN_WIDTH_80;
3023*a90b9d01SCy Schubert adjusted_snr = snr +
3024*a90b9d01SCy Schubert wpas_channel_width_rssi_bump(
3025*a90b9d01SCy Schubert ies, ies_len, CHAN_WIDTH_80);
3026*a90b9d01SCy Schubert tmp = max_vht80_rate(adjusted_snr) + 1;
30275b9c547cSRui Paulo if (tmp > est)
30285b9c547cSRui Paulo est = tmp;
30295b9c547cSRui Paulo }
3030c1d255d3SCy Schubert
3031c1d255d3SCy Schubert if (vht160 &&
3032c1d255d3SCy Schubert (hw_mode->vht_capab &
3033c1d255d3SCy Schubert (VHT_CAP_SUPP_CHAN_WIDTH_160MHZ |
3034c1d255d3SCy Schubert VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ))) {
3035*a90b9d01SCy Schubert *max_cw = CHAN_WIDTH_160;
3036*a90b9d01SCy Schubert adjusted_snr = snr +
3037*a90b9d01SCy Schubert wpas_channel_width_rssi_bump(
3038*a90b9d01SCy Schubert ies, ies_len, CHAN_WIDTH_160);
3039*a90b9d01SCy Schubert tmp = max_vht160_rate(adjusted_snr) + 1;
3040c1d255d3SCy Schubert if (tmp > est)
3041c1d255d3SCy Schubert est = tmp;
30425b9c547cSRui Paulo }
30435b9c547cSRui Paulo }
3044c1d255d3SCy Schubert }
3045c1d255d3SCy Schubert
3046c1d255d3SCy Schubert if (hw_mode && hw_mode->he_capab[IEEE80211_MODE_INFRA].he_supported) {
3047c1d255d3SCy Schubert /* Use +2 to assume HE is always faster than HT/VHT */
3048c1d255d3SCy Schubert struct ieee80211_he_capabilities *he;
3049*a90b9d01SCy Schubert struct ieee80211_eht_capabilities *eht;
3050c1d255d3SCy Schubert struct he_capabilities *own_he;
3051*a90b9d01SCy Schubert u8 cw, boost = 2;
3052*a90b9d01SCy Schubert const u8 *eht_ie;
3053*a90b9d01SCy Schubert bool is_eht = false;
3054c1d255d3SCy Schubert
3055c1d255d3SCy Schubert ie = get_ie_ext(ies, ies_len, WLAN_EID_EXT_HE_CAPABILITIES);
3056c1d255d3SCy Schubert if (!ie || (ie[1] < 1 + IEEE80211_HE_CAPAB_MIN_LEN))
3057c1d255d3SCy Schubert return est;
3058c1d255d3SCy Schubert he = (struct ieee80211_he_capabilities *) &ie[3];
3059c1d255d3SCy Schubert own_he = &hw_mode->he_capab[IEEE80211_MODE_INFRA];
3060c1d255d3SCy Schubert
3061*a90b9d01SCy Schubert /* Use +3 to assume EHT is always faster than HE */
3062*a90b9d01SCy Schubert if (hw_mode->eht_capab[IEEE80211_MODE_INFRA].eht_supported) {
3063*a90b9d01SCy Schubert eht_ie = get_ie_ext(ies, ies_len,
3064*a90b9d01SCy Schubert WLAN_EID_EXT_EHT_CAPABILITIES);
3065*a90b9d01SCy Schubert if (eht_ie &&
3066*a90b9d01SCy Schubert (eht_ie[1] >= 1 + IEEE80211_EHT_CAPAB_MIN_LEN)) {
3067*a90b9d01SCy Schubert is_eht = true;
3068*a90b9d01SCy Schubert boost = 3;
3069*a90b9d01SCy Schubert }
3070*a90b9d01SCy Schubert }
3071*a90b9d01SCy Schubert
3072*a90b9d01SCy Schubert if (*max_cw == CHAN_WIDTH_UNKNOWN)
3073*a90b9d01SCy Schubert *max_cw = CHAN_WIDTH_20;
3074*a90b9d01SCy Schubert tmp = max_he_eht_rate(he20_table, snr, is_eht) + boost;
3075c1d255d3SCy Schubert if (tmp > est)
3076c1d255d3SCy Schubert est = tmp;
3077c1d255d3SCy Schubert
3078c1d255d3SCy Schubert cw = he->he_phy_capab_info[HE_PHYCAP_CHANNEL_WIDTH_SET_IDX] &
3079c1d255d3SCy Schubert own_he->phy_cap[HE_PHYCAP_CHANNEL_WIDTH_SET_IDX];
3080*a90b9d01SCy Schubert if ((cw &
3081*a90b9d01SCy Schubert (IS_2P4GHZ(freq) ?
3082*a90b9d01SCy Schubert HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_IN_2G :
3083*a90b9d01SCy Schubert HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G)) && ht40) {
3084*a90b9d01SCy Schubert if (*max_cw == CHAN_WIDTH_UNKNOWN ||
3085*a90b9d01SCy Schubert *max_cw < CHAN_WIDTH_40)
3086*a90b9d01SCy Schubert *max_cw = CHAN_WIDTH_40;
3087*a90b9d01SCy Schubert adjusted_snr = snr + wpas_channel_width_rssi_bump(
3088*a90b9d01SCy Schubert ies, ies_len, CHAN_WIDTH_40);
3089*a90b9d01SCy Schubert tmp = max_he_eht_rate(he40_table, adjusted_snr,
3090*a90b9d01SCy Schubert is_eht) + boost;
3091c1d255d3SCy Schubert if (tmp > est)
3092c1d255d3SCy Schubert est = tmp;
3093c1d255d3SCy Schubert }
3094c1d255d3SCy Schubert
3095c1d255d3SCy Schubert if (!IS_2P4GHZ(freq) &&
3096*a90b9d01SCy Schubert (cw & HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G) &&
3097*a90b9d01SCy Schubert (!IS_5GHZ(freq) || vht80)) {
3098*a90b9d01SCy Schubert if (*max_cw == CHAN_WIDTH_UNKNOWN ||
3099*a90b9d01SCy Schubert *max_cw < CHAN_WIDTH_80)
3100*a90b9d01SCy Schubert *max_cw = CHAN_WIDTH_80;
3101*a90b9d01SCy Schubert adjusted_snr = snr + wpas_channel_width_rssi_bump(
3102*a90b9d01SCy Schubert ies, ies_len, CHAN_WIDTH_80);
3103*a90b9d01SCy Schubert tmp = max_he_eht_rate(he80_table, adjusted_snr,
3104*a90b9d01SCy Schubert is_eht) + boost;
3105c1d255d3SCy Schubert if (tmp > est)
3106c1d255d3SCy Schubert est = tmp;
3107c1d255d3SCy Schubert }
3108c1d255d3SCy Schubert
3109c1d255d3SCy Schubert if (!IS_2P4GHZ(freq) &&
3110c1d255d3SCy Schubert (cw & (HE_PHYCAP_CHANNEL_WIDTH_SET_160MHZ_IN_5G |
3111*a90b9d01SCy Schubert HE_PHYCAP_CHANNEL_WIDTH_SET_80PLUS80MHZ_IN_5G)) &&
3112*a90b9d01SCy Schubert (!IS_5GHZ(freq) || vht160)) {
3113*a90b9d01SCy Schubert if (*max_cw == CHAN_WIDTH_UNKNOWN ||
3114*a90b9d01SCy Schubert *max_cw < CHAN_WIDTH_160)
3115*a90b9d01SCy Schubert *max_cw = CHAN_WIDTH_160;
3116*a90b9d01SCy Schubert adjusted_snr = snr + wpas_channel_width_rssi_bump(
3117*a90b9d01SCy Schubert ies, ies_len, CHAN_WIDTH_160);
3118*a90b9d01SCy Schubert tmp = max_he_eht_rate(he160_table, adjusted_snr,
3119*a90b9d01SCy Schubert is_eht) + boost;
3120*a90b9d01SCy Schubert if (tmp > est)
3121*a90b9d01SCy Schubert est = tmp;
3122*a90b9d01SCy Schubert }
3123*a90b9d01SCy Schubert
3124*a90b9d01SCy Schubert if (!is_eht)
3125*a90b9d01SCy Schubert return est;
3126*a90b9d01SCy Schubert
3127*a90b9d01SCy Schubert eht = (struct ieee80211_eht_capabilities *) &eht_ie[3];
3128*a90b9d01SCy Schubert
3129*a90b9d01SCy Schubert if (is_6ghz_freq(freq) &&
3130*a90b9d01SCy Schubert (eht->phy_cap[EHT_PHYCAP_320MHZ_IN_6GHZ_SUPPORT_IDX] &
3131*a90b9d01SCy Schubert EHT_PHYCAP_320MHZ_IN_6GHZ_SUPPORT_MASK)) {
3132*a90b9d01SCy Schubert if (*max_cw == CHAN_WIDTH_UNKNOWN ||
3133*a90b9d01SCy Schubert *max_cw < CHAN_WIDTH_320)
3134*a90b9d01SCy Schubert *max_cw = CHAN_WIDTH_320;
3135*a90b9d01SCy Schubert adjusted_snr = snr + wpas_channel_width_rssi_bump(
3136*a90b9d01SCy Schubert ies, ies_len, CHAN_WIDTH_320);
3137*a90b9d01SCy Schubert tmp = max_he_eht_rate(eht320_table, adjusted_snr, true);
3138c1d255d3SCy Schubert if (tmp > est)
3139c1d255d3SCy Schubert est = tmp;
3140c1d255d3SCy Schubert }
3141c1d255d3SCy Schubert }
3142c1d255d3SCy Schubert
3143c1d255d3SCy Schubert return est;
3144c1d255d3SCy Schubert }
3145c1d255d3SCy Schubert
3146c1d255d3SCy Schubert
scan_est_throughput(struct wpa_supplicant * wpa_s,struct wpa_scan_res * res)3147c1d255d3SCy Schubert void scan_est_throughput(struct wpa_supplicant *wpa_s,
3148c1d255d3SCy Schubert struct wpa_scan_res *res)
3149c1d255d3SCy Schubert {
3150c1d255d3SCy Schubert int rate; /* max legacy rate in 500 kb/s units */
3151c1d255d3SCy Schubert int snr = res->snr;
3152c1d255d3SCy Schubert const u8 *ies = (const void *) (res + 1);
3153c1d255d3SCy Schubert size_t ie_len = res->ie_len;
3154c1d255d3SCy Schubert
3155c1d255d3SCy Schubert if (res->est_throughput)
3156c1d255d3SCy Schubert return;
3157c1d255d3SCy Schubert
3158c1d255d3SCy Schubert /* Get maximum legacy rate */
3159c1d255d3SCy Schubert rate = wpa_scan_get_max_rate(res);
3160c1d255d3SCy Schubert
3161c1d255d3SCy Schubert if (!ie_len)
3162c1d255d3SCy Schubert ie_len = res->beacon_ie_len;
3163*a90b9d01SCy Schubert res->est_throughput = wpas_get_est_tpt(wpa_s, ies, ie_len, rate, snr,
3164*a90b9d01SCy Schubert res->freq, &res->max_cw);
31655b9c547cSRui Paulo
31665b9c547cSRui Paulo /* TODO: channel utilization and AP load (e.g., from AP Beacon) */
31675b9c547cSRui Paulo }
31685b9c547cSRui Paulo
31695b9c547cSRui Paulo
3170e28a4053SRui Paulo /**
3171e28a4053SRui Paulo * wpa_supplicant_get_scan_results - Get scan results
3172e28a4053SRui Paulo * @wpa_s: Pointer to wpa_supplicant data
3173e28a4053SRui Paulo * @info: Information about what was scanned or %NULL if not available
3174e28a4053SRui Paulo * @new_scan: Whether a new scan was performed
3175*a90b9d01SCy Schubert * @bssid: Return BSS entries only for a single BSSID, %NULL for all
3176e28a4053SRui Paulo * Returns: Scan results, %NULL on failure
3177e28a4053SRui Paulo *
3178e28a4053SRui Paulo * This function request the current scan results from the driver and updates
3179e28a4053SRui Paulo * the local BSS list wpa_s->bss. The caller is responsible for freeing the
3180e28a4053SRui Paulo * results with wpa_scan_results_free().
3181e28a4053SRui Paulo */
3182e28a4053SRui Paulo struct wpa_scan_results *
wpa_supplicant_get_scan_results(struct wpa_supplicant * wpa_s,struct scan_info * info,int new_scan,const u8 * bssid)3183e28a4053SRui Paulo wpa_supplicant_get_scan_results(struct wpa_supplicant *wpa_s,
3184*a90b9d01SCy Schubert struct scan_info *info, int new_scan,
3185*a90b9d01SCy Schubert const u8 *bssid)
3186e28a4053SRui Paulo {
3187e28a4053SRui Paulo struct wpa_scan_results *scan_res;
3188e28a4053SRui Paulo size_t i;
3189f05cddf9SRui Paulo int (*compar)(const void *, const void *) = wpa_scan_result_compar;
3190e28a4053SRui Paulo
3191*a90b9d01SCy Schubert scan_res = wpa_drv_get_scan_results(wpa_s, bssid);
3192e28a4053SRui Paulo if (scan_res == NULL) {
3193f05cddf9SRui Paulo wpa_dbg(wpa_s, MSG_DEBUG, "Failed to get scan results");
3194e28a4053SRui Paulo return NULL;
3195e28a4053SRui Paulo }
31965b9c547cSRui Paulo if (scan_res->fetch_time.sec == 0) {
31975b9c547cSRui Paulo /*
31985b9c547cSRui Paulo * Make sure we have a valid timestamp if the driver wrapper
31995b9c547cSRui Paulo * does not set this.
32005b9c547cSRui Paulo */
32015b9c547cSRui Paulo os_get_reltime(&scan_res->fetch_time);
32025b9c547cSRui Paulo }
3203f05cddf9SRui Paulo filter_scan_res(wpa_s, scan_res);
3204f05cddf9SRui Paulo
32055b9c547cSRui Paulo for (i = 0; i < scan_res->num; i++) {
32065b9c547cSRui Paulo struct wpa_scan_res *scan_res_item = scan_res->res[i];
32075b9c547cSRui Paulo
32085b9c547cSRui Paulo scan_snr(scan_res_item);
32095b9c547cSRui Paulo scan_est_throughput(wpa_s, scan_res_item);
32105b9c547cSRui Paulo }
32115b9c547cSRui Paulo
3212f05cddf9SRui Paulo #ifdef CONFIG_WPS
32135b9c547cSRui Paulo if (wpas_wps_searching(wpa_s)) {
3214f05cddf9SRui Paulo wpa_dbg(wpa_s, MSG_DEBUG, "WPS: Order scan results with WPS "
3215f05cddf9SRui Paulo "provisioning rules");
3216f05cddf9SRui Paulo compar = wpa_scan_result_wps_compar;
3217f05cddf9SRui Paulo }
3218f05cddf9SRui Paulo #endif /* CONFIG_WPS */
3219e28a4053SRui Paulo
322085732ac8SCy Schubert if (scan_res->res) {
322185732ac8SCy Schubert qsort(scan_res->res, scan_res->num,
322285732ac8SCy Schubert sizeof(struct wpa_scan_res *), compar);
322385732ac8SCy Schubert }
3224f05cddf9SRui Paulo dump_scan_res(scan_res);
3225e28a4053SRui Paulo
322685732ac8SCy Schubert if (wpa_s->ignore_post_flush_scan_res) {
322785732ac8SCy Schubert /* FLUSH command aborted an ongoing scan and these are the
322885732ac8SCy Schubert * results from the aborted scan. Do not process the results to
322985732ac8SCy Schubert * maintain flushed state. */
323085732ac8SCy Schubert wpa_dbg(wpa_s, MSG_DEBUG,
323185732ac8SCy Schubert "Do not update BSS table based on pending post-FLUSH scan results");
323285732ac8SCy Schubert wpa_s->ignore_post_flush_scan_res = 0;
323385732ac8SCy Schubert return scan_res;
323485732ac8SCy Schubert }
323585732ac8SCy Schubert
3236e28a4053SRui Paulo wpa_bss_update_start(wpa_s);
3237e28a4053SRui Paulo for (i = 0; i < scan_res->num; i++)
32385b9c547cSRui Paulo wpa_bss_update_scan_res(wpa_s, scan_res->res[i],
32395b9c547cSRui Paulo &scan_res->fetch_time);
3240e28a4053SRui Paulo wpa_bss_update_end(wpa_s, info, new_scan);
3241e28a4053SRui Paulo
3242e28a4053SRui Paulo return scan_res;
3243e28a4053SRui Paulo }
3244e28a4053SRui Paulo
3245e28a4053SRui Paulo
3246f05cddf9SRui Paulo /**
3247f05cddf9SRui Paulo * wpa_supplicant_update_scan_results - Update scan results from the driver
3248f05cddf9SRui Paulo * @wpa_s: Pointer to wpa_supplicant data
3249*a90b9d01SCy Schubert * @bssid: Update BSS entries only for a single BSSID, %NULL for all
3250f05cddf9SRui Paulo * Returns: 0 on success, -1 on failure
3251f05cddf9SRui Paulo *
3252f05cddf9SRui Paulo * This function updates the BSS table within wpa_supplicant based on the
3253f05cddf9SRui Paulo * currently available scan results from the driver without requesting a new
3254f05cddf9SRui Paulo * scan. This is used in cases where the driver indicates an association
3255f05cddf9SRui Paulo * (including roaming within ESS) and wpa_supplicant does not yet have the
3256f05cddf9SRui Paulo * needed information to complete the connection (e.g., to perform validation
3257f05cddf9SRui Paulo * steps in 4-way handshake).
3258f05cddf9SRui Paulo */
wpa_supplicant_update_scan_results(struct wpa_supplicant * wpa_s,const u8 * bssid)3259*a90b9d01SCy Schubert int wpa_supplicant_update_scan_results(struct wpa_supplicant *wpa_s,
3260*a90b9d01SCy Schubert const u8 *bssid)
3261e28a4053SRui Paulo {
3262e28a4053SRui Paulo struct wpa_scan_results *scan_res;
3263*a90b9d01SCy Schubert scan_res = wpa_supplicant_get_scan_results(wpa_s, NULL, 0, bssid);
3264e28a4053SRui Paulo if (scan_res == NULL)
3265e28a4053SRui Paulo return -1;
3266e28a4053SRui Paulo wpa_scan_results_free(scan_res);
3267e28a4053SRui Paulo
3268e28a4053SRui Paulo return 0;
3269e28a4053SRui Paulo }
32705b9c547cSRui Paulo
32715b9c547cSRui Paulo
32725b9c547cSRui Paulo /**
32735b9c547cSRui Paulo * scan_only_handler - Reports scan results
32745b9c547cSRui Paulo */
scan_only_handler(struct wpa_supplicant * wpa_s,struct wpa_scan_results * scan_res)32755b9c547cSRui Paulo void scan_only_handler(struct wpa_supplicant *wpa_s,
32765b9c547cSRui Paulo struct wpa_scan_results *scan_res)
32775b9c547cSRui Paulo {
32785b9c547cSRui Paulo wpa_dbg(wpa_s, MSG_DEBUG, "Scan-only results received");
32795b9c547cSRui Paulo if (wpa_s->last_scan_req == MANUAL_SCAN_REQ &&
32805b9c547cSRui Paulo wpa_s->manual_scan_use_id && wpa_s->own_scan_running) {
32815b9c547cSRui Paulo wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_SCAN_RESULTS "id=%u",
32825b9c547cSRui Paulo wpa_s->manual_scan_id);
32835b9c547cSRui Paulo wpa_s->manual_scan_use_id = 0;
32845b9c547cSRui Paulo } else {
32855b9c547cSRui Paulo wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_SCAN_RESULTS);
32865b9c547cSRui Paulo }
32875b9c547cSRui Paulo wpas_notify_scan_results(wpa_s);
32885b9c547cSRui Paulo wpas_notify_scan_done(wpa_s, 1);
32895b9c547cSRui Paulo if (wpa_s->scan_work) {
32905b9c547cSRui Paulo struct wpa_radio_work *work = wpa_s->scan_work;
32915b9c547cSRui Paulo wpa_s->scan_work = NULL;
32925b9c547cSRui Paulo radio_work_done(work);
32935b9c547cSRui Paulo }
3294780fb4a2SCy Schubert
3295780fb4a2SCy Schubert if (wpa_s->wpa_state == WPA_SCANNING)
3296780fb4a2SCy Schubert wpa_supplicant_set_state(wpa_s, wpa_s->scan_prev_wpa_state);
32975b9c547cSRui Paulo }
32985b9c547cSRui Paulo
32995b9c547cSRui Paulo
wpas_scan_scheduled(struct wpa_supplicant * wpa_s)33005b9c547cSRui Paulo int wpas_scan_scheduled(struct wpa_supplicant *wpa_s)
33015b9c547cSRui Paulo {
33025b9c547cSRui Paulo return eloop_is_timeout_registered(wpa_supplicant_scan, wpa_s, NULL);
33035b9c547cSRui Paulo }
33045b9c547cSRui Paulo
33055b9c547cSRui Paulo
33065b9c547cSRui Paulo struct wpa_driver_scan_params *
wpa_scan_clone_params(const struct wpa_driver_scan_params * src)33075b9c547cSRui Paulo wpa_scan_clone_params(const struct wpa_driver_scan_params *src)
33085b9c547cSRui Paulo {
33095b9c547cSRui Paulo struct wpa_driver_scan_params *params;
33105b9c547cSRui Paulo size_t i;
33115b9c547cSRui Paulo u8 *n;
33125b9c547cSRui Paulo
33135b9c547cSRui Paulo params = os_zalloc(sizeof(*params));
33145b9c547cSRui Paulo if (params == NULL)
33155b9c547cSRui Paulo return NULL;
33165b9c547cSRui Paulo
33175b9c547cSRui Paulo for (i = 0; i < src->num_ssids; i++) {
33185b9c547cSRui Paulo if (src->ssids[i].ssid) {
331985732ac8SCy Schubert n = os_memdup(src->ssids[i].ssid,
332085732ac8SCy Schubert src->ssids[i].ssid_len);
33215b9c547cSRui Paulo if (n == NULL)
33225b9c547cSRui Paulo goto failed;
33235b9c547cSRui Paulo params->ssids[i].ssid = n;
33245b9c547cSRui Paulo params->ssids[i].ssid_len = src->ssids[i].ssid_len;
33255b9c547cSRui Paulo }
33265b9c547cSRui Paulo }
33275b9c547cSRui Paulo params->num_ssids = src->num_ssids;
33285b9c547cSRui Paulo
33295b9c547cSRui Paulo if (src->extra_ies) {
333085732ac8SCy Schubert n = os_memdup(src->extra_ies, src->extra_ies_len);
33315b9c547cSRui Paulo if (n == NULL)
33325b9c547cSRui Paulo goto failed;
33335b9c547cSRui Paulo params->extra_ies = n;
33345b9c547cSRui Paulo params->extra_ies_len = src->extra_ies_len;
33355b9c547cSRui Paulo }
33365b9c547cSRui Paulo
33375b9c547cSRui Paulo if (src->freqs) {
33385b9c547cSRui Paulo int len = int_array_len(src->freqs);
333985732ac8SCy Schubert params->freqs = os_memdup(src->freqs, (len + 1) * sizeof(int));
33405b9c547cSRui Paulo if (params->freqs == NULL)
33415b9c547cSRui Paulo goto failed;
33425b9c547cSRui Paulo }
33435b9c547cSRui Paulo
33445b9c547cSRui Paulo if (src->filter_ssids) {
334585732ac8SCy Schubert params->filter_ssids = os_memdup(src->filter_ssids,
334685732ac8SCy Schubert sizeof(*params->filter_ssids) *
33475b9c547cSRui Paulo src->num_filter_ssids);
33485b9c547cSRui Paulo if (params->filter_ssids == NULL)
33495b9c547cSRui Paulo goto failed;
33505b9c547cSRui Paulo params->num_filter_ssids = src->num_filter_ssids;
33515b9c547cSRui Paulo }
33525b9c547cSRui Paulo
33535b9c547cSRui Paulo params->filter_rssi = src->filter_rssi;
33545b9c547cSRui Paulo params->p2p_probe = src->p2p_probe;
33555b9c547cSRui Paulo params->only_new_results = src->only_new_results;
33565b9c547cSRui Paulo params->low_priority = src->low_priority;
335785732ac8SCy Schubert params->duration = src->duration;
335885732ac8SCy Schubert params->duration_mandatory = src->duration_mandatory;
335985732ac8SCy Schubert params->oce_scan = src->oce_scan;
3360*a90b9d01SCy Schubert params->link_id = src->link_id;
33615b9c547cSRui Paulo
3362780fb4a2SCy Schubert if (src->sched_scan_plans_num > 0) {
3363780fb4a2SCy Schubert params->sched_scan_plans =
336485732ac8SCy Schubert os_memdup(src->sched_scan_plans,
336585732ac8SCy Schubert sizeof(*src->sched_scan_plans) *
3366780fb4a2SCy Schubert src->sched_scan_plans_num);
3367780fb4a2SCy Schubert if (!params->sched_scan_plans)
3368780fb4a2SCy Schubert goto failed;
3369780fb4a2SCy Schubert
3370780fb4a2SCy Schubert params->sched_scan_plans_num = src->sched_scan_plans_num;
3371780fb4a2SCy Schubert }
3372780fb4a2SCy Schubert
3373c1d255d3SCy Schubert if (src->mac_addr_rand &&
3374c1d255d3SCy Schubert wpa_setup_mac_addr_rand_params(params, src->mac_addr))
33755b9c547cSRui Paulo goto failed;
33765b9c547cSRui Paulo
3377780fb4a2SCy Schubert if (src->bssid) {
3378780fb4a2SCy Schubert u8 *bssid;
3379780fb4a2SCy Schubert
338085732ac8SCy Schubert bssid = os_memdup(src->bssid, ETH_ALEN);
3381780fb4a2SCy Schubert if (!bssid)
3382780fb4a2SCy Schubert goto failed;
3383780fb4a2SCy Schubert params->bssid = bssid;
3384780fb4a2SCy Schubert }
3385780fb4a2SCy Schubert
338685732ac8SCy Schubert params->relative_rssi_set = src->relative_rssi_set;
338785732ac8SCy Schubert params->relative_rssi = src->relative_rssi;
338885732ac8SCy Schubert params->relative_adjust_band = src->relative_adjust_band;
338985732ac8SCy Schubert params->relative_adjust_rssi = src->relative_adjust_rssi;
3390c1d255d3SCy Schubert params->p2p_include_6ghz = src->p2p_include_6ghz;
3391*a90b9d01SCy Schubert params->non_coloc_6ghz = src->non_coloc_6ghz;
3392*a90b9d01SCy Schubert params->min_probe_req_content = src->min_probe_req_content;
33935b9c547cSRui Paulo return params;
33945b9c547cSRui Paulo
33955b9c547cSRui Paulo failed:
33965b9c547cSRui Paulo wpa_scan_free_params(params);
33975b9c547cSRui Paulo return NULL;
33985b9c547cSRui Paulo }
33995b9c547cSRui Paulo
34005b9c547cSRui Paulo
wpa_scan_free_params(struct wpa_driver_scan_params * params)34015b9c547cSRui Paulo void wpa_scan_free_params(struct wpa_driver_scan_params *params)
34025b9c547cSRui Paulo {
34035b9c547cSRui Paulo size_t i;
34045b9c547cSRui Paulo
34055b9c547cSRui Paulo if (params == NULL)
34065b9c547cSRui Paulo return;
34075b9c547cSRui Paulo
34085b9c547cSRui Paulo for (i = 0; i < params->num_ssids; i++)
34095b9c547cSRui Paulo os_free((u8 *) params->ssids[i].ssid);
34105b9c547cSRui Paulo os_free((u8 *) params->extra_ies);
34115b9c547cSRui Paulo os_free(params->freqs);
34125b9c547cSRui Paulo os_free(params->filter_ssids);
3413780fb4a2SCy Schubert os_free(params->sched_scan_plans);
34145b9c547cSRui Paulo
34155b9c547cSRui Paulo /*
34165b9c547cSRui Paulo * Note: params->mac_addr_mask points to same memory allocation and
34175b9c547cSRui Paulo * must not be freed separately.
34185b9c547cSRui Paulo */
34195b9c547cSRui Paulo os_free((u8 *) params->mac_addr);
34205b9c547cSRui Paulo
3421780fb4a2SCy Schubert os_free((u8 *) params->bssid);
3422780fb4a2SCy Schubert
34235b9c547cSRui Paulo os_free(params);
34245b9c547cSRui Paulo }
34255b9c547cSRui Paulo
34265b9c547cSRui Paulo
wpas_start_pno(struct wpa_supplicant * wpa_s)34275b9c547cSRui Paulo int wpas_start_pno(struct wpa_supplicant *wpa_s)
34285b9c547cSRui Paulo {
3429c1d255d3SCy Schubert int ret;
3430c1d255d3SCy Schubert size_t prio, i, num_ssid, num_match_ssid;
34315b9c547cSRui Paulo struct wpa_ssid *ssid;
34325b9c547cSRui Paulo struct wpa_driver_scan_params params;
3433780fb4a2SCy Schubert struct sched_scan_plan scan_plan;
3434780fb4a2SCy Schubert unsigned int max_sched_scan_ssids;
34355b9c547cSRui Paulo
34365b9c547cSRui Paulo if (!wpa_s->sched_scan_supported)
34375b9c547cSRui Paulo return -1;
34385b9c547cSRui Paulo
3439780fb4a2SCy Schubert if (wpa_s->max_sched_scan_ssids > WPAS_MAX_SCAN_SSIDS)
3440780fb4a2SCy Schubert max_sched_scan_ssids = WPAS_MAX_SCAN_SSIDS;
3441780fb4a2SCy Schubert else
3442780fb4a2SCy Schubert max_sched_scan_ssids = wpa_s->max_sched_scan_ssids;
3443780fb4a2SCy Schubert if (max_sched_scan_ssids < 1)
3444780fb4a2SCy Schubert return -1;
3445780fb4a2SCy Schubert
34465b9c547cSRui Paulo if (wpa_s->pno || wpa_s->pno_sched_pending)
34475b9c547cSRui Paulo return 0;
34485b9c547cSRui Paulo
34495b9c547cSRui Paulo if ((wpa_s->wpa_state > WPA_SCANNING) &&
345085732ac8SCy Schubert (wpa_s->wpa_state < WPA_COMPLETED)) {
34515b9c547cSRui Paulo wpa_printf(MSG_ERROR, "PNO: In assoc process");
34525b9c547cSRui Paulo return -EAGAIN;
34535b9c547cSRui Paulo }
34545b9c547cSRui Paulo
34555b9c547cSRui Paulo if (wpa_s->wpa_state == WPA_SCANNING) {
34565b9c547cSRui Paulo wpa_supplicant_cancel_scan(wpa_s);
34575b9c547cSRui Paulo if (wpa_s->sched_scanning) {
34585b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "Schedule PNO on completion of "
34595b9c547cSRui Paulo "ongoing sched scan");
34605b9c547cSRui Paulo wpa_supplicant_cancel_sched_scan(wpa_s);
34615b9c547cSRui Paulo wpa_s->pno_sched_pending = 1;
34625b9c547cSRui Paulo return 0;
34635b9c547cSRui Paulo }
34645b9c547cSRui Paulo }
34655b9c547cSRui Paulo
3466780fb4a2SCy Schubert if (wpa_s->sched_scan_stop_req) {
3467780fb4a2SCy Schubert wpa_printf(MSG_DEBUG,
3468780fb4a2SCy Schubert "Schedule PNO after previous sched scan has stopped");
3469780fb4a2SCy Schubert wpa_s->pno_sched_pending = 1;
3470780fb4a2SCy Schubert return 0;
3471780fb4a2SCy Schubert }
3472780fb4a2SCy Schubert
34735b9c547cSRui Paulo os_memset(¶ms, 0, sizeof(params));
34745b9c547cSRui Paulo
34755b9c547cSRui Paulo num_ssid = num_match_ssid = 0;
34765b9c547cSRui Paulo ssid = wpa_s->conf->ssid;
34775b9c547cSRui Paulo while (ssid) {
34785b9c547cSRui Paulo if (!wpas_network_disabled(wpa_s, ssid)) {
34795b9c547cSRui Paulo num_match_ssid++;
34805b9c547cSRui Paulo if (ssid->scan_ssid)
34815b9c547cSRui Paulo num_ssid++;
34825b9c547cSRui Paulo }
34835b9c547cSRui Paulo ssid = ssid->next;
34845b9c547cSRui Paulo }
34855b9c547cSRui Paulo
34865b9c547cSRui Paulo if (num_match_ssid == 0) {
34875b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "PNO: No configured SSIDs");
34885b9c547cSRui Paulo return -1;
34895b9c547cSRui Paulo }
34905b9c547cSRui Paulo
34915b9c547cSRui Paulo if (num_match_ssid > num_ssid) {
34925b9c547cSRui Paulo params.num_ssids++; /* wildcard */
34935b9c547cSRui Paulo num_ssid++;
34945b9c547cSRui Paulo }
34955b9c547cSRui Paulo
3496780fb4a2SCy Schubert if (num_ssid > max_sched_scan_ssids) {
34975b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "PNO: Use only the first %u SSIDs from "
3498780fb4a2SCy Schubert "%u", max_sched_scan_ssids, (unsigned int) num_ssid);
3499780fb4a2SCy Schubert num_ssid = max_sched_scan_ssids;
35005b9c547cSRui Paulo }
35015b9c547cSRui Paulo
35025b9c547cSRui Paulo if (num_match_ssid > wpa_s->max_match_sets) {
35035b9c547cSRui Paulo num_match_ssid = wpa_s->max_match_sets;
35045b9c547cSRui Paulo wpa_dbg(wpa_s, MSG_DEBUG, "PNO: Too many SSIDs to match");
35055b9c547cSRui Paulo }
35065b9c547cSRui Paulo params.filter_ssids = os_calloc(num_match_ssid,
35075b9c547cSRui Paulo sizeof(struct wpa_driver_scan_filter));
35085b9c547cSRui Paulo if (params.filter_ssids == NULL)
35095b9c547cSRui Paulo return -1;
35105b9c547cSRui Paulo
35115b9c547cSRui Paulo i = 0;
35125b9c547cSRui Paulo prio = 0;
35135b9c547cSRui Paulo ssid = wpa_s->conf->pssid[prio];
35145b9c547cSRui Paulo while (ssid) {
35155b9c547cSRui Paulo if (!wpas_network_disabled(wpa_s, ssid)) {
35165b9c547cSRui Paulo if (ssid->scan_ssid && params.num_ssids < num_ssid) {
35175b9c547cSRui Paulo params.ssids[params.num_ssids].ssid =
35185b9c547cSRui Paulo ssid->ssid;
35195b9c547cSRui Paulo params.ssids[params.num_ssids].ssid_len =
35205b9c547cSRui Paulo ssid->ssid_len;
35215b9c547cSRui Paulo params.num_ssids++;
35225b9c547cSRui Paulo }
35235b9c547cSRui Paulo os_memcpy(params.filter_ssids[i].ssid, ssid->ssid,
35245b9c547cSRui Paulo ssid->ssid_len);
35255b9c547cSRui Paulo params.filter_ssids[i].ssid_len = ssid->ssid_len;
35265b9c547cSRui Paulo params.num_filter_ssids++;
35275b9c547cSRui Paulo i++;
35285b9c547cSRui Paulo if (i == num_match_ssid)
35295b9c547cSRui Paulo break;
35305b9c547cSRui Paulo }
35315b9c547cSRui Paulo if (ssid->pnext)
35325b9c547cSRui Paulo ssid = ssid->pnext;
35335b9c547cSRui Paulo else if (prio + 1 == wpa_s->conf->num_prio)
35345b9c547cSRui Paulo break;
35355b9c547cSRui Paulo else
35365b9c547cSRui Paulo ssid = wpa_s->conf->pssid[++prio];
35375b9c547cSRui Paulo }
35385b9c547cSRui Paulo
35395b9c547cSRui Paulo if (wpa_s->conf->filter_rssi)
35405b9c547cSRui Paulo params.filter_rssi = wpa_s->conf->filter_rssi;
35415b9c547cSRui Paulo
3542780fb4a2SCy Schubert if (wpa_s->sched_scan_plans_num) {
3543780fb4a2SCy Schubert params.sched_scan_plans = wpa_s->sched_scan_plans;
3544780fb4a2SCy Schubert params.sched_scan_plans_num = wpa_s->sched_scan_plans_num;
3545780fb4a2SCy Schubert } else {
3546780fb4a2SCy Schubert /* Set one scan plan that will run infinitely */
3547780fb4a2SCy Schubert if (wpa_s->conf->sched_scan_interval)
3548780fb4a2SCy Schubert scan_plan.interval = wpa_s->conf->sched_scan_interval;
3549780fb4a2SCy Schubert else
3550780fb4a2SCy Schubert scan_plan.interval = 10;
3551780fb4a2SCy Schubert
3552780fb4a2SCy Schubert scan_plan.iterations = 0;
3553780fb4a2SCy Schubert params.sched_scan_plans = &scan_plan;
3554780fb4a2SCy Schubert params.sched_scan_plans_num = 1;
3555780fb4a2SCy Schubert }
35565b9c547cSRui Paulo
355785732ac8SCy Schubert params.sched_scan_start_delay = wpa_s->conf->sched_scan_start_delay;
355885732ac8SCy Schubert
35595b9c547cSRui Paulo if (params.freqs == NULL && wpa_s->manual_sched_scan_freqs) {
35605b9c547cSRui Paulo wpa_dbg(wpa_s, MSG_DEBUG, "Limit sched scan to specified channels");
35615b9c547cSRui Paulo params.freqs = wpa_s->manual_sched_scan_freqs;
35625b9c547cSRui Paulo }
35635b9c547cSRui Paulo
356485732ac8SCy Schubert if ((wpa_s->mac_addr_rand_enable & MAC_ADDR_RAND_PNO) &&
3565c1d255d3SCy Schubert wpa_s->wpa_state <= WPA_SCANNING)
3566c1d255d3SCy Schubert wpa_setup_mac_addr_rand_params(¶ms, wpa_s->mac_addr_pno);
35675b9c547cSRui Paulo
356885732ac8SCy Schubert wpa_scan_set_relative_rssi_params(wpa_s, ¶ms);
356985732ac8SCy Schubert
3570780fb4a2SCy Schubert ret = wpa_supplicant_start_sched_scan(wpa_s, ¶ms);
35715b9c547cSRui Paulo os_free(params.filter_ssids);
3572c1d255d3SCy Schubert os_free(params.mac_addr);
35735b9c547cSRui Paulo if (ret == 0)
35745b9c547cSRui Paulo wpa_s->pno = 1;
35755b9c547cSRui Paulo else
35765b9c547cSRui Paulo wpa_msg(wpa_s, MSG_ERROR, "Failed to schedule PNO");
35775b9c547cSRui Paulo return ret;
35785b9c547cSRui Paulo }
35795b9c547cSRui Paulo
35805b9c547cSRui Paulo
wpas_stop_pno(struct wpa_supplicant * wpa_s)35815b9c547cSRui Paulo int wpas_stop_pno(struct wpa_supplicant *wpa_s)
35825b9c547cSRui Paulo {
35835b9c547cSRui Paulo int ret = 0;
35845b9c547cSRui Paulo
35855b9c547cSRui Paulo if (!wpa_s->pno)
35865b9c547cSRui Paulo return 0;
35875b9c547cSRui Paulo
35885b9c547cSRui Paulo ret = wpa_supplicant_stop_sched_scan(wpa_s);
3589780fb4a2SCy Schubert wpa_s->sched_scan_stop_req = 1;
35905b9c547cSRui Paulo
35915b9c547cSRui Paulo wpa_s->pno = 0;
35925b9c547cSRui Paulo wpa_s->pno_sched_pending = 0;
35935b9c547cSRui Paulo
35945b9c547cSRui Paulo if (wpa_s->wpa_state == WPA_SCANNING)
35955b9c547cSRui Paulo wpa_supplicant_req_scan(wpa_s, 0, 0);
35965b9c547cSRui Paulo
35975b9c547cSRui Paulo return ret;
35985b9c547cSRui Paulo }
35995b9c547cSRui Paulo
36005b9c547cSRui Paulo
wpas_mac_addr_rand_scan_clear(struct wpa_supplicant * wpa_s,unsigned int type)36015b9c547cSRui Paulo void wpas_mac_addr_rand_scan_clear(struct wpa_supplicant *wpa_s,
36025b9c547cSRui Paulo unsigned int type)
36035b9c547cSRui Paulo {
36045b9c547cSRui Paulo type &= MAC_ADDR_RAND_ALL;
36055b9c547cSRui Paulo wpa_s->mac_addr_rand_enable &= ~type;
36065b9c547cSRui Paulo
36075b9c547cSRui Paulo if (type & MAC_ADDR_RAND_SCAN) {
36085b9c547cSRui Paulo os_free(wpa_s->mac_addr_scan);
36095b9c547cSRui Paulo wpa_s->mac_addr_scan = NULL;
36105b9c547cSRui Paulo }
36115b9c547cSRui Paulo
36125b9c547cSRui Paulo if (type & MAC_ADDR_RAND_SCHED_SCAN) {
36135b9c547cSRui Paulo os_free(wpa_s->mac_addr_sched_scan);
36145b9c547cSRui Paulo wpa_s->mac_addr_sched_scan = NULL;
36155b9c547cSRui Paulo }
36165b9c547cSRui Paulo
36175b9c547cSRui Paulo if (type & MAC_ADDR_RAND_PNO) {
36185b9c547cSRui Paulo os_free(wpa_s->mac_addr_pno);
36195b9c547cSRui Paulo wpa_s->mac_addr_pno = NULL;
36205b9c547cSRui Paulo }
36215b9c547cSRui Paulo }
36225b9c547cSRui Paulo
36235b9c547cSRui Paulo
wpas_mac_addr_rand_scan_set(struct wpa_supplicant * wpa_s,unsigned int type,const u8 * addr,const u8 * mask)36245b9c547cSRui Paulo int wpas_mac_addr_rand_scan_set(struct wpa_supplicant *wpa_s,
36255b9c547cSRui Paulo unsigned int type, const u8 *addr,
36265b9c547cSRui Paulo const u8 *mask)
36275b9c547cSRui Paulo {
36285b9c547cSRui Paulo u8 *tmp = NULL;
36295b9c547cSRui Paulo
36304bc52338SCy Schubert if ((wpa_s->mac_addr_rand_supported & type) != type ) {
36314bc52338SCy Schubert wpa_printf(MSG_INFO,
36324bc52338SCy Schubert "scan: MAC randomization type %u != supported=%u",
36334bc52338SCy Schubert type, wpa_s->mac_addr_rand_supported);
36344bc52338SCy Schubert return -1;
36354bc52338SCy Schubert }
36364bc52338SCy Schubert
36375b9c547cSRui Paulo wpas_mac_addr_rand_scan_clear(wpa_s, type);
36385b9c547cSRui Paulo
36395b9c547cSRui Paulo if (addr) {
36405b9c547cSRui Paulo tmp = os_malloc(2 * ETH_ALEN);
36415b9c547cSRui Paulo if (!tmp)
36425b9c547cSRui Paulo return -1;
36435b9c547cSRui Paulo os_memcpy(tmp, addr, ETH_ALEN);
36445b9c547cSRui Paulo os_memcpy(tmp + ETH_ALEN, mask, ETH_ALEN);
36455b9c547cSRui Paulo }
36465b9c547cSRui Paulo
36475b9c547cSRui Paulo if (type == MAC_ADDR_RAND_SCAN) {
36485b9c547cSRui Paulo wpa_s->mac_addr_scan = tmp;
36495b9c547cSRui Paulo } else if (type == MAC_ADDR_RAND_SCHED_SCAN) {
36505b9c547cSRui Paulo wpa_s->mac_addr_sched_scan = tmp;
36515b9c547cSRui Paulo } else if (type == MAC_ADDR_RAND_PNO) {
36525b9c547cSRui Paulo wpa_s->mac_addr_pno = tmp;
36535b9c547cSRui Paulo } else {
36545b9c547cSRui Paulo wpa_printf(MSG_INFO,
36555b9c547cSRui Paulo "scan: Invalid MAC randomization type=0x%x",
36565b9c547cSRui Paulo type);
36575b9c547cSRui Paulo os_free(tmp);
36585b9c547cSRui Paulo return -1;
36595b9c547cSRui Paulo }
36605b9c547cSRui Paulo
36615b9c547cSRui Paulo wpa_s->mac_addr_rand_enable |= type;
36625b9c547cSRui Paulo return 0;
36635b9c547cSRui Paulo }
3664780fb4a2SCy Schubert
3665780fb4a2SCy Schubert
wpas_mac_addr_rand_scan_get_mask(struct wpa_supplicant * wpa_s,unsigned int type,u8 * mask)3666c1d255d3SCy Schubert int wpas_mac_addr_rand_scan_get_mask(struct wpa_supplicant *wpa_s,
3667c1d255d3SCy Schubert unsigned int type, u8 *mask)
3668c1d255d3SCy Schubert {
3669c1d255d3SCy Schubert const u8 *to_copy;
3670c1d255d3SCy Schubert
3671c1d255d3SCy Schubert if ((wpa_s->mac_addr_rand_enable & type) != type)
3672c1d255d3SCy Schubert return -1;
3673c1d255d3SCy Schubert
3674c1d255d3SCy Schubert if (type == MAC_ADDR_RAND_SCAN) {
3675c1d255d3SCy Schubert to_copy = wpa_s->mac_addr_scan;
3676c1d255d3SCy Schubert } else if (type == MAC_ADDR_RAND_SCHED_SCAN) {
3677c1d255d3SCy Schubert to_copy = wpa_s->mac_addr_sched_scan;
3678c1d255d3SCy Schubert } else if (type == MAC_ADDR_RAND_PNO) {
3679c1d255d3SCy Schubert to_copy = wpa_s->mac_addr_pno;
3680c1d255d3SCy Schubert } else {
3681c1d255d3SCy Schubert wpa_printf(MSG_DEBUG,
3682c1d255d3SCy Schubert "scan: Invalid MAC randomization type=0x%x",
3683c1d255d3SCy Schubert type);
3684c1d255d3SCy Schubert return -1;
3685c1d255d3SCy Schubert }
3686c1d255d3SCy Schubert
3687c1d255d3SCy Schubert os_memcpy(mask, to_copy + ETH_ALEN, ETH_ALEN);
3688c1d255d3SCy Schubert return 0;
3689c1d255d3SCy Schubert }
3690c1d255d3SCy Schubert
3691c1d255d3SCy Schubert
wpas_abort_ongoing_scan(struct wpa_supplicant * wpa_s)3692780fb4a2SCy Schubert int wpas_abort_ongoing_scan(struct wpa_supplicant *wpa_s)
3693780fb4a2SCy Schubert {
369485732ac8SCy Schubert struct wpa_radio_work *work;
369585732ac8SCy Schubert struct wpa_radio *radio = wpa_s->radio;
3696780fb4a2SCy Schubert
369785732ac8SCy Schubert dl_list_for_each(work, &radio->work, struct wpa_radio_work, list) {
369885732ac8SCy Schubert if (work->wpa_s != wpa_s || !work->started ||
369985732ac8SCy Schubert (os_strcmp(work->type, "scan") != 0 &&
370085732ac8SCy Schubert os_strcmp(work->type, "p2p-scan") != 0))
370185732ac8SCy Schubert continue;
3702780fb4a2SCy Schubert wpa_dbg(wpa_s, MSG_DEBUG, "Abort an ongoing scan");
370385732ac8SCy Schubert return wpa_drv_abort_scan(wpa_s, wpa_s->curr_scan_cookie);
3704780fb4a2SCy Schubert }
3705780fb4a2SCy Schubert
370685732ac8SCy Schubert wpa_dbg(wpa_s, MSG_DEBUG, "No ongoing scan/p2p-scan found to abort");
370785732ac8SCy Schubert return -1;
3708780fb4a2SCy Schubert }
3709780fb4a2SCy Schubert
3710780fb4a2SCy Schubert
wpas_sched_scan_plans_set(struct wpa_supplicant * wpa_s,const char * cmd)3711780fb4a2SCy Schubert int wpas_sched_scan_plans_set(struct wpa_supplicant *wpa_s, const char *cmd)
3712780fb4a2SCy Schubert {
3713780fb4a2SCy Schubert struct sched_scan_plan *scan_plans = NULL;
3714780fb4a2SCy Schubert const char *token, *context = NULL;
3715780fb4a2SCy Schubert unsigned int num = 0;
3716780fb4a2SCy Schubert
3717780fb4a2SCy Schubert if (!cmd)
3718780fb4a2SCy Schubert return -1;
3719780fb4a2SCy Schubert
3720780fb4a2SCy Schubert if (!cmd[0]) {
3721780fb4a2SCy Schubert wpa_printf(MSG_DEBUG, "Clear sched scan plans");
3722780fb4a2SCy Schubert os_free(wpa_s->sched_scan_plans);
3723780fb4a2SCy Schubert wpa_s->sched_scan_plans = NULL;
3724780fb4a2SCy Schubert wpa_s->sched_scan_plans_num = 0;
3725780fb4a2SCy Schubert return 0;
3726780fb4a2SCy Schubert }
3727780fb4a2SCy Schubert
3728780fb4a2SCy Schubert while ((token = cstr_token(cmd, " ", &context))) {
3729780fb4a2SCy Schubert int ret;
3730780fb4a2SCy Schubert struct sched_scan_plan *scan_plan, *n;
3731780fb4a2SCy Schubert
3732780fb4a2SCy Schubert n = os_realloc_array(scan_plans, num + 1, sizeof(*scan_plans));
3733780fb4a2SCy Schubert if (!n)
3734780fb4a2SCy Schubert goto fail;
3735780fb4a2SCy Schubert
3736780fb4a2SCy Schubert scan_plans = n;
3737780fb4a2SCy Schubert scan_plan = &scan_plans[num];
3738780fb4a2SCy Schubert num++;
3739780fb4a2SCy Schubert
3740780fb4a2SCy Schubert ret = sscanf(token, "%u:%u", &scan_plan->interval,
3741780fb4a2SCy Schubert &scan_plan->iterations);
3742780fb4a2SCy Schubert if (ret <= 0 || ret > 2 || !scan_plan->interval) {
3743780fb4a2SCy Schubert wpa_printf(MSG_ERROR,
3744780fb4a2SCy Schubert "Invalid sched scan plan input: %s", token);
3745780fb4a2SCy Schubert goto fail;
3746780fb4a2SCy Schubert }
3747780fb4a2SCy Schubert
3748780fb4a2SCy Schubert if (scan_plan->interval > wpa_s->max_sched_scan_plan_interval) {
3749780fb4a2SCy Schubert wpa_printf(MSG_WARNING,
3750780fb4a2SCy Schubert "scan plan %u: Scan interval too long(%u), use the maximum allowed(%u)",
3751780fb4a2SCy Schubert num, scan_plan->interval,
3752780fb4a2SCy Schubert wpa_s->max_sched_scan_plan_interval);
3753780fb4a2SCy Schubert scan_plan->interval =
3754780fb4a2SCy Schubert wpa_s->max_sched_scan_plan_interval;
3755780fb4a2SCy Schubert }
3756780fb4a2SCy Schubert
3757780fb4a2SCy Schubert if (ret == 1) {
3758780fb4a2SCy Schubert scan_plan->iterations = 0;
3759780fb4a2SCy Schubert break;
3760780fb4a2SCy Schubert }
3761780fb4a2SCy Schubert
3762780fb4a2SCy Schubert if (!scan_plan->iterations) {
3763780fb4a2SCy Schubert wpa_printf(MSG_ERROR,
3764780fb4a2SCy Schubert "scan plan %u: Number of iterations cannot be zero",
3765780fb4a2SCy Schubert num);
3766780fb4a2SCy Schubert goto fail;
3767780fb4a2SCy Schubert }
3768780fb4a2SCy Schubert
3769780fb4a2SCy Schubert if (scan_plan->iterations >
3770780fb4a2SCy Schubert wpa_s->max_sched_scan_plan_iterations) {
3771780fb4a2SCy Schubert wpa_printf(MSG_WARNING,
3772780fb4a2SCy Schubert "scan plan %u: Too many iterations(%u), use the maximum allowed(%u)",
3773780fb4a2SCy Schubert num, scan_plan->iterations,
3774780fb4a2SCy Schubert wpa_s->max_sched_scan_plan_iterations);
3775780fb4a2SCy Schubert scan_plan->iterations =
3776780fb4a2SCy Schubert wpa_s->max_sched_scan_plan_iterations;
3777780fb4a2SCy Schubert }
3778780fb4a2SCy Schubert
3779780fb4a2SCy Schubert wpa_printf(MSG_DEBUG,
3780780fb4a2SCy Schubert "scan plan %u: interval=%u iterations=%u",
3781780fb4a2SCy Schubert num, scan_plan->interval, scan_plan->iterations);
3782780fb4a2SCy Schubert }
3783780fb4a2SCy Schubert
3784780fb4a2SCy Schubert if (!scan_plans) {
3785780fb4a2SCy Schubert wpa_printf(MSG_ERROR, "Invalid scan plans entry");
3786780fb4a2SCy Schubert goto fail;
3787780fb4a2SCy Schubert }
3788780fb4a2SCy Schubert
3789780fb4a2SCy Schubert if (cstr_token(cmd, " ", &context) || scan_plans[num - 1].iterations) {
3790780fb4a2SCy Schubert wpa_printf(MSG_ERROR,
3791780fb4a2SCy Schubert "All scan plans but the last must specify a number of iterations");
3792780fb4a2SCy Schubert goto fail;
3793780fb4a2SCy Schubert }
3794780fb4a2SCy Schubert
3795780fb4a2SCy Schubert wpa_printf(MSG_DEBUG, "scan plan %u (last plan): interval=%u",
3796780fb4a2SCy Schubert num, scan_plans[num - 1].interval);
3797780fb4a2SCy Schubert
3798780fb4a2SCy Schubert if (num > wpa_s->max_sched_scan_plans) {
3799780fb4a2SCy Schubert wpa_printf(MSG_WARNING,
3800780fb4a2SCy Schubert "Too many scheduled scan plans (only %u supported)",
3801780fb4a2SCy Schubert wpa_s->max_sched_scan_plans);
3802780fb4a2SCy Schubert wpa_printf(MSG_WARNING,
3803780fb4a2SCy Schubert "Use only the first %u scan plans, and the last one (in infinite loop)",
3804780fb4a2SCy Schubert wpa_s->max_sched_scan_plans - 1);
3805780fb4a2SCy Schubert os_memcpy(&scan_plans[wpa_s->max_sched_scan_plans - 1],
3806780fb4a2SCy Schubert &scan_plans[num - 1], sizeof(*scan_plans));
3807780fb4a2SCy Schubert num = wpa_s->max_sched_scan_plans;
3808780fb4a2SCy Schubert }
3809780fb4a2SCy Schubert
3810780fb4a2SCy Schubert os_free(wpa_s->sched_scan_plans);
3811780fb4a2SCy Schubert wpa_s->sched_scan_plans = scan_plans;
3812780fb4a2SCy Schubert wpa_s->sched_scan_plans_num = num;
3813780fb4a2SCy Schubert
3814780fb4a2SCy Schubert return 0;
3815780fb4a2SCy Schubert
3816780fb4a2SCy Schubert fail:
3817780fb4a2SCy Schubert os_free(scan_plans);
3818780fb4a2SCy Schubert wpa_printf(MSG_ERROR, "invalid scan plans list");
3819780fb4a2SCy Schubert return -1;
3820780fb4a2SCy Schubert }
3821780fb4a2SCy Schubert
3822780fb4a2SCy Schubert
3823780fb4a2SCy Schubert /**
3824780fb4a2SCy Schubert * wpas_scan_reset_sched_scan - Reset sched_scan state
3825780fb4a2SCy Schubert * @wpa_s: Pointer to wpa_supplicant data
3826780fb4a2SCy Schubert *
3827780fb4a2SCy Schubert * This function is used to cancel a running scheduled scan and to reset an
3828780fb4a2SCy Schubert * internal scan state to continue with a regular scan on the following
3829780fb4a2SCy Schubert * wpa_supplicant_req_scan() calls.
3830780fb4a2SCy Schubert */
wpas_scan_reset_sched_scan(struct wpa_supplicant * wpa_s)3831780fb4a2SCy Schubert void wpas_scan_reset_sched_scan(struct wpa_supplicant *wpa_s)
3832780fb4a2SCy Schubert {
3833780fb4a2SCy Schubert wpa_s->normal_scans = 0;
3834780fb4a2SCy Schubert if (wpa_s->sched_scanning) {
3835780fb4a2SCy Schubert wpa_s->sched_scan_timed_out = 0;
3836780fb4a2SCy Schubert wpa_s->prev_sched_ssid = NULL;
3837780fb4a2SCy Schubert wpa_supplicant_cancel_sched_scan(wpa_s);
3838780fb4a2SCy Schubert }
3839780fb4a2SCy Schubert }
3840780fb4a2SCy Schubert
3841780fb4a2SCy Schubert
wpas_scan_restart_sched_scan(struct wpa_supplicant * wpa_s)3842780fb4a2SCy Schubert void wpas_scan_restart_sched_scan(struct wpa_supplicant *wpa_s)
3843780fb4a2SCy Schubert {
3844780fb4a2SCy Schubert /* simulate timeout to restart the sched scan */
3845780fb4a2SCy Schubert wpa_s->sched_scan_timed_out = 1;
3846780fb4a2SCy Schubert wpa_s->prev_sched_ssid = NULL;
3847780fb4a2SCy Schubert wpa_supplicant_cancel_sched_scan(wpa_s);
3848780fb4a2SCy Schubert }
3849