aboutsummaryrefslogtreecommitdiffstats
path: root/net/wireless/nl80211.c
diff options
context:
space:
mode:
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 01da83ddcff7..aaa1aad566cd 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -589,6 +589,7 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags,
589 i++; 589 i++;
590 NLA_PUT_U32(msg, i, NL80211_CMD_SET_WIPHY_NETNS); 590 NLA_PUT_U32(msg, i, NL80211_CMD_SET_WIPHY_NETNS);
591 } 591 }
592 CMD(set_channel, SET_CHANNEL);
592 593
593#undef CMD 594#undef CMD
594 595
@@ -689,10 +690,90 @@ static int parse_txq_params(struct nlattr *tb[],
689 return 0; 690 return 0;
690} 691}
691 692
693static bool nl80211_can_set_dev_channel(struct wireless_dev *wdev)
694{
695 /*
696 * You can only set the channel explicitly for AP, mesh
697 * and WDS type interfaces; all others have their channel
698 * managed via their respective "establish a connection"
699 * command (connect, join, ...)
700 *
701 * Monitors are special as they are normally slaved to
702 * whatever else is going on, so they behave as though
703 * you tried setting the wiphy channel itself.
704 */
705 return !wdev ||
706 wdev->iftype == NL80211_IFTYPE_AP ||
707 wdev->iftype == NL80211_IFTYPE_WDS ||
708 wdev->iftype == NL80211_IFTYPE_MESH_POINT ||
709 wdev->iftype == NL80211_IFTYPE_MONITOR;
710}
711
712static int __nl80211_set_channel(struct cfg80211_registered_device *rdev,
713 struct wireless_dev *wdev,
714 struct genl_info *info)
715{
716 enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT;
717 u32 freq;
718 int result;
719
720 if (!info->attrs[NL80211_ATTR_WIPHY_FREQ])
721 return -EINVAL;
722
723 if (!nl80211_can_set_dev_channel(wdev))
724 return -EOPNOTSUPP;
725
726 if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) {
727 channel_type = nla_get_u32(info->attrs[
728 NL80211_ATTR_WIPHY_CHANNEL_TYPE]);
729 if (channel_type != NL80211_CHAN_NO_HT &&
730 channel_type != NL80211_CHAN_HT20 &&
731 channel_type != NL80211_CHAN_HT40PLUS &&
732 channel_type != NL80211_CHAN_HT40MINUS)
733 return -EINVAL;
734 }
735
736 freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]);
737
738 mutex_lock(&rdev->devlist_mtx);
739 if (wdev) {
740 wdev_lock(wdev);
741 result = cfg80211_set_freq(rdev, wdev, freq, channel_type);
742 wdev_unlock(wdev);
743 } else {
744 result = cfg80211_set_freq(rdev, NULL, freq, channel_type);
745 }
746 mutex_unlock(&rdev->devlist_mtx);
747
748 return result;
749}
750
751static int nl80211_set_channel(struct sk_buff *skb, struct genl_info *info)
752{
753 struct cfg80211_registered_device *rdev;
754 struct net_device *netdev;
755 int result;
756
757 rtnl_lock();
758
759 result = get_rdev_dev_by_info_ifindex(info, &rdev, &netdev);
760 if (result)
761 goto unlock;
762
763 result = __nl80211_set_channel(rdev, netdev->ieee80211_ptr, info);
764
765 unlock:
766 rtnl_unlock();
767
768 return result;
769}
770
692static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) 771static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
693{ 772{
694 struct cfg80211_registered_device *rdev; 773 struct cfg80211_registered_device *rdev;
695 int result = 0, rem_txq_params = 0; 774 struct net_device *netdev = NULL;
775 struct wireless_dev *wdev;
776 int result, rem_txq_params = 0;
696 struct nlattr *nl_txq_params; 777 struct nlattr *nl_txq_params;
697 u32 changed; 778 u32 changed;
698 u8 retry_short = 0, retry_long = 0; 779 u8 retry_short = 0, retry_long = 0;
@@ -701,16 +782,50 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
701 782
702 rtnl_lock(); 783 rtnl_lock();
703 784
785 /*
786 * Try to find the wiphy and netdev. Normally this
787 * function shouldn't need the netdev, but this is
788 * done for backward compatibility -- previously
789 * setting the channel was done per wiphy, but now
790 * it is per netdev. Previous userland like hostapd
791 * also passed a netdev to set_wiphy, so that it is
792 * possible to let that go to the right netdev!
793 */
704 mutex_lock(&cfg80211_mutex); 794 mutex_lock(&cfg80211_mutex);
705 795
706 rdev = __cfg80211_rdev_from_info(info); 796 if (info->attrs[NL80211_ATTR_IFINDEX]) {
707 if (IS_ERR(rdev)) { 797 int ifindex = nla_get_u32(info->attrs[NL80211_ATTR_IFINDEX]);
708 mutex_unlock(&cfg80211_mutex); 798
709 result = PTR_ERR(rdev); 799 netdev = dev_get_by_index(genl_info_net(info), ifindex);
710 goto unlock; 800 if (netdev && netdev->ieee80211_ptr) {
801 rdev = wiphy_to_dev(netdev->ieee80211_ptr->wiphy);
802 mutex_lock(&rdev->mtx);
803 } else
804 netdev = NULL;
711 } 805 }
712 806
713 mutex_lock(&rdev->mtx); 807 if (!netdev) {
808 rdev = __cfg80211_rdev_from_info(info);
809 if (IS_ERR(rdev)) {
810 mutex_unlock(&cfg80211_mutex);
811 result = PTR_ERR(rdev);
812 goto unlock;
813 }
814 wdev = NULL;
815 netdev = NULL;
816 result = 0;
817
818 mutex_lock(&rdev->mtx);
819 } else if (netif_running(netdev) &&
820 nl80211_can_set_dev_channel(netdev->ieee80211_ptr))
821 wdev = netdev->ieee80211_ptr;
822 else
823 wdev = NULL;
824
825 /*
826 * end workaround code, by now the rdev is available
827 * and locked, and wdev may or may not be NULL.
828 */
714 829
715 if (info->attrs[NL80211_ATTR_WIPHY_NAME]) 830 if (info->attrs[NL80211_ATTR_WIPHY_NAME])
716 result = cfg80211_dev_rename( 831 result = cfg80211_dev_rename(
@@ -749,26 +864,7 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
749 } 864 }
750 865
751 if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) { 866 if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
752 enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT; 867 result = __nl80211_set_channel(rdev, wdev, info);
753 u32 freq;
754
755 result = -EINVAL;
756
757 if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) {
758 channel_type = nla_get_u32(info->attrs[
759 NL80211_ATTR_WIPHY_CHANNEL_TYPE]);
760 if (channel_type != NL80211_CHAN_NO_HT &&
761 channel_type != NL80211_CHAN_HT20 &&
762 channel_type != NL80211_CHAN_HT40PLUS &&
763 channel_type != NL80211_CHAN_HT40MINUS)
764 goto bad_res;
765 }
766
767 freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]);
768
769 mutex_lock(&rdev->devlist_mtx);
770 result = rdev_set_freq(rdev, NULL, freq, channel_type);
771 mutex_unlock(&rdev->devlist_mtx);
772 if (result) 868 if (result)
773 goto bad_res; 869 goto bad_res;
774 } 870 }
@@ -865,6 +961,8 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
865 961
866 bad_res: 962 bad_res:
867 mutex_unlock(&rdev->mtx); 963 mutex_unlock(&rdev->mtx);
964 if (netdev)
965 dev_put(netdev);
868 unlock: 966 unlock:
869 rtnl_unlock(); 967 rtnl_unlock();
870 return result; 968 return result;
@@ -3562,9 +3660,8 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info)
3562{ 3660{
3563 struct cfg80211_registered_device *rdev; 3661 struct cfg80211_registered_device *rdev;
3564 struct net_device *dev; 3662 struct net_device *dev;
3565 struct wireless_dev *wdev;
3566 struct cfg80211_crypto_settings crypto; 3663 struct cfg80211_crypto_settings crypto;
3567 struct ieee80211_channel *chan, *fixedchan; 3664 struct ieee80211_channel *chan;
3568 const u8 *bssid, *ssid, *ie = NULL, *prev_bssid = NULL; 3665 const u8 *bssid, *ssid, *ie = NULL, *prev_bssid = NULL;
3569 int err, ssid_len, ie_len = 0; 3666 int err, ssid_len, ie_len = 0;
3570 bool use_mfp = false; 3667 bool use_mfp = false;
@@ -3607,16 +3704,6 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info)
3607 goto out; 3704 goto out;
3608 } 3705 }
3609 3706
3610 mutex_lock(&rdev->devlist_mtx);
3611 wdev = dev->ieee80211_ptr;
3612 fixedchan = rdev_fixed_channel(rdev, wdev);
3613 if (fixedchan && chan != fixedchan) {
3614 err = -EBUSY;
3615 mutex_unlock(&rdev->devlist_mtx);
3616 goto out;
3617 }
3618 mutex_unlock(&rdev->devlist_mtx);
3619
3620 ssid = nla_data(info->attrs[NL80211_ATTR_SSID]); 3707 ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
3621 ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]); 3708 ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
3622 3709
@@ -5186,6 +5273,12 @@ static struct genl_ops nl80211_ops[] = {
5186 .policy = nl80211_policy, 5273 .policy = nl80211_policy,
5187 .flags = GENL_ADMIN_PERM, 5274 .flags = GENL_ADMIN_PERM,
5188 }, 5275 },
5276 {
5277 .cmd = NL80211_CMD_SET_CHANNEL,
5278 .doit = nl80211_set_channel,
5279 .policy = nl80211_policy,
5280 .flags = GENL_ADMIN_PERM,
5281 },
5189}; 5282};
5190 5283
5191static struct genl_multicast_group nl80211_mlme_mcgrp = { 5284static struct genl_multicast_group nl80211_mlme_mcgrp = {