blob: 1001db4912f723bada113e61d9211c1ce9a35998 [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 Berg667503d2009-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 Berg667503d2009-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 Berg667503d2009-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 Berg667503d2009-07-07 03:56:11 +0200125
Johannes Bergf401a6f2009-08-07 14:51:05 +0200126 out:
Johannes Berg667503d2009-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 Schurigce470613c2009-10-13 13:28:13 +0200131void __cfg80211_send_deauth(struct net_device *dev,
Johannes Berg667503d2009-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 Berg6829c872009-07-02 09:13:27 +0200140
Johannes Berg667503d2009-07-07 03:56:11 +0200141 ASSERT_WDEV_LOCK(wdev);
Johannes Bergcb0b4be2009-07-07 03:56:07 +0200142
143 nl80211_send_deauth(rdev, dev, buf, len, GFP_KERNEL);
Johannes Berg6829c872009-07-02 09:13:27 +0200144
Johannes Berg19957bb2009-07-02 17:20:43 +0200145 if (wdev->current_bss &&
146 memcmp(wdev->current_bss->pub.bssid, bssid, ETH_ALEN) == 0) {
Johannes Berg19957bb2009-07-02 17:20:43 +0200147 cfg80211_unhold_bss(wdev->current_bss);
148 cfg80211_put_bss(&wdev->current_bss->pub);
149 wdev->current_bss = NULL;
150 } 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 Berg19957bb2009-07-02 17:20:43 +0200156 break;
157 }
158 if (wdev->authtry_bsses[i] &&
159 memcmp(wdev->authtry_bsses[i]->pub.bssid, bssid, ETH_ALEN) == 0) {
160 cfg80211_unhold_bss(wdev->authtry_bsses[i]);
161 cfg80211_put_bss(&wdev->authtry_bsses[i]->pub);
162 wdev->authtry_bsses[i] = NULL;
Johannes Berg19957bb2009-07-02 17:20:43 +0200163 break;
164 }
165 }
Johannes Berg19957bb2009-07-02 17:20:43 +0200166
Johannes Berg6829c872009-07-02 09:13:27 +0200167 if (wdev->sme_state == CFG80211_SME_CONNECTED) {
168 u16 reason_code;
169 bool from_ap;
170
171 reason_code = le16_to_cpu(mgmt->u.deauth.reason_code);
172
Johannes Berge458b8a2009-08-06 20:41:33 +0200173 from_ap = memcmp(mgmt->sa, dev->dev_addr, ETH_ALEN) != 0;
Johannes Berg667503d2009-07-07 03:56:11 +0200174 __cfg80211_disconnected(dev, NULL, 0, reason_code, from_ap);
Johannes Berg6829c872009-07-02 09:13:27 +0200175 } else if (wdev->sme_state == CFG80211_SME_CONNECTING) {
Johannes Berg667503d2009-07-07 03:56:11 +0200176 __cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, NULL, 0,
177 WLAN_STATUS_UNSPECIFIED_FAILURE,
Johannes Bergdf7fc0f2009-07-29 11:23:49 +0200178 false, NULL);
Johannes Berg667503d2009-07-07 03:56:11 +0200179 }
180}
Holger Schurigce470613c2009-10-13 13:28:13 +0200181EXPORT_SYMBOL(__cfg80211_send_deauth);
Johannes Berg667503d2009-07-07 03:56:11 +0200182
Holger Schurigce470613c2009-10-13 13:28:13 +0200183void cfg80211_send_deauth(struct net_device *dev, const u8 *buf, size_t len)
Johannes Berg667503d2009-07-07 03:56:11 +0200184{
185 struct wireless_dev *wdev = dev->ieee80211_ptr;
186
Holger Schurigce470613c2009-10-13 13:28:13 +0200187 wdev_lock(wdev);
188 __cfg80211_send_deauth(dev, buf, len);
189 wdev_unlock(wdev);
Jouni Malinen6039f6d2009-03-19 13:39:21 +0200190}
Jouni Malinen53b46b82009-03-27 20:53:56 +0200191EXPORT_SYMBOL(cfg80211_send_deauth);
Jouni Malinen6039f6d2009-03-19 13:39:21 +0200192
Holger Schurigce470613c2009-10-13 13:28:13 +0200193void __cfg80211_send_disassoc(struct net_device *dev,
Johannes Berg667503d2009-07-07 03:56:11 +0200194 const u8 *buf, size_t len)
Jouni Malinen6039f6d2009-03-19 13:39:21 +0200195{
Johannes Berg6829c872009-07-02 09:13:27 +0200196 struct wireless_dev *wdev = dev->ieee80211_ptr;
197 struct wiphy *wiphy = wdev->wiphy;
Jouni Malinen6039f6d2009-03-19 13:39:21 +0200198 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
Johannes Berg6829c872009-07-02 09:13:27 +0200199 struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf;
Johannes Berg19957bb2009-07-02 17:20:43 +0200200 const u8 *bssid = mgmt->bssid;
201 int i;
202 u16 reason_code;
203 bool from_ap;
204 bool done = false;
Johannes Berg6829c872009-07-02 09:13:27 +0200205
Johannes Berg596a07c2009-07-11 00:17:32 +0200206 ASSERT_WDEV_LOCK(wdev);
Johannes Bergcb0b4be2009-07-07 03:56:07 +0200207
208 nl80211_send_disassoc(rdev, dev, buf, len, GFP_KERNEL);
Johannes Berg6829c872009-07-02 09:13:27 +0200209
Johannes Berg596a07c2009-07-11 00:17:32 +0200210 if (wdev->sme_state != CFG80211_SME_CONNECTED)
211 return;
Johannes Berg6829c872009-07-02 09:13:27 +0200212
Johannes Berg19957bb2009-07-02 17:20:43 +0200213 if (wdev->current_bss &&
Pavel Roskinb935df02009-08-06 04:52:42 -0400214 memcmp(wdev->current_bss->pub.bssid, bssid, ETH_ALEN) == 0) {
Johannes Berg19957bb2009-07-02 17:20:43 +0200215 for (i = 0; i < MAX_AUTH_BSSES; i++) {
216 if (wdev->authtry_bsses[i] || wdev->auth_bsses[i])
217 continue;
218 wdev->auth_bsses[i] = wdev->current_bss;
219 wdev->current_bss = NULL;
220 done = true;
221 cfg80211_sme_disassoc(dev, i);
222 break;
223 }
224 WARN_ON(!done);
225 } else
226 WARN_ON(1);
Johannes Berg6829c872009-07-02 09:13:27 +0200227
Johannes Berg6829c872009-07-02 09:13:27 +0200228
Johannes Berg19957bb2009-07-02 17:20:43 +0200229 reason_code = le16_to_cpu(mgmt->u.disassoc.reason_code);
230
Johannes Berge458b8a2009-08-06 20:41:33 +0200231 from_ap = memcmp(mgmt->sa, dev->dev_addr, ETH_ALEN) != 0;
Johannes Berg667503d2009-07-07 03:56:11 +0200232 __cfg80211_disconnected(dev, NULL, 0, reason_code, from_ap);
Johannes Berg667503d2009-07-07 03:56:11 +0200233}
Holger Schurigce470613c2009-10-13 13:28:13 +0200234EXPORT_SYMBOL(__cfg80211_send_disassoc);
Johannes Berg667503d2009-07-07 03:56:11 +0200235
Holger Schurigce470613c2009-10-13 13:28:13 +0200236void cfg80211_send_disassoc(struct net_device *dev, const u8 *buf, size_t len)
Johannes Berg667503d2009-07-07 03:56:11 +0200237{
238 struct wireless_dev *wdev = dev->ieee80211_ptr;
239
Holger Schurigce470613c2009-10-13 13:28:13 +0200240 wdev_lock(wdev);
241 __cfg80211_send_disassoc(dev, buf, len);
242 wdev_unlock(wdev);
Jouni Malinen6039f6d2009-03-19 13:39:21 +0200243}
Jouni Malinen53b46b82009-03-27 20:53:56 +0200244EXPORT_SYMBOL(cfg80211_send_disassoc);
Jouni Malinena3b8b052009-03-27 21:59:49 +0200245
Johannes Berga58ce432009-11-19 12:45:42 +0100246static void __cfg80211_auth_remove(struct wireless_dev *wdev, const u8 *addr)
Jouni Malinen1965c852009-04-22 21:38:25 +0300247{
Johannes Berg19957bb2009-07-02 17:20:43 +0200248 int i;
249 bool done = false;
250
Johannes Berga58ce432009-11-19 12:45:42 +0100251 ASSERT_WDEV_LOCK(wdev);
Johannes Berg19957bb2009-07-02 17:20:43 +0200252
253 for (i = 0; addr && i < MAX_AUTH_BSSES; i++) {
254 if (wdev->authtry_bsses[i] &&
255 memcmp(wdev->authtry_bsses[i]->pub.bssid,
256 addr, ETH_ALEN) == 0) {
257 cfg80211_unhold_bss(wdev->authtry_bsses[i]);
258 cfg80211_put_bss(&wdev->authtry_bsses[i]->pub);
259 wdev->authtry_bsses[i] = NULL;
260 done = true;
261 break;
262 }
263 }
264
265 WARN_ON(!done);
Johannes Berga58ce432009-11-19 12:45:42 +0100266}
267
268void __cfg80211_auth_canceled(struct net_device *dev, const u8 *addr)
269{
270 __cfg80211_auth_remove(dev->ieee80211_ptr, addr);
271}
272EXPORT_SYMBOL(__cfg80211_auth_canceled);
273
274void cfg80211_send_auth_timeout(struct net_device *dev, const u8 *addr)
275{
276 struct wireless_dev *wdev = dev->ieee80211_ptr;
277 struct wiphy *wiphy = wdev->wiphy;
278 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
279
280 wdev_lock(wdev);
281
282 nl80211_send_auth_timeout(rdev, dev, addr, GFP_KERNEL);
283 if (wdev->sme_state == CFG80211_SME_CONNECTING)
284 __cfg80211_connect_result(dev, addr, NULL, 0, NULL, 0,
285 WLAN_STATUS_UNSPECIFIED_FAILURE,
286 false, NULL);
287
288 __cfg80211_auth_remove(wdev, addr);
Johannes Berg667503d2009-07-07 03:56:11 +0200289
290 wdev_unlock(wdev);
Jouni Malinen1965c852009-04-22 21:38:25 +0300291}
292EXPORT_SYMBOL(cfg80211_send_auth_timeout);
293
Johannes Bergcb0b4be2009-07-07 03:56:07 +0200294void cfg80211_send_assoc_timeout(struct net_device *dev, const u8 *addr)
Jouni Malinen1965c852009-04-22 21:38:25 +0300295{
Johannes Berg6829c872009-07-02 09:13:27 +0200296 struct wireless_dev *wdev = dev->ieee80211_ptr;
297 struct wiphy *wiphy = wdev->wiphy;
Jouni Malinen1965c852009-04-22 21:38:25 +0300298 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
Johannes Berg19957bb2009-07-02 17:20:43 +0200299 int i;
300 bool done = false;
301
Johannes Berg667503d2009-07-07 03:56:11 +0200302 wdev_lock(wdev);
Johannes Bergcb0b4be2009-07-07 03:56:07 +0200303
304 nl80211_send_assoc_timeout(rdev, dev, addr, GFP_KERNEL);
Johannes Berg6829c872009-07-02 09:13:27 +0200305 if (wdev->sme_state == CFG80211_SME_CONNECTING)
Johannes Berg667503d2009-07-07 03:56:11 +0200306 __cfg80211_connect_result(dev, addr, NULL, 0, NULL, 0,
307 WLAN_STATUS_UNSPECIFIED_FAILURE,
Johannes Bergdf7fc0f2009-07-29 11:23:49 +0200308 false, NULL);
Johannes Berg19957bb2009-07-02 17:20:43 +0200309
310 for (i = 0; addr && i < MAX_AUTH_BSSES; i++) {
311 if (wdev->auth_bsses[i] &&
312 memcmp(wdev->auth_bsses[i]->pub.bssid,
313 addr, ETH_ALEN) == 0) {
314 cfg80211_unhold_bss(wdev->auth_bsses[i]);
315 cfg80211_put_bss(&wdev->auth_bsses[i]->pub);
316 wdev->auth_bsses[i] = NULL;
317 done = true;
318 break;
319 }
320 }
321
322 WARN_ON(!done);
Johannes Berg667503d2009-07-07 03:56:11 +0200323
324 wdev_unlock(wdev);
Jouni Malinen1965c852009-04-22 21:38:25 +0300325}
326EXPORT_SYMBOL(cfg80211_send_assoc_timeout);
327
Jouni Malinena3b8b052009-03-27 21:59:49 +0200328void cfg80211_michael_mic_failure(struct net_device *dev, const u8 *addr,
329 enum nl80211_key_type key_type, int key_id,
Johannes Berge6d6e342009-07-01 21:26:47 +0200330 const u8 *tsc, gfp_t gfp)
Jouni Malinena3b8b052009-03-27 21:59:49 +0200331{
332 struct wiphy *wiphy = dev->ieee80211_ptr->wiphy;
333 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
Johannes Berg3d23e342009-09-29 23:27:28 +0200334#ifdef CONFIG_CFG80211_WEXT
Johannes Bergf58d4ed2009-06-19 02:45:21 +0200335 union iwreq_data wrqu;
Johannes Berge6d6e342009-07-01 21:26:47 +0200336 char *buf = kmalloc(128, gfp);
Johannes Bergf58d4ed2009-06-19 02:45:21 +0200337
338 if (buf) {
339 sprintf(buf, "MLME-MICHAELMICFAILURE.indication("
340 "keyid=%d %scast addr=%pM)", key_id,
341 key_type == NL80211_KEYTYPE_GROUP ? "broad" : "uni",
342 addr);
343 memset(&wrqu, 0, sizeof(wrqu));
344 wrqu.data.length = strlen(buf);
345 wireless_send_event(dev, IWEVCUSTOM, &wrqu, buf);
346 kfree(buf);
347 }
348#endif
349
Johannes Berge6d6e342009-07-01 21:26:47 +0200350 nl80211_michael_mic_failure(rdev, dev, addr, key_type, key_id, tsc, gfp);
Jouni Malinena3b8b052009-03-27 21:59:49 +0200351}
352EXPORT_SYMBOL(cfg80211_michael_mic_failure);
Johannes Berg19957bb2009-07-02 17:20:43 +0200353
354/* some MLME handling for userspace SME */
Johannes Berg667503d2009-07-07 03:56:11 +0200355int __cfg80211_mlme_auth(struct cfg80211_registered_device *rdev,
356 struct net_device *dev,
357 struct ieee80211_channel *chan,
358 enum nl80211_auth_type auth_type,
359 const u8 *bssid,
360 const u8 *ssid, int ssid_len,
Johannes Bergfffd0932009-07-08 14:22:54 +0200361 const u8 *ie, int ie_len,
362 const u8 *key, int key_len, int key_idx)
Johannes Berg19957bb2009-07-02 17:20:43 +0200363{
364 struct wireless_dev *wdev = dev->ieee80211_ptr;
365 struct cfg80211_auth_request req;
366 struct cfg80211_internal_bss *bss;
367 int i, err, slot = -1, nfree = 0;
368
Johannes Berg667503d2009-07-07 03:56:11 +0200369 ASSERT_WDEV_LOCK(wdev);
370
Johannes Bergfffd0932009-07-08 14:22:54 +0200371 if (auth_type == NL80211_AUTHTYPE_SHARED_KEY)
372 if (!key || !key_len || key_idx < 0 || key_idx > 4)
373 return -EINVAL;
374
Johannes Berg0a9b5e12009-07-02 18:26:18 +0200375 if (wdev->current_bss &&
376 memcmp(bssid, wdev->current_bss->pub.bssid, ETH_ALEN) == 0)
377 return -EALREADY;
378
379 for (i = 0; i < MAX_AUTH_BSSES; i++) {
380 if (wdev->authtry_bsses[i] &&
381 memcmp(bssid, wdev->authtry_bsses[i]->pub.bssid,
382 ETH_ALEN) == 0)
383 return -EALREADY;
384 if (wdev->auth_bsses[i] &&
385 memcmp(bssid, wdev->auth_bsses[i]->pub.bssid,
386 ETH_ALEN) == 0)
387 return -EALREADY;
388 }
389
Johannes Berg19957bb2009-07-02 17:20:43 +0200390 memset(&req, 0, sizeof(req));
391
392 req.ie = ie;
393 req.ie_len = ie_len;
394 req.auth_type = auth_type;
395 req.bss = cfg80211_get_bss(&rdev->wiphy, chan, bssid, ssid, ssid_len,
396 WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS);
Johannes Bergfffd0932009-07-08 14:22:54 +0200397 req.key = key;
398 req.key_len = key_len;
399 req.key_idx = key_idx;
Johannes Berg19957bb2009-07-02 17:20:43 +0200400 if (!req.bss)
401 return -ENOENT;
402
403 bss = bss_from_pub(req.bss);
404
405 for (i = 0; i < MAX_AUTH_BSSES; i++) {
Johannes Berg19957bb2009-07-02 17:20:43 +0200406 if (!wdev->auth_bsses[i] && !wdev->authtry_bsses[i]) {
407 slot = i;
408 nfree++;
409 }
410 }
411
412 /* we need one free slot for disassoc and one for this auth */
413 if (nfree < 2) {
414 err = -ENOSPC;
415 goto out;
416 }
417
418 wdev->authtry_bsses[slot] = bss;
419 cfg80211_hold_bss(bss);
420
421 err = rdev->ops->auth(&rdev->wiphy, dev, &req);
422 if (err) {
423 wdev->authtry_bsses[slot] = NULL;
424 cfg80211_unhold_bss(bss);
425 }
426
427 out:
428 if (err)
429 cfg80211_put_bss(req.bss);
430 return err;
431}
432
Johannes Berg667503d2009-07-07 03:56:11 +0200433int cfg80211_mlme_auth(struct cfg80211_registered_device *rdev,
434 struct net_device *dev, struct ieee80211_channel *chan,
435 enum nl80211_auth_type auth_type, const u8 *bssid,
436 const u8 *ssid, int ssid_len,
Johannes Bergfffd0932009-07-08 14:22:54 +0200437 const u8 *ie, int ie_len,
438 const u8 *key, int key_len, int key_idx)
Johannes Berg667503d2009-07-07 03:56:11 +0200439{
440 int err;
441
442 wdev_lock(dev->ieee80211_ptr);
443 err = __cfg80211_mlme_auth(rdev, dev, chan, auth_type, bssid,
Johannes Bergfffd0932009-07-08 14:22:54 +0200444 ssid, ssid_len, ie, ie_len,
445 key, key_len, key_idx);
Johannes Berg667503d2009-07-07 03:56:11 +0200446 wdev_unlock(dev->ieee80211_ptr);
447
448 return err;
449}
450
451int __cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev,
452 struct net_device *dev,
453 struct ieee80211_channel *chan,
454 const u8 *bssid, const u8 *prev_bssid,
455 const u8 *ssid, int ssid_len,
456 const u8 *ie, int ie_len, bool use_mfp,
457 struct cfg80211_crypto_settings *crypt)
Johannes Berg19957bb2009-07-02 17:20:43 +0200458{
459 struct wireless_dev *wdev = dev->ieee80211_ptr;
460 struct cfg80211_assoc_request req;
461 struct cfg80211_internal_bss *bss;
462 int i, err, slot = -1;
Jouni Malinen24b6b152009-11-17 21:35:38 +0200463 bool was_connected = false;
Johannes Berg19957bb2009-07-02 17:20:43 +0200464
Johannes Berg667503d2009-07-07 03:56:11 +0200465 ASSERT_WDEV_LOCK(wdev);
466
Johannes Berg19957bb2009-07-02 17:20:43 +0200467 memset(&req, 0, sizeof(req));
468
Jouni Malinen24b6b152009-11-17 21:35:38 +0200469 if (wdev->current_bss && prev_bssid &&
470 memcmp(wdev->current_bss->pub.bssid, prev_bssid, ETH_ALEN) == 0) {
471 /*
472 * Trying to reassociate: Allow this to proceed and let the old
473 * association to be dropped when the new one is completed.
474 */
475 if (wdev->sme_state == CFG80211_SME_CONNECTED) {
476 was_connected = true;
477 wdev->sme_state = CFG80211_SME_CONNECTING;
478 }
479 } else if (wdev->current_bss)
Johannes Berg19957bb2009-07-02 17:20:43 +0200480 return -EALREADY;
481
482 req.ie = ie;
483 req.ie_len = ie_len;
484 memcpy(&req.crypto, crypt, sizeof(req.crypto));
485 req.use_mfp = use_mfp;
Johannes Berg3e5d7642009-07-07 14:37:26 +0200486 req.prev_bssid = prev_bssid;
Johannes Berg19957bb2009-07-02 17:20:43 +0200487 req.bss = cfg80211_get_bss(&rdev->wiphy, chan, bssid, ssid, ssid_len,
488 WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS);
Jouni Malinen24b6b152009-11-17 21:35:38 +0200489 if (!req.bss) {
490 if (was_connected)
491 wdev->sme_state = CFG80211_SME_CONNECTED;
Johannes Berg19957bb2009-07-02 17:20:43 +0200492 return -ENOENT;
Jouni Malinen24b6b152009-11-17 21:35:38 +0200493 }
Johannes Berg19957bb2009-07-02 17:20:43 +0200494
495 bss = bss_from_pub(req.bss);
496
497 for (i = 0; i < MAX_AUTH_BSSES; i++) {
498 if (bss == wdev->auth_bsses[i]) {
499 slot = i;
500 break;
501 }
502 }
503
504 if (slot < 0) {
505 err = -ENOTCONN;
506 goto out;
507 }
508
509 err = rdev->ops->assoc(&rdev->wiphy, dev, &req);
510 out:
Jouni Malinen24b6b152009-11-17 21:35:38 +0200511 if (err && was_connected)
512 wdev->sme_state = CFG80211_SME_CONNECTED;
Johannes Berg19957bb2009-07-02 17:20:43 +0200513 /* still a reference in wdev->auth_bsses[slot] */
514 cfg80211_put_bss(req.bss);
515 return err;
516}
517
Johannes Berg667503d2009-07-07 03:56:11 +0200518int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev,
519 struct net_device *dev,
520 struct ieee80211_channel *chan,
521 const u8 *bssid, const u8 *prev_bssid,
522 const u8 *ssid, int ssid_len,
523 const u8 *ie, int ie_len, bool use_mfp,
524 struct cfg80211_crypto_settings *crypt)
525{
526 struct wireless_dev *wdev = dev->ieee80211_ptr;
527 int err;
528
529 wdev_lock(wdev);
530 err = __cfg80211_mlme_assoc(rdev, dev, chan, bssid, prev_bssid,
531 ssid, ssid_len, ie, ie_len, use_mfp, crypt);
532 wdev_unlock(wdev);
533
534 return err;
535}
536
537int __cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev,
538 struct net_device *dev, const u8 *bssid,
539 const u8 *ie, int ie_len, u16 reason)
Johannes Berg19957bb2009-07-02 17:20:43 +0200540{
541 struct wireless_dev *wdev = dev->ieee80211_ptr;
542 struct cfg80211_deauth_request req;
543 int i;
544
Johannes Berg667503d2009-07-07 03:56:11 +0200545 ASSERT_WDEV_LOCK(wdev);
546
Johannes Berg19957bb2009-07-02 17:20:43 +0200547 memset(&req, 0, sizeof(req));
548 req.reason_code = reason;
549 req.ie = ie;
550 req.ie_len = ie_len;
551 if (wdev->current_bss &&
552 memcmp(wdev->current_bss->pub.bssid, bssid, ETH_ALEN) == 0) {
553 req.bss = &wdev->current_bss->pub;
554 } else for (i = 0; i < MAX_AUTH_BSSES; i++) {
555 if (wdev->auth_bsses[i] &&
556 memcmp(bssid, wdev->auth_bsses[i]->pub.bssid, ETH_ALEN) == 0) {
557 req.bss = &wdev->auth_bsses[i]->pub;
558 break;
559 }
560 if (wdev->authtry_bsses[i] &&
561 memcmp(bssid, wdev->authtry_bsses[i]->pub.bssid, ETH_ALEN) == 0) {
562 req.bss = &wdev->authtry_bsses[i]->pub;
563 break;
564 }
565 }
566
567 if (!req.bss)
568 return -ENOTCONN;
569
Johannes Berg667503d2009-07-07 03:56:11 +0200570 return rdev->ops->deauth(&rdev->wiphy, dev, &req, wdev);
Johannes Berg19957bb2009-07-02 17:20:43 +0200571}
572
Johannes Berg667503d2009-07-07 03:56:11 +0200573int cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev,
574 struct net_device *dev, const u8 *bssid,
575 const u8 *ie, int ie_len, u16 reason)
576{
577 struct wireless_dev *wdev = dev->ieee80211_ptr;
578 int err;
579
580 wdev_lock(wdev);
581 err = __cfg80211_mlme_deauth(rdev, dev, bssid, ie, ie_len, reason);
582 wdev_unlock(wdev);
583
584 return err;
585}
586
587static int __cfg80211_mlme_disassoc(struct cfg80211_registered_device *rdev,
588 struct net_device *dev, const u8 *bssid,
589 const u8 *ie, int ie_len, u16 reason)
Johannes Berg19957bb2009-07-02 17:20:43 +0200590{
591 struct wireless_dev *wdev = dev->ieee80211_ptr;
592 struct cfg80211_disassoc_request req;
593
Johannes Berg667503d2009-07-07 03:56:11 +0200594 ASSERT_WDEV_LOCK(wdev);
595
Johannes Bergf9d6b402009-07-27 10:22:28 +0200596 if (wdev->sme_state != CFG80211_SME_CONNECTED)
597 return -ENOTCONN;
598
599 if (WARN_ON(!wdev->current_bss))
600 return -ENOTCONN;
601
Johannes Berg19957bb2009-07-02 17:20:43 +0200602 memset(&req, 0, sizeof(req));
603 req.reason_code = reason;
604 req.ie = ie;
605 req.ie_len = ie_len;
606 if (memcmp(wdev->current_bss->pub.bssid, bssid, ETH_ALEN) == 0)
607 req.bss = &wdev->current_bss->pub;
608 else
609 return -ENOTCONN;
610
Johannes Berg667503d2009-07-07 03:56:11 +0200611 return rdev->ops->disassoc(&rdev->wiphy, dev, &req, wdev);
612}
613
614int cfg80211_mlme_disassoc(struct cfg80211_registered_device *rdev,
615 struct net_device *dev, const u8 *bssid,
616 const u8 *ie, int ie_len, u16 reason)
617{
618 struct wireless_dev *wdev = dev->ieee80211_ptr;
619 int err;
620
621 wdev_lock(wdev);
622 err = __cfg80211_mlme_disassoc(rdev, dev, bssid, ie, ie_len, reason);
623 wdev_unlock(wdev);
624
625 return err;
Johannes Berg19957bb2009-07-02 17:20:43 +0200626}
627
628void cfg80211_mlme_down(struct cfg80211_registered_device *rdev,
629 struct net_device *dev)
630{
631 struct wireless_dev *wdev = dev->ieee80211_ptr;
632 struct cfg80211_deauth_request req;
633 int i;
634
Johannes Berg667503d2009-07-07 03:56:11 +0200635 ASSERT_WDEV_LOCK(wdev);
636
Johannes Berg19957bb2009-07-02 17:20:43 +0200637 if (!rdev->ops->deauth)
638 return;
639
640 memset(&req, 0, sizeof(req));
641 req.reason_code = WLAN_REASON_DEAUTH_LEAVING;
642 req.ie = NULL;
643 req.ie_len = 0;
644
645 if (wdev->current_bss) {
646 req.bss = &wdev->current_bss->pub;
Johannes Berg667503d2009-07-07 03:56:11 +0200647 rdev->ops->deauth(&rdev->wiphy, dev, &req, wdev);
Johannes Berg19957bb2009-07-02 17:20:43 +0200648 if (wdev->current_bss) {
649 cfg80211_unhold_bss(wdev->current_bss);
650 cfg80211_put_bss(&wdev->current_bss->pub);
651 wdev->current_bss = NULL;
652 }
653 }
654
655 for (i = 0; i < MAX_AUTH_BSSES; i++) {
656 if (wdev->auth_bsses[i]) {
657 req.bss = &wdev->auth_bsses[i]->pub;
Johannes Berg667503d2009-07-07 03:56:11 +0200658 rdev->ops->deauth(&rdev->wiphy, dev, &req, wdev);
Johannes Berg19957bb2009-07-02 17:20:43 +0200659 if (wdev->auth_bsses[i]) {
660 cfg80211_unhold_bss(wdev->auth_bsses[i]);
661 cfg80211_put_bss(&wdev->auth_bsses[i]->pub);
662 wdev->auth_bsses[i] = NULL;
663 }
664 }
665 if (wdev->authtry_bsses[i]) {
666 req.bss = &wdev->authtry_bsses[i]->pub;
Johannes Berg667503d2009-07-07 03:56:11 +0200667 rdev->ops->deauth(&rdev->wiphy, dev, &req, wdev);
Johannes Berg19957bb2009-07-02 17:20:43 +0200668 if (wdev->authtry_bsses[i]) {
669 cfg80211_unhold_bss(wdev->authtry_bsses[i]);
670 cfg80211_put_bss(&wdev->authtry_bsses[i]->pub);
671 wdev->authtry_bsses[i] = NULL;
672 }
673 }
674 }
675}