blob: a13a71205240278e01abd7999cabe609941f2cda [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);
97 }
98
Johannes Bergea416a72009-08-17 12:22:14 +020099 if (!wdev->conn && wdev->sme_state == CFG80211_SME_IDLE) {
100 /*
101 * This is for the userspace SME, the CONNECTING
102 * state will be changed to CONNECTED by
103 * __cfg80211_connect_result() below.
104 */
105 wdev->sme_state = CFG80211_SME_CONNECTING;
106 }
107
Johannes Bergdf7fc0f2009-07-29 11:23:49 +0200108 /* this consumes one bss reference (unless bss is NULL) */
109 __cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, ie, len - ieoffs,
110 status_code,
111 status_code == WLAN_STATUS_SUCCESS,
112 bss ? &bss->pub : NULL);
113 /* drop hold now, and also reference acquired above */
114 if (bss) {
115 cfg80211_unhold_bss(bss);
116 cfg80211_put_bss(&bss->pub);
Johannes Berg19957bb2009-07-02 17:20:43 +0200117 }
Johannes Berg667503dd2009-07-07 03:56:11 +0200118
Johannes Bergf401a6f2009-08-07 14:51:05 +0200119 out:
Johannes Berg667503dd2009-07-07 03:56:11 +0200120 wdev_unlock(wdev);
Jouni Malinen6039f6d2009-03-19 13:39:21 +0200121}
122EXPORT_SYMBOL(cfg80211_send_rx_assoc);
123
Holger Schurigce470612009-10-13 13:28:13 +0200124void __cfg80211_send_deauth(struct net_device *dev,
Johannes Berg667503dd2009-07-07 03:56:11 +0200125 const u8 *buf, size_t len)
Jouni Malinen6039f6d2009-03-19 13:39:21 +0200126{
Johannes Berg6829c872009-07-02 09:13:27 +0200127 struct wireless_dev *wdev = dev->ieee80211_ptr;
128 struct wiphy *wiphy = wdev->wiphy;
Jouni Malinen6039f6d2009-03-19 13:39:21 +0200129 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
Johannes Berg6829c872009-07-02 09:13:27 +0200130 struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf;
Johannes Berg19957bb2009-07-02 17:20:43 +0200131 const u8 *bssid = mgmt->bssid;
132 int i;
133 bool done = false;
Johannes Berg6829c872009-07-02 09:13:27 +0200134
Johannes Berg667503dd2009-07-07 03:56:11 +0200135 ASSERT_WDEV_LOCK(wdev);
Johannes Bergcb0b4be2009-07-07 03:56:07 +0200136
137 nl80211_send_deauth(rdev, dev, buf, len, GFP_KERNEL);
Johannes Berg6829c872009-07-02 09:13:27 +0200138
Johannes Berg19957bb2009-07-02 17:20:43 +0200139 if (wdev->current_bss &&
140 memcmp(wdev->current_bss->pub.bssid, bssid, ETH_ALEN) == 0) {
141 done = true;
142 cfg80211_unhold_bss(wdev->current_bss);
143 cfg80211_put_bss(&wdev->current_bss->pub);
144 wdev->current_bss = NULL;
145 } else for (i = 0; i < MAX_AUTH_BSSES; i++) {
146 if (wdev->auth_bsses[i] &&
147 memcmp(wdev->auth_bsses[i]->pub.bssid, bssid, ETH_ALEN) == 0) {
148 cfg80211_unhold_bss(wdev->auth_bsses[i]);
149 cfg80211_put_bss(&wdev->auth_bsses[i]->pub);
150 wdev->auth_bsses[i] = NULL;
151 done = true;
152 break;
153 }
154 if (wdev->authtry_bsses[i] &&
155 memcmp(wdev->authtry_bsses[i]->pub.bssid, bssid, ETH_ALEN) == 0) {
156 cfg80211_unhold_bss(wdev->authtry_bsses[i]);
157 cfg80211_put_bss(&wdev->authtry_bsses[i]->pub);
158 wdev->authtry_bsses[i] = NULL;
159 done = true;
160 break;
161 }
162 }
Johannes Berg19957bb2009-07-02 17:20:43 +0200163
164 WARN_ON(!done);
165
Johannes Berg6829c872009-07-02 09:13:27 +0200166 if (wdev->sme_state == CFG80211_SME_CONNECTED) {
167 u16 reason_code;
168 bool from_ap;
169
170 reason_code = le16_to_cpu(mgmt->u.deauth.reason_code);
171
Johannes Berge458b8a2009-08-06 20:41:33 +0200172 from_ap = memcmp(mgmt->sa, dev->dev_addr, ETH_ALEN) != 0;
Johannes Berg667503dd2009-07-07 03:56:11 +0200173 __cfg80211_disconnected(dev, NULL, 0, reason_code, from_ap);
Johannes Berg6829c872009-07-02 09:13:27 +0200174 } else if (wdev->sme_state == CFG80211_SME_CONNECTING) {
Johannes Berg667503dd2009-07-07 03:56:11 +0200175 __cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, NULL, 0,
176 WLAN_STATUS_UNSPECIFIED_FAILURE,
Johannes Bergdf7fc0f2009-07-29 11:23:49 +0200177 false, NULL);
Johannes Berg667503dd2009-07-07 03:56:11 +0200178 }
179}
Holger Schurigce470612009-10-13 13:28:13 +0200180EXPORT_SYMBOL(__cfg80211_send_deauth);
Johannes Berg667503dd2009-07-07 03:56:11 +0200181
Holger Schurigce470612009-10-13 13:28:13 +0200182void cfg80211_send_deauth(struct net_device *dev, const u8 *buf, size_t len)
Johannes Berg667503dd2009-07-07 03:56:11 +0200183{
184 struct wireless_dev *wdev = dev->ieee80211_ptr;
185
Holger Schurigce470612009-10-13 13:28:13 +0200186 wdev_lock(wdev);
187 __cfg80211_send_deauth(dev, buf, len);
188 wdev_unlock(wdev);
Jouni Malinen6039f6d2009-03-19 13:39:21 +0200189}
Jouni Malinen53b46b82009-03-27 20:53:56 +0200190EXPORT_SYMBOL(cfg80211_send_deauth);
Jouni Malinen6039f6d2009-03-19 13:39:21 +0200191
Holger Schurigce470612009-10-13 13:28:13 +0200192void __cfg80211_send_disassoc(struct net_device *dev,
Johannes Berg667503dd2009-07-07 03:56:11 +0200193 const u8 *buf, size_t len)
Jouni Malinen6039f6d2009-03-19 13:39:21 +0200194{
Johannes Berg6829c872009-07-02 09:13:27 +0200195 struct wireless_dev *wdev = dev->ieee80211_ptr;
196 struct wiphy *wiphy = wdev->wiphy;
Jouni Malinen6039f6d2009-03-19 13:39:21 +0200197 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
Johannes Berg6829c872009-07-02 09:13:27 +0200198 struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf;
Johannes Berg19957bb2009-07-02 17:20:43 +0200199 const u8 *bssid = mgmt->bssid;
200 int i;
201 u16 reason_code;
202 bool from_ap;
203 bool done = false;
Johannes Berg6829c872009-07-02 09:13:27 +0200204
Johannes Berg596a07c2009-07-11 00:17:32 +0200205 ASSERT_WDEV_LOCK(wdev);
Johannes Bergcb0b4be2009-07-07 03:56:07 +0200206
207 nl80211_send_disassoc(rdev, dev, buf, len, GFP_KERNEL);
Johannes Berg6829c872009-07-02 09:13:27 +0200208
Johannes Berg596a07c2009-07-11 00:17:32 +0200209 if (wdev->sme_state != CFG80211_SME_CONNECTED)
210 return;
Johannes Berg6829c872009-07-02 09:13:27 +0200211
Johannes Berg19957bb2009-07-02 17:20:43 +0200212 if (wdev->current_bss &&
Pavel Roskinb935df02009-08-06 04:52:42 -0400213 memcmp(wdev->current_bss->pub.bssid, bssid, ETH_ALEN) == 0) {
Johannes Berg19957bb2009-07-02 17:20:43 +0200214 for (i = 0; i < MAX_AUTH_BSSES; i++) {
215 if (wdev->authtry_bsses[i] || wdev->auth_bsses[i])
216 continue;
217 wdev->auth_bsses[i] = wdev->current_bss;
218 wdev->current_bss = NULL;
219 done = true;
220 cfg80211_sme_disassoc(dev, i);
221 break;
222 }
223 WARN_ON(!done);
224 } else
225 WARN_ON(1);
Johannes Berg6829c872009-07-02 09:13:27 +0200226
Johannes Berg6829c872009-07-02 09:13:27 +0200227
Johannes Berg19957bb2009-07-02 17:20:43 +0200228 reason_code = le16_to_cpu(mgmt->u.disassoc.reason_code);
229
Johannes Berge458b8a2009-08-06 20:41:33 +0200230 from_ap = memcmp(mgmt->sa, dev->dev_addr, ETH_ALEN) != 0;
Johannes Berg667503dd2009-07-07 03:56:11 +0200231 __cfg80211_disconnected(dev, NULL, 0, reason_code, from_ap);
Johannes Berg667503dd2009-07-07 03:56:11 +0200232}
Holger Schurigce470612009-10-13 13:28:13 +0200233EXPORT_SYMBOL(__cfg80211_send_disassoc);
Johannes Berg667503dd2009-07-07 03:56:11 +0200234
Holger Schurigce470612009-10-13 13:28:13 +0200235void cfg80211_send_disassoc(struct net_device *dev, const u8 *buf, size_t len)
Johannes Berg667503dd2009-07-07 03:56:11 +0200236{
237 struct wireless_dev *wdev = dev->ieee80211_ptr;
238
Holger Schurigce470612009-10-13 13:28:13 +0200239 wdev_lock(wdev);
240 __cfg80211_send_disassoc(dev, buf, len);
241 wdev_unlock(wdev);
Jouni Malinen6039f6d2009-03-19 13:39:21 +0200242}
Jouni Malinen53b46b82009-03-27 20:53:56 +0200243EXPORT_SYMBOL(cfg80211_send_disassoc);
Jouni Malinena3b8b052009-03-27 21:59:49 +0200244
Johannes Bergcb0b4be2009-07-07 03:56:07 +0200245void cfg80211_send_auth_timeout(struct net_device *dev, const u8 *addr)
Jouni Malinen1965c852009-04-22 21:38:25 +0300246{
Johannes Berg6829c872009-07-02 09:13:27 +0200247 struct wireless_dev *wdev = dev->ieee80211_ptr;
248 struct wiphy *wiphy = wdev->wiphy;
Jouni Malinen1965c852009-04-22 21:38:25 +0300249 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
Johannes Berg19957bb2009-07-02 17:20:43 +0200250 int i;
251 bool done = false;
252
Johannes Berg667503dd2009-07-07 03:56:11 +0200253 wdev_lock(wdev);
Johannes Bergcb0b4be2009-07-07 03:56:07 +0200254
255 nl80211_send_auth_timeout(rdev, dev, addr, GFP_KERNEL);
Johannes Berg6829c872009-07-02 09:13:27 +0200256 if (wdev->sme_state == CFG80211_SME_CONNECTING)
Johannes Berg667503dd2009-07-07 03:56:11 +0200257 __cfg80211_connect_result(dev, addr, NULL, 0, NULL, 0,
258 WLAN_STATUS_UNSPECIFIED_FAILURE,
Johannes Bergdf7fc0f2009-07-29 11:23:49 +0200259 false, NULL);
Johannes Berg19957bb2009-07-02 17:20:43 +0200260
261 for (i = 0; addr && i < MAX_AUTH_BSSES; i++) {
262 if (wdev->authtry_bsses[i] &&
263 memcmp(wdev->authtry_bsses[i]->pub.bssid,
264 addr, ETH_ALEN) == 0) {
265 cfg80211_unhold_bss(wdev->authtry_bsses[i]);
266 cfg80211_put_bss(&wdev->authtry_bsses[i]->pub);
267 wdev->authtry_bsses[i] = NULL;
268 done = true;
269 break;
270 }
271 }
272
273 WARN_ON(!done);
Johannes Berg667503dd2009-07-07 03:56:11 +0200274
275 wdev_unlock(wdev);
Jouni Malinen1965c852009-04-22 21:38:25 +0300276}
277EXPORT_SYMBOL(cfg80211_send_auth_timeout);
278
Johannes Bergcb0b4be2009-07-07 03:56:07 +0200279void cfg80211_send_assoc_timeout(struct net_device *dev, const u8 *addr)
Jouni Malinen1965c852009-04-22 21:38:25 +0300280{
Johannes Berg6829c872009-07-02 09:13:27 +0200281 struct wireless_dev *wdev = dev->ieee80211_ptr;
282 struct wiphy *wiphy = wdev->wiphy;
Jouni Malinen1965c852009-04-22 21:38:25 +0300283 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
Johannes Berg19957bb2009-07-02 17:20:43 +0200284 int i;
285 bool done = false;
286
Johannes Berg667503dd2009-07-07 03:56:11 +0200287 wdev_lock(wdev);
Johannes Bergcb0b4be2009-07-07 03:56:07 +0200288
289 nl80211_send_assoc_timeout(rdev, dev, addr, GFP_KERNEL);
Johannes Berg6829c872009-07-02 09:13:27 +0200290 if (wdev->sme_state == CFG80211_SME_CONNECTING)
Johannes Berg667503dd2009-07-07 03:56:11 +0200291 __cfg80211_connect_result(dev, addr, NULL, 0, NULL, 0,
292 WLAN_STATUS_UNSPECIFIED_FAILURE,
Johannes Bergdf7fc0f2009-07-29 11:23:49 +0200293 false, NULL);
Johannes Berg19957bb2009-07-02 17:20:43 +0200294
295 for (i = 0; addr && i < MAX_AUTH_BSSES; i++) {
296 if (wdev->auth_bsses[i] &&
297 memcmp(wdev->auth_bsses[i]->pub.bssid,
298 addr, ETH_ALEN) == 0) {
299 cfg80211_unhold_bss(wdev->auth_bsses[i]);
300 cfg80211_put_bss(&wdev->auth_bsses[i]->pub);
301 wdev->auth_bsses[i] = NULL;
302 done = true;
303 break;
304 }
305 }
306
307 WARN_ON(!done);
Johannes Berg667503dd2009-07-07 03:56:11 +0200308
309 wdev_unlock(wdev);
Jouni Malinen1965c852009-04-22 21:38:25 +0300310}
311EXPORT_SYMBOL(cfg80211_send_assoc_timeout);
312
Jouni Malinena3b8b052009-03-27 21:59:49 +0200313void cfg80211_michael_mic_failure(struct net_device *dev, const u8 *addr,
314 enum nl80211_key_type key_type, int key_id,
Johannes Berge6d6e342009-07-01 21:26:47 +0200315 const u8 *tsc, gfp_t gfp)
Jouni Malinena3b8b052009-03-27 21:59:49 +0200316{
317 struct wiphy *wiphy = dev->ieee80211_ptr->wiphy;
318 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
Johannes Berg3d23e342009-09-29 23:27:28 +0200319#ifdef CONFIG_CFG80211_WEXT
Johannes Bergf58d4ed2009-06-19 02:45:21 +0200320 union iwreq_data wrqu;
Johannes Berge6d6e342009-07-01 21:26:47 +0200321 char *buf = kmalloc(128, gfp);
Johannes Bergf58d4ed2009-06-19 02:45:21 +0200322
323 if (buf) {
324 sprintf(buf, "MLME-MICHAELMICFAILURE.indication("
325 "keyid=%d %scast addr=%pM)", key_id,
326 key_type == NL80211_KEYTYPE_GROUP ? "broad" : "uni",
327 addr);
328 memset(&wrqu, 0, sizeof(wrqu));
329 wrqu.data.length = strlen(buf);
330 wireless_send_event(dev, IWEVCUSTOM, &wrqu, buf);
331 kfree(buf);
332 }
333#endif
334
Johannes Berge6d6e342009-07-01 21:26:47 +0200335 nl80211_michael_mic_failure(rdev, dev, addr, key_type, key_id, tsc, gfp);
Jouni Malinena3b8b052009-03-27 21:59:49 +0200336}
337EXPORT_SYMBOL(cfg80211_michael_mic_failure);
Johannes Berg19957bb2009-07-02 17:20:43 +0200338
339/* some MLME handling for userspace SME */
Johannes Berg667503dd2009-07-07 03:56:11 +0200340int __cfg80211_mlme_auth(struct cfg80211_registered_device *rdev,
341 struct net_device *dev,
342 struct ieee80211_channel *chan,
343 enum nl80211_auth_type auth_type,
344 const u8 *bssid,
345 const u8 *ssid, int ssid_len,
Johannes Bergfffd0932009-07-08 14:22:54 +0200346 const u8 *ie, int ie_len,
347 const u8 *key, int key_len, int key_idx)
Johannes Berg19957bb2009-07-02 17:20:43 +0200348{
349 struct wireless_dev *wdev = dev->ieee80211_ptr;
350 struct cfg80211_auth_request req;
351 struct cfg80211_internal_bss *bss;
352 int i, err, slot = -1, nfree = 0;
353
Johannes Berg667503dd2009-07-07 03:56:11 +0200354 ASSERT_WDEV_LOCK(wdev);
355
Johannes Bergfffd0932009-07-08 14:22:54 +0200356 if (auth_type == NL80211_AUTHTYPE_SHARED_KEY)
357 if (!key || !key_len || key_idx < 0 || key_idx > 4)
358 return -EINVAL;
359
Johannes Berg0a9b5e12009-07-02 18:26:18 +0200360 if (wdev->current_bss &&
361 memcmp(bssid, wdev->current_bss->pub.bssid, ETH_ALEN) == 0)
362 return -EALREADY;
363
364 for (i = 0; i < MAX_AUTH_BSSES; i++) {
365 if (wdev->authtry_bsses[i] &&
366 memcmp(bssid, wdev->authtry_bsses[i]->pub.bssid,
367 ETH_ALEN) == 0)
368 return -EALREADY;
369 if (wdev->auth_bsses[i] &&
370 memcmp(bssid, wdev->auth_bsses[i]->pub.bssid,
371 ETH_ALEN) == 0)
372 return -EALREADY;
373 }
374
Johannes Berg19957bb2009-07-02 17:20:43 +0200375 memset(&req, 0, sizeof(req));
376
377 req.ie = ie;
378 req.ie_len = ie_len;
379 req.auth_type = auth_type;
380 req.bss = cfg80211_get_bss(&rdev->wiphy, chan, bssid, ssid, ssid_len,
381 WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS);
Johannes Bergfffd0932009-07-08 14:22:54 +0200382 req.key = key;
383 req.key_len = key_len;
384 req.key_idx = key_idx;
Johannes Berg19957bb2009-07-02 17:20:43 +0200385 if (!req.bss)
386 return -ENOENT;
387
388 bss = bss_from_pub(req.bss);
389
390 for (i = 0; i < MAX_AUTH_BSSES; i++) {
Johannes Berg19957bb2009-07-02 17:20:43 +0200391 if (!wdev->auth_bsses[i] && !wdev->authtry_bsses[i]) {
392 slot = i;
393 nfree++;
394 }
395 }
396
397 /* we need one free slot for disassoc and one for this auth */
398 if (nfree < 2) {
399 err = -ENOSPC;
400 goto out;
401 }
402
403 wdev->authtry_bsses[slot] = bss;
404 cfg80211_hold_bss(bss);
405
406 err = rdev->ops->auth(&rdev->wiphy, dev, &req);
407 if (err) {
408 wdev->authtry_bsses[slot] = NULL;
409 cfg80211_unhold_bss(bss);
410 }
411
412 out:
413 if (err)
414 cfg80211_put_bss(req.bss);
415 return err;
416}
417
Johannes Berg667503dd2009-07-07 03:56:11 +0200418int cfg80211_mlme_auth(struct cfg80211_registered_device *rdev,
419 struct net_device *dev, struct ieee80211_channel *chan,
420 enum nl80211_auth_type auth_type, const u8 *bssid,
421 const u8 *ssid, int ssid_len,
Johannes Bergfffd0932009-07-08 14:22:54 +0200422 const u8 *ie, int ie_len,
423 const u8 *key, int key_len, int key_idx)
Johannes Berg667503dd2009-07-07 03:56:11 +0200424{
425 int err;
426
427 wdev_lock(dev->ieee80211_ptr);
428 err = __cfg80211_mlme_auth(rdev, dev, chan, auth_type, bssid,
Johannes Bergfffd0932009-07-08 14:22:54 +0200429 ssid, ssid_len, ie, ie_len,
430 key, key_len, key_idx);
Johannes Berg667503dd2009-07-07 03:56:11 +0200431 wdev_unlock(dev->ieee80211_ptr);
432
433 return err;
434}
435
436int __cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev,
437 struct net_device *dev,
438 struct ieee80211_channel *chan,
439 const u8 *bssid, const u8 *prev_bssid,
440 const u8 *ssid, int ssid_len,
441 const u8 *ie, int ie_len, bool use_mfp,
442 struct cfg80211_crypto_settings *crypt)
Johannes Berg19957bb2009-07-02 17:20:43 +0200443{
444 struct wireless_dev *wdev = dev->ieee80211_ptr;
445 struct cfg80211_assoc_request req;
446 struct cfg80211_internal_bss *bss;
447 int i, err, slot = -1;
448
Johannes Berg667503dd2009-07-07 03:56:11 +0200449 ASSERT_WDEV_LOCK(wdev);
450
Johannes Berg19957bb2009-07-02 17:20:43 +0200451 memset(&req, 0, sizeof(req));
452
453 if (wdev->current_bss)
454 return -EALREADY;
455
456 req.ie = ie;
457 req.ie_len = ie_len;
458 memcpy(&req.crypto, crypt, sizeof(req.crypto));
459 req.use_mfp = use_mfp;
Johannes Berg3e5d7642009-07-07 14:37:26 +0200460 req.prev_bssid = prev_bssid;
Johannes Berg19957bb2009-07-02 17:20:43 +0200461 req.bss = cfg80211_get_bss(&rdev->wiphy, chan, bssid, ssid, ssid_len,
462 WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS);
463 if (!req.bss)
464 return -ENOENT;
465
466 bss = bss_from_pub(req.bss);
467
468 for (i = 0; i < MAX_AUTH_BSSES; i++) {
469 if (bss == wdev->auth_bsses[i]) {
470 slot = i;
471 break;
472 }
473 }
474
475 if (slot < 0) {
476 err = -ENOTCONN;
477 goto out;
478 }
479
480 err = rdev->ops->assoc(&rdev->wiphy, dev, &req);
481 out:
482 /* still a reference in wdev->auth_bsses[slot] */
483 cfg80211_put_bss(req.bss);
484 return err;
485}
486
Johannes Berg667503dd2009-07-07 03:56:11 +0200487int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev,
488 struct net_device *dev,
489 struct ieee80211_channel *chan,
490 const u8 *bssid, const u8 *prev_bssid,
491 const u8 *ssid, int ssid_len,
492 const u8 *ie, int ie_len, bool use_mfp,
493 struct cfg80211_crypto_settings *crypt)
494{
495 struct wireless_dev *wdev = dev->ieee80211_ptr;
496 int err;
497
498 wdev_lock(wdev);
499 err = __cfg80211_mlme_assoc(rdev, dev, chan, bssid, prev_bssid,
500 ssid, ssid_len, ie, ie_len, use_mfp, crypt);
501 wdev_unlock(wdev);
502
503 return err;
504}
505
506int __cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev,
507 struct net_device *dev, const u8 *bssid,
508 const u8 *ie, int ie_len, u16 reason)
Johannes Berg19957bb2009-07-02 17:20:43 +0200509{
510 struct wireless_dev *wdev = dev->ieee80211_ptr;
511 struct cfg80211_deauth_request req;
512 int i;
513
Johannes Berg667503dd2009-07-07 03:56:11 +0200514 ASSERT_WDEV_LOCK(wdev);
515
Johannes Berg19957bb2009-07-02 17:20:43 +0200516 memset(&req, 0, sizeof(req));
517 req.reason_code = reason;
518 req.ie = ie;
519 req.ie_len = ie_len;
520 if (wdev->current_bss &&
521 memcmp(wdev->current_bss->pub.bssid, bssid, ETH_ALEN) == 0) {
522 req.bss = &wdev->current_bss->pub;
523 } else for (i = 0; i < MAX_AUTH_BSSES; i++) {
524 if (wdev->auth_bsses[i] &&
525 memcmp(bssid, wdev->auth_bsses[i]->pub.bssid, ETH_ALEN) == 0) {
526 req.bss = &wdev->auth_bsses[i]->pub;
527 break;
528 }
529 if (wdev->authtry_bsses[i] &&
530 memcmp(bssid, wdev->authtry_bsses[i]->pub.bssid, ETH_ALEN) == 0) {
531 req.bss = &wdev->authtry_bsses[i]->pub;
532 break;
533 }
534 }
535
536 if (!req.bss)
537 return -ENOTCONN;
538
Johannes Berg667503dd2009-07-07 03:56:11 +0200539 return rdev->ops->deauth(&rdev->wiphy, dev, &req, wdev);
Johannes Berg19957bb2009-07-02 17:20:43 +0200540}
541
Johannes Berg667503dd2009-07-07 03:56:11 +0200542int cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev,
543 struct net_device *dev, const u8 *bssid,
544 const u8 *ie, int ie_len, u16 reason)
545{
546 struct wireless_dev *wdev = dev->ieee80211_ptr;
547 int err;
548
549 wdev_lock(wdev);
550 err = __cfg80211_mlme_deauth(rdev, dev, bssid, ie, ie_len, reason);
551 wdev_unlock(wdev);
552
553 return err;
554}
555
556static int __cfg80211_mlme_disassoc(struct cfg80211_registered_device *rdev,
557 struct net_device *dev, const u8 *bssid,
558 const u8 *ie, int ie_len, u16 reason)
Johannes Berg19957bb2009-07-02 17:20:43 +0200559{
560 struct wireless_dev *wdev = dev->ieee80211_ptr;
561 struct cfg80211_disassoc_request req;
562
Johannes Berg667503dd2009-07-07 03:56:11 +0200563 ASSERT_WDEV_LOCK(wdev);
564
Johannes Bergf9d6b402009-07-27 10:22:28 +0200565 if (wdev->sme_state != CFG80211_SME_CONNECTED)
566 return -ENOTCONN;
567
568 if (WARN_ON(!wdev->current_bss))
569 return -ENOTCONN;
570
Johannes Berg19957bb2009-07-02 17:20:43 +0200571 memset(&req, 0, sizeof(req));
572 req.reason_code = reason;
573 req.ie = ie;
574 req.ie_len = ie_len;
575 if (memcmp(wdev->current_bss->pub.bssid, bssid, ETH_ALEN) == 0)
576 req.bss = &wdev->current_bss->pub;
577 else
578 return -ENOTCONN;
579
Johannes Berg667503dd2009-07-07 03:56:11 +0200580 return rdev->ops->disassoc(&rdev->wiphy, dev, &req, wdev);
581}
582
583int cfg80211_mlme_disassoc(struct cfg80211_registered_device *rdev,
584 struct net_device *dev, const u8 *bssid,
585 const u8 *ie, int ie_len, u16 reason)
586{
587 struct wireless_dev *wdev = dev->ieee80211_ptr;
588 int err;
589
590 wdev_lock(wdev);
591 err = __cfg80211_mlme_disassoc(rdev, dev, bssid, ie, ie_len, reason);
592 wdev_unlock(wdev);
593
594 return err;
Johannes Berg19957bb2009-07-02 17:20:43 +0200595}
596
597void cfg80211_mlme_down(struct cfg80211_registered_device *rdev,
598 struct net_device *dev)
599{
600 struct wireless_dev *wdev = dev->ieee80211_ptr;
601 struct cfg80211_deauth_request req;
602 int i;
603
Johannes Berg667503dd2009-07-07 03:56:11 +0200604 ASSERT_WDEV_LOCK(wdev);
605
Johannes Berg19957bb2009-07-02 17:20:43 +0200606 if (!rdev->ops->deauth)
607 return;
608
609 memset(&req, 0, sizeof(req));
610 req.reason_code = WLAN_REASON_DEAUTH_LEAVING;
611 req.ie = NULL;
612 req.ie_len = 0;
613
614 if (wdev->current_bss) {
615 req.bss = &wdev->current_bss->pub;
Johannes Berg667503dd2009-07-07 03:56:11 +0200616 rdev->ops->deauth(&rdev->wiphy, dev, &req, wdev);
Johannes Berg19957bb2009-07-02 17:20:43 +0200617 if (wdev->current_bss) {
618 cfg80211_unhold_bss(wdev->current_bss);
619 cfg80211_put_bss(&wdev->current_bss->pub);
620 wdev->current_bss = NULL;
621 }
622 }
623
624 for (i = 0; i < MAX_AUTH_BSSES; i++) {
625 if (wdev->auth_bsses[i]) {
626 req.bss = &wdev->auth_bsses[i]->pub;
Johannes Berg667503dd2009-07-07 03:56:11 +0200627 rdev->ops->deauth(&rdev->wiphy, dev, &req, wdev);
Johannes Berg19957bb2009-07-02 17:20:43 +0200628 if (wdev->auth_bsses[i]) {
629 cfg80211_unhold_bss(wdev->auth_bsses[i]);
630 cfg80211_put_bss(&wdev->auth_bsses[i]->pub);
631 wdev->auth_bsses[i] = NULL;
632 }
633 }
634 if (wdev->authtry_bsses[i]) {
635 req.bss = &wdev->authtry_bsses[i]->pub;
Johannes Berg667503dd2009-07-07 03:56:11 +0200636 rdev->ops->deauth(&rdev->wiphy, dev, &req, wdev);
Johannes Berg19957bb2009-07-02 17:20:43 +0200637 if (wdev->authtry_bsses[i]) {
638 cfg80211_unhold_bss(wdev->authtry_bsses[i]);
639 cfg80211_put_bss(&wdev->authtry_bsses[i]->pub);
640 wdev->authtry_bsses[i] = NULL;
641 }
642 }
643 }
644}