Commit | Line | Data |
---|---|---|
6039f6d2 JM |
1 | /* |
2 | * cfg80211 MLME SAP interface | |
3 | * | |
4 | * Copyright (c) 2009, Jouni Malinen <j@w1.fi> | |
5 | */ | |
6 | ||
7 | #include <linux/kernel.h> | |
8 | #include <linux/module.h> | |
9 | #include <linux/netdevice.h> | |
10 | #include <linux/nl80211.h> | |
5a0e3ad6 | 11 | #include <linux/slab.h> |
a9a11622 | 12 | #include <linux/wireless.h> |
6039f6d2 | 13 | #include <net/cfg80211.h> |
a9a11622 | 14 | #include <net/iw_handler.h> |
6039f6d2 JM |
15 | #include "core.h" |
16 | #include "nl80211.h" | |
17 | ||
cb0b4beb | 18 | void cfg80211_send_rx_auth(struct net_device *dev, const u8 *buf, size_t len) |
6039f6d2 | 19 | { |
19957bb3 JB |
20 | struct wireless_dev *wdev = dev->ieee80211_ptr; |
21 | struct wiphy *wiphy = wdev->wiphy; | |
6039f6d2 | 22 | struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); |
19957bb3 | 23 | |
667503dd | 24 | wdev_lock(wdev); |
cb0b4beb | 25 | |
95de817b JB |
26 | nl80211_send_rx_auth(rdev, dev, buf, len, GFP_KERNEL); |
27 | cfg80211_sme_rx_auth(dev, buf, len); | |
667503dd JB |
28 | |
29 | wdev_unlock(wdev); | |
6039f6d2 JM |
30 | } |
31 | EXPORT_SYMBOL(cfg80211_send_rx_auth); | |
32 | ||
95de817b JB |
33 | void cfg80211_send_rx_assoc(struct net_device *dev, struct cfg80211_bss *bss, |
34 | const u8 *buf, size_t len) | |
6039f6d2 | 35 | { |
6829c878 JB |
36 | u16 status_code; |
37 | struct wireless_dev *wdev = dev->ieee80211_ptr; | |
38 | struct wiphy *wiphy = wdev->wiphy; | |
6039f6d2 | 39 | struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); |
6829c878 JB |
40 | struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf; |
41 | u8 *ie = mgmt->u.assoc_resp.variable; | |
95de817b | 42 | int ieoffs = offsetof(struct ieee80211_mgmt, u.assoc_resp.variable); |
6829c878 | 43 | |
667503dd | 44 | wdev_lock(wdev); |
cb0b4beb | 45 | |
6829c878 JB |
46 | status_code = le16_to_cpu(mgmt->u.assoc_resp.status_code); |
47 | ||
f401a6f7 JB |
48 | /* |
49 | * This is a bit of a hack, we don't notify userspace of | |
50 | * a (re-)association reply if we tried to send a reassoc | |
51 | * and got a reject -- we only try again with an assoc | |
52 | * frame instead of reassoc. | |
53 | */ | |
54 | if (status_code != WLAN_STATUS_SUCCESS && wdev->conn && | |
95de817b JB |
55 | cfg80211_sme_failed_reassoc(wdev)) { |
56 | cfg80211_put_bss(bss); | |
f401a6f7 | 57 | goto out; |
95de817b | 58 | } |
f401a6f7 | 59 | |
cb0b4beb | 60 | nl80211_send_rx_assoc(rdev, dev, buf, len, GFP_KERNEL); |
6829c878 | 61 | |
95de817b | 62 | if (status_code != WLAN_STATUS_SUCCESS && wdev->conn) { |
7d930bc3 | 63 | cfg80211_sme_failed_assoc(wdev); |
7d930bc3 JB |
64 | /* |
65 | * do not call connect_result() now because the | |
66 | * sme will schedule work that does it later. | |
67 | */ | |
95de817b | 68 | cfg80211_put_bss(bss); |
7d930bc3 | 69 | goto out; |
df7fc0f9 JB |
70 | } |
71 | ||
ea416a79 JB |
72 | if (!wdev->conn && wdev->sme_state == CFG80211_SME_IDLE) { |
73 | /* | |
74 | * This is for the userspace SME, the CONNECTING | |
75 | * state will be changed to CONNECTED by | |
76 | * __cfg80211_connect_result() below. | |
77 | */ | |
78 | wdev->sme_state = CFG80211_SME_CONNECTING; | |
79 | } | |
80 | ||
95de817b | 81 | /* this consumes the bss reference */ |
df7fc0f9 JB |
82 | __cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, ie, len - ieoffs, |
83 | status_code, | |
95de817b | 84 | status_code == WLAN_STATUS_SUCCESS, bss); |
f401a6f7 | 85 | out: |
667503dd | 86 | wdev_unlock(wdev); |
6039f6d2 JM |
87 | } |
88 | EXPORT_SYMBOL(cfg80211_send_rx_assoc); | |
89 | ||
ce470613 | 90 | void __cfg80211_send_deauth(struct net_device *dev, |
667503dd | 91 | const u8 *buf, size_t len) |
6039f6d2 | 92 | { |
6829c878 JB |
93 | struct wireless_dev *wdev = dev->ieee80211_ptr; |
94 | struct wiphy *wiphy = wdev->wiphy; | |
6039f6d2 | 95 | struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); |
6829c878 | 96 | struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf; |
19957bb3 | 97 | const u8 *bssid = mgmt->bssid; |
95de817b | 98 | bool was_current = false; |
6829c878 | 99 | |
667503dd | 100 | ASSERT_WDEV_LOCK(wdev); |
cb0b4beb | 101 | |
19957bb3 JB |
102 | if (wdev->current_bss && |
103 | memcmp(wdev->current_bss->pub.bssid, bssid, ETH_ALEN) == 0) { | |
19957bb3 JB |
104 | cfg80211_unhold_bss(wdev->current_bss); |
105 | cfg80211_put_bss(&wdev->current_bss->pub); | |
106 | wdev->current_bss = NULL; | |
3f3b6a8d | 107 | was_current = true; |
19957bb3 | 108 | } |
19957bb3 | 109 | |
5fba4af3 JB |
110 | nl80211_send_deauth(rdev, dev, buf, len, GFP_KERNEL); |
111 | ||
3f3b6a8d | 112 | if (wdev->sme_state == CFG80211_SME_CONNECTED && was_current) { |
6829c878 JB |
113 | u16 reason_code; |
114 | bool from_ap; | |
115 | ||
116 | reason_code = le16_to_cpu(mgmt->u.deauth.reason_code); | |
117 | ||
e458b8a2 | 118 | from_ap = memcmp(mgmt->sa, dev->dev_addr, ETH_ALEN) != 0; |
667503dd | 119 | __cfg80211_disconnected(dev, NULL, 0, reason_code, from_ap); |
6829c878 | 120 | } else if (wdev->sme_state == CFG80211_SME_CONNECTING) { |
667503dd JB |
121 | __cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, NULL, 0, |
122 | WLAN_STATUS_UNSPECIFIED_FAILURE, | |
df7fc0f9 | 123 | false, NULL); |
667503dd JB |
124 | } |
125 | } | |
ce470613 | 126 | EXPORT_SYMBOL(__cfg80211_send_deauth); |
667503dd | 127 | |
ce470613 | 128 | void cfg80211_send_deauth(struct net_device *dev, const u8 *buf, size_t len) |
667503dd JB |
129 | { |
130 | struct wireless_dev *wdev = dev->ieee80211_ptr; | |
131 | ||
ce470613 HS |
132 | wdev_lock(wdev); |
133 | __cfg80211_send_deauth(dev, buf, len); | |
134 | wdev_unlock(wdev); | |
6039f6d2 | 135 | } |
53b46b84 | 136 | EXPORT_SYMBOL(cfg80211_send_deauth); |
6039f6d2 | 137 | |
ce470613 | 138 | void __cfg80211_send_disassoc(struct net_device *dev, |
667503dd | 139 | const u8 *buf, size_t len) |
6039f6d2 | 140 | { |
6829c878 JB |
141 | struct wireless_dev *wdev = dev->ieee80211_ptr; |
142 | struct wiphy *wiphy = wdev->wiphy; | |
6039f6d2 | 143 | struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); |
6829c878 | 144 | struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf; |
19957bb3 | 145 | const u8 *bssid = mgmt->bssid; |
19957bb3 JB |
146 | u16 reason_code; |
147 | bool from_ap; | |
6829c878 | 148 | |
596a07c1 | 149 | ASSERT_WDEV_LOCK(wdev); |
cb0b4beb JB |
150 | |
151 | nl80211_send_disassoc(rdev, dev, buf, len, GFP_KERNEL); | |
a3b8b056 | 152 | |
596a07c1 JB |
153 | if (wdev->sme_state != CFG80211_SME_CONNECTED) |
154 | return; | |
6829c878 | 155 | |
19957bb3 | 156 | if (wdev->current_bss && |
b935df01 | 157 | memcmp(wdev->current_bss->pub.bssid, bssid, ETH_ALEN) == 0) { |
95de817b JB |
158 | cfg80211_sme_disassoc(dev, wdev->current_bss); |
159 | cfg80211_unhold_bss(wdev->current_bss); | |
160 | cfg80211_put_bss(&wdev->current_bss->pub); | |
161 | wdev->current_bss = NULL; | |
19957bb3 JB |
162 | } else |
163 | WARN_ON(1); | |
6829c878 | 164 | |
6829c878 | 165 | |
19957bb3 JB |
166 | reason_code = le16_to_cpu(mgmt->u.disassoc.reason_code); |
167 | ||
e458b8a2 | 168 | from_ap = memcmp(mgmt->sa, dev->dev_addr, ETH_ALEN) != 0; |
667503dd | 169 | __cfg80211_disconnected(dev, NULL, 0, reason_code, from_ap); |
667503dd | 170 | } |
ce470613 | 171 | EXPORT_SYMBOL(__cfg80211_send_disassoc); |
667503dd | 172 | |
ce470613 | 173 | void cfg80211_send_disassoc(struct net_device *dev, const u8 *buf, size_t len) |
667503dd JB |
174 | { |
175 | struct wireless_dev *wdev = dev->ieee80211_ptr; | |
176 | ||
ce470613 HS |
177 | wdev_lock(wdev); |
178 | __cfg80211_send_disassoc(dev, buf, len); | |
179 | wdev_unlock(wdev); | |
1965c853 | 180 | } |
6829c878 | 181 | EXPORT_SYMBOL(cfg80211_send_disassoc); |
1965c853 | 182 | |
cf4e594e JM |
183 | void cfg80211_send_unprot_deauth(struct net_device *dev, const u8 *buf, |
184 | size_t len) | |
185 | { | |
186 | struct wireless_dev *wdev = dev->ieee80211_ptr; | |
187 | struct wiphy *wiphy = wdev->wiphy; | |
188 | struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); | |
189 | ||
190 | nl80211_send_unprot_deauth(rdev, dev, buf, len, GFP_ATOMIC); | |
191 | } | |
192 | EXPORT_SYMBOL(cfg80211_send_unprot_deauth); | |
193 | ||
194 | void cfg80211_send_unprot_disassoc(struct net_device *dev, const u8 *buf, | |
195 | size_t len) | |
196 | { | |
197 | struct wireless_dev *wdev = dev->ieee80211_ptr; | |
198 | struct wiphy *wiphy = wdev->wiphy; | |
199 | struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); | |
200 | ||
201 | nl80211_send_unprot_disassoc(rdev, dev, buf, len, GFP_ATOMIC); | |
202 | } | |
203 | EXPORT_SYMBOL(cfg80211_send_unprot_disassoc); | |
204 | ||
a58ce43f JB |
205 | void cfg80211_send_auth_timeout(struct net_device *dev, const u8 *addr) |
206 | { | |
207 | struct wireless_dev *wdev = dev->ieee80211_ptr; | |
208 | struct wiphy *wiphy = wdev->wiphy; | |
209 | struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); | |
210 | ||
211 | wdev_lock(wdev); | |
212 | ||
213 | nl80211_send_auth_timeout(rdev, dev, addr, GFP_KERNEL); | |
214 | if (wdev->sme_state == CFG80211_SME_CONNECTING) | |
215 | __cfg80211_connect_result(dev, addr, NULL, 0, NULL, 0, | |
216 | WLAN_STATUS_UNSPECIFIED_FAILURE, | |
217 | false, NULL); | |
218 | ||
667503dd | 219 | wdev_unlock(wdev); |
1965c853 JM |
220 | } |
221 | EXPORT_SYMBOL(cfg80211_send_auth_timeout); | |
222 | ||
cb0b4beb | 223 | void cfg80211_send_assoc_timeout(struct net_device *dev, const u8 *addr) |
1965c853 | 224 | { |
6829c878 JB |
225 | struct wireless_dev *wdev = dev->ieee80211_ptr; |
226 | struct wiphy *wiphy = wdev->wiphy; | |
1965c853 | 227 | struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); |
19957bb3 | 228 | |
667503dd | 229 | wdev_lock(wdev); |
cb0b4beb JB |
230 | |
231 | nl80211_send_assoc_timeout(rdev, dev, addr, GFP_KERNEL); | |
6829c878 | 232 | if (wdev->sme_state == CFG80211_SME_CONNECTING) |
667503dd JB |
233 | __cfg80211_connect_result(dev, addr, NULL, 0, NULL, 0, |
234 | WLAN_STATUS_UNSPECIFIED_FAILURE, | |
df7fc0f9 | 235 | false, NULL); |
19957bb3 | 236 | |
667503dd | 237 | wdev_unlock(wdev); |
1965c853 JM |
238 | } |
239 | EXPORT_SYMBOL(cfg80211_send_assoc_timeout); | |
240 | ||
a3b8b056 JM |
241 | void cfg80211_michael_mic_failure(struct net_device *dev, const u8 *addr, |
242 | enum nl80211_key_type key_type, int key_id, | |
e6d6e342 | 243 | const u8 *tsc, gfp_t gfp) |
a3b8b056 JM |
244 | { |
245 | struct wiphy *wiphy = dev->ieee80211_ptr->wiphy; | |
246 | struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); | |
3d23e349 | 247 | #ifdef CONFIG_CFG80211_WEXT |
f58d4ed9 | 248 | union iwreq_data wrqu; |
e6d6e342 | 249 | char *buf = kmalloc(128, gfp); |
f58d4ed9 JB |
250 | |
251 | if (buf) { | |
252 | sprintf(buf, "MLME-MICHAELMICFAILURE.indication(" | |
253 | "keyid=%d %scast addr=%pM)", key_id, | |
254 | key_type == NL80211_KEYTYPE_GROUP ? "broad" : "uni", | |
255 | addr); | |
256 | memset(&wrqu, 0, sizeof(wrqu)); | |
257 | wrqu.data.length = strlen(buf); | |
258 | wireless_send_event(dev, IWEVCUSTOM, &wrqu, buf); | |
259 | kfree(buf); | |
260 | } | |
261 | #endif | |
262 | ||
e6d6e342 | 263 | nl80211_michael_mic_failure(rdev, dev, addr, key_type, key_id, tsc, gfp); |
a3b8b056 JM |
264 | } |
265 | EXPORT_SYMBOL(cfg80211_michael_mic_failure); | |
19957bb3 JB |
266 | |
267 | /* some MLME handling for userspace SME */ | |
667503dd JB |
268 | int __cfg80211_mlme_auth(struct cfg80211_registered_device *rdev, |
269 | struct net_device *dev, | |
270 | struct ieee80211_channel *chan, | |
271 | enum nl80211_auth_type auth_type, | |
272 | const u8 *bssid, | |
273 | const u8 *ssid, int ssid_len, | |
fffd0934 | 274 | const u8 *ie, int ie_len, |
95de817b | 275 | const u8 *key, int key_len, int key_idx) |
19957bb3 JB |
276 | { |
277 | struct wireless_dev *wdev = dev->ieee80211_ptr; | |
278 | struct cfg80211_auth_request req; | |
95de817b | 279 | int err; |
19957bb3 | 280 | |
667503dd JB |
281 | ASSERT_WDEV_LOCK(wdev); |
282 | ||
fffd0934 JB |
283 | if (auth_type == NL80211_AUTHTYPE_SHARED_KEY) |
284 | if (!key || !key_len || key_idx < 0 || key_idx > 4) | |
285 | return -EINVAL; | |
286 | ||
0a9b5e17 JB |
287 | if (wdev->current_bss && |
288 | memcmp(bssid, wdev->current_bss->pub.bssid, ETH_ALEN) == 0) | |
289 | return -EALREADY; | |
290 | ||
19957bb3 JB |
291 | memset(&req, 0, sizeof(req)); |
292 | ||
293 | req.ie = ie; | |
294 | req.ie_len = ie_len; | |
295 | req.auth_type = auth_type; | |
296 | req.bss = cfg80211_get_bss(&rdev->wiphy, chan, bssid, ssid, ssid_len, | |
297 | WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS); | |
fffd0934 JB |
298 | req.key = key; |
299 | req.key_len = key_len; | |
300 | req.key_idx = key_idx; | |
19957bb3 JB |
301 | if (!req.bss) |
302 | return -ENOENT; | |
303 | ||
19957bb3 | 304 | err = rdev->ops->auth(&rdev->wiphy, dev, &req); |
19957bb3 | 305 | |
95de817b | 306 | cfg80211_put_bss(req.bss); |
19957bb3 JB |
307 | return err; |
308 | } | |
309 | ||
667503dd JB |
310 | int cfg80211_mlme_auth(struct cfg80211_registered_device *rdev, |
311 | struct net_device *dev, struct ieee80211_channel *chan, | |
312 | enum nl80211_auth_type auth_type, const u8 *bssid, | |
313 | const u8 *ssid, int ssid_len, | |
fffd0934 | 314 | const u8 *ie, int ie_len, |
95de817b | 315 | const u8 *key, int key_len, int key_idx) |
667503dd JB |
316 | { |
317 | int err; | |
318 | ||
319 | wdev_lock(dev->ieee80211_ptr); | |
320 | err = __cfg80211_mlme_auth(rdev, dev, chan, auth_type, bssid, | |
fffd0934 | 321 | ssid, ssid_len, ie, ie_len, |
95de817b | 322 | key, key_len, key_idx); |
667503dd JB |
323 | wdev_unlock(dev->ieee80211_ptr); |
324 | ||
325 | return err; | |
326 | } | |
327 | ||
7e7c8926 BG |
328 | /* Do a logical ht_capa &= ht_capa_mask. */ |
329 | void cfg80211_oper_and_ht_capa(struct ieee80211_ht_cap *ht_capa, | |
330 | const struct ieee80211_ht_cap *ht_capa_mask) | |
331 | { | |
332 | int i; | |
333 | u8 *p1, *p2; | |
334 | if (!ht_capa_mask) { | |
335 | memset(ht_capa, 0, sizeof(*ht_capa)); | |
336 | return; | |
337 | } | |
338 | ||
339 | p1 = (u8*)(ht_capa); | |
340 | p2 = (u8*)(ht_capa_mask); | |
341 | for (i = 0; i<sizeof(*ht_capa); i++) | |
342 | p1[i] &= p2[i]; | |
343 | } | |
344 | ||
667503dd JB |
345 | int __cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev, |
346 | struct net_device *dev, | |
347 | struct ieee80211_channel *chan, | |
348 | const u8 *bssid, const u8 *prev_bssid, | |
349 | const u8 *ssid, int ssid_len, | |
350 | const u8 *ie, int ie_len, bool use_mfp, | |
7e7c8926 BG |
351 | struct cfg80211_crypto_settings *crypt, |
352 | u32 assoc_flags, struct ieee80211_ht_cap *ht_capa, | |
353 | struct ieee80211_ht_cap *ht_capa_mask) | |
19957bb3 JB |
354 | { |
355 | struct wireless_dev *wdev = dev->ieee80211_ptr; | |
356 | struct cfg80211_assoc_request req; | |
95de817b | 357 | int err; |
24b6b15f | 358 | bool was_connected = false; |
19957bb3 | 359 | |
667503dd JB |
360 | ASSERT_WDEV_LOCK(wdev); |
361 | ||
19957bb3 JB |
362 | memset(&req, 0, sizeof(req)); |
363 | ||
24b6b15f JM |
364 | if (wdev->current_bss && prev_bssid && |
365 | memcmp(wdev->current_bss->pub.bssid, prev_bssid, ETH_ALEN) == 0) { | |
366 | /* | |
367 | * Trying to reassociate: Allow this to proceed and let the old | |
368 | * association to be dropped when the new one is completed. | |
369 | */ | |
370 | if (wdev->sme_state == CFG80211_SME_CONNECTED) { | |
371 | was_connected = true; | |
372 | wdev->sme_state = CFG80211_SME_CONNECTING; | |
373 | } | |
374 | } else if (wdev->current_bss) | |
19957bb3 JB |
375 | return -EALREADY; |
376 | ||
377 | req.ie = ie; | |
378 | req.ie_len = ie_len; | |
379 | memcpy(&req.crypto, crypt, sizeof(req.crypto)); | |
380 | req.use_mfp = use_mfp; | |
3e5d7649 | 381 | req.prev_bssid = prev_bssid; |
7e7c8926 BG |
382 | req.flags = assoc_flags; |
383 | if (ht_capa) | |
384 | memcpy(&req.ht_capa, ht_capa, sizeof(req.ht_capa)); | |
385 | if (ht_capa_mask) | |
386 | memcpy(&req.ht_capa_mask, ht_capa_mask, | |
387 | sizeof(req.ht_capa_mask)); | |
388 | cfg80211_oper_and_ht_capa(&req.ht_capa_mask, | |
389 | rdev->wiphy.ht_capa_mod_mask); | |
390 | ||
19957bb3 JB |
391 | req.bss = cfg80211_get_bss(&rdev->wiphy, chan, bssid, ssid, ssid_len, |
392 | WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS); | |
24b6b15f JM |
393 | if (!req.bss) { |
394 | if (was_connected) | |
395 | wdev->sme_state = CFG80211_SME_CONNECTED; | |
19957bb3 | 396 | return -ENOENT; |
24b6b15f | 397 | } |
19957bb3 | 398 | |
95de817b | 399 | err = rdev->ops->assoc(&rdev->wiphy, dev, &req); |
19957bb3 | 400 | |
95de817b JB |
401 | if (err) { |
402 | if (was_connected) | |
403 | wdev->sme_state = CFG80211_SME_CONNECTED; | |
404 | cfg80211_put_bss(req.bss); | |
19957bb3 JB |
405 | } |
406 | ||
19957bb3 JB |
407 | return err; |
408 | } | |
409 | ||
667503dd JB |
410 | int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev, |
411 | struct net_device *dev, | |
412 | struct ieee80211_channel *chan, | |
413 | const u8 *bssid, const u8 *prev_bssid, | |
414 | const u8 *ssid, int ssid_len, | |
415 | const u8 *ie, int ie_len, bool use_mfp, | |
7e7c8926 BG |
416 | struct cfg80211_crypto_settings *crypt, |
417 | u32 assoc_flags, struct ieee80211_ht_cap *ht_capa, | |
418 | struct ieee80211_ht_cap *ht_capa_mask) | |
667503dd JB |
419 | { |
420 | struct wireless_dev *wdev = dev->ieee80211_ptr; | |
421 | int err; | |
422 | ||
423 | wdev_lock(wdev); | |
424 | err = __cfg80211_mlme_assoc(rdev, dev, chan, bssid, prev_bssid, | |
7e7c8926 BG |
425 | ssid, ssid_len, ie, ie_len, use_mfp, crypt, |
426 | assoc_flags, ht_capa, ht_capa_mask); | |
667503dd JB |
427 | wdev_unlock(wdev); |
428 | ||
429 | return err; | |
430 | } | |
431 | ||
432 | int __cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev, | |
433 | struct net_device *dev, const u8 *bssid, | |
d5cdfacb JM |
434 | const u8 *ie, int ie_len, u16 reason, |
435 | bool local_state_change) | |
19957bb3 JB |
436 | { |
437 | struct wireless_dev *wdev = dev->ieee80211_ptr; | |
95de817b JB |
438 | struct cfg80211_deauth_request req = { |
439 | .bssid = bssid, | |
440 | .reason_code = reason, | |
441 | .ie = ie, | |
442 | .ie_len = ie_len, | |
443 | }; | |
19957bb3 | 444 | |
667503dd JB |
445 | ASSERT_WDEV_LOCK(wdev); |
446 | ||
95de817b JB |
447 | if (local_state_change) { |
448 | if (wdev->current_bss && | |
449 | memcmp(wdev->current_bss->pub.bssid, bssid, ETH_ALEN) == 0) { | |
450 | cfg80211_unhold_bss(wdev->current_bss); | |
451 | cfg80211_put_bss(&wdev->current_bss->pub); | |
452 | wdev->current_bss = NULL; | |
19957bb3 | 453 | } |
19957bb3 | 454 | |
95de817b JB |
455 | return 0; |
456 | } | |
19957bb3 | 457 | |
63c9c5e7 | 458 | return rdev->ops->deauth(&rdev->wiphy, dev, &req); |
19957bb3 JB |
459 | } |
460 | ||
667503dd JB |
461 | int cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev, |
462 | struct net_device *dev, const u8 *bssid, | |
d5cdfacb JM |
463 | const u8 *ie, int ie_len, u16 reason, |
464 | bool local_state_change) | |
667503dd JB |
465 | { |
466 | struct wireless_dev *wdev = dev->ieee80211_ptr; | |
467 | int err; | |
468 | ||
469 | wdev_lock(wdev); | |
d5cdfacb JM |
470 | err = __cfg80211_mlme_deauth(rdev, dev, bssid, ie, ie_len, reason, |
471 | local_state_change); | |
667503dd JB |
472 | wdev_unlock(wdev); |
473 | ||
474 | return err; | |
475 | } | |
476 | ||
477 | static int __cfg80211_mlme_disassoc(struct cfg80211_registered_device *rdev, | |
478 | struct net_device *dev, const u8 *bssid, | |
d5cdfacb JM |
479 | const u8 *ie, int ie_len, u16 reason, |
480 | bool local_state_change) | |
19957bb3 JB |
481 | { |
482 | struct wireless_dev *wdev = dev->ieee80211_ptr; | |
483 | struct cfg80211_disassoc_request req; | |
484 | ||
667503dd JB |
485 | ASSERT_WDEV_LOCK(wdev); |
486 | ||
f9d6b402 JB |
487 | if (wdev->sme_state != CFG80211_SME_CONNECTED) |
488 | return -ENOTCONN; | |
489 | ||
490 | if (WARN_ON(!wdev->current_bss)) | |
491 | return -ENOTCONN; | |
492 | ||
19957bb3 JB |
493 | memset(&req, 0, sizeof(req)); |
494 | req.reason_code = reason; | |
d5cdfacb | 495 | req.local_state_change = local_state_change; |
19957bb3 JB |
496 | req.ie = ie; |
497 | req.ie_len = ie_len; | |
498 | if (memcmp(wdev->current_bss->pub.bssid, bssid, ETH_ALEN) == 0) | |
499 | req.bss = &wdev->current_bss->pub; | |
500 | else | |
501 | return -ENOTCONN; | |
502 | ||
63c9c5e7 | 503 | return rdev->ops->disassoc(&rdev->wiphy, dev, &req); |
667503dd JB |
504 | } |
505 | ||
506 | int cfg80211_mlme_disassoc(struct cfg80211_registered_device *rdev, | |
507 | struct net_device *dev, const u8 *bssid, | |
d5cdfacb JM |
508 | const u8 *ie, int ie_len, u16 reason, |
509 | bool local_state_change) | |
667503dd JB |
510 | { |
511 | struct wireless_dev *wdev = dev->ieee80211_ptr; | |
512 | int err; | |
513 | ||
514 | wdev_lock(wdev); | |
d5cdfacb JM |
515 | err = __cfg80211_mlme_disassoc(rdev, dev, bssid, ie, ie_len, reason, |
516 | local_state_change); | |
667503dd JB |
517 | wdev_unlock(wdev); |
518 | ||
519 | return err; | |
19957bb3 JB |
520 | } |
521 | ||
522 | void cfg80211_mlme_down(struct cfg80211_registered_device *rdev, | |
523 | struct net_device *dev) | |
524 | { | |
525 | struct wireless_dev *wdev = dev->ieee80211_ptr; | |
526 | struct cfg80211_deauth_request req; | |
95de817b | 527 | u8 bssid[ETH_ALEN]; |
19957bb3 | 528 | |
667503dd JB |
529 | ASSERT_WDEV_LOCK(wdev); |
530 | ||
19957bb3 JB |
531 | if (!rdev->ops->deauth) |
532 | return; | |
533 | ||
534 | memset(&req, 0, sizeof(req)); | |
535 | req.reason_code = WLAN_REASON_DEAUTH_LEAVING; | |
536 | req.ie = NULL; | |
537 | req.ie_len = 0; | |
538 | ||
95de817b JB |
539 | if (!wdev->current_bss) |
540 | return; | |
19957bb3 | 541 | |
95de817b JB |
542 | memcpy(bssid, wdev->current_bss->pub.bssid, ETH_ALEN); |
543 | req.bssid = bssid; | |
63c9c5e7 | 544 | rdev->ops->deauth(&rdev->wiphy, dev, &req); |
95de817b JB |
545 | |
546 | if (wdev->current_bss) { | |
547 | cfg80211_unhold_bss(wdev->current_bss); | |
548 | cfg80211_put_bss(&wdev->current_bss->pub); | |
549 | wdev->current_bss = NULL; | |
19957bb3 JB |
550 | } |
551 | } | |
9588bbd5 JM |
552 | |
553 | void cfg80211_ready_on_channel(struct net_device *dev, u64 cookie, | |
554 | struct ieee80211_channel *chan, | |
555 | enum nl80211_channel_type channel_type, | |
556 | unsigned int duration, gfp_t gfp) | |
557 | { | |
558 | struct wiphy *wiphy = dev->ieee80211_ptr->wiphy; | |
559 | struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); | |
560 | ||
561 | nl80211_send_remain_on_channel(rdev, dev, cookie, chan, channel_type, | |
562 | duration, gfp); | |
563 | } | |
564 | EXPORT_SYMBOL(cfg80211_ready_on_channel); | |
565 | ||
566 | void cfg80211_remain_on_channel_expired(struct net_device *dev, | |
567 | u64 cookie, | |
568 | struct ieee80211_channel *chan, | |
569 | enum nl80211_channel_type channel_type, | |
570 | gfp_t gfp) | |
571 | { | |
572 | struct wiphy *wiphy = dev->ieee80211_ptr->wiphy; | |
573 | struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); | |
574 | ||
575 | nl80211_send_remain_on_channel_cancel(rdev, dev, cookie, chan, | |
576 | channel_type, gfp); | |
577 | } | |
578 | EXPORT_SYMBOL(cfg80211_remain_on_channel_expired); | |
98b62183 JB |
579 | |
580 | void cfg80211_new_sta(struct net_device *dev, const u8 *mac_addr, | |
581 | struct station_info *sinfo, gfp_t gfp) | |
582 | { | |
583 | struct wiphy *wiphy = dev->ieee80211_ptr->wiphy; | |
584 | struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); | |
585 | ||
586 | nl80211_send_sta_event(rdev, dev, mac_addr, sinfo, gfp); | |
587 | } | |
588 | EXPORT_SYMBOL(cfg80211_new_sta); | |
026331c4 | 589 | |
ec15e68b JM |
590 | void cfg80211_del_sta(struct net_device *dev, const u8 *mac_addr, gfp_t gfp) |
591 | { | |
592 | struct wiphy *wiphy = dev->ieee80211_ptr->wiphy; | |
593 | struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); | |
594 | ||
595 | nl80211_send_sta_del_event(rdev, dev, mac_addr, gfp); | |
596 | } | |
597 | EXPORT_SYMBOL(cfg80211_del_sta); | |
598 | ||
2e161f78 | 599 | struct cfg80211_mgmt_registration { |
026331c4 JM |
600 | struct list_head list; |
601 | ||
602 | u32 nlpid; | |
603 | ||
604 | int match_len; | |
605 | ||
2e161f78 JB |
606 | __le16 frame_type; |
607 | ||
026331c4 JM |
608 | u8 match[]; |
609 | }; | |
610 | ||
2e161f78 JB |
611 | int cfg80211_mlme_register_mgmt(struct wireless_dev *wdev, u32 snd_pid, |
612 | u16 frame_type, const u8 *match_data, | |
613 | int match_len) | |
026331c4 | 614 | { |
271733cf JB |
615 | struct wiphy *wiphy = wdev->wiphy; |
616 | struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); | |
2e161f78 | 617 | struct cfg80211_mgmt_registration *reg, *nreg; |
026331c4 | 618 | int err = 0; |
2e161f78 JB |
619 | u16 mgmt_type; |
620 | ||
621 | if (!wdev->wiphy->mgmt_stypes) | |
622 | return -EOPNOTSUPP; | |
623 | ||
624 | if ((frame_type & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_MGMT) | |
625 | return -EINVAL; | |
626 | ||
627 | if (frame_type & ~(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) | |
628 | return -EINVAL; | |
629 | ||
630 | mgmt_type = (frame_type & IEEE80211_FCTL_STYPE) >> 4; | |
631 | if (!(wdev->wiphy->mgmt_stypes[wdev->iftype].rx & BIT(mgmt_type))) | |
632 | return -EINVAL; | |
026331c4 JM |
633 | |
634 | nreg = kzalloc(sizeof(*reg) + match_len, GFP_KERNEL); | |
635 | if (!nreg) | |
636 | return -ENOMEM; | |
637 | ||
2e161f78 | 638 | spin_lock_bh(&wdev->mgmt_registrations_lock); |
026331c4 | 639 | |
2e161f78 | 640 | list_for_each_entry(reg, &wdev->mgmt_registrations, list) { |
026331c4 JM |
641 | int mlen = min(match_len, reg->match_len); |
642 | ||
2e161f78 JB |
643 | if (frame_type != le16_to_cpu(reg->frame_type)) |
644 | continue; | |
645 | ||
026331c4 JM |
646 | if (memcmp(reg->match, match_data, mlen) == 0) { |
647 | err = -EALREADY; | |
648 | break; | |
649 | } | |
650 | } | |
651 | ||
652 | if (err) { | |
653 | kfree(nreg); | |
654 | goto out; | |
655 | } | |
656 | ||
657 | memcpy(nreg->match, match_data, match_len); | |
658 | nreg->match_len = match_len; | |
659 | nreg->nlpid = snd_pid; | |
2e161f78 JB |
660 | nreg->frame_type = cpu_to_le16(frame_type); |
661 | list_add(&nreg->list, &wdev->mgmt_registrations); | |
026331c4 | 662 | |
271733cf JB |
663 | if (rdev->ops->mgmt_frame_register) |
664 | rdev->ops->mgmt_frame_register(wiphy, wdev->netdev, | |
665 | frame_type, true); | |
666 | ||
026331c4 | 667 | out: |
2e161f78 | 668 | spin_unlock_bh(&wdev->mgmt_registrations_lock); |
271733cf | 669 | |
026331c4 JM |
670 | return err; |
671 | } | |
672 | ||
2e161f78 | 673 | void cfg80211_mlme_unregister_socket(struct wireless_dev *wdev, u32 nlpid) |
026331c4 | 674 | { |
271733cf JB |
675 | struct wiphy *wiphy = wdev->wiphy; |
676 | struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); | |
2e161f78 | 677 | struct cfg80211_mgmt_registration *reg, *tmp; |
026331c4 | 678 | |
2e161f78 | 679 | spin_lock_bh(&wdev->mgmt_registrations_lock); |
026331c4 | 680 | |
2e161f78 | 681 | list_for_each_entry_safe(reg, tmp, &wdev->mgmt_registrations, list) { |
271733cf JB |
682 | if (reg->nlpid != nlpid) |
683 | continue; | |
684 | ||
685 | if (rdev->ops->mgmt_frame_register) { | |
686 | u16 frame_type = le16_to_cpu(reg->frame_type); | |
687 | ||
688 | rdev->ops->mgmt_frame_register(wiphy, wdev->netdev, | |
689 | frame_type, false); | |
026331c4 | 690 | } |
271733cf JB |
691 | |
692 | list_del(®->list); | |
693 | kfree(reg); | |
026331c4 JM |
694 | } |
695 | ||
2e161f78 | 696 | spin_unlock_bh(&wdev->mgmt_registrations_lock); |
28946da7 JB |
697 | |
698 | if (nlpid == wdev->ap_unexpected_nlpid) | |
699 | wdev->ap_unexpected_nlpid = 0; | |
026331c4 JM |
700 | } |
701 | ||
2e161f78 | 702 | void cfg80211_mlme_purge_registrations(struct wireless_dev *wdev) |
026331c4 | 703 | { |
2e161f78 | 704 | struct cfg80211_mgmt_registration *reg, *tmp; |
026331c4 | 705 | |
2e161f78 | 706 | spin_lock_bh(&wdev->mgmt_registrations_lock); |
026331c4 | 707 | |
2e161f78 | 708 | list_for_each_entry_safe(reg, tmp, &wdev->mgmt_registrations, list) { |
026331c4 JM |
709 | list_del(®->list); |
710 | kfree(reg); | |
711 | } | |
712 | ||
2e161f78 | 713 | spin_unlock_bh(&wdev->mgmt_registrations_lock); |
026331c4 JM |
714 | } |
715 | ||
2e161f78 JB |
716 | int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev, |
717 | struct net_device *dev, | |
f7ca38df | 718 | struct ieee80211_channel *chan, bool offchan, |
2e161f78 | 719 | enum nl80211_channel_type channel_type, |
f7ca38df | 720 | bool channel_type_valid, unsigned int wait, |
e9f935e3 | 721 | const u8 *buf, size_t len, bool no_cck, |
e247bd90 | 722 | bool dont_wait_for_ack, u64 *cookie) |
026331c4 JM |
723 | { |
724 | struct wireless_dev *wdev = dev->ieee80211_ptr; | |
725 | const struct ieee80211_mgmt *mgmt; | |
2e161f78 JB |
726 | u16 stype; |
727 | ||
728 | if (!wdev->wiphy->mgmt_stypes) | |
729 | return -EOPNOTSUPP; | |
026331c4 | 730 | |
2e161f78 | 731 | if (!rdev->ops->mgmt_tx) |
026331c4 | 732 | return -EOPNOTSUPP; |
2e161f78 | 733 | |
026331c4 JM |
734 | if (len < 24 + 1) |
735 | return -EINVAL; | |
736 | ||
737 | mgmt = (const struct ieee80211_mgmt *) buf; | |
2e161f78 JB |
738 | |
739 | if (!ieee80211_is_mgmt(mgmt->frame_control)) | |
026331c4 | 740 | return -EINVAL; |
2e161f78 JB |
741 | |
742 | stype = le16_to_cpu(mgmt->frame_control) & IEEE80211_FCTL_STYPE; | |
743 | if (!(wdev->wiphy->mgmt_stypes[wdev->iftype].tx & BIT(stype >> 4))) | |
744 | return -EINVAL; | |
745 | ||
746 | if (ieee80211_is_action(mgmt->frame_control) && | |
747 | mgmt->u.action.category != WLAN_CATEGORY_PUBLIC) { | |
663fcafd JB |
748 | int err = 0; |
749 | ||
fe100acd JB |
750 | wdev_lock(wdev); |
751 | ||
663fcafd JB |
752 | switch (wdev->iftype) { |
753 | case NL80211_IFTYPE_ADHOC: | |
754 | case NL80211_IFTYPE_STATION: | |
755 | case NL80211_IFTYPE_P2P_CLIENT: | |
756 | if (!wdev->current_bss) { | |
757 | err = -ENOTCONN; | |
758 | break; | |
759 | } | |
760 | ||
761 | if (memcmp(wdev->current_bss->pub.bssid, | |
762 | mgmt->bssid, ETH_ALEN)) { | |
763 | err = -ENOTCONN; | |
764 | break; | |
765 | } | |
766 | ||
767 | /* | |
768 | * check for IBSS DA must be done by driver as | |
769 | * cfg80211 doesn't track the stations | |
770 | */ | |
771 | if (wdev->iftype == NL80211_IFTYPE_ADHOC) | |
772 | break; | |
fe100acd | 773 | |
663fcafd JB |
774 | /* for station, check that DA is the AP */ |
775 | if (memcmp(wdev->current_bss->pub.bssid, | |
776 | mgmt->da, ETH_ALEN)) { | |
777 | err = -ENOTCONN; | |
778 | break; | |
779 | } | |
780 | break; | |
781 | case NL80211_IFTYPE_AP: | |
782 | case NL80211_IFTYPE_P2P_GO: | |
783 | case NL80211_IFTYPE_AP_VLAN: | |
784 | if (memcmp(mgmt->bssid, dev->dev_addr, ETH_ALEN)) | |
785 | err = -EINVAL; | |
786 | break; | |
0778a6a3 JC |
787 | case NL80211_IFTYPE_MESH_POINT: |
788 | if (memcmp(mgmt->sa, mgmt->bssid, ETH_ALEN)) { | |
789 | err = -EINVAL; | |
790 | break; | |
791 | } | |
792 | /* | |
793 | * check for mesh DA must be done by driver as | |
794 | * cfg80211 doesn't track the stations | |
795 | */ | |
796 | break; | |
663fcafd JB |
797 | default: |
798 | err = -EOPNOTSUPP; | |
799 | break; | |
800 | } | |
fe100acd | 801 | wdev_unlock(wdev); |
663fcafd JB |
802 | |
803 | if (err) | |
804 | return err; | |
026331c4 JM |
805 | } |
806 | ||
807 | if (memcmp(mgmt->sa, dev->dev_addr, ETH_ALEN) != 0) | |
808 | return -EINVAL; | |
809 | ||
810 | /* Transmit the Action frame as requested by user space */ | |
f7ca38df JB |
811 | return rdev->ops->mgmt_tx(&rdev->wiphy, dev, chan, offchan, |
812 | channel_type, channel_type_valid, | |
e247bd90 JB |
813 | wait, buf, len, no_cck, dont_wait_for_ack, |
814 | cookie); | |
026331c4 JM |
815 | } |
816 | ||
2e161f78 JB |
817 | bool cfg80211_rx_mgmt(struct net_device *dev, int freq, const u8 *buf, |
818 | size_t len, gfp_t gfp) | |
026331c4 JM |
819 | { |
820 | struct wireless_dev *wdev = dev->ieee80211_ptr; | |
821 | struct wiphy *wiphy = wdev->wiphy; | |
822 | struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); | |
2e161f78 JB |
823 | struct cfg80211_mgmt_registration *reg; |
824 | const struct ieee80211_txrx_stypes *stypes = | |
825 | &wiphy->mgmt_stypes[wdev->iftype]; | |
826 | struct ieee80211_mgmt *mgmt = (void *)buf; | |
827 | const u8 *data; | |
828 | int data_len; | |
026331c4 | 829 | bool result = false; |
2e161f78 JB |
830 | __le16 ftype = mgmt->frame_control & |
831 | cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE); | |
832 | u16 stype; | |
026331c4 | 833 | |
2e161f78 | 834 | stype = (le16_to_cpu(mgmt->frame_control) & IEEE80211_FCTL_STYPE) >> 4; |
026331c4 | 835 | |
2e161f78 JB |
836 | if (!(stypes->rx & BIT(stype))) |
837 | return false; | |
026331c4 | 838 | |
2e161f78 JB |
839 | data = buf + ieee80211_hdrlen(mgmt->frame_control); |
840 | data_len = len - ieee80211_hdrlen(mgmt->frame_control); | |
841 | ||
842 | spin_lock_bh(&wdev->mgmt_registrations_lock); | |
843 | ||
844 | list_for_each_entry(reg, &wdev->mgmt_registrations, list) { | |
845 | if (reg->frame_type != ftype) | |
846 | continue; | |
026331c4 | 847 | |
2e161f78 | 848 | if (reg->match_len > data_len) |
026331c4 JM |
849 | continue; |
850 | ||
2e161f78 | 851 | if (memcmp(reg->match, data, reg->match_len)) |
026331c4 JM |
852 | continue; |
853 | ||
854 | /* found match! */ | |
855 | ||
856 | /* Indicate the received Action frame to user space */ | |
2e161f78 JB |
857 | if (nl80211_send_mgmt(rdev, dev, reg->nlpid, freq, |
858 | buf, len, gfp)) | |
026331c4 JM |
859 | continue; |
860 | ||
861 | result = true; | |
862 | break; | |
863 | } | |
864 | ||
2e161f78 | 865 | spin_unlock_bh(&wdev->mgmt_registrations_lock); |
026331c4 JM |
866 | |
867 | return result; | |
868 | } | |
2e161f78 | 869 | EXPORT_SYMBOL(cfg80211_rx_mgmt); |
026331c4 | 870 | |
2e161f78 JB |
871 | void cfg80211_mgmt_tx_status(struct net_device *dev, u64 cookie, |
872 | const u8 *buf, size_t len, bool ack, gfp_t gfp) | |
026331c4 JM |
873 | { |
874 | struct wireless_dev *wdev = dev->ieee80211_ptr; | |
875 | struct wiphy *wiphy = wdev->wiphy; | |
876 | struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); | |
877 | ||
878 | /* Indicate TX status of the Action frame to user space */ | |
2e161f78 | 879 | nl80211_send_mgmt_tx_status(rdev, dev, cookie, buf, len, ack, gfp); |
026331c4 | 880 | } |
2e161f78 | 881 | EXPORT_SYMBOL(cfg80211_mgmt_tx_status); |
d6dc1a38 JO |
882 | |
883 | void cfg80211_cqm_rssi_notify(struct net_device *dev, | |
884 | enum nl80211_cqm_rssi_threshold_event rssi_event, | |
885 | gfp_t gfp) | |
886 | { | |
887 | struct wireless_dev *wdev = dev->ieee80211_ptr; | |
888 | struct wiphy *wiphy = wdev->wiphy; | |
889 | struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); | |
890 | ||
891 | /* Indicate roaming trigger event to user space */ | |
892 | nl80211_send_cqm_rssi_notify(rdev, dev, rssi_event, gfp); | |
893 | } | |
894 | EXPORT_SYMBOL(cfg80211_cqm_rssi_notify); | |
c063dbf5 JB |
895 | |
896 | void cfg80211_cqm_pktloss_notify(struct net_device *dev, | |
897 | const u8 *peer, u32 num_packets, gfp_t gfp) | |
898 | { | |
899 | struct wireless_dev *wdev = dev->ieee80211_ptr; | |
900 | struct wiphy *wiphy = wdev->wiphy; | |
901 | struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); | |
902 | ||
903 | /* Indicate roaming trigger event to user space */ | |
904 | nl80211_send_cqm_pktloss_notify(rdev, dev, peer, num_packets, gfp); | |
905 | } | |
906 | EXPORT_SYMBOL(cfg80211_cqm_pktloss_notify); | |
e5497d76 JB |
907 | |
908 | void cfg80211_gtk_rekey_notify(struct net_device *dev, const u8 *bssid, | |
909 | const u8 *replay_ctr, gfp_t gfp) | |
910 | { | |
911 | struct wireless_dev *wdev = dev->ieee80211_ptr; | |
912 | struct wiphy *wiphy = wdev->wiphy; | |
913 | struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); | |
914 | ||
915 | nl80211_gtk_rekey_notify(rdev, dev, bssid, replay_ctr, gfp); | |
916 | } | |
917 | EXPORT_SYMBOL(cfg80211_gtk_rekey_notify); | |
c9df56b4 JM |
918 | |
919 | void cfg80211_pmksa_candidate_notify(struct net_device *dev, int index, | |
920 | const u8 *bssid, bool preauth, gfp_t gfp) | |
921 | { | |
922 | struct wireless_dev *wdev = dev->ieee80211_ptr; | |
923 | struct wiphy *wiphy = wdev->wiphy; | |
924 | struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); | |
925 | ||
926 | nl80211_pmksa_candidate_notify(rdev, dev, index, bssid, preauth, gfp); | |
927 | } | |
928 | EXPORT_SYMBOL(cfg80211_pmksa_candidate_notify); | |
28946da7 JB |
929 | |
930 | bool cfg80211_rx_spurious_frame(struct net_device *dev, | |
931 | const u8 *addr, gfp_t gfp) | |
932 | { | |
933 | struct wireless_dev *wdev = dev->ieee80211_ptr; | |
934 | ||
935 | if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP && | |
936 | wdev->iftype != NL80211_IFTYPE_P2P_GO)) | |
937 | return false; | |
938 | ||
939 | return nl80211_unexpected_frame(dev, addr, gfp); | |
940 | } | |
941 | EXPORT_SYMBOL(cfg80211_rx_spurious_frame); | |
b92ab5d8 JB |
942 | |
943 | bool cfg80211_rx_unexpected_4addr_frame(struct net_device *dev, | |
944 | const u8 *addr, gfp_t gfp) | |
945 | { | |
946 | struct wireless_dev *wdev = dev->ieee80211_ptr; | |
947 | ||
948 | if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP && | |
949 | wdev->iftype != NL80211_IFTYPE_P2P_GO && | |
950 | wdev->iftype != NL80211_IFTYPE_AP_VLAN)) | |
951 | return false; | |
952 | ||
953 | return nl80211_unexpected_4addr_frame(dev, addr, gfp); | |
954 | } | |
955 | EXPORT_SYMBOL(cfg80211_rx_unexpected_4addr_frame); |