diff options
Diffstat (limited to 'net/wireless/nl80211.c')
-rw-r--r-- | net/wireless/nl80211.c | 286 |
1 files changed, 229 insertions, 57 deletions
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index ca3c92a0a14f..149539ade15e 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c | |||
@@ -138,6 +138,7 @@ static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = { | |||
138 | [NL80211_ATTR_CIPHER_SUITE_GROUP] = { .type = NLA_U32 }, | 138 | [NL80211_ATTR_CIPHER_SUITE_GROUP] = { .type = NLA_U32 }, |
139 | [NL80211_ATTR_WPA_VERSIONS] = { .type = NLA_U32 }, | 139 | [NL80211_ATTR_WPA_VERSIONS] = { .type = NLA_U32 }, |
140 | [NL80211_ATTR_PID] = { .type = NLA_U32 }, | 140 | [NL80211_ATTR_PID] = { .type = NLA_U32 }, |
141 | [NL80211_ATTR_4ADDR] = { .type = NLA_U8 }, | ||
141 | }; | 142 | }; |
142 | 143 | ||
143 | /* policy for the attributes */ | 144 | /* policy for the attributes */ |
@@ -151,6 +152,26 @@ nl80211_key_policy[NL80211_KEY_MAX + 1] __read_mostly = { | |||
151 | [NL80211_KEY_DEFAULT_MGMT] = { .type = NLA_FLAG }, | 152 | [NL80211_KEY_DEFAULT_MGMT] = { .type = NLA_FLAG }, |
152 | }; | 153 | }; |
153 | 154 | ||
155 | /* ifidx get helper */ | ||
156 | static int nl80211_get_ifidx(struct netlink_callback *cb) | ||
157 | { | ||
158 | int res; | ||
159 | |||
160 | res = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize, | ||
161 | nl80211_fam.attrbuf, nl80211_fam.maxattr, | ||
162 | nl80211_policy); | ||
163 | if (res) | ||
164 | return res; | ||
165 | |||
166 | if (!nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX]) | ||
167 | return -EINVAL; | ||
168 | |||
169 | res = nla_get_u32(nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX]); | ||
170 | if (!res) | ||
171 | return -EINVAL; | ||
172 | return res; | ||
173 | } | ||
174 | |||
154 | /* IE validation */ | 175 | /* IE validation */ |
155 | static bool is_valid_ie_attr(const struct nlattr *attr) | 176 | static bool is_valid_ie_attr(const struct nlattr *attr) |
156 | { | 177 | { |
@@ -540,7 +561,7 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, | |||
540 | CMD(deauth, DEAUTHENTICATE); | 561 | CMD(deauth, DEAUTHENTICATE); |
541 | CMD(disassoc, DISASSOCIATE); | 562 | CMD(disassoc, DISASSOCIATE); |
542 | CMD(join_ibss, JOIN_IBSS); | 563 | CMD(join_ibss, JOIN_IBSS); |
543 | if (dev->wiphy.netnsok) { | 564 | if (dev->wiphy.flags & WIPHY_FLAG_NETNS_OK) { |
544 | i++; | 565 | i++; |
545 | NLA_PUT_U32(msg, i, NL80211_CMD_SET_WIPHY_NETNS); | 566 | NLA_PUT_U32(msg, i, NL80211_CMD_SET_WIPHY_NETNS); |
546 | } | 567 | } |
@@ -947,6 +968,32 @@ static int parse_monitor_flags(struct nlattr *nla, u32 *mntrflags) | |||
947 | return 0; | 968 | return 0; |
948 | } | 969 | } |
949 | 970 | ||
971 | static int nl80211_valid_4addr(struct cfg80211_registered_device *rdev, | ||
972 | struct net_device *netdev, u8 use_4addr, | ||
973 | enum nl80211_iftype iftype) | ||
974 | { | ||
975 | if (!use_4addr) { | ||
976 | if (netdev && netdev->br_port) | ||
977 | return -EBUSY; | ||
978 | return 0; | ||
979 | } | ||
980 | |||
981 | switch (iftype) { | ||
982 | case NL80211_IFTYPE_AP_VLAN: | ||
983 | if (rdev->wiphy.flags & WIPHY_FLAG_4ADDR_AP) | ||
984 | return 0; | ||
985 | break; | ||
986 | case NL80211_IFTYPE_STATION: | ||
987 | if (rdev->wiphy.flags & WIPHY_FLAG_4ADDR_STATION) | ||
988 | return 0; | ||
989 | break; | ||
990 | default: | ||
991 | break; | ||
992 | } | ||
993 | |||
994 | return -EOPNOTSUPP; | ||
995 | } | ||
996 | |||
950 | static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info) | 997 | static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info) |
951 | { | 998 | { |
952 | struct cfg80211_registered_device *rdev; | 999 | struct cfg80211_registered_device *rdev; |
@@ -987,6 +1034,16 @@ static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info) | |||
987 | change = true; | 1034 | change = true; |
988 | } | 1035 | } |
989 | 1036 | ||
1037 | if (info->attrs[NL80211_ATTR_4ADDR]) { | ||
1038 | params.use_4addr = !!nla_get_u8(info->attrs[NL80211_ATTR_4ADDR]); | ||
1039 | change = true; | ||
1040 | err = nl80211_valid_4addr(rdev, dev, params.use_4addr, ntype); | ||
1041 | if (err) | ||
1042 | goto unlock; | ||
1043 | } else { | ||
1044 | params.use_4addr = -1; | ||
1045 | } | ||
1046 | |||
990 | if (info->attrs[NL80211_ATTR_MNTR_FLAGS]) { | 1047 | if (info->attrs[NL80211_ATTR_MNTR_FLAGS]) { |
991 | if (ntype != NL80211_IFTYPE_MONITOR) { | 1048 | if (ntype != NL80211_IFTYPE_MONITOR) { |
992 | err = -EINVAL; | 1049 | err = -EINVAL; |
@@ -1006,6 +1063,9 @@ static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info) | |||
1006 | else | 1063 | else |
1007 | err = 0; | 1064 | err = 0; |
1008 | 1065 | ||
1066 | if (!err && params.use_4addr != -1) | ||
1067 | dev->ieee80211_ptr->use_4addr = params.use_4addr; | ||
1068 | |||
1009 | unlock: | 1069 | unlock: |
1010 | dev_put(dev); | 1070 | dev_put(dev); |
1011 | cfg80211_unlock_rdev(rdev); | 1071 | cfg80211_unlock_rdev(rdev); |
@@ -1053,6 +1113,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]); | 1113 | params.mesh_id_len = nla_len(info->attrs[NL80211_ATTR_MESH_ID]); |
1054 | } | 1114 | } |
1055 | 1115 | ||
1116 | if (info->attrs[NL80211_ATTR_4ADDR]) { | ||
1117 | params.use_4addr = !!nla_get_u8(info->attrs[NL80211_ATTR_4ADDR]); | ||
1118 | err = nl80211_valid_4addr(rdev, NULL, params.use_4addr, type); | ||
1119 | if (err) | ||
1120 | goto unlock; | ||
1121 | } | ||
1122 | |||
1056 | err = parse_monitor_flags(type == NL80211_IFTYPE_MONITOR ? | 1123 | err = parse_monitor_flags(type == NL80211_IFTYPE_MONITOR ? |
1057 | info->attrs[NL80211_ATTR_MNTR_FLAGS] : NULL, | 1124 | info->attrs[NL80211_ATTR_MNTR_FLAGS] : NULL, |
1058 | &flags); | 1125 | &flags); |
@@ -1264,7 +1331,7 @@ static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info) | |||
1264 | if (!err) | 1331 | if (!err) |
1265 | err = func(&rdev->wiphy, dev, key.idx); | 1332 | err = func(&rdev->wiphy, dev, key.idx); |
1266 | 1333 | ||
1267 | #ifdef CONFIG_WIRELESS_EXT | 1334 | #ifdef CONFIG_CFG80211_WEXT |
1268 | if (!err) { | 1335 | if (!err) { |
1269 | if (func == rdev->ops->set_default_key) | 1336 | if (func == rdev->ops->set_default_key) |
1270 | dev->ieee80211_ptr->wext.default_key = key.idx; | 1337 | dev->ieee80211_ptr->wext.default_key = key.idx; |
@@ -1365,7 +1432,7 @@ static int nl80211_del_key(struct sk_buff *skb, struct genl_info *info) | |||
1365 | if (!err) | 1432 | if (!err) |
1366 | err = rdev->ops->del_key(&rdev->wiphy, dev, key.idx, mac_addr); | 1433 | err = rdev->ops->del_key(&rdev->wiphy, dev, key.idx, mac_addr); |
1367 | 1434 | ||
1368 | #ifdef CONFIG_WIRELESS_EXT | 1435 | #ifdef CONFIG_CFG80211_WEXT |
1369 | if (!err) { | 1436 | if (!err) { |
1370 | if (key.idx == dev->ieee80211_ptr->wext.default_key) | 1437 | if (key.idx == dev->ieee80211_ptr->wext.default_key) |
1371 | dev->ieee80211_ptr->wext.default_key = -1; | 1438 | dev->ieee80211_ptr->wext.default_key = -1; |
@@ -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 | } |
@@ -2105,9 +2164,9 @@ static int nl80211_send_mpath(struct sk_buff *msg, u32 pid, u32 seq, | |||
2105 | if (pinfo->filled & MPATH_INFO_FRAME_QLEN) | 2164 | if (pinfo->filled & MPATH_INFO_FRAME_QLEN) |
2106 | NLA_PUT_U32(msg, NL80211_MPATH_INFO_FRAME_QLEN, | 2165 | NLA_PUT_U32(msg, NL80211_MPATH_INFO_FRAME_QLEN, |
2107 | pinfo->frame_qlen); | 2166 | pinfo->frame_qlen); |
2108 | if (pinfo->filled & MPATH_INFO_DSN) | 2167 | if (pinfo->filled & MPATH_INFO_SN) |
2109 | NLA_PUT_U32(msg, NL80211_MPATH_INFO_DSN, | 2168 | NLA_PUT_U32(msg, NL80211_MPATH_INFO_SN, |
2110 | pinfo->dsn); | 2169 | pinfo->sn); |
2111 | if (pinfo->filled & MPATH_INFO_METRIC) | 2170 | if (pinfo->filled & MPATH_INFO_METRIC) |
2112 | NLA_PUT_U32(msg, NL80211_MPATH_INFO_METRIC, | 2171 | NLA_PUT_U32(msg, NL80211_MPATH_INFO_METRIC, |
2113 | pinfo->metric); | 2172 | pinfo->metric); |
@@ -2145,20 +2204,10 @@ static int nl80211_dump_mpath(struct sk_buff *skb, | |||
2145 | int path_idx = cb->args[1]; | 2204 | int path_idx = cb->args[1]; |
2146 | int err; | 2205 | int err; |
2147 | 2206 | ||
2148 | if (!ifidx) { | 2207 | if (!ifidx) |
2149 | err = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize, | 2208 | ifidx = nl80211_get_ifidx(cb); |
2150 | nl80211_fam.attrbuf, nl80211_fam.maxattr, | 2209 | if (ifidx < 0) |
2151 | nl80211_policy); | 2210 | 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 | 2211 | ||
2163 | rtnl_lock(); | 2212 | rtnl_lock(); |
2164 | 2213 | ||
@@ -2605,6 +2654,8 @@ static int nl80211_get_mesh_params(struct sk_buff *skb, | |||
2605 | cur_params.dot11MeshHWMPpreqMinInterval); | 2654 | cur_params.dot11MeshHWMPpreqMinInterval); |
2606 | NLA_PUT_U16(msg, NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME, | 2655 | NLA_PUT_U16(msg, NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME, |
2607 | cur_params.dot11MeshHWMPnetDiameterTraversalTime); | 2656 | cur_params.dot11MeshHWMPnetDiameterTraversalTime); |
2657 | NLA_PUT_U8(msg, NL80211_MESHCONF_HWMP_ROOTMODE, | ||
2658 | cur_params.dot11MeshHWMPRootMode); | ||
2608 | nla_nest_end(msg, pinfoattr); | 2659 | nla_nest_end(msg, pinfoattr); |
2609 | genlmsg_end(msg, hdr); | 2660 | genlmsg_end(msg, hdr); |
2610 | err = genlmsg_reply(msg, info); | 2661 | err = genlmsg_reply(msg, info); |
@@ -2715,6 +2766,10 @@ static int nl80211_set_mesh_params(struct sk_buff *skb, struct genl_info *info) | |||
2715 | dot11MeshHWMPnetDiameterTraversalTime, | 2766 | dot11MeshHWMPnetDiameterTraversalTime, |
2716 | mask, NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME, | 2767 | mask, NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME, |
2717 | nla_get_u16); | 2768 | nla_get_u16); |
2769 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, | ||
2770 | dot11MeshHWMPRootMode, mask, | ||
2771 | NL80211_MESHCONF_HWMP_ROOTMODE, | ||
2772 | nla_get_u8); | ||
2718 | 2773 | ||
2719 | /* Apply changes */ | 2774 | /* Apply changes */ |
2720 | err = rdev->ops->set_mesh_params(&rdev->wiphy, dev, &cfg, mask); | 2775 | err = rdev->ops->set_mesh_params(&rdev->wiphy, dev, &cfg, mask); |
@@ -2988,7 +3043,6 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info) | |||
2988 | goto out; | 3043 | goto out; |
2989 | } | 3044 | } |
2990 | 3045 | ||
2991 | request->n_channels = n_channels; | ||
2992 | if (n_ssids) | 3046 | if (n_ssids) |
2993 | request->ssids = (void *)&request->channels[n_channels]; | 3047 | request->ssids = (void *)&request->channels[n_channels]; |
2994 | request->n_ssids = n_ssids; | 3048 | request->n_ssids = n_ssids; |
@@ -2999,32 +3053,53 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info) | |||
2999 | request->ie = (void *)(request->channels + n_channels); | 3053 | request->ie = (void *)(request->channels + n_channels); |
3000 | } | 3054 | } |
3001 | 3055 | ||
3056 | i = 0; | ||
3002 | if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) { | 3057 | if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) { |
3003 | /* user specified, bail out if channel not found */ | 3058 | /* 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) { | 3059 | nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_FREQUENCIES], tmp) { |
3007 | request->channels[i] = ieee80211_get_channel(wiphy, nla_get_u32(attr)); | 3060 | struct ieee80211_channel *chan; |
3008 | if (!request->channels[i]) { | 3061 | |
3062 | chan = ieee80211_get_channel(wiphy, nla_get_u32(attr)); | ||
3063 | |||
3064 | if (!chan) { | ||
3009 | err = -EINVAL; | 3065 | err = -EINVAL; |
3010 | goto out_free; | 3066 | goto out_free; |
3011 | } | 3067 | } |
3068 | |||
3069 | /* ignore disabled channels */ | ||
3070 | if (chan->flags & IEEE80211_CHAN_DISABLED) | ||
3071 | continue; | ||
3072 | |||
3073 | request->channels[i] = chan; | ||
3012 | i++; | 3074 | i++; |
3013 | } | 3075 | } |
3014 | } else { | 3076 | } else { |
3015 | /* all channels */ | 3077 | /* all channels */ |
3016 | i = 0; | ||
3017 | for (band = 0; band < IEEE80211_NUM_BANDS; band++) { | 3078 | for (band = 0; band < IEEE80211_NUM_BANDS; band++) { |
3018 | int j; | 3079 | int j; |
3019 | if (!wiphy->bands[band]) | 3080 | if (!wiphy->bands[band]) |
3020 | continue; | 3081 | continue; |
3021 | for (j = 0; j < wiphy->bands[band]->n_channels; j++) { | 3082 | for (j = 0; j < wiphy->bands[band]->n_channels; j++) { |
3022 | request->channels[i] = &wiphy->bands[band]->channels[j]; | 3083 | struct ieee80211_channel *chan; |
3084 | |||
3085 | chan = &wiphy->bands[band]->channels[j]; | ||
3086 | |||
3087 | if (chan->flags & IEEE80211_CHAN_DISABLED) | ||
3088 | continue; | ||
3089 | |||
3090 | request->channels[i] = chan; | ||
3023 | i++; | 3091 | i++; |
3024 | } | 3092 | } |
3025 | } | 3093 | } |
3026 | } | 3094 | } |
3027 | 3095 | ||
3096 | if (!i) { | ||
3097 | err = -EINVAL; | ||
3098 | goto out_free; | ||
3099 | } | ||
3100 | |||
3101 | request->n_channels = i; | ||
3102 | |||
3028 | i = 0; | 3103 | i = 0; |
3029 | if (info->attrs[NL80211_ATTR_SCAN_SSIDS]) { | 3104 | if (info->attrs[NL80211_ATTR_SCAN_SSIDS]) { |
3030 | nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS], tmp) { | 3105 | nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS], tmp) { |
@@ -3105,6 +3180,8 @@ static int nl80211_send_bss(struct sk_buff *msg, u32 pid, u32 seq, int flags, | |||
3105 | NLA_PUT_U16(msg, NL80211_BSS_BEACON_INTERVAL, res->beacon_interval); | 3180 | NLA_PUT_U16(msg, NL80211_BSS_BEACON_INTERVAL, res->beacon_interval); |
3106 | NLA_PUT_U16(msg, NL80211_BSS_CAPABILITY, res->capability); | 3181 | NLA_PUT_U16(msg, NL80211_BSS_CAPABILITY, res->capability); |
3107 | NLA_PUT_U32(msg, NL80211_BSS_FREQUENCY, res->channel->center_freq); | 3182 | NLA_PUT_U32(msg, NL80211_BSS_FREQUENCY, res->channel->center_freq); |
3183 | NLA_PUT_U32(msg, NL80211_BSS_SEEN_MS_AGO, | ||
3184 | jiffies_to_msecs(jiffies - intbss->ts)); | ||
3108 | 3185 | ||
3109 | switch (rdev->wiphy.signal_type) { | 3186 | switch (rdev->wiphy.signal_type) { |
3110 | case CFG80211_SIGNAL_TYPE_MBM: | 3187 | case CFG80211_SIGNAL_TYPE_MBM: |
@@ -3159,21 +3236,11 @@ static int nl80211_dump_scan(struct sk_buff *skb, | |||
3159 | int start = cb->args[1], idx = 0; | 3236 | int start = cb->args[1], idx = 0; |
3160 | int err; | 3237 | int err; |
3161 | 3238 | ||
3162 | if (!ifidx) { | 3239 | if (!ifidx) |
3163 | err = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize, | 3240 | ifidx = nl80211_get_ifidx(cb); |
3164 | nl80211_fam.attrbuf, nl80211_fam.maxattr, | 3241 | if (ifidx < 0) |
3165 | nl80211_policy); | 3242 | return ifidx; |
3166 | if (err) | 3243 | 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 | 3244 | ||
3178 | dev = dev_get_by_index(sock_net(skb->sk), ifidx); | 3245 | dev = dev_get_by_index(sock_net(skb->sk), ifidx); |
3179 | if (!dev) | 3246 | if (!dev) |
@@ -3216,6 +3283,106 @@ static int nl80211_dump_scan(struct sk_buff *skb, | |||
3216 | return err; | 3283 | return err; |
3217 | } | 3284 | } |
3218 | 3285 | ||
3286 | static int nl80211_send_survey(struct sk_buff *msg, u32 pid, u32 seq, | ||
3287 | int flags, struct net_device *dev, | ||
3288 | struct survey_info *survey) | ||
3289 | { | ||
3290 | void *hdr; | ||
3291 | struct nlattr *infoattr; | ||
3292 | |||
3293 | /* Survey without a channel doesn't make sense */ | ||
3294 | if (!survey->channel) | ||
3295 | return -EINVAL; | ||
3296 | |||
3297 | hdr = nl80211hdr_put(msg, pid, seq, flags, | ||
3298 | NL80211_CMD_NEW_SURVEY_RESULTS); | ||
3299 | if (!hdr) | ||
3300 | return -ENOMEM; | ||
3301 | |||
3302 | NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex); | ||
3303 | |||
3304 | infoattr = nla_nest_start(msg, NL80211_ATTR_SURVEY_INFO); | ||
3305 | if (!infoattr) | ||
3306 | goto nla_put_failure; | ||
3307 | |||
3308 | NLA_PUT_U32(msg, NL80211_SURVEY_INFO_FREQUENCY, | ||
3309 | survey->channel->center_freq); | ||
3310 | if (survey->filled & SURVEY_INFO_NOISE_DBM) | ||
3311 | NLA_PUT_U8(msg, NL80211_SURVEY_INFO_NOISE, | ||
3312 | survey->noise); | ||
3313 | |||
3314 | nla_nest_end(msg, infoattr); | ||
3315 | |||
3316 | return genlmsg_end(msg, hdr); | ||
3317 | |||
3318 | nla_put_failure: | ||
3319 | genlmsg_cancel(msg, hdr); | ||
3320 | return -EMSGSIZE; | ||
3321 | } | ||
3322 | |||
3323 | static int nl80211_dump_survey(struct sk_buff *skb, | ||
3324 | struct netlink_callback *cb) | ||
3325 | { | ||
3326 | struct survey_info survey; | ||
3327 | struct cfg80211_registered_device *dev; | ||
3328 | struct net_device *netdev; | ||
3329 | int ifidx = cb->args[0]; | ||
3330 | int survey_idx = cb->args[1]; | ||
3331 | int res; | ||
3332 | |||
3333 | if (!ifidx) | ||
3334 | ifidx = nl80211_get_ifidx(cb); | ||
3335 | if (ifidx < 0) | ||
3336 | return ifidx; | ||
3337 | cb->args[0] = ifidx; | ||
3338 | |||
3339 | rtnl_lock(); | ||
3340 | |||
3341 | netdev = __dev_get_by_index(sock_net(skb->sk), ifidx); | ||
3342 | if (!netdev) { | ||
3343 | res = -ENODEV; | ||
3344 | goto out_rtnl; | ||
3345 | } | ||
3346 | |||
3347 | dev = cfg80211_get_dev_from_ifindex(sock_net(skb->sk), ifidx); | ||
3348 | if (IS_ERR(dev)) { | ||
3349 | res = PTR_ERR(dev); | ||
3350 | goto out_rtnl; | ||
3351 | } | ||
3352 | |||
3353 | if (!dev->ops->dump_survey) { | ||
3354 | res = -EOPNOTSUPP; | ||
3355 | goto out_err; | ||
3356 | } | ||
3357 | |||
3358 | while (1) { | ||
3359 | res = dev->ops->dump_survey(&dev->wiphy, netdev, survey_idx, | ||
3360 | &survey); | ||
3361 | if (res == -ENOENT) | ||
3362 | break; | ||
3363 | if (res) | ||
3364 | goto out_err; | ||
3365 | |||
3366 | if (nl80211_send_survey(skb, | ||
3367 | NETLINK_CB(cb->skb).pid, | ||
3368 | cb->nlh->nlmsg_seq, NLM_F_MULTI, | ||
3369 | netdev, | ||
3370 | &survey) < 0) | ||
3371 | goto out; | ||
3372 | survey_idx++; | ||
3373 | } | ||
3374 | |||
3375 | out: | ||
3376 | cb->args[1] = survey_idx; | ||
3377 | res = skb->len; | ||
3378 | out_err: | ||
3379 | cfg80211_unlock_rdev(dev); | ||
3380 | out_rtnl: | ||
3381 | rtnl_unlock(); | ||
3382 | |||
3383 | return res; | ||
3384 | } | ||
3385 | |||
3219 | static bool nl80211_valid_auth_type(enum nl80211_auth_type auth_type) | 3386 | static bool nl80211_valid_auth_type(enum nl80211_auth_type auth_type) |
3220 | { | 3387 | { |
3221 | return auth_type <= NL80211_AUTHTYPE_MAX; | 3388 | return auth_type <= NL80211_AUTHTYPE_MAX; |
@@ -4293,6 +4460,11 @@ static struct genl_ops nl80211_ops[] = { | |||
4293 | .policy = nl80211_policy, | 4460 | .policy = nl80211_policy, |
4294 | .flags = GENL_ADMIN_PERM, | 4461 | .flags = GENL_ADMIN_PERM, |
4295 | }, | 4462 | }, |
4463 | { | ||
4464 | .cmd = NL80211_CMD_GET_SURVEY, | ||
4465 | .policy = nl80211_policy, | ||
4466 | .dumpit = nl80211_dump_survey, | ||
4467 | }, | ||
4296 | }; | 4468 | }; |
4297 | static struct genl_multicast_group nl80211_mlme_mcgrp = { | 4469 | static struct genl_multicast_group nl80211_mlme_mcgrp = { |
4298 | .name = "mlme", | 4470 | .name = "mlme", |