diff options
Diffstat (limited to 'net/wireless/nl80211.c')
-rw-r--r-- | net/wireless/nl80211.c | 602 |
1 files changed, 583 insertions, 19 deletions
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 31b807af323..ab9d8f14e15 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c | |||
@@ -7,13 +7,13 @@ | |||
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/mutex.h> | ||
11 | #include <linux/list.h> | 10 | #include <linux/list.h> |
12 | #include <linux/if_ether.h> | 11 | #include <linux/if_ether.h> |
13 | #include <linux/ieee80211.h> | 12 | #include <linux/ieee80211.h> |
14 | #include <linux/nl80211.h> | 13 | #include <linux/nl80211.h> |
15 | #include <linux/rtnetlink.h> | 14 | #include <linux/rtnetlink.h> |
16 | #include <linux/netlink.h> | 15 | #include <linux/netlink.h> |
16 | #include <linux/etherdevice.h> | ||
17 | #include <net/genetlink.h> | 17 | #include <net/genetlink.h> |
18 | #include <net/cfg80211.h> | 18 | #include <net/cfg80211.h> |
19 | #include "core.h" | 19 | #include "core.h" |
@@ -105,6 +105,12 @@ static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = { | |||
105 | 105 | ||
106 | [NL80211_ATTR_HT_CAPABILITY] = { .type = NLA_BINARY, | 106 | [NL80211_ATTR_HT_CAPABILITY] = { .type = NLA_BINARY, |
107 | .len = NL80211_HT_CAPABILITY_LEN }, | 107 | .len = NL80211_HT_CAPABILITY_LEN }, |
108 | |||
109 | [NL80211_ATTR_MGMT_SUBTYPE] = { .type = NLA_U8 }, | ||
110 | [NL80211_ATTR_IE] = { .type = NLA_BINARY, | ||
111 | .len = IEEE80211_MAX_DATA_LEN }, | ||
112 | [NL80211_ATTR_SCAN_FREQUENCIES] = { .type = NLA_NESTED }, | ||
113 | [NL80211_ATTR_SCAN_SSIDS] = { .type = NLA_NESTED }, | ||
108 | }; | 114 | }; |
109 | 115 | ||
110 | /* message building helper */ | 116 | /* message building helper */ |
@@ -135,8 +141,10 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, | |||
135 | if (!hdr) | 141 | if (!hdr) |
136 | return -1; | 142 | return -1; |
137 | 143 | ||
138 | NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, dev->idx); | 144 | NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, dev->wiphy_idx); |
139 | NLA_PUT_STRING(msg, NL80211_ATTR_WIPHY_NAME, wiphy_name(&dev->wiphy)); | 145 | NLA_PUT_STRING(msg, NL80211_ATTR_WIPHY_NAME, wiphy_name(&dev->wiphy)); |
146 | NLA_PUT_U8(msg, NL80211_ATTR_MAX_NUM_SCAN_SSIDS, | ||
147 | dev->wiphy.max_scan_ssids); | ||
140 | 148 | ||
141 | nl_modes = nla_nest_start(msg, NL80211_ATTR_SUPPORTED_IFTYPES); | 149 | nl_modes = nla_nest_start(msg, NL80211_ATTR_SUPPORTED_IFTYPES); |
142 | if (!nl_modes) | 150 | if (!nl_modes) |
@@ -247,7 +255,7 @@ static int nl80211_dump_wiphy(struct sk_buff *skb, struct netlink_callback *cb) | |||
247 | int start = cb->args[0]; | 255 | int start = cb->args[0]; |
248 | struct cfg80211_registered_device *dev; | 256 | struct cfg80211_registered_device *dev; |
249 | 257 | ||
250 | mutex_lock(&cfg80211_drv_mutex); | 258 | mutex_lock(&cfg80211_mutex); |
251 | list_for_each_entry(dev, &cfg80211_drv_list, list) { | 259 | list_for_each_entry(dev, &cfg80211_drv_list, list) { |
252 | if (++idx <= start) | 260 | if (++idx <= start) |
253 | continue; | 261 | continue; |
@@ -258,7 +266,7 @@ static int nl80211_dump_wiphy(struct sk_buff *skb, struct netlink_callback *cb) | |||
258 | break; | 266 | break; |
259 | } | 267 | } |
260 | } | 268 | } |
261 | mutex_unlock(&cfg80211_drv_mutex); | 269 | mutex_unlock(&cfg80211_mutex); |
262 | 270 | ||
263 | cb->args[0] = idx; | 271 | cb->args[0] = idx; |
264 | 272 | ||
@@ -461,7 +469,7 @@ static int nl80211_dump_interface(struct sk_buff *skb, struct netlink_callback * | |||
461 | struct cfg80211_registered_device *dev; | 469 | struct cfg80211_registered_device *dev; |
462 | struct wireless_dev *wdev; | 470 | struct wireless_dev *wdev; |
463 | 471 | ||
464 | mutex_lock(&cfg80211_drv_mutex); | 472 | mutex_lock(&cfg80211_mutex); |
465 | list_for_each_entry(dev, &cfg80211_drv_list, list) { | 473 | list_for_each_entry(dev, &cfg80211_drv_list, list) { |
466 | if (wp_idx < wp_start) { | 474 | if (wp_idx < wp_start) { |
467 | wp_idx++; | 475 | wp_idx++; |
@@ -488,7 +496,7 @@ static int nl80211_dump_interface(struct sk_buff *skb, struct netlink_callback * | |||
488 | wp_idx++; | 496 | wp_idx++; |
489 | } | 497 | } |
490 | out: | 498 | out: |
491 | mutex_unlock(&cfg80211_drv_mutex); | 499 | mutex_unlock(&cfg80211_mutex); |
492 | 500 | ||
493 | cb->args[0] = wp_idx; | 501 | cb->args[0] = wp_idx; |
494 | cb->args[1] = if_idx; | 502 | cb->args[1] = if_idx; |
@@ -738,7 +746,7 @@ static int nl80211_get_key(struct sk_buff *skb, struct genl_info *info) | |||
738 | if (info->attrs[NL80211_ATTR_KEY_IDX]) | 746 | if (info->attrs[NL80211_ATTR_KEY_IDX]) |
739 | key_idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]); | 747 | key_idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]); |
740 | 748 | ||
741 | if (key_idx > 3) | 749 | if (key_idx > 5) |
742 | return -EINVAL; | 750 | return -EINVAL; |
743 | 751 | ||
744 | if (info->attrs[NL80211_ATTR_MAC]) | 752 | if (info->attrs[NL80211_ATTR_MAC]) |
@@ -804,30 +812,41 @@ static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info) | |||
804 | int err; | 812 | int err; |
805 | struct net_device *dev; | 813 | struct net_device *dev; |
806 | u8 key_idx; | 814 | u8 key_idx; |
815 | int (*func)(struct wiphy *wiphy, struct net_device *netdev, | ||
816 | u8 key_index); | ||
807 | 817 | ||
808 | if (!info->attrs[NL80211_ATTR_KEY_IDX]) | 818 | if (!info->attrs[NL80211_ATTR_KEY_IDX]) |
809 | return -EINVAL; | 819 | return -EINVAL; |
810 | 820 | ||
811 | key_idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]); | 821 | key_idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]); |
812 | 822 | ||
813 | if (key_idx > 3) | 823 | if (info->attrs[NL80211_ATTR_KEY_DEFAULT_MGMT]) { |
824 | if (key_idx < 4 || key_idx > 5) | ||
825 | return -EINVAL; | ||
826 | } else if (key_idx > 3) | ||
814 | return -EINVAL; | 827 | return -EINVAL; |
815 | 828 | ||
816 | /* currently only support setting default key */ | 829 | /* currently only support setting default key */ |
817 | if (!info->attrs[NL80211_ATTR_KEY_DEFAULT]) | 830 | if (!info->attrs[NL80211_ATTR_KEY_DEFAULT] && |
831 | !info->attrs[NL80211_ATTR_KEY_DEFAULT_MGMT]) | ||
818 | return -EINVAL; | 832 | return -EINVAL; |
819 | 833 | ||
820 | err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); | 834 | err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); |
821 | if (err) | 835 | if (err) |
822 | return err; | 836 | return err; |
823 | 837 | ||
824 | if (!drv->ops->set_default_key) { | 838 | if (info->attrs[NL80211_ATTR_KEY_DEFAULT]) |
839 | func = drv->ops->set_default_key; | ||
840 | else | ||
841 | func = drv->ops->set_default_mgmt_key; | ||
842 | |||
843 | if (!func) { | ||
825 | err = -EOPNOTSUPP; | 844 | err = -EOPNOTSUPP; |
826 | goto out; | 845 | goto out; |
827 | } | 846 | } |
828 | 847 | ||
829 | rtnl_lock(); | 848 | rtnl_lock(); |
830 | err = drv->ops->set_default_key(&drv->wiphy, dev, key_idx); | 849 | err = func(&drv->wiphy, dev, key_idx); |
831 | rtnl_unlock(); | 850 | rtnl_unlock(); |
832 | 851 | ||
833 | out: | 852 | out: |
@@ -863,7 +882,7 @@ static int nl80211_new_key(struct sk_buff *skb, struct genl_info *info) | |||
863 | if (info->attrs[NL80211_ATTR_MAC]) | 882 | if (info->attrs[NL80211_ATTR_MAC]) |
864 | mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]); | 883 | mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]); |
865 | 884 | ||
866 | if (key_idx > 3) | 885 | if (key_idx > 5) |
867 | return -EINVAL; | 886 | return -EINVAL; |
868 | 887 | ||
869 | /* | 888 | /* |
@@ -894,6 +913,10 @@ static int nl80211_new_key(struct sk_buff *skb, struct genl_info *info) | |||
894 | if (params.key_len != 13) | 913 | if (params.key_len != 13) |
895 | return -EINVAL; | 914 | return -EINVAL; |
896 | break; | 915 | break; |
916 | case WLAN_CIPHER_SUITE_AES_CMAC: | ||
917 | if (params.key_len != 16) | ||
918 | return -EINVAL; | ||
919 | break; | ||
897 | default: | 920 | default: |
898 | return -EINVAL; | 921 | return -EINVAL; |
899 | } | 922 | } |
@@ -928,7 +951,7 @@ static int nl80211_del_key(struct sk_buff *skb, struct genl_info *info) | |||
928 | if (info->attrs[NL80211_ATTR_KEY_IDX]) | 951 | if (info->attrs[NL80211_ATTR_KEY_IDX]) |
929 | key_idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]); | 952 | key_idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]); |
930 | 953 | ||
931 | if (key_idx > 3) | 954 | if (key_idx > 5) |
932 | return -EINVAL; | 955 | return -EINVAL; |
933 | 956 | ||
934 | if (info->attrs[NL80211_ATTR_MAC]) | 957 | if (info->attrs[NL80211_ATTR_MAC]) |
@@ -1182,6 +1205,12 @@ static int nl80211_send_station(struct sk_buff *msg, u32 pid, u32 seq, | |||
1182 | 1205 | ||
1183 | nla_nest_end(msg, txrate); | 1206 | nla_nest_end(msg, txrate); |
1184 | } | 1207 | } |
1208 | if (sinfo->filled & STATION_INFO_RX_PACKETS) | ||
1209 | NLA_PUT_U32(msg, NL80211_STA_INFO_RX_PACKETS, | ||
1210 | sinfo->rx_packets); | ||
1211 | if (sinfo->filled & STATION_INFO_TX_PACKETS) | ||
1212 | NLA_PUT_U32(msg, NL80211_STA_INFO_TX_PACKETS, | ||
1213 | sinfo->tx_packets); | ||
1185 | nla_nest_end(msg, sinfoattr); | 1214 | nla_nest_end(msg, sinfoattr); |
1186 | 1215 | ||
1187 | return genlmsg_end(msg, hdr); | 1216 | return genlmsg_end(msg, hdr); |
@@ -1876,6 +1905,19 @@ static int nl80211_req_set_reg(struct sk_buff *skb, struct genl_info *info) | |||
1876 | int r; | 1905 | int r; |
1877 | char *data = NULL; | 1906 | char *data = NULL; |
1878 | 1907 | ||
1908 | /* | ||
1909 | * You should only get this when cfg80211 hasn't yet initialized | ||
1910 | * completely when built-in to the kernel right between the time | ||
1911 | * window between nl80211_init() and regulatory_init(), if that is | ||
1912 | * even possible. | ||
1913 | */ | ||
1914 | mutex_lock(&cfg80211_mutex); | ||
1915 | if (unlikely(!cfg80211_regdomain)) { | ||
1916 | mutex_unlock(&cfg80211_mutex); | ||
1917 | return -EINPROGRESS; | ||
1918 | } | ||
1919 | mutex_unlock(&cfg80211_mutex); | ||
1920 | |||
1879 | if (!info->attrs[NL80211_ATTR_REG_ALPHA2]) | 1921 | if (!info->attrs[NL80211_ATTR_REG_ALPHA2]) |
1880 | return -EINVAL; | 1922 | return -EINVAL; |
1881 | 1923 | ||
@@ -1886,9 +1928,9 @@ static int nl80211_req_set_reg(struct sk_buff *skb, struct genl_info *info) | |||
1886 | if (is_world_regdom(data)) | 1928 | if (is_world_regdom(data)) |
1887 | return -EINVAL; | 1929 | return -EINVAL; |
1888 | #endif | 1930 | #endif |
1889 | mutex_lock(&cfg80211_drv_mutex); | 1931 | |
1890 | r = __regulatory_hint(NULL, REGDOM_SET_BY_USER, data, 0, ENVIRON_ANY); | 1932 | r = regulatory_hint_user(data); |
1891 | mutex_unlock(&cfg80211_drv_mutex); | 1933 | |
1892 | return r; | 1934 | return r; |
1893 | } | 1935 | } |
1894 | 1936 | ||
@@ -2080,6 +2122,81 @@ static int nl80211_set_mesh_params(struct sk_buff *skb, struct genl_info *info) | |||
2080 | 2122 | ||
2081 | #undef FILL_IN_MESH_PARAM_IF_SET | 2123 | #undef FILL_IN_MESH_PARAM_IF_SET |
2082 | 2124 | ||
2125 | static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info) | ||
2126 | { | ||
2127 | struct sk_buff *msg; | ||
2128 | void *hdr = NULL; | ||
2129 | struct nlattr *nl_reg_rules; | ||
2130 | unsigned int i; | ||
2131 | int err = -EINVAL; | ||
2132 | |||
2133 | mutex_lock(&cfg80211_mutex); | ||
2134 | |||
2135 | if (!cfg80211_regdomain) | ||
2136 | goto out; | ||
2137 | |||
2138 | msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); | ||
2139 | if (!msg) { | ||
2140 | err = -ENOBUFS; | ||
2141 | goto out; | ||
2142 | } | ||
2143 | |||
2144 | hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0, | ||
2145 | NL80211_CMD_GET_REG); | ||
2146 | if (!hdr) | ||
2147 | goto nla_put_failure; | ||
2148 | |||
2149 | NLA_PUT_STRING(msg, NL80211_ATTR_REG_ALPHA2, | ||
2150 | cfg80211_regdomain->alpha2); | ||
2151 | |||
2152 | nl_reg_rules = nla_nest_start(msg, NL80211_ATTR_REG_RULES); | ||
2153 | if (!nl_reg_rules) | ||
2154 | goto nla_put_failure; | ||
2155 | |||
2156 | for (i = 0; i < cfg80211_regdomain->n_reg_rules; i++) { | ||
2157 | struct nlattr *nl_reg_rule; | ||
2158 | const struct ieee80211_reg_rule *reg_rule; | ||
2159 | const struct ieee80211_freq_range *freq_range; | ||
2160 | const struct ieee80211_power_rule *power_rule; | ||
2161 | |||
2162 | reg_rule = &cfg80211_regdomain->reg_rules[i]; | ||
2163 | freq_range = ®_rule->freq_range; | ||
2164 | power_rule = ®_rule->power_rule; | ||
2165 | |||
2166 | nl_reg_rule = nla_nest_start(msg, i); | ||
2167 | if (!nl_reg_rule) | ||
2168 | goto nla_put_failure; | ||
2169 | |||
2170 | NLA_PUT_U32(msg, NL80211_ATTR_REG_RULE_FLAGS, | ||
2171 | reg_rule->flags); | ||
2172 | NLA_PUT_U32(msg, NL80211_ATTR_FREQ_RANGE_START, | ||
2173 | freq_range->start_freq_khz); | ||
2174 | NLA_PUT_U32(msg, NL80211_ATTR_FREQ_RANGE_END, | ||
2175 | freq_range->end_freq_khz); | ||
2176 | NLA_PUT_U32(msg, NL80211_ATTR_FREQ_RANGE_MAX_BW, | ||
2177 | freq_range->max_bandwidth_khz); | ||
2178 | NLA_PUT_U32(msg, NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN, | ||
2179 | power_rule->max_antenna_gain); | ||
2180 | NLA_PUT_U32(msg, NL80211_ATTR_POWER_RULE_MAX_EIRP, | ||
2181 | power_rule->max_eirp); | ||
2182 | |||
2183 | nla_nest_end(msg, nl_reg_rule); | ||
2184 | } | ||
2185 | |||
2186 | nla_nest_end(msg, nl_reg_rules); | ||
2187 | |||
2188 | genlmsg_end(msg, hdr); | ||
2189 | err = genlmsg_unicast(msg, info->snd_pid); | ||
2190 | goto out; | ||
2191 | |||
2192 | nla_put_failure: | ||
2193 | genlmsg_cancel(msg, hdr); | ||
2194 | err = -EMSGSIZE; | ||
2195 | out: | ||
2196 | mutex_unlock(&cfg80211_mutex); | ||
2197 | return err; | ||
2198 | } | ||
2199 | |||
2083 | static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info) | 2200 | static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info) |
2084 | { | 2201 | { |
2085 | struct nlattr *tb[NL80211_REG_RULE_ATTR_MAX + 1]; | 2202 | struct nlattr *tb[NL80211_REG_RULE_ATTR_MAX + 1]; |
@@ -2135,9 +2252,9 @@ static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info) | |||
2135 | 2252 | ||
2136 | BUG_ON(rule_idx != num_rules); | 2253 | BUG_ON(rule_idx != num_rules); |
2137 | 2254 | ||
2138 | mutex_lock(&cfg80211_drv_mutex); | 2255 | mutex_lock(&cfg80211_mutex); |
2139 | r = set_regdom(rd); | 2256 | r = set_regdom(rd); |
2140 | mutex_unlock(&cfg80211_drv_mutex); | 2257 | mutex_unlock(&cfg80211_mutex); |
2141 | return r; | 2258 | return r; |
2142 | 2259 | ||
2143 | bad_reg: | 2260 | bad_reg: |
@@ -2145,6 +2262,302 @@ static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info) | |||
2145 | return -EINVAL; | 2262 | return -EINVAL; |
2146 | } | 2263 | } |
2147 | 2264 | ||
2265 | static int nl80211_set_mgmt_extra_ie(struct sk_buff *skb, | ||
2266 | struct genl_info *info) | ||
2267 | { | ||
2268 | struct cfg80211_registered_device *drv; | ||
2269 | int err; | ||
2270 | struct net_device *dev; | ||
2271 | struct mgmt_extra_ie_params params; | ||
2272 | |||
2273 | memset(¶ms, 0, sizeof(params)); | ||
2274 | |||
2275 | if (!info->attrs[NL80211_ATTR_MGMT_SUBTYPE]) | ||
2276 | return -EINVAL; | ||
2277 | params.subtype = nla_get_u8(info->attrs[NL80211_ATTR_MGMT_SUBTYPE]); | ||
2278 | if (params.subtype > 15) | ||
2279 | return -EINVAL; /* FC Subtype field is 4 bits (0..15) */ | ||
2280 | |||
2281 | if (info->attrs[NL80211_ATTR_IE]) { | ||
2282 | params.ies = nla_data(info->attrs[NL80211_ATTR_IE]); | ||
2283 | params.ies_len = nla_len(info->attrs[NL80211_ATTR_IE]); | ||
2284 | } | ||
2285 | |||
2286 | err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); | ||
2287 | if (err) | ||
2288 | return err; | ||
2289 | |||
2290 | if (drv->ops->set_mgmt_extra_ie) { | ||
2291 | rtnl_lock(); | ||
2292 | err = drv->ops->set_mgmt_extra_ie(&drv->wiphy, dev, ¶ms); | ||
2293 | rtnl_unlock(); | ||
2294 | } else | ||
2295 | err = -EOPNOTSUPP; | ||
2296 | |||
2297 | cfg80211_put_dev(drv); | ||
2298 | dev_put(dev); | ||
2299 | return err; | ||
2300 | } | ||
2301 | |||
2302 | static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info) | ||
2303 | { | ||
2304 | struct cfg80211_registered_device *drv; | ||
2305 | struct net_device *dev; | ||
2306 | struct cfg80211_scan_request *request; | ||
2307 | struct cfg80211_ssid *ssid; | ||
2308 | struct ieee80211_channel *channel; | ||
2309 | struct nlattr *attr; | ||
2310 | struct wiphy *wiphy; | ||
2311 | int err, tmp, n_ssids = 0, n_channels = 0, i; | ||
2312 | enum ieee80211_band band; | ||
2313 | size_t ie_len; | ||
2314 | |||
2315 | err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); | ||
2316 | if (err) | ||
2317 | return err; | ||
2318 | |||
2319 | wiphy = &drv->wiphy; | ||
2320 | |||
2321 | if (!drv->ops->scan) { | ||
2322 | err = -EOPNOTSUPP; | ||
2323 | goto out; | ||
2324 | } | ||
2325 | |||
2326 | rtnl_lock(); | ||
2327 | |||
2328 | if (drv->scan_req) { | ||
2329 | err = -EBUSY; | ||
2330 | goto out_unlock; | ||
2331 | } | ||
2332 | |||
2333 | if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) { | ||
2334 | nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_FREQUENCIES], tmp) | ||
2335 | n_channels++; | ||
2336 | if (!n_channels) { | ||
2337 | err = -EINVAL; | ||
2338 | goto out_unlock; | ||
2339 | } | ||
2340 | } else { | ||
2341 | for (band = 0; band < IEEE80211_NUM_BANDS; band++) | ||
2342 | if (wiphy->bands[band]) | ||
2343 | n_channels += wiphy->bands[band]->n_channels; | ||
2344 | } | ||
2345 | |||
2346 | if (info->attrs[NL80211_ATTR_SCAN_SSIDS]) | ||
2347 | nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS], tmp) | ||
2348 | n_ssids++; | ||
2349 | |||
2350 | if (n_ssids > wiphy->max_scan_ssids) { | ||
2351 | err = -EINVAL; | ||
2352 | goto out_unlock; | ||
2353 | } | ||
2354 | |||
2355 | if (info->attrs[NL80211_ATTR_IE]) | ||
2356 | ie_len = nla_len(info->attrs[NL80211_ATTR_IE]); | ||
2357 | else | ||
2358 | ie_len = 0; | ||
2359 | |||
2360 | request = kzalloc(sizeof(*request) | ||
2361 | + sizeof(*ssid) * n_ssids | ||
2362 | + sizeof(channel) * n_channels | ||
2363 | + ie_len, GFP_KERNEL); | ||
2364 | if (!request) { | ||
2365 | err = -ENOMEM; | ||
2366 | goto out_unlock; | ||
2367 | } | ||
2368 | |||
2369 | request->channels = (void *)((char *)request + sizeof(*request)); | ||
2370 | request->n_channels = n_channels; | ||
2371 | if (n_ssids) | ||
2372 | request->ssids = (void *)(request->channels + n_channels); | ||
2373 | request->n_ssids = n_ssids; | ||
2374 | if (ie_len) { | ||
2375 | if (request->ssids) | ||
2376 | request->ie = (void *)(request->ssids + n_ssids); | ||
2377 | else | ||
2378 | request->ie = (void *)(request->channels + n_channels); | ||
2379 | } | ||
2380 | |||
2381 | if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) { | ||
2382 | /* user specified, bail out if channel not found */ | ||
2383 | request->n_channels = n_channels; | ||
2384 | i = 0; | ||
2385 | nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_FREQUENCIES], tmp) { | ||
2386 | request->channels[i] = ieee80211_get_channel(wiphy, nla_get_u32(attr)); | ||
2387 | if (!request->channels[i]) { | ||
2388 | err = -EINVAL; | ||
2389 | goto out_free; | ||
2390 | } | ||
2391 | i++; | ||
2392 | } | ||
2393 | } else { | ||
2394 | /* all channels */ | ||
2395 | i = 0; | ||
2396 | for (band = 0; band < IEEE80211_NUM_BANDS; band++) { | ||
2397 | int j; | ||
2398 | if (!wiphy->bands[band]) | ||
2399 | continue; | ||
2400 | for (j = 0; j < wiphy->bands[band]->n_channels; j++) { | ||
2401 | request->channels[i] = &wiphy->bands[band]->channels[j]; | ||
2402 | i++; | ||
2403 | } | ||
2404 | } | ||
2405 | } | ||
2406 | |||
2407 | i = 0; | ||
2408 | if (info->attrs[NL80211_ATTR_SCAN_SSIDS]) { | ||
2409 | nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS], tmp) { | ||
2410 | if (request->ssids[i].ssid_len > IEEE80211_MAX_SSID_LEN) { | ||
2411 | err = -EINVAL; | ||
2412 | goto out_free; | ||
2413 | } | ||
2414 | memcpy(request->ssids[i].ssid, nla_data(attr), nla_len(attr)); | ||
2415 | request->ssids[i].ssid_len = nla_len(attr); | ||
2416 | i++; | ||
2417 | } | ||
2418 | } | ||
2419 | |||
2420 | if (info->attrs[NL80211_ATTR_IE]) { | ||
2421 | request->ie_len = nla_len(info->attrs[NL80211_ATTR_IE]); | ||
2422 | memcpy(request->ie, nla_data(info->attrs[NL80211_ATTR_IE]), | ||
2423 | request->ie_len); | ||
2424 | } | ||
2425 | |||
2426 | request->ifidx = dev->ifindex; | ||
2427 | request->wiphy = &drv->wiphy; | ||
2428 | |||
2429 | drv->scan_req = request; | ||
2430 | err = drv->ops->scan(&drv->wiphy, dev, request); | ||
2431 | |||
2432 | out_free: | ||
2433 | if (err) { | ||
2434 | drv->scan_req = NULL; | ||
2435 | kfree(request); | ||
2436 | } | ||
2437 | out_unlock: | ||
2438 | rtnl_unlock(); | ||
2439 | out: | ||
2440 | cfg80211_put_dev(drv); | ||
2441 | dev_put(dev); | ||
2442 | return err; | ||
2443 | } | ||
2444 | |||
2445 | static int nl80211_send_bss(struct sk_buff *msg, u32 pid, u32 seq, int flags, | ||
2446 | struct cfg80211_registered_device *rdev, | ||
2447 | struct net_device *dev, | ||
2448 | struct cfg80211_bss *res) | ||
2449 | { | ||
2450 | void *hdr; | ||
2451 | struct nlattr *bss; | ||
2452 | |||
2453 | hdr = nl80211hdr_put(msg, pid, seq, flags, | ||
2454 | NL80211_CMD_NEW_SCAN_RESULTS); | ||
2455 | if (!hdr) | ||
2456 | return -1; | ||
2457 | |||
2458 | NLA_PUT_U32(msg, NL80211_ATTR_SCAN_GENERATION, | ||
2459 | rdev->bss_generation); | ||
2460 | NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex); | ||
2461 | |||
2462 | bss = nla_nest_start(msg, NL80211_ATTR_BSS); | ||
2463 | if (!bss) | ||
2464 | goto nla_put_failure; | ||
2465 | if (!is_zero_ether_addr(res->bssid)) | ||
2466 | NLA_PUT(msg, NL80211_BSS_BSSID, ETH_ALEN, res->bssid); | ||
2467 | if (res->information_elements && res->len_information_elements) | ||
2468 | NLA_PUT(msg, NL80211_BSS_INFORMATION_ELEMENTS, | ||
2469 | res->len_information_elements, | ||
2470 | res->information_elements); | ||
2471 | if (res->tsf) | ||
2472 | NLA_PUT_U64(msg, NL80211_BSS_TSF, res->tsf); | ||
2473 | if (res->beacon_interval) | ||
2474 | NLA_PUT_U16(msg, NL80211_BSS_BEACON_INTERVAL, res->beacon_interval); | ||
2475 | NLA_PUT_U16(msg, NL80211_BSS_CAPABILITY, res->capability); | ||
2476 | NLA_PUT_U32(msg, NL80211_BSS_FREQUENCY, res->channel->center_freq); | ||
2477 | |||
2478 | switch (rdev->wiphy.signal_type) { | ||
2479 | case CFG80211_SIGNAL_TYPE_MBM: | ||
2480 | NLA_PUT_U32(msg, NL80211_BSS_SIGNAL_MBM, res->signal); | ||
2481 | break; | ||
2482 | case CFG80211_SIGNAL_TYPE_UNSPEC: | ||
2483 | NLA_PUT_U8(msg, NL80211_BSS_SIGNAL_UNSPEC, res->signal); | ||
2484 | break; | ||
2485 | default: | ||
2486 | break; | ||
2487 | } | ||
2488 | |||
2489 | nla_nest_end(msg, bss); | ||
2490 | |||
2491 | return genlmsg_end(msg, hdr); | ||
2492 | |||
2493 | nla_put_failure: | ||
2494 | genlmsg_cancel(msg, hdr); | ||
2495 | return -EMSGSIZE; | ||
2496 | } | ||
2497 | |||
2498 | static int nl80211_dump_scan(struct sk_buff *skb, | ||
2499 | struct netlink_callback *cb) | ||
2500 | { | ||
2501 | struct cfg80211_registered_device *dev; | ||
2502 | struct net_device *netdev; | ||
2503 | struct cfg80211_internal_bss *scan; | ||
2504 | int ifidx = cb->args[0]; | ||
2505 | int start = cb->args[1], idx = 0; | ||
2506 | int err; | ||
2507 | |||
2508 | if (!ifidx) { | ||
2509 | err = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize, | ||
2510 | nl80211_fam.attrbuf, nl80211_fam.maxattr, | ||
2511 | nl80211_policy); | ||
2512 | if (err) | ||
2513 | return err; | ||
2514 | |||
2515 | if (!nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX]) | ||
2516 | return -EINVAL; | ||
2517 | |||
2518 | ifidx = nla_get_u32(nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX]); | ||
2519 | if (!ifidx) | ||
2520 | return -EINVAL; | ||
2521 | cb->args[0] = ifidx; | ||
2522 | } | ||
2523 | |||
2524 | netdev = dev_get_by_index(&init_net, ifidx); | ||
2525 | if (!netdev) | ||
2526 | return -ENODEV; | ||
2527 | |||
2528 | dev = cfg80211_get_dev_from_ifindex(ifidx); | ||
2529 | if (IS_ERR(dev)) { | ||
2530 | err = PTR_ERR(dev); | ||
2531 | goto out_put_netdev; | ||
2532 | } | ||
2533 | |||
2534 | spin_lock_bh(&dev->bss_lock); | ||
2535 | cfg80211_bss_expire(dev); | ||
2536 | |||
2537 | list_for_each_entry(scan, &dev->bss_list, list) { | ||
2538 | if (++idx <= start) | ||
2539 | continue; | ||
2540 | if (nl80211_send_bss(skb, | ||
2541 | NETLINK_CB(cb->skb).pid, | ||
2542 | cb->nlh->nlmsg_seq, NLM_F_MULTI, | ||
2543 | dev, netdev, &scan->pub) < 0) { | ||
2544 | idx--; | ||
2545 | goto out; | ||
2546 | } | ||
2547 | } | ||
2548 | |||
2549 | out: | ||
2550 | spin_unlock_bh(&dev->bss_lock); | ||
2551 | |||
2552 | cb->args[1] = idx; | ||
2553 | err = skb->len; | ||
2554 | cfg80211_put_dev(dev); | ||
2555 | out_put_netdev: | ||
2556 | dev_put(netdev); | ||
2557 | |||
2558 | return err; | ||
2559 | } | ||
2560 | |||
2148 | static struct genl_ops nl80211_ops[] = { | 2561 | static struct genl_ops nl80211_ops[] = { |
2149 | { | 2562 | { |
2150 | .cmd = NL80211_CMD_GET_WIPHY, | 2563 | .cmd = NL80211_CMD_GET_WIPHY, |
@@ -2231,7 +2644,6 @@ static struct genl_ops nl80211_ops[] = { | |||
2231 | .doit = nl80211_get_station, | 2644 | .doit = nl80211_get_station, |
2232 | .dumpit = nl80211_dump_station, | 2645 | .dumpit = nl80211_dump_station, |
2233 | .policy = nl80211_policy, | 2646 | .policy = nl80211_policy, |
2234 | .flags = GENL_ADMIN_PERM, | ||
2235 | }, | 2647 | }, |
2236 | { | 2648 | { |
2237 | .cmd = NL80211_CMD_SET_STATION, | 2649 | .cmd = NL80211_CMD_SET_STATION, |
@@ -2283,6 +2695,12 @@ static struct genl_ops nl80211_ops[] = { | |||
2283 | .flags = GENL_ADMIN_PERM, | 2695 | .flags = GENL_ADMIN_PERM, |
2284 | }, | 2696 | }, |
2285 | { | 2697 | { |
2698 | .cmd = NL80211_CMD_GET_REG, | ||
2699 | .doit = nl80211_get_reg, | ||
2700 | .policy = nl80211_policy, | ||
2701 | /* can be retrieved by unprivileged users */ | ||
2702 | }, | ||
2703 | { | ||
2286 | .cmd = NL80211_CMD_SET_REG, | 2704 | .cmd = NL80211_CMD_SET_REG, |
2287 | .doit = nl80211_set_reg, | 2705 | .doit = nl80211_set_reg, |
2288 | .policy = nl80211_policy, | 2706 | .policy = nl80211_policy, |
@@ -2306,12 +2724,35 @@ static struct genl_ops nl80211_ops[] = { | |||
2306 | .policy = nl80211_policy, | 2724 | .policy = nl80211_policy, |
2307 | .flags = GENL_ADMIN_PERM, | 2725 | .flags = GENL_ADMIN_PERM, |
2308 | }, | 2726 | }, |
2727 | { | ||
2728 | .cmd = NL80211_CMD_SET_MGMT_EXTRA_IE, | ||
2729 | .doit = nl80211_set_mgmt_extra_ie, | ||
2730 | .policy = nl80211_policy, | ||
2731 | .flags = GENL_ADMIN_PERM, | ||
2732 | }, | ||
2733 | { | ||
2734 | .cmd = NL80211_CMD_TRIGGER_SCAN, | ||
2735 | .doit = nl80211_trigger_scan, | ||
2736 | .policy = nl80211_policy, | ||
2737 | .flags = GENL_ADMIN_PERM, | ||
2738 | }, | ||
2739 | { | ||
2740 | .cmd = NL80211_CMD_GET_SCAN, | ||
2741 | .policy = nl80211_policy, | ||
2742 | .dumpit = nl80211_dump_scan, | ||
2743 | }, | ||
2309 | }; | 2744 | }; |
2310 | 2745 | ||
2311 | /* multicast groups */ | 2746 | /* multicast groups */ |
2312 | static struct genl_multicast_group nl80211_config_mcgrp = { | 2747 | static struct genl_multicast_group nl80211_config_mcgrp = { |
2313 | .name = "config", | 2748 | .name = "config", |
2314 | }; | 2749 | }; |
2750 | static struct genl_multicast_group nl80211_scan_mcgrp = { | ||
2751 | .name = "scan", | ||
2752 | }; | ||
2753 | static struct genl_multicast_group nl80211_regulatory_mcgrp = { | ||
2754 | .name = "regulatory", | ||
2755 | }; | ||
2315 | 2756 | ||
2316 | /* notification functions */ | 2757 | /* notification functions */ |
2317 | 2758 | ||
@@ -2331,6 +2772,121 @@ void nl80211_notify_dev_rename(struct cfg80211_registered_device *rdev) | |||
2331 | genlmsg_multicast(msg, 0, nl80211_config_mcgrp.id, GFP_KERNEL); | 2772 | genlmsg_multicast(msg, 0, nl80211_config_mcgrp.id, GFP_KERNEL); |
2332 | } | 2773 | } |
2333 | 2774 | ||
2775 | static int nl80211_send_scan_donemsg(struct sk_buff *msg, | ||
2776 | struct cfg80211_registered_device *rdev, | ||
2777 | struct net_device *netdev, | ||
2778 | u32 pid, u32 seq, int flags, | ||
2779 | u32 cmd) | ||
2780 | { | ||
2781 | void *hdr; | ||
2782 | |||
2783 | hdr = nl80211hdr_put(msg, pid, seq, flags, cmd); | ||
2784 | if (!hdr) | ||
2785 | return -1; | ||
2786 | |||
2787 | NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); | ||
2788 | NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex); | ||
2789 | |||
2790 | /* XXX: we should probably bounce back the request? */ | ||
2791 | |||
2792 | return genlmsg_end(msg, hdr); | ||
2793 | |||
2794 | nla_put_failure: | ||
2795 | genlmsg_cancel(msg, hdr); | ||
2796 | return -EMSGSIZE; | ||
2797 | } | ||
2798 | |||
2799 | void nl80211_send_scan_done(struct cfg80211_registered_device *rdev, | ||
2800 | struct net_device *netdev) | ||
2801 | { | ||
2802 | struct sk_buff *msg; | ||
2803 | |||
2804 | msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); | ||
2805 | if (!msg) | ||
2806 | return; | ||
2807 | |||
2808 | if (nl80211_send_scan_donemsg(msg, rdev, netdev, 0, 0, 0, | ||
2809 | NL80211_CMD_NEW_SCAN_RESULTS) < 0) { | ||
2810 | nlmsg_free(msg); | ||
2811 | return; | ||
2812 | } | ||
2813 | |||
2814 | genlmsg_multicast(msg, 0, nl80211_scan_mcgrp.id, GFP_KERNEL); | ||
2815 | } | ||
2816 | |||
2817 | void nl80211_send_scan_aborted(struct cfg80211_registered_device *rdev, | ||
2818 | struct net_device *netdev) | ||
2819 | { | ||
2820 | struct sk_buff *msg; | ||
2821 | |||
2822 | msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); | ||
2823 | if (!msg) | ||
2824 | return; | ||
2825 | |||
2826 | if (nl80211_send_scan_donemsg(msg, rdev, netdev, 0, 0, 0, | ||
2827 | NL80211_CMD_SCAN_ABORTED) < 0) { | ||
2828 | nlmsg_free(msg); | ||
2829 | return; | ||
2830 | } | ||
2831 | |||
2832 | genlmsg_multicast(msg, 0, nl80211_scan_mcgrp.id, GFP_KERNEL); | ||
2833 | } | ||
2834 | |||
2835 | /* | ||
2836 | * This can happen on global regulatory changes or device specific settings | ||
2837 | * based on custom world regulatory domains. | ||
2838 | */ | ||
2839 | void nl80211_send_reg_change_event(struct regulatory_request *request) | ||
2840 | { | ||
2841 | struct sk_buff *msg; | ||
2842 | void *hdr; | ||
2843 | |||
2844 | msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); | ||
2845 | if (!msg) | ||
2846 | return; | ||
2847 | |||
2848 | hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_REG_CHANGE); | ||
2849 | if (!hdr) { | ||
2850 | nlmsg_free(msg); | ||
2851 | return; | ||
2852 | } | ||
2853 | |||
2854 | /* Userspace can always count this one always being set */ | ||
2855 | NLA_PUT_U8(msg, NL80211_ATTR_REG_INITIATOR, request->initiator); | ||
2856 | |||
2857 | if (request->alpha2[0] == '0' && request->alpha2[1] == '0') | ||
2858 | NLA_PUT_U8(msg, NL80211_ATTR_REG_TYPE, | ||
2859 | NL80211_REGDOM_TYPE_WORLD); | ||
2860 | else if (request->alpha2[0] == '9' && request->alpha2[1] == '9') | ||
2861 | NLA_PUT_U8(msg, NL80211_ATTR_REG_TYPE, | ||
2862 | NL80211_REGDOM_TYPE_CUSTOM_WORLD); | ||
2863 | else if ((request->alpha2[0] == '9' && request->alpha2[1] == '8') || | ||
2864 | request->intersect) | ||
2865 | NLA_PUT_U8(msg, NL80211_ATTR_REG_TYPE, | ||
2866 | NL80211_REGDOM_TYPE_INTERSECTION); | ||
2867 | else { | ||
2868 | NLA_PUT_U8(msg, NL80211_ATTR_REG_TYPE, | ||
2869 | NL80211_REGDOM_TYPE_COUNTRY); | ||
2870 | NLA_PUT_STRING(msg, NL80211_ATTR_REG_ALPHA2, request->alpha2); | ||
2871 | } | ||
2872 | |||
2873 | if (wiphy_idx_valid(request->wiphy_idx)) | ||
2874 | NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, request->wiphy_idx); | ||
2875 | |||
2876 | if (genlmsg_end(msg, hdr) < 0) { | ||
2877 | nlmsg_free(msg); | ||
2878 | return; | ||
2879 | } | ||
2880 | |||
2881 | genlmsg_multicast(msg, 0, nl80211_regulatory_mcgrp.id, GFP_KERNEL); | ||
2882 | |||
2883 | return; | ||
2884 | |||
2885 | nla_put_failure: | ||
2886 | genlmsg_cancel(msg, hdr); | ||
2887 | nlmsg_free(msg); | ||
2888 | } | ||
2889 | |||
2334 | /* initialisation/exit functions */ | 2890 | /* initialisation/exit functions */ |
2335 | 2891 | ||
2336 | int nl80211_init(void) | 2892 | int nl80211_init(void) |
@@ -2351,6 +2907,14 @@ int nl80211_init(void) | |||
2351 | if (err) | 2907 | if (err) |
2352 | goto err_out; | 2908 | goto err_out; |
2353 | 2909 | ||
2910 | err = genl_register_mc_group(&nl80211_fam, &nl80211_scan_mcgrp); | ||
2911 | if (err) | ||
2912 | goto err_out; | ||
2913 | |||
2914 | err = genl_register_mc_group(&nl80211_fam, &nl80211_regulatory_mcgrp); | ||
2915 | if (err) | ||
2916 | goto err_out; | ||
2917 | |||
2354 | return 0; | 2918 | return 0; |
2355 | err_out: | 2919 | err_out: |
2356 | genl_unregister_family(&nl80211_fam); | 2920 | genl_unregister_family(&nl80211_fam); |