blob: 22b3d999006559d7572287c2b8e6f4fad286766d [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>
Johannes Berg33d87832015-06-23 17:47:05 +02005 * Copyright (c) 2015 Intel Deutschland GmbH
Jouni Malinen6039f6d2009-03-19 13:39:21 +02006 */
7
8#include <linux/kernel.h>
9#include <linux/module.h>
Felix Fietkauc6fb08a2012-03-18 22:58:04 +010010#include <linux/etherdevice.h>
Jouni Malinen6039f6d2009-03-19 13:39:21 +020011#include <linux/netdevice.h>
12#include <linux/nl80211.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090013#include <linux/slab.h>
Johannes Berga9a11622009-07-27 12:01:53 +020014#include <linux/wireless.h>
Jouni Malinen6039f6d2009-03-19 13:39:21 +020015#include <net/cfg80211.h>
Johannes Berga9a11622009-07-27 12:01:53 +020016#include <net/iw_handler.h>
Jouni Malinen6039f6d2009-03-19 13:39:21 +020017#include "core.h"
18#include "nl80211.h"
Hila Gonene35e4d22012-06-27 17:19:42 +030019#include "rdev-ops.h"
20
Jouni Malinen6039f6d2009-03-19 13:39:21 +020021
Johannes Berg6ff57cf2013-05-16 00:55:00 +020022void cfg80211_rx_assoc_resp(struct net_device *dev, struct cfg80211_bss *bss,
Eliad Pellerb0b6aa22014-09-09 17:09:45 +030023 const u8 *buf, size_t len, int uapsd_queues)
Jouni Malinen6039f6d2009-03-19 13:39:21 +020024{
Johannes Berg6829c872009-07-02 09:13:27 +020025 struct wireless_dev *wdev = dev->ieee80211_ptr;
26 struct wiphy *wiphy = wdev->wiphy;
Zhao, Gangf26cbf42014-04-21 12:53:03 +080027 struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
Johannes Berg6829c872009-07-02 09:13:27 +020028 struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf;
29 u8 *ie = mgmt->u.assoc_resp.variable;
Johannes Berg95de8172012-01-20 13:55:25 +010030 int ieoffs = offsetof(struct ieee80211_mgmt, u.assoc_resp.variable);
Johannes Bergceca7b72013-05-16 00:55:45 +020031 u16 status_code = le16_to_cpu(mgmt->u.assoc_resp.status_code);
Johannes Berg6829c872009-07-02 09:13:27 +020032
Beni Lev4ee3e062012-08-27 12:49:39 +030033 trace_cfg80211_send_rx_assoc(dev, bss);
Johannes Bergcb0b4be2009-07-07 03:56:07 +020034
Johannes Bergf401a6f2009-08-07 14:51:05 +020035 /*
36 * This is a bit of a hack, we don't notify userspace of
37 * a (re-)association reply if we tried to send a reassoc
38 * and got a reject -- we only try again with an assoc
39 * frame instead of reassoc.
40 */
Johannes Bergceca7b72013-05-16 00:55:45 +020041 if (cfg80211_sme_rx_assoc_resp(wdev, status_code)) {
Johannes Bergf1940c52013-06-19 13:21:15 +020042 cfg80211_unhold_bss(bss_from_pub(bss));
Johannes Berg5b112d32013-02-01 01:49:58 +010043 cfg80211_put_bss(wiphy, bss);
Johannes Berg8d61ffa2013-05-10 12:32:47 +020044 return;
Johannes Berg95de8172012-01-20 13:55:25 +010045 }
Johannes Bergf401a6f2009-08-07 14:51:05 +020046
Eliad Pellerb0b6aa22014-09-09 17:09:45 +030047 nl80211_send_rx_assoc(rdev, dev, buf, len, GFP_KERNEL, uapsd_queues);
Johannes Bergceca7b72013-05-16 00:55:45 +020048 /* update current_bss etc., consumes the bss reference */
Johannes Bergdf7fc0f2009-07-29 11:23:49 +020049 __cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, ie, len - ieoffs,
50 status_code,
Purushottam Kushwaha3093ebbeab2017-01-13 01:12:21 +020051 status_code == WLAN_STATUS_SUCCESS, bss,
52 NL80211_TIMEOUT_UNSPECIFIED);
Jouni Malinen6039f6d2009-03-19 13:39:21 +020053}
Johannes Berg6ff57cf2013-05-16 00:55:00 +020054EXPORT_SYMBOL(cfg80211_rx_assoc_resp);
Jouni Malinen6039f6d2009-03-19 13:39:21 +020055
Johannes Bergceca7b72013-05-16 00:55:45 +020056static void cfg80211_process_auth(struct wireless_dev *wdev,
57 const u8 *buf, size_t len)
Jouni Malinen6039f6d2009-03-19 13:39:21 +020058{
Zhao, Gangf26cbf42014-04-21 12:53:03 +080059 struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
Johannes Berg6829c872009-07-02 09:13:27 +020060
Johannes Bergceca7b72013-05-16 00:55:45 +020061 nl80211_send_rx_auth(rdev, wdev->netdev, buf, len, GFP_KERNEL);
62 cfg80211_sme_rx_auth(wdev, buf, len);
Johannes Berg667503d2009-07-07 03:56:11 +020063}
Jouni Malinen6039f6d2009-03-19 13:39:21 +020064
Johannes Bergceca7b72013-05-16 00:55:45 +020065static void cfg80211_process_deauth(struct wireless_dev *wdev,
66 const u8 *buf, size_t len)
Jouni Malinen6039f6d2009-03-19 13:39:21 +020067{
Zhao, Gangf26cbf42014-04-21 12:53:03 +080068 struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
Johannes Berg6829c872009-07-02 09:13:27 +020069 struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf;
Johannes Berg19957bb2009-07-02 17:20:43 +020070 const u8 *bssid = mgmt->bssid;
Johannes Bergceca7b72013-05-16 00:55:45 +020071 u16 reason_code = le16_to_cpu(mgmt->u.deauth.reason_code);
72 bool from_ap = !ether_addr_equal(mgmt->sa, wdev->netdev->dev_addr);
Johannes Berg6829c872009-07-02 09:13:27 +020073
Johannes Bergceca7b72013-05-16 00:55:45 +020074 nl80211_send_deauth(rdev, wdev->netdev, buf, len, GFP_KERNEL);
Johannes Berg6829c872009-07-02 09:13:27 +020075
Johannes Bergceca7b72013-05-16 00:55:45 +020076 if (!wdev->current_bss ||
77 !ether_addr_equal(wdev->current_bss->pub.bssid, bssid))
Johannes Berg596a07c2009-07-11 00:17:32 +020078 return;
Johannes Berg6829c872009-07-02 09:13:27 +020079
Johannes Bergceca7b72013-05-16 00:55:45 +020080 __cfg80211_disconnected(wdev->netdev, NULL, 0, reason_code, from_ap);
81 cfg80211_sme_deauth(wdev);
82}
Johannes Berg6829c872009-07-02 09:13:27 +020083
Johannes Bergceca7b72013-05-16 00:55:45 +020084static void cfg80211_process_disassoc(struct wireless_dev *wdev,
85 const u8 *buf, size_t len)
86{
Zhao, Gangf26cbf42014-04-21 12:53:03 +080087 struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
Johannes Bergceca7b72013-05-16 00:55:45 +020088 struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf;
89 const u8 *bssid = mgmt->bssid;
90 u16 reason_code = le16_to_cpu(mgmt->u.disassoc.reason_code);
91 bool from_ap = !ether_addr_equal(mgmt->sa, wdev->netdev->dev_addr);
Johannes Berg19957bb2009-07-02 17:20:43 +020092
Johannes Bergceca7b72013-05-16 00:55:45 +020093 nl80211_send_disassoc(rdev, wdev->netdev, buf, len, GFP_KERNEL);
94
95 if (WARN_ON(!wdev->current_bss ||
96 !ether_addr_equal(wdev->current_bss->pub.bssid, bssid)))
97 return;
98
99 __cfg80211_disconnected(wdev->netdev, NULL, 0, reason_code, from_ap);
100 cfg80211_sme_disassoc(wdev);
Johannes Berg667503d2009-07-07 03:56:11 +0200101}
Jouni Malinena3b8b052009-03-27 21:59:49 +0200102
Johannes Berg6ff57cf2013-05-16 00:55:00 +0200103void cfg80211_rx_mlme_mgmt(struct net_device *dev, const u8 *buf, size_t len)
104{
105 struct wireless_dev *wdev = dev->ieee80211_ptr;
Johannes Berg6ff57cf2013-05-16 00:55:00 +0200106 struct ieee80211_mgmt *mgmt = (void *)buf;
107
108 ASSERT_WDEV_LOCK(wdev);
109
110 trace_cfg80211_rx_mlme_mgmt(dev, buf, len);
111
112 if (WARN_ON(len < 2))
113 return;
114
Johannes Bergceca7b72013-05-16 00:55:45 +0200115 if (ieee80211_is_auth(mgmt->frame_control))
116 cfg80211_process_auth(wdev, buf, len);
117 else if (ieee80211_is_deauth(mgmt->frame_control))
118 cfg80211_process_deauth(wdev, buf, len);
119 else if (ieee80211_is_disassoc(mgmt->frame_control))
120 cfg80211_process_disassoc(wdev, buf, len);
Johannes Berg6ff57cf2013-05-16 00:55:00 +0200121}
122EXPORT_SYMBOL(cfg80211_rx_mlme_mgmt);
123
124void cfg80211_auth_timeout(struct net_device *dev, const u8 *addr)
Johannes Berga58ce432009-11-19 12:45:42 +0100125{
126 struct wireless_dev *wdev = dev->ieee80211_ptr;
127 struct wiphy *wiphy = wdev->wiphy;
Zhao, Gangf26cbf42014-04-21 12:53:03 +0800128 struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
Johannes Berga58ce432009-11-19 12:45:42 +0100129
Beni Lev4ee3e062012-08-27 12:49:39 +0300130 trace_cfg80211_send_auth_timeout(dev, addr);
Johannes Berga58ce432009-11-19 12:45:42 +0100131
132 nl80211_send_auth_timeout(rdev, dev, addr, GFP_KERNEL);
Johannes Bergceca7b72013-05-16 00:55:45 +0200133 cfg80211_sme_auth_timeout(wdev);
Jouni Malinen1965c852009-04-22 21:38:25 +0300134}
Johannes Berg6ff57cf2013-05-16 00:55:00 +0200135EXPORT_SYMBOL(cfg80211_auth_timeout);
Jouni Malinen1965c852009-04-22 21:38:25 +0300136
Johannes Berg959867f2013-06-19 13:05:42 +0200137void cfg80211_assoc_timeout(struct net_device *dev, struct cfg80211_bss *bss)
Jouni Malinen1965c852009-04-22 21:38:25 +0300138{
Johannes Berg6829c872009-07-02 09:13:27 +0200139 struct wireless_dev *wdev = dev->ieee80211_ptr;
140 struct wiphy *wiphy = wdev->wiphy;
Zhao, Gangf26cbf42014-04-21 12:53:03 +0800141 struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
Johannes Berg19957bb2009-07-02 17:20:43 +0200142
Johannes Berg959867f2013-06-19 13:05:42 +0200143 trace_cfg80211_send_assoc_timeout(dev, bss->bssid);
Johannes Bergcb0b4be2009-07-07 03:56:07 +0200144
Johannes Berg959867f2013-06-19 13:05:42 +0200145 nl80211_send_assoc_timeout(rdev, dev, bss->bssid, GFP_KERNEL);
Johannes Bergceca7b72013-05-16 00:55:45 +0200146 cfg80211_sme_assoc_timeout(wdev);
Johannes Berg959867f2013-06-19 13:05:42 +0200147
Johannes Bergf1940c52013-06-19 13:21:15 +0200148 cfg80211_unhold_bss(bss_from_pub(bss));
Johannes Berg959867f2013-06-19 13:05:42 +0200149 cfg80211_put_bss(wiphy, bss);
Jouni Malinen1965c852009-04-22 21:38:25 +0300150}
Johannes Berg6ff57cf2013-05-16 00:55:00 +0200151EXPORT_SYMBOL(cfg80211_assoc_timeout);
152
Johannes Berge6f462d2016-12-08 17:22:09 +0100153void cfg80211_abandon_assoc(struct net_device *dev, struct cfg80211_bss *bss)
154{
155 struct wireless_dev *wdev = dev->ieee80211_ptr;
156 struct wiphy *wiphy = wdev->wiphy;
157
158 cfg80211_sme_abandon_assoc(wdev);
159
160 cfg80211_unhold_bss(bss_from_pub(bss));
161 cfg80211_put_bss(wiphy, bss);
162}
163EXPORT_SYMBOL(cfg80211_abandon_assoc);
164
Johannes Berg6ff57cf2013-05-16 00:55:00 +0200165void cfg80211_tx_mlme_mgmt(struct net_device *dev, const u8 *buf, size_t len)
166{
167 struct wireless_dev *wdev = dev->ieee80211_ptr;
168 struct ieee80211_mgmt *mgmt = (void *)buf;
169
170 ASSERT_WDEV_LOCK(wdev);
171
172 trace_cfg80211_tx_mlme_mgmt(dev, buf, len);
173
174 if (WARN_ON(len < 2))
175 return;
176
177 if (ieee80211_is_deauth(mgmt->frame_control))
Johannes Bergceca7b72013-05-16 00:55:45 +0200178 cfg80211_process_deauth(wdev, buf, len);
Johannes Berg6ff57cf2013-05-16 00:55:00 +0200179 else
Johannes Bergceca7b72013-05-16 00:55:45 +0200180 cfg80211_process_disassoc(wdev, buf, len);
Johannes Berg6ff57cf2013-05-16 00:55:00 +0200181}
182EXPORT_SYMBOL(cfg80211_tx_mlme_mgmt);
Jouni Malinen1965c852009-04-22 21:38:25 +0300183
Jouni Malinena3b8b052009-03-27 21:59:49 +0200184void cfg80211_michael_mic_failure(struct net_device *dev, const u8 *addr,
185 enum nl80211_key_type key_type, int key_id,
Johannes Berge6d6e342009-07-01 21:26:47 +0200186 const u8 *tsc, gfp_t gfp)
Jouni Malinena3b8b052009-03-27 21:59:49 +0200187{
188 struct wiphy *wiphy = dev->ieee80211_ptr->wiphy;
Zhao, Gangf26cbf42014-04-21 12:53:03 +0800189 struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
Johannes Berg3d23e342009-09-29 23:27:28 +0200190#ifdef CONFIG_CFG80211_WEXT
Johannes Bergf58d4ed2009-06-19 02:45:21 +0200191 union iwreq_data wrqu;
Johannes Berge6d6e342009-07-01 21:26:47 +0200192 char *buf = kmalloc(128, gfp);
Johannes Bergf58d4ed2009-06-19 02:45:21 +0200193
194 if (buf) {
195 sprintf(buf, "MLME-MICHAELMICFAILURE.indication("
196 "keyid=%d %scast addr=%pM)", key_id,
197 key_type == NL80211_KEYTYPE_GROUP ? "broad" : "uni",
198 addr);
199 memset(&wrqu, 0, sizeof(wrqu));
200 wrqu.data.length = strlen(buf);
201 wireless_send_event(dev, IWEVCUSTOM, &wrqu, buf);
202 kfree(buf);
203 }
204#endif
205
Beni Lev4ee3e062012-08-27 12:49:39 +0300206 trace_cfg80211_michael_mic_failure(dev, addr, key_type, key_id, tsc);
Johannes Berge6d6e342009-07-01 21:26:47 +0200207 nl80211_michael_mic_failure(rdev, dev, addr, key_type, key_id, tsc, gfp);
Jouni Malinena3b8b052009-03-27 21:59:49 +0200208}
209EXPORT_SYMBOL(cfg80211_michael_mic_failure);
Johannes Berg19957bb2009-07-02 17:20:43 +0200210
211/* some MLME handling for userspace SME */
Johannes Berg91bf9b22013-05-15 17:44:01 +0200212int cfg80211_mlme_auth(struct cfg80211_registered_device *rdev,
213 struct net_device *dev,
214 struct ieee80211_channel *chan,
215 enum nl80211_auth_type auth_type,
216 const u8 *bssid,
217 const u8 *ssid, int ssid_len,
218 const u8 *ie, int ie_len,
219 const u8 *key, int key_len, int key_idx,
Jouni Malinen11b6b5a2016-10-27 00:41:58 +0300220 const u8 *auth_data, int auth_data_len)
Johannes Berg19957bb2009-07-02 17:20:43 +0200221{
222 struct wireless_dev *wdev = dev->ieee80211_ptr;
Johannes Berg7ade7032013-05-13 11:37:30 +0200223 struct cfg80211_auth_request req = {
224 .ie = ie,
225 .ie_len = ie_len,
Jouni Malinen11b6b5a2016-10-27 00:41:58 +0300226 .auth_data = auth_data,
227 .auth_data_len = auth_data_len,
Johannes Berg7ade7032013-05-13 11:37:30 +0200228 .auth_type = auth_type,
229 .key = key,
230 .key_len = key_len,
231 .key_idx = key_idx,
232 };
Johannes Berg95de8172012-01-20 13:55:25 +0100233 int err;
Johannes Berg19957bb2009-07-02 17:20:43 +0200234
Johannes Berg667503d2009-07-07 03:56:11 +0200235 ASSERT_WDEV_LOCK(wdev);
236
Johannes Bergfffd0932009-07-08 14:22:54 +0200237 if (auth_type == NL80211_AUTHTYPE_SHARED_KEY)
Johannes Bergb6b55552016-09-13 16:25:58 +0200238 if (!key || !key_len || key_idx < 0 || key_idx > 3)
Johannes Bergfffd0932009-07-08 14:22:54 +0200239 return -EINVAL;
240
Johannes Berg0a9b5e12009-07-02 18:26:18 +0200241 if (wdev->current_bss &&
Joe Perchesac422d32012-05-08 18:56:55 +0000242 ether_addr_equal(bssid, wdev->current_bss->pub.bssid))
Johannes Berg0a9b5e12009-07-02 18:26:18 +0200243 return -EALREADY;
244
Johannes Berg19957bb2009-07-02 17:20:43 +0200245 req.bss = cfg80211_get_bss(&rdev->wiphy, chan, bssid, ssid, ssid_len,
Dedy Lansky6eb18132015-02-08 15:52:03 +0200246 IEEE80211_BSS_TYPE_ESS,
247 IEEE80211_PRIVACY_ANY);
Johannes Berg19957bb2009-07-02 17:20:43 +0200248 if (!req.bss)
249 return -ENOENT;
250
Hila Gonene35e4d22012-06-27 17:19:42 +0300251 err = rdev_auth(rdev, dev, &req);
Johannes Berg19957bb2009-07-02 17:20:43 +0200252
Johannes Berg5b112d32013-02-01 01:49:58 +0100253 cfg80211_put_bss(&rdev->wiphy, req.bss);
Johannes Berg19957bb2009-07-02 17:20:43 +0200254 return err;
255}
256
Ben Greear7e7c8922011-11-18 11:31:59 -0800257/* Do a logical ht_capa &= ht_capa_mask. */
258void cfg80211_oper_and_ht_capa(struct ieee80211_ht_cap *ht_capa,
259 const struct ieee80211_ht_cap *ht_capa_mask)
260{
261 int i;
262 u8 *p1, *p2;
263 if (!ht_capa_mask) {
264 memset(ht_capa, 0, sizeof(*ht_capa));
265 return;
266 }
267
268 p1 = (u8*)(ht_capa);
269 p2 = (u8*)(ht_capa_mask);
270 for (i = 0; i<sizeof(*ht_capa); i++)
271 p1[i] &= p2[i];
272}
273
Johannes Bergee2aca32013-02-21 17:36:01 +0100274/* Do a logical ht_capa &= ht_capa_mask. */
275void cfg80211_oper_and_vht_capa(struct ieee80211_vht_cap *vht_capa,
276 const struct ieee80211_vht_cap *vht_capa_mask)
277{
278 int i;
279 u8 *p1, *p2;
280 if (!vht_capa_mask) {
281 memset(vht_capa, 0, sizeof(*vht_capa));
282 return;
283 }
284
285 p1 = (u8*)(vht_capa);
286 p2 = (u8*)(vht_capa_mask);
287 for (i = 0; i < sizeof(*vht_capa); i++)
288 p1[i] &= p2[i];
289}
290
Johannes Berg91bf9b22013-05-15 17:44:01 +0200291int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev,
292 struct net_device *dev,
293 struct ieee80211_channel *chan,
294 const u8 *bssid,
295 const u8 *ssid, int ssid_len,
296 struct cfg80211_assoc_request *req)
Johannes Berg19957bb2009-07-02 17:20:43 +0200297{
298 struct wireless_dev *wdev = dev->ieee80211_ptr;
Johannes Berg95de8172012-01-20 13:55:25 +0100299 int err;
Johannes Berg19957bb2009-07-02 17:20:43 +0200300
Johannes Berg667503d2009-07-07 03:56:11 +0200301 ASSERT_WDEV_LOCK(wdev);
302
Johannes Bergceca7b72013-05-16 00:55:45 +0200303 if (wdev->current_bss &&
304 (!req->prev_bssid || !ether_addr_equal(wdev->current_bss->pub.bssid,
305 req->prev_bssid)))
Johannes Berg19957bb2009-07-02 17:20:43 +0200306 return -EALREADY;
307
Johannes Bergf62fab72013-02-21 20:09:09 +0100308 cfg80211_oper_and_ht_capa(&req->ht_capa_mask,
Ben Greear7e7c8922011-11-18 11:31:59 -0800309 rdev->wiphy.ht_capa_mod_mask);
Johannes Bergf62fab72013-02-21 20:09:09 +0100310 cfg80211_oper_and_vht_capa(&req->vht_capa_mask,
Johannes Bergee2aca32013-02-21 17:36:01 +0100311 rdev->wiphy.vht_capa_mod_mask);
Ben Greear7e7c8922011-11-18 11:31:59 -0800312
Johannes Bergf62fab72013-02-21 20:09:09 +0100313 req->bss = cfg80211_get_bss(&rdev->wiphy, chan, bssid, ssid, ssid_len,
Dedy Lansky6eb18132015-02-08 15:52:03 +0200314 IEEE80211_BSS_TYPE_ESS,
315 IEEE80211_PRIVACY_ANY);
Johannes Bergceca7b72013-05-16 00:55:45 +0200316 if (!req->bss)
Johannes Berg19957bb2009-07-02 17:20:43 +0200317 return -ENOENT;
318
Johannes Bergf62fab72013-02-21 20:09:09 +0100319 err = rdev_assoc(rdev, dev, req);
Johannes Bergf1940c52013-06-19 13:21:15 +0200320 if (!err)
321 cfg80211_hold_bss(bss_from_pub(req->bss));
Luciano Coelho73de86a2014-02-13 11:31:59 +0200322 else
Johannes Bergf62fab72013-02-21 20:09:09 +0100323 cfg80211_put_bss(&rdev->wiphy, req->bss);
Johannes Berg95de8172012-01-20 13:55:25 +0100324
Johannes Berg19957bb2009-07-02 17:20:43 +0200325 return err;
326}
327
Johannes Berg91bf9b22013-05-15 17:44:01 +0200328int cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev,
329 struct net_device *dev, const u8 *bssid,
330 const u8 *ie, int ie_len, u16 reason,
331 bool local_state_change)
Johannes Berg19957bb2009-07-02 17:20:43 +0200332{
333 struct wireless_dev *wdev = dev->ieee80211_ptr;
Johannes Berg95de8172012-01-20 13:55:25 +0100334 struct cfg80211_deauth_request req = {
335 .bssid = bssid,
336 .reason_code = reason,
337 .ie = ie,
338 .ie_len = ie_len,
Stanislaw Gruszka68632552012-10-15 14:52:41 +0200339 .local_state_change = local_state_change,
Johannes Berg95de8172012-01-20 13:55:25 +0100340 };
Johannes Berg19957bb2009-07-02 17:20:43 +0200341
Johannes Berg667503d2009-07-07 03:56:11 +0200342 ASSERT_WDEV_LOCK(wdev);
343
Johannes Bergceca7b72013-05-16 00:55:45 +0200344 if (local_state_change &&
345 (!wdev->current_bss ||
346 !ether_addr_equal(wdev->current_bss->pub.bssid, bssid)))
Johannes Berg95de8172012-01-20 13:55:25 +0100347 return 0;
Johannes Berg19957bb2009-07-02 17:20:43 +0200348
Andrzej Zaborowskibd2522b2017-01-06 16:33:43 -0500349 if (ether_addr_equal(wdev->disconnect_bssid, bssid) ||
350 (wdev->current_bss &&
351 ether_addr_equal(wdev->current_bss->pub.bssid, bssid)))
352 wdev->conn_owner_nlportid = 0;
353
Hila Gonene35e4d22012-06-27 17:19:42 +0300354 return rdev_deauth(rdev, dev, &req);
Johannes Berg19957bb2009-07-02 17:20:43 +0200355}
356
Johannes Berg91bf9b22013-05-15 17:44:01 +0200357int cfg80211_mlme_disassoc(struct cfg80211_registered_device *rdev,
358 struct net_device *dev, const u8 *bssid,
359 const u8 *ie, int ie_len, u16 reason,
360 bool local_state_change)
Johannes Berg19957bb2009-07-02 17:20:43 +0200361{
362 struct wireless_dev *wdev = dev->ieee80211_ptr;
Johannes Berg7ade7032013-05-13 11:37:30 +0200363 struct cfg80211_disassoc_request req = {
364 .reason_code = reason,
365 .local_state_change = local_state_change,
366 .ie = ie,
367 .ie_len = ie_len,
368 };
Johannes Bergceca7b72013-05-16 00:55:45 +0200369 int err;
Johannes Berg19957bb2009-07-02 17:20:43 +0200370
Johannes Berg667503d2009-07-07 03:56:11 +0200371 ASSERT_WDEV_LOCK(wdev);
372
Johannes Bergceca7b72013-05-16 00:55:45 +0200373 if (!wdev->current_bss)
Johannes Bergf9d6b402009-07-27 10:22:28 +0200374 return -ENOTCONN;
375
Joe Perchesac422d32012-05-08 18:56:55 +0000376 if (ether_addr_equal(wdev->current_bss->pub.bssid, bssid))
Johannes Berg19957bb2009-07-02 17:20:43 +0200377 req.bss = &wdev->current_bss->pub;
378 else
379 return -ENOTCONN;
380
Johannes Bergceca7b72013-05-16 00:55:45 +0200381 err = rdev_disassoc(rdev, dev, &req);
382 if (err)
383 return err;
384
385 /* driver should have reported the disassoc */
386 WARN_ON(wdev->current_bss);
387 return 0;
Johannes Berg667503d2009-07-07 03:56:11 +0200388}
389
Johannes Berg19957bb2009-07-02 17:20:43 +0200390void cfg80211_mlme_down(struct cfg80211_registered_device *rdev,
391 struct net_device *dev)
392{
393 struct wireless_dev *wdev = dev->ieee80211_ptr;
Johannes Berg95de8172012-01-20 13:55:25 +0100394 u8 bssid[ETH_ALEN];
Johannes Berg19957bb2009-07-02 17:20:43 +0200395
Johannes Berg667503d2009-07-07 03:56:11 +0200396 ASSERT_WDEV_LOCK(wdev);
397
Johannes Berg19957bb2009-07-02 17:20:43 +0200398 if (!rdev->ops->deauth)
399 return;
400
Johannes Berg95de8172012-01-20 13:55:25 +0100401 if (!wdev->current_bss)
402 return;
Johannes Berg19957bb2009-07-02 17:20:43 +0200403
Johannes Berg95de8172012-01-20 13:55:25 +0100404 memcpy(bssid, wdev->current_bss->pub.bssid, ETH_ALEN);
Johannes Bergceca7b72013-05-16 00:55:45 +0200405 cfg80211_mlme_deauth(rdev, dev, bssid, NULL, 0,
406 WLAN_REASON_DEAUTH_LEAVING, false);
Johannes Berg19957bb2009-07-02 17:20:43 +0200407}
Jouni Malinen9588bbd2009-12-23 13:15:41 +0100408
Johannes Berg2e161f72010-08-12 15:38:38 +0200409struct cfg80211_mgmt_registration {
Jouni Malinen026331c2010-02-15 12:53:10 +0200410 struct list_head list;
Johannes Berg33d87832015-06-23 17:47:05 +0200411 struct wireless_dev *wdev;
Jouni Malinen026331c2010-02-15 12:53:10 +0200412
Eric W. Biederman15e47302012-09-07 20:12:54 +0000413 u32 nlportid;
Jouni Malinen026331c2010-02-15 12:53:10 +0200414
415 int match_len;
416
Johannes Berg2e161f72010-08-12 15:38:38 +0200417 __le16 frame_type;
418
Jouni Malinen026331c2010-02-15 12:53:10 +0200419 u8 match[];
420};
421
Johannes Berg33d87832015-06-23 17:47:05 +0200422static void
423cfg80211_process_mlme_unregistrations(struct cfg80211_registered_device *rdev)
424{
425 struct cfg80211_mgmt_registration *reg;
426
427 ASSERT_RTNL();
428
429 spin_lock_bh(&rdev->mlme_unreg_lock);
430 while ((reg = list_first_entry_or_null(&rdev->mlme_unreg,
431 struct cfg80211_mgmt_registration,
432 list))) {
433 list_del(&reg->list);
434 spin_unlock_bh(&rdev->mlme_unreg_lock);
435
436 if (rdev->ops->mgmt_frame_register) {
437 u16 frame_type = le16_to_cpu(reg->frame_type);
438
439 rdev_mgmt_frame_register(rdev, reg->wdev,
440 frame_type, false);
441 }
442
443 kfree(reg);
444
445 spin_lock_bh(&rdev->mlme_unreg_lock);
446 }
447 spin_unlock_bh(&rdev->mlme_unreg_lock);
448}
449
450void cfg80211_mlme_unreg_wk(struct work_struct *wk)
451{
452 struct cfg80211_registered_device *rdev;
453
454 rdev = container_of(wk, struct cfg80211_registered_device,
455 mlme_unreg_wk);
456
457 rtnl_lock();
458 cfg80211_process_mlme_unregistrations(rdev);
459 rtnl_unlock();
460}
461
Eric W. Biederman15e47302012-09-07 20:12:54 +0000462int cfg80211_mlme_register_mgmt(struct wireless_dev *wdev, u32 snd_portid,
Johannes Berg2e161f72010-08-12 15:38:38 +0200463 u16 frame_type, const u8 *match_data,
464 int match_len)
Jouni Malinen026331c2010-02-15 12:53:10 +0200465{
Johannes Berg271733c2010-10-13 12:06:23 +0200466 struct wiphy *wiphy = wdev->wiphy;
Zhao, Gangf26cbf42014-04-21 12:53:03 +0800467 struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
Johannes Berg2e161f72010-08-12 15:38:38 +0200468 struct cfg80211_mgmt_registration *reg, *nreg;
Jouni Malinen026331c2010-02-15 12:53:10 +0200469 int err = 0;
Johannes Berg2e161f72010-08-12 15:38:38 +0200470 u16 mgmt_type;
471
472 if (!wdev->wiphy->mgmt_stypes)
473 return -EOPNOTSUPP;
474
475 if ((frame_type & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_MGMT)
476 return -EINVAL;
477
478 if (frame_type & ~(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE))
479 return -EINVAL;
480
481 mgmt_type = (frame_type & IEEE80211_FCTL_STYPE) >> 4;
482 if (!(wdev->wiphy->mgmt_stypes[wdev->iftype].rx & BIT(mgmt_type)))
483 return -EINVAL;
Jouni Malinen026331c2010-02-15 12:53:10 +0200484
485 nreg = kzalloc(sizeof(*reg) + match_len, GFP_KERNEL);
486 if (!nreg)
487 return -ENOMEM;
488
Johannes Berg2e161f72010-08-12 15:38:38 +0200489 spin_lock_bh(&wdev->mgmt_registrations_lock);
Jouni Malinen026331c2010-02-15 12:53:10 +0200490
Johannes Berg2e161f72010-08-12 15:38:38 +0200491 list_for_each_entry(reg, &wdev->mgmt_registrations, list) {
Jouni Malinen026331c2010-02-15 12:53:10 +0200492 int mlen = min(match_len, reg->match_len);
493
Johannes Berg2e161f72010-08-12 15:38:38 +0200494 if (frame_type != le16_to_cpu(reg->frame_type))
495 continue;
496
Jouni Malinen026331c2010-02-15 12:53:10 +0200497 if (memcmp(reg->match, match_data, mlen) == 0) {
498 err = -EALREADY;
499 break;
500 }
501 }
502
503 if (err) {
504 kfree(nreg);
505 goto out;
506 }
507
508 memcpy(nreg->match, match_data, match_len);
509 nreg->match_len = match_len;
Eric W. Biederman15e47302012-09-07 20:12:54 +0000510 nreg->nlportid = snd_portid;
Johannes Berg2e161f72010-08-12 15:38:38 +0200511 nreg->frame_type = cpu_to_le16(frame_type);
Johannes Berg33d87832015-06-23 17:47:05 +0200512 nreg->wdev = wdev;
Johannes Berg2e161f72010-08-12 15:38:38 +0200513 list_add(&nreg->list, &wdev->mgmt_registrations);
Johannes Berg33d87832015-06-23 17:47:05 +0200514 spin_unlock_bh(&wdev->mgmt_registrations_lock);
515
516 /* process all unregistrations to avoid driver confusion */
517 cfg80211_process_mlme_unregistrations(rdev);
Jouni Malinen026331c2010-02-15 12:53:10 +0200518
Johannes Berg271733c2010-10-13 12:06:23 +0200519 if (rdev->ops->mgmt_frame_register)
Hila Gonene35e4d22012-06-27 17:19:42 +0300520 rdev_mgmt_frame_register(rdev, wdev, frame_type, true);
Johannes Berg271733c2010-10-13 12:06:23 +0200521
Johannes Berg33d87832015-06-23 17:47:05 +0200522 return 0;
523
Jouni Malinen026331c2010-02-15 12:53:10 +0200524 out:
Johannes Berg2e161f72010-08-12 15:38:38 +0200525 spin_unlock_bh(&wdev->mgmt_registrations_lock);
Johannes Berg271733c2010-10-13 12:06:23 +0200526
Jouni Malinen026331c2010-02-15 12:53:10 +0200527 return err;
528}
529
Eric W. Biederman15e47302012-09-07 20:12:54 +0000530void cfg80211_mlme_unregister_socket(struct wireless_dev *wdev, u32 nlportid)
Jouni Malinen026331c2010-02-15 12:53:10 +0200531{
Johannes Berg271733c2010-10-13 12:06:23 +0200532 struct wiphy *wiphy = wdev->wiphy;
Zhao, Gangf26cbf42014-04-21 12:53:03 +0800533 struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
Johannes Berg2e161f72010-08-12 15:38:38 +0200534 struct cfg80211_mgmt_registration *reg, *tmp;
Jouni Malinen026331c2010-02-15 12:53:10 +0200535
Johannes Berg2e161f72010-08-12 15:38:38 +0200536 spin_lock_bh(&wdev->mgmt_registrations_lock);
Jouni Malinen026331c2010-02-15 12:53:10 +0200537
Johannes Berg2e161f72010-08-12 15:38:38 +0200538 list_for_each_entry_safe(reg, tmp, &wdev->mgmt_registrations, list) {
Eric W. Biederman15e47302012-09-07 20:12:54 +0000539 if (reg->nlportid != nlportid)
Johannes Berg271733c2010-10-13 12:06:23 +0200540 continue;
541
Johannes Berg271733c2010-10-13 12:06:23 +0200542 list_del(&reg->list);
Johannes Berg33d87832015-06-23 17:47:05 +0200543 spin_lock(&rdev->mlme_unreg_lock);
544 list_add_tail(&reg->list, &rdev->mlme_unreg);
545 spin_unlock(&rdev->mlme_unreg_lock);
546
547 schedule_work(&rdev->mlme_unreg_wk);
Jouni Malinen026331c2010-02-15 12:53:10 +0200548 }
549
Johannes Berg2e161f72010-08-12 15:38:38 +0200550 spin_unlock_bh(&wdev->mgmt_registrations_lock);
Johannes Berg28946da2011-11-04 11:18:12 +0100551
Arend van Spriel5de17982013-04-18 15:49:00 +0200552 if (nlportid && rdev->crit_proto_nlportid == nlportid) {
553 rdev->crit_proto_nlportid = 0;
554 rdev_crit_proto_stop(rdev, wdev);
555 }
556
Eric W. Biederman15e47302012-09-07 20:12:54 +0000557 if (nlportid == wdev->ap_unexpected_nlportid)
558 wdev->ap_unexpected_nlportid = 0;
Jouni Malinen026331c2010-02-15 12:53:10 +0200559}
560
Johannes Berg2e161f72010-08-12 15:38:38 +0200561void cfg80211_mlme_purge_registrations(struct wireless_dev *wdev)
Jouni Malinen026331c2010-02-15 12:53:10 +0200562{
Johannes Berg33d87832015-06-23 17:47:05 +0200563 struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
Jouni Malinen026331c2010-02-15 12:53:10 +0200564
Johannes Berg2e161f72010-08-12 15:38:38 +0200565 spin_lock_bh(&wdev->mgmt_registrations_lock);
Johannes Berg33d87832015-06-23 17:47:05 +0200566 spin_lock(&rdev->mlme_unreg_lock);
567 list_splice_tail_init(&wdev->mgmt_registrations, &rdev->mlme_unreg);
568 spin_unlock(&rdev->mlme_unreg_lock);
Johannes Berg2e161f72010-08-12 15:38:38 +0200569 spin_unlock_bh(&wdev->mgmt_registrations_lock);
Johannes Berg33d87832015-06-23 17:47:05 +0200570
571 cfg80211_process_mlme_unregistrations(rdev);
Jouni Malinen026331c2010-02-15 12:53:10 +0200572}
573
Johannes Berg2e161f72010-08-12 15:38:38 +0200574int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev,
Johannes Berg71bbc992012-06-15 15:30:18 +0200575 struct wireless_dev *wdev,
Andrei Otcheretianskib176e622013-11-18 19:06:49 +0200576 struct cfg80211_mgmt_tx_params *params, u64 *cookie)
Jouni Malinen026331c2010-02-15 12:53:10 +0200577{
Jouni Malinen026331c2010-02-15 12:53:10 +0200578 const struct ieee80211_mgmt *mgmt;
Johannes Berg2e161f72010-08-12 15:38:38 +0200579 u16 stype;
Jouni Malinen026331c2010-02-15 12:53:10 +0200580
Johannes Berg2e161f72010-08-12 15:38:38 +0200581 if (!wdev->wiphy->mgmt_stypes)
Jouni Malinen026331c2010-02-15 12:53:10 +0200582 return -EOPNOTSUPP;
Johannes Berg2e161f72010-08-12 15:38:38 +0200583
584 if (!rdev->ops->mgmt_tx)
585 return -EOPNOTSUPP;
586
Andrei Otcheretianskib176e622013-11-18 19:06:49 +0200587 if (params->len < 24 + 1)
Jouni Malinen026331c2010-02-15 12:53:10 +0200588 return -EINVAL;
589
Andrei Otcheretianskib176e622013-11-18 19:06:49 +0200590 mgmt = (const struct ieee80211_mgmt *)params->buf;
Johannes Berg2e161f72010-08-12 15:38:38 +0200591
592 if (!ieee80211_is_mgmt(mgmt->frame_control))
Jouni Malinen026331c2010-02-15 12:53:10 +0200593 return -EINVAL;
Johannes Berg2e161f72010-08-12 15:38:38 +0200594
595 stype = le16_to_cpu(mgmt->frame_control) & IEEE80211_FCTL_STYPE;
596 if (!(wdev->wiphy->mgmt_stypes[wdev->iftype].tx & BIT(stype >> 4)))
597 return -EINVAL;
598
599 if (ieee80211_is_action(mgmt->frame_control) &&
600 mgmt->u.action.category != WLAN_CATEGORY_PUBLIC) {
Johannes Berg663fcaf2010-09-30 21:06:09 +0200601 int err = 0;
602
Johannes Bergfe100ac2010-08-09 15:52:03 +0200603 wdev_lock(wdev);
604
Johannes Berg663fcaf2010-09-30 21:06:09 +0200605 switch (wdev->iftype) {
606 case NL80211_IFTYPE_ADHOC:
607 case NL80211_IFTYPE_STATION:
608 case NL80211_IFTYPE_P2P_CLIENT:
609 if (!wdev->current_bss) {
610 err = -ENOTCONN;
611 break;
612 }
Johannes Bergfe100ac2010-08-09 15:52:03 +0200613
Joe Perchesac422d32012-05-08 18:56:55 +0000614 if (!ether_addr_equal(wdev->current_bss->pub.bssid,
615 mgmt->bssid)) {
Johannes Berg663fcaf2010-09-30 21:06:09 +0200616 err = -ENOTCONN;
617 break;
618 }
619
620 /*
621 * check for IBSS DA must be done by driver as
622 * cfg80211 doesn't track the stations
623 */
624 if (wdev->iftype == NL80211_IFTYPE_ADHOC)
625 break;
626
627 /* for station, check that DA is the AP */
Joe Perchesac422d32012-05-08 18:56:55 +0000628 if (!ether_addr_equal(wdev->current_bss->pub.bssid,
629 mgmt->da)) {
Johannes Berg663fcaf2010-09-30 21:06:09 +0200630 err = -ENOTCONN;
631 break;
632 }
633 break;
634 case NL80211_IFTYPE_AP:
635 case NL80211_IFTYPE_P2P_GO:
636 case NL80211_IFTYPE_AP_VLAN:
Johannes Berg98104fde2012-06-16 00:19:54 +0200637 if (!ether_addr_equal(mgmt->bssid, wdev_address(wdev)))
Johannes Berg663fcaf2010-09-30 21:06:09 +0200638 err = -EINVAL;
639 break;
Javier Cardona0778a6a2011-05-03 16:57:08 -0700640 case NL80211_IFTYPE_MESH_POINT:
Joe Perchesac422d32012-05-08 18:56:55 +0000641 if (!ether_addr_equal(mgmt->sa, mgmt->bssid)) {
Javier Cardona0778a6a2011-05-03 16:57:08 -0700642 err = -EINVAL;
643 break;
644 }
645 /*
646 * check for mesh DA must be done by driver as
647 * cfg80211 doesn't track the stations
648 */
649 break;
Johannes Berg98104fde2012-06-16 00:19:54 +0200650 case NL80211_IFTYPE_P2P_DEVICE:
651 /*
652 * fall through, P2P device only supports
653 * public action frames
654 */
Ayala Bekercb3b7d82016-09-20 17:31:13 +0300655 case NL80211_IFTYPE_NAN:
Johannes Berg663fcaf2010-09-30 21:06:09 +0200656 default:
657 err = -EOPNOTSUPP;
658 break;
659 }
Johannes Bergfe100ac2010-08-09 15:52:03 +0200660 wdev_unlock(wdev);
Johannes Berg663fcaf2010-09-30 21:06:09 +0200661
662 if (err)
663 return err;
Jouni Malinen026331c2010-02-15 12:53:10 +0200664 }
665
vamsi krishnaab5bb2d2017-01-13 01:12:19 +0200666 if (!ether_addr_equal(mgmt->sa, wdev_address(wdev))) {
667 /* Allow random TA to be used with Public Action frames if the
668 * driver has indicated support for this. Otherwise, only allow
669 * the local address to be used.
670 */
671 if (!ieee80211_is_action(mgmt->frame_control) ||
672 mgmt->u.action.category != WLAN_CATEGORY_PUBLIC)
673 return -EINVAL;
674 if (!wdev->current_bss &&
675 !wiphy_ext_feature_isset(
676 &rdev->wiphy,
677 NL80211_EXT_FEATURE_MGMT_TX_RANDOM_TA))
678 return -EINVAL;
679 if (wdev->current_bss &&
680 !wiphy_ext_feature_isset(
681 &rdev->wiphy,
682 NL80211_EXT_FEATURE_MGMT_TX_RANDOM_TA_CONNECTED))
683 return -EINVAL;
684 }
Jouni Malinen026331c2010-02-15 12:53:10 +0200685
686 /* Transmit the Action frame as requested by user space */
Andrei Otcheretianskib176e622013-11-18 19:06:49 +0200687 return rdev_mgmt_tx(rdev, wdev, params, cookie);
Jouni Malinen026331c2010-02-15 12:53:10 +0200688}
689
Johannes Berg71bbc992012-06-15 15:30:18 +0200690bool cfg80211_rx_mgmt(struct wireless_dev *wdev, int freq, int sig_mbm,
Vladimir Kondratiev970fdfa2014-08-11 03:29:57 -0700691 const u8 *buf, size_t len, u32 flags)
Jouni Malinen026331c2010-02-15 12:53:10 +0200692{
Jouni Malinen026331c2010-02-15 12:53:10 +0200693 struct wiphy *wiphy = wdev->wiphy;
Zhao, Gangf26cbf42014-04-21 12:53:03 +0800694 struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
Johannes Berg2e161f72010-08-12 15:38:38 +0200695 struct cfg80211_mgmt_registration *reg;
696 const struct ieee80211_txrx_stypes *stypes =
697 &wiphy->mgmt_stypes[wdev->iftype];
698 struct ieee80211_mgmt *mgmt = (void *)buf;
699 const u8 *data;
700 int data_len;
Jouni Malinen026331c2010-02-15 12:53:10 +0200701 bool result = false;
Johannes Berg2e161f72010-08-12 15:38:38 +0200702 __le16 ftype = mgmt->frame_control &
703 cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE);
704 u16 stype;
Jouni Malinen026331c2010-02-15 12:53:10 +0200705
Beni Lev4ee3e062012-08-27 12:49:39 +0300706 trace_cfg80211_rx_mgmt(wdev, freq, sig_mbm);
Johannes Berg2e161f72010-08-12 15:38:38 +0200707 stype = (le16_to_cpu(mgmt->frame_control) & IEEE80211_FCTL_STYPE) >> 4;
Jouni Malinen026331c2010-02-15 12:53:10 +0200708
Beni Lev4ee3e062012-08-27 12:49:39 +0300709 if (!(stypes->rx & BIT(stype))) {
710 trace_cfg80211_return_bool(false);
Johannes Berg2e161f72010-08-12 15:38:38 +0200711 return false;
Beni Lev4ee3e062012-08-27 12:49:39 +0300712 }
Jouni Malinen026331c2010-02-15 12:53:10 +0200713
Johannes Berg2e161f72010-08-12 15:38:38 +0200714 data = buf + ieee80211_hdrlen(mgmt->frame_control);
715 data_len = len - ieee80211_hdrlen(mgmt->frame_control);
Jouni Malinen026331c2010-02-15 12:53:10 +0200716
Johannes Berg2e161f72010-08-12 15:38:38 +0200717 spin_lock_bh(&wdev->mgmt_registrations_lock);
718
719 list_for_each_entry(reg, &wdev->mgmt_registrations, list) {
720 if (reg->frame_type != ftype)
Jouni Malinen026331c2010-02-15 12:53:10 +0200721 continue;
722
Johannes Berg2e161f72010-08-12 15:38:38 +0200723 if (reg->match_len > data_len)
724 continue;
725
726 if (memcmp(reg->match, data, reg->match_len))
Jouni Malinen026331c2010-02-15 12:53:10 +0200727 continue;
728
729 /* found match! */
730
731 /* Indicate the received Action frame to user space */
Eric W. Biederman15e47302012-09-07 20:12:54 +0000732 if (nl80211_send_mgmt(rdev, wdev, reg->nlportid,
Johannes Berg804483e2012-03-05 22:18:41 +0100733 freq, sig_mbm,
Vladimir Kondratiev970fdfa2014-08-11 03:29:57 -0700734 buf, len, flags, GFP_ATOMIC))
Jouni Malinen026331c2010-02-15 12:53:10 +0200735 continue;
736
737 result = true;
738 break;
739 }
740
Johannes Berg2e161f72010-08-12 15:38:38 +0200741 spin_unlock_bh(&wdev->mgmt_registrations_lock);
Jouni Malinen026331c2010-02-15 12:53:10 +0200742
Beni Lev4ee3e062012-08-27 12:49:39 +0300743 trace_cfg80211_return_bool(result);
Jouni Malinen026331c2010-02-15 12:53:10 +0200744 return result;
745}
Johannes Berg2e161f72010-08-12 15:38:38 +0200746EXPORT_SYMBOL(cfg80211_rx_mgmt);
Jouni Malinen026331c2010-02-15 12:53:10 +0200747
Simon Wunderlich04f39042013-02-08 18:16:19 +0100748void cfg80211_dfs_channels_update_work(struct work_struct *work)
749{
Geliang Tanga85a7e22016-01-01 23:48:52 +0800750 struct delayed_work *delayed_work = to_delayed_work(work);
Simon Wunderlich04f39042013-02-08 18:16:19 +0100751 struct cfg80211_registered_device *rdev;
752 struct cfg80211_chan_def chandef;
753 struct ieee80211_supported_band *sband;
754 struct ieee80211_channel *c;
755 struct wiphy *wiphy;
756 bool check_again = false;
757 unsigned long timeout, next_time = 0;
758 int bandid, i;
759
Simon Wunderlich04f39042013-02-08 18:16:19 +0100760 rdev = container_of(delayed_work, struct cfg80211_registered_device,
761 dfs_update_channels_wk);
762 wiphy = &rdev->wiphy;
763
Johannes Berg5fe231e2013-05-08 21:45:15 +0200764 rtnl_lock();
Johannes Berg57fbcce2016-04-12 15:56:15 +0200765 for (bandid = 0; bandid < NUM_NL80211_BANDS; bandid++) {
Simon Wunderlich04f39042013-02-08 18:16:19 +0100766 sband = wiphy->bands[bandid];
767 if (!sband)
768 continue;
769
770 for (i = 0; i < sband->n_channels; i++) {
771 c = &sband->channels[i];
772
773 if (c->dfs_state != NL80211_DFS_UNAVAILABLE)
774 continue;
775
Michal Kaziorc532a582013-10-17 11:21:11 -0700776 timeout = c->dfs_state_entered + msecs_to_jiffies(
777 IEEE80211_DFS_MIN_NOP_TIME_MS);
Simon Wunderlich04f39042013-02-08 18:16:19 +0100778
779 if (time_after_eq(jiffies, timeout)) {
780 c->dfs_state = NL80211_DFS_USABLE;
Michal Kaziorbbe09bb2013-10-17 11:21:12 -0700781 c->dfs_state_entered = jiffies;
782
Simon Wunderlich04f39042013-02-08 18:16:19 +0100783 cfg80211_chandef_create(&chandef, c,
784 NL80211_CHAN_NO_HT);
785
786 nl80211_radar_notify(rdev, &chandef,
787 NL80211_RADAR_NOP_FINISHED,
788 NULL, GFP_ATOMIC);
789 continue;
790 }
791
792 if (!check_again)
793 next_time = timeout - jiffies;
794 else
795 next_time = min(next_time, timeout - jiffies);
796 check_again = true;
797 }
798 }
Johannes Berg5fe231e2013-05-08 21:45:15 +0200799 rtnl_unlock();
Simon Wunderlich04f39042013-02-08 18:16:19 +0100800
801 /* reschedule if there are other channels waiting to be cleared again */
802 if (check_again)
803 queue_delayed_work(cfg80211_wq, &rdev->dfs_update_channels_wk,
804 next_time);
805}
806
807
808void cfg80211_radar_event(struct wiphy *wiphy,
809 struct cfg80211_chan_def *chandef,
810 gfp_t gfp)
811{
Zhao, Gangf26cbf42014-04-21 12:53:03 +0800812 struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
Simon Wunderlich04f39042013-02-08 18:16:19 +0100813 unsigned long timeout;
814
815 trace_cfg80211_radar_event(wiphy, chandef);
816
817 /* only set the chandef supplied channel to unavailable, in
818 * case the radar is detected on only one of multiple channels
819 * spanned by the chandef.
820 */
821 cfg80211_set_dfs_state(wiphy, chandef, NL80211_DFS_UNAVAILABLE);
822
823 timeout = msecs_to_jiffies(IEEE80211_DFS_MIN_NOP_TIME_MS);
824 queue_delayed_work(cfg80211_wq, &rdev->dfs_update_channels_wk,
825 timeout);
826
827 nl80211_radar_notify(rdev, chandef, NL80211_RADAR_DETECTED, NULL, gfp);
828}
829EXPORT_SYMBOL(cfg80211_radar_event);
830
831void cfg80211_cac_event(struct net_device *netdev,
Janusz Dziedzicd2859df2013-11-06 13:55:51 +0100832 const struct cfg80211_chan_def *chandef,
Simon Wunderlich04f39042013-02-08 18:16:19 +0100833 enum nl80211_radar_event event, gfp_t gfp)
834{
835 struct wireless_dev *wdev = netdev->ieee80211_ptr;
836 struct wiphy *wiphy = wdev->wiphy;
Zhao, Gangf26cbf42014-04-21 12:53:03 +0800837 struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
Simon Wunderlich04f39042013-02-08 18:16:19 +0100838 unsigned long timeout;
839
840 trace_cfg80211_cac_event(netdev, event);
841
842 if (WARN_ON(!wdev->cac_started))
843 return;
844
Michal Kazior9e0e2962014-01-29 14:22:27 +0100845 if (WARN_ON(!wdev->chandef.chan))
Simon Wunderlich04f39042013-02-08 18:16:19 +0100846 return;
847
Simon Wunderlich04f39042013-02-08 18:16:19 +0100848 switch (event) {
849 case NL80211_RADAR_CAC_FINISHED:
850 timeout = wdev->cac_start_time +
Janusz Dziedzic31559f32014-02-21 19:46:13 +0100851 msecs_to_jiffies(wdev->cac_time_ms);
Simon Wunderlich04f39042013-02-08 18:16:19 +0100852 WARN_ON(!time_after_eq(jiffies, timeout));
Janusz Dziedzicd2859df2013-11-06 13:55:51 +0100853 cfg80211_set_dfs_state(wiphy, chandef, NL80211_DFS_AVAILABLE);
Simon Wunderlich04f39042013-02-08 18:16:19 +0100854 break;
855 case NL80211_RADAR_CAC_ABORTED:
856 break;
857 default:
858 WARN_ON(1);
859 return;
860 }
861 wdev->cac_started = false;
862
Janusz Dziedzicd2859df2013-11-06 13:55:51 +0100863 nl80211_radar_notify(rdev, chandef, event, netdev, gfp);
Simon Wunderlich04f39042013-02-08 18:16:19 +0100864}
865EXPORT_SYMBOL(cfg80211_cac_event);