nl80211.c 106 KB
Newer Older
1
2
3
/*
 * This is the new netlink-based wireless configuration interface.
 *
4
 * Copyright 2006-2009	Johannes Berg <johannes@sipsolutions.net>
5
6
7
8
9
10
11
12
13
14
15
 */

#include <linux/if.h>
#include <linux/module.h>
#include <linux/err.h>
#include <linux/list.h>
#include <linux/if_ether.h>
#include <linux/ieee80211.h>
#include <linux/nl80211.h>
#include <linux/rtnetlink.h>
#include <linux/netlink.h>
16
#include <linux/etherdevice.h>
17
18
19
20
#include <net/genetlink.h>
#include <net/cfg80211.h>
#include "core.h"
#include "nl80211.h"
21
#include "reg.h"
22
23
24
25
26
27
28
29
30
31
32

/* the netlink family */
static struct genl_family nl80211_fam = {
	.id = GENL_ID_GENERATE,	/* don't bother with a hardcoded ID */
	.name = "nl80211",	/* have users key off the name instead */
	.hdrsize = 0,		/* no private header */
	.version = 1,		/* no particular meaning now */
	.maxattr = NL80211_ATTR_MAX,
};

/* internal helper: get drv and dev */
Johannes Berg's avatar
Johannes Berg committed
33
static int get_drv_dev_by_info_ifindex(struct nlattr **attrs,
34
35
36
37
38
				       struct cfg80211_registered_device **drv,
				       struct net_device **dev)
{
	int ifindex;

Johannes Berg's avatar
Johannes Berg committed
39
	if (!attrs[NL80211_ATTR_IFINDEX])
40
41
		return -EINVAL;

Johannes Berg's avatar
Johannes Berg committed
42
	ifindex = nla_get_u32(attrs[NL80211_ATTR_IFINDEX]);
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
	*dev = dev_get_by_index(&init_net, ifindex);
	if (!*dev)
		return -ENODEV;

	*drv = cfg80211_get_dev_from_ifindex(ifindex);
	if (IS_ERR(*drv)) {
		dev_put(*dev);
		return PTR_ERR(*drv);
	}

	return 0;
}

/* policy for the attributes */
static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = {
	[NL80211_ATTR_WIPHY] = { .type = NLA_U32 },
	[NL80211_ATTR_WIPHY_NAME] = { .type = NLA_NUL_STRING,
60
				      .len = 20-1 },
61
	[NL80211_ATTR_WIPHY_TXQ_PARAMS] = { .type = NLA_NESTED },
62
	[NL80211_ATTR_WIPHY_FREQ] = { .type = NLA_U32 },
Sujith's avatar
Sujith committed
63
	[NL80211_ATTR_WIPHY_CHANNEL_TYPE] = { .type = NLA_U32 },
64
65
66
67
	[NL80211_ATTR_WIPHY_RETRY_SHORT] = { .type = NLA_U8 },
	[NL80211_ATTR_WIPHY_RETRY_LONG] = { .type = NLA_U8 },
	[NL80211_ATTR_WIPHY_FRAG_THRESHOLD] = { .type = NLA_U32 },
	[NL80211_ATTR_WIPHY_RTS_THRESHOLD] = { .type = NLA_U32 },
68
69
70
71

	[NL80211_ATTR_IFTYPE] = { .type = NLA_U32 },
	[NL80211_ATTR_IFINDEX] = { .type = NLA_U32 },
	[NL80211_ATTR_IFNAME] = { .type = NLA_NUL_STRING, .len = IFNAMSIZ-1 },
72
73

	[NL80211_ATTR_MAC] = { .type = NLA_BINARY, .len = ETH_ALEN },
74
	[NL80211_ATTR_PREV_BSSID] = { .type = NLA_BINARY, .len = ETH_ALEN },
75
76
77
78
79
80

	[NL80211_ATTR_KEY_DATA] = { .type = NLA_BINARY,
				    .len = WLAN_MAX_KEY_LEN },
	[NL80211_ATTR_KEY_IDX] = { .type = NLA_U8 },
	[NL80211_ATTR_KEY_CIPHER] = { .type = NLA_U32 },
	[NL80211_ATTR_KEY_DEFAULT] = { .type = NLA_FLAG },
81
	[NL80211_ATTR_KEY_SEQ] = { .type = NLA_BINARY, .len = 8 },
82
83
84
85
86
87
88

	[NL80211_ATTR_BEACON_INTERVAL] = { .type = NLA_U32 },
	[NL80211_ATTR_DTIM_PERIOD] = { .type = NLA_U32 },
	[NL80211_ATTR_BEACON_HEAD] = { .type = NLA_BINARY,
				       .len = IEEE80211_MAX_DATA_LEN },
	[NL80211_ATTR_BEACON_TAIL] = { .type = NLA_BINARY,
				       .len = IEEE80211_MAX_DATA_LEN },
89
90
91
92
93
	[NL80211_ATTR_STA_AID] = { .type = NLA_U16 },
	[NL80211_ATTR_STA_FLAGS] = { .type = NLA_NESTED },
	[NL80211_ATTR_STA_LISTEN_INTERVAL] = { .type = NLA_U16 },
	[NL80211_ATTR_STA_SUPPORTED_RATES] = { .type = NLA_BINARY,
					       .len = NL80211_MAX_SUPP_RATES },
94
	[NL80211_ATTR_STA_PLINK_ACTION] = { .type = NLA_U8 },
95
	[NL80211_ATTR_STA_VLAN] = { .type = NLA_U32 },
Johannes Berg's avatar
Johannes Berg committed
96
	[NL80211_ATTR_MNTR_FLAGS] = { /* NLA_NESTED can't be empty */ },
97
98
99
	[NL80211_ATTR_MESH_ID] = { .type = NLA_BINARY,
				.len = IEEE80211_MAX_MESH_ID_LEN },
	[NL80211_ATTR_MPATH_NEXT_HOP] = { .type = NLA_U32 },
100

101
102
103
	[NL80211_ATTR_REG_ALPHA2] = { .type = NLA_STRING, .len = 2 },
	[NL80211_ATTR_REG_RULES] = { .type = NLA_NESTED },

104
105
106
	[NL80211_ATTR_BSS_CTS_PROT] = { .type = NLA_U8 },
	[NL80211_ATTR_BSS_SHORT_PREAMBLE] = { .type = NLA_U8 },
	[NL80211_ATTR_BSS_SHORT_SLOT_TIME] = { .type = NLA_U8 },
107
108
	[NL80211_ATTR_BSS_BASIC_RATES] = { .type = NLA_BINARY,
					   .len = NL80211_MAX_SUPP_RATES },
109

110
111
	[NL80211_ATTR_MESH_PARAMS] = { .type = NLA_NESTED },

112
113
	[NL80211_ATTR_HT_CAPABILITY] = { .type = NLA_BINARY,
					 .len = NL80211_HT_CAPABILITY_LEN },
114
115
116
117

	[NL80211_ATTR_MGMT_SUBTYPE] = { .type = NLA_U8 },
	[NL80211_ATTR_IE] = { .type = NLA_BINARY,
			      .len = IEEE80211_MAX_DATA_LEN },
118
119
	[NL80211_ATTR_SCAN_FREQUENCIES] = { .type = NLA_NESTED },
	[NL80211_ATTR_SCAN_SSIDS] = { .type = NLA_NESTED },
120
121
122
123
124

	[NL80211_ATTR_SSID] = { .type = NLA_BINARY,
				.len = IEEE80211_MAX_SSID_LEN },
	[NL80211_ATTR_AUTH_TYPE] = { .type = NLA_U32 },
	[NL80211_ATTR_REASON_CODE] = { .type = NLA_U16 },
Johannes Berg's avatar
Johannes Berg committed
125
	[NL80211_ATTR_FREQ_FIXED] = { .type = NLA_FLAG },
126
	[NL80211_ATTR_TIMED_OUT] = { .type = NLA_FLAG },
127
	[NL80211_ATTR_USE_MFP] = { .type = NLA_U32 },
128
129
130
	[NL80211_ATTR_STA_FLAGS2] = {
		.len = sizeof(struct nl80211_sta_flag_update),
	},
131
	[NL80211_ATTR_CONTROL_PORT] = { .type = NLA_FLAG },
132
133
134
	[NL80211_ATTR_PRIVACY] = { .type = NLA_FLAG },
	[NL80211_ATTR_CIPHER_SUITE_GROUP] = { .type = NLA_U32 },
	[NL80211_ATTR_WPA_VERSIONS] = { .type = NLA_U32 },
135
136
};

