Blame view

net/wireless/mlme.c 24.3 KB
6039f6d23   Jouni Malinen   nl80211: Event no...
1
2
3
4
5
6
7
8
  /*
   * cfg80211 MLME SAP interface
   *
   * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
   */
  
  #include <linux/kernel.h>
  #include <linux/module.h>
c6fb08aaa   Felix Fietkau   cfg80211: use com...
9
  #include <linux/etherdevice.h>
6039f6d23   Jouni Malinen   nl80211: Event no...
10
11
  #include <linux/netdevice.h>
  #include <linux/nl80211.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
12
  #include <linux/slab.h>
a9a11622c   Johannes Berg   cfg80211: self-co...
13
  #include <linux/wireless.h>
6039f6d23   Jouni Malinen   nl80211: Event no...
14
  #include <net/cfg80211.h>
a9a11622c   Johannes Berg   cfg80211: self-co...
15
  #include <net/iw_handler.h>
6039f6d23   Jouni Malinen   nl80211: Event no...
16
17
  #include "core.h"
  #include "nl80211.h"
e35e4d28b   Hila Gonen   cfg80211: add wra...
18
  #include "rdev-ops.h"
6039f6d23   Jouni Malinen   nl80211: Event no...
19

cb0b4beb9   Johannes Berg   cfg80211: mlme AP...
20
  void cfg80211_send_rx_auth(struct net_device *dev, const u8 *buf, size_t len)
6039f6d23   Jouni Malinen   nl80211: Event no...
21
  {
19957bb39   Johannes Berg   cfg80211: keep tr...
22
23
  	struct wireless_dev *wdev = dev->ieee80211_ptr;
  	struct wiphy *wiphy = wdev->wiphy;
6039f6d23   Jouni Malinen   nl80211: Event no...
24
  	struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
19957bb39   Johannes Berg   cfg80211: keep tr...
25

4ee3e063f   Beni Lev   cfg80211: add cfg...
26
  	trace_cfg80211_send_rx_auth(dev);
667503ddc   Johannes Berg   cfg80211: fix loc...
27
  	wdev_lock(wdev);
cb0b4beb9   Johannes Berg   cfg80211: mlme AP...
28

95de817b9   Johannes Berg   cfg80211: stop tr...
29
30
  	nl80211_send_rx_auth(rdev, dev, buf, len, GFP_KERNEL);
  	cfg80211_sme_rx_auth(dev, buf, len);
667503ddc   Johannes Berg   cfg80211: fix loc...
31
32
  
  	wdev_unlock(wdev);
6039f6d23   Jouni Malinen   nl80211: Event no...
33
34
  }
  EXPORT_SYMBOL(cfg80211_send_rx_auth);
95de817b9   Johannes Berg   cfg80211: stop tr...
35
36
  void cfg80211_send_rx_assoc(struct net_device *dev, struct cfg80211_bss *bss,
  			    const u8 *buf, size_t len)
6039f6d23   Jouni Malinen   nl80211: Event no...
37
  {
6829c878e   Johannes Berg   cfg80211: emulate...
38
39
40
  	u16 status_code;
  	struct wireless_dev *wdev = dev->ieee80211_ptr;
  	struct wiphy *wiphy = wdev->wiphy;
6039f6d23   Jouni Malinen   nl80211: Event no...
41
  	struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
6829c878e   Johannes Berg   cfg80211: emulate...
42
43
  	struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf;
  	u8 *ie = mgmt->u.assoc_resp.variable;
95de817b9   Johannes Berg   cfg80211: stop tr...
44
  	int ieoffs = offsetof(struct ieee80211_mgmt, u.assoc_resp.variable);
6829c878e   Johannes Berg   cfg80211: emulate...
45

4ee3e063f   Beni Lev   cfg80211: add cfg...
46
  	trace_cfg80211_send_rx_assoc(dev, bss);
667503ddc   Johannes Berg   cfg80211: fix loc...
47
  	wdev_lock(wdev);
cb0b4beb9   Johannes Berg   cfg80211: mlme AP...
48

6829c878e   Johannes Berg   cfg80211: emulate...
49
  	status_code = le16_to_cpu(mgmt->u.assoc_resp.status_code);
f401a6f7e   Johannes Berg   cfg80211: use rea...
50
51
52
53
54
55
56
  	/*
  	 * This is a bit of a hack, we don't notify userspace of
  	 * a (re-)association reply if we tried to send a reassoc
  	 * and got a reject -- we only try again with an assoc
  	 * frame instead of reassoc.
  	 */
  	if (status_code != WLAN_STATUS_SUCCESS && wdev->conn &&
95de817b9   Johannes Berg   cfg80211: stop tr...
57
  	    cfg80211_sme_failed_reassoc(wdev)) {
5b112d3d0   Johannes Berg   cfg80211: pass wi...
58
  		cfg80211_put_bss(wiphy, bss);
f401a6f7e   Johannes Berg   cfg80211: use rea...
59
  		goto out;
95de817b9   Johannes Berg   cfg80211: stop tr...
60
  	}
f401a6f7e   Johannes Berg   cfg80211: use rea...
61

cb0b4beb9   Johannes Berg   cfg80211: mlme AP...
62
  	nl80211_send_rx_assoc(rdev, dev, buf, len, GFP_KERNEL);
6829c878e   Johannes Berg   cfg80211: emulate...
63

95de817b9   Johannes Berg   cfg80211: stop tr...
64
  	if (status_code != WLAN_STATUS_SUCCESS && wdev->conn) {
7d930bc33   Johannes Berg   cfg80211: sme: de...
65
  		cfg80211_sme_failed_assoc(wdev);
7d930bc33   Johannes Berg   cfg80211: sme: de...
66
67
68
69
  		/*
  		 * do not call connect_result() now because the
  		 * sme will schedule work that does it later.
  		 */
5b112d3d0   Johannes Berg   cfg80211: pass wi...
70
  		cfg80211_put_bss(wiphy, bss);
7d930bc33   Johannes Berg   cfg80211: sme: de...
71
  		goto out;
df7fc0f97   Johannes Berg   cfg80211: keep tr...
72
  	}
ea416a793   Johannes Berg   cfg80211: report ...
73
74
75
76
77
78
79
80
  	if (!wdev->conn && wdev->sme_state == CFG80211_SME_IDLE) {
  		/*
  		 * This is for the userspace SME, the CONNECTING
  		 * state will be changed to CONNECTED by
  		 * __cfg80211_connect_result() below.
  		 */
  		wdev->sme_state = CFG80211_SME_CONNECTING;
  	}
95de817b9   Johannes Berg   cfg80211: stop tr...
81
  	/* this consumes the bss reference */
df7fc0f97   Johannes Berg   cfg80211: keep tr...
82
83
  	__cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, ie, len - ieoffs,
  				  status_code,
95de817b9   Johannes Berg   cfg80211: stop tr...
84
  				  status_code == WLAN_STATUS_SUCCESS, bss);
f401a6f7e   Johannes Berg   cfg80211: use rea...
85
   out:
667503ddc   Johannes Berg   cfg80211: fix loc...
86
  	wdev_unlock(wdev);
6039f6d23   Jouni Malinen   nl80211: Event no...
87
88
  }
  EXPORT_SYMBOL(cfg80211_send_rx_assoc);
ce470613c   Holger Schurig   cfg80211: no cook...
89
  void __cfg80211_send_deauth(struct net_device *dev,
667503ddc   Johannes Berg   cfg80211: fix loc...
90
  				   const u8 *buf, size_t len)
