aboutsummaryrefslogtreecommitdiffstats
path: root/net/wireless/nl80211.c
diff options
context:
space:
mode:
authorJohannes Berg <johannes@sipsolutions.net>2010-05-05 09:25:02 -0400
committerJohn W. Linville <linville@tuxdriver.com>2010-05-07 14:55:50 -0400
commitf444de05d20e27cdd960c13fcbcfca3099f03143 (patch)
treea7fbef60420d88dda5840e06094be21ee3eb1dc0 /net/wireless/nl80211.c
parentac8dd506e40ee2c7fcc61654a44c32555a0a8d6c (diff)
cfg80211/mac80211: better channel handling
Currently (all tested with hwsim) you can do stupid things like setting up an AP on a certain channel, then adding another virtual interface and making that associate on another channel -- this will make the beaconing to move channel but obviously without the necessary IEs data update. In order to improve this situation, first make the configuration APIs (cfg80211 and nl80211) aware of multi-channel operation -- we'll eventually need that in the future anyway. There's one userland API change and one API addition. The API change is that now SET_WIPHY must be called with virtual interface index rather than only wiphy index in order to take effect for that interface -- luckily all current users (hostapd) do that. For monitor interfaces, the old setting is preserved, but monitors are always slaved to other devices anyway so no guarantees. The second userland API change is the introduction of a per virtual interface SET_CHANNEL command, that hostapd should use going forward to make it easier to understand what's going on (it can automatically detect a kernel with this command). Other than mac80211, no existing cfg80211 drivers are affected by this change because they only allow a single virtual interface. mac80211, however, now needs to be aware that the channel settings are per interface now, and needs to disallow (for now) real multi-channel operation, which is another important part of this patch. One of the immediate benefits is that you can now start hostapd to operate on a hardware that already has a connection on another virtual interface, as long as you specify the same channel. Note that two things are left unhandled (this is an improvement -- not a complete fix): * different HT/no-HT modes currently you could start an HT AP and then connect to a non-HT network on the same channel which would configure the hardware for no HT; that can be fixed fairly easily * CSA An AP we're connected to on a virtual interface might indicate switching channels, and in that case we would follow it, regardless of how many other interfaces are operating; this requires more effort to fix but is pretty rare after all 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.c171
1 files changed, 132 insertions, 39 deletions
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index c27bef8e0c11..ec1b4a896c6e 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -588,6 +588,7 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags,
588 i++; 588 i++;
589 NLA_PUT_U32(msg, i, NL80211_CMD_SET_WIPHY_NETNS); 589 NLA_PUT_U32(msg, i, NL80211_CMD_SET_WIPHY_NETNS);
590 } 590 }
591 CMD(set_channel, SET_CHANNEL);
591 592
592#undef CMD 593#undef CMD
593 594
@@ -688,10 +689,90 @@ static int parse_txq_params(struct nlattr *tb[],
688 return 0; 689 return 0;
689} 690}
690 691
692static bool nl80211_can_set_dev_channel(struct wireless_dev *wdev)
693{
694 /*
695 * You can only set the channel explicitly for AP, mesh
696 * and WDS type interfaces; all others have their channel
697 * managed via their respective "establish a connection"
698 * command (connect, join, ...)
699 *
700 * Monitors are special as they are normally slaved to
701 * whatever else is going on, so they behave as though
702 * you tried setting the wiphy channel itself.
703 */
704 return !wdev ||
705 wdev->iftype == NL80211_IFTYPE_AP ||
706 wdev->iftype == NL80211_IFTYPE_WDS ||
707 wdev->iftype == NL80211_IFTYPE_MESH_POINT ||
708 wdev->iftype == NL80211_IFTYPE_MONITOR;
709}
710
711static int __nl80211_set_channel(struct cfg80211_registered_device *rdev,
712 struct wireless_dev *wdev,
713 struct genl_info *info)
714{
715 enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT;
716 u32 freq;
717 int result;
718
719 if (!info->attrs[NL80211_ATTR_WIPHY_FREQ])
720 return -EINVAL;
721
722 if (!nl80211_can_set_dev_channel(wdev))
723 return -EOPNOTSUPP;
724
725 if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) {
726 channel_type = nla_get_u32(info->attrs[
727 NL80211_ATTR_WIPHY_CHANNEL_TYPE]);
728 if (channel_type != NL80211_CHAN_NO_HT &&
729 channel_type != NL80211_CHAN_HT20 &&
730 channel_type != NL80211_CHAN_HT40PLUS &&
731 channel_type != NL80211_CHAN_HT40MINUS)
732 return -EINVAL;
733 }
734
735 freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]);
736
737 mutex_lock(&rdev->devlist_mtx);
738 if (wdev) {
739 wdev_lock(wdev);
740 result = cfg80211_set_freq(rdev, wdev, freq, channel_type);
741 wdev_unlock(wdev);
742 } else {
743 result = cfg80211_set_freq(rdev, NULL, freq, channel_type);
744 }
745 mutex_unlock(&rdev->devlist_mtx);
746
747 return result;
748}
749
750static int nl80211_set_channel(struct sk_buff *skb, struct genl_info *info)
751{
752 struct cfg80211_registered_device *rdev;
753 struct net_device *netdev;
754 int result;
755
756 rtnl_lock();
757
758 result = get_rdev_dev_by_info_ifindex(info, &rdev, &netdev);
759 if (result)
760 goto unlock;
761
762 result = __nl80211_set_channel(rdev, netdev->ieee80211_ptr, info);
763
764 unlock:
765 rtnl_unlock();
766
767 return result;
768}
769
691static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) 770static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
692{ 771{
693 struct cfg80211_registered_device *rdev; 772 struct cfg80211_registered_device *rdev;
694 int result = 0, rem_txq_params = 0; 773 struct net_device *netdev = NULL;
774 struct wireless_dev *wdev;
775 int result, rem_txq_params = 0;
695 struct nlattr *nl_txq_params; 776 struct nlattr *nl_txq_params;
696 u32 changed; 777 u32 changed;
697 u8 retry_short = 0, retry_long = 0; 778 u8 retry_short = 0, retry_long = 0;
@@ -700,16 +781,50 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
700 781
701 rtnl_lock(); 782 rtnl_lock();
702 783
784 /*
785 * Try to find the wiphy and netdev. Normally this
786 * function shouldn't need the netdev, but this is
787 * done for backward compatibility -- previously
788 * setting the channel was done per wiphy, but now
789 * it is per netdev. Previous userland like hostapd
790 * also passed a netdev to set_wiphy, so that it is
791 * possible to let that go to the right netdev!
792 */
703 mutex_lock(&cfg80211_mutex); 793 mutex_lock(&cfg80211_mutex);
704 794
705 rdev = __cfg80211_rdev_from_info(info); 795 if (info->attrs[NL80211_ATTR_IFINDEX]) {
706 if (IS_ERR(rdev)) { 796 int ifindex = nla_get_u32(info->attrs[NL80211_ATTR_IFINDEX]);
707 mutex_unlock(&cfg80211_mutex); 797
708 result = PTR_ERR(rdev); 798 netdev = dev_get_by_index(genl_info_net(info), ifindex);
709 goto unlock; 799 if (netdev && netdev->ieee80211_ptr) {
800 rdev = wiphy_to_dev(netdev->ieee80211_ptr->wiphy);
801 mutex_lock(&rdev->mtx);
802 } else
803 netdev = NULL;
710 } 804 }
711 805
712 mutex_lock(&rdev->mtx); 806 if (!netdev) {
807 rdev = __cfg80211_rdev_from_info(info);
808 if (IS_ERR(rdev)) {
809 mutex_unlock(&cfg80211_mutex);
810 result = PTR_ERR(rdev);
811 goto unlock;
812 }
813 wdev = NULL;
814 netdev = NULL;
815 result = 0;
816
817 mutex_lock(&rdev->mtx);
818 } else if (netif_running(netdev) &&
819 nl80211_can_set_dev_channel(netdev->ieee80211_ptr))
820 wdev = netdev->ieee80211_ptr;
821 else
822 wdev = NULL;
823
824 /*
825 * end workaround code, by now the rdev is available
826 * and locked, and wdev may or may not be NULL.
827 */
713 828
714 if (info->attrs[NL80211_ATTR_WIPHY_NAME]) 829 if (info->attrs[NL80211_ATTR_WIPHY_NAME])
715 result = cfg80211_dev_rename( 830 result = cfg80211_dev_rename(
@@ -748,26 +863,7 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
748 } 863 }
749 864
750 if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) { 865 if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
751 enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT; 866 result = __nl80211_set_channel(rdev, wdev, info);
752 u32 freq;
753
754 result = -EINVAL;
755
756 if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) {
757 channel_type = nla_get_u32(info->attrs[
758 NL80211_ATTR_WIPHY_CHANNEL_TYPE]);
759 if (channel_type != NL80211_CHAN_NO_HT &&
760 channel_type != NL80211_CHAN_HT20 &&
761 channel_type != NL80211_CHAN_HT40PLUS &&
762 channel_type != NL80211_CHAN_HT40MINUS)
763 goto bad_res;
764 }
765
766 freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]);
767
768 mutex_lock(&rdev->devlist_mtx);
769 result = rdev_set_freq(rdev, NULL, freq, channel_type);
770 mutex_unlock(&rdev->devlist_mtx);
771 if (result) 867 if (result)
772 goto bad_res; 868 goto bad_res;
773 } 869 }
@@ -864,6 +960,8 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
864 960
865 bad_res: 961 bad_res:
866 mutex_unlock(&rdev->mtx); 962 mutex_unlock(&rdev->mtx);
963 if (netdev)
964 dev_put(netdev);
867 unlock: 965 unlock:
868 rtnl_unlock(); 966 rtnl_unlock();
869 return result; 967 return result;
@@ -3561,9 +3659,8 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info)
3561{ 3659{
3562 struct cfg80211_registered_device *rdev; 3660 struct cfg80211_registered_device *rdev;
3563 struct net_device *dev; 3661 struct net_device *dev;
3564 struct wireless_dev *wdev;
3565 struct cfg80211_crypto_settings crypto; 3662 struct cfg80211_crypto_settings crypto;
3566 struct ieee80211_channel *chan, *fixedchan; 3663 struct ieee80211_channel *chan;
3567 const u8 *bssid, *ssid, *ie = NULL, *prev_bssid = NULL; 3664 const u8 *bssid, *ssid, *ie = NULL, *prev_bssid = NULL;
3568 int err, ssid_len, ie_len = 0; 3665 int err, ssid_len, ie_len = 0;
3569 bool use_mfp = false; 3666 bool use_mfp = false;
@@ -3606,16 +3703,6 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info)
3606 goto out; 3703 goto out;
3607 } 3704 }
3608 3705
3609 mutex_lock(&rdev->devlist_mtx);
3610 wdev = dev->ieee80211_ptr;
3611 fixedchan = rdev_fixed_channel(rdev, wdev);
3612 if (fixedchan && chan != fixedchan) {
3613 err = -EBUSY;
3614 mutex_unlock(&rdev->devlist_mtx);
3615 goto out;
3616 }
3617 mutex_unlock(&rdev->devlist_mtx);
3618
3619 ssid = nla_data(info->attrs[NL80211_ATTR_SSID]); 3706 ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
3620 ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]); 3707 ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
3621 3708
@@ -5185,6 +5272,12 @@ static struct genl_ops nl80211_ops[] = {
5185 .policy = nl80211_policy, 5272 .policy = nl80211_policy,
5186 .flags = GENL_ADMIN_PERM, 5273 .flags = GENL_ADMIN_PERM,
5187 }, 5274 },
5275 {
5276 .cmd = NL80211_CMD_SET_CHANNEL,
5277 .doit = nl80211_set_channel,
5278 .policy = nl80211_policy,
5279 .flags = GENL_ADMIN_PERM,
5280 },
5188}; 5281};
5189 5282
5190static struct genl_multicast_group nl80211_mlme_mcgrp = { 5283static struct genl_multicast_group nl80211_mlme_mcgrp = {