137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
/* IE validation */
static bool is_valid_ie_attr(const struct nlattr *attr)
{
	const u8 *pos;
	int len;

	if (!attr)
		return true;

	pos = nla_data(attr);
	len = nla_len(attr);

	while (len) {
		u8 elemlen;

		if (len < 2)
			return false;
		len -= 2;

		elemlen = pos[1];
		if (elemlen > len)
			return false;

		len -= elemlen;
		pos += 2 + elemlen;
	}

	return true;
}

167
168
169
170
171
172
173
174
/* message building helper */
static inline void *nl80211hdr_put(struct sk_buff *skb, u32 pid, u32 seq,
				   int flags, u8 cmd)
{
	/* since there is no private header just add the generic one */
	return genlmsg_put(skb, pid, seq, &nl80211_fam, flags, cmd);
}

175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
static int nl80211_msg_put_channel(struct sk_buff *msg,
				   struct ieee80211_channel *chan)
{
	NLA_PUT_U32(msg, NL80211_FREQUENCY_ATTR_FREQ,
		    chan->center_freq);

	if (chan->flags & IEEE80211_CHAN_DISABLED)
		NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_DISABLED);
	if (chan->flags & IEEE80211_CHAN_PASSIVE_SCAN)
		NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_PASSIVE_SCAN);
	if (chan->flags & IEEE80211_CHAN_NO_IBSS)
		NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_NO_IBSS);
	if (chan->flags & IEEE80211_CHAN_RADAR)
		NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_RADAR);

	NLA_PUT_U32(msg, NL80211_FREQUENCY_ATTR_MAX_TX_POWER,
		    DBM_TO_MBM(chan->max_power));

	return 0;

 nla_put_failure:
	return -ENOBUFS;
}

199
200
201
202
203
204
/* netlink command implementations */

static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags,
			      struct cfg80211_registered_device *dev)
{
	void *hdr;
205
206
207
	struct nlattr *nl_bands, *nl_band;
	struct nlattr *nl_freqs, *nl_freq;
	struct nlattr *nl_rates, *nl_rate;
208
	struct nlattr *nl_modes;
209
	struct nlattr *nl_cmds;
210
211
212
213
	enum ieee80211_band band;
	struct ieee80211_channel *chan;
	struct ieee80211_rate *rate;
	int i;
214
	u16 ifmodes = dev->wiphy.interface_modes;
215
216
217
218
219

	hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_WIPHY);
	if (!hdr)
		return -1;

220
	NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, dev->wiphy_idx);
221
	NLA_PUT_STRING(msg, NL80211_ATTR_WIPHY_NAME, wiphy_name(&dev->wiphy));
222
223
224
225
226
227
228
229
230
231

	NLA_PUT_U8(msg, NL80211_ATTR_WIPHY_RETRY_SHORT,
		   dev->wiphy.retry_short);
	NLA_PUT_U8(msg, NL80211_ATTR_WIPHY_RETRY_LONG,
		   dev->wiphy.retry_long);
	NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FRAG_THRESHOLD,
		    dev->wiphy.frag_threshold);
	NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_RTS_THRESHOLD,
		    dev->wiphy.rts_threshold);

232
233
	NLA_PUT_U8(msg, NL80211_ATTR_MAX_NUM_SCAN_SSIDS,
		   dev->wiphy.max_scan_ssids);
234
235
	NLA_PUT_U16(msg, NL80211_ATTR_MAX_SCAN_IE_LEN,
		    dev->wiphy.max_scan_ie_len);
236

237
238
239
240
	NLA_PUT(msg, NL80211_ATTR_CIPHER_SUITES,
		sizeof(u32) * dev->wiphy.n_cipher_suites,
		dev->wiphy.cipher_suites);

241
242
243
244
245
246
247
248
249
250
251
252
253
254
	nl_modes = nla_nest_start(msg, NL80211_ATTR_SUPPORTED_IFTYPES);
	if (!nl_modes)
		goto nla_put_failure;

	i = 0;
	while (ifmodes) {
		if (ifmodes & 1)
			NLA_PUT_FLAG(msg, i);
		ifmodes >>= 1;
		i++;
	}

	nla_nest_end(msg, nl_modes);