6039f6d23   Jouni Malinen   nl80211: Event no...
91
  {
6829c878e   Johannes Berg   cfg80211: emulate...
92
93
  	struct wireless_dev *wdev = dev->ieee80211_ptr;
  	struct wiphy *wiphy = wdev->wiphy;
6039f6d23   Jouni Malinen   nl80211: Event no...
94
  	struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
6829c878e   Johannes Berg   cfg80211: emulate...
95
  	struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf;
19957bb39   Johannes Berg   cfg80211: keep tr...
96
  	const u8 *bssid = mgmt->bssid;
95de817b9   Johannes Berg   cfg80211: stop tr...
97
  	bool was_current = false;
6829c878e   Johannes Berg   cfg80211: emulate...
98

4ee3e063f   Beni Lev   cfg80211: add cfg...
99
  	trace___cfg80211_send_deauth(dev);
667503ddc   Johannes Berg   cfg80211: fix loc...
100
  	ASSERT_WDEV_LOCK(wdev);
cb0b4beb9   Johannes Berg   cfg80211: mlme AP...
101

19957bb39   Johannes Berg   cfg80211: keep tr...
102
  	if (wdev->current_bss &&
ac422d3cc   Joe Perches   wireless: Convert...
103
  	    ether_addr_equal(wdev->current_bss->pub.bssid, bssid)) {
19957bb39   Johannes Berg   cfg80211: keep tr...
104
  		cfg80211_unhold_bss(wdev->current_bss);
5b112d3d0   Johannes Berg   cfg80211: pass wi...
105
  		cfg80211_put_bss(wiphy, &wdev->current_bss->pub);
19957bb39   Johannes Berg   cfg80211: keep tr...
106
  		wdev->current_bss = NULL;
3f3b6a8d9   Johannes Berg   cfg80211: deauth ...
107
  		was_current = true;
19957bb39   Johannes Berg   cfg80211: keep tr...
108
  	}
19957bb39   Johannes Berg   cfg80211: keep tr...
109

5fba4af32   Johannes Berg   cfg80211: avoid s...
110
  	nl80211_send_deauth(rdev, dev, buf, len, GFP_KERNEL);
3f3b6a8d9   Johannes Berg   cfg80211: deauth ...
111
  	if (wdev->sme_state == CFG80211_SME_CONNECTED && was_current) {
6829c878e   Johannes Berg   cfg80211: emulate...
112
113
114
115
  		u16 reason_code;
  		bool from_ap;
  
  		reason_code = le16_to_cpu(mgmt->u.deauth.reason_code);
ac422d3cc   Joe Perches   wireless: Convert...
116
  		from_ap = !ether_addr_equal(mgmt->sa, dev->dev_addr);
667503ddc   Johannes Berg   cfg80211: fix loc...
117
  		__cfg80211_disconnected(dev, NULL, 0, reason_code, from_ap);
6829c878e   Johannes Berg   cfg80211: emulate...
118
  	} else if (wdev->sme_state == CFG80211_SME_CONNECTING) {
667503ddc   Johannes Berg   cfg80211: fix loc...
119
120
  		__cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, NULL, 0,
  					  WLAN_STATUS_UNSPECIFIED_FAILURE,
df7fc0f97   Johannes Berg   cfg80211: keep tr...
121
  					  false, NULL);
667503ddc   Johannes Berg   cfg80211: fix loc...
122
123
  	}
  }
ce470613c   Holger Schurig   cfg80211: no cook...
124
  EXPORT_SYMBOL(__cfg80211_send_deauth);
667503ddc   Johannes Berg   cfg80211: fix loc...
125

ce470613c   Holger Schurig   cfg80211: no cook...
126
  void cfg80211_send_deauth(struct net_device *dev, const u8 *buf, size_t len)
667503ddc   Johannes Berg   cfg80211: fix loc...
127
128
  {
  	struct wireless_dev *wdev = dev->ieee80211_ptr;
ce470613c   Holger Schurig   cfg80211: no cook...
129
130
131
  	wdev_lock(wdev);
  	__cfg80211_send_deauth(dev, buf, len);
  	wdev_unlock(wdev);
6039f6d23   Jouni Malinen   nl80211: Event no...
132
  }
53b46b844   Jouni Malinen   nl80211: Generate...
133
  EXPORT_SYMBOL(cfg80211_send_deauth);
6039f6d23   Jouni Malinen   nl80211: Event no...
134

ce470613c   Holger Schurig   cfg80211: no cook...
135
  void __cfg80211_send_disassoc(struct net_device *dev,
667503ddc   Johannes Berg   cfg80211: fix loc...
136
  				     const u8 *buf, size_t len)
6039f6d23   Jouni Malinen   nl80211: Event no...
137
  {
6829c878e   Johannes Berg   cfg80211: emulate...
138
139
  	struct wireless_dev *wdev = dev->ieee80211_ptr;
  	struct wiphy *wiphy = wdev->wiphy;
6039f6d23   Jouni Malinen   nl80211: Event no...
140
  	struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
6829c878e   Johannes Berg   cfg80211: emulate...
141
  	struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf;
19957bb39   Johannes Berg   cfg80211: keep tr...
142
  	const u8 *bssid = mgmt->bssid;
19957bb39   Johannes Berg   cfg80211: keep tr...
143
144
  	u16 reason_code;
  	bool from_ap;
6829c878e   Johannes Berg   cfg80211: emulate...
145

4ee3e063f   Beni Lev   cfg80211: add cfg...
146
  	trace___cfg80211_send_disassoc(dev);
596a07c18   Johannes Berg   cfg80211: fix mor...
147
  	ASSERT_WDEV_LOCK(wdev);
cb0b4beb9   Johannes Berg   cfg80211: mlme AP...
148
149
  
  	nl80211_send_disassoc(rdev, dev, buf, len, GFP_KERNEL);
a3b8b0569   Jouni Malinen   nl80211: Add Mich...
150

596a07c18   Johannes Berg   cfg80211: fix mor...
151
152
  	if (wdev->sme_state != CFG80211_SME_CONNECTED)
  		return;
6829c878e   Johannes Berg   cfg80211: emulate...
153

19957bb39   Johannes Berg   cfg80211: keep tr...
154
  	if (wdev->current_bss &&
ac422d3cc   Joe Perches   wireless: Convert...
155
  	    ether_addr_equal(wdev->current_bss->pub.bssid, bssid)) {
95de817b9   Johannes Berg   cfg80211: stop tr...
156
157
  		cfg80211_sme_disassoc(dev, wdev->current_bss);
  		cfg80211_unhold_bss(wdev->current_bss);
5b112d3d0   Johannes Berg   cfg80211: pass wi...
158
  		cfg80211_put_bss(wiphy, &wdev->current_bss->pub);
95de817b9   Johannes Berg   cfg80211: stop tr...
159
  		wdev->current_bss = NULL;
19957bb39   Johannes Berg   cfg80211: keep tr...
160
161
  	} else
  		WARN_ON(1);
6829c878e   Johannes Berg   cfg80211: emulate...
162

6829c878e   Johannes Berg   cfg80211: emulate...
163

19957bb39   Johannes Berg   cfg80211: keep tr...
164
  	reason_code = le16_to_cpu(mgmt->u.disassoc.reason_code);
ac422d3cc   Joe Perches   wireless: Convert...
165
  	from_ap = !ether_addr_equal(mgmt->sa, dev->dev_addr);
667503ddc   Johannes Berg   cfg80211: fix loc...
166
  	__cfg80211_disconnected(dev, NULL, 0, reason_code, from_ap);
667503ddc   Johannes Berg   cfg80211: fix loc...
167
  }
ce470613c   Holger Schurig   cfg80211: no cook...
168
  EXPORT_SYMBOL(__cfg80211_send_disassoc);
667503ddc   Johannes Berg   cfg80211: fix loc...
169

ce470613c   Holger Schurig   cfg80211: no cook...
170
  void cfg80211_send_disassoc(struct net_device *dev, const u8 *buf, size_t len)
667503ddc   Johannes Berg   cfg80211: fix loc...
171
172
  {
  	struct wireless_dev *wdev = dev->ieee80211_ptr;
ce470613c   Holger Schurig   cfg80211: no cook...
173
174
175
  	wdev_lock(wdev);
  	__cfg80211_send_disassoc(dev, buf, len);
  	wdev_unlock(wdev);
1965c8533   Jouni Malinen   nl80211: Add even...
176
  }
6829c878e   Johannes Berg   cfg80211: emulate...
177
  EXPORT_SYMBOL(cfg80211_send_disassoc);
1965c8533   Jouni Malinen   nl80211: Add even...
178

