diff options
author | 2025-03-08 22:04:20 +0800 | |
---|---|---|
committer | 2025-03-08 22:04:20 +0800 | |
commit | a07bb8fd1299070229f0e8f3dcb57ffd5ef9870a (patch) | |
tree | 84f21bd0bf7071bc5fc7dd989e77d7ceb5476682 /net/wireless/mlme.c | |
download | ohosKernel-a07bb8fd1299070229f0e8f3dcb57ffd5ef9870a.tar.gz ohosKernel-a07bb8fd1299070229f0e8f3dcb57ffd5ef9870a.zip |
Initial commit: OpenHarmony-v4.0-ReleaseOpenHarmony-v4.0-Release
Diffstat (limited to 'net/wireless/mlme.c')
-rw-r--r-- | net/wireless/mlme.c | 968 |
1 files changed, 968 insertions, 0 deletions
diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c new file mode 100644 index 000000000..6dcfc5a34 --- /dev/null +++ b/net/wireless/mlme.c | |||
@@ -0,0 +1,968 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | /* | ||
3 | * cfg80211 MLME SAP interface | ||
4 | * | ||
5 | * Copyright (c) 2009, Jouni Malinen <j@w1.fi> | ||
6 | * Copyright (c) 2015 Intel Deutschland GmbH | ||
7 | * Copyright (C) 2019 Intel Corporation | ||
8 | */ | ||
9 | |||
10 | #include <linux/kernel.h> | ||
11 | #include <linux/module.h> | ||
12 | #include <linux/etherdevice.h> | ||
13 | #include <linux/netdevice.h> | ||
14 | #include <linux/nl80211.h> | ||
15 | #include <linux/slab.h> | ||
16 | #include <linux/wireless.h> | ||
17 | #include <net/cfg80211.h> | ||
18 | #include <net/iw_handler.h> | ||
19 | #include "core.h" | ||
20 | #include "nl80211.h" | ||
21 | #include "rdev-ops.h" | ||
22 | |||
23 | |||
24 | void cfg80211_rx_assoc_resp(struct net_device *dev, struct cfg80211_bss *bss, | ||
25 | const u8 *buf, size_t len, int uapsd_queues, | ||
26 | const u8 *req_ies, size_t req_ies_len) | ||
27 | { | ||
28 | struct wireless_dev *wdev = dev->ieee80211_ptr; | ||
29 | struct wiphy *wiphy = wdev->wiphy; | ||
30 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); | ||
31 | struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf; | ||
32 | struct cfg80211_connect_resp_params cr; | ||
33 | const u8 *resp_ie = mgmt->u.assoc_resp.variable; | ||
34 | size_t resp_ie_len = len - offsetof(struct ieee80211_mgmt, | ||
35 | u.assoc_resp.variable); | ||
36 | |||
37 | if (bss->channel->band == NL80211_BAND_S1GHZ) { | ||
38 | resp_ie = (u8 *)&mgmt->u.s1g_assoc_resp.variable; | ||
39 | resp_ie_len = len - offsetof(struct ieee80211_mgmt, | ||
40 | u.s1g_assoc_resp.variable); | ||
41 | } | ||
42 | |||
43 | memset(&cr, 0, sizeof(cr)); | ||
44 | cr.status = (int)le16_to_cpu(mgmt->u.assoc_resp.status_code); | ||
45 | cr.bssid = mgmt->bssid; | ||
46 | cr.bss = bss; | ||
47 | cr.req_ie = req_ies; | ||
48 | cr.req_ie_len = req_ies_len; | ||
49 | cr.resp_ie = resp_ie; | ||
50 | cr.resp_ie_len = resp_ie_len; | ||
51 | cr.timeout_reason = NL80211_TIMEOUT_UNSPECIFIED; | ||
52 | |||
53 | trace_cfg80211_send_rx_assoc(dev, bss); | ||
54 | |||
55 | /* | ||
56 | * This is a bit of a hack, we don't notify userspace of | ||
57 | * a (re-)association reply if we tried to send a reassoc | ||
58 | * and got a reject -- we only try again with an assoc | ||
59 | * frame instead of reassoc. | ||
60 | */ | ||
61 | if (cfg80211_sme_rx_assoc_resp(wdev, cr.status)) { | ||
62 | cfg80211_unhold_bss(bss_from_pub(bss)); | ||
63 | cfg80211_put_bss(wiphy, bss); | ||
64 | return; | ||
65 | } | ||
66 | |||
67 | nl80211_send_rx_assoc(rdev, dev, buf, len, GFP_KERNEL, uapsd_queues, | ||
68 | req_ies, req_ies_len); | ||
69 | /* update current_bss etc., consumes the bss reference */ | ||
70 | __cfg80211_connect_result(dev, &cr, cr.status == WLAN_STATUS_SUCCESS); | ||
71 | } | ||
72 | EXPORT_SYMBOL(cfg80211_rx_assoc_resp); | ||
73 | |||
74 | static void cfg80211_process_auth(struct wireless_dev *wdev, | ||
75 | const u8 *buf, size_t len) | ||
76 | { | ||
77 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); | ||
78 | |||
79 | nl80211_send_rx_auth(rdev, wdev->netdev, buf, len, GFP_KERNEL); | ||
80 | cfg80211_sme_rx_auth(wdev, buf, len); | ||
81 | } | ||
82 | |||
83 | static void cfg80211_process_deauth(struct wireless_dev *wdev, | ||
84 | const u8 *buf, size_t len) | ||
85 | { | ||
86 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); | ||
87 | struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf; | ||
88 | const u8 *bssid = mgmt->bssid; | ||
89 | u16 reason_code = le16_to_cpu(mgmt->u.deauth.reason_code); | ||
90 | bool from_ap = !ether_addr_equal(mgmt->sa, wdev->netdev->dev_addr); | ||
91 | |||
92 | nl80211_send_deauth(rdev, wdev->netdev, buf, len, GFP_KERNEL); | ||
93 | |||
94 | if (!wdev->current_bss || | ||
95 | !ether_addr_equal(wdev->current_bss->pub.bssid, bssid)) | ||
96 | return; | ||
97 | |||
98 | __cfg80211_disconnected(wdev->netdev, NULL, 0, reason_code, from_ap); | ||
99 | cfg80211_sme_deauth(wdev); | ||
100 | } | ||
101 | |||
102 | static void cfg80211_process_disassoc(struct wireless_dev *wdev, | ||
103 | const u8 *buf, size_t len) | ||
104 | { | ||
105 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); | ||
106 | struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf; | ||
107 | const u8 *bssid = mgmt->bssid; | ||
108 | u16 reason_code = le16_to_cpu(mgmt->u.disassoc.reason_code); | ||
109 | bool from_ap = !ether_addr_equal(mgmt->sa, wdev->netdev->dev_addr); | ||
110 | |||
111 | nl80211_send_disassoc(rdev, wdev->netdev, buf, len, GFP_KERNEL); | ||
112 | |||
113 | if (WARN_ON(!wdev->current_bss || | ||
114 | !ether_addr_equal(wdev->current_bss->pub.bssid, bssid))) | ||
115 | return; | ||
116 | |||
117 | __cfg80211_disconnected(wdev->netdev, NULL, 0, reason_code, from_ap); | ||
118 | cfg80211_sme_disassoc(wdev); | ||
119 | } | ||
120 | |||
121 | void cfg80211_rx_mlme_mgmt(struct net_device *dev, const u8 *buf, size_t len) | ||
122 | { | ||
123 | struct wireless_dev *wdev = dev->ieee80211_ptr; | ||
124 | struct ieee80211_mgmt *mgmt = (void *)buf; | ||
125 | |||
126 | ASSERT_WDEV_LOCK(wdev); | ||
127 | |||
128 | trace_cfg80211_rx_mlme_mgmt(dev, buf, len); | ||
129 | |||
130 | if (WARN_ON(len < 2)) | ||
131 | return; | ||
132 | |||
133 | if (ieee80211_is_auth(mgmt->frame_control)) | ||
134 | cfg80211_process_auth(wdev, buf, len); | ||
135 | else if (ieee80211_is_deauth(mgmt->frame_control)) | ||
136 | cfg80211_process_deauth(wdev, buf, len); | ||
137 | else if (ieee80211_is_disassoc(mgmt->frame_control)) | ||
138 | cfg80211_process_disassoc(wdev, buf, len); | ||
139 | } | ||
140 | EXPORT_SYMBOL(cfg80211_rx_mlme_mgmt); | ||
141 | |||
142 | void cfg80211_auth_timeout(struct net_device *dev, const u8 *addr) | ||
143 | { | ||
144 | struct wireless_dev *wdev = dev->ieee80211_ptr; | ||
145 | struct wiphy *wiphy = wdev->wiphy; | ||
146 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); | ||
147 | |||
148 | trace_cfg80211_send_auth_timeout(dev, addr); | ||
149 | |||
150 | nl80211_send_auth_timeout(rdev, dev, addr, GFP_KERNEL); | ||
151 | cfg80211_sme_auth_timeout(wdev); | ||
152 | } | ||
153 | EXPORT_SYMBOL(cfg80211_auth_timeout); | ||
154 | |||
155 | void cfg80211_assoc_timeout(struct net_device *dev, struct cfg80211_bss *bss) | ||
156 | { | ||
157 | struct wireless_dev *wdev = dev->ieee80211_ptr; | ||
158 | struct wiphy *wiphy = wdev->wiphy; | ||
159 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); | ||
160 | |||
161 | trace_cfg80211_send_assoc_timeout(dev, bss->bssid); | ||
162 | |||
163 | nl80211_send_assoc_timeout(rdev, dev, bss->bssid, GFP_KERNEL); | ||
164 | cfg80211_sme_assoc_timeout(wdev); | ||
165 | |||
166 | cfg80211_unhold_bss(bss_from_pub(bss)); | ||
167 | cfg80211_put_bss(wiphy, bss); | ||
168 | } | ||
169 | EXPORT_SYMBOL(cfg80211_assoc_timeout); | ||
170 | |||
171 | void cfg80211_abandon_assoc(struct net_device *dev, struct cfg80211_bss *bss) | ||
172 | { | ||
173 | struct wireless_dev *wdev = dev->ieee80211_ptr; | ||
174 | struct wiphy *wiphy = wdev->wiphy; | ||
175 | |||
176 | cfg80211_sme_abandon_assoc(wdev); | ||
177 | |||
178 | cfg80211_unhold_bss(bss_from_pub(bss)); | ||
179 | cfg80211_put_bss(wiphy, bss); | ||
180 | } | ||
181 | EXPORT_SYMBOL(cfg80211_abandon_assoc); | ||
182 | |||
183 | void cfg80211_tx_mlme_mgmt(struct net_device *dev, const u8 *buf, size_t len) | ||
184 | { | ||
185 | struct wireless_dev *wdev = dev->ieee80211_ptr; | ||
186 | struct ieee80211_mgmt *mgmt = (void *)buf; | ||
187 | |||
188 | ASSERT_WDEV_LOCK(wdev); | ||
189 | |||
190 | trace_cfg80211_tx_mlme_mgmt(dev, buf, len); | ||
191 | |||
192 | if (WARN_ON(len < 2)) | ||
193 | return; | ||
194 | |||
195 | if (ieee80211_is_deauth(mgmt->frame_control)) | ||
196 | cfg80211_process_deauth(wdev, buf, len); | ||
197 | else | ||
198 | cfg80211_process_disassoc(wdev, buf, len); | ||
199 | } | ||
200 | EXPORT_SYMBOL(cfg80211_tx_mlme_mgmt); | ||
201 | |||
202 | void cfg80211_michael_mic_failure(struct net_device *dev, const u8 *addr, | ||
203 | enum nl80211_key_type key_type, int key_id, | ||
204 | const u8 *tsc, gfp_t gfp) | ||
205 | { | ||
206 | struct wiphy *wiphy = dev->ieee80211_ptr->wiphy; | ||
207 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); | ||
208 | #ifdef CONFIG_CFG80211_WEXT | ||
209 | union iwreq_data wrqu; | ||
210 | char *buf = kmalloc(128, gfp); | ||
211 | |||
212 | if (buf) { | ||
213 | sprintf(buf, "MLME-MICHAELMICFAILURE.indication(" | ||
214 | "keyid=%d %scast addr=%pM)", key_id, | ||
215 | key_type == NL80211_KEYTYPE_GROUP ? "broad" : "uni", | ||
216 | addr); | ||
217 | memset(&wrqu, 0, sizeof(wrqu)); | ||
218 | wrqu.data.length = strlen(buf); | ||
219 | wireless_send_event(dev, IWEVCUSTOM, &wrqu, buf); | ||
220 | kfree(buf); | ||
221 | } | ||
222 | #endif | ||
223 | |||
224 | trace_cfg80211_michael_mic_failure(dev, addr, key_type, key_id, tsc); | ||
225 | nl80211_michael_mic_failure(rdev, dev, addr, key_type, key_id, tsc, gfp); | ||
226 | } | ||
227 | EXPORT_SYMBOL(cfg80211_michael_mic_failure); | ||
228 | |||
229 | /* some MLME handling for userspace SME */ | ||
230 | int cfg80211_mlme_auth(struct cfg80211_registered_device *rdev, | ||
231 | struct net_device *dev, | ||
232 | struct ieee80211_channel *chan, | ||
233 | enum nl80211_auth_type auth_type, | ||
234 | const u8 *bssid, | ||
235 | const u8 *ssid, int ssid_len, | ||
236 | const u8 *ie, int ie_len, | ||
237 | const u8 *key, int key_len, int key_idx, | ||
238 | const u8 *auth_data, int auth_data_len) | ||
239 | { | ||
240 | struct wireless_dev *wdev = dev->ieee80211_ptr; | ||
241 | struct cfg80211_auth_request req = { | ||
242 | .ie = ie, | ||
243 | .ie_len = ie_len, | ||
244 | .auth_data = auth_data, | ||
245 | .auth_data_len = auth_data_len, | ||
246 | .auth_type = auth_type, | ||
247 | .key = key, | ||
248 | .key_len = key_len, | ||
249 | .key_idx = key_idx, | ||
250 | }; | ||
251 | int err; | ||
252 | |||
253 | ASSERT_WDEV_LOCK(wdev); | ||
254 | |||
255 | if (auth_type == NL80211_AUTHTYPE_SHARED_KEY) | ||
256 | if (!key || !key_len || key_idx < 0 || key_idx > 3) | ||
257 | return -EINVAL; | ||
258 | |||
259 | if (wdev->current_bss && | ||
260 | ether_addr_equal(bssid, wdev->current_bss->pub.bssid)) | ||
261 | return -EALREADY; | ||
262 | |||
263 | req.bss = cfg80211_get_bss(&rdev->wiphy, chan, bssid, ssid, ssid_len, | ||
264 | IEEE80211_BSS_TYPE_ESS, | ||
265 | IEEE80211_PRIVACY_ANY); | ||
266 | if (!req.bss) | ||
267 | return -ENOENT; | ||
268 | |||
269 | err = rdev_auth(rdev, dev, &req); | ||
270 | |||
271 | cfg80211_put_bss(&rdev->wiphy, req.bss); | ||
272 | return err; | ||
273 | } | ||
274 | |||
275 | /* Do a logical ht_capa &= ht_capa_mask. */ | ||
276 | void cfg80211_oper_and_ht_capa(struct ieee80211_ht_cap *ht_capa, | ||
277 | const struct ieee80211_ht_cap *ht_capa_mask) | ||
278 | { | ||
279 | int i; | ||
280 | u8 *p1, *p2; | ||
281 | if (!ht_capa_mask) { | ||
282 | memset(ht_capa, 0, sizeof(*ht_capa)); | ||
283 | return; | ||
284 | } | ||
285 | |||
286 | p1 = (u8*)(ht_capa); | ||
287 | p2 = (u8*)(ht_capa_mask); | ||
288 | for (i = 0; i < sizeof(*ht_capa); i++) | ||
289 | p1[i] &= p2[i]; | ||
290 | } | ||
291 | |||
292 | /* Do a logical vht_capa &= vht_capa_mask. */ | ||
293 | void cfg80211_oper_and_vht_capa(struct ieee80211_vht_cap *vht_capa, | ||
294 | const struct ieee80211_vht_cap *vht_capa_mask) | ||
295 | { | ||
296 | int i; | ||
297 | u8 *p1, *p2; | ||
298 | if (!vht_capa_mask) { | ||
299 | memset(vht_capa, 0, sizeof(*vht_capa)); | ||
300 | return; | ||
301 | } | ||
302 | |||
303 | p1 = (u8*)(vht_capa); | ||
304 | p2 = (u8*)(vht_capa_mask); | ||
305 | for (i = 0; i < sizeof(*vht_capa); i++) | ||
306 | p1[i] &= p2[i]; | ||
307 | } | ||
308 | |||
309 | int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev, | ||
310 | struct net_device *dev, | ||
311 | struct ieee80211_channel *chan, | ||
312 | const u8 *bssid, | ||
313 | const u8 *ssid, int ssid_len, | ||
314 | struct cfg80211_assoc_request *req) | ||
315 | { | ||
316 | struct wireless_dev *wdev = dev->ieee80211_ptr; | ||
317 | int err; | ||
318 | |||
319 | ASSERT_WDEV_LOCK(wdev); | ||
320 | |||
321 | if (wdev->current_bss && | ||
322 | (!req->prev_bssid || !ether_addr_equal(wdev->current_bss->pub.bssid, | ||
323 | req->prev_bssid))) | ||
324 | return -EALREADY; | ||
325 | |||
326 | cfg80211_oper_and_ht_capa(&req->ht_capa_mask, | ||
327 | rdev->wiphy.ht_capa_mod_mask); | ||
328 | cfg80211_oper_and_vht_capa(&req->vht_capa_mask, | ||
329 | rdev->wiphy.vht_capa_mod_mask); | ||
330 | |||
331 | req->bss = cfg80211_get_bss(&rdev->wiphy, chan, bssid, ssid, ssid_len, | ||
332 | IEEE80211_BSS_TYPE_ESS, | ||
333 | IEEE80211_PRIVACY_ANY); | ||
334 | if (!req->bss) | ||
335 | return -ENOENT; | ||
336 | |||
337 | err = rdev_assoc(rdev, dev, req); | ||
338 | if (!err) | ||
339 | cfg80211_hold_bss(bss_from_pub(req->bss)); | ||
340 | else | ||
341 | cfg80211_put_bss(&rdev->wiphy, req->bss); | ||
342 | |||
343 | return err; | ||
344 | } | ||
345 | |||
346 | int cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev, | ||
347 | struct net_device *dev, const u8 *bssid, | ||
348 | const u8 *ie, int ie_len, u16 reason, | ||
349 | bool local_state_change) | ||
350 | { | ||
351 | struct wireless_dev *wdev = dev->ieee80211_ptr; | ||
352 | struct cfg80211_deauth_request req = { | ||
353 | .bssid = bssid, | ||
354 | .reason_code = reason, | ||
355 | .ie = ie, | ||
356 | .ie_len = ie_len, | ||
357 | .local_state_change = local_state_change, | ||
358 | }; | ||
359 | |||
360 | ASSERT_WDEV_LOCK(wdev); | ||
361 | |||
362 | if (local_state_change && | ||
363 | (!wdev->current_bss || | ||
364 | !ether_addr_equal(wdev->current_bss->pub.bssid, bssid))) | ||
365 | return 0; | ||
366 | |||
367 | if (ether_addr_equal(wdev->disconnect_bssid, bssid) || | ||
368 | (wdev->current_bss && | ||
369 | ether_addr_equal(wdev->current_bss->pub.bssid, bssid))) | ||
370 | wdev->conn_owner_nlportid = 0; | ||
371 | |||
372 | return rdev_deauth(rdev, dev, &req); | ||
373 | } | ||
374 | |||
375 | int cfg80211_mlme_disassoc(struct cfg80211_registered_device *rdev, | ||
376 | struct net_device *dev, const u8 *bssid, | ||
377 | const u8 *ie, int ie_len, u16 reason, | ||
378 | bool local_state_change) | ||
379 | { | ||
380 | struct wireless_dev *wdev = dev->ieee80211_ptr; | ||
381 | struct cfg80211_disassoc_request req = { | ||
382 | .reason_code = reason, | ||
383 | .local_state_change = local_state_change, | ||
384 | .ie = ie, | ||
385 | .ie_len = ie_len, | ||
386 | }; | ||
387 | int err; | ||
388 | |||
389 | ASSERT_WDEV_LOCK(wdev); | ||
390 | |||
391 | if (!wdev->current_bss) | ||
392 | return -ENOTCONN; | ||
393 | |||
394 | if (ether_addr_equal(wdev->current_bss->pub.bssid, bssid)) | ||
395 | req.bss = &wdev->current_bss->pub; | ||
396 | else | ||
397 | return -ENOTCONN; | ||
398 | |||
399 | err = rdev_disassoc(rdev, dev, &req); | ||
400 | if (err) | ||
401 | return err; | ||
402 | |||
403 | /* driver should have reported the disassoc */ | ||
404 | WARN_ON(wdev->current_bss); | ||
405 | return 0; | ||
406 | } | ||
407 | |||
408 | void cfg80211_mlme_down(struct cfg80211_registered_device *rdev, | ||
409 | struct net_device *dev) | ||
410 | { | ||
411 | struct wireless_dev *wdev = dev->ieee80211_ptr; | ||
412 | u8 bssid[ETH_ALEN]; | ||
413 | |||
414 | ASSERT_WDEV_LOCK(wdev); | ||
415 | |||
416 | if (!rdev->ops->deauth) | ||
417 | return; | ||
418 | |||
419 | if (!wdev->current_bss) | ||
420 | return; | ||
421 | |||
422 | memcpy(bssid, wdev->current_bss->pub.bssid, ETH_ALEN); | ||
423 | cfg80211_mlme_deauth(rdev, dev, bssid, NULL, 0, | ||
424 | WLAN_REASON_DEAUTH_LEAVING, false); | ||
425 | } | ||
426 | |||
427 | struct cfg80211_mgmt_registration { | ||
428 | struct list_head list; | ||
429 | struct wireless_dev *wdev; | ||
430 | |||
431 | u32 nlportid; | ||
432 | |||
433 | int match_len; | ||
434 | |||
435 | __le16 frame_type; | ||
436 | |||
437 | bool multicast_rx; | ||
438 | |||
439 | u8 match[]; | ||
440 | }; | ||
441 | |||
442 | static void cfg80211_mgmt_registrations_update(struct wireless_dev *wdev) | ||
443 | { | ||
444 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); | ||
445 | struct wireless_dev *tmp; | ||
446 | struct cfg80211_mgmt_registration *reg; | ||
447 | struct mgmt_frame_regs upd = {}; | ||
448 | |||
449 | ASSERT_RTNL(); | ||
450 | |||
451 | spin_lock_bh(&rdev->mgmt_registrations_lock); | ||
452 | if (!wdev->mgmt_registrations_need_update) { | ||
453 | spin_unlock_bh(&rdev->mgmt_registrations_lock); | ||
454 | return; | ||
455 | } | ||
456 | |||
457 | rcu_read_lock(); | ||
458 | list_for_each_entry_rcu(tmp, &rdev->wiphy.wdev_list, list) { | ||
459 | list_for_each_entry(reg, &tmp->mgmt_registrations, list) { | ||
460 | u32 mask = BIT(le16_to_cpu(reg->frame_type) >> 4); | ||
461 | u32 mcast_mask = 0; | ||
462 | |||
463 | if (reg->multicast_rx) | ||
464 | mcast_mask = mask; | ||
465 | |||
466 | upd.global_stypes |= mask; | ||
467 | upd.global_mcast_stypes |= mcast_mask; | ||
468 | |||
469 | if (tmp == wdev) { | ||
470 | upd.interface_stypes |= mask; | ||
471 | upd.interface_mcast_stypes |= mcast_mask; | ||
472 | } | ||
473 | } | ||
474 | } | ||
475 | rcu_read_unlock(); | ||
476 | |||
477 | wdev->mgmt_registrations_need_update = 0; | ||
478 | spin_unlock_bh(&rdev->mgmt_registrations_lock); | ||
479 | |||
480 | rdev_update_mgmt_frame_registrations(rdev, wdev, &upd); | ||
481 | } | ||
482 | |||
483 | void cfg80211_mgmt_registrations_update_wk(struct work_struct *wk) | ||
484 | { | ||
485 | struct cfg80211_registered_device *rdev; | ||
486 | struct wireless_dev *wdev; | ||
487 | |||
488 | rdev = container_of(wk, struct cfg80211_registered_device, | ||
489 | mgmt_registrations_update_wk); | ||
490 | |||
491 | rtnl_lock(); | ||
492 | list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) | ||
493 | cfg80211_mgmt_registrations_update(wdev); | ||
494 | rtnl_unlock(); | ||
495 | } | ||
496 | |||
497 | int cfg80211_mlme_register_mgmt(struct wireless_dev *wdev, u32 snd_portid, | ||
498 | u16 frame_type, const u8 *match_data, | ||
499 | int match_len, bool multicast_rx, | ||
500 | struct netlink_ext_ack *extack) | ||
501 | { | ||
502 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); | ||
503 | struct cfg80211_mgmt_registration *reg, *nreg; | ||
504 | int err = 0; | ||
505 | u16 mgmt_type; | ||
506 | bool update_multicast = false; | ||
507 | |||
508 | if (!wdev->wiphy->mgmt_stypes) | ||
509 | return -EOPNOTSUPP; | ||
510 | |||
511 | if ((frame_type & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_MGMT) { | ||
512 | NL_SET_ERR_MSG(extack, "frame type not management"); | ||
513 | return -EINVAL; | ||
514 | } | ||
515 | |||
516 | if (frame_type & ~(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) { | ||
517 | NL_SET_ERR_MSG(extack, "Invalid frame type"); | ||
518 | return -EINVAL; | ||
519 | } | ||
520 | |||
521 | mgmt_type = (frame_type & IEEE80211_FCTL_STYPE) >> 4; | ||
522 | if (!(wdev->wiphy->mgmt_stypes[wdev->iftype].rx & BIT(mgmt_type))) { | ||
523 | NL_SET_ERR_MSG(extack, | ||
524 | "Registration to specific type not supported"); | ||
525 | return -EINVAL; | ||
526 | } | ||
527 | |||
528 | /* | ||
529 | * To support Pre Association Security Negotiation (PASN), registration | ||
530 | * for authentication frames should be supported. However, as some | ||
531 | * versions of the user space daemons wrongly register to all types of | ||
532 | * authentication frames (which might result in unexpected behavior) | ||
533 | * allow such registration if the request is for a specific | ||
534 | * authentication algorithm number. | ||
535 | */ | ||
536 | if (wdev->iftype == NL80211_IFTYPE_STATION && | ||
537 | (frame_type & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_AUTH && | ||
538 | !(match_data && match_len >= 2)) { | ||
539 | NL_SET_ERR_MSG(extack, | ||
540 | "Authentication algorithm number required"); | ||
541 | return -EINVAL; | ||
542 | } | ||
543 | |||
544 | nreg = kzalloc(sizeof(*reg) + match_len, GFP_KERNEL); | ||
545 | if (!nreg) | ||
546 | return -ENOMEM; | ||
547 | |||
548 | spin_lock_bh(&rdev->mgmt_registrations_lock); | ||
549 | |||
550 | list_for_each_entry(reg, &wdev->mgmt_registrations, list) { | ||
551 | int mlen = min(match_len, reg->match_len); | ||
552 | |||
553 | if (frame_type != le16_to_cpu(reg->frame_type)) | ||
554 | continue; | ||
555 | |||
556 | if (memcmp(reg->match, match_data, mlen) == 0) { | ||
557 | if (reg->multicast_rx != multicast_rx) { | ||
558 | update_multicast = true; | ||
559 | reg->multicast_rx = multicast_rx; | ||
560 | break; | ||
561 | } | ||
562 | NL_SET_ERR_MSG(extack, "Match already configured"); | ||
563 | err = -EALREADY; | ||
564 | break; | ||
565 | } | ||
566 | } | ||
567 | |||
568 | if (err) | ||
569 | goto out; | ||
570 | |||
571 | if (update_multicast) { | ||
572 | kfree(nreg); | ||
573 | } else { | ||
574 | memcpy(nreg->match, match_data, match_len); | ||
575 | nreg->match_len = match_len; | ||
576 | nreg->nlportid = snd_portid; | ||
577 | nreg->frame_type = cpu_to_le16(frame_type); | ||
578 | nreg->wdev = wdev; | ||
579 | nreg->multicast_rx = multicast_rx; | ||
580 | list_add(&nreg->list, &wdev->mgmt_registrations); | ||
581 | } | ||
582 | wdev->mgmt_registrations_need_update = 1; | ||
583 | spin_unlock_bh(&rdev->mgmt_registrations_lock); | ||
584 | |||
585 | cfg80211_mgmt_registrations_update(wdev); | ||
586 | |||
587 | return 0; | ||
588 | |||
589 | out: | ||
590 | kfree(nreg); | ||
591 | spin_unlock_bh(&rdev->mgmt_registrations_lock); | ||
592 | |||
593 | return err; | ||
594 | } | ||
595 | |||
596 | void cfg80211_mlme_unregister_socket(struct wireless_dev *wdev, u32 nlportid) | ||
597 | { | ||
598 | struct wiphy *wiphy = wdev->wiphy; | ||
599 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); | ||
600 | struct cfg80211_mgmt_registration *reg, *tmp; | ||
601 | |||
602 | spin_lock_bh(&rdev->mgmt_registrations_lock); | ||
603 | |||
604 | list_for_each_entry_safe(reg, tmp, &wdev->mgmt_registrations, list) { | ||
605 | if (reg->nlportid != nlportid) | ||
606 | continue; | ||
607 | |||
608 | list_del(®->list); | ||
609 | kfree(reg); | ||
610 | |||
611 | wdev->mgmt_registrations_need_update = 1; | ||
612 | schedule_work(&rdev->mgmt_registrations_update_wk); | ||
613 | } | ||
614 | |||
615 | spin_unlock_bh(&rdev->mgmt_registrations_lock); | ||
616 | |||
617 | if (nlportid && rdev->crit_proto_nlportid == nlportid) { | ||
618 | rdev->crit_proto_nlportid = 0; | ||
619 | rdev_crit_proto_stop(rdev, wdev); | ||
620 | } | ||
621 | |||
622 | if (nlportid == wdev->ap_unexpected_nlportid) | ||
623 | wdev->ap_unexpected_nlportid = 0; | ||
624 | } | ||
625 | |||
626 | void cfg80211_mlme_purge_registrations(struct wireless_dev *wdev) | ||
627 | { | ||
628 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); | ||
629 | struct cfg80211_mgmt_registration *reg, *tmp; | ||
630 | |||
631 | spin_lock_bh(&rdev->mgmt_registrations_lock); | ||
632 | list_for_each_entry_safe(reg, tmp, &wdev->mgmt_registrations, list) { | ||
633 | list_del(®->list); | ||
634 | kfree(reg); | ||
635 | } | ||
636 | wdev->mgmt_registrations_need_update = 1; | ||
637 | spin_unlock_bh(&rdev->mgmt_registrations_lock); | ||
638 | |||
639 | cfg80211_mgmt_registrations_update(wdev); | ||
640 | } | ||
641 | |||
642 | int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev, | ||
643 | struct wireless_dev *wdev, | ||
644 | struct cfg80211_mgmt_tx_params *params, u64 *cookie) | ||
645 | { | ||
646 | const struct ieee80211_mgmt *mgmt; | ||
647 | u16 stype; | ||
648 | |||
649 | if (!wdev->wiphy->mgmt_stypes) | ||
650 | return -EOPNOTSUPP; | ||
651 | |||
652 | if (!rdev->ops->mgmt_tx) | ||
653 | return -EOPNOTSUPP; | ||
654 | |||
655 | if (params->len < 24 + 1) | ||
656 | return -EINVAL; | ||
657 | |||
658 | mgmt = (const struct ieee80211_mgmt *)params->buf; | ||
659 | |||
660 | if (!ieee80211_is_mgmt(mgmt->frame_control)) | ||
661 | return -EINVAL; | ||
662 | |||
663 | stype = le16_to_cpu(mgmt->frame_control) & IEEE80211_FCTL_STYPE; | ||
664 | if (!(wdev->wiphy->mgmt_stypes[wdev->iftype].tx & BIT(stype >> 4))) | ||
665 | return -EINVAL; | ||
666 | |||
667 | if (ieee80211_is_action(mgmt->frame_control) && | ||
668 | mgmt->u.action.category != WLAN_CATEGORY_PUBLIC) { | ||
669 | int err = 0; | ||
670 | |||
671 | wdev_lock(wdev); | ||
672 | |||
673 | switch (wdev->iftype) { | ||
674 | case NL80211_IFTYPE_ADHOC: | ||
675 | case NL80211_IFTYPE_STATION: | ||
676 | case NL80211_IFTYPE_P2P_CLIENT: | ||
677 | if (!wdev->current_bss) { | ||
678 | err = -ENOTCONN; | ||
679 | break; | ||
680 | } | ||
681 | |||
682 | if (!ether_addr_equal(wdev->current_bss->pub.bssid, | ||
683 | mgmt->bssid)) { | ||
684 | err = -ENOTCONN; | ||
685 | break; | ||
686 | } | ||
687 | |||
688 | /* | ||
689 | * check for IBSS DA must be done by driver as | ||
690 | * cfg80211 doesn't track the stations | ||
691 | */ | ||
692 | if (wdev->iftype == NL80211_IFTYPE_ADHOC) | ||
693 | break; | ||
694 | |||
695 | /* for station, check that DA is the AP */ | ||
696 | if (!ether_addr_equal(wdev->current_bss->pub.bssid, | ||
697 | mgmt->da)) { | ||
698 | err = -ENOTCONN; | ||
699 | break; | ||
700 | } | ||
701 | break; | ||
702 | case NL80211_IFTYPE_AP: | ||
703 | case NL80211_IFTYPE_P2P_GO: | ||
704 | case NL80211_IFTYPE_AP_VLAN: | ||
705 | if (!ether_addr_equal(mgmt->bssid, wdev_address(wdev))) | ||
706 | err = -EINVAL; | ||
707 | break; | ||
708 | case NL80211_IFTYPE_MESH_POINT: | ||
709 | if (!ether_addr_equal(mgmt->sa, mgmt->bssid)) { | ||
710 | err = -EINVAL; | ||
711 | break; | ||
712 | } | ||
713 | /* | ||
714 | * check for mesh DA must be done by driver as | ||
715 | * cfg80211 doesn't track the stations | ||
716 | */ | ||
717 | break; | ||
718 | case NL80211_IFTYPE_P2P_DEVICE: | ||
719 | /* | ||
720 | * fall through, P2P device only supports | ||
721 | * public action frames | ||
722 | */ | ||
723 | case NL80211_IFTYPE_NAN: | ||
724 | default: | ||
725 | err = -EOPNOTSUPP; | ||
726 | break; | ||
727 | } | ||
728 | wdev_unlock(wdev); | ||
729 | |||
730 | if (err) | ||
731 | return err; | ||
732 | } | ||
733 | |||
734 | if (!ether_addr_equal(mgmt->sa, wdev_address(wdev))) { | ||
735 | /* Allow random TA to be used with Public Action frames if the | ||
736 | * driver has indicated support for this. Otherwise, only allow | ||
737 | * the local address to be used. | ||
738 | */ | ||
739 | if (!ieee80211_is_action(mgmt->frame_control) || | ||
740 | mgmt->u.action.category != WLAN_CATEGORY_PUBLIC) | ||
741 | return -EINVAL; | ||
742 | if (!wdev->current_bss && | ||
743 | !wiphy_ext_feature_isset( | ||
744 | &rdev->wiphy, | ||
745 | NL80211_EXT_FEATURE_MGMT_TX_RANDOM_TA)) | ||
746 | return -EINVAL; | ||
747 | if (wdev->current_bss && | ||
748 | !wiphy_ext_feature_isset( | ||
749 | &rdev->wiphy, | ||
750 | NL80211_EXT_FEATURE_MGMT_TX_RANDOM_TA_CONNECTED)) | ||
751 | return -EINVAL; | ||
752 | } | ||
753 | |||
754 | /* Transmit the Action frame as requested by user space */ | ||
755 | return rdev_mgmt_tx(rdev, wdev, params, cookie); | ||
756 | } | ||
757 | |||
758 | bool cfg80211_rx_mgmt_khz(struct wireless_dev *wdev, int freq, int sig_dbm, | ||
759 | const u8 *buf, size_t len, u32 flags) | ||
760 | { | ||
761 | struct wiphy *wiphy = wdev->wiphy; | ||
762 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); | ||
763 | struct cfg80211_mgmt_registration *reg; | ||
764 | const struct ieee80211_txrx_stypes *stypes = | ||
765 | &wiphy->mgmt_stypes[wdev->iftype]; | ||
766 | struct ieee80211_mgmt *mgmt = (void *)buf; | ||
767 | const u8 *data; | ||
768 | int data_len; | ||
769 | bool result = false; | ||
770 | __le16 ftype = mgmt->frame_control & | ||
771 | cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE); | ||
772 | u16 stype; | ||
773 | |||
774 | trace_cfg80211_rx_mgmt(wdev, freq, sig_dbm); | ||
775 | stype = (le16_to_cpu(mgmt->frame_control) & IEEE80211_FCTL_STYPE) >> 4; | ||
776 | |||
777 | if (!(stypes->rx & BIT(stype))) { | ||
778 | trace_cfg80211_return_bool(false); | ||
779 | return false; | ||
780 | } | ||
781 | |||
782 | data = buf + ieee80211_hdrlen(mgmt->frame_control); | ||
783 | data_len = len - ieee80211_hdrlen(mgmt->frame_control); | ||
784 | |||
785 | spin_lock_bh(&rdev->mgmt_registrations_lock); | ||
786 | |||
787 | list_for_each_entry(reg, &wdev->mgmt_registrations, list) { | ||
788 | if (reg->frame_type != ftype) | ||
789 | continue; | ||
790 | |||
791 | if (reg->match_len > data_len) | ||
792 | continue; | ||
793 | |||
794 | if (memcmp(reg->match, data, reg->match_len)) | ||
795 | continue; | ||
796 | |||
797 | /* found match! */ | ||
798 | |||
799 | /* Indicate the received Action frame to user space */ | ||
800 | if (nl80211_send_mgmt(rdev, wdev, reg->nlportid, | ||
801 | freq, sig_dbm, | ||
802 | buf, len, flags, GFP_ATOMIC)) | ||
803 | continue; | ||
804 | |||
805 | result = true; | ||
806 | break; | ||
807 | } | ||
808 | |||
809 | spin_unlock_bh(&rdev->mgmt_registrations_lock); | ||
810 | |||
811 | trace_cfg80211_return_bool(result); | ||
812 | return result; | ||
813 | } | ||
814 | EXPORT_SYMBOL(cfg80211_rx_mgmt_khz); | ||
815 | |||
816 | void cfg80211_sched_dfs_chan_update(struct cfg80211_registered_device *rdev) | ||
817 | { | ||
818 | cancel_delayed_work(&rdev->dfs_update_channels_wk); | ||
819 | queue_delayed_work(cfg80211_wq, &rdev->dfs_update_channels_wk, 0); | ||
820 | } | ||
821 | |||
822 | void cfg80211_dfs_channels_update_work(struct work_struct *work) | ||
823 | { | ||
824 | struct delayed_work *delayed_work = to_delayed_work(work); | ||
825 | struct cfg80211_registered_device *rdev; | ||
826 | struct cfg80211_chan_def chandef; | ||
827 | struct ieee80211_supported_band *sband; | ||
828 | struct ieee80211_channel *c; | ||
829 | struct wiphy *wiphy; | ||
830 | bool check_again = false; | ||
831 | unsigned long timeout, next_time = 0; | ||
832 | unsigned long time_dfs_update; | ||
833 | enum nl80211_radar_event radar_event; | ||
834 | int bandid, i; | ||
835 | |||
836 | rdev = container_of(delayed_work, struct cfg80211_registered_device, | ||
837 | dfs_update_channels_wk); | ||
838 | wiphy = &rdev->wiphy; | ||
839 | |||
840 | rtnl_lock(); | ||
841 | for (bandid = 0; bandid < NUM_NL80211_BANDS; bandid++) { | ||
842 | sband = wiphy->bands[bandid]; | ||
843 | if (!sband) | ||
844 | continue; | ||
845 | |||
846 | for (i = 0; i < sband->n_channels; i++) { | ||
847 | c = &sband->channels[i]; | ||
848 | |||
849 | if (!(c->flags & IEEE80211_CHAN_RADAR)) | ||
850 | continue; | ||
851 | |||
852 | if (c->dfs_state != NL80211_DFS_UNAVAILABLE && | ||
853 | c->dfs_state != NL80211_DFS_AVAILABLE) | ||
854 | continue; | ||
855 | |||
856 | if (c->dfs_state == NL80211_DFS_UNAVAILABLE) { | ||
857 | time_dfs_update = IEEE80211_DFS_MIN_NOP_TIME_MS; | ||
858 | radar_event = NL80211_RADAR_NOP_FINISHED; | ||
859 | } else { | ||
860 | if (regulatory_pre_cac_allowed(wiphy) || | ||
861 | cfg80211_any_wiphy_oper_chan(wiphy, c)) | ||
862 | continue; | ||
863 | |||
864 | time_dfs_update = REG_PRE_CAC_EXPIRY_GRACE_MS; | ||
865 | radar_event = NL80211_RADAR_PRE_CAC_EXPIRED; | ||
866 | } | ||
867 | |||
868 | timeout = c->dfs_state_entered + | ||
869 | msecs_to_jiffies(time_dfs_update); | ||
870 | |||
871 | if (time_after_eq(jiffies, timeout)) { | ||
872 | c->dfs_state = NL80211_DFS_USABLE; | ||
873 | c->dfs_state_entered = jiffies; | ||
874 | |||
875 | cfg80211_chandef_create(&chandef, c, | ||
876 | NL80211_CHAN_NO_HT); | ||
877 | |||
878 | nl80211_radar_notify(rdev, &chandef, | ||
879 | radar_event, NULL, | ||
880 | GFP_ATOMIC); | ||
881 | |||
882 | regulatory_propagate_dfs_state(wiphy, &chandef, | ||
883 | c->dfs_state, | ||
884 | radar_event); | ||
885 | continue; | ||
886 | } | ||
887 | |||
888 | if (!check_again) | ||
889 | next_time = timeout - jiffies; | ||
890 | else | ||
891 | next_time = min(next_time, timeout - jiffies); | ||
892 | check_again = true; | ||
893 | } | ||
894 | } | ||
895 | rtnl_unlock(); | ||
896 | |||
897 | /* reschedule if there are other channels waiting to be cleared again */ | ||
898 | if (check_again) | ||
899 | queue_delayed_work(cfg80211_wq, &rdev->dfs_update_channels_wk, | ||
900 | next_time); | ||
901 | } | ||
902 | |||
903 | |||
904 | void cfg80211_radar_event(struct wiphy *wiphy, | ||
905 | struct cfg80211_chan_def *chandef, | ||
906 | gfp_t gfp) | ||
907 | { | ||
908 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); | ||
909 | |||
910 | trace_cfg80211_radar_event(wiphy, chandef); | ||
911 | |||
912 | /* only set the chandef supplied channel to unavailable, in | ||
913 | * case the radar is detected on only one of multiple channels | ||
914 | * spanned by the chandef. | ||
915 | */ | ||
916 | cfg80211_set_dfs_state(wiphy, chandef, NL80211_DFS_UNAVAILABLE); | ||
917 | |||
918 | cfg80211_sched_dfs_chan_update(rdev); | ||
919 | |||
920 | nl80211_radar_notify(rdev, chandef, NL80211_RADAR_DETECTED, NULL, gfp); | ||
921 | |||
922 | memcpy(&rdev->radar_chandef, chandef, sizeof(struct cfg80211_chan_def)); | ||
923 | queue_work(cfg80211_wq, &rdev->propagate_radar_detect_wk); | ||
924 | } | ||
925 | EXPORT_SYMBOL(cfg80211_radar_event); | ||
926 | |||
927 | void cfg80211_cac_event(struct net_device *netdev, | ||
928 | const struct cfg80211_chan_def *chandef, | ||
929 | enum nl80211_radar_event event, gfp_t gfp) | ||
930 | { | ||
931 | struct wireless_dev *wdev = netdev->ieee80211_ptr; | ||
932 | struct wiphy *wiphy = wdev->wiphy; | ||
933 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); | ||
934 | unsigned long timeout; | ||
935 | |||
936 | trace_cfg80211_cac_event(netdev, event); | ||
937 | |||
938 | if (WARN_ON(!wdev->cac_started && event != NL80211_RADAR_CAC_STARTED)) | ||
939 | return; | ||
940 | |||
941 | if (WARN_ON(!wdev->chandef.chan)) | ||
942 | return; | ||
943 | |||
944 | switch (event) { | ||
945 | case NL80211_RADAR_CAC_FINISHED: | ||
946 | timeout = wdev->cac_start_time + | ||
947 | msecs_to_jiffies(wdev->cac_time_ms); | ||
948 | WARN_ON(!time_after_eq(jiffies, timeout)); | ||
949 | cfg80211_set_dfs_state(wiphy, chandef, NL80211_DFS_AVAILABLE); | ||
950 | memcpy(&rdev->cac_done_chandef, chandef, | ||
951 | sizeof(struct cfg80211_chan_def)); | ||
952 | queue_work(cfg80211_wq, &rdev->propagate_cac_done_wk); | ||
953 | cfg80211_sched_dfs_chan_update(rdev); | ||
954 | fallthrough; | ||
955 | case NL80211_RADAR_CAC_ABORTED: | ||
956 | wdev->cac_started = false; | ||
957 | break; | ||
958 | case NL80211_RADAR_CAC_STARTED: | ||
959 | wdev->cac_started = true; | ||
960 | break; | ||
961 | default: | ||
962 | WARN_ON(1); | ||
963 | return; | ||
964 | } | ||
965 | |||
966 | nl80211_radar_notify(rdev, chandef, event, netdev, gfp); | ||
967 | } | ||
968 | EXPORT_SYMBOL(cfg80211_cac_event); | ||