255
256
257
258
259
260
261
262
263
264
265
266
	nl_bands = nla_nest_start(msg, NL80211_ATTR_WIPHY_BANDS);
	if (!nl_bands)
		goto nla_put_failure;

	for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
		if (!dev->wiphy.bands[band])
			continue;

		nl_band = nla_nest_start(msg, band);
		if (!nl_band)
			goto nla_put_failure;

267
268
269
270
271
272
273
274
275
276
277
278
279
		/* add HT info */
		if (dev->wiphy.bands[band]->ht_cap.ht_supported) {
			NLA_PUT(msg, NL80211_BAND_ATTR_HT_MCS_SET,
				sizeof(dev->wiphy.bands[band]->ht_cap.mcs),
				&dev->wiphy.bands[band]->ht_cap.mcs);
			NLA_PUT_U16(msg, NL80211_BAND_ATTR_HT_CAPA,
				dev->wiphy.bands[band]->ht_cap.cap);
			NLA_PUT_U8(msg, NL80211_BAND_ATTR_HT_AMPDU_FACTOR,
				dev->wiphy.bands[band]->ht_cap.ampdu_factor);
			NLA_PUT_U8(msg, NL80211_BAND_ATTR_HT_AMPDU_DENSITY,
				dev->wiphy.bands[band]->ht_cap.ampdu_density);
		}

280
281
282
283
284
285
286
287
288
289
290
		/* add frequencies */
		nl_freqs = nla_nest_start(msg, NL80211_BAND_ATTR_FREQS);
		if (!nl_freqs)
			goto nla_put_failure;

		for (i = 0; i < dev->wiphy.bands[band]->n_channels; i++) {
			nl_freq = nla_nest_start(msg, i);
			if (!nl_freq)
				goto nla_put_failure;

			chan = &dev->wiphy.bands[band]->channels[i];
291
292
293

			if (nl80211_msg_put_channel(msg, chan))
				goto nla_put_failure;
294

295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
			nla_nest_end(msg, nl_freq);
		}

		nla_nest_end(msg, nl_freqs);

		/* add bitrates */
		nl_rates = nla_nest_start(msg, NL80211_BAND_ATTR_RATES);
		if (!nl_rates)
			goto nla_put_failure;

		for (i = 0; i < dev->wiphy.bands[band]->n_bitrates; i++) {
			nl_rate = nla_nest_start(msg, i);
			if (!nl_rate)
				goto nla_put_failure;

			rate = &dev->wiphy.bands[band]->bitrates[i];
			NLA_PUT_U32(msg, NL80211_BITRATE_ATTR_RATE,
				    rate->bitrate);
			if (rate->flags & IEEE80211_RATE_SHORT_PREAMBLE)
				NLA_PUT_FLAG(msg,
					NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE);

			nla_nest_end(msg, nl_rate);
		}

		nla_nest_end(msg, nl_rates);

		nla_nest_end(msg, nl_band);
	}
	nla_nest_end(msg, nl_bands);

326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
	nl_cmds = nla_nest_start(msg, NL80211_ATTR_SUPPORTED_COMMANDS);
	if (!nl_cmds)
		goto nla_put_failure;

	i = 0;
#define CMD(op, n)						\
	 do {							\
		if (dev->ops->op) {				\
			i++;					\
			NLA_PUT_U32(msg, i, NL80211_CMD_ ## n);	\
		}						\
	} while (0)

	CMD(add_virtual_intf, NEW_INTERFACE);
	CMD(change_virtual_intf, SET_INTERFACE);
	CMD(add_key, NEW_KEY);
	CMD(add_beacon, NEW_BEACON);
	CMD(add_station, NEW_STATION);
	CMD(add_mpath, NEW_MPATH);
	CMD(set_mesh_params, SET_MESH_PARAMS);
	CMD(change_bss, SET_BSS);
347
348
349
350
	CMD(auth, AUTHENTICATE);
	CMD(assoc, ASSOCIATE);
	CMD(deauth, DEAUTHENTICATE);
	CMD(disassoc, DISASSOCIATE);
Johannes Berg's avatar
Johannes Berg committed
351
	CMD(join_ibss, JOIN_IBSS);
352
353

#undef CMD
354

355
	if (dev->ops->connect || dev->ops->auth) {
356
357
358
359
		i++;
		NLA_PUT_U32(msg, i, NL80211_CMD_CONNECT);
	}

360
	if (dev->ops->disconnect || dev->ops->deauth) {
361
362
363
364
		i++;
		NLA_PUT_U32(msg, i, NL80211_CMD_DISCONNECT);
	}

365
366
	nla_nest_end(msg, nl_cmds);

367
368
369
	return genlmsg_end(msg, hdr);

 nla_put_failure:
370
371
	genlmsg_cancel(msg, hdr);
	return -EMSGSIZE;
372
373
374
375
376
377
378
379
}

static int nl80211_dump_wiphy(struct sk_buff *skb, struct netlink_callback *cb)
{
	int idx = 0;
	int start = cb->args[0];
	struct cfg80211_registered_device *dev;

380
	mutex_lock(&cfg80211_mutex);
381
	list_for_each_entry(dev, &cfg80211_drv_list, list) {
382
		if (++idx <= start)
383
384
385
			continue;
		if (nl80211_send_wiphy(skb, NETLINK_CB(cb->skb).pid,
				       cb->nlh->nlmsg_seq, NLM_F_MULTI,
386
387
				       dev) < 0) {
			idx--;
388
			break;
389
		}
390
	}
391
	mutex_unlock(&cfg80211_mutex);
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406

	cb->args[0] = idx;

	return skb->len;
}

static int nl80211_get_wiphy(struct sk_buff *skb, struct genl_info *info)
{
	struct sk_buff *msg;
	struct cfg80211_registered_device *dev;

	dev = cfg80211_get_dev_from_info(info);
	if (IS_ERR(dev))
		return PTR_ERR(dev);

407
	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
408
409
410
411
412
413
	if (!msg)
		goto out_err;

	if (nl80211_send_wiphy(msg, info->snd_pid, info->snd_seq, 0, dev) < 0)
		goto out_free;

414
	cfg80211_unlock_rdev(dev);
415
416
417
418
419
420

	return genlmsg_unicast(msg, info->snd_pid);

 out_free:
	nlmsg_free(msg);
 out_err:
421
	cfg80211_unlock_rdev(dev);
422
423
424
	return -ENOBUFS;
}

