]>
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; | |
135 | } | |
136 | for (i = 0; i <= alen; i++) | |
137 | n[reslen + i] = a[i]; | |
138 | *res = n; | |
139 | } | |
140 | ||
141 | ||
142 | static int freq_cmp(const void *a, const void *b) | |
143 | { | |
144 | int _a = *(int *) a; | |
145 | int _b = *(int *) b; | |
146 | ||
147 | if (_a == 0) | |
148 | return 1; | |
149 | if (_b == 0) | |
150 | return -1; | |
151 | return _a - _b; | |
152 | } | |
153 | ||
154 | ||
155 | static void int_array_sort_unique(int *a) | |
156 | { | |
157 | int alen; | |
158 | int i, j; | |
159 | ||
160 | if (a == NULL) | |
161 | return; | |
162 | ||
163 | alen = int_array_len(a); | |
164 | qsort(a, alen, sizeof(int), freq_cmp); | |
165 | ||
166 | i = 0; | |
167 | j = 1; | |
168 | while (a[i] && a[j]) { | |
169 | if (a[i] == a[j]) { | |
170 | j++; | |
171 | continue; | |
172 | } | |
173 | a[++i] = a[j++]; | |
174 | } | |
175 | if (a[i]) | |
176 | i++; | |
177 | a[i] = 0; | |
178 | } | |
179 | ||
180 | ||
60b94c98 JM |
181 | int wpa_supplicant_trigger_scan(struct wpa_supplicant *wpa_s, |
182 | struct wpa_driver_scan_params *params) | |
183 | { | |
184 | int ret; | |
185 | ||
186 | wpa_supplicant_notify_scanning(wpa_s, 1); | |
187 | ||
188 | if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME) { | |
189 | ieee80211_sta_set_probe_req_ie(wpa_s, params->extra_ies, | |
190 | params->extra_ies_len); | |
191 | ret = ieee80211_sta_req_scan(wpa_s, params->ssids[0].ssid, | |
192 | params->ssids[0].ssid_len); | |
193 | } else { | |
194 | wpa_drv_set_probe_req_ie(wpa_s, params->extra_ies, | |
195 | params->extra_ies_len); | |
196 | ret = wpa_drv_scan(wpa_s, params); | |
197 | } | |
198 | ||
199 | if (ret) { | |
200 | wpa_supplicant_notify_scanning(wpa_s, 0); | |
201 | wpas_notify_scan_done(wpa_s, 0); | |
202 | } else | |
203 | wpa_s->scan_runs++; | |
204 | ||
205 | return ret; | |
206 | } | |
207 | ||
208 | ||
6fc6879b JM |
209 | static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx) |
210 | { | |
211 | struct wpa_supplicant *wpa_s = eloop_ctx; | |
212 | struct wpa_ssid *ssid; | |
e76baaac | 213 | int scan_req = 0, ret; |
ad08c363 | 214 | struct wpabuf *wps_ie = NULL; |
ad08c363 | 215 | int wps = 0; |
b01c18a8 | 216 | #ifdef CONFIG_WPS |
f90c86d4 | 217 | enum wps_request_type req_type = WPS_REQ_ENROLLEE_INFO; |
b01c18a8 | 218 | #endif /* CONFIG_WPS */ |
e76baaac JM |
219 | struct wpa_driver_scan_params params; |
220 | size_t max_ssids; | |
6fc6879b | 221 | |
3180d7a2 SO |
222 | if (wpa_s->disconnected && !wpa_s->scan_req) { |
223 | wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED); | |
6fc6879b | 224 | return; |
3180d7a2 | 225 | } |
6fc6879b | 226 | |
e76baaac JM |
227 | if (!wpa_supplicant_enabled_networks(wpa_s->conf) && |
228 | !wpa_s->scan_req) { | |
6fc6879b JM |
229 | wpa_printf(MSG_DEBUG, "No enabled networks - do not scan"); |
230 | wpa_supplicant_set_state(wpa_s, WPA_INACTIVE); | |
231 | return; | |
232 | } | |
6fc6879b | 233 | |
c2a04078 JM |
234 | if (wpa_s->conf->ap_scan != 0 && |
235 | (wpa_s->drv_flags & WPA_DRIVER_FLAGS_WIRED)) { | |
e519314e JW |
236 | wpa_printf(MSG_DEBUG, "Using wired authentication - " |
237 | "overriding ap_scan configuration"); | |
6fc6879b | 238 | wpa_s->conf->ap_scan = 0; |
8bac466b | 239 | wpas_notify_ap_scan_changed(wpa_s); |
6fc6879b JM |
240 | } |
241 | ||
242 | if (wpa_s->conf->ap_scan == 0) { | |
243 | wpa_supplicant_gen_assoc_event(wpa_s); | |
244 | return; | |
245 | } | |
246 | ||
c2a04078 JM |
247 | if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME) || |
248 | wpa_s->conf->ap_scan == 2) | |
e76baaac JM |
249 | max_ssids = 1; |
250 | else { | |
251 | max_ssids = wpa_s->max_scan_ssids; | |
252 | if (max_ssids > WPAS_MAX_SCAN_SSIDS) | |
253 | max_ssids = WPAS_MAX_SCAN_SSIDS; | |
254 | } | |
255 | ||
256 | #ifdef CONFIG_WPS | |
257 | wps = wpas_wps_in_use(wpa_s->conf, &req_type); | |
258 | #endif /* CONFIG_WPS */ | |
259 | ||
260 | if (wpa_s->scan_res_tried == 0 && wpa_s->conf->ap_scan == 1 && | |
c2a04078 JM |
261 | !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME) && |
262 | wps != 2) { | |
e76baaac JM |
263 | wpa_s->scan_res_tried++; |
264 | wpa_printf(MSG_DEBUG, "Trying to get current scan results " | |
265 | "first without requesting a new scan to speed up " | |
266 | "initial association"); | |
267 | wpa_supplicant_event(wpa_s, EVENT_SCAN_RESULTS, NULL); | |
268 | return; | |
269 | } | |
270 | ||
271 | scan_req = wpa_s->scan_req; | |
272 | wpa_s->scan_req = 0; | |
273 | ||
274 | os_memset(¶ms, 0, sizeof(params)); | |
275 | ||
6fc6879b JM |
276 | if (wpa_s->wpa_state == WPA_DISCONNECTED || |
277 | wpa_s->wpa_state == WPA_INACTIVE) | |
278 | wpa_supplicant_set_state(wpa_s, WPA_SCANNING); | |
279 | ||
e76baaac | 280 | /* Find the starting point from which to continue scanning */ |
6fc6879b | 281 | ssid = wpa_s->conf->ssid; |
ba2a573c | 282 | if (wpa_s->prev_scan_ssid != WILDCARD_SSID_SCAN) { |
6fc6879b JM |
283 | while (ssid) { |
284 | if (ssid == wpa_s->prev_scan_ssid) { | |
285 | ssid = ssid->next; | |
286 | break; | |
287 | } | |
288 | ssid = ssid->next; | |
289 | } | |
290 | } | |
6fc6879b JM |
291 | |
292 | if (scan_req != 2 && wpa_s->conf->ap_scan == 2) { | |
e76baaac JM |
293 | wpa_supplicant_assoc_try(wpa_s, ssid); |
294 | return; | |
295 | } else if (wpa_s->conf->ap_scan == 2) { | |
6fc6879b | 296 | /* |
ba2a573c JM |
297 | * User-initiated scan request in ap_scan == 2; scan with |
298 | * wildcard SSID. | |
6fc6879b | 299 | */ |
e76baaac JM |
300 | ssid = NULL; |
301 | } else { | |
302 | struct wpa_ssid *start = ssid; | |
d3a98225 | 303 | int freqs_set = 0; |
e76baaac JM |
304 | if (ssid == NULL && max_ssids > 1) |
305 | ssid = wpa_s->conf->ssid; | |
306 | while (ssid) { | |
307 | if (!ssid->disabled && ssid->scan_ssid) { | |
308 | wpa_hexdump_ascii(MSG_DEBUG, "Scan SSID", | |
309 | ssid->ssid, ssid->ssid_len); | |
310 | params.ssids[params.num_ssids].ssid = | |
311 | ssid->ssid; | |
312 | params.ssids[params.num_ssids].ssid_len = | |
313 | ssid->ssid_len; | |
314 | params.num_ssids++; | |
315 | if (params.num_ssids + 1 >= max_ssids) | |
316 | break; | |
317 | } | |
318 | ssid = ssid->next; | |
319 | if (ssid == start) | |
320 | break; | |
321 | if (ssid == NULL && max_ssids > 1 && | |
322 | start != wpa_s->conf->ssid) | |
323 | ssid = wpa_s->conf->ssid; | |
6fc6879b | 324 | } |
d3a98225 JM |
325 | |
326 | for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) { | |
327 | if (ssid->disabled) | |
328 | continue; | |
329 | if ((params.freqs || !freqs_set) && ssid->scan_freq) { | |
330 | int_array_concat(¶ms.freqs, | |
331 | ssid->scan_freq); | |
332 | } else { | |
333 | os_free(params.freqs); | |
334 | params.freqs = NULL; | |
335 | } | |
336 | freqs_set = 1; | |
337 | } | |
338 | int_array_sort_unique(params.freqs); | |
6fc6879b JM |
339 | } |
340 | ||
6fc6879b | 341 | if (ssid) { |
6fc6879b | 342 | wpa_s->prev_scan_ssid = ssid; |
e76baaac | 343 | if (max_ssids > 1) { |
ba2a573c | 344 | wpa_printf(MSG_DEBUG, "Include wildcard SSID in the " |
e76baaac | 345 | "scan request"); |
ba2a573c | 346 | params.num_ssids++; |
e76baaac JM |
347 | } |
348 | wpa_printf(MSG_DEBUG, "Starting AP scan for specific SSID(s)"); | |
349 | } else { | |
ba2a573c | 350 | wpa_s->prev_scan_ssid = WILDCARD_SSID_SCAN; |
e76baaac | 351 | params.num_ssids++; |
ba2a573c | 352 | wpa_printf(MSG_DEBUG, "Starting AP scan for wildcard SSID"); |
6fc6879b JM |
353 | } |
354 | ||
ad08c363 JM |
355 | #ifdef CONFIG_WPS |
356 | if (wps) { | |
c0d041d9 | 357 | wps_ie = wps_build_probe_req_ie(wps == 2, &wpa_s->wps->dev, |
79da74a2 | 358 | wpa_s->wps->uuid, req_type); |
ad08c363 | 359 | if (wps_ie) { |
e76baaac JM |
360 | params.extra_ies = wpabuf_head(wps_ie); |
361 | params.extra_ies_len = wpabuf_len(wps_ie); | |
ad08c363 JM |
362 | } |
363 | } | |
364 | #endif /* CONFIG_WPS */ | |
365 | ||
60b94c98 | 366 | ret = wpa_supplicant_trigger_scan(wpa_s, ¶ms); |
6fc6879b | 367 | |
ad08c363 | 368 | wpabuf_free(wps_ie); |
d3a98225 | 369 | os_free(params.freqs); |
ad08c363 | 370 | |
6fc6879b JM |
371 | if (ret) { |
372 | wpa_printf(MSG_WARNING, "Failed to initiate AP scan."); | |
373 | wpa_supplicant_req_scan(wpa_s, 10, 0); | |
a6099152 JM |
374 | } else |
375 | wpa_s->scan_runs++; | |
6fc6879b JM |
376 | } |
377 | ||
378 | ||
379 | /** | |
380 | * wpa_supplicant_req_scan - Schedule a scan for neighboring access points | |
381 | * @wpa_s: Pointer to wpa_supplicant data | |
382 | * @sec: Number of seconds after which to scan | |
383 | * @usec: Number of microseconds after which to scan | |
384 | * | |
385 | * This function is used to schedule a scan for neighboring access points after | |
386 | * the specified time. | |
387 | */ | |
388 | void wpa_supplicant_req_scan(struct wpa_supplicant *wpa_s, int sec, int usec) | |
389 | { | |
7e148849 DW |
390 | /* If there's at least one network that should be specifically scanned |
391 | * then don't cancel the scan and reschedule. Some drivers do | |
392 | * background scanning which generates frequent scan results, and that | |
393 | * causes the specific SSID scan to get continually pushed back and | |
394 | * never happen, which causes hidden APs to never get probe-scanned. | |
395 | */ | |
396 | if (eloop_is_timeout_registered(wpa_supplicant_scan, wpa_s, NULL) && | |
397 | wpa_s->conf->ap_scan == 1) { | |
398 | struct wpa_ssid *ssid = wpa_s->conf->ssid; | |
399 | ||
400 | while (ssid) { | |
401 | if (!ssid->disabled && ssid->scan_ssid) | |
402 | break; | |
403 | ssid = ssid->next; | |
404 | } | |
405 | if (ssid) { | |
406 | wpa_msg(wpa_s, MSG_DEBUG, "Not rescheduling scan to " | |
407 | "ensure that specific SSID scans occur"); | |
408 | return; | |
409 | } | |
410 | } | |
411 | ||
6fc6879b JM |
412 | wpa_msg(wpa_s, MSG_DEBUG, "Setting scan request: %d sec %d usec", |
413 | sec, usec); | |
414 | eloop_cancel_timeout(wpa_supplicant_scan, wpa_s, NULL); | |
415 | eloop_register_timeout(sec, usec, wpa_supplicant_scan, wpa_s, NULL); | |
416 | } | |
417 | ||
418 | ||
419 | /** | |
420 | * wpa_supplicant_cancel_scan - Cancel a scheduled scan request | |
421 | * @wpa_s: Pointer to wpa_supplicant data | |
422 | * | |
423 | * This function is used to cancel a scan request scheduled with | |
424 | * wpa_supplicant_req_scan(). | |
425 | */ | |
426 | void wpa_supplicant_cancel_scan(struct wpa_supplicant *wpa_s) | |
427 | { | |
428 | wpa_msg(wpa_s, MSG_DEBUG, "Cancelling scan request"); | |
429 | eloop_cancel_timeout(wpa_supplicant_scan, wpa_s, NULL); | |
430 | } | |
cb8564b1 DW |
431 | |
432 | ||
433 | void wpa_supplicant_notify_scanning(struct wpa_supplicant *wpa_s, | |
434 | int scanning) | |
435 | { | |
436 | if (wpa_s->scanning != scanning) { | |
437 | wpa_s->scanning = scanning; | |
8bac466b | 438 | wpas_notify_scanning(wpa_s); |
cb8564b1 DW |
439 | } |
440 | } | |
441 |