aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJohannes Berg <johannes@sipsolutions.net>2009-08-07 11:22:35 -0400
committerJohn W. Linville <linville@tuxdriver.com>2009-08-14 09:13:42 -0400
commit59bbb6f7574bc693ed8313b98eac641116c95b94 (patch)
treeda24ed15c5e375782e79b3dab7022d2100a7987a
parentf26b32ed4bd5780855a79bb17fb1a431fa867dad (diff)
cfg80211: validate channel settings across interfaces
Currently, there's a problem that affects regulatory enforcement and connection stability, in that it is possible to switch the channel while connected to a network or joined to an IBSS. The problem comes from the fact that we only validate the channel against the current interface's type, not against any other interface. Thus, you have any type of interface up, additionally bring up a monitor mode interface and switch the channel on the monitor. This will obviously also switch the channel on the other interface. The problem now is that if you do that while sending beacons for IBSS mode, you can switch to a disabled channel or a channel that doesn't allow beaconing. Combined with a managed mode interface connected to an AP instead of an IBSS interface, you can easily break the connection that way. To fix this, this patch validates any channel change with all available interfaces, and disallows such changes on secondary interfaces if another interface is connected to an AP or joined to an IBSS. Signed-off-by: Johannes Berg <johannes@sipsolutions.net> Signed-off-by: John W. Linville <linville@tuxdriver.com>
-rw-r--r--net/wireless/Makefile3
-rw-r--r--net/wireless/chan.c88
-rw-r--r--net/wireless/core.h6
-rw-r--r--net/wireless/ibss.c61
-rw-r--r--net/wireless/nl80211.c54
-rw-r--r--net/wireless/sme.c9
-rw-r--r--net/wireless/wext-compat.c55
-rw-r--r--net/wireless/wext-compat.h3
-rw-r--r--net/wireless/wext-sme.c65
9 files changed, 213 insertions, 131 deletions
diff --git a/net/wireless/Makefile b/net/wireless/Makefile
index d74cc77fa57a..3ecaa9179977 100644
--- a/net/wireless/Makefile
+++ b/net/wireless/Makefile
@@ -5,7 +5,8 @@ obj-$(CONFIG_LIB80211_CRYPT_WEP) += lib80211_crypt_wep.o
5obj-$(CONFIG_LIB80211_CRYPT_CCMP) += lib80211_crypt_ccmp.o 5obj-$(CONFIG_LIB80211_CRYPT_CCMP) += lib80211_crypt_ccmp.o
6obj-$(CONFIG_LIB80211_CRYPT_TKIP) += lib80211_crypt_tkip.o 6obj-$(CONFIG_LIB80211_CRYPT_TKIP) += lib80211_crypt_tkip.o
7 7
8cfg80211-y += core.o sysfs.o radiotap.o util.o reg.o scan.o nl80211.o mlme.o ibss.o sme.o 8cfg80211-y += core.o sysfs.o radiotap.o util.o reg.o scan.o nl80211.o
9cfg80211-y += mlme.o ibss.o sme.o chan.o
9cfg80211-$(CONFIG_CFG80211_DEBUGFS) += debugfs.o 10cfg80211-$(CONFIG_CFG80211_DEBUGFS) += debugfs.o
10cfg80211-$(CONFIG_WIRELESS_EXT) += wext-compat.o wext-sme.o 11cfg80211-$(CONFIG_WIRELESS_EXT) += wext-compat.o wext-sme.o
11 12
diff --git a/net/wireless/chan.c b/net/wireless/chan.c
new file mode 100644
index 000000000000..bc00c9a06b3d
--- /dev/null
+++ b/net/wireless/chan.c
@@ -0,0 +1,88 @@
1/*
2 * This file contains helper code to handle channel
3 * settings and keeping track of what is possible at
4 * any point in time.
5 *
6 * Copyright 2009 Johannes Berg <johannes@sipsolutions.net>
7 */
8
9#include <net/cfg80211.h>
10#include "core.h"
11
12struct ieee80211_channel *
13rdev_fixed_channel(struct cfg80211_registered_device *rdev,
14 struct wireless_dev *for_wdev)
15{
16 struct wireless_dev *wdev;
17 struct ieee80211_channel *result = NULL;
18
19 WARN_ON(!mutex_is_locked(&rdev->devlist_mtx));
20
21 list_for_each_entry(wdev, &rdev->netdev_list, list) {
22 if (wdev == for_wdev)
23 continue;
24
25 /*
26 * Lock manually to tell lockdep about allowed
27 * nesting here if for_wdev->mtx is held already.
28 * This is ok as it's all under the rdev devlist
29 * mutex and as such can only be done once at any
30 * given time.
31 */
32 mutex_lock_nested(&wdev->mtx, SINGLE_DEPTH_NESTING);
33 if (wdev->current_bss)
34 result = wdev->current_bss->pub.channel;
35 wdev_unlock(wdev);
36
37 if (result)
38 break;
39 }
40
41 return result;
42}
43
44int rdev_set_freq(struct cfg80211_registered_device *rdev,
45 int freq, enum nl80211_channel_type channel_type)
46{
47 struct ieee80211_channel *chan;
48 struct ieee80211_sta_ht_cap *ht_cap;
49 int result;
50
51 if (rdev_fixed_channel(rdev, NULL))
52 return -EBUSY;
53
54 if (!rdev->ops->set_channel)
55 return -EOPNOTSUPP;
56
57 chan = ieee80211_get_channel(&rdev->wiphy, freq);
58
59 /* Primary channel not allowed */
60 if (!chan || chan->flags & IEEE80211_CHAN_DISABLED)
61 return -EINVAL;
62
63 if (channel_type == NL80211_CHAN_HT40MINUS &&
64 chan->flags & IEEE80211_CHAN_NO_HT40MINUS)
65 return -EINVAL;
66 else if (channel_type == NL80211_CHAN_HT40PLUS &&
67 chan->flags & IEEE80211_CHAN_NO_HT40PLUS)
68 return -EINVAL;
69
70 ht_cap = &rdev->wiphy.bands[chan->band]->ht_cap;
71
72 if (channel_type != NL80211_CHAN_NO_HT) {
73 if (!ht_cap->ht_supported)
74 return -EINVAL;
75
76 if (!(ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) ||
77 ht_cap->cap & IEEE80211_HT_CAP_40MHZ_INTOLERANT)
78 return -EINVAL;
79 }
80
81 result = rdev->ops->set_channel(&rdev->wiphy, chan, channel_type);
82 if (result)
83 return result;
84
85 rdev->channel = chan;
86
87 return 0;
88}
diff --git a/net/wireless/core.h b/net/wireless/core.h
index 325c17e6198c..5696b95af9be 100644
--- a/net/wireless/core.h
+++ b/net/wireless/core.h
@@ -366,4 +366,10 @@ void cfg80211_sme_disassoc(struct net_device *dev, int idx);
366void __cfg80211_scan_done(struct work_struct *wk); 366void __cfg80211_scan_done(struct work_struct *wk);
367void cfg80211_upload_connect_keys(struct wireless_dev *wdev); 367void cfg80211_upload_connect_keys(struct wireless_dev *wdev);
368 368
369struct ieee80211_channel *
370rdev_fixed_channel(struct cfg80211_registered_device *rdev,
371 struct wireless_dev *for_wdev);
372int rdev_set_freq(struct cfg80211_registered_device *rdev,
373 int freq, enum nl80211_channel_type channel_type);
374
369#endif /* __NET_WIRELESS_CORE_H */ 375#endif /* __NET_WIRELESS_CORE_H */
diff --git a/net/wireless/ibss.c b/net/wireless/ibss.c
index 4d7a084b35e2..42840a01be74 100644
--- a/net/wireless/ibss.c
+++ b/net/wireless/ibss.c
@@ -78,10 +78,15 @@ int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev,
78 struct cfg80211_cached_keys *connkeys) 78 struct cfg80211_cached_keys *connkeys)
79{ 79{
80 struct wireless_dev *wdev = dev->ieee80211_ptr; 80 struct wireless_dev *wdev = dev->ieee80211_ptr;
81 struct ieee80211_channel *chan;
81 int err; 82 int err;
82 83
83 ASSERT_WDEV_LOCK(wdev); 84 ASSERT_WDEV_LOCK(wdev);
84 85
86 chan = rdev_fixed_channel(rdev, wdev);
87 if (chan && chan != params->channel)
88 return -EBUSY;
89
85 if (wdev->ssid_len) 90 if (wdev->ssid_len)
86 return -EALREADY; 91 return -EALREADY;
87 92
@@ -112,9 +117,11 @@ int cfg80211_join_ibss(struct cfg80211_registered_device *rdev,
112 struct wireless_dev *wdev = dev->ieee80211_ptr; 117 struct wireless_dev *wdev = dev->ieee80211_ptr;
113 int err; 118 int err;
114 119
120 mutex_lock(&rdev->devlist_mtx);
115 wdev_lock(wdev); 121 wdev_lock(wdev);
116 err = __cfg80211_join_ibss(rdev, dev, params, connkeys); 122 err = __cfg80211_join_ibss(rdev, dev, params, connkeys);
117 wdev_unlock(wdev); 123 wdev_unlock(wdev);
124 mutex_unlock(&rdev->devlist_mtx);
118 125
119 return err; 126 return err;
120} 127}
@@ -264,27 +271,32 @@ int cfg80211_ibss_wext_join(struct cfg80211_registered_device *rdev,
264 271
265int cfg80211_ibss_wext_siwfreq(struct net_device *dev, 272int cfg80211_ibss_wext_siwfreq(struct net_device *dev,
266 struct iw_request_info *info, 273 struct iw_request_info *info,
267 struct iw_freq *freq, char *extra) 274 struct iw_freq *wextfreq, char *extra)
268{ 275{
269 struct wireless_dev *wdev = dev->ieee80211_ptr; 276 struct wireless_dev *wdev = dev->ieee80211_ptr;
270 struct ieee80211_channel *chan; 277 struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
271 int err; 278 struct ieee80211_channel *chan = NULL;
279 int err, freq;
272 280
273 /* call only for ibss! */ 281 /* call only for ibss! */
274 if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC)) 282 if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC))
275 return -EINVAL; 283 return -EINVAL;
276 284
277 if (!wiphy_to_dev(wdev->wiphy)->ops->join_ibss) 285 if (!rdev->ops->join_ibss)
278 return -EOPNOTSUPP; 286 return -EOPNOTSUPP;
279 287
280 chan = cfg80211_wext_freq(wdev->wiphy, freq); 288 freq = cfg80211_wext_freq(wdev->wiphy, wextfreq);
281 if (chan && IS_ERR(chan)) 289 if (freq < 0)
282 return PTR_ERR(chan); 290 return freq;
283 291
284 if (chan && 292 if (freq) {
285 (chan->flags & IEEE80211_CHAN_NO_IBSS || 293 chan = ieee80211_get_channel(wdev->wiphy, freq);
286 chan->flags & IEEE80211_CHAN_DISABLED)) 294 if (!chan)
287 return -EINVAL; 295 return -EINVAL;
296 if (chan->flags & IEEE80211_CHAN_NO_IBSS ||
297 chan->flags & IEEE80211_CHAN_DISABLED)
298 return -EINVAL;
299 }
288 300
289 if (wdev->wext.ibss.channel == chan) 301 if (wdev->wext.ibss.channel == chan)
290 return 0; 302 return 0;
@@ -292,8 +304,7 @@ int cfg80211_ibss_wext_siwfreq(struct net_device *dev,
292 wdev_lock(wdev); 304 wdev_lock(wdev);
293 err = 0; 305 err = 0;
294 if (wdev->ssid_len) 306 if (wdev->ssid_len)
295 err = __cfg80211_leave_ibss(wiphy_to_dev(wdev->wiphy), 307 err = __cfg80211_leave_ibss(rdev, dev, true);
296 dev, true);
297 wdev_unlock(wdev); 308 wdev_unlock(wdev);
298 309
299 if (err) 310 if (err)
@@ -307,9 +318,11 @@ int cfg80211_ibss_wext_siwfreq(struct net_device *dev,
307 wdev->wext.ibss.channel_fixed = false; 318 wdev->wext.ibss.channel_fixed = false;
308 } 319 }
309 320
321 mutex_lock(&rdev->devlist_mtx);
310 wdev_lock(wdev); 322 wdev_lock(wdev);
311 err = cfg80211_ibss_wext_join(wiphy_to_dev(wdev->wiphy), wdev); 323 err = cfg80211_ibss_wext_join(rdev, wdev);
312 wdev_unlock(wdev); 324 wdev_unlock(wdev);
325 mutex_unlock(&rdev->devlist_mtx);
313 326
314 return err; 327 return err;
315} 328}
@@ -347,6 +360,7 @@ int cfg80211_ibss_wext_siwessid(struct net_device *dev,
347 struct iw_point *data, char *ssid) 360 struct iw_point *data, char *ssid)
348{ 361{
349 struct wireless_dev *wdev = dev->ieee80211_ptr; 362 struct wireless_dev *wdev = dev->ieee80211_ptr;
363 struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
350 size_t len = data->length; 364 size_t len = data->length;
351 int err; 365 int err;
352 366
@@ -354,14 +368,13 @@ int cfg80211_ibss_wext_siwessid(struct net_device *dev,
354 if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC)) 368 if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC))
355 return -EINVAL; 369 return -EINVAL;
356 370
357 if (!wiphy_to_dev(wdev->wiphy)->ops->join_ibss) 371 if (!rdev->ops->join_ibss)
358 return -EOPNOTSUPP; 372 return -EOPNOTSUPP;
359 373
360 wdev_lock(wdev); 374 wdev_lock(wdev);
361 err = 0; 375 err = 0;
362 if (wdev->ssid_len) 376 if (wdev->ssid_len)
363 err = __cfg80211_leave_ibss(wiphy_to_dev(wdev->wiphy), 377 err = __cfg80211_leave_ibss(rdev, dev, true);
364 dev, true);
365 wdev_unlock(wdev); 378 wdev_unlock(wdev);
366 379
367 if (err) 380 if (err)
@@ -375,9 +388,11 @@ int cfg80211_ibss_wext_siwessid(struct net_device *dev,
375 memcpy(wdev->wext.ibss.ssid, ssid, len); 388 memcpy(wdev->wext.ibss.ssid, ssid, len);
376 wdev->wext.ibss.ssid_len = len; 389 wdev->wext.ibss.ssid_len = len;
377 390
391 mutex_lock(&rdev->devlist_mtx);
378 wdev_lock(wdev); 392 wdev_lock(wdev);
379 err = cfg80211_ibss_wext_join(wiphy_to_dev(wdev->wiphy), wdev); 393 err = cfg80211_ibss_wext_join(rdev, wdev);
380 wdev_unlock(wdev); 394 wdev_unlock(wdev);
395 mutex_unlock(&rdev->devlist_mtx);
381 396
382 return err; 397 return err;
383} 398}
@@ -414,6 +429,7 @@ int cfg80211_ibss_wext_siwap(struct net_device *dev,
414 struct sockaddr *ap_addr, char *extra) 429 struct sockaddr *ap_addr, char *extra)
415{ 430{
416 struct wireless_dev *wdev = dev->ieee80211_ptr; 431 struct wireless_dev *wdev = dev->ieee80211_ptr;
432 struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
417 u8 *bssid = ap_addr->sa_data; 433 u8 *bssid = ap_addr->sa_data;
418 int err; 434 int err;
419 435
@@ -421,7 +437,7 @@ int cfg80211_ibss_wext_siwap(struct net_device *dev,
421 if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC)) 437 if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC))
422 return -EINVAL; 438 return -EINVAL;
423 439
424 if (!wiphy_to_dev(wdev->wiphy)->ops->join_ibss) 440 if (!rdev->ops->join_ibss)
425 return -EOPNOTSUPP; 441 return -EOPNOTSUPP;
426 442
427 if (ap_addr->sa_family != ARPHRD_ETHER) 443 if (ap_addr->sa_family != ARPHRD_ETHER)
@@ -443,8 +459,7 @@ int cfg80211_ibss_wext_siwap(struct net_device *dev,
443 wdev_lock(wdev); 459 wdev_lock(wdev);
444 err = 0; 460 err = 0;
445 if (wdev->ssid_len) 461 if (wdev->ssid_len)
446 err = __cfg80211_leave_ibss(wiphy_to_dev(wdev->wiphy), 462 err = __cfg80211_leave_ibss(rdev, dev, true);
447 dev, true);
448 wdev_unlock(wdev); 463 wdev_unlock(wdev);
449 464
450 if (err) 465 if (err)
@@ -456,9 +471,11 @@ int cfg80211_ibss_wext_siwap(struct net_device *dev,
456 } else 471 } else
457 wdev->wext.ibss.bssid = NULL; 472 wdev->wext.ibss.bssid = NULL;
458 473
474 mutex_lock(&rdev->devlist_mtx);
459 wdev_lock(wdev); 475 wdev_lock(wdev);
460 err = cfg80211_ibss_wext_join(wiphy_to_dev(wdev->wiphy), wdev); 476 err = cfg80211_ibss_wext_join(rdev, wdev);
461 wdev_unlock(wdev); 477 wdev_unlock(wdev);
478 mutex_unlock(&rdev->devlist_mtx);
462 479
463 return err; 480 return err;
464} 481}
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 0cd548267d4a..2ff7376f35a3 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -701,15 +701,8 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
701 701
702 if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) { 702 if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
703 enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT; 703 enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT;
704 struct ieee80211_channel *chan;
705 struct ieee80211_sta_ht_cap *ht_cap;
706 u32 freq; 704 u32 freq;
707 705
708 if (!rdev->ops->set_channel) {
709 result = -EOPNOTSUPP;
710 goto bad_res;
711 }
712
713 result = -EINVAL; 706 result = -EINVAL;
714 707
715 if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) { 708 if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) {
@@ -723,42 +716,12 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
723 } 716 }
724 717
725 freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]); 718 freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]);
726 chan = ieee80211_get_channel(&rdev->wiphy, freq);
727
728 /* Primary channel not allowed */
729 if (!chan || chan->flags & IEEE80211_CHAN_DISABLED)
730 goto bad_res;
731
732 if (channel_type == NL80211_CHAN_HT40MINUS &&
733 (chan->flags & IEEE80211_CHAN_NO_HT40MINUS))
734 goto bad_res;
735 else if (channel_type == NL80211_CHAN_HT40PLUS &&
736 (chan->flags & IEEE80211_CHAN_NO_HT40PLUS))
737 goto bad_res;
738
739 /*
740 * At this point we know if that if HT40 was requested
741 * we are allowed to use it and the extension channel
742 * exists.
743 */
744 719
745 ht_cap = &rdev->wiphy.bands[chan->band]->ht_cap; 720 mutex_lock(&rdev->devlist_mtx);
746 721 result = rdev_set_freq(rdev, freq, channel_type);
747 /* no HT capabilities or intolerant */ 722 mutex_unlock(&rdev->devlist_mtx);
748 if (channel_type != NL80211_CHAN_NO_HT) {
749 if (!ht_cap->ht_supported)
750 goto bad_res;
751 if (!(ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) ||
752 (ht_cap->cap & IEEE80211_HT_CAP_40MHZ_INTOLERANT))
753 goto bad_res;
754 }
755
756 result = rdev->ops->set_channel(&rdev->wiphy, chan,
757 channel_type);
758 if (result) 723 if (result)
759 goto bad_res; 724 goto bad_res;
760
761 rdev->channel = chan;
762 } 725 }
763 726
764 changed = 0; 727 changed = 0;
@@ -3453,7 +3416,7 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info)
3453 struct cfg80211_registered_device *rdev; 3416 struct cfg80211_registered_device *rdev;
3454 struct net_device *dev; 3417 struct net_device *dev;
3455 struct cfg80211_crypto_settings crypto; 3418 struct cfg80211_crypto_settings crypto;
3456 struct ieee80211_channel *chan; 3419 struct ieee80211_channel *chan, *fixedchan;
3457 const u8 *bssid, *ssid, *ie = NULL, *prev_bssid = NULL; 3420 const u8 *bssid, *ssid, *ie = NULL, *prev_bssid = NULL;
3458 int err, ssid_len, ie_len = 0; 3421 int err, ssid_len, ie_len = 0;
3459 bool use_mfp = false; 3422 bool use_mfp = false;
@@ -3496,6 +3459,15 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info)
3496 goto out; 3459 goto out;
3497 } 3460 }
3498 3461
3462 mutex_lock(&rdev->devlist_mtx);
3463 fixedchan = rdev_fixed_channel(rdev, NULL);
3464 if (fixedchan && chan != fixedchan) {
3465 err = -EBUSY;
3466 mutex_unlock(&rdev->devlist_mtx);
3467 goto out;
3468 }
3469 mutex_unlock(&rdev->devlist_mtx);
3470
3499 ssid = nla_data(info->attrs[NL80211_ATTR_SSID]); 3471 ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
3500 ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]); 3472 ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
3501 3473
diff --git a/net/wireless/sme.c b/net/wireless/sme.c
index 340934f714b2..219c3bc2c37d 100644
--- a/net/wireless/sme.c
+++ b/net/wireless/sme.c
@@ -256,9 +256,11 @@ void cfg80211_sme_scan_done(struct net_device *dev)
256{ 256{
257 struct wireless_dev *wdev = dev->ieee80211_ptr; 257 struct wireless_dev *wdev = dev->ieee80211_ptr;
258 258
259 mutex_lock(&wiphy_to_dev(wdev->wiphy)->devlist_mtx);
259 wdev_lock(wdev); 260 wdev_lock(wdev);
260 __cfg80211_sme_scan_done(dev); 261 __cfg80211_sme_scan_done(dev);
261 wdev_unlock(wdev); 262 wdev_unlock(wdev);
263 mutex_unlock(&wiphy_to_dev(wdev->wiphy)->devlist_mtx);
262} 264}
263 265
264void cfg80211_sme_rx_auth(struct net_device *dev, 266void cfg80211_sme_rx_auth(struct net_device *dev,
@@ -644,6 +646,7 @@ int __cfg80211_connect(struct cfg80211_registered_device *rdev,
644 struct cfg80211_cached_keys *connkeys) 646 struct cfg80211_cached_keys *connkeys)
645{ 647{
646 struct wireless_dev *wdev = dev->ieee80211_ptr; 648 struct wireless_dev *wdev = dev->ieee80211_ptr;
649 struct ieee80211_channel *chan;
647 int err; 650 int err;
648 651
649 ASSERT_WDEV_LOCK(wdev); 652 ASSERT_WDEV_LOCK(wdev);
@@ -651,6 +654,10 @@ int __cfg80211_connect(struct cfg80211_registered_device *rdev,
651 if (wdev->sme_state != CFG80211_SME_IDLE) 654 if (wdev->sme_state != CFG80211_SME_IDLE)
652 return -EALREADY; 655 return -EALREADY;
653 656
657 chan = rdev_fixed_channel(rdev, wdev);
658 if (chan && chan != connect->channel)
659 return -EBUSY;
660
654 if (WARN_ON(wdev->connect_keys)) { 661 if (WARN_ON(wdev->connect_keys)) {
655 kfree(wdev->connect_keys); 662 kfree(wdev->connect_keys);
656 wdev->connect_keys = NULL; 663 wdev->connect_keys = NULL;
@@ -785,9 +792,11 @@ int cfg80211_connect(struct cfg80211_registered_device *rdev,
785{ 792{
786 int err; 793 int err;
787 794
795 mutex_lock(&rdev->devlist_mtx);
788 wdev_lock(dev->ieee80211_ptr); 796 wdev_lock(dev->ieee80211_ptr);
789 err = __cfg80211_connect(rdev, dev, connect, connkeys); 797 err = __cfg80211_connect(rdev, dev, connect, connkeys);
790 wdev_unlock(dev->ieee80211_ptr); 798 wdev_unlock(dev->ieee80211_ptr);
799 mutex_unlock(&rdev->devlist_mtx);
791 800
792 return err; 801 return err;
793} 802}
diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c
index e4e90e249bab..17648dc79867 100644
--- a/net/wireless/wext-compat.c
+++ b/net/wireless/wext-compat.c
@@ -267,39 +267,26 @@ EXPORT_SYMBOL_GPL(cfg80211_wext_giwrange);
267 * @wiphy: the wiphy 267 * @wiphy: the wiphy
268 * @freq: the wext freq encoding 268 * @freq: the wext freq encoding
269 * 269 *
270 * Returns a channel, %NULL for auto, or an ERR_PTR for errors! 270 * Returns a frequency, or a negative error code, or 0 for auto.
271 */ 271 */
272struct ieee80211_channel *cfg80211_wext_freq(struct wiphy *wiphy, 272int cfg80211_wext_freq(struct wiphy *wiphy, struct iw_freq *freq)
273 struct iw_freq *freq)
274{ 273{
275 struct ieee80211_channel *chan;
276 int f;
277
278 /* 274 /*
279 * Parse frequency - return NULL for auto and 275 * Parse frequency - return 0 for auto and
280 * -EINVAL for impossible things. 276 * -EINVAL for impossible things.
281 */ 277 */
282 if (freq->e == 0) { 278 if (freq->e == 0) {
283 if (freq->m < 0) 279 if (freq->m < 0)
284 return NULL; 280 return 0;
285 f = ieee80211_channel_to_frequency(freq->m); 281 return ieee80211_channel_to_frequency(freq->m);
286 } else { 282 } else {
287 int i, div = 1000000; 283 int i, div = 1000000;
288 for (i = 0; i < freq->e; i++) 284 for (i = 0; i < freq->e; i++)
289 div /= 10; 285 div /= 10;
290 if (div <= 0) 286 if (div <= 0)
291 return ERR_PTR(-EINVAL); 287 return -EINVAL;
292 f = freq->m / div; 288 return freq->m / div;
293 } 289 }
294
295 /*
296 * Look up channel struct and return -EINVAL when
297 * it cannot be found.
298 */
299 chan = ieee80211_get_channel(wiphy, f);
300 if (!chan)
301 return ERR_PTR(-EINVAL);
302 return chan;
303} 290}
304 291
305int cfg80211_wext_siwrts(struct net_device *dev, 292int cfg80211_wext_siwrts(struct net_device *dev,
@@ -761,33 +748,29 @@ EXPORT_SYMBOL_GPL(cfg80211_wext_giwencode);
761 748
762int cfg80211_wext_siwfreq(struct net_device *dev, 749int cfg80211_wext_siwfreq(struct net_device *dev,
763 struct iw_request_info *info, 750 struct iw_request_info *info,
764 struct iw_freq *freq, char *extra) 751 struct iw_freq *wextfreq, char *extra)
765{ 752{
766 struct wireless_dev *wdev = dev->ieee80211_ptr; 753 struct wireless_dev *wdev = dev->ieee80211_ptr;
767 struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); 754 struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
768 struct ieee80211_channel *chan; 755 int freq, err;
769 int err;
770 756
771 switch (wdev->iftype) { 757 switch (wdev->iftype) {
772 case NL80211_IFTYPE_STATION: 758 case NL80211_IFTYPE_STATION:
773 return cfg80211_mgd_wext_siwfreq(dev, info, freq, extra); 759 return cfg80211_mgd_wext_siwfreq(dev, info, wextfreq, extra);
774 case NL80211_IFTYPE_ADHOC: 760 case NL80211_IFTYPE_ADHOC:
775 return cfg80211_ibss_wext_siwfreq(dev, info, freq, extra); 761 return cfg80211_ibss_wext_siwfreq(dev, info, wextfreq, extra);
776 default: 762 default:
777 chan = cfg80211_wext_freq(wdev->wiphy, freq); 763 freq = cfg80211_wext_freq(wdev->wiphy, wextfreq);
778 if (!chan) 764 if (freq < 0)
765 return freq;
766 if (freq == 0)
779 return -EINVAL; 767 return -EINVAL;
780 if (IS_ERR(chan)) 768 mutex_lock(&rdev->devlist_mtx);
781 return PTR_ERR(chan); 769 err = rdev_set_freq(rdev, freq, NL80211_CHAN_NO_HT);
782 err = rdev->ops->set_channel(wdev->wiphy, chan, 770 mutex_unlock(&rdev->devlist_mtx);
783 NL80211_CHAN_NO_HT); 771 return err;
784 if (err)
785 return err;
786 rdev->channel = chan;
787 return 0;
788 } 772 }
789} 773}
790EXPORT_SYMBOL_GPL(cfg80211_wext_siwfreq);
791 774
792int cfg80211_wext_giwfreq(struct net_device *dev, 775int cfg80211_wext_giwfreq(struct net_device *dev,
793 struct iw_request_info *info, 776 struct iw_request_info *info,
diff --git a/net/wireless/wext-compat.h b/net/wireless/wext-compat.h
index 9a3774749589..20b3daef6964 100644
--- a/net/wireless/wext-compat.h
+++ b/net/wireless/wext-compat.h
@@ -42,8 +42,7 @@ int cfg80211_mgd_wext_giwessid(struct net_device *dev,
42 struct iw_request_info *info, 42 struct iw_request_info *info,
43 struct iw_point *data, char *ssid); 43 struct iw_point *data, char *ssid);
44 44
45struct ieee80211_channel *cfg80211_wext_freq(struct wiphy *wiphy, 45int cfg80211_wext_freq(struct wiphy *wiphy, struct iw_freq *freq);
46 struct iw_freq *freq);
47 46
48 47
49extern const struct iw_handler_def cfg80211_wext_handler; 48extern const struct iw_handler_def cfg80211_wext_handler;
diff --git a/net/wireless/wext-sme.c b/net/wireless/wext-sme.c
index e4a054aceb5a..fe1a53639122 100644
--- a/net/wireless/wext-sme.c
+++ b/net/wireless/wext-sme.c
@@ -52,25 +52,31 @@ int cfg80211_mgd_wext_connect(struct cfg80211_registered_device *rdev,
52 52
53int cfg80211_mgd_wext_siwfreq(struct net_device *dev, 53int cfg80211_mgd_wext_siwfreq(struct net_device *dev,
54 struct iw_request_info *info, 54 struct iw_request_info *info,
55 struct iw_freq *freq, char *extra) 55 struct iw_freq *wextfreq, char *extra)
56{ 56{
57 struct wireless_dev *wdev = dev->ieee80211_ptr; 57 struct wireless_dev *wdev = dev->ieee80211_ptr;
58 struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); 58 struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
59 struct ieee80211_channel *chan; 59 struct ieee80211_channel *chan = NULL;
60 int err; 60 int err, freq;
61 61
62 /* call only for station! */ 62 /* call only for station! */
63 if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION)) 63 if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION))
64 return -EINVAL; 64 return -EINVAL;
65 65
66 chan = cfg80211_wext_freq(wdev->wiphy, freq); 66 freq = cfg80211_wext_freq(wdev->wiphy, wextfreq);
67 if (chan && IS_ERR(chan)) 67 if (freq < 0)
68 return PTR_ERR(chan); 68 return freq;
69 69
70 if (chan && (chan->flags & IEEE80211_CHAN_DISABLED)) 70 if (freq) {
71 return -EINVAL; 71 chan = ieee80211_get_channel(wdev->wiphy, freq);
72 if (!chan)
73 return -EINVAL;
74 if (chan->flags & IEEE80211_CHAN_DISABLED)
75 return -EINVAL;
76 }
72 77
73 cfg80211_lock_rdev(rdev); 78 cfg80211_lock_rdev(rdev);
79 mutex_lock(&rdev->devlist_mtx);
74 wdev_lock(wdev); 80 wdev_lock(wdev);
75 81
76 if (wdev->sme_state != CFG80211_SME_IDLE) { 82 if (wdev->sme_state != CFG80211_SME_IDLE) {
@@ -84,9 +90,8 @@ int cfg80211_mgd_wext_siwfreq(struct net_device *dev,
84 /* if SSID set, we'll try right again, avoid event */ 90 /* if SSID set, we'll try right again, avoid event */
85 if (wdev->wext.connect.ssid_len) 91 if (wdev->wext.connect.ssid_len)
86 event = false; 92 event = false;
87 err = __cfg80211_disconnect(wiphy_to_dev(wdev->wiphy), 93 err = __cfg80211_disconnect(rdev, dev,
88 dev, WLAN_REASON_DEAUTH_LEAVING, 94 WLAN_REASON_DEAUTH_LEAVING, event);
89 event);
90 if (err) 95 if (err)
91 goto out; 96 goto out;
92 } 97 }
@@ -95,17 +100,15 @@ int cfg80211_mgd_wext_siwfreq(struct net_device *dev,
95 wdev->wext.connect.channel = chan; 100 wdev->wext.connect.channel = chan;
96 101
97 /* SSID is not set, we just want to switch channel */ 102 /* SSID is not set, we just want to switch channel */
98 if (wdev->wext.connect.ssid_len && chan) { 103 if (chan && !wdev->wext.connect.ssid_len) {
99 err = -EOPNOTSUPP; 104 err = rdev_set_freq(rdev, freq, NL80211_CHAN_NO_HT);
100 if (rdev->ops->set_channel)
101 err = rdev->ops->set_channel(wdev->wiphy, chan,
102 NL80211_CHAN_NO_HT);
103 goto out; 105 goto out;
104 } 106 }
105 107
106 err = cfg80211_mgd_wext_connect(wiphy_to_dev(wdev->wiphy), wdev); 108 err = cfg80211_mgd_wext_connect(rdev, wdev);
107 out: 109 out:
108 wdev_unlock(wdev); 110 wdev_unlock(wdev);
111 mutex_unlock(&rdev->devlist_mtx);
109 cfg80211_unlock_rdev(rdev); 112 cfg80211_unlock_rdev(rdev);
110 return err; 113 return err;
111} 114}
@@ -143,6 +146,7 @@ int cfg80211_mgd_wext_siwessid(struct net_device *dev,
143 struct iw_point *data, char *ssid) 146 struct iw_point *data, char *ssid)
144{ 147{
145 struct wireless_dev *wdev = dev->ieee80211_ptr; 148 struct wireless_dev *wdev = dev->ieee80211_ptr;
149 struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
146 size_t len = data->length; 150 size_t len = data->length;
147 int err; 151 int err;
148 152
@@ -157,7 +161,8 @@ int cfg80211_mgd_wext_siwessid(struct net_device *dev,
157 if (len > 0 && ssid[len - 1] == '\0') 161 if (len > 0 && ssid[len - 1] == '\0')
158 len--; 162 len--;
159 163
160 cfg80211_lock_rdev(wiphy_to_dev(wdev->wiphy)); 164 cfg80211_lock_rdev(rdev);
165 mutex_lock(&rdev->devlist_mtx);
161 wdev_lock(wdev); 166 wdev_lock(wdev);
162 167
163 err = 0; 168 err = 0;
@@ -173,9 +178,8 @@ int cfg80211_mgd_wext_siwessid(struct net_device *dev,
173 /* if SSID set now, we'll try to connect, avoid event */ 178 /* if SSID set now, we'll try to connect, avoid event */
174 if (len) 179 if (len)
175 event = false; 180 event = false;
176 err = __cfg80211_disconnect(wiphy_to_dev(wdev->wiphy), 181 err = __cfg80211_disconnect(rdev, dev,
177 dev, WLAN_REASON_DEAUTH_LEAVING, 182 WLAN_REASON_DEAUTH_LEAVING, event);
178 event);
179 if (err) 183 if (err)
180 goto out; 184 goto out;
181 } 185 }
@@ -186,10 +190,11 @@ int cfg80211_mgd_wext_siwessid(struct net_device *dev,
186 190
187 wdev->wext.connect.crypto.control_port = false; 191 wdev->wext.connect.crypto.control_port = false;
188 192
189 err = cfg80211_mgd_wext_connect(wiphy_to_dev(wdev->wiphy), wdev); 193 err = cfg80211_mgd_wext_connect(rdev, wdev);
190 out: 194 out:
191 wdev_unlock(wdev); 195 wdev_unlock(wdev);
192 cfg80211_unlock_rdev(wiphy_to_dev(wdev->wiphy)); 196 mutex_unlock(&rdev->devlist_mtx);
197 cfg80211_unlock_rdev(rdev);
193 return err; 198 return err;
194} 199}
195 200
@@ -230,6 +235,7 @@ int cfg80211_mgd_wext_siwap(struct net_device *dev,
230 struct sockaddr *ap_addr, char *extra) 235 struct sockaddr *ap_addr, char *extra)
231{ 236{
232 struct wireless_dev *wdev = dev->ieee80211_ptr; 237 struct wireless_dev *wdev = dev->ieee80211_ptr;
238 struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
233 u8 *bssid = ap_addr->sa_data; 239 u8 *bssid = ap_addr->sa_data;
234 int err; 240 int err;
235 241
@@ -244,7 +250,8 @@ int cfg80211_mgd_wext_siwap(struct net_device *dev,
244 if (is_zero_ether_addr(bssid) || is_broadcast_ether_addr(bssid)) 250 if (is_zero_ether_addr(bssid) || is_broadcast_ether_addr(bssid))
245 bssid = NULL; 251 bssid = NULL;
246 252
247 cfg80211_lock_rdev(wiphy_to_dev(wdev->wiphy)); 253 cfg80211_lock_rdev(rdev);
254 mutex_lock(&rdev->devlist_mtx);
248 wdev_lock(wdev); 255 wdev_lock(wdev);
249 256
250 if (wdev->sme_state != CFG80211_SME_IDLE) { 257 if (wdev->sme_state != CFG80211_SME_IDLE) {
@@ -258,9 +265,8 @@ int cfg80211_mgd_wext_siwap(struct net_device *dev,
258 compare_ether_addr(bssid, wdev->wext.connect.bssid) == 0) 265 compare_ether_addr(bssid, wdev->wext.connect.bssid) == 0)
259 goto out; 266 goto out;
260 267
261 err = __cfg80211_disconnect(wiphy_to_dev(wdev->wiphy), 268 err = __cfg80211_disconnect(rdev, dev,
262 dev, WLAN_REASON_DEAUTH_LEAVING, 269 WLAN_REASON_DEAUTH_LEAVING, false);
263 false);
264 if (err) 270 if (err)
265 goto out; 271 goto out;
266 } 272 }
@@ -271,10 +277,11 @@ int cfg80211_mgd_wext_siwap(struct net_device *dev,
271 } else 277 } else
272 wdev->wext.connect.bssid = NULL; 278 wdev->wext.connect.bssid = NULL;
273 279
274 err = cfg80211_mgd_wext_connect(wiphy_to_dev(wdev->wiphy), wdev); 280 err = cfg80211_mgd_wext_connect(rdev, wdev);
275 out: 281 out:
276 wdev_unlock(wdev); 282 wdev_unlock(wdev);
277 cfg80211_unlock_rdev(wiphy_to_dev(wdev->wiphy)); 283 mutex_unlock(&rdev->devlist_mtx);
284 cfg80211_unlock_rdev(rdev);
278 return err; 285 return err;
279} 286}
280 287