diff options
author | Johannes Berg <johannes@sipsolutions.net> | 2009-08-07 11:22:35 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2009-08-14 09:13:42 -0400 |
commit | 59bbb6f7574bc693ed8313b98eac641116c95b94 (patch) | |
tree | da24ed15c5e375782e79b3dab7022d2100a7987a /net/wireless/nl80211.c | |
parent | f26b32ed4bd5780855a79bb17fb1a431fa867dad (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>
Diffstat (limited to 'net/wireless/nl80211.c')
-rw-r--r-- | net/wireless/nl80211.c | 54 |
1 files changed, 13 insertions, 41 deletions
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 | ||