a58ce43f2   Johannes Berg   mac80211: avoid s...
179
180
181
182
183
  void cfg80211_send_auth_timeout(struct net_device *dev, const u8 *addr)
  {
  	struct wireless_dev *wdev = dev->ieee80211_ptr;
  	struct wiphy *wiphy = wdev->wiphy;
  	struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
4ee3e063f   Beni Lev   cfg80211: add cfg...
184
  	trace_cfg80211_send_auth_timeout(dev, addr);
a58ce43f2   Johannes Berg   mac80211: avoid s...
185
186
187
188
189
190
191
  	wdev_lock(wdev);
  
  	nl80211_send_auth_timeout(rdev, dev, addr, GFP_KERNEL);
  	if (wdev->sme_state == CFG80211_SME_CONNECTING)
  		__cfg80211_connect_result(dev, addr, NULL, 0, NULL, 0,
  					  WLAN_STATUS_UNSPECIFIED_FAILURE,
  					  false, NULL);
667503ddc   Johannes Berg   cfg80211: fix loc...
192
  	wdev_unlock(wdev);
1965c8533   Jouni Malinen   nl80211: Add even...
193
194
  }
  EXPORT_SYMBOL(cfg80211_send_auth_timeout);
cb0b4beb9   Johannes Berg   cfg80211: mlme AP...
195
  void cfg80211_send_assoc_timeout(struct net_device *dev, const u8 *addr)
1965c8533   Jouni Malinen   nl80211: Add even...
196
  {
6829c878e   Johannes Berg   cfg80211: emulate...
197
198
  	struct wireless_dev *wdev = dev->ieee80211_ptr;
  	struct wiphy *wiphy = wdev->wiphy;
1965c8533   Jouni Malinen   nl80211: Add even...
199
  	struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
19957bb39   Johannes Berg   cfg80211: keep tr...
200

4ee3e063f   Beni Lev   cfg80211: add cfg...
201
  	trace_cfg80211_send_assoc_timeout(dev, addr);
667503ddc   Johannes Berg   cfg80211: fix loc...
202
  	wdev_lock(wdev);
cb0b4beb9   Johannes Berg   cfg80211: mlme AP...
203
204
  
  	nl80211_send_assoc_timeout(rdev, dev, addr, GFP_KERNEL);
6829c878e   Johannes Berg   cfg80211: emulate...
205
  	if (wdev->sme_state == CFG80211_SME_CONNECTING)
667503ddc   Johannes Berg   cfg80211: fix loc...
206
207
  		__cfg80211_connect_result(dev, addr, NULL, 0, NULL, 0,
  					  WLAN_STATUS_UNSPECIFIED_FAILURE,
df7fc0f97   Johannes Berg   cfg80211: keep tr...
208
  					  false, NULL);
19957bb39   Johannes Berg   cfg80211: keep tr...
209

667503ddc   Johannes Berg   cfg80211: fix loc...
210
  	wdev_unlock(wdev);
1965c8533   Jouni Malinen   nl80211: Add even...
211
212
  }
  EXPORT_SYMBOL(cfg80211_send_assoc_timeout);
a3b8b0569   Jouni Malinen   nl80211: Add Mich...
213
214
  void cfg80211_michael_mic_failure(struct net_device *dev, const u8 *addr,
  				  enum nl80211_key_type key_type, int key_id,
e6d6e3420   Johannes Berg   cfg80211: use pro...
215
  				  const u8 *tsc, gfp_t gfp)
a3b8b0569   Jouni Malinen   nl80211: Add Mich...
216
217
218
  {
  	struct wiphy *wiphy = dev->ieee80211_ptr->wiphy;
  	struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
3d23e349d   Johannes Berg   wext: refactor
219
  #ifdef CONFIG_CFG80211_WEXT
f58d4ed98   Johannes Berg   cfg80211: send we...
220
  	union iwreq_data wrqu;
e6d6e3420   Johannes Berg   cfg80211: use pro...
221
  	char *buf = kmalloc(128, gfp);
f58d4ed98   Johannes Berg   cfg80211: send we...
222
223
224
225
226
227
228
229
230
231
232
233
  
  	if (buf) {
  		sprintf(buf, "MLME-MICHAELMICFAILURE.indication("
  			"keyid=%d %scast addr=%pM)", key_id,
  			key_type == NL80211_KEYTYPE_GROUP ? "broad" : "uni",
  			addr);
  		memset(&wrqu, 0, sizeof(wrqu));
  		wrqu.data.length = strlen(buf);
  		wireless_send_event(dev, IWEVCUSTOM, &wrqu, buf);
  		kfree(buf);
  	}
  #endif
4ee3e063f   Beni Lev   cfg80211: add cfg...
234
  	trace_cfg80211_michael_mic_failure(dev, addr, key_type, key_id, tsc);
e6d6e3420   Johannes Berg   cfg80211: use pro...
235
  	nl80211_michael_mic_failure(rdev, dev, addr, key_type, key_id, tsc, gfp);
a3b8b0569   Jouni Malinen   nl80211: Add Mich...
236
237
  }
  EXPORT_SYMBOL(cfg80211_michael_mic_failure);
19957bb39   Johannes Berg   cfg80211: keep tr...
238
239
  
  /* some MLME handling for userspace SME */
667503ddc   Johannes Berg   cfg80211: fix loc...
240
241
242
243
244
245
  int __cfg80211_mlme_auth(struct cfg80211_registered_device *rdev,
  			 struct net_device *dev,
  			 struct ieee80211_channel *chan,
  			 enum nl80211_auth_type auth_type,
  			 const u8 *bssid,
  			 const u8 *ssid, int ssid_len,
fffd0934b   Johannes Berg   cfg80211: rework ...
246
  			 const u8 *ie, int ie_len,
e39e5b5e7   Jouni Malinen   cfg80211: Allow u...
247
248
  			 const u8 *key, int key_len, int key_idx,
  			 const u8 *sae_data, int sae_data_len)
19957bb39   Johannes Berg   cfg80211: keep tr...
249
250
251
  {
  	struct wireless_dev *wdev = dev->ieee80211_ptr;
  	struct cfg80211_auth_request req;
95de817b9   Johannes Berg   cfg80211: stop tr...
252
  	int err;
19957bb39   Johannes Berg   cfg80211: keep tr...
253

667503ddc   Johannes Berg   cfg80211: fix loc...
254
  	ASSERT_WDEV_LOCK(wdev);
fffd0934b   Johannes Berg   cfg80211: rework ...
255
256
257
  	if (auth_type == NL80211_AUTHTYPE_SHARED_KEY)
  		if (!key || !key_len || key_idx < 0 || key_idx > 4)
  			return -EINVAL;
0a9b5e179   Johannes Berg   cfg80211: refuse ...
258
  	if (wdev->current_bss &&
ac422d3cc   Joe Perches   wireless: Convert...
259
  	    ether_addr_equal(bssid, wdev->current_bss->pub.bssid))
0a9b5e179   Johannes Berg   cfg80211: refuse ...
260
  		return -EALREADY;
19957bb39   Johannes Berg   cfg80211: keep tr...
261
262
263
264
  	memset(&req, 0, sizeof(req));
  
  	req.ie = ie;
  	req.ie_len = ie_len;
e39e5b5e7   Jouni Malinen   cfg80211: Allow u...
265
266
  	req.sae_data = sae_data;
  	req.sae_data_len = sae_data_len;
19957bb39   Johannes Berg   cfg80211: keep tr...
267
268
269
  	req.auth_type = auth_type;
  	req.bss = cfg80211_get_bss(&rdev->wiphy, chan, bssid, ssid, ssid_len,
  				   WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS);
fffd0934b   Johannes Berg   cfg80211: rework ...
270
271
272
  	req.key = key;
  	req.key_len = key_len;
  	req.key_idx = key_idx;
19957bb39   Johannes Berg   cfg80211: keep tr...
273
274
  	if (!req.bss)
  		return -ENOENT;
e4e32459c   Michal Kazior   cfg80211: respect...
275
276
277
278
  	err = cfg80211_can_use_chan(rdev, wdev, req.bss->channel,
  				    CHAN_MODE_SHARED);
  	if (err)
  		goto out;
e35e4d28b   Hila Gonen   cfg80211: add wra...
279
  	err = rdev_auth(rdev, dev, &req);
19957bb39   Johannes Berg   cfg80211: keep tr...
280

e4e32459c   Michal Kazior   cfg80211: respect...
281
  out:
5b112d3d0   Johannes Berg   cfg80211: pass wi...
282
  	cfg80211_put_bss(&rdev->wiphy, req.bss);
19957bb39   Johannes Berg   cfg80211: keep tr...
283
284
  	return err;
  }