425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
static const struct nla_policy txq_params_policy[NL80211_TXQ_ATTR_MAX + 1] = {
	[NL80211_TXQ_ATTR_QUEUE]		= { .type = NLA_U8 },
	[NL80211_TXQ_ATTR_TXOP]			= { .type = NLA_U16 },
	[NL80211_TXQ_ATTR_CWMIN]		= { .type = NLA_U16 },
	[NL80211_TXQ_ATTR_CWMAX]		= { .type = NLA_U16 },
	[NL80211_TXQ_ATTR_AIFS]			= { .type = NLA_U8 },
};

static int parse_txq_params(struct nlattr *tb[],
			    struct ieee80211_txq_params *txq_params)
{
	if (!tb[NL80211_TXQ_ATTR_QUEUE] || !tb[NL80211_TXQ_ATTR_TXOP] ||
	    !tb[NL80211_TXQ_ATTR_CWMIN] || !tb[NL80211_TXQ_ATTR_CWMAX] ||
	    !tb[NL80211_TXQ_ATTR_AIFS])
		return -EINVAL;

	txq_params->queue = nla_get_u8(tb[NL80211_TXQ_ATTR_QUEUE]);
	txq_params->txop = nla_get_u16(tb[NL80211_TXQ_ATTR_TXOP]);
	txq_params->cwmin = nla_get_u16(tb[NL80211_TXQ_ATTR_CWMIN]);
	txq_params->cwmax = nla_get_u16(tb[NL80211_TXQ_ATTR_CWMAX]);
	txq_params->aifs = nla_get_u8(tb[NL80211_TXQ_ATTR_AIFS]);

	return 0;
}

450
451
452
static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
{
	struct cfg80211_registered_device *rdev;
453
454
	int result = 0, rem_txq_params = 0;
	struct nlattr *nl_txq_params;
455
456
457
	u32 changed;
	u8 retry_short = 0, retry_long = 0;
	u32 frag_threshold = 0, rts_threshold = 0;
458

459
	rtnl_lock();
460

461
462
463
464
	mutex_lock(&cfg80211_mutex);

	rdev = __cfg80211_drv_from_info(info);
	if (IS_ERR(rdev)) {
465
		mutex_unlock(&cfg80211_mutex);
466
467
468
469
470
471
472
		result = PTR_ERR(rdev);
		goto unlock;
	}

	mutex_lock(&rdev->mtx);

	if (info->attrs[NL80211_ATTR_WIPHY_NAME])
473
474
		result = cfg80211_dev_rename(
			rdev, nla_data(info->attrs[NL80211_ATTR_WIPHY_NAME]));
475
476
477
478
479

	mutex_unlock(&cfg80211_mutex);

	if (result)
		goto bad_res;
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506

	if (info->attrs[NL80211_ATTR_WIPHY_TXQ_PARAMS]) {
		struct ieee80211_txq_params txq_params;
		struct nlattr *tb[NL80211_TXQ_ATTR_MAX + 1];

		if (!rdev->ops->set_txq_params) {
			result = -EOPNOTSUPP;
			goto bad_res;
		}

		nla_for_each_nested(nl_txq_params,
				    info->attrs[NL80211_ATTR_WIPHY_TXQ_PARAMS],
				    rem_txq_params) {
			nla_parse(tb, NL80211_TXQ_ATTR_MAX,
				  nla_data(nl_txq_params),
				  nla_len(nl_txq_params),
				  txq_params_policy);
			result = parse_txq_params(tb, &txq_params);
			if (result)
				goto bad_res;

			result = rdev->ops->set_txq_params(&rdev->wiphy,
							   &txq_params);
			if (result)
				goto bad_res;
		}
	}
507

508
	if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
Sujith's avatar
Sujith committed
509
		enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT;
510
		struct ieee80211_channel *chan;
511
		struct ieee80211_sta_ht_cap *ht_cap;
512
		u32 freq;
513
514
515
516
517
518

		if (!rdev->ops->set_channel) {
			result = -EOPNOTSUPP;
			goto bad_res;
		}

519
520
		result = -EINVAL;

Sujith's avatar
Sujith committed
521
522
523
524
525
526
527
		if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) {
			channel_type = nla_get_u32(info->attrs[
					   NL80211_ATTR_WIPHY_CHANNEL_TYPE]);
			if (channel_type != NL80211_CHAN_NO_HT &&
			    channel_type != NL80211_CHAN_HT20 &&
			    channel_type != NL80211_CHAN_HT40PLUS &&
			    channel_type != NL80211_CHAN_HT40MINUS)
528
529
530
531
532
				goto bad_res;
		}

		freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]);
		chan = ieee80211_get_channel(&rdev->wiphy, freq);
533
534
535

		/* Primary channel not allowed */
		if (!chan || chan->flags & IEEE80211_CHAN_DISABLED)
536
			goto bad_res;
537

538
539
		if (channel_type == NL80211_CHAN_HT40MINUS &&
		    (chan->flags & IEEE80211_CHAN_NO_HT40MINUS))
540
			goto bad_res;
541
542
		else if (channel_type == NL80211_CHAN_HT40PLUS &&
			 (chan->flags & IEEE80211_CHAN_NO_HT40PLUS))
543
544
			goto bad_res;

545
546
547
548
549
		/*
		 * At this point we know if that if HT40 was requested
		 * we are allowed to use it and the extension channel
		 * exists.
		 */
550

551
		ht_cap = &rdev->wiphy.bands[chan->band]->ht_cap;
552

553
554
555
556
		/* no HT capabilities or intolerant */
		if (channel_type != NL80211_CHAN_NO_HT) {
			if (!ht_cap->ht_supported)
				goto bad_res;
557
558
559
			if (!(ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) ||
			    (ht_cap->cap & IEEE80211_HT_CAP_40MHZ_INTOLERANT))
				goto bad_res;
560
561
562
		}

		result = rdev->ops->set_channel(&rdev->wiphy, chan,
Sujith's avatar
Sujith committed
563
						channel_type);
564
565
566
567
		if (result)
			goto bad_res;
	}

