blob: fd92b6b7ff045858034d9838ab94d1b870566b6e [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
26/* the netlink family */
27static struct genl_family nl80211_fam = {
28 .id = GENL_ID_GENERATE, /* don't bother with a hardcoded ID */
29 .name = "nl80211", /* have users key off the name instead */
30 .hdrsize = 0, /* no private header */
31 .version = 1, /* no particular meaning now */
32 .maxattr = NL80211_ATTR_MAX,
Johannes Berg463d0182009-07-14 00:33:35 +020033 .netnsok = true,
Johannes Berg55682962007-09-20 13:09:35 -040034};
35
Johannes Berg79c97e92009-07-07 03:56:12 +020036/* internal helper: get rdev and dev */
Johannes Berg463d0182009-07-14 00:33:35 +020037static int get_rdev_dev_by_info_ifindex(struct genl_info *info,
Johannes Berg79c97e92009-07-07 03:56:12 +020038 struct cfg80211_registered_device **rdev,
Johannes Berg55682962007-09-20 13:09:35 -040039 struct net_device **dev)
40{
Johannes Berg463d0182009-07-14 00:33:35 +020041 struct nlattr **attrs = info->attrs;
Johannes Berg55682962007-09-20 13:09:35 -040042 int ifindex;
43
Johannes Bergbba95fe2008-07-29 13:22:51 +020044 if (!attrs[NL80211_ATTR_IFINDEX])
Johannes Berg55682962007-09-20 13:09:35 -040045 return -EINVAL;
46
Johannes Bergbba95fe2008-07-29 13:22:51 +020047 ifindex = nla_get_u32(attrs[NL80211_ATTR_IFINDEX]);
Johannes Berg463d0182009-07-14 00:33:35 +020048 *dev = dev_get_by_index(genl_info_net(info), ifindex);
Johannes Berg55682962007-09-20 13:09:35 -040049 if (!*dev)
50 return -ENODEV;
51
Johannes Berg463d0182009-07-14 00:33:35 +020052 *rdev = cfg80211_get_dev_from_ifindex(genl_info_net(info), ifindex);
Johannes Berg79c97e92009-07-07 03:56:12 +020053 if (IS_ERR(*rdev)) {
Johannes Berg55682962007-09-20 13:09:35 -040054 dev_put(*dev);
Johannes Berg79c97e92009-07-07 03:56:12 +020055 return PTR_ERR(*rdev);
Johannes Berg55682962007-09-20 13:09:35 -040056 }
57
58 return 0;
59}
60
61/* policy for the attributes */
Alexey Dobriyanb54452b2010-02-18 08:14:31 +000062static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = {
Johannes Berg55682962007-09-20 13:09:35 -040063 [NL80211_ATTR_WIPHY] = { .type = NLA_U32 },
64 [NL80211_ATTR_WIPHY_NAME] = { .type = NLA_NUL_STRING,
David S. Miller079e24e2009-05-26 21:15:00 -070065 .len = 20-1 },
Jouni Malinen31888482008-10-30 16:59:24 +020066 [NL80211_ATTR_WIPHY_TXQ_PARAMS] = { .type = NLA_NESTED },
Jouni Malinen72bdcf32008-11-26 16:15:24 +020067 [NL80211_ATTR_WIPHY_FREQ] = { .type = NLA_U32 },
Sujith094d05d2008-12-12 11:57:43 +053068 [NL80211_ATTR_WIPHY_CHANNEL_TYPE] = { .type = NLA_U32 },
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +020069 [NL80211_ATTR_WIPHY_RETRY_SHORT] = { .type = NLA_U8 },
70 [NL80211_ATTR_WIPHY_RETRY_LONG] = { .type = NLA_U8 },
71 [NL80211_ATTR_WIPHY_FRAG_THRESHOLD] = { .type = NLA_U32 },
72 [NL80211_ATTR_WIPHY_RTS_THRESHOLD] = { .type = NLA_U32 },
Lukáš Turek81077e82009-12-21 22:50:47 +010073 [NL80211_ATTR_WIPHY_COVERAGE_CLASS] = { .type = NLA_U8 },
Johannes Berg55682962007-09-20 13:09:35 -040074
75 [NL80211_ATTR_IFTYPE] = { .type = NLA_U32 },
76 [NL80211_ATTR_IFINDEX] = { .type = NLA_U32 },
77 [NL80211_ATTR_IFNAME] = { .type = NLA_NUL_STRING, .len = IFNAMSIZ-1 },
Johannes Berg41ade002007-12-19 02:03:29 +010078
79 [NL80211_ATTR_MAC] = { .type = NLA_BINARY, .len = ETH_ALEN },
Johannes Berg3e5d7642009-07-07 14:37:26 +020080 [NL80211_ATTR_PREV_BSSID] = { .type = NLA_BINARY, .len = ETH_ALEN },
Johannes Berg41ade002007-12-19 02:03:29 +010081
Johannes Bergb9454e82009-07-08 13:29:08 +020082 [NL80211_ATTR_KEY] = { .type = NLA_NESTED, },
Johannes Berg41ade002007-12-19 02:03:29 +010083 [NL80211_ATTR_KEY_DATA] = { .type = NLA_BINARY,
84 .len = WLAN_MAX_KEY_LEN },
85 [NL80211_ATTR_KEY_IDX] = { .type = NLA_U8 },
86 [NL80211_ATTR_KEY_CIPHER] = { .type = NLA_U32 },
87 [NL80211_ATTR_KEY_DEFAULT] = { .type = NLA_FLAG },
Jouni Malinen9f26a952009-05-15 12:38:32 +030088 [NL80211_ATTR_KEY_SEQ] = { .type = NLA_BINARY, .len = 8 },
Johannes Berged1b6cc2007-12-19 02:03:32 +010089
90 [NL80211_ATTR_BEACON_INTERVAL] = { .type = NLA_U32 },
91 [NL80211_ATTR_DTIM_PERIOD] = { .type = NLA_U32 },
92 [NL80211_ATTR_BEACON_HEAD] = { .type = NLA_BINARY,
93 .len = IEEE80211_MAX_DATA_LEN },
94 [NL80211_ATTR_BEACON_TAIL] = { .type = NLA_BINARY,
95 .len = IEEE80211_MAX_DATA_LEN },
Johannes Berg5727ef12007-12-19 02:03:34 +010096 [NL80211_ATTR_STA_AID] = { .type = NLA_U16 },
97 [NL80211_ATTR_STA_FLAGS] = { .type = NLA_NESTED },
98 [NL80211_ATTR_STA_LISTEN_INTERVAL] = { .type = NLA_U16 },
99 [NL80211_ATTR_STA_SUPPORTED_RATES] = { .type = NLA_BINARY,
100 .len = NL80211_MAX_SUPP_RATES },
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100101 [NL80211_ATTR_STA_PLINK_ACTION] = { .type = NLA_U8 },
Johannes Berg5727ef12007-12-19 02:03:34 +0100102 [NL80211_ATTR_STA_VLAN] = { .type = NLA_U32 },
Johannes Berg0a9542e2008-10-15 11:54:04 +0200103 [NL80211_ATTR_MNTR_FLAGS] = { /* NLA_NESTED can't be empty */ },
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100104 [NL80211_ATTR_MESH_ID] = { .type = NLA_BINARY,
105 .len = IEEE80211_MAX_MESH_ID_LEN },
106 [NL80211_ATTR_MPATH_NEXT_HOP] = { .type = NLA_U32 },
Jouni Malinen9f1ba902008-08-07 20:07:01 +0300107
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -0700108 [NL80211_ATTR_REG_ALPHA2] = { .type = NLA_STRING, .len = 2 },
109 [NL80211_ATTR_REG_RULES] = { .type = NLA_NESTED },
110
Jouni Malinen9f1ba902008-08-07 20:07:01 +0300111 [NL80211_ATTR_BSS_CTS_PROT] = { .type = NLA_U8 },
112 [NL80211_ATTR_BSS_SHORT_PREAMBLE] = { .type = NLA_U8 },
113 [NL80211_ATTR_BSS_SHORT_SLOT_TIME] = { .type = NLA_U8 },
Jouni Malinen90c97a02008-10-30 16:59:22 +0200114 [NL80211_ATTR_BSS_BASIC_RATES] = { .type = NLA_BINARY,
115 .len = NL80211_MAX_SUPP_RATES },
Jouni Malinen36aedc92008-08-25 11:58:58 +0300116
colin@cozybit.com93da9cc2008-10-21 12:03:48 -0700117 [NL80211_ATTR_MESH_PARAMS] = { .type = NLA_NESTED },
118
Jouni Malinen36aedc92008-08-25 11:58:58 +0300119 [NL80211_ATTR_HT_CAPABILITY] = { .type = NLA_BINARY,
120 .len = NL80211_HT_CAPABILITY_LEN },
Jouni Malinen9aed3cc2009-01-13 16:03:29 +0200121
122 [NL80211_ATTR_MGMT_SUBTYPE] = { .type = NLA_U8 },
123 [NL80211_ATTR_IE] = { .type = NLA_BINARY,
124 .len = IEEE80211_MAX_DATA_LEN },
Johannes Berg2a519312009-02-10 21:25:55 +0100125 [NL80211_ATTR_SCAN_FREQUENCIES] = { .type = NLA_NESTED },
126 [NL80211_ATTR_SCAN_SSIDS] = { .type = NLA_NESTED },
Jouni Malinen636a5d32009-03-19 13:39:22 +0200127
128 [NL80211_ATTR_SSID] = { .type = NLA_BINARY,
129 .len = IEEE80211_MAX_SSID_LEN },
130 [NL80211_ATTR_AUTH_TYPE] = { .type = NLA_U32 },
131 [NL80211_ATTR_REASON_CODE] = { .type = NLA_U16 },
Johannes Berg04a773a2009-04-19 21:24:32 +0200132 [NL80211_ATTR_FREQ_FIXED] = { .type = NLA_FLAG },
Jouni Malinen1965c852009-04-22 21:38:25 +0300133 [NL80211_ATTR_TIMED_OUT] = { .type = NLA_FLAG },
Jouni Malinendc6382c2009-05-06 22:09:37 +0300134 [NL80211_ATTR_USE_MFP] = { .type = NLA_U32 },
Johannes Bergeccb8e82009-05-11 21:57:56 +0300135 [NL80211_ATTR_STA_FLAGS2] = {
136 .len = sizeof(struct nl80211_sta_flag_update),
137 },
Jouni Malinen3f77316c2009-05-11 21:57:57 +0300138 [NL80211_ATTR_CONTROL_PORT] = { .type = NLA_FLAG },
Johannes Bergc0692b82010-08-27 14:26:53 +0300139 [NL80211_ATTR_CONTROL_PORT_ETHERTYPE] = { .type = NLA_U16 },
140 [NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT] = { .type = NLA_FLAG },
Samuel Ortizb23aa672009-07-01 21:26:54 +0200141 [NL80211_ATTR_PRIVACY] = { .type = NLA_FLAG },
142 [NL80211_ATTR_CIPHER_SUITE_GROUP] = { .type = NLA_U32 },
143 [NL80211_ATTR_WPA_VERSIONS] = { .type = NLA_U32 },
Johannes Berg463d0182009-07-14 00:33:35 +0200144 [NL80211_ATTR_PID] = { .type = NLA_U32 },
Felix Fietkau8b787642009-11-10 18:53:10 +0100145 [NL80211_ATTR_4ADDR] = { .type = NLA_U8 },
Samuel Ortiz67fbb162009-11-24 23:59:15 +0100146 [NL80211_ATTR_PMKID] = { .type = NLA_BINARY,
147 .len = WLAN_PMKID_LEN },
Jouni Malinen9588bbd2009-12-23 13:15:41 +0100148 [NL80211_ATTR_DURATION] = { .type = NLA_U32 },
149 [NL80211_ATTR_COOKIE] = { .type = NLA_U64 },
Jouni Malinen13ae75b2009-12-29 12:59:45 +0200150 [NL80211_ATTR_TX_RATES] = { .type = NLA_NESTED },
Jouni Malinen026331c2010-02-15 12:53:10 +0200151 [NL80211_ATTR_FRAME] = { .type = NLA_BINARY,
152 .len = IEEE80211_MAX_DATA_LEN },
153 [NL80211_ATTR_FRAME_MATCH] = { .type = NLA_BINARY, },
Kalle Valoffb9eb32010-02-17 17:58:10 +0200154 [NL80211_ATTR_PS_STATE] = { .type = NLA_U32 },
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +0200155 [NL80211_ATTR_CQM] = { .type = NLA_NESTED, },
Jouni Malinend5cdfac2010-04-04 09:37:19 +0300156 [NL80211_ATTR_LOCAL_STATE_CHANGE] = { .type = NLA_FLAG },
Felix Fietkaufd8aaaf2010-04-27 01:23:35 +0200157 [NL80211_ATTR_AP_ISOLATE] = { .type = NLA_U8 },
Juuso Oikarinen98d2ff82010-06-23 12:12:38 +0300158
159 [NL80211_ATTR_WIPHY_TX_POWER_SETTING] = { .type = NLA_U32 },
160 [NL80211_ATTR_WIPHY_TX_POWER_LEVEL] = { .type = NLA_U32 },
Johannes Berg2e161f72010-08-12 15:38:38 +0200161 [NL80211_ATTR_FRAME_TYPE] = { .type = NLA_U16 },
Johannes Berg55682962007-09-20 13:09:35 -0400162};
163
Johannes Bergb9454e82009-07-08 13:29:08 +0200164/* policy for the attributes */
Alexey Dobriyanb54452b2010-02-18 08:14:31 +0000165static const struct nla_policy nl80211_key_policy[NL80211_KEY_MAX + 1] = {
Johannes Bergfffd0932009-07-08 14:22:54 +0200166 [NL80211_KEY_DATA] = { .type = NLA_BINARY, .len = WLAN_MAX_KEY_LEN },
Johannes Bergb9454e82009-07-08 13:29:08 +0200167 [NL80211_KEY_IDX] = { .type = NLA_U8 },
168 [NL80211_KEY_CIPHER] = { .type = NLA_U32 },
169 [NL80211_KEY_SEQ] = { .type = NLA_BINARY, .len = 8 },
170 [NL80211_KEY_DEFAULT] = { .type = NLA_FLAG },
171 [NL80211_KEY_DEFAULT_MGMT] = { .type = NLA_FLAG },
172};
173
Holger Schuriga0438972009-11-11 11:30:02 +0100174/* ifidx get helper */
175static int nl80211_get_ifidx(struct netlink_callback *cb)
176{
177 int res;
178
179 res = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize,
180 nl80211_fam.attrbuf, nl80211_fam.maxattr,
181 nl80211_policy);
182 if (res)
183 return res;
184
185 if (!nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX])
186 return -EINVAL;
187
188 res = nla_get_u32(nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX]);
189 if (!res)
190 return -EINVAL;
191 return res;
192}
193
Johannes Bergf4a11bb2009-03-27 12:40:28 +0100194/* IE validation */
195static bool is_valid_ie_attr(const struct nlattr *attr)
196{
197 const u8 *pos;
198 int len;
199
200 if (!attr)
201 return true;
202
203 pos = nla_data(attr);
204 len = nla_len(attr);
205
206 while (len) {
207 u8 elemlen;
208
209 if (len < 2)
210 return false;
211 len -= 2;
212
213 elemlen = pos[1];
214 if (elemlen > len)
215 return false;
216
217 len -= elemlen;
218 pos += 2 + elemlen;
219 }
220
221 return true;
222}
223
Johannes Berg55682962007-09-20 13:09:35 -0400224/* message building helper */
225static inline void *nl80211hdr_put(struct sk_buff *skb, u32 pid, u32 seq,
226 int flags, u8 cmd)
227{
228 /* since there is no private header just add the generic one */
229 return genlmsg_put(skb, pid, seq, &nl80211_fam, flags, cmd);
230}
231
Luis R. Rodriguez5dab3b82009-04-02 14:08:08 -0400232static int nl80211_msg_put_channel(struct sk_buff *msg,
233 struct ieee80211_channel *chan)
234{
235 NLA_PUT_U32(msg, NL80211_FREQUENCY_ATTR_FREQ,
236 chan->center_freq);
237
238 if (chan->flags & IEEE80211_CHAN_DISABLED)
239 NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_DISABLED);
240 if (chan->flags & IEEE80211_CHAN_PASSIVE_SCAN)
241 NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_PASSIVE_SCAN);
242 if (chan->flags & IEEE80211_CHAN_NO_IBSS)
243 NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_NO_IBSS);
244 if (chan->flags & IEEE80211_CHAN_RADAR)
245 NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_RADAR);
246
247 NLA_PUT_U32(msg, NL80211_FREQUENCY_ATTR_MAX_TX_POWER,
248 DBM_TO_MBM(chan->max_power));
249
250 return 0;
251
252 nla_put_failure:
253 return -ENOBUFS;
254}
255
Johannes Berg55682962007-09-20 13:09:35 -0400256/* netlink command implementations */
257
Johannes Bergb9454e82009-07-08 13:29:08 +0200258struct key_parse {
259 struct key_params p;
260 int idx;
261 bool def, defmgmt;
262};
263
264static int nl80211_parse_key_new(struct nlattr *key, struct key_parse *k)
265{
266 struct nlattr *tb[NL80211_KEY_MAX + 1];
267 int err = nla_parse_nested(tb, NL80211_KEY_MAX, key,
268 nl80211_key_policy);
269 if (err)
270 return err;
271
272 k->def = !!tb[NL80211_KEY_DEFAULT];
273 k->defmgmt = !!tb[NL80211_KEY_DEFAULT_MGMT];
274
275 if (tb[NL80211_KEY_IDX])
276 k->idx = nla_get_u8(tb[NL80211_KEY_IDX]);
277
278 if (tb[NL80211_KEY_DATA]) {
279 k->p.key = nla_data(tb[NL80211_KEY_DATA]);
280 k->p.key_len = nla_len(tb[NL80211_KEY_DATA]);
281 }
282
283 if (tb[NL80211_KEY_SEQ]) {
284 k->p.seq = nla_data(tb[NL80211_KEY_SEQ]);
285 k->p.seq_len = nla_len(tb[NL80211_KEY_SEQ]);
286 }
287
288 if (tb[NL80211_KEY_CIPHER])
289 k->p.cipher = nla_get_u32(tb[NL80211_KEY_CIPHER]);
290
291 return 0;
292}
293
294static int nl80211_parse_key_old(struct genl_info *info, struct key_parse *k)
295{
296 if (info->attrs[NL80211_ATTR_KEY_DATA]) {
297 k->p.key = nla_data(info->attrs[NL80211_ATTR_KEY_DATA]);
298 k->p.key_len = nla_len(info->attrs[NL80211_ATTR_KEY_DATA]);
299 }
300
301 if (info->attrs[NL80211_ATTR_KEY_SEQ]) {
302 k->p.seq = nla_data(info->attrs[NL80211_ATTR_KEY_SEQ]);
303 k->p.seq_len = nla_len(info->attrs[NL80211_ATTR_KEY_SEQ]);
304 }
305
306 if (info->attrs[NL80211_ATTR_KEY_IDX])
307 k->idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]);
308
309 if (info->attrs[NL80211_ATTR_KEY_CIPHER])
310 k->p.cipher = nla_get_u32(info->attrs[NL80211_ATTR_KEY_CIPHER]);
311
312 k->def = !!info->attrs[NL80211_ATTR_KEY_DEFAULT];
313 k->defmgmt = !!info->attrs[NL80211_ATTR_KEY_DEFAULT_MGMT];
314
315 return 0;
316}
317
318static int nl80211_parse_key(struct genl_info *info, struct key_parse *k)
319{
320 int err;
321
322 memset(k, 0, sizeof(*k));
323 k->idx = -1;
324
325 if (info->attrs[NL80211_ATTR_KEY])
326 err = nl80211_parse_key_new(info->attrs[NL80211_ATTR_KEY], k);
327 else
328 err = nl80211_parse_key_old(info, k);
329
330 if (err)
331 return err;
332
333 if (k->def && k->defmgmt)
334 return -EINVAL;
335
336 if (k->idx != -1) {
337 if (k->defmgmt) {
338 if (k->idx < 4 || k->idx > 5)
339 return -EINVAL;
340 } else if (k->def) {
341 if (k->idx < 0 || k->idx > 3)
342 return -EINVAL;
343 } else {
344 if (k->idx < 0 || k->idx > 5)
345 return -EINVAL;
346 }
347 }
348
349 return 0;
350}
351
Johannes Bergfffd0932009-07-08 14:22:54 +0200352static struct cfg80211_cached_keys *
353nl80211_parse_connkeys(struct cfg80211_registered_device *rdev,
354 struct nlattr *keys)
355{
356 struct key_parse parse;
357 struct nlattr *key;
358 struct cfg80211_cached_keys *result;
359 int rem, err, def = 0;
360
361 result = kzalloc(sizeof(*result), GFP_KERNEL);
362 if (!result)
363 return ERR_PTR(-ENOMEM);
364
365 result->def = -1;
366 result->defmgmt = -1;
367
368 nla_for_each_nested(key, keys, rem) {
369 memset(&parse, 0, sizeof(parse));
370 parse.idx = -1;
371
372 err = nl80211_parse_key_new(key, &parse);
373 if (err)
374 goto error;
375 err = -EINVAL;
376 if (!parse.p.key)
377 goto error;
378 if (parse.idx < 0 || parse.idx > 4)
379 goto error;
380 if (parse.def) {
381 if (def)
382 goto error;
383 def = 1;
384 result->def = parse.idx;
385 } else if (parse.defmgmt)
386 goto error;
387 err = cfg80211_validate_key_settings(rdev, &parse.p,
388 parse.idx, NULL);
389 if (err)
390 goto error;
391 result->params[parse.idx].cipher = parse.p.cipher;
392 result->params[parse.idx].key_len = parse.p.key_len;
393 result->params[parse.idx].key = result->data[parse.idx];
394 memcpy(result->data[parse.idx], parse.p.key, parse.p.key_len);
395 }
396
397 return result;
398 error:
399 kfree(result);
400 return ERR_PTR(err);
401}
402
403static int nl80211_key_allowed(struct wireless_dev *wdev)
404{
405 ASSERT_WDEV_LOCK(wdev);
406
407 if (!netif_running(wdev->netdev))
408 return -ENETDOWN;
409
410 switch (wdev->iftype) {
411 case NL80211_IFTYPE_AP:
412 case NL80211_IFTYPE_AP_VLAN:
Johannes Berg074ac8d2010-09-16 14:58:22 +0200413 case NL80211_IFTYPE_P2P_GO:
Johannes Bergfffd0932009-07-08 14:22:54 +0200414 break;
415 case NL80211_IFTYPE_ADHOC:
416 if (!wdev->current_bss)
417 return -ENOLINK;
418 break;
419 case NL80211_IFTYPE_STATION:
Johannes Berg074ac8d2010-09-16 14:58:22 +0200420 case NL80211_IFTYPE_P2P_CLIENT:
Johannes Bergfffd0932009-07-08 14:22:54 +0200421 if (wdev->sme_state != CFG80211_SME_CONNECTED)
422 return -ENOLINK;
423 break;
424 default:
425 return -EINVAL;
426 }
427
428 return 0;
429}
430
Johannes Berg55682962007-09-20 13:09:35 -0400431static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags,
432 struct cfg80211_registered_device *dev)
433{
434 void *hdr;
Johannes Bergee688b002008-01-24 19:38:39 +0100435 struct nlattr *nl_bands, *nl_band;
436 struct nlattr *nl_freqs, *nl_freq;
437 struct nlattr *nl_rates, *nl_rate;
Luis R. Rodriguezf59ac042008-08-29 16:26:43 -0700438 struct nlattr *nl_modes;
Johannes Berg8fdc6212009-03-14 09:34:01 +0100439 struct nlattr *nl_cmds;
Johannes Bergee688b002008-01-24 19:38:39 +0100440 enum ieee80211_band band;
441 struct ieee80211_channel *chan;
442 struct ieee80211_rate *rate;
443 int i;
Luis R. Rodriguezf59ac042008-08-29 16:26:43 -0700444 u16 ifmodes = dev->wiphy.interface_modes;
Johannes Berg2e161f72010-08-12 15:38:38 +0200445 const struct ieee80211_txrx_stypes *mgmt_stypes =
446 dev->wiphy.mgmt_stypes;
Johannes Berg55682962007-09-20 13:09:35 -0400447
448 hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_WIPHY);
449 if (!hdr)
450 return -1;
451
Luis R. Rodriguezb5850a72009-02-21 00:04:19 -0500452 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, dev->wiphy_idx);
Johannes Berg55682962007-09-20 13:09:35 -0400453 NLA_PUT_STRING(msg, NL80211_ATTR_WIPHY_NAME, wiphy_name(&dev->wiphy));
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +0200454
Johannes Bergf5ea9122009-08-07 16:17:38 +0200455 NLA_PUT_U32(msg, NL80211_ATTR_GENERATION,
456 cfg80211_rdev_list_generation);
457
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +0200458 NLA_PUT_U8(msg, NL80211_ATTR_WIPHY_RETRY_SHORT,
459 dev->wiphy.retry_short);
460 NLA_PUT_U8(msg, NL80211_ATTR_WIPHY_RETRY_LONG,
461 dev->wiphy.retry_long);
462 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FRAG_THRESHOLD,
463 dev->wiphy.frag_threshold);
464 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_RTS_THRESHOLD,
465 dev->wiphy.rts_threshold);
Lukáš Turek81077e82009-12-21 22:50:47 +0100466 NLA_PUT_U8(msg, NL80211_ATTR_WIPHY_COVERAGE_CLASS,
467 dev->wiphy.coverage_class);
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +0200468
Johannes Berg2a519312009-02-10 21:25:55 +0100469 NLA_PUT_U8(msg, NL80211_ATTR_MAX_NUM_SCAN_SSIDS,
470 dev->wiphy.max_scan_ssids);
Johannes Berg18a83652009-03-31 12:12:05 +0200471 NLA_PUT_U16(msg, NL80211_ATTR_MAX_SCAN_IE_LEN,
472 dev->wiphy.max_scan_ie_len);
Johannes Bergee688b002008-01-24 19:38:39 +0100473
Johannes Berg25e47c12009-04-02 20:14:06 +0200474 NLA_PUT(msg, NL80211_ATTR_CIPHER_SUITES,
475 sizeof(u32) * dev->wiphy.n_cipher_suites,
476 dev->wiphy.cipher_suites);
477
Samuel Ortiz67fbb162009-11-24 23:59:15 +0100478 NLA_PUT_U8(msg, NL80211_ATTR_MAX_NUM_PMKIDS,
479 dev->wiphy.max_num_pmkids);
480
Johannes Bergc0692b82010-08-27 14:26:53 +0300481 if (dev->wiphy.flags & WIPHY_FLAG_CONTROL_PORT_PROTOCOL)
482 NLA_PUT_FLAG(msg, NL80211_ATTR_CONTROL_PORT_ETHERTYPE);
483
Luis R. Rodriguezf59ac042008-08-29 16:26:43 -0700484 nl_modes = nla_nest_start(msg, NL80211_ATTR_SUPPORTED_IFTYPES);
485 if (!nl_modes)
486 goto nla_put_failure;
487
488 i = 0;
489 while (ifmodes) {
490 if (ifmodes & 1)
491 NLA_PUT_FLAG(msg, i);
492 ifmodes >>= 1;
493 i++;
494 }
495
496 nla_nest_end(msg, nl_modes);
497
Johannes Bergee688b002008-01-24 19:38:39 +0100498 nl_bands = nla_nest_start(msg, NL80211_ATTR_WIPHY_BANDS);
499 if (!nl_bands)
500 goto nla_put_failure;
501
502 for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
503 if (!dev->wiphy.bands[band])
504 continue;
505
506 nl_band = nla_nest_start(msg, band);
507 if (!nl_band)
508 goto nla_put_failure;
509
Johannes Bergd51626d2008-10-09 12:20:13 +0200510 /* add HT info */
511 if (dev->wiphy.bands[band]->ht_cap.ht_supported) {
512 NLA_PUT(msg, NL80211_BAND_ATTR_HT_MCS_SET,
513 sizeof(dev->wiphy.bands[band]->ht_cap.mcs),
514 &dev->wiphy.bands[band]->ht_cap.mcs);
515 NLA_PUT_U16(msg, NL80211_BAND_ATTR_HT_CAPA,
516 dev->wiphy.bands[band]->ht_cap.cap);
517 NLA_PUT_U8(msg, NL80211_BAND_ATTR_HT_AMPDU_FACTOR,
518 dev->wiphy.bands[band]->ht_cap.ampdu_factor);
519 NLA_PUT_U8(msg, NL80211_BAND_ATTR_HT_AMPDU_DENSITY,
520 dev->wiphy.bands[band]->ht_cap.ampdu_density);
521 }
522
Johannes Bergee688b002008-01-24 19:38:39 +0100523 /* add frequencies */
524 nl_freqs = nla_nest_start(msg, NL80211_BAND_ATTR_FREQS);
525 if (!nl_freqs)
526 goto nla_put_failure;
527
528 for (i = 0; i < dev->wiphy.bands[band]->n_channels; i++) {
529 nl_freq = nla_nest_start(msg, i);
530 if (!nl_freq)
531 goto nla_put_failure;
532
533 chan = &dev->wiphy.bands[band]->channels[i];
Johannes Bergee688b002008-01-24 19:38:39 +0100534
Luis R. Rodriguez5dab3b82009-04-02 14:08:08 -0400535 if (nl80211_msg_put_channel(msg, chan))
536 goto nla_put_failure;
Jouni Malinene2f367f262008-11-21 19:01:30 +0200537
Johannes Bergee688b002008-01-24 19:38:39 +0100538 nla_nest_end(msg, nl_freq);
539 }
540
541 nla_nest_end(msg, nl_freqs);
542
543 /* add bitrates */
544 nl_rates = nla_nest_start(msg, NL80211_BAND_ATTR_RATES);
545 if (!nl_rates)
546 goto nla_put_failure;
547
548 for (i = 0; i < dev->wiphy.bands[band]->n_bitrates; i++) {
549 nl_rate = nla_nest_start(msg, i);
550 if (!nl_rate)
551 goto nla_put_failure;
552
553 rate = &dev->wiphy.bands[band]->bitrates[i];
554 NLA_PUT_U32(msg, NL80211_BITRATE_ATTR_RATE,
555 rate->bitrate);
556 if (rate->flags & IEEE80211_RATE_SHORT_PREAMBLE)
557 NLA_PUT_FLAG(msg,
558 NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE);
559
560 nla_nest_end(msg, nl_rate);
561 }
562
563 nla_nest_end(msg, nl_rates);
564
565 nla_nest_end(msg, nl_band);
566 }
567 nla_nest_end(msg, nl_bands);
568
Johannes Berg8fdc6212009-03-14 09:34:01 +0100569 nl_cmds = nla_nest_start(msg, NL80211_ATTR_SUPPORTED_COMMANDS);
570 if (!nl_cmds)
571 goto nla_put_failure;
572
573 i = 0;
574#define CMD(op, n) \
575 do { \
576 if (dev->ops->op) { \
577 i++; \
578 NLA_PUT_U32(msg, i, NL80211_CMD_ ## n); \
579 } \
580 } while (0)
581
582 CMD(add_virtual_intf, NEW_INTERFACE);
583 CMD(change_virtual_intf, SET_INTERFACE);
584 CMD(add_key, NEW_KEY);
585 CMD(add_beacon, NEW_BEACON);
586 CMD(add_station, NEW_STATION);
587 CMD(add_mpath, NEW_MPATH);
588 CMD(set_mesh_params, SET_MESH_PARAMS);
589 CMD(change_bss, SET_BSS);
Jouni Malinen636a5d32009-03-19 13:39:22 +0200590 CMD(auth, AUTHENTICATE);
591 CMD(assoc, ASSOCIATE);
592 CMD(deauth, DEAUTHENTICATE);
593 CMD(disassoc, DISASSOCIATE);
Johannes Berg04a773a2009-04-19 21:24:32 +0200594 CMD(join_ibss, JOIN_IBSS);
Samuel Ortiz67fbb162009-11-24 23:59:15 +0100595 CMD(set_pmksa, SET_PMKSA);
596 CMD(del_pmksa, DEL_PMKSA);
597 CMD(flush_pmksa, FLUSH_PMKSA);
Jouni Malinen9588bbd2009-12-23 13:15:41 +0100598 CMD(remain_on_channel, REMAIN_ON_CHANNEL);
Jouni Malinen13ae75b2009-12-29 12:59:45 +0200599 CMD(set_bitrate_mask, SET_TX_BITRATE_MASK);
Johannes Berg2e161f72010-08-12 15:38:38 +0200600 CMD(mgmt_tx, FRAME);
Johannes Berg5be83de2009-11-19 00:56:28 +0100601 if (dev->wiphy.flags & WIPHY_FLAG_NETNS_OK) {
Johannes Berg463d0182009-07-14 00:33:35 +0200602 i++;
603 NLA_PUT_U32(msg, i, NL80211_CMD_SET_WIPHY_NETNS);
604 }
Johannes Bergf444de02010-05-05 15:25:02 +0200605 CMD(set_channel, SET_CHANNEL);
Bill Jordane8347eb2010-10-01 13:54:28 -0400606 CMD(set_wds_peer, SET_WDS_PEER);
Johannes Berg8fdc6212009-03-14 09:34:01 +0100607
608#undef CMD
Samuel Ortizb23aa672009-07-01 21:26:54 +0200609
Johannes Berg6829c872009-07-02 09:13:27 +0200610 if (dev->ops->connect || dev->ops->auth) {
Samuel Ortizb23aa672009-07-01 21:26:54 +0200611 i++;
612 NLA_PUT_U32(msg, i, NL80211_CMD_CONNECT);
613 }
614
Johannes Berg6829c872009-07-02 09:13:27 +0200615 if (dev->ops->disconnect || dev->ops->deauth) {
Samuel Ortizb23aa672009-07-01 21:26:54 +0200616 i++;
617 NLA_PUT_U32(msg, i, NL80211_CMD_DISCONNECT);
618 }
619
Johannes Berg8fdc6212009-03-14 09:34:01 +0100620 nla_nest_end(msg, nl_cmds);
621
Johannes Berg2e161f72010-08-12 15:38:38 +0200622 if (mgmt_stypes) {
623 u16 stypes;
624 struct nlattr *nl_ftypes, *nl_ifs;
625 enum nl80211_iftype ift;
626
627 nl_ifs = nla_nest_start(msg, NL80211_ATTR_TX_FRAME_TYPES);
628 if (!nl_ifs)
629 goto nla_put_failure;
630
631 for (ift = 0; ift < NUM_NL80211_IFTYPES; ift++) {
632 nl_ftypes = nla_nest_start(msg, ift);
633 if (!nl_ftypes)
634 goto nla_put_failure;
635 i = 0;
636 stypes = mgmt_stypes[ift].tx;
637 while (stypes) {
638 if (stypes & 1)
639 NLA_PUT_U16(msg, NL80211_ATTR_FRAME_TYPE,
640 (i << 4) | IEEE80211_FTYPE_MGMT);
641 stypes >>= 1;
642 i++;
643 }
644 nla_nest_end(msg, nl_ftypes);
645 }
646
Johannes Berg74b70a42010-08-24 12:15:53 +0200647 nla_nest_end(msg, nl_ifs);
648
Johannes Berg2e161f72010-08-12 15:38:38 +0200649 nl_ifs = nla_nest_start(msg, NL80211_ATTR_RX_FRAME_TYPES);
650 if (!nl_ifs)
651 goto nla_put_failure;
652
653 for (ift = 0; ift < NUM_NL80211_IFTYPES; ift++) {
654 nl_ftypes = nla_nest_start(msg, ift);
655 if (!nl_ftypes)
656 goto nla_put_failure;
657 i = 0;
658 stypes = mgmt_stypes[ift].rx;
659 while (stypes) {
660 if (stypes & 1)
661 NLA_PUT_U16(msg, NL80211_ATTR_FRAME_TYPE,
662 (i << 4) | IEEE80211_FTYPE_MGMT);
663 stypes >>= 1;
664 i++;
665 }
666 nla_nest_end(msg, nl_ftypes);
667 }
668 nla_nest_end(msg, nl_ifs);
669 }
670
Johannes Berg55682962007-09-20 13:09:35 -0400671 return genlmsg_end(msg, hdr);
672
673 nla_put_failure:
Thomas Grafbc3ed282008-06-03 16:36:54 -0700674 genlmsg_cancel(msg, hdr);
675 return -EMSGSIZE;
Johannes Berg55682962007-09-20 13:09:35 -0400676}
677
678static int nl80211_dump_wiphy(struct sk_buff *skb, struct netlink_callback *cb)
679{
680 int idx = 0;
681 int start = cb->args[0];
682 struct cfg80211_registered_device *dev;
683
Luis R. Rodrigueza1794392009-02-21 00:04:21 -0500684 mutex_lock(&cfg80211_mutex);
Johannes Berg79c97e92009-07-07 03:56:12 +0200685 list_for_each_entry(dev, &cfg80211_rdev_list, list) {
Johannes Berg463d0182009-07-14 00:33:35 +0200686 if (!net_eq(wiphy_net(&dev->wiphy), sock_net(skb->sk)))
687 continue;
Julius Volzb4637272008-07-08 14:02:19 +0200688 if (++idx <= start)
Johannes Berg55682962007-09-20 13:09:35 -0400689 continue;
690 if (nl80211_send_wiphy(skb, NETLINK_CB(cb->skb).pid,
691 cb->nlh->nlmsg_seq, NLM_F_MULTI,
Julius Volzb4637272008-07-08 14:02:19 +0200692 dev) < 0) {
693 idx--;
Johannes Berg55682962007-09-20 13:09:35 -0400694 break;
Julius Volzb4637272008-07-08 14:02:19 +0200695 }
Johannes Berg55682962007-09-20 13:09:35 -0400696 }
Luis R. Rodrigueza1794392009-02-21 00:04:21 -0500697 mutex_unlock(&cfg80211_mutex);
Johannes Berg55682962007-09-20 13:09:35 -0400698
699 cb->args[0] = idx;
700
701 return skb->len;
702}
703
704static int nl80211_get_wiphy(struct sk_buff *skb, struct genl_info *info)
705{
706 struct sk_buff *msg;
707 struct cfg80211_registered_device *dev;
708
709 dev = cfg80211_get_dev_from_info(info);
710 if (IS_ERR(dev))
711 return PTR_ERR(dev);
712
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)
715 goto out_err;
716
717 if (nl80211_send_wiphy(msg, info->snd_pid, info->snd_seq, 0, dev) < 0)
718 goto out_free;
719
Johannes Berg4d0c8ae2009-07-07 03:56:09 +0200720 cfg80211_unlock_rdev(dev);
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 out_free:
725 nlmsg_free(msg);
726 out_err:
Johannes Berg4d0c8ae2009-07-07 03:56:09 +0200727 cfg80211_unlock_rdev(dev);
Johannes Berg55682962007-09-20 13:09:35 -0400728 return -ENOBUFS;
729}
730
Jouni Malinen31888482008-10-30 16:59:24 +0200731static const struct nla_policy txq_params_policy[NL80211_TXQ_ATTR_MAX + 1] = {
732 [NL80211_TXQ_ATTR_QUEUE] = { .type = NLA_U8 },
733 [NL80211_TXQ_ATTR_TXOP] = { .type = NLA_U16 },
734 [NL80211_TXQ_ATTR_CWMIN] = { .type = NLA_U16 },
735 [NL80211_TXQ_ATTR_CWMAX] = { .type = NLA_U16 },
736 [NL80211_TXQ_ATTR_AIFS] = { .type = NLA_U8 },
737};
738
739static int parse_txq_params(struct nlattr *tb[],
740 struct ieee80211_txq_params *txq_params)
741{
742 if (!tb[NL80211_TXQ_ATTR_QUEUE] || !tb[NL80211_TXQ_ATTR_TXOP] ||
743 !tb[NL80211_TXQ_ATTR_CWMIN] || !tb[NL80211_TXQ_ATTR_CWMAX] ||
744 !tb[NL80211_TXQ_ATTR_AIFS])
745 return -EINVAL;
746
747 txq_params->queue = nla_get_u8(tb[NL80211_TXQ_ATTR_QUEUE]);
748 txq_params->txop = nla_get_u16(tb[NL80211_TXQ_ATTR_TXOP]);
749 txq_params->cwmin = nla_get_u16(tb[NL80211_TXQ_ATTR_CWMIN]);
750 txq_params->cwmax = nla_get_u16(tb[NL80211_TXQ_ATTR_CWMAX]);
751 txq_params->aifs = nla_get_u8(tb[NL80211_TXQ_ATTR_AIFS]);
752
753 return 0;
754}
755
Johannes Bergf444de02010-05-05 15:25:02 +0200756static bool nl80211_can_set_dev_channel(struct wireless_dev *wdev)
757{
758 /*
759 * You can only set the channel explicitly for AP, mesh
760 * and WDS type interfaces; all others have their channel
761 * managed via their respective "establish a connection"
762 * command (connect, join, ...)
763 *
764 * Monitors are special as they are normally slaved to
765 * whatever else is going on, so they behave as though
766 * you tried setting the wiphy channel itself.
767 */
768 return !wdev ||
769 wdev->iftype == NL80211_IFTYPE_AP ||
770 wdev->iftype == NL80211_IFTYPE_WDS ||
771 wdev->iftype == NL80211_IFTYPE_MESH_POINT ||
Johannes Berg074ac8d2010-09-16 14:58:22 +0200772 wdev->iftype == NL80211_IFTYPE_MONITOR ||
773 wdev->iftype == NL80211_IFTYPE_P2P_GO;
Johannes Bergf444de02010-05-05 15:25:02 +0200774}
775
776static int __nl80211_set_channel(struct cfg80211_registered_device *rdev,
777 struct wireless_dev *wdev,
778 struct genl_info *info)
779{
780 enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT;
781 u32 freq;
782 int result;
783
784 if (!info->attrs[NL80211_ATTR_WIPHY_FREQ])
785 return -EINVAL;
786
787 if (!nl80211_can_set_dev_channel(wdev))
788 return -EOPNOTSUPP;
789
790 if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) {
791 channel_type = nla_get_u32(info->attrs[
792 NL80211_ATTR_WIPHY_CHANNEL_TYPE]);
793 if (channel_type != NL80211_CHAN_NO_HT &&
794 channel_type != NL80211_CHAN_HT20 &&
795 channel_type != NL80211_CHAN_HT40PLUS &&
796 channel_type != NL80211_CHAN_HT40MINUS)
797 return -EINVAL;
798 }
799
800 freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]);
801
802 mutex_lock(&rdev->devlist_mtx);
803 if (wdev) {
804 wdev_lock(wdev);
805 result = cfg80211_set_freq(rdev, wdev, freq, channel_type);
806 wdev_unlock(wdev);
807 } else {
808 result = cfg80211_set_freq(rdev, NULL, freq, channel_type);
809 }
810 mutex_unlock(&rdev->devlist_mtx);
811
812 return result;
813}
814
815static int nl80211_set_channel(struct sk_buff *skb, struct genl_info *info)
816{
817 struct cfg80211_registered_device *rdev;
818 struct net_device *netdev;
819 int result;
820
821 rtnl_lock();
822
823 result = get_rdev_dev_by_info_ifindex(info, &rdev, &netdev);
824 if (result)
Johannes Berg22343622010-09-30 22:17:43 +0200825 goto unlock_rtnl;
Johannes Bergf444de02010-05-05 15:25:02 +0200826
827 result = __nl80211_set_channel(rdev, netdev->ieee80211_ptr, info);
828
Johannes Berg22343622010-09-30 22:17:43 +0200829 dev_put(netdev);
830 cfg80211_unlock_rdev(rdev);
831 unlock_rtnl:
Johannes Bergf444de02010-05-05 15:25:02 +0200832 rtnl_unlock();
833
834 return result;
835}
836
Bill Jordane8347eb2010-10-01 13:54:28 -0400837static int nl80211_set_wds_peer(struct sk_buff *skb, struct genl_info *info)
838{
839 struct cfg80211_registered_device *rdev;
840 struct wireless_dev *wdev;
841 struct net_device *dev;
842 u8 *bssid;
843 int err;
844
845 if (!info->attrs[NL80211_ATTR_MAC])
846 return -EINVAL;
847
848 rtnl_lock();
849
850 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
851 if (err)
852 goto unlock_rtnl;
853
854 wdev = dev->ieee80211_ptr;
855
856 if (netif_running(dev)) {
857 err = -EBUSY;
858 goto out;
859 }
860
861 if (!rdev->ops->set_wds_peer) {
862 err = -EOPNOTSUPP;
863 goto out;
864 }
865
866 if (wdev->iftype != NL80211_IFTYPE_WDS) {
867 err = -EOPNOTSUPP;
868 goto out;
869 }
870
871 bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
872 err = rdev->ops->set_wds_peer(wdev->wiphy, dev, bssid);
873
874out:
875 cfg80211_unlock_rdev(rdev);
876 dev_put(dev);
877unlock_rtnl:
878 rtnl_unlock();
879
880 return err;
881}
882
883
Johannes Berg55682962007-09-20 13:09:35 -0400884static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
885{
886 struct cfg80211_registered_device *rdev;
Johannes Bergf444de02010-05-05 15:25:02 +0200887 struct net_device *netdev = NULL;
888 struct wireless_dev *wdev;
Bill Jordana1e567c2010-09-10 11:22:32 -0400889 int result = 0, rem_txq_params = 0;
Jouni Malinen31888482008-10-30 16:59:24 +0200890 struct nlattr *nl_txq_params;
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +0200891 u32 changed;
892 u8 retry_short = 0, retry_long = 0;
893 u32 frag_threshold = 0, rts_threshold = 0;
Lukáš Turek81077e82009-12-21 22:50:47 +0100894 u8 coverage_class = 0;
Johannes Berg55682962007-09-20 13:09:35 -0400895
Johannes Berg4bbf4d52009-03-24 09:35:46 +0100896 rtnl_lock();
Johannes Berg55682962007-09-20 13:09:35 -0400897
Johannes Bergf444de02010-05-05 15:25:02 +0200898 /*
899 * Try to find the wiphy and netdev. Normally this
900 * function shouldn't need the netdev, but this is
901 * done for backward compatibility -- previously
902 * setting the channel was done per wiphy, but now
903 * it is per netdev. Previous userland like hostapd
904 * also passed a netdev to set_wiphy, so that it is
905 * possible to let that go to the right netdev!
906 */
Johannes Berg4bbf4d52009-03-24 09:35:46 +0100907 mutex_lock(&cfg80211_mutex);
908
Johannes Bergf444de02010-05-05 15:25:02 +0200909 if (info->attrs[NL80211_ATTR_IFINDEX]) {
910 int ifindex = nla_get_u32(info->attrs[NL80211_ATTR_IFINDEX]);
911
912 netdev = dev_get_by_index(genl_info_net(info), ifindex);
913 if (netdev && netdev->ieee80211_ptr) {
914 rdev = wiphy_to_dev(netdev->ieee80211_ptr->wiphy);
915 mutex_lock(&rdev->mtx);
916 } else
917 netdev = NULL;
Johannes Berg4bbf4d52009-03-24 09:35:46 +0100918 }
919
Johannes Bergf444de02010-05-05 15:25:02 +0200920 if (!netdev) {
921 rdev = __cfg80211_rdev_from_info(info);
922 if (IS_ERR(rdev)) {
923 mutex_unlock(&cfg80211_mutex);
924 result = PTR_ERR(rdev);
925 goto unlock;
926 }
927 wdev = NULL;
928 netdev = NULL;
929 result = 0;
930
931 mutex_lock(&rdev->mtx);
932 } else if (netif_running(netdev) &&
933 nl80211_can_set_dev_channel(netdev->ieee80211_ptr))
934 wdev = netdev->ieee80211_ptr;
935 else
936 wdev = NULL;
937
938 /*
939 * end workaround code, by now the rdev is available
940 * and locked, and wdev may or may not be NULL.
941 */
Johannes Berg4bbf4d52009-03-24 09:35:46 +0100942
943 if (info->attrs[NL80211_ATTR_WIPHY_NAME])
Jouni Malinen31888482008-10-30 16:59:24 +0200944 result = cfg80211_dev_rename(
945 rdev, nla_data(info->attrs[NL80211_ATTR_WIPHY_NAME]));
Johannes Berg4bbf4d52009-03-24 09:35:46 +0100946
947 mutex_unlock(&cfg80211_mutex);
948
949 if (result)
950 goto bad_res;
Johannes Berg55682962007-09-20 13:09:35 -0400951
Jouni Malinen31888482008-10-30 16:59:24 +0200952 if (info->attrs[NL80211_ATTR_WIPHY_TXQ_PARAMS]) {
953 struct ieee80211_txq_params txq_params;
954 struct nlattr *tb[NL80211_TXQ_ATTR_MAX + 1];
955
956 if (!rdev->ops->set_txq_params) {
957 result = -EOPNOTSUPP;
958 goto bad_res;
959 }
960
961 nla_for_each_nested(nl_txq_params,
962 info->attrs[NL80211_ATTR_WIPHY_TXQ_PARAMS],
963 rem_txq_params) {
964 nla_parse(tb, NL80211_TXQ_ATTR_MAX,
965 nla_data(nl_txq_params),
966 nla_len(nl_txq_params),
967 txq_params_policy);
968 result = parse_txq_params(tb, &txq_params);
969 if (result)
970 goto bad_res;
971
972 result = rdev->ops->set_txq_params(&rdev->wiphy,
973 &txq_params);
974 if (result)
975 goto bad_res;
976 }
977 }
978
Jouni Malinen72bdcf32008-11-26 16:15:24 +0200979 if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
Johannes Bergf444de02010-05-05 15:25:02 +0200980 result = __nl80211_set_channel(rdev, wdev, info);
Jouni Malinen72bdcf32008-11-26 16:15:24 +0200981 if (result)
982 goto bad_res;
983 }
984
Juuso Oikarinen98d2ff82010-06-23 12:12:38 +0300985 if (info->attrs[NL80211_ATTR_WIPHY_TX_POWER_SETTING]) {
986 enum nl80211_tx_power_setting type;
987 int idx, mbm = 0;
988
989 if (!rdev->ops->set_tx_power) {
Jiri Slaby60ea3852010-07-07 15:02:46 +0200990 result = -EOPNOTSUPP;
Juuso Oikarinen98d2ff82010-06-23 12:12:38 +0300991 goto bad_res;
992 }
993
994 idx = NL80211_ATTR_WIPHY_TX_POWER_SETTING;
995 type = nla_get_u32(info->attrs[idx]);
996
997 if (!info->attrs[NL80211_ATTR_WIPHY_TX_POWER_LEVEL] &&
998 (type != NL80211_TX_POWER_AUTOMATIC)) {
999 result = -EINVAL;
1000 goto bad_res;
1001 }
1002
1003 if (type != NL80211_TX_POWER_AUTOMATIC) {
1004 idx = NL80211_ATTR_WIPHY_TX_POWER_LEVEL;
1005 mbm = nla_get_u32(info->attrs[idx]);
1006 }
1007
1008 result = rdev->ops->set_tx_power(&rdev->wiphy, type, mbm);
1009 if (result)
1010 goto bad_res;
1011 }
1012
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +02001013 changed = 0;
1014
1015 if (info->attrs[NL80211_ATTR_WIPHY_RETRY_SHORT]) {
1016 retry_short = nla_get_u8(
1017 info->attrs[NL80211_ATTR_WIPHY_RETRY_SHORT]);
1018 if (retry_short == 0) {
1019 result = -EINVAL;
1020 goto bad_res;
1021 }
1022 changed |= WIPHY_PARAM_RETRY_SHORT;
1023 }
1024
1025 if (info->attrs[NL80211_ATTR_WIPHY_RETRY_LONG]) {
1026 retry_long = nla_get_u8(
1027 info->attrs[NL80211_ATTR_WIPHY_RETRY_LONG]);
1028 if (retry_long == 0) {
1029 result = -EINVAL;
1030 goto bad_res;
1031 }
1032 changed |= WIPHY_PARAM_RETRY_LONG;
1033 }
1034
1035 if (info->attrs[NL80211_ATTR_WIPHY_FRAG_THRESHOLD]) {
1036 frag_threshold = nla_get_u32(
1037 info->attrs[NL80211_ATTR_WIPHY_FRAG_THRESHOLD]);
1038 if (frag_threshold < 256) {
1039 result = -EINVAL;
1040 goto bad_res;
1041 }
1042 if (frag_threshold != (u32) -1) {
1043 /*
1044 * Fragments (apart from the last one) are required to
1045 * have even length. Make the fragmentation code
1046 * simpler by stripping LSB should someone try to use
1047 * odd threshold value.
1048 */
1049 frag_threshold &= ~0x1;
1050 }
1051 changed |= WIPHY_PARAM_FRAG_THRESHOLD;
1052 }
1053
1054 if (info->attrs[NL80211_ATTR_WIPHY_RTS_THRESHOLD]) {
1055 rts_threshold = nla_get_u32(
1056 info->attrs[NL80211_ATTR_WIPHY_RTS_THRESHOLD]);
1057 changed |= WIPHY_PARAM_RTS_THRESHOLD;
1058 }
1059
Lukáš Turek81077e82009-12-21 22:50:47 +01001060 if (info->attrs[NL80211_ATTR_WIPHY_COVERAGE_CLASS]) {
1061 coverage_class = nla_get_u8(
1062 info->attrs[NL80211_ATTR_WIPHY_COVERAGE_CLASS]);
1063 changed |= WIPHY_PARAM_COVERAGE_CLASS;
1064 }
1065
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +02001066 if (changed) {
1067 u8 old_retry_short, old_retry_long;
1068 u32 old_frag_threshold, old_rts_threshold;
Lukáš Turek81077e82009-12-21 22:50:47 +01001069 u8 old_coverage_class;
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +02001070
1071 if (!rdev->ops->set_wiphy_params) {
1072 result = -EOPNOTSUPP;
1073 goto bad_res;
1074 }
1075
1076 old_retry_short = rdev->wiphy.retry_short;
1077 old_retry_long = rdev->wiphy.retry_long;
1078 old_frag_threshold = rdev->wiphy.frag_threshold;
1079 old_rts_threshold = rdev->wiphy.rts_threshold;
Lukáš Turek81077e82009-12-21 22:50:47 +01001080 old_coverage_class = rdev->wiphy.coverage_class;
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +02001081
1082 if (changed & WIPHY_PARAM_RETRY_SHORT)
1083 rdev->wiphy.retry_short = retry_short;
1084 if (changed & WIPHY_PARAM_RETRY_LONG)
1085 rdev->wiphy.retry_long = retry_long;
1086 if (changed & WIPHY_PARAM_FRAG_THRESHOLD)
1087 rdev->wiphy.frag_threshold = frag_threshold;
1088 if (changed & WIPHY_PARAM_RTS_THRESHOLD)
1089 rdev->wiphy.rts_threshold = rts_threshold;
Lukáš Turek81077e82009-12-21 22:50:47 +01001090 if (changed & WIPHY_PARAM_COVERAGE_CLASS)
1091 rdev->wiphy.coverage_class = coverage_class;
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +02001092
1093 result = rdev->ops->set_wiphy_params(&rdev->wiphy, changed);
1094 if (result) {
1095 rdev->wiphy.retry_short = old_retry_short;
1096 rdev->wiphy.retry_long = old_retry_long;
1097 rdev->wiphy.frag_threshold = old_frag_threshold;
1098 rdev->wiphy.rts_threshold = old_rts_threshold;
Lukáš Turek81077e82009-12-21 22:50:47 +01001099 rdev->wiphy.coverage_class = old_coverage_class;
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +02001100 }
1101 }
Jouni Malinen72bdcf32008-11-26 16:15:24 +02001102
Johannes Berg306d6112008-12-08 12:39:04 +01001103 bad_res:
Johannes Berg4bbf4d52009-03-24 09:35:46 +01001104 mutex_unlock(&rdev->mtx);
Johannes Bergf444de02010-05-05 15:25:02 +02001105 if (netdev)
1106 dev_put(netdev);
Johannes Berg4bbf4d52009-03-24 09:35:46 +01001107 unlock:
1108 rtnl_unlock();
Johannes Berg55682962007-09-20 13:09:35 -04001109 return result;
1110}
1111
1112
1113static int nl80211_send_iface(struct sk_buff *msg, u32 pid, u32 seq, int flags,
Johannes Bergd7264052009-04-19 16:23:20 +02001114 struct cfg80211_registered_device *rdev,
Johannes Berg55682962007-09-20 13:09:35 -04001115 struct net_device *dev)
1116{
1117 void *hdr;
1118
1119 hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_INTERFACE);
1120 if (!hdr)
1121 return -1;
1122
1123 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
Johannes Bergd7264052009-04-19 16:23:20 +02001124 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
Johannes Berg55682962007-09-20 13:09:35 -04001125 NLA_PUT_STRING(msg, NL80211_ATTR_IFNAME, dev->name);
Johannes Berg60719ff2008-09-16 14:55:09 +02001126 NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, dev->ieee80211_ptr->iftype);
Johannes Bergf5ea9122009-08-07 16:17:38 +02001127
1128 NLA_PUT_U32(msg, NL80211_ATTR_GENERATION,
1129 rdev->devlist_generation ^
1130 (cfg80211_rdev_list_generation << 2));
1131
Johannes Berg55682962007-09-20 13:09:35 -04001132 return genlmsg_end(msg, hdr);
1133
1134 nla_put_failure:
Thomas Grafbc3ed282008-06-03 16:36:54 -07001135 genlmsg_cancel(msg, hdr);
1136 return -EMSGSIZE;
Johannes Berg55682962007-09-20 13:09:35 -04001137}
1138
1139static int nl80211_dump_interface(struct sk_buff *skb, struct netlink_callback *cb)
1140{
1141 int wp_idx = 0;
1142 int if_idx = 0;
1143 int wp_start = cb->args[0];
1144 int if_start = cb->args[1];
Johannes Bergf5ea9122009-08-07 16:17:38 +02001145 struct cfg80211_registered_device *rdev;
Johannes Berg55682962007-09-20 13:09:35 -04001146 struct wireless_dev *wdev;
1147
Luis R. Rodrigueza1794392009-02-21 00:04:21 -05001148 mutex_lock(&cfg80211_mutex);
Johannes Bergf5ea9122009-08-07 16:17:38 +02001149 list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
1150 if (!net_eq(wiphy_net(&rdev->wiphy), sock_net(skb->sk)))
Johannes Berg463d0182009-07-14 00:33:35 +02001151 continue;
Johannes Bergbba95fe2008-07-29 13:22:51 +02001152 if (wp_idx < wp_start) {
1153 wp_idx++;
Johannes Berg55682962007-09-20 13:09:35 -04001154 continue;
Johannes Bergbba95fe2008-07-29 13:22:51 +02001155 }
Johannes Berg55682962007-09-20 13:09:35 -04001156 if_idx = 0;
1157
Johannes Bergf5ea9122009-08-07 16:17:38 +02001158 mutex_lock(&rdev->devlist_mtx);
1159 list_for_each_entry(wdev, &rdev->netdev_list, list) {
Johannes Bergbba95fe2008-07-29 13:22:51 +02001160 if (if_idx < if_start) {
1161 if_idx++;
Johannes Berg55682962007-09-20 13:09:35 -04001162 continue;
Johannes Bergbba95fe2008-07-29 13:22:51 +02001163 }
Johannes Berg55682962007-09-20 13:09:35 -04001164 if (nl80211_send_iface(skb, NETLINK_CB(cb->skb).pid,
1165 cb->nlh->nlmsg_seq, NLM_F_MULTI,
Johannes Bergf5ea9122009-08-07 16:17:38 +02001166 rdev, wdev->netdev) < 0) {
1167 mutex_unlock(&rdev->devlist_mtx);
Johannes Bergbba95fe2008-07-29 13:22:51 +02001168 goto out;
1169 }
1170 if_idx++;
Johannes Berg55682962007-09-20 13:09:35 -04001171 }
Johannes Bergf5ea9122009-08-07 16:17:38 +02001172 mutex_unlock(&rdev->devlist_mtx);
Johannes Bergbba95fe2008-07-29 13:22:51 +02001173
1174 wp_idx++;
Johannes Berg55682962007-09-20 13:09:35 -04001175 }
Johannes Bergbba95fe2008-07-29 13:22:51 +02001176 out:
Luis R. Rodrigueza1794392009-02-21 00:04:21 -05001177 mutex_unlock(&cfg80211_mutex);
Johannes Berg55682962007-09-20 13:09:35 -04001178
1179 cb->args[0] = wp_idx;
1180 cb->args[1] = if_idx;
1181
1182 return skb->len;
1183}
1184
1185static int nl80211_get_interface(struct sk_buff *skb, struct genl_info *info)
1186{
1187 struct sk_buff *msg;
1188 struct cfg80211_registered_device *dev;
1189 struct net_device *netdev;
1190 int err;
1191
Johannes Berg463d0182009-07-14 00:33:35 +02001192 err = get_rdev_dev_by_info_ifindex(info, &dev, &netdev);
Johannes Berg55682962007-09-20 13:09:35 -04001193 if (err)
1194 return err;
1195
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07001196 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg55682962007-09-20 13:09:35 -04001197 if (!msg)
1198 goto out_err;
1199
Johannes Bergd7264052009-04-19 16:23:20 +02001200 if (nl80211_send_iface(msg, info->snd_pid, info->snd_seq, 0,
1201 dev, netdev) < 0)
Johannes Berg55682962007-09-20 13:09:35 -04001202 goto out_free;
1203
1204 dev_put(netdev);
Johannes Berg4d0c8ae2009-07-07 03:56:09 +02001205 cfg80211_unlock_rdev(dev);
Johannes Berg55682962007-09-20 13:09:35 -04001206
Johannes Berg134e6372009-07-10 09:51:34 +00001207 return genlmsg_reply(msg, info);
Johannes Berg55682962007-09-20 13:09:35 -04001208
1209 out_free:
1210 nlmsg_free(msg);
1211 out_err:
1212 dev_put(netdev);
Johannes Berg4d0c8ae2009-07-07 03:56:09 +02001213 cfg80211_unlock_rdev(dev);
Johannes Berg55682962007-09-20 13:09:35 -04001214 return -ENOBUFS;
1215}
1216
Michael Wu66f7ac52008-01-31 19:48:22 +01001217static const struct nla_policy mntr_flags_policy[NL80211_MNTR_FLAG_MAX + 1] = {
1218 [NL80211_MNTR_FLAG_FCSFAIL] = { .type = NLA_FLAG },
1219 [NL80211_MNTR_FLAG_PLCPFAIL] = { .type = NLA_FLAG },
1220 [NL80211_MNTR_FLAG_CONTROL] = { .type = NLA_FLAG },
1221 [NL80211_MNTR_FLAG_OTHER_BSS] = { .type = NLA_FLAG },
1222 [NL80211_MNTR_FLAG_COOK_FRAMES] = { .type = NLA_FLAG },
1223};
1224
1225static int parse_monitor_flags(struct nlattr *nla, u32 *mntrflags)
1226{
1227 struct nlattr *flags[NL80211_MNTR_FLAG_MAX + 1];
1228 int flag;
1229
1230 *mntrflags = 0;
1231
1232 if (!nla)
1233 return -EINVAL;
1234
1235 if (nla_parse_nested(flags, NL80211_MNTR_FLAG_MAX,
1236 nla, mntr_flags_policy))
1237 return -EINVAL;
1238
1239 for (flag = 1; flag <= NL80211_MNTR_FLAG_MAX; flag++)
1240 if (flags[flag])
1241 *mntrflags |= (1<<flag);
1242
1243 return 0;
1244}
1245
Johannes Berg9bc383d2009-11-19 11:55:19 +01001246static int nl80211_valid_4addr(struct cfg80211_registered_device *rdev,
Johannes Bergad4bb6f2009-11-19 00:56:30 +01001247 struct net_device *netdev, u8 use_4addr,
1248 enum nl80211_iftype iftype)
Johannes Berg9bc383d2009-11-19 11:55:19 +01001249{
Johannes Bergad4bb6f2009-11-19 00:56:30 +01001250 if (!use_4addr) {
Jiri Pirkof350a0a82010-06-15 06:50:45 +00001251 if (netdev && (netdev->priv_flags & IFF_BRIDGE_PORT))
Johannes Bergad4bb6f2009-11-19 00:56:30 +01001252 return -EBUSY;
Johannes Berg9bc383d2009-11-19 11:55:19 +01001253 return 0;
Johannes Bergad4bb6f2009-11-19 00:56:30 +01001254 }
Johannes Berg9bc383d2009-11-19 11:55:19 +01001255
1256 switch (iftype) {
1257 case NL80211_IFTYPE_AP_VLAN:
1258 if (rdev->wiphy.flags & WIPHY_FLAG_4ADDR_AP)
1259 return 0;
1260 break;
1261 case NL80211_IFTYPE_STATION:
1262 if (rdev->wiphy.flags & WIPHY_FLAG_4ADDR_STATION)
1263 return 0;
1264 break;
1265 default:
1266 break;
1267 }
1268
1269 return -EOPNOTSUPP;
1270}
1271
Johannes Berg55682962007-09-20 13:09:35 -04001272static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info)
1273{
Johannes Berg79c97e92009-07-07 03:56:12 +02001274 struct cfg80211_registered_device *rdev;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001275 struct vif_params params;
Johannes Berge36d56b2009-06-09 21:04:43 +02001276 int err;
Johannes Berg04a773a2009-04-19 21:24:32 +02001277 enum nl80211_iftype otype, ntype;
Johannes Berg55682962007-09-20 13:09:35 -04001278 struct net_device *dev;
Johannes Berg92ffe052008-09-16 20:39:36 +02001279 u32 _flags, *flags = NULL;
Johannes Bergac7f9cf2009-03-21 17:07:59 +01001280 bool change = false;
Johannes Berg55682962007-09-20 13:09:35 -04001281
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001282 memset(&params, 0, sizeof(params));
1283
Johannes Berg3b858752009-03-12 09:55:09 +01001284 rtnl_lock();
1285
Johannes Berg463d0182009-07-14 00:33:35 +02001286 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Johannes Berg55682962007-09-20 13:09:35 -04001287 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001288 goto unlock_rtnl;
1289
Johannes Berg04a773a2009-04-19 21:24:32 +02001290 otype = ntype = dev->ieee80211_ptr->iftype;
Johannes Berg55682962007-09-20 13:09:35 -04001291
Johannes Berg723b0382008-09-16 20:22:09 +02001292 if (info->attrs[NL80211_ATTR_IFTYPE]) {
Johannes Bergac7f9cf2009-03-21 17:07:59 +01001293 ntype = nla_get_u32(info->attrs[NL80211_ATTR_IFTYPE]);
Johannes Berg04a773a2009-04-19 21:24:32 +02001294 if (otype != ntype)
Johannes Bergac7f9cf2009-03-21 17:07:59 +01001295 change = true;
Johannes Berg04a773a2009-04-19 21:24:32 +02001296 if (ntype > NL80211_IFTYPE_MAX) {
Johannes Bergac7f9cf2009-03-21 17:07:59 +01001297 err = -EINVAL;
Johannes Berg723b0382008-09-16 20:22:09 +02001298 goto unlock;
Johannes Bergac7f9cf2009-03-21 17:07:59 +01001299 }
Johannes Berg723b0382008-09-16 20:22:09 +02001300 }
1301
Johannes Berg92ffe052008-09-16 20:39:36 +02001302 if (info->attrs[NL80211_ATTR_MESH_ID]) {
Johannes Berg04a773a2009-04-19 21:24:32 +02001303 if (ntype != NL80211_IFTYPE_MESH_POINT) {
Johannes Berg92ffe052008-09-16 20:39:36 +02001304 err = -EINVAL;
1305 goto unlock;
1306 }
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001307 params.mesh_id = nla_data(info->attrs[NL80211_ATTR_MESH_ID]);
1308 params.mesh_id_len = nla_len(info->attrs[NL80211_ATTR_MESH_ID]);
Johannes Bergac7f9cf2009-03-21 17:07:59 +01001309 change = true;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001310 }
1311
Felix Fietkau8b787642009-11-10 18:53:10 +01001312 if (info->attrs[NL80211_ATTR_4ADDR]) {
1313 params.use_4addr = !!nla_get_u8(info->attrs[NL80211_ATTR_4ADDR]);
1314 change = true;
Johannes Bergad4bb6f2009-11-19 00:56:30 +01001315 err = nl80211_valid_4addr(rdev, dev, params.use_4addr, ntype);
Johannes Berg9bc383d2009-11-19 11:55:19 +01001316 if (err)
1317 goto unlock;
Felix Fietkau8b787642009-11-10 18:53:10 +01001318 } else {
1319 params.use_4addr = -1;
1320 }
1321
Johannes Berg92ffe052008-09-16 20:39:36 +02001322 if (info->attrs[NL80211_ATTR_MNTR_FLAGS]) {
Johannes Berg04a773a2009-04-19 21:24:32 +02001323 if (ntype != NL80211_IFTYPE_MONITOR) {
Johannes Berg92ffe052008-09-16 20:39:36 +02001324 err = -EINVAL;
1325 goto unlock;
1326 }
1327 err = parse_monitor_flags(info->attrs[NL80211_ATTR_MNTR_FLAGS],
1328 &_flags);
Johannes Bergac7f9cf2009-03-21 17:07:59 +01001329 if (err)
1330 goto unlock;
1331
1332 flags = &_flags;
1333 change = true;
Johannes Berg92ffe052008-09-16 20:39:36 +02001334 }
Johannes Berg3b858752009-03-12 09:55:09 +01001335
Johannes Bergac7f9cf2009-03-21 17:07:59 +01001336 if (change)
Johannes Berg3d54d252009-08-21 14:51:05 +02001337 err = cfg80211_change_iface(rdev, dev, ntype, flags, &params);
Johannes Bergac7f9cf2009-03-21 17:07:59 +01001338 else
1339 err = 0;
Johannes Berg60719ff2008-09-16 14:55:09 +02001340
Johannes Berg9bc383d2009-11-19 11:55:19 +01001341 if (!err && params.use_4addr != -1)
1342 dev->ieee80211_ptr->use_4addr = params.use_4addr;
1343
Johannes Berg55682962007-09-20 13:09:35 -04001344 unlock:
Johannes Berge36d56b2009-06-09 21:04:43 +02001345 dev_put(dev);
Johannes Berg79c97e92009-07-07 03:56:12 +02001346 cfg80211_unlock_rdev(rdev);
Johannes Berg3b858752009-03-12 09:55:09 +01001347 unlock_rtnl:
1348 rtnl_unlock();
Johannes Berg55682962007-09-20 13:09:35 -04001349 return err;
1350}
1351
1352static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info)
1353{
Johannes Berg79c97e92009-07-07 03:56:12 +02001354 struct cfg80211_registered_device *rdev;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001355 struct vif_params params;
Johannes Berg55682962007-09-20 13:09:35 -04001356 int err;
1357 enum nl80211_iftype type = NL80211_IFTYPE_UNSPECIFIED;
Michael Wu66f7ac52008-01-31 19:48:22 +01001358 u32 flags;
Johannes Berg55682962007-09-20 13:09:35 -04001359
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001360 memset(&params, 0, sizeof(params));
1361
Johannes Berg55682962007-09-20 13:09:35 -04001362 if (!info->attrs[NL80211_ATTR_IFNAME])
1363 return -EINVAL;
1364
1365 if (info->attrs[NL80211_ATTR_IFTYPE]) {
1366 type = nla_get_u32(info->attrs[NL80211_ATTR_IFTYPE]);
1367 if (type > NL80211_IFTYPE_MAX)
1368 return -EINVAL;
1369 }
1370
Johannes Berg3b858752009-03-12 09:55:09 +01001371 rtnl_lock();
1372
Johannes Berg79c97e92009-07-07 03:56:12 +02001373 rdev = cfg80211_get_dev_from_info(info);
1374 if (IS_ERR(rdev)) {
1375 err = PTR_ERR(rdev);
Johannes Berg3b858752009-03-12 09:55:09 +01001376 goto unlock_rtnl;
1377 }
Johannes Berg55682962007-09-20 13:09:35 -04001378
Johannes Berg79c97e92009-07-07 03:56:12 +02001379 if (!rdev->ops->add_virtual_intf ||
1380 !(rdev->wiphy.interface_modes & (1 << type))) {
Johannes Berg55682962007-09-20 13:09:35 -04001381 err = -EOPNOTSUPP;
1382 goto unlock;
1383 }
1384
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001385 if (type == NL80211_IFTYPE_MESH_POINT &&
1386 info->attrs[NL80211_ATTR_MESH_ID]) {
1387 params.mesh_id = nla_data(info->attrs[NL80211_ATTR_MESH_ID]);
1388 params.mesh_id_len = nla_len(info->attrs[NL80211_ATTR_MESH_ID]);
1389 }
1390
Johannes Berg9bc383d2009-11-19 11:55:19 +01001391 if (info->attrs[NL80211_ATTR_4ADDR]) {
Felix Fietkau8b787642009-11-10 18:53:10 +01001392 params.use_4addr = !!nla_get_u8(info->attrs[NL80211_ATTR_4ADDR]);
Johannes Bergad4bb6f2009-11-19 00:56:30 +01001393 err = nl80211_valid_4addr(rdev, NULL, params.use_4addr, type);
Johannes Berg9bc383d2009-11-19 11:55:19 +01001394 if (err)
1395 goto unlock;
1396 }
Felix Fietkau8b787642009-11-10 18:53:10 +01001397
Michael Wu66f7ac52008-01-31 19:48:22 +01001398 err = parse_monitor_flags(type == NL80211_IFTYPE_MONITOR ?
1399 info->attrs[NL80211_ATTR_MNTR_FLAGS] : NULL,
1400 &flags);
Johannes Berg79c97e92009-07-07 03:56:12 +02001401 err = rdev->ops->add_virtual_intf(&rdev->wiphy,
Michael Wu66f7ac52008-01-31 19:48:22 +01001402 nla_data(info->attrs[NL80211_ATTR_IFNAME]),
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001403 type, err ? NULL : &flags, &params);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001404
Johannes Berg55682962007-09-20 13:09:35 -04001405 unlock:
Johannes Berg79c97e92009-07-07 03:56:12 +02001406 cfg80211_unlock_rdev(rdev);
Johannes Berg3b858752009-03-12 09:55:09 +01001407 unlock_rtnl:
1408 rtnl_unlock();
Johannes Berg55682962007-09-20 13:09:35 -04001409 return err;
1410}
1411
1412static int nl80211_del_interface(struct sk_buff *skb, struct genl_info *info)
1413{
Johannes Berg79c97e92009-07-07 03:56:12 +02001414 struct cfg80211_registered_device *rdev;
Johannes Berg463d0182009-07-14 00:33:35 +02001415 int err;
Johannes Berg55682962007-09-20 13:09:35 -04001416 struct net_device *dev;
1417
Johannes Berg3b858752009-03-12 09:55:09 +01001418 rtnl_lock();
1419
Johannes Berg463d0182009-07-14 00:33:35 +02001420 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Johannes Berg55682962007-09-20 13:09:35 -04001421 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001422 goto unlock_rtnl;
Johannes Berg55682962007-09-20 13:09:35 -04001423
Johannes Berg79c97e92009-07-07 03:56:12 +02001424 if (!rdev->ops->del_virtual_intf) {
Johannes Berg55682962007-09-20 13:09:35 -04001425 err = -EOPNOTSUPP;
1426 goto out;
1427 }
1428
Johannes Berg463d0182009-07-14 00:33:35 +02001429 err = rdev->ops->del_virtual_intf(&rdev->wiphy, dev);
Johannes Berg55682962007-09-20 13:09:35 -04001430
1431 out:
Johannes Berg79c97e92009-07-07 03:56:12 +02001432 cfg80211_unlock_rdev(rdev);
Johannes Berg463d0182009-07-14 00:33:35 +02001433 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001434 unlock_rtnl:
1435 rtnl_unlock();
Johannes Berg55682962007-09-20 13:09:35 -04001436 return err;
1437}
1438
Johannes Berg41ade002007-12-19 02:03:29 +01001439struct get_key_cookie {
1440 struct sk_buff *msg;
1441 int error;
Johannes Bergb9454e82009-07-08 13:29:08 +02001442 int idx;
Johannes Berg41ade002007-12-19 02:03:29 +01001443};
1444
1445static void get_key_callback(void *c, struct key_params *params)
1446{
Johannes Bergb9454e82009-07-08 13:29:08 +02001447 struct nlattr *key;
Johannes Berg41ade002007-12-19 02:03:29 +01001448 struct get_key_cookie *cookie = c;
1449
1450 if (params->key)
1451 NLA_PUT(cookie->msg, NL80211_ATTR_KEY_DATA,
1452 params->key_len, params->key);
1453
1454 if (params->seq)
1455 NLA_PUT(cookie->msg, NL80211_ATTR_KEY_SEQ,
1456 params->seq_len, params->seq);
1457
1458 if (params->cipher)
1459 NLA_PUT_U32(cookie->msg, NL80211_ATTR_KEY_CIPHER,
1460 params->cipher);
1461
Johannes Bergb9454e82009-07-08 13:29:08 +02001462 key = nla_nest_start(cookie->msg, NL80211_ATTR_KEY);
1463 if (!key)
1464 goto nla_put_failure;
1465
1466 if (params->key)
1467 NLA_PUT(cookie->msg, NL80211_KEY_DATA,
1468 params->key_len, params->key);
1469
1470 if (params->seq)
1471 NLA_PUT(cookie->msg, NL80211_KEY_SEQ,
1472 params->seq_len, params->seq);
1473
1474 if (params->cipher)
1475 NLA_PUT_U32(cookie->msg, NL80211_KEY_CIPHER,
1476 params->cipher);
1477
1478 NLA_PUT_U8(cookie->msg, NL80211_ATTR_KEY_IDX, cookie->idx);
1479
1480 nla_nest_end(cookie->msg, key);
1481
Johannes Berg41ade002007-12-19 02:03:29 +01001482 return;
1483 nla_put_failure:
1484 cookie->error = 1;
1485}
1486
1487static int nl80211_get_key(struct sk_buff *skb, struct genl_info *info)
1488{
Johannes Berg79c97e92009-07-07 03:56:12 +02001489 struct cfg80211_registered_device *rdev;
Johannes Berg41ade002007-12-19 02:03:29 +01001490 int err;
1491 struct net_device *dev;
1492 u8 key_idx = 0;
1493 u8 *mac_addr = NULL;
1494 struct get_key_cookie cookie = {
1495 .error = 0,
1496 };
1497 void *hdr;
1498 struct sk_buff *msg;
1499
1500 if (info->attrs[NL80211_ATTR_KEY_IDX])
1501 key_idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]);
1502
Jouni Malinen3cfcf6ac2009-01-08 13:32:02 +02001503 if (key_idx > 5)
Johannes Berg41ade002007-12-19 02:03:29 +01001504 return -EINVAL;
1505
1506 if (info->attrs[NL80211_ATTR_MAC])
1507 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
1508
Johannes Berg3b858752009-03-12 09:55:09 +01001509 rtnl_lock();
1510
Johannes Berg463d0182009-07-14 00:33:35 +02001511 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Johannes Berg41ade002007-12-19 02:03:29 +01001512 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001513 goto unlock_rtnl;
Johannes Berg41ade002007-12-19 02:03:29 +01001514
Johannes Berg79c97e92009-07-07 03:56:12 +02001515 if (!rdev->ops->get_key) {
Johannes Berg41ade002007-12-19 02:03:29 +01001516 err = -EOPNOTSUPP;
1517 goto out;
1518 }
1519
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07001520 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg41ade002007-12-19 02:03:29 +01001521 if (!msg) {
1522 err = -ENOMEM;
1523 goto out;
1524 }
1525
1526 hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
1527 NL80211_CMD_NEW_KEY);
1528
1529 if (IS_ERR(hdr)) {
1530 err = PTR_ERR(hdr);
Niko Jokinen6c95e2a2009-07-15 11:00:53 +03001531 goto free_msg;
Johannes Berg41ade002007-12-19 02:03:29 +01001532 }
1533
1534 cookie.msg = msg;
Johannes Bergb9454e82009-07-08 13:29:08 +02001535 cookie.idx = key_idx;
Johannes Berg41ade002007-12-19 02:03:29 +01001536
1537 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
1538 NLA_PUT_U8(msg, NL80211_ATTR_KEY_IDX, key_idx);
1539 if (mac_addr)
1540 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr);
1541
Johannes Berg79c97e92009-07-07 03:56:12 +02001542 err = rdev->ops->get_key(&rdev->wiphy, dev, key_idx, mac_addr,
Johannes Berg41ade002007-12-19 02:03:29 +01001543 &cookie, get_key_callback);
Johannes Berg41ade002007-12-19 02:03:29 +01001544
1545 if (err)
Niko Jokinen6c95e2a2009-07-15 11:00:53 +03001546 goto free_msg;
Johannes Berg41ade002007-12-19 02:03:29 +01001547
1548 if (cookie.error)
1549 goto nla_put_failure;
1550
1551 genlmsg_end(msg, hdr);
Johannes Berg134e6372009-07-10 09:51:34 +00001552 err = genlmsg_reply(msg, info);
Johannes Berg41ade002007-12-19 02:03:29 +01001553 goto out;
1554
1555 nla_put_failure:
1556 err = -ENOBUFS;
Niko Jokinen6c95e2a2009-07-15 11:00:53 +03001557 free_msg:
Johannes Berg41ade002007-12-19 02:03:29 +01001558 nlmsg_free(msg);
1559 out:
Johannes Berg79c97e92009-07-07 03:56:12 +02001560 cfg80211_unlock_rdev(rdev);
Johannes Berg41ade002007-12-19 02:03:29 +01001561 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001562 unlock_rtnl:
1563 rtnl_unlock();
1564
Johannes Berg41ade002007-12-19 02:03:29 +01001565 return err;
1566}
1567
1568static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info)
1569{
Johannes Berg79c97e92009-07-07 03:56:12 +02001570 struct cfg80211_registered_device *rdev;
Johannes Bergb9454e82009-07-08 13:29:08 +02001571 struct key_parse key;
Johannes Berg41ade002007-12-19 02:03:29 +01001572 int err;
1573 struct net_device *dev;
Jouni Malinen3cfcf6ac2009-01-08 13:32:02 +02001574 int (*func)(struct wiphy *wiphy, struct net_device *netdev,
1575 u8 key_index);
Johannes Berg41ade002007-12-19 02:03:29 +01001576
Johannes Bergb9454e82009-07-08 13:29:08 +02001577 err = nl80211_parse_key(info, &key);
1578 if (err)
1579 return err;
1580
1581 if (key.idx < 0)
Johannes Berg41ade002007-12-19 02:03:29 +01001582 return -EINVAL;
1583
Johannes Bergb9454e82009-07-08 13:29:08 +02001584 /* only support setting default key */
1585 if (!key.def && !key.defmgmt)
Johannes Berg41ade002007-12-19 02:03:29 +01001586 return -EINVAL;
1587
Johannes Berg3b858752009-03-12 09:55:09 +01001588 rtnl_lock();
1589
Johannes Berg463d0182009-07-14 00:33:35 +02001590 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Johannes Berg41ade002007-12-19 02:03:29 +01001591 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001592 goto unlock_rtnl;
Johannes Berg41ade002007-12-19 02:03:29 +01001593
Johannes Bergb9454e82009-07-08 13:29:08 +02001594 if (key.def)
Johannes Berg79c97e92009-07-07 03:56:12 +02001595 func = rdev->ops->set_default_key;
Jouni Malinen3cfcf6ac2009-01-08 13:32:02 +02001596 else
Johannes Berg79c97e92009-07-07 03:56:12 +02001597 func = rdev->ops->set_default_mgmt_key;
Jouni Malinen3cfcf6ac2009-01-08 13:32:02 +02001598
1599 if (!func) {
Johannes Berg41ade002007-12-19 02:03:29 +01001600 err = -EOPNOTSUPP;
1601 goto out;
1602 }
1603
Johannes Bergfffd0932009-07-08 14:22:54 +02001604 wdev_lock(dev->ieee80211_ptr);
1605 err = nl80211_key_allowed(dev->ieee80211_ptr);
1606 if (!err)
1607 err = func(&rdev->wiphy, dev, key.idx);
1608
Johannes Berg3d23e342009-09-29 23:27:28 +02001609#ifdef CONFIG_CFG80211_WEXT
Johannes Berg08645122009-05-11 13:54:58 +02001610 if (!err) {
Johannes Berg79c97e92009-07-07 03:56:12 +02001611 if (func == rdev->ops->set_default_key)
Johannes Bergb9454e82009-07-08 13:29:08 +02001612 dev->ieee80211_ptr->wext.default_key = key.idx;
Johannes Berg08645122009-05-11 13:54:58 +02001613 else
Johannes Bergb9454e82009-07-08 13:29:08 +02001614 dev->ieee80211_ptr->wext.default_mgmt_key = key.idx;
Johannes Berg08645122009-05-11 13:54:58 +02001615 }
1616#endif
Johannes Bergfffd0932009-07-08 14:22:54 +02001617 wdev_unlock(dev->ieee80211_ptr);
Johannes Berg41ade002007-12-19 02:03:29 +01001618
1619 out:
Johannes Berg79c97e92009-07-07 03:56:12 +02001620 cfg80211_unlock_rdev(rdev);
Johannes Berg41ade002007-12-19 02:03:29 +01001621 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001622
1623 unlock_rtnl:
1624 rtnl_unlock();
1625
Johannes Berg41ade002007-12-19 02:03:29 +01001626 return err;
1627}
1628
1629static int nl80211_new_key(struct sk_buff *skb, struct genl_info *info)
1630{
Johannes Berg79c97e92009-07-07 03:56:12 +02001631 struct cfg80211_registered_device *rdev;
Johannes Bergfffd0932009-07-08 14:22:54 +02001632 int err;
Johannes Berg41ade002007-12-19 02:03:29 +01001633 struct net_device *dev;
Johannes Bergb9454e82009-07-08 13:29:08 +02001634 struct key_parse key;
Johannes Berg41ade002007-12-19 02:03:29 +01001635 u8 *mac_addr = NULL;
1636
Johannes Bergb9454e82009-07-08 13:29:08 +02001637 err = nl80211_parse_key(info, &key);
1638 if (err)
1639 return err;
Johannes Berg41ade002007-12-19 02:03:29 +01001640
Johannes Bergb9454e82009-07-08 13:29:08 +02001641 if (!key.p.key)
Johannes Berg41ade002007-12-19 02:03:29 +01001642 return -EINVAL;
1643
Johannes Berg41ade002007-12-19 02:03:29 +01001644 if (info->attrs[NL80211_ATTR_MAC])
1645 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
1646
Johannes Berg3b858752009-03-12 09:55:09 +01001647 rtnl_lock();
1648
Johannes Berg463d0182009-07-14 00:33:35 +02001649 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Johannes Berg41ade002007-12-19 02:03:29 +01001650 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001651 goto unlock_rtnl;
Johannes Berg41ade002007-12-19 02:03:29 +01001652
Johannes Berg79c97e92009-07-07 03:56:12 +02001653 if (!rdev->ops->add_key) {
Johannes Berg41ade002007-12-19 02:03:29 +01001654 err = -EOPNOTSUPP;
1655 goto out;
1656 }
1657
Johannes Bergfffd0932009-07-08 14:22:54 +02001658 if (cfg80211_validate_key_settings(rdev, &key.p, key.idx, mac_addr)) {
1659 err = -EINVAL;
1660 goto out;
1661 }
1662
1663 wdev_lock(dev->ieee80211_ptr);
1664 err = nl80211_key_allowed(dev->ieee80211_ptr);
1665 if (!err)
1666 err = rdev->ops->add_key(&rdev->wiphy, dev, key.idx,
1667 mac_addr, &key.p);
1668 wdev_unlock(dev->ieee80211_ptr);
Johannes Berg41ade002007-12-19 02:03:29 +01001669
1670 out:
Johannes Berg79c97e92009-07-07 03:56:12 +02001671 cfg80211_unlock_rdev(rdev);
Johannes Berg41ade002007-12-19 02:03:29 +01001672 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001673 unlock_rtnl:
1674 rtnl_unlock();
1675
Johannes Berg41ade002007-12-19 02:03:29 +01001676 return err;
1677}
1678
1679static int nl80211_del_key(struct sk_buff *skb, struct genl_info *info)
1680{
Johannes Berg79c97e92009-07-07 03:56:12 +02001681 struct cfg80211_registered_device *rdev;
Johannes Berg41ade002007-12-19 02:03:29 +01001682 int err;
1683 struct net_device *dev;
Johannes Berg41ade002007-12-19 02:03:29 +01001684 u8 *mac_addr = NULL;
Johannes Bergb9454e82009-07-08 13:29:08 +02001685 struct key_parse key;
Johannes Berg41ade002007-12-19 02:03:29 +01001686
Johannes Bergb9454e82009-07-08 13:29:08 +02001687 err = nl80211_parse_key(info, &key);
1688 if (err)
1689 return err;
Johannes Berg41ade002007-12-19 02:03:29 +01001690
1691 if (info->attrs[NL80211_ATTR_MAC])
1692 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
1693
Johannes Berg3b858752009-03-12 09:55:09 +01001694 rtnl_lock();
1695
Johannes Berg463d0182009-07-14 00:33:35 +02001696 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Johannes Berg41ade002007-12-19 02:03:29 +01001697 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001698 goto unlock_rtnl;
Johannes Berg41ade002007-12-19 02:03:29 +01001699
Johannes Berg79c97e92009-07-07 03:56:12 +02001700 if (!rdev->ops->del_key) {
Johannes Berg41ade002007-12-19 02:03:29 +01001701 err = -EOPNOTSUPP;
1702 goto out;
1703 }
1704
Johannes Bergfffd0932009-07-08 14:22:54 +02001705 wdev_lock(dev->ieee80211_ptr);
1706 err = nl80211_key_allowed(dev->ieee80211_ptr);
1707 if (!err)
1708 err = rdev->ops->del_key(&rdev->wiphy, dev, key.idx, mac_addr);
Johannes Berg41ade002007-12-19 02:03:29 +01001709
Johannes Berg3d23e342009-09-29 23:27:28 +02001710#ifdef CONFIG_CFG80211_WEXT
Johannes Berg08645122009-05-11 13:54:58 +02001711 if (!err) {
Johannes Bergb9454e82009-07-08 13:29:08 +02001712 if (key.idx == dev->ieee80211_ptr->wext.default_key)
Johannes Berg08645122009-05-11 13:54:58 +02001713 dev->ieee80211_ptr->wext.default_key = -1;
Johannes Bergb9454e82009-07-08 13:29:08 +02001714 else if (key.idx == dev->ieee80211_ptr->wext.default_mgmt_key)
Johannes Berg08645122009-05-11 13:54:58 +02001715 dev->ieee80211_ptr->wext.default_mgmt_key = -1;
1716 }
1717#endif
Johannes Bergfffd0932009-07-08 14:22:54 +02001718 wdev_unlock(dev->ieee80211_ptr);
Johannes Berg08645122009-05-11 13:54:58 +02001719
Johannes Berg41ade002007-12-19 02:03:29 +01001720 out:
Johannes Berg79c97e92009-07-07 03:56:12 +02001721 cfg80211_unlock_rdev(rdev);
Johannes Berg41ade002007-12-19 02:03:29 +01001722 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001723
1724 unlock_rtnl:
1725 rtnl_unlock();
1726
Johannes Berg41ade002007-12-19 02:03:29 +01001727 return err;
1728}
1729
Johannes Berged1b6cc2007-12-19 02:03:32 +01001730static int nl80211_addset_beacon(struct sk_buff *skb, struct genl_info *info)
1731{
1732 int (*call)(struct wiphy *wiphy, struct net_device *dev,
1733 struct beacon_parameters *info);
Johannes Berg79c97e92009-07-07 03:56:12 +02001734 struct cfg80211_registered_device *rdev;
Johannes Berged1b6cc2007-12-19 02:03:32 +01001735 int err;
1736 struct net_device *dev;
1737 struct beacon_parameters params;
1738 int haveinfo = 0;
1739
Johannes Bergf4a11bb2009-03-27 12:40:28 +01001740 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_BEACON_TAIL]))
1741 return -EINVAL;
1742
Johannes Berg3b858752009-03-12 09:55:09 +01001743 rtnl_lock();
1744
Johannes Berg463d0182009-07-14 00:33:35 +02001745 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Johannes Berged1b6cc2007-12-19 02:03:32 +01001746 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001747 goto unlock_rtnl;
Johannes Berged1b6cc2007-12-19 02:03:32 +01001748
Johannes Berg074ac8d2010-09-16 14:58:22 +02001749 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
1750 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) {
Jouni Malineneec60b02009-03-20 21:21:19 +02001751 err = -EOPNOTSUPP;
1752 goto out;
1753 }
1754
Johannes Berged1b6cc2007-12-19 02:03:32 +01001755 switch (info->genlhdr->cmd) {
1756 case NL80211_CMD_NEW_BEACON:
1757 /* these are required for NEW_BEACON */
1758 if (!info->attrs[NL80211_ATTR_BEACON_INTERVAL] ||
1759 !info->attrs[NL80211_ATTR_DTIM_PERIOD] ||
1760 !info->attrs[NL80211_ATTR_BEACON_HEAD]) {
1761 err = -EINVAL;
1762 goto out;
1763 }
1764
Johannes Berg79c97e92009-07-07 03:56:12 +02001765 call = rdev->ops->add_beacon;
Johannes Berged1b6cc2007-12-19 02:03:32 +01001766 break;
1767 case NL80211_CMD_SET_BEACON:
Johannes Berg79c97e92009-07-07 03:56:12 +02001768 call = rdev->ops->set_beacon;
Johannes Berged1b6cc2007-12-19 02:03:32 +01001769 break;
1770 default:
1771 WARN_ON(1);
1772 err = -EOPNOTSUPP;
1773 goto out;
1774 }
1775
1776 if (!call) {
1777 err = -EOPNOTSUPP;
1778 goto out;
1779 }
1780
1781 memset(&params, 0, sizeof(params));
1782
1783 if (info->attrs[NL80211_ATTR_BEACON_INTERVAL]) {
1784 params.interval =
1785 nla_get_u32(info->attrs[NL80211_ATTR_BEACON_INTERVAL]);
1786 haveinfo = 1;
1787 }
1788
1789 if (info->attrs[NL80211_ATTR_DTIM_PERIOD]) {
1790 params.dtim_period =
1791 nla_get_u32(info->attrs[NL80211_ATTR_DTIM_PERIOD]);
1792 haveinfo = 1;
1793 }
1794
1795 if (info->attrs[NL80211_ATTR_BEACON_HEAD]) {
1796 params.head = nla_data(info->attrs[NL80211_ATTR_BEACON_HEAD]);
1797 params.head_len =
1798 nla_len(info->attrs[NL80211_ATTR_BEACON_HEAD]);
1799 haveinfo = 1;
1800 }
1801
1802 if (info->attrs[NL80211_ATTR_BEACON_TAIL]) {
1803 params.tail = nla_data(info->attrs[NL80211_ATTR_BEACON_TAIL]);
1804 params.tail_len =
1805 nla_len(info->attrs[NL80211_ATTR_BEACON_TAIL]);
1806 haveinfo = 1;
1807 }
1808
1809 if (!haveinfo) {
1810 err = -EINVAL;
1811 goto out;
1812 }
1813
Johannes Berg79c97e92009-07-07 03:56:12 +02001814 err = call(&rdev->wiphy, dev, &params);
Johannes Berged1b6cc2007-12-19 02:03:32 +01001815
1816 out:
Johannes Berg79c97e92009-07-07 03:56:12 +02001817 cfg80211_unlock_rdev(rdev);
Johannes Berged1b6cc2007-12-19 02:03:32 +01001818 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001819 unlock_rtnl:
1820 rtnl_unlock();
1821
Johannes Berged1b6cc2007-12-19 02:03:32 +01001822 return err;
1823}
1824
1825static int nl80211_del_beacon(struct sk_buff *skb, struct genl_info *info)
1826{
Johannes Berg79c97e92009-07-07 03:56:12 +02001827 struct cfg80211_registered_device *rdev;
Johannes Berged1b6cc2007-12-19 02:03:32 +01001828 int err;
1829 struct net_device *dev;
1830
Johannes Berg3b858752009-03-12 09:55:09 +01001831 rtnl_lock();
1832
Johannes Berg463d0182009-07-14 00:33:35 +02001833 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Johannes Berged1b6cc2007-12-19 02:03:32 +01001834 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001835 goto unlock_rtnl;
Johannes Berged1b6cc2007-12-19 02:03:32 +01001836
Johannes Berg79c97e92009-07-07 03:56:12 +02001837 if (!rdev->ops->del_beacon) {
Johannes Berged1b6cc2007-12-19 02:03:32 +01001838 err = -EOPNOTSUPP;
1839 goto out;
1840 }
1841
Johannes Berg074ac8d2010-09-16 14:58:22 +02001842 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
1843 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) {
Jouni Malineneec60b02009-03-20 21:21:19 +02001844 err = -EOPNOTSUPP;
1845 goto out;
1846 }
Johannes Berg79c97e92009-07-07 03:56:12 +02001847 err = rdev->ops->del_beacon(&rdev->wiphy, dev);
Johannes Berged1b6cc2007-12-19 02:03:32 +01001848
1849 out:
Johannes Berg79c97e92009-07-07 03:56:12 +02001850 cfg80211_unlock_rdev(rdev);
Johannes Berged1b6cc2007-12-19 02:03:32 +01001851 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001852 unlock_rtnl:
1853 rtnl_unlock();
1854
Johannes Berged1b6cc2007-12-19 02:03:32 +01001855 return err;
1856}
1857
Johannes Berg5727ef12007-12-19 02:03:34 +01001858static const struct nla_policy sta_flags_policy[NL80211_STA_FLAG_MAX + 1] = {
1859 [NL80211_STA_FLAG_AUTHORIZED] = { .type = NLA_FLAG },
1860 [NL80211_STA_FLAG_SHORT_PREAMBLE] = { .type = NLA_FLAG },
1861 [NL80211_STA_FLAG_WME] = { .type = NLA_FLAG },
Jouni Malinen0e467242009-05-11 21:57:55 +03001862 [NL80211_STA_FLAG_MFP] = { .type = NLA_FLAG },
Johannes Berg5727ef12007-12-19 02:03:34 +01001863};
1864
Johannes Bergeccb8e82009-05-11 21:57:56 +03001865static int parse_station_flags(struct genl_info *info,
1866 struct station_parameters *params)
Johannes Berg5727ef12007-12-19 02:03:34 +01001867{
1868 struct nlattr *flags[NL80211_STA_FLAG_MAX + 1];
Johannes Bergeccb8e82009-05-11 21:57:56 +03001869 struct nlattr *nla;
Johannes Berg5727ef12007-12-19 02:03:34 +01001870 int flag;
1871
Johannes Bergeccb8e82009-05-11 21:57:56 +03001872 /*
1873 * Try parsing the new attribute first so userspace
1874 * can specify both for older kernels.
1875 */
1876 nla = info->attrs[NL80211_ATTR_STA_FLAGS2];
1877 if (nla) {
1878 struct nl80211_sta_flag_update *sta_flags;
Johannes Berg5727ef12007-12-19 02:03:34 +01001879
Johannes Bergeccb8e82009-05-11 21:57:56 +03001880 sta_flags = nla_data(nla);
1881 params->sta_flags_mask = sta_flags->mask;
1882 params->sta_flags_set = sta_flags->set;
1883 if ((params->sta_flags_mask |
1884 params->sta_flags_set) & BIT(__NL80211_STA_FLAG_INVALID))
1885 return -EINVAL;
1886 return 0;
1887 }
1888
1889 /* if present, parse the old attribute */
1890
1891 nla = info->attrs[NL80211_ATTR_STA_FLAGS];
Johannes Berg5727ef12007-12-19 02:03:34 +01001892 if (!nla)
1893 return 0;
1894
1895 if (nla_parse_nested(flags, NL80211_STA_FLAG_MAX,
1896 nla, sta_flags_policy))
1897 return -EINVAL;
1898
Johannes Bergeccb8e82009-05-11 21:57:56 +03001899 params->sta_flags_mask = (1 << __NL80211_STA_FLAG_AFTER_LAST) - 1;
1900 params->sta_flags_mask &= ~1;
Johannes Berg5727ef12007-12-19 02:03:34 +01001901
1902 for (flag = 1; flag <= NL80211_STA_FLAG_MAX; flag++)
1903 if (flags[flag])
Johannes Bergeccb8e82009-05-11 21:57:56 +03001904 params->sta_flags_set |= (1<<flag);
Johannes Berg5727ef12007-12-19 02:03:34 +01001905
1906 return 0;
1907}
1908
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001909static int nl80211_send_station(struct sk_buff *msg, u32 pid, u32 seq,
1910 int flags, struct net_device *dev,
Johannes Berg98b62182009-12-23 13:15:44 +01001911 const u8 *mac_addr, struct station_info *sinfo)
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001912{
1913 void *hdr;
Henning Rogge420e7fa2008-12-11 22:04:19 +01001914 struct nlattr *sinfoattr, *txrate;
1915 u16 bitrate;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001916
1917 hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_STATION);
1918 if (!hdr)
1919 return -1;
1920
1921 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
1922 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr);
1923
Johannes Bergf5ea9122009-08-07 16:17:38 +02001924 NLA_PUT_U32(msg, NL80211_ATTR_GENERATION, sinfo->generation);
1925
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001926 sinfoattr = nla_nest_start(msg, NL80211_ATTR_STA_INFO);
1927 if (!sinfoattr)
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001928 goto nla_put_failure;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001929 if (sinfo->filled & STATION_INFO_INACTIVE_TIME)
1930 NLA_PUT_U32(msg, NL80211_STA_INFO_INACTIVE_TIME,
1931 sinfo->inactive_time);
1932 if (sinfo->filled & STATION_INFO_RX_BYTES)
1933 NLA_PUT_U32(msg, NL80211_STA_INFO_RX_BYTES,
1934 sinfo->rx_bytes);
1935 if (sinfo->filled & STATION_INFO_TX_BYTES)
1936 NLA_PUT_U32(msg, NL80211_STA_INFO_TX_BYTES,
1937 sinfo->tx_bytes);
1938 if (sinfo->filled & STATION_INFO_LLID)
1939 NLA_PUT_U16(msg, NL80211_STA_INFO_LLID,
1940 sinfo->llid);
1941 if (sinfo->filled & STATION_INFO_PLID)
1942 NLA_PUT_U16(msg, NL80211_STA_INFO_PLID,
1943 sinfo->plid);
1944 if (sinfo->filled & STATION_INFO_PLINK_STATE)
1945 NLA_PUT_U8(msg, NL80211_STA_INFO_PLINK_STATE,
1946 sinfo->plink_state);
Henning Rogge420e7fa2008-12-11 22:04:19 +01001947 if (sinfo->filled & STATION_INFO_SIGNAL)
1948 NLA_PUT_U8(msg, NL80211_STA_INFO_SIGNAL,
1949 sinfo->signal);
1950 if (sinfo->filled & STATION_INFO_TX_BITRATE) {
1951 txrate = nla_nest_start(msg, NL80211_STA_INFO_TX_BITRATE);
1952 if (!txrate)
1953 goto nla_put_failure;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001954
John W. Linville254416a2009-12-09 16:43:52 -05001955 /* cfg80211_calculate_bitrate will return 0 for mcs >= 32 */
1956 bitrate = cfg80211_calculate_bitrate(&sinfo->txrate);
Henning Rogge420e7fa2008-12-11 22:04:19 +01001957 if (bitrate > 0)
1958 NLA_PUT_U16(msg, NL80211_RATE_INFO_BITRATE, bitrate);
1959
1960 if (sinfo->txrate.flags & RATE_INFO_FLAGS_MCS)
1961 NLA_PUT_U8(msg, NL80211_RATE_INFO_MCS,
1962 sinfo->txrate.mcs);
1963 if (sinfo->txrate.flags & RATE_INFO_FLAGS_40_MHZ_WIDTH)
1964 NLA_PUT_FLAG(msg, NL80211_RATE_INFO_40_MHZ_WIDTH);
1965 if (sinfo->txrate.flags & RATE_INFO_FLAGS_SHORT_GI)
1966 NLA_PUT_FLAG(msg, NL80211_RATE_INFO_SHORT_GI);
1967
1968 nla_nest_end(msg, txrate);
1969 }
Jouni Malinen98c8a60a2009-02-17 13:24:57 +02001970 if (sinfo->filled & STATION_INFO_RX_PACKETS)
1971 NLA_PUT_U32(msg, NL80211_STA_INFO_RX_PACKETS,
1972 sinfo->rx_packets);
1973 if (sinfo->filled & STATION_INFO_TX_PACKETS)
1974 NLA_PUT_U32(msg, NL80211_STA_INFO_TX_PACKETS,
1975 sinfo->tx_packets);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001976 nla_nest_end(msg, sinfoattr);
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001977
1978 return genlmsg_end(msg, hdr);
1979
1980 nla_put_failure:
Thomas Grafbc3ed282008-06-03 16:36:54 -07001981 genlmsg_cancel(msg, hdr);
1982 return -EMSGSIZE;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001983}
1984
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001985static int nl80211_dump_station(struct sk_buff *skb,
Johannes Bergbba95fe2008-07-29 13:22:51 +02001986 struct netlink_callback *cb)
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001987{
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001988 struct station_info sinfo;
1989 struct cfg80211_registered_device *dev;
Johannes Bergbba95fe2008-07-29 13:22:51 +02001990 struct net_device *netdev;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001991 u8 mac_addr[ETH_ALEN];
Johannes Bergbba95fe2008-07-29 13:22:51 +02001992 int ifidx = cb->args[0];
1993 int sta_idx = cb->args[1];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001994 int err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001995
Holger Schuriga0438972009-11-11 11:30:02 +01001996 if (!ifidx)
1997 ifidx = nl80211_get_ifidx(cb);
1998 if (ifidx < 0)
1999 return ifidx;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002000
Johannes Berg3b858752009-03-12 09:55:09 +01002001 rtnl_lock();
2002
Johannes Berg463d0182009-07-14 00:33:35 +02002003 netdev = __dev_get_by_index(sock_net(skb->sk), ifidx);
Johannes Berg3b858752009-03-12 09:55:09 +01002004 if (!netdev) {
2005 err = -ENODEV;
2006 goto out_rtnl;
2007 }
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002008
Johannes Berg463d0182009-07-14 00:33:35 +02002009 dev = cfg80211_get_dev_from_ifindex(sock_net(skb->sk), ifidx);
Johannes Bergbba95fe2008-07-29 13:22:51 +02002010 if (IS_ERR(dev)) {
2011 err = PTR_ERR(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01002012 goto out_rtnl;
Johannes Bergbba95fe2008-07-29 13:22:51 +02002013 }
2014
2015 if (!dev->ops->dump_station) {
Jouni Malineneec60b02009-03-20 21:21:19 +02002016 err = -EOPNOTSUPP;
Johannes Bergbba95fe2008-07-29 13:22:51 +02002017 goto out_err;
2018 }
2019
Johannes Bergbba95fe2008-07-29 13:22:51 +02002020 while (1) {
2021 err = dev->ops->dump_station(&dev->wiphy, netdev, sta_idx,
2022 mac_addr, &sinfo);
2023 if (err == -ENOENT)
2024 break;
2025 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01002026 goto out_err;
Johannes Bergbba95fe2008-07-29 13:22:51 +02002027
2028 if (nl80211_send_station(skb,
2029 NETLINK_CB(cb->skb).pid,
2030 cb->nlh->nlmsg_seq, NLM_F_MULTI,
2031 netdev, mac_addr,
2032 &sinfo) < 0)
2033 goto out;
2034
2035 sta_idx++;
2036 }
2037
2038
2039 out:
2040 cb->args[1] = sta_idx;
2041 err = skb->len;
Johannes Bergbba95fe2008-07-29 13:22:51 +02002042 out_err:
Johannes Berg4d0c8ae2009-07-07 03:56:09 +02002043 cfg80211_unlock_rdev(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01002044 out_rtnl:
2045 rtnl_unlock();
Johannes Bergbba95fe2008-07-29 13:22:51 +02002046
2047 return err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002048}
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002049
Johannes Berg5727ef12007-12-19 02:03:34 +01002050static int nl80211_get_station(struct sk_buff *skb, struct genl_info *info)
2051{
Johannes Berg79c97e92009-07-07 03:56:12 +02002052 struct cfg80211_registered_device *rdev;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002053 int err;
2054 struct net_device *dev;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002055 struct station_info sinfo;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002056 struct sk_buff *msg;
2057 u8 *mac_addr = NULL;
2058
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002059 memset(&sinfo, 0, sizeof(sinfo));
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002060
2061 if (!info->attrs[NL80211_ATTR_MAC])
2062 return -EINVAL;
2063
2064 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
2065
Johannes Berg3b858752009-03-12 09:55:09 +01002066 rtnl_lock();
2067
Johannes Berg463d0182009-07-14 00:33:35 +02002068 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002069 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01002070 goto out_rtnl;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002071
Johannes Berg79c97e92009-07-07 03:56:12 +02002072 if (!rdev->ops->get_station) {
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002073 err = -EOPNOTSUPP;
2074 goto out;
2075 }
2076
Johannes Berg79c97e92009-07-07 03:56:12 +02002077 err = rdev->ops->get_station(&rdev->wiphy, dev, mac_addr, &sinfo);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002078 if (err)
2079 goto out;
2080
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07002081 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002082 if (!msg)
2083 goto out;
2084
2085 if (nl80211_send_station(msg, info->snd_pid, info->snd_seq, 0,
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002086 dev, mac_addr, &sinfo) < 0)
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002087 goto out_free;
2088
Johannes Berg134e6372009-07-10 09:51:34 +00002089 err = genlmsg_reply(msg, info);
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002090 goto out;
2091
2092 out_free:
2093 nlmsg_free(msg);
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002094 out:
Johannes Berg79c97e92009-07-07 03:56:12 +02002095 cfg80211_unlock_rdev(rdev);
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002096 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01002097 out_rtnl:
2098 rtnl_unlock();
2099
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002100 return err;
Johannes Berg5727ef12007-12-19 02:03:34 +01002101}
2102
2103/*
Felix Fietkauc258d2d2009-11-11 17:23:31 +01002104 * Get vlan interface making sure it is running and on the right wiphy.
Johannes Berg5727ef12007-12-19 02:03:34 +01002105 */
Johannes Berg463d0182009-07-14 00:33:35 +02002106static int get_vlan(struct genl_info *info,
Johannes Berg5727ef12007-12-19 02:03:34 +01002107 struct cfg80211_registered_device *rdev,
2108 struct net_device **vlan)
2109{
Johannes Berg463d0182009-07-14 00:33:35 +02002110 struct nlattr *vlanattr = info->attrs[NL80211_ATTR_STA_VLAN];
Johannes Berg5727ef12007-12-19 02:03:34 +01002111 *vlan = NULL;
2112
2113 if (vlanattr) {
Johannes Berg463d0182009-07-14 00:33:35 +02002114 *vlan = dev_get_by_index(genl_info_net(info),
2115 nla_get_u32(vlanattr));
Johannes Berg5727ef12007-12-19 02:03:34 +01002116 if (!*vlan)
2117 return -ENODEV;
2118 if (!(*vlan)->ieee80211_ptr)
2119 return -EINVAL;
2120 if ((*vlan)->ieee80211_ptr->wiphy != &rdev->wiphy)
2121 return -EINVAL;
Felix Fietkauc258d2d2009-11-11 17:23:31 +01002122 if (!netif_running(*vlan))
2123 return -ENETDOWN;
Johannes Berg5727ef12007-12-19 02:03:34 +01002124 }
2125 return 0;
2126}
2127
2128static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
2129{
Johannes Berg79c97e92009-07-07 03:56:12 +02002130 struct cfg80211_registered_device *rdev;
Johannes Berg5727ef12007-12-19 02:03:34 +01002131 int err;
2132 struct net_device *dev;
2133 struct station_parameters params;
2134 u8 *mac_addr = NULL;
2135
2136 memset(&params, 0, sizeof(params));
2137
2138 params.listen_interval = -1;
2139
2140 if (info->attrs[NL80211_ATTR_STA_AID])
2141 return -EINVAL;
2142
2143 if (!info->attrs[NL80211_ATTR_MAC])
2144 return -EINVAL;
2145
2146 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
2147
2148 if (info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]) {
2149 params.supported_rates =
2150 nla_data(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]);
2151 params.supported_rates_len =
2152 nla_len(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]);
2153 }
2154
2155 if (info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL])
2156 params.listen_interval =
2157 nla_get_u16(info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]);
2158
Jouni Malinen36aedc92008-08-25 11:58:58 +03002159 if (info->attrs[NL80211_ATTR_HT_CAPABILITY])
2160 params.ht_capa =
2161 nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]);
2162
Johannes Bergeccb8e82009-05-11 21:57:56 +03002163 if (parse_station_flags(info, &params))
Johannes Berg5727ef12007-12-19 02:03:34 +01002164 return -EINVAL;
2165
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002166 if (info->attrs[NL80211_ATTR_STA_PLINK_ACTION])
2167 params.plink_action =
2168 nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_ACTION]);
2169
Johannes Berg3b858752009-03-12 09:55:09 +01002170 rtnl_lock();
2171
Johannes Berg463d0182009-07-14 00:33:35 +02002172 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Johannes Berg5727ef12007-12-19 02:03:34 +01002173 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01002174 goto out_rtnl;
Johannes Berg5727ef12007-12-19 02:03:34 +01002175
Johannes Berg463d0182009-07-14 00:33:35 +02002176 err = get_vlan(info, rdev, &params.vlan);
Johannes Berga97f4422009-06-18 17:23:43 +02002177 if (err)
Johannes Berg034d6552009-05-27 10:35:29 +02002178 goto out;
Johannes Berga97f4422009-06-18 17:23:43 +02002179
2180 /* validate settings */
2181 err = 0;
2182
2183 switch (dev->ieee80211_ptr->iftype) {
2184 case NL80211_IFTYPE_AP:
2185 case NL80211_IFTYPE_AP_VLAN:
Johannes Berg074ac8d2010-09-16 14:58:22 +02002186 case NL80211_IFTYPE_P2P_GO:
Johannes Berga97f4422009-06-18 17:23:43 +02002187 /* disallow mesh-specific things */
2188 if (params.plink_action)
2189 err = -EINVAL;
2190 break;
Johannes Berg074ac8d2010-09-16 14:58:22 +02002191 case NL80211_IFTYPE_P2P_CLIENT:
Johannes Berga97f4422009-06-18 17:23:43 +02002192 case NL80211_IFTYPE_STATION:
2193 /* disallow everything but AUTHORIZED flag */
2194 if (params.plink_action)
2195 err = -EINVAL;
2196 if (params.vlan)
2197 err = -EINVAL;
2198 if (params.supported_rates)
2199 err = -EINVAL;
2200 if (params.ht_capa)
2201 err = -EINVAL;
2202 if (params.listen_interval >= 0)
2203 err = -EINVAL;
2204 if (params.sta_flags_mask & ~BIT(NL80211_STA_FLAG_AUTHORIZED))
2205 err = -EINVAL;
2206 break;
2207 case NL80211_IFTYPE_MESH_POINT:
2208 /* disallow things mesh doesn't support */
2209 if (params.vlan)
2210 err = -EINVAL;
2211 if (params.ht_capa)
2212 err = -EINVAL;
2213 if (params.listen_interval >= 0)
2214 err = -EINVAL;
2215 if (params.supported_rates)
2216 err = -EINVAL;
2217 if (params.sta_flags_mask)
2218 err = -EINVAL;
2219 break;
2220 default:
2221 err = -EINVAL;
Johannes Berg034d6552009-05-27 10:35:29 +02002222 }
2223
Johannes Berg5727ef12007-12-19 02:03:34 +01002224 if (err)
2225 goto out;
2226
Johannes Berg79c97e92009-07-07 03:56:12 +02002227 if (!rdev->ops->change_station) {
Johannes Berg5727ef12007-12-19 02:03:34 +01002228 err = -EOPNOTSUPP;
2229 goto out;
2230 }
2231
Johannes Berg79c97e92009-07-07 03:56:12 +02002232 err = rdev->ops->change_station(&rdev->wiphy, dev, mac_addr, &params);
Johannes Berg5727ef12007-12-19 02:03:34 +01002233
2234 out:
2235 if (params.vlan)
2236 dev_put(params.vlan);
Johannes Berg79c97e92009-07-07 03:56:12 +02002237 cfg80211_unlock_rdev(rdev);
Johannes Berg5727ef12007-12-19 02:03:34 +01002238 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01002239 out_rtnl:
2240 rtnl_unlock();
2241
Johannes Berg5727ef12007-12-19 02:03:34 +01002242 return err;
2243}
2244
2245static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
2246{
Johannes Berg79c97e92009-07-07 03:56:12 +02002247 struct cfg80211_registered_device *rdev;
Johannes Berg5727ef12007-12-19 02:03:34 +01002248 int err;
2249 struct net_device *dev;
2250 struct station_parameters params;
2251 u8 *mac_addr = NULL;
2252
2253 memset(&params, 0, sizeof(params));
2254
2255 if (!info->attrs[NL80211_ATTR_MAC])
2256 return -EINVAL;
2257
Johannes Berg5727ef12007-12-19 02:03:34 +01002258 if (!info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL])
2259 return -EINVAL;
2260
2261 if (!info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES])
2262 return -EINVAL;
2263
Thadeu Lima de Souza Cascardo0e956c12010-02-12 12:34:50 -02002264 if (!info->attrs[NL80211_ATTR_STA_AID])
2265 return -EINVAL;
2266
Johannes Berg5727ef12007-12-19 02:03:34 +01002267 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
2268 params.supported_rates =
2269 nla_data(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]);
2270 params.supported_rates_len =
2271 nla_len(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]);
2272 params.listen_interval =
2273 nla_get_u16(info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]);
Johannes Berg51b50fb2009-05-24 16:42:30 +02002274
Thadeu Lima de Souza Cascardo0e956c12010-02-12 12:34:50 -02002275 params.aid = nla_get_u16(info->attrs[NL80211_ATTR_STA_AID]);
2276 if (!params.aid || params.aid > IEEE80211_MAX_AID)
2277 return -EINVAL;
Johannes Berg51b50fb2009-05-24 16:42:30 +02002278
Jouni Malinen36aedc92008-08-25 11:58:58 +03002279 if (info->attrs[NL80211_ATTR_HT_CAPABILITY])
2280 params.ht_capa =
2281 nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]);
Johannes Berg5727ef12007-12-19 02:03:34 +01002282
Johannes Bergeccb8e82009-05-11 21:57:56 +03002283 if (parse_station_flags(info, &params))
Johannes Berg5727ef12007-12-19 02:03:34 +01002284 return -EINVAL;
2285
Johannes Berg3b858752009-03-12 09:55:09 +01002286 rtnl_lock();
2287
Johannes Berg463d0182009-07-14 00:33:35 +02002288 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Johannes Berg5727ef12007-12-19 02:03:34 +01002289 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01002290 goto out_rtnl;
Johannes Berg5727ef12007-12-19 02:03:34 +01002291
Thadeu Lima de Souza Cascardo0e956c12010-02-12 12:34:50 -02002292 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
Johannes Berg074ac8d2010-09-16 14:58:22 +02002293 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN &&
2294 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) {
Thadeu Lima de Souza Cascardo0e956c12010-02-12 12:34:50 -02002295 err = -EINVAL;
2296 goto out;
2297 }
2298
Johannes Berg463d0182009-07-14 00:33:35 +02002299 err = get_vlan(info, rdev, &params.vlan);
Johannes Berga97f4422009-06-18 17:23:43 +02002300 if (err)
Johannes Berge80cf852009-05-11 14:43:13 +02002301 goto out;
Johannes Berga97f4422009-06-18 17:23:43 +02002302
2303 /* validate settings */
2304 err = 0;
2305
Johannes Berg79c97e92009-07-07 03:56:12 +02002306 if (!rdev->ops->add_station) {
Johannes Berg5727ef12007-12-19 02:03:34 +01002307 err = -EOPNOTSUPP;
2308 goto out;
2309 }
2310
Jouni Malinen35a8efe2009-03-20 21:21:18 +02002311 if (!netif_running(dev)) {
2312 err = -ENETDOWN;
2313 goto out;
2314 }
2315
Johannes Berg79c97e92009-07-07 03:56:12 +02002316 err = rdev->ops->add_station(&rdev->wiphy, dev, mac_addr, &params);
Johannes Berg5727ef12007-12-19 02:03:34 +01002317
2318 out:
2319 if (params.vlan)
2320 dev_put(params.vlan);
Johannes Berg79c97e92009-07-07 03:56:12 +02002321 cfg80211_unlock_rdev(rdev);
Johannes Berg5727ef12007-12-19 02:03:34 +01002322 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01002323 out_rtnl:
2324 rtnl_unlock();
2325
Johannes Berg5727ef12007-12-19 02:03:34 +01002326 return err;
2327}
2328
2329static int nl80211_del_station(struct sk_buff *skb, struct genl_info *info)
2330{
Johannes Berg79c97e92009-07-07 03:56:12 +02002331 struct cfg80211_registered_device *rdev;
Johannes Berg5727ef12007-12-19 02:03:34 +01002332 int err;
2333 struct net_device *dev;
2334 u8 *mac_addr = NULL;
2335
2336 if (info->attrs[NL80211_ATTR_MAC])
2337 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
2338
Johannes Berg3b858752009-03-12 09:55:09 +01002339 rtnl_lock();
2340
Johannes Berg463d0182009-07-14 00:33:35 +02002341 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Johannes Berg5727ef12007-12-19 02:03:34 +01002342 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01002343 goto out_rtnl;
Johannes Berg5727ef12007-12-19 02:03:34 +01002344
Johannes Berge80cf852009-05-11 14:43:13 +02002345 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
Marco Porschd5d9de02010-03-30 10:00:16 +02002346 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN &&
Johannes Berg074ac8d2010-09-16 14:58:22 +02002347 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT &&
2348 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) {
Johannes Berge80cf852009-05-11 14:43:13 +02002349 err = -EINVAL;
2350 goto out;
2351 }
2352
Johannes Berg79c97e92009-07-07 03:56:12 +02002353 if (!rdev->ops->del_station) {
Johannes Berg5727ef12007-12-19 02:03:34 +01002354 err = -EOPNOTSUPP;
2355 goto out;
2356 }
2357
Johannes Berg79c97e92009-07-07 03:56:12 +02002358 err = rdev->ops->del_station(&rdev->wiphy, dev, mac_addr);
Johannes Berg5727ef12007-12-19 02:03:34 +01002359
2360 out:
Johannes Berg79c97e92009-07-07 03:56:12 +02002361 cfg80211_unlock_rdev(rdev);
Johannes Berg5727ef12007-12-19 02:03:34 +01002362 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01002363 out_rtnl:
2364 rtnl_unlock();
2365
Johannes Berg5727ef12007-12-19 02:03:34 +01002366 return err;
2367}
2368
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002369static int nl80211_send_mpath(struct sk_buff *msg, u32 pid, u32 seq,
2370 int flags, struct net_device *dev,
2371 u8 *dst, u8 *next_hop,
2372 struct mpath_info *pinfo)
2373{
2374 void *hdr;
2375 struct nlattr *pinfoattr;
2376
2377 hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_STATION);
2378 if (!hdr)
2379 return -1;
2380
2381 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
2382 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, dst);
2383 NLA_PUT(msg, NL80211_ATTR_MPATH_NEXT_HOP, ETH_ALEN, next_hop);
2384
Johannes Bergf5ea9122009-08-07 16:17:38 +02002385 NLA_PUT_U32(msg, NL80211_ATTR_GENERATION, pinfo->generation);
2386
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002387 pinfoattr = nla_nest_start(msg, NL80211_ATTR_MPATH_INFO);
2388 if (!pinfoattr)
2389 goto nla_put_failure;
2390 if (pinfo->filled & MPATH_INFO_FRAME_QLEN)
2391 NLA_PUT_U32(msg, NL80211_MPATH_INFO_FRAME_QLEN,
2392 pinfo->frame_qlen);
Rui Paulod19b3bf2009-11-09 23:46:55 +00002393 if (pinfo->filled & MPATH_INFO_SN)
2394 NLA_PUT_U32(msg, NL80211_MPATH_INFO_SN,
2395 pinfo->sn);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002396 if (pinfo->filled & MPATH_INFO_METRIC)
2397 NLA_PUT_U32(msg, NL80211_MPATH_INFO_METRIC,
2398 pinfo->metric);
2399 if (pinfo->filled & MPATH_INFO_EXPTIME)
2400 NLA_PUT_U32(msg, NL80211_MPATH_INFO_EXPTIME,
2401 pinfo->exptime);
2402 if (pinfo->filled & MPATH_INFO_FLAGS)
2403 NLA_PUT_U8(msg, NL80211_MPATH_INFO_FLAGS,
2404 pinfo->flags);
2405 if (pinfo->filled & MPATH_INFO_DISCOVERY_TIMEOUT)
2406 NLA_PUT_U32(msg, NL80211_MPATH_INFO_DISCOVERY_TIMEOUT,
2407 pinfo->discovery_timeout);
2408 if (pinfo->filled & MPATH_INFO_DISCOVERY_RETRIES)
2409 NLA_PUT_U8(msg, NL80211_MPATH_INFO_DISCOVERY_RETRIES,
2410 pinfo->discovery_retries);
2411
2412 nla_nest_end(msg, pinfoattr);
2413
2414 return genlmsg_end(msg, hdr);
2415
2416 nla_put_failure:
Thomas Grafbc3ed282008-06-03 16:36:54 -07002417 genlmsg_cancel(msg, hdr);
2418 return -EMSGSIZE;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002419}
2420
2421static int nl80211_dump_mpath(struct sk_buff *skb,
Johannes Bergbba95fe2008-07-29 13:22:51 +02002422 struct netlink_callback *cb)
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002423{
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002424 struct mpath_info pinfo;
2425 struct cfg80211_registered_device *dev;
Johannes Bergbba95fe2008-07-29 13:22:51 +02002426 struct net_device *netdev;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002427 u8 dst[ETH_ALEN];
2428 u8 next_hop[ETH_ALEN];
Johannes Bergbba95fe2008-07-29 13:22:51 +02002429 int ifidx = cb->args[0];
2430 int path_idx = cb->args[1];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002431 int err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002432
Holger Schuriga0438972009-11-11 11:30:02 +01002433 if (!ifidx)
2434 ifidx = nl80211_get_ifidx(cb);
2435 if (ifidx < 0)
2436 return ifidx;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002437
Johannes Berg3b858752009-03-12 09:55:09 +01002438 rtnl_lock();
2439
Johannes Berg463d0182009-07-14 00:33:35 +02002440 netdev = __dev_get_by_index(sock_net(skb->sk), ifidx);
Johannes Berg3b858752009-03-12 09:55:09 +01002441 if (!netdev) {
2442 err = -ENODEV;
2443 goto out_rtnl;
2444 }
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002445
Johannes Berg463d0182009-07-14 00:33:35 +02002446 dev = cfg80211_get_dev_from_ifindex(sock_net(skb->sk), ifidx);
Johannes Bergbba95fe2008-07-29 13:22:51 +02002447 if (IS_ERR(dev)) {
2448 err = PTR_ERR(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01002449 goto out_rtnl;
Johannes Bergbba95fe2008-07-29 13:22:51 +02002450 }
2451
2452 if (!dev->ops->dump_mpath) {
Jouni Malineneec60b02009-03-20 21:21:19 +02002453 err = -EOPNOTSUPP;
Johannes Bergbba95fe2008-07-29 13:22:51 +02002454 goto out_err;
2455 }
2456
Jouni Malineneec60b02009-03-20 21:21:19 +02002457 if (netdev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) {
2458 err = -EOPNOTSUPP;
Roel Kluin0448b5f2009-08-22 21:15:49 +02002459 goto out_err;
Jouni Malineneec60b02009-03-20 21:21:19 +02002460 }
2461
Johannes Bergbba95fe2008-07-29 13:22:51 +02002462 while (1) {
2463 err = dev->ops->dump_mpath(&dev->wiphy, netdev, path_idx,
2464 dst, next_hop, &pinfo);
2465 if (err == -ENOENT)
2466 break;
2467 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01002468 goto out_err;
Johannes Bergbba95fe2008-07-29 13:22:51 +02002469
2470 if (nl80211_send_mpath(skb, NETLINK_CB(cb->skb).pid,
2471 cb->nlh->nlmsg_seq, NLM_F_MULTI,
2472 netdev, dst, next_hop,
2473 &pinfo) < 0)
2474 goto out;
2475
2476 path_idx++;
2477 }
2478
2479
2480 out:
2481 cb->args[1] = path_idx;
2482 err = skb->len;
Johannes Bergbba95fe2008-07-29 13:22:51 +02002483 out_err:
Johannes Berg4d0c8ae2009-07-07 03:56:09 +02002484 cfg80211_unlock_rdev(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01002485 out_rtnl:
2486 rtnl_unlock();
Johannes Bergbba95fe2008-07-29 13:22:51 +02002487
2488 return err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002489}
2490
2491static int nl80211_get_mpath(struct sk_buff *skb, struct genl_info *info)
2492{
Johannes Berg79c97e92009-07-07 03:56:12 +02002493 struct cfg80211_registered_device *rdev;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002494 int err;
2495 struct net_device *dev;
2496 struct mpath_info pinfo;
2497 struct sk_buff *msg;
2498 u8 *dst = NULL;
2499 u8 next_hop[ETH_ALEN];
2500
2501 memset(&pinfo, 0, sizeof(pinfo));
2502
2503 if (!info->attrs[NL80211_ATTR_MAC])
2504 return -EINVAL;
2505
2506 dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
2507
Johannes Berg3b858752009-03-12 09:55:09 +01002508 rtnl_lock();
2509
Johannes Berg463d0182009-07-14 00:33:35 +02002510 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002511 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01002512 goto out_rtnl;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002513
Johannes Berg79c97e92009-07-07 03:56:12 +02002514 if (!rdev->ops->get_mpath) {
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002515 err = -EOPNOTSUPP;
2516 goto out;
2517 }
2518
Jouni Malineneec60b02009-03-20 21:21:19 +02002519 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) {
2520 err = -EOPNOTSUPP;
2521 goto out;
2522 }
2523
Johannes Berg79c97e92009-07-07 03:56:12 +02002524 err = rdev->ops->get_mpath(&rdev->wiphy, dev, dst, next_hop, &pinfo);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002525 if (err)
2526 goto out;
2527
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07002528 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002529 if (!msg)
2530 goto out;
2531
2532 if (nl80211_send_mpath(msg, info->snd_pid, info->snd_seq, 0,
2533 dev, dst, next_hop, &pinfo) < 0)
2534 goto out_free;
2535
Johannes Berg134e6372009-07-10 09:51:34 +00002536 err = genlmsg_reply(msg, info);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002537 goto out;
2538
2539 out_free:
2540 nlmsg_free(msg);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002541 out:
Johannes Berg79c97e92009-07-07 03:56:12 +02002542 cfg80211_unlock_rdev(rdev);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002543 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01002544 out_rtnl:
2545 rtnl_unlock();
2546
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002547 return err;
2548}
2549
2550static int nl80211_set_mpath(struct sk_buff *skb, struct genl_info *info)
2551{
Johannes Berg79c97e92009-07-07 03:56:12 +02002552 struct cfg80211_registered_device *rdev;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002553 int err;
2554 struct net_device *dev;
2555 u8 *dst = NULL;
2556 u8 *next_hop = NULL;
2557
2558 if (!info->attrs[NL80211_ATTR_MAC])
2559 return -EINVAL;
2560
2561 if (!info->attrs[NL80211_ATTR_MPATH_NEXT_HOP])
2562 return -EINVAL;
2563
2564 dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
2565 next_hop = nla_data(info->attrs[NL80211_ATTR_MPATH_NEXT_HOP]);
2566
Johannes Berg3b858752009-03-12 09:55:09 +01002567 rtnl_lock();
2568
Johannes Berg463d0182009-07-14 00:33:35 +02002569 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002570 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01002571 goto out_rtnl;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002572
Johannes Berg79c97e92009-07-07 03:56:12 +02002573 if (!rdev->ops->change_mpath) {
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002574 err = -EOPNOTSUPP;
2575 goto out;
2576 }
2577
Jouni Malineneec60b02009-03-20 21:21:19 +02002578 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) {
2579 err = -EOPNOTSUPP;
2580 goto out;
2581 }
2582
Jouni Malinen35a8efe2009-03-20 21:21:18 +02002583 if (!netif_running(dev)) {
2584 err = -ENETDOWN;
2585 goto out;
2586 }
2587
Johannes Berg79c97e92009-07-07 03:56:12 +02002588 err = rdev->ops->change_mpath(&rdev->wiphy, dev, dst, next_hop);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002589
2590 out:
Johannes Berg79c97e92009-07-07 03:56:12 +02002591 cfg80211_unlock_rdev(rdev);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002592 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01002593 out_rtnl:
2594 rtnl_unlock();
2595
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002596 return err;
2597}
2598static int nl80211_new_mpath(struct sk_buff *skb, struct genl_info *info)
2599{
Johannes Berg79c97e92009-07-07 03:56:12 +02002600 struct cfg80211_registered_device *rdev;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002601 int err;
2602 struct net_device *dev;
2603 u8 *dst = NULL;
2604 u8 *next_hop = NULL;
2605
2606 if (!info->attrs[NL80211_ATTR_MAC])
2607 return -EINVAL;
2608
2609 if (!info->attrs[NL80211_ATTR_MPATH_NEXT_HOP])
2610 return -EINVAL;
2611
2612 dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
2613 next_hop = nla_data(info->attrs[NL80211_ATTR_MPATH_NEXT_HOP]);
2614
Johannes Berg3b858752009-03-12 09:55:09 +01002615 rtnl_lock();
2616
Johannes Berg463d0182009-07-14 00:33:35 +02002617 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002618 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01002619 goto out_rtnl;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002620
Johannes Berg79c97e92009-07-07 03:56:12 +02002621 if (!rdev->ops->add_mpath) {
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002622 err = -EOPNOTSUPP;
2623 goto out;
2624 }
2625
Jouni Malineneec60b02009-03-20 21:21:19 +02002626 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) {
2627 err = -EOPNOTSUPP;
2628 goto out;
2629 }
2630
Jouni Malinen35a8efe2009-03-20 21:21:18 +02002631 if (!netif_running(dev)) {
2632 err = -ENETDOWN;
2633 goto out;
2634 }
2635
Johannes Berg79c97e92009-07-07 03:56:12 +02002636 err = rdev->ops->add_mpath(&rdev->wiphy, dev, dst, next_hop);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002637
2638 out:
Johannes Berg79c97e92009-07-07 03:56:12 +02002639 cfg80211_unlock_rdev(rdev);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002640 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01002641 out_rtnl:
2642 rtnl_unlock();
2643
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002644 return err;
2645}
2646
2647static int nl80211_del_mpath(struct sk_buff *skb, struct genl_info *info)
2648{
Johannes Berg79c97e92009-07-07 03:56:12 +02002649 struct cfg80211_registered_device *rdev;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002650 int err;
2651 struct net_device *dev;
2652 u8 *dst = NULL;
2653
2654 if (info->attrs[NL80211_ATTR_MAC])
2655 dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
2656
Johannes Berg3b858752009-03-12 09:55:09 +01002657 rtnl_lock();
2658
Johannes Berg463d0182009-07-14 00:33:35 +02002659 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002660 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01002661 goto out_rtnl;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002662
Johannes Berg79c97e92009-07-07 03:56:12 +02002663 if (!rdev->ops->del_mpath) {
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002664 err = -EOPNOTSUPP;
2665 goto out;
2666 }
2667
Johannes Berg79c97e92009-07-07 03:56:12 +02002668 err = rdev->ops->del_mpath(&rdev->wiphy, dev, dst);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002669
2670 out:
Johannes Berg79c97e92009-07-07 03:56:12 +02002671 cfg80211_unlock_rdev(rdev);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002672 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01002673 out_rtnl:
2674 rtnl_unlock();
2675
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002676 return err;
2677}
2678
Jouni Malinen9f1ba902008-08-07 20:07:01 +03002679static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info)
2680{
Johannes Berg79c97e92009-07-07 03:56:12 +02002681 struct cfg80211_registered_device *rdev;
Jouni Malinen9f1ba902008-08-07 20:07:01 +03002682 int err;
2683 struct net_device *dev;
2684 struct bss_parameters params;
2685
2686 memset(&params, 0, sizeof(params));
2687 /* default to not changing parameters */
2688 params.use_cts_prot = -1;
2689 params.use_short_preamble = -1;
2690 params.use_short_slot_time = -1;
Felix Fietkaufd8aaaf2010-04-27 01:23:35 +02002691 params.ap_isolate = -1;
Jouni Malinen9f1ba902008-08-07 20:07:01 +03002692
2693 if (info->attrs[NL80211_ATTR_BSS_CTS_PROT])
2694 params.use_cts_prot =
2695 nla_get_u8(info->attrs[NL80211_ATTR_BSS_CTS_PROT]);
2696 if (info->attrs[NL80211_ATTR_BSS_SHORT_PREAMBLE])
2697 params.use_short_preamble =
2698 nla_get_u8(info->attrs[NL80211_ATTR_BSS_SHORT_PREAMBLE]);
2699 if (info->attrs[NL80211_ATTR_BSS_SHORT_SLOT_TIME])
2700 params.use_short_slot_time =
2701 nla_get_u8(info->attrs[NL80211_ATTR_BSS_SHORT_SLOT_TIME]);
Jouni Malinen90c97a02008-10-30 16:59:22 +02002702 if (info->attrs[NL80211_ATTR_BSS_BASIC_RATES]) {
2703 params.basic_rates =
2704 nla_data(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]);
2705 params.basic_rates_len =
2706 nla_len(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]);
2707 }
Felix Fietkaufd8aaaf2010-04-27 01:23:35 +02002708 if (info->attrs[NL80211_ATTR_AP_ISOLATE])
2709 params.ap_isolate = !!nla_get_u8(info->attrs[NL80211_ATTR_AP_ISOLATE]);
Jouni Malinen9f1ba902008-08-07 20:07:01 +03002710
Johannes Berg3b858752009-03-12 09:55:09 +01002711 rtnl_lock();
2712
Johannes Berg463d0182009-07-14 00:33:35 +02002713 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Jouni Malinen9f1ba902008-08-07 20:07:01 +03002714 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01002715 goto out_rtnl;
Jouni Malinen9f1ba902008-08-07 20:07:01 +03002716
Johannes Berg79c97e92009-07-07 03:56:12 +02002717 if (!rdev->ops->change_bss) {
Jouni Malinen9f1ba902008-08-07 20:07:01 +03002718 err = -EOPNOTSUPP;
2719 goto out;
2720 }
2721
Johannes Berg074ac8d2010-09-16 14:58:22 +02002722 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
2723 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) {
Jouni Malineneec60b02009-03-20 21:21:19 +02002724 err = -EOPNOTSUPP;
2725 goto out;
2726 }
2727
Johannes Berg79c97e92009-07-07 03:56:12 +02002728 err = rdev->ops->change_bss(&rdev->wiphy, dev, &params);
Jouni Malinen9f1ba902008-08-07 20:07:01 +03002729
2730 out:
Johannes Berg79c97e92009-07-07 03:56:12 +02002731 cfg80211_unlock_rdev(rdev);
Jouni Malinen9f1ba902008-08-07 20:07:01 +03002732 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01002733 out_rtnl:
2734 rtnl_unlock();
2735
Jouni Malinen9f1ba902008-08-07 20:07:01 +03002736 return err;
2737}
2738
Alexey Dobriyanb54452b2010-02-18 08:14:31 +00002739static const struct nla_policy reg_rule_policy[NL80211_REG_RULE_ATTR_MAX + 1] = {
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07002740 [NL80211_ATTR_REG_RULE_FLAGS] = { .type = NLA_U32 },
2741 [NL80211_ATTR_FREQ_RANGE_START] = { .type = NLA_U32 },
2742 [NL80211_ATTR_FREQ_RANGE_END] = { .type = NLA_U32 },
2743 [NL80211_ATTR_FREQ_RANGE_MAX_BW] = { .type = NLA_U32 },
2744 [NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN] = { .type = NLA_U32 },
2745 [NL80211_ATTR_POWER_RULE_MAX_EIRP] = { .type = NLA_U32 },
2746};
2747
2748static int parse_reg_rule(struct nlattr *tb[],
2749 struct ieee80211_reg_rule *reg_rule)
2750{
2751 struct ieee80211_freq_range *freq_range = &reg_rule->freq_range;
2752 struct ieee80211_power_rule *power_rule = &reg_rule->power_rule;
2753
2754 if (!tb[NL80211_ATTR_REG_RULE_FLAGS])
2755 return -EINVAL;
2756 if (!tb[NL80211_ATTR_FREQ_RANGE_START])
2757 return -EINVAL;
2758 if (!tb[NL80211_ATTR_FREQ_RANGE_END])
2759 return -EINVAL;
2760 if (!tb[NL80211_ATTR_FREQ_RANGE_MAX_BW])
2761 return -EINVAL;
2762 if (!tb[NL80211_ATTR_POWER_RULE_MAX_EIRP])
2763 return -EINVAL;
2764
2765 reg_rule->flags = nla_get_u32(tb[NL80211_ATTR_REG_RULE_FLAGS]);
2766
2767 freq_range->start_freq_khz =
2768 nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_START]);
2769 freq_range->end_freq_khz =
2770 nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_END]);
2771 freq_range->max_bandwidth_khz =
2772 nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_MAX_BW]);
2773
2774 power_rule->max_eirp =
2775 nla_get_u32(tb[NL80211_ATTR_POWER_RULE_MAX_EIRP]);
2776
2777 if (tb[NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN])
2778 power_rule->max_antenna_gain =
2779 nla_get_u32(tb[NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN]);
2780
2781 return 0;
2782}
2783
2784static int nl80211_req_set_reg(struct sk_buff *skb, struct genl_info *info)
2785{
2786 int r;
2787 char *data = NULL;
2788
Luis R. Rodriguez80778f12009-02-21 00:04:22 -05002789 /*
2790 * You should only get this when cfg80211 hasn't yet initialized
2791 * completely when built-in to the kernel right between the time
2792 * window between nl80211_init() and regulatory_init(), if that is
2793 * even possible.
2794 */
2795 mutex_lock(&cfg80211_mutex);
2796 if (unlikely(!cfg80211_regdomain)) {
Luis R. Rodriguezfe33eb32009-02-21 00:04:30 -05002797 mutex_unlock(&cfg80211_mutex);
2798 return -EINPROGRESS;
Luis R. Rodriguez80778f12009-02-21 00:04:22 -05002799 }
Luis R. Rodriguezfe33eb32009-02-21 00:04:30 -05002800 mutex_unlock(&cfg80211_mutex);
Luis R. Rodriguez80778f12009-02-21 00:04:22 -05002801
Luis R. Rodriguezfe33eb32009-02-21 00:04:30 -05002802 if (!info->attrs[NL80211_ATTR_REG_ALPHA2])
2803 return -EINVAL;
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07002804
2805 data = nla_data(info->attrs[NL80211_ATTR_REG_ALPHA2]);
2806
Luis R. Rodriguezfe33eb32009-02-21 00:04:30 -05002807 r = regulatory_hint_user(data);
2808
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07002809 return r;
2810}
2811
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002812static int nl80211_get_mesh_params(struct sk_buff *skb,
2813 struct genl_info *info)
2814{
Johannes Berg79c97e92009-07-07 03:56:12 +02002815 struct cfg80211_registered_device *rdev;
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002816 struct mesh_config cur_params;
2817 int err;
2818 struct net_device *dev;
2819 void *hdr;
2820 struct nlattr *pinfoattr;
2821 struct sk_buff *msg;
2822
Johannes Berg3b858752009-03-12 09:55:09 +01002823 rtnl_lock();
2824
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002825 /* Look up our device */
Johannes Berg463d0182009-07-14 00:33:35 +02002826 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002827 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01002828 goto out_rtnl;
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002829
Johannes Berg79c97e92009-07-07 03:56:12 +02002830 if (!rdev->ops->get_mesh_params) {
Jouni Malinenf3f92582009-03-20 17:57:36 +02002831 err = -EOPNOTSUPP;
2832 goto out;
2833 }
2834
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002835 /* Get the mesh params */
Johannes Berg79c97e92009-07-07 03:56:12 +02002836 err = rdev->ops->get_mesh_params(&rdev->wiphy, dev, &cur_params);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002837 if (err)
2838 goto out;
2839
2840 /* Draw up a netlink message to send back */
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07002841 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002842 if (!msg) {
2843 err = -ENOBUFS;
2844 goto out;
2845 }
2846 hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
2847 NL80211_CMD_GET_MESH_PARAMS);
2848 if (!hdr)
2849 goto nla_put_failure;
2850 pinfoattr = nla_nest_start(msg, NL80211_ATTR_MESH_PARAMS);
2851 if (!pinfoattr)
2852 goto nla_put_failure;
2853 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
2854 NLA_PUT_U16(msg, NL80211_MESHCONF_RETRY_TIMEOUT,
2855 cur_params.dot11MeshRetryTimeout);
2856 NLA_PUT_U16(msg, NL80211_MESHCONF_CONFIRM_TIMEOUT,
2857 cur_params.dot11MeshConfirmTimeout);
2858 NLA_PUT_U16(msg, NL80211_MESHCONF_HOLDING_TIMEOUT,
2859 cur_params.dot11MeshHoldingTimeout);
2860 NLA_PUT_U16(msg, NL80211_MESHCONF_MAX_PEER_LINKS,
2861 cur_params.dot11MeshMaxPeerLinks);
2862 NLA_PUT_U8(msg, NL80211_MESHCONF_MAX_RETRIES,
2863 cur_params.dot11MeshMaxRetries);
2864 NLA_PUT_U8(msg, NL80211_MESHCONF_TTL,
2865 cur_params.dot11MeshTTL);
2866 NLA_PUT_U8(msg, NL80211_MESHCONF_AUTO_OPEN_PLINKS,
2867 cur_params.auto_open_plinks);
2868 NLA_PUT_U8(msg, NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES,
2869 cur_params.dot11MeshHWMPmaxPREQretries);
2870 NLA_PUT_U32(msg, NL80211_MESHCONF_PATH_REFRESH_TIME,
2871 cur_params.path_refresh_time);
2872 NLA_PUT_U16(msg, NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT,
2873 cur_params.min_discovery_timeout);
2874 NLA_PUT_U32(msg, NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT,
2875 cur_params.dot11MeshHWMPactivePathTimeout);
2876 NLA_PUT_U16(msg, NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL,
2877 cur_params.dot11MeshHWMPpreqMinInterval);
2878 NLA_PUT_U16(msg, NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME,
2879 cur_params.dot11MeshHWMPnetDiameterTraversalTime);
Rui Paulo63c57232009-11-09 23:46:57 +00002880 NLA_PUT_U8(msg, NL80211_MESHCONF_HWMP_ROOTMODE,
2881 cur_params.dot11MeshHWMPRootMode);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002882 nla_nest_end(msg, pinfoattr);
2883 genlmsg_end(msg, hdr);
Johannes Berg134e6372009-07-10 09:51:34 +00002884 err = genlmsg_reply(msg, info);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002885 goto out;
2886
Johannes Berg3b858752009-03-12 09:55:09 +01002887 nla_put_failure:
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002888 genlmsg_cancel(msg, hdr);
Yuri Ershovd080e272010-06-29 15:08:07 +04002889 nlmsg_free(msg);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002890 err = -EMSGSIZE;
Johannes Berg3b858752009-03-12 09:55:09 +01002891 out:
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002892 /* Cleanup */
Johannes Berg79c97e92009-07-07 03:56:12 +02002893 cfg80211_unlock_rdev(rdev);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002894 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01002895 out_rtnl:
2896 rtnl_unlock();
2897
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002898 return err;
2899}
2900
2901#define FILL_IN_MESH_PARAM_IF_SET(table, cfg, param, mask, attr_num, nla_fn) \
2902do {\
2903 if (table[attr_num]) {\
2904 cfg.param = nla_fn(table[attr_num]); \
2905 mask |= (1 << (attr_num - 1)); \
2906 } \
2907} while (0);\
2908
Alexey Dobriyanb54452b2010-02-18 08:14:31 +00002909static const struct nla_policy nl80211_meshconf_params_policy[NL80211_MESHCONF_ATTR_MAX+1] = {
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002910 [NL80211_MESHCONF_RETRY_TIMEOUT] = { .type = NLA_U16 },
2911 [NL80211_MESHCONF_CONFIRM_TIMEOUT] = { .type = NLA_U16 },
2912 [NL80211_MESHCONF_HOLDING_TIMEOUT] = { .type = NLA_U16 },
2913 [NL80211_MESHCONF_MAX_PEER_LINKS] = { .type = NLA_U16 },
2914 [NL80211_MESHCONF_MAX_RETRIES] = { .type = NLA_U8 },
2915 [NL80211_MESHCONF_TTL] = { .type = NLA_U8 },
2916 [NL80211_MESHCONF_AUTO_OPEN_PLINKS] = { .type = NLA_U8 },
2917
2918 [NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES] = { .type = NLA_U8 },
2919 [NL80211_MESHCONF_PATH_REFRESH_TIME] = { .type = NLA_U32 },
2920 [NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT] = { .type = NLA_U16 },
2921 [NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT] = { .type = NLA_U32 },
2922 [NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL] = { .type = NLA_U16 },
2923 [NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME] = { .type = NLA_U16 },
2924};
2925
2926static int nl80211_set_mesh_params(struct sk_buff *skb, struct genl_info *info)
2927{
2928 int err;
2929 u32 mask;
Johannes Berg79c97e92009-07-07 03:56:12 +02002930 struct cfg80211_registered_device *rdev;
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002931 struct net_device *dev;
2932 struct mesh_config cfg;
2933 struct nlattr *tb[NL80211_MESHCONF_ATTR_MAX + 1];
2934 struct nlattr *parent_attr;
2935
2936 parent_attr = info->attrs[NL80211_ATTR_MESH_PARAMS];
2937 if (!parent_attr)
2938 return -EINVAL;
2939 if (nla_parse_nested(tb, NL80211_MESHCONF_ATTR_MAX,
2940 parent_attr, nl80211_meshconf_params_policy))
2941 return -EINVAL;
2942
Johannes Berg3b858752009-03-12 09:55:09 +01002943 rtnl_lock();
2944
Johannes Berg463d0182009-07-14 00:33:35 +02002945 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002946 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01002947 goto out_rtnl;
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002948
Johannes Berg79c97e92009-07-07 03:56:12 +02002949 if (!rdev->ops->set_mesh_params) {
Jouni Malinenf3f92582009-03-20 17:57:36 +02002950 err = -EOPNOTSUPP;
2951 goto out;
2952 }
2953
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002954 /* This makes sure that there aren't more than 32 mesh config
2955 * parameters (otherwise our bitfield scheme would not work.) */
2956 BUILD_BUG_ON(NL80211_MESHCONF_ATTR_MAX > 32);
2957
2958 /* Fill in the params struct */
2959 mask = 0;
2960 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshRetryTimeout,
2961 mask, NL80211_MESHCONF_RETRY_TIMEOUT, nla_get_u16);
2962 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshConfirmTimeout,
2963 mask, NL80211_MESHCONF_CONFIRM_TIMEOUT, nla_get_u16);
2964 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHoldingTimeout,
2965 mask, NL80211_MESHCONF_HOLDING_TIMEOUT, nla_get_u16);
2966 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshMaxPeerLinks,
2967 mask, NL80211_MESHCONF_MAX_PEER_LINKS, nla_get_u16);
2968 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshMaxRetries,
2969 mask, NL80211_MESHCONF_MAX_RETRIES, nla_get_u8);
2970 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshTTL,
2971 mask, NL80211_MESHCONF_TTL, nla_get_u8);
2972 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, auto_open_plinks,
2973 mask, NL80211_MESHCONF_AUTO_OPEN_PLINKS, nla_get_u8);
2974 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPmaxPREQretries,
2975 mask, NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES,
2976 nla_get_u8);
2977 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, path_refresh_time,
2978 mask, NL80211_MESHCONF_PATH_REFRESH_TIME, nla_get_u32);
2979 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, min_discovery_timeout,
2980 mask, NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT,
2981 nla_get_u16);
2982 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPactivePathTimeout,
2983 mask, NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT,
2984 nla_get_u32);
2985 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPpreqMinInterval,
2986 mask, NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL,
2987 nla_get_u16);
2988 FILL_IN_MESH_PARAM_IF_SET(tb, cfg,
2989 dot11MeshHWMPnetDiameterTraversalTime,
2990 mask, NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME,
2991 nla_get_u16);
Rui Paulo63c57232009-11-09 23:46:57 +00002992 FILL_IN_MESH_PARAM_IF_SET(tb, cfg,
2993 dot11MeshHWMPRootMode, mask,
2994 NL80211_MESHCONF_HWMP_ROOTMODE,
2995 nla_get_u8);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002996
2997 /* Apply changes */
Johannes Berg79c97e92009-07-07 03:56:12 +02002998 err = rdev->ops->set_mesh_params(&rdev->wiphy, dev, &cfg, mask);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002999
Jouni Malinenf3f92582009-03-20 17:57:36 +02003000 out:
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003001 /* cleanup */
Johannes Berg79c97e92009-07-07 03:56:12 +02003002 cfg80211_unlock_rdev(rdev);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003003 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01003004 out_rtnl:
3005 rtnl_unlock();
3006
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003007 return err;
3008}
3009
3010#undef FILL_IN_MESH_PARAM_IF_SET
3011
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08003012static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info)
3013{
3014 struct sk_buff *msg;
3015 void *hdr = NULL;
3016 struct nlattr *nl_reg_rules;
3017 unsigned int i;
3018 int err = -EINVAL;
3019
Luis R. Rodrigueza1794392009-02-21 00:04:21 -05003020 mutex_lock(&cfg80211_mutex);
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08003021
3022 if (!cfg80211_regdomain)
3023 goto out;
3024
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07003025 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08003026 if (!msg) {
3027 err = -ENOBUFS;
3028 goto out;
3029 }
3030
3031 hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
3032 NL80211_CMD_GET_REG);
3033 if (!hdr)
3034 goto nla_put_failure;
3035
3036 NLA_PUT_STRING(msg, NL80211_ATTR_REG_ALPHA2,
3037 cfg80211_regdomain->alpha2);
3038
3039 nl_reg_rules = nla_nest_start(msg, NL80211_ATTR_REG_RULES);
3040 if (!nl_reg_rules)
3041 goto nla_put_failure;
3042
3043 for (i = 0; i < cfg80211_regdomain->n_reg_rules; i++) {
3044 struct nlattr *nl_reg_rule;
3045 const struct ieee80211_reg_rule *reg_rule;
3046 const struct ieee80211_freq_range *freq_range;
3047 const struct ieee80211_power_rule *power_rule;
3048
3049 reg_rule = &cfg80211_regdomain->reg_rules[i];
3050 freq_range = &reg_rule->freq_range;
3051 power_rule = &reg_rule->power_rule;
3052
3053 nl_reg_rule = nla_nest_start(msg, i);
3054 if (!nl_reg_rule)
3055 goto nla_put_failure;
3056
3057 NLA_PUT_U32(msg, NL80211_ATTR_REG_RULE_FLAGS,
3058 reg_rule->flags);
3059 NLA_PUT_U32(msg, NL80211_ATTR_FREQ_RANGE_START,
3060 freq_range->start_freq_khz);
3061 NLA_PUT_U32(msg, NL80211_ATTR_FREQ_RANGE_END,
3062 freq_range->end_freq_khz);
3063 NLA_PUT_U32(msg, NL80211_ATTR_FREQ_RANGE_MAX_BW,
3064 freq_range->max_bandwidth_khz);
3065 NLA_PUT_U32(msg, NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN,
3066 power_rule->max_antenna_gain);
3067 NLA_PUT_U32(msg, NL80211_ATTR_POWER_RULE_MAX_EIRP,
3068 power_rule->max_eirp);
3069
3070 nla_nest_end(msg, nl_reg_rule);
3071 }
3072
3073 nla_nest_end(msg, nl_reg_rules);
3074
3075 genlmsg_end(msg, hdr);
Johannes Berg134e6372009-07-10 09:51:34 +00003076 err = genlmsg_reply(msg, info);
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08003077 goto out;
3078
3079nla_put_failure:
3080 genlmsg_cancel(msg, hdr);
Yuri Ershovd080e272010-06-29 15:08:07 +04003081 nlmsg_free(msg);
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08003082 err = -EMSGSIZE;
3083out:
Luis R. Rodrigueza1794392009-02-21 00:04:21 -05003084 mutex_unlock(&cfg80211_mutex);
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08003085 return err;
3086}
3087
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003088static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info)
3089{
3090 struct nlattr *tb[NL80211_REG_RULE_ATTR_MAX + 1];
3091 struct nlattr *nl_reg_rule;
3092 char *alpha2 = NULL;
3093 int rem_reg_rules = 0, r = 0;
3094 u32 num_rules = 0, rule_idx = 0, size_of_regd;
3095 struct ieee80211_regdomain *rd = NULL;
3096
3097 if (!info->attrs[NL80211_ATTR_REG_ALPHA2])
3098 return -EINVAL;
3099
3100 if (!info->attrs[NL80211_ATTR_REG_RULES])
3101 return -EINVAL;
3102
3103 alpha2 = nla_data(info->attrs[NL80211_ATTR_REG_ALPHA2]);
3104
3105 nla_for_each_nested(nl_reg_rule, info->attrs[NL80211_ATTR_REG_RULES],
3106 rem_reg_rules) {
3107 num_rules++;
3108 if (num_rules > NL80211_MAX_SUPP_REG_RULES)
Luis R. Rodriguez4776c6e2009-05-13 17:04:39 -04003109 return -EINVAL;
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003110 }
3111
Luis R. Rodriguez61405e92009-05-13 17:04:41 -04003112 mutex_lock(&cfg80211_mutex);
3113
Luis R. Rodriguezd0e18f82009-05-13 17:04:40 -04003114 if (!reg_is_valid_request(alpha2)) {
3115 r = -EINVAL;
3116 goto bad_reg;
3117 }
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003118
3119 size_of_regd = sizeof(struct ieee80211_regdomain) +
3120 (num_rules * sizeof(struct ieee80211_reg_rule));
3121
3122 rd = kzalloc(size_of_regd, GFP_KERNEL);
Luis R. Rodriguezd0e18f82009-05-13 17:04:40 -04003123 if (!rd) {
3124 r = -ENOMEM;
3125 goto bad_reg;
3126 }
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003127
3128 rd->n_reg_rules = num_rules;
3129 rd->alpha2[0] = alpha2[0];
3130 rd->alpha2[1] = alpha2[1];
3131
3132 nla_for_each_nested(nl_reg_rule, info->attrs[NL80211_ATTR_REG_RULES],
3133 rem_reg_rules) {
3134 nla_parse(tb, NL80211_REG_RULE_ATTR_MAX,
3135 nla_data(nl_reg_rule), nla_len(nl_reg_rule),
3136 reg_rule_policy);
3137 r = parse_reg_rule(tb, &rd->reg_rules[rule_idx]);
3138 if (r)
3139 goto bad_reg;
3140
3141 rule_idx++;
3142
Luis R. Rodriguezd0e18f82009-05-13 17:04:40 -04003143 if (rule_idx > NL80211_MAX_SUPP_REG_RULES) {
3144 r = -EINVAL;
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003145 goto bad_reg;
Luis R. Rodriguezd0e18f82009-05-13 17:04:40 -04003146 }
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003147 }
3148
3149 BUG_ON(rule_idx != num_rules);
3150
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003151 r = set_regdom(rd);
Luis R. Rodriguez61405e92009-05-13 17:04:41 -04003152
Luis R. Rodrigueza1794392009-02-21 00:04:21 -05003153 mutex_unlock(&cfg80211_mutex);
Luis R. Rodriguezd0e18f82009-05-13 17:04:40 -04003154
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003155 return r;
3156
Johannes Bergd2372b32008-10-24 20:32:20 +02003157 bad_reg:
Luis R. Rodriguez61405e92009-05-13 17:04:41 -04003158 mutex_unlock(&cfg80211_mutex);
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003159 kfree(rd);
Luis R. Rodriguezd0e18f82009-05-13 17:04:40 -04003160 return r;
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003161}
3162
Johannes Berg83f5e2c2009-06-17 17:41:49 +02003163static int validate_scan_freqs(struct nlattr *freqs)
3164{
3165 struct nlattr *attr1, *attr2;
3166 int n_channels = 0, tmp1, tmp2;
3167
3168 nla_for_each_nested(attr1, freqs, tmp1) {
3169 n_channels++;
3170 /*
3171 * Some hardware has a limited channel list for
3172 * scanning, and it is pretty much nonsensical
3173 * to scan for a channel twice, so disallow that
3174 * and don't require drivers to check that the
3175 * channel list they get isn't longer than what
3176 * they can scan, as long as they can scan all
3177 * the channels they registered at once.
3178 */
3179 nla_for_each_nested(attr2, freqs, tmp2)
3180 if (attr1 != attr2 &&
3181 nla_get_u32(attr1) == nla_get_u32(attr2))
3182 return 0;
3183 }
3184
3185 return n_channels;
3186}
3187
Johannes Berg2a519312009-02-10 21:25:55 +01003188static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
3189{
Johannes Berg79c97e92009-07-07 03:56:12 +02003190 struct cfg80211_registered_device *rdev;
Johannes Berg2a519312009-02-10 21:25:55 +01003191 struct net_device *dev;
3192 struct cfg80211_scan_request *request;
3193 struct cfg80211_ssid *ssid;
3194 struct ieee80211_channel *channel;
3195 struct nlattr *attr;
3196 struct wiphy *wiphy;
Johannes Berg83f5e2c2009-06-17 17:41:49 +02003197 int err, tmp, n_ssids = 0, n_channels, i;
Johannes Berg2a519312009-02-10 21:25:55 +01003198 enum ieee80211_band band;
Jouni Malinen70692ad2009-02-16 19:39:13 +02003199 size_t ie_len;
Johannes Berg2a519312009-02-10 21:25:55 +01003200
Johannes Bergf4a11bb2009-03-27 12:40:28 +01003201 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
3202 return -EINVAL;
3203
Johannes Berg3b858752009-03-12 09:55:09 +01003204 rtnl_lock();
3205
Johannes Berg463d0182009-07-14 00:33:35 +02003206 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Johannes Berg2a519312009-02-10 21:25:55 +01003207 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01003208 goto out_rtnl;
Johannes Berg2a519312009-02-10 21:25:55 +01003209
Johannes Berg79c97e92009-07-07 03:56:12 +02003210 wiphy = &rdev->wiphy;
Johannes Berg2a519312009-02-10 21:25:55 +01003211
Johannes Berg79c97e92009-07-07 03:56:12 +02003212 if (!rdev->ops->scan) {
Johannes Berg2a519312009-02-10 21:25:55 +01003213 err = -EOPNOTSUPP;
3214 goto out;
3215 }
3216
Jouni Malinen35a8efe2009-03-20 21:21:18 +02003217 if (!netif_running(dev)) {
3218 err = -ENETDOWN;
3219 goto out;
3220 }
3221
Johannes Berg79c97e92009-07-07 03:56:12 +02003222 if (rdev->scan_req) {
Johannes Berg2a519312009-02-10 21:25:55 +01003223 err = -EBUSY;
Johannes Berg3b858752009-03-12 09:55:09 +01003224 goto out;
Johannes Berg2a519312009-02-10 21:25:55 +01003225 }
3226
3227 if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
Johannes Berg83f5e2c2009-06-17 17:41:49 +02003228 n_channels = validate_scan_freqs(
3229 info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]);
Johannes Berg2a519312009-02-10 21:25:55 +01003230 if (!n_channels) {
3231 err = -EINVAL;
Johannes Berg3b858752009-03-12 09:55:09 +01003232 goto out;
Johannes Berg2a519312009-02-10 21:25:55 +01003233 }
3234 } else {
Johannes Berg83f5e2c2009-06-17 17:41:49 +02003235 n_channels = 0;
3236
Johannes Berg2a519312009-02-10 21:25:55 +01003237 for (band = 0; band < IEEE80211_NUM_BANDS; band++)
3238 if (wiphy->bands[band])
3239 n_channels += wiphy->bands[band]->n_channels;
3240 }
3241
3242 if (info->attrs[NL80211_ATTR_SCAN_SSIDS])
3243 nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS], tmp)
3244 n_ssids++;
3245
3246 if (n_ssids > wiphy->max_scan_ssids) {
3247 err = -EINVAL;
Johannes Berg3b858752009-03-12 09:55:09 +01003248 goto out;
Johannes Berg2a519312009-02-10 21:25:55 +01003249 }
3250
Jouni Malinen70692ad2009-02-16 19:39:13 +02003251 if (info->attrs[NL80211_ATTR_IE])
3252 ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
3253 else
3254 ie_len = 0;
3255
Johannes Berg18a83652009-03-31 12:12:05 +02003256 if (ie_len > wiphy->max_scan_ie_len) {
3257 err = -EINVAL;
3258 goto out;
3259 }
3260
Johannes Berg2a519312009-02-10 21:25:55 +01003261 request = kzalloc(sizeof(*request)
3262 + sizeof(*ssid) * n_ssids
Jouni Malinen70692ad2009-02-16 19:39:13 +02003263 + sizeof(channel) * n_channels
3264 + ie_len, GFP_KERNEL);
Johannes Berg2a519312009-02-10 21:25:55 +01003265 if (!request) {
3266 err = -ENOMEM;
Johannes Berg3b858752009-03-12 09:55:09 +01003267 goto out;
Johannes Berg2a519312009-02-10 21:25:55 +01003268 }
3269
Johannes Berg2a519312009-02-10 21:25:55 +01003270 if (n_ssids)
Johannes Berg5ba63532009-08-07 17:54:07 +02003271 request->ssids = (void *)&request->channels[n_channels];
Johannes Berg2a519312009-02-10 21:25:55 +01003272 request->n_ssids = n_ssids;
Jouni Malinen70692ad2009-02-16 19:39:13 +02003273 if (ie_len) {
3274 if (request->ssids)
3275 request->ie = (void *)(request->ssids + n_ssids);
3276 else
3277 request->ie = (void *)(request->channels + n_channels);
3278 }
Johannes Berg2a519312009-02-10 21:25:55 +01003279
Johannes Berg584991d2009-11-02 13:32:03 +01003280 i = 0;
Johannes Berg2a519312009-02-10 21:25:55 +01003281 if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
3282 /* user specified, bail out if channel not found */
Johannes Berg2a519312009-02-10 21:25:55 +01003283 nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_FREQUENCIES], tmp) {
Johannes Berg584991d2009-11-02 13:32:03 +01003284 struct ieee80211_channel *chan;
3285
3286 chan = ieee80211_get_channel(wiphy, nla_get_u32(attr));
3287
3288 if (!chan) {
Johannes Berg2a519312009-02-10 21:25:55 +01003289 err = -EINVAL;
3290 goto out_free;
3291 }
Johannes Berg584991d2009-11-02 13:32:03 +01003292
3293 /* ignore disabled channels */
3294 if (chan->flags & IEEE80211_CHAN_DISABLED)
3295 continue;
3296
3297 request->channels[i] = chan;
Johannes Berg2a519312009-02-10 21:25:55 +01003298 i++;
3299 }
3300 } else {
3301 /* all channels */
Johannes Berg2a519312009-02-10 21:25:55 +01003302 for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
3303 int j;
3304 if (!wiphy->bands[band])
3305 continue;
3306 for (j = 0; j < wiphy->bands[band]->n_channels; j++) {
Johannes Berg584991d2009-11-02 13:32:03 +01003307 struct ieee80211_channel *chan;
3308
3309 chan = &wiphy->bands[band]->channels[j];
3310
3311 if (chan->flags & IEEE80211_CHAN_DISABLED)
3312 continue;
3313
3314 request->channels[i] = chan;
Johannes Berg2a519312009-02-10 21:25:55 +01003315 i++;
3316 }
3317 }
3318 }
3319
Johannes Berg584991d2009-11-02 13:32:03 +01003320 if (!i) {
3321 err = -EINVAL;
3322 goto out_free;
3323 }
3324
3325 request->n_channels = i;
3326
Johannes Berg2a519312009-02-10 21:25:55 +01003327 i = 0;
3328 if (info->attrs[NL80211_ATTR_SCAN_SSIDS]) {
3329 nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS], tmp) {
3330 if (request->ssids[i].ssid_len > IEEE80211_MAX_SSID_LEN) {
3331 err = -EINVAL;
3332 goto out_free;
3333 }
3334 memcpy(request->ssids[i].ssid, nla_data(attr), nla_len(attr));
3335 request->ssids[i].ssid_len = nla_len(attr);
3336 i++;
3337 }
3338 }
3339
Jouni Malinen70692ad2009-02-16 19:39:13 +02003340 if (info->attrs[NL80211_ATTR_IE]) {
3341 request->ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
Johannes Bergde95a542009-04-01 11:58:36 +02003342 memcpy((void *)request->ie,
3343 nla_data(info->attrs[NL80211_ATTR_IE]),
Jouni Malinen70692ad2009-02-16 19:39:13 +02003344 request->ie_len);
3345 }
3346
Johannes Berg463d0182009-07-14 00:33:35 +02003347 request->dev = dev;
Johannes Berg79c97e92009-07-07 03:56:12 +02003348 request->wiphy = &rdev->wiphy;
Johannes Berg2a519312009-02-10 21:25:55 +01003349
Johannes Berg79c97e92009-07-07 03:56:12 +02003350 rdev->scan_req = request;
3351 err = rdev->ops->scan(&rdev->wiphy, dev, request);
Johannes Berg2a519312009-02-10 21:25:55 +01003352
Johannes Berg463d0182009-07-14 00:33:35 +02003353 if (!err) {
Johannes Berg79c97e92009-07-07 03:56:12 +02003354 nl80211_send_scan_start(rdev, dev);
Johannes Berg463d0182009-07-14 00:33:35 +02003355 dev_hold(dev);
3356 }
Johannes Berga538e2d2009-06-16 19:56:42 +02003357
Johannes Berg2a519312009-02-10 21:25:55 +01003358 out_free:
3359 if (err) {
Johannes Berg79c97e92009-07-07 03:56:12 +02003360 rdev->scan_req = NULL;
Johannes Berg2a519312009-02-10 21:25:55 +01003361 kfree(request);
3362 }
Johannes Berg2a519312009-02-10 21:25:55 +01003363 out:
Johannes Berg79c97e92009-07-07 03:56:12 +02003364 cfg80211_unlock_rdev(rdev);
Johannes Berg2a519312009-02-10 21:25:55 +01003365 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01003366 out_rtnl:
3367 rtnl_unlock();
3368
Johannes Berg2a519312009-02-10 21:25:55 +01003369 return err;
3370}
3371
3372static int nl80211_send_bss(struct sk_buff *msg, u32 pid, u32 seq, int flags,
3373 struct cfg80211_registered_device *rdev,
Johannes Berg48ab9052009-07-10 18:42:31 +02003374 struct wireless_dev *wdev,
3375 struct cfg80211_internal_bss *intbss)
Johannes Berg2a519312009-02-10 21:25:55 +01003376{
Johannes Berg48ab9052009-07-10 18:42:31 +02003377 struct cfg80211_bss *res = &intbss->pub;
Johannes Berg2a519312009-02-10 21:25:55 +01003378 void *hdr;
3379 struct nlattr *bss;
Johannes Berg48ab9052009-07-10 18:42:31 +02003380 int i;
3381
3382 ASSERT_WDEV_LOCK(wdev);
Johannes Berg2a519312009-02-10 21:25:55 +01003383
3384 hdr = nl80211hdr_put(msg, pid, seq, flags,
3385 NL80211_CMD_NEW_SCAN_RESULTS);
3386 if (!hdr)
3387 return -1;
3388
Johannes Bergf5ea9122009-08-07 16:17:38 +02003389 NLA_PUT_U32(msg, NL80211_ATTR_GENERATION, rdev->bss_generation);
Johannes Berg48ab9052009-07-10 18:42:31 +02003390 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, wdev->netdev->ifindex);
Johannes Berg2a519312009-02-10 21:25:55 +01003391
3392 bss = nla_nest_start(msg, NL80211_ATTR_BSS);
3393 if (!bss)
3394 goto nla_put_failure;
3395 if (!is_zero_ether_addr(res->bssid))
3396 NLA_PUT(msg, NL80211_BSS_BSSID, ETH_ALEN, res->bssid);
3397 if (res->information_elements && res->len_information_elements)
3398 NLA_PUT(msg, NL80211_BSS_INFORMATION_ELEMENTS,
3399 res->len_information_elements,
3400 res->information_elements);
Jouni Malinen34a6edd2010-01-06 16:19:24 +02003401 if (res->beacon_ies && res->len_beacon_ies &&
3402 res->beacon_ies != res->information_elements)
3403 NLA_PUT(msg, NL80211_BSS_BEACON_IES,
3404 res->len_beacon_ies, res->beacon_ies);
Johannes Berg2a519312009-02-10 21:25:55 +01003405 if (res->tsf)
3406 NLA_PUT_U64(msg, NL80211_BSS_TSF, res->tsf);
3407 if (res->beacon_interval)
3408 NLA_PUT_U16(msg, NL80211_BSS_BEACON_INTERVAL, res->beacon_interval);
3409 NLA_PUT_U16(msg, NL80211_BSS_CAPABILITY, res->capability);
3410 NLA_PUT_U32(msg, NL80211_BSS_FREQUENCY, res->channel->center_freq);
Holger Schurig7c896062009-09-24 12:21:01 +02003411 NLA_PUT_U32(msg, NL80211_BSS_SEEN_MS_AGO,
3412 jiffies_to_msecs(jiffies - intbss->ts));
Johannes Berg2a519312009-02-10 21:25:55 +01003413
Johannes Berg77965c92009-02-18 18:45:06 +01003414 switch (rdev->wiphy.signal_type) {
Johannes Berg2a519312009-02-10 21:25:55 +01003415 case CFG80211_SIGNAL_TYPE_MBM:
3416 NLA_PUT_U32(msg, NL80211_BSS_SIGNAL_MBM, res->signal);
3417 break;
3418 case CFG80211_SIGNAL_TYPE_UNSPEC:
3419 NLA_PUT_U8(msg, NL80211_BSS_SIGNAL_UNSPEC, res->signal);
3420 break;
3421 default:
3422 break;
3423 }
3424
Johannes Berg48ab9052009-07-10 18:42:31 +02003425 switch (wdev->iftype) {
Johannes Berg074ac8d2010-09-16 14:58:22 +02003426 case NL80211_IFTYPE_P2P_CLIENT:
Johannes Berg48ab9052009-07-10 18:42:31 +02003427 case NL80211_IFTYPE_STATION:
3428 if (intbss == wdev->current_bss)
3429 NLA_PUT_U32(msg, NL80211_BSS_STATUS,
3430 NL80211_BSS_STATUS_ASSOCIATED);
3431 else for (i = 0; i < MAX_AUTH_BSSES; i++) {
3432 if (intbss != wdev->auth_bsses[i])
3433 continue;
3434 NLA_PUT_U32(msg, NL80211_BSS_STATUS,
3435 NL80211_BSS_STATUS_AUTHENTICATED);
3436 break;
3437 }
3438 break;
3439 case NL80211_IFTYPE_ADHOC:
3440 if (intbss == wdev->current_bss)
3441 NLA_PUT_U32(msg, NL80211_BSS_STATUS,
3442 NL80211_BSS_STATUS_IBSS_JOINED);
3443 break;
3444 default:
3445 break;
3446 }
3447
Johannes Berg2a519312009-02-10 21:25:55 +01003448 nla_nest_end(msg, bss);
3449
3450 return genlmsg_end(msg, hdr);
3451
3452 nla_put_failure:
3453 genlmsg_cancel(msg, hdr);
3454 return -EMSGSIZE;
3455}
3456
3457static int nl80211_dump_scan(struct sk_buff *skb,
3458 struct netlink_callback *cb)
3459{
Johannes Berg48ab9052009-07-10 18:42:31 +02003460 struct cfg80211_registered_device *rdev;
3461 struct net_device *dev;
Johannes Berg2a519312009-02-10 21:25:55 +01003462 struct cfg80211_internal_bss *scan;
Johannes Berg48ab9052009-07-10 18:42:31 +02003463 struct wireless_dev *wdev;
Johannes Berg2a519312009-02-10 21:25:55 +01003464 int ifidx = cb->args[0];
3465 int start = cb->args[1], idx = 0;
3466 int err;
3467
Holger Schuriga0438972009-11-11 11:30:02 +01003468 if (!ifidx)
3469 ifidx = nl80211_get_ifidx(cb);
3470 if (ifidx < 0)
3471 return ifidx;
3472 cb->args[0] = ifidx;
Johannes Berg2a519312009-02-10 21:25:55 +01003473
Johannes Berg463d0182009-07-14 00:33:35 +02003474 dev = dev_get_by_index(sock_net(skb->sk), ifidx);
Johannes Berg48ab9052009-07-10 18:42:31 +02003475 if (!dev)
Johannes Berg2a519312009-02-10 21:25:55 +01003476 return -ENODEV;
3477
Johannes Berg463d0182009-07-14 00:33:35 +02003478 rdev = cfg80211_get_dev_from_ifindex(sock_net(skb->sk), ifidx);
Johannes Berg48ab9052009-07-10 18:42:31 +02003479 if (IS_ERR(rdev)) {
3480 err = PTR_ERR(rdev);
Johannes Berg2a519312009-02-10 21:25:55 +01003481 goto out_put_netdev;
3482 }
3483
Johannes Berg48ab9052009-07-10 18:42:31 +02003484 wdev = dev->ieee80211_ptr;
Johannes Berg2a519312009-02-10 21:25:55 +01003485
Johannes Berg48ab9052009-07-10 18:42:31 +02003486 wdev_lock(wdev);
3487 spin_lock_bh(&rdev->bss_lock);
3488 cfg80211_bss_expire(rdev);
3489
3490 list_for_each_entry(scan, &rdev->bss_list, list) {
Johannes Berg2a519312009-02-10 21:25:55 +01003491 if (++idx <= start)
3492 continue;
3493 if (nl80211_send_bss(skb,
3494 NETLINK_CB(cb->skb).pid,
3495 cb->nlh->nlmsg_seq, NLM_F_MULTI,
Johannes Berg48ab9052009-07-10 18:42:31 +02003496 rdev, wdev, scan) < 0) {
Johannes Berg2a519312009-02-10 21:25:55 +01003497 idx--;
3498 goto out;
3499 }
3500 }
3501
3502 out:
Johannes Berg48ab9052009-07-10 18:42:31 +02003503 spin_unlock_bh(&rdev->bss_lock);
3504 wdev_unlock(wdev);
Johannes Berg2a519312009-02-10 21:25:55 +01003505
3506 cb->args[1] = idx;
3507 err = skb->len;
Johannes Berg48ab9052009-07-10 18:42:31 +02003508 cfg80211_unlock_rdev(rdev);
Johannes Berg2a519312009-02-10 21:25:55 +01003509 out_put_netdev:
Johannes Berg48ab9052009-07-10 18:42:31 +02003510 dev_put(dev);
Johannes Berg2a519312009-02-10 21:25:55 +01003511
3512 return err;
3513}
3514
Holger Schurig61fa7132009-11-11 12:25:40 +01003515static int nl80211_send_survey(struct sk_buff *msg, u32 pid, u32 seq,
3516 int flags, struct net_device *dev,
3517 struct survey_info *survey)
3518{
3519 void *hdr;
3520 struct nlattr *infoattr;
3521
3522 /* Survey without a channel doesn't make sense */
3523 if (!survey->channel)
3524 return -EINVAL;
3525
3526 hdr = nl80211hdr_put(msg, pid, seq, flags,
3527 NL80211_CMD_NEW_SURVEY_RESULTS);
3528 if (!hdr)
3529 return -ENOMEM;
3530
3531 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
3532
3533 infoattr = nla_nest_start(msg, NL80211_ATTR_SURVEY_INFO);
3534 if (!infoattr)
3535 goto nla_put_failure;
3536
3537 NLA_PUT_U32(msg, NL80211_SURVEY_INFO_FREQUENCY,
3538 survey->channel->center_freq);
3539 if (survey->filled & SURVEY_INFO_NOISE_DBM)
3540 NLA_PUT_U8(msg, NL80211_SURVEY_INFO_NOISE,
3541 survey->noise);
Felix Fietkau17e5a802010-09-29 17:15:30 +02003542 if (survey->filled & SURVEY_INFO_IN_USE)
3543 NLA_PUT_FLAG(msg, NL80211_SURVEY_INFO_IN_USE);
Holger Schurig61fa7132009-11-11 12:25:40 +01003544
3545 nla_nest_end(msg, infoattr);
3546
3547 return genlmsg_end(msg, hdr);
3548
3549 nla_put_failure:
3550 genlmsg_cancel(msg, hdr);
3551 return -EMSGSIZE;
3552}
3553
3554static int nl80211_dump_survey(struct sk_buff *skb,
3555 struct netlink_callback *cb)
3556{
3557 struct survey_info survey;
3558 struct cfg80211_registered_device *dev;
3559 struct net_device *netdev;
3560 int ifidx = cb->args[0];
3561 int survey_idx = cb->args[1];
3562 int res;
3563
3564 if (!ifidx)
3565 ifidx = nl80211_get_ifidx(cb);
3566 if (ifidx < 0)
3567 return ifidx;
3568 cb->args[0] = ifidx;
3569
3570 rtnl_lock();
3571
3572 netdev = __dev_get_by_index(sock_net(skb->sk), ifidx);
3573 if (!netdev) {
3574 res = -ENODEV;
3575 goto out_rtnl;
3576 }
3577
3578 dev = cfg80211_get_dev_from_ifindex(sock_net(skb->sk), ifidx);
3579 if (IS_ERR(dev)) {
3580 res = PTR_ERR(dev);
3581 goto out_rtnl;
3582 }
3583
3584 if (!dev->ops->dump_survey) {
3585 res = -EOPNOTSUPP;
3586 goto out_err;
3587 }
3588
3589 while (1) {
3590 res = dev->ops->dump_survey(&dev->wiphy, netdev, survey_idx,
3591 &survey);
3592 if (res == -ENOENT)
3593 break;
3594 if (res)
3595 goto out_err;
3596
3597 if (nl80211_send_survey(skb,
3598 NETLINK_CB(cb->skb).pid,
3599 cb->nlh->nlmsg_seq, NLM_F_MULTI,
3600 netdev,
3601 &survey) < 0)
3602 goto out;
3603 survey_idx++;
3604 }
3605
3606 out:
3607 cb->args[1] = survey_idx;
3608 res = skb->len;
3609 out_err:
3610 cfg80211_unlock_rdev(dev);
3611 out_rtnl:
3612 rtnl_unlock();
3613
3614 return res;
3615}
3616
Jouni Malinen255e7372009-03-20 21:21:17 +02003617static bool nl80211_valid_auth_type(enum nl80211_auth_type auth_type)
3618{
Samuel Ortizb23aa672009-07-01 21:26:54 +02003619 return auth_type <= NL80211_AUTHTYPE_MAX;
Jouni Malinen255e7372009-03-20 21:21:17 +02003620}
3621
Samuel Ortizb23aa672009-07-01 21:26:54 +02003622static bool nl80211_valid_wpa_versions(u32 wpa_versions)
3623{
3624 return !(wpa_versions & ~(NL80211_WPA_VERSION_1 |
3625 NL80211_WPA_VERSION_2));
3626}
3627
3628static bool nl80211_valid_akm_suite(u32 akm)
3629{
3630 return akm == WLAN_AKM_SUITE_8021X ||
3631 akm == WLAN_AKM_SUITE_PSK;
3632}
3633
3634static bool nl80211_valid_cipher_suite(u32 cipher)
3635{
3636 return cipher == WLAN_CIPHER_SUITE_WEP40 ||
3637 cipher == WLAN_CIPHER_SUITE_WEP104 ||
3638 cipher == WLAN_CIPHER_SUITE_TKIP ||
3639 cipher == WLAN_CIPHER_SUITE_CCMP ||
3640 cipher == WLAN_CIPHER_SUITE_AES_CMAC;
3641}
3642
3643
Jouni Malinen636a5d32009-03-19 13:39:22 +02003644static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info)
3645{
Johannes Berg79c97e92009-07-07 03:56:12 +02003646 struct cfg80211_registered_device *rdev;
Jouni Malinen636a5d32009-03-19 13:39:22 +02003647 struct net_device *dev;
Johannes Berg19957bb2009-07-02 17:20:43 +02003648 struct ieee80211_channel *chan;
3649 const u8 *bssid, *ssid, *ie = NULL;
3650 int err, ssid_len, ie_len = 0;
3651 enum nl80211_auth_type auth_type;
Johannes Bergfffd0932009-07-08 14:22:54 +02003652 struct key_parse key;
Jouni Malinend5cdfac2010-04-04 09:37:19 +03003653 bool local_state_change;
Jouni Malinen636a5d32009-03-19 13:39:22 +02003654
Johannes Bergf4a11bb2009-03-27 12:40:28 +01003655 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
3656 return -EINVAL;
3657
3658 if (!info->attrs[NL80211_ATTR_MAC])
3659 return -EINVAL;
3660
Jouni Malinen17780922009-03-27 20:52:47 +02003661 if (!info->attrs[NL80211_ATTR_AUTH_TYPE])
3662 return -EINVAL;
3663
Johannes Berg19957bb2009-07-02 17:20:43 +02003664 if (!info->attrs[NL80211_ATTR_SSID])
3665 return -EINVAL;
3666
3667 if (!info->attrs[NL80211_ATTR_WIPHY_FREQ])
3668 return -EINVAL;
3669
Johannes Bergfffd0932009-07-08 14:22:54 +02003670 err = nl80211_parse_key(info, &key);
3671 if (err)
3672 return err;
3673
3674 if (key.idx >= 0) {
3675 if (!key.p.key || !key.p.key_len)
3676 return -EINVAL;
3677 if ((key.p.cipher != WLAN_CIPHER_SUITE_WEP40 ||
3678 key.p.key_len != WLAN_KEY_LEN_WEP40) &&
3679 (key.p.cipher != WLAN_CIPHER_SUITE_WEP104 ||
3680 key.p.key_len != WLAN_KEY_LEN_WEP104))
3681 return -EINVAL;
3682 if (key.idx > 4)
3683 return -EINVAL;
3684 } else {
3685 key.p.key_len = 0;
3686 key.p.key = NULL;
3687 }
3688
Jouni Malinen636a5d32009-03-19 13:39:22 +02003689 rtnl_lock();
3690
Johannes Berg463d0182009-07-14 00:33:35 +02003691 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Jouni Malinen636a5d32009-03-19 13:39:22 +02003692 if (err)
3693 goto unlock_rtnl;
3694
Johannes Bergafea0b72010-08-10 09:46:42 +02003695 if (key.idx >= 0) {
3696 int i;
3697 bool ok = false;
3698 for (i = 0; i < rdev->wiphy.n_cipher_suites; i++) {
3699 if (key.p.cipher == rdev->wiphy.cipher_suites[i]) {
3700 ok = true;
3701 break;
3702 }
3703 }
3704 if (!ok) {
3705 err = -EINVAL;
3706 goto out;
3707 }
3708 }
3709
Johannes Berg79c97e92009-07-07 03:56:12 +02003710 if (!rdev->ops->auth) {
Jouni Malinen636a5d32009-03-19 13:39:22 +02003711 err = -EOPNOTSUPP;
3712 goto out;
3713 }
3714
Johannes Berg074ac8d2010-09-16 14:58:22 +02003715 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
3716 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT) {
Jouni Malineneec60b02009-03-20 21:21:19 +02003717 err = -EOPNOTSUPP;
3718 goto out;
3719 }
3720
Jouni Malinen35a8efe2009-03-20 21:21:18 +02003721 if (!netif_running(dev)) {
3722 err = -ENETDOWN;
3723 goto out;
3724 }
3725
Johannes Berg19957bb2009-07-02 17:20:43 +02003726 bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
Johannes Berg79c97e92009-07-07 03:56:12 +02003727 chan = ieee80211_get_channel(&rdev->wiphy,
Johannes Berg19957bb2009-07-02 17:20:43 +02003728 nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]));
3729 if (!chan || (chan->flags & IEEE80211_CHAN_DISABLED)) {
Jouni Malinen17780922009-03-27 20:52:47 +02003730 err = -EINVAL;
3731 goto out;
Jouni Malinen636a5d32009-03-19 13:39:22 +02003732 }
3733
Johannes Berg19957bb2009-07-02 17:20:43 +02003734 ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
3735 ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
3736
3737 if (info->attrs[NL80211_ATTR_IE]) {
3738 ie = nla_data(info->attrs[NL80211_ATTR_IE]);
3739 ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
3740 }
3741
3742 auth_type = nla_get_u32(info->attrs[NL80211_ATTR_AUTH_TYPE]);
3743 if (!nl80211_valid_auth_type(auth_type)) {
3744 err = -EINVAL;
3745 goto out;
3746 }
3747
Jouni Malinend5cdfac2010-04-04 09:37:19 +03003748 local_state_change = !!info->attrs[NL80211_ATTR_LOCAL_STATE_CHANGE];
3749
Johannes Berg79c97e92009-07-07 03:56:12 +02003750 err = cfg80211_mlme_auth(rdev, dev, chan, auth_type, bssid,
Johannes Bergfffd0932009-07-08 14:22:54 +02003751 ssid, ssid_len, ie, ie_len,
Jouni Malinend5cdfac2010-04-04 09:37:19 +03003752 key.p.key, key.p.key_len, key.idx,
3753 local_state_change);
Jouni Malinen636a5d32009-03-19 13:39:22 +02003754
3755out:
Johannes Berg79c97e92009-07-07 03:56:12 +02003756 cfg80211_unlock_rdev(rdev);
Jouni Malinen636a5d32009-03-19 13:39:22 +02003757 dev_put(dev);
3758unlock_rtnl:
3759 rtnl_unlock();
3760 return err;
3761}
3762
Johannes Bergc0692b82010-08-27 14:26:53 +03003763static int nl80211_crypto_settings(struct cfg80211_registered_device *rdev,
3764 struct genl_info *info,
Johannes Berg3dc27d22009-07-02 21:36:37 +02003765 struct cfg80211_crypto_settings *settings,
3766 int cipher_limit)
Samuel Ortizb23aa672009-07-01 21:26:54 +02003767{
Johannes Bergc0b2bbd2009-07-25 16:54:36 +02003768 memset(settings, 0, sizeof(*settings));
3769
Samuel Ortizb23aa672009-07-01 21:26:54 +02003770 settings->control_port = info->attrs[NL80211_ATTR_CONTROL_PORT];
3771
Johannes Bergc0692b82010-08-27 14:26:53 +03003772 if (info->attrs[NL80211_ATTR_CONTROL_PORT_ETHERTYPE]) {
3773 u16 proto;
3774 proto = nla_get_u16(
3775 info->attrs[NL80211_ATTR_CONTROL_PORT_ETHERTYPE]);
3776 settings->control_port_ethertype = cpu_to_be16(proto);
3777 if (!(rdev->wiphy.flags & WIPHY_FLAG_CONTROL_PORT_PROTOCOL) &&
3778 proto != ETH_P_PAE)
3779 return -EINVAL;
3780 if (info->attrs[NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT])
3781 settings->control_port_no_encrypt = true;
3782 } else
3783 settings->control_port_ethertype = cpu_to_be16(ETH_P_PAE);
3784
Samuel Ortizb23aa672009-07-01 21:26:54 +02003785 if (info->attrs[NL80211_ATTR_CIPHER_SUITES_PAIRWISE]) {
3786 void *data;
3787 int len, i;
3788
3789 data = nla_data(info->attrs[NL80211_ATTR_CIPHER_SUITES_PAIRWISE]);
3790 len = nla_len(info->attrs[NL80211_ATTR_CIPHER_SUITES_PAIRWISE]);
3791 settings->n_ciphers_pairwise = len / sizeof(u32);
3792
3793 if (len % sizeof(u32))
3794 return -EINVAL;
3795
Johannes Berg3dc27d22009-07-02 21:36:37 +02003796 if (settings->n_ciphers_pairwise > cipher_limit)
Samuel Ortizb23aa672009-07-01 21:26:54 +02003797 return -EINVAL;
3798
3799 memcpy(settings->ciphers_pairwise, data, len);
3800
3801 for (i = 0; i < settings->n_ciphers_pairwise; i++)
3802 if (!nl80211_valid_cipher_suite(
3803 settings->ciphers_pairwise[i]))
3804 return -EINVAL;
3805 }
3806
3807 if (info->attrs[NL80211_ATTR_CIPHER_SUITE_GROUP]) {
3808 settings->cipher_group =
3809 nla_get_u32(info->attrs[NL80211_ATTR_CIPHER_SUITE_GROUP]);
3810 if (!nl80211_valid_cipher_suite(settings->cipher_group))
3811 return -EINVAL;
3812 }
3813
3814 if (info->attrs[NL80211_ATTR_WPA_VERSIONS]) {
3815 settings->wpa_versions =
3816 nla_get_u32(info->attrs[NL80211_ATTR_WPA_VERSIONS]);
3817 if (!nl80211_valid_wpa_versions(settings->wpa_versions))
3818 return -EINVAL;
3819 }
3820
3821 if (info->attrs[NL80211_ATTR_AKM_SUITES]) {
3822 void *data;
3823 int len, i;
3824
3825 data = nla_data(info->attrs[NL80211_ATTR_AKM_SUITES]);
3826 len = nla_len(info->attrs[NL80211_ATTR_AKM_SUITES]);
3827 settings->n_akm_suites = len / sizeof(u32);
3828
3829 if (len % sizeof(u32))
3830 return -EINVAL;
3831
3832 memcpy(settings->akm_suites, data, len);
3833
3834 for (i = 0; i < settings->n_ciphers_pairwise; i++)
3835 if (!nl80211_valid_akm_suite(settings->akm_suites[i]))
3836 return -EINVAL;
3837 }
3838
3839 return 0;
3840}
3841
Jouni Malinen636a5d32009-03-19 13:39:22 +02003842static int nl80211_associate(struct sk_buff *skb, struct genl_info *info)
3843{
Johannes Berg19957bb2009-07-02 17:20:43 +02003844 struct cfg80211_registered_device *rdev;
Jouni Malinen636a5d32009-03-19 13:39:22 +02003845 struct net_device *dev;
Johannes Berg19957bb2009-07-02 17:20:43 +02003846 struct cfg80211_crypto_settings crypto;
Johannes Bergf444de02010-05-05 15:25:02 +02003847 struct ieee80211_channel *chan;
Johannes Berg3e5d7642009-07-07 14:37:26 +02003848 const u8 *bssid, *ssid, *ie = NULL, *prev_bssid = NULL;
Johannes Berg19957bb2009-07-02 17:20:43 +02003849 int err, ssid_len, ie_len = 0;
3850 bool use_mfp = false;
Jouni Malinen636a5d32009-03-19 13:39:22 +02003851
Johannes Bergf4a11bb2009-03-27 12:40:28 +01003852 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
3853 return -EINVAL;
3854
3855 if (!info->attrs[NL80211_ATTR_MAC] ||
Johannes Berg19957bb2009-07-02 17:20:43 +02003856 !info->attrs[NL80211_ATTR_SSID] ||
3857 !info->attrs[NL80211_ATTR_WIPHY_FREQ])
Johannes Bergf4a11bb2009-03-27 12:40:28 +01003858 return -EINVAL;
3859
Jouni Malinen636a5d32009-03-19 13:39:22 +02003860 rtnl_lock();
3861
Johannes Berg463d0182009-07-14 00:33:35 +02003862 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Jouni Malinen636a5d32009-03-19 13:39:22 +02003863 if (err)
3864 goto unlock_rtnl;
3865
Johannes Berg19957bb2009-07-02 17:20:43 +02003866 if (!rdev->ops->assoc) {
Jouni Malinen636a5d32009-03-19 13:39:22 +02003867 err = -EOPNOTSUPP;
3868 goto out;
3869 }
3870
Johannes Berg074ac8d2010-09-16 14:58:22 +02003871 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
3872 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT) {
Jouni Malineneec60b02009-03-20 21:21:19 +02003873 err = -EOPNOTSUPP;
3874 goto out;
3875 }
3876
Jouni Malinen35a8efe2009-03-20 21:21:18 +02003877 if (!netif_running(dev)) {
3878 err = -ENETDOWN;
3879 goto out;
3880 }
3881
Johannes Berg19957bb2009-07-02 17:20:43 +02003882 bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
Jouni Malinen636a5d32009-03-19 13:39:22 +02003883
Johannes Berg19957bb2009-07-02 17:20:43 +02003884 chan = ieee80211_get_channel(&rdev->wiphy,
3885 nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]));
3886 if (!chan || (chan->flags & IEEE80211_CHAN_DISABLED)) {
3887 err = -EINVAL;
3888 goto out;
Jouni Malinen636a5d32009-03-19 13:39:22 +02003889 }
3890
Johannes Berg19957bb2009-07-02 17:20:43 +02003891 ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
3892 ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
Jouni Malinen636a5d32009-03-19 13:39:22 +02003893
3894 if (info->attrs[NL80211_ATTR_IE]) {
Johannes Berg19957bb2009-07-02 17:20:43 +02003895 ie = nla_data(info->attrs[NL80211_ATTR_IE]);
3896 ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
Jouni Malinen636a5d32009-03-19 13:39:22 +02003897 }
3898
Jouni Malinendc6382c2009-05-06 22:09:37 +03003899 if (info->attrs[NL80211_ATTR_USE_MFP]) {
Johannes Berg4f5dadc2009-07-07 03:56:10 +02003900 enum nl80211_mfp mfp =
Jouni Malinendc6382c2009-05-06 22:09:37 +03003901 nla_get_u32(info->attrs[NL80211_ATTR_USE_MFP]);
Johannes Berg4f5dadc2009-07-07 03:56:10 +02003902 if (mfp == NL80211_MFP_REQUIRED)
Johannes Berg19957bb2009-07-02 17:20:43 +02003903 use_mfp = true;
Johannes Berg4f5dadc2009-07-07 03:56:10 +02003904 else if (mfp != NL80211_MFP_NO) {
Jouni Malinendc6382c2009-05-06 22:09:37 +03003905 err = -EINVAL;
3906 goto out;
3907 }
3908 }
3909
Johannes Berg3e5d7642009-07-07 14:37:26 +02003910 if (info->attrs[NL80211_ATTR_PREV_BSSID])
3911 prev_bssid = nla_data(info->attrs[NL80211_ATTR_PREV_BSSID]);
3912
Johannes Bergc0692b82010-08-27 14:26:53 +03003913 err = nl80211_crypto_settings(rdev, info, &crypto, 1);
Samuel Ortizb23aa672009-07-01 21:26:54 +02003914 if (!err)
Johannes Berg3e5d7642009-07-07 14:37:26 +02003915 err = cfg80211_mlme_assoc(rdev, dev, chan, bssid, prev_bssid,
3916 ssid, ssid_len, ie, ie_len, use_mfp,
Johannes Berg19957bb2009-07-02 17:20:43 +02003917 &crypto);
Jouni Malinen636a5d32009-03-19 13:39:22 +02003918
3919out:
Johannes Berg4d0c8ae2009-07-07 03:56:09 +02003920 cfg80211_unlock_rdev(rdev);
Jouni Malinen636a5d32009-03-19 13:39:22 +02003921 dev_put(dev);
3922unlock_rtnl:
3923 rtnl_unlock();
3924 return err;
3925}
3926
3927static int nl80211_deauthenticate(struct sk_buff *skb, struct genl_info *info)
3928{
Johannes Berg79c97e92009-07-07 03:56:12 +02003929 struct cfg80211_registered_device *rdev;
Jouni Malinen636a5d32009-03-19 13:39:22 +02003930 struct net_device *dev;
Johannes Berg19957bb2009-07-02 17:20:43 +02003931 const u8 *ie = NULL, *bssid;
3932 int err, ie_len = 0;
3933 u16 reason_code;
Jouni Malinend5cdfac2010-04-04 09:37:19 +03003934 bool local_state_change;
Jouni Malinen636a5d32009-03-19 13:39:22 +02003935
Johannes Bergf4a11bb2009-03-27 12:40:28 +01003936 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
3937 return -EINVAL;
3938
3939 if (!info->attrs[NL80211_ATTR_MAC])
3940 return -EINVAL;
3941
3942 if (!info->attrs[NL80211_ATTR_REASON_CODE])
3943 return -EINVAL;
3944
Jouni Malinen636a5d32009-03-19 13:39:22 +02003945 rtnl_lock();
3946
Johannes Berg463d0182009-07-14 00:33:35 +02003947 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Jouni Malinen636a5d32009-03-19 13:39:22 +02003948 if (err)
3949 goto unlock_rtnl;
3950
Johannes Berg79c97e92009-07-07 03:56:12 +02003951 if (!rdev->ops->deauth) {
Jouni Malinen636a5d32009-03-19 13:39:22 +02003952 err = -EOPNOTSUPP;
3953 goto out;
3954 }
3955
Johannes Berg074ac8d2010-09-16 14:58:22 +02003956 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
3957 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT) {
Jouni Malineneec60b02009-03-20 21:21:19 +02003958 err = -EOPNOTSUPP;
3959 goto out;
3960 }
3961
Jouni Malinen35a8efe2009-03-20 21:21:18 +02003962 if (!netif_running(dev)) {
3963 err = -ENETDOWN;
3964 goto out;
3965 }
3966
Johannes Berg19957bb2009-07-02 17:20:43 +02003967 bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
Jouni Malinen636a5d32009-03-19 13:39:22 +02003968
Johannes Berg19957bb2009-07-02 17:20:43 +02003969 reason_code = nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]);
3970 if (reason_code == 0) {
Johannes Bergf4a11bb2009-03-27 12:40:28 +01003971 /* Reason Code 0 is reserved */
3972 err = -EINVAL;
3973 goto out;
Jouni Malinen255e7372009-03-20 21:21:17 +02003974 }
Jouni Malinen636a5d32009-03-19 13:39:22 +02003975
3976 if (info->attrs[NL80211_ATTR_IE]) {
Johannes Berg19957bb2009-07-02 17:20:43 +02003977 ie = nla_data(info->attrs[NL80211_ATTR_IE]);
3978 ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
Jouni Malinen636a5d32009-03-19 13:39:22 +02003979 }
3980
Jouni Malinend5cdfac2010-04-04 09:37:19 +03003981 local_state_change = !!info->attrs[NL80211_ATTR_LOCAL_STATE_CHANGE];
3982
3983 err = cfg80211_mlme_deauth(rdev, dev, bssid, ie, ie_len, reason_code,
3984 local_state_change);
Jouni Malinen636a5d32009-03-19 13:39:22 +02003985
3986out:
Johannes Berg79c97e92009-07-07 03:56:12 +02003987 cfg80211_unlock_rdev(rdev);
Jouni Malinen636a5d32009-03-19 13:39:22 +02003988 dev_put(dev);
3989unlock_rtnl:
3990 rtnl_unlock();
3991 return err;
3992}
3993
3994static int nl80211_disassociate(struct sk_buff *skb, struct genl_info *info)
3995{
Johannes Berg79c97e92009-07-07 03:56:12 +02003996 struct cfg80211_registered_device *rdev;
Jouni Malinen636a5d32009-03-19 13:39:22 +02003997 struct net_device *dev;
Johannes Berg19957bb2009-07-02 17:20:43 +02003998 const u8 *ie = NULL, *bssid;
3999 int err, ie_len = 0;
4000 u16 reason_code;
Jouni Malinend5cdfac2010-04-04 09:37:19 +03004001 bool local_state_change;
Jouni Malinen636a5d32009-03-19 13:39:22 +02004002
Johannes Bergf4a11bb2009-03-27 12:40:28 +01004003 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
4004 return -EINVAL;
4005
4006 if (!info->attrs[NL80211_ATTR_MAC])
4007 return -EINVAL;
4008
4009 if (!info->attrs[NL80211_ATTR_REASON_CODE])
4010 return -EINVAL;
4011
Jouni Malinen636a5d32009-03-19 13:39:22 +02004012 rtnl_lock();
4013
Johannes Berg463d0182009-07-14 00:33:35 +02004014 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Jouni Malinen636a5d32009-03-19 13:39:22 +02004015 if (err)
4016 goto unlock_rtnl;
4017
Johannes Berg79c97e92009-07-07 03:56:12 +02004018 if (!rdev->ops->disassoc) {
Jouni Malinen636a5d32009-03-19 13:39:22 +02004019 err = -EOPNOTSUPP;
4020 goto out;
4021 }
4022
Johannes Berg074ac8d2010-09-16 14:58:22 +02004023 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
4024 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT) {
Jouni Malineneec60b02009-03-20 21:21:19 +02004025 err = -EOPNOTSUPP;
4026 goto out;
4027 }
4028
Jouni Malinen35a8efe2009-03-20 21:21:18 +02004029 if (!netif_running(dev)) {
4030 err = -ENETDOWN;
4031 goto out;
4032 }
4033
Johannes Berg19957bb2009-07-02 17:20:43 +02004034 bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
Jouni Malinen636a5d32009-03-19 13:39:22 +02004035
Johannes Berg19957bb2009-07-02 17:20:43 +02004036 reason_code = nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]);
4037 if (reason_code == 0) {
Johannes Bergf4a11bb2009-03-27 12:40:28 +01004038 /* Reason Code 0 is reserved */
4039 err = -EINVAL;
4040 goto out;
Jouni Malinen255e7372009-03-20 21:21:17 +02004041 }
Jouni Malinen636a5d32009-03-19 13:39:22 +02004042
4043 if (info->attrs[NL80211_ATTR_IE]) {
Johannes Berg19957bb2009-07-02 17:20:43 +02004044 ie = nla_data(info->attrs[NL80211_ATTR_IE]);
4045 ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
Jouni Malinen636a5d32009-03-19 13:39:22 +02004046 }
4047
Jouni Malinend5cdfac2010-04-04 09:37:19 +03004048 local_state_change = !!info->attrs[NL80211_ATTR_LOCAL_STATE_CHANGE];
4049
4050 err = cfg80211_mlme_disassoc(rdev, dev, bssid, ie, ie_len, reason_code,
4051 local_state_change);
Jouni Malinen636a5d32009-03-19 13:39:22 +02004052
4053out:
Johannes Berg79c97e92009-07-07 03:56:12 +02004054 cfg80211_unlock_rdev(rdev);
Jouni Malinen636a5d32009-03-19 13:39:22 +02004055 dev_put(dev);
4056unlock_rtnl:
4057 rtnl_unlock();
4058 return err;
4059}
4060
Johannes Berg04a773a2009-04-19 21:24:32 +02004061static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info)
4062{
Johannes Berg79c97e92009-07-07 03:56:12 +02004063 struct cfg80211_registered_device *rdev;
Johannes Berg04a773a2009-04-19 21:24:32 +02004064 struct net_device *dev;
4065 struct cfg80211_ibss_params ibss;
4066 struct wiphy *wiphy;
Johannes Bergfffd0932009-07-08 14:22:54 +02004067 struct cfg80211_cached_keys *connkeys = NULL;
Johannes Berg04a773a2009-04-19 21:24:32 +02004068 int err;
4069
Johannes Berg8e30bc52009-04-22 17:45:38 +02004070 memset(&ibss, 0, sizeof(ibss));
4071
Johannes Berg04a773a2009-04-19 21:24:32 +02004072 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
4073 return -EINVAL;
4074
4075 if (!info->attrs[NL80211_ATTR_WIPHY_FREQ] ||
4076 !info->attrs[NL80211_ATTR_SSID] ||
4077 !nla_len(info->attrs[NL80211_ATTR_SSID]))
4078 return -EINVAL;
4079
Johannes Berg8e30bc52009-04-22 17:45:38 +02004080 ibss.beacon_interval = 100;
4081
4082 if (info->attrs[NL80211_ATTR_BEACON_INTERVAL]) {
4083 ibss.beacon_interval =
4084 nla_get_u32(info->attrs[NL80211_ATTR_BEACON_INTERVAL]);
4085 if (ibss.beacon_interval < 1 || ibss.beacon_interval > 10000)
4086 return -EINVAL;
4087 }
4088
Johannes Berg04a773a2009-04-19 21:24:32 +02004089 rtnl_lock();
4090
Johannes Berg463d0182009-07-14 00:33:35 +02004091 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Johannes Berg04a773a2009-04-19 21:24:32 +02004092 if (err)
4093 goto unlock_rtnl;
4094
Johannes Berg79c97e92009-07-07 03:56:12 +02004095 if (!rdev->ops->join_ibss) {
Johannes Berg04a773a2009-04-19 21:24:32 +02004096 err = -EOPNOTSUPP;
4097 goto out;
4098 }
4099
4100 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC) {
4101 err = -EOPNOTSUPP;
4102 goto out;
4103 }
4104
4105 if (!netif_running(dev)) {
4106 err = -ENETDOWN;
4107 goto out;
4108 }
4109
Johannes Berg79c97e92009-07-07 03:56:12 +02004110 wiphy = &rdev->wiphy;
Johannes Berg04a773a2009-04-19 21:24:32 +02004111
4112 if (info->attrs[NL80211_ATTR_MAC])
4113 ibss.bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
4114 ibss.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
4115 ibss.ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
4116
4117 if (info->attrs[NL80211_ATTR_IE]) {
4118 ibss.ie = nla_data(info->attrs[NL80211_ATTR_IE]);
4119 ibss.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
4120 }
4121
4122 ibss.channel = ieee80211_get_channel(wiphy,
4123 nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]));
4124 if (!ibss.channel ||
4125 ibss.channel->flags & IEEE80211_CHAN_NO_IBSS ||
4126 ibss.channel->flags & IEEE80211_CHAN_DISABLED) {
4127 err = -EINVAL;
4128 goto out;
4129 }
4130
4131 ibss.channel_fixed = !!info->attrs[NL80211_ATTR_FREQ_FIXED];
Johannes Bergfffd0932009-07-08 14:22:54 +02004132 ibss.privacy = !!info->attrs[NL80211_ATTR_PRIVACY];
Johannes Berg04a773a2009-04-19 21:24:32 +02004133
Johannes Bergfffd0932009-07-08 14:22:54 +02004134 if (ibss.privacy && info->attrs[NL80211_ATTR_KEYS]) {
4135 connkeys = nl80211_parse_connkeys(rdev,
4136 info->attrs[NL80211_ATTR_KEYS]);
4137 if (IS_ERR(connkeys)) {
4138 err = PTR_ERR(connkeys);
4139 connkeys = NULL;
4140 goto out;
4141 }
4142 }
4143
Teemu Paasikivifbd2c8d2010-06-14 12:55:31 +03004144 if (info->attrs[NL80211_ATTR_BSS_BASIC_RATES]) {
4145 u8 *rates =
4146 nla_data(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]);
4147 int n_rates =
4148 nla_len(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]);
4149 struct ieee80211_supported_band *sband =
4150 wiphy->bands[ibss.channel->band];
4151 int i, j;
4152
4153 if (n_rates == 0) {
4154 err = -EINVAL;
4155 goto out;
4156 }
4157
4158 for (i = 0; i < n_rates; i++) {
4159 int rate = (rates[i] & 0x7f) * 5;
4160 bool found = false;
4161
4162 for (j = 0; j < sband->n_bitrates; j++) {
4163 if (sband->bitrates[j].bitrate == rate) {
4164 found = true;
4165 ibss.basic_rates |= BIT(j);
4166 break;
4167 }
4168 }
4169 if (!found) {
4170 err = -EINVAL;
4171 goto out;
4172 }
4173 }
Teemu Paasikivifbd2c8d2010-06-14 12:55:31 +03004174 }
4175
Johannes Bergfffd0932009-07-08 14:22:54 +02004176 err = cfg80211_join_ibss(rdev, dev, &ibss, connkeys);
Johannes Berg04a773a2009-04-19 21:24:32 +02004177
4178out:
Johannes Berg79c97e92009-07-07 03:56:12 +02004179 cfg80211_unlock_rdev(rdev);
Johannes Berg04a773a2009-04-19 21:24:32 +02004180 dev_put(dev);
4181unlock_rtnl:
Johannes Bergfffd0932009-07-08 14:22:54 +02004182 if (err)
4183 kfree(connkeys);
Johannes Berg04a773a2009-04-19 21:24:32 +02004184 rtnl_unlock();
4185 return err;
4186}
4187
4188static int nl80211_leave_ibss(struct sk_buff *skb, struct genl_info *info)
4189{
Johannes Berg79c97e92009-07-07 03:56:12 +02004190 struct cfg80211_registered_device *rdev;
Johannes Berg04a773a2009-04-19 21:24:32 +02004191 struct net_device *dev;
4192 int err;
4193
4194 rtnl_lock();
4195
Johannes Berg463d0182009-07-14 00:33:35 +02004196 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Johannes Berg04a773a2009-04-19 21:24:32 +02004197 if (err)
4198 goto unlock_rtnl;
4199
Johannes Berg79c97e92009-07-07 03:56:12 +02004200 if (!rdev->ops->leave_ibss) {
Johannes Berg04a773a2009-04-19 21:24:32 +02004201 err = -EOPNOTSUPP;
4202 goto out;
4203 }
4204
4205 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC) {
4206 err = -EOPNOTSUPP;
4207 goto out;
4208 }
4209
4210 if (!netif_running(dev)) {
4211 err = -ENETDOWN;
4212 goto out;
4213 }
4214
Johannes Berg79c97e92009-07-07 03:56:12 +02004215 err = cfg80211_leave_ibss(rdev, dev, false);
Johannes Berg04a773a2009-04-19 21:24:32 +02004216
4217out:
Johannes Berg79c97e92009-07-07 03:56:12 +02004218 cfg80211_unlock_rdev(rdev);
Johannes Berg04a773a2009-04-19 21:24:32 +02004219 dev_put(dev);
4220unlock_rtnl:
4221 rtnl_unlock();
4222 return err;
4223}
4224
Johannes Bergaff89a92009-07-01 21:26:51 +02004225#ifdef CONFIG_NL80211_TESTMODE
4226static struct genl_multicast_group nl80211_testmode_mcgrp = {
4227 .name = "testmode",
4228};
4229
4230static int nl80211_testmode_do(struct sk_buff *skb, struct genl_info *info)
4231{
4232 struct cfg80211_registered_device *rdev;
4233 int err;
4234
4235 if (!info->attrs[NL80211_ATTR_TESTDATA])
4236 return -EINVAL;
4237
4238 rtnl_lock();
4239
4240 rdev = cfg80211_get_dev_from_info(info);
4241 if (IS_ERR(rdev)) {
4242 err = PTR_ERR(rdev);
4243 goto unlock_rtnl;
4244 }
4245
4246 err = -EOPNOTSUPP;
4247 if (rdev->ops->testmode_cmd) {
4248 rdev->testmode_info = info;
4249 err = rdev->ops->testmode_cmd(&rdev->wiphy,
4250 nla_data(info->attrs[NL80211_ATTR_TESTDATA]),
4251 nla_len(info->attrs[NL80211_ATTR_TESTDATA]));
4252 rdev->testmode_info = NULL;
4253 }
4254
Johannes Berg4d0c8ae2009-07-07 03:56:09 +02004255 cfg80211_unlock_rdev(rdev);
Johannes Bergaff89a92009-07-01 21:26:51 +02004256
4257 unlock_rtnl:
4258 rtnl_unlock();
4259 return err;
4260}
4261
4262static struct sk_buff *
4263__cfg80211_testmode_alloc_skb(struct cfg80211_registered_device *rdev,
4264 int approxlen, u32 pid, u32 seq, gfp_t gfp)
4265{
4266 struct sk_buff *skb;
4267 void *hdr;
4268 struct nlattr *data;
4269
4270 skb = nlmsg_new(approxlen + 100, gfp);
4271 if (!skb)
4272 return NULL;
4273
4274 hdr = nl80211hdr_put(skb, pid, seq, 0, NL80211_CMD_TESTMODE);
4275 if (!hdr) {
4276 kfree_skb(skb);
4277 return NULL;
4278 }
4279
4280 NLA_PUT_U32(skb, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
4281 data = nla_nest_start(skb, NL80211_ATTR_TESTDATA);
4282
4283 ((void **)skb->cb)[0] = rdev;
4284 ((void **)skb->cb)[1] = hdr;
4285 ((void **)skb->cb)[2] = data;
4286
4287 return skb;
4288
4289 nla_put_failure:
4290 kfree_skb(skb);
4291 return NULL;
4292}
4293
4294struct sk_buff *cfg80211_testmode_alloc_reply_skb(struct wiphy *wiphy,
4295 int approxlen)
4296{
4297 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
4298
4299 if (WARN_ON(!rdev->testmode_info))
4300 return NULL;
4301
4302 return __cfg80211_testmode_alloc_skb(rdev, approxlen,
4303 rdev->testmode_info->snd_pid,
4304 rdev->testmode_info->snd_seq,
4305 GFP_KERNEL);
4306}
4307EXPORT_SYMBOL(cfg80211_testmode_alloc_reply_skb);
4308
4309int cfg80211_testmode_reply(struct sk_buff *skb)
4310{
4311 struct cfg80211_registered_device *rdev = ((void **)skb->cb)[0];
4312 void *hdr = ((void **)skb->cb)[1];
4313 struct nlattr *data = ((void **)skb->cb)[2];
4314
4315 if (WARN_ON(!rdev->testmode_info)) {
4316 kfree_skb(skb);
4317 return -EINVAL;
4318 }
4319
4320 nla_nest_end(skb, data);
4321 genlmsg_end(skb, hdr);
4322 return genlmsg_reply(skb, rdev->testmode_info);
4323}
4324EXPORT_SYMBOL(cfg80211_testmode_reply);
4325
4326struct sk_buff *cfg80211_testmode_alloc_event_skb(struct wiphy *wiphy,
4327 int approxlen, gfp_t gfp)
4328{
4329 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
4330
4331 return __cfg80211_testmode_alloc_skb(rdev, approxlen, 0, 0, gfp);
4332}
4333EXPORT_SYMBOL(cfg80211_testmode_alloc_event_skb);
4334
4335void cfg80211_testmode_event(struct sk_buff *skb, gfp_t gfp)
4336{
4337 void *hdr = ((void **)skb->cb)[1];
4338 struct nlattr *data = ((void **)skb->cb)[2];
4339
4340 nla_nest_end(skb, data);
4341 genlmsg_end(skb, hdr);
4342 genlmsg_multicast(skb, 0, nl80211_testmode_mcgrp.id, gfp);
4343}
4344EXPORT_SYMBOL(cfg80211_testmode_event);
4345#endif
4346
Samuel Ortizb23aa672009-07-01 21:26:54 +02004347static int nl80211_connect(struct sk_buff *skb, struct genl_info *info)
4348{
Johannes Berg79c97e92009-07-07 03:56:12 +02004349 struct cfg80211_registered_device *rdev;
Samuel Ortizb23aa672009-07-01 21:26:54 +02004350 struct net_device *dev;
4351 struct cfg80211_connect_params connect;
4352 struct wiphy *wiphy;
Johannes Bergfffd0932009-07-08 14:22:54 +02004353 struct cfg80211_cached_keys *connkeys = NULL;
Samuel Ortizb23aa672009-07-01 21:26:54 +02004354 int err;
4355
4356 memset(&connect, 0, sizeof(connect));
4357
4358 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
4359 return -EINVAL;
4360
4361 if (!info->attrs[NL80211_ATTR_SSID] ||
4362 !nla_len(info->attrs[NL80211_ATTR_SSID]))
4363 return -EINVAL;
4364
4365 if (info->attrs[NL80211_ATTR_AUTH_TYPE]) {
4366 connect.auth_type =
4367 nla_get_u32(info->attrs[NL80211_ATTR_AUTH_TYPE]);
4368 if (!nl80211_valid_auth_type(connect.auth_type))
4369 return -EINVAL;
4370 } else
4371 connect.auth_type = NL80211_AUTHTYPE_AUTOMATIC;
4372
4373 connect.privacy = info->attrs[NL80211_ATTR_PRIVACY];
4374
Johannes Bergc0692b82010-08-27 14:26:53 +03004375 err = nl80211_crypto_settings(rdev, info, &connect.crypto,
Johannes Berg3dc27d22009-07-02 21:36:37 +02004376 NL80211_MAX_NR_CIPHER_SUITES);
Samuel Ortizb23aa672009-07-01 21:26:54 +02004377 if (err)
4378 return err;
4379 rtnl_lock();
4380
Johannes Berg463d0182009-07-14 00:33:35 +02004381 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Samuel Ortizb23aa672009-07-01 21:26:54 +02004382 if (err)
4383 goto unlock_rtnl;
4384
Johannes Berg074ac8d2010-09-16 14:58:22 +02004385 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
4386 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT) {
Samuel Ortizb23aa672009-07-01 21:26:54 +02004387 err = -EOPNOTSUPP;
4388 goto out;
4389 }
4390
4391 if (!netif_running(dev)) {
4392 err = -ENETDOWN;
4393 goto out;
4394 }
4395
Johannes Berg79c97e92009-07-07 03:56:12 +02004396 wiphy = &rdev->wiphy;
Samuel Ortizb23aa672009-07-01 21:26:54 +02004397
Samuel Ortizb23aa672009-07-01 21:26:54 +02004398 if (info->attrs[NL80211_ATTR_MAC])
4399 connect.bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
4400 connect.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
4401 connect.ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
4402
4403 if (info->attrs[NL80211_ATTR_IE]) {
4404 connect.ie = nla_data(info->attrs[NL80211_ATTR_IE]);
4405 connect.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
4406 }
4407
4408 if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
4409 connect.channel =
4410 ieee80211_get_channel(wiphy,
4411 nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]));
4412 if (!connect.channel ||
4413 connect.channel->flags & IEEE80211_CHAN_DISABLED) {
4414 err = -EINVAL;
4415 goto out;
4416 }
4417 }
4418
Johannes Bergfffd0932009-07-08 14:22:54 +02004419 if (connect.privacy && info->attrs[NL80211_ATTR_KEYS]) {
4420 connkeys = nl80211_parse_connkeys(rdev,
4421 info->attrs[NL80211_ATTR_KEYS]);
4422 if (IS_ERR(connkeys)) {
4423 err = PTR_ERR(connkeys);
4424 connkeys = NULL;
4425 goto out;
4426 }
4427 }
4428
4429 err = cfg80211_connect(rdev, dev, &connect, connkeys);
Samuel Ortizb23aa672009-07-01 21:26:54 +02004430
4431out:
Johannes Berg79c97e92009-07-07 03:56:12 +02004432 cfg80211_unlock_rdev(rdev);
Samuel Ortizb23aa672009-07-01 21:26:54 +02004433 dev_put(dev);
4434unlock_rtnl:
Johannes Bergfffd0932009-07-08 14:22:54 +02004435 if (err)
4436 kfree(connkeys);
Samuel Ortizb23aa672009-07-01 21:26:54 +02004437 rtnl_unlock();
4438 return err;
4439}
4440
4441static int nl80211_disconnect(struct sk_buff *skb, struct genl_info *info)
4442{
Johannes Berg79c97e92009-07-07 03:56:12 +02004443 struct cfg80211_registered_device *rdev;
Samuel Ortizb23aa672009-07-01 21:26:54 +02004444 struct net_device *dev;
4445 int err;
4446 u16 reason;
4447
4448 if (!info->attrs[NL80211_ATTR_REASON_CODE])
4449 reason = WLAN_REASON_DEAUTH_LEAVING;
4450 else
4451 reason = nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]);
4452
4453 if (reason == 0)
4454 return -EINVAL;
4455
4456 rtnl_lock();
4457
Johannes Berg463d0182009-07-14 00:33:35 +02004458 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Samuel Ortizb23aa672009-07-01 21:26:54 +02004459 if (err)
4460 goto unlock_rtnl;
4461
Johannes Berg074ac8d2010-09-16 14:58:22 +02004462 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
4463 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT) {
Samuel Ortizb23aa672009-07-01 21:26:54 +02004464 err = -EOPNOTSUPP;
4465 goto out;
4466 }
4467
4468 if (!netif_running(dev)) {
4469 err = -ENETDOWN;
4470 goto out;
4471 }
4472
Johannes Berg79c97e92009-07-07 03:56:12 +02004473 err = cfg80211_disconnect(rdev, dev, reason, true);
Samuel Ortizb23aa672009-07-01 21:26:54 +02004474
4475out:
Johannes Berg79c97e92009-07-07 03:56:12 +02004476 cfg80211_unlock_rdev(rdev);
Samuel Ortizb23aa672009-07-01 21:26:54 +02004477 dev_put(dev);
4478unlock_rtnl:
4479 rtnl_unlock();
4480 return err;
4481}
4482
Johannes Berg463d0182009-07-14 00:33:35 +02004483static int nl80211_wiphy_netns(struct sk_buff *skb, struct genl_info *info)
4484{
4485 struct cfg80211_registered_device *rdev;
4486 struct net *net;
4487 int err;
4488 u32 pid;
4489
4490 if (!info->attrs[NL80211_ATTR_PID])
4491 return -EINVAL;
4492
4493 pid = nla_get_u32(info->attrs[NL80211_ATTR_PID]);
4494
4495 rtnl_lock();
4496
4497 rdev = cfg80211_get_dev_from_info(info);
4498 if (IS_ERR(rdev)) {
4499 err = PTR_ERR(rdev);
Johannes Berg8a8e05e2009-10-08 21:02:02 +02004500 goto out_rtnl;
Johannes Berg463d0182009-07-14 00:33:35 +02004501 }
4502
4503 net = get_net_ns_by_pid(pid);
4504 if (IS_ERR(net)) {
4505 err = PTR_ERR(net);
4506 goto out;
4507 }
4508
4509 err = 0;
4510
4511 /* check if anything to do */
4512 if (net_eq(wiphy_net(&rdev->wiphy), net))
4513 goto out_put_net;
4514
4515 err = cfg80211_switch_netns(rdev, net);
4516 out_put_net:
4517 put_net(net);
4518 out:
4519 cfg80211_unlock_rdev(rdev);
Johannes Berg8a8e05e2009-10-08 21:02:02 +02004520 out_rtnl:
Johannes Berg463d0182009-07-14 00:33:35 +02004521 rtnl_unlock();
4522 return err;
4523}
4524
Samuel Ortiz67fbb162009-11-24 23:59:15 +01004525static int nl80211_setdel_pmksa(struct sk_buff *skb, struct genl_info *info)
4526{
4527 struct cfg80211_registered_device *rdev;
4528 int (*rdev_ops)(struct wiphy *wiphy, struct net_device *dev,
4529 struct cfg80211_pmksa *pmksa) = NULL;
4530 int err;
4531 struct net_device *dev;
4532 struct cfg80211_pmksa pmksa;
4533
4534 memset(&pmksa, 0, sizeof(struct cfg80211_pmksa));
4535
4536 if (!info->attrs[NL80211_ATTR_MAC])
4537 return -EINVAL;
4538
4539 if (!info->attrs[NL80211_ATTR_PMKID])
4540 return -EINVAL;
4541
4542 rtnl_lock();
4543
4544 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
4545 if (err)
4546 goto out_rtnl;
4547
4548 pmksa.pmkid = nla_data(info->attrs[NL80211_ATTR_PMKID]);
4549 pmksa.bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
4550
Johannes Berg074ac8d2010-09-16 14:58:22 +02004551 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
4552 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT) {
Samuel Ortiz67fbb162009-11-24 23:59:15 +01004553 err = -EOPNOTSUPP;
4554 goto out;
4555 }
4556
4557 switch (info->genlhdr->cmd) {
4558 case NL80211_CMD_SET_PMKSA:
4559 rdev_ops = rdev->ops->set_pmksa;
4560 break;
4561 case NL80211_CMD_DEL_PMKSA:
4562 rdev_ops = rdev->ops->del_pmksa;
4563 break;
4564 default:
4565 WARN_ON(1);
4566 break;
4567 }
4568
4569 if (!rdev_ops) {
4570 err = -EOPNOTSUPP;
4571 goto out;
4572 }
4573
4574 err = rdev_ops(&rdev->wiphy, dev, &pmksa);
4575
4576 out:
4577 cfg80211_unlock_rdev(rdev);
4578 dev_put(dev);
4579 out_rtnl:
4580 rtnl_unlock();
4581
4582 return err;
4583}
4584
4585static int nl80211_flush_pmksa(struct sk_buff *skb, struct genl_info *info)
4586{
4587 struct cfg80211_registered_device *rdev;
4588 int err;
4589 struct net_device *dev;
4590
4591 rtnl_lock();
4592
4593 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
4594 if (err)
4595 goto out_rtnl;
4596
Johannes Berg074ac8d2010-09-16 14:58:22 +02004597 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
4598 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT) {
Samuel Ortiz67fbb162009-11-24 23:59:15 +01004599 err = -EOPNOTSUPP;
4600 goto out;
4601 }
4602
4603 if (!rdev->ops->flush_pmksa) {
4604 err = -EOPNOTSUPP;
4605 goto out;
4606 }
4607
4608 err = rdev->ops->flush_pmksa(&rdev->wiphy, dev);
4609
4610 out:
4611 cfg80211_unlock_rdev(rdev);
4612 dev_put(dev);
4613 out_rtnl:
4614 rtnl_unlock();
4615
4616 return err;
4617
4618}
4619
Jouni Malinen9588bbd2009-12-23 13:15:41 +01004620static int nl80211_remain_on_channel(struct sk_buff *skb,
4621 struct genl_info *info)
4622{
4623 struct cfg80211_registered_device *rdev;
4624 struct net_device *dev;
4625 struct ieee80211_channel *chan;
4626 struct sk_buff *msg;
4627 void *hdr;
4628 u64 cookie;
4629 enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT;
4630 u32 freq, duration;
4631 int err;
4632
4633 if (!info->attrs[NL80211_ATTR_WIPHY_FREQ] ||
4634 !info->attrs[NL80211_ATTR_DURATION])
4635 return -EINVAL;
4636
4637 duration = nla_get_u32(info->attrs[NL80211_ATTR_DURATION]);
4638
4639 /*
4640 * We should be on that channel for at least one jiffie,
4641 * and more than 5 seconds seems excessive.
4642 */
4643 if (!duration || !msecs_to_jiffies(duration) || duration > 5000)
4644 return -EINVAL;
4645
4646 rtnl_lock();
4647
4648 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
4649 if (err)
4650 goto unlock_rtnl;
4651
4652 if (!rdev->ops->remain_on_channel) {
4653 err = -EOPNOTSUPP;
4654 goto out;
4655 }
4656
4657 if (!netif_running(dev)) {
4658 err = -ENETDOWN;
4659 goto out;
4660 }
4661
4662 if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) {
4663 channel_type = nla_get_u32(
4664 info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]);
4665 if (channel_type != NL80211_CHAN_NO_HT &&
4666 channel_type != NL80211_CHAN_HT20 &&
4667 channel_type != NL80211_CHAN_HT40PLUS &&
Johannes Berg579d7532010-05-18 14:36:34 +02004668 channel_type != NL80211_CHAN_HT40MINUS) {
Jouni Malinen9588bbd2009-12-23 13:15:41 +01004669 err = -EINVAL;
4670 goto out;
Johannes Berg579d7532010-05-18 14:36:34 +02004671 }
Jouni Malinen9588bbd2009-12-23 13:15:41 +01004672 }
4673
4674 freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]);
4675 chan = rdev_freq_to_chan(rdev, freq, channel_type);
4676 if (chan == NULL) {
4677 err = -EINVAL;
4678 goto out;
4679 }
4680
4681 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
4682 if (!msg) {
4683 err = -ENOMEM;
4684 goto out;
4685 }
4686
4687 hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
4688 NL80211_CMD_REMAIN_ON_CHANNEL);
4689
4690 if (IS_ERR(hdr)) {
4691 err = PTR_ERR(hdr);
4692 goto free_msg;
4693 }
4694
4695 err = rdev->ops->remain_on_channel(&rdev->wiphy, dev, chan,
4696 channel_type, duration, &cookie);
4697
4698 if (err)
4699 goto free_msg;
4700
4701 NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, cookie);
4702
4703 genlmsg_end(msg, hdr);
4704 err = genlmsg_reply(msg, info);
4705 goto out;
4706
4707 nla_put_failure:
4708 err = -ENOBUFS;
4709 free_msg:
4710 nlmsg_free(msg);
4711 out:
4712 cfg80211_unlock_rdev(rdev);
4713 dev_put(dev);
4714 unlock_rtnl:
4715 rtnl_unlock();
4716 return err;
4717}
4718
4719static int nl80211_cancel_remain_on_channel(struct sk_buff *skb,
4720 struct genl_info *info)
4721{
4722 struct cfg80211_registered_device *rdev;
4723 struct net_device *dev;
4724 u64 cookie;
4725 int err;
4726
4727 if (!info->attrs[NL80211_ATTR_COOKIE])
4728 return -EINVAL;
4729
4730 rtnl_lock();
4731
4732 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
4733 if (err)
4734 goto unlock_rtnl;
4735
4736 if (!rdev->ops->cancel_remain_on_channel) {
4737 err = -EOPNOTSUPP;
4738 goto out;
4739 }
4740
4741 if (!netif_running(dev)) {
4742 err = -ENETDOWN;
4743 goto out;
4744 }
4745
4746 cookie = nla_get_u64(info->attrs[NL80211_ATTR_COOKIE]);
4747
4748 err = rdev->ops->cancel_remain_on_channel(&rdev->wiphy, dev, cookie);
4749
4750 out:
4751 cfg80211_unlock_rdev(rdev);
4752 dev_put(dev);
4753 unlock_rtnl:
4754 rtnl_unlock();
4755 return err;
4756}
4757
Jouni Malinen13ae75b2009-12-29 12:59:45 +02004758static u32 rateset_to_mask(struct ieee80211_supported_band *sband,
4759 u8 *rates, u8 rates_len)
4760{
4761 u8 i;
4762 u32 mask = 0;
4763
4764 for (i = 0; i < rates_len; i++) {
4765 int rate = (rates[i] & 0x7f) * 5;
4766 int ridx;
4767 for (ridx = 0; ridx < sband->n_bitrates; ridx++) {
4768 struct ieee80211_rate *srate =
4769 &sband->bitrates[ridx];
4770 if (rate == srate->bitrate) {
4771 mask |= 1 << ridx;
4772 break;
4773 }
4774 }
4775 if (ridx == sband->n_bitrates)
4776 return 0; /* rate not found */
4777 }
4778
4779 return mask;
4780}
4781
Alexey Dobriyanb54452b2010-02-18 08:14:31 +00004782static const struct nla_policy nl80211_txattr_policy[NL80211_TXRATE_MAX + 1] = {
Jouni Malinen13ae75b2009-12-29 12:59:45 +02004783 [NL80211_TXRATE_LEGACY] = { .type = NLA_BINARY,
4784 .len = NL80211_MAX_SUPP_RATES },
4785};
4786
4787static int nl80211_set_tx_bitrate_mask(struct sk_buff *skb,
4788 struct genl_info *info)
4789{
4790 struct nlattr *tb[NL80211_TXRATE_MAX + 1];
4791 struct cfg80211_registered_device *rdev;
4792 struct cfg80211_bitrate_mask mask;
4793 int err, rem, i;
4794 struct net_device *dev;
4795 struct nlattr *tx_rates;
4796 struct ieee80211_supported_band *sband;
4797
4798 if (info->attrs[NL80211_ATTR_TX_RATES] == NULL)
4799 return -EINVAL;
4800
4801 rtnl_lock();
4802
4803 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
4804 if (err)
4805 goto unlock_rtnl;
4806
4807 if (!rdev->ops->set_bitrate_mask) {
4808 err = -EOPNOTSUPP;
4809 goto unlock;
4810 }
4811
4812 memset(&mask, 0, sizeof(mask));
4813 /* Default to all rates enabled */
4814 for (i = 0; i < IEEE80211_NUM_BANDS; i++) {
4815 sband = rdev->wiphy.bands[i];
4816 mask.control[i].legacy =
4817 sband ? (1 << sband->n_bitrates) - 1 : 0;
4818 }
4819
4820 /*
4821 * The nested attribute uses enum nl80211_band as the index. This maps
4822 * directly to the enum ieee80211_band values used in cfg80211.
4823 */
4824 nla_for_each_nested(tx_rates, info->attrs[NL80211_ATTR_TX_RATES], rem)
4825 {
4826 enum ieee80211_band band = nla_type(tx_rates);
4827 if (band < 0 || band >= IEEE80211_NUM_BANDS) {
4828 err = -EINVAL;
4829 goto unlock;
4830 }
4831 sband = rdev->wiphy.bands[band];
4832 if (sband == NULL) {
4833 err = -EINVAL;
4834 goto unlock;
4835 }
4836 nla_parse(tb, NL80211_TXRATE_MAX, nla_data(tx_rates),
4837 nla_len(tx_rates), nl80211_txattr_policy);
4838 if (tb[NL80211_TXRATE_LEGACY]) {
4839 mask.control[band].legacy = rateset_to_mask(
4840 sband,
4841 nla_data(tb[NL80211_TXRATE_LEGACY]),
4842 nla_len(tb[NL80211_TXRATE_LEGACY]));
4843 if (mask.control[band].legacy == 0) {
4844 err = -EINVAL;
4845 goto unlock;
4846 }
4847 }
4848 }
4849
4850 err = rdev->ops->set_bitrate_mask(&rdev->wiphy, dev, NULL, &mask);
4851
4852 unlock:
4853 dev_put(dev);
4854 cfg80211_unlock_rdev(rdev);
4855 unlock_rtnl:
4856 rtnl_unlock();
4857 return err;
4858}
4859
Johannes Berg2e161f72010-08-12 15:38:38 +02004860static int nl80211_register_mgmt(struct sk_buff *skb, struct genl_info *info)
Jouni Malinen026331c2010-02-15 12:53:10 +02004861{
4862 struct cfg80211_registered_device *rdev;
4863 struct net_device *dev;
Johannes Berg2e161f72010-08-12 15:38:38 +02004864 u16 frame_type = IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_ACTION;
Jouni Malinen026331c2010-02-15 12:53:10 +02004865 int err;
4866
4867 if (!info->attrs[NL80211_ATTR_FRAME_MATCH])
4868 return -EINVAL;
4869
Johannes Berg2e161f72010-08-12 15:38:38 +02004870 if (info->attrs[NL80211_ATTR_FRAME_TYPE])
4871 frame_type = nla_get_u16(info->attrs[NL80211_ATTR_FRAME_TYPE]);
Jouni Malinen026331c2010-02-15 12:53:10 +02004872
4873 rtnl_lock();
4874
4875 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
4876 if (err)
4877 goto unlock_rtnl;
4878
Johannes Berg9d38d852010-06-09 17:20:33 +02004879 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg074ac8d2010-09-16 14:58:22 +02004880 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC &&
Johannes Berg663fcaf2010-09-30 21:06:09 +02004881 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT &&
4882 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
4883 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN &&
4884 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) {
Jouni Malinen026331c2010-02-15 12:53:10 +02004885 err = -EOPNOTSUPP;
4886 goto out;
4887 }
4888
4889 /* not much point in registering if we can't reply */
Johannes Berg2e161f72010-08-12 15:38:38 +02004890 if (!rdev->ops->mgmt_tx) {
Jouni Malinen026331c2010-02-15 12:53:10 +02004891 err = -EOPNOTSUPP;
4892 goto out;
4893 }
4894
Johannes Berg2e161f72010-08-12 15:38:38 +02004895 err = cfg80211_mlme_register_mgmt(dev->ieee80211_ptr, info->snd_pid,
4896 frame_type,
Jouni Malinen026331c2010-02-15 12:53:10 +02004897 nla_data(info->attrs[NL80211_ATTR_FRAME_MATCH]),
4898 nla_len(info->attrs[NL80211_ATTR_FRAME_MATCH]));
4899 out:
4900 cfg80211_unlock_rdev(rdev);
4901 dev_put(dev);
4902 unlock_rtnl:
4903 rtnl_unlock();
4904 return err;
4905}
4906
Johannes Berg2e161f72010-08-12 15:38:38 +02004907static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info)
Jouni Malinen026331c2010-02-15 12:53:10 +02004908{
4909 struct cfg80211_registered_device *rdev;
4910 struct net_device *dev;
4911 struct ieee80211_channel *chan;
4912 enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT;
Johannes Berg252aa632010-05-19 12:17:12 +02004913 bool channel_type_valid = false;
Jouni Malinen026331c2010-02-15 12:53:10 +02004914 u32 freq;
4915 int err;
4916 void *hdr;
4917 u64 cookie;
4918 struct sk_buff *msg;
4919
4920 if (!info->attrs[NL80211_ATTR_FRAME] ||
4921 !info->attrs[NL80211_ATTR_WIPHY_FREQ])
4922 return -EINVAL;
4923
4924 rtnl_lock();
4925
4926 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
4927 if (err)
4928 goto unlock_rtnl;
4929
Johannes Berg2e161f72010-08-12 15:38:38 +02004930 if (!rdev->ops->mgmt_tx) {
Jouni Malinen026331c2010-02-15 12:53:10 +02004931 err = -EOPNOTSUPP;
4932 goto out;
4933 }
4934
Johannes Berg9d38d852010-06-09 17:20:33 +02004935 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg074ac8d2010-09-16 14:58:22 +02004936 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC &&
Johannes Berg663fcaf2010-09-30 21:06:09 +02004937 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT &&
4938 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
4939 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN &&
4940 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) {
Jouni Malinen026331c2010-02-15 12:53:10 +02004941 err = -EOPNOTSUPP;
4942 goto out;
4943 }
4944
4945 if (!netif_running(dev)) {
4946 err = -ENETDOWN;
4947 goto out;
4948 }
4949
4950 if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) {
4951 channel_type = nla_get_u32(
4952 info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]);
4953 if (channel_type != NL80211_CHAN_NO_HT &&
4954 channel_type != NL80211_CHAN_HT20 &&
4955 channel_type != NL80211_CHAN_HT40PLUS &&
Johannes Berg579d7532010-05-18 14:36:34 +02004956 channel_type != NL80211_CHAN_HT40MINUS) {
Jouni Malinen026331c2010-02-15 12:53:10 +02004957 err = -EINVAL;
4958 goto out;
Johannes Berg579d7532010-05-18 14:36:34 +02004959 }
Johannes Berg252aa632010-05-19 12:17:12 +02004960 channel_type_valid = true;
Jouni Malinen026331c2010-02-15 12:53:10 +02004961 }
4962
4963 freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]);
4964 chan = rdev_freq_to_chan(rdev, freq, channel_type);
4965 if (chan == NULL) {
4966 err = -EINVAL;
4967 goto out;
4968 }
4969
4970 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
4971 if (!msg) {
4972 err = -ENOMEM;
4973 goto out;
4974 }
4975
4976 hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
Johannes Berg2e161f72010-08-12 15:38:38 +02004977 NL80211_CMD_FRAME);
Jouni Malinen026331c2010-02-15 12:53:10 +02004978
4979 if (IS_ERR(hdr)) {
4980 err = PTR_ERR(hdr);
4981 goto free_msg;
4982 }
Johannes Berg2e161f72010-08-12 15:38:38 +02004983 err = cfg80211_mlme_mgmt_tx(rdev, dev, chan, channel_type,
4984 channel_type_valid,
4985 nla_data(info->attrs[NL80211_ATTR_FRAME]),
4986 nla_len(info->attrs[NL80211_ATTR_FRAME]),
4987 &cookie);
Jouni Malinen026331c2010-02-15 12:53:10 +02004988 if (err)
4989 goto free_msg;
4990
4991 NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, cookie);
4992
4993 genlmsg_end(msg, hdr);
4994 err = genlmsg_reply(msg, info);
4995 goto out;
4996
4997 nla_put_failure:
4998 err = -ENOBUFS;
4999 free_msg:
5000 nlmsg_free(msg);
5001 out:
5002 cfg80211_unlock_rdev(rdev);
5003 dev_put(dev);
5004unlock_rtnl:
5005 rtnl_unlock();
5006 return err;
5007}
5008
Kalle Valoffb9eb32010-02-17 17:58:10 +02005009static int nl80211_set_power_save(struct sk_buff *skb, struct genl_info *info)
5010{
5011 struct cfg80211_registered_device *rdev;
5012 struct wireless_dev *wdev;
5013 struct net_device *dev;
5014 u8 ps_state;
5015 bool state;
5016 int err;
5017
5018 if (!info->attrs[NL80211_ATTR_PS_STATE]) {
5019 err = -EINVAL;
5020 goto out;
5021 }
5022
5023 ps_state = nla_get_u32(info->attrs[NL80211_ATTR_PS_STATE]);
5024
5025 if (ps_state != NL80211_PS_DISABLED && ps_state != NL80211_PS_ENABLED) {
5026 err = -EINVAL;
5027 goto out;
5028 }
5029
5030 rtnl_lock();
5031
5032 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
5033 if (err)
Teemu Paasikivi92e44942010-09-24 07:23:55 +03005034 goto unlock_rtnl;
Kalle Valoffb9eb32010-02-17 17:58:10 +02005035
5036 wdev = dev->ieee80211_ptr;
5037
5038 if (!rdev->ops->set_power_mgmt) {
5039 err = -EOPNOTSUPP;
5040 goto unlock_rdev;
5041 }
5042
5043 state = (ps_state == NL80211_PS_ENABLED) ? true : false;
5044
5045 if (state == wdev->ps)
5046 goto unlock_rdev;
5047
5048 wdev->ps = state;
5049
5050 if (rdev->ops->set_power_mgmt(wdev->wiphy, dev, wdev->ps,
5051 wdev->ps_timeout))
5052 /* assume this means it's off */
5053 wdev->ps = false;
5054
5055unlock_rdev:
5056 cfg80211_unlock_rdev(rdev);
5057 dev_put(dev);
Teemu Paasikivi92e44942010-09-24 07:23:55 +03005058unlock_rtnl:
Kalle Valoffb9eb32010-02-17 17:58:10 +02005059 rtnl_unlock();
5060
5061out:
5062 return err;
5063}
5064
5065static int nl80211_get_power_save(struct sk_buff *skb, struct genl_info *info)
5066{
5067 struct cfg80211_registered_device *rdev;
5068 enum nl80211_ps_state ps_state;
5069 struct wireless_dev *wdev;
5070 struct net_device *dev;
5071 struct sk_buff *msg;
5072 void *hdr;
5073 int err;
5074
5075 rtnl_lock();
5076
5077 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
5078 if (err)
5079 goto unlock_rtnl;
5080
5081 wdev = dev->ieee80211_ptr;
5082
5083 if (!rdev->ops->set_power_mgmt) {
5084 err = -EOPNOTSUPP;
5085 goto out;
5086 }
5087
5088 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
5089 if (!msg) {
5090 err = -ENOMEM;
5091 goto out;
5092 }
5093
5094 hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
5095 NL80211_CMD_GET_POWER_SAVE);
5096 if (!hdr) {
5097 err = -ENOMEM;
5098 goto free_msg;
5099 }
5100
5101 if (wdev->ps)
5102 ps_state = NL80211_PS_ENABLED;
5103 else
5104 ps_state = NL80211_PS_DISABLED;
5105
5106 NLA_PUT_U32(msg, NL80211_ATTR_PS_STATE, ps_state);
5107
5108 genlmsg_end(msg, hdr);
5109 err = genlmsg_reply(msg, info);
5110 goto out;
5111
5112nla_put_failure:
5113 err = -ENOBUFS;
5114
5115free_msg:
5116 nlmsg_free(msg);
5117
5118out:
5119 cfg80211_unlock_rdev(rdev);
5120 dev_put(dev);
5121
5122unlock_rtnl:
5123 rtnl_unlock();
5124
5125 return err;
5126}
5127
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02005128static struct nla_policy
5129nl80211_attr_cqm_policy[NL80211_ATTR_CQM_MAX + 1] __read_mostly = {
5130 [NL80211_ATTR_CQM_RSSI_THOLD] = { .type = NLA_U32 },
5131 [NL80211_ATTR_CQM_RSSI_HYST] = { .type = NLA_U32 },
5132 [NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] = { .type = NLA_U32 },
5133};
5134
5135static int nl80211_set_cqm_rssi(struct genl_info *info,
5136 s32 threshold, u32 hysteresis)
5137{
5138 struct cfg80211_registered_device *rdev;
5139 struct wireless_dev *wdev;
5140 struct net_device *dev;
5141 int err;
5142
5143 if (threshold > 0)
5144 return -EINVAL;
5145
5146 rtnl_lock();
5147
5148 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
5149 if (err)
Johannes Berg22343622010-09-30 22:17:43 +02005150 goto unlock_rtnl;
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02005151
5152 wdev = dev->ieee80211_ptr;
5153
5154 if (!rdev->ops->set_cqm_rssi_config) {
5155 err = -EOPNOTSUPP;
5156 goto unlock_rdev;
5157 }
5158
Johannes Berg074ac8d2010-09-16 14:58:22 +02005159 if (wdev->iftype != NL80211_IFTYPE_STATION &&
5160 wdev->iftype != NL80211_IFTYPE_P2P_CLIENT) {
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02005161 err = -EOPNOTSUPP;
5162 goto unlock_rdev;
5163 }
5164
5165 err = rdev->ops->set_cqm_rssi_config(wdev->wiphy, dev,
5166 threshold, hysteresis);
5167
Johannes Berg22343622010-09-30 22:17:43 +02005168 unlock_rdev:
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02005169 cfg80211_unlock_rdev(rdev);
5170 dev_put(dev);
Johannes Berg22343622010-09-30 22:17:43 +02005171 unlock_rtnl:
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02005172 rtnl_unlock();
5173
5174 return err;
5175}
5176
5177static int nl80211_set_cqm(struct sk_buff *skb, struct genl_info *info)
5178{
5179 struct nlattr *attrs[NL80211_ATTR_CQM_MAX + 1];
5180 struct nlattr *cqm;
5181 int err;
5182
5183 cqm = info->attrs[NL80211_ATTR_CQM];
5184 if (!cqm) {
5185 err = -EINVAL;
5186 goto out;
5187 }
5188
5189 err = nla_parse_nested(attrs, NL80211_ATTR_CQM_MAX, cqm,
5190 nl80211_attr_cqm_policy);
5191 if (err)
5192 goto out;
5193
5194 if (attrs[NL80211_ATTR_CQM_RSSI_THOLD] &&
5195 attrs[NL80211_ATTR_CQM_RSSI_HYST]) {
5196 s32 threshold;
5197 u32 hysteresis;
5198 threshold = nla_get_u32(attrs[NL80211_ATTR_CQM_RSSI_THOLD]);
5199 hysteresis = nla_get_u32(attrs[NL80211_ATTR_CQM_RSSI_HYST]);
5200 err = nl80211_set_cqm_rssi(info, threshold, hysteresis);
5201 } else
5202 err = -EINVAL;
5203
5204out:
5205 return err;
5206}
5207
Johannes Berg55682962007-09-20 13:09:35 -04005208static struct genl_ops nl80211_ops[] = {
5209 {
5210 .cmd = NL80211_CMD_GET_WIPHY,
5211 .doit = nl80211_get_wiphy,
5212 .dumpit = nl80211_dump_wiphy,
5213 .policy = nl80211_policy,
5214 /* can be retrieved by unprivileged users */
5215 },
5216 {
5217 .cmd = NL80211_CMD_SET_WIPHY,
5218 .doit = nl80211_set_wiphy,
5219 .policy = nl80211_policy,
5220 .flags = GENL_ADMIN_PERM,
5221 },
5222 {
5223 .cmd = NL80211_CMD_GET_INTERFACE,
5224 .doit = nl80211_get_interface,
5225 .dumpit = nl80211_dump_interface,
5226 .policy = nl80211_policy,
5227 /* can be retrieved by unprivileged users */
5228 },
5229 {
5230 .cmd = NL80211_CMD_SET_INTERFACE,
5231 .doit = nl80211_set_interface,
5232 .policy = nl80211_policy,
5233 .flags = GENL_ADMIN_PERM,
5234 },
5235 {
5236 .cmd = NL80211_CMD_NEW_INTERFACE,
5237 .doit = nl80211_new_interface,
5238 .policy = nl80211_policy,
5239 .flags = GENL_ADMIN_PERM,
5240 },
5241 {
5242 .cmd = NL80211_CMD_DEL_INTERFACE,
5243 .doit = nl80211_del_interface,
5244 .policy = nl80211_policy,
5245 .flags = GENL_ADMIN_PERM,
5246 },
Johannes Berg41ade002007-12-19 02:03:29 +01005247 {
5248 .cmd = NL80211_CMD_GET_KEY,
5249 .doit = nl80211_get_key,
5250 .policy = nl80211_policy,
5251 .flags = GENL_ADMIN_PERM,
5252 },
5253 {
5254 .cmd = NL80211_CMD_SET_KEY,
5255 .doit = nl80211_set_key,
5256 .policy = nl80211_policy,
5257 .flags = GENL_ADMIN_PERM,
5258 },
5259 {
5260 .cmd = NL80211_CMD_NEW_KEY,
5261 .doit = nl80211_new_key,
5262 .policy = nl80211_policy,
5263 .flags = GENL_ADMIN_PERM,
5264 },
5265 {
5266 .cmd = NL80211_CMD_DEL_KEY,
5267 .doit = nl80211_del_key,
5268 .policy = nl80211_policy,
5269 .flags = GENL_ADMIN_PERM,
5270 },
Johannes Berged1b6cc2007-12-19 02:03:32 +01005271 {
5272 .cmd = NL80211_CMD_SET_BEACON,
5273 .policy = nl80211_policy,
5274 .flags = GENL_ADMIN_PERM,
5275 .doit = nl80211_addset_beacon,
5276 },
5277 {
5278 .cmd = NL80211_CMD_NEW_BEACON,
5279 .policy = nl80211_policy,
5280 .flags = GENL_ADMIN_PERM,
5281 .doit = nl80211_addset_beacon,
5282 },
5283 {
5284 .cmd = NL80211_CMD_DEL_BEACON,
5285 .policy = nl80211_policy,
5286 .flags = GENL_ADMIN_PERM,
5287 .doit = nl80211_del_beacon,
5288 },
Johannes Berg5727ef12007-12-19 02:03:34 +01005289 {
5290 .cmd = NL80211_CMD_GET_STATION,
5291 .doit = nl80211_get_station,
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01005292 .dumpit = nl80211_dump_station,
Johannes Berg5727ef12007-12-19 02:03:34 +01005293 .policy = nl80211_policy,
Johannes Berg5727ef12007-12-19 02:03:34 +01005294 },
5295 {
5296 .cmd = NL80211_CMD_SET_STATION,
5297 .doit = nl80211_set_station,
5298 .policy = nl80211_policy,
5299 .flags = GENL_ADMIN_PERM,
5300 },
5301 {
5302 .cmd = NL80211_CMD_NEW_STATION,
5303 .doit = nl80211_new_station,
5304 .policy = nl80211_policy,
5305 .flags = GENL_ADMIN_PERM,
5306 },
5307 {
5308 .cmd = NL80211_CMD_DEL_STATION,
5309 .doit = nl80211_del_station,
5310 .policy = nl80211_policy,
5311 .flags = GENL_ADMIN_PERM,
5312 },
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01005313 {
5314 .cmd = NL80211_CMD_GET_MPATH,
5315 .doit = nl80211_get_mpath,
5316 .dumpit = nl80211_dump_mpath,
5317 .policy = nl80211_policy,
5318 .flags = GENL_ADMIN_PERM,
5319 },
5320 {
5321 .cmd = NL80211_CMD_SET_MPATH,
5322 .doit = nl80211_set_mpath,
5323 .policy = nl80211_policy,
5324 .flags = GENL_ADMIN_PERM,
5325 },
5326 {
5327 .cmd = NL80211_CMD_NEW_MPATH,
5328 .doit = nl80211_new_mpath,
5329 .policy = nl80211_policy,
5330 .flags = GENL_ADMIN_PERM,
5331 },
5332 {
5333 .cmd = NL80211_CMD_DEL_MPATH,
5334 .doit = nl80211_del_mpath,
5335 .policy = nl80211_policy,
5336 .flags = GENL_ADMIN_PERM,
5337 },
Jouni Malinen9f1ba902008-08-07 20:07:01 +03005338 {
5339 .cmd = NL80211_CMD_SET_BSS,
5340 .doit = nl80211_set_bss,
5341 .policy = nl80211_policy,
5342 .flags = GENL_ADMIN_PERM,
5343 },
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07005344 {
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08005345 .cmd = NL80211_CMD_GET_REG,
5346 .doit = nl80211_get_reg,
5347 .policy = nl80211_policy,
5348 /* can be retrieved by unprivileged users */
5349 },
5350 {
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07005351 .cmd = NL80211_CMD_SET_REG,
5352 .doit = nl80211_set_reg,
5353 .policy = nl80211_policy,
5354 .flags = GENL_ADMIN_PERM,
5355 },
5356 {
5357 .cmd = NL80211_CMD_REQ_SET_REG,
5358 .doit = nl80211_req_set_reg,
5359 .policy = nl80211_policy,
5360 .flags = GENL_ADMIN_PERM,
5361 },
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07005362 {
5363 .cmd = NL80211_CMD_GET_MESH_PARAMS,
5364 .doit = nl80211_get_mesh_params,
5365 .policy = nl80211_policy,
5366 /* can be retrieved by unprivileged users */
5367 },
5368 {
5369 .cmd = NL80211_CMD_SET_MESH_PARAMS,
5370 .doit = nl80211_set_mesh_params,
5371 .policy = nl80211_policy,
5372 .flags = GENL_ADMIN_PERM,
5373 },
Jouni Malinen9aed3cc2009-01-13 16:03:29 +02005374 {
Johannes Berg2a519312009-02-10 21:25:55 +01005375 .cmd = NL80211_CMD_TRIGGER_SCAN,
5376 .doit = nl80211_trigger_scan,
5377 .policy = nl80211_policy,
5378 .flags = GENL_ADMIN_PERM,
5379 },
5380 {
5381 .cmd = NL80211_CMD_GET_SCAN,
5382 .policy = nl80211_policy,
5383 .dumpit = nl80211_dump_scan,
5384 },
Jouni Malinen636a5d32009-03-19 13:39:22 +02005385 {
5386 .cmd = NL80211_CMD_AUTHENTICATE,
5387 .doit = nl80211_authenticate,
5388 .policy = nl80211_policy,
5389 .flags = GENL_ADMIN_PERM,
5390 },
5391 {
5392 .cmd = NL80211_CMD_ASSOCIATE,
5393 .doit = nl80211_associate,
5394 .policy = nl80211_policy,
5395 .flags = GENL_ADMIN_PERM,
5396 },
5397 {
5398 .cmd = NL80211_CMD_DEAUTHENTICATE,
5399 .doit = nl80211_deauthenticate,
5400 .policy = nl80211_policy,
5401 .flags = GENL_ADMIN_PERM,
5402 },
5403 {
5404 .cmd = NL80211_CMD_DISASSOCIATE,
5405 .doit = nl80211_disassociate,
5406 .policy = nl80211_policy,
5407 .flags = GENL_ADMIN_PERM,
5408 },
Johannes Berg04a773a2009-04-19 21:24:32 +02005409 {
5410 .cmd = NL80211_CMD_JOIN_IBSS,
5411 .doit = nl80211_join_ibss,
5412 .policy = nl80211_policy,
5413 .flags = GENL_ADMIN_PERM,
5414 },
5415 {
5416 .cmd = NL80211_CMD_LEAVE_IBSS,
5417 .doit = nl80211_leave_ibss,
5418 .policy = nl80211_policy,
5419 .flags = GENL_ADMIN_PERM,
5420 },
Johannes Bergaff89a92009-07-01 21:26:51 +02005421#ifdef CONFIG_NL80211_TESTMODE
5422 {
5423 .cmd = NL80211_CMD_TESTMODE,
5424 .doit = nl80211_testmode_do,
5425 .policy = nl80211_policy,
5426 .flags = GENL_ADMIN_PERM,
5427 },
5428#endif
Samuel Ortizb23aa672009-07-01 21:26:54 +02005429 {
5430 .cmd = NL80211_CMD_CONNECT,
5431 .doit = nl80211_connect,
5432 .policy = nl80211_policy,
5433 .flags = GENL_ADMIN_PERM,
5434 },
5435 {
5436 .cmd = NL80211_CMD_DISCONNECT,
5437 .doit = nl80211_disconnect,
5438 .policy = nl80211_policy,
5439 .flags = GENL_ADMIN_PERM,
5440 },
Johannes Berg463d0182009-07-14 00:33:35 +02005441 {
5442 .cmd = NL80211_CMD_SET_WIPHY_NETNS,
5443 .doit = nl80211_wiphy_netns,
5444 .policy = nl80211_policy,
5445 .flags = GENL_ADMIN_PERM,
5446 },
Holger Schurig61fa7132009-11-11 12:25:40 +01005447 {
5448 .cmd = NL80211_CMD_GET_SURVEY,
5449 .policy = nl80211_policy,
5450 .dumpit = nl80211_dump_survey,
5451 },
Samuel Ortiz67fbb162009-11-24 23:59:15 +01005452 {
5453 .cmd = NL80211_CMD_SET_PMKSA,
5454 .doit = nl80211_setdel_pmksa,
5455 .policy = nl80211_policy,
5456 .flags = GENL_ADMIN_PERM,
5457 },
5458 {
5459 .cmd = NL80211_CMD_DEL_PMKSA,
5460 .doit = nl80211_setdel_pmksa,
5461 .policy = nl80211_policy,
5462 .flags = GENL_ADMIN_PERM,
5463 },
5464 {
5465 .cmd = NL80211_CMD_FLUSH_PMKSA,
5466 .doit = nl80211_flush_pmksa,
5467 .policy = nl80211_policy,
5468 .flags = GENL_ADMIN_PERM,
5469 },
Jouni Malinen9588bbd2009-12-23 13:15:41 +01005470 {
5471 .cmd = NL80211_CMD_REMAIN_ON_CHANNEL,
5472 .doit = nl80211_remain_on_channel,
5473 .policy = nl80211_policy,
5474 .flags = GENL_ADMIN_PERM,
5475 },
5476 {
5477 .cmd = NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL,
5478 .doit = nl80211_cancel_remain_on_channel,
5479 .policy = nl80211_policy,
5480 .flags = GENL_ADMIN_PERM,
5481 },
Jouni Malinen13ae75b2009-12-29 12:59:45 +02005482 {
5483 .cmd = NL80211_CMD_SET_TX_BITRATE_MASK,
5484 .doit = nl80211_set_tx_bitrate_mask,
5485 .policy = nl80211_policy,
5486 .flags = GENL_ADMIN_PERM,
5487 },
Jouni Malinen026331c2010-02-15 12:53:10 +02005488 {
Johannes Berg2e161f72010-08-12 15:38:38 +02005489 .cmd = NL80211_CMD_REGISTER_FRAME,
5490 .doit = nl80211_register_mgmt,
Jouni Malinen026331c2010-02-15 12:53:10 +02005491 .policy = nl80211_policy,
5492 .flags = GENL_ADMIN_PERM,
5493 },
5494 {
Johannes Berg2e161f72010-08-12 15:38:38 +02005495 .cmd = NL80211_CMD_FRAME,
5496 .doit = nl80211_tx_mgmt,
Jouni Malinen026331c2010-02-15 12:53:10 +02005497 .policy = nl80211_policy,
5498 .flags = GENL_ADMIN_PERM,
5499 },
Kalle Valoffb9eb32010-02-17 17:58:10 +02005500 {
5501 .cmd = NL80211_CMD_SET_POWER_SAVE,
5502 .doit = nl80211_set_power_save,
5503 .policy = nl80211_policy,
5504 .flags = GENL_ADMIN_PERM,
5505 },
5506 {
5507 .cmd = NL80211_CMD_GET_POWER_SAVE,
5508 .doit = nl80211_get_power_save,
5509 .policy = nl80211_policy,
5510 /* can be retrieved by unprivileged users */
5511 },
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02005512 {
5513 .cmd = NL80211_CMD_SET_CQM,
5514 .doit = nl80211_set_cqm,
5515 .policy = nl80211_policy,
5516 .flags = GENL_ADMIN_PERM,
5517 },
Johannes Bergf444de02010-05-05 15:25:02 +02005518 {
5519 .cmd = NL80211_CMD_SET_CHANNEL,
5520 .doit = nl80211_set_channel,
5521 .policy = nl80211_policy,
5522 .flags = GENL_ADMIN_PERM,
5523 },
Bill Jordane8347eb2010-10-01 13:54:28 -04005524 {
5525 .cmd = NL80211_CMD_SET_WDS_PEER,
5526 .doit = nl80211_set_wds_peer,
5527 .policy = nl80211_policy,
5528 .flags = GENL_ADMIN_PERM,
5529 },
Johannes Berg55682962007-09-20 13:09:35 -04005530};
Jouni Malinen9588bbd2009-12-23 13:15:41 +01005531
Jouni Malinen6039f6d2009-03-19 13:39:21 +02005532static struct genl_multicast_group nl80211_mlme_mcgrp = {
5533 .name = "mlme",
5534};
Johannes Berg55682962007-09-20 13:09:35 -04005535
5536/* multicast groups */
5537static struct genl_multicast_group nl80211_config_mcgrp = {
5538 .name = "config",
5539};
Johannes Berg2a519312009-02-10 21:25:55 +01005540static struct genl_multicast_group nl80211_scan_mcgrp = {
5541 .name = "scan",
5542};
Luis R. Rodriguez73d54c92009-03-09 22:07:42 -04005543static struct genl_multicast_group nl80211_regulatory_mcgrp = {
5544 .name = "regulatory",
5545};
Johannes Berg55682962007-09-20 13:09:35 -04005546
5547/* notification functions */
5548
5549void nl80211_notify_dev_rename(struct cfg80211_registered_device *rdev)
5550{
5551 struct sk_buff *msg;
5552
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07005553 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg55682962007-09-20 13:09:35 -04005554 if (!msg)
5555 return;
5556
5557 if (nl80211_send_wiphy(msg, 0, 0, 0, rdev) < 0) {
5558 nlmsg_free(msg);
5559 return;
5560 }
5561
Johannes Berg463d0182009-07-14 00:33:35 +02005562 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
5563 nl80211_config_mcgrp.id, GFP_KERNEL);
Johannes Berg55682962007-09-20 13:09:35 -04005564}
5565
Johannes Berg362a4152009-05-24 16:43:15 +02005566static int nl80211_add_scan_req(struct sk_buff *msg,
5567 struct cfg80211_registered_device *rdev)
5568{
5569 struct cfg80211_scan_request *req = rdev->scan_req;
5570 struct nlattr *nest;
5571 int i;
5572
Johannes Berg667503dd2009-07-07 03:56:11 +02005573 ASSERT_RDEV_LOCK(rdev);
5574
Johannes Berg362a4152009-05-24 16:43:15 +02005575 if (WARN_ON(!req))
5576 return 0;
5577
5578 nest = nla_nest_start(msg, NL80211_ATTR_SCAN_SSIDS);
5579 if (!nest)
5580 goto nla_put_failure;
5581 for (i = 0; i < req->n_ssids; i++)
5582 NLA_PUT(msg, i, req->ssids[i].ssid_len, req->ssids[i].ssid);
5583 nla_nest_end(msg, nest);
5584
5585 nest = nla_nest_start(msg, NL80211_ATTR_SCAN_FREQUENCIES);
5586 if (!nest)
5587 goto nla_put_failure;
5588 for (i = 0; i < req->n_channels; i++)
5589 NLA_PUT_U32(msg, i, req->channels[i]->center_freq);
5590 nla_nest_end(msg, nest);
5591
5592 if (req->ie)
5593 NLA_PUT(msg, NL80211_ATTR_IE, req->ie_len, req->ie);
5594
5595 return 0;
5596 nla_put_failure:
5597 return -ENOBUFS;
5598}
5599
Johannes Berga538e2d2009-06-16 19:56:42 +02005600static int nl80211_send_scan_msg(struct sk_buff *msg,
5601 struct cfg80211_registered_device *rdev,
5602 struct net_device *netdev,
5603 u32 pid, u32 seq, int flags,
5604 u32 cmd)
Johannes Berg2a519312009-02-10 21:25:55 +01005605{
5606 void *hdr;
5607
5608 hdr = nl80211hdr_put(msg, pid, seq, flags, cmd);
5609 if (!hdr)
5610 return -1;
5611
Luis R. Rodriguezb5850a72009-02-21 00:04:19 -05005612 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
Johannes Berg2a519312009-02-10 21:25:55 +01005613 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
5614
Johannes Berg362a4152009-05-24 16:43:15 +02005615 /* ignore errors and send incomplete event anyway */
5616 nl80211_add_scan_req(msg, rdev);
Johannes Berg2a519312009-02-10 21:25:55 +01005617
5618 return genlmsg_end(msg, hdr);
5619
5620 nla_put_failure:
5621 genlmsg_cancel(msg, hdr);
5622 return -EMSGSIZE;
5623}
5624
Johannes Berga538e2d2009-06-16 19:56:42 +02005625void nl80211_send_scan_start(struct cfg80211_registered_device *rdev,
5626 struct net_device *netdev)
5627{
5628 struct sk_buff *msg;
5629
5630 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
5631 if (!msg)
5632 return;
5633
5634 if (nl80211_send_scan_msg(msg, rdev, netdev, 0, 0, 0,
5635 NL80211_CMD_TRIGGER_SCAN) < 0) {
5636 nlmsg_free(msg);
5637 return;
5638 }
5639
Johannes Berg463d0182009-07-14 00:33:35 +02005640 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
5641 nl80211_scan_mcgrp.id, GFP_KERNEL);
Johannes Berga538e2d2009-06-16 19:56:42 +02005642}
5643
Johannes Berg2a519312009-02-10 21:25:55 +01005644void nl80211_send_scan_done(struct cfg80211_registered_device *rdev,
5645 struct net_device *netdev)
5646{
5647 struct sk_buff *msg;
5648
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07005649 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg2a519312009-02-10 21:25:55 +01005650 if (!msg)
5651 return;
5652
Johannes Berga538e2d2009-06-16 19:56:42 +02005653 if (nl80211_send_scan_msg(msg, rdev, netdev, 0, 0, 0,
5654 NL80211_CMD_NEW_SCAN_RESULTS) < 0) {
Johannes Berg2a519312009-02-10 21:25:55 +01005655 nlmsg_free(msg);
5656 return;
5657 }
5658
Johannes Berg463d0182009-07-14 00:33:35 +02005659 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
5660 nl80211_scan_mcgrp.id, GFP_KERNEL);
Johannes Berg2a519312009-02-10 21:25:55 +01005661}
5662
5663void nl80211_send_scan_aborted(struct cfg80211_registered_device *rdev,
5664 struct net_device *netdev)
5665{
5666 struct sk_buff *msg;
5667
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07005668 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg2a519312009-02-10 21:25:55 +01005669 if (!msg)
5670 return;
5671
Johannes Berga538e2d2009-06-16 19:56:42 +02005672 if (nl80211_send_scan_msg(msg, rdev, netdev, 0, 0, 0,
5673 NL80211_CMD_SCAN_ABORTED) < 0) {
Johannes Berg2a519312009-02-10 21:25:55 +01005674 nlmsg_free(msg);
5675 return;
5676 }
5677
Johannes Berg463d0182009-07-14 00:33:35 +02005678 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
5679 nl80211_scan_mcgrp.id, GFP_KERNEL);
Johannes Berg2a519312009-02-10 21:25:55 +01005680}
5681
Luis R. Rodriguez73d54c92009-03-09 22:07:42 -04005682/*
5683 * This can happen on global regulatory changes or device specific settings
5684 * based on custom world regulatory domains.
5685 */
5686void nl80211_send_reg_change_event(struct regulatory_request *request)
5687{
5688 struct sk_buff *msg;
5689 void *hdr;
5690
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07005691 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Luis R. Rodriguez73d54c92009-03-09 22:07:42 -04005692 if (!msg)
5693 return;
5694
5695 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_REG_CHANGE);
5696 if (!hdr) {
5697 nlmsg_free(msg);
5698 return;
5699 }
5700
5701 /* Userspace can always count this one always being set */
5702 NLA_PUT_U8(msg, NL80211_ATTR_REG_INITIATOR, request->initiator);
5703
5704 if (request->alpha2[0] == '0' && request->alpha2[1] == '0')
5705 NLA_PUT_U8(msg, NL80211_ATTR_REG_TYPE,
5706 NL80211_REGDOM_TYPE_WORLD);
5707 else if (request->alpha2[0] == '9' && request->alpha2[1] == '9')
5708 NLA_PUT_U8(msg, NL80211_ATTR_REG_TYPE,
5709 NL80211_REGDOM_TYPE_CUSTOM_WORLD);
5710 else if ((request->alpha2[0] == '9' && request->alpha2[1] == '8') ||
5711 request->intersect)
5712 NLA_PUT_U8(msg, NL80211_ATTR_REG_TYPE,
5713 NL80211_REGDOM_TYPE_INTERSECTION);
5714 else {
5715 NLA_PUT_U8(msg, NL80211_ATTR_REG_TYPE,
5716 NL80211_REGDOM_TYPE_COUNTRY);
5717 NLA_PUT_STRING(msg, NL80211_ATTR_REG_ALPHA2, request->alpha2);
5718 }
5719
5720 if (wiphy_idx_valid(request->wiphy_idx))
5721 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, request->wiphy_idx);
5722
5723 if (genlmsg_end(msg, hdr) < 0) {
5724 nlmsg_free(msg);
5725 return;
5726 }
5727
Johannes Bergbc43b282009-07-25 10:54:13 +02005728 rcu_read_lock();
Johannes Berg463d0182009-07-14 00:33:35 +02005729 genlmsg_multicast_allns(msg, 0, nl80211_regulatory_mcgrp.id,
Johannes Bergbc43b282009-07-25 10:54:13 +02005730 GFP_ATOMIC);
5731 rcu_read_unlock();
Luis R. Rodriguez73d54c92009-03-09 22:07:42 -04005732
5733 return;
5734
5735nla_put_failure:
5736 genlmsg_cancel(msg, hdr);
5737 nlmsg_free(msg);
5738}
5739
Jouni Malinen6039f6d2009-03-19 13:39:21 +02005740static void nl80211_send_mlme_event(struct cfg80211_registered_device *rdev,
5741 struct net_device *netdev,
5742 const u8 *buf, size_t len,
Johannes Berge6d6e342009-07-01 21:26:47 +02005743 enum nl80211_commands cmd, gfp_t gfp)
Jouni Malinen6039f6d2009-03-19 13:39:21 +02005744{
5745 struct sk_buff *msg;
5746 void *hdr;
5747
Johannes Berge6d6e342009-07-01 21:26:47 +02005748 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
Jouni Malinen6039f6d2009-03-19 13:39:21 +02005749 if (!msg)
5750 return;
5751
5752 hdr = nl80211hdr_put(msg, 0, 0, 0, cmd);
5753 if (!hdr) {
5754 nlmsg_free(msg);
5755 return;
5756 }
5757
5758 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
5759 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
5760 NLA_PUT(msg, NL80211_ATTR_FRAME, len, buf);
5761
5762 if (genlmsg_end(msg, hdr) < 0) {
5763 nlmsg_free(msg);
5764 return;
5765 }
5766
Johannes Berg463d0182009-07-14 00:33:35 +02005767 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
5768 nl80211_mlme_mcgrp.id, gfp);
Jouni Malinen6039f6d2009-03-19 13:39:21 +02005769 return;
5770
5771 nla_put_failure:
5772 genlmsg_cancel(msg, hdr);
5773 nlmsg_free(msg);
5774}
5775
5776void nl80211_send_rx_auth(struct cfg80211_registered_device *rdev,
Johannes Berge6d6e342009-07-01 21:26:47 +02005777 struct net_device *netdev, const u8 *buf,
5778 size_t len, gfp_t gfp)
Jouni Malinen6039f6d2009-03-19 13:39:21 +02005779{
5780 nl80211_send_mlme_event(rdev, netdev, buf, len,
Johannes Berge6d6e342009-07-01 21:26:47 +02005781 NL80211_CMD_AUTHENTICATE, gfp);
Jouni Malinen6039f6d2009-03-19 13:39:21 +02005782}
5783
5784void nl80211_send_rx_assoc(struct cfg80211_registered_device *rdev,
5785 struct net_device *netdev, const u8 *buf,
Johannes Berge6d6e342009-07-01 21:26:47 +02005786 size_t len, gfp_t gfp)
Jouni Malinen6039f6d2009-03-19 13:39:21 +02005787{
Johannes Berge6d6e342009-07-01 21:26:47 +02005788 nl80211_send_mlme_event(rdev, netdev, buf, len,
5789 NL80211_CMD_ASSOCIATE, gfp);
Jouni Malinen6039f6d2009-03-19 13:39:21 +02005790}
5791
Jouni Malinen53b46b82009-03-27 20:53:56 +02005792void nl80211_send_deauth(struct cfg80211_registered_device *rdev,
Johannes Berge6d6e342009-07-01 21:26:47 +02005793 struct net_device *netdev, const u8 *buf,
5794 size_t len, gfp_t gfp)
Jouni Malinen6039f6d2009-03-19 13:39:21 +02005795{
5796 nl80211_send_mlme_event(rdev, netdev, buf, len,
Johannes Berge6d6e342009-07-01 21:26:47 +02005797 NL80211_CMD_DEAUTHENTICATE, gfp);
Jouni Malinen6039f6d2009-03-19 13:39:21 +02005798}
5799
Jouni Malinen53b46b82009-03-27 20:53:56 +02005800void nl80211_send_disassoc(struct cfg80211_registered_device *rdev,
5801 struct net_device *netdev, const u8 *buf,
Johannes Berge6d6e342009-07-01 21:26:47 +02005802 size_t len, gfp_t gfp)
Jouni Malinen6039f6d2009-03-19 13:39:21 +02005803{
5804 nl80211_send_mlme_event(rdev, netdev, buf, len,
Johannes Berge6d6e342009-07-01 21:26:47 +02005805 NL80211_CMD_DISASSOCIATE, gfp);
Jouni Malinen6039f6d2009-03-19 13:39:21 +02005806}
5807
Luis R. Rodriguez1b06bb42009-05-02 00:34:48 -04005808static void nl80211_send_mlme_timeout(struct cfg80211_registered_device *rdev,
5809 struct net_device *netdev, int cmd,
Johannes Berge6d6e342009-07-01 21:26:47 +02005810 const u8 *addr, gfp_t gfp)
Jouni Malinen1965c852009-04-22 21:38:25 +03005811{
5812 struct sk_buff *msg;
5813 void *hdr;
5814
Johannes Berge6d6e342009-07-01 21:26:47 +02005815 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
Jouni Malinen1965c852009-04-22 21:38:25 +03005816 if (!msg)
5817 return;
5818
5819 hdr = nl80211hdr_put(msg, 0, 0, 0, cmd);
5820 if (!hdr) {
5821 nlmsg_free(msg);
5822 return;
5823 }
5824
5825 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
5826 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
5827 NLA_PUT_FLAG(msg, NL80211_ATTR_TIMED_OUT);
5828 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr);
5829
5830 if (genlmsg_end(msg, hdr) < 0) {
5831 nlmsg_free(msg);
5832 return;
5833 }
5834
Johannes Berg463d0182009-07-14 00:33:35 +02005835 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
5836 nl80211_mlme_mcgrp.id, gfp);
Jouni Malinen1965c852009-04-22 21:38:25 +03005837 return;
5838
5839 nla_put_failure:
5840 genlmsg_cancel(msg, hdr);
5841 nlmsg_free(msg);
5842}
5843
5844void nl80211_send_auth_timeout(struct cfg80211_registered_device *rdev,
Johannes Berge6d6e342009-07-01 21:26:47 +02005845 struct net_device *netdev, const u8 *addr,
5846 gfp_t gfp)
Jouni Malinen1965c852009-04-22 21:38:25 +03005847{
5848 nl80211_send_mlme_timeout(rdev, netdev, NL80211_CMD_AUTHENTICATE,
Johannes Berge6d6e342009-07-01 21:26:47 +02005849 addr, gfp);
Jouni Malinen1965c852009-04-22 21:38:25 +03005850}
5851
5852void nl80211_send_assoc_timeout(struct cfg80211_registered_device *rdev,
Johannes Berge6d6e342009-07-01 21:26:47 +02005853 struct net_device *netdev, const u8 *addr,
5854 gfp_t gfp)
Jouni Malinen1965c852009-04-22 21:38:25 +03005855{
Johannes Berge6d6e342009-07-01 21:26:47 +02005856 nl80211_send_mlme_timeout(rdev, netdev, NL80211_CMD_ASSOCIATE,
5857 addr, gfp);
Jouni Malinen1965c852009-04-22 21:38:25 +03005858}
5859
Samuel Ortizb23aa672009-07-01 21:26:54 +02005860void nl80211_send_connect_result(struct cfg80211_registered_device *rdev,
5861 struct net_device *netdev, const u8 *bssid,
5862 const u8 *req_ie, size_t req_ie_len,
5863 const u8 *resp_ie, size_t resp_ie_len,
5864 u16 status, gfp_t gfp)
5865{
5866 struct sk_buff *msg;
5867 void *hdr;
5868
5869 msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
5870 if (!msg)
5871 return;
5872
5873 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_CONNECT);
5874 if (!hdr) {
5875 nlmsg_free(msg);
5876 return;
5877 }
5878
5879 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
5880 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
5881 if (bssid)
5882 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid);
5883 NLA_PUT_U16(msg, NL80211_ATTR_STATUS_CODE, status);
5884 if (req_ie)
5885 NLA_PUT(msg, NL80211_ATTR_REQ_IE, req_ie_len, req_ie);
5886 if (resp_ie)
5887 NLA_PUT(msg, NL80211_ATTR_RESP_IE, resp_ie_len, resp_ie);
5888
5889 if (genlmsg_end(msg, hdr) < 0) {
5890 nlmsg_free(msg);
5891 return;
5892 }
5893
Johannes Berg463d0182009-07-14 00:33:35 +02005894 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
5895 nl80211_mlme_mcgrp.id, gfp);
Samuel Ortizb23aa672009-07-01 21:26:54 +02005896 return;
5897
5898 nla_put_failure:
5899 genlmsg_cancel(msg, hdr);
5900 nlmsg_free(msg);
5901
5902}
5903
5904void nl80211_send_roamed(struct cfg80211_registered_device *rdev,
5905 struct net_device *netdev, const u8 *bssid,
5906 const u8 *req_ie, size_t req_ie_len,
5907 const u8 *resp_ie, size_t resp_ie_len, gfp_t gfp)
5908{
5909 struct sk_buff *msg;
5910 void *hdr;
5911
5912 msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
5913 if (!msg)
5914 return;
5915
5916 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_ROAM);
5917 if (!hdr) {
5918 nlmsg_free(msg);
5919 return;
5920 }
5921
5922 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
5923 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
5924 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid);
5925 if (req_ie)
5926 NLA_PUT(msg, NL80211_ATTR_REQ_IE, req_ie_len, req_ie);
5927 if (resp_ie)
5928 NLA_PUT(msg, NL80211_ATTR_RESP_IE, resp_ie_len, resp_ie);
5929
5930 if (genlmsg_end(msg, hdr) < 0) {
5931 nlmsg_free(msg);
5932 return;
5933 }
5934
Johannes Berg463d0182009-07-14 00:33:35 +02005935 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
5936 nl80211_mlme_mcgrp.id, gfp);
Samuel Ortizb23aa672009-07-01 21:26:54 +02005937 return;
5938
5939 nla_put_failure:
5940 genlmsg_cancel(msg, hdr);
5941 nlmsg_free(msg);
5942
5943}
5944
5945void nl80211_send_disconnected(struct cfg80211_registered_device *rdev,
5946 struct net_device *netdev, u16 reason,
Johannes Berg667503dd2009-07-07 03:56:11 +02005947 const u8 *ie, size_t ie_len, bool from_ap)
Samuel Ortizb23aa672009-07-01 21:26:54 +02005948{
5949 struct sk_buff *msg;
5950 void *hdr;
5951
Johannes Berg667503dd2009-07-07 03:56:11 +02005952 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
Samuel Ortizb23aa672009-07-01 21:26:54 +02005953 if (!msg)
5954 return;
5955
5956 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_DISCONNECT);
5957 if (!hdr) {
5958 nlmsg_free(msg);
5959 return;
5960 }
5961
5962 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
5963 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
5964 if (from_ap && reason)
5965 NLA_PUT_U16(msg, NL80211_ATTR_REASON_CODE, reason);
5966 if (from_ap)
5967 NLA_PUT_FLAG(msg, NL80211_ATTR_DISCONNECTED_BY_AP);
5968 if (ie)
5969 NLA_PUT(msg, NL80211_ATTR_IE, ie_len, ie);
5970
5971 if (genlmsg_end(msg, hdr) < 0) {
5972 nlmsg_free(msg);
5973 return;
5974 }
5975
Johannes Berg463d0182009-07-14 00:33:35 +02005976 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
5977 nl80211_mlme_mcgrp.id, GFP_KERNEL);
Samuel Ortizb23aa672009-07-01 21:26:54 +02005978 return;
5979
5980 nla_put_failure:
5981 genlmsg_cancel(msg, hdr);
5982 nlmsg_free(msg);
5983
5984}
5985
Johannes Berg04a773a2009-04-19 21:24:32 +02005986void nl80211_send_ibss_bssid(struct cfg80211_registered_device *rdev,
5987 struct net_device *netdev, const u8 *bssid,
5988 gfp_t gfp)
5989{
5990 struct sk_buff *msg;
5991 void *hdr;
5992
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07005993 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
Johannes Berg04a773a2009-04-19 21:24:32 +02005994 if (!msg)
5995 return;
5996
5997 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_JOIN_IBSS);
5998 if (!hdr) {
5999 nlmsg_free(msg);
6000 return;
6001 }
6002
6003 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
6004 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
6005 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid);
6006
6007 if (genlmsg_end(msg, hdr) < 0) {
6008 nlmsg_free(msg);
6009 return;
6010 }
6011
Johannes Berg463d0182009-07-14 00:33:35 +02006012 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
6013 nl80211_mlme_mcgrp.id, gfp);
Johannes Berg04a773a2009-04-19 21:24:32 +02006014 return;
6015
6016 nla_put_failure:
6017 genlmsg_cancel(msg, hdr);
6018 nlmsg_free(msg);
6019}
6020
Jouni Malinena3b8b052009-03-27 21:59:49 +02006021void nl80211_michael_mic_failure(struct cfg80211_registered_device *rdev,
6022 struct net_device *netdev, const u8 *addr,
6023 enum nl80211_key_type key_type, int key_id,
Johannes Berge6d6e342009-07-01 21:26:47 +02006024 const u8 *tsc, gfp_t gfp)
Jouni Malinena3b8b052009-03-27 21:59:49 +02006025{
6026 struct sk_buff *msg;
6027 void *hdr;
6028
Johannes Berge6d6e342009-07-01 21:26:47 +02006029 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
Jouni Malinena3b8b052009-03-27 21:59:49 +02006030 if (!msg)
6031 return;
6032
6033 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_MICHAEL_MIC_FAILURE);
6034 if (!hdr) {
6035 nlmsg_free(msg);
6036 return;
6037 }
6038
6039 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
6040 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
6041 if (addr)
6042 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr);
6043 NLA_PUT_U32(msg, NL80211_ATTR_KEY_TYPE, key_type);
6044 NLA_PUT_U8(msg, NL80211_ATTR_KEY_IDX, key_id);
6045 if (tsc)
6046 NLA_PUT(msg, NL80211_ATTR_KEY_SEQ, 6, tsc);
6047
6048 if (genlmsg_end(msg, hdr) < 0) {
6049 nlmsg_free(msg);
6050 return;
6051 }
6052
Johannes Berg463d0182009-07-14 00:33:35 +02006053 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
6054 nl80211_mlme_mcgrp.id, gfp);
Jouni Malinena3b8b052009-03-27 21:59:49 +02006055 return;
6056
6057 nla_put_failure:
6058 genlmsg_cancel(msg, hdr);
6059 nlmsg_free(msg);
6060}
6061
Luis R. Rodriguez6bad8762009-04-02 14:08:09 -04006062void nl80211_send_beacon_hint_event(struct wiphy *wiphy,
6063 struct ieee80211_channel *channel_before,
6064 struct ieee80211_channel *channel_after)
6065{
6066 struct sk_buff *msg;
6067 void *hdr;
6068 struct nlattr *nl_freq;
6069
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07006070 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC);
Luis R. Rodriguez6bad8762009-04-02 14:08:09 -04006071 if (!msg)
6072 return;
6073
6074 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_REG_BEACON_HINT);
6075 if (!hdr) {
6076 nlmsg_free(msg);
6077 return;
6078 }
6079
6080 /*
6081 * Since we are applying the beacon hint to a wiphy we know its
6082 * wiphy_idx is valid
6083 */
6084 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, get_wiphy_idx(wiphy));
6085
6086 /* Before */
6087 nl_freq = nla_nest_start(msg, NL80211_ATTR_FREQ_BEFORE);
6088 if (!nl_freq)
6089 goto nla_put_failure;
6090 if (nl80211_msg_put_channel(msg, channel_before))
6091 goto nla_put_failure;
6092 nla_nest_end(msg, nl_freq);
6093
6094 /* After */
6095 nl_freq = nla_nest_start(msg, NL80211_ATTR_FREQ_AFTER);
6096 if (!nl_freq)
6097 goto nla_put_failure;
6098 if (nl80211_msg_put_channel(msg, channel_after))
6099 goto nla_put_failure;
6100 nla_nest_end(msg, nl_freq);
6101
6102 if (genlmsg_end(msg, hdr) < 0) {
6103 nlmsg_free(msg);
6104 return;
6105 }
6106
Johannes Berg463d0182009-07-14 00:33:35 +02006107 rcu_read_lock();
6108 genlmsg_multicast_allns(msg, 0, nl80211_regulatory_mcgrp.id,
6109 GFP_ATOMIC);
6110 rcu_read_unlock();
Luis R. Rodriguez6bad8762009-04-02 14:08:09 -04006111
6112 return;
6113
6114nla_put_failure:
6115 genlmsg_cancel(msg, hdr);
6116 nlmsg_free(msg);
6117}
6118
Jouni Malinen9588bbd2009-12-23 13:15:41 +01006119static void nl80211_send_remain_on_chan_event(
6120 int cmd, struct cfg80211_registered_device *rdev,
6121 struct net_device *netdev, u64 cookie,
6122 struct ieee80211_channel *chan,
6123 enum nl80211_channel_type channel_type,
6124 unsigned int duration, gfp_t gfp)
6125{
6126 struct sk_buff *msg;
6127 void *hdr;
6128
6129 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
6130 if (!msg)
6131 return;
6132
6133 hdr = nl80211hdr_put(msg, 0, 0, 0, cmd);
6134 if (!hdr) {
6135 nlmsg_free(msg);
6136 return;
6137 }
6138
6139 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
6140 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
6141 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, chan->center_freq);
6142 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, channel_type);
6143 NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, cookie);
6144
6145 if (cmd == NL80211_CMD_REMAIN_ON_CHANNEL)
6146 NLA_PUT_U32(msg, NL80211_ATTR_DURATION, duration);
6147
6148 if (genlmsg_end(msg, hdr) < 0) {
6149 nlmsg_free(msg);
6150 return;
6151 }
6152
6153 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
6154 nl80211_mlme_mcgrp.id, gfp);
6155 return;
6156
6157 nla_put_failure:
6158 genlmsg_cancel(msg, hdr);
6159 nlmsg_free(msg);
6160}
6161
6162void nl80211_send_remain_on_channel(struct cfg80211_registered_device *rdev,
6163 struct net_device *netdev, u64 cookie,
6164 struct ieee80211_channel *chan,
6165 enum nl80211_channel_type channel_type,
6166 unsigned int duration, gfp_t gfp)
6167{
6168 nl80211_send_remain_on_chan_event(NL80211_CMD_REMAIN_ON_CHANNEL,
6169 rdev, netdev, cookie, chan,
6170 channel_type, duration, gfp);
6171}
6172
6173void nl80211_send_remain_on_channel_cancel(
6174 struct cfg80211_registered_device *rdev, struct net_device *netdev,
6175 u64 cookie, struct ieee80211_channel *chan,
6176 enum nl80211_channel_type channel_type, gfp_t gfp)
6177{
6178 nl80211_send_remain_on_chan_event(NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL,
6179 rdev, netdev, cookie, chan,
6180 channel_type, 0, gfp);
6181}
6182
Johannes Berg98b62182009-12-23 13:15:44 +01006183void nl80211_send_sta_event(struct cfg80211_registered_device *rdev,
6184 struct net_device *dev, const u8 *mac_addr,
6185 struct station_info *sinfo, gfp_t gfp)
6186{
6187 struct sk_buff *msg;
6188
6189 msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
6190 if (!msg)
6191 return;
6192
6193 if (nl80211_send_station(msg, 0, 0, 0, dev, mac_addr, sinfo) < 0) {
6194 nlmsg_free(msg);
6195 return;
6196 }
6197
6198 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
6199 nl80211_mlme_mcgrp.id, gfp);
6200}
6201
Johannes Berg2e161f72010-08-12 15:38:38 +02006202int nl80211_send_mgmt(struct cfg80211_registered_device *rdev,
6203 struct net_device *netdev, u32 nlpid,
6204 int freq, const u8 *buf, size_t len, gfp_t gfp)
Jouni Malinen026331c2010-02-15 12:53:10 +02006205{
6206 struct sk_buff *msg;
6207 void *hdr;
6208 int err;
6209
6210 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
6211 if (!msg)
6212 return -ENOMEM;
6213
Johannes Berg2e161f72010-08-12 15:38:38 +02006214 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_FRAME);
Jouni Malinen026331c2010-02-15 12:53:10 +02006215 if (!hdr) {
6216 nlmsg_free(msg);
6217 return -ENOMEM;
6218 }
6219
6220 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
6221 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
6222 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq);
6223 NLA_PUT(msg, NL80211_ATTR_FRAME, len, buf);
6224
6225 err = genlmsg_end(msg, hdr);
6226 if (err < 0) {
6227 nlmsg_free(msg);
6228 return err;
6229 }
6230
6231 err = genlmsg_unicast(wiphy_net(&rdev->wiphy), msg, nlpid);
6232 if (err < 0)
6233 return err;
6234 return 0;
6235
6236 nla_put_failure:
6237 genlmsg_cancel(msg, hdr);
6238 nlmsg_free(msg);
6239 return -ENOBUFS;
6240}
6241
Johannes Berg2e161f72010-08-12 15:38:38 +02006242void nl80211_send_mgmt_tx_status(struct cfg80211_registered_device *rdev,
6243 struct net_device *netdev, u64 cookie,
6244 const u8 *buf, size_t len, bool ack,
6245 gfp_t gfp)
Jouni Malinen026331c2010-02-15 12:53:10 +02006246{
6247 struct sk_buff *msg;
6248 void *hdr;
6249
6250 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
6251 if (!msg)
6252 return;
6253
Johannes Berg2e161f72010-08-12 15:38:38 +02006254 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_FRAME_TX_STATUS);
Jouni Malinen026331c2010-02-15 12:53:10 +02006255 if (!hdr) {
6256 nlmsg_free(msg);
6257 return;
6258 }
6259
6260 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
6261 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
6262 NLA_PUT(msg, NL80211_ATTR_FRAME, len, buf);
6263 NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, cookie);
6264 if (ack)
6265 NLA_PUT_FLAG(msg, NL80211_ATTR_ACK);
6266
6267 if (genlmsg_end(msg, hdr) < 0) {
6268 nlmsg_free(msg);
6269 return;
6270 }
6271
6272 genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, gfp);
6273 return;
6274
6275 nla_put_failure:
6276 genlmsg_cancel(msg, hdr);
6277 nlmsg_free(msg);
6278}
6279
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02006280void
6281nl80211_send_cqm_rssi_notify(struct cfg80211_registered_device *rdev,
6282 struct net_device *netdev,
6283 enum nl80211_cqm_rssi_threshold_event rssi_event,
6284 gfp_t gfp)
6285{
6286 struct sk_buff *msg;
6287 struct nlattr *pinfoattr;
6288 void *hdr;
6289
6290 msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
6291 if (!msg)
6292 return;
6293
6294 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_NOTIFY_CQM);
6295 if (!hdr) {
6296 nlmsg_free(msg);
6297 return;
6298 }
6299
6300 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
6301 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
6302
6303 pinfoattr = nla_nest_start(msg, NL80211_ATTR_CQM);
6304 if (!pinfoattr)
6305 goto nla_put_failure;
6306
6307 NLA_PUT_U32(msg, NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT,
6308 rssi_event);
6309
6310 nla_nest_end(msg, pinfoattr);
6311
6312 if (genlmsg_end(msg, hdr) < 0) {
6313 nlmsg_free(msg);
6314 return;
6315 }
6316
6317 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
6318 nl80211_mlme_mcgrp.id, gfp);
6319 return;
6320
6321 nla_put_failure:
6322 genlmsg_cancel(msg, hdr);
6323 nlmsg_free(msg);
6324}
6325
Jouni Malinen026331c2010-02-15 12:53:10 +02006326static int nl80211_netlink_notify(struct notifier_block * nb,
6327 unsigned long state,
6328 void *_notify)
6329{
6330 struct netlink_notify *notify = _notify;
6331 struct cfg80211_registered_device *rdev;
6332 struct wireless_dev *wdev;
6333
6334 if (state != NETLINK_URELEASE)
6335 return NOTIFY_DONE;
6336
6337 rcu_read_lock();
6338
6339 list_for_each_entry_rcu(rdev, &cfg80211_rdev_list, list)
6340 list_for_each_entry_rcu(wdev, &rdev->netdev_list, list)
Johannes Berg2e161f72010-08-12 15:38:38 +02006341 cfg80211_mlme_unregister_socket(wdev, notify->pid);
Jouni Malinen026331c2010-02-15 12:53:10 +02006342
6343 rcu_read_unlock();
6344
6345 return NOTIFY_DONE;
6346}
6347
6348static struct notifier_block nl80211_netlink_notifier = {
6349 .notifier_call = nl80211_netlink_notify,
6350};
6351
Johannes Berg55682962007-09-20 13:09:35 -04006352/* initialisation/exit functions */
6353
6354int nl80211_init(void)
6355{
Michał Mirosław0d63cbb2009-05-21 10:34:06 +00006356 int err;
Johannes Berg55682962007-09-20 13:09:35 -04006357
Michał Mirosław0d63cbb2009-05-21 10:34:06 +00006358 err = genl_register_family_with_ops(&nl80211_fam,
6359 nl80211_ops, ARRAY_SIZE(nl80211_ops));
Johannes Berg55682962007-09-20 13:09:35 -04006360 if (err)
6361 return err;
6362
Johannes Berg55682962007-09-20 13:09:35 -04006363 err = genl_register_mc_group(&nl80211_fam, &nl80211_config_mcgrp);
6364 if (err)
6365 goto err_out;
6366
Johannes Berg2a519312009-02-10 21:25:55 +01006367 err = genl_register_mc_group(&nl80211_fam, &nl80211_scan_mcgrp);
6368 if (err)
6369 goto err_out;
6370
Luis R. Rodriguez73d54c92009-03-09 22:07:42 -04006371 err = genl_register_mc_group(&nl80211_fam, &nl80211_regulatory_mcgrp);
6372 if (err)
6373 goto err_out;
6374
Jouni Malinen6039f6d2009-03-19 13:39:21 +02006375 err = genl_register_mc_group(&nl80211_fam, &nl80211_mlme_mcgrp);
6376 if (err)
6377 goto err_out;
6378
Johannes Bergaff89a92009-07-01 21:26:51 +02006379#ifdef CONFIG_NL80211_TESTMODE
6380 err = genl_register_mc_group(&nl80211_fam, &nl80211_testmode_mcgrp);
6381 if (err)
6382 goto err_out;
6383#endif
6384
Jouni Malinen026331c2010-02-15 12:53:10 +02006385 err = netlink_register_notifier(&nl80211_netlink_notifier);
6386 if (err)
6387 goto err_out;
6388
Johannes Berg55682962007-09-20 13:09:35 -04006389 return 0;
6390 err_out:
6391 genl_unregister_family(&nl80211_fam);
6392 return err;
6393}
6394
6395void nl80211_exit(void)
6396{
Jouni Malinen026331c2010-02-15 12:53:10 +02006397 netlink_unregister_notifier(&nl80211_netlink_notifier);
Johannes Berg55682962007-09-20 13:09:35 -04006398 genl_unregister_family(&nl80211_fam);
6399}