]>
Commit | Line | Data |
---|---|---|
c2a04078 JM |
1 | /* |
2 | * wpa_supplicant - SME | |
3 | * Copyright (c) 2009, 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" | |
90973fb2 | 18 | #include "common/ieee802_11_defs.h" |
c2a04078 | 19 | #include "eapol_supp/eapol_supp_sm.h" |
90973fb2 | 20 | #include "common/wpa_common.h" |
3acb5005 JM |
21 | #include "rsn_supp/wpa.h" |
22 | #include "rsn_supp/pmksa_cache.h" | |
c2a04078 JM |
23 | #include "config.h" |
24 | #include "wpa_supplicant_i.h" | |
2d5b792d | 25 | #include "driver_i.h" |
c2a04078 JM |
26 | #include "wpas_glue.h" |
27 | #include "wps_supplicant.h" | |
8bac466b | 28 | #include "notify.h" |
76d11d3f | 29 | #include "blacklist.h" |
c2a04078 JM |
30 | #include "sme.h" |
31 | ||
32 | void sme_authenticate(struct wpa_supplicant *wpa_s, | |
33 | struct wpa_scan_res *bss, struct wpa_ssid *ssid) | |
34 | { | |
35 | struct wpa_driver_auth_params params; | |
8bac466b | 36 | struct wpa_ssid *old_ssid; |
c2a04078 JM |
37 | const u8 *ie; |
38 | #ifdef CONFIG_IEEE80211R | |
39 | const u8 *md = NULL; | |
40 | #endif /* CONFIG_IEEE80211R */ | |
8bac466b | 41 | int i, bssid_changed; |
c2a04078 JM |
42 | |
43 | if (bss == NULL) { | |
44 | wpa_printf(MSG_ERROR, "SME: No scan result available for the " | |
45 | "network"); | |
46 | return; | |
47 | } | |
48 | ||
49 | os_memset(¶ms, 0, sizeof(params)); | |
50 | wpa_s->reassociate = 0; | |
51 | ||
52 | params.freq = bss->freq; | |
53 | params.bssid = bss->bssid; | |
54 | ie = wpa_scan_get_ie(bss, WLAN_EID_SSID); | |
55 | if (ie == NULL) { | |
56 | wpa_printf(MSG_ERROR, "SME: SSID not available for the BSS"); | |
57 | return; | |
58 | } | |
59 | params.ssid = ie + 2; | |
60 | params.ssid_len = ie[1]; | |
61 | ||
62fa124c JM |
62 | if (wpa_s->sme.ssid_len != params.ssid_len || |
63 | os_memcmp(wpa_s->sme.ssid, params.ssid, params.ssid_len) != 0) | |
64 | wpa_s->sme.prev_bssid_set = 0; | |
65 | ||
c2a04078 JM |
66 | wpa_s->sme.freq = params.freq; |
67 | os_memcpy(wpa_s->sme.ssid, params.ssid, params.ssid_len); | |
68 | wpa_s->sme.ssid_len = params.ssid_len; | |
69 | ||
70 | params.auth_alg = AUTH_ALG_OPEN_SYSTEM; | |
71 | #ifdef IEEE8021X_EAPOL | |
72 | if (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA) { | |
73 | if (ssid->leap) { | |
74 | if (ssid->non_leap == 0) | |
75 | params.auth_alg = AUTH_ALG_LEAP; | |
76 | else | |
77 | params.auth_alg |= AUTH_ALG_LEAP; | |
78 | } | |
79 | } | |
80 | #endif /* IEEE8021X_EAPOL */ | |
81 | wpa_printf(MSG_DEBUG, "Automatic auth_alg selection: 0x%x", | |
82 | params.auth_alg); | |
83 | if (ssid->auth_alg) { | |
84 | params.auth_alg = 0; | |
85 | if (ssid->auth_alg & WPA_AUTH_ALG_OPEN) | |
86 | params.auth_alg |= AUTH_ALG_OPEN_SYSTEM; | |
87 | if (ssid->auth_alg & WPA_AUTH_ALG_SHARED) | |
88 | params.auth_alg |= AUTH_ALG_SHARED_KEY; | |
89 | if (ssid->auth_alg & WPA_AUTH_ALG_LEAP) | |
90 | params.auth_alg |= AUTH_ALG_LEAP; | |
91 | wpa_printf(MSG_DEBUG, "Overriding auth_alg selection: 0x%x", | |
92 | params.auth_alg); | |
93 | } | |
94 | ||
a0b2f99b JM |
95 | for (i = 0; i < NUM_WEP_KEYS; i++) { |
96 | if (ssid->wep_key_len[i]) | |
97 | params.wep_key[i] = ssid->wep_key[i]; | |
98 | params.wep_key_len[i] = ssid->wep_key_len[i]; | |
99 | } | |
100 | params.wep_tx_keyidx = ssid->wep_tx_keyidx; | |
101 | ||
8bac466b | 102 | bssid_changed = !is_zero_ether_addr(wpa_s->bssid); |
c2a04078 JM |
103 | os_memset(wpa_s->bssid, 0, ETH_ALEN); |
104 | os_memcpy(wpa_s->pending_bssid, bss->bssid, ETH_ALEN); | |
8bac466b JM |
105 | if (bssid_changed) |
106 | wpas_notify_bssid_changed(wpa_s); | |
c2a04078 JM |
107 | |
108 | if (bss && (wpa_scan_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE) || | |
109 | wpa_scan_get_ie(bss, WLAN_EID_RSN)) && | |
110 | (ssid->key_mgmt & (WPA_KEY_MGMT_IEEE8021X | WPA_KEY_MGMT_PSK | | |
111 | WPA_KEY_MGMT_FT_IEEE8021X | | |
112 | WPA_KEY_MGMT_FT_PSK | | |
113 | WPA_KEY_MGMT_IEEE8021X_SHA256 | | |
114 | WPA_KEY_MGMT_PSK_SHA256))) { | |
115 | int try_opportunistic; | |
116 | try_opportunistic = ssid->proactive_key_caching && | |
117 | (ssid->proto & WPA_PROTO_RSN); | |
118 | if (pmksa_cache_set_current(wpa_s->wpa, NULL, bss->bssid, | |
119 | wpa_s->current_ssid, | |
120 | try_opportunistic) == 0) | |
121 | eapol_sm_notify_pmkid_attempt(wpa_s->eapol, 1); | |
122 | wpa_s->sme.assoc_req_ie_len = sizeof(wpa_s->sme.assoc_req_ie); | |
123 | if (wpa_supplicant_set_suites(wpa_s, bss, ssid, | |
124 | wpa_s->sme.assoc_req_ie, | |
125 | &wpa_s->sme.assoc_req_ie_len)) { | |
126 | wpa_printf(MSG_WARNING, "SME: Failed to set WPA key " | |
127 | "management and encryption suites"); | |
128 | return; | |
129 | } | |
130 | } else if (ssid->key_mgmt & | |
131 | (WPA_KEY_MGMT_PSK | WPA_KEY_MGMT_IEEE8021X | | |
132 | WPA_KEY_MGMT_WPA_NONE | WPA_KEY_MGMT_FT_PSK | | |
133 | WPA_KEY_MGMT_FT_IEEE8021X | WPA_KEY_MGMT_PSK_SHA256 | | |
134 | WPA_KEY_MGMT_IEEE8021X_SHA256)) { | |
135 | wpa_s->sme.assoc_req_ie_len = sizeof(wpa_s->sme.assoc_req_ie); | |
136 | if (wpa_supplicant_set_suites(wpa_s, NULL, ssid, | |
137 | wpa_s->sme.assoc_req_ie, | |
138 | &wpa_s->sme.assoc_req_ie_len)) { | |
139 | wpa_printf(MSG_WARNING, "SME: Failed to set WPA key " | |
140 | "management and encryption suites (no scan " | |
141 | "results)"); | |
142 | return; | |
143 | } | |
144 | #ifdef CONFIG_WPS | |
145 | } else if (ssid->key_mgmt & WPA_KEY_MGMT_WPS) { | |
146 | struct wpabuf *wps_ie; | |
147 | wps_ie = wps_build_assoc_req_ie(wpas_wps_get_req_type(ssid)); | |
148 | if (wps_ie && wpabuf_len(wps_ie) <= | |
149 | sizeof(wpa_s->sme.assoc_req_ie)) { | |
150 | wpa_s->sme.assoc_req_ie_len = wpabuf_len(wps_ie); | |
151 | os_memcpy(wpa_s->sme.assoc_req_ie, wpabuf_head(wps_ie), | |
152 | wpa_s->sme.assoc_req_ie_len); | |
153 | } else | |
154 | wpa_s->sme.assoc_req_ie_len = 0; | |
155 | wpabuf_free(wps_ie); | |
156 | wpa_supplicant_set_non_wpa_policy(wpa_s, ssid); | |
157 | #endif /* CONFIG_WPS */ | |
158 | } else { | |
159 | wpa_supplicant_set_non_wpa_policy(wpa_s, ssid); | |
160 | wpa_s->sme.assoc_req_ie_len = 0; | |
161 | } | |
162 | ||
163 | #ifdef CONFIG_IEEE80211R | |
164 | ie = wpa_scan_get_ie(bss, WLAN_EID_MOBILITY_DOMAIN); | |
165 | if (ie && ie[1] >= MOBILITY_DOMAIN_ID_LEN) | |
166 | md = ie + 2; | |
167 | wpa_sm_set_ft_params(wpa_s->wpa, md, NULL, 0, NULL); | |
168 | if (md) { | |
169 | /* Prepare for the next transition */ | |
170 | wpa_ft_prepare_auth_request(wpa_s->wpa); | |
171 | } | |
172 | ||
173 | if (md && ssid->key_mgmt & (WPA_KEY_MGMT_FT_PSK | | |
174 | WPA_KEY_MGMT_FT_IEEE8021X)) { | |
175 | if (wpa_s->sme.assoc_req_ie_len + 5 < | |
176 | sizeof(wpa_s->sme.assoc_req_ie)) { | |
177 | struct rsn_mdie *mdie; | |
178 | u8 *pos = wpa_s->sme.assoc_req_ie + | |
179 | wpa_s->sme.assoc_req_ie_len; | |
180 | *pos++ = WLAN_EID_MOBILITY_DOMAIN; | |
181 | *pos++ = sizeof(*mdie); | |
182 | mdie = (struct rsn_mdie *) pos; | |
183 | os_memcpy(mdie->mobility_domain, md, | |
184 | MOBILITY_DOMAIN_ID_LEN); | |
185 | mdie->ft_capab = 0; | |
186 | wpa_s->sme.assoc_req_ie_len += 5; | |
187 | } | |
188 | ||
189 | if (wpa_s->sme.ft_used && | |
190 | os_memcmp(md, wpa_s->sme.mobility_domain, 2) == 0) { | |
191 | wpa_printf(MSG_DEBUG, "SME: Trying to use FT " | |
192 | "over-the-air"); | |
193 | params.auth_alg = AUTH_ALG_FT; | |
194 | params.ie = wpa_s->sme.ft_ies; | |
195 | params.ie_len = wpa_s->sme.ft_ies_len; | |
196 | } | |
197 | } | |
198 | #endif /* CONFIG_IEEE80211R */ | |
199 | ||
200 | #ifdef CONFIG_IEEE80211W | |
201 | switch (ssid->ieee80211w) { | |
202 | case NO_IEEE80211W: | |
203 | wpa_s->sme.mfp = NO_MGMT_FRAME_PROTECTION; | |
204 | break; | |
205 | case IEEE80211W_OPTIONAL: | |
206 | wpa_s->sme.mfp = MGMT_FRAME_PROTECTION_OPTIONAL; | |
207 | break; | |
208 | case IEEE80211W_REQUIRED: | |
209 | wpa_s->sme.mfp = MGMT_FRAME_PROTECTION_REQUIRED; | |
210 | break; | |
211 | } | |
212 | if (ssid->ieee80211w != NO_IEEE80211W && bss) { | |
213 | const u8 *rsn = wpa_scan_get_ie(bss, WLAN_EID_RSN); | |
214 | struct wpa_ie_data _ie; | |
215 | if (rsn && wpa_parse_wpa_ie(rsn, 2 + rsn[1], &_ie) == 0 && | |
216 | _ie.capabilities & | |
217 | (WPA_CAPABILITY_MFPC | WPA_CAPABILITY_MFPR)) { | |
218 | wpa_printf(MSG_DEBUG, "WPA: Selected AP supports MFP: " | |
219 | "require MFP"); | |
220 | wpa_s->sme.mfp = MGMT_FRAME_PROTECTION_REQUIRED; | |
221 | } | |
222 | } | |
223 | #endif /* CONFIG_IEEE80211W */ | |
224 | ||
225 | wpa_supplicant_cancel_scan(wpa_s); | |
226 | ||
227 | wpa_msg(wpa_s, MSG_INFO, "Trying to authenticate with " MACSTR | |
228 | " (SSID='%s' freq=%d MHz)", MAC2STR(params.bssid), | |
229 | wpa_ssid_txt(params.ssid, params.ssid_len), params.freq); | |
230 | ||
231 | wpa_clear_keys(wpa_s, bss->bssid); | |
232 | wpa_supplicant_set_state(wpa_s, WPA_AUTHENTICATING); | |
8bac466b | 233 | old_ssid = wpa_s->current_ssid; |
c2a04078 JM |
234 | wpa_s->current_ssid = ssid; |
235 | wpa_supplicant_rsn_supp_set_config(wpa_s, wpa_s->current_ssid); | |
236 | wpa_supplicant_initiate_eapol(wpa_s); | |
8bac466b JM |
237 | if (old_ssid != wpa_s->current_ssid) |
238 | wpas_notify_network_changed(wpa_s); | |
c2a04078 JM |
239 | |
240 | if (wpa_drv_authenticate(wpa_s, ¶ms) < 0) { | |
241 | wpa_msg(wpa_s, MSG_INFO, "Authentication request to the " | |
242 | "driver failed"); | |
243 | return; | |
244 | } | |
245 | ||
246 | /* TODO: add timeout on authentication */ | |
247 | ||
248 | /* | |
249 | * Association will be started based on the authentication event from | |
250 | * the driver. | |
251 | */ | |
252 | } | |
253 | ||
254 | ||
255 | void sme_event_auth(struct wpa_supplicant *wpa_s, union wpa_event_data *data) | |
256 | { | |
257 | struct wpa_driver_associate_params params; | |
258 | struct wpa_ssid *ssid = wpa_s->current_ssid; | |
259 | ||
260 | if (ssid == NULL) { | |
261 | wpa_printf(MSG_DEBUG, "SME: Ignore authentication event when " | |
262 | "network is not selected"); | |
263 | return; | |
264 | } | |
265 | ||
266 | if (wpa_s->wpa_state != WPA_AUTHENTICATING) { | |
267 | wpa_printf(MSG_DEBUG, "SME: Ignore authentication event when " | |
268 | "not in authenticating state"); | |
269 | return; | |
270 | } | |
271 | ||
272 | if (os_memcmp(wpa_s->pending_bssid, data->auth.peer, ETH_ALEN) != 0) { | |
273 | wpa_printf(MSG_DEBUG, "SME: Ignore authentication with " | |
274 | "unexpected peer " MACSTR, | |
275 | MAC2STR(data->auth.peer)); | |
276 | return; | |
277 | } | |
278 | ||
279 | wpa_printf(MSG_DEBUG, "SME: Authentication response: peer=" MACSTR | |
280 | " auth_type=%d status_code=%d", | |
281 | MAC2STR(data->auth.peer), data->auth.auth_type, | |
282 | data->auth.status_code); | |
283 | wpa_hexdump(MSG_MSGDUMP, "SME: Authentication response IEs", | |
284 | data->auth.ies, data->auth.ies_len); | |
285 | ||
286 | if (data->auth.status_code != WLAN_STATUS_SUCCESS) { | |
287 | wpa_printf(MSG_DEBUG, "SME: Authentication failed (status " | |
288 | "code %d)", data->auth.status_code); | |
289 | return; | |
290 | } | |
291 | ||
292 | #ifdef CONFIG_IEEE80211R | |
293 | if (data->auth.auth_type == WLAN_AUTH_FT) { | |
294 | union wpa_event_data edata; | |
295 | os_memset(&edata, 0, sizeof(edata)); | |
296 | edata.ft_ies.ies = data->auth.ies; | |
297 | edata.ft_ies.ies_len = data->auth.ies_len; | |
298 | os_memcpy(edata.ft_ies.target_ap, data->auth.peer, ETH_ALEN); | |
299 | wpa_supplicant_event(wpa_s, EVENT_FT_RESPONSE, &edata); | |
300 | } | |
301 | #endif /* CONFIG_IEEE80211R */ | |
302 | ||
303 | os_memset(¶ms, 0, sizeof(params)); | |
304 | params.bssid = data->auth.peer; | |
305 | params.ssid = wpa_s->sme.ssid; | |
306 | params.ssid_len = wpa_s->sme.ssid_len; | |
307 | params.freq = wpa_s->sme.freq; | |
308 | params.wpa_ie = wpa_s->sme.assoc_req_ie_len ? | |
309 | wpa_s->sme.assoc_req_ie : NULL; | |
310 | params.wpa_ie_len = wpa_s->sme.assoc_req_ie_len; | |
311 | #ifdef CONFIG_IEEE80211R | |
312 | if (data->auth.auth_type == WLAN_AUTH_FT && wpa_s->sme.ft_ies) { | |
313 | params.wpa_ie = wpa_s->sme.ft_ies; | |
314 | params.wpa_ie_len = wpa_s->sme.ft_ies_len; | |
315 | } | |
316 | #endif /* CONFIG_IEEE80211R */ | |
317 | params.mode = ssid->mode; | |
318 | params.mgmt_frame_protection = wpa_s->sme.mfp; | |
62fa124c JM |
319 | if (wpa_s->sme.prev_bssid_set) |
320 | params.prev_bssid = wpa_s->sme.prev_bssid; | |
c2a04078 JM |
321 | |
322 | wpa_msg(wpa_s, MSG_INFO, "Trying to associate with " MACSTR | |
323 | " (SSID='%s' freq=%d MHz)", MAC2STR(params.bssid), | |
324 | params.ssid ? wpa_ssid_txt(params.ssid, params.ssid_len) : "", | |
325 | params.freq); | |
326 | ||
327 | wpa_supplicant_set_state(wpa_s, WPA_ASSOCIATING); | |
328 | ||
329 | if (wpa_drv_associate(wpa_s, ¶ms) < 0) { | |
330 | wpa_msg(wpa_s, MSG_INFO, "Association request to the driver " | |
331 | "failed"); | |
332 | return; | |
333 | } | |
334 | ||
335 | /* TODO: add timeout on association */ | |
336 | } | |
337 | ||
338 | ||
339 | int sme_update_ft_ies(struct wpa_supplicant *wpa_s, const u8 *md, | |
340 | const u8 *ies, size_t ies_len) | |
341 | { | |
342 | if (md == NULL || ies == NULL) { | |
343 | wpa_printf(MSG_DEBUG, "SME: Remove mobility domain"); | |
344 | os_free(wpa_s->sme.ft_ies); | |
345 | wpa_s->sme.ft_ies = NULL; | |
346 | wpa_s->sme.ft_ies_len = 0; | |
347 | wpa_s->sme.ft_used = 0; | |
348 | return 0; | |
349 | } | |
350 | ||
351 | os_memcpy(wpa_s->sme.mobility_domain, md, MOBILITY_DOMAIN_ID_LEN); | |
352 | wpa_hexdump(MSG_DEBUG, "SME: FT IEs", ies, ies_len); | |
353 | os_free(wpa_s->sme.ft_ies); | |
354 | wpa_s->sme.ft_ies = os_malloc(ies_len); | |
355 | if (wpa_s->sme.ft_ies == NULL) | |
356 | return -1; | |
357 | os_memcpy(wpa_s->sme.ft_ies, ies, ies_len); | |
358 | wpa_s->sme.ft_ies_len = ies_len; | |
359 | return 0; | |
360 | } | |
efa46078 JM |
361 | |
362 | ||
363 | void sme_event_assoc_reject(struct wpa_supplicant *wpa_s, | |
364 | union wpa_event_data *data) | |
365 | { | |
8bac466b | 366 | int bssid_changed; |
76d11d3f | 367 | int timeout = 5000; |
8bac466b | 368 | |
76d11d3f JM |
369 | wpa_printf(MSG_DEBUG, "SME: Association with " MACSTR " failed: " |
370 | "status code %d", MAC2STR(wpa_s->pending_bssid), | |
efa46078 JM |
371 | data->assoc_reject.status_code); |
372 | ||
8bac466b | 373 | bssid_changed = !is_zero_ether_addr(wpa_s->bssid); |
76d11d3f JM |
374 | |
375 | /* | |
376 | * For now, unconditionally terminate the previous authentication. In | |
377 | * theory, this should not be needed, but mac80211 gets quite confused | |
378 | * if the authentication is left pending.. Some roaming cases might | |
379 | * benefit from using the previous authentication, so this could be | |
380 | * optimized in the future. | |
381 | */ | |
382 | if (wpa_drv_deauthenticate(wpa_s, wpa_s->pending_bssid, | |
383 | WLAN_REASON_DEAUTH_LEAVING) < 0) { | |
384 | wpa_msg(wpa_s, MSG_INFO, | |
385 | "Deauth request to the driver failed"); | |
386 | } | |
62fa124c | 387 | wpa_s->sme.prev_bssid_set = 0; |
76d11d3f JM |
388 | |
389 | if (wpa_blacklist_add(wpa_s, wpa_s->pending_bssid) == 0) { | |
390 | struct wpa_blacklist *b; | |
391 | b = wpa_blacklist_get(wpa_s, wpa_s->pending_bssid); | |
392 | if (b && b->count < 3) { | |
393 | /* | |
394 | * Speed up next attempt if there could be other APs | |
395 | * that could accept association. | |
396 | */ | |
397 | timeout = 100; | |
398 | } | |
399 | } | |
400 | wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED); | |
efa46078 JM |
401 | os_memset(wpa_s->bssid, 0, ETH_ALEN); |
402 | os_memset(wpa_s->pending_bssid, 0, ETH_ALEN); | |
8bac466b JM |
403 | if (bssid_changed) |
404 | wpas_notify_bssid_changed(wpa_s); | |
efa46078 JM |
405 | |
406 | /* | |
407 | * TODO: if more than one possible AP is available in scan results, | |
408 | * could try the other ones before requesting a new scan. | |
409 | */ | |
76d11d3f JM |
410 | wpa_supplicant_req_scan(wpa_s, timeout / 1000, |
411 | 1000 * (timeout % 1000)); | |
efa46078 | 412 | } |
da1fb17c JM |
413 | |
414 | ||
415 | void sme_event_auth_timed_out(struct wpa_supplicant *wpa_s, | |
416 | union wpa_event_data *data) | |
417 | { | |
418 | wpa_printf(MSG_DEBUG, "SME: Authentication timed out"); | |
419 | wpa_supplicant_req_scan(wpa_s, 5, 0); | |
420 | } | |
421 | ||
422 | ||
423 | void sme_event_assoc_timed_out(struct wpa_supplicant *wpa_s, | |
424 | union wpa_event_data *data) | |
425 | { | |
426 | wpa_printf(MSG_DEBUG, "SME: Association timed out"); | |
427 | wpa_supplicant_mark_disassoc(wpa_s); | |
428 | wpa_supplicant_req_scan(wpa_s, 5, 0); | |
429 | } | |
a84ed99e JM |
430 | |
431 | ||
432 | void sme_event_disassoc(struct wpa_supplicant *wpa_s, | |
433 | union wpa_event_data *data) | |
434 | { | |
435 | wpa_printf(MSG_DEBUG, "SME: Disassociation event received"); | |
436 | if (!is_zero_ether_addr(wpa_s->bssid) && | |
437 | !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME)) { | |
438 | /* | |
439 | * cfg80211/mac80211 can get into somewhat confused state if | |
440 | * the AP only disassociates us and leaves us in authenticated | |
441 | * state. For now, force the state to be cleared to avoid | |
442 | * confusing errors if we try to associate with the AP again. | |
443 | */ | |
444 | wpa_printf(MSG_DEBUG, "SME: Deauthenticate to clear driver " | |
445 | "state"); | |
446 | wpa_drv_deauthenticate(wpa_s, wpa_s->bssid, | |
447 | WLAN_REASON_DEAUTH_LEAVING); | |
448 | } | |
449 | } |