568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
	changed = 0;

	if (info->attrs[NL80211_ATTR_WIPHY_RETRY_SHORT]) {
		retry_short = nla_get_u8(
			info->attrs[NL80211_ATTR_WIPHY_RETRY_SHORT]);
		if (retry_short == 0) {
			result = -EINVAL;
			goto bad_res;
		}
		changed |= WIPHY_PARAM_RETRY_SHORT;
	}

	if (info->attrs[NL80211_ATTR_WIPHY_RETRY_LONG]) {
		retry_long = nla_get_u8(
			info->attrs[NL80211_ATTR_WIPHY_RETRY_LONG]);
		if (retry_long == 0) {
			result = -EINVAL;
			goto bad_res;
		}
		changed |= WIPHY_PARAM_RETRY_LONG;
	}

	if (info->attrs[NL80211_ATTR_WIPHY_FRAG_THRESHOLD]) {
		frag_threshold = nla_get_u32(
			info->attrs[NL80211_ATTR_WIPHY_FRAG_THRESHOLD]);
		if (frag_threshold < 256) {
			result = -EINVAL;
			goto bad_res;
		}
		if (frag_threshold != (u32) -1) {
			/*
			 * Fragments (apart from the last one) are required to
			 * have even length. Make the fragmentation code
			 * simpler by stripping LSB should someone try to use
			 * odd threshold value.
			 */
			frag_threshold &= ~0x1;
		}
		changed |= WIPHY_PARAM_FRAG_THRESHOLD;
	}

	if (info->attrs[NL80211_ATTR_WIPHY_RTS_THRESHOLD]) {
		rts_threshold = nla_get_u32(
			info->attrs[NL80211_ATTR_WIPHY_RTS_THRESHOLD]);
		changed |= WIPHY_PARAM_RTS_THRESHOLD;
	}

	if (changed) {
		u8 old_retry_short, old_retry_long;
		u32 old_frag_threshold, old_rts_threshold;

		if (!rdev->ops->set_wiphy_params) {
			result = -EOPNOTSUPP;
			goto bad_res;
		}

		old_retry_short = rdev->wiphy.retry_short;
		old_retry_long = rdev->wiphy.retry_long;
		old_frag_threshold = rdev->wiphy.frag_threshold;
		old_rts_threshold = rdev->wiphy.rts_threshold;

		if (changed & WIPHY_PARAM_RETRY_SHORT)
			rdev->wiphy.retry_short = retry_short;
		if (changed & WIPHY_PARAM_RETRY_LONG)
			rdev->wiphy.retry_long = retry_long;
		if (changed & WIPHY_PARAM_FRAG_THRESHOLD)
			rdev->wiphy.frag_threshold = frag_threshold;
		if (changed & WIPHY_PARAM_RTS_THRESHOLD)
			rdev->wiphy.rts_threshold = rts_threshold;

		result = rdev->ops->set_wiphy_params(&rdev->wiphy, changed);
		if (result) {
			rdev->wiphy.retry_short = old_retry_short;
			rdev->wiphy.retry_long = old_retry_long;
			rdev->wiphy.frag_threshold = old_frag_threshold;
			rdev->wiphy.rts_threshold = old_rts_threshold;
		}
	}
646

647
 bad_res:
648
649
650
	mutex_unlock(&rdev->mtx);
 unlock:
	rtnl_unlock();
651
652
653
654
655
	return result;
}


static int nl80211_send_iface(struct sk_buff *msg, u32 pid, u32 seq, int flags,
656
			      struct cfg80211_registered_device *rdev,
657
658
659
660
661
662
663
664
665
			      struct net_device *dev)
{
	void *hdr;

	hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_INTERFACE);
	if (!hdr)
		return -1;

	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
666
	NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
667
	NLA_PUT_STRING(msg, NL80211_ATTR_IFNAME, dev->name);
Johannes Berg's avatar
Johannes Berg committed
668
	NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, dev->ieee80211_ptr->iftype);
669
670
671
	return genlmsg_end(msg, hdr);

 nla_put_failure:
672
673
	genlmsg_cancel(msg, hdr);
	return -EMSGSIZE;
674
675
676
677
678
679
680
681
682
683
684
}

static int nl80211_dump_interface(struct sk_buff *skb, struct netlink_callback *cb)
{
	int wp_idx = 0;
	int if_idx = 0;
	int wp_start = cb->args[0];
	int if_start = cb->args[1];
	struct cfg80211_registered_device *dev;
	struct wireless_dev *wdev;

685
	mutex_lock(&cfg80211_mutex);
686
	list_for_each_entry(dev, &cfg80211_drv_list, list) {
Johannes Berg's avatar
Johannes Berg committed
687
688
		if (wp_idx < wp_start) {
			wp_idx++;
689
			continue;
Johannes Berg's avatar
Johannes Berg committed
690
		}
691
692
693
694
		if_idx = 0;

		mutex_lock(&dev->devlist_mtx);
		list_for_each_entry(wdev, &dev->netdev_list, list) {
Johannes Berg's avatar
Johannes Berg committed
695
696
			if (if_idx < if_start) {
				if_idx++;
697
				continue;
Johannes Berg's avatar
Johannes Berg committed
698
			}
699
700
			if (nl80211_send_iface(skb, NETLINK_CB(cb->skb).pid,
					       cb->nlh->nlmsg_seq, NLM_F_MULTI,
701
					       dev, wdev->netdev) < 0) {
Johannes Berg's avatar
Johannes Berg committed
702
703
704
705
				mutex_unlock(&dev->devlist_mtx);
				goto out;
			}
			if_idx++;
706
707
		}
		mutex_unlock(&dev->devlist_mtx);
Johannes Berg's avatar
Johannes Berg committed
708
709

		wp_idx++;
710
	}
Johannes Berg's avatar
Johannes Berg committed
711
 out:
712
	mutex_unlock(&cfg80211_mutex);
713
714
715
716
717
718
719
720
721
722
723
724
725
726

	cb->args[0] = wp_idx;
	cb->args[1] = if_idx;

	return skb->len;
}

static int nl80211_get_interface(struct sk_buff *skb, struct genl_info *info)
{
	struct sk_buff *msg;
	struct cfg80211_registered_device *dev;
	struct net_device *netdev;
	int err;

Johannes Berg's avatar
Johannes Berg committed
727
	err = get_drv_dev_by_info_ifindex(info->attrs, &dev, &netdev);
728
729
730
	if (err)
		return err;

731
	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
732
733
734
	if (!msg)
		goto out_err;

735
736
	if (nl80211_send_iface(msg, info->snd_pid, info->snd_seq, 0,
			       dev, netdev) < 0)
737
738
739
		goto out_free;

	dev_put(netdev);
740
	cfg80211_unlock_rdev(dev);
741
742
743
744
745
746
747

	return genlmsg_unicast(msg, info->snd_pid);

 out_free:
	nlmsg_free(msg);
 out_err:
	dev_put(netdev);
748
	cfg80211_unlock_rdev(dev);
749
750
751
	return -ENOBUFS;
}

