blob: 82e6002c8d678654f2cdfd7c88baa4ee53586a0b [file] [log] [blame]
Jouni Malinen6039f6d2009-03-19 13:39:21 +02001/*
2 * cfg80211 MLME SAP interface
3 *
4 * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
5 */
6
7#include <linux/kernel.h>
8#include <linux/module.h>
9#include <linux/netdevice.h>
10#include <linux/nl80211.h>
Johannes Berga9a11622009-07-27 12:01:53 +020011#include <linux/wireless.h>
Jouni Malinen6039f6d2009-03-19 13:39:21 +020012#include <net/cfg80211.h>
Johannes Berga9a11622009-07-27 12:01:53 +020013#include <net/iw_handler.h>
Jouni Malinen6039f6d2009-03-19 13:39:21 +020014#include "core.h"
15#include "nl80211.h"
16
Johannes Bergcb0b4be2009-07-07 03:56:07 +020017void cfg80211_send_rx_auth(struct net_device *dev, const u8 *buf, size_t len)
Jouni Malinen6039f6d2009-03-19 13:39:21 +020018{
Johannes Berg19957bb2009-07-02 17:20:43 +020019 struct wireless_dev *wdev = dev->ieee80211_ptr;
20 struct wiphy *wiphy = wdev->wiphy;
Jouni Malinen6039f6d2009-03-19 13:39:21 +020021 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
Johannes Berg19957bb2009-07-02 17:20:43 +020022 struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf;
23 u8 *bssid = mgmt->bssid;
24 int i;
25 u16 status = le16_to_cpu(mgmt->u.auth.status_code);
26 bool done = false;
27
Johannes Berg667503dd2009-07-07 03:56:11 +020028 wdev_lock(wdev);
Johannes Bergcb0b4be2009-07-07 03:56:07 +020029
Johannes Berg19957bb2009-07-02 17:20:43 +020030 for (i = 0; i < MAX_AUTH_BSSES; i++) {
31 if (wdev->authtry_bsses[i] &&
32 memcmp(wdev->authtry_bsses[i]->pub.bssid, bssid,
33 ETH_ALEN) == 0) {
34 if (status == WLAN_STATUS_SUCCESS) {
35 wdev->auth_bsses[i] = wdev->authtry_bsses[i];
36 } else {
37 cfg80211_unhold_bss(wdev->authtry_bsses[i]);
38 cfg80211_put_bss(&wdev->authtry_bsses[i]->pub);
39 }
40 wdev->authtry_bsses[i] = NULL;
41 done = true;
42 break;
43 }
44 }
45
46 WARN_ON(!done);
Johannes Berg6829c872009-07-02 09:13:27 +020047
Johannes Bergcb0b4be2009-07-07 03:56:07 +020048 nl80211_send_rx_auth(rdev, dev, buf, len, GFP_KERNEL);
Johannes Berg6829c872009-07-02 09:13:27 +020049 cfg80211_sme_rx_auth(dev, buf, len);
Johannes Berg667503dd2009-07-07 03:56:11 +020050
51 wdev_unlock(wdev);
Jouni Malinen6039f6d2009-03-19 13:39:21 +020052}
53EXPORT_SYMBOL(cfg80211_send_rx_auth);
54
Johannes Bergcb0b4be2009-07-07 03:56:07 +020055void cfg80211_send_rx_assoc(struct net_device *dev, const u8 *buf, size_t len)
Jouni Malinen6039f6d2009-03-19 13:39:21 +020056{
Johannes Berg6829c872009-07-02 09:13:27 +020057 u16 status_code;
58 struct wireless_dev *wdev = dev->ieee80211_ptr;
59 struct wiphy *wiphy = wdev->wiphy;
Jouni Malinen6039f6d2009-03-19 13:39:21 +020060 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
Johannes Berg6829c872009-07-02 09:13:27 +020061 struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf;
62 u8 *ie = mgmt->u.assoc_resp.variable;
Johannes Berg19957bb2009-07-02 17:20:43 +020063 int i, ieoffs = offsetof(struct ieee80211_mgmt, u.assoc_resp.variable);
Johannes Bergdf7fc0f2009-07-29 11:23:49 +020064 struct cfg80211_internal_bss *bss = NULL;
Johannes Berg6829c872009-07-02 09:13:27 +020065
Johannes Berg667503dd2009-07-07 03:56:11 +020066 wdev_lock(wdev);
Johannes Bergcb0b4be2009-07-07 03:56:07 +020067
Johannes Berg6829c872009-07-02 09:13:27 +020068 status_code = le16_to_cpu(mgmt->u.assoc_resp.status_code);
69
Johannes Bergf401a6f2009-08-07 14:51:05 +020070 /*
71 * This is a bit of a hack, we don't notify userspace of
72 * a (re-)association reply if we tried to send a reassoc
73 * and got a reject -- we only try again with an assoc
74 * frame instead of reassoc.
75 */
76 if (status_code != WLAN_STATUS_SUCCESS && wdev->conn &&
77 cfg80211_sme_failed_reassoc(wdev))
78 goto out;
79
Johannes Bergcb0b4be2009-07-07 03:56:07 +020080 nl80211_send_rx_assoc(rdev, dev, buf, len, GFP_KERNEL);
Johannes Berg6829c872009-07-02 09:13:27 +020081
Johannes Berg19957bb2009-07-02 17:20:43 +020082 if (status_code == WLAN_STATUS_SUCCESS) {
Johannes Bergdf7fc0f2009-07-29 11:23:49 +020083 for (i = 0; i < MAX_AUTH_BSSES; i++) {
84 if (!wdev->auth_bsses[i])
85 continue;
86 if (memcmp(wdev->auth_bsses[i]->pub.bssid, mgmt->bssid,
87 ETH_ALEN) == 0) {
88 bss = wdev->auth_bsses[i];
Johannes Berg19957bb2009-07-02 17:20:43 +020089 wdev->auth_bsses[i] = NULL;
Johannes Bergdf7fc0f2009-07-29 11:23:49 +020090 /* additional reference to drop hold */
91 cfg80211_ref_bss(bss);
Johannes Berg19957bb2009-07-02 17:20:43 +020092 break;
93 }
94 }
95
Johannes Berg3bdb2d42009-12-23 13:12:05 +010096 /*
97 * We might be coming here because the driver reported
98 * a successful association at the same time as the
99 * user requested a deauth. In that case, we will have
100 * removed the BSS from the auth_bsses list due to the
101 * deauth request when the assoc response makes it. If
102 * the two code paths acquire the lock the other way
103 * around, that's just the standard situation of a
104 * deauth being requested while connected.
105 */
106 if (!bss)
107 goto out;
Johannes Berg7d930bc2009-10-20 15:08:53 +0900108 } else if (wdev->conn) {
109 cfg80211_sme_failed_assoc(wdev);
Johannes Berg7d930bc2009-10-20 15:08:53 +0900110 /*
111 * do not call connect_result() now because the
112 * sme will schedule work that does it later.
113 */
114 goto out;
Johannes Bergdf7fc0f2009-07-29 11:23:49 +0200115 }
116
Johannes Bergea416a72009-08-17 12:22:14 +0200117 if (!wdev->conn && wdev->sme_state == CFG80211_SME_IDLE) {
118 /*
119 * This is for the userspace SME, the CONNECTING
120 * state will be changed to CONNECTED by
121 * __cfg80211_connect_result() below.
122 */
123 wdev->sme_state = CFG80211_SME_CONNECTING;
124 }
125
Johannes Bergdf7fc0f2009-07-29 11:23:49 +0200126 /* this consumes one bss reference (unless bss is NULL) */
127 __cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, ie, len - ieoffs,
128 status_code,
129 status_code == WLAN_STATUS_SUCCESS,
130 bss ? &bss->pub : NULL);
131 /* drop hold now, and also reference acquired above */
132 if (bss) {
133 cfg80211_unhold_bss(bss);
134 cfg80211_put_bss(&bss->pub);
Johannes Berg19957bb2009-07-02 17:20:43 +0200135 }
Johannes Berg667503dd2009-07-07 03:56:11 +0200136
Johannes Bergf401a6f2009-08-07 14:51:05 +0200137 out:
Johannes Berg667503dd2009-07-07 03:56:11 +0200138 wdev_unlock(wdev);
Jouni Malinen6039f6d2009-03-19 13:39:21 +0200139}
140EXPORT_SYMBOL(cfg80211_send_rx_assoc);
141
Holger Schurigce470612009-10-13 13:28:13 +0200142void __cfg80211_send_deauth(struct net_device *dev,
Johannes Berg667503dd2009-07-07 03:56:11 +0200143 const u8 *buf, size_t len)
Jouni Malinen6039f6d2009-03-19 13:39:21 +0200144{
Johannes Berg6829c872009-07-02 09:13:27 +0200145 struct wireless_dev *wdev = dev->ieee80211_ptr;
146 struct wiphy *wiphy = wdev->wiphy;
Jouni Malinen6039f6d2009-03-19 13:39:21 +0200147 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
Johannes Berg6829c872009-07-02 09:13:27 +0200148 struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf;
Johannes Berg19957bb2009-07-02 17:20:43 +0200149 const u8 *bssid = mgmt->bssid;
150 int i;
Johannes Berg6829c872009-07-02 09:13:27 +0200151
Johannes Berg667503dd2009-07-07 03:56:11 +0200152 ASSERT_WDEV_LOCK(wdev);
Johannes Bergcb0b4be2009-07-07 03:56:07 +0200153
154 nl80211_send_deauth(rdev, dev, buf, len, GFP_KERNEL);
Johannes Berg6829c872009-07-02 09:13:27 +0200155
Johannes Berg19957bb2009-07-02 17:20:43 +0200156 if (wdev->current_bss &&
157 memcmp(wdev->current_bss->pub.bssid, bssid, ETH_ALEN) == 0) {
Johannes Berg19957bb2009-07-02 17:20:43 +0200158 cfg80211_unhold_bss(wdev->current_bss);
159 cfg80211_put_bss(&wdev->current_bss->pub);
160 wdev->current_bss = NULL;
161 } else for (i = 0; i < MAX_AUTH_BSSES; i++) {
162 if (wdev->auth_bsses[i] &&
163 memcmp(wdev->auth_bsses[i]->pub.bssid, bssid, ETH_ALEN) == 0) {
164 cfg80211_unhold_bss(wdev->auth_bsses[i]);
165 cfg80211_put_bss(&wdev->auth_bsses[i]->pub);
166 wdev->auth_bsses[i] = NULL;
Johannes Berg19957bb2009-07-02 17:20:43 +0200167 break;
168 }
169 if (wdev->authtry_bsses[i] &&
170 memcmp(wdev->authtry_bsses[i]->pub.bssid, bssid, ETH_ALEN) == 0) {
171 cfg80211_unhold_bss(wdev->authtry_bsses[i]);
172 cfg80211_put_bss(&wdev->authtry_bsses[i]->pub);
173 wdev->authtry_bsses[i] = NULL;
Johannes Berg19957bb2009-07-02 17:20:43 +0200174 break;
175 }
176 }
Johannes Berg19957bb2009-07-02 17:20:43 +0200177
Johannes Berg6829c872009-07-02 09:13:27 +0200178 if (wdev->sme_state == CFG80211_SME_CONNECTED) {
179 u16 reason_code;
180 bool from_ap;
181
182 reason_code = le16_to_cpu(mgmt->u.deauth.reason_code);
183
Johannes Berge458b8a2009-08-06 20:41:33 +0200184 from_ap = memcmp(mgmt->sa, dev->dev_addr, ETH_ALEN) != 0;
Johannes Berg667503dd2009-07-07 03:56:11 +0200185 __cfg80211_disconnected(dev, NULL, 0, reason_code, from_ap);
Johannes Berg6829c872009-07-02 09:13:27 +0200186 } else if (wdev->sme_state == CFG80211_SME_CONNECTING) {
Johannes Berg667503dd2009-07-07 03:56:11 +0200187 __cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, NULL, 0,
188 WLAN_STATUS_UNSPECIFIED_FAILURE,
Johannes Bergdf7fc0f2009-07-29 11:23:49 +0200189 false, NULL);
Johannes Berg667503dd2009-07-07 03:56:11 +0200190 }
191}
Holger Schurigce470612009-10-13 13:28:13 +0200192EXPORT_SYMBOL(__cfg80211_send_deauth);
Johannes Berg667503dd2009-07-07 03:56:11 +0200193
Holger Schurigce470612009-10-13 13:28:13 +0200194void cfg80211_send_deauth(struct net_device *dev, const u8 *buf, size_t len)
Johannes Berg667503dd2009-07-07 03:56:11 +0200195{
196 struct wireless_dev *wdev = dev->ieee80211_ptr;
197
Holger Schurigce470612009-10-13 13:28:13 +0200198 wdev_lock(wdev);
199 __cfg80211_send_deauth(dev, buf, len);
200 wdev_unlock(wdev);
Jouni Malinen6039f6d2009-03-19 13:39:21 +0200201}
Jouni Malinen53b46b82009-03-27 20:53:56 +0200202EXPORT_SYMBOL(cfg80211_send_deauth);
Jouni Malinen6039f6d2009-03-19 13:39:21 +0200203
Holger Schurigce470612009-10-13 13:28:13 +0200204void __cfg80211_send_disassoc(struct net_device *dev,
Johannes Berg667503dd2009-07-07 03:56:11 +0200205 const u8 *buf, size_t len)
Jouni Malinen6039f6d2009-03-19 13:39:21 +0200206{
Johannes Berg6829c872009-07-02 09:13:27 +0200207 struct wireless_dev *wdev = dev->ieee80211_ptr;
208 struct wiphy *wiphy = wdev->wiphy;
Jouni Malinen6039f6d2009-03-19 13:39:21 +0200209 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
Johannes Berg6829c872009-07-02 09:13:27 +0200210 struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf;
Johannes Berg19957bb2009-07-02 17:20:43 +0200211 const u8 *bssid = mgmt->bssid;
212 int i;
213 u16 reason_code;
214 bool from_ap;
215 bool done = false;
Johannes Berg6829c872009-07-02 09:13:27 +0200216
Johannes Berg596a07c2009-07-11 00:17:32 +0200217 ASSERT_WDEV_LOCK(wdev);
Johannes Bergcb0b4be2009-07-07 03:56:07 +0200218
219 nl80211_send_disassoc(rdev, dev, buf, len, GFP_KERNEL);
Johannes Berg6829c872009-07-02 09:13:27 +0200220
Johannes Berg596a07c2009-07-11 00:17:32 +0200221 if (wdev->sme_state != CFG80211_SME_CONNECTED)
222 return;
Johannes Berg6829c872009-07-02 09:13:27 +0200223
Johannes Berg19957bb2009-07-02 17:20:43 +0200224 if (wdev->current_bss &&
Pavel Roskinb935df02009-08-06 04:52:42 -0400225 memcmp(wdev->current_bss->pub.bssid, bssid, ETH_ALEN) == 0) {
Johannes Berg19957bb2009-07-02 17:20:43 +0200226 for (i = 0; i < MAX_AUTH_BSSES; i++) {
227 if (wdev->authtry_bsses[i] || wdev->auth_bsses[i])
228 continue;
229 wdev->auth_bsses[i] = wdev->current_bss;
230 wdev->current_bss = NULL;
231 done = true;
232 cfg80211_sme_disassoc(dev, i);
233 break;
234 }
235 WARN_ON(!done);
236 } else
237 WARN_ON(1);
Johannes Berg6829c872009-07-02 09:13:27 +0200238
Johannes Berg6829c872009-07-02 09:13:27 +0200239
Johannes Berg19957bb2009-07-02 17:20:43 +0200240 reason_code = le16_to_cpu(mgmt->u.disassoc.reason_code);
241
Johannes Berge458b8a2009-08-06 20:41:33 +0200242 from_ap = memcmp(mgmt->sa, dev->dev_addr, ETH_ALEN) != 0;
Johannes Berg667503dd2009-07-07 03:56:11 +0200243 __cfg80211_disconnected(dev, NULL, 0, reason_code, from_ap);
Johannes Berg667503dd2009-07-07 03:56:11 +0200244}
Holger Schurigce470612009-10-13 13:28:13 +0200245EXPORT_SYMBOL(__cfg80211_send_disassoc);
Johannes Berg667503dd2009-07-07 03:56:11 +0200246
Holger Schurigce470612009-10-13 13:28:13 +0200247void cfg80211_send_disassoc(struct net_device *dev, const u8 *buf, size_t len)
Johannes Berg667503dd2009-07-07 03:56:11 +0200248{
249 struct wireless_dev *wdev = dev->ieee80211_ptr;
250
Holger Schurigce470612009-10-13 13:28:13 +0200251 wdev_lock(wdev);
252 __cfg80211_send_disassoc(dev, buf, len);
253 wdev_unlock(wdev);
Jouni Malinen6039f6d2009-03-19 13:39:21 +0200254}
Jouni Malinen53b46b82009-03-27 20:53:56 +0200255EXPORT_SYMBOL(cfg80211_send_disassoc);
Jouni Malinena3b8b052009-03-27 21:59:49 +0200256
Johannes Berga58ce432009-11-19 12:45:42 +0100257static void __cfg80211_auth_remove(struct wireless_dev *wdev, const u8 *addr)
Jouni Malinen1965c852009-04-22 21:38:25 +0300258{
Johannes Berg19957bb2009-07-02 17:20:43 +0200259 int i;
260 bool done = false;
261
Johannes Berga58ce432009-11-19 12:45:42 +0100262 ASSERT_WDEV_LOCK(wdev);
Johannes Berg19957bb2009-07-02 17:20:43 +0200263
264 for (i = 0; addr && i < MAX_AUTH_BSSES; i++) {
265 if (wdev->authtry_bsses[i] &&
266 memcmp(wdev->authtry_bsses[i]->pub.bssid,
267 addr, ETH_ALEN) == 0) {
268 cfg80211_unhold_bss(wdev->authtry_bsses[i]);
269 cfg80211_put_bss(&wdev->authtry_bsses[i]->pub);
270 wdev->authtry_bsses[i] = NULL;
271 done = true;
272 break;
273 }
274 }
275
276 WARN_ON(!done);
Johannes Berga58ce432009-11-19 12:45:42 +0100277}
278
279void __cfg80211_auth_canceled(struct net_device *dev, const u8 *addr)
280{
281 __cfg80211_auth_remove(dev->ieee80211_ptr, addr);
282}
283EXPORT_SYMBOL(__cfg80211_auth_canceled);
284
285void cfg80211_send_auth_timeout(struct net_device *dev, const u8 *addr)
286{
287 struct wireless_dev *wdev = dev->ieee80211_ptr;
288 struct wiphy *wiphy = wdev->wiphy;
289 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
290
291 wdev_lock(wdev);
292
293 nl80211_send_auth_timeout(rdev, dev, addr, GFP_KERNEL);
294 if (wdev->sme_state == CFG80211_SME_CONNECTING)
295 __cfg80211_connect_result(dev, addr, NULL, 0, NULL, 0,
296 WLAN_STATUS_UNSPECIFIED_FAILURE,
297 false, NULL);
298
299 __cfg80211_auth_remove(wdev, addr);
Johannes Berg667503dd2009-07-07 03:56:11 +0200300
301 wdev_unlock(wdev);
Jouni Malinen1965c852009-04-22 21:38:25 +0300302}
303EXPORT_SYMBOL(cfg80211_send_auth_timeout);
304
Johannes Bergcb0b4be2009-07-07 03:56:07 +0200305void cfg80211_send_assoc_timeout(struct net_device *dev, const u8 *addr)
Jouni Malinen1965c852009-04-22 21:38:25 +0300306{
Johannes Berg6829c872009-07-02 09:13:27 +0200307 struct wireless_dev *wdev = dev->ieee80211_ptr;
308 struct wiphy *wiphy = wdev->wiphy;
Jouni Malinen1965c852009-04-22 21:38:25 +0300309 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
Johannes Berg19957bb2009-07-02 17:20:43 +0200310 int i;
311 bool done = false;
312
Johannes Berg667503dd2009-07-07 03:56:11 +0200313 wdev_lock(wdev);
Johannes Bergcb0b4be2009-07-07 03:56:07 +0200314
315 nl80211_send_assoc_timeout(rdev, dev, addr, GFP_KERNEL);
Johannes Berg6829c872009-07-02 09:13:27 +0200316 if (wdev->sme_state == CFG80211_SME_CONNECTING)
Johannes Berg667503dd2009-07-07 03:56:11 +0200317 __cfg80211_connect_result(dev, addr, NULL, 0, NULL, 0,
318 WLAN_STATUS_UNSPECIFIED_FAILURE,
Johannes Bergdf7fc0f2009-07-29 11:23:49 +0200319 false, NULL);
Johannes Berg19957bb2009-07-02 17:20:43 +0200320
321 for (i = 0; addr && i < MAX_AUTH_BSSES; i++) {
322 if (wdev->auth_bsses[i] &&
323 memcmp(wdev->auth_bsses[i]->pub.bssid,
324 addr, ETH_ALEN) == 0) {
325 cfg80211_unhold_bss(wdev->auth_bsses[i]);
326 cfg80211_put_bss(&wdev->auth_bsses[i]->pub);
327 wdev->auth_bsses[i] = NULL;
328 done = true;
329 break;
330 }
331 }
332
333 WARN_ON(!done);
Johannes Berg667503dd2009-07-07 03:56:11 +0200334
335 wdev_unlock(wdev);
Jouni Malinen1965c852009-04-22 21:38:25 +0300336}
337EXPORT_SYMBOL(cfg80211_send_assoc_timeout);
338
Jouni Malinena3b8b052009-03-27 21:59:49 +0200339void cfg80211_michael_mic_failure(struct net_device *dev, const u8 *addr,
340 enum nl80211_key_type key_type, int key_id,
Johannes Berge6d6e342009-07-01 21:26:47 +0200341 const u8 *tsc, gfp_t gfp)
Jouni Malinena3b8b052009-03-27 21:59:49 +0200342{
343 struct wiphy *wiphy = dev->ieee80211_ptr->wiphy;
344 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
Johannes Berg3d23e342009-09-29 23:27:28 +0200345#ifdef CONFIG_CFG80211_WEXT
Johannes Bergf58d4ed2009-06-19 02:45:21 +0200346 union iwreq_data wrqu;
Johannes Berge6d6e342009-07-01 21:26:47 +0200347 char *buf = kmalloc(128, gfp);
Johannes Bergf58d4ed2009-06-19 02:45:21 +0200348
349 if (buf) {
350 sprintf(buf, "MLME-MICHAELMICFAILURE.indication("
351 "keyid=%d %scast addr=%pM)", key_id,
352 key_type == NL80211_KEYTYPE_GROUP ? "broad" : "uni",
353 addr);
354 memset(&wrqu, 0, sizeof(wrqu));
355 wrqu.data.length = strlen(buf);
356 wireless_send_event(dev, IWEVCUSTOM, &wrqu, buf);
357 kfree(buf);
358 }
359#endif
360
Johannes Berge6d6e342009-07-01 21:26:47 +0200361 nl80211_michael_mic_failure(rdev, dev, addr, key_type, key_id, tsc, gfp);
Jouni Malinena3b8b052009-03-27 21:59:49 +0200362}
363EXPORT_SYMBOL(cfg80211_michael_mic_failure);
Johannes Berg19957bb2009-07-02 17:20:43 +0200364
365/* some MLME handling for userspace SME */
Johannes Berg667503dd2009-07-07 03:56:11 +0200366int __cfg80211_mlme_auth(struct cfg80211_registered_device *rdev,
367 struct net_device *dev,
368 struct ieee80211_channel *chan,
369 enum nl80211_auth_type auth_type,
370 const u8 *bssid,
371 const u8 *ssid, int ssid_len,
Johannes Bergfffd0932009-07-08 14:22:54 +0200372 const u8 *ie, int ie_len,
373 const u8 *key, int key_len, int key_idx)
Johannes Berg19957bb2009-07-02 17:20:43 +0200374{
375 struct wireless_dev *wdev = dev->ieee80211_ptr;
376 struct cfg80211_auth_request req;
377 struct cfg80211_internal_bss *bss;
378 int i, err, slot = -1, nfree = 0;
379
Johannes Berg667503dd2009-07-07 03:56:11 +0200380 ASSERT_WDEV_LOCK(wdev);
381
Johannes Bergfffd0932009-07-08 14:22:54 +0200382 if (auth_type == NL80211_AUTHTYPE_SHARED_KEY)
383 if (!key || !key_len || key_idx < 0 || key_idx > 4)
384 return -EINVAL;
385
Johannes Berg0a9b5e12009-07-02 18:26:18 +0200386 if (wdev->current_bss &&
387 memcmp(bssid, wdev->current_bss->pub.bssid, ETH_ALEN) == 0)
388 return -EALREADY;
389
390 for (i = 0; i < MAX_AUTH_BSSES; i++) {
391 if (wdev->authtry_bsses[i] &&
392 memcmp(bssid, wdev->authtry_bsses[i]->pub.bssid,
393 ETH_ALEN) == 0)
394 return -EALREADY;
395 if (wdev->auth_bsses[i] &&
396 memcmp(bssid, wdev->auth_bsses[i]->pub.bssid,
397 ETH_ALEN) == 0)
398 return -EALREADY;
399 }
400
Johannes Berg19957bb2009-07-02 17:20:43 +0200401 memset(&req, 0, sizeof(req));
402
403 req.ie = ie;
404 req.ie_len = ie_len;
405 req.auth_type = auth_type;
406 req.bss = cfg80211_get_bss(&rdev->wiphy, chan, bssid, ssid, ssid_len,
407 WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS);
Johannes Bergfffd0932009-07-08 14:22:54 +0200408 req.key = key;
409 req.key_len = key_len;
410 req.key_idx = key_idx;
Johannes Berg19957bb2009-07-02 17:20:43 +0200411 if (!req.bss)
412 return -ENOENT;
413
414 bss = bss_from_pub(req.bss);
415
416 for (i = 0; i < MAX_AUTH_BSSES; i++) {
Johannes Berg19957bb2009-07-02 17:20:43 +0200417 if (!wdev->auth_bsses[i] && !wdev->authtry_bsses[i]) {
418 slot = i;
419 nfree++;
420 }
421 }
422
423 /* we need one free slot for disassoc and one for this auth */
424 if (nfree < 2) {
425 err = -ENOSPC;
426 goto out;
427 }
428
429 wdev->authtry_bsses[slot] = bss;
430 cfg80211_hold_bss(bss);
431
432 err = rdev->ops->auth(&rdev->wiphy, dev, &req);
433 if (err) {
434 wdev->authtry_bsses[slot] = NULL;
435 cfg80211_unhold_bss(bss);
436 }
437
438 out:
439 if (err)
440 cfg80211_put_bss(req.bss);
441 return err;
442}
443
Johannes Berg667503dd2009-07-07 03:56:11 +0200444int cfg80211_mlme_auth(struct cfg80211_registered_device *rdev,
445 struct net_device *dev, struct ieee80211_channel *chan,
446 enum nl80211_auth_type auth_type, const u8 *bssid,
447 const u8 *ssid, int ssid_len,
Johannes Bergfffd0932009-07-08 14:22:54 +0200448 const u8 *ie, int ie_len,
449 const u8 *key, int key_len, int key_idx)
Johannes Berg667503dd2009-07-07 03:56:11 +0200450{
451 int err;
452
453 wdev_lock(dev->ieee80211_ptr);
454 err = __cfg80211_mlme_auth(rdev, dev, chan, auth_type, bssid,
Johannes Bergfffd0932009-07-08 14:22:54 +0200455 ssid, ssid_len, ie, ie_len,
456 key, key_len, key_idx);
Johannes Berg667503dd2009-07-07 03:56:11 +0200457 wdev_unlock(dev->ieee80211_ptr);
458
459 return err;
460}
461
462int __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)
Johannes Berg19957bb2009-07-02 17:20:43 +0200469{
470 struct wireless_dev *wdev = dev->ieee80211_ptr;
471 struct cfg80211_assoc_request req;
472 struct cfg80211_internal_bss *bss;
473 int i, err, slot = -1;
Jouni Malinen24b6b152009-11-17 21:35:38 +0200474 bool was_connected = false;
Johannes Berg19957bb2009-07-02 17:20:43 +0200475
Johannes Berg667503dd2009-07-07 03:56:11 +0200476 ASSERT_WDEV_LOCK(wdev);
477
Johannes Berg19957bb2009-07-02 17:20:43 +0200478 memset(&req, 0, sizeof(req));
479
Jouni Malinen24b6b152009-11-17 21:35:38 +0200480 if (wdev->current_bss && prev_bssid &&
481 memcmp(wdev->current_bss->pub.bssid, prev_bssid, ETH_ALEN) == 0) {
482 /*
483 * Trying to reassociate: Allow this to proceed and let the old
484 * association to be dropped when the new one is completed.
485 */
486 if (wdev->sme_state == CFG80211_SME_CONNECTED) {
487 was_connected = true;
488 wdev->sme_state = CFG80211_SME_CONNECTING;
489 }
490 } else if (wdev->current_bss)
Johannes Berg19957bb2009-07-02 17:20:43 +0200491 return -EALREADY;
492
493 req.ie = ie;
494 req.ie_len = ie_len;
495 memcpy(&req.crypto, crypt, sizeof(req.crypto));
496 req.use_mfp = use_mfp;
Johannes Berg3e5d7642009-07-07 14:37:26 +0200497 req.prev_bssid = prev_bssid;
Johannes Berg19957bb2009-07-02 17:20:43 +0200498 req.bss = cfg80211_get_bss(&rdev->wiphy, chan, bssid, ssid, ssid_len,
499 WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS);
Jouni Malinen24b6b152009-11-17 21:35:38 +0200500 if (!req.bss) {
501 if (was_connected)
502 wdev->sme_state = CFG80211_SME_CONNECTED;
Johannes Berg19957bb2009-07-02 17:20:43 +0200503 return -ENOENT;
Jouni Malinen24b6b152009-11-17 21:35:38 +0200504 }
Johannes Berg19957bb2009-07-02 17:20:43 +0200505
506 bss = bss_from_pub(req.bss);
507
508 for (i = 0; i < MAX_AUTH_BSSES; i++) {
509 if (bss == wdev->auth_bsses[i]) {
510 slot = i;
511 break;
512 }
513 }
514
515 if (slot < 0) {
516 err = -ENOTCONN;
517 goto out;
518 }
519
520 err = rdev->ops->assoc(&rdev->wiphy, dev, &req);
521 out:
Jouni Malinen24b6b152009-11-17 21:35:38 +0200522 if (err && was_connected)
523 wdev->sme_state = CFG80211_SME_CONNECTED;
Johannes Berg19957bb2009-07-02 17:20:43 +0200524 /* still a reference in wdev->auth_bsses[slot] */
525 cfg80211_put_bss(req.bss);
526 return err;
527}
528
Johannes Berg667503dd2009-07-07 03:56:11 +0200529int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev,
530 struct net_device *dev,
531 struct ieee80211_channel *chan,
532 const u8 *bssid, const u8 *prev_bssid,
533 const u8 *ssid, int ssid_len,
534 const u8 *ie, int ie_len, bool use_mfp,
535 struct cfg80211_crypto_settings *crypt)
536{
537 struct wireless_dev *wdev = dev->ieee80211_ptr;
538 int err;
539
540 wdev_lock(wdev);
541 err = __cfg80211_mlme_assoc(rdev, dev, chan, bssid, prev_bssid,
542 ssid, ssid_len, ie, ie_len, use_mfp, crypt);
543 wdev_unlock(wdev);
544
545 return err;
546}
547
548int __cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev,
549 struct net_device *dev, const u8 *bssid,
550 const u8 *ie, int ie_len, u16 reason)
Johannes Berg19957bb2009-07-02 17:20:43 +0200551{
552 struct wireless_dev *wdev = dev->ieee80211_ptr;
553 struct cfg80211_deauth_request req;
554 int i;
555
Johannes Berg667503dd2009-07-07 03:56:11 +0200556 ASSERT_WDEV_LOCK(wdev);
557
Johannes Berg19957bb2009-07-02 17:20:43 +0200558 memset(&req, 0, sizeof(req));
559 req.reason_code = reason;
560 req.ie = ie;
561 req.ie_len = ie_len;
562 if (wdev->current_bss &&
563 memcmp(wdev->current_bss->pub.bssid, bssid, ETH_ALEN) == 0) {
564 req.bss = &wdev->current_bss->pub;
565 } else for (i = 0; i < MAX_AUTH_BSSES; i++) {
566 if (wdev->auth_bsses[i] &&
567 memcmp(bssid, wdev->auth_bsses[i]->pub.bssid, ETH_ALEN) == 0) {
568 req.bss = &wdev->auth_bsses[i]->pub;
569 break;
570 }
571 if (wdev->authtry_bsses[i] &&
572 memcmp(bssid, wdev->authtry_bsses[i]->pub.bssid, ETH_ALEN) == 0) {
573 req.bss = &wdev->authtry_bsses[i]->pub;
574 break;
575 }
576 }
577
578 if (!req.bss)
579 return -ENOTCONN;
580
Johannes Berg667503dd2009-07-07 03:56:11 +0200581 return rdev->ops->deauth(&rdev->wiphy, dev, &req, wdev);
Johannes Berg19957bb2009-07-02 17:20:43 +0200582}
583
Johannes Berg667503dd2009-07-07 03:56:11 +0200584int cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev,
585 struct net_device *dev, const u8 *bssid,
586 const u8 *ie, int ie_len, u16 reason)
587{
588 struct wireless_dev *wdev = dev->ieee80211_ptr;
589 int err;
590
591 wdev_lock(wdev);
592 err = __cfg80211_mlme_deauth(rdev, dev, bssid, ie, ie_len, reason);
593 wdev_unlock(wdev);
594
595 return err;
596}
597
598static int __cfg80211_mlme_disassoc(struct cfg80211_registered_device *rdev,
599 struct net_device *dev, const u8 *bssid,
600 const u8 *ie, int ie_len, u16 reason)
Johannes Berg19957bb2009-07-02 17:20:43 +0200601{
602 struct wireless_dev *wdev = dev->ieee80211_ptr;
603 struct cfg80211_disassoc_request req;
604
Johannes Berg667503dd2009-07-07 03:56:11 +0200605 ASSERT_WDEV_LOCK(wdev);
606
Johannes Bergf9d6b402009-07-27 10:22:28 +0200607 if (wdev->sme_state != CFG80211_SME_CONNECTED)
608 return -ENOTCONN;
609
610 if (WARN_ON(!wdev->current_bss))
611 return -ENOTCONN;
612
Johannes Berg19957bb2009-07-02 17:20:43 +0200613 memset(&req, 0, sizeof(req));
614 req.reason_code = reason;
615 req.ie = ie;
616 req.ie_len = ie_len;
617 if (memcmp(wdev->current_bss->pub.bssid, bssid, ETH_ALEN) == 0)
618 req.bss = &wdev->current_bss->pub;
619 else
620 return -ENOTCONN;
621
Johannes Berg667503dd2009-07-07 03:56:11 +0200622 return rdev->ops->disassoc(&rdev->wiphy, dev, &req, wdev);
623}
624
625int cfg80211_mlme_disassoc(struct cfg80211_registered_device *rdev,
626 struct net_device *dev, const u8 *bssid,
627 const u8 *ie, int ie_len, u16 reason)
628{
629 struct wireless_dev *wdev = dev->ieee80211_ptr;
630 int err;
631
632 wdev_lock(wdev);
633 err = __cfg80211_mlme_disassoc(rdev, dev, bssid, ie, ie_len, reason);
634 wdev_unlock(wdev);
635
636 return err;
Johannes Berg19957bb2009-07-02 17:20:43 +0200637}
638
639void cfg80211_mlme_down(struct cfg80211_registered_device *rdev,
640 struct net_device *dev)
641{
642 struct wireless_dev *wdev = dev->ieee80211_ptr;
643 struct cfg80211_deauth_request req;
644 int i;
645
Johannes Berg667503dd2009-07-07 03:56:11 +0200646 ASSERT_WDEV_LOCK(wdev);
647
Johannes Berg19957bb2009-07-02 17:20:43 +0200648 if (!rdev->ops->deauth)
649 return;
650
651 memset(&req, 0, sizeof(req));
652 req.reason_code = WLAN_REASON_DEAUTH_LEAVING;
653 req.ie = NULL;
654 req.ie_len = 0;
655
656 if (wdev->current_bss) {
657 req.bss = &wdev->current_bss->pub;
Johannes Berg667503dd2009-07-07 03:56:11 +0200658 rdev->ops->deauth(&rdev->wiphy, dev, &req, wdev);
Johannes Berg19957bb2009-07-02 17:20:43 +0200659 if (wdev->current_bss) {
660 cfg80211_unhold_bss(wdev->current_bss);
661 cfg80211_put_bss(&wdev->current_bss->pub);
662 wdev->current_bss = NULL;
663 }
664 }
665
666 for (i = 0; i < MAX_AUTH_BSSES; i++) {
667 if (wdev->auth_bsses[i]) {
668 req.bss = &wdev->auth_bsses[i]->pub;
Johannes Berg667503dd2009-07-07 03:56:11 +0200669 rdev->ops->deauth(&rdev->wiphy, dev, &req, wdev);
Johannes Berg19957bb2009-07-02 17:20:43 +0200670 if (wdev->auth_bsses[i]) {
671 cfg80211_unhold_bss(wdev->auth_bsses[i]);
672 cfg80211_put_bss(&wdev->auth_bsses[i]->pub);
673 wdev->auth_bsses[i] = NULL;
674 }
675 }
676 if (wdev->authtry_bsses[i]) {
677 req.bss = &wdev->authtry_bsses[i]->pub;
Johannes Berg667503dd2009-07-07 03:56:11 +0200678 rdev->ops->deauth(&rdev->wiphy, dev, &req, wdev);
Johannes Berg19957bb2009-07-02 17:20:43 +0200679 if (wdev->authtry_bsses[i]) {
680 cfg80211_unhold_bss(wdev->authtry_bsses[i]);
681 cfg80211_put_bss(&wdev->authtry_bsses[i]->pub);
682 wdev->authtry_bsses[i] = NULL;
683 }
684 }
685 }
686}