diff options
Diffstat (limited to 'net/wireless/nl80211.c')
-rw-r--r-- | net/wireless/nl80211.c | 208 |
1 files changed, 161 insertions, 47 deletions
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 8ed62b6c172b..37264d56bace 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 | { |
@@ -987,6 +1008,13 @@ static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info) | |||
987 | change = true; | 1008 | change = true; |
988 | } | 1009 | } |
989 | 1010 | ||
1011 | if (info->attrs[NL80211_ATTR_4ADDR]) { | ||
1012 | params.use_4addr = !!nla_get_u8(info->attrs[NL80211_ATTR_4ADDR]); | ||
1013 | change = true; | ||
1014 | } else { | ||
1015 | params.use_4addr = -1; | ||
1016 | } | ||
1017 | |||
990 | if (info->attrs[NL80211_ATTR_MNTR_FLAGS]) { | 1018 | if (info->attrs[NL80211_ATTR_MNTR_FLAGS]) { |
991 | if (ntype != NL80211_IFTYPE_MONITOR) { | 1019 | if (ntype != NL80211_IFTYPE_MONITOR) { |
992 | err = -EINVAL; | 1020 | err = -EINVAL; |
@@ -1053,6 +1081,9 @@ 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]); | 1081 | params.mesh_id_len = nla_len(info->attrs[NL80211_ATTR_MESH_ID]); |
1054 | } | 1082 | } |
1055 | 1083 | ||
1084 | if (info->attrs[NL80211_ATTR_4ADDR]) | ||
1085 | params.use_4addr = !!nla_get_u8(info->attrs[NL80211_ATTR_4ADDR]); | ||
1086 | |||
1056 | err = parse_monitor_flags(type == NL80211_IFTYPE_MONITOR ? | 1087 | err = parse_monitor_flags(type == NL80211_IFTYPE_MONITOR ? |
1057 | info->attrs[NL80211_ATTR_MNTR_FLAGS] : NULL, | 1088 | info->attrs[NL80211_ATTR_MNTR_FLAGS] : NULL, |
1058 | &flags); | 1089 | &flags); |
@@ -1682,20 +1713,10 @@ static int nl80211_dump_station(struct sk_buff *skb, | |||
1682 | int sta_idx = cb->args[1]; | 1713 | int sta_idx = cb->args[1]; |
1683 | int err; | 1714 | int err; |
1684 | 1715 | ||
1685 | if (!ifidx) { | 1716 | if (!ifidx) |
1686 | err = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize, | 1717 | ifidx = nl80211_get_ifidx(cb); |
1687 | nl80211_fam.attrbuf, nl80211_fam.maxattr, | 1718 | if (ifidx < 0) |
1688 | nl80211_policy); | 1719 | 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 | 1720 | ||
1700 | rtnl_lock(); | 1721 | rtnl_lock(); |
1701 | 1722 | ||
@@ -1800,7 +1821,7 @@ static int nl80211_get_station(struct sk_buff *skb, struct genl_info *info) | |||
1800 | } | 1821 | } |
1801 | 1822 | ||
1802 | /* | 1823 | /* |
1803 | * Get vlan interface making sure it is on the right wiphy. | 1824 | * Get vlan interface making sure it is running and on the right wiphy. |
1804 | */ | 1825 | */ |
1805 | static int get_vlan(struct genl_info *info, | 1826 | static int get_vlan(struct genl_info *info, |
1806 | struct cfg80211_registered_device *rdev, | 1827 | struct cfg80211_registered_device *rdev, |
@@ -1818,6 +1839,8 @@ static int get_vlan(struct genl_info *info, | |||
1818 | return -EINVAL; | 1839 | return -EINVAL; |
1819 | if ((*vlan)->ieee80211_ptr->wiphy != &rdev->wiphy) | 1840 | if ((*vlan)->ieee80211_ptr->wiphy != &rdev->wiphy) |
1820 | return -EINVAL; | 1841 | return -EINVAL; |
1842 | if (!netif_running(*vlan)) | ||
1843 | return -ENETDOWN; | ||
1821 | } | 1844 | } |
1822 | return 0; | 1845 | return 0; |
1823 | } | 1846 | } |
@@ -2105,9 +2128,9 @@ static int nl80211_send_mpath(struct sk_buff *msg, u32 pid, u32 seq, | |||
2105 | if (pinfo->filled & MPATH_INFO_FRAME_QLEN) | 2128 | if (pinfo->filled & MPATH_INFO_FRAME_QLEN) |
2106 | NLA_PUT_U32(msg, NL80211_MPATH_INFO_FRAME_QLEN, | 2129 | NLA_PUT_U32(msg, NL80211_MPATH_INFO_FRAME_QLEN, |
2107 | pinfo->frame_qlen); | 2130 | pinfo->frame_qlen); |
2108 | if (pinfo->filled & MPATH_INFO_DSN) | 2131 | if (pinfo->filled & MPATH_INFO_SN) |
2109 | NLA_PUT_U32(msg, NL80211_MPATH_INFO_DSN, | 2132 | NLA_PUT_U32(msg, NL80211_MPATH_INFO_SN, |
2110 | pinfo->dsn); | 2133 | pinfo->sn); |
2111 | if (pinfo->filled & MPATH_INFO_METRIC) | 2134 | if (pinfo->filled & MPATH_INFO_METRIC) |
2112 | NLA_PUT_U32(msg, NL80211_MPATH_INFO_METRIC, | 2135 | NLA_PUT_U32(msg, NL80211_MPATH_INFO_METRIC, |
2113 | pinfo->metric); | 2136 | pinfo->metric); |
@@ -2145,20 +2168,10 @@ static int nl80211_dump_mpath(struct sk_buff *skb, | |||
2145 | int path_idx = cb->args[1]; | 2168 | int path_idx = cb->args[1]; |
2146 | int err; | 2169 | int err; |
2147 | 2170 | ||
2148 | if (!ifidx) { | 2171 | if (!ifidx) |
2149 | err = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize, | 2172 | ifidx = nl80211_get_ifidx(cb); |
2150 | nl80211_fam.attrbuf, nl80211_fam.maxattr, | 2173 | if (ifidx < 0) |
2151 | nl80211_policy); | 2174 | 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 | 2175 | ||
2163 | rtnl_lock(); | 2176 | rtnl_lock(); |
2164 | 2177 | ||
@@ -2605,6 +2618,8 @@ static int nl80211_get_mesh_params(struct sk_buff *skb, | |||
2605 | cur_params.dot11MeshHWMPpreqMinInterval); | 2618 | cur_params.dot11MeshHWMPpreqMinInterval); |
2606 | NLA_PUT_U16(msg, NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME, | 2619 | NLA_PUT_U16(msg, NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME, |
2607 | cur_params.dot11MeshHWMPnetDiameterTraversalTime); | 2620 | cur_params.dot11MeshHWMPnetDiameterTraversalTime); |
2621 | NLA_PUT_U8(msg, NL80211_MESHCONF_HWMP_ROOTMODE, | ||
2622 | cur_params.dot11MeshHWMPRootMode); | ||
2608 | nla_nest_end(msg, pinfoattr); | 2623 | nla_nest_end(msg, pinfoattr); |
2609 | genlmsg_end(msg, hdr); | 2624 | genlmsg_end(msg, hdr); |
2610 | err = genlmsg_reply(msg, info); | 2625 | err = genlmsg_reply(msg, info); |
@@ -2715,6 +2730,10 @@ static int nl80211_set_mesh_params(struct sk_buff *skb, struct genl_info *info) | |||
2715 | dot11MeshHWMPnetDiameterTraversalTime, | 2730 | dot11MeshHWMPnetDiameterTraversalTime, |
2716 | mask, NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME, | 2731 | mask, NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME, |
2717 | nla_get_u16); | 2732 | nla_get_u16); |
2733 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, | ||
2734 | dot11MeshHWMPRootMode, mask, | ||
2735 | NL80211_MESHCONF_HWMP_ROOTMODE, | ||
2736 | nla_get_u8); | ||
2718 | 2737 | ||
2719 | /* Apply changes */ | 2738 | /* Apply changes */ |
2720 | err = rdev->ops->set_mesh_params(&rdev->wiphy, dev, &cfg, mask); | 2739 | err = rdev->ops->set_mesh_params(&rdev->wiphy, dev, &cfg, mask); |
@@ -3181,21 +3200,11 @@ static int nl80211_dump_scan(struct sk_buff *skb, | |||
3181 | int start = cb->args[1], idx = 0; | 3200 | int start = cb->args[1], idx = 0; |
3182 | int err; | 3201 | int err; |
3183 | 3202 | ||
3184 | if (!ifidx) { | 3203 | if (!ifidx) |
3185 | err = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize, | 3204 | ifidx = nl80211_get_ifidx(cb); |
3186 | nl80211_fam.attrbuf, nl80211_fam.maxattr, | 3205 | if (ifidx < 0) |
3187 | nl80211_policy); | 3206 | return ifidx; |
3188 | if (err) | 3207 | cb->args[0] = ifidx; |
3189 | return err; | ||
3190 | |||
3191 | if (!nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX]) | ||
3192 | return -EINVAL; | ||
3193 | |||
3194 | ifidx = nla_get_u32(nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX]); | ||
3195 | if (!ifidx) | ||
3196 | return -EINVAL; | ||
3197 | cb->args[0] = ifidx; | ||
3198 | } | ||
3199 | 3208 | ||
3200 | dev = dev_get_by_index(sock_net(skb->sk), ifidx); | 3209 | dev = dev_get_by_index(sock_net(skb->sk), ifidx); |
3201 | if (!dev) | 3210 | if (!dev) |
@@ -3238,6 +3247,106 @@ static int nl80211_dump_scan(struct sk_buff *skb, | |||
3238 | return err; | 3247 | return err; |
3239 | } | 3248 | } |
3240 | 3249 | ||
3250 | static int nl80211_send_survey(struct sk_buff *msg, u32 pid, u32 seq, | ||
3251 | int flags, struct net_device *dev, | ||
3252 | struct survey_info *survey) | ||
3253 | { | ||
3254 | void *hdr; | ||
3255 | struct nlattr *infoattr; | ||
3256 | |||
3257 | /* Survey without a channel doesn't make sense */ | ||
3258 | if (!survey->channel) | ||
3259 | return -EINVAL; | ||
3260 | |||
3261 | hdr = nl80211hdr_put(msg, pid, seq, flags, | ||
3262 | NL80211_CMD_NEW_SURVEY_RESULTS); | ||
3263 | if (!hdr) | ||
3264 | return -ENOMEM; | ||
3265 | |||
3266 | NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex); | ||
3267 | |||
3268 | infoattr = nla_nest_start(msg, NL80211_ATTR_SURVEY_INFO); | ||
3269 | if (!infoattr) | ||
3270 | goto nla_put_failure; | ||
3271 | |||
3272 | NLA_PUT_U32(msg, NL80211_SURVEY_INFO_FREQUENCY, | ||
3273 | survey->channel->center_freq); | ||
3274 | if (survey->filled & SURVEY_INFO_NOISE_DBM) | ||
3275 | NLA_PUT_U8(msg, NL80211_SURVEY_INFO_NOISE, | ||
3276 | survey->noise); | ||
3277 | |||
3278 | nla_nest_end(msg, infoattr); | ||
3279 | |||
3280 | return genlmsg_end(msg, hdr); | ||
3281 | |||
3282 | nla_put_failure: | ||
3283 | genlmsg_cancel(msg, hdr); | ||
3284 | return -EMSGSIZE; | ||
3285 | } | ||
3286 | |||
3287 | static int nl80211_dump_survey(struct sk_buff *skb, | ||
3288 | struct netlink_callback *cb) | ||
3289 | { | ||
3290 | struct survey_info survey; | ||
3291 | struct cfg80211_registered_device *dev; | ||
3292 | struct net_device *netdev; | ||
3293 | int ifidx = cb->args[0]; | ||
3294 | int survey_idx = cb->args[1]; | ||
3295 | int res; | ||
3296 | |||
3297 | if (!ifidx) | ||
3298 | ifidx = nl80211_get_ifidx(cb); | ||
3299 | if (ifidx < 0) | ||
3300 | return ifidx; | ||
3301 | cb->args[0] = ifidx; | ||
3302 | |||
3303 | rtnl_lock(); | ||
3304 | |||
3305 | netdev = __dev_get_by_index(sock_net(skb->sk), ifidx); | ||
3306 | if (!netdev) { | ||
3307 | res = -ENODEV; | ||
3308 | goto out_rtnl; | ||
3309 | } | ||
3310 | |||
3311 | dev = cfg80211_get_dev_from_ifindex(sock_net(skb->sk), ifidx); | ||
3312 | if (IS_ERR(dev)) { | ||
3313 | res = PTR_ERR(dev); | ||
3314 | goto out_rtnl; | ||
3315 | } | ||
3316 | |||
3317 | if (!dev->ops->dump_survey) { | ||
3318 | res = -EOPNOTSUPP; | ||
3319 | goto out_err; | ||
3320 | } | ||
3321 | |||
3322 | while (1) { | ||
3323 | res = dev->ops->dump_survey(&dev->wiphy, netdev, survey_idx, | ||
3324 | &survey); | ||
3325 | if (res == -ENOENT) | ||
3326 | break; | ||
3327 | if (res) | ||
3328 | goto out_err; | ||
3329 | |||
3330 | if (nl80211_send_survey(skb, | ||
3331 | NETLINK_CB(cb->skb).pid, | ||
3332 | cb->nlh->nlmsg_seq, NLM_F_MULTI, | ||
3333 | netdev, | ||
3334 | &survey) < 0) | ||
3335 | goto out; | ||
3336 | survey_idx++; | ||
3337 | } | ||
3338 | |||
3339 | out: | ||
3340 | cb->args[1] = survey_idx; | ||
3341 | res = skb->len; | ||
3342 | out_err: | ||
3343 | cfg80211_unlock_rdev(dev); | ||
3344 | out_rtnl: | ||
3345 | rtnl_unlock(); | ||
3346 | |||
3347 | return res; | ||
3348 | } | ||
3349 | |||
3241 | static bool nl80211_valid_auth_type(enum nl80211_auth_type auth_type) | 3350 | static bool nl80211_valid_auth_type(enum nl80211_auth_type auth_type) |
3242 | { | 3351 | { |
3243 | return auth_type <= NL80211_AUTHTYPE_MAX; | 3352 | return auth_type <= NL80211_AUTHTYPE_MAX; |
@@ -4315,6 +4424,11 @@ static struct genl_ops nl80211_ops[] = { | |||
4315 | .policy = nl80211_policy, | 4424 | .policy = nl80211_policy, |
4316 | .flags = GENL_ADMIN_PERM, | 4425 | .flags = GENL_ADMIN_PERM, |
4317 | }, | 4426 | }, |
4427 | { | ||
4428 | .cmd = NL80211_CMD_GET_SURVEY, | ||
4429 | .policy = nl80211_policy, | ||
4430 | .dumpit = nl80211_dump_survey, | ||
4431 | }, | ||
4318 | }; | 4432 | }; |
4319 | static struct genl_multicast_group nl80211_mlme_mcgrp = { | 4433 | static struct genl_multicast_group nl80211_mlme_mcgrp = { |
4320 | .name = "mlme", | 4434 | .name = "mlme", |