752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
static const struct nla_policy mntr_flags_policy[NL80211_MNTR_FLAG_MAX + 1] = {
	[NL80211_MNTR_FLAG_FCSFAIL] = { .type = NLA_FLAG },
	[NL80211_MNTR_FLAG_PLCPFAIL] = { .type = NLA_FLAG },
	[NL80211_MNTR_FLAG_CONTROL] = { .type = NLA_FLAG },
	[NL80211_MNTR_FLAG_OTHER_BSS] = { .type = NLA_FLAG },
	[NL80211_MNTR_FLAG_COOK_FRAMES] = { .type = NLA_FLAG },
};

static int parse_monitor_flags(struct nlattr *nla, u32 *mntrflags)
{
	struct nlattr *flags[NL80211_MNTR_FLAG_MAX + 1];
	int flag;

	*mntrflags = 0;

	if (!nla)
		return -EINVAL;

	if (nla_parse_nested(flags, NL80211_MNTR_FLAG_MAX,
			     nla, mntr_flags_policy))
		return -EINVAL;

	for (flag = 1; flag <= NL80211_MNTR_FLAG_MAX; flag++)
		if (flags[flag])
			*mntrflags |= (1<<flag);

	return 0;
}

781
782
783
static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info)
{
	struct cfg80211_registered_device *drv;
784
	struct vif_params params;
785
	int err;
Johannes Berg's avatar
Johannes Berg committed
786
	enum nl80211_iftype otype, ntype;
787
	struct net_device *dev;
788
	u32 _flags, *flags = NULL;
789
	bool change = false;
790

791
792
	memset(&params, 0, sizeof(params));

Johannes Berg's avatar
Johannes Berg committed
793
794
	rtnl_lock();

Johannes Berg's avatar
Johannes Berg committed
795
	err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
796
	if (err)
Johannes Berg's avatar
Johannes Berg committed
797
798
		goto unlock_rtnl;

Johannes Berg's avatar
Johannes Berg committed
799
	otype = ntype = dev->ieee80211_ptr->iftype;
800

801
	if (info->attrs[NL80211_ATTR_IFTYPE]) {
802
		ntype = nla_get_u32(info->attrs[NL80211_ATTR_IFTYPE]);
Johannes Berg's avatar
Johannes Berg committed
803
		if (otype != ntype)
804
			change = true;
Johannes Berg's avatar
Johannes Berg committed
805
		if (ntype > NL80211_IFTYPE_MAX) {
806
			err = -EINVAL;
807
			goto unlock;
808
		}
809
810
	}

811
	if (!drv->ops->change_virtual_intf ||
Johannes Berg's avatar
Johannes Berg committed
812
	    !(drv->wiphy.interface_modes & (1 << ntype))) {
813
814
815
816
		err = -EOPNOTSUPP;
		goto unlock;
	}

817
	if (info->attrs[NL80211_ATTR_MESH_ID]) {
Johannes Berg's avatar
Johannes Berg committed
818
		if (ntype != NL80211_IFTYPE_MESH_POINT) {
819
820
821
			err = -EINVAL;
			goto unlock;
		}
822
823
		params.mesh_id = nla_data(info->attrs[NL80211_ATTR_MESH_ID]);
		params.mesh_id_len = nla_len(info->attrs[NL80211_ATTR_MESH_ID]);
824
		change = true;
825
826
	}

827
	if (info->attrs[NL80211_ATTR_MNTR_FLAGS]) {
Johannes Berg's avatar
Johannes Berg committed
828
		if (ntype != NL80211_IFTYPE_MONITOR) {
829
830
831
832
833
			err = -EINVAL;
			goto unlock;
		}
		err = parse_monitor_flags(info->attrs[NL80211_ATTR_MNTR_FLAGS],
					  &_flags);
834
835
836
837
838
		if (err)
			goto unlock;

		flags = &_flags;
		change = true;
839
	}
Johannes Berg's avatar
Johannes Berg committed
840

841
	if (change)
842
		err = drv->ops->change_virtual_intf(&drv->wiphy, dev,
Johannes Berg's avatar
Johannes Berg committed
843
						    ntype, flags, &params);
844
845
	else
		err = 0;
Johannes Berg's avatar
Johannes Berg committed
846

847
	WARN_ON(!err && dev->ieee80211_ptr->iftype != ntype);
Johannes Berg's avatar
Johannes Berg committed
848

849
	if (!err && (ntype != otype)) {
Johannes Berg's avatar
Johannes Berg committed
850
		if (otype == NL80211_IFTYPE_ADHOC)
851
			cfg80211_clear_ibss(dev, false);
Johannes Berg's avatar
Johannes Berg committed
852
	}
Johannes Berg's avatar
Johannes Berg committed
853

854
 unlock:
855
	dev_put(dev);
856
	cfg80211_unlock_rdev(drv);
Johannes Berg's avatar
Johannes Berg committed
857
858
 unlock_rtnl:
	rtnl_unlock();
859
860
861
862
863
864
	return err;
}

