diff options
Diffstat (limited to 'net/wireless/nl80211.c')
| -rw-r--r-- | net/wireless/nl80211.c | 334 |
1 files changed, 289 insertions, 45 deletions
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 030cf153bea2..db71150b8040 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c | |||
| @@ -150,6 +150,9 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = { | |||
| 150 | .len = IEEE80211_MAX_DATA_LEN }, | 150 | .len = IEEE80211_MAX_DATA_LEN }, |
| 151 | [NL80211_ATTR_FRAME_MATCH] = { .type = NLA_BINARY, }, | 151 | [NL80211_ATTR_FRAME_MATCH] = { .type = NLA_BINARY, }, |
| 152 | [NL80211_ATTR_PS_STATE] = { .type = NLA_U32 }, | 152 | [NL80211_ATTR_PS_STATE] = { .type = NLA_U32 }, |
| 153 | [NL80211_ATTR_CQM] = { .type = NLA_NESTED, }, | ||
| 154 | [NL80211_ATTR_LOCAL_STATE_CHANGE] = { .type = NLA_FLAG }, | ||
| 155 | [NL80211_ATTR_AP_ISOLATE] = { .type = NLA_U8 }, | ||
| 153 | }; | 156 | }; |
| 154 | 157 | ||
| 155 | /* policy for the attributes */ | 158 | /* policy for the attributes */ |
| @@ -586,6 +589,7 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, | |||
| 586 | i++; | 589 | i++; |
| 587 | NLA_PUT_U32(msg, i, NL80211_CMD_SET_WIPHY_NETNS); | 590 | NLA_PUT_U32(msg, i, NL80211_CMD_SET_WIPHY_NETNS); |
| 588 | } | 591 | } |
| 592 | CMD(set_channel, SET_CHANNEL); | ||
| 589 | 593 | ||
| 590 | #undef CMD | 594 | #undef CMD |
| 591 | 595 | ||
| @@ -686,10 +690,90 @@ static int parse_txq_params(struct nlattr *tb[], | |||
| 686 | return 0; | 690 | return 0; |
| 687 | } | 691 | } |
| 688 | 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 | |||
| 689 | 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) |
| 690 | { | 772 | { |
| 691 | struct cfg80211_registered_device *rdev; | 773 | struct cfg80211_registered_device *rdev; |
| 692 | 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; | ||
| 693 | struct nlattr *nl_txq_params; | 777 | struct nlattr *nl_txq_params; |
| 694 | u32 changed; | 778 | u32 changed; |
| 695 | u8 retry_short = 0, retry_long = 0; | 779 | u8 retry_short = 0, retry_long = 0; |
| @@ -698,16 +782,50 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) | |||
| 698 | 782 | ||
| 699 | rtnl_lock(); | 783 | rtnl_lock(); |
| 700 | 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 | */ | ||
| 701 | mutex_lock(&cfg80211_mutex); | 794 | mutex_lock(&cfg80211_mutex); |
| 702 | 795 | ||
| 703 | rdev = __cfg80211_rdev_from_info(info); | 796 | if (info->attrs[NL80211_ATTR_IFINDEX]) { |
| 704 | if (IS_ERR(rdev)) { | 797 | int ifindex = nla_get_u32(info->attrs[NL80211_ATTR_IFINDEX]); |
| 705 | mutex_unlock(&cfg80211_mutex); | 798 | |
| 706 | result = PTR_ERR(rdev); | 799 | netdev = dev_get_by_index(genl_info_net(info), ifindex); |
| 707 | 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; | ||
| 708 | } | 805 | } |
| 709 | 806 | ||
| 710 | 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 | */ | ||
| 711 | 829 | ||
| 712 | if (info->attrs[NL80211_ATTR_WIPHY_NAME]) | 830 | if (info->attrs[NL80211_ATTR_WIPHY_NAME]) |
| 713 | result = cfg80211_dev_rename( | 831 | result = cfg80211_dev_rename( |
| @@ -746,26 +864,7 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) | |||
| 746 | } | 864 | } |
| 747 | 865 | ||
| 748 | if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) { | 866 | if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) { |
| 749 | enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT; | 867 | result = __nl80211_set_channel(rdev, wdev, info); |
| 750 | u32 freq; | ||
| 751 | |||
| 752 | result = -EINVAL; | ||
| 753 | |||
| 754 | if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) { | ||
| 755 | channel_type = nla_get_u32(info->attrs[ | ||
| 756 | NL80211_ATTR_WIPHY_CHANNEL_TYPE]); | ||
| 757 | if (channel_type != NL80211_CHAN_NO_HT && | ||
| 758 | channel_type != NL80211_CHAN_HT20 && | ||
| 759 | channel_type != NL80211_CHAN_HT40PLUS && | ||
| 760 | channel_type != NL80211_CHAN_HT40MINUS) | ||
| 761 | goto bad_res; | ||
| 762 | } | ||
| 763 | |||
| 764 | freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]); | ||
| 765 | |||
| 766 | mutex_lock(&rdev->devlist_mtx); | ||
| 767 | result = rdev_set_freq(rdev, NULL, freq, channel_type); | ||
| 768 | mutex_unlock(&rdev->devlist_mtx); | ||
| 769 | if (result) | 868 | if (result) |
| 770 | goto bad_res; | 869 | goto bad_res; |
| 771 | } | 870 | } |
| @@ -862,6 +961,8 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) | |||
| 862 | 961 | ||
| 863 | bad_res: | 962 | bad_res: |
| 864 | mutex_unlock(&rdev->mtx); | 963 | mutex_unlock(&rdev->mtx); |
| 964 | if (netdev) | ||
| 965 | dev_put(netdev); | ||
| 865 | unlock: | 966 | unlock: |
| 866 | rtnl_unlock(); | 967 | rtnl_unlock(); |
| 867 | return result; | 968 | return result; |
| @@ -2096,7 +2197,8 @@ static int nl80211_del_station(struct sk_buff *skb, struct genl_info *info) | |||
| 2096 | goto out_rtnl; | 2197 | goto out_rtnl; |
| 2097 | 2198 | ||
| 2098 | if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP && | 2199 | if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP && |
| 2099 | dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN) { | 2200 | dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN && |
| 2201 | dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) { | ||
| 2100 | err = -EINVAL; | 2202 | err = -EINVAL; |
| 2101 | goto out; | 2203 | goto out; |
| 2102 | } | 2204 | } |
| @@ -2439,6 +2541,7 @@ static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info) | |||
| 2439 | params.use_cts_prot = -1; | 2541 | params.use_cts_prot = -1; |
| 2440 | params.use_short_preamble = -1; | 2542 | params.use_short_preamble = -1; |
| 2441 | params.use_short_slot_time = -1; | 2543 | params.use_short_slot_time = -1; |
| 2544 | params.ap_isolate = -1; | ||
| 2442 | 2545 | ||
| 2443 | if (info->attrs[NL80211_ATTR_BSS_CTS_PROT]) | 2546 | if (info->attrs[NL80211_ATTR_BSS_CTS_PROT]) |
| 2444 | params.use_cts_prot = | 2547 | params.use_cts_prot = |
| @@ -2455,6 +2558,8 @@ static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info) | |||
| 2455 | params.basic_rates_len = | 2558 | params.basic_rates_len = |
| 2456 | nla_len(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]); | 2559 | nla_len(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]); |
| 2457 | } | 2560 | } |
| 2561 | if (info->attrs[NL80211_ATTR_AP_ISOLATE]) | ||
| 2562 | params.ap_isolate = !!nla_get_u8(info->attrs[NL80211_ATTR_AP_ISOLATE]); | ||
| 2458 | 2563 | ||
| 2459 | rtnl_lock(); | 2564 | rtnl_lock(); |
| 2460 | 2565 | ||
| @@ -3392,6 +3497,7 @@ static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info) | |||
| 3392 | int err, ssid_len, ie_len = 0; | 3497 | int err, ssid_len, ie_len = 0; |
| 3393 | enum nl80211_auth_type auth_type; | 3498 | enum nl80211_auth_type auth_type; |
| 3394 | struct key_parse key; | 3499 | struct key_parse key; |
| 3500 | bool local_state_change; | ||
| 3395 | 3501 | ||
| 3396 | if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE])) | 3502 | if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE])) |
| 3397 | return -EINVAL; | 3503 | return -EINVAL; |
| @@ -3470,9 +3576,12 @@ static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info) | |||
| 3470 | goto out; | 3576 | goto out; |
| 3471 | } | 3577 | } |
| 3472 | 3578 | ||
| 3579 | local_state_change = !!info->attrs[NL80211_ATTR_LOCAL_STATE_CHANGE]; | ||
| 3580 | |||
| 3473 | err = cfg80211_mlme_auth(rdev, dev, chan, auth_type, bssid, | 3581 | err = cfg80211_mlme_auth(rdev, dev, chan, auth_type, bssid, |
| 3474 | ssid, ssid_len, ie, ie_len, | 3582 | ssid, ssid_len, ie, ie_len, |
| 3475 | key.p.key, key.p.key_len, key.idx); | 3583 | key.p.key, key.p.key_len, key.idx, |
| 3584 | local_state_change); | ||
| 3476 | 3585 | ||
| 3477 | out: | 3586 | out: |
| 3478 | cfg80211_unlock_rdev(rdev); | 3587 | cfg80211_unlock_rdev(rdev); |
| @@ -3551,9 +3660,8 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info) | |||
| 3551 | { | 3660 | { |
| 3552 | struct cfg80211_registered_device *rdev; | 3661 | struct cfg80211_registered_device *rdev; |
| 3553 | struct net_device *dev; | 3662 | struct net_device *dev; |
| 3554 | struct wireless_dev *wdev; | ||
| 3555 | struct cfg80211_crypto_settings crypto; | 3663 | struct cfg80211_crypto_settings crypto; |
| 3556 | struct ieee80211_channel *chan, *fixedchan; | 3664 | struct ieee80211_channel *chan; |
| 3557 | const u8 *bssid, *ssid, *ie = NULL, *prev_bssid = NULL; | 3665 | const u8 *bssid, *ssid, *ie = NULL, *prev_bssid = NULL; |
| 3558 | int err, ssid_len, ie_len = 0; | 3666 | int err, ssid_len, ie_len = 0; |
| 3559 | bool use_mfp = false; | 3667 | bool use_mfp = false; |
| @@ -3596,16 +3704,6 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info) | |||
| 3596 | goto out; | 3704 | goto out; |
| 3597 | } | 3705 | } |
| 3598 | 3706 | ||
| 3599 | mutex_lock(&rdev->devlist_mtx); | ||
| 3600 | wdev = dev->ieee80211_ptr; | ||
| 3601 | fixedchan = rdev_fixed_channel(rdev, wdev); | ||
| 3602 | if (fixedchan && chan != fixedchan) { | ||
| 3603 | err = -EBUSY; | ||
| 3604 | mutex_unlock(&rdev->devlist_mtx); | ||
| 3605 | goto out; | ||
| 3606 | } | ||
| 3607 | mutex_unlock(&rdev->devlist_mtx); | ||
| 3608 | |||
| 3609 | ssid = nla_data(info->attrs[NL80211_ATTR_SSID]); | 3707 | ssid = nla_data(info->attrs[NL80211_ATTR_SSID]); |
| 3610 | ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]); | 3708 | ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]); |
| 3611 | 3709 | ||
| @@ -3649,6 +3747,7 @@ static int nl80211_deauthenticate(struct sk_buff *skb, struct genl_info *info) | |||
| 3649 | const u8 *ie = NULL, *bssid; | 3747 | const u8 *ie = NULL, *bssid; |
| 3650 | int err, ie_len = 0; | 3748 | int err, ie_len = 0; |
| 3651 | u16 reason_code; | 3749 | u16 reason_code; |
| 3750 | bool local_state_change; | ||
| 3652 | 3751 | ||
| 3653 | if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE])) | 3752 | if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE])) |
| 3654 | return -EINVAL; | 3753 | return -EINVAL; |
| @@ -3694,7 +3793,10 @@ static int nl80211_deauthenticate(struct sk_buff *skb, struct genl_info *info) | |||
| 3694 | ie_len = nla_len(info->attrs[NL80211_ATTR_IE]); | 3793 | ie_len = nla_len(info->attrs[NL80211_ATTR_IE]); |
| 3695 | } | 3794 | } |
| 3696 | 3795 | ||
| 3697 | err = cfg80211_mlme_deauth(rdev, dev, bssid, ie, ie_len, reason_code); | 3796 | local_state_change = !!info->attrs[NL80211_ATTR_LOCAL_STATE_CHANGE]; |
| 3797 | |||
| 3798 | err = cfg80211_mlme_deauth(rdev, dev, bssid, ie, ie_len, reason_code, | ||
| 3799 | local_state_change); | ||
| 3698 | 3800 | ||
| 3699 | out: | 3801 | out: |
| 3700 | cfg80211_unlock_rdev(rdev); | 3802 | cfg80211_unlock_rdev(rdev); |
| @@ -3711,6 +3813,7 @@ static int nl80211_disassociate(struct sk_buff *skb, struct genl_info *info) | |||
| 3711 | const u8 *ie = NULL, *bssid; | 3813 | const u8 *ie = NULL, *bssid; |
| 3712 | int err, ie_len = 0; | 3814 | int err, ie_len = 0; |
| 3713 | u16 reason_code; | 3815 | u16 reason_code; |
| 3816 | bool local_state_change; | ||
| 3714 | 3817 | ||
| 3715 | if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE])) | 3818 | if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE])) |
| 3716 | return -EINVAL; | 3819 | return -EINVAL; |
| @@ -3756,7 +3859,10 @@ static int nl80211_disassociate(struct sk_buff *skb, struct genl_info *info) | |||
| 3756 | ie_len = nla_len(info->attrs[NL80211_ATTR_IE]); | 3859 | ie_len = nla_len(info->attrs[NL80211_ATTR_IE]); |
| 3757 | } | 3860 | } |
| 3758 | 3861 | ||
| 3759 | err = cfg80211_mlme_disassoc(rdev, dev, bssid, ie, ie_len, reason_code); | 3862 | local_state_change = !!info->attrs[NL80211_ATTR_LOCAL_STATE_CHANGE]; |
| 3863 | |||
| 3864 | err = cfg80211_mlme_disassoc(rdev, dev, bssid, ie, ie_len, reason_code, | ||
| 3865 | local_state_change); | ||
| 3760 | 3866 | ||
| 3761 | out: | 3867 | out: |
| 3762 | cfg80211_unlock_rdev(rdev); | 3868 | cfg80211_unlock_rdev(rdev); |
| @@ -4337,9 +4443,10 @@ static int nl80211_remain_on_channel(struct sk_buff *skb, | |||
| 4337 | if (channel_type != NL80211_CHAN_NO_HT && | 4443 | if (channel_type != NL80211_CHAN_NO_HT && |
| 4338 | channel_type != NL80211_CHAN_HT20 && | 4444 | channel_type != NL80211_CHAN_HT20 && |
| 4339 | channel_type != NL80211_CHAN_HT40PLUS && | 4445 | channel_type != NL80211_CHAN_HT40PLUS && |
| 4340 | channel_type != NL80211_CHAN_HT40MINUS) | 4446 | channel_type != NL80211_CHAN_HT40MINUS) { |
| 4341 | err = -EINVAL; | 4447 | err = -EINVAL; |
| 4342 | goto out; | 4448 | goto out; |
| 4449 | } | ||
| 4343 | } | 4450 | } |
| 4344 | 4451 | ||
| 4345 | freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]); | 4452 | freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]); |
| @@ -4611,9 +4718,10 @@ static int nl80211_action(struct sk_buff *skb, struct genl_info *info) | |||
| 4611 | if (channel_type != NL80211_CHAN_NO_HT && | 4718 | if (channel_type != NL80211_CHAN_NO_HT && |
| 4612 | channel_type != NL80211_CHAN_HT20 && | 4719 | channel_type != NL80211_CHAN_HT20 && |
| 4613 | channel_type != NL80211_CHAN_HT40PLUS && | 4720 | channel_type != NL80211_CHAN_HT40PLUS && |
| 4614 | channel_type != NL80211_CHAN_HT40MINUS) | 4721 | channel_type != NL80211_CHAN_HT40MINUS) { |
| 4615 | err = -EINVAL; | 4722 | err = -EINVAL; |
| 4616 | goto out; | 4723 | goto out; |
| 4724 | } | ||
| 4617 | } | 4725 | } |
| 4618 | 4726 | ||
| 4619 | freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]); | 4727 | freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]); |
| @@ -4779,6 +4887,84 @@ unlock_rtnl: | |||
| 4779 | return err; | 4887 | return err; |
| 4780 | } | 4888 | } |
| 4781 | 4889 | ||
| 4890 | static struct nla_policy | ||
| 4891 | nl80211_attr_cqm_policy[NL80211_ATTR_CQM_MAX + 1] __read_mostly = { | ||
| 4892 | [NL80211_ATTR_CQM_RSSI_THOLD] = { .type = NLA_U32 }, | ||
| 4893 | [NL80211_ATTR_CQM_RSSI_HYST] = { .type = NLA_U32 }, | ||
| 4894 | [NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] = { .type = NLA_U32 }, | ||
| 4895 | }; | ||
| 4896 | |||
| 4897 | static int nl80211_set_cqm_rssi(struct genl_info *info, | ||
| 4898 | s32 threshold, u32 hysteresis) | ||
| 4899 | { | ||
| 4900 | struct cfg80211_registered_device *rdev; | ||
| 4901 | struct wireless_dev *wdev; | ||
| 4902 | struct net_device *dev; | ||
| 4903 | int err; | ||
| 4904 | |||
| 4905 | if (threshold > 0) | ||
| 4906 | return -EINVAL; | ||
| 4907 | |||
| 4908 | rtnl_lock(); | ||
| 4909 | |||
| 4910 | err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); | ||
| 4911 | if (err) | ||
| 4912 | goto unlock_rdev; | ||
| 4913 | |||
| 4914 | wdev = dev->ieee80211_ptr; | ||
| 4915 | |||
| 4916 | if (!rdev->ops->set_cqm_rssi_config) { | ||
| 4917 | err = -EOPNOTSUPP; | ||
| 4918 | goto unlock_rdev; | ||
| 4919 | } | ||
| 4920 | |||
| 4921 | if (wdev->iftype != NL80211_IFTYPE_STATION) { | ||
| 4922 | err = -EOPNOTSUPP; | ||
| 4923 | goto unlock_rdev; | ||
| 4924 | } | ||
| 4925 | |||
| 4926 | err = rdev->ops->set_cqm_rssi_config(wdev->wiphy, dev, | ||
| 4927 | threshold, hysteresis); | ||
| 4928 | |||
| 4929 | unlock_rdev: | ||
| 4930 | cfg80211_unlock_rdev(rdev); | ||
| 4931 | dev_put(dev); | ||
| 4932 | rtnl_unlock(); | ||
| 4933 | |||
| 4934 | return err; | ||
| 4935 | } | ||
| 4936 | |||
| 4937 | static int nl80211_set_cqm(struct sk_buff *skb, struct genl_info *info) | ||
| 4938 | { | ||
| 4939 | struct nlattr *attrs[NL80211_ATTR_CQM_MAX + 1]; | ||
| 4940 | struct nlattr *cqm; | ||
| 4941 | int err; | ||
| 4942 | |||
| 4943 | cqm = info->attrs[NL80211_ATTR_CQM]; | ||
| 4944 | if (!cqm) { | ||
| 4945 | err = -EINVAL; | ||
| 4946 | goto out; | ||
| 4947 | } | ||
| 4948 | |||
| 4949 | err = nla_parse_nested(attrs, NL80211_ATTR_CQM_MAX, cqm, | ||
| 4950 | nl80211_attr_cqm_policy); | ||
| 4951 | if (err) | ||
| 4952 | goto out; | ||
| 4953 | |||
| 4954 | if (attrs[NL80211_ATTR_CQM_RSSI_THOLD] && | ||
| 4955 | attrs[NL80211_ATTR_CQM_RSSI_HYST]) { | ||
| 4956 | s32 threshold; | ||
| 4957 | u32 hysteresis; | ||
| 4958 | threshold = nla_get_u32(attrs[NL80211_ATTR_CQM_RSSI_THOLD]); | ||
| 4959 | hysteresis = nla_get_u32(attrs[NL80211_ATTR_CQM_RSSI_HYST]); | ||
| 4960 | err = nl80211_set_cqm_rssi(info, threshold, hysteresis); | ||
| 4961 | } else | ||
| 4962 | err = -EINVAL; | ||
| 4963 | |||
| 4964 | out: | ||
| 4965 | return err; | ||
| 4966 | } | ||
| 4967 | |||
| 4782 | static struct genl_ops nl80211_ops[] = { | 4968 | static struct genl_ops nl80211_ops[] = { |
| 4783 | { | 4969 | { |
| 4784 | .cmd = NL80211_CMD_GET_WIPHY, | 4970 | .cmd = NL80211_CMD_GET_WIPHY, |
| @@ -5083,6 +5269,18 @@ static struct genl_ops nl80211_ops[] = { | |||
| 5083 | .policy = nl80211_policy, | 5269 | .policy = nl80211_policy, |
| 5084 | /* can be retrieved by unprivileged users */ | 5270 | /* can be retrieved by unprivileged users */ |
| 5085 | }, | 5271 | }, |
| 5272 | { | ||
| 5273 | .cmd = NL80211_CMD_SET_CQM, | ||
| 5274 | .doit = nl80211_set_cqm, | ||
| 5275 | .policy = nl80211_policy, | ||
| 5276 | .flags = GENL_ADMIN_PERM, | ||
| 5277 | }, | ||
| 5278 | { | ||
| 5279 | .cmd = NL80211_CMD_SET_CHANNEL, | ||
| 5280 | .doit = nl80211_set_channel, | ||
| 5281 | .policy = nl80211_policy, | ||
| 5282 | .flags = GENL_ADMIN_PERM, | ||
| 5283 | }, | ||
| 5086 | }; | 5284 | }; |
| 5087 | 5285 | ||
| 5088 | static struct genl_multicast_group nl80211_mlme_mcgrp = { | 5286 | static struct genl_multicast_group nl80211_mlme_mcgrp = { |
| @@ -5833,6 +6031,52 @@ void nl80211_send_action_tx_status(struct cfg80211_registered_device *rdev, | |||
| 5833 | nlmsg_free(msg); | 6031 | nlmsg_free(msg); |
| 5834 | } | 6032 | } |
| 5835 | 6033 | ||
| 6034 | void | ||
| 6035 | nl80211_send_cqm_rssi_notify(struct cfg80211_registered_device *rdev, | ||
| 6036 | struct net_device *netdev, | ||
| 6037 | enum nl80211_cqm_rssi_threshold_event rssi_event, | ||
| 6038 | gfp_t gfp) | ||
| 6039 | { | ||
| 6040 | struct sk_buff *msg; | ||
| 6041 | struct nlattr *pinfoattr; | ||
| 6042 | void *hdr; | ||
| 6043 | |||
| 6044 | msg = nlmsg_new(NLMSG_GOODSIZE, gfp); | ||
| 6045 | if (!msg) | ||
| 6046 | return; | ||
| 6047 | |||
| 6048 | hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_NOTIFY_CQM); | ||
| 6049 | if (!hdr) { | ||
| 6050 | nlmsg_free(msg); | ||
| 6051 | return; | ||
| 6052 | } | ||
| 6053 | |||
| 6054 | NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); | ||
| 6055 | NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex); | ||
| 6056 | |||
| 6057 | pinfoattr = nla_nest_start(msg, NL80211_ATTR_CQM); | ||
| 6058 | if (!pinfoattr) | ||
| 6059 | goto nla_put_failure; | ||
| 6060 | |||
| 6061 | NLA_PUT_U32(msg, NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT, | ||
| 6062 | rssi_event); | ||
| 6063 | |||
| 6064 | nla_nest_end(msg, pinfoattr); | ||
| 6065 | |||
| 6066 | if (genlmsg_end(msg, hdr) < 0) { | ||
| 6067 | nlmsg_free(msg); | ||
| 6068 | return; | ||
| 6069 | } | ||
| 6070 | |||
| 6071 | genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, | ||
| 6072 | nl80211_mlme_mcgrp.id, gfp); | ||
| 6073 | return; | ||
| 6074 | |||
| 6075 | nla_put_failure: | ||
| 6076 | genlmsg_cancel(msg, hdr); | ||
| 6077 | nlmsg_free(msg); | ||
| 6078 | } | ||
| 6079 | |||
| 5836 | static int nl80211_netlink_notify(struct notifier_block * nb, | 6080 | static int nl80211_netlink_notify(struct notifier_block * nb, |
| 5837 | unsigned long state, | 6081 | unsigned long state, |
| 5838 | void *_notify) | 6082 | void *_notify) |
