diff options
author | 2025-03-08 22:04:20 +0800 | |
---|---|---|
committer | 2025-03-08 22:04:20 +0800 | |
commit | a07bb8fd1299070229f0e8f3dcb57ffd5ef9870a (patch) | |
tree | 84f21bd0bf7071bc5fc7dd989e77d7ceb5476682 /net/wireless/sme.c | |
download | ohosKernel-a07bb8fd1299070229f0e8f3dcb57ffd5ef9870a.tar.gz ohosKernel-a07bb8fd1299070229f0e8f3dcb57ffd5ef9870a.zip |
Initial commit: OpenHarmony-v4.0-ReleaseOpenHarmony-v4.0-Release
Diffstat (limited to 'net/wireless/sme.c')
-rw-r--r-- | net/wireless/sme.c | 1355 |
1 files changed, 1355 insertions, 0 deletions
diff --git a/net/wireless/sme.c b/net/wireless/sme.c new file mode 100644 index 000000000..060e365c8 --- /dev/null +++ b/net/wireless/sme.c | |||
@@ -0,0 +1,1355 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | /* | ||
3 | * SME code for cfg80211 | ||
4 | * both driver SME event handling and the SME implementation | ||
5 | * (for nl80211's connect() and wext) | ||
6 | * | ||
7 | * Copyright 2009 Johannes Berg <johannes@sipsolutions.net> | ||
8 | * Copyright (C) 2009, 2020 Intel Corporation. All rights reserved. | ||
9 | * Copyright 2017 Intel Deutschland GmbH | ||
10 | */ | ||
11 | |||
12 | #include <linux/etherdevice.h> | ||
13 | #include <linux/if_arp.h> | ||
14 | #include <linux/slab.h> | ||
15 | #include <linux/workqueue.h> | ||
16 | #include <linux/wireless.h> | ||
17 | #include <linux/export.h> | ||
18 | #include <net/iw_handler.h> | ||
19 | #include <net/cfg80211.h> | ||
20 | #include <net/rtnetlink.h> | ||
21 | #include "nl80211.h" | ||
22 | #include "reg.h" | ||
23 | #include "rdev-ops.h" | ||
24 | |||
25 | /* | ||
26 | * Software SME in cfg80211, using auth/assoc/deauth calls to the | ||
27 | * driver. This is for implementing nl80211's connect/disconnect | ||
28 | * and wireless extensions (if configured.) | ||
29 | */ | ||
30 | |||
31 | struct cfg80211_conn { | ||
32 | struct cfg80211_connect_params params; | ||
33 | /* these are sub-states of the _CONNECTING sme_state */ | ||
34 | enum { | ||
35 | CFG80211_CONN_SCANNING, | ||
36 | CFG80211_CONN_SCAN_AGAIN, | ||
37 | CFG80211_CONN_AUTHENTICATE_NEXT, | ||
38 | CFG80211_CONN_AUTHENTICATING, | ||
39 | CFG80211_CONN_AUTH_FAILED_TIMEOUT, | ||
40 | CFG80211_CONN_ASSOCIATE_NEXT, | ||
41 | CFG80211_CONN_ASSOCIATING, | ||
42 | CFG80211_CONN_ASSOC_FAILED, | ||
43 | CFG80211_CONN_ASSOC_FAILED_TIMEOUT, | ||
44 | CFG80211_CONN_DEAUTH, | ||
45 | CFG80211_CONN_ABANDON, | ||
46 | CFG80211_CONN_CONNECTED, | ||
47 | } state; | ||
48 | u8 bssid[ETH_ALEN], prev_bssid[ETH_ALEN]; | ||
49 | const u8 *ie; | ||
50 | size_t ie_len; | ||
51 | bool auto_auth, prev_bssid_valid; | ||
52 | }; | ||
53 | |||
54 | static void cfg80211_sme_free(struct wireless_dev *wdev) | ||
55 | { | ||
56 | if (!wdev->conn) | ||
57 | return; | ||
58 | |||
59 | kfree(wdev->conn->ie); | ||
60 | kfree(wdev->conn); | ||
61 | wdev->conn = NULL; | ||
62 | } | ||
63 | |||
64 | static int cfg80211_conn_scan(struct wireless_dev *wdev) | ||
65 | { | ||
66 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); | ||
67 | struct cfg80211_scan_request *request; | ||
68 | int n_channels, err; | ||
69 | |||
70 | ASSERT_RTNL(); | ||
71 | ASSERT_WDEV_LOCK(wdev); | ||
72 | |||
73 | if (rdev->scan_req || rdev->scan_msg) | ||
74 | return -EBUSY; | ||
75 | |||
76 | if (wdev->conn->params.channel) | ||
77 | n_channels = 1; | ||
78 | else | ||
79 | n_channels = ieee80211_get_num_supported_channels(wdev->wiphy); | ||
80 | |||
81 | request = kzalloc(sizeof(*request) + sizeof(request->ssids[0]) + | ||
82 | sizeof(request->channels[0]) * n_channels, | ||
83 | GFP_KERNEL); | ||
84 | if (!request) | ||
85 | return -ENOMEM; | ||
86 | |||
87 | if (wdev->conn->params.channel) { | ||
88 | enum nl80211_band band = wdev->conn->params.channel->band; | ||
89 | struct ieee80211_supported_band *sband = | ||
90 | wdev->wiphy->bands[band]; | ||
91 | |||
92 | if (!sband) { | ||
93 | kfree(request); | ||
94 | return -EINVAL; | ||
95 | } | ||
96 | request->channels[0] = wdev->conn->params.channel; | ||
97 | request->rates[band] = (1 << sband->n_bitrates) - 1; | ||
98 | } else { | ||
99 | int i = 0, j; | ||
100 | enum nl80211_band band; | ||
101 | struct ieee80211_supported_band *bands; | ||
102 | struct ieee80211_channel *channel; | ||
103 | |||
104 | for (band = 0; band < NUM_NL80211_BANDS; band++) { | ||
105 | bands = wdev->wiphy->bands[band]; | ||
106 | if (!bands) | ||
107 | continue; | ||
108 | for (j = 0; j < bands->n_channels; j++) { | ||
109 | channel = &bands->channels[j]; | ||
110 | if (channel->flags & IEEE80211_CHAN_DISABLED) | ||
111 | continue; | ||
112 | request->channels[i++] = channel; | ||
113 | } | ||
114 | request->rates[band] = (1 << bands->n_bitrates) - 1; | ||
115 | } | ||
116 | n_channels = i; | ||
117 | } | ||
118 | request->n_channels = n_channels; | ||
119 | request->ssids = (void *)&request->channels[n_channels]; | ||
120 | request->n_ssids = 1; | ||
121 | |||
122 | memcpy(request->ssids[0].ssid, wdev->conn->params.ssid, | ||
123 | wdev->conn->params.ssid_len); | ||
124 | request->ssids[0].ssid_len = wdev->conn->params.ssid_len; | ||
125 | |||
126 | eth_broadcast_addr(request->bssid); | ||
127 | |||
128 | request->wdev = wdev; | ||
129 | request->wiphy = &rdev->wiphy; | ||
130 | request->scan_start = jiffies; | ||
131 | |||
132 | rdev->scan_req = request; | ||
133 | |||
134 | err = rdev_scan(rdev, request); | ||
135 | if (!err) { | ||
136 | wdev->conn->state = CFG80211_CONN_SCANNING; | ||
137 | nl80211_send_scan_start(rdev, wdev); | ||
138 | dev_hold(wdev->netdev); | ||
139 | } else { | ||
140 | rdev->scan_req = NULL; | ||
141 | kfree(request); | ||
142 | } | ||
143 | return err; | ||
144 | } | ||
145 | |||
146 | static int cfg80211_conn_do_work(struct wireless_dev *wdev, | ||
147 | enum nl80211_timeout_reason *treason) | ||
148 | { | ||
149 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); | ||
150 | struct cfg80211_connect_params *params; | ||
151 | struct cfg80211_assoc_request req = {}; | ||
152 | int err; | ||
153 | |||
154 | ASSERT_WDEV_LOCK(wdev); | ||
155 | |||
156 | if (!wdev->conn) | ||
157 | return 0; | ||
158 | |||
159 | params = &wdev->conn->params; | ||
160 | |||
161 | switch (wdev->conn->state) { | ||
162 | case CFG80211_CONN_SCANNING: | ||
163 | /* didn't find it during scan ... */ | ||
164 | return -ENOENT; | ||
165 | case CFG80211_CONN_SCAN_AGAIN: | ||
166 | return cfg80211_conn_scan(wdev); | ||
167 | case CFG80211_CONN_AUTHENTICATE_NEXT: | ||
168 | if (WARN_ON(!rdev->ops->auth)) | ||
169 | return -EOPNOTSUPP; | ||
170 | wdev->conn->state = CFG80211_CONN_AUTHENTICATING; | ||
171 | return cfg80211_mlme_auth(rdev, wdev->netdev, | ||
172 | params->channel, params->auth_type, | ||
173 | params->bssid, | ||
174 | params->ssid, params->ssid_len, | ||
175 | NULL, 0, | ||
176 | params->key, params->key_len, | ||
177 | params->key_idx, NULL, 0); | ||
178 | case CFG80211_CONN_AUTH_FAILED_TIMEOUT: | ||
179 | *treason = NL80211_TIMEOUT_AUTH; | ||
180 | return -ENOTCONN; | ||
181 | case CFG80211_CONN_ASSOCIATE_NEXT: | ||
182 | if (WARN_ON(!rdev->ops->assoc)) | ||
183 | return -EOPNOTSUPP; | ||
184 | wdev->conn->state = CFG80211_CONN_ASSOCIATING; | ||
185 | if (wdev->conn->prev_bssid_valid) | ||
186 | req.prev_bssid = wdev->conn->prev_bssid; | ||
187 | req.ie = params->ie; | ||
188 | req.ie_len = params->ie_len; | ||
189 | req.use_mfp = params->mfp != NL80211_MFP_NO; | ||
190 | req.crypto = params->crypto; | ||
191 | req.flags = params->flags; | ||
192 | req.ht_capa = params->ht_capa; | ||
193 | req.ht_capa_mask = params->ht_capa_mask; | ||
194 | req.vht_capa = params->vht_capa; | ||
195 | req.vht_capa_mask = params->vht_capa_mask; | ||
196 | |||
197 | err = cfg80211_mlme_assoc(rdev, wdev->netdev, params->channel, | ||
198 | params->bssid, params->ssid, | ||
199 | params->ssid_len, &req); | ||
200 | if (err) | ||
201 | cfg80211_mlme_deauth(rdev, wdev->netdev, params->bssid, | ||
202 | NULL, 0, | ||
203 | WLAN_REASON_DEAUTH_LEAVING, | ||
204 | false); | ||
205 | return err; | ||
206 | case CFG80211_CONN_ASSOC_FAILED_TIMEOUT: | ||
207 | *treason = NL80211_TIMEOUT_ASSOC; | ||
208 | fallthrough; | ||
209 | case CFG80211_CONN_ASSOC_FAILED: | ||
210 | cfg80211_mlme_deauth(rdev, wdev->netdev, params->bssid, | ||
211 | NULL, 0, | ||
212 | WLAN_REASON_DEAUTH_LEAVING, false); | ||
213 | return -ENOTCONN; | ||
214 | case CFG80211_CONN_DEAUTH: | ||
215 | cfg80211_mlme_deauth(rdev, wdev->netdev, params->bssid, | ||
216 | NULL, 0, | ||
217 | WLAN_REASON_DEAUTH_LEAVING, false); | ||
218 | fallthrough; | ||
219 | case CFG80211_CONN_ABANDON: | ||
220 | /* free directly, disconnected event already sent */ | ||
221 | cfg80211_sme_free(wdev); | ||
222 | return 0; | ||
223 | default: | ||
224 | return 0; | ||
225 | } | ||
226 | } | ||
227 | |||
228 | void cfg80211_conn_work(struct work_struct *work) | ||
229 | { | ||
230 | struct cfg80211_registered_device *rdev = | ||
231 | container_of(work, struct cfg80211_registered_device, conn_work); | ||
232 | struct wireless_dev *wdev; | ||
233 | u8 bssid_buf[ETH_ALEN], *bssid = NULL; | ||
234 | enum nl80211_timeout_reason treason; | ||
235 | |||
236 | rtnl_lock(); | ||
237 | |||
238 | list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) { | ||
239 | if (!wdev->netdev) | ||
240 | continue; | ||
241 | |||
242 | wdev_lock(wdev); | ||
243 | if (!netif_running(wdev->netdev)) { | ||
244 | wdev_unlock(wdev); | ||
245 | continue; | ||
246 | } | ||
247 | if (!wdev->conn || | ||
248 | wdev->conn->state == CFG80211_CONN_CONNECTED) { | ||
249 | wdev_unlock(wdev); | ||
250 | continue; | ||
251 | } | ||
252 | if (wdev->conn->params.bssid) { | ||
253 | memcpy(bssid_buf, wdev->conn->params.bssid, ETH_ALEN); | ||
254 | bssid = bssid_buf; | ||
255 | } | ||
256 | treason = NL80211_TIMEOUT_UNSPECIFIED; | ||
257 | if (cfg80211_conn_do_work(wdev, &treason)) { | ||
258 | struct cfg80211_connect_resp_params cr; | ||
259 | |||
260 | memset(&cr, 0, sizeof(cr)); | ||
261 | cr.status = -1; | ||
262 | cr.bssid = bssid; | ||
263 | cr.timeout_reason = treason; | ||
264 | __cfg80211_connect_result(wdev->netdev, &cr, false); | ||
265 | } | ||
266 | wdev_unlock(wdev); | ||
267 | } | ||
268 | |||
269 | rtnl_unlock(); | ||
270 | } | ||
271 | |||
272 | /* Returned bss is reference counted and must be cleaned up appropriately. */ | ||
273 | static struct cfg80211_bss *cfg80211_get_conn_bss(struct wireless_dev *wdev) | ||
274 | { | ||
275 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); | ||
276 | struct cfg80211_bss *bss; | ||
277 | |||
278 | ASSERT_WDEV_LOCK(wdev); | ||
279 | |||
280 | bss = cfg80211_get_bss(wdev->wiphy, wdev->conn->params.channel, | ||
281 | wdev->conn->params.bssid, | ||
282 | wdev->conn->params.ssid, | ||
283 | wdev->conn->params.ssid_len, | ||
284 | wdev->conn_bss_type, | ||
285 | IEEE80211_PRIVACY(wdev->conn->params.privacy)); | ||
286 | if (!bss) | ||
287 | return NULL; | ||
288 | |||
289 | memcpy(wdev->conn->bssid, bss->bssid, ETH_ALEN); | ||
290 | wdev->conn->params.bssid = wdev->conn->bssid; | ||
291 | wdev->conn->params.channel = bss->channel; | ||
292 | wdev->conn->state = CFG80211_CONN_AUTHENTICATE_NEXT; | ||
293 | schedule_work(&rdev->conn_work); | ||
294 | |||
295 | return bss; | ||
296 | } | ||
297 | |||
298 | static void __cfg80211_sme_scan_done(struct net_device *dev) | ||
299 | { | ||
300 | struct wireless_dev *wdev = dev->ieee80211_ptr; | ||
301 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); | ||
302 | struct cfg80211_bss *bss; | ||
303 | |||
304 | ASSERT_WDEV_LOCK(wdev); | ||
305 | |||
306 | if (!wdev->conn) | ||
307 | return; | ||
308 | |||
309 | if (wdev->conn->state != CFG80211_CONN_SCANNING && | ||
310 | wdev->conn->state != CFG80211_CONN_SCAN_AGAIN) | ||
311 | return; | ||
312 | |||
313 | bss = cfg80211_get_conn_bss(wdev); | ||
314 | if (bss) | ||
315 | cfg80211_put_bss(&rdev->wiphy, bss); | ||
316 | else | ||
317 | schedule_work(&rdev->conn_work); | ||
318 | } | ||
319 | |||
320 | void cfg80211_sme_scan_done(struct net_device *dev) | ||
321 | { | ||
322 | struct wireless_dev *wdev = dev->ieee80211_ptr; | ||
323 | |||
324 | wdev_lock(wdev); | ||
325 | __cfg80211_sme_scan_done(dev); | ||
326 | wdev_unlock(wdev); | ||
327 | } | ||
328 | |||
329 | void cfg80211_sme_rx_auth(struct wireless_dev *wdev, const u8 *buf, size_t len) | ||
330 | { | ||
331 | struct wiphy *wiphy = wdev->wiphy; | ||
332 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); | ||
333 | struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf; | ||
334 | u16 status_code = le16_to_cpu(mgmt->u.auth.status_code); | ||
335 | |||
336 | ASSERT_WDEV_LOCK(wdev); | ||
337 | |||
338 | if (!wdev->conn || wdev->conn->state == CFG80211_CONN_CONNECTED) | ||
339 | return; | ||
340 | |||
341 | if (status_code == WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG && | ||
342 | wdev->conn->auto_auth && | ||
343 | wdev->conn->params.auth_type != NL80211_AUTHTYPE_NETWORK_EAP) { | ||
344 | /* select automatically between only open, shared, leap */ | ||
345 | switch (wdev->conn->params.auth_type) { | ||
346 | case NL80211_AUTHTYPE_OPEN_SYSTEM: | ||
347 | if (wdev->connect_keys) | ||
348 | wdev->conn->params.auth_type = | ||
349 | NL80211_AUTHTYPE_SHARED_KEY; | ||
350 | else | ||
351 | wdev->conn->params.auth_type = | ||
352 | NL80211_AUTHTYPE_NETWORK_EAP; | ||
353 | break; | ||
354 | case NL80211_AUTHTYPE_SHARED_KEY: | ||
355 | wdev->conn->params.auth_type = | ||
356 | NL80211_AUTHTYPE_NETWORK_EAP; | ||
357 | break; | ||
358 | default: | ||
359 | /* huh? */ | ||
360 | wdev->conn->params.auth_type = | ||
361 | NL80211_AUTHTYPE_OPEN_SYSTEM; | ||
362 | break; | ||
363 | } | ||
364 | wdev->conn->state = CFG80211_CONN_AUTHENTICATE_NEXT; | ||
365 | schedule_work(&rdev->conn_work); | ||
366 | } else if (status_code != WLAN_STATUS_SUCCESS) { | ||
367 | struct cfg80211_connect_resp_params cr; | ||
368 | |||
369 | memset(&cr, 0, sizeof(cr)); | ||
370 | cr.status = status_code; | ||
371 | cr.bssid = mgmt->bssid; | ||
372 | cr.timeout_reason = NL80211_TIMEOUT_UNSPECIFIED; | ||
373 | __cfg80211_connect_result(wdev->netdev, &cr, false); | ||
374 | } else if (wdev->conn->state == CFG80211_CONN_AUTHENTICATING) { | ||
375 | wdev->conn->state = CFG80211_CONN_ASSOCIATE_NEXT; | ||
376 | schedule_work(&rdev->conn_work); | ||
377 | } | ||
378 | } | ||
379 | |||
380 | bool cfg80211_sme_rx_assoc_resp(struct wireless_dev *wdev, u16 status) | ||
381 | { | ||
382 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); | ||
383 | |||
384 | if (!wdev->conn) | ||
385 | return false; | ||
386 | |||
387 | if (status == WLAN_STATUS_SUCCESS) { | ||
388 | wdev->conn->state = CFG80211_CONN_CONNECTED; | ||
389 | return false; | ||
390 | } | ||
391 | |||
392 | if (wdev->conn->prev_bssid_valid) { | ||
393 | /* | ||
394 | * Some stupid APs don't accept reassoc, so we | ||
395 | * need to fall back to trying regular assoc; | ||
396 | * return true so no event is sent to userspace. | ||
397 | */ | ||
398 | wdev->conn->prev_bssid_valid = false; | ||
399 | wdev->conn->state = CFG80211_CONN_ASSOCIATE_NEXT; | ||
400 | schedule_work(&rdev->conn_work); | ||
401 | return true; | ||
402 | } | ||
403 | |||
404 | wdev->conn->state = CFG80211_CONN_ASSOC_FAILED; | ||
405 | schedule_work(&rdev->conn_work); | ||
406 | return false; | ||
407 | } | ||
408 | |||
409 | void cfg80211_sme_deauth(struct wireless_dev *wdev) | ||
410 | { | ||
411 | cfg80211_sme_free(wdev); | ||
412 | } | ||
413 | |||
414 | void cfg80211_sme_auth_timeout(struct wireless_dev *wdev) | ||
415 | { | ||
416 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); | ||
417 | |||
418 | if (!wdev->conn) | ||
419 | return; | ||
420 | |||
421 | wdev->conn->state = CFG80211_CONN_AUTH_FAILED_TIMEOUT; | ||
422 | schedule_work(&rdev->conn_work); | ||
423 | } | ||
424 | |||
425 | void cfg80211_sme_disassoc(struct wireless_dev *wdev) | ||
426 | { | ||
427 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); | ||
428 | |||
429 | if (!wdev->conn) | ||
430 | return; | ||
431 | |||
432 | wdev->conn->state = CFG80211_CONN_DEAUTH; | ||
433 | schedule_work(&rdev->conn_work); | ||
434 | } | ||
435 | |||
436 | void cfg80211_sme_assoc_timeout(struct wireless_dev *wdev) | ||
437 | { | ||
438 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); | ||
439 | |||
440 | if (!wdev->conn) | ||
441 | return; | ||
442 | |||
443 | wdev->conn->state = CFG80211_CONN_ASSOC_FAILED_TIMEOUT; | ||
444 | schedule_work(&rdev->conn_work); | ||
445 | } | ||
446 | |||
447 | void cfg80211_sme_abandon_assoc(struct wireless_dev *wdev) | ||
448 | { | ||
449 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); | ||
450 | |||
451 | if (!wdev->conn) | ||
452 | return; | ||
453 | |||
454 | wdev->conn->state = CFG80211_CONN_ABANDON; | ||
455 | schedule_work(&rdev->conn_work); | ||
456 | } | ||
457 | |||
458 | static int cfg80211_sme_get_conn_ies(struct wireless_dev *wdev, | ||
459 | const u8 *ies, size_t ies_len, | ||
460 | const u8 **out_ies, size_t *out_ies_len) | ||
461 | { | ||
462 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); | ||
463 | u8 *buf; | ||
464 | size_t offs; | ||
465 | |||
466 | if (!rdev->wiphy.extended_capabilities_len || | ||
467 | (ies && cfg80211_find_ie(WLAN_EID_EXT_CAPABILITY, ies, ies_len))) { | ||
468 | *out_ies = kmemdup(ies, ies_len, GFP_KERNEL); | ||
469 | if (!*out_ies) | ||
470 | return -ENOMEM; | ||
471 | *out_ies_len = ies_len; | ||
472 | return 0; | ||
473 | } | ||
474 | |||
475 | buf = kmalloc(ies_len + rdev->wiphy.extended_capabilities_len + 2, | ||
476 | GFP_KERNEL); | ||
477 | if (!buf) | ||
478 | return -ENOMEM; | ||
479 | |||
480 | if (ies_len) { | ||
481 | static const u8 before_extcapa[] = { | ||
482 | /* not listing IEs expected to be created by driver */ | ||
483 | WLAN_EID_RSN, | ||
484 | WLAN_EID_QOS_CAPA, | ||
485 | WLAN_EID_RRM_ENABLED_CAPABILITIES, | ||
486 | WLAN_EID_MOBILITY_DOMAIN, | ||
487 | WLAN_EID_SUPPORTED_REGULATORY_CLASSES, | ||
488 | WLAN_EID_BSS_COEX_2040, | ||
489 | }; | ||
490 | |||
491 | offs = ieee80211_ie_split(ies, ies_len, before_extcapa, | ||
492 | ARRAY_SIZE(before_extcapa), 0); | ||
493 | memcpy(buf, ies, offs); | ||
494 | /* leave a whole for extended capabilities IE */ | ||
495 | memcpy(buf + offs + rdev->wiphy.extended_capabilities_len + 2, | ||
496 | ies + offs, ies_len - offs); | ||
497 | } else { | ||
498 | offs = 0; | ||
499 | } | ||
500 | |||
501 | /* place extended capabilities IE (with only driver capabilities) */ | ||
502 | buf[offs] = WLAN_EID_EXT_CAPABILITY; | ||
503 | buf[offs + 1] = rdev->wiphy.extended_capabilities_len; | ||
504 | memcpy(buf + offs + 2, | ||
505 | rdev->wiphy.extended_capabilities, | ||
506 | rdev->wiphy.extended_capabilities_len); | ||
507 | |||
508 | *out_ies = buf; | ||
509 | *out_ies_len = ies_len + rdev->wiphy.extended_capabilities_len + 2; | ||
510 | |||
511 | return 0; | ||
512 | } | ||
513 | |||
514 | static int cfg80211_sme_connect(struct wireless_dev *wdev, | ||
515 | struct cfg80211_connect_params *connect, | ||
516 | const u8 *prev_bssid) | ||
517 | { | ||
518 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); | ||
519 | struct cfg80211_bss *bss; | ||
520 | int err; | ||
521 | |||
522 | if (!rdev->ops->auth || !rdev->ops->assoc) | ||
523 | return -EOPNOTSUPP; | ||
524 | |||
525 | if (wdev->current_bss) { | ||
526 | cfg80211_unhold_bss(wdev->current_bss); | ||
527 | cfg80211_put_bss(wdev->wiphy, &wdev->current_bss->pub); | ||
528 | wdev->current_bss = NULL; | ||
529 | |||
530 | cfg80211_sme_free(wdev); | ||
531 | } | ||
532 | |||
533 | if (wdev->conn) | ||
534 | return -EINPROGRESS; | ||
535 | |||
536 | wdev->conn = kzalloc(sizeof(*wdev->conn), GFP_KERNEL); | ||
537 | if (!wdev->conn) | ||
538 | return -ENOMEM; | ||
539 | |||
540 | /* | ||
541 | * Copy all parameters, and treat explicitly IEs, BSSID, SSID. | ||
542 | */ | ||
543 | memcpy(&wdev->conn->params, connect, sizeof(*connect)); | ||
544 | if (connect->bssid) { | ||
545 | wdev->conn->params.bssid = wdev->conn->bssid; | ||
546 | memcpy(wdev->conn->bssid, connect->bssid, ETH_ALEN); | ||
547 | } | ||
548 | |||
549 | if (cfg80211_sme_get_conn_ies(wdev, connect->ie, connect->ie_len, | ||
550 | &wdev->conn->ie, | ||
551 | &wdev->conn->params.ie_len)) { | ||
552 | kfree(wdev->conn); | ||
553 | wdev->conn = NULL; | ||
554 | return -ENOMEM; | ||
555 | } | ||
556 | wdev->conn->params.ie = wdev->conn->ie; | ||
557 | |||
558 | if (connect->auth_type == NL80211_AUTHTYPE_AUTOMATIC) { | ||
559 | wdev->conn->auto_auth = true; | ||
560 | /* start with open system ... should mostly work */ | ||
561 | wdev->conn->params.auth_type = | ||
562 | NL80211_AUTHTYPE_OPEN_SYSTEM; | ||
563 | } else { | ||
564 | wdev->conn->auto_auth = false; | ||
565 | } | ||
566 | |||
567 | wdev->conn->params.ssid = wdev->ssid; | ||
568 | wdev->conn->params.ssid_len = wdev->ssid_len; | ||
569 | |||
570 | /* see if we have the bss already */ | ||
571 | bss = cfg80211_get_conn_bss(wdev); | ||
572 | |||
573 | if (prev_bssid) { | ||
574 | memcpy(wdev->conn->prev_bssid, prev_bssid, ETH_ALEN); | ||
575 | wdev->conn->prev_bssid_valid = true; | ||
576 | } | ||
577 | |||
578 | /* we're good if we have a matching bss struct */ | ||
579 | if (bss) { | ||
580 | enum nl80211_timeout_reason treason; | ||
581 | |||
582 | err = cfg80211_conn_do_work(wdev, &treason); | ||
583 | cfg80211_put_bss(wdev->wiphy, bss); | ||
584 | } else { | ||
585 | /* otherwise we'll need to scan for the AP first */ | ||
586 | err = cfg80211_conn_scan(wdev); | ||
587 | |||
588 | /* | ||
589 | * If we can't scan right now, then we need to scan again | ||
590 | * after the current scan finished, since the parameters | ||
591 | * changed (unless we find a good AP anyway). | ||
592 | */ | ||
593 | if (err == -EBUSY) { | ||
594 | err = 0; | ||
595 | wdev->conn->state = CFG80211_CONN_SCAN_AGAIN; | ||
596 | } | ||
597 | } | ||
598 | |||
599 | if (err) | ||
600 | cfg80211_sme_free(wdev); | ||
601 | |||
602 | return err; | ||
603 | } | ||
604 | |||
605 | static int cfg80211_sme_disconnect(struct wireless_dev *wdev, u16 reason) | ||
606 | { | ||
607 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); | ||
608 | int err; | ||
609 | |||
610 | if (!wdev->conn) | ||
611 | return 0; | ||
612 | |||
613 | if (!rdev->ops->deauth) | ||
614 | return -EOPNOTSUPP; | ||
615 | |||
616 | if (wdev->conn->state == CFG80211_CONN_SCANNING || | ||
617 | wdev->conn->state == CFG80211_CONN_SCAN_AGAIN) { | ||
618 | err = 0; | ||
619 | goto out; | ||
620 | } | ||
621 | |||
622 | /* wdev->conn->params.bssid must be set if > SCANNING */ | ||
623 | err = cfg80211_mlme_deauth(rdev, wdev->netdev, | ||
624 | wdev->conn->params.bssid, | ||
625 | NULL, 0, reason, false); | ||
626 | out: | ||
627 | cfg80211_sme_free(wdev); | ||
628 | return err; | ||
629 | } | ||
630 | |||
631 | /* | ||
632 | * code shared for in-device and software SME | ||
633 | */ | ||
634 | |||
635 | static bool cfg80211_is_all_idle(void) | ||
636 | { | ||
637 | struct cfg80211_registered_device *rdev; | ||
638 | struct wireless_dev *wdev; | ||
639 | bool is_all_idle = true; | ||
640 | |||
641 | /* | ||
642 | * All devices must be idle as otherwise if you are actively | ||
643 | * scanning some new beacon hints could be learned and would | ||
644 | * count as new regulatory hints. | ||
645 | * Also if there is any other active beaconing interface we | ||
646 | * need not issue a disconnect hint and reset any info such | ||
647 | * as chan dfs state, etc. | ||
648 | */ | ||
649 | list_for_each_entry(rdev, &cfg80211_rdev_list, list) { | ||
650 | list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) { | ||
651 | wdev_lock(wdev); | ||
652 | if (wdev->conn || wdev->current_bss || | ||
653 | cfg80211_beaconing_iface_active(wdev)) | ||
654 | is_all_idle = false; | ||
655 | wdev_unlock(wdev); | ||
656 | } | ||
657 | } | ||
658 | |||
659 | return is_all_idle; | ||
660 | } | ||
661 | |||
662 | static void disconnect_work(struct work_struct *work) | ||
663 | { | ||
664 | rtnl_lock(); | ||
665 | if (cfg80211_is_all_idle()) | ||
666 | regulatory_hint_disconnect(); | ||
667 | rtnl_unlock(); | ||
668 | } | ||
669 | |||
670 | DECLARE_WORK(cfg80211_disconnect_work, disconnect_work); | ||
671 | |||
672 | |||
673 | /* | ||
674 | * API calls for drivers implementing connect/disconnect and | ||
675 | * SME event handling | ||
676 | */ | ||
677 | |||
678 | /* This method must consume bss one way or another */ | ||
679 | void __cfg80211_connect_result(struct net_device *dev, | ||
680 | struct cfg80211_connect_resp_params *cr, | ||
681 | bool wextev) | ||
682 | { | ||
683 | struct wireless_dev *wdev = dev->ieee80211_ptr; | ||
684 | const u8 *country_ie; | ||
685 | #ifdef CONFIG_CFG80211_WEXT | ||
686 | union iwreq_data wrqu; | ||
687 | #endif | ||
688 | |||
689 | ASSERT_WDEV_LOCK(wdev); | ||
690 | |||
691 | if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION && | ||
692 | wdev->iftype != NL80211_IFTYPE_P2P_CLIENT)) { | ||
693 | cfg80211_put_bss(wdev->wiphy, cr->bss); | ||
694 | return; | ||
695 | } | ||
696 | |||
697 | wdev->unprot_beacon_reported = 0; | ||
698 | nl80211_send_connect_result(wiphy_to_rdev(wdev->wiphy), dev, cr, | ||
699 | GFP_KERNEL); | ||
700 | |||
701 | #ifdef CONFIG_CFG80211_WEXT | ||
702 | if (wextev) { | ||
703 | if (cr->req_ie && cr->status == WLAN_STATUS_SUCCESS) { | ||
704 | memset(&wrqu, 0, sizeof(wrqu)); | ||
705 | wrqu.data.length = cr->req_ie_len; | ||
706 | wireless_send_event(dev, IWEVASSOCREQIE, &wrqu, | ||
707 | cr->req_ie); | ||
708 | } | ||
709 | |||
710 | if (cr->resp_ie && cr->status == WLAN_STATUS_SUCCESS) { | ||
711 | memset(&wrqu, 0, sizeof(wrqu)); | ||
712 | wrqu.data.length = cr->resp_ie_len; | ||
713 | wireless_send_event(dev, IWEVASSOCRESPIE, &wrqu, | ||
714 | cr->resp_ie); | ||
715 | } | ||
716 | |||
717 | memset(&wrqu, 0, sizeof(wrqu)); | ||
718 | wrqu.ap_addr.sa_family = ARPHRD_ETHER; | ||
719 | if (cr->bssid && cr->status == WLAN_STATUS_SUCCESS) { | ||
720 | memcpy(wrqu.ap_addr.sa_data, cr->bssid, ETH_ALEN); | ||
721 | memcpy(wdev->wext.prev_bssid, cr->bssid, ETH_ALEN); | ||
722 | wdev->wext.prev_bssid_valid = true; | ||
723 | } | ||
724 | wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL); | ||
725 | } | ||
726 | #endif | ||
727 | |||
728 | if (!cr->bss && (cr->status == WLAN_STATUS_SUCCESS)) { | ||
729 | WARN_ON_ONCE(!wiphy_to_rdev(wdev->wiphy)->ops->connect); | ||
730 | cr->bss = cfg80211_get_bss(wdev->wiphy, NULL, cr->bssid, | ||
731 | wdev->ssid, wdev->ssid_len, | ||
732 | wdev->conn_bss_type, | ||
733 | IEEE80211_PRIVACY_ANY); | ||
734 | if (cr->bss) | ||
735 | cfg80211_hold_bss(bss_from_pub(cr->bss)); | ||
736 | } | ||
737 | |||
738 | if (wdev->current_bss) { | ||
739 | cfg80211_unhold_bss(wdev->current_bss); | ||
740 | cfg80211_put_bss(wdev->wiphy, &wdev->current_bss->pub); | ||
741 | wdev->current_bss = NULL; | ||
742 | } | ||
743 | |||
744 | if (cr->status != WLAN_STATUS_SUCCESS) { | ||
745 | kfree_sensitive(wdev->connect_keys); | ||
746 | wdev->connect_keys = NULL; | ||
747 | wdev->ssid_len = 0; | ||
748 | wdev->conn_owner_nlportid = 0; | ||
749 | if (cr->bss) { | ||
750 | cfg80211_unhold_bss(bss_from_pub(cr->bss)); | ||
751 | cfg80211_put_bss(wdev->wiphy, cr->bss); | ||
752 | } | ||
753 | cfg80211_sme_free(wdev); | ||
754 | return; | ||
755 | } | ||
756 | |||
757 | if (WARN_ON(!cr->bss)) | ||
758 | return; | ||
759 | |||
760 | wdev->current_bss = bss_from_pub(cr->bss); | ||
761 | |||
762 | if (!(wdev->wiphy->flags & WIPHY_FLAG_HAS_STATIC_WEP)) | ||
763 | cfg80211_upload_connect_keys(wdev); | ||
764 | |||
765 | rcu_read_lock(); | ||
766 | country_ie = ieee80211_bss_get_ie(cr->bss, WLAN_EID_COUNTRY); | ||
767 | if (!country_ie) { | ||
768 | rcu_read_unlock(); | ||
769 | return; | ||
770 | } | ||
771 | |||
772 | country_ie = kmemdup(country_ie, 2 + country_ie[1], GFP_ATOMIC); | ||
773 | rcu_read_unlock(); | ||
774 | |||
775 | if (!country_ie) | ||
776 | return; | ||
777 | |||
778 | /* | ||
779 | * ieee80211_bss_get_ie() ensures we can access: | ||
780 | * - country_ie + 2, the start of the country ie data, and | ||
781 | * - and country_ie[1] which is the IE length | ||
782 | */ | ||
783 | regulatory_hint_country_ie(wdev->wiphy, cr->bss->channel->band, | ||
784 | country_ie + 2, country_ie[1]); | ||
785 | kfree(country_ie); | ||
786 | } | ||
787 | |||
788 | /* Consumes bss object one way or another */ | ||
789 | void cfg80211_connect_done(struct net_device *dev, | ||
790 | struct cfg80211_connect_resp_params *params, | ||
791 | gfp_t gfp) | ||
792 | { | ||
793 | struct wireless_dev *wdev = dev->ieee80211_ptr; | ||
794 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); | ||
795 | struct cfg80211_event *ev; | ||
796 | unsigned long flags; | ||
797 | u8 *next; | ||
798 | |||
799 | if (params->bss) { | ||
800 | struct cfg80211_internal_bss *ibss = bss_from_pub(params->bss); | ||
801 | |||
802 | if (list_empty(&ibss->list)) { | ||
803 | struct cfg80211_bss *found = NULL, *tmp = params->bss; | ||
804 | |||
805 | found = cfg80211_get_bss(wdev->wiphy, NULL, | ||
806 | params->bss->bssid, | ||
807 | wdev->ssid, wdev->ssid_len, | ||
808 | wdev->conn_bss_type, | ||
809 | IEEE80211_PRIVACY_ANY); | ||
810 | if (found) { | ||
811 | /* The same BSS is already updated so use it | ||
812 | * instead, as it has latest info. | ||
813 | */ | ||
814 | params->bss = found; | ||
815 | } else { | ||
816 | /* Update with BSS provided by driver, it will | ||
817 | * be freshly added and ref cnted, we can free | ||
818 | * the old one. | ||
819 | * | ||
820 | * signal_valid can be false, as we are not | ||
821 | * expecting the BSS to be found. | ||
822 | * | ||
823 | * keep the old timestamp to avoid confusion | ||
824 | */ | ||
825 | cfg80211_bss_update(rdev, ibss, false, | ||
826 | ibss->ts); | ||
827 | } | ||
828 | |||
829 | cfg80211_put_bss(wdev->wiphy, tmp); | ||
830 | } | ||
831 | } | ||
832 | |||
833 | ev = kzalloc(sizeof(*ev) + (params->bssid ? ETH_ALEN : 0) + | ||
834 | params->req_ie_len + params->resp_ie_len + | ||
835 | params->fils.kek_len + params->fils.pmk_len + | ||
836 | (params->fils.pmkid ? WLAN_PMKID_LEN : 0), gfp); | ||
837 | if (!ev) { | ||
838 | cfg80211_put_bss(wdev->wiphy, params->bss); | ||
839 | return; | ||
840 | } | ||
841 | |||
842 | ev->type = EVENT_CONNECT_RESULT; | ||
843 | next = ((u8 *)ev) + sizeof(*ev); | ||
844 | if (params->bssid) { | ||
845 | ev->cr.bssid = next; | ||
846 | memcpy((void *)ev->cr.bssid, params->bssid, ETH_ALEN); | ||
847 | next += ETH_ALEN; | ||
848 | } | ||
849 | if (params->req_ie_len) { | ||
850 | ev->cr.req_ie = next; | ||
851 | ev->cr.req_ie_len = params->req_ie_len; | ||
852 | memcpy((void *)ev->cr.req_ie, params->req_ie, | ||
853 | params->req_ie_len); | ||
854 | next += params->req_ie_len; | ||
855 | } | ||
856 | if (params->resp_ie_len) { | ||
857 | ev->cr.resp_ie = next; | ||
858 | ev->cr.resp_ie_len = params->resp_ie_len; | ||
859 | memcpy((void *)ev->cr.resp_ie, params->resp_ie, | ||
860 | params->resp_ie_len); | ||
861 | next += params->resp_ie_len; | ||
862 | } | ||
863 | if (params->fils.kek_len) { | ||
864 | ev->cr.fils.kek = next; | ||
865 | ev->cr.fils.kek_len = params->fils.kek_len; | ||
866 | memcpy((void *)ev->cr.fils.kek, params->fils.kek, | ||
867 | params->fils.kek_len); | ||
868 | next += params->fils.kek_len; | ||
869 | } | ||
870 | if (params->fils.pmk_len) { | ||
871 | ev->cr.fils.pmk = next; | ||
872 | ev->cr.fils.pmk_len = params->fils.pmk_len; | ||
873 | memcpy((void *)ev->cr.fils.pmk, params->fils.pmk, | ||
874 | params->fils.pmk_len); | ||
875 | next += params->fils.pmk_len; | ||
876 | } | ||
877 | if (params->fils.pmkid) { | ||
878 | ev->cr.fils.pmkid = next; | ||
879 | memcpy((void *)ev->cr.fils.pmkid, params->fils.pmkid, | ||
880 | WLAN_PMKID_LEN); | ||
881 | next += WLAN_PMKID_LEN; | ||
882 | } | ||
883 | ev->cr.fils.update_erp_next_seq_num = params->fils.update_erp_next_seq_num; | ||
884 | if (params->fils.update_erp_next_seq_num) | ||
885 | ev->cr.fils.erp_next_seq_num = params->fils.erp_next_seq_num; | ||
886 | if (params->bss) | ||
887 | cfg80211_hold_bss(bss_from_pub(params->bss)); | ||
888 | ev->cr.bss = params->bss; | ||
889 | ev->cr.status = params->status; | ||
890 | ev->cr.timeout_reason = params->timeout_reason; | ||
891 | |||
892 | spin_lock_irqsave(&wdev->event_lock, flags); | ||
893 | list_add_tail(&ev->list, &wdev->event_list); | ||
894 | spin_unlock_irqrestore(&wdev->event_lock, flags); | ||
895 | queue_work(cfg80211_wq, &rdev->event_work); | ||
896 | } | ||
897 | EXPORT_SYMBOL(cfg80211_connect_done); | ||
898 | |||
899 | /* Consumes bss object one way or another */ | ||
900 | void __cfg80211_roamed(struct wireless_dev *wdev, | ||
901 | struct cfg80211_roam_info *info) | ||
902 | { | ||
903 | #ifdef CONFIG_CFG80211_WEXT | ||
904 | union iwreq_data wrqu; | ||
905 | #endif | ||
906 | ASSERT_WDEV_LOCK(wdev); | ||
907 | |||
908 | if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION && | ||
909 | wdev->iftype != NL80211_IFTYPE_P2P_CLIENT)) | ||
910 | goto out; | ||
911 | |||
912 | if (WARN_ON(!wdev->current_bss)) | ||
913 | goto out; | ||
914 | |||
915 | cfg80211_unhold_bss(wdev->current_bss); | ||
916 | cfg80211_put_bss(wdev->wiphy, &wdev->current_bss->pub); | ||
917 | wdev->current_bss = NULL; | ||
918 | |||
919 | if (WARN_ON(!info->bss)) | ||
920 | return; | ||
921 | |||
922 | cfg80211_hold_bss(bss_from_pub(info->bss)); | ||
923 | wdev->current_bss = bss_from_pub(info->bss); | ||
924 | |||
925 | wdev->unprot_beacon_reported = 0; | ||
926 | nl80211_send_roamed(wiphy_to_rdev(wdev->wiphy), | ||
927 | wdev->netdev, info, GFP_KERNEL); | ||
928 | |||
929 | #ifdef CONFIG_CFG80211_WEXT | ||
930 | if (info->req_ie) { | ||
931 | memset(&wrqu, 0, sizeof(wrqu)); | ||
932 | wrqu.data.length = info->req_ie_len; | ||
933 | wireless_send_event(wdev->netdev, IWEVASSOCREQIE, | ||
934 | &wrqu, info->req_ie); | ||
935 | } | ||
936 | |||
937 | if (info->resp_ie) { | ||
938 | memset(&wrqu, 0, sizeof(wrqu)); | ||
939 | wrqu.data.length = info->resp_ie_len; | ||
940 | wireless_send_event(wdev->netdev, IWEVASSOCRESPIE, | ||
941 | &wrqu, info->resp_ie); | ||
942 | } | ||
943 | |||
944 | memset(&wrqu, 0, sizeof(wrqu)); | ||
945 | wrqu.ap_addr.sa_family = ARPHRD_ETHER; | ||
946 | memcpy(wrqu.ap_addr.sa_data, info->bss->bssid, ETH_ALEN); | ||
947 | memcpy(wdev->wext.prev_bssid, info->bss->bssid, ETH_ALEN); | ||
948 | wdev->wext.prev_bssid_valid = true; | ||
949 | wireless_send_event(wdev->netdev, SIOCGIWAP, &wrqu, NULL); | ||
950 | #endif | ||
951 | |||
952 | return; | ||
953 | out: | ||
954 | cfg80211_put_bss(wdev->wiphy, info->bss); | ||
955 | } | ||
956 | |||
957 | /* Consumes info->bss object one way or another */ | ||
958 | void cfg80211_roamed(struct net_device *dev, struct cfg80211_roam_info *info, | ||
959 | gfp_t gfp) | ||
960 | { | ||
961 | struct wireless_dev *wdev = dev->ieee80211_ptr; | ||
962 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); | ||
963 | struct cfg80211_event *ev; | ||
964 | unsigned long flags; | ||
965 | u8 *next; | ||
966 | |||
967 | if (!info->bss) { | ||
968 | info->bss = cfg80211_get_bss(wdev->wiphy, info->channel, | ||
969 | info->bssid, wdev->ssid, | ||
970 | wdev->ssid_len, | ||
971 | wdev->conn_bss_type, | ||
972 | IEEE80211_PRIVACY_ANY); | ||
973 | } | ||
974 | |||
975 | if (WARN_ON(!info->bss)) | ||
976 | return; | ||
977 | |||
978 | ev = kzalloc(sizeof(*ev) + info->req_ie_len + info->resp_ie_len + | ||
979 | info->fils.kek_len + info->fils.pmk_len + | ||
980 | (info->fils.pmkid ? WLAN_PMKID_LEN : 0), gfp); | ||
981 | if (!ev) { | ||
982 | cfg80211_put_bss(wdev->wiphy, info->bss); | ||
983 | return; | ||
984 | } | ||
985 | |||
986 | ev->type = EVENT_ROAMED; | ||
987 | next = ((u8 *)ev) + sizeof(*ev); | ||
988 | if (info->req_ie_len) { | ||
989 | ev->rm.req_ie = next; | ||
990 | ev->rm.req_ie_len = info->req_ie_len; | ||
991 | memcpy((void *)ev->rm.req_ie, info->req_ie, info->req_ie_len); | ||
992 | next += info->req_ie_len; | ||
993 | } | ||
994 | if (info->resp_ie_len) { | ||
995 | ev->rm.resp_ie = next; | ||
996 | ev->rm.resp_ie_len = info->resp_ie_len; | ||
997 | memcpy((void *)ev->rm.resp_ie, info->resp_ie, | ||
998 | info->resp_ie_len); | ||
999 | next += info->resp_ie_len; | ||
1000 | } | ||
1001 | if (info->fils.kek_len) { | ||
1002 | ev->rm.fils.kek = next; | ||
1003 | ev->rm.fils.kek_len = info->fils.kek_len; | ||
1004 | memcpy((void *)ev->rm.fils.kek, info->fils.kek, | ||
1005 | info->fils.kek_len); | ||
1006 | next += info->fils.kek_len; | ||
1007 | } | ||
1008 | if (info->fils.pmk_len) { | ||
1009 | ev->rm.fils.pmk = next; | ||
1010 | ev->rm.fils.pmk_len = info->fils.pmk_len; | ||
1011 | memcpy((void *)ev->rm.fils.pmk, info->fils.pmk, | ||
1012 | info->fils.pmk_len); | ||
1013 | next += info->fils.pmk_len; | ||
1014 | } | ||
1015 | if (info->fils.pmkid) { | ||
1016 | ev->rm.fils.pmkid = next; | ||
1017 | memcpy((void *)ev->rm.fils.pmkid, info->fils.pmkid, | ||
1018 | WLAN_PMKID_LEN); | ||
1019 | next += WLAN_PMKID_LEN; | ||
1020 | } | ||
1021 | ev->rm.fils.update_erp_next_seq_num = info->fils.update_erp_next_seq_num; | ||
1022 | if (info->fils.update_erp_next_seq_num) | ||
1023 | ev->rm.fils.erp_next_seq_num = info->fils.erp_next_seq_num; | ||
1024 | ev->rm.bss = info->bss; | ||
1025 | |||
1026 | spin_lock_irqsave(&wdev->event_lock, flags); | ||
1027 | list_add_tail(&ev->list, &wdev->event_list); | ||
1028 | spin_unlock_irqrestore(&wdev->event_lock, flags); | ||
1029 | queue_work(cfg80211_wq, &rdev->event_work); | ||
1030 | } | ||
1031 | EXPORT_SYMBOL(cfg80211_roamed); | ||
1032 | |||
1033 | void __cfg80211_port_authorized(struct wireless_dev *wdev, const u8 *bssid) | ||
1034 | { | ||
1035 | ASSERT_WDEV_LOCK(wdev); | ||
1036 | |||
1037 | if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION)) | ||
1038 | return; | ||
1039 | |||
1040 | if (WARN_ON(!wdev->current_bss) || | ||
1041 | WARN_ON(!ether_addr_equal(wdev->current_bss->pub.bssid, bssid))) | ||
1042 | return; | ||
1043 | |||
1044 | nl80211_send_port_authorized(wiphy_to_rdev(wdev->wiphy), wdev->netdev, | ||
1045 | bssid); | ||
1046 | } | ||
1047 | |||
1048 | void cfg80211_port_authorized(struct net_device *dev, const u8 *bssid, | ||
1049 | gfp_t gfp) | ||
1050 | { | ||
1051 | struct wireless_dev *wdev = dev->ieee80211_ptr; | ||
1052 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); | ||
1053 | struct cfg80211_event *ev; | ||
1054 | unsigned long flags; | ||
1055 | |||
1056 | if (WARN_ON(!bssid)) | ||
1057 | return; | ||
1058 | |||
1059 | ev = kzalloc(sizeof(*ev), gfp); | ||
1060 | if (!ev) | ||
1061 | return; | ||
1062 | |||
1063 | ev->type = EVENT_PORT_AUTHORIZED; | ||
1064 | memcpy(ev->pa.bssid, bssid, ETH_ALEN); | ||
1065 | |||
1066 | /* | ||
1067 | * Use the wdev event list so that if there are pending | ||
1068 | * connected/roamed events, they will be reported first. | ||
1069 | */ | ||
1070 | spin_lock_irqsave(&wdev->event_lock, flags); | ||
1071 | list_add_tail(&ev->list, &wdev->event_list); | ||
1072 | spin_unlock_irqrestore(&wdev->event_lock, flags); | ||
1073 | queue_work(cfg80211_wq, &rdev->event_work); | ||
1074 | } | ||
1075 | EXPORT_SYMBOL(cfg80211_port_authorized); | ||
1076 | |||
1077 | void __cfg80211_disconnected(struct net_device *dev, const u8 *ie, | ||
1078 | size_t ie_len, u16 reason, bool from_ap) | ||
1079 | { | ||
1080 | struct wireless_dev *wdev = dev->ieee80211_ptr; | ||
1081 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); | ||
1082 | int i; | ||
1083 | #ifdef CONFIG_CFG80211_WEXT | ||
1084 | union iwreq_data wrqu; | ||
1085 | #endif | ||
1086 | |||
1087 | ASSERT_WDEV_LOCK(wdev); | ||
1088 | |||
1089 | if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION && | ||
1090 | wdev->iftype != NL80211_IFTYPE_P2P_CLIENT)) | ||
1091 | return; | ||
1092 | |||
1093 | if (wdev->current_bss) { | ||
1094 | cfg80211_unhold_bss(wdev->current_bss); | ||
1095 | cfg80211_put_bss(wdev->wiphy, &wdev->current_bss->pub); | ||
1096 | } | ||
1097 | |||
1098 | wdev->current_bss = NULL; | ||
1099 | wdev->ssid_len = 0; | ||
1100 | wdev->conn_owner_nlportid = 0; | ||
1101 | kfree_sensitive(wdev->connect_keys); | ||
1102 | wdev->connect_keys = NULL; | ||
1103 | |||
1104 | nl80211_send_disconnected(rdev, dev, reason, ie, ie_len, from_ap); | ||
1105 | |||
1106 | /* stop critical protocol if supported */ | ||
1107 | if (rdev->ops->crit_proto_stop && rdev->crit_proto_nlportid) { | ||
1108 | rdev->crit_proto_nlportid = 0; | ||
1109 | rdev_crit_proto_stop(rdev, wdev); | ||
1110 | } | ||
1111 | |||
1112 | /* | ||
1113 | * Delete all the keys ... pairwise keys can't really | ||
1114 | * exist any more anyway, but default keys might. | ||
1115 | */ | ||
1116 | if (rdev->ops->del_key) { | ||
1117 | int max_key_idx = 5; | ||
1118 | |||
1119 | if (wiphy_ext_feature_isset( | ||
1120 | wdev->wiphy, | ||
1121 | NL80211_EXT_FEATURE_BEACON_PROTECTION) || | ||
1122 | wiphy_ext_feature_isset( | ||
1123 | wdev->wiphy, | ||
1124 | NL80211_EXT_FEATURE_BEACON_PROTECTION_CLIENT)) | ||
1125 | max_key_idx = 7; | ||
1126 | for (i = 0; i <= max_key_idx; i++) | ||
1127 | rdev_del_key(rdev, dev, i, false, NULL); | ||
1128 | } | ||
1129 | |||
1130 | rdev_set_qos_map(rdev, dev, NULL); | ||
1131 | |||
1132 | #ifdef CONFIG_CFG80211_WEXT | ||
1133 | memset(&wrqu, 0, sizeof(wrqu)); | ||
1134 | wrqu.ap_addr.sa_family = ARPHRD_ETHER; | ||
1135 | wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL); | ||
1136 | wdev->wext.connect.ssid_len = 0; | ||
1137 | #endif | ||
1138 | |||
1139 | schedule_work(&cfg80211_disconnect_work); | ||
1140 | } | ||
1141 | |||
1142 | void cfg80211_disconnected(struct net_device *dev, u16 reason, | ||
1143 | const u8 *ie, size_t ie_len, | ||
1144 | bool locally_generated, gfp_t gfp) | ||
1145 | { | ||
1146 | struct wireless_dev *wdev = dev->ieee80211_ptr; | ||
1147 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); | ||
1148 | struct cfg80211_event *ev; | ||
1149 | unsigned long flags; | ||
1150 | |||
1151 | ev = kzalloc(sizeof(*ev) + ie_len, gfp); | ||
1152 | if (!ev) | ||
1153 | return; | ||
1154 | |||
1155 | ev->type = EVENT_DISCONNECTED; | ||
1156 | ev->dc.ie = ((u8 *)ev) + sizeof(*ev); | ||
1157 | ev->dc.ie_len = ie_len; | ||
1158 | memcpy((void *)ev->dc.ie, ie, ie_len); | ||
1159 | ev->dc.reason = reason; | ||
1160 | ev->dc.locally_generated = locally_generated; | ||
1161 | |||
1162 | spin_lock_irqsave(&wdev->event_lock, flags); | ||
1163 | list_add_tail(&ev->list, &wdev->event_list); | ||
1164 | spin_unlock_irqrestore(&wdev->event_lock, flags); | ||
1165 | queue_work(cfg80211_wq, &rdev->event_work); | ||
1166 | } | ||
1167 | EXPORT_SYMBOL(cfg80211_disconnected); | ||
1168 | |||
1169 | /* | ||
1170 | * API calls for nl80211/wext compatibility code | ||
1171 | */ | ||
1172 | int cfg80211_connect(struct cfg80211_registered_device *rdev, | ||
1173 | struct net_device *dev, | ||
1174 | struct cfg80211_connect_params *connect, | ||
1175 | struct cfg80211_cached_keys *connkeys, | ||
1176 | const u8 *prev_bssid) | ||
1177 | { | ||
1178 | struct wireless_dev *wdev = dev->ieee80211_ptr; | ||
1179 | int err; | ||
1180 | |||
1181 | ASSERT_WDEV_LOCK(wdev); | ||
1182 | |||
1183 | /* | ||
1184 | * If we have an ssid_len, we're trying to connect or are | ||
1185 | * already connected, so reject a new SSID unless it's the | ||
1186 | * same (which is the case for re-association.) | ||
1187 | */ | ||
1188 | if (wdev->ssid_len && | ||
1189 | (wdev->ssid_len != connect->ssid_len || | ||
1190 | memcmp(wdev->ssid, connect->ssid, wdev->ssid_len))) | ||
1191 | return -EALREADY; | ||
1192 | |||
1193 | /* | ||
1194 | * If connected, reject (re-)association unless prev_bssid | ||
1195 | * matches the current BSSID. | ||
1196 | */ | ||
1197 | if (wdev->current_bss) { | ||
1198 | if (!prev_bssid) | ||
1199 | return -EALREADY; | ||
1200 | if (!ether_addr_equal(prev_bssid, wdev->current_bss->pub.bssid)) | ||
1201 | return -ENOTCONN; | ||
1202 | } | ||
1203 | |||
1204 | /* | ||
1205 | * Reject if we're in the process of connecting with WEP, | ||
1206 | * this case isn't very interesting and trying to handle | ||
1207 | * it would make the code much more complex. | ||
1208 | */ | ||
1209 | if (wdev->connect_keys) | ||
1210 | return -EINPROGRESS; | ||
1211 | |||
1212 | cfg80211_oper_and_ht_capa(&connect->ht_capa_mask, | ||
1213 | rdev->wiphy.ht_capa_mod_mask); | ||
1214 | cfg80211_oper_and_vht_capa(&connect->vht_capa_mask, | ||
1215 | rdev->wiphy.vht_capa_mod_mask); | ||
1216 | |||
1217 | if (connkeys && connkeys->def >= 0) { | ||
1218 | int idx; | ||
1219 | u32 cipher; | ||
1220 | |||
1221 | idx = connkeys->def; | ||
1222 | cipher = connkeys->params[idx].cipher; | ||
1223 | /* If given a WEP key we may need it for shared key auth */ | ||
1224 | if (cipher == WLAN_CIPHER_SUITE_WEP40 || | ||
1225 | cipher == WLAN_CIPHER_SUITE_WEP104) { | ||
1226 | connect->key_idx = idx; | ||
1227 | connect->key = connkeys->params[idx].key; | ||
1228 | connect->key_len = connkeys->params[idx].key_len; | ||
1229 | |||
1230 | /* | ||
1231 | * If ciphers are not set (e.g. when going through | ||
1232 | * iwconfig), we have to set them appropriately here. | ||
1233 | */ | ||
1234 | if (connect->crypto.cipher_group == 0) | ||
1235 | connect->crypto.cipher_group = cipher; | ||
1236 | |||
1237 | if (connect->crypto.n_ciphers_pairwise == 0) { | ||
1238 | connect->crypto.n_ciphers_pairwise = 1; | ||
1239 | connect->crypto.ciphers_pairwise[0] = cipher; | ||
1240 | } | ||
1241 | } | ||
1242 | |||
1243 | connect->crypto.wep_keys = connkeys->params; | ||
1244 | connect->crypto.wep_tx_key = connkeys->def; | ||
1245 | } else { | ||
1246 | if (WARN_ON(connkeys)) | ||
1247 | return -EINVAL; | ||
1248 | } | ||
1249 | |||
1250 | wdev->connect_keys = connkeys; | ||
1251 | memcpy(wdev->ssid, connect->ssid, connect->ssid_len); | ||
1252 | wdev->ssid_len = connect->ssid_len; | ||
1253 | |||
1254 | wdev->conn_bss_type = connect->pbss ? IEEE80211_BSS_TYPE_PBSS : | ||
1255 | IEEE80211_BSS_TYPE_ESS; | ||
1256 | |||
1257 | if (!rdev->ops->connect) | ||
1258 | err = cfg80211_sme_connect(wdev, connect, prev_bssid); | ||
1259 | else | ||
1260 | err = rdev_connect(rdev, dev, connect); | ||
1261 | |||
1262 | if (err) { | ||
1263 | wdev->connect_keys = NULL; | ||
1264 | /* | ||
1265 | * This could be reassoc getting refused, don't clear | ||
1266 | * ssid_len in that case. | ||
1267 | */ | ||
1268 | if (!wdev->current_bss) | ||
1269 | wdev->ssid_len = 0; | ||
1270 | return err; | ||
1271 | } | ||
1272 | |||
1273 | return 0; | ||
1274 | } | ||
1275 | |||
1276 | int cfg80211_disconnect(struct cfg80211_registered_device *rdev, | ||
1277 | struct net_device *dev, u16 reason, bool wextev) | ||
1278 | { | ||
1279 | struct wireless_dev *wdev = dev->ieee80211_ptr; | ||
1280 | int err = 0; | ||
1281 | |||
1282 | ASSERT_WDEV_LOCK(wdev); | ||
1283 | |||
1284 | kfree_sensitive(wdev->connect_keys); | ||
1285 | wdev->connect_keys = NULL; | ||
1286 | |||
1287 | wdev->conn_owner_nlportid = 0; | ||
1288 | |||
1289 | if (wdev->conn) | ||
1290 | err = cfg80211_sme_disconnect(wdev, reason); | ||
1291 | else if (!rdev->ops->disconnect) | ||
1292 | cfg80211_mlme_down(rdev, dev); | ||
1293 | else if (wdev->ssid_len) | ||
1294 | err = rdev_disconnect(rdev, dev, reason); | ||
1295 | |||
1296 | /* | ||
1297 | * Clear ssid_len unless we actually were fully connected, | ||
1298 | * in which case cfg80211_disconnected() will take care of | ||
1299 | * this later. | ||
1300 | */ | ||
1301 | if (!wdev->current_bss) | ||
1302 | wdev->ssid_len = 0; | ||
1303 | |||
1304 | return err; | ||
1305 | } | ||
1306 | |||
1307 | /* | ||
1308 | * Used to clean up after the connection / connection attempt owner socket | ||
1309 | * disconnects | ||
1310 | */ | ||
1311 | void cfg80211_autodisconnect_wk(struct work_struct *work) | ||
1312 | { | ||
1313 | struct wireless_dev *wdev = | ||
1314 | container_of(work, struct wireless_dev, disconnect_wk); | ||
1315 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); | ||
1316 | |||
1317 | wdev_lock(wdev); | ||
1318 | |||
1319 | if (wdev->conn_owner_nlportid) { | ||
1320 | switch (wdev->iftype) { | ||
1321 | case NL80211_IFTYPE_ADHOC: | ||
1322 | __cfg80211_leave_ibss(rdev, wdev->netdev, false); | ||
1323 | break; | ||
1324 | case NL80211_IFTYPE_AP: | ||
1325 | case NL80211_IFTYPE_P2P_GO: | ||
1326 | __cfg80211_stop_ap(rdev, wdev->netdev, false); | ||
1327 | break; | ||
1328 | case NL80211_IFTYPE_MESH_POINT: | ||
1329 | __cfg80211_leave_mesh(rdev, wdev->netdev); | ||
1330 | break; | ||
1331 | case NL80211_IFTYPE_STATION: | ||
1332 | case NL80211_IFTYPE_P2P_CLIENT: | ||
1333 | /* | ||
1334 | * Use disconnect_bssid if still connecting and | ||
1335 | * ops->disconnect not implemented. Otherwise we can | ||
1336 | * use cfg80211_disconnect. | ||
1337 | */ | ||
1338 | if (rdev->ops->disconnect || wdev->current_bss) | ||
1339 | cfg80211_disconnect(rdev, wdev->netdev, | ||
1340 | WLAN_REASON_DEAUTH_LEAVING, | ||
1341 | true); | ||
1342 | else | ||
1343 | cfg80211_mlme_deauth(rdev, wdev->netdev, | ||
1344 | wdev->disconnect_bssid, | ||
1345 | NULL, 0, | ||
1346 | WLAN_REASON_DEAUTH_LEAVING, | ||
1347 | false); | ||
1348 | break; | ||
1349 | default: | ||
1350 | break; | ||
1351 | } | ||
1352 | } | ||
1353 | |||
1354 | wdev_unlock(wdev); | ||
1355 | } | ||