667503ddc   Johannes Berg   cfg80211: fix loc...
285
286
287
288
  int cfg80211_mlme_auth(struct cfg80211_registered_device *rdev,
  		       struct net_device *dev, struct ieee80211_channel *chan,
  		       enum nl80211_auth_type auth_type, const u8 *bssid,
  		       const u8 *ssid, int ssid_len,
fffd0934b   Johannes Berg   cfg80211: rework ...
289
  		       const u8 *ie, int ie_len,
e39e5b5e7   Jouni Malinen   cfg80211: Allow u...
290
291
  		       const u8 *key, int key_len, int key_idx,
  		       const u8 *sae_data, int sae_data_len)
667503ddc   Johannes Berg   cfg80211: fix loc...
292
293
  {
  	int err;
e4e32459c   Michal Kazior   cfg80211: respect...
294
  	mutex_lock(&rdev->devlist_mtx);
667503ddc   Johannes Berg   cfg80211: fix loc...
295
296
  	wdev_lock(dev->ieee80211_ptr);
  	err = __cfg80211_mlme_auth(rdev, dev, chan, auth_type, bssid,
fffd0934b   Johannes Berg   cfg80211: rework ...
297
  				   ssid, ssid_len, ie, ie_len,
e39e5b5e7   Jouni Malinen   cfg80211: Allow u...
298
299
  				   key, key_len, key_idx,
  				   sae_data, sae_data_len);
667503ddc   Johannes Berg   cfg80211: fix loc...
300
  	wdev_unlock(dev->ieee80211_ptr);
e4e32459c   Michal Kazior   cfg80211: respect...
301
  	mutex_unlock(&rdev->devlist_mtx);
667503ddc   Johannes Berg   cfg80211: fix loc...
302
303
304
  
  	return err;
  }
7e7c8926b   Ben Greear   wireless: Support...
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
  /*  Do a logical ht_capa &= ht_capa_mask.  */
  void cfg80211_oper_and_ht_capa(struct ieee80211_ht_cap *ht_capa,
  			       const struct ieee80211_ht_cap *ht_capa_mask)
  {
  	int i;
  	u8 *p1, *p2;
  	if (!ht_capa_mask) {
  		memset(ht_capa, 0, sizeof(*ht_capa));
  		return;
  	}
  
  	p1 = (u8*)(ht_capa);
  	p2 = (u8*)(ht_capa_mask);
  	for (i = 0; i<sizeof(*ht_capa); i++)
  		p1[i] &= p2[i];
  }
ee2aca343   Johannes Berg   cfg80211: add abi...
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
  /*  Do a logical ht_capa &= ht_capa_mask.  */
  void cfg80211_oper_and_vht_capa(struct ieee80211_vht_cap *vht_capa,
  				const struct ieee80211_vht_cap *vht_capa_mask)
  {
  	int i;
  	u8 *p1, *p2;
  	if (!vht_capa_mask) {
  		memset(vht_capa, 0, sizeof(*vht_capa));
  		return;
  	}
  
  	p1 = (u8*)(vht_capa);
  	p2 = (u8*)(vht_capa_mask);
  	for (i = 0; i < sizeof(*vht_capa); i++)
  		p1[i] &= p2[i];
  }
667503ddc   Johannes Berg   cfg80211: fix loc...
337
338
339
  int __cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev,
  			  struct net_device *dev,
  			  struct ieee80211_channel *chan,
f62fab735   Johannes Berg   cfg80211: refacto...
340
  			  const u8 *bssid,
667503ddc   Johannes Berg   cfg80211: fix loc...
341
  			  const u8 *ssid, int ssid_len,
f62fab735   Johannes Berg   cfg80211: refacto...
342
  			  struct cfg80211_assoc_request *req)
19957bb39   Johannes Berg   cfg80211: keep tr...
343
344
  {
  	struct wireless_dev *wdev = dev->ieee80211_ptr;
95de817b9   Johannes Berg   cfg80211: stop tr...
345
  	int err;
24b6b15f7   Jouni Malinen   cfg80211: Allow r...
346
  	bool was_connected = false;
19957bb39   Johannes Berg   cfg80211: keep tr...
347

667503ddc   Johannes Berg   cfg80211: fix loc...
348
  	ASSERT_WDEV_LOCK(wdev);
f62fab735   Johannes Berg   cfg80211: refacto...
349
350
  	if (wdev->current_bss && req->prev_bssid &&
  	    ether_addr_equal(wdev->current_bss->pub.bssid, req->prev_bssid)) {
24b6b15f7   Jouni Malinen   cfg80211: Allow r...
351
352
353
354
355
356
357
358
359
  		/*
  		 * Trying to reassociate: Allow this to proceed and let the old
  		 * association to be dropped when the new one is completed.
  		 */
  		if (wdev->sme_state == CFG80211_SME_CONNECTED) {
  			was_connected = true;
  			wdev->sme_state = CFG80211_SME_CONNECTING;
  		}
  	} else if (wdev->current_bss)
19957bb39   Johannes Berg   cfg80211: keep tr...
360
  		return -EALREADY;
f62fab735   Johannes Berg   cfg80211: refacto...
361
  	cfg80211_oper_and_ht_capa(&req->ht_capa_mask,
7e7c8926b   Ben Greear   wireless: Support...
362
  				  rdev->wiphy.ht_capa_mod_mask);
f62fab735   Johannes Berg   cfg80211: refacto...
363
  	cfg80211_oper_and_vht_capa(&req->vht_capa_mask,
ee2aca343   Johannes Berg   cfg80211: add abi...
364
  				   rdev->wiphy.vht_capa_mod_mask);
7e7c8926b   Ben Greear   wireless: Support...
365

f62fab735   Johannes Berg   cfg80211: refacto...
366
367
368
  	req->bss = cfg80211_get_bss(&rdev->wiphy, chan, bssid, ssid, ssid_len,
  				    WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS);
  	if (!req->bss) {
24b6b15f7   Jouni Malinen   cfg80211: Allow r...
369
370
  		if (was_connected)
  			wdev->sme_state = CFG80211_SME_CONNECTED;
19957bb39   Johannes Berg   cfg80211: keep tr...
371
  		return -ENOENT;
24b6b15f7   Jouni Malinen   cfg80211: Allow r...
372
  	}
19957bb39   Johannes Berg   cfg80211: keep tr...
373

f62fab735   Johannes Berg   cfg80211: refacto...
374
  	err = cfg80211_can_use_chan(rdev, wdev, chan, CHAN_MODE_SHARED);
e4e32459c   Michal Kazior   cfg80211: respect...
375
376
  	if (err)
  		goto out;
f62fab735   Johannes Berg   cfg80211: refacto...
377
  	err = rdev_assoc(rdev, dev, req);
19957bb39   Johannes Berg   cfg80211: keep tr...
378

e4e32459c   Michal Kazior   cfg80211: respect...
379
  out:
95de817b9   Johannes Berg   cfg80211: stop tr...
380
381
382
  	if (err) {
  		if (was_connected)
  			wdev->sme_state = CFG80211_SME_CONNECTED;
f62fab735   Johannes Berg   cfg80211: refacto...
383
  		cfg80211_put_bss(&rdev->wiphy, req->bss);
19957bb39   Johannes Berg   cfg80211: keep tr...
384
  	}
19957bb39   Johannes Berg   cfg80211: keep tr...
385
386
  	return err;
  }
667503ddc   Johannes Berg   cfg80211: fix loc...
387
388
389
  int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev,
  			struct net_device *dev,
  			struct ieee80211_channel *chan,
f62fab735   Johannes Berg   cfg80211: refacto...
390
  			const u8 *bssid,
