diff options
Diffstat (limited to 'net/wireless/nl80211.c')
-rw-r--r-- | net/wireless/nl80211.c | 171 |
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 | ||
693 | static 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 | |||
712 | static 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 | |||
751 | static 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 | |||
692 | static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) | 771 | static 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 | ||
5191 | static struct genl_multicast_group nl80211_mlme_mcgrp = { | 5284 | static struct genl_multicast_group nl80211_mlme_mcgrp = { |