blob: f5a7ac3a0939ab0e782bf31bc538c0fa2a22cb3d [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>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090011#include <linux/slab.h>
Johannes Berga9a11622009-07-27 12:01:53 +020012#include <linux/wireless.h>
Jouni Malinen6039f6d2009-03-19 13:39:21 +020013#include <net/cfg80211.h>
Johannes Berga9a11622009-07-27 12:01:53 +020014#include <net/iw_handler.h>
Jouni Malinen6039f6d2009-03-19 13:39:21 +020015#include "core.h"
16#include "nl80211.h"
17
Johannes Bergcb0b4be2009-07-07 03:56:07 +020018void cfg80211_send_rx_auth(struct net_device *dev, const u8 *buf, size_t len)
Jouni Malinen6039f6d2009-03-19 13:39:21 +020019{
Johannes Berg19957bb2009-07-02 17:20:43 +020020 struct wireless_dev *wdev = dev->ieee80211_ptr;
21 struct wiphy *wiphy = wdev->wiphy;
Jouni Malinen6039f6d2009-03-19 13:39:21 +020022 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
Johannes Berg19957bb2009-07-02 17:20:43 +020023
Johannes Berg667503d2009-07-07 03:56:11 +020024 wdev_lock(wdev);
Johannes Bergcb0b4be2009-07-07 03:56:07 +020025
Johannes Berg95de8172012-01-20 13:55:25 +010026 nl80211_send_rx_auth(rdev, dev, buf, len, GFP_KERNEL);
27 cfg80211_sme_rx_auth(dev, buf, len);
Johannes Berg667503d2009-07-07 03:56:11 +020028
29 wdev_unlock(wdev);
Jouni Malinen6039f6d2009-03-19 13:39:21 +020030}
31EXPORT_SYMBOL(cfg80211_send_rx_auth);
32
Johannes Berg95de8172012-01-20 13:55:25 +010033void cfg80211_send_rx_assoc(struct net_device *dev, struct cfg80211_bss *bss,
34 const u8 *buf, size_t len)
Jouni Malinen6039f6d2009-03-19 13:39:21 +020035{
Johannes Berg6829c872009-07-02 09:13:27 +020036 u16 status_code;
37 struct wireless_dev *wdev = dev->ieee80211_ptr;
38 struct wiphy *wiphy = wdev->wiphy;
Jouni Malinen6039f6d2009-03-19 13:39:21 +020039 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
Johannes Berg6829c872009-07-02 09:13:27 +020040 struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf;
41 u8 *ie = mgmt->u.assoc_resp.variable;
Johannes Berg95de8172012-01-20 13:55:25 +010042 int ieoffs = offsetof(struct ieee80211_mgmt, u.assoc_resp.variable);
Johannes Berg6829c872009-07-02 09:13:27 +020043
Johannes Berg667503d2009-07-07 03:56:11 +020044 wdev_lock(wdev);
Johannes Bergcb0b4be2009-07-07 03:56:07 +020045
Johannes Berg6829c872009-07-02 09:13:27 +020046 status_code = le16_to_cpu(mgmt->u.assoc_resp.status_code);
47
Johannes Bergf401a6f2009-08-07 14:51:05 +020048 /*
49 * This is a bit of a hack, we don't notify userspace of
50 * a (re-)association reply if we tried to send a reassoc
51 * and got a reject -- we only try again with an assoc
52 * frame instead of reassoc.
53 */
54 if (status_code != WLAN_STATUS_SUCCESS && wdev->conn &&
Johannes Berg95de8172012-01-20 13:55:25 +010055 cfg80211_sme_failed_reassoc(wdev)) {
56 cfg80211_put_bss(bss);
Johannes Bergf401a6f2009-08-07 14:51:05 +020057 goto out;
Johannes Berg95de8172012-01-20 13:55:25 +010058 }
Johannes Bergf401a6f2009-08-07 14:51:05 +020059
Johannes Bergcb0b4be2009-07-07 03:56:07 +020060 nl80211_send_rx_assoc(rdev, dev, buf, len, GFP_KERNEL);
Johannes Berg6829c872009-07-02 09:13:27 +020061
Johannes Berg95de8172012-01-20 13:55:25 +010062 if (status_code != WLAN_STATUS_SUCCESS && wdev->conn) {
Johannes Berg7d930bc2009-10-20 15:08:53 +090063 cfg80211_sme_failed_assoc(wdev);
Johannes Berg7d930bc2009-10-20 15:08:53 +090064 /*
65 * do not call connect_result() now because the
66 * sme will schedule work that does it later.
67 */
Johannes Berg95de8172012-01-20 13:55:25 +010068 cfg80211_put_bss(bss);
Johannes Berg7d930bc2009-10-20 15:08:53 +090069 goto out;
Johannes Bergdf7fc0f2009-07-29 11:23:49 +020070 }
71
Johannes Bergea416a72009-08-17 12:22:14 +020072 if (!wdev->conn && wdev->sme_state == CFG80211_SME_IDLE) {
73 /*
74 * This is for the userspace SME, the CONNECTING
75 * state will be changed to CONNECTED by
76 * __cfg80211_connect_result() below.
77 */
78 wdev->sme_state = CFG80211_SME_CONNECTING;
79 }
80
Johannes Berg95de8172012-01-20 13:55:25 +010081 /* this consumes the bss reference */
Johannes Bergdf7fc0f2009-07-29 11:23:49 +020082 __cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, ie, len - ieoffs,
83 status_code,
Johannes Berg95de8172012-01-20 13:55:25 +010084 status_code == WLAN_STATUS_SUCCESS, bss);
Johannes Bergf401a6f2009-08-07 14:51:05 +020085 out:
Johannes Berg667503d2009-07-07 03:56:11 +020086 wdev_unlock(wdev);
Jouni Malinen6039f6d2009-03-19 13:39:21 +020087}
88EXPORT_SYMBOL(cfg80211_send_rx_assoc);
89
Holger Schurigce470613c2009-10-13 13:28:13 +020090void __cfg80211_send_deauth(struct net_device *dev,
Johannes Berg667503d2009-07-07 03:56:11 +020091 const u8 *buf, size_t len)
Jouni Malinen6039f6d2009-03-19 13:39:21 +020092{
Johannes Berg6829c872009-07-02 09:13:27 +020093 struct wireless_dev *wdev = dev->ieee80211_ptr;
94 struct wiphy *wiphy = wdev->wiphy;
Jouni Malinen6039f6d2009-03-19 13:39:21 +020095 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
Johannes Berg6829c872009-07-02 09:13:27 +020096 struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf;
Johannes Berg19957bb2009-07-02 17:20:43 +020097 const u8 *bssid = mgmt->bssid;
Johannes Berg95de8172012-01-20 13:55:25 +010098 bool was_current = false;
Johannes Berg6829c872009-07-02 09:13:27 +020099
Johannes Berg667503d2009-07-07 03:56:11 +0200100 ASSERT_WDEV_LOCK(wdev);
Johannes Bergcb0b4be2009-07-07 03:56:07 +0200101
Johannes Berg19957bb2009-07-02 17:20:43 +0200102 if (wdev->current_bss &&
103 memcmp(wdev->current_bss->pub.bssid, bssid, ETH_ALEN) == 0) {
Johannes Berg19957bb2009-07-02 17:20:43 +0200104 cfg80211_unhold_bss(wdev->current_bss);
105 cfg80211_put_bss(&wdev->current_bss->pub);
106 wdev->current_bss = NULL;
Johannes Berg3f3b6a82010-08-05 10:20:27 +0200107 was_current = true;
Johannes Berg19957bb2009-07-02 17:20:43 +0200108 }
Johannes Berg19957bb2009-07-02 17:20:43 +0200109
Johannes Berg5fba4af2009-12-02 12:43:42 +0100110 nl80211_send_deauth(rdev, dev, buf, len, GFP_KERNEL);
111
Johannes Berg3f3b6a82010-08-05 10:20:27 +0200112 if (wdev->sme_state == CFG80211_SME_CONNECTED && was_current) {
Johannes Berg6829c872009-07-02 09:13:27 +0200113 u16 reason_code;
114 bool from_ap;
115
116 reason_code = le16_to_cpu(mgmt->u.deauth.reason_code);
117
Johannes Berge458b8a2009-08-06 20:41:33 +0200118 from_ap = memcmp(mgmt->sa, dev->dev_addr, ETH_ALEN) != 0;
Johannes Berg667503d2009-07-07 03:56:11 +0200119 __cfg80211_disconnected(dev, NULL, 0, reason_code, from_ap);
Johannes Berg6829c872009-07-02 09:13:27 +0200120 } else if (wdev->sme_state == CFG80211_SME_CONNECTING) {
Johannes Berg667503d2009-07-07 03:56:11 +0200121 __cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, NULL, 0,
122 WLAN_STATUS_UNSPECIFIED_FAILURE,
Johannes Bergdf7fc0f2009-07-29 11:23:49 +0200123 false, NULL);
Johannes Berg667503d2009-07-07 03:56:11 +0200124 }
125}
Holger Schurigce470613c2009-10-13 13:28:13 +0200126EXPORT_SYMBOL(__cfg80211_send_deauth);
Johannes Berg667503d2009-07-07 03:56:11 +0200127
Holger Schurigce470613c2009-10-13 13:28:13 +0200128void cfg80211_send_deauth(struct net_device *dev, const u8 *buf, size_t len)
Johannes Berg667503d2009-07-07 03:56:11 +0200129{
130 struct wireless_dev *wdev = dev->ieee80211_ptr;
131
Holger Schurigce470613c2009-10-13 13:28:13 +0200132 wdev_lock(wdev);
133 __cfg80211_send_deauth(dev, buf, len);
134 wdev_unlock(wdev);
Jouni Malinen6039f6d2009-03-19 13:39:21 +0200135}
Jouni Malinen53b46b82009-03-27 20:53:56 +0200136EXPORT_SYMBOL(cfg80211_send_deauth);
Jouni Malinen6039f6d2009-03-19 13:39:21 +0200137
Holger Schurigce470613c2009-10-13 13:28:13 +0200138void __cfg80211_send_disassoc(struct net_device *dev,
Johannes Berg667503d2009-07-07 03:56:11 +0200139 const u8 *buf, size_t len)
Jouni Malinen6039f6d2009-03-19 13:39:21 +0200140{
Johannes Berg6829c872009-07-02 09:13:27 +0200141 struct wireless_dev *wdev = dev->ieee80211_ptr;
142 struct wiphy *wiphy = wdev->wiphy;
Jouni Malinen6039f6d2009-03-19 13:39:21 +0200143 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
Johannes Berg6829c872009-07-02 09:13:27 +0200144 struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf;
Johannes Berg19957bb2009-07-02 17:20:43 +0200145 const u8 *bssid = mgmt->bssid;
Johannes Berg19957bb2009-07-02 17:20:43 +0200146 u16 reason_code;
147 bool from_ap;
Johannes Berg6829c872009-07-02 09:13:27 +0200148
Johannes Berg596a07c2009-07-11 00:17:32 +0200149 ASSERT_WDEV_LOCK(wdev);
Johannes Bergcb0b4be2009-07-07 03:56:07 +0200150
151 nl80211_send_disassoc(rdev, dev, buf, len, GFP_KERNEL);
Johannes Berg6829c872009-07-02 09:13:27 +0200152
Johannes Berg596a07c2009-07-11 00:17:32 +0200153 if (wdev->sme_state != CFG80211_SME_CONNECTED)
154 return;
Johannes Berg6829c872009-07-02 09:13:27 +0200155
Johannes Berg19957bb2009-07-02 17:20:43 +0200156 if (wdev->current_bss &&
Pavel Roskinb935df02009-08-06 04:52:42 -0400157 memcmp(wdev->current_bss->pub.bssid, bssid, ETH_ALEN) == 0) {
Johannes Berg95de8172012-01-20 13:55:25 +0100158 cfg80211_sme_disassoc(dev, wdev->current_bss);
159 cfg80211_unhold_bss(wdev->current_bss);
160 cfg80211_put_bss(&wdev->current_bss->pub);
161 wdev->current_bss = NULL;
Johannes Berg19957bb2009-07-02 17:20:43 +0200162 } else
163 WARN_ON(1);
Johannes Berg6829c872009-07-02 09:13:27 +0200164
Johannes Berg6829c872009-07-02 09:13:27 +0200165
Johannes Berg19957bb2009-07-02 17:20:43 +0200166 reason_code = le16_to_cpu(mgmt->u.disassoc.reason_code);
167
Johannes Berge458b8a2009-08-06 20:41:33 +0200168 from_ap = memcmp(mgmt->sa, dev->dev_addr, ETH_ALEN) != 0;
Johannes Berg667503d2009-07-07 03:56:11 +0200169 __cfg80211_disconnected(dev, NULL, 0, reason_code, from_ap);
Johannes Berg667503d2009-07-07 03:56:11 +0200170}
Holger Schurigce470613c2009-10-13 13:28:13 +0200171EXPORT_SYMBOL(__cfg80211_send_disassoc);
Johannes Berg667503d2009-07-07 03:56:11 +0200172
Holger Schurigce470613c2009-10-13 13:28:13 +0200173void cfg80211_send_disassoc(struct net_device *dev, const u8 *buf, size_t len)
Johannes Berg667503d2009-07-07 03:56:11 +0200174{
175 struct wireless_dev *wdev = dev->ieee80211_ptr;
176
Holger Schurigce470613c2009-10-13 13:28:13 +0200177 wdev_lock(wdev);
178 __cfg80211_send_disassoc(dev, buf, len);
179 wdev_unlock(wdev);
Jouni Malinen6039f6d2009-03-19 13:39:21 +0200180}
Jouni Malinen53b46b82009-03-27 20:53:56 +0200181EXPORT_SYMBOL(cfg80211_send_disassoc);
Jouni Malinena3b8b052009-03-27 21:59:49 +0200182
Jouni Malinencf4e5942010-12-16 00:52:40 +0200183void cfg80211_send_unprot_deauth(struct net_device *dev, const u8 *buf,
184 size_t len)
185{
186 struct wireless_dev *wdev = dev->ieee80211_ptr;
187 struct wiphy *wiphy = wdev->wiphy;
188 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
189
190 nl80211_send_unprot_deauth(rdev, dev, buf, len, GFP_ATOMIC);
191}
192EXPORT_SYMBOL(cfg80211_send_unprot_deauth);
193
194void cfg80211_send_unprot_disassoc(struct net_device *dev, const u8 *buf,
195 size_t len)
196{
197 struct wireless_dev *wdev = dev->ieee80211_ptr;
198 struct wiphy *wiphy = wdev->wiphy;
199 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
200
201 nl80211_send_unprot_disassoc(rdev, dev, buf, len, GFP_ATOMIC);
202}
203EXPORT_SYMBOL(cfg80211_send_unprot_disassoc);
204
Johannes Berga58ce432009-11-19 12:45:42 +0100205void cfg80211_send_auth_timeout(struct net_device *dev, const u8 *addr)
206{
207 struct wireless_dev *wdev = dev->ieee80211_ptr;
208 struct wiphy *wiphy = wdev->wiphy;
209 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
210
211 wdev_lock(wdev);
212
213 nl80211_send_auth_timeout(rdev, dev, addr, GFP_KERNEL);
214 if (wdev->sme_state == CFG80211_SME_CONNECTING)
215 __cfg80211_connect_result(dev, addr, NULL, 0, NULL, 0,
216 WLAN_STATUS_UNSPECIFIED_FAILURE,
217 false, NULL);
218
Johannes Berg667503d2009-07-07 03:56:11 +0200219 wdev_unlock(wdev);
Jouni Malinen1965c852009-04-22 21:38:25 +0300220}
221EXPORT_SYMBOL(cfg80211_send_auth_timeout);
222
Johannes Bergcb0b4be2009-07-07 03:56:07 +0200223void cfg80211_send_assoc_timeout(struct net_device *dev, const u8 *addr)
Jouni Malinen1965c852009-04-22 21:38:25 +0300224{
Johannes Berg6829c872009-07-02 09:13:27 +0200225 struct wireless_dev *wdev = dev->ieee80211_ptr;
226 struct wiphy *wiphy = wdev->wiphy;
Jouni Malinen1965c852009-04-22 21:38:25 +0300227 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
Johannes Berg19957bb2009-07-02 17:20:43 +0200228
Johannes Berg667503d2009-07-07 03:56:11 +0200229 wdev_lock(wdev);
Johannes Bergcb0b4be2009-07-07 03:56:07 +0200230
231 nl80211_send_assoc_timeout(rdev, dev, addr, GFP_KERNEL);
Johannes Berg6829c872009-07-02 09:13:27 +0200232 if (wdev->sme_state == CFG80211_SME_CONNECTING)
Johannes Berg667503d2009-07-07 03:56:11 +0200233 __cfg80211_connect_result(dev, addr, NULL, 0, NULL, 0,
234 WLAN_STATUS_UNSPECIFIED_FAILURE,
Johannes Bergdf7fc0f2009-07-29 11:23:49 +0200235 false, NULL);
Johannes Berg19957bb2009-07-02 17:20:43 +0200236
Johannes Berg667503d2009-07-07 03:56:11 +0200237 wdev_unlock(wdev);
Jouni Malinen1965c852009-04-22 21:38:25 +0300238}
239EXPORT_SYMBOL(cfg80211_send_assoc_timeout);
240
Jouni Malinena3b8b052009-03-27 21:59:49 +0200241void cfg80211_michael_mic_failure(struct net_device *dev, const u8 *addr,
242 enum nl80211_key_type key_type, int key_id,
Johannes Berge6d6e342009-07-01 21:26:47 +0200243 const u8 *tsc, gfp_t gfp)
Jouni Malinena3b8b052009-03-27 21:59:49 +0200244{
245 struct wiphy *wiphy = dev->ieee80211_ptr->wiphy;
246 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
Johannes Berg3d23e342009-09-29 23:27:28 +0200247#ifdef CONFIG_CFG80211_WEXT
Johannes Bergf58d4ed2009-06-19 02:45:21 +0200248 union iwreq_data wrqu;
Johannes Berge6d6e342009-07-01 21:26:47 +0200249 char *buf = kmalloc(128, gfp);
Johannes Bergf58d4ed2009-06-19 02:45:21 +0200250
251 if (buf) {
252 sprintf(buf, "MLME-MICHAELMICFAILURE.indication("
253 "keyid=%d %scast addr=%pM)", key_id,
254 key_type == NL80211_KEYTYPE_GROUP ? "broad" : "uni",
255 addr);
256 memset(&wrqu, 0, sizeof(wrqu));
257 wrqu.data.length = strlen(buf);
258 wireless_send_event(dev, IWEVCUSTOM, &wrqu, buf);
259 kfree(buf);
260 }
261#endif
262
Johannes Berge6d6e342009-07-01 21:26:47 +0200263 nl80211_michael_mic_failure(rdev, dev, addr, key_type, key_id, tsc, gfp);
Jouni Malinena3b8b052009-03-27 21:59:49 +0200264}
265EXPORT_SYMBOL(cfg80211_michael_mic_failure);
Johannes Berg19957bb2009-07-02 17:20:43 +0200266
267/* some MLME handling for userspace SME */
Johannes Berg667503d2009-07-07 03:56:11 +0200268int __cfg80211_mlme_auth(struct cfg80211_registered_device *rdev,
269 struct net_device *dev,
270 struct ieee80211_channel *chan,
271 enum nl80211_auth_type auth_type,
272 const u8 *bssid,
273 const u8 *ssid, int ssid_len,
Johannes Bergfffd0932009-07-08 14:22:54 +0200274 const u8 *ie, int ie_len,
Johannes Berg95de8172012-01-20 13:55:25 +0100275 const u8 *key, int key_len, int key_idx)
Johannes Berg19957bb2009-07-02 17:20:43 +0200276{
277 struct wireless_dev *wdev = dev->ieee80211_ptr;
278 struct cfg80211_auth_request req;
Johannes Berg95de8172012-01-20 13:55:25 +0100279 int err;
Johannes Berg19957bb2009-07-02 17:20:43 +0200280
Johannes Berg667503d2009-07-07 03:56:11 +0200281 ASSERT_WDEV_LOCK(wdev);
282
Johannes Bergfffd0932009-07-08 14:22:54 +0200283 if (auth_type == NL80211_AUTHTYPE_SHARED_KEY)
284 if (!key || !key_len || key_idx < 0 || key_idx > 4)
285 return -EINVAL;
286
Johannes Berg0a9b5e12009-07-02 18:26:18 +0200287 if (wdev->current_bss &&
288 memcmp(bssid, wdev->current_bss->pub.bssid, ETH_ALEN) == 0)
289 return -EALREADY;
290
Johannes Berg19957bb2009-07-02 17:20:43 +0200291 memset(&req, 0, sizeof(req));
292
293 req.ie = ie;
294 req.ie_len = ie_len;
295 req.auth_type = auth_type;
296 req.bss = cfg80211_get_bss(&rdev->wiphy, chan, bssid, ssid, ssid_len,
297 WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS);
Johannes Bergfffd0932009-07-08 14:22:54 +0200298 req.key = key;
299 req.key_len = key_len;
300 req.key_idx = key_idx;
Johannes Berg19957bb2009-07-02 17:20:43 +0200301 if (!req.bss)
302 return -ENOENT;
303
Johannes Berg19957bb2009-07-02 17:20:43 +0200304 err = rdev->ops->auth(&rdev->wiphy, dev, &req);
Johannes Berg19957bb2009-07-02 17:20:43 +0200305
Johannes Berg95de8172012-01-20 13:55:25 +0100306 cfg80211_put_bss(req.bss);
Johannes Berg19957bb2009-07-02 17:20:43 +0200307 return err;
308}
309
Johannes Berg667503d2009-07-07 03:56:11 +0200310int cfg80211_mlme_auth(struct cfg80211_registered_device *rdev,
311 struct net_device *dev, struct ieee80211_channel *chan,
312 enum nl80211_auth_type auth_type, const u8 *bssid,
313 const u8 *ssid, int ssid_len,
Johannes Bergfffd0932009-07-08 14:22:54 +0200314 const u8 *ie, int ie_len,
Johannes Berg95de8172012-01-20 13:55:25 +0100315 const u8 *key, int key_len, int key_idx)
Johannes Berg667503d2009-07-07 03:56:11 +0200316{
317 int err;
318
319 wdev_lock(dev->ieee80211_ptr);
320 err = __cfg80211_mlme_auth(rdev, dev, chan, auth_type, bssid,
Johannes Bergfffd0932009-07-08 14:22:54 +0200321 ssid, ssid_len, ie, ie_len,
Johannes Berg95de8172012-01-20 13:55:25 +0100322 key, key_len, key_idx);
Johannes Berg667503d2009-07-07 03:56:11 +0200323 wdev_unlock(dev->ieee80211_ptr);
324
325 return err;
326}
327
Ben Greear7e7c8922011-11-18 11:31:59 -0800328/* Do a logical ht_capa &= ht_capa_mask. */
329void cfg80211_oper_and_ht_capa(struct ieee80211_ht_cap *ht_capa,
330 const struct ieee80211_ht_cap *ht_capa_mask)
331{
332 int i;
333 u8 *p1, *p2;
334 if (!ht_capa_mask) {
335 memset(ht_capa, 0, sizeof(*ht_capa));
336 return;
337 }
338
339 p1 = (u8*)(ht_capa);
340 p2 = (u8*)(ht_capa_mask);
341 for (i = 0; i<sizeof(*ht_capa); i++)
342 p1[i] &= p2[i];
343}
344
Johannes Berg667503d2009-07-07 03:56:11 +0200345int __cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev,
346 struct net_device *dev,
347 struct ieee80211_channel *chan,
348 const u8 *bssid, const u8 *prev_bssid,
349 const u8 *ssid, int ssid_len,
350 const u8 *ie, int ie_len, bool use_mfp,
Ben Greear7e7c8922011-11-18 11:31:59 -0800351 struct cfg80211_crypto_settings *crypt,
352 u32 assoc_flags, struct ieee80211_ht_cap *ht_capa,
353 struct ieee80211_ht_cap *ht_capa_mask)
Johannes Berg19957bb2009-07-02 17:20:43 +0200354{
355 struct wireless_dev *wdev = dev->ieee80211_ptr;
356 struct cfg80211_assoc_request req;
Johannes Berg95de8172012-01-20 13:55:25 +0100357 int err;
Jouni Malinen24b6b152009-11-17 21:35:38 +0200358 bool was_connected = false;
Johannes Berg19957bb2009-07-02 17:20:43 +0200359
Johannes Berg667503d2009-07-07 03:56:11 +0200360 ASSERT_WDEV_LOCK(wdev);
361
Johannes Berg19957bb2009-07-02 17:20:43 +0200362 memset(&req, 0, sizeof(req));
363
Jouni Malinen24b6b152009-11-17 21:35:38 +0200364 if (wdev->current_bss && prev_bssid &&
365 memcmp(wdev->current_bss->pub.bssid, prev_bssid, ETH_ALEN) == 0) {
366 /*
367 * Trying to reassociate: Allow this to proceed and let the old
368 * association to be dropped when the new one is completed.
369 */
370 if (wdev->sme_state == CFG80211_SME_CONNECTED) {
371 was_connected = true;
372 wdev->sme_state = CFG80211_SME_CONNECTING;
373 }
374 } else if (wdev->current_bss)
Johannes Berg19957bb2009-07-02 17:20:43 +0200375 return -EALREADY;
376
377 req.ie = ie;
378 req.ie_len = ie_len;
379 memcpy(&req.crypto, crypt, sizeof(req.crypto));
380 req.use_mfp = use_mfp;
Johannes Berg3e5d7642009-07-07 14:37:26 +0200381 req.prev_bssid = prev_bssid;
Ben Greear7e7c8922011-11-18 11:31:59 -0800382 req.flags = assoc_flags;
383 if (ht_capa)
384 memcpy(&req.ht_capa, ht_capa, sizeof(req.ht_capa));
385 if (ht_capa_mask)
386 memcpy(&req.ht_capa_mask, ht_capa_mask,
387 sizeof(req.ht_capa_mask));
388 cfg80211_oper_and_ht_capa(&req.ht_capa_mask,
389 rdev->wiphy.ht_capa_mod_mask);
390
Johannes Berg19957bb2009-07-02 17:20:43 +0200391 req.bss = cfg80211_get_bss(&rdev->wiphy, chan, bssid, ssid, ssid_len,
392 WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS);
Jouni Malinen24b6b152009-11-17 21:35:38 +0200393 if (!req.bss) {
394 if (was_connected)
395 wdev->sme_state = CFG80211_SME_CONNECTED;
Johannes Berg19957bb2009-07-02 17:20:43 +0200396 return -ENOENT;
Jouni Malinen24b6b152009-11-17 21:35:38 +0200397 }
Johannes Berg19957bb2009-07-02 17:20:43 +0200398
Johannes Berg19957bb2009-07-02 17:20:43 +0200399 err = rdev->ops->assoc(&rdev->wiphy, dev, &req);
Johannes Berg95de8172012-01-20 13:55:25 +0100400
401 if (err) {
402 if (was_connected)
403 wdev->sme_state = CFG80211_SME_CONNECTED;
404 cfg80211_put_bss(req.bss);
405 }
406
Johannes Berg19957bb2009-07-02 17:20:43 +0200407 return err;
408}
409
Johannes Berg667503d2009-07-07 03:56:11 +0200410int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev,
411 struct net_device *dev,
412 struct ieee80211_channel *chan,
413 const u8 *bssid, const u8 *prev_bssid,
414 const u8 *ssid, int ssid_len,
415 const u8 *ie, int ie_len, bool use_mfp,
Ben Greear7e7c8922011-11-18 11:31:59 -0800416 struct cfg80211_crypto_settings *crypt,
417 u32 assoc_flags, struct ieee80211_ht_cap *ht_capa,
418 struct ieee80211_ht_cap *ht_capa_mask)
Johannes Berg667503d2009-07-07 03:56:11 +0200419{
420 struct wireless_dev *wdev = dev->ieee80211_ptr;
421 int err;
422
423 wdev_lock(wdev);
424 err = __cfg80211_mlme_assoc(rdev, dev, chan, bssid, prev_bssid,
Ben Greear7e7c8922011-11-18 11:31:59 -0800425 ssid, ssid_len, ie, ie_len, use_mfp, crypt,
426 assoc_flags, ht_capa, ht_capa_mask);
Johannes Berg667503d2009-07-07 03:56:11 +0200427 wdev_unlock(wdev);
428
429 return err;
430}
431
432int __cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev,
433 struct net_device *dev, const u8 *bssid,
Jouni Malinend5cdfac2010-04-04 09:37:19 +0300434 const u8 *ie, int ie_len, u16 reason,
435 bool local_state_change)
Johannes Berg19957bb2009-07-02 17:20:43 +0200436{
437 struct wireless_dev *wdev = dev->ieee80211_ptr;
Johannes Berg95de8172012-01-20 13:55:25 +0100438 struct cfg80211_deauth_request req = {
439 .bssid = bssid,
440 .reason_code = reason,
441 .ie = ie,
442 .ie_len = ie_len,
443 };
Johannes Berg19957bb2009-07-02 17:20:43 +0200444
Johannes Berg667503d2009-07-07 03:56:11 +0200445 ASSERT_WDEV_LOCK(wdev);
446
Johannes Berg95de8172012-01-20 13:55:25 +0100447 if (local_state_change) {
448 if (wdev->current_bss &&
449 memcmp(wdev->current_bss->pub.bssid, bssid, ETH_ALEN) == 0) {
450 cfg80211_unhold_bss(wdev->current_bss);
451 cfg80211_put_bss(&wdev->current_bss->pub);
452 wdev->current_bss = NULL;
Johannes Berg19957bb2009-07-02 17:20:43 +0200453 }
Johannes Berg19957bb2009-07-02 17:20:43 +0200454
Johannes Berg95de8172012-01-20 13:55:25 +0100455 return 0;
456 }
Johannes Berg19957bb2009-07-02 17:20:43 +0200457
Johannes Berg63c9c5e2012-02-24 13:50:51 +0100458 return rdev->ops->deauth(&rdev->wiphy, dev, &req);
Johannes Berg19957bb2009-07-02 17:20:43 +0200459}
460
Johannes Berg667503d2009-07-07 03:56:11 +0200461int cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev,
462 struct net_device *dev, const u8 *bssid,
Jouni Malinend5cdfac2010-04-04 09:37:19 +0300463 const u8 *ie, int ie_len, u16 reason,
464 bool local_state_change)
Johannes Berg667503d2009-07-07 03:56:11 +0200465{
466 struct wireless_dev *wdev = dev->ieee80211_ptr;
467 int err;
468
469 wdev_lock(wdev);
Jouni Malinend5cdfac2010-04-04 09:37:19 +0300470 err = __cfg80211_mlme_deauth(rdev, dev, bssid, ie, ie_len, reason,
471 local_state_change);
Johannes Berg667503d2009-07-07 03:56:11 +0200472 wdev_unlock(wdev);
473
474 return err;
475}
476
477static int __cfg80211_mlme_disassoc(struct cfg80211_registered_device *rdev,
478 struct net_device *dev, const u8 *bssid,
Jouni Malinend5cdfac2010-04-04 09:37:19 +0300479 const u8 *ie, int ie_len, u16 reason,
480 bool local_state_change)
Johannes Berg19957bb2009-07-02 17:20:43 +0200481{
482 struct wireless_dev *wdev = dev->ieee80211_ptr;
483 struct cfg80211_disassoc_request req;
484
Johannes Berg667503d2009-07-07 03:56:11 +0200485 ASSERT_WDEV_LOCK(wdev);
486
Johannes Bergf9d6b402009-07-27 10:22:28 +0200487 if (wdev->sme_state != CFG80211_SME_CONNECTED)
488 return -ENOTCONN;
489
490 if (WARN_ON(!wdev->current_bss))
491 return -ENOTCONN;
492
Johannes Berg19957bb2009-07-02 17:20:43 +0200493 memset(&req, 0, sizeof(req));
494 req.reason_code = reason;
Jouni Malinend5cdfac2010-04-04 09:37:19 +0300495 req.local_state_change = local_state_change;
Johannes Berg19957bb2009-07-02 17:20:43 +0200496 req.ie = ie;
497 req.ie_len = ie_len;
498 if (memcmp(wdev->current_bss->pub.bssid, bssid, ETH_ALEN) == 0)
499 req.bss = &wdev->current_bss->pub;
500 else
501 return -ENOTCONN;
502
Johannes Berg63c9c5e2012-02-24 13:50:51 +0100503 return rdev->ops->disassoc(&rdev->wiphy, dev, &req);
Johannes Berg667503d2009-07-07 03:56:11 +0200504}
505
506int cfg80211_mlme_disassoc(struct cfg80211_registered_device *rdev,
507 struct net_device *dev, const u8 *bssid,
Jouni Malinend5cdfac2010-04-04 09:37:19 +0300508 const u8 *ie, int ie_len, u16 reason,
509 bool local_state_change)
Johannes Berg667503d2009-07-07 03:56:11 +0200510{
511 struct wireless_dev *wdev = dev->ieee80211_ptr;
512 int err;
513
514 wdev_lock(wdev);
Jouni Malinend5cdfac2010-04-04 09:37:19 +0300515 err = __cfg80211_mlme_disassoc(rdev, dev, bssid, ie, ie_len, reason,
516 local_state_change);
Johannes Berg667503d2009-07-07 03:56:11 +0200517 wdev_unlock(wdev);
518
519 return err;
Johannes Berg19957bb2009-07-02 17:20:43 +0200520}
521
522void cfg80211_mlme_down(struct cfg80211_registered_device *rdev,
523 struct net_device *dev)
524{
525 struct wireless_dev *wdev = dev->ieee80211_ptr;
526 struct cfg80211_deauth_request req;
Johannes Berg95de8172012-01-20 13:55:25 +0100527 u8 bssid[ETH_ALEN];
Johannes Berg19957bb2009-07-02 17:20:43 +0200528
Johannes Berg667503d2009-07-07 03:56:11 +0200529 ASSERT_WDEV_LOCK(wdev);
530
Johannes Berg19957bb2009-07-02 17:20:43 +0200531 if (!rdev->ops->deauth)
532 return;
533
534 memset(&req, 0, sizeof(req));
535 req.reason_code = WLAN_REASON_DEAUTH_LEAVING;
536 req.ie = NULL;
537 req.ie_len = 0;
538
Johannes Berg95de8172012-01-20 13:55:25 +0100539 if (!wdev->current_bss)
540 return;
Johannes Berg19957bb2009-07-02 17:20:43 +0200541
Johannes Berg95de8172012-01-20 13:55:25 +0100542 memcpy(bssid, wdev->current_bss->pub.bssid, ETH_ALEN);
543 req.bssid = bssid;
Johannes Berg63c9c5e2012-02-24 13:50:51 +0100544 rdev->ops->deauth(&rdev->wiphy, dev, &req);
Johannes Berg95de8172012-01-20 13:55:25 +0100545
546 if (wdev->current_bss) {
547 cfg80211_unhold_bss(wdev->current_bss);
548 cfg80211_put_bss(&wdev->current_bss->pub);
549 wdev->current_bss = NULL;
Johannes Berg19957bb2009-07-02 17:20:43 +0200550 }
551}
Jouni Malinen9588bbd2009-12-23 13:15:41 +0100552
553void cfg80211_ready_on_channel(struct net_device *dev, u64 cookie,
554 struct ieee80211_channel *chan,
555 enum nl80211_channel_type channel_type,
556 unsigned int duration, gfp_t gfp)
557{
558 struct wiphy *wiphy = dev->ieee80211_ptr->wiphy;
559 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
560
561 nl80211_send_remain_on_channel(rdev, dev, cookie, chan, channel_type,
562 duration, gfp);
563}
564EXPORT_SYMBOL(cfg80211_ready_on_channel);
565
566void cfg80211_remain_on_channel_expired(struct net_device *dev,
567 u64 cookie,
568 struct ieee80211_channel *chan,
569 enum nl80211_channel_type channel_type,
570 gfp_t gfp)
571{
572 struct wiphy *wiphy = dev->ieee80211_ptr->wiphy;
573 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
574
575 nl80211_send_remain_on_channel_cancel(rdev, dev, cookie, chan,
576 channel_type, gfp);
577}
578EXPORT_SYMBOL(cfg80211_remain_on_channel_expired);
Johannes Berg98b62182009-12-23 13:15:44 +0100579
580void cfg80211_new_sta(struct net_device *dev, const u8 *mac_addr,
581 struct station_info *sinfo, gfp_t gfp)
582{
583 struct wiphy *wiphy = dev->ieee80211_ptr->wiphy;
584 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
585
586 nl80211_send_sta_event(rdev, dev, mac_addr, sinfo, gfp);
587}
588EXPORT_SYMBOL(cfg80211_new_sta);
Jouni Malinen026331c2010-02-15 12:53:10 +0200589
Jouni Malinenec15e682011-03-23 15:29:52 +0200590void cfg80211_del_sta(struct net_device *dev, const u8 *mac_addr, gfp_t gfp)
591{
592 struct wiphy *wiphy = dev->ieee80211_ptr->wiphy;
593 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
594
595 nl80211_send_sta_del_event(rdev, dev, mac_addr, gfp);
596}
597EXPORT_SYMBOL(cfg80211_del_sta);
598
Johannes Berg2e161f72010-08-12 15:38:38 +0200599struct cfg80211_mgmt_registration {
Jouni Malinen026331c2010-02-15 12:53:10 +0200600 struct list_head list;
601
602 u32 nlpid;
603
604 int match_len;
605
Johannes Berg2e161f72010-08-12 15:38:38 +0200606 __le16 frame_type;
607
Jouni Malinen026331c2010-02-15 12:53:10 +0200608 u8 match[];
609};
610
Johannes Berg2e161f72010-08-12 15:38:38 +0200611int cfg80211_mlme_register_mgmt(struct wireless_dev *wdev, u32 snd_pid,
612 u16 frame_type, const u8 *match_data,
613 int match_len)
Jouni Malinen026331c2010-02-15 12:53:10 +0200614{
Johannes Berg271733c2010-10-13 12:06:23 +0200615 struct wiphy *wiphy = wdev->wiphy;
616 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
Johannes Berg2e161f72010-08-12 15:38:38 +0200617 struct cfg80211_mgmt_registration *reg, *nreg;
Jouni Malinen026331c2010-02-15 12:53:10 +0200618 int err = 0;
Johannes Berg2e161f72010-08-12 15:38:38 +0200619 u16 mgmt_type;
620
621 if (!wdev->wiphy->mgmt_stypes)
622 return -EOPNOTSUPP;
623
624 if ((frame_type & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_MGMT)
625 return -EINVAL;
626
627 if (frame_type & ~(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE))
628 return -EINVAL;
629
630 mgmt_type = (frame_type & IEEE80211_FCTL_STYPE) >> 4;
631 if (!(wdev->wiphy->mgmt_stypes[wdev->iftype].rx & BIT(mgmt_type)))
632 return -EINVAL;
Jouni Malinen026331c2010-02-15 12:53:10 +0200633
634 nreg = kzalloc(sizeof(*reg) + match_len, GFP_KERNEL);
635 if (!nreg)
636 return -ENOMEM;
637
Johannes Berg2e161f72010-08-12 15:38:38 +0200638 spin_lock_bh(&wdev->mgmt_registrations_lock);
Jouni Malinen026331c2010-02-15 12:53:10 +0200639
Johannes Berg2e161f72010-08-12 15:38:38 +0200640 list_for_each_entry(reg, &wdev->mgmt_registrations, list) {
Jouni Malinen026331c2010-02-15 12:53:10 +0200641 int mlen = min(match_len, reg->match_len);
642
Johannes Berg2e161f72010-08-12 15:38:38 +0200643 if (frame_type != le16_to_cpu(reg->frame_type))
644 continue;
645
Jouni Malinen026331c2010-02-15 12:53:10 +0200646 if (memcmp(reg->match, match_data, mlen) == 0) {
647 err = -EALREADY;
648 break;
649 }
650 }
651
652 if (err) {
653 kfree(nreg);
654 goto out;
655 }
656
657 memcpy(nreg->match, match_data, match_len);
658 nreg->match_len = match_len;
659 nreg->nlpid = snd_pid;
Johannes Berg2e161f72010-08-12 15:38:38 +0200660 nreg->frame_type = cpu_to_le16(frame_type);
661 list_add(&nreg->list, &wdev->mgmt_registrations);
Jouni Malinen026331c2010-02-15 12:53:10 +0200662
Johannes Berg271733c2010-10-13 12:06:23 +0200663 if (rdev->ops->mgmt_frame_register)
664 rdev->ops->mgmt_frame_register(wiphy, wdev->netdev,
665 frame_type, true);
666
Jouni Malinen026331c2010-02-15 12:53:10 +0200667 out:
Johannes Berg2e161f72010-08-12 15:38:38 +0200668 spin_unlock_bh(&wdev->mgmt_registrations_lock);
Johannes Berg271733c2010-10-13 12:06:23 +0200669
Jouni Malinen026331c2010-02-15 12:53:10 +0200670 return err;
671}
672
Johannes Berg2e161f72010-08-12 15:38:38 +0200673void cfg80211_mlme_unregister_socket(struct wireless_dev *wdev, u32 nlpid)
Jouni Malinen026331c2010-02-15 12:53:10 +0200674{
Johannes Berg271733c2010-10-13 12:06:23 +0200675 struct wiphy *wiphy = wdev->wiphy;
676 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
Johannes Berg2e161f72010-08-12 15:38:38 +0200677 struct cfg80211_mgmt_registration *reg, *tmp;
Jouni Malinen026331c2010-02-15 12:53:10 +0200678
Johannes Berg2e161f72010-08-12 15:38:38 +0200679 spin_lock_bh(&wdev->mgmt_registrations_lock);
Jouni Malinen026331c2010-02-15 12:53:10 +0200680
Johannes Berg2e161f72010-08-12 15:38:38 +0200681 list_for_each_entry_safe(reg, tmp, &wdev->mgmt_registrations, list) {
Johannes Berg271733c2010-10-13 12:06:23 +0200682 if (reg->nlpid != nlpid)
683 continue;
684
685 if (rdev->ops->mgmt_frame_register) {
686 u16 frame_type = le16_to_cpu(reg->frame_type);
687
688 rdev->ops->mgmt_frame_register(wiphy, wdev->netdev,
689 frame_type, false);
Jouni Malinen026331c2010-02-15 12:53:10 +0200690 }
Johannes Berg271733c2010-10-13 12:06:23 +0200691
692 list_del(&reg->list);
693 kfree(reg);
Jouni Malinen026331c2010-02-15 12:53:10 +0200694 }
695
Johannes Berg2e161f72010-08-12 15:38:38 +0200696 spin_unlock_bh(&wdev->mgmt_registrations_lock);
Johannes Berg28946da2011-11-04 11:18:12 +0100697
698 if (nlpid == wdev->ap_unexpected_nlpid)
699 wdev->ap_unexpected_nlpid = 0;
Jouni Malinen026331c2010-02-15 12:53:10 +0200700}
701
Johannes Berg2e161f72010-08-12 15:38:38 +0200702void cfg80211_mlme_purge_registrations(struct wireless_dev *wdev)
Jouni Malinen026331c2010-02-15 12:53:10 +0200703{
Johannes Berg2e161f72010-08-12 15:38:38 +0200704 struct cfg80211_mgmt_registration *reg, *tmp;
Jouni Malinen026331c2010-02-15 12:53:10 +0200705
Johannes Berg2e161f72010-08-12 15:38:38 +0200706 spin_lock_bh(&wdev->mgmt_registrations_lock);
Jouni Malinen026331c2010-02-15 12:53:10 +0200707
Johannes Berg2e161f72010-08-12 15:38:38 +0200708 list_for_each_entry_safe(reg, tmp, &wdev->mgmt_registrations, list) {
Jouni Malinen026331c2010-02-15 12:53:10 +0200709 list_del(&reg->list);
710 kfree(reg);
711 }
712
Johannes Berg2e161f72010-08-12 15:38:38 +0200713 spin_unlock_bh(&wdev->mgmt_registrations_lock);
Jouni Malinen026331c2010-02-15 12:53:10 +0200714}
715
Johannes Berg2e161f72010-08-12 15:38:38 +0200716int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev,
717 struct net_device *dev,
Johannes Bergf7ca38d2010-11-25 10:02:29 +0100718 struct ieee80211_channel *chan, bool offchan,
Johannes Berg2e161f72010-08-12 15:38:38 +0200719 enum nl80211_channel_type channel_type,
Johannes Bergf7ca38d2010-11-25 10:02:29 +0100720 bool channel_type_valid, unsigned int wait,
Rajkumar Manoharane9f935e2011-09-25 14:53:30 +0530721 const u8 *buf, size_t len, bool no_cck,
Johannes Berge247bd902011-11-04 11:18:21 +0100722 bool dont_wait_for_ack, u64 *cookie)
Jouni Malinen026331c2010-02-15 12:53:10 +0200723{
724 struct wireless_dev *wdev = dev->ieee80211_ptr;
725 const struct ieee80211_mgmt *mgmt;
Johannes Berg2e161f72010-08-12 15:38:38 +0200726 u16 stype;
Jouni Malinen026331c2010-02-15 12:53:10 +0200727
Johannes Berg2e161f72010-08-12 15:38:38 +0200728 if (!wdev->wiphy->mgmt_stypes)
Jouni Malinen026331c2010-02-15 12:53:10 +0200729 return -EOPNOTSUPP;
Johannes Berg2e161f72010-08-12 15:38:38 +0200730
731 if (!rdev->ops->mgmt_tx)
732 return -EOPNOTSUPP;
733
Jouni Malinen026331c2010-02-15 12:53:10 +0200734 if (len < 24 + 1)
735 return -EINVAL;
736
737 mgmt = (const struct ieee80211_mgmt *) buf;
Johannes Berg2e161f72010-08-12 15:38:38 +0200738
739 if (!ieee80211_is_mgmt(mgmt->frame_control))
Jouni Malinen026331c2010-02-15 12:53:10 +0200740 return -EINVAL;
Johannes Berg2e161f72010-08-12 15:38:38 +0200741
742 stype = le16_to_cpu(mgmt->frame_control) & IEEE80211_FCTL_STYPE;
743 if (!(wdev->wiphy->mgmt_stypes[wdev->iftype].tx & BIT(stype >> 4)))
744 return -EINVAL;
745
746 if (ieee80211_is_action(mgmt->frame_control) &&
747 mgmt->u.action.category != WLAN_CATEGORY_PUBLIC) {
Johannes Berg663fcaf2010-09-30 21:06:09 +0200748 int err = 0;
749
Johannes Bergfe100ac2010-08-09 15:52:03 +0200750 wdev_lock(wdev);
751
Johannes Berg663fcaf2010-09-30 21:06:09 +0200752 switch (wdev->iftype) {
753 case NL80211_IFTYPE_ADHOC:
754 case NL80211_IFTYPE_STATION:
755 case NL80211_IFTYPE_P2P_CLIENT:
756 if (!wdev->current_bss) {
757 err = -ENOTCONN;
758 break;
759 }
Johannes Bergfe100ac2010-08-09 15:52:03 +0200760
Johannes Berg663fcaf2010-09-30 21:06:09 +0200761 if (memcmp(wdev->current_bss->pub.bssid,
762 mgmt->bssid, ETH_ALEN)) {
763 err = -ENOTCONN;
764 break;
765 }
766
767 /*
768 * check for IBSS DA must be done by driver as
769 * cfg80211 doesn't track the stations
770 */
771 if (wdev->iftype == NL80211_IFTYPE_ADHOC)
772 break;
773
774 /* for station, check that DA is the AP */
775 if (memcmp(wdev->current_bss->pub.bssid,
776 mgmt->da, ETH_ALEN)) {
777 err = -ENOTCONN;
778 break;
779 }
780 break;
781 case NL80211_IFTYPE_AP:
782 case NL80211_IFTYPE_P2P_GO:
783 case NL80211_IFTYPE_AP_VLAN:
784 if (memcmp(mgmt->bssid, dev->dev_addr, ETH_ALEN))
785 err = -EINVAL;
786 break;
Javier Cardona0778a6a2011-05-03 16:57:08 -0700787 case NL80211_IFTYPE_MESH_POINT:
788 if (memcmp(mgmt->sa, mgmt->bssid, ETH_ALEN)) {
789 err = -EINVAL;
790 break;
791 }
792 /*
793 * check for mesh DA must be done by driver as
794 * cfg80211 doesn't track the stations
795 */
796 break;
Johannes Berg663fcaf2010-09-30 21:06:09 +0200797 default:
798 err = -EOPNOTSUPP;
799 break;
800 }
Johannes Bergfe100ac2010-08-09 15:52:03 +0200801 wdev_unlock(wdev);
Johannes Berg663fcaf2010-09-30 21:06:09 +0200802
803 if (err)
804 return err;
Jouni Malinen026331c2010-02-15 12:53:10 +0200805 }
806
807 if (memcmp(mgmt->sa, dev->dev_addr, ETH_ALEN) != 0)
808 return -EINVAL;
809
810 /* Transmit the Action frame as requested by user space */
Johannes Bergf7ca38d2010-11-25 10:02:29 +0100811 return rdev->ops->mgmt_tx(&rdev->wiphy, dev, chan, offchan,
812 channel_type, channel_type_valid,
Johannes Berge247bd902011-11-04 11:18:21 +0100813 wait, buf, len, no_cck, dont_wait_for_ack,
814 cookie);
Jouni Malinen026331c2010-02-15 12:53:10 +0200815}
816
Johannes Berg804483e2012-03-05 22:18:41 +0100817bool cfg80211_rx_mgmt(struct net_device *dev, int freq, int sig_mbm,
818 const u8 *buf, size_t len, gfp_t gfp)
Jouni Malinen026331c2010-02-15 12:53:10 +0200819{
820 struct wireless_dev *wdev = dev->ieee80211_ptr;
821 struct wiphy *wiphy = wdev->wiphy;
822 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
Johannes Berg2e161f72010-08-12 15:38:38 +0200823 struct cfg80211_mgmt_registration *reg;
824 const struct ieee80211_txrx_stypes *stypes =
825 &wiphy->mgmt_stypes[wdev->iftype];
826 struct ieee80211_mgmt *mgmt = (void *)buf;
827 const u8 *data;
828 int data_len;
Jouni Malinen026331c2010-02-15 12:53:10 +0200829 bool result = false;
Johannes Berg2e161f72010-08-12 15:38:38 +0200830 __le16 ftype = mgmt->frame_control &
831 cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE);
832 u16 stype;
Jouni Malinen026331c2010-02-15 12:53:10 +0200833
Johannes Berg2e161f72010-08-12 15:38:38 +0200834 stype = (le16_to_cpu(mgmt->frame_control) & IEEE80211_FCTL_STYPE) >> 4;
Jouni Malinen026331c2010-02-15 12:53:10 +0200835
Johannes Berg2e161f72010-08-12 15:38:38 +0200836 if (!(stypes->rx & BIT(stype)))
837 return false;
Jouni Malinen026331c2010-02-15 12:53:10 +0200838
Johannes Berg2e161f72010-08-12 15:38:38 +0200839 data = buf + ieee80211_hdrlen(mgmt->frame_control);
840 data_len = len - ieee80211_hdrlen(mgmt->frame_control);
Jouni Malinen026331c2010-02-15 12:53:10 +0200841
Johannes Berg2e161f72010-08-12 15:38:38 +0200842 spin_lock_bh(&wdev->mgmt_registrations_lock);
843
844 list_for_each_entry(reg, &wdev->mgmt_registrations, list) {
845 if (reg->frame_type != ftype)
Jouni Malinen026331c2010-02-15 12:53:10 +0200846 continue;
847
Johannes Berg2e161f72010-08-12 15:38:38 +0200848 if (reg->match_len > data_len)
849 continue;
850
851 if (memcmp(reg->match, data, reg->match_len))
Jouni Malinen026331c2010-02-15 12:53:10 +0200852 continue;
853
854 /* found match! */
855
856 /* Indicate the received Action frame to user space */
Johannes Berg804483e2012-03-05 22:18:41 +0100857 if (nl80211_send_mgmt(rdev, dev, reg->nlpid,
858 freq, sig_mbm,
Johannes Berg2e161f72010-08-12 15:38:38 +0200859 buf, len, gfp))
Jouni Malinen026331c2010-02-15 12:53:10 +0200860 continue;
861
862 result = true;
863 break;
864 }
865
Johannes Berg2e161f72010-08-12 15:38:38 +0200866 spin_unlock_bh(&wdev->mgmt_registrations_lock);
Jouni Malinen026331c2010-02-15 12:53:10 +0200867
868 return result;
869}
Johannes Berg2e161f72010-08-12 15:38:38 +0200870EXPORT_SYMBOL(cfg80211_rx_mgmt);
Jouni Malinen026331c2010-02-15 12:53:10 +0200871
Johannes Berg2e161f72010-08-12 15:38:38 +0200872void cfg80211_mgmt_tx_status(struct net_device *dev, u64 cookie,
873 const u8 *buf, size_t len, bool ack, gfp_t gfp)
Jouni Malinen026331c2010-02-15 12:53:10 +0200874{
875 struct wireless_dev *wdev = dev->ieee80211_ptr;
876 struct wiphy *wiphy = wdev->wiphy;
877 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
878
879 /* Indicate TX status of the Action frame to user space */
Johannes Berg2e161f72010-08-12 15:38:38 +0200880 nl80211_send_mgmt_tx_status(rdev, dev, cookie, buf, len, ack, gfp);
Jouni Malinen026331c2010-02-15 12:53:10 +0200881}
Johannes Berg2e161f72010-08-12 15:38:38 +0200882EXPORT_SYMBOL(cfg80211_mgmt_tx_status);
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +0200883
884void cfg80211_cqm_rssi_notify(struct net_device *dev,
885 enum nl80211_cqm_rssi_threshold_event rssi_event,
886 gfp_t gfp)
887{
888 struct wireless_dev *wdev = dev->ieee80211_ptr;
889 struct wiphy *wiphy = wdev->wiphy;
890 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
891
892 /* Indicate roaming trigger event to user space */
893 nl80211_send_cqm_rssi_notify(rdev, dev, rssi_event, gfp);
894}
895EXPORT_SYMBOL(cfg80211_cqm_rssi_notify);
Johannes Bergc063dbf2010-11-24 08:10:05 +0100896
897void cfg80211_cqm_pktloss_notify(struct net_device *dev,
898 const u8 *peer, u32 num_packets, gfp_t gfp)
899{
900 struct wireless_dev *wdev = dev->ieee80211_ptr;
901 struct wiphy *wiphy = wdev->wiphy;
902 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
903
904 /* Indicate roaming trigger event to user space */
905 nl80211_send_cqm_pktloss_notify(rdev, dev, peer, num_packets, gfp);
906}
907EXPORT_SYMBOL(cfg80211_cqm_pktloss_notify);
Johannes Berge5497d72011-07-05 16:35:40 +0200908
909void cfg80211_gtk_rekey_notify(struct net_device *dev, const u8 *bssid,
910 const u8 *replay_ctr, gfp_t gfp)
911{
912 struct wireless_dev *wdev = dev->ieee80211_ptr;
913 struct wiphy *wiphy = wdev->wiphy;
914 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
915
916 nl80211_gtk_rekey_notify(rdev, dev, bssid, replay_ctr, gfp);
917}
918EXPORT_SYMBOL(cfg80211_gtk_rekey_notify);
Jouni Malinenc9df56b2011-09-16 18:56:23 +0300919
920void cfg80211_pmksa_candidate_notify(struct net_device *dev, int index,
921 const u8 *bssid, bool preauth, 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 nl80211_pmksa_candidate_notify(rdev, dev, index, bssid, preauth, gfp);
928}
929EXPORT_SYMBOL(cfg80211_pmksa_candidate_notify);
Johannes Berg28946da2011-11-04 11:18:12 +0100930
931bool cfg80211_rx_spurious_frame(struct net_device *dev,
932 const u8 *addr, gfp_t gfp)
933{
934 struct wireless_dev *wdev = dev->ieee80211_ptr;
935
936 if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP &&
937 wdev->iftype != NL80211_IFTYPE_P2P_GO))
938 return false;
939
940 return nl80211_unexpected_frame(dev, addr, gfp);
941}
942EXPORT_SYMBOL(cfg80211_rx_spurious_frame);
Johannes Bergb92ab5d2011-11-04 11:18:19 +0100943
944bool cfg80211_rx_unexpected_4addr_frame(struct net_device *dev,
945 const u8 *addr, gfp_t gfp)
946{
947 struct wireless_dev *wdev = dev->ieee80211_ptr;
948
949 if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP &&
950 wdev->iftype != NL80211_IFTYPE_P2P_GO &&
951 wdev->iftype != NL80211_IFTYPE_AP_VLAN))
952 return false;
953
954 return nl80211_unexpected_4addr_frame(dev, addr, gfp);
955}
956EXPORT_SYMBOL(cfg80211_rx_unexpected_4addr_frame);