blob: 097a87d7bae14d222d2d36e76bd32d821de912c4 [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);
64 bool done;
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 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 Berg667503d2009-07-07 03:56:11 +020072 __cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, ie, len - ieoffs,
73 status_code,
74 status_code == WLAN_STATUS_SUCCESS);
Johannes Berg19957bb2009-07-02 17:20:43 +020075
76 if (status_code == WLAN_STATUS_SUCCESS) {
77 for (i = 0; wdev->current_bss && i < MAX_AUTH_BSSES; i++) {
78 if (wdev->auth_bsses[i] == wdev->current_bss) {
79 cfg80211_unhold_bss(wdev->auth_bsses[i]);
80 cfg80211_put_bss(&wdev->auth_bsses[i]->pub);
81 wdev->auth_bsses[i] = NULL;
82 done = true;
83 break;
84 }
85 }
86
87 WARN_ON(!done);
88 }
Johannes Berg667503d2009-07-07 03:56:11 +020089
90 wdev_unlock(wdev);
Jouni Malinen6039f6d2009-03-19 13:39:21 +020091}
92EXPORT_SYMBOL(cfg80211_send_rx_assoc);
93
Johannes Berg667503d2009-07-07 03:56:11 +020094static void __cfg80211_send_deauth(struct net_device *dev,
95 const u8 *buf, size_t len)
Jouni Malinen6039f6d2009-03-19 13:39:21 +020096{
Johannes Berg6829c872009-07-02 09:13:27 +020097 struct wireless_dev *wdev = dev->ieee80211_ptr;
98 struct wiphy *wiphy = wdev->wiphy;
Jouni Malinen6039f6d2009-03-19 13:39:21 +020099 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
Johannes Berg6829c872009-07-02 09:13:27 +0200100 struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf;
Johannes Berg19957bb2009-07-02 17:20:43 +0200101 const u8 *bssid = mgmt->bssid;
102 int i;
103 bool done = false;
Johannes Berg6829c872009-07-02 09:13:27 +0200104
Johannes Berg667503d2009-07-07 03:56:11 +0200105 ASSERT_WDEV_LOCK(wdev);
Johannes Bergcb0b4be2009-07-07 03:56:07 +0200106
107 nl80211_send_deauth(rdev, dev, buf, len, GFP_KERNEL);
Johannes Berg6829c872009-07-02 09:13:27 +0200108
Johannes Berg19957bb2009-07-02 17:20:43 +0200109 if (wdev->current_bss &&
110 memcmp(wdev->current_bss->pub.bssid, bssid, ETH_ALEN) == 0) {
111 done = true;
112 cfg80211_unhold_bss(wdev->current_bss);
113 cfg80211_put_bss(&wdev->current_bss->pub);
114 wdev->current_bss = NULL;
115 } else for (i = 0; i < MAX_AUTH_BSSES; i++) {
116 if (wdev->auth_bsses[i] &&
117 memcmp(wdev->auth_bsses[i]->pub.bssid, bssid, ETH_ALEN) == 0) {
118 cfg80211_unhold_bss(wdev->auth_bsses[i]);
119 cfg80211_put_bss(&wdev->auth_bsses[i]->pub);
120 wdev->auth_bsses[i] = NULL;
121 done = true;
122 break;
123 }
124 if (wdev->authtry_bsses[i] &&
125 memcmp(wdev->authtry_bsses[i]->pub.bssid, bssid, ETH_ALEN) == 0) {
126 cfg80211_unhold_bss(wdev->authtry_bsses[i]);
127 cfg80211_put_bss(&wdev->authtry_bsses[i]->pub);
128 wdev->authtry_bsses[i] = NULL;
129 done = true;
130 break;
131 }
132 }
Johannes Berg19957bb2009-07-02 17:20:43 +0200133
134 WARN_ON(!done);
135
Johannes Berg6829c872009-07-02 09:13:27 +0200136 if (wdev->sme_state == CFG80211_SME_CONNECTED) {
137 u16 reason_code;
138 bool from_ap;
139
140 reason_code = le16_to_cpu(mgmt->u.deauth.reason_code);
141
142 from_ap = memcmp(mgmt->da, dev->dev_addr, ETH_ALEN) == 0;
Johannes Berg667503d2009-07-07 03:56:11 +0200143 __cfg80211_disconnected(dev, NULL, 0, reason_code, from_ap);
Johannes Berg6829c872009-07-02 09:13:27 +0200144 } else if (wdev->sme_state == CFG80211_SME_CONNECTING) {
Johannes Berg667503d2009-07-07 03:56:11 +0200145 __cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, NULL, 0,
146 WLAN_STATUS_UNSPECIFIED_FAILURE,
147 false);
148 }
149}
150
151
152void cfg80211_send_deauth(struct net_device *dev, const u8 *buf, size_t len,
153 void *cookie)
154{
155 struct wireless_dev *wdev = dev->ieee80211_ptr;
156
157 BUG_ON(cookie && wdev != cookie);
158
159 if (cookie) {
160 /* called within callback */
161 __cfg80211_send_deauth(dev, buf, len);
162 } else {
163 wdev_lock(wdev);
164 __cfg80211_send_deauth(dev, buf, len);
165 wdev_unlock(wdev);
Johannes Berg6829c872009-07-02 09:13:27 +0200166 }
Jouni Malinen6039f6d2009-03-19 13:39:21 +0200167}
Jouni Malinen53b46b82009-03-27 20:53:56 +0200168EXPORT_SYMBOL(cfg80211_send_deauth);
Jouni Malinen6039f6d2009-03-19 13:39:21 +0200169
Johannes Berg667503d2009-07-07 03:56:11 +0200170static void __cfg80211_send_disassoc(struct net_device *dev,
171 const u8 *buf, size_t len)
Jouni Malinen6039f6d2009-03-19 13:39:21 +0200172{
Johannes Berg6829c872009-07-02 09:13:27 +0200173 struct wireless_dev *wdev = dev->ieee80211_ptr;
174 struct wiphy *wiphy = wdev->wiphy;
Jouni Malinen6039f6d2009-03-19 13:39:21 +0200175 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
Johannes Berg6829c872009-07-02 09:13:27 +0200176 struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf;
Johannes Berg19957bb2009-07-02 17:20:43 +0200177 const u8 *bssid = mgmt->bssid;
178 int i;
179 u16 reason_code;
180 bool from_ap;
181 bool done = false;
Johannes Berg6829c872009-07-02 09:13:27 +0200182
Johannes Berg596a07c2009-07-11 00:17:32 +0200183 ASSERT_WDEV_LOCK(wdev);
Johannes Bergcb0b4be2009-07-07 03:56:07 +0200184
185 nl80211_send_disassoc(rdev, dev, buf, len, GFP_KERNEL);
Johannes Berg6829c872009-07-02 09:13:27 +0200186
Johannes Berg596a07c2009-07-11 00:17:32 +0200187 if (wdev->sme_state != CFG80211_SME_CONNECTED)
188 return;
Johannes Berg6829c872009-07-02 09:13:27 +0200189
Johannes Berg19957bb2009-07-02 17:20:43 +0200190 if (wdev->current_bss &&
191 memcmp(wdev->current_bss, bssid, ETH_ALEN) == 0) {
192 for (i = 0; i < MAX_AUTH_BSSES; i++) {
193 if (wdev->authtry_bsses[i] || wdev->auth_bsses[i])
194 continue;
195 wdev->auth_bsses[i] = wdev->current_bss;
196 wdev->current_bss = NULL;
197 done = true;
198 cfg80211_sme_disassoc(dev, i);
199 break;
200 }
201 WARN_ON(!done);
202 } else
203 WARN_ON(1);
Johannes Berg6829c872009-07-02 09:13:27 +0200204
Johannes Berg6829c872009-07-02 09:13:27 +0200205
Johannes Berg19957bb2009-07-02 17:20:43 +0200206 reason_code = le16_to_cpu(mgmt->u.disassoc.reason_code);
207
208 from_ap = memcmp(mgmt->da, dev->dev_addr, ETH_ALEN) == 0;
Johannes Berg667503d2009-07-07 03:56:11 +0200209 __cfg80211_disconnected(dev, NULL, 0, reason_code, from_ap);
Johannes Berg667503d2009-07-07 03:56:11 +0200210}
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 Berg667503d2009-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 Berg667503d2009-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 Berg667503d2009-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 Berg667503d2009-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 Berg667503d2009-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 Berg667503d2009-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 Berg667503d2009-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,
Johannes Bergfffd0932009-07-08 14:22:54 +0200331 const u8 *ie, int ie_len,
332 const u8 *key, int key_len, int key_idx)
Johannes Berg19957bb2009-07-02 17:20:43 +0200333{
334 struct wireless_dev *wdev = dev->ieee80211_ptr;
335 struct cfg80211_auth_request req;
336 struct cfg80211_internal_bss *bss;
337 int i, err, slot = -1, nfree = 0;
338
Johannes Berg667503d2009-07-07 03:56:11 +0200339 ASSERT_WDEV_LOCK(wdev);
340
Johannes Bergfffd0932009-07-08 14:22:54 +0200341 if (auth_type == NL80211_AUTHTYPE_SHARED_KEY)
342 if (!key || !key_len || key_idx < 0 || key_idx > 4)
343 return -EINVAL;
344
Johannes Berg0a9b5e12009-07-02 18:26:18 +0200345 if (wdev->current_bss &&
346 memcmp(bssid, wdev->current_bss->pub.bssid, ETH_ALEN) == 0)
347 return -EALREADY;
348
349 for (i = 0; i < MAX_AUTH_BSSES; i++) {
350 if (wdev->authtry_bsses[i] &&
351 memcmp(bssid, wdev->authtry_bsses[i]->pub.bssid,
352 ETH_ALEN) == 0)
353 return -EALREADY;
354 if (wdev->auth_bsses[i] &&
355 memcmp(bssid, wdev->auth_bsses[i]->pub.bssid,
356 ETH_ALEN) == 0)
357 return -EALREADY;
358 }
359
Johannes Berg19957bb2009-07-02 17:20:43 +0200360 memset(&req, 0, sizeof(req));
361
362 req.ie = ie;
363 req.ie_len = ie_len;
364 req.auth_type = auth_type;
365 req.bss = cfg80211_get_bss(&rdev->wiphy, chan, bssid, ssid, ssid_len,
366 WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS);
Johannes Bergfffd0932009-07-08 14:22:54 +0200367 req.key = key;
368 req.key_len = key_len;
369 req.key_idx = key_idx;
Johannes Berg19957bb2009-07-02 17:20:43 +0200370 if (!req.bss)
371 return -ENOENT;
372
373 bss = bss_from_pub(req.bss);
374
375 for (i = 0; i < MAX_AUTH_BSSES; i++) {
Johannes Berg19957bb2009-07-02 17:20:43 +0200376 if (!wdev->auth_bsses[i] && !wdev->authtry_bsses[i]) {
377 slot = i;
378 nfree++;
379 }
380 }
381
382 /* we need one free slot for disassoc and one for this auth */
383 if (nfree < 2) {
384 err = -ENOSPC;
385 goto out;
386 }
387
388 wdev->authtry_bsses[slot] = bss;
389 cfg80211_hold_bss(bss);
390
391 err = rdev->ops->auth(&rdev->wiphy, dev, &req);
392 if (err) {
393 wdev->authtry_bsses[slot] = NULL;
394 cfg80211_unhold_bss(bss);
395 }
396
397 out:
398 if (err)
399 cfg80211_put_bss(req.bss);
400 return err;
401}
402
Johannes Berg667503d2009-07-07 03:56:11 +0200403int cfg80211_mlme_auth(struct cfg80211_registered_device *rdev,
404 struct net_device *dev, struct ieee80211_channel *chan,
405 enum nl80211_auth_type auth_type, const u8 *bssid,
406 const u8 *ssid, int ssid_len,
Johannes Bergfffd0932009-07-08 14:22:54 +0200407 const u8 *ie, int ie_len,
408 const u8 *key, int key_len, int key_idx)
Johannes Berg667503d2009-07-07 03:56:11 +0200409{
410 int err;
411
412 wdev_lock(dev->ieee80211_ptr);
413 err = __cfg80211_mlme_auth(rdev, dev, chan, auth_type, bssid,
Johannes Bergfffd0932009-07-08 14:22:54 +0200414 ssid, ssid_len, ie, ie_len,
415 key, key_len, key_idx);
Johannes Berg667503d2009-07-07 03:56:11 +0200416 wdev_unlock(dev->ieee80211_ptr);
417
418 return err;
419}
420
421int __cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev,
422 struct net_device *dev,
423 struct ieee80211_channel *chan,
424 const u8 *bssid, const u8 *prev_bssid,
425 const u8 *ssid, int ssid_len,
426 const u8 *ie, int ie_len, bool use_mfp,
427 struct cfg80211_crypto_settings *crypt)
Johannes Berg19957bb2009-07-02 17:20:43 +0200428{
429 struct wireless_dev *wdev = dev->ieee80211_ptr;
430 struct cfg80211_assoc_request req;
431 struct cfg80211_internal_bss *bss;
432 int i, err, slot = -1;
433
Johannes Berg667503d2009-07-07 03:56:11 +0200434 ASSERT_WDEV_LOCK(wdev);
435
Johannes Berg19957bb2009-07-02 17:20:43 +0200436 memset(&req, 0, sizeof(req));
437
438 if (wdev->current_bss)
439 return -EALREADY;
440
441 req.ie = ie;
442 req.ie_len = ie_len;
443 memcpy(&req.crypto, crypt, sizeof(req.crypto));
444 req.use_mfp = use_mfp;
Johannes Berg3e5d7642009-07-07 14:37:26 +0200445 req.prev_bssid = prev_bssid;
Johannes Berg19957bb2009-07-02 17:20:43 +0200446 req.bss = cfg80211_get_bss(&rdev->wiphy, chan, bssid, ssid, ssid_len,
447 WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS);
448 if (!req.bss)
449 return -ENOENT;
450
451 bss = bss_from_pub(req.bss);
452
453 for (i = 0; i < MAX_AUTH_BSSES; i++) {
454 if (bss == wdev->auth_bsses[i]) {
455 slot = i;
456 break;
457 }
458 }
459
460 if (slot < 0) {
461 err = -ENOTCONN;
462 goto out;
463 }
464
465 err = rdev->ops->assoc(&rdev->wiphy, dev, &req);
466 out:
467 /* still a reference in wdev->auth_bsses[slot] */
468 cfg80211_put_bss(req.bss);
469 return err;
470}
471
Johannes Berg667503d2009-07-07 03:56:11 +0200472int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev,
473 struct net_device *dev,
474 struct ieee80211_channel *chan,
475 const u8 *bssid, const u8 *prev_bssid,
476 const u8 *ssid, int ssid_len,
477 const u8 *ie, int ie_len, bool use_mfp,
478 struct cfg80211_crypto_settings *crypt)
479{
480 struct wireless_dev *wdev = dev->ieee80211_ptr;
481 int err;
482
483 wdev_lock(wdev);
484 err = __cfg80211_mlme_assoc(rdev, dev, chan, bssid, prev_bssid,
485 ssid, ssid_len, ie, ie_len, use_mfp, crypt);
486 wdev_unlock(wdev);
487
488 return err;
489}
490
491int __cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev,
492 struct net_device *dev, const u8 *bssid,
493 const u8 *ie, int ie_len, u16 reason)
Johannes Berg19957bb2009-07-02 17:20:43 +0200494{
495 struct wireless_dev *wdev = dev->ieee80211_ptr;
496 struct cfg80211_deauth_request req;
497 int i;
498
Johannes Berg667503d2009-07-07 03:56:11 +0200499 ASSERT_WDEV_LOCK(wdev);
500
Johannes Berg19957bb2009-07-02 17:20:43 +0200501 memset(&req, 0, sizeof(req));
502 req.reason_code = reason;
503 req.ie = ie;
504 req.ie_len = ie_len;
505 if (wdev->current_bss &&
506 memcmp(wdev->current_bss->pub.bssid, bssid, ETH_ALEN) == 0) {
507 req.bss = &wdev->current_bss->pub;
508 } else for (i = 0; i < MAX_AUTH_BSSES; i++) {
509 if (wdev->auth_bsses[i] &&
510 memcmp(bssid, wdev->auth_bsses[i]->pub.bssid, ETH_ALEN) == 0) {
511 req.bss = &wdev->auth_bsses[i]->pub;
512 break;
513 }
514 if (wdev->authtry_bsses[i] &&
515 memcmp(bssid, wdev->authtry_bsses[i]->pub.bssid, ETH_ALEN) == 0) {
516 req.bss = &wdev->authtry_bsses[i]->pub;
517 break;
518 }
519 }
520
521 if (!req.bss)
522 return -ENOTCONN;
523
Johannes Berg667503d2009-07-07 03:56:11 +0200524 return rdev->ops->deauth(&rdev->wiphy, dev, &req, wdev);
Johannes Berg19957bb2009-07-02 17:20:43 +0200525}
526
Johannes Berg667503d2009-07-07 03:56:11 +0200527int cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev,
528 struct net_device *dev, const u8 *bssid,
529 const u8 *ie, int ie_len, u16 reason)
530{
531 struct wireless_dev *wdev = dev->ieee80211_ptr;
532 int err;
533
534 wdev_lock(wdev);
535 err = __cfg80211_mlme_deauth(rdev, dev, bssid, ie, ie_len, reason);
536 wdev_unlock(wdev);
537
538 return err;
539}
540
541static int __cfg80211_mlme_disassoc(struct cfg80211_registered_device *rdev,
542 struct net_device *dev, const u8 *bssid,
543 const u8 *ie, int ie_len, u16 reason)
Johannes Berg19957bb2009-07-02 17:20:43 +0200544{
545 struct wireless_dev *wdev = dev->ieee80211_ptr;
546 struct cfg80211_disassoc_request req;
547
Johannes Berg667503d2009-07-07 03:56:11 +0200548 ASSERT_WDEV_LOCK(wdev);
549
Johannes Bergf9d6b402009-07-27 10:22:28 +0200550 if (wdev->sme_state != CFG80211_SME_CONNECTED)
551 return -ENOTCONN;
552
553 if (WARN_ON(!wdev->current_bss))
554 return -ENOTCONN;
555
Johannes Berg19957bb2009-07-02 17:20:43 +0200556 memset(&req, 0, sizeof(req));
557 req.reason_code = reason;
558 req.ie = ie;
559 req.ie_len = ie_len;
560 if (memcmp(wdev->current_bss->pub.bssid, bssid, ETH_ALEN) == 0)
561 req.bss = &wdev->current_bss->pub;
562 else
563 return -ENOTCONN;
564
Johannes Berg667503d2009-07-07 03:56:11 +0200565 return rdev->ops->disassoc(&rdev->wiphy, dev, &req, wdev);
566}
567
568int cfg80211_mlme_disassoc(struct cfg80211_registered_device *rdev,
569 struct net_device *dev, const u8 *bssid,
570 const u8 *ie, int ie_len, u16 reason)
571{
572 struct wireless_dev *wdev = dev->ieee80211_ptr;
573 int err;
574
575 wdev_lock(wdev);
576 err = __cfg80211_mlme_disassoc(rdev, dev, bssid, ie, ie_len, reason);
577 wdev_unlock(wdev);
578
579 return err;
Johannes Berg19957bb2009-07-02 17:20:43 +0200580}
581
582void cfg80211_mlme_down(struct cfg80211_registered_device *rdev,
583 struct net_device *dev)
584{
585 struct wireless_dev *wdev = dev->ieee80211_ptr;
586 struct cfg80211_deauth_request req;
587 int i;
588
Johannes Berg667503d2009-07-07 03:56:11 +0200589 ASSERT_WDEV_LOCK(wdev);
590
Johannes Berg19957bb2009-07-02 17:20:43 +0200591 if (!rdev->ops->deauth)
592 return;
593
594 memset(&req, 0, sizeof(req));
595 req.reason_code = WLAN_REASON_DEAUTH_LEAVING;
596 req.ie = NULL;
597 req.ie_len = 0;
598
599 if (wdev->current_bss) {
600 req.bss = &wdev->current_bss->pub;
Johannes Berg667503d2009-07-07 03:56:11 +0200601 rdev->ops->deauth(&rdev->wiphy, dev, &req, wdev);
Johannes Berg19957bb2009-07-02 17:20:43 +0200602 if (wdev->current_bss) {
603 cfg80211_unhold_bss(wdev->current_bss);
604 cfg80211_put_bss(&wdev->current_bss->pub);
605 wdev->current_bss = NULL;
606 }
607 }
608
609 for (i = 0; i < MAX_AUTH_BSSES; i++) {
610 if (wdev->auth_bsses[i]) {
611 req.bss = &wdev->auth_bsses[i]->pub;
Johannes Berg667503d2009-07-07 03:56:11 +0200612 rdev->ops->deauth(&rdev->wiphy, dev, &req, wdev);
Johannes Berg19957bb2009-07-02 17:20:43 +0200613 if (wdev->auth_bsses[i]) {
614 cfg80211_unhold_bss(wdev->auth_bsses[i]);
615 cfg80211_put_bss(&wdev->auth_bsses[i]->pub);
616 wdev->auth_bsses[i] = NULL;
617 }
618 }
619 if (wdev->authtry_bsses[i]) {
620 req.bss = &wdev->authtry_bsses[i]->pub;
Johannes Berg667503d2009-07-07 03:56:11 +0200621 rdev->ops->deauth(&rdev->wiphy, dev, &req, wdev);
Johannes Berg19957bb2009-07-02 17:20:43 +0200622 if (wdev->authtry_bsses[i]) {
623 cfg80211_unhold_bss(wdev->authtry_bsses[i]);
624 cfg80211_put_bss(&wdev->authtry_bsses[i]->pub);
625 wdev->authtry_bsses[i] = NULL;
626 }
627 }
628 }
629}