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" 21 #include "mlme.h" 22 #include "wps_supplicant.h" 23 #include "ctrl_iface_dbus.h" 24 25 26 static void wpa_supplicant_gen_assoc_event(struct wpa_supplicant *wpa_s) 27 { 28 struct wpa_ssid *ssid; 29 union wpa_event_data data; 30 31 ssid = wpa_supplicant_get_ssid(wpa_s); 32 if (ssid == NULL) 33 return; 34 35 if (wpa_s->current_ssid == NULL) 36 wpa_s->current_ssid = ssid; 37 wpa_supplicant_initiate_eapol(wpa_s); 38 wpa_printf(MSG_DEBUG, "Already associated with a configured network - " 39 "generating associated event"); 40 os_memset(&data, 0, sizeof(data)); 41 wpa_supplicant_event(wpa_s, EVENT_ASSOC, &data); 42 } 43 44 45 #ifdef CONFIG_WPS 46 static int wpas_wps_in_use(struct wpa_config *conf, 47 enum wps_request_type *req_type) 48 { 49 struct wpa_ssid *ssid; 50 int wps = 0; 51 52 for (ssid = conf->ssid; ssid; ssid = ssid->next) { 53 if (!(ssid->key_mgmt & WPA_KEY_MGMT_WPS)) 54 continue; 55 56 wps = 1; 57 *req_type = wpas_wps_get_req_type(ssid); 58 if (!ssid->eap.phase1) 59 continue; 60 61 if (os_strstr(ssid->eap.phase1, "pbc=1")) 62 return 2; 63 } 64 65 return wps; 66 } 67 #endif /* CONFIG_WPS */ 68 69 70 int wpa_supplicant_enabled_networks(struct wpa_config *conf) 71 { 72 struct wpa_ssid *ssid = conf->ssid; 73 while (ssid) { 74 if (!ssid->disabled) 75 return 1; 76 ssid = ssid->next; 77 } 78 return 0; 79 } 80 81 82 static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx) 83 { 84 struct wpa_supplicant *wpa_s = eloop_ctx; 85 struct wpa_ssid *ssid; 86 int scan_req = 0, ret; 87 struct wpabuf *wps_ie = NULL; 88 const u8 *extra_ie = NULL; 89 size_t extra_ie_len = 0; 90 int wps = 0; 91 #ifdef CONFIG_WPS 92 enum wps_request_type req_type = WPS_REQ_ENROLLEE_INFO; 93 #endif /* CONFIG_WPS */ 94 95 if (wpa_s->disconnected && !wpa_s->scan_req) { 96 wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED); 97 return; 98 } 99 100 if (!wpa_supplicant_enabled_networks(wpa_s->conf) && 101 !wpa_s->scan_req) { 102 wpa_printf(MSG_DEBUG, "No enabled networks - do not scan"); 103 wpa_supplicant_set_state(wpa_s, WPA_INACTIVE); 104 return; 105 } 106 scan_req = wpa_s->scan_req; 107 wpa_s->scan_req = 0; 108 109 if (wpa_s->conf->ap_scan != 0 && 110 wpa_s->driver && IS_WIRED(wpa_s->driver)) { 111 wpa_printf(MSG_DEBUG, "Using wired authentication - " 112 "overriding ap_scan configuration"); 113 wpa_s->conf->ap_scan = 0; 114 } 115 116 if (wpa_s->conf->ap_scan == 0) { 117 wpa_supplicant_gen_assoc_event(wpa_s); 118 return; 119 } 120 121 if (wpa_s->wpa_state == WPA_DISCONNECTED || 122 wpa_s->wpa_state == WPA_INACTIVE) 123 wpa_supplicant_set_state(wpa_s, WPA_SCANNING); 124 125 ssid = wpa_s->conf->ssid; 126 if (wpa_s->prev_scan_ssid != BROADCAST_SSID_SCAN) { 127 while (ssid) { 128 if (ssid == wpa_s->prev_scan_ssid) { 129 ssid = ssid->next; 130 break; 131 } 132 ssid = ssid->next; 133 } 134 } 135 while (ssid) { 136 if (!ssid->disabled && 137 (ssid->scan_ssid || wpa_s->conf->ap_scan == 2)) 138 break; 139 ssid = ssid->next; 140 } 141 142 if (scan_req != 2 && wpa_s->conf->ap_scan == 2) { 143 /* 144 * ap_scan=2 mode - try to associate with each SSID instead of 145 * scanning for each scan_ssid=1 network. 146 */ 147 if (ssid == NULL) { 148 wpa_printf(MSG_DEBUG, "wpa_supplicant_scan: Reached " 149 "end of scan list - go back to beginning"); 150 wpa_s->prev_scan_ssid = BROADCAST_SSID_SCAN; 151 wpa_supplicant_req_scan(wpa_s, 0, 0); 152 return; 153 } 154 if (ssid->next) { 155 /* Continue from the next SSID on the next attempt. */ 156 wpa_s->prev_scan_ssid = ssid; 157 } else { 158 /* Start from the beginning of the SSID list. */ 159 wpa_s->prev_scan_ssid = BROADCAST_SSID_SCAN; 160 } 161 wpa_supplicant_associate(wpa_s, NULL, ssid); 162 return; 163 } 164 165 wpa_printf(MSG_DEBUG, "Starting AP scan (%s SSID)", 166 ssid ? "specific": "broadcast"); 167 if (ssid) { 168 wpa_hexdump_ascii(MSG_DEBUG, "Scan SSID", 169 ssid->ssid, ssid->ssid_len); 170 wpa_s->prev_scan_ssid = ssid; 171 } else 172 wpa_s->prev_scan_ssid = BROADCAST_SSID_SCAN; 173 174 #ifdef CONFIG_WPS 175 wps = wpas_wps_in_use(wpa_s->conf, &req_type); 176 #endif /* CONFIG_WPS */ 177 178 if (wpa_s->scan_res_tried == 0 && wpa_s->conf->ap_scan == 1 && 179 !wpa_s->use_client_mlme && wps != 2) { 180 wpa_s->scan_res_tried++; 181 wpa_s->scan_req = scan_req; 182 wpa_printf(MSG_DEBUG, "Trying to get current scan results " 183 "first without requesting a new scan to speed up " 184 "initial association"); 185 wpa_supplicant_event(wpa_s, EVENT_SCAN_RESULTS, NULL); 186 return; 187 } 188 189 #ifdef CONFIG_WPS 190 if (wps) { 191 wps_ie = wps_build_probe_req_ie(wps == 2, &wpa_s->wps->dev, 192 wpa_s->wps->uuid, req_type); 193 if (wps_ie) { 194 extra_ie = wpabuf_head(wps_ie); 195 extra_ie_len = wpabuf_len(wps_ie); 196 } 197 } 198 #endif /* CONFIG_WPS */ 199 200 wpa_supplicant_notify_scanning(wpa_s, 1); 201 202 if (wpa_s->use_client_mlme) { 203 ieee80211_sta_set_probe_req_ie(wpa_s, extra_ie, extra_ie_len); 204 ret = ieee80211_sta_req_scan(wpa_s, ssid ? ssid->ssid : NULL, 205 ssid ? ssid->ssid_len : 0); 206 } else { 207 wpa_drv_set_probe_req_ie(wpa_s, extra_ie, extra_ie_len); 208 ret = wpa_drv_scan(wpa_s, ssid ? ssid->ssid : NULL, 209 ssid ? ssid->ssid_len : 0); 210 } 211 212 wpabuf_free(wps_ie); 213 214 if (ret) { 215 wpa_printf(MSG_WARNING, "Failed to initiate AP scan."); 216 wpa_supplicant_notify_scanning(wpa_s, 0); 217 wpa_supplicant_req_scan(wpa_s, 10, 0); 218 } else 219 wpa_s->scan_runs++; 220 } 221 222 223 /** 224 * wpa_supplicant_req_scan - Schedule a scan for neighboring access points 225 * @wpa_s: Pointer to wpa_supplicant data 226 * @sec: Number of seconds after which to scan 227 * @usec: Number of microseconds after which to scan 228 * 229 * This function is used to schedule a scan for neighboring access points after 230 * the specified time. 231 */ 232 void wpa_supplicant_req_scan(struct wpa_supplicant *wpa_s, int sec, int usec) 233 { 234 /* If there's at least one network that should be specifically scanned 235 * then don't cancel the scan and reschedule. Some drivers do 236 * background scanning which generates frequent scan results, and that 237 * causes the specific SSID scan to get continually pushed back and 238 * never happen, which causes hidden APs to never get probe-scanned. 239 */ 240 if (eloop_is_timeout_registered(wpa_supplicant_scan, wpa_s, NULL) && 241 wpa_s->conf->ap_scan == 1) { 242 struct wpa_ssid *ssid = wpa_s->conf->ssid; 243 244 while (ssid) { 245 if (!ssid->disabled && ssid->scan_ssid) 246 break; 247 ssid = ssid->next; 248 } 249 if (ssid) { 250 wpa_msg(wpa_s, MSG_DEBUG, "Not rescheduling scan to " 251 "ensure that specific SSID scans occur"); 252 return; 253 } 254 } 255 256 wpa_msg(wpa_s, MSG_DEBUG, "Setting scan request: %d sec %d usec", 257 sec, usec); 258 eloop_cancel_timeout(wpa_supplicant_scan, wpa_s, NULL); 259 eloop_register_timeout(sec, usec, wpa_supplicant_scan, wpa_s, NULL); 260 } 261 262 263 /** 264 * wpa_supplicant_cancel_scan - Cancel a scheduled scan request 265 * @wpa_s: Pointer to wpa_supplicant data 266 * 267 * This function is used to cancel a scan request scheduled with 268 * wpa_supplicant_req_scan(). 269 */ 270 void wpa_supplicant_cancel_scan(struct wpa_supplicant *wpa_s) 271 { 272 wpa_msg(wpa_s, MSG_DEBUG, "Cancelling scan request"); 273 eloop_cancel_timeout(wpa_supplicant_scan, wpa_s, NULL); 274 } 275 276 277 void wpa_supplicant_notify_scanning(struct wpa_supplicant *wpa_s, 278 int scanning) 279 { 280 if (wpa_s->scanning != scanning) { 281 wpa_s->scanning = scanning; 282 wpa_supplicant_dbus_notify_scanning(wpa_s); 283 } 284 } 285 286