blob: 2a74395e6ab3b1eb06fa05bb3a190a1bfa2d9a86 [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>
Felix Fietkauc6fb08a2012-03-18 22:58:04 +01009#include <linux/etherdevice.h>
Jouni Malinen6039f6d2009-03-19 13:39:21 +020010#include <linux/netdevice.h>
11#include <linux/nl80211.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090012#include <linux/slab.h>
Johannes Berga9a11622009-07-27 12:01:53 +020013#include <linux/wireless.h>
Jouni Malinen6039f6d2009-03-19 13:39:21 +020014#include <net/cfg80211.h>
Johannes Berga9a11622009-07-27 12:01:53 +020015#include <net/iw_handler.h>
Jouni Malinen6039f6d2009-03-19 13:39:21 +020016#include "core.h"
17#include "nl80211.h"
Hila Gonene35e4d22012-06-27 17:19:42 +030018#include "rdev-ops.h"
19
Jouni Malinen6039f6d2009-03-19 13:39:21 +020020
Johannes Bergcb0b4be2009-07-07 03:56:07 +020021void cfg80211_send_rx_auth(struct net_device *dev, const u8 *buf, size_t len)
Jouni Malinen6039f6d2009-03-19 13:39:21 +020022{
Johannes Berg19957bb2009-07-02 17:20:43 +020023 struct wireless_dev *wdev = dev->ieee80211_ptr;
24 struct wiphy *wiphy = wdev->wiphy;
Jouni Malinen6039f6d2009-03-19 13:39:21 +020025 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
Johannes Berg19957bb2009-07-02 17:20:43 +020026
Johannes Berg667503dd2009-07-07 03:56:11 +020027 wdev_lock(wdev);
Johannes Bergcb0b4be2009-07-07 03:56:07 +020028
Johannes Berg95de8172012-01-20 13:55:25 +010029 nl80211_send_rx_auth(rdev, dev, buf, len, GFP_KERNEL);
30 cfg80211_sme_rx_auth(dev, buf, len);
Johannes Berg667503dd2009-07-07 03:56:11 +020031
32 wdev_unlock(wdev);
Jouni Malinen6039f6d2009-03-19 13:39:21 +020033}
34EXPORT_SYMBOL(cfg80211_send_rx_auth);
35
Johannes Berg95de8172012-01-20 13:55:25 +010036void cfg80211_send_rx_assoc(struct net_device *dev, struct cfg80211_bss *bss,
37 const u8 *buf, size_t len)
Jouni Malinen6039f6d2009-03-19 13:39:21 +020038{
Johannes Berg6829c872009-07-02 09:13:27 +020039 u16 status_code;
40 struct wireless_dev *wdev = dev->ieee80211_ptr;
41 struct wiphy *wiphy = wdev->wiphy;
Jouni Malinen6039f6d2009-03-19 13:39:21 +020042 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
Johannes Berg6829c872009-07-02 09:13:27 +020043 struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf;
44 u8 *ie = mgmt->u.assoc_resp.variable;
Johannes Berg95de8172012-01-20 13:55:25 +010045 int ieoffs = offsetof(struct ieee80211_mgmt, u.assoc_resp.variable);
Johannes Berg6829c872009-07-02 09:13:27 +020046
Johannes Berg667503dd2009-07-07 03:56:11 +020047 wdev_lock(wdev);
Johannes Bergcb0b4be2009-07-07 03:56:07 +020048
Johannes Berg6829c872009-07-02 09:13:27 +020049 status_code = le16_to_cpu(mgmt->u.assoc_resp.status_code);
50
Johannes Bergf401a6f2009-08-07 14:51:05 +020051 /*
52 * This is a bit of a hack, we don't notify userspace of
53 * a (re-)association reply if we tried to send a reassoc
54 * and got a reject -- we only try again with an assoc
55 * frame instead of reassoc.
56 */
57 if (status_code != WLAN_STATUS_SUCCESS && wdev->conn &&
Johannes Berg95de8172012-01-20 13:55:25 +010058 cfg80211_sme_failed_reassoc(wdev)) {
59 cfg80211_put_bss(bss);
Johannes Bergf401a6f2009-08-07 14:51:05 +020060 goto out;
Johannes Berg95de8172012-01-20 13:55:25 +010061 }
Johannes Bergf401a6f2009-08-07 14:51:05 +020062
Johannes Bergcb0b4be2009-07-07 03:56:07 +020063 nl80211_send_rx_assoc(rdev, dev, buf, len, GFP_KERNEL);
Johannes Berg6829c872009-07-02 09:13:27 +020064
Johannes Berg95de8172012-01-20 13:55:25 +010065 if (status_code != WLAN_STATUS_SUCCESS && wdev->conn) {
Johannes Berg7d930bc2009-10-20 15:08:53 +090066 cfg80211_sme_failed_assoc(wdev);
Johannes Berg7d930bc2009-10-20 15:08:53 +090067 /*
68 * do not call connect_result() now because the
69 * sme will schedule work that does it later.
70 */
Johannes Berg95de8172012-01-20 13:55:25 +010071 cfg80211_put_bss(bss);
Johannes Berg7d930bc2009-10-20 15:08:53 +090072 goto out;
Johannes Bergdf7fc0f2009-07-29 11:23:49 +020073 }
74
Johannes Bergea416a72009-08-17 12:22:14 +020075 if (!wdev->conn && wdev->sme_state == CFG80211_SME_IDLE) {
76 /*
77 * This is for the userspace SME, the CONNECTING
78 * state will be changed to CONNECTED by
79 * __cfg80211_connect_result() below.
80 */
81 wdev->sme_state = CFG80211_SME_CONNECTING;
82 }
83
Johannes Berg95de8172012-01-20 13:55:25 +010084 /* this consumes the bss reference */
Johannes Bergdf7fc0f2009-07-29 11:23:49 +020085 __cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, ie, len - ieoffs,
86 status_code,
Johannes Berg95de8172012-01-20 13:55:25 +010087 status_code == WLAN_STATUS_SUCCESS, bss);
Johannes Bergf401a6f2009-08-07 14:51:05 +020088 out:
Johannes Berg667503dd2009-07-07 03:56:11 +020089 wdev_unlock(wdev);
Jouni Malinen6039f6d2009-03-19 13:39:21 +020090}
91EXPORT_SYMBOL(cfg80211_send_rx_assoc);
92
Holger Schurigce470612009-10-13 13:28:13 +020093void __cfg80211_send_deauth(struct net_device *dev,
Johannes Berg667503dd2009-07-07 03:56:11 +020094 const u8 *buf, size_t len)
Jouni Malinen6039f6d2009-03-19 13:39:21 +020095{
Johannes Berg6829c872009-07-02 09:13:27 +020096 struct wireless_dev *wdev = dev->ieee80211_ptr;
97 struct wiphy *wiphy = wdev->wiphy;
Jouni Malinen6039f6d2009-03-19 13:39:21 +020098 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
Johannes Berg6829c872009-07-02 09:13:27 +020099 struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf;
Johannes Berg19957bb2009-07-02 17:20:43 +0200100 const u8 *bssid = mgmt->bssid;
Johannes Berg95de8172012-01-20 13:55:25 +0100101 bool was_current = false;
Johannes Berg6829c872009-07-02 09:13:27 +0200102
Johannes Berg667503dd2009-07-07 03:56:11 +0200103 ASSERT_WDEV_LOCK(wdev);
Johannes Bergcb0b4be2009-07-07 03:56:07 +0200104
Johannes Berg19957bb2009-07-02 17:20:43 +0200105 if (wdev->current_bss &&
Joe Perchesac422d32012-05-08 18:56:55 +0000106 ether_addr_equal(wdev->current_bss->pub.bssid, bssid)) {
Johannes Berg19957bb2009-07-02 17:20:43 +0200107 cfg80211_unhold_bss(wdev->current_bss);
108 cfg80211_put_bss(&wdev->current_bss->pub);
109 wdev->current_bss = NULL;
Johannes Berg3f3b6a82010-08-05 10:20:27 +0200110 was_current = true;
Johannes Berg19957bb2009-07-02 17:20:43 +0200111 }
Johannes Berg19957bb2009-07-02 17:20:43 +0200112
Johannes Berg5fba4af2009-12-02 12:43:42 +0100113 nl80211_send_deauth(rdev, dev, buf, len, GFP_KERNEL);
114
Johannes Berg3f3b6a82010-08-05 10:20:27 +0200115 if (wdev->sme_state == CFG80211_SME_CONNECTED && was_current) {
Johannes Berg6829c872009-07-02 09:13:27 +0200116 u16 reason_code;
117 bool from_ap;
118
119 reason_code = le16_to_cpu(mgmt->u.deauth.reason_code);
120
Joe Perchesac422d32012-05-08 18:56:55 +0000121 from_ap = !ether_addr_equal(mgmt->sa, dev->dev_addr);
Johannes Berg667503dd2009-07-07 03:56:11 +0200122 __cfg80211_disconnected(dev, NULL, 0, reason_code, from_ap);
Johannes Berg6829c872009-07-02 09:13:27 +0200123 } else if (wdev->sme_state == CFG80211_SME_CONNECTING) {
Johannes Berg667503dd2009-07-07 03:56:11 +0200124 __cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, NULL, 0,
125 WLAN_STATUS_UNSPECIFIED_FAILURE,
Johannes Bergdf7fc0f2009-07-29 11:23:49 +0200126 false, NULL);
Johannes Berg667503dd2009-07-07 03:56:11 +0200127 }
128}
Holger Schurigce470612009-10-13 13:28:13 +0200129EXPORT_SYMBOL(__cfg80211_send_deauth);
Johannes Berg667503dd2009-07-07 03:56:11 +0200130
Holger Schurigce470612009-10-13 13:28:13 +0200131void cfg80211_send_deauth(struct net_device *dev, const u8 *buf, size_t len)
Johannes Berg667503dd2009-07-07 03:56:11 +0200132{
133 struct wireless_dev *wdev = dev->ieee80211_ptr;
134
Holger Schurigce470612009-10-13 13:28:13 +0200135 wdev_lock(wdev);
136 __cfg80211_send_deauth(dev, buf, len);
137 wdev_unlock(wdev);
Jouni Malinen6039f6d2009-03-19 13:39:21 +0200138}
Jouni Malinen53b46b82009-03-27 20:53:56 +0200139EXPORT_SYMBOL(cfg80211_send_deauth);
Jouni Malinen6039f6d2009-03-19 13:39:21 +0200140
Holger Schurigce470612009-10-13 13:28:13 +0200141void __cfg80211_send_disassoc(struct net_device *dev,
Johannes Berg667503dd2009-07-07 03:56:11 +0200142 const u8 *buf, size_t len)
Jouni Malinen6039f6d2009-03-19 13:39:21 +0200143{
Johannes Berg6829c872009-07-02 09:13:27 +0200144 struct wireless_dev *wdev = dev->ieee80211_ptr;
145 struct wiphy *wiphy = wdev->wiphy;
Jouni Malinen6039f6d2009-03-19 13:39:21 +0200146 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
Johannes Berg6829c872009-07-02 09:13:27 +0200147 struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf;
Johannes Berg19957bb2009-07-02 17:20:43 +0200148 const u8 *bssid = mgmt->bssid;
Johannes Berg19957bb2009-07-02 17:20:43 +0200149 u16 reason_code;
150 bool from_ap;
Johannes Berg6829c872009-07-02 09:13:27 +0200151
Johannes Berg596a07c2009-07-11 00:17:32 +0200152 ASSERT_WDEV_LOCK(wdev);
Johannes Bergcb0b4be2009-07-07 03:56:07 +0200153
154 nl80211_send_disassoc(rdev, dev, buf, len, GFP_KERNEL);
Johannes Berg6829c872009-07-02 09:13:27 +0200155
Johannes Berg596a07c2009-07-11 00:17:32 +0200156 if (wdev->sme_state != CFG80211_SME_CONNECTED)
157 return;
Johannes Berg6829c872009-07-02 09:13:27 +0200158
Johannes Berg19957bb2009-07-02 17:20:43 +0200159 if (wdev->current_bss &&
Joe Perchesac422d32012-05-08 18:56:55 +0000160 ether_addr_equal(wdev->current_bss->pub.bssid, bssid)) {
Johannes Berg95de8172012-01-20 13:55:25 +0100161 cfg80211_sme_disassoc(dev, wdev->current_bss);
162 cfg80211_unhold_bss(wdev->current_bss);
163 cfg80211_put_bss(&wdev->current_bss->pub);
164 wdev->current_bss = NULL;
Johannes Berg19957bb2009-07-02 17:20:43 +0200165 } else
166 WARN_ON(1);
Johannes Berg6829c872009-07-02 09:13:27 +0200167
Johannes Berg6829c872009-07-02 09:13:27 +0200168
Johannes Berg19957bb2009-07-02 17:20:43 +0200169 reason_code = le16_to_cpu(mgmt->u.disassoc.reason_code);
170
Joe Perchesac422d32012-05-08 18:56:55 +0000171 from_ap = !ether_addr_equal(mgmt->sa, dev->dev_addr);
Johannes Berg667503dd2009-07-07 03:56:11 +0200172 __cfg80211_disconnected(dev, NULL, 0, reason_code, from_ap);
Johannes Berg667503dd2009-07-07 03:56:11 +0200173}
Holger Schurigce470612009-10-13 13:28:13 +0200174EXPORT_SYMBOL(__cfg80211_send_disassoc);
Johannes Berg667503dd2009-07-07 03:56:11 +0200175
Holger Schurigce470612009-10-13 13:28:13 +0200176void cfg80211_send_disassoc(struct net_device *dev, const u8 *buf, size_t len)
Johannes Berg667503dd2009-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_disassoc(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_disassoc);
Jouni Malinena3b8b052009-03-27 21:59:49 +0200185
Jouni Malinencf4e5942010-12-16 00:52:40 +0200186void cfg80211_send_unprot_deauth(struct net_device *dev, const u8 *buf,
187 size_t len)
188{
189 struct wireless_dev *wdev = dev->ieee80211_ptr;
190 struct wiphy *wiphy = wdev->wiphy;
191 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
192
193 nl80211_send_unprot_deauth(rdev, dev, buf, len, GFP_ATOMIC);
194}
195EXPORT_SYMBOL(cfg80211_send_unprot_deauth);
196
197void cfg80211_send_unprot_disassoc(struct net_device *dev, const u8 *buf,
198 size_t len)
199{
200 struct wireless_dev *wdev = dev->ieee80211_ptr;
201 struct wiphy *wiphy = wdev->wiphy;
202 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
203
204 nl80211_send_unprot_disassoc(rdev, dev, buf, len, GFP_ATOMIC);
205}
206EXPORT_SYMBOL(cfg80211_send_unprot_disassoc);
207
Johannes Berga58ce432009-11-19 12:45:42 +0100208void cfg80211_send_auth_timeout(struct net_device *dev, const u8 *addr)
209{
210 struct wireless_dev *wdev = dev->ieee80211_ptr;
211 struct wiphy *wiphy = wdev->wiphy;
212 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
213
214 wdev_lock(wdev);
215
216 nl80211_send_auth_timeout(rdev, dev, addr, GFP_KERNEL);
217 if (wdev->sme_state == CFG80211_SME_CONNECTING)
218 __cfg80211_connect_result(dev, addr, NULL, 0, NULL, 0,
219 WLAN_STATUS_UNSPECIFIED_FAILURE,
220 false, NULL);
221
Johannes Berg667503dd2009-07-07 03:56:11 +0200222 wdev_unlock(wdev);
Jouni Malinen1965c852009-04-22 21:38:25 +0300223}
224EXPORT_SYMBOL(cfg80211_send_auth_timeout);
225
Johannes Bergcb0b4be2009-07-07 03:56:07 +0200226void cfg80211_send_assoc_timeout(struct net_device *dev, const u8 *addr)
Jouni Malinen1965c852009-04-22 21:38:25 +0300227{
Johannes Berg6829c872009-07-02 09:13:27 +0200228 struct wireless_dev *wdev = dev->ieee80211_ptr;
229 struct wiphy *wiphy = wdev->wiphy;
Jouni Malinen1965c852009-04-22 21:38:25 +0300230 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
Johannes Berg19957bb2009-07-02 17:20:43 +0200231
Johannes Berg667503dd2009-07-07 03:56:11 +0200232 wdev_lock(wdev);
Johannes Bergcb0b4be2009-07-07 03:56:07 +0200233
234 nl80211_send_assoc_timeout(rdev, dev, addr, GFP_KERNEL);
Johannes Berg6829c872009-07-02 09:13:27 +0200235 if (wdev->sme_state == CFG80211_SME_CONNECTING)
Johannes Berg667503dd2009-07-07 03:56:11 +0200236 __cfg80211_connect_result(dev, addr, NULL, 0, NULL, 0,
237 WLAN_STATUS_UNSPECIFIED_FAILURE,
Johannes Bergdf7fc0f2009-07-29 11:23:49 +0200238 false, NULL);
Johannes Berg19957bb2009-07-02 17:20:43 +0200239
Johannes Berg667503dd2009-07-07 03:56:11 +0200240 wdev_unlock(wdev);
Jouni Malinen1965c852009-04-22 21:38:25 +0300241}
242EXPORT_SYMBOL(cfg80211_send_assoc_timeout);
243
Jouni Malinena3b8b052009-03-27 21:59:49 +0200244void cfg80211_michael_mic_failure(struct net_device *dev, const u8 *addr,
245 enum nl80211_key_type key_type, int key_id,
Johannes Berge6d6e342009-07-01 21:26:47 +0200246 const u8 *tsc, gfp_t gfp)
Jouni Malinena3b8b052009-03-27 21:59:49 +0200247{
248 struct wiphy *wiphy = dev->ieee80211_ptr->wiphy;
249 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
Johannes Berg3d23e342009-09-29 23:27:28 +0200250#ifdef CONFIG_CFG80211_WEXT
Johannes Bergf58d4ed2009-06-19 02:45:21 +0200251 union iwreq_data wrqu;
Johannes Berge6d6e342009-07-01 21:26:47 +0200252 char *buf = kmalloc(128, gfp);
Johannes Bergf58d4ed2009-06-19 02:45:21 +0200253
254 if (buf) {
255 sprintf(buf, "MLME-MICHAELMICFAILURE.indication("
256 "keyid=%d %scast addr=%pM)", key_id,
257 key_type == NL80211_KEYTYPE_GROUP ? "broad" : "uni",
258 addr);
259 memset(&wrqu, 0, sizeof(wrqu));
260 wrqu.data.length = strlen(buf);
261 wireless_send_event(dev, IWEVCUSTOM, &wrqu, buf);
262 kfree(buf);
263 }
264#endif
265
Johannes Berge6d6e342009-07-01 21:26:47 +0200266 nl80211_michael_mic_failure(rdev, dev, addr, key_type, key_id, tsc, gfp);
Jouni Malinena3b8b052009-03-27 21:59:49 +0200267}
268EXPORT_SYMBOL(cfg80211_michael_mic_failure);
Johannes Berg19957bb2009-07-02 17:20:43 +0200269
270/* some MLME handling for userspace SME */
Johannes Berg667503dd2009-07-07 03:56:11 +0200271int __cfg80211_mlme_auth(struct cfg80211_registered_device *rdev,
272 struct net_device *dev,
273 struct ieee80211_channel *chan,
274 enum nl80211_auth_type auth_type,
275 const u8 *bssid,
276 const u8 *ssid, int ssid_len,
Johannes Bergfffd0932009-07-08 14:22:54 +0200277 const u8 *ie, int ie_len,
Jouni Malinene39e5b52012-09-30 19:29:39 +0300278 const u8 *key, int key_len, int key_idx,
279 const u8 *sae_data, int sae_data_len)
Johannes Berg19957bb2009-07-02 17:20:43 +0200280{
281 struct wireless_dev *wdev = dev->ieee80211_ptr;
282 struct cfg80211_auth_request req;
Johannes Berg95de8172012-01-20 13:55:25 +0100283 int err;
Johannes Berg19957bb2009-07-02 17:20:43 +0200284
Johannes Berg667503dd2009-07-07 03:56:11 +0200285 ASSERT_WDEV_LOCK(wdev);
286
Johannes Bergfffd0932009-07-08 14:22:54 +0200287 if (auth_type == NL80211_AUTHTYPE_SHARED_KEY)
288 if (!key || !key_len || key_idx < 0 || key_idx > 4)
289 return -EINVAL;
290
Johannes Berg0a9b5e12009-07-02 18:26:18 +0200291 if (wdev->current_bss &&
Joe Perchesac422d32012-05-08 18:56:55 +0000292 ether_addr_equal(bssid, wdev->current_bss->pub.bssid))
Johannes Berg0a9b5e12009-07-02 18:26:18 +0200293 return -EALREADY;
294
Johannes Berg19957bb2009-07-02 17:20:43 +0200295 memset(&req, 0, sizeof(req));
296
297 req.ie = ie;
298 req.ie_len = ie_len;
Jouni Malinene39e5b52012-09-30 19:29:39 +0300299 req.sae_data = sae_data;
300 req.sae_data_len = sae_data_len;
Johannes Berg19957bb2009-07-02 17:20:43 +0200301 req.auth_type = auth_type;
302 req.bss = cfg80211_get_bss(&rdev->wiphy, chan, bssid, ssid, ssid_len,
303 WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS);
Johannes Bergfffd0932009-07-08 14:22:54 +0200304 req.key = key;
305 req.key_len = key_len;
306 req.key_idx = key_idx;
Johannes Berg19957bb2009-07-02 17:20:43 +0200307 if (!req.bss)
308 return -ENOENT;
309
Michal Kaziore4e32452012-06-29 12:47:08 +0200310 err = cfg80211_can_use_chan(rdev, wdev, req.bss->channel,
311 CHAN_MODE_SHARED);
312 if (err)
313 goto out;
314
Hila Gonene35e4d22012-06-27 17:19:42 +0300315 err = rdev_auth(rdev, dev, &req);
Johannes Berg19957bb2009-07-02 17:20:43 +0200316
Michal Kaziore4e32452012-06-29 12:47:08 +0200317out:
Johannes Berg95de8172012-01-20 13:55:25 +0100318 cfg80211_put_bss(req.bss);
Johannes Berg19957bb2009-07-02 17:20:43 +0200319 return err;
320}
321
Johannes Berg667503dd2009-07-07 03:56:11 +0200322int cfg80211_mlme_auth(struct cfg80211_registered_device *rdev,
323 struct net_device *dev, struct ieee80211_channel *chan,
324 enum nl80211_auth_type auth_type, const u8 *bssid,
325 const u8 *ssid, int ssid_len,
Johannes Bergfffd0932009-07-08 14:22:54 +0200326 const u8 *ie, int ie_len,
Jouni Malinene39e5b52012-09-30 19:29:39 +0300327 const u8 *key, int key_len, int key_idx,
328 const u8 *sae_data, int sae_data_len)
Johannes Berg667503dd2009-07-07 03:56:11 +0200329{
330 int err;
331
Michal Kaziore4e32452012-06-29 12:47:08 +0200332 mutex_lock(&rdev->devlist_mtx);
Johannes Berg667503dd2009-07-07 03:56:11 +0200333 wdev_lock(dev->ieee80211_ptr);
334 err = __cfg80211_mlme_auth(rdev, dev, chan, auth_type, bssid,
Johannes Bergfffd0932009-07-08 14:22:54 +0200335 ssid, ssid_len, ie, ie_len,
Jouni Malinene39e5b52012-09-30 19:29:39 +0300336 key, key_len, key_idx,
337 sae_data, sae_data_len);
Johannes Berg667503dd2009-07-07 03:56:11 +0200338 wdev_unlock(dev->ieee80211_ptr);
Michal Kaziore4e32452012-06-29 12:47:08 +0200339 mutex_unlock(&rdev->devlist_mtx);
Johannes Berg667503dd2009-07-07 03:56:11 +0200340
341 return err;
342}
343
Ben Greear7e7c8922011-11-18 11:31:59 -0800344/* Do a logical ht_capa &= ht_capa_mask. */
345void cfg80211_oper_and_ht_capa(struct ieee80211_ht_cap *ht_capa,
346 const struct ieee80211_ht_cap *ht_capa_mask)
347{
348 int i;
349 u8 *p1, *p2;
350 if (!ht_capa_mask) {
351 memset(ht_capa, 0, sizeof(*ht_capa));
352 return;
353 }
354
355 p1 = (u8*)(ht_capa);
356 p2 = (u8*)(ht_capa_mask);
357 for (i = 0; i<sizeof(*ht_capa); i++)
358 p1[i] &= p2[i];
359}
360
Johannes Berg667503dd2009-07-07 03:56:11 +0200361int __cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev,
362 struct net_device *dev,
363 struct ieee80211_channel *chan,
364 const u8 *bssid, const u8 *prev_bssid,
365 const u8 *ssid, int ssid_len,
366 const u8 *ie, int ie_len, bool use_mfp,
Ben Greear7e7c8922011-11-18 11:31:59 -0800367 struct cfg80211_crypto_settings *crypt,
368 u32 assoc_flags, struct ieee80211_ht_cap *ht_capa,
369 struct ieee80211_ht_cap *ht_capa_mask)
Johannes Berg19957bb2009-07-02 17:20:43 +0200370{
371 struct wireless_dev *wdev = dev->ieee80211_ptr;
372 struct cfg80211_assoc_request req;
Johannes Berg95de8172012-01-20 13:55:25 +0100373 int err;
Jouni Malinen24b6b152009-11-17 21:35:38 +0200374 bool was_connected = false;
Johannes Berg19957bb2009-07-02 17:20:43 +0200375
Johannes Berg667503dd2009-07-07 03:56:11 +0200376 ASSERT_WDEV_LOCK(wdev);
377
Johannes Berg19957bb2009-07-02 17:20:43 +0200378 memset(&req, 0, sizeof(req));
379
Jouni Malinen24b6b152009-11-17 21:35:38 +0200380 if (wdev->current_bss && prev_bssid &&
Joe Perchesac422d32012-05-08 18:56:55 +0000381 ether_addr_equal(wdev->current_bss->pub.bssid, prev_bssid)) {
Jouni Malinen24b6b152009-11-17 21:35:38 +0200382 /*
383 * Trying to reassociate: Allow this to proceed and let the old
384 * association to be dropped when the new one is completed.
385 */
386 if (wdev->sme_state == CFG80211_SME_CONNECTED) {
387 was_connected = true;
388 wdev->sme_state = CFG80211_SME_CONNECTING;
389 }
390 } else if (wdev->current_bss)
Johannes Berg19957bb2009-07-02 17:20:43 +0200391 return -EALREADY;
392
393 req.ie = ie;
394 req.ie_len = ie_len;
395 memcpy(&req.crypto, crypt, sizeof(req.crypto));
396 req.use_mfp = use_mfp;
Johannes Berg3e5d7642009-07-07 14:37:26 +0200397 req.prev_bssid = prev_bssid;
Ben Greear7e7c8922011-11-18 11:31:59 -0800398 req.flags = assoc_flags;
399 if (ht_capa)
400 memcpy(&req.ht_capa, ht_capa, sizeof(req.ht_capa));
401 if (ht_capa_mask)
402 memcpy(&req.ht_capa_mask, ht_capa_mask,
403 sizeof(req.ht_capa_mask));
404 cfg80211_oper_and_ht_capa(&req.ht_capa_mask,
405 rdev->wiphy.ht_capa_mod_mask);
406
Johannes Berg19957bb2009-07-02 17:20:43 +0200407 req.bss = cfg80211_get_bss(&rdev->wiphy, chan, bssid, ssid, ssid_len,
408 WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS);
Jouni Malinen24b6b152009-11-17 21:35:38 +0200409 if (!req.bss) {
410 if (was_connected)
411 wdev->sme_state = CFG80211_SME_CONNECTED;
Johannes Berg19957bb2009-07-02 17:20:43 +0200412 return -ENOENT;
Jouni Malinen24b6b152009-11-17 21:35:38 +0200413 }
Johannes Berg19957bb2009-07-02 17:20:43 +0200414
Michal Kaziore4e32452012-06-29 12:47:08 +0200415 err = cfg80211_can_use_chan(rdev, wdev, req.bss->channel,
416 CHAN_MODE_SHARED);
417 if (err)
418 goto out;
419
Hila Gonene35e4d22012-06-27 17:19:42 +0300420 err = rdev_assoc(rdev, dev, &req);
Johannes Berg95de8172012-01-20 13:55:25 +0100421
Michal Kaziore4e32452012-06-29 12:47:08 +0200422out:
Johannes Berg95de8172012-01-20 13:55:25 +0100423 if (err) {
424 if (was_connected)
425 wdev->sme_state = CFG80211_SME_CONNECTED;
426 cfg80211_put_bss(req.bss);
427 }
428
Johannes Berg19957bb2009-07-02 17:20:43 +0200429 return err;
430}
431
Johannes Berg667503dd2009-07-07 03:56:11 +0200432int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev,
433 struct net_device *dev,
434 struct ieee80211_channel *chan,
435 const u8 *bssid, const u8 *prev_bssid,
436 const u8 *ssid, int ssid_len,
437 const u8 *ie, int ie_len, bool use_mfp,
Ben Greear7e7c8922011-11-18 11:31:59 -0800438 struct cfg80211_crypto_settings *crypt,
439 u32 assoc_flags, struct ieee80211_ht_cap *ht_capa,
440 struct ieee80211_ht_cap *ht_capa_mask)
Johannes Berg667503dd2009-07-07 03:56:11 +0200441{
442 struct wireless_dev *wdev = dev->ieee80211_ptr;
443 int err;
444
Michal Kaziore4e32452012-06-29 12:47:08 +0200445 mutex_lock(&rdev->devlist_mtx);
Johannes Berg667503dd2009-07-07 03:56:11 +0200446 wdev_lock(wdev);
447 err = __cfg80211_mlme_assoc(rdev, dev, chan, bssid, prev_bssid,
Ben Greear7e7c8922011-11-18 11:31:59 -0800448 ssid, ssid_len, ie, ie_len, use_mfp, crypt,
449 assoc_flags, ht_capa, ht_capa_mask);
Johannes Berg667503dd2009-07-07 03:56:11 +0200450 wdev_unlock(wdev);
Michal Kaziore4e32452012-06-29 12:47:08 +0200451 mutex_unlock(&rdev->devlist_mtx);
Johannes Berg667503dd2009-07-07 03:56:11 +0200452
453 return err;
454}
455
456int __cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev,
457 struct net_device *dev, const u8 *bssid,
Jouni Malinend5cdfac2010-04-04 09:37:19 +0300458 const u8 *ie, int ie_len, u16 reason,
459 bool local_state_change)
Johannes Berg19957bb2009-07-02 17:20:43 +0200460{
461 struct wireless_dev *wdev = dev->ieee80211_ptr;
Johannes Berg95de8172012-01-20 13:55:25 +0100462 struct cfg80211_deauth_request req = {
463 .bssid = bssid,
464 .reason_code = reason,
465 .ie = ie,
466 .ie_len = ie_len,
467 };
Johannes Berg19957bb2009-07-02 17:20:43 +0200468
Johannes Berg667503dd2009-07-07 03:56:11 +0200469 ASSERT_WDEV_LOCK(wdev);
470
Johannes Berg95de8172012-01-20 13:55:25 +0100471 if (local_state_change) {
472 if (wdev->current_bss &&
Joe Perchesac422d32012-05-08 18:56:55 +0000473 ether_addr_equal(wdev->current_bss->pub.bssid, bssid)) {
Johannes Berg95de8172012-01-20 13:55:25 +0100474 cfg80211_unhold_bss(wdev->current_bss);
475 cfg80211_put_bss(&wdev->current_bss->pub);
476 wdev->current_bss = NULL;
Johannes Berg19957bb2009-07-02 17:20:43 +0200477 }
Johannes Berg19957bb2009-07-02 17:20:43 +0200478
Johannes Berg95de8172012-01-20 13:55:25 +0100479 return 0;
480 }
Johannes Berg19957bb2009-07-02 17:20:43 +0200481
Hila Gonene35e4d22012-06-27 17:19:42 +0300482 return rdev_deauth(rdev, dev, &req);
Johannes Berg19957bb2009-07-02 17:20:43 +0200483}
484
Johannes Berg667503dd2009-07-07 03:56:11 +0200485int cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev,
486 struct net_device *dev, const u8 *bssid,
Jouni Malinend5cdfac2010-04-04 09:37:19 +0300487 const u8 *ie, int ie_len, u16 reason,
488 bool local_state_change)
Johannes Berg667503dd2009-07-07 03:56:11 +0200489{
490 struct wireless_dev *wdev = dev->ieee80211_ptr;
491 int err;
492
493 wdev_lock(wdev);
Jouni Malinend5cdfac2010-04-04 09:37:19 +0300494 err = __cfg80211_mlme_deauth(rdev, dev, bssid, ie, ie_len, reason,
495 local_state_change);
Johannes Berg667503dd2009-07-07 03:56:11 +0200496 wdev_unlock(wdev);
497
498 return err;
499}
500
501static int __cfg80211_mlme_disassoc(struct cfg80211_registered_device *rdev,
502 struct net_device *dev, const u8 *bssid,
Jouni Malinend5cdfac2010-04-04 09:37:19 +0300503 const u8 *ie, int ie_len, u16 reason,
504 bool local_state_change)
Johannes Berg19957bb2009-07-02 17:20:43 +0200505{
506 struct wireless_dev *wdev = dev->ieee80211_ptr;
507 struct cfg80211_disassoc_request req;
508
Johannes Berg667503dd2009-07-07 03:56:11 +0200509 ASSERT_WDEV_LOCK(wdev);
510
Johannes Bergf9d6b402009-07-27 10:22:28 +0200511 if (wdev->sme_state != CFG80211_SME_CONNECTED)
512 return -ENOTCONN;
513
514 if (WARN_ON(!wdev->current_bss))
515 return -ENOTCONN;
516
Johannes Berg19957bb2009-07-02 17:20:43 +0200517 memset(&req, 0, sizeof(req));
518 req.reason_code = reason;
Jouni Malinend5cdfac2010-04-04 09:37:19 +0300519 req.local_state_change = local_state_change;
Johannes Berg19957bb2009-07-02 17:20:43 +0200520 req.ie = ie;
521 req.ie_len = ie_len;
Joe Perchesac422d32012-05-08 18:56:55 +0000522 if (ether_addr_equal(wdev->current_bss->pub.bssid, bssid))
Johannes Berg19957bb2009-07-02 17:20:43 +0200523 req.bss = &wdev->current_bss->pub;
524 else
525 return -ENOTCONN;
526
Hila Gonene35e4d22012-06-27 17:19:42 +0300527 return rdev_disassoc(rdev, dev, &req);
Johannes Berg667503dd2009-07-07 03:56:11 +0200528}
529
530int cfg80211_mlme_disassoc(struct cfg80211_registered_device *rdev,
531 struct net_device *dev, const u8 *bssid,
Jouni Malinend5cdfac2010-04-04 09:37:19 +0300532 const u8 *ie, int ie_len, u16 reason,
533 bool local_state_change)
Johannes Berg667503dd2009-07-07 03:56:11 +0200534{
535 struct wireless_dev *wdev = dev->ieee80211_ptr;
536 int err;
537
538 wdev_lock(wdev);
Jouni Malinend5cdfac2010-04-04 09:37:19 +0300539 err = __cfg80211_mlme_disassoc(rdev, dev, bssid, ie, ie_len, reason,
540 local_state_change);
Johannes Berg667503dd2009-07-07 03:56:11 +0200541 wdev_unlock(wdev);
542
543 return err;
Johannes Berg19957bb2009-07-02 17:20:43 +0200544}
545
546void cfg80211_mlme_down(struct cfg80211_registered_device *rdev,
547 struct net_device *dev)
548{
549 struct wireless_dev *wdev = dev->ieee80211_ptr;
550 struct cfg80211_deauth_request req;
Johannes Berg95de8172012-01-20 13:55:25 +0100551 u8 bssid[ETH_ALEN];
Johannes Berg19957bb2009-07-02 17:20:43 +0200552
Johannes Berg667503dd2009-07-07 03:56:11 +0200553 ASSERT_WDEV_LOCK(wdev);
554
Johannes Berg19957bb2009-07-02 17:20:43 +0200555 if (!rdev->ops->deauth)
556 return;
557
558 memset(&req, 0, sizeof(req));
559 req.reason_code = WLAN_REASON_DEAUTH_LEAVING;
560 req.ie = NULL;
561 req.ie_len = 0;
562
Johannes Berg95de8172012-01-20 13:55:25 +0100563 if (!wdev->current_bss)
564 return;
Johannes Berg19957bb2009-07-02 17:20:43 +0200565
Johannes Berg95de8172012-01-20 13:55:25 +0100566 memcpy(bssid, wdev->current_bss->pub.bssid, ETH_ALEN);
567 req.bssid = bssid;
Hila Gonene35e4d22012-06-27 17:19:42 +0300568 rdev_deauth(rdev, dev, &req);
Johannes Berg95de8172012-01-20 13:55:25 +0100569
570 if (wdev->current_bss) {
571 cfg80211_unhold_bss(wdev->current_bss);
572 cfg80211_put_bss(&wdev->current_bss->pub);
573 wdev->current_bss = NULL;
Johannes Berg19957bb2009-07-02 17:20:43 +0200574 }
575}
Jouni Malinen9588bbd2009-12-23 13:15:41 +0100576
Johannes Berg71bbc992012-06-15 15:30:18 +0200577void cfg80211_ready_on_channel(struct wireless_dev *wdev, u64 cookie,
Jouni Malinen9588bbd2009-12-23 13:15:41 +0100578 struct ieee80211_channel *chan,
579 enum nl80211_channel_type channel_type,
580 unsigned int duration, gfp_t gfp)
581{
Johannes Berg71bbc992012-06-15 15:30:18 +0200582 struct wiphy *wiphy = wdev->wiphy;
Jouni Malinen9588bbd2009-12-23 13:15:41 +0100583 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
584
Johannes Berg71bbc992012-06-15 15:30:18 +0200585 nl80211_send_remain_on_channel(rdev, wdev, cookie, chan, channel_type,
Jouni Malinen9588bbd2009-12-23 13:15:41 +0100586 duration, gfp);
587}
588EXPORT_SYMBOL(cfg80211_ready_on_channel);
589
Johannes Berg71bbc992012-06-15 15:30:18 +0200590void cfg80211_remain_on_channel_expired(struct wireless_dev *wdev, u64 cookie,
Jouni Malinen9588bbd2009-12-23 13:15:41 +0100591 struct ieee80211_channel *chan,
592 enum nl80211_channel_type channel_type,
593 gfp_t gfp)
594{
Johannes Berg71bbc992012-06-15 15:30:18 +0200595 struct wiphy *wiphy = wdev->wiphy;
Jouni Malinen9588bbd2009-12-23 13:15:41 +0100596 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
597
Johannes Berg71bbc992012-06-15 15:30:18 +0200598 nl80211_send_remain_on_channel_cancel(rdev, wdev, cookie, chan,
Jouni Malinen9588bbd2009-12-23 13:15:41 +0100599 channel_type, gfp);
600}
601EXPORT_SYMBOL(cfg80211_remain_on_channel_expired);
Johannes Berg98b62182009-12-23 13:15:44 +0100602
603void cfg80211_new_sta(struct net_device *dev, const u8 *mac_addr,
604 struct station_info *sinfo, gfp_t gfp)
605{
606 struct wiphy *wiphy = dev->ieee80211_ptr->wiphy;
607 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
608
609 nl80211_send_sta_event(rdev, dev, mac_addr, sinfo, gfp);
610}
611EXPORT_SYMBOL(cfg80211_new_sta);
Jouni Malinen026331c2010-02-15 12:53:10 +0200612
Jouni Malinenec15e682011-03-23 15:29:52 +0200613void cfg80211_del_sta(struct net_device *dev, const u8 *mac_addr, gfp_t gfp)
614{
615 struct wiphy *wiphy = dev->ieee80211_ptr->wiphy;
616 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
617
618 nl80211_send_sta_del_event(rdev, dev, mac_addr, gfp);
619}
620EXPORT_SYMBOL(cfg80211_del_sta);
621
Pandiyarajan Pitchaimuthued44a952012-09-18 16:50:49 +0530622void cfg80211_conn_failed(struct net_device *dev, const u8 *mac_addr,
623 enum nl80211_connect_failed_reason reason,
624 gfp_t gfp)
625{
626 struct wiphy *wiphy = dev->ieee80211_ptr->wiphy;
627 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
628
629 nl80211_send_conn_failed_event(rdev, dev, mac_addr, reason, gfp);
630}
631EXPORT_SYMBOL(cfg80211_conn_failed);
632
Johannes Berg2e161f72010-08-12 15:38:38 +0200633struct cfg80211_mgmt_registration {
Jouni Malinen026331c2010-02-15 12:53:10 +0200634 struct list_head list;
635
Eric W. Biederman15e47302012-09-07 20:12:54 +0000636 u32 nlportid;
Jouni Malinen026331c2010-02-15 12:53:10 +0200637
638 int match_len;
639
Johannes Berg2e161f72010-08-12 15:38:38 +0200640 __le16 frame_type;
641
Jouni Malinen026331c2010-02-15 12:53:10 +0200642 u8 match[];
643};
644
Eric W. Biederman15e47302012-09-07 20:12:54 +0000645int cfg80211_mlme_register_mgmt(struct wireless_dev *wdev, u32 snd_portid,
Johannes Berg2e161f72010-08-12 15:38:38 +0200646 u16 frame_type, const u8 *match_data,
647 int match_len)
Jouni Malinen026331c2010-02-15 12:53:10 +0200648{
Johannes Berg271733c2010-10-13 12:06:23 +0200649 struct wiphy *wiphy = wdev->wiphy;
650 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
Johannes Berg2e161f72010-08-12 15:38:38 +0200651 struct cfg80211_mgmt_registration *reg, *nreg;
Jouni Malinen026331c2010-02-15 12:53:10 +0200652 int err = 0;
Johannes Berg2e161f72010-08-12 15:38:38 +0200653 u16 mgmt_type;
654
655 if (!wdev->wiphy->mgmt_stypes)
656 return -EOPNOTSUPP;
657
658 if ((frame_type & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_MGMT)
659 return -EINVAL;
660
661 if (frame_type & ~(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE))
662 return -EINVAL;
663
664 mgmt_type = (frame_type & IEEE80211_FCTL_STYPE) >> 4;
665 if (!(wdev->wiphy->mgmt_stypes[wdev->iftype].rx & BIT(mgmt_type)))
666 return -EINVAL;
Jouni Malinen026331c2010-02-15 12:53:10 +0200667
668 nreg = kzalloc(sizeof(*reg) + match_len, GFP_KERNEL);
669 if (!nreg)
670 return -ENOMEM;
671
Johannes Berg2e161f72010-08-12 15:38:38 +0200672 spin_lock_bh(&wdev->mgmt_registrations_lock);
Jouni Malinen026331c2010-02-15 12:53:10 +0200673
Johannes Berg2e161f72010-08-12 15:38:38 +0200674 list_for_each_entry(reg, &wdev->mgmt_registrations, list) {
Jouni Malinen026331c2010-02-15 12:53:10 +0200675 int mlen = min(match_len, reg->match_len);
676
Johannes Berg2e161f72010-08-12 15:38:38 +0200677 if (frame_type != le16_to_cpu(reg->frame_type))
678 continue;
679
Jouni Malinen026331c2010-02-15 12:53:10 +0200680 if (memcmp(reg->match, match_data, mlen) == 0) {
681 err = -EALREADY;
682 break;
683 }
684 }
685
686 if (err) {
687 kfree(nreg);
688 goto out;
689 }
690
691 memcpy(nreg->match, match_data, match_len);
692 nreg->match_len = match_len;
Eric W. Biederman15e47302012-09-07 20:12:54 +0000693 nreg->nlportid = snd_portid;
Johannes Berg2e161f72010-08-12 15:38:38 +0200694 nreg->frame_type = cpu_to_le16(frame_type);
695 list_add(&nreg->list, &wdev->mgmt_registrations);
Jouni Malinen026331c2010-02-15 12:53:10 +0200696
Johannes Berg271733c2010-10-13 12:06:23 +0200697 if (rdev->ops->mgmt_frame_register)
Hila Gonene35e4d22012-06-27 17:19:42 +0300698 rdev_mgmt_frame_register(rdev, wdev, frame_type, true);
Johannes Berg271733c2010-10-13 12:06:23 +0200699
Jouni Malinen026331c2010-02-15 12:53:10 +0200700 out:
Johannes Berg2e161f72010-08-12 15:38:38 +0200701 spin_unlock_bh(&wdev->mgmt_registrations_lock);
Johannes Berg271733c2010-10-13 12:06:23 +0200702
Jouni Malinen026331c2010-02-15 12:53:10 +0200703 return err;
704}
705
Eric W. Biederman15e47302012-09-07 20:12:54 +0000706void cfg80211_mlme_unregister_socket(struct wireless_dev *wdev, u32 nlportid)
Jouni Malinen026331c2010-02-15 12:53:10 +0200707{
Johannes Berg271733c2010-10-13 12:06:23 +0200708 struct wiphy *wiphy = wdev->wiphy;
709 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
Johannes Berg2e161f72010-08-12 15:38:38 +0200710 struct cfg80211_mgmt_registration *reg, *tmp;
Jouni Malinen026331c2010-02-15 12:53:10 +0200711
Johannes Berg2e161f72010-08-12 15:38:38 +0200712 spin_lock_bh(&wdev->mgmt_registrations_lock);
Jouni Malinen026331c2010-02-15 12:53:10 +0200713
Johannes Berg2e161f72010-08-12 15:38:38 +0200714 list_for_each_entry_safe(reg, tmp, &wdev->mgmt_registrations, list) {
Eric W. Biederman15e47302012-09-07 20:12:54 +0000715 if (reg->nlportid != nlportid)
Johannes Berg271733c2010-10-13 12:06:23 +0200716 continue;
717
718 if (rdev->ops->mgmt_frame_register) {
719 u16 frame_type = le16_to_cpu(reg->frame_type);
720
Hila Gonene35e4d22012-06-27 17:19:42 +0300721 rdev_mgmt_frame_register(rdev, wdev,
722 frame_type, false);
Jouni Malinen026331c2010-02-15 12:53:10 +0200723 }
Johannes Berg271733c2010-10-13 12:06:23 +0200724
725 list_del(&reg->list);
726 kfree(reg);
Jouni Malinen026331c2010-02-15 12:53:10 +0200727 }
728
Johannes Berg2e161f72010-08-12 15:38:38 +0200729 spin_unlock_bh(&wdev->mgmt_registrations_lock);
Johannes Berg28946da2011-11-04 11:18:12 +0100730
Eric W. Biederman15e47302012-09-07 20:12:54 +0000731 if (nlportid == wdev->ap_unexpected_nlportid)
732 wdev->ap_unexpected_nlportid = 0;
Jouni Malinen026331c2010-02-15 12:53:10 +0200733}
734
Johannes Berg2e161f72010-08-12 15:38:38 +0200735void cfg80211_mlme_purge_registrations(struct wireless_dev *wdev)
Jouni Malinen026331c2010-02-15 12:53:10 +0200736{
Johannes Berg2e161f72010-08-12 15:38:38 +0200737 struct cfg80211_mgmt_registration *reg, *tmp;
Jouni Malinen026331c2010-02-15 12:53:10 +0200738
Johannes Berg2e161f72010-08-12 15:38:38 +0200739 spin_lock_bh(&wdev->mgmt_registrations_lock);
Jouni Malinen026331c2010-02-15 12:53:10 +0200740
Johannes Berg2e161f72010-08-12 15:38:38 +0200741 list_for_each_entry_safe(reg, tmp, &wdev->mgmt_registrations, list) {
Jouni Malinen026331c2010-02-15 12:53:10 +0200742 list_del(&reg->list);
743 kfree(reg);
744 }
745
Johannes Berg2e161f72010-08-12 15:38:38 +0200746 spin_unlock_bh(&wdev->mgmt_registrations_lock);
Jouni Malinen026331c2010-02-15 12:53:10 +0200747}
748
Johannes Berg2e161f72010-08-12 15:38:38 +0200749int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev,
Johannes Berg71bbc992012-06-15 15:30:18 +0200750 struct wireless_dev *wdev,
Johannes Bergf7ca38d2010-11-25 10:02:29 +0100751 struct ieee80211_channel *chan, bool offchan,
Johannes Berg2e161f72010-08-12 15:38:38 +0200752 enum nl80211_channel_type channel_type,
Johannes Bergf7ca38d2010-11-25 10:02:29 +0100753 bool channel_type_valid, unsigned int wait,
Rajkumar Manoharane9f935e2011-09-25 14:53:30 +0530754 const u8 *buf, size_t len, bool no_cck,
Johannes Berge247bd902011-11-04 11:18:21 +0100755 bool dont_wait_for_ack, u64 *cookie)
Jouni Malinen026331c2010-02-15 12:53:10 +0200756{
Jouni Malinen026331c2010-02-15 12:53:10 +0200757 const struct ieee80211_mgmt *mgmt;
Johannes Berg2e161f72010-08-12 15:38:38 +0200758 u16 stype;
Jouni Malinen026331c2010-02-15 12:53:10 +0200759
Johannes Berg2e161f72010-08-12 15:38:38 +0200760 if (!wdev->wiphy->mgmt_stypes)
Jouni Malinen026331c2010-02-15 12:53:10 +0200761 return -EOPNOTSUPP;
Johannes Berg2e161f72010-08-12 15:38:38 +0200762
763 if (!rdev->ops->mgmt_tx)
764 return -EOPNOTSUPP;
765
Jouni Malinen026331c2010-02-15 12:53:10 +0200766 if (len < 24 + 1)
767 return -EINVAL;
768
769 mgmt = (const struct ieee80211_mgmt *) buf;
Johannes Berg2e161f72010-08-12 15:38:38 +0200770
771 if (!ieee80211_is_mgmt(mgmt->frame_control))
Jouni Malinen026331c2010-02-15 12:53:10 +0200772 return -EINVAL;
Johannes Berg2e161f72010-08-12 15:38:38 +0200773
774 stype = le16_to_cpu(mgmt->frame_control) & IEEE80211_FCTL_STYPE;
775 if (!(wdev->wiphy->mgmt_stypes[wdev->iftype].tx & BIT(stype >> 4)))
776 return -EINVAL;
777
778 if (ieee80211_is_action(mgmt->frame_control) &&
779 mgmt->u.action.category != WLAN_CATEGORY_PUBLIC) {
Johannes Berg663fcaf2010-09-30 21:06:09 +0200780 int err = 0;
781
Johannes Bergfe100ac2010-08-09 15:52:03 +0200782 wdev_lock(wdev);
783
Johannes Berg663fcaf2010-09-30 21:06:09 +0200784 switch (wdev->iftype) {
785 case NL80211_IFTYPE_ADHOC:
786 case NL80211_IFTYPE_STATION:
787 case NL80211_IFTYPE_P2P_CLIENT:
788 if (!wdev->current_bss) {
789 err = -ENOTCONN;
790 break;
791 }
Johannes Bergfe100ac2010-08-09 15:52:03 +0200792
Joe Perchesac422d32012-05-08 18:56:55 +0000793 if (!ether_addr_equal(wdev->current_bss->pub.bssid,
794 mgmt->bssid)) {
Johannes Berg663fcaf2010-09-30 21:06:09 +0200795 err = -ENOTCONN;
796 break;
797 }
798
799 /*
800 * check for IBSS DA must be done by driver as
801 * cfg80211 doesn't track the stations
802 */
803 if (wdev->iftype == NL80211_IFTYPE_ADHOC)
804 break;
805
806 /* for station, check that DA is the AP */
Joe Perchesac422d32012-05-08 18:56:55 +0000807 if (!ether_addr_equal(wdev->current_bss->pub.bssid,
808 mgmt->da)) {
Johannes Berg663fcaf2010-09-30 21:06:09 +0200809 err = -ENOTCONN;
810 break;
811 }
812 break;
813 case NL80211_IFTYPE_AP:
814 case NL80211_IFTYPE_P2P_GO:
815 case NL80211_IFTYPE_AP_VLAN:
Johannes Berg98104fde2012-06-16 00:19:54 +0200816 if (!ether_addr_equal(mgmt->bssid, wdev_address(wdev)))
Johannes Berg663fcaf2010-09-30 21:06:09 +0200817 err = -EINVAL;
818 break;
Javier Cardona0778a6a2011-05-03 16:57:08 -0700819 case NL80211_IFTYPE_MESH_POINT:
Joe Perchesac422d32012-05-08 18:56:55 +0000820 if (!ether_addr_equal(mgmt->sa, mgmt->bssid)) {
Javier Cardona0778a6a2011-05-03 16:57:08 -0700821 err = -EINVAL;
822 break;
823 }
824 /*
825 * check for mesh DA must be done by driver as
826 * cfg80211 doesn't track the stations
827 */
828 break;
Johannes Berg98104fde2012-06-16 00:19:54 +0200829 case NL80211_IFTYPE_P2P_DEVICE:
830 /*
831 * fall through, P2P device only supports
832 * public action frames
833 */
Johannes Berg663fcaf2010-09-30 21:06:09 +0200834 default:
835 err = -EOPNOTSUPP;
836 break;
837 }
Johannes Bergfe100ac2010-08-09 15:52:03 +0200838 wdev_unlock(wdev);
Johannes Berg663fcaf2010-09-30 21:06:09 +0200839
840 if (err)
841 return err;
Jouni Malinen026331c2010-02-15 12:53:10 +0200842 }
843
Johannes Berg98104fde2012-06-16 00:19:54 +0200844 if (!ether_addr_equal(mgmt->sa, wdev_address(wdev)))
Jouni Malinen026331c2010-02-15 12:53:10 +0200845 return -EINVAL;
846
847 /* Transmit the Action frame as requested by user space */
Hila Gonene35e4d22012-06-27 17:19:42 +0300848 return rdev_mgmt_tx(rdev, wdev, chan, offchan,
849 channel_type, channel_type_valid,
850 wait, buf, len, no_cck, dont_wait_for_ack,
851 cookie);
Jouni Malinen026331c2010-02-15 12:53:10 +0200852}
853
Johannes Berg71bbc992012-06-15 15:30:18 +0200854bool cfg80211_rx_mgmt(struct wireless_dev *wdev, int freq, int sig_mbm,
Johannes Berg804483e2012-03-05 22:18:41 +0100855 const u8 *buf, size_t len, gfp_t gfp)
Jouni Malinen026331c2010-02-15 12:53:10 +0200856{
Jouni Malinen026331c2010-02-15 12:53:10 +0200857 struct wiphy *wiphy = wdev->wiphy;
858 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
Johannes Berg2e161f72010-08-12 15:38:38 +0200859 struct cfg80211_mgmt_registration *reg;
860 const struct ieee80211_txrx_stypes *stypes =
861 &wiphy->mgmt_stypes[wdev->iftype];
862 struct ieee80211_mgmt *mgmt = (void *)buf;
863 const u8 *data;
864 int data_len;
Jouni Malinen026331c2010-02-15 12:53:10 +0200865 bool result = false;
Johannes Berg2e161f72010-08-12 15:38:38 +0200866 __le16 ftype = mgmt->frame_control &
867 cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE);
868 u16 stype;
Jouni Malinen026331c2010-02-15 12:53:10 +0200869
Johannes Berg2e161f72010-08-12 15:38:38 +0200870 stype = (le16_to_cpu(mgmt->frame_control) & IEEE80211_FCTL_STYPE) >> 4;
Jouni Malinen026331c2010-02-15 12:53:10 +0200871
Johannes Berg2e161f72010-08-12 15:38:38 +0200872 if (!(stypes->rx & BIT(stype)))
873 return false;
Jouni Malinen026331c2010-02-15 12:53:10 +0200874
Johannes Berg2e161f72010-08-12 15:38:38 +0200875 data = buf + ieee80211_hdrlen(mgmt->frame_control);
876 data_len = len - ieee80211_hdrlen(mgmt->frame_control);
Jouni Malinen026331c2010-02-15 12:53:10 +0200877
Johannes Berg2e161f72010-08-12 15:38:38 +0200878 spin_lock_bh(&wdev->mgmt_registrations_lock);
879
880 list_for_each_entry(reg, &wdev->mgmt_registrations, list) {
881 if (reg->frame_type != ftype)
Jouni Malinen026331c2010-02-15 12:53:10 +0200882 continue;
883
Johannes Berg2e161f72010-08-12 15:38:38 +0200884 if (reg->match_len > data_len)
885 continue;
886
887 if (memcmp(reg->match, data, reg->match_len))
Jouni Malinen026331c2010-02-15 12:53:10 +0200888 continue;
889
890 /* found match! */
891
892 /* Indicate the received Action frame to user space */
Eric W. Biederman15e47302012-09-07 20:12:54 +0000893 if (nl80211_send_mgmt(rdev, wdev, reg->nlportid,
Johannes Berg804483e2012-03-05 22:18:41 +0100894 freq, sig_mbm,
Johannes Berg2e161f72010-08-12 15:38:38 +0200895 buf, len, gfp))
Jouni Malinen026331c2010-02-15 12:53:10 +0200896 continue;
897
898 result = true;
899 break;
900 }
901
Johannes Berg2e161f72010-08-12 15:38:38 +0200902 spin_unlock_bh(&wdev->mgmt_registrations_lock);
Jouni Malinen026331c2010-02-15 12:53:10 +0200903
904 return result;
905}
Johannes Berg2e161f72010-08-12 15:38:38 +0200906EXPORT_SYMBOL(cfg80211_rx_mgmt);
Jouni Malinen026331c2010-02-15 12:53:10 +0200907
Johannes Berg71bbc992012-06-15 15:30:18 +0200908void cfg80211_mgmt_tx_status(struct wireless_dev *wdev, u64 cookie,
Johannes Berg2e161f72010-08-12 15:38:38 +0200909 const u8 *buf, size_t len, bool ack, gfp_t gfp)
Jouni Malinen026331c2010-02-15 12:53:10 +0200910{
Jouni Malinen026331c2010-02-15 12:53:10 +0200911 struct wiphy *wiphy = wdev->wiphy;
912 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
913
914 /* Indicate TX status of the Action frame to user space */
Johannes Berg71bbc992012-06-15 15:30:18 +0200915 nl80211_send_mgmt_tx_status(rdev, wdev, cookie, buf, len, ack, gfp);
Jouni Malinen026331c2010-02-15 12:53:10 +0200916}
Johannes Berg2e161f72010-08-12 15:38:38 +0200917EXPORT_SYMBOL(cfg80211_mgmt_tx_status);
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +0200918
919void cfg80211_cqm_rssi_notify(struct net_device *dev,
920 enum nl80211_cqm_rssi_threshold_event rssi_event,
921 gfp_t gfp)
922{
923 struct wireless_dev *wdev = dev->ieee80211_ptr;
924 struct wiphy *wiphy = wdev->wiphy;
925 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
926
927 /* Indicate roaming trigger event to user space */
928 nl80211_send_cqm_rssi_notify(rdev, dev, rssi_event, gfp);
929}
930EXPORT_SYMBOL(cfg80211_cqm_rssi_notify);
Johannes Bergc063dbf2010-11-24 08:10:05 +0100931
932void cfg80211_cqm_pktloss_notify(struct net_device *dev,
933 const u8 *peer, u32 num_packets, gfp_t gfp)
934{
935 struct wireless_dev *wdev = dev->ieee80211_ptr;
936 struct wiphy *wiphy = wdev->wiphy;
937 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
938
939 /* Indicate roaming trigger event to user space */
940 nl80211_send_cqm_pktloss_notify(rdev, dev, peer, num_packets, gfp);
941}
942EXPORT_SYMBOL(cfg80211_cqm_pktloss_notify);
Johannes Berge5497d72011-07-05 16:35:40 +0200943
Thomas Pedersen84f10702012-07-12 16:17:33 -0700944void cfg80211_cqm_txe_notify(struct net_device *dev,
945 const u8 *peer, u32 num_packets,
946 u32 rate, u32 intvl, gfp_t gfp)
947{
948 struct wireless_dev *wdev = dev->ieee80211_ptr;
949 struct wiphy *wiphy = wdev->wiphy;
950 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
951
952 nl80211_send_cqm_txe_notify(rdev, dev, peer, num_packets,
953 rate, intvl, gfp);
954}
955EXPORT_SYMBOL(cfg80211_cqm_txe_notify);
956
Johannes Berge5497d72011-07-05 16:35:40 +0200957void cfg80211_gtk_rekey_notify(struct net_device *dev, const u8 *bssid,
958 const u8 *replay_ctr, gfp_t gfp)
959{
960 struct wireless_dev *wdev = dev->ieee80211_ptr;
961 struct wiphy *wiphy = wdev->wiphy;
962 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
963
964 nl80211_gtk_rekey_notify(rdev, dev, bssid, replay_ctr, gfp);
965}
966EXPORT_SYMBOL(cfg80211_gtk_rekey_notify);
Jouni Malinenc9df56b2011-09-16 18:56:23 +0300967
968void cfg80211_pmksa_candidate_notify(struct net_device *dev, int index,
969 const u8 *bssid, bool preauth, gfp_t gfp)
970{
971 struct wireless_dev *wdev = dev->ieee80211_ptr;
972 struct wiphy *wiphy = wdev->wiphy;
973 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
974
975 nl80211_pmksa_candidate_notify(rdev, dev, index, bssid, preauth, gfp);
976}
977EXPORT_SYMBOL(cfg80211_pmksa_candidate_notify);
Johannes Berg28946da2011-11-04 11:18:12 +0100978
Thomas Pedersen53145262012-04-06 13:35:47 -0700979void cfg80211_ch_switch_notify(struct net_device *dev, int freq,
980 enum nl80211_channel_type type)
981{
982 struct wireless_dev *wdev = dev->ieee80211_ptr;
983 struct wiphy *wiphy = wdev->wiphy;
984 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
985 struct ieee80211_channel *chan;
986
987 wdev_lock(wdev);
988
989 if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP &&
990 wdev->iftype != NL80211_IFTYPE_P2P_GO))
991 goto out;
992
993 chan = rdev_freq_to_chan(rdev, freq, type);
994 if (WARN_ON(!chan))
995 goto out;
996
Michal Kaziorf4489eb2012-06-29 12:46:58 +0200997 wdev->channel = chan;
Thomas Pedersen53145262012-04-06 13:35:47 -0700998 nl80211_ch_switch_notify(rdev, dev, freq, type, GFP_KERNEL);
999out:
1000 wdev_unlock(wdev);
1001 return;
1002}
1003EXPORT_SYMBOL(cfg80211_ch_switch_notify);
1004
Johannes Berg28946da2011-11-04 11:18:12 +01001005bool cfg80211_rx_spurious_frame(struct net_device *dev,
1006 const u8 *addr, gfp_t gfp)
1007{
1008 struct wireless_dev *wdev = dev->ieee80211_ptr;
1009
1010 if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP &&
1011 wdev->iftype != NL80211_IFTYPE_P2P_GO))
1012 return false;
1013
1014 return nl80211_unexpected_frame(dev, addr, gfp);
1015}
1016EXPORT_SYMBOL(cfg80211_rx_spurious_frame);
Johannes Bergb92ab5d2011-11-04 11:18:19 +01001017
1018bool cfg80211_rx_unexpected_4addr_frame(struct net_device *dev,
1019 const u8 *addr, gfp_t gfp)
1020{
1021 struct wireless_dev *wdev = dev->ieee80211_ptr;
1022
1023 if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP &&
1024 wdev->iftype != NL80211_IFTYPE_P2P_GO &&
1025 wdev->iftype != NL80211_IFTYPE_AP_VLAN))
1026 return false;
1027
1028 return nl80211_unexpected_4addr_frame(dev, addr, gfp);
1029}
1030EXPORT_SYMBOL(cfg80211_rx_unexpected_4addr_frame);