667503ddc   Johannes Berg   cfg80211: fix loc...
391
  			const u8 *ssid, int ssid_len,
f62fab735   Johannes Berg   cfg80211: refacto...
392
  			struct cfg80211_assoc_request *req)
667503ddc   Johannes Berg   cfg80211: fix loc...
393
394
395
  {
  	struct wireless_dev *wdev = dev->ieee80211_ptr;
  	int err;
e4e32459c   Michal Kazior   cfg80211: respect...
396
  	mutex_lock(&rdev->devlist_mtx);
667503ddc   Johannes Berg   cfg80211: fix loc...
397
  	wdev_lock(wdev);
f62fab735   Johannes Berg   cfg80211: refacto...
398
399
  	err = __cfg80211_mlme_assoc(rdev, dev, chan, bssid,
  				    ssid, ssid_len, req);
667503ddc   Johannes Berg   cfg80211: fix loc...
400
  	wdev_unlock(wdev);
e4e32459c   Michal Kazior   cfg80211: respect...
401
  	mutex_unlock(&rdev->devlist_mtx);
667503ddc   Johannes Berg   cfg80211: fix loc...
402
403
404
405
406
407
  
  	return err;
  }
  
  int __cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev,
  			   struct net_device *dev, const u8 *bssid,
d5cdfacb3   Jouni Malinen   cfg80211: Add loc...
408
409
  			   const u8 *ie, int ie_len, u16 reason,
  			   bool local_state_change)
19957bb39   Johannes Berg   cfg80211: keep tr...
410
411
  {
  	struct wireless_dev *wdev = dev->ieee80211_ptr;
95de817b9   Johannes Berg   cfg80211: stop tr...
412
413
414
415
416
  	struct cfg80211_deauth_request req = {
  		.bssid = bssid,
  		.reason_code = reason,
  		.ie = ie,
  		.ie_len = ie_len,
6863255bd   Stanislaw Gruszka   cfg80211/mac80211...
417
  		.local_state_change = local_state_change,
95de817b9   Johannes Berg   cfg80211: stop tr...
418
  	};
19957bb39   Johannes Berg   cfg80211: keep tr...
419

667503ddc   Johannes Berg   cfg80211: fix loc...
420
  	ASSERT_WDEV_LOCK(wdev);
6863255bd   Stanislaw Gruszka   cfg80211/mac80211...
421
422
  	if (local_state_change && (!wdev->current_bss ||
  	    !ether_addr_equal(wdev->current_bss->pub.bssid, bssid)))
95de817b9   Johannes Berg   cfg80211: stop tr...
423
  		return 0;
19957bb39   Johannes Berg   cfg80211: keep tr...
424

e35e4d28b   Hila Gonen   cfg80211: add wra...
425
  	return rdev_deauth(rdev, dev, &req);
19957bb39   Johannes Berg   cfg80211: keep tr...
426
  }
667503ddc   Johannes Berg   cfg80211: fix loc...
427
428
  int cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev,
  			 struct net_device *dev, const u8 *bssid,
d5cdfacb3   Jouni Malinen   cfg80211: Add loc...
429
430
  			 const u8 *ie, int ie_len, u16 reason,
  			 bool local_state_change)
667503ddc   Johannes Berg   cfg80211: fix loc...
431
432
433
434
435
  {
  	struct wireless_dev *wdev = dev->ieee80211_ptr;
  	int err;
  
  	wdev_lock(wdev);
d5cdfacb3   Jouni Malinen   cfg80211: Add loc...
436
437
  	err = __cfg80211_mlme_deauth(rdev, dev, bssid, ie, ie_len, reason,
  				     local_state_change);
667503ddc   Johannes Berg   cfg80211: fix loc...
438
439
440
441
442
443
444
  	wdev_unlock(wdev);
  
  	return err;
  }
  
  static int __cfg80211_mlme_disassoc(struct cfg80211_registered_device *rdev,
  				    struct net_device *dev, const u8 *bssid,
d5cdfacb3   Jouni Malinen   cfg80211: Add loc...
445
446
  				    const u8 *ie, int ie_len, u16 reason,
  				    bool local_state_change)
