blob: ca8d04cb172f8bee4ad4ce6a3cd12ca2f9159840 [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 Berg55682962007-09-20 13:09:35 -040022#include "core.h"
23#include "nl80211.h"
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -070024#include "reg.h"
Johannes Berg55682962007-09-20 13:09:35 -040025
Johannes Berg4c476992010-10-04 21:36:35 +020026static int nl80211_pre_doit(struct genl_ops *ops, struct sk_buff *skb,
27 struct genl_info *info);
28static void nl80211_post_doit(struct genl_ops *ops, struct sk_buff *skb,
29 struct genl_info *info);
30
Johannes Berg55682962007-09-20 13:09:35 -040031/* the netlink family */
32static struct genl_family nl80211_fam = {
33 .id = GENL_ID_GENERATE, /* don't bother with a hardcoded ID */
34 .name = "nl80211", /* have users key off the name instead */
35 .hdrsize = 0, /* no private header */
36 .version = 1, /* no particular meaning now */
37 .maxattr = NL80211_ATTR_MAX,
Johannes Berg463d0182009-07-14 00:33:35 +020038 .netnsok = true,
Johannes Berg4c476992010-10-04 21:36:35 +020039 .pre_doit = nl80211_pre_doit,
40 .post_doit = nl80211_post_doit,
Johannes Berg55682962007-09-20 13:09:35 -040041};
42
Johannes Berg79c97e92009-07-07 03:56:12 +020043/* internal helper: get rdev and dev */
Johannes Berg463d0182009-07-14 00:33:35 +020044static int get_rdev_dev_by_info_ifindex(struct genl_info *info,
Johannes Berg79c97e92009-07-07 03:56:12 +020045 struct cfg80211_registered_device **rdev,
Johannes Berg55682962007-09-20 13:09:35 -040046 struct net_device **dev)
47{
Johannes Berg463d0182009-07-14 00:33:35 +020048 struct nlattr **attrs = info->attrs;
Johannes Berg55682962007-09-20 13:09:35 -040049 int ifindex;
50
Johannes Bergbba95fe2008-07-29 13:22:51 +020051 if (!attrs[NL80211_ATTR_IFINDEX])
Johannes Berg55682962007-09-20 13:09:35 -040052 return -EINVAL;
53
Johannes Bergbba95fe2008-07-29 13:22:51 +020054 ifindex = nla_get_u32(attrs[NL80211_ATTR_IFINDEX]);
Johannes Berg463d0182009-07-14 00:33:35 +020055 *dev = dev_get_by_index(genl_info_net(info), ifindex);
Johannes Berg55682962007-09-20 13:09:35 -040056 if (!*dev)
57 return -ENODEV;
58
Johannes Berg463d0182009-07-14 00:33:35 +020059 *rdev = cfg80211_get_dev_from_ifindex(genl_info_net(info), ifindex);
Johannes Berg79c97e92009-07-07 03:56:12 +020060 if (IS_ERR(*rdev)) {
Johannes Berg55682962007-09-20 13:09:35 -040061 dev_put(*dev);
Johannes Berg79c97e92009-07-07 03:56:12 +020062 return PTR_ERR(*rdev);
Johannes Berg55682962007-09-20 13:09:35 -040063 }
64
65 return 0;
66}
67
68/* policy for the attributes */
Alexey Dobriyanb54452b2010-02-18 08:14:31 +000069static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = {
Johannes Berg55682962007-09-20 13:09:35 -040070 [NL80211_ATTR_WIPHY] = { .type = NLA_U32 },
71 [NL80211_ATTR_WIPHY_NAME] = { .type = NLA_NUL_STRING,
David S. Miller079e24e2009-05-26 21:15:00 -070072 .len = 20-1 },
Jouni Malinen31888482008-10-30 16:59:24 +020073 [NL80211_ATTR_WIPHY_TXQ_PARAMS] = { .type = NLA_NESTED },
Jouni Malinen72bdcf32008-11-26 16:15:24 +020074 [NL80211_ATTR_WIPHY_FREQ] = { .type = NLA_U32 },
Sujith094d05d2008-12-12 11:57:43 +053075 [NL80211_ATTR_WIPHY_CHANNEL_TYPE] = { .type = NLA_U32 },
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +020076 [NL80211_ATTR_WIPHY_RETRY_SHORT] = { .type = NLA_U8 },
77 [NL80211_ATTR_WIPHY_RETRY_LONG] = { .type = NLA_U8 },
78 [NL80211_ATTR_WIPHY_FRAG_THRESHOLD] = { .type = NLA_U32 },
79 [NL80211_ATTR_WIPHY_RTS_THRESHOLD] = { .type = NLA_U32 },
Lukáš Turek81077e82009-12-21 22:50:47 +010080 [NL80211_ATTR_WIPHY_COVERAGE_CLASS] = { .type = NLA_U8 },
Johannes Berg55682962007-09-20 13:09:35 -040081
82 [NL80211_ATTR_IFTYPE] = { .type = NLA_U32 },
83 [NL80211_ATTR_IFINDEX] = { .type = NLA_U32 },
84 [NL80211_ATTR_IFNAME] = { .type = NLA_NUL_STRING, .len = IFNAMSIZ-1 },
Johannes Berg41ade002007-12-19 02:03:29 +010085
86 [NL80211_ATTR_MAC] = { .type = NLA_BINARY, .len = ETH_ALEN },
Johannes Berg3e5d7642009-07-07 14:37:26 +020087 [NL80211_ATTR_PREV_BSSID] = { .type = NLA_BINARY, .len = ETH_ALEN },
Johannes Berg41ade002007-12-19 02:03:29 +010088
Johannes Bergb9454e82009-07-08 13:29:08 +020089 [NL80211_ATTR_KEY] = { .type = NLA_NESTED, },
Johannes Berg41ade002007-12-19 02:03:29 +010090 [NL80211_ATTR_KEY_DATA] = { .type = NLA_BINARY,
91 .len = WLAN_MAX_KEY_LEN },
92 [NL80211_ATTR_KEY_IDX] = { .type = NLA_U8 },
93 [NL80211_ATTR_KEY_CIPHER] = { .type = NLA_U32 },
94 [NL80211_ATTR_KEY_DEFAULT] = { .type = NLA_FLAG },
Jouni Malinen9f26a952009-05-15 12:38:32 +030095 [NL80211_ATTR_KEY_SEQ] = { .type = NLA_BINARY, .len = 8 },
Johannes Berged1b6cc2007-12-19 02:03:32 +010096
97 [NL80211_ATTR_BEACON_INTERVAL] = { .type = NLA_U32 },
98 [NL80211_ATTR_DTIM_PERIOD] = { .type = NLA_U32 },
99 [NL80211_ATTR_BEACON_HEAD] = { .type = NLA_BINARY,
100 .len = IEEE80211_MAX_DATA_LEN },
101 [NL80211_ATTR_BEACON_TAIL] = { .type = NLA_BINARY,
102 .len = IEEE80211_MAX_DATA_LEN },
Johannes Berg5727ef12007-12-19 02:03:34 +0100103 [NL80211_ATTR_STA_AID] = { .type = NLA_U16 },
104 [NL80211_ATTR_STA_FLAGS] = { .type = NLA_NESTED },
105 [NL80211_ATTR_STA_LISTEN_INTERVAL] = { .type = NLA_U16 },
106 [NL80211_ATTR_STA_SUPPORTED_RATES] = { .type = NLA_BINARY,
107 .len = NL80211_MAX_SUPP_RATES },
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100108 [NL80211_ATTR_STA_PLINK_ACTION] = { .type = NLA_U8 },
Johannes Berg5727ef12007-12-19 02:03:34 +0100109 [NL80211_ATTR_STA_VLAN] = { .type = NLA_U32 },
Johannes Berg0a9542e2008-10-15 11:54:04 +0200110 [NL80211_ATTR_MNTR_FLAGS] = { /* NLA_NESTED can't be empty */ },
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100111 [NL80211_ATTR_MESH_ID] = { .type = NLA_BINARY,
112 .len = IEEE80211_MAX_MESH_ID_LEN },
113 [NL80211_ATTR_MPATH_NEXT_HOP] = { .type = NLA_U32 },
Jouni Malinen9f1ba902008-08-07 20:07:01 +0300114
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -0700115 [NL80211_ATTR_REG_ALPHA2] = { .type = NLA_STRING, .len = 2 },
116 [NL80211_ATTR_REG_RULES] = { .type = NLA_NESTED },
117
Jouni Malinen9f1ba902008-08-07 20:07:01 +0300118 [NL80211_ATTR_BSS_CTS_PROT] = { .type = NLA_U8 },
119 [NL80211_ATTR_BSS_SHORT_PREAMBLE] = { .type = NLA_U8 },
120 [NL80211_ATTR_BSS_SHORT_SLOT_TIME] = { .type = NLA_U8 },
Jouni Malinen90c97a02008-10-30 16:59:22 +0200121 [NL80211_ATTR_BSS_BASIC_RATES] = { .type = NLA_BINARY,
122 .len = NL80211_MAX_SUPP_RATES },
Jouni Malinen36aedc92008-08-25 11:58:58 +0300123
colin@cozybit.com93da9cc2008-10-21 12:03:48 -0700124 [NL80211_ATTR_MESH_PARAMS] = { .type = NLA_NESTED },
125
Jouni Malinen36aedc92008-08-25 11:58:58 +0300126 [NL80211_ATTR_HT_CAPABILITY] = { .type = NLA_BINARY,
127 .len = NL80211_HT_CAPABILITY_LEN },
Jouni Malinen9aed3cc2009-01-13 16:03:29 +0200128
129 [NL80211_ATTR_MGMT_SUBTYPE] = { .type = NLA_U8 },
130 [NL80211_ATTR_IE] = { .type = NLA_BINARY,
131 .len = IEEE80211_MAX_DATA_LEN },
Johannes Berg2a519312009-02-10 21:25:55 +0100132 [NL80211_ATTR_SCAN_FREQUENCIES] = { .type = NLA_NESTED },
133 [NL80211_ATTR_SCAN_SSIDS] = { .type = NLA_NESTED },
Jouni Malinen636a5d32009-03-19 13:39:22 +0200134
135 [NL80211_ATTR_SSID] = { .type = NLA_BINARY,
136 .len = IEEE80211_MAX_SSID_LEN },
137 [NL80211_ATTR_AUTH_TYPE] = { .type = NLA_U32 },
138 [NL80211_ATTR_REASON_CODE] = { .type = NLA_U16 },
Johannes Berg04a773a2009-04-19 21:24:32 +0200139 [NL80211_ATTR_FREQ_FIXED] = { .type = NLA_FLAG },
Jouni Malinen1965c852009-04-22 21:38:25 +0300140 [NL80211_ATTR_TIMED_OUT] = { .type = NLA_FLAG },
Jouni Malinendc6382c2009-05-06 22:09:37 +0300141 [NL80211_ATTR_USE_MFP] = { .type = NLA_U32 },
Johannes Bergeccb8e82009-05-11 21:57:56 +0300142 [NL80211_ATTR_STA_FLAGS2] = {
143 .len = sizeof(struct nl80211_sta_flag_update),
144 },
Jouni Malinen3f77316c2009-05-11 21:57:57 +0300145 [NL80211_ATTR_CONTROL_PORT] = { .type = NLA_FLAG },
Johannes Bergc0692b82010-08-27 14:26:53 +0300146 [NL80211_ATTR_CONTROL_PORT_ETHERTYPE] = { .type = NLA_U16 },
147 [NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT] = { .type = NLA_FLAG },
Samuel Ortizb23aa672009-07-01 21:26:54 +0200148 [NL80211_ATTR_PRIVACY] = { .type = NLA_FLAG },
149 [NL80211_ATTR_CIPHER_SUITE_GROUP] = { .type = NLA_U32 },
150 [NL80211_ATTR_WPA_VERSIONS] = { .type = NLA_U32 },
Johannes Berg463d0182009-07-14 00:33:35 +0200151 [NL80211_ATTR_PID] = { .type = NLA_U32 },
Felix Fietkau8b787642009-11-10 18:53:10 +0100152 [NL80211_ATTR_4ADDR] = { .type = NLA_U8 },
Samuel Ortiz67fbb162009-11-24 23:59:15 +0100153 [NL80211_ATTR_PMKID] = { .type = NLA_BINARY,
154 .len = WLAN_PMKID_LEN },
Jouni Malinen9588bbd2009-12-23 13:15:41 +0100155 [NL80211_ATTR_DURATION] = { .type = NLA_U32 },
156 [NL80211_ATTR_COOKIE] = { .type = NLA_U64 },
Jouni Malinen13ae75b2009-12-29 12:59:45 +0200157 [NL80211_ATTR_TX_RATES] = { .type = NLA_NESTED },
Jouni Malinen026331c2010-02-15 12:53:10 +0200158 [NL80211_ATTR_FRAME] = { .type = NLA_BINARY,
159 .len = IEEE80211_MAX_DATA_LEN },
160 [NL80211_ATTR_FRAME_MATCH] = { .type = NLA_BINARY, },
Kalle Valoffb9eb32010-02-17 17:58:10 +0200161 [NL80211_ATTR_PS_STATE] = { .type = NLA_U32 },
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +0200162 [NL80211_ATTR_CQM] = { .type = NLA_NESTED, },
Jouni Malinend5cdfac2010-04-04 09:37:19 +0300163 [NL80211_ATTR_LOCAL_STATE_CHANGE] = { .type = NLA_FLAG },
Felix Fietkaufd8aaaf2010-04-27 01:23:35 +0200164 [NL80211_ATTR_AP_ISOLATE] = { .type = NLA_U8 },
Juuso Oikarinen98d2ff82010-06-23 12:12:38 +0300165
166 [NL80211_ATTR_WIPHY_TX_POWER_SETTING] = { .type = NLA_U32 },
167 [NL80211_ATTR_WIPHY_TX_POWER_LEVEL] = { .type = NLA_U32 },
Johannes Berg2e161f72010-08-12 15:38:38 +0200168 [NL80211_ATTR_FRAME_TYPE] = { .type = NLA_U16 },
Johannes Berg55682962007-09-20 13:09:35 -0400169};
170
Johannes Bergb9454e82009-07-08 13:29:08 +0200171/* policy for the attributes */
Alexey Dobriyanb54452b2010-02-18 08:14:31 +0000172static const struct nla_policy nl80211_key_policy[NL80211_KEY_MAX + 1] = {
Johannes Bergfffd0932009-07-08 14:22:54 +0200173 [NL80211_KEY_DATA] = { .type = NLA_BINARY, .len = WLAN_MAX_KEY_LEN },
Johannes Bergb9454e82009-07-08 13:29:08 +0200174 [NL80211_KEY_IDX] = { .type = NLA_U8 },
175 [NL80211_KEY_CIPHER] = { .type = NLA_U32 },
176 [NL80211_KEY_SEQ] = { .type = NLA_BINARY, .len = 8 },
177 [NL80211_KEY_DEFAULT] = { .type = NLA_FLAG },
178 [NL80211_KEY_DEFAULT_MGMT] = { .type = NLA_FLAG },
179};
180
Holger Schuriga0438972009-11-11 11:30:02 +0100181/* ifidx get helper */
182static int nl80211_get_ifidx(struct netlink_callback *cb)
183{
184 int res;
185
186 res = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize,
187 nl80211_fam.attrbuf, nl80211_fam.maxattr,
188 nl80211_policy);
189 if (res)
190 return res;
191
192 if (!nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX])
193 return -EINVAL;
194
195 res = nla_get_u32(nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX]);
196 if (!res)
197 return -EINVAL;
198 return res;
199}
200
Johannes Bergf4a11bb2009-03-27 12:40:28 +0100201/* IE validation */
202static bool is_valid_ie_attr(const struct nlattr *attr)
203{
204 const u8 *pos;
205 int len;
206
207 if (!attr)
208 return true;
209
210 pos = nla_data(attr);
211 len = nla_len(attr);
212
213 while (len) {
214 u8 elemlen;
215
216 if (len < 2)
217 return false;
218 len -= 2;
219
220 elemlen = pos[1];
221 if (elemlen > len)
222 return false;
223
224 len -= elemlen;
225 pos += 2 + elemlen;
226 }
227
228 return true;
229}
230
Johannes Berg55682962007-09-20 13:09:35 -0400231/* message building helper */
232static inline void *nl80211hdr_put(struct sk_buff *skb, u32 pid, u32 seq,
233 int flags, u8 cmd)
234{
235 /* since there is no private header just add the generic one */
236 return genlmsg_put(skb, pid, seq, &nl80211_fam, flags, cmd);
237}
238
Luis R. Rodriguez5dab3b82009-04-02 14:08:08 -0400239static int nl80211_msg_put_channel(struct sk_buff *msg,
240 struct ieee80211_channel *chan)
241{
242 NLA_PUT_U32(msg, NL80211_FREQUENCY_ATTR_FREQ,
243 chan->center_freq);
244
245 if (chan->flags & IEEE80211_CHAN_DISABLED)
246 NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_DISABLED);
247 if (chan->flags & IEEE80211_CHAN_PASSIVE_SCAN)
248 NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_PASSIVE_SCAN);
249 if (chan->flags & IEEE80211_CHAN_NO_IBSS)
250 NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_NO_IBSS);
251 if (chan->flags & IEEE80211_CHAN_RADAR)
252 NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_RADAR);
253
254 NLA_PUT_U32(msg, NL80211_FREQUENCY_ATTR_MAX_TX_POWER,
255 DBM_TO_MBM(chan->max_power));
256
257 return 0;
258
259 nla_put_failure:
260 return -ENOBUFS;
261}
262
Johannes Berg55682962007-09-20 13:09:35 -0400263/* netlink command implementations */
264
Johannes Bergb9454e82009-07-08 13:29:08 +0200265struct key_parse {
266 struct key_params p;
267 int idx;
268 bool def, defmgmt;
269};
270
271static int nl80211_parse_key_new(struct nlattr *key, struct key_parse *k)
272{
273 struct nlattr *tb[NL80211_KEY_MAX + 1];
274 int err = nla_parse_nested(tb, NL80211_KEY_MAX, key,
275 nl80211_key_policy);
276 if (err)
277 return err;
278
279 k->def = !!tb[NL80211_KEY_DEFAULT];
280 k->defmgmt = !!tb[NL80211_KEY_DEFAULT_MGMT];
281
282 if (tb[NL80211_KEY_IDX])
283 k->idx = nla_get_u8(tb[NL80211_KEY_IDX]);
284
285 if (tb[NL80211_KEY_DATA]) {
286 k->p.key = nla_data(tb[NL80211_KEY_DATA]);
287 k->p.key_len = nla_len(tb[NL80211_KEY_DATA]);
288 }
289
290 if (tb[NL80211_KEY_SEQ]) {
291 k->p.seq = nla_data(tb[NL80211_KEY_SEQ]);
292 k->p.seq_len = nla_len(tb[NL80211_KEY_SEQ]);
293 }
294
295 if (tb[NL80211_KEY_CIPHER])
296 k->p.cipher = nla_get_u32(tb[NL80211_KEY_CIPHER]);
297
298 return 0;
299}
300
301static int nl80211_parse_key_old(struct genl_info *info, struct key_parse *k)
302{
303 if (info->attrs[NL80211_ATTR_KEY_DATA]) {
304 k->p.key = nla_data(info->attrs[NL80211_ATTR_KEY_DATA]);
305 k->p.key_len = nla_len(info->attrs[NL80211_ATTR_KEY_DATA]);
306 }
307
308 if (info->attrs[NL80211_ATTR_KEY_SEQ]) {
309 k->p.seq = nla_data(info->attrs[NL80211_ATTR_KEY_SEQ]);
310 k->p.seq_len = nla_len(info->attrs[NL80211_ATTR_KEY_SEQ]);
311 }
312
313 if (info->attrs[NL80211_ATTR_KEY_IDX])
314 k->idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]);
315
316 if (info->attrs[NL80211_ATTR_KEY_CIPHER])
317 k->p.cipher = nla_get_u32(info->attrs[NL80211_ATTR_KEY_CIPHER]);
318
319 k->def = !!info->attrs[NL80211_ATTR_KEY_DEFAULT];
320 k->defmgmt = !!info->attrs[NL80211_ATTR_KEY_DEFAULT_MGMT];
321
322 return 0;
323}
324
325static int nl80211_parse_key(struct genl_info *info, struct key_parse *k)
326{
327 int err;
328
329 memset(k, 0, sizeof(*k));
330 k->idx = -1;
331
332 if (info->attrs[NL80211_ATTR_KEY])
333 err = nl80211_parse_key_new(info->attrs[NL80211_ATTR_KEY], k);
334 else
335 err = nl80211_parse_key_old(info, k);
336
337 if (err)
338 return err;
339
340 if (k->def && k->defmgmt)
341 return -EINVAL;
342
343 if (k->idx != -1) {
344 if (k->defmgmt) {
345 if (k->idx < 4 || k->idx > 5)
346 return -EINVAL;
347 } else if (k->def) {
348 if (k->idx < 0 || k->idx > 3)
349 return -EINVAL;
350 } else {
351 if (k->idx < 0 || k->idx > 5)
352 return -EINVAL;
353 }
354 }
355
356 return 0;
357}
358
Johannes Bergfffd0932009-07-08 14:22:54 +0200359static struct cfg80211_cached_keys *
360nl80211_parse_connkeys(struct cfg80211_registered_device *rdev,
361 struct nlattr *keys)
362{
363 struct key_parse parse;
364 struct nlattr *key;
365 struct cfg80211_cached_keys *result;
366 int rem, err, def = 0;
367
368 result = kzalloc(sizeof(*result), GFP_KERNEL);
369 if (!result)
370 return ERR_PTR(-ENOMEM);
371
372 result->def = -1;
373 result->defmgmt = -1;
374
375 nla_for_each_nested(key, keys, rem) {
376 memset(&parse, 0, sizeof(parse));
377 parse.idx = -1;
378
379 err = nl80211_parse_key_new(key, &parse);
380 if (err)
381 goto error;
382 err = -EINVAL;
383 if (!parse.p.key)
384 goto error;
385 if (parse.idx < 0 || parse.idx > 4)
386 goto error;
387 if (parse.def) {
388 if (def)
389 goto error;
390 def = 1;
391 result->def = parse.idx;
392 } else if (parse.defmgmt)
393 goto error;
394 err = cfg80211_validate_key_settings(rdev, &parse.p,
395 parse.idx, NULL);
396 if (err)
397 goto error;
398 result->params[parse.idx].cipher = parse.p.cipher;
399 result->params[parse.idx].key_len = parse.p.key_len;
400 result->params[parse.idx].key = result->data[parse.idx];
401 memcpy(result->data[parse.idx], parse.p.key, parse.p.key_len);
402 }
403
404 return result;
405 error:
406 kfree(result);
407 return ERR_PTR(err);
408}
409
410static int nl80211_key_allowed(struct wireless_dev *wdev)
411{
412 ASSERT_WDEV_LOCK(wdev);
413
Johannes Bergfffd0932009-07-08 14:22:54 +0200414 switch (wdev->iftype) {
415 case NL80211_IFTYPE_AP:
416 case NL80211_IFTYPE_AP_VLAN:
Johannes Berg074ac8d2010-09-16 14:58:22 +0200417 case NL80211_IFTYPE_P2P_GO:
Johannes Bergfffd0932009-07-08 14:22:54 +0200418 break;
419 case NL80211_IFTYPE_ADHOC:
420 if (!wdev->current_bss)
421 return -ENOLINK;
422 break;
423 case NL80211_IFTYPE_STATION:
Johannes Berg074ac8d2010-09-16 14:58:22 +0200424 case NL80211_IFTYPE_P2P_CLIENT:
Johannes Bergfffd0932009-07-08 14:22:54 +0200425 if (wdev->sme_state != CFG80211_SME_CONNECTED)
426 return -ENOLINK;
427 break;
428 default:
429 return -EINVAL;
430 }
431
432 return 0;
433}
434
Johannes Berg55682962007-09-20 13:09:35 -0400435static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags,
436 struct cfg80211_registered_device *dev)
437{
438 void *hdr;
Johannes Bergee688b002008-01-24 19:38:39 +0100439 struct nlattr *nl_bands, *nl_band;
440 struct nlattr *nl_freqs, *nl_freq;
441 struct nlattr *nl_rates, *nl_rate;
Luis R. Rodriguezf59ac042008-08-29 16:26:43 -0700442 struct nlattr *nl_modes;
Johannes Berg8fdc6212009-03-14 09:34:01 +0100443 struct nlattr *nl_cmds;
Johannes Bergee688b002008-01-24 19:38:39 +0100444 enum ieee80211_band band;
445 struct ieee80211_channel *chan;
446 struct ieee80211_rate *rate;
447 int i;
Luis R. Rodriguezf59ac042008-08-29 16:26:43 -0700448 u16 ifmodes = dev->wiphy.interface_modes;
Johannes Berg2e161f72010-08-12 15:38:38 +0200449 const struct ieee80211_txrx_stypes *mgmt_stypes =
450 dev->wiphy.mgmt_stypes;
Johannes Berg55682962007-09-20 13:09:35 -0400451
452 hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_WIPHY);
453 if (!hdr)
454 return -1;
455
Luis R. Rodriguezb5850a72009-02-21 00:04:19 -0500456 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, dev->wiphy_idx);
Johannes Berg55682962007-09-20 13:09:35 -0400457 NLA_PUT_STRING(msg, NL80211_ATTR_WIPHY_NAME, wiphy_name(&dev->wiphy));
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +0200458
Johannes Bergf5ea9122009-08-07 16:17:38 +0200459 NLA_PUT_U32(msg, NL80211_ATTR_GENERATION,
460 cfg80211_rdev_list_generation);
461
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +0200462 NLA_PUT_U8(msg, NL80211_ATTR_WIPHY_RETRY_SHORT,
463 dev->wiphy.retry_short);
464 NLA_PUT_U8(msg, NL80211_ATTR_WIPHY_RETRY_LONG,
465 dev->wiphy.retry_long);
466 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FRAG_THRESHOLD,
467 dev->wiphy.frag_threshold);
468 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_RTS_THRESHOLD,
469 dev->wiphy.rts_threshold);
Lukáš Turek81077e82009-12-21 22:50:47 +0100470 NLA_PUT_U8(msg, NL80211_ATTR_WIPHY_COVERAGE_CLASS,
471 dev->wiphy.coverage_class);
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +0200472
Johannes Berg2a519312009-02-10 21:25:55 +0100473 NLA_PUT_U8(msg, NL80211_ATTR_MAX_NUM_SCAN_SSIDS,
474 dev->wiphy.max_scan_ssids);
Johannes Berg18a83652009-03-31 12:12:05 +0200475 NLA_PUT_U16(msg, NL80211_ATTR_MAX_SCAN_IE_LEN,
476 dev->wiphy.max_scan_ie_len);
Johannes Bergee688b002008-01-24 19:38:39 +0100477
Johannes Berg25e47c12009-04-02 20:14:06 +0200478 NLA_PUT(msg, NL80211_ATTR_CIPHER_SUITES,
479 sizeof(u32) * dev->wiphy.n_cipher_suites,
480 dev->wiphy.cipher_suites);
481
Samuel Ortiz67fbb162009-11-24 23:59:15 +0100482 NLA_PUT_U8(msg, NL80211_ATTR_MAX_NUM_PMKIDS,
483 dev->wiphy.max_num_pmkids);
484
Johannes Bergc0692b82010-08-27 14:26:53 +0300485 if (dev->wiphy.flags & WIPHY_FLAG_CONTROL_PORT_PROTOCOL)
486 NLA_PUT_FLAG(msg, NL80211_ATTR_CONTROL_PORT_ETHERTYPE);
487
Luis R. Rodriguezf59ac042008-08-29 16:26:43 -0700488 nl_modes = nla_nest_start(msg, NL80211_ATTR_SUPPORTED_IFTYPES);
489 if (!nl_modes)
490 goto nla_put_failure;
491
492 i = 0;
493 while (ifmodes) {
494 if (ifmodes & 1)
495 NLA_PUT_FLAG(msg, i);
496 ifmodes >>= 1;
497 i++;
498 }
499
500 nla_nest_end(msg, nl_modes);
501
Johannes Bergee688b002008-01-24 19:38:39 +0100502 nl_bands = nla_nest_start(msg, NL80211_ATTR_WIPHY_BANDS);
503 if (!nl_bands)
504 goto nla_put_failure;
505
506 for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
507 if (!dev->wiphy.bands[band])
508 continue;
509
510 nl_band = nla_nest_start(msg, band);
511 if (!nl_band)
512 goto nla_put_failure;
513
Johannes Bergd51626d2008-10-09 12:20:13 +0200514 /* add HT info */
515 if (dev->wiphy.bands[band]->ht_cap.ht_supported) {
516 NLA_PUT(msg, NL80211_BAND_ATTR_HT_MCS_SET,
517 sizeof(dev->wiphy.bands[band]->ht_cap.mcs),
518 &dev->wiphy.bands[band]->ht_cap.mcs);
519 NLA_PUT_U16(msg, NL80211_BAND_ATTR_HT_CAPA,
520 dev->wiphy.bands[band]->ht_cap.cap);
521 NLA_PUT_U8(msg, NL80211_BAND_ATTR_HT_AMPDU_FACTOR,
522 dev->wiphy.bands[band]->ht_cap.ampdu_factor);
523 NLA_PUT_U8(msg, NL80211_BAND_ATTR_HT_AMPDU_DENSITY,
524 dev->wiphy.bands[band]->ht_cap.ampdu_density);
525 }
526
Johannes Bergee688b002008-01-24 19:38:39 +0100527 /* add frequencies */
528 nl_freqs = nla_nest_start(msg, NL80211_BAND_ATTR_FREQS);
529 if (!nl_freqs)
530 goto nla_put_failure;
531
532 for (i = 0; i < dev->wiphy.bands[band]->n_channels; i++) {
533 nl_freq = nla_nest_start(msg, i);
534 if (!nl_freq)
535 goto nla_put_failure;
536
537 chan = &dev->wiphy.bands[band]->channels[i];
Johannes Bergee688b002008-01-24 19:38:39 +0100538
Luis R. Rodriguez5dab3b82009-04-02 14:08:08 -0400539 if (nl80211_msg_put_channel(msg, chan))
540 goto nla_put_failure;
Jouni Malinene2f367f262008-11-21 19:01:30 +0200541
Johannes Bergee688b002008-01-24 19:38:39 +0100542 nla_nest_end(msg, nl_freq);
543 }
544
545 nla_nest_end(msg, nl_freqs);
546
547 /* add bitrates */
548 nl_rates = nla_nest_start(msg, NL80211_BAND_ATTR_RATES);
549 if (!nl_rates)
550 goto nla_put_failure;
551
552 for (i = 0; i < dev->wiphy.bands[band]->n_bitrates; i++) {
553 nl_rate = nla_nest_start(msg, i);
554 if (!nl_rate)
555 goto nla_put_failure;
556
557 rate = &dev->wiphy.bands[band]->bitrates[i];
558 NLA_PUT_U32(msg, NL80211_BITRATE_ATTR_RATE,
559 rate->bitrate);
560 if (rate->flags & IEEE80211_RATE_SHORT_PREAMBLE)
561 NLA_PUT_FLAG(msg,
562 NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE);
563
564 nla_nest_end(msg, nl_rate);
565 }
566
567 nla_nest_end(msg, nl_rates);
568
569 nla_nest_end(msg, nl_band);
570 }
571 nla_nest_end(msg, nl_bands);
572
Johannes Berg8fdc6212009-03-14 09:34:01 +0100573 nl_cmds = nla_nest_start(msg, NL80211_ATTR_SUPPORTED_COMMANDS);
574 if (!nl_cmds)
575 goto nla_put_failure;
576
577 i = 0;
578#define CMD(op, n) \
579 do { \
580 if (dev->ops->op) { \
581 i++; \
582 NLA_PUT_U32(msg, i, NL80211_CMD_ ## n); \
583 } \
584 } while (0)
585
586 CMD(add_virtual_intf, NEW_INTERFACE);
587 CMD(change_virtual_intf, SET_INTERFACE);
588 CMD(add_key, NEW_KEY);
589 CMD(add_beacon, NEW_BEACON);
590 CMD(add_station, NEW_STATION);
591 CMD(add_mpath, NEW_MPATH);
592 CMD(set_mesh_params, SET_MESH_PARAMS);
593 CMD(change_bss, SET_BSS);
Jouni Malinen636a5d32009-03-19 13:39:22 +0200594 CMD(auth, AUTHENTICATE);
595 CMD(assoc, ASSOCIATE);
596 CMD(deauth, DEAUTHENTICATE);
597 CMD(disassoc, DISASSOCIATE);
Johannes Berg04a773a2009-04-19 21:24:32 +0200598 CMD(join_ibss, JOIN_IBSS);
Samuel Ortiz67fbb162009-11-24 23:59:15 +0100599 CMD(set_pmksa, SET_PMKSA);
600 CMD(del_pmksa, DEL_PMKSA);
601 CMD(flush_pmksa, FLUSH_PMKSA);
Jouni Malinen9588bbd2009-12-23 13:15:41 +0100602 CMD(remain_on_channel, REMAIN_ON_CHANNEL);
Jouni Malinen13ae75b2009-12-29 12:59:45 +0200603 CMD(set_bitrate_mask, SET_TX_BITRATE_MASK);
Johannes Berg2e161f72010-08-12 15:38:38 +0200604 CMD(mgmt_tx, FRAME);
Johannes Berg5be83de2009-11-19 00:56:28 +0100605 if (dev->wiphy.flags & WIPHY_FLAG_NETNS_OK) {
Johannes Berg463d0182009-07-14 00:33:35 +0200606 i++;
607 NLA_PUT_U32(msg, i, NL80211_CMD_SET_WIPHY_NETNS);
608 }
Johannes Bergf444de02010-05-05 15:25:02 +0200609 CMD(set_channel, SET_CHANNEL);
Bill Jordane8347eb2010-10-01 13:54:28 -0400610 CMD(set_wds_peer, SET_WDS_PEER);
Johannes Berg8fdc6212009-03-14 09:34:01 +0100611
612#undef CMD
Samuel Ortizb23aa672009-07-01 21:26:54 +0200613
Johannes Berg6829c872009-07-02 09:13:27 +0200614 if (dev->ops->connect || dev->ops->auth) {
Samuel Ortizb23aa672009-07-01 21:26:54 +0200615 i++;
616 NLA_PUT_U32(msg, i, NL80211_CMD_CONNECT);
617 }
618
Johannes Berg6829c872009-07-02 09:13:27 +0200619 if (dev->ops->disconnect || dev->ops->deauth) {
Samuel Ortizb23aa672009-07-01 21:26:54 +0200620 i++;
621 NLA_PUT_U32(msg, i, NL80211_CMD_DISCONNECT);
622 }
623
Johannes Berg8fdc6212009-03-14 09:34:01 +0100624 nla_nest_end(msg, nl_cmds);
625
Johannes Berg2e161f72010-08-12 15:38:38 +0200626 if (mgmt_stypes) {
627 u16 stypes;
628 struct nlattr *nl_ftypes, *nl_ifs;
629 enum nl80211_iftype ift;
630
631 nl_ifs = nla_nest_start(msg, NL80211_ATTR_TX_FRAME_TYPES);
632 if (!nl_ifs)
633 goto nla_put_failure;
634
635 for (ift = 0; ift < NUM_NL80211_IFTYPES; ift++) {
636 nl_ftypes = nla_nest_start(msg, ift);
637 if (!nl_ftypes)
638 goto nla_put_failure;
639 i = 0;
640 stypes = mgmt_stypes[ift].tx;
641 while (stypes) {
642 if (stypes & 1)
643 NLA_PUT_U16(msg, NL80211_ATTR_FRAME_TYPE,
644 (i << 4) | IEEE80211_FTYPE_MGMT);
645 stypes >>= 1;
646 i++;
647 }
648 nla_nest_end(msg, nl_ftypes);
649 }
650
Johannes Berg74b70a42010-08-24 12:15:53 +0200651 nla_nest_end(msg, nl_ifs);
652
Johannes Berg2e161f72010-08-12 15:38:38 +0200653 nl_ifs = nla_nest_start(msg, NL80211_ATTR_RX_FRAME_TYPES);
654 if (!nl_ifs)
655 goto nla_put_failure;
656
657 for (ift = 0; ift < NUM_NL80211_IFTYPES; ift++) {
658 nl_ftypes = nla_nest_start(msg, ift);
659 if (!nl_ftypes)
660 goto nla_put_failure;
661 i = 0;
662 stypes = mgmt_stypes[ift].rx;
663 while (stypes) {
664 if (stypes & 1)
665 NLA_PUT_U16(msg, NL80211_ATTR_FRAME_TYPE,
666 (i << 4) | IEEE80211_FTYPE_MGMT);
667 stypes >>= 1;
668 i++;
669 }
670 nla_nest_end(msg, nl_ftypes);
671 }
672 nla_nest_end(msg, nl_ifs);
673 }
674
Johannes Berg55682962007-09-20 13:09:35 -0400675 return genlmsg_end(msg, hdr);
676
677 nla_put_failure:
Thomas Grafbc3ed282008-06-03 16:36:54 -0700678 genlmsg_cancel(msg, hdr);
679 return -EMSGSIZE;
Johannes Berg55682962007-09-20 13:09:35 -0400680}
681
682static int nl80211_dump_wiphy(struct sk_buff *skb, struct netlink_callback *cb)
683{
684 int idx = 0;
685 int start = cb->args[0];
686 struct cfg80211_registered_device *dev;
687
Luis R. Rodrigueza1794392009-02-21 00:04:21 -0500688 mutex_lock(&cfg80211_mutex);
Johannes Berg79c97e92009-07-07 03:56:12 +0200689 list_for_each_entry(dev, &cfg80211_rdev_list, list) {
Johannes Berg463d0182009-07-14 00:33:35 +0200690 if (!net_eq(wiphy_net(&dev->wiphy), sock_net(skb->sk)))
691 continue;
Julius Volzb4637272008-07-08 14:02:19 +0200692 if (++idx <= start)
Johannes Berg55682962007-09-20 13:09:35 -0400693 continue;
694 if (nl80211_send_wiphy(skb, NETLINK_CB(cb->skb).pid,
695 cb->nlh->nlmsg_seq, NLM_F_MULTI,
Julius Volzb4637272008-07-08 14:02:19 +0200696 dev) < 0) {
697 idx--;
Johannes Berg55682962007-09-20 13:09:35 -0400698 break;
Julius Volzb4637272008-07-08 14:02:19 +0200699 }
Johannes Berg55682962007-09-20 13:09:35 -0400700 }
Luis R. Rodrigueza1794392009-02-21 00:04:21 -0500701 mutex_unlock(&cfg80211_mutex);
Johannes Berg55682962007-09-20 13:09:35 -0400702
703 cb->args[0] = idx;
704
705 return skb->len;
706}
707
708static int nl80211_get_wiphy(struct sk_buff *skb, struct genl_info *info)
709{
710 struct sk_buff *msg;
Johannes Berg4c476992010-10-04 21:36:35 +0200711 struct cfg80211_registered_device *dev = info->user_ptr[0];
Johannes Berg55682962007-09-20 13:09:35 -0400712
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -0700713 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg55682962007-09-20 13:09:35 -0400714 if (!msg)
Johannes Berg4c476992010-10-04 21:36:35 +0200715 return -ENOMEM;
Johannes Berg55682962007-09-20 13:09:35 -0400716
Johannes Berg4c476992010-10-04 21:36:35 +0200717 if (nl80211_send_wiphy(msg, info->snd_pid, info->snd_seq, 0, dev) < 0) {
718 nlmsg_free(msg);
719 return -ENOBUFS;
720 }
Johannes Berg55682962007-09-20 13:09:35 -0400721
Johannes Berg134e6372009-07-10 09:51:34 +0000722 return genlmsg_reply(msg, info);
Johannes Berg55682962007-09-20 13:09:35 -0400723}
724
Jouni Malinen31888482008-10-30 16:59:24 +0200725static const struct nla_policy txq_params_policy[NL80211_TXQ_ATTR_MAX + 1] = {
726 [NL80211_TXQ_ATTR_QUEUE] = { .type = NLA_U8 },
727 [NL80211_TXQ_ATTR_TXOP] = { .type = NLA_U16 },
728 [NL80211_TXQ_ATTR_CWMIN] = { .type = NLA_U16 },
729 [NL80211_TXQ_ATTR_CWMAX] = { .type = NLA_U16 },
730 [NL80211_TXQ_ATTR_AIFS] = { .type = NLA_U8 },
731};
732
733static int parse_txq_params(struct nlattr *tb[],
734 struct ieee80211_txq_params *txq_params)
735{
736 if (!tb[NL80211_TXQ_ATTR_QUEUE] || !tb[NL80211_TXQ_ATTR_TXOP] ||
737 !tb[NL80211_TXQ_ATTR_CWMIN] || !tb[NL80211_TXQ_ATTR_CWMAX] ||
738 !tb[NL80211_TXQ_ATTR_AIFS])
739 return -EINVAL;
740
741 txq_params->queue = nla_get_u8(tb[NL80211_TXQ_ATTR_QUEUE]);
742 txq_params->txop = nla_get_u16(tb[NL80211_TXQ_ATTR_TXOP]);
743 txq_params->cwmin = nla_get_u16(tb[NL80211_TXQ_ATTR_CWMIN]);
744 txq_params->cwmax = nla_get_u16(tb[NL80211_TXQ_ATTR_CWMAX]);
745 txq_params->aifs = nla_get_u8(tb[NL80211_TXQ_ATTR_AIFS]);
746
747 return 0;
748}
749
Johannes Bergf444de02010-05-05 15:25:02 +0200750static bool nl80211_can_set_dev_channel(struct wireless_dev *wdev)
751{
752 /*
753 * You can only set the channel explicitly for AP, mesh
754 * and WDS type interfaces; all others have their channel
755 * managed via their respective "establish a connection"
756 * command (connect, join, ...)
757 *
758 * Monitors are special as they are normally slaved to
759 * whatever else is going on, so they behave as though
760 * you tried setting the wiphy channel itself.
761 */
762 return !wdev ||
763 wdev->iftype == NL80211_IFTYPE_AP ||
764 wdev->iftype == NL80211_IFTYPE_WDS ||
765 wdev->iftype == NL80211_IFTYPE_MESH_POINT ||
Johannes Berg074ac8d2010-09-16 14:58:22 +0200766 wdev->iftype == NL80211_IFTYPE_MONITOR ||
767 wdev->iftype == NL80211_IFTYPE_P2P_GO;
Johannes Bergf444de02010-05-05 15:25:02 +0200768}
769
770static int __nl80211_set_channel(struct cfg80211_registered_device *rdev,
771 struct wireless_dev *wdev,
772 struct genl_info *info)
773{
774 enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT;
775 u32 freq;
776 int result;
777
778 if (!info->attrs[NL80211_ATTR_WIPHY_FREQ])
779 return -EINVAL;
780
781 if (!nl80211_can_set_dev_channel(wdev))
782 return -EOPNOTSUPP;
783
784 if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) {
785 channel_type = nla_get_u32(info->attrs[
786 NL80211_ATTR_WIPHY_CHANNEL_TYPE]);
787 if (channel_type != NL80211_CHAN_NO_HT &&
788 channel_type != NL80211_CHAN_HT20 &&
789 channel_type != NL80211_CHAN_HT40PLUS &&
790 channel_type != NL80211_CHAN_HT40MINUS)
791 return -EINVAL;
792 }
793
794 freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]);
795
796 mutex_lock(&rdev->devlist_mtx);
797 if (wdev) {
798 wdev_lock(wdev);
799 result = cfg80211_set_freq(rdev, wdev, freq, channel_type);
800 wdev_unlock(wdev);
801 } else {
802 result = cfg80211_set_freq(rdev, NULL, freq, channel_type);
803 }
804 mutex_unlock(&rdev->devlist_mtx);
805
806 return result;
807}
808
809static int nl80211_set_channel(struct sk_buff *skb, struct genl_info *info)
810{
Johannes Berg4c476992010-10-04 21:36:35 +0200811 struct cfg80211_registered_device *rdev = info->user_ptr[0];
812 struct net_device *netdev = info->user_ptr[1];
Johannes Bergf444de02010-05-05 15:25:02 +0200813
Johannes Berg4c476992010-10-04 21:36:35 +0200814 return __nl80211_set_channel(rdev, netdev->ieee80211_ptr, info);
Johannes Bergf444de02010-05-05 15:25:02 +0200815}
816
Bill Jordane8347eb2010-10-01 13:54:28 -0400817static int nl80211_set_wds_peer(struct sk_buff *skb, struct genl_info *info)
818{
819 struct cfg80211_registered_device *rdev;
820 struct wireless_dev *wdev;
821 struct net_device *dev;
822 u8 *bssid;
823 int err;
824
825 if (!info->attrs[NL80211_ATTR_MAC])
826 return -EINVAL;
827
828 rtnl_lock();
829
830 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
831 if (err)
832 goto unlock_rtnl;
833
834 wdev = dev->ieee80211_ptr;
835
836 if (netif_running(dev)) {
837 err = -EBUSY;
838 goto out;
839 }
840
841 if (!rdev->ops->set_wds_peer) {
842 err = -EOPNOTSUPP;
843 goto out;
844 }
845
846 if (wdev->iftype != NL80211_IFTYPE_WDS) {
847 err = -EOPNOTSUPP;
848 goto out;
849 }
850
851 bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
852 err = rdev->ops->set_wds_peer(wdev->wiphy, dev, bssid);
853
854out:
855 cfg80211_unlock_rdev(rdev);
856 dev_put(dev);
857unlock_rtnl:
858 rtnl_unlock();
859
860 return err;
861}
862
863
Johannes Berg55682962007-09-20 13:09:35 -0400864static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
865{
866 struct cfg80211_registered_device *rdev;
Johannes Bergf444de02010-05-05 15:25:02 +0200867 struct net_device *netdev = NULL;
868 struct wireless_dev *wdev;
Bill Jordana1e567c2010-09-10 11:22:32 -0400869 int result = 0, rem_txq_params = 0;
Jouni Malinen31888482008-10-30 16:59:24 +0200870 struct nlattr *nl_txq_params;
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +0200871 u32 changed;
872 u8 retry_short = 0, retry_long = 0;
873 u32 frag_threshold = 0, rts_threshold = 0;
Lukáš Turek81077e82009-12-21 22:50:47 +0100874 u8 coverage_class = 0;
Johannes Berg55682962007-09-20 13:09:35 -0400875
Johannes Bergf444de02010-05-05 15:25:02 +0200876 /*
877 * Try to find the wiphy and netdev. Normally this
878 * function shouldn't need the netdev, but this is
879 * done for backward compatibility -- previously
880 * setting the channel was done per wiphy, but now
881 * it is per netdev. Previous userland like hostapd
882 * also passed a netdev to set_wiphy, so that it is
883 * possible to let that go to the right netdev!
884 */
Johannes Berg4bbf4d52009-03-24 09:35:46 +0100885 mutex_lock(&cfg80211_mutex);
886
Johannes Bergf444de02010-05-05 15:25:02 +0200887 if (info->attrs[NL80211_ATTR_IFINDEX]) {
888 int ifindex = nla_get_u32(info->attrs[NL80211_ATTR_IFINDEX]);
889
890 netdev = dev_get_by_index(genl_info_net(info), ifindex);
891 if (netdev && netdev->ieee80211_ptr) {
892 rdev = wiphy_to_dev(netdev->ieee80211_ptr->wiphy);
893 mutex_lock(&rdev->mtx);
894 } else
895 netdev = NULL;
Johannes Berg4bbf4d52009-03-24 09:35:46 +0100896 }
897
Johannes Bergf444de02010-05-05 15:25:02 +0200898 if (!netdev) {
899 rdev = __cfg80211_rdev_from_info(info);
900 if (IS_ERR(rdev)) {
901 mutex_unlock(&cfg80211_mutex);
Johannes Berg4c476992010-10-04 21:36:35 +0200902 return PTR_ERR(rdev);
Johannes Bergf444de02010-05-05 15:25:02 +0200903 }
904 wdev = NULL;
905 netdev = NULL;
906 result = 0;
907
908 mutex_lock(&rdev->mtx);
909 } else if (netif_running(netdev) &&
910 nl80211_can_set_dev_channel(netdev->ieee80211_ptr))
911 wdev = netdev->ieee80211_ptr;
912 else
913 wdev = NULL;
914
915 /*
916 * end workaround code, by now the rdev is available
917 * and locked, and wdev may or may not be NULL.
918 */
Johannes Berg4bbf4d52009-03-24 09:35:46 +0100919
920 if (info->attrs[NL80211_ATTR_WIPHY_NAME])
Jouni Malinen31888482008-10-30 16:59:24 +0200921 result = cfg80211_dev_rename(
922 rdev, nla_data(info->attrs[NL80211_ATTR_WIPHY_NAME]));
Johannes Berg4bbf4d52009-03-24 09:35:46 +0100923
924 mutex_unlock(&cfg80211_mutex);
925
926 if (result)
927 goto bad_res;
Johannes Berg55682962007-09-20 13:09:35 -0400928
Jouni Malinen31888482008-10-30 16:59:24 +0200929 if (info->attrs[NL80211_ATTR_WIPHY_TXQ_PARAMS]) {
930 struct ieee80211_txq_params txq_params;
931 struct nlattr *tb[NL80211_TXQ_ATTR_MAX + 1];
932
933 if (!rdev->ops->set_txq_params) {
934 result = -EOPNOTSUPP;
935 goto bad_res;
936 }
937
938 nla_for_each_nested(nl_txq_params,
939 info->attrs[NL80211_ATTR_WIPHY_TXQ_PARAMS],
940 rem_txq_params) {
941 nla_parse(tb, NL80211_TXQ_ATTR_MAX,
942 nla_data(nl_txq_params),
943 nla_len(nl_txq_params),
944 txq_params_policy);
945 result = parse_txq_params(tb, &txq_params);
946 if (result)
947 goto bad_res;
948
949 result = rdev->ops->set_txq_params(&rdev->wiphy,
950 &txq_params);
951 if (result)
952 goto bad_res;
953 }
954 }
955
Jouni Malinen72bdcf32008-11-26 16:15:24 +0200956 if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
Johannes Bergf444de02010-05-05 15:25:02 +0200957 result = __nl80211_set_channel(rdev, wdev, info);
Jouni Malinen72bdcf32008-11-26 16:15:24 +0200958 if (result)
959 goto bad_res;
960 }
961
Juuso Oikarinen98d2ff82010-06-23 12:12:38 +0300962 if (info->attrs[NL80211_ATTR_WIPHY_TX_POWER_SETTING]) {
963 enum nl80211_tx_power_setting type;
964 int idx, mbm = 0;
965
966 if (!rdev->ops->set_tx_power) {
Jiri Slaby60ea3852010-07-07 15:02:46 +0200967 result = -EOPNOTSUPP;
Juuso Oikarinen98d2ff82010-06-23 12:12:38 +0300968 goto bad_res;
969 }
970
971 idx = NL80211_ATTR_WIPHY_TX_POWER_SETTING;
972 type = nla_get_u32(info->attrs[idx]);
973
974 if (!info->attrs[NL80211_ATTR_WIPHY_TX_POWER_LEVEL] &&
975 (type != NL80211_TX_POWER_AUTOMATIC)) {
976 result = -EINVAL;
977 goto bad_res;
978 }
979
980 if (type != NL80211_TX_POWER_AUTOMATIC) {
981 idx = NL80211_ATTR_WIPHY_TX_POWER_LEVEL;
982 mbm = nla_get_u32(info->attrs[idx]);
983 }
984
985 result = rdev->ops->set_tx_power(&rdev->wiphy, type, mbm);
986 if (result)
987 goto bad_res;
988 }
989
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +0200990 changed = 0;
991
992 if (info->attrs[NL80211_ATTR_WIPHY_RETRY_SHORT]) {
993 retry_short = nla_get_u8(
994 info->attrs[NL80211_ATTR_WIPHY_RETRY_SHORT]);
995 if (retry_short == 0) {
996 result = -EINVAL;
997 goto bad_res;
998 }
999 changed |= WIPHY_PARAM_RETRY_SHORT;
1000 }
1001
1002 if (info->attrs[NL80211_ATTR_WIPHY_RETRY_LONG]) {
1003 retry_long = nla_get_u8(
1004 info->attrs[NL80211_ATTR_WIPHY_RETRY_LONG]);
1005 if (retry_long == 0) {
1006 result = -EINVAL;
1007 goto bad_res;
1008 }
1009 changed |= WIPHY_PARAM_RETRY_LONG;
1010 }
1011
1012 if (info->attrs[NL80211_ATTR_WIPHY_FRAG_THRESHOLD]) {
1013 frag_threshold = nla_get_u32(
1014 info->attrs[NL80211_ATTR_WIPHY_FRAG_THRESHOLD]);
1015 if (frag_threshold < 256) {
1016 result = -EINVAL;
1017 goto bad_res;
1018 }
1019 if (frag_threshold != (u32) -1) {
1020 /*
1021 * Fragments (apart from the last one) are required to
1022 * have even length. Make the fragmentation code
1023 * simpler by stripping LSB should someone try to use
1024 * odd threshold value.
1025 */
1026 frag_threshold &= ~0x1;
1027 }
1028 changed |= WIPHY_PARAM_FRAG_THRESHOLD;
1029 }
1030
1031 if (info->attrs[NL80211_ATTR_WIPHY_RTS_THRESHOLD]) {
1032 rts_threshold = nla_get_u32(
1033 info->attrs[NL80211_ATTR_WIPHY_RTS_THRESHOLD]);
1034 changed |= WIPHY_PARAM_RTS_THRESHOLD;
1035 }
1036
Lukáš Turek81077e82009-12-21 22:50:47 +01001037 if (info->attrs[NL80211_ATTR_WIPHY_COVERAGE_CLASS]) {
1038 coverage_class = nla_get_u8(
1039 info->attrs[NL80211_ATTR_WIPHY_COVERAGE_CLASS]);
1040 changed |= WIPHY_PARAM_COVERAGE_CLASS;
1041 }
1042
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +02001043 if (changed) {
1044 u8 old_retry_short, old_retry_long;
1045 u32 old_frag_threshold, old_rts_threshold;
Lukáš Turek81077e82009-12-21 22:50:47 +01001046 u8 old_coverage_class;
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +02001047
1048 if (!rdev->ops->set_wiphy_params) {
1049 result = -EOPNOTSUPP;
1050 goto bad_res;
1051 }
1052
1053 old_retry_short = rdev->wiphy.retry_short;
1054 old_retry_long = rdev->wiphy.retry_long;
1055 old_frag_threshold = rdev->wiphy.frag_threshold;
1056 old_rts_threshold = rdev->wiphy.rts_threshold;
Lukáš Turek81077e82009-12-21 22:50:47 +01001057 old_coverage_class = rdev->wiphy.coverage_class;
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +02001058
1059 if (changed & WIPHY_PARAM_RETRY_SHORT)
1060 rdev->wiphy.retry_short = retry_short;
1061 if (changed & WIPHY_PARAM_RETRY_LONG)
1062 rdev->wiphy.retry_long = retry_long;
1063 if (changed & WIPHY_PARAM_FRAG_THRESHOLD)
1064 rdev->wiphy.frag_threshold = frag_threshold;
1065 if (changed & WIPHY_PARAM_RTS_THRESHOLD)
1066 rdev->wiphy.rts_threshold = rts_threshold;
Lukáš Turek81077e82009-12-21 22:50:47 +01001067 if (changed & WIPHY_PARAM_COVERAGE_CLASS)
1068 rdev->wiphy.coverage_class = coverage_class;
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +02001069
1070 result = rdev->ops->set_wiphy_params(&rdev->wiphy, changed);
1071 if (result) {
1072 rdev->wiphy.retry_short = old_retry_short;
1073 rdev->wiphy.retry_long = old_retry_long;
1074 rdev->wiphy.frag_threshold = old_frag_threshold;
1075 rdev->wiphy.rts_threshold = old_rts_threshold;
Lukáš Turek81077e82009-12-21 22:50:47 +01001076 rdev->wiphy.coverage_class = old_coverage_class;
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +02001077 }
1078 }
Jouni Malinen72bdcf32008-11-26 16:15:24 +02001079
Johannes Berg306d6112008-12-08 12:39:04 +01001080 bad_res:
Johannes Berg4bbf4d52009-03-24 09:35:46 +01001081 mutex_unlock(&rdev->mtx);
Johannes Bergf444de02010-05-05 15:25:02 +02001082 if (netdev)
1083 dev_put(netdev);
Johannes Berg55682962007-09-20 13:09:35 -04001084 return result;
1085}
1086
1087
1088static int nl80211_send_iface(struct sk_buff *msg, u32 pid, u32 seq, int flags,
Johannes Bergd7264052009-04-19 16:23:20 +02001089 struct cfg80211_registered_device *rdev,
Johannes Berg55682962007-09-20 13:09:35 -04001090 struct net_device *dev)
1091{
1092 void *hdr;
1093
1094 hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_INTERFACE);
1095 if (!hdr)
1096 return -1;
1097
1098 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
Johannes Bergd7264052009-04-19 16:23:20 +02001099 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
Johannes Berg55682962007-09-20 13:09:35 -04001100 NLA_PUT_STRING(msg, NL80211_ATTR_IFNAME, dev->name);
Johannes Berg60719ff2008-09-16 14:55:09 +02001101 NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, dev->ieee80211_ptr->iftype);
Johannes Bergf5ea9122009-08-07 16:17:38 +02001102
1103 NLA_PUT_U32(msg, NL80211_ATTR_GENERATION,
1104 rdev->devlist_generation ^
1105 (cfg80211_rdev_list_generation << 2));
1106
Johannes Berg55682962007-09-20 13:09:35 -04001107 return genlmsg_end(msg, hdr);
1108
1109 nla_put_failure:
Thomas Grafbc3ed282008-06-03 16:36:54 -07001110 genlmsg_cancel(msg, hdr);
1111 return -EMSGSIZE;
Johannes Berg55682962007-09-20 13:09:35 -04001112}
1113
1114static int nl80211_dump_interface(struct sk_buff *skb, struct netlink_callback *cb)
1115{
1116 int wp_idx = 0;
1117 int if_idx = 0;
1118 int wp_start = cb->args[0];
1119 int if_start = cb->args[1];
Johannes Bergf5ea9122009-08-07 16:17:38 +02001120 struct cfg80211_registered_device *rdev;
Johannes Berg55682962007-09-20 13:09:35 -04001121 struct wireless_dev *wdev;
1122
Luis R. Rodrigueza1794392009-02-21 00:04:21 -05001123 mutex_lock(&cfg80211_mutex);
Johannes Bergf5ea9122009-08-07 16:17:38 +02001124 list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
1125 if (!net_eq(wiphy_net(&rdev->wiphy), sock_net(skb->sk)))
Johannes Berg463d0182009-07-14 00:33:35 +02001126 continue;
Johannes Bergbba95fe2008-07-29 13:22:51 +02001127 if (wp_idx < wp_start) {
1128 wp_idx++;
Johannes Berg55682962007-09-20 13:09:35 -04001129 continue;
Johannes Bergbba95fe2008-07-29 13:22:51 +02001130 }
Johannes Berg55682962007-09-20 13:09:35 -04001131 if_idx = 0;
1132
Johannes Bergf5ea9122009-08-07 16:17:38 +02001133 mutex_lock(&rdev->devlist_mtx);
1134 list_for_each_entry(wdev, &rdev->netdev_list, list) {
Johannes Bergbba95fe2008-07-29 13:22:51 +02001135 if (if_idx < if_start) {
1136 if_idx++;
Johannes Berg55682962007-09-20 13:09:35 -04001137 continue;
Johannes Bergbba95fe2008-07-29 13:22:51 +02001138 }
Johannes Berg55682962007-09-20 13:09:35 -04001139 if (nl80211_send_iface(skb, NETLINK_CB(cb->skb).pid,
1140 cb->nlh->nlmsg_seq, NLM_F_MULTI,
Johannes Bergf5ea9122009-08-07 16:17:38 +02001141 rdev, wdev->netdev) < 0) {
1142 mutex_unlock(&rdev->devlist_mtx);
Johannes Bergbba95fe2008-07-29 13:22:51 +02001143 goto out;
1144 }
1145 if_idx++;
Johannes Berg55682962007-09-20 13:09:35 -04001146 }
Johannes Bergf5ea9122009-08-07 16:17:38 +02001147 mutex_unlock(&rdev->devlist_mtx);
Johannes Bergbba95fe2008-07-29 13:22:51 +02001148
1149 wp_idx++;
Johannes Berg55682962007-09-20 13:09:35 -04001150 }
Johannes Bergbba95fe2008-07-29 13:22:51 +02001151 out:
Luis R. Rodrigueza1794392009-02-21 00:04:21 -05001152 mutex_unlock(&cfg80211_mutex);
Johannes Berg55682962007-09-20 13:09:35 -04001153
1154 cb->args[0] = wp_idx;
1155 cb->args[1] = if_idx;
1156
1157 return skb->len;
1158}
1159
1160static int nl80211_get_interface(struct sk_buff *skb, struct genl_info *info)
1161{
1162 struct sk_buff *msg;
Johannes Berg4c476992010-10-04 21:36:35 +02001163 struct cfg80211_registered_device *dev = info->user_ptr[0];
1164 struct net_device *netdev = info->user_ptr[1];
Johannes Berg55682962007-09-20 13:09:35 -04001165
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07001166 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg55682962007-09-20 13:09:35 -04001167 if (!msg)
Johannes Berg4c476992010-10-04 21:36:35 +02001168 return -ENOMEM;
Johannes Berg55682962007-09-20 13:09:35 -04001169
Johannes Bergd7264052009-04-19 16:23:20 +02001170 if (nl80211_send_iface(msg, info->snd_pid, info->snd_seq, 0,
Johannes Berg4c476992010-10-04 21:36:35 +02001171 dev, netdev) < 0) {
1172 nlmsg_free(msg);
1173 return -ENOBUFS;
1174 }
Johannes Berg55682962007-09-20 13:09:35 -04001175
Johannes Berg134e6372009-07-10 09:51:34 +00001176 return genlmsg_reply(msg, info);
Johannes Berg55682962007-09-20 13:09:35 -04001177}
1178
Michael Wu66f7ac52008-01-31 19:48:22 +01001179static const struct nla_policy mntr_flags_policy[NL80211_MNTR_FLAG_MAX + 1] = {
1180 [NL80211_MNTR_FLAG_FCSFAIL] = { .type = NLA_FLAG },
1181 [NL80211_MNTR_FLAG_PLCPFAIL] = { .type = NLA_FLAG },
1182 [NL80211_MNTR_FLAG_CONTROL] = { .type = NLA_FLAG },
1183 [NL80211_MNTR_FLAG_OTHER_BSS] = { .type = NLA_FLAG },
1184 [NL80211_MNTR_FLAG_COOK_FRAMES] = { .type = NLA_FLAG },
1185};
1186
1187static int parse_monitor_flags(struct nlattr *nla, u32 *mntrflags)
1188{
1189 struct nlattr *flags[NL80211_MNTR_FLAG_MAX + 1];
1190 int flag;
1191
1192 *mntrflags = 0;
1193
1194 if (!nla)
1195 return -EINVAL;
1196
1197 if (nla_parse_nested(flags, NL80211_MNTR_FLAG_MAX,
1198 nla, mntr_flags_policy))
1199 return -EINVAL;
1200
1201 for (flag = 1; flag <= NL80211_MNTR_FLAG_MAX; flag++)
1202 if (flags[flag])
1203 *mntrflags |= (1<<flag);
1204
1205 return 0;
1206}
1207
Johannes Berg9bc383d2009-11-19 11:55:19 +01001208static int nl80211_valid_4addr(struct cfg80211_registered_device *rdev,
Johannes Bergad4bb6f2009-11-19 00:56:30 +01001209 struct net_device *netdev, u8 use_4addr,
1210 enum nl80211_iftype iftype)
Johannes Berg9bc383d2009-11-19 11:55:19 +01001211{
Johannes Bergad4bb6f2009-11-19 00:56:30 +01001212 if (!use_4addr) {
Jiri Pirkof350a0a82010-06-15 06:50:45 +00001213 if (netdev && (netdev->priv_flags & IFF_BRIDGE_PORT))
Johannes Bergad4bb6f2009-11-19 00:56:30 +01001214 return -EBUSY;
Johannes Berg9bc383d2009-11-19 11:55:19 +01001215 return 0;
Johannes Bergad4bb6f2009-11-19 00:56:30 +01001216 }
Johannes Berg9bc383d2009-11-19 11:55:19 +01001217
1218 switch (iftype) {
1219 case NL80211_IFTYPE_AP_VLAN:
1220 if (rdev->wiphy.flags & WIPHY_FLAG_4ADDR_AP)
1221 return 0;
1222 break;
1223 case NL80211_IFTYPE_STATION:
1224 if (rdev->wiphy.flags & WIPHY_FLAG_4ADDR_STATION)
1225 return 0;
1226 break;
1227 default:
1228 break;
1229 }
1230
1231 return -EOPNOTSUPP;
1232}
1233
Johannes Berg55682962007-09-20 13:09:35 -04001234static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info)
1235{
Johannes Berg4c476992010-10-04 21:36:35 +02001236 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001237 struct vif_params params;
Johannes Berge36d56b2009-06-09 21:04:43 +02001238 int err;
Johannes Berg04a773a2009-04-19 21:24:32 +02001239 enum nl80211_iftype otype, ntype;
Johannes Berg4c476992010-10-04 21:36:35 +02001240 struct net_device *dev = info->user_ptr[1];
Johannes Berg92ffe052008-09-16 20:39:36 +02001241 u32 _flags, *flags = NULL;
Johannes Bergac7f9cf2009-03-21 17:07:59 +01001242 bool change = false;
Johannes Berg55682962007-09-20 13:09:35 -04001243
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001244 memset(&params, 0, sizeof(params));
1245
Johannes Berg04a773a2009-04-19 21:24:32 +02001246 otype = ntype = dev->ieee80211_ptr->iftype;
Johannes Berg55682962007-09-20 13:09:35 -04001247
Johannes Berg723b0382008-09-16 20:22:09 +02001248 if (info->attrs[NL80211_ATTR_IFTYPE]) {
Johannes Bergac7f9cf2009-03-21 17:07:59 +01001249 ntype = nla_get_u32(info->attrs[NL80211_ATTR_IFTYPE]);
Johannes Berg04a773a2009-04-19 21:24:32 +02001250 if (otype != ntype)
Johannes Bergac7f9cf2009-03-21 17:07:59 +01001251 change = true;
Johannes Berg4c476992010-10-04 21:36:35 +02001252 if (ntype > NL80211_IFTYPE_MAX)
1253 return -EINVAL;
Johannes Berg723b0382008-09-16 20:22:09 +02001254 }
1255
Johannes Berg92ffe052008-09-16 20:39:36 +02001256 if (info->attrs[NL80211_ATTR_MESH_ID]) {
Johannes Berg4c476992010-10-04 21:36:35 +02001257 if (ntype != NL80211_IFTYPE_MESH_POINT)
1258 return -EINVAL;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001259 params.mesh_id = nla_data(info->attrs[NL80211_ATTR_MESH_ID]);
1260 params.mesh_id_len = nla_len(info->attrs[NL80211_ATTR_MESH_ID]);
Johannes Bergac7f9cf2009-03-21 17:07:59 +01001261 change = true;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001262 }
1263
Felix Fietkau8b787642009-11-10 18:53:10 +01001264 if (info->attrs[NL80211_ATTR_4ADDR]) {
1265 params.use_4addr = !!nla_get_u8(info->attrs[NL80211_ATTR_4ADDR]);
1266 change = true;
Johannes Bergad4bb6f2009-11-19 00:56:30 +01001267 err = nl80211_valid_4addr(rdev, dev, params.use_4addr, ntype);
Johannes Berg9bc383d2009-11-19 11:55:19 +01001268 if (err)
Johannes Berg4c476992010-10-04 21:36:35 +02001269 return err;
Felix Fietkau8b787642009-11-10 18:53:10 +01001270 } else {
1271 params.use_4addr = -1;
1272 }
1273
Johannes Berg92ffe052008-09-16 20:39:36 +02001274 if (info->attrs[NL80211_ATTR_MNTR_FLAGS]) {
Johannes Berg4c476992010-10-04 21:36:35 +02001275 if (ntype != NL80211_IFTYPE_MONITOR)
1276 return -EINVAL;
Johannes Berg92ffe052008-09-16 20:39:36 +02001277 err = parse_monitor_flags(info->attrs[NL80211_ATTR_MNTR_FLAGS],
1278 &_flags);
Johannes Bergac7f9cf2009-03-21 17:07:59 +01001279 if (err)
Johannes Berg4c476992010-10-04 21:36:35 +02001280 return err;
Johannes Bergac7f9cf2009-03-21 17:07:59 +01001281
1282 flags = &_flags;
1283 change = true;
Johannes Berg92ffe052008-09-16 20:39:36 +02001284 }
Johannes Berg3b858752009-03-12 09:55:09 +01001285
Johannes Bergac7f9cf2009-03-21 17:07:59 +01001286 if (change)
Johannes Berg3d54d252009-08-21 14:51:05 +02001287 err = cfg80211_change_iface(rdev, dev, ntype, flags, &params);
Johannes Bergac7f9cf2009-03-21 17:07:59 +01001288 else
1289 err = 0;
Johannes Berg60719ff2008-09-16 14:55:09 +02001290
Johannes Berg9bc383d2009-11-19 11:55:19 +01001291 if (!err && params.use_4addr != -1)
1292 dev->ieee80211_ptr->use_4addr = params.use_4addr;
1293
Johannes Berg55682962007-09-20 13:09:35 -04001294 return err;
1295}
1296
1297static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info)
1298{
Johannes Berg4c476992010-10-04 21:36:35 +02001299 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001300 struct vif_params params;
Johannes Berg55682962007-09-20 13:09:35 -04001301 int err;
1302 enum nl80211_iftype type = NL80211_IFTYPE_UNSPECIFIED;
Michael Wu66f7ac52008-01-31 19:48:22 +01001303 u32 flags;
Johannes Berg55682962007-09-20 13:09:35 -04001304
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001305 memset(&params, 0, sizeof(params));
1306
Johannes Berg55682962007-09-20 13:09:35 -04001307 if (!info->attrs[NL80211_ATTR_IFNAME])
1308 return -EINVAL;
1309
1310 if (info->attrs[NL80211_ATTR_IFTYPE]) {
1311 type = nla_get_u32(info->attrs[NL80211_ATTR_IFTYPE]);
1312 if (type > NL80211_IFTYPE_MAX)
1313 return -EINVAL;
1314 }
1315
Johannes Berg79c97e92009-07-07 03:56:12 +02001316 if (!rdev->ops->add_virtual_intf ||
Johannes Berg4c476992010-10-04 21:36:35 +02001317 !(rdev->wiphy.interface_modes & (1 << type)))
1318 return -EOPNOTSUPP;
Johannes Berg55682962007-09-20 13:09:35 -04001319
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001320 if (type == NL80211_IFTYPE_MESH_POINT &&
1321 info->attrs[NL80211_ATTR_MESH_ID]) {
1322 params.mesh_id = nla_data(info->attrs[NL80211_ATTR_MESH_ID]);
1323 params.mesh_id_len = nla_len(info->attrs[NL80211_ATTR_MESH_ID]);
1324 }
1325
Johannes Berg9bc383d2009-11-19 11:55:19 +01001326 if (info->attrs[NL80211_ATTR_4ADDR]) {
Felix Fietkau8b787642009-11-10 18:53:10 +01001327 params.use_4addr = !!nla_get_u8(info->attrs[NL80211_ATTR_4ADDR]);
Johannes Bergad4bb6f2009-11-19 00:56:30 +01001328 err = nl80211_valid_4addr(rdev, NULL, params.use_4addr, type);
Johannes Berg9bc383d2009-11-19 11:55:19 +01001329 if (err)
Johannes Berg4c476992010-10-04 21:36:35 +02001330 return err;
Johannes Berg9bc383d2009-11-19 11:55:19 +01001331 }
Felix Fietkau8b787642009-11-10 18:53:10 +01001332
Michael Wu66f7ac52008-01-31 19:48:22 +01001333 err = parse_monitor_flags(type == NL80211_IFTYPE_MONITOR ?
1334 info->attrs[NL80211_ATTR_MNTR_FLAGS] : NULL,
1335 &flags);
Johannes Berg79c97e92009-07-07 03:56:12 +02001336 err = rdev->ops->add_virtual_intf(&rdev->wiphy,
Michael Wu66f7ac52008-01-31 19:48:22 +01001337 nla_data(info->attrs[NL80211_ATTR_IFNAME]),
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001338 type, err ? NULL : &flags, &params);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001339
Johannes Berg55682962007-09-20 13:09:35 -04001340 return err;
1341}
1342
1343static int nl80211_del_interface(struct sk_buff *skb, struct genl_info *info)
1344{
Johannes Berg4c476992010-10-04 21:36:35 +02001345 struct cfg80211_registered_device *rdev = info->user_ptr[0];
1346 struct net_device *dev = info->user_ptr[1];
Johannes Berg55682962007-09-20 13:09:35 -04001347
Johannes Berg4c476992010-10-04 21:36:35 +02001348 if (!rdev->ops->del_virtual_intf)
1349 return -EOPNOTSUPP;
Johannes Berg3b858752009-03-12 09:55:09 +01001350
Johannes Berg4c476992010-10-04 21:36:35 +02001351 return rdev->ops->del_virtual_intf(&rdev->wiphy, dev);
Johannes Berg55682962007-09-20 13:09:35 -04001352}
1353
Johannes Berg41ade002007-12-19 02:03:29 +01001354struct get_key_cookie {
1355 struct sk_buff *msg;
1356 int error;
Johannes Bergb9454e82009-07-08 13:29:08 +02001357 int idx;
Johannes Berg41ade002007-12-19 02:03:29 +01001358};
1359
1360static void get_key_callback(void *c, struct key_params *params)
1361{
Johannes Bergb9454e82009-07-08 13:29:08 +02001362 struct nlattr *key;
Johannes Berg41ade002007-12-19 02:03:29 +01001363 struct get_key_cookie *cookie = c;
1364
1365 if (params->key)
1366 NLA_PUT(cookie->msg, NL80211_ATTR_KEY_DATA,
1367 params->key_len, params->key);
1368
1369 if (params->seq)
1370 NLA_PUT(cookie->msg, NL80211_ATTR_KEY_SEQ,
1371 params->seq_len, params->seq);
1372
1373 if (params->cipher)
1374 NLA_PUT_U32(cookie->msg, NL80211_ATTR_KEY_CIPHER,
1375 params->cipher);
1376
Johannes Bergb9454e82009-07-08 13:29:08 +02001377 key = nla_nest_start(cookie->msg, NL80211_ATTR_KEY);
1378 if (!key)
1379 goto nla_put_failure;
1380
1381 if (params->key)
1382 NLA_PUT(cookie->msg, NL80211_KEY_DATA,
1383 params->key_len, params->key);
1384
1385 if (params->seq)
1386 NLA_PUT(cookie->msg, NL80211_KEY_SEQ,
1387 params->seq_len, params->seq);
1388
1389 if (params->cipher)
1390 NLA_PUT_U32(cookie->msg, NL80211_KEY_CIPHER,
1391 params->cipher);
1392
1393 NLA_PUT_U8(cookie->msg, NL80211_ATTR_KEY_IDX, cookie->idx);
1394
1395 nla_nest_end(cookie->msg, key);
1396
Johannes Berg41ade002007-12-19 02:03:29 +01001397 return;
1398 nla_put_failure:
1399 cookie->error = 1;
1400}
1401
1402static int nl80211_get_key(struct sk_buff *skb, struct genl_info *info)
1403{
Johannes Berg4c476992010-10-04 21:36:35 +02001404 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Johannes Berg41ade002007-12-19 02:03:29 +01001405 int err;
Johannes Berg4c476992010-10-04 21:36:35 +02001406 struct net_device *dev = info->user_ptr[1];
Johannes Berg41ade002007-12-19 02:03:29 +01001407 u8 key_idx = 0;
1408 u8 *mac_addr = NULL;
1409 struct get_key_cookie cookie = {
1410 .error = 0,
1411 };
1412 void *hdr;
1413 struct sk_buff *msg;
1414
1415 if (info->attrs[NL80211_ATTR_KEY_IDX])
1416 key_idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]);
1417
Jouni Malinen3cfcf6ac2009-01-08 13:32:02 +02001418 if (key_idx > 5)
Johannes Berg41ade002007-12-19 02:03:29 +01001419 return -EINVAL;
1420
1421 if (info->attrs[NL80211_ATTR_MAC])
1422 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
1423
Johannes Berg4c476992010-10-04 21:36:35 +02001424 if (!rdev->ops->get_key)
1425 return -EOPNOTSUPP;
Johannes Berg41ade002007-12-19 02:03:29 +01001426
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07001427 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg4c476992010-10-04 21:36:35 +02001428 if (!msg)
1429 return -ENOMEM;
Johannes Berg41ade002007-12-19 02:03:29 +01001430
1431 hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
1432 NL80211_CMD_NEW_KEY);
Johannes Berg4c476992010-10-04 21:36:35 +02001433 if (IS_ERR(hdr))
1434 return PTR_ERR(hdr);
Johannes Berg41ade002007-12-19 02:03:29 +01001435
1436 cookie.msg = msg;
Johannes Bergb9454e82009-07-08 13:29:08 +02001437 cookie.idx = key_idx;
Johannes Berg41ade002007-12-19 02:03:29 +01001438
1439 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
1440 NLA_PUT_U8(msg, NL80211_ATTR_KEY_IDX, key_idx);
1441 if (mac_addr)
1442 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr);
1443
Johannes Berg79c97e92009-07-07 03:56:12 +02001444 err = rdev->ops->get_key(&rdev->wiphy, dev, key_idx, mac_addr,
Johannes Berg41ade002007-12-19 02:03:29 +01001445 &cookie, get_key_callback);
Johannes Berg41ade002007-12-19 02:03:29 +01001446
1447 if (err)
Niko Jokinen6c95e2a2009-07-15 11:00:53 +03001448 goto free_msg;
Johannes Berg41ade002007-12-19 02:03:29 +01001449
1450 if (cookie.error)
1451 goto nla_put_failure;
1452
1453 genlmsg_end(msg, hdr);
Johannes Berg4c476992010-10-04 21:36:35 +02001454 return genlmsg_reply(msg, info);
Johannes Berg41ade002007-12-19 02:03:29 +01001455
1456 nla_put_failure:
1457 err = -ENOBUFS;
Niko Jokinen6c95e2a2009-07-15 11:00:53 +03001458 free_msg:
Johannes Berg41ade002007-12-19 02:03:29 +01001459 nlmsg_free(msg);
Johannes Berg41ade002007-12-19 02:03:29 +01001460 return err;
1461}
1462
1463static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info)
1464{
Johannes Berg4c476992010-10-04 21:36:35 +02001465 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Johannes Bergb9454e82009-07-08 13:29:08 +02001466 struct key_parse key;
Johannes Berg41ade002007-12-19 02:03:29 +01001467 int err;
Johannes Berg4c476992010-10-04 21:36:35 +02001468 struct net_device *dev = info->user_ptr[1];
Jouni Malinen3cfcf6ac2009-01-08 13:32:02 +02001469 int (*func)(struct wiphy *wiphy, struct net_device *netdev,
1470 u8 key_index);
Johannes Berg41ade002007-12-19 02:03:29 +01001471
Johannes Bergb9454e82009-07-08 13:29:08 +02001472 err = nl80211_parse_key(info, &key);
1473 if (err)
1474 return err;
1475
1476 if (key.idx < 0)
Johannes Berg41ade002007-12-19 02:03:29 +01001477 return -EINVAL;
1478
Johannes Bergb9454e82009-07-08 13:29:08 +02001479 /* only support setting default key */
1480 if (!key.def && !key.defmgmt)
Johannes Berg41ade002007-12-19 02:03:29 +01001481 return -EINVAL;
1482
Johannes Bergb9454e82009-07-08 13:29:08 +02001483 if (key.def)
Johannes Berg79c97e92009-07-07 03:56:12 +02001484 func = rdev->ops->set_default_key;
Jouni Malinen3cfcf6ac2009-01-08 13:32:02 +02001485 else
Johannes Berg79c97e92009-07-07 03:56:12 +02001486 func = rdev->ops->set_default_mgmt_key;
Jouni Malinen3cfcf6ac2009-01-08 13:32:02 +02001487
Johannes Berg4c476992010-10-04 21:36:35 +02001488 if (!func)
1489 return -EOPNOTSUPP;
Johannes Berg41ade002007-12-19 02:03:29 +01001490
Johannes Bergfffd0932009-07-08 14:22:54 +02001491 wdev_lock(dev->ieee80211_ptr);
1492 err = nl80211_key_allowed(dev->ieee80211_ptr);
1493 if (!err)
1494 err = func(&rdev->wiphy, dev, key.idx);
1495
Johannes Berg3d23e342009-09-29 23:27:28 +02001496#ifdef CONFIG_CFG80211_WEXT
Johannes Berg08645122009-05-11 13:54:58 +02001497 if (!err) {
Johannes Berg79c97e92009-07-07 03:56:12 +02001498 if (func == rdev->ops->set_default_key)
Johannes Bergb9454e82009-07-08 13:29:08 +02001499 dev->ieee80211_ptr->wext.default_key = key.idx;
Johannes Berg08645122009-05-11 13:54:58 +02001500 else
Johannes Bergb9454e82009-07-08 13:29:08 +02001501 dev->ieee80211_ptr->wext.default_mgmt_key = key.idx;
Johannes Berg08645122009-05-11 13:54:58 +02001502 }
1503#endif
Johannes Bergfffd0932009-07-08 14:22:54 +02001504 wdev_unlock(dev->ieee80211_ptr);
Johannes Berg41ade002007-12-19 02:03:29 +01001505
Johannes Berg41ade002007-12-19 02:03:29 +01001506 return err;
1507}
1508
1509static int nl80211_new_key(struct sk_buff *skb, struct genl_info *info)
1510{
Johannes Berg4c476992010-10-04 21:36:35 +02001511 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Johannes Bergfffd0932009-07-08 14:22:54 +02001512 int err;
Johannes Berg4c476992010-10-04 21:36:35 +02001513 struct net_device *dev = info->user_ptr[1];
Johannes Bergb9454e82009-07-08 13:29:08 +02001514 struct key_parse key;
Johannes Berg41ade002007-12-19 02:03:29 +01001515 u8 *mac_addr = NULL;
1516
Johannes Bergb9454e82009-07-08 13:29:08 +02001517 err = nl80211_parse_key(info, &key);
1518 if (err)
1519 return err;
Johannes Berg41ade002007-12-19 02:03:29 +01001520
Johannes Bergb9454e82009-07-08 13:29:08 +02001521 if (!key.p.key)
Johannes Berg41ade002007-12-19 02:03:29 +01001522 return -EINVAL;
1523
Johannes Berg41ade002007-12-19 02:03:29 +01001524 if (info->attrs[NL80211_ATTR_MAC])
1525 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
1526
Johannes Berg4c476992010-10-04 21:36:35 +02001527 if (!rdev->ops->add_key)
1528 return -EOPNOTSUPP;
Johannes Berg3b858752009-03-12 09:55:09 +01001529
Johannes Berg4c476992010-10-04 21:36:35 +02001530 if (cfg80211_validate_key_settings(rdev, &key.p, key.idx, mac_addr))
1531 return -EINVAL;
Johannes Bergfffd0932009-07-08 14:22:54 +02001532
1533 wdev_lock(dev->ieee80211_ptr);
1534 err = nl80211_key_allowed(dev->ieee80211_ptr);
1535 if (!err)
1536 err = rdev->ops->add_key(&rdev->wiphy, dev, key.idx,
1537 mac_addr, &key.p);
1538 wdev_unlock(dev->ieee80211_ptr);
Johannes Berg41ade002007-12-19 02:03:29 +01001539
Johannes Berg41ade002007-12-19 02:03:29 +01001540 return err;
1541}
1542
1543static int nl80211_del_key(struct sk_buff *skb, struct genl_info *info)
1544{
Johannes Berg4c476992010-10-04 21:36:35 +02001545 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Johannes Berg41ade002007-12-19 02:03:29 +01001546 int err;
Johannes Berg4c476992010-10-04 21:36:35 +02001547 struct net_device *dev = info->user_ptr[1];
Johannes Berg41ade002007-12-19 02:03:29 +01001548 u8 *mac_addr = NULL;
Johannes Bergb9454e82009-07-08 13:29:08 +02001549 struct key_parse key;
Johannes Berg41ade002007-12-19 02:03:29 +01001550
Johannes Bergb9454e82009-07-08 13:29:08 +02001551 err = nl80211_parse_key(info, &key);
1552 if (err)
1553 return err;
Johannes Berg41ade002007-12-19 02:03:29 +01001554
1555 if (info->attrs[NL80211_ATTR_MAC])
1556 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
1557
Johannes Berg4c476992010-10-04 21:36:35 +02001558 if (!rdev->ops->del_key)
1559 return -EOPNOTSUPP;
Johannes Berg41ade002007-12-19 02:03:29 +01001560
Johannes Bergfffd0932009-07-08 14:22:54 +02001561 wdev_lock(dev->ieee80211_ptr);
1562 err = nl80211_key_allowed(dev->ieee80211_ptr);
1563 if (!err)
1564 err = rdev->ops->del_key(&rdev->wiphy, dev, key.idx, mac_addr);
Johannes Berg41ade002007-12-19 02:03:29 +01001565
Johannes Berg3d23e342009-09-29 23:27:28 +02001566#ifdef CONFIG_CFG80211_WEXT
Johannes Berg08645122009-05-11 13:54:58 +02001567 if (!err) {
Johannes Bergb9454e82009-07-08 13:29:08 +02001568 if (key.idx == dev->ieee80211_ptr->wext.default_key)
Johannes Berg08645122009-05-11 13:54:58 +02001569 dev->ieee80211_ptr->wext.default_key = -1;
Johannes Bergb9454e82009-07-08 13:29:08 +02001570 else if (key.idx == dev->ieee80211_ptr->wext.default_mgmt_key)
Johannes Berg08645122009-05-11 13:54:58 +02001571 dev->ieee80211_ptr->wext.default_mgmt_key = -1;
1572 }
1573#endif
Johannes Bergfffd0932009-07-08 14:22:54 +02001574 wdev_unlock(dev->ieee80211_ptr);
Johannes Berg08645122009-05-11 13:54:58 +02001575
Johannes Berg41ade002007-12-19 02:03:29 +01001576 return err;
1577}
1578
Johannes Berged1b6cc2007-12-19 02:03:32 +01001579static int nl80211_addset_beacon(struct sk_buff *skb, struct genl_info *info)
1580{
1581 int (*call)(struct wiphy *wiphy, struct net_device *dev,
1582 struct beacon_parameters *info);
Johannes Berg4c476992010-10-04 21:36:35 +02001583 struct cfg80211_registered_device *rdev = info->user_ptr[0];
1584 struct net_device *dev = info->user_ptr[1];
Johannes Berged1b6cc2007-12-19 02:03:32 +01001585 struct beacon_parameters params;
1586 int haveinfo = 0;
1587
Johannes Bergf4a11bb2009-03-27 12:40:28 +01001588 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_BEACON_TAIL]))
1589 return -EINVAL;
1590
Johannes Berg074ac8d2010-09-16 14:58:22 +02001591 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
Johannes Berg4c476992010-10-04 21:36:35 +02001592 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
1593 return -EOPNOTSUPP;
Jouni Malineneec60b02009-03-20 21:21:19 +02001594
Johannes Berged1b6cc2007-12-19 02:03:32 +01001595 switch (info->genlhdr->cmd) {
1596 case NL80211_CMD_NEW_BEACON:
1597 /* these are required for NEW_BEACON */
1598 if (!info->attrs[NL80211_ATTR_BEACON_INTERVAL] ||
1599 !info->attrs[NL80211_ATTR_DTIM_PERIOD] ||
Johannes Berg4c476992010-10-04 21:36:35 +02001600 !info->attrs[NL80211_ATTR_BEACON_HEAD])
1601 return -EINVAL;
Johannes Berged1b6cc2007-12-19 02:03:32 +01001602
Johannes Berg79c97e92009-07-07 03:56:12 +02001603 call = rdev->ops->add_beacon;
Johannes Berged1b6cc2007-12-19 02:03:32 +01001604 break;
1605 case NL80211_CMD_SET_BEACON:
Johannes Berg79c97e92009-07-07 03:56:12 +02001606 call = rdev->ops->set_beacon;
Johannes Berged1b6cc2007-12-19 02:03:32 +01001607 break;
1608 default:
1609 WARN_ON(1);
Johannes Berg4c476992010-10-04 21:36:35 +02001610 return -EOPNOTSUPP;
Johannes Berged1b6cc2007-12-19 02:03:32 +01001611 }
1612
Johannes Berg4c476992010-10-04 21:36:35 +02001613 if (!call)
1614 return -EOPNOTSUPP;
Johannes Berged1b6cc2007-12-19 02:03:32 +01001615
1616 memset(&params, 0, sizeof(params));
1617
1618 if (info->attrs[NL80211_ATTR_BEACON_INTERVAL]) {
1619 params.interval =
1620 nla_get_u32(info->attrs[NL80211_ATTR_BEACON_INTERVAL]);
1621 haveinfo = 1;
1622 }
1623
1624 if (info->attrs[NL80211_ATTR_DTIM_PERIOD]) {
1625 params.dtim_period =
1626 nla_get_u32(info->attrs[NL80211_ATTR_DTIM_PERIOD]);
1627 haveinfo = 1;
1628 }
1629
1630 if (info->attrs[NL80211_ATTR_BEACON_HEAD]) {
1631 params.head = nla_data(info->attrs[NL80211_ATTR_BEACON_HEAD]);
1632 params.head_len =
1633 nla_len(info->attrs[NL80211_ATTR_BEACON_HEAD]);
1634 haveinfo = 1;
1635 }
1636
1637 if (info->attrs[NL80211_ATTR_BEACON_TAIL]) {
1638 params.tail = nla_data(info->attrs[NL80211_ATTR_BEACON_TAIL]);
1639 params.tail_len =
1640 nla_len(info->attrs[NL80211_ATTR_BEACON_TAIL]);
1641 haveinfo = 1;
1642 }
1643
Johannes Berg4c476992010-10-04 21:36:35 +02001644 if (!haveinfo)
1645 return -EINVAL;
Johannes Berged1b6cc2007-12-19 02:03:32 +01001646
Johannes Berg4c476992010-10-04 21:36:35 +02001647 return call(&rdev->wiphy, dev, &params);
Johannes Berged1b6cc2007-12-19 02:03:32 +01001648}
1649
1650static int nl80211_del_beacon(struct sk_buff *skb, struct genl_info *info)
1651{
Johannes Berg4c476992010-10-04 21:36:35 +02001652 struct cfg80211_registered_device *rdev = info->user_ptr[0];
1653 struct net_device *dev = info->user_ptr[1];
Johannes Berged1b6cc2007-12-19 02:03:32 +01001654
Johannes Berg4c476992010-10-04 21:36:35 +02001655 if (!rdev->ops->del_beacon)
1656 return -EOPNOTSUPP;
Johannes Berged1b6cc2007-12-19 02:03:32 +01001657
Johannes Berg074ac8d2010-09-16 14:58:22 +02001658 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
Johannes Berg4c476992010-10-04 21:36:35 +02001659 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
1660 return -EOPNOTSUPP;
Johannes Berged1b6cc2007-12-19 02:03:32 +01001661
Johannes Berg4c476992010-10-04 21:36:35 +02001662 return rdev->ops->del_beacon(&rdev->wiphy, dev);
Johannes Berged1b6cc2007-12-19 02:03:32 +01001663}
1664
Johannes Berg5727ef12007-12-19 02:03:34 +01001665static const struct nla_policy sta_flags_policy[NL80211_STA_FLAG_MAX + 1] = {
1666 [NL80211_STA_FLAG_AUTHORIZED] = { .type = NLA_FLAG },
1667 [NL80211_STA_FLAG_SHORT_PREAMBLE] = { .type = NLA_FLAG },
1668 [NL80211_STA_FLAG_WME] = { .type = NLA_FLAG },
Jouni Malinen0e467242009-05-11 21:57:55 +03001669 [NL80211_STA_FLAG_MFP] = { .type = NLA_FLAG },
Johannes Berg5727ef12007-12-19 02:03:34 +01001670};
1671
Johannes Bergeccb8e82009-05-11 21:57:56 +03001672static int parse_station_flags(struct genl_info *info,
1673 struct station_parameters *params)
Johannes Berg5727ef12007-12-19 02:03:34 +01001674{
1675 struct nlattr *flags[NL80211_STA_FLAG_MAX + 1];
Johannes Bergeccb8e82009-05-11 21:57:56 +03001676 struct nlattr *nla;
Johannes Berg5727ef12007-12-19 02:03:34 +01001677 int flag;
1678
Johannes Bergeccb8e82009-05-11 21:57:56 +03001679 /*
1680 * Try parsing the new attribute first so userspace
1681 * can specify both for older kernels.
1682 */
1683 nla = info->attrs[NL80211_ATTR_STA_FLAGS2];
1684 if (nla) {
1685 struct nl80211_sta_flag_update *sta_flags;
Johannes Berg5727ef12007-12-19 02:03:34 +01001686
Johannes Bergeccb8e82009-05-11 21:57:56 +03001687 sta_flags = nla_data(nla);
1688 params->sta_flags_mask = sta_flags->mask;
1689 params->sta_flags_set = sta_flags->set;
1690 if ((params->sta_flags_mask |
1691 params->sta_flags_set) & BIT(__NL80211_STA_FLAG_INVALID))
1692 return -EINVAL;
1693 return 0;
1694 }
1695
1696 /* if present, parse the old attribute */
1697
1698 nla = info->attrs[NL80211_ATTR_STA_FLAGS];
Johannes Berg5727ef12007-12-19 02:03:34 +01001699 if (!nla)
1700 return 0;
1701
1702 if (nla_parse_nested(flags, NL80211_STA_FLAG_MAX,
1703 nla, sta_flags_policy))
1704 return -EINVAL;
1705
Johannes Bergeccb8e82009-05-11 21:57:56 +03001706 params->sta_flags_mask = (1 << __NL80211_STA_FLAG_AFTER_LAST) - 1;
1707 params->sta_flags_mask &= ~1;
Johannes Berg5727ef12007-12-19 02:03:34 +01001708
1709 for (flag = 1; flag <= NL80211_STA_FLAG_MAX; flag++)
1710 if (flags[flag])
Johannes Bergeccb8e82009-05-11 21:57:56 +03001711 params->sta_flags_set |= (1<<flag);
Johannes Berg5727ef12007-12-19 02:03:34 +01001712
1713 return 0;
1714}
1715
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001716static int nl80211_send_station(struct sk_buff *msg, u32 pid, u32 seq,
1717 int flags, struct net_device *dev,
Johannes Berg98b62182009-12-23 13:15:44 +01001718 const u8 *mac_addr, struct station_info *sinfo)
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001719{
1720 void *hdr;
Henning Rogge420e7fa2008-12-11 22:04:19 +01001721 struct nlattr *sinfoattr, *txrate;
1722 u16 bitrate;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001723
1724 hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_STATION);
1725 if (!hdr)
1726 return -1;
1727
1728 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
1729 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr);
1730
Johannes Bergf5ea9122009-08-07 16:17:38 +02001731 NLA_PUT_U32(msg, NL80211_ATTR_GENERATION, sinfo->generation);
1732
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001733 sinfoattr = nla_nest_start(msg, NL80211_ATTR_STA_INFO);
1734 if (!sinfoattr)
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001735 goto nla_put_failure;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001736 if (sinfo->filled & STATION_INFO_INACTIVE_TIME)
1737 NLA_PUT_U32(msg, NL80211_STA_INFO_INACTIVE_TIME,
1738 sinfo->inactive_time);
1739 if (sinfo->filled & STATION_INFO_RX_BYTES)
1740 NLA_PUT_U32(msg, NL80211_STA_INFO_RX_BYTES,
1741 sinfo->rx_bytes);
1742 if (sinfo->filled & STATION_INFO_TX_BYTES)
1743 NLA_PUT_U32(msg, NL80211_STA_INFO_TX_BYTES,
1744 sinfo->tx_bytes);
1745 if (sinfo->filled & STATION_INFO_LLID)
1746 NLA_PUT_U16(msg, NL80211_STA_INFO_LLID,
1747 sinfo->llid);
1748 if (sinfo->filled & STATION_INFO_PLID)
1749 NLA_PUT_U16(msg, NL80211_STA_INFO_PLID,
1750 sinfo->plid);
1751 if (sinfo->filled & STATION_INFO_PLINK_STATE)
1752 NLA_PUT_U8(msg, NL80211_STA_INFO_PLINK_STATE,
1753 sinfo->plink_state);
Henning Rogge420e7fa2008-12-11 22:04:19 +01001754 if (sinfo->filled & STATION_INFO_SIGNAL)
1755 NLA_PUT_U8(msg, NL80211_STA_INFO_SIGNAL,
1756 sinfo->signal);
1757 if (sinfo->filled & STATION_INFO_TX_BITRATE) {
1758 txrate = nla_nest_start(msg, NL80211_STA_INFO_TX_BITRATE);
1759 if (!txrate)
1760 goto nla_put_failure;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001761
John W. Linville254416a2009-12-09 16:43:52 -05001762 /* cfg80211_calculate_bitrate will return 0 for mcs >= 32 */
1763 bitrate = cfg80211_calculate_bitrate(&sinfo->txrate);
Henning Rogge420e7fa2008-12-11 22:04:19 +01001764 if (bitrate > 0)
1765 NLA_PUT_U16(msg, NL80211_RATE_INFO_BITRATE, bitrate);
1766
1767 if (sinfo->txrate.flags & RATE_INFO_FLAGS_MCS)
1768 NLA_PUT_U8(msg, NL80211_RATE_INFO_MCS,
1769 sinfo->txrate.mcs);
1770 if (sinfo->txrate.flags & RATE_INFO_FLAGS_40_MHZ_WIDTH)
1771 NLA_PUT_FLAG(msg, NL80211_RATE_INFO_40_MHZ_WIDTH);
1772 if (sinfo->txrate.flags & RATE_INFO_FLAGS_SHORT_GI)
1773 NLA_PUT_FLAG(msg, NL80211_RATE_INFO_SHORT_GI);
1774
1775 nla_nest_end(msg, txrate);
1776 }
Jouni Malinen98c8a60a2009-02-17 13:24:57 +02001777 if (sinfo->filled & STATION_INFO_RX_PACKETS)
1778 NLA_PUT_U32(msg, NL80211_STA_INFO_RX_PACKETS,
1779 sinfo->rx_packets);
1780 if (sinfo->filled & STATION_INFO_TX_PACKETS)
1781 NLA_PUT_U32(msg, NL80211_STA_INFO_TX_PACKETS,
1782 sinfo->tx_packets);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001783 nla_nest_end(msg, sinfoattr);
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001784
1785 return genlmsg_end(msg, hdr);
1786
1787 nla_put_failure:
Thomas Grafbc3ed282008-06-03 16:36:54 -07001788 genlmsg_cancel(msg, hdr);
1789 return -EMSGSIZE;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001790}
1791
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001792static int nl80211_dump_station(struct sk_buff *skb,
Johannes Bergbba95fe2008-07-29 13:22:51 +02001793 struct netlink_callback *cb)
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001794{
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001795 struct station_info sinfo;
1796 struct cfg80211_registered_device *dev;
Johannes Bergbba95fe2008-07-29 13:22:51 +02001797 struct net_device *netdev;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001798 u8 mac_addr[ETH_ALEN];
Johannes Bergbba95fe2008-07-29 13:22:51 +02001799 int ifidx = cb->args[0];
1800 int sta_idx = cb->args[1];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001801 int err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001802
Holger Schuriga0438972009-11-11 11:30:02 +01001803 if (!ifidx)
1804 ifidx = nl80211_get_ifidx(cb);
1805 if (ifidx < 0)
1806 return ifidx;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001807
Johannes Berg3b858752009-03-12 09:55:09 +01001808 rtnl_lock();
1809
Johannes Berg463d0182009-07-14 00:33:35 +02001810 netdev = __dev_get_by_index(sock_net(skb->sk), ifidx);
Johannes Berg3b858752009-03-12 09:55:09 +01001811 if (!netdev) {
1812 err = -ENODEV;
1813 goto out_rtnl;
1814 }
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001815
Johannes Berg463d0182009-07-14 00:33:35 +02001816 dev = cfg80211_get_dev_from_ifindex(sock_net(skb->sk), ifidx);
Johannes Bergbba95fe2008-07-29 13:22:51 +02001817 if (IS_ERR(dev)) {
1818 err = PTR_ERR(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001819 goto out_rtnl;
Johannes Bergbba95fe2008-07-29 13:22:51 +02001820 }
1821
1822 if (!dev->ops->dump_station) {
Jouni Malineneec60b02009-03-20 21:21:19 +02001823 err = -EOPNOTSUPP;
Johannes Bergbba95fe2008-07-29 13:22:51 +02001824 goto out_err;
1825 }
1826
Johannes Bergbba95fe2008-07-29 13:22:51 +02001827 while (1) {
1828 err = dev->ops->dump_station(&dev->wiphy, netdev, sta_idx,
1829 mac_addr, &sinfo);
1830 if (err == -ENOENT)
1831 break;
1832 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001833 goto out_err;
Johannes Bergbba95fe2008-07-29 13:22:51 +02001834
1835 if (nl80211_send_station(skb,
1836 NETLINK_CB(cb->skb).pid,
1837 cb->nlh->nlmsg_seq, NLM_F_MULTI,
1838 netdev, mac_addr,
1839 &sinfo) < 0)
1840 goto out;
1841
1842 sta_idx++;
1843 }
1844
1845
1846 out:
1847 cb->args[1] = sta_idx;
1848 err = skb->len;
Johannes Bergbba95fe2008-07-29 13:22:51 +02001849 out_err:
Johannes Berg4d0c8ae2009-07-07 03:56:09 +02001850 cfg80211_unlock_rdev(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001851 out_rtnl:
1852 rtnl_unlock();
Johannes Bergbba95fe2008-07-29 13:22:51 +02001853
1854 return err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001855}
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001856
Johannes Berg5727ef12007-12-19 02:03:34 +01001857static int nl80211_get_station(struct sk_buff *skb, struct genl_info *info)
1858{
Johannes Berg4c476992010-10-04 21:36:35 +02001859 struct cfg80211_registered_device *rdev = info->user_ptr[0];
1860 struct net_device *dev = info->user_ptr[1];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001861 struct station_info sinfo;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001862 struct sk_buff *msg;
1863 u8 *mac_addr = NULL;
Johannes Berg4c476992010-10-04 21:36:35 +02001864 int err;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001865
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001866 memset(&sinfo, 0, sizeof(sinfo));
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001867
1868 if (!info->attrs[NL80211_ATTR_MAC])
1869 return -EINVAL;
1870
1871 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
1872
Johannes Berg4c476992010-10-04 21:36:35 +02001873 if (!rdev->ops->get_station)
1874 return -EOPNOTSUPP;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001875
Johannes Berg79c97e92009-07-07 03:56:12 +02001876 err = rdev->ops->get_station(&rdev->wiphy, dev, mac_addr, &sinfo);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001877 if (err)
Johannes Berg4c476992010-10-04 21:36:35 +02001878 return err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001879
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07001880 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001881 if (!msg)
Johannes Berg4c476992010-10-04 21:36:35 +02001882 return -ENOMEM;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001883
1884 if (nl80211_send_station(msg, info->snd_pid, info->snd_seq, 0,
Johannes Berg4c476992010-10-04 21:36:35 +02001885 dev, mac_addr, &sinfo) < 0) {
1886 nlmsg_free(msg);
1887 return -ENOBUFS;
1888 }
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001889
Johannes Berg4c476992010-10-04 21:36:35 +02001890 return genlmsg_reply(msg, info);
Johannes Berg5727ef12007-12-19 02:03:34 +01001891}
1892
1893/*
Felix Fietkauc258d2d2009-11-11 17:23:31 +01001894 * Get vlan interface making sure it is running and on the right wiphy.
Johannes Berg5727ef12007-12-19 02:03:34 +01001895 */
Johannes Berg463d0182009-07-14 00:33:35 +02001896static int get_vlan(struct genl_info *info,
Johannes Berg5727ef12007-12-19 02:03:34 +01001897 struct cfg80211_registered_device *rdev,
1898 struct net_device **vlan)
1899{
Johannes Berg463d0182009-07-14 00:33:35 +02001900 struct nlattr *vlanattr = info->attrs[NL80211_ATTR_STA_VLAN];
Johannes Berg5727ef12007-12-19 02:03:34 +01001901 *vlan = NULL;
1902
1903 if (vlanattr) {
Johannes Berg463d0182009-07-14 00:33:35 +02001904 *vlan = dev_get_by_index(genl_info_net(info),
1905 nla_get_u32(vlanattr));
Johannes Berg5727ef12007-12-19 02:03:34 +01001906 if (!*vlan)
1907 return -ENODEV;
1908 if (!(*vlan)->ieee80211_ptr)
1909 return -EINVAL;
1910 if ((*vlan)->ieee80211_ptr->wiphy != &rdev->wiphy)
1911 return -EINVAL;
Felix Fietkauc258d2d2009-11-11 17:23:31 +01001912 if (!netif_running(*vlan))
1913 return -ENETDOWN;
Johannes Berg5727ef12007-12-19 02:03:34 +01001914 }
1915 return 0;
1916}
1917
1918static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
1919{
Johannes Berg4c476992010-10-04 21:36:35 +02001920 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Johannes Berg5727ef12007-12-19 02:03:34 +01001921 int err;
Johannes Berg4c476992010-10-04 21:36:35 +02001922 struct net_device *dev = info->user_ptr[1];
Johannes Berg5727ef12007-12-19 02:03:34 +01001923 struct station_parameters params;
1924 u8 *mac_addr = NULL;
1925
1926 memset(&params, 0, sizeof(params));
1927
1928 params.listen_interval = -1;
1929
1930 if (info->attrs[NL80211_ATTR_STA_AID])
1931 return -EINVAL;
1932
1933 if (!info->attrs[NL80211_ATTR_MAC])
1934 return -EINVAL;
1935
1936 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
1937
1938 if (info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]) {
1939 params.supported_rates =
1940 nla_data(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]);
1941 params.supported_rates_len =
1942 nla_len(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]);
1943 }
1944
1945 if (info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL])
1946 params.listen_interval =
1947 nla_get_u16(info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]);
1948
Jouni Malinen36aedc92008-08-25 11:58:58 +03001949 if (info->attrs[NL80211_ATTR_HT_CAPABILITY])
1950 params.ht_capa =
1951 nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]);
1952
Johannes Bergeccb8e82009-05-11 21:57:56 +03001953 if (parse_station_flags(info, &params))
Johannes Berg5727ef12007-12-19 02:03:34 +01001954 return -EINVAL;
1955
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001956 if (info->attrs[NL80211_ATTR_STA_PLINK_ACTION])
1957 params.plink_action =
1958 nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_ACTION]);
1959
Johannes Berg463d0182009-07-14 00:33:35 +02001960 err = get_vlan(info, rdev, &params.vlan);
Johannes Berga97f4422009-06-18 17:23:43 +02001961 if (err)
Johannes Berg034d6552009-05-27 10:35:29 +02001962 goto out;
Johannes Berga97f4422009-06-18 17:23:43 +02001963
1964 /* validate settings */
1965 err = 0;
1966
1967 switch (dev->ieee80211_ptr->iftype) {
1968 case NL80211_IFTYPE_AP:
1969 case NL80211_IFTYPE_AP_VLAN:
Johannes Berg074ac8d2010-09-16 14:58:22 +02001970 case NL80211_IFTYPE_P2P_GO:
Johannes Berga97f4422009-06-18 17:23:43 +02001971 /* disallow mesh-specific things */
1972 if (params.plink_action)
1973 err = -EINVAL;
1974 break;
Johannes Berg074ac8d2010-09-16 14:58:22 +02001975 case NL80211_IFTYPE_P2P_CLIENT:
Johannes Berga97f4422009-06-18 17:23:43 +02001976 case NL80211_IFTYPE_STATION:
1977 /* disallow everything but AUTHORIZED flag */
1978 if (params.plink_action)
1979 err = -EINVAL;
1980 if (params.vlan)
1981 err = -EINVAL;
1982 if (params.supported_rates)
1983 err = -EINVAL;
1984 if (params.ht_capa)
1985 err = -EINVAL;
1986 if (params.listen_interval >= 0)
1987 err = -EINVAL;
1988 if (params.sta_flags_mask & ~BIT(NL80211_STA_FLAG_AUTHORIZED))
1989 err = -EINVAL;
1990 break;
1991 case NL80211_IFTYPE_MESH_POINT:
1992 /* disallow things mesh doesn't support */
1993 if (params.vlan)
1994 err = -EINVAL;
1995 if (params.ht_capa)
1996 err = -EINVAL;
1997 if (params.listen_interval >= 0)
1998 err = -EINVAL;
1999 if (params.supported_rates)
2000 err = -EINVAL;
2001 if (params.sta_flags_mask)
2002 err = -EINVAL;
2003 break;
2004 default:
2005 err = -EINVAL;
Johannes Berg034d6552009-05-27 10:35:29 +02002006 }
2007
Johannes Berg5727ef12007-12-19 02:03:34 +01002008 if (err)
2009 goto out;
2010
Johannes Berg79c97e92009-07-07 03:56:12 +02002011 if (!rdev->ops->change_station) {
Johannes Berg5727ef12007-12-19 02:03:34 +01002012 err = -EOPNOTSUPP;
2013 goto out;
2014 }
2015
Johannes Berg79c97e92009-07-07 03:56:12 +02002016 err = rdev->ops->change_station(&rdev->wiphy, dev, mac_addr, &params);
Johannes Berg5727ef12007-12-19 02:03:34 +01002017
2018 out:
2019 if (params.vlan)
2020 dev_put(params.vlan);
Johannes Berg3b858752009-03-12 09:55:09 +01002021
Johannes Berg5727ef12007-12-19 02:03:34 +01002022 return err;
2023}
2024
2025static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
2026{
Johannes Berg4c476992010-10-04 21:36:35 +02002027 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Johannes Berg5727ef12007-12-19 02:03:34 +01002028 int err;
Johannes Berg4c476992010-10-04 21:36:35 +02002029 struct net_device *dev = info->user_ptr[1];
Johannes Berg5727ef12007-12-19 02:03:34 +01002030 struct station_parameters params;
2031 u8 *mac_addr = NULL;
2032
2033 memset(&params, 0, sizeof(params));
2034
2035 if (!info->attrs[NL80211_ATTR_MAC])
2036 return -EINVAL;
2037
Johannes Berg5727ef12007-12-19 02:03:34 +01002038 if (!info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL])
2039 return -EINVAL;
2040
2041 if (!info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES])
2042 return -EINVAL;
2043
Thadeu Lima de Souza Cascardo0e956c12010-02-12 12:34:50 -02002044 if (!info->attrs[NL80211_ATTR_STA_AID])
2045 return -EINVAL;
2046
Johannes Berg5727ef12007-12-19 02:03:34 +01002047 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
2048 params.supported_rates =
2049 nla_data(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]);
2050 params.supported_rates_len =
2051 nla_len(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]);
2052 params.listen_interval =
2053 nla_get_u16(info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]);
Johannes Berg51b50fb2009-05-24 16:42:30 +02002054
Thadeu Lima de Souza Cascardo0e956c12010-02-12 12:34:50 -02002055 params.aid = nla_get_u16(info->attrs[NL80211_ATTR_STA_AID]);
2056 if (!params.aid || params.aid > IEEE80211_MAX_AID)
2057 return -EINVAL;
Johannes Berg51b50fb2009-05-24 16:42:30 +02002058
Jouni Malinen36aedc92008-08-25 11:58:58 +03002059 if (info->attrs[NL80211_ATTR_HT_CAPABILITY])
2060 params.ht_capa =
2061 nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]);
Johannes Berg5727ef12007-12-19 02:03:34 +01002062
Johannes Bergeccb8e82009-05-11 21:57:56 +03002063 if (parse_station_flags(info, &params))
Johannes Berg5727ef12007-12-19 02:03:34 +01002064 return -EINVAL;
2065
Thadeu Lima de Souza Cascardo0e956c12010-02-12 12:34:50 -02002066 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
Johannes Berg074ac8d2010-09-16 14:58:22 +02002067 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN &&
Johannes Berg4c476992010-10-04 21:36:35 +02002068 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
2069 return -EINVAL;
Thadeu Lima de Souza Cascardo0e956c12010-02-12 12:34:50 -02002070
Johannes Berg463d0182009-07-14 00:33:35 +02002071 err = get_vlan(info, rdev, &params.vlan);
Johannes Berga97f4422009-06-18 17:23:43 +02002072 if (err)
Johannes Berge80cf852009-05-11 14:43:13 +02002073 goto out;
Johannes Berga97f4422009-06-18 17:23:43 +02002074
2075 /* validate settings */
2076 err = 0;
2077
Johannes Berg79c97e92009-07-07 03:56:12 +02002078 if (!rdev->ops->add_station) {
Johannes Berg5727ef12007-12-19 02:03:34 +01002079 err = -EOPNOTSUPP;
2080 goto out;
2081 }
2082
Johannes Berg79c97e92009-07-07 03:56:12 +02002083 err = rdev->ops->add_station(&rdev->wiphy, dev, mac_addr, &params);
Johannes Berg5727ef12007-12-19 02:03:34 +01002084
2085 out:
2086 if (params.vlan)
2087 dev_put(params.vlan);
Johannes Berg5727ef12007-12-19 02:03:34 +01002088 return err;
2089}
2090
2091static int nl80211_del_station(struct sk_buff *skb, struct genl_info *info)
2092{
Johannes Berg4c476992010-10-04 21:36:35 +02002093 struct cfg80211_registered_device *rdev = info->user_ptr[0];
2094 struct net_device *dev = info->user_ptr[1];
Johannes Berg5727ef12007-12-19 02:03:34 +01002095 u8 *mac_addr = NULL;
2096
2097 if (info->attrs[NL80211_ATTR_MAC])
2098 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
2099
Johannes Berge80cf852009-05-11 14:43:13 +02002100 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
Marco Porschd5d9de02010-03-30 10:00:16 +02002101 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN &&
Johannes Berg074ac8d2010-09-16 14:58:22 +02002102 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT &&
Johannes Berg4c476992010-10-04 21:36:35 +02002103 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
2104 return -EINVAL;
Johannes Berge80cf852009-05-11 14:43:13 +02002105
Johannes Berg4c476992010-10-04 21:36:35 +02002106 if (!rdev->ops->del_station)
2107 return -EOPNOTSUPP;
Johannes Berg5727ef12007-12-19 02:03:34 +01002108
Johannes Berg4c476992010-10-04 21:36:35 +02002109 return rdev->ops->del_station(&rdev->wiphy, dev, mac_addr);
Johannes Berg5727ef12007-12-19 02:03:34 +01002110}
2111
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002112static int nl80211_send_mpath(struct sk_buff *msg, u32 pid, u32 seq,
2113 int flags, struct net_device *dev,
2114 u8 *dst, u8 *next_hop,
2115 struct mpath_info *pinfo)
2116{
2117 void *hdr;
2118 struct nlattr *pinfoattr;
2119
2120 hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_STATION);
2121 if (!hdr)
2122 return -1;
2123
2124 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
2125 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, dst);
2126 NLA_PUT(msg, NL80211_ATTR_MPATH_NEXT_HOP, ETH_ALEN, next_hop);
2127
Johannes Bergf5ea9122009-08-07 16:17:38 +02002128 NLA_PUT_U32(msg, NL80211_ATTR_GENERATION, pinfo->generation);
2129
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002130 pinfoattr = nla_nest_start(msg, NL80211_ATTR_MPATH_INFO);
2131 if (!pinfoattr)
2132 goto nla_put_failure;
2133 if (pinfo->filled & MPATH_INFO_FRAME_QLEN)
2134 NLA_PUT_U32(msg, NL80211_MPATH_INFO_FRAME_QLEN,
2135 pinfo->frame_qlen);
Rui Paulod19b3bf2009-11-09 23:46:55 +00002136 if (pinfo->filled & MPATH_INFO_SN)
2137 NLA_PUT_U32(msg, NL80211_MPATH_INFO_SN,
2138 pinfo->sn);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002139 if (pinfo->filled & MPATH_INFO_METRIC)
2140 NLA_PUT_U32(msg, NL80211_MPATH_INFO_METRIC,
2141 pinfo->metric);
2142 if (pinfo->filled & MPATH_INFO_EXPTIME)
2143 NLA_PUT_U32(msg, NL80211_MPATH_INFO_EXPTIME,
2144 pinfo->exptime);
2145 if (pinfo->filled & MPATH_INFO_FLAGS)
2146 NLA_PUT_U8(msg, NL80211_MPATH_INFO_FLAGS,
2147 pinfo->flags);
2148 if (pinfo->filled & MPATH_INFO_DISCOVERY_TIMEOUT)
2149 NLA_PUT_U32(msg, NL80211_MPATH_INFO_DISCOVERY_TIMEOUT,
2150 pinfo->discovery_timeout);
2151 if (pinfo->filled & MPATH_INFO_DISCOVERY_RETRIES)
2152 NLA_PUT_U8(msg, NL80211_MPATH_INFO_DISCOVERY_RETRIES,
2153 pinfo->discovery_retries);
2154
2155 nla_nest_end(msg, pinfoattr);
2156
2157 return genlmsg_end(msg, hdr);
2158
2159 nla_put_failure:
Thomas Grafbc3ed282008-06-03 16:36:54 -07002160 genlmsg_cancel(msg, hdr);
2161 return -EMSGSIZE;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002162}
2163
2164static int nl80211_dump_mpath(struct sk_buff *skb,
Johannes Bergbba95fe2008-07-29 13:22:51 +02002165 struct netlink_callback *cb)
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002166{
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002167 struct mpath_info pinfo;
2168 struct cfg80211_registered_device *dev;
Johannes Bergbba95fe2008-07-29 13:22:51 +02002169 struct net_device *netdev;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002170 u8 dst[ETH_ALEN];
2171 u8 next_hop[ETH_ALEN];
Johannes Bergbba95fe2008-07-29 13:22:51 +02002172 int ifidx = cb->args[0];
2173 int path_idx = cb->args[1];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002174 int err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002175
Holger Schuriga0438972009-11-11 11:30:02 +01002176 if (!ifidx)
2177 ifidx = nl80211_get_ifidx(cb);
2178 if (ifidx < 0)
2179 return ifidx;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002180
Johannes Berg3b858752009-03-12 09:55:09 +01002181 rtnl_lock();
2182
Johannes Berg463d0182009-07-14 00:33:35 +02002183 netdev = __dev_get_by_index(sock_net(skb->sk), ifidx);
Johannes Berg3b858752009-03-12 09:55:09 +01002184 if (!netdev) {
2185 err = -ENODEV;
2186 goto out_rtnl;
2187 }
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002188
Johannes Berg463d0182009-07-14 00:33:35 +02002189 dev = cfg80211_get_dev_from_ifindex(sock_net(skb->sk), ifidx);
Johannes Bergbba95fe2008-07-29 13:22:51 +02002190 if (IS_ERR(dev)) {
2191 err = PTR_ERR(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01002192 goto out_rtnl;
Johannes Bergbba95fe2008-07-29 13:22:51 +02002193 }
2194
2195 if (!dev->ops->dump_mpath) {
Jouni Malineneec60b02009-03-20 21:21:19 +02002196 err = -EOPNOTSUPP;
Johannes Bergbba95fe2008-07-29 13:22:51 +02002197 goto out_err;
2198 }
2199
Jouni Malineneec60b02009-03-20 21:21:19 +02002200 if (netdev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) {
2201 err = -EOPNOTSUPP;
Roel Kluin0448b5f2009-08-22 21:15:49 +02002202 goto out_err;
Jouni Malineneec60b02009-03-20 21:21:19 +02002203 }
2204
Johannes Bergbba95fe2008-07-29 13:22:51 +02002205 while (1) {
2206 err = dev->ops->dump_mpath(&dev->wiphy, netdev, path_idx,
2207 dst, next_hop, &pinfo);
2208 if (err == -ENOENT)
2209 break;
2210 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01002211 goto out_err;
Johannes Bergbba95fe2008-07-29 13:22:51 +02002212
2213 if (nl80211_send_mpath(skb, NETLINK_CB(cb->skb).pid,
2214 cb->nlh->nlmsg_seq, NLM_F_MULTI,
2215 netdev, dst, next_hop,
2216 &pinfo) < 0)
2217 goto out;
2218
2219 path_idx++;
2220 }
2221
2222
2223 out:
2224 cb->args[1] = path_idx;
2225 err = skb->len;
Johannes Bergbba95fe2008-07-29 13:22:51 +02002226 out_err:
Johannes Berg4d0c8ae2009-07-07 03:56:09 +02002227 cfg80211_unlock_rdev(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01002228 out_rtnl:
2229 rtnl_unlock();
Johannes Bergbba95fe2008-07-29 13:22:51 +02002230
2231 return err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002232}
2233
2234static int nl80211_get_mpath(struct sk_buff *skb, struct genl_info *info)
2235{
Johannes Berg4c476992010-10-04 21:36:35 +02002236 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002237 int err;
Johannes Berg4c476992010-10-04 21:36:35 +02002238 struct net_device *dev = info->user_ptr[1];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002239 struct mpath_info pinfo;
2240 struct sk_buff *msg;
2241 u8 *dst = NULL;
2242 u8 next_hop[ETH_ALEN];
2243
2244 memset(&pinfo, 0, sizeof(pinfo));
2245
2246 if (!info->attrs[NL80211_ATTR_MAC])
2247 return -EINVAL;
2248
2249 dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
2250
Johannes Berg4c476992010-10-04 21:36:35 +02002251 if (!rdev->ops->get_mpath)
2252 return -EOPNOTSUPP;
Johannes Berg3b858752009-03-12 09:55:09 +01002253
Johannes Berg4c476992010-10-04 21:36:35 +02002254 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT)
2255 return -EOPNOTSUPP;
Jouni Malineneec60b02009-03-20 21:21:19 +02002256
Johannes Berg79c97e92009-07-07 03:56:12 +02002257 err = rdev->ops->get_mpath(&rdev->wiphy, dev, dst, next_hop, &pinfo);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002258 if (err)
Johannes Berg4c476992010-10-04 21:36:35 +02002259 return err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002260
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07002261 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002262 if (!msg)
Johannes Berg4c476992010-10-04 21:36:35 +02002263 return -ENOMEM;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002264
2265 if (nl80211_send_mpath(msg, info->snd_pid, info->snd_seq, 0,
Johannes Berg4c476992010-10-04 21:36:35 +02002266 dev, dst, next_hop, &pinfo) < 0) {
2267 nlmsg_free(msg);
2268 return -ENOBUFS;
2269 }
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002270
Johannes Berg4c476992010-10-04 21:36:35 +02002271 return genlmsg_reply(msg, info);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002272}
2273
2274static int nl80211_set_mpath(struct sk_buff *skb, struct genl_info *info)
2275{
Johannes Berg4c476992010-10-04 21:36:35 +02002276 struct cfg80211_registered_device *rdev = info->user_ptr[0];
2277 struct net_device *dev = info->user_ptr[1];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002278 u8 *dst = NULL;
2279 u8 *next_hop = NULL;
2280
2281 if (!info->attrs[NL80211_ATTR_MAC])
2282 return -EINVAL;
2283
2284 if (!info->attrs[NL80211_ATTR_MPATH_NEXT_HOP])
2285 return -EINVAL;
2286
2287 dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
2288 next_hop = nla_data(info->attrs[NL80211_ATTR_MPATH_NEXT_HOP]);
2289
Johannes Berg4c476992010-10-04 21:36:35 +02002290 if (!rdev->ops->change_mpath)
2291 return -EOPNOTSUPP;
Johannes Berg3b858752009-03-12 09:55:09 +01002292
Johannes Berg4c476992010-10-04 21:36:35 +02002293 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT)
2294 return -EOPNOTSUPP;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002295
Johannes Berg4c476992010-10-04 21:36:35 +02002296 return rdev->ops->change_mpath(&rdev->wiphy, dev, dst, next_hop);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002297}
Johannes Berg4c476992010-10-04 21:36:35 +02002298
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002299static int nl80211_new_mpath(struct sk_buff *skb, struct genl_info *info)
2300{
Johannes Berg4c476992010-10-04 21:36:35 +02002301 struct cfg80211_registered_device *rdev = info->user_ptr[0];
2302 struct net_device *dev = info->user_ptr[1];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002303 u8 *dst = NULL;
2304 u8 *next_hop = NULL;
2305
2306 if (!info->attrs[NL80211_ATTR_MAC])
2307 return -EINVAL;
2308
2309 if (!info->attrs[NL80211_ATTR_MPATH_NEXT_HOP])
2310 return -EINVAL;
2311
2312 dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
2313 next_hop = nla_data(info->attrs[NL80211_ATTR_MPATH_NEXT_HOP]);
2314
Johannes Berg4c476992010-10-04 21:36:35 +02002315 if (!rdev->ops->add_mpath)
2316 return -EOPNOTSUPP;
Johannes Berg3b858752009-03-12 09:55:09 +01002317
Johannes Berg4c476992010-10-04 21:36:35 +02002318 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT)
2319 return -EOPNOTSUPP;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002320
Johannes Berg4c476992010-10-04 21:36:35 +02002321 return rdev->ops->add_mpath(&rdev->wiphy, dev, dst, next_hop);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002322}
2323
2324static int nl80211_del_mpath(struct sk_buff *skb, struct genl_info *info)
2325{
Johannes Berg4c476992010-10-04 21:36:35 +02002326 struct cfg80211_registered_device *rdev = info->user_ptr[0];
2327 struct net_device *dev = info->user_ptr[1];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002328 u8 *dst = NULL;
2329
2330 if (info->attrs[NL80211_ATTR_MAC])
2331 dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
2332
Johannes Berg4c476992010-10-04 21:36:35 +02002333 if (!rdev->ops->del_mpath)
2334 return -EOPNOTSUPP;
Johannes Berg3b858752009-03-12 09:55:09 +01002335
Johannes Berg4c476992010-10-04 21:36:35 +02002336 return rdev->ops->del_mpath(&rdev->wiphy, dev, dst);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002337}
2338
Jouni Malinen9f1ba902008-08-07 20:07:01 +03002339static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info)
2340{
Johannes Berg4c476992010-10-04 21:36:35 +02002341 struct cfg80211_registered_device *rdev = info->user_ptr[0];
2342 struct net_device *dev = info->user_ptr[1];
Jouni Malinen9f1ba902008-08-07 20:07:01 +03002343 struct bss_parameters params;
2344
2345 memset(&params, 0, sizeof(params));
2346 /* default to not changing parameters */
2347 params.use_cts_prot = -1;
2348 params.use_short_preamble = -1;
2349 params.use_short_slot_time = -1;
Felix Fietkaufd8aaaf2010-04-27 01:23:35 +02002350 params.ap_isolate = -1;
Jouni Malinen9f1ba902008-08-07 20:07:01 +03002351
2352 if (info->attrs[NL80211_ATTR_BSS_CTS_PROT])
2353 params.use_cts_prot =
2354 nla_get_u8(info->attrs[NL80211_ATTR_BSS_CTS_PROT]);
2355 if (info->attrs[NL80211_ATTR_BSS_SHORT_PREAMBLE])
2356 params.use_short_preamble =
2357 nla_get_u8(info->attrs[NL80211_ATTR_BSS_SHORT_PREAMBLE]);
2358 if (info->attrs[NL80211_ATTR_BSS_SHORT_SLOT_TIME])
2359 params.use_short_slot_time =
2360 nla_get_u8(info->attrs[NL80211_ATTR_BSS_SHORT_SLOT_TIME]);
Jouni Malinen90c97a02008-10-30 16:59:22 +02002361 if (info->attrs[NL80211_ATTR_BSS_BASIC_RATES]) {
2362 params.basic_rates =
2363 nla_data(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]);
2364 params.basic_rates_len =
2365 nla_len(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]);
2366 }
Felix Fietkaufd8aaaf2010-04-27 01:23:35 +02002367 if (info->attrs[NL80211_ATTR_AP_ISOLATE])
2368 params.ap_isolate = !!nla_get_u8(info->attrs[NL80211_ATTR_AP_ISOLATE]);
Jouni Malinen9f1ba902008-08-07 20:07:01 +03002369
Johannes Berg4c476992010-10-04 21:36:35 +02002370 if (!rdev->ops->change_bss)
2371 return -EOPNOTSUPP;
Jouni Malinen9f1ba902008-08-07 20:07:01 +03002372
Johannes Berg074ac8d2010-09-16 14:58:22 +02002373 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
Johannes Berg4c476992010-10-04 21:36:35 +02002374 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
2375 return -EOPNOTSUPP;
Jouni Malineneec60b02009-03-20 21:21:19 +02002376
Johannes Berg4c476992010-10-04 21:36:35 +02002377 return rdev->ops->change_bss(&rdev->wiphy, dev, &params);
Jouni Malinen9f1ba902008-08-07 20:07:01 +03002378}
2379
Alexey Dobriyanb54452b2010-02-18 08:14:31 +00002380static const struct nla_policy reg_rule_policy[NL80211_REG_RULE_ATTR_MAX + 1] = {
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07002381 [NL80211_ATTR_REG_RULE_FLAGS] = { .type = NLA_U32 },
2382 [NL80211_ATTR_FREQ_RANGE_START] = { .type = NLA_U32 },
2383 [NL80211_ATTR_FREQ_RANGE_END] = { .type = NLA_U32 },
2384 [NL80211_ATTR_FREQ_RANGE_MAX_BW] = { .type = NLA_U32 },
2385 [NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN] = { .type = NLA_U32 },
2386 [NL80211_ATTR_POWER_RULE_MAX_EIRP] = { .type = NLA_U32 },
2387};
2388
2389static int parse_reg_rule(struct nlattr *tb[],
2390 struct ieee80211_reg_rule *reg_rule)
2391{
2392 struct ieee80211_freq_range *freq_range = &reg_rule->freq_range;
2393 struct ieee80211_power_rule *power_rule = &reg_rule->power_rule;
2394
2395 if (!tb[NL80211_ATTR_REG_RULE_FLAGS])
2396 return -EINVAL;
2397 if (!tb[NL80211_ATTR_FREQ_RANGE_START])
2398 return -EINVAL;
2399 if (!tb[NL80211_ATTR_FREQ_RANGE_END])
2400 return -EINVAL;
2401 if (!tb[NL80211_ATTR_FREQ_RANGE_MAX_BW])
2402 return -EINVAL;
2403 if (!tb[NL80211_ATTR_POWER_RULE_MAX_EIRP])
2404 return -EINVAL;
2405
2406 reg_rule->flags = nla_get_u32(tb[NL80211_ATTR_REG_RULE_FLAGS]);
2407
2408 freq_range->start_freq_khz =
2409 nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_START]);
2410 freq_range->end_freq_khz =
2411 nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_END]);
2412 freq_range->max_bandwidth_khz =
2413 nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_MAX_BW]);
2414
2415 power_rule->max_eirp =
2416 nla_get_u32(tb[NL80211_ATTR_POWER_RULE_MAX_EIRP]);
2417
2418 if (tb[NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN])
2419 power_rule->max_antenna_gain =
2420 nla_get_u32(tb[NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN]);
2421
2422 return 0;
2423}
2424
2425static int nl80211_req_set_reg(struct sk_buff *skb, struct genl_info *info)
2426{
2427 int r;
2428 char *data = NULL;
2429
Luis R. Rodriguez80778f12009-02-21 00:04:22 -05002430 /*
2431 * You should only get this when cfg80211 hasn't yet initialized
2432 * completely when built-in to the kernel right between the time
2433 * window between nl80211_init() and regulatory_init(), if that is
2434 * even possible.
2435 */
2436 mutex_lock(&cfg80211_mutex);
2437 if (unlikely(!cfg80211_regdomain)) {
Luis R. Rodriguezfe33eb32009-02-21 00:04:30 -05002438 mutex_unlock(&cfg80211_mutex);
2439 return -EINPROGRESS;
Luis R. Rodriguez80778f12009-02-21 00:04:22 -05002440 }
Luis R. Rodriguezfe33eb32009-02-21 00:04:30 -05002441 mutex_unlock(&cfg80211_mutex);
Luis R. Rodriguez80778f12009-02-21 00:04:22 -05002442
Luis R. Rodriguezfe33eb32009-02-21 00:04:30 -05002443 if (!info->attrs[NL80211_ATTR_REG_ALPHA2])
2444 return -EINVAL;
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07002445
2446 data = nla_data(info->attrs[NL80211_ATTR_REG_ALPHA2]);
2447
Luis R. Rodriguezfe33eb32009-02-21 00:04:30 -05002448 r = regulatory_hint_user(data);
2449
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07002450 return r;
2451}
2452
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002453static int nl80211_get_mesh_params(struct sk_buff *skb,
2454 struct genl_info *info)
2455{
Johannes Berg4c476992010-10-04 21:36:35 +02002456 struct cfg80211_registered_device *rdev = info->user_ptr[0];
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002457 struct mesh_config cur_params;
2458 int err;
Johannes Berg4c476992010-10-04 21:36:35 +02002459 struct net_device *dev = info->user_ptr[1];
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002460 void *hdr;
2461 struct nlattr *pinfoattr;
2462 struct sk_buff *msg;
2463
Johannes Berg4c476992010-10-04 21:36:35 +02002464 if (!rdev->ops->get_mesh_params)
2465 return -EOPNOTSUPP;
Jouni Malinenf3f92582009-03-20 17:57:36 +02002466
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002467 /* Get the mesh params */
Johannes Berg79c97e92009-07-07 03:56:12 +02002468 err = rdev->ops->get_mesh_params(&rdev->wiphy, dev, &cur_params);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002469 if (err)
Johannes Berg4c476992010-10-04 21:36:35 +02002470 return err;
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002471
2472 /* Draw up a netlink message to send back */
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07002473 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg4c476992010-10-04 21:36:35 +02002474 if (!msg)
2475 return -ENOMEM;
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002476 hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
2477 NL80211_CMD_GET_MESH_PARAMS);
2478 if (!hdr)
2479 goto nla_put_failure;
2480 pinfoattr = nla_nest_start(msg, NL80211_ATTR_MESH_PARAMS);
2481 if (!pinfoattr)
2482 goto nla_put_failure;
2483 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
2484 NLA_PUT_U16(msg, NL80211_MESHCONF_RETRY_TIMEOUT,
2485 cur_params.dot11MeshRetryTimeout);
2486 NLA_PUT_U16(msg, NL80211_MESHCONF_CONFIRM_TIMEOUT,
2487 cur_params.dot11MeshConfirmTimeout);
2488 NLA_PUT_U16(msg, NL80211_MESHCONF_HOLDING_TIMEOUT,
2489 cur_params.dot11MeshHoldingTimeout);
2490 NLA_PUT_U16(msg, NL80211_MESHCONF_MAX_PEER_LINKS,
2491 cur_params.dot11MeshMaxPeerLinks);
2492 NLA_PUT_U8(msg, NL80211_MESHCONF_MAX_RETRIES,
2493 cur_params.dot11MeshMaxRetries);
2494 NLA_PUT_U8(msg, NL80211_MESHCONF_TTL,
2495 cur_params.dot11MeshTTL);
2496 NLA_PUT_U8(msg, NL80211_MESHCONF_AUTO_OPEN_PLINKS,
2497 cur_params.auto_open_plinks);
2498 NLA_PUT_U8(msg, NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES,
2499 cur_params.dot11MeshHWMPmaxPREQretries);
2500 NLA_PUT_U32(msg, NL80211_MESHCONF_PATH_REFRESH_TIME,
2501 cur_params.path_refresh_time);
2502 NLA_PUT_U16(msg, NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT,
2503 cur_params.min_discovery_timeout);
2504 NLA_PUT_U32(msg, NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT,
2505 cur_params.dot11MeshHWMPactivePathTimeout);
2506 NLA_PUT_U16(msg, NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL,
2507 cur_params.dot11MeshHWMPpreqMinInterval);
2508 NLA_PUT_U16(msg, NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME,
2509 cur_params.dot11MeshHWMPnetDiameterTraversalTime);
Rui Paulo63c57232009-11-09 23:46:57 +00002510 NLA_PUT_U8(msg, NL80211_MESHCONF_HWMP_ROOTMODE,
2511 cur_params.dot11MeshHWMPRootMode);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002512 nla_nest_end(msg, pinfoattr);
2513 genlmsg_end(msg, hdr);
Johannes Berg4c476992010-10-04 21:36:35 +02002514 return genlmsg_reply(msg, info);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002515
Johannes Berg3b858752009-03-12 09:55:09 +01002516 nla_put_failure:
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002517 genlmsg_cancel(msg, hdr);
Yuri Ershovd080e272010-06-29 15:08:07 +04002518 nlmsg_free(msg);
Johannes Berg4c476992010-10-04 21:36:35 +02002519 return -ENOBUFS;
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002520}
2521
2522#define FILL_IN_MESH_PARAM_IF_SET(table, cfg, param, mask, attr_num, nla_fn) \
2523do {\
2524 if (table[attr_num]) {\
2525 cfg.param = nla_fn(table[attr_num]); \
2526 mask |= (1 << (attr_num - 1)); \
2527 } \
2528} while (0);\
2529
Alexey Dobriyanb54452b2010-02-18 08:14:31 +00002530static const struct nla_policy nl80211_meshconf_params_policy[NL80211_MESHCONF_ATTR_MAX+1] = {
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002531 [NL80211_MESHCONF_RETRY_TIMEOUT] = { .type = NLA_U16 },
2532 [NL80211_MESHCONF_CONFIRM_TIMEOUT] = { .type = NLA_U16 },
2533 [NL80211_MESHCONF_HOLDING_TIMEOUT] = { .type = NLA_U16 },
2534 [NL80211_MESHCONF_MAX_PEER_LINKS] = { .type = NLA_U16 },
2535 [NL80211_MESHCONF_MAX_RETRIES] = { .type = NLA_U8 },
2536 [NL80211_MESHCONF_TTL] = { .type = NLA_U8 },
2537 [NL80211_MESHCONF_AUTO_OPEN_PLINKS] = { .type = NLA_U8 },
2538
2539 [NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES] = { .type = NLA_U8 },
2540 [NL80211_MESHCONF_PATH_REFRESH_TIME] = { .type = NLA_U32 },
2541 [NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT] = { .type = NLA_U16 },
2542 [NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT] = { .type = NLA_U32 },
2543 [NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL] = { .type = NLA_U16 },
2544 [NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME] = { .type = NLA_U16 },
2545};
2546
2547static int nl80211_set_mesh_params(struct sk_buff *skb, struct genl_info *info)
2548{
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002549 u32 mask;
Johannes Berg4c476992010-10-04 21:36:35 +02002550 struct cfg80211_registered_device *rdev = info->user_ptr[0];
2551 struct net_device *dev = info->user_ptr[1];
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002552 struct mesh_config cfg;
2553 struct nlattr *tb[NL80211_MESHCONF_ATTR_MAX + 1];
2554 struct nlattr *parent_attr;
2555
2556 parent_attr = info->attrs[NL80211_ATTR_MESH_PARAMS];
2557 if (!parent_attr)
2558 return -EINVAL;
2559 if (nla_parse_nested(tb, NL80211_MESHCONF_ATTR_MAX,
2560 parent_attr, nl80211_meshconf_params_policy))
2561 return -EINVAL;
2562
Johannes Berg4c476992010-10-04 21:36:35 +02002563 if (!rdev->ops->set_mesh_params)
2564 return -EOPNOTSUPP;
Jouni Malinenf3f92582009-03-20 17:57:36 +02002565
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002566 /* This makes sure that there aren't more than 32 mesh config
2567 * parameters (otherwise our bitfield scheme would not work.) */
2568 BUILD_BUG_ON(NL80211_MESHCONF_ATTR_MAX > 32);
2569
2570 /* Fill in the params struct */
2571 mask = 0;
2572 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshRetryTimeout,
2573 mask, NL80211_MESHCONF_RETRY_TIMEOUT, nla_get_u16);
2574 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshConfirmTimeout,
2575 mask, NL80211_MESHCONF_CONFIRM_TIMEOUT, nla_get_u16);
2576 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHoldingTimeout,
2577 mask, NL80211_MESHCONF_HOLDING_TIMEOUT, nla_get_u16);
2578 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshMaxPeerLinks,
2579 mask, NL80211_MESHCONF_MAX_PEER_LINKS, nla_get_u16);
2580 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshMaxRetries,
2581 mask, NL80211_MESHCONF_MAX_RETRIES, nla_get_u8);
2582 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshTTL,
2583 mask, NL80211_MESHCONF_TTL, nla_get_u8);
2584 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, auto_open_plinks,
2585 mask, NL80211_MESHCONF_AUTO_OPEN_PLINKS, nla_get_u8);
2586 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPmaxPREQretries,
2587 mask, NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES,
2588 nla_get_u8);
2589 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, path_refresh_time,
2590 mask, NL80211_MESHCONF_PATH_REFRESH_TIME, nla_get_u32);
2591 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, min_discovery_timeout,
2592 mask, NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT,
2593 nla_get_u16);
2594 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPactivePathTimeout,
2595 mask, NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT,
2596 nla_get_u32);
2597 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPpreqMinInterval,
2598 mask, NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL,
2599 nla_get_u16);
2600 FILL_IN_MESH_PARAM_IF_SET(tb, cfg,
2601 dot11MeshHWMPnetDiameterTraversalTime,
2602 mask, NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME,
2603 nla_get_u16);
Rui Paulo63c57232009-11-09 23:46:57 +00002604 FILL_IN_MESH_PARAM_IF_SET(tb, cfg,
2605 dot11MeshHWMPRootMode, mask,
2606 NL80211_MESHCONF_HWMP_ROOTMODE,
2607 nla_get_u8);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002608
2609 /* Apply changes */
Johannes Berg4c476992010-10-04 21:36:35 +02002610 return rdev->ops->set_mesh_params(&rdev->wiphy, dev, &cfg, mask);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002611}
2612
2613#undef FILL_IN_MESH_PARAM_IF_SET
2614
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08002615static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info)
2616{
2617 struct sk_buff *msg;
2618 void *hdr = NULL;
2619 struct nlattr *nl_reg_rules;
2620 unsigned int i;
2621 int err = -EINVAL;
2622
Luis R. Rodrigueza1794392009-02-21 00:04:21 -05002623 mutex_lock(&cfg80211_mutex);
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08002624
2625 if (!cfg80211_regdomain)
2626 goto out;
2627
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07002628 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08002629 if (!msg) {
2630 err = -ENOBUFS;
2631 goto out;
2632 }
2633
2634 hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
2635 NL80211_CMD_GET_REG);
2636 if (!hdr)
2637 goto nla_put_failure;
2638
2639 NLA_PUT_STRING(msg, NL80211_ATTR_REG_ALPHA2,
2640 cfg80211_regdomain->alpha2);
2641
2642 nl_reg_rules = nla_nest_start(msg, NL80211_ATTR_REG_RULES);
2643 if (!nl_reg_rules)
2644 goto nla_put_failure;
2645
2646 for (i = 0; i < cfg80211_regdomain->n_reg_rules; i++) {
2647 struct nlattr *nl_reg_rule;
2648 const struct ieee80211_reg_rule *reg_rule;
2649 const struct ieee80211_freq_range *freq_range;
2650 const struct ieee80211_power_rule *power_rule;
2651
2652 reg_rule = &cfg80211_regdomain->reg_rules[i];
2653 freq_range = &reg_rule->freq_range;
2654 power_rule = &reg_rule->power_rule;
2655
2656 nl_reg_rule = nla_nest_start(msg, i);
2657 if (!nl_reg_rule)
2658 goto nla_put_failure;
2659
2660 NLA_PUT_U32(msg, NL80211_ATTR_REG_RULE_FLAGS,
2661 reg_rule->flags);
2662 NLA_PUT_U32(msg, NL80211_ATTR_FREQ_RANGE_START,
2663 freq_range->start_freq_khz);
2664 NLA_PUT_U32(msg, NL80211_ATTR_FREQ_RANGE_END,
2665 freq_range->end_freq_khz);
2666 NLA_PUT_U32(msg, NL80211_ATTR_FREQ_RANGE_MAX_BW,
2667 freq_range->max_bandwidth_khz);
2668 NLA_PUT_U32(msg, NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN,
2669 power_rule->max_antenna_gain);
2670 NLA_PUT_U32(msg, NL80211_ATTR_POWER_RULE_MAX_EIRP,
2671 power_rule->max_eirp);
2672
2673 nla_nest_end(msg, nl_reg_rule);
2674 }
2675
2676 nla_nest_end(msg, nl_reg_rules);
2677
2678 genlmsg_end(msg, hdr);
Johannes Berg134e6372009-07-10 09:51:34 +00002679 err = genlmsg_reply(msg, info);
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08002680 goto out;
2681
2682nla_put_failure:
2683 genlmsg_cancel(msg, hdr);
Yuri Ershovd080e272010-06-29 15:08:07 +04002684 nlmsg_free(msg);
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08002685 err = -EMSGSIZE;
2686out:
Luis R. Rodrigueza1794392009-02-21 00:04:21 -05002687 mutex_unlock(&cfg80211_mutex);
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08002688 return err;
2689}
2690
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07002691static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info)
2692{
2693 struct nlattr *tb[NL80211_REG_RULE_ATTR_MAX + 1];
2694 struct nlattr *nl_reg_rule;
2695 char *alpha2 = NULL;
2696 int rem_reg_rules = 0, r = 0;
2697 u32 num_rules = 0, rule_idx = 0, size_of_regd;
2698 struct ieee80211_regdomain *rd = NULL;
2699
2700 if (!info->attrs[NL80211_ATTR_REG_ALPHA2])
2701 return -EINVAL;
2702
2703 if (!info->attrs[NL80211_ATTR_REG_RULES])
2704 return -EINVAL;
2705
2706 alpha2 = nla_data(info->attrs[NL80211_ATTR_REG_ALPHA2]);
2707
2708 nla_for_each_nested(nl_reg_rule, info->attrs[NL80211_ATTR_REG_RULES],
2709 rem_reg_rules) {
2710 num_rules++;
2711 if (num_rules > NL80211_MAX_SUPP_REG_RULES)
Luis R. Rodriguez4776c6e2009-05-13 17:04:39 -04002712 return -EINVAL;
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07002713 }
2714
Luis R. Rodriguez61405e92009-05-13 17:04:41 -04002715 mutex_lock(&cfg80211_mutex);
2716
Luis R. Rodriguezd0e18f82009-05-13 17:04:40 -04002717 if (!reg_is_valid_request(alpha2)) {
2718 r = -EINVAL;
2719 goto bad_reg;
2720 }
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07002721
2722 size_of_regd = sizeof(struct ieee80211_regdomain) +
2723 (num_rules * sizeof(struct ieee80211_reg_rule));
2724
2725 rd = kzalloc(size_of_regd, GFP_KERNEL);
Luis R. Rodriguezd0e18f82009-05-13 17:04:40 -04002726 if (!rd) {
2727 r = -ENOMEM;
2728 goto bad_reg;
2729 }
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07002730
2731 rd->n_reg_rules = num_rules;
2732 rd->alpha2[0] = alpha2[0];
2733 rd->alpha2[1] = alpha2[1];
2734
2735 nla_for_each_nested(nl_reg_rule, info->attrs[NL80211_ATTR_REG_RULES],
2736 rem_reg_rules) {
2737 nla_parse(tb, NL80211_REG_RULE_ATTR_MAX,
2738 nla_data(nl_reg_rule), nla_len(nl_reg_rule),
2739 reg_rule_policy);
2740 r = parse_reg_rule(tb, &rd->reg_rules[rule_idx]);
2741 if (r)
2742 goto bad_reg;
2743
2744 rule_idx++;
2745
Luis R. Rodriguezd0e18f82009-05-13 17:04:40 -04002746 if (rule_idx > NL80211_MAX_SUPP_REG_RULES) {
2747 r = -EINVAL;
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07002748 goto bad_reg;
Luis R. Rodriguezd0e18f82009-05-13 17:04:40 -04002749 }
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07002750 }
2751
2752 BUG_ON(rule_idx != num_rules);
2753
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07002754 r = set_regdom(rd);
Luis R. Rodriguez61405e92009-05-13 17:04:41 -04002755
Luis R. Rodrigueza1794392009-02-21 00:04:21 -05002756 mutex_unlock(&cfg80211_mutex);
Luis R. Rodriguezd0e18f82009-05-13 17:04:40 -04002757
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07002758 return r;
2759
Johannes Bergd2372b32008-10-24 20:32:20 +02002760 bad_reg:
Luis R. Rodriguez61405e92009-05-13 17:04:41 -04002761 mutex_unlock(&cfg80211_mutex);
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07002762 kfree(rd);
Luis R. Rodriguezd0e18f82009-05-13 17:04:40 -04002763 return r;
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07002764}
2765
Johannes Berg83f5e2c2009-06-17 17:41:49 +02002766static int validate_scan_freqs(struct nlattr *freqs)
2767{
2768 struct nlattr *attr1, *attr2;
2769 int n_channels = 0, tmp1, tmp2;
2770
2771 nla_for_each_nested(attr1, freqs, tmp1) {
2772 n_channels++;
2773 /*
2774 * Some hardware has a limited channel list for
2775 * scanning, and it is pretty much nonsensical
2776 * to scan for a channel twice, so disallow that
2777 * and don't require drivers to check that the
2778 * channel list they get isn't longer than what
2779 * they can scan, as long as they can scan all
2780 * the channels they registered at once.
2781 */
2782 nla_for_each_nested(attr2, freqs, tmp2)
2783 if (attr1 != attr2 &&
2784 nla_get_u32(attr1) == nla_get_u32(attr2))
2785 return 0;
2786 }
2787
2788 return n_channels;
2789}
2790
Johannes Berg2a519312009-02-10 21:25:55 +01002791static int nl80211_trigger_scan(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];
2794 struct net_device *dev = info->user_ptr[1];
Johannes Berg2a519312009-02-10 21:25:55 +01002795 struct cfg80211_scan_request *request;
2796 struct cfg80211_ssid *ssid;
2797 struct ieee80211_channel *channel;
2798 struct nlattr *attr;
2799 struct wiphy *wiphy;
Johannes Berg83f5e2c2009-06-17 17:41:49 +02002800 int err, tmp, n_ssids = 0, n_channels, i;
Johannes Berg2a519312009-02-10 21:25:55 +01002801 enum ieee80211_band band;
Jouni Malinen70692ad2009-02-16 19:39:13 +02002802 size_t ie_len;
Johannes Berg2a519312009-02-10 21:25:55 +01002803
Johannes Bergf4a11bb2009-03-27 12:40:28 +01002804 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
2805 return -EINVAL;
2806
Johannes Berg79c97e92009-07-07 03:56:12 +02002807 wiphy = &rdev->wiphy;
Johannes Berg2a519312009-02-10 21:25:55 +01002808
Johannes Berg4c476992010-10-04 21:36:35 +02002809 if (!rdev->ops->scan)
2810 return -EOPNOTSUPP;
Johannes Berg2a519312009-02-10 21:25:55 +01002811
Johannes Berg4c476992010-10-04 21:36:35 +02002812 if (rdev->scan_req)
2813 return -EBUSY;
Johannes Berg2a519312009-02-10 21:25:55 +01002814
2815 if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
Johannes Berg83f5e2c2009-06-17 17:41:49 +02002816 n_channels = validate_scan_freqs(
2817 info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]);
Johannes Berg4c476992010-10-04 21:36:35 +02002818 if (!n_channels)
2819 return -EINVAL;
Johannes Berg2a519312009-02-10 21:25:55 +01002820 } else {
Johannes Berg83f5e2c2009-06-17 17:41:49 +02002821 n_channels = 0;
2822
Johannes Berg2a519312009-02-10 21:25:55 +01002823 for (band = 0; band < IEEE80211_NUM_BANDS; band++)
2824 if (wiphy->bands[band])
2825 n_channels += wiphy->bands[band]->n_channels;
2826 }
2827
2828 if (info->attrs[NL80211_ATTR_SCAN_SSIDS])
2829 nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS], tmp)
2830 n_ssids++;
2831
Johannes Berg4c476992010-10-04 21:36:35 +02002832 if (n_ssids > wiphy->max_scan_ssids)
2833 return -EINVAL;
Johannes Berg2a519312009-02-10 21:25:55 +01002834
Jouni Malinen70692ad2009-02-16 19:39:13 +02002835 if (info->attrs[NL80211_ATTR_IE])
2836 ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
2837 else
2838 ie_len = 0;
2839
Johannes Berg4c476992010-10-04 21:36:35 +02002840 if (ie_len > wiphy->max_scan_ie_len)
2841 return -EINVAL;
Johannes Berg18a83652009-03-31 12:12:05 +02002842
Johannes Berg2a519312009-02-10 21:25:55 +01002843 request = kzalloc(sizeof(*request)
2844 + sizeof(*ssid) * n_ssids
Jouni Malinen70692ad2009-02-16 19:39:13 +02002845 + sizeof(channel) * n_channels
2846 + ie_len, GFP_KERNEL);
Johannes Berg4c476992010-10-04 21:36:35 +02002847 if (!request)
2848 return -ENOMEM;
Johannes Berg2a519312009-02-10 21:25:55 +01002849
Johannes Berg2a519312009-02-10 21:25:55 +01002850 if (n_ssids)
Johannes Berg5ba63532009-08-07 17:54:07 +02002851 request->ssids = (void *)&request->channels[n_channels];
Johannes Berg2a519312009-02-10 21:25:55 +01002852 request->n_ssids = n_ssids;
Jouni Malinen70692ad2009-02-16 19:39:13 +02002853 if (ie_len) {
2854 if (request->ssids)
2855 request->ie = (void *)(request->ssids + n_ssids);
2856 else
2857 request->ie = (void *)(request->channels + n_channels);
2858 }
Johannes Berg2a519312009-02-10 21:25:55 +01002859
Johannes Berg584991d2009-11-02 13:32:03 +01002860 i = 0;
Johannes Berg2a519312009-02-10 21:25:55 +01002861 if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
2862 /* user specified, bail out if channel not found */
Johannes Berg2a519312009-02-10 21:25:55 +01002863 nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_FREQUENCIES], tmp) {
Johannes Berg584991d2009-11-02 13:32:03 +01002864 struct ieee80211_channel *chan;
2865
2866 chan = ieee80211_get_channel(wiphy, nla_get_u32(attr));
2867
2868 if (!chan) {
Johannes Berg2a519312009-02-10 21:25:55 +01002869 err = -EINVAL;
2870 goto out_free;
2871 }
Johannes Berg584991d2009-11-02 13:32:03 +01002872
2873 /* ignore disabled channels */
2874 if (chan->flags & IEEE80211_CHAN_DISABLED)
2875 continue;
2876
2877 request->channels[i] = chan;
Johannes Berg2a519312009-02-10 21:25:55 +01002878 i++;
2879 }
2880 } else {
2881 /* all channels */
Johannes Berg2a519312009-02-10 21:25:55 +01002882 for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
2883 int j;
2884 if (!wiphy->bands[band])
2885 continue;
2886 for (j = 0; j < wiphy->bands[band]->n_channels; j++) {
Johannes Berg584991d2009-11-02 13:32:03 +01002887 struct ieee80211_channel *chan;
2888
2889 chan = &wiphy->bands[band]->channels[j];
2890
2891 if (chan->flags & IEEE80211_CHAN_DISABLED)
2892 continue;
2893
2894 request->channels[i] = chan;
Johannes Berg2a519312009-02-10 21:25:55 +01002895 i++;
2896 }
2897 }
2898 }
2899
Johannes Berg584991d2009-11-02 13:32:03 +01002900 if (!i) {
2901 err = -EINVAL;
2902 goto out_free;
2903 }
2904
2905 request->n_channels = i;
2906
Johannes Berg2a519312009-02-10 21:25:55 +01002907 i = 0;
2908 if (info->attrs[NL80211_ATTR_SCAN_SSIDS]) {
2909 nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS], tmp) {
2910 if (request->ssids[i].ssid_len > IEEE80211_MAX_SSID_LEN) {
2911 err = -EINVAL;
2912 goto out_free;
2913 }
2914 memcpy(request->ssids[i].ssid, nla_data(attr), nla_len(attr));
2915 request->ssids[i].ssid_len = nla_len(attr);
2916 i++;
2917 }
2918 }
2919
Jouni Malinen70692ad2009-02-16 19:39:13 +02002920 if (info->attrs[NL80211_ATTR_IE]) {
2921 request->ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
Johannes Bergde95a542009-04-01 11:58:36 +02002922 memcpy((void *)request->ie,
2923 nla_data(info->attrs[NL80211_ATTR_IE]),
Jouni Malinen70692ad2009-02-16 19:39:13 +02002924 request->ie_len);
2925 }
2926
Johannes Berg463d0182009-07-14 00:33:35 +02002927 request->dev = dev;
Johannes Berg79c97e92009-07-07 03:56:12 +02002928 request->wiphy = &rdev->wiphy;
Johannes Berg2a519312009-02-10 21:25:55 +01002929
Johannes Berg79c97e92009-07-07 03:56:12 +02002930 rdev->scan_req = request;
2931 err = rdev->ops->scan(&rdev->wiphy, dev, request);
Johannes Berg2a519312009-02-10 21:25:55 +01002932
Johannes Berg463d0182009-07-14 00:33:35 +02002933 if (!err) {
Johannes Berg79c97e92009-07-07 03:56:12 +02002934 nl80211_send_scan_start(rdev, dev);
Johannes Berg463d0182009-07-14 00:33:35 +02002935 dev_hold(dev);
Johannes Berg4c476992010-10-04 21:36:35 +02002936 } else {
Johannes Berg2a519312009-02-10 21:25:55 +01002937 out_free:
Johannes Berg79c97e92009-07-07 03:56:12 +02002938 rdev->scan_req = NULL;
Johannes Berg2a519312009-02-10 21:25:55 +01002939 kfree(request);
2940 }
Johannes Berg3b858752009-03-12 09:55:09 +01002941
Johannes Berg2a519312009-02-10 21:25:55 +01002942 return err;
2943}
2944
2945static int nl80211_send_bss(struct sk_buff *msg, u32 pid, u32 seq, int flags,
2946 struct cfg80211_registered_device *rdev,
Johannes Berg48ab9052009-07-10 18:42:31 +02002947 struct wireless_dev *wdev,
2948 struct cfg80211_internal_bss *intbss)
Johannes Berg2a519312009-02-10 21:25:55 +01002949{
Johannes Berg48ab9052009-07-10 18:42:31 +02002950 struct cfg80211_bss *res = &intbss->pub;
Johannes Berg2a519312009-02-10 21:25:55 +01002951 void *hdr;
2952 struct nlattr *bss;
Johannes Berg48ab9052009-07-10 18:42:31 +02002953 int i;
2954
2955 ASSERT_WDEV_LOCK(wdev);
Johannes Berg2a519312009-02-10 21:25:55 +01002956
2957 hdr = nl80211hdr_put(msg, pid, seq, flags,
2958 NL80211_CMD_NEW_SCAN_RESULTS);
2959 if (!hdr)
2960 return -1;
2961
Johannes Bergf5ea9122009-08-07 16:17:38 +02002962 NLA_PUT_U32(msg, NL80211_ATTR_GENERATION, rdev->bss_generation);
Johannes Berg48ab9052009-07-10 18:42:31 +02002963 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, wdev->netdev->ifindex);
Johannes Berg2a519312009-02-10 21:25:55 +01002964
2965 bss = nla_nest_start(msg, NL80211_ATTR_BSS);
2966 if (!bss)
2967 goto nla_put_failure;
2968 if (!is_zero_ether_addr(res->bssid))
2969 NLA_PUT(msg, NL80211_BSS_BSSID, ETH_ALEN, res->bssid);
2970 if (res->information_elements && res->len_information_elements)
2971 NLA_PUT(msg, NL80211_BSS_INFORMATION_ELEMENTS,
2972 res->len_information_elements,
2973 res->information_elements);
Jouni Malinen34a6edd2010-01-06 16:19:24 +02002974 if (res->beacon_ies && res->len_beacon_ies &&
2975 res->beacon_ies != res->information_elements)
2976 NLA_PUT(msg, NL80211_BSS_BEACON_IES,
2977 res->len_beacon_ies, res->beacon_ies);
Johannes Berg2a519312009-02-10 21:25:55 +01002978 if (res->tsf)
2979 NLA_PUT_U64(msg, NL80211_BSS_TSF, res->tsf);
2980 if (res->beacon_interval)
2981 NLA_PUT_U16(msg, NL80211_BSS_BEACON_INTERVAL, res->beacon_interval);
2982 NLA_PUT_U16(msg, NL80211_BSS_CAPABILITY, res->capability);
2983 NLA_PUT_U32(msg, NL80211_BSS_FREQUENCY, res->channel->center_freq);
Holger Schurig7c896062009-09-24 12:21:01 +02002984 NLA_PUT_U32(msg, NL80211_BSS_SEEN_MS_AGO,
2985 jiffies_to_msecs(jiffies - intbss->ts));
Johannes Berg2a519312009-02-10 21:25:55 +01002986
Johannes Berg77965c92009-02-18 18:45:06 +01002987 switch (rdev->wiphy.signal_type) {
Johannes Berg2a519312009-02-10 21:25:55 +01002988 case CFG80211_SIGNAL_TYPE_MBM:
2989 NLA_PUT_U32(msg, NL80211_BSS_SIGNAL_MBM, res->signal);
2990 break;
2991 case CFG80211_SIGNAL_TYPE_UNSPEC:
2992 NLA_PUT_U8(msg, NL80211_BSS_SIGNAL_UNSPEC, res->signal);
2993 break;
2994 default:
2995 break;
2996 }
2997
Johannes Berg48ab9052009-07-10 18:42:31 +02002998 switch (wdev->iftype) {
Johannes Berg074ac8d2010-09-16 14:58:22 +02002999 case NL80211_IFTYPE_P2P_CLIENT:
Johannes Berg48ab9052009-07-10 18:42:31 +02003000 case NL80211_IFTYPE_STATION:
3001 if (intbss == wdev->current_bss)
3002 NLA_PUT_U32(msg, NL80211_BSS_STATUS,
3003 NL80211_BSS_STATUS_ASSOCIATED);
3004 else for (i = 0; i < MAX_AUTH_BSSES; i++) {
3005 if (intbss != wdev->auth_bsses[i])
3006 continue;
3007 NLA_PUT_U32(msg, NL80211_BSS_STATUS,
3008 NL80211_BSS_STATUS_AUTHENTICATED);
3009 break;
3010 }
3011 break;
3012 case NL80211_IFTYPE_ADHOC:
3013 if (intbss == wdev->current_bss)
3014 NLA_PUT_U32(msg, NL80211_BSS_STATUS,
3015 NL80211_BSS_STATUS_IBSS_JOINED);
3016 break;
3017 default:
3018 break;
3019 }
3020
Johannes Berg2a519312009-02-10 21:25:55 +01003021 nla_nest_end(msg, bss);
3022
3023 return genlmsg_end(msg, hdr);
3024
3025 nla_put_failure:
3026 genlmsg_cancel(msg, hdr);
3027 return -EMSGSIZE;
3028}
3029
3030static int nl80211_dump_scan(struct sk_buff *skb,
3031 struct netlink_callback *cb)
3032{
Johannes Berg48ab9052009-07-10 18:42:31 +02003033 struct cfg80211_registered_device *rdev;
3034 struct net_device *dev;
Johannes Berg2a519312009-02-10 21:25:55 +01003035 struct cfg80211_internal_bss *scan;
Johannes Berg48ab9052009-07-10 18:42:31 +02003036 struct wireless_dev *wdev;
Johannes Berg2a519312009-02-10 21:25:55 +01003037 int ifidx = cb->args[0];
3038 int start = cb->args[1], idx = 0;
3039 int err;
3040
Holger Schuriga0438972009-11-11 11:30:02 +01003041 if (!ifidx)
3042 ifidx = nl80211_get_ifidx(cb);
3043 if (ifidx < 0)
3044 return ifidx;
3045 cb->args[0] = ifidx;
Johannes Berg2a519312009-02-10 21:25:55 +01003046
Johannes Berg463d0182009-07-14 00:33:35 +02003047 dev = dev_get_by_index(sock_net(skb->sk), ifidx);
Johannes Berg48ab9052009-07-10 18:42:31 +02003048 if (!dev)
Johannes Berg2a519312009-02-10 21:25:55 +01003049 return -ENODEV;
3050
Johannes Berg463d0182009-07-14 00:33:35 +02003051 rdev = cfg80211_get_dev_from_ifindex(sock_net(skb->sk), ifidx);
Johannes Berg48ab9052009-07-10 18:42:31 +02003052 if (IS_ERR(rdev)) {
3053 err = PTR_ERR(rdev);
Johannes Berg2a519312009-02-10 21:25:55 +01003054 goto out_put_netdev;
3055 }
3056
Johannes Berg48ab9052009-07-10 18:42:31 +02003057 wdev = dev->ieee80211_ptr;
Johannes Berg2a519312009-02-10 21:25:55 +01003058
Johannes Berg48ab9052009-07-10 18:42:31 +02003059 wdev_lock(wdev);
3060 spin_lock_bh(&rdev->bss_lock);
3061 cfg80211_bss_expire(rdev);
3062
3063 list_for_each_entry(scan, &rdev->bss_list, list) {
Johannes Berg2a519312009-02-10 21:25:55 +01003064 if (++idx <= start)
3065 continue;
3066 if (nl80211_send_bss(skb,
3067 NETLINK_CB(cb->skb).pid,
3068 cb->nlh->nlmsg_seq, NLM_F_MULTI,
Johannes Berg48ab9052009-07-10 18:42:31 +02003069 rdev, wdev, scan) < 0) {
Johannes Berg2a519312009-02-10 21:25:55 +01003070 idx--;
3071 goto out;
3072 }
3073 }
3074
3075 out:
Johannes Berg48ab9052009-07-10 18:42:31 +02003076 spin_unlock_bh(&rdev->bss_lock);
3077 wdev_unlock(wdev);
Johannes Berg2a519312009-02-10 21:25:55 +01003078
3079 cb->args[1] = idx;
3080 err = skb->len;
Johannes Berg48ab9052009-07-10 18:42:31 +02003081 cfg80211_unlock_rdev(rdev);
Johannes Berg2a519312009-02-10 21:25:55 +01003082 out_put_netdev:
Johannes Berg48ab9052009-07-10 18:42:31 +02003083 dev_put(dev);
Johannes Berg2a519312009-02-10 21:25:55 +01003084
3085 return err;
3086}
3087
Holger Schurig61fa7132009-11-11 12:25:40 +01003088static int nl80211_send_survey(struct sk_buff *msg, u32 pid, u32 seq,
3089 int flags, struct net_device *dev,
3090 struct survey_info *survey)
3091{
3092 void *hdr;
3093 struct nlattr *infoattr;
3094
3095 /* Survey without a channel doesn't make sense */
3096 if (!survey->channel)
3097 return -EINVAL;
3098
3099 hdr = nl80211hdr_put(msg, pid, seq, flags,
3100 NL80211_CMD_NEW_SURVEY_RESULTS);
3101 if (!hdr)
3102 return -ENOMEM;
3103
3104 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
3105
3106 infoattr = nla_nest_start(msg, NL80211_ATTR_SURVEY_INFO);
3107 if (!infoattr)
3108 goto nla_put_failure;
3109
3110 NLA_PUT_U32(msg, NL80211_SURVEY_INFO_FREQUENCY,
3111 survey->channel->center_freq);
3112 if (survey->filled & SURVEY_INFO_NOISE_DBM)
3113 NLA_PUT_U8(msg, NL80211_SURVEY_INFO_NOISE,
3114 survey->noise);
Felix Fietkau17e5a802010-09-29 17:15:30 +02003115 if (survey->filled & SURVEY_INFO_IN_USE)
3116 NLA_PUT_FLAG(msg, NL80211_SURVEY_INFO_IN_USE);
Holger Schurig61fa7132009-11-11 12:25:40 +01003117
3118 nla_nest_end(msg, infoattr);
3119
3120 return genlmsg_end(msg, hdr);
3121
3122 nla_put_failure:
3123 genlmsg_cancel(msg, hdr);
3124 return -EMSGSIZE;
3125}
3126
3127static int nl80211_dump_survey(struct sk_buff *skb,
3128 struct netlink_callback *cb)
3129{
3130 struct survey_info survey;
3131 struct cfg80211_registered_device *dev;
3132 struct net_device *netdev;
3133 int ifidx = cb->args[0];
3134 int survey_idx = cb->args[1];
3135 int res;
3136
3137 if (!ifidx)
3138 ifidx = nl80211_get_ifidx(cb);
3139 if (ifidx < 0)
3140 return ifidx;
3141 cb->args[0] = ifidx;
3142
3143 rtnl_lock();
3144
3145 netdev = __dev_get_by_index(sock_net(skb->sk), ifidx);
3146 if (!netdev) {
3147 res = -ENODEV;
3148 goto out_rtnl;
3149 }
3150
3151 dev = cfg80211_get_dev_from_ifindex(sock_net(skb->sk), ifidx);
3152 if (IS_ERR(dev)) {
3153 res = PTR_ERR(dev);
3154 goto out_rtnl;
3155 }
3156
3157 if (!dev->ops->dump_survey) {
3158 res = -EOPNOTSUPP;
3159 goto out_err;
3160 }
3161
3162 while (1) {
3163 res = dev->ops->dump_survey(&dev->wiphy, netdev, survey_idx,
3164 &survey);
3165 if (res == -ENOENT)
3166 break;
3167 if (res)
3168 goto out_err;
3169
3170 if (nl80211_send_survey(skb,
3171 NETLINK_CB(cb->skb).pid,
3172 cb->nlh->nlmsg_seq, NLM_F_MULTI,
3173 netdev,
3174 &survey) < 0)
3175 goto out;
3176 survey_idx++;
3177 }
3178
3179 out:
3180 cb->args[1] = survey_idx;
3181 res = skb->len;
3182 out_err:
3183 cfg80211_unlock_rdev(dev);
3184 out_rtnl:
3185 rtnl_unlock();
3186
3187 return res;
3188}
3189
Jouni Malinen255e7372009-03-20 21:21:17 +02003190static bool nl80211_valid_auth_type(enum nl80211_auth_type auth_type)
3191{
Samuel Ortizb23aa672009-07-01 21:26:54 +02003192 return auth_type <= NL80211_AUTHTYPE_MAX;
Jouni Malinen255e7372009-03-20 21:21:17 +02003193}
3194
Samuel Ortizb23aa672009-07-01 21:26:54 +02003195static bool nl80211_valid_wpa_versions(u32 wpa_versions)
3196{
3197 return !(wpa_versions & ~(NL80211_WPA_VERSION_1 |
3198 NL80211_WPA_VERSION_2));
3199}
3200
3201static bool nl80211_valid_akm_suite(u32 akm)
3202{
3203 return akm == WLAN_AKM_SUITE_8021X ||
3204 akm == WLAN_AKM_SUITE_PSK;
3205}
3206
3207static bool nl80211_valid_cipher_suite(u32 cipher)
3208{
3209 return cipher == WLAN_CIPHER_SUITE_WEP40 ||
3210 cipher == WLAN_CIPHER_SUITE_WEP104 ||
3211 cipher == WLAN_CIPHER_SUITE_TKIP ||
3212 cipher == WLAN_CIPHER_SUITE_CCMP ||
3213 cipher == WLAN_CIPHER_SUITE_AES_CMAC;
3214}
3215
3216
Jouni Malinen636a5d32009-03-19 13:39:22 +02003217static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info)
3218{
Johannes Berg4c476992010-10-04 21:36:35 +02003219 struct cfg80211_registered_device *rdev = info->user_ptr[0];
3220 struct net_device *dev = info->user_ptr[1];
Johannes Berg19957bb2009-07-02 17:20:43 +02003221 struct ieee80211_channel *chan;
3222 const u8 *bssid, *ssid, *ie = NULL;
3223 int err, ssid_len, ie_len = 0;
3224 enum nl80211_auth_type auth_type;
Johannes Bergfffd0932009-07-08 14:22:54 +02003225 struct key_parse key;
Jouni Malinend5cdfac2010-04-04 09:37:19 +03003226 bool local_state_change;
Jouni Malinen636a5d32009-03-19 13:39:22 +02003227
Johannes Bergf4a11bb2009-03-27 12:40:28 +01003228 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
3229 return -EINVAL;
3230
3231 if (!info->attrs[NL80211_ATTR_MAC])
3232 return -EINVAL;
3233
Jouni Malinen17780922009-03-27 20:52:47 +02003234 if (!info->attrs[NL80211_ATTR_AUTH_TYPE])
3235 return -EINVAL;
3236
Johannes Berg19957bb2009-07-02 17:20:43 +02003237 if (!info->attrs[NL80211_ATTR_SSID])
3238 return -EINVAL;
3239
3240 if (!info->attrs[NL80211_ATTR_WIPHY_FREQ])
3241 return -EINVAL;
3242
Johannes Bergfffd0932009-07-08 14:22:54 +02003243 err = nl80211_parse_key(info, &key);
3244 if (err)
3245 return err;
3246
3247 if (key.idx >= 0) {
3248 if (!key.p.key || !key.p.key_len)
3249 return -EINVAL;
3250 if ((key.p.cipher != WLAN_CIPHER_SUITE_WEP40 ||
3251 key.p.key_len != WLAN_KEY_LEN_WEP40) &&
3252 (key.p.cipher != WLAN_CIPHER_SUITE_WEP104 ||
3253 key.p.key_len != WLAN_KEY_LEN_WEP104))
3254 return -EINVAL;
3255 if (key.idx > 4)
3256 return -EINVAL;
3257 } else {
3258 key.p.key_len = 0;
3259 key.p.key = NULL;
3260 }
3261
Johannes Bergafea0b72010-08-10 09:46:42 +02003262 if (key.idx >= 0) {
3263 int i;
3264 bool ok = false;
3265 for (i = 0; i < rdev->wiphy.n_cipher_suites; i++) {
3266 if (key.p.cipher == rdev->wiphy.cipher_suites[i]) {
3267 ok = true;
3268 break;
3269 }
3270 }
Johannes Berg4c476992010-10-04 21:36:35 +02003271 if (!ok)
3272 return -EINVAL;
Johannes Bergafea0b72010-08-10 09:46:42 +02003273 }
3274
Johannes Berg4c476992010-10-04 21:36:35 +02003275 if (!rdev->ops->auth)
3276 return -EOPNOTSUPP;
Jouni Malinen636a5d32009-03-19 13:39:22 +02003277
Johannes Berg074ac8d2010-09-16 14:58:22 +02003278 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg4c476992010-10-04 21:36:35 +02003279 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT)
3280 return -EOPNOTSUPP;
Jouni Malineneec60b02009-03-20 21:21:19 +02003281
Johannes Berg19957bb2009-07-02 17:20:43 +02003282 bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
Johannes Berg79c97e92009-07-07 03:56:12 +02003283 chan = ieee80211_get_channel(&rdev->wiphy,
Johannes Berg19957bb2009-07-02 17:20:43 +02003284 nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]));
Johannes Berg4c476992010-10-04 21:36:35 +02003285 if (!chan || (chan->flags & IEEE80211_CHAN_DISABLED))
3286 return -EINVAL;
Jouni Malinen636a5d32009-03-19 13:39:22 +02003287
Johannes Berg19957bb2009-07-02 17:20:43 +02003288 ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
3289 ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
3290
3291 if (info->attrs[NL80211_ATTR_IE]) {
3292 ie = nla_data(info->attrs[NL80211_ATTR_IE]);
3293 ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
3294 }
3295
3296 auth_type = nla_get_u32(info->attrs[NL80211_ATTR_AUTH_TYPE]);
Johannes Berg4c476992010-10-04 21:36:35 +02003297 if (!nl80211_valid_auth_type(auth_type))
3298 return -EINVAL;
Johannes Berg19957bb2009-07-02 17:20:43 +02003299
Jouni Malinend5cdfac2010-04-04 09:37:19 +03003300 local_state_change = !!info->attrs[NL80211_ATTR_LOCAL_STATE_CHANGE];
3301
Johannes Berg4c476992010-10-04 21:36:35 +02003302 return cfg80211_mlme_auth(rdev, dev, chan, auth_type, bssid,
3303 ssid, ssid_len, ie, ie_len,
3304 key.p.key, key.p.key_len, key.idx,
3305 local_state_change);
Jouni Malinen636a5d32009-03-19 13:39:22 +02003306}
3307
Johannes Bergc0692b82010-08-27 14:26:53 +03003308static int nl80211_crypto_settings(struct cfg80211_registered_device *rdev,
3309 struct genl_info *info,
Johannes Berg3dc27d22009-07-02 21:36:37 +02003310 struct cfg80211_crypto_settings *settings,
3311 int cipher_limit)
Samuel Ortizb23aa672009-07-01 21:26:54 +02003312{
Johannes Bergc0b2bbd2009-07-25 16:54:36 +02003313 memset(settings, 0, sizeof(*settings));
3314
Samuel Ortizb23aa672009-07-01 21:26:54 +02003315 settings->control_port = info->attrs[NL80211_ATTR_CONTROL_PORT];
3316
Johannes Bergc0692b82010-08-27 14:26:53 +03003317 if (info->attrs[NL80211_ATTR_CONTROL_PORT_ETHERTYPE]) {
3318 u16 proto;
3319 proto = nla_get_u16(
3320 info->attrs[NL80211_ATTR_CONTROL_PORT_ETHERTYPE]);
3321 settings->control_port_ethertype = cpu_to_be16(proto);
3322 if (!(rdev->wiphy.flags & WIPHY_FLAG_CONTROL_PORT_PROTOCOL) &&
3323 proto != ETH_P_PAE)
3324 return -EINVAL;
3325 if (info->attrs[NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT])
3326 settings->control_port_no_encrypt = true;
3327 } else
3328 settings->control_port_ethertype = cpu_to_be16(ETH_P_PAE);
3329
Samuel Ortizb23aa672009-07-01 21:26:54 +02003330 if (info->attrs[NL80211_ATTR_CIPHER_SUITES_PAIRWISE]) {
3331 void *data;
3332 int len, i;
3333
3334 data = nla_data(info->attrs[NL80211_ATTR_CIPHER_SUITES_PAIRWISE]);
3335 len = nla_len(info->attrs[NL80211_ATTR_CIPHER_SUITES_PAIRWISE]);
3336 settings->n_ciphers_pairwise = len / sizeof(u32);
3337
3338 if (len % sizeof(u32))
3339 return -EINVAL;
3340
Johannes Berg3dc27d22009-07-02 21:36:37 +02003341 if (settings->n_ciphers_pairwise > cipher_limit)
Samuel Ortizb23aa672009-07-01 21:26:54 +02003342 return -EINVAL;
3343
3344 memcpy(settings->ciphers_pairwise, data, len);
3345
3346 for (i = 0; i < settings->n_ciphers_pairwise; i++)
3347 if (!nl80211_valid_cipher_suite(
3348 settings->ciphers_pairwise[i]))
3349 return -EINVAL;
3350 }
3351
3352 if (info->attrs[NL80211_ATTR_CIPHER_SUITE_GROUP]) {
3353 settings->cipher_group =
3354 nla_get_u32(info->attrs[NL80211_ATTR_CIPHER_SUITE_GROUP]);
3355 if (!nl80211_valid_cipher_suite(settings->cipher_group))
3356 return -EINVAL;
3357 }
3358
3359 if (info->attrs[NL80211_ATTR_WPA_VERSIONS]) {
3360 settings->wpa_versions =
3361 nla_get_u32(info->attrs[NL80211_ATTR_WPA_VERSIONS]);
3362 if (!nl80211_valid_wpa_versions(settings->wpa_versions))
3363 return -EINVAL;
3364 }
3365
3366 if (info->attrs[NL80211_ATTR_AKM_SUITES]) {
3367 void *data;
3368 int len, i;
3369
3370 data = nla_data(info->attrs[NL80211_ATTR_AKM_SUITES]);
3371 len = nla_len(info->attrs[NL80211_ATTR_AKM_SUITES]);
3372 settings->n_akm_suites = len / sizeof(u32);
3373
3374 if (len % sizeof(u32))
3375 return -EINVAL;
3376
3377 memcpy(settings->akm_suites, data, len);
3378
3379 for (i = 0; i < settings->n_ciphers_pairwise; i++)
3380 if (!nl80211_valid_akm_suite(settings->akm_suites[i]))
3381 return -EINVAL;
3382 }
3383
3384 return 0;
3385}
3386
Jouni Malinen636a5d32009-03-19 13:39:22 +02003387static int nl80211_associate(struct sk_buff *skb, struct genl_info *info)
3388{
Johannes Berg4c476992010-10-04 21:36:35 +02003389 struct cfg80211_registered_device *rdev = info->user_ptr[0];
3390 struct net_device *dev = info->user_ptr[1];
Johannes Berg19957bb2009-07-02 17:20:43 +02003391 struct cfg80211_crypto_settings crypto;
Johannes Bergf444de02010-05-05 15:25:02 +02003392 struct ieee80211_channel *chan;
Johannes Berg3e5d7642009-07-07 14:37:26 +02003393 const u8 *bssid, *ssid, *ie = NULL, *prev_bssid = NULL;
Johannes Berg19957bb2009-07-02 17:20:43 +02003394 int err, ssid_len, ie_len = 0;
3395 bool use_mfp = false;
Jouni Malinen636a5d32009-03-19 13:39:22 +02003396
Johannes Bergf4a11bb2009-03-27 12:40:28 +01003397 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
3398 return -EINVAL;
3399
3400 if (!info->attrs[NL80211_ATTR_MAC] ||
Johannes Berg19957bb2009-07-02 17:20:43 +02003401 !info->attrs[NL80211_ATTR_SSID] ||
3402 !info->attrs[NL80211_ATTR_WIPHY_FREQ])
Johannes Bergf4a11bb2009-03-27 12:40:28 +01003403 return -EINVAL;
3404
Johannes Berg4c476992010-10-04 21:36:35 +02003405 if (!rdev->ops->assoc)
3406 return -EOPNOTSUPP;
Jouni Malinen636a5d32009-03-19 13:39:22 +02003407
Johannes Berg074ac8d2010-09-16 14:58:22 +02003408 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg4c476992010-10-04 21:36:35 +02003409 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT)
3410 return -EOPNOTSUPP;
Jouni Malineneec60b02009-03-20 21:21:19 +02003411
Johannes Berg19957bb2009-07-02 17:20:43 +02003412 bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
Jouni Malinen636a5d32009-03-19 13:39:22 +02003413
Johannes Berg19957bb2009-07-02 17:20:43 +02003414 chan = ieee80211_get_channel(&rdev->wiphy,
3415 nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]));
Johannes Berg4c476992010-10-04 21:36:35 +02003416 if (!chan || (chan->flags & IEEE80211_CHAN_DISABLED))
3417 return -EINVAL;
Jouni Malinen636a5d32009-03-19 13:39:22 +02003418
Johannes Berg19957bb2009-07-02 17:20:43 +02003419 ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
3420 ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
Jouni Malinen636a5d32009-03-19 13:39:22 +02003421
3422 if (info->attrs[NL80211_ATTR_IE]) {
Johannes Berg19957bb2009-07-02 17:20:43 +02003423 ie = nla_data(info->attrs[NL80211_ATTR_IE]);
3424 ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
Jouni Malinen636a5d32009-03-19 13:39:22 +02003425 }
3426
Jouni Malinendc6382c2009-05-06 22:09:37 +03003427 if (info->attrs[NL80211_ATTR_USE_MFP]) {
Johannes Berg4f5dadc2009-07-07 03:56:10 +02003428 enum nl80211_mfp mfp =
Jouni Malinendc6382c2009-05-06 22:09:37 +03003429 nla_get_u32(info->attrs[NL80211_ATTR_USE_MFP]);
Johannes Berg4f5dadc2009-07-07 03:56:10 +02003430 if (mfp == NL80211_MFP_REQUIRED)
Johannes Berg19957bb2009-07-02 17:20:43 +02003431 use_mfp = true;
Johannes Berg4c476992010-10-04 21:36:35 +02003432 else if (mfp != NL80211_MFP_NO)
3433 return -EINVAL;
Jouni Malinendc6382c2009-05-06 22:09:37 +03003434 }
3435
Johannes Berg3e5d7642009-07-07 14:37:26 +02003436 if (info->attrs[NL80211_ATTR_PREV_BSSID])
3437 prev_bssid = nla_data(info->attrs[NL80211_ATTR_PREV_BSSID]);
3438
Johannes Bergc0692b82010-08-27 14:26:53 +03003439 err = nl80211_crypto_settings(rdev, info, &crypto, 1);
Samuel Ortizb23aa672009-07-01 21:26:54 +02003440 if (!err)
Johannes Berg3e5d7642009-07-07 14:37:26 +02003441 err = cfg80211_mlme_assoc(rdev, dev, chan, bssid, prev_bssid,
3442 ssid, ssid_len, ie, ie_len, use_mfp,
Johannes Berg19957bb2009-07-02 17:20:43 +02003443 &crypto);
Jouni Malinen636a5d32009-03-19 13:39:22 +02003444
Jouni Malinen636a5d32009-03-19 13:39:22 +02003445 return err;
3446}
3447
3448static int nl80211_deauthenticate(struct sk_buff *skb, struct genl_info *info)
3449{
Johannes Berg4c476992010-10-04 21:36:35 +02003450 struct cfg80211_registered_device *rdev = info->user_ptr[0];
3451 struct net_device *dev = info->user_ptr[1];
Johannes Berg19957bb2009-07-02 17:20:43 +02003452 const u8 *ie = NULL, *bssid;
Johannes Berg4c476992010-10-04 21:36:35 +02003453 int ie_len = 0;
Johannes Berg19957bb2009-07-02 17:20:43 +02003454 u16 reason_code;
Jouni Malinend5cdfac2010-04-04 09:37:19 +03003455 bool local_state_change;
Jouni Malinen636a5d32009-03-19 13:39:22 +02003456
Johannes Bergf4a11bb2009-03-27 12:40:28 +01003457 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
3458 return -EINVAL;
3459
3460 if (!info->attrs[NL80211_ATTR_MAC])
3461 return -EINVAL;
3462
3463 if (!info->attrs[NL80211_ATTR_REASON_CODE])
3464 return -EINVAL;
3465
Johannes Berg4c476992010-10-04 21:36:35 +02003466 if (!rdev->ops->deauth)
3467 return -EOPNOTSUPP;
Jouni Malinen636a5d32009-03-19 13:39:22 +02003468
Johannes Berg074ac8d2010-09-16 14:58:22 +02003469 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg4c476992010-10-04 21:36:35 +02003470 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT)
3471 return -EOPNOTSUPP;
Jouni Malineneec60b02009-03-20 21:21:19 +02003472
Johannes Berg19957bb2009-07-02 17:20:43 +02003473 bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
Jouni Malinen636a5d32009-03-19 13:39:22 +02003474
Johannes Berg19957bb2009-07-02 17:20:43 +02003475 reason_code = nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]);
3476 if (reason_code == 0) {
Johannes Bergf4a11bb2009-03-27 12:40:28 +01003477 /* Reason Code 0 is reserved */
Johannes Berg4c476992010-10-04 21:36:35 +02003478 return -EINVAL;
Jouni Malinen255e7372009-03-20 21:21:17 +02003479 }
Jouni Malinen636a5d32009-03-19 13:39:22 +02003480
3481 if (info->attrs[NL80211_ATTR_IE]) {
Johannes Berg19957bb2009-07-02 17:20:43 +02003482 ie = nla_data(info->attrs[NL80211_ATTR_IE]);
3483 ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
Jouni Malinen636a5d32009-03-19 13:39:22 +02003484 }
3485
Jouni Malinend5cdfac2010-04-04 09:37:19 +03003486 local_state_change = !!info->attrs[NL80211_ATTR_LOCAL_STATE_CHANGE];
3487
Johannes Berg4c476992010-10-04 21:36:35 +02003488 return cfg80211_mlme_deauth(rdev, dev, bssid, ie, ie_len, reason_code,
3489 local_state_change);
Jouni Malinen636a5d32009-03-19 13:39:22 +02003490}
3491
3492static int nl80211_disassociate(struct sk_buff *skb, struct genl_info *info)
3493{
Johannes Berg4c476992010-10-04 21:36:35 +02003494 struct cfg80211_registered_device *rdev = info->user_ptr[0];
3495 struct net_device *dev = info->user_ptr[1];
Johannes Berg19957bb2009-07-02 17:20:43 +02003496 const u8 *ie = NULL, *bssid;
Johannes Berg4c476992010-10-04 21:36:35 +02003497 int ie_len = 0;
Johannes Berg19957bb2009-07-02 17:20:43 +02003498 u16 reason_code;
Jouni Malinend5cdfac2010-04-04 09:37:19 +03003499 bool local_state_change;
Jouni Malinen636a5d32009-03-19 13:39:22 +02003500
Johannes Bergf4a11bb2009-03-27 12:40:28 +01003501 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
3502 return -EINVAL;
3503
3504 if (!info->attrs[NL80211_ATTR_MAC])
3505 return -EINVAL;
3506
3507 if (!info->attrs[NL80211_ATTR_REASON_CODE])
3508 return -EINVAL;
3509
Johannes Berg4c476992010-10-04 21:36:35 +02003510 if (!rdev->ops->disassoc)
3511 return -EOPNOTSUPP;
Jouni Malinen636a5d32009-03-19 13:39:22 +02003512
Johannes Berg074ac8d2010-09-16 14:58:22 +02003513 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg4c476992010-10-04 21:36:35 +02003514 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT)
3515 return -EOPNOTSUPP;
Jouni Malineneec60b02009-03-20 21:21:19 +02003516
Johannes Berg19957bb2009-07-02 17:20:43 +02003517 bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
Jouni Malinen636a5d32009-03-19 13:39:22 +02003518
Johannes Berg19957bb2009-07-02 17:20:43 +02003519 reason_code = nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]);
3520 if (reason_code == 0) {
Johannes Bergf4a11bb2009-03-27 12:40:28 +01003521 /* Reason Code 0 is reserved */
Johannes Berg4c476992010-10-04 21:36:35 +02003522 return -EINVAL;
Jouni Malinen255e7372009-03-20 21:21:17 +02003523 }
Jouni Malinen636a5d32009-03-19 13:39:22 +02003524
3525 if (info->attrs[NL80211_ATTR_IE]) {
Johannes Berg19957bb2009-07-02 17:20:43 +02003526 ie = nla_data(info->attrs[NL80211_ATTR_IE]);
3527 ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
Jouni Malinen636a5d32009-03-19 13:39:22 +02003528 }
3529
Jouni Malinend5cdfac2010-04-04 09:37:19 +03003530 local_state_change = !!info->attrs[NL80211_ATTR_LOCAL_STATE_CHANGE];
3531
Johannes Berg4c476992010-10-04 21:36:35 +02003532 return cfg80211_mlme_disassoc(rdev, dev, bssid, ie, ie_len, reason_code,
3533 local_state_change);
Jouni Malinen636a5d32009-03-19 13:39:22 +02003534}
3535
Johannes Berg04a773a2009-04-19 21:24:32 +02003536static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info)
3537{
Johannes Berg4c476992010-10-04 21:36:35 +02003538 struct cfg80211_registered_device *rdev = info->user_ptr[0];
3539 struct net_device *dev = info->user_ptr[1];
Johannes Berg04a773a2009-04-19 21:24:32 +02003540 struct cfg80211_ibss_params ibss;
3541 struct wiphy *wiphy;
Johannes Bergfffd0932009-07-08 14:22:54 +02003542 struct cfg80211_cached_keys *connkeys = NULL;
Johannes Berg04a773a2009-04-19 21:24:32 +02003543 int err;
3544
Johannes Berg8e30bc52009-04-22 17:45:38 +02003545 memset(&ibss, 0, sizeof(ibss));
3546
Johannes Berg04a773a2009-04-19 21:24:32 +02003547 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
3548 return -EINVAL;
3549
3550 if (!info->attrs[NL80211_ATTR_WIPHY_FREQ] ||
3551 !info->attrs[NL80211_ATTR_SSID] ||
3552 !nla_len(info->attrs[NL80211_ATTR_SSID]))
3553 return -EINVAL;
3554
Johannes Berg8e30bc52009-04-22 17:45:38 +02003555 ibss.beacon_interval = 100;
3556
3557 if (info->attrs[NL80211_ATTR_BEACON_INTERVAL]) {
3558 ibss.beacon_interval =
3559 nla_get_u32(info->attrs[NL80211_ATTR_BEACON_INTERVAL]);
3560 if (ibss.beacon_interval < 1 || ibss.beacon_interval > 10000)
3561 return -EINVAL;
3562 }
3563
Johannes Berg4c476992010-10-04 21:36:35 +02003564 if (!rdev->ops->join_ibss)
3565 return -EOPNOTSUPP;
Johannes Berg04a773a2009-04-19 21:24:32 +02003566
Johannes Berg4c476992010-10-04 21:36:35 +02003567 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC)
3568 return -EOPNOTSUPP;
Johannes Berg04a773a2009-04-19 21:24:32 +02003569
Johannes Berg79c97e92009-07-07 03:56:12 +02003570 wiphy = &rdev->wiphy;
Johannes Berg04a773a2009-04-19 21:24:32 +02003571
3572 if (info->attrs[NL80211_ATTR_MAC])
3573 ibss.bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
3574 ibss.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
3575 ibss.ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
3576
3577 if (info->attrs[NL80211_ATTR_IE]) {
3578 ibss.ie = nla_data(info->attrs[NL80211_ATTR_IE]);
3579 ibss.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
3580 }
3581
3582 ibss.channel = ieee80211_get_channel(wiphy,
3583 nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]));
3584 if (!ibss.channel ||
3585 ibss.channel->flags & IEEE80211_CHAN_NO_IBSS ||
Johannes Berg4c476992010-10-04 21:36:35 +02003586 ibss.channel->flags & IEEE80211_CHAN_DISABLED)
3587 return -EINVAL;
Johannes Berg04a773a2009-04-19 21:24:32 +02003588
3589 ibss.channel_fixed = !!info->attrs[NL80211_ATTR_FREQ_FIXED];
Johannes Bergfffd0932009-07-08 14:22:54 +02003590 ibss.privacy = !!info->attrs[NL80211_ATTR_PRIVACY];
Johannes Berg04a773a2009-04-19 21:24:32 +02003591
Teemu Paasikivifbd2c8d2010-06-14 12:55:31 +03003592 if (info->attrs[NL80211_ATTR_BSS_BASIC_RATES]) {
3593 u8 *rates =
3594 nla_data(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]);
3595 int n_rates =
3596 nla_len(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]);
3597 struct ieee80211_supported_band *sband =
3598 wiphy->bands[ibss.channel->band];
3599 int i, j;
3600
Johannes Berg4c476992010-10-04 21:36:35 +02003601 if (n_rates == 0)
3602 return -EINVAL;
Teemu Paasikivifbd2c8d2010-06-14 12:55:31 +03003603
3604 for (i = 0; i < n_rates; i++) {
3605 int rate = (rates[i] & 0x7f) * 5;
3606 bool found = false;
3607
3608 for (j = 0; j < sband->n_bitrates; j++) {
3609 if (sband->bitrates[j].bitrate == rate) {
3610 found = true;
3611 ibss.basic_rates |= BIT(j);
3612 break;
3613 }
3614 }
Johannes Berg4c476992010-10-04 21:36:35 +02003615 if (!found)
3616 return -EINVAL;
Teemu Paasikivifbd2c8d2010-06-14 12:55:31 +03003617 }
Teemu Paasikivifbd2c8d2010-06-14 12:55:31 +03003618 }
3619
Johannes Berg4c476992010-10-04 21:36:35 +02003620 if (ibss.privacy && info->attrs[NL80211_ATTR_KEYS]) {
3621 connkeys = nl80211_parse_connkeys(rdev,
3622 info->attrs[NL80211_ATTR_KEYS]);
3623 if (IS_ERR(connkeys))
3624 return PTR_ERR(connkeys);
3625 }
Johannes Berg04a773a2009-04-19 21:24:32 +02003626
Johannes Berg4c476992010-10-04 21:36:35 +02003627 err = cfg80211_join_ibss(rdev, dev, &ibss, connkeys);
Johannes Bergfffd0932009-07-08 14:22:54 +02003628 if (err)
3629 kfree(connkeys);
Johannes Berg04a773a2009-04-19 21:24:32 +02003630 return err;
3631}
3632
3633static int nl80211_leave_ibss(struct sk_buff *skb, struct genl_info *info)
3634{
Johannes Berg4c476992010-10-04 21:36:35 +02003635 struct cfg80211_registered_device *rdev = info->user_ptr[0];
3636 struct net_device *dev = info->user_ptr[1];
Johannes Berg04a773a2009-04-19 21:24:32 +02003637
Johannes Berg4c476992010-10-04 21:36:35 +02003638 if (!rdev->ops->leave_ibss)
3639 return -EOPNOTSUPP;
Johannes Berg04a773a2009-04-19 21:24:32 +02003640
Johannes Berg4c476992010-10-04 21:36:35 +02003641 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC)
3642 return -EOPNOTSUPP;
Johannes Berg04a773a2009-04-19 21:24:32 +02003643
Johannes Berg4c476992010-10-04 21:36:35 +02003644 return cfg80211_leave_ibss(rdev, dev, false);
Johannes Berg04a773a2009-04-19 21:24:32 +02003645}
3646
Johannes Bergaff89a92009-07-01 21:26:51 +02003647#ifdef CONFIG_NL80211_TESTMODE
3648static struct genl_multicast_group nl80211_testmode_mcgrp = {
3649 .name = "testmode",
3650};
3651
3652static int nl80211_testmode_do(struct sk_buff *skb, struct genl_info *info)
3653{
Johannes Berg4c476992010-10-04 21:36:35 +02003654 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Johannes Bergaff89a92009-07-01 21:26:51 +02003655 int err;
3656
3657 if (!info->attrs[NL80211_ATTR_TESTDATA])
3658 return -EINVAL;
3659
Johannes Bergaff89a92009-07-01 21:26:51 +02003660 err = -EOPNOTSUPP;
3661 if (rdev->ops->testmode_cmd) {
3662 rdev->testmode_info = info;
3663 err = rdev->ops->testmode_cmd(&rdev->wiphy,
3664 nla_data(info->attrs[NL80211_ATTR_TESTDATA]),
3665 nla_len(info->attrs[NL80211_ATTR_TESTDATA]));
3666 rdev->testmode_info = NULL;
3667 }
3668
Johannes Bergaff89a92009-07-01 21:26:51 +02003669 return err;
3670}
3671
3672static struct sk_buff *
3673__cfg80211_testmode_alloc_skb(struct cfg80211_registered_device *rdev,
3674 int approxlen, u32 pid, u32 seq, gfp_t gfp)
3675{
3676 struct sk_buff *skb;
3677 void *hdr;
3678 struct nlattr *data;
3679
3680 skb = nlmsg_new(approxlen + 100, gfp);
3681 if (!skb)
3682 return NULL;
3683
3684 hdr = nl80211hdr_put(skb, pid, seq, 0, NL80211_CMD_TESTMODE);
3685 if (!hdr) {
3686 kfree_skb(skb);
3687 return NULL;
3688 }
3689
3690 NLA_PUT_U32(skb, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
3691 data = nla_nest_start(skb, NL80211_ATTR_TESTDATA);
3692
3693 ((void **)skb->cb)[0] = rdev;
3694 ((void **)skb->cb)[1] = hdr;
3695 ((void **)skb->cb)[2] = data;
3696
3697 return skb;
3698
3699 nla_put_failure:
3700 kfree_skb(skb);
3701 return NULL;
3702}
3703
3704struct sk_buff *cfg80211_testmode_alloc_reply_skb(struct wiphy *wiphy,
3705 int approxlen)
3706{
3707 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
3708
3709 if (WARN_ON(!rdev->testmode_info))
3710 return NULL;
3711
3712 return __cfg80211_testmode_alloc_skb(rdev, approxlen,
3713 rdev->testmode_info->snd_pid,
3714 rdev->testmode_info->snd_seq,
3715 GFP_KERNEL);
3716}
3717EXPORT_SYMBOL(cfg80211_testmode_alloc_reply_skb);
3718
3719int cfg80211_testmode_reply(struct sk_buff *skb)
3720{
3721 struct cfg80211_registered_device *rdev = ((void **)skb->cb)[0];
3722 void *hdr = ((void **)skb->cb)[1];
3723 struct nlattr *data = ((void **)skb->cb)[2];
3724
3725 if (WARN_ON(!rdev->testmode_info)) {
3726 kfree_skb(skb);
3727 return -EINVAL;
3728 }
3729
3730 nla_nest_end(skb, data);
3731 genlmsg_end(skb, hdr);
3732 return genlmsg_reply(skb, rdev->testmode_info);
3733}
3734EXPORT_SYMBOL(cfg80211_testmode_reply);
3735
3736struct sk_buff *cfg80211_testmode_alloc_event_skb(struct wiphy *wiphy,
3737 int approxlen, gfp_t gfp)
3738{
3739 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
3740
3741 return __cfg80211_testmode_alloc_skb(rdev, approxlen, 0, 0, gfp);
3742}
3743EXPORT_SYMBOL(cfg80211_testmode_alloc_event_skb);
3744
3745void cfg80211_testmode_event(struct sk_buff *skb, gfp_t gfp)
3746{
3747 void *hdr = ((void **)skb->cb)[1];
3748 struct nlattr *data = ((void **)skb->cb)[2];
3749
3750 nla_nest_end(skb, data);
3751 genlmsg_end(skb, hdr);
3752 genlmsg_multicast(skb, 0, nl80211_testmode_mcgrp.id, gfp);
3753}
3754EXPORT_SYMBOL(cfg80211_testmode_event);
3755#endif
3756
Samuel Ortizb23aa672009-07-01 21:26:54 +02003757static int nl80211_connect(struct sk_buff *skb, struct genl_info *info)
3758{
Johannes Berg4c476992010-10-04 21:36:35 +02003759 struct cfg80211_registered_device *rdev = info->user_ptr[0];
3760 struct net_device *dev = info->user_ptr[1];
Samuel Ortizb23aa672009-07-01 21:26:54 +02003761 struct cfg80211_connect_params connect;
3762 struct wiphy *wiphy;
Johannes Bergfffd0932009-07-08 14:22:54 +02003763 struct cfg80211_cached_keys *connkeys = NULL;
Samuel Ortizb23aa672009-07-01 21:26:54 +02003764 int err;
3765
3766 memset(&connect, 0, sizeof(connect));
3767
3768 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
3769 return -EINVAL;
3770
3771 if (!info->attrs[NL80211_ATTR_SSID] ||
3772 !nla_len(info->attrs[NL80211_ATTR_SSID]))
3773 return -EINVAL;
3774
3775 if (info->attrs[NL80211_ATTR_AUTH_TYPE]) {
3776 connect.auth_type =
3777 nla_get_u32(info->attrs[NL80211_ATTR_AUTH_TYPE]);
3778 if (!nl80211_valid_auth_type(connect.auth_type))
3779 return -EINVAL;
3780 } else
3781 connect.auth_type = NL80211_AUTHTYPE_AUTOMATIC;
3782
3783 connect.privacy = info->attrs[NL80211_ATTR_PRIVACY];
3784
Johannes Bergc0692b82010-08-27 14:26:53 +03003785 err = nl80211_crypto_settings(rdev, info, &connect.crypto,
Johannes Berg3dc27d22009-07-02 21:36:37 +02003786 NL80211_MAX_NR_CIPHER_SUITES);
Samuel Ortizb23aa672009-07-01 21:26:54 +02003787 if (err)
3788 return err;
Samuel Ortizb23aa672009-07-01 21:26:54 +02003789
Johannes Berg074ac8d2010-09-16 14:58:22 +02003790 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg4c476992010-10-04 21:36:35 +02003791 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT)
3792 return -EOPNOTSUPP;
Samuel Ortizb23aa672009-07-01 21:26:54 +02003793
Johannes Berg79c97e92009-07-07 03:56:12 +02003794 wiphy = &rdev->wiphy;
Samuel Ortizb23aa672009-07-01 21:26:54 +02003795
Samuel Ortizb23aa672009-07-01 21:26:54 +02003796 if (info->attrs[NL80211_ATTR_MAC])
3797 connect.bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
3798 connect.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
3799 connect.ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
3800
3801 if (info->attrs[NL80211_ATTR_IE]) {
3802 connect.ie = nla_data(info->attrs[NL80211_ATTR_IE]);
3803 connect.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
3804 }
3805
3806 if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
3807 connect.channel =
3808 ieee80211_get_channel(wiphy,
3809 nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]));
3810 if (!connect.channel ||
Johannes Berg4c476992010-10-04 21:36:35 +02003811 connect.channel->flags & IEEE80211_CHAN_DISABLED)
3812 return -EINVAL;
Samuel Ortizb23aa672009-07-01 21:26:54 +02003813 }
3814
Johannes Bergfffd0932009-07-08 14:22:54 +02003815 if (connect.privacy && info->attrs[NL80211_ATTR_KEYS]) {
3816 connkeys = nl80211_parse_connkeys(rdev,
3817 info->attrs[NL80211_ATTR_KEYS]);
Johannes Berg4c476992010-10-04 21:36:35 +02003818 if (IS_ERR(connkeys))
3819 return PTR_ERR(connkeys);
Johannes Bergfffd0932009-07-08 14:22:54 +02003820 }
3821
3822 err = cfg80211_connect(rdev, dev, &connect, connkeys);
Johannes Bergfffd0932009-07-08 14:22:54 +02003823 if (err)
3824 kfree(connkeys);
Samuel Ortizb23aa672009-07-01 21:26:54 +02003825 return err;
3826}
3827
3828static int nl80211_disconnect(struct sk_buff *skb, struct genl_info *info)
3829{
Johannes Berg4c476992010-10-04 21:36:35 +02003830 struct cfg80211_registered_device *rdev = info->user_ptr[0];
3831 struct net_device *dev = info->user_ptr[1];
Samuel Ortizb23aa672009-07-01 21:26:54 +02003832 u16 reason;
3833
3834 if (!info->attrs[NL80211_ATTR_REASON_CODE])
3835 reason = WLAN_REASON_DEAUTH_LEAVING;
3836 else
3837 reason = nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]);
3838
3839 if (reason == 0)
3840 return -EINVAL;
3841
Johannes Berg074ac8d2010-09-16 14:58:22 +02003842 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg4c476992010-10-04 21:36:35 +02003843 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT)
3844 return -EOPNOTSUPP;
Samuel Ortizb23aa672009-07-01 21:26:54 +02003845
Johannes Berg4c476992010-10-04 21:36:35 +02003846 return cfg80211_disconnect(rdev, dev, reason, true);
Samuel Ortizb23aa672009-07-01 21:26:54 +02003847}
3848
Johannes Berg463d0182009-07-14 00:33:35 +02003849static int nl80211_wiphy_netns(struct sk_buff *skb, struct genl_info *info)
3850{
Johannes Berg4c476992010-10-04 21:36:35 +02003851 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Johannes Berg463d0182009-07-14 00:33:35 +02003852 struct net *net;
3853 int err;
3854 u32 pid;
3855
3856 if (!info->attrs[NL80211_ATTR_PID])
3857 return -EINVAL;
3858
3859 pid = nla_get_u32(info->attrs[NL80211_ATTR_PID]);
3860
Johannes Berg463d0182009-07-14 00:33:35 +02003861 net = get_net_ns_by_pid(pid);
Johannes Berg4c476992010-10-04 21:36:35 +02003862 if (IS_ERR(net))
3863 return PTR_ERR(net);
Johannes Berg463d0182009-07-14 00:33:35 +02003864
3865 err = 0;
3866
3867 /* check if anything to do */
Johannes Berg4c476992010-10-04 21:36:35 +02003868 if (!net_eq(wiphy_net(&rdev->wiphy), net))
3869 err = cfg80211_switch_netns(rdev, net);
Johannes Berg463d0182009-07-14 00:33:35 +02003870
Johannes Berg463d0182009-07-14 00:33:35 +02003871 put_net(net);
Johannes Berg463d0182009-07-14 00:33:35 +02003872 return err;
3873}
3874
Samuel Ortiz67fbb162009-11-24 23:59:15 +01003875static int nl80211_setdel_pmksa(struct sk_buff *skb, struct genl_info *info)
3876{
Johannes Berg4c476992010-10-04 21:36:35 +02003877 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Samuel Ortiz67fbb162009-11-24 23:59:15 +01003878 int (*rdev_ops)(struct wiphy *wiphy, struct net_device *dev,
3879 struct cfg80211_pmksa *pmksa) = NULL;
Johannes Berg4c476992010-10-04 21:36:35 +02003880 struct net_device *dev = info->user_ptr[1];
Samuel Ortiz67fbb162009-11-24 23:59:15 +01003881 struct cfg80211_pmksa pmksa;
3882
3883 memset(&pmksa, 0, sizeof(struct cfg80211_pmksa));
3884
3885 if (!info->attrs[NL80211_ATTR_MAC])
3886 return -EINVAL;
3887
3888 if (!info->attrs[NL80211_ATTR_PMKID])
3889 return -EINVAL;
3890
Samuel Ortiz67fbb162009-11-24 23:59:15 +01003891 pmksa.pmkid = nla_data(info->attrs[NL80211_ATTR_PMKID]);
3892 pmksa.bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
3893
Johannes Berg074ac8d2010-09-16 14:58:22 +02003894 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg4c476992010-10-04 21:36:35 +02003895 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT)
3896 return -EOPNOTSUPP;
Samuel Ortiz67fbb162009-11-24 23:59:15 +01003897
3898 switch (info->genlhdr->cmd) {
3899 case NL80211_CMD_SET_PMKSA:
3900 rdev_ops = rdev->ops->set_pmksa;
3901 break;
3902 case NL80211_CMD_DEL_PMKSA:
3903 rdev_ops = rdev->ops->del_pmksa;
3904 break;
3905 default:
3906 WARN_ON(1);
3907 break;
3908 }
3909
Johannes Berg4c476992010-10-04 21:36:35 +02003910 if (!rdev_ops)
3911 return -EOPNOTSUPP;
Samuel Ortiz67fbb162009-11-24 23:59:15 +01003912
Johannes Berg4c476992010-10-04 21:36:35 +02003913 return rdev_ops(&rdev->wiphy, dev, &pmksa);
Samuel Ortiz67fbb162009-11-24 23:59:15 +01003914}
3915
3916static int nl80211_flush_pmksa(struct sk_buff *skb, struct genl_info *info)
3917{
Johannes Berg4c476992010-10-04 21:36:35 +02003918 struct cfg80211_registered_device *rdev = info->user_ptr[0];
3919 struct net_device *dev = info->user_ptr[1];
Samuel Ortiz67fbb162009-11-24 23:59:15 +01003920
Johannes Berg074ac8d2010-09-16 14:58:22 +02003921 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg4c476992010-10-04 21:36:35 +02003922 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT)
3923 return -EOPNOTSUPP;
Samuel Ortiz67fbb162009-11-24 23:59:15 +01003924
Johannes Berg4c476992010-10-04 21:36:35 +02003925 if (!rdev->ops->flush_pmksa)
3926 return -EOPNOTSUPP;
Samuel Ortiz67fbb162009-11-24 23:59:15 +01003927
Johannes Berg4c476992010-10-04 21:36:35 +02003928 return rdev->ops->flush_pmksa(&rdev->wiphy, dev);
Samuel Ortiz67fbb162009-11-24 23:59:15 +01003929}
3930
Jouni Malinen9588bbd2009-12-23 13:15:41 +01003931static int nl80211_remain_on_channel(struct sk_buff *skb,
3932 struct genl_info *info)
3933{
Johannes Berg4c476992010-10-04 21:36:35 +02003934 struct cfg80211_registered_device *rdev = info->user_ptr[0];
3935 struct net_device *dev = info->user_ptr[1];
Jouni Malinen9588bbd2009-12-23 13:15:41 +01003936 struct ieee80211_channel *chan;
3937 struct sk_buff *msg;
3938 void *hdr;
3939 u64 cookie;
3940 enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT;
3941 u32 freq, duration;
3942 int err;
3943
3944 if (!info->attrs[NL80211_ATTR_WIPHY_FREQ] ||
3945 !info->attrs[NL80211_ATTR_DURATION])
3946 return -EINVAL;
3947
3948 duration = nla_get_u32(info->attrs[NL80211_ATTR_DURATION]);
3949
3950 /*
3951 * We should be on that channel for at least one jiffie,
3952 * and more than 5 seconds seems excessive.
3953 */
3954 if (!duration || !msecs_to_jiffies(duration) || duration > 5000)
3955 return -EINVAL;
3956
Johannes Berg4c476992010-10-04 21:36:35 +02003957 if (!rdev->ops->remain_on_channel)
3958 return -EOPNOTSUPP;
Jouni Malinen9588bbd2009-12-23 13:15:41 +01003959
Jouni Malinen9588bbd2009-12-23 13:15:41 +01003960 if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) {
3961 channel_type = nla_get_u32(
3962 info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]);
3963 if (channel_type != NL80211_CHAN_NO_HT &&
3964 channel_type != NL80211_CHAN_HT20 &&
3965 channel_type != NL80211_CHAN_HT40PLUS &&
Johannes Berg4c476992010-10-04 21:36:35 +02003966 channel_type != NL80211_CHAN_HT40MINUS)
3967 return -EINVAL;
Jouni Malinen9588bbd2009-12-23 13:15:41 +01003968 }
3969
3970 freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]);
3971 chan = rdev_freq_to_chan(rdev, freq, channel_type);
Johannes Berg4c476992010-10-04 21:36:35 +02003972 if (chan == NULL)
3973 return -EINVAL;
Jouni Malinen9588bbd2009-12-23 13:15:41 +01003974
3975 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg4c476992010-10-04 21:36:35 +02003976 if (!msg)
3977 return -ENOMEM;
Jouni Malinen9588bbd2009-12-23 13:15:41 +01003978
3979 hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
3980 NL80211_CMD_REMAIN_ON_CHANNEL);
3981
3982 if (IS_ERR(hdr)) {
3983 err = PTR_ERR(hdr);
3984 goto free_msg;
3985 }
3986
3987 err = rdev->ops->remain_on_channel(&rdev->wiphy, dev, chan,
3988 channel_type, duration, &cookie);
3989
3990 if (err)
3991 goto free_msg;
3992
3993 NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, cookie);
3994
3995 genlmsg_end(msg, hdr);
Johannes Berg4c476992010-10-04 21:36:35 +02003996
3997 return genlmsg_reply(msg, info);
Jouni Malinen9588bbd2009-12-23 13:15:41 +01003998
3999 nla_put_failure:
4000 err = -ENOBUFS;
4001 free_msg:
4002 nlmsg_free(msg);
Jouni Malinen9588bbd2009-12-23 13:15:41 +01004003 return err;
4004}
4005
4006static int nl80211_cancel_remain_on_channel(struct sk_buff *skb,
4007 struct genl_info *info)
4008{
Johannes Berg4c476992010-10-04 21:36:35 +02004009 struct cfg80211_registered_device *rdev = info->user_ptr[0];
4010 struct net_device *dev = info->user_ptr[1];
Jouni Malinen9588bbd2009-12-23 13:15:41 +01004011 u64 cookie;
Jouni Malinen9588bbd2009-12-23 13:15:41 +01004012
4013 if (!info->attrs[NL80211_ATTR_COOKIE])
4014 return -EINVAL;
4015
Johannes Berg4c476992010-10-04 21:36:35 +02004016 if (!rdev->ops->cancel_remain_on_channel)
4017 return -EOPNOTSUPP;
Jouni Malinen9588bbd2009-12-23 13:15:41 +01004018
Jouni Malinen9588bbd2009-12-23 13:15:41 +01004019 cookie = nla_get_u64(info->attrs[NL80211_ATTR_COOKIE]);
4020
Johannes Berg4c476992010-10-04 21:36:35 +02004021 return rdev->ops->cancel_remain_on_channel(&rdev->wiphy, dev, cookie);
Jouni Malinen9588bbd2009-12-23 13:15:41 +01004022}
4023
Jouni Malinen13ae75b2009-12-29 12:59:45 +02004024static u32 rateset_to_mask(struct ieee80211_supported_band *sband,
4025 u8 *rates, u8 rates_len)
4026{
4027 u8 i;
4028 u32 mask = 0;
4029
4030 for (i = 0; i < rates_len; i++) {
4031 int rate = (rates[i] & 0x7f) * 5;
4032 int ridx;
4033 for (ridx = 0; ridx < sband->n_bitrates; ridx++) {
4034 struct ieee80211_rate *srate =
4035 &sband->bitrates[ridx];
4036 if (rate == srate->bitrate) {
4037 mask |= 1 << ridx;
4038 break;
4039 }
4040 }
4041 if (ridx == sband->n_bitrates)
4042 return 0; /* rate not found */
4043 }
4044
4045 return mask;
4046}
4047
Alexey Dobriyanb54452b2010-02-18 08:14:31 +00004048static const struct nla_policy nl80211_txattr_policy[NL80211_TXRATE_MAX + 1] = {
Jouni Malinen13ae75b2009-12-29 12:59:45 +02004049 [NL80211_TXRATE_LEGACY] = { .type = NLA_BINARY,
4050 .len = NL80211_MAX_SUPP_RATES },
4051};
4052
4053static int nl80211_set_tx_bitrate_mask(struct sk_buff *skb,
4054 struct genl_info *info)
4055{
4056 struct nlattr *tb[NL80211_TXRATE_MAX + 1];
Johannes Berg4c476992010-10-04 21:36:35 +02004057 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Jouni Malinen13ae75b2009-12-29 12:59:45 +02004058 struct cfg80211_bitrate_mask mask;
Johannes Berg4c476992010-10-04 21:36:35 +02004059 int rem, i;
4060 struct net_device *dev = info->user_ptr[1];
Jouni Malinen13ae75b2009-12-29 12:59:45 +02004061 struct nlattr *tx_rates;
4062 struct ieee80211_supported_band *sband;
4063
4064 if (info->attrs[NL80211_ATTR_TX_RATES] == NULL)
4065 return -EINVAL;
4066
Johannes Berg4c476992010-10-04 21:36:35 +02004067 if (!rdev->ops->set_bitrate_mask)
4068 return -EOPNOTSUPP;
Jouni Malinen13ae75b2009-12-29 12:59:45 +02004069
4070 memset(&mask, 0, sizeof(mask));
4071 /* Default to all rates enabled */
4072 for (i = 0; i < IEEE80211_NUM_BANDS; i++) {
4073 sband = rdev->wiphy.bands[i];
4074 mask.control[i].legacy =
4075 sband ? (1 << sband->n_bitrates) - 1 : 0;
4076 }
4077
4078 /*
4079 * The nested attribute uses enum nl80211_band as the index. This maps
4080 * directly to the enum ieee80211_band values used in cfg80211.
4081 */
4082 nla_for_each_nested(tx_rates, info->attrs[NL80211_ATTR_TX_RATES], rem)
4083 {
4084 enum ieee80211_band band = nla_type(tx_rates);
Johannes Berg4c476992010-10-04 21:36:35 +02004085 if (band < 0 || band >= IEEE80211_NUM_BANDS)
4086 return -EINVAL;
Jouni Malinen13ae75b2009-12-29 12:59:45 +02004087 sband = rdev->wiphy.bands[band];
Johannes Berg4c476992010-10-04 21:36:35 +02004088 if (sband == NULL)
4089 return -EINVAL;
Jouni Malinen13ae75b2009-12-29 12:59:45 +02004090 nla_parse(tb, NL80211_TXRATE_MAX, nla_data(tx_rates),
4091 nla_len(tx_rates), nl80211_txattr_policy);
4092 if (tb[NL80211_TXRATE_LEGACY]) {
4093 mask.control[band].legacy = rateset_to_mask(
4094 sband,
4095 nla_data(tb[NL80211_TXRATE_LEGACY]),
4096 nla_len(tb[NL80211_TXRATE_LEGACY]));
Johannes Berg4c476992010-10-04 21:36:35 +02004097 if (mask.control[band].legacy == 0)
4098 return -EINVAL;
Jouni Malinen13ae75b2009-12-29 12:59:45 +02004099 }
4100 }
4101
Johannes Berg4c476992010-10-04 21:36:35 +02004102 return rdev->ops->set_bitrate_mask(&rdev->wiphy, dev, NULL, &mask);
Jouni Malinen13ae75b2009-12-29 12:59:45 +02004103}
4104
Johannes Berg2e161f72010-08-12 15:38:38 +02004105static int nl80211_register_mgmt(struct sk_buff *skb, struct genl_info *info)
Jouni Malinen026331c2010-02-15 12:53:10 +02004106{
Johannes Berg4c476992010-10-04 21:36:35 +02004107 struct cfg80211_registered_device *rdev = info->user_ptr[0];
4108 struct net_device *dev = info->user_ptr[1];
Johannes Berg2e161f72010-08-12 15:38:38 +02004109 u16 frame_type = IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_ACTION;
Jouni Malinen026331c2010-02-15 12:53:10 +02004110
4111 if (!info->attrs[NL80211_ATTR_FRAME_MATCH])
4112 return -EINVAL;
4113
Johannes Berg2e161f72010-08-12 15:38:38 +02004114 if (info->attrs[NL80211_ATTR_FRAME_TYPE])
4115 frame_type = nla_get_u16(info->attrs[NL80211_ATTR_FRAME_TYPE]);
Jouni Malinen026331c2010-02-15 12:53:10 +02004116
Johannes Berg9d38d852010-06-09 17:20:33 +02004117 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg074ac8d2010-09-16 14:58:22 +02004118 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC &&
Johannes Berg663fcaf2010-09-30 21:06:09 +02004119 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT &&
4120 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
4121 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN &&
Johannes Berg4c476992010-10-04 21:36:35 +02004122 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
4123 return -EOPNOTSUPP;
Jouni Malinen026331c2010-02-15 12:53:10 +02004124
4125 /* not much point in registering if we can't reply */
Johannes Berg4c476992010-10-04 21:36:35 +02004126 if (!rdev->ops->mgmt_tx)
4127 return -EOPNOTSUPP;
Jouni Malinen026331c2010-02-15 12:53:10 +02004128
Johannes Berg4c476992010-10-04 21:36:35 +02004129 return cfg80211_mlme_register_mgmt(dev->ieee80211_ptr, info->snd_pid,
Johannes Berg2e161f72010-08-12 15:38:38 +02004130 frame_type,
Jouni Malinen026331c2010-02-15 12:53:10 +02004131 nla_data(info->attrs[NL80211_ATTR_FRAME_MATCH]),
4132 nla_len(info->attrs[NL80211_ATTR_FRAME_MATCH]));
Jouni Malinen026331c2010-02-15 12:53:10 +02004133}
4134
Johannes Berg2e161f72010-08-12 15:38:38 +02004135static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info)
Jouni Malinen026331c2010-02-15 12:53:10 +02004136{
Johannes Berg4c476992010-10-04 21:36:35 +02004137 struct cfg80211_registered_device *rdev = info->user_ptr[0];
4138 struct net_device *dev = info->user_ptr[1];
Jouni Malinen026331c2010-02-15 12:53:10 +02004139 struct ieee80211_channel *chan;
4140 enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT;
Johannes Berg252aa632010-05-19 12:17:12 +02004141 bool channel_type_valid = false;
Jouni Malinen026331c2010-02-15 12:53:10 +02004142 u32 freq;
4143 int err;
4144 void *hdr;
4145 u64 cookie;
4146 struct sk_buff *msg;
4147
4148 if (!info->attrs[NL80211_ATTR_FRAME] ||
4149 !info->attrs[NL80211_ATTR_WIPHY_FREQ])
4150 return -EINVAL;
4151
Johannes Berg4c476992010-10-04 21:36:35 +02004152 if (!rdev->ops->mgmt_tx)
4153 return -EOPNOTSUPP;
Jouni Malinen026331c2010-02-15 12:53:10 +02004154
Johannes Berg9d38d852010-06-09 17:20:33 +02004155 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg074ac8d2010-09-16 14:58:22 +02004156 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC &&
Johannes Berg663fcaf2010-09-30 21:06:09 +02004157 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT &&
4158 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
4159 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN &&
Johannes Berg4c476992010-10-04 21:36:35 +02004160 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
4161 return -EOPNOTSUPP;
Jouni Malinen026331c2010-02-15 12:53:10 +02004162
Jouni Malinen026331c2010-02-15 12:53:10 +02004163 if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) {
4164 channel_type = nla_get_u32(
4165 info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]);
4166 if (channel_type != NL80211_CHAN_NO_HT &&
4167 channel_type != NL80211_CHAN_HT20 &&
4168 channel_type != NL80211_CHAN_HT40PLUS &&
Johannes Berg4c476992010-10-04 21:36:35 +02004169 channel_type != NL80211_CHAN_HT40MINUS)
4170 return -EINVAL;
Johannes Berg252aa632010-05-19 12:17:12 +02004171 channel_type_valid = true;
Jouni Malinen026331c2010-02-15 12:53:10 +02004172 }
4173
4174 freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]);
4175 chan = rdev_freq_to_chan(rdev, freq, channel_type);
Johannes Berg4c476992010-10-04 21:36:35 +02004176 if (chan == NULL)
4177 return -EINVAL;
Jouni Malinen026331c2010-02-15 12:53:10 +02004178
4179 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg4c476992010-10-04 21:36:35 +02004180 if (!msg)
4181 return -ENOMEM;
Jouni Malinen026331c2010-02-15 12:53:10 +02004182
4183 hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
Johannes Berg2e161f72010-08-12 15:38:38 +02004184 NL80211_CMD_FRAME);
Jouni Malinen026331c2010-02-15 12:53:10 +02004185
4186 if (IS_ERR(hdr)) {
4187 err = PTR_ERR(hdr);
4188 goto free_msg;
4189 }
Johannes Berg2e161f72010-08-12 15:38:38 +02004190 err = cfg80211_mlme_mgmt_tx(rdev, dev, chan, channel_type,
4191 channel_type_valid,
4192 nla_data(info->attrs[NL80211_ATTR_FRAME]),
4193 nla_len(info->attrs[NL80211_ATTR_FRAME]),
4194 &cookie);
Jouni Malinen026331c2010-02-15 12:53:10 +02004195 if (err)
4196 goto free_msg;
4197
4198 NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, cookie);
4199
4200 genlmsg_end(msg, hdr);
Johannes Berg4c476992010-10-04 21:36:35 +02004201 return genlmsg_reply(msg, info);
Jouni Malinen026331c2010-02-15 12:53:10 +02004202
4203 nla_put_failure:
4204 err = -ENOBUFS;
4205 free_msg:
4206 nlmsg_free(msg);
Jouni Malinen026331c2010-02-15 12:53:10 +02004207 return err;
4208}
4209
Kalle Valoffb9eb32010-02-17 17:58:10 +02004210static int nl80211_set_power_save(struct sk_buff *skb, struct genl_info *info)
4211{
Johannes Berg4c476992010-10-04 21:36:35 +02004212 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Kalle Valoffb9eb32010-02-17 17:58:10 +02004213 struct wireless_dev *wdev;
Johannes Berg4c476992010-10-04 21:36:35 +02004214 struct net_device *dev = info->user_ptr[1];
Kalle Valoffb9eb32010-02-17 17:58:10 +02004215 u8 ps_state;
4216 bool state;
4217 int err;
4218
Johannes Berg4c476992010-10-04 21:36:35 +02004219 if (!info->attrs[NL80211_ATTR_PS_STATE])
4220 return -EINVAL;
Kalle Valoffb9eb32010-02-17 17:58:10 +02004221
4222 ps_state = nla_get_u32(info->attrs[NL80211_ATTR_PS_STATE]);
4223
Johannes Berg4c476992010-10-04 21:36:35 +02004224 if (ps_state != NL80211_PS_DISABLED && ps_state != NL80211_PS_ENABLED)
4225 return -EINVAL;
Kalle Valoffb9eb32010-02-17 17:58:10 +02004226
4227 wdev = dev->ieee80211_ptr;
4228
Johannes Berg4c476992010-10-04 21:36:35 +02004229 if (!rdev->ops->set_power_mgmt)
4230 return -EOPNOTSUPP;
Kalle Valoffb9eb32010-02-17 17:58:10 +02004231
4232 state = (ps_state == NL80211_PS_ENABLED) ? true : false;
4233
4234 if (state == wdev->ps)
Johannes Berg4c476992010-10-04 21:36:35 +02004235 return 0;
Kalle Valoffb9eb32010-02-17 17:58:10 +02004236
Johannes Berg4c476992010-10-04 21:36:35 +02004237 err = rdev->ops->set_power_mgmt(wdev->wiphy, dev, state,
4238 wdev->ps_timeout);
4239 if (!err)
4240 wdev->ps = state;
Kalle Valoffb9eb32010-02-17 17:58:10 +02004241 return err;
4242}
4243
4244static int nl80211_get_power_save(struct sk_buff *skb, struct genl_info *info)
4245{
Johannes Berg4c476992010-10-04 21:36:35 +02004246 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Kalle Valoffb9eb32010-02-17 17:58:10 +02004247 enum nl80211_ps_state ps_state;
4248 struct wireless_dev *wdev;
Johannes Berg4c476992010-10-04 21:36:35 +02004249 struct net_device *dev = info->user_ptr[1];
Kalle Valoffb9eb32010-02-17 17:58:10 +02004250 struct sk_buff *msg;
4251 void *hdr;
4252 int err;
4253
Kalle Valoffb9eb32010-02-17 17:58:10 +02004254 wdev = dev->ieee80211_ptr;
4255
Johannes Berg4c476992010-10-04 21:36:35 +02004256 if (!rdev->ops->set_power_mgmt)
4257 return -EOPNOTSUPP;
Kalle Valoffb9eb32010-02-17 17:58:10 +02004258
4259 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg4c476992010-10-04 21:36:35 +02004260 if (!msg)
4261 return -ENOMEM;
Kalle Valoffb9eb32010-02-17 17:58:10 +02004262
4263 hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
4264 NL80211_CMD_GET_POWER_SAVE);
4265 if (!hdr) {
Johannes Berg4c476992010-10-04 21:36:35 +02004266 err = -ENOBUFS;
Kalle Valoffb9eb32010-02-17 17:58:10 +02004267 goto free_msg;
4268 }
4269
4270 if (wdev->ps)
4271 ps_state = NL80211_PS_ENABLED;
4272 else
4273 ps_state = NL80211_PS_DISABLED;
4274
4275 NLA_PUT_U32(msg, NL80211_ATTR_PS_STATE, ps_state);
4276
4277 genlmsg_end(msg, hdr);
Johannes Berg4c476992010-10-04 21:36:35 +02004278 return genlmsg_reply(msg, info);
Kalle Valoffb9eb32010-02-17 17:58:10 +02004279
Johannes Berg4c476992010-10-04 21:36:35 +02004280 nla_put_failure:
Kalle Valoffb9eb32010-02-17 17:58:10 +02004281 err = -ENOBUFS;
Johannes Berg4c476992010-10-04 21:36:35 +02004282 free_msg:
Kalle Valoffb9eb32010-02-17 17:58:10 +02004283 nlmsg_free(msg);
Kalle Valoffb9eb32010-02-17 17:58:10 +02004284 return err;
4285}
4286
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02004287static struct nla_policy
4288nl80211_attr_cqm_policy[NL80211_ATTR_CQM_MAX + 1] __read_mostly = {
4289 [NL80211_ATTR_CQM_RSSI_THOLD] = { .type = NLA_U32 },
4290 [NL80211_ATTR_CQM_RSSI_HYST] = { .type = NLA_U32 },
4291 [NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] = { .type = NLA_U32 },
4292};
4293
4294static int nl80211_set_cqm_rssi(struct genl_info *info,
4295 s32 threshold, u32 hysteresis)
4296{
Johannes Berg4c476992010-10-04 21:36:35 +02004297 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02004298 struct wireless_dev *wdev;
Johannes Berg4c476992010-10-04 21:36:35 +02004299 struct net_device *dev = info->user_ptr[1];
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02004300
4301 if (threshold > 0)
4302 return -EINVAL;
4303
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02004304 wdev = dev->ieee80211_ptr;
4305
Johannes Berg4c476992010-10-04 21:36:35 +02004306 if (!rdev->ops->set_cqm_rssi_config)
4307 return -EOPNOTSUPP;
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02004308
Johannes Berg074ac8d2010-09-16 14:58:22 +02004309 if (wdev->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg4c476992010-10-04 21:36:35 +02004310 wdev->iftype != NL80211_IFTYPE_P2P_CLIENT)
4311 return -EOPNOTSUPP;
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02004312
Johannes Berg4c476992010-10-04 21:36:35 +02004313 return rdev->ops->set_cqm_rssi_config(wdev->wiphy, dev,
4314 threshold, hysteresis);
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02004315}
4316
4317static int nl80211_set_cqm(struct sk_buff *skb, struct genl_info *info)
4318{
4319 struct nlattr *attrs[NL80211_ATTR_CQM_MAX + 1];
4320 struct nlattr *cqm;
4321 int err;
4322
4323 cqm = info->attrs[NL80211_ATTR_CQM];
4324 if (!cqm) {
4325 err = -EINVAL;
4326 goto out;
4327 }
4328
4329 err = nla_parse_nested(attrs, NL80211_ATTR_CQM_MAX, cqm,
4330 nl80211_attr_cqm_policy);
4331 if (err)
4332 goto out;
4333
4334 if (attrs[NL80211_ATTR_CQM_RSSI_THOLD] &&
4335 attrs[NL80211_ATTR_CQM_RSSI_HYST]) {
4336 s32 threshold;
4337 u32 hysteresis;
4338 threshold = nla_get_u32(attrs[NL80211_ATTR_CQM_RSSI_THOLD]);
4339 hysteresis = nla_get_u32(attrs[NL80211_ATTR_CQM_RSSI_HYST]);
4340 err = nl80211_set_cqm_rssi(info, threshold, hysteresis);
4341 } else
4342 err = -EINVAL;
4343
4344out:
4345 return err;
4346}
4347
Johannes Berg4c476992010-10-04 21:36:35 +02004348#define NL80211_FLAG_NEED_WIPHY 0x01
4349#define NL80211_FLAG_NEED_NETDEV 0x02
4350#define NL80211_FLAG_NEED_RTNL 0x04
Johannes Berg41265712010-10-04 21:14:05 +02004351#define NL80211_FLAG_CHECK_NETDEV_UP 0x08
4352#define NL80211_FLAG_NEED_NETDEV_UP (NL80211_FLAG_NEED_NETDEV |\
4353 NL80211_FLAG_CHECK_NETDEV_UP)
Johannes Berg4c476992010-10-04 21:36:35 +02004354
4355static int nl80211_pre_doit(struct genl_ops *ops, struct sk_buff *skb,
4356 struct genl_info *info)
4357{
4358 struct cfg80211_registered_device *rdev;
4359 struct net_device *dev;
4360 int err;
4361 bool rtnl = ops->internal_flags & NL80211_FLAG_NEED_RTNL;
4362
4363 if (rtnl)
4364 rtnl_lock();
4365
4366 if (ops->internal_flags & NL80211_FLAG_NEED_WIPHY) {
4367 rdev = cfg80211_get_dev_from_info(info);
4368 if (IS_ERR(rdev)) {
4369 if (rtnl)
4370 rtnl_unlock();
4371 return PTR_ERR(rdev);
4372 }
4373 info->user_ptr[0] = rdev;
4374 } else if (ops->internal_flags & NL80211_FLAG_NEED_NETDEV) {
4375 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
4376 if (err) {
4377 if (rtnl)
4378 rtnl_unlock();
4379 return err;
4380 }
Johannes Berg41265712010-10-04 21:14:05 +02004381 if (ops->internal_flags & NL80211_FLAG_CHECK_NETDEV_UP &&
4382 !netif_running(dev)) {
4383 if (rtnl)
4384 rtnl_unlock();
4385 return -ENETDOWN;
4386 }
Johannes Berg4c476992010-10-04 21:36:35 +02004387 info->user_ptr[0] = rdev;
4388 info->user_ptr[1] = dev;
4389 }
4390
4391 return 0;
4392}
4393
4394static void nl80211_post_doit(struct genl_ops *ops, struct sk_buff *skb,
4395 struct genl_info *info)
4396{
4397 if (info->user_ptr[0])
4398 cfg80211_unlock_rdev(info->user_ptr[0]);
4399 if (info->user_ptr[1])
4400 dev_put(info->user_ptr[1]);
4401 if (ops->internal_flags & NL80211_FLAG_NEED_RTNL)
4402 rtnl_unlock();
4403}
4404
Johannes Berg55682962007-09-20 13:09:35 -04004405static struct genl_ops nl80211_ops[] = {
4406 {
4407 .cmd = NL80211_CMD_GET_WIPHY,
4408 .doit = nl80211_get_wiphy,
4409 .dumpit = nl80211_dump_wiphy,
4410 .policy = nl80211_policy,
4411 /* can be retrieved by unprivileged users */
Johannes Berg4c476992010-10-04 21:36:35 +02004412 .internal_flags = NL80211_FLAG_NEED_WIPHY,
Johannes Berg55682962007-09-20 13:09:35 -04004413 },
4414 {
4415 .cmd = NL80211_CMD_SET_WIPHY,
4416 .doit = nl80211_set_wiphy,
4417 .policy = nl80211_policy,
4418 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02004419 .internal_flags = NL80211_FLAG_NEED_RTNL,
Johannes Berg55682962007-09-20 13:09:35 -04004420 },
4421 {
4422 .cmd = NL80211_CMD_GET_INTERFACE,
4423 .doit = nl80211_get_interface,
4424 .dumpit = nl80211_dump_interface,
4425 .policy = nl80211_policy,
4426 /* can be retrieved by unprivileged users */
Johannes Berg4c476992010-10-04 21:36:35 +02004427 .internal_flags = NL80211_FLAG_NEED_NETDEV,
Johannes Berg55682962007-09-20 13:09:35 -04004428 },
4429 {
4430 .cmd = NL80211_CMD_SET_INTERFACE,
4431 .doit = nl80211_set_interface,
4432 .policy = nl80211_policy,
4433 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02004434 .internal_flags = NL80211_FLAG_NEED_NETDEV |
4435 NL80211_FLAG_NEED_RTNL,
Johannes Berg55682962007-09-20 13:09:35 -04004436 },
4437 {
4438 .cmd = NL80211_CMD_NEW_INTERFACE,
4439 .doit = nl80211_new_interface,
4440 .policy = nl80211_policy,
4441 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02004442 .internal_flags = NL80211_FLAG_NEED_WIPHY |
4443 NL80211_FLAG_NEED_RTNL,
Johannes Berg55682962007-09-20 13:09:35 -04004444 },
4445 {
4446 .cmd = NL80211_CMD_DEL_INTERFACE,
4447 .doit = nl80211_del_interface,
4448 .policy = nl80211_policy,
4449 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02004450 .internal_flags = NL80211_FLAG_NEED_NETDEV |
4451 NL80211_FLAG_NEED_RTNL,
Johannes Berg55682962007-09-20 13:09:35 -04004452 },
Johannes Berg41ade002007-12-19 02:03:29 +01004453 {
4454 .cmd = NL80211_CMD_GET_KEY,
4455 .doit = nl80211_get_key,
4456 .policy = nl80211_policy,
4457 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02004458 .internal_flags = NL80211_FLAG_NEED_NETDEV |
4459 NL80211_FLAG_NEED_RTNL,
Johannes Berg41ade002007-12-19 02:03:29 +01004460 },
4461 {
4462 .cmd = NL80211_CMD_SET_KEY,
4463 .doit = nl80211_set_key,
4464 .policy = nl80211_policy,
4465 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02004466 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02004467 NL80211_FLAG_NEED_RTNL,
Johannes Berg41ade002007-12-19 02:03:29 +01004468 },
4469 {
4470 .cmd = NL80211_CMD_NEW_KEY,
4471 .doit = nl80211_new_key,
4472 .policy = nl80211_policy,
4473 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02004474 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02004475 NL80211_FLAG_NEED_RTNL,
Johannes Berg41ade002007-12-19 02:03:29 +01004476 },
4477 {
4478 .cmd = NL80211_CMD_DEL_KEY,
4479 .doit = nl80211_del_key,
4480 .policy = nl80211_policy,
4481 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02004482 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02004483 NL80211_FLAG_NEED_RTNL,
Johannes Berg41ade002007-12-19 02:03:29 +01004484 },
Johannes Berged1b6cc2007-12-19 02:03:32 +01004485 {
4486 .cmd = NL80211_CMD_SET_BEACON,
4487 .policy = nl80211_policy,
4488 .flags = GENL_ADMIN_PERM,
4489 .doit = nl80211_addset_beacon,
Johannes Berg4c476992010-10-04 21:36:35 +02004490 .internal_flags = NL80211_FLAG_NEED_NETDEV |
4491 NL80211_FLAG_NEED_RTNL,
Johannes Berged1b6cc2007-12-19 02:03:32 +01004492 },
4493 {
4494 .cmd = NL80211_CMD_NEW_BEACON,
4495 .policy = nl80211_policy,
4496 .flags = GENL_ADMIN_PERM,
4497 .doit = nl80211_addset_beacon,
Johannes Berg4c476992010-10-04 21:36:35 +02004498 .internal_flags = NL80211_FLAG_NEED_NETDEV |
4499 NL80211_FLAG_NEED_RTNL,
Johannes Berged1b6cc2007-12-19 02:03:32 +01004500 },
4501 {
4502 .cmd = NL80211_CMD_DEL_BEACON,
4503 .policy = nl80211_policy,
4504 .flags = GENL_ADMIN_PERM,
4505 .doit = nl80211_del_beacon,
Johannes Berg4c476992010-10-04 21:36:35 +02004506 .internal_flags = NL80211_FLAG_NEED_NETDEV |
4507 NL80211_FLAG_NEED_RTNL,
Johannes Berged1b6cc2007-12-19 02:03:32 +01004508 },
Johannes Berg5727ef12007-12-19 02:03:34 +01004509 {
4510 .cmd = NL80211_CMD_GET_STATION,
4511 .doit = nl80211_get_station,
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01004512 .dumpit = nl80211_dump_station,
Johannes Berg5727ef12007-12-19 02:03:34 +01004513 .policy = nl80211_policy,
Johannes Berg4c476992010-10-04 21:36:35 +02004514 .internal_flags = NL80211_FLAG_NEED_NETDEV |
4515 NL80211_FLAG_NEED_RTNL,
Johannes Berg5727ef12007-12-19 02:03:34 +01004516 },
4517 {
4518 .cmd = NL80211_CMD_SET_STATION,
4519 .doit = nl80211_set_station,
4520 .policy = nl80211_policy,
4521 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02004522 .internal_flags = NL80211_FLAG_NEED_NETDEV |
4523 NL80211_FLAG_NEED_RTNL,
Johannes Berg5727ef12007-12-19 02:03:34 +01004524 },
4525 {
4526 .cmd = NL80211_CMD_NEW_STATION,
4527 .doit = nl80211_new_station,
4528 .policy = nl80211_policy,
4529 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02004530 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02004531 NL80211_FLAG_NEED_RTNL,
Johannes Berg5727ef12007-12-19 02:03:34 +01004532 },
4533 {
4534 .cmd = NL80211_CMD_DEL_STATION,
4535 .doit = nl80211_del_station,
4536 .policy = nl80211_policy,
4537 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02004538 .internal_flags = NL80211_FLAG_NEED_NETDEV |
4539 NL80211_FLAG_NEED_RTNL,
Johannes Berg5727ef12007-12-19 02:03:34 +01004540 },
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01004541 {
4542 .cmd = NL80211_CMD_GET_MPATH,
4543 .doit = nl80211_get_mpath,
4544 .dumpit = nl80211_dump_mpath,
4545 .policy = nl80211_policy,
4546 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02004547 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02004548 NL80211_FLAG_NEED_RTNL,
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01004549 },
4550 {
4551 .cmd = NL80211_CMD_SET_MPATH,
4552 .doit = nl80211_set_mpath,
4553 .policy = nl80211_policy,
4554 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02004555 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02004556 NL80211_FLAG_NEED_RTNL,
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01004557 },
4558 {
4559 .cmd = NL80211_CMD_NEW_MPATH,
4560 .doit = nl80211_new_mpath,
4561 .policy = nl80211_policy,
4562 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02004563 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02004564 NL80211_FLAG_NEED_RTNL,
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01004565 },
4566 {
4567 .cmd = NL80211_CMD_DEL_MPATH,
4568 .doit = nl80211_del_mpath,
4569 .policy = nl80211_policy,
4570 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02004571 .internal_flags = NL80211_FLAG_NEED_NETDEV |
4572 NL80211_FLAG_NEED_RTNL,
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01004573 },
Jouni Malinen9f1ba902008-08-07 20:07:01 +03004574 {
4575 .cmd = NL80211_CMD_SET_BSS,
4576 .doit = nl80211_set_bss,
4577 .policy = nl80211_policy,
4578 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02004579 .internal_flags = NL80211_FLAG_NEED_NETDEV |
4580 NL80211_FLAG_NEED_RTNL,
Jouni Malinen9f1ba902008-08-07 20:07:01 +03004581 },
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07004582 {
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08004583 .cmd = NL80211_CMD_GET_REG,
4584 .doit = nl80211_get_reg,
4585 .policy = nl80211_policy,
4586 /* can be retrieved by unprivileged users */
4587 },
4588 {
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07004589 .cmd = NL80211_CMD_SET_REG,
4590 .doit = nl80211_set_reg,
4591 .policy = nl80211_policy,
4592 .flags = GENL_ADMIN_PERM,
4593 },
4594 {
4595 .cmd = NL80211_CMD_REQ_SET_REG,
4596 .doit = nl80211_req_set_reg,
4597 .policy = nl80211_policy,
4598 .flags = GENL_ADMIN_PERM,
4599 },
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07004600 {
4601 .cmd = NL80211_CMD_GET_MESH_PARAMS,
4602 .doit = nl80211_get_mesh_params,
4603 .policy = nl80211_policy,
4604 /* can be retrieved by unprivileged users */
Johannes Berg4c476992010-10-04 21:36:35 +02004605 .internal_flags = NL80211_FLAG_NEED_NETDEV |
4606 NL80211_FLAG_NEED_RTNL,
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07004607 },
4608 {
4609 .cmd = NL80211_CMD_SET_MESH_PARAMS,
4610 .doit = nl80211_set_mesh_params,
4611 .policy = nl80211_policy,
4612 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02004613 .internal_flags = NL80211_FLAG_NEED_NETDEV |
4614 NL80211_FLAG_NEED_RTNL,
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07004615 },
Jouni Malinen9aed3cc2009-01-13 16:03:29 +02004616 {
Johannes Berg2a519312009-02-10 21:25:55 +01004617 .cmd = NL80211_CMD_TRIGGER_SCAN,
4618 .doit = nl80211_trigger_scan,
4619 .policy = nl80211_policy,
4620 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02004621 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02004622 NL80211_FLAG_NEED_RTNL,
Johannes Berg2a519312009-02-10 21:25:55 +01004623 },
4624 {
4625 .cmd = NL80211_CMD_GET_SCAN,
4626 .policy = nl80211_policy,
4627 .dumpit = nl80211_dump_scan,
4628 },
Jouni Malinen636a5d32009-03-19 13:39:22 +02004629 {
4630 .cmd = NL80211_CMD_AUTHENTICATE,
4631 .doit = nl80211_authenticate,
4632 .policy = nl80211_policy,
4633 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02004634 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02004635 NL80211_FLAG_NEED_RTNL,
Jouni Malinen636a5d32009-03-19 13:39:22 +02004636 },
4637 {
4638 .cmd = NL80211_CMD_ASSOCIATE,
4639 .doit = nl80211_associate,
4640 .policy = nl80211_policy,
4641 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02004642 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02004643 NL80211_FLAG_NEED_RTNL,
Jouni Malinen636a5d32009-03-19 13:39:22 +02004644 },
4645 {
4646 .cmd = NL80211_CMD_DEAUTHENTICATE,
4647 .doit = nl80211_deauthenticate,
4648 .policy = nl80211_policy,
4649 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02004650 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02004651 NL80211_FLAG_NEED_RTNL,
Jouni Malinen636a5d32009-03-19 13:39:22 +02004652 },
4653 {
4654 .cmd = NL80211_CMD_DISASSOCIATE,
4655 .doit = nl80211_disassociate,
4656 .policy = nl80211_policy,
4657 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02004658 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02004659 NL80211_FLAG_NEED_RTNL,
Jouni Malinen636a5d32009-03-19 13:39:22 +02004660 },
Johannes Berg04a773a2009-04-19 21:24:32 +02004661 {
4662 .cmd = NL80211_CMD_JOIN_IBSS,
4663 .doit = nl80211_join_ibss,
4664 .policy = nl80211_policy,
4665 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02004666 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02004667 NL80211_FLAG_NEED_RTNL,
Johannes Berg04a773a2009-04-19 21:24:32 +02004668 },
4669 {
4670 .cmd = NL80211_CMD_LEAVE_IBSS,
4671 .doit = nl80211_leave_ibss,
4672 .policy = nl80211_policy,
4673 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02004674 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02004675 NL80211_FLAG_NEED_RTNL,
Johannes Berg04a773a2009-04-19 21:24:32 +02004676 },
Johannes Bergaff89a92009-07-01 21:26:51 +02004677#ifdef CONFIG_NL80211_TESTMODE
4678 {
4679 .cmd = NL80211_CMD_TESTMODE,
4680 .doit = nl80211_testmode_do,
4681 .policy = nl80211_policy,
4682 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02004683 .internal_flags = NL80211_FLAG_NEED_WIPHY |
4684 NL80211_FLAG_NEED_RTNL,
Johannes Bergaff89a92009-07-01 21:26:51 +02004685 },
4686#endif
Samuel Ortizb23aa672009-07-01 21:26:54 +02004687 {
4688 .cmd = NL80211_CMD_CONNECT,
4689 .doit = nl80211_connect,
4690 .policy = nl80211_policy,
4691 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02004692 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02004693 NL80211_FLAG_NEED_RTNL,
Samuel Ortizb23aa672009-07-01 21:26:54 +02004694 },
4695 {
4696 .cmd = NL80211_CMD_DISCONNECT,
4697 .doit = nl80211_disconnect,
4698 .policy = nl80211_policy,
4699 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02004700 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02004701 NL80211_FLAG_NEED_RTNL,
Samuel Ortizb23aa672009-07-01 21:26:54 +02004702 },
Johannes Berg463d0182009-07-14 00:33:35 +02004703 {
4704 .cmd = NL80211_CMD_SET_WIPHY_NETNS,
4705 .doit = nl80211_wiphy_netns,
4706 .policy = nl80211_policy,
4707 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02004708 .internal_flags = NL80211_FLAG_NEED_WIPHY |
4709 NL80211_FLAG_NEED_RTNL,
Johannes Berg463d0182009-07-14 00:33:35 +02004710 },
Holger Schurig61fa7132009-11-11 12:25:40 +01004711 {
4712 .cmd = NL80211_CMD_GET_SURVEY,
4713 .policy = nl80211_policy,
4714 .dumpit = nl80211_dump_survey,
4715 },
Samuel Ortiz67fbb162009-11-24 23:59:15 +01004716 {
4717 .cmd = NL80211_CMD_SET_PMKSA,
4718 .doit = nl80211_setdel_pmksa,
4719 .policy = nl80211_policy,
4720 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02004721 .internal_flags = NL80211_FLAG_NEED_NETDEV |
4722 NL80211_FLAG_NEED_RTNL,
Samuel Ortiz67fbb162009-11-24 23:59:15 +01004723 },
4724 {
4725 .cmd = NL80211_CMD_DEL_PMKSA,
4726 .doit = nl80211_setdel_pmksa,
4727 .policy = nl80211_policy,
4728 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02004729 .internal_flags = NL80211_FLAG_NEED_NETDEV |
4730 NL80211_FLAG_NEED_RTNL,
Samuel Ortiz67fbb162009-11-24 23:59:15 +01004731 },
4732 {
4733 .cmd = NL80211_CMD_FLUSH_PMKSA,
4734 .doit = nl80211_flush_pmksa,
4735 .policy = nl80211_policy,
4736 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02004737 .internal_flags = NL80211_FLAG_NEED_NETDEV |
4738 NL80211_FLAG_NEED_RTNL,
Samuel Ortiz67fbb162009-11-24 23:59:15 +01004739 },
Jouni Malinen9588bbd2009-12-23 13:15:41 +01004740 {
4741 .cmd = NL80211_CMD_REMAIN_ON_CHANNEL,
4742 .doit = nl80211_remain_on_channel,
4743 .policy = nl80211_policy,
4744 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02004745 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02004746 NL80211_FLAG_NEED_RTNL,
Jouni Malinen9588bbd2009-12-23 13:15:41 +01004747 },
4748 {
4749 .cmd = NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL,
4750 .doit = nl80211_cancel_remain_on_channel,
4751 .policy = nl80211_policy,
4752 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02004753 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02004754 NL80211_FLAG_NEED_RTNL,
Jouni Malinen9588bbd2009-12-23 13:15:41 +01004755 },
Jouni Malinen13ae75b2009-12-29 12:59:45 +02004756 {
4757 .cmd = NL80211_CMD_SET_TX_BITRATE_MASK,
4758 .doit = nl80211_set_tx_bitrate_mask,
4759 .policy = nl80211_policy,
4760 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02004761 .internal_flags = NL80211_FLAG_NEED_NETDEV |
4762 NL80211_FLAG_NEED_RTNL,
Jouni Malinen13ae75b2009-12-29 12:59:45 +02004763 },
Jouni Malinen026331c2010-02-15 12:53:10 +02004764 {
Johannes Berg2e161f72010-08-12 15:38:38 +02004765 .cmd = NL80211_CMD_REGISTER_FRAME,
4766 .doit = nl80211_register_mgmt,
Jouni Malinen026331c2010-02-15 12:53:10 +02004767 .policy = nl80211_policy,
4768 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02004769 .internal_flags = NL80211_FLAG_NEED_NETDEV |
4770 NL80211_FLAG_NEED_RTNL,
Jouni Malinen026331c2010-02-15 12:53:10 +02004771 },
4772 {
Johannes Berg2e161f72010-08-12 15:38:38 +02004773 .cmd = NL80211_CMD_FRAME,
4774 .doit = nl80211_tx_mgmt,
Jouni Malinen026331c2010-02-15 12:53:10 +02004775 .policy = nl80211_policy,
4776 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02004777 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02004778 NL80211_FLAG_NEED_RTNL,
Jouni Malinen026331c2010-02-15 12:53:10 +02004779 },
Kalle Valoffb9eb32010-02-17 17:58:10 +02004780 {
4781 .cmd = NL80211_CMD_SET_POWER_SAVE,
4782 .doit = nl80211_set_power_save,
4783 .policy = nl80211_policy,
4784 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02004785 .internal_flags = NL80211_FLAG_NEED_NETDEV |
4786 NL80211_FLAG_NEED_RTNL,
Kalle Valoffb9eb32010-02-17 17:58:10 +02004787 },
4788 {
4789 .cmd = NL80211_CMD_GET_POWER_SAVE,
4790 .doit = nl80211_get_power_save,
4791 .policy = nl80211_policy,
4792 /* can be retrieved by unprivileged users */
Johannes Berg4c476992010-10-04 21:36:35 +02004793 .internal_flags = NL80211_FLAG_NEED_NETDEV |
4794 NL80211_FLAG_NEED_RTNL,
Kalle Valoffb9eb32010-02-17 17:58:10 +02004795 },
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02004796 {
4797 .cmd = NL80211_CMD_SET_CQM,
4798 .doit = nl80211_set_cqm,
4799 .policy = nl80211_policy,
4800 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02004801 .internal_flags = NL80211_FLAG_NEED_NETDEV |
4802 NL80211_FLAG_NEED_RTNL,
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02004803 },
Johannes Bergf444de02010-05-05 15:25:02 +02004804 {
4805 .cmd = NL80211_CMD_SET_CHANNEL,
4806 .doit = nl80211_set_channel,
4807 .policy = nl80211_policy,
4808 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02004809 .internal_flags = NL80211_FLAG_NEED_NETDEV |
4810 NL80211_FLAG_NEED_RTNL,
Johannes Bergf444de02010-05-05 15:25:02 +02004811 },
Bill Jordane8347eb2010-10-01 13:54:28 -04004812 {
4813 .cmd = NL80211_CMD_SET_WDS_PEER,
4814 .doit = nl80211_set_wds_peer,
4815 .policy = nl80211_policy,
4816 .flags = GENL_ADMIN_PERM,
4817 },
Johannes Berg55682962007-09-20 13:09:35 -04004818};
Jouni Malinen9588bbd2009-12-23 13:15:41 +01004819
Jouni Malinen6039f6d2009-03-19 13:39:21 +02004820static struct genl_multicast_group nl80211_mlme_mcgrp = {
4821 .name = "mlme",
4822};
Johannes Berg55682962007-09-20 13:09:35 -04004823
4824/* multicast groups */
4825static struct genl_multicast_group nl80211_config_mcgrp = {
4826 .name = "config",
4827};
Johannes Berg2a519312009-02-10 21:25:55 +01004828static struct genl_multicast_group nl80211_scan_mcgrp = {
4829 .name = "scan",
4830};
Luis R. Rodriguez73d54c92009-03-09 22:07:42 -04004831static struct genl_multicast_group nl80211_regulatory_mcgrp = {
4832 .name = "regulatory",
4833};
Johannes Berg55682962007-09-20 13:09:35 -04004834
4835/* notification functions */
4836
4837void nl80211_notify_dev_rename(struct cfg80211_registered_device *rdev)
4838{
4839 struct sk_buff *msg;
4840
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07004841 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg55682962007-09-20 13:09:35 -04004842 if (!msg)
4843 return;
4844
4845 if (nl80211_send_wiphy(msg, 0, 0, 0, rdev) < 0) {
4846 nlmsg_free(msg);
4847 return;
4848 }
4849
Johannes Berg463d0182009-07-14 00:33:35 +02004850 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
4851 nl80211_config_mcgrp.id, GFP_KERNEL);
Johannes Berg55682962007-09-20 13:09:35 -04004852}
4853
Johannes Berg362a4152009-05-24 16:43:15 +02004854static int nl80211_add_scan_req(struct sk_buff *msg,
4855 struct cfg80211_registered_device *rdev)
4856{
4857 struct cfg80211_scan_request *req = rdev->scan_req;
4858 struct nlattr *nest;
4859 int i;
4860
Johannes Berg667503d2009-07-07 03:56:11 +02004861 ASSERT_RDEV_LOCK(rdev);
4862
Johannes Berg362a4152009-05-24 16:43:15 +02004863 if (WARN_ON(!req))
4864 return 0;
4865
4866 nest = nla_nest_start(msg, NL80211_ATTR_SCAN_SSIDS);
4867 if (!nest)
4868 goto nla_put_failure;
4869 for (i = 0; i < req->n_ssids; i++)
4870 NLA_PUT(msg, i, req->ssids[i].ssid_len, req->ssids[i].ssid);
4871 nla_nest_end(msg, nest);
4872
4873 nest = nla_nest_start(msg, NL80211_ATTR_SCAN_FREQUENCIES);
4874 if (!nest)
4875 goto nla_put_failure;
4876 for (i = 0; i < req->n_channels; i++)
4877 NLA_PUT_U32(msg, i, req->channels[i]->center_freq);
4878 nla_nest_end(msg, nest);
4879
4880 if (req->ie)
4881 NLA_PUT(msg, NL80211_ATTR_IE, req->ie_len, req->ie);
4882
4883 return 0;
4884 nla_put_failure:
4885 return -ENOBUFS;
4886}
4887
Johannes Berga538e2d2009-06-16 19:56:42 +02004888static int nl80211_send_scan_msg(struct sk_buff *msg,
4889 struct cfg80211_registered_device *rdev,
4890 struct net_device *netdev,
4891 u32 pid, u32 seq, int flags,
4892 u32 cmd)
Johannes Berg2a519312009-02-10 21:25:55 +01004893{
4894 void *hdr;
4895
4896 hdr = nl80211hdr_put(msg, pid, seq, flags, cmd);
4897 if (!hdr)
4898 return -1;
4899
Luis R. Rodriguezb5850a72009-02-21 00:04:19 -05004900 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
Johannes Berg2a519312009-02-10 21:25:55 +01004901 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
4902
Johannes Berg362a4152009-05-24 16:43:15 +02004903 /* ignore errors and send incomplete event anyway */
4904 nl80211_add_scan_req(msg, rdev);
Johannes Berg2a519312009-02-10 21:25:55 +01004905
4906 return genlmsg_end(msg, hdr);
4907
4908 nla_put_failure:
4909 genlmsg_cancel(msg, hdr);
4910 return -EMSGSIZE;
4911}
4912
Johannes Berga538e2d2009-06-16 19:56:42 +02004913void nl80211_send_scan_start(struct cfg80211_registered_device *rdev,
4914 struct net_device *netdev)
4915{
4916 struct sk_buff *msg;
4917
4918 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
4919 if (!msg)
4920 return;
4921
4922 if (nl80211_send_scan_msg(msg, rdev, netdev, 0, 0, 0,
4923 NL80211_CMD_TRIGGER_SCAN) < 0) {
4924 nlmsg_free(msg);
4925 return;
4926 }
4927
Johannes Berg463d0182009-07-14 00:33:35 +02004928 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
4929 nl80211_scan_mcgrp.id, GFP_KERNEL);
Johannes Berga538e2d2009-06-16 19:56:42 +02004930}
4931
Johannes Berg2a519312009-02-10 21:25:55 +01004932void nl80211_send_scan_done(struct cfg80211_registered_device *rdev,
4933 struct net_device *netdev)
4934{
4935 struct sk_buff *msg;
4936
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07004937 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg2a519312009-02-10 21:25:55 +01004938 if (!msg)
4939 return;
4940
Johannes Berga538e2d2009-06-16 19:56:42 +02004941 if (nl80211_send_scan_msg(msg, rdev, netdev, 0, 0, 0,
4942 NL80211_CMD_NEW_SCAN_RESULTS) < 0) {
Johannes Berg2a519312009-02-10 21:25:55 +01004943 nlmsg_free(msg);
4944 return;
4945 }
4946
Johannes Berg463d0182009-07-14 00:33:35 +02004947 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
4948 nl80211_scan_mcgrp.id, GFP_KERNEL);
Johannes Berg2a519312009-02-10 21:25:55 +01004949}
4950
4951void nl80211_send_scan_aborted(struct cfg80211_registered_device *rdev,
4952 struct net_device *netdev)
4953{
4954 struct sk_buff *msg;
4955
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07004956 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg2a519312009-02-10 21:25:55 +01004957 if (!msg)
4958 return;
4959
Johannes Berga538e2d2009-06-16 19:56:42 +02004960 if (nl80211_send_scan_msg(msg, rdev, netdev, 0, 0, 0,
4961 NL80211_CMD_SCAN_ABORTED) < 0) {
Johannes Berg2a519312009-02-10 21:25:55 +01004962 nlmsg_free(msg);
4963 return;
4964 }
4965
Johannes Berg463d0182009-07-14 00:33:35 +02004966 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
4967 nl80211_scan_mcgrp.id, GFP_KERNEL);
Johannes Berg2a519312009-02-10 21:25:55 +01004968}
4969
Luis R. Rodriguez73d54c92009-03-09 22:07:42 -04004970/*
4971 * This can happen on global regulatory changes or device specific settings
4972 * based on custom world regulatory domains.
4973 */
4974void nl80211_send_reg_change_event(struct regulatory_request *request)
4975{
4976 struct sk_buff *msg;
4977 void *hdr;
4978
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07004979 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Luis R. Rodriguez73d54c92009-03-09 22:07:42 -04004980 if (!msg)
4981 return;
4982
4983 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_REG_CHANGE);
4984 if (!hdr) {
4985 nlmsg_free(msg);
4986 return;
4987 }
4988
4989 /* Userspace can always count this one always being set */
4990 NLA_PUT_U8(msg, NL80211_ATTR_REG_INITIATOR, request->initiator);
4991
4992 if (request->alpha2[0] == '0' && request->alpha2[1] == '0')
4993 NLA_PUT_U8(msg, NL80211_ATTR_REG_TYPE,
4994 NL80211_REGDOM_TYPE_WORLD);
4995 else if (request->alpha2[0] == '9' && request->alpha2[1] == '9')
4996 NLA_PUT_U8(msg, NL80211_ATTR_REG_TYPE,
4997 NL80211_REGDOM_TYPE_CUSTOM_WORLD);
4998 else if ((request->alpha2[0] == '9' && request->alpha2[1] == '8') ||
4999 request->intersect)
5000 NLA_PUT_U8(msg, NL80211_ATTR_REG_TYPE,
5001 NL80211_REGDOM_TYPE_INTERSECTION);
5002 else {
5003 NLA_PUT_U8(msg, NL80211_ATTR_REG_TYPE,
5004 NL80211_REGDOM_TYPE_COUNTRY);
5005 NLA_PUT_STRING(msg, NL80211_ATTR_REG_ALPHA2, request->alpha2);
5006 }
5007
5008 if (wiphy_idx_valid(request->wiphy_idx))
5009 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, request->wiphy_idx);
5010
5011 if (genlmsg_end(msg, hdr) < 0) {
5012 nlmsg_free(msg);
5013 return;
5014 }
5015
Johannes Bergbc43b282009-07-25 10:54:13 +02005016 rcu_read_lock();
Johannes Berg463d0182009-07-14 00:33:35 +02005017 genlmsg_multicast_allns(msg, 0, nl80211_regulatory_mcgrp.id,
Johannes Bergbc43b282009-07-25 10:54:13 +02005018 GFP_ATOMIC);
5019 rcu_read_unlock();
Luis R. Rodriguez73d54c92009-03-09 22:07:42 -04005020
5021 return;
5022
5023nla_put_failure:
5024 genlmsg_cancel(msg, hdr);
5025 nlmsg_free(msg);
5026}
5027
Jouni Malinen6039f6d2009-03-19 13:39:21 +02005028static void nl80211_send_mlme_event(struct cfg80211_registered_device *rdev,
5029 struct net_device *netdev,
5030 const u8 *buf, size_t len,
Johannes Berge6d6e342009-07-01 21:26:47 +02005031 enum nl80211_commands cmd, gfp_t gfp)
Jouni Malinen6039f6d2009-03-19 13:39:21 +02005032{
5033 struct sk_buff *msg;
5034 void *hdr;
5035
Johannes Berge6d6e342009-07-01 21:26:47 +02005036 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
Jouni Malinen6039f6d2009-03-19 13:39:21 +02005037 if (!msg)
5038 return;
5039
5040 hdr = nl80211hdr_put(msg, 0, 0, 0, cmd);
5041 if (!hdr) {
5042 nlmsg_free(msg);
5043 return;
5044 }
5045
5046 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
5047 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
5048 NLA_PUT(msg, NL80211_ATTR_FRAME, len, buf);
5049
5050 if (genlmsg_end(msg, hdr) < 0) {
5051 nlmsg_free(msg);
5052 return;
5053 }
5054
Johannes Berg463d0182009-07-14 00:33:35 +02005055 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
5056 nl80211_mlme_mcgrp.id, gfp);
Jouni Malinen6039f6d2009-03-19 13:39:21 +02005057 return;
5058
5059 nla_put_failure:
5060 genlmsg_cancel(msg, hdr);
5061 nlmsg_free(msg);
5062}
5063
5064void nl80211_send_rx_auth(struct cfg80211_registered_device *rdev,
Johannes Berge6d6e342009-07-01 21:26:47 +02005065 struct net_device *netdev, const u8 *buf,
5066 size_t len, gfp_t gfp)
Jouni Malinen6039f6d2009-03-19 13:39:21 +02005067{
5068 nl80211_send_mlme_event(rdev, netdev, buf, len,
Johannes Berge6d6e342009-07-01 21:26:47 +02005069 NL80211_CMD_AUTHENTICATE, gfp);
Jouni Malinen6039f6d2009-03-19 13:39:21 +02005070}
5071
5072void nl80211_send_rx_assoc(struct cfg80211_registered_device *rdev,
5073 struct net_device *netdev, const u8 *buf,
Johannes Berge6d6e342009-07-01 21:26:47 +02005074 size_t len, gfp_t gfp)
Jouni Malinen6039f6d2009-03-19 13:39:21 +02005075{
Johannes Berge6d6e342009-07-01 21:26:47 +02005076 nl80211_send_mlme_event(rdev, netdev, buf, len,
5077 NL80211_CMD_ASSOCIATE, gfp);
Jouni Malinen6039f6d2009-03-19 13:39:21 +02005078}
5079
Jouni Malinen53b46b82009-03-27 20:53:56 +02005080void nl80211_send_deauth(struct cfg80211_registered_device *rdev,
Johannes Berge6d6e342009-07-01 21:26:47 +02005081 struct net_device *netdev, const u8 *buf,
5082 size_t len, gfp_t gfp)
Jouni Malinen6039f6d2009-03-19 13:39:21 +02005083{
5084 nl80211_send_mlme_event(rdev, netdev, buf, len,
Johannes Berge6d6e342009-07-01 21:26:47 +02005085 NL80211_CMD_DEAUTHENTICATE, gfp);
Jouni Malinen6039f6d2009-03-19 13:39:21 +02005086}
5087
Jouni Malinen53b46b82009-03-27 20:53:56 +02005088void nl80211_send_disassoc(struct cfg80211_registered_device *rdev,
5089 struct net_device *netdev, const u8 *buf,
Johannes Berge6d6e342009-07-01 21:26:47 +02005090 size_t len, gfp_t gfp)
Jouni Malinen6039f6d2009-03-19 13:39:21 +02005091{
5092 nl80211_send_mlme_event(rdev, netdev, buf, len,
Johannes Berge6d6e342009-07-01 21:26:47 +02005093 NL80211_CMD_DISASSOCIATE, gfp);
Jouni Malinen6039f6d2009-03-19 13:39:21 +02005094}
5095
Luis R. Rodriguez1b06bb42009-05-02 00:34:48 -04005096static void nl80211_send_mlme_timeout(struct cfg80211_registered_device *rdev,
5097 struct net_device *netdev, int cmd,
Johannes Berge6d6e342009-07-01 21:26:47 +02005098 const u8 *addr, gfp_t gfp)
Jouni Malinen1965c852009-04-22 21:38:25 +03005099{
5100 struct sk_buff *msg;
5101 void *hdr;
5102
Johannes Berge6d6e342009-07-01 21:26:47 +02005103 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
Jouni Malinen1965c852009-04-22 21:38:25 +03005104 if (!msg)
5105 return;
5106
5107 hdr = nl80211hdr_put(msg, 0, 0, 0, cmd);
5108 if (!hdr) {
5109 nlmsg_free(msg);
5110 return;
5111 }
5112
5113 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
5114 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
5115 NLA_PUT_FLAG(msg, NL80211_ATTR_TIMED_OUT);
5116 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr);
5117
5118 if (genlmsg_end(msg, hdr) < 0) {
5119 nlmsg_free(msg);
5120 return;
5121 }
5122
Johannes Berg463d0182009-07-14 00:33:35 +02005123 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
5124 nl80211_mlme_mcgrp.id, gfp);
Jouni Malinen1965c852009-04-22 21:38:25 +03005125 return;
5126
5127 nla_put_failure:
5128 genlmsg_cancel(msg, hdr);
5129 nlmsg_free(msg);
5130}
5131
5132void nl80211_send_auth_timeout(struct cfg80211_registered_device *rdev,
Johannes Berge6d6e342009-07-01 21:26:47 +02005133 struct net_device *netdev, const u8 *addr,
5134 gfp_t gfp)
Jouni Malinen1965c852009-04-22 21:38:25 +03005135{
5136 nl80211_send_mlme_timeout(rdev, netdev, NL80211_CMD_AUTHENTICATE,
Johannes Berge6d6e342009-07-01 21:26:47 +02005137 addr, gfp);
Jouni Malinen1965c852009-04-22 21:38:25 +03005138}
5139
5140void nl80211_send_assoc_timeout(struct cfg80211_registered_device *rdev,
Johannes Berge6d6e342009-07-01 21:26:47 +02005141 struct net_device *netdev, const u8 *addr,
5142 gfp_t gfp)
Jouni Malinen1965c852009-04-22 21:38:25 +03005143{
Johannes Berge6d6e342009-07-01 21:26:47 +02005144 nl80211_send_mlme_timeout(rdev, netdev, NL80211_CMD_ASSOCIATE,
5145 addr, gfp);
Jouni Malinen1965c852009-04-22 21:38:25 +03005146}
5147
Samuel Ortizb23aa672009-07-01 21:26:54 +02005148void nl80211_send_connect_result(struct cfg80211_registered_device *rdev,
5149 struct net_device *netdev, const u8 *bssid,
5150 const u8 *req_ie, size_t req_ie_len,
5151 const u8 *resp_ie, size_t resp_ie_len,
5152 u16 status, gfp_t gfp)
5153{
5154 struct sk_buff *msg;
5155 void *hdr;
5156
5157 msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
5158 if (!msg)
5159 return;
5160
5161 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_CONNECT);
5162 if (!hdr) {
5163 nlmsg_free(msg);
5164 return;
5165 }
5166
5167 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
5168 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
5169 if (bssid)
5170 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid);
5171 NLA_PUT_U16(msg, NL80211_ATTR_STATUS_CODE, status);
5172 if (req_ie)
5173 NLA_PUT(msg, NL80211_ATTR_REQ_IE, req_ie_len, req_ie);
5174 if (resp_ie)
5175 NLA_PUT(msg, NL80211_ATTR_RESP_IE, resp_ie_len, resp_ie);
5176
5177 if (genlmsg_end(msg, hdr) < 0) {
5178 nlmsg_free(msg);
5179 return;
5180 }
5181
Johannes Berg463d0182009-07-14 00:33:35 +02005182 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
5183 nl80211_mlme_mcgrp.id, gfp);
Samuel Ortizb23aa672009-07-01 21:26:54 +02005184 return;
5185
5186 nla_put_failure:
5187 genlmsg_cancel(msg, hdr);
5188 nlmsg_free(msg);
5189
5190}
5191
5192void nl80211_send_roamed(struct cfg80211_registered_device *rdev,
5193 struct net_device *netdev, const u8 *bssid,
5194 const u8 *req_ie, size_t req_ie_len,
5195 const u8 *resp_ie, size_t resp_ie_len, gfp_t gfp)
5196{
5197 struct sk_buff *msg;
5198 void *hdr;
5199
5200 msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
5201 if (!msg)
5202 return;
5203
5204 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_ROAM);
5205 if (!hdr) {
5206 nlmsg_free(msg);
5207 return;
5208 }
5209
5210 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
5211 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
5212 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid);
5213 if (req_ie)
5214 NLA_PUT(msg, NL80211_ATTR_REQ_IE, req_ie_len, req_ie);
5215 if (resp_ie)
5216 NLA_PUT(msg, NL80211_ATTR_RESP_IE, resp_ie_len, resp_ie);
5217
5218 if (genlmsg_end(msg, hdr) < 0) {
5219 nlmsg_free(msg);
5220 return;
5221 }
5222
Johannes Berg463d0182009-07-14 00:33:35 +02005223 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
5224 nl80211_mlme_mcgrp.id, gfp);
Samuel Ortizb23aa672009-07-01 21:26:54 +02005225 return;
5226
5227 nla_put_failure:
5228 genlmsg_cancel(msg, hdr);
5229 nlmsg_free(msg);
5230
5231}
5232
5233void nl80211_send_disconnected(struct cfg80211_registered_device *rdev,
5234 struct net_device *netdev, u16 reason,
Johannes Berg667503d2009-07-07 03:56:11 +02005235 const u8 *ie, size_t ie_len, bool from_ap)
Samuel Ortizb23aa672009-07-01 21:26:54 +02005236{
5237 struct sk_buff *msg;
5238 void *hdr;
5239
Johannes Berg667503d2009-07-07 03:56:11 +02005240 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
Samuel Ortizb23aa672009-07-01 21:26:54 +02005241 if (!msg)
5242 return;
5243
5244 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_DISCONNECT);
5245 if (!hdr) {
5246 nlmsg_free(msg);
5247 return;
5248 }
5249
5250 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
5251 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
5252 if (from_ap && reason)
5253 NLA_PUT_U16(msg, NL80211_ATTR_REASON_CODE, reason);
5254 if (from_ap)
5255 NLA_PUT_FLAG(msg, NL80211_ATTR_DISCONNECTED_BY_AP);
5256 if (ie)
5257 NLA_PUT(msg, NL80211_ATTR_IE, ie_len, ie);
5258
5259 if (genlmsg_end(msg, hdr) < 0) {
5260 nlmsg_free(msg);
5261 return;
5262 }
5263
Johannes Berg463d0182009-07-14 00:33:35 +02005264 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
5265 nl80211_mlme_mcgrp.id, GFP_KERNEL);
Samuel Ortizb23aa672009-07-01 21:26:54 +02005266 return;
5267
5268 nla_put_failure:
5269 genlmsg_cancel(msg, hdr);
5270 nlmsg_free(msg);
5271
5272}
5273
Johannes Berg04a773a2009-04-19 21:24:32 +02005274void nl80211_send_ibss_bssid(struct cfg80211_registered_device *rdev,
5275 struct net_device *netdev, const u8 *bssid,
5276 gfp_t gfp)
5277{
5278 struct sk_buff *msg;
5279 void *hdr;
5280
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07005281 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
Johannes Berg04a773a2009-04-19 21:24:32 +02005282 if (!msg)
5283 return;
5284
5285 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_JOIN_IBSS);
5286 if (!hdr) {
5287 nlmsg_free(msg);
5288 return;
5289 }
5290
5291 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
5292 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
5293 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid);
5294
5295 if (genlmsg_end(msg, hdr) < 0) {
5296 nlmsg_free(msg);
5297 return;
5298 }
5299
Johannes Berg463d0182009-07-14 00:33:35 +02005300 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
5301 nl80211_mlme_mcgrp.id, gfp);
Johannes Berg04a773a2009-04-19 21:24:32 +02005302 return;
5303
5304 nla_put_failure:
5305 genlmsg_cancel(msg, hdr);
5306 nlmsg_free(msg);
5307}
5308
Jouni Malinena3b8b052009-03-27 21:59:49 +02005309void nl80211_michael_mic_failure(struct cfg80211_registered_device *rdev,
5310 struct net_device *netdev, const u8 *addr,
5311 enum nl80211_key_type key_type, int key_id,
Johannes Berge6d6e342009-07-01 21:26:47 +02005312 const u8 *tsc, gfp_t gfp)
Jouni Malinena3b8b052009-03-27 21:59:49 +02005313{
5314 struct sk_buff *msg;
5315 void *hdr;
5316
Johannes Berge6d6e342009-07-01 21:26:47 +02005317 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
Jouni Malinena3b8b052009-03-27 21:59:49 +02005318 if (!msg)
5319 return;
5320
5321 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_MICHAEL_MIC_FAILURE);
5322 if (!hdr) {
5323 nlmsg_free(msg);
5324 return;
5325 }
5326
5327 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
5328 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
5329 if (addr)
5330 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr);
5331 NLA_PUT_U32(msg, NL80211_ATTR_KEY_TYPE, key_type);
5332 NLA_PUT_U8(msg, NL80211_ATTR_KEY_IDX, key_id);
5333 if (tsc)
5334 NLA_PUT(msg, NL80211_ATTR_KEY_SEQ, 6, tsc);
5335
5336 if (genlmsg_end(msg, hdr) < 0) {
5337 nlmsg_free(msg);
5338 return;
5339 }
5340
Johannes Berg463d0182009-07-14 00:33:35 +02005341 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
5342 nl80211_mlme_mcgrp.id, gfp);
Jouni Malinena3b8b052009-03-27 21:59:49 +02005343 return;
5344
5345 nla_put_failure:
5346 genlmsg_cancel(msg, hdr);
5347 nlmsg_free(msg);
5348}
5349
Luis R. Rodriguez6bad8762009-04-02 14:08:09 -04005350void nl80211_send_beacon_hint_event(struct wiphy *wiphy,
5351 struct ieee80211_channel *channel_before,
5352 struct ieee80211_channel *channel_after)
5353{
5354 struct sk_buff *msg;
5355 void *hdr;
5356 struct nlattr *nl_freq;
5357
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07005358 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC);
Luis R. Rodriguez6bad8762009-04-02 14:08:09 -04005359 if (!msg)
5360 return;
5361
5362 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_REG_BEACON_HINT);
5363 if (!hdr) {
5364 nlmsg_free(msg);
5365 return;
5366 }
5367
5368 /*
5369 * Since we are applying the beacon hint to a wiphy we know its
5370 * wiphy_idx is valid
5371 */
5372 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, get_wiphy_idx(wiphy));
5373
5374 /* Before */
5375 nl_freq = nla_nest_start(msg, NL80211_ATTR_FREQ_BEFORE);
5376 if (!nl_freq)
5377 goto nla_put_failure;
5378 if (nl80211_msg_put_channel(msg, channel_before))
5379 goto nla_put_failure;
5380 nla_nest_end(msg, nl_freq);
5381
5382 /* After */
5383 nl_freq = nla_nest_start(msg, NL80211_ATTR_FREQ_AFTER);
5384 if (!nl_freq)
5385 goto nla_put_failure;
5386 if (nl80211_msg_put_channel(msg, channel_after))
5387 goto nla_put_failure;
5388 nla_nest_end(msg, nl_freq);
5389
5390 if (genlmsg_end(msg, hdr) < 0) {
5391 nlmsg_free(msg);
5392 return;
5393 }
5394
Johannes Berg463d0182009-07-14 00:33:35 +02005395 rcu_read_lock();
5396 genlmsg_multicast_allns(msg, 0, nl80211_regulatory_mcgrp.id,
5397 GFP_ATOMIC);
5398 rcu_read_unlock();
Luis R. Rodriguez6bad8762009-04-02 14:08:09 -04005399
5400 return;
5401
5402nla_put_failure:
5403 genlmsg_cancel(msg, hdr);
5404 nlmsg_free(msg);
5405}
5406
Jouni Malinen9588bbd2009-12-23 13:15:41 +01005407static void nl80211_send_remain_on_chan_event(
5408 int cmd, struct cfg80211_registered_device *rdev,
5409 struct net_device *netdev, u64 cookie,
5410 struct ieee80211_channel *chan,
5411 enum nl80211_channel_type channel_type,
5412 unsigned int duration, gfp_t gfp)
5413{
5414 struct sk_buff *msg;
5415 void *hdr;
5416
5417 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
5418 if (!msg)
5419 return;
5420
5421 hdr = nl80211hdr_put(msg, 0, 0, 0, cmd);
5422 if (!hdr) {
5423 nlmsg_free(msg);
5424 return;
5425 }
5426
5427 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
5428 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
5429 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, chan->center_freq);
5430 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, channel_type);
5431 NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, cookie);
5432
5433 if (cmd == NL80211_CMD_REMAIN_ON_CHANNEL)
5434 NLA_PUT_U32(msg, NL80211_ATTR_DURATION, duration);
5435
5436 if (genlmsg_end(msg, hdr) < 0) {
5437 nlmsg_free(msg);
5438 return;
5439 }
5440
5441 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
5442 nl80211_mlme_mcgrp.id, gfp);
5443 return;
5444
5445 nla_put_failure:
5446 genlmsg_cancel(msg, hdr);
5447 nlmsg_free(msg);
5448}
5449
5450void nl80211_send_remain_on_channel(struct cfg80211_registered_device *rdev,
5451 struct net_device *netdev, u64 cookie,
5452 struct ieee80211_channel *chan,
5453 enum nl80211_channel_type channel_type,
5454 unsigned int duration, gfp_t gfp)
5455{
5456 nl80211_send_remain_on_chan_event(NL80211_CMD_REMAIN_ON_CHANNEL,
5457 rdev, netdev, cookie, chan,
5458 channel_type, duration, gfp);
5459}
5460
5461void nl80211_send_remain_on_channel_cancel(
5462 struct cfg80211_registered_device *rdev, struct net_device *netdev,
5463 u64 cookie, struct ieee80211_channel *chan,
5464 enum nl80211_channel_type channel_type, gfp_t gfp)
5465{
5466 nl80211_send_remain_on_chan_event(NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL,
5467 rdev, netdev, cookie, chan,
5468 channel_type, 0, gfp);
5469}
5470
Johannes Berg98b62182009-12-23 13:15:44 +01005471void nl80211_send_sta_event(struct cfg80211_registered_device *rdev,
5472 struct net_device *dev, const u8 *mac_addr,
5473 struct station_info *sinfo, gfp_t gfp)
5474{
5475 struct sk_buff *msg;
5476
5477 msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
5478 if (!msg)
5479 return;
5480
5481 if (nl80211_send_station(msg, 0, 0, 0, dev, mac_addr, sinfo) < 0) {
5482 nlmsg_free(msg);
5483 return;
5484 }
5485
5486 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
5487 nl80211_mlme_mcgrp.id, gfp);
5488}
5489
Johannes Berg2e161f72010-08-12 15:38:38 +02005490int nl80211_send_mgmt(struct cfg80211_registered_device *rdev,
5491 struct net_device *netdev, u32 nlpid,
5492 int freq, const u8 *buf, size_t len, gfp_t gfp)
Jouni Malinen026331c2010-02-15 12:53:10 +02005493{
5494 struct sk_buff *msg;
5495 void *hdr;
5496 int err;
5497
5498 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
5499 if (!msg)
5500 return -ENOMEM;
5501
Johannes Berg2e161f72010-08-12 15:38:38 +02005502 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_FRAME);
Jouni Malinen026331c2010-02-15 12:53:10 +02005503 if (!hdr) {
5504 nlmsg_free(msg);
5505 return -ENOMEM;
5506 }
5507
5508 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
5509 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
5510 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq);
5511 NLA_PUT(msg, NL80211_ATTR_FRAME, len, buf);
5512
5513 err = genlmsg_end(msg, hdr);
5514 if (err < 0) {
5515 nlmsg_free(msg);
5516 return err;
5517 }
5518
5519 err = genlmsg_unicast(wiphy_net(&rdev->wiphy), msg, nlpid);
5520 if (err < 0)
5521 return err;
5522 return 0;
5523
5524 nla_put_failure:
5525 genlmsg_cancel(msg, hdr);
5526 nlmsg_free(msg);
5527 return -ENOBUFS;
5528}
5529
Johannes Berg2e161f72010-08-12 15:38:38 +02005530void nl80211_send_mgmt_tx_status(struct cfg80211_registered_device *rdev,
5531 struct net_device *netdev, u64 cookie,
5532 const u8 *buf, size_t len, bool ack,
5533 gfp_t gfp)
Jouni Malinen026331c2010-02-15 12:53:10 +02005534{
5535 struct sk_buff *msg;
5536 void *hdr;
5537
5538 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
5539 if (!msg)
5540 return;
5541
Johannes Berg2e161f72010-08-12 15:38:38 +02005542 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_FRAME_TX_STATUS);
Jouni Malinen026331c2010-02-15 12:53:10 +02005543 if (!hdr) {
5544 nlmsg_free(msg);
5545 return;
5546 }
5547
5548 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
5549 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
5550 NLA_PUT(msg, NL80211_ATTR_FRAME, len, buf);
5551 NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, cookie);
5552 if (ack)
5553 NLA_PUT_FLAG(msg, NL80211_ATTR_ACK);
5554
5555 if (genlmsg_end(msg, hdr) < 0) {
5556 nlmsg_free(msg);
5557 return;
5558 }
5559
5560 genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, gfp);
5561 return;
5562
5563 nla_put_failure:
5564 genlmsg_cancel(msg, hdr);
5565 nlmsg_free(msg);
5566}
5567
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02005568void
5569nl80211_send_cqm_rssi_notify(struct cfg80211_registered_device *rdev,
5570 struct net_device *netdev,
5571 enum nl80211_cqm_rssi_threshold_event rssi_event,
5572 gfp_t gfp)
5573{
5574 struct sk_buff *msg;
5575 struct nlattr *pinfoattr;
5576 void *hdr;
5577
5578 msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
5579 if (!msg)
5580 return;
5581
5582 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_NOTIFY_CQM);
5583 if (!hdr) {
5584 nlmsg_free(msg);
5585 return;
5586 }
5587
5588 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
5589 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
5590
5591 pinfoattr = nla_nest_start(msg, NL80211_ATTR_CQM);
5592 if (!pinfoattr)
5593 goto nla_put_failure;
5594
5595 NLA_PUT_U32(msg, NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT,
5596 rssi_event);
5597
5598 nla_nest_end(msg, pinfoattr);
5599
5600 if (genlmsg_end(msg, hdr) < 0) {
5601 nlmsg_free(msg);
5602 return;
5603 }
5604
5605 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
5606 nl80211_mlme_mcgrp.id, gfp);
5607 return;
5608
5609 nla_put_failure:
5610 genlmsg_cancel(msg, hdr);
5611 nlmsg_free(msg);
5612}
5613
Jouni Malinen026331c2010-02-15 12:53:10 +02005614static int nl80211_netlink_notify(struct notifier_block * nb,
5615 unsigned long state,
5616 void *_notify)
5617{
5618 struct netlink_notify *notify = _notify;
5619 struct cfg80211_registered_device *rdev;
5620 struct wireless_dev *wdev;
5621
5622 if (state != NETLINK_URELEASE)
5623 return NOTIFY_DONE;
5624
5625 rcu_read_lock();
5626
5627 list_for_each_entry_rcu(rdev, &cfg80211_rdev_list, list)
5628 list_for_each_entry_rcu(wdev, &rdev->netdev_list, list)
Johannes Berg2e161f72010-08-12 15:38:38 +02005629 cfg80211_mlme_unregister_socket(wdev, notify->pid);
Jouni Malinen026331c2010-02-15 12:53:10 +02005630
5631 rcu_read_unlock();
5632
5633 return NOTIFY_DONE;
5634}
5635
5636static struct notifier_block nl80211_netlink_notifier = {
5637 .notifier_call = nl80211_netlink_notify,
5638};
5639
Johannes Berg55682962007-09-20 13:09:35 -04005640/* initialisation/exit functions */
5641
5642int nl80211_init(void)
5643{
Michał Mirosław0d63cbb2009-05-21 10:34:06 +00005644 int err;
Johannes Berg55682962007-09-20 13:09:35 -04005645
Michał Mirosław0d63cbb2009-05-21 10:34:06 +00005646 err = genl_register_family_with_ops(&nl80211_fam,
5647 nl80211_ops, ARRAY_SIZE(nl80211_ops));
Johannes Berg55682962007-09-20 13:09:35 -04005648 if (err)
5649 return err;
5650
Johannes Berg55682962007-09-20 13:09:35 -04005651 err = genl_register_mc_group(&nl80211_fam, &nl80211_config_mcgrp);
5652 if (err)
5653 goto err_out;
5654
Johannes Berg2a519312009-02-10 21:25:55 +01005655 err = genl_register_mc_group(&nl80211_fam, &nl80211_scan_mcgrp);
5656 if (err)
5657 goto err_out;
5658
Luis R. Rodriguez73d54c92009-03-09 22:07:42 -04005659 err = genl_register_mc_group(&nl80211_fam, &nl80211_regulatory_mcgrp);
5660 if (err)
5661 goto err_out;
5662
Jouni Malinen6039f6d2009-03-19 13:39:21 +02005663 err = genl_register_mc_group(&nl80211_fam, &nl80211_mlme_mcgrp);
5664 if (err)
5665 goto err_out;
5666
Johannes Bergaff89a92009-07-01 21:26:51 +02005667#ifdef CONFIG_NL80211_TESTMODE
5668 err = genl_register_mc_group(&nl80211_fam, &nl80211_testmode_mcgrp);
5669 if (err)
5670 goto err_out;
5671#endif
5672
Jouni Malinen026331c2010-02-15 12:53:10 +02005673 err = netlink_register_notifier(&nl80211_netlink_notifier);
5674 if (err)
5675 goto err_out;
5676
Johannes Berg55682962007-09-20 13:09:35 -04005677 return 0;
5678 err_out:
5679 genl_unregister_family(&nl80211_fam);
5680 return err;
5681}
5682
5683void nl80211_exit(void)
5684{
Jouni Malinen026331c2010-02-15 12:53:10 +02005685 netlink_unregister_notifier(&nl80211_netlink_notifier);
Johannes Berg55682962007-09-20 13:09:35 -04005686 genl_unregister_family(&nl80211_fam);
5687}