diff options
Diffstat (limited to 'net/wireless/nl80211.c')
-rw-r--r-- | net/wireless/nl80211.c | 1271 |
1 files changed, 1127 insertions, 144 deletions
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index ca3c92a0a14f..030cf153bea2 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c | |||
@@ -1,12 +1,13 @@ | |||
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> |
8 | #include <linux/module.h> | 8 | #include <linux/module.h> |
9 | #include <linux/err.h> | 9 | #include <linux/err.h> |
10 | #include <linux/slab.h> | ||
10 | #include <linux/list.h> | 11 | #include <linux/list.h> |
11 | #include <linux/if_ether.h> | 12 | #include <linux/if_ether.h> |
12 | #include <linux/ieee80211.h> | 13 | #include <linux/ieee80211.h> |
@@ -58,7 +59,7 @@ static int get_rdev_dev_by_info_ifindex(struct genl_info *info, | |||
58 | } | 59 | } |
59 | 60 | ||
60 | /* policy for the attributes */ | 61 | /* policy for the attributes */ |
61 | static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = { | 62 | static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = { |
62 | [NL80211_ATTR_WIPHY] = { .type = NLA_U32 }, | 63 | [NL80211_ATTR_WIPHY] = { .type = NLA_U32 }, |
63 | [NL80211_ATTR_WIPHY_NAME] = { .type = NLA_NUL_STRING, | 64 | [NL80211_ATTR_WIPHY_NAME] = { .type = NLA_NUL_STRING, |
64 | .len = 20-1 }, | 65 | .len = 20-1 }, |
@@ -69,6 +70,7 @@ static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = { | |||
69 | [NL80211_ATTR_WIPHY_RETRY_LONG] = { .type = NLA_U8 }, | 70 | [NL80211_ATTR_WIPHY_RETRY_LONG] = { .type = NLA_U8 }, |
70 | [NL80211_ATTR_WIPHY_FRAG_THRESHOLD] = { .type = NLA_U32 }, | 71 | [NL80211_ATTR_WIPHY_FRAG_THRESHOLD] = { .type = NLA_U32 }, |
71 | [NL80211_ATTR_WIPHY_RTS_THRESHOLD] = { .type = NLA_U32 }, | 72 | [NL80211_ATTR_WIPHY_RTS_THRESHOLD] = { .type = NLA_U32 }, |
73 | [NL80211_ATTR_WIPHY_COVERAGE_CLASS] = { .type = NLA_U8 }, | ||
72 | 74 | ||
73 | [NL80211_ATTR_IFTYPE] = { .type = NLA_U32 }, | 75 | [NL80211_ATTR_IFTYPE] = { .type = NLA_U32 }, |
74 | [NL80211_ATTR_IFINDEX] = { .type = NLA_U32 }, | 76 | [NL80211_ATTR_IFINDEX] = { .type = NLA_U32 }, |
@@ -138,11 +140,20 @@ static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = { | |||
138 | [NL80211_ATTR_CIPHER_SUITE_GROUP] = { .type = NLA_U32 }, | 140 | [NL80211_ATTR_CIPHER_SUITE_GROUP] = { .type = NLA_U32 }, |
139 | [NL80211_ATTR_WPA_VERSIONS] = { .type = NLA_U32 }, | 141 | [NL80211_ATTR_WPA_VERSIONS] = { .type = NLA_U32 }, |
140 | [NL80211_ATTR_PID] = { .type = NLA_U32 }, | 142 | [NL80211_ATTR_PID] = { .type = NLA_U32 }, |
143 | [NL80211_ATTR_4ADDR] = { .type = NLA_U8 }, | ||
144 | [NL80211_ATTR_PMKID] = { .type = NLA_BINARY, | ||
145 | .len = WLAN_PMKID_LEN }, | ||
146 | [NL80211_ATTR_DURATION] = { .type = NLA_U32 }, | ||
147 | [NL80211_ATTR_COOKIE] = { .type = NLA_U64 }, | ||
148 | [NL80211_ATTR_TX_RATES] = { .type = NLA_NESTED }, | ||
149 | [NL80211_ATTR_FRAME] = { .type = NLA_BINARY, | ||
150 | .len = IEEE80211_MAX_DATA_LEN }, | ||
151 | [NL80211_ATTR_FRAME_MATCH] = { .type = NLA_BINARY, }, | ||
152 | [NL80211_ATTR_PS_STATE] = { .type = NLA_U32 }, | ||
141 | }; | 153 | }; |
142 | 154 | ||
143 | /* policy for the attributes */ | 155 | /* policy for the attributes */ |
144 | static struct nla_policy | 156 | static const struct nla_policy nl80211_key_policy[NL80211_KEY_MAX + 1] = { |
145 | nl80211_key_policy[NL80211_KEY_MAX + 1] __read_mostly = { | ||
146 | [NL80211_KEY_DATA] = { .type = NLA_BINARY, .len = WLAN_MAX_KEY_LEN }, | 157 | [NL80211_KEY_DATA] = { .type = NLA_BINARY, .len = WLAN_MAX_KEY_LEN }, |
147 | [NL80211_KEY_IDX] = { .type = NLA_U8 }, | 158 | [NL80211_KEY_IDX] = { .type = NLA_U8 }, |
148 | [NL80211_KEY_CIPHER] = { .type = NLA_U32 }, | 159 | [NL80211_KEY_CIPHER] = { .type = NLA_U32 }, |
@@ -151,6 +162,26 @@ nl80211_key_policy[NL80211_KEY_MAX + 1] __read_mostly = { | |||
151 | [NL80211_KEY_DEFAULT_MGMT] = { .type = NLA_FLAG }, | 162 | [NL80211_KEY_DEFAULT_MGMT] = { .type = NLA_FLAG }, |
152 | }; | 163 | }; |
153 | 164 | ||
165 | /* ifidx get helper */ | ||
166 | static int nl80211_get_ifidx(struct netlink_callback *cb) | ||
167 | { | ||
168 | int res; | ||
169 | |||
170 | res = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize, | ||
171 | nl80211_fam.attrbuf, nl80211_fam.maxattr, | ||
172 | nl80211_policy); | ||
173 | if (res) | ||
174 | return res; | ||
175 | |||
176 | if (!nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX]) | ||
177 | return -EINVAL; | ||
178 | |||
179 | res = nla_get_u32(nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX]); | ||
180 | if (!res) | ||
181 | return -EINVAL; | ||
182 | return res; | ||
183 | } | ||
184 | |||
154 | /* IE validation */ | 185 | /* IE validation */ |
155 | static bool is_valid_ie_attr(const struct nlattr *attr) | 186 | static bool is_valid_ie_attr(const struct nlattr *attr) |
156 | { | 187 | { |
@@ -419,6 +450,8 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, | |||
419 | dev->wiphy.frag_threshold); | 450 | dev->wiphy.frag_threshold); |
420 | NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_RTS_THRESHOLD, | 451 | NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_RTS_THRESHOLD, |
421 | dev->wiphy.rts_threshold); | 452 | dev->wiphy.rts_threshold); |
453 | NLA_PUT_U8(msg, NL80211_ATTR_WIPHY_COVERAGE_CLASS, | ||
454 | dev->wiphy.coverage_class); | ||
422 | 455 | ||
423 | NLA_PUT_U8(msg, NL80211_ATTR_MAX_NUM_SCAN_SSIDS, | 456 | NLA_PUT_U8(msg, NL80211_ATTR_MAX_NUM_SCAN_SSIDS, |
424 | dev->wiphy.max_scan_ssids); | 457 | dev->wiphy.max_scan_ssids); |
@@ -429,6 +462,9 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, | |||
429 | sizeof(u32) * dev->wiphy.n_cipher_suites, | 462 | sizeof(u32) * dev->wiphy.n_cipher_suites, |
430 | dev->wiphy.cipher_suites); | 463 | dev->wiphy.cipher_suites); |
431 | 464 | ||
465 | NLA_PUT_U8(msg, NL80211_ATTR_MAX_NUM_PMKIDS, | ||
466 | dev->wiphy.max_num_pmkids); | ||
467 | |||
432 | nl_modes = nla_nest_start(msg, NL80211_ATTR_SUPPORTED_IFTYPES); | 468 | nl_modes = nla_nest_start(msg, NL80211_ATTR_SUPPORTED_IFTYPES); |
433 | if (!nl_modes) | 469 | if (!nl_modes) |
434 | goto nla_put_failure; | 470 | goto nla_put_failure; |
@@ -540,7 +576,13 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, | |||
540 | CMD(deauth, DEAUTHENTICATE); | 576 | CMD(deauth, DEAUTHENTICATE); |
541 | CMD(disassoc, DISASSOCIATE); | 577 | CMD(disassoc, DISASSOCIATE); |
542 | CMD(join_ibss, JOIN_IBSS); | 578 | CMD(join_ibss, JOIN_IBSS); |
543 | if (dev->wiphy.netnsok) { | 579 | CMD(set_pmksa, SET_PMKSA); |
580 | CMD(del_pmksa, DEL_PMKSA); | ||
581 | CMD(flush_pmksa, FLUSH_PMKSA); | ||
582 | CMD(remain_on_channel, REMAIN_ON_CHANNEL); | ||
583 | CMD(set_bitrate_mask, SET_TX_BITRATE_MASK); | ||
584 | CMD(action, ACTION); | ||
585 | if (dev->wiphy.flags & WIPHY_FLAG_NETNS_OK) { | ||
544 | i++; | 586 | i++; |
545 | NLA_PUT_U32(msg, i, NL80211_CMD_SET_WIPHY_NETNS); | 587 | NLA_PUT_U32(msg, i, NL80211_CMD_SET_WIPHY_NETNS); |
546 | } | 588 | } |
@@ -652,6 +694,7 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) | |||
652 | u32 changed; | 694 | u32 changed; |
653 | u8 retry_short = 0, retry_long = 0; | 695 | u8 retry_short = 0, retry_long = 0; |
654 | u32 frag_threshold = 0, rts_threshold = 0; | 696 | u32 frag_threshold = 0, rts_threshold = 0; |
697 | u8 coverage_class = 0; | ||
655 | 698 | ||
656 | rtnl_lock(); | 699 | rtnl_lock(); |
657 | 700 | ||
@@ -774,9 +817,16 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) | |||
774 | changed |= WIPHY_PARAM_RTS_THRESHOLD; | 817 | changed |= WIPHY_PARAM_RTS_THRESHOLD; |
775 | } | 818 | } |
776 | 819 | ||
820 | if (info->attrs[NL80211_ATTR_WIPHY_COVERAGE_CLASS]) { | ||
821 | coverage_class = nla_get_u8( | ||
822 | info->attrs[NL80211_ATTR_WIPHY_COVERAGE_CLASS]); | ||
823 | changed |= WIPHY_PARAM_COVERAGE_CLASS; | ||
824 | } | ||
825 | |||
777 | if (changed) { | 826 | if (changed) { |
778 | u8 old_retry_short, old_retry_long; | 827 | u8 old_retry_short, old_retry_long; |
779 | u32 old_frag_threshold, old_rts_threshold; | 828 | u32 old_frag_threshold, old_rts_threshold; |
829 | u8 old_coverage_class; | ||
780 | 830 | ||
781 | if (!rdev->ops->set_wiphy_params) { | 831 | if (!rdev->ops->set_wiphy_params) { |
782 | result = -EOPNOTSUPP; | 832 | result = -EOPNOTSUPP; |
@@ -787,6 +837,7 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) | |||
787 | old_retry_long = rdev->wiphy.retry_long; | 837 | old_retry_long = rdev->wiphy.retry_long; |
788 | old_frag_threshold = rdev->wiphy.frag_threshold; | 838 | old_frag_threshold = rdev->wiphy.frag_threshold; |
789 | old_rts_threshold = rdev->wiphy.rts_threshold; | 839 | old_rts_threshold = rdev->wiphy.rts_threshold; |
840 | old_coverage_class = rdev->wiphy.coverage_class; | ||
790 | 841 | ||
791 | if (changed & WIPHY_PARAM_RETRY_SHORT) | 842 | if (changed & WIPHY_PARAM_RETRY_SHORT) |
792 | rdev->wiphy.retry_short = retry_short; | 843 | rdev->wiphy.retry_short = retry_short; |
@@ -796,6 +847,8 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) | |||
796 | rdev->wiphy.frag_threshold = frag_threshold; | 847 | rdev->wiphy.frag_threshold = frag_threshold; |
797 | if (changed & WIPHY_PARAM_RTS_THRESHOLD) | 848 | if (changed & WIPHY_PARAM_RTS_THRESHOLD) |
798 | rdev->wiphy.rts_threshold = rts_threshold; | 849 | rdev->wiphy.rts_threshold = rts_threshold; |
850 | if (changed & WIPHY_PARAM_COVERAGE_CLASS) | ||
851 | rdev->wiphy.coverage_class = coverage_class; | ||
799 | 852 | ||
800 | result = rdev->ops->set_wiphy_params(&rdev->wiphy, changed); | 853 | result = rdev->ops->set_wiphy_params(&rdev->wiphy, changed); |
801 | if (result) { | 854 | if (result) { |
@@ -803,6 +856,7 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) | |||
803 | rdev->wiphy.retry_long = old_retry_long; | 856 | rdev->wiphy.retry_long = old_retry_long; |
804 | rdev->wiphy.frag_threshold = old_frag_threshold; | 857 | rdev->wiphy.frag_threshold = old_frag_threshold; |
805 | rdev->wiphy.rts_threshold = old_rts_threshold; | 858 | rdev->wiphy.rts_threshold = old_rts_threshold; |
859 | rdev->wiphy.coverage_class = old_coverage_class; | ||
806 | } | 860 | } |
807 | } | 861 | } |
808 | 862 | ||
@@ -947,6 +1001,32 @@ static int parse_monitor_flags(struct nlattr *nla, u32 *mntrflags) | |||
947 | return 0; | 1001 | return 0; |
948 | } | 1002 | } |
949 | 1003 | ||
1004 | static int nl80211_valid_4addr(struct cfg80211_registered_device *rdev, | ||
1005 | struct net_device *netdev, u8 use_4addr, | ||
1006 | enum nl80211_iftype iftype) | ||
1007 | { | ||
1008 | if (!use_4addr) { | ||
1009 | if (netdev && netdev->br_port) | ||
1010 | return -EBUSY; | ||
1011 | return 0; | ||
1012 | } | ||
1013 | |||
1014 | switch (iftype) { | ||
1015 | case NL80211_IFTYPE_AP_VLAN: | ||
1016 | if (rdev->wiphy.flags & WIPHY_FLAG_4ADDR_AP) | ||
1017 | return 0; | ||
1018 | break; | ||
1019 | case NL80211_IFTYPE_STATION: | ||
1020 | if (rdev->wiphy.flags & WIPHY_FLAG_4ADDR_STATION) | ||
1021 | return 0; | ||
1022 | break; | ||
1023 | default: | ||
1024 | break; | ||
1025 | } | ||
1026 | |||
1027 | return -EOPNOTSUPP; | ||
1028 | } | ||
1029 | |||
950 | static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info) | 1030 | static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info) |
951 | { | 1031 | { |
952 | struct cfg80211_registered_device *rdev; | 1032 | struct cfg80211_registered_device *rdev; |
@@ -987,6 +1067,16 @@ static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info) | |||
987 | change = true; | 1067 | change = true; |
988 | } | 1068 | } |
989 | 1069 | ||
1070 | if (info->attrs[NL80211_ATTR_4ADDR]) { | ||
1071 | params.use_4addr = !!nla_get_u8(info->attrs[NL80211_ATTR_4ADDR]); | ||
1072 | change = true; | ||
1073 | err = nl80211_valid_4addr(rdev, dev, params.use_4addr, ntype); | ||
1074 | if (err) | ||
1075 | goto unlock; | ||
1076 | } else { | ||
1077 | params.use_4addr = -1; | ||
1078 | } | ||
1079 | |||
990 | if (info->attrs[NL80211_ATTR_MNTR_FLAGS]) { | 1080 | if (info->attrs[NL80211_ATTR_MNTR_FLAGS]) { |
991 | if (ntype != NL80211_IFTYPE_MONITOR) { | 1081 | if (ntype != NL80211_IFTYPE_MONITOR) { |
992 | err = -EINVAL; | 1082 | err = -EINVAL; |
@@ -1006,6 +1096,9 @@ static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info) | |||
1006 | else | 1096 | else |
1007 | err = 0; | 1097 | err = 0; |
1008 | 1098 | ||
1099 | if (!err && params.use_4addr != -1) | ||
1100 | dev->ieee80211_ptr->use_4addr = params.use_4addr; | ||
1101 | |||
1009 | unlock: | 1102 | unlock: |
1010 | dev_put(dev); | 1103 | dev_put(dev); |
1011 | cfg80211_unlock_rdev(rdev); | 1104 | cfg80211_unlock_rdev(rdev); |
@@ -1053,6 +1146,13 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info) | |||
1053 | params.mesh_id_len = nla_len(info->attrs[NL80211_ATTR_MESH_ID]); | 1146 | params.mesh_id_len = nla_len(info->attrs[NL80211_ATTR_MESH_ID]); |
1054 | } | 1147 | } |
1055 | 1148 | ||
1149 | if (info->attrs[NL80211_ATTR_4ADDR]) { | ||
1150 | params.use_4addr = !!nla_get_u8(info->attrs[NL80211_ATTR_4ADDR]); | ||
1151 | err = nl80211_valid_4addr(rdev, NULL, params.use_4addr, type); | ||
1152 | if (err) | ||
1153 | goto unlock; | ||
1154 | } | ||
1155 | |||
1056 | err = parse_monitor_flags(type == NL80211_IFTYPE_MONITOR ? | 1156 | err = parse_monitor_flags(type == NL80211_IFTYPE_MONITOR ? |
1057 | info->attrs[NL80211_ATTR_MNTR_FLAGS] : NULL, | 1157 | info->attrs[NL80211_ATTR_MNTR_FLAGS] : NULL, |
1058 | &flags); | 1158 | &flags); |
@@ -1264,7 +1364,7 @@ static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info) | |||
1264 | if (!err) | 1364 | if (!err) |
1265 | err = func(&rdev->wiphy, dev, key.idx); | 1365 | err = func(&rdev->wiphy, dev, key.idx); |
1266 | 1366 | ||
1267 | #ifdef CONFIG_WIRELESS_EXT | 1367 | #ifdef CONFIG_CFG80211_WEXT |
1268 | if (!err) { | 1368 | if (!err) { |
1269 | if (func == rdev->ops->set_default_key) | 1369 | if (func == rdev->ops->set_default_key) |
1270 | dev->ieee80211_ptr->wext.default_key = key.idx; | 1370 | dev->ieee80211_ptr->wext.default_key = key.idx; |
@@ -1365,7 +1465,7 @@ static int nl80211_del_key(struct sk_buff *skb, struct genl_info *info) | |||
1365 | if (!err) | 1465 | if (!err) |
1366 | err = rdev->ops->del_key(&rdev->wiphy, dev, key.idx, mac_addr); | 1466 | err = rdev->ops->del_key(&rdev->wiphy, dev, key.idx, mac_addr); |
1367 | 1467 | ||
1368 | #ifdef CONFIG_WIRELESS_EXT | 1468 | #ifdef CONFIG_CFG80211_WEXT |
1369 | if (!err) { | 1469 | if (!err) { |
1370 | if (key.idx == dev->ieee80211_ptr->wext.default_key) | 1470 | if (key.idx == dev->ieee80211_ptr->wext.default_key) |
1371 | dev->ieee80211_ptr->wext.default_key = -1; | 1471 | dev->ieee80211_ptr->wext.default_key = -1; |
@@ -1562,42 +1662,9 @@ static int parse_station_flags(struct genl_info *info, | |||
1562 | return 0; | 1662 | return 0; |
1563 | } | 1663 | } |
1564 | 1664 | ||
1565 | static u16 nl80211_calculate_bitrate(struct rate_info *rate) | ||
1566 | { | ||
1567 | int modulation, streams, bitrate; | ||
1568 | |||
1569 | if (!(rate->flags & RATE_INFO_FLAGS_MCS)) | ||
1570 | return rate->legacy; | ||
1571 | |||
1572 | /* the formula below does only work for MCS values smaller than 32 */ | ||
1573 | if (rate->mcs >= 32) | ||
1574 | return 0; | ||
1575 | |||
1576 | modulation = rate->mcs & 7; | ||
1577 | streams = (rate->mcs >> 3) + 1; | ||
1578 | |||
1579 | bitrate = (rate->flags & RATE_INFO_FLAGS_40_MHZ_WIDTH) ? | ||
1580 | 13500000 : 6500000; | ||
1581 | |||
1582 | if (modulation < 4) | ||
1583 | bitrate *= (modulation + 1); | ||
1584 | else if (modulation == 4) | ||
1585 | bitrate *= (modulation + 2); | ||
1586 | else | ||
1587 | bitrate *= (modulation + 3); | ||
1588 | |||
1589 | bitrate *= streams; | ||
1590 | |||
1591 | if (rate->flags & RATE_INFO_FLAGS_SHORT_GI) | ||
1592 | bitrate = (bitrate / 9) * 10; | ||
1593 | |||
1594 | /* do NOT round down here */ | ||
1595 | return (bitrate + 50000) / 100000; | ||
1596 | } | ||
1597 | |||
1598 | static int nl80211_send_station(struct sk_buff *msg, u32 pid, u32 seq, | 1665 | static int nl80211_send_station(struct sk_buff *msg, u32 pid, u32 seq, |
1599 | int flags, struct net_device *dev, | 1666 | int flags, struct net_device *dev, |
1600 | u8 *mac_addr, struct station_info *sinfo) | 1667 | const u8 *mac_addr, struct station_info *sinfo) |
1601 | { | 1668 | { |
1602 | void *hdr; | 1669 | void *hdr; |
1603 | struct nlattr *sinfoattr, *txrate; | 1670 | struct nlattr *sinfoattr, *txrate; |
@@ -1641,8 +1708,8 @@ static int nl80211_send_station(struct sk_buff *msg, u32 pid, u32 seq, | |||
1641 | if (!txrate) | 1708 | if (!txrate) |
1642 | goto nla_put_failure; | 1709 | goto nla_put_failure; |
1643 | 1710 | ||
1644 | /* nl80211_calculate_bitrate will return 0 for mcs >= 32 */ | 1711 | /* cfg80211_calculate_bitrate will return 0 for mcs >= 32 */ |
1645 | bitrate = nl80211_calculate_bitrate(&sinfo->txrate); | 1712 | bitrate = cfg80211_calculate_bitrate(&sinfo->txrate); |
1646 | if (bitrate > 0) | 1713 | if (bitrate > 0) |
1647 | NLA_PUT_U16(msg, NL80211_RATE_INFO_BITRATE, bitrate); | 1714 | NLA_PUT_U16(msg, NL80211_RATE_INFO_BITRATE, bitrate); |
1648 | 1715 | ||
@@ -1682,20 +1749,10 @@ static int nl80211_dump_station(struct sk_buff *skb, | |||
1682 | int sta_idx = cb->args[1]; | 1749 | int sta_idx = cb->args[1]; |
1683 | int err; | 1750 | int err; |
1684 | 1751 | ||
1685 | if (!ifidx) { | 1752 | if (!ifidx) |
1686 | err = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize, | 1753 | ifidx = nl80211_get_ifidx(cb); |
1687 | nl80211_fam.attrbuf, nl80211_fam.maxattr, | 1754 | if (ifidx < 0) |
1688 | nl80211_policy); | 1755 | return ifidx; |
1689 | if (err) | ||
1690 | return err; | ||
1691 | |||
1692 | if (!nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX]) | ||
1693 | return -EINVAL; | ||
1694 | |||
1695 | ifidx = nla_get_u32(nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX]); | ||
1696 | if (!ifidx) | ||
1697 | return -EINVAL; | ||
1698 | } | ||
1699 | 1756 | ||
1700 | rtnl_lock(); | 1757 | rtnl_lock(); |
1701 | 1758 | ||
@@ -1800,7 +1857,7 @@ static int nl80211_get_station(struct sk_buff *skb, struct genl_info *info) | |||
1800 | } | 1857 | } |
1801 | 1858 | ||
1802 | /* | 1859 | /* |
1803 | * Get vlan interface making sure it is on the right wiphy. | 1860 | * Get vlan interface making sure it is running and on the right wiphy. |
1804 | */ | 1861 | */ |
1805 | static int get_vlan(struct genl_info *info, | 1862 | static int get_vlan(struct genl_info *info, |
1806 | struct cfg80211_registered_device *rdev, | 1863 | struct cfg80211_registered_device *rdev, |
@@ -1818,6 +1875,8 @@ static int get_vlan(struct genl_info *info, | |||
1818 | return -EINVAL; | 1875 | return -EINVAL; |
1819 | if ((*vlan)->ieee80211_ptr->wiphy != &rdev->wiphy) | 1876 | if ((*vlan)->ieee80211_ptr->wiphy != &rdev->wiphy) |
1820 | return -EINVAL; | 1877 | return -EINVAL; |
1878 | if (!netif_running(*vlan)) | ||
1879 | return -ENETDOWN; | ||
1821 | } | 1880 | } |
1822 | return 0; | 1881 | return 0; |
1823 | } | 1882 | } |
@@ -1956,6 +2015,9 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info) | |||
1956 | if (!info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]) | 2015 | if (!info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]) |
1957 | return -EINVAL; | 2016 | return -EINVAL; |
1958 | 2017 | ||
2018 | if (!info->attrs[NL80211_ATTR_STA_AID]) | ||
2019 | return -EINVAL; | ||
2020 | |||
1959 | mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]); | 2021 | mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]); |
1960 | params.supported_rates = | 2022 | params.supported_rates = |
1961 | nla_data(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]); | 2023 | nla_data(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]); |
@@ -1964,11 +2026,9 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info) | |||
1964 | params.listen_interval = | 2026 | params.listen_interval = |
1965 | nla_get_u16(info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]); | 2027 | nla_get_u16(info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]); |
1966 | 2028 | ||
1967 | if (info->attrs[NL80211_ATTR_STA_AID]) { | 2029 | params.aid = nla_get_u16(info->attrs[NL80211_ATTR_STA_AID]); |
1968 | params.aid = nla_get_u16(info->attrs[NL80211_ATTR_STA_AID]); | 2030 | if (!params.aid || params.aid > IEEE80211_MAX_AID) |
1969 | if (!params.aid || params.aid > IEEE80211_MAX_AID) | 2031 | return -EINVAL; |
1970 | return -EINVAL; | ||
1971 | } | ||
1972 | 2032 | ||
1973 | if (info->attrs[NL80211_ATTR_HT_CAPABILITY]) | 2033 | if (info->attrs[NL80211_ATTR_HT_CAPABILITY]) |
1974 | params.ht_capa = | 2034 | params.ht_capa = |
@@ -1983,6 +2043,12 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info) | |||
1983 | if (err) | 2043 | if (err) |
1984 | goto out_rtnl; | 2044 | goto out_rtnl; |
1985 | 2045 | ||
2046 | if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP && | ||
2047 | dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN) { | ||
2048 | err = -EINVAL; | ||
2049 | goto out; | ||
2050 | } | ||
2051 | |||
1986 | err = get_vlan(info, rdev, ¶ms.vlan); | 2052 | err = get_vlan(info, rdev, ¶ms.vlan); |
1987 | if (err) | 2053 | if (err) |
1988 | goto out; | 2054 | goto out; |
@@ -1990,35 +2056,6 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info) | |||
1990 | /* validate settings */ | 2056 | /* validate settings */ |
1991 | err = 0; | 2057 | err = 0; |
1992 | 2058 | ||
1993 | switch (dev->ieee80211_ptr->iftype) { | ||
1994 | case NL80211_IFTYPE_AP: | ||
1995 | case NL80211_IFTYPE_AP_VLAN: | ||
1996 | /* all ok but must have AID */ | ||
1997 | if (!params.aid) | ||
1998 | err = -EINVAL; | ||
1999 | break; | ||
2000 | case NL80211_IFTYPE_MESH_POINT: | ||
2001 | /* disallow things mesh doesn't support */ | ||
2002 | if (params.vlan) | ||
2003 | err = -EINVAL; | ||
2004 | if (params.aid) | ||
2005 | err = -EINVAL; | ||
2006 | if (params.ht_capa) | ||
2007 | err = -EINVAL; | ||
2008 | if (params.listen_interval >= 0) | ||
2009 | err = -EINVAL; | ||
2010 | if (params.supported_rates) | ||
2011 | err = -EINVAL; | ||
2012 | if (params.sta_flags_mask) | ||
2013 | err = -EINVAL; | ||
2014 | break; | ||
2015 | default: | ||
2016 | err = -EINVAL; | ||
2017 | } | ||
2018 | |||
2019 | if (err) | ||
2020 | goto out; | ||
2021 | |||
2022 | if (!rdev->ops->add_station) { | 2059 | if (!rdev->ops->add_station) { |
2023 | err = -EOPNOTSUPP; | 2060 | err = -EOPNOTSUPP; |
2024 | goto out; | 2061 | goto out; |
@@ -2059,8 +2096,7 @@ static int nl80211_del_station(struct sk_buff *skb, struct genl_info *info) | |||
2059 | goto out_rtnl; | 2096 | goto out_rtnl; |
2060 | 2097 | ||
2061 | if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP && | 2098 | if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP && |
2062 | dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN && | 2099 | dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN) { |
2063 | dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) { | ||
2064 | err = -EINVAL; | 2100 | err = -EINVAL; |
2065 | goto out; | 2101 | goto out; |
2066 | } | 2102 | } |
@@ -2105,9 +2141,9 @@ static int nl80211_send_mpath(struct sk_buff *msg, u32 pid, u32 seq, | |||
2105 | if (pinfo->filled & MPATH_INFO_FRAME_QLEN) | 2141 | if (pinfo->filled & MPATH_INFO_FRAME_QLEN) |
2106 | NLA_PUT_U32(msg, NL80211_MPATH_INFO_FRAME_QLEN, | 2142 | NLA_PUT_U32(msg, NL80211_MPATH_INFO_FRAME_QLEN, |
2107 | pinfo->frame_qlen); | 2143 | pinfo->frame_qlen); |
2108 | if (pinfo->filled & MPATH_INFO_DSN) | 2144 | if (pinfo->filled & MPATH_INFO_SN) |
2109 | NLA_PUT_U32(msg, NL80211_MPATH_INFO_DSN, | 2145 | NLA_PUT_U32(msg, NL80211_MPATH_INFO_SN, |
2110 | pinfo->dsn); | 2146 | pinfo->sn); |
2111 | if (pinfo->filled & MPATH_INFO_METRIC) | 2147 | if (pinfo->filled & MPATH_INFO_METRIC) |
2112 | NLA_PUT_U32(msg, NL80211_MPATH_INFO_METRIC, | 2148 | NLA_PUT_U32(msg, NL80211_MPATH_INFO_METRIC, |
2113 | pinfo->metric); | 2149 | pinfo->metric); |
@@ -2145,20 +2181,10 @@ static int nl80211_dump_mpath(struct sk_buff *skb, | |||
2145 | int path_idx = cb->args[1]; | 2181 | int path_idx = cb->args[1]; |
2146 | int err; | 2182 | int err; |
2147 | 2183 | ||
2148 | if (!ifidx) { | 2184 | if (!ifidx) |
2149 | err = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize, | 2185 | ifidx = nl80211_get_ifidx(cb); |
2150 | nl80211_fam.attrbuf, nl80211_fam.maxattr, | 2186 | if (ifidx < 0) |
2151 | nl80211_policy); | 2187 | return ifidx; |
2152 | if (err) | ||
2153 | return err; | ||
2154 | |||
2155 | if (!nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX]) | ||
2156 | return -EINVAL; | ||
2157 | |||
2158 | ifidx = nla_get_u32(nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX]); | ||
2159 | if (!ifidx) | ||
2160 | return -EINVAL; | ||
2161 | } | ||
2162 | 2188 | ||
2163 | rtnl_lock(); | 2189 | rtnl_lock(); |
2164 | 2190 | ||
@@ -2457,8 +2483,7 @@ static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info) | |||
2457 | return err; | 2483 | return err; |
2458 | } | 2484 | } |
2459 | 2485 | ||
2460 | static const struct nla_policy | 2486 | static const struct nla_policy reg_rule_policy[NL80211_REG_RULE_ATTR_MAX + 1] = { |
2461 | reg_rule_policy[NL80211_REG_RULE_ATTR_MAX + 1] = { | ||
2462 | [NL80211_ATTR_REG_RULE_FLAGS] = { .type = NLA_U32 }, | 2487 | [NL80211_ATTR_REG_RULE_FLAGS] = { .type = NLA_U32 }, |
2463 | [NL80211_ATTR_FREQ_RANGE_START] = { .type = NLA_U32 }, | 2488 | [NL80211_ATTR_FREQ_RANGE_START] = { .type = NLA_U32 }, |
2464 | [NL80211_ATTR_FREQ_RANGE_END] = { .type = NLA_U32 }, | 2489 | [NL80211_ATTR_FREQ_RANGE_END] = { .type = NLA_U32 }, |
@@ -2526,12 +2551,6 @@ static int nl80211_req_set_reg(struct sk_buff *skb, struct genl_info *info) | |||
2526 | 2551 | ||
2527 | data = nla_data(info->attrs[NL80211_ATTR_REG_ALPHA2]); | 2552 | data = nla_data(info->attrs[NL80211_ATTR_REG_ALPHA2]); |
2528 | 2553 | ||
2529 | #ifdef CONFIG_WIRELESS_OLD_REGULATORY | ||
2530 | /* We ignore world regdom requests with the old regdom setup */ | ||
2531 | if (is_world_regdom(data)) | ||
2532 | return -EINVAL; | ||
2533 | #endif | ||
2534 | |||
2535 | r = regulatory_hint_user(data); | 2554 | r = regulatory_hint_user(data); |
2536 | 2555 | ||
2537 | return r; | 2556 | return r; |
@@ -2605,6 +2624,8 @@ static int nl80211_get_mesh_params(struct sk_buff *skb, | |||
2605 | cur_params.dot11MeshHWMPpreqMinInterval); | 2624 | cur_params.dot11MeshHWMPpreqMinInterval); |
2606 | NLA_PUT_U16(msg, NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME, | 2625 | NLA_PUT_U16(msg, NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME, |
2607 | cur_params.dot11MeshHWMPnetDiameterTraversalTime); | 2626 | cur_params.dot11MeshHWMPnetDiameterTraversalTime); |
2627 | NLA_PUT_U8(msg, NL80211_MESHCONF_HWMP_ROOTMODE, | ||
2628 | cur_params.dot11MeshHWMPRootMode); | ||
2608 | nla_nest_end(msg, pinfoattr); | 2629 | nla_nest_end(msg, pinfoattr); |
2609 | genlmsg_end(msg, hdr); | 2630 | genlmsg_end(msg, hdr); |
2610 | err = genlmsg_reply(msg, info); | 2631 | err = genlmsg_reply(msg, info); |
@@ -2631,8 +2652,7 @@ do {\ | |||
2631 | } \ | 2652 | } \ |
2632 | } while (0);\ | 2653 | } while (0);\ |
2633 | 2654 | ||
2634 | static struct nla_policy | 2655 | static const struct nla_policy nl80211_meshconf_params_policy[NL80211_MESHCONF_ATTR_MAX+1] = { |
2635 | nl80211_meshconf_params_policy[NL80211_MESHCONF_ATTR_MAX+1] __read_mostly = { | ||
2636 | [NL80211_MESHCONF_RETRY_TIMEOUT] = { .type = NLA_U16 }, | 2656 | [NL80211_MESHCONF_RETRY_TIMEOUT] = { .type = NLA_U16 }, |
2637 | [NL80211_MESHCONF_CONFIRM_TIMEOUT] = { .type = NLA_U16 }, | 2657 | [NL80211_MESHCONF_CONFIRM_TIMEOUT] = { .type = NLA_U16 }, |
2638 | [NL80211_MESHCONF_HOLDING_TIMEOUT] = { .type = NLA_U16 }, | 2658 | [NL80211_MESHCONF_HOLDING_TIMEOUT] = { .type = NLA_U16 }, |
@@ -2715,6 +2735,10 @@ static int nl80211_set_mesh_params(struct sk_buff *skb, struct genl_info *info) | |||
2715 | dot11MeshHWMPnetDiameterTraversalTime, | 2735 | dot11MeshHWMPnetDiameterTraversalTime, |
2716 | mask, NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME, | 2736 | mask, NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME, |
2717 | nla_get_u16); | 2737 | nla_get_u16); |
2738 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, | ||
2739 | dot11MeshHWMPRootMode, mask, | ||
2740 | NL80211_MESHCONF_HWMP_ROOTMODE, | ||
2741 | nla_get_u8); | ||
2718 | 2742 | ||
2719 | /* Apply changes */ | 2743 | /* Apply changes */ |
2720 | err = rdev->ops->set_mesh_params(&rdev->wiphy, dev, &cfg, mask); | 2744 | err = rdev->ops->set_mesh_params(&rdev->wiphy, dev, &cfg, mask); |
@@ -2988,7 +3012,6 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info) | |||
2988 | goto out; | 3012 | goto out; |
2989 | } | 3013 | } |
2990 | 3014 | ||
2991 | request->n_channels = n_channels; | ||
2992 | if (n_ssids) | 3015 | if (n_ssids) |
2993 | request->ssids = (void *)&request->channels[n_channels]; | 3016 | request->ssids = (void *)&request->channels[n_channels]; |
2994 | request->n_ssids = n_ssids; | 3017 | request->n_ssids = n_ssids; |
@@ -2999,32 +3022,53 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info) | |||
2999 | request->ie = (void *)(request->channels + n_channels); | 3022 | request->ie = (void *)(request->channels + n_channels); |
3000 | } | 3023 | } |
3001 | 3024 | ||
3025 | i = 0; | ||
3002 | if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) { | 3026 | if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) { |
3003 | /* user specified, bail out if channel not found */ | 3027 | /* user specified, bail out if channel not found */ |
3004 | request->n_channels = n_channels; | ||
3005 | i = 0; | ||
3006 | nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_FREQUENCIES], tmp) { | 3028 | nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_FREQUENCIES], tmp) { |
3007 | request->channels[i] = ieee80211_get_channel(wiphy, nla_get_u32(attr)); | 3029 | struct ieee80211_channel *chan; |
3008 | if (!request->channels[i]) { | 3030 | |
3031 | chan = ieee80211_get_channel(wiphy, nla_get_u32(attr)); | ||
3032 | |||
3033 | if (!chan) { | ||
3009 | err = -EINVAL; | 3034 | err = -EINVAL; |
3010 | goto out_free; | 3035 | goto out_free; |
3011 | } | 3036 | } |
3037 | |||
3038 | /* ignore disabled channels */ | ||
3039 | if (chan->flags & IEEE80211_CHAN_DISABLED) | ||
3040 | continue; | ||
3041 | |||
3042 | request->channels[i] = chan; | ||
3012 | i++; | 3043 | i++; |
3013 | } | 3044 | } |
3014 | } else { | 3045 | } else { |
3015 | /* all channels */ | 3046 | /* all channels */ |
3016 | i = 0; | ||
3017 | for (band = 0; band < IEEE80211_NUM_BANDS; band++) { | 3047 | for (band = 0; band < IEEE80211_NUM_BANDS; band++) { |
3018 | int j; | 3048 | int j; |
3019 | if (!wiphy->bands[band]) | 3049 | if (!wiphy->bands[band]) |
3020 | continue; | 3050 | continue; |
3021 | for (j = 0; j < wiphy->bands[band]->n_channels; j++) { | 3051 | for (j = 0; j < wiphy->bands[band]->n_channels; j++) { |
3022 | request->channels[i] = &wiphy->bands[band]->channels[j]; | 3052 | struct ieee80211_channel *chan; |
3053 | |||
3054 | chan = &wiphy->bands[band]->channels[j]; | ||
3055 | |||
3056 | if (chan->flags & IEEE80211_CHAN_DISABLED) | ||
3057 | continue; | ||
3058 | |||
3059 | request->channels[i] = chan; | ||
3023 | i++; | 3060 | i++; |
3024 | } | 3061 | } |
3025 | } | 3062 | } |
3026 | } | 3063 | } |
3027 | 3064 | ||
3065 | if (!i) { | ||
3066 | err = -EINVAL; | ||
3067 | goto out_free; | ||
3068 | } | ||
3069 | |||
3070 | request->n_channels = i; | ||
3071 | |||
3028 | i = 0; | 3072 | i = 0; |
3029 | if (info->attrs[NL80211_ATTR_SCAN_SSIDS]) { | 3073 | if (info->attrs[NL80211_ATTR_SCAN_SSIDS]) { |
3030 | nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS], tmp) { | 3074 | nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS], tmp) { |
@@ -3099,12 +3143,18 @@ static int nl80211_send_bss(struct sk_buff *msg, u32 pid, u32 seq, int flags, | |||
3099 | NLA_PUT(msg, NL80211_BSS_INFORMATION_ELEMENTS, | 3143 | NLA_PUT(msg, NL80211_BSS_INFORMATION_ELEMENTS, |
3100 | res->len_information_elements, | 3144 | res->len_information_elements, |
3101 | res->information_elements); | 3145 | res->information_elements); |
3146 | if (res->beacon_ies && res->len_beacon_ies && | ||
3147 | res->beacon_ies != res->information_elements) | ||
3148 | NLA_PUT(msg, NL80211_BSS_BEACON_IES, | ||
3149 | res->len_beacon_ies, res->beacon_ies); | ||
3102 | if (res->tsf) | 3150 | if (res->tsf) |
3103 | NLA_PUT_U64(msg, NL80211_BSS_TSF, res->tsf); | 3151 | NLA_PUT_U64(msg, NL80211_BSS_TSF, res->tsf); |
3104 | if (res->beacon_interval) | 3152 | if (res->beacon_interval) |
3105 | NLA_PUT_U16(msg, NL80211_BSS_BEACON_INTERVAL, res->beacon_interval); | 3153 | NLA_PUT_U16(msg, NL80211_BSS_BEACON_INTERVAL, res->beacon_interval); |
3106 | NLA_PUT_U16(msg, NL80211_BSS_CAPABILITY, res->capability); | 3154 | NLA_PUT_U16(msg, NL80211_BSS_CAPABILITY, res->capability); |
3107 | NLA_PUT_U32(msg, NL80211_BSS_FREQUENCY, res->channel->center_freq); | 3155 | NLA_PUT_U32(msg, NL80211_BSS_FREQUENCY, res->channel->center_freq); |
3156 | NLA_PUT_U32(msg, NL80211_BSS_SEEN_MS_AGO, | ||
3157 | jiffies_to_msecs(jiffies - intbss->ts)); | ||
3108 | 3158 | ||
3109 | switch (rdev->wiphy.signal_type) { | 3159 | switch (rdev->wiphy.signal_type) { |
3110 | case CFG80211_SIGNAL_TYPE_MBM: | 3160 | case CFG80211_SIGNAL_TYPE_MBM: |
@@ -3159,21 +3209,11 @@ static int nl80211_dump_scan(struct sk_buff *skb, | |||
3159 | int start = cb->args[1], idx = 0; | 3209 | int start = cb->args[1], idx = 0; |
3160 | int err; | 3210 | int err; |
3161 | 3211 | ||
3162 | if (!ifidx) { | 3212 | if (!ifidx) |
3163 | err = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize, | 3213 | ifidx = nl80211_get_ifidx(cb); |
3164 | nl80211_fam.attrbuf, nl80211_fam.maxattr, | 3214 | if (ifidx < 0) |
3165 | nl80211_policy); | 3215 | return ifidx; |
3166 | if (err) | 3216 | cb->args[0] = ifidx; |
3167 | return err; | ||
3168 | |||
3169 | if (!nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX]) | ||
3170 | return -EINVAL; | ||
3171 | |||
3172 | ifidx = nla_get_u32(nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX]); | ||
3173 | if (!ifidx) | ||
3174 | return -EINVAL; | ||
3175 | cb->args[0] = ifidx; | ||
3176 | } | ||
3177 | 3217 | ||
3178 | dev = dev_get_by_index(sock_net(skb->sk), ifidx); | 3218 | dev = dev_get_by_index(sock_net(skb->sk), ifidx); |
3179 | if (!dev) | 3219 | if (!dev) |
@@ -3216,6 +3256,106 @@ static int nl80211_dump_scan(struct sk_buff *skb, | |||
3216 | return err; | 3256 | return err; |
3217 | } | 3257 | } |
3218 | 3258 | ||
3259 | static int nl80211_send_survey(struct sk_buff *msg, u32 pid, u32 seq, | ||
3260 | int flags, struct net_device *dev, | ||
3261 | struct survey_info *survey) | ||
3262 | { | ||
3263 | void *hdr; | ||
3264 | struct nlattr *infoattr; | ||
3265 | |||
3266 | /* Survey without a channel doesn't make sense */ | ||
3267 | if (!survey->channel) | ||
3268 | return -EINVAL; | ||
3269 | |||
3270 | hdr = nl80211hdr_put(msg, pid, seq, flags, | ||
3271 | NL80211_CMD_NEW_SURVEY_RESULTS); | ||
3272 | if (!hdr) | ||
3273 | return -ENOMEM; | ||
3274 | |||
3275 | NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex); | ||
3276 | |||
3277 | infoattr = nla_nest_start(msg, NL80211_ATTR_SURVEY_INFO); | ||
3278 | if (!infoattr) | ||
3279 | goto nla_put_failure; | ||
3280 | |||
3281 | NLA_PUT_U32(msg, NL80211_SURVEY_INFO_FREQUENCY, | ||
3282 | survey->channel->center_freq); | ||
3283 | if (survey->filled & SURVEY_INFO_NOISE_DBM) | ||
3284 | NLA_PUT_U8(msg, NL80211_SURVEY_INFO_NOISE, | ||
3285 | survey->noise); | ||
3286 | |||
3287 | nla_nest_end(msg, infoattr); | ||
3288 | |||
3289 | return genlmsg_end(msg, hdr); | ||
3290 | |||
3291 | nla_put_failure: | ||
3292 | genlmsg_cancel(msg, hdr); | ||
3293 | return -EMSGSIZE; | ||
3294 | } | ||
3295 | |||
3296 | static int nl80211_dump_survey(struct sk_buff *skb, | ||
3297 | struct netlink_callback *cb) | ||
3298 | { | ||
3299 | struct survey_info survey; | ||
3300 | struct cfg80211_registered_device *dev; | ||
3301 | struct net_device *netdev; | ||
3302 | int ifidx = cb->args[0]; | ||
3303 | int survey_idx = cb->args[1]; | ||
3304 | int res; | ||
3305 | |||
3306 | if (!ifidx) | ||
3307 | ifidx = nl80211_get_ifidx(cb); | ||
3308 | if (ifidx < 0) | ||
3309 | return ifidx; | ||
3310 | cb->args[0] = ifidx; | ||
3311 | |||
3312 | rtnl_lock(); | ||
3313 | |||
3314 | netdev = __dev_get_by_index(sock_net(skb->sk), ifidx); | ||
3315 | if (!netdev) { | ||
3316 | res = -ENODEV; | ||
3317 | goto out_rtnl; | ||
3318 | } | ||
3319 | |||
3320 | dev = cfg80211_get_dev_from_ifindex(sock_net(skb->sk), ifidx); | ||
3321 | if (IS_ERR(dev)) { | ||
3322 | res = PTR_ERR(dev); | ||
3323 | goto out_rtnl; | ||
3324 | } | ||
3325 | |||
3326 | if (!dev->ops->dump_survey) { | ||
3327 | res = -EOPNOTSUPP; | ||
3328 | goto out_err; | ||
3329 | } | ||
3330 | |||
3331 | while (1) { | ||
3332 | res = dev->ops->dump_survey(&dev->wiphy, netdev, survey_idx, | ||
3333 | &survey); | ||
3334 | if (res == -ENOENT) | ||
3335 | break; | ||
3336 | if (res) | ||
3337 | goto out_err; | ||
3338 | |||
3339 | if (nl80211_send_survey(skb, | ||
3340 | NETLINK_CB(cb->skb).pid, | ||
3341 | cb->nlh->nlmsg_seq, NLM_F_MULTI, | ||
3342 | netdev, | ||
3343 | &survey) < 0) | ||
3344 | goto out; | ||
3345 | survey_idx++; | ||
3346 | } | ||
3347 | |||
3348 | out: | ||
3349 | cb->args[1] = survey_idx; | ||
3350 | res = skb->len; | ||
3351 | out_err: | ||
3352 | cfg80211_unlock_rdev(dev); | ||
3353 | out_rtnl: | ||
3354 | rtnl_unlock(); | ||
3355 | |||
3356 | return res; | ||
3357 | } | ||
3358 | |||
3219 | static bool nl80211_valid_auth_type(enum nl80211_auth_type auth_type) | 3359 | static bool nl80211_valid_auth_type(enum nl80211_auth_type auth_type) |
3220 | { | 3360 | { |
3221 | return auth_type <= NL80211_AUTHTYPE_MAX; | 3361 | return auth_type <= NL80211_AUTHTYPE_MAX; |
@@ -3411,6 +3551,7 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info) | |||
3411 | { | 3551 | { |
3412 | struct cfg80211_registered_device *rdev; | 3552 | struct cfg80211_registered_device *rdev; |
3413 | struct net_device *dev; | 3553 | struct net_device *dev; |
3554 | struct wireless_dev *wdev; | ||
3414 | struct cfg80211_crypto_settings crypto; | 3555 | struct cfg80211_crypto_settings crypto; |
3415 | struct ieee80211_channel *chan, *fixedchan; | 3556 | struct ieee80211_channel *chan, *fixedchan; |
3416 | const u8 *bssid, *ssid, *ie = NULL, *prev_bssid = NULL; | 3557 | const u8 *bssid, *ssid, *ie = NULL, *prev_bssid = NULL; |
@@ -3456,7 +3597,8 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info) | |||
3456 | } | 3597 | } |
3457 | 3598 | ||
3458 | mutex_lock(&rdev->devlist_mtx); | 3599 | mutex_lock(&rdev->devlist_mtx); |
3459 | fixedchan = rdev_fixed_channel(rdev, NULL); | 3600 | wdev = dev->ieee80211_ptr; |
3601 | fixedchan = rdev_fixed_channel(rdev, wdev); | ||
3460 | if (fixedchan && chan != fixedchan) { | 3602 | if (fixedchan && chan != fixedchan) { |
3461 | err = -EBUSY; | 3603 | err = -EBUSY; |
3462 | mutex_unlock(&rdev->devlist_mtx); | 3604 | mutex_unlock(&rdev->devlist_mtx); |
@@ -4054,6 +4196,589 @@ static int nl80211_wiphy_netns(struct sk_buff *skb, struct genl_info *info) | |||
4054 | return err; | 4196 | return err; |
4055 | } | 4197 | } |
4056 | 4198 | ||
4199 | static int nl80211_setdel_pmksa(struct sk_buff *skb, struct genl_info *info) | ||
4200 | { | ||
4201 | struct cfg80211_registered_device *rdev; | ||
4202 | int (*rdev_ops)(struct wiphy *wiphy, struct net_device *dev, | ||
4203 | struct cfg80211_pmksa *pmksa) = NULL; | ||
4204 | int err; | ||
4205 | struct net_device *dev; | ||
4206 | struct cfg80211_pmksa pmksa; | ||
4207 | |||
4208 | memset(&pmksa, 0, sizeof(struct cfg80211_pmksa)); | ||
4209 | |||
4210 | if (!info->attrs[NL80211_ATTR_MAC]) | ||
4211 | return -EINVAL; | ||
4212 | |||
4213 | if (!info->attrs[NL80211_ATTR_PMKID]) | ||
4214 | return -EINVAL; | ||
4215 | |||
4216 | rtnl_lock(); | ||
4217 | |||
4218 | err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); | ||
4219 | if (err) | ||
4220 | goto out_rtnl; | ||
4221 | |||
4222 | pmksa.pmkid = nla_data(info->attrs[NL80211_ATTR_PMKID]); | ||
4223 | pmksa.bssid = nla_data(info->attrs[NL80211_ATTR_MAC]); | ||
4224 | |||
4225 | if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) { | ||
4226 | err = -EOPNOTSUPP; | ||
4227 | goto out; | ||
4228 | } | ||
4229 | |||
4230 | switch (info->genlhdr->cmd) { | ||
4231 | case NL80211_CMD_SET_PMKSA: | ||
4232 | rdev_ops = rdev->ops->set_pmksa; | ||
4233 | break; | ||
4234 | case NL80211_CMD_DEL_PMKSA: | ||
4235 | rdev_ops = rdev->ops->del_pmksa; | ||
4236 | break; | ||
4237 | default: | ||
4238 | WARN_ON(1); | ||
4239 | break; | ||
4240 | } | ||
4241 | |||
4242 | if (!rdev_ops) { | ||
4243 | err = -EOPNOTSUPP; | ||
4244 | goto out; | ||
4245 | } | ||
4246 | |||
4247 | err = rdev_ops(&rdev->wiphy, dev, &pmksa); | ||
4248 | |||
4249 | out: | ||
4250 | cfg80211_unlock_rdev(rdev); | ||
4251 | dev_put(dev); | ||
4252 | out_rtnl: | ||
4253 | rtnl_unlock(); | ||
4254 | |||
4255 | return err; | ||
4256 | } | ||
4257 | |||
4258 | static int nl80211_flush_pmksa(struct sk_buff *skb, struct genl_info *info) | ||
4259 | { | ||
4260 | struct cfg80211_registered_device *rdev; | ||
4261 | int err; | ||
4262 | struct net_device *dev; | ||
4263 | |||
4264 | rtnl_lock(); | ||
4265 | |||
4266 | err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); | ||
4267 | if (err) | ||
4268 | goto out_rtnl; | ||
4269 | |||
4270 | if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) { | ||
4271 | err = -EOPNOTSUPP; | ||
4272 | goto out; | ||
4273 | } | ||
4274 | |||
4275 | if (!rdev->ops->flush_pmksa) { | ||
4276 | err = -EOPNOTSUPP; | ||
4277 | goto out; | ||
4278 | } | ||
4279 | |||
4280 | err = rdev->ops->flush_pmksa(&rdev->wiphy, dev); | ||
4281 | |||
4282 | out: | ||
4283 | cfg80211_unlock_rdev(rdev); | ||
4284 | dev_put(dev); | ||
4285 | out_rtnl: | ||
4286 | rtnl_unlock(); | ||
4287 | |||
4288 | return err; | ||
4289 | |||
4290 | } | ||
4291 | |||
4292 | static int nl80211_remain_on_channel(struct sk_buff *skb, | ||
4293 | struct genl_info *info) | ||
4294 | { | ||
4295 | struct cfg80211_registered_device *rdev; | ||
4296 | struct net_device *dev; | ||
4297 | struct ieee80211_channel *chan; | ||
4298 | struct sk_buff *msg; | ||
4299 | void *hdr; | ||
4300 | u64 cookie; | ||
4301 | enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT; | ||
4302 | u32 freq, duration; | ||
4303 | int err; | ||
4304 | |||
4305 | if (!info->attrs[NL80211_ATTR_WIPHY_FREQ] || | ||
4306 | !info->attrs[NL80211_ATTR_DURATION]) | ||
4307 | return -EINVAL; | ||
4308 | |||
4309 | duration = nla_get_u32(info->attrs[NL80211_ATTR_DURATION]); | ||
4310 | |||
4311 | /* | ||
4312 | * We should be on that channel for at least one jiffie, | ||
4313 | * and more than 5 seconds seems excessive. | ||
4314 | */ | ||
4315 | if (!duration || !msecs_to_jiffies(duration) || duration > 5000) | ||
4316 | return -EINVAL; | ||
4317 | |||
4318 | rtnl_lock(); | ||
4319 | |||
4320 | err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); | ||
4321 | if (err) | ||
4322 | goto unlock_rtnl; | ||
4323 | |||
4324 | if (!rdev->ops->remain_on_channel) { | ||
4325 | err = -EOPNOTSUPP; | ||
4326 | goto out; | ||
4327 | } | ||
4328 | |||
4329 | if (!netif_running(dev)) { | ||
4330 | err = -ENETDOWN; | ||
4331 | goto out; | ||
4332 | } | ||
4333 | |||
4334 | if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) { | ||
4335 | channel_type = nla_get_u32( | ||
4336 | info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]); | ||
4337 | if (channel_type != NL80211_CHAN_NO_HT && | ||
4338 | channel_type != NL80211_CHAN_HT20 && | ||
4339 | channel_type != NL80211_CHAN_HT40PLUS && | ||
4340 | channel_type != NL80211_CHAN_HT40MINUS) | ||
4341 | err = -EINVAL; | ||
4342 | goto out; | ||
4343 | } | ||
4344 | |||
4345 | freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]); | ||
4346 | chan = rdev_freq_to_chan(rdev, freq, channel_type); | ||
4347 | if (chan == NULL) { | ||
4348 | err = -EINVAL; | ||
4349 | goto out; | ||
4350 | } | ||
4351 | |||
4352 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); | ||
4353 | if (!msg) { | ||
4354 | err = -ENOMEM; | ||
4355 | goto out; | ||
4356 | } | ||
4357 | |||
4358 | hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0, | ||
4359 | NL80211_CMD_REMAIN_ON_CHANNEL); | ||
4360 | |||
4361 | if (IS_ERR(hdr)) { | ||
4362 | err = PTR_ERR(hdr); | ||
4363 | goto free_msg; | ||
4364 | } | ||
4365 | |||
4366 | err = rdev->ops->remain_on_channel(&rdev->wiphy, dev, chan, | ||
4367 | channel_type, duration, &cookie); | ||
4368 | |||
4369 | if (err) | ||
4370 | goto free_msg; | ||
4371 | |||
4372 | NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, cookie); | ||
4373 | |||
4374 | genlmsg_end(msg, hdr); | ||
4375 | err = genlmsg_reply(msg, info); | ||
4376 | goto out; | ||
4377 | |||
4378 | nla_put_failure: | ||
4379 | err = -ENOBUFS; | ||
4380 | free_msg: | ||
4381 | nlmsg_free(msg); | ||
4382 | out: | ||
4383 | cfg80211_unlock_rdev(rdev); | ||
4384 | dev_put(dev); | ||
4385 | unlock_rtnl: | ||
4386 | rtnl_unlock(); | ||
4387 | return err; | ||
4388 | } | ||
4389 | |||
4390 | static int nl80211_cancel_remain_on_channel(struct sk_buff *skb, | ||
4391 | struct genl_info *info) | ||
4392 | { | ||
4393 | struct cfg80211_registered_device *rdev; | ||
4394 | struct net_device *dev; | ||
4395 | u64 cookie; | ||
4396 | int err; | ||
4397 | |||
4398 | if (!info->attrs[NL80211_ATTR_COOKIE]) | ||
4399 | return -EINVAL; | ||
4400 | |||
4401 | rtnl_lock(); | ||
4402 | |||
4403 | err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); | ||
4404 | if (err) | ||
4405 | goto unlock_rtnl; | ||
4406 | |||
4407 | if (!rdev->ops->cancel_remain_on_channel) { | ||
4408 | err = -EOPNOTSUPP; | ||
4409 | goto out; | ||
4410 | } | ||
4411 | |||
4412 | if (!netif_running(dev)) { | ||
4413 | err = -ENETDOWN; | ||
4414 | goto out; | ||
4415 | } | ||
4416 | |||
4417 | cookie = nla_get_u64(info->attrs[NL80211_ATTR_COOKIE]); | ||
4418 | |||
4419 | err = rdev->ops->cancel_remain_on_channel(&rdev->wiphy, dev, cookie); | ||
4420 | |||
4421 | out: | ||
4422 | cfg80211_unlock_rdev(rdev); | ||
4423 | dev_put(dev); | ||
4424 | unlock_rtnl: | ||
4425 | rtnl_unlock(); | ||
4426 | return err; | ||
4427 | } | ||
4428 | |||
4429 | static u32 rateset_to_mask(struct ieee80211_supported_band *sband, | ||
4430 | u8 *rates, u8 rates_len) | ||
4431 | { | ||
4432 | u8 i; | ||
4433 | u32 mask = 0; | ||
4434 | |||
4435 | for (i = 0; i < rates_len; i++) { | ||
4436 | int rate = (rates[i] & 0x7f) * 5; | ||
4437 | int ridx; | ||
4438 | for (ridx = 0; ridx < sband->n_bitrates; ridx++) { | ||
4439 | struct ieee80211_rate *srate = | ||
4440 | &sband->bitrates[ridx]; | ||
4441 | if (rate == srate->bitrate) { | ||
4442 | mask |= 1 << ridx; | ||
4443 | break; | ||
4444 | } | ||
4445 | } | ||
4446 | if (ridx == sband->n_bitrates) | ||
4447 | return 0; /* rate not found */ | ||
4448 | } | ||
4449 | |||
4450 | return mask; | ||
4451 | } | ||
4452 | |||
4453 | static const struct nla_policy nl80211_txattr_policy[NL80211_TXRATE_MAX + 1] = { | ||
4454 | [NL80211_TXRATE_LEGACY] = { .type = NLA_BINARY, | ||
4455 | .len = NL80211_MAX_SUPP_RATES }, | ||
4456 | }; | ||
4457 | |||
4458 | static int nl80211_set_tx_bitrate_mask(struct sk_buff *skb, | ||
4459 | struct genl_info *info) | ||
4460 | { | ||
4461 | struct nlattr *tb[NL80211_TXRATE_MAX + 1]; | ||
4462 | struct cfg80211_registered_device *rdev; | ||
4463 | struct cfg80211_bitrate_mask mask; | ||
4464 | int err, rem, i; | ||
4465 | struct net_device *dev; | ||
4466 | struct nlattr *tx_rates; | ||
4467 | struct ieee80211_supported_band *sband; | ||
4468 | |||
4469 | if (info->attrs[NL80211_ATTR_TX_RATES] == NULL) | ||
4470 | return -EINVAL; | ||
4471 | |||
4472 | rtnl_lock(); | ||
4473 | |||
4474 | err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); | ||
4475 | if (err) | ||
4476 | goto unlock_rtnl; | ||
4477 | |||
4478 | if (!rdev->ops->set_bitrate_mask) { | ||
4479 | err = -EOPNOTSUPP; | ||
4480 | goto unlock; | ||
4481 | } | ||
4482 | |||
4483 | memset(&mask, 0, sizeof(mask)); | ||
4484 | /* Default to all rates enabled */ | ||
4485 | for (i = 0; i < IEEE80211_NUM_BANDS; i++) { | ||
4486 | sband = rdev->wiphy.bands[i]; | ||
4487 | mask.control[i].legacy = | ||
4488 | sband ? (1 << sband->n_bitrates) - 1 : 0; | ||
4489 | } | ||
4490 | |||
4491 | /* | ||
4492 | * The nested attribute uses enum nl80211_band as the index. This maps | ||
4493 | * directly to the enum ieee80211_band values used in cfg80211. | ||
4494 | */ | ||
4495 | nla_for_each_nested(tx_rates, info->attrs[NL80211_ATTR_TX_RATES], rem) | ||
4496 | { | ||
4497 | enum ieee80211_band band = nla_type(tx_rates); | ||
4498 | if (band < 0 || band >= IEEE80211_NUM_BANDS) { | ||
4499 | err = -EINVAL; | ||
4500 | goto unlock; | ||
4501 | } | ||
4502 | sband = rdev->wiphy.bands[band]; | ||
4503 | if (sband == NULL) { | ||
4504 | err = -EINVAL; | ||
4505 | goto unlock; | ||
4506 | } | ||
4507 | nla_parse(tb, NL80211_TXRATE_MAX, nla_data(tx_rates), | ||
4508 | nla_len(tx_rates), nl80211_txattr_policy); | ||
4509 | if (tb[NL80211_TXRATE_LEGACY]) { | ||
4510 | mask.control[band].legacy = rateset_to_mask( | ||
4511 | sband, | ||
4512 | nla_data(tb[NL80211_TXRATE_LEGACY]), | ||
4513 | nla_len(tb[NL80211_TXRATE_LEGACY])); | ||
4514 | if (mask.control[band].legacy == 0) { | ||
4515 | err = -EINVAL; | ||
4516 | goto unlock; | ||
4517 | } | ||
4518 | } | ||
4519 | } | ||
4520 | |||
4521 | err = rdev->ops->set_bitrate_mask(&rdev->wiphy, dev, NULL, &mask); | ||
4522 | |||
4523 | unlock: | ||
4524 | dev_put(dev); | ||
4525 | cfg80211_unlock_rdev(rdev); | ||
4526 | unlock_rtnl: | ||
4527 | rtnl_unlock(); | ||
4528 | return err; | ||
4529 | } | ||
4530 | |||
4531 | static int nl80211_register_action(struct sk_buff *skb, struct genl_info *info) | ||
4532 | { | ||
4533 | struct cfg80211_registered_device *rdev; | ||
4534 | struct net_device *dev; | ||
4535 | int err; | ||
4536 | |||
4537 | if (!info->attrs[NL80211_ATTR_FRAME_MATCH]) | ||
4538 | return -EINVAL; | ||
4539 | |||
4540 | if (nla_len(info->attrs[NL80211_ATTR_FRAME_MATCH]) < 1) | ||
4541 | return -EINVAL; | ||
4542 | |||
4543 | rtnl_lock(); | ||
4544 | |||
4545 | err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); | ||
4546 | if (err) | ||
4547 | goto unlock_rtnl; | ||
4548 | |||
4549 | if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) { | ||
4550 | err = -EOPNOTSUPP; | ||
4551 | goto out; | ||
4552 | } | ||
4553 | |||
4554 | /* not much point in registering if we can't reply */ | ||
4555 | if (!rdev->ops->action) { | ||
4556 | err = -EOPNOTSUPP; | ||
4557 | goto out; | ||
4558 | } | ||
4559 | |||
4560 | err = cfg80211_mlme_register_action(dev->ieee80211_ptr, info->snd_pid, | ||
4561 | nla_data(info->attrs[NL80211_ATTR_FRAME_MATCH]), | ||
4562 | nla_len(info->attrs[NL80211_ATTR_FRAME_MATCH])); | ||
4563 | out: | ||
4564 | cfg80211_unlock_rdev(rdev); | ||
4565 | dev_put(dev); | ||
4566 | unlock_rtnl: | ||
4567 | rtnl_unlock(); | ||
4568 | return err; | ||
4569 | } | ||
4570 | |||
4571 | static int nl80211_action(struct sk_buff *skb, struct genl_info *info) | ||
4572 | { | ||
4573 | struct cfg80211_registered_device *rdev; | ||
4574 | struct net_device *dev; | ||
4575 | struct ieee80211_channel *chan; | ||
4576 | enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT; | ||
4577 | u32 freq; | ||
4578 | int err; | ||
4579 | void *hdr; | ||
4580 | u64 cookie; | ||
4581 | struct sk_buff *msg; | ||
4582 | |||
4583 | if (!info->attrs[NL80211_ATTR_FRAME] || | ||
4584 | !info->attrs[NL80211_ATTR_WIPHY_FREQ]) | ||
4585 | return -EINVAL; | ||
4586 | |||
4587 | rtnl_lock(); | ||
4588 | |||
4589 | err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); | ||
4590 | if (err) | ||
4591 | goto unlock_rtnl; | ||
4592 | |||
4593 | if (!rdev->ops->action) { | ||
4594 | err = -EOPNOTSUPP; | ||
4595 | goto out; | ||
4596 | } | ||
4597 | |||
4598 | if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) { | ||
4599 | err = -EOPNOTSUPP; | ||
4600 | goto out; | ||
4601 | } | ||
4602 | |||
4603 | if (!netif_running(dev)) { | ||
4604 | err = -ENETDOWN; | ||
4605 | goto out; | ||
4606 | } | ||
4607 | |||
4608 | if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) { | ||
4609 | channel_type = nla_get_u32( | ||
4610 | info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]); | ||
4611 | if (channel_type != NL80211_CHAN_NO_HT && | ||
4612 | channel_type != NL80211_CHAN_HT20 && | ||
4613 | channel_type != NL80211_CHAN_HT40PLUS && | ||
4614 | channel_type != NL80211_CHAN_HT40MINUS) | ||
4615 | err = -EINVAL; | ||
4616 | goto out; | ||
4617 | } | ||
4618 | |||
4619 | freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]); | ||
4620 | chan = rdev_freq_to_chan(rdev, freq, channel_type); | ||
4621 | if (chan == NULL) { | ||
4622 | err = -EINVAL; | ||
4623 | goto out; | ||
4624 | } | ||
4625 | |||
4626 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); | ||
4627 | if (!msg) { | ||
4628 | err = -ENOMEM; | ||
4629 | goto out; | ||
4630 | } | ||
4631 | |||
4632 | hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0, | ||
4633 | NL80211_CMD_ACTION); | ||
4634 | |||
4635 | if (IS_ERR(hdr)) { | ||
4636 | err = PTR_ERR(hdr); | ||
4637 | goto free_msg; | ||
4638 | } | ||
4639 | err = cfg80211_mlme_action(rdev, dev, chan, channel_type, | ||
4640 | nla_data(info->attrs[NL80211_ATTR_FRAME]), | ||
4641 | nla_len(info->attrs[NL80211_ATTR_FRAME]), | ||
4642 | &cookie); | ||
4643 | if (err) | ||
4644 | goto free_msg; | ||
4645 | |||
4646 | NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, cookie); | ||
4647 | |||
4648 | genlmsg_end(msg, hdr); | ||
4649 | err = genlmsg_reply(msg, info); | ||
4650 | goto out; | ||
4651 | |||
4652 | nla_put_failure: | ||
4653 | err = -ENOBUFS; | ||
4654 | free_msg: | ||
4655 | nlmsg_free(msg); | ||
4656 | out: | ||
4657 | cfg80211_unlock_rdev(rdev); | ||
4658 | dev_put(dev); | ||
4659 | unlock_rtnl: | ||
4660 | rtnl_unlock(); | ||
4661 | return err; | ||
4662 | } | ||
4663 | |||
4664 | static int nl80211_set_power_save(struct sk_buff *skb, struct genl_info *info) | ||
4665 | { | ||
4666 | struct cfg80211_registered_device *rdev; | ||
4667 | struct wireless_dev *wdev; | ||
4668 | struct net_device *dev; | ||
4669 | u8 ps_state; | ||
4670 | bool state; | ||
4671 | int err; | ||
4672 | |||
4673 | if (!info->attrs[NL80211_ATTR_PS_STATE]) { | ||
4674 | err = -EINVAL; | ||
4675 | goto out; | ||
4676 | } | ||
4677 | |||
4678 | ps_state = nla_get_u32(info->attrs[NL80211_ATTR_PS_STATE]); | ||
4679 | |||
4680 | if (ps_state != NL80211_PS_DISABLED && ps_state != NL80211_PS_ENABLED) { | ||
4681 | err = -EINVAL; | ||
4682 | goto out; | ||
4683 | } | ||
4684 | |||
4685 | rtnl_lock(); | ||
4686 | |||
4687 | err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); | ||
4688 | if (err) | ||
4689 | goto unlock_rdev; | ||
4690 | |||
4691 | wdev = dev->ieee80211_ptr; | ||
4692 | |||
4693 | if (!rdev->ops->set_power_mgmt) { | ||
4694 | err = -EOPNOTSUPP; | ||
4695 | goto unlock_rdev; | ||
4696 | } | ||
4697 | |||
4698 | state = (ps_state == NL80211_PS_ENABLED) ? true : false; | ||
4699 | |||
4700 | if (state == wdev->ps) | ||
4701 | goto unlock_rdev; | ||
4702 | |||
4703 | wdev->ps = state; | ||
4704 | |||
4705 | if (rdev->ops->set_power_mgmt(wdev->wiphy, dev, wdev->ps, | ||
4706 | wdev->ps_timeout)) | ||
4707 | /* assume this means it's off */ | ||
4708 | wdev->ps = false; | ||
4709 | |||
4710 | unlock_rdev: | ||
4711 | cfg80211_unlock_rdev(rdev); | ||
4712 | dev_put(dev); | ||
4713 | rtnl_unlock(); | ||
4714 | |||
4715 | out: | ||
4716 | return err; | ||
4717 | } | ||
4718 | |||
4719 | static int nl80211_get_power_save(struct sk_buff *skb, struct genl_info *info) | ||
4720 | { | ||
4721 | struct cfg80211_registered_device *rdev; | ||
4722 | enum nl80211_ps_state ps_state; | ||
4723 | struct wireless_dev *wdev; | ||
4724 | struct net_device *dev; | ||
4725 | struct sk_buff *msg; | ||
4726 | void *hdr; | ||
4727 | int err; | ||
4728 | |||
4729 | rtnl_lock(); | ||
4730 | |||
4731 | err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); | ||
4732 | if (err) | ||
4733 | goto unlock_rtnl; | ||
4734 | |||
4735 | wdev = dev->ieee80211_ptr; | ||
4736 | |||
4737 | if (!rdev->ops->set_power_mgmt) { | ||
4738 | err = -EOPNOTSUPP; | ||
4739 | goto out; | ||
4740 | } | ||
4741 | |||
4742 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); | ||
4743 | if (!msg) { | ||
4744 | err = -ENOMEM; | ||
4745 | goto out; | ||
4746 | } | ||
4747 | |||
4748 | hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0, | ||
4749 | NL80211_CMD_GET_POWER_SAVE); | ||
4750 | if (!hdr) { | ||
4751 | err = -ENOMEM; | ||
4752 | goto free_msg; | ||
4753 | } | ||
4754 | |||
4755 | if (wdev->ps) | ||
4756 | ps_state = NL80211_PS_ENABLED; | ||
4757 | else | ||
4758 | ps_state = NL80211_PS_DISABLED; | ||
4759 | |||
4760 | NLA_PUT_U32(msg, NL80211_ATTR_PS_STATE, ps_state); | ||
4761 | |||
4762 | genlmsg_end(msg, hdr); | ||
4763 | err = genlmsg_reply(msg, info); | ||
4764 | goto out; | ||
4765 | |||
4766 | nla_put_failure: | ||
4767 | err = -ENOBUFS; | ||
4768 | |||
4769 | free_msg: | ||
4770 | nlmsg_free(msg); | ||
4771 | |||
4772 | out: | ||
4773 | cfg80211_unlock_rdev(rdev); | ||
4774 | dev_put(dev); | ||
4775 | |||
4776 | unlock_rtnl: | ||
4777 | rtnl_unlock(); | ||
4778 | |||
4779 | return err; | ||
4780 | } | ||
4781 | |||
4057 | static struct genl_ops nl80211_ops[] = { | 4782 | static struct genl_ops nl80211_ops[] = { |
4058 | { | 4783 | { |
4059 | .cmd = NL80211_CMD_GET_WIPHY, | 4784 | .cmd = NL80211_CMD_GET_WIPHY, |
@@ -4293,7 +5018,73 @@ static struct genl_ops nl80211_ops[] = { | |||
4293 | .policy = nl80211_policy, | 5018 | .policy = nl80211_policy, |
4294 | .flags = GENL_ADMIN_PERM, | 5019 | .flags = GENL_ADMIN_PERM, |
4295 | }, | 5020 | }, |
5021 | { | ||
5022 | .cmd = NL80211_CMD_GET_SURVEY, | ||
5023 | .policy = nl80211_policy, | ||
5024 | .dumpit = nl80211_dump_survey, | ||
5025 | }, | ||
5026 | { | ||
5027 | .cmd = NL80211_CMD_SET_PMKSA, | ||
5028 | .doit = nl80211_setdel_pmksa, | ||
5029 | .policy = nl80211_policy, | ||
5030 | .flags = GENL_ADMIN_PERM, | ||
5031 | }, | ||
5032 | { | ||
5033 | .cmd = NL80211_CMD_DEL_PMKSA, | ||
5034 | .doit = nl80211_setdel_pmksa, | ||
5035 | .policy = nl80211_policy, | ||
5036 | .flags = GENL_ADMIN_PERM, | ||
5037 | }, | ||
5038 | { | ||
5039 | .cmd = NL80211_CMD_FLUSH_PMKSA, | ||
5040 | .doit = nl80211_flush_pmksa, | ||
5041 | .policy = nl80211_policy, | ||
5042 | .flags = GENL_ADMIN_PERM, | ||
5043 | }, | ||
5044 | { | ||
5045 | .cmd = NL80211_CMD_REMAIN_ON_CHANNEL, | ||
5046 | .doit = nl80211_remain_on_channel, | ||
5047 | .policy = nl80211_policy, | ||
5048 | .flags = GENL_ADMIN_PERM, | ||
5049 | }, | ||
5050 | { | ||
5051 | .cmd = NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL, | ||
5052 | .doit = nl80211_cancel_remain_on_channel, | ||
5053 | .policy = nl80211_policy, | ||
5054 | .flags = GENL_ADMIN_PERM, | ||
5055 | }, | ||
5056 | { | ||
5057 | .cmd = NL80211_CMD_SET_TX_BITRATE_MASK, | ||
5058 | .doit = nl80211_set_tx_bitrate_mask, | ||
5059 | .policy = nl80211_policy, | ||
5060 | .flags = GENL_ADMIN_PERM, | ||
5061 | }, | ||
5062 | { | ||
5063 | .cmd = NL80211_CMD_REGISTER_ACTION, | ||
5064 | .doit = nl80211_register_action, | ||
5065 | .policy = nl80211_policy, | ||
5066 | .flags = GENL_ADMIN_PERM, | ||
5067 | }, | ||
5068 | { | ||
5069 | .cmd = NL80211_CMD_ACTION, | ||
5070 | .doit = nl80211_action, | ||
5071 | .policy = nl80211_policy, | ||
5072 | .flags = GENL_ADMIN_PERM, | ||
5073 | }, | ||
5074 | { | ||
5075 | .cmd = NL80211_CMD_SET_POWER_SAVE, | ||
5076 | .doit = nl80211_set_power_save, | ||
5077 | .policy = nl80211_policy, | ||
5078 | .flags = GENL_ADMIN_PERM, | ||
5079 | }, | ||
5080 | { | ||
5081 | .cmd = NL80211_CMD_GET_POWER_SAVE, | ||
5082 | .doit = nl80211_get_power_save, | ||
5083 | .policy = nl80211_policy, | ||
5084 | /* can be retrieved by unprivileged users */ | ||
5085 | }, | ||
4296 | }; | 5086 | }; |
5087 | |||
4297 | static struct genl_multicast_group nl80211_mlme_mcgrp = { | 5088 | static struct genl_multicast_group nl80211_mlme_mcgrp = { |
4298 | .name = "mlme", | 5089 | .name = "mlme", |
4299 | }; | 5090 | }; |
@@ -4881,6 +5672,193 @@ nla_put_failure: | |||
4881 | nlmsg_free(msg); | 5672 | nlmsg_free(msg); |
4882 | } | 5673 | } |
4883 | 5674 | ||
5675 | static void nl80211_send_remain_on_chan_event( | ||
5676 | int cmd, struct cfg80211_registered_device *rdev, | ||
5677 | struct net_device *netdev, u64 cookie, | ||
5678 | struct ieee80211_channel *chan, | ||
5679 | enum nl80211_channel_type channel_type, | ||
5680 | unsigned int duration, gfp_t gfp) | ||
5681 | { | ||
5682 | struct sk_buff *msg; | ||
5683 | void *hdr; | ||
5684 | |||
5685 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); | ||
5686 | if (!msg) | ||
5687 | return; | ||
5688 | |||
5689 | hdr = nl80211hdr_put(msg, 0, 0, 0, cmd); | ||
5690 | if (!hdr) { | ||
5691 | nlmsg_free(msg); | ||
5692 | return; | ||
5693 | } | ||
5694 | |||
5695 | NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); | ||
5696 | NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex); | ||
5697 | NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, chan->center_freq); | ||
5698 | NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, channel_type); | ||
5699 | NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, cookie); | ||
5700 | |||
5701 | if (cmd == NL80211_CMD_REMAIN_ON_CHANNEL) | ||
5702 | NLA_PUT_U32(msg, NL80211_ATTR_DURATION, duration); | ||
5703 | |||
5704 | if (genlmsg_end(msg, hdr) < 0) { | ||
5705 | nlmsg_free(msg); | ||
5706 | return; | ||
5707 | } | ||
5708 | |||
5709 | genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, | ||
5710 | nl80211_mlme_mcgrp.id, gfp); | ||
5711 | return; | ||
5712 | |||
5713 | nla_put_failure: | ||
5714 | genlmsg_cancel(msg, hdr); | ||
5715 | nlmsg_free(msg); | ||
5716 | } | ||
5717 | |||
5718 | void nl80211_send_remain_on_channel(struct cfg80211_registered_device *rdev, | ||
5719 | struct net_device *netdev, u64 cookie, | ||
5720 | struct ieee80211_channel *chan, | ||
5721 | enum nl80211_channel_type channel_type, | ||
5722 | unsigned int duration, gfp_t gfp) | ||
5723 | { | ||
5724 | nl80211_send_remain_on_chan_event(NL80211_CMD_REMAIN_ON_CHANNEL, | ||
5725 | rdev, netdev, cookie, chan, | ||
5726 | channel_type, duration, gfp); | ||
5727 | } | ||
5728 | |||
5729 | void nl80211_send_remain_on_channel_cancel( | ||
5730 | struct cfg80211_registered_device *rdev, struct net_device *netdev, | ||
5731 | u64 cookie, struct ieee80211_channel *chan, | ||
5732 | enum nl80211_channel_type channel_type, gfp_t gfp) | ||
5733 | { | ||
5734 | nl80211_send_remain_on_chan_event(NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL, | ||
5735 | rdev, netdev, cookie, chan, | ||
5736 | channel_type, 0, gfp); | ||
5737 | } | ||
5738 | |||
5739 | void nl80211_send_sta_event(struct cfg80211_registered_device *rdev, | ||
5740 | struct net_device *dev, const u8 *mac_addr, | ||
5741 | struct station_info *sinfo, gfp_t gfp) | ||
5742 | { | ||
5743 | struct sk_buff *msg; | ||
5744 | |||
5745 | msg = nlmsg_new(NLMSG_GOODSIZE, gfp); | ||
5746 | if (!msg) | ||
5747 | return; | ||
5748 | |||
5749 | if (nl80211_send_station(msg, 0, 0, 0, dev, mac_addr, sinfo) < 0) { | ||
5750 | nlmsg_free(msg); | ||
5751 | return; | ||
5752 | } | ||
5753 | |||
5754 | genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, | ||
5755 | nl80211_mlme_mcgrp.id, gfp); | ||
5756 | } | ||
5757 | |||
5758 | int nl80211_send_action(struct cfg80211_registered_device *rdev, | ||
5759 | struct net_device *netdev, u32 nlpid, | ||
5760 | int freq, const u8 *buf, size_t len, gfp_t gfp) | ||
5761 | { | ||
5762 | struct sk_buff *msg; | ||
5763 | void *hdr; | ||
5764 | int err; | ||
5765 | |||
5766 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); | ||
5767 | if (!msg) | ||
5768 | return -ENOMEM; | ||
5769 | |||
5770 | hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_ACTION); | ||
5771 | if (!hdr) { | ||
5772 | nlmsg_free(msg); | ||
5773 | return -ENOMEM; | ||
5774 | } | ||
5775 | |||
5776 | NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); | ||
5777 | NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex); | ||
5778 | NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq); | ||
5779 | NLA_PUT(msg, NL80211_ATTR_FRAME, len, buf); | ||
5780 | |||
5781 | err = genlmsg_end(msg, hdr); | ||
5782 | if (err < 0) { | ||
5783 | nlmsg_free(msg); | ||
5784 | return err; | ||
5785 | } | ||
5786 | |||
5787 | err = genlmsg_unicast(wiphy_net(&rdev->wiphy), msg, nlpid); | ||
5788 | if (err < 0) | ||
5789 | return err; | ||
5790 | return 0; | ||
5791 | |||
5792 | nla_put_failure: | ||
5793 | genlmsg_cancel(msg, hdr); | ||
5794 | nlmsg_free(msg); | ||
5795 | return -ENOBUFS; | ||
5796 | } | ||
5797 | |||
5798 | void nl80211_send_action_tx_status(struct cfg80211_registered_device *rdev, | ||
5799 | struct net_device *netdev, u64 cookie, | ||
5800 | const u8 *buf, size_t len, bool ack, | ||
5801 | gfp_t gfp) | ||
5802 | { | ||
5803 | struct sk_buff *msg; | ||
5804 | void *hdr; | ||
5805 | |||
5806 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); | ||
5807 | if (!msg) | ||
5808 | return; | ||
5809 | |||
5810 | hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_ACTION_TX_STATUS); | ||
5811 | if (!hdr) { | ||
5812 | nlmsg_free(msg); | ||
5813 | return; | ||
5814 | } | ||
5815 | |||
5816 | NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); | ||
5817 | NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex); | ||
5818 | NLA_PUT(msg, NL80211_ATTR_FRAME, len, buf); | ||
5819 | NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, cookie); | ||
5820 | if (ack) | ||
5821 | NLA_PUT_FLAG(msg, NL80211_ATTR_ACK); | ||
5822 | |||
5823 | if (genlmsg_end(msg, hdr) < 0) { | ||
5824 | nlmsg_free(msg); | ||
5825 | return; | ||
5826 | } | ||
5827 | |||
5828 | genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, gfp); | ||
5829 | return; | ||
5830 | |||
5831 | nla_put_failure: | ||
5832 | genlmsg_cancel(msg, hdr); | ||
5833 | nlmsg_free(msg); | ||
5834 | } | ||
5835 | |||
5836 | static int nl80211_netlink_notify(struct notifier_block * nb, | ||
5837 | unsigned long state, | ||
5838 | void *_notify) | ||
5839 | { | ||
5840 | struct netlink_notify *notify = _notify; | ||
5841 | struct cfg80211_registered_device *rdev; | ||
5842 | struct wireless_dev *wdev; | ||
5843 | |||
5844 | if (state != NETLINK_URELEASE) | ||
5845 | return NOTIFY_DONE; | ||
5846 | |||
5847 | rcu_read_lock(); | ||
5848 | |||
5849 | list_for_each_entry_rcu(rdev, &cfg80211_rdev_list, list) | ||
5850 | list_for_each_entry_rcu(wdev, &rdev->netdev_list, list) | ||
5851 | cfg80211_mlme_unregister_actions(wdev, notify->pid); | ||
5852 | |||
5853 | rcu_read_unlock(); | ||
5854 | |||
5855 | return NOTIFY_DONE; | ||
5856 | } | ||
5857 | |||
5858 | static struct notifier_block nl80211_netlink_notifier = { | ||
5859 | .notifier_call = nl80211_netlink_notify, | ||
5860 | }; | ||
5861 | |||
4884 | /* initialisation/exit functions */ | 5862 | /* initialisation/exit functions */ |
4885 | 5863 | ||
4886 | int nl80211_init(void) | 5864 | int nl80211_init(void) |
@@ -4914,6 +5892,10 @@ int nl80211_init(void) | |||
4914 | goto err_out; | 5892 | goto err_out; |
4915 | #endif | 5893 | #endif |
4916 | 5894 | ||
5895 | err = netlink_register_notifier(&nl80211_netlink_notifier); | ||
5896 | if (err) | ||
5897 | goto err_out; | ||
5898 | |||
4917 | return 0; | 5899 | return 0; |
4918 | err_out: | 5900 | err_out: |
4919 | genl_unregister_family(&nl80211_fam); | 5901 | genl_unregister_family(&nl80211_fam); |
@@ -4922,5 +5904,6 @@ int nl80211_init(void) | |||
4922 | 5904 | ||
4923 | void nl80211_exit(void) | 5905 | void nl80211_exit(void) |
4924 | { | 5906 | { |
5907 | netlink_unregister_notifier(&nl80211_netlink_notifier); | ||
4925 | genl_unregister_family(&nl80211_fam); | 5908 | genl_unregister_family(&nl80211_fam); |
4926 | } | 5909 | } |