blob: 90a0e552cb32a56566361b9073b449e884e26a57 [file] [log] [blame]
Johannes Berg55682962007-09-20 13:09:35 -04001/*
2 * This is the new netlink-based wireless configuration interface.
3 *
Jouni Malinen026331c2010-02-15 12:53:10 +02004 * Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net>
Johannes Berg55682962007-09-20 13:09:35 -04005 */
6
7#include <linux/if.h>
8#include <linux/module.h>
9#include <linux/err.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090010#include <linux/slab.h>
Johannes Berg55682962007-09-20 13:09:35 -040011#include <linux/list.h>
12#include <linux/if_ether.h>
13#include <linux/ieee80211.h>
14#include <linux/nl80211.h>
15#include <linux/rtnetlink.h>
16#include <linux/netlink.h>
Johannes Berg2a519312009-02-10 21:25:55 +010017#include <linux/etherdevice.h>
Johannes Berg463d0182009-07-14 00:33:35 +020018#include <net/net_namespace.h>
Johannes Berg55682962007-09-20 13:09:35 -040019#include <net/genetlink.h>
20#include <net/cfg80211.h>
Johannes Berg463d0182009-07-14 00:33:35 +020021#include <net/sock.h>
Johannes Berg2a0e0472013-01-23 22:57:40 +010022#include <net/inet_connection_sock.h>
Johannes Berg55682962007-09-20 13:09:35 -040023#include "core.h"
24#include "nl80211.h"
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -070025#include "reg.h"
Hila Gonene35e4d22012-06-27 17:19:42 +030026#include "rdev-ops.h"
Johannes Berg55682962007-09-20 13:09:35 -040027
Jouni Malinen5fb628e2011-08-10 23:54:35 +030028static int nl80211_crypto_settings(struct cfg80211_registered_device *rdev,
29 struct genl_info *info,
30 struct cfg80211_crypto_settings *settings,
31 int cipher_limit);
32
Johannes Berg4c476992010-10-04 21:36:35 +020033static int nl80211_pre_doit(struct genl_ops *ops, struct sk_buff *skb,
34 struct genl_info *info);
35static void nl80211_post_doit(struct genl_ops *ops, struct sk_buff *skb,
36 struct genl_info *info);
37
Johannes Berg55682962007-09-20 13:09:35 -040038/* the netlink family */
39static struct genl_family nl80211_fam = {
40 .id = GENL_ID_GENERATE, /* don't bother with a hardcoded ID */
41 .name = "nl80211", /* have users key off the name instead */
42 .hdrsize = 0, /* no private header */
43 .version = 1, /* no particular meaning now */
44 .maxattr = NL80211_ATTR_MAX,
Johannes Berg463d0182009-07-14 00:33:35 +020045 .netnsok = true,
Johannes Berg4c476992010-10-04 21:36:35 +020046 .pre_doit = nl80211_pre_doit,
47 .post_doit = nl80211_post_doit,
Johannes Berg55682962007-09-20 13:09:35 -040048};
49
Johannes Berg89a54e42012-06-15 14:33:17 +020050/* returns ERR_PTR values */
51static struct wireless_dev *
52__cfg80211_wdev_from_attrs(struct net *netns, struct nlattr **attrs)
Johannes Berg55682962007-09-20 13:09:35 -040053{
Johannes Berg89a54e42012-06-15 14:33:17 +020054 struct cfg80211_registered_device *rdev;
55 struct wireless_dev *result = NULL;
56 bool have_ifidx = attrs[NL80211_ATTR_IFINDEX];
57 bool have_wdev_id = attrs[NL80211_ATTR_WDEV];
58 u64 wdev_id;
59 int wiphy_idx = -1;
60 int ifidx = -1;
Johannes Berg55682962007-09-20 13:09:35 -040061
Johannes Berg89a54e42012-06-15 14:33:17 +020062 assert_cfg80211_lock();
Johannes Berg55682962007-09-20 13:09:35 -040063
Johannes Berg89a54e42012-06-15 14:33:17 +020064 if (!have_ifidx && !have_wdev_id)
65 return ERR_PTR(-EINVAL);
Johannes Berg55682962007-09-20 13:09:35 -040066
Johannes Berg89a54e42012-06-15 14:33:17 +020067 if (have_ifidx)
68 ifidx = nla_get_u32(attrs[NL80211_ATTR_IFINDEX]);
69 if (have_wdev_id) {
70 wdev_id = nla_get_u64(attrs[NL80211_ATTR_WDEV]);
71 wiphy_idx = wdev_id >> 32;
Johannes Berg55682962007-09-20 13:09:35 -040072 }
73
Johannes Berg89a54e42012-06-15 14:33:17 +020074 list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
75 struct wireless_dev *wdev;
76
77 if (wiphy_net(&rdev->wiphy) != netns)
78 continue;
79
80 if (have_wdev_id && rdev->wiphy_idx != wiphy_idx)
81 continue;
82
83 mutex_lock(&rdev->devlist_mtx);
84 list_for_each_entry(wdev, &rdev->wdev_list, list) {
85 if (have_ifidx && wdev->netdev &&
86 wdev->netdev->ifindex == ifidx) {
87 result = wdev;
88 break;
89 }
90 if (have_wdev_id && wdev->identifier == (u32)wdev_id) {
91 result = wdev;
92 break;
93 }
94 }
95 mutex_unlock(&rdev->devlist_mtx);
96
97 if (result)
98 break;
99 }
100
101 if (result)
102 return result;
103 return ERR_PTR(-ENODEV);
Johannes Berg55682962007-09-20 13:09:35 -0400104}
105
Johannes Berga9455402012-06-15 13:32:49 +0200106static struct cfg80211_registered_device *
Johannes Berg878d9ec2012-06-15 14:18:32 +0200107__cfg80211_rdev_from_attrs(struct net *netns, struct nlattr **attrs)
Johannes Berga9455402012-06-15 13:32:49 +0200108{
Johannes Berg7fee4772012-06-15 14:09:58 +0200109 struct cfg80211_registered_device *rdev = NULL, *tmp;
110 struct net_device *netdev;
Johannes Berga9455402012-06-15 13:32:49 +0200111
112 assert_cfg80211_lock();
113
Johannes Berg878d9ec2012-06-15 14:18:32 +0200114 if (!attrs[NL80211_ATTR_WIPHY] &&
Johannes Berg89a54e42012-06-15 14:33:17 +0200115 !attrs[NL80211_ATTR_IFINDEX] &&
116 !attrs[NL80211_ATTR_WDEV])
Johannes Berg7fee4772012-06-15 14:09:58 +0200117 return ERR_PTR(-EINVAL);
118
Johannes Berg878d9ec2012-06-15 14:18:32 +0200119 if (attrs[NL80211_ATTR_WIPHY])
Johannes Berg7fee4772012-06-15 14:09:58 +0200120 rdev = cfg80211_rdev_by_wiphy_idx(
Johannes Berg878d9ec2012-06-15 14:18:32 +0200121 nla_get_u32(attrs[NL80211_ATTR_WIPHY]));
Johannes Berga9455402012-06-15 13:32:49 +0200122
Johannes Berg89a54e42012-06-15 14:33:17 +0200123 if (attrs[NL80211_ATTR_WDEV]) {
124 u64 wdev_id = nla_get_u64(attrs[NL80211_ATTR_WDEV]);
125 struct wireless_dev *wdev;
126 bool found = false;
127
128 tmp = cfg80211_rdev_by_wiphy_idx(wdev_id >> 32);
129 if (tmp) {
130 /* make sure wdev exists */
131 mutex_lock(&tmp->devlist_mtx);
132 list_for_each_entry(wdev, &tmp->wdev_list, list) {
133 if (wdev->identifier != (u32)wdev_id)
134 continue;
135 found = true;
136 break;
137 }
138 mutex_unlock(&tmp->devlist_mtx);
139
140 if (!found)
141 tmp = NULL;
142
143 if (rdev && tmp != rdev)
144 return ERR_PTR(-EINVAL);
145 rdev = tmp;
146 }
147 }
148
Johannes Berg878d9ec2012-06-15 14:18:32 +0200149 if (attrs[NL80211_ATTR_IFINDEX]) {
150 int ifindex = nla_get_u32(attrs[NL80211_ATTR_IFINDEX]);
Johannes Berg4f7eff12012-06-15 14:14:22 +0200151 netdev = dev_get_by_index(netns, ifindex);
Johannes Berg7fee4772012-06-15 14:09:58 +0200152 if (netdev) {
153 if (netdev->ieee80211_ptr)
154 tmp = wiphy_to_dev(
155 netdev->ieee80211_ptr->wiphy);
156 else
157 tmp = NULL;
158
159 dev_put(netdev);
160
161 /* not wireless device -- return error */
162 if (!tmp)
163 return ERR_PTR(-EINVAL);
164
165 /* mismatch -- return error */
166 if (rdev && tmp != rdev)
167 return ERR_PTR(-EINVAL);
168
169 rdev = tmp;
Johannes Berga9455402012-06-15 13:32:49 +0200170 }
Johannes Berga9455402012-06-15 13:32:49 +0200171 }
172
Johannes Berg4f7eff12012-06-15 14:14:22 +0200173 if (!rdev)
174 return ERR_PTR(-ENODEV);
Johannes Berga9455402012-06-15 13:32:49 +0200175
Johannes Berg4f7eff12012-06-15 14:14:22 +0200176 if (netns != wiphy_net(&rdev->wiphy))
177 return ERR_PTR(-ENODEV);
178
179 return rdev;
Johannes Berga9455402012-06-15 13:32:49 +0200180}
181
182/*
183 * This function returns a pointer to the driver
184 * that the genl_info item that is passed refers to.
185 * If successful, it returns non-NULL and also locks
186 * the driver's mutex!
187 *
188 * This means that you need to call cfg80211_unlock_rdev()
189 * before being allowed to acquire &cfg80211_mutex!
190 *
191 * This is necessary because we need to lock the global
192 * mutex to get an item off the list safely, and then
193 * we lock the rdev mutex so it doesn't go away under us.
194 *
195 * We don't want to keep cfg80211_mutex locked
196 * for all the time in order to allow requests on
197 * other interfaces to go through at the same time.
198 *
199 * The result of this can be a PTR_ERR and hence must
200 * be checked with IS_ERR() for errors.
201 */
202static struct cfg80211_registered_device *
Johannes Berg4f7eff12012-06-15 14:14:22 +0200203cfg80211_get_dev_from_info(struct net *netns, struct genl_info *info)
Johannes Berga9455402012-06-15 13:32:49 +0200204{
205 struct cfg80211_registered_device *rdev;
206
207 mutex_lock(&cfg80211_mutex);
Johannes Berg878d9ec2012-06-15 14:18:32 +0200208 rdev = __cfg80211_rdev_from_attrs(netns, info->attrs);
Johannes Berga9455402012-06-15 13:32:49 +0200209
210 /* if it is not an error we grab the lock on
211 * it to assure it won't be going away while
212 * we operate on it */
213 if (!IS_ERR(rdev))
214 mutex_lock(&rdev->mtx);
215
216 mutex_unlock(&cfg80211_mutex);
217
218 return rdev;
219}
220
Johannes Berg55682962007-09-20 13:09:35 -0400221/* policy for the attributes */
Alexey Dobriyanb54452b2010-02-18 08:14:31 +0000222static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = {
Johannes Berg55682962007-09-20 13:09:35 -0400223 [NL80211_ATTR_WIPHY] = { .type = NLA_U32 },
224 [NL80211_ATTR_WIPHY_NAME] = { .type = NLA_NUL_STRING,
David S. Miller079e24e2009-05-26 21:15:00 -0700225 .len = 20-1 },
Jouni Malinen31888482008-10-30 16:59:24 +0200226 [NL80211_ATTR_WIPHY_TXQ_PARAMS] = { .type = NLA_NESTED },
Johannes Berg3d9d1d62012-11-08 23:14:50 +0100227
Jouni Malinen72bdcf32008-11-26 16:15:24 +0200228 [NL80211_ATTR_WIPHY_FREQ] = { .type = NLA_U32 },
Sujith094d05d2008-12-12 11:57:43 +0530229 [NL80211_ATTR_WIPHY_CHANNEL_TYPE] = { .type = NLA_U32 },
Johannes Berg3d9d1d62012-11-08 23:14:50 +0100230 [NL80211_ATTR_CHANNEL_WIDTH] = { .type = NLA_U32 },
231 [NL80211_ATTR_CENTER_FREQ1] = { .type = NLA_U32 },
232 [NL80211_ATTR_CENTER_FREQ2] = { .type = NLA_U32 },
233
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +0200234 [NL80211_ATTR_WIPHY_RETRY_SHORT] = { .type = NLA_U8 },
235 [NL80211_ATTR_WIPHY_RETRY_LONG] = { .type = NLA_U8 },
236 [NL80211_ATTR_WIPHY_FRAG_THRESHOLD] = { .type = NLA_U32 },
237 [NL80211_ATTR_WIPHY_RTS_THRESHOLD] = { .type = NLA_U32 },
Lukáš Turek81077e82009-12-21 22:50:47 +0100238 [NL80211_ATTR_WIPHY_COVERAGE_CLASS] = { .type = NLA_U8 },
Johannes Berg55682962007-09-20 13:09:35 -0400239
240 [NL80211_ATTR_IFTYPE] = { .type = NLA_U32 },
241 [NL80211_ATTR_IFINDEX] = { .type = NLA_U32 },
242 [NL80211_ATTR_IFNAME] = { .type = NLA_NUL_STRING, .len = IFNAMSIZ-1 },
Johannes Berg41ade002007-12-19 02:03:29 +0100243
Eliad Pellere007b852011-11-24 18:13:56 +0200244 [NL80211_ATTR_MAC] = { .len = ETH_ALEN },
245 [NL80211_ATTR_PREV_BSSID] = { .len = ETH_ALEN },
Johannes Berg41ade002007-12-19 02:03:29 +0100246
Johannes Bergb9454e82009-07-08 13:29:08 +0200247 [NL80211_ATTR_KEY] = { .type = NLA_NESTED, },
Johannes Berg41ade002007-12-19 02:03:29 +0100248 [NL80211_ATTR_KEY_DATA] = { .type = NLA_BINARY,
249 .len = WLAN_MAX_KEY_LEN },
250 [NL80211_ATTR_KEY_IDX] = { .type = NLA_U8 },
251 [NL80211_ATTR_KEY_CIPHER] = { .type = NLA_U32 },
252 [NL80211_ATTR_KEY_DEFAULT] = { .type = NLA_FLAG },
Jouni Malinen81962262011-11-02 23:36:31 +0200253 [NL80211_ATTR_KEY_SEQ] = { .type = NLA_BINARY, .len = 16 },
Johannes Berge31b8212010-10-05 19:39:30 +0200254 [NL80211_ATTR_KEY_TYPE] = { .type = NLA_U32 },
Johannes Berged1b6cc2007-12-19 02:03:32 +0100255
256 [NL80211_ATTR_BEACON_INTERVAL] = { .type = NLA_U32 },
257 [NL80211_ATTR_DTIM_PERIOD] = { .type = NLA_U32 },
258 [NL80211_ATTR_BEACON_HEAD] = { .type = NLA_BINARY,
259 .len = IEEE80211_MAX_DATA_LEN },
260 [NL80211_ATTR_BEACON_TAIL] = { .type = NLA_BINARY,
261 .len = IEEE80211_MAX_DATA_LEN },
Johannes Berg5727ef12007-12-19 02:03:34 +0100262 [NL80211_ATTR_STA_AID] = { .type = NLA_U16 },
263 [NL80211_ATTR_STA_FLAGS] = { .type = NLA_NESTED },
264 [NL80211_ATTR_STA_LISTEN_INTERVAL] = { .type = NLA_U16 },
265 [NL80211_ATTR_STA_SUPPORTED_RATES] = { .type = NLA_BINARY,
266 .len = NL80211_MAX_SUPP_RATES },
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100267 [NL80211_ATTR_STA_PLINK_ACTION] = { .type = NLA_U8 },
Johannes Berg5727ef12007-12-19 02:03:34 +0100268 [NL80211_ATTR_STA_VLAN] = { .type = NLA_U32 },
Johannes Berg0a9542e2008-10-15 11:54:04 +0200269 [NL80211_ATTR_MNTR_FLAGS] = { /* NLA_NESTED can't be empty */ },
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100270 [NL80211_ATTR_MESH_ID] = { .type = NLA_BINARY,
Chun-Yeow Yeoha4f606e2012-06-11 11:59:36 +0800271 .len = IEEE80211_MAX_MESH_ID_LEN },
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100272 [NL80211_ATTR_MPATH_NEXT_HOP] = { .type = NLA_U32 },
Jouni Malinen9f1ba902008-08-07 20:07:01 +0300273
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -0700274 [NL80211_ATTR_REG_ALPHA2] = { .type = NLA_STRING, .len = 2 },
275 [NL80211_ATTR_REG_RULES] = { .type = NLA_NESTED },
276
Jouni Malinen9f1ba902008-08-07 20:07:01 +0300277 [NL80211_ATTR_BSS_CTS_PROT] = { .type = NLA_U8 },
278 [NL80211_ATTR_BSS_SHORT_PREAMBLE] = { .type = NLA_U8 },
279 [NL80211_ATTR_BSS_SHORT_SLOT_TIME] = { .type = NLA_U8 },
Jouni Malinen90c97a02008-10-30 16:59:22 +0200280 [NL80211_ATTR_BSS_BASIC_RATES] = { .type = NLA_BINARY,
281 .len = NL80211_MAX_SUPP_RATES },
Helmut Schaa50b12f52010-11-19 12:40:25 +0100282 [NL80211_ATTR_BSS_HT_OPMODE] = { .type = NLA_U16 },
Jouni Malinen36aedc92008-08-25 11:58:58 +0300283
Javier Cardona24bdd9f2010-12-16 17:37:48 -0800284 [NL80211_ATTR_MESH_CONFIG] = { .type = NLA_NESTED },
Javier Cardona15d5dda2011-04-07 15:08:28 -0700285 [NL80211_ATTR_SUPPORT_MESH_AUTH] = { .type = NLA_FLAG },
colin@cozybit.com93da9cc2008-10-21 12:03:48 -0700286
Johannes Berg6c739412011-11-03 09:27:01 +0100287 [NL80211_ATTR_HT_CAPABILITY] = { .len = NL80211_HT_CAPABILITY_LEN },
Jouni Malinen9aed3cc2009-01-13 16:03:29 +0200288
289 [NL80211_ATTR_MGMT_SUBTYPE] = { .type = NLA_U8 },
290 [NL80211_ATTR_IE] = { .type = NLA_BINARY,
291 .len = IEEE80211_MAX_DATA_LEN },
Johannes Berg2a519312009-02-10 21:25:55 +0100292 [NL80211_ATTR_SCAN_FREQUENCIES] = { .type = NLA_NESTED },
293 [NL80211_ATTR_SCAN_SSIDS] = { .type = NLA_NESTED },
Jouni Malinen636a5d32009-03-19 13:39:22 +0200294
295 [NL80211_ATTR_SSID] = { .type = NLA_BINARY,
296 .len = IEEE80211_MAX_SSID_LEN },
297 [NL80211_ATTR_AUTH_TYPE] = { .type = NLA_U32 },
298 [NL80211_ATTR_REASON_CODE] = { .type = NLA_U16 },
Johannes Berg04a773a2009-04-19 21:24:32 +0200299 [NL80211_ATTR_FREQ_FIXED] = { .type = NLA_FLAG },
Jouni Malinen1965c852009-04-22 21:38:25 +0300300 [NL80211_ATTR_TIMED_OUT] = { .type = NLA_FLAG },
Jouni Malinendc6382ce2009-05-06 22:09:37 +0300301 [NL80211_ATTR_USE_MFP] = { .type = NLA_U32 },
Johannes Bergeccb8e82009-05-11 21:57:56 +0300302 [NL80211_ATTR_STA_FLAGS2] = {
303 .len = sizeof(struct nl80211_sta_flag_update),
304 },
Jouni Malinen3f77316c2009-05-11 21:57:57 +0300305 [NL80211_ATTR_CONTROL_PORT] = { .type = NLA_FLAG },
Johannes Bergc0692b82010-08-27 14:26:53 +0300306 [NL80211_ATTR_CONTROL_PORT_ETHERTYPE] = { .type = NLA_U16 },
307 [NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT] = { .type = NLA_FLAG },
Samuel Ortizb23aa672009-07-01 21:26:54 +0200308 [NL80211_ATTR_PRIVACY] = { .type = NLA_FLAG },
309 [NL80211_ATTR_CIPHER_SUITE_GROUP] = { .type = NLA_U32 },
310 [NL80211_ATTR_WPA_VERSIONS] = { .type = NLA_U32 },
Johannes Berg463d0182009-07-14 00:33:35 +0200311 [NL80211_ATTR_PID] = { .type = NLA_U32 },
Felix Fietkau8b787642009-11-10 18:53:10 +0100312 [NL80211_ATTR_4ADDR] = { .type = NLA_U8 },
Samuel Ortiz67fbb162009-11-24 23:59:15 +0100313 [NL80211_ATTR_PMKID] = { .type = NLA_BINARY,
314 .len = WLAN_PMKID_LEN },
Jouni Malinen9588bbd2009-12-23 13:15:41 +0100315 [NL80211_ATTR_DURATION] = { .type = NLA_U32 },
316 [NL80211_ATTR_COOKIE] = { .type = NLA_U64 },
Jouni Malinen13ae75b2009-12-29 12:59:45 +0200317 [NL80211_ATTR_TX_RATES] = { .type = NLA_NESTED },
Jouni Malinen026331c2010-02-15 12:53:10 +0200318 [NL80211_ATTR_FRAME] = { .type = NLA_BINARY,
319 .len = IEEE80211_MAX_DATA_LEN },
320 [NL80211_ATTR_FRAME_MATCH] = { .type = NLA_BINARY, },
Kalle Valoffb9eb32010-02-17 17:58:10 +0200321 [NL80211_ATTR_PS_STATE] = { .type = NLA_U32 },
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +0200322 [NL80211_ATTR_CQM] = { .type = NLA_NESTED, },
Jouni Malinend5cdfac2010-04-04 09:37:19 +0300323 [NL80211_ATTR_LOCAL_STATE_CHANGE] = { .type = NLA_FLAG },
Felix Fietkaufd8aaaf2010-04-27 01:23:35 +0200324 [NL80211_ATTR_AP_ISOLATE] = { .type = NLA_U8 },
Juuso Oikarinen98d2ff82010-06-23 12:12:38 +0300325 [NL80211_ATTR_WIPHY_TX_POWER_SETTING] = { .type = NLA_U32 },
326 [NL80211_ATTR_WIPHY_TX_POWER_LEVEL] = { .type = NLA_U32 },
Johannes Berg2e161f72010-08-12 15:38:38 +0200327 [NL80211_ATTR_FRAME_TYPE] = { .type = NLA_U16 },
Bruno Randolfafe0cbf2010-11-10 12:50:50 +0900328 [NL80211_ATTR_WIPHY_ANTENNA_TX] = { .type = NLA_U32 },
329 [NL80211_ATTR_WIPHY_ANTENNA_RX] = { .type = NLA_U32 },
Felix Fietkau885a46d2010-11-11 15:07:22 +0100330 [NL80211_ATTR_MCAST_RATE] = { .type = NLA_U32 },
Johannes Bergf7ca38d2010-11-25 10:02:29 +0100331 [NL80211_ATTR_OFFCHANNEL_TX_OK] = { .type = NLA_FLAG },
Johannes Bergdbd2fd62010-12-09 19:58:59 +0100332 [NL80211_ATTR_KEY_DEFAULT_TYPES] = { .type = NLA_NESTED },
Johannes Bergff1b6e62011-05-04 15:37:28 +0200333 [NL80211_ATTR_WOWLAN_TRIGGERS] = { .type = NLA_NESTED },
Javier Cardona9c3990a2011-05-03 16:57:11 -0700334 [NL80211_ATTR_STA_PLINK_STATE] = { .type = NLA_U8 },
Luciano Coelhobbe6ad62011-05-11 17:09:37 +0300335 [NL80211_ATTR_SCHED_SCAN_INTERVAL] = { .type = NLA_U32 },
Johannes Berge5497d72011-07-05 16:35:40 +0200336 [NL80211_ATTR_REKEY_DATA] = { .type = NLA_NESTED },
Johannes Berg34850ab2011-07-18 18:08:35 +0200337 [NL80211_ATTR_SCAN_SUPP_RATES] = { .type = NLA_NESTED },
Jouni Malinen32e9de82011-08-10 23:53:31 +0300338 [NL80211_ATTR_HIDDEN_SSID] = { .type = NLA_U32 },
Jouni Malinen9946ecf2011-08-10 23:55:56 +0300339 [NL80211_ATTR_IE_PROBE_RESP] = { .type = NLA_BINARY,
340 .len = IEEE80211_MAX_DATA_LEN },
341 [NL80211_ATTR_IE_ASSOC_RESP] = { .type = NLA_BINARY,
342 .len = IEEE80211_MAX_DATA_LEN },
Vivek Natarajanf4b34b52011-08-29 14:23:03 +0530343 [NL80211_ATTR_ROAM_SUPPORT] = { .type = NLA_FLAG },
Luciano Coelhoa1f1c212011-08-31 16:01:48 +0300344 [NL80211_ATTR_SCHED_SCAN_MATCH] = { .type = NLA_NESTED },
Rajkumar Manoharane9f935e2011-09-25 14:53:30 +0530345 [NL80211_ATTR_TX_NO_CCK_RATE] = { .type = NLA_FLAG },
Arik Nemtsov109086c2011-09-28 14:12:50 +0300346 [NL80211_ATTR_TDLS_ACTION] = { .type = NLA_U8 },
347 [NL80211_ATTR_TDLS_DIALOG_TOKEN] = { .type = NLA_U8 },
348 [NL80211_ATTR_TDLS_OPERATION] = { .type = NLA_U8 },
349 [NL80211_ATTR_TDLS_SUPPORT] = { .type = NLA_FLAG },
350 [NL80211_ATTR_TDLS_EXTERNAL_SETUP] = { .type = NLA_FLAG },
Johannes Berge247bd902011-11-04 11:18:21 +0100351 [NL80211_ATTR_DONT_WAIT_FOR_ACK] = { .type = NLA_FLAG },
Arik Nemtsov00f740e2011-11-10 11:28:56 +0200352 [NL80211_ATTR_PROBE_RESP] = { .type = NLA_BINARY,
353 .len = IEEE80211_MAX_DATA_LEN },
Luis R. Rodriguez8b60b072011-10-11 10:59:02 -0700354 [NL80211_ATTR_DFS_REGION] = { .type = NLA_U8 },
Ben Greear7e7c8922011-11-18 11:31:59 -0800355 [NL80211_ATTR_DISABLE_HT] = { .type = NLA_FLAG },
356 [NL80211_ATTR_HT_CAPABILITY_MASK] = {
357 .len = NL80211_HT_CAPABILITY_LEN
358 },
Simon Wunderlich1d9d9212011-11-18 14:20:43 +0100359 [NL80211_ATTR_NOACK_MAP] = { .type = NLA_U16 },
Vasanthakumar Thiagarajan1b658f12012-03-02 15:50:02 +0530360 [NL80211_ATTR_INACTIVITY_TIMEOUT] = { .type = NLA_U16 },
Bala Shanmugam4486ea92012-03-07 17:27:12 +0530361 [NL80211_ATTR_BG_SCAN_PERIOD] = { .type = NLA_U16 },
Johannes Berg89a54e42012-06-15 14:33:17 +0200362 [NL80211_ATTR_WDEV] = { .type = NLA_U64 },
Luis R. Rodriguez57b5ce02012-07-12 11:49:18 -0700363 [NL80211_ATTR_USER_REG_HINT_TYPE] = { .type = NLA_U32 },
Jouni Malinene39e5b52012-09-30 19:29:39 +0300364 [NL80211_ATTR_SAE_DATA] = { .type = NLA_BINARY, },
Mahesh Palivelaf461be3e2012-10-11 08:04:52 +0000365 [NL80211_ATTR_VHT_CAPABILITY] = { .len = NL80211_VHT_CAPABILITY_LEN },
Sam Lefflered4737712012-10-11 21:03:31 -0700366 [NL80211_ATTR_SCAN_FLAGS] = { .type = NLA_U32 },
Johannes Berg53cabad2012-11-14 15:17:28 +0100367 [NL80211_ATTR_P2P_CTWINDOW] = { .type = NLA_U8 },
368 [NL80211_ATTR_P2P_OPPPS] = { .type = NLA_U8 },
Vasanthakumar Thiagarajan77765ea2013-01-18 11:18:45 +0530369 [NL80211_ATTR_ACL_POLICY] = {. type = NLA_U32 },
370 [NL80211_ATTR_MAC_ADDRS] = { .type = NLA_NESTED },
Jouni Malinen9d62a982013-02-14 21:10:13 +0200371 [NL80211_ATTR_STA_CAPABILITY] = { .type = NLA_U16 },
372 [NL80211_ATTR_STA_EXT_CAPABILITY] = { .type = NLA_BINARY, },
Johannes Berg3713b4e2013-02-14 16:19:38 +0100373 [NL80211_ATTR_SPLIT_WIPHY_DUMP] = { .type = NLA_FLAG, },
Johannes Bergee2aca32013-02-21 17:36:01 +0100374 [NL80211_ATTR_DISABLE_VHT] = { .type = NLA_FLAG },
375 [NL80211_ATTR_VHT_CAPABILITY_MASK] = {
376 .len = NL80211_VHT_CAPABILITY_LEN,
377 },
Jouni Malinen355199e2013-02-27 17:14:27 +0200378 [NL80211_ATTR_MDID] = { .type = NLA_U16 },
379 [NL80211_ATTR_IE_RIC] = { .type = NLA_BINARY,
380 .len = IEEE80211_MAX_DATA_LEN },
Johannes Berg55682962007-09-20 13:09:35 -0400381};
382
Johannes Berge31b8212010-10-05 19:39:30 +0200383/* policy for the key attributes */
Alexey Dobriyanb54452b2010-02-18 08:14:31 +0000384static const struct nla_policy nl80211_key_policy[NL80211_KEY_MAX + 1] = {
Johannes Bergfffd0932009-07-08 14:22:54 +0200385 [NL80211_KEY_DATA] = { .type = NLA_BINARY, .len = WLAN_MAX_KEY_LEN },
Johannes Bergb9454e82009-07-08 13:29:08 +0200386 [NL80211_KEY_IDX] = { .type = NLA_U8 },
387 [NL80211_KEY_CIPHER] = { .type = NLA_U32 },
Jouni Malinen81962262011-11-02 23:36:31 +0200388 [NL80211_KEY_SEQ] = { .type = NLA_BINARY, .len = 16 },
Johannes Bergb9454e82009-07-08 13:29:08 +0200389 [NL80211_KEY_DEFAULT] = { .type = NLA_FLAG },
390 [NL80211_KEY_DEFAULT_MGMT] = { .type = NLA_FLAG },
Johannes Berge31b8212010-10-05 19:39:30 +0200391 [NL80211_KEY_TYPE] = { .type = NLA_U32 },
Johannes Bergdbd2fd62010-12-09 19:58:59 +0100392 [NL80211_KEY_DEFAULT_TYPES] = { .type = NLA_NESTED },
393};
394
395/* policy for the key default flags */
396static const struct nla_policy
397nl80211_key_default_policy[NUM_NL80211_KEY_DEFAULT_TYPES] = {
398 [NL80211_KEY_DEFAULT_TYPE_UNICAST] = { .type = NLA_FLAG },
399 [NL80211_KEY_DEFAULT_TYPE_MULTICAST] = { .type = NLA_FLAG },
Johannes Bergb9454e82009-07-08 13:29:08 +0200400};
401
Johannes Bergff1b6e62011-05-04 15:37:28 +0200402/* policy for WoWLAN attributes */
403static const struct nla_policy
404nl80211_wowlan_policy[NUM_NL80211_WOWLAN_TRIG] = {
405 [NL80211_WOWLAN_TRIG_ANY] = { .type = NLA_FLAG },
406 [NL80211_WOWLAN_TRIG_DISCONNECT] = { .type = NLA_FLAG },
407 [NL80211_WOWLAN_TRIG_MAGIC_PKT] = { .type = NLA_FLAG },
408 [NL80211_WOWLAN_TRIG_PKT_PATTERN] = { .type = NLA_NESTED },
Johannes Berg77dbbb12011-07-13 10:48:55 +0200409 [NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE] = { .type = NLA_FLAG },
410 [NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST] = { .type = NLA_FLAG },
411 [NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE] = { .type = NLA_FLAG },
412 [NL80211_WOWLAN_TRIG_RFKILL_RELEASE] = { .type = NLA_FLAG },
Johannes Berg2a0e0472013-01-23 22:57:40 +0100413 [NL80211_WOWLAN_TRIG_TCP_CONNECTION] = { .type = NLA_NESTED },
414};
415
416static const struct nla_policy
417nl80211_wowlan_tcp_policy[NUM_NL80211_WOWLAN_TCP] = {
418 [NL80211_WOWLAN_TCP_SRC_IPV4] = { .type = NLA_U32 },
419 [NL80211_WOWLAN_TCP_DST_IPV4] = { .type = NLA_U32 },
420 [NL80211_WOWLAN_TCP_DST_MAC] = { .len = ETH_ALEN },
421 [NL80211_WOWLAN_TCP_SRC_PORT] = { .type = NLA_U16 },
422 [NL80211_WOWLAN_TCP_DST_PORT] = { .type = NLA_U16 },
423 [NL80211_WOWLAN_TCP_DATA_PAYLOAD] = { .len = 1 },
424 [NL80211_WOWLAN_TCP_DATA_PAYLOAD_SEQ] = {
425 .len = sizeof(struct nl80211_wowlan_tcp_data_seq)
426 },
427 [NL80211_WOWLAN_TCP_DATA_PAYLOAD_TOKEN] = {
428 .len = sizeof(struct nl80211_wowlan_tcp_data_token)
429 },
430 [NL80211_WOWLAN_TCP_DATA_INTERVAL] = { .type = NLA_U32 },
431 [NL80211_WOWLAN_TCP_WAKE_PAYLOAD] = { .len = 1 },
432 [NL80211_WOWLAN_TCP_WAKE_MASK] = { .len = 1 },
Johannes Bergff1b6e62011-05-04 15:37:28 +0200433};
434
Johannes Berge5497d72011-07-05 16:35:40 +0200435/* policy for GTK rekey offload attributes */
436static const struct nla_policy
437nl80211_rekey_policy[NUM_NL80211_REKEY_DATA] = {
438 [NL80211_REKEY_DATA_KEK] = { .len = NL80211_KEK_LEN },
439 [NL80211_REKEY_DATA_KCK] = { .len = NL80211_KCK_LEN },
440 [NL80211_REKEY_DATA_REPLAY_CTR] = { .len = NL80211_REPLAY_CTR_LEN },
441};
442
Luciano Coelhoa1f1c212011-08-31 16:01:48 +0300443static const struct nla_policy
444nl80211_match_policy[NL80211_SCHED_SCAN_MATCH_ATTR_MAX + 1] = {
Johannes Berg4a4ab0d2012-06-13 11:17:11 +0200445 [NL80211_SCHED_SCAN_MATCH_ATTR_SSID] = { .type = NLA_BINARY,
Luciano Coelhoa1f1c212011-08-31 16:01:48 +0300446 .len = IEEE80211_MAX_SSID_LEN },
Thomas Pedersen88e920b2012-06-21 11:09:54 -0700447 [NL80211_SCHED_SCAN_MATCH_ATTR_RSSI] = { .type = NLA_U32 },
Luciano Coelhoa1f1c212011-08-31 16:01:48 +0300448};
449
Johannes Berg97990a02013-04-19 01:02:55 +0200450static int nl80211_prepare_wdev_dump(struct sk_buff *skb,
451 struct netlink_callback *cb,
452 struct cfg80211_registered_device **rdev,
453 struct wireless_dev **wdev)
Holger Schuriga0438972009-11-11 11:30:02 +0100454{
Johannes Berg67748892010-10-04 21:14:06 +0200455 int err;
456
Johannes Berg67748892010-10-04 21:14:06 +0200457 rtnl_lock();
Johannes Berg97990a02013-04-19 01:02:55 +0200458 mutex_lock(&cfg80211_mutex);
Johannes Berg67748892010-10-04 21:14:06 +0200459
Johannes Berg97990a02013-04-19 01:02:55 +0200460 if (!cb->args[0]) {
461 err = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize,
462 nl80211_fam.attrbuf, nl80211_fam.maxattr,
463 nl80211_policy);
464 if (err)
465 goto out_unlock;
466
467 *wdev = __cfg80211_wdev_from_attrs(sock_net(skb->sk),
468 nl80211_fam.attrbuf);
469 if (IS_ERR(*wdev)) {
470 err = PTR_ERR(*wdev);
471 goto out_unlock;
472 }
473 *rdev = wiphy_to_dev((*wdev)->wiphy);
Johannes Berg289813a2013-07-30 22:34:28 +0200474 /* 0 is the first index - add 1 to parse only once */
475 cb->args[0] = (*rdev)->wiphy_idx + 1;
Johannes Berg97990a02013-04-19 01:02:55 +0200476 cb->args[1] = (*wdev)->identifier;
477 } else {
Johannes Berg289813a2013-07-30 22:34:28 +0200478 /* subtract the 1 again here */
479 struct wiphy *wiphy = wiphy_idx_to_wiphy(cb->args[0] - 1);
Johannes Berg97990a02013-04-19 01:02:55 +0200480 struct wireless_dev *tmp;
481
482 if (!wiphy) {
483 err = -ENODEV;
484 goto out_unlock;
485 }
486 *rdev = wiphy_to_dev(wiphy);
487 *wdev = NULL;
488
489 mutex_lock(&(*rdev)->devlist_mtx);
490 list_for_each_entry(tmp, &(*rdev)->wdev_list, list) {
491 if (tmp->identifier == cb->args[1]) {
492 *wdev = tmp;
493 break;
494 }
495 }
496 mutex_unlock(&(*rdev)->devlist_mtx);
497
498 if (!*wdev) {
499 err = -ENODEV;
500 goto out_unlock;
501 }
Johannes Berg67748892010-10-04 21:14:06 +0200502 }
503
Johannes Berg97990a02013-04-19 01:02:55 +0200504 cfg80211_lock_rdev(*rdev);
Johannes Berg67748892010-10-04 21:14:06 +0200505
Johannes Berg97990a02013-04-19 01:02:55 +0200506 mutex_unlock(&cfg80211_mutex);
Johannes Berg67748892010-10-04 21:14:06 +0200507 return 0;
Johannes Berg97990a02013-04-19 01:02:55 +0200508 out_unlock:
509 mutex_unlock(&cfg80211_mutex);
Johannes Berg67748892010-10-04 21:14:06 +0200510 rtnl_unlock();
511 return err;
512}
513
Johannes Berg97990a02013-04-19 01:02:55 +0200514static void nl80211_finish_wdev_dump(struct cfg80211_registered_device *rdev)
Johannes Berg67748892010-10-04 21:14:06 +0200515{
516 cfg80211_unlock_rdev(rdev);
517 rtnl_unlock();
518}
519
Johannes Bergf4a11bb2009-03-27 12:40:28 +0100520/* IE validation */
521static bool is_valid_ie_attr(const struct nlattr *attr)
522{
523 const u8 *pos;
524 int len;
525
526 if (!attr)
527 return true;
528
529 pos = nla_data(attr);
530 len = nla_len(attr);
531
532 while (len) {
533 u8 elemlen;
534
535 if (len < 2)
536 return false;
537 len -= 2;
538
539 elemlen = pos[1];
540 if (elemlen > len)
541 return false;
542
543 len -= elemlen;
544 pos += 2 + elemlen;
545 }
546
547 return true;
548}
549
Johannes Berg55682962007-09-20 13:09:35 -0400550/* message building helper */
Eric W. Biederman15e47302012-09-07 20:12:54 +0000551static inline void *nl80211hdr_put(struct sk_buff *skb, u32 portid, u32 seq,
Johannes Berg55682962007-09-20 13:09:35 -0400552 int flags, u8 cmd)
553{
554 /* since there is no private header just add the generic one */
Eric W. Biederman15e47302012-09-07 20:12:54 +0000555 return genlmsg_put(skb, portid, seq, &nl80211_fam, flags, cmd);
Johannes Berg55682962007-09-20 13:09:35 -0400556}
557
Luis R. Rodriguez5dab3b82009-04-02 14:08:08 -0400558static int nl80211_msg_put_channel(struct sk_buff *msg,
Johannes Bergcdc89b92013-02-18 23:54:36 +0100559 struct ieee80211_channel *chan,
560 bool large)
Luis R. Rodriguez5dab3b82009-04-02 14:08:08 -0400561{
David S. Miller9360ffd2012-03-29 04:41:26 -0400562 if (nla_put_u32(msg, NL80211_FREQUENCY_ATTR_FREQ,
563 chan->center_freq))
564 goto nla_put_failure;
Luis R. Rodriguez5dab3b82009-04-02 14:08:08 -0400565
David S. Miller9360ffd2012-03-29 04:41:26 -0400566 if ((chan->flags & IEEE80211_CHAN_DISABLED) &&
567 nla_put_flag(msg, NL80211_FREQUENCY_ATTR_DISABLED))
568 goto nla_put_failure;
569 if ((chan->flags & IEEE80211_CHAN_PASSIVE_SCAN) &&
570 nla_put_flag(msg, NL80211_FREQUENCY_ATTR_PASSIVE_SCAN))
571 goto nla_put_failure;
572 if ((chan->flags & IEEE80211_CHAN_NO_IBSS) &&
573 nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_IBSS))
574 goto nla_put_failure;
Johannes Bergcdc89b92013-02-18 23:54:36 +0100575 if (chan->flags & IEEE80211_CHAN_RADAR) {
576 if (nla_put_flag(msg, NL80211_FREQUENCY_ATTR_RADAR))
577 goto nla_put_failure;
578 if (large) {
579 u32 time;
580
581 time = elapsed_jiffies_msecs(chan->dfs_state_entered);
582
583 if (nla_put_u32(msg, NL80211_FREQUENCY_ATTR_DFS_STATE,
584 chan->dfs_state))
585 goto nla_put_failure;
586 if (nla_put_u32(msg, NL80211_FREQUENCY_ATTR_DFS_TIME,
587 time))
588 goto nla_put_failure;
589 }
590 }
Luis R. Rodriguez5dab3b82009-04-02 14:08:08 -0400591
Johannes Bergfe1abaf2013-02-27 15:39:45 +0100592 if (large) {
593 if ((chan->flags & IEEE80211_CHAN_NO_HT40MINUS) &&
594 nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_HT40_MINUS))
595 goto nla_put_failure;
596 if ((chan->flags & IEEE80211_CHAN_NO_HT40PLUS) &&
597 nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_HT40_PLUS))
598 goto nla_put_failure;
599 if ((chan->flags & IEEE80211_CHAN_NO_80MHZ) &&
600 nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_80MHZ))
601 goto nla_put_failure;
602 if ((chan->flags & IEEE80211_CHAN_NO_160MHZ) &&
603 nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_160MHZ))
604 goto nla_put_failure;
605 }
606
David S. Miller9360ffd2012-03-29 04:41:26 -0400607 if (nla_put_u32(msg, NL80211_FREQUENCY_ATTR_MAX_TX_POWER,
608 DBM_TO_MBM(chan->max_power)))
609 goto nla_put_failure;
Luis R. Rodriguez5dab3b82009-04-02 14:08:08 -0400610
611 return 0;
612
613 nla_put_failure:
614 return -ENOBUFS;
615}
616
Johannes Berg55682962007-09-20 13:09:35 -0400617/* netlink command implementations */
618
Johannes Bergb9454e82009-07-08 13:29:08 +0200619struct key_parse {
620 struct key_params p;
621 int idx;
Johannes Berge31b8212010-10-05 19:39:30 +0200622 int type;
Johannes Bergb9454e82009-07-08 13:29:08 +0200623 bool def, defmgmt;
Johannes Bergdbd2fd62010-12-09 19:58:59 +0100624 bool def_uni, def_multi;
Johannes Bergb9454e82009-07-08 13:29:08 +0200625};
626
627static int nl80211_parse_key_new(struct nlattr *key, struct key_parse *k)
628{
629 struct nlattr *tb[NL80211_KEY_MAX + 1];
630 int err = nla_parse_nested(tb, NL80211_KEY_MAX, key,
631 nl80211_key_policy);
632 if (err)
633 return err;
634
635 k->def = !!tb[NL80211_KEY_DEFAULT];
636 k->defmgmt = !!tb[NL80211_KEY_DEFAULT_MGMT];
637
Johannes Bergdbd2fd62010-12-09 19:58:59 +0100638 if (k->def) {
639 k->def_uni = true;
640 k->def_multi = true;
641 }
642 if (k->defmgmt)
643 k->def_multi = true;
644
Johannes Bergb9454e82009-07-08 13:29:08 +0200645 if (tb[NL80211_KEY_IDX])
646 k->idx = nla_get_u8(tb[NL80211_KEY_IDX]);
647
648 if (tb[NL80211_KEY_DATA]) {
649 k->p.key = nla_data(tb[NL80211_KEY_DATA]);
650 k->p.key_len = nla_len(tb[NL80211_KEY_DATA]);
651 }
652
653 if (tb[NL80211_KEY_SEQ]) {
654 k->p.seq = nla_data(tb[NL80211_KEY_SEQ]);
655 k->p.seq_len = nla_len(tb[NL80211_KEY_SEQ]);
656 }
657
658 if (tb[NL80211_KEY_CIPHER])
659 k->p.cipher = nla_get_u32(tb[NL80211_KEY_CIPHER]);
660
Johannes Berge31b8212010-10-05 19:39:30 +0200661 if (tb[NL80211_KEY_TYPE]) {
662 k->type = nla_get_u32(tb[NL80211_KEY_TYPE]);
663 if (k->type < 0 || k->type >= NUM_NL80211_KEYTYPES)
664 return -EINVAL;
665 }
666
Johannes Bergdbd2fd62010-12-09 19:58:59 +0100667 if (tb[NL80211_KEY_DEFAULT_TYPES]) {
668 struct nlattr *kdt[NUM_NL80211_KEY_DEFAULT_TYPES];
Johannes Berg2da8f412012-01-20 13:52:37 +0100669 err = nla_parse_nested(kdt, NUM_NL80211_KEY_DEFAULT_TYPES - 1,
670 tb[NL80211_KEY_DEFAULT_TYPES],
671 nl80211_key_default_policy);
Johannes Bergdbd2fd62010-12-09 19:58:59 +0100672 if (err)
673 return err;
674
675 k->def_uni = kdt[NL80211_KEY_DEFAULT_TYPE_UNICAST];
676 k->def_multi = kdt[NL80211_KEY_DEFAULT_TYPE_MULTICAST];
677 }
678
Johannes Bergb9454e82009-07-08 13:29:08 +0200679 return 0;
680}
681
682static int nl80211_parse_key_old(struct genl_info *info, struct key_parse *k)
683{
684 if (info->attrs[NL80211_ATTR_KEY_DATA]) {
685 k->p.key = nla_data(info->attrs[NL80211_ATTR_KEY_DATA]);
686 k->p.key_len = nla_len(info->attrs[NL80211_ATTR_KEY_DATA]);
687 }
688
689 if (info->attrs[NL80211_ATTR_KEY_SEQ]) {
690 k->p.seq = nla_data(info->attrs[NL80211_ATTR_KEY_SEQ]);
691 k->p.seq_len = nla_len(info->attrs[NL80211_ATTR_KEY_SEQ]);
692 }
693
694 if (info->attrs[NL80211_ATTR_KEY_IDX])
695 k->idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]);
696
697 if (info->attrs[NL80211_ATTR_KEY_CIPHER])
698 k->p.cipher = nla_get_u32(info->attrs[NL80211_ATTR_KEY_CIPHER]);
699
700 k->def = !!info->attrs[NL80211_ATTR_KEY_DEFAULT];
701 k->defmgmt = !!info->attrs[NL80211_ATTR_KEY_DEFAULT_MGMT];
702
Johannes Bergdbd2fd62010-12-09 19:58:59 +0100703 if (k->def) {
704 k->def_uni = true;
705 k->def_multi = true;
706 }
707 if (k->defmgmt)
708 k->def_multi = true;
709
Johannes Berge31b8212010-10-05 19:39:30 +0200710 if (info->attrs[NL80211_ATTR_KEY_TYPE]) {
711 k->type = nla_get_u32(info->attrs[NL80211_ATTR_KEY_TYPE]);
712 if (k->type < 0 || k->type >= NUM_NL80211_KEYTYPES)
713 return -EINVAL;
714 }
715
Johannes Bergdbd2fd62010-12-09 19:58:59 +0100716 if (info->attrs[NL80211_ATTR_KEY_DEFAULT_TYPES]) {
717 struct nlattr *kdt[NUM_NL80211_KEY_DEFAULT_TYPES];
718 int err = nla_parse_nested(
719 kdt, NUM_NL80211_KEY_DEFAULT_TYPES - 1,
720 info->attrs[NL80211_ATTR_KEY_DEFAULT_TYPES],
721 nl80211_key_default_policy);
722 if (err)
723 return err;
724
725 k->def_uni = kdt[NL80211_KEY_DEFAULT_TYPE_UNICAST];
726 k->def_multi = kdt[NL80211_KEY_DEFAULT_TYPE_MULTICAST];
727 }
728
Johannes Bergb9454e82009-07-08 13:29:08 +0200729 return 0;
730}
731
732static int nl80211_parse_key(struct genl_info *info, struct key_parse *k)
733{
734 int err;
735
736 memset(k, 0, sizeof(*k));
737 k->idx = -1;
Johannes Berge31b8212010-10-05 19:39:30 +0200738 k->type = -1;
Johannes Bergb9454e82009-07-08 13:29:08 +0200739
740 if (info->attrs[NL80211_ATTR_KEY])
741 err = nl80211_parse_key_new(info->attrs[NL80211_ATTR_KEY], k);
742 else
743 err = nl80211_parse_key_old(info, k);
744
745 if (err)
746 return err;
747
748 if (k->def && k->defmgmt)
749 return -EINVAL;
750
Johannes Bergdbd2fd62010-12-09 19:58:59 +0100751 if (k->defmgmt) {
752 if (k->def_uni || !k->def_multi)
753 return -EINVAL;
754 }
755
Johannes Bergb9454e82009-07-08 13:29:08 +0200756 if (k->idx != -1) {
757 if (k->defmgmt) {
758 if (k->idx < 4 || k->idx > 5)
759 return -EINVAL;
760 } else if (k->def) {
761 if (k->idx < 0 || k->idx > 3)
762 return -EINVAL;
763 } else {
764 if (k->idx < 0 || k->idx > 5)
765 return -EINVAL;
766 }
767 }
768
769 return 0;
770}
771
Johannes Bergfffd0932009-07-08 14:22:54 +0200772static struct cfg80211_cached_keys *
773nl80211_parse_connkeys(struct cfg80211_registered_device *rdev,
Sujith Manoharande7044e2012-10-18 10:19:28 +0530774 struct nlattr *keys, bool *no_ht)
Johannes Bergfffd0932009-07-08 14:22:54 +0200775{
776 struct key_parse parse;
777 struct nlattr *key;
778 struct cfg80211_cached_keys *result;
779 int rem, err, def = 0;
780
781 result = kzalloc(sizeof(*result), GFP_KERNEL);
782 if (!result)
783 return ERR_PTR(-ENOMEM);
784
785 result->def = -1;
786 result->defmgmt = -1;
787
788 nla_for_each_nested(key, keys, rem) {
789 memset(&parse, 0, sizeof(parse));
790 parse.idx = -1;
791
792 err = nl80211_parse_key_new(key, &parse);
793 if (err)
794 goto error;
795 err = -EINVAL;
796 if (!parse.p.key)
797 goto error;
798 if (parse.idx < 0 || parse.idx > 4)
799 goto error;
800 if (parse.def) {
801 if (def)
802 goto error;
803 def = 1;
804 result->def = parse.idx;
Johannes Bergdbd2fd62010-12-09 19:58:59 +0100805 if (!parse.def_uni || !parse.def_multi)
806 goto error;
Johannes Bergfffd0932009-07-08 14:22:54 +0200807 } else if (parse.defmgmt)
808 goto error;
809 err = cfg80211_validate_key_settings(rdev, &parse.p,
Johannes Berge31b8212010-10-05 19:39:30 +0200810 parse.idx, false, NULL);
Johannes Bergfffd0932009-07-08 14:22:54 +0200811 if (err)
812 goto error;
813 result->params[parse.idx].cipher = parse.p.cipher;
814 result->params[parse.idx].key_len = parse.p.key_len;
815 result->params[parse.idx].key = result->data[parse.idx];
816 memcpy(result->data[parse.idx], parse.p.key, parse.p.key_len);
Sujith Manoharande7044e2012-10-18 10:19:28 +0530817
818 if (parse.p.cipher == WLAN_CIPHER_SUITE_WEP40 ||
819 parse.p.cipher == WLAN_CIPHER_SUITE_WEP104) {
820 if (no_ht)
821 *no_ht = true;
822 }
Johannes Bergfffd0932009-07-08 14:22:54 +0200823 }
824
825 return result;
826 error:
827 kfree(result);
828 return ERR_PTR(err);
829}
830
831static int nl80211_key_allowed(struct wireless_dev *wdev)
832{
833 ASSERT_WDEV_LOCK(wdev);
834
Johannes Bergfffd0932009-07-08 14:22:54 +0200835 switch (wdev->iftype) {
836 case NL80211_IFTYPE_AP:
837 case NL80211_IFTYPE_AP_VLAN:
Johannes Berg074ac8d2010-09-16 14:58:22 +0200838 case NL80211_IFTYPE_P2P_GO:
Thomas Pedersenff973af2011-05-03 16:57:12 -0700839 case NL80211_IFTYPE_MESH_POINT:
Johannes Bergfffd0932009-07-08 14:22:54 +0200840 break;
841 case NL80211_IFTYPE_ADHOC:
842 if (!wdev->current_bss)
843 return -ENOLINK;
844 break;
845 case NL80211_IFTYPE_STATION:
Johannes Berg074ac8d2010-09-16 14:58:22 +0200846 case NL80211_IFTYPE_P2P_CLIENT:
Johannes Bergfffd0932009-07-08 14:22:54 +0200847 if (wdev->sme_state != CFG80211_SME_CONNECTED)
848 return -ENOLINK;
849 break;
850 default:
851 return -EINVAL;
852 }
853
854 return 0;
855}
856
Johannes Berg7527a782011-05-13 10:58:57 +0200857static int nl80211_put_iftypes(struct sk_buff *msg, u32 attr, u16 ifmodes)
858{
859 struct nlattr *nl_modes = nla_nest_start(msg, attr);
860 int i;
861
862 if (!nl_modes)
863 goto nla_put_failure;
864
865 i = 0;
866 while (ifmodes) {
David S. Miller9360ffd2012-03-29 04:41:26 -0400867 if ((ifmodes & 1) && nla_put_flag(msg, i))
868 goto nla_put_failure;
Johannes Berg7527a782011-05-13 10:58:57 +0200869 ifmodes >>= 1;
870 i++;
871 }
872
873 nla_nest_end(msg, nl_modes);
874 return 0;
875
876nla_put_failure:
877 return -ENOBUFS;
878}
879
880static int nl80211_put_iface_combinations(struct wiphy *wiphy,
Johannes Bergcdc89b92013-02-18 23:54:36 +0100881 struct sk_buff *msg,
882 bool large)
Johannes Berg7527a782011-05-13 10:58:57 +0200883{
884 struct nlattr *nl_combis;
885 int i, j;
886
887 nl_combis = nla_nest_start(msg,
888 NL80211_ATTR_INTERFACE_COMBINATIONS);
889 if (!nl_combis)
890 goto nla_put_failure;
891
892 for (i = 0; i < wiphy->n_iface_combinations; i++) {
893 const struct ieee80211_iface_combination *c;
894 struct nlattr *nl_combi, *nl_limits;
895
896 c = &wiphy->iface_combinations[i];
897
898 nl_combi = nla_nest_start(msg, i + 1);
899 if (!nl_combi)
900 goto nla_put_failure;
901
902 nl_limits = nla_nest_start(msg, NL80211_IFACE_COMB_LIMITS);
903 if (!nl_limits)
904 goto nla_put_failure;
905
906 for (j = 0; j < c->n_limits; j++) {
907 struct nlattr *nl_limit;
908
909 nl_limit = nla_nest_start(msg, j + 1);
910 if (!nl_limit)
911 goto nla_put_failure;
David S. Miller9360ffd2012-03-29 04:41:26 -0400912 if (nla_put_u32(msg, NL80211_IFACE_LIMIT_MAX,
913 c->limits[j].max))
914 goto nla_put_failure;
Johannes Berg7527a782011-05-13 10:58:57 +0200915 if (nl80211_put_iftypes(msg, NL80211_IFACE_LIMIT_TYPES,
916 c->limits[j].types))
917 goto nla_put_failure;
918 nla_nest_end(msg, nl_limit);
919 }
920
921 nla_nest_end(msg, nl_limits);
922
David S. Miller9360ffd2012-03-29 04:41:26 -0400923 if (c->beacon_int_infra_match &&
924 nla_put_flag(msg, NL80211_IFACE_COMB_STA_AP_BI_MATCH))
925 goto nla_put_failure;
926 if (nla_put_u32(msg, NL80211_IFACE_COMB_NUM_CHANNELS,
927 c->num_different_channels) ||
928 nla_put_u32(msg, NL80211_IFACE_COMB_MAXNUM,
929 c->max_interfaces))
930 goto nla_put_failure;
Johannes Bergcdc89b92013-02-18 23:54:36 +0100931 if (large &&
932 nla_put_u32(msg, NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS,
933 c->radar_detect_widths))
934 goto nla_put_failure;
Johannes Berg7527a782011-05-13 10:58:57 +0200935
936 nla_nest_end(msg, nl_combi);
937 }
938
939 nla_nest_end(msg, nl_combis);
940
941 return 0;
942nla_put_failure:
943 return -ENOBUFS;
944}
945
Johannes Berg3713b4e2013-02-14 16:19:38 +0100946#ifdef CONFIG_PM
Johannes Bergb56cf722013-02-20 01:02:38 +0100947static int nl80211_send_wowlan_tcp_caps(struct cfg80211_registered_device *rdev,
948 struct sk_buff *msg)
949{
950 const struct wiphy_wowlan_tcp_support *tcp = rdev->wiphy.wowlan.tcp;
951 struct nlattr *nl_tcp;
952
953 if (!tcp)
954 return 0;
955
956 nl_tcp = nla_nest_start(msg, NL80211_WOWLAN_TRIG_TCP_CONNECTION);
957 if (!nl_tcp)
958 return -ENOBUFS;
959
960 if (nla_put_u32(msg, NL80211_WOWLAN_TCP_DATA_PAYLOAD,
961 tcp->data_payload_max))
962 return -ENOBUFS;
963
964 if (nla_put_u32(msg, NL80211_WOWLAN_TCP_DATA_PAYLOAD,
965 tcp->data_payload_max))
966 return -ENOBUFS;
967
968 if (tcp->seq && nla_put_flag(msg, NL80211_WOWLAN_TCP_DATA_PAYLOAD_SEQ))
969 return -ENOBUFS;
970
971 if (tcp->tok && nla_put(msg, NL80211_WOWLAN_TCP_DATA_PAYLOAD_TOKEN,
972 sizeof(*tcp->tok), tcp->tok))
973 return -ENOBUFS;
974
975 if (nla_put_u32(msg, NL80211_WOWLAN_TCP_DATA_INTERVAL,
976 tcp->data_interval_max))
977 return -ENOBUFS;
978
979 if (nla_put_u32(msg, NL80211_WOWLAN_TCP_WAKE_PAYLOAD,
980 tcp->wake_payload_max))
981 return -ENOBUFS;
982
983 nla_nest_end(msg, nl_tcp);
984 return 0;
985}
986
Johannes Berg3713b4e2013-02-14 16:19:38 +0100987static int nl80211_send_wowlan(struct sk_buff *msg,
Johannes Bergb56cf722013-02-20 01:02:38 +0100988 struct cfg80211_registered_device *dev,
989 bool large)
Johannes Berg3713b4e2013-02-14 16:19:38 +0100990{
991 struct nlattr *nl_wowlan;
992
993 if (!dev->wiphy.wowlan.flags && !dev->wiphy.wowlan.n_patterns)
994 return 0;
995
996 nl_wowlan = nla_nest_start(msg, NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED);
997 if (!nl_wowlan)
998 return -ENOBUFS;
999
1000 if (((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_ANY) &&
1001 nla_put_flag(msg, NL80211_WOWLAN_TRIG_ANY)) ||
1002 ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_DISCONNECT) &&
1003 nla_put_flag(msg, NL80211_WOWLAN_TRIG_DISCONNECT)) ||
1004 ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_MAGIC_PKT) &&
1005 nla_put_flag(msg, NL80211_WOWLAN_TRIG_MAGIC_PKT)) ||
1006 ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_SUPPORTS_GTK_REKEY) &&
1007 nla_put_flag(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_SUPPORTED)) ||
1008 ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_GTK_REKEY_FAILURE) &&
1009 nla_put_flag(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE)) ||
1010 ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_EAP_IDENTITY_REQ) &&
1011 nla_put_flag(msg, NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST)) ||
1012 ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_4WAY_HANDSHAKE) &&
1013 nla_put_flag(msg, NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE)) ||
1014 ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_RFKILL_RELEASE) &&
1015 nla_put_flag(msg, NL80211_WOWLAN_TRIG_RFKILL_RELEASE)))
1016 return -ENOBUFS;
1017
1018 if (dev->wiphy.wowlan.n_patterns) {
1019 struct nl80211_wowlan_pattern_support pat = {
1020 .max_patterns = dev->wiphy.wowlan.n_patterns,
1021 .min_pattern_len = dev->wiphy.wowlan.pattern_min_len,
1022 .max_pattern_len = dev->wiphy.wowlan.pattern_max_len,
1023 .max_pkt_offset = dev->wiphy.wowlan.max_pkt_offset,
1024 };
1025
1026 if (nla_put(msg, NL80211_WOWLAN_TRIG_PKT_PATTERN,
1027 sizeof(pat), &pat))
1028 return -ENOBUFS;
1029 }
1030
Johannes Bergb56cf722013-02-20 01:02:38 +01001031 if (large && nl80211_send_wowlan_tcp_caps(dev, msg))
1032 return -ENOBUFS;
1033
Johannes Berg3713b4e2013-02-14 16:19:38 +01001034 nla_nest_end(msg, nl_wowlan);
1035
1036 return 0;
1037}
1038#endif
1039
1040static int nl80211_send_band_rateinfo(struct sk_buff *msg,
1041 struct ieee80211_supported_band *sband)
1042{
1043 struct nlattr *nl_rates, *nl_rate;
1044 struct ieee80211_rate *rate;
1045 int i;
1046
1047 /* add HT info */
1048 if (sband->ht_cap.ht_supported &&
1049 (nla_put(msg, NL80211_BAND_ATTR_HT_MCS_SET,
1050 sizeof(sband->ht_cap.mcs),
1051 &sband->ht_cap.mcs) ||
1052 nla_put_u16(msg, NL80211_BAND_ATTR_HT_CAPA,
1053 sband->ht_cap.cap) ||
1054 nla_put_u8(msg, NL80211_BAND_ATTR_HT_AMPDU_FACTOR,
1055 sband->ht_cap.ampdu_factor) ||
1056 nla_put_u8(msg, NL80211_BAND_ATTR_HT_AMPDU_DENSITY,
1057 sband->ht_cap.ampdu_density)))
1058 return -ENOBUFS;
1059
1060 /* add VHT info */
1061 if (sband->vht_cap.vht_supported &&
1062 (nla_put(msg, NL80211_BAND_ATTR_VHT_MCS_SET,
1063 sizeof(sband->vht_cap.vht_mcs),
1064 &sband->vht_cap.vht_mcs) ||
1065 nla_put_u32(msg, NL80211_BAND_ATTR_VHT_CAPA,
1066 sband->vht_cap.cap)))
1067 return -ENOBUFS;
1068
1069 /* add bitrates */
1070 nl_rates = nla_nest_start(msg, NL80211_BAND_ATTR_RATES);
1071 if (!nl_rates)
1072 return -ENOBUFS;
1073
1074 for (i = 0; i < sband->n_bitrates; i++) {
1075 nl_rate = nla_nest_start(msg, i);
1076 if (!nl_rate)
1077 return -ENOBUFS;
1078
1079 rate = &sband->bitrates[i];
1080 if (nla_put_u32(msg, NL80211_BITRATE_ATTR_RATE,
1081 rate->bitrate))
1082 return -ENOBUFS;
1083 if ((rate->flags & IEEE80211_RATE_SHORT_PREAMBLE) &&
1084 nla_put_flag(msg,
1085 NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE))
1086 return -ENOBUFS;
1087
1088 nla_nest_end(msg, nl_rate);
1089 }
1090
1091 nla_nest_end(msg, nl_rates);
1092
1093 return 0;
1094}
1095
1096static int
1097nl80211_send_mgmt_stypes(struct sk_buff *msg,
1098 const struct ieee80211_txrx_stypes *mgmt_stypes)
1099{
1100 u16 stypes;
1101 struct nlattr *nl_ftypes, *nl_ifs;
1102 enum nl80211_iftype ift;
1103 int i;
1104
1105 if (!mgmt_stypes)
1106 return 0;
1107
1108 nl_ifs = nla_nest_start(msg, NL80211_ATTR_TX_FRAME_TYPES);
1109 if (!nl_ifs)
1110 return -ENOBUFS;
1111
1112 for (ift = 0; ift < NUM_NL80211_IFTYPES; ift++) {
1113 nl_ftypes = nla_nest_start(msg, ift);
1114 if (!nl_ftypes)
1115 return -ENOBUFS;
1116 i = 0;
1117 stypes = mgmt_stypes[ift].tx;
1118 while (stypes) {
1119 if ((stypes & 1) &&
1120 nla_put_u16(msg, NL80211_ATTR_FRAME_TYPE,
1121 (i << 4) | IEEE80211_FTYPE_MGMT))
1122 return -ENOBUFS;
1123 stypes >>= 1;
1124 i++;
1125 }
1126 nla_nest_end(msg, nl_ftypes);
1127 }
1128
1129 nla_nest_end(msg, nl_ifs);
1130
1131 nl_ifs = nla_nest_start(msg, NL80211_ATTR_RX_FRAME_TYPES);
1132 if (!nl_ifs)
1133 return -ENOBUFS;
1134
1135 for (ift = 0; ift < NUM_NL80211_IFTYPES; ift++) {
1136 nl_ftypes = nla_nest_start(msg, ift);
1137 if (!nl_ftypes)
1138 return -ENOBUFS;
1139 i = 0;
1140 stypes = mgmt_stypes[ift].rx;
1141 while (stypes) {
1142 if ((stypes & 1) &&
1143 nla_put_u16(msg, NL80211_ATTR_FRAME_TYPE,
1144 (i << 4) | IEEE80211_FTYPE_MGMT))
1145 return -ENOBUFS;
1146 stypes >>= 1;
1147 i++;
1148 }
1149 nla_nest_end(msg, nl_ftypes);
1150 }
1151 nla_nest_end(msg, nl_ifs);
1152
1153 return 0;
1154}
1155
1156static int nl80211_send_wiphy(struct cfg80211_registered_device *dev,
1157 struct sk_buff *msg, u32 portid, u32 seq,
1158 int flags, bool split, long *split_start,
1159 long *band_start, long *chan_start)
Johannes Berg55682962007-09-20 13:09:35 -04001160{
1161 void *hdr;
Johannes Bergee688b002008-01-24 19:38:39 +01001162 struct nlattr *nl_bands, *nl_band;
1163 struct nlattr *nl_freqs, *nl_freq;
Johannes Berg8fdc6212009-03-14 09:34:01 +01001164 struct nlattr *nl_cmds;
Johannes Bergee688b002008-01-24 19:38:39 +01001165 enum ieee80211_band band;
1166 struct ieee80211_channel *chan;
Johannes Bergee688b002008-01-24 19:38:39 +01001167 int i;
Johannes Berg2e161f72010-08-12 15:38:38 +02001168 const struct ieee80211_txrx_stypes *mgmt_stypes =
1169 dev->wiphy.mgmt_stypes;
Johannes Berg3713b4e2013-02-14 16:19:38 +01001170 long start = 0, start_chan = 0, start_band = 0;
Johannes Bergfe1abaf2013-02-27 15:39:45 +01001171 u32 features;
Johannes Berg55682962007-09-20 13:09:35 -04001172
Eric W. Biederman15e47302012-09-07 20:12:54 +00001173 hdr = nl80211hdr_put(msg, portid, seq, flags, NL80211_CMD_NEW_WIPHY);
Johannes Berg55682962007-09-20 13:09:35 -04001174 if (!hdr)
Johannes Berg3713b4e2013-02-14 16:19:38 +01001175 return -ENOBUFS;
1176
1177 /* allow always using the variables */
1178 if (!split) {
1179 split_start = &start;
1180 band_start = &start_band;
1181 chan_start = &start_chan;
1182 }
Johannes Berg55682962007-09-20 13:09:35 -04001183
David S. Miller9360ffd2012-03-29 04:41:26 -04001184 if (nla_put_u32(msg, NL80211_ATTR_WIPHY, dev->wiphy_idx) ||
Johannes Berg3713b4e2013-02-14 16:19:38 +01001185 nla_put_string(msg, NL80211_ATTR_WIPHY_NAME,
1186 wiphy_name(&dev->wiphy)) ||
David S. Miller9360ffd2012-03-29 04:41:26 -04001187 nla_put_u32(msg, NL80211_ATTR_GENERATION,
Johannes Berg3713b4e2013-02-14 16:19:38 +01001188 cfg80211_rdev_list_generation))
David S. Miller9360ffd2012-03-29 04:41:26 -04001189 goto nla_put_failure;
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +02001190
Johannes Berg3713b4e2013-02-14 16:19:38 +01001191 switch (*split_start) {
1192 case 0:
1193 if (nla_put_u8(msg, NL80211_ATTR_WIPHY_RETRY_SHORT,
1194 dev->wiphy.retry_short) ||
1195 nla_put_u8(msg, NL80211_ATTR_WIPHY_RETRY_LONG,
1196 dev->wiphy.retry_long) ||
1197 nla_put_u32(msg, NL80211_ATTR_WIPHY_FRAG_THRESHOLD,
1198 dev->wiphy.frag_threshold) ||
1199 nla_put_u32(msg, NL80211_ATTR_WIPHY_RTS_THRESHOLD,
1200 dev->wiphy.rts_threshold) ||
1201 nla_put_u8(msg, NL80211_ATTR_WIPHY_COVERAGE_CLASS,
1202 dev->wiphy.coverage_class) ||
1203 nla_put_u8(msg, NL80211_ATTR_MAX_NUM_SCAN_SSIDS,
1204 dev->wiphy.max_scan_ssids) ||
1205 nla_put_u8(msg, NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS,
1206 dev->wiphy.max_sched_scan_ssids) ||
1207 nla_put_u16(msg, NL80211_ATTR_MAX_SCAN_IE_LEN,
1208 dev->wiphy.max_scan_ie_len) ||
1209 nla_put_u16(msg, NL80211_ATTR_MAX_SCHED_SCAN_IE_LEN,
1210 dev->wiphy.max_sched_scan_ie_len) ||
1211 nla_put_u8(msg, NL80211_ATTR_MAX_MATCH_SETS,
1212 dev->wiphy.max_match_sets))
Johannes Bergee688b002008-01-24 19:38:39 +01001213 goto nla_put_failure;
1214
Johannes Berg3713b4e2013-02-14 16:19:38 +01001215 if ((dev->wiphy.flags & WIPHY_FLAG_IBSS_RSN) &&
1216 nla_put_flag(msg, NL80211_ATTR_SUPPORT_IBSS_RSN))
1217 goto nla_put_failure;
1218 if ((dev->wiphy.flags & WIPHY_FLAG_MESH_AUTH) &&
1219 nla_put_flag(msg, NL80211_ATTR_SUPPORT_MESH_AUTH))
1220 goto nla_put_failure;
1221 if ((dev->wiphy.flags & WIPHY_FLAG_AP_UAPSD) &&
1222 nla_put_flag(msg, NL80211_ATTR_SUPPORT_AP_UAPSD))
1223 goto nla_put_failure;
1224 if ((dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_FW_ROAM) &&
1225 nla_put_flag(msg, NL80211_ATTR_ROAM_SUPPORT))
1226 goto nla_put_failure;
1227 if ((dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) &&
1228 nla_put_flag(msg, NL80211_ATTR_TDLS_SUPPORT))
1229 goto nla_put_failure;
1230 if ((dev->wiphy.flags & WIPHY_FLAG_TDLS_EXTERNAL_SETUP) &&
1231 nla_put_flag(msg, NL80211_ATTR_TDLS_EXTERNAL_SETUP))
David S. Miller9360ffd2012-03-29 04:41:26 -04001232 goto nla_put_failure;
Johannes Bergd51626d2008-10-09 12:20:13 +02001233
Johannes Berg3713b4e2013-02-14 16:19:38 +01001234 (*split_start)++;
1235 if (split)
1236 break;
1237 case 1:
1238 if (nla_put(msg, NL80211_ATTR_CIPHER_SUITES,
1239 sizeof(u32) * dev->wiphy.n_cipher_suites,
1240 dev->wiphy.cipher_suites))
Mahesh Palivelabf0c111e2012-06-22 07:27:46 +00001241 goto nla_put_failure;
1242
Johannes Berg3713b4e2013-02-14 16:19:38 +01001243 if (nla_put_u8(msg, NL80211_ATTR_MAX_NUM_PMKIDS,
1244 dev->wiphy.max_num_pmkids))
Johannes Bergee688b002008-01-24 19:38:39 +01001245 goto nla_put_failure;
1246
Johannes Berg3713b4e2013-02-14 16:19:38 +01001247 if ((dev->wiphy.flags & WIPHY_FLAG_CONTROL_PORT_PROTOCOL) &&
1248 nla_put_flag(msg, NL80211_ATTR_CONTROL_PORT_ETHERTYPE))
1249 goto nla_put_failure;
Johannes Bergee688b002008-01-24 19:38:39 +01001250
Johannes Berg3713b4e2013-02-14 16:19:38 +01001251 if (nla_put_u32(msg, NL80211_ATTR_WIPHY_ANTENNA_AVAIL_TX,
1252 dev->wiphy.available_antennas_tx) ||
1253 nla_put_u32(msg, NL80211_ATTR_WIPHY_ANTENNA_AVAIL_RX,
1254 dev->wiphy.available_antennas_rx))
1255 goto nla_put_failure;
Johannes Bergee688b002008-01-24 19:38:39 +01001256
Johannes Berg3713b4e2013-02-14 16:19:38 +01001257 if ((dev->wiphy.flags & WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD) &&
1258 nla_put_u32(msg, NL80211_ATTR_PROBE_RESP_OFFLOAD,
1259 dev->wiphy.probe_resp_offload))
1260 goto nla_put_failure;
Jouni Malinene2f367f262008-11-21 19:01:30 +02001261
Johannes Berg3713b4e2013-02-14 16:19:38 +01001262 if ((dev->wiphy.available_antennas_tx ||
1263 dev->wiphy.available_antennas_rx) &&
1264 dev->ops->get_antenna) {
1265 u32 tx_ant = 0, rx_ant = 0;
1266 int res;
1267 res = rdev_get_antenna(dev, &tx_ant, &rx_ant);
1268 if (!res) {
1269 if (nla_put_u32(msg,
1270 NL80211_ATTR_WIPHY_ANTENNA_TX,
1271 tx_ant) ||
1272 nla_put_u32(msg,
1273 NL80211_ATTR_WIPHY_ANTENNA_RX,
1274 rx_ant))
1275 goto nla_put_failure;
1276 }
Johannes Bergee688b002008-01-24 19:38:39 +01001277 }
1278
Johannes Berg3713b4e2013-02-14 16:19:38 +01001279 (*split_start)++;
1280 if (split)
1281 break;
1282 case 2:
1283 if (nl80211_put_iftypes(msg, NL80211_ATTR_SUPPORTED_IFTYPES,
1284 dev->wiphy.interface_modes))
1285 goto nla_put_failure;
1286 (*split_start)++;
1287 if (split)
1288 break;
1289 case 3:
1290 nl_bands = nla_nest_start(msg, NL80211_ATTR_WIPHY_BANDS);
1291 if (!nl_bands)
Johannes Bergee688b002008-01-24 19:38:39 +01001292 goto nla_put_failure;
1293
Johannes Berg3713b4e2013-02-14 16:19:38 +01001294 for (band = *band_start; band < IEEE80211_NUM_BANDS; band++) {
1295 struct ieee80211_supported_band *sband;
1296
1297 sband = dev->wiphy.bands[band];
1298
1299 if (!sband)
1300 continue;
1301
1302 nl_band = nla_nest_start(msg, band);
1303 if (!nl_band)
Johannes Bergee688b002008-01-24 19:38:39 +01001304 goto nla_put_failure;
1305
Johannes Berg3713b4e2013-02-14 16:19:38 +01001306 switch (*chan_start) {
1307 case 0:
1308 if (nl80211_send_band_rateinfo(msg, sband))
1309 goto nla_put_failure;
1310 (*chan_start)++;
1311 if (split)
1312 break;
1313 default:
1314 /* add frequencies */
1315 nl_freqs = nla_nest_start(
1316 msg, NL80211_BAND_ATTR_FREQS);
1317 if (!nl_freqs)
1318 goto nla_put_failure;
Johannes Bergee688b002008-01-24 19:38:39 +01001319
Johannes Berg3713b4e2013-02-14 16:19:38 +01001320 for (i = *chan_start - 1;
1321 i < sband->n_channels;
1322 i++) {
1323 nl_freq = nla_nest_start(msg, i);
1324 if (!nl_freq)
1325 goto nla_put_failure;
1326
1327 chan = &sband->channels[i];
1328
Johannes Bergcdc89b92013-02-18 23:54:36 +01001329 if (nl80211_msg_put_channel(msg, chan,
1330 split))
Johannes Berg3713b4e2013-02-14 16:19:38 +01001331 goto nla_put_failure;
1332
1333 nla_nest_end(msg, nl_freq);
1334 if (split)
1335 break;
1336 }
1337 if (i < sband->n_channels)
1338 *chan_start = i + 2;
1339 else
1340 *chan_start = 0;
1341 nla_nest_end(msg, nl_freqs);
1342 }
1343
1344 nla_nest_end(msg, nl_band);
1345
1346 if (split) {
1347 /* start again here */
1348 if (*chan_start)
1349 band--;
1350 break;
1351 }
Johannes Bergee688b002008-01-24 19:38:39 +01001352 }
Johannes Berg3713b4e2013-02-14 16:19:38 +01001353 nla_nest_end(msg, nl_bands);
Johannes Bergee688b002008-01-24 19:38:39 +01001354
Johannes Berg3713b4e2013-02-14 16:19:38 +01001355 if (band < IEEE80211_NUM_BANDS)
1356 *band_start = band + 1;
1357 else
1358 *band_start = 0;
Johannes Bergee688b002008-01-24 19:38:39 +01001359
Johannes Berg3713b4e2013-02-14 16:19:38 +01001360 /* if bands & channels are done, continue outside */
1361 if (*band_start == 0 && *chan_start == 0)
1362 (*split_start)++;
1363 if (split)
1364 break;
1365 case 4:
1366 nl_cmds = nla_nest_start(msg, NL80211_ATTR_SUPPORTED_COMMANDS);
1367 if (!nl_cmds)
David S. Miller9360ffd2012-03-29 04:41:26 -04001368 goto nla_put_failure;
Johannes Berg3713b4e2013-02-14 16:19:38 +01001369
1370 i = 0;
1371#define CMD(op, n) \
1372 do { \
1373 if (dev->ops->op) { \
1374 i++; \
1375 if (nla_put_u32(msg, i, NL80211_CMD_ ## n)) \
1376 goto nla_put_failure; \
1377 } \
1378 } while (0)
1379
1380 CMD(add_virtual_intf, NEW_INTERFACE);
1381 CMD(change_virtual_intf, SET_INTERFACE);
1382 CMD(add_key, NEW_KEY);
1383 CMD(start_ap, START_AP);
1384 CMD(add_station, NEW_STATION);
1385 CMD(add_mpath, NEW_MPATH);
1386 CMD(update_mesh_config, SET_MESH_CONFIG);
1387 CMD(change_bss, SET_BSS);
1388 CMD(auth, AUTHENTICATE);
1389 CMD(assoc, ASSOCIATE);
1390 CMD(deauth, DEAUTHENTICATE);
1391 CMD(disassoc, DISASSOCIATE);
1392 CMD(join_ibss, JOIN_IBSS);
1393 CMD(join_mesh, JOIN_MESH);
1394 CMD(set_pmksa, SET_PMKSA);
1395 CMD(del_pmksa, DEL_PMKSA);
1396 CMD(flush_pmksa, FLUSH_PMKSA);
1397 if (dev->wiphy.flags & WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL)
1398 CMD(remain_on_channel, REMAIN_ON_CHANNEL);
1399 CMD(set_bitrate_mask, SET_TX_BITRATE_MASK);
1400 CMD(mgmt_tx, FRAME);
1401 CMD(mgmt_tx_cancel_wait, FRAME_WAIT_CANCEL);
1402 if (dev->wiphy.flags & WIPHY_FLAG_NETNS_OK) {
1403 i++;
1404 if (nla_put_u32(msg, i, NL80211_CMD_SET_WIPHY_NETNS))
1405 goto nla_put_failure;
1406 }
1407 if (dev->ops->set_monitor_channel || dev->ops->start_ap ||
1408 dev->ops->join_mesh) {
1409 i++;
1410 if (nla_put_u32(msg, i, NL80211_CMD_SET_CHANNEL))
1411 goto nla_put_failure;
1412 }
1413 CMD(set_wds_peer, SET_WDS_PEER);
1414 if (dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) {
1415 CMD(tdls_mgmt, TDLS_MGMT);
1416 CMD(tdls_oper, TDLS_OPER);
1417 }
1418 if (dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN)
1419 CMD(sched_scan_start, START_SCHED_SCAN);
1420 CMD(probe_client, PROBE_CLIENT);
1421 CMD(set_noack_map, SET_NOACK_MAP);
1422 if (dev->wiphy.flags & WIPHY_FLAG_REPORTS_OBSS) {
1423 i++;
1424 if (nla_put_u32(msg, i, NL80211_CMD_REGISTER_BEACONS))
1425 goto nla_put_failure;
1426 }
1427 CMD(start_p2p_device, START_P2P_DEVICE);
1428 CMD(set_mcast_rate, SET_MCAST_RATE);
Arend van Spriel5de17982013-04-18 15:49:00 +02001429 if (split) {
1430 CMD(crit_proto_start, CRIT_PROTOCOL_START);
1431 CMD(crit_proto_stop, CRIT_PROTOCOL_STOP);
1432 }
Johannes Berg8fdc6212009-03-14 09:34:01 +01001433
Kalle Valo4745fc02011-11-17 19:06:10 +02001434#ifdef CONFIG_NL80211_TESTMODE
Johannes Berg3713b4e2013-02-14 16:19:38 +01001435 CMD(testmode_cmd, TESTMODE);
Kalle Valo4745fc02011-11-17 19:06:10 +02001436#endif
1437
Johannes Berg8fdc6212009-03-14 09:34:01 +01001438#undef CMD
Samuel Ortizb23aa672009-07-01 21:26:54 +02001439
Johannes Berg3713b4e2013-02-14 16:19:38 +01001440 if (dev->ops->connect || dev->ops->auth) {
1441 i++;
1442 if (nla_put_u32(msg, i, NL80211_CMD_CONNECT))
Johannes Berg2e161f72010-08-12 15:38:38 +02001443 goto nla_put_failure;
Johannes Berg2e161f72010-08-12 15:38:38 +02001444 }
1445
Johannes Berg3713b4e2013-02-14 16:19:38 +01001446 if (dev->ops->disconnect || dev->ops->deauth) {
1447 i++;
1448 if (nla_put_u32(msg, i, NL80211_CMD_DISCONNECT))
1449 goto nla_put_failure;
1450 }
Johannes Berg74b70a42010-08-24 12:15:53 +02001451
Johannes Berg3713b4e2013-02-14 16:19:38 +01001452 nla_nest_end(msg, nl_cmds);
1453 (*split_start)++;
1454 if (split)
1455 break;
1456 case 5:
1457 if (dev->ops->remain_on_channel &&
1458 (dev->wiphy.flags & WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL) &&
1459 nla_put_u32(msg,
1460 NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION,
1461 dev->wiphy.max_remain_on_channel_duration))
Johannes Berg2e161f72010-08-12 15:38:38 +02001462 goto nla_put_failure;
1463
Johannes Berg3713b4e2013-02-14 16:19:38 +01001464 if ((dev->wiphy.flags & WIPHY_FLAG_OFFCHAN_TX) &&
1465 nla_put_flag(msg, NL80211_ATTR_OFFCHANNEL_TX_OK))
1466 goto nla_put_failure;
Johannes Berg2e161f72010-08-12 15:38:38 +02001467
Johannes Berg3713b4e2013-02-14 16:19:38 +01001468 if (nl80211_send_mgmt_stypes(msg, mgmt_stypes))
1469 goto nla_put_failure;
1470 (*split_start)++;
1471 if (split)
1472 break;
1473 case 6:
Johannes Bergdfb89c52012-06-27 09:23:48 +02001474#ifdef CONFIG_PM
Johannes Bergb56cf722013-02-20 01:02:38 +01001475 if (nl80211_send_wowlan(msg, dev, split))
Johannes Berg3713b4e2013-02-14 16:19:38 +01001476 goto nla_put_failure;
1477 (*split_start)++;
1478 if (split)
1479 break;
1480#else
1481 (*split_start)++;
1482#endif
1483 case 7:
1484 if (nl80211_put_iftypes(msg, NL80211_ATTR_SOFTWARE_IFTYPES,
1485 dev->wiphy.software_iftypes))
Johannes Bergff1b6e62011-05-04 15:37:28 +02001486 goto nla_put_failure;
1487
Johannes Bergcdc89b92013-02-18 23:54:36 +01001488 if (nl80211_put_iface_combinations(&dev->wiphy, msg, split))
Johannes Berg3713b4e2013-02-14 16:19:38 +01001489 goto nla_put_failure;
Johannes Bergff1b6e62011-05-04 15:37:28 +02001490
Johannes Berg3713b4e2013-02-14 16:19:38 +01001491 (*split_start)++;
1492 if (split)
1493 break;
1494 case 8:
1495 if ((dev->wiphy.flags & WIPHY_FLAG_HAVE_AP_SME) &&
1496 nla_put_u32(msg, NL80211_ATTR_DEVICE_AP_SME,
1497 dev->wiphy.ap_sme_capa))
1498 goto nla_put_failure;
1499
Johannes Bergfe1abaf2013-02-27 15:39:45 +01001500 features = dev->wiphy.features;
1501 /*
1502 * We can only add the per-channel limit information if the
1503 * dump is split, otherwise it makes it too big. Therefore
1504 * only advertise it in that case.
1505 */
1506 if (split)
1507 features |= NL80211_FEATURE_ADVERTISE_CHAN_LIMITS;
1508 if (nla_put_u32(msg, NL80211_ATTR_FEATURE_FLAGS, features))
Johannes Berg3713b4e2013-02-14 16:19:38 +01001509 goto nla_put_failure;
1510
1511 if (dev->wiphy.ht_capa_mod_mask &&
1512 nla_put(msg, NL80211_ATTR_HT_CAPABILITY_MASK,
1513 sizeof(*dev->wiphy.ht_capa_mod_mask),
1514 dev->wiphy.ht_capa_mod_mask))
1515 goto nla_put_failure;
1516
1517 if (dev->wiphy.flags & WIPHY_FLAG_HAVE_AP_SME &&
1518 dev->wiphy.max_acl_mac_addrs &&
1519 nla_put_u32(msg, NL80211_ATTR_MAC_ACL_MAX,
1520 dev->wiphy.max_acl_mac_addrs))
1521 goto nla_put_failure;
1522
1523 /*
1524 * Any information below this point is only available to
1525 * applications that can deal with it being split. This
1526 * helps ensure that newly added capabilities don't break
1527 * older tools by overrunning their buffers.
1528 *
1529 * We still increment split_start so that in the split
1530 * case we'll continue with more data in the next round,
1531 * but break unconditionally so unsplit data stops here.
1532 */
1533 (*split_start)++;
1534 break;
1535 case 9:
Johannes Bergfe1abaf2013-02-27 15:39:45 +01001536 if (dev->wiphy.extended_capabilities &&
1537 (nla_put(msg, NL80211_ATTR_EXT_CAPA,
1538 dev->wiphy.extended_capabilities_len,
1539 dev->wiphy.extended_capabilities) ||
1540 nla_put(msg, NL80211_ATTR_EXT_CAPA_MASK,
1541 dev->wiphy.extended_capabilities_len,
1542 dev->wiphy.extended_capabilities_mask)))
1543 goto nla_put_failure;
Johannes Berg3713b4e2013-02-14 16:19:38 +01001544
Johannes Bergee2aca32013-02-21 17:36:01 +01001545 if (dev->wiphy.vht_capa_mod_mask &&
1546 nla_put(msg, NL80211_ATTR_VHT_CAPABILITY_MASK,
1547 sizeof(*dev->wiphy.vht_capa_mod_mask),
1548 dev->wiphy.vht_capa_mod_mask))
1549 goto nla_put_failure;
1550
Johannes Berg3713b4e2013-02-14 16:19:38 +01001551 /* done */
1552 *split_start = 0;
1553 break;
Johannes Bergff1b6e62011-05-04 15:37:28 +02001554 }
Johannes Berg55682962007-09-20 13:09:35 -04001555 return genlmsg_end(msg, hdr);
1556
1557 nla_put_failure:
Thomas Grafbc3ed282008-06-03 16:36:54 -07001558 genlmsg_cancel(msg, hdr);
1559 return -EMSGSIZE;
Johannes Berg55682962007-09-20 13:09:35 -04001560}
1561
1562static int nl80211_dump_wiphy(struct sk_buff *skb, struct netlink_callback *cb)
1563{
Johannes Berg645e77d2013-03-01 14:03:49 +01001564 int idx = 0, ret;
Johannes Berg55682962007-09-20 13:09:35 -04001565 int start = cb->args[0];
1566 struct cfg80211_registered_device *dev;
Johannes Berg3713b4e2013-02-14 16:19:38 +01001567 s64 filter_wiphy = -1;
1568 bool split = false;
Johannes Berg3a5a4232013-06-19 10:09:57 +02001569 struct nlattr **tb;
Johannes Berg3713b4e2013-02-14 16:19:38 +01001570 int res;
Johannes Berg55682962007-09-20 13:09:35 -04001571
Johannes Berg3a5a4232013-06-19 10:09:57 +02001572 /* will be zeroed in nlmsg_parse() */
1573 tb = kmalloc(sizeof(*tb) * (NL80211_ATTR_MAX + 1), GFP_KERNEL);
1574 if (!tb)
1575 return -ENOMEM;
1576
Luis R. Rodrigueza1794392009-02-21 00:04:21 -05001577 mutex_lock(&cfg80211_mutex);
Johannes Berg3713b4e2013-02-14 16:19:38 +01001578 res = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize,
Johannes Berg3a5a4232013-06-19 10:09:57 +02001579 tb, NL80211_ATTR_MAX, nl80211_policy);
Johannes Berg3713b4e2013-02-14 16:19:38 +01001580 if (res == 0) {
1581 split = tb[NL80211_ATTR_SPLIT_WIPHY_DUMP];
1582 if (tb[NL80211_ATTR_WIPHY])
1583 filter_wiphy = nla_get_u32(tb[NL80211_ATTR_WIPHY]);
1584 if (tb[NL80211_ATTR_WDEV])
1585 filter_wiphy = nla_get_u64(tb[NL80211_ATTR_WDEV]) >> 32;
1586 if (tb[NL80211_ATTR_IFINDEX]) {
1587 struct net_device *netdev;
1588 int ifidx = nla_get_u32(tb[NL80211_ATTR_IFINDEX]);
1589
1590 netdev = dev_get_by_index(sock_net(skb->sk), ifidx);
1591 if (!netdev) {
1592 mutex_unlock(&cfg80211_mutex);
Johannes Berg3a5a4232013-06-19 10:09:57 +02001593 kfree(tb);
Johannes Berg3713b4e2013-02-14 16:19:38 +01001594 return -ENODEV;
1595 }
1596 if (netdev->ieee80211_ptr) {
1597 dev = wiphy_to_dev(
1598 netdev->ieee80211_ptr->wiphy);
1599 filter_wiphy = dev->wiphy_idx;
1600 }
1601 dev_put(netdev);
1602 }
1603 }
Johannes Berg3a5a4232013-06-19 10:09:57 +02001604 kfree(tb);
Johannes Berg3713b4e2013-02-14 16:19:38 +01001605
Johannes Berg79c97e92009-07-07 03:56:12 +02001606 list_for_each_entry(dev, &cfg80211_rdev_list, list) {
Johannes Berg463d0182009-07-14 00:33:35 +02001607 if (!net_eq(wiphy_net(&dev->wiphy), sock_net(skb->sk)))
1608 continue;
Julius Volzb4637272008-07-08 14:02:19 +02001609 if (++idx <= start)
Johannes Berg55682962007-09-20 13:09:35 -04001610 continue;
Johannes Berg3713b4e2013-02-14 16:19:38 +01001611 if (filter_wiphy != -1 && dev->wiphy_idx != filter_wiphy)
1612 continue;
1613 /* attempt to fit multiple wiphy data chunks into the skb */
1614 do {
1615 ret = nl80211_send_wiphy(dev, skb,
1616 NETLINK_CB(cb->skb).portid,
1617 cb->nlh->nlmsg_seq,
1618 NLM_F_MULTI,
1619 split, &cb->args[1],
1620 &cb->args[2],
1621 &cb->args[3]);
1622 if (ret < 0) {
1623 /*
1624 * If sending the wiphy data didn't fit (ENOBUFS
1625 * or EMSGSIZE returned), this SKB is still
1626 * empty (so it's not too big because another
1627 * wiphy dataset is already in the skb) and
1628 * we've not tried to adjust the dump allocation
1629 * yet ... then adjust the alloc size to be
1630 * bigger, and return 1 but with the empty skb.
1631 * This results in an empty message being RX'ed
1632 * in userspace, but that is ignored.
1633 *
1634 * We can then retry with the larger buffer.
1635 */
1636 if ((ret == -ENOBUFS || ret == -EMSGSIZE) &&
1637 !skb->len &&
1638 cb->min_dump_alloc < 4096) {
1639 cb->min_dump_alloc = 4096;
1640 mutex_unlock(&cfg80211_mutex);
1641 return 1;
1642 }
1643 idx--;
1644 break;
Johannes Berg645e77d2013-03-01 14:03:49 +01001645 }
Johannes Berg3713b4e2013-02-14 16:19:38 +01001646 } while (cb->args[1] > 0);
1647 break;
Johannes Berg55682962007-09-20 13:09:35 -04001648 }
Luis R. Rodrigueza1794392009-02-21 00:04:21 -05001649 mutex_unlock(&cfg80211_mutex);
Johannes Berg55682962007-09-20 13:09:35 -04001650
1651 cb->args[0] = idx;
1652
1653 return skb->len;
1654}
1655
1656static int nl80211_get_wiphy(struct sk_buff *skb, struct genl_info *info)
1657{
1658 struct sk_buff *msg;
Johannes Berg4c476992010-10-04 21:36:35 +02001659 struct cfg80211_registered_device *dev = info->user_ptr[0];
Johannes Berg55682962007-09-20 13:09:35 -04001660
Johannes Berg645e77d2013-03-01 14:03:49 +01001661 msg = nlmsg_new(4096, GFP_KERNEL);
Johannes Berg55682962007-09-20 13:09:35 -04001662 if (!msg)
Johannes Berg4c476992010-10-04 21:36:35 +02001663 return -ENOMEM;
Johannes Berg55682962007-09-20 13:09:35 -04001664
Johannes Berg3713b4e2013-02-14 16:19:38 +01001665 if (nl80211_send_wiphy(dev, msg, info->snd_portid, info->snd_seq, 0,
1666 false, NULL, NULL, NULL) < 0) {
Johannes Berg4c476992010-10-04 21:36:35 +02001667 nlmsg_free(msg);
1668 return -ENOBUFS;
1669 }
Johannes Berg55682962007-09-20 13:09:35 -04001670
Johannes Berg134e6372009-07-10 09:51:34 +00001671 return genlmsg_reply(msg, info);
Johannes Berg55682962007-09-20 13:09:35 -04001672}
1673
Jouni Malinen31888482008-10-30 16:59:24 +02001674static const struct nla_policy txq_params_policy[NL80211_TXQ_ATTR_MAX + 1] = {
1675 [NL80211_TXQ_ATTR_QUEUE] = { .type = NLA_U8 },
1676 [NL80211_TXQ_ATTR_TXOP] = { .type = NLA_U16 },
1677 [NL80211_TXQ_ATTR_CWMIN] = { .type = NLA_U16 },
1678 [NL80211_TXQ_ATTR_CWMAX] = { .type = NLA_U16 },
1679 [NL80211_TXQ_ATTR_AIFS] = { .type = NLA_U8 },
1680};
1681
1682static int parse_txq_params(struct nlattr *tb[],
1683 struct ieee80211_txq_params *txq_params)
1684{
Johannes Berga3304b02012-03-28 11:04:24 +02001685 if (!tb[NL80211_TXQ_ATTR_AC] || !tb[NL80211_TXQ_ATTR_TXOP] ||
Jouni Malinen31888482008-10-30 16:59:24 +02001686 !tb[NL80211_TXQ_ATTR_CWMIN] || !tb[NL80211_TXQ_ATTR_CWMAX] ||
1687 !tb[NL80211_TXQ_ATTR_AIFS])
1688 return -EINVAL;
1689
Johannes Berga3304b02012-03-28 11:04:24 +02001690 txq_params->ac = nla_get_u8(tb[NL80211_TXQ_ATTR_AC]);
Jouni Malinen31888482008-10-30 16:59:24 +02001691 txq_params->txop = nla_get_u16(tb[NL80211_TXQ_ATTR_TXOP]);
1692 txq_params->cwmin = nla_get_u16(tb[NL80211_TXQ_ATTR_CWMIN]);
1693 txq_params->cwmax = nla_get_u16(tb[NL80211_TXQ_ATTR_CWMAX]);
1694 txq_params->aifs = nla_get_u8(tb[NL80211_TXQ_ATTR_AIFS]);
1695
Johannes Berga3304b02012-03-28 11:04:24 +02001696 if (txq_params->ac >= NL80211_NUM_ACS)
1697 return -EINVAL;
1698
Jouni Malinen31888482008-10-30 16:59:24 +02001699 return 0;
1700}
1701
Johannes Bergf444de02010-05-05 15:25:02 +02001702static bool nl80211_can_set_dev_channel(struct wireless_dev *wdev)
1703{
1704 /*
Johannes Bergcc1d2802012-05-16 23:50:20 +02001705 * You can only set the channel explicitly for WDS interfaces,
1706 * all others have their channel managed via their respective
1707 * "establish a connection" command (connect, join, ...)
1708 *
1709 * For AP/GO and mesh mode, the channel can be set with the
1710 * channel userspace API, but is only stored and passed to the
1711 * low-level driver when the AP starts or the mesh is joined.
1712 * This is for backward compatibility, userspace can also give
1713 * the channel in the start-ap or join-mesh commands instead.
Johannes Bergf444de02010-05-05 15:25:02 +02001714 *
1715 * Monitors are special as they are normally slaved to
Johannes Berge8c9bd52012-06-06 08:18:22 +02001716 * whatever else is going on, so they have their own special
1717 * operation to set the monitor channel if possible.
Johannes Bergf444de02010-05-05 15:25:02 +02001718 */
1719 return !wdev ||
1720 wdev->iftype == NL80211_IFTYPE_AP ||
Johannes Bergf444de02010-05-05 15:25:02 +02001721 wdev->iftype == NL80211_IFTYPE_MESH_POINT ||
Johannes Berg074ac8d2010-09-16 14:58:22 +02001722 wdev->iftype == NL80211_IFTYPE_MONITOR ||
1723 wdev->iftype == NL80211_IFTYPE_P2P_GO;
Johannes Bergf444de02010-05-05 15:25:02 +02001724}
1725
Johannes Berg683b6d32012-11-08 21:25:48 +01001726static int nl80211_parse_chandef(struct cfg80211_registered_device *rdev,
1727 struct genl_info *info,
1728 struct cfg80211_chan_def *chandef)
1729{
Mahesh Paliveladbeca2e2012-11-29 14:11:07 +05301730 u32 control_freq;
Johannes Berg683b6d32012-11-08 21:25:48 +01001731
1732 if (!info->attrs[NL80211_ATTR_WIPHY_FREQ])
1733 return -EINVAL;
1734
1735 control_freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]);
1736
1737 chandef->chan = ieee80211_get_channel(&rdev->wiphy, control_freq);
Johannes Berg3d9d1d62012-11-08 23:14:50 +01001738 chandef->width = NL80211_CHAN_WIDTH_20_NOHT;
1739 chandef->center_freq1 = control_freq;
1740 chandef->center_freq2 = 0;
Johannes Berg683b6d32012-11-08 21:25:48 +01001741
1742 /* Primary channel not allowed */
1743 if (!chandef->chan || chandef->chan->flags & IEEE80211_CHAN_DISABLED)
1744 return -EINVAL;
1745
Johannes Berg3d9d1d62012-11-08 23:14:50 +01001746 if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) {
1747 enum nl80211_channel_type chantype;
Johannes Berg683b6d32012-11-08 21:25:48 +01001748
Johannes Berg3d9d1d62012-11-08 23:14:50 +01001749 chantype = nla_get_u32(
1750 info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]);
1751
1752 switch (chantype) {
1753 case NL80211_CHAN_NO_HT:
1754 case NL80211_CHAN_HT20:
1755 case NL80211_CHAN_HT40PLUS:
1756 case NL80211_CHAN_HT40MINUS:
1757 cfg80211_chandef_create(chandef, chandef->chan,
1758 chantype);
1759 break;
1760 default:
Johannes Berg683b6d32012-11-08 21:25:48 +01001761 return -EINVAL;
Johannes Berg683b6d32012-11-08 21:25:48 +01001762 }
Johannes Berg3d9d1d62012-11-08 23:14:50 +01001763 } else if (info->attrs[NL80211_ATTR_CHANNEL_WIDTH]) {
1764 chandef->width =
1765 nla_get_u32(info->attrs[NL80211_ATTR_CHANNEL_WIDTH]);
1766 if (info->attrs[NL80211_ATTR_CENTER_FREQ1])
1767 chandef->center_freq1 =
1768 nla_get_u32(
1769 info->attrs[NL80211_ATTR_CENTER_FREQ1]);
1770 if (info->attrs[NL80211_ATTR_CENTER_FREQ2])
1771 chandef->center_freq2 =
1772 nla_get_u32(
1773 info->attrs[NL80211_ATTR_CENTER_FREQ2]);
1774 }
1775
Johannes Berg9f5e8f62012-11-22 16:59:45 +01001776 if (!cfg80211_chandef_valid(chandef))
Johannes Berg3d9d1d62012-11-08 23:14:50 +01001777 return -EINVAL;
1778
Johannes Berg9f5e8f62012-11-22 16:59:45 +01001779 if (!cfg80211_chandef_usable(&rdev->wiphy, chandef,
1780 IEEE80211_CHAN_DISABLED))
Johannes Berg3d9d1d62012-11-08 23:14:50 +01001781 return -EINVAL;
Johannes Berg3d9d1d62012-11-08 23:14:50 +01001782
Johannes Berg683b6d32012-11-08 21:25:48 +01001783 return 0;
1784}
1785
Johannes Bergf444de02010-05-05 15:25:02 +02001786static int __nl80211_set_channel(struct cfg80211_registered_device *rdev,
1787 struct wireless_dev *wdev,
1788 struct genl_info *info)
1789{
Johannes Berg683b6d32012-11-08 21:25:48 +01001790 struct cfg80211_chan_def chandef;
Johannes Bergf444de02010-05-05 15:25:02 +02001791 int result;
Johannes Berge8c9bd52012-06-06 08:18:22 +02001792 enum nl80211_iftype iftype = NL80211_IFTYPE_MONITOR;
1793
1794 if (wdev)
1795 iftype = wdev->iftype;
Johannes Bergf444de02010-05-05 15:25:02 +02001796
Johannes Bergf444de02010-05-05 15:25:02 +02001797 if (!nl80211_can_set_dev_channel(wdev))
1798 return -EOPNOTSUPP;
1799
Johannes Berg683b6d32012-11-08 21:25:48 +01001800 result = nl80211_parse_chandef(rdev, info, &chandef);
1801 if (result)
1802 return result;
Johannes Bergf444de02010-05-05 15:25:02 +02001803
1804 mutex_lock(&rdev->devlist_mtx);
Johannes Berge8c9bd52012-06-06 08:18:22 +02001805 switch (iftype) {
Johannes Bergaa430da2012-05-16 23:50:18 +02001806 case NL80211_IFTYPE_AP:
1807 case NL80211_IFTYPE_P2P_GO:
1808 if (wdev->beacon_interval) {
1809 result = -EBUSY;
1810 break;
1811 }
Johannes Berg683b6d32012-11-08 21:25:48 +01001812 if (!cfg80211_reg_can_beacon(&rdev->wiphy, &chandef)) {
Johannes Bergaa430da2012-05-16 23:50:18 +02001813 result = -EINVAL;
1814 break;
1815 }
Johannes Berg683b6d32012-11-08 21:25:48 +01001816 wdev->preset_chandef = chandef;
Johannes Bergaa430da2012-05-16 23:50:18 +02001817 result = 0;
1818 break;
Johannes Bergcc1d2802012-05-16 23:50:20 +02001819 case NL80211_IFTYPE_MESH_POINT:
Johannes Berg683b6d32012-11-08 21:25:48 +01001820 result = cfg80211_set_mesh_channel(rdev, wdev, &chandef);
Johannes Bergcc1d2802012-05-16 23:50:20 +02001821 break;
Johannes Berge8c9bd52012-06-06 08:18:22 +02001822 case NL80211_IFTYPE_MONITOR:
Johannes Berg683b6d32012-11-08 21:25:48 +01001823 result = cfg80211_set_monitor_channel(rdev, &chandef);
Johannes Berge8c9bd52012-06-06 08:18:22 +02001824 break;
Johannes Bergaa430da2012-05-16 23:50:18 +02001825 default:
Johannes Berge8c9bd52012-06-06 08:18:22 +02001826 result = -EINVAL;
Johannes Bergf444de02010-05-05 15:25:02 +02001827 }
1828 mutex_unlock(&rdev->devlist_mtx);
1829
1830 return result;
1831}
1832
1833static int nl80211_set_channel(struct sk_buff *skb, struct genl_info *info)
1834{
Johannes Berg4c476992010-10-04 21:36:35 +02001835 struct cfg80211_registered_device *rdev = info->user_ptr[0];
1836 struct net_device *netdev = info->user_ptr[1];
Johannes Bergf444de02010-05-05 15:25:02 +02001837
Johannes Berg4c476992010-10-04 21:36:35 +02001838 return __nl80211_set_channel(rdev, netdev->ieee80211_ptr, info);
Johannes Bergf444de02010-05-05 15:25:02 +02001839}
1840
Bill Jordane8347eb2010-10-01 13:54:28 -04001841static int nl80211_set_wds_peer(struct sk_buff *skb, struct genl_info *info)
1842{
Johannes Berg43b19952010-10-07 13:10:30 +02001843 struct cfg80211_registered_device *rdev = info->user_ptr[0];
1844 struct net_device *dev = info->user_ptr[1];
1845 struct wireless_dev *wdev = dev->ieee80211_ptr;
Johannes Berg388ac772010-10-07 13:11:09 +02001846 const u8 *bssid;
Bill Jordane8347eb2010-10-01 13:54:28 -04001847
1848 if (!info->attrs[NL80211_ATTR_MAC])
1849 return -EINVAL;
1850
Johannes Berg43b19952010-10-07 13:10:30 +02001851 if (netif_running(dev))
1852 return -EBUSY;
Bill Jordane8347eb2010-10-01 13:54:28 -04001853
Johannes Berg43b19952010-10-07 13:10:30 +02001854 if (!rdev->ops->set_wds_peer)
1855 return -EOPNOTSUPP;
Bill Jordane8347eb2010-10-01 13:54:28 -04001856
Johannes Berg43b19952010-10-07 13:10:30 +02001857 if (wdev->iftype != NL80211_IFTYPE_WDS)
1858 return -EOPNOTSUPP;
Bill Jordane8347eb2010-10-01 13:54:28 -04001859
1860 bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
Hila Gonene35e4d22012-06-27 17:19:42 +03001861 return rdev_set_wds_peer(rdev, dev, bssid);
Bill Jordane8347eb2010-10-01 13:54:28 -04001862}
1863
1864
Johannes Berg55682962007-09-20 13:09:35 -04001865static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
1866{
1867 struct cfg80211_registered_device *rdev;
Johannes Bergf444de02010-05-05 15:25:02 +02001868 struct net_device *netdev = NULL;
1869 struct wireless_dev *wdev;
Bill Jordana1e567c2010-09-10 11:22:32 -04001870 int result = 0, rem_txq_params = 0;
Jouni Malinen31888482008-10-30 16:59:24 +02001871 struct nlattr *nl_txq_params;
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +02001872 u32 changed;
1873 u8 retry_short = 0, retry_long = 0;
1874 u32 frag_threshold = 0, rts_threshold = 0;
Lukáš Turek81077e82009-12-21 22:50:47 +01001875 u8 coverage_class = 0;
Johannes Berg55682962007-09-20 13:09:35 -04001876
Johannes Bergf444de02010-05-05 15:25:02 +02001877 /*
1878 * Try to find the wiphy and netdev. Normally this
1879 * function shouldn't need the netdev, but this is
1880 * done for backward compatibility -- previously
1881 * setting the channel was done per wiphy, but now
1882 * it is per netdev. Previous userland like hostapd
1883 * also passed a netdev to set_wiphy, so that it is
1884 * possible to let that go to the right netdev!
1885 */
Johannes Berg4bbf4d52009-03-24 09:35:46 +01001886 mutex_lock(&cfg80211_mutex);
1887
Johannes Bergf444de02010-05-05 15:25:02 +02001888 if (info->attrs[NL80211_ATTR_IFINDEX]) {
1889 int ifindex = nla_get_u32(info->attrs[NL80211_ATTR_IFINDEX]);
1890
1891 netdev = dev_get_by_index(genl_info_net(info), ifindex);
1892 if (netdev && netdev->ieee80211_ptr) {
1893 rdev = wiphy_to_dev(netdev->ieee80211_ptr->wiphy);
1894 mutex_lock(&rdev->mtx);
1895 } else
1896 netdev = NULL;
Johannes Berg4bbf4d52009-03-24 09:35:46 +01001897 }
1898
Johannes Bergf444de02010-05-05 15:25:02 +02001899 if (!netdev) {
Johannes Berg878d9ec2012-06-15 14:18:32 +02001900 rdev = __cfg80211_rdev_from_attrs(genl_info_net(info),
1901 info->attrs);
Johannes Bergf444de02010-05-05 15:25:02 +02001902 if (IS_ERR(rdev)) {
1903 mutex_unlock(&cfg80211_mutex);
Johannes Berg4c476992010-10-04 21:36:35 +02001904 return PTR_ERR(rdev);
Johannes Bergf444de02010-05-05 15:25:02 +02001905 }
1906 wdev = NULL;
1907 netdev = NULL;
1908 result = 0;
1909
1910 mutex_lock(&rdev->mtx);
Johannes Berg71fe96b2012-10-24 10:04:58 +02001911 } else
Johannes Bergf444de02010-05-05 15:25:02 +02001912 wdev = netdev->ieee80211_ptr;
Johannes Bergf444de02010-05-05 15:25:02 +02001913
1914 /*
1915 * end workaround code, by now the rdev is available
1916 * and locked, and wdev may or may not be NULL.
1917 */
Johannes Berg4bbf4d52009-03-24 09:35:46 +01001918
1919 if (info->attrs[NL80211_ATTR_WIPHY_NAME])
Jouni Malinen31888482008-10-30 16:59:24 +02001920 result = cfg80211_dev_rename(
1921 rdev, nla_data(info->attrs[NL80211_ATTR_WIPHY_NAME]));
Johannes Berg4bbf4d52009-03-24 09:35:46 +01001922
1923 mutex_unlock(&cfg80211_mutex);
1924
1925 if (result)
1926 goto bad_res;
Johannes Berg55682962007-09-20 13:09:35 -04001927
Jouni Malinen31888482008-10-30 16:59:24 +02001928 if (info->attrs[NL80211_ATTR_WIPHY_TXQ_PARAMS]) {
1929 struct ieee80211_txq_params txq_params;
1930 struct nlattr *tb[NL80211_TXQ_ATTR_MAX + 1];
1931
1932 if (!rdev->ops->set_txq_params) {
1933 result = -EOPNOTSUPP;
1934 goto bad_res;
1935 }
1936
Eliad Pellerf70f01c2011-09-25 20:06:53 +03001937 if (!netdev) {
1938 result = -EINVAL;
1939 goto bad_res;
1940 }
1941
Johannes Berg133a3ff2011-11-03 14:50:13 +01001942 if (netdev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
1943 netdev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) {
1944 result = -EINVAL;
1945 goto bad_res;
1946 }
1947
Johannes Berg2b5f8b02012-04-02 10:51:55 +02001948 if (!netif_running(netdev)) {
1949 result = -ENETDOWN;
1950 goto bad_res;
1951 }
1952
Jouni Malinen31888482008-10-30 16:59:24 +02001953 nla_for_each_nested(nl_txq_params,
1954 info->attrs[NL80211_ATTR_WIPHY_TXQ_PARAMS],
1955 rem_txq_params) {
1956 nla_parse(tb, NL80211_TXQ_ATTR_MAX,
1957 nla_data(nl_txq_params),
1958 nla_len(nl_txq_params),
1959 txq_params_policy);
1960 result = parse_txq_params(tb, &txq_params);
1961 if (result)
1962 goto bad_res;
1963
Hila Gonene35e4d22012-06-27 17:19:42 +03001964 result = rdev_set_txq_params(rdev, netdev,
1965 &txq_params);
Jouni Malinen31888482008-10-30 16:59:24 +02001966 if (result)
1967 goto bad_res;
1968 }
1969 }
1970
Jouni Malinen72bdcf32008-11-26 16:15:24 +02001971 if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
Johannes Berg71fe96b2012-10-24 10:04:58 +02001972 result = __nl80211_set_channel(rdev,
1973 nl80211_can_set_dev_channel(wdev) ? wdev : NULL,
1974 info);
Jouni Malinen72bdcf32008-11-26 16:15:24 +02001975 if (result)
1976 goto bad_res;
1977 }
1978
Juuso Oikarinen98d2ff82010-06-23 12:12:38 +03001979 if (info->attrs[NL80211_ATTR_WIPHY_TX_POWER_SETTING]) {
Johannes Bergc8442112012-10-24 10:17:18 +02001980 struct wireless_dev *txp_wdev = wdev;
Juuso Oikarinen98d2ff82010-06-23 12:12:38 +03001981 enum nl80211_tx_power_setting type;
1982 int idx, mbm = 0;
1983
Johannes Bergc8442112012-10-24 10:17:18 +02001984 if (!(rdev->wiphy.features & NL80211_FEATURE_VIF_TXPOWER))
1985 txp_wdev = NULL;
1986
Juuso Oikarinen98d2ff82010-06-23 12:12:38 +03001987 if (!rdev->ops->set_tx_power) {
Jiri Slaby60ea3852010-07-07 15:02:46 +02001988 result = -EOPNOTSUPP;
Juuso Oikarinen98d2ff82010-06-23 12:12:38 +03001989 goto bad_res;
1990 }
1991
1992 idx = NL80211_ATTR_WIPHY_TX_POWER_SETTING;
1993 type = nla_get_u32(info->attrs[idx]);
1994
1995 if (!info->attrs[NL80211_ATTR_WIPHY_TX_POWER_LEVEL] &&
1996 (type != NL80211_TX_POWER_AUTOMATIC)) {
1997 result = -EINVAL;
1998 goto bad_res;
1999 }
2000
2001 if (type != NL80211_TX_POWER_AUTOMATIC) {
2002 idx = NL80211_ATTR_WIPHY_TX_POWER_LEVEL;
2003 mbm = nla_get_u32(info->attrs[idx]);
2004 }
2005
Johannes Bergc8442112012-10-24 10:17:18 +02002006 result = rdev_set_tx_power(rdev, txp_wdev, type, mbm);
Juuso Oikarinen98d2ff82010-06-23 12:12:38 +03002007 if (result)
2008 goto bad_res;
2009 }
2010
Bruno Randolfafe0cbf2010-11-10 12:50:50 +09002011 if (info->attrs[NL80211_ATTR_WIPHY_ANTENNA_TX] &&
2012 info->attrs[NL80211_ATTR_WIPHY_ANTENNA_RX]) {
2013 u32 tx_ant, rx_ant;
Bruno Randolf7f531e02010-12-16 11:30:22 +09002014 if ((!rdev->wiphy.available_antennas_tx &&
2015 !rdev->wiphy.available_antennas_rx) ||
2016 !rdev->ops->set_antenna) {
Bruno Randolfafe0cbf2010-11-10 12:50:50 +09002017 result = -EOPNOTSUPP;
2018 goto bad_res;
2019 }
2020
2021 tx_ant = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_ANTENNA_TX]);
2022 rx_ant = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_ANTENNA_RX]);
2023
Bruno Randolfa7ffac92010-12-08 13:59:24 +09002024 /* reject antenna configurations which don't match the
Bruno Randolf7f531e02010-12-16 11:30:22 +09002025 * available antenna masks, except for the "all" mask */
2026 if ((~tx_ant && (tx_ant & ~rdev->wiphy.available_antennas_tx)) ||
2027 (~rx_ant && (rx_ant & ~rdev->wiphy.available_antennas_rx))) {
Bruno Randolfa7ffac92010-12-08 13:59:24 +09002028 result = -EINVAL;
2029 goto bad_res;
2030 }
2031
Bruno Randolf7f531e02010-12-16 11:30:22 +09002032 tx_ant = tx_ant & rdev->wiphy.available_antennas_tx;
2033 rx_ant = rx_ant & rdev->wiphy.available_antennas_rx;
Bruno Randolfa7ffac92010-12-08 13:59:24 +09002034
Hila Gonene35e4d22012-06-27 17:19:42 +03002035 result = rdev_set_antenna(rdev, tx_ant, rx_ant);
Bruno Randolfafe0cbf2010-11-10 12:50:50 +09002036 if (result)
2037 goto bad_res;
2038 }
2039
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +02002040 changed = 0;
2041
2042 if (info->attrs[NL80211_ATTR_WIPHY_RETRY_SHORT]) {
2043 retry_short = nla_get_u8(
2044 info->attrs[NL80211_ATTR_WIPHY_RETRY_SHORT]);
2045 if (retry_short == 0) {
2046 result = -EINVAL;
2047 goto bad_res;
2048 }
2049 changed |= WIPHY_PARAM_RETRY_SHORT;
2050 }
2051
2052 if (info->attrs[NL80211_ATTR_WIPHY_RETRY_LONG]) {
2053 retry_long = nla_get_u8(
2054 info->attrs[NL80211_ATTR_WIPHY_RETRY_LONG]);
2055 if (retry_long == 0) {
2056 result = -EINVAL;
2057 goto bad_res;
2058 }
2059 changed |= WIPHY_PARAM_RETRY_LONG;
2060 }
2061
2062 if (info->attrs[NL80211_ATTR_WIPHY_FRAG_THRESHOLD]) {
2063 frag_threshold = nla_get_u32(
2064 info->attrs[NL80211_ATTR_WIPHY_FRAG_THRESHOLD]);
2065 if (frag_threshold < 256) {
2066 result = -EINVAL;
2067 goto bad_res;
2068 }
2069 if (frag_threshold != (u32) -1) {
2070 /*
2071 * Fragments (apart from the last one) are required to
2072 * have even length. Make the fragmentation code
2073 * simpler by stripping LSB should someone try to use
2074 * odd threshold value.
2075 */
2076 frag_threshold &= ~0x1;
2077 }
2078 changed |= WIPHY_PARAM_FRAG_THRESHOLD;
2079 }
2080
2081 if (info->attrs[NL80211_ATTR_WIPHY_RTS_THRESHOLD]) {
2082 rts_threshold = nla_get_u32(
2083 info->attrs[NL80211_ATTR_WIPHY_RTS_THRESHOLD]);
2084 changed |= WIPHY_PARAM_RTS_THRESHOLD;
2085 }
2086
Lukáš Turek81077e82009-12-21 22:50:47 +01002087 if (info->attrs[NL80211_ATTR_WIPHY_COVERAGE_CLASS]) {
2088 coverage_class = nla_get_u8(
2089 info->attrs[NL80211_ATTR_WIPHY_COVERAGE_CLASS]);
2090 changed |= WIPHY_PARAM_COVERAGE_CLASS;
2091 }
2092
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +02002093 if (changed) {
2094 u8 old_retry_short, old_retry_long;
2095 u32 old_frag_threshold, old_rts_threshold;
Lukáš Turek81077e82009-12-21 22:50:47 +01002096 u8 old_coverage_class;
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +02002097
2098 if (!rdev->ops->set_wiphy_params) {
2099 result = -EOPNOTSUPP;
2100 goto bad_res;
2101 }
2102
2103 old_retry_short = rdev->wiphy.retry_short;
2104 old_retry_long = rdev->wiphy.retry_long;
2105 old_frag_threshold = rdev->wiphy.frag_threshold;
2106 old_rts_threshold = rdev->wiphy.rts_threshold;
Lukáš Turek81077e82009-12-21 22:50:47 +01002107 old_coverage_class = rdev->wiphy.coverage_class;
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +02002108
2109 if (changed & WIPHY_PARAM_RETRY_SHORT)
2110 rdev->wiphy.retry_short = retry_short;
2111 if (changed & WIPHY_PARAM_RETRY_LONG)
2112 rdev->wiphy.retry_long = retry_long;
2113 if (changed & WIPHY_PARAM_FRAG_THRESHOLD)
2114 rdev->wiphy.frag_threshold = frag_threshold;
2115 if (changed & WIPHY_PARAM_RTS_THRESHOLD)
2116 rdev->wiphy.rts_threshold = rts_threshold;
Lukáš Turek81077e82009-12-21 22:50:47 +01002117 if (changed & WIPHY_PARAM_COVERAGE_CLASS)
2118 rdev->wiphy.coverage_class = coverage_class;
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +02002119
Hila Gonene35e4d22012-06-27 17:19:42 +03002120 result = rdev_set_wiphy_params(rdev, changed);
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +02002121 if (result) {
2122 rdev->wiphy.retry_short = old_retry_short;
2123 rdev->wiphy.retry_long = old_retry_long;
2124 rdev->wiphy.frag_threshold = old_frag_threshold;
2125 rdev->wiphy.rts_threshold = old_rts_threshold;
Lukáš Turek81077e82009-12-21 22:50:47 +01002126 rdev->wiphy.coverage_class = old_coverage_class;
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +02002127 }
2128 }
Jouni Malinen72bdcf32008-11-26 16:15:24 +02002129
Johannes Berg306d6112008-12-08 12:39:04 +01002130 bad_res:
Johannes Berg4bbf4d52009-03-24 09:35:46 +01002131 mutex_unlock(&rdev->mtx);
Johannes Bergf444de02010-05-05 15:25:02 +02002132 if (netdev)
2133 dev_put(netdev);
Johannes Berg55682962007-09-20 13:09:35 -04002134 return result;
2135}
2136
Johannes Berg71bbc992012-06-15 15:30:18 +02002137static inline u64 wdev_id(struct wireless_dev *wdev)
2138{
2139 return (u64)wdev->identifier |
2140 ((u64)wiphy_to_dev(wdev->wiphy)->wiphy_idx << 32);
2141}
Johannes Berg55682962007-09-20 13:09:35 -04002142
Johannes Berg683b6d32012-11-08 21:25:48 +01002143static int nl80211_send_chandef(struct sk_buff *msg,
2144 struct cfg80211_chan_def *chandef)
2145{
Johannes Berg9f5e8f62012-11-22 16:59:45 +01002146 WARN_ON(!cfg80211_chandef_valid(chandef));
Johannes Berg3d9d1d62012-11-08 23:14:50 +01002147
Johannes Berg683b6d32012-11-08 21:25:48 +01002148 if (nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ,
2149 chandef->chan->center_freq))
2150 return -ENOBUFS;
Johannes Berg3d9d1d62012-11-08 23:14:50 +01002151 switch (chandef->width) {
2152 case NL80211_CHAN_WIDTH_20_NOHT:
2153 case NL80211_CHAN_WIDTH_20:
2154 case NL80211_CHAN_WIDTH_40:
2155 if (nla_put_u32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE,
2156 cfg80211_get_chandef_type(chandef)))
2157 return -ENOBUFS;
2158 break;
2159 default:
2160 break;
2161 }
2162 if (nla_put_u32(msg, NL80211_ATTR_CHANNEL_WIDTH, chandef->width))
2163 return -ENOBUFS;
2164 if (nla_put_u32(msg, NL80211_ATTR_CENTER_FREQ1, chandef->center_freq1))
2165 return -ENOBUFS;
2166 if (chandef->center_freq2 &&
2167 nla_put_u32(msg, NL80211_ATTR_CENTER_FREQ2, chandef->center_freq2))
Johannes Berg683b6d32012-11-08 21:25:48 +01002168 return -ENOBUFS;
2169 return 0;
2170}
2171
Eric W. Biederman15e47302012-09-07 20:12:54 +00002172static int nl80211_send_iface(struct sk_buff *msg, u32 portid, u32 seq, int flags,
Johannes Bergd7264052009-04-19 16:23:20 +02002173 struct cfg80211_registered_device *rdev,
Johannes Berg72fb2ab2012-06-15 17:52:47 +02002174 struct wireless_dev *wdev)
Johannes Berg55682962007-09-20 13:09:35 -04002175{
Johannes Berg72fb2ab2012-06-15 17:52:47 +02002176 struct net_device *dev = wdev->netdev;
Johannes Berg55682962007-09-20 13:09:35 -04002177 void *hdr;
2178
Eric W. Biederman15e47302012-09-07 20:12:54 +00002179 hdr = nl80211hdr_put(msg, portid, seq, flags, NL80211_CMD_NEW_INTERFACE);
Johannes Berg55682962007-09-20 13:09:35 -04002180 if (!hdr)
2181 return -1;
2182
Johannes Berg72fb2ab2012-06-15 17:52:47 +02002183 if (dev &&
2184 (nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) ||
Johannes Berg98104fde2012-06-16 00:19:54 +02002185 nla_put_string(msg, NL80211_ATTR_IFNAME, dev->name)))
Johannes Berg72fb2ab2012-06-15 17:52:47 +02002186 goto nla_put_failure;
2187
2188 if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
2189 nla_put_u32(msg, NL80211_ATTR_IFTYPE, wdev->iftype) ||
Johannes Berg71bbc992012-06-15 15:30:18 +02002190 nla_put_u64(msg, NL80211_ATTR_WDEV, wdev_id(wdev)) ||
Johannes Berg98104fde2012-06-16 00:19:54 +02002191 nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, wdev_address(wdev)) ||
David S. Miller9360ffd2012-03-29 04:41:26 -04002192 nla_put_u32(msg, NL80211_ATTR_GENERATION,
2193 rdev->devlist_generation ^
2194 (cfg80211_rdev_list_generation << 2)))
2195 goto nla_put_failure;
Johannes Bergf5ea9122009-08-07 16:17:38 +02002196
Johannes Berg5b7ccaf2012-07-12 19:45:08 +02002197 if (rdev->ops->get_channel) {
Johannes Berg683b6d32012-11-08 21:25:48 +01002198 int ret;
2199 struct cfg80211_chan_def chandef;
Johannes Berg5b7ccaf2012-07-12 19:45:08 +02002200
Johannes Berg683b6d32012-11-08 21:25:48 +01002201 ret = rdev_get_channel(rdev, wdev, &chandef);
2202 if (ret == 0) {
2203 if (nl80211_send_chandef(msg, &chandef))
2204 goto nla_put_failure;
2205 }
Pontus Fuchsd91df0e2012-04-03 16:39:58 +02002206 }
2207
Antonio Quartullib84e7a02012-11-07 12:52:20 +01002208 if (wdev->ssid_len) {
2209 if (nla_put(msg, NL80211_ATTR_SSID, wdev->ssid_len, wdev->ssid))
2210 goto nla_put_failure;
2211 }
2212
Johannes Berg55682962007-09-20 13:09:35 -04002213 return genlmsg_end(msg, hdr);
2214
2215 nla_put_failure:
Thomas Grafbc3ed282008-06-03 16:36:54 -07002216 genlmsg_cancel(msg, hdr);
2217 return -EMSGSIZE;
Johannes Berg55682962007-09-20 13:09:35 -04002218}
2219
2220static int nl80211_dump_interface(struct sk_buff *skb, struct netlink_callback *cb)
2221{
2222 int wp_idx = 0;
2223 int if_idx = 0;
2224 int wp_start = cb->args[0];
2225 int if_start = cb->args[1];
Johannes Bergf5ea9122009-08-07 16:17:38 +02002226 struct cfg80211_registered_device *rdev;
Johannes Berg55682962007-09-20 13:09:35 -04002227 struct wireless_dev *wdev;
2228
Luis R. Rodrigueza1794392009-02-21 00:04:21 -05002229 mutex_lock(&cfg80211_mutex);
Johannes Bergf5ea9122009-08-07 16:17:38 +02002230 list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
2231 if (!net_eq(wiphy_net(&rdev->wiphy), sock_net(skb->sk)))
Johannes Berg463d0182009-07-14 00:33:35 +02002232 continue;
Johannes Bergbba95fe2008-07-29 13:22:51 +02002233 if (wp_idx < wp_start) {
2234 wp_idx++;
Johannes Berg55682962007-09-20 13:09:35 -04002235 continue;
Johannes Bergbba95fe2008-07-29 13:22:51 +02002236 }
Johannes Berg55682962007-09-20 13:09:35 -04002237 if_idx = 0;
2238
Johannes Bergf5ea9122009-08-07 16:17:38 +02002239 mutex_lock(&rdev->devlist_mtx);
Johannes Berg89a54e42012-06-15 14:33:17 +02002240 list_for_each_entry(wdev, &rdev->wdev_list, list) {
Johannes Bergbba95fe2008-07-29 13:22:51 +02002241 if (if_idx < if_start) {
2242 if_idx++;
Johannes Berg55682962007-09-20 13:09:35 -04002243 continue;
Johannes Bergbba95fe2008-07-29 13:22:51 +02002244 }
Eric W. Biederman15e47302012-09-07 20:12:54 +00002245 if (nl80211_send_iface(skb, NETLINK_CB(cb->skb).portid,
Johannes Berg55682962007-09-20 13:09:35 -04002246 cb->nlh->nlmsg_seq, NLM_F_MULTI,
Johannes Berg72fb2ab2012-06-15 17:52:47 +02002247 rdev, wdev) < 0) {
Johannes Bergf5ea9122009-08-07 16:17:38 +02002248 mutex_unlock(&rdev->devlist_mtx);
Johannes Bergbba95fe2008-07-29 13:22:51 +02002249 goto out;
2250 }
2251 if_idx++;
Johannes Berg55682962007-09-20 13:09:35 -04002252 }
Johannes Bergf5ea9122009-08-07 16:17:38 +02002253 mutex_unlock(&rdev->devlist_mtx);
Johannes Bergbba95fe2008-07-29 13:22:51 +02002254
2255 wp_idx++;
Johannes Berg55682962007-09-20 13:09:35 -04002256 }
Johannes Bergbba95fe2008-07-29 13:22:51 +02002257 out:
Luis R. Rodrigueza1794392009-02-21 00:04:21 -05002258 mutex_unlock(&cfg80211_mutex);
Johannes Berg55682962007-09-20 13:09:35 -04002259
2260 cb->args[0] = wp_idx;
2261 cb->args[1] = if_idx;
2262
2263 return skb->len;
2264}
2265
2266static int nl80211_get_interface(struct sk_buff *skb, struct genl_info *info)
2267{
2268 struct sk_buff *msg;
Johannes Berg4c476992010-10-04 21:36:35 +02002269 struct cfg80211_registered_device *dev = info->user_ptr[0];
Johannes Berg72fb2ab2012-06-15 17:52:47 +02002270 struct wireless_dev *wdev = info->user_ptr[1];
Johannes Berg55682962007-09-20 13:09:35 -04002271
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07002272 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg55682962007-09-20 13:09:35 -04002273 if (!msg)
Johannes Berg4c476992010-10-04 21:36:35 +02002274 return -ENOMEM;
Johannes Berg55682962007-09-20 13:09:35 -04002275
Eric W. Biederman15e47302012-09-07 20:12:54 +00002276 if (nl80211_send_iface(msg, info->snd_portid, info->snd_seq, 0,
Johannes Berg72fb2ab2012-06-15 17:52:47 +02002277 dev, wdev) < 0) {
Johannes Berg4c476992010-10-04 21:36:35 +02002278 nlmsg_free(msg);
2279 return -ENOBUFS;
2280 }
Johannes Berg55682962007-09-20 13:09:35 -04002281
Johannes Berg134e6372009-07-10 09:51:34 +00002282 return genlmsg_reply(msg, info);
Johannes Berg55682962007-09-20 13:09:35 -04002283}
2284
Michael Wu66f7ac52008-01-31 19:48:22 +01002285static const struct nla_policy mntr_flags_policy[NL80211_MNTR_FLAG_MAX + 1] = {
2286 [NL80211_MNTR_FLAG_FCSFAIL] = { .type = NLA_FLAG },
2287 [NL80211_MNTR_FLAG_PLCPFAIL] = { .type = NLA_FLAG },
2288 [NL80211_MNTR_FLAG_CONTROL] = { .type = NLA_FLAG },
2289 [NL80211_MNTR_FLAG_OTHER_BSS] = { .type = NLA_FLAG },
2290 [NL80211_MNTR_FLAG_COOK_FRAMES] = { .type = NLA_FLAG },
2291};
2292
2293static int parse_monitor_flags(struct nlattr *nla, u32 *mntrflags)
2294{
2295 struct nlattr *flags[NL80211_MNTR_FLAG_MAX + 1];
2296 int flag;
2297
2298 *mntrflags = 0;
2299
2300 if (!nla)
2301 return -EINVAL;
2302
2303 if (nla_parse_nested(flags, NL80211_MNTR_FLAG_MAX,
2304 nla, mntr_flags_policy))
2305 return -EINVAL;
2306
2307 for (flag = 1; flag <= NL80211_MNTR_FLAG_MAX; flag++)
2308 if (flags[flag])
2309 *mntrflags |= (1<<flag);
2310
2311 return 0;
2312}
2313
Johannes Berg9bc383d2009-11-19 11:55:19 +01002314static int nl80211_valid_4addr(struct cfg80211_registered_device *rdev,
Johannes Bergad4bb6f2009-11-19 00:56:30 +01002315 struct net_device *netdev, u8 use_4addr,
2316 enum nl80211_iftype iftype)
Johannes Berg9bc383d2009-11-19 11:55:19 +01002317{
Johannes Bergad4bb6f2009-11-19 00:56:30 +01002318 if (!use_4addr) {
Jiri Pirkof350a0a82010-06-15 06:50:45 +00002319 if (netdev && (netdev->priv_flags & IFF_BRIDGE_PORT))
Johannes Bergad4bb6f2009-11-19 00:56:30 +01002320 return -EBUSY;
Johannes Berg9bc383d2009-11-19 11:55:19 +01002321 return 0;
Johannes Bergad4bb6f2009-11-19 00:56:30 +01002322 }
Johannes Berg9bc383d2009-11-19 11:55:19 +01002323
2324 switch (iftype) {
2325 case NL80211_IFTYPE_AP_VLAN:
2326 if (rdev->wiphy.flags & WIPHY_FLAG_4ADDR_AP)
2327 return 0;
2328 break;
2329 case NL80211_IFTYPE_STATION:
2330 if (rdev->wiphy.flags & WIPHY_FLAG_4ADDR_STATION)
2331 return 0;
2332 break;
2333 default:
2334 break;
2335 }
2336
2337 return -EOPNOTSUPP;
2338}
2339
Johannes Berg55682962007-09-20 13:09:35 -04002340static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info)
2341{
Johannes Berg4c476992010-10-04 21:36:35 +02002342 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002343 struct vif_params params;
Johannes Berge36d56b2009-06-09 21:04:43 +02002344 int err;
Johannes Berg04a773a2009-04-19 21:24:32 +02002345 enum nl80211_iftype otype, ntype;
Johannes Berg4c476992010-10-04 21:36:35 +02002346 struct net_device *dev = info->user_ptr[1];
Johannes Berg92ffe052008-09-16 20:39:36 +02002347 u32 _flags, *flags = NULL;
Johannes Bergac7f9cf2009-03-21 17:07:59 +01002348 bool change = false;
Johannes Berg55682962007-09-20 13:09:35 -04002349
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002350 memset(&params, 0, sizeof(params));
2351
Johannes Berg04a773a2009-04-19 21:24:32 +02002352 otype = ntype = dev->ieee80211_ptr->iftype;
Johannes Berg55682962007-09-20 13:09:35 -04002353
Johannes Berg723b0382008-09-16 20:22:09 +02002354 if (info->attrs[NL80211_ATTR_IFTYPE]) {
Johannes Bergac7f9cf2009-03-21 17:07:59 +01002355 ntype = nla_get_u32(info->attrs[NL80211_ATTR_IFTYPE]);
Johannes Berg04a773a2009-04-19 21:24:32 +02002356 if (otype != ntype)
Johannes Bergac7f9cf2009-03-21 17:07:59 +01002357 change = true;
Johannes Berg4c476992010-10-04 21:36:35 +02002358 if (ntype > NL80211_IFTYPE_MAX)
2359 return -EINVAL;
Johannes Berg723b0382008-09-16 20:22:09 +02002360 }
2361
Johannes Berg92ffe052008-09-16 20:39:36 +02002362 if (info->attrs[NL80211_ATTR_MESH_ID]) {
Johannes Berg29cbe682010-12-03 09:20:44 +01002363 struct wireless_dev *wdev = dev->ieee80211_ptr;
2364
Johannes Berg4c476992010-10-04 21:36:35 +02002365 if (ntype != NL80211_IFTYPE_MESH_POINT)
2366 return -EINVAL;
Johannes Berg29cbe682010-12-03 09:20:44 +01002367 if (netif_running(dev))
2368 return -EBUSY;
2369
2370 wdev_lock(wdev);
2371 BUILD_BUG_ON(IEEE80211_MAX_SSID_LEN !=
2372 IEEE80211_MAX_MESH_ID_LEN);
2373 wdev->mesh_id_up_len =
2374 nla_len(info->attrs[NL80211_ATTR_MESH_ID]);
2375 memcpy(wdev->ssid, nla_data(info->attrs[NL80211_ATTR_MESH_ID]),
2376 wdev->mesh_id_up_len);
2377 wdev_unlock(wdev);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002378 }
2379
Felix Fietkau8b787642009-11-10 18:53:10 +01002380 if (info->attrs[NL80211_ATTR_4ADDR]) {
2381 params.use_4addr = !!nla_get_u8(info->attrs[NL80211_ATTR_4ADDR]);
2382 change = true;
Johannes Bergad4bb6f2009-11-19 00:56:30 +01002383 err = nl80211_valid_4addr(rdev, dev, params.use_4addr, ntype);
Johannes Berg9bc383d2009-11-19 11:55:19 +01002384 if (err)
Johannes Berg4c476992010-10-04 21:36:35 +02002385 return err;
Felix Fietkau8b787642009-11-10 18:53:10 +01002386 } else {
2387 params.use_4addr = -1;
2388 }
2389
Johannes Berg92ffe052008-09-16 20:39:36 +02002390 if (info->attrs[NL80211_ATTR_MNTR_FLAGS]) {
Johannes Berg4c476992010-10-04 21:36:35 +02002391 if (ntype != NL80211_IFTYPE_MONITOR)
2392 return -EINVAL;
Johannes Berg92ffe052008-09-16 20:39:36 +02002393 err = parse_monitor_flags(info->attrs[NL80211_ATTR_MNTR_FLAGS],
2394 &_flags);
Johannes Bergac7f9cf2009-03-21 17:07:59 +01002395 if (err)
Johannes Berg4c476992010-10-04 21:36:35 +02002396 return err;
Johannes Bergac7f9cf2009-03-21 17:07:59 +01002397
2398 flags = &_flags;
2399 change = true;
Johannes Berg92ffe052008-09-16 20:39:36 +02002400 }
Johannes Berg3b858752009-03-12 09:55:09 +01002401
Johannes Bergac7f9cf2009-03-21 17:07:59 +01002402 if (change)
Johannes Berg3d54d252009-08-21 14:51:05 +02002403 err = cfg80211_change_iface(rdev, dev, ntype, flags, &params);
Johannes Bergac7f9cf2009-03-21 17:07:59 +01002404 else
2405 err = 0;
Johannes Berg60719ff2008-09-16 14:55:09 +02002406
Johannes Berg9bc383d2009-11-19 11:55:19 +01002407 if (!err && params.use_4addr != -1)
2408 dev->ieee80211_ptr->use_4addr = params.use_4addr;
2409
Johannes Berg55682962007-09-20 13:09:35 -04002410 return err;
2411}
2412
2413static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info)
2414{
Johannes Berg4c476992010-10-04 21:36:35 +02002415 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002416 struct vif_params params;
Johannes Berg84efbb82012-06-16 00:00:26 +02002417 struct wireless_dev *wdev;
Johannes Berg1c90f9d2012-06-16 00:05:37 +02002418 struct sk_buff *msg;
Johannes Berg55682962007-09-20 13:09:35 -04002419 int err;
2420 enum nl80211_iftype type = NL80211_IFTYPE_UNSPECIFIED;
Michael Wu66f7ac52008-01-31 19:48:22 +01002421 u32 flags;
Johannes Berg55682962007-09-20 13:09:35 -04002422
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002423 memset(&params, 0, sizeof(params));
2424
Johannes Berg55682962007-09-20 13:09:35 -04002425 if (!info->attrs[NL80211_ATTR_IFNAME])
2426 return -EINVAL;
2427
2428 if (info->attrs[NL80211_ATTR_IFTYPE]) {
2429 type = nla_get_u32(info->attrs[NL80211_ATTR_IFTYPE]);
2430 if (type > NL80211_IFTYPE_MAX)
2431 return -EINVAL;
2432 }
2433
Johannes Berg79c97e92009-07-07 03:56:12 +02002434 if (!rdev->ops->add_virtual_intf ||
Johannes Berg4c476992010-10-04 21:36:35 +02002435 !(rdev->wiphy.interface_modes & (1 << type)))
2436 return -EOPNOTSUPP;
Johannes Berg55682962007-09-20 13:09:35 -04002437
Arend van Spriel1c18f142013-01-08 10:17:27 +01002438 if (type == NL80211_IFTYPE_P2P_DEVICE && info->attrs[NL80211_ATTR_MAC]) {
2439 nla_memcpy(params.macaddr, info->attrs[NL80211_ATTR_MAC],
2440 ETH_ALEN);
2441 if (!is_valid_ether_addr(params.macaddr))
2442 return -EADDRNOTAVAIL;
2443 }
2444
Johannes Berg9bc383d2009-11-19 11:55:19 +01002445 if (info->attrs[NL80211_ATTR_4ADDR]) {
Felix Fietkau8b787642009-11-10 18:53:10 +01002446 params.use_4addr = !!nla_get_u8(info->attrs[NL80211_ATTR_4ADDR]);
Johannes Bergad4bb6f2009-11-19 00:56:30 +01002447 err = nl80211_valid_4addr(rdev, NULL, params.use_4addr, type);
Johannes Berg9bc383d2009-11-19 11:55:19 +01002448 if (err)
Johannes Berg4c476992010-10-04 21:36:35 +02002449 return err;
Johannes Berg9bc383d2009-11-19 11:55:19 +01002450 }
Felix Fietkau8b787642009-11-10 18:53:10 +01002451
Johannes Berg1c90f9d2012-06-16 00:05:37 +02002452 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
2453 if (!msg)
2454 return -ENOMEM;
2455
Michael Wu66f7ac52008-01-31 19:48:22 +01002456 err = parse_monitor_flags(type == NL80211_IFTYPE_MONITOR ?
2457 info->attrs[NL80211_ATTR_MNTR_FLAGS] : NULL,
2458 &flags);
Hila Gonene35e4d22012-06-27 17:19:42 +03002459 wdev = rdev_add_virtual_intf(rdev,
2460 nla_data(info->attrs[NL80211_ATTR_IFNAME]),
2461 type, err ? NULL : &flags, &params);
Johannes Berg1c90f9d2012-06-16 00:05:37 +02002462 if (IS_ERR(wdev)) {
2463 nlmsg_free(msg);
Johannes Berg84efbb82012-06-16 00:00:26 +02002464 return PTR_ERR(wdev);
Johannes Berg1c90f9d2012-06-16 00:05:37 +02002465 }
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002466
Johannes Berg98104fde2012-06-16 00:19:54 +02002467 switch (type) {
2468 case NL80211_IFTYPE_MESH_POINT:
2469 if (!info->attrs[NL80211_ATTR_MESH_ID])
2470 break;
Johannes Berg29cbe682010-12-03 09:20:44 +01002471 wdev_lock(wdev);
2472 BUILD_BUG_ON(IEEE80211_MAX_SSID_LEN !=
2473 IEEE80211_MAX_MESH_ID_LEN);
2474 wdev->mesh_id_up_len =
2475 nla_len(info->attrs[NL80211_ATTR_MESH_ID]);
2476 memcpy(wdev->ssid, nla_data(info->attrs[NL80211_ATTR_MESH_ID]),
2477 wdev->mesh_id_up_len);
2478 wdev_unlock(wdev);
Johannes Berg98104fde2012-06-16 00:19:54 +02002479 break;
2480 case NL80211_IFTYPE_P2P_DEVICE:
2481 /*
2482 * P2P Device doesn't have a netdev, so doesn't go
2483 * through the netdev notifier and must be added here
2484 */
2485 mutex_init(&wdev->mtx);
2486 INIT_LIST_HEAD(&wdev->event_list);
2487 spin_lock_init(&wdev->event_lock);
2488 INIT_LIST_HEAD(&wdev->mgmt_registrations);
2489 spin_lock_init(&wdev->mgmt_registrations_lock);
2490
2491 mutex_lock(&rdev->devlist_mtx);
2492 wdev->identifier = ++rdev->wdev_id;
2493 list_add_rcu(&wdev->list, &rdev->wdev_list);
2494 rdev->devlist_generation++;
2495 mutex_unlock(&rdev->devlist_mtx);
2496 break;
2497 default:
2498 break;
Johannes Berg29cbe682010-12-03 09:20:44 +01002499 }
2500
Eric W. Biederman15e47302012-09-07 20:12:54 +00002501 if (nl80211_send_iface(msg, info->snd_portid, info->snd_seq, 0,
Johannes Berg1c90f9d2012-06-16 00:05:37 +02002502 rdev, wdev) < 0) {
2503 nlmsg_free(msg);
2504 return -ENOBUFS;
2505 }
2506
2507 return genlmsg_reply(msg, info);
Johannes Berg55682962007-09-20 13:09:35 -04002508}
2509
2510static int nl80211_del_interface(struct sk_buff *skb, struct genl_info *info)
2511{
Johannes Berg4c476992010-10-04 21:36:35 +02002512 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Johannes Berg84efbb82012-06-16 00:00:26 +02002513 struct wireless_dev *wdev = info->user_ptr[1];
Johannes Berg55682962007-09-20 13:09:35 -04002514
Johannes Berg4c476992010-10-04 21:36:35 +02002515 if (!rdev->ops->del_virtual_intf)
2516 return -EOPNOTSUPP;
Johannes Berg3b858752009-03-12 09:55:09 +01002517
Johannes Berg84efbb82012-06-16 00:00:26 +02002518 /*
2519 * If we remove a wireless device without a netdev then clear
2520 * user_ptr[1] so that nl80211_post_doit won't dereference it
2521 * to check if it needs to do dev_put(). Otherwise it crashes
2522 * since the wdev has been freed, unlike with a netdev where
2523 * we need the dev_put() for the netdev to really be freed.
2524 */
2525 if (!wdev->netdev)
2526 info->user_ptr[1] = NULL;
2527
Hila Gonene35e4d22012-06-27 17:19:42 +03002528 return rdev_del_virtual_intf(rdev, wdev);
Johannes Berg55682962007-09-20 13:09:35 -04002529}
2530
Simon Wunderlich1d9d9212011-11-18 14:20:43 +01002531static int nl80211_set_noack_map(struct sk_buff *skb, struct genl_info *info)
2532{
2533 struct cfg80211_registered_device *rdev = info->user_ptr[0];
2534 struct net_device *dev = info->user_ptr[1];
2535 u16 noack_map;
2536
2537 if (!info->attrs[NL80211_ATTR_NOACK_MAP])
2538 return -EINVAL;
2539
2540 if (!rdev->ops->set_noack_map)
2541 return -EOPNOTSUPP;
2542
2543 noack_map = nla_get_u16(info->attrs[NL80211_ATTR_NOACK_MAP]);
2544
Hila Gonene35e4d22012-06-27 17:19:42 +03002545 return rdev_set_noack_map(rdev, dev, noack_map);
Simon Wunderlich1d9d9212011-11-18 14:20:43 +01002546}
2547
Johannes Berg41ade002007-12-19 02:03:29 +01002548struct get_key_cookie {
2549 struct sk_buff *msg;
2550 int error;
Johannes Bergb9454e82009-07-08 13:29:08 +02002551 int idx;
Johannes Berg41ade002007-12-19 02:03:29 +01002552};
2553
2554static void get_key_callback(void *c, struct key_params *params)
2555{
Johannes Bergb9454e82009-07-08 13:29:08 +02002556 struct nlattr *key;
Johannes Berg41ade002007-12-19 02:03:29 +01002557 struct get_key_cookie *cookie = c;
2558
David S. Miller9360ffd2012-03-29 04:41:26 -04002559 if ((params->key &&
2560 nla_put(cookie->msg, NL80211_ATTR_KEY_DATA,
2561 params->key_len, params->key)) ||
2562 (params->seq &&
2563 nla_put(cookie->msg, NL80211_ATTR_KEY_SEQ,
2564 params->seq_len, params->seq)) ||
2565 (params->cipher &&
2566 nla_put_u32(cookie->msg, NL80211_ATTR_KEY_CIPHER,
2567 params->cipher)))
2568 goto nla_put_failure;
Johannes Berg41ade002007-12-19 02:03:29 +01002569
Johannes Bergb9454e82009-07-08 13:29:08 +02002570 key = nla_nest_start(cookie->msg, NL80211_ATTR_KEY);
2571 if (!key)
2572 goto nla_put_failure;
2573
David S. Miller9360ffd2012-03-29 04:41:26 -04002574 if ((params->key &&
2575 nla_put(cookie->msg, NL80211_KEY_DATA,
2576 params->key_len, params->key)) ||
2577 (params->seq &&
2578 nla_put(cookie->msg, NL80211_KEY_SEQ,
2579 params->seq_len, params->seq)) ||
2580 (params->cipher &&
2581 nla_put_u32(cookie->msg, NL80211_KEY_CIPHER,
2582 params->cipher)))
2583 goto nla_put_failure;
Johannes Bergb9454e82009-07-08 13:29:08 +02002584
David S. Miller9360ffd2012-03-29 04:41:26 -04002585 if (nla_put_u8(cookie->msg, NL80211_ATTR_KEY_IDX, cookie->idx))
2586 goto nla_put_failure;
Johannes Bergb9454e82009-07-08 13:29:08 +02002587
2588 nla_nest_end(cookie->msg, key);
2589
Johannes Berg41ade002007-12-19 02:03:29 +01002590 return;
2591 nla_put_failure:
2592 cookie->error = 1;
2593}
2594
2595static int nl80211_get_key(struct sk_buff *skb, struct genl_info *info)
2596{
Johannes Berg4c476992010-10-04 21:36:35 +02002597 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Johannes Berg41ade002007-12-19 02:03:29 +01002598 int err;
Johannes Berg4c476992010-10-04 21:36:35 +02002599 struct net_device *dev = info->user_ptr[1];
Johannes Berg41ade002007-12-19 02:03:29 +01002600 u8 key_idx = 0;
Johannes Berge31b8212010-10-05 19:39:30 +02002601 const u8 *mac_addr = NULL;
2602 bool pairwise;
Johannes Berg41ade002007-12-19 02:03:29 +01002603 struct get_key_cookie cookie = {
2604 .error = 0,
2605 };
2606 void *hdr;
2607 struct sk_buff *msg;
2608
2609 if (info->attrs[NL80211_ATTR_KEY_IDX])
2610 key_idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]);
2611
Jouni Malinen3cfcf6ac2009-01-08 13:32:02 +02002612 if (key_idx > 5)
Johannes Berg41ade002007-12-19 02:03:29 +01002613 return -EINVAL;
2614
2615 if (info->attrs[NL80211_ATTR_MAC])
2616 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
2617
Johannes Berge31b8212010-10-05 19:39:30 +02002618 pairwise = !!mac_addr;
2619 if (info->attrs[NL80211_ATTR_KEY_TYPE]) {
2620 u32 kt = nla_get_u32(info->attrs[NL80211_ATTR_KEY_TYPE]);
2621 if (kt >= NUM_NL80211_KEYTYPES)
2622 return -EINVAL;
2623 if (kt != NL80211_KEYTYPE_GROUP &&
2624 kt != NL80211_KEYTYPE_PAIRWISE)
2625 return -EINVAL;
2626 pairwise = kt == NL80211_KEYTYPE_PAIRWISE;
2627 }
2628
Johannes Berg4c476992010-10-04 21:36:35 +02002629 if (!rdev->ops->get_key)
2630 return -EOPNOTSUPP;
Johannes Berg41ade002007-12-19 02:03:29 +01002631
Johannes Berg2811d3a2015-01-23 11:10:12 +01002632 if (!pairwise && mac_addr && !(rdev->wiphy.flags & WIPHY_FLAG_IBSS_RSN))
2633 return -ENOENT;
2634
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07002635 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg4c476992010-10-04 21:36:35 +02002636 if (!msg)
2637 return -ENOMEM;
Johannes Berg41ade002007-12-19 02:03:29 +01002638
Eric W. Biederman15e47302012-09-07 20:12:54 +00002639 hdr = nl80211hdr_put(msg, info->snd_portid, info->snd_seq, 0,
Johannes Berg41ade002007-12-19 02:03:29 +01002640 NL80211_CMD_NEW_KEY);
Johannes Berg4c476992010-10-04 21:36:35 +02002641 if (IS_ERR(hdr))
2642 return PTR_ERR(hdr);
Johannes Berg41ade002007-12-19 02:03:29 +01002643
2644 cookie.msg = msg;
Johannes Bergb9454e82009-07-08 13:29:08 +02002645 cookie.idx = key_idx;
Johannes Berg41ade002007-12-19 02:03:29 +01002646
David S. Miller9360ffd2012-03-29 04:41:26 -04002647 if (nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) ||
2648 nla_put_u8(msg, NL80211_ATTR_KEY_IDX, key_idx))
2649 goto nla_put_failure;
2650 if (mac_addr &&
2651 nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr))
2652 goto nla_put_failure;
Johannes Berg41ade002007-12-19 02:03:29 +01002653
Hila Gonene35e4d22012-06-27 17:19:42 +03002654 err = rdev_get_key(rdev, dev, key_idx, pairwise, mac_addr, &cookie,
2655 get_key_callback);
Johannes Berg41ade002007-12-19 02:03:29 +01002656
2657 if (err)
Niko Jokinen6c95e2a2009-07-15 11:00:53 +03002658 goto free_msg;
Johannes Berg41ade002007-12-19 02:03:29 +01002659
2660 if (cookie.error)
2661 goto nla_put_failure;
2662
2663 genlmsg_end(msg, hdr);
Johannes Berg4c476992010-10-04 21:36:35 +02002664 return genlmsg_reply(msg, info);
Johannes Berg41ade002007-12-19 02:03:29 +01002665
2666 nla_put_failure:
2667 err = -ENOBUFS;
Niko Jokinen6c95e2a2009-07-15 11:00:53 +03002668 free_msg:
Johannes Berg41ade002007-12-19 02:03:29 +01002669 nlmsg_free(msg);
Johannes Berg41ade002007-12-19 02:03:29 +01002670 return err;
2671}
2672
2673static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info)
2674{
Johannes Berg4c476992010-10-04 21:36:35 +02002675 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Johannes Bergb9454e82009-07-08 13:29:08 +02002676 struct key_parse key;
Johannes Berg41ade002007-12-19 02:03:29 +01002677 int err;
Johannes Berg4c476992010-10-04 21:36:35 +02002678 struct net_device *dev = info->user_ptr[1];
Johannes Berg41ade002007-12-19 02:03:29 +01002679
Johannes Bergb9454e82009-07-08 13:29:08 +02002680 err = nl80211_parse_key(info, &key);
2681 if (err)
2682 return err;
2683
2684 if (key.idx < 0)
Johannes Berg41ade002007-12-19 02:03:29 +01002685 return -EINVAL;
2686
Johannes Bergb9454e82009-07-08 13:29:08 +02002687 /* only support setting default key */
2688 if (!key.def && !key.defmgmt)
Johannes Berg41ade002007-12-19 02:03:29 +01002689 return -EINVAL;
2690
Johannes Bergfffd0932009-07-08 14:22:54 +02002691 wdev_lock(dev->ieee80211_ptr);
Johannes Bergdbd2fd62010-12-09 19:58:59 +01002692
2693 if (key.def) {
2694 if (!rdev->ops->set_default_key) {
2695 err = -EOPNOTSUPP;
2696 goto out;
2697 }
2698
2699 err = nl80211_key_allowed(dev->ieee80211_ptr);
2700 if (err)
2701 goto out;
2702
Hila Gonene35e4d22012-06-27 17:19:42 +03002703 err = rdev_set_default_key(rdev, dev, key.idx,
Johannes Bergdbd2fd62010-12-09 19:58:59 +01002704 key.def_uni, key.def_multi);
2705
2706 if (err)
2707 goto out;
Johannes Bergfffd0932009-07-08 14:22:54 +02002708
Johannes Berg3d23e342009-09-29 23:27:28 +02002709#ifdef CONFIG_CFG80211_WEXT
Johannes Bergdbd2fd62010-12-09 19:58:59 +01002710 dev->ieee80211_ptr->wext.default_key = key.idx;
Johannes Berg08645122009-05-11 13:54:58 +02002711#endif
Johannes Bergdbd2fd62010-12-09 19:58:59 +01002712 } else {
2713 if (key.def_uni || !key.def_multi) {
2714 err = -EINVAL;
2715 goto out;
2716 }
2717
2718 if (!rdev->ops->set_default_mgmt_key) {
2719 err = -EOPNOTSUPP;
2720 goto out;
2721 }
2722
2723 err = nl80211_key_allowed(dev->ieee80211_ptr);
2724 if (err)
2725 goto out;
2726
Hila Gonene35e4d22012-06-27 17:19:42 +03002727 err = rdev_set_default_mgmt_key(rdev, dev, key.idx);
Johannes Bergdbd2fd62010-12-09 19:58:59 +01002728 if (err)
2729 goto out;
2730
2731#ifdef CONFIG_CFG80211_WEXT
2732 dev->ieee80211_ptr->wext.default_mgmt_key = key.idx;
2733#endif
2734 }
2735
2736 out:
Johannes Bergfffd0932009-07-08 14:22:54 +02002737 wdev_unlock(dev->ieee80211_ptr);
Johannes Berg41ade002007-12-19 02:03:29 +01002738
Johannes Berg41ade002007-12-19 02:03:29 +01002739 return err;
2740}
2741
2742static int nl80211_new_key(struct sk_buff *skb, struct genl_info *info)
2743{
Johannes Berg4c476992010-10-04 21:36:35 +02002744 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Johannes Bergfffd0932009-07-08 14:22:54 +02002745 int err;
Johannes Berg4c476992010-10-04 21:36:35 +02002746 struct net_device *dev = info->user_ptr[1];
Johannes Bergb9454e82009-07-08 13:29:08 +02002747 struct key_parse key;
Johannes Berge31b8212010-10-05 19:39:30 +02002748 const u8 *mac_addr = NULL;
Johannes Berg41ade002007-12-19 02:03:29 +01002749
Johannes Bergb9454e82009-07-08 13:29:08 +02002750 err = nl80211_parse_key(info, &key);
2751 if (err)
2752 return err;
Johannes Berg41ade002007-12-19 02:03:29 +01002753
Johannes Bergb9454e82009-07-08 13:29:08 +02002754 if (!key.p.key)
Johannes Berg41ade002007-12-19 02:03:29 +01002755 return -EINVAL;
2756
Johannes Berg41ade002007-12-19 02:03:29 +01002757 if (info->attrs[NL80211_ATTR_MAC])
2758 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
2759
Johannes Berge31b8212010-10-05 19:39:30 +02002760 if (key.type == -1) {
2761 if (mac_addr)
2762 key.type = NL80211_KEYTYPE_PAIRWISE;
2763 else
2764 key.type = NL80211_KEYTYPE_GROUP;
2765 }
2766
2767 /* for now */
2768 if (key.type != NL80211_KEYTYPE_PAIRWISE &&
2769 key.type != NL80211_KEYTYPE_GROUP)
2770 return -EINVAL;
2771
Johannes Berg4c476992010-10-04 21:36:35 +02002772 if (!rdev->ops->add_key)
2773 return -EOPNOTSUPP;
Johannes Berg3b858752009-03-12 09:55:09 +01002774
Johannes Berge31b8212010-10-05 19:39:30 +02002775 if (cfg80211_validate_key_settings(rdev, &key.p, key.idx,
2776 key.type == NL80211_KEYTYPE_PAIRWISE,
2777 mac_addr))
Johannes Berg4c476992010-10-04 21:36:35 +02002778 return -EINVAL;
Johannes Bergfffd0932009-07-08 14:22:54 +02002779
2780 wdev_lock(dev->ieee80211_ptr);
2781 err = nl80211_key_allowed(dev->ieee80211_ptr);
2782 if (!err)
Hila Gonene35e4d22012-06-27 17:19:42 +03002783 err = rdev_add_key(rdev, dev, key.idx,
2784 key.type == NL80211_KEYTYPE_PAIRWISE,
2785 mac_addr, &key.p);
Johannes Bergfffd0932009-07-08 14:22:54 +02002786 wdev_unlock(dev->ieee80211_ptr);
Johannes Berg41ade002007-12-19 02:03:29 +01002787
Johannes Berg41ade002007-12-19 02:03:29 +01002788 return err;
2789}
2790
2791static int nl80211_del_key(struct sk_buff *skb, struct genl_info *info)
2792{
Johannes Berg4c476992010-10-04 21:36:35 +02002793 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Johannes Berg41ade002007-12-19 02:03:29 +01002794 int err;
Johannes Berg4c476992010-10-04 21:36:35 +02002795 struct net_device *dev = info->user_ptr[1];
Johannes Berg41ade002007-12-19 02:03:29 +01002796 u8 *mac_addr = NULL;
Johannes Bergb9454e82009-07-08 13:29:08 +02002797 struct key_parse key;
Johannes Berg41ade002007-12-19 02:03:29 +01002798
Johannes Bergb9454e82009-07-08 13:29:08 +02002799 err = nl80211_parse_key(info, &key);
2800 if (err)
2801 return err;
Johannes Berg41ade002007-12-19 02:03:29 +01002802
2803 if (info->attrs[NL80211_ATTR_MAC])
2804 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
2805
Johannes Berge31b8212010-10-05 19:39:30 +02002806 if (key.type == -1) {
2807 if (mac_addr)
2808 key.type = NL80211_KEYTYPE_PAIRWISE;
2809 else
2810 key.type = NL80211_KEYTYPE_GROUP;
2811 }
2812
2813 /* for now */
2814 if (key.type != NL80211_KEYTYPE_PAIRWISE &&
2815 key.type != NL80211_KEYTYPE_GROUP)
2816 return -EINVAL;
2817
Johannes Berg4c476992010-10-04 21:36:35 +02002818 if (!rdev->ops->del_key)
2819 return -EOPNOTSUPP;
Johannes Berg41ade002007-12-19 02:03:29 +01002820
Johannes Bergfffd0932009-07-08 14:22:54 +02002821 wdev_lock(dev->ieee80211_ptr);
2822 err = nl80211_key_allowed(dev->ieee80211_ptr);
Johannes Berge31b8212010-10-05 19:39:30 +02002823
Johannes Berg2811d3a2015-01-23 11:10:12 +01002824 if (key.type == NL80211_KEYTYPE_GROUP && mac_addr &&
Johannes Berge31b8212010-10-05 19:39:30 +02002825 !(rdev->wiphy.flags & WIPHY_FLAG_IBSS_RSN))
2826 err = -ENOENT;
2827
Johannes Bergfffd0932009-07-08 14:22:54 +02002828 if (!err)
Hila Gonene35e4d22012-06-27 17:19:42 +03002829 err = rdev_del_key(rdev, dev, key.idx,
2830 key.type == NL80211_KEYTYPE_PAIRWISE,
2831 mac_addr);
Johannes Berg41ade002007-12-19 02:03:29 +01002832
Johannes Berg3d23e342009-09-29 23:27:28 +02002833#ifdef CONFIG_CFG80211_WEXT
Johannes Berg08645122009-05-11 13:54:58 +02002834 if (!err) {
Johannes Bergb9454e82009-07-08 13:29:08 +02002835 if (key.idx == dev->ieee80211_ptr->wext.default_key)
Johannes Berg08645122009-05-11 13:54:58 +02002836 dev->ieee80211_ptr->wext.default_key = -1;
Johannes Bergb9454e82009-07-08 13:29:08 +02002837 else if (key.idx == dev->ieee80211_ptr->wext.default_mgmt_key)
Johannes Berg08645122009-05-11 13:54:58 +02002838 dev->ieee80211_ptr->wext.default_mgmt_key = -1;
2839 }
2840#endif
Johannes Bergfffd0932009-07-08 14:22:54 +02002841 wdev_unlock(dev->ieee80211_ptr);
Johannes Berg08645122009-05-11 13:54:58 +02002842
Johannes Berg41ade002007-12-19 02:03:29 +01002843 return err;
2844}
2845
Vasanthakumar Thiagarajan77765ea2013-01-18 11:18:45 +05302846/* This function returns an error or the number of nested attributes */
2847static int validate_acl_mac_addrs(struct nlattr *nl_attr)
2848{
2849 struct nlattr *attr;
2850 int n_entries = 0, tmp;
2851
2852 nla_for_each_nested(attr, nl_attr, tmp) {
2853 if (nla_len(attr) != ETH_ALEN)
2854 return -EINVAL;
2855
2856 n_entries++;
2857 }
2858
2859 return n_entries;
2860}
2861
2862/*
2863 * This function parses ACL information and allocates memory for ACL data.
2864 * On successful return, the calling function is responsible to free the
2865 * ACL buffer returned by this function.
2866 */
2867static struct cfg80211_acl_data *parse_acl_data(struct wiphy *wiphy,
2868 struct genl_info *info)
2869{
2870 enum nl80211_acl_policy acl_policy;
2871 struct nlattr *attr;
2872 struct cfg80211_acl_data *acl;
2873 int i = 0, n_entries, tmp;
2874
2875 if (!wiphy->max_acl_mac_addrs)
2876 return ERR_PTR(-EOPNOTSUPP);
2877
2878 if (!info->attrs[NL80211_ATTR_ACL_POLICY])
2879 return ERR_PTR(-EINVAL);
2880
2881 acl_policy = nla_get_u32(info->attrs[NL80211_ATTR_ACL_POLICY]);
2882 if (acl_policy != NL80211_ACL_POLICY_ACCEPT_UNLESS_LISTED &&
2883 acl_policy != NL80211_ACL_POLICY_DENY_UNLESS_LISTED)
2884 return ERR_PTR(-EINVAL);
2885
2886 if (!info->attrs[NL80211_ATTR_MAC_ADDRS])
2887 return ERR_PTR(-EINVAL);
2888
2889 n_entries = validate_acl_mac_addrs(info->attrs[NL80211_ATTR_MAC_ADDRS]);
2890 if (n_entries < 0)
2891 return ERR_PTR(n_entries);
2892
2893 if (n_entries > wiphy->max_acl_mac_addrs)
2894 return ERR_PTR(-ENOTSUPP);
2895
2896 acl = kzalloc(sizeof(*acl) + (sizeof(struct mac_address) * n_entries),
2897 GFP_KERNEL);
2898 if (!acl)
2899 return ERR_PTR(-ENOMEM);
2900
2901 nla_for_each_nested(attr, info->attrs[NL80211_ATTR_MAC_ADDRS], tmp) {
2902 memcpy(acl->mac_addrs[i].addr, nla_data(attr), ETH_ALEN);
2903 i++;
2904 }
2905
2906 acl->n_acl_entries = n_entries;
2907 acl->acl_policy = acl_policy;
2908
2909 return acl;
2910}
2911
2912static int nl80211_set_mac_acl(struct sk_buff *skb, struct genl_info *info)
2913{
2914 struct cfg80211_registered_device *rdev = info->user_ptr[0];
2915 struct net_device *dev = info->user_ptr[1];
2916 struct cfg80211_acl_data *acl;
2917 int err;
2918
2919 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
2920 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
2921 return -EOPNOTSUPP;
2922
2923 if (!dev->ieee80211_ptr->beacon_interval)
2924 return -EINVAL;
2925
2926 acl = parse_acl_data(&rdev->wiphy, info);
2927 if (IS_ERR(acl))
2928 return PTR_ERR(acl);
2929
2930 err = rdev_set_mac_acl(rdev, dev, acl);
2931
2932 kfree(acl);
2933
2934 return err;
2935}
2936
Johannes Berg88600202012-02-13 15:17:18 +01002937static int nl80211_parse_beacon(struct genl_info *info,
2938 struct cfg80211_beacon_data *bcn)
Johannes Berged1b6cc2007-12-19 02:03:32 +01002939{
Johannes Berg88600202012-02-13 15:17:18 +01002940 bool haveinfo = false;
Johannes Berged1b6cc2007-12-19 02:03:32 +01002941
Jouni Malinen9946ecf2011-08-10 23:55:56 +03002942 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_BEACON_TAIL]) ||
2943 !is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]) ||
2944 !is_valid_ie_attr(info->attrs[NL80211_ATTR_IE_PROBE_RESP]) ||
2945 !is_valid_ie_attr(info->attrs[NL80211_ATTR_IE_ASSOC_RESP]))
Johannes Bergf4a11bb2009-03-27 12:40:28 +01002946 return -EINVAL;
2947
Johannes Berg88600202012-02-13 15:17:18 +01002948 memset(bcn, 0, sizeof(*bcn));
Johannes Berged1b6cc2007-12-19 02:03:32 +01002949
Johannes Berged1b6cc2007-12-19 02:03:32 +01002950 if (info->attrs[NL80211_ATTR_BEACON_HEAD]) {
Johannes Berg88600202012-02-13 15:17:18 +01002951 bcn->head = nla_data(info->attrs[NL80211_ATTR_BEACON_HEAD]);
2952 bcn->head_len = nla_len(info->attrs[NL80211_ATTR_BEACON_HEAD]);
2953 if (!bcn->head_len)
2954 return -EINVAL;
2955 haveinfo = true;
Johannes Berged1b6cc2007-12-19 02:03:32 +01002956 }
2957
2958 if (info->attrs[NL80211_ATTR_BEACON_TAIL]) {
Johannes Berg88600202012-02-13 15:17:18 +01002959 bcn->tail = nla_data(info->attrs[NL80211_ATTR_BEACON_TAIL]);
2960 bcn->tail_len =
Johannes Berged1b6cc2007-12-19 02:03:32 +01002961 nla_len(info->attrs[NL80211_ATTR_BEACON_TAIL]);
Johannes Berg88600202012-02-13 15:17:18 +01002962 haveinfo = true;
Johannes Berged1b6cc2007-12-19 02:03:32 +01002963 }
2964
Johannes Berg4c476992010-10-04 21:36:35 +02002965 if (!haveinfo)
2966 return -EINVAL;
Johannes Berged1b6cc2007-12-19 02:03:32 +01002967
Jouni Malinen9946ecf2011-08-10 23:55:56 +03002968 if (info->attrs[NL80211_ATTR_IE]) {
Johannes Berg88600202012-02-13 15:17:18 +01002969 bcn->beacon_ies = nla_data(info->attrs[NL80211_ATTR_IE]);
2970 bcn->beacon_ies_len = nla_len(info->attrs[NL80211_ATTR_IE]);
Jouni Malinen9946ecf2011-08-10 23:55:56 +03002971 }
2972
2973 if (info->attrs[NL80211_ATTR_IE_PROBE_RESP]) {
Johannes Berg88600202012-02-13 15:17:18 +01002974 bcn->proberesp_ies =
Jouni Malinen9946ecf2011-08-10 23:55:56 +03002975 nla_data(info->attrs[NL80211_ATTR_IE_PROBE_RESP]);
Johannes Berg88600202012-02-13 15:17:18 +01002976 bcn->proberesp_ies_len =
Jouni Malinen9946ecf2011-08-10 23:55:56 +03002977 nla_len(info->attrs[NL80211_ATTR_IE_PROBE_RESP]);
2978 }
2979
2980 if (info->attrs[NL80211_ATTR_IE_ASSOC_RESP]) {
Johannes Berg88600202012-02-13 15:17:18 +01002981 bcn->assocresp_ies =
Jouni Malinen9946ecf2011-08-10 23:55:56 +03002982 nla_data(info->attrs[NL80211_ATTR_IE_ASSOC_RESP]);
Johannes Berg88600202012-02-13 15:17:18 +01002983 bcn->assocresp_ies_len =
Jouni Malinen9946ecf2011-08-10 23:55:56 +03002984 nla_len(info->attrs[NL80211_ATTR_IE_ASSOC_RESP]);
2985 }
2986
Arik Nemtsov00f740e2011-11-10 11:28:56 +02002987 if (info->attrs[NL80211_ATTR_PROBE_RESP]) {
Johannes Berg88600202012-02-13 15:17:18 +01002988 bcn->probe_resp =
Arik Nemtsov00f740e2011-11-10 11:28:56 +02002989 nla_data(info->attrs[NL80211_ATTR_PROBE_RESP]);
Johannes Berg88600202012-02-13 15:17:18 +01002990 bcn->probe_resp_len =
Arik Nemtsov00f740e2011-11-10 11:28:56 +02002991 nla_len(info->attrs[NL80211_ATTR_PROBE_RESP]);
2992 }
2993
Johannes Berg88600202012-02-13 15:17:18 +01002994 return 0;
2995}
2996
Felix Fietkau46c1dd02012-06-19 02:50:57 +02002997static bool nl80211_get_ap_channel(struct cfg80211_registered_device *rdev,
2998 struct cfg80211_ap_settings *params)
2999{
3000 struct wireless_dev *wdev;
3001 bool ret = false;
3002
3003 mutex_lock(&rdev->devlist_mtx);
3004
Johannes Berg89a54e42012-06-15 14:33:17 +02003005 list_for_each_entry(wdev, &rdev->wdev_list, list) {
Felix Fietkau46c1dd02012-06-19 02:50:57 +02003006 if (wdev->iftype != NL80211_IFTYPE_AP &&
3007 wdev->iftype != NL80211_IFTYPE_P2P_GO)
3008 continue;
3009
Johannes Berg683b6d32012-11-08 21:25:48 +01003010 if (!wdev->preset_chandef.chan)
Felix Fietkau46c1dd02012-06-19 02:50:57 +02003011 continue;
3012
Johannes Berg683b6d32012-11-08 21:25:48 +01003013 params->chandef = wdev->preset_chandef;
Felix Fietkau46c1dd02012-06-19 02:50:57 +02003014 ret = true;
3015 break;
3016 }
3017
3018 mutex_unlock(&rdev->devlist_mtx);
3019
3020 return ret;
3021}
3022
Jouni Malinene39e5b52012-09-30 19:29:39 +03003023static bool nl80211_valid_auth_type(struct cfg80211_registered_device *rdev,
3024 enum nl80211_auth_type auth_type,
3025 enum nl80211_commands cmd)
3026{
3027 if (auth_type > NL80211_AUTHTYPE_MAX)
3028 return false;
3029
3030 switch (cmd) {
3031 case NL80211_CMD_AUTHENTICATE:
3032 if (!(rdev->wiphy.features & NL80211_FEATURE_SAE) &&
3033 auth_type == NL80211_AUTHTYPE_SAE)
3034 return false;
3035 return true;
3036 case NL80211_CMD_CONNECT:
3037 case NL80211_CMD_START_AP:
3038 /* SAE not supported yet */
3039 if (auth_type == NL80211_AUTHTYPE_SAE)
3040 return false;
3041 return true;
3042 default:
3043 return false;
3044 }
3045}
3046
Johannes Berg88600202012-02-13 15:17:18 +01003047static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info)
3048{
3049 struct cfg80211_registered_device *rdev = info->user_ptr[0];
3050 struct net_device *dev = info->user_ptr[1];
3051 struct wireless_dev *wdev = dev->ieee80211_ptr;
3052 struct cfg80211_ap_settings params;
3053 int err;
Simon Wunderlich04f39042013-02-08 18:16:19 +01003054 u8 radar_detect_width = 0;
Johannes Berg88600202012-02-13 15:17:18 +01003055
3056 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
3057 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
3058 return -EOPNOTSUPP;
3059
3060 if (!rdev->ops->start_ap)
3061 return -EOPNOTSUPP;
3062
3063 if (wdev->beacon_interval)
3064 return -EALREADY;
3065
3066 memset(&params, 0, sizeof(params));
3067
3068 /* these are required for START_AP */
3069 if (!info->attrs[NL80211_ATTR_BEACON_INTERVAL] ||
3070 !info->attrs[NL80211_ATTR_DTIM_PERIOD] ||
3071 !info->attrs[NL80211_ATTR_BEACON_HEAD])
3072 return -EINVAL;
3073
3074 err = nl80211_parse_beacon(info, &params.beacon);
3075 if (err)
3076 return err;
3077
3078 params.beacon_interval =
3079 nla_get_u32(info->attrs[NL80211_ATTR_BEACON_INTERVAL]);
3080 params.dtim_period =
3081 nla_get_u32(info->attrs[NL80211_ATTR_DTIM_PERIOD]);
3082
3083 err = cfg80211_validate_beacon_int(rdev, params.beacon_interval);
3084 if (err)
3085 return err;
3086
3087 /*
3088 * In theory, some of these attributes should be required here
3089 * but since they were not used when the command was originally
3090 * added, keep them optional for old user space programs to let
3091 * them continue to work with drivers that do not need the
3092 * additional information -- drivers must check!
3093 */
3094 if (info->attrs[NL80211_ATTR_SSID]) {
3095 params.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
3096 params.ssid_len =
3097 nla_len(info->attrs[NL80211_ATTR_SSID]);
3098 if (params.ssid_len == 0 ||
3099 params.ssid_len > IEEE80211_MAX_SSID_LEN)
3100 return -EINVAL;
3101 }
3102
3103 if (info->attrs[NL80211_ATTR_HIDDEN_SSID]) {
3104 params.hidden_ssid = nla_get_u32(
3105 info->attrs[NL80211_ATTR_HIDDEN_SSID]);
3106 if (params.hidden_ssid != NL80211_HIDDEN_SSID_NOT_IN_USE &&
3107 params.hidden_ssid != NL80211_HIDDEN_SSID_ZERO_LEN &&
3108 params.hidden_ssid != NL80211_HIDDEN_SSID_ZERO_CONTENTS)
3109 return -EINVAL;
3110 }
3111
3112 params.privacy = !!info->attrs[NL80211_ATTR_PRIVACY];
3113
3114 if (info->attrs[NL80211_ATTR_AUTH_TYPE]) {
3115 params.auth_type = nla_get_u32(
3116 info->attrs[NL80211_ATTR_AUTH_TYPE]);
Jouni Malinene39e5b52012-09-30 19:29:39 +03003117 if (!nl80211_valid_auth_type(rdev, params.auth_type,
3118 NL80211_CMD_START_AP))
Johannes Berg88600202012-02-13 15:17:18 +01003119 return -EINVAL;
3120 } else
3121 params.auth_type = NL80211_AUTHTYPE_AUTOMATIC;
3122
3123 err = nl80211_crypto_settings(rdev, info, &params.crypto,
3124 NL80211_MAX_NR_CIPHER_SUITES);
3125 if (err)
3126 return err;
3127
Vasanthakumar Thiagarajan1b658f12012-03-02 15:50:02 +05303128 if (info->attrs[NL80211_ATTR_INACTIVITY_TIMEOUT]) {
3129 if (!(rdev->wiphy.features & NL80211_FEATURE_INACTIVITY_TIMER))
3130 return -EOPNOTSUPP;
3131 params.inactivity_timeout = nla_get_u16(
3132 info->attrs[NL80211_ATTR_INACTIVITY_TIMEOUT]);
3133 }
3134
Johannes Berg53cabad2012-11-14 15:17:28 +01003135 if (info->attrs[NL80211_ATTR_P2P_CTWINDOW]) {
3136 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
3137 return -EINVAL;
3138 params.p2p_ctwindow =
3139 nla_get_u8(info->attrs[NL80211_ATTR_P2P_CTWINDOW]);
3140 if (params.p2p_ctwindow > 127)
3141 return -EINVAL;
3142 if (params.p2p_ctwindow != 0 &&
3143 !(rdev->wiphy.features & NL80211_FEATURE_P2P_GO_CTWIN))
3144 return -EINVAL;
3145 }
3146
3147 if (info->attrs[NL80211_ATTR_P2P_OPPPS]) {
3148 u8 tmp;
3149
3150 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
3151 return -EINVAL;
3152 tmp = nla_get_u8(info->attrs[NL80211_ATTR_P2P_OPPPS]);
3153 if (tmp > 1)
3154 return -EINVAL;
3155 params.p2p_opp_ps = tmp;
3156 if (params.p2p_opp_ps != 0 &&
3157 !(rdev->wiphy.features & NL80211_FEATURE_P2P_GO_OPPPS))
3158 return -EINVAL;
3159 }
3160
Johannes Bergaa430da2012-05-16 23:50:18 +02003161 if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
Johannes Berg683b6d32012-11-08 21:25:48 +01003162 err = nl80211_parse_chandef(rdev, info, &params.chandef);
3163 if (err)
3164 return err;
3165 } else if (wdev->preset_chandef.chan) {
3166 params.chandef = wdev->preset_chandef;
Felix Fietkau46c1dd02012-06-19 02:50:57 +02003167 } else if (!nl80211_get_ap_channel(rdev, &params))
Johannes Bergaa430da2012-05-16 23:50:18 +02003168 return -EINVAL;
3169
Johannes Berg683b6d32012-11-08 21:25:48 +01003170 if (!cfg80211_reg_can_beacon(&rdev->wiphy, &params.chandef))
Johannes Bergaa430da2012-05-16 23:50:18 +02003171 return -EINVAL;
3172
Simon Wunderlich04f39042013-02-08 18:16:19 +01003173 err = cfg80211_chandef_dfs_required(wdev->wiphy, &params.chandef);
3174 if (err < 0)
3175 return err;
3176 if (err) {
3177 radar_detect_width = BIT(params.chandef.width);
3178 params.radar_required = true;
3179 }
3180
Michal Kaziore4e32452012-06-29 12:47:08 +02003181 mutex_lock(&rdev->devlist_mtx);
Simon Wunderlich04f39042013-02-08 18:16:19 +01003182 err = cfg80211_can_use_iftype_chan(rdev, wdev, wdev->iftype,
3183 params.chandef.chan,
3184 CHAN_MODE_SHARED,
3185 radar_detect_width);
Michal Kaziore4e32452012-06-29 12:47:08 +02003186 mutex_unlock(&rdev->devlist_mtx);
3187
3188 if (err)
3189 return err;
3190
Vasanthakumar Thiagarajan77765ea2013-01-18 11:18:45 +05303191 if (info->attrs[NL80211_ATTR_ACL_POLICY]) {
3192 params.acl = parse_acl_data(&rdev->wiphy, info);
3193 if (IS_ERR(params.acl))
3194 return PTR_ERR(params.acl);
3195 }
3196
Hila Gonene35e4d22012-06-27 17:19:42 +03003197 err = rdev_start_ap(rdev, dev, &params);
Felix Fietkau46c1dd02012-06-19 02:50:57 +02003198 if (!err) {
Johannes Berg683b6d32012-11-08 21:25:48 +01003199 wdev->preset_chandef = params.chandef;
Johannes Berg88600202012-02-13 15:17:18 +01003200 wdev->beacon_interval = params.beacon_interval;
Johannes Berg683b6d32012-11-08 21:25:48 +01003201 wdev->channel = params.chandef.chan;
Antonio Quartulli06e191e2012-11-07 12:52:19 +01003202 wdev->ssid_len = params.ssid_len;
3203 memcpy(wdev->ssid, params.ssid, wdev->ssid_len);
Felix Fietkau46c1dd02012-06-19 02:50:57 +02003204 }
Vasanthakumar Thiagarajan77765ea2013-01-18 11:18:45 +05303205
3206 kfree(params.acl);
3207
Johannes Berg56d18932011-05-09 18:41:15 +02003208 return err;
Johannes Berged1b6cc2007-12-19 02:03:32 +01003209}
3210
Johannes Berg88600202012-02-13 15:17:18 +01003211static int nl80211_set_beacon(struct sk_buff *skb, struct genl_info *info)
3212{
3213 struct cfg80211_registered_device *rdev = info->user_ptr[0];
3214 struct net_device *dev = info->user_ptr[1];
3215 struct wireless_dev *wdev = dev->ieee80211_ptr;
3216 struct cfg80211_beacon_data params;
3217 int err;
3218
3219 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
3220 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
3221 return -EOPNOTSUPP;
3222
3223 if (!rdev->ops->change_beacon)
3224 return -EOPNOTSUPP;
3225
3226 if (!wdev->beacon_interval)
3227 return -EINVAL;
3228
3229 err = nl80211_parse_beacon(info, &params);
3230 if (err)
3231 return err;
3232
Hila Gonene35e4d22012-06-27 17:19:42 +03003233 return rdev_change_beacon(rdev, dev, &params);
Johannes Berg88600202012-02-13 15:17:18 +01003234}
3235
3236static int nl80211_stop_ap(struct sk_buff *skb, struct genl_info *info)
Johannes Berged1b6cc2007-12-19 02:03:32 +01003237{
Johannes Berg4c476992010-10-04 21:36:35 +02003238 struct cfg80211_registered_device *rdev = info->user_ptr[0];
3239 struct net_device *dev = info->user_ptr[1];
Johannes Berged1b6cc2007-12-19 02:03:32 +01003240
Michal Kazior60771782012-06-29 12:46:56 +02003241 return cfg80211_stop_ap(rdev, dev);
Johannes Berged1b6cc2007-12-19 02:03:32 +01003242}
3243
Johannes Berg5727ef12007-12-19 02:03:34 +01003244static const struct nla_policy sta_flags_policy[NL80211_STA_FLAG_MAX + 1] = {
3245 [NL80211_STA_FLAG_AUTHORIZED] = { .type = NLA_FLAG },
3246 [NL80211_STA_FLAG_SHORT_PREAMBLE] = { .type = NLA_FLAG },
3247 [NL80211_STA_FLAG_WME] = { .type = NLA_FLAG },
Jouni Malinen0e467242009-05-11 21:57:55 +03003248 [NL80211_STA_FLAG_MFP] = { .type = NLA_FLAG },
Javier Cardonab39c48f2011-04-07 15:08:30 -07003249 [NL80211_STA_FLAG_AUTHENTICATED] = { .type = NLA_FLAG },
Johannes Bergd83023d2011-12-14 09:29:15 +01003250 [NL80211_STA_FLAG_TDLS_PEER] = { .type = NLA_FLAG },
Johannes Berg5727ef12007-12-19 02:03:34 +01003251};
3252
Johannes Bergeccb8e82009-05-11 21:57:56 +03003253static int parse_station_flags(struct genl_info *info,
Johannes Bergbdd3ae32012-01-02 13:30:03 +01003254 enum nl80211_iftype iftype,
Johannes Bergeccb8e82009-05-11 21:57:56 +03003255 struct station_parameters *params)
Johannes Berg5727ef12007-12-19 02:03:34 +01003256{
3257 struct nlattr *flags[NL80211_STA_FLAG_MAX + 1];
Johannes Bergeccb8e82009-05-11 21:57:56 +03003258 struct nlattr *nla;
Johannes Berg5727ef12007-12-19 02:03:34 +01003259 int flag;
3260
Johannes Bergeccb8e82009-05-11 21:57:56 +03003261 /*
3262 * Try parsing the new attribute first so userspace
3263 * can specify both for older kernels.
3264 */
3265 nla = info->attrs[NL80211_ATTR_STA_FLAGS2];
3266 if (nla) {
3267 struct nl80211_sta_flag_update *sta_flags;
Johannes Berg5727ef12007-12-19 02:03:34 +01003268
Johannes Bergeccb8e82009-05-11 21:57:56 +03003269 sta_flags = nla_data(nla);
3270 params->sta_flags_mask = sta_flags->mask;
3271 params->sta_flags_set = sta_flags->set;
Johannes Berg77ee7c82013-02-15 00:48:33 +01003272 params->sta_flags_set &= params->sta_flags_mask;
Johannes Bergeccb8e82009-05-11 21:57:56 +03003273 if ((params->sta_flags_mask |
3274 params->sta_flags_set) & BIT(__NL80211_STA_FLAG_INVALID))
3275 return -EINVAL;
3276 return 0;
3277 }
3278
3279 /* if present, parse the old attribute */
3280
3281 nla = info->attrs[NL80211_ATTR_STA_FLAGS];
Johannes Berg5727ef12007-12-19 02:03:34 +01003282 if (!nla)
3283 return 0;
3284
3285 if (nla_parse_nested(flags, NL80211_STA_FLAG_MAX,
3286 nla, sta_flags_policy))
3287 return -EINVAL;
3288
Johannes Bergbdd3ae32012-01-02 13:30:03 +01003289 /*
3290 * Only allow certain flags for interface types so that
3291 * other attributes are silently ignored. Remember that
3292 * this is backward compatibility code with old userspace
3293 * and shouldn't be hit in other cases anyway.
3294 */
3295 switch (iftype) {
3296 case NL80211_IFTYPE_AP:
3297 case NL80211_IFTYPE_AP_VLAN:
3298 case NL80211_IFTYPE_P2P_GO:
3299 params->sta_flags_mask = BIT(NL80211_STA_FLAG_AUTHORIZED) |
3300 BIT(NL80211_STA_FLAG_SHORT_PREAMBLE) |
3301 BIT(NL80211_STA_FLAG_WME) |
3302 BIT(NL80211_STA_FLAG_MFP);
3303 break;
3304 case NL80211_IFTYPE_P2P_CLIENT:
3305 case NL80211_IFTYPE_STATION:
3306 params->sta_flags_mask = BIT(NL80211_STA_FLAG_AUTHORIZED) |
3307 BIT(NL80211_STA_FLAG_TDLS_PEER);
3308 break;
3309 case NL80211_IFTYPE_MESH_POINT:
3310 params->sta_flags_mask = BIT(NL80211_STA_FLAG_AUTHENTICATED) |
3311 BIT(NL80211_STA_FLAG_MFP) |
3312 BIT(NL80211_STA_FLAG_AUTHORIZED);
3313 default:
3314 return -EINVAL;
3315 }
Johannes Berg5727ef12007-12-19 02:03:34 +01003316
Johannes Berg3383b5a2012-05-10 20:14:43 +02003317 for (flag = 1; flag <= NL80211_STA_FLAG_MAX; flag++) {
3318 if (flags[flag]) {
Johannes Bergeccb8e82009-05-11 21:57:56 +03003319 params->sta_flags_set |= (1<<flag);
Johannes Berg5727ef12007-12-19 02:03:34 +01003320
Johannes Berg3383b5a2012-05-10 20:14:43 +02003321 /* no longer support new API additions in old API */
3322 if (flag > NL80211_STA_FLAG_MAX_OLD_API)
3323 return -EINVAL;
3324 }
3325 }
3326
Johannes Berg5727ef12007-12-19 02:03:34 +01003327 return 0;
3328}
3329
Felix Fietkauc8dcfd82011-02-27 22:08:00 +01003330static bool nl80211_put_sta_rate(struct sk_buff *msg, struct rate_info *info,
3331 int attr)
3332{
3333 struct nlattr *rate;
Vladimir Kondratiev8eb41c82012-07-05 14:25:49 +03003334 u32 bitrate;
3335 u16 bitrate_compat;
Felix Fietkauc8dcfd82011-02-27 22:08:00 +01003336
3337 rate = nla_nest_start(msg, attr);
3338 if (!rate)
Johannes Bergdb9c64c2012-11-09 14:56:41 +01003339 return false;
Felix Fietkauc8dcfd82011-02-27 22:08:00 +01003340
3341 /* cfg80211_calculate_bitrate will return 0 for mcs >= 32 */
3342 bitrate = cfg80211_calculate_bitrate(info);
Vladimir Kondratiev8eb41c82012-07-05 14:25:49 +03003343 /* report 16-bit bitrate only if we can */
3344 bitrate_compat = bitrate < (1UL << 16) ? bitrate : 0;
Johannes Bergdb9c64c2012-11-09 14:56:41 +01003345 if (bitrate > 0 &&
3346 nla_put_u32(msg, NL80211_RATE_INFO_BITRATE32, bitrate))
3347 return false;
3348 if (bitrate_compat > 0 &&
3349 nla_put_u16(msg, NL80211_RATE_INFO_BITRATE, bitrate_compat))
3350 return false;
3351
3352 if (info->flags & RATE_INFO_FLAGS_MCS) {
3353 if (nla_put_u8(msg, NL80211_RATE_INFO_MCS, info->mcs))
3354 return false;
3355 if (info->flags & RATE_INFO_FLAGS_40_MHZ_WIDTH &&
3356 nla_put_flag(msg, NL80211_RATE_INFO_40_MHZ_WIDTH))
3357 return false;
3358 if (info->flags & RATE_INFO_FLAGS_SHORT_GI &&
3359 nla_put_flag(msg, NL80211_RATE_INFO_SHORT_GI))
3360 return false;
3361 } else if (info->flags & RATE_INFO_FLAGS_VHT_MCS) {
3362 if (nla_put_u8(msg, NL80211_RATE_INFO_VHT_MCS, info->mcs))
3363 return false;
3364 if (nla_put_u8(msg, NL80211_RATE_INFO_VHT_NSS, info->nss))
3365 return false;
3366 if (info->flags & RATE_INFO_FLAGS_40_MHZ_WIDTH &&
3367 nla_put_flag(msg, NL80211_RATE_INFO_40_MHZ_WIDTH))
3368 return false;
3369 if (info->flags & RATE_INFO_FLAGS_80_MHZ_WIDTH &&
3370 nla_put_flag(msg, NL80211_RATE_INFO_80_MHZ_WIDTH))
3371 return false;
3372 if (info->flags & RATE_INFO_FLAGS_80P80_MHZ_WIDTH &&
3373 nla_put_flag(msg, NL80211_RATE_INFO_80P80_MHZ_WIDTH))
3374 return false;
3375 if (info->flags & RATE_INFO_FLAGS_160_MHZ_WIDTH &&
3376 nla_put_flag(msg, NL80211_RATE_INFO_160_MHZ_WIDTH))
3377 return false;
3378 if (info->flags & RATE_INFO_FLAGS_SHORT_GI &&
3379 nla_put_flag(msg, NL80211_RATE_INFO_SHORT_GI))
3380 return false;
3381 }
Felix Fietkauc8dcfd82011-02-27 22:08:00 +01003382
3383 nla_nest_end(msg, rate);
3384 return true;
Felix Fietkauc8dcfd82011-02-27 22:08:00 +01003385}
3386
Eric W. Biederman15e47302012-09-07 20:12:54 +00003387static int nl80211_send_station(struct sk_buff *msg, u32 portid, u32 seq,
John W. Linville66266b32012-03-15 13:25:41 -04003388 int flags,
3389 struct cfg80211_registered_device *rdev,
3390 struct net_device *dev,
Johannes Berg98b62182009-12-23 13:15:44 +01003391 const u8 *mac_addr, struct station_info *sinfo)
Johannes Bergfd5b74d2007-12-19 02:03:36 +01003392{
3393 void *hdr;
Paul Stewartf4263c92011-03-31 09:25:41 -07003394 struct nlattr *sinfoattr, *bss_param;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01003395
Eric W. Biederman15e47302012-09-07 20:12:54 +00003396 hdr = nl80211hdr_put(msg, portid, seq, flags, NL80211_CMD_NEW_STATION);
Johannes Bergfd5b74d2007-12-19 02:03:36 +01003397 if (!hdr)
3398 return -1;
3399
David S. Miller9360ffd2012-03-29 04:41:26 -04003400 if (nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) ||
3401 nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr) ||
3402 nla_put_u32(msg, NL80211_ATTR_GENERATION, sinfo->generation))
3403 goto nla_put_failure;
Johannes Bergf5ea9122009-08-07 16:17:38 +02003404
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003405 sinfoattr = nla_nest_start(msg, NL80211_ATTR_STA_INFO);
3406 if (!sinfoattr)
Johannes Bergfd5b74d2007-12-19 02:03:36 +01003407 goto nla_put_failure;
David S. Miller9360ffd2012-03-29 04:41:26 -04003408 if ((sinfo->filled & STATION_INFO_CONNECTED_TIME) &&
3409 nla_put_u32(msg, NL80211_STA_INFO_CONNECTED_TIME,
3410 sinfo->connected_time))
3411 goto nla_put_failure;
3412 if ((sinfo->filled & STATION_INFO_INACTIVE_TIME) &&
3413 nla_put_u32(msg, NL80211_STA_INFO_INACTIVE_TIME,
3414 sinfo->inactive_time))
3415 goto nla_put_failure;
Vladimir Kondratiev42745e02013-02-04 13:53:11 +02003416 if ((sinfo->filled & (STATION_INFO_RX_BYTES |
3417 STATION_INFO_RX_BYTES64)) &&
David S. Miller9360ffd2012-03-29 04:41:26 -04003418 nla_put_u32(msg, NL80211_STA_INFO_RX_BYTES,
Vladimir Kondratiev42745e02013-02-04 13:53:11 +02003419 (u32)sinfo->rx_bytes))
3420 goto nla_put_failure;
3421 if ((sinfo->filled & (STATION_INFO_TX_BYTES |
Felix Fietkau4325d722013-05-23 15:05:59 +02003422 STATION_INFO_TX_BYTES64)) &&
Vladimir Kondratiev42745e02013-02-04 13:53:11 +02003423 nla_put_u32(msg, NL80211_STA_INFO_TX_BYTES,
3424 (u32)sinfo->tx_bytes))
3425 goto nla_put_failure;
3426 if ((sinfo->filled & STATION_INFO_RX_BYTES64) &&
3427 nla_put_u64(msg, NL80211_STA_INFO_RX_BYTES64,
David S. Miller9360ffd2012-03-29 04:41:26 -04003428 sinfo->rx_bytes))
3429 goto nla_put_failure;
Vladimir Kondratiev42745e02013-02-04 13:53:11 +02003430 if ((sinfo->filled & STATION_INFO_TX_BYTES64) &&
3431 nla_put_u64(msg, NL80211_STA_INFO_TX_BYTES64,
David S. Miller9360ffd2012-03-29 04:41:26 -04003432 sinfo->tx_bytes))
3433 goto nla_put_failure;
3434 if ((sinfo->filled & STATION_INFO_LLID) &&
3435 nla_put_u16(msg, NL80211_STA_INFO_LLID, sinfo->llid))
3436 goto nla_put_failure;
3437 if ((sinfo->filled & STATION_INFO_PLID) &&
3438 nla_put_u16(msg, NL80211_STA_INFO_PLID, sinfo->plid))
3439 goto nla_put_failure;
3440 if ((sinfo->filled & STATION_INFO_PLINK_STATE) &&
3441 nla_put_u8(msg, NL80211_STA_INFO_PLINK_STATE,
3442 sinfo->plink_state))
3443 goto nla_put_failure;
John W. Linville66266b32012-03-15 13:25:41 -04003444 switch (rdev->wiphy.signal_type) {
3445 case CFG80211_SIGNAL_TYPE_MBM:
David S. Miller9360ffd2012-03-29 04:41:26 -04003446 if ((sinfo->filled & STATION_INFO_SIGNAL) &&
3447 nla_put_u8(msg, NL80211_STA_INFO_SIGNAL,
3448 sinfo->signal))
3449 goto nla_put_failure;
3450 if ((sinfo->filled & STATION_INFO_SIGNAL_AVG) &&
3451 nla_put_u8(msg, NL80211_STA_INFO_SIGNAL_AVG,
3452 sinfo->signal_avg))
3453 goto nla_put_failure;
John W. Linville66266b32012-03-15 13:25:41 -04003454 break;
3455 default:
3456 break;
3457 }
Henning Rogge420e7fa2008-12-11 22:04:19 +01003458 if (sinfo->filled & STATION_INFO_TX_BITRATE) {
Felix Fietkauc8dcfd82011-02-27 22:08:00 +01003459 if (!nl80211_put_sta_rate(msg, &sinfo->txrate,
3460 NL80211_STA_INFO_TX_BITRATE))
Henning Rogge420e7fa2008-12-11 22:04:19 +01003461 goto nla_put_failure;
Felix Fietkauc8dcfd82011-02-27 22:08:00 +01003462 }
3463 if (sinfo->filled & STATION_INFO_RX_BITRATE) {
3464 if (!nl80211_put_sta_rate(msg, &sinfo->rxrate,
3465 NL80211_STA_INFO_RX_BITRATE))
3466 goto nla_put_failure;
Henning Rogge420e7fa2008-12-11 22:04:19 +01003467 }
David S. Miller9360ffd2012-03-29 04:41:26 -04003468 if ((sinfo->filled & STATION_INFO_RX_PACKETS) &&
3469 nla_put_u32(msg, NL80211_STA_INFO_RX_PACKETS,
3470 sinfo->rx_packets))
3471 goto nla_put_failure;
3472 if ((sinfo->filled & STATION_INFO_TX_PACKETS) &&
3473 nla_put_u32(msg, NL80211_STA_INFO_TX_PACKETS,
3474 sinfo->tx_packets))
3475 goto nla_put_failure;
3476 if ((sinfo->filled & STATION_INFO_TX_RETRIES) &&
3477 nla_put_u32(msg, NL80211_STA_INFO_TX_RETRIES,
3478 sinfo->tx_retries))
3479 goto nla_put_failure;
3480 if ((sinfo->filled & STATION_INFO_TX_FAILED) &&
3481 nla_put_u32(msg, NL80211_STA_INFO_TX_FAILED,
3482 sinfo->tx_failed))
3483 goto nla_put_failure;
3484 if ((sinfo->filled & STATION_INFO_BEACON_LOSS_COUNT) &&
3485 nla_put_u32(msg, NL80211_STA_INFO_BEACON_LOSS,
3486 sinfo->beacon_loss_count))
3487 goto nla_put_failure;
Marco Porsch3b1c5a52013-01-07 16:04:52 +01003488 if ((sinfo->filled & STATION_INFO_LOCAL_PM) &&
3489 nla_put_u32(msg, NL80211_STA_INFO_LOCAL_PM,
3490 sinfo->local_pm))
3491 goto nla_put_failure;
3492 if ((sinfo->filled & STATION_INFO_PEER_PM) &&
3493 nla_put_u32(msg, NL80211_STA_INFO_PEER_PM,
3494 sinfo->peer_pm))
3495 goto nla_put_failure;
3496 if ((sinfo->filled & STATION_INFO_NONPEER_PM) &&
3497 nla_put_u32(msg, NL80211_STA_INFO_NONPEER_PM,
3498 sinfo->nonpeer_pm))
3499 goto nla_put_failure;
Paul Stewartf4263c92011-03-31 09:25:41 -07003500 if (sinfo->filled & STATION_INFO_BSS_PARAM) {
3501 bss_param = nla_nest_start(msg, NL80211_STA_INFO_BSS_PARAM);
3502 if (!bss_param)
3503 goto nla_put_failure;
3504
David S. Miller9360ffd2012-03-29 04:41:26 -04003505 if (((sinfo->bss_param.flags & BSS_PARAM_FLAGS_CTS_PROT) &&
3506 nla_put_flag(msg, NL80211_STA_BSS_PARAM_CTS_PROT)) ||
3507 ((sinfo->bss_param.flags & BSS_PARAM_FLAGS_SHORT_PREAMBLE) &&
3508 nla_put_flag(msg, NL80211_STA_BSS_PARAM_SHORT_PREAMBLE)) ||
3509 ((sinfo->bss_param.flags & BSS_PARAM_FLAGS_SHORT_SLOT_TIME) &&
3510 nla_put_flag(msg, NL80211_STA_BSS_PARAM_SHORT_SLOT_TIME)) ||
3511 nla_put_u8(msg, NL80211_STA_BSS_PARAM_DTIM_PERIOD,
3512 sinfo->bss_param.dtim_period) ||
3513 nla_put_u16(msg, NL80211_STA_BSS_PARAM_BEACON_INTERVAL,
3514 sinfo->bss_param.beacon_interval))
3515 goto nla_put_failure;
Paul Stewartf4263c92011-03-31 09:25:41 -07003516
3517 nla_nest_end(msg, bss_param);
3518 }
David S. Miller9360ffd2012-03-29 04:41:26 -04003519 if ((sinfo->filled & STATION_INFO_STA_FLAGS) &&
3520 nla_put(msg, NL80211_STA_INFO_STA_FLAGS,
3521 sizeof(struct nl80211_sta_flag_update),
3522 &sinfo->sta_flags))
3523 goto nla_put_failure;
John W. Linville7eab0f62012-04-12 14:25:14 -04003524 if ((sinfo->filled & STATION_INFO_T_OFFSET) &&
3525 nla_put_u64(msg, NL80211_STA_INFO_T_OFFSET,
3526 sinfo->t_offset))
3527 goto nla_put_failure;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003528 nla_nest_end(msg, sinfoattr);
Johannes Bergfd5b74d2007-12-19 02:03:36 +01003529
David S. Miller9360ffd2012-03-29 04:41:26 -04003530 if ((sinfo->filled & STATION_INFO_ASSOC_REQ_IES) &&
3531 nla_put(msg, NL80211_ATTR_IE, sinfo->assoc_req_ies_len,
3532 sinfo->assoc_req_ies))
3533 goto nla_put_failure;
Jouni Malinen50d3dfb2011-08-08 12:11:52 +03003534
Johannes Bergfd5b74d2007-12-19 02:03:36 +01003535 return genlmsg_end(msg, hdr);
3536
3537 nla_put_failure:
Thomas Grafbc3ed282008-06-03 16:36:54 -07003538 genlmsg_cancel(msg, hdr);
3539 return -EMSGSIZE;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01003540}
3541
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003542static int nl80211_dump_station(struct sk_buff *skb,
Johannes Bergbba95fe2008-07-29 13:22:51 +02003543 struct netlink_callback *cb)
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003544{
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003545 struct station_info sinfo;
3546 struct cfg80211_registered_device *dev;
Johannes Berg97990a02013-04-19 01:02:55 +02003547 struct wireless_dev *wdev;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003548 u8 mac_addr[ETH_ALEN];
Johannes Berg97990a02013-04-19 01:02:55 +02003549 int sta_idx = cb->args[2];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003550 int err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003551
Johannes Berg97990a02013-04-19 01:02:55 +02003552 err = nl80211_prepare_wdev_dump(skb, cb, &dev, &wdev);
Johannes Berg67748892010-10-04 21:14:06 +02003553 if (err)
3554 return err;
Johannes Bergbba95fe2008-07-29 13:22:51 +02003555
Johannes Berg97990a02013-04-19 01:02:55 +02003556 if (!wdev->netdev) {
3557 err = -EINVAL;
3558 goto out_err;
3559 }
3560
Johannes Bergbba95fe2008-07-29 13:22:51 +02003561 if (!dev->ops->dump_station) {
Jouni Malineneec60b02009-03-20 21:21:19 +02003562 err = -EOPNOTSUPP;
Johannes Bergbba95fe2008-07-29 13:22:51 +02003563 goto out_err;
3564 }
3565
Johannes Bergbba95fe2008-07-29 13:22:51 +02003566 while (1) {
Jouni Malinenf612ced2011-08-11 11:46:22 +03003567 memset(&sinfo, 0, sizeof(sinfo));
Johannes Berg97990a02013-04-19 01:02:55 +02003568 err = rdev_dump_station(dev, wdev->netdev, sta_idx,
Hila Gonene35e4d22012-06-27 17:19:42 +03003569 mac_addr, &sinfo);
Johannes Bergbba95fe2008-07-29 13:22:51 +02003570 if (err == -ENOENT)
3571 break;
3572 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01003573 goto out_err;
Johannes Bergbba95fe2008-07-29 13:22:51 +02003574
3575 if (nl80211_send_station(skb,
Eric W. Biederman15e47302012-09-07 20:12:54 +00003576 NETLINK_CB(cb->skb).portid,
Johannes Bergbba95fe2008-07-29 13:22:51 +02003577 cb->nlh->nlmsg_seq, NLM_F_MULTI,
Johannes Berg97990a02013-04-19 01:02:55 +02003578 dev, wdev->netdev, mac_addr,
Johannes Bergbba95fe2008-07-29 13:22:51 +02003579 &sinfo) < 0)
3580 goto out;
3581
3582 sta_idx++;
3583 }
3584
3585
3586 out:
Johannes Berg97990a02013-04-19 01:02:55 +02003587 cb->args[2] = sta_idx;
Johannes Bergbba95fe2008-07-29 13:22:51 +02003588 err = skb->len;
Johannes Bergbba95fe2008-07-29 13:22:51 +02003589 out_err:
Johannes Berg97990a02013-04-19 01:02:55 +02003590 nl80211_finish_wdev_dump(dev);
Johannes Bergbba95fe2008-07-29 13:22:51 +02003591
3592 return err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003593}
Johannes Bergfd5b74d2007-12-19 02:03:36 +01003594
Johannes Berg5727ef12007-12-19 02:03:34 +01003595static int nl80211_get_station(struct sk_buff *skb, struct genl_info *info)
3596{
Johannes Berg4c476992010-10-04 21:36:35 +02003597 struct cfg80211_registered_device *rdev = info->user_ptr[0];
3598 struct net_device *dev = info->user_ptr[1];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003599 struct station_info sinfo;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01003600 struct sk_buff *msg;
3601 u8 *mac_addr = NULL;
Johannes Berg4c476992010-10-04 21:36:35 +02003602 int err;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01003603
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003604 memset(&sinfo, 0, sizeof(sinfo));
Johannes Bergfd5b74d2007-12-19 02:03:36 +01003605
3606 if (!info->attrs[NL80211_ATTR_MAC])
3607 return -EINVAL;
3608
3609 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
3610
Johannes Berg4c476992010-10-04 21:36:35 +02003611 if (!rdev->ops->get_station)
3612 return -EOPNOTSUPP;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01003613
Hila Gonene35e4d22012-06-27 17:19:42 +03003614 err = rdev_get_station(rdev, dev, mac_addr, &sinfo);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003615 if (err)
Johannes Berg4c476992010-10-04 21:36:35 +02003616 return err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003617
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07003618 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Bergfd5b74d2007-12-19 02:03:36 +01003619 if (!msg)
Johannes Berg4c476992010-10-04 21:36:35 +02003620 return -ENOMEM;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01003621
Eric W. Biederman15e47302012-09-07 20:12:54 +00003622 if (nl80211_send_station(msg, info->snd_portid, info->snd_seq, 0,
John W. Linville66266b32012-03-15 13:25:41 -04003623 rdev, dev, mac_addr, &sinfo) < 0) {
Johannes Berg4c476992010-10-04 21:36:35 +02003624 nlmsg_free(msg);
3625 return -ENOBUFS;
3626 }
Johannes Bergfd5b74d2007-12-19 02:03:36 +01003627
Johannes Berg4c476992010-10-04 21:36:35 +02003628 return genlmsg_reply(msg, info);
Johannes Berg5727ef12007-12-19 02:03:34 +01003629}
3630
Johannes Berg77ee7c82013-02-15 00:48:33 +01003631int cfg80211_check_station_change(struct wiphy *wiphy,
3632 struct station_parameters *params,
3633 enum cfg80211_station_type statype)
3634{
3635 if (params->listen_interval != -1)
3636 return -EINVAL;
3637 if (params->aid)
3638 return -EINVAL;
3639
3640 /* When you run into this, adjust the code below for the new flag */
3641 BUILD_BUG_ON(NL80211_STA_FLAG_MAX != 7);
3642
3643 switch (statype) {
Thomas Pederseneef941e2013-03-04 13:06:11 -08003644 case CFG80211_STA_MESH_PEER_KERNEL:
3645 case CFG80211_STA_MESH_PEER_USER:
Johannes Berg77ee7c82013-02-15 00:48:33 +01003646 /*
3647 * No ignoring the TDLS flag here -- the userspace mesh
3648 * code doesn't have the bug of including TDLS in the
3649 * mask everywhere.
3650 */
3651 if (params->sta_flags_mask &
3652 ~(BIT(NL80211_STA_FLAG_AUTHENTICATED) |
3653 BIT(NL80211_STA_FLAG_MFP) |
3654 BIT(NL80211_STA_FLAG_AUTHORIZED)))
3655 return -EINVAL;
3656 break;
3657 case CFG80211_STA_TDLS_PEER_SETUP:
3658 case CFG80211_STA_TDLS_PEER_ACTIVE:
3659 if (!(params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)))
3660 return -EINVAL;
3661 /* ignore since it can't change */
3662 params->sta_flags_mask &= ~BIT(NL80211_STA_FLAG_TDLS_PEER);
3663 break;
3664 default:
3665 /* disallow mesh-specific things */
3666 if (params->plink_action != NL80211_PLINK_ACTION_NO_ACTION)
3667 return -EINVAL;
3668 if (params->local_pm)
3669 return -EINVAL;
3670 if (params->sta_modify_mask & STATION_PARAM_APPLY_PLINK_STATE)
3671 return -EINVAL;
3672 }
3673
3674 if (statype != CFG80211_STA_TDLS_PEER_SETUP &&
3675 statype != CFG80211_STA_TDLS_PEER_ACTIVE) {
3676 /* TDLS can't be set, ... */
3677 if (params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))
3678 return -EINVAL;
3679 /*
3680 * ... but don't bother the driver with it. This works around
3681 * a hostapd/wpa_supplicant issue -- it always includes the
3682 * TLDS_PEER flag in the mask even for AP mode.
3683 */
3684 params->sta_flags_mask &= ~BIT(NL80211_STA_FLAG_TDLS_PEER);
3685 }
3686
3687 if (statype != CFG80211_STA_TDLS_PEER_SETUP) {
3688 /* reject other things that can't change */
3689 if (params->sta_modify_mask & STATION_PARAM_APPLY_UAPSD)
3690 return -EINVAL;
3691 if (params->sta_modify_mask & STATION_PARAM_APPLY_CAPABILITY)
3692 return -EINVAL;
3693 if (params->supported_rates)
3694 return -EINVAL;
3695 if (params->ext_capab || params->ht_capa || params->vht_capa)
3696 return -EINVAL;
3697 }
3698
3699 if (statype != CFG80211_STA_AP_CLIENT) {
3700 if (params->vlan)
3701 return -EINVAL;
3702 }
3703
3704 switch (statype) {
3705 case CFG80211_STA_AP_MLME_CLIENT:
3706 /* Use this only for authorizing/unauthorizing a station */
3707 if (!(params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED)))
3708 return -EOPNOTSUPP;
3709 break;
3710 case CFG80211_STA_AP_CLIENT:
3711 /* accept only the listed bits */
3712 if (params->sta_flags_mask &
3713 ~(BIT(NL80211_STA_FLAG_AUTHORIZED) |
3714 BIT(NL80211_STA_FLAG_AUTHENTICATED) |
3715 BIT(NL80211_STA_FLAG_ASSOCIATED) |
3716 BIT(NL80211_STA_FLAG_SHORT_PREAMBLE) |
3717 BIT(NL80211_STA_FLAG_WME) |
3718 BIT(NL80211_STA_FLAG_MFP)))
3719 return -EINVAL;
3720
3721 /* but authenticated/associated only if driver handles it */
3722 if (!(wiphy->features & NL80211_FEATURE_FULL_AP_CLIENT_STATE) &&
3723 params->sta_flags_mask &
3724 (BIT(NL80211_STA_FLAG_AUTHENTICATED) |
3725 BIT(NL80211_STA_FLAG_ASSOCIATED)))
3726 return -EINVAL;
3727 break;
3728 case CFG80211_STA_IBSS:
3729 case CFG80211_STA_AP_STA:
3730 /* reject any changes other than AUTHORIZED */
3731 if (params->sta_flags_mask & ~BIT(NL80211_STA_FLAG_AUTHORIZED))
3732 return -EINVAL;
3733 break;
3734 case CFG80211_STA_TDLS_PEER_SETUP:
3735 /* reject any changes other than AUTHORIZED or WME */
3736 if (params->sta_flags_mask & ~(BIT(NL80211_STA_FLAG_AUTHORIZED) |
3737 BIT(NL80211_STA_FLAG_WME)))
3738 return -EINVAL;
3739 /* force (at least) rates when authorizing */
3740 if (params->sta_flags_set & BIT(NL80211_STA_FLAG_AUTHORIZED) &&
3741 !params->supported_rates)
3742 return -EINVAL;
3743 break;
3744 case CFG80211_STA_TDLS_PEER_ACTIVE:
3745 /* reject any changes */
3746 return -EINVAL;
Thomas Pederseneef941e2013-03-04 13:06:11 -08003747 case CFG80211_STA_MESH_PEER_KERNEL:
Johannes Berg77ee7c82013-02-15 00:48:33 +01003748 if (params->sta_modify_mask & STATION_PARAM_APPLY_PLINK_STATE)
3749 return -EINVAL;
3750 break;
Thomas Pederseneef941e2013-03-04 13:06:11 -08003751 case CFG80211_STA_MESH_PEER_USER:
Johannes Berg77ee7c82013-02-15 00:48:33 +01003752 if (params->plink_action != NL80211_PLINK_ACTION_NO_ACTION)
3753 return -EINVAL;
3754 break;
3755 }
3756
3757 return 0;
3758}
3759EXPORT_SYMBOL(cfg80211_check_station_change);
3760
Johannes Berg5727ef12007-12-19 02:03:34 +01003761/*
Felix Fietkauc258d2d2009-11-11 17:23:31 +01003762 * Get vlan interface making sure it is running and on the right wiphy.
Johannes Berg5727ef12007-12-19 02:03:34 +01003763 */
Johannes Berg80b99892011-11-18 16:23:01 +01003764static struct net_device *get_vlan(struct genl_info *info,
3765 struct cfg80211_registered_device *rdev)
Johannes Berg5727ef12007-12-19 02:03:34 +01003766{
Johannes Berg463d0182009-07-14 00:33:35 +02003767 struct nlattr *vlanattr = info->attrs[NL80211_ATTR_STA_VLAN];
Johannes Berg80b99892011-11-18 16:23:01 +01003768 struct net_device *v;
3769 int ret;
Johannes Berg5727ef12007-12-19 02:03:34 +01003770
Johannes Berg80b99892011-11-18 16:23:01 +01003771 if (!vlanattr)
3772 return NULL;
3773
3774 v = dev_get_by_index(genl_info_net(info), nla_get_u32(vlanattr));
3775 if (!v)
3776 return ERR_PTR(-ENODEV);
3777
3778 if (!v->ieee80211_ptr || v->ieee80211_ptr->wiphy != &rdev->wiphy) {
3779 ret = -EINVAL;
3780 goto error;
Johannes Berg5727ef12007-12-19 02:03:34 +01003781 }
Johannes Berg80b99892011-11-18 16:23:01 +01003782
Johannes Berg77ee7c82013-02-15 00:48:33 +01003783 if (v->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN &&
3784 v->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
3785 v->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) {
3786 ret = -EINVAL;
3787 goto error;
3788 }
3789
Johannes Berg80b99892011-11-18 16:23:01 +01003790 if (!netif_running(v)) {
3791 ret = -ENETDOWN;
3792 goto error;
3793 }
3794
3795 return v;
3796 error:
3797 dev_put(v);
3798 return ERR_PTR(ret);
Johannes Berg5727ef12007-12-19 02:03:34 +01003799}
3800
Jouni Malinendf881292013-02-14 21:10:54 +02003801static struct nla_policy
3802nl80211_sta_wme_policy[NL80211_STA_WME_MAX + 1] __read_mostly = {
3803 [NL80211_STA_WME_UAPSD_QUEUES] = { .type = NLA_U8 },
3804 [NL80211_STA_WME_MAX_SP] = { .type = NLA_U8 },
3805};
3806
Johannes Bergff276692013-02-15 00:09:01 +01003807static int nl80211_parse_sta_wme(struct genl_info *info,
3808 struct station_parameters *params)
Jouni Malinendf881292013-02-14 21:10:54 +02003809{
Jouni Malinendf881292013-02-14 21:10:54 +02003810 struct nlattr *tb[NL80211_STA_WME_MAX + 1];
3811 struct nlattr *nla;
3812 int err;
3813
Jouni Malinendf881292013-02-14 21:10:54 +02003814 /* parse WME attributes if present */
3815 if (!info->attrs[NL80211_ATTR_STA_WME])
3816 return 0;
3817
3818 nla = info->attrs[NL80211_ATTR_STA_WME];
3819 err = nla_parse_nested(tb, NL80211_STA_WME_MAX, nla,
3820 nl80211_sta_wme_policy);
3821 if (err)
3822 return err;
3823
3824 if (tb[NL80211_STA_WME_UAPSD_QUEUES])
3825 params->uapsd_queues = nla_get_u8(
3826 tb[NL80211_STA_WME_UAPSD_QUEUES]);
3827 if (params->uapsd_queues & ~IEEE80211_WMM_IE_STA_QOSINFO_AC_MASK)
3828 return -EINVAL;
3829
3830 if (tb[NL80211_STA_WME_MAX_SP])
3831 params->max_sp = nla_get_u8(tb[NL80211_STA_WME_MAX_SP]);
3832
3833 if (params->max_sp & ~IEEE80211_WMM_IE_STA_QOSINFO_SP_MASK)
3834 return -EINVAL;
3835
3836 params->sta_modify_mask |= STATION_PARAM_APPLY_UAPSD;
3837
3838 return 0;
3839}
3840
Johannes Bergff276692013-02-15 00:09:01 +01003841static int nl80211_set_station_tdls(struct genl_info *info,
3842 struct station_parameters *params)
3843{
3844 /* Dummy STA entry gets updated once the peer capabilities are known */
3845 if (info->attrs[NL80211_ATTR_HT_CAPABILITY])
3846 params->ht_capa =
3847 nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]);
3848 if (info->attrs[NL80211_ATTR_VHT_CAPABILITY])
3849 params->vht_capa =
3850 nla_data(info->attrs[NL80211_ATTR_VHT_CAPABILITY]);
3851
3852 return nl80211_parse_sta_wme(info, params);
3853}
3854
Johannes Berg5727ef12007-12-19 02:03:34 +01003855static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
3856{
Johannes Berg4c476992010-10-04 21:36:35 +02003857 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Johannes Berg4c476992010-10-04 21:36:35 +02003858 struct net_device *dev = info->user_ptr[1];
Johannes Berg5727ef12007-12-19 02:03:34 +01003859 struct station_parameters params;
Johannes Berg77ee7c82013-02-15 00:48:33 +01003860 u8 *mac_addr;
3861 int err;
Johannes Berg5727ef12007-12-19 02:03:34 +01003862
3863 memset(&params, 0, sizeof(params));
3864
3865 params.listen_interval = -1;
3866
Johannes Berg77ee7c82013-02-15 00:48:33 +01003867 if (!rdev->ops->change_station)
3868 return -EOPNOTSUPP;
3869
Johannes Berg5727ef12007-12-19 02:03:34 +01003870 if (info->attrs[NL80211_ATTR_STA_AID])
3871 return -EINVAL;
3872
3873 if (!info->attrs[NL80211_ATTR_MAC])
3874 return -EINVAL;
3875
3876 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
3877
3878 if (info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]) {
3879 params.supported_rates =
3880 nla_data(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]);
3881 params.supported_rates_len =
3882 nla_len(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]);
3883 }
3884
Jouni Malinen9d62a982013-02-14 21:10:13 +02003885 if (info->attrs[NL80211_ATTR_STA_CAPABILITY]) {
3886 params.capability =
3887 nla_get_u16(info->attrs[NL80211_ATTR_STA_CAPABILITY]);
3888 params.sta_modify_mask |= STATION_PARAM_APPLY_CAPABILITY;
3889 }
3890
3891 if (info->attrs[NL80211_ATTR_STA_EXT_CAPABILITY]) {
3892 params.ext_capab =
3893 nla_data(info->attrs[NL80211_ATTR_STA_EXT_CAPABILITY]);
3894 params.ext_capab_len =
3895 nla_len(info->attrs[NL80211_ATTR_STA_EXT_CAPABILITY]);
3896 }
3897
Jouni Malinendf881292013-02-14 21:10:54 +02003898 if (info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL])
Johannes Bergba23d202012-12-27 17:32:09 +01003899 return -EINVAL;
Jouni Malinen36aedc92008-08-25 11:58:58 +03003900
Johannes Bergbdd3ae32012-01-02 13:30:03 +01003901 if (parse_station_flags(info, dev->ieee80211_ptr->iftype, &params))
Johannes Berg5727ef12007-12-19 02:03:34 +01003902 return -EINVAL;
3903
Johannes Bergf8bacc22013-02-14 23:27:01 +01003904 if (info->attrs[NL80211_ATTR_STA_PLINK_ACTION]) {
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003905 params.plink_action =
Johannes Bergf8bacc22013-02-14 23:27:01 +01003906 nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_ACTION]);
3907 if (params.plink_action >= NUM_NL80211_PLINK_ACTIONS)
3908 return -EINVAL;
3909 }
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003910
Johannes Bergf8bacc22013-02-14 23:27:01 +01003911 if (info->attrs[NL80211_ATTR_STA_PLINK_STATE]) {
Javier Cardona9c3990a2011-05-03 16:57:11 -07003912 params.plink_state =
Johannes Bergf8bacc22013-02-14 23:27:01 +01003913 nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_STATE]);
3914 if (params.plink_state >= NUM_NL80211_PLINK_STATES)
3915 return -EINVAL;
3916 params.sta_modify_mask |= STATION_PARAM_APPLY_PLINK_STATE;
3917 }
Javier Cardona9c3990a2011-05-03 16:57:11 -07003918
Marco Porsch3b1c5a52013-01-07 16:04:52 +01003919 if (info->attrs[NL80211_ATTR_LOCAL_MESH_POWER_MODE]) {
3920 enum nl80211_mesh_power_mode pm = nla_get_u32(
3921 info->attrs[NL80211_ATTR_LOCAL_MESH_POWER_MODE]);
3922
3923 if (pm <= NL80211_MESH_POWER_UNKNOWN ||
3924 pm > NL80211_MESH_POWER_MAX)
3925 return -EINVAL;
3926
3927 params.local_pm = pm;
3928 }
3929
Johannes Berg77ee7c82013-02-15 00:48:33 +01003930 /* Include parameters for TDLS peer (will check later) */
3931 err = nl80211_set_station_tdls(info, &params);
3932 if (err)
3933 return err;
3934
3935 params.vlan = get_vlan(info, rdev);
3936 if (IS_ERR(params.vlan))
3937 return PTR_ERR(params.vlan);
3938
Johannes Berga97f4422009-06-18 17:23:43 +02003939 switch (dev->ieee80211_ptr->iftype) {
3940 case NL80211_IFTYPE_AP:
3941 case NL80211_IFTYPE_AP_VLAN:
Johannes Berg074ac8d2010-09-16 14:58:22 +02003942 case NL80211_IFTYPE_P2P_GO:
Johannes Berg074ac8d2010-09-16 14:58:22 +02003943 case NL80211_IFTYPE_P2P_CLIENT:
Johannes Berga97f4422009-06-18 17:23:43 +02003944 case NL80211_IFTYPE_STATION:
Antonio Quartulli267335d2012-01-31 20:25:47 +01003945 case NL80211_IFTYPE_ADHOC:
Johannes Berga97f4422009-06-18 17:23:43 +02003946 case NL80211_IFTYPE_MESH_POINT:
Johannes Berga97f4422009-06-18 17:23:43 +02003947 break;
3948 default:
Johannes Berg77ee7c82013-02-15 00:48:33 +01003949 err = -EOPNOTSUPP;
3950 goto out_put_vlan;
Johannes Berg034d6552009-05-27 10:35:29 +02003951 }
3952
Johannes Berg77ee7c82013-02-15 00:48:33 +01003953 /* driver will call cfg80211_check_station_change() */
Hila Gonene35e4d22012-06-27 17:19:42 +03003954 err = rdev_change_station(rdev, dev, mac_addr, &params);
Johannes Berg5727ef12007-12-19 02:03:34 +01003955
Johannes Berg77ee7c82013-02-15 00:48:33 +01003956 out_put_vlan:
Johannes Berg5727ef12007-12-19 02:03:34 +01003957 if (params.vlan)
3958 dev_put(params.vlan);
Johannes Berg3b858752009-03-12 09:55:09 +01003959
Johannes Berg5727ef12007-12-19 02:03:34 +01003960 return err;
3961}
3962
3963static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
3964{
Johannes Berg4c476992010-10-04 21:36:35 +02003965 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Johannes Berg5727ef12007-12-19 02:03:34 +01003966 int err;
Johannes Berg4c476992010-10-04 21:36:35 +02003967 struct net_device *dev = info->user_ptr[1];
Johannes Berg5727ef12007-12-19 02:03:34 +01003968 struct station_parameters params;
3969 u8 *mac_addr = NULL;
3970
3971 memset(&params, 0, sizeof(params));
3972
Johannes Berg984c3112013-02-14 23:43:25 +01003973 if (!rdev->ops->add_station)
3974 return -EOPNOTSUPP;
3975
Johannes Berg5727ef12007-12-19 02:03:34 +01003976 if (!info->attrs[NL80211_ATTR_MAC])
3977 return -EINVAL;
3978
Johannes Berg5727ef12007-12-19 02:03:34 +01003979 if (!info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL])
3980 return -EINVAL;
3981
3982 if (!info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES])
3983 return -EINVAL;
3984
Thadeu Lima de Souza Cascardo0e956c12010-02-12 12:34:50 -02003985 if (!info->attrs[NL80211_ATTR_STA_AID])
3986 return -EINVAL;
3987
Johannes Berg5727ef12007-12-19 02:03:34 +01003988 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
3989 params.supported_rates =
3990 nla_data(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]);
3991 params.supported_rates_len =
3992 nla_len(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]);
3993 params.listen_interval =
3994 nla_get_u16(info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]);
Johannes Berg51b50fb2009-05-24 16:42:30 +02003995
Thadeu Lima de Souza Cascardo0e956c12010-02-12 12:34:50 -02003996 params.aid = nla_get_u16(info->attrs[NL80211_ATTR_STA_AID]);
3997 if (!params.aid || params.aid > IEEE80211_MAX_AID)
3998 return -EINVAL;
Johannes Berg51b50fb2009-05-24 16:42:30 +02003999
Jouni Malinen9d62a982013-02-14 21:10:13 +02004000 if (info->attrs[NL80211_ATTR_STA_CAPABILITY]) {
4001 params.capability =
4002 nla_get_u16(info->attrs[NL80211_ATTR_STA_CAPABILITY]);
4003 params.sta_modify_mask |= STATION_PARAM_APPLY_CAPABILITY;
4004 }
4005
4006 if (info->attrs[NL80211_ATTR_STA_EXT_CAPABILITY]) {
4007 params.ext_capab =
4008 nla_data(info->attrs[NL80211_ATTR_STA_EXT_CAPABILITY]);
4009 params.ext_capab_len =
4010 nla_len(info->attrs[NL80211_ATTR_STA_EXT_CAPABILITY]);
4011 }
4012
Jouni Malinen36aedc92008-08-25 11:58:58 +03004013 if (info->attrs[NL80211_ATTR_HT_CAPABILITY])
4014 params.ht_capa =
4015 nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]);
Johannes Berg5727ef12007-12-19 02:03:34 +01004016
Mahesh Palivelaf461be3e2012-10-11 08:04:52 +00004017 if (info->attrs[NL80211_ATTR_VHT_CAPABILITY])
4018 params.vht_capa =
4019 nla_data(info->attrs[NL80211_ATTR_VHT_CAPABILITY]);
4020
Johannes Bergf8bacc22013-02-14 23:27:01 +01004021 if (info->attrs[NL80211_ATTR_STA_PLINK_ACTION]) {
Javier Cardona96b78df2011-04-07 15:08:33 -07004022 params.plink_action =
Johannes Bergf8bacc22013-02-14 23:27:01 +01004023 nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_ACTION]);
4024 if (params.plink_action >= NUM_NL80211_PLINK_ACTIONS)
4025 return -EINVAL;
4026 }
Javier Cardona96b78df2011-04-07 15:08:33 -07004027
Johannes Bergff276692013-02-15 00:09:01 +01004028 err = nl80211_parse_sta_wme(info, &params);
4029 if (err)
4030 return err;
Johannes Bergbdd90d52011-12-14 12:20:27 +01004031
Johannes Bergbdd3ae32012-01-02 13:30:03 +01004032 if (parse_station_flags(info, dev->ieee80211_ptr->iftype, &params))
Johannes Berg5727ef12007-12-19 02:03:34 +01004033 return -EINVAL;
4034
Johannes Berg9de9cea2015-03-12 08:53:27 +02004035 /* HT/VHT requires QoS, but if we don't have that just ignore HT/VHT
4036 * as userspace might just pass through the capabilities from the IEs
4037 * directly, rather than enforcing this restriction and returning an
4038 * error in this case.
4039 */
4040 if (!(params.sta_flags_set & BIT(NL80211_STA_FLAG_WME))) {
4041 params.ht_capa = NULL;
4042 params.vht_capa = NULL;
4043 }
4044
Johannes Berg77ee7c82013-02-15 00:48:33 +01004045 /* When you run into this, adjust the code below for the new flag */
4046 BUILD_BUG_ON(NL80211_STA_FLAG_MAX != 7);
4047
Johannes Bergbdd90d52011-12-14 12:20:27 +01004048 switch (dev->ieee80211_ptr->iftype) {
4049 case NL80211_IFTYPE_AP:
4050 case NL80211_IFTYPE_AP_VLAN:
4051 case NL80211_IFTYPE_P2P_GO:
Johannes Berg984c3112013-02-14 23:43:25 +01004052 /* ignore WME attributes if iface/sta is not capable */
4053 if (!(rdev->wiphy.flags & WIPHY_FLAG_AP_UAPSD) ||
4054 !(params.sta_flags_set & BIT(NL80211_STA_FLAG_WME)))
4055 params.sta_modify_mask &= ~STATION_PARAM_APPLY_UAPSD;
Eliad Pellerc75786c2011-08-23 14:37:46 +03004056
Johannes Bergbdd90d52011-12-14 12:20:27 +01004057 /* TDLS peers cannot be added */
4058 if (params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))
Johannes Berg4319e192011-09-07 11:50:48 +02004059 return -EINVAL;
Johannes Bergbdd90d52011-12-14 12:20:27 +01004060 /* but don't bother the driver with it */
4061 params.sta_flags_mask &= ~BIT(NL80211_STA_FLAG_TDLS_PEER);
Eliad Pellerc75786c2011-08-23 14:37:46 +03004062
Johannes Bergd582cff2012-10-26 17:53:44 +02004063 /* allow authenticated/associated only if driver handles it */
4064 if (!(rdev->wiphy.features &
4065 NL80211_FEATURE_FULL_AP_CLIENT_STATE) &&
4066 params.sta_flags_mask &
4067 (BIT(NL80211_STA_FLAG_AUTHENTICATED) |
4068 BIT(NL80211_STA_FLAG_ASSOCIATED)))
4069 return -EINVAL;
4070
Johannes Bergbdd90d52011-12-14 12:20:27 +01004071 /* must be last in here for error handling */
4072 params.vlan = get_vlan(info, rdev);
4073 if (IS_ERR(params.vlan))
4074 return PTR_ERR(params.vlan);
4075 break;
4076 case NL80211_IFTYPE_MESH_POINT:
Johannes Berg984c3112013-02-14 23:43:25 +01004077 /* ignore uAPSD data */
4078 params.sta_modify_mask &= ~STATION_PARAM_APPLY_UAPSD;
4079
Johannes Bergd582cff2012-10-26 17:53:44 +02004080 /* associated is disallowed */
4081 if (params.sta_flags_mask & BIT(NL80211_STA_FLAG_ASSOCIATED))
4082 return -EINVAL;
Johannes Bergbdd90d52011-12-14 12:20:27 +01004083 /* TDLS peers cannot be added */
4084 if (params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))
Johannes Berg4319e192011-09-07 11:50:48 +02004085 return -EINVAL;
Johannes Bergbdd90d52011-12-14 12:20:27 +01004086 break;
4087 case NL80211_IFTYPE_STATION:
Johannes Berg93d08f02013-03-04 09:29:46 +01004088 case NL80211_IFTYPE_P2P_CLIENT:
Johannes Berg984c3112013-02-14 23:43:25 +01004089 /* ignore uAPSD data */
4090 params.sta_modify_mask &= ~STATION_PARAM_APPLY_UAPSD;
4091
Johannes Berg77ee7c82013-02-15 00:48:33 +01004092 /* these are disallowed */
4093 if (params.sta_flags_mask &
4094 (BIT(NL80211_STA_FLAG_ASSOCIATED) |
4095 BIT(NL80211_STA_FLAG_AUTHENTICATED)))
Johannes Bergd582cff2012-10-26 17:53:44 +02004096 return -EINVAL;
Johannes Bergbdd90d52011-12-14 12:20:27 +01004097 /* Only TDLS peers can be added */
4098 if (!(params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)))
4099 return -EINVAL;
4100 /* Can only add if TDLS ... */
4101 if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS))
4102 return -EOPNOTSUPP;
4103 /* ... with external setup is supported */
4104 if (!(rdev->wiphy.flags & WIPHY_FLAG_TDLS_EXTERNAL_SETUP))
4105 return -EOPNOTSUPP;
Johannes Berg77ee7c82013-02-15 00:48:33 +01004106 /*
4107 * Older wpa_supplicant versions always mark the TDLS peer
4108 * as authorized, but it shouldn't yet be.
4109 */
4110 params.sta_flags_mask &= ~BIT(NL80211_STA_FLAG_AUTHORIZED);
Johannes Bergbdd90d52011-12-14 12:20:27 +01004111 break;
4112 default:
4113 return -EOPNOTSUPP;
Eliad Pellerc75786c2011-08-23 14:37:46 +03004114 }
4115
Johannes Bergbdd90d52011-12-14 12:20:27 +01004116 /* be aware of params.vlan when changing code here */
Johannes Berg5727ef12007-12-19 02:03:34 +01004117
Hila Gonene35e4d22012-06-27 17:19:42 +03004118 err = rdev_add_station(rdev, dev, mac_addr, &params);
Johannes Berg5727ef12007-12-19 02:03:34 +01004119
Johannes Berg5727ef12007-12-19 02:03:34 +01004120 if (params.vlan)
4121 dev_put(params.vlan);
Johannes Berg5727ef12007-12-19 02:03:34 +01004122 return err;
4123}
4124
4125static int nl80211_del_station(struct sk_buff *skb, struct genl_info *info)
4126{
Johannes Berg4c476992010-10-04 21:36:35 +02004127 struct cfg80211_registered_device *rdev = info->user_ptr[0];
4128 struct net_device *dev = info->user_ptr[1];
Johannes Berg5727ef12007-12-19 02:03:34 +01004129 u8 *mac_addr = NULL;
4130
4131 if (info->attrs[NL80211_ATTR_MAC])
4132 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
4133
Johannes Berge80cf852009-05-11 14:43:13 +02004134 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
Marco Porschd5d9de02010-03-30 10:00:16 +02004135 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN &&
Johannes Berg074ac8d2010-09-16 14:58:22 +02004136 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT &&
Johannes Berg4c476992010-10-04 21:36:35 +02004137 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
4138 return -EINVAL;
Johannes Berge80cf852009-05-11 14:43:13 +02004139
Johannes Berg4c476992010-10-04 21:36:35 +02004140 if (!rdev->ops->del_station)
4141 return -EOPNOTSUPP;
Johannes Berg5727ef12007-12-19 02:03:34 +01004142
Hila Gonene35e4d22012-06-27 17:19:42 +03004143 return rdev_del_station(rdev, dev, mac_addr);
Johannes Berg5727ef12007-12-19 02:03:34 +01004144}
4145
Eric W. Biederman15e47302012-09-07 20:12:54 +00004146static int nl80211_send_mpath(struct sk_buff *msg, u32 portid, u32 seq,
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01004147 int flags, struct net_device *dev,
4148 u8 *dst, u8 *next_hop,
4149 struct mpath_info *pinfo)
4150{
4151 void *hdr;
4152 struct nlattr *pinfoattr;
4153
Eric W. Biederman15e47302012-09-07 20:12:54 +00004154 hdr = nl80211hdr_put(msg, portid, seq, flags, NL80211_CMD_NEW_STATION);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01004155 if (!hdr)
4156 return -1;
4157
David S. Miller9360ffd2012-03-29 04:41:26 -04004158 if (nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) ||
4159 nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, dst) ||
4160 nla_put(msg, NL80211_ATTR_MPATH_NEXT_HOP, ETH_ALEN, next_hop) ||
4161 nla_put_u32(msg, NL80211_ATTR_GENERATION, pinfo->generation))
4162 goto nla_put_failure;
Johannes Bergf5ea9122009-08-07 16:17:38 +02004163
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01004164 pinfoattr = nla_nest_start(msg, NL80211_ATTR_MPATH_INFO);
4165 if (!pinfoattr)
4166 goto nla_put_failure;
David S. Miller9360ffd2012-03-29 04:41:26 -04004167 if ((pinfo->filled & MPATH_INFO_FRAME_QLEN) &&
4168 nla_put_u32(msg, NL80211_MPATH_INFO_FRAME_QLEN,
4169 pinfo->frame_qlen))
4170 goto nla_put_failure;
4171 if (((pinfo->filled & MPATH_INFO_SN) &&
4172 nla_put_u32(msg, NL80211_MPATH_INFO_SN, pinfo->sn)) ||
4173 ((pinfo->filled & MPATH_INFO_METRIC) &&
4174 nla_put_u32(msg, NL80211_MPATH_INFO_METRIC,
4175 pinfo->metric)) ||
4176 ((pinfo->filled & MPATH_INFO_EXPTIME) &&
4177 nla_put_u32(msg, NL80211_MPATH_INFO_EXPTIME,
4178 pinfo->exptime)) ||
4179 ((pinfo->filled & MPATH_INFO_FLAGS) &&
4180 nla_put_u8(msg, NL80211_MPATH_INFO_FLAGS,
4181 pinfo->flags)) ||
4182 ((pinfo->filled & MPATH_INFO_DISCOVERY_TIMEOUT) &&
4183 nla_put_u32(msg, NL80211_MPATH_INFO_DISCOVERY_TIMEOUT,
4184 pinfo->discovery_timeout)) ||
4185 ((pinfo->filled & MPATH_INFO_DISCOVERY_RETRIES) &&
4186 nla_put_u8(msg, NL80211_MPATH_INFO_DISCOVERY_RETRIES,
4187 pinfo->discovery_retries)))
4188 goto nla_put_failure;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01004189
4190 nla_nest_end(msg, pinfoattr);
4191
4192 return genlmsg_end(msg, hdr);
4193
4194 nla_put_failure:
Thomas Grafbc3ed282008-06-03 16:36:54 -07004195 genlmsg_cancel(msg, hdr);
4196 return -EMSGSIZE;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01004197}
4198
4199static int nl80211_dump_mpath(struct sk_buff *skb,
Johannes Bergbba95fe2008-07-29 13:22:51 +02004200 struct netlink_callback *cb)
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01004201{
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01004202 struct mpath_info pinfo;
4203 struct cfg80211_registered_device *dev;
Johannes Berg97990a02013-04-19 01:02:55 +02004204 struct wireless_dev *wdev;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01004205 u8 dst[ETH_ALEN];
4206 u8 next_hop[ETH_ALEN];
Johannes Berg97990a02013-04-19 01:02:55 +02004207 int path_idx = cb->args[2];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01004208 int err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01004209
Johannes Berg97990a02013-04-19 01:02:55 +02004210 err = nl80211_prepare_wdev_dump(skb, cb, &dev, &wdev);
Johannes Berg67748892010-10-04 21:14:06 +02004211 if (err)
4212 return err;
Johannes Bergbba95fe2008-07-29 13:22:51 +02004213
4214 if (!dev->ops->dump_mpath) {
Jouni Malineneec60b02009-03-20 21:21:19 +02004215 err = -EOPNOTSUPP;
Johannes Bergbba95fe2008-07-29 13:22:51 +02004216 goto out_err;
4217 }
4218
Johannes Berg97990a02013-04-19 01:02:55 +02004219 if (wdev->iftype != NL80211_IFTYPE_MESH_POINT) {
Jouni Malineneec60b02009-03-20 21:21:19 +02004220 err = -EOPNOTSUPP;
Roel Kluin0448b5f2009-08-22 21:15:49 +02004221 goto out_err;
Jouni Malineneec60b02009-03-20 21:21:19 +02004222 }
4223
Johannes Bergbba95fe2008-07-29 13:22:51 +02004224 while (1) {
Johannes Berg97990a02013-04-19 01:02:55 +02004225 err = rdev_dump_mpath(dev, wdev->netdev, path_idx, dst,
4226 next_hop, &pinfo);
Johannes Bergbba95fe2008-07-29 13:22:51 +02004227 if (err == -ENOENT)
4228 break;
4229 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01004230 goto out_err;
Johannes Bergbba95fe2008-07-29 13:22:51 +02004231
Eric W. Biederman15e47302012-09-07 20:12:54 +00004232 if (nl80211_send_mpath(skb, NETLINK_CB(cb->skb).portid,
Johannes Bergbba95fe2008-07-29 13:22:51 +02004233 cb->nlh->nlmsg_seq, NLM_F_MULTI,
Johannes Berg97990a02013-04-19 01:02:55 +02004234 wdev->netdev, dst, next_hop,
Johannes Bergbba95fe2008-07-29 13:22:51 +02004235 &pinfo) < 0)
4236 goto out;
4237
4238 path_idx++;
4239 }
4240
4241
4242 out:
Johannes Berg97990a02013-04-19 01:02:55 +02004243 cb->args[2] = path_idx;
Johannes Bergbba95fe2008-07-29 13:22:51 +02004244 err = skb->len;
Johannes Bergbba95fe2008-07-29 13:22:51 +02004245 out_err:
Johannes Berg97990a02013-04-19 01:02:55 +02004246 nl80211_finish_wdev_dump(dev);
Johannes Bergbba95fe2008-07-29 13:22:51 +02004247 return err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01004248}
4249
4250static int nl80211_get_mpath(struct sk_buff *skb, struct genl_info *info)
4251{
Johannes Berg4c476992010-10-04 21:36:35 +02004252 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01004253 int err;
Johannes Berg4c476992010-10-04 21:36:35 +02004254 struct net_device *dev = info->user_ptr[1];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01004255 struct mpath_info pinfo;
4256 struct sk_buff *msg;
4257 u8 *dst = NULL;
4258 u8 next_hop[ETH_ALEN];
4259
4260 memset(&pinfo, 0, sizeof(pinfo));
4261
4262 if (!info->attrs[NL80211_ATTR_MAC])
4263 return -EINVAL;
4264
4265 dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
4266
Johannes Berg4c476992010-10-04 21:36:35 +02004267 if (!rdev->ops->get_mpath)
4268 return -EOPNOTSUPP;
Johannes Berg3b858752009-03-12 09:55:09 +01004269
Johannes Berg4c476992010-10-04 21:36:35 +02004270 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT)
4271 return -EOPNOTSUPP;
Jouni Malineneec60b02009-03-20 21:21:19 +02004272
Hila Gonene35e4d22012-06-27 17:19:42 +03004273 err = rdev_get_mpath(rdev, dev, dst, next_hop, &pinfo);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01004274 if (err)
Johannes Berg4c476992010-10-04 21:36:35 +02004275 return err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01004276
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07004277 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01004278 if (!msg)
Johannes Berg4c476992010-10-04 21:36:35 +02004279 return -ENOMEM;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01004280
Eric W. Biederman15e47302012-09-07 20:12:54 +00004281 if (nl80211_send_mpath(msg, info->snd_portid, info->snd_seq, 0,
Johannes Berg4c476992010-10-04 21:36:35 +02004282 dev, dst, next_hop, &pinfo) < 0) {
4283 nlmsg_free(msg);
4284 return -ENOBUFS;
4285 }
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01004286
Johannes Berg4c476992010-10-04 21:36:35 +02004287 return genlmsg_reply(msg, info);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01004288}
4289
4290static int nl80211_set_mpath(struct sk_buff *skb, struct genl_info *info)
4291{
Johannes Berg4c476992010-10-04 21:36:35 +02004292 struct cfg80211_registered_device *rdev = info->user_ptr[0];
4293 struct net_device *dev = info->user_ptr[1];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01004294 u8 *dst = NULL;
4295 u8 *next_hop = NULL;
4296
4297 if (!info->attrs[NL80211_ATTR_MAC])
4298 return -EINVAL;
4299
4300 if (!info->attrs[NL80211_ATTR_MPATH_NEXT_HOP])
4301 return -EINVAL;
4302
4303 dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
4304 next_hop = nla_data(info->attrs[NL80211_ATTR_MPATH_NEXT_HOP]);
4305
Johannes Berg4c476992010-10-04 21:36:35 +02004306 if (!rdev->ops->change_mpath)
4307 return -EOPNOTSUPP;
Johannes Berg3b858752009-03-12 09:55:09 +01004308
Johannes Berg4c476992010-10-04 21:36:35 +02004309 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT)
4310 return -EOPNOTSUPP;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01004311
Hila Gonene35e4d22012-06-27 17:19:42 +03004312 return rdev_change_mpath(rdev, dev, dst, next_hop);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01004313}
Johannes Berg4c476992010-10-04 21:36:35 +02004314
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01004315static int nl80211_new_mpath(struct sk_buff *skb, struct genl_info *info)
4316{
Johannes Berg4c476992010-10-04 21:36:35 +02004317 struct cfg80211_registered_device *rdev = info->user_ptr[0];
4318 struct net_device *dev = info->user_ptr[1];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01004319 u8 *dst = NULL;
4320 u8 *next_hop = NULL;
4321
4322 if (!info->attrs[NL80211_ATTR_MAC])
4323 return -EINVAL;
4324
4325 if (!info->attrs[NL80211_ATTR_MPATH_NEXT_HOP])
4326 return -EINVAL;
4327
4328 dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
4329 next_hop = nla_data(info->attrs[NL80211_ATTR_MPATH_NEXT_HOP]);
4330
Johannes Berg4c476992010-10-04 21:36:35 +02004331 if (!rdev->ops->add_mpath)
4332 return -EOPNOTSUPP;
Johannes Berg3b858752009-03-12 09:55:09 +01004333
Johannes Berg4c476992010-10-04 21:36:35 +02004334 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT)
4335 return -EOPNOTSUPP;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01004336
Hila Gonene35e4d22012-06-27 17:19:42 +03004337 return rdev_add_mpath(rdev, dev, dst, next_hop);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01004338}
4339
4340static int nl80211_del_mpath(struct sk_buff *skb, struct genl_info *info)
4341{
Johannes Berg4c476992010-10-04 21:36:35 +02004342 struct cfg80211_registered_device *rdev = info->user_ptr[0];
4343 struct net_device *dev = info->user_ptr[1];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01004344 u8 *dst = NULL;
4345
4346 if (info->attrs[NL80211_ATTR_MAC])
4347 dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
4348
Johannes Berg4c476992010-10-04 21:36:35 +02004349 if (!rdev->ops->del_mpath)
4350 return -EOPNOTSUPP;
Johannes Berg3b858752009-03-12 09:55:09 +01004351
Hila Gonene35e4d22012-06-27 17:19:42 +03004352 return rdev_del_mpath(rdev, dev, dst);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01004353}
4354
Jouni Malinen9f1ba902008-08-07 20:07:01 +03004355static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info)
4356{
Johannes Berg4c476992010-10-04 21:36:35 +02004357 struct cfg80211_registered_device *rdev = info->user_ptr[0];
4358 struct net_device *dev = info->user_ptr[1];
Jouni Malinen9f1ba902008-08-07 20:07:01 +03004359 struct bss_parameters params;
4360
4361 memset(&params, 0, sizeof(params));
4362 /* default to not changing parameters */
4363 params.use_cts_prot = -1;
4364 params.use_short_preamble = -1;
4365 params.use_short_slot_time = -1;
Felix Fietkaufd8aaaf2010-04-27 01:23:35 +02004366 params.ap_isolate = -1;
Helmut Schaa50b12f52010-11-19 12:40:25 +01004367 params.ht_opmode = -1;
Johannes Berg53cabad2012-11-14 15:17:28 +01004368 params.p2p_ctwindow = -1;
4369 params.p2p_opp_ps = -1;
Jouni Malinen9f1ba902008-08-07 20:07:01 +03004370
4371 if (info->attrs[NL80211_ATTR_BSS_CTS_PROT])
4372 params.use_cts_prot =
4373 nla_get_u8(info->attrs[NL80211_ATTR_BSS_CTS_PROT]);
4374 if (info->attrs[NL80211_ATTR_BSS_SHORT_PREAMBLE])
4375 params.use_short_preamble =
4376 nla_get_u8(info->attrs[NL80211_ATTR_BSS_SHORT_PREAMBLE]);
4377 if (info->attrs[NL80211_ATTR_BSS_SHORT_SLOT_TIME])
4378 params.use_short_slot_time =
4379 nla_get_u8(info->attrs[NL80211_ATTR_BSS_SHORT_SLOT_TIME]);
Jouni Malinen90c97a02008-10-30 16:59:22 +02004380 if (info->attrs[NL80211_ATTR_BSS_BASIC_RATES]) {
4381 params.basic_rates =
4382 nla_data(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]);
4383 params.basic_rates_len =
4384 nla_len(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]);
4385 }
Felix Fietkaufd8aaaf2010-04-27 01:23:35 +02004386 if (info->attrs[NL80211_ATTR_AP_ISOLATE])
4387 params.ap_isolate = !!nla_get_u8(info->attrs[NL80211_ATTR_AP_ISOLATE]);
Helmut Schaa50b12f52010-11-19 12:40:25 +01004388 if (info->attrs[NL80211_ATTR_BSS_HT_OPMODE])
4389 params.ht_opmode =
4390 nla_get_u16(info->attrs[NL80211_ATTR_BSS_HT_OPMODE]);
Jouni Malinen9f1ba902008-08-07 20:07:01 +03004391
Johannes Berg53cabad2012-11-14 15:17:28 +01004392 if (info->attrs[NL80211_ATTR_P2P_CTWINDOW]) {
4393 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
4394 return -EINVAL;
4395 params.p2p_ctwindow =
4396 nla_get_s8(info->attrs[NL80211_ATTR_P2P_CTWINDOW]);
4397 if (params.p2p_ctwindow < 0)
4398 return -EINVAL;
4399 if (params.p2p_ctwindow != 0 &&
4400 !(rdev->wiphy.features & NL80211_FEATURE_P2P_GO_CTWIN))
4401 return -EINVAL;
4402 }
4403
4404 if (info->attrs[NL80211_ATTR_P2P_OPPPS]) {
4405 u8 tmp;
4406
4407 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
4408 return -EINVAL;
4409 tmp = nla_get_u8(info->attrs[NL80211_ATTR_P2P_OPPPS]);
4410 if (tmp > 1)
4411 return -EINVAL;
4412 params.p2p_opp_ps = tmp;
4413 if (params.p2p_opp_ps &&
4414 !(rdev->wiphy.features & NL80211_FEATURE_P2P_GO_OPPPS))
4415 return -EINVAL;
4416 }
4417
Johannes Berg4c476992010-10-04 21:36:35 +02004418 if (!rdev->ops->change_bss)
4419 return -EOPNOTSUPP;
Jouni Malinen9f1ba902008-08-07 20:07:01 +03004420
Johannes Berg074ac8d2010-09-16 14:58:22 +02004421 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
Johannes Berg4c476992010-10-04 21:36:35 +02004422 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
4423 return -EOPNOTSUPP;
Jouni Malineneec60b02009-03-20 21:21:19 +02004424
Hila Gonene35e4d22012-06-27 17:19:42 +03004425 return rdev_change_bss(rdev, dev, &params);
Jouni Malinen9f1ba902008-08-07 20:07:01 +03004426}
4427
Alexey Dobriyanb54452b2010-02-18 08:14:31 +00004428static const struct nla_policy reg_rule_policy[NL80211_REG_RULE_ATTR_MAX + 1] = {
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07004429 [NL80211_ATTR_REG_RULE_FLAGS] = { .type = NLA_U32 },
4430 [NL80211_ATTR_FREQ_RANGE_START] = { .type = NLA_U32 },
4431 [NL80211_ATTR_FREQ_RANGE_END] = { .type = NLA_U32 },
4432 [NL80211_ATTR_FREQ_RANGE_MAX_BW] = { .type = NLA_U32 },
4433 [NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN] = { .type = NLA_U32 },
4434 [NL80211_ATTR_POWER_RULE_MAX_EIRP] = { .type = NLA_U32 },
4435};
4436
4437static int parse_reg_rule(struct nlattr *tb[],
4438 struct ieee80211_reg_rule *reg_rule)
4439{
4440 struct ieee80211_freq_range *freq_range = &reg_rule->freq_range;
4441 struct ieee80211_power_rule *power_rule = &reg_rule->power_rule;
4442
4443 if (!tb[NL80211_ATTR_REG_RULE_FLAGS])
4444 return -EINVAL;
4445 if (!tb[NL80211_ATTR_FREQ_RANGE_START])
4446 return -EINVAL;
4447 if (!tb[NL80211_ATTR_FREQ_RANGE_END])
4448 return -EINVAL;
4449 if (!tb[NL80211_ATTR_FREQ_RANGE_MAX_BW])
4450 return -EINVAL;
4451 if (!tb[NL80211_ATTR_POWER_RULE_MAX_EIRP])
4452 return -EINVAL;
4453
4454 reg_rule->flags = nla_get_u32(tb[NL80211_ATTR_REG_RULE_FLAGS]);
4455
4456 freq_range->start_freq_khz =
4457 nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_START]);
4458 freq_range->end_freq_khz =
4459 nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_END]);
4460 freq_range->max_bandwidth_khz =
4461 nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_MAX_BW]);
4462
4463 power_rule->max_eirp =
4464 nla_get_u32(tb[NL80211_ATTR_POWER_RULE_MAX_EIRP]);
4465
4466 if (tb[NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN])
4467 power_rule->max_antenna_gain =
4468 nla_get_u32(tb[NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN]);
4469
4470 return 0;
4471}
4472
4473static int nl80211_req_set_reg(struct sk_buff *skb, struct genl_info *info)
4474{
4475 int r;
4476 char *data = NULL;
Luis R. Rodriguez57b5ce02012-07-12 11:49:18 -07004477 enum nl80211_user_reg_hint_type user_reg_hint_type;
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07004478
Luis R. Rodriguez80778f12009-02-21 00:04:22 -05004479 /*
4480 * You should only get this when cfg80211 hasn't yet initialized
4481 * completely when built-in to the kernel right between the time
4482 * window between nl80211_init() and regulatory_init(), if that is
4483 * even possible.
4484 */
Johannes Berg458f4f92012-12-06 15:47:38 +01004485 if (unlikely(!rcu_access_pointer(cfg80211_regdomain)))
Luis R. Rodriguezfe33eb32009-02-21 00:04:30 -05004486 return -EINPROGRESS;
Luis R. Rodriguez80778f12009-02-21 00:04:22 -05004487
Luis R. Rodriguezfe33eb32009-02-21 00:04:30 -05004488 if (!info->attrs[NL80211_ATTR_REG_ALPHA2])
4489 return -EINVAL;
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07004490
4491 data = nla_data(info->attrs[NL80211_ATTR_REG_ALPHA2]);
4492
Luis R. Rodriguez57b5ce02012-07-12 11:49:18 -07004493 if (info->attrs[NL80211_ATTR_USER_REG_HINT_TYPE])
4494 user_reg_hint_type =
4495 nla_get_u32(info->attrs[NL80211_ATTR_USER_REG_HINT_TYPE]);
4496 else
4497 user_reg_hint_type = NL80211_USER_REG_HINT_USER;
4498
4499 switch (user_reg_hint_type) {
4500 case NL80211_USER_REG_HINT_USER:
4501 case NL80211_USER_REG_HINT_CELL_BASE:
4502 break;
4503 default:
4504 return -EINVAL;
4505 }
4506
4507 r = regulatory_hint_user(data, user_reg_hint_type);
Luis R. Rodriguezfe33eb32009-02-21 00:04:30 -05004508
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07004509 return r;
4510}
4511
Javier Cardona24bdd9f2010-12-16 17:37:48 -08004512static int nl80211_get_mesh_config(struct sk_buff *skb,
Johannes Berg29cbe682010-12-03 09:20:44 +01004513 struct genl_info *info)
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07004514{
Johannes Berg4c476992010-10-04 21:36:35 +02004515 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Johannes Berg4c476992010-10-04 21:36:35 +02004516 struct net_device *dev = info->user_ptr[1];
Johannes Berg29cbe682010-12-03 09:20:44 +01004517 struct wireless_dev *wdev = dev->ieee80211_ptr;
4518 struct mesh_config cur_params;
4519 int err = 0;
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07004520 void *hdr;
4521 struct nlattr *pinfoattr;
4522 struct sk_buff *msg;
4523
Johannes Berg29cbe682010-12-03 09:20:44 +01004524 if (wdev->iftype != NL80211_IFTYPE_MESH_POINT)
4525 return -EOPNOTSUPP;
4526
Javier Cardona24bdd9f2010-12-16 17:37:48 -08004527 if (!rdev->ops->get_mesh_config)
Johannes Berg4c476992010-10-04 21:36:35 +02004528 return -EOPNOTSUPP;
Jouni Malinenf3f92582009-03-20 17:57:36 +02004529
Johannes Berg29cbe682010-12-03 09:20:44 +01004530 wdev_lock(wdev);
4531 /* If not connected, get default parameters */
4532 if (!wdev->mesh_id_len)
4533 memcpy(&cur_params, &default_mesh_config, sizeof(cur_params));
4534 else
Hila Gonene35e4d22012-06-27 17:19:42 +03004535 err = rdev_get_mesh_config(rdev, dev, &cur_params);
Johannes Berg29cbe682010-12-03 09:20:44 +01004536 wdev_unlock(wdev);
4537
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07004538 if (err)
Johannes Berg4c476992010-10-04 21:36:35 +02004539 return err;
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07004540
4541 /* Draw up a netlink message to send back */
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07004542 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg4c476992010-10-04 21:36:35 +02004543 if (!msg)
4544 return -ENOMEM;
Eric W. Biederman15e47302012-09-07 20:12:54 +00004545 hdr = nl80211hdr_put(msg, info->snd_portid, info->snd_seq, 0,
Javier Cardona24bdd9f2010-12-16 17:37:48 -08004546 NL80211_CMD_GET_MESH_CONFIG);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07004547 if (!hdr)
Julia Lawallefe1cf02011-01-28 15:17:11 +01004548 goto out;
Javier Cardona24bdd9f2010-12-16 17:37:48 -08004549 pinfoattr = nla_nest_start(msg, NL80211_ATTR_MESH_CONFIG);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07004550 if (!pinfoattr)
4551 goto nla_put_failure;
David S. Miller9360ffd2012-03-29 04:41:26 -04004552 if (nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) ||
4553 nla_put_u16(msg, NL80211_MESHCONF_RETRY_TIMEOUT,
4554 cur_params.dot11MeshRetryTimeout) ||
4555 nla_put_u16(msg, NL80211_MESHCONF_CONFIRM_TIMEOUT,
4556 cur_params.dot11MeshConfirmTimeout) ||
4557 nla_put_u16(msg, NL80211_MESHCONF_HOLDING_TIMEOUT,
4558 cur_params.dot11MeshHoldingTimeout) ||
4559 nla_put_u16(msg, NL80211_MESHCONF_MAX_PEER_LINKS,
4560 cur_params.dot11MeshMaxPeerLinks) ||
4561 nla_put_u8(msg, NL80211_MESHCONF_MAX_RETRIES,
4562 cur_params.dot11MeshMaxRetries) ||
4563 nla_put_u8(msg, NL80211_MESHCONF_TTL,
4564 cur_params.dot11MeshTTL) ||
4565 nla_put_u8(msg, NL80211_MESHCONF_ELEMENT_TTL,
4566 cur_params.element_ttl) ||
4567 nla_put_u8(msg, NL80211_MESHCONF_AUTO_OPEN_PLINKS,
4568 cur_params.auto_open_plinks) ||
John W. Linville7eab0f62012-04-12 14:25:14 -04004569 nla_put_u32(msg, NL80211_MESHCONF_SYNC_OFFSET_MAX_NEIGHBOR,
4570 cur_params.dot11MeshNbrOffsetMaxNeighbor) ||
David S. Miller9360ffd2012-03-29 04:41:26 -04004571 nla_put_u8(msg, NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES,
4572 cur_params.dot11MeshHWMPmaxPREQretries) ||
4573 nla_put_u32(msg, NL80211_MESHCONF_PATH_REFRESH_TIME,
4574 cur_params.path_refresh_time) ||
4575 nla_put_u16(msg, NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT,
4576 cur_params.min_discovery_timeout) ||
4577 nla_put_u32(msg, NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT,
4578 cur_params.dot11MeshHWMPactivePathTimeout) ||
4579 nla_put_u16(msg, NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL,
4580 cur_params.dot11MeshHWMPpreqMinInterval) ||
4581 nla_put_u16(msg, NL80211_MESHCONF_HWMP_PERR_MIN_INTERVAL,
4582 cur_params.dot11MeshHWMPperrMinInterval) ||
4583 nla_put_u16(msg, NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME,
4584 cur_params.dot11MeshHWMPnetDiameterTraversalTime) ||
4585 nla_put_u8(msg, NL80211_MESHCONF_HWMP_ROOTMODE,
4586 cur_params.dot11MeshHWMPRootMode) ||
4587 nla_put_u16(msg, NL80211_MESHCONF_HWMP_RANN_INTERVAL,
4588 cur_params.dot11MeshHWMPRannInterval) ||
4589 nla_put_u8(msg, NL80211_MESHCONF_GATE_ANNOUNCEMENTS,
4590 cur_params.dot11MeshGateAnnouncementProtocol) ||
4591 nla_put_u8(msg, NL80211_MESHCONF_FORWARDING,
4592 cur_params.dot11MeshForwarding) ||
4593 nla_put_u32(msg, NL80211_MESHCONF_RSSI_THRESHOLD,
Ashok Nagarajan70c33ea2012-04-30 14:20:32 -07004594 cur_params.rssi_threshold) ||
4595 nla_put_u32(msg, NL80211_MESHCONF_HT_OPMODE,
Chun-Yeow Yeohac1073a2012-06-14 02:06:06 +08004596 cur_params.ht_opmode) ||
4597 nla_put_u32(msg, NL80211_MESHCONF_HWMP_PATH_TO_ROOT_TIMEOUT,
4598 cur_params.dot11MeshHWMPactivePathToRootTimeout) ||
4599 nla_put_u16(msg, NL80211_MESHCONF_HWMP_ROOT_INTERVAL,
Chun-Yeow Yeoh728b19e2012-06-14 02:06:10 +08004600 cur_params.dot11MeshHWMProotInterval) ||
4601 nla_put_u16(msg, NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL,
Marco Porsch3b1c5a52013-01-07 16:04:52 +01004602 cur_params.dot11MeshHWMPconfirmationInterval) ||
4603 nla_put_u32(msg, NL80211_MESHCONF_POWER_MODE,
4604 cur_params.power_mode) ||
4605 nla_put_u16(msg, NL80211_MESHCONF_AWAKE_WINDOW,
4606 cur_params.dot11MeshAwakeWindowDuration))
David S. Miller9360ffd2012-03-29 04:41:26 -04004607 goto nla_put_failure;
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07004608 nla_nest_end(msg, pinfoattr);
4609 genlmsg_end(msg, hdr);
Johannes Berg4c476992010-10-04 21:36:35 +02004610 return genlmsg_reply(msg, info);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07004611
Johannes Berg3b858752009-03-12 09:55:09 +01004612 nla_put_failure:
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07004613 genlmsg_cancel(msg, hdr);
Julia Lawallefe1cf02011-01-28 15:17:11 +01004614 out:
Yuri Ershovd080e272010-06-29 15:08:07 +04004615 nlmsg_free(msg);
Johannes Berg4c476992010-10-04 21:36:35 +02004616 return -ENOBUFS;
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07004617}
4618
Alexey Dobriyanb54452b2010-02-18 08:14:31 +00004619static const struct nla_policy nl80211_meshconf_params_policy[NL80211_MESHCONF_ATTR_MAX+1] = {
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07004620 [NL80211_MESHCONF_RETRY_TIMEOUT] = { .type = NLA_U16 },
4621 [NL80211_MESHCONF_CONFIRM_TIMEOUT] = { .type = NLA_U16 },
4622 [NL80211_MESHCONF_HOLDING_TIMEOUT] = { .type = NLA_U16 },
4623 [NL80211_MESHCONF_MAX_PEER_LINKS] = { .type = NLA_U16 },
4624 [NL80211_MESHCONF_MAX_RETRIES] = { .type = NLA_U8 },
4625 [NL80211_MESHCONF_TTL] = { .type = NLA_U8 },
Javier Cardona45904f22010-12-03 09:20:40 +01004626 [NL80211_MESHCONF_ELEMENT_TTL] = { .type = NLA_U8 },
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07004627 [NL80211_MESHCONF_AUTO_OPEN_PLINKS] = { .type = NLA_U8 },
Javier Cardonad299a1f2012-03-31 11:31:33 -07004628 [NL80211_MESHCONF_SYNC_OFFSET_MAX_NEIGHBOR] = { .type = NLA_U32 },
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07004629 [NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES] = { .type = NLA_U8 },
4630 [NL80211_MESHCONF_PATH_REFRESH_TIME] = { .type = NLA_U32 },
4631 [NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT] = { .type = NLA_U16 },
4632 [NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT] = { .type = NLA_U32 },
4633 [NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL] = { .type = NLA_U16 },
Thomas Pedersendca7e942011-11-24 17:15:24 -08004634 [NL80211_MESHCONF_HWMP_PERR_MIN_INTERVAL] = { .type = NLA_U16 },
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07004635 [NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME] = { .type = NLA_U16 },
Javier Cardona699403d2011-08-09 16:45:09 -07004636 [NL80211_MESHCONF_HWMP_ROOTMODE] = { .type = NLA_U8 },
Javier Cardona0507e152011-08-09 16:45:10 -07004637 [NL80211_MESHCONF_HWMP_RANN_INTERVAL] = { .type = NLA_U16 },
Javier Cardona16dd7262011-08-09 16:45:11 -07004638 [NL80211_MESHCONF_GATE_ANNOUNCEMENTS] = { .type = NLA_U8 },
Chun-Yeow Yeoh94f90652012-01-21 01:02:16 +08004639 [NL80211_MESHCONF_FORWARDING] = { .type = NLA_U8 },
Chun-Yeow Yeoha4f606e2012-06-11 11:59:36 +08004640 [NL80211_MESHCONF_RSSI_THRESHOLD] = { .type = NLA_U32 },
4641 [NL80211_MESHCONF_HT_OPMODE] = { .type = NLA_U16 },
Chun-Yeow Yeohac1073a2012-06-14 02:06:06 +08004642 [NL80211_MESHCONF_HWMP_PATH_TO_ROOT_TIMEOUT] = { .type = NLA_U32 },
4643 [NL80211_MESHCONF_HWMP_ROOT_INTERVAL] = { .type = NLA_U16 },
Chun-Yeow Yeoh728b19e2012-06-14 02:06:10 +08004644 [NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL] = { .type = NLA_U16 },
Marco Porsch3b1c5a52013-01-07 16:04:52 +01004645 [NL80211_MESHCONF_POWER_MODE] = { .type = NLA_U32 },
4646 [NL80211_MESHCONF_AWAKE_WINDOW] = { .type = NLA_U16 },
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07004647};
4648
Javier Cardonac80d5452010-12-16 17:37:49 -08004649static const struct nla_policy
4650 nl80211_mesh_setup_params_policy[NL80211_MESH_SETUP_ATTR_MAX+1] = {
Javier Cardonad299a1f2012-03-31 11:31:33 -07004651 [NL80211_MESH_SETUP_ENABLE_VENDOR_SYNC] = { .type = NLA_U8 },
Javier Cardonac80d5452010-12-16 17:37:49 -08004652 [NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL] = { .type = NLA_U8 },
4653 [NL80211_MESH_SETUP_ENABLE_VENDOR_METRIC] = { .type = NLA_U8 },
Javier Cardona15d5dda2011-04-07 15:08:28 -07004654 [NL80211_MESH_SETUP_USERSPACE_AUTH] = { .type = NLA_FLAG },
Thomas Pedersenbb2798d2013-03-04 13:06:10 -08004655 [NL80211_MESH_SETUP_USERSPACE_MPM] = { .type = NLA_FLAG },
Javier Cardona581a8b02011-04-07 15:08:27 -07004656 [NL80211_MESH_SETUP_IE] = { .type = NLA_BINARY,
Chun-Yeow Yeoha4f606e2012-06-11 11:59:36 +08004657 .len = IEEE80211_MAX_DATA_LEN },
Javier Cardonab130e5c2011-05-03 16:57:07 -07004658 [NL80211_MESH_SETUP_USERSPACE_AMPE] = { .type = NLA_FLAG },
Javier Cardonac80d5452010-12-16 17:37:49 -08004659};
4660
Javier Cardona24bdd9f2010-12-16 17:37:48 -08004661static int nl80211_parse_mesh_config(struct genl_info *info,
Johannes Bergbd90fdc2010-12-03 09:20:43 +01004662 struct mesh_config *cfg,
4663 u32 *mask_out)
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07004664{
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07004665 struct nlattr *tb[NL80211_MESHCONF_ATTR_MAX + 1];
Johannes Bergbd90fdc2010-12-03 09:20:43 +01004666 u32 mask = 0;
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07004667
Marco Porschea54fba2013-01-07 16:04:48 +01004668#define FILL_IN_MESH_PARAM_IF_SET(tb, cfg, param, min, max, mask, attr, fn) \
4669do { \
4670 if (tb[attr]) { \
4671 if (fn(tb[attr]) < min || fn(tb[attr]) > max) \
4672 return -EINVAL; \
4673 cfg->param = fn(tb[attr]); \
4674 mask |= (1 << (attr - 1)); \
4675 } \
4676} while (0)
Johannes Bergbd90fdc2010-12-03 09:20:43 +01004677
4678
Javier Cardona24bdd9f2010-12-16 17:37:48 -08004679 if (!info->attrs[NL80211_ATTR_MESH_CONFIG])
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07004680 return -EINVAL;
4681 if (nla_parse_nested(tb, NL80211_MESHCONF_ATTR_MAX,
Javier Cardona24bdd9f2010-12-16 17:37:48 -08004682 info->attrs[NL80211_ATTR_MESH_CONFIG],
Johannes Bergbd90fdc2010-12-03 09:20:43 +01004683 nl80211_meshconf_params_policy))
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07004684 return -EINVAL;
4685
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07004686 /* This makes sure that there aren't more than 32 mesh config
4687 * parameters (otherwise our bitfield scheme would not work.) */
4688 BUILD_BUG_ON(NL80211_MESHCONF_ATTR_MAX > 32);
4689
4690 /* Fill in the params struct */
Marco Porschea54fba2013-01-07 16:04:48 +01004691 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshRetryTimeout, 1, 255,
Chun-Yeow Yeoha4f606e2012-06-11 11:59:36 +08004692 mask, NL80211_MESHCONF_RETRY_TIMEOUT,
4693 nla_get_u16);
Marco Porschea54fba2013-01-07 16:04:48 +01004694 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshConfirmTimeout, 1, 255,
Chun-Yeow Yeoha4f606e2012-06-11 11:59:36 +08004695 mask, NL80211_MESHCONF_CONFIRM_TIMEOUT,
4696 nla_get_u16);
Marco Porschea54fba2013-01-07 16:04:48 +01004697 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHoldingTimeout, 1, 255,
Chun-Yeow Yeoha4f606e2012-06-11 11:59:36 +08004698 mask, NL80211_MESHCONF_HOLDING_TIMEOUT,
4699 nla_get_u16);
Marco Porschea54fba2013-01-07 16:04:48 +01004700 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshMaxPeerLinks, 0, 255,
Chun-Yeow Yeoha4f606e2012-06-11 11:59:36 +08004701 mask, NL80211_MESHCONF_MAX_PEER_LINKS,
4702 nla_get_u16);
Marco Porschea54fba2013-01-07 16:04:48 +01004703 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshMaxRetries, 0, 16,
Chun-Yeow Yeoha4f606e2012-06-11 11:59:36 +08004704 mask, NL80211_MESHCONF_MAX_RETRIES,
4705 nla_get_u8);
Marco Porschea54fba2013-01-07 16:04:48 +01004706 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshTTL, 1, 255,
Chun-Yeow Yeoha4f606e2012-06-11 11:59:36 +08004707 mask, NL80211_MESHCONF_TTL, nla_get_u8);
Marco Porschea54fba2013-01-07 16:04:48 +01004708 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, element_ttl, 1, 255,
Chun-Yeow Yeoha4f606e2012-06-11 11:59:36 +08004709 mask, NL80211_MESHCONF_ELEMENT_TTL,
4710 nla_get_u8);
Marco Porschea54fba2013-01-07 16:04:48 +01004711 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, auto_open_plinks, 0, 1,
Chun-Yeow Yeoha4f606e2012-06-11 11:59:36 +08004712 mask, NL80211_MESHCONF_AUTO_OPEN_PLINKS,
4713 nla_get_u8);
Marco Porschea54fba2013-01-07 16:04:48 +01004714 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshNbrOffsetMaxNeighbor,
4715 1, 255, mask,
Chun-Yeow Yeoha4f606e2012-06-11 11:59:36 +08004716 NL80211_MESHCONF_SYNC_OFFSET_MAX_NEIGHBOR,
4717 nla_get_u32);
Marco Porschea54fba2013-01-07 16:04:48 +01004718 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPmaxPREQretries, 0, 255,
Chun-Yeow Yeoha4f606e2012-06-11 11:59:36 +08004719 mask, NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES,
4720 nla_get_u8);
Marco Porschea54fba2013-01-07 16:04:48 +01004721 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, path_refresh_time, 1, 65535,
Chun-Yeow Yeoha4f606e2012-06-11 11:59:36 +08004722 mask, NL80211_MESHCONF_PATH_REFRESH_TIME,
4723 nla_get_u32);
Marco Porschea54fba2013-01-07 16:04:48 +01004724 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, min_discovery_timeout, 1, 65535,
Chun-Yeow Yeoha4f606e2012-06-11 11:59:36 +08004725 mask, NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT,
4726 nla_get_u16);
Marco Porschea54fba2013-01-07 16:04:48 +01004727 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPactivePathTimeout,
4728 1, 65535, mask,
Chun-Yeow Yeoha4f606e2012-06-11 11:59:36 +08004729 NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT,
4730 nla_get_u32);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07004731 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPpreqMinInterval,
Marco Porschea54fba2013-01-07 16:04:48 +01004732 1, 65535, mask,
4733 NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL,
Chun-Yeow Yeoha4f606e2012-06-11 11:59:36 +08004734 nla_get_u16);
Thomas Pedersendca7e942011-11-24 17:15:24 -08004735 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPperrMinInterval,
Marco Porschea54fba2013-01-07 16:04:48 +01004736 1, 65535, mask,
4737 NL80211_MESHCONF_HWMP_PERR_MIN_INTERVAL,
Chun-Yeow Yeoha4f606e2012-06-11 11:59:36 +08004738 nla_get_u16);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07004739 FILL_IN_MESH_PARAM_IF_SET(tb, cfg,
Marco Porschea54fba2013-01-07 16:04:48 +01004740 dot11MeshHWMPnetDiameterTraversalTime,
4741 1, 65535, mask,
Chun-Yeow Yeoha4f606e2012-06-11 11:59:36 +08004742 NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME,
4743 nla_get_u16);
Marco Porschea54fba2013-01-07 16:04:48 +01004744 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPRootMode, 0, 4,
4745 mask, NL80211_MESHCONF_HWMP_ROOTMODE,
4746 nla_get_u8);
4747 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPRannInterval, 1, 65535,
4748 mask, NL80211_MESHCONF_HWMP_RANN_INTERVAL,
Chun-Yeow Yeoha4f606e2012-06-11 11:59:36 +08004749 nla_get_u16);
Rui Paulo63c57232009-11-09 23:46:57 +00004750 FILL_IN_MESH_PARAM_IF_SET(tb, cfg,
Marco Porschea54fba2013-01-07 16:04:48 +01004751 dot11MeshGateAnnouncementProtocol, 0, 1,
4752 mask, NL80211_MESHCONF_GATE_ANNOUNCEMENTS,
Chun-Yeow Yeoha4f606e2012-06-11 11:59:36 +08004753 nla_get_u8);
Marco Porschea54fba2013-01-07 16:04:48 +01004754 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshForwarding, 0, 1,
Chun-Yeow Yeoha4f606e2012-06-11 11:59:36 +08004755 mask, NL80211_MESHCONF_FORWARDING,
4756 nla_get_u8);
Marco Porschea54fba2013-01-07 16:04:48 +01004757 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, rssi_threshold, 1, 255,
Chun-Yeow Yeoha4f606e2012-06-11 11:59:36 +08004758 mask, NL80211_MESHCONF_RSSI_THRESHOLD,
4759 nla_get_u32);
Marco Porschea54fba2013-01-07 16:04:48 +01004760 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, ht_opmode, 0, 16,
Chun-Yeow Yeoha4f606e2012-06-11 11:59:36 +08004761 mask, NL80211_MESHCONF_HT_OPMODE,
4762 nla_get_u16);
Chun-Yeow Yeohac1073a2012-06-14 02:06:06 +08004763 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPactivePathToRootTimeout,
Marco Porschea54fba2013-01-07 16:04:48 +01004764 1, 65535, mask,
Chun-Yeow Yeohac1073a2012-06-14 02:06:06 +08004765 NL80211_MESHCONF_HWMP_PATH_TO_ROOT_TIMEOUT,
4766 nla_get_u32);
Marco Porschea54fba2013-01-07 16:04:48 +01004767 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMProotInterval, 1, 65535,
Chun-Yeow Yeohac1073a2012-06-14 02:06:06 +08004768 mask, NL80211_MESHCONF_HWMP_ROOT_INTERVAL,
4769 nla_get_u16);
Chun-Yeow Yeoh728b19e2012-06-14 02:06:10 +08004770 FILL_IN_MESH_PARAM_IF_SET(tb, cfg,
Marco Porschea54fba2013-01-07 16:04:48 +01004771 dot11MeshHWMPconfirmationInterval,
4772 1, 65535, mask,
Chun-Yeow Yeoh728b19e2012-06-14 02:06:10 +08004773 NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL,
4774 nla_get_u16);
Marco Porsch3b1c5a52013-01-07 16:04:52 +01004775 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, power_mode,
4776 NL80211_MESH_POWER_ACTIVE,
4777 NL80211_MESH_POWER_MAX,
4778 mask, NL80211_MESHCONF_POWER_MODE,
4779 nla_get_u32);
4780 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshAwakeWindowDuration,
4781 0, 65535, mask,
4782 NL80211_MESHCONF_AWAKE_WINDOW, nla_get_u16);
Johannes Bergbd90fdc2010-12-03 09:20:43 +01004783 if (mask_out)
4784 *mask_out = mask;
Javier Cardonac80d5452010-12-16 17:37:49 -08004785
Johannes Bergbd90fdc2010-12-03 09:20:43 +01004786 return 0;
4787
4788#undef FILL_IN_MESH_PARAM_IF_SET
4789}
4790
Javier Cardonac80d5452010-12-16 17:37:49 -08004791static int nl80211_parse_mesh_setup(struct genl_info *info,
4792 struct mesh_setup *setup)
4793{
Thomas Pedersenbb2798d2013-03-04 13:06:10 -08004794 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Javier Cardonac80d5452010-12-16 17:37:49 -08004795 struct nlattr *tb[NL80211_MESH_SETUP_ATTR_MAX + 1];
4796
4797 if (!info->attrs[NL80211_ATTR_MESH_SETUP])
4798 return -EINVAL;
4799 if (nla_parse_nested(tb, NL80211_MESH_SETUP_ATTR_MAX,
4800 info->attrs[NL80211_ATTR_MESH_SETUP],
4801 nl80211_mesh_setup_params_policy))
4802 return -EINVAL;
4803
Javier Cardonad299a1f2012-03-31 11:31:33 -07004804 if (tb[NL80211_MESH_SETUP_ENABLE_VENDOR_SYNC])
4805 setup->sync_method =
4806 (nla_get_u8(tb[NL80211_MESH_SETUP_ENABLE_VENDOR_SYNC])) ?
4807 IEEE80211_SYNC_METHOD_VENDOR :
4808 IEEE80211_SYNC_METHOD_NEIGHBOR_OFFSET;
4809
Javier Cardonac80d5452010-12-16 17:37:49 -08004810 if (tb[NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL])
4811 setup->path_sel_proto =
4812 (nla_get_u8(tb[NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL])) ?
4813 IEEE80211_PATH_PROTOCOL_VENDOR :
4814 IEEE80211_PATH_PROTOCOL_HWMP;
4815
4816 if (tb[NL80211_MESH_SETUP_ENABLE_VENDOR_METRIC])
4817 setup->path_metric =
4818 (nla_get_u8(tb[NL80211_MESH_SETUP_ENABLE_VENDOR_METRIC])) ?
4819 IEEE80211_PATH_METRIC_VENDOR :
4820 IEEE80211_PATH_METRIC_AIRTIME;
4821
Javier Cardona581a8b02011-04-07 15:08:27 -07004822
4823 if (tb[NL80211_MESH_SETUP_IE]) {
Javier Cardonac80d5452010-12-16 17:37:49 -08004824 struct nlattr *ieattr =
Javier Cardona581a8b02011-04-07 15:08:27 -07004825 tb[NL80211_MESH_SETUP_IE];
Javier Cardonac80d5452010-12-16 17:37:49 -08004826 if (!is_valid_ie_attr(ieattr))
4827 return -EINVAL;
Javier Cardona581a8b02011-04-07 15:08:27 -07004828 setup->ie = nla_data(ieattr);
4829 setup->ie_len = nla_len(ieattr);
Javier Cardonac80d5452010-12-16 17:37:49 -08004830 }
Thomas Pedersenbb2798d2013-03-04 13:06:10 -08004831 if (tb[NL80211_MESH_SETUP_USERSPACE_MPM] &&
4832 !(rdev->wiphy.features & NL80211_FEATURE_USERSPACE_MPM))
4833 return -EINVAL;
4834 setup->user_mpm = nla_get_flag(tb[NL80211_MESH_SETUP_USERSPACE_MPM]);
Javier Cardonab130e5c2011-05-03 16:57:07 -07004835 setup->is_authenticated = nla_get_flag(tb[NL80211_MESH_SETUP_USERSPACE_AUTH]);
4836 setup->is_secure = nla_get_flag(tb[NL80211_MESH_SETUP_USERSPACE_AMPE]);
Thomas Pedersenbb2798d2013-03-04 13:06:10 -08004837 if (setup->is_secure)
4838 setup->user_mpm = true;
Javier Cardonac80d5452010-12-16 17:37:49 -08004839
4840 return 0;
4841}
4842
Javier Cardona24bdd9f2010-12-16 17:37:48 -08004843static int nl80211_update_mesh_config(struct sk_buff *skb,
Johannes Berg29cbe682010-12-03 09:20:44 +01004844 struct genl_info *info)
Johannes Bergbd90fdc2010-12-03 09:20:43 +01004845{
4846 struct cfg80211_registered_device *rdev = info->user_ptr[0];
4847 struct net_device *dev = info->user_ptr[1];
Johannes Berg29cbe682010-12-03 09:20:44 +01004848 struct wireless_dev *wdev = dev->ieee80211_ptr;
Johannes Bergbd90fdc2010-12-03 09:20:43 +01004849 struct mesh_config cfg;
4850 u32 mask;
4851 int err;
4852
Johannes Berg29cbe682010-12-03 09:20:44 +01004853 if (wdev->iftype != NL80211_IFTYPE_MESH_POINT)
4854 return -EOPNOTSUPP;
4855
Javier Cardona24bdd9f2010-12-16 17:37:48 -08004856 if (!rdev->ops->update_mesh_config)
Johannes Bergbd90fdc2010-12-03 09:20:43 +01004857 return -EOPNOTSUPP;
4858
Javier Cardona24bdd9f2010-12-16 17:37:48 -08004859 err = nl80211_parse_mesh_config(info, &cfg, &mask);
Johannes Bergbd90fdc2010-12-03 09:20:43 +01004860 if (err)
4861 return err;
4862
Johannes Berg29cbe682010-12-03 09:20:44 +01004863 wdev_lock(wdev);
4864 if (!wdev->mesh_id_len)
4865 err = -ENOLINK;
4866
4867 if (!err)
Hila Gonene35e4d22012-06-27 17:19:42 +03004868 err = rdev_update_mesh_config(rdev, dev, mask, &cfg);
Johannes Berg29cbe682010-12-03 09:20:44 +01004869
4870 wdev_unlock(wdev);
4871
4872 return err;
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07004873}
4874
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08004875static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info)
4876{
Johannes Berg458f4f92012-12-06 15:47:38 +01004877 const struct ieee80211_regdomain *regdom;
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08004878 struct sk_buff *msg;
4879 void *hdr = NULL;
4880 struct nlattr *nl_reg_rules;
4881 unsigned int i;
4882 int err = -EINVAL;
4883
Luis R. Rodrigueza1794392009-02-21 00:04:21 -05004884 mutex_lock(&cfg80211_mutex);
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08004885
4886 if (!cfg80211_regdomain)
4887 goto out;
4888
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07004889 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08004890 if (!msg) {
4891 err = -ENOBUFS;
4892 goto out;
4893 }
4894
Eric W. Biederman15e47302012-09-07 20:12:54 +00004895 hdr = nl80211hdr_put(msg, info->snd_portid, info->snd_seq, 0,
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08004896 NL80211_CMD_GET_REG);
4897 if (!hdr)
Julia Lawallefe1cf02011-01-28 15:17:11 +01004898 goto put_failure;
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08004899
Luis R. Rodriguez57b5ce02012-07-12 11:49:18 -07004900 if (reg_last_request_cell_base() &&
4901 nla_put_u32(msg, NL80211_ATTR_USER_REG_HINT_TYPE,
4902 NL80211_USER_REG_HINT_CELL_BASE))
4903 goto nla_put_failure;
4904
Johannes Berg458f4f92012-12-06 15:47:38 +01004905 rcu_read_lock();
4906 regdom = rcu_dereference(cfg80211_regdomain);
4907
4908 if (nla_put_string(msg, NL80211_ATTR_REG_ALPHA2, regdom->alpha2) ||
4909 (regdom->dfs_region &&
4910 nla_put_u8(msg, NL80211_ATTR_DFS_REGION, regdom->dfs_region)))
4911 goto nla_put_failure_rcu;
4912
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08004913 nl_reg_rules = nla_nest_start(msg, NL80211_ATTR_REG_RULES);
4914 if (!nl_reg_rules)
Johannes Berg458f4f92012-12-06 15:47:38 +01004915 goto nla_put_failure_rcu;
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08004916
Johannes Berg458f4f92012-12-06 15:47:38 +01004917 for (i = 0; i < regdom->n_reg_rules; i++) {
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08004918 struct nlattr *nl_reg_rule;
4919 const struct ieee80211_reg_rule *reg_rule;
4920 const struct ieee80211_freq_range *freq_range;
4921 const struct ieee80211_power_rule *power_rule;
4922
Johannes Berg458f4f92012-12-06 15:47:38 +01004923 reg_rule = &regdom->reg_rules[i];
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08004924 freq_range = &reg_rule->freq_range;
4925 power_rule = &reg_rule->power_rule;
4926
4927 nl_reg_rule = nla_nest_start(msg, i);
4928 if (!nl_reg_rule)
Johannes Berg458f4f92012-12-06 15:47:38 +01004929 goto nla_put_failure_rcu;
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08004930
David S. Miller9360ffd2012-03-29 04:41:26 -04004931 if (nla_put_u32(msg, NL80211_ATTR_REG_RULE_FLAGS,
4932 reg_rule->flags) ||
4933 nla_put_u32(msg, NL80211_ATTR_FREQ_RANGE_START,
4934 freq_range->start_freq_khz) ||
4935 nla_put_u32(msg, NL80211_ATTR_FREQ_RANGE_END,
4936 freq_range->end_freq_khz) ||
4937 nla_put_u32(msg, NL80211_ATTR_FREQ_RANGE_MAX_BW,
4938 freq_range->max_bandwidth_khz) ||
4939 nla_put_u32(msg, NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN,
4940 power_rule->max_antenna_gain) ||
4941 nla_put_u32(msg, NL80211_ATTR_POWER_RULE_MAX_EIRP,
4942 power_rule->max_eirp))
Johannes Berg458f4f92012-12-06 15:47:38 +01004943 goto nla_put_failure_rcu;
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08004944
4945 nla_nest_end(msg, nl_reg_rule);
4946 }
Johannes Berg458f4f92012-12-06 15:47:38 +01004947 rcu_read_unlock();
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08004948
4949 nla_nest_end(msg, nl_reg_rules);
4950
4951 genlmsg_end(msg, hdr);
Johannes Berg134e6372009-07-10 09:51:34 +00004952 err = genlmsg_reply(msg, info);
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08004953 goto out;
4954
Johannes Berg458f4f92012-12-06 15:47:38 +01004955nla_put_failure_rcu:
4956 rcu_read_unlock();
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08004957nla_put_failure:
4958 genlmsg_cancel(msg, hdr);
Julia Lawallefe1cf02011-01-28 15:17:11 +01004959put_failure:
Yuri Ershovd080e272010-06-29 15:08:07 +04004960 nlmsg_free(msg);
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08004961 err = -EMSGSIZE;
4962out:
Luis R. Rodrigueza1794392009-02-21 00:04:21 -05004963 mutex_unlock(&cfg80211_mutex);
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08004964 return err;
4965}
4966
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07004967static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info)
4968{
4969 struct nlattr *tb[NL80211_REG_RULE_ATTR_MAX + 1];
4970 struct nlattr *nl_reg_rule;
4971 char *alpha2 = NULL;
4972 int rem_reg_rules = 0, r = 0;
4973 u32 num_rules = 0, rule_idx = 0, size_of_regd;
Luis R. Rodriguez8b60b072011-10-11 10:59:02 -07004974 u8 dfs_region = 0;
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07004975 struct ieee80211_regdomain *rd = NULL;
4976
4977 if (!info->attrs[NL80211_ATTR_REG_ALPHA2])
4978 return -EINVAL;
4979
4980 if (!info->attrs[NL80211_ATTR_REG_RULES])
4981 return -EINVAL;
4982
4983 alpha2 = nla_data(info->attrs[NL80211_ATTR_REG_ALPHA2]);
4984
Luis R. Rodriguez8b60b072011-10-11 10:59:02 -07004985 if (info->attrs[NL80211_ATTR_DFS_REGION])
4986 dfs_region = nla_get_u8(info->attrs[NL80211_ATTR_DFS_REGION]);
4987
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07004988 nla_for_each_nested(nl_reg_rule, info->attrs[NL80211_ATTR_REG_RULES],
Johannes Berg1a919312012-12-03 17:21:11 +01004989 rem_reg_rules) {
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07004990 num_rules++;
4991 if (num_rules > NL80211_MAX_SUPP_REG_RULES)
Luis R. Rodriguez4776c6e2009-05-13 17:04:39 -04004992 return -EINVAL;
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07004993 }
4994
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07004995 size_of_regd = sizeof(struct ieee80211_regdomain) +
Johannes Berg1a919312012-12-03 17:21:11 +01004996 num_rules * sizeof(struct ieee80211_reg_rule);
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07004997
4998 rd = kzalloc(size_of_regd, GFP_KERNEL);
Johannes Berg6913b492012-12-04 00:48:59 +01004999 if (!rd)
5000 return -ENOMEM;
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07005001
5002 rd->n_reg_rules = num_rules;
5003 rd->alpha2[0] = alpha2[0];
5004 rd->alpha2[1] = alpha2[1];
5005
Luis R. Rodriguez8b60b072011-10-11 10:59:02 -07005006 /*
5007 * Disable DFS master mode if the DFS region was
5008 * not supported or known on this kernel.
5009 */
5010 if (reg_supported_dfs_region(dfs_region))
5011 rd->dfs_region = dfs_region;
5012
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07005013 nla_for_each_nested(nl_reg_rule, info->attrs[NL80211_ATTR_REG_RULES],
Johannes Berg1a919312012-12-03 17:21:11 +01005014 rem_reg_rules) {
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07005015 nla_parse(tb, NL80211_REG_RULE_ATTR_MAX,
Johannes Berg1a919312012-12-03 17:21:11 +01005016 nla_data(nl_reg_rule), nla_len(nl_reg_rule),
5017 reg_rule_policy);
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07005018 r = parse_reg_rule(tb, &rd->reg_rules[rule_idx]);
5019 if (r)
5020 goto bad_reg;
5021
5022 rule_idx++;
5023
Luis R. Rodriguezd0e18f82009-05-13 17:04:40 -04005024 if (rule_idx > NL80211_MAX_SUPP_REG_RULES) {
5025 r = -EINVAL;
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07005026 goto bad_reg;
Luis R. Rodriguezd0e18f82009-05-13 17:04:40 -04005027 }
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07005028 }
5029
Johannes Berg6913b492012-12-04 00:48:59 +01005030 mutex_lock(&cfg80211_mutex);
5031
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07005032 r = set_regdom(rd);
Johannes Berg6913b492012-12-04 00:48:59 +01005033 /* set_regdom took ownership */
Johannes Berg1a919312012-12-03 17:21:11 +01005034 rd = NULL;
Johannes Berg6913b492012-12-04 00:48:59 +01005035 mutex_unlock(&cfg80211_mutex);
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07005036
Johannes Bergd2372b32008-10-24 20:32:20 +02005037 bad_reg:
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07005038 kfree(rd);
Luis R. Rodriguezd0e18f82009-05-13 17:04:40 -04005039 return r;
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07005040}
5041
Johannes Berg83f5e2c2009-06-17 17:41:49 +02005042static int validate_scan_freqs(struct nlattr *freqs)
5043{
5044 struct nlattr *attr1, *attr2;
5045 int n_channels = 0, tmp1, tmp2;
5046
5047 nla_for_each_nested(attr1, freqs, tmp1) {
5048 n_channels++;
5049 /*
5050 * Some hardware has a limited channel list for
5051 * scanning, and it is pretty much nonsensical
5052 * to scan for a channel twice, so disallow that
5053 * and don't require drivers to check that the
5054 * channel list they get isn't longer than what
5055 * they can scan, as long as they can scan all
5056 * the channels they registered at once.
5057 */
5058 nla_for_each_nested(attr2, freqs, tmp2)
5059 if (attr1 != attr2 &&
5060 nla_get_u32(attr1) == nla_get_u32(attr2))
5061 return 0;
5062 }
5063
5064 return n_channels;
5065}
5066
Johannes Berg2a519312009-02-10 21:25:55 +01005067static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
5068{
Johannes Berg4c476992010-10-04 21:36:35 +02005069 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Johannes Bergfd014282012-06-18 19:17:03 +02005070 struct wireless_dev *wdev = info->user_ptr[1];
Johannes Berg2a519312009-02-10 21:25:55 +01005071 struct cfg80211_scan_request *request;
Johannes Berg2a519312009-02-10 21:25:55 +01005072 struct nlattr *attr;
5073 struct wiphy *wiphy;
Johannes Berg83f5e2c2009-06-17 17:41:49 +02005074 int err, tmp, n_ssids = 0, n_channels, i;
Jouni Malinen70692ad2009-02-16 19:39:13 +02005075 size_t ie_len;
Johannes Berg2a519312009-02-10 21:25:55 +01005076
Johannes Bergf4a11bb2009-03-27 12:40:28 +01005077 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
5078 return -EINVAL;
5079
Johannes Berg79c97e92009-07-07 03:56:12 +02005080 wiphy = &rdev->wiphy;
Johannes Berg2a519312009-02-10 21:25:55 +01005081
Johannes Berg4c476992010-10-04 21:36:35 +02005082 if (!rdev->ops->scan)
5083 return -EOPNOTSUPP;
Johannes Berg2a519312009-02-10 21:25:55 +01005084
Johannes Bergf9f47522013-03-19 15:04:07 +01005085 mutex_lock(&rdev->sched_scan_mtx);
5086 if (rdev->scan_req) {
5087 err = -EBUSY;
5088 goto unlock;
5089 }
Johannes Berg2a519312009-02-10 21:25:55 +01005090
5091 if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
Johannes Berg83f5e2c2009-06-17 17:41:49 +02005092 n_channels = validate_scan_freqs(
5093 info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]);
Johannes Bergf9f47522013-03-19 15:04:07 +01005094 if (!n_channels) {
5095 err = -EINVAL;
5096 goto unlock;
5097 }
Johannes Berg2a519312009-02-10 21:25:55 +01005098 } else {
Johannes Berg34850ab2011-07-18 18:08:35 +02005099 enum ieee80211_band band;
Johannes Berg83f5e2c2009-06-17 17:41:49 +02005100 n_channels = 0;
5101
Johannes Berg2a519312009-02-10 21:25:55 +01005102 for (band = 0; band < IEEE80211_NUM_BANDS; band++)
5103 if (wiphy->bands[band])
5104 n_channels += wiphy->bands[band]->n_channels;
5105 }
5106
5107 if (info->attrs[NL80211_ATTR_SCAN_SSIDS])
5108 nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS], tmp)
5109 n_ssids++;
5110
Johannes Bergf9f47522013-03-19 15:04:07 +01005111 if (n_ssids > wiphy->max_scan_ssids) {
5112 err = -EINVAL;
5113 goto unlock;
5114 }
Johannes Berg2a519312009-02-10 21:25:55 +01005115
Jouni Malinen70692ad2009-02-16 19:39:13 +02005116 if (info->attrs[NL80211_ATTR_IE])
5117 ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
5118 else
5119 ie_len = 0;
5120
Johannes Bergf9f47522013-03-19 15:04:07 +01005121 if (ie_len > wiphy->max_scan_ie_len) {
5122 err = -EINVAL;
5123 goto unlock;
5124 }
Johannes Berg18a83652009-03-31 12:12:05 +02005125
Johannes Berg2a519312009-02-10 21:25:55 +01005126 request = kzalloc(sizeof(*request)
Luciano Coelhoa2cd43c2011-05-18 11:42:03 +03005127 + sizeof(*request->ssids) * n_ssids
5128 + sizeof(*request->channels) * n_channels
Jouni Malinen70692ad2009-02-16 19:39:13 +02005129 + ie_len, GFP_KERNEL);
Johannes Bergf9f47522013-03-19 15:04:07 +01005130 if (!request) {
5131 err = -ENOMEM;
5132 goto unlock;
5133 }
Johannes Berg2a519312009-02-10 21:25:55 +01005134
Johannes Berg2a519312009-02-10 21:25:55 +01005135 if (n_ssids)
Johannes Berg5ba63532009-08-07 17:54:07 +02005136 request->ssids = (void *)&request->channels[n_channels];
Johannes Berg2a519312009-02-10 21:25:55 +01005137 request->n_ssids = n_ssids;
Jouni Malinen70692ad2009-02-16 19:39:13 +02005138 if (ie_len) {
5139 if (request->ssids)
5140 request->ie = (void *)(request->ssids + n_ssids);
5141 else
5142 request->ie = (void *)(request->channels + n_channels);
5143 }
Johannes Berg2a519312009-02-10 21:25:55 +01005144
Johannes Berg584991d2009-11-02 13:32:03 +01005145 i = 0;
Johannes Berg2a519312009-02-10 21:25:55 +01005146 if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
5147 /* user specified, bail out if channel not found */
Johannes Berg2a519312009-02-10 21:25:55 +01005148 nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_FREQUENCIES], tmp) {
Johannes Berg584991d2009-11-02 13:32:03 +01005149 struct ieee80211_channel *chan;
5150
5151 chan = ieee80211_get_channel(wiphy, nla_get_u32(attr));
5152
5153 if (!chan) {
Johannes Berg2a519312009-02-10 21:25:55 +01005154 err = -EINVAL;
5155 goto out_free;
5156 }
Johannes Berg584991d2009-11-02 13:32:03 +01005157
5158 /* ignore disabled channels */
5159 if (chan->flags & IEEE80211_CHAN_DISABLED)
5160 continue;
5161
5162 request->channels[i] = chan;
Johannes Berg2a519312009-02-10 21:25:55 +01005163 i++;
5164 }
5165 } else {
Johannes Berg34850ab2011-07-18 18:08:35 +02005166 enum ieee80211_band band;
5167
Johannes Berg2a519312009-02-10 21:25:55 +01005168 /* all channels */
Johannes Berg2a519312009-02-10 21:25:55 +01005169 for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
5170 int j;
5171 if (!wiphy->bands[band])
5172 continue;
5173 for (j = 0; j < wiphy->bands[band]->n_channels; j++) {
Johannes Berg584991d2009-11-02 13:32:03 +01005174 struct ieee80211_channel *chan;
5175
5176 chan = &wiphy->bands[band]->channels[j];
5177
5178 if (chan->flags & IEEE80211_CHAN_DISABLED)
5179 continue;
5180
5181 request->channels[i] = chan;
Johannes Berg2a519312009-02-10 21:25:55 +01005182 i++;
5183 }
5184 }
5185 }
5186
Johannes Berg584991d2009-11-02 13:32:03 +01005187 if (!i) {
5188 err = -EINVAL;
5189 goto out_free;
5190 }
5191
5192 request->n_channels = i;
5193
Johannes Berg2a519312009-02-10 21:25:55 +01005194 i = 0;
5195 if (info->attrs[NL80211_ATTR_SCAN_SSIDS]) {
5196 nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS], tmp) {
Luciano Coelho57a27e12011-06-07 20:42:26 +03005197 if (nla_len(attr) > IEEE80211_MAX_SSID_LEN) {
Johannes Berg2a519312009-02-10 21:25:55 +01005198 err = -EINVAL;
5199 goto out_free;
5200 }
Luciano Coelho57a27e12011-06-07 20:42:26 +03005201 request->ssids[i].ssid_len = nla_len(attr);
Johannes Berg2a519312009-02-10 21:25:55 +01005202 memcpy(request->ssids[i].ssid, nla_data(attr), nla_len(attr));
Johannes Berg2a519312009-02-10 21:25:55 +01005203 i++;
5204 }
5205 }
5206
Jouni Malinen70692ad2009-02-16 19:39:13 +02005207 if (info->attrs[NL80211_ATTR_IE]) {
5208 request->ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
Johannes Bergde95a542009-04-01 11:58:36 +02005209 memcpy((void *)request->ie,
5210 nla_data(info->attrs[NL80211_ATTR_IE]),
Jouni Malinen70692ad2009-02-16 19:39:13 +02005211 request->ie_len);
5212 }
5213
Johannes Berg34850ab2011-07-18 18:08:35 +02005214 for (i = 0; i < IEEE80211_NUM_BANDS; i++)
Johannes Berga401d2b2011-07-20 00:52:16 +02005215 if (wiphy->bands[i])
5216 request->rates[i] =
5217 (1 << wiphy->bands[i]->n_bitrates) - 1;
Johannes Berg34850ab2011-07-18 18:08:35 +02005218
5219 if (info->attrs[NL80211_ATTR_SCAN_SUPP_RATES]) {
5220 nla_for_each_nested(attr,
5221 info->attrs[NL80211_ATTR_SCAN_SUPP_RATES],
5222 tmp) {
5223 enum ieee80211_band band = nla_type(attr);
5224
Dan Carpenter84404622011-07-29 11:52:18 +03005225 if (band < 0 || band >= IEEE80211_NUM_BANDS) {
Johannes Berg34850ab2011-07-18 18:08:35 +02005226 err = -EINVAL;
5227 goto out_free;
5228 }
5229 err = ieee80211_get_ratemask(wiphy->bands[band],
5230 nla_data(attr),
5231 nla_len(attr),
5232 &request->rates[band]);
5233 if (err)
5234 goto out_free;
5235 }
5236 }
5237
Sam Leffler46856bb2012-10-11 21:03:32 -07005238 if (info->attrs[NL80211_ATTR_SCAN_FLAGS]) {
Sam Lefflered4737712012-10-11 21:03:31 -07005239 request->flags = nla_get_u32(
5240 info->attrs[NL80211_ATTR_SCAN_FLAGS]);
Sam Leffler15d60302012-10-11 21:03:34 -07005241 if (((request->flags & NL80211_SCAN_FLAG_LOW_PRIORITY) &&
5242 !(wiphy->features & NL80211_FEATURE_LOW_PRIORITY_SCAN)) ||
5243 ((request->flags & NL80211_SCAN_FLAG_FLUSH) &&
5244 !(wiphy->features & NL80211_FEATURE_SCAN_FLUSH))) {
Sam Leffler46856bb2012-10-11 21:03:32 -07005245 err = -EOPNOTSUPP;
5246 goto out_free;
5247 }
5248 }
Sam Lefflered4737712012-10-11 21:03:31 -07005249
Rajkumar Manoharane9f935e2011-09-25 14:53:30 +05305250 request->no_cck =
5251 nla_get_flag(info->attrs[NL80211_ATTR_TX_NO_CCK_RATE]);
5252
Johannes Bergfd014282012-06-18 19:17:03 +02005253 request->wdev = wdev;
Johannes Berg79c97e92009-07-07 03:56:12 +02005254 request->wiphy = &rdev->wiphy;
Sam Leffler15d60302012-10-11 21:03:34 -07005255 request->scan_start = jiffies;
Johannes Berg2a519312009-02-10 21:25:55 +01005256
Johannes Berg79c97e92009-07-07 03:56:12 +02005257 rdev->scan_req = request;
Hila Gonene35e4d22012-06-27 17:19:42 +03005258 err = rdev_scan(rdev, request);
Johannes Berg2a519312009-02-10 21:25:55 +01005259
Johannes Berg463d0182009-07-14 00:33:35 +02005260 if (!err) {
Johannes Bergfd014282012-06-18 19:17:03 +02005261 nl80211_send_scan_start(rdev, wdev);
5262 if (wdev->netdev)
5263 dev_hold(wdev->netdev);
Johannes Berg4c476992010-10-04 21:36:35 +02005264 } else {
Johannes Berg2a519312009-02-10 21:25:55 +01005265 out_free:
Johannes Berg79c97e92009-07-07 03:56:12 +02005266 rdev->scan_req = NULL;
Johannes Berg2a519312009-02-10 21:25:55 +01005267 kfree(request);
5268 }
Johannes Berg3b858752009-03-12 09:55:09 +01005269
Johannes Bergf9f47522013-03-19 15:04:07 +01005270 unlock:
5271 mutex_unlock(&rdev->sched_scan_mtx);
Johannes Berg2a519312009-02-10 21:25:55 +01005272 return err;
5273}
5274
Luciano Coelho807f8a82011-05-11 17:09:35 +03005275static int nl80211_start_sched_scan(struct sk_buff *skb,
5276 struct genl_info *info)
5277{
5278 struct cfg80211_sched_scan_request *request;
5279 struct cfg80211_registered_device *rdev = info->user_ptr[0];
5280 struct net_device *dev = info->user_ptr[1];
Luciano Coelho807f8a82011-05-11 17:09:35 +03005281 struct nlattr *attr;
5282 struct wiphy *wiphy;
Luciano Coelhoa1f1c212011-08-31 16:01:48 +03005283 int err, tmp, n_ssids = 0, n_match_sets = 0, n_channels, i;
Luciano Coelhobbe6ad62011-05-11 17:09:37 +03005284 u32 interval;
Luciano Coelho807f8a82011-05-11 17:09:35 +03005285 enum ieee80211_band band;
5286 size_t ie_len;
Luciano Coelhoa1f1c212011-08-31 16:01:48 +03005287 struct nlattr *tb[NL80211_SCHED_SCAN_MATCH_ATTR_MAX + 1];
Luciano Coelho807f8a82011-05-11 17:09:35 +03005288
5289 if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) ||
5290 !rdev->ops->sched_scan_start)
5291 return -EOPNOTSUPP;
5292
5293 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
5294 return -EINVAL;
5295
Luciano Coelhobbe6ad62011-05-11 17:09:37 +03005296 if (!info->attrs[NL80211_ATTR_SCHED_SCAN_INTERVAL])
5297 return -EINVAL;
5298
5299 interval = nla_get_u32(info->attrs[NL80211_ATTR_SCHED_SCAN_INTERVAL]);
5300 if (interval == 0)
5301 return -EINVAL;
5302
Luciano Coelho807f8a82011-05-11 17:09:35 +03005303 wiphy = &rdev->wiphy;
5304
5305 if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
5306 n_channels = validate_scan_freqs(
5307 info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]);
5308 if (!n_channels)
5309 return -EINVAL;
5310 } else {
5311 n_channels = 0;
5312
5313 for (band = 0; band < IEEE80211_NUM_BANDS; band++)
5314 if (wiphy->bands[band])
5315 n_channels += wiphy->bands[band]->n_channels;
5316 }
5317
5318 if (info->attrs[NL80211_ATTR_SCAN_SSIDS])
5319 nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS],
5320 tmp)
5321 n_ssids++;
5322
Luciano Coelho93b6aa62011-07-13 14:57:28 +03005323 if (n_ssids > wiphy->max_sched_scan_ssids)
Luciano Coelho807f8a82011-05-11 17:09:35 +03005324 return -EINVAL;
5325
Luciano Coelhoa1f1c212011-08-31 16:01:48 +03005326 if (info->attrs[NL80211_ATTR_SCHED_SCAN_MATCH])
5327 nla_for_each_nested(attr,
5328 info->attrs[NL80211_ATTR_SCHED_SCAN_MATCH],
5329 tmp)
5330 n_match_sets++;
5331
5332 if (n_match_sets > wiphy->max_match_sets)
5333 return -EINVAL;
5334
Luciano Coelho807f8a82011-05-11 17:09:35 +03005335 if (info->attrs[NL80211_ATTR_IE])
5336 ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
5337 else
5338 ie_len = 0;
5339
Luciano Coelho5a865ba2011-07-13 14:57:29 +03005340 if (ie_len > wiphy->max_sched_scan_ie_len)
Luciano Coelho807f8a82011-05-11 17:09:35 +03005341 return -EINVAL;
5342
Luciano Coelhoc10841c2011-06-30 08:32:41 +03005343 mutex_lock(&rdev->sched_scan_mtx);
5344
5345 if (rdev->sched_scan_req) {
5346 err = -EINPROGRESS;
5347 goto out;
5348 }
5349
Luciano Coelho807f8a82011-05-11 17:09:35 +03005350 request = kzalloc(sizeof(*request)
Luciano Coelhoa2cd43c2011-05-18 11:42:03 +03005351 + sizeof(*request->ssids) * n_ssids
Luciano Coelhoa1f1c212011-08-31 16:01:48 +03005352 + sizeof(*request->match_sets) * n_match_sets
Luciano Coelhoa2cd43c2011-05-18 11:42:03 +03005353 + sizeof(*request->channels) * n_channels
Luciano Coelho807f8a82011-05-11 17:09:35 +03005354 + ie_len, GFP_KERNEL);
Luciano Coelhoc10841c2011-06-30 08:32:41 +03005355 if (!request) {
5356 err = -ENOMEM;
5357 goto out;
5358 }
Luciano Coelho807f8a82011-05-11 17:09:35 +03005359
5360 if (n_ssids)
5361 request->ssids = (void *)&request->channels[n_channels];
5362 request->n_ssids = n_ssids;
5363 if (ie_len) {
5364 if (request->ssids)
5365 request->ie = (void *)(request->ssids + n_ssids);
5366 else
5367 request->ie = (void *)(request->channels + n_channels);
5368 }
5369
Luciano Coelhoa1f1c212011-08-31 16:01:48 +03005370 if (n_match_sets) {
5371 if (request->ie)
5372 request->match_sets = (void *)(request->ie + ie_len);
5373 else if (request->ssids)
5374 request->match_sets =
5375 (void *)(request->ssids + n_ssids);
5376 else
5377 request->match_sets =
5378 (void *)(request->channels + n_channels);
5379 }
5380 request->n_match_sets = n_match_sets;
5381
Luciano Coelho807f8a82011-05-11 17:09:35 +03005382 i = 0;
5383 if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
5384 /* user specified, bail out if channel not found */
5385 nla_for_each_nested(attr,
5386 info->attrs[NL80211_ATTR_SCAN_FREQUENCIES],
5387 tmp) {
5388 struct ieee80211_channel *chan;
5389
5390 chan = ieee80211_get_channel(wiphy, nla_get_u32(attr));
5391
5392 if (!chan) {
5393 err = -EINVAL;
5394 goto out_free;
5395 }
5396
5397 /* ignore disabled channels */
5398 if (chan->flags & IEEE80211_CHAN_DISABLED)
5399 continue;
5400
5401 request->channels[i] = chan;
5402 i++;
5403 }
5404 } else {
5405 /* all channels */
5406 for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
5407 int j;
5408 if (!wiphy->bands[band])
5409 continue;
5410 for (j = 0; j < wiphy->bands[band]->n_channels; j++) {
5411 struct ieee80211_channel *chan;
5412
5413 chan = &wiphy->bands[band]->channels[j];
5414
5415 if (chan->flags & IEEE80211_CHAN_DISABLED)
5416 continue;
5417
5418 request->channels[i] = chan;
5419 i++;
5420 }
5421 }
5422 }
5423
5424 if (!i) {
5425 err = -EINVAL;
5426 goto out_free;
5427 }
5428
5429 request->n_channels = i;
5430
5431 i = 0;
5432 if (info->attrs[NL80211_ATTR_SCAN_SSIDS]) {
5433 nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS],
5434 tmp) {
Luciano Coelho57a27e12011-06-07 20:42:26 +03005435 if (nla_len(attr) > IEEE80211_MAX_SSID_LEN) {
Luciano Coelho807f8a82011-05-11 17:09:35 +03005436 err = -EINVAL;
5437 goto out_free;
5438 }
Luciano Coelho57a27e12011-06-07 20:42:26 +03005439 request->ssids[i].ssid_len = nla_len(attr);
Luciano Coelho807f8a82011-05-11 17:09:35 +03005440 memcpy(request->ssids[i].ssid, nla_data(attr),
5441 nla_len(attr));
Luciano Coelho807f8a82011-05-11 17:09:35 +03005442 i++;
5443 }
5444 }
5445
Luciano Coelhoa1f1c212011-08-31 16:01:48 +03005446 i = 0;
5447 if (info->attrs[NL80211_ATTR_SCHED_SCAN_MATCH]) {
5448 nla_for_each_nested(attr,
5449 info->attrs[NL80211_ATTR_SCHED_SCAN_MATCH],
5450 tmp) {
Thomas Pedersen88e920b2012-06-21 11:09:54 -07005451 struct nlattr *ssid, *rssi;
Luciano Coelhoa1f1c212011-08-31 16:01:48 +03005452
5453 nla_parse(tb, NL80211_SCHED_SCAN_MATCH_ATTR_MAX,
5454 nla_data(attr), nla_len(attr),
5455 nl80211_match_policy);
Johannes Berg4a4ab0d2012-06-13 11:17:11 +02005456 ssid = tb[NL80211_SCHED_SCAN_MATCH_ATTR_SSID];
Luciano Coelhoa1f1c212011-08-31 16:01:48 +03005457 if (ssid) {
5458 if (nla_len(ssid) > IEEE80211_MAX_SSID_LEN) {
5459 err = -EINVAL;
5460 goto out_free;
5461 }
5462 memcpy(request->match_sets[i].ssid.ssid,
5463 nla_data(ssid), nla_len(ssid));
5464 request->match_sets[i].ssid.ssid_len =
5465 nla_len(ssid);
5466 }
Thomas Pedersen88e920b2012-06-21 11:09:54 -07005467 rssi = tb[NL80211_SCHED_SCAN_MATCH_ATTR_RSSI];
5468 if (rssi)
5469 request->rssi_thold = nla_get_u32(rssi);
5470 else
5471 request->rssi_thold =
5472 NL80211_SCAN_RSSI_THOLD_OFF;
Luciano Coelhoa1f1c212011-08-31 16:01:48 +03005473 i++;
5474 }
5475 }
5476
Luciano Coelho807f8a82011-05-11 17:09:35 +03005477 if (info->attrs[NL80211_ATTR_IE]) {
5478 request->ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
5479 memcpy((void *)request->ie,
5480 nla_data(info->attrs[NL80211_ATTR_IE]),
5481 request->ie_len);
5482 }
5483
Sam Leffler46856bb2012-10-11 21:03:32 -07005484 if (info->attrs[NL80211_ATTR_SCAN_FLAGS]) {
Sam Lefflered4737712012-10-11 21:03:31 -07005485 request->flags = nla_get_u32(
5486 info->attrs[NL80211_ATTR_SCAN_FLAGS]);
Sam Leffler15d60302012-10-11 21:03:34 -07005487 if (((request->flags & NL80211_SCAN_FLAG_LOW_PRIORITY) &&
5488 !(wiphy->features & NL80211_FEATURE_LOW_PRIORITY_SCAN)) ||
5489 ((request->flags & NL80211_SCAN_FLAG_FLUSH) &&
5490 !(wiphy->features & NL80211_FEATURE_SCAN_FLUSH))) {
Sam Leffler46856bb2012-10-11 21:03:32 -07005491 err = -EOPNOTSUPP;
5492 goto out_free;
5493 }
5494 }
Sam Lefflered4737712012-10-11 21:03:31 -07005495
Luciano Coelho807f8a82011-05-11 17:09:35 +03005496 request->dev = dev;
5497 request->wiphy = &rdev->wiphy;
Luciano Coelhobbe6ad62011-05-11 17:09:37 +03005498 request->interval = interval;
Sam Leffler15d60302012-10-11 21:03:34 -07005499 request->scan_start = jiffies;
Luciano Coelho807f8a82011-05-11 17:09:35 +03005500
Hila Gonene35e4d22012-06-27 17:19:42 +03005501 err = rdev_sched_scan_start(rdev, dev, request);
Luciano Coelho807f8a82011-05-11 17:09:35 +03005502 if (!err) {
5503 rdev->sched_scan_req = request;
5504 nl80211_send_sched_scan(rdev, dev,
5505 NL80211_CMD_START_SCHED_SCAN);
5506 goto out;
5507 }
5508
5509out_free:
5510 kfree(request);
5511out:
Luciano Coelhoc10841c2011-06-30 08:32:41 +03005512 mutex_unlock(&rdev->sched_scan_mtx);
Luciano Coelho807f8a82011-05-11 17:09:35 +03005513 return err;
5514}
5515
5516static int nl80211_stop_sched_scan(struct sk_buff *skb,
5517 struct genl_info *info)
5518{
5519 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Luciano Coelhoc10841c2011-06-30 08:32:41 +03005520 int err;
Luciano Coelho807f8a82011-05-11 17:09:35 +03005521
5522 if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) ||
5523 !rdev->ops->sched_scan_stop)
5524 return -EOPNOTSUPP;
5525
Luciano Coelhoc10841c2011-06-30 08:32:41 +03005526 mutex_lock(&rdev->sched_scan_mtx);
5527 err = __cfg80211_stop_sched_scan(rdev, false);
5528 mutex_unlock(&rdev->sched_scan_mtx);
5529
5530 return err;
Luciano Coelho807f8a82011-05-11 17:09:35 +03005531}
5532
Simon Wunderlich04f39042013-02-08 18:16:19 +01005533static int nl80211_start_radar_detection(struct sk_buff *skb,
5534 struct genl_info *info)
5535{
5536 struct cfg80211_registered_device *rdev = info->user_ptr[0];
5537 struct net_device *dev = info->user_ptr[1];
5538 struct wireless_dev *wdev = dev->ieee80211_ptr;
5539 struct cfg80211_chan_def chandef;
5540 int err;
5541
5542 err = nl80211_parse_chandef(rdev, info, &chandef);
5543 if (err)
5544 return err;
5545
5546 if (wdev->cac_started)
5547 return -EBUSY;
5548
5549 err = cfg80211_chandef_dfs_required(wdev->wiphy, &chandef);
5550 if (err < 0)
5551 return err;
5552
5553 if (err == 0)
5554 return -EINVAL;
5555
5556 if (chandef.chan->dfs_state != NL80211_DFS_USABLE)
5557 return -EINVAL;
5558
5559 if (!rdev->ops->start_radar_detection)
5560 return -EOPNOTSUPP;
5561
5562 mutex_lock(&rdev->devlist_mtx);
5563 err = cfg80211_can_use_iftype_chan(rdev, wdev, wdev->iftype,
5564 chandef.chan, CHAN_MODE_SHARED,
5565 BIT(chandef.width));
5566 if (err)
5567 goto err_locked;
5568
5569 err = rdev->ops->start_radar_detection(&rdev->wiphy, dev, &chandef);
5570 if (!err) {
5571 wdev->channel = chandef.chan;
5572 wdev->cac_started = true;
5573 wdev->cac_start_time = jiffies;
5574 }
5575err_locked:
5576 mutex_unlock(&rdev->devlist_mtx);
5577
5578 return err;
5579}
5580
Johannes Berg9720bb32011-06-21 09:45:33 +02005581static int nl80211_send_bss(struct sk_buff *msg, struct netlink_callback *cb,
5582 u32 seq, int flags,
Johannes Berg2a519312009-02-10 21:25:55 +01005583 struct cfg80211_registered_device *rdev,
Johannes Berg48ab9052009-07-10 18:42:31 +02005584 struct wireless_dev *wdev,
5585 struct cfg80211_internal_bss *intbss)
Johannes Berg2a519312009-02-10 21:25:55 +01005586{
Johannes Berg48ab9052009-07-10 18:42:31 +02005587 struct cfg80211_bss *res = &intbss->pub;
Johannes Berg9caf0362012-11-29 01:25:20 +01005588 const struct cfg80211_bss_ies *ies;
Johannes Berg2a519312009-02-10 21:25:55 +01005589 void *hdr;
5590 struct nlattr *bss;
Johannes Berg8cef2c92013-02-05 16:54:31 +01005591 bool tsf = false;
Johannes Berg48ab9052009-07-10 18:42:31 +02005592
5593 ASSERT_WDEV_LOCK(wdev);
Johannes Berg2a519312009-02-10 21:25:55 +01005594
Eric W. Biederman15e47302012-09-07 20:12:54 +00005595 hdr = nl80211hdr_put(msg, NETLINK_CB(cb->skb).portid, seq, flags,
Johannes Berg2a519312009-02-10 21:25:55 +01005596 NL80211_CMD_NEW_SCAN_RESULTS);
5597 if (!hdr)
5598 return -1;
5599
Johannes Berg9720bb32011-06-21 09:45:33 +02005600 genl_dump_check_consistent(cb, hdr, &nl80211_fam);
5601
Johannes Berg97990a02013-04-19 01:02:55 +02005602 if (nla_put_u32(msg, NL80211_ATTR_GENERATION, rdev->bss_generation))
5603 goto nla_put_failure;
5604 if (wdev->netdev &&
David S. Miller9360ffd2012-03-29 04:41:26 -04005605 nla_put_u32(msg, NL80211_ATTR_IFINDEX, wdev->netdev->ifindex))
5606 goto nla_put_failure;
Johannes Berg97990a02013-04-19 01:02:55 +02005607 if (nla_put_u64(msg, NL80211_ATTR_WDEV, wdev_id(wdev)))
5608 goto nla_put_failure;
Johannes Berg2a519312009-02-10 21:25:55 +01005609
5610 bss = nla_nest_start(msg, NL80211_ATTR_BSS);
5611 if (!bss)
5612 goto nla_put_failure;
David S. Miller9360ffd2012-03-29 04:41:26 -04005613 if ((!is_zero_ether_addr(res->bssid) &&
Johannes Berg9caf0362012-11-29 01:25:20 +01005614 nla_put(msg, NL80211_BSS_BSSID, ETH_ALEN, res->bssid)))
David S. Miller9360ffd2012-03-29 04:41:26 -04005615 goto nla_put_failure;
Johannes Berg9caf0362012-11-29 01:25:20 +01005616
5617 rcu_read_lock();
5618 ies = rcu_dereference(res->ies);
Johannes Berg8cef2c92013-02-05 16:54:31 +01005619 if (ies) {
5620 if (nla_put_u64(msg, NL80211_BSS_TSF, ies->tsf))
5621 goto fail_unlock_rcu;
5622 tsf = true;
5623 if (ies->len && nla_put(msg, NL80211_BSS_INFORMATION_ELEMENTS,
5624 ies->len, ies->data))
5625 goto fail_unlock_rcu;
Johannes Berg9caf0362012-11-29 01:25:20 +01005626 }
5627 ies = rcu_dereference(res->beacon_ies);
Johannes Berg8cef2c92013-02-05 16:54:31 +01005628 if (ies) {
5629 if (!tsf && nla_put_u64(msg, NL80211_BSS_TSF, ies->tsf))
5630 goto fail_unlock_rcu;
5631 if (ies->len && nla_put(msg, NL80211_BSS_BEACON_IES,
5632 ies->len, ies->data))
5633 goto fail_unlock_rcu;
Johannes Berg9caf0362012-11-29 01:25:20 +01005634 }
5635 rcu_read_unlock();
5636
David S. Miller9360ffd2012-03-29 04:41:26 -04005637 if (res->beacon_interval &&
5638 nla_put_u16(msg, NL80211_BSS_BEACON_INTERVAL, res->beacon_interval))
5639 goto nla_put_failure;
5640 if (nla_put_u16(msg, NL80211_BSS_CAPABILITY, res->capability) ||
5641 nla_put_u32(msg, NL80211_BSS_FREQUENCY, res->channel->center_freq) ||
5642 nla_put_u32(msg, NL80211_BSS_SEEN_MS_AGO,
5643 jiffies_to_msecs(jiffies - intbss->ts)))
5644 goto nla_put_failure;
Johannes Berg2a519312009-02-10 21:25:55 +01005645
Johannes Berg77965c92009-02-18 18:45:06 +01005646 switch (rdev->wiphy.signal_type) {
Johannes Berg2a519312009-02-10 21:25:55 +01005647 case CFG80211_SIGNAL_TYPE_MBM:
David S. Miller9360ffd2012-03-29 04:41:26 -04005648 if (nla_put_u32(msg, NL80211_BSS_SIGNAL_MBM, res->signal))
5649 goto nla_put_failure;
Johannes Berg2a519312009-02-10 21:25:55 +01005650 break;
5651 case CFG80211_SIGNAL_TYPE_UNSPEC:
David S. Miller9360ffd2012-03-29 04:41:26 -04005652 if (nla_put_u8(msg, NL80211_BSS_SIGNAL_UNSPEC, res->signal))
5653 goto nla_put_failure;
Johannes Berg2a519312009-02-10 21:25:55 +01005654 break;
5655 default:
5656 break;
5657 }
5658
Johannes Berg48ab9052009-07-10 18:42:31 +02005659 switch (wdev->iftype) {
Johannes Berg074ac8d2010-09-16 14:58:22 +02005660 case NL80211_IFTYPE_P2P_CLIENT:
Johannes Berg48ab9052009-07-10 18:42:31 +02005661 case NL80211_IFTYPE_STATION:
David S. Miller9360ffd2012-03-29 04:41:26 -04005662 if (intbss == wdev->current_bss &&
5663 nla_put_u32(msg, NL80211_BSS_STATUS,
5664 NL80211_BSS_STATUS_ASSOCIATED))
5665 goto nla_put_failure;
Johannes Berg48ab9052009-07-10 18:42:31 +02005666 break;
5667 case NL80211_IFTYPE_ADHOC:
David S. Miller9360ffd2012-03-29 04:41:26 -04005668 if (intbss == wdev->current_bss &&
5669 nla_put_u32(msg, NL80211_BSS_STATUS,
5670 NL80211_BSS_STATUS_IBSS_JOINED))
5671 goto nla_put_failure;
Johannes Berg48ab9052009-07-10 18:42:31 +02005672 break;
5673 default:
5674 break;
5675 }
5676
Johannes Berg2a519312009-02-10 21:25:55 +01005677 nla_nest_end(msg, bss);
5678
5679 return genlmsg_end(msg, hdr);
5680
Johannes Berg8cef2c92013-02-05 16:54:31 +01005681 fail_unlock_rcu:
5682 rcu_read_unlock();
Johannes Berg2a519312009-02-10 21:25:55 +01005683 nla_put_failure:
5684 genlmsg_cancel(msg, hdr);
5685 return -EMSGSIZE;
5686}
5687
Johannes Berg97990a02013-04-19 01:02:55 +02005688static int nl80211_dump_scan(struct sk_buff *skb, struct netlink_callback *cb)
Johannes Berg2a519312009-02-10 21:25:55 +01005689{
Johannes Berg48ab9052009-07-10 18:42:31 +02005690 struct cfg80211_registered_device *rdev;
Johannes Berg2a519312009-02-10 21:25:55 +01005691 struct cfg80211_internal_bss *scan;
Johannes Berg48ab9052009-07-10 18:42:31 +02005692 struct wireless_dev *wdev;
Johannes Berg97990a02013-04-19 01:02:55 +02005693 int start = cb->args[2], idx = 0;
Johannes Berg2a519312009-02-10 21:25:55 +01005694 int err;
5695
Johannes Berg97990a02013-04-19 01:02:55 +02005696 err = nl80211_prepare_wdev_dump(skb, cb, &rdev, &wdev);
Johannes Berg67748892010-10-04 21:14:06 +02005697 if (err)
5698 return err;
Johannes Berg2a519312009-02-10 21:25:55 +01005699
Johannes Berg48ab9052009-07-10 18:42:31 +02005700 wdev_lock(wdev);
5701 spin_lock_bh(&rdev->bss_lock);
5702 cfg80211_bss_expire(rdev);
5703
Johannes Berg9720bb32011-06-21 09:45:33 +02005704 cb->seq = rdev->bss_generation;
5705
Johannes Berg48ab9052009-07-10 18:42:31 +02005706 list_for_each_entry(scan, &rdev->bss_list, list) {
Johannes Berg2a519312009-02-10 21:25:55 +01005707 if (++idx <= start)
5708 continue;
Johannes Berg9720bb32011-06-21 09:45:33 +02005709 if (nl80211_send_bss(skb, cb,
Johannes Berg2a519312009-02-10 21:25:55 +01005710 cb->nlh->nlmsg_seq, NLM_F_MULTI,
Johannes Berg48ab9052009-07-10 18:42:31 +02005711 rdev, wdev, scan) < 0) {
Johannes Berg2a519312009-02-10 21:25:55 +01005712 idx--;
Johannes Berg67748892010-10-04 21:14:06 +02005713 break;
Johannes Berg2a519312009-02-10 21:25:55 +01005714 }
5715 }
5716
Johannes Berg48ab9052009-07-10 18:42:31 +02005717 spin_unlock_bh(&rdev->bss_lock);
5718 wdev_unlock(wdev);
Johannes Berg2a519312009-02-10 21:25:55 +01005719
Johannes Berg97990a02013-04-19 01:02:55 +02005720 cb->args[2] = idx;
5721 nl80211_finish_wdev_dump(rdev);
Johannes Berg2a519312009-02-10 21:25:55 +01005722
Johannes Berg67748892010-10-04 21:14:06 +02005723 return skb->len;
Johannes Berg2a519312009-02-10 21:25:55 +01005724}
5725
Eric W. Biederman15e47302012-09-07 20:12:54 +00005726static int nl80211_send_survey(struct sk_buff *msg, u32 portid, u32 seq,
Holger Schurig61fa7132009-11-11 12:25:40 +01005727 int flags, struct net_device *dev,
5728 struct survey_info *survey)
5729{
5730 void *hdr;
5731 struct nlattr *infoattr;
5732
Eric W. Biederman15e47302012-09-07 20:12:54 +00005733 hdr = nl80211hdr_put(msg, portid, seq, flags,
Holger Schurig61fa7132009-11-11 12:25:40 +01005734 NL80211_CMD_NEW_SURVEY_RESULTS);
5735 if (!hdr)
5736 return -ENOMEM;
5737
David S. Miller9360ffd2012-03-29 04:41:26 -04005738 if (nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex))
5739 goto nla_put_failure;
Holger Schurig61fa7132009-11-11 12:25:40 +01005740
5741 infoattr = nla_nest_start(msg, NL80211_ATTR_SURVEY_INFO);
5742 if (!infoattr)
5743 goto nla_put_failure;
5744
David S. Miller9360ffd2012-03-29 04:41:26 -04005745 if (nla_put_u32(msg, NL80211_SURVEY_INFO_FREQUENCY,
5746 survey->channel->center_freq))
5747 goto nla_put_failure;
5748
5749 if ((survey->filled & SURVEY_INFO_NOISE_DBM) &&
5750 nla_put_u8(msg, NL80211_SURVEY_INFO_NOISE, survey->noise))
5751 goto nla_put_failure;
5752 if ((survey->filled & SURVEY_INFO_IN_USE) &&
5753 nla_put_flag(msg, NL80211_SURVEY_INFO_IN_USE))
5754 goto nla_put_failure;
5755 if ((survey->filled & SURVEY_INFO_CHANNEL_TIME) &&
5756 nla_put_u64(msg, NL80211_SURVEY_INFO_CHANNEL_TIME,
5757 survey->channel_time))
5758 goto nla_put_failure;
5759 if ((survey->filled & SURVEY_INFO_CHANNEL_TIME_BUSY) &&
5760 nla_put_u64(msg, NL80211_SURVEY_INFO_CHANNEL_TIME_BUSY,
5761 survey->channel_time_busy))
5762 goto nla_put_failure;
5763 if ((survey->filled & SURVEY_INFO_CHANNEL_TIME_EXT_BUSY) &&
5764 nla_put_u64(msg, NL80211_SURVEY_INFO_CHANNEL_TIME_EXT_BUSY,
5765 survey->channel_time_ext_busy))
5766 goto nla_put_failure;
5767 if ((survey->filled & SURVEY_INFO_CHANNEL_TIME_RX) &&
5768 nla_put_u64(msg, NL80211_SURVEY_INFO_CHANNEL_TIME_RX,
5769 survey->channel_time_rx))
5770 goto nla_put_failure;
5771 if ((survey->filled & SURVEY_INFO_CHANNEL_TIME_TX) &&
5772 nla_put_u64(msg, NL80211_SURVEY_INFO_CHANNEL_TIME_TX,
5773 survey->channel_time_tx))
5774 goto nla_put_failure;
Holger Schurig61fa7132009-11-11 12:25:40 +01005775
5776 nla_nest_end(msg, infoattr);
5777
5778 return genlmsg_end(msg, hdr);
5779
5780 nla_put_failure:
5781 genlmsg_cancel(msg, hdr);
5782 return -EMSGSIZE;
5783}
5784
5785static int nl80211_dump_survey(struct sk_buff *skb,
5786 struct netlink_callback *cb)
5787{
5788 struct survey_info survey;
5789 struct cfg80211_registered_device *dev;
Johannes Berg97990a02013-04-19 01:02:55 +02005790 struct wireless_dev *wdev;
5791 int survey_idx = cb->args[2];
Holger Schurig61fa7132009-11-11 12:25:40 +01005792 int res;
5793
Johannes Berg97990a02013-04-19 01:02:55 +02005794 res = nl80211_prepare_wdev_dump(skb, cb, &dev, &wdev);
Johannes Berg67748892010-10-04 21:14:06 +02005795 if (res)
5796 return res;
Holger Schurig61fa7132009-11-11 12:25:40 +01005797
Johannes Berg97990a02013-04-19 01:02:55 +02005798 if (!wdev->netdev) {
5799 res = -EINVAL;
5800 goto out_err;
5801 }
5802
Holger Schurig61fa7132009-11-11 12:25:40 +01005803 if (!dev->ops->dump_survey) {
5804 res = -EOPNOTSUPP;
5805 goto out_err;
5806 }
5807
5808 while (1) {
Luis R. Rodriguez180cdc72011-05-27 07:24:02 -07005809 struct ieee80211_channel *chan;
5810
Johannes Berg97990a02013-04-19 01:02:55 +02005811 res = rdev_dump_survey(dev, wdev->netdev, survey_idx, &survey);
Holger Schurig61fa7132009-11-11 12:25:40 +01005812 if (res == -ENOENT)
5813 break;
5814 if (res)
5815 goto out_err;
5816
Luis R. Rodriguez180cdc72011-05-27 07:24:02 -07005817 /* Survey without a channel doesn't make sense */
5818 if (!survey.channel) {
5819 res = -EINVAL;
5820 goto out;
5821 }
5822
5823 chan = ieee80211_get_channel(&dev->wiphy,
5824 survey.channel->center_freq);
5825 if (!chan || chan->flags & IEEE80211_CHAN_DISABLED) {
5826 survey_idx++;
5827 continue;
5828 }
5829
Holger Schurig61fa7132009-11-11 12:25:40 +01005830 if (nl80211_send_survey(skb,
Eric W. Biederman15e47302012-09-07 20:12:54 +00005831 NETLINK_CB(cb->skb).portid,
Holger Schurig61fa7132009-11-11 12:25:40 +01005832 cb->nlh->nlmsg_seq, NLM_F_MULTI,
Johannes Berg97990a02013-04-19 01:02:55 +02005833 wdev->netdev, &survey) < 0)
Holger Schurig61fa7132009-11-11 12:25:40 +01005834 goto out;
5835 survey_idx++;
5836 }
5837
5838 out:
Johannes Berg97990a02013-04-19 01:02:55 +02005839 cb->args[2] = survey_idx;
Holger Schurig61fa7132009-11-11 12:25:40 +01005840 res = skb->len;
5841 out_err:
Johannes Berg97990a02013-04-19 01:02:55 +02005842 nl80211_finish_wdev_dump(dev);
Holger Schurig61fa7132009-11-11 12:25:40 +01005843 return res;
5844}
5845
Samuel Ortizb23aa672009-07-01 21:26:54 +02005846static bool nl80211_valid_wpa_versions(u32 wpa_versions)
5847{
5848 return !(wpa_versions & ~(NL80211_WPA_VERSION_1 |
5849 NL80211_WPA_VERSION_2));
5850}
5851
Jouni Malinen636a5d32009-03-19 13:39:22 +02005852static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info)
5853{
Johannes Berg4c476992010-10-04 21:36:35 +02005854 struct cfg80211_registered_device *rdev = info->user_ptr[0];
5855 struct net_device *dev = info->user_ptr[1];
Johannes Berg19957bb2009-07-02 17:20:43 +02005856 struct ieee80211_channel *chan;
Jouni Malinene39e5b52012-09-30 19:29:39 +03005857 const u8 *bssid, *ssid, *ie = NULL, *sae_data = NULL;
5858 int err, ssid_len, ie_len = 0, sae_data_len = 0;
Johannes Berg19957bb2009-07-02 17:20:43 +02005859 enum nl80211_auth_type auth_type;
Johannes Bergfffd0932009-07-08 14:22:54 +02005860 struct key_parse key;
Jouni Malinend5cdfac2010-04-04 09:37:19 +03005861 bool local_state_change;
Jouni Malinen636a5d32009-03-19 13:39:22 +02005862
Johannes Bergf4a11bb2009-03-27 12:40:28 +01005863 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
5864 return -EINVAL;
5865
5866 if (!info->attrs[NL80211_ATTR_MAC])
5867 return -EINVAL;
5868
Jouni Malinen17780922009-03-27 20:52:47 +02005869 if (!info->attrs[NL80211_ATTR_AUTH_TYPE])
5870 return -EINVAL;
5871
Johannes Berg19957bb2009-07-02 17:20:43 +02005872 if (!info->attrs[NL80211_ATTR_SSID])
5873 return -EINVAL;
5874
5875 if (!info->attrs[NL80211_ATTR_WIPHY_FREQ])
5876 return -EINVAL;
5877
Johannes Bergfffd0932009-07-08 14:22:54 +02005878 err = nl80211_parse_key(info, &key);
5879 if (err)
5880 return err;
5881
5882 if (key.idx >= 0) {
Johannes Berge31b8212010-10-05 19:39:30 +02005883 if (key.type != -1 && key.type != NL80211_KEYTYPE_GROUP)
5884 return -EINVAL;
Johannes Bergfffd0932009-07-08 14:22:54 +02005885 if (!key.p.key || !key.p.key_len)
5886 return -EINVAL;
5887 if ((key.p.cipher != WLAN_CIPHER_SUITE_WEP40 ||
5888 key.p.key_len != WLAN_KEY_LEN_WEP40) &&
5889 (key.p.cipher != WLAN_CIPHER_SUITE_WEP104 ||
5890 key.p.key_len != WLAN_KEY_LEN_WEP104))
5891 return -EINVAL;
5892 if (key.idx > 4)
5893 return -EINVAL;
5894 } else {
5895 key.p.key_len = 0;
5896 key.p.key = NULL;
5897 }
5898
Johannes Bergafea0b72010-08-10 09:46:42 +02005899 if (key.idx >= 0) {
5900 int i;
5901 bool ok = false;
5902 for (i = 0; i < rdev->wiphy.n_cipher_suites; i++) {
5903 if (key.p.cipher == rdev->wiphy.cipher_suites[i]) {
5904 ok = true;
5905 break;
5906 }
5907 }
Johannes Berg4c476992010-10-04 21:36:35 +02005908 if (!ok)
5909 return -EINVAL;
Johannes Bergafea0b72010-08-10 09:46:42 +02005910 }
5911
Johannes Berg4c476992010-10-04 21:36:35 +02005912 if (!rdev->ops->auth)
5913 return -EOPNOTSUPP;
Jouni Malinen636a5d32009-03-19 13:39:22 +02005914
Johannes Berg074ac8d2010-09-16 14:58:22 +02005915 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg4c476992010-10-04 21:36:35 +02005916 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT)
5917 return -EOPNOTSUPP;
Jouni Malineneec60b02009-03-20 21:21:19 +02005918
Johannes Berg19957bb2009-07-02 17:20:43 +02005919 bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
Johannes Berg79c97e92009-07-07 03:56:12 +02005920 chan = ieee80211_get_channel(&rdev->wiphy,
Johannes Berg19957bb2009-07-02 17:20:43 +02005921 nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]));
Johannes Berg4c476992010-10-04 21:36:35 +02005922 if (!chan || (chan->flags & IEEE80211_CHAN_DISABLED))
5923 return -EINVAL;
Jouni Malinen636a5d32009-03-19 13:39:22 +02005924
Johannes Berg19957bb2009-07-02 17:20:43 +02005925 ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
5926 ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
5927
5928 if (info->attrs[NL80211_ATTR_IE]) {
5929 ie = nla_data(info->attrs[NL80211_ATTR_IE]);
5930 ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
5931 }
5932
5933 auth_type = nla_get_u32(info->attrs[NL80211_ATTR_AUTH_TYPE]);
Jouni Malinene39e5b52012-09-30 19:29:39 +03005934 if (!nl80211_valid_auth_type(rdev, auth_type, NL80211_CMD_AUTHENTICATE))
Johannes Berg4c476992010-10-04 21:36:35 +02005935 return -EINVAL;
Johannes Berg19957bb2009-07-02 17:20:43 +02005936
Jouni Malinene39e5b52012-09-30 19:29:39 +03005937 if (auth_type == NL80211_AUTHTYPE_SAE &&
5938 !info->attrs[NL80211_ATTR_SAE_DATA])
5939 return -EINVAL;
5940
5941 if (info->attrs[NL80211_ATTR_SAE_DATA]) {
5942 if (auth_type != NL80211_AUTHTYPE_SAE)
5943 return -EINVAL;
5944 sae_data = nla_data(info->attrs[NL80211_ATTR_SAE_DATA]);
5945 sae_data_len = nla_len(info->attrs[NL80211_ATTR_SAE_DATA]);
5946 /* need to include at least Auth Transaction and Status Code */
5947 if (sae_data_len < 4)
5948 return -EINVAL;
5949 }
5950
Jouni Malinend5cdfac2010-04-04 09:37:19 +03005951 local_state_change = !!info->attrs[NL80211_ATTR_LOCAL_STATE_CHANGE];
5952
Johannes Berg95de8172012-01-20 13:55:25 +01005953 /*
5954 * Since we no longer track auth state, ignore
5955 * requests to only change local state.
5956 */
5957 if (local_state_change)
5958 return 0;
5959
Johannes Berg4c476992010-10-04 21:36:35 +02005960 return cfg80211_mlme_auth(rdev, dev, chan, auth_type, bssid,
5961 ssid, ssid_len, ie, ie_len,
Jouni Malinene39e5b52012-09-30 19:29:39 +03005962 key.p.key, key.p.key_len, key.idx,
5963 sae_data, sae_data_len);
Jouni Malinen636a5d32009-03-19 13:39:22 +02005964}
5965
Johannes Bergc0692b82010-08-27 14:26:53 +03005966static int nl80211_crypto_settings(struct cfg80211_registered_device *rdev,
5967 struct genl_info *info,
Johannes Berg3dc27d22009-07-02 21:36:37 +02005968 struct cfg80211_crypto_settings *settings,
5969 int cipher_limit)
Samuel Ortizb23aa672009-07-01 21:26:54 +02005970{
Johannes Bergc0b2bbd2009-07-25 16:54:36 +02005971 memset(settings, 0, sizeof(*settings));
5972
Samuel Ortizb23aa672009-07-01 21:26:54 +02005973 settings->control_port = info->attrs[NL80211_ATTR_CONTROL_PORT];
5974
Johannes Bergc0692b82010-08-27 14:26:53 +03005975 if (info->attrs[NL80211_ATTR_CONTROL_PORT_ETHERTYPE]) {
5976 u16 proto;
5977 proto = nla_get_u16(
5978 info->attrs[NL80211_ATTR_CONTROL_PORT_ETHERTYPE]);
5979 settings->control_port_ethertype = cpu_to_be16(proto);
5980 if (!(rdev->wiphy.flags & WIPHY_FLAG_CONTROL_PORT_PROTOCOL) &&
5981 proto != ETH_P_PAE)
5982 return -EINVAL;
5983 if (info->attrs[NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT])
5984 settings->control_port_no_encrypt = true;
5985 } else
5986 settings->control_port_ethertype = cpu_to_be16(ETH_P_PAE);
5987
Samuel Ortizb23aa672009-07-01 21:26:54 +02005988 if (info->attrs[NL80211_ATTR_CIPHER_SUITES_PAIRWISE]) {
5989 void *data;
5990 int len, i;
5991
5992 data = nla_data(info->attrs[NL80211_ATTR_CIPHER_SUITES_PAIRWISE]);
5993 len = nla_len(info->attrs[NL80211_ATTR_CIPHER_SUITES_PAIRWISE]);
5994 settings->n_ciphers_pairwise = len / sizeof(u32);
5995
5996 if (len % sizeof(u32))
5997 return -EINVAL;
5998
Johannes Berg3dc27d22009-07-02 21:36:37 +02005999 if (settings->n_ciphers_pairwise > cipher_limit)
Samuel Ortizb23aa672009-07-01 21:26:54 +02006000 return -EINVAL;
6001
6002 memcpy(settings->ciphers_pairwise, data, len);
6003
6004 for (i = 0; i < settings->n_ciphers_pairwise; i++)
Jouni Malinen38ba3c52011-09-21 18:14:56 +03006005 if (!cfg80211_supported_cipher_suite(
6006 &rdev->wiphy,
Samuel Ortizb23aa672009-07-01 21:26:54 +02006007 settings->ciphers_pairwise[i]))
6008 return -EINVAL;
6009 }
6010
6011 if (info->attrs[NL80211_ATTR_CIPHER_SUITE_GROUP]) {
6012 settings->cipher_group =
6013 nla_get_u32(info->attrs[NL80211_ATTR_CIPHER_SUITE_GROUP]);
Jouni Malinen38ba3c52011-09-21 18:14:56 +03006014 if (!cfg80211_supported_cipher_suite(&rdev->wiphy,
6015 settings->cipher_group))
Samuel Ortizb23aa672009-07-01 21:26:54 +02006016 return -EINVAL;
6017 }
6018
6019 if (info->attrs[NL80211_ATTR_WPA_VERSIONS]) {
6020 settings->wpa_versions =
6021 nla_get_u32(info->attrs[NL80211_ATTR_WPA_VERSIONS]);
6022 if (!nl80211_valid_wpa_versions(settings->wpa_versions))
6023 return -EINVAL;
6024 }
6025
6026 if (info->attrs[NL80211_ATTR_AKM_SUITES]) {
6027 void *data;
Jouni Malinen6d302402011-09-21 18:11:33 +03006028 int len;
Samuel Ortizb23aa672009-07-01 21:26:54 +02006029
6030 data = nla_data(info->attrs[NL80211_ATTR_AKM_SUITES]);
6031 len = nla_len(info->attrs[NL80211_ATTR_AKM_SUITES]);
6032 settings->n_akm_suites = len / sizeof(u32);
6033
6034 if (len % sizeof(u32))
6035 return -EINVAL;
6036
Jouni Malinen1b9ca022011-09-21 16:13:07 +03006037 if (settings->n_akm_suites > NL80211_MAX_NR_AKM_SUITES)
6038 return -EINVAL;
6039
Samuel Ortizb23aa672009-07-01 21:26:54 +02006040 memcpy(settings->akm_suites, data, len);
Samuel Ortizb23aa672009-07-01 21:26:54 +02006041 }
6042
6043 return 0;
6044}
6045
Jouni Malinen636a5d32009-03-19 13:39:22 +02006046static int nl80211_associate(struct sk_buff *skb, struct genl_info *info)
6047{
Johannes Berg4c476992010-10-04 21:36:35 +02006048 struct cfg80211_registered_device *rdev = info->user_ptr[0];
6049 struct net_device *dev = info->user_ptr[1];
Johannes Bergf444de02010-05-05 15:25:02 +02006050 struct ieee80211_channel *chan;
Johannes Bergf62fab72013-02-21 20:09:09 +01006051 struct cfg80211_assoc_request req = {};
6052 const u8 *bssid, *ssid;
6053 int err, ssid_len = 0;
Jouni Malinen636a5d32009-03-19 13:39:22 +02006054
Johannes Bergf4a11bb2009-03-27 12:40:28 +01006055 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
6056 return -EINVAL;
6057
6058 if (!info->attrs[NL80211_ATTR_MAC] ||
Johannes Berg19957bb2009-07-02 17:20:43 +02006059 !info->attrs[NL80211_ATTR_SSID] ||
6060 !info->attrs[NL80211_ATTR_WIPHY_FREQ])
Johannes Bergf4a11bb2009-03-27 12:40:28 +01006061 return -EINVAL;
6062
Johannes Berg4c476992010-10-04 21:36:35 +02006063 if (!rdev->ops->assoc)
6064 return -EOPNOTSUPP;
Jouni Malinen636a5d32009-03-19 13:39:22 +02006065
Johannes Berg074ac8d2010-09-16 14:58:22 +02006066 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg4c476992010-10-04 21:36:35 +02006067 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT)
6068 return -EOPNOTSUPP;
Jouni Malineneec60b02009-03-20 21:21:19 +02006069
Johannes Berg19957bb2009-07-02 17:20:43 +02006070 bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
Jouni Malinen636a5d32009-03-19 13:39:22 +02006071
Johannes Berg19957bb2009-07-02 17:20:43 +02006072 chan = ieee80211_get_channel(&rdev->wiphy,
6073 nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]));
Johannes Berg4c476992010-10-04 21:36:35 +02006074 if (!chan || (chan->flags & IEEE80211_CHAN_DISABLED))
6075 return -EINVAL;
Jouni Malinen636a5d32009-03-19 13:39:22 +02006076
Johannes Berg19957bb2009-07-02 17:20:43 +02006077 ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
6078 ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
Jouni Malinen636a5d32009-03-19 13:39:22 +02006079
6080 if (info->attrs[NL80211_ATTR_IE]) {
Johannes Bergf62fab72013-02-21 20:09:09 +01006081 req.ie = nla_data(info->attrs[NL80211_ATTR_IE]);
6082 req.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
Jouni Malinen636a5d32009-03-19 13:39:22 +02006083 }
6084
Jouni Malinendc6382ce2009-05-06 22:09:37 +03006085 if (info->attrs[NL80211_ATTR_USE_MFP]) {
Johannes Berg4f5dadc2009-07-07 03:56:10 +02006086 enum nl80211_mfp mfp =
Jouni Malinendc6382ce2009-05-06 22:09:37 +03006087 nla_get_u32(info->attrs[NL80211_ATTR_USE_MFP]);
Johannes Berg4f5dadc2009-07-07 03:56:10 +02006088 if (mfp == NL80211_MFP_REQUIRED)
Johannes Bergf62fab72013-02-21 20:09:09 +01006089 req.use_mfp = true;
Johannes Berg4c476992010-10-04 21:36:35 +02006090 else if (mfp != NL80211_MFP_NO)
6091 return -EINVAL;
Jouni Malinendc6382ce2009-05-06 22:09:37 +03006092 }
6093
Johannes Berg3e5d7642009-07-07 14:37:26 +02006094 if (info->attrs[NL80211_ATTR_PREV_BSSID])
Johannes Bergf62fab72013-02-21 20:09:09 +01006095 req.prev_bssid = nla_data(info->attrs[NL80211_ATTR_PREV_BSSID]);
Johannes Berg3e5d7642009-07-07 14:37:26 +02006096
Ben Greear7e7c8922011-11-18 11:31:59 -08006097 if (nla_get_flag(info->attrs[NL80211_ATTR_DISABLE_HT]))
Johannes Bergf62fab72013-02-21 20:09:09 +01006098 req.flags |= ASSOC_REQ_DISABLE_HT;
Ben Greear7e7c8922011-11-18 11:31:59 -08006099
6100 if (info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK])
Johannes Bergf62fab72013-02-21 20:09:09 +01006101 memcpy(&req.ht_capa_mask,
6102 nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK]),
6103 sizeof(req.ht_capa_mask));
Ben Greear7e7c8922011-11-18 11:31:59 -08006104
6105 if (info->attrs[NL80211_ATTR_HT_CAPABILITY]) {
Johannes Bergf62fab72013-02-21 20:09:09 +01006106 if (!info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK])
Ben Greear7e7c8922011-11-18 11:31:59 -08006107 return -EINVAL;
Johannes Bergf62fab72013-02-21 20:09:09 +01006108 memcpy(&req.ht_capa,
6109 nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]),
6110 sizeof(req.ht_capa));
Ben Greear7e7c8922011-11-18 11:31:59 -08006111 }
6112
Johannes Bergee2aca32013-02-21 17:36:01 +01006113 if (nla_get_flag(info->attrs[NL80211_ATTR_DISABLE_VHT]))
Johannes Bergf62fab72013-02-21 20:09:09 +01006114 req.flags |= ASSOC_REQ_DISABLE_VHT;
Johannes Bergee2aca32013-02-21 17:36:01 +01006115
6116 if (info->attrs[NL80211_ATTR_VHT_CAPABILITY_MASK])
Johannes Bergf62fab72013-02-21 20:09:09 +01006117 memcpy(&req.vht_capa_mask,
6118 nla_data(info->attrs[NL80211_ATTR_VHT_CAPABILITY_MASK]),
6119 sizeof(req.vht_capa_mask));
Johannes Bergee2aca32013-02-21 17:36:01 +01006120
6121 if (info->attrs[NL80211_ATTR_VHT_CAPABILITY]) {
Johannes Bergf62fab72013-02-21 20:09:09 +01006122 if (!info->attrs[NL80211_ATTR_VHT_CAPABILITY_MASK])
Johannes Bergee2aca32013-02-21 17:36:01 +01006123 return -EINVAL;
Johannes Bergf62fab72013-02-21 20:09:09 +01006124 memcpy(&req.vht_capa,
6125 nla_data(info->attrs[NL80211_ATTR_VHT_CAPABILITY]),
6126 sizeof(req.vht_capa));
Johannes Bergee2aca32013-02-21 17:36:01 +01006127 }
6128
Johannes Bergf62fab72013-02-21 20:09:09 +01006129 err = nl80211_crypto_settings(rdev, info, &req.crypto, 1);
Samuel Ortizb23aa672009-07-01 21:26:54 +02006130 if (!err)
Johannes Bergf62fab72013-02-21 20:09:09 +01006131 err = cfg80211_mlme_assoc(rdev, dev, chan, bssid,
6132 ssid, ssid_len, &req);
Jouni Malinen636a5d32009-03-19 13:39:22 +02006133
Jouni Malinen636a5d32009-03-19 13:39:22 +02006134 return err;
6135}
6136
6137static int nl80211_deauthenticate(struct sk_buff *skb, struct genl_info *info)
6138{
Johannes Berg4c476992010-10-04 21:36:35 +02006139 struct cfg80211_registered_device *rdev = info->user_ptr[0];
6140 struct net_device *dev = info->user_ptr[1];
Johannes Berg19957bb2009-07-02 17:20:43 +02006141 const u8 *ie = NULL, *bssid;
Johannes Berg4c476992010-10-04 21:36:35 +02006142 int ie_len = 0;
Johannes Berg19957bb2009-07-02 17:20:43 +02006143 u16 reason_code;
Jouni Malinend5cdfac2010-04-04 09:37:19 +03006144 bool local_state_change;
Jouni Malinen636a5d32009-03-19 13:39:22 +02006145
Johannes Bergf4a11bb2009-03-27 12:40:28 +01006146 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
6147 return -EINVAL;
6148
6149 if (!info->attrs[NL80211_ATTR_MAC])
6150 return -EINVAL;
6151
6152 if (!info->attrs[NL80211_ATTR_REASON_CODE])
6153 return -EINVAL;
6154
Johannes Berg4c476992010-10-04 21:36:35 +02006155 if (!rdev->ops->deauth)
6156 return -EOPNOTSUPP;
Jouni Malinen636a5d32009-03-19 13:39:22 +02006157
Johannes Berg074ac8d2010-09-16 14:58:22 +02006158 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg4c476992010-10-04 21:36:35 +02006159 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT)
6160 return -EOPNOTSUPP;
Jouni Malineneec60b02009-03-20 21:21:19 +02006161
Johannes Berg19957bb2009-07-02 17:20:43 +02006162 bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
Jouni Malinen636a5d32009-03-19 13:39:22 +02006163
Johannes Berg19957bb2009-07-02 17:20:43 +02006164 reason_code = nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]);
6165 if (reason_code == 0) {
Johannes Bergf4a11bb2009-03-27 12:40:28 +01006166 /* Reason Code 0 is reserved */
Johannes Berg4c476992010-10-04 21:36:35 +02006167 return -EINVAL;
Jouni Malinen255e7372009-03-20 21:21:17 +02006168 }
Jouni Malinen636a5d32009-03-19 13:39:22 +02006169
6170 if (info->attrs[NL80211_ATTR_IE]) {
Johannes Berg19957bb2009-07-02 17:20:43 +02006171 ie = nla_data(info->attrs[NL80211_ATTR_IE]);
6172 ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
Jouni Malinen636a5d32009-03-19 13:39:22 +02006173 }
6174
Jouni Malinend5cdfac2010-04-04 09:37:19 +03006175 local_state_change = !!info->attrs[NL80211_ATTR_LOCAL_STATE_CHANGE];
6176
Johannes Berg4c476992010-10-04 21:36:35 +02006177 return cfg80211_mlme_deauth(rdev, dev, bssid, ie, ie_len, reason_code,
6178 local_state_change);
Jouni Malinen636a5d32009-03-19 13:39:22 +02006179}
6180
6181static int nl80211_disassociate(struct sk_buff *skb, struct genl_info *info)
6182{
Johannes Berg4c476992010-10-04 21:36:35 +02006183 struct cfg80211_registered_device *rdev = info->user_ptr[0];
6184 struct net_device *dev = info->user_ptr[1];
Johannes Berg19957bb2009-07-02 17:20:43 +02006185 const u8 *ie = NULL, *bssid;
Johannes Berg4c476992010-10-04 21:36:35 +02006186 int ie_len = 0;
Johannes Berg19957bb2009-07-02 17:20:43 +02006187 u16 reason_code;
Jouni Malinend5cdfac2010-04-04 09:37:19 +03006188 bool local_state_change;
Jouni Malinen636a5d32009-03-19 13:39:22 +02006189
Johannes Bergf4a11bb2009-03-27 12:40:28 +01006190 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
6191 return -EINVAL;
6192
6193 if (!info->attrs[NL80211_ATTR_MAC])
6194 return -EINVAL;
6195
6196 if (!info->attrs[NL80211_ATTR_REASON_CODE])
6197 return -EINVAL;
6198
Johannes Berg4c476992010-10-04 21:36:35 +02006199 if (!rdev->ops->disassoc)
6200 return -EOPNOTSUPP;
Jouni Malinen636a5d32009-03-19 13:39:22 +02006201
Johannes Berg074ac8d2010-09-16 14:58:22 +02006202 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg4c476992010-10-04 21:36:35 +02006203 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT)
6204 return -EOPNOTSUPP;
Jouni Malineneec60b02009-03-20 21:21:19 +02006205
Johannes Berg19957bb2009-07-02 17:20:43 +02006206 bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
Jouni Malinen636a5d32009-03-19 13:39:22 +02006207
Johannes Berg19957bb2009-07-02 17:20:43 +02006208 reason_code = nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]);
6209 if (reason_code == 0) {
Johannes Bergf4a11bb2009-03-27 12:40:28 +01006210 /* Reason Code 0 is reserved */
Johannes Berg4c476992010-10-04 21:36:35 +02006211 return -EINVAL;
Jouni Malinen255e7372009-03-20 21:21:17 +02006212 }
Jouni Malinen636a5d32009-03-19 13:39:22 +02006213
6214 if (info->attrs[NL80211_ATTR_IE]) {
Johannes Berg19957bb2009-07-02 17:20:43 +02006215 ie = nla_data(info->attrs[NL80211_ATTR_IE]);
6216 ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
Jouni Malinen636a5d32009-03-19 13:39:22 +02006217 }
6218
Jouni Malinend5cdfac2010-04-04 09:37:19 +03006219 local_state_change = !!info->attrs[NL80211_ATTR_LOCAL_STATE_CHANGE];
6220
Johannes Berg4c476992010-10-04 21:36:35 +02006221 return cfg80211_mlme_disassoc(rdev, dev, bssid, ie, ie_len, reason_code,
6222 local_state_change);
Jouni Malinen636a5d32009-03-19 13:39:22 +02006223}
6224
Felix Fietkaudd5b4cc2010-11-22 20:58:24 +01006225static bool
6226nl80211_parse_mcast_rate(struct cfg80211_registered_device *rdev,
6227 int mcast_rate[IEEE80211_NUM_BANDS],
6228 int rateval)
6229{
6230 struct wiphy *wiphy = &rdev->wiphy;
6231 bool found = false;
6232 int band, i;
6233
6234 for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
6235 struct ieee80211_supported_band *sband;
6236
6237 sband = wiphy->bands[band];
6238 if (!sband)
6239 continue;
6240
6241 for (i = 0; i < sband->n_bitrates; i++) {
6242 if (sband->bitrates[i].bitrate == rateval) {
6243 mcast_rate[band] = i + 1;
6244 found = true;
6245 break;
6246 }
6247 }
6248 }
6249
6250 return found;
6251}
6252
Johannes Berg04a773a2009-04-19 21:24:32 +02006253static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info)
6254{
Johannes Berg4c476992010-10-04 21:36:35 +02006255 struct cfg80211_registered_device *rdev = info->user_ptr[0];
6256 struct net_device *dev = info->user_ptr[1];
Johannes Berg04a773a2009-04-19 21:24:32 +02006257 struct cfg80211_ibss_params ibss;
6258 struct wiphy *wiphy;
Johannes Bergfffd0932009-07-08 14:22:54 +02006259 struct cfg80211_cached_keys *connkeys = NULL;
Johannes Berg04a773a2009-04-19 21:24:32 +02006260 int err;
6261
Johannes Berg8e30bc52009-04-22 17:45:38 +02006262 memset(&ibss, 0, sizeof(ibss));
6263
Johannes Berg04a773a2009-04-19 21:24:32 +02006264 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
6265 return -EINVAL;
6266
Johannes Berg683b6d32012-11-08 21:25:48 +01006267 if (!info->attrs[NL80211_ATTR_SSID] ||
Johannes Berg04a773a2009-04-19 21:24:32 +02006268 !nla_len(info->attrs[NL80211_ATTR_SSID]))
6269 return -EINVAL;
6270
Johannes Berg8e30bc52009-04-22 17:45:38 +02006271 ibss.beacon_interval = 100;
6272
6273 if (info->attrs[NL80211_ATTR_BEACON_INTERVAL]) {
6274 ibss.beacon_interval =
6275 nla_get_u32(info->attrs[NL80211_ATTR_BEACON_INTERVAL]);
6276 if (ibss.beacon_interval < 1 || ibss.beacon_interval > 10000)
6277 return -EINVAL;
6278 }
6279
Johannes Berg4c476992010-10-04 21:36:35 +02006280 if (!rdev->ops->join_ibss)
6281 return -EOPNOTSUPP;
Johannes Berg04a773a2009-04-19 21:24:32 +02006282
Johannes Berg4c476992010-10-04 21:36:35 +02006283 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC)
6284 return -EOPNOTSUPP;
Johannes Berg04a773a2009-04-19 21:24:32 +02006285
Johannes Berg79c97e92009-07-07 03:56:12 +02006286 wiphy = &rdev->wiphy;
Johannes Berg04a773a2009-04-19 21:24:32 +02006287
Johannes Berg39193492011-09-16 13:45:25 +02006288 if (info->attrs[NL80211_ATTR_MAC]) {
Johannes Berg04a773a2009-04-19 21:24:32 +02006289 ibss.bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
Johannes Berg39193492011-09-16 13:45:25 +02006290
6291 if (!is_valid_ether_addr(ibss.bssid))
6292 return -EINVAL;
6293 }
Johannes Berg04a773a2009-04-19 21:24:32 +02006294 ibss.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
6295 ibss.ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
6296
6297 if (info->attrs[NL80211_ATTR_IE]) {
6298 ibss.ie = nla_data(info->attrs[NL80211_ATTR_IE]);
6299 ibss.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
6300 }
6301
Johannes Berg683b6d32012-11-08 21:25:48 +01006302 err = nl80211_parse_chandef(rdev, info, &ibss.chandef);
6303 if (err)
6304 return err;
Alexander Simon54858ee5b2011-11-30 16:56:32 +01006305
Johannes Berg683b6d32012-11-08 21:25:48 +01006306 if (!cfg80211_reg_can_beacon(&rdev->wiphy, &ibss.chandef))
Alexander Simon54858ee5b2011-11-30 16:56:32 +01006307 return -EINVAL;
6308
Johannes Bergdb9c64c2012-11-09 14:56:41 +01006309 if (ibss.chandef.width > NL80211_CHAN_WIDTH_40)
6310 return -EINVAL;
6311 if (ibss.chandef.width != NL80211_CHAN_WIDTH_20_NOHT &&
6312 !(rdev->wiphy.features & NL80211_FEATURE_HT_IBSS))
Simon Wunderlichc04d6152012-11-29 18:37:22 +01006313 return -EINVAL;
Johannes Bergdb9c64c2012-11-09 14:56:41 +01006314
Johannes Berg04a773a2009-04-19 21:24:32 +02006315 ibss.channel_fixed = !!info->attrs[NL80211_ATTR_FREQ_FIXED];
Johannes Bergfffd0932009-07-08 14:22:54 +02006316 ibss.privacy = !!info->attrs[NL80211_ATTR_PRIVACY];
Johannes Berg04a773a2009-04-19 21:24:32 +02006317
Teemu Paasikivifbd2c8d2010-06-14 12:55:31 +03006318 if (info->attrs[NL80211_ATTR_BSS_BASIC_RATES]) {
6319 u8 *rates =
6320 nla_data(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]);
6321 int n_rates =
6322 nla_len(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]);
6323 struct ieee80211_supported_band *sband =
Johannes Berg683b6d32012-11-08 21:25:48 +01006324 wiphy->bands[ibss.chandef.chan->band];
Teemu Paasikivifbd2c8d2010-06-14 12:55:31 +03006325
Johannes Berg34850ab2011-07-18 18:08:35 +02006326 err = ieee80211_get_ratemask(sband, rates, n_rates,
6327 &ibss.basic_rates);
6328 if (err)
6329 return err;
Teemu Paasikivifbd2c8d2010-06-14 12:55:31 +03006330 }
Felix Fietkaudd5b4cc2010-11-22 20:58:24 +01006331
6332 if (info->attrs[NL80211_ATTR_MCAST_RATE] &&
6333 !nl80211_parse_mcast_rate(rdev, ibss.mcast_rate,
6334 nla_get_u32(info->attrs[NL80211_ATTR_MCAST_RATE])))
6335 return -EINVAL;
Teemu Paasikivifbd2c8d2010-06-14 12:55:31 +03006336
Johannes Berg4c476992010-10-04 21:36:35 +02006337 if (ibss.privacy && info->attrs[NL80211_ATTR_KEYS]) {
Sujith Manoharande7044e2012-10-18 10:19:28 +05306338 bool no_ht = false;
6339
Johannes Berg4c476992010-10-04 21:36:35 +02006340 connkeys = nl80211_parse_connkeys(rdev,
Sujith Manoharande7044e2012-10-18 10:19:28 +05306341 info->attrs[NL80211_ATTR_KEYS],
6342 &no_ht);
Johannes Berg4c476992010-10-04 21:36:35 +02006343 if (IS_ERR(connkeys))
6344 return PTR_ERR(connkeys);
Sujith Manoharande7044e2012-10-18 10:19:28 +05306345
Johannes Berg3d9d1d62012-11-08 23:14:50 +01006346 if ((ibss.chandef.width != NL80211_CHAN_WIDTH_20_NOHT) &&
6347 no_ht) {
Sujith Manoharande7044e2012-10-18 10:19:28 +05306348 kfree(connkeys);
6349 return -EINVAL;
6350 }
Johannes Berg4c476992010-10-04 21:36:35 +02006351 }
Johannes Berg04a773a2009-04-19 21:24:32 +02006352
Antonio Quartulli267335d2012-01-31 20:25:47 +01006353 ibss.control_port =
6354 nla_get_flag(info->attrs[NL80211_ATTR_CONTROL_PORT]);
6355
Johannes Berg4c476992010-10-04 21:36:35 +02006356 err = cfg80211_join_ibss(rdev, dev, &ibss, connkeys);
Johannes Bergfffd0932009-07-08 14:22:54 +02006357 if (err)
6358 kfree(connkeys);
Johannes Berg04a773a2009-04-19 21:24:32 +02006359 return err;
6360}
6361
6362static int nl80211_leave_ibss(struct sk_buff *skb, struct genl_info *info)
6363{
Johannes Berg4c476992010-10-04 21:36:35 +02006364 struct cfg80211_registered_device *rdev = info->user_ptr[0];
6365 struct net_device *dev = info->user_ptr[1];
Johannes Berg04a773a2009-04-19 21:24:32 +02006366
Johannes Berg4c476992010-10-04 21:36:35 +02006367 if (!rdev->ops->leave_ibss)
6368 return -EOPNOTSUPP;
Johannes Berg04a773a2009-04-19 21:24:32 +02006369
Johannes Berg4c476992010-10-04 21:36:35 +02006370 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC)
6371 return -EOPNOTSUPP;
Johannes Berg04a773a2009-04-19 21:24:32 +02006372
Johannes Berg4c476992010-10-04 21:36:35 +02006373 return cfg80211_leave_ibss(rdev, dev, false);
Johannes Berg04a773a2009-04-19 21:24:32 +02006374}
6375
Antonio Quartullif4e583c2012-11-02 13:27:48 +01006376static int nl80211_set_mcast_rate(struct sk_buff *skb, struct genl_info *info)
6377{
6378 struct cfg80211_registered_device *rdev = info->user_ptr[0];
6379 struct net_device *dev = info->user_ptr[1];
6380 int mcast_rate[IEEE80211_NUM_BANDS];
6381 u32 nla_rate;
6382 int err;
6383
6384 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC &&
6385 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT)
6386 return -EOPNOTSUPP;
6387
6388 if (!rdev->ops->set_mcast_rate)
6389 return -EOPNOTSUPP;
6390
6391 memset(mcast_rate, 0, sizeof(mcast_rate));
6392
6393 if (!info->attrs[NL80211_ATTR_MCAST_RATE])
6394 return -EINVAL;
6395
6396 nla_rate = nla_get_u32(info->attrs[NL80211_ATTR_MCAST_RATE]);
6397 if (!nl80211_parse_mcast_rate(rdev, mcast_rate, nla_rate))
6398 return -EINVAL;
6399
6400 err = rdev->ops->set_mcast_rate(&rdev->wiphy, dev, mcast_rate);
6401
6402 return err;
6403}
6404
6405
Johannes Bergaff89a92009-07-01 21:26:51 +02006406#ifdef CONFIG_NL80211_TESTMODE
6407static struct genl_multicast_group nl80211_testmode_mcgrp = {
6408 .name = "testmode",
6409};
6410
6411static int nl80211_testmode_do(struct sk_buff *skb, struct genl_info *info)
6412{
Johannes Berg4c476992010-10-04 21:36:35 +02006413 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Johannes Bergaff89a92009-07-01 21:26:51 +02006414 int err;
6415
6416 if (!info->attrs[NL80211_ATTR_TESTDATA])
6417 return -EINVAL;
6418
Johannes Bergaff89a92009-07-01 21:26:51 +02006419 err = -EOPNOTSUPP;
6420 if (rdev->ops->testmode_cmd) {
6421 rdev->testmode_info = info;
Hila Gonene35e4d22012-06-27 17:19:42 +03006422 err = rdev_testmode_cmd(rdev,
Johannes Bergaff89a92009-07-01 21:26:51 +02006423 nla_data(info->attrs[NL80211_ATTR_TESTDATA]),
6424 nla_len(info->attrs[NL80211_ATTR_TESTDATA]));
6425 rdev->testmode_info = NULL;
6426 }
6427
Johannes Bergaff89a92009-07-01 21:26:51 +02006428 return err;
6429}
6430
Wey-Yi Guy71063f02011-05-20 09:05:54 -07006431static int nl80211_testmode_dump(struct sk_buff *skb,
6432 struct netlink_callback *cb)
6433{
Johannes Berg00918d32011-12-13 17:22:05 +01006434 struct cfg80211_registered_device *rdev;
Wey-Yi Guy71063f02011-05-20 09:05:54 -07006435 int err;
6436 long phy_idx;
6437 void *data = NULL;
6438 int data_len = 0;
6439
6440 if (cb->args[0]) {
6441 /*
6442 * 0 is a valid index, but not valid for args[0],
6443 * so we need to offset by 1.
6444 */
6445 phy_idx = cb->args[0] - 1;
6446 } else {
6447 err = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize,
6448 nl80211_fam.attrbuf, nl80211_fam.maxattr,
6449 nl80211_policy);
6450 if (err)
6451 return err;
Johannes Berg00918d32011-12-13 17:22:05 +01006452
Johannes Berg2bd7e352012-06-15 14:23:16 +02006453 mutex_lock(&cfg80211_mutex);
6454 rdev = __cfg80211_rdev_from_attrs(sock_net(skb->sk),
6455 nl80211_fam.attrbuf);
6456 if (IS_ERR(rdev)) {
6457 mutex_unlock(&cfg80211_mutex);
6458 return PTR_ERR(rdev);
Johannes Berg00918d32011-12-13 17:22:05 +01006459 }
Johannes Berg2bd7e352012-06-15 14:23:16 +02006460 phy_idx = rdev->wiphy_idx;
6461 rdev = NULL;
6462 mutex_unlock(&cfg80211_mutex);
6463
Wey-Yi Guy71063f02011-05-20 09:05:54 -07006464 if (nl80211_fam.attrbuf[NL80211_ATTR_TESTDATA])
6465 cb->args[1] =
6466 (long)nl80211_fam.attrbuf[NL80211_ATTR_TESTDATA];
6467 }
6468
6469 if (cb->args[1]) {
6470 data = nla_data((void *)cb->args[1]);
6471 data_len = nla_len((void *)cb->args[1]);
6472 }
6473
6474 mutex_lock(&cfg80211_mutex);
Johannes Berg00918d32011-12-13 17:22:05 +01006475 rdev = cfg80211_rdev_by_wiphy_idx(phy_idx);
6476 if (!rdev) {
Wey-Yi Guy71063f02011-05-20 09:05:54 -07006477 mutex_unlock(&cfg80211_mutex);
6478 return -ENOENT;
6479 }
Johannes Berg00918d32011-12-13 17:22:05 +01006480 cfg80211_lock_rdev(rdev);
Wey-Yi Guy71063f02011-05-20 09:05:54 -07006481 mutex_unlock(&cfg80211_mutex);
6482
Johannes Berg00918d32011-12-13 17:22:05 +01006483 if (!rdev->ops->testmode_dump) {
Wey-Yi Guy71063f02011-05-20 09:05:54 -07006484 err = -EOPNOTSUPP;
6485 goto out_err;
6486 }
6487
6488 while (1) {
Eric W. Biederman15e47302012-09-07 20:12:54 +00006489 void *hdr = nl80211hdr_put(skb, NETLINK_CB(cb->skb).portid,
Wey-Yi Guy71063f02011-05-20 09:05:54 -07006490 cb->nlh->nlmsg_seq, NLM_F_MULTI,
6491 NL80211_CMD_TESTMODE);
6492 struct nlattr *tmdata;
6493
David S. Miller9360ffd2012-03-29 04:41:26 -04006494 if (nla_put_u32(skb, NL80211_ATTR_WIPHY, phy_idx)) {
Wey-Yi Guy71063f02011-05-20 09:05:54 -07006495 genlmsg_cancel(skb, hdr);
6496 break;
6497 }
6498
6499 tmdata = nla_nest_start(skb, NL80211_ATTR_TESTDATA);
6500 if (!tmdata) {
6501 genlmsg_cancel(skb, hdr);
6502 break;
6503 }
Hila Gonene35e4d22012-06-27 17:19:42 +03006504 err = rdev_testmode_dump(rdev, skb, cb, data, data_len);
Wey-Yi Guy71063f02011-05-20 09:05:54 -07006505 nla_nest_end(skb, tmdata);
6506
6507 if (err == -ENOBUFS || err == -ENOENT) {
6508 genlmsg_cancel(skb, hdr);
6509 break;
6510 } else if (err) {
6511 genlmsg_cancel(skb, hdr);
6512 goto out_err;
6513 }
6514
6515 genlmsg_end(skb, hdr);
6516 }
6517
6518 err = skb->len;
6519 /* see above */
6520 cb->args[0] = phy_idx + 1;
6521 out_err:
Johannes Berg00918d32011-12-13 17:22:05 +01006522 cfg80211_unlock_rdev(rdev);
Wey-Yi Guy71063f02011-05-20 09:05:54 -07006523 return err;
6524}
6525
Johannes Bergaff89a92009-07-01 21:26:51 +02006526static struct sk_buff *
6527__cfg80211_testmode_alloc_skb(struct cfg80211_registered_device *rdev,
Eric W. Biederman15e47302012-09-07 20:12:54 +00006528 int approxlen, u32 portid, u32 seq, gfp_t gfp)
Johannes Bergaff89a92009-07-01 21:26:51 +02006529{
6530 struct sk_buff *skb;
6531 void *hdr;
6532 struct nlattr *data;
6533
6534 skb = nlmsg_new(approxlen + 100, gfp);
6535 if (!skb)
6536 return NULL;
6537
Eric W. Biederman15e47302012-09-07 20:12:54 +00006538 hdr = nl80211hdr_put(skb, portid, seq, 0, NL80211_CMD_TESTMODE);
Johannes Bergaff89a92009-07-01 21:26:51 +02006539 if (!hdr) {
6540 kfree_skb(skb);
6541 return NULL;
6542 }
6543
David S. Miller9360ffd2012-03-29 04:41:26 -04006544 if (nla_put_u32(skb, NL80211_ATTR_WIPHY, rdev->wiphy_idx))
6545 goto nla_put_failure;
Johannes Bergaff89a92009-07-01 21:26:51 +02006546 data = nla_nest_start(skb, NL80211_ATTR_TESTDATA);
6547
6548 ((void **)skb->cb)[0] = rdev;
6549 ((void **)skb->cb)[1] = hdr;
6550 ((void **)skb->cb)[2] = data;
6551
6552 return skb;
6553
6554 nla_put_failure:
6555 kfree_skb(skb);
6556 return NULL;
6557}
6558
6559struct sk_buff *cfg80211_testmode_alloc_reply_skb(struct wiphy *wiphy,
6560 int approxlen)
6561{
6562 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
6563
6564 if (WARN_ON(!rdev->testmode_info))
6565 return NULL;
6566
6567 return __cfg80211_testmode_alloc_skb(rdev, approxlen,
Eric W. Biederman15e47302012-09-07 20:12:54 +00006568 rdev->testmode_info->snd_portid,
Johannes Bergaff89a92009-07-01 21:26:51 +02006569 rdev->testmode_info->snd_seq,
6570 GFP_KERNEL);
6571}
6572EXPORT_SYMBOL(cfg80211_testmode_alloc_reply_skb);
6573
6574int cfg80211_testmode_reply(struct sk_buff *skb)
6575{
6576 struct cfg80211_registered_device *rdev = ((void **)skb->cb)[0];
6577 void *hdr = ((void **)skb->cb)[1];
6578 struct nlattr *data = ((void **)skb->cb)[2];
6579
Johannes Berg7dd31112014-07-30 14:55:26 +02006580 /* clear CB data for netlink core to own from now on */
6581 memset(skb->cb, 0, sizeof(skb->cb));
6582
Johannes Bergaff89a92009-07-01 21:26:51 +02006583 if (WARN_ON(!rdev->testmode_info)) {
6584 kfree_skb(skb);
6585 return -EINVAL;
6586 }
6587
6588 nla_nest_end(skb, data);
6589 genlmsg_end(skb, hdr);
6590 return genlmsg_reply(skb, rdev->testmode_info);
6591}
6592EXPORT_SYMBOL(cfg80211_testmode_reply);
6593
6594struct sk_buff *cfg80211_testmode_alloc_event_skb(struct wiphy *wiphy,
6595 int approxlen, gfp_t gfp)
6596{
6597 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
6598
6599 return __cfg80211_testmode_alloc_skb(rdev, approxlen, 0, 0, gfp);
6600}
6601EXPORT_SYMBOL(cfg80211_testmode_alloc_event_skb);
6602
6603void cfg80211_testmode_event(struct sk_buff *skb, gfp_t gfp)
6604{
Michal Kaziorc8942c62013-06-25 09:17:17 +02006605 struct cfg80211_registered_device *rdev = ((void **)skb->cb)[0];
Johannes Bergaff89a92009-07-01 21:26:51 +02006606 void *hdr = ((void **)skb->cb)[1];
6607 struct nlattr *data = ((void **)skb->cb)[2];
6608
Johannes Berg7dd31112014-07-30 14:55:26 +02006609 /* clear CB data for netlink core to own from now on */
6610 memset(skb->cb, 0, sizeof(skb->cb));
6611
Johannes Bergaff89a92009-07-01 21:26:51 +02006612 nla_nest_end(skb, data);
6613 genlmsg_end(skb, hdr);
Michal Kaziorc8942c62013-06-25 09:17:17 +02006614 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), skb, 0,
6615 nl80211_testmode_mcgrp.id, gfp);
Johannes Bergaff89a92009-07-01 21:26:51 +02006616}
6617EXPORT_SYMBOL(cfg80211_testmode_event);
6618#endif
6619
Samuel Ortizb23aa672009-07-01 21:26:54 +02006620static int nl80211_connect(struct sk_buff *skb, struct genl_info *info)
6621{
Johannes Berg4c476992010-10-04 21:36:35 +02006622 struct cfg80211_registered_device *rdev = info->user_ptr[0];
6623 struct net_device *dev = info->user_ptr[1];
Samuel Ortizb23aa672009-07-01 21:26:54 +02006624 struct cfg80211_connect_params connect;
6625 struct wiphy *wiphy;
Johannes Bergfffd0932009-07-08 14:22:54 +02006626 struct cfg80211_cached_keys *connkeys = NULL;
Samuel Ortizb23aa672009-07-01 21:26:54 +02006627 int err;
6628
6629 memset(&connect, 0, sizeof(connect));
6630
6631 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
6632 return -EINVAL;
6633
6634 if (!info->attrs[NL80211_ATTR_SSID] ||
6635 !nla_len(info->attrs[NL80211_ATTR_SSID]))
6636 return -EINVAL;
6637
6638 if (info->attrs[NL80211_ATTR_AUTH_TYPE]) {
6639 connect.auth_type =
6640 nla_get_u32(info->attrs[NL80211_ATTR_AUTH_TYPE]);
Jouni Malinene39e5b52012-09-30 19:29:39 +03006641 if (!nl80211_valid_auth_type(rdev, connect.auth_type,
6642 NL80211_CMD_CONNECT))
Samuel Ortizb23aa672009-07-01 21:26:54 +02006643 return -EINVAL;
6644 } else
6645 connect.auth_type = NL80211_AUTHTYPE_AUTOMATIC;
6646
6647 connect.privacy = info->attrs[NL80211_ATTR_PRIVACY];
6648
Johannes Bergc0692b82010-08-27 14:26:53 +03006649 err = nl80211_crypto_settings(rdev, info, &connect.crypto,
Johannes Berg3dc27d22009-07-02 21:36:37 +02006650 NL80211_MAX_NR_CIPHER_SUITES);
Samuel Ortizb23aa672009-07-01 21:26:54 +02006651 if (err)
6652 return err;
Samuel Ortizb23aa672009-07-01 21:26:54 +02006653
Johannes Berg074ac8d2010-09-16 14:58:22 +02006654 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg4c476992010-10-04 21:36:35 +02006655 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT)
6656 return -EOPNOTSUPP;
Samuel Ortizb23aa672009-07-01 21:26:54 +02006657
Johannes Berg79c97e92009-07-07 03:56:12 +02006658 wiphy = &rdev->wiphy;
Samuel Ortizb23aa672009-07-01 21:26:54 +02006659
Bala Shanmugam4486ea92012-03-07 17:27:12 +05306660 connect.bg_scan_period = -1;
6661 if (info->attrs[NL80211_ATTR_BG_SCAN_PERIOD] &&
6662 (wiphy->flags & WIPHY_FLAG_SUPPORTS_FW_ROAM)) {
6663 connect.bg_scan_period =
6664 nla_get_u16(info->attrs[NL80211_ATTR_BG_SCAN_PERIOD]);
6665 }
6666
Samuel Ortizb23aa672009-07-01 21:26:54 +02006667 if (info->attrs[NL80211_ATTR_MAC])
6668 connect.bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
6669 connect.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
6670 connect.ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
6671
6672 if (info->attrs[NL80211_ATTR_IE]) {
6673 connect.ie = nla_data(info->attrs[NL80211_ATTR_IE]);
6674 connect.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
6675 }
6676
Jouni Malinencee00a92013-01-15 17:15:57 +02006677 if (info->attrs[NL80211_ATTR_USE_MFP]) {
6678 connect.mfp = nla_get_u32(info->attrs[NL80211_ATTR_USE_MFP]);
6679 if (connect.mfp != NL80211_MFP_REQUIRED &&
6680 connect.mfp != NL80211_MFP_NO)
6681 return -EINVAL;
6682 } else {
6683 connect.mfp = NL80211_MFP_NO;
6684 }
6685
Samuel Ortizb23aa672009-07-01 21:26:54 +02006686 if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
6687 connect.channel =
6688 ieee80211_get_channel(wiphy,
6689 nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]));
6690 if (!connect.channel ||
Johannes Berg4c476992010-10-04 21:36:35 +02006691 connect.channel->flags & IEEE80211_CHAN_DISABLED)
6692 return -EINVAL;
Samuel Ortizb23aa672009-07-01 21:26:54 +02006693 }
6694
Johannes Bergfffd0932009-07-08 14:22:54 +02006695 if (connect.privacy && info->attrs[NL80211_ATTR_KEYS]) {
6696 connkeys = nl80211_parse_connkeys(rdev,
Sujith Manoharande7044e2012-10-18 10:19:28 +05306697 info->attrs[NL80211_ATTR_KEYS], NULL);
Johannes Berg4c476992010-10-04 21:36:35 +02006698 if (IS_ERR(connkeys))
6699 return PTR_ERR(connkeys);
Johannes Bergfffd0932009-07-08 14:22:54 +02006700 }
6701
Ben Greear7e7c8922011-11-18 11:31:59 -08006702 if (nla_get_flag(info->attrs[NL80211_ATTR_DISABLE_HT]))
6703 connect.flags |= ASSOC_REQ_DISABLE_HT;
6704
6705 if (info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK])
6706 memcpy(&connect.ht_capa_mask,
6707 nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK]),
6708 sizeof(connect.ht_capa_mask));
6709
6710 if (info->attrs[NL80211_ATTR_HT_CAPABILITY]) {
Wei Yongjunb4e4f472012-09-02 21:41:04 +08006711 if (!info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK]) {
6712 kfree(connkeys);
Ben Greear7e7c8922011-11-18 11:31:59 -08006713 return -EINVAL;
Wei Yongjunb4e4f472012-09-02 21:41:04 +08006714 }
Ben Greear7e7c8922011-11-18 11:31:59 -08006715 memcpy(&connect.ht_capa,
6716 nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]),
6717 sizeof(connect.ht_capa));
6718 }
6719
Johannes Bergee2aca32013-02-21 17:36:01 +01006720 if (nla_get_flag(info->attrs[NL80211_ATTR_DISABLE_VHT]))
6721 connect.flags |= ASSOC_REQ_DISABLE_VHT;
6722
6723 if (info->attrs[NL80211_ATTR_VHT_CAPABILITY_MASK])
6724 memcpy(&connect.vht_capa_mask,
6725 nla_data(info->attrs[NL80211_ATTR_VHT_CAPABILITY_MASK]),
6726 sizeof(connect.vht_capa_mask));
6727
6728 if (info->attrs[NL80211_ATTR_VHT_CAPABILITY]) {
6729 if (!info->attrs[NL80211_ATTR_VHT_CAPABILITY_MASK]) {
6730 kfree(connkeys);
6731 return -EINVAL;
6732 }
6733 memcpy(&connect.vht_capa,
6734 nla_data(info->attrs[NL80211_ATTR_VHT_CAPABILITY]),
6735 sizeof(connect.vht_capa));
6736 }
6737
Johannes Bergfffd0932009-07-08 14:22:54 +02006738 err = cfg80211_connect(rdev, dev, &connect, connkeys);
Johannes Bergfffd0932009-07-08 14:22:54 +02006739 if (err)
6740 kfree(connkeys);
Samuel Ortizb23aa672009-07-01 21:26:54 +02006741 return err;
6742}
6743
6744static int nl80211_disconnect(struct sk_buff *skb, struct genl_info *info)
6745{
Johannes Berg4c476992010-10-04 21:36:35 +02006746 struct cfg80211_registered_device *rdev = info->user_ptr[0];
6747 struct net_device *dev = info->user_ptr[1];
Samuel Ortizb23aa672009-07-01 21:26:54 +02006748 u16 reason;
6749
6750 if (!info->attrs[NL80211_ATTR_REASON_CODE])
6751 reason = WLAN_REASON_DEAUTH_LEAVING;
6752 else
6753 reason = nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]);
6754
6755 if (reason == 0)
6756 return -EINVAL;
6757
Johannes Berg074ac8d2010-09-16 14:58:22 +02006758 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg4c476992010-10-04 21:36:35 +02006759 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT)
6760 return -EOPNOTSUPP;
Samuel Ortizb23aa672009-07-01 21:26:54 +02006761
Johannes Berg4c476992010-10-04 21:36:35 +02006762 return cfg80211_disconnect(rdev, dev, reason, true);
Samuel Ortizb23aa672009-07-01 21:26:54 +02006763}
6764
Johannes Berg463d0182009-07-14 00:33:35 +02006765static int nl80211_wiphy_netns(struct sk_buff *skb, struct genl_info *info)
6766{
Johannes Berg4c476992010-10-04 21:36:35 +02006767 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Johannes Berg463d0182009-07-14 00:33:35 +02006768 struct net *net;
6769 int err;
6770 u32 pid;
6771
6772 if (!info->attrs[NL80211_ATTR_PID])
6773 return -EINVAL;
6774
6775 pid = nla_get_u32(info->attrs[NL80211_ATTR_PID]);
6776
Johannes Berg463d0182009-07-14 00:33:35 +02006777 net = get_net_ns_by_pid(pid);
Johannes Berg4c476992010-10-04 21:36:35 +02006778 if (IS_ERR(net))
6779 return PTR_ERR(net);
Johannes Berg463d0182009-07-14 00:33:35 +02006780
6781 err = 0;
6782
6783 /* check if anything to do */
Johannes Berg4c476992010-10-04 21:36:35 +02006784 if (!net_eq(wiphy_net(&rdev->wiphy), net))
6785 err = cfg80211_switch_netns(rdev, net);
Johannes Berg463d0182009-07-14 00:33:35 +02006786
Johannes Berg463d0182009-07-14 00:33:35 +02006787 put_net(net);
Johannes Berg463d0182009-07-14 00:33:35 +02006788 return err;
6789}
6790
Samuel Ortiz67fbb162009-11-24 23:59:15 +01006791static int nl80211_setdel_pmksa(struct sk_buff *skb, struct genl_info *info)
6792{
Johannes Berg4c476992010-10-04 21:36:35 +02006793 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Samuel Ortiz67fbb162009-11-24 23:59:15 +01006794 int (*rdev_ops)(struct wiphy *wiphy, struct net_device *dev,
6795 struct cfg80211_pmksa *pmksa) = NULL;
Johannes Berg4c476992010-10-04 21:36:35 +02006796 struct net_device *dev = info->user_ptr[1];
Samuel Ortiz67fbb162009-11-24 23:59:15 +01006797 struct cfg80211_pmksa pmksa;
6798
6799 memset(&pmksa, 0, sizeof(struct cfg80211_pmksa));
6800
6801 if (!info->attrs[NL80211_ATTR_MAC])
6802 return -EINVAL;
6803
6804 if (!info->attrs[NL80211_ATTR_PMKID])
6805 return -EINVAL;
6806
Samuel Ortiz67fbb162009-11-24 23:59:15 +01006807 pmksa.pmkid = nla_data(info->attrs[NL80211_ATTR_PMKID]);
6808 pmksa.bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
6809
Johannes Berg074ac8d2010-09-16 14:58:22 +02006810 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg4c476992010-10-04 21:36:35 +02006811 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT)
6812 return -EOPNOTSUPP;
Samuel Ortiz67fbb162009-11-24 23:59:15 +01006813
6814 switch (info->genlhdr->cmd) {
6815 case NL80211_CMD_SET_PMKSA:
6816 rdev_ops = rdev->ops->set_pmksa;
6817 break;
6818 case NL80211_CMD_DEL_PMKSA:
6819 rdev_ops = rdev->ops->del_pmksa;
6820 break;
6821 default:
6822 WARN_ON(1);
6823 break;
6824 }
6825
Johannes Berg4c476992010-10-04 21:36:35 +02006826 if (!rdev_ops)
6827 return -EOPNOTSUPP;
Samuel Ortiz67fbb162009-11-24 23:59:15 +01006828
Johannes Berg4c476992010-10-04 21:36:35 +02006829 return rdev_ops(&rdev->wiphy, dev, &pmksa);
Samuel Ortiz67fbb162009-11-24 23:59:15 +01006830}
6831
6832static int nl80211_flush_pmksa(struct sk_buff *skb, struct genl_info *info)
6833{
Johannes Berg4c476992010-10-04 21:36:35 +02006834 struct cfg80211_registered_device *rdev = info->user_ptr[0];
6835 struct net_device *dev = info->user_ptr[1];
Samuel Ortiz67fbb162009-11-24 23:59:15 +01006836
Johannes Berg074ac8d2010-09-16 14:58:22 +02006837 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg4c476992010-10-04 21:36:35 +02006838 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT)
6839 return -EOPNOTSUPP;
Samuel Ortiz67fbb162009-11-24 23:59:15 +01006840
Johannes Berg4c476992010-10-04 21:36:35 +02006841 if (!rdev->ops->flush_pmksa)
6842 return -EOPNOTSUPP;
Samuel Ortiz67fbb162009-11-24 23:59:15 +01006843
Hila Gonene35e4d22012-06-27 17:19:42 +03006844 return rdev_flush_pmksa(rdev, dev);
Samuel Ortiz67fbb162009-11-24 23:59:15 +01006845}
6846
Arik Nemtsov109086c2011-09-28 14:12:50 +03006847static int nl80211_tdls_mgmt(struct sk_buff *skb, struct genl_info *info)
6848{
6849 struct cfg80211_registered_device *rdev = info->user_ptr[0];
6850 struct net_device *dev = info->user_ptr[1];
6851 u8 action_code, dialog_token;
6852 u16 status_code;
6853 u8 *peer;
6854
6855 if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) ||
6856 !rdev->ops->tdls_mgmt)
6857 return -EOPNOTSUPP;
6858
6859 if (!info->attrs[NL80211_ATTR_TDLS_ACTION] ||
6860 !info->attrs[NL80211_ATTR_STATUS_CODE] ||
6861 !info->attrs[NL80211_ATTR_TDLS_DIALOG_TOKEN] ||
6862 !info->attrs[NL80211_ATTR_IE] ||
6863 !info->attrs[NL80211_ATTR_MAC])
6864 return -EINVAL;
6865
6866 peer = nla_data(info->attrs[NL80211_ATTR_MAC]);
6867 action_code = nla_get_u8(info->attrs[NL80211_ATTR_TDLS_ACTION]);
6868 status_code = nla_get_u16(info->attrs[NL80211_ATTR_STATUS_CODE]);
6869 dialog_token = nla_get_u8(info->attrs[NL80211_ATTR_TDLS_DIALOG_TOKEN]);
6870
Hila Gonene35e4d22012-06-27 17:19:42 +03006871 return rdev_tdls_mgmt(rdev, dev, peer, action_code,
6872 dialog_token, status_code,
6873 nla_data(info->attrs[NL80211_ATTR_IE]),
6874 nla_len(info->attrs[NL80211_ATTR_IE]));
Arik Nemtsov109086c2011-09-28 14:12:50 +03006875}
6876
6877static int nl80211_tdls_oper(struct sk_buff *skb, struct genl_info *info)
6878{
6879 struct cfg80211_registered_device *rdev = info->user_ptr[0];
6880 struct net_device *dev = info->user_ptr[1];
6881 enum nl80211_tdls_operation operation;
6882 u8 *peer;
6883
6884 if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) ||
6885 !rdev->ops->tdls_oper)
6886 return -EOPNOTSUPP;
6887
6888 if (!info->attrs[NL80211_ATTR_TDLS_OPERATION] ||
6889 !info->attrs[NL80211_ATTR_MAC])
6890 return -EINVAL;
6891
6892 operation = nla_get_u8(info->attrs[NL80211_ATTR_TDLS_OPERATION]);
6893 peer = nla_data(info->attrs[NL80211_ATTR_MAC]);
6894
Hila Gonene35e4d22012-06-27 17:19:42 +03006895 return rdev_tdls_oper(rdev, dev, peer, operation);
Arik Nemtsov109086c2011-09-28 14:12:50 +03006896}
6897
Jouni Malinen9588bbd2009-12-23 13:15:41 +01006898static int nl80211_remain_on_channel(struct sk_buff *skb,
6899 struct genl_info *info)
6900{
Johannes Berg4c476992010-10-04 21:36:35 +02006901 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Johannes Berg71bbc992012-06-15 15:30:18 +02006902 struct wireless_dev *wdev = info->user_ptr[1];
Johannes Berg683b6d32012-11-08 21:25:48 +01006903 struct cfg80211_chan_def chandef;
Jouni Malinen9588bbd2009-12-23 13:15:41 +01006904 struct sk_buff *msg;
6905 void *hdr;
6906 u64 cookie;
Johannes Berg683b6d32012-11-08 21:25:48 +01006907 u32 duration;
Jouni Malinen9588bbd2009-12-23 13:15:41 +01006908 int err;
6909
6910 if (!info->attrs[NL80211_ATTR_WIPHY_FREQ] ||
6911 !info->attrs[NL80211_ATTR_DURATION])
6912 return -EINVAL;
6913
6914 duration = nla_get_u32(info->attrs[NL80211_ATTR_DURATION]);
6915
Johannes Berg7c4ef712011-11-18 15:33:48 +01006916 if (!rdev->ops->remain_on_channel ||
6917 !(rdev->wiphy.flags & WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL))
Johannes Berg4c476992010-10-04 21:36:35 +02006918 return -EOPNOTSUPP;
Jouni Malinen9588bbd2009-12-23 13:15:41 +01006919
Johannes Bergebf348f2012-06-01 12:50:54 +02006920 /*
6921 * We should be on that channel for at least a minimum amount of
6922 * time (10ms) but no longer than the driver supports.
6923 */
6924 if (duration < NL80211_MIN_REMAIN_ON_CHANNEL_TIME ||
6925 duration > rdev->wiphy.max_remain_on_channel_duration)
6926 return -EINVAL;
6927
Johannes Berg683b6d32012-11-08 21:25:48 +01006928 err = nl80211_parse_chandef(rdev, info, &chandef);
6929 if (err)
6930 return err;
Jouni Malinen9588bbd2009-12-23 13:15:41 +01006931
6932 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg4c476992010-10-04 21:36:35 +02006933 if (!msg)
6934 return -ENOMEM;
Jouni Malinen9588bbd2009-12-23 13:15:41 +01006935
Eric W. Biederman15e47302012-09-07 20:12:54 +00006936 hdr = nl80211hdr_put(msg, info->snd_portid, info->snd_seq, 0,
Jouni Malinen9588bbd2009-12-23 13:15:41 +01006937 NL80211_CMD_REMAIN_ON_CHANNEL);
6938
6939 if (IS_ERR(hdr)) {
6940 err = PTR_ERR(hdr);
6941 goto free_msg;
6942 }
6943
Johannes Berg683b6d32012-11-08 21:25:48 +01006944 err = rdev_remain_on_channel(rdev, wdev, chandef.chan,
6945 duration, &cookie);
Jouni Malinen9588bbd2009-12-23 13:15:41 +01006946
6947 if (err)
6948 goto free_msg;
6949
David S. Miller9360ffd2012-03-29 04:41:26 -04006950 if (nla_put_u64(msg, NL80211_ATTR_COOKIE, cookie))
6951 goto nla_put_failure;
Jouni Malinen9588bbd2009-12-23 13:15:41 +01006952
6953 genlmsg_end(msg, hdr);
Johannes Berg4c476992010-10-04 21:36:35 +02006954
6955 return genlmsg_reply(msg, info);
Jouni Malinen9588bbd2009-12-23 13:15:41 +01006956
6957 nla_put_failure:
6958 err = -ENOBUFS;
6959 free_msg:
6960 nlmsg_free(msg);
Jouni Malinen9588bbd2009-12-23 13:15:41 +01006961 return err;
6962}
6963
6964static int nl80211_cancel_remain_on_channel(struct sk_buff *skb,
6965 struct genl_info *info)
6966{
Johannes Berg4c476992010-10-04 21:36:35 +02006967 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Johannes Berg71bbc992012-06-15 15:30:18 +02006968 struct wireless_dev *wdev = info->user_ptr[1];
Jouni Malinen9588bbd2009-12-23 13:15:41 +01006969 u64 cookie;
Jouni Malinen9588bbd2009-12-23 13:15:41 +01006970
6971 if (!info->attrs[NL80211_ATTR_COOKIE])
6972 return -EINVAL;
6973
Johannes Berg4c476992010-10-04 21:36:35 +02006974 if (!rdev->ops->cancel_remain_on_channel)
6975 return -EOPNOTSUPP;
Jouni Malinen9588bbd2009-12-23 13:15:41 +01006976
Jouni Malinen9588bbd2009-12-23 13:15:41 +01006977 cookie = nla_get_u64(info->attrs[NL80211_ATTR_COOKIE]);
6978
Hila Gonene35e4d22012-06-27 17:19:42 +03006979 return rdev_cancel_remain_on_channel(rdev, wdev, cookie);
Jouni Malinen9588bbd2009-12-23 13:15:41 +01006980}
6981
Jouni Malinen13ae75b2009-12-29 12:59:45 +02006982static u32 rateset_to_mask(struct ieee80211_supported_band *sband,
6983 u8 *rates, u8 rates_len)
6984{
6985 u8 i;
6986 u32 mask = 0;
6987
6988 for (i = 0; i < rates_len; i++) {
6989 int rate = (rates[i] & 0x7f) * 5;
6990 int ridx;
6991 for (ridx = 0; ridx < sband->n_bitrates; ridx++) {
6992 struct ieee80211_rate *srate =
6993 &sband->bitrates[ridx];
6994 if (rate == srate->bitrate) {
6995 mask |= 1 << ridx;
6996 break;
6997 }
6998 }
6999 if (ridx == sband->n_bitrates)
7000 return 0; /* rate not found */
7001 }
7002
7003 return mask;
7004}
7005
Simon Wunderlich24db78c2012-01-28 17:25:32 +01007006static bool ht_rateset_to_mask(struct ieee80211_supported_band *sband,
7007 u8 *rates, u8 rates_len,
7008 u8 mcs[IEEE80211_HT_MCS_MASK_LEN])
7009{
7010 u8 i;
7011
7012 memset(mcs, 0, IEEE80211_HT_MCS_MASK_LEN);
7013
7014 for (i = 0; i < rates_len; i++) {
7015 int ridx, rbit;
7016
7017 ridx = rates[i] / 8;
7018 rbit = BIT(rates[i] % 8);
7019
7020 /* check validity */
Dan Carpenter910570b52012-02-01 10:42:11 +03007021 if ((ridx < 0) || (ridx >= IEEE80211_HT_MCS_MASK_LEN))
Simon Wunderlich24db78c2012-01-28 17:25:32 +01007022 return false;
7023
7024 /* check availability */
7025 if (sband->ht_cap.mcs.rx_mask[ridx] & rbit)
7026 mcs[ridx] |= rbit;
7027 else
7028 return false;
7029 }
7030
7031 return true;
7032}
7033
Alexey Dobriyanb54452b2010-02-18 08:14:31 +00007034static const struct nla_policy nl80211_txattr_policy[NL80211_TXRATE_MAX + 1] = {
Jouni Malinen13ae75b2009-12-29 12:59:45 +02007035 [NL80211_TXRATE_LEGACY] = { .type = NLA_BINARY,
7036 .len = NL80211_MAX_SUPP_RATES },
Simon Wunderlich24db78c2012-01-28 17:25:32 +01007037 [NL80211_TXRATE_MCS] = { .type = NLA_BINARY,
7038 .len = NL80211_MAX_SUPP_HT_RATES },
Jouni Malinen13ae75b2009-12-29 12:59:45 +02007039};
7040
7041static int nl80211_set_tx_bitrate_mask(struct sk_buff *skb,
7042 struct genl_info *info)
7043{
7044 struct nlattr *tb[NL80211_TXRATE_MAX + 1];
Johannes Berg4c476992010-10-04 21:36:35 +02007045 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Jouni Malinen13ae75b2009-12-29 12:59:45 +02007046 struct cfg80211_bitrate_mask mask;
Johannes Berg4c476992010-10-04 21:36:35 +02007047 int rem, i;
7048 struct net_device *dev = info->user_ptr[1];
Jouni Malinen13ae75b2009-12-29 12:59:45 +02007049 struct nlattr *tx_rates;
7050 struct ieee80211_supported_band *sband;
7051
7052 if (info->attrs[NL80211_ATTR_TX_RATES] == NULL)
7053 return -EINVAL;
7054
Johannes Berg4c476992010-10-04 21:36:35 +02007055 if (!rdev->ops->set_bitrate_mask)
7056 return -EOPNOTSUPP;
Jouni Malinen13ae75b2009-12-29 12:59:45 +02007057
7058 memset(&mask, 0, sizeof(mask));
7059 /* Default to all rates enabled */
7060 for (i = 0; i < IEEE80211_NUM_BANDS; i++) {
7061 sband = rdev->wiphy.bands[i];
7062 mask.control[i].legacy =
7063 sband ? (1 << sband->n_bitrates) - 1 : 0;
Simon Wunderlich24db78c2012-01-28 17:25:32 +01007064 if (sband)
7065 memcpy(mask.control[i].mcs,
7066 sband->ht_cap.mcs.rx_mask,
7067 sizeof(mask.control[i].mcs));
7068 else
7069 memset(mask.control[i].mcs, 0,
7070 sizeof(mask.control[i].mcs));
Jouni Malinen13ae75b2009-12-29 12:59:45 +02007071 }
7072
7073 /*
7074 * The nested attribute uses enum nl80211_band as the index. This maps
7075 * directly to the enum ieee80211_band values used in cfg80211.
7076 */
Simon Wunderlich24db78c2012-01-28 17:25:32 +01007077 BUILD_BUG_ON(NL80211_MAX_SUPP_HT_RATES > IEEE80211_HT_MCS_MASK_LEN * 8);
Jouni Malinen13ae75b2009-12-29 12:59:45 +02007078 nla_for_each_nested(tx_rates, info->attrs[NL80211_ATTR_TX_RATES], rem)
7079 {
7080 enum ieee80211_band band = nla_type(tx_rates);
Johannes Berg4c476992010-10-04 21:36:35 +02007081 if (band < 0 || band >= IEEE80211_NUM_BANDS)
7082 return -EINVAL;
Jouni Malinen13ae75b2009-12-29 12:59:45 +02007083 sband = rdev->wiphy.bands[band];
Johannes Berg4c476992010-10-04 21:36:35 +02007084 if (sband == NULL)
7085 return -EINVAL;
Jouni Malinen13ae75b2009-12-29 12:59:45 +02007086 nla_parse(tb, NL80211_TXRATE_MAX, nla_data(tx_rates),
7087 nla_len(tx_rates), nl80211_txattr_policy);
7088 if (tb[NL80211_TXRATE_LEGACY]) {
7089 mask.control[band].legacy = rateset_to_mask(
7090 sband,
7091 nla_data(tb[NL80211_TXRATE_LEGACY]),
7092 nla_len(tb[NL80211_TXRATE_LEGACY]));
Bala Shanmugam218d2e22012-04-20 19:12:58 +05307093 if ((mask.control[band].legacy == 0) &&
7094 nla_len(tb[NL80211_TXRATE_LEGACY]))
7095 return -EINVAL;
Simon Wunderlich24db78c2012-01-28 17:25:32 +01007096 }
7097 if (tb[NL80211_TXRATE_MCS]) {
7098 if (!ht_rateset_to_mask(
7099 sband,
7100 nla_data(tb[NL80211_TXRATE_MCS]),
7101 nla_len(tb[NL80211_TXRATE_MCS]),
7102 mask.control[band].mcs))
7103 return -EINVAL;
7104 }
7105
7106 if (mask.control[band].legacy == 0) {
7107 /* don't allow empty legacy rates if HT
7108 * is not even supported. */
7109 if (!rdev->wiphy.bands[band]->ht_cap.ht_supported)
7110 return -EINVAL;
7111
7112 for (i = 0; i < IEEE80211_HT_MCS_MASK_LEN; i++)
7113 if (mask.control[band].mcs[i])
7114 break;
7115
7116 /* legacy and mcs rates may not be both empty */
7117 if (i == IEEE80211_HT_MCS_MASK_LEN)
Johannes Berg4c476992010-10-04 21:36:35 +02007118 return -EINVAL;
Jouni Malinen13ae75b2009-12-29 12:59:45 +02007119 }
7120 }
7121
Hila Gonene35e4d22012-06-27 17:19:42 +03007122 return rdev_set_bitrate_mask(rdev, dev, NULL, &mask);
Jouni Malinen13ae75b2009-12-29 12:59:45 +02007123}
7124
Johannes Berg2e161f72010-08-12 15:38:38 +02007125static int nl80211_register_mgmt(struct sk_buff *skb, struct genl_info *info)
Jouni Malinen026331c2010-02-15 12:53:10 +02007126{
Johannes Berg4c476992010-10-04 21:36:35 +02007127 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Johannes Berg71bbc992012-06-15 15:30:18 +02007128 struct wireless_dev *wdev = info->user_ptr[1];
Johannes Berg2e161f72010-08-12 15:38:38 +02007129 u16 frame_type = IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_ACTION;
Jouni Malinen026331c2010-02-15 12:53:10 +02007130
7131 if (!info->attrs[NL80211_ATTR_FRAME_MATCH])
7132 return -EINVAL;
7133
Johannes Berg2e161f72010-08-12 15:38:38 +02007134 if (info->attrs[NL80211_ATTR_FRAME_TYPE])
7135 frame_type = nla_get_u16(info->attrs[NL80211_ATTR_FRAME_TYPE]);
Jouni Malinen026331c2010-02-15 12:53:10 +02007136
Johannes Berg71bbc992012-06-15 15:30:18 +02007137 switch (wdev->iftype) {
7138 case NL80211_IFTYPE_STATION:
7139 case NL80211_IFTYPE_ADHOC:
7140 case NL80211_IFTYPE_P2P_CLIENT:
7141 case NL80211_IFTYPE_AP:
7142 case NL80211_IFTYPE_AP_VLAN:
7143 case NL80211_IFTYPE_MESH_POINT:
7144 case NL80211_IFTYPE_P2P_GO:
Johannes Berg98104fde2012-06-16 00:19:54 +02007145 case NL80211_IFTYPE_P2P_DEVICE:
Johannes Berg71bbc992012-06-15 15:30:18 +02007146 break;
7147 default:
Johannes Berg4c476992010-10-04 21:36:35 +02007148 return -EOPNOTSUPP;
Johannes Berg71bbc992012-06-15 15:30:18 +02007149 }
Jouni Malinen026331c2010-02-15 12:53:10 +02007150
7151 /* not much point in registering if we can't reply */
Johannes Berg4c476992010-10-04 21:36:35 +02007152 if (!rdev->ops->mgmt_tx)
7153 return -EOPNOTSUPP;
Jouni Malinen026331c2010-02-15 12:53:10 +02007154
Eric W. Biederman15e47302012-09-07 20:12:54 +00007155 return cfg80211_mlme_register_mgmt(wdev, info->snd_portid, frame_type,
Jouni Malinen026331c2010-02-15 12:53:10 +02007156 nla_data(info->attrs[NL80211_ATTR_FRAME_MATCH]),
7157 nla_len(info->attrs[NL80211_ATTR_FRAME_MATCH]));
Jouni Malinen026331c2010-02-15 12:53:10 +02007158}
7159
Johannes Berg2e161f72010-08-12 15:38:38 +02007160static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info)
Jouni Malinen026331c2010-02-15 12:53:10 +02007161{
Johannes Berg4c476992010-10-04 21:36:35 +02007162 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Johannes Berg71bbc992012-06-15 15:30:18 +02007163 struct wireless_dev *wdev = info->user_ptr[1];
Johannes Berg683b6d32012-11-08 21:25:48 +01007164 struct cfg80211_chan_def chandef;
Jouni Malinen026331c2010-02-15 12:53:10 +02007165 int err;
Johannes Bergd64d3732011-11-10 09:44:46 +01007166 void *hdr = NULL;
Jouni Malinen026331c2010-02-15 12:53:10 +02007167 u64 cookie;
Johannes Berge247bd902011-11-04 11:18:21 +01007168 struct sk_buff *msg = NULL;
Johannes Bergf7ca38d2010-11-25 10:02:29 +01007169 unsigned int wait = 0;
Johannes Berge247bd902011-11-04 11:18:21 +01007170 bool offchan, no_cck, dont_wait_for_ack;
7171
7172 dont_wait_for_ack = info->attrs[NL80211_ATTR_DONT_WAIT_FOR_ACK];
Jouni Malinen026331c2010-02-15 12:53:10 +02007173
Johannes Berg683b6d32012-11-08 21:25:48 +01007174 if (!info->attrs[NL80211_ATTR_FRAME])
Jouni Malinen026331c2010-02-15 12:53:10 +02007175 return -EINVAL;
7176
Johannes Berg4c476992010-10-04 21:36:35 +02007177 if (!rdev->ops->mgmt_tx)
7178 return -EOPNOTSUPP;
Jouni Malinen026331c2010-02-15 12:53:10 +02007179
Johannes Berg71bbc992012-06-15 15:30:18 +02007180 switch (wdev->iftype) {
7181 case NL80211_IFTYPE_STATION:
7182 case NL80211_IFTYPE_ADHOC:
7183 case NL80211_IFTYPE_P2P_CLIENT:
7184 case NL80211_IFTYPE_AP:
7185 case NL80211_IFTYPE_AP_VLAN:
7186 case NL80211_IFTYPE_MESH_POINT:
7187 case NL80211_IFTYPE_P2P_GO:
Johannes Berg98104fde2012-06-16 00:19:54 +02007188 case NL80211_IFTYPE_P2P_DEVICE:
Johannes Berg71bbc992012-06-15 15:30:18 +02007189 break;
7190 default:
Johannes Berg4c476992010-10-04 21:36:35 +02007191 return -EOPNOTSUPP;
Johannes Berg71bbc992012-06-15 15:30:18 +02007192 }
Jouni Malinen026331c2010-02-15 12:53:10 +02007193
Johannes Bergf7ca38d2010-11-25 10:02:29 +01007194 if (info->attrs[NL80211_ATTR_DURATION]) {
Johannes Berg7c4ef712011-11-18 15:33:48 +01007195 if (!(rdev->wiphy.flags & WIPHY_FLAG_OFFCHAN_TX))
Johannes Bergf7ca38d2010-11-25 10:02:29 +01007196 return -EINVAL;
7197 wait = nla_get_u32(info->attrs[NL80211_ATTR_DURATION]);
Johannes Bergebf348f2012-06-01 12:50:54 +02007198
7199 /*
7200 * We should wait on the channel for at least a minimum amount
7201 * of time (10ms) but no longer than the driver supports.
7202 */
7203 if (wait < NL80211_MIN_REMAIN_ON_CHANNEL_TIME ||
7204 wait > rdev->wiphy.max_remain_on_channel_duration)
7205 return -EINVAL;
7206
Johannes Bergf7ca38d2010-11-25 10:02:29 +01007207 }
7208
Johannes Bergf7ca38d2010-11-25 10:02:29 +01007209 offchan = info->attrs[NL80211_ATTR_OFFCHANNEL_TX_OK];
7210
Johannes Berg7c4ef712011-11-18 15:33:48 +01007211 if (offchan && !(rdev->wiphy.flags & WIPHY_FLAG_OFFCHAN_TX))
7212 return -EINVAL;
7213
Rajkumar Manoharane9f935e2011-09-25 14:53:30 +05307214 no_cck = nla_get_flag(info->attrs[NL80211_ATTR_TX_NO_CCK_RATE]);
7215
Johannes Berg683b6d32012-11-08 21:25:48 +01007216 err = nl80211_parse_chandef(rdev, info, &chandef);
7217 if (err)
7218 return err;
Jouni Malinen026331c2010-02-15 12:53:10 +02007219
Johannes Berge247bd902011-11-04 11:18:21 +01007220 if (!dont_wait_for_ack) {
7221 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
7222 if (!msg)
7223 return -ENOMEM;
Jouni Malinen026331c2010-02-15 12:53:10 +02007224
Eric W. Biederman15e47302012-09-07 20:12:54 +00007225 hdr = nl80211hdr_put(msg, info->snd_portid, info->snd_seq, 0,
Johannes Berge247bd902011-11-04 11:18:21 +01007226 NL80211_CMD_FRAME);
Jouni Malinen026331c2010-02-15 12:53:10 +02007227
Johannes Berge247bd902011-11-04 11:18:21 +01007228 if (IS_ERR(hdr)) {
7229 err = PTR_ERR(hdr);
7230 goto free_msg;
7231 }
Jouni Malinen026331c2010-02-15 12:53:10 +02007232 }
Johannes Berge247bd902011-11-04 11:18:21 +01007233
Johannes Berg683b6d32012-11-08 21:25:48 +01007234 err = cfg80211_mlme_mgmt_tx(rdev, wdev, chandef.chan, offchan, wait,
Johannes Berg2e161f72010-08-12 15:38:38 +02007235 nla_data(info->attrs[NL80211_ATTR_FRAME]),
7236 nla_len(info->attrs[NL80211_ATTR_FRAME]),
Johannes Berge247bd902011-11-04 11:18:21 +01007237 no_cck, dont_wait_for_ack, &cookie);
Jouni Malinen026331c2010-02-15 12:53:10 +02007238 if (err)
7239 goto free_msg;
7240
Johannes Berge247bd902011-11-04 11:18:21 +01007241 if (msg) {
David S. Miller9360ffd2012-03-29 04:41:26 -04007242 if (nla_put_u64(msg, NL80211_ATTR_COOKIE, cookie))
7243 goto nla_put_failure;
Jouni Malinen026331c2010-02-15 12:53:10 +02007244
Johannes Berge247bd902011-11-04 11:18:21 +01007245 genlmsg_end(msg, hdr);
7246 return genlmsg_reply(msg, info);
7247 }
7248
7249 return 0;
Jouni Malinen026331c2010-02-15 12:53:10 +02007250
7251 nla_put_failure:
7252 err = -ENOBUFS;
7253 free_msg:
7254 nlmsg_free(msg);
Jouni Malinen026331c2010-02-15 12:53:10 +02007255 return err;
7256}
7257
Johannes Bergf7ca38d2010-11-25 10:02:29 +01007258static int nl80211_tx_mgmt_cancel_wait(struct sk_buff *skb, struct genl_info *info)
7259{
7260 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Johannes Berg71bbc992012-06-15 15:30:18 +02007261 struct wireless_dev *wdev = info->user_ptr[1];
Johannes Bergf7ca38d2010-11-25 10:02:29 +01007262 u64 cookie;
7263
7264 if (!info->attrs[NL80211_ATTR_COOKIE])
7265 return -EINVAL;
7266
7267 if (!rdev->ops->mgmt_tx_cancel_wait)
7268 return -EOPNOTSUPP;
7269
Johannes Berg71bbc992012-06-15 15:30:18 +02007270 switch (wdev->iftype) {
7271 case NL80211_IFTYPE_STATION:
7272 case NL80211_IFTYPE_ADHOC:
7273 case NL80211_IFTYPE_P2P_CLIENT:
7274 case NL80211_IFTYPE_AP:
7275 case NL80211_IFTYPE_AP_VLAN:
7276 case NL80211_IFTYPE_P2P_GO:
Johannes Berg98104fde2012-06-16 00:19:54 +02007277 case NL80211_IFTYPE_P2P_DEVICE:
Johannes Berg71bbc992012-06-15 15:30:18 +02007278 break;
7279 default:
Johannes Bergf7ca38d2010-11-25 10:02:29 +01007280 return -EOPNOTSUPP;
Johannes Berg71bbc992012-06-15 15:30:18 +02007281 }
Johannes Bergf7ca38d2010-11-25 10:02:29 +01007282
7283 cookie = nla_get_u64(info->attrs[NL80211_ATTR_COOKIE]);
7284
Hila Gonene35e4d22012-06-27 17:19:42 +03007285 return rdev_mgmt_tx_cancel_wait(rdev, wdev, cookie);
Johannes Bergf7ca38d2010-11-25 10:02:29 +01007286}
7287
Kalle Valoffb9eb32010-02-17 17:58:10 +02007288static int nl80211_set_power_save(struct sk_buff *skb, struct genl_info *info)
7289{
Johannes Berg4c476992010-10-04 21:36:35 +02007290 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Kalle Valoffb9eb32010-02-17 17:58:10 +02007291 struct wireless_dev *wdev;
Johannes Berg4c476992010-10-04 21:36:35 +02007292 struct net_device *dev = info->user_ptr[1];
Kalle Valoffb9eb32010-02-17 17:58:10 +02007293 u8 ps_state;
7294 bool state;
7295 int err;
7296
Johannes Berg4c476992010-10-04 21:36:35 +02007297 if (!info->attrs[NL80211_ATTR_PS_STATE])
7298 return -EINVAL;
Kalle Valoffb9eb32010-02-17 17:58:10 +02007299
7300 ps_state = nla_get_u32(info->attrs[NL80211_ATTR_PS_STATE]);
7301
Johannes Berg4c476992010-10-04 21:36:35 +02007302 if (ps_state != NL80211_PS_DISABLED && ps_state != NL80211_PS_ENABLED)
7303 return -EINVAL;
Kalle Valoffb9eb32010-02-17 17:58:10 +02007304
7305 wdev = dev->ieee80211_ptr;
7306
Johannes Berg4c476992010-10-04 21:36:35 +02007307 if (!rdev->ops->set_power_mgmt)
7308 return -EOPNOTSUPP;
Kalle Valoffb9eb32010-02-17 17:58:10 +02007309
7310 state = (ps_state == NL80211_PS_ENABLED) ? true : false;
7311
7312 if (state == wdev->ps)
Johannes Berg4c476992010-10-04 21:36:35 +02007313 return 0;
Kalle Valoffb9eb32010-02-17 17:58:10 +02007314
Hila Gonene35e4d22012-06-27 17:19:42 +03007315 err = rdev_set_power_mgmt(rdev, dev, state, wdev->ps_timeout);
Johannes Berg4c476992010-10-04 21:36:35 +02007316 if (!err)
7317 wdev->ps = state;
Kalle Valoffb9eb32010-02-17 17:58:10 +02007318 return err;
7319}
7320
7321static int nl80211_get_power_save(struct sk_buff *skb, struct genl_info *info)
7322{
Johannes Berg4c476992010-10-04 21:36:35 +02007323 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Kalle Valoffb9eb32010-02-17 17:58:10 +02007324 enum nl80211_ps_state ps_state;
7325 struct wireless_dev *wdev;
Johannes Berg4c476992010-10-04 21:36:35 +02007326 struct net_device *dev = info->user_ptr[1];
Kalle Valoffb9eb32010-02-17 17:58:10 +02007327 struct sk_buff *msg;
7328 void *hdr;
7329 int err;
7330
Kalle Valoffb9eb32010-02-17 17:58:10 +02007331 wdev = dev->ieee80211_ptr;
7332
Johannes Berg4c476992010-10-04 21:36:35 +02007333 if (!rdev->ops->set_power_mgmt)
7334 return -EOPNOTSUPP;
Kalle Valoffb9eb32010-02-17 17:58:10 +02007335
7336 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg4c476992010-10-04 21:36:35 +02007337 if (!msg)
7338 return -ENOMEM;
Kalle Valoffb9eb32010-02-17 17:58:10 +02007339
Eric W. Biederman15e47302012-09-07 20:12:54 +00007340 hdr = nl80211hdr_put(msg, info->snd_portid, info->snd_seq, 0,
Kalle Valoffb9eb32010-02-17 17:58:10 +02007341 NL80211_CMD_GET_POWER_SAVE);
7342 if (!hdr) {
Johannes Berg4c476992010-10-04 21:36:35 +02007343 err = -ENOBUFS;
Kalle Valoffb9eb32010-02-17 17:58:10 +02007344 goto free_msg;
7345 }
7346
7347 if (wdev->ps)
7348 ps_state = NL80211_PS_ENABLED;
7349 else
7350 ps_state = NL80211_PS_DISABLED;
7351
David S. Miller9360ffd2012-03-29 04:41:26 -04007352 if (nla_put_u32(msg, NL80211_ATTR_PS_STATE, ps_state))
7353 goto nla_put_failure;
Kalle Valoffb9eb32010-02-17 17:58:10 +02007354
7355 genlmsg_end(msg, hdr);
Johannes Berg4c476992010-10-04 21:36:35 +02007356 return genlmsg_reply(msg, info);
Kalle Valoffb9eb32010-02-17 17:58:10 +02007357
Johannes Berg4c476992010-10-04 21:36:35 +02007358 nla_put_failure:
Kalle Valoffb9eb32010-02-17 17:58:10 +02007359 err = -ENOBUFS;
Johannes Berg4c476992010-10-04 21:36:35 +02007360 free_msg:
Kalle Valoffb9eb32010-02-17 17:58:10 +02007361 nlmsg_free(msg);
Kalle Valoffb9eb32010-02-17 17:58:10 +02007362 return err;
7363}
7364
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02007365static struct nla_policy
7366nl80211_attr_cqm_policy[NL80211_ATTR_CQM_MAX + 1] __read_mostly = {
7367 [NL80211_ATTR_CQM_RSSI_THOLD] = { .type = NLA_U32 },
7368 [NL80211_ATTR_CQM_RSSI_HYST] = { .type = NLA_U32 },
7369 [NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] = { .type = NLA_U32 },
Thomas Pedersen84f10702012-07-12 16:17:33 -07007370 [NL80211_ATTR_CQM_TXE_RATE] = { .type = NLA_U32 },
7371 [NL80211_ATTR_CQM_TXE_PKTS] = { .type = NLA_U32 },
7372 [NL80211_ATTR_CQM_TXE_INTVL] = { .type = NLA_U32 },
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02007373};
7374
Thomas Pedersen84f10702012-07-12 16:17:33 -07007375static int nl80211_set_cqm_txe(struct genl_info *info,
Johannes Bergd9d8b012012-11-26 12:51:52 +01007376 u32 rate, u32 pkts, u32 intvl)
Thomas Pedersen84f10702012-07-12 16:17:33 -07007377{
7378 struct cfg80211_registered_device *rdev = info->user_ptr[0];
7379 struct wireless_dev *wdev;
7380 struct net_device *dev = info->user_ptr[1];
7381
Johannes Bergd9d8b012012-11-26 12:51:52 +01007382 if (rate > 100 || intvl > NL80211_CQM_TXE_MAX_INTVL)
Thomas Pedersen84f10702012-07-12 16:17:33 -07007383 return -EINVAL;
7384
7385 wdev = dev->ieee80211_ptr;
7386
7387 if (!rdev->ops->set_cqm_txe_config)
7388 return -EOPNOTSUPP;
7389
7390 if (wdev->iftype != NL80211_IFTYPE_STATION &&
7391 wdev->iftype != NL80211_IFTYPE_P2P_CLIENT)
7392 return -EOPNOTSUPP;
7393
Hila Gonene35e4d22012-06-27 17:19:42 +03007394 return rdev_set_cqm_txe_config(rdev, dev, rate, pkts, intvl);
Thomas Pedersen84f10702012-07-12 16:17:33 -07007395}
7396
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02007397static int nl80211_set_cqm_rssi(struct genl_info *info,
7398 s32 threshold, u32 hysteresis)
7399{
Johannes Berg4c476992010-10-04 21:36:35 +02007400 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02007401 struct wireless_dev *wdev;
Johannes Berg4c476992010-10-04 21:36:35 +02007402 struct net_device *dev = info->user_ptr[1];
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02007403
7404 if (threshold > 0)
7405 return -EINVAL;
7406
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02007407 wdev = dev->ieee80211_ptr;
7408
Johannes Berg4c476992010-10-04 21:36:35 +02007409 if (!rdev->ops->set_cqm_rssi_config)
7410 return -EOPNOTSUPP;
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02007411
Johannes Berg074ac8d2010-09-16 14:58:22 +02007412 if (wdev->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg4c476992010-10-04 21:36:35 +02007413 wdev->iftype != NL80211_IFTYPE_P2P_CLIENT)
7414 return -EOPNOTSUPP;
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02007415
Hila Gonene35e4d22012-06-27 17:19:42 +03007416 return rdev_set_cqm_rssi_config(rdev, dev, threshold, hysteresis);
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02007417}
7418
7419static int nl80211_set_cqm(struct sk_buff *skb, struct genl_info *info)
7420{
7421 struct nlattr *attrs[NL80211_ATTR_CQM_MAX + 1];
7422 struct nlattr *cqm;
7423 int err;
7424
7425 cqm = info->attrs[NL80211_ATTR_CQM];
7426 if (!cqm) {
7427 err = -EINVAL;
7428 goto out;
7429 }
7430
7431 err = nla_parse_nested(attrs, NL80211_ATTR_CQM_MAX, cqm,
7432 nl80211_attr_cqm_policy);
7433 if (err)
7434 goto out;
7435
7436 if (attrs[NL80211_ATTR_CQM_RSSI_THOLD] &&
7437 attrs[NL80211_ATTR_CQM_RSSI_HYST]) {
7438 s32 threshold;
7439 u32 hysteresis;
7440 threshold = nla_get_u32(attrs[NL80211_ATTR_CQM_RSSI_THOLD]);
7441 hysteresis = nla_get_u32(attrs[NL80211_ATTR_CQM_RSSI_HYST]);
7442 err = nl80211_set_cqm_rssi(info, threshold, hysteresis);
Thomas Pedersen84f10702012-07-12 16:17:33 -07007443 } else if (attrs[NL80211_ATTR_CQM_TXE_RATE] &&
7444 attrs[NL80211_ATTR_CQM_TXE_PKTS] &&
7445 attrs[NL80211_ATTR_CQM_TXE_INTVL]) {
7446 u32 rate, pkts, intvl;
7447 rate = nla_get_u32(attrs[NL80211_ATTR_CQM_TXE_RATE]);
7448 pkts = nla_get_u32(attrs[NL80211_ATTR_CQM_TXE_PKTS]);
7449 intvl = nla_get_u32(attrs[NL80211_ATTR_CQM_TXE_INTVL]);
7450 err = nl80211_set_cqm_txe(info, rate, pkts, intvl);
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02007451 } else
7452 err = -EINVAL;
7453
7454out:
7455 return err;
7456}
7457
Johannes Berg29cbe682010-12-03 09:20:44 +01007458static int nl80211_join_mesh(struct sk_buff *skb, struct genl_info *info)
7459{
7460 struct cfg80211_registered_device *rdev = info->user_ptr[0];
7461 struct net_device *dev = info->user_ptr[1];
7462 struct mesh_config cfg;
Javier Cardonac80d5452010-12-16 17:37:49 -08007463 struct mesh_setup setup;
Johannes Berg29cbe682010-12-03 09:20:44 +01007464 int err;
7465
7466 /* start with default */
7467 memcpy(&cfg, &default_mesh_config, sizeof(cfg));
Javier Cardonac80d5452010-12-16 17:37:49 -08007468 memcpy(&setup, &default_mesh_setup, sizeof(setup));
Johannes Berg29cbe682010-12-03 09:20:44 +01007469
Javier Cardona24bdd9f2010-12-16 17:37:48 -08007470 if (info->attrs[NL80211_ATTR_MESH_CONFIG]) {
Johannes Berg29cbe682010-12-03 09:20:44 +01007471 /* and parse parameters if given */
Javier Cardona24bdd9f2010-12-16 17:37:48 -08007472 err = nl80211_parse_mesh_config(info, &cfg, NULL);
Johannes Berg29cbe682010-12-03 09:20:44 +01007473 if (err)
7474 return err;
7475 }
7476
7477 if (!info->attrs[NL80211_ATTR_MESH_ID] ||
7478 !nla_len(info->attrs[NL80211_ATTR_MESH_ID]))
7479 return -EINVAL;
7480
Javier Cardonac80d5452010-12-16 17:37:49 -08007481 setup.mesh_id = nla_data(info->attrs[NL80211_ATTR_MESH_ID]);
7482 setup.mesh_id_len = nla_len(info->attrs[NL80211_ATTR_MESH_ID]);
7483
Chun-Yeow Yeoh4bb62342011-11-24 17:15:20 -08007484 if (info->attrs[NL80211_ATTR_MCAST_RATE] &&
7485 !nl80211_parse_mcast_rate(rdev, setup.mcast_rate,
7486 nla_get_u32(info->attrs[NL80211_ATTR_MCAST_RATE])))
7487 return -EINVAL;
7488
Marco Porsch9bdbf042013-01-07 16:04:51 +01007489 if (info->attrs[NL80211_ATTR_BEACON_INTERVAL]) {
7490 setup.beacon_interval =
7491 nla_get_u32(info->attrs[NL80211_ATTR_BEACON_INTERVAL]);
7492 if (setup.beacon_interval < 10 ||
7493 setup.beacon_interval > 10000)
7494 return -EINVAL;
7495 }
7496
7497 if (info->attrs[NL80211_ATTR_DTIM_PERIOD]) {
7498 setup.dtim_period =
7499 nla_get_u32(info->attrs[NL80211_ATTR_DTIM_PERIOD]);
7500 if (setup.dtim_period < 1 || setup.dtim_period > 100)
7501 return -EINVAL;
7502 }
7503
Javier Cardonac80d5452010-12-16 17:37:49 -08007504 if (info->attrs[NL80211_ATTR_MESH_SETUP]) {
7505 /* parse additional setup parameters if given */
7506 err = nl80211_parse_mesh_setup(info, &setup);
7507 if (err)
7508 return err;
7509 }
7510
Thomas Pedersend37bb182013-03-04 13:06:13 -08007511 if (setup.user_mpm)
7512 cfg.auto_open_plinks = false;
7513
Johannes Bergcc1d2802012-05-16 23:50:20 +02007514 if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
Johannes Berg683b6d32012-11-08 21:25:48 +01007515 err = nl80211_parse_chandef(rdev, info, &setup.chandef);
7516 if (err)
7517 return err;
Johannes Bergcc1d2802012-05-16 23:50:20 +02007518 } else {
7519 /* cfg80211_join_mesh() will sort it out */
Johannes Berg683b6d32012-11-08 21:25:48 +01007520 setup.chandef.chan = NULL;
Johannes Bergcc1d2802012-05-16 23:50:20 +02007521 }
7522
Javier Cardonac80d5452010-12-16 17:37:49 -08007523 return cfg80211_join_mesh(rdev, dev, &setup, &cfg);
Johannes Berg29cbe682010-12-03 09:20:44 +01007524}
7525
7526static int nl80211_leave_mesh(struct sk_buff *skb, struct genl_info *info)
7527{
7528 struct cfg80211_registered_device *rdev = info->user_ptr[0];
7529 struct net_device *dev = info->user_ptr[1];
7530
7531 return cfg80211_leave_mesh(rdev, dev);
7532}
7533
Johannes Bergdfb89c52012-06-27 09:23:48 +02007534#ifdef CONFIG_PM
Amitkumar Karwarbb92d192013-02-12 12:16:26 -08007535static int nl80211_send_wowlan_patterns(struct sk_buff *msg,
7536 struct cfg80211_registered_device *rdev)
7537{
7538 struct nlattr *nl_pats, *nl_pat;
7539 int i, pat_len;
7540
7541 if (!rdev->wowlan->n_patterns)
7542 return 0;
7543
7544 nl_pats = nla_nest_start(msg, NL80211_WOWLAN_TRIG_PKT_PATTERN);
7545 if (!nl_pats)
7546 return -ENOBUFS;
7547
7548 for (i = 0; i < rdev->wowlan->n_patterns; i++) {
7549 nl_pat = nla_nest_start(msg, i + 1);
7550 if (!nl_pat)
7551 return -ENOBUFS;
7552 pat_len = rdev->wowlan->patterns[i].pattern_len;
7553 if (nla_put(msg, NL80211_WOWLAN_PKTPAT_MASK,
7554 DIV_ROUND_UP(pat_len, 8),
7555 rdev->wowlan->patterns[i].mask) ||
7556 nla_put(msg, NL80211_WOWLAN_PKTPAT_PATTERN,
7557 pat_len, rdev->wowlan->patterns[i].pattern) ||
7558 nla_put_u32(msg, NL80211_WOWLAN_PKTPAT_OFFSET,
7559 rdev->wowlan->patterns[i].pkt_offset))
7560 return -ENOBUFS;
7561 nla_nest_end(msg, nl_pat);
7562 }
7563 nla_nest_end(msg, nl_pats);
7564
7565 return 0;
7566}
7567
Johannes Berg2a0e0472013-01-23 22:57:40 +01007568static int nl80211_send_wowlan_tcp(struct sk_buff *msg,
7569 struct cfg80211_wowlan_tcp *tcp)
7570{
7571 struct nlattr *nl_tcp;
7572
7573 if (!tcp)
7574 return 0;
7575
7576 nl_tcp = nla_nest_start(msg, NL80211_WOWLAN_TRIG_TCP_CONNECTION);
7577 if (!nl_tcp)
7578 return -ENOBUFS;
7579
7580 if (nla_put_be32(msg, NL80211_WOWLAN_TCP_SRC_IPV4, tcp->src) ||
7581 nla_put_be32(msg, NL80211_WOWLAN_TCP_DST_IPV4, tcp->dst) ||
7582 nla_put(msg, NL80211_WOWLAN_TCP_DST_MAC, ETH_ALEN, tcp->dst_mac) ||
7583 nla_put_u16(msg, NL80211_WOWLAN_TCP_SRC_PORT, tcp->src_port) ||
7584 nla_put_u16(msg, NL80211_WOWLAN_TCP_DST_PORT, tcp->dst_port) ||
7585 nla_put(msg, NL80211_WOWLAN_TCP_DATA_PAYLOAD,
7586 tcp->payload_len, tcp->payload) ||
7587 nla_put_u32(msg, NL80211_WOWLAN_TCP_DATA_INTERVAL,
7588 tcp->data_interval) ||
7589 nla_put(msg, NL80211_WOWLAN_TCP_WAKE_PAYLOAD,
7590 tcp->wake_len, tcp->wake_data) ||
7591 nla_put(msg, NL80211_WOWLAN_TCP_WAKE_MASK,
7592 DIV_ROUND_UP(tcp->wake_len, 8), tcp->wake_mask))
7593 return -ENOBUFS;
7594
7595 if (tcp->payload_seq.len &&
7596 nla_put(msg, NL80211_WOWLAN_TCP_DATA_PAYLOAD_SEQ,
7597 sizeof(tcp->payload_seq), &tcp->payload_seq))
7598 return -ENOBUFS;
7599
7600 if (tcp->payload_tok.len &&
7601 nla_put(msg, NL80211_WOWLAN_TCP_DATA_PAYLOAD_TOKEN,
7602 sizeof(tcp->payload_tok) + tcp->tokens_size,
7603 &tcp->payload_tok))
7604 return -ENOBUFS;
7605
Johannes Berge248ad32013-05-16 10:24:28 +02007606 nla_nest_end(msg, nl_tcp);
7607
Johannes Berg2a0e0472013-01-23 22:57:40 +01007608 return 0;
7609}
7610
Johannes Bergff1b6e62011-05-04 15:37:28 +02007611static int nl80211_get_wowlan(struct sk_buff *skb, struct genl_info *info)
7612{
7613 struct cfg80211_registered_device *rdev = info->user_ptr[0];
7614 struct sk_buff *msg;
7615 void *hdr;
Johannes Berg2a0e0472013-01-23 22:57:40 +01007616 u32 size = NLMSG_DEFAULT_SIZE;
Johannes Bergff1b6e62011-05-04 15:37:28 +02007617
Johannes Berg2a0e0472013-01-23 22:57:40 +01007618 if (!rdev->wiphy.wowlan.flags && !rdev->wiphy.wowlan.n_patterns &&
7619 !rdev->wiphy.wowlan.tcp)
Johannes Bergff1b6e62011-05-04 15:37:28 +02007620 return -EOPNOTSUPP;
7621
Johannes Berg2a0e0472013-01-23 22:57:40 +01007622 if (rdev->wowlan && rdev->wowlan->tcp) {
7623 /* adjust size to have room for all the data */
7624 size += rdev->wowlan->tcp->tokens_size +
7625 rdev->wowlan->tcp->payload_len +
7626 rdev->wowlan->tcp->wake_len +
7627 rdev->wowlan->tcp->wake_len / 8;
7628 }
7629
7630 msg = nlmsg_new(size, GFP_KERNEL);
Johannes Bergff1b6e62011-05-04 15:37:28 +02007631 if (!msg)
7632 return -ENOMEM;
7633
Eric W. Biederman15e47302012-09-07 20:12:54 +00007634 hdr = nl80211hdr_put(msg, info->snd_portid, info->snd_seq, 0,
Johannes Bergff1b6e62011-05-04 15:37:28 +02007635 NL80211_CMD_GET_WOWLAN);
7636 if (!hdr)
7637 goto nla_put_failure;
7638
7639 if (rdev->wowlan) {
7640 struct nlattr *nl_wowlan;
7641
7642 nl_wowlan = nla_nest_start(msg, NL80211_ATTR_WOWLAN_TRIGGERS);
7643 if (!nl_wowlan)
7644 goto nla_put_failure;
7645
David S. Miller9360ffd2012-03-29 04:41:26 -04007646 if ((rdev->wowlan->any &&
7647 nla_put_flag(msg, NL80211_WOWLAN_TRIG_ANY)) ||
7648 (rdev->wowlan->disconnect &&
7649 nla_put_flag(msg, NL80211_WOWLAN_TRIG_DISCONNECT)) ||
7650 (rdev->wowlan->magic_pkt &&
7651 nla_put_flag(msg, NL80211_WOWLAN_TRIG_MAGIC_PKT)) ||
7652 (rdev->wowlan->gtk_rekey_failure &&
7653 nla_put_flag(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE)) ||
7654 (rdev->wowlan->eap_identity_req &&
7655 nla_put_flag(msg, NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST)) ||
7656 (rdev->wowlan->four_way_handshake &&
7657 nla_put_flag(msg, NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE)) ||
7658 (rdev->wowlan->rfkill_release &&
7659 nla_put_flag(msg, NL80211_WOWLAN_TRIG_RFKILL_RELEASE)))
7660 goto nla_put_failure;
Johannes Berg2a0e0472013-01-23 22:57:40 +01007661
Amitkumar Karwarbb92d192013-02-12 12:16:26 -08007662 if (nl80211_send_wowlan_patterns(msg, rdev))
7663 goto nla_put_failure;
Johannes Berg2a0e0472013-01-23 22:57:40 +01007664
7665 if (nl80211_send_wowlan_tcp(msg, rdev->wowlan->tcp))
7666 goto nla_put_failure;
7667
Johannes Bergff1b6e62011-05-04 15:37:28 +02007668 nla_nest_end(msg, nl_wowlan);
7669 }
7670
7671 genlmsg_end(msg, hdr);
7672 return genlmsg_reply(msg, info);
7673
7674nla_put_failure:
7675 nlmsg_free(msg);
7676 return -ENOBUFS;
7677}
7678
Johannes Berg2a0e0472013-01-23 22:57:40 +01007679static int nl80211_parse_wowlan_tcp(struct cfg80211_registered_device *rdev,
7680 struct nlattr *attr,
7681 struct cfg80211_wowlan *trig)
7682{
7683 struct nlattr *tb[NUM_NL80211_WOWLAN_TCP];
7684 struct cfg80211_wowlan_tcp *cfg;
7685 struct nl80211_wowlan_tcp_data_token *tok = NULL;
7686 struct nl80211_wowlan_tcp_data_seq *seq = NULL;
7687 u32 size;
7688 u32 data_size, wake_size, tokens_size = 0, wake_mask_size;
7689 int err, port;
7690
7691 if (!rdev->wiphy.wowlan.tcp)
7692 return -EINVAL;
7693
7694 err = nla_parse(tb, MAX_NL80211_WOWLAN_TCP,
7695 nla_data(attr), nla_len(attr),
7696 nl80211_wowlan_tcp_policy);
7697 if (err)
7698 return err;
7699
7700 if (!tb[NL80211_WOWLAN_TCP_SRC_IPV4] ||
7701 !tb[NL80211_WOWLAN_TCP_DST_IPV4] ||
7702 !tb[NL80211_WOWLAN_TCP_DST_MAC] ||
7703 !tb[NL80211_WOWLAN_TCP_DST_PORT] ||
7704 !tb[NL80211_WOWLAN_TCP_DATA_PAYLOAD] ||
7705 !tb[NL80211_WOWLAN_TCP_DATA_INTERVAL] ||
7706 !tb[NL80211_WOWLAN_TCP_WAKE_PAYLOAD] ||
7707 !tb[NL80211_WOWLAN_TCP_WAKE_MASK])
7708 return -EINVAL;
7709
7710 data_size = nla_len(tb[NL80211_WOWLAN_TCP_DATA_PAYLOAD]);
7711 if (data_size > rdev->wiphy.wowlan.tcp->data_payload_max)
7712 return -EINVAL;
7713
7714 if (nla_get_u32(tb[NL80211_WOWLAN_TCP_DATA_INTERVAL]) >
Johannes Berg723d5682013-02-26 13:56:40 +01007715 rdev->wiphy.wowlan.tcp->data_interval_max ||
7716 nla_get_u32(tb[NL80211_WOWLAN_TCP_DATA_INTERVAL]) == 0)
Johannes Berg2a0e0472013-01-23 22:57:40 +01007717 return -EINVAL;
7718
7719 wake_size = nla_len(tb[NL80211_WOWLAN_TCP_WAKE_PAYLOAD]);
7720 if (wake_size > rdev->wiphy.wowlan.tcp->wake_payload_max)
7721 return -EINVAL;
7722
7723 wake_mask_size = nla_len(tb[NL80211_WOWLAN_TCP_WAKE_MASK]);
7724 if (wake_mask_size != DIV_ROUND_UP(wake_size, 8))
7725 return -EINVAL;
7726
7727 if (tb[NL80211_WOWLAN_TCP_DATA_PAYLOAD_TOKEN]) {
7728 u32 tokln = nla_len(tb[NL80211_WOWLAN_TCP_DATA_PAYLOAD_TOKEN]);
7729
7730 tok = nla_data(tb[NL80211_WOWLAN_TCP_DATA_PAYLOAD_TOKEN]);
7731 tokens_size = tokln - sizeof(*tok);
7732
7733 if (!tok->len || tokens_size % tok->len)
7734 return -EINVAL;
7735 if (!rdev->wiphy.wowlan.tcp->tok)
7736 return -EINVAL;
7737 if (tok->len > rdev->wiphy.wowlan.tcp->tok->max_len)
7738 return -EINVAL;
7739 if (tok->len < rdev->wiphy.wowlan.tcp->tok->min_len)
7740 return -EINVAL;
7741 if (tokens_size > rdev->wiphy.wowlan.tcp->tok->bufsize)
7742 return -EINVAL;
7743 if (tok->offset + tok->len > data_size)
7744 return -EINVAL;
7745 }
7746
7747 if (tb[NL80211_WOWLAN_TCP_DATA_PAYLOAD_SEQ]) {
7748 seq = nla_data(tb[NL80211_WOWLAN_TCP_DATA_PAYLOAD_SEQ]);
7749 if (!rdev->wiphy.wowlan.tcp->seq)
7750 return -EINVAL;
7751 if (seq->len == 0 || seq->len > 4)
7752 return -EINVAL;
7753 if (seq->len + seq->offset > data_size)
7754 return -EINVAL;
7755 }
7756
7757 size = sizeof(*cfg);
7758 size += data_size;
7759 size += wake_size + wake_mask_size;
7760 size += tokens_size;
7761
7762 cfg = kzalloc(size, GFP_KERNEL);
7763 if (!cfg)
7764 return -ENOMEM;
7765 cfg->src = nla_get_be32(tb[NL80211_WOWLAN_TCP_SRC_IPV4]);
7766 cfg->dst = nla_get_be32(tb[NL80211_WOWLAN_TCP_DST_IPV4]);
7767 memcpy(cfg->dst_mac, nla_data(tb[NL80211_WOWLAN_TCP_DST_MAC]),
7768 ETH_ALEN);
7769 if (tb[NL80211_WOWLAN_TCP_SRC_PORT])
7770 port = nla_get_u16(tb[NL80211_WOWLAN_TCP_SRC_PORT]);
7771 else
7772 port = 0;
7773#ifdef CONFIG_INET
7774 /* allocate a socket and port for it and use it */
7775 err = __sock_create(wiphy_net(&rdev->wiphy), PF_INET, SOCK_STREAM,
7776 IPPROTO_TCP, &cfg->sock, 1);
7777 if (err) {
7778 kfree(cfg);
7779 return err;
7780 }
7781 if (inet_csk_get_port(cfg->sock->sk, port)) {
7782 sock_release(cfg->sock);
7783 kfree(cfg);
7784 return -EADDRINUSE;
7785 }
7786 cfg->src_port = inet_sk(cfg->sock->sk)->inet_num;
7787#else
7788 if (!port) {
7789 kfree(cfg);
7790 return -EINVAL;
7791 }
7792 cfg->src_port = port;
7793#endif
7794
7795 cfg->dst_port = nla_get_u16(tb[NL80211_WOWLAN_TCP_DST_PORT]);
7796 cfg->payload_len = data_size;
7797 cfg->payload = (u8 *)cfg + sizeof(*cfg) + tokens_size;
7798 memcpy((void *)cfg->payload,
7799 nla_data(tb[NL80211_WOWLAN_TCP_DATA_PAYLOAD]),
7800 data_size);
7801 if (seq)
7802 cfg->payload_seq = *seq;
7803 cfg->data_interval = nla_get_u32(tb[NL80211_WOWLAN_TCP_DATA_INTERVAL]);
7804 cfg->wake_len = wake_size;
7805 cfg->wake_data = (u8 *)cfg + sizeof(*cfg) + tokens_size + data_size;
7806 memcpy((void *)cfg->wake_data,
7807 nla_data(tb[NL80211_WOWLAN_TCP_WAKE_PAYLOAD]),
7808 wake_size);
7809 cfg->wake_mask = (u8 *)cfg + sizeof(*cfg) + tokens_size +
7810 data_size + wake_size;
7811 memcpy((void *)cfg->wake_mask,
7812 nla_data(tb[NL80211_WOWLAN_TCP_WAKE_MASK]),
7813 wake_mask_size);
7814 if (tok) {
7815 cfg->tokens_size = tokens_size;
7816 memcpy(&cfg->payload_tok, tok, sizeof(*tok) + tokens_size);
7817 }
7818
7819 trig->tcp = cfg;
7820
7821 return 0;
7822}
7823
Johannes Bergff1b6e62011-05-04 15:37:28 +02007824static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info)
7825{
7826 struct cfg80211_registered_device *rdev = info->user_ptr[0];
7827 struct nlattr *tb[NUM_NL80211_WOWLAN_TRIG];
Johannes Bergff1b6e62011-05-04 15:37:28 +02007828 struct cfg80211_wowlan new_triggers = {};
Johannes Bergae33bd82012-07-12 16:25:02 +02007829 struct cfg80211_wowlan *ntrig;
Johannes Bergff1b6e62011-05-04 15:37:28 +02007830 struct wiphy_wowlan_support *wowlan = &rdev->wiphy.wowlan;
7831 int err, i;
Johannes Berg6d525632012-04-04 15:05:25 +02007832 bool prev_enabled = rdev->wowlan;
Johannes Bergff1b6e62011-05-04 15:37:28 +02007833
Johannes Berg2a0e0472013-01-23 22:57:40 +01007834 if (!rdev->wiphy.wowlan.flags && !rdev->wiphy.wowlan.n_patterns &&
7835 !rdev->wiphy.wowlan.tcp)
Johannes Bergff1b6e62011-05-04 15:37:28 +02007836 return -EOPNOTSUPP;
7837
Johannes Bergae33bd82012-07-12 16:25:02 +02007838 if (!info->attrs[NL80211_ATTR_WOWLAN_TRIGGERS]) {
7839 cfg80211_rdev_free_wowlan(rdev);
7840 rdev->wowlan = NULL;
7841 goto set_wakeup;
7842 }
Johannes Bergff1b6e62011-05-04 15:37:28 +02007843
7844 err = nla_parse(tb, MAX_NL80211_WOWLAN_TRIG,
7845 nla_data(info->attrs[NL80211_ATTR_WOWLAN_TRIGGERS]),
7846 nla_len(info->attrs[NL80211_ATTR_WOWLAN_TRIGGERS]),
7847 nl80211_wowlan_policy);
7848 if (err)
7849 return err;
7850
7851 if (tb[NL80211_WOWLAN_TRIG_ANY]) {
7852 if (!(wowlan->flags & WIPHY_WOWLAN_ANY))
7853 return -EINVAL;
7854 new_triggers.any = true;
7855 }
7856
7857 if (tb[NL80211_WOWLAN_TRIG_DISCONNECT]) {
7858 if (!(wowlan->flags & WIPHY_WOWLAN_DISCONNECT))
7859 return -EINVAL;
7860 new_triggers.disconnect = true;
7861 }
7862
7863 if (tb[NL80211_WOWLAN_TRIG_MAGIC_PKT]) {
7864 if (!(wowlan->flags & WIPHY_WOWLAN_MAGIC_PKT))
7865 return -EINVAL;
7866 new_triggers.magic_pkt = true;
7867 }
7868
Johannes Berg77dbbb12011-07-13 10:48:55 +02007869 if (tb[NL80211_WOWLAN_TRIG_GTK_REKEY_SUPPORTED])
7870 return -EINVAL;
7871
7872 if (tb[NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE]) {
7873 if (!(wowlan->flags & WIPHY_WOWLAN_GTK_REKEY_FAILURE))
7874 return -EINVAL;
7875 new_triggers.gtk_rekey_failure = true;
7876 }
7877
7878 if (tb[NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST]) {
7879 if (!(wowlan->flags & WIPHY_WOWLAN_EAP_IDENTITY_REQ))
7880 return -EINVAL;
7881 new_triggers.eap_identity_req = true;
7882 }
7883
7884 if (tb[NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE]) {
7885 if (!(wowlan->flags & WIPHY_WOWLAN_4WAY_HANDSHAKE))
7886 return -EINVAL;
7887 new_triggers.four_way_handshake = true;
7888 }
7889
7890 if (tb[NL80211_WOWLAN_TRIG_RFKILL_RELEASE]) {
7891 if (!(wowlan->flags & WIPHY_WOWLAN_RFKILL_RELEASE))
7892 return -EINVAL;
7893 new_triggers.rfkill_release = true;
7894 }
7895
Johannes Bergff1b6e62011-05-04 15:37:28 +02007896 if (tb[NL80211_WOWLAN_TRIG_PKT_PATTERN]) {
7897 struct nlattr *pat;
7898 int n_patterns = 0;
Amitkumar Karwarbb92d192013-02-12 12:16:26 -08007899 int rem, pat_len, mask_len, pkt_offset;
Johannes Bergff1b6e62011-05-04 15:37:28 +02007900 struct nlattr *pat_tb[NUM_NL80211_WOWLAN_PKTPAT];
7901
7902 nla_for_each_nested(pat, tb[NL80211_WOWLAN_TRIG_PKT_PATTERN],
7903 rem)
7904 n_patterns++;
7905 if (n_patterns > wowlan->n_patterns)
7906 return -EINVAL;
7907
7908 new_triggers.patterns = kcalloc(n_patterns,
7909 sizeof(new_triggers.patterns[0]),
7910 GFP_KERNEL);
7911 if (!new_triggers.patterns)
7912 return -ENOMEM;
7913
7914 new_triggers.n_patterns = n_patterns;
7915 i = 0;
7916
7917 nla_for_each_nested(pat, tb[NL80211_WOWLAN_TRIG_PKT_PATTERN],
7918 rem) {
7919 nla_parse(pat_tb, MAX_NL80211_WOWLAN_PKTPAT,
7920 nla_data(pat), nla_len(pat), NULL);
7921 err = -EINVAL;
7922 if (!pat_tb[NL80211_WOWLAN_PKTPAT_MASK] ||
7923 !pat_tb[NL80211_WOWLAN_PKTPAT_PATTERN])
7924 goto error;
7925 pat_len = nla_len(pat_tb[NL80211_WOWLAN_PKTPAT_PATTERN]);
7926 mask_len = DIV_ROUND_UP(pat_len, 8);
7927 if (nla_len(pat_tb[NL80211_WOWLAN_PKTPAT_MASK]) !=
7928 mask_len)
7929 goto error;
7930 if (pat_len > wowlan->pattern_max_len ||
7931 pat_len < wowlan->pattern_min_len)
7932 goto error;
7933
Amitkumar Karwarbb92d192013-02-12 12:16:26 -08007934 if (!pat_tb[NL80211_WOWLAN_PKTPAT_OFFSET])
7935 pkt_offset = 0;
7936 else
7937 pkt_offset = nla_get_u32(
7938 pat_tb[NL80211_WOWLAN_PKTPAT_OFFSET]);
7939 if (pkt_offset > wowlan->max_pkt_offset)
7940 goto error;
7941 new_triggers.patterns[i].pkt_offset = pkt_offset;
7942
Johannes Bergff1b6e62011-05-04 15:37:28 +02007943 new_triggers.patterns[i].mask =
7944 kmalloc(mask_len + pat_len, GFP_KERNEL);
7945 if (!new_triggers.patterns[i].mask) {
7946 err = -ENOMEM;
7947 goto error;
7948 }
7949 new_triggers.patterns[i].pattern =
7950 new_triggers.patterns[i].mask + mask_len;
7951 memcpy(new_triggers.patterns[i].mask,
7952 nla_data(pat_tb[NL80211_WOWLAN_PKTPAT_MASK]),
7953 mask_len);
7954 new_triggers.patterns[i].pattern_len = pat_len;
7955 memcpy(new_triggers.patterns[i].pattern,
7956 nla_data(pat_tb[NL80211_WOWLAN_PKTPAT_PATTERN]),
7957 pat_len);
7958 i++;
7959 }
7960 }
7961
Johannes Berg2a0e0472013-01-23 22:57:40 +01007962 if (tb[NL80211_WOWLAN_TRIG_TCP_CONNECTION]) {
7963 err = nl80211_parse_wowlan_tcp(
7964 rdev, tb[NL80211_WOWLAN_TRIG_TCP_CONNECTION],
7965 &new_triggers);
7966 if (err)
7967 goto error;
7968 }
7969
Johannes Bergae33bd82012-07-12 16:25:02 +02007970 ntrig = kmemdup(&new_triggers, sizeof(new_triggers), GFP_KERNEL);
7971 if (!ntrig) {
7972 err = -ENOMEM;
7973 goto error;
Johannes Bergff1b6e62011-05-04 15:37:28 +02007974 }
Johannes Bergae33bd82012-07-12 16:25:02 +02007975 cfg80211_rdev_free_wowlan(rdev);
7976 rdev->wowlan = ntrig;
Johannes Bergff1b6e62011-05-04 15:37:28 +02007977
Johannes Bergae33bd82012-07-12 16:25:02 +02007978 set_wakeup:
Johannes Berg6d525632012-04-04 15:05:25 +02007979 if (rdev->ops->set_wakeup && prev_enabled != !!rdev->wowlan)
Hila Gonene35e4d22012-06-27 17:19:42 +03007980 rdev_set_wakeup(rdev, rdev->wowlan);
Johannes Berg6d525632012-04-04 15:05:25 +02007981
Johannes Bergff1b6e62011-05-04 15:37:28 +02007982 return 0;
7983 error:
7984 for (i = 0; i < new_triggers.n_patterns; i++)
7985 kfree(new_triggers.patterns[i].mask);
7986 kfree(new_triggers.patterns);
Johannes Berg2a0e0472013-01-23 22:57:40 +01007987 if (new_triggers.tcp && new_triggers.tcp->sock)
7988 sock_release(new_triggers.tcp->sock);
7989 kfree(new_triggers.tcp);
Johannes Bergff1b6e62011-05-04 15:37:28 +02007990 return err;
7991}
Johannes Bergdfb89c52012-06-27 09:23:48 +02007992#endif
Johannes Bergff1b6e62011-05-04 15:37:28 +02007993
Johannes Berge5497d72011-07-05 16:35:40 +02007994static int nl80211_set_rekey_data(struct sk_buff *skb, struct genl_info *info)
7995{
7996 struct cfg80211_registered_device *rdev = info->user_ptr[0];
7997 struct net_device *dev = info->user_ptr[1];
7998 struct wireless_dev *wdev = dev->ieee80211_ptr;
7999 struct nlattr *tb[NUM_NL80211_REKEY_DATA];
8000 struct cfg80211_gtk_rekey_data rekey_data;
8001 int err;
8002
8003 if (!info->attrs[NL80211_ATTR_REKEY_DATA])
8004 return -EINVAL;
8005
8006 err = nla_parse(tb, MAX_NL80211_REKEY_DATA,
8007 nla_data(info->attrs[NL80211_ATTR_REKEY_DATA]),
8008 nla_len(info->attrs[NL80211_ATTR_REKEY_DATA]),
8009 nl80211_rekey_policy);
8010 if (err)
8011 return err;
8012
8013 if (nla_len(tb[NL80211_REKEY_DATA_REPLAY_CTR]) != NL80211_REPLAY_CTR_LEN)
8014 return -ERANGE;
8015 if (nla_len(tb[NL80211_REKEY_DATA_KEK]) != NL80211_KEK_LEN)
8016 return -ERANGE;
8017 if (nla_len(tb[NL80211_REKEY_DATA_KCK]) != NL80211_KCK_LEN)
8018 return -ERANGE;
8019
8020 memcpy(rekey_data.kek, nla_data(tb[NL80211_REKEY_DATA_KEK]),
8021 NL80211_KEK_LEN);
8022 memcpy(rekey_data.kck, nla_data(tb[NL80211_REKEY_DATA_KCK]),
8023 NL80211_KCK_LEN);
8024 memcpy(rekey_data.replay_ctr,
8025 nla_data(tb[NL80211_REKEY_DATA_REPLAY_CTR]),
8026 NL80211_REPLAY_CTR_LEN);
8027
8028 wdev_lock(wdev);
8029 if (!wdev->current_bss) {
8030 err = -ENOTCONN;
8031 goto out;
8032 }
8033
8034 if (!rdev->ops->set_rekey_data) {
8035 err = -EOPNOTSUPP;
8036 goto out;
8037 }
8038
Hila Gonene35e4d22012-06-27 17:19:42 +03008039 err = rdev_set_rekey_data(rdev, dev, &rekey_data);
Johannes Berge5497d72011-07-05 16:35:40 +02008040 out:
8041 wdev_unlock(wdev);
8042 return err;
8043}
8044
Johannes Berg28946da2011-11-04 11:18:12 +01008045static int nl80211_register_unexpected_frame(struct sk_buff *skb,
8046 struct genl_info *info)
8047{
8048 struct net_device *dev = info->user_ptr[1];
8049 struct wireless_dev *wdev = dev->ieee80211_ptr;
8050
8051 if (wdev->iftype != NL80211_IFTYPE_AP &&
8052 wdev->iftype != NL80211_IFTYPE_P2P_GO)
8053 return -EINVAL;
8054
Eric W. Biederman15e47302012-09-07 20:12:54 +00008055 if (wdev->ap_unexpected_nlportid)
Johannes Berg28946da2011-11-04 11:18:12 +01008056 return -EBUSY;
8057
Eric W. Biederman15e47302012-09-07 20:12:54 +00008058 wdev->ap_unexpected_nlportid = info->snd_portid;
Johannes Berg28946da2011-11-04 11:18:12 +01008059 return 0;
8060}
8061
Johannes Berg7f6cf312011-11-04 11:18:15 +01008062static int nl80211_probe_client(struct sk_buff *skb,
8063 struct genl_info *info)
8064{
8065 struct cfg80211_registered_device *rdev = info->user_ptr[0];
8066 struct net_device *dev = info->user_ptr[1];
8067 struct wireless_dev *wdev = dev->ieee80211_ptr;
8068 struct sk_buff *msg;
8069 void *hdr;
8070 const u8 *addr;
8071 u64 cookie;
8072 int err;
8073
8074 if (wdev->iftype != NL80211_IFTYPE_AP &&
8075 wdev->iftype != NL80211_IFTYPE_P2P_GO)
8076 return -EOPNOTSUPP;
8077
8078 if (!info->attrs[NL80211_ATTR_MAC])
8079 return -EINVAL;
8080
8081 if (!rdev->ops->probe_client)
8082 return -EOPNOTSUPP;
8083
8084 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
8085 if (!msg)
8086 return -ENOMEM;
8087
Eric W. Biederman15e47302012-09-07 20:12:54 +00008088 hdr = nl80211hdr_put(msg, info->snd_portid, info->snd_seq, 0,
Johannes Berg7f6cf312011-11-04 11:18:15 +01008089 NL80211_CMD_PROBE_CLIENT);
8090
8091 if (IS_ERR(hdr)) {
8092 err = PTR_ERR(hdr);
8093 goto free_msg;
8094 }
8095
8096 addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
8097
Hila Gonene35e4d22012-06-27 17:19:42 +03008098 err = rdev_probe_client(rdev, dev, addr, &cookie);
Johannes Berg7f6cf312011-11-04 11:18:15 +01008099 if (err)
8100 goto free_msg;
8101
David S. Miller9360ffd2012-03-29 04:41:26 -04008102 if (nla_put_u64(msg, NL80211_ATTR_COOKIE, cookie))
8103 goto nla_put_failure;
Johannes Berg7f6cf312011-11-04 11:18:15 +01008104
8105 genlmsg_end(msg, hdr);
8106
8107 return genlmsg_reply(msg, info);
8108
8109 nla_put_failure:
8110 err = -ENOBUFS;
8111 free_msg:
8112 nlmsg_free(msg);
8113 return err;
8114}
8115
Johannes Berg5e760232011-11-04 11:18:17 +01008116static int nl80211_register_beacons(struct sk_buff *skb, struct genl_info *info)
8117{
8118 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Ben Greear37c73b52012-10-26 14:49:25 -07008119 struct cfg80211_beacon_registration *reg, *nreg;
8120 int rv;
Johannes Berg5e760232011-11-04 11:18:17 +01008121
8122 if (!(rdev->wiphy.flags & WIPHY_FLAG_REPORTS_OBSS))
8123 return -EOPNOTSUPP;
8124
Ben Greear37c73b52012-10-26 14:49:25 -07008125 nreg = kzalloc(sizeof(*nreg), GFP_KERNEL);
8126 if (!nreg)
8127 return -ENOMEM;
Johannes Berg5e760232011-11-04 11:18:17 +01008128
Ben Greear37c73b52012-10-26 14:49:25 -07008129 /* First, check if already registered. */
8130 spin_lock_bh(&rdev->beacon_registrations_lock);
8131 list_for_each_entry(reg, &rdev->beacon_registrations, list) {
8132 if (reg->nlportid == info->snd_portid) {
8133 rv = -EALREADY;
8134 goto out_err;
8135 }
8136 }
8137 /* Add it to the list */
8138 nreg->nlportid = info->snd_portid;
8139 list_add(&nreg->list, &rdev->beacon_registrations);
8140
8141 spin_unlock_bh(&rdev->beacon_registrations_lock);
Johannes Berg5e760232011-11-04 11:18:17 +01008142
8143 return 0;
Ben Greear37c73b52012-10-26 14:49:25 -07008144out_err:
8145 spin_unlock_bh(&rdev->beacon_registrations_lock);
8146 kfree(nreg);
8147 return rv;
Johannes Berg5e760232011-11-04 11:18:17 +01008148}
8149
Johannes Berg98104fde2012-06-16 00:19:54 +02008150static int nl80211_start_p2p_device(struct sk_buff *skb, struct genl_info *info)
8151{
8152 struct cfg80211_registered_device *rdev = info->user_ptr[0];
8153 struct wireless_dev *wdev = info->user_ptr[1];
8154 int err;
8155
8156 if (!rdev->ops->start_p2p_device)
8157 return -EOPNOTSUPP;
8158
8159 if (wdev->iftype != NL80211_IFTYPE_P2P_DEVICE)
8160 return -EOPNOTSUPP;
8161
8162 if (wdev->p2p_started)
8163 return 0;
8164
8165 mutex_lock(&rdev->devlist_mtx);
8166 err = cfg80211_can_add_interface(rdev, wdev->iftype);
8167 mutex_unlock(&rdev->devlist_mtx);
8168 if (err)
8169 return err;
8170
Johannes Bergeeb126e2012-10-23 15:16:50 +02008171 err = rdev_start_p2p_device(rdev, wdev);
Johannes Berg98104fde2012-06-16 00:19:54 +02008172 if (err)
8173 return err;
8174
8175 wdev->p2p_started = true;
8176 mutex_lock(&rdev->devlist_mtx);
8177 rdev->opencount++;
8178 mutex_unlock(&rdev->devlist_mtx);
8179
8180 return 0;
8181}
8182
8183static int nl80211_stop_p2p_device(struct sk_buff *skb, struct genl_info *info)
8184{
8185 struct cfg80211_registered_device *rdev = info->user_ptr[0];
8186 struct wireless_dev *wdev = info->user_ptr[1];
8187
8188 if (wdev->iftype != NL80211_IFTYPE_P2P_DEVICE)
8189 return -EOPNOTSUPP;
8190
8191 if (!rdev->ops->stop_p2p_device)
8192 return -EOPNOTSUPP;
8193
Johannes Berg65e8d5b2013-04-19 12:18:19 +02008194 mutex_lock(&rdev->devlist_mtx);
Johannes Bergf9f47522013-03-19 15:04:07 +01008195 mutex_lock(&rdev->sched_scan_mtx);
8196 cfg80211_stop_p2p_device(rdev, wdev);
8197 mutex_unlock(&rdev->sched_scan_mtx);
Johannes Berg65e8d5b2013-04-19 12:18:19 +02008198 mutex_unlock(&rdev->devlist_mtx);
Johannes Berg98104fde2012-06-16 00:19:54 +02008199
8200 return 0;
8201}
8202
Johannes Berg3713b4e2013-02-14 16:19:38 +01008203static int nl80211_get_protocol_features(struct sk_buff *skb,
8204 struct genl_info *info)
8205{
8206 void *hdr;
8207 struct sk_buff *msg;
8208
8209 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
8210 if (!msg)
8211 return -ENOMEM;
8212
8213 hdr = nl80211hdr_put(msg, info->snd_portid, info->snd_seq, 0,
8214 NL80211_CMD_GET_PROTOCOL_FEATURES);
8215 if (!hdr)
8216 goto nla_put_failure;
8217
8218 if (nla_put_u32(msg, NL80211_ATTR_PROTOCOL_FEATURES,
8219 NL80211_PROTOCOL_FEATURE_SPLIT_WIPHY_DUMP))
8220 goto nla_put_failure;
8221
8222 genlmsg_end(msg, hdr);
8223 return genlmsg_reply(msg, info);
8224
8225 nla_put_failure:
8226 kfree_skb(msg);
8227 return -ENOBUFS;
8228}
8229
Jouni Malinen355199e2013-02-27 17:14:27 +02008230static int nl80211_update_ft_ies(struct sk_buff *skb, struct genl_info *info)
8231{
8232 struct cfg80211_registered_device *rdev = info->user_ptr[0];
8233 struct cfg80211_update_ft_ies_params ft_params;
8234 struct net_device *dev = info->user_ptr[1];
8235
8236 if (!rdev->ops->update_ft_ies)
8237 return -EOPNOTSUPP;
8238
8239 if (!info->attrs[NL80211_ATTR_MDID] ||
8240 !is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
8241 return -EINVAL;
8242
8243 memset(&ft_params, 0, sizeof(ft_params));
8244 ft_params.md = nla_get_u16(info->attrs[NL80211_ATTR_MDID]);
8245 ft_params.ie = nla_data(info->attrs[NL80211_ATTR_IE]);
8246 ft_params.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
8247
8248 return rdev_update_ft_ies(rdev, dev, &ft_params);
8249}
8250
Arend van Spriel5de17982013-04-18 15:49:00 +02008251static int nl80211_crit_protocol_start(struct sk_buff *skb,
8252 struct genl_info *info)
8253{
8254 struct cfg80211_registered_device *rdev = info->user_ptr[0];
8255 struct wireless_dev *wdev = info->user_ptr[1];
8256 enum nl80211_crit_proto_id proto = NL80211_CRIT_PROTO_UNSPEC;
8257 u16 duration;
8258 int ret;
8259
8260 if (!rdev->ops->crit_proto_start)
8261 return -EOPNOTSUPP;
8262
8263 if (WARN_ON(!rdev->ops->crit_proto_stop))
8264 return -EINVAL;
8265
8266 if (rdev->crit_proto_nlportid)
8267 return -EBUSY;
8268
8269 /* determine protocol if provided */
8270 if (info->attrs[NL80211_ATTR_CRIT_PROT_ID])
8271 proto = nla_get_u16(info->attrs[NL80211_ATTR_CRIT_PROT_ID]);
8272
8273 if (proto >= NUM_NL80211_CRIT_PROTO)
8274 return -EINVAL;
8275
8276 /* timeout must be provided */
8277 if (!info->attrs[NL80211_ATTR_MAX_CRIT_PROT_DURATION])
8278 return -EINVAL;
8279
8280 duration =
8281 nla_get_u16(info->attrs[NL80211_ATTR_MAX_CRIT_PROT_DURATION]);
8282
8283 if (duration > NL80211_CRIT_PROTO_MAX_DURATION)
8284 return -ERANGE;
8285
8286 ret = rdev_crit_proto_start(rdev, wdev, proto, duration);
8287 if (!ret)
8288 rdev->crit_proto_nlportid = info->snd_portid;
8289
8290 return ret;
8291}
8292
8293static int nl80211_crit_protocol_stop(struct sk_buff *skb,
8294 struct genl_info *info)
8295{
8296 struct cfg80211_registered_device *rdev = info->user_ptr[0];
8297 struct wireless_dev *wdev = info->user_ptr[1];
8298
8299 if (!rdev->ops->crit_proto_stop)
8300 return -EOPNOTSUPP;
8301
8302 if (rdev->crit_proto_nlportid) {
8303 rdev->crit_proto_nlportid = 0;
8304 rdev_crit_proto_stop(rdev, wdev);
8305 }
8306 return 0;
8307}
8308
Johannes Berg4c476992010-10-04 21:36:35 +02008309#define NL80211_FLAG_NEED_WIPHY 0x01
8310#define NL80211_FLAG_NEED_NETDEV 0x02
8311#define NL80211_FLAG_NEED_RTNL 0x04
Johannes Berg41265712010-10-04 21:14:05 +02008312#define NL80211_FLAG_CHECK_NETDEV_UP 0x08
8313#define NL80211_FLAG_NEED_NETDEV_UP (NL80211_FLAG_NEED_NETDEV |\
8314 NL80211_FLAG_CHECK_NETDEV_UP)
Johannes Berg1bf614e2012-06-15 15:23:36 +02008315#define NL80211_FLAG_NEED_WDEV 0x10
Johannes Berg98104fde2012-06-16 00:19:54 +02008316/* If a netdev is associated, it must be UP, P2P must be started */
Johannes Berg1bf614e2012-06-15 15:23:36 +02008317#define NL80211_FLAG_NEED_WDEV_UP (NL80211_FLAG_NEED_WDEV |\
8318 NL80211_FLAG_CHECK_NETDEV_UP)
Johannes Berg4c476992010-10-04 21:36:35 +02008319
8320static int nl80211_pre_doit(struct genl_ops *ops, struct sk_buff *skb,
8321 struct genl_info *info)
8322{
8323 struct cfg80211_registered_device *rdev;
Johannes Berg89a54e42012-06-15 14:33:17 +02008324 struct wireless_dev *wdev;
Johannes Berg4c476992010-10-04 21:36:35 +02008325 struct net_device *dev;
Johannes Berg4c476992010-10-04 21:36:35 +02008326 bool rtnl = ops->internal_flags & NL80211_FLAG_NEED_RTNL;
8327
8328 if (rtnl)
8329 rtnl_lock();
8330
8331 if (ops->internal_flags & NL80211_FLAG_NEED_WIPHY) {
Johannes Berg4f7eff12012-06-15 14:14:22 +02008332 rdev = cfg80211_get_dev_from_info(genl_info_net(info), info);
Johannes Berg4c476992010-10-04 21:36:35 +02008333 if (IS_ERR(rdev)) {
8334 if (rtnl)
8335 rtnl_unlock();
8336 return PTR_ERR(rdev);
8337 }
8338 info->user_ptr[0] = rdev;
Johannes Berg1bf614e2012-06-15 15:23:36 +02008339 } else if (ops->internal_flags & NL80211_FLAG_NEED_NETDEV ||
8340 ops->internal_flags & NL80211_FLAG_NEED_WDEV) {
Johannes Berg89a54e42012-06-15 14:33:17 +02008341 mutex_lock(&cfg80211_mutex);
8342 wdev = __cfg80211_wdev_from_attrs(genl_info_net(info),
8343 info->attrs);
8344 if (IS_ERR(wdev)) {
8345 mutex_unlock(&cfg80211_mutex);
Johannes Berg4c476992010-10-04 21:36:35 +02008346 if (rtnl)
8347 rtnl_unlock();
Johannes Berg89a54e42012-06-15 14:33:17 +02008348 return PTR_ERR(wdev);
Johannes Berg4c476992010-10-04 21:36:35 +02008349 }
Johannes Berg89a54e42012-06-15 14:33:17 +02008350
Johannes Berg89a54e42012-06-15 14:33:17 +02008351 dev = wdev->netdev;
8352 rdev = wiphy_to_dev(wdev->wiphy);
8353
Johannes Berg1bf614e2012-06-15 15:23:36 +02008354 if (ops->internal_flags & NL80211_FLAG_NEED_NETDEV) {
8355 if (!dev) {
8356 mutex_unlock(&cfg80211_mutex);
8357 if (rtnl)
8358 rtnl_unlock();
8359 return -EINVAL;
8360 }
8361
8362 info->user_ptr[1] = dev;
8363 } else {
8364 info->user_ptr[1] = wdev;
Johannes Berg41265712010-10-04 21:14:05 +02008365 }
Johannes Berg89a54e42012-06-15 14:33:17 +02008366
Johannes Berg1bf614e2012-06-15 15:23:36 +02008367 if (dev) {
8368 if (ops->internal_flags & NL80211_FLAG_CHECK_NETDEV_UP &&
8369 !netif_running(dev)) {
8370 mutex_unlock(&cfg80211_mutex);
8371 if (rtnl)
8372 rtnl_unlock();
8373 return -ENETDOWN;
8374 }
8375
8376 dev_hold(dev);
Johannes Berg98104fde2012-06-16 00:19:54 +02008377 } else if (ops->internal_flags & NL80211_FLAG_CHECK_NETDEV_UP) {
8378 if (!wdev->p2p_started) {
8379 mutex_unlock(&cfg80211_mutex);
8380 if (rtnl)
8381 rtnl_unlock();
8382 return -ENETDOWN;
8383 }
Johannes Berg1bf614e2012-06-15 15:23:36 +02008384 }
8385
Johannes Berg89a54e42012-06-15 14:33:17 +02008386 cfg80211_lock_rdev(rdev);
8387
8388 mutex_unlock(&cfg80211_mutex);
8389
Johannes Berg4c476992010-10-04 21:36:35 +02008390 info->user_ptr[0] = rdev;
Johannes Berg4c476992010-10-04 21:36:35 +02008391 }
8392
8393 return 0;
8394}
8395
8396static void nl80211_post_doit(struct genl_ops *ops, struct sk_buff *skb,
8397 struct genl_info *info)
8398{
8399 if (info->user_ptr[0])
8400 cfg80211_unlock_rdev(info->user_ptr[0]);
Johannes Berg1bf614e2012-06-15 15:23:36 +02008401 if (info->user_ptr[1]) {
8402 if (ops->internal_flags & NL80211_FLAG_NEED_WDEV) {
8403 struct wireless_dev *wdev = info->user_ptr[1];
8404
8405 if (wdev->netdev)
8406 dev_put(wdev->netdev);
8407 } else {
8408 dev_put(info->user_ptr[1]);
8409 }
8410 }
Johannes Berg4c476992010-10-04 21:36:35 +02008411 if (ops->internal_flags & NL80211_FLAG_NEED_RTNL)
8412 rtnl_unlock();
8413}
8414
Johannes Berg55682962007-09-20 13:09:35 -04008415static struct genl_ops nl80211_ops[] = {
8416 {
8417 .cmd = NL80211_CMD_GET_WIPHY,
8418 .doit = nl80211_get_wiphy,
8419 .dumpit = nl80211_dump_wiphy,
8420 .policy = nl80211_policy,
8421 /* can be retrieved by unprivileged users */
Johannes Berg4c476992010-10-04 21:36:35 +02008422 .internal_flags = NL80211_FLAG_NEED_WIPHY,
Johannes Berg55682962007-09-20 13:09:35 -04008423 },
8424 {
8425 .cmd = NL80211_CMD_SET_WIPHY,
8426 .doit = nl80211_set_wiphy,
8427 .policy = nl80211_policy,
8428 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02008429 .internal_flags = NL80211_FLAG_NEED_RTNL,
Johannes Berg55682962007-09-20 13:09:35 -04008430 },
8431 {
8432 .cmd = NL80211_CMD_GET_INTERFACE,
8433 .doit = nl80211_get_interface,
8434 .dumpit = nl80211_dump_interface,
8435 .policy = nl80211_policy,
8436 /* can be retrieved by unprivileged users */
Johannes Berg72fb2ab2012-06-15 17:52:47 +02008437 .internal_flags = NL80211_FLAG_NEED_WDEV,
Johannes Berg55682962007-09-20 13:09:35 -04008438 },
8439 {
8440 .cmd = NL80211_CMD_SET_INTERFACE,
8441 .doit = nl80211_set_interface,
8442 .policy = nl80211_policy,
8443 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02008444 .internal_flags = NL80211_FLAG_NEED_NETDEV |
8445 NL80211_FLAG_NEED_RTNL,
Johannes Berg55682962007-09-20 13:09:35 -04008446 },
8447 {
8448 .cmd = NL80211_CMD_NEW_INTERFACE,
8449 .doit = nl80211_new_interface,
8450 .policy = nl80211_policy,
8451 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02008452 .internal_flags = NL80211_FLAG_NEED_WIPHY |
8453 NL80211_FLAG_NEED_RTNL,
Johannes Berg55682962007-09-20 13:09:35 -04008454 },
8455 {
8456 .cmd = NL80211_CMD_DEL_INTERFACE,
8457 .doit = nl80211_del_interface,
8458 .policy = nl80211_policy,
8459 .flags = GENL_ADMIN_PERM,
Johannes Berg84efbb82012-06-16 00:00:26 +02008460 .internal_flags = NL80211_FLAG_NEED_WDEV |
Johannes Berg4c476992010-10-04 21:36:35 +02008461 NL80211_FLAG_NEED_RTNL,
Johannes Berg55682962007-09-20 13:09:35 -04008462 },
Johannes Berg41ade002007-12-19 02:03:29 +01008463 {
8464 .cmd = NL80211_CMD_GET_KEY,
8465 .doit = nl80211_get_key,
8466 .policy = nl80211_policy,
8467 .flags = GENL_ADMIN_PERM,
Johannes Berg2b5f8b02012-04-02 10:51:55 +02008468 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02008469 NL80211_FLAG_NEED_RTNL,
Johannes Berg41ade002007-12-19 02:03:29 +01008470 },
8471 {
8472 .cmd = NL80211_CMD_SET_KEY,
8473 .doit = nl80211_set_key,
8474 .policy = nl80211_policy,
8475 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02008476 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02008477 NL80211_FLAG_NEED_RTNL,
Johannes Berg41ade002007-12-19 02:03:29 +01008478 },
8479 {
8480 .cmd = NL80211_CMD_NEW_KEY,
8481 .doit = nl80211_new_key,
8482 .policy = nl80211_policy,
8483 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02008484 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02008485 NL80211_FLAG_NEED_RTNL,
Johannes Berg41ade002007-12-19 02:03:29 +01008486 },
8487 {
8488 .cmd = NL80211_CMD_DEL_KEY,
8489 .doit = nl80211_del_key,
8490 .policy = nl80211_policy,
8491 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02008492 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02008493 NL80211_FLAG_NEED_RTNL,
Johannes Berg41ade002007-12-19 02:03:29 +01008494 },
Johannes Berged1b6cc2007-12-19 02:03:32 +01008495 {
8496 .cmd = NL80211_CMD_SET_BEACON,
8497 .policy = nl80211_policy,
8498 .flags = GENL_ADMIN_PERM,
Johannes Berg88600202012-02-13 15:17:18 +01008499 .doit = nl80211_set_beacon,
Johannes Berg2b5f8b02012-04-02 10:51:55 +02008500 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02008501 NL80211_FLAG_NEED_RTNL,
Johannes Berged1b6cc2007-12-19 02:03:32 +01008502 },
8503 {
Johannes Berg88600202012-02-13 15:17:18 +01008504 .cmd = NL80211_CMD_START_AP,
Johannes Berged1b6cc2007-12-19 02:03:32 +01008505 .policy = nl80211_policy,
8506 .flags = GENL_ADMIN_PERM,
Johannes Berg88600202012-02-13 15:17:18 +01008507 .doit = nl80211_start_ap,
Johannes Berg2b5f8b02012-04-02 10:51:55 +02008508 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02008509 NL80211_FLAG_NEED_RTNL,
Johannes Berged1b6cc2007-12-19 02:03:32 +01008510 },
8511 {
Johannes Berg88600202012-02-13 15:17:18 +01008512 .cmd = NL80211_CMD_STOP_AP,
Johannes Berged1b6cc2007-12-19 02:03:32 +01008513 .policy = nl80211_policy,
8514 .flags = GENL_ADMIN_PERM,
Johannes Berg88600202012-02-13 15:17:18 +01008515 .doit = nl80211_stop_ap,
Johannes Berg2b5f8b02012-04-02 10:51:55 +02008516 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02008517 NL80211_FLAG_NEED_RTNL,
Johannes Berged1b6cc2007-12-19 02:03:32 +01008518 },
Johannes Berg5727ef12007-12-19 02:03:34 +01008519 {
8520 .cmd = NL80211_CMD_GET_STATION,
8521 .doit = nl80211_get_station,
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01008522 .dumpit = nl80211_dump_station,
Johannes Berg5727ef12007-12-19 02:03:34 +01008523 .policy = nl80211_policy,
Johannes Berg4c476992010-10-04 21:36:35 +02008524 .internal_flags = NL80211_FLAG_NEED_NETDEV |
8525 NL80211_FLAG_NEED_RTNL,
Johannes Berg5727ef12007-12-19 02:03:34 +01008526 },
8527 {
8528 .cmd = NL80211_CMD_SET_STATION,
8529 .doit = nl80211_set_station,
8530 .policy = nl80211_policy,
8531 .flags = GENL_ADMIN_PERM,
Johannes Berg2b5f8b02012-04-02 10:51:55 +02008532 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02008533 NL80211_FLAG_NEED_RTNL,
Johannes Berg5727ef12007-12-19 02:03:34 +01008534 },
8535 {
8536 .cmd = NL80211_CMD_NEW_STATION,
8537 .doit = nl80211_new_station,
8538 .policy = nl80211_policy,
8539 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02008540 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02008541 NL80211_FLAG_NEED_RTNL,
Johannes Berg5727ef12007-12-19 02:03:34 +01008542 },
8543 {
8544 .cmd = NL80211_CMD_DEL_STATION,
8545 .doit = nl80211_del_station,
8546 .policy = nl80211_policy,
8547 .flags = GENL_ADMIN_PERM,
Johannes Berg2b5f8b02012-04-02 10:51:55 +02008548 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02008549 NL80211_FLAG_NEED_RTNL,
Johannes Berg5727ef12007-12-19 02:03:34 +01008550 },
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01008551 {
8552 .cmd = NL80211_CMD_GET_MPATH,
8553 .doit = nl80211_get_mpath,
8554 .dumpit = nl80211_dump_mpath,
8555 .policy = nl80211_policy,
8556 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02008557 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02008558 NL80211_FLAG_NEED_RTNL,
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01008559 },
8560 {
8561 .cmd = NL80211_CMD_SET_MPATH,
8562 .doit = nl80211_set_mpath,
8563 .policy = nl80211_policy,
8564 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02008565 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02008566 NL80211_FLAG_NEED_RTNL,
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01008567 },
8568 {
8569 .cmd = NL80211_CMD_NEW_MPATH,
8570 .doit = nl80211_new_mpath,
8571 .policy = nl80211_policy,
8572 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02008573 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02008574 NL80211_FLAG_NEED_RTNL,
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01008575 },
8576 {
8577 .cmd = NL80211_CMD_DEL_MPATH,
8578 .doit = nl80211_del_mpath,
8579 .policy = nl80211_policy,
8580 .flags = GENL_ADMIN_PERM,
Johannes Berg2b5f8b02012-04-02 10:51:55 +02008581 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02008582 NL80211_FLAG_NEED_RTNL,
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01008583 },
Jouni Malinen9f1ba902008-08-07 20:07:01 +03008584 {
8585 .cmd = NL80211_CMD_SET_BSS,
8586 .doit = nl80211_set_bss,
8587 .policy = nl80211_policy,
8588 .flags = GENL_ADMIN_PERM,
Johannes Berg2b5f8b02012-04-02 10:51:55 +02008589 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02008590 NL80211_FLAG_NEED_RTNL,
Jouni Malinen9f1ba902008-08-07 20:07:01 +03008591 },
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07008592 {
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08008593 .cmd = NL80211_CMD_GET_REG,
8594 .doit = nl80211_get_reg,
8595 .policy = nl80211_policy,
8596 /* can be retrieved by unprivileged users */
8597 },
8598 {
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07008599 .cmd = NL80211_CMD_SET_REG,
8600 .doit = nl80211_set_reg,
8601 .policy = nl80211_policy,
8602 .flags = GENL_ADMIN_PERM,
8603 },
8604 {
8605 .cmd = NL80211_CMD_REQ_SET_REG,
8606 .doit = nl80211_req_set_reg,
8607 .policy = nl80211_policy,
8608 .flags = GENL_ADMIN_PERM,
8609 },
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07008610 {
Javier Cardona24bdd9f2010-12-16 17:37:48 -08008611 .cmd = NL80211_CMD_GET_MESH_CONFIG,
8612 .doit = nl80211_get_mesh_config,
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07008613 .policy = nl80211_policy,
8614 /* can be retrieved by unprivileged users */
Johannes Berg2b5f8b02012-04-02 10:51:55 +02008615 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02008616 NL80211_FLAG_NEED_RTNL,
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07008617 },
8618 {
Javier Cardona24bdd9f2010-12-16 17:37:48 -08008619 .cmd = NL80211_CMD_SET_MESH_CONFIG,
8620 .doit = nl80211_update_mesh_config,
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07008621 .policy = nl80211_policy,
8622 .flags = GENL_ADMIN_PERM,
Johannes Berg29cbe682010-12-03 09:20:44 +01008623 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02008624 NL80211_FLAG_NEED_RTNL,
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07008625 },
Jouni Malinen9aed3cc2009-01-13 16:03:29 +02008626 {
Johannes Berg2a519312009-02-10 21:25:55 +01008627 .cmd = NL80211_CMD_TRIGGER_SCAN,
8628 .doit = nl80211_trigger_scan,
8629 .policy = nl80211_policy,
8630 .flags = GENL_ADMIN_PERM,
Johannes Bergfd014282012-06-18 19:17:03 +02008631 .internal_flags = NL80211_FLAG_NEED_WDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02008632 NL80211_FLAG_NEED_RTNL,
Johannes Berg2a519312009-02-10 21:25:55 +01008633 },
8634 {
8635 .cmd = NL80211_CMD_GET_SCAN,
8636 .policy = nl80211_policy,
8637 .dumpit = nl80211_dump_scan,
8638 },
Jouni Malinen636a5d32009-03-19 13:39:22 +02008639 {
Luciano Coelho807f8a82011-05-11 17:09:35 +03008640 .cmd = NL80211_CMD_START_SCHED_SCAN,
8641 .doit = nl80211_start_sched_scan,
8642 .policy = nl80211_policy,
8643 .flags = GENL_ADMIN_PERM,
8644 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
8645 NL80211_FLAG_NEED_RTNL,
8646 },
8647 {
8648 .cmd = NL80211_CMD_STOP_SCHED_SCAN,
8649 .doit = nl80211_stop_sched_scan,
8650 .policy = nl80211_policy,
8651 .flags = GENL_ADMIN_PERM,
8652 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
8653 NL80211_FLAG_NEED_RTNL,
8654 },
8655 {
Jouni Malinen636a5d32009-03-19 13:39:22 +02008656 .cmd = NL80211_CMD_AUTHENTICATE,
8657 .doit = nl80211_authenticate,
8658 .policy = nl80211_policy,
8659 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02008660 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02008661 NL80211_FLAG_NEED_RTNL,
Jouni Malinen636a5d32009-03-19 13:39:22 +02008662 },
8663 {
8664 .cmd = NL80211_CMD_ASSOCIATE,
8665 .doit = nl80211_associate,
8666 .policy = nl80211_policy,
8667 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02008668 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02008669 NL80211_FLAG_NEED_RTNL,
Jouni Malinen636a5d32009-03-19 13:39:22 +02008670 },
8671 {
8672 .cmd = NL80211_CMD_DEAUTHENTICATE,
8673 .doit = nl80211_deauthenticate,
8674 .policy = nl80211_policy,
8675 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02008676 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02008677 NL80211_FLAG_NEED_RTNL,
Jouni Malinen636a5d32009-03-19 13:39:22 +02008678 },
8679 {
8680 .cmd = NL80211_CMD_DISASSOCIATE,
8681 .doit = nl80211_disassociate,
8682 .policy = nl80211_policy,
8683 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02008684 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02008685 NL80211_FLAG_NEED_RTNL,
Jouni Malinen636a5d32009-03-19 13:39:22 +02008686 },
Johannes Berg04a773a2009-04-19 21:24:32 +02008687 {
8688 .cmd = NL80211_CMD_JOIN_IBSS,
8689 .doit = nl80211_join_ibss,
8690 .policy = nl80211_policy,
8691 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02008692 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02008693 NL80211_FLAG_NEED_RTNL,
Johannes Berg04a773a2009-04-19 21:24:32 +02008694 },
8695 {
8696 .cmd = NL80211_CMD_LEAVE_IBSS,
8697 .doit = nl80211_leave_ibss,
8698 .policy = nl80211_policy,
8699 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02008700 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02008701 NL80211_FLAG_NEED_RTNL,
Johannes Berg04a773a2009-04-19 21:24:32 +02008702 },
Johannes Bergaff89a92009-07-01 21:26:51 +02008703#ifdef CONFIG_NL80211_TESTMODE
8704 {
8705 .cmd = NL80211_CMD_TESTMODE,
8706 .doit = nl80211_testmode_do,
Wey-Yi Guy71063f02011-05-20 09:05:54 -07008707 .dumpit = nl80211_testmode_dump,
Johannes Bergaff89a92009-07-01 21:26:51 +02008708 .policy = nl80211_policy,
8709 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02008710 .internal_flags = NL80211_FLAG_NEED_WIPHY |
8711 NL80211_FLAG_NEED_RTNL,
Johannes Bergaff89a92009-07-01 21:26:51 +02008712 },
8713#endif
Samuel Ortizb23aa672009-07-01 21:26:54 +02008714 {
8715 .cmd = NL80211_CMD_CONNECT,
8716 .doit = nl80211_connect,
8717 .policy = nl80211_policy,
8718 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02008719 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02008720 NL80211_FLAG_NEED_RTNL,
Samuel Ortizb23aa672009-07-01 21:26:54 +02008721 },
8722 {
8723 .cmd = NL80211_CMD_DISCONNECT,
8724 .doit = nl80211_disconnect,
8725 .policy = nl80211_policy,
8726 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02008727 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02008728 NL80211_FLAG_NEED_RTNL,
Samuel Ortizb23aa672009-07-01 21:26:54 +02008729 },
Johannes Berg463d0182009-07-14 00:33:35 +02008730 {
8731 .cmd = NL80211_CMD_SET_WIPHY_NETNS,
8732 .doit = nl80211_wiphy_netns,
8733 .policy = nl80211_policy,
8734 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02008735 .internal_flags = NL80211_FLAG_NEED_WIPHY |
8736 NL80211_FLAG_NEED_RTNL,
Johannes Berg463d0182009-07-14 00:33:35 +02008737 },
Holger Schurig61fa7132009-11-11 12:25:40 +01008738 {
8739 .cmd = NL80211_CMD_GET_SURVEY,
8740 .policy = nl80211_policy,
8741 .dumpit = nl80211_dump_survey,
8742 },
Samuel Ortiz67fbb162009-11-24 23:59:15 +01008743 {
8744 .cmd = NL80211_CMD_SET_PMKSA,
8745 .doit = nl80211_setdel_pmksa,
8746 .policy = nl80211_policy,
8747 .flags = GENL_ADMIN_PERM,
Johannes Berg2b5f8b02012-04-02 10:51:55 +02008748 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02008749 NL80211_FLAG_NEED_RTNL,
Samuel Ortiz67fbb162009-11-24 23:59:15 +01008750 },
8751 {
8752 .cmd = NL80211_CMD_DEL_PMKSA,
8753 .doit = nl80211_setdel_pmksa,
8754 .policy = nl80211_policy,
8755 .flags = GENL_ADMIN_PERM,
Johannes Berg2b5f8b02012-04-02 10:51:55 +02008756 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02008757 NL80211_FLAG_NEED_RTNL,
Samuel Ortiz67fbb162009-11-24 23:59:15 +01008758 },
8759 {
8760 .cmd = NL80211_CMD_FLUSH_PMKSA,
8761 .doit = nl80211_flush_pmksa,
8762 .policy = nl80211_policy,
8763 .flags = GENL_ADMIN_PERM,
Johannes Berg2b5f8b02012-04-02 10:51:55 +02008764 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02008765 NL80211_FLAG_NEED_RTNL,
Samuel Ortiz67fbb162009-11-24 23:59:15 +01008766 },
Jouni Malinen9588bbd2009-12-23 13:15:41 +01008767 {
8768 .cmd = NL80211_CMD_REMAIN_ON_CHANNEL,
8769 .doit = nl80211_remain_on_channel,
8770 .policy = nl80211_policy,
8771 .flags = GENL_ADMIN_PERM,
Johannes Berg71bbc992012-06-15 15:30:18 +02008772 .internal_flags = NL80211_FLAG_NEED_WDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02008773 NL80211_FLAG_NEED_RTNL,
Jouni Malinen9588bbd2009-12-23 13:15:41 +01008774 },
8775 {
8776 .cmd = NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL,
8777 .doit = nl80211_cancel_remain_on_channel,
8778 .policy = nl80211_policy,
8779 .flags = GENL_ADMIN_PERM,
Johannes Berg71bbc992012-06-15 15:30:18 +02008780 .internal_flags = NL80211_FLAG_NEED_WDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02008781 NL80211_FLAG_NEED_RTNL,
Jouni Malinen9588bbd2009-12-23 13:15:41 +01008782 },
Jouni Malinen13ae75b2009-12-29 12:59:45 +02008783 {
8784 .cmd = NL80211_CMD_SET_TX_BITRATE_MASK,
8785 .doit = nl80211_set_tx_bitrate_mask,
8786 .policy = nl80211_policy,
8787 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02008788 .internal_flags = NL80211_FLAG_NEED_NETDEV |
8789 NL80211_FLAG_NEED_RTNL,
Jouni Malinen13ae75b2009-12-29 12:59:45 +02008790 },
Jouni Malinen026331c2010-02-15 12:53:10 +02008791 {
Johannes Berg2e161f72010-08-12 15:38:38 +02008792 .cmd = NL80211_CMD_REGISTER_FRAME,
8793 .doit = nl80211_register_mgmt,
Jouni Malinen026331c2010-02-15 12:53:10 +02008794 .policy = nl80211_policy,
8795 .flags = GENL_ADMIN_PERM,
Johannes Berg71bbc992012-06-15 15:30:18 +02008796 .internal_flags = NL80211_FLAG_NEED_WDEV |
Johannes Berg4c476992010-10-04 21:36:35 +02008797 NL80211_FLAG_NEED_RTNL,
Jouni Malinen026331c2010-02-15 12:53:10 +02008798 },
8799 {
Johannes Berg2e161f72010-08-12 15:38:38 +02008800 .cmd = NL80211_CMD_FRAME,
8801 .doit = nl80211_tx_mgmt,
Jouni Malinen026331c2010-02-15 12:53:10 +02008802 .policy = nl80211_policy,
8803 .flags = GENL_ADMIN_PERM,
Johannes Berg71bbc992012-06-15 15:30:18 +02008804 .internal_flags = NL80211_FLAG_NEED_WDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02008805 NL80211_FLAG_NEED_RTNL,
Jouni Malinen026331c2010-02-15 12:53:10 +02008806 },
Kalle Valoffb9eb32010-02-17 17:58:10 +02008807 {
Johannes Bergf7ca38d2010-11-25 10:02:29 +01008808 .cmd = NL80211_CMD_FRAME_WAIT_CANCEL,
8809 .doit = nl80211_tx_mgmt_cancel_wait,
8810 .policy = nl80211_policy,
8811 .flags = GENL_ADMIN_PERM,
Johannes Berg71bbc992012-06-15 15:30:18 +02008812 .internal_flags = NL80211_FLAG_NEED_WDEV_UP |
Johannes Bergf7ca38d2010-11-25 10:02:29 +01008813 NL80211_FLAG_NEED_RTNL,
8814 },
8815 {
Kalle Valoffb9eb32010-02-17 17:58:10 +02008816 .cmd = NL80211_CMD_SET_POWER_SAVE,
8817 .doit = nl80211_set_power_save,
8818 .policy = nl80211_policy,
8819 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02008820 .internal_flags = NL80211_FLAG_NEED_NETDEV |
8821 NL80211_FLAG_NEED_RTNL,
Kalle Valoffb9eb32010-02-17 17:58:10 +02008822 },
8823 {
8824 .cmd = NL80211_CMD_GET_POWER_SAVE,
8825 .doit = nl80211_get_power_save,
8826 .policy = nl80211_policy,
8827 /* can be retrieved by unprivileged users */
Johannes Berg4c476992010-10-04 21:36:35 +02008828 .internal_flags = NL80211_FLAG_NEED_NETDEV |
8829 NL80211_FLAG_NEED_RTNL,
Kalle Valoffb9eb32010-02-17 17:58:10 +02008830 },
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02008831 {
8832 .cmd = NL80211_CMD_SET_CQM,
8833 .doit = nl80211_set_cqm,
8834 .policy = nl80211_policy,
8835 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02008836 .internal_flags = NL80211_FLAG_NEED_NETDEV |
8837 NL80211_FLAG_NEED_RTNL,
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02008838 },
Johannes Bergf444de02010-05-05 15:25:02 +02008839 {
8840 .cmd = NL80211_CMD_SET_CHANNEL,
8841 .doit = nl80211_set_channel,
8842 .policy = nl80211_policy,
8843 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02008844 .internal_flags = NL80211_FLAG_NEED_NETDEV |
8845 NL80211_FLAG_NEED_RTNL,
Johannes Bergf444de02010-05-05 15:25:02 +02008846 },
Bill Jordane8347eb2010-10-01 13:54:28 -04008847 {
8848 .cmd = NL80211_CMD_SET_WDS_PEER,
8849 .doit = nl80211_set_wds_peer,
8850 .policy = nl80211_policy,
8851 .flags = GENL_ADMIN_PERM,
Johannes Berg43b19952010-10-07 13:10:30 +02008852 .internal_flags = NL80211_FLAG_NEED_NETDEV |
8853 NL80211_FLAG_NEED_RTNL,
Bill Jordane8347eb2010-10-01 13:54:28 -04008854 },
Johannes Berg29cbe682010-12-03 09:20:44 +01008855 {
8856 .cmd = NL80211_CMD_JOIN_MESH,
8857 .doit = nl80211_join_mesh,
8858 .policy = nl80211_policy,
8859 .flags = GENL_ADMIN_PERM,
8860 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
8861 NL80211_FLAG_NEED_RTNL,
8862 },
8863 {
8864 .cmd = NL80211_CMD_LEAVE_MESH,
8865 .doit = nl80211_leave_mesh,
8866 .policy = nl80211_policy,
8867 .flags = GENL_ADMIN_PERM,
8868 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
8869 NL80211_FLAG_NEED_RTNL,
8870 },
Johannes Bergdfb89c52012-06-27 09:23:48 +02008871#ifdef CONFIG_PM
Johannes Bergff1b6e62011-05-04 15:37:28 +02008872 {
8873 .cmd = NL80211_CMD_GET_WOWLAN,
8874 .doit = nl80211_get_wowlan,
8875 .policy = nl80211_policy,
8876 /* can be retrieved by unprivileged users */
8877 .internal_flags = NL80211_FLAG_NEED_WIPHY |
8878 NL80211_FLAG_NEED_RTNL,
8879 },
8880 {
8881 .cmd = NL80211_CMD_SET_WOWLAN,
8882 .doit = nl80211_set_wowlan,
8883 .policy = nl80211_policy,
8884 .flags = GENL_ADMIN_PERM,
8885 .internal_flags = NL80211_FLAG_NEED_WIPHY |
8886 NL80211_FLAG_NEED_RTNL,
8887 },
Johannes Bergdfb89c52012-06-27 09:23:48 +02008888#endif
Johannes Berge5497d72011-07-05 16:35:40 +02008889 {
8890 .cmd = NL80211_CMD_SET_REKEY_OFFLOAD,
8891 .doit = nl80211_set_rekey_data,
8892 .policy = nl80211_policy,
8893 .flags = GENL_ADMIN_PERM,
8894 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
8895 NL80211_FLAG_NEED_RTNL,
8896 },
Arik Nemtsov109086c2011-09-28 14:12:50 +03008897 {
8898 .cmd = NL80211_CMD_TDLS_MGMT,
8899 .doit = nl80211_tdls_mgmt,
8900 .policy = nl80211_policy,
8901 .flags = GENL_ADMIN_PERM,
8902 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
8903 NL80211_FLAG_NEED_RTNL,
8904 },
8905 {
8906 .cmd = NL80211_CMD_TDLS_OPER,
8907 .doit = nl80211_tdls_oper,
8908 .policy = nl80211_policy,
8909 .flags = GENL_ADMIN_PERM,
8910 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
8911 NL80211_FLAG_NEED_RTNL,
8912 },
Johannes Berg28946da2011-11-04 11:18:12 +01008913 {
8914 .cmd = NL80211_CMD_UNEXPECTED_FRAME,
8915 .doit = nl80211_register_unexpected_frame,
8916 .policy = nl80211_policy,
8917 .flags = GENL_ADMIN_PERM,
8918 .internal_flags = NL80211_FLAG_NEED_NETDEV |
8919 NL80211_FLAG_NEED_RTNL,
8920 },
Johannes Berg7f6cf312011-11-04 11:18:15 +01008921 {
8922 .cmd = NL80211_CMD_PROBE_CLIENT,
8923 .doit = nl80211_probe_client,
8924 .policy = nl80211_policy,
8925 .flags = GENL_ADMIN_PERM,
Johannes Berg2b5f8b02012-04-02 10:51:55 +02008926 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg7f6cf312011-11-04 11:18:15 +01008927 NL80211_FLAG_NEED_RTNL,
8928 },
Johannes Berg5e760232011-11-04 11:18:17 +01008929 {
8930 .cmd = NL80211_CMD_REGISTER_BEACONS,
8931 .doit = nl80211_register_beacons,
8932 .policy = nl80211_policy,
8933 .flags = GENL_ADMIN_PERM,
8934 .internal_flags = NL80211_FLAG_NEED_WIPHY |
8935 NL80211_FLAG_NEED_RTNL,
8936 },
Simon Wunderlich1d9d9212011-11-18 14:20:43 +01008937 {
8938 .cmd = NL80211_CMD_SET_NOACK_MAP,
8939 .doit = nl80211_set_noack_map,
8940 .policy = nl80211_policy,
8941 .flags = GENL_ADMIN_PERM,
8942 .internal_flags = NL80211_FLAG_NEED_NETDEV |
8943 NL80211_FLAG_NEED_RTNL,
8944 },
Johannes Berg98104fde2012-06-16 00:19:54 +02008945 {
8946 .cmd = NL80211_CMD_START_P2P_DEVICE,
8947 .doit = nl80211_start_p2p_device,
8948 .policy = nl80211_policy,
8949 .flags = GENL_ADMIN_PERM,
8950 .internal_flags = NL80211_FLAG_NEED_WDEV |
8951 NL80211_FLAG_NEED_RTNL,
8952 },
8953 {
8954 .cmd = NL80211_CMD_STOP_P2P_DEVICE,
8955 .doit = nl80211_stop_p2p_device,
8956 .policy = nl80211_policy,
8957 .flags = GENL_ADMIN_PERM,
8958 .internal_flags = NL80211_FLAG_NEED_WDEV_UP |
8959 NL80211_FLAG_NEED_RTNL,
8960 },
Antonio Quartullif4e583c2012-11-02 13:27:48 +01008961 {
8962 .cmd = NL80211_CMD_SET_MCAST_RATE,
8963 .doit = nl80211_set_mcast_rate,
8964 .policy = nl80211_policy,
8965 .flags = GENL_ADMIN_PERM,
8966 .internal_flags = NL80211_FLAG_NEED_NETDEV |
8967 NL80211_FLAG_NEED_RTNL,
8968 },
Vasanthakumar Thiagarajan77765ea2013-01-18 11:18:45 +05308969 {
8970 .cmd = NL80211_CMD_SET_MAC_ACL,
8971 .doit = nl80211_set_mac_acl,
8972 .policy = nl80211_policy,
8973 .flags = GENL_ADMIN_PERM,
8974 .internal_flags = NL80211_FLAG_NEED_NETDEV |
8975 NL80211_FLAG_NEED_RTNL,
8976 },
Simon Wunderlich04f39042013-02-08 18:16:19 +01008977 {
8978 .cmd = NL80211_CMD_RADAR_DETECT,
8979 .doit = nl80211_start_radar_detection,
8980 .policy = nl80211_policy,
8981 .flags = GENL_ADMIN_PERM,
8982 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
8983 NL80211_FLAG_NEED_RTNL,
8984 },
Johannes Berg3713b4e2013-02-14 16:19:38 +01008985 {
8986 .cmd = NL80211_CMD_GET_PROTOCOL_FEATURES,
8987 .doit = nl80211_get_protocol_features,
8988 .policy = nl80211_policy,
8989 },
Jouni Malinen355199e2013-02-27 17:14:27 +02008990 {
8991 .cmd = NL80211_CMD_UPDATE_FT_IES,
8992 .doit = nl80211_update_ft_ies,
8993 .policy = nl80211_policy,
8994 .flags = GENL_ADMIN_PERM,
8995 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
8996 NL80211_FLAG_NEED_RTNL,
8997 },
Arend van Spriel5de17982013-04-18 15:49:00 +02008998 {
8999 .cmd = NL80211_CMD_CRIT_PROTOCOL_START,
9000 .doit = nl80211_crit_protocol_start,
9001 .policy = nl80211_policy,
9002 .flags = GENL_ADMIN_PERM,
9003 .internal_flags = NL80211_FLAG_NEED_WDEV_UP |
9004 NL80211_FLAG_NEED_RTNL,
9005 },
9006 {
9007 .cmd = NL80211_CMD_CRIT_PROTOCOL_STOP,
9008 .doit = nl80211_crit_protocol_stop,
9009 .policy = nl80211_policy,
9010 .flags = GENL_ADMIN_PERM,
9011 .internal_flags = NL80211_FLAG_NEED_WDEV_UP |
9012 NL80211_FLAG_NEED_RTNL,
9013 }
Johannes Berg55682962007-09-20 13:09:35 -04009014};
Jouni Malinen9588bbd2009-12-23 13:15:41 +01009015
Jouni Malinen6039f6d2009-03-19 13:39:21 +02009016static struct genl_multicast_group nl80211_mlme_mcgrp = {
9017 .name = "mlme",
9018};
Johannes Berg55682962007-09-20 13:09:35 -04009019
9020/* multicast groups */
9021static struct genl_multicast_group nl80211_config_mcgrp = {
9022 .name = "config",
9023};
Johannes Berg2a519312009-02-10 21:25:55 +01009024static struct genl_multicast_group nl80211_scan_mcgrp = {
9025 .name = "scan",
9026};
Luis R. Rodriguez73d54c92009-03-09 22:07:42 -04009027static struct genl_multicast_group nl80211_regulatory_mcgrp = {
9028 .name = "regulatory",
9029};
Johannes Berg55682962007-09-20 13:09:35 -04009030
9031/* notification functions */
9032
9033void nl80211_notify_dev_rename(struct cfg80211_registered_device *rdev)
9034{
9035 struct sk_buff *msg;
9036
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07009037 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg55682962007-09-20 13:09:35 -04009038 if (!msg)
9039 return;
9040
Johannes Berg3713b4e2013-02-14 16:19:38 +01009041 if (nl80211_send_wiphy(rdev, msg, 0, 0, 0,
9042 false, NULL, NULL, NULL) < 0) {
Johannes Berg55682962007-09-20 13:09:35 -04009043 nlmsg_free(msg);
9044 return;
9045 }
9046
Johannes Berg463d0182009-07-14 00:33:35 +02009047 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
9048 nl80211_config_mcgrp.id, GFP_KERNEL);
Johannes Berg55682962007-09-20 13:09:35 -04009049}
9050
Johannes Berg362a4152009-05-24 16:43:15 +02009051static int nl80211_add_scan_req(struct sk_buff *msg,
9052 struct cfg80211_registered_device *rdev)
9053{
9054 struct cfg80211_scan_request *req = rdev->scan_req;
9055 struct nlattr *nest;
9056 int i;
9057
Johannes Bergf9f47522013-03-19 15:04:07 +01009058 lockdep_assert_held(&rdev->sched_scan_mtx);
Johannes Berg667503d2009-07-07 03:56:11 +02009059
Johannes Berg362a4152009-05-24 16:43:15 +02009060 if (WARN_ON(!req))
9061 return 0;
9062
9063 nest = nla_nest_start(msg, NL80211_ATTR_SCAN_SSIDS);
9064 if (!nest)
9065 goto nla_put_failure;
David S. Miller9360ffd2012-03-29 04:41:26 -04009066 for (i = 0; i < req->n_ssids; i++) {
9067 if (nla_put(msg, i, req->ssids[i].ssid_len, req->ssids[i].ssid))
9068 goto nla_put_failure;
9069 }
Johannes Berg362a4152009-05-24 16:43:15 +02009070 nla_nest_end(msg, nest);
9071
9072 nest = nla_nest_start(msg, NL80211_ATTR_SCAN_FREQUENCIES);
9073 if (!nest)
9074 goto nla_put_failure;
David S. Miller9360ffd2012-03-29 04:41:26 -04009075 for (i = 0; i < req->n_channels; i++) {
9076 if (nla_put_u32(msg, i, req->channels[i]->center_freq))
9077 goto nla_put_failure;
9078 }
Johannes Berg362a4152009-05-24 16:43:15 +02009079 nla_nest_end(msg, nest);
9080
David S. Miller9360ffd2012-03-29 04:41:26 -04009081 if (req->ie &&
9082 nla_put(msg, NL80211_ATTR_IE, req->ie_len, req->ie))
9083 goto nla_put_failure;
Johannes Berg362a4152009-05-24 16:43:15 +02009084
Sam Lefflered4737712012-10-11 21:03:31 -07009085 if (req->flags)
9086 nla_put_u32(msg, NL80211_ATTR_SCAN_FLAGS, req->flags);
9087
Johannes Berg362a4152009-05-24 16:43:15 +02009088 return 0;
9089 nla_put_failure:
9090 return -ENOBUFS;
9091}
9092
Johannes Berga538e2d2009-06-16 19:56:42 +02009093static int nl80211_send_scan_msg(struct sk_buff *msg,
9094 struct cfg80211_registered_device *rdev,
Johannes Bergfd014282012-06-18 19:17:03 +02009095 struct wireless_dev *wdev,
Eric W. Biederman15e47302012-09-07 20:12:54 +00009096 u32 portid, u32 seq, int flags,
Johannes Berga538e2d2009-06-16 19:56:42 +02009097 u32 cmd)
Johannes Berg2a519312009-02-10 21:25:55 +01009098{
9099 void *hdr;
9100
Eric W. Biederman15e47302012-09-07 20:12:54 +00009101 hdr = nl80211hdr_put(msg, portid, seq, flags, cmd);
Johannes Berg2a519312009-02-10 21:25:55 +01009102 if (!hdr)
9103 return -1;
9104
David S. Miller9360ffd2012-03-29 04:41:26 -04009105 if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
Johannes Bergfd014282012-06-18 19:17:03 +02009106 (wdev->netdev && nla_put_u32(msg, NL80211_ATTR_IFINDEX,
9107 wdev->netdev->ifindex)) ||
9108 nla_put_u64(msg, NL80211_ATTR_WDEV, wdev_id(wdev)))
David S. Miller9360ffd2012-03-29 04:41:26 -04009109 goto nla_put_failure;
Johannes Berg2a519312009-02-10 21:25:55 +01009110
Johannes Berg362a4152009-05-24 16:43:15 +02009111 /* ignore errors and send incomplete event anyway */
9112 nl80211_add_scan_req(msg, rdev);
Johannes Berg2a519312009-02-10 21:25:55 +01009113
9114 return genlmsg_end(msg, hdr);
9115
9116 nla_put_failure:
9117 genlmsg_cancel(msg, hdr);
9118 return -EMSGSIZE;
9119}
9120
Luciano Coelho807f8a82011-05-11 17:09:35 +03009121static int
9122nl80211_send_sched_scan_msg(struct sk_buff *msg,
9123 struct cfg80211_registered_device *rdev,
9124 struct net_device *netdev,
Eric W. Biederman15e47302012-09-07 20:12:54 +00009125 u32 portid, u32 seq, int flags, u32 cmd)
Luciano Coelho807f8a82011-05-11 17:09:35 +03009126{
9127 void *hdr;
9128
Eric W. Biederman15e47302012-09-07 20:12:54 +00009129 hdr = nl80211hdr_put(msg, portid, seq, flags, cmd);
Luciano Coelho807f8a82011-05-11 17:09:35 +03009130 if (!hdr)
9131 return -1;
9132
David S. Miller9360ffd2012-03-29 04:41:26 -04009133 if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
9134 nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex))
9135 goto nla_put_failure;
Luciano Coelho807f8a82011-05-11 17:09:35 +03009136
9137 return genlmsg_end(msg, hdr);
9138
9139 nla_put_failure:
9140 genlmsg_cancel(msg, hdr);
9141 return -EMSGSIZE;
9142}
9143
Johannes Berga538e2d2009-06-16 19:56:42 +02009144void nl80211_send_scan_start(struct cfg80211_registered_device *rdev,
Johannes Bergfd014282012-06-18 19:17:03 +02009145 struct wireless_dev *wdev)
Johannes Berga538e2d2009-06-16 19:56:42 +02009146{
9147 struct sk_buff *msg;
9148
Thomas Graf58050fc2012-06-28 03:57:45 +00009149 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berga538e2d2009-06-16 19:56:42 +02009150 if (!msg)
9151 return;
9152
Johannes Bergfd014282012-06-18 19:17:03 +02009153 if (nl80211_send_scan_msg(msg, rdev, wdev, 0, 0, 0,
Johannes Berga538e2d2009-06-16 19:56:42 +02009154 NL80211_CMD_TRIGGER_SCAN) < 0) {
9155 nlmsg_free(msg);
9156 return;
9157 }
9158
Johannes Berg463d0182009-07-14 00:33:35 +02009159 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
9160 nl80211_scan_mcgrp.id, GFP_KERNEL);
Johannes Berga538e2d2009-06-16 19:56:42 +02009161}
9162
Johannes Berg2a519312009-02-10 21:25:55 +01009163void nl80211_send_scan_done(struct cfg80211_registered_device *rdev,
Johannes Bergfd014282012-06-18 19:17:03 +02009164 struct wireless_dev *wdev)
Johannes Berg2a519312009-02-10 21:25:55 +01009165{
9166 struct sk_buff *msg;
9167
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07009168 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg2a519312009-02-10 21:25:55 +01009169 if (!msg)
9170 return;
9171
Johannes Bergfd014282012-06-18 19:17:03 +02009172 if (nl80211_send_scan_msg(msg, rdev, wdev, 0, 0, 0,
Johannes Berga538e2d2009-06-16 19:56:42 +02009173 NL80211_CMD_NEW_SCAN_RESULTS) < 0) {
Johannes Berg2a519312009-02-10 21:25:55 +01009174 nlmsg_free(msg);
9175 return;
9176 }
9177
Johannes Berg463d0182009-07-14 00:33:35 +02009178 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
9179 nl80211_scan_mcgrp.id, GFP_KERNEL);
Johannes Berg2a519312009-02-10 21:25:55 +01009180}
9181
9182void nl80211_send_scan_aborted(struct cfg80211_registered_device *rdev,
Johannes Bergfd014282012-06-18 19:17:03 +02009183 struct wireless_dev *wdev)
Johannes Berg2a519312009-02-10 21:25:55 +01009184{
9185 struct sk_buff *msg;
9186
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07009187 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg2a519312009-02-10 21:25:55 +01009188 if (!msg)
9189 return;
9190
Johannes Bergfd014282012-06-18 19:17:03 +02009191 if (nl80211_send_scan_msg(msg, rdev, wdev, 0, 0, 0,
Johannes Berga538e2d2009-06-16 19:56:42 +02009192 NL80211_CMD_SCAN_ABORTED) < 0) {
Johannes Berg2a519312009-02-10 21:25:55 +01009193 nlmsg_free(msg);
9194 return;
9195 }
9196
Johannes Berg463d0182009-07-14 00:33:35 +02009197 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
9198 nl80211_scan_mcgrp.id, GFP_KERNEL);
Johannes Berg2a519312009-02-10 21:25:55 +01009199}
9200
Luciano Coelho807f8a82011-05-11 17:09:35 +03009201void nl80211_send_sched_scan_results(struct cfg80211_registered_device *rdev,
9202 struct net_device *netdev)
9203{
9204 struct sk_buff *msg;
9205
9206 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
9207 if (!msg)
9208 return;
9209
9210 if (nl80211_send_sched_scan_msg(msg, rdev, netdev, 0, 0, 0,
9211 NL80211_CMD_SCHED_SCAN_RESULTS) < 0) {
9212 nlmsg_free(msg);
9213 return;
9214 }
9215
9216 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
9217 nl80211_scan_mcgrp.id, GFP_KERNEL);
9218}
9219
9220void nl80211_send_sched_scan(struct cfg80211_registered_device *rdev,
9221 struct net_device *netdev, u32 cmd)
9222{
9223 struct sk_buff *msg;
9224
Thomas Graf58050fc2012-06-28 03:57:45 +00009225 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Luciano Coelho807f8a82011-05-11 17:09:35 +03009226 if (!msg)
9227 return;
9228
9229 if (nl80211_send_sched_scan_msg(msg, rdev, netdev, 0, 0, 0, cmd) < 0) {
9230 nlmsg_free(msg);
9231 return;
9232 }
9233
9234 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
9235 nl80211_scan_mcgrp.id, GFP_KERNEL);
9236}
9237
Luis R. Rodriguez73d54c92009-03-09 22:07:42 -04009238/*
9239 * This can happen on global regulatory changes or device specific settings
9240 * based on custom world regulatory domains.
9241 */
9242void nl80211_send_reg_change_event(struct regulatory_request *request)
9243{
9244 struct sk_buff *msg;
9245 void *hdr;
9246
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07009247 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Luis R. Rodriguez73d54c92009-03-09 22:07:42 -04009248 if (!msg)
9249 return;
9250
9251 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_REG_CHANGE);
9252 if (!hdr) {
9253 nlmsg_free(msg);
9254 return;
9255 }
9256
9257 /* Userspace can always count this one always being set */
David S. Miller9360ffd2012-03-29 04:41:26 -04009258 if (nla_put_u8(msg, NL80211_ATTR_REG_INITIATOR, request->initiator))
9259 goto nla_put_failure;
Luis R. Rodriguez73d54c92009-03-09 22:07:42 -04009260
David S. Miller9360ffd2012-03-29 04:41:26 -04009261 if (request->alpha2[0] == '0' && request->alpha2[1] == '0') {
9262 if (nla_put_u8(msg, NL80211_ATTR_REG_TYPE,
9263 NL80211_REGDOM_TYPE_WORLD))
9264 goto nla_put_failure;
9265 } else if (request->alpha2[0] == '9' && request->alpha2[1] == '9') {
9266 if (nla_put_u8(msg, NL80211_ATTR_REG_TYPE,
9267 NL80211_REGDOM_TYPE_CUSTOM_WORLD))
9268 goto nla_put_failure;
9269 } else if ((request->alpha2[0] == '9' && request->alpha2[1] == '8') ||
9270 request->intersect) {
9271 if (nla_put_u8(msg, NL80211_ATTR_REG_TYPE,
9272 NL80211_REGDOM_TYPE_INTERSECTION))
9273 goto nla_put_failure;
9274 } else {
9275 if (nla_put_u8(msg, NL80211_ATTR_REG_TYPE,
9276 NL80211_REGDOM_TYPE_COUNTRY) ||
9277 nla_put_string(msg, NL80211_ATTR_REG_ALPHA2,
9278 request->alpha2))
9279 goto nla_put_failure;
Luis R. Rodriguez73d54c92009-03-09 22:07:42 -04009280 }
9281
Johannes Bergf4173762012-12-03 18:23:37 +01009282 if (request->wiphy_idx != WIPHY_IDX_INVALID &&
David S. Miller9360ffd2012-03-29 04:41:26 -04009283 nla_put_u32(msg, NL80211_ATTR_WIPHY, request->wiphy_idx))
9284 goto nla_put_failure;
Luis R. Rodriguez73d54c92009-03-09 22:07:42 -04009285
Johannes Berg3b7b72e2011-10-22 19:05:51 +02009286 genlmsg_end(msg, hdr);
Luis R. Rodriguez73d54c92009-03-09 22:07:42 -04009287
Johannes Bergbc43b282009-07-25 10:54:13 +02009288 rcu_read_lock();
Johannes Berg463d0182009-07-14 00:33:35 +02009289 genlmsg_multicast_allns(msg, 0, nl80211_regulatory_mcgrp.id,
Johannes Bergbc43b282009-07-25 10:54:13 +02009290 GFP_ATOMIC);
9291 rcu_read_unlock();
Luis R. Rodriguez73d54c92009-03-09 22:07:42 -04009292
9293 return;
9294
9295nla_put_failure:
9296 genlmsg_cancel(msg, hdr);
9297 nlmsg_free(msg);
9298}
9299
Jouni Malinen6039f6d2009-03-19 13:39:21 +02009300static void nl80211_send_mlme_event(struct cfg80211_registered_device *rdev,
9301 struct net_device *netdev,
9302 const u8 *buf, size_t len,
Johannes Berge6d6e342009-07-01 21:26:47 +02009303 enum nl80211_commands cmd, gfp_t gfp)
Jouni Malinen6039f6d2009-03-19 13:39:21 +02009304{
9305 struct sk_buff *msg;
9306 void *hdr;
9307
Johannes Berge6d6e342009-07-01 21:26:47 +02009308 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
Jouni Malinen6039f6d2009-03-19 13:39:21 +02009309 if (!msg)
9310 return;
9311
9312 hdr = nl80211hdr_put(msg, 0, 0, 0, cmd);
9313 if (!hdr) {
9314 nlmsg_free(msg);
9315 return;
9316 }
9317
David S. Miller9360ffd2012-03-29 04:41:26 -04009318 if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
9319 nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) ||
9320 nla_put(msg, NL80211_ATTR_FRAME, len, buf))
9321 goto nla_put_failure;
Jouni Malinen6039f6d2009-03-19 13:39:21 +02009322
Johannes Berg3b7b72e2011-10-22 19:05:51 +02009323 genlmsg_end(msg, hdr);
Jouni Malinen6039f6d2009-03-19 13:39:21 +02009324
Johannes Berg463d0182009-07-14 00:33:35 +02009325 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
9326 nl80211_mlme_mcgrp.id, gfp);
Jouni Malinen6039f6d2009-03-19 13:39:21 +02009327 return;
9328
9329 nla_put_failure:
9330 genlmsg_cancel(msg, hdr);
9331 nlmsg_free(msg);
9332}
9333
9334void nl80211_send_rx_auth(struct cfg80211_registered_device *rdev,
Johannes Berge6d6e342009-07-01 21:26:47 +02009335 struct net_device *netdev, const u8 *buf,
9336 size_t len, gfp_t gfp)
Jouni Malinen6039f6d2009-03-19 13:39:21 +02009337{
9338 nl80211_send_mlme_event(rdev, netdev, buf, len,
Johannes Berge6d6e342009-07-01 21:26:47 +02009339 NL80211_CMD_AUTHENTICATE, gfp);
Jouni Malinen6039f6d2009-03-19 13:39:21 +02009340}
9341
9342void nl80211_send_rx_assoc(struct cfg80211_registered_device *rdev,
9343 struct net_device *netdev, const u8 *buf,
Johannes Berge6d6e342009-07-01 21:26:47 +02009344 size_t len, gfp_t gfp)
Jouni Malinen6039f6d2009-03-19 13:39:21 +02009345{
Johannes Berge6d6e342009-07-01 21:26:47 +02009346 nl80211_send_mlme_event(rdev, netdev, buf, len,
9347 NL80211_CMD_ASSOCIATE, gfp);
Jouni Malinen6039f6d2009-03-19 13:39:21 +02009348}
9349
Jouni Malinen53b46b82009-03-27 20:53:56 +02009350void nl80211_send_deauth(struct cfg80211_registered_device *rdev,
Johannes Berge6d6e342009-07-01 21:26:47 +02009351 struct net_device *netdev, const u8 *buf,
9352 size_t len, gfp_t gfp)
Jouni Malinen6039f6d2009-03-19 13:39:21 +02009353{
9354 nl80211_send_mlme_event(rdev, netdev, buf, len,
Johannes Berge6d6e342009-07-01 21:26:47 +02009355 NL80211_CMD_DEAUTHENTICATE, gfp);
Jouni Malinen6039f6d2009-03-19 13:39:21 +02009356}
9357
Jouni Malinen53b46b82009-03-27 20:53:56 +02009358void nl80211_send_disassoc(struct cfg80211_registered_device *rdev,
9359 struct net_device *netdev, const u8 *buf,
Johannes Berge6d6e342009-07-01 21:26:47 +02009360 size_t len, gfp_t gfp)
Jouni Malinen6039f6d2009-03-19 13:39:21 +02009361{
9362 nl80211_send_mlme_event(rdev, netdev, buf, len,
Johannes Berge6d6e342009-07-01 21:26:47 +02009363 NL80211_CMD_DISASSOCIATE, gfp);
Jouni Malinen6039f6d2009-03-19 13:39:21 +02009364}
9365
Johannes Berg947add32013-02-22 22:05:20 +01009366void cfg80211_send_unprot_deauth(struct net_device *dev, const u8 *buf,
9367 size_t len)
Jouni Malinencf4e5942010-12-16 00:52:40 +02009368{
Johannes Berg947add32013-02-22 22:05:20 +01009369 struct wireless_dev *wdev = dev->ieee80211_ptr;
9370 struct wiphy *wiphy = wdev->wiphy;
9371 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
Jouni Malinencf4e5942010-12-16 00:52:40 +02009372
Johannes Berg947add32013-02-22 22:05:20 +01009373 trace_cfg80211_send_unprot_deauth(dev);
9374 nl80211_send_mlme_event(rdev, dev, buf, len,
9375 NL80211_CMD_UNPROT_DEAUTHENTICATE, GFP_ATOMIC);
Jouni Malinencf4e5942010-12-16 00:52:40 +02009376}
Johannes Berg947add32013-02-22 22:05:20 +01009377EXPORT_SYMBOL(cfg80211_send_unprot_deauth);
9378
9379void cfg80211_send_unprot_disassoc(struct net_device *dev, const u8 *buf,
9380 size_t len)
9381{
9382 struct wireless_dev *wdev = dev->ieee80211_ptr;
9383 struct wiphy *wiphy = wdev->wiphy;
9384 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
9385
9386 trace_cfg80211_send_unprot_disassoc(dev);
9387 nl80211_send_mlme_event(rdev, dev, buf, len,
9388 NL80211_CMD_UNPROT_DISASSOCIATE, GFP_ATOMIC);
9389}
9390EXPORT_SYMBOL(cfg80211_send_unprot_disassoc);
Jouni Malinencf4e5942010-12-16 00:52:40 +02009391
Luis R. Rodriguez1b06bb42009-05-02 00:34:48 -04009392static void nl80211_send_mlme_timeout(struct cfg80211_registered_device *rdev,
9393 struct net_device *netdev, int cmd,
Johannes Berge6d6e342009-07-01 21:26:47 +02009394 const u8 *addr, gfp_t gfp)
Jouni Malinen1965c852009-04-22 21:38:25 +03009395{
9396 struct sk_buff *msg;
9397 void *hdr;
9398
Johannes Berge6d6e342009-07-01 21:26:47 +02009399 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
Jouni Malinen1965c852009-04-22 21:38:25 +03009400 if (!msg)
9401 return;
9402
9403 hdr = nl80211hdr_put(msg, 0, 0, 0, cmd);
9404 if (!hdr) {
9405 nlmsg_free(msg);
9406 return;
9407 }
9408
David S. Miller9360ffd2012-03-29 04:41:26 -04009409 if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
9410 nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) ||
9411 nla_put_flag(msg, NL80211_ATTR_TIMED_OUT) ||
9412 nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr))
9413 goto nla_put_failure;
Jouni Malinen1965c852009-04-22 21:38:25 +03009414
Johannes Berg3b7b72e2011-10-22 19:05:51 +02009415 genlmsg_end(msg, hdr);
Jouni Malinen1965c852009-04-22 21:38:25 +03009416
Johannes Berg463d0182009-07-14 00:33:35 +02009417 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
9418 nl80211_mlme_mcgrp.id, gfp);
Jouni Malinen1965c852009-04-22 21:38:25 +03009419 return;
9420
9421 nla_put_failure:
9422 genlmsg_cancel(msg, hdr);
9423 nlmsg_free(msg);
9424}
9425
9426void nl80211_send_auth_timeout(struct cfg80211_registered_device *rdev,
Johannes Berge6d6e342009-07-01 21:26:47 +02009427 struct net_device *netdev, const u8 *addr,
9428 gfp_t gfp)
Jouni Malinen1965c852009-04-22 21:38:25 +03009429{
9430 nl80211_send_mlme_timeout(rdev, netdev, NL80211_CMD_AUTHENTICATE,
Johannes Berge6d6e342009-07-01 21:26:47 +02009431 addr, gfp);
Jouni Malinen1965c852009-04-22 21:38:25 +03009432}
9433
9434void nl80211_send_assoc_timeout(struct cfg80211_registered_device *rdev,
Johannes Berge6d6e342009-07-01 21:26:47 +02009435 struct net_device *netdev, const u8 *addr,
9436 gfp_t gfp)
Jouni Malinen1965c852009-04-22 21:38:25 +03009437{
Johannes Berge6d6e342009-07-01 21:26:47 +02009438 nl80211_send_mlme_timeout(rdev, netdev, NL80211_CMD_ASSOCIATE,
9439 addr, gfp);
Jouni Malinen1965c852009-04-22 21:38:25 +03009440}
9441
Samuel Ortizb23aa672009-07-01 21:26:54 +02009442void nl80211_send_connect_result(struct cfg80211_registered_device *rdev,
9443 struct net_device *netdev, const u8 *bssid,
9444 const u8 *req_ie, size_t req_ie_len,
9445 const u8 *resp_ie, size_t resp_ie_len,
9446 u16 status, gfp_t gfp)
9447{
9448 struct sk_buff *msg;
9449 void *hdr;
9450
Thomas Graf58050fc2012-06-28 03:57:45 +00009451 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
Samuel Ortizb23aa672009-07-01 21:26:54 +02009452 if (!msg)
9453 return;
9454
9455 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_CONNECT);
9456 if (!hdr) {
9457 nlmsg_free(msg);
9458 return;
9459 }
9460
David S. Miller9360ffd2012-03-29 04:41:26 -04009461 if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
9462 nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) ||
9463 (bssid && nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid)) ||
9464 nla_put_u16(msg, NL80211_ATTR_STATUS_CODE, status) ||
9465 (req_ie &&
9466 nla_put(msg, NL80211_ATTR_REQ_IE, req_ie_len, req_ie)) ||
9467 (resp_ie &&
9468 nla_put(msg, NL80211_ATTR_RESP_IE, resp_ie_len, resp_ie)))
9469 goto nla_put_failure;
Samuel Ortizb23aa672009-07-01 21:26:54 +02009470
Johannes Berg3b7b72e2011-10-22 19:05:51 +02009471 genlmsg_end(msg, hdr);
Samuel Ortizb23aa672009-07-01 21:26:54 +02009472
Johannes Berg463d0182009-07-14 00:33:35 +02009473 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
9474 nl80211_mlme_mcgrp.id, gfp);
Samuel Ortizb23aa672009-07-01 21:26:54 +02009475 return;
9476
9477 nla_put_failure:
9478 genlmsg_cancel(msg, hdr);
9479 nlmsg_free(msg);
9480
9481}
9482
9483void nl80211_send_roamed(struct cfg80211_registered_device *rdev,
9484 struct net_device *netdev, const u8 *bssid,
9485 const u8 *req_ie, size_t req_ie_len,
9486 const u8 *resp_ie, size_t resp_ie_len, gfp_t gfp)
9487{
9488 struct sk_buff *msg;
9489 void *hdr;
9490
Thomas Graf58050fc2012-06-28 03:57:45 +00009491 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
Samuel Ortizb23aa672009-07-01 21:26:54 +02009492 if (!msg)
9493 return;
9494
9495 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_ROAM);
9496 if (!hdr) {
9497 nlmsg_free(msg);
9498 return;
9499 }
9500
David S. Miller9360ffd2012-03-29 04:41:26 -04009501 if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
9502 nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) ||
9503 nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid) ||
9504 (req_ie &&
9505 nla_put(msg, NL80211_ATTR_REQ_IE, req_ie_len, req_ie)) ||
9506 (resp_ie &&
9507 nla_put(msg, NL80211_ATTR_RESP_IE, resp_ie_len, resp_ie)))
9508 goto nla_put_failure;
Samuel Ortizb23aa672009-07-01 21:26:54 +02009509
Johannes Berg3b7b72e2011-10-22 19:05:51 +02009510 genlmsg_end(msg, hdr);
Samuel Ortizb23aa672009-07-01 21:26:54 +02009511
Johannes Berg463d0182009-07-14 00:33:35 +02009512 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
9513 nl80211_mlme_mcgrp.id, gfp);
Samuel Ortizb23aa672009-07-01 21:26:54 +02009514 return;
9515
9516 nla_put_failure:
9517 genlmsg_cancel(msg, hdr);
9518 nlmsg_free(msg);
9519
9520}
9521
9522void nl80211_send_disconnected(struct cfg80211_registered_device *rdev,
9523 struct net_device *netdev, u16 reason,
Johannes Berg667503d2009-07-07 03:56:11 +02009524 const u8 *ie, size_t ie_len, bool from_ap)
Samuel Ortizb23aa672009-07-01 21:26:54 +02009525{
9526 struct sk_buff *msg;
9527 void *hdr;
9528
Thomas Graf58050fc2012-06-28 03:57:45 +00009529 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Samuel Ortizb23aa672009-07-01 21:26:54 +02009530 if (!msg)
9531 return;
9532
9533 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_DISCONNECT);
9534 if (!hdr) {
9535 nlmsg_free(msg);
9536 return;
9537 }
9538
David S. Miller9360ffd2012-03-29 04:41:26 -04009539 if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
9540 nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) ||
9541 (from_ap && reason &&
9542 nla_put_u16(msg, NL80211_ATTR_REASON_CODE, reason)) ||
9543 (from_ap &&
9544 nla_put_flag(msg, NL80211_ATTR_DISCONNECTED_BY_AP)) ||
9545 (ie && nla_put(msg, NL80211_ATTR_IE, ie_len, ie)))
9546 goto nla_put_failure;
Samuel Ortizb23aa672009-07-01 21:26:54 +02009547
Johannes Berg3b7b72e2011-10-22 19:05:51 +02009548 genlmsg_end(msg, hdr);
Samuel Ortizb23aa672009-07-01 21:26:54 +02009549
Johannes Berg463d0182009-07-14 00:33:35 +02009550 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
9551 nl80211_mlme_mcgrp.id, GFP_KERNEL);
Samuel Ortizb23aa672009-07-01 21:26:54 +02009552 return;
9553
9554 nla_put_failure:
9555 genlmsg_cancel(msg, hdr);
9556 nlmsg_free(msg);
9557
9558}
9559
Johannes Berg04a773a2009-04-19 21:24:32 +02009560void nl80211_send_ibss_bssid(struct cfg80211_registered_device *rdev,
9561 struct net_device *netdev, const u8 *bssid,
9562 gfp_t gfp)
9563{
9564 struct sk_buff *msg;
9565 void *hdr;
9566
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07009567 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
Johannes Berg04a773a2009-04-19 21:24:32 +02009568 if (!msg)
9569 return;
9570
9571 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_JOIN_IBSS);
9572 if (!hdr) {
9573 nlmsg_free(msg);
9574 return;
9575 }
9576
David S. Miller9360ffd2012-03-29 04:41:26 -04009577 if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
9578 nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) ||
9579 nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid))
9580 goto nla_put_failure;
Johannes Berg04a773a2009-04-19 21:24:32 +02009581
Johannes Berg3b7b72e2011-10-22 19:05:51 +02009582 genlmsg_end(msg, hdr);
Johannes Berg04a773a2009-04-19 21:24:32 +02009583
Johannes Berg463d0182009-07-14 00:33:35 +02009584 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
9585 nl80211_mlme_mcgrp.id, gfp);
Johannes Berg04a773a2009-04-19 21:24:32 +02009586 return;
9587
9588 nla_put_failure:
9589 genlmsg_cancel(msg, hdr);
9590 nlmsg_free(msg);
9591}
9592
Johannes Berg947add32013-02-22 22:05:20 +01009593void cfg80211_notify_new_peer_candidate(struct net_device *dev, const u8 *addr,
9594 const u8* ie, u8 ie_len, gfp_t gfp)
Javier Cardonac93b5e72011-04-07 15:08:34 -07009595{
Johannes Berg947add32013-02-22 22:05:20 +01009596 struct wireless_dev *wdev = dev->ieee80211_ptr;
9597 struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
Javier Cardonac93b5e72011-04-07 15:08:34 -07009598 struct sk_buff *msg;
9599 void *hdr;
9600
Johannes Berg947add32013-02-22 22:05:20 +01009601 if (WARN_ON(wdev->iftype != NL80211_IFTYPE_MESH_POINT))
9602 return;
9603
9604 trace_cfg80211_notify_new_peer_candidate(dev, addr);
9605
Javier Cardonac93b5e72011-04-07 15:08:34 -07009606 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
9607 if (!msg)
9608 return;
9609
9610 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_NEW_PEER_CANDIDATE);
9611 if (!hdr) {
9612 nlmsg_free(msg);
9613 return;
9614 }
9615
David S. Miller9360ffd2012-03-29 04:41:26 -04009616 if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
Johannes Berg947add32013-02-22 22:05:20 +01009617 nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) ||
9618 nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr) ||
David S. Miller9360ffd2012-03-29 04:41:26 -04009619 (ie_len && ie &&
9620 nla_put(msg, NL80211_ATTR_IE, ie_len , ie)))
9621 goto nla_put_failure;
Javier Cardonac93b5e72011-04-07 15:08:34 -07009622
Johannes Berg3b7b72e2011-10-22 19:05:51 +02009623 genlmsg_end(msg, hdr);
Javier Cardonac93b5e72011-04-07 15:08:34 -07009624
9625 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
9626 nl80211_mlme_mcgrp.id, gfp);
9627 return;
9628
9629 nla_put_failure:
9630 genlmsg_cancel(msg, hdr);
9631 nlmsg_free(msg);
9632}
Johannes Berg947add32013-02-22 22:05:20 +01009633EXPORT_SYMBOL(cfg80211_notify_new_peer_candidate);
Javier Cardonac93b5e72011-04-07 15:08:34 -07009634
Jouni Malinena3b8b052009-03-27 21:59:49 +02009635void nl80211_michael_mic_failure(struct cfg80211_registered_device *rdev,
9636 struct net_device *netdev, const u8 *addr,
9637 enum nl80211_key_type key_type, int key_id,
Johannes Berge6d6e342009-07-01 21:26:47 +02009638 const u8 *tsc, gfp_t gfp)
Jouni Malinena3b8b052009-03-27 21:59:49 +02009639{
9640 struct sk_buff *msg;
9641 void *hdr;
9642
Johannes Berge6d6e342009-07-01 21:26:47 +02009643 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
Jouni Malinena3b8b052009-03-27 21:59:49 +02009644 if (!msg)
9645 return;
9646
9647 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_MICHAEL_MIC_FAILURE);
9648 if (!hdr) {
9649 nlmsg_free(msg);
9650 return;
9651 }
9652
David S. Miller9360ffd2012-03-29 04:41:26 -04009653 if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
9654 nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) ||
9655 (addr && nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr)) ||
9656 nla_put_u32(msg, NL80211_ATTR_KEY_TYPE, key_type) ||
9657 (key_id != -1 &&
9658 nla_put_u8(msg, NL80211_ATTR_KEY_IDX, key_id)) ||
9659 (tsc && nla_put(msg, NL80211_ATTR_KEY_SEQ, 6, tsc)))
9660 goto nla_put_failure;
Jouni Malinena3b8b052009-03-27 21:59:49 +02009661
Johannes Berg3b7b72e2011-10-22 19:05:51 +02009662 genlmsg_end(msg, hdr);
Jouni Malinena3b8b052009-03-27 21:59:49 +02009663
Johannes Berg463d0182009-07-14 00:33:35 +02009664 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
9665 nl80211_mlme_mcgrp.id, gfp);
Jouni Malinena3b8b052009-03-27 21:59:49 +02009666 return;
9667
9668 nla_put_failure:
9669 genlmsg_cancel(msg, hdr);
9670 nlmsg_free(msg);
9671}
9672
Luis R. Rodriguez6bad8762009-04-02 14:08:09 -04009673void nl80211_send_beacon_hint_event(struct wiphy *wiphy,
9674 struct ieee80211_channel *channel_before,
9675 struct ieee80211_channel *channel_after)
9676{
9677 struct sk_buff *msg;
9678 void *hdr;
9679 struct nlattr *nl_freq;
9680
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07009681 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC);
Luis R. Rodriguez6bad8762009-04-02 14:08:09 -04009682 if (!msg)
9683 return;
9684
9685 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_REG_BEACON_HINT);
9686 if (!hdr) {
9687 nlmsg_free(msg);
9688 return;
9689 }
9690
9691 /*
9692 * Since we are applying the beacon hint to a wiphy we know its
9693 * wiphy_idx is valid
9694 */
David S. Miller9360ffd2012-03-29 04:41:26 -04009695 if (nla_put_u32(msg, NL80211_ATTR_WIPHY, get_wiphy_idx(wiphy)))
9696 goto nla_put_failure;
Luis R. Rodriguez6bad8762009-04-02 14:08:09 -04009697
9698 /* Before */
9699 nl_freq = nla_nest_start(msg, NL80211_ATTR_FREQ_BEFORE);
9700 if (!nl_freq)
9701 goto nla_put_failure;
Johannes Bergcdc89b92013-02-18 23:54:36 +01009702 if (nl80211_msg_put_channel(msg, channel_before, false))
Luis R. Rodriguez6bad8762009-04-02 14:08:09 -04009703 goto nla_put_failure;
9704 nla_nest_end(msg, nl_freq);
9705
9706 /* After */
9707 nl_freq = nla_nest_start(msg, NL80211_ATTR_FREQ_AFTER);
9708 if (!nl_freq)
9709 goto nla_put_failure;
Johannes Bergcdc89b92013-02-18 23:54:36 +01009710 if (nl80211_msg_put_channel(msg, channel_after, false))
Luis R. Rodriguez6bad8762009-04-02 14:08:09 -04009711 goto nla_put_failure;
9712 nla_nest_end(msg, nl_freq);
9713
Johannes Berg3b7b72e2011-10-22 19:05:51 +02009714 genlmsg_end(msg, hdr);
Luis R. Rodriguez6bad8762009-04-02 14:08:09 -04009715
Johannes Berg463d0182009-07-14 00:33:35 +02009716 rcu_read_lock();
9717 genlmsg_multicast_allns(msg, 0, nl80211_regulatory_mcgrp.id,
9718 GFP_ATOMIC);
9719 rcu_read_unlock();
Luis R. Rodriguez6bad8762009-04-02 14:08:09 -04009720
9721 return;
9722
9723nla_put_failure:
9724 genlmsg_cancel(msg, hdr);
9725 nlmsg_free(msg);
9726}
9727
Jouni Malinen9588bbd2009-12-23 13:15:41 +01009728static void nl80211_send_remain_on_chan_event(
9729 int cmd, struct cfg80211_registered_device *rdev,
Johannes Berg71bbc992012-06-15 15:30:18 +02009730 struct wireless_dev *wdev, u64 cookie,
Jouni Malinen9588bbd2009-12-23 13:15:41 +01009731 struct ieee80211_channel *chan,
Jouni Malinen9588bbd2009-12-23 13:15:41 +01009732 unsigned int duration, gfp_t gfp)
9733{
9734 struct sk_buff *msg;
9735 void *hdr;
9736
9737 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
9738 if (!msg)
9739 return;
9740
9741 hdr = nl80211hdr_put(msg, 0, 0, 0, cmd);
9742 if (!hdr) {
9743 nlmsg_free(msg);
9744 return;
9745 }
9746
David S. Miller9360ffd2012-03-29 04:41:26 -04009747 if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
Johannes Berg71bbc992012-06-15 15:30:18 +02009748 (wdev->netdev && nla_put_u32(msg, NL80211_ATTR_IFINDEX,
9749 wdev->netdev->ifindex)) ||
Johannes Berg00f53352012-07-17 11:53:12 +02009750 nla_put_u64(msg, NL80211_ATTR_WDEV, wdev_id(wdev)) ||
David S. Miller9360ffd2012-03-29 04:41:26 -04009751 nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, chan->center_freq) ||
Johannes Berg42d97a52012-11-08 18:31:02 +01009752 nla_put_u32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE,
9753 NL80211_CHAN_NO_HT) ||
David S. Miller9360ffd2012-03-29 04:41:26 -04009754 nla_put_u64(msg, NL80211_ATTR_COOKIE, cookie))
9755 goto nla_put_failure;
Jouni Malinen9588bbd2009-12-23 13:15:41 +01009756
David S. Miller9360ffd2012-03-29 04:41:26 -04009757 if (cmd == NL80211_CMD_REMAIN_ON_CHANNEL &&
9758 nla_put_u32(msg, NL80211_ATTR_DURATION, duration))
9759 goto nla_put_failure;
Jouni Malinen9588bbd2009-12-23 13:15:41 +01009760
Johannes Berg3b7b72e2011-10-22 19:05:51 +02009761 genlmsg_end(msg, hdr);
Jouni Malinen9588bbd2009-12-23 13:15:41 +01009762
9763 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
9764 nl80211_mlme_mcgrp.id, gfp);
9765 return;
9766
9767 nla_put_failure:
9768 genlmsg_cancel(msg, hdr);
9769 nlmsg_free(msg);
9770}
9771
Johannes Berg947add32013-02-22 22:05:20 +01009772void cfg80211_ready_on_channel(struct wireless_dev *wdev, u64 cookie,
9773 struct ieee80211_channel *chan,
9774 unsigned int duration, gfp_t gfp)
Jouni Malinen9588bbd2009-12-23 13:15:41 +01009775{
Johannes Berg947add32013-02-22 22:05:20 +01009776 struct wiphy *wiphy = wdev->wiphy;
9777 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
9778
9779 trace_cfg80211_ready_on_channel(wdev, cookie, chan, duration);
Jouni Malinen9588bbd2009-12-23 13:15:41 +01009780 nl80211_send_remain_on_chan_event(NL80211_CMD_REMAIN_ON_CHANNEL,
Johannes Berg71bbc992012-06-15 15:30:18 +02009781 rdev, wdev, cookie, chan,
Johannes Berg42d97a52012-11-08 18:31:02 +01009782 duration, gfp);
Jouni Malinen9588bbd2009-12-23 13:15:41 +01009783}
Johannes Berg947add32013-02-22 22:05:20 +01009784EXPORT_SYMBOL(cfg80211_ready_on_channel);
Jouni Malinen9588bbd2009-12-23 13:15:41 +01009785
Johannes Berg947add32013-02-22 22:05:20 +01009786void cfg80211_remain_on_channel_expired(struct wireless_dev *wdev, u64 cookie,
9787 struct ieee80211_channel *chan,
9788 gfp_t gfp)
Jouni Malinen9588bbd2009-12-23 13:15:41 +01009789{
Johannes Berg947add32013-02-22 22:05:20 +01009790 struct wiphy *wiphy = wdev->wiphy;
9791 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
9792
9793 trace_cfg80211_ready_on_channel_expired(wdev, cookie, chan);
Jouni Malinen9588bbd2009-12-23 13:15:41 +01009794 nl80211_send_remain_on_chan_event(NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL,
Johannes Berg42d97a52012-11-08 18:31:02 +01009795 rdev, wdev, cookie, chan, 0, gfp);
Jouni Malinen9588bbd2009-12-23 13:15:41 +01009796}
Johannes Berg947add32013-02-22 22:05:20 +01009797EXPORT_SYMBOL(cfg80211_remain_on_channel_expired);
Jouni Malinen9588bbd2009-12-23 13:15:41 +01009798
Johannes Berg947add32013-02-22 22:05:20 +01009799void cfg80211_new_sta(struct net_device *dev, const u8 *mac_addr,
9800 struct station_info *sinfo, gfp_t gfp)
Johannes Berg98b62182009-12-23 13:15:44 +01009801{
Johannes Berg947add32013-02-22 22:05:20 +01009802 struct wiphy *wiphy = dev->ieee80211_ptr->wiphy;
9803 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
Johannes Berg98b62182009-12-23 13:15:44 +01009804 struct sk_buff *msg;
9805
Johannes Berg947add32013-02-22 22:05:20 +01009806 trace_cfg80211_new_sta(dev, mac_addr, sinfo);
9807
Thomas Graf58050fc2012-06-28 03:57:45 +00009808 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
Johannes Berg98b62182009-12-23 13:15:44 +01009809 if (!msg)
9810 return;
9811
John W. Linville66266b32012-03-15 13:25:41 -04009812 if (nl80211_send_station(msg, 0, 0, 0,
9813 rdev, dev, mac_addr, sinfo) < 0) {
Johannes Berg98b62182009-12-23 13:15:44 +01009814 nlmsg_free(msg);
9815 return;
9816 }
9817
9818 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
9819 nl80211_mlme_mcgrp.id, gfp);
9820}
Johannes Berg947add32013-02-22 22:05:20 +01009821EXPORT_SYMBOL(cfg80211_new_sta);
Johannes Berg98b62182009-12-23 13:15:44 +01009822
Johannes Berg947add32013-02-22 22:05:20 +01009823void cfg80211_del_sta(struct net_device *dev, const u8 *mac_addr, gfp_t gfp)
Jouni Malinenec15e682011-03-23 15:29:52 +02009824{
Johannes Berg947add32013-02-22 22:05:20 +01009825 struct wiphy *wiphy = dev->ieee80211_ptr->wiphy;
9826 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
Jouni Malinenec15e682011-03-23 15:29:52 +02009827 struct sk_buff *msg;
9828 void *hdr;
9829
Johannes Berg947add32013-02-22 22:05:20 +01009830 trace_cfg80211_del_sta(dev, mac_addr);
9831
Thomas Graf58050fc2012-06-28 03:57:45 +00009832 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
Jouni Malinenec15e682011-03-23 15:29:52 +02009833 if (!msg)
9834 return;
9835
9836 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_DEL_STATION);
9837 if (!hdr) {
9838 nlmsg_free(msg);
9839 return;
9840 }
9841
David S. Miller9360ffd2012-03-29 04:41:26 -04009842 if (nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) ||
9843 nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr))
9844 goto nla_put_failure;
Jouni Malinenec15e682011-03-23 15:29:52 +02009845
Johannes Berg3b7b72e2011-10-22 19:05:51 +02009846 genlmsg_end(msg, hdr);
Jouni Malinenec15e682011-03-23 15:29:52 +02009847
9848 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
9849 nl80211_mlme_mcgrp.id, gfp);
9850 return;
9851
9852 nla_put_failure:
9853 genlmsg_cancel(msg, hdr);
9854 nlmsg_free(msg);
9855}
Johannes Berg947add32013-02-22 22:05:20 +01009856EXPORT_SYMBOL(cfg80211_del_sta);
Jouni Malinenec15e682011-03-23 15:29:52 +02009857
Johannes Berg947add32013-02-22 22:05:20 +01009858void cfg80211_conn_failed(struct net_device *dev, const u8 *mac_addr,
9859 enum nl80211_connect_failed_reason reason,
9860 gfp_t gfp)
Pandiyarajan Pitchaimuthued44a952012-09-18 16:50:49 +05309861{
Johannes Berg947add32013-02-22 22:05:20 +01009862 struct wiphy *wiphy = dev->ieee80211_ptr->wiphy;
9863 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
Pandiyarajan Pitchaimuthued44a952012-09-18 16:50:49 +05309864 struct sk_buff *msg;
9865 void *hdr;
9866
9867 msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
9868 if (!msg)
9869 return;
9870
9871 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_CONN_FAILED);
9872 if (!hdr) {
9873 nlmsg_free(msg);
9874 return;
9875 }
9876
9877 if (nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) ||
9878 nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr) ||
9879 nla_put_u32(msg, NL80211_ATTR_CONN_FAILED_REASON, reason))
9880 goto nla_put_failure;
9881
9882 genlmsg_end(msg, hdr);
9883
9884 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
9885 nl80211_mlme_mcgrp.id, gfp);
9886 return;
9887
9888 nla_put_failure:
9889 genlmsg_cancel(msg, hdr);
9890 nlmsg_free(msg);
9891}
Johannes Berg947add32013-02-22 22:05:20 +01009892EXPORT_SYMBOL(cfg80211_conn_failed);
Pandiyarajan Pitchaimuthued44a952012-09-18 16:50:49 +05309893
Johannes Bergb92ab5d2011-11-04 11:18:19 +01009894static bool __nl80211_unexpected_frame(struct net_device *dev, u8 cmd,
9895 const u8 *addr, gfp_t gfp)
Johannes Berg28946da2011-11-04 11:18:12 +01009896{
9897 struct wireless_dev *wdev = dev->ieee80211_ptr;
9898 struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
9899 struct sk_buff *msg;
9900 void *hdr;
9901 int err;
Eric W. Biederman15e47302012-09-07 20:12:54 +00009902 u32 nlportid = ACCESS_ONCE(wdev->ap_unexpected_nlportid);
Johannes Berg28946da2011-11-04 11:18:12 +01009903
Eric W. Biederman15e47302012-09-07 20:12:54 +00009904 if (!nlportid)
Johannes Berg28946da2011-11-04 11:18:12 +01009905 return false;
9906
9907 msg = nlmsg_new(100, gfp);
9908 if (!msg)
9909 return true;
9910
Johannes Bergb92ab5d2011-11-04 11:18:19 +01009911 hdr = nl80211hdr_put(msg, 0, 0, 0, cmd);
Johannes Berg28946da2011-11-04 11:18:12 +01009912 if (!hdr) {
9913 nlmsg_free(msg);
9914 return true;
9915 }
9916
David S. Miller9360ffd2012-03-29 04:41:26 -04009917 if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
9918 nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) ||
9919 nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr))
9920 goto nla_put_failure;
Johannes Berg28946da2011-11-04 11:18:12 +01009921
9922 err = genlmsg_end(msg, hdr);
9923 if (err < 0) {
9924 nlmsg_free(msg);
9925 return true;
9926 }
9927
Eric W. Biederman15e47302012-09-07 20:12:54 +00009928 genlmsg_unicast(wiphy_net(&rdev->wiphy), msg, nlportid);
Johannes Berg28946da2011-11-04 11:18:12 +01009929 return true;
9930
9931 nla_put_failure:
9932 genlmsg_cancel(msg, hdr);
9933 nlmsg_free(msg);
9934 return true;
9935}
9936
Johannes Berg947add32013-02-22 22:05:20 +01009937bool cfg80211_rx_spurious_frame(struct net_device *dev,
9938 const u8 *addr, gfp_t gfp)
Johannes Bergb92ab5d2011-11-04 11:18:19 +01009939{
Johannes Berg947add32013-02-22 22:05:20 +01009940 struct wireless_dev *wdev = dev->ieee80211_ptr;
9941 bool ret;
Johannes Bergb92ab5d2011-11-04 11:18:19 +01009942
Johannes Berg947add32013-02-22 22:05:20 +01009943 trace_cfg80211_rx_spurious_frame(dev, addr);
9944
9945 if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP &&
9946 wdev->iftype != NL80211_IFTYPE_P2P_GO)) {
9947 trace_cfg80211_return_bool(false);
9948 return false;
9949 }
9950 ret = __nl80211_unexpected_frame(dev, NL80211_CMD_UNEXPECTED_FRAME,
9951 addr, gfp);
9952 trace_cfg80211_return_bool(ret);
9953 return ret;
Johannes Bergb92ab5d2011-11-04 11:18:19 +01009954}
Johannes Berg947add32013-02-22 22:05:20 +01009955EXPORT_SYMBOL(cfg80211_rx_spurious_frame);
9956
9957bool cfg80211_rx_unexpected_4addr_frame(struct net_device *dev,
9958 const u8 *addr, gfp_t gfp)
9959{
9960 struct wireless_dev *wdev = dev->ieee80211_ptr;
9961 bool ret;
9962
9963 trace_cfg80211_rx_unexpected_4addr_frame(dev, addr);
9964
9965 if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP &&
9966 wdev->iftype != NL80211_IFTYPE_P2P_GO &&
9967 wdev->iftype != NL80211_IFTYPE_AP_VLAN)) {
9968 trace_cfg80211_return_bool(false);
9969 return false;
9970 }
9971 ret = __nl80211_unexpected_frame(dev,
9972 NL80211_CMD_UNEXPECTED_4ADDR_FRAME,
9973 addr, gfp);
9974 trace_cfg80211_return_bool(ret);
9975 return ret;
9976}
9977EXPORT_SYMBOL(cfg80211_rx_unexpected_4addr_frame);
Johannes Bergb92ab5d2011-11-04 11:18:19 +01009978
Johannes Berg2e161f72010-08-12 15:38:38 +02009979int nl80211_send_mgmt(struct cfg80211_registered_device *rdev,
Eric W. Biederman15e47302012-09-07 20:12:54 +00009980 struct wireless_dev *wdev, u32 nlportid,
Johannes Berg804483e2012-03-05 22:18:41 +01009981 int freq, int sig_dbm,
9982 const u8 *buf, size_t len, gfp_t gfp)
Jouni Malinen026331c2010-02-15 12:53:10 +02009983{
Johannes Berg71bbc992012-06-15 15:30:18 +02009984 struct net_device *netdev = wdev->netdev;
Jouni Malinen026331c2010-02-15 12:53:10 +02009985 struct sk_buff *msg;
9986 void *hdr;
Jouni Malinen026331c2010-02-15 12:53:10 +02009987
9988 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
9989 if (!msg)
9990 return -ENOMEM;
9991
Johannes Berg2e161f72010-08-12 15:38:38 +02009992 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_FRAME);
Jouni Malinen026331c2010-02-15 12:53:10 +02009993 if (!hdr) {
9994 nlmsg_free(msg);
9995 return -ENOMEM;
9996 }
9997
David S. Miller9360ffd2012-03-29 04:41:26 -04009998 if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
Johannes Berg71bbc992012-06-15 15:30:18 +02009999 (netdev && nla_put_u32(msg, NL80211_ATTR_IFINDEX,
10000 netdev->ifindex)) ||
Ilan Peera8384902013-05-08 16:35:55 +030010001 nla_put_u64(msg, NL80211_ATTR_WDEV, wdev_id(wdev)) ||
David S. Miller9360ffd2012-03-29 04:41:26 -040010002 nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, freq) ||
10003 (sig_dbm &&
10004 nla_put_u32(msg, NL80211_ATTR_RX_SIGNAL_DBM, sig_dbm)) ||
10005 nla_put(msg, NL80211_ATTR_FRAME, len, buf))
10006 goto nla_put_failure;
Jouni Malinen026331c2010-02-15 12:53:10 +020010007
Johannes Berg3b7b72e2011-10-22 19:05:51 +020010008 genlmsg_end(msg, hdr);
Jouni Malinen026331c2010-02-15 12:53:10 +020010009
Eric W. Biederman15e47302012-09-07 20:12:54 +000010010 return genlmsg_unicast(wiphy_net(&rdev->wiphy), msg, nlportid);
Jouni Malinen026331c2010-02-15 12:53:10 +020010011
10012 nla_put_failure:
10013 genlmsg_cancel(msg, hdr);
10014 nlmsg_free(msg);
10015 return -ENOBUFS;
10016}
10017
Johannes Berg947add32013-02-22 22:05:20 +010010018void cfg80211_mgmt_tx_status(struct wireless_dev *wdev, u64 cookie,
10019 const u8 *buf, size_t len, bool ack, gfp_t gfp)
Jouni Malinen026331c2010-02-15 12:53:10 +020010020{
Johannes Berg947add32013-02-22 22:05:20 +010010021 struct wiphy *wiphy = wdev->wiphy;
10022 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
Johannes Berg71bbc992012-06-15 15:30:18 +020010023 struct net_device *netdev = wdev->netdev;
Jouni Malinen026331c2010-02-15 12:53:10 +020010024 struct sk_buff *msg;
10025 void *hdr;
10026
Johannes Berg947add32013-02-22 22:05:20 +010010027 trace_cfg80211_mgmt_tx_status(wdev, cookie, ack);
10028
Jouni Malinen026331c2010-02-15 12:53:10 +020010029 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
10030 if (!msg)
10031 return;
10032
Johannes Berg2e161f72010-08-12 15:38:38 +020010033 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_FRAME_TX_STATUS);
Jouni Malinen026331c2010-02-15 12:53:10 +020010034 if (!hdr) {
10035 nlmsg_free(msg);
10036 return;
10037 }
10038
David S. Miller9360ffd2012-03-29 04:41:26 -040010039 if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
Johannes Berg71bbc992012-06-15 15:30:18 +020010040 (netdev && nla_put_u32(msg, NL80211_ATTR_IFINDEX,
10041 netdev->ifindex)) ||
Ilan Peera8384902013-05-08 16:35:55 +030010042 nla_put_u64(msg, NL80211_ATTR_WDEV, wdev_id(wdev)) ||
David S. Miller9360ffd2012-03-29 04:41:26 -040010043 nla_put(msg, NL80211_ATTR_FRAME, len, buf) ||
10044 nla_put_u64(msg, NL80211_ATTR_COOKIE, cookie) ||
10045 (ack && nla_put_flag(msg, NL80211_ATTR_ACK)))
10046 goto nla_put_failure;
Jouni Malinen026331c2010-02-15 12:53:10 +020010047
Johannes Berg3b7b72e2011-10-22 19:05:51 +020010048 genlmsg_end(msg, hdr);
Jouni Malinen026331c2010-02-15 12:53:10 +020010049
Michal Kaziorc8942c62013-06-25 09:17:17 +020010050 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
10051 nl80211_mlme_mcgrp.id, gfp);
Jouni Malinen026331c2010-02-15 12:53:10 +020010052 return;
10053
10054 nla_put_failure:
10055 genlmsg_cancel(msg, hdr);
10056 nlmsg_free(msg);
10057}
Johannes Berg947add32013-02-22 22:05:20 +010010058EXPORT_SYMBOL(cfg80211_mgmt_tx_status);
Jouni Malinen026331c2010-02-15 12:53:10 +020010059
Johannes Berg947add32013-02-22 22:05:20 +010010060void cfg80211_cqm_rssi_notify(struct net_device *dev,
10061 enum nl80211_cqm_rssi_threshold_event rssi_event,
10062 gfp_t gfp)
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +020010063{
Johannes Berg947add32013-02-22 22:05:20 +010010064 struct wireless_dev *wdev = dev->ieee80211_ptr;
10065 struct wiphy *wiphy = wdev->wiphy;
10066 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +020010067 struct sk_buff *msg;
10068 struct nlattr *pinfoattr;
10069 void *hdr;
10070
Johannes Berg947add32013-02-22 22:05:20 +010010071 trace_cfg80211_cqm_rssi_notify(dev, rssi_event);
10072
Thomas Graf58050fc2012-06-28 03:57:45 +000010073 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +020010074 if (!msg)
10075 return;
10076
10077 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_NOTIFY_CQM);
10078 if (!hdr) {
10079 nlmsg_free(msg);
10080 return;
10081 }
10082
David S. Miller9360ffd2012-03-29 04:41:26 -040010083 if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
Johannes Berg947add32013-02-22 22:05:20 +010010084 nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex))
David S. Miller9360ffd2012-03-29 04:41:26 -040010085 goto nla_put_failure;
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +020010086
10087 pinfoattr = nla_nest_start(msg, NL80211_ATTR_CQM);
10088 if (!pinfoattr)
10089 goto nla_put_failure;
10090
David S. Miller9360ffd2012-03-29 04:41:26 -040010091 if (nla_put_u32(msg, NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT,
10092 rssi_event))
10093 goto nla_put_failure;
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +020010094
10095 nla_nest_end(msg, pinfoattr);
10096
Johannes Berg3b7b72e2011-10-22 19:05:51 +020010097 genlmsg_end(msg, hdr);
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +020010098
10099 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
10100 nl80211_mlme_mcgrp.id, gfp);
10101 return;
10102
10103 nla_put_failure:
10104 genlmsg_cancel(msg, hdr);
10105 nlmsg_free(msg);
10106}
Johannes Berg947add32013-02-22 22:05:20 +010010107EXPORT_SYMBOL(cfg80211_cqm_rssi_notify);
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +020010108
Johannes Berg947add32013-02-22 22:05:20 +010010109static void nl80211_gtk_rekey_notify(struct cfg80211_registered_device *rdev,
10110 struct net_device *netdev, const u8 *bssid,
10111 const u8 *replay_ctr, gfp_t gfp)
Johannes Berge5497d72011-07-05 16:35:40 +020010112{
10113 struct sk_buff *msg;
10114 struct nlattr *rekey_attr;
10115 void *hdr;
10116
Thomas Graf58050fc2012-06-28 03:57:45 +000010117 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
Johannes Berge5497d72011-07-05 16:35:40 +020010118 if (!msg)
10119 return;
10120
10121 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_SET_REKEY_OFFLOAD);
10122 if (!hdr) {
10123 nlmsg_free(msg);
10124 return;
10125 }
10126
David S. Miller9360ffd2012-03-29 04:41:26 -040010127 if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
10128 nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) ||
10129 nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid))
10130 goto nla_put_failure;
Johannes Berge5497d72011-07-05 16:35:40 +020010131
10132 rekey_attr = nla_nest_start(msg, NL80211_ATTR_REKEY_DATA);
10133 if (!rekey_attr)
10134 goto nla_put_failure;
10135
David S. Miller9360ffd2012-03-29 04:41:26 -040010136 if (nla_put(msg, NL80211_REKEY_DATA_REPLAY_CTR,
10137 NL80211_REPLAY_CTR_LEN, replay_ctr))
10138 goto nla_put_failure;
Johannes Berge5497d72011-07-05 16:35:40 +020010139
10140 nla_nest_end(msg, rekey_attr);
10141
Johannes Berg3b7b72e2011-10-22 19:05:51 +020010142 genlmsg_end(msg, hdr);
Johannes Berge5497d72011-07-05 16:35:40 +020010143
10144 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
10145 nl80211_mlme_mcgrp.id, gfp);
10146 return;
10147
10148 nla_put_failure:
10149 genlmsg_cancel(msg, hdr);
10150 nlmsg_free(msg);
10151}
10152
Johannes Berg947add32013-02-22 22:05:20 +010010153void cfg80211_gtk_rekey_notify(struct net_device *dev, const u8 *bssid,
10154 const u8 *replay_ctr, gfp_t gfp)
10155{
10156 struct wireless_dev *wdev = dev->ieee80211_ptr;
10157 struct wiphy *wiphy = wdev->wiphy;
10158 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
10159
10160 trace_cfg80211_gtk_rekey_notify(dev, bssid);
10161 nl80211_gtk_rekey_notify(rdev, dev, bssid, replay_ctr, gfp);
10162}
10163EXPORT_SYMBOL(cfg80211_gtk_rekey_notify);
10164
10165static void
10166nl80211_pmksa_candidate_notify(struct cfg80211_registered_device *rdev,
10167 struct net_device *netdev, int index,
10168 const u8 *bssid, bool preauth, gfp_t gfp)
Jouni Malinenc9df56b2011-09-16 18:56:23 +030010169{
10170 struct sk_buff *msg;
10171 struct nlattr *attr;
10172 void *hdr;
10173
Thomas Graf58050fc2012-06-28 03:57:45 +000010174 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
Jouni Malinenc9df56b2011-09-16 18:56:23 +030010175 if (!msg)
10176 return;
10177
10178 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_PMKSA_CANDIDATE);
10179 if (!hdr) {
10180 nlmsg_free(msg);
10181 return;
10182 }
10183
David S. Miller9360ffd2012-03-29 04:41:26 -040010184 if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
10185 nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex))
10186 goto nla_put_failure;
Jouni Malinenc9df56b2011-09-16 18:56:23 +030010187
10188 attr = nla_nest_start(msg, NL80211_ATTR_PMKSA_CANDIDATE);
10189 if (!attr)
10190 goto nla_put_failure;
10191
David S. Miller9360ffd2012-03-29 04:41:26 -040010192 if (nla_put_u32(msg, NL80211_PMKSA_CANDIDATE_INDEX, index) ||
10193 nla_put(msg, NL80211_PMKSA_CANDIDATE_BSSID, ETH_ALEN, bssid) ||
10194 (preauth &&
10195 nla_put_flag(msg, NL80211_PMKSA_CANDIDATE_PREAUTH)))
10196 goto nla_put_failure;
Jouni Malinenc9df56b2011-09-16 18:56:23 +030010197
10198 nla_nest_end(msg, attr);
10199
Johannes Berg3b7b72e2011-10-22 19:05:51 +020010200 genlmsg_end(msg, hdr);
Jouni Malinenc9df56b2011-09-16 18:56:23 +030010201
10202 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
10203 nl80211_mlme_mcgrp.id, gfp);
10204 return;
10205
10206 nla_put_failure:
10207 genlmsg_cancel(msg, hdr);
10208 nlmsg_free(msg);
10209}
10210
Johannes Berg947add32013-02-22 22:05:20 +010010211void cfg80211_pmksa_candidate_notify(struct net_device *dev, int index,
10212 const u8 *bssid, bool preauth, gfp_t gfp)
10213{
10214 struct wireless_dev *wdev = dev->ieee80211_ptr;
10215 struct wiphy *wiphy = wdev->wiphy;
10216 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
10217
10218 trace_cfg80211_pmksa_candidate_notify(dev, index, bssid, preauth);
10219 nl80211_pmksa_candidate_notify(rdev, dev, index, bssid, preauth, gfp);
10220}
10221EXPORT_SYMBOL(cfg80211_pmksa_candidate_notify);
10222
10223static void nl80211_ch_switch_notify(struct cfg80211_registered_device *rdev,
10224 struct net_device *netdev,
10225 struct cfg80211_chan_def *chandef,
10226 gfp_t gfp)
Thomas Pedersen53145262012-04-06 13:35:47 -070010227{
10228 struct sk_buff *msg;
10229 void *hdr;
10230
Thomas Graf58050fc2012-06-28 03:57:45 +000010231 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
Thomas Pedersen53145262012-04-06 13:35:47 -070010232 if (!msg)
10233 return;
10234
10235 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_CH_SWITCH_NOTIFY);
10236 if (!hdr) {
10237 nlmsg_free(msg);
10238 return;
10239 }
10240
Johannes Berg683b6d32012-11-08 21:25:48 +010010241 if (nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex))
10242 goto nla_put_failure;
10243
10244 if (nl80211_send_chandef(msg, chandef))
John W. Linville7eab0f62012-04-12 14:25:14 -040010245 goto nla_put_failure;
Thomas Pedersen53145262012-04-06 13:35:47 -070010246
10247 genlmsg_end(msg, hdr);
10248
10249 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
10250 nl80211_mlme_mcgrp.id, gfp);
10251 return;
10252
10253 nla_put_failure:
10254 genlmsg_cancel(msg, hdr);
10255 nlmsg_free(msg);
10256}
10257
Johannes Berg947add32013-02-22 22:05:20 +010010258void cfg80211_ch_switch_notify(struct net_device *dev,
10259 struct cfg80211_chan_def *chandef)
Thomas Pedersen84f10702012-07-12 16:17:33 -070010260{
Johannes Berg947add32013-02-22 22:05:20 +010010261 struct wireless_dev *wdev = dev->ieee80211_ptr;
10262 struct wiphy *wiphy = wdev->wiphy;
10263 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
10264
10265 trace_cfg80211_ch_switch_notify(dev, chandef);
10266
10267 wdev_lock(wdev);
10268
10269 if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP &&
10270 wdev->iftype != NL80211_IFTYPE_P2P_GO))
10271 goto out;
10272
10273 wdev->channel = chandef->chan;
10274 nl80211_ch_switch_notify(rdev, dev, chandef, GFP_KERNEL);
10275out:
10276 wdev_unlock(wdev);
10277 return;
10278}
10279EXPORT_SYMBOL(cfg80211_ch_switch_notify);
10280
10281void cfg80211_cqm_txe_notify(struct net_device *dev,
10282 const u8 *peer, u32 num_packets,
10283 u32 rate, u32 intvl, gfp_t gfp)
10284{
10285 struct wireless_dev *wdev = dev->ieee80211_ptr;
10286 struct wiphy *wiphy = wdev->wiphy;
10287 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
Thomas Pedersen84f10702012-07-12 16:17:33 -070010288 struct sk_buff *msg;
10289 struct nlattr *pinfoattr;
10290 void *hdr;
10291
10292 msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
10293 if (!msg)
10294 return;
10295
10296 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_NOTIFY_CQM);
10297 if (!hdr) {
10298 nlmsg_free(msg);
10299 return;
10300 }
10301
10302 if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
Johannes Berg947add32013-02-22 22:05:20 +010010303 nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) ||
Thomas Pedersen84f10702012-07-12 16:17:33 -070010304 nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, peer))
10305 goto nla_put_failure;
10306
10307 pinfoattr = nla_nest_start(msg, NL80211_ATTR_CQM);
10308 if (!pinfoattr)
10309 goto nla_put_failure;
10310
10311 if (nla_put_u32(msg, NL80211_ATTR_CQM_TXE_PKTS, num_packets))
10312 goto nla_put_failure;
10313
10314 if (nla_put_u32(msg, NL80211_ATTR_CQM_TXE_RATE, rate))
10315 goto nla_put_failure;
10316
10317 if (nla_put_u32(msg, NL80211_ATTR_CQM_TXE_INTVL, intvl))
10318 goto nla_put_failure;
10319
10320 nla_nest_end(msg, pinfoattr);
10321
10322 genlmsg_end(msg, hdr);
10323
10324 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
10325 nl80211_mlme_mcgrp.id, gfp);
10326 return;
10327
10328 nla_put_failure:
10329 genlmsg_cancel(msg, hdr);
10330 nlmsg_free(msg);
10331}
Johannes Berg947add32013-02-22 22:05:20 +010010332EXPORT_SYMBOL(cfg80211_cqm_txe_notify);
Thomas Pedersen84f10702012-07-12 16:17:33 -070010333
10334void
Simon Wunderlich04f39042013-02-08 18:16:19 +010010335nl80211_radar_notify(struct cfg80211_registered_device *rdev,
10336 struct cfg80211_chan_def *chandef,
10337 enum nl80211_radar_event event,
10338 struct net_device *netdev, gfp_t gfp)
10339{
10340 struct sk_buff *msg;
10341 void *hdr;
10342
10343 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
10344 if (!msg)
10345 return;
10346
10347 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_RADAR_DETECT);
10348 if (!hdr) {
10349 nlmsg_free(msg);
10350 return;
10351 }
10352
10353 if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx))
10354 goto nla_put_failure;
10355
10356 /* NOP and radar events don't need a netdev parameter */
10357 if (netdev) {
10358 struct wireless_dev *wdev = netdev->ieee80211_ptr;
10359
10360 if (nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) ||
10361 nla_put_u64(msg, NL80211_ATTR_WDEV, wdev_id(wdev)))
10362 goto nla_put_failure;
10363 }
10364
10365 if (nla_put_u32(msg, NL80211_ATTR_RADAR_EVENT, event))
10366 goto nla_put_failure;
10367
10368 if (nl80211_send_chandef(msg, chandef))
10369 goto nla_put_failure;
10370
10371 if (genlmsg_end(msg, hdr) < 0) {
10372 nlmsg_free(msg);
10373 return;
10374 }
10375
10376 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
10377 nl80211_mlme_mcgrp.id, gfp);
10378 return;
10379
10380 nla_put_failure:
10381 genlmsg_cancel(msg, hdr);
10382 nlmsg_free(msg);
10383}
10384
Johannes Berg947add32013-02-22 22:05:20 +010010385void cfg80211_cqm_pktloss_notify(struct net_device *dev,
10386 const u8 *peer, u32 num_packets, gfp_t gfp)
Johannes Bergc063dbf2010-11-24 08:10:05 +010010387{
Johannes Berg947add32013-02-22 22:05:20 +010010388 struct wireless_dev *wdev = dev->ieee80211_ptr;
10389 struct wiphy *wiphy = wdev->wiphy;
10390 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
Johannes Bergc063dbf2010-11-24 08:10:05 +010010391 struct sk_buff *msg;
10392 struct nlattr *pinfoattr;
10393 void *hdr;
10394
Johannes Berg947add32013-02-22 22:05:20 +010010395 trace_cfg80211_cqm_pktloss_notify(dev, peer, num_packets);
10396
Thomas Graf58050fc2012-06-28 03:57:45 +000010397 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
Johannes Bergc063dbf2010-11-24 08:10:05 +010010398 if (!msg)
10399 return;
10400
10401 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_NOTIFY_CQM);
10402 if (!hdr) {
10403 nlmsg_free(msg);
10404 return;
10405 }
10406
David S. Miller9360ffd2012-03-29 04:41:26 -040010407 if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
Johannes Berg947add32013-02-22 22:05:20 +010010408 nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) ||
David S. Miller9360ffd2012-03-29 04:41:26 -040010409 nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, peer))
10410 goto nla_put_failure;
Johannes Bergc063dbf2010-11-24 08:10:05 +010010411
10412 pinfoattr = nla_nest_start(msg, NL80211_ATTR_CQM);
10413 if (!pinfoattr)
10414 goto nla_put_failure;
10415
David S. Miller9360ffd2012-03-29 04:41:26 -040010416 if (nla_put_u32(msg, NL80211_ATTR_CQM_PKT_LOSS_EVENT, num_packets))
10417 goto nla_put_failure;
Johannes Bergc063dbf2010-11-24 08:10:05 +010010418
10419 nla_nest_end(msg, pinfoattr);
10420
Johannes Berg3b7b72e2011-10-22 19:05:51 +020010421 genlmsg_end(msg, hdr);
Johannes Bergc063dbf2010-11-24 08:10:05 +010010422
10423 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
10424 nl80211_mlme_mcgrp.id, gfp);
10425 return;
10426
10427 nla_put_failure:
10428 genlmsg_cancel(msg, hdr);
10429 nlmsg_free(msg);
10430}
Johannes Berg947add32013-02-22 22:05:20 +010010431EXPORT_SYMBOL(cfg80211_cqm_pktloss_notify);
Johannes Bergc063dbf2010-11-24 08:10:05 +010010432
Johannes Berg7f6cf312011-11-04 11:18:15 +010010433void cfg80211_probe_status(struct net_device *dev, const u8 *addr,
10434 u64 cookie, bool acked, gfp_t gfp)
10435{
10436 struct wireless_dev *wdev = dev->ieee80211_ptr;
10437 struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
10438 struct sk_buff *msg;
10439 void *hdr;
10440 int err;
10441
Beni Lev4ee3e062012-08-27 12:49:39 +030010442 trace_cfg80211_probe_status(dev, addr, cookie, acked);
10443
Thomas Graf58050fc2012-06-28 03:57:45 +000010444 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
Beni Lev4ee3e062012-08-27 12:49:39 +030010445
Johannes Berg7f6cf312011-11-04 11:18:15 +010010446 if (!msg)
10447 return;
10448
10449 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_PROBE_CLIENT);
10450 if (!hdr) {
10451 nlmsg_free(msg);
10452 return;
10453 }
10454
David S. Miller9360ffd2012-03-29 04:41:26 -040010455 if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
10456 nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) ||
10457 nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr) ||
10458 nla_put_u64(msg, NL80211_ATTR_COOKIE, cookie) ||
10459 (acked && nla_put_flag(msg, NL80211_ATTR_ACK)))
10460 goto nla_put_failure;
Johannes Berg7f6cf312011-11-04 11:18:15 +010010461
10462 err = genlmsg_end(msg, hdr);
10463 if (err < 0) {
10464 nlmsg_free(msg);
10465 return;
10466 }
10467
10468 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
10469 nl80211_mlme_mcgrp.id, gfp);
10470 return;
10471
10472 nla_put_failure:
10473 genlmsg_cancel(msg, hdr);
10474 nlmsg_free(msg);
10475}
10476EXPORT_SYMBOL(cfg80211_probe_status);
10477
Johannes Berg5e760232011-11-04 11:18:17 +010010478void cfg80211_report_obss_beacon(struct wiphy *wiphy,
10479 const u8 *frame, size_t len,
Ben Greear37c73b52012-10-26 14:49:25 -070010480 int freq, int sig_dbm)
Johannes Berg5e760232011-11-04 11:18:17 +010010481{
10482 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
10483 struct sk_buff *msg;
10484 void *hdr;
Ben Greear37c73b52012-10-26 14:49:25 -070010485 struct cfg80211_beacon_registration *reg;
Johannes Berg5e760232011-11-04 11:18:17 +010010486
Beni Lev4ee3e062012-08-27 12:49:39 +030010487 trace_cfg80211_report_obss_beacon(wiphy, frame, len, freq, sig_dbm);
10488
Ben Greear37c73b52012-10-26 14:49:25 -070010489 spin_lock_bh(&rdev->beacon_registrations_lock);
10490 list_for_each_entry(reg, &rdev->beacon_registrations, list) {
10491 msg = nlmsg_new(len + 100, GFP_ATOMIC);
10492 if (!msg) {
10493 spin_unlock_bh(&rdev->beacon_registrations_lock);
10494 return;
10495 }
Johannes Berg5e760232011-11-04 11:18:17 +010010496
Ben Greear37c73b52012-10-26 14:49:25 -070010497 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_FRAME);
10498 if (!hdr)
10499 goto nla_put_failure;
Johannes Berg5e760232011-11-04 11:18:17 +010010500
Ben Greear37c73b52012-10-26 14:49:25 -070010501 if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
10502 (freq &&
10503 nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, freq)) ||
10504 (sig_dbm &&
10505 nla_put_u32(msg, NL80211_ATTR_RX_SIGNAL_DBM, sig_dbm)) ||
10506 nla_put(msg, NL80211_ATTR_FRAME, len, frame))
10507 goto nla_put_failure;
10508
10509 genlmsg_end(msg, hdr);
10510
10511 genlmsg_unicast(wiphy_net(&rdev->wiphy), msg, reg->nlportid);
Johannes Berg5e760232011-11-04 11:18:17 +010010512 }
Ben Greear37c73b52012-10-26 14:49:25 -070010513 spin_unlock_bh(&rdev->beacon_registrations_lock);
Johannes Berg5e760232011-11-04 11:18:17 +010010514 return;
10515
10516 nla_put_failure:
Ben Greear37c73b52012-10-26 14:49:25 -070010517 spin_unlock_bh(&rdev->beacon_registrations_lock);
10518 if (hdr)
10519 genlmsg_cancel(msg, hdr);
Johannes Berg5e760232011-11-04 11:18:17 +010010520 nlmsg_free(msg);
10521}
10522EXPORT_SYMBOL(cfg80211_report_obss_beacon);
10523
Johannes Bergcd8f7cb2013-01-22 12:34:29 +010010524#ifdef CONFIG_PM
10525void cfg80211_report_wowlan_wakeup(struct wireless_dev *wdev,
10526 struct cfg80211_wowlan_wakeup *wakeup,
10527 gfp_t gfp)
10528{
10529 struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
10530 struct sk_buff *msg;
10531 void *hdr;
10532 int err, size = 200;
10533
10534 trace_cfg80211_report_wowlan_wakeup(wdev->wiphy, wdev, wakeup);
10535
10536 if (wakeup)
10537 size += wakeup->packet_present_len;
10538
10539 msg = nlmsg_new(size, gfp);
10540 if (!msg)
10541 return;
10542
10543 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_SET_WOWLAN);
10544 if (!hdr)
10545 goto free_msg;
10546
10547 if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
10548 nla_put_u64(msg, NL80211_ATTR_WDEV, wdev_id(wdev)))
10549 goto free_msg;
10550
10551 if (wdev->netdev && nla_put_u32(msg, NL80211_ATTR_IFINDEX,
10552 wdev->netdev->ifindex))
10553 goto free_msg;
10554
10555 if (wakeup) {
10556 struct nlattr *reasons;
10557
10558 reasons = nla_nest_start(msg, NL80211_ATTR_WOWLAN_TRIGGERS);
10559
10560 if (wakeup->disconnect &&
10561 nla_put_flag(msg, NL80211_WOWLAN_TRIG_DISCONNECT))
10562 goto free_msg;
10563 if (wakeup->magic_pkt &&
10564 nla_put_flag(msg, NL80211_WOWLAN_TRIG_MAGIC_PKT))
10565 goto free_msg;
10566 if (wakeup->gtk_rekey_failure &&
10567 nla_put_flag(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE))
10568 goto free_msg;
10569 if (wakeup->eap_identity_req &&
10570 nla_put_flag(msg, NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST))
10571 goto free_msg;
10572 if (wakeup->four_way_handshake &&
10573 nla_put_flag(msg, NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE))
10574 goto free_msg;
10575 if (wakeup->rfkill_release &&
10576 nla_put_flag(msg, NL80211_WOWLAN_TRIG_RFKILL_RELEASE))
10577 goto free_msg;
10578
10579 if (wakeup->pattern_idx >= 0 &&
10580 nla_put_u32(msg, NL80211_WOWLAN_TRIG_PKT_PATTERN,
10581 wakeup->pattern_idx))
10582 goto free_msg;
10583
Johannes Berg2a0e0472013-01-23 22:57:40 +010010584 if (wakeup->tcp_match)
10585 nla_put_flag(msg, NL80211_WOWLAN_TRIG_WAKEUP_TCP_MATCH);
10586
10587 if (wakeup->tcp_connlost)
10588 nla_put_flag(msg,
10589 NL80211_WOWLAN_TRIG_WAKEUP_TCP_CONNLOST);
10590
10591 if (wakeup->tcp_nomoretokens)
10592 nla_put_flag(msg,
10593 NL80211_WOWLAN_TRIG_WAKEUP_TCP_NOMORETOKENS);
10594
Johannes Bergcd8f7cb2013-01-22 12:34:29 +010010595 if (wakeup->packet) {
10596 u32 pkt_attr = NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211;
10597 u32 len_attr = NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211_LEN;
10598
10599 if (!wakeup->packet_80211) {
10600 pkt_attr =
10601 NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023;
10602 len_attr =
10603 NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023_LEN;
10604 }
10605
10606 if (wakeup->packet_len &&
10607 nla_put_u32(msg, len_attr, wakeup->packet_len))
10608 goto free_msg;
10609
10610 if (nla_put(msg, pkt_attr, wakeup->packet_present_len,
10611 wakeup->packet))
10612 goto free_msg;
10613 }
10614
10615 nla_nest_end(msg, reasons);
10616 }
10617
10618 err = genlmsg_end(msg, hdr);
10619 if (err < 0)
10620 goto free_msg;
10621
10622 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
10623 nl80211_mlme_mcgrp.id, gfp);
10624 return;
10625
10626 free_msg:
10627 nlmsg_free(msg);
10628}
10629EXPORT_SYMBOL(cfg80211_report_wowlan_wakeup);
10630#endif
10631
Jouni Malinen3475b092012-11-16 22:49:57 +020010632void cfg80211_tdls_oper_request(struct net_device *dev, const u8 *peer,
10633 enum nl80211_tdls_operation oper,
10634 u16 reason_code, gfp_t gfp)
10635{
10636 struct wireless_dev *wdev = dev->ieee80211_ptr;
10637 struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
10638 struct sk_buff *msg;
10639 void *hdr;
10640 int err;
10641
10642 trace_cfg80211_tdls_oper_request(wdev->wiphy, dev, peer, oper,
10643 reason_code);
10644
10645 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
10646 if (!msg)
10647 return;
10648
10649 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_TDLS_OPER);
10650 if (!hdr) {
10651 nlmsg_free(msg);
10652 return;
10653 }
10654
10655 if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
10656 nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) ||
10657 nla_put_u8(msg, NL80211_ATTR_TDLS_OPERATION, oper) ||
10658 nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, peer) ||
10659 (reason_code > 0 &&
10660 nla_put_u16(msg, NL80211_ATTR_REASON_CODE, reason_code)))
10661 goto nla_put_failure;
10662
10663 err = genlmsg_end(msg, hdr);
10664 if (err < 0) {
10665 nlmsg_free(msg);
10666 return;
10667 }
10668
10669 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
10670 nl80211_mlme_mcgrp.id, gfp);
10671 return;
10672
10673 nla_put_failure:
10674 genlmsg_cancel(msg, hdr);
10675 nlmsg_free(msg);
10676}
10677EXPORT_SYMBOL(cfg80211_tdls_oper_request);
10678
Jouni Malinen026331c2010-02-15 12:53:10 +020010679static int nl80211_netlink_notify(struct notifier_block * nb,
10680 unsigned long state,
10681 void *_notify)
10682{
10683 struct netlink_notify *notify = _notify;
10684 struct cfg80211_registered_device *rdev;
10685 struct wireless_dev *wdev;
Ben Greear37c73b52012-10-26 14:49:25 -070010686 struct cfg80211_beacon_registration *reg, *tmp;
Jouni Malinen026331c2010-02-15 12:53:10 +020010687
10688 if (state != NETLINK_URELEASE)
10689 return NOTIFY_DONE;
10690
10691 rcu_read_lock();
10692
Johannes Berg5e760232011-11-04 11:18:17 +010010693 list_for_each_entry_rcu(rdev, &cfg80211_rdev_list, list) {
Johannes Berg89a54e42012-06-15 14:33:17 +020010694 list_for_each_entry_rcu(wdev, &rdev->wdev_list, list)
Eric W. Biederman15e47302012-09-07 20:12:54 +000010695 cfg80211_mlme_unregister_socket(wdev, notify->portid);
Ben Greear37c73b52012-10-26 14:49:25 -070010696
10697 spin_lock_bh(&rdev->beacon_registrations_lock);
10698 list_for_each_entry_safe(reg, tmp, &rdev->beacon_registrations,
10699 list) {
10700 if (reg->nlportid == notify->portid) {
10701 list_del(&reg->list);
10702 kfree(reg);
10703 break;
10704 }
10705 }
10706 spin_unlock_bh(&rdev->beacon_registrations_lock);
Johannes Berg5e760232011-11-04 11:18:17 +010010707 }
Jouni Malinen026331c2010-02-15 12:53:10 +020010708
10709 rcu_read_unlock();
10710
10711 return NOTIFY_DONE;
10712}
10713
10714static struct notifier_block nl80211_netlink_notifier = {
10715 .notifier_call = nl80211_netlink_notify,
10716};
10717
Jouni Malinen355199e2013-02-27 17:14:27 +020010718void cfg80211_ft_event(struct net_device *netdev,
10719 struct cfg80211_ft_event_params *ft_event)
10720{
10721 struct wiphy *wiphy = netdev->ieee80211_ptr->wiphy;
10722 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
10723 struct sk_buff *msg;
10724 void *hdr;
10725 int err;
10726
10727 trace_cfg80211_ft_event(wiphy, netdev, ft_event);
10728
10729 if (!ft_event->target_ap)
10730 return;
10731
10732 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
10733 if (!msg)
10734 return;
10735
10736 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_FT_EVENT);
10737 if (!hdr) {
10738 nlmsg_free(msg);
10739 return;
10740 }
10741
10742 nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
10743 nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
10744 nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, ft_event->target_ap);
10745 if (ft_event->ies)
10746 nla_put(msg, NL80211_ATTR_IE, ft_event->ies_len, ft_event->ies);
10747 if (ft_event->ric_ies)
10748 nla_put(msg, NL80211_ATTR_IE_RIC, ft_event->ric_ies_len,
10749 ft_event->ric_ies);
10750
10751 err = genlmsg_end(msg, hdr);
10752 if (err < 0) {
10753 nlmsg_free(msg);
10754 return;
10755 }
10756
10757 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
10758 nl80211_mlme_mcgrp.id, GFP_KERNEL);
10759}
10760EXPORT_SYMBOL(cfg80211_ft_event);
10761
Arend van Spriel5de17982013-04-18 15:49:00 +020010762void cfg80211_crit_proto_stopped(struct wireless_dev *wdev, gfp_t gfp)
10763{
10764 struct cfg80211_registered_device *rdev;
10765 struct sk_buff *msg;
10766 void *hdr;
10767 u32 nlportid;
10768
10769 rdev = wiphy_to_dev(wdev->wiphy);
10770 if (!rdev->crit_proto_nlportid)
10771 return;
10772
10773 nlportid = rdev->crit_proto_nlportid;
10774 rdev->crit_proto_nlportid = 0;
10775
10776 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
10777 if (!msg)
10778 return;
10779
10780 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_CRIT_PROTOCOL_STOP);
10781 if (!hdr)
10782 goto nla_put_failure;
10783
10784 if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
10785 nla_put_u64(msg, NL80211_ATTR_WDEV, wdev_id(wdev)))
10786 goto nla_put_failure;
10787
10788 genlmsg_end(msg, hdr);
10789
10790 genlmsg_unicast(wiphy_net(&rdev->wiphy), msg, nlportid);
10791 return;
10792
10793 nla_put_failure:
10794 if (hdr)
10795 genlmsg_cancel(msg, hdr);
10796 nlmsg_free(msg);
10797
10798}
10799EXPORT_SYMBOL(cfg80211_crit_proto_stopped);
10800
Johannes Berg55682962007-09-20 13:09:35 -040010801/* initialisation/exit functions */
10802
10803int nl80211_init(void)
10804{
Michał Mirosław0d63cbb2009-05-21 10:34:06 +000010805 int err;
Johannes Berg55682962007-09-20 13:09:35 -040010806
Michał Mirosław0d63cbb2009-05-21 10:34:06 +000010807 err = genl_register_family_with_ops(&nl80211_fam,
10808 nl80211_ops, ARRAY_SIZE(nl80211_ops));
Johannes Berg55682962007-09-20 13:09:35 -040010809 if (err)
10810 return err;
10811
Johannes Berg55682962007-09-20 13:09:35 -040010812 err = genl_register_mc_group(&nl80211_fam, &nl80211_config_mcgrp);
10813 if (err)
10814 goto err_out;
10815
Johannes Berg2a519312009-02-10 21:25:55 +010010816 err = genl_register_mc_group(&nl80211_fam, &nl80211_scan_mcgrp);
10817 if (err)
10818 goto err_out;
10819
Luis R. Rodriguez73d54c92009-03-09 22:07:42 -040010820 err = genl_register_mc_group(&nl80211_fam, &nl80211_regulatory_mcgrp);
10821 if (err)
10822 goto err_out;
10823
Jouni Malinen6039f6d2009-03-19 13:39:21 +020010824 err = genl_register_mc_group(&nl80211_fam, &nl80211_mlme_mcgrp);
10825 if (err)
10826 goto err_out;
10827
Johannes Bergaff89a92009-07-01 21:26:51 +020010828#ifdef CONFIG_NL80211_TESTMODE
10829 err = genl_register_mc_group(&nl80211_fam, &nl80211_testmode_mcgrp);
10830 if (err)
10831 goto err_out;
10832#endif
10833
Jouni Malinen026331c2010-02-15 12:53:10 +020010834 err = netlink_register_notifier(&nl80211_netlink_notifier);
10835 if (err)
10836 goto err_out;
10837
Johannes Berg55682962007-09-20 13:09:35 -040010838 return 0;
10839 err_out:
10840 genl_unregister_family(&nl80211_fam);
10841 return err;
10842}
10843
10844void nl80211_exit(void)
10845{
Jouni Malinen026331c2010-02-15 12:53:10 +020010846 netlink_unregister_notifier(&nl80211_netlink_notifier);
Johannes Berg55682962007-09-20 13:09:35 -040010847 genl_unregister_family(&nl80211_fam);
10848}