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 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 | ||
692 | static 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 | |||
711 | static 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 | |||
750 | static 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 | |||
691 | static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) | 770 | static 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 | ||
5190 | static struct genl_multicast_group nl80211_mlme_mcgrp = { | 5283 | static struct genl_multicast_group nl80211_mlme_mcgrp = { |