]> git.ipfire.org Git - thirdparty/hostap.git/blame - wpa_supplicant/scan.c
Add scheduled scan driver operations
[thirdparty/hostap.git] / wpa_supplicant / scan.c
CommitLineData
6fc6879b
JM
1/*
2 * WPA Supplicant - Scanning
9ba9fa07 3 * Copyright (c) 2003-2010, Jouni Malinen <j@w1.fi>
6fc6879b
JM
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2 as
7 * published by the Free Software Foundation.
8 *
9 * Alternatively, this software may be distributed under the terms of BSD
10 * license.
11 *
12 * See README and COPYING for more details.
13 */
14
9ba9fa07 15#include "utils/includes.h"
6fc6879b 16
9ba9fa07
JM
17#include "utils/common.h"
18#include "utils/eloop.h"
19#include "common/ieee802_11_defs.h"
6fc6879b
JM
20#include "config.h"
21#include "wpa_supplicant_i.h"
2d5b792d 22#include "driver_i.h"
6fc6879b 23#include "mlme.h"
b01c18a8 24#include "wps_supplicant.h"
0e65037c
JM
25#include "p2p_supplicant.h"
26#include "p2p/p2p.h"
8bac466b 27#include "notify.h"
9ba9fa07
JM
28#include "bss.h"
29#include "scan.h"
6fc6879b
JM
30
31
32static void wpa_supplicant_gen_assoc_event(struct wpa_supplicant *wpa_s)
33{
34 struct wpa_ssid *ssid;
35 union wpa_event_data data;
36
37 ssid = wpa_supplicant_get_ssid(wpa_s);
38 if (ssid == NULL)
39 return;
40
8bac466b 41 if (wpa_s->current_ssid == NULL) {
6fc6879b 42 wpa_s->current_ssid = ssid;
8bac466b
JM
43 if (wpa_s->current_ssid != NULL)
44 wpas_notify_network_changed(wpa_s);
45 }
6fc6879b 46 wpa_supplicant_initiate_eapol(wpa_s);
f049052b
BG
47 wpa_dbg(wpa_s, MSG_DEBUG, "Already associated with a configured "
48 "network - generating associated event");
6fc6879b
JM
49 os_memset(&data, 0, sizeof(data));
50 wpa_supplicant_event(wpa_s, EVENT_ASSOC, &data);
51}
52
53
ad08c363 54#ifdef CONFIG_WPS
5f738a21 55static int wpas_wps_in_use(struct wpa_supplicant *wpa_s,
f90c86d4 56 enum wps_request_type *req_type)
ad08c363
JM
57{
58 struct wpa_ssid *ssid;
59 int wps = 0;
ad08c363 60
5f738a21 61 for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
ad08c363
JM
62 if (!(ssid->key_mgmt & WPA_KEY_MGMT_WPS))
63 continue;
64
65 wps = 1;
b01c18a8 66 *req_type = wpas_wps_get_req_type(ssid);
ad08c363
JM
67 if (!ssid->eap.phase1)
68 continue;
69
ad08c363
JM
70 if (os_strstr(ssid->eap.phase1, "pbc=1"))
71 return 2;
72 }
73
5f738a21
LC
74#ifdef CONFIG_P2P
75 wpa_s->wps->dev.p2p = 1;
76 if (!wps) {
77 wps = 1;
78 *req_type = WPS_REQ_ENROLLEE_INFO;
79 }
80#endif /* CONFIG_P2P */
81
ad08c363
JM
82 return wps;
83}
84#endif /* CONFIG_WPS */
85
e76baaac 86
4f34d51a 87int wpa_supplicant_enabled_networks(struct wpa_config *conf)
e76baaac
JM
88{
89 struct wpa_ssid *ssid = conf->ssid;
5471c343 90 int count = 0;
e76baaac
JM
91 while (ssid) {
92 if (!ssid->disabled)
5471c343 93 count++;
e76baaac
JM
94 ssid = ssid->next;
95 }
5471c343 96 return count;
e76baaac
JM
97}
98
99
100static void wpa_supplicant_assoc_try(struct wpa_supplicant *wpa_s,
101 struct wpa_ssid *ssid)
102{
103 while (ssid) {
104 if (!ssid->disabled)
105 break;
106 ssid = ssid->next;
107 }
108
109 /* ap_scan=2 mode - try to associate with each SSID. */
110 if (ssid == NULL) {
f049052b
BG
111 wpa_dbg(wpa_s, MSG_DEBUG, "wpa_supplicant_assoc_try: Reached "
112 "end of scan list - go back to beginning");
ba2a573c 113 wpa_s->prev_scan_ssid = WILDCARD_SSID_SCAN;
e76baaac
JM
114 wpa_supplicant_req_scan(wpa_s, 0, 0);
115 return;
116 }
117 if (ssid->next) {
118 /* Continue from the next SSID on the next attempt. */
119 wpa_s->prev_scan_ssid = ssid;
120 } else {
121 /* Start from the beginning of the SSID list. */
ba2a573c 122 wpa_s->prev_scan_ssid = WILDCARD_SSID_SCAN;
e76baaac
JM
123 }
124 wpa_supplicant_associate(wpa_s, NULL, ssid);
125}
126
127
d3a98225
JM
128static int int_array_len(const int *a)
129{
130 int i;
131 for (i = 0; a && a[i]; i++)
132 ;
133 return i;
134}
135
136
137static void int_array_concat(int **res, const int *a)
138{
139 int reslen, alen, i;
140 int *n;
141
142 reslen = int_array_len(*res);
143 alen = int_array_len(a);
144
145 n = os_realloc(*res, (reslen + alen + 1) * sizeof(int));
146 if (n == NULL) {
147 os_free(*res);
148 *res = NULL;
e6c0ebff 149 return;
d3a98225
JM
150 }
151 for (i = 0; i <= alen; i++)
152 n[reslen + i] = a[i];
153 *res = n;
154}
155
156
157static int freq_cmp(const void *a, const void *b)
158{
159 int _a = *(int *) a;
160 int _b = *(int *) b;
161
162 if (_a == 0)
163 return 1;
164 if (_b == 0)
165 return -1;
166 return _a - _b;
167}
168
169
170static void int_array_sort_unique(int *a)
171{
172 int alen;
173 int i, j;
174
175 if (a == NULL)
176 return;
177
178 alen = int_array_len(a);
179 qsort(a, alen, sizeof(int), freq_cmp);
180
181 i = 0;
182 j = 1;
183 while (a[i] && a[j]) {
184 if (a[i] == a[j]) {
185 j++;
186 continue;
187 }
188 a[++i] = a[j++];
189 }
190 if (a[i])
191 i++;
192 a[i] = 0;
193}
194
195
60b94c98
JM
196int wpa_supplicant_trigger_scan(struct wpa_supplicant *wpa_s,
197 struct wpa_driver_scan_params *params)
198{
199 int ret;
200
201 wpa_supplicant_notify_scanning(wpa_s, 1);
202
d009a9da
JM
203 if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME)
204 ret = ieee80211_sta_req_scan(wpa_s, params);
205 else
60b94c98 206 ret = wpa_drv_scan(wpa_s, params);
60b94c98
JM
207
208 if (ret) {
209 wpa_supplicant_notify_scanning(wpa_s, 0);
210 wpas_notify_scan_done(wpa_s, 0);
211 } else
212 wpa_s->scan_runs++;
213
214 return ret;
215}
216
217
cbdf3507
LC
218static void
219wpa_supplicant_sched_scan_timeout(void *eloop_ctx, void *timeout_ctx)
220{
221 struct wpa_supplicant *wpa_s = eloop_ctx;
222
223 wpa_dbg(wpa_s, MSG_DEBUG, "Sched scan timeout - stopping it");
224
225 wpa_s->sched_scan_timed_out = 1;
226 wpa_supplicant_cancel_sched_scan(wpa_s);
227}
228
229
230static int
231wpa_supplicant_start_sched_scan(struct wpa_supplicant *wpa_s,
232 struct wpa_driver_scan_params *params,
233 int interval)
234{
235 int ret;
236
237 if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME)
238 return -1;
239
240 wpa_supplicant_notify_scanning(wpa_s, 1);
241 ret = wpa_drv_sched_scan(wpa_s, params, interval * 1000);
242 if (ret)
243 wpa_supplicant_notify_scanning(wpa_s, 0);
244 else
245 wpa_s->sched_scanning = 1;
246
247 return ret;
248}
249
250
251static int wpa_supplicant_stop_sched_scan(struct wpa_supplicant *wpa_s)
252{
253 int ret;
254
255 ret = wpa_drv_stop_sched_scan(wpa_s);
256 if (ret) {
257 wpa_dbg(wpa_s, MSG_DEBUG, "stopping sched_scan failed!");
258 /* TODO: what to do if stopping fails? */
259 return -1;
260 }
261
262 return ret;
263}
264
265
3812464c
JM
266static struct wpa_driver_scan_filter *
267wpa_supplicant_build_filter_ssids(struct wpa_config *conf, size_t *num_ssids)
268{
269 struct wpa_driver_scan_filter *ssids;
270 struct wpa_ssid *ssid;
271 size_t count;
272
273 *num_ssids = 0;
274 if (!conf->filter_ssids)
275 return NULL;
276
277 for (count = 0, ssid = conf->ssid; ssid; ssid = ssid->next) {
278 if (ssid->ssid && ssid->ssid_len)
279 count++;
280 }
281 if (count == 0)
282 return NULL;
283 ssids = os_zalloc(count * sizeof(struct wpa_driver_scan_filter));
284 if (ssids == NULL)
285 return NULL;
286
287 for (ssid = conf->ssid; ssid; ssid = ssid->next) {
288 if (!ssid->ssid || !ssid->ssid_len)
289 continue;
290 os_memcpy(ssids[*num_ssids].ssid, ssid->ssid, ssid->ssid_len);
291 ssids[*num_ssids].ssid_len = ssid->ssid_len;
292 (*num_ssids)++;
293 }
294
295 return ssids;
296}
297
298
5f738a21
LC
299static void wpa_supplicant_optimize_freqs(
300 struct wpa_supplicant *wpa_s, struct wpa_driver_scan_params *params)
301{
302#ifdef CONFIG_P2P
303 if (params->freqs == NULL && wpa_s->p2p_in_provisioning &&
304 wpa_s->go_params) {
305 /* Optimize provisioning state scan based on GO information */
306 if (wpa_s->p2p_in_provisioning < 5 &&
307 wpa_s->go_params->freq > 0) {
308 wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Scan only GO "
309 "preferred frequency %d MHz",
310 wpa_s->go_params->freq);
311 params->freqs = os_zalloc(2 * sizeof(int));
312 if (params->freqs)
313 params->freqs[0] = wpa_s->go_params->freq;
314 } else if (wpa_s->p2p_in_provisioning < 8 &&
315 wpa_s->go_params->freq_list[0]) {
316 wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Scan only common "
317 "channels");
318 int_array_concat(&params->freqs,
319 wpa_s->go_params->freq_list);
320 if (params->freqs)
321 int_array_sort_unique(params->freqs);
322 }
323 wpa_s->p2p_in_provisioning++;
324 }
325#endif /* CONFIG_P2P */
326
327#ifdef CONFIG_WPS
328 if (params->freqs == NULL && wpa_s->after_wps && wpa_s->wps_freq) {
329 /*
330 * Optimize post-provisioning scan based on channel used
331 * during provisioning.
332 */
333 wpa_dbg(wpa_s, MSG_DEBUG, "WPS: Scan only frequency %u MHz "
334 "that was used during provisioning", wpa_s->wps_freq);
335 params->freqs = os_zalloc(2 * sizeof(int));
336 if (params->freqs)
337 params->freqs[0] = wpa_s->wps_freq;
338 wpa_s->after_wps--;
339 }
340
341#endif /* CONFIG_WPS */
342}
343
344
345static struct wpabuf *
346wpa_supplicant_extra_ies(struct wpa_supplicant *wpa_s,
347 struct wpa_driver_scan_params *params)
6fc6879b 348{
ad08c363 349 struct wpabuf *wps_ie = NULL;
b01c18a8 350#ifdef CONFIG_WPS
509a3972 351 int wps = 0;
f90c86d4 352 enum wps_request_type req_type = WPS_REQ_ENROLLEE_INFO;
5f738a21
LC
353
354 wps = wpas_wps_in_use(wpa_s, &req_type);
355
356 if (wps) {
357 wps_ie = wps_build_probe_req_ie(wps == 2, &wpa_s->wps->dev,
358 wpa_s->wps->uuid, req_type,
359 0, NULL);
360 if (wps_ie) {
361 params->extra_ies = wpabuf_head(wps_ie);
362 params->extra_ies_len = wpabuf_len(wps_ie);
363 }
364 }
365
366#ifdef CONFIG_P2P
367 if (wps_ie) {
368 size_t ielen = p2p_scan_ie_buf_len(wpa_s->global->p2p);
369 if (wpabuf_resize(&wps_ie, ielen) == 0) {
370 wpas_p2p_scan_ie(wpa_s, wps_ie);
371 params->extra_ies = wpabuf_head(wps_ie);
372 params->extra_ies_len = wpabuf_len(wps_ie);
373 }
374 }
375#endif /* CONFIG_P2P */
376
b01c18a8 377#endif /* CONFIG_WPS */
5f738a21
LC
378
379 return wps_ie;
380}
381
382
383static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx)
384{
385 struct wpa_supplicant *wpa_s = eloop_ctx;
386 struct wpa_ssid *ssid;
387 int scan_req = 0, ret;
388 struct wpabuf *wps_ie;
e76baaac
JM
389 struct wpa_driver_scan_params params;
390 size_t max_ssids;
207ef3fb 391 enum wpa_states prev_state;
6fc6879b 392
8401a6b0 393 if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) {
f049052b 394 wpa_dbg(wpa_s, MSG_DEBUG, "Skip scan - interface disabled");
8401a6b0
JM
395 return;
396 }
397
3180d7a2
SO
398 if (wpa_s->disconnected && !wpa_s->scan_req) {
399 wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
6fc6879b 400 return;
3180d7a2 401 }
6fc6879b 402
e76baaac
JM
403 if (!wpa_supplicant_enabled_networks(wpa_s->conf) &&
404 !wpa_s->scan_req) {
f049052b 405 wpa_dbg(wpa_s, MSG_DEBUG, "No enabled networks - do not scan");
6fc6879b
JM
406 wpa_supplicant_set_state(wpa_s, WPA_INACTIVE);
407 return;
408 }
6fc6879b 409
c2a04078
JM
410 if (wpa_s->conf->ap_scan != 0 &&
411 (wpa_s->drv_flags & WPA_DRIVER_FLAGS_WIRED)) {
f049052b
BG
412 wpa_dbg(wpa_s, MSG_DEBUG, "Using wired authentication - "
413 "overriding ap_scan configuration");
6fc6879b 414 wpa_s->conf->ap_scan = 0;
8bac466b 415 wpas_notify_ap_scan_changed(wpa_s);
6fc6879b
JM
416 }
417
418 if (wpa_s->conf->ap_scan == 0) {
419 wpa_supplicant_gen_assoc_event(wpa_s);
420 return;
421 }
422
303f60d3
JM
423#ifdef CONFIG_P2P
424 if (wpas_p2p_in_progress(wpa_s)) {
425 if (wpa_s->wpa_state == WPA_SCANNING) {
426 wpa_dbg(wpa_s, MSG_DEBUG, "Delay station mode scan "
427 "while P2P operation is in progress");
428 wpa_supplicant_req_scan(wpa_s, 5, 0);
429 } else {
430 wpa_dbg(wpa_s, MSG_DEBUG, "Do not request scan while "
431 "P2P operation is in progress");
432 }
433 return;
434 }
435#endif /* CONFIG_P2P */
436
c2a04078
JM
437 if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME) ||
438 wpa_s->conf->ap_scan == 2)
e76baaac
JM
439 max_ssids = 1;
440 else {
441 max_ssids = wpa_s->max_scan_ssids;
442 if (max_ssids > WPAS_MAX_SCAN_SSIDS)
443 max_ssids = WPAS_MAX_SCAN_SSIDS;
444 }
445
e76baaac
JM
446 scan_req = wpa_s->scan_req;
447 wpa_s->scan_req = 0;
448
449 os_memset(&params, 0, sizeof(params));
450
207ef3fb 451 prev_state = wpa_s->wpa_state;
6fc6879b
JM
452 if (wpa_s->wpa_state == WPA_DISCONNECTED ||
453 wpa_s->wpa_state == WPA_INACTIVE)
454 wpa_supplicant_set_state(wpa_s, WPA_SCANNING);
455
7dcdcfd6
JM
456 if (scan_req != 2 && wpa_s->connect_without_scan) {
457 for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
458 if (ssid == wpa_s->connect_without_scan)
459 break;
460 }
461 wpa_s->connect_without_scan = NULL;
462 if (ssid) {
463 wpa_printf(MSG_DEBUG, "Start a pre-selected network "
464 "without scan step");
465 wpa_supplicant_associate(wpa_s, NULL, ssid);
466 return;
467 }
468 }
469
e76baaac 470 /* Find the starting point from which to continue scanning */
6fc6879b 471 ssid = wpa_s->conf->ssid;
ba2a573c 472 if (wpa_s->prev_scan_ssid != WILDCARD_SSID_SCAN) {
6fc6879b
JM
473 while (ssid) {
474 if (ssid == wpa_s->prev_scan_ssid) {
475 ssid = ssid->next;
476 break;
477 }
478 ssid = ssid->next;
479 }
480 }
6fc6879b 481
7dcdcfd6
JM
482 if (scan_req != 2 && wpa_s->conf->ap_scan == 2) {
483 wpa_s->connect_without_scan = NULL;
e76baaac
JM
484 wpa_supplicant_assoc_try(wpa_s, ssid);
485 return;
486 } else if (wpa_s->conf->ap_scan == 2) {
6fc6879b 487 /*
ba2a573c
JM
488 * User-initiated scan request in ap_scan == 2; scan with
489 * wildcard SSID.
6fc6879b 490 */
e76baaac
JM
491 ssid = NULL;
492 } else {
5be45e2e 493 struct wpa_ssid *start = ssid, *tssid;
d3a98225 494 int freqs_set = 0;
e76baaac
JM
495 if (ssid == NULL && max_ssids > 1)
496 ssid = wpa_s->conf->ssid;
497 while (ssid) {
498 if (!ssid->disabled && ssid->scan_ssid) {
499 wpa_hexdump_ascii(MSG_DEBUG, "Scan SSID",
500 ssid->ssid, ssid->ssid_len);
501 params.ssids[params.num_ssids].ssid =
502 ssid->ssid;
503 params.ssids[params.num_ssids].ssid_len =
504 ssid->ssid_len;
505 params.num_ssids++;
506 if (params.num_ssids + 1 >= max_ssids)
507 break;
508 }
509 ssid = ssid->next;
510 if (ssid == start)
511 break;
512 if (ssid == NULL && max_ssids > 1 &&
513 start != wpa_s->conf->ssid)
514 ssid = wpa_s->conf->ssid;
6fc6879b 515 }
d3a98225 516
5be45e2e
JM
517 for (tssid = wpa_s->conf->ssid; tssid; tssid = tssid->next) {
518 if (tssid->disabled)
d3a98225 519 continue;
5be45e2e 520 if ((params.freqs || !freqs_set) && tssid->scan_freq) {
d3a98225 521 int_array_concat(&params.freqs,
5be45e2e 522 tssid->scan_freq);
d3a98225
JM
523 } else {
524 os_free(params.freqs);
525 params.freqs = NULL;
526 }
527 freqs_set = 1;
528 }
529 int_array_sort_unique(params.freqs);
6fc6879b
JM
530 }
531
6fc6879b 532 if (ssid) {
6fc6879b 533 wpa_s->prev_scan_ssid = ssid;
e76baaac 534 if (max_ssids > 1) {
f049052b
BG
535 wpa_dbg(wpa_s, MSG_DEBUG, "Include wildcard SSID in "
536 "the scan request");
ba2a573c 537 params.num_ssids++;
e76baaac 538 }
f049052b
BG
539 wpa_dbg(wpa_s, MSG_DEBUG, "Starting AP scan for specific "
540 "SSID(s)");
e76baaac 541 } else {
ba2a573c 542 wpa_s->prev_scan_ssid = WILDCARD_SSID_SCAN;
e76baaac 543 params.num_ssids++;
f049052b
BG
544 wpa_dbg(wpa_s, MSG_DEBUG, "Starting AP scan for wildcard "
545 "SSID");
6fc6879b
JM
546 }
547
5f738a21
LC
548 wpa_supplicant_optimize_freqs(wpa_s, &params);
549 wps_ie = wpa_supplicant_extra_ies(wpa_s, &params);
0e65037c 550
f47d639d 551 if (params.freqs == NULL && wpa_s->next_scan_freqs) {
f049052b
BG
552 wpa_dbg(wpa_s, MSG_DEBUG, "Optimize scan based on previously "
553 "generated frequency list");
f47d639d
JM
554 params.freqs = wpa_s->next_scan_freqs;
555 } else
556 os_free(wpa_s->next_scan_freqs);
557 wpa_s->next_scan_freqs = NULL;
558
3812464c
JM
559 params.filter_ssids = wpa_supplicant_build_filter_ssids(
560 wpa_s->conf, &params.num_filter_ssids);
561
60b94c98 562 ret = wpa_supplicant_trigger_scan(wpa_s, &params);
6fc6879b 563
ad08c363 564 wpabuf_free(wps_ie);
d3a98225 565 os_free(params.freqs);
3812464c 566 os_free(params.filter_ssids);
ad08c363 567
6fc6879b 568 if (ret) {
f049052b 569 wpa_msg(wpa_s, MSG_WARNING, "Failed to initiate AP scan");
207ef3fb
JM
570 if (prev_state != wpa_s->wpa_state)
571 wpa_supplicant_set_state(wpa_s, prev_state);
1c4c9c50 572 wpa_supplicant_req_scan(wpa_s, 1, 0);
d902a9c1 573 }
6fc6879b
JM
574}
575
576
577/**
578 * wpa_supplicant_req_scan - Schedule a scan for neighboring access points
579 * @wpa_s: Pointer to wpa_supplicant data
580 * @sec: Number of seconds after which to scan
581 * @usec: Number of microseconds after which to scan
582 *
583 * This function is used to schedule a scan for neighboring access points after
584 * the specified time.
585 */
586void wpa_supplicant_req_scan(struct wpa_supplicant *wpa_s, int sec, int usec)
587{
7e148849
DW
588 /* If there's at least one network that should be specifically scanned
589 * then don't cancel the scan and reschedule. Some drivers do
590 * background scanning which generates frequent scan results, and that
591 * causes the specific SSID scan to get continually pushed back and
592 * never happen, which causes hidden APs to never get probe-scanned.
593 */
594 if (eloop_is_timeout_registered(wpa_supplicant_scan, wpa_s, NULL) &&
595 wpa_s->conf->ap_scan == 1) {
596 struct wpa_ssid *ssid = wpa_s->conf->ssid;
597
598 while (ssid) {
599 if (!ssid->disabled && ssid->scan_ssid)
600 break;
601 ssid = ssid->next;
602 }
603 if (ssid) {
f049052b 604 wpa_dbg(wpa_s, MSG_DEBUG, "Not rescheduling scan to "
7e148849
DW
605 "ensure that specific SSID scans occur");
606 return;
607 }
608 }
609
f049052b 610 wpa_dbg(wpa_s, MSG_DEBUG, "Setting scan request: %d sec %d usec",
6fc6879b
JM
611 sec, usec);
612 eloop_cancel_timeout(wpa_supplicant_scan, wpa_s, NULL);
613 eloop_register_timeout(sec, usec, wpa_supplicant_scan, wpa_s, NULL);
614}
615
616
cbdf3507
LC
617/**
618 * wpa_supplicant_req_sched_scan - Start a periodic scheduled scan
619 * @wpa_s: Pointer to wpa_supplicant data
620 *
621 * This function is used to schedule periodic scans for neighboring
622 * access points repeating the scan continuously.
623 */
624int wpa_supplicant_req_sched_scan(struct wpa_supplicant *wpa_s)
625{
626 struct wpa_driver_scan_params params;
627 enum wpa_states prev_state;
628 struct wpa_ssid *ssid;
629 struct wpabuf *wps_ie = NULL;
630 int ret;
631 int use_wildcard = 0;
632 unsigned int max_sched_scan_ssids;
633
634 if (!wpa_s->sched_scan_supported)
635 return -1;
636
637 if (wpa_s->max_sched_scan_ssids > WPAS_MAX_SCAN_SSIDS)
638 max_sched_scan_ssids = WPAS_MAX_SCAN_SSIDS;
639 else
640 max_sched_scan_ssids = wpa_s->max_sched_scan_ssids;
641
642 if (wpa_s->sched_scanning)
643 return 0;
644
645 os_memset(&params, 0, sizeof(params));
646
647 prev_state = wpa_s->wpa_state;
648 if (wpa_s->wpa_state == WPA_DISCONNECTED ||
649 wpa_s->wpa_state == WPA_INACTIVE)
650 wpa_supplicant_set_state(wpa_s, WPA_SCANNING);
651
652 /* Find the starting point from which to continue scanning */
653 ssid = wpa_s->conf->ssid;
654 if (wpa_s->prev_sched_ssid) {
655 while (ssid) {
656 if (ssid == wpa_s->prev_sched_ssid) {
657 ssid = ssid->next;
658 break;
659 }
660 ssid = ssid->next;
661 }
662 }
663
664 if (!ssid || !wpa_s->prev_sched_ssid) {
665 wpa_dbg(wpa_s, MSG_DEBUG, "Beginning of SSID list");
666
667 wpa_s->sched_scan_interval = 2;
668 wpa_s->sched_scan_timeout = max_sched_scan_ssids * 2;
669 wpa_s->first_sched_scan = 1;
670 ssid = wpa_s->conf->ssid;
671 wpa_s->prev_sched_ssid = ssid;
672 }
673
674 while (ssid) {
675 if (ssid->disabled) {
676 wpa_s->prev_sched_ssid = ssid;
677 ssid = ssid->next;
678 continue;
679 }
680
681 if (!ssid->scan_ssid)
682 use_wildcard = 1;
683 else {
684 params.ssids[params.num_ssids].ssid =
685 ssid->ssid;
686 params.ssids[params.num_ssids].ssid_len =
687 ssid->ssid_len;
688 params.num_ssids++;
689 if (params.num_ssids + 1 >= max_sched_scan_ssids) {
690 wpa_s->prev_sched_ssid = ssid;
691 break;
692 }
693 }
694 wpa_s->prev_sched_ssid = ssid;
695 ssid = ssid->next;
696 }
697
698 if (ssid || use_wildcard) {
699 wpa_dbg(wpa_s, MSG_DEBUG, "Include wildcard SSID in "
700 "the sched scan request");
701 params.num_ssids++;
702 } else {
703 wpa_dbg(wpa_s, MSG_DEBUG, "ssid %p - list ended", ssid);
704 }
705
706 if (!params.num_ssids)
707 return 0;
708
709 if (wpa_s->wps)
710 wps_ie = wpa_supplicant_extra_ies(wpa_s, &params);
711
712 wpa_dbg(wpa_s, MSG_DEBUG,
713 "Starting sched scan: interval %d timeout %d",
714 wpa_s->sched_scan_interval, wpa_s->sched_scan_timeout);
715
716 ret = wpa_supplicant_start_sched_scan(wpa_s, &params,
717 wpa_s->sched_scan_interval);
718 wpabuf_free(wps_ie);
719 if (ret) {
720 wpa_msg(wpa_s, MSG_WARNING, "Failed to initiate sched scan");
721 if (prev_state != wpa_s->wpa_state)
722 wpa_supplicant_set_state(wpa_s, prev_state);
723 return ret;
724 }
725
726 /* If we have more SSIDs to scan, add a timeout so we scan them too */
727 if (ssid || !wpa_s->first_sched_scan) {
728 wpa_s->sched_scan_timed_out = 0;
729 eloop_register_timeout(wpa_s->sched_scan_timeout, 0,
730 wpa_supplicant_sched_scan_timeout,
731 wpa_s, NULL);
732 wpa_s->first_sched_scan = 0;
733 wpa_s->sched_scan_timeout /= 2;
734 wpa_s->sched_scan_interval *= 2;
735 }
736
737 return 0;
738}
739
740
6fc6879b
JM
741/**
742 * wpa_supplicant_cancel_scan - Cancel a scheduled scan request
743 * @wpa_s: Pointer to wpa_supplicant data
744 *
745 * This function is used to cancel a scan request scheduled with
746 * wpa_supplicant_req_scan().
747 */
748void wpa_supplicant_cancel_scan(struct wpa_supplicant *wpa_s)
749{
f049052b 750 wpa_dbg(wpa_s, MSG_DEBUG, "Cancelling scan request");
6fc6879b
JM
751 eloop_cancel_timeout(wpa_supplicant_scan, wpa_s, NULL);
752}
cb8564b1
DW
753
754
cbdf3507
LC
755/**
756 * wpa_supplicant_cancel_sched_scan - Stop running scheduled scans
757 * @wpa_s: Pointer to wpa_supplicant data
758 *
759 * This function is used to stop a periodic scheduled scan.
760 */
761void wpa_supplicant_cancel_sched_scan(struct wpa_supplicant *wpa_s)
762{
763 if (!wpa_s->sched_scanning)
764 return;
765
766 wpa_dbg(wpa_s, MSG_DEBUG, "Cancelling sched scan");
767 eloop_cancel_timeout(wpa_supplicant_sched_scan_timeout, wpa_s, NULL);
768 wpa_supplicant_stop_sched_scan(wpa_s);
769}
770
771
cb8564b1
DW
772void wpa_supplicant_notify_scanning(struct wpa_supplicant *wpa_s,
773 int scanning)
774{
775 if (wpa_s->scanning != scanning) {
776 wpa_s->scanning = scanning;
8bac466b 777 wpas_notify_scanning(wpa_s);
cb8564b1
DW
778 }
779}
780
9ba9fa07
JM
781
782static int wpa_scan_get_max_rate(const struct wpa_scan_res *res)
783{
784 int rate = 0;
785 const u8 *ie;
786 int i;
787
788 ie = wpa_scan_get_ie(res, WLAN_EID_SUPP_RATES);
789 for (i = 0; ie && i < ie[1]; i++) {
790 if ((ie[i + 2] & 0x7f) > rate)
791 rate = ie[i + 2] & 0x7f;
792 }
793
794 ie = wpa_scan_get_ie(res, WLAN_EID_EXT_SUPP_RATES);
795 for (i = 0; ie && i < ie[1]; i++) {
796 if ((ie[i + 2] & 0x7f) > rate)
797 rate = ie[i + 2] & 0x7f;
798 }
799
800 return rate;
801}
802
803
d1f9c410
JM
804const u8 * wpa_scan_get_ie(const struct wpa_scan_res *res, u8 ie)
805{
806 const u8 *end, *pos;
807
808 pos = (const u8 *) (res + 1);
809 end = pos + res->ie_len;
810
811 while (pos + 1 < end) {
812 if (pos + 2 + pos[1] > end)
813 break;
814 if (pos[0] == ie)
815 return pos;
816 pos += 2 + pos[1];
817 }
818
819 return NULL;
820}
821
822
9ba9fa07
JM
823const u8 * wpa_scan_get_vendor_ie(const struct wpa_scan_res *res,
824 u32 vendor_type)
825{
826 const u8 *end, *pos;
827
828 pos = (const u8 *) (res + 1);
829 end = pos + res->ie_len;
830
831 while (pos + 1 < end) {
832 if (pos + 2 + pos[1] > end)
833 break;
834 if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
835 vendor_type == WPA_GET_BE32(&pos[2]))
836 return pos;
837 pos += 2 + pos[1];
838 }
839
840 return NULL;
841}
842
843
844struct wpabuf * wpa_scan_get_vendor_ie_multi(const struct wpa_scan_res *res,
845 u32 vendor_type)
846{
847 struct wpabuf *buf;
848 const u8 *end, *pos;
849
850 buf = wpabuf_alloc(res->ie_len);
851 if (buf == NULL)
852 return NULL;
853
854 pos = (const u8 *) (res + 1);
855 end = pos + res->ie_len;
856
54f489be
JM
857 while (pos + 1 < end) {
858 if (pos + 2 + pos[1] > end)
859 break;
860 if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
861 vendor_type == WPA_GET_BE32(&pos[2]))
862 wpabuf_put_data(buf, pos + 2 + 4, pos[1] - 4);
863 pos += 2 + pos[1];
864 }
865
866 if (wpabuf_len(buf) == 0) {
867 wpabuf_free(buf);
868 buf = NULL;
869 }
870
871 return buf;
872}
873
874
875struct wpabuf * wpa_scan_get_vendor_ie_multi_beacon(
876 const struct wpa_scan_res *res, u32 vendor_type)
877{
878 struct wpabuf *buf;
879 const u8 *end, *pos;
880
881 if (res->beacon_ie_len == 0)
882 return NULL;
883 buf = wpabuf_alloc(res->beacon_ie_len);
884 if (buf == NULL)
885 return NULL;
886
887 pos = (const u8 *) (res + 1);
888 pos += res->ie_len;
889 end = pos + res->beacon_ie_len;
890
9ba9fa07
JM
891 while (pos + 1 < end) {
892 if (pos + 2 + pos[1] > end)
893 break;
894 if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
895 vendor_type == WPA_GET_BE32(&pos[2]))
896 wpabuf_put_data(buf, pos + 2 + 4, pos[1] - 4);
897 pos += 2 + pos[1];
898 }
899
900 if (wpabuf_len(buf) == 0) {
901 wpabuf_free(buf);
902 buf = NULL;
903 }
904
905 return buf;
906}
907
908
909/* Compare function for sorting scan results. Return >0 if @b is considered
910 * better. */
911static int wpa_scan_result_compar(const void *a, const void *b)
912{
913 struct wpa_scan_res **_wa = (void *) a;
914 struct wpa_scan_res **_wb = (void *) b;
915 struct wpa_scan_res *wa = *_wa;
916 struct wpa_scan_res *wb = *_wb;
917 int wpa_a, wpa_b, maxrate_a, maxrate_b;
918
919 /* WPA/WPA2 support preferred */
920 wpa_a = wpa_scan_get_vendor_ie(wa, WPA_IE_VENDOR_TYPE) != NULL ||
921 wpa_scan_get_ie(wa, WLAN_EID_RSN) != NULL;
922 wpa_b = wpa_scan_get_vendor_ie(wb, WPA_IE_VENDOR_TYPE) != NULL ||
923 wpa_scan_get_ie(wb, WLAN_EID_RSN) != NULL;
924
925 if (wpa_b && !wpa_a)
926 return 1;
927 if (!wpa_b && wpa_a)
928 return -1;
929
930 /* privacy support preferred */
931 if ((wa->caps & IEEE80211_CAP_PRIVACY) == 0 &&
932 (wb->caps & IEEE80211_CAP_PRIVACY))
933 return 1;
934 if ((wa->caps & IEEE80211_CAP_PRIVACY) &&
935 (wb->caps & IEEE80211_CAP_PRIVACY) == 0)
936 return -1;
937
938 /* best/max rate preferred if signal level close enough XXX */
939 if ((wa->level && wb->level && abs(wb->level - wa->level) < 5) ||
940 (wa->qual && wb->qual && abs(wb->qual - wa->qual) < 10)) {
941 maxrate_a = wpa_scan_get_max_rate(wa);
942 maxrate_b = wpa_scan_get_max_rate(wb);
943 if (maxrate_a != maxrate_b)
944 return maxrate_b - maxrate_a;
945 }
946
947 /* use freq for channel preference */
948
949 /* all things being equal, use signal level; if signal levels are
950 * identical, use quality values since some drivers may only report
951 * that value and leave the signal level zero */
952 if (wb->level == wa->level)
953 return wb->qual - wa->qual;
954 return wb->level - wa->level;
955}
956
957
41e650ae
JM
958#ifdef CONFIG_WPS
959/* Compare function for sorting scan results when searching a WPS AP for
960 * provisioning. Return >0 if @b is considered better. */
961static int wpa_scan_result_wps_compar(const void *a, const void *b)
962{
963 struct wpa_scan_res **_wa = (void *) a;
964 struct wpa_scan_res **_wb = (void *) b;
965 struct wpa_scan_res *wa = *_wa;
966 struct wpa_scan_res *wb = *_wb;
967 int uses_wps_a, uses_wps_b;
968 struct wpabuf *wps_a, *wps_b;
969 int res;
970
971 /* Optimization - check WPS IE existence before allocated memory and
972 * doing full reassembly. */
973 uses_wps_a = wpa_scan_get_vendor_ie(wa, WPS_IE_VENDOR_TYPE) != NULL;
974 uses_wps_b = wpa_scan_get_vendor_ie(wb, WPS_IE_VENDOR_TYPE) != NULL;
975 if (uses_wps_a && !uses_wps_b)
976 return -1;
977 if (!uses_wps_a && uses_wps_b)
978 return 1;
979
980 if (uses_wps_a && uses_wps_b) {
981 wps_a = wpa_scan_get_vendor_ie_multi(wa, WPS_IE_VENDOR_TYPE);
982 wps_b = wpa_scan_get_vendor_ie_multi(wb, WPS_IE_VENDOR_TYPE);
983 res = wps_ap_priority_compar(wps_a, wps_b);
984 wpabuf_free(wps_a);
985 wpabuf_free(wps_b);
986 if (res)
987 return res;
988 }
989
990 /*
991 * Do not use current AP security policy as a sorting criteria during
992 * WPS provisioning step since the AP may get reconfigured at the
993 * completion of provisioning.
994 */
995
996 /* all things being equal, use signal level; if signal levels are
997 * identical, use quality values since some drivers may only report
998 * that value and leave the signal level zero */
999 if (wb->level == wa->level)
1000 return wb->qual - wa->qual;
1001 return wb->level - wa->level;
1002}
1003#endif /* CONFIG_WPS */
1004
1005
9ba9fa07
JM
1006/**
1007 * wpa_supplicant_get_scan_results - Get scan results
1008 * @wpa_s: Pointer to wpa_supplicant data
1009 * @info: Information about what was scanned or %NULL if not available
1010 * @new_scan: Whether a new scan was performed
1011 * Returns: Scan results, %NULL on failure
1012 *
1013 * This function request the current scan results from the driver and updates
1014 * the local BSS list wpa_s->bss. The caller is responsible for freeing the
1015 * results with wpa_scan_results_free().
1016 */
1017struct wpa_scan_results *
1018wpa_supplicant_get_scan_results(struct wpa_supplicant *wpa_s,
1019 struct scan_info *info, int new_scan)
1020{
1021 struct wpa_scan_results *scan_res;
1022 size_t i;
41e650ae 1023 int (*compar)(const void *, const void *) = wpa_scan_result_compar;
9ba9fa07
JM
1024
1025 if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME)
1026 scan_res = ieee80211_sta_get_scan_results(wpa_s);
1027 else
1028 scan_res = wpa_drv_get_scan_results2(wpa_s);
1029 if (scan_res == NULL) {
f049052b 1030 wpa_dbg(wpa_s, MSG_DEBUG, "Failed to get scan results");
9ba9fa07
JM
1031 return NULL;
1032 }
1033
41e650ae
JM
1034#ifdef CONFIG_WPS
1035 if (wpas_wps_in_progress(wpa_s)) {
f049052b
BG
1036 wpa_dbg(wpa_s, MSG_DEBUG, "WPS: Order scan results with WPS "
1037 "provisioning rules");
41e650ae
JM
1038 compar = wpa_scan_result_wps_compar;
1039 }
1040#endif /* CONFIG_WPS */
1041
9ba9fa07 1042 qsort(scan_res->res, scan_res->num, sizeof(struct wpa_scan_res *),
41e650ae 1043 compar);
9ba9fa07
JM
1044
1045 wpa_bss_update_start(wpa_s);
1046 for (i = 0; i < scan_res->num; i++)
1047 wpa_bss_update_scan_res(wpa_s, scan_res->res[i]);
1048 wpa_bss_update_end(wpa_s, info, new_scan);
1049
1050 return scan_res;
1051}
1052
1053
1054int wpa_supplicant_update_scan_results(struct wpa_supplicant *wpa_s)
1055{
1056 struct wpa_scan_results *scan_res;
1057 scan_res = wpa_supplicant_get_scan_results(wpa_s, NULL, 0);
1058 if (scan_res == NULL)
1059 return -1;
1060 wpa_scan_results_free(scan_res);
1061
1062 return 0;
1063}
d1f9c410
JM
1064
1065
1066void wpa_scan_results_free(struct wpa_scan_results *res)
1067{
1068 size_t i;
1069
1070 if (res == NULL)
1071 return;
1072
1073 for (i = 0; i < res->num; i++)
1074 os_free(res->res[i]);
1075 os_free(res->res);
1076 os_free(res);
1077}