blob: 1f87b4e7f4f77b3e83e2e83111b2532ad707322f [file] [log] [blame]
Jouni Malinen6039f6d2009-03-19 13:39:21 +02001/*
2 * cfg80211 MLME SAP interface
3 *
4 * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
5 */
6
7#include <linux/kernel.h>
8#include <linux/module.h>
9#include <linux/netdevice.h>
10#include <linux/nl80211.h>
Johannes Berga9a11622009-07-27 12:01:53 +020011#include <linux/wireless.h>
Jouni Malinen6039f6d2009-03-19 13:39:21 +020012#include <net/cfg80211.h>
Johannes Berga9a11622009-07-27 12:01:53 +020013#include <net/iw_handler.h>
Jouni Malinen6039f6d2009-03-19 13:39:21 +020014#include "core.h"
15#include "nl80211.h"
16
Johannes Bergcb0b4be2009-07-07 03:56:07 +020017void cfg80211_send_rx_auth(struct net_device *dev, const u8 *buf, size_t len)
Jouni Malinen6039f6d2009-03-19 13:39:21 +020018{
Johannes Berg19957bb2009-07-02 17:20:43 +020019 struct wireless_dev *wdev = dev->ieee80211_ptr;
20 struct wiphy *wiphy = wdev->wiphy;
Jouni Malinen6039f6d2009-03-19 13:39:21 +020021 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
Johannes Berg19957bb2009-07-02 17:20:43 +020022 struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf;
23 u8 *bssid = mgmt->bssid;
24 int i;
25 u16 status = le16_to_cpu(mgmt->u.auth.status_code);
26 bool done = false;
27
Johannes Berg667503d2009-07-07 03:56:11 +020028 wdev_lock(wdev);
Johannes Bergcb0b4be2009-07-07 03:56:07 +020029
Johannes Berg19957bb2009-07-02 17:20:43 +020030 for (i = 0; i < MAX_AUTH_BSSES; i++) {
31 if (wdev->authtry_bsses[i] &&
32 memcmp(wdev->authtry_bsses[i]->pub.bssid, bssid,
33 ETH_ALEN) == 0) {
34 if (status == WLAN_STATUS_SUCCESS) {
35 wdev->auth_bsses[i] = wdev->authtry_bsses[i];
36 } else {
37 cfg80211_unhold_bss(wdev->authtry_bsses[i]);
38 cfg80211_put_bss(&wdev->authtry_bsses[i]->pub);
39 }
40 wdev->authtry_bsses[i] = NULL;
41 done = true;
42 break;
43 }
44 }
45
46 WARN_ON(!done);
Johannes Berg6829c872009-07-02 09:13:27 +020047
Johannes Bergcb0b4be2009-07-07 03:56:07 +020048 nl80211_send_rx_auth(rdev, dev, buf, len, GFP_KERNEL);
Johannes Berg6829c872009-07-02 09:13:27 +020049 cfg80211_sme_rx_auth(dev, buf, len);
Johannes Berg667503d2009-07-07 03:56:11 +020050
51 wdev_unlock(wdev);
Jouni Malinen6039f6d2009-03-19 13:39:21 +020052}
53EXPORT_SYMBOL(cfg80211_send_rx_auth);
54
Johannes Bergcb0b4be2009-07-07 03:56:07 +020055void cfg80211_send_rx_assoc(struct net_device *dev, const u8 *buf, size_t len)
Jouni Malinen6039f6d2009-03-19 13:39:21 +020056{
Johannes Berg6829c872009-07-02 09:13:27 +020057 u16 status_code;
58 struct wireless_dev *wdev = dev->ieee80211_ptr;
59 struct wiphy *wiphy = wdev->wiphy;
Jouni Malinen6039f6d2009-03-19 13:39:21 +020060 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
Johannes Berg6829c872009-07-02 09:13:27 +020061 struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf;
62 u8 *ie = mgmt->u.assoc_resp.variable;
Johannes Berg19957bb2009-07-02 17:20:43 +020063 int i, ieoffs = offsetof(struct ieee80211_mgmt, u.assoc_resp.variable);
Johannes Bergdf7fc0f2009-07-29 11:23:49 +020064 struct cfg80211_internal_bss *bss = NULL;
Johannes Berg6829c872009-07-02 09:13:27 +020065
Johannes Berg667503d2009-07-07 03:56:11 +020066 wdev_lock(wdev);
Johannes Bergcb0b4be2009-07-07 03:56:07 +020067
Johannes Berg6829c872009-07-02 09:13:27 +020068 status_code = le16_to_cpu(mgmt->u.assoc_resp.status_code);
69
Johannes Bergf401a6f2009-08-07 14:51:05 +020070 /*
71 * This is a bit of a hack, we don't notify userspace of
72 * a (re-)association reply if we tried to send a reassoc
73 * and got a reject -- we only try again with an assoc
74 * frame instead of reassoc.
75 */
76 if (status_code != WLAN_STATUS_SUCCESS && wdev->conn &&
77 cfg80211_sme_failed_reassoc(wdev))
78 goto out;
79
Johannes Bergcb0b4be2009-07-07 03:56:07 +020080 nl80211_send_rx_assoc(rdev, dev, buf, len, GFP_KERNEL);
Johannes Berg6829c872009-07-02 09:13:27 +020081
Johannes Berg19957bb2009-07-02 17:20:43 +020082 if (status_code == WLAN_STATUS_SUCCESS) {
Johannes Bergdf7fc0f2009-07-29 11:23:49 +020083 for (i = 0; i < MAX_AUTH_BSSES; i++) {
84 if (!wdev->auth_bsses[i])
85 continue;
86 if (memcmp(wdev->auth_bsses[i]->pub.bssid, mgmt->bssid,
87 ETH_ALEN) == 0) {
88 bss = wdev->auth_bsses[i];
Johannes Berg19957bb2009-07-02 17:20:43 +020089 wdev->auth_bsses[i] = NULL;
Johannes Bergdf7fc0f2009-07-29 11:23:49 +020090 /* additional reference to drop hold */
91 cfg80211_ref_bss(bss);
Johannes Berg19957bb2009-07-02 17:20:43 +020092 break;
93 }
94 }
95
Johannes Bergdf7fc0f2009-07-29 11:23:49 +020096 WARN_ON(!bss);
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 Berg667503d2009-07-07 03:56:11 +0200118
Johannes Bergf401a6f2009-08-07 14:51:05 +0200119 out:
Johannes Berg667503d2009-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 Berg667503d2009-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;
Johannes Berg6829c872009-07-02 09:13:27 +0200133
Johannes Berg667503d2009-07-07 03:56:11 +0200134 ASSERT_WDEV_LOCK(wdev);
Johannes Bergcb0b4be2009-07-07 03:56:07 +0200135
136 nl80211_send_deauth(rdev, dev, buf, len, GFP_KERNEL);
Johannes Berg6829c872009-07-02 09:13:27 +0200137
Johannes Berg19957bb2009-07-02 17:20:43 +0200138 if (wdev->current_bss &&
139 memcmp(wdev->current_bss->pub.bssid, bssid, ETH_ALEN) == 0) {
Johannes Berg19957bb2009-07-02 17:20:43 +0200140 cfg80211_unhold_bss(wdev->current_bss);
141 cfg80211_put_bss(&wdev->current_bss->pub);
142 wdev->current_bss = NULL;
143 } else for (i = 0; i < MAX_AUTH_BSSES; i++) {
144 if (wdev->auth_bsses[i] &&
145 memcmp(wdev->auth_bsses[i]->pub.bssid, bssid, ETH_ALEN) == 0) {
146 cfg80211_unhold_bss(wdev->auth_bsses[i]);
147 cfg80211_put_bss(&wdev->auth_bsses[i]->pub);
148 wdev->auth_bsses[i] = NULL;
Johannes Berg19957bb2009-07-02 17:20:43 +0200149 break;
150 }
151 if (wdev->authtry_bsses[i] &&
152 memcmp(wdev->authtry_bsses[i]->pub.bssid, bssid, ETH_ALEN) == 0) {
153 cfg80211_unhold_bss(wdev->authtry_bsses[i]);
154 cfg80211_put_bss(&wdev->authtry_bsses[i]->pub);
155 wdev->authtry_bsses[i] = NULL;
Johannes Berg19957bb2009-07-02 17:20:43 +0200156 break;
157 }
158 }
Johannes Berg19957bb2009-07-02 17:20:43 +0200159
Johannes Berg6829c872009-07-02 09:13:27 +0200160 if (wdev->sme_state == CFG80211_SME_CONNECTED) {
161 u16 reason_code;
162 bool from_ap;
163
164 reason_code = le16_to_cpu(mgmt->u.deauth.reason_code);
165
Johannes Berge458b8a2009-08-06 20:41:33 +0200166 from_ap = memcmp(mgmt->sa, dev->dev_addr, ETH_ALEN) != 0;
Johannes Berg667503d2009-07-07 03:56:11 +0200167 __cfg80211_disconnected(dev, NULL, 0, reason_code, from_ap);
Johannes Berg6829c872009-07-02 09:13:27 +0200168 } else if (wdev->sme_state == CFG80211_SME_CONNECTING) {
Johannes Berg667503d2009-07-07 03:56:11 +0200169 __cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, NULL, 0,
170 WLAN_STATUS_UNSPECIFIED_FAILURE,
Johannes Bergdf7fc0f2009-07-29 11:23:49 +0200171 false, NULL);
Johannes Berg667503d2009-07-07 03:56:11 +0200172 }
173}
Holger Schurigce470612009-10-13 13:28:13 +0200174EXPORT_SYMBOL(__cfg80211_send_deauth);
Johannes Berg667503d2009-07-07 03:56:11 +0200175
Holger Schurigce470612009-10-13 13:28:13 +0200176void cfg80211_send_deauth(struct net_device *dev, const u8 *buf, size_t len)
Johannes Berg667503d2009-07-07 03:56:11 +0200177{
178 struct wireless_dev *wdev = dev->ieee80211_ptr;
179
Holger Schurigce470612009-10-13 13:28:13 +0200180 wdev_lock(wdev);
181 __cfg80211_send_deauth(dev, buf, len);
182 wdev_unlock(wdev);
Jouni Malinen6039f6d2009-03-19 13:39:21 +0200183}
Jouni Malinen53b46b82009-03-27 20:53:56 +0200184EXPORT_SYMBOL(cfg80211_send_deauth);
Jouni Malinen6039f6d2009-03-19 13:39:21 +0200185
Holger Schurigce470612009-10-13 13:28:13 +0200186void __cfg80211_send_disassoc(struct net_device *dev,
Johannes Berg667503d2009-07-07 03:56:11 +0200187 const u8 *buf, size_t len)
Jouni Malinen6039f6d2009-03-19 13:39:21 +0200188{
Johannes Berg6829c872009-07-02 09:13:27 +0200189 struct wireless_dev *wdev = dev->ieee80211_ptr;
190 struct wiphy *wiphy = wdev->wiphy;
Jouni Malinen6039f6d2009-03-19 13:39:21 +0200191 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
Johannes Berg6829c872009-07-02 09:13:27 +0200192 struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf;
Johannes Berg19957bb2009-07-02 17:20:43 +0200193 const u8 *bssid = mgmt->bssid;
194 int i;
195 u16 reason_code;
196 bool from_ap;
197 bool done = false;
Johannes Berg6829c872009-07-02 09:13:27 +0200198
Johannes Berg596a07c2009-07-11 00:17:32 +0200199 ASSERT_WDEV_LOCK(wdev);
Johannes Bergcb0b4be2009-07-07 03:56:07 +0200200
201 nl80211_send_disassoc(rdev, dev, buf, len, GFP_KERNEL);
Johannes Berg6829c872009-07-02 09:13:27 +0200202
Johannes Berg596a07c2009-07-11 00:17:32 +0200203 if (wdev->sme_state != CFG80211_SME_CONNECTED)
204 return;
Johannes Berg6829c872009-07-02 09:13:27 +0200205
Johannes Berg19957bb2009-07-02 17:20:43 +0200206 if (wdev->current_bss &&
Pavel Roskinb935df02009-08-06 04:52:42 -0400207 memcmp(wdev->current_bss->pub.bssid, bssid, ETH_ALEN) == 0) {
Johannes Berg19957bb2009-07-02 17:20:43 +0200208 for (i = 0; i < MAX_AUTH_BSSES; i++) {
209 if (wdev->authtry_bsses[i] || wdev->auth_bsses[i])
210 continue;
211 wdev->auth_bsses[i] = wdev->current_bss;
212 wdev->current_bss = NULL;
213 done = true;
214 cfg80211_sme_disassoc(dev, i);
215 break;
216 }
217 WARN_ON(!done);
218 } else
219 WARN_ON(1);
Johannes Berg6829c872009-07-02 09:13:27 +0200220
Johannes Berg6829c872009-07-02 09:13:27 +0200221
Johannes Berg19957bb2009-07-02 17:20:43 +0200222 reason_code = le16_to_cpu(mgmt->u.disassoc.reason_code);
223
Johannes Berge458b8a2009-08-06 20:41:33 +0200224 from_ap = memcmp(mgmt->sa, dev->dev_addr, ETH_ALEN) != 0;
Johannes Berg667503d2009-07-07 03:56:11 +0200225 __cfg80211_disconnected(dev, NULL, 0, reason_code, from_ap);
Johannes Berg667503d2009-07-07 03:56:11 +0200226}
Holger Schurigce470612009-10-13 13:28:13 +0200227EXPORT_SYMBOL(__cfg80211_send_disassoc);
Johannes Berg667503d2009-07-07 03:56:11 +0200228
Holger Schurigce470612009-10-13 13:28:13 +0200229void cfg80211_send_disassoc(struct net_device *dev, const u8 *buf, size_t len)
Johannes Berg667503d2009-07-07 03:56:11 +0200230{
231 struct wireless_dev *wdev = dev->ieee80211_ptr;
232
Holger Schurigce470612009-10-13 13:28:13 +0200233 wdev_lock(wdev);
234 __cfg80211_send_disassoc(dev, buf, len);
235 wdev_unlock(wdev);
Jouni Malinen6039f6d2009-03-19 13:39:21 +0200236}
Jouni Malinen53b46b82009-03-27 20:53:56 +0200237EXPORT_SYMBOL(cfg80211_send_disassoc);
Jouni Malinena3b8b052009-03-27 21:59:49 +0200238
Johannes Bergcb0b4be2009-07-07 03:56:07 +0200239void cfg80211_send_auth_timeout(struct net_device *dev, const u8 *addr)
Jouni Malinen1965c852009-04-22 21:38:25 +0300240{
Johannes Berg6829c872009-07-02 09:13:27 +0200241 struct wireless_dev *wdev = dev->ieee80211_ptr;
242 struct wiphy *wiphy = wdev->wiphy;
Jouni Malinen1965c852009-04-22 21:38:25 +0300243 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
Johannes Berg19957bb2009-07-02 17:20:43 +0200244 int i;
245 bool done = false;
246
Johannes Berg667503d2009-07-07 03:56:11 +0200247 wdev_lock(wdev);
Johannes Bergcb0b4be2009-07-07 03:56:07 +0200248
249 nl80211_send_auth_timeout(rdev, dev, addr, GFP_KERNEL);
Johannes Berg6829c872009-07-02 09:13:27 +0200250 if (wdev->sme_state == CFG80211_SME_CONNECTING)
Johannes Berg667503d2009-07-07 03:56:11 +0200251 __cfg80211_connect_result(dev, addr, NULL, 0, NULL, 0,
252 WLAN_STATUS_UNSPECIFIED_FAILURE,
Johannes Bergdf7fc0f2009-07-29 11:23:49 +0200253 false, NULL);
Johannes Berg19957bb2009-07-02 17:20:43 +0200254
255 for (i = 0; addr && i < MAX_AUTH_BSSES; i++) {
256 if (wdev->authtry_bsses[i] &&
257 memcmp(wdev->authtry_bsses[i]->pub.bssid,
258 addr, ETH_ALEN) == 0) {
259 cfg80211_unhold_bss(wdev->authtry_bsses[i]);
260 cfg80211_put_bss(&wdev->authtry_bsses[i]->pub);
261 wdev->authtry_bsses[i] = NULL;
262 done = true;
263 break;
264 }
265 }
266
267 WARN_ON(!done);
Johannes Berg667503d2009-07-07 03:56:11 +0200268
269 wdev_unlock(wdev);
Jouni Malinen1965c852009-04-22 21:38:25 +0300270}
271EXPORT_SYMBOL(cfg80211_send_auth_timeout);
272
Johannes Bergcb0b4be2009-07-07 03:56:07 +0200273void cfg80211_send_assoc_timeout(struct net_device *dev, const u8 *addr)
Jouni Malinen1965c852009-04-22 21:38:25 +0300274{
Johannes Berg6829c872009-07-02 09:13:27 +0200275 struct wireless_dev *wdev = dev->ieee80211_ptr;
276 struct wiphy *wiphy = wdev->wiphy;
Jouni Malinen1965c852009-04-22 21:38:25 +0300277 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
Johannes Berg19957bb2009-07-02 17:20:43 +0200278 int i;
279 bool done = false;
280
Johannes Berg667503d2009-07-07 03:56:11 +0200281 wdev_lock(wdev);
Johannes Bergcb0b4be2009-07-07 03:56:07 +0200282
283 nl80211_send_assoc_timeout(rdev, dev, addr, GFP_KERNEL);
Johannes Berg6829c872009-07-02 09:13:27 +0200284 if (wdev->sme_state == CFG80211_SME_CONNECTING)
Johannes Berg667503d2009-07-07 03:56:11 +0200285 __cfg80211_connect_result(dev, addr, NULL, 0, NULL, 0,
286 WLAN_STATUS_UNSPECIFIED_FAILURE,
Johannes Bergdf7fc0f2009-07-29 11:23:49 +0200287 false, NULL);
Johannes Berg19957bb2009-07-02 17:20:43 +0200288
289 for (i = 0; addr && i < MAX_AUTH_BSSES; i++) {
290 if (wdev->auth_bsses[i] &&
291 memcmp(wdev->auth_bsses[i]->pub.bssid,
292 addr, ETH_ALEN) == 0) {
293 cfg80211_unhold_bss(wdev->auth_bsses[i]);
294 cfg80211_put_bss(&wdev->auth_bsses[i]->pub);
295 wdev->auth_bsses[i] = NULL;
296 done = true;
297 break;
298 }
299 }
300
301 WARN_ON(!done);
Johannes Berg667503d2009-07-07 03:56:11 +0200302
303 wdev_unlock(wdev);
Jouni Malinen1965c852009-04-22 21:38:25 +0300304}
305EXPORT_SYMBOL(cfg80211_send_assoc_timeout);
306
Jouni Malinena3b8b052009-03-27 21:59:49 +0200307void cfg80211_michael_mic_failure(struct net_device *dev, const u8 *addr,
308 enum nl80211_key_type key_type, int key_id,
Johannes Berge6d6e342009-07-01 21:26:47 +0200309 const u8 *tsc, gfp_t gfp)
Jouni Malinena3b8b052009-03-27 21:59:49 +0200310{
311 struct wiphy *wiphy = dev->ieee80211_ptr->wiphy;
312 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
Johannes Berg3d23e342009-09-29 23:27:28 +0200313#ifdef CONFIG_CFG80211_WEXT
Johannes Bergf58d4ed2009-06-19 02:45:21 +0200314 union iwreq_data wrqu;
Johannes Berge6d6e342009-07-01 21:26:47 +0200315 char *buf = kmalloc(128, gfp);
Johannes Bergf58d4ed2009-06-19 02:45:21 +0200316
317 if (buf) {
318 sprintf(buf, "MLME-MICHAELMICFAILURE.indication("
319 "keyid=%d %scast addr=%pM)", key_id,
320 key_type == NL80211_KEYTYPE_GROUP ? "broad" : "uni",
321 addr);
322 memset(&wrqu, 0, sizeof(wrqu));
323 wrqu.data.length = strlen(buf);
324 wireless_send_event(dev, IWEVCUSTOM, &wrqu, buf);
325 kfree(buf);
326 }
327#endif
328
Johannes Berge6d6e342009-07-01 21:26:47 +0200329 nl80211_michael_mic_failure(rdev, dev, addr, key_type, key_id, tsc, gfp);
Jouni Malinena3b8b052009-03-27 21:59:49 +0200330}
331EXPORT_SYMBOL(cfg80211_michael_mic_failure);
Johannes Berg19957bb2009-07-02 17:20:43 +0200332
333/* some MLME handling for userspace SME */
Johannes Berg667503d2009-07-07 03:56:11 +0200334int __cfg80211_mlme_auth(struct cfg80211_registered_device *rdev,
335 struct net_device *dev,
336 struct ieee80211_channel *chan,
337 enum nl80211_auth_type auth_type,
338 const u8 *bssid,
339 const u8 *ssid, int ssid_len,
Johannes Bergfffd0932009-07-08 14:22:54 +0200340 const u8 *ie, int ie_len,
341 const u8 *key, int key_len, int key_idx)
Johannes Berg19957bb2009-07-02 17:20:43 +0200342{
343 struct wireless_dev *wdev = dev->ieee80211_ptr;
344 struct cfg80211_auth_request req;
345 struct cfg80211_internal_bss *bss;
346 int i, err, slot = -1, nfree = 0;
347
Johannes Berg667503d2009-07-07 03:56:11 +0200348 ASSERT_WDEV_LOCK(wdev);
349
Johannes Bergfffd0932009-07-08 14:22:54 +0200350 if (auth_type == NL80211_AUTHTYPE_SHARED_KEY)
351 if (!key || !key_len || key_idx < 0 || key_idx > 4)
352 return -EINVAL;
353
Johannes Berg0a9b5e12009-07-02 18:26:18 +0200354 if (wdev->current_bss &&
355 memcmp(bssid, wdev->current_bss->pub.bssid, ETH_ALEN) == 0)
356 return -EALREADY;
357
358 for (i = 0; i < MAX_AUTH_BSSES; i++) {
359 if (wdev->authtry_bsses[i] &&
360 memcmp(bssid, wdev->authtry_bsses[i]->pub.bssid,
361 ETH_ALEN) == 0)
362 return -EALREADY;
363 if (wdev->auth_bsses[i] &&
364 memcmp(bssid, wdev->auth_bsses[i]->pub.bssid,
365 ETH_ALEN) == 0)
366 return -EALREADY;
367 }
368
Johannes Berg19957bb2009-07-02 17:20:43 +0200369 memset(&req, 0, sizeof(req));
370
371 req.ie = ie;
372 req.ie_len = ie_len;
373 req.auth_type = auth_type;
374 req.bss = cfg80211_get_bss(&rdev->wiphy, chan, bssid, ssid, ssid_len,
375 WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS);
Johannes Bergfffd0932009-07-08 14:22:54 +0200376 req.key = key;
377 req.key_len = key_len;
378 req.key_idx = key_idx;
Johannes Berg19957bb2009-07-02 17:20:43 +0200379 if (!req.bss)
380 return -ENOENT;
381
382 bss = bss_from_pub(req.bss);
383
384 for (i = 0; i < MAX_AUTH_BSSES; i++) {
Johannes Berg19957bb2009-07-02 17:20:43 +0200385 if (!wdev->auth_bsses[i] && !wdev->authtry_bsses[i]) {
386 slot = i;
387 nfree++;
388 }
389 }
390
391 /* we need one free slot for disassoc and one for this auth */
392 if (nfree < 2) {
393 err = -ENOSPC;
394 goto out;
395 }
396
397 wdev->authtry_bsses[slot] = bss;
398 cfg80211_hold_bss(bss);
399
400 err = rdev->ops->auth(&rdev->wiphy, dev, &req);
401 if (err) {
402 wdev->authtry_bsses[slot] = NULL;
403 cfg80211_unhold_bss(bss);
404 }
405
406 out:
407 if (err)
408 cfg80211_put_bss(req.bss);
409 return err;
410}
411
Johannes Berg667503d2009-07-07 03:56:11 +0200412int cfg80211_mlme_auth(struct cfg80211_registered_device *rdev,
413 struct net_device *dev, struct ieee80211_channel *chan,
414 enum nl80211_auth_type auth_type, const u8 *bssid,
415 const u8 *ssid, int ssid_len,
Johannes Bergfffd0932009-07-08 14:22:54 +0200416 const u8 *ie, int ie_len,
417 const u8 *key, int key_len, int key_idx)
Johannes Berg667503d2009-07-07 03:56:11 +0200418{
419 int err;
420
421 wdev_lock(dev->ieee80211_ptr);
422 err = __cfg80211_mlme_auth(rdev, dev, chan, auth_type, bssid,
Johannes Bergfffd0932009-07-08 14:22:54 +0200423 ssid, ssid_len, ie, ie_len,
424 key, key_len, key_idx);
Johannes Berg667503d2009-07-07 03:56:11 +0200425 wdev_unlock(dev->ieee80211_ptr);
426
427 return err;
428}
429
430int __cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev,
431 struct net_device *dev,
432 struct ieee80211_channel *chan,
433 const u8 *bssid, const u8 *prev_bssid,
434 const u8 *ssid, int ssid_len,
435 const u8 *ie, int ie_len, bool use_mfp,
436 struct cfg80211_crypto_settings *crypt)
Johannes Berg19957bb2009-07-02 17:20:43 +0200437{
438 struct wireless_dev *wdev = dev->ieee80211_ptr;
439 struct cfg80211_assoc_request req;
440 struct cfg80211_internal_bss *bss;
441 int i, err, slot = -1;
442
Johannes Berg667503d2009-07-07 03:56:11 +0200443 ASSERT_WDEV_LOCK(wdev);
444
Johannes Berg19957bb2009-07-02 17:20:43 +0200445 memset(&req, 0, sizeof(req));
446
447 if (wdev->current_bss)
448 return -EALREADY;
449
450 req.ie = ie;
451 req.ie_len = ie_len;
452 memcpy(&req.crypto, crypt, sizeof(req.crypto));
453 req.use_mfp = use_mfp;
Johannes Berg3e5d7642009-07-07 14:37:26 +0200454 req.prev_bssid = prev_bssid;
Johannes Berg19957bb2009-07-02 17:20:43 +0200455 req.bss = cfg80211_get_bss(&rdev->wiphy, chan, bssid, ssid, ssid_len,
456 WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS);
457 if (!req.bss)
458 return -ENOENT;
459
460 bss = bss_from_pub(req.bss);
461
462 for (i = 0; i < MAX_AUTH_BSSES; i++) {
463 if (bss == wdev->auth_bsses[i]) {
464 slot = i;
465 break;
466 }
467 }
468
469 if (slot < 0) {
470 err = -ENOTCONN;
471 goto out;
472 }
473
474 err = rdev->ops->assoc(&rdev->wiphy, dev, &req);
475 out:
476 /* still a reference in wdev->auth_bsses[slot] */
477 cfg80211_put_bss(req.bss);
478 return err;
479}
480
Johannes Berg667503d2009-07-07 03:56:11 +0200481int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev,
482 struct net_device *dev,
483 struct ieee80211_channel *chan,
484 const u8 *bssid, const u8 *prev_bssid,
485 const u8 *ssid, int ssid_len,
486 const u8 *ie, int ie_len, bool use_mfp,
487 struct cfg80211_crypto_settings *crypt)
488{
489 struct wireless_dev *wdev = dev->ieee80211_ptr;
490 int err;
491
492 wdev_lock(wdev);
493 err = __cfg80211_mlme_assoc(rdev, dev, chan, bssid, prev_bssid,
494 ssid, ssid_len, ie, ie_len, use_mfp, crypt);
495 wdev_unlock(wdev);
496
497 return err;
498}
499
500int __cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev,
501 struct net_device *dev, const u8 *bssid,
502 const u8 *ie, int ie_len, u16 reason)
Johannes Berg19957bb2009-07-02 17:20:43 +0200503{
504 struct wireless_dev *wdev = dev->ieee80211_ptr;
505 struct cfg80211_deauth_request req;
506 int i;
507
Johannes Berg667503d2009-07-07 03:56:11 +0200508 ASSERT_WDEV_LOCK(wdev);
509
Johannes Berg19957bb2009-07-02 17:20:43 +0200510 memset(&req, 0, sizeof(req));
511 req.reason_code = reason;
512 req.ie = ie;
513 req.ie_len = ie_len;
514 if (wdev->current_bss &&
515 memcmp(wdev->current_bss->pub.bssid, bssid, ETH_ALEN) == 0) {
516 req.bss = &wdev->current_bss->pub;
517 } else for (i = 0; i < MAX_AUTH_BSSES; i++) {
518 if (wdev->auth_bsses[i] &&
519 memcmp(bssid, wdev->auth_bsses[i]->pub.bssid, ETH_ALEN) == 0) {
520 req.bss = &wdev->auth_bsses[i]->pub;
521 break;
522 }
523 if (wdev->authtry_bsses[i] &&
524 memcmp(bssid, wdev->authtry_bsses[i]->pub.bssid, ETH_ALEN) == 0) {
525 req.bss = &wdev->authtry_bsses[i]->pub;
526 break;
527 }
528 }
529
530 if (!req.bss)
531 return -ENOTCONN;
532
Johannes Berg667503d2009-07-07 03:56:11 +0200533 return rdev->ops->deauth(&rdev->wiphy, dev, &req, wdev);
Johannes Berg19957bb2009-07-02 17:20:43 +0200534}
535
Johannes Berg667503d2009-07-07 03:56:11 +0200536int cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev,
537 struct net_device *dev, const u8 *bssid,
538 const u8 *ie, int ie_len, u16 reason)
539{
540 struct wireless_dev *wdev = dev->ieee80211_ptr;
541 int err;
542
543 wdev_lock(wdev);
544 err = __cfg80211_mlme_deauth(rdev, dev, bssid, ie, ie_len, reason);
545 wdev_unlock(wdev);
546
547 return err;
548}
549
550static int __cfg80211_mlme_disassoc(struct cfg80211_registered_device *rdev,
551 struct net_device *dev, const u8 *bssid,
552 const u8 *ie, int ie_len, u16 reason)
Johannes Berg19957bb2009-07-02 17:20:43 +0200553{
554 struct wireless_dev *wdev = dev->ieee80211_ptr;
555 struct cfg80211_disassoc_request req;
556
Johannes Berg667503d2009-07-07 03:56:11 +0200557 ASSERT_WDEV_LOCK(wdev);
558
Johannes Bergf9d6b402009-07-27 10:22:28 +0200559 if (wdev->sme_state != CFG80211_SME_CONNECTED)
560 return -ENOTCONN;
561
562 if (WARN_ON(!wdev->current_bss))
563 return -ENOTCONN;
564
Johannes Berg19957bb2009-07-02 17:20:43 +0200565 memset(&req, 0, sizeof(req));
566 req.reason_code = reason;
567 req.ie = ie;
568 req.ie_len = ie_len;
569 if (memcmp(wdev->current_bss->pub.bssid, bssid, ETH_ALEN) == 0)
570 req.bss = &wdev->current_bss->pub;
571 else
572 return -ENOTCONN;
573
Johannes Berg667503d2009-07-07 03:56:11 +0200574 return rdev->ops->disassoc(&rdev->wiphy, dev, &req, wdev);
575}
576
577int cfg80211_mlme_disassoc(struct cfg80211_registered_device *rdev,
578 struct net_device *dev, const u8 *bssid,
579 const u8 *ie, int ie_len, u16 reason)
580{
581 struct wireless_dev *wdev = dev->ieee80211_ptr;
582 int err;
583
584 wdev_lock(wdev);
585 err = __cfg80211_mlme_disassoc(rdev, dev, bssid, ie, ie_len, reason);
586 wdev_unlock(wdev);
587
588 return err;
Johannes Berg19957bb2009-07-02 17:20:43 +0200589}
590
591void cfg80211_mlme_down(struct cfg80211_registered_device *rdev,
592 struct net_device *dev)
593{
594 struct wireless_dev *wdev = dev->ieee80211_ptr;
595 struct cfg80211_deauth_request req;
596 int i;
597
Johannes Berg667503d2009-07-07 03:56:11 +0200598 ASSERT_WDEV_LOCK(wdev);
599
Johannes Berg19957bb2009-07-02 17:20:43 +0200600 if (!rdev->ops->deauth)
601 return;
602
603 memset(&req, 0, sizeof(req));
604 req.reason_code = WLAN_REASON_DEAUTH_LEAVING;
605 req.ie = NULL;
606 req.ie_len = 0;
607
608 if (wdev->current_bss) {
609 req.bss = &wdev->current_bss->pub;
Johannes Berg667503d2009-07-07 03:56:11 +0200610 rdev->ops->deauth(&rdev->wiphy, dev, &req, wdev);
Johannes Berg19957bb2009-07-02 17:20:43 +0200611 if (wdev->current_bss) {
612 cfg80211_unhold_bss(wdev->current_bss);
613 cfg80211_put_bss(&wdev->current_bss->pub);
614 wdev->current_bss = NULL;
615 }
616 }
617
618 for (i = 0; i < MAX_AUTH_BSSES; i++) {
619 if (wdev->auth_bsses[i]) {
620 req.bss = &wdev->auth_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->auth_bsses[i]) {
623 cfg80211_unhold_bss(wdev->auth_bsses[i]);
624 cfg80211_put_bss(&wdev->auth_bsses[i]->pub);
625 wdev->auth_bsses[i] = NULL;
626 }
627 }
628 if (wdev->authtry_bsses[i]) {
629 req.bss = &wdev->authtry_bsses[i]->pub;
Johannes Berg667503d2009-07-07 03:56:11 +0200630 rdev->ops->deauth(&rdev->wiphy, dev, &req, wdev);
Johannes Berg19957bb2009-07-02 17:20:43 +0200631 if (wdev->authtry_bsses[i]) {
632 cfg80211_unhold_bss(wdev->authtry_bsses[i]);
633 cfg80211_put_bss(&wdev->authtry_bsses[i]->pub);
634 wdev->authtry_bsses[i] = NULL;
635 }
636 }
637 }
638}