blob: 3dba19e1727147610af5630704e1a77f17887b0a [file] [log] [blame]
Jouni Malinen6039f6d2009-03-19 13:39:21 +02001/*
2 * cfg80211 MLME SAP interface
3 *
4 * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
5 */
6
7#include <linux/kernel.h>
8#include <linux/module.h>
9#include <linux/netdevice.h>
10#include <linux/nl80211.h>
Johannes Berga9a11622009-07-27 12:01:53 +020011#include <linux/wireless.h>
Jouni Malinen6039f6d2009-03-19 13:39:21 +020012#include <net/cfg80211.h>
Johannes Berga9a11622009-07-27 12:01:53 +020013#include <net/iw_handler.h>
Jouni Malinen6039f6d2009-03-19 13:39:21 +020014#include "core.h"
15#include "nl80211.h"
16
Johannes Bergcb0b4be2009-07-07 03:56:07 +020017void cfg80211_send_rx_auth(struct net_device *dev, const u8 *buf, size_t len)
Jouni Malinen6039f6d2009-03-19 13:39:21 +020018{
Johannes Berg19957bb2009-07-02 17:20:43 +020019 struct wireless_dev *wdev = dev->ieee80211_ptr;
20 struct wiphy *wiphy = wdev->wiphy;
Jouni Malinen6039f6d2009-03-19 13:39:21 +020021 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
Johannes Berg19957bb2009-07-02 17:20:43 +020022 struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf;
23 u8 *bssid = mgmt->bssid;
24 int i;
25 u16 status = le16_to_cpu(mgmt->u.auth.status_code);
26 bool done = false;
27
Johannes Berg667503dd2009-07-07 03:56:11 +020028 wdev_lock(wdev);
Johannes Bergcb0b4be2009-07-07 03:56:07 +020029
Johannes Berg19957bb2009-07-02 17:20:43 +020030 for (i = 0; i < MAX_AUTH_BSSES; i++) {
31 if (wdev->authtry_bsses[i] &&
32 memcmp(wdev->authtry_bsses[i]->pub.bssid, bssid,
33 ETH_ALEN) == 0) {
34 if (status == WLAN_STATUS_SUCCESS) {
35 wdev->auth_bsses[i] = wdev->authtry_bsses[i];
36 } else {
37 cfg80211_unhold_bss(wdev->authtry_bsses[i]);
38 cfg80211_put_bss(&wdev->authtry_bsses[i]->pub);
39 }
40 wdev->authtry_bsses[i] = NULL;
41 done = true;
42 break;
43 }
44 }
45
46 WARN_ON(!done);
Johannes Berg6829c872009-07-02 09:13:27 +020047
Johannes Bergcb0b4be2009-07-07 03:56:07 +020048 nl80211_send_rx_auth(rdev, dev, buf, len, GFP_KERNEL);
Johannes Berg6829c872009-07-02 09:13:27 +020049 cfg80211_sme_rx_auth(dev, buf, len);
Johannes Berg667503dd2009-07-07 03:56:11 +020050
51 wdev_unlock(wdev);
Jouni Malinen6039f6d2009-03-19 13:39:21 +020052}
53EXPORT_SYMBOL(cfg80211_send_rx_auth);
54
Johannes Bergcb0b4be2009-07-07 03:56:07 +020055void cfg80211_send_rx_assoc(struct net_device *dev, const u8 *buf, size_t len)
Jouni Malinen6039f6d2009-03-19 13:39:21 +020056{
Johannes Berg6829c872009-07-02 09:13:27 +020057 u16 status_code;
58 struct wireless_dev *wdev = dev->ieee80211_ptr;
59 struct wiphy *wiphy = wdev->wiphy;
Jouni Malinen6039f6d2009-03-19 13:39:21 +020060 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
Johannes Berg6829c872009-07-02 09:13:27 +020061 struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf;
62 u8 *ie = mgmt->u.assoc_resp.variable;
Johannes Berg19957bb2009-07-02 17:20:43 +020063 int i, ieoffs = offsetof(struct ieee80211_mgmt, u.assoc_resp.variable);
Johannes Bergdf7fc0f2009-07-29 11:23:49 +020064 struct cfg80211_internal_bss *bss = NULL;
Johannes Berg6829c872009-07-02 09:13:27 +020065
Johannes Berg667503dd2009-07-07 03:56:11 +020066 wdev_lock(wdev);
Johannes Bergcb0b4be2009-07-07 03:56:07 +020067
Johannes Berg6829c872009-07-02 09:13:27 +020068 status_code = le16_to_cpu(mgmt->u.assoc_resp.status_code);
69
Johannes Bergf401a6f2009-08-07 14:51:05 +020070 /*
71 * This is a bit of a hack, we don't notify userspace of
72 * a (re-)association reply if we tried to send a reassoc
73 * and got a reject -- we only try again with an assoc
74 * frame instead of reassoc.
75 */
76 if (status_code != WLAN_STATUS_SUCCESS && wdev->conn &&
77 cfg80211_sme_failed_reassoc(wdev))
78 goto out;
79
Johannes Bergcb0b4be2009-07-07 03:56:07 +020080 nl80211_send_rx_assoc(rdev, dev, buf, len, GFP_KERNEL);
Johannes Berg6829c872009-07-02 09:13:27 +020081
Johannes Berg19957bb2009-07-02 17:20:43 +020082 if (status_code == WLAN_STATUS_SUCCESS) {
Johannes Bergdf7fc0f2009-07-29 11:23:49 +020083 for (i = 0; i < MAX_AUTH_BSSES; i++) {
84 if (!wdev->auth_bsses[i])
85 continue;
86 if (memcmp(wdev->auth_bsses[i]->pub.bssid, mgmt->bssid,
87 ETH_ALEN) == 0) {
88 bss = wdev->auth_bsses[i];
Johannes Berg19957bb2009-07-02 17:20:43 +020089 wdev->auth_bsses[i] = NULL;
Johannes Bergdf7fc0f2009-07-29 11:23:49 +020090 /* additional reference to drop hold */
91 cfg80211_ref_bss(bss);
Johannes Berg19957bb2009-07-02 17:20:43 +020092 break;
93 }
94 }
95
Johannes Bergdf7fc0f2009-07-29 11:23:49 +020096 WARN_ON(!bss);
Johannes Berg7d930bc2009-10-20 15:08:53 +090097 } else if (wdev->conn) {
98 cfg80211_sme_failed_assoc(wdev);
Johannes Berg7d930bc2009-10-20 15:08:53 +090099 /*
100 * do not call connect_result() now because the
101 * sme will schedule work that does it later.
102 */
103 goto out;
Johannes Bergdf7fc0f2009-07-29 11:23:49 +0200104 }
105
Johannes Bergea416a72009-08-17 12:22:14 +0200106 if (!wdev->conn && wdev->sme_state == CFG80211_SME_IDLE) {
107 /*
108 * This is for the userspace SME, the CONNECTING
109 * state will be changed to CONNECTED by
110 * __cfg80211_connect_result() below.
111 */
112 wdev->sme_state = CFG80211_SME_CONNECTING;
113 }
114
Johannes Bergdf7fc0f2009-07-29 11:23:49 +0200115 /* this consumes one bss reference (unless bss is NULL) */
116 __cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, ie, len - ieoffs,
117 status_code,
118 status_code == WLAN_STATUS_SUCCESS,
119 bss ? &bss->pub : NULL);
120 /* drop hold now, and also reference acquired above */
121 if (bss) {
122 cfg80211_unhold_bss(bss);
123 cfg80211_put_bss(&bss->pub);
Johannes Berg19957bb2009-07-02 17:20:43 +0200124 }
Johannes Berg667503dd2009-07-07 03:56:11 +0200125
Johannes Bergf401a6f2009-08-07 14:51:05 +0200126 out:
Johannes Berg667503dd2009-07-07 03:56:11 +0200127 wdev_unlock(wdev);
Jouni Malinen6039f6d2009-03-19 13:39:21 +0200128}
129EXPORT_SYMBOL(cfg80211_send_rx_assoc);
130
Holger Schurigce470612009-10-13 13:28:13 +0200131void __cfg80211_send_deauth(struct net_device *dev,
Johannes Berg667503dd2009-07-07 03:56:11 +0200132 const u8 *buf, size_t len)
Jouni Malinen6039f6d2009-03-19 13:39:21 +0200133{
Johannes Berg6829c872009-07-02 09:13:27 +0200134 struct wireless_dev *wdev = dev->ieee80211_ptr;
135 struct wiphy *wiphy = wdev->wiphy;
Jouni Malinen6039f6d2009-03-19 13:39:21 +0200136 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
Johannes Berg6829c872009-07-02 09:13:27 +0200137 struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf;
Johannes Berg19957bb2009-07-02 17:20:43 +0200138 const u8 *bssid = mgmt->bssid;
139 int i;
Johannes Berg5fba4af2009-12-02 12:43:42 +0100140 bool found = false;
Johannes Berg6829c872009-07-02 09:13:27 +0200141
Johannes Berg667503dd2009-07-07 03:56:11 +0200142 ASSERT_WDEV_LOCK(wdev);
Johannes Bergcb0b4be2009-07-07 03:56:07 +0200143
Johannes Berg19957bb2009-07-02 17:20:43 +0200144 if (wdev->current_bss &&
145 memcmp(wdev->current_bss->pub.bssid, bssid, ETH_ALEN) == 0) {
Johannes Berg19957bb2009-07-02 17:20:43 +0200146 cfg80211_unhold_bss(wdev->current_bss);
147 cfg80211_put_bss(&wdev->current_bss->pub);
148 wdev->current_bss = NULL;
Johannes Berg5fba4af2009-12-02 12:43:42 +0100149 found = true;
Johannes Berg19957bb2009-07-02 17:20:43 +0200150 } else for (i = 0; i < MAX_AUTH_BSSES; i++) {
151 if (wdev->auth_bsses[i] &&
152 memcmp(wdev->auth_bsses[i]->pub.bssid, bssid, ETH_ALEN) == 0) {
153 cfg80211_unhold_bss(wdev->auth_bsses[i]);
154 cfg80211_put_bss(&wdev->auth_bsses[i]->pub);
155 wdev->auth_bsses[i] = NULL;
Johannes Berg5fba4af2009-12-02 12:43:42 +0100156 found = true;
Johannes Berg19957bb2009-07-02 17:20:43 +0200157 break;
158 }
159 if (wdev->authtry_bsses[i] &&
160 memcmp(wdev->authtry_bsses[i]->pub.bssid, bssid, ETH_ALEN) == 0) {
161 cfg80211_unhold_bss(wdev->authtry_bsses[i]);
162 cfg80211_put_bss(&wdev->authtry_bsses[i]->pub);
163 wdev->authtry_bsses[i] = NULL;
Johannes Berg5fba4af2009-12-02 12:43:42 +0100164 found = true;
Johannes Berg19957bb2009-07-02 17:20:43 +0200165 break;
166 }
167 }
Johannes Berg19957bb2009-07-02 17:20:43 +0200168
Johannes Berg5fba4af2009-12-02 12:43:42 +0100169 if (!found)
170 return;
171
172 nl80211_send_deauth(rdev, dev, buf, len, GFP_KERNEL);
173
Johannes Berg6829c872009-07-02 09:13:27 +0200174 if (wdev->sme_state == CFG80211_SME_CONNECTED) {
175 u16 reason_code;
176 bool from_ap;
177
178 reason_code = le16_to_cpu(mgmt->u.deauth.reason_code);
179
Johannes Berge458b8a2009-08-06 20:41:33 +0200180 from_ap = memcmp(mgmt->sa, dev->dev_addr, ETH_ALEN) != 0;
Johannes Berg667503dd2009-07-07 03:56:11 +0200181 __cfg80211_disconnected(dev, NULL, 0, reason_code, from_ap);
Johannes Berg6829c872009-07-02 09:13:27 +0200182 } else if (wdev->sme_state == CFG80211_SME_CONNECTING) {
Johannes Berg667503dd2009-07-07 03:56:11 +0200183 __cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, NULL, 0,
184 WLAN_STATUS_UNSPECIFIED_FAILURE,
Johannes Bergdf7fc0f2009-07-29 11:23:49 +0200185 false, NULL);
Johannes Berg667503dd2009-07-07 03:56:11 +0200186 }
187}
Holger Schurigce470612009-10-13 13:28:13 +0200188EXPORT_SYMBOL(__cfg80211_send_deauth);
Johannes Berg667503dd2009-07-07 03:56:11 +0200189
Holger Schurigce470612009-10-13 13:28:13 +0200190void cfg80211_send_deauth(struct net_device *dev, const u8 *buf, size_t len)
Johannes Berg667503dd2009-07-07 03:56:11 +0200191{
192 struct wireless_dev *wdev = dev->ieee80211_ptr;
193
Holger Schurigce470612009-10-13 13:28:13 +0200194 wdev_lock(wdev);
195 __cfg80211_send_deauth(dev, buf, len);
196 wdev_unlock(wdev);
Jouni Malinen6039f6d2009-03-19 13:39:21 +0200197}
Jouni Malinen53b46b82009-03-27 20:53:56 +0200198EXPORT_SYMBOL(cfg80211_send_deauth);
Jouni Malinen6039f6d2009-03-19 13:39:21 +0200199
Holger Schurigce470612009-10-13 13:28:13 +0200200void __cfg80211_send_disassoc(struct net_device *dev,
Johannes Berg667503dd2009-07-07 03:56:11 +0200201 const u8 *buf, size_t len)
Jouni Malinen6039f6d2009-03-19 13:39:21 +0200202{
Johannes Berg6829c872009-07-02 09:13:27 +0200203 struct wireless_dev *wdev = dev->ieee80211_ptr;
204 struct wiphy *wiphy = wdev->wiphy;
Jouni Malinen6039f6d2009-03-19 13:39:21 +0200205 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
Johannes Berg6829c872009-07-02 09:13:27 +0200206 struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf;
Johannes Berg19957bb2009-07-02 17:20:43 +0200207 const u8 *bssid = mgmt->bssid;
208 int i;
209 u16 reason_code;
210 bool from_ap;
211 bool done = false;
Johannes Berg6829c872009-07-02 09:13:27 +0200212
Johannes Berg596a07c2009-07-11 00:17:32 +0200213 ASSERT_WDEV_LOCK(wdev);
Johannes Bergcb0b4be2009-07-07 03:56:07 +0200214
215 nl80211_send_disassoc(rdev, dev, buf, len, GFP_KERNEL);
Johannes Berg6829c872009-07-02 09:13:27 +0200216
Johannes Berg596a07c2009-07-11 00:17:32 +0200217 if (wdev->sme_state != CFG80211_SME_CONNECTED)
218 return;
Johannes Berg6829c872009-07-02 09:13:27 +0200219
Johannes Berg19957bb2009-07-02 17:20:43 +0200220 if (wdev->current_bss &&
Pavel Roskinb935df02009-08-06 04:52:42 -0400221 memcmp(wdev->current_bss->pub.bssid, bssid, ETH_ALEN) == 0) {
Johannes Berg19957bb2009-07-02 17:20:43 +0200222 for (i = 0; i < MAX_AUTH_BSSES; i++) {
223 if (wdev->authtry_bsses[i] || wdev->auth_bsses[i])
224 continue;
225 wdev->auth_bsses[i] = wdev->current_bss;
226 wdev->current_bss = NULL;
227 done = true;
228 cfg80211_sme_disassoc(dev, i);
229 break;
230 }
231 WARN_ON(!done);
232 } else
233 WARN_ON(1);
Johannes Berg6829c872009-07-02 09:13:27 +0200234
Johannes Berg6829c872009-07-02 09:13:27 +0200235
Johannes Berg19957bb2009-07-02 17:20:43 +0200236 reason_code = le16_to_cpu(mgmt->u.disassoc.reason_code);
237
Johannes Berge458b8a2009-08-06 20:41:33 +0200238 from_ap = memcmp(mgmt->sa, dev->dev_addr, ETH_ALEN) != 0;
Johannes Berg667503dd2009-07-07 03:56:11 +0200239 __cfg80211_disconnected(dev, NULL, 0, reason_code, from_ap);
Johannes Berg667503dd2009-07-07 03:56:11 +0200240}
Holger Schurigce470612009-10-13 13:28:13 +0200241EXPORT_SYMBOL(__cfg80211_send_disassoc);
Johannes Berg667503dd2009-07-07 03:56:11 +0200242
Holger Schurigce470612009-10-13 13:28:13 +0200243void cfg80211_send_disassoc(struct net_device *dev, const u8 *buf, size_t len)
Johannes Berg667503dd2009-07-07 03:56:11 +0200244{
245 struct wireless_dev *wdev = dev->ieee80211_ptr;
246
Holger Schurigce470612009-10-13 13:28:13 +0200247 wdev_lock(wdev);
248 __cfg80211_send_disassoc(dev, buf, len);
249 wdev_unlock(wdev);
Jouni Malinen6039f6d2009-03-19 13:39:21 +0200250}
Jouni Malinen53b46b82009-03-27 20:53:56 +0200251EXPORT_SYMBOL(cfg80211_send_disassoc);
Jouni Malinena3b8b052009-03-27 21:59:49 +0200252
Johannes Berga58ce432009-11-19 12:45:42 +0100253static void __cfg80211_auth_remove(struct wireless_dev *wdev, const u8 *addr)
Jouni Malinen1965c852009-04-22 21:38:25 +0300254{
Johannes Berg19957bb2009-07-02 17:20:43 +0200255 int i;
256 bool done = false;
257
Johannes Berga58ce432009-11-19 12:45:42 +0100258 ASSERT_WDEV_LOCK(wdev);
Johannes Berg19957bb2009-07-02 17:20:43 +0200259
260 for (i = 0; addr && i < MAX_AUTH_BSSES; i++) {
261 if (wdev->authtry_bsses[i] &&
262 memcmp(wdev->authtry_bsses[i]->pub.bssid,
263 addr, ETH_ALEN) == 0) {
264 cfg80211_unhold_bss(wdev->authtry_bsses[i]);
265 cfg80211_put_bss(&wdev->authtry_bsses[i]->pub);
266 wdev->authtry_bsses[i] = NULL;
267 done = true;
268 break;
269 }
270 }
271
272 WARN_ON(!done);
Johannes Berga58ce432009-11-19 12:45:42 +0100273}
274
275void __cfg80211_auth_canceled(struct net_device *dev, const u8 *addr)
276{
277 __cfg80211_auth_remove(dev->ieee80211_ptr, addr);
278}
279EXPORT_SYMBOL(__cfg80211_auth_canceled);
280
281void cfg80211_send_auth_timeout(struct net_device *dev, const u8 *addr)
282{
283 struct wireless_dev *wdev = dev->ieee80211_ptr;
284 struct wiphy *wiphy = wdev->wiphy;
285 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
286
287 wdev_lock(wdev);
288
289 nl80211_send_auth_timeout(rdev, dev, addr, GFP_KERNEL);
290 if (wdev->sme_state == CFG80211_SME_CONNECTING)
291 __cfg80211_connect_result(dev, addr, NULL, 0, NULL, 0,
292 WLAN_STATUS_UNSPECIFIED_FAILURE,
293 false, NULL);
294
295 __cfg80211_auth_remove(wdev, addr);
Johannes Berg667503dd2009-07-07 03:56:11 +0200296
297 wdev_unlock(wdev);
Jouni Malinen1965c852009-04-22 21:38:25 +0300298}
299EXPORT_SYMBOL(cfg80211_send_auth_timeout);
300
Johannes Bergcb0b4be2009-07-07 03:56:07 +0200301void cfg80211_send_assoc_timeout(struct net_device *dev, const u8 *addr)
Jouni Malinen1965c852009-04-22 21:38:25 +0300302{
Johannes Berg6829c872009-07-02 09:13:27 +0200303 struct wireless_dev *wdev = dev->ieee80211_ptr;
304 struct wiphy *wiphy = wdev->wiphy;
Jouni Malinen1965c852009-04-22 21:38:25 +0300305 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
Johannes Berg19957bb2009-07-02 17:20:43 +0200306 int i;
307 bool done = false;
308
Johannes Berg667503dd2009-07-07 03:56:11 +0200309 wdev_lock(wdev);
Johannes Bergcb0b4be2009-07-07 03:56:07 +0200310
311 nl80211_send_assoc_timeout(rdev, dev, addr, GFP_KERNEL);
Johannes Berg6829c872009-07-02 09:13:27 +0200312 if (wdev->sme_state == CFG80211_SME_CONNECTING)
Johannes Berg667503dd2009-07-07 03:56:11 +0200313 __cfg80211_connect_result(dev, addr, NULL, 0, NULL, 0,
314 WLAN_STATUS_UNSPECIFIED_FAILURE,
Johannes Bergdf7fc0f2009-07-29 11:23:49 +0200315 false, NULL);
Johannes Berg19957bb2009-07-02 17:20:43 +0200316
317 for (i = 0; addr && i < MAX_AUTH_BSSES; i++) {
318 if (wdev->auth_bsses[i] &&
319 memcmp(wdev->auth_bsses[i]->pub.bssid,
320 addr, ETH_ALEN) == 0) {
321 cfg80211_unhold_bss(wdev->auth_bsses[i]);
322 cfg80211_put_bss(&wdev->auth_bsses[i]->pub);
323 wdev->auth_bsses[i] = NULL;
324 done = true;
325 break;
326 }
327 }
328
329 WARN_ON(!done);
Johannes Berg667503dd2009-07-07 03:56:11 +0200330
331 wdev_unlock(wdev);
Jouni Malinen1965c852009-04-22 21:38:25 +0300332}
333EXPORT_SYMBOL(cfg80211_send_assoc_timeout);
334
Jouni Malinena3b8b052009-03-27 21:59:49 +0200335void cfg80211_michael_mic_failure(struct net_device *dev, const u8 *addr,
336 enum nl80211_key_type key_type, int key_id,
Johannes Berge6d6e342009-07-01 21:26:47 +0200337 const u8 *tsc, gfp_t gfp)
Jouni Malinena3b8b052009-03-27 21:59:49 +0200338{
339 struct wiphy *wiphy = dev->ieee80211_ptr->wiphy;
340 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
Johannes Berg3d23e342009-09-29 23:27:28 +0200341#ifdef CONFIG_CFG80211_WEXT
Johannes Bergf58d4ed2009-06-19 02:45:21 +0200342 union iwreq_data wrqu;
Johannes Berge6d6e342009-07-01 21:26:47 +0200343 char *buf = kmalloc(128, gfp);
Johannes Bergf58d4ed2009-06-19 02:45:21 +0200344
345 if (buf) {
346 sprintf(buf, "MLME-MICHAELMICFAILURE.indication("
347 "keyid=%d %scast addr=%pM)", key_id,
348 key_type == NL80211_KEYTYPE_GROUP ? "broad" : "uni",
349 addr);
350 memset(&wrqu, 0, sizeof(wrqu));
351 wrqu.data.length = strlen(buf);
352 wireless_send_event(dev, IWEVCUSTOM, &wrqu, buf);
353 kfree(buf);
354 }
355#endif
356
Johannes Berge6d6e342009-07-01 21:26:47 +0200357 nl80211_michael_mic_failure(rdev, dev, addr, key_type, key_id, tsc, gfp);
Jouni Malinena3b8b052009-03-27 21:59:49 +0200358}
359EXPORT_SYMBOL(cfg80211_michael_mic_failure);
Johannes Berg19957bb2009-07-02 17:20:43 +0200360
361/* some MLME handling for userspace SME */
Johannes Berg667503dd2009-07-07 03:56:11 +0200362int __cfg80211_mlme_auth(struct cfg80211_registered_device *rdev,
363 struct net_device *dev,
364 struct ieee80211_channel *chan,
365 enum nl80211_auth_type auth_type,
366 const u8 *bssid,
367 const u8 *ssid, int ssid_len,
Johannes Bergfffd0932009-07-08 14:22:54 +0200368 const u8 *ie, int ie_len,
369 const u8 *key, int key_len, int key_idx)
Johannes Berg19957bb2009-07-02 17:20:43 +0200370{
371 struct wireless_dev *wdev = dev->ieee80211_ptr;
372 struct cfg80211_auth_request req;
373 struct cfg80211_internal_bss *bss;
374 int i, err, slot = -1, nfree = 0;
375
Johannes Berg667503dd2009-07-07 03:56:11 +0200376 ASSERT_WDEV_LOCK(wdev);
377
Johannes Bergfffd0932009-07-08 14:22:54 +0200378 if (auth_type == NL80211_AUTHTYPE_SHARED_KEY)
379 if (!key || !key_len || key_idx < 0 || key_idx > 4)
380 return -EINVAL;
381
Johannes Berg0a9b5e12009-07-02 18:26:18 +0200382 if (wdev->current_bss &&
383 memcmp(bssid, wdev->current_bss->pub.bssid, ETH_ALEN) == 0)
384 return -EALREADY;
385
386 for (i = 0; i < MAX_AUTH_BSSES; i++) {
387 if (wdev->authtry_bsses[i] &&
388 memcmp(bssid, wdev->authtry_bsses[i]->pub.bssid,
389 ETH_ALEN) == 0)
390 return -EALREADY;
391 if (wdev->auth_bsses[i] &&
392 memcmp(bssid, wdev->auth_bsses[i]->pub.bssid,
393 ETH_ALEN) == 0)
394 return -EALREADY;
395 }
396
Johannes Berg19957bb2009-07-02 17:20:43 +0200397 memset(&req, 0, sizeof(req));
398
399 req.ie = ie;
400 req.ie_len = ie_len;
401 req.auth_type = auth_type;
402 req.bss = cfg80211_get_bss(&rdev->wiphy, chan, bssid, ssid, ssid_len,
403 WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS);
Johannes Bergfffd0932009-07-08 14:22:54 +0200404 req.key = key;
405 req.key_len = key_len;
406 req.key_idx = key_idx;
Johannes Berg19957bb2009-07-02 17:20:43 +0200407 if (!req.bss)
408 return -ENOENT;
409
410 bss = bss_from_pub(req.bss);
411
412 for (i = 0; i < MAX_AUTH_BSSES; i++) {
Johannes Berg19957bb2009-07-02 17:20:43 +0200413 if (!wdev->auth_bsses[i] && !wdev->authtry_bsses[i]) {
414 slot = i;
415 nfree++;
416 }
417 }
418
419 /* we need one free slot for disassoc and one for this auth */
420 if (nfree < 2) {
421 err = -ENOSPC;
422 goto out;
423 }
424
425 wdev->authtry_bsses[slot] = bss;
426 cfg80211_hold_bss(bss);
427
428 err = rdev->ops->auth(&rdev->wiphy, dev, &req);
429 if (err) {
430 wdev->authtry_bsses[slot] = NULL;
431 cfg80211_unhold_bss(bss);
432 }
433
434 out:
435 if (err)
436 cfg80211_put_bss(req.bss);
437 return err;
438}
439
Johannes Berg667503dd2009-07-07 03:56:11 +0200440int cfg80211_mlme_auth(struct cfg80211_registered_device *rdev,
441 struct net_device *dev, struct ieee80211_channel *chan,
442 enum nl80211_auth_type auth_type, const u8 *bssid,
443 const u8 *ssid, int ssid_len,
Johannes Bergfffd0932009-07-08 14:22:54 +0200444 const u8 *ie, int ie_len,
445 const u8 *key, int key_len, int key_idx)
Johannes Berg667503dd2009-07-07 03:56:11 +0200446{
447 int err;
448
449 wdev_lock(dev->ieee80211_ptr);
450 err = __cfg80211_mlme_auth(rdev, dev, chan, auth_type, bssid,
Johannes Bergfffd0932009-07-08 14:22:54 +0200451 ssid, ssid_len, ie, ie_len,
452 key, key_len, key_idx);
Johannes Berg667503dd2009-07-07 03:56:11 +0200453 wdev_unlock(dev->ieee80211_ptr);
454
455 return err;
456}
457
458int __cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev,
459 struct net_device *dev,
460 struct ieee80211_channel *chan,
461 const u8 *bssid, const u8 *prev_bssid,
462 const u8 *ssid, int ssid_len,
463 const u8 *ie, int ie_len, bool use_mfp,
464 struct cfg80211_crypto_settings *crypt)
Johannes Berg19957bb2009-07-02 17:20:43 +0200465{
466 struct wireless_dev *wdev = dev->ieee80211_ptr;
467 struct cfg80211_assoc_request req;
468 struct cfg80211_internal_bss *bss;
469 int i, err, slot = -1;
Jouni Malinen24b6b152009-11-17 21:35:38 +0200470 bool was_connected = false;
Johannes Berg19957bb2009-07-02 17:20:43 +0200471
Johannes Berg667503dd2009-07-07 03:56:11 +0200472 ASSERT_WDEV_LOCK(wdev);
473
Johannes Berg19957bb2009-07-02 17:20:43 +0200474 memset(&req, 0, sizeof(req));
475
Jouni Malinen24b6b152009-11-17 21:35:38 +0200476 if (wdev->current_bss && prev_bssid &&
477 memcmp(wdev->current_bss->pub.bssid, prev_bssid, ETH_ALEN) == 0) {
478 /*
479 * Trying to reassociate: Allow this to proceed and let the old
480 * association to be dropped when the new one is completed.
481 */
482 if (wdev->sme_state == CFG80211_SME_CONNECTED) {
483 was_connected = true;
484 wdev->sme_state = CFG80211_SME_CONNECTING;
485 }
486 } else if (wdev->current_bss)
Johannes Berg19957bb2009-07-02 17:20:43 +0200487 return -EALREADY;
488
489 req.ie = ie;
490 req.ie_len = ie_len;
491 memcpy(&req.crypto, crypt, sizeof(req.crypto));
492 req.use_mfp = use_mfp;
Johannes Berg3e5d7642009-07-07 14:37:26 +0200493 req.prev_bssid = prev_bssid;
Johannes Berg19957bb2009-07-02 17:20:43 +0200494 req.bss = cfg80211_get_bss(&rdev->wiphy, chan, bssid, ssid, ssid_len,
495 WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS);
Jouni Malinen24b6b152009-11-17 21:35:38 +0200496 if (!req.bss) {
497 if (was_connected)
498 wdev->sme_state = CFG80211_SME_CONNECTED;
Johannes Berg19957bb2009-07-02 17:20:43 +0200499 return -ENOENT;
Jouni Malinen24b6b152009-11-17 21:35:38 +0200500 }
Johannes Berg19957bb2009-07-02 17:20:43 +0200501
502 bss = bss_from_pub(req.bss);
503
504 for (i = 0; i < MAX_AUTH_BSSES; i++) {
505 if (bss == wdev->auth_bsses[i]) {
506 slot = i;
507 break;
508 }
509 }
510
511 if (slot < 0) {
512 err = -ENOTCONN;
513 goto out;
514 }
515
516 err = rdev->ops->assoc(&rdev->wiphy, dev, &req);
517 out:
Jouni Malinen24b6b152009-11-17 21:35:38 +0200518 if (err && was_connected)
519 wdev->sme_state = CFG80211_SME_CONNECTED;
Johannes Berg19957bb2009-07-02 17:20:43 +0200520 /* still a reference in wdev->auth_bsses[slot] */
521 cfg80211_put_bss(req.bss);
522 return err;
523}
524
Johannes Berg667503dd2009-07-07 03:56:11 +0200525int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev,
526 struct net_device *dev,
527 struct ieee80211_channel *chan,
528 const u8 *bssid, const u8 *prev_bssid,
529 const u8 *ssid, int ssid_len,
530 const u8 *ie, int ie_len, bool use_mfp,
531 struct cfg80211_crypto_settings *crypt)
532{
533 struct wireless_dev *wdev = dev->ieee80211_ptr;
534 int err;
535
536 wdev_lock(wdev);
537 err = __cfg80211_mlme_assoc(rdev, dev, chan, bssid, prev_bssid,
538 ssid, ssid_len, ie, ie_len, use_mfp, crypt);
539 wdev_unlock(wdev);
540
541 return err;
542}
543
544int __cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev,
545 struct net_device *dev, const u8 *bssid,
546 const u8 *ie, int ie_len, u16 reason)
Johannes Berg19957bb2009-07-02 17:20:43 +0200547{
548 struct wireless_dev *wdev = dev->ieee80211_ptr;
549 struct cfg80211_deauth_request req;
550 int i;
551
Johannes Berg667503dd2009-07-07 03:56:11 +0200552 ASSERT_WDEV_LOCK(wdev);
553
Johannes Berg19957bb2009-07-02 17:20:43 +0200554 memset(&req, 0, sizeof(req));
555 req.reason_code = reason;
556 req.ie = ie;
557 req.ie_len = ie_len;
558 if (wdev->current_bss &&
559 memcmp(wdev->current_bss->pub.bssid, bssid, ETH_ALEN) == 0) {
560 req.bss = &wdev->current_bss->pub;
561 } else for (i = 0; i < MAX_AUTH_BSSES; i++) {
562 if (wdev->auth_bsses[i] &&
563 memcmp(bssid, wdev->auth_bsses[i]->pub.bssid, ETH_ALEN) == 0) {
564 req.bss = &wdev->auth_bsses[i]->pub;
565 break;
566 }
567 if (wdev->authtry_bsses[i] &&
568 memcmp(bssid, wdev->authtry_bsses[i]->pub.bssid, ETH_ALEN) == 0) {
569 req.bss = &wdev->authtry_bsses[i]->pub;
570 break;
571 }
572 }
573
574 if (!req.bss)
575 return -ENOTCONN;
576
Johannes Berg667503dd2009-07-07 03:56:11 +0200577 return rdev->ops->deauth(&rdev->wiphy, dev, &req, wdev);
Johannes Berg19957bb2009-07-02 17:20:43 +0200578}
579
Johannes Berg667503dd2009-07-07 03:56:11 +0200580int cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev,
581 struct net_device *dev, const u8 *bssid,
582 const u8 *ie, int ie_len, u16 reason)
583{
584 struct wireless_dev *wdev = dev->ieee80211_ptr;
585 int err;
586
587 wdev_lock(wdev);
588 err = __cfg80211_mlme_deauth(rdev, dev, bssid, ie, ie_len, reason);
589 wdev_unlock(wdev);
590
591 return err;
592}
593
594static int __cfg80211_mlme_disassoc(struct cfg80211_registered_device *rdev,
595 struct net_device *dev, const u8 *bssid,
596 const u8 *ie, int ie_len, u16 reason)
Johannes Berg19957bb2009-07-02 17:20:43 +0200597{
598 struct wireless_dev *wdev = dev->ieee80211_ptr;
599 struct cfg80211_disassoc_request req;
600
Johannes Berg667503dd2009-07-07 03:56:11 +0200601 ASSERT_WDEV_LOCK(wdev);
602
Johannes Bergf9d6b402009-07-27 10:22:28 +0200603 if (wdev->sme_state != CFG80211_SME_CONNECTED)
604 return -ENOTCONN;
605
606 if (WARN_ON(!wdev->current_bss))
607 return -ENOTCONN;
608
Johannes Berg19957bb2009-07-02 17:20:43 +0200609 memset(&req, 0, sizeof(req));
610 req.reason_code = reason;
611 req.ie = ie;
612 req.ie_len = ie_len;
613 if (memcmp(wdev->current_bss->pub.bssid, bssid, ETH_ALEN) == 0)
614 req.bss = &wdev->current_bss->pub;
615 else
616 return -ENOTCONN;
617
Johannes Berg667503dd2009-07-07 03:56:11 +0200618 return rdev->ops->disassoc(&rdev->wiphy, dev, &req, wdev);
619}
620
621int cfg80211_mlme_disassoc(struct cfg80211_registered_device *rdev,
622 struct net_device *dev, const u8 *bssid,
623 const u8 *ie, int ie_len, u16 reason)
624{
625 struct wireless_dev *wdev = dev->ieee80211_ptr;
626 int err;
627
628 wdev_lock(wdev);
629 err = __cfg80211_mlme_disassoc(rdev, dev, bssid, ie, ie_len, reason);
630 wdev_unlock(wdev);
631
632 return err;
Johannes Berg19957bb2009-07-02 17:20:43 +0200633}
634
635void cfg80211_mlme_down(struct cfg80211_registered_device *rdev,
636 struct net_device *dev)
637{
638 struct wireless_dev *wdev = dev->ieee80211_ptr;
639 struct cfg80211_deauth_request req;
640 int i;
641
Johannes Berg667503dd2009-07-07 03:56:11 +0200642 ASSERT_WDEV_LOCK(wdev);
643
Johannes Berg19957bb2009-07-02 17:20:43 +0200644 if (!rdev->ops->deauth)
645 return;
646
647 memset(&req, 0, sizeof(req));
648 req.reason_code = WLAN_REASON_DEAUTH_LEAVING;
649 req.ie = NULL;
650 req.ie_len = 0;
651
652 if (wdev->current_bss) {
653 req.bss = &wdev->current_bss->pub;
Johannes Berg667503dd2009-07-07 03:56:11 +0200654 rdev->ops->deauth(&rdev->wiphy, dev, &req, wdev);
Johannes Berg19957bb2009-07-02 17:20:43 +0200655 if (wdev->current_bss) {
656 cfg80211_unhold_bss(wdev->current_bss);
657 cfg80211_put_bss(&wdev->current_bss->pub);
658 wdev->current_bss = NULL;
659 }
660 }
661
662 for (i = 0; i < MAX_AUTH_BSSES; i++) {
663 if (wdev->auth_bsses[i]) {
664 req.bss = &wdev->auth_bsses[i]->pub;
Johannes Berg667503dd2009-07-07 03:56:11 +0200665 rdev->ops->deauth(&rdev->wiphy, dev, &req, wdev);
Johannes Berg19957bb2009-07-02 17:20:43 +0200666 if (wdev->auth_bsses[i]) {
667 cfg80211_unhold_bss(wdev->auth_bsses[i]);
668 cfg80211_put_bss(&wdev->auth_bsses[i]->pub);
669 wdev->auth_bsses[i] = NULL;
670 }
671 }
672 if (wdev->authtry_bsses[i]) {
673 req.bss = &wdev->authtry_bsses[i]->pub;
Johannes Berg667503dd2009-07-07 03:56:11 +0200674 rdev->ops->deauth(&rdev->wiphy, dev, &req, wdev);
Johannes Berg19957bb2009-07-02 17:20:43 +0200675 if (wdev->authtry_bsses[i]) {
676 cfg80211_unhold_bss(wdev->authtry_bsses[i]);
677 cfg80211_put_bss(&wdev->authtry_bsses[i]->pub);
678 wdev->authtry_bsses[i] = NULL;
679 }
680 }
681 }
682}
Jouni Malinen9588bbd2009-12-23 13:15:41 +0100683
684void cfg80211_ready_on_channel(struct net_device *dev, u64 cookie,
685 struct ieee80211_channel *chan,
686 enum nl80211_channel_type channel_type,
687 unsigned int duration, gfp_t gfp)
688{
689 struct wiphy *wiphy = dev->ieee80211_ptr->wiphy;
690 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
691
692 nl80211_send_remain_on_channel(rdev, dev, cookie, chan, channel_type,
693 duration, gfp);
694}
695EXPORT_SYMBOL(cfg80211_ready_on_channel);
696
697void cfg80211_remain_on_channel_expired(struct net_device *dev,
698 u64 cookie,
699 struct ieee80211_channel *chan,
700 enum nl80211_channel_type channel_type,
701 gfp_t gfp)
702{
703 struct wiphy *wiphy = dev->ieee80211_ptr->wiphy;
704 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
705
706 nl80211_send_remain_on_channel_cancel(rdev, dev, cookie, chan,
707 channel_type, gfp);
708}
709EXPORT_SYMBOL(cfg80211_remain_on_channel_expired);
Johannes Berg98b62182009-12-23 13:15:44 +0100710
711void cfg80211_new_sta(struct net_device *dev, const u8 *mac_addr,
712 struct station_info *sinfo, gfp_t gfp)
713{
714 struct wiphy *wiphy = dev->ieee80211_ptr->wiphy;
715 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
716
717 nl80211_send_sta_event(rdev, dev, mac_addr, sinfo, gfp);
718}
719EXPORT_SYMBOL(cfg80211_new_sta);