diff options
Diffstat (limited to 'net/wireless/nl80211.c')
| -rw-r--r-- | net/wireless/nl80211.c | 866 |
1 files changed, 778 insertions, 88 deletions
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index a6028433e3a0..e447db04cf76 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c | |||
| @@ -1,7 +1,7 @@ | |||
| 1 | /* | 1 | /* |
| 2 | * This is the new netlink-based wireless configuration interface. | 2 | * This is the new netlink-based wireless configuration interface. |
| 3 | * | 3 | * |
| 4 | * Copyright 2006-2009 Johannes Berg <johannes@sipsolutions.net> | 4 | * Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net> |
| 5 | */ | 5 | */ |
| 6 | 6 | ||
| 7 | #include <linux/if.h> | 7 | #include <linux/if.h> |
| @@ -58,7 +58,7 @@ static int get_rdev_dev_by_info_ifindex(struct genl_info *info, | |||
| 58 | } | 58 | } |
| 59 | 59 | ||
| 60 | /* policy for the attributes */ | 60 | /* policy for the attributes */ |
| 61 | static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = { | 61 | static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = { |
| 62 | [NL80211_ATTR_WIPHY] = { .type = NLA_U32 }, | 62 | [NL80211_ATTR_WIPHY] = { .type = NLA_U32 }, |
| 63 | [NL80211_ATTR_WIPHY_NAME] = { .type = NLA_NUL_STRING, | 63 | [NL80211_ATTR_WIPHY_NAME] = { .type = NLA_NUL_STRING, |
| 64 | .len = 20-1 }, | 64 | .len = 20-1 }, |
| @@ -69,6 +69,7 @@ static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = { | |||
| 69 | [NL80211_ATTR_WIPHY_RETRY_LONG] = { .type = NLA_U8 }, | 69 | [NL80211_ATTR_WIPHY_RETRY_LONG] = { .type = NLA_U8 }, |
| 70 | [NL80211_ATTR_WIPHY_FRAG_THRESHOLD] = { .type = NLA_U32 }, | 70 | [NL80211_ATTR_WIPHY_FRAG_THRESHOLD] = { .type = NLA_U32 }, |
| 71 | [NL80211_ATTR_WIPHY_RTS_THRESHOLD] = { .type = NLA_U32 }, | 71 | [NL80211_ATTR_WIPHY_RTS_THRESHOLD] = { .type = NLA_U32 }, |
| 72 | [NL80211_ATTR_WIPHY_COVERAGE_CLASS] = { .type = NLA_U8 }, | ||
| 72 | 73 | ||
| 73 | [NL80211_ATTR_IFTYPE] = { .type = NLA_U32 }, | 74 | [NL80211_ATTR_IFTYPE] = { .type = NLA_U32 }, |
| 74 | [NL80211_ATTR_IFINDEX] = { .type = NLA_U32 }, | 75 | [NL80211_ATTR_IFINDEX] = { .type = NLA_U32 }, |
| @@ -141,11 +142,17 @@ static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = { | |||
| 141 | [NL80211_ATTR_4ADDR] = { .type = NLA_U8 }, | 142 | [NL80211_ATTR_4ADDR] = { .type = NLA_U8 }, |
| 142 | [NL80211_ATTR_PMKID] = { .type = NLA_BINARY, | 143 | [NL80211_ATTR_PMKID] = { .type = NLA_BINARY, |
| 143 | .len = WLAN_PMKID_LEN }, | 144 | .len = WLAN_PMKID_LEN }, |
| 145 | [NL80211_ATTR_DURATION] = { .type = NLA_U32 }, | ||
| 146 | [NL80211_ATTR_COOKIE] = { .type = NLA_U64 }, | ||
| 147 | [NL80211_ATTR_TX_RATES] = { .type = NLA_NESTED }, | ||
| 148 | [NL80211_ATTR_FRAME] = { .type = NLA_BINARY, | ||
| 149 | .len = IEEE80211_MAX_DATA_LEN }, | ||
| 150 | [NL80211_ATTR_FRAME_MATCH] = { .type = NLA_BINARY, }, | ||
| 151 | [NL80211_ATTR_PS_STATE] = { .type = NLA_U32 }, | ||
| 144 | }; | 152 | }; |
| 145 | 153 | ||
| 146 | /* policy for the attributes */ | 154 | /* policy for the attributes */ |
| 147 | static struct nla_policy | 155 | static const struct nla_policy nl80211_key_policy[NL80211_KEY_MAX + 1] = { |
| 148 | nl80211_key_policy[NL80211_KEY_MAX + 1] __read_mostly = { | ||
| 149 | [NL80211_KEY_DATA] = { .type = NLA_BINARY, .len = WLAN_MAX_KEY_LEN }, | 156 | [NL80211_KEY_DATA] = { .type = NLA_BINARY, .len = WLAN_MAX_KEY_LEN }, |
| 150 | [NL80211_KEY_IDX] = { .type = NLA_U8 }, | 157 | [NL80211_KEY_IDX] = { .type = NLA_U8 }, |
| 151 | [NL80211_KEY_CIPHER] = { .type = NLA_U32 }, | 158 | [NL80211_KEY_CIPHER] = { .type = NLA_U32 }, |
| @@ -442,6 +449,8 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, | |||
| 442 | dev->wiphy.frag_threshold); | 449 | dev->wiphy.frag_threshold); |
| 443 | NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_RTS_THRESHOLD, | 450 | NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_RTS_THRESHOLD, |
| 444 | dev->wiphy.rts_threshold); | 451 | dev->wiphy.rts_threshold); |
| 452 | NLA_PUT_U8(msg, NL80211_ATTR_WIPHY_COVERAGE_CLASS, | ||
| 453 | dev->wiphy.coverage_class); | ||
| 445 | 454 | ||
| 446 | NLA_PUT_U8(msg, NL80211_ATTR_MAX_NUM_SCAN_SSIDS, | 455 | NLA_PUT_U8(msg, NL80211_ATTR_MAX_NUM_SCAN_SSIDS, |
| 447 | dev->wiphy.max_scan_ssids); | 456 | dev->wiphy.max_scan_ssids); |
| @@ -569,6 +578,9 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, | |||
| 569 | CMD(set_pmksa, SET_PMKSA); | 578 | CMD(set_pmksa, SET_PMKSA); |
| 570 | CMD(del_pmksa, DEL_PMKSA); | 579 | CMD(del_pmksa, DEL_PMKSA); |
| 571 | CMD(flush_pmksa, FLUSH_PMKSA); | 580 | CMD(flush_pmksa, FLUSH_PMKSA); |
| 581 | CMD(remain_on_channel, REMAIN_ON_CHANNEL); | ||
| 582 | CMD(set_bitrate_mask, SET_TX_BITRATE_MASK); | ||
| 583 | CMD(action, ACTION); | ||
| 572 | if (dev->wiphy.flags & WIPHY_FLAG_NETNS_OK) { | 584 | if (dev->wiphy.flags & WIPHY_FLAG_NETNS_OK) { |
| 573 | i++; | 585 | i++; |
| 574 | NLA_PUT_U32(msg, i, NL80211_CMD_SET_WIPHY_NETNS); | 586 | NLA_PUT_U32(msg, i, NL80211_CMD_SET_WIPHY_NETNS); |
| @@ -681,6 +693,7 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) | |||
| 681 | u32 changed; | 693 | u32 changed; |
| 682 | u8 retry_short = 0, retry_long = 0; | 694 | u8 retry_short = 0, retry_long = 0; |
| 683 | u32 frag_threshold = 0, rts_threshold = 0; | 695 | u32 frag_threshold = 0, rts_threshold = 0; |
| 696 | u8 coverage_class = 0; | ||
| 684 | 697 | ||
| 685 | rtnl_lock(); | 698 | rtnl_lock(); |
| 686 | 699 | ||
| @@ -803,9 +816,16 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) | |||
| 803 | changed |= WIPHY_PARAM_RTS_THRESHOLD; | 816 | changed |= WIPHY_PARAM_RTS_THRESHOLD; |
| 804 | } | 817 | } |
| 805 | 818 | ||
| 819 | if (info->attrs[NL80211_ATTR_WIPHY_COVERAGE_CLASS]) { | ||
| 820 | coverage_class = nla_get_u8( | ||
| 821 | info->attrs[NL80211_ATTR_WIPHY_COVERAGE_CLASS]); | ||
| 822 | changed |= WIPHY_PARAM_COVERAGE_CLASS; | ||
| 823 | } | ||
| 824 | |||
| 806 | if (changed) { | 825 | if (changed) { |
| 807 | u8 old_retry_short, old_retry_long; | 826 | u8 old_retry_short, old_retry_long; |
| 808 | u32 old_frag_threshold, old_rts_threshold; | 827 | u32 old_frag_threshold, old_rts_threshold; |
| 828 | u8 old_coverage_class; | ||
| 809 | 829 | ||
| 810 | if (!rdev->ops->set_wiphy_params) { | 830 | if (!rdev->ops->set_wiphy_params) { |
| 811 | result = -EOPNOTSUPP; | 831 | result = -EOPNOTSUPP; |
| @@ -816,6 +836,7 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) | |||
| 816 | old_retry_long = rdev->wiphy.retry_long; | 836 | old_retry_long = rdev->wiphy.retry_long; |
| 817 | old_frag_threshold = rdev->wiphy.frag_threshold; | 837 | old_frag_threshold = rdev->wiphy.frag_threshold; |
| 818 | old_rts_threshold = rdev->wiphy.rts_threshold; | 838 | old_rts_threshold = rdev->wiphy.rts_threshold; |
| 839 | old_coverage_class = rdev->wiphy.coverage_class; | ||
| 819 | 840 | ||
| 820 | if (changed & WIPHY_PARAM_RETRY_SHORT) | 841 | if (changed & WIPHY_PARAM_RETRY_SHORT) |
| 821 | rdev->wiphy.retry_short = retry_short; | 842 | rdev->wiphy.retry_short = retry_short; |
| @@ -825,6 +846,8 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) | |||
| 825 | rdev->wiphy.frag_threshold = frag_threshold; | 846 | rdev->wiphy.frag_threshold = frag_threshold; |
| 826 | if (changed & WIPHY_PARAM_RTS_THRESHOLD) | 847 | if (changed & WIPHY_PARAM_RTS_THRESHOLD) |
| 827 | rdev->wiphy.rts_threshold = rts_threshold; | 848 | rdev->wiphy.rts_threshold = rts_threshold; |
| 849 | if (changed & WIPHY_PARAM_COVERAGE_CLASS) | ||
| 850 | rdev->wiphy.coverage_class = coverage_class; | ||
| 828 | 851 | ||
| 829 | result = rdev->ops->set_wiphy_params(&rdev->wiphy, changed); | 852 | result = rdev->ops->set_wiphy_params(&rdev->wiphy, changed); |
| 830 | if (result) { | 853 | if (result) { |
| @@ -832,6 +855,7 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) | |||
| 832 | rdev->wiphy.retry_long = old_retry_long; | 855 | rdev->wiphy.retry_long = old_retry_long; |
| 833 | rdev->wiphy.frag_threshold = old_frag_threshold; | 856 | rdev->wiphy.frag_threshold = old_frag_threshold; |
| 834 | rdev->wiphy.rts_threshold = old_rts_threshold; | 857 | rdev->wiphy.rts_threshold = old_rts_threshold; |
| 858 | rdev->wiphy.coverage_class = old_coverage_class; | ||
| 835 | } | 859 | } |
| 836 | } | 860 | } |
| 837 | 861 | ||
| @@ -1637,42 +1661,9 @@ static int parse_station_flags(struct genl_info *info, | |||
| 1637 | return 0; | 1661 | return 0; |
| 1638 | } | 1662 | } |
| 1639 | 1663 | ||
| 1640 | static u16 nl80211_calculate_bitrate(struct rate_info *rate) | ||
| 1641 | { | ||
| 1642 | int modulation, streams, bitrate; | ||
| 1643 | |||
| 1644 | if (!(rate->flags & RATE_INFO_FLAGS_MCS)) | ||
| 1645 | return rate->legacy; | ||
| 1646 | |||
| 1647 | /* the formula below does only work for MCS values smaller than 32 */ | ||
| 1648 | if (rate->mcs >= 32) | ||
| 1649 | return 0; | ||
| 1650 | |||
| 1651 | modulation = rate->mcs & 7; | ||
| 1652 | streams = (rate->mcs >> 3) + 1; | ||
| 1653 | |||
| 1654 | bitrate = (rate->flags & RATE_INFO_FLAGS_40_MHZ_WIDTH) ? | ||
| 1655 | 13500000 : 6500000; | ||
| 1656 | |||
| 1657 | if (modulation < 4) | ||
| 1658 | bitrate *= (modulation + 1); | ||
| 1659 | else if (modulation == 4) | ||
| 1660 | bitrate *= (modulation + 2); | ||
| 1661 | else | ||
| 1662 | bitrate *= (modulation + 3); | ||
| 1663 | |||
| 1664 | bitrate *= streams; | ||
| 1665 | |||
| 1666 | if (rate->flags & RATE_INFO_FLAGS_SHORT_GI) | ||
| 1667 | bitrate = (bitrate / 9) * 10; | ||
| 1668 | |||
| 1669 | /* do NOT round down here */ | ||
| 1670 | return (bitrate + 50000) / 100000; | ||
| 1671 | } | ||
| 1672 | |||
| 1673 | static int nl80211_send_station(struct sk_buff *msg, u32 pid, u32 seq, | 1664 | static int nl80211_send_station(struct sk_buff *msg, u32 pid, u32 seq, |
| 1674 | int flags, struct net_device *dev, | 1665 | int flags, struct net_device *dev, |
| 1675 | u8 *mac_addr, struct station_info *sinfo) | 1666 | const u8 *mac_addr, struct station_info *sinfo) |
| 1676 | { | 1667 | { |
| 1677 | void *hdr; | 1668 | void *hdr; |
| 1678 | struct nlattr *sinfoattr, *txrate; | 1669 | struct nlattr *sinfoattr, *txrate; |
| @@ -1716,8 +1707,8 @@ static int nl80211_send_station(struct sk_buff *msg, u32 pid, u32 seq, | |||
| 1716 | if (!txrate) | 1707 | if (!txrate) |
| 1717 | goto nla_put_failure; | 1708 | goto nla_put_failure; |
| 1718 | 1709 | ||
| 1719 | /* nl80211_calculate_bitrate will return 0 for mcs >= 32 */ | 1710 | /* cfg80211_calculate_bitrate will return 0 for mcs >= 32 */ |
| 1720 | bitrate = nl80211_calculate_bitrate(&sinfo->txrate); | 1711 | bitrate = cfg80211_calculate_bitrate(&sinfo->txrate); |
| 1721 | if (bitrate > 0) | 1712 | if (bitrate > 0) |
| 1722 | NLA_PUT_U16(msg, NL80211_RATE_INFO_BITRATE, bitrate); | 1713 | NLA_PUT_U16(msg, NL80211_RATE_INFO_BITRATE, bitrate); |
| 1723 | 1714 | ||
| @@ -2023,6 +2014,9 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info) | |||
| 2023 | if (!info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]) | 2014 | if (!info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]) |
| 2024 | return -EINVAL; | 2015 | return -EINVAL; |
| 2025 | 2016 | ||
| 2017 | if (!info->attrs[NL80211_ATTR_STA_AID]) | ||
| 2018 | return -EINVAL; | ||
| 2019 | |||
| 2026 | mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]); | 2020 | mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]); |
| 2027 | params.supported_rates = | 2021 | params.supported_rates = |
| 2028 | nla_data(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]); | 2022 | nla_data(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]); |
| @@ -2031,11 +2025,9 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info) | |||
| 2031 | params.listen_interval = | 2025 | params.listen_interval = |
| 2032 | nla_get_u16(info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]); | 2026 | nla_get_u16(info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]); |
| 2033 | 2027 | ||
| 2034 | if (info->attrs[NL80211_ATTR_STA_AID]) { | 2028 | params.aid = nla_get_u16(info->attrs[NL80211_ATTR_STA_AID]); |
| 2035 | params.aid = nla_get_u16(info->attrs[NL80211_ATTR_STA_AID]); | 2029 | if (!params.aid || params.aid > IEEE80211_MAX_AID) |
| 2036 | if (!params.aid || params.aid > IEEE80211_MAX_AID) | 2030 | return -EINVAL; |
| 2037 | return -EINVAL; | ||
| 2038 | } | ||
| 2039 | 2031 | ||
| 2040 | if (info->attrs[NL80211_ATTR_HT_CAPABILITY]) | 2032 | if (info->attrs[NL80211_ATTR_HT_CAPABILITY]) |
| 2041 | params.ht_capa = | 2033 | params.ht_capa = |
| @@ -2050,6 +2042,12 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info) | |||
| 2050 | if (err) | 2042 | if (err) |
| 2051 | goto out_rtnl; | 2043 | goto out_rtnl; |
| 2052 | 2044 | ||
| 2045 | if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP && | ||
| 2046 | dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN) { | ||
| 2047 | err = -EINVAL; | ||
| 2048 | goto out; | ||
| 2049 | } | ||
| 2050 | |||
| 2053 | err = get_vlan(info, rdev, ¶ms.vlan); | 2051 | err = get_vlan(info, rdev, ¶ms.vlan); |
| 2054 | if (err) | 2052 | if (err) |
| 2055 | goto out; | 2053 | goto out; |
| @@ -2057,35 +2055,6 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info) | |||
| 2057 | /* validate settings */ | 2055 | /* validate settings */ |
| 2058 | err = 0; | 2056 | err = 0; |
| 2059 | 2057 | ||
| 2060 | switch (dev->ieee80211_ptr->iftype) { | ||
| 2061 | case NL80211_IFTYPE_AP: | ||
| 2062 | case NL80211_IFTYPE_AP_VLAN: | ||
| 2063 | /* all ok but must have AID */ | ||
| 2064 | if (!params.aid) | ||
| 2065 | err = -EINVAL; | ||
| 2066 | break; | ||
| 2067 | case NL80211_IFTYPE_MESH_POINT: | ||
| 2068 | /* disallow things mesh doesn't support */ | ||
| 2069 | if (params.vlan) | ||
| 2070 | err = -EINVAL; | ||
| 2071 | if (params.aid) | ||
| 2072 | err = -EINVAL; | ||
| 2073 | if (params.ht_capa) | ||
| 2074 | err = -EINVAL; | ||
| 2075 | if (params.listen_interval >= 0) | ||
| 2076 | err = -EINVAL; | ||
| 2077 | if (params.supported_rates) | ||
| 2078 | err = -EINVAL; | ||
| 2079 | if (params.sta_flags_mask) | ||
| 2080 | err = -EINVAL; | ||
| 2081 | break; | ||
| 2082 | default: | ||
| 2083 | err = -EINVAL; | ||
| 2084 | } | ||
| 2085 | |||
| 2086 | if (err) | ||
| 2087 | goto out; | ||
| 2088 | |||
| 2089 | if (!rdev->ops->add_station) { | 2058 | if (!rdev->ops->add_station) { |
| 2090 | err = -EOPNOTSUPP; | 2059 | err = -EOPNOTSUPP; |
| 2091 | goto out; | 2060 | goto out; |
| @@ -2126,8 +2095,7 @@ static int nl80211_del_station(struct sk_buff *skb, struct genl_info *info) | |||
| 2126 | goto out_rtnl; | 2095 | goto out_rtnl; |
| 2127 | 2096 | ||
| 2128 | if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP && | 2097 | if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP && |
| 2129 | dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN && | 2098 | dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN) { |
| 2130 | dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) { | ||
| 2131 | err = -EINVAL; | 2099 | err = -EINVAL; |
| 2132 | goto out; | 2100 | goto out; |
| 2133 | } | 2101 | } |
| @@ -2514,8 +2482,7 @@ static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info) | |||
| 2514 | return err; | 2482 | return err; |
| 2515 | } | 2483 | } |
| 2516 | 2484 | ||
| 2517 | static const struct nla_policy | 2485 | static const struct nla_policy reg_rule_policy[NL80211_REG_RULE_ATTR_MAX + 1] = { |
| 2518 | reg_rule_policy[NL80211_REG_RULE_ATTR_MAX + 1] = { | ||
| 2519 | [NL80211_ATTR_REG_RULE_FLAGS] = { .type = NLA_U32 }, | 2486 | [NL80211_ATTR_REG_RULE_FLAGS] = { .type = NLA_U32 }, |
| 2520 | [NL80211_ATTR_FREQ_RANGE_START] = { .type = NLA_U32 }, | 2487 | [NL80211_ATTR_FREQ_RANGE_START] = { .type = NLA_U32 }, |
| 2521 | [NL80211_ATTR_FREQ_RANGE_END] = { .type = NLA_U32 }, | 2488 | [NL80211_ATTR_FREQ_RANGE_END] = { .type = NLA_U32 }, |
| @@ -2583,12 +2550,6 @@ static int nl80211_req_set_reg(struct sk_buff *skb, struct genl_info *info) | |||
| 2583 | 2550 | ||
| 2584 | data = nla_data(info->attrs[NL80211_ATTR_REG_ALPHA2]); | 2551 | data = nla_data(info->attrs[NL80211_ATTR_REG_ALPHA2]); |
| 2585 | 2552 | ||
| 2586 | #ifdef CONFIG_WIRELESS_OLD_REGULATORY | ||
| 2587 | /* We ignore world regdom requests with the old regdom setup */ | ||
| 2588 | if (is_world_regdom(data)) | ||
| 2589 | return -EINVAL; | ||
| 2590 | #endif | ||
| 2591 | |||
| 2592 | r = regulatory_hint_user(data); | 2553 | r = regulatory_hint_user(data); |
| 2593 | 2554 | ||
| 2594 | return r; | 2555 | return r; |
| @@ -2690,8 +2651,7 @@ do {\ | |||
| 2690 | } \ | 2651 | } \ |
| 2691 | } while (0);\ | 2652 | } while (0);\ |
| 2692 | 2653 | ||
| 2693 | static struct nla_policy | 2654 | static const struct nla_policy nl80211_meshconf_params_policy[NL80211_MESHCONF_ATTR_MAX+1] = { |
| 2694 | nl80211_meshconf_params_policy[NL80211_MESHCONF_ATTR_MAX+1] __read_mostly = { | ||
| 2695 | [NL80211_MESHCONF_RETRY_TIMEOUT] = { .type = NLA_U16 }, | 2655 | [NL80211_MESHCONF_RETRY_TIMEOUT] = { .type = NLA_U16 }, |
| 2696 | [NL80211_MESHCONF_CONFIRM_TIMEOUT] = { .type = NLA_U16 }, | 2656 | [NL80211_MESHCONF_CONFIRM_TIMEOUT] = { .type = NLA_U16 }, |
| 2697 | [NL80211_MESHCONF_HOLDING_TIMEOUT] = { .type = NLA_U16 }, | 2657 | [NL80211_MESHCONF_HOLDING_TIMEOUT] = { .type = NLA_U16 }, |
| @@ -3182,6 +3142,10 @@ static int nl80211_send_bss(struct sk_buff *msg, u32 pid, u32 seq, int flags, | |||
| 3182 | NLA_PUT(msg, NL80211_BSS_INFORMATION_ELEMENTS, | 3142 | NLA_PUT(msg, NL80211_BSS_INFORMATION_ELEMENTS, |
| 3183 | res->len_information_elements, | 3143 | res->len_information_elements, |
| 3184 | res->information_elements); | 3144 | res->information_elements); |
| 3145 | if (res->beacon_ies && res->len_beacon_ies && | ||
| 3146 | res->beacon_ies != res->information_elements) | ||
| 3147 | NLA_PUT(msg, NL80211_BSS_BEACON_IES, | ||
| 3148 | res->len_beacon_ies, res->beacon_ies); | ||
| 3185 | if (res->tsf) | 3149 | if (res->tsf) |
| 3186 | NLA_PUT_U64(msg, NL80211_BSS_TSF, res->tsf); | 3150 | NLA_PUT_U64(msg, NL80211_BSS_TSF, res->tsf); |
| 3187 | if (res->beacon_interval) | 3151 | if (res->beacon_interval) |
| @@ -3586,6 +3550,7 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info) | |||
| 3586 | { | 3550 | { |
| 3587 | struct cfg80211_registered_device *rdev; | 3551 | struct cfg80211_registered_device *rdev; |
| 3588 | struct net_device *dev; | 3552 | struct net_device *dev; |
| 3553 | struct wireless_dev *wdev; | ||
| 3589 | struct cfg80211_crypto_settings crypto; | 3554 | struct cfg80211_crypto_settings crypto; |
| 3590 | struct ieee80211_channel *chan, *fixedchan; | 3555 | struct ieee80211_channel *chan, *fixedchan; |
| 3591 | const u8 *bssid, *ssid, *ie = NULL, *prev_bssid = NULL; | 3556 | const u8 *bssid, *ssid, *ie = NULL, *prev_bssid = NULL; |
| @@ -3631,7 +3596,8 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info) | |||
| 3631 | } | 3596 | } |
| 3632 | 3597 | ||
| 3633 | mutex_lock(&rdev->devlist_mtx); | 3598 | mutex_lock(&rdev->devlist_mtx); |
| 3634 | fixedchan = rdev_fixed_channel(rdev, NULL); | 3599 | wdev = dev->ieee80211_ptr; |
| 3600 | fixedchan = rdev_fixed_channel(rdev, wdev); | ||
| 3635 | if (fixedchan && chan != fixedchan) { | 3601 | if (fixedchan && chan != fixedchan) { |
| 3636 | err = -EBUSY; | 3602 | err = -EBUSY; |
| 3637 | mutex_unlock(&rdev->devlist_mtx); | 3603 | mutex_unlock(&rdev->devlist_mtx); |
| @@ -4322,6 +4288,496 @@ static int nl80211_flush_pmksa(struct sk_buff *skb, struct genl_info *info) | |||
| 4322 | 4288 | ||
| 4323 | } | 4289 | } |
| 4324 | 4290 | ||
| 4291 | static int nl80211_remain_on_channel(struct sk_buff *skb, | ||
| 4292 | struct genl_info *info) | ||
| 4293 | { | ||
| 4294 | struct cfg80211_registered_device *rdev; | ||
| 4295 | struct net_device *dev; | ||
| 4296 | struct ieee80211_channel *chan; | ||
| 4297 | struct sk_buff *msg; | ||
| 4298 | void *hdr; | ||
| 4299 | u64 cookie; | ||
| 4300 | enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT; | ||
| 4301 | u32 freq, duration; | ||
| 4302 | int err; | ||
| 4303 | |||
| 4304 | if (!info->attrs[NL80211_ATTR_WIPHY_FREQ] || | ||
| 4305 | !info->attrs[NL80211_ATTR_DURATION]) | ||
| 4306 | return -EINVAL; | ||
| 4307 | |||
| 4308 | duration = nla_get_u32(info->attrs[NL80211_ATTR_DURATION]); | ||
| 4309 | |||
| 4310 | /* | ||
| 4311 | * We should be on that channel for at least one jiffie, | ||
| 4312 | * and more than 5 seconds seems excessive. | ||
| 4313 | */ | ||
| 4314 | if (!duration || !msecs_to_jiffies(duration) || duration > 5000) | ||
| 4315 | return -EINVAL; | ||
| 4316 | |||
| 4317 | rtnl_lock(); | ||
| 4318 | |||
| 4319 | err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); | ||
| 4320 | if (err) | ||
| 4321 | goto unlock_rtnl; | ||
| 4322 | |||
| 4323 | if (!rdev->ops->remain_on_channel) { | ||
| 4324 | err = -EOPNOTSUPP; | ||
| 4325 | goto out; | ||
| 4326 | } | ||
| 4327 | |||
| 4328 | if (!netif_running(dev)) { | ||
| 4329 | err = -ENETDOWN; | ||
| 4330 | goto out; | ||
| 4331 | } | ||
| 4332 | |||
| 4333 | if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) { | ||
| 4334 | channel_type = nla_get_u32( | ||
| 4335 | info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]); | ||
| 4336 | if (channel_type != NL80211_CHAN_NO_HT && | ||
| 4337 | channel_type != NL80211_CHAN_HT20 && | ||
| 4338 | channel_type != NL80211_CHAN_HT40PLUS && | ||
| 4339 | channel_type != NL80211_CHAN_HT40MINUS) | ||
| 4340 | err = -EINVAL; | ||
| 4341 | goto out; | ||
| 4342 | } | ||
| 4343 | |||
| 4344 | freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]); | ||
| 4345 | chan = rdev_freq_to_chan(rdev, freq, channel_type); | ||
| 4346 | if (chan == NULL) { | ||
| 4347 | err = -EINVAL; | ||
| 4348 | goto out; | ||
| 4349 | } | ||
| 4350 | |||
| 4351 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); | ||
| 4352 | if (!msg) { | ||
| 4353 | err = -ENOMEM; | ||
| 4354 | goto out; | ||
| 4355 | } | ||
| 4356 | |||
| 4357 | hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0, | ||
| 4358 | NL80211_CMD_REMAIN_ON_CHANNEL); | ||
| 4359 | |||
| 4360 | if (IS_ERR(hdr)) { | ||
| 4361 | err = PTR_ERR(hdr); | ||
| 4362 | goto free_msg; | ||
| 4363 | } | ||
| 4364 | |||
| 4365 | err = rdev->ops->remain_on_channel(&rdev->wiphy, dev, chan, | ||
| 4366 | channel_type, duration, &cookie); | ||
| 4367 | |||
| 4368 | if (err) | ||
| 4369 | goto free_msg; | ||
| 4370 | |||
| 4371 | NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, cookie); | ||
| 4372 | |||
| 4373 | genlmsg_end(msg, hdr); | ||
| 4374 | err = genlmsg_reply(msg, info); | ||
| 4375 | goto out; | ||
| 4376 | |||
| 4377 | nla_put_failure: | ||
| 4378 | err = -ENOBUFS; | ||
| 4379 | free_msg: | ||
| 4380 | nlmsg_free(msg); | ||
| 4381 | out: | ||
| 4382 | cfg80211_unlock_rdev(rdev); | ||
| 4383 | dev_put(dev); | ||
| 4384 | unlock_rtnl: | ||
| 4385 | rtnl_unlock(); | ||
| 4386 | return err; | ||
| 4387 | } | ||
| 4388 | |||
| 4389 | static int nl80211_cancel_remain_on_channel(struct sk_buff *skb, | ||
| 4390 | struct genl_info *info) | ||
| 4391 | { | ||
| 4392 | struct cfg80211_registered_device *rdev; | ||
| 4393 | struct net_device *dev; | ||
| 4394 | u64 cookie; | ||
| 4395 | int err; | ||
| 4396 | |||
| 4397 | if (!info->attrs[NL80211_ATTR_COOKIE]) | ||
| 4398 | return -EINVAL; | ||
| 4399 | |||
| 4400 | rtnl_lock(); | ||
| 4401 | |||
| 4402 | err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); | ||
| 4403 | if (err) | ||
| 4404 | goto unlock_rtnl; | ||
| 4405 | |||
| 4406 | if (!rdev->ops->cancel_remain_on_channel) { | ||
| 4407 | err = -EOPNOTSUPP; | ||
| 4408 | goto out; | ||
| 4409 | } | ||
| 4410 | |||
| 4411 | if (!netif_running(dev)) { | ||
| 4412 | err = -ENETDOWN; | ||
| 4413 | goto out; | ||
| 4414 | } | ||
| 4415 | |||
| 4416 | cookie = nla_get_u64(info->attrs[NL80211_ATTR_COOKIE]); | ||
| 4417 | |||
| 4418 | err = rdev->ops->cancel_remain_on_channel(&rdev->wiphy, dev, cookie); | ||
| 4419 | |||
| 4420 | out: | ||
| 4421 | cfg80211_unlock_rdev(rdev); | ||
| 4422 | dev_put(dev); | ||
| 4423 | unlock_rtnl: | ||
| 4424 | rtnl_unlock(); | ||
| 4425 | return err; | ||
| 4426 | } | ||
| 4427 | |||
| 4428 | static u32 rateset_to_mask(struct ieee80211_supported_band *sband, | ||
| 4429 | u8 *rates, u8 rates_len) | ||
| 4430 | { | ||
| 4431 | u8 i; | ||
| 4432 | u32 mask = 0; | ||
| 4433 | |||
| 4434 | for (i = 0; i < rates_len; i++) { | ||
| 4435 | int rate = (rates[i] & 0x7f) * 5; | ||
| 4436 | int ridx; | ||
| 4437 | for (ridx = 0; ridx < sband->n_bitrates; ridx++) { | ||
| 4438 | struct ieee80211_rate *srate = | ||
| 4439 | &sband->bitrates[ridx]; | ||
| 4440 | if (rate == srate->bitrate) { | ||
| 4441 | mask |= 1 << ridx; | ||
| 4442 | break; | ||
| 4443 | } | ||
| 4444 | } | ||
| 4445 | if (ridx == sband->n_bitrates) | ||
| 4446 | return 0; /* rate not found */ | ||
| 4447 | } | ||
| 4448 | |||
| 4449 | return mask; | ||
| 4450 | } | ||
| 4451 | |||
| 4452 | static const struct nla_policy nl80211_txattr_policy[NL80211_TXRATE_MAX + 1] = { | ||
| 4453 | [NL80211_TXRATE_LEGACY] = { .type = NLA_BINARY, | ||
| 4454 | .len = NL80211_MAX_SUPP_RATES }, | ||
| 4455 | }; | ||
| 4456 | |||
| 4457 | static int nl80211_set_tx_bitrate_mask(struct sk_buff *skb, | ||
| 4458 | struct genl_info *info) | ||
| 4459 | { | ||
| 4460 | struct nlattr *tb[NL80211_TXRATE_MAX + 1]; | ||
| 4461 | struct cfg80211_registered_device *rdev; | ||
| 4462 | struct cfg80211_bitrate_mask mask; | ||
| 4463 | int err, rem, i; | ||
| 4464 | struct net_device *dev; | ||
| 4465 | struct nlattr *tx_rates; | ||
| 4466 | struct ieee80211_supported_band *sband; | ||
| 4467 | |||
| 4468 | if (info->attrs[NL80211_ATTR_TX_RATES] == NULL) | ||
| 4469 | return -EINVAL; | ||
| 4470 | |||
| 4471 | rtnl_lock(); | ||
| 4472 | |||
| 4473 | err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); | ||
| 4474 | if (err) | ||
| 4475 | goto unlock_rtnl; | ||
| 4476 | |||
| 4477 | if (!rdev->ops->set_bitrate_mask) { | ||
| 4478 | err = -EOPNOTSUPP; | ||
| 4479 | goto unlock; | ||
| 4480 | } | ||
| 4481 | |||
| 4482 | memset(&mask, 0, sizeof(mask)); | ||
| 4483 | /* Default to all rates enabled */ | ||
| 4484 | for (i = 0; i < IEEE80211_NUM_BANDS; i++) { | ||
| 4485 | sband = rdev->wiphy.bands[i]; | ||
| 4486 | mask.control[i].legacy = | ||
| 4487 | sband ? (1 << sband->n_bitrates) - 1 : 0; | ||
| 4488 | } | ||
| 4489 | |||
| 4490 | /* | ||
| 4491 | * The nested attribute uses enum nl80211_band as the index. This maps | ||
| 4492 | * directly to the enum ieee80211_band values used in cfg80211. | ||
| 4493 | */ | ||
| 4494 | nla_for_each_nested(tx_rates, info->attrs[NL80211_ATTR_TX_RATES], rem) | ||
| 4495 | { | ||
| 4496 | enum ieee80211_band band = nla_type(tx_rates); | ||
| 4497 | if (band < 0 || band >= IEEE80211_NUM_BANDS) { | ||
| 4498 | err = -EINVAL; | ||
| 4499 | goto unlock; | ||
| 4500 | } | ||
| 4501 | sband = rdev->wiphy.bands[band]; | ||
| 4502 | if (sband == NULL) { | ||
| 4503 | err = -EINVAL; | ||
| 4504 | goto unlock; | ||
| 4505 | } | ||
| 4506 | nla_parse(tb, NL80211_TXRATE_MAX, nla_data(tx_rates), | ||
| 4507 | nla_len(tx_rates), nl80211_txattr_policy); | ||
| 4508 | if (tb[NL80211_TXRATE_LEGACY]) { | ||
| 4509 | mask.control[band].legacy = rateset_to_mask( | ||
| 4510 | sband, | ||
| 4511 | nla_data(tb[NL80211_TXRATE_LEGACY]), | ||
| 4512 | nla_len(tb[NL80211_TXRATE_LEGACY])); | ||
| 4513 | if (mask.control[band].legacy == 0) { | ||
| 4514 | err = -EINVAL; | ||
| 4515 | goto unlock; | ||
| 4516 | } | ||
| 4517 | } | ||
| 4518 | } | ||
| 4519 | |||
| 4520 | err = rdev->ops->set_bitrate_mask(&rdev->wiphy, dev, NULL, &mask); | ||
| 4521 | |||
| 4522 | unlock: | ||
| 4523 | dev_put(dev); | ||
| 4524 | cfg80211_unlock_rdev(rdev); | ||
| 4525 | unlock_rtnl: | ||
| 4526 | rtnl_unlock(); | ||
| 4527 | return err; | ||
| 4528 | } | ||
| 4529 | |||
| 4530 | static int nl80211_register_action(struct sk_buff *skb, struct genl_info *info) | ||
| 4531 | { | ||
| 4532 | struct cfg80211_registered_device *rdev; | ||
| 4533 | struct net_device *dev; | ||
| 4534 | int err; | ||
| 4535 | |||
| 4536 | if (!info->attrs[NL80211_ATTR_FRAME_MATCH]) | ||
| 4537 | return -EINVAL; | ||
| 4538 | |||
| 4539 | if (nla_len(info->attrs[NL80211_ATTR_FRAME_MATCH]) < 1) | ||
| 4540 | return -EINVAL; | ||
| 4541 | |||
| 4542 | rtnl_lock(); | ||
| 4543 | |||
| 4544 | err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); | ||
| 4545 | if (err) | ||
| 4546 | goto unlock_rtnl; | ||
| 4547 | |||
| 4548 | if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) { | ||
| 4549 | err = -EOPNOTSUPP; | ||
| 4550 | goto out; | ||
| 4551 | } | ||
| 4552 | |||
| 4553 | /* not much point in registering if we can't reply */ | ||
| 4554 | if (!rdev->ops->action) { | ||
| 4555 | err = -EOPNOTSUPP; | ||
| 4556 | goto out; | ||
| 4557 | } | ||
| 4558 | |||
| 4559 | err = cfg80211_mlme_register_action(dev->ieee80211_ptr, info->snd_pid, | ||
| 4560 | nla_data(info->attrs[NL80211_ATTR_FRAME_MATCH]), | ||
| 4561 | nla_len(info->attrs[NL80211_ATTR_FRAME_MATCH])); | ||
| 4562 | out: | ||
| 4563 | cfg80211_unlock_rdev(rdev); | ||
| 4564 | dev_put(dev); | ||
| 4565 | unlock_rtnl: | ||
| 4566 | rtnl_unlock(); | ||
| 4567 | return err; | ||
| 4568 | } | ||
| 4569 | |||
| 4570 | static int nl80211_action(struct sk_buff *skb, struct genl_info *info) | ||
| 4571 | { | ||
| 4572 | struct cfg80211_registered_device *rdev; | ||
| 4573 | struct net_device *dev; | ||
| 4574 | struct ieee80211_channel *chan; | ||
| 4575 | enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT; | ||
| 4576 | u32 freq; | ||
| 4577 | int err; | ||
| 4578 | void *hdr; | ||
| 4579 | u64 cookie; | ||
| 4580 | struct sk_buff *msg; | ||
| 4581 | |||
| 4582 | if (!info->attrs[NL80211_ATTR_FRAME] || | ||
| 4583 | !info->attrs[NL80211_ATTR_WIPHY_FREQ]) | ||
| 4584 | return -EINVAL; | ||
| 4585 | |||
| 4586 | rtnl_lock(); | ||
| 4587 | |||
| 4588 | err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); | ||
| 4589 | if (err) | ||
| 4590 | goto unlock_rtnl; | ||
| 4591 | |||
| 4592 | if (!rdev->ops->action) { | ||
| 4593 | err = -EOPNOTSUPP; | ||
| 4594 | goto out; | ||
| 4595 | } | ||
| 4596 | |||
| 4597 | if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) { | ||
| 4598 | err = -EOPNOTSUPP; | ||
| 4599 | goto out; | ||
| 4600 | } | ||
| 4601 | |||
| 4602 | if (!netif_running(dev)) { | ||
| 4603 | err = -ENETDOWN; | ||
| 4604 | goto out; | ||
| 4605 | } | ||
| 4606 | |||
| 4607 | if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) { | ||
| 4608 | channel_type = nla_get_u32( | ||
| 4609 | info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]); | ||
| 4610 | if (channel_type != NL80211_CHAN_NO_HT && | ||
| 4611 | channel_type != NL80211_CHAN_HT20 && | ||
| 4612 | channel_type != NL80211_CHAN_HT40PLUS && | ||
| 4613 | channel_type != NL80211_CHAN_HT40MINUS) | ||
| 4614 | err = -EINVAL; | ||
| 4615 | goto out; | ||
| 4616 | } | ||
| 4617 | |||
| 4618 | freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]); | ||
| 4619 | chan = rdev_freq_to_chan(rdev, freq, channel_type); | ||
| 4620 | if (chan == NULL) { | ||
| 4621 | err = -EINVAL; | ||
| 4622 | goto out; | ||
| 4623 | } | ||
| 4624 | |||
| 4625 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); | ||
| 4626 | if (!msg) { | ||
| 4627 | err = -ENOMEM; | ||
| 4628 | goto out; | ||
| 4629 | } | ||
| 4630 | |||
| 4631 | hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0, | ||
| 4632 | NL80211_CMD_ACTION); | ||
| 4633 | |||
| 4634 | if (IS_ERR(hdr)) { | ||
| 4635 | err = PTR_ERR(hdr); | ||
| 4636 | goto free_msg; | ||
| 4637 | } | ||
| 4638 | err = cfg80211_mlme_action(rdev, dev, chan, channel_type, | ||
| 4639 | nla_data(info->attrs[NL80211_ATTR_FRAME]), | ||
| 4640 | nla_len(info->attrs[NL80211_ATTR_FRAME]), | ||
| 4641 | &cookie); | ||
| 4642 | if (err) | ||
| 4643 | goto free_msg; | ||
| 4644 | |||
| 4645 | NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, cookie); | ||
| 4646 | |||
| 4647 | genlmsg_end(msg, hdr); | ||
| 4648 | err = genlmsg_reply(msg, info); | ||
| 4649 | goto out; | ||
| 4650 | |||
| 4651 | nla_put_failure: | ||
| 4652 | err = -ENOBUFS; | ||
| 4653 | free_msg: | ||
| 4654 | nlmsg_free(msg); | ||
| 4655 | out: | ||
| 4656 | cfg80211_unlock_rdev(rdev); | ||
| 4657 | dev_put(dev); | ||
| 4658 | unlock_rtnl: | ||
| 4659 | rtnl_unlock(); | ||
| 4660 | return err; | ||
| 4661 | } | ||
| 4662 | |||
| 4663 | static int nl80211_set_power_save(struct sk_buff *skb, struct genl_info *info) | ||
| 4664 | { | ||
| 4665 | struct cfg80211_registered_device *rdev; | ||
| 4666 | struct wireless_dev *wdev; | ||
| 4667 | struct net_device *dev; | ||
| 4668 | u8 ps_state; | ||
| 4669 | bool state; | ||
| 4670 | int err; | ||
| 4671 | |||
| 4672 | if (!info->attrs[NL80211_ATTR_PS_STATE]) { | ||
| 4673 | err = -EINVAL; | ||
| 4674 | goto out; | ||
| 4675 | } | ||
| 4676 | |||
| 4677 | ps_state = nla_get_u32(info->attrs[NL80211_ATTR_PS_STATE]); | ||
| 4678 | |||
| 4679 | if (ps_state != NL80211_PS_DISABLED && ps_state != NL80211_PS_ENABLED) { | ||
| 4680 | err = -EINVAL; | ||
| 4681 | goto out; | ||
| 4682 | } | ||
| 4683 | |||
| 4684 | rtnl_lock(); | ||
| 4685 | |||
| 4686 | err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); | ||
| 4687 | if (err) | ||
| 4688 | goto unlock_rdev; | ||
| 4689 | |||
| 4690 | wdev = dev->ieee80211_ptr; | ||
| 4691 | |||
| 4692 | if (!rdev->ops->set_power_mgmt) { | ||
| 4693 | err = -EOPNOTSUPP; | ||
| 4694 | goto unlock_rdev; | ||
| 4695 | } | ||
| 4696 | |||
| 4697 | state = (ps_state == NL80211_PS_ENABLED) ? true : false; | ||
| 4698 | |||
| 4699 | if (state == wdev->ps) | ||
| 4700 | goto unlock_rdev; | ||
| 4701 | |||
| 4702 | wdev->ps = state; | ||
| 4703 | |||
| 4704 | if (rdev->ops->set_power_mgmt(wdev->wiphy, dev, wdev->ps, | ||
| 4705 | wdev->ps_timeout)) | ||
| 4706 | /* assume this means it's off */ | ||
| 4707 | wdev->ps = false; | ||
| 4708 | |||
| 4709 | unlock_rdev: | ||
| 4710 | cfg80211_unlock_rdev(rdev); | ||
| 4711 | dev_put(dev); | ||
| 4712 | rtnl_unlock(); | ||
| 4713 | |||
| 4714 | out: | ||
| 4715 | return err; | ||
| 4716 | } | ||
| 4717 | |||
| 4718 | static int nl80211_get_power_save(struct sk_buff *skb, struct genl_info *info) | ||
| 4719 | { | ||
| 4720 | struct cfg80211_registered_device *rdev; | ||
| 4721 | enum nl80211_ps_state ps_state; | ||
| 4722 | struct wireless_dev *wdev; | ||
| 4723 | struct net_device *dev; | ||
| 4724 | struct sk_buff *msg; | ||
| 4725 | void *hdr; | ||
| 4726 | int err; | ||
| 4727 | |||
| 4728 | rtnl_lock(); | ||
| 4729 | |||
| 4730 | err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); | ||
| 4731 | if (err) | ||
| 4732 | goto unlock_rtnl; | ||
| 4733 | |||
| 4734 | wdev = dev->ieee80211_ptr; | ||
| 4735 | |||
| 4736 | if (!rdev->ops->set_power_mgmt) { | ||
| 4737 | err = -EOPNOTSUPP; | ||
| 4738 | goto out; | ||
| 4739 | } | ||
| 4740 | |||
| 4741 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); | ||
| 4742 | if (!msg) { | ||
| 4743 | err = -ENOMEM; | ||
| 4744 | goto out; | ||
| 4745 | } | ||
| 4746 | |||
| 4747 | hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0, | ||
| 4748 | NL80211_CMD_GET_POWER_SAVE); | ||
| 4749 | if (!hdr) { | ||
| 4750 | err = -ENOMEM; | ||
| 4751 | goto free_msg; | ||
| 4752 | } | ||
| 4753 | |||
| 4754 | if (wdev->ps) | ||
| 4755 | ps_state = NL80211_PS_ENABLED; | ||
| 4756 | else | ||
| 4757 | ps_state = NL80211_PS_DISABLED; | ||
| 4758 | |||
| 4759 | NLA_PUT_U32(msg, NL80211_ATTR_PS_STATE, ps_state); | ||
| 4760 | |||
| 4761 | genlmsg_end(msg, hdr); | ||
| 4762 | err = genlmsg_reply(msg, info); | ||
| 4763 | goto out; | ||
| 4764 | |||
| 4765 | nla_put_failure: | ||
| 4766 | err = -ENOBUFS; | ||
| 4767 | |||
| 4768 | free_msg: | ||
| 4769 | nlmsg_free(msg); | ||
| 4770 | |||
| 4771 | out: | ||
| 4772 | cfg80211_unlock_rdev(rdev); | ||
| 4773 | dev_put(dev); | ||
| 4774 | |||
| 4775 | unlock_rtnl: | ||
| 4776 | rtnl_unlock(); | ||
| 4777 | |||
| 4778 | return err; | ||
| 4779 | } | ||
| 4780 | |||
| 4325 | static struct genl_ops nl80211_ops[] = { | 4781 | static struct genl_ops nl80211_ops[] = { |
| 4326 | { | 4782 | { |
| 4327 | .cmd = NL80211_CMD_GET_WIPHY, | 4783 | .cmd = NL80211_CMD_GET_WIPHY, |
| @@ -4584,8 +5040,50 @@ static struct genl_ops nl80211_ops[] = { | |||
| 4584 | .policy = nl80211_policy, | 5040 | .policy = nl80211_policy, |
| 4585 | .flags = GENL_ADMIN_PERM, | 5041 | .flags = GENL_ADMIN_PERM, |
| 4586 | }, | 5042 | }, |
| 4587 | 5043 | { | |
| 5044 | .cmd = NL80211_CMD_REMAIN_ON_CHANNEL, | ||
| 5045 | .doit = nl80211_remain_on_channel, | ||
| 5046 | .policy = nl80211_policy, | ||
| 5047 | .flags = GENL_ADMIN_PERM, | ||
| 5048 | }, | ||
| 5049 | { | ||
| 5050 | .cmd = NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL, | ||
| 5051 | .doit = nl80211_cancel_remain_on_channel, | ||
| 5052 | .policy = nl80211_policy, | ||
| 5053 | .flags = GENL_ADMIN_PERM, | ||
| 5054 | }, | ||
| 5055 | { | ||
| 5056 | .cmd = NL80211_CMD_SET_TX_BITRATE_MASK, | ||
| 5057 | .doit = nl80211_set_tx_bitrate_mask, | ||
| 5058 | .policy = nl80211_policy, | ||
| 5059 | .flags = GENL_ADMIN_PERM, | ||
| 5060 | }, | ||
| 5061 | { | ||
| 5062 | .cmd = NL80211_CMD_REGISTER_ACTION, | ||
| 5063 | .doit = nl80211_register_action, | ||
| 5064 | .policy = nl80211_policy, | ||
| 5065 | .flags = GENL_ADMIN_PERM, | ||
| 5066 | }, | ||
| 5067 | { | ||
| 5068 | .cmd = NL80211_CMD_ACTION, | ||
| 5069 | .doit = nl80211_action, | ||
| 5070 | .policy = nl80211_policy, | ||
| 5071 | .flags = GENL_ADMIN_PERM, | ||
| 5072 | }, | ||
| 5073 | { | ||
| 5074 | .cmd = NL80211_CMD_SET_POWER_SAVE, | ||
| 5075 | .doit = nl80211_set_power_save, | ||
| 5076 | .policy = nl80211_policy, | ||
| 5077 | .flags = GENL_ADMIN_PERM, | ||
| 5078 | }, | ||
| 5079 | { | ||
| 5080 | .cmd = NL80211_CMD_GET_POWER_SAVE, | ||
| 5081 | .doit = nl80211_get_power_save, | ||
| 5082 | .policy = nl80211_policy, | ||
| 5083 | /* can be retrieved by unprivileged users */ | ||
| 5084 | }, | ||
| 4588 | }; | 5085 | }; |
| 5086 | |||
| 4589 | static struct genl_multicast_group nl80211_mlme_mcgrp = { | 5087 | static struct genl_multicast_group nl80211_mlme_mcgrp = { |
| 4590 | .name = "mlme", | 5088 | .name = "mlme", |
| 4591 | }; | 5089 | }; |
| @@ -5173,6 +5671,193 @@ nla_put_failure: | |||
| 5173 | nlmsg_free(msg); | 5671 | nlmsg_free(msg); |
| 5174 | } | 5672 | } |
| 5175 | 5673 | ||
| 5674 | static void nl80211_send_remain_on_chan_event( | ||
| 5675 | int cmd, struct cfg80211_registered_device *rdev, | ||
| 5676 | struct net_device *netdev, u64 cookie, | ||
| 5677 | struct ieee80211_channel *chan, | ||
| 5678 | enum nl80211_channel_type channel_type, | ||
| 5679 | unsigned int duration, gfp_t gfp) | ||
| 5680 | { | ||
| 5681 | struct sk_buff *msg; | ||
| 5682 | void *hdr; | ||
| 5683 | |||
| 5684 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); | ||
| 5685 | if (!msg) | ||
| 5686 | return; | ||
| 5687 | |||
| 5688 | hdr = nl80211hdr_put(msg, 0, 0, 0, cmd); | ||
| 5689 | if (!hdr) { | ||
| 5690 | nlmsg_free(msg); | ||
| 5691 | return; | ||
| 5692 | } | ||
| 5693 | |||
| 5694 | NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); | ||
| 5695 | NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex); | ||
| 5696 | NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, chan->center_freq); | ||
| 5697 | NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, channel_type); | ||
| 5698 | NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, cookie); | ||
| 5699 | |||
| 5700 | if (cmd == NL80211_CMD_REMAIN_ON_CHANNEL) | ||
| 5701 | NLA_PUT_U32(msg, NL80211_ATTR_DURATION, duration); | ||
| 5702 | |||
| 5703 | if (genlmsg_end(msg, hdr) < 0) { | ||
| 5704 | nlmsg_free(msg); | ||
| 5705 | return; | ||
| 5706 | } | ||
| 5707 | |||
| 5708 | genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, | ||
| 5709 | nl80211_mlme_mcgrp.id, gfp); | ||
| 5710 | return; | ||
| 5711 | |||
| 5712 | nla_put_failure: | ||
| 5713 | genlmsg_cancel(msg, hdr); | ||
| 5714 | nlmsg_free(msg); | ||
| 5715 | } | ||
| 5716 | |||
| 5717 | void nl80211_send_remain_on_channel(struct cfg80211_registered_device *rdev, | ||
| 5718 | struct net_device *netdev, u64 cookie, | ||
| 5719 | struct ieee80211_channel *chan, | ||
| 5720 | enum nl80211_channel_type channel_type, | ||
| 5721 | unsigned int duration, gfp_t gfp) | ||
| 5722 | { | ||
| 5723 | nl80211_send_remain_on_chan_event(NL80211_CMD_REMAIN_ON_CHANNEL, | ||
| 5724 | rdev, netdev, cookie, chan, | ||
| 5725 | channel_type, duration, gfp); | ||
| 5726 | } | ||
| 5727 | |||
| 5728 | void nl80211_send_remain_on_channel_cancel( | ||
| 5729 | struct cfg80211_registered_device *rdev, struct net_device *netdev, | ||
| 5730 | u64 cookie, struct ieee80211_channel *chan, | ||
| 5731 | enum nl80211_channel_type channel_type, gfp_t gfp) | ||
| 5732 | { | ||
| 5733 | nl80211_send_remain_on_chan_event(NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL, | ||
| 5734 | rdev, netdev, cookie, chan, | ||
| 5735 | channel_type, 0, gfp); | ||
| 5736 | } | ||
| 5737 | |||
| 5738 | void nl80211_send_sta_event(struct cfg80211_registered_device *rdev, | ||
| 5739 | struct net_device *dev, const u8 *mac_addr, | ||
| 5740 | struct station_info *sinfo, gfp_t gfp) | ||
| 5741 | { | ||
| 5742 | struct sk_buff *msg; | ||
| 5743 | |||
| 5744 | msg = nlmsg_new(NLMSG_GOODSIZE, gfp); | ||
| 5745 | if (!msg) | ||
| 5746 | return; | ||
| 5747 | |||
| 5748 | if (nl80211_send_station(msg, 0, 0, 0, dev, mac_addr, sinfo) < 0) { | ||
| 5749 | nlmsg_free(msg); | ||
| 5750 | return; | ||
| 5751 | } | ||
| 5752 | |||
| 5753 | genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, | ||
| 5754 | nl80211_mlme_mcgrp.id, gfp); | ||
| 5755 | } | ||
| 5756 | |||
| 5757 | int nl80211_send_action(struct cfg80211_registered_device *rdev, | ||
| 5758 | struct net_device *netdev, u32 nlpid, | ||
| 5759 | int freq, const u8 *buf, size_t len, gfp_t gfp) | ||
| 5760 | { | ||
| 5761 | struct sk_buff *msg; | ||
| 5762 | void *hdr; | ||
| 5763 | int err; | ||
| 5764 | |||
| 5765 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); | ||
| 5766 | if (!msg) | ||
| 5767 | return -ENOMEM; | ||
| 5768 | |||
| 5769 | hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_ACTION); | ||
| 5770 | if (!hdr) { | ||
| 5771 | nlmsg_free(msg); | ||
| 5772 | return -ENOMEM; | ||
| 5773 | } | ||
| 5774 | |||
| 5775 | NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); | ||
| 5776 | NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex); | ||
| 5777 | NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq); | ||
| 5778 | NLA_PUT(msg, NL80211_ATTR_FRAME, len, buf); | ||
| 5779 | |||
| 5780 | err = genlmsg_end(msg, hdr); | ||
| 5781 | if (err < 0) { | ||
| 5782 | nlmsg_free(msg); | ||
| 5783 | return err; | ||
| 5784 | } | ||
| 5785 | |||
| 5786 | err = genlmsg_unicast(wiphy_net(&rdev->wiphy), msg, nlpid); | ||
| 5787 | if (err < 0) | ||
| 5788 | return err; | ||
| 5789 | return 0; | ||
| 5790 | |||
| 5791 | nla_put_failure: | ||
| 5792 | genlmsg_cancel(msg, hdr); | ||
| 5793 | nlmsg_free(msg); | ||
| 5794 | return -ENOBUFS; | ||
| 5795 | } | ||
| 5796 | |||
| 5797 | void nl80211_send_action_tx_status(struct cfg80211_registered_device *rdev, | ||
| 5798 | struct net_device *netdev, u64 cookie, | ||
| 5799 | const u8 *buf, size_t len, bool ack, | ||
| 5800 | gfp_t gfp) | ||
| 5801 | { | ||
| 5802 | struct sk_buff *msg; | ||
| 5803 | void *hdr; | ||
| 5804 | |||
| 5805 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); | ||
| 5806 | if (!msg) | ||
| 5807 | return; | ||
| 5808 | |||
| 5809 | hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_ACTION_TX_STATUS); | ||
| 5810 | if (!hdr) { | ||
| 5811 | nlmsg_free(msg); | ||
| 5812 | return; | ||
| 5813 | } | ||
| 5814 | |||
| 5815 | NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); | ||
| 5816 | NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex); | ||
| 5817 | NLA_PUT(msg, NL80211_ATTR_FRAME, len, buf); | ||
| 5818 | NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, cookie); | ||
| 5819 | if (ack) | ||
| 5820 | NLA_PUT_FLAG(msg, NL80211_ATTR_ACK); | ||
| 5821 | |||
| 5822 | if (genlmsg_end(msg, hdr) < 0) { | ||
| 5823 | nlmsg_free(msg); | ||
| 5824 | return; | ||
| 5825 | } | ||
| 5826 | |||
| 5827 | genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, gfp); | ||
| 5828 | return; | ||
| 5829 | |||
| 5830 | nla_put_failure: | ||
| 5831 | genlmsg_cancel(msg, hdr); | ||
| 5832 | nlmsg_free(msg); | ||
| 5833 | } | ||
| 5834 | |||
| 5835 | static int nl80211_netlink_notify(struct notifier_block * nb, | ||
| 5836 | unsigned long state, | ||
| 5837 | void *_notify) | ||
| 5838 | { | ||
| 5839 | struct netlink_notify *notify = _notify; | ||
| 5840 | struct cfg80211_registered_device *rdev; | ||
| 5841 | struct wireless_dev *wdev; | ||
| 5842 | |||
| 5843 | if (state != NETLINK_URELEASE) | ||
| 5844 | return NOTIFY_DONE; | ||
| 5845 | |||
| 5846 | rcu_read_lock(); | ||
| 5847 | |||
| 5848 | list_for_each_entry_rcu(rdev, &cfg80211_rdev_list, list) | ||
| 5849 | list_for_each_entry_rcu(wdev, &rdev->netdev_list, list) | ||
| 5850 | cfg80211_mlme_unregister_actions(wdev, notify->pid); | ||
| 5851 | |||
| 5852 | rcu_read_unlock(); | ||
| 5853 | |||
| 5854 | return NOTIFY_DONE; | ||
| 5855 | } | ||
| 5856 | |||
| 5857 | static struct notifier_block nl80211_netlink_notifier = { | ||
| 5858 | .notifier_call = nl80211_netlink_notify, | ||
| 5859 | }; | ||
| 5860 | |||
| 5176 | /* initialisation/exit functions */ | 5861 | /* initialisation/exit functions */ |
| 5177 | 5862 | ||
| 5178 | int nl80211_init(void) | 5863 | int nl80211_init(void) |
| @@ -5206,6 +5891,10 @@ int nl80211_init(void) | |||
| 5206 | goto err_out; | 5891 | goto err_out; |
| 5207 | #endif | 5892 | #endif |
| 5208 | 5893 | ||
| 5894 | err = netlink_register_notifier(&nl80211_netlink_notifier); | ||
| 5895 | if (err) | ||
| 5896 | goto err_out; | ||
| 5897 | |||
| 5209 | return 0; | 5898 | return 0; |
| 5210 | err_out: | 5899 | err_out: |
| 5211 | genl_unregister_family(&nl80211_fam); | 5900 | genl_unregister_family(&nl80211_fam); |
| @@ -5214,5 +5903,6 @@ int nl80211_init(void) | |||
| 5214 | 5903 | ||
| 5215 | void nl80211_exit(void) | 5904 | void nl80211_exit(void) |
| 5216 | { | 5905 | { |
| 5906 | netlink_unregister_notifier(&nl80211_netlink_notifier); | ||
| 5217 | genl_unregister_family(&nl80211_fam); | 5907 | genl_unregister_family(&nl80211_fam); |
| 5218 | } | 5908 | } |