static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info)
{
	struct cfg80211_registered_device *drv;
865
	struct vif_params params;
866
867
	int err;
	enum nl80211_iftype type = NL80211_IFTYPE_UNSPECIFIED;
868
	u32 flags;
869

870
871
	memset(&params, 0, sizeof(params));

872
873
874
875
876
877
878
879
880
	if (!info->attrs[NL80211_ATTR_IFNAME])
		return -EINVAL;

	if (info->attrs[NL80211_ATTR_IFTYPE]) {
		type = nla_get_u32(info->attrs[NL80211_ATTR_IFTYPE]);
		if (type > NL80211_IFTYPE_MAX)
			return -EINVAL;
	}

Johannes Berg's avatar
Johannes Berg committed
881
882
	rtnl_lock();

883
	drv = cfg80211_get_dev_from_info(info);
Johannes Berg's avatar
Johannes Berg committed
884
885
886
887
	if (IS_ERR(drv)) {
		err = PTR_ERR(drv);
		goto unlock_rtnl;
	}
888

889
890
	if (!drv->ops->add_virtual_intf ||
	    !(drv->wiphy.interface_modes & (1 << type))) {
891
892
893
894
		err = -EOPNOTSUPP;
		goto unlock;
	}

895
896
897
898
899
900
	if (type == NL80211_IFTYPE_MESH_POINT &&
	    info->attrs[NL80211_ATTR_MESH_ID]) {
		params.mesh_id = nla_data(info->attrs[NL80211_ATTR_MESH_ID]);
		params.mesh_id_len = nla_len(info->attrs[NL80211_ATTR_MESH_ID]);
	}

901
902
903
	err = parse_monitor_flags(type == NL80211_IFTYPE_MONITOR ?
				  info->attrs[NL80211_ATTR_MNTR_FLAGS] : NULL,
				  &flags);
904
	err = drv->ops->add_virtual_intf(&drv->wiphy,
905
		nla_data(info->attrs[NL80211_ATTR_IFNAME]),
906
907
		type, err ? NULL : &flags, &params);

908
 unlock:
909
	cfg80211_unlock_rdev(drv);
Johannes Berg's avatar
Johannes Berg committed
910
911
 unlock_rtnl:
	rtnl_unlock();
912
913
914
915
916
917
918
919
920
	return err;
}

static int nl80211_del_interface(struct sk_buff *skb, struct genl_info *info)
{
	struct cfg80211_registered_device *drv;
	int ifindex, err;
	struct net_device *dev;

Johannes Berg's avatar
Johannes Berg committed
921
922
	rtnl_lock();

Johannes Berg's avatar
Johannes Berg committed
923
	err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
924
	if (err)
Johannes Berg's avatar
Johannes Berg committed
925
		goto unlock_rtnl;
926
927
928
929
930
931
932
933
934
935
936
	ifindex = dev->ifindex;
	dev_put(dev);

	if (!drv->ops->del_virtual_intf) {
		err = -EOPNOTSUPP;
		goto out;
	}

	err = drv->ops->del_virtual_intf(&drv->wiphy, ifindex);

 out:
937
	cfg80211_unlock_rdev(drv);
Johannes Berg's avatar
Johannes Berg committed
938
939
 unlock_rtnl:
	rtnl_unlock();
940
941
942
	return err;
}

943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
struct get_key_cookie {
	struct sk_buff *msg;
	int error;
};

static void get_key_callback(void *c, struct key_params *params)
{
	struct get_key_cookie *cookie = c;

	if (params->key)
		NLA_PUT(cookie->msg, NL80211_ATTR_KEY_DATA,
			params->key_len, params->key);

	if (params->seq)
		NLA_PUT(cookie->msg, NL80211_ATTR_KEY_SEQ,
			params->seq_len, params->seq);

	if (params->cipher)
		NLA_PUT_U32(cookie->msg, NL80211_ATTR_KEY_CIPHER,
			    params->cipher);

	return;
 nla_put_failure:
	cookie->error = 1;
}

static int nl80211_get_key(struct sk_buff *skb, struct genl_info *info)
{
	struct cfg80211_registered_device *drv;
	int err;
	struct net_device *dev;
	u8 key_idx = 0;
	u8 *mac_addr = NULL;
	struct get_key_cookie cookie = {
		.error = 0,
	};
	void *hdr;
	struct sk_buff *msg;

	if (info->attrs[NL80211_ATTR_KEY_IDX])
		key_idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]);

985
	if (key_idx > 5)
986
987
988
989
990
		return -EINVAL;

	if (info->attrs[NL80211_ATTR_MAC])
		mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);

Johannes Berg's avatar
Johannes Berg committed
991
992
	rtnl_lock();

Johannes Berg's avatar
Johannes Berg committed
993
	err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
994
	if (err)
Johannes Berg's avatar
Johannes Berg committed
995
		goto unlock_rtnl;
996
997
998
999
1000
1001

	if (!drv->ops->get_key) {
		err = -EOPNOTSUPP;
		goto out;
	}

1002
	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
	if (!msg) {
		err = -ENOMEM;
		goto out;
	}

	hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
			     NL80211_CMD_NEW_KEY);

	if (IS_ERR(hdr)) {
		err = PTR_ERR(hdr);
		goto out;
	}

	cookie.msg = msg;

	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
	NLA_PUT_U8(msg, NL80211_ATTR_KEY_IDX, key_idx);
	if (mac_addr)
		NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr);

	err = drv->ops->get_key(&drv->wiphy, dev, key_idx, mac_addr,
				&cookie, get_key_callback);

	if (err)
		goto out;

	if (cookie.error)
		goto nla_put_failure;

	genlmsg_end(msg, hdr);
	err = genlmsg_unicast(msg, info->snd_pid);
	goto out;

 nla_put_failure:
	err = -ENOBUFS;
	nlmsg_free(msg);
 out:
1040
	cfg80211_unlock_rdev(drv);
1041
	dev_put(dev);
Johannes Berg's avatar
Johannes Berg committed
1042
1043
1044
 unlock_rtnl:
	rtnl_unlock();

1045
1046
1047
1048
1049
1050
1051
1052
1053
	return err;
}

static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info)
{
	struct cfg80211_registered_device *drv;
	int err;
	struct net_device *dev;
	u8 key_idx;
1054
1055
	int (*func)(struct wiphy *wiphy, struct net_device *netdev,
		    u8 key_index);
1056
1057
1058
1059
1060
1061

	if (!info->attrs[NL80211_ATTR_KEY_IDX])
		return -EINVAL;

	key_idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]);

1062
1063
1064
1065
	if (info->attrs[NL80211_ATTR_KEY_DEFAULT_MGMT]) {
		if (key_idx < 4 || key_idx > 5)
			return -EINVAL;
	} else if (key_idx > 3)
1066
1067
1068
		return -EINVAL;

	/* currently only support setting default key */
1069
1070
	if (!info->attrs[NL80211_ATTR_KEY_DEFAULT] &&
	    !info->attrs[NL80211_ATTR_KEY_DEFAULT_MGMT])
1071
1072
		return -EINVAL;

Johannes Berg's avatar
Johannes Berg committed
1073
1074
	rtnl_lock();

Johannes Berg's avatar
Johannes Berg committed
1075
	err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
1076
	if (err)
