blob: 51d5df67c632825b27d127efceb541c984a69585 [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 Bergcb0b4be2009-07-07 03:56:07 +020070 nl80211_send_rx_assoc(rdev, dev, buf, len, GFP_KERNEL);
Johannes Berg6829c872009-07-02 09:13:27 +020071
Johannes Berg19957bb2009-07-02 17:20:43 +020072 if (status_code == WLAN_STATUS_SUCCESS) {
Johannes Bergdf7fc0f2009-07-29 11:23:49 +020073 for (i = 0; i < MAX_AUTH_BSSES; i++) {
74 if (!wdev->auth_bsses[i])
75 continue;
76 if (memcmp(wdev->auth_bsses[i]->pub.bssid, mgmt->bssid,
77 ETH_ALEN) == 0) {
78 bss = wdev->auth_bsses[i];
Johannes Berg19957bb2009-07-02 17:20:43 +020079 wdev->auth_bsses[i] = NULL;
Johannes Bergdf7fc0f2009-07-29 11:23:49 +020080 /* additional reference to drop hold */
81 cfg80211_ref_bss(bss);
Johannes Berg19957bb2009-07-02 17:20:43 +020082 break;
83 }
84 }
85
Johannes Bergdf7fc0f2009-07-29 11:23:49 +020086 WARN_ON(!bss);
87 }
88
89 /* this consumes one bss reference (unless bss is NULL) */
90 __cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, ie, len - ieoffs,
91 status_code,
92 status_code == WLAN_STATUS_SUCCESS,
93 bss ? &bss->pub : NULL);
94 /* drop hold now, and also reference acquired above */
95 if (bss) {
96 cfg80211_unhold_bss(bss);
97 cfg80211_put_bss(&bss->pub);
Johannes Berg19957bb2009-07-02 17:20:43 +020098 }
Johannes Berg667503dd2009-07-07 03:56:11 +020099
100 wdev_unlock(wdev);
Jouni Malinen6039f6d2009-03-19 13:39:21 +0200101}
102EXPORT_SYMBOL(cfg80211_send_rx_assoc);
103
Johannes Berg667503dd2009-07-07 03:56:11 +0200104static void __cfg80211_send_deauth(struct net_device *dev,
105 const u8 *buf, size_t len)
Jouni Malinen6039f6d2009-03-19 13:39:21 +0200106{
Johannes Berg6829c872009-07-02 09:13:27 +0200107 struct wireless_dev *wdev = dev->ieee80211_ptr;
108 struct wiphy *wiphy = wdev->wiphy;
Jouni Malinen6039f6d2009-03-19 13:39:21 +0200109 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
Johannes Berg6829c872009-07-02 09:13:27 +0200110 struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf;
Johannes Berg19957bb2009-07-02 17:20:43 +0200111 const u8 *bssid = mgmt->bssid;
112 int i;
113 bool done = false;
Johannes Berg6829c872009-07-02 09:13:27 +0200114
Johannes Berg667503dd2009-07-07 03:56:11 +0200115 ASSERT_WDEV_LOCK(wdev);
Johannes Bergcb0b4be2009-07-07 03:56:07 +0200116
117 nl80211_send_deauth(rdev, dev, buf, len, GFP_KERNEL);
Johannes Berg6829c872009-07-02 09:13:27 +0200118
Johannes Berg19957bb2009-07-02 17:20:43 +0200119 if (wdev->current_bss &&
120 memcmp(wdev->current_bss->pub.bssid, bssid, ETH_ALEN) == 0) {
121 done = true;
122 cfg80211_unhold_bss(wdev->current_bss);
123 cfg80211_put_bss(&wdev->current_bss->pub);
124 wdev->current_bss = NULL;
125 } else for (i = 0; i < MAX_AUTH_BSSES; i++) {
126 if (wdev->auth_bsses[i] &&
127 memcmp(wdev->auth_bsses[i]->pub.bssid, bssid, ETH_ALEN) == 0) {
128 cfg80211_unhold_bss(wdev->auth_bsses[i]);
129 cfg80211_put_bss(&wdev->auth_bsses[i]->pub);
130 wdev->auth_bsses[i] = NULL;
131 done = true;
132 break;
133 }
134 if (wdev->authtry_bsses[i] &&
135 memcmp(wdev->authtry_bsses[i]->pub.bssid, bssid, ETH_ALEN) == 0) {
136 cfg80211_unhold_bss(wdev->authtry_bsses[i]);
137 cfg80211_put_bss(&wdev->authtry_bsses[i]->pub);
138 wdev->authtry_bsses[i] = NULL;
139 done = true;
140 break;
141 }
142 }
Johannes Berg19957bb2009-07-02 17:20:43 +0200143
144 WARN_ON(!done);
145
Johannes Berg6829c872009-07-02 09:13:27 +0200146 if (wdev->sme_state == CFG80211_SME_CONNECTED) {
147 u16 reason_code;
148 bool from_ap;
149
150 reason_code = le16_to_cpu(mgmt->u.deauth.reason_code);
151
Johannes Berge458b8a2009-08-06 20:41:33 +0200152 from_ap = memcmp(mgmt->sa, dev->dev_addr, ETH_ALEN) != 0;
Johannes Berg667503dd2009-07-07 03:56:11 +0200153 __cfg80211_disconnected(dev, NULL, 0, reason_code, from_ap);
Johannes Berg6829c872009-07-02 09:13:27 +0200154 } else if (wdev->sme_state == CFG80211_SME_CONNECTING) {
Johannes Berg667503dd2009-07-07 03:56:11 +0200155 __cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, NULL, 0,
156 WLAN_STATUS_UNSPECIFIED_FAILURE,
Johannes Bergdf7fc0f2009-07-29 11:23:49 +0200157 false, NULL);
Johannes Berg667503dd2009-07-07 03:56:11 +0200158 }
159}
160
161
162void cfg80211_send_deauth(struct net_device *dev, const u8 *buf, size_t len,
163 void *cookie)
164{
165 struct wireless_dev *wdev = dev->ieee80211_ptr;
166
167 BUG_ON(cookie && wdev != cookie);
168
169 if (cookie) {
170 /* called within callback */
171 __cfg80211_send_deauth(dev, buf, len);
172 } else {
173 wdev_lock(wdev);
174 __cfg80211_send_deauth(dev, buf, len);
175 wdev_unlock(wdev);
Johannes Berg6829c872009-07-02 09:13:27 +0200176 }
Jouni Malinen6039f6d2009-03-19 13:39:21 +0200177}
Jouni Malinen53b46b82009-03-27 20:53:56 +0200178EXPORT_SYMBOL(cfg80211_send_deauth);
Jouni Malinen6039f6d2009-03-19 13:39:21 +0200179
Johannes Berg667503dd2009-07-07 03:56:11 +0200180static void __cfg80211_send_disassoc(struct net_device *dev,
181 const u8 *buf, size_t len)
Jouni Malinen6039f6d2009-03-19 13:39:21 +0200182{
Johannes Berg6829c872009-07-02 09:13:27 +0200183 struct wireless_dev *wdev = dev->ieee80211_ptr;
184 struct wiphy *wiphy = wdev->wiphy;
Jouni Malinen6039f6d2009-03-19 13:39:21 +0200185 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
Johannes Berg6829c872009-07-02 09:13:27 +0200186 struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf;
Johannes Berg19957bb2009-07-02 17:20:43 +0200187 const u8 *bssid = mgmt->bssid;
188 int i;
189 u16 reason_code;
190 bool from_ap;
191 bool done = false;
Johannes Berg6829c872009-07-02 09:13:27 +0200192
Johannes Berg596a07c2009-07-11 00:17:32 +0200193 ASSERT_WDEV_LOCK(wdev);
Johannes Bergcb0b4be2009-07-07 03:56:07 +0200194
195 nl80211_send_disassoc(rdev, dev, buf, len, GFP_KERNEL);
Johannes Berg6829c872009-07-02 09:13:27 +0200196
Johannes Berg596a07c2009-07-11 00:17:32 +0200197 if (wdev->sme_state != CFG80211_SME_CONNECTED)
198 return;
Johannes Berg6829c872009-07-02 09:13:27 +0200199
Johannes Berg19957bb2009-07-02 17:20:43 +0200200 if (wdev->current_bss &&
Pavel Roskinb935df02009-08-06 04:52:42 -0400201 memcmp(wdev->current_bss->pub.bssid, bssid, ETH_ALEN) == 0) {
Johannes Berg19957bb2009-07-02 17:20:43 +0200202 for (i = 0; i < MAX_AUTH_BSSES; i++) {
203 if (wdev->authtry_bsses[i] || wdev->auth_bsses[i])
204 continue;
205 wdev->auth_bsses[i] = wdev->current_bss;
206 wdev->current_bss = NULL;
207 done = true;
208 cfg80211_sme_disassoc(dev, i);
209 break;
210 }
211 WARN_ON(!done);
212 } else
213 WARN_ON(1);
Johannes Berg6829c872009-07-02 09:13:27 +0200214
Johannes Berg6829c872009-07-02 09:13:27 +0200215
Johannes Berg19957bb2009-07-02 17:20:43 +0200216 reason_code = le16_to_cpu(mgmt->u.disassoc.reason_code);
217
Johannes Berge458b8a2009-08-06 20:41:33 +0200218 from_ap = memcmp(mgmt->sa, dev->dev_addr, ETH_ALEN) != 0;
Johannes Berg667503dd2009-07-07 03:56:11 +0200219 __cfg80211_disconnected(dev, NULL, 0, reason_code, from_ap);
Johannes Berg667503dd2009-07-07 03:56:11 +0200220}
221
222void cfg80211_send_disassoc(struct net_device *dev, const u8 *buf, size_t len,
223 void *cookie)
224{
225 struct wireless_dev *wdev = dev->ieee80211_ptr;
226
227 BUG_ON(cookie && wdev != cookie);
228
229 if (cookie) {
230 /* called within callback */
231 __cfg80211_send_disassoc(dev, buf, len);
232 } else {
233 wdev_lock(wdev);
234 __cfg80211_send_disassoc(dev, buf, len);
235 wdev_unlock(wdev);
236 }
Jouni Malinen6039f6d2009-03-19 13:39:21 +0200237}
Jouni Malinen53b46b82009-03-27 20:53:56 +0200238EXPORT_SYMBOL(cfg80211_send_disassoc);
Jouni Malinena3b8b052009-03-27 21:59:49 +0200239
Johannes Bergcb0b4be2009-07-07 03:56:07 +0200240void cfg80211_send_auth_timeout(struct net_device *dev, const u8 *addr)
Jouni Malinen1965c852009-04-22 21:38:25 +0300241{
Johannes Berg6829c872009-07-02 09:13:27 +0200242 struct wireless_dev *wdev = dev->ieee80211_ptr;
243 struct wiphy *wiphy = wdev->wiphy;
Jouni Malinen1965c852009-04-22 21:38:25 +0300244 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
Johannes Berg19957bb2009-07-02 17:20:43 +0200245 int i;
246 bool done = false;
247
Johannes Berg667503dd2009-07-07 03:56:11 +0200248 wdev_lock(wdev);
Johannes Bergcb0b4be2009-07-07 03:56:07 +0200249
250 nl80211_send_auth_timeout(rdev, dev, addr, GFP_KERNEL);
Johannes Berg6829c872009-07-02 09:13:27 +0200251 if (wdev->sme_state == CFG80211_SME_CONNECTING)
Johannes Berg667503dd2009-07-07 03:56:11 +0200252 __cfg80211_connect_result(dev, addr, NULL, 0, NULL, 0,
253 WLAN_STATUS_UNSPECIFIED_FAILURE,
Johannes Bergdf7fc0f2009-07-29 11:23:49 +0200254 false, NULL);
Johannes Berg19957bb2009-07-02 17:20:43 +0200255
256 for (i = 0; addr && i < MAX_AUTH_BSSES; i++) {
257 if (wdev->authtry_bsses[i] &&
258 memcmp(wdev->authtry_bsses[i]->pub.bssid,
259 addr, ETH_ALEN) == 0) {
260 cfg80211_unhold_bss(wdev->authtry_bsses[i]);
261 cfg80211_put_bss(&wdev->authtry_bsses[i]->pub);
262 wdev->authtry_bsses[i] = NULL;
263 done = true;
264 break;
265 }
266 }
267
268 WARN_ON(!done);
Johannes Berg667503dd2009-07-07 03:56:11 +0200269
270 wdev_unlock(wdev);
Jouni Malinen1965c852009-04-22 21:38:25 +0300271}
272EXPORT_SYMBOL(cfg80211_send_auth_timeout);
273
Johannes Bergcb0b4be2009-07-07 03:56:07 +0200274void cfg80211_send_assoc_timeout(struct net_device *dev, const u8 *addr)
Jouni Malinen1965c852009-04-22 21:38:25 +0300275{
Johannes Berg6829c872009-07-02 09:13:27 +0200276 struct wireless_dev *wdev = dev->ieee80211_ptr;
277 struct wiphy *wiphy = wdev->wiphy;
Jouni Malinen1965c852009-04-22 21:38:25 +0300278 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
Johannes Berg19957bb2009-07-02 17:20:43 +0200279 int i;
280 bool done = false;
281
Johannes Berg667503dd2009-07-07 03:56:11 +0200282 wdev_lock(wdev);
Johannes Bergcb0b4be2009-07-07 03:56:07 +0200283
284 nl80211_send_assoc_timeout(rdev, dev, addr, GFP_KERNEL);
Johannes Berg6829c872009-07-02 09:13:27 +0200285 if (wdev->sme_state == CFG80211_SME_CONNECTING)
Johannes Berg667503dd2009-07-07 03:56:11 +0200286 __cfg80211_connect_result(dev, addr, NULL, 0, NULL, 0,
287 WLAN_STATUS_UNSPECIFIED_FAILURE,
Johannes Bergdf7fc0f2009-07-29 11:23:49 +0200288 false, NULL);
Johannes Berg19957bb2009-07-02 17:20:43 +0200289
290 for (i = 0; addr && i < MAX_AUTH_BSSES; i++) {
291 if (wdev->auth_bsses[i] &&
292 memcmp(wdev->auth_bsses[i]->pub.bssid,
293 addr, ETH_ALEN) == 0) {
294 cfg80211_unhold_bss(wdev->auth_bsses[i]);
295 cfg80211_put_bss(&wdev->auth_bsses[i]->pub);
296 wdev->auth_bsses[i] = NULL;
297 done = true;
298 break;
299 }
300 }
301
302 WARN_ON(!done);
Johannes Berg667503dd2009-07-07 03:56:11 +0200303
304 wdev_unlock(wdev);
Jouni Malinen1965c852009-04-22 21:38:25 +0300305}
306EXPORT_SYMBOL(cfg80211_send_assoc_timeout);
307
Jouni Malinena3b8b052009-03-27 21:59:49 +0200308void cfg80211_michael_mic_failure(struct net_device *dev, const u8 *addr,
309 enum nl80211_key_type key_type, int key_id,
Johannes Berge6d6e342009-07-01 21:26:47 +0200310 const u8 *tsc, gfp_t gfp)
Jouni Malinena3b8b052009-03-27 21:59:49 +0200311{
312 struct wiphy *wiphy = dev->ieee80211_ptr->wiphy;
313 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
Johannes Bergf58d4ed2009-06-19 02:45:21 +0200314#ifdef CONFIG_WIRELESS_EXT
315 union iwreq_data wrqu;
Johannes Berge6d6e342009-07-01 21:26:47 +0200316 char *buf = kmalloc(128, gfp);
Johannes Bergf58d4ed2009-06-19 02:45:21 +0200317
318 if (buf) {
319 sprintf(buf, "MLME-MICHAELMICFAILURE.indication("
320 "keyid=%d %scast addr=%pM)", key_id,
321 key_type == NL80211_KEYTYPE_GROUP ? "broad" : "uni",
322 addr);
323 memset(&wrqu, 0, sizeof(wrqu));
324 wrqu.data.length = strlen(buf);
325 wireless_send_event(dev, IWEVCUSTOM, &wrqu, buf);
326 kfree(buf);
327 }
328#endif
329
Johannes Berge6d6e342009-07-01 21:26:47 +0200330 nl80211_michael_mic_failure(rdev, dev, addr, key_type, key_id, tsc, gfp);
Jouni Malinena3b8b052009-03-27 21:59:49 +0200331}
332EXPORT_SYMBOL(cfg80211_michael_mic_failure);
Johannes Berg19957bb2009-07-02 17:20:43 +0200333
334/* some MLME handling for userspace SME */
Johannes Berg667503dd2009-07-07 03:56:11 +0200335int __cfg80211_mlme_auth(struct cfg80211_registered_device *rdev,
336 struct net_device *dev,
337 struct ieee80211_channel *chan,
338 enum nl80211_auth_type auth_type,
339 const u8 *bssid,
340 const u8 *ssid, int ssid_len,
Johannes Bergfffd0932009-07-08 14:22:54 +0200341 const u8 *ie, int ie_len,
342 const u8 *key, int key_len, int key_idx)
Johannes Berg19957bb2009-07-02 17:20:43 +0200343{
344 struct wireless_dev *wdev = dev->ieee80211_ptr;
345 struct cfg80211_auth_request req;
346 struct cfg80211_internal_bss *bss;
347 int i, err, slot = -1, nfree = 0;
348
Johannes Berg667503dd2009-07-07 03:56:11 +0200349 ASSERT_WDEV_LOCK(wdev);
350
Johannes Bergfffd0932009-07-08 14:22:54 +0200351 if (auth_type == NL80211_AUTHTYPE_SHARED_KEY)
352 if (!key || !key_len || key_idx < 0 || key_idx > 4)
353 return -EINVAL;
354
Johannes Berg0a9b5e12009-07-02 18:26:18 +0200355 if (wdev->current_bss &&
356 memcmp(bssid, wdev->current_bss->pub.bssid, ETH_ALEN) == 0)
357 return -EALREADY;
358
359 for (i = 0; i < MAX_AUTH_BSSES; i++) {
360 if (wdev->authtry_bsses[i] &&
361 memcmp(bssid, wdev->authtry_bsses[i]->pub.bssid,
362 ETH_ALEN) == 0)
363 return -EALREADY;
364 if (wdev->auth_bsses[i] &&
365 memcmp(bssid, wdev->auth_bsses[i]->pub.bssid,
366 ETH_ALEN) == 0)
367 return -EALREADY;
368 }
369
Johannes Berg19957bb2009-07-02 17:20:43 +0200370 memset(&req, 0, sizeof(req));
371
372 req.ie = ie;
373 req.ie_len = ie_len;
374 req.auth_type = auth_type;
375 req.bss = cfg80211_get_bss(&rdev->wiphy, chan, bssid, ssid, ssid_len,
376 WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS);
Johannes Bergfffd0932009-07-08 14:22:54 +0200377 req.key = key;
378 req.key_len = key_len;
379 req.key_idx = key_idx;
Johannes Berg19957bb2009-07-02 17:20:43 +0200380 if (!req.bss)
381 return -ENOENT;
382
383 bss = bss_from_pub(req.bss);
384
385 for (i = 0; i < MAX_AUTH_BSSES; i++) {
Johannes Berg19957bb2009-07-02 17:20:43 +0200386 if (!wdev->auth_bsses[i] && !wdev->authtry_bsses[i]) {
387 slot = i;
388 nfree++;
389 }
390 }
391
392 /* we need one free slot for disassoc and one for this auth */
393 if (nfree < 2) {
394 err = -ENOSPC;
395 goto out;
396 }
397
398 wdev->authtry_bsses[slot] = bss;
399 cfg80211_hold_bss(bss);
400
401 err = rdev->ops->auth(&rdev->wiphy, dev, &req);
402 if (err) {
403 wdev->authtry_bsses[slot] = NULL;
404 cfg80211_unhold_bss(bss);
405 }
406
407 out:
408 if (err)
409 cfg80211_put_bss(req.bss);
410 return err;
411}
412
Johannes Berg667503dd2009-07-07 03:56:11 +0200413int cfg80211_mlme_auth(struct cfg80211_registered_device *rdev,
414 struct net_device *dev, struct ieee80211_channel *chan,
415 enum nl80211_auth_type auth_type, const u8 *bssid,
416 const u8 *ssid, int ssid_len,
Johannes Bergfffd0932009-07-08 14:22:54 +0200417 const u8 *ie, int ie_len,
418 const u8 *key, int key_len, int key_idx)
Johannes Berg667503dd2009-07-07 03:56:11 +0200419{
420 int err;
421
422 wdev_lock(dev->ieee80211_ptr);
423 err = __cfg80211_mlme_auth(rdev, dev, chan, auth_type, bssid,
Johannes Bergfffd0932009-07-08 14:22:54 +0200424 ssid, ssid_len, ie, ie_len,
425 key, key_len, key_idx);
Johannes Berg667503dd2009-07-07 03:56:11 +0200426 wdev_unlock(dev->ieee80211_ptr);
427
428 return err;
429}
430
431int __cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev,
432 struct net_device *dev,
433 struct ieee80211_channel *chan,
434 const u8 *bssid, const u8 *prev_bssid,
435 const u8 *ssid, int ssid_len,
436 const u8 *ie, int ie_len, bool use_mfp,
437 struct cfg80211_crypto_settings *crypt)
Johannes Berg19957bb2009-07-02 17:20:43 +0200438{
439 struct wireless_dev *wdev = dev->ieee80211_ptr;
440 struct cfg80211_assoc_request req;
441 struct cfg80211_internal_bss *bss;
442 int i, err, slot = -1;
443
Johannes Berg667503dd2009-07-07 03:56:11 +0200444 ASSERT_WDEV_LOCK(wdev);
445
Johannes Berg19957bb2009-07-02 17:20:43 +0200446 memset(&req, 0, sizeof(req));
447
448 if (wdev->current_bss)
449 return -EALREADY;
450
451 req.ie = ie;
452 req.ie_len = ie_len;
453 memcpy(&req.crypto, crypt, sizeof(req.crypto));
454 req.use_mfp = use_mfp;
Johannes Berg3e5d7642009-07-07 14:37:26 +0200455 req.prev_bssid = prev_bssid;
Johannes Berg19957bb2009-07-02 17:20:43 +0200456 req.bss = cfg80211_get_bss(&rdev->wiphy, chan, bssid, ssid, ssid_len,
457 WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS);
458 if (!req.bss)
459 return -ENOENT;
460
461 bss = bss_from_pub(req.bss);
462
463 for (i = 0; i < MAX_AUTH_BSSES; i++) {
464 if (bss == wdev->auth_bsses[i]) {
465 slot = i;
466 break;
467 }
468 }
469
470 if (slot < 0) {
471 err = -ENOTCONN;
472 goto out;
473 }
474
475 err = rdev->ops->assoc(&rdev->wiphy, dev, &req);
476 out:
477 /* still a reference in wdev->auth_bsses[slot] */
478 cfg80211_put_bss(req.bss);
479 return err;
480}
481
Johannes Berg667503dd2009-07-07 03:56:11 +0200482int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev,
483 struct net_device *dev,
484 struct ieee80211_channel *chan,
485 const u8 *bssid, const u8 *prev_bssid,
486 const u8 *ssid, int ssid_len,
487 const u8 *ie, int ie_len, bool use_mfp,
488 struct cfg80211_crypto_settings *crypt)
489{
490 struct wireless_dev *wdev = dev->ieee80211_ptr;
491 int err;
492
493 wdev_lock(wdev);
494 err = __cfg80211_mlme_assoc(rdev, dev, chan, bssid, prev_bssid,
495 ssid, ssid_len, ie, ie_len, use_mfp, crypt);
496 wdev_unlock(wdev);
497
498 return err;
499}
500
501int __cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev,
502 struct net_device *dev, const u8 *bssid,
503 const u8 *ie, int ie_len, u16 reason)
Johannes Berg19957bb2009-07-02 17:20:43 +0200504{
505 struct wireless_dev *wdev = dev->ieee80211_ptr;
506 struct cfg80211_deauth_request req;
507 int i;
508
Johannes Berg667503dd2009-07-07 03:56:11 +0200509 ASSERT_WDEV_LOCK(wdev);
510
Johannes Berg19957bb2009-07-02 17:20:43 +0200511 memset(&req, 0, sizeof(req));
512 req.reason_code = reason;
513 req.ie = ie;
514 req.ie_len = ie_len;
515 if (wdev->current_bss &&
516 memcmp(wdev->current_bss->pub.bssid, bssid, ETH_ALEN) == 0) {
517 req.bss = &wdev->current_bss->pub;
518 } else for (i = 0; i < MAX_AUTH_BSSES; i++) {
519 if (wdev->auth_bsses[i] &&
520 memcmp(bssid, wdev->auth_bsses[i]->pub.bssid, ETH_ALEN) == 0) {
521 req.bss = &wdev->auth_bsses[i]->pub;
522 break;
523 }
524 if (wdev->authtry_bsses[i] &&
525 memcmp(bssid, wdev->authtry_bsses[i]->pub.bssid, ETH_ALEN) == 0) {
526 req.bss = &wdev->authtry_bsses[i]->pub;
527 break;
528 }
529 }
530
531 if (!req.bss)
532 return -ENOTCONN;
533
Johannes Berg667503dd2009-07-07 03:56:11 +0200534 return rdev->ops->deauth(&rdev->wiphy, dev, &req, wdev);
Johannes Berg19957bb2009-07-02 17:20:43 +0200535}
536
Johannes Berg667503dd2009-07-07 03:56:11 +0200537int 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)
540{
541 struct wireless_dev *wdev = dev->ieee80211_ptr;
542 int err;
543
544 wdev_lock(wdev);
545 err = __cfg80211_mlme_deauth(rdev, dev, bssid, ie, ie_len, reason);
546 wdev_unlock(wdev);
547
548 return err;
549}
550
551static int __cfg80211_mlme_disassoc(struct cfg80211_registered_device *rdev,
552 struct net_device *dev, const u8 *bssid,
553 const u8 *ie, int ie_len, u16 reason)
Johannes Berg19957bb2009-07-02 17:20:43 +0200554{
555 struct wireless_dev *wdev = dev->ieee80211_ptr;
556 struct cfg80211_disassoc_request req;
557
Johannes Berg667503dd2009-07-07 03:56:11 +0200558 ASSERT_WDEV_LOCK(wdev);
559
Johannes Bergf9d6b402009-07-27 10:22:28 +0200560 if (wdev->sme_state != CFG80211_SME_CONNECTED)
561 return -ENOTCONN;
562
563 if (WARN_ON(!wdev->current_bss))
564 return -ENOTCONN;
565
Johannes Berg19957bb2009-07-02 17:20:43 +0200566 memset(&req, 0, sizeof(req));
567 req.reason_code = reason;
568 req.ie = ie;
569 req.ie_len = ie_len;
570 if (memcmp(wdev->current_bss->pub.bssid, bssid, ETH_ALEN) == 0)
571 req.bss = &wdev->current_bss->pub;
572 else
573 return -ENOTCONN;
574
Johannes Berg667503dd2009-07-07 03:56:11 +0200575 return rdev->ops->disassoc(&rdev->wiphy, dev, &req, wdev);
576}
577
578int cfg80211_mlme_disassoc(struct cfg80211_registered_device *rdev,
579 struct net_device *dev, const u8 *bssid,
580 const u8 *ie, int ie_len, u16 reason)
581{
582 struct wireless_dev *wdev = dev->ieee80211_ptr;
583 int err;
584
585 wdev_lock(wdev);
586 err = __cfg80211_mlme_disassoc(rdev, dev, bssid, ie, ie_len, reason);
587 wdev_unlock(wdev);
588
589 return err;
Johannes Berg19957bb2009-07-02 17:20:43 +0200590}
591
592void cfg80211_mlme_down(struct cfg80211_registered_device *rdev,
593 struct net_device *dev)
594{
595 struct wireless_dev *wdev = dev->ieee80211_ptr;
596 struct cfg80211_deauth_request req;
597 int i;
598
Johannes Berg667503dd2009-07-07 03:56:11 +0200599 ASSERT_WDEV_LOCK(wdev);
600
Johannes Berg19957bb2009-07-02 17:20:43 +0200601 if (!rdev->ops->deauth)
602 return;
603
604 memset(&req, 0, sizeof(req));
605 req.reason_code = WLAN_REASON_DEAUTH_LEAVING;
606 req.ie = NULL;
607 req.ie_len = 0;
608
609 if (wdev->current_bss) {
610 req.bss = &wdev->current_bss->pub;
Johannes Berg667503dd2009-07-07 03:56:11 +0200611 rdev->ops->deauth(&rdev->wiphy, dev, &req, wdev);
Johannes Berg19957bb2009-07-02 17:20:43 +0200612 if (wdev->current_bss) {
613 cfg80211_unhold_bss(wdev->current_bss);
614 cfg80211_put_bss(&wdev->current_bss->pub);
615 wdev->current_bss = NULL;
616 }
617 }
618
619 for (i = 0; i < MAX_AUTH_BSSES; i++) {
620 if (wdev->auth_bsses[i]) {
621 req.bss = &wdev->auth_bsses[i]->pub;
Johannes Berg667503dd2009-07-07 03:56:11 +0200622 rdev->ops->deauth(&rdev->wiphy, dev, &req, wdev);
Johannes Berg19957bb2009-07-02 17:20:43 +0200623 if (wdev->auth_bsses[i]) {
624 cfg80211_unhold_bss(wdev->auth_bsses[i]);
625 cfg80211_put_bss(&wdev->auth_bsses[i]->pub);
626 wdev->auth_bsses[i] = NULL;
627 }
628 }
629 if (wdev->authtry_bsses[i]) {
630 req.bss = &wdev->authtry_bsses[i]->pub;
Johannes Berg667503dd2009-07-07 03:56:11 +0200631 rdev->ops->deauth(&rdev->wiphy, dev, &req, wdev);
Johannes Berg19957bb2009-07-02 17:20:43 +0200632 if (wdev->authtry_bsses[i]) {
633 cfg80211_unhold_bss(wdev->authtry_bsses[i]);
634 cfg80211_put_bss(&wdev->authtry_bsses[i]->pub);
635 wdev->authtry_bsses[i] = NULL;
636 }
637 }
638 }
639}