blob: 62bc8855e1237db6b7d83f7a19c79e5c3976a0f7 [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 Berg5fba4af2009-12-02 12:43:42 +0100151 bool found = false;
Johannes Berg6829c872009-07-02 09:13:27 +0200152
Johannes Berg667503dd2009-07-07 03:56:11 +0200153 ASSERT_WDEV_LOCK(wdev);
Johannes Bergcb0b4be2009-07-07 03:56:07 +0200154
Johannes Berg19957bb2009-07-02 17:20:43 +0200155 if (wdev->current_bss &&
156 memcmp(wdev->current_bss->pub.bssid, bssid, ETH_ALEN) == 0) {
Johannes Berg19957bb2009-07-02 17:20:43 +0200157 cfg80211_unhold_bss(wdev->current_bss);
158 cfg80211_put_bss(&wdev->current_bss->pub);
159 wdev->current_bss = NULL;
Johannes Berg5fba4af2009-12-02 12:43:42 +0100160 found = true;
Johannes Berg19957bb2009-07-02 17:20:43 +0200161 } 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 Berg5fba4af2009-12-02 12:43:42 +0100167 found = true;
Johannes Berg19957bb2009-07-02 17:20:43 +0200168 break;
169 }
170 if (wdev->authtry_bsses[i] &&
171 memcmp(wdev->authtry_bsses[i]->pub.bssid, bssid, ETH_ALEN) == 0) {
172 cfg80211_unhold_bss(wdev->authtry_bsses[i]);
173 cfg80211_put_bss(&wdev->authtry_bsses[i]->pub);
174 wdev->authtry_bsses[i] = NULL;
Johannes Berg5fba4af2009-12-02 12:43:42 +0100175 found = true;
Johannes Berg19957bb2009-07-02 17:20:43 +0200176 break;
177 }
178 }
Johannes Berg19957bb2009-07-02 17:20:43 +0200179
Johannes Berg5fba4af2009-12-02 12:43:42 +0100180 if (!found)
181 return;
182
183 nl80211_send_deauth(rdev, dev, buf, len, GFP_KERNEL);
184
Johannes Berg6829c872009-07-02 09:13:27 +0200185 if (wdev->sme_state == CFG80211_SME_CONNECTED) {
186 u16 reason_code;
187 bool from_ap;
188
189 reason_code = le16_to_cpu(mgmt->u.deauth.reason_code);
190
Johannes Berge458b8a2009-08-06 20:41:33 +0200191 from_ap = memcmp(mgmt->sa, dev->dev_addr, ETH_ALEN) != 0;
Johannes Berg667503dd2009-07-07 03:56:11 +0200192 __cfg80211_disconnected(dev, NULL, 0, reason_code, from_ap);
Johannes Berg6829c872009-07-02 09:13:27 +0200193 } else if (wdev->sme_state == CFG80211_SME_CONNECTING) {
Johannes Berg667503dd2009-07-07 03:56:11 +0200194 __cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, NULL, 0,
195 WLAN_STATUS_UNSPECIFIED_FAILURE,
Johannes Bergdf7fc0f2009-07-29 11:23:49 +0200196 false, NULL);
Johannes Berg667503dd2009-07-07 03:56:11 +0200197 }
198}
Holger Schurigce470612009-10-13 13:28:13 +0200199EXPORT_SYMBOL(__cfg80211_send_deauth);
Johannes Berg667503dd2009-07-07 03:56:11 +0200200
Holger Schurigce470612009-10-13 13:28:13 +0200201void cfg80211_send_deauth(struct net_device *dev, const u8 *buf, size_t len)
Johannes Berg667503dd2009-07-07 03:56:11 +0200202{
203 struct wireless_dev *wdev = dev->ieee80211_ptr;
204
Holger Schurigce470612009-10-13 13:28:13 +0200205 wdev_lock(wdev);
206 __cfg80211_send_deauth(dev, buf, len);
207 wdev_unlock(wdev);
Jouni Malinen6039f6d2009-03-19 13:39:21 +0200208}
Jouni Malinen53b46b82009-03-27 20:53:56 +0200209EXPORT_SYMBOL(cfg80211_send_deauth);
Jouni Malinen6039f6d2009-03-19 13:39:21 +0200210
Holger Schurigce470612009-10-13 13:28:13 +0200211void __cfg80211_send_disassoc(struct net_device *dev,
Johannes Berg667503dd2009-07-07 03:56:11 +0200212 const u8 *buf, size_t len)
Jouni Malinen6039f6d2009-03-19 13:39:21 +0200213{
Johannes Berg6829c872009-07-02 09:13:27 +0200214 struct wireless_dev *wdev = dev->ieee80211_ptr;
215 struct wiphy *wiphy = wdev->wiphy;
Jouni Malinen6039f6d2009-03-19 13:39:21 +0200216 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
Johannes Berg6829c872009-07-02 09:13:27 +0200217 struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf;
Johannes Berg19957bb2009-07-02 17:20:43 +0200218 const u8 *bssid = mgmt->bssid;
219 int i;
220 u16 reason_code;
221 bool from_ap;
222 bool done = false;
Johannes Berg6829c872009-07-02 09:13:27 +0200223
Johannes Berg596a07c2009-07-11 00:17:32 +0200224 ASSERT_WDEV_LOCK(wdev);
Johannes Bergcb0b4be2009-07-07 03:56:07 +0200225
226 nl80211_send_disassoc(rdev, dev, buf, len, GFP_KERNEL);
Johannes Berg6829c872009-07-02 09:13:27 +0200227
Johannes Berg596a07c2009-07-11 00:17:32 +0200228 if (wdev->sme_state != CFG80211_SME_CONNECTED)
229 return;
Johannes Berg6829c872009-07-02 09:13:27 +0200230
Johannes Berg19957bb2009-07-02 17:20:43 +0200231 if (wdev->current_bss &&
Pavel Roskinb935df02009-08-06 04:52:42 -0400232 memcmp(wdev->current_bss->pub.bssid, bssid, ETH_ALEN) == 0) {
Johannes Berg19957bb2009-07-02 17:20:43 +0200233 for (i = 0; i < MAX_AUTH_BSSES; i++) {
234 if (wdev->authtry_bsses[i] || wdev->auth_bsses[i])
235 continue;
236 wdev->auth_bsses[i] = wdev->current_bss;
237 wdev->current_bss = NULL;
238 done = true;
239 cfg80211_sme_disassoc(dev, i);
240 break;
241 }
242 WARN_ON(!done);
243 } else
244 WARN_ON(1);
Johannes Berg6829c872009-07-02 09:13:27 +0200245
Johannes Berg6829c872009-07-02 09:13:27 +0200246
Johannes Berg19957bb2009-07-02 17:20:43 +0200247 reason_code = le16_to_cpu(mgmt->u.disassoc.reason_code);
248
Johannes Berge458b8a2009-08-06 20:41:33 +0200249 from_ap = memcmp(mgmt->sa, dev->dev_addr, ETH_ALEN) != 0;
Johannes Berg667503dd2009-07-07 03:56:11 +0200250 __cfg80211_disconnected(dev, NULL, 0, reason_code, from_ap);
Johannes Berg667503dd2009-07-07 03:56:11 +0200251}
Holger Schurigce470612009-10-13 13:28:13 +0200252EXPORT_SYMBOL(__cfg80211_send_disassoc);
Johannes Berg667503dd2009-07-07 03:56:11 +0200253
Holger Schurigce470612009-10-13 13:28:13 +0200254void cfg80211_send_disassoc(struct net_device *dev, const u8 *buf, size_t len)
Johannes Berg667503dd2009-07-07 03:56:11 +0200255{
256 struct wireless_dev *wdev = dev->ieee80211_ptr;
257
Holger Schurigce470612009-10-13 13:28:13 +0200258 wdev_lock(wdev);
259 __cfg80211_send_disassoc(dev, buf, len);
260 wdev_unlock(wdev);
Jouni Malinen6039f6d2009-03-19 13:39:21 +0200261}
Jouni Malinen53b46b82009-03-27 20:53:56 +0200262EXPORT_SYMBOL(cfg80211_send_disassoc);
Jouni Malinena3b8b052009-03-27 21:59:49 +0200263
Johannes Berga58ce432009-11-19 12:45:42 +0100264static void __cfg80211_auth_remove(struct wireless_dev *wdev, const u8 *addr)
Jouni Malinen1965c852009-04-22 21:38:25 +0300265{
Johannes Berg19957bb2009-07-02 17:20:43 +0200266 int i;
267 bool done = false;
268
Johannes Berga58ce432009-11-19 12:45:42 +0100269 ASSERT_WDEV_LOCK(wdev);
Johannes Berg19957bb2009-07-02 17:20:43 +0200270
271 for (i = 0; addr && i < MAX_AUTH_BSSES; i++) {
272 if (wdev->authtry_bsses[i] &&
273 memcmp(wdev->authtry_bsses[i]->pub.bssid,
274 addr, ETH_ALEN) == 0) {
275 cfg80211_unhold_bss(wdev->authtry_bsses[i]);
276 cfg80211_put_bss(&wdev->authtry_bsses[i]->pub);
277 wdev->authtry_bsses[i] = NULL;
278 done = true;
279 break;
280 }
281 }
282
283 WARN_ON(!done);
Johannes Berga58ce432009-11-19 12:45:42 +0100284}
285
286void __cfg80211_auth_canceled(struct net_device *dev, const u8 *addr)
287{
288 __cfg80211_auth_remove(dev->ieee80211_ptr, addr);
289}
290EXPORT_SYMBOL(__cfg80211_auth_canceled);
291
292void cfg80211_send_auth_timeout(struct net_device *dev, const u8 *addr)
293{
294 struct wireless_dev *wdev = dev->ieee80211_ptr;
295 struct wiphy *wiphy = wdev->wiphy;
296 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
297
298 wdev_lock(wdev);
299
300 nl80211_send_auth_timeout(rdev, dev, addr, GFP_KERNEL);
301 if (wdev->sme_state == CFG80211_SME_CONNECTING)
302 __cfg80211_connect_result(dev, addr, NULL, 0, NULL, 0,
303 WLAN_STATUS_UNSPECIFIED_FAILURE,
304 false, NULL);
305
306 __cfg80211_auth_remove(wdev, addr);
Johannes Berg667503dd2009-07-07 03:56:11 +0200307
308 wdev_unlock(wdev);
Jouni Malinen1965c852009-04-22 21:38:25 +0300309}
310EXPORT_SYMBOL(cfg80211_send_auth_timeout);
311
Johannes Bergcb0b4be2009-07-07 03:56:07 +0200312void cfg80211_send_assoc_timeout(struct net_device *dev, const u8 *addr)
Jouni Malinen1965c852009-04-22 21:38:25 +0300313{
Johannes Berg6829c872009-07-02 09:13:27 +0200314 struct wireless_dev *wdev = dev->ieee80211_ptr;
315 struct wiphy *wiphy = wdev->wiphy;
Jouni Malinen1965c852009-04-22 21:38:25 +0300316 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
Johannes Berg19957bb2009-07-02 17:20:43 +0200317 int i;
318 bool done = false;
319
Johannes Berg667503dd2009-07-07 03:56:11 +0200320 wdev_lock(wdev);
Johannes Bergcb0b4be2009-07-07 03:56:07 +0200321
322 nl80211_send_assoc_timeout(rdev, dev, addr, GFP_KERNEL);
Johannes Berg6829c872009-07-02 09:13:27 +0200323 if (wdev->sme_state == CFG80211_SME_CONNECTING)
Johannes Berg667503dd2009-07-07 03:56:11 +0200324 __cfg80211_connect_result(dev, addr, NULL, 0, NULL, 0,
325 WLAN_STATUS_UNSPECIFIED_FAILURE,
Johannes Bergdf7fc0f2009-07-29 11:23:49 +0200326 false, NULL);
Johannes Berg19957bb2009-07-02 17:20:43 +0200327
328 for (i = 0; addr && i < MAX_AUTH_BSSES; i++) {
329 if (wdev->auth_bsses[i] &&
330 memcmp(wdev->auth_bsses[i]->pub.bssid,
331 addr, ETH_ALEN) == 0) {
332 cfg80211_unhold_bss(wdev->auth_bsses[i]);
333 cfg80211_put_bss(&wdev->auth_bsses[i]->pub);
334 wdev->auth_bsses[i] = NULL;
335 done = true;
336 break;
337 }
338 }
339
340 WARN_ON(!done);
Johannes Berg667503dd2009-07-07 03:56:11 +0200341
342 wdev_unlock(wdev);
Jouni Malinen1965c852009-04-22 21:38:25 +0300343}
344EXPORT_SYMBOL(cfg80211_send_assoc_timeout);
345
Jouni Malinena3b8b052009-03-27 21:59:49 +0200346void cfg80211_michael_mic_failure(struct net_device *dev, const u8 *addr,
347 enum nl80211_key_type key_type, int key_id,
Johannes Berge6d6e342009-07-01 21:26:47 +0200348 const u8 *tsc, gfp_t gfp)
Jouni Malinena3b8b052009-03-27 21:59:49 +0200349{
350 struct wiphy *wiphy = dev->ieee80211_ptr->wiphy;
351 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
Johannes Berg3d23e342009-09-29 23:27:28 +0200352#ifdef CONFIG_CFG80211_WEXT
Johannes Bergf58d4ed2009-06-19 02:45:21 +0200353 union iwreq_data wrqu;
Johannes Berge6d6e342009-07-01 21:26:47 +0200354 char *buf = kmalloc(128, gfp);
Johannes Bergf58d4ed2009-06-19 02:45:21 +0200355
356 if (buf) {
357 sprintf(buf, "MLME-MICHAELMICFAILURE.indication("
358 "keyid=%d %scast addr=%pM)", key_id,
359 key_type == NL80211_KEYTYPE_GROUP ? "broad" : "uni",
360 addr);
361 memset(&wrqu, 0, sizeof(wrqu));
362 wrqu.data.length = strlen(buf);
363 wireless_send_event(dev, IWEVCUSTOM, &wrqu, buf);
364 kfree(buf);
365 }
366#endif
367
Johannes Berge6d6e342009-07-01 21:26:47 +0200368 nl80211_michael_mic_failure(rdev, dev, addr, key_type, key_id, tsc, gfp);
Jouni Malinena3b8b052009-03-27 21:59:49 +0200369}
370EXPORT_SYMBOL(cfg80211_michael_mic_failure);
Johannes Berg19957bb2009-07-02 17:20:43 +0200371
372/* some MLME handling for userspace SME */
Johannes Berg667503dd2009-07-07 03:56:11 +0200373int __cfg80211_mlme_auth(struct cfg80211_registered_device *rdev,
374 struct net_device *dev,
375 struct ieee80211_channel *chan,
376 enum nl80211_auth_type auth_type,
377 const u8 *bssid,
378 const u8 *ssid, int ssid_len,
Johannes Bergfffd0932009-07-08 14:22:54 +0200379 const u8 *ie, int ie_len,
380 const u8 *key, int key_len, int key_idx)
Johannes Berg19957bb2009-07-02 17:20:43 +0200381{
382 struct wireless_dev *wdev = dev->ieee80211_ptr;
383 struct cfg80211_auth_request req;
384 struct cfg80211_internal_bss *bss;
385 int i, err, slot = -1, nfree = 0;
386
Johannes Berg667503dd2009-07-07 03:56:11 +0200387 ASSERT_WDEV_LOCK(wdev);
388
Johannes Bergfffd0932009-07-08 14:22:54 +0200389 if (auth_type == NL80211_AUTHTYPE_SHARED_KEY)
390 if (!key || !key_len || key_idx < 0 || key_idx > 4)
391 return -EINVAL;
392
Johannes Berg0a9b5e12009-07-02 18:26:18 +0200393 if (wdev->current_bss &&
394 memcmp(bssid, wdev->current_bss->pub.bssid, ETH_ALEN) == 0)
395 return -EALREADY;
396
397 for (i = 0; i < MAX_AUTH_BSSES; i++) {
398 if (wdev->authtry_bsses[i] &&
399 memcmp(bssid, wdev->authtry_bsses[i]->pub.bssid,
400 ETH_ALEN) == 0)
401 return -EALREADY;
402 if (wdev->auth_bsses[i] &&
403 memcmp(bssid, wdev->auth_bsses[i]->pub.bssid,
404 ETH_ALEN) == 0)
405 return -EALREADY;
406 }
407
Johannes Berg19957bb2009-07-02 17:20:43 +0200408 memset(&req, 0, sizeof(req));
409
410 req.ie = ie;
411 req.ie_len = ie_len;
412 req.auth_type = auth_type;
413 req.bss = cfg80211_get_bss(&rdev->wiphy, chan, bssid, ssid, ssid_len,
414 WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS);
Johannes Bergfffd0932009-07-08 14:22:54 +0200415 req.key = key;
416 req.key_len = key_len;
417 req.key_idx = key_idx;
Johannes Berg19957bb2009-07-02 17:20:43 +0200418 if (!req.bss)
419 return -ENOENT;
420
421 bss = bss_from_pub(req.bss);
422
423 for (i = 0; i < MAX_AUTH_BSSES; i++) {
Johannes Berg19957bb2009-07-02 17:20:43 +0200424 if (!wdev->auth_bsses[i] && !wdev->authtry_bsses[i]) {
425 slot = i;
426 nfree++;
427 }
428 }
429
430 /* we need one free slot for disassoc and one for this auth */
431 if (nfree < 2) {
432 err = -ENOSPC;
433 goto out;
434 }
435
436 wdev->authtry_bsses[slot] = bss;
437 cfg80211_hold_bss(bss);
438
439 err = rdev->ops->auth(&rdev->wiphy, dev, &req);
440 if (err) {
441 wdev->authtry_bsses[slot] = NULL;
442 cfg80211_unhold_bss(bss);
443 }
444
445 out:
446 if (err)
447 cfg80211_put_bss(req.bss);
448 return err;
449}
450
Johannes Berg667503dd2009-07-07 03:56:11 +0200451int cfg80211_mlme_auth(struct cfg80211_registered_device *rdev,
452 struct net_device *dev, struct ieee80211_channel *chan,
453 enum nl80211_auth_type auth_type, const u8 *bssid,
454 const u8 *ssid, int ssid_len,
Johannes Bergfffd0932009-07-08 14:22:54 +0200455 const u8 *ie, int ie_len,
456 const u8 *key, int key_len, int key_idx)
Johannes Berg667503dd2009-07-07 03:56:11 +0200457{
458 int err;
459
460 wdev_lock(dev->ieee80211_ptr);
461 err = __cfg80211_mlme_auth(rdev, dev, chan, auth_type, bssid,
Johannes Bergfffd0932009-07-08 14:22:54 +0200462 ssid, ssid_len, ie, ie_len,
463 key, key_len, key_idx);
Johannes Berg667503dd2009-07-07 03:56:11 +0200464 wdev_unlock(dev->ieee80211_ptr);
465
466 return err;
467}
468
469int __cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev,
470 struct net_device *dev,
471 struct ieee80211_channel *chan,
472 const u8 *bssid, const u8 *prev_bssid,
473 const u8 *ssid, int ssid_len,
474 const u8 *ie, int ie_len, bool use_mfp,
475 struct cfg80211_crypto_settings *crypt)
Johannes Berg19957bb2009-07-02 17:20:43 +0200476{
477 struct wireless_dev *wdev = dev->ieee80211_ptr;
478 struct cfg80211_assoc_request req;
479 struct cfg80211_internal_bss *bss;
480 int i, err, slot = -1;
Jouni Malinen24b6b152009-11-17 21:35:38 +0200481 bool was_connected = false;
Johannes Berg19957bb2009-07-02 17:20:43 +0200482
Johannes Berg667503dd2009-07-07 03:56:11 +0200483 ASSERT_WDEV_LOCK(wdev);
484
Johannes Berg19957bb2009-07-02 17:20:43 +0200485 memset(&req, 0, sizeof(req));
486
Jouni Malinen24b6b152009-11-17 21:35:38 +0200487 if (wdev->current_bss && prev_bssid &&
488 memcmp(wdev->current_bss->pub.bssid, prev_bssid, ETH_ALEN) == 0) {
489 /*
490 * Trying to reassociate: Allow this to proceed and let the old
491 * association to be dropped when the new one is completed.
492 */
493 if (wdev->sme_state == CFG80211_SME_CONNECTED) {
494 was_connected = true;
495 wdev->sme_state = CFG80211_SME_CONNECTING;
496 }
497 } else if (wdev->current_bss)
Johannes Berg19957bb2009-07-02 17:20:43 +0200498 return -EALREADY;
499
500 req.ie = ie;
501 req.ie_len = ie_len;
502 memcpy(&req.crypto, crypt, sizeof(req.crypto));
503 req.use_mfp = use_mfp;
Johannes Berg3e5d7642009-07-07 14:37:26 +0200504 req.prev_bssid = prev_bssid;
Johannes Berg19957bb2009-07-02 17:20:43 +0200505 req.bss = cfg80211_get_bss(&rdev->wiphy, chan, bssid, ssid, ssid_len,
506 WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS);
Jouni Malinen24b6b152009-11-17 21:35:38 +0200507 if (!req.bss) {
508 if (was_connected)
509 wdev->sme_state = CFG80211_SME_CONNECTED;
Johannes Berg19957bb2009-07-02 17:20:43 +0200510 return -ENOENT;
Jouni Malinen24b6b152009-11-17 21:35:38 +0200511 }
Johannes Berg19957bb2009-07-02 17:20:43 +0200512
513 bss = bss_from_pub(req.bss);
514
515 for (i = 0; i < MAX_AUTH_BSSES; i++) {
516 if (bss == wdev->auth_bsses[i]) {
517 slot = i;
518 break;
519 }
520 }
521
522 if (slot < 0) {
523 err = -ENOTCONN;
524 goto out;
525 }
526
527 err = rdev->ops->assoc(&rdev->wiphy, dev, &req);
528 out:
Jouni Malinen24b6b152009-11-17 21:35:38 +0200529 if (err && was_connected)
530 wdev->sme_state = CFG80211_SME_CONNECTED;
Johannes Berg19957bb2009-07-02 17:20:43 +0200531 /* still a reference in wdev->auth_bsses[slot] */
532 cfg80211_put_bss(req.bss);
533 return err;
534}
535
Johannes Berg667503dd2009-07-07 03:56:11 +0200536int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev,
537 struct net_device *dev,
538 struct ieee80211_channel *chan,
539 const u8 *bssid, const u8 *prev_bssid,
540 const u8 *ssid, int ssid_len,
541 const u8 *ie, int ie_len, bool use_mfp,
542 struct cfg80211_crypto_settings *crypt)
543{
544 struct wireless_dev *wdev = dev->ieee80211_ptr;
545 int err;
546
547 wdev_lock(wdev);
548 err = __cfg80211_mlme_assoc(rdev, dev, chan, bssid, prev_bssid,
549 ssid, ssid_len, ie, ie_len, use_mfp, crypt);
550 wdev_unlock(wdev);
551
552 return err;
553}
554
555int __cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev,
556 struct net_device *dev, const u8 *bssid,
557 const u8 *ie, int ie_len, u16 reason)
Johannes Berg19957bb2009-07-02 17:20:43 +0200558{
559 struct wireless_dev *wdev = dev->ieee80211_ptr;
560 struct cfg80211_deauth_request req;
561 int i;
562
Johannes Berg667503dd2009-07-07 03:56:11 +0200563 ASSERT_WDEV_LOCK(wdev);
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 (wdev->current_bss &&
570 memcmp(wdev->current_bss->pub.bssid, bssid, ETH_ALEN) == 0) {
571 req.bss = &wdev->current_bss->pub;
572 } else for (i = 0; i < MAX_AUTH_BSSES; i++) {
573 if (wdev->auth_bsses[i] &&
574 memcmp(bssid, wdev->auth_bsses[i]->pub.bssid, ETH_ALEN) == 0) {
575 req.bss = &wdev->auth_bsses[i]->pub;
576 break;
577 }
578 if (wdev->authtry_bsses[i] &&
579 memcmp(bssid, wdev->authtry_bsses[i]->pub.bssid, ETH_ALEN) == 0) {
580 req.bss = &wdev->authtry_bsses[i]->pub;
581 break;
582 }
583 }
584
585 if (!req.bss)
586 return -ENOTCONN;
587
Johannes Berg667503dd2009-07-07 03:56:11 +0200588 return rdev->ops->deauth(&rdev->wiphy, dev, &req, wdev);
Johannes Berg19957bb2009-07-02 17:20:43 +0200589}
590
Johannes Berg667503dd2009-07-07 03:56:11 +0200591int cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev,
592 struct net_device *dev, const u8 *bssid,
593 const u8 *ie, int ie_len, u16 reason)
594{
595 struct wireless_dev *wdev = dev->ieee80211_ptr;
596 int err;
597
598 wdev_lock(wdev);
599 err = __cfg80211_mlme_deauth(rdev, dev, bssid, ie, ie_len, reason);
600 wdev_unlock(wdev);
601
602 return err;
603}
604
605static int __cfg80211_mlme_disassoc(struct cfg80211_registered_device *rdev,
606 struct net_device *dev, const u8 *bssid,
607 const u8 *ie, int ie_len, u16 reason)
Johannes Berg19957bb2009-07-02 17:20:43 +0200608{
609 struct wireless_dev *wdev = dev->ieee80211_ptr;
610 struct cfg80211_disassoc_request req;
611
Johannes Berg667503dd2009-07-07 03:56:11 +0200612 ASSERT_WDEV_LOCK(wdev);
613
Johannes Bergf9d6b402009-07-27 10:22:28 +0200614 if (wdev->sme_state != CFG80211_SME_CONNECTED)
615 return -ENOTCONN;
616
617 if (WARN_ON(!wdev->current_bss))
618 return -ENOTCONN;
619
Johannes Berg19957bb2009-07-02 17:20:43 +0200620 memset(&req, 0, sizeof(req));
621 req.reason_code = reason;
622 req.ie = ie;
623 req.ie_len = ie_len;
624 if (memcmp(wdev->current_bss->pub.bssid, bssid, ETH_ALEN) == 0)
625 req.bss = &wdev->current_bss->pub;
626 else
627 return -ENOTCONN;
628
Johannes Berg667503dd2009-07-07 03:56:11 +0200629 return rdev->ops->disassoc(&rdev->wiphy, dev, &req, wdev);
630}
631
632int cfg80211_mlme_disassoc(struct cfg80211_registered_device *rdev,
633 struct net_device *dev, const u8 *bssid,
634 const u8 *ie, int ie_len, u16 reason)
635{
636 struct wireless_dev *wdev = dev->ieee80211_ptr;
637 int err;
638
639 wdev_lock(wdev);
640 err = __cfg80211_mlme_disassoc(rdev, dev, bssid, ie, ie_len, reason);
641 wdev_unlock(wdev);
642
643 return err;
Johannes Berg19957bb2009-07-02 17:20:43 +0200644}
645
646void cfg80211_mlme_down(struct cfg80211_registered_device *rdev,
647 struct net_device *dev)
648{
649 struct wireless_dev *wdev = dev->ieee80211_ptr;
650 struct cfg80211_deauth_request req;
651 int i;
652
Johannes Berg667503dd2009-07-07 03:56:11 +0200653 ASSERT_WDEV_LOCK(wdev);
654
Johannes Berg19957bb2009-07-02 17:20:43 +0200655 if (!rdev->ops->deauth)
656 return;
657
658 memset(&req, 0, sizeof(req));
659 req.reason_code = WLAN_REASON_DEAUTH_LEAVING;
660 req.ie = NULL;
661 req.ie_len = 0;
662
663 if (wdev->current_bss) {
664 req.bss = &wdev->current_bss->pub;
Johannes Berg667503dd2009-07-07 03:56:11 +0200665 rdev->ops->deauth(&rdev->wiphy, dev, &req, wdev);
Johannes Berg19957bb2009-07-02 17:20:43 +0200666 if (wdev->current_bss) {
667 cfg80211_unhold_bss(wdev->current_bss);
668 cfg80211_put_bss(&wdev->current_bss->pub);
669 wdev->current_bss = NULL;
670 }
671 }
672
673 for (i = 0; i < MAX_AUTH_BSSES; i++) {
674 if (wdev->auth_bsses[i]) {
675 req.bss = &wdev->auth_bsses[i]->pub;
Johannes Berg667503dd2009-07-07 03:56:11 +0200676 rdev->ops->deauth(&rdev->wiphy, dev, &req, wdev);
Johannes Berg19957bb2009-07-02 17:20:43 +0200677 if (wdev->auth_bsses[i]) {
678 cfg80211_unhold_bss(wdev->auth_bsses[i]);
679 cfg80211_put_bss(&wdev->auth_bsses[i]->pub);
680 wdev->auth_bsses[i] = NULL;
681 }
682 }
683 if (wdev->authtry_bsses[i]) {
684 req.bss = &wdev->authtry_bsses[i]->pub;
Johannes Berg667503dd2009-07-07 03:56:11 +0200685 rdev->ops->deauth(&rdev->wiphy, dev, &req, wdev);
Johannes Berg19957bb2009-07-02 17:20:43 +0200686 if (wdev->authtry_bsses[i]) {
687 cfg80211_unhold_bss(wdev->authtry_bsses[i]);
688 cfg80211_put_bss(&wdev->authtry_bsses[i]->pub);
689 wdev->authtry_bsses[i] = NULL;
690 }
691 }
692 }
693}
Jouni Malinen9588bbd2009-12-23 13:15:41 +0100694
695void cfg80211_ready_on_channel(struct net_device *dev, u64 cookie,
696 struct ieee80211_channel *chan,
697 enum nl80211_channel_type channel_type,
698 unsigned int duration, gfp_t gfp)
699{
700 struct wiphy *wiphy = dev->ieee80211_ptr->wiphy;
701 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
702
703 nl80211_send_remain_on_channel(rdev, dev, cookie, chan, channel_type,
704 duration, gfp);
705}
706EXPORT_SYMBOL(cfg80211_ready_on_channel);
707
708void cfg80211_remain_on_channel_expired(struct net_device *dev,
709 u64 cookie,
710 struct ieee80211_channel *chan,
711 enum nl80211_channel_type channel_type,
712 gfp_t gfp)
713{
714 struct wiphy *wiphy = dev->ieee80211_ptr->wiphy;
715 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
716
717 nl80211_send_remain_on_channel_cancel(rdev, dev, cookie, chan,
718 channel_type, gfp);
719}
720EXPORT_SYMBOL(cfg80211_remain_on_channel_expired);
Johannes Berg98b62182009-12-23 13:15:44 +0100721
722void cfg80211_new_sta(struct net_device *dev, const u8 *mac_addr,
723 struct station_info *sinfo, gfp_t gfp)
724{
725 struct wiphy *wiphy = dev->ieee80211_ptr->wiphy;
726 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
727
728 nl80211_send_sta_event(rdev, dev, mac_addr, sinfo, gfp);
729}
730EXPORT_SYMBOL(cfg80211_new_sta);
Jouni Malinen026331c2010-02-15 12:53:10 +0200731
732struct cfg80211_action_registration {
733 struct list_head list;
734
735 u32 nlpid;
736
737 int match_len;
738
739 u8 match[];
740};
741
742int cfg80211_mlme_register_action(struct wireless_dev *wdev, u32 snd_pid,
743 const u8 *match_data, int match_len)
744{
745 struct cfg80211_action_registration *reg, *nreg;
746 int err = 0;
747
748 nreg = kzalloc(sizeof(*reg) + match_len, GFP_KERNEL);
749 if (!nreg)
750 return -ENOMEM;
751
752 spin_lock_bh(&wdev->action_registrations_lock);
753
754 list_for_each_entry(reg, &wdev->action_registrations, list) {
755 int mlen = min(match_len, reg->match_len);
756
757 if (memcmp(reg->match, match_data, mlen) == 0) {
758 err = -EALREADY;
759 break;
760 }
761 }
762
763 if (err) {
764 kfree(nreg);
765 goto out;
766 }
767
768 memcpy(nreg->match, match_data, match_len);
769 nreg->match_len = match_len;
770 nreg->nlpid = snd_pid;
771 list_add(&nreg->list, &wdev->action_registrations);
772
773 out:
774 spin_unlock_bh(&wdev->action_registrations_lock);
775 return err;
776}
777
778void cfg80211_mlme_unregister_actions(struct wireless_dev *wdev, u32 nlpid)
779{
780 struct cfg80211_action_registration *reg, *tmp;
781
782 spin_lock_bh(&wdev->action_registrations_lock);
783
784 list_for_each_entry_safe(reg, tmp, &wdev->action_registrations, list) {
785 if (reg->nlpid == nlpid) {
786 list_del(&reg->list);
787 kfree(reg);
788 }
789 }
790
791 spin_unlock_bh(&wdev->action_registrations_lock);
792}
793
794void cfg80211_mlme_purge_actions(struct wireless_dev *wdev)
795{
796 struct cfg80211_action_registration *reg, *tmp;
797
798 spin_lock_bh(&wdev->action_registrations_lock);
799
800 list_for_each_entry_safe(reg, tmp, &wdev->action_registrations, list) {
801 list_del(&reg->list);
802 kfree(reg);
803 }
804
805 spin_unlock_bh(&wdev->action_registrations_lock);
806}
807
808int cfg80211_mlme_action(struct cfg80211_registered_device *rdev,
809 struct net_device *dev,
810 struct ieee80211_channel *chan,
811 enum nl80211_channel_type channel_type,
812 const u8 *buf, size_t len, u64 *cookie)
813{
814 struct wireless_dev *wdev = dev->ieee80211_ptr;
815 const struct ieee80211_mgmt *mgmt;
816
817 if (rdev->ops->action == NULL)
818 return -EOPNOTSUPP;
819 if (len < 24 + 1)
820 return -EINVAL;
821
822 mgmt = (const struct ieee80211_mgmt *) buf;
823 if (!ieee80211_is_action(mgmt->frame_control))
824 return -EINVAL;
825 if (mgmt->u.action.category != WLAN_CATEGORY_PUBLIC) {
826 /* Verify that we are associated with the destination AP */
827 if (!wdev->current_bss ||
828 memcmp(wdev->current_bss->pub.bssid, mgmt->bssid,
829 ETH_ALEN) != 0 ||
830 memcmp(wdev->current_bss->pub.bssid, mgmt->da,
831 ETH_ALEN) != 0)
832 return -ENOTCONN;
833 }
834
835 if (memcmp(mgmt->sa, dev->dev_addr, ETH_ALEN) != 0)
836 return -EINVAL;
837
838 /* Transmit the Action frame as requested by user space */
839 return rdev->ops->action(&rdev->wiphy, dev, chan, channel_type,
840 buf, len, cookie);
841}
842
843bool cfg80211_rx_action(struct net_device *dev, int freq, const u8 *buf,
844 size_t len, gfp_t gfp)
845{
846 struct wireless_dev *wdev = dev->ieee80211_ptr;
847 struct wiphy *wiphy = wdev->wiphy;
848 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
849 struct cfg80211_action_registration *reg;
850 const u8 *action_data;
851 int action_data_len;
852 bool result = false;
853
854 /* frame length - min size excluding category */
855 action_data_len = len - (IEEE80211_MIN_ACTION_SIZE - 1);
856
857 /* action data starts with category */
858 action_data = buf + IEEE80211_MIN_ACTION_SIZE - 1;
859
860 spin_lock_bh(&wdev->action_registrations_lock);
861
862 list_for_each_entry(reg, &wdev->action_registrations, list) {
863 if (reg->match_len > action_data_len)
864 continue;
865
866 if (memcmp(reg->match, action_data, reg->match_len))
867 continue;
868
869 /* found match! */
870
871 /* Indicate the received Action frame to user space */
872 if (nl80211_send_action(rdev, dev, reg->nlpid, freq,
873 buf, len, gfp))
874 continue;
875
876 result = true;
877 break;
878 }
879
880 spin_unlock_bh(&wdev->action_registrations_lock);
881
882 return result;
883}
884EXPORT_SYMBOL(cfg80211_rx_action);
885
886void cfg80211_action_tx_status(struct net_device *dev, u64 cookie,
887 const u8 *buf, size_t len, bool ack, gfp_t gfp)
888{
889 struct wireless_dev *wdev = dev->ieee80211_ptr;
890 struct wiphy *wiphy = wdev->wiphy;
891 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
892
893 /* Indicate TX status of the Action frame to user space */
894 nl80211_send_action_tx_status(rdev, dev, cookie, buf, len, ack, gfp);
895}
896EXPORT_SYMBOL(cfg80211_action_tx_status);