]>
Commit | Line | Data |
---|---|---|
6fc6879b JM |
1 | /* |
2 | * WPA Supplicant - Scanning | |
3 | * Copyright (c) 2003-2008, Jouni Malinen <j@w1.fi> | |
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 | ||
15 | #include "includes.h" | |
16 | ||
17 | #include "common.h" | |
18 | #include "eloop.h" | |
19 | #include "config.h" | |
20 | #include "wpa_supplicant_i.h" | |
2d5b792d | 21 | #include "driver_i.h" |
6fc6879b | 22 | #include "mlme.h" |
b01c18a8 | 23 | #include "wps_supplicant.h" |
8bac466b | 24 | #include "notify.h" |
6fc6879b JM |
25 | |
26 | ||
27 | static void wpa_supplicant_gen_assoc_event(struct wpa_supplicant *wpa_s) | |
28 | { | |
29 | struct wpa_ssid *ssid; | |
30 | union wpa_event_data data; | |
31 | ||
32 | ssid = wpa_supplicant_get_ssid(wpa_s); | |
33 | if (ssid == NULL) | |
34 | return; | |
35 | ||
8bac466b | 36 | if (wpa_s->current_ssid == NULL) { |
6fc6879b | 37 | wpa_s->current_ssid = ssid; |
8bac466b JM |
38 | if (wpa_s->current_ssid != NULL) |
39 | wpas_notify_network_changed(wpa_s); | |
40 | } | |
6fc6879b JM |
41 | wpa_supplicant_initiate_eapol(wpa_s); |
42 | wpa_printf(MSG_DEBUG, "Already associated with a configured network - " | |
43 | "generating associated event"); | |
44 | os_memset(&data, 0, sizeof(data)); | |
45 | wpa_supplicant_event(wpa_s, EVENT_ASSOC, &data); | |
46 | } | |
47 | ||
48 | ||
ad08c363 | 49 | #ifdef CONFIG_WPS |
f90c86d4 JM |
50 | static int wpas_wps_in_use(struct wpa_config *conf, |
51 | enum wps_request_type *req_type) | |
ad08c363 JM |
52 | { |
53 | struct wpa_ssid *ssid; | |
54 | int wps = 0; | |
ad08c363 JM |
55 | |
56 | for (ssid = conf->ssid; ssid; ssid = ssid->next) { | |
57 | if (!(ssid->key_mgmt & WPA_KEY_MGMT_WPS)) | |
58 | continue; | |
59 | ||
60 | wps = 1; | |
b01c18a8 | 61 | *req_type = wpas_wps_get_req_type(ssid); |
ad08c363 JM |
62 | if (!ssid->eap.phase1) |
63 | continue; | |
64 | ||
ad08c363 JM |
65 | if (os_strstr(ssid->eap.phase1, "pbc=1")) |
66 | return 2; | |
67 | } | |
68 | ||
69 | return wps; | |
70 | } | |
71 | #endif /* CONFIG_WPS */ | |
72 | ||
e76baaac | 73 | |
4f34d51a | 74 | int wpa_supplicant_enabled_networks(struct wpa_config *conf) |
e76baaac JM |
75 | { |
76 | struct wpa_ssid *ssid = conf->ssid; | |
77 | while (ssid) { | |
78 | if (!ssid->disabled) | |
79 | return 1; | |
80 | ssid = ssid->next; | |
81 | } | |
82 | return 0; | |
83 | } | |
84 | ||
85 | ||
86 | static void wpa_supplicant_assoc_try(struct wpa_supplicant *wpa_s, | |
87 | struct wpa_ssid *ssid) | |
88 | { | |
89 | while (ssid) { | |
90 | if (!ssid->disabled) | |
91 | break; | |
92 | ssid = ssid->next; | |
93 | } | |
94 | ||
95 | /* ap_scan=2 mode - try to associate with each SSID. */ | |
96 | if (ssid == NULL) { | |
97 | wpa_printf(MSG_DEBUG, "wpa_supplicant_scan: Reached " | |
98 | "end of scan list - go back to beginning"); | |
ba2a573c | 99 | wpa_s->prev_scan_ssid = WILDCARD_SSID_SCAN; |
e76baaac JM |
100 | wpa_supplicant_req_scan(wpa_s, 0, 0); |
101 | return; | |
102 | } | |
103 | if (ssid->next) { | |
104 | /* Continue from the next SSID on the next attempt. */ | |
105 | wpa_s->prev_scan_ssid = ssid; | |
106 | } else { | |
107 | /* Start from the beginning of the SSID list. */ | |
ba2a573c | 108 | wpa_s->prev_scan_ssid = WILDCARD_SSID_SCAN; |
e76baaac JM |
109 | } |
110 | wpa_supplicant_associate(wpa_s, NULL, ssid); | |
111 | } | |
112 | ||
113 | ||
d3a98225 JM |
114 | static int int_array_len(const int *a) |
115 | { | |
116 | int i; | |
117 | for (i = 0; a && a[i]; i++) | |
118 | ; | |
119 | return i; | |
120 | } | |
121 | ||
122 | ||
123 | static void int_array_concat(int **res, const int *a) | |
124 | { | |
125 | int reslen, alen, i; | |
126 | int *n; | |
127 | ||
128 | reslen = int_array_len(*res); | |
129 | alen = int_array_len(a); | |
130 | ||
131 | n = os_realloc(*res, (reslen + alen + 1) * sizeof(int)); | |
132 | if (n == NULL) { | |
133 | os_free(*res); | |
134 | *res = NULL; | |
e6c0ebff | 135 | return; |
d3a98225 JM |
136 | } |
137 | for (i = 0; i <= alen; i++) | |
138 | n[reslen + i] = a[i]; | |
139 | *res = n; | |
140 | } | |
141 | ||
142 | ||
143 | static int freq_cmp(const void *a, const void *b) | |
144 | { | |
145 | int _a = *(int *) a; | |
146 | int _b = *(int *) b; | |
147 | ||
148 | if (_a == 0) | |
149 | return 1; | |
150 | if (_b == 0) | |
151 | return -1; | |
152 | return _a - _b; | |
153 | } | |
154 | ||
155 | ||
156 | static void int_array_sort_unique(int *a) | |
157 | { | |
158 | int alen; | |
159 | int i, j; | |
160 | ||
161 | if (a == NULL) | |
162 | return; | |
163 | ||
164 | alen = int_array_len(a); | |
165 | qsort(a, alen, sizeof(int), freq_cmp); | |
166 | ||
167 | i = 0; | |
168 | j = 1; | |
169 | while (a[i] && a[j]) { | |
170 | if (a[i] == a[j]) { | |
171 | j++; | |
172 | continue; | |
173 | } | |
174 | a[++i] = a[j++]; | |
175 | } | |
176 | if (a[i]) | |
177 | i++; | |
178 | a[i] = 0; | |
179 | } | |
180 | ||
181 | ||
60b94c98 JM |
182 | int wpa_supplicant_trigger_scan(struct wpa_supplicant *wpa_s, |
183 | struct wpa_driver_scan_params *params) | |
184 | { | |
185 | int ret; | |
186 | ||
187 | wpa_supplicant_notify_scanning(wpa_s, 1); | |
188 | ||
d009a9da JM |
189 | if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME) |
190 | ret = ieee80211_sta_req_scan(wpa_s, params); | |
191 | else | |
60b94c98 | 192 | ret = wpa_drv_scan(wpa_s, params); |
60b94c98 JM |
193 | |
194 | if (ret) { | |
195 | wpa_supplicant_notify_scanning(wpa_s, 0); | |
196 | wpas_notify_scan_done(wpa_s, 0); | |
197 | } else | |
198 | wpa_s->scan_runs++; | |
199 | ||
200 | return ret; | |
201 | } | |
202 | ||
203 | ||
6fc6879b JM |
204 | static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx) |
205 | { | |
206 | struct wpa_supplicant *wpa_s = eloop_ctx; | |
207 | struct wpa_ssid *ssid; | |
e76baaac | 208 | int scan_req = 0, ret; |
ad08c363 | 209 | struct wpabuf *wps_ie = NULL; |
ad08c363 | 210 | int wps = 0; |
b01c18a8 | 211 | #ifdef CONFIG_WPS |
f90c86d4 | 212 | enum wps_request_type req_type = WPS_REQ_ENROLLEE_INFO; |
b01c18a8 | 213 | #endif /* CONFIG_WPS */ |
e76baaac JM |
214 | struct wpa_driver_scan_params params; |
215 | size_t max_ssids; | |
6fc6879b | 216 | |
3180d7a2 SO |
217 | if (wpa_s->disconnected && !wpa_s->scan_req) { |
218 | wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED); | |
6fc6879b | 219 | return; |
3180d7a2 | 220 | } |
6fc6879b | 221 | |
e76baaac JM |
222 | if (!wpa_supplicant_enabled_networks(wpa_s->conf) && |
223 | !wpa_s->scan_req) { | |
6fc6879b JM |
224 | wpa_printf(MSG_DEBUG, "No enabled networks - do not scan"); |
225 | wpa_supplicant_set_state(wpa_s, WPA_INACTIVE); | |
226 | return; | |
227 | } | |
6fc6879b | 228 | |
c2a04078 JM |
229 | if (wpa_s->conf->ap_scan != 0 && |
230 | (wpa_s->drv_flags & WPA_DRIVER_FLAGS_WIRED)) { | |
e519314e JW |
231 | wpa_printf(MSG_DEBUG, "Using wired authentication - " |
232 | "overriding ap_scan configuration"); | |
6fc6879b | 233 | wpa_s->conf->ap_scan = 0; |
8bac466b | 234 | wpas_notify_ap_scan_changed(wpa_s); |
6fc6879b JM |
235 | } |
236 | ||
237 | if (wpa_s->conf->ap_scan == 0) { | |
238 | wpa_supplicant_gen_assoc_event(wpa_s); | |
239 | return; | |
240 | } | |
241 | ||
c2a04078 JM |
242 | if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME) || |
243 | wpa_s->conf->ap_scan == 2) | |
e76baaac JM |
244 | max_ssids = 1; |
245 | else { | |
246 | max_ssids = wpa_s->max_scan_ssids; | |
247 | if (max_ssids > WPAS_MAX_SCAN_SSIDS) | |
248 | max_ssids = WPAS_MAX_SCAN_SSIDS; | |
249 | } | |
250 | ||
251 | #ifdef CONFIG_WPS | |
252 | wps = wpas_wps_in_use(wpa_s->conf, &req_type); | |
253 | #endif /* CONFIG_WPS */ | |
254 | ||
255 | if (wpa_s->scan_res_tried == 0 && wpa_s->conf->ap_scan == 1 && | |
c2a04078 JM |
256 | !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME) && |
257 | wps != 2) { | |
e76baaac JM |
258 | wpa_s->scan_res_tried++; |
259 | wpa_printf(MSG_DEBUG, "Trying to get current scan results " | |
260 | "first without requesting a new scan to speed up " | |
261 | "initial association"); | |
262 | wpa_supplicant_event(wpa_s, EVENT_SCAN_RESULTS, NULL); | |
263 | return; | |
264 | } | |
265 | ||
266 | scan_req = wpa_s->scan_req; | |
267 | wpa_s->scan_req = 0; | |
268 | ||
269 | os_memset(¶ms, 0, sizeof(params)); | |
270 | ||
6fc6879b JM |
271 | if (wpa_s->wpa_state == WPA_DISCONNECTED || |
272 | wpa_s->wpa_state == WPA_INACTIVE) | |
273 | wpa_supplicant_set_state(wpa_s, WPA_SCANNING); | |
274 | ||
e76baaac | 275 | /* Find the starting point from which to continue scanning */ |
6fc6879b | 276 | ssid = wpa_s->conf->ssid; |
ba2a573c | 277 | if (wpa_s->prev_scan_ssid != WILDCARD_SSID_SCAN) { |
6fc6879b JM |
278 | while (ssid) { |
279 | if (ssid == wpa_s->prev_scan_ssid) { | |
280 | ssid = ssid->next; | |
281 | break; | |
282 | } | |
283 | ssid = ssid->next; | |
284 | } | |
285 | } | |
6fc6879b JM |
286 | |
287 | if (scan_req != 2 && wpa_s->conf->ap_scan == 2) { | |
e76baaac JM |
288 | wpa_supplicant_assoc_try(wpa_s, ssid); |
289 | return; | |
290 | } else if (wpa_s->conf->ap_scan == 2) { | |
6fc6879b | 291 | /* |
ba2a573c JM |
292 | * User-initiated scan request in ap_scan == 2; scan with |
293 | * wildcard SSID. | |
6fc6879b | 294 | */ |
e76baaac JM |
295 | ssid = NULL; |
296 | } else { | |
5be45e2e | 297 | struct wpa_ssid *start = ssid, *tssid; |
d3a98225 | 298 | int freqs_set = 0; |
e76baaac JM |
299 | if (ssid == NULL && max_ssids > 1) |
300 | ssid = wpa_s->conf->ssid; | |
301 | while (ssid) { | |
302 | if (!ssid->disabled && ssid->scan_ssid) { | |
303 | wpa_hexdump_ascii(MSG_DEBUG, "Scan SSID", | |
304 | ssid->ssid, ssid->ssid_len); | |
305 | params.ssids[params.num_ssids].ssid = | |
306 | ssid->ssid; | |
307 | params.ssids[params.num_ssids].ssid_len = | |
308 | ssid->ssid_len; | |
309 | params.num_ssids++; | |
310 | if (params.num_ssids + 1 >= max_ssids) | |
311 | break; | |
312 | } | |
313 | ssid = ssid->next; | |
314 | if (ssid == start) | |
315 | break; | |
316 | if (ssid == NULL && max_ssids > 1 && | |
317 | start != wpa_s->conf->ssid) | |
318 | ssid = wpa_s->conf->ssid; | |
6fc6879b | 319 | } |
d3a98225 | 320 | |
5be45e2e JM |
321 | for (tssid = wpa_s->conf->ssid; tssid; tssid = tssid->next) { |
322 | if (tssid->disabled) | |
d3a98225 | 323 | continue; |
5be45e2e | 324 | if ((params.freqs || !freqs_set) && tssid->scan_freq) { |
d3a98225 | 325 | int_array_concat(¶ms.freqs, |
5be45e2e | 326 | tssid->scan_freq); |
d3a98225 JM |
327 | } else { |
328 | os_free(params.freqs); | |
329 | params.freqs = NULL; | |
330 | } | |
331 | freqs_set = 1; | |
332 | } | |
333 | int_array_sort_unique(params.freqs); | |
6fc6879b JM |
334 | } |
335 | ||
6fc6879b | 336 | if (ssid) { |
6fc6879b | 337 | wpa_s->prev_scan_ssid = ssid; |
e76baaac | 338 | if (max_ssids > 1) { |
ba2a573c | 339 | wpa_printf(MSG_DEBUG, "Include wildcard SSID in the " |
e76baaac | 340 | "scan request"); |
ba2a573c | 341 | params.num_ssids++; |
e76baaac JM |
342 | } |
343 | wpa_printf(MSG_DEBUG, "Starting AP scan for specific SSID(s)"); | |
344 | } else { | |
ba2a573c | 345 | wpa_s->prev_scan_ssid = WILDCARD_SSID_SCAN; |
e76baaac | 346 | params.num_ssids++; |
ba2a573c | 347 | wpa_printf(MSG_DEBUG, "Starting AP scan for wildcard SSID"); |
6fc6879b JM |
348 | } |
349 | ||
ad08c363 JM |
350 | #ifdef CONFIG_WPS |
351 | if (wps) { | |
c0d041d9 | 352 | wps_ie = wps_build_probe_req_ie(wps == 2, &wpa_s->wps->dev, |
79da74a2 | 353 | wpa_s->wps->uuid, req_type); |
ad08c363 | 354 | if (wps_ie) { |
e76baaac JM |
355 | params.extra_ies = wpabuf_head(wps_ie); |
356 | params.extra_ies_len = wpabuf_len(wps_ie); | |
ad08c363 JM |
357 | } |
358 | } | |
359 | #endif /* CONFIG_WPS */ | |
360 | ||
60b94c98 | 361 | ret = wpa_supplicant_trigger_scan(wpa_s, ¶ms); |
6fc6879b | 362 | |
ad08c363 | 363 | wpabuf_free(wps_ie); |
d3a98225 | 364 | os_free(params.freqs); |
ad08c363 | 365 | |
6fc6879b JM |
366 | if (ret) { |
367 | wpa_printf(MSG_WARNING, "Failed to initiate AP scan."); | |
368 | wpa_supplicant_req_scan(wpa_s, 10, 0); | |
a6099152 JM |
369 | } else |
370 | wpa_s->scan_runs++; | |
6fc6879b JM |
371 | } |
372 | ||
373 | ||
374 | /** | |
375 | * wpa_supplicant_req_scan - Schedule a scan for neighboring access points | |
376 | * @wpa_s: Pointer to wpa_supplicant data | |
377 | * @sec: Number of seconds after which to scan | |
378 | * @usec: Number of microseconds after which to scan | |
379 | * | |
380 | * This function is used to schedule a scan for neighboring access points after | |
381 | * the specified time. | |
382 | */ | |
383 | void wpa_supplicant_req_scan(struct wpa_supplicant *wpa_s, int sec, int usec) | |
384 | { | |
7e148849 DW |
385 | /* If there's at least one network that should be specifically scanned |
386 | * then don't cancel the scan and reschedule. Some drivers do | |
387 | * background scanning which generates frequent scan results, and that | |
388 | * causes the specific SSID scan to get continually pushed back and | |
389 | * never happen, which causes hidden APs to never get probe-scanned. | |
390 | */ | |
391 | if (eloop_is_timeout_registered(wpa_supplicant_scan, wpa_s, NULL) && | |
392 | wpa_s->conf->ap_scan == 1) { | |
393 | struct wpa_ssid *ssid = wpa_s->conf->ssid; | |
394 | ||
395 | while (ssid) { | |
396 | if (!ssid->disabled && ssid->scan_ssid) | |
397 | break; | |
398 | ssid = ssid->next; | |
399 | } | |
400 | if (ssid) { | |
401 | wpa_msg(wpa_s, MSG_DEBUG, "Not rescheduling scan to " | |
402 | "ensure that specific SSID scans occur"); | |
403 | return; | |
404 | } | |
405 | } | |
406 | ||
6fc6879b JM |
407 | wpa_msg(wpa_s, MSG_DEBUG, "Setting scan request: %d sec %d usec", |
408 | sec, usec); | |
409 | eloop_cancel_timeout(wpa_supplicant_scan, wpa_s, NULL); | |
410 | eloop_register_timeout(sec, usec, wpa_supplicant_scan, wpa_s, NULL); | |
411 | } | |
412 | ||
413 | ||
414 | /** | |
415 | * wpa_supplicant_cancel_scan - Cancel a scheduled scan request | |
416 | * @wpa_s: Pointer to wpa_supplicant data | |
417 | * | |
418 | * This function is used to cancel a scan request scheduled with | |
419 | * wpa_supplicant_req_scan(). | |
420 | */ | |
421 | void wpa_supplicant_cancel_scan(struct wpa_supplicant *wpa_s) | |
422 | { | |
423 | wpa_msg(wpa_s, MSG_DEBUG, "Cancelling scan request"); | |
424 | eloop_cancel_timeout(wpa_supplicant_scan, wpa_s, NULL); | |
425 | } | |
cb8564b1 DW |
426 | |
427 | ||
428 | void wpa_supplicant_notify_scanning(struct wpa_supplicant *wpa_s, | |
429 | int scanning) | |
430 | { | |
431 | if (wpa_s->scanning != scanning) { | |
432 | wpa_s->scanning = scanning; | |
8bac466b | 433 | wpas_notify_scanning(wpa_s); |
cb8564b1 DW |
434 | } |
435 | } | |
436 |