19957bb39   Johannes Berg   cfg80211: keep tr...
447
448
449
  {
  	struct wireless_dev *wdev = dev->ieee80211_ptr;
  	struct cfg80211_disassoc_request req;
667503ddc   Johannes Berg   cfg80211: fix loc...
450
  	ASSERT_WDEV_LOCK(wdev);
f9d6b4026   Johannes Berg   cfg80211: fix dis...
451
452
  	if (wdev->sme_state != CFG80211_SME_CONNECTED)
  		return -ENOTCONN;
8dcf011ab   Johannes Berg   cfg80211: add SME...
453
454
  	if (WARN(!wdev->current_bss, "sme_state=%d
  ", wdev->sme_state))
f9d6b4026   Johannes Berg   cfg80211: fix dis...
455
  		return -ENOTCONN;
19957bb39   Johannes Berg   cfg80211: keep tr...
456
457
  	memset(&req, 0, sizeof(req));
  	req.reason_code = reason;
d5cdfacb3   Jouni Malinen   cfg80211: Add loc...
458
  	req.local_state_change = local_state_change;
19957bb39   Johannes Berg   cfg80211: keep tr...
459
460
  	req.ie = ie;
  	req.ie_len = ie_len;
ac422d3cc   Joe Perches   wireless: Convert...
461
  	if (ether_addr_equal(wdev->current_bss->pub.bssid, bssid))
19957bb39   Johannes Berg   cfg80211: keep tr...
462
463
464
  		req.bss = &wdev->current_bss->pub;
  	else
  		return -ENOTCONN;
e35e4d28b   Hila Gonen   cfg80211: add wra...
465
  	return rdev_disassoc(rdev, dev, &req);
667503ddc   Johannes Berg   cfg80211: fix loc...
466
467
468
469
  }
  
  int cfg80211_mlme_disassoc(struct cfg80211_registered_device *rdev,
  			   struct net_device *dev, const u8 *bssid,
d5cdfacb3   Jouni Malinen   cfg80211: Add loc...
470
471
  			   const u8 *ie, int ie_len, u16 reason,
  			   bool local_state_change)
667503ddc   Johannes Berg   cfg80211: fix loc...
472
473
474
475
476
  {
  	struct wireless_dev *wdev = dev->ieee80211_ptr;
  	int err;
  
  	wdev_lock(wdev);
d5cdfacb3   Jouni Malinen   cfg80211: Add loc...
477
478
  	err = __cfg80211_mlme_disassoc(rdev, dev, bssid, ie, ie_len, reason,
  				       local_state_change);
667503ddc   Johannes Berg   cfg80211: fix loc...
479
480
481
  	wdev_unlock(wdev);
  
  	return err;
19957bb39   Johannes Berg   cfg80211: keep tr...
482
483
484
485
486
487
488
  }
  
  void cfg80211_mlme_down(struct cfg80211_registered_device *rdev,
  			struct net_device *dev)
  {
  	struct wireless_dev *wdev = dev->ieee80211_ptr;
  	struct cfg80211_deauth_request req;
95de817b9   Johannes Berg   cfg80211: stop tr...
489
  	u8 bssid[ETH_ALEN];
19957bb39   Johannes Berg   cfg80211: keep tr...
490

667503ddc   Johannes Berg   cfg80211: fix loc...
491
  	ASSERT_WDEV_LOCK(wdev);
19957bb39   Johannes Berg   cfg80211: keep tr...
492
493
494
495
496
497
498
  	if (!rdev->ops->deauth)
  		return;
  
  	memset(&req, 0, sizeof(req));
  	req.reason_code = WLAN_REASON_DEAUTH_LEAVING;
  	req.ie = NULL;
  	req.ie_len = 0;
95de817b9   Johannes Berg   cfg80211: stop tr...
499
500
  	if (!wdev->current_bss)
  		return;
19957bb39   Johannes Berg   cfg80211: keep tr...
501

95de817b9   Johannes Berg   cfg80211: stop tr...
502
503
  	memcpy(bssid, wdev->current_bss->pub.bssid, ETH_ALEN);
  	req.bssid = bssid;
e35e4d28b   Hila Gonen   cfg80211: add wra...
504
  	rdev_deauth(rdev, dev, &req);
95de817b9   Johannes Berg   cfg80211: stop tr...
505
506
507
  
  	if (wdev->current_bss) {
  		cfg80211_unhold_bss(wdev->current_bss);
5b112d3d0   Johannes Berg   cfg80211: pass wi...
508
  		cfg80211_put_bss(&rdev->wiphy, &wdev->current_bss->pub);
95de817b9   Johannes Berg   cfg80211: stop tr...
509
  		wdev->current_bss = NULL;
19957bb39   Johannes Berg   cfg80211: keep tr...
510
511
  	}
  }
9588bbd55   Jouni Malinen   cfg80211: add rem...
512

2e161f78e   Johannes Berg   cfg80211/mac80211...
513
  struct cfg80211_mgmt_registration {
026331c4d   Jouni Malinen   cfg80211/mac80211...
514
  	struct list_head list;
15e473046   Eric W. Biederman   netlink: Rename p...
515
  	u32 nlportid;
026331c4d   Jouni Malinen   cfg80211/mac80211...
516
517
  
  	int match_len;
2e161f78e   Johannes Berg   cfg80211/mac80211...
518
  	__le16 frame_type;
026331c4d   Jouni Malinen   cfg80211/mac80211...
519
520
  	u8 match[];
  };
15e473046   Eric W. Biederman   netlink: Rename p...
521
  int cfg80211_mlme_register_mgmt(struct wireless_dev *wdev, u32 snd_portid,
2e161f78e   Johannes Berg   cfg80211/mac80211...
522
523
  				u16 frame_type, const u8 *match_data,
  				int match_len)
026331c4d   Jouni Malinen   cfg80211/mac80211...
524
  {
271733cf8   Johannes Berg   cfg80211: notify ...
525
526
  	struct wiphy *wiphy = wdev->wiphy;
  	struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
2e161f78e   Johannes Berg   cfg80211/mac80211...
527
  	struct cfg80211_mgmt_registration *reg, *nreg;
026331c4d   Jouni Malinen   cfg80211/mac80211...
528
  	int err = 0;
2e161f78e   Johannes Berg   cfg80211/mac80211...
529
530
531
532
533
534
535
536
537
538
539
540
541
542
  	u16 mgmt_type;
  
  	if (!wdev->wiphy->mgmt_stypes)
  		return -EOPNOTSUPP;
  
  	if ((frame_type & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_MGMT)
  		return -EINVAL;
  
  	if (frame_type & ~(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE))
  		return -EINVAL;
  
  	mgmt_type = (frame_type & IEEE80211_FCTL_STYPE) >> 4;
  	if (!(wdev->wiphy->mgmt_stypes[wdev->iftype].rx & BIT(mgmt_type)))
  		return -EINVAL;
026331c4d   Jouni Malinen   cfg80211/mac80211...
543
544
545
546
  
  	nreg = kzalloc(sizeof(*reg) + match_len, GFP_KERNEL);
  	if (!nreg)
  		return -ENOMEM;
2e161f78e   Johannes Berg   cfg80211/mac80211...
547
  	spin_lock_bh(&wdev->mgmt_registrations_lock);
026331c4d   Jouni Malinen   cfg80211/mac80211...
548

2e161f78e   Johannes Berg   cfg80211/mac80211...
549
  	list_for_each_entry(reg, &wdev->mgmt_registrations, list) {
026331c4d   Jouni Malinen   cfg80211/mac80211...
550
  		int mlen = min(match_len, reg->match_len);
2e161f78e   Johannes Berg   cfg80211/mac80211...
551
552
  		if (frame_type != le16_to_cpu(reg->frame_type))
  			continue;
026331c4d   Jouni Malinen   cfg80211/mac80211...
553
554
555
556
557
558
559
560
561
562
563
564
565
  		if (memcmp(reg->match, match_data, mlen) == 0) {
  			err = -EALREADY;
  			break;
  		}
  	}
  
  	if (err) {
  		kfree(nreg);
  		goto out;
  	}
  
  	memcpy(nreg->match, match_data, match_len);
  	nreg->match_len = match_len;
15e473046   Eric W. Biederman   netlink: Rename p...
566
  	nreg->nlportid = snd_portid;
2e161f78e   Johannes Berg   cfg80211/mac80211...
567
568
  	nreg->frame_type = cpu_to_le16(frame_type);
  	list_add(&nreg->list, &wdev->mgmt_registrations);
026331c4d   Jouni Malinen   cfg80211/mac80211...
569

271733cf8   Johannes Berg   cfg80211: notify ...
570
  	if (rdev->ops->mgmt_frame_register)
e35e4d28b   Hila Gonen   cfg80211: add wra...
571
  		rdev_mgmt_frame_register(rdev, wdev, frame_type, true);
271733cf8   Johannes Berg   cfg80211: notify ...
572

026331c4d   Jouni Malinen   cfg80211/mac80211...
573
   out:
2e161f78e   Johannes Berg   cfg80211/mac80211...
574
  	spin_unlock_bh(&wdev->mgmt_registrations_lock);
271733cf8   Johannes Berg   cfg80211: notify ...
575

026331c4d   Jouni Malinen   cfg80211/mac80211...
576
577
  	return err;
  }
15e473046   Eric W. Biederman   netlink: Rename p...
578
  void cfg80211_mlme_unregister_socket(struct wireless_dev *wdev, u32 nlportid)
026331c4d   Jouni Malinen   cfg80211/mac80211...
579
  {
271733cf8   Johannes Berg   cfg80211: notify ...
580
581
  	struct wiphy *wiphy = wdev->wiphy;
  	struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
2e161f78e   Johannes Berg   cfg80211/mac80211...
582
  	struct cfg80211_mgmt_registration *reg, *tmp;
026331c4d   Jouni Malinen   cfg80211/mac80211...
583

2e161f78e   Johannes Berg   cfg80211/mac80211...
584
  	spin_lock_bh(&wdev->mgmt_registrations_lock);
026331c4d   Jouni Malinen   cfg80211/mac80211...
585

2e161f78e   Johannes Berg   cfg80211/mac80211...
586
  	list_for_each_entry_safe(reg, tmp, &wdev->mgmt_registrations, list) {
15e473046   Eric W. Biederman   netlink: Rename p...
587
  		if (reg->nlportid != nlportid)
271733cf8   Johannes Berg   cfg80211: notify ...
588
589
590
591
  			continue;
  
  		if (rdev->ops->mgmt_frame_register) {
  			u16 frame_type = le16_to_cpu(reg->frame_type);
e35e4d28b   Hila Gonen   cfg80211: add wra...
592
593
  			rdev_mgmt_frame_register(rdev, wdev,
  						 frame_type, false);
026331c4d   Jouni Malinen   cfg80211/mac80211...
594
  		}
271733cf8   Johannes Berg   cfg80211: notify ...
595
596
597
  
  		list_del(&reg->list);
  		kfree(reg);
026331c4d   Jouni Malinen   cfg80211/mac80211...
598
  	}
2e161f78e   Johannes Berg   cfg80211/mac80211...
599
  	spin_unlock_bh(&wdev->mgmt_registrations_lock);
28946da76   Johannes Berg   nl80211: allow su...
600

5de179848   Arend van Spriel   cfg80211: introdu...
601
602
603
604
  	if (nlportid && rdev->crit_proto_nlportid == nlportid) {
  		rdev->crit_proto_nlportid = 0;
  		rdev_crit_proto_stop(rdev, wdev);
  	}
15e473046   Eric W. Biederman   netlink: Rename p...
605
606
  	if (nlportid == wdev->ap_unexpected_nlportid)
  		wdev->ap_unexpected_nlportid = 0;
026331c4d   Jouni Malinen   cfg80211/mac80211...
607
  }
2e161f78e   Johannes Berg   cfg80211/mac80211...
608
  void cfg80211_mlme_purge_registrations(struct wireless_dev *wdev)
026331c4d   Jouni Malinen   cfg80211/mac80211...
609
  {
2e161f78e   Johannes Berg   cfg80211/mac80211...
610
  	struct cfg80211_mgmt_registration *reg, *tmp;
026331c4d   Jouni Malinen   cfg80211/mac80211...
611

2e161f78e   Johannes Berg   cfg80211/mac80211...
612
  	spin_lock_bh(&wdev->mgmt_registrations_lock);
026331c4d   Jouni Malinen   cfg80211/mac80211...
613

2e161f78e   Johannes Berg   cfg80211/mac80211...
614
  	list_for_each_entry_safe(reg, tmp, &wdev->mgmt_registrations, list) {
026331c4d   Jouni Malinen   cfg80211/mac80211...
615
616
617
  		list_del(&reg->list);
  		kfree(reg);
  	}
2e161f78e   Johannes Berg   cfg80211/mac80211...
618
  	spin_unlock_bh(&wdev->mgmt_registrations_lock);
026331c4d   Jouni Malinen   cfg80211/mac80211...
619
  }
2e161f78e   Johannes Berg   cfg80211/mac80211...
620
  int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev,
71bbc9943   Johannes Berg   cfg80211: use wde...
621
  			  struct wireless_dev *wdev,
f7ca38dfe   Johannes Berg   nl80211/cfg80211:...
622
  			  struct ieee80211_channel *chan, bool offchan,
42d97a599   Johannes Berg   cfg80211: remove ...
623
624
  			  unsigned int wait, const u8 *buf, size_t len,
  			  bool no_cck, bool dont_wait_for_ack, u64 *cookie)
026331c4d   Jouni Malinen   cfg80211/mac80211...
625
  {
026331c4d   Jouni Malinen   cfg80211/mac80211...
626
  	const struct ieee80211_mgmt *mgmt;
2e161f78e   Johannes Berg   cfg80211/mac80211...
627
628
629
630
  	u16 stype;
  
  	if (!wdev->wiphy->mgmt_stypes)
  		return -EOPNOTSUPP;
026331c4d   Jouni Malinen   cfg80211/mac80211...
631

2e161f78e   Johannes Berg   cfg80211/mac80211...
632
  	if (!rdev->ops->mgmt_tx)
026331c4d   Jouni Malinen   cfg80211/mac80211...
633
  		return -EOPNOTSUPP;
2e161f78e   Johannes Berg   cfg80211/mac80211...
634

026331c4d   Jouni Malinen   cfg80211/mac80211...
635
636
637
638
  	if (len < 24 + 1)
  		return -EINVAL;
  
  	mgmt = (const struct ieee80211_mgmt *) buf;
2e161f78e   Johannes Berg   cfg80211/mac80211...
639
640
  
  	if (!ieee80211_is_mgmt(mgmt->frame_control))
026331c4d   Jouni Malinen   cfg80211/mac80211...
641
  		return -EINVAL;
2e161f78e   Johannes Berg   cfg80211/mac80211...
642
643
644
645
646
647
648
  
  	stype = le16_to_cpu(mgmt->frame_control) & IEEE80211_FCTL_STYPE;
  	if (!(wdev->wiphy->mgmt_stypes[wdev->iftype].tx & BIT(stype >> 4)))
  		return -EINVAL;
  
  	if (ieee80211_is_action(mgmt->frame_control) &&
  	    mgmt->u.action.category != WLAN_CATEGORY_PUBLIC) {
663fcafd9   Johannes Berg   cfg80211/mac80211...
649
  		int err = 0;
fe100acdd   Johannes Berg   cfg80211: fix loc...
650
  		wdev_lock(wdev);
663fcafd9   Johannes Berg   cfg80211/mac80211...
651
652
653
654
655
656
657
658
  		switch (wdev->iftype) {
  		case NL80211_IFTYPE_ADHOC:
  		case NL80211_IFTYPE_STATION:
  		case NL80211_IFTYPE_P2P_CLIENT:
  			if (!wdev->current_bss) {
  				err = -ENOTCONN;
  				break;
  			}
ac422d3cc   Joe Perches   wireless: Convert...
659
660
  			if (!ether_addr_equal(wdev->current_bss->pub.bssid,
  					      mgmt->bssid)) {
663fcafd9   Johannes Berg   cfg80211/mac80211...
661
662
663
664
665
666
667
668
669
670
  				err = -ENOTCONN;
  				break;
  			}
  
  			/*
  			 * check for IBSS DA must be done by driver as
  			 * cfg80211 doesn't track the stations
  			 */
  			if (wdev->iftype == NL80211_IFTYPE_ADHOC)
  				break;
fe100acdd   Johannes Berg   cfg80211: fix loc...
671

663fcafd9   Johannes Berg   cfg80211/mac80211...
672
  			/* for station, check that DA is the AP */
ac422d3cc   Joe Perches   wireless: Convert...
673
674
  			if (!ether_addr_equal(wdev->current_bss->pub.bssid,
  					      mgmt->da)) {
663fcafd9   Johannes Berg   cfg80211/mac80211...
675
676
677
678
679
680
681
  				err = -ENOTCONN;
  				break;
  			}
  			break;
  		case NL80211_IFTYPE_AP:
  		case NL80211_IFTYPE_P2P_GO:
  		case NL80211_IFTYPE_AP_VLAN:
98104fded   Johannes Berg   cfg80211: add P2P...
682
  			if (!ether_addr_equal(mgmt->bssid, wdev_address(wdev)))
663fcafd9   Johannes Berg   cfg80211/mac80211...
683
684
  				err = -EINVAL;
  			break;
0778a6a3e   Javier Cardona   mac80211: Let use...
685
  		case NL80211_IFTYPE_MESH_POINT:
ac422d3cc   Joe Perches   wireless: Convert...
686
  			if (!ether_addr_equal(mgmt->sa, mgmt->bssid)) {
0778a6a3e   Javier Cardona   mac80211: Let use...
687
688
689
690
691
692
693
694
  				err = -EINVAL;
  				break;
  			}
  			/*
  			 * check for mesh DA must be done by driver as
  			 * cfg80211 doesn't track the stations
  			 */
  			break;
98104fded   Johannes Berg   cfg80211: add P2P...
695
696
697
698
699
  		case NL80211_IFTYPE_P2P_DEVICE:
  			/*
  			 * fall through, P2P device only supports
  			 * public action frames
  			 */
663fcafd9   Johannes Berg   cfg80211/mac80211...
700
701
702
703
  		default:
  			err = -EOPNOTSUPP;
  			break;
  		}
fe100acdd   Johannes Berg   cfg80211: fix loc...
704
  		wdev_unlock(wdev);
663fcafd9   Johannes Berg   cfg80211/mac80211...
705
706
707
  
  		if (err)
  			return err;
026331c4d   Jouni Malinen   cfg80211/mac80211...
708
  	}
98104fded   Johannes Berg   cfg80211: add P2P...
709
  	if (!ether_addr_equal(mgmt->sa, wdev_address(wdev)))
026331c4d   Jouni Malinen   cfg80211/mac80211...
710
711
712
  		return -EINVAL;
  
  	/* Transmit the Action frame as requested by user space */
e35e4d28b   Hila Gonen   cfg80211: add wra...
713
  	return rdev_mgmt_tx(rdev, wdev, chan, offchan,
e35e4d28b   Hila Gonen   cfg80211: add wra...
714
715
  			    wait, buf, len, no_cck, dont_wait_for_ack,
  			    cookie);
026331c4d   Jouni Malinen   cfg80211/mac80211...
716
  }
71bbc9943   Johannes Berg   cfg80211: use wde...
717
  bool cfg80211_rx_mgmt(struct wireless_dev *wdev, int freq, int sig_mbm,
804483e90   Johannes Berg   cfg80211/mac80211...
718
  		      const u8 *buf, size_t len, gfp_t gfp)
026331c4d   Jouni Malinen   cfg80211/mac80211...
719
  {
026331c4d   Jouni Malinen   cfg80211/mac80211...
720
721
  	struct wiphy *wiphy = wdev->wiphy;
  	struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
2e161f78e   Johannes Berg   cfg80211/mac80211...
722
723
724
725
726
727
  	struct cfg80211_mgmt_registration *reg;
  	const struct ieee80211_txrx_stypes *stypes =
  		&wiphy->mgmt_stypes[wdev->iftype];
  	struct ieee80211_mgmt *mgmt = (void *)buf;
  	const u8 *data;
  	int data_len;
026331c4d   Jouni Malinen   cfg80211/mac80211...
728
  	bool result = false;
2e161f78e   Johannes Berg   cfg80211/mac80211...
729
730
731
  	__le16 ftype = mgmt->frame_control &
  		cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE);
  	u16 stype;
026331c4d   Jouni Malinen   cfg80211/mac80211...
732

4ee3e063f   Beni Lev   cfg80211: add cfg...
733
  	trace_cfg80211_rx_mgmt(wdev, freq, sig_mbm);
2e161f78e   Johannes Berg   cfg80211/mac80211...
734
  	stype = (le16_to_cpu(mgmt->frame_control) & IEEE80211_FCTL_STYPE) >> 4;
026331c4d   Jouni Malinen   cfg80211/mac80211...
735

4ee3e063f   Beni Lev   cfg80211: add cfg...
736
737
  	if (!(stypes->rx & BIT(stype))) {
  		trace_cfg80211_return_bool(false);
2e161f78e   Johannes Berg   cfg80211/mac80211...
738
  		return false;
4ee3e063f   Beni Lev   cfg80211: add cfg...
739
  	}
026331c4d   Jouni Malinen   cfg80211/mac80211...
740

2e161f78e   Johannes Berg   cfg80211/mac80211...
741
742
743
744
745
746
747
748
  	data = buf + ieee80211_hdrlen(mgmt->frame_control);
  	data_len = len - ieee80211_hdrlen(mgmt->frame_control);
  
  	spin_lock_bh(&wdev->mgmt_registrations_lock);
  
  	list_for_each_entry(reg, &wdev->mgmt_registrations, list) {
  		if (reg->frame_type != ftype)
  			continue;
026331c4d   Jouni Malinen   cfg80211/mac80211...
749

2e161f78e   Johannes Berg   cfg80211/mac80211...
750
  		if (reg->match_len > data_len)
026331c4d   Jouni Malinen   cfg80211/mac80211...
751
  			continue;
2e161f78e   Johannes Berg   cfg80211/mac80211...
752
  		if (memcmp(reg->match, data, reg->match_len))
026331c4d   Jouni Malinen   cfg80211/mac80211...
753
754
755
756
757
  			continue;
  
  		/* found match! */
  
  		/* Indicate the received Action frame to user space */
15e473046   Eric W. Biederman   netlink: Rename p...
758
  		if (nl80211_send_mgmt(rdev, wdev, reg->nlportid,
804483e90   Johannes Berg   cfg80211/mac80211...
759
  				      freq, sig_mbm,
2e161f78e   Johannes Berg   cfg80211/mac80211...
760
  				      buf, len, gfp))
026331c4d   Jouni Malinen   cfg80211/mac80211...
761
762
763
764
765
  			continue;
  
  		result = true;
  		break;
  	}
2e161f78e   Johannes Berg   cfg80211/mac80211...
766
  	spin_unlock_bh(&wdev->mgmt_registrations_lock);
026331c4d   Jouni Malinen   cfg80211/mac80211...
767

4ee3e063f   Beni Lev   cfg80211: add cfg...
768
  	trace_cfg80211_return_bool(result);
026331c4d   Jouni Malinen   cfg80211/mac80211...
769
770
  	return result;
  }
2e161f78e   Johannes Berg   cfg80211/mac80211...
771
  EXPORT_SYMBOL(cfg80211_rx_mgmt);
026331c4d   Jouni Malinen   cfg80211/mac80211...
772

04f39047a   Simon Wunderlich   nl80211/cfg80211:...
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
  void cfg80211_dfs_channels_update_work(struct work_struct *work)
  {
  	struct delayed_work *delayed_work;
  	struct cfg80211_registered_device *rdev;
  	struct cfg80211_chan_def chandef;
  	struct ieee80211_supported_band *sband;
  	struct ieee80211_channel *c;
  	struct wiphy *wiphy;
  	bool check_again = false;
  	unsigned long timeout, next_time = 0;
  	int bandid, i;
  
  	delayed_work = container_of(work, struct delayed_work, work);
  	rdev = container_of(delayed_work, struct cfg80211_registered_device,
  			    dfs_update_channels_wk);
  	wiphy = &rdev->wiphy;
  
  	mutex_lock(&cfg80211_mutex);
  	for (bandid = 0; bandid < IEEE80211_NUM_BANDS; bandid++) {
  		sband = wiphy->bands[bandid];
  		if (!sband)
  			continue;
  
  		for (i = 0; i < sband->n_channels; i++) {
  			c = &sband->channels[i];
  
  			if (c->dfs_state != NL80211_DFS_UNAVAILABLE)
  				continue;
  
  			timeout = c->dfs_state_entered +
  				  IEEE80211_DFS_MIN_NOP_TIME_MS;
  
  			if (time_after_eq(jiffies, timeout)) {
  				c->dfs_state = NL80211_DFS_USABLE;
  				cfg80211_chandef_create(&chandef, c,
  							NL80211_CHAN_NO_HT);
  
  				nl80211_radar_notify(rdev, &chandef,
  						     NL80211_RADAR_NOP_FINISHED,
  						     NULL, GFP_ATOMIC);
  				continue;
  			}
  
  			if (!check_again)
  				next_time = timeout - jiffies;
  			else
  				next_time = min(next_time, timeout - jiffies);
  			check_again = true;
  		}
  	}
  	mutex_unlock(&cfg80211_mutex);
  
  	/* reschedule if there are other channels waiting to be cleared again */
  	if (check_again)
  		queue_delayed_work(cfg80211_wq, &rdev->dfs_update_channels_wk,
  				   next_time);
  }
  
  
  void cfg80211_radar_event(struct wiphy *wiphy,
  			  struct cfg80211_chan_def *chandef,
  			  gfp_t gfp)
  {
  	struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
  	unsigned long timeout;
  
  	trace_cfg80211_radar_event(wiphy, chandef);
  
  	/* only set the chandef supplied channel to unavailable, in
  	 * case the radar is detected on only one of multiple channels
  	 * spanned by the chandef.
  	 */
  	cfg80211_set_dfs_state(wiphy, chandef, NL80211_DFS_UNAVAILABLE);
  
  	timeout = msecs_to_jiffies(IEEE80211_DFS_MIN_NOP_TIME_MS);
  	queue_delayed_work(cfg80211_wq, &rdev->dfs_update_channels_wk,
  			   timeout);
  
  	nl80211_radar_notify(rdev, chandef, NL80211_RADAR_DETECTED, NULL, gfp);
  }
  EXPORT_SYMBOL(cfg80211_radar_event);
  
  void cfg80211_cac_event(struct net_device *netdev,
  			enum nl80211_radar_event event, gfp_t gfp)
  {
  	struct wireless_dev *wdev = netdev->ieee80211_ptr;
  	struct wiphy *wiphy = wdev->wiphy;
  	struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
  	struct cfg80211_chan_def chandef;
  	unsigned long timeout;
  
  	trace_cfg80211_cac_event(netdev, event);
  
  	if (WARN_ON(!wdev->cac_started))
  		return;
  
  	if (WARN_ON(!wdev->channel))
  		return;
  
  	cfg80211_chandef_create(&chandef, wdev->channel, NL80211_CHAN_NO_HT);
  
  	switch (event) {
  	case NL80211_RADAR_CAC_FINISHED:
  		timeout = wdev->cac_start_time +
  			  msecs_to_jiffies(IEEE80211_DFS_MIN_CAC_TIME_MS);
  		WARN_ON(!time_after_eq(jiffies, timeout));
  		cfg80211_set_dfs_state(wiphy, &chandef, NL80211_DFS_AVAILABLE);
  		break;
  	case NL80211_RADAR_CAC_ABORTED:
  		break;
  	default:
  		WARN_ON(1);
  		return;
  	}
  	wdev->cac_started = false;
  
  	nl80211_radar_notify(rdev, &chandef, event, netdev, gfp);
  }
  EXPORT_SYMBOL(cfg80211_cac_event);