Johannes Berg's avatar
Johannes Berg committed
1077
		goto unlock_rtnl;
1078

1079
1080
1081
1082
1083
1084
	if (info->attrs[NL80211_ATTR_KEY_DEFAULT])
		func = drv->ops->set_default_key;
	else
		func = drv->ops->set_default_mgmt_key;

	if (!func) {
1085
1086
1087
1088
		err = -EOPNOTSUPP;
		goto out;
	}

1089
	err = func(&drv->wiphy, dev, key_idx);
1090
1091
1092
1093
1094
1095
1096
1097
#ifdef CONFIG_WIRELESS_EXT
	if (!err) {
		if (func == drv->ops->set_default_key)
			dev->ieee80211_ptr->wext.default_key = key_idx;
		else
			dev->ieee80211_ptr->wext.default_mgmt_key = key_idx;
	}
#endif
1098
1099

 out:
1100
	cfg80211_unlock_rdev(drv);
1101
	dev_put(dev);
Johannes Berg's avatar
Johannes Berg committed
1102
1103
1104
1105

 unlock_rtnl:
	rtnl_unlock();

1106
1107
1108
1109
1110
1111
	return err;
}

static int nl80211_new_key(struct sk_buff *skb, struct genl_info *info)
{
	struct cfg80211_registered_device *drv;
1112
	int err, i;
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
	struct net_device *dev;
	struct key_params params;
	u8 key_idx = 0;
	u8 *mac_addr = NULL;

	memset(&params, 0, sizeof(params));

	if (!info->attrs[NL80211_ATTR_KEY_CIPHER])
		return -EINVAL;

	if (info->attrs[NL80211_ATTR_KEY_DATA]) {
		params.key = nla_data(info->attrs[NL80211_ATTR_KEY_DATA]);
		params.key_len = nla_len(info->attrs[NL80211_ATTR_KEY_DATA]);
	}

1128
1129
1130
1131
1132
	if (info->attrs[NL80211_ATTR_KEY_SEQ]) {
		params.seq = nla_data(info->attrs[NL80211_ATTR_KEY_SEQ]);
		params.seq_len = nla_len(info->attrs[NL80211_ATTR_KEY_SEQ]);
	}

1133
1134
1135
1136
1137
1138
1139
1140
	if (info->attrs[NL80211_ATTR_KEY_IDX])
		key_idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]);

	params.cipher = nla_get_u32(info->attrs[NL80211_ATTR_KEY_CIPHER]);

	if (info->attrs[NL80211_ATTR_MAC])
		mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);

1141
	if (cfg80211_validate_key_settings(&params, key_idx, mac_addr))
1142
1143
		return -EINVAL;

Johannes Berg's avatar
Johannes Berg committed
1144
1145
	rtnl_lock();

Johannes Berg's avatar
Johannes Berg committed
1146
	err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
1147
	if (err)
Johannes Berg's avatar
Johannes Berg committed
1148
		goto unlock_rtnl;
1149

1150
1151
1152
1153
1154
1155
1156
1157
	for (i = 0; i < drv->wiphy.n_cipher_suites; i++)
		if (params.cipher == drv->wiphy.cipher_suites[i])
			break;
	if (i == drv->wiphy.n_cipher_suites) {
		err = -EINVAL;
		goto out;
	}

1158
1159
1160
1161
1162
1163
1164
1165
	if (!drv->ops->add_key) {
		err = -EOPNOTSUPP;
		goto out;
	}

	err = drv->ops->add_key(&drv->wiphy, dev, key_idx, mac_addr, &params);

 out:
1166
	cfg80211_unlock_rdev(drv);
1167
	dev_put(dev);
Johannes Berg's avatar
Johannes Berg committed
1168
1169
1170
 unlock_rtnl:
	rtnl_unlock();

1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
	return err;
}

static int nl80211_del_key(struct sk_buff *skb, struct genl_info *info)
{
	struct cfg80211_registered_device *drv;
	int err;
	struct net_device *dev;
	u8 key_idx = 0;
	u8 *mac_addr = NULL;

	if (info->attrs[NL80211_ATTR_KEY_IDX])
		key_idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]);

1185
	if (key_idx > 5)
1186
1187
1188
1189
1190
		return -EINVAL;

	if (info->attrs[NL80211_ATTR_MAC])
		mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);

Johannes Berg's avatar
Johannes Berg committed
1191
1192
	rtnl_lock();

Johannes Berg's avatar
Johannes Berg committed
1193
	err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
1194
	if (err)
Johannes Berg's avatar
Johannes Berg committed
1195
		goto unlock_rtnl;
1196
1197
1198
1199
1200
1201
1202
1203

	if (!drv->ops->del_key) {
		err = -EOPNOTSUPP;
		goto out;
	}

	err = drv->ops->del_key(&drv->wiphy, dev, key_idx, mac_addr);

1204
1205
1206
1207
1208
1209
1210
1211
1212
#ifdef CONFIG_WIRELESS_EXT
	if (!err) {
		if (key_idx == dev->ieee80211_ptr->wext.default_key)
			dev->ieee80211_ptr->wext.default_key = -1;
		else if (key_idx == dev->ieee80211_ptr->wext.default_mgmt_key)
			dev->ieee80211_ptr->wext.default_mgmt_key = -1;
	}
#endif

1213
 out:
1214
	cfg80211_unlock_rdev(drv);
1215
	dev_put(dev);
Johannes Berg's avatar
Johannes Berg committed
1216
1217
1218
1219

 unlock_rtnl:
	rtnl_unlock();

1220
1221
1222
	return err;
}

1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
static int nl80211_addset_beacon(struct sk_buff *skb, struct genl_info *info)
{
        int (*call)(struct wiphy *wiphy, struct net_device *dev,
		    struct beacon_parameters *info);
	struct cfg80211_registered_device *drv;
	int err;
	struct net_device *dev;
	struct beacon_parameters params;
	int haveinfo = 0;

1233
1234
1235
	if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_BEACON_TAIL]))
		return -EINVAL;

Johannes Berg's avatar
Johannes Berg committed
1236
1237
	rtnl_lock();

Johannes Berg's avatar
Johannes Berg committed
1238
	err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
1239
	if (err)
Johannes Berg's avatar
Johannes Berg committed
1240
		goto unlock_rtnl;
1241

1242
1243
1244
1245
1246
	if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP) {
		err = -EOPNOTSUPP;
		goto out;
	}

1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302