blob: 1b2ca1fea7a1bba75963615f048f550b06e77c64 [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>
11#include <net/cfg80211.h>
12#include "core.h"
13#include "nl80211.h"
14
Johannes Bergcb0b4be2009-07-07 03:56:07 +020015void cfg80211_send_rx_auth(struct net_device *dev, const u8 *buf, size_t len)
Jouni Malinen6039f6d2009-03-19 13:39:21 +020016{
Johannes Berg19957bb2009-07-02 17:20:43 +020017 struct wireless_dev *wdev = dev->ieee80211_ptr;
18 struct wiphy *wiphy = wdev->wiphy;
Jouni Malinen6039f6d2009-03-19 13:39:21 +020019 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
Johannes Berg19957bb2009-07-02 17:20:43 +020020 struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf;
21 u8 *bssid = mgmt->bssid;
22 int i;
23 u16 status = le16_to_cpu(mgmt->u.auth.status_code);
24 bool done = false;
25
Johannes Berg667503dd2009-07-07 03:56:11 +020026 wdev_lock(wdev);
Johannes Bergcb0b4be2009-07-07 03:56:07 +020027
Johannes Berg19957bb2009-07-02 17:20:43 +020028 for (i = 0; i < MAX_AUTH_BSSES; i++) {
29 if (wdev->authtry_bsses[i] &&
30 memcmp(wdev->authtry_bsses[i]->pub.bssid, bssid,
31 ETH_ALEN) == 0) {
32 if (status == WLAN_STATUS_SUCCESS) {
33 wdev->auth_bsses[i] = wdev->authtry_bsses[i];
34 } else {
35 cfg80211_unhold_bss(wdev->authtry_bsses[i]);
36 cfg80211_put_bss(&wdev->authtry_bsses[i]->pub);
37 }
38 wdev->authtry_bsses[i] = NULL;
39 done = true;
40 break;
41 }
42 }
43
44 WARN_ON(!done);
Johannes Berg6829c872009-07-02 09:13:27 +020045
Johannes Bergcb0b4be2009-07-07 03:56:07 +020046 nl80211_send_rx_auth(rdev, dev, buf, len, GFP_KERNEL);
Johannes Berg6829c872009-07-02 09:13:27 +020047 cfg80211_sme_rx_auth(dev, buf, len);
Johannes Berg667503dd2009-07-07 03:56:11 +020048
49 wdev_unlock(wdev);
Jouni Malinen6039f6d2009-03-19 13:39:21 +020050}
51EXPORT_SYMBOL(cfg80211_send_rx_auth);
52
Johannes Bergcb0b4be2009-07-07 03:56:07 +020053void cfg80211_send_rx_assoc(struct net_device *dev, const u8 *buf, size_t len)
Jouni Malinen6039f6d2009-03-19 13:39:21 +020054{
Johannes Berg6829c872009-07-02 09:13:27 +020055 u16 status_code;
56 struct wireless_dev *wdev = dev->ieee80211_ptr;
57 struct wiphy *wiphy = wdev->wiphy;
Jouni Malinen6039f6d2009-03-19 13:39:21 +020058 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
Johannes Berg6829c872009-07-02 09:13:27 +020059 struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf;
60 u8 *ie = mgmt->u.assoc_resp.variable;
Johannes Berg19957bb2009-07-02 17:20:43 +020061 int i, ieoffs = offsetof(struct ieee80211_mgmt, u.assoc_resp.variable);
62 bool done;
Johannes Berg6829c872009-07-02 09:13:27 +020063
Johannes Berg667503dd2009-07-07 03:56:11 +020064 wdev_lock(wdev);
Johannes Bergcb0b4be2009-07-07 03:56:07 +020065
Johannes Berg6829c872009-07-02 09:13:27 +020066 status_code = le16_to_cpu(mgmt->u.assoc_resp.status_code);
67
Johannes Bergcb0b4be2009-07-07 03:56:07 +020068 nl80211_send_rx_assoc(rdev, dev, buf, len, GFP_KERNEL);
Johannes Berg6829c872009-07-02 09:13:27 +020069
Johannes Berg667503dd2009-07-07 03:56:11 +020070 __cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, ie, len - ieoffs,
71 status_code,
72 status_code == WLAN_STATUS_SUCCESS);
Johannes Berg19957bb2009-07-02 17:20:43 +020073
74 if (status_code == WLAN_STATUS_SUCCESS) {
75 for (i = 0; wdev->current_bss && i < MAX_AUTH_BSSES; i++) {
76 if (wdev->auth_bsses[i] == wdev->current_bss) {
77 cfg80211_unhold_bss(wdev->auth_bsses[i]);
78 cfg80211_put_bss(&wdev->auth_bsses[i]->pub);
79 wdev->auth_bsses[i] = NULL;
80 done = true;
81 break;
82 }
83 }
84
85 WARN_ON(!done);
86 }
Johannes Berg667503dd2009-07-07 03:56:11 +020087
88 wdev_unlock(wdev);
Jouni Malinen6039f6d2009-03-19 13:39:21 +020089}
90EXPORT_SYMBOL(cfg80211_send_rx_assoc);
91
Johannes Berg667503dd2009-07-07 03:56:11 +020092static void __cfg80211_send_deauth(struct net_device *dev,
93 const u8 *buf, size_t len)
Jouni Malinen6039f6d2009-03-19 13:39:21 +020094{
Johannes Berg6829c872009-07-02 09:13:27 +020095 struct wireless_dev *wdev = dev->ieee80211_ptr;
96 struct wiphy *wiphy = wdev->wiphy;
Jouni Malinen6039f6d2009-03-19 13:39:21 +020097 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
Johannes Berg6829c872009-07-02 09:13:27 +020098 struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf;
Johannes Berg19957bb2009-07-02 17:20:43 +020099 const u8 *bssid = mgmt->bssid;
100 int i;
101 bool done = false;
Johannes Berg6829c872009-07-02 09:13:27 +0200102
Johannes Berg667503dd2009-07-07 03:56:11 +0200103 ASSERT_WDEV_LOCK(wdev);
Johannes Bergcb0b4be2009-07-07 03:56:07 +0200104
105 nl80211_send_deauth(rdev, dev, buf, len, GFP_KERNEL);
Johannes Berg6829c872009-07-02 09:13:27 +0200106
Johannes Berg19957bb2009-07-02 17:20:43 +0200107 if (wdev->current_bss &&
108 memcmp(wdev->current_bss->pub.bssid, bssid, ETH_ALEN) == 0) {
109 done = true;
110 cfg80211_unhold_bss(wdev->current_bss);
111 cfg80211_put_bss(&wdev->current_bss->pub);
112 wdev->current_bss = NULL;
113 } else for (i = 0; i < MAX_AUTH_BSSES; i++) {
114 if (wdev->auth_bsses[i] &&
115 memcmp(wdev->auth_bsses[i]->pub.bssid, bssid, ETH_ALEN) == 0) {
116 cfg80211_unhold_bss(wdev->auth_bsses[i]);
117 cfg80211_put_bss(&wdev->auth_bsses[i]->pub);
118 wdev->auth_bsses[i] = NULL;
119 done = true;
120 break;
121 }
122 if (wdev->authtry_bsses[i] &&
123 memcmp(wdev->authtry_bsses[i]->pub.bssid, bssid, ETH_ALEN) == 0) {
124 cfg80211_unhold_bss(wdev->authtry_bsses[i]);
125 cfg80211_put_bss(&wdev->authtry_bsses[i]->pub);
126 wdev->authtry_bsses[i] = NULL;
127 done = true;
128 break;
129 }
130 }
Johannes Berg19957bb2009-07-02 17:20:43 +0200131
132 WARN_ON(!done);
133
Johannes Berg6829c872009-07-02 09:13:27 +0200134 if (wdev->sme_state == CFG80211_SME_CONNECTED) {
135 u16 reason_code;
136 bool from_ap;
137
138 reason_code = le16_to_cpu(mgmt->u.deauth.reason_code);
139
140 from_ap = memcmp(mgmt->da, dev->dev_addr, ETH_ALEN) == 0;
Johannes Berg667503dd2009-07-07 03:56:11 +0200141 __cfg80211_disconnected(dev, NULL, 0, reason_code, from_ap);
Johannes Berg6829c872009-07-02 09:13:27 +0200142 } else if (wdev->sme_state == CFG80211_SME_CONNECTING) {
Johannes Berg667503dd2009-07-07 03:56:11 +0200143 __cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, NULL, 0,
144 WLAN_STATUS_UNSPECIFIED_FAILURE,
145 false);
146 }
147}
148
149
150void cfg80211_send_deauth(struct net_device *dev, const u8 *buf, size_t len,
151 void *cookie)
152{
153 struct wireless_dev *wdev = dev->ieee80211_ptr;
154
155 BUG_ON(cookie && wdev != cookie);
156
157 if (cookie) {
158 /* called within callback */
159 __cfg80211_send_deauth(dev, buf, len);
160 } else {
161 wdev_lock(wdev);
162 __cfg80211_send_deauth(dev, buf, len);
163 wdev_unlock(wdev);
Johannes Berg6829c872009-07-02 09:13:27 +0200164 }
Jouni Malinen6039f6d2009-03-19 13:39:21 +0200165}
Jouni Malinen53b46b82009-03-27 20:53:56 +0200166EXPORT_SYMBOL(cfg80211_send_deauth);
Jouni Malinen6039f6d2009-03-19 13:39:21 +0200167
Johannes Berg667503dd2009-07-07 03:56:11 +0200168static void __cfg80211_send_disassoc(struct net_device *dev,
169 const u8 *buf, size_t len)
Jouni Malinen6039f6d2009-03-19 13:39:21 +0200170{
Johannes Berg6829c872009-07-02 09:13:27 +0200171 struct wireless_dev *wdev = dev->ieee80211_ptr;
172 struct wiphy *wiphy = wdev->wiphy;
Jouni Malinen6039f6d2009-03-19 13:39:21 +0200173 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
Johannes Berg6829c872009-07-02 09:13:27 +0200174 struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf;
Johannes Berg19957bb2009-07-02 17:20:43 +0200175 const u8 *bssid = mgmt->bssid;
176 int i;
177 u16 reason_code;
178 bool from_ap;
179 bool done = false;
Johannes Berg6829c872009-07-02 09:13:27 +0200180
Johannes Berg667503dd2009-07-07 03:56:11 +0200181 wdev_lock(wdev);
Johannes Bergcb0b4be2009-07-07 03:56:07 +0200182
183 nl80211_send_disassoc(rdev, dev, buf, len, GFP_KERNEL);
Johannes Berg6829c872009-07-02 09:13:27 +0200184
Johannes Berg19957bb2009-07-02 17:20:43 +0200185 if (!wdev->sme_state == CFG80211_SME_CONNECTED)
Johannes Berg667503dd2009-07-07 03:56:11 +0200186 goto out;
Johannes Berg6829c872009-07-02 09:13:27 +0200187
Johannes Berg19957bb2009-07-02 17:20:43 +0200188 if (wdev->current_bss &&
189 memcmp(wdev->current_bss, bssid, ETH_ALEN) == 0) {
190 for (i = 0; i < MAX_AUTH_BSSES; i++) {
191 if (wdev->authtry_bsses[i] || wdev->auth_bsses[i])
192 continue;
193 wdev->auth_bsses[i] = wdev->current_bss;
194 wdev->current_bss = NULL;
195 done = true;
196 cfg80211_sme_disassoc(dev, i);
197 break;
198 }
199 WARN_ON(!done);
200 } else
201 WARN_ON(1);
Johannes Berg6829c872009-07-02 09:13:27 +0200202
Johannes Berg6829c872009-07-02 09:13:27 +0200203
Johannes Berg19957bb2009-07-02 17:20:43 +0200204 reason_code = le16_to_cpu(mgmt->u.disassoc.reason_code);
205
206 from_ap = memcmp(mgmt->da, dev->dev_addr, ETH_ALEN) == 0;
Johannes Berg667503dd2009-07-07 03:56:11 +0200207 __cfg80211_disconnected(dev, NULL, 0, reason_code, from_ap);
208 out:
209 wdev_unlock(wdev);
210}
211
212void cfg80211_send_disassoc(struct net_device *dev, const u8 *buf, size_t len,
213 void *cookie)
214{
215 struct wireless_dev *wdev = dev->ieee80211_ptr;
216
217 BUG_ON(cookie && wdev != cookie);
218
219 if (cookie) {
220 /* called within callback */
221 __cfg80211_send_disassoc(dev, buf, len);
222 } else {
223 wdev_lock(wdev);
224 __cfg80211_send_disassoc(dev, buf, len);
225 wdev_unlock(wdev);
226 }
Jouni Malinen6039f6d2009-03-19 13:39:21 +0200227}
Jouni Malinen53b46b82009-03-27 20:53:56 +0200228EXPORT_SYMBOL(cfg80211_send_disassoc);
Jouni Malinena3b8b052009-03-27 21:59:49 +0200229
Johannes Bergcb0b4be2009-07-07 03:56:07 +0200230void cfg80211_send_auth_timeout(struct net_device *dev, const u8 *addr)
Jouni Malinen1965c852009-04-22 21:38:25 +0300231{
Johannes Berg6829c872009-07-02 09:13:27 +0200232 struct wireless_dev *wdev = dev->ieee80211_ptr;
233 struct wiphy *wiphy = wdev->wiphy;
Jouni Malinen1965c852009-04-22 21:38:25 +0300234 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
Johannes Berg19957bb2009-07-02 17:20:43 +0200235 int i;
236 bool done = false;
237
Johannes Berg667503dd2009-07-07 03:56:11 +0200238 wdev_lock(wdev);
Johannes Bergcb0b4be2009-07-07 03:56:07 +0200239
240 nl80211_send_auth_timeout(rdev, dev, addr, GFP_KERNEL);
Johannes Berg6829c872009-07-02 09:13:27 +0200241 if (wdev->sme_state == CFG80211_SME_CONNECTING)
Johannes Berg667503dd2009-07-07 03:56:11 +0200242 __cfg80211_connect_result(dev, addr, NULL, 0, NULL, 0,
243 WLAN_STATUS_UNSPECIFIED_FAILURE,
244 false);
Johannes Berg19957bb2009-07-02 17:20:43 +0200245
246 for (i = 0; addr && i < MAX_AUTH_BSSES; i++) {
247 if (wdev->authtry_bsses[i] &&
248 memcmp(wdev->authtry_bsses[i]->pub.bssid,
249 addr, ETH_ALEN) == 0) {
250 cfg80211_unhold_bss(wdev->authtry_bsses[i]);
251 cfg80211_put_bss(&wdev->authtry_bsses[i]->pub);
252 wdev->authtry_bsses[i] = NULL;
253 done = true;
254 break;
255 }
256 }
257
258 WARN_ON(!done);
Johannes Berg667503dd2009-07-07 03:56:11 +0200259
260 wdev_unlock(wdev);
Jouni Malinen1965c852009-04-22 21:38:25 +0300261}
262EXPORT_SYMBOL(cfg80211_send_auth_timeout);
263
Johannes Bergcb0b4be2009-07-07 03:56:07 +0200264void cfg80211_send_assoc_timeout(struct net_device *dev, const u8 *addr)
Jouni Malinen1965c852009-04-22 21:38:25 +0300265{
Johannes Berg6829c872009-07-02 09:13:27 +0200266 struct wireless_dev *wdev = dev->ieee80211_ptr;
267 struct wiphy *wiphy = wdev->wiphy;
Jouni Malinen1965c852009-04-22 21:38:25 +0300268 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
Johannes Berg19957bb2009-07-02 17:20:43 +0200269 int i;
270 bool done = false;
271
Johannes Berg667503dd2009-07-07 03:56:11 +0200272 wdev_lock(wdev);
Johannes Bergcb0b4be2009-07-07 03:56:07 +0200273
274 nl80211_send_assoc_timeout(rdev, dev, addr, GFP_KERNEL);
Johannes Berg6829c872009-07-02 09:13:27 +0200275 if (wdev->sme_state == CFG80211_SME_CONNECTING)
Johannes Berg667503dd2009-07-07 03:56:11 +0200276 __cfg80211_connect_result(dev, addr, NULL, 0, NULL, 0,
277 WLAN_STATUS_UNSPECIFIED_FAILURE,
278 false);
Johannes Berg19957bb2009-07-02 17:20:43 +0200279
280 for (i = 0; addr && i < MAX_AUTH_BSSES; i++) {
281 if (wdev->auth_bsses[i] &&
282 memcmp(wdev->auth_bsses[i]->pub.bssid,
283 addr, ETH_ALEN) == 0) {
284 cfg80211_unhold_bss(wdev->auth_bsses[i]);
285 cfg80211_put_bss(&wdev->auth_bsses[i]->pub);
286 wdev->auth_bsses[i] = NULL;
287 done = true;
288 break;
289 }
290 }
291
292 WARN_ON(!done);
Johannes Berg667503dd2009-07-07 03:56:11 +0200293
294 wdev_unlock(wdev);
Jouni Malinen1965c852009-04-22 21:38:25 +0300295}
296EXPORT_SYMBOL(cfg80211_send_assoc_timeout);
297
Jouni Malinena3b8b052009-03-27 21:59:49 +0200298void cfg80211_michael_mic_failure(struct net_device *dev, const u8 *addr,
299 enum nl80211_key_type key_type, int key_id,
Johannes Berge6d6e342009-07-01 21:26:47 +0200300 const u8 *tsc, gfp_t gfp)
Jouni Malinena3b8b052009-03-27 21:59:49 +0200301{
302 struct wiphy *wiphy = dev->ieee80211_ptr->wiphy;
303 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
Johannes Bergf58d4ed2009-06-19 02:45:21 +0200304#ifdef CONFIG_WIRELESS_EXT
305 union iwreq_data wrqu;
Johannes Berge6d6e342009-07-01 21:26:47 +0200306 char *buf = kmalloc(128, gfp);
Johannes Bergf58d4ed2009-06-19 02:45:21 +0200307
308 if (buf) {
309 sprintf(buf, "MLME-MICHAELMICFAILURE.indication("
310 "keyid=%d %scast addr=%pM)", key_id,
311 key_type == NL80211_KEYTYPE_GROUP ? "broad" : "uni",
312 addr);
313 memset(&wrqu, 0, sizeof(wrqu));
314 wrqu.data.length = strlen(buf);
315 wireless_send_event(dev, IWEVCUSTOM, &wrqu, buf);
316 kfree(buf);
317 }
318#endif
319
Johannes Berge6d6e342009-07-01 21:26:47 +0200320 nl80211_michael_mic_failure(rdev, dev, addr, key_type, key_id, tsc, gfp);
Jouni Malinena3b8b052009-03-27 21:59:49 +0200321}
322EXPORT_SYMBOL(cfg80211_michael_mic_failure);
Johannes Berg19957bb2009-07-02 17:20:43 +0200323
324/* some MLME handling for userspace SME */
Johannes Berg667503dd2009-07-07 03:56:11 +0200325int __cfg80211_mlme_auth(struct cfg80211_registered_device *rdev,
326 struct net_device *dev,
327 struct ieee80211_channel *chan,
328 enum nl80211_auth_type auth_type,
329 const u8 *bssid,
330 const u8 *ssid, int ssid_len,
331 const u8 *ie, int ie_len)
Johannes Berg19957bb2009-07-02 17:20:43 +0200332{
333 struct wireless_dev *wdev = dev->ieee80211_ptr;
334 struct cfg80211_auth_request req;
335 struct cfg80211_internal_bss *bss;
336 int i, err, slot = -1, nfree = 0;
337
Johannes Berg667503dd2009-07-07 03:56:11 +0200338 ASSERT_WDEV_LOCK(wdev);
339
Johannes Berg0a9b5e12009-07-02 18:26:18 +0200340 if (wdev->current_bss &&
341 memcmp(bssid, wdev->current_bss->pub.bssid, ETH_ALEN) == 0)
342 return -EALREADY;
343
344 for (i = 0; i < MAX_AUTH_BSSES; i++) {
345 if (wdev->authtry_bsses[i] &&
346 memcmp(bssid, wdev->authtry_bsses[i]->pub.bssid,
347 ETH_ALEN) == 0)
348 return -EALREADY;
349 if (wdev->auth_bsses[i] &&
350 memcmp(bssid, wdev->auth_bsses[i]->pub.bssid,
351 ETH_ALEN) == 0)
352 return -EALREADY;
353 }
354
Johannes Berg19957bb2009-07-02 17:20:43 +0200355 memset(&req, 0, sizeof(req));
356
357 req.ie = ie;
358 req.ie_len = ie_len;
359 req.auth_type = auth_type;
360 req.bss = cfg80211_get_bss(&rdev->wiphy, chan, bssid, ssid, ssid_len,
361 WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS);
362 if (!req.bss)
363 return -ENOENT;
364
365 bss = bss_from_pub(req.bss);
366
367 for (i = 0; i < MAX_AUTH_BSSES; i++) {
Johannes Berg19957bb2009-07-02 17:20:43 +0200368 if (!wdev->auth_bsses[i] && !wdev->authtry_bsses[i]) {
369 slot = i;
370 nfree++;
371 }
372 }
373
374 /* we need one free slot for disassoc and one for this auth */
375 if (nfree < 2) {
376 err = -ENOSPC;
377 goto out;
378 }
379
380 wdev->authtry_bsses[slot] = bss;
381 cfg80211_hold_bss(bss);
382
383 err = rdev->ops->auth(&rdev->wiphy, dev, &req);
384 if (err) {
385 wdev->authtry_bsses[slot] = NULL;
386 cfg80211_unhold_bss(bss);
387 }
388
389 out:
390 if (err)
391 cfg80211_put_bss(req.bss);
392 return err;
393}
394
Johannes Berg667503dd2009-07-07 03:56:11 +0200395int cfg80211_mlme_auth(struct cfg80211_registered_device *rdev,
396 struct net_device *dev, struct ieee80211_channel *chan,
397 enum nl80211_auth_type auth_type, const u8 *bssid,
398 const u8 *ssid, int ssid_len,
399 const u8 *ie, int ie_len)
400{
401 int err;
402
403 wdev_lock(dev->ieee80211_ptr);
404 err = __cfg80211_mlme_auth(rdev, dev, chan, auth_type, bssid,
405 ssid, ssid_len, ie, ie_len);
406 wdev_unlock(dev->ieee80211_ptr);
407
408 return err;
409}
410
411int __cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev,
412 struct net_device *dev,
413 struct ieee80211_channel *chan,
414 const u8 *bssid, const u8 *prev_bssid,
415 const u8 *ssid, int ssid_len,
416 const u8 *ie, int ie_len, bool use_mfp,
417 struct cfg80211_crypto_settings *crypt)
Johannes Berg19957bb2009-07-02 17:20:43 +0200418{
419 struct wireless_dev *wdev = dev->ieee80211_ptr;
420 struct cfg80211_assoc_request req;
421 struct cfg80211_internal_bss *bss;
422 int i, err, slot = -1;
423
Johannes Berg667503dd2009-07-07 03:56:11 +0200424 ASSERT_WDEV_LOCK(wdev);
425
Johannes Berg19957bb2009-07-02 17:20:43 +0200426 memset(&req, 0, sizeof(req));
427
428 if (wdev->current_bss)
429 return -EALREADY;
430
431 req.ie = ie;
432 req.ie_len = ie_len;
433 memcpy(&req.crypto, crypt, sizeof(req.crypto));
434 req.use_mfp = use_mfp;
Johannes Berg3e5d7642009-07-07 14:37:26 +0200435 req.prev_bssid = prev_bssid;
Johannes Berg19957bb2009-07-02 17:20:43 +0200436 req.bss = cfg80211_get_bss(&rdev->wiphy, chan, bssid, ssid, ssid_len,
437 WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS);
438 if (!req.bss)
439 return -ENOENT;
440
441 bss = bss_from_pub(req.bss);
442
443 for (i = 0; i < MAX_AUTH_BSSES; i++) {
444 if (bss == wdev->auth_bsses[i]) {
445 slot = i;
446 break;
447 }
448 }
449
450 if (slot < 0) {
451 err = -ENOTCONN;
452 goto out;
453 }
454
455 err = rdev->ops->assoc(&rdev->wiphy, dev, &req);
456 out:
457 /* still a reference in wdev->auth_bsses[slot] */
458 cfg80211_put_bss(req.bss);
459 return err;
460}
461
Johannes Berg667503dd2009-07-07 03:56:11 +0200462int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev,
463 struct net_device *dev,
464 struct ieee80211_channel *chan,
465 const u8 *bssid, const u8 *prev_bssid,
466 const u8 *ssid, int ssid_len,
467 const u8 *ie, int ie_len, bool use_mfp,
468 struct cfg80211_crypto_settings *crypt)
469{
470 struct wireless_dev *wdev = dev->ieee80211_ptr;
471 int err;
472
473 wdev_lock(wdev);
474 err = __cfg80211_mlme_assoc(rdev, dev, chan, bssid, prev_bssid,
475 ssid, ssid_len, ie, ie_len, use_mfp, crypt);
476 wdev_unlock(wdev);
477
478 return err;
479}
480
481int __cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev,
482 struct net_device *dev, const u8 *bssid,
483 const u8 *ie, int ie_len, u16 reason)
Johannes Berg19957bb2009-07-02 17:20:43 +0200484{
485 struct wireless_dev *wdev = dev->ieee80211_ptr;
486 struct cfg80211_deauth_request req;
487 int i;
488
Johannes Berg667503dd2009-07-07 03:56:11 +0200489 ASSERT_WDEV_LOCK(wdev);
490
Johannes Berg19957bb2009-07-02 17:20:43 +0200491 memset(&req, 0, sizeof(req));
492 req.reason_code = reason;
493 req.ie = ie;
494 req.ie_len = ie_len;
495 if (wdev->current_bss &&
496 memcmp(wdev->current_bss->pub.bssid, bssid, ETH_ALEN) == 0) {
497 req.bss = &wdev->current_bss->pub;
498 } else for (i = 0; i < MAX_AUTH_BSSES; i++) {
499 if (wdev->auth_bsses[i] &&
500 memcmp(bssid, wdev->auth_bsses[i]->pub.bssid, ETH_ALEN) == 0) {
501 req.bss = &wdev->auth_bsses[i]->pub;
502 break;
503 }
504 if (wdev->authtry_bsses[i] &&
505 memcmp(bssid, wdev->authtry_bsses[i]->pub.bssid, ETH_ALEN) == 0) {
506 req.bss = &wdev->authtry_bsses[i]->pub;
507 break;
508 }
509 }
510
511 if (!req.bss)
512 return -ENOTCONN;
513
Johannes Berg667503dd2009-07-07 03:56:11 +0200514 return rdev->ops->deauth(&rdev->wiphy, dev, &req, wdev);
Johannes Berg19957bb2009-07-02 17:20:43 +0200515}
516
Johannes Berg667503dd2009-07-07 03:56:11 +0200517int cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev,
518 struct net_device *dev, const u8 *bssid,
519 const u8 *ie, int ie_len, u16 reason)
520{
521 struct wireless_dev *wdev = dev->ieee80211_ptr;
522 int err;
523
524 wdev_lock(wdev);
525 err = __cfg80211_mlme_deauth(rdev, dev, bssid, ie, ie_len, reason);
526 wdev_unlock(wdev);
527
528 return err;
529}
530
531static int __cfg80211_mlme_disassoc(struct cfg80211_registered_device *rdev,
532 struct net_device *dev, const u8 *bssid,
533 const u8 *ie, int ie_len, u16 reason)
Johannes Berg19957bb2009-07-02 17:20:43 +0200534{
535 struct wireless_dev *wdev = dev->ieee80211_ptr;
536 struct cfg80211_disassoc_request req;
537
Johannes Berg667503dd2009-07-07 03:56:11 +0200538 ASSERT_WDEV_LOCK(wdev);
539
Johannes Berg19957bb2009-07-02 17:20:43 +0200540 memset(&req, 0, sizeof(req));
541 req.reason_code = reason;
542 req.ie = ie;
543 req.ie_len = ie_len;
544 if (memcmp(wdev->current_bss->pub.bssid, bssid, ETH_ALEN) == 0)
545 req.bss = &wdev->current_bss->pub;
546 else
547 return -ENOTCONN;
548
Johannes Berg667503dd2009-07-07 03:56:11 +0200549 return rdev->ops->disassoc(&rdev->wiphy, dev, &req, wdev);
550}
551
552int cfg80211_mlme_disassoc(struct cfg80211_registered_device *rdev,
553 struct net_device *dev, const u8 *bssid,
554 const u8 *ie, int ie_len, u16 reason)
555{
556 struct wireless_dev *wdev = dev->ieee80211_ptr;
557 int err;
558
559 wdev_lock(wdev);
560 err = __cfg80211_mlme_disassoc(rdev, dev, bssid, ie, ie_len, reason);
561 wdev_unlock(wdev);
562
563 return err;
Johannes Berg19957bb2009-07-02 17:20:43 +0200564}
565
566void cfg80211_mlme_down(struct cfg80211_registered_device *rdev,
567 struct net_device *dev)
568{
569 struct wireless_dev *wdev = dev->ieee80211_ptr;
570 struct cfg80211_deauth_request req;
571 int i;
572
Johannes Berg667503dd2009-07-07 03:56:11 +0200573 ASSERT_WDEV_LOCK(wdev);
574
Johannes Berg19957bb2009-07-02 17:20:43 +0200575 if (!rdev->ops->deauth)
576 return;
577
578 memset(&req, 0, sizeof(req));
579 req.reason_code = WLAN_REASON_DEAUTH_LEAVING;
580 req.ie = NULL;
581 req.ie_len = 0;
582
583 if (wdev->current_bss) {
584 req.bss = &wdev->current_bss->pub;
Johannes Berg667503dd2009-07-07 03:56:11 +0200585 rdev->ops->deauth(&rdev->wiphy, dev, &req, wdev);
Johannes Berg19957bb2009-07-02 17:20:43 +0200586 if (wdev->current_bss) {
587 cfg80211_unhold_bss(wdev->current_bss);
588 cfg80211_put_bss(&wdev->current_bss->pub);
589 wdev->current_bss = NULL;
590 }
591 }
592
593 for (i = 0; i < MAX_AUTH_BSSES; i++) {
594 if (wdev->auth_bsses[i]) {
595 req.bss = &wdev->auth_bsses[i]->pub;
Johannes Berg667503dd2009-07-07 03:56:11 +0200596 rdev->ops->deauth(&rdev->wiphy, dev, &req, wdev);
Johannes Berg19957bb2009-07-02 17:20:43 +0200597 if (wdev->auth_bsses[i]) {
598 cfg80211_unhold_bss(wdev->auth_bsses[i]);
599 cfg80211_put_bss(&wdev->auth_bsses[i]->pub);
600 wdev->auth_bsses[i] = NULL;
601 }
602 }
603 if (wdev->authtry_bsses[i]) {
604 req.bss = &wdev->authtry_bsses[i]->pub;
Johannes Berg667503dd2009-07-07 03:56:11 +0200605 rdev->ops->deauth(&rdev->wiphy, dev, &req, wdev);
Johannes Berg19957bb2009-07-02 17:20:43 +0200606 if (wdev->authtry_bsses[i]) {
607 cfg80211_unhold_bss(wdev->authtry_bsses[i]);
608 cfg80211_put_bss(&wdev->authtry_bsses[i]->pub);
609 wdev->authtry_bsses[i] = NULL;
610 }
611 }
612 }
613}