diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2009-09-14 13:37:28 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2009-09-14 13:37:28 -0400 |
commit | d7e9660ad9d5e0845f52848bce31bcf5cdcdea6b (patch) | |
tree | c6c67d145771187b194d79d603742b31090a59d6 /net/wireless/mlme.c | |
parent | b8cb48aae1b8c50b37dcb7710363aa69a7a0d9ca (diff) | |
parent | 13af7a6ea502fcdd4c0e3d7de6e332b102309491 (diff) |
Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next-2.6
* git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next-2.6: (1623 commits)
netxen: update copyright
netxen: fix tx timeout recovery
netxen: fix file firmware leak
netxen: improve pci memory access
netxen: change firmware write size
tg3: Fix return ring size breakage
netxen: build fix for INET=n
cdc-phonet: autoconfigure Phonet address
Phonet: back-end for autoconfigured addresses
Phonet: fix netlink address dump error handling
ipv6: Add IFA_F_DADFAILED flag
net: Add DEVTYPE support for Ethernet based devices
mv643xx_eth.c: remove unused txq_set_wrr()
ucc_geth: Fix hangs after switching from full to half duplex
ucc_geth: Rearrange some code to avoid forward declarations
phy/marvell: Make non-aneg speed/duplex forcing work for 88E1111 PHYs
drivers/net/phy: introduce missing kfree
drivers/net/wan: introduce missing kfree
net: force bridge module(s) to be GPL
Subject: [PATCH] appletalk: Fix skb leak when ipddp interface is not loaded
...
Fixed up trivial conflicts:
- arch/x86/include/asm/socket.h
converted to <asm-generic/socket.h> in the x86 tree. The generic
header has the same new #define's, so that works out fine.
- drivers/net/tun.c
fix conflict between 89f56d1e9 ("tun: reuse struct sock fields") that
switched over to using 'tun->socket.sk' instead of the redundantly
available (and thus removed) 'tun->sk', and 2b980dbd ("lsm: Add hooks
to the TUN driver") which added a new 'tun->sk' use.
Noted in 'next' by Stephen Rothwell.
Diffstat (limited to 'net/wireless/mlme.c')
-rw-r--r-- | net/wireless/mlme.c | 627 |
1 files changed, 602 insertions, 25 deletions
diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index 42184361a109..79d2eec54cec 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c | |||
@@ -8,75 +8,652 @@ | |||
8 | #include <linux/module.h> | 8 | #include <linux/module.h> |
9 | #include <linux/netdevice.h> | 9 | #include <linux/netdevice.h> |
10 | #include <linux/nl80211.h> | 10 | #include <linux/nl80211.h> |
11 | #include <linux/wireless.h> | ||
11 | #include <net/cfg80211.h> | 12 | #include <net/cfg80211.h> |
13 | #include <net/iw_handler.h> | ||
12 | #include "core.h" | 14 | #include "core.h" |
13 | #include "nl80211.h" | 15 | #include "nl80211.h" |
14 | 16 | ||
15 | void cfg80211_send_rx_auth(struct net_device *dev, const u8 *buf, size_t len) | 17 | void cfg80211_send_rx_auth(struct net_device *dev, const u8 *buf, size_t len) |
16 | { | 18 | { |
17 | struct wiphy *wiphy = dev->ieee80211_ptr->wiphy; | 19 | struct wireless_dev *wdev = dev->ieee80211_ptr; |
20 | struct wiphy *wiphy = wdev->wiphy; | ||
18 | struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); | 21 | struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); |
19 | nl80211_send_rx_auth(rdev, dev, buf, len); | 22 | struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf; |
23 | u8 *bssid = mgmt->bssid; | ||
24 | int i; | ||
25 | u16 status = le16_to_cpu(mgmt->u.auth.status_code); | ||
26 | bool done = false; | ||
27 | |||
28 | wdev_lock(wdev); | ||
29 | |||
30 | for (i = 0; i < MAX_AUTH_BSSES; i++) { | ||
31 | if (wdev->authtry_bsses[i] && | ||
32 | memcmp(wdev->authtry_bsses[i]->pub.bssid, bssid, | ||
33 | ETH_ALEN) == 0) { | ||
34 | if (status == WLAN_STATUS_SUCCESS) { | ||
35 | wdev->auth_bsses[i] = wdev->authtry_bsses[i]; | ||
36 | } else { | ||
37 | cfg80211_unhold_bss(wdev->authtry_bsses[i]); | ||
38 | cfg80211_put_bss(&wdev->authtry_bsses[i]->pub); | ||
39 | } | ||
40 | wdev->authtry_bsses[i] = NULL; | ||
41 | done = true; | ||
42 | break; | ||
43 | } | ||
44 | } | ||
45 | |||
46 | WARN_ON(!done); | ||
47 | |||
48 | nl80211_send_rx_auth(rdev, dev, buf, len, GFP_KERNEL); | ||
49 | cfg80211_sme_rx_auth(dev, buf, len); | ||
50 | |||
51 | wdev_unlock(wdev); | ||
20 | } | 52 | } |
21 | EXPORT_SYMBOL(cfg80211_send_rx_auth); | 53 | EXPORT_SYMBOL(cfg80211_send_rx_auth); |
22 | 54 | ||
23 | void cfg80211_send_rx_assoc(struct net_device *dev, const u8 *buf, size_t len) | 55 | void cfg80211_send_rx_assoc(struct net_device *dev, const u8 *buf, size_t len) |
24 | { | 56 | { |
25 | struct wiphy *wiphy = dev->ieee80211_ptr->wiphy; | 57 | u16 status_code; |
58 | struct wireless_dev *wdev = dev->ieee80211_ptr; | ||
59 | struct wiphy *wiphy = wdev->wiphy; | ||
26 | struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); | 60 | struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); |
27 | nl80211_send_rx_assoc(rdev, dev, buf, len); | 61 | struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf; |
62 | u8 *ie = mgmt->u.assoc_resp.variable; | ||
63 | int i, ieoffs = offsetof(struct ieee80211_mgmt, u.assoc_resp.variable); | ||
64 | struct cfg80211_internal_bss *bss = NULL; | ||
65 | |||
66 | wdev_lock(wdev); | ||
67 | |||
68 | status_code = le16_to_cpu(mgmt->u.assoc_resp.status_code); | ||
69 | |||
70 | /* | ||
71 | * This is a bit of a hack, we don't notify userspace of | ||
72 | * a (re-)association reply if we tried to send a reassoc | ||
73 | * and got a reject -- we only try again with an assoc | ||
74 | * frame instead of reassoc. | ||
75 | */ | ||
76 | if (status_code != WLAN_STATUS_SUCCESS && wdev->conn && | ||
77 | cfg80211_sme_failed_reassoc(wdev)) | ||
78 | goto out; | ||
79 | |||
80 | nl80211_send_rx_assoc(rdev, dev, buf, len, GFP_KERNEL); | ||
81 | |||
82 | if (status_code == WLAN_STATUS_SUCCESS) { | ||
83 | for (i = 0; i < MAX_AUTH_BSSES; i++) { | ||
84 | if (!wdev->auth_bsses[i]) | ||
85 | continue; | ||
86 | if (memcmp(wdev->auth_bsses[i]->pub.bssid, mgmt->bssid, | ||
87 | ETH_ALEN) == 0) { | ||
88 | bss = wdev->auth_bsses[i]; | ||
89 | wdev->auth_bsses[i] = NULL; | ||
90 | /* additional reference to drop hold */ | ||
91 | cfg80211_ref_bss(bss); | ||
92 | break; | ||
93 | } | ||
94 | } | ||
95 | |||
96 | WARN_ON(!bss); | ||
97 | } | ||
98 | |||
99 | if (!wdev->conn && wdev->sme_state == CFG80211_SME_IDLE) { | ||
100 | /* | ||
101 | * This is for the userspace SME, the CONNECTING | ||
102 | * state will be changed to CONNECTED by | ||
103 | * __cfg80211_connect_result() below. | ||
104 | */ | ||
105 | wdev->sme_state = CFG80211_SME_CONNECTING; | ||
106 | } | ||
107 | |||
108 | /* this consumes one bss reference (unless bss is NULL) */ | ||
109 | __cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, ie, len - ieoffs, | ||
110 | status_code, | ||
111 | status_code == WLAN_STATUS_SUCCESS, | ||
112 | bss ? &bss->pub : NULL); | ||
113 | /* drop hold now, and also reference acquired above */ | ||
114 | if (bss) { | ||
115 | cfg80211_unhold_bss(bss); | ||
116 | cfg80211_put_bss(&bss->pub); | ||
117 | } | ||
118 | |||
119 | out: | ||
120 | wdev_unlock(wdev); | ||
28 | } | 121 | } |
29 | EXPORT_SYMBOL(cfg80211_send_rx_assoc); | 122 | EXPORT_SYMBOL(cfg80211_send_rx_assoc); |
30 | 123 | ||
31 | void cfg80211_send_deauth(struct net_device *dev, const u8 *buf, size_t len) | 124 | static void __cfg80211_send_deauth(struct net_device *dev, |
125 | const u8 *buf, size_t len) | ||
32 | { | 126 | { |
33 | struct wiphy *wiphy = dev->ieee80211_ptr->wiphy; | 127 | struct wireless_dev *wdev = dev->ieee80211_ptr; |
128 | struct wiphy *wiphy = wdev->wiphy; | ||
34 | struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); | 129 | struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); |
35 | nl80211_send_deauth(rdev, dev, buf, len); | 130 | struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf; |
131 | const u8 *bssid = mgmt->bssid; | ||
132 | int i; | ||
133 | bool done = false; | ||
134 | |||
135 | ASSERT_WDEV_LOCK(wdev); | ||
136 | |||
137 | nl80211_send_deauth(rdev, dev, buf, len, GFP_KERNEL); | ||
138 | |||
139 | if (wdev->current_bss && | ||
140 | memcmp(wdev->current_bss->pub.bssid, bssid, ETH_ALEN) == 0) { | ||
141 | done = true; | ||
142 | cfg80211_unhold_bss(wdev->current_bss); | ||
143 | cfg80211_put_bss(&wdev->current_bss->pub); | ||
144 | wdev->current_bss = NULL; | ||
145 | } else for (i = 0; i < MAX_AUTH_BSSES; i++) { | ||
146 | if (wdev->auth_bsses[i] && | ||
147 | memcmp(wdev->auth_bsses[i]->pub.bssid, bssid, ETH_ALEN) == 0) { | ||
148 | cfg80211_unhold_bss(wdev->auth_bsses[i]); | ||
149 | cfg80211_put_bss(&wdev->auth_bsses[i]->pub); | ||
150 | wdev->auth_bsses[i] = NULL; | ||
151 | done = true; | ||
152 | break; | ||
153 | } | ||
154 | if (wdev->authtry_bsses[i] && | ||
155 | memcmp(wdev->authtry_bsses[i]->pub.bssid, bssid, ETH_ALEN) == 0) { | ||
156 | cfg80211_unhold_bss(wdev->authtry_bsses[i]); | ||
157 | cfg80211_put_bss(&wdev->authtry_bsses[i]->pub); | ||
158 | wdev->authtry_bsses[i] = NULL; | ||
159 | done = true; | ||
160 | break; | ||
161 | } | ||
162 | } | ||
163 | |||
164 | WARN_ON(!done); | ||
165 | |||
166 | if (wdev->sme_state == CFG80211_SME_CONNECTED) { | ||
167 | u16 reason_code; | ||
168 | bool from_ap; | ||
169 | |||
170 | reason_code = le16_to_cpu(mgmt->u.deauth.reason_code); | ||
171 | |||
172 | from_ap = memcmp(mgmt->sa, dev->dev_addr, ETH_ALEN) != 0; | ||
173 | __cfg80211_disconnected(dev, NULL, 0, reason_code, from_ap); | ||
174 | } else if (wdev->sme_state == CFG80211_SME_CONNECTING) { | ||
175 | __cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, NULL, 0, | ||
176 | WLAN_STATUS_UNSPECIFIED_FAILURE, | ||
177 | false, NULL); | ||
178 | } | ||
179 | } | ||
180 | |||
181 | |||
182 | void cfg80211_send_deauth(struct net_device *dev, const u8 *buf, size_t len, | ||
183 | void *cookie) | ||
184 | { | ||
185 | struct wireless_dev *wdev = dev->ieee80211_ptr; | ||
186 | |||
187 | BUG_ON(cookie && wdev != cookie); | ||
188 | |||
189 | if (cookie) { | ||
190 | /* called within callback */ | ||
191 | __cfg80211_send_deauth(dev, buf, len); | ||
192 | } else { | ||
193 | wdev_lock(wdev); | ||
194 | __cfg80211_send_deauth(dev, buf, len); | ||
195 | wdev_unlock(wdev); | ||
196 | } | ||
36 | } | 197 | } |
37 | EXPORT_SYMBOL(cfg80211_send_deauth); | 198 | EXPORT_SYMBOL(cfg80211_send_deauth); |
38 | 199 | ||
39 | void cfg80211_send_disassoc(struct net_device *dev, const u8 *buf, size_t len) | 200 | static void __cfg80211_send_disassoc(struct net_device *dev, |
201 | const u8 *buf, size_t len) | ||
40 | { | 202 | { |
41 | struct wiphy *wiphy = dev->ieee80211_ptr->wiphy; | 203 | struct wireless_dev *wdev = dev->ieee80211_ptr; |
204 | struct wiphy *wiphy = wdev->wiphy; | ||
42 | struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); | 205 | struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); |
43 | nl80211_send_disassoc(rdev, dev, buf, len); | 206 | struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf; |
207 | const u8 *bssid = mgmt->bssid; | ||
208 | int i; | ||
209 | u16 reason_code; | ||
210 | bool from_ap; | ||
211 | bool done = false; | ||
212 | |||
213 | ASSERT_WDEV_LOCK(wdev); | ||
214 | |||
215 | nl80211_send_disassoc(rdev, dev, buf, len, GFP_KERNEL); | ||
216 | |||
217 | if (wdev->sme_state != CFG80211_SME_CONNECTED) | ||
218 | return; | ||
219 | |||
220 | if (wdev->current_bss && | ||
221 | memcmp(wdev->current_bss->pub.bssid, bssid, ETH_ALEN) == 0) { | ||
222 | for (i = 0; i < MAX_AUTH_BSSES; i++) { | ||
223 | if (wdev->authtry_bsses[i] || wdev->auth_bsses[i]) | ||
224 | continue; | ||
225 | wdev->auth_bsses[i] = wdev->current_bss; | ||
226 | wdev->current_bss = NULL; | ||
227 | done = true; | ||
228 | cfg80211_sme_disassoc(dev, i); | ||
229 | break; | ||
230 | } | ||
231 | WARN_ON(!done); | ||
232 | } else | ||
233 | WARN_ON(1); | ||
234 | |||
235 | |||
236 | reason_code = le16_to_cpu(mgmt->u.disassoc.reason_code); | ||
237 | |||
238 | from_ap = memcmp(mgmt->sa, dev->dev_addr, ETH_ALEN) != 0; | ||
239 | __cfg80211_disconnected(dev, NULL, 0, reason_code, from_ap); | ||
44 | } | 240 | } |
45 | EXPORT_SYMBOL(cfg80211_send_disassoc); | ||
46 | 241 | ||
47 | static void cfg80211_wext_disconnected(struct net_device *dev) | 242 | void cfg80211_send_disassoc(struct net_device *dev, const u8 *buf, size_t len, |
243 | void *cookie) | ||
48 | { | 244 | { |
49 | #ifdef CONFIG_WIRELESS_EXT | 245 | struct wireless_dev *wdev = dev->ieee80211_ptr; |
50 | union iwreq_data wrqu; | 246 | |
51 | memset(&wrqu, 0, sizeof(wrqu)); | 247 | BUG_ON(cookie && wdev != cookie); |
52 | wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL); | 248 | |
53 | #endif | 249 | if (cookie) { |
250 | /* called within callback */ | ||
251 | __cfg80211_send_disassoc(dev, buf, len); | ||
252 | } else { | ||
253 | wdev_lock(wdev); | ||
254 | __cfg80211_send_disassoc(dev, buf, len); | ||
255 | wdev_unlock(wdev); | ||
256 | } | ||
54 | } | 257 | } |
258 | EXPORT_SYMBOL(cfg80211_send_disassoc); | ||
55 | 259 | ||
56 | void cfg80211_send_auth_timeout(struct net_device *dev, const u8 *addr) | 260 | void cfg80211_send_auth_timeout(struct net_device *dev, const u8 *addr) |
57 | { | 261 | { |
58 | struct wiphy *wiphy = dev->ieee80211_ptr->wiphy; | 262 | struct wireless_dev *wdev = dev->ieee80211_ptr; |
263 | struct wiphy *wiphy = wdev->wiphy; | ||
59 | struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); | 264 | struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); |
60 | nl80211_send_auth_timeout(rdev, dev, addr); | 265 | int i; |
61 | cfg80211_wext_disconnected(dev); | 266 | bool done = false; |
267 | |||
268 | wdev_lock(wdev); | ||
269 | |||
270 | nl80211_send_auth_timeout(rdev, dev, addr, GFP_KERNEL); | ||
271 | if (wdev->sme_state == CFG80211_SME_CONNECTING) | ||
272 | __cfg80211_connect_result(dev, addr, NULL, 0, NULL, 0, | ||
273 | WLAN_STATUS_UNSPECIFIED_FAILURE, | ||
274 | false, NULL); | ||
275 | |||
276 | for (i = 0; addr && i < MAX_AUTH_BSSES; i++) { | ||
277 | if (wdev->authtry_bsses[i] && | ||
278 | memcmp(wdev->authtry_bsses[i]->pub.bssid, | ||
279 | addr, ETH_ALEN) == 0) { | ||
280 | cfg80211_unhold_bss(wdev->authtry_bsses[i]); | ||
281 | cfg80211_put_bss(&wdev->authtry_bsses[i]->pub); | ||
282 | wdev->authtry_bsses[i] = NULL; | ||
283 | done = true; | ||
284 | break; | ||
285 | } | ||
286 | } | ||
287 | |||
288 | WARN_ON(!done); | ||
289 | |||
290 | wdev_unlock(wdev); | ||
62 | } | 291 | } |
63 | EXPORT_SYMBOL(cfg80211_send_auth_timeout); | 292 | EXPORT_SYMBOL(cfg80211_send_auth_timeout); |
64 | 293 | ||
65 | void cfg80211_send_assoc_timeout(struct net_device *dev, const u8 *addr) | 294 | void cfg80211_send_assoc_timeout(struct net_device *dev, const u8 *addr) |
66 | { | 295 | { |
67 | struct wiphy *wiphy = dev->ieee80211_ptr->wiphy; | 296 | struct wireless_dev *wdev = dev->ieee80211_ptr; |
297 | struct wiphy *wiphy = wdev->wiphy; | ||
68 | struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); | 298 | struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); |
69 | nl80211_send_assoc_timeout(rdev, dev, addr); | 299 | int i; |
70 | cfg80211_wext_disconnected(dev); | 300 | bool done = false; |
301 | |||
302 | wdev_lock(wdev); | ||
303 | |||
304 | nl80211_send_assoc_timeout(rdev, dev, addr, GFP_KERNEL); | ||
305 | if (wdev->sme_state == CFG80211_SME_CONNECTING) | ||
306 | __cfg80211_connect_result(dev, addr, NULL, 0, NULL, 0, | ||
307 | WLAN_STATUS_UNSPECIFIED_FAILURE, | ||
308 | false, NULL); | ||
309 | |||
310 | for (i = 0; addr && i < MAX_AUTH_BSSES; i++) { | ||
311 | if (wdev->auth_bsses[i] && | ||
312 | memcmp(wdev->auth_bsses[i]->pub.bssid, | ||
313 | addr, ETH_ALEN) == 0) { | ||
314 | cfg80211_unhold_bss(wdev->auth_bsses[i]); | ||
315 | cfg80211_put_bss(&wdev->auth_bsses[i]->pub); | ||
316 | wdev->auth_bsses[i] = NULL; | ||
317 | done = true; | ||
318 | break; | ||
319 | } | ||
320 | } | ||
321 | |||
322 | WARN_ON(!done); | ||
323 | |||
324 | wdev_unlock(wdev); | ||
71 | } | 325 | } |
72 | EXPORT_SYMBOL(cfg80211_send_assoc_timeout); | 326 | EXPORT_SYMBOL(cfg80211_send_assoc_timeout); |
73 | 327 | ||
74 | void cfg80211_michael_mic_failure(struct net_device *dev, const u8 *addr, | 328 | void cfg80211_michael_mic_failure(struct net_device *dev, const u8 *addr, |
75 | enum nl80211_key_type key_type, int key_id, | 329 | enum nl80211_key_type key_type, int key_id, |
76 | const u8 *tsc) | 330 | const u8 *tsc, gfp_t gfp) |
77 | { | 331 | { |
78 | struct wiphy *wiphy = dev->ieee80211_ptr->wiphy; | 332 | struct wiphy *wiphy = dev->ieee80211_ptr->wiphy; |
79 | struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); | 333 | struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); |
80 | nl80211_michael_mic_failure(rdev, dev, addr, key_type, key_id, tsc); | 334 | #ifdef CONFIG_WIRELESS_EXT |
335 | union iwreq_data wrqu; | ||
336 | char *buf = kmalloc(128, gfp); | ||
337 | |||
338 | if (buf) { | ||
339 | sprintf(buf, "MLME-MICHAELMICFAILURE.indication(" | ||
340 | "keyid=%d %scast addr=%pM)", key_id, | ||
341 | key_type == NL80211_KEYTYPE_GROUP ? "broad" : "uni", | ||
342 | addr); | ||
343 | memset(&wrqu, 0, sizeof(wrqu)); | ||
344 | wrqu.data.length = strlen(buf); | ||
345 | wireless_send_event(dev, IWEVCUSTOM, &wrqu, buf); | ||
346 | kfree(buf); | ||
347 | } | ||
348 | #endif | ||
349 | |||
350 | nl80211_michael_mic_failure(rdev, dev, addr, key_type, key_id, tsc, gfp); | ||
81 | } | 351 | } |
82 | EXPORT_SYMBOL(cfg80211_michael_mic_failure); | 352 | EXPORT_SYMBOL(cfg80211_michael_mic_failure); |
353 | |||
354 | /* some MLME handling for userspace SME */ | ||
355 | int __cfg80211_mlme_auth(struct cfg80211_registered_device *rdev, | ||
356 | struct net_device *dev, | ||
357 | struct ieee80211_channel *chan, | ||
358 | enum nl80211_auth_type auth_type, | ||
359 | const u8 *bssid, | ||
360 | const u8 *ssid, int ssid_len, | ||
361 | const u8 *ie, int ie_len, | ||
362 | const u8 *key, int key_len, int key_idx) | ||
363 | { | ||
364 | struct wireless_dev *wdev = dev->ieee80211_ptr; | ||
365 | struct cfg80211_auth_request req; | ||
366 | struct cfg80211_internal_bss *bss; | ||
367 | int i, err, slot = -1, nfree = 0; | ||
368 | |||
369 | ASSERT_WDEV_LOCK(wdev); | ||
370 | |||
371 | if (auth_type == NL80211_AUTHTYPE_SHARED_KEY) | ||
372 | if (!key || !key_len || key_idx < 0 || key_idx > 4) | ||
373 | return -EINVAL; | ||
374 | |||
375 | if (wdev->current_bss && | ||
376 | memcmp(bssid, wdev->current_bss->pub.bssid, ETH_ALEN) == 0) | ||
377 | return -EALREADY; | ||
378 | |||
379 | for (i = 0; i < MAX_AUTH_BSSES; i++) { | ||
380 | if (wdev->authtry_bsses[i] && | ||
381 | memcmp(bssid, wdev->authtry_bsses[i]->pub.bssid, | ||
382 | ETH_ALEN) == 0) | ||
383 | return -EALREADY; | ||
384 | if (wdev->auth_bsses[i] && | ||
385 | memcmp(bssid, wdev->auth_bsses[i]->pub.bssid, | ||
386 | ETH_ALEN) == 0) | ||
387 | return -EALREADY; | ||
388 | } | ||
389 | |||
390 | memset(&req, 0, sizeof(req)); | ||
391 | |||
392 | req.ie = ie; | ||
393 | req.ie_len = ie_len; | ||
394 | req.auth_type = auth_type; | ||
395 | req.bss = cfg80211_get_bss(&rdev->wiphy, chan, bssid, ssid, ssid_len, | ||
396 | WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS); | ||
397 | req.key = key; | ||
398 | req.key_len = key_len; | ||
399 | req.key_idx = key_idx; | ||
400 | if (!req.bss) | ||
401 | return -ENOENT; | ||
402 | |||
403 | bss = bss_from_pub(req.bss); | ||
404 | |||
405 | for (i = 0; i < MAX_AUTH_BSSES; i++) { | ||
406 | if (!wdev->auth_bsses[i] && !wdev->authtry_bsses[i]) { | ||
407 | slot = i; | ||
408 | nfree++; | ||
409 | } | ||
410 | } | ||
411 | |||
412 | /* we need one free slot for disassoc and one for this auth */ | ||
413 | if (nfree < 2) { | ||
414 | err = -ENOSPC; | ||
415 | goto out; | ||
416 | } | ||
417 | |||
418 | wdev->authtry_bsses[slot] = bss; | ||
419 | cfg80211_hold_bss(bss); | ||
420 | |||
421 | err = rdev->ops->auth(&rdev->wiphy, dev, &req); | ||
422 | if (err) { | ||
423 | wdev->authtry_bsses[slot] = NULL; | ||
424 | cfg80211_unhold_bss(bss); | ||
425 | } | ||
426 | |||
427 | out: | ||
428 | if (err) | ||
429 | cfg80211_put_bss(req.bss); | ||
430 | return err; | ||
431 | } | ||
432 | |||
433 | int cfg80211_mlme_auth(struct cfg80211_registered_device *rdev, | ||
434 | struct net_device *dev, struct ieee80211_channel *chan, | ||
435 | enum nl80211_auth_type auth_type, const u8 *bssid, | ||
436 | const u8 *ssid, int ssid_len, | ||
437 | const u8 *ie, int ie_len, | ||
438 | const u8 *key, int key_len, int key_idx) | ||
439 | { | ||
440 | int err; | ||
441 | |||
442 | wdev_lock(dev->ieee80211_ptr); | ||
443 | err = __cfg80211_mlme_auth(rdev, dev, chan, auth_type, bssid, | ||
444 | ssid, ssid_len, ie, ie_len, | ||
445 | key, key_len, key_idx); | ||
446 | wdev_unlock(dev->ieee80211_ptr); | ||
447 | |||
448 | return err; | ||
449 | } | ||
450 | |||
451 | int __cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev, | ||
452 | struct net_device *dev, | ||
453 | struct ieee80211_channel *chan, | ||
454 | const u8 *bssid, const u8 *prev_bssid, | ||
455 | const u8 *ssid, int ssid_len, | ||
456 | const u8 *ie, int ie_len, bool use_mfp, | ||
457 | struct cfg80211_crypto_settings *crypt) | ||
458 | { | ||
459 | struct wireless_dev *wdev = dev->ieee80211_ptr; | ||
460 | struct cfg80211_assoc_request req; | ||
461 | struct cfg80211_internal_bss *bss; | ||
462 | int i, err, slot = -1; | ||
463 | |||
464 | ASSERT_WDEV_LOCK(wdev); | ||
465 | |||
466 | memset(&req, 0, sizeof(req)); | ||
467 | |||
468 | if (wdev->current_bss) | ||
469 | return -EALREADY; | ||
470 | |||
471 | req.ie = ie; | ||
472 | req.ie_len = ie_len; | ||
473 | memcpy(&req.crypto, crypt, sizeof(req.crypto)); | ||
474 | req.use_mfp = use_mfp; | ||
475 | req.prev_bssid = prev_bssid; | ||
476 | req.bss = cfg80211_get_bss(&rdev->wiphy, chan, bssid, ssid, ssid_len, | ||
477 | WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS); | ||
478 | if (!req.bss) | ||
479 | return -ENOENT; | ||
480 | |||
481 | bss = bss_from_pub(req.bss); | ||
482 | |||
483 | for (i = 0; i < MAX_AUTH_BSSES; i++) { | ||
484 | if (bss == wdev->auth_bsses[i]) { | ||
485 | slot = i; | ||
486 | break; | ||
487 | } | ||
488 | } | ||
489 | |||
490 | if (slot < 0) { | ||
491 | err = -ENOTCONN; | ||
492 | goto out; | ||
493 | } | ||
494 | |||
495 | err = rdev->ops->assoc(&rdev->wiphy, dev, &req); | ||
496 | out: | ||
497 | /* still a reference in wdev->auth_bsses[slot] */ | ||
498 | cfg80211_put_bss(req.bss); | ||
499 | return err; | ||
500 | } | ||
501 | |||
502 | int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev, | ||
503 | struct net_device *dev, | ||
504 | struct ieee80211_channel *chan, | ||
505 | const u8 *bssid, const u8 *prev_bssid, | ||
506 | const u8 *ssid, int ssid_len, | ||
507 | const u8 *ie, int ie_len, bool use_mfp, | ||
508 | struct cfg80211_crypto_settings *crypt) | ||
509 | { | ||
510 | struct wireless_dev *wdev = dev->ieee80211_ptr; | ||
511 | int err; | ||
512 | |||
513 | wdev_lock(wdev); | ||
514 | err = __cfg80211_mlme_assoc(rdev, dev, chan, bssid, prev_bssid, | ||
515 | ssid, ssid_len, ie, ie_len, use_mfp, crypt); | ||
516 | wdev_unlock(wdev); | ||
517 | |||
518 | return err; | ||
519 | } | ||
520 | |||
521 | int __cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev, | ||
522 | struct net_device *dev, const u8 *bssid, | ||
523 | const u8 *ie, int ie_len, u16 reason) | ||
524 | { | ||
525 | struct wireless_dev *wdev = dev->ieee80211_ptr; | ||
526 | struct cfg80211_deauth_request req; | ||
527 | int i; | ||
528 | |||
529 | ASSERT_WDEV_LOCK(wdev); | ||
530 | |||
531 | memset(&req, 0, sizeof(req)); | ||
532 | req.reason_code = reason; | ||
533 | req.ie = ie; | ||
534 | req.ie_len = ie_len; | ||
535 | if (wdev->current_bss && | ||
536 | memcmp(wdev->current_bss->pub.bssid, bssid, ETH_ALEN) == 0) { | ||
537 | req.bss = &wdev->current_bss->pub; | ||
538 | } else for (i = 0; i < MAX_AUTH_BSSES; i++) { | ||
539 | if (wdev->auth_bsses[i] && | ||
540 | memcmp(bssid, wdev->auth_bsses[i]->pub.bssid, ETH_ALEN) == 0) { | ||
541 | req.bss = &wdev->auth_bsses[i]->pub; | ||
542 | break; | ||
543 | } | ||
544 | if (wdev->authtry_bsses[i] && | ||
545 | memcmp(bssid, wdev->authtry_bsses[i]->pub.bssid, ETH_ALEN) == 0) { | ||
546 | req.bss = &wdev->authtry_bsses[i]->pub; | ||
547 | break; | ||
548 | } | ||
549 | } | ||
550 | |||
551 | if (!req.bss) | ||
552 | return -ENOTCONN; | ||
553 | |||
554 | return rdev->ops->deauth(&rdev->wiphy, dev, &req, wdev); | ||
555 | } | ||
556 | |||
557 | int cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev, | ||
558 | struct net_device *dev, const u8 *bssid, | ||
559 | const u8 *ie, int ie_len, u16 reason) | ||
560 | { | ||
561 | struct wireless_dev *wdev = dev->ieee80211_ptr; | ||
562 | int err; | ||
563 | |||
564 | wdev_lock(wdev); | ||
565 | err = __cfg80211_mlme_deauth(rdev, dev, bssid, ie, ie_len, reason); | ||
566 | wdev_unlock(wdev); | ||
567 | |||
568 | return err; | ||
569 | } | ||
570 | |||
571 | static int __cfg80211_mlme_disassoc(struct cfg80211_registered_device *rdev, | ||
572 | struct net_device *dev, const u8 *bssid, | ||
573 | const u8 *ie, int ie_len, u16 reason) | ||
574 | { | ||
575 | struct wireless_dev *wdev = dev->ieee80211_ptr; | ||
576 | struct cfg80211_disassoc_request req; | ||
577 | |||
578 | ASSERT_WDEV_LOCK(wdev); | ||
579 | |||
580 | if (wdev->sme_state != CFG80211_SME_CONNECTED) | ||
581 | return -ENOTCONN; | ||
582 | |||
583 | if (WARN_ON(!wdev->current_bss)) | ||
584 | return -ENOTCONN; | ||
585 | |||
586 | memset(&req, 0, sizeof(req)); | ||
587 | req.reason_code = reason; | ||
588 | req.ie = ie; | ||
589 | req.ie_len = ie_len; | ||
590 | if (memcmp(wdev->current_bss->pub.bssid, bssid, ETH_ALEN) == 0) | ||
591 | req.bss = &wdev->current_bss->pub; | ||
592 | else | ||
593 | return -ENOTCONN; | ||
594 | |||
595 | return rdev->ops->disassoc(&rdev->wiphy, dev, &req, wdev); | ||
596 | } | ||
597 | |||
598 | int cfg80211_mlme_disassoc(struct cfg80211_registered_device *rdev, | ||
599 | struct net_device *dev, const u8 *bssid, | ||
600 | const u8 *ie, int ie_len, u16 reason) | ||
601 | { | ||
602 | struct wireless_dev *wdev = dev->ieee80211_ptr; | ||
603 | int err; | ||
604 | |||
605 | wdev_lock(wdev); | ||
606 | err = __cfg80211_mlme_disassoc(rdev, dev, bssid, ie, ie_len, reason); | ||
607 | wdev_unlock(wdev); | ||
608 | |||
609 | return err; | ||
610 | } | ||
611 | |||
612 | void cfg80211_mlme_down(struct cfg80211_registered_device *rdev, | ||
613 | struct net_device *dev) | ||
614 | { | ||
615 | struct wireless_dev *wdev = dev->ieee80211_ptr; | ||
616 | struct cfg80211_deauth_request req; | ||
617 | int i; | ||
618 | |||
619 | ASSERT_WDEV_LOCK(wdev); | ||
620 | |||
621 | if (!rdev->ops->deauth) | ||
622 | return; | ||
623 | |||
624 | memset(&req, 0, sizeof(req)); | ||
625 | req.reason_code = WLAN_REASON_DEAUTH_LEAVING; | ||
626 | req.ie = NULL; | ||
627 | req.ie_len = 0; | ||
628 | |||
629 | if (wdev->current_bss) { | ||
630 | req.bss = &wdev->current_bss->pub; | ||
631 | rdev->ops->deauth(&rdev->wiphy, dev, &req, wdev); | ||
632 | if (wdev->current_bss) { | ||
633 | cfg80211_unhold_bss(wdev->current_bss); | ||
634 | cfg80211_put_bss(&wdev->current_bss->pub); | ||
635 | wdev->current_bss = NULL; | ||
636 | } | ||
637 | } | ||
638 | |||
639 | for (i = 0; i < MAX_AUTH_BSSES; i++) { | ||
640 | if (wdev->auth_bsses[i]) { | ||
641 | req.bss = &wdev->auth_bsses[i]->pub; | ||
642 | rdev->ops->deauth(&rdev->wiphy, dev, &req, wdev); | ||
643 | if (wdev->auth_bsses[i]) { | ||
644 | cfg80211_unhold_bss(wdev->auth_bsses[i]); | ||
645 | cfg80211_put_bss(&wdev->auth_bsses[i]->pub); | ||
646 | wdev->auth_bsses[i] = NULL; | ||
647 | } | ||
648 | } | ||
649 | if (wdev->authtry_bsses[i]) { | ||
650 | req.bss = &wdev->authtry_bsses[i]->pub; | ||
651 | rdev->ops->deauth(&rdev->wiphy, dev, &req, wdev); | ||
652 | if (wdev->authtry_bsses[i]) { | ||
653 | cfg80211_unhold_bss(wdev->authtry_bsses[i]); | ||
654 | cfg80211_put_bss(&wdev->authtry_bsses[i]->pub); | ||
655 | wdev->authtry_bsses[i] = NULL; | ||
656 | } | ||
657 | } | ||
658 | } | ||
659 | } | ||