diff options
Diffstat (limited to 'net/wireless/nl80211.c')
-rw-r--r-- | net/wireless/nl80211.c | 406 |
1 files changed, 349 insertions, 57 deletions
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index ca3c92a0a14f..a6028433e3a0 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c | |||
@@ -138,6 +138,9 @@ 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 }, | ||
142 | [NL80211_ATTR_PMKID] = { .type = NLA_BINARY, | ||
143 | .len = WLAN_PMKID_LEN }, | ||
141 | }; | 144 | }; |
142 | 145 | ||
143 | /* policy for the attributes */ | 146 | /* policy for the attributes */ |
@@ -151,6 +154,26 @@ nl80211_key_policy[NL80211_KEY_MAX + 1] __read_mostly = { | |||
151 | [NL80211_KEY_DEFAULT_MGMT] = { .type = NLA_FLAG }, | 154 | [NL80211_KEY_DEFAULT_MGMT] = { .type = NLA_FLAG }, |
152 | }; | 155 | }; |
153 | 156 | ||
157 | /* ifidx get helper */ | ||
158 | static int nl80211_get_ifidx(struct netlink_callback *cb) | ||
159 | { | ||
160 | int res; | ||
161 | |||
162 | res = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize, | ||
163 | nl80211_fam.attrbuf, nl80211_fam.maxattr, | ||
164 | nl80211_policy); | ||
165 | if (res) | ||
166 | return res; | ||
167 | |||
168 | if (!nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX]) | ||
169 | return -EINVAL; | ||
170 | |||
171 | res = nla_get_u32(nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX]); | ||
172 | if (!res) | ||
173 | return -EINVAL; | ||
174 | return res; | ||
175 | } | ||
176 | |||
154 | /* IE validation */ | 177 | /* IE validation */ |
155 | static bool is_valid_ie_attr(const struct nlattr *attr) | 178 | static bool is_valid_ie_attr(const struct nlattr *attr) |
156 | { | 179 | { |
@@ -429,6 +452,9 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, | |||
429 | sizeof(u32) * dev->wiphy.n_cipher_suites, | 452 | sizeof(u32) * dev->wiphy.n_cipher_suites, |
430 | dev->wiphy.cipher_suites); | 453 | dev->wiphy.cipher_suites); |
431 | 454 | ||
455 | NLA_PUT_U8(msg, NL80211_ATTR_MAX_NUM_PMKIDS, | ||
456 | dev->wiphy.max_num_pmkids); | ||
457 | |||
432 | nl_modes = nla_nest_start(msg, NL80211_ATTR_SUPPORTED_IFTYPES); | 458 | nl_modes = nla_nest_start(msg, NL80211_ATTR_SUPPORTED_IFTYPES); |
433 | if (!nl_modes) | 459 | if (!nl_modes) |
434 | goto nla_put_failure; | 460 | goto nla_put_failure; |
@@ -540,7 +566,10 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, | |||
540 | CMD(deauth, DEAUTHENTICATE); | 566 | CMD(deauth, DEAUTHENTICATE); |
541 | CMD(disassoc, DISASSOCIATE); | 567 | CMD(disassoc, DISASSOCIATE); |
542 | CMD(join_ibss, JOIN_IBSS); | 568 | CMD(join_ibss, JOIN_IBSS); |
543 | if (dev->wiphy.netnsok) { | 569 | CMD(set_pmksa, SET_PMKSA); |
570 | CMD(del_pmksa, DEL_PMKSA); | ||
571 | CMD(flush_pmksa, FLUSH_PMKSA); | ||
572 | if (dev->wiphy.flags & WIPHY_FLAG_NETNS_OK) { | ||
544 | i++; | 573 | i++; |
545 | NLA_PUT_U32(msg, i, NL80211_CMD_SET_WIPHY_NETNS); | 574 | NLA_PUT_U32(msg, i, NL80211_CMD_SET_WIPHY_NETNS); |
546 | } | 575 | } |
@@ -947,6 +976,32 @@ static int parse_monitor_flags(struct nlattr *nla, u32 *mntrflags) | |||
947 | return 0; | 976 | return 0; |
948 | } | 977 | } |
949 | 978 | ||
979 | static int nl80211_valid_4addr(struct cfg80211_registered_device *rdev, | ||
980 | struct net_device *netdev, u8 use_4addr, | ||
981 | enum nl80211_iftype iftype) | ||
982 | { | ||
983 | if (!use_4addr) { | ||
984 | if (netdev && netdev->br_port) | ||
985 | return -EBUSY; | ||
986 | return 0; | ||
987 | } | ||
988 | |||
989 | switch (iftype) { | ||
990 | case NL80211_IFTYPE_AP_VLAN: | ||
991 | if (rdev->wiphy.flags & WIPHY_FLAG_4ADDR_AP) | ||
992 | return 0; | ||
993 | break; | ||
994 | case NL80211_IFTYPE_STATION: | ||
995 | if (rdev->wiphy.flags & WIPHY_FLAG_4ADDR_STATION) | ||
996 | return 0; | ||
997 | break; | ||
998 | default: | ||
999 | break; | ||
1000 | } | ||
1001 | |||
1002 | return -EOPNOTSUPP; | ||
1003 | } | ||
1004 | |||
950 | static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info) | 1005 | static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info) |
951 | { | 1006 | { |
952 | struct cfg80211_registered_device *rdev; | 1007 | struct cfg80211_registered_device *rdev; |
@@ -987,6 +1042,16 @@ static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info) | |||
987 | change = true; | 1042 | change = true; |
988 | } | 1043 | } |
989 | 1044 | ||
1045 | if (info->attrs[NL80211_ATTR_4ADDR]) { | ||
1046 | params.use_4addr = !!nla_get_u8(info->attrs[NL80211_ATTR_4ADDR]); | ||
1047 | change = true; | ||
1048 | err = nl80211_valid_4addr(rdev, dev, params.use_4addr, ntype); | ||
1049 | if (err) | ||
1050 | goto unlock; | ||
1051 | } else { | ||
1052 | params.use_4addr = -1; | ||
1053 | } | ||
1054 | |||
990 | if (info->attrs[NL80211_ATTR_MNTR_FLAGS]) { | 1055 | if (info->attrs[NL80211_ATTR_MNTR_FLAGS]) { |
991 | if (ntype != NL80211_IFTYPE_MONITOR) { | 1056 | if (ntype != NL80211_IFTYPE_MONITOR) { |
992 | err = -EINVAL; | 1057 | err = -EINVAL; |
@@ -1006,6 +1071,9 @@ static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info) | |||
1006 | else | 1071 | else |
1007 | err = 0; | 1072 | err = 0; |
1008 | 1073 | ||
1074 | if (!err && params.use_4addr != -1) | ||
1075 | dev->ieee80211_ptr->use_4addr = params.use_4addr; | ||
1076 | |||
1009 | unlock: | 1077 | unlock: |
1010 | dev_put(dev); | 1078 | dev_put(dev); |
1011 | cfg80211_unlock_rdev(rdev); | 1079 | cfg80211_unlock_rdev(rdev); |
@@ -1053,6 +1121,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]); | 1121 | params.mesh_id_len = nla_len(info->attrs[NL80211_ATTR_MESH_ID]); |
1054 | } | 1122 | } |
1055 | 1123 | ||
1124 | if (info->attrs[NL80211_ATTR_4ADDR]) { | ||
1125 | params.use_4addr = !!nla_get_u8(info->attrs[NL80211_ATTR_4ADDR]); | ||
1126 | err = nl80211_valid_4addr(rdev, NULL, params.use_4addr, type); | ||
1127 | if (err) | ||
1128 | goto unlock; | ||
1129 | } | ||
1130 | |||
1056 | err = parse_monitor_flags(type == NL80211_IFTYPE_MONITOR ? | 1131 | err = parse_monitor_flags(type == NL80211_IFTYPE_MONITOR ? |
1057 | info->attrs[NL80211_ATTR_MNTR_FLAGS] : NULL, | 1132 | info->attrs[NL80211_ATTR_MNTR_FLAGS] : NULL, |
1058 | &flags); | 1133 | &flags); |
@@ -1264,7 +1339,7 @@ static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info) | |||
1264 | if (!err) | 1339 | if (!err) |
1265 | err = func(&rdev->wiphy, dev, key.idx); | 1340 | err = func(&rdev->wiphy, dev, key.idx); |
1266 | 1341 | ||
1267 | #ifdef CONFIG_WIRELESS_EXT | 1342 | #ifdef CONFIG_CFG80211_WEXT |
1268 | if (!err) { | 1343 | if (!err) { |
1269 | if (func == rdev->ops->set_default_key) | 1344 | if (func == rdev->ops->set_default_key) |
1270 | dev->ieee80211_ptr->wext.default_key = key.idx; | 1345 | dev->ieee80211_ptr->wext.default_key = key.idx; |
@@ -1365,7 +1440,7 @@ static int nl80211_del_key(struct sk_buff *skb, struct genl_info *info) | |||
1365 | if (!err) | 1440 | if (!err) |
1366 | err = rdev->ops->del_key(&rdev->wiphy, dev, key.idx, mac_addr); | 1441 | err = rdev->ops->del_key(&rdev->wiphy, dev, key.idx, mac_addr); |
1367 | 1442 | ||
1368 | #ifdef CONFIG_WIRELESS_EXT | 1443 | #ifdef CONFIG_CFG80211_WEXT |
1369 | if (!err) { | 1444 | if (!err) { |
1370 | if (key.idx == dev->ieee80211_ptr->wext.default_key) | 1445 | if (key.idx == dev->ieee80211_ptr->wext.default_key) |
1371 | dev->ieee80211_ptr->wext.default_key = -1; | 1446 | dev->ieee80211_ptr->wext.default_key = -1; |
@@ -1682,20 +1757,10 @@ static int nl80211_dump_station(struct sk_buff *skb, | |||
1682 | int sta_idx = cb->args[1]; | 1757 | int sta_idx = cb->args[1]; |
1683 | int err; | 1758 | int err; |
1684 | 1759 | ||
1685 | if (!ifidx) { | 1760 | if (!ifidx) |
1686 | err = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize, | 1761 | ifidx = nl80211_get_ifidx(cb); |
1687 | nl80211_fam.attrbuf, nl80211_fam.maxattr, | 1762 | if (ifidx < 0) |
1688 | nl80211_policy); | 1763 | 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 | 1764 | ||
1700 | rtnl_lock(); | 1765 | rtnl_lock(); |
1701 | 1766 | ||
@@ -1800,7 +1865,7 @@ static int nl80211_get_station(struct sk_buff *skb, struct genl_info *info) | |||
1800 | } | 1865 | } |
1801 | 1866 | ||
1802 | /* | 1867 | /* |
1803 | * Get vlan interface making sure it is on the right wiphy. | 1868 | * Get vlan interface making sure it is running and on the right wiphy. |
1804 | */ | 1869 | */ |
1805 | static int get_vlan(struct genl_info *info, | 1870 | static int get_vlan(struct genl_info *info, |
1806 | struct cfg80211_registered_device *rdev, | 1871 | struct cfg80211_registered_device *rdev, |
@@ -1818,6 +1883,8 @@ static int get_vlan(struct genl_info *info, | |||
1818 | return -EINVAL; | 1883 | return -EINVAL; |
1819 | if ((*vlan)->ieee80211_ptr->wiphy != &rdev->wiphy) | 1884 | if ((*vlan)->ieee80211_ptr->wiphy != &rdev->wiphy) |
1820 | return -EINVAL; | 1885 | return -EINVAL; |
1886 | if (!netif_running(*vlan)) | ||
1887 | return -ENETDOWN; | ||
1821 | } | 1888 | } |
1822 | return 0; | 1889 | return 0; |
1823 | } | 1890 | } |
@@ -2105,9 +2172,9 @@ static int nl80211_send_mpath(struct sk_buff *msg, u32 pid, u32 seq, | |||
2105 | if (pinfo->filled & MPATH_INFO_FRAME_QLEN) | 2172 | if (pinfo->filled & MPATH_INFO_FRAME_QLEN) |
2106 | NLA_PUT_U32(msg, NL80211_MPATH_INFO_FRAME_QLEN, | 2173 | NLA_PUT_U32(msg, NL80211_MPATH_INFO_FRAME_QLEN, |
2107 | pinfo->frame_qlen); | 2174 | pinfo->frame_qlen); |
2108 | if (pinfo->filled & MPATH_INFO_DSN) | 2175 | if (pinfo->filled & MPATH_INFO_SN) |
2109 | NLA_PUT_U32(msg, NL80211_MPATH_INFO_DSN, | 2176 | NLA_PUT_U32(msg, NL80211_MPATH_INFO_SN, |
2110 | pinfo->dsn); | 2177 | pinfo->sn); |
2111 | if (pinfo->filled & MPATH_INFO_METRIC) | 2178 | if (pinfo->filled & MPATH_INFO_METRIC) |
2112 | NLA_PUT_U32(msg, NL80211_MPATH_INFO_METRIC, | 2179 | NLA_PUT_U32(msg, NL80211_MPATH_INFO_METRIC, |
2113 | pinfo->metric); | 2180 | pinfo->metric); |
@@ -2145,20 +2212,10 @@ static int nl80211_dump_mpath(struct sk_buff *skb, | |||
2145 | int path_idx = cb->args[1]; | 2212 | int path_idx = cb->args[1]; |
2146 | int err; | 2213 | int err; |
2147 | 2214 | ||
2148 | if (!ifidx) { | 2215 | if (!ifidx) |
2149 | err = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize, | 2216 | ifidx = nl80211_get_ifidx(cb); |
2150 | nl80211_fam.attrbuf, nl80211_fam.maxattr, | 2217 | if (ifidx < 0) |
2151 | nl80211_policy); | 2218 | 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 | 2219 | ||
2163 | rtnl_lock(); | 2220 | rtnl_lock(); |
2164 | 2221 | ||
@@ -2605,6 +2662,8 @@ static int nl80211_get_mesh_params(struct sk_buff *skb, | |||
2605 | cur_params.dot11MeshHWMPpreqMinInterval); | 2662 | cur_params.dot11MeshHWMPpreqMinInterval); |
2606 | NLA_PUT_U16(msg, NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME, | 2663 | NLA_PUT_U16(msg, NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME, |
2607 | cur_params.dot11MeshHWMPnetDiameterTraversalTime); | 2664 | cur_params.dot11MeshHWMPnetDiameterTraversalTime); |
2665 | NLA_PUT_U8(msg, NL80211_MESHCONF_HWMP_ROOTMODE, | ||
2666 | cur_params.dot11MeshHWMPRootMode); | ||
2608 | nla_nest_end(msg, pinfoattr); | 2667 | nla_nest_end(msg, pinfoattr); |
2609 | genlmsg_end(msg, hdr); | 2668 | genlmsg_end(msg, hdr); |
2610 | err = genlmsg_reply(msg, info); | 2669 | err = genlmsg_reply(msg, info); |
@@ -2715,6 +2774,10 @@ static int nl80211_set_mesh_params(struct sk_buff *skb, struct genl_info *info) | |||
2715 | dot11MeshHWMPnetDiameterTraversalTime, | 2774 | dot11MeshHWMPnetDiameterTraversalTime, |
2716 | mask, NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME, | 2775 | mask, NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME, |
2717 | nla_get_u16); | 2776 | nla_get_u16); |
2777 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, | ||
2778 | dot11MeshHWMPRootMode, mask, | ||
2779 | NL80211_MESHCONF_HWMP_ROOTMODE, | ||
2780 | nla_get_u8); | ||
2718 | 2781 | ||
2719 | /* Apply changes */ | 2782 | /* Apply changes */ |
2720 | err = rdev->ops->set_mesh_params(&rdev->wiphy, dev, &cfg, mask); | 2783 | err = rdev->ops->set_mesh_params(&rdev->wiphy, dev, &cfg, mask); |
@@ -2988,7 +3051,6 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info) | |||
2988 | goto out; | 3051 | goto out; |
2989 | } | 3052 | } |
2990 | 3053 | ||
2991 | request->n_channels = n_channels; | ||
2992 | if (n_ssids) | 3054 | if (n_ssids) |
2993 | request->ssids = (void *)&request->channels[n_channels]; | 3055 | request->ssids = (void *)&request->channels[n_channels]; |
2994 | request->n_ssids = n_ssids; | 3056 | request->n_ssids = n_ssids; |
@@ -2999,32 +3061,53 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info) | |||
2999 | request->ie = (void *)(request->channels + n_channels); | 3061 | request->ie = (void *)(request->channels + n_channels); |
3000 | } | 3062 | } |
3001 | 3063 | ||
3064 | i = 0; | ||
3002 | if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) { | 3065 | if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) { |
3003 | /* user specified, bail out if channel not found */ | 3066 | /* 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) { | 3067 | nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_FREQUENCIES], tmp) { |
3007 | request->channels[i] = ieee80211_get_channel(wiphy, nla_get_u32(attr)); | 3068 | struct ieee80211_channel *chan; |
3008 | if (!request->channels[i]) { | 3069 | |
3070 | chan = ieee80211_get_channel(wiphy, nla_get_u32(attr)); | ||
3071 | |||
3072 | if (!chan) { | ||
3009 | err = -EINVAL; | 3073 | err = -EINVAL; |
3010 | goto out_free; | 3074 | goto out_free; |
3011 | } | 3075 | } |
3076 | |||
3077 | /* ignore disabled channels */ | ||
3078 | if (chan->flags & IEEE80211_CHAN_DISABLED) | ||
3079 | continue; | ||
3080 | |||
3081 | request->channels[i] = chan; | ||
3012 | i++; | 3082 | i++; |
3013 | } | 3083 | } |
3014 | } else { | 3084 | } else { |
3015 | /* all channels */ | 3085 | /* all channels */ |
3016 | i = 0; | ||
3017 | for (band = 0; band < IEEE80211_NUM_BANDS; band++) { | 3086 | for (band = 0; band < IEEE80211_NUM_BANDS; band++) { |
3018 | int j; | 3087 | int j; |
3019 | if (!wiphy->bands[band]) | 3088 | if (!wiphy->bands[band]) |
3020 | continue; | 3089 | continue; |
3021 | for (j = 0; j < wiphy->bands[band]->n_channels; j++) { | 3090 | for (j = 0; j < wiphy->bands[band]->n_channels; j++) { |
3022 | request->channels[i] = &wiphy->bands[band]->channels[j]; | 3091 | struct ieee80211_channel *chan; |
3092 | |||
3093 | chan = &wiphy->bands[band]->channels[j]; | ||
3094 | |||
3095 | if (chan->flags & IEEE80211_CHAN_DISABLED) | ||
3096 | continue; | ||
3097 | |||
3098 | request->channels[i] = chan; | ||
3023 | i++; | 3099 | i++; |
3024 | } | 3100 | } |
3025 | } | 3101 | } |
3026 | } | 3102 | } |
3027 | 3103 | ||
3104 | if (!i) { | ||
3105 | err = -EINVAL; | ||
3106 | goto out_free; | ||
3107 | } | ||
3108 | |||
3109 | request->n_channels = i; | ||
3110 | |||
3028 | i = 0; | 3111 | i = 0; |
3029 | if (info->attrs[NL80211_ATTR_SCAN_SSIDS]) { | 3112 | if (info->attrs[NL80211_ATTR_SCAN_SSIDS]) { |
3030 | nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS], tmp) { | 3113 | nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS], tmp) { |
@@ -3105,6 +3188,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); | 3188 | NLA_PUT_U16(msg, NL80211_BSS_BEACON_INTERVAL, res->beacon_interval); |
3106 | NLA_PUT_U16(msg, NL80211_BSS_CAPABILITY, res->capability); | 3189 | NLA_PUT_U16(msg, NL80211_BSS_CAPABILITY, res->capability); |
3107 | NLA_PUT_U32(msg, NL80211_BSS_FREQUENCY, res->channel->center_freq); | 3190 | NLA_PUT_U32(msg, NL80211_BSS_FREQUENCY, res->channel->center_freq); |
3191 | NLA_PUT_U32(msg, NL80211_BSS_SEEN_MS_AGO, | ||
3192 | jiffies_to_msecs(jiffies - intbss->ts)); | ||
3108 | 3193 | ||
3109 | switch (rdev->wiphy.signal_type) { | 3194 | switch (rdev->wiphy.signal_type) { |
3110 | case CFG80211_SIGNAL_TYPE_MBM: | 3195 | case CFG80211_SIGNAL_TYPE_MBM: |
@@ -3159,21 +3244,11 @@ static int nl80211_dump_scan(struct sk_buff *skb, | |||
3159 | int start = cb->args[1], idx = 0; | 3244 | int start = cb->args[1], idx = 0; |
3160 | int err; | 3245 | int err; |
3161 | 3246 | ||
3162 | if (!ifidx) { | 3247 | if (!ifidx) |
3163 | err = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize, | 3248 | ifidx = nl80211_get_ifidx(cb); |
3164 | nl80211_fam.attrbuf, nl80211_fam.maxattr, | 3249 | if (ifidx < 0) |
3165 | nl80211_policy); | 3250 | return ifidx; |
3166 | if (err) | 3251 | 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 | 3252 | ||
3178 | dev = dev_get_by_index(sock_net(skb->sk), ifidx); | 3253 | dev = dev_get_by_index(sock_net(skb->sk), ifidx); |
3179 | if (!dev) | 3254 | if (!dev) |
@@ -3216,6 +3291,106 @@ static int nl80211_dump_scan(struct sk_buff *skb, | |||
3216 | return err; | 3291 | return err; |
3217 | } | 3292 | } |
3218 | 3293 | ||
3294 | static int nl80211_send_survey(struct sk_buff *msg, u32 pid, u32 seq, | ||
3295 | int flags, struct net_device *dev, | ||
3296 | struct survey_info *survey) | ||
3297 | { | ||
3298 | void *hdr; | ||
3299 | struct nlattr *infoattr; | ||
3300 | |||
3301 | /* Survey without a channel doesn't make sense */ | ||
3302 | if (!survey->channel) | ||
3303 | return -EINVAL; | ||
3304 | |||
3305 | hdr = nl80211hdr_put(msg, pid, seq, flags, | ||
3306 | NL80211_CMD_NEW_SURVEY_RESULTS); | ||
3307 | if (!hdr) | ||
3308 | return -ENOMEM; | ||
3309 | |||
3310 | NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex); | ||
3311 | |||
3312 | infoattr = nla_nest_start(msg, NL80211_ATTR_SURVEY_INFO); | ||
3313 | if (!infoattr) | ||
3314 | goto nla_put_failure; | ||
3315 | |||
3316 | NLA_PUT_U32(msg, NL80211_SURVEY_INFO_FREQUENCY, | ||
3317 | survey->channel->center_freq); | ||
3318 | if (survey->filled & SURVEY_INFO_NOISE_DBM) | ||
3319 | NLA_PUT_U8(msg, NL80211_SURVEY_INFO_NOISE, | ||
3320 | survey->noise); | ||
3321 | |||
3322 | nla_nest_end(msg, infoattr); | ||
3323 | |||
3324 | return genlmsg_end(msg, hdr); | ||
3325 | |||
3326 | nla_put_failure: | ||
3327 | genlmsg_cancel(msg, hdr); | ||
3328 | return -EMSGSIZE; | ||
3329 | } | ||
3330 | |||
3331 | static int nl80211_dump_survey(struct sk_buff *skb, | ||
3332 | struct netlink_callback *cb) | ||
3333 | { | ||
3334 | struct survey_info survey; | ||
3335 | struct cfg80211_registered_device *dev; | ||
3336 | struct net_device *netdev; | ||
3337 | int ifidx = cb->args[0]; | ||
3338 | int survey_idx = cb->args[1]; | ||
3339 | int res; | ||
3340 | |||
3341 | if (!ifidx) | ||
3342 | ifidx = nl80211_get_ifidx(cb); | ||
3343 | if (ifidx < 0) | ||
3344 | return ifidx; | ||
3345 | cb->args[0] = ifidx; | ||
3346 | |||
3347 | rtnl_lock(); | ||
3348 | |||
3349 | netdev = __dev_get_by_index(sock_net(skb->sk), ifidx); | ||
3350 | if (!netdev) { | ||
3351 | res = -ENODEV; | ||
3352 | goto out_rtnl; | ||
3353 | } | ||
3354 | |||
3355 | dev = cfg80211_get_dev_from_ifindex(sock_net(skb->sk), ifidx); | ||
3356 | if (IS_ERR(dev)) { | ||
3357 | res = PTR_ERR(dev); | ||
3358 | goto out_rtnl; | ||
3359 | } | ||
3360 | |||
3361 | if (!dev->ops->dump_survey) { | ||
3362 | res = -EOPNOTSUPP; | ||
3363 | goto out_err; | ||
3364 | } | ||
3365 | |||
3366 | while (1) { | ||
3367 | res = dev->ops->dump_survey(&dev->wiphy, netdev, survey_idx, | ||
3368 | &survey); | ||
3369 | if (res == -ENOENT) | ||
3370 | break; | ||
3371 | if (res) | ||
3372 | goto out_err; | ||
3373 | |||
3374 | if (nl80211_send_survey(skb, | ||
3375 | NETLINK_CB(cb->skb).pid, | ||
3376 | cb->nlh->nlmsg_seq, NLM_F_MULTI, | ||
3377 | netdev, | ||
3378 | &survey) < 0) | ||
3379 | goto out; | ||
3380 | survey_idx++; | ||
3381 | } | ||
3382 | |||
3383 | out: | ||
3384 | cb->args[1] = survey_idx; | ||
3385 | res = skb->len; | ||
3386 | out_err: | ||
3387 | cfg80211_unlock_rdev(dev); | ||
3388 | out_rtnl: | ||
3389 | rtnl_unlock(); | ||
3390 | |||
3391 | return res; | ||
3392 | } | ||
3393 | |||
3219 | static bool nl80211_valid_auth_type(enum nl80211_auth_type auth_type) | 3394 | static bool nl80211_valid_auth_type(enum nl80211_auth_type auth_type) |
3220 | { | 3395 | { |
3221 | return auth_type <= NL80211_AUTHTYPE_MAX; | 3396 | return auth_type <= NL80211_AUTHTYPE_MAX; |
@@ -4054,6 +4229,99 @@ static int nl80211_wiphy_netns(struct sk_buff *skb, struct genl_info *info) | |||
4054 | return err; | 4229 | return err; |
4055 | } | 4230 | } |
4056 | 4231 | ||
4232 | static int nl80211_setdel_pmksa(struct sk_buff *skb, struct genl_info *info) | ||
4233 | { | ||
4234 | struct cfg80211_registered_device *rdev; | ||
4235 | int (*rdev_ops)(struct wiphy *wiphy, struct net_device *dev, | ||
4236 | struct cfg80211_pmksa *pmksa) = NULL; | ||
4237 | int err; | ||
4238 | struct net_device *dev; | ||
4239 | struct cfg80211_pmksa pmksa; | ||
4240 | |||
4241 | memset(&pmksa, 0, sizeof(struct cfg80211_pmksa)); | ||
4242 | |||
4243 | if (!info->attrs[NL80211_ATTR_MAC]) | ||
4244 | return -EINVAL; | ||
4245 | |||
4246 | if (!info->attrs[NL80211_ATTR_PMKID]) | ||
4247 | return -EINVAL; | ||
4248 | |||
4249 | rtnl_lock(); | ||
4250 | |||
4251 | err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); | ||
4252 | if (err) | ||
4253 | goto out_rtnl; | ||
4254 | |||
4255 | pmksa.pmkid = nla_data(info->attrs[NL80211_ATTR_PMKID]); | ||
4256 | pmksa.bssid = nla_data(info->attrs[NL80211_ATTR_MAC]); | ||
4257 | |||
4258 | if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) { | ||
4259 | err = -EOPNOTSUPP; | ||
4260 | goto out; | ||
4261 | } | ||
4262 | |||
4263 | switch (info->genlhdr->cmd) { | ||
4264 | case NL80211_CMD_SET_PMKSA: | ||
4265 | rdev_ops = rdev->ops->set_pmksa; | ||
4266 | break; | ||
4267 | case NL80211_CMD_DEL_PMKSA: | ||
4268 | rdev_ops = rdev->ops->del_pmksa; | ||
4269 | break; | ||
4270 | default: | ||
4271 | WARN_ON(1); | ||
4272 | break; | ||
4273 | } | ||
4274 | |||
4275 | if (!rdev_ops) { | ||
4276 | err = -EOPNOTSUPP; | ||
4277 | goto out; | ||
4278 | } | ||
4279 | |||
4280 | err = rdev_ops(&rdev->wiphy, dev, &pmksa); | ||
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 | static int nl80211_flush_pmksa(struct sk_buff *skb, struct genl_info *info) | ||
4292 | { | ||
4293 | struct cfg80211_registered_device *rdev; | ||
4294 | int err; | ||
4295 | struct net_device *dev; | ||
4296 | |||
4297 | rtnl_lock(); | ||
4298 | |||
4299 | err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); | ||
4300 | if (err) | ||
4301 | goto out_rtnl; | ||
4302 | |||
4303 | if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) { | ||
4304 | err = -EOPNOTSUPP; | ||
4305 | goto out; | ||
4306 | } | ||
4307 | |||
4308 | if (!rdev->ops->flush_pmksa) { | ||
4309 | err = -EOPNOTSUPP; | ||
4310 | goto out; | ||
4311 | } | ||
4312 | |||
4313 | err = rdev->ops->flush_pmksa(&rdev->wiphy, dev); | ||
4314 | |||
4315 | out: | ||
4316 | cfg80211_unlock_rdev(rdev); | ||
4317 | dev_put(dev); | ||
4318 | out_rtnl: | ||
4319 | rtnl_unlock(); | ||
4320 | |||
4321 | return err; | ||
4322 | |||
4323 | } | ||
4324 | |||
4057 | static struct genl_ops nl80211_ops[] = { | 4325 | static struct genl_ops nl80211_ops[] = { |
4058 | { | 4326 | { |
4059 | .cmd = NL80211_CMD_GET_WIPHY, | 4327 | .cmd = NL80211_CMD_GET_WIPHY, |
@@ -4293,6 +4561,30 @@ static struct genl_ops nl80211_ops[] = { | |||
4293 | .policy = nl80211_policy, | 4561 | .policy = nl80211_policy, |
4294 | .flags = GENL_ADMIN_PERM, | 4562 | .flags = GENL_ADMIN_PERM, |
4295 | }, | 4563 | }, |
4564 | { | ||
4565 | .cmd = NL80211_CMD_GET_SURVEY, | ||
4566 | .policy = nl80211_policy, | ||
4567 | .dumpit = nl80211_dump_survey, | ||
4568 | }, | ||
4569 | { | ||
4570 | .cmd = NL80211_CMD_SET_PMKSA, | ||
4571 | .doit = nl80211_setdel_pmksa, | ||
4572 | .policy = nl80211_policy, | ||
4573 | .flags = GENL_ADMIN_PERM, | ||
4574 | }, | ||
4575 | { | ||
4576 | .cmd = NL80211_CMD_DEL_PMKSA, | ||
4577 | .doit = nl80211_setdel_pmksa, | ||
4578 | .policy = nl80211_policy, | ||
4579 | .flags = GENL_ADMIN_PERM, | ||
4580 | }, | ||
4581 | { | ||
4582 | .cmd = NL80211_CMD_FLUSH_PMKSA, | ||
4583 | .doit = nl80211_flush_pmksa, | ||
4584 | .policy = nl80211_policy, | ||
4585 | .flags = GENL_ADMIN_PERM, | ||
4586 | }, | ||
4587 | |||
4296 | }; | 4588 | }; |
4297 | static struct genl_multicast_group nl80211_mlme_mcgrp = { | 4589 | static struct genl_multicast_group nl80211_mlme_mcgrp = { |
4298 | .name = "mlme", | 4590 | .name = "mlme", |