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

#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
60
	*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,
				      .len = BUS_ID_SIZE-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_IFTYPE] = { .type = NLA_U32 },
	[NL80211_ATTR_IFINDEX] = { .type = NLA_U32 },
	[NL80211_ATTR_IFNAME] = { .type = NLA_NUL_STRING, .len = IFNAMSIZ-1 },
68
69
70
71
72
73
74
75

	[NL80211_ATTR_MAC] = { .type = NLA_BINARY, .len = ETH_ALEN },

	[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 },
76
77
78
79
80
81
82

	[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 },
83
84
85
86
87
	[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 },
88
	[NL80211_ATTR_STA_PLINK_ACTION] = { .type = NLA_U8 },
89
	[NL80211_ATTR_STA_VLAN] = { .type = NLA_U32 },
Johannes Berg's avatar
Johannes Berg committed
90
	[NL80211_ATTR_MNTR_FLAGS] = { /* NLA_NESTED can't be empty */ },
91
92
93
	[NL80211_ATTR_MESH_ID] = { .type = NLA_BINARY,
				.len = IEEE80211_MAX_MESH_ID_LEN },
	[NL80211_ATTR_MPATH_NEXT_HOP] = { .type = NLA_U32 },
94

95
96
97
	[NL80211_ATTR_REG_ALPHA2] = { .type = NLA_STRING, .len = 2 },
	[NL80211_ATTR_REG_RULES] = { .type = NLA_NESTED },

98
99
100
	[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 },
101
102
	[NL80211_ATTR_BSS_BASIC_RATES] = { .type = NLA_BINARY,
					   .len = NL80211_MAX_SUPP_RATES },
103

104
105
	[NL80211_ATTR_MESH_PARAMS] = { .type = NLA_NESTED },

106
107
	[NL80211_ATTR_HT_CAPABILITY] = { .type = NLA_BINARY,
					 .len = NL80211_HT_CAPABILITY_LEN },
108
109
110
111

	[NL80211_ATTR_MGMT_SUBTYPE] = { .type = NLA_U8 },
	[NL80211_ATTR_IE] = { .type = NLA_BINARY,
			      .len = IEEE80211_MAX_DATA_LEN },
112
113
	[NL80211_ATTR_SCAN_FREQUENCIES] = { .type = NLA_NESTED },
	[NL80211_ATTR_SCAN_SSIDS] = { .type = NLA_NESTED },
114
115
116
117
118

	[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 },
119
120
};

121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
/* 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;
}

151
152
153
154
155
156
157
158
/* 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);
}

159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
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;
}

183
184
185
186
187
188
/* 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;
189
190
191
	struct nlattr *nl_bands, *nl_band;
	struct nlattr *nl_freqs, *nl_freq;
	struct nlattr *nl_rates, *nl_rate;
192
	struct nlattr *nl_modes;
193
	struct nlattr *nl_cmds;
194
195
196
197
	enum ieee80211_band band;
	struct ieee80211_channel *chan;
	struct ieee80211_rate *rate;
	int i;
198
	u16 ifmodes = dev->wiphy.interface_modes;
199
200
201
202
203

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

204
	NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, dev->wiphy_idx);
205
	NLA_PUT_STRING(msg, NL80211_ATTR_WIPHY_NAME, wiphy_name(&dev->wiphy));
206
207
	NLA_PUT_U8(msg, NL80211_ATTR_MAX_NUM_SCAN_SSIDS,
		   dev->wiphy.max_scan_ssids);
208
209
	NLA_PUT_U16(msg, NL80211_ATTR_MAX_SCAN_IE_LEN,
		    dev->wiphy.max_scan_ie_len);
210

211
212
213
214
	NLA_PUT(msg, NL80211_ATTR_CIPHER_SUITES,
		sizeof(u32) * dev->wiphy.n_cipher_suites,
		dev->wiphy.cipher_suites);

215
216
217
218
219
220
221
222
223
224
225
226
227
228
	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);

229
230
231
232
233
234
235
236
237
238
239
240
	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;

241
242
243
244
245
246
247
248
249
250
251
252
253
		/* 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);
		}

254
255
256
257
258
259
260
261
262
263
264
		/* 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];
265
266
267

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

269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
			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);

300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
	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);
321
322
323
324
	CMD(auth, AUTHENTICATE);
	CMD(assoc, ASSOCIATE);
	CMD(deauth, DEAUTHENTICATE);
	CMD(disassoc, DISASSOCIATE);
325
326
327
328

#undef CMD
	nla_nest_end(msg, nl_cmds);

329
330
331
	return genlmsg_end(msg, hdr);

 nla_put_failure:
332
333
	genlmsg_cancel(msg, hdr);
	return -EMSGSIZE;
334
335
336
337
338
339
340
341
}

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;

342
	mutex_lock(&cfg80211_mutex);
343
	list_for_each_entry(dev, &cfg80211_drv_list, list) {
344
		if (++idx <= start)
345
346
347
			continue;
		if (nl80211_send_wiphy(skb, NETLINK_CB(cb->skb).pid,
				       cb->nlh->nlmsg_seq, NLM_F_MULTI,
348
349
				       dev) < 0) {
			idx--;
350
			break;
351
		}
352
	}
353
	mutex_unlock(&cfg80211_mutex);
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386

	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);

	msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
	if (!msg)
		goto out_err;

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

	cfg80211_put_dev(dev);

	return genlmsg_unicast(msg, info->snd_pid);

 out_free:
	nlmsg_free(msg);
 out_err:
	cfg80211_put_dev(dev);
	return -ENOBUFS;
}

387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
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;
}

412
413
414
static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
{
	struct cfg80211_registered_device *rdev;
415
416
	int result = 0, rem_txq_params = 0;
	struct nlattr *nl_txq_params;
417

418
	rtnl_lock();
419

420
421
422
423
424
425
426
427
428
429
430
	mutex_lock(&cfg80211_mutex);

	rdev = __cfg80211_drv_from_info(info);
	if (IS_ERR(rdev)) {
		result = PTR_ERR(rdev);
		goto unlock;
	}

	mutex_lock(&rdev->mtx);

	if (info->attrs[NL80211_ATTR_WIPHY_NAME])
431
432
		result = cfg80211_dev_rename(
			rdev, nla_data(info->attrs[NL80211_ATTR_WIPHY_NAME]));
433
434
435
436
437

	mutex_unlock(&cfg80211_mutex);

	if (result)
		goto bad_res;
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464

	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;
		}
	}
465

466
	if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
Sujith's avatar
Sujith committed
467
		enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT;
468
		struct ieee80211_channel *chan;
469
		struct ieee80211_sta_ht_cap *ht_cap;
470
471
472
473
474
475
476
		u32 freq, sec_freq;

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

477
478
		result = -EINVAL;

Sujith's avatar
Sujith committed
479
480
481
482
483
484
485
		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)
486
487
488
489
490
				goto bad_res;
		}

		freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]);
		chan = ieee80211_get_channel(&rdev->wiphy, freq);
491
492
493

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

Sujith's avatar
Sujith committed
496
		if (channel_type == NL80211_CHAN_HT40MINUS)
497
			sec_freq = freq - 20;
Sujith's avatar
Sujith committed
498
		else if (channel_type == NL80211_CHAN_HT40PLUS)
499
500
501
502
			sec_freq = freq + 20;
		else
			sec_freq = 0;

503
504
505
		ht_cap = &rdev->wiphy.bands[chan->band]->ht_cap;

		/* no HT capabilities */
Sujith's avatar
Sujith committed
506
		if (channel_type != NL80211_CHAN_NO_HT &&
507
508
509
		    !ht_cap->ht_supported)
			goto bad_res;

510
511
		if (sec_freq) {
			struct ieee80211_channel *schan;
512
513
514
515
516
517

			/* no 40 MHz capabilities */
			if (!(ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) ||
			    (ht_cap->cap & IEEE80211_HT_CAP_40MHZ_INTOLERANT))
				goto bad_res;

518
			schan = ieee80211_get_channel(&rdev->wiphy, sec_freq);
519
520
521

			/* Secondary channel not allowed */
			if (!schan || schan->flags & IEEE80211_CHAN_DISABLED)
522
523
524
525
				goto bad_res;
		}

		result = rdev->ops->set_channel(&rdev->wiphy, chan,
Sujith's avatar
Sujith committed
526
						channel_type);
527
528
529
530
531
		if (result)
			goto bad_res;
	}


532
 bad_res:
533
534
535
	mutex_unlock(&rdev->mtx);
 unlock:
	rtnl_unlock();
536
537
538
539
540
	return result;
}


static int nl80211_send_iface(struct sk_buff *msg, u32 pid, u32 seq, int flags,
541
			      struct cfg80211_registered_device *rdev,
542
543
544
545
546
547
548
549
550
			      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);
551
	NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
552
	NLA_PUT_STRING(msg, NL80211_ATTR_IFNAME, dev->name);
Johannes Berg's avatar
Johannes Berg committed
553
	NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, dev->ieee80211_ptr->iftype);
554
555
556
	return genlmsg_end(msg, hdr);

 nla_put_failure:
557
558
	genlmsg_cancel(msg, hdr);
	return -EMSGSIZE;
559
560
561
562
563
564
565
566
567
568
569
}

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;

570
	mutex_lock(&cfg80211_mutex);
571
	list_for_each_entry(dev, &cfg80211_drv_list, list) {
Johannes Berg's avatar
Johannes Berg committed
572
573
		if (wp_idx < wp_start) {
			wp_idx++;
574
			continue;
Johannes Berg's avatar
Johannes Berg committed
575
		}
576
577
578
579
		if_idx = 0;

		mutex_lock(&dev->devlist_mtx);
		list_for_each_entry(wdev, &dev->netdev_list, list) {
Johannes Berg's avatar
Johannes Berg committed
580
581
			if (if_idx < if_start) {
				if_idx++;
582
				continue;
Johannes Berg's avatar
Johannes Berg committed
583
			}
584
585
			if (nl80211_send_iface(skb, NETLINK_CB(cb->skb).pid,
					       cb->nlh->nlmsg_seq, NLM_F_MULTI,
586
					       dev, wdev->netdev) < 0) {
Johannes Berg's avatar
Johannes Berg committed
587
588
589
590
				mutex_unlock(&dev->devlist_mtx);
				goto out;
			}
			if_idx++;
591
592
		}
		mutex_unlock(&dev->devlist_mtx);
Johannes Berg's avatar
Johannes Berg committed
593
594

		wp_idx++;
595
	}
Johannes Berg's avatar
Johannes Berg committed
596
 out:
597
	mutex_unlock(&cfg80211_mutex);
598
599
600
601
602
603
604
605
606
607
608
609
610
611

	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
612
	err = get_drv_dev_by_info_ifindex(info->attrs, &dev, &netdev);
613
614
615
616
617
618
619
	if (err)
		return err;

	msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
	if (!msg)
		goto out_err;

620
621
	if (nl80211_send_iface(msg, info->snd_pid, info->snd_seq, 0,
			       dev, netdev) < 0)
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
		goto out_free;

	dev_put(netdev);
	cfg80211_put_dev(dev);

	return genlmsg_unicast(msg, info->snd_pid);

 out_free:
	nlmsg_free(msg);
 out_err:
	dev_put(netdev);
	cfg80211_put_dev(dev);
	return -ENOBUFS;
}

637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
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;
}

666
667
668
static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info)
{
	struct cfg80211_registered_device *drv;
669
	struct vif_params params;
670
671
672
	int err, ifindex;
	enum nl80211_iftype type;
	struct net_device *dev;
673
	u32 _flags, *flags = NULL;
674
	bool change = false;
675

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

Johannes Berg's avatar
Johannes Berg committed
678
679
	rtnl_lock();

Johannes Berg's avatar
Johannes Berg committed
680
	err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
681
	if (err)
Johannes Berg's avatar
Johannes Berg committed
682
683
		goto unlock_rtnl;

684
	ifindex = dev->ifindex;
685
	type = dev->ieee80211_ptr->iftype;
686
687
	dev_put(dev);

688
	if (info->attrs[NL80211_ATTR_IFTYPE]) {
689
690
691
692
693
694
695
696
		enum nl80211_iftype ntype;

		ntype = nla_get_u32(info->attrs[NL80211_ATTR_IFTYPE]);
		if (type != ntype)
			change = true;
		type = ntype;
		if (type > NL80211_IFTYPE_MAX) {
			err = -EINVAL;
697
			goto unlock;
698
		}
699
700
	}

701
702
	if (!drv->ops->change_virtual_intf ||
	    !(drv->wiphy.interface_modes & (1 << type))) {
703
704
705
706
		err = -EOPNOTSUPP;
		goto unlock;
	}

707
708
709
710
711
	if (info->attrs[NL80211_ATTR_MESH_ID]) {
		if (type != NL80211_IFTYPE_MESH_POINT) {
			err = -EINVAL;
			goto unlock;
		}
712
713
		params.mesh_id = nla_data(info->attrs[NL80211_ATTR_MESH_ID]);
		params.mesh_id_len = nla_len(info->attrs[NL80211_ATTR_MESH_ID]);
714
		change = true;
715
716
	}

717
718
719
720
721
722
723
	if (info->attrs[NL80211_ATTR_MNTR_FLAGS]) {
		if (type != NL80211_IFTYPE_MONITOR) {
			err = -EINVAL;
			goto unlock;
		}
		err = parse_monitor_flags(info->attrs[NL80211_ATTR_MNTR_FLAGS],
					  &_flags);
724
725
726
727
728
		if (err)
			goto unlock;

		flags = &_flags;
		change = true;
729
	}
Johannes Berg's avatar
Johannes Berg committed
730

731
732
733
734
735
	if (change)
		err = drv->ops->change_virtual_intf(&drv->wiphy, ifindex,
						    type, flags, &params);
	else
		err = 0;
Johannes Berg's avatar
Johannes Berg committed
736
737
738
739

	dev = __dev_get_by_index(&init_net, ifindex);
	WARN_ON(!dev || (!err && dev->ieee80211_ptr->iftype != type));

740
741
 unlock:
	cfg80211_put_dev(drv);
Johannes Berg's avatar
Johannes Berg committed
742
743
 unlock_rtnl:
	rtnl_unlock();
744
745
746
747
748
749
	return err;
}

static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info)
{
	struct cfg80211_registered_device *drv;
750
	struct vif_params params;
751
752
	int err;
	enum nl80211_iftype type = NL80211_IFTYPE_UNSPECIFIED;
753
	u32 flags;
754

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

757
758
759
760
761
762
763
764
765
	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
766
767
	rtnl_lock();

768
	drv = cfg80211_get_dev_from_info(info);
Johannes Berg's avatar
Johannes Berg committed
769
770
771
772
	if (IS_ERR(drv)) {
		err = PTR_ERR(drv);
		goto unlock_rtnl;
	}
773

774
775
	if (!drv->ops->add_virtual_intf ||
	    !(drv->wiphy.interface_modes & (1 << type))) {
776
777
778
779
		err = -EOPNOTSUPP;
		goto unlock;
	}

780
781
782
783
784
785
	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]);
	}

786
787
788
	err = parse_monitor_flags(type == NL80211_IFTYPE_MONITOR ?
				  info->attrs[NL80211_ATTR_MNTR_FLAGS] : NULL,
				  &flags);
789
	err = drv->ops->add_virtual_intf(&drv->wiphy,
790
		nla_data(info->attrs[NL80211_ATTR_IFNAME]),
791
792
		type, err ? NULL : &flags, &params);

793
794
 unlock:
	cfg80211_put_dev(drv);
Johannes Berg's avatar
Johannes Berg committed
795
796
 unlock_rtnl:
	rtnl_unlock();
797
798
799
800
801
802
803
804
805
	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
806
807
	rtnl_lock();

Johannes Berg's avatar
Johannes Berg committed
808
	err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
809
	if (err)
Johannes Berg's avatar
Johannes Berg committed
810
		goto unlock_rtnl;
811
812
813
814
815
816
817
818
819
820
821
822
	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:
	cfg80211_put_dev(drv);
Johannes Berg's avatar
Johannes Berg committed
823
824
 unlock_rtnl:
	rtnl_unlock();
825
826
827
	return err;
}

828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
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]);

870
	if (key_idx > 5)
871
872
873
874
875
		return -EINVAL;

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

Johannes Berg's avatar
Johannes Berg committed
876
877
	rtnl_lock();

Johannes Berg's avatar
Johannes Berg committed
878
	err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
879
	if (err)
Johannes Berg's avatar
Johannes Berg committed
880
		goto unlock_rtnl;
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926

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

	msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
	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:
	cfg80211_put_dev(drv);
	dev_put(dev);
Johannes Berg's avatar
Johannes Berg committed
927
928
929
 unlock_rtnl:
	rtnl_unlock();

930
931
932
933
934
935
936
937
938
	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;
939
940
	int (*func)(struct wiphy *wiphy, struct net_device *netdev,
		    u8 key_index);
941
942
943
944
945
946

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

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

947
948
949
950
	if (info->attrs[NL80211_ATTR_KEY_DEFAULT_MGMT]) {
		if (key_idx < 4 || key_idx > 5)
			return -EINVAL;
	} else if (key_idx > 3)
951
952
953
		return -EINVAL;

	/* currently only support setting default key */
954
955
	if (!info->attrs[NL80211_ATTR_KEY_DEFAULT] &&
	    !info->attrs[NL80211_ATTR_KEY_DEFAULT_MGMT])
956
957
		return -EINVAL;

Johannes Berg's avatar
Johannes Berg committed
958
959
	rtnl_lock();

Johannes Berg's avatar
Johannes Berg committed
960
	err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
961
	if (err)
Johannes Berg's avatar
Johannes Berg committed
962
		goto unlock_rtnl;
963

964
965
966
967
968
969
	if (info->attrs[NL80211_ATTR_KEY_DEFAULT])
		func = drv->ops->set_default_key;
	else
		func = drv->ops->set_default_mgmt_key;

	if (!func) {
970
971
972
973
		err = -EOPNOTSUPP;
		goto out;
	}

974
	err = func(&drv->wiphy, dev, key_idx);
975
976
977
978

 out:
	cfg80211_put_dev(drv);
	dev_put(dev);
Johannes Berg's avatar
Johannes Berg committed
979
980
981
982

 unlock_rtnl:
	rtnl_unlock();

983
984
985
986
987
988
	return err;
}

static int nl80211_new_key(struct sk_buff *skb, struct genl_info *info)
{
	struct cfg80211_registered_device *drv;
989
	int err, i;
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
	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]);
	}

	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]);

1013
	if (key_idx > 5)
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
1040
1041
1042
1043
		return -EINVAL;

	/*
	 * Disallow pairwise keys with non-zero index unless it's WEP
	 * (because current deployments use pairwise WEP keys with
	 * non-zero indizes but 802.11i clearly specifies to use zero)
	 */
	if (mac_addr && key_idx &&
	    params.cipher != WLAN_CIPHER_SUITE_WEP40 &&
	    params.cipher != WLAN_CIPHER_SUITE_WEP104)
		return -EINVAL;

	/* TODO: add definitions for the lengths to linux/ieee80211.h */
	switch (params.cipher) {
	case WLAN_CIPHER_SUITE_WEP40:
		if (params.key_len != 5)
			return -EINVAL;
		break;
	case WLAN_CIPHER_SUITE_TKIP:
		if (params.key_len != 32)
			return -EINVAL;
		break;
	case WLAN_CIPHER_SUITE_CCMP:
		if (params.key_len != 16)
			return -EINVAL;
		break;
	case WLAN_CIPHER_SUITE_WEP104:
		if (params.key_len != 13)
			return -EINVAL;
		break;
1044
1045
1046
1047
	case WLAN_CIPHER_SUITE_AES_CMAC:
		if (params.key_len != 16)
			return -EINVAL;
		break;
1048
1049
1050
1051
	default:
		return -EINVAL;
	}

Johannes Berg's avatar
Johannes Berg committed
1052
1053
	rtnl_lock();

Johannes Berg's avatar
Johannes Berg committed
1054
	err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
1055
	if (err)
Johannes Berg's avatar
Johannes Berg committed
1056
		goto unlock_rtnl;
1057

1058
1059
1060
1061
1062
1063
1064
1065
	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;
	}

1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
	if (!drv->ops->add_key) {
		err = -EOPNOTSUPP;
		goto out;
	}

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

 out:
	cfg80211_put_dev(drv);
	dev_put(dev);
Johannes Berg's avatar
Johannes Berg committed
1076
1077
1078
 unlock_rtnl:
	rtnl_unlock();

1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
	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]);

1093
	if (key_idx > 5)
1094
1095
1096
1097
1098
		return -EINVAL;

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

Johannes Berg's avatar
Johannes Berg committed
1099
1100
	rtnl_lock();

Johannes Berg's avatar
Johannes Berg committed
1101
	err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
1102
	if (err)
Johannes Berg's avatar
Johannes Berg committed
1103
		goto unlock_rtnl;
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114

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

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

 out:
	cfg80211_put_dev(drv);
	dev_put(dev);
Johannes Berg's avatar
Johannes Berg committed
1115
1116
1117
1118

 unlock_rtnl:
	rtnl_unlock();

1119
1120
1121
	return err;
}

1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
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;

1132
1133
1134
	if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_BEACON_TAIL]))
		return -EINVAL;

Johannes Berg's avatar
Johannes Berg committed
1135
1136
	rtnl_lock();

Johannes Berg's avatar
Johannes Berg committed
1137
	err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
1138
	if (err)
Johannes Berg's avatar
Johannes Berg committed
1139
		goto unlock_rtnl;
1140

1141
1142
1143
1144
1145
	if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP) {
		err = -EOPNOTSUPP;
		goto out;
	}

1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
	switch (info->genlhdr->cmd) {
	case NL80211_CMD_NEW_BEACON:
		/* these are required for NEW_BEACON */
		if (!info->attrs[NL80211_ATTR_BEACON_INTERVAL] ||
		    !info->attrs[NL80211_ATTR_DTIM_PERIOD] ||
		    !info->attrs[NL80211_ATTR_BEACON_HEAD]) {
			err = -EINVAL;
			goto out;
		}

		call = drv->ops->add_beacon;
		break;
	case NL80211_CMD_SET_BEACON:
		call = drv->ops->set_beacon;
		break;
	default:
		WARN_ON(1);
		err = -EOPNOTSUPP;
		goto out;
	}

	if (!call) {
		err = -EOPNOTSUPP;
		goto out;
	}

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

	if (info->attrs[NL80211_ATTR_BEACON_INTERVAL]) {
		params.interval =
		    nla_get_u32(info->attrs[NL80211_ATTR_BEACON_INTERVAL]);
		haveinfo = 1;
	}

	if (info->attrs[NL80211_ATTR_DTIM_PERIOD]) {
		params.dtim_period =
		    nla_get_u32(info->attrs[NL80211_ATTR_DTIM_PERIOD]);
		haveinfo = 1;
	}

	if (info->attrs[NL80211_ATTR_BEACON_HEAD]) {
		params.head = nla_data(info->attrs[NL80211_ATTR_BEACON_HEAD]);
		params.head_len =
		    nla_len(info->attrs[NL80211_ATTR_BEACON_HEAD]);
		haveinfo = 1;
	}

	if (info->attrs[NL80211_ATTR_BEACON_TAIL]) {
		params.tail = nla_data(info->attrs[NL80211_ATTR_BEACON_TAIL]);
		params.tail_len =
		    nla_len(info->attrs[NL80211_ATTR_BEACON_TAIL]);
		haveinfo = 1;
	}

	if (!haveinfo) {
		err = -EINVAL;
		goto out;
	}

	err = call(&drv->wiphy, dev, &params);

 out:
	cfg80211_put_dev(drv);
	dev_put(dev);
Johannes Berg's avatar
Johannes Berg committed
1210
1211
1212
 unlock_rtnl:
	rtnl_unlock();

1213
1214
1215
1216
1217
1218
1219
1220
1221
	return err;
}

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

Johannes Berg's avatar
Johannes Berg committed
1222
1223
	rtnl_lock();

Johannes Berg's avatar
Johannes Berg committed
1224
	err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
1225
	if (err)
Johannes Berg's avatar
Johannes Berg committed
1226
		goto unlock_rtnl;
1227
1228
1229
1230
1231
1232

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

1233
1234
1235
1236
	if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP) {
		err = -EOPNOTSUPP;
		goto out;
	}
1237
1238
1239
1240
1241
	err = drv->ops->del_beacon(&drv->wiphy, dev);

 out:
	cfg80211_put_dev(drv);
	dev_put(dev);
Johannes Berg's avatar
Johannes Berg committed
1242
1243
1244
 unlock_rtnl:
	rtnl_unlock();

1245
1246
1247
	return err;
}

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
static const struct nla_policy sta_flags_policy[NL80211_STA_FLAG_MAX + 1] = {
	[NL80211_STA_FLAG_AUTHORIZED] = { .type = NLA_FLAG },
	[NL80211_STA_FLAG_SHORT_PREAMBLE] = { .type = NLA_FLAG },
	[NL80211_STA_FLAG_WME] = { .type = NLA_FLAG },
};

static int parse_station_flags(struct nlattr *nla, u32 *staflags)
{
	struct nlattr *flags[NL80211_STA_FLAG_MAX + 1];
	int flag;

	*staflags = 0;

	if (!nla)
		return 0;

	if (nla_parse_nested(flags, NL80211_STA_FLAG_MAX,
			     nla, sta_flags_policy))
		return -EINVAL;

	*staflags = STATION_FLAG_CHANGED;

	for (flag = 1; flag <= NL80211_STA_FLAG_MAX; flag++)
		if (flags[flag])
			*staflags |= (1<<flag);

	return 0;
}

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
1303
1304
1305
1306
1307
1308
1309
static u16 nl80211_calculate_bitrate(struct rate_info *rate)
{
	int modulation, streams, bitrate;

	if (!(rate->flags & RATE_INFO_FLAGS_MCS))
		return rate->legacy;

	/* the formula below does only work for MCS values smaller than 32 */
	if (rate->mcs >= 32)
		return 0;

	modulation = rate->mcs & 7;
	streams = (rate->mcs >> 3) + 1;

	bitrate = (rate->flags & RATE_INFO_FLAGS_40_MHZ_WIDTH) ?
			13500000 : 6500000;

	if (modulation < 4)
		bitrate *= (modulation + 1);
	else if (modulation == 4)
		bitrate *= (modulation + 2);
	else
		bitrate *= (modulation + 3);

	bitrate *= streams;

	if (rate->flags & RATE_INFO_FLAGS_SHORT_GI)
		bitrate = (bitrate / 9) * 10;

	/* do NOT round down here */
	return (bitrate + 50000) / 100000;
}

1310
1311
static int nl80211_send_station(struct sk_buff *msg, u32 pid, u32 seq,
				int flags, struct net_device *dev,
1312
				u8 *mac_addr, struct station_info *sinfo)
1313
1314
{
	void *hdr;
1315
1316
	struct nlattr *sinfoattr, *txrate;
	u16 bitrate;
1317
1318
1319
1320
1321
1322
1323
1324

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

	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
	NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr);

1325
1326
	sinfoattr = nla_nest_start(msg, NL80211_ATTR_STA_INFO);
	if (!sinfoattr)
1327
		goto nla_put_failure;
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
	if (sinfo->filled & STATION_INFO_INACTIVE_TIME)
		NLA_PUT_U32(msg, NL80211_STA_INFO_INACTIVE_TIME,
			    sinfo->inactive_time);
	if (sinfo->filled & STATION_INFO_RX_BYTES)
		NLA_PUT_U32(msg, NL80211_STA_INFO_RX_BYTES,
			    sinfo->rx_bytes);
	if (sinfo->filled & STATION_INFO_TX_BYTES)
		NLA_PUT_U32(msg, NL80211_STA_INFO_TX_BYTES,
			    sinfo->tx_bytes);
	if (sinfo->filled & STATION_INFO_LLID)
		NLA_PUT_U16(msg, NL80211_STA_INFO_LLID,
			    sinfo->llid);
	if (sinfo->filled & STATION_INFO_PLID)
		NLA_PUT_U16(msg, NL80211_STA_INFO_PLID,
			    sinfo->plid);
	if (sinfo->filled & STATION_INFO_PLINK_STATE)
		NLA_PUT_U8(msg, NL80211_STA_INFO_PLINK_STATE,
			    sinfo->plink_state);
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
	if (sinfo->filled & STATION_INFO_SIGNAL)
		NLA_PUT_U8(msg, NL80211_STA_INFO_SIGNAL,
			   sinfo->signal);
	if (sinfo->filled & STATION_INFO_TX_BITRATE) {
		txrate = nla_nest_start(msg, NL80211_STA_INFO_TX_BITRATE);
		if (!txrate)
			goto nla_put_failure;

		/* nl80211_calculate_bitrate will return 0 for mcs >= 32 */
		bitrate = nl80211_calculate_bitrate(&sinfo->txrate);
		if (bitrate > 0)
			NLA_PUT_U16(msg, NL80211_RATE_INFO_BITRATE, bitrate);
1358

1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
		if (sinfo->txrate.flags & RATE_INFO_FLAGS_MCS)
			NLA_PUT_U8(msg, NL80211_RATE_INFO_MCS,
				    sinfo->txrate.mcs);
		if (sinfo->txrate.flags & RATE_INFO_FLAGS_40_MHZ_WIDTH)
			NLA_PUT_FLAG(msg, NL80211_RATE_INFO_40_MHZ_WIDTH);
		if (sinfo->txrate.flags & RATE_INFO_FLAGS_SHORT_GI)
			NLA_PUT_FLAG(msg, NL80211_RATE_INFO_SHORT_GI);

		nla_nest_end(msg, txrate);
	}
1369
1370
1371
1372
1373
1374
	if (sinfo->filled & STATION_INFO_RX_PACKETS)
		NLA_PUT_U32(msg, NL80211_STA_INFO_RX_PACKETS,
			    sinfo->rx_packets);
	if (sinfo->filled & STATION_INFO_TX_PACKETS)
		NLA_PUT_U32(msg, NL80211_STA_INFO_TX_PACKETS,
			    sinfo->tx_packets);
1375
	nla_nest_end(msg, sinfoattr);
1376
1377
1378
1379

	return genlmsg_end(msg, hdr);

 nla_put_failure:
1380
1381
	genlmsg_cancel(msg, hdr);
	return -EMSGSIZE;
1382
1383
}

1384
static int nl80211_dump_station(struct sk_buff *skb,
Johannes Berg's avatar
Johannes Berg committed
1385
				struct netlink_callback *cb)
1386
1387
1388
{
	struct station_info sinfo;
	struct cfg80211_registered_device *dev;
Johannes Berg's avatar
Johannes Berg committed
1389
	struct net_device *netdev;
1390
	u8 mac_addr[ETH_ALEN];
Johannes Berg's avatar
Johannes Berg committed
1391
1392
	int ifidx = cb->args[0];
	int sta_idx = cb->args[1];
1393
1394
	int err;

Johannes Berg's avatar
Johannes Berg committed
1395
1396
1397
1398
1399
1400
	if (!ifidx) {
		err = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize,
				  nl80211_fam.attrbuf, nl80211_fam.maxattr,
				  nl80211_policy);
		if (err)
			return err;
1401

Johannes Berg's avatar
Johannes Berg committed
1402
1403
		if (!nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX])
			return -EINVAL;
1404

Johannes Berg's avatar
Johannes Berg committed
1405
1406
1407
		ifidx = nla_get_u32(nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX]);
		if (!ifidx)
			return -EINVAL;
1408
1409
	}

Johannes Berg's avatar
Johannes Berg committed
1410
1411
1412
1413
1414
1415
1416
	rtnl_lock();

	netdev = __dev_get_by_index(&init_net, ifidx);
	if (!netdev) {
		err = -ENODEV;
		goto out_rtnl;
	}
1417

Johannes Berg's avatar
Johannes Berg committed
1418
1419
1420
	dev = cfg80211_get_dev_from_ifindex(ifidx);
	if (IS_ERR(dev)) {
		err = PTR_ERR(dev);
Johannes Berg's avatar
Johannes Berg committed
1421
		goto out_rtnl;
Johannes Berg's avatar
Johannes Berg committed
1422
1423
1424
	}

	if (!dev->ops->dump_station) {
1425
		err = -EOPNOTSUPP;
Johannes Berg's avatar
Johannes Berg committed
1426
1427
1428
1429
1430
1431
1432
1433
1434
		goto out_err;
	}

	while (1) {
		err = dev->ops->dump_station(&dev->wiphy, netdev, sta_idx,
					     mac_addr, &sinfo);
		if (err == -ENOENT)
			break;
		if (err)
Johannes Berg's avatar
Johannes Berg committed
1435
			goto out_err;
Johannes Berg's avatar
Johannes Berg committed
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452

		if (nl80211_send_station(skb,
				NETLINK_CB(cb->skb).pid,
				cb->nlh->nlmsg_seq, NLM_F_MULTI,
				netdev, mac_addr,
				&sinfo) < 0)
			goto out;

		sta_idx++;
	}


 out:
	cb->args[1] = sta_idx;
	err = skb->len;
 out_err:
	cfg80211_put_dev(dev);
Johannes Berg's avatar
Johannes Berg committed
1453
1454
 out_rtnl:
	rtnl_unlock();
Johannes Berg's avatar
Johannes Berg committed
1455
1456

	return err;
1457
}
1458

1459
1460
static int nl80211_get_station(struct sk_buff *skb, struct genl_info *info)
{
1461
1462
1463
	struct cfg80211_registered_device *drv;
	int err;
	struct net_device *dev;
1464
	struct station_info sinfo;
1465
1466
1467
	struct sk_buff *msg;
	u8 *mac_addr = NULL;

1468
	memset(&sinfo, 0, sizeof(sinfo));
1469
1470
1471
1472
1473
1474

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

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

Johannes Berg's avatar
Johannes Berg committed
1475
1476
	rtnl_lock();

Johannes Berg's avatar
Johannes Berg committed
1477
	err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
1478
	if (err)
Johannes Berg's avatar
Johannes Berg committed
1479
		goto out_rtnl;
1480
1481
1482
1483
1484
1485

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

1486
1487
1488
1489
	err = drv->ops->get_station(&drv->wiphy, dev, mac_addr, &sinfo);
	if (err)
		goto out;

1490
1491
1492
1493
1494
	msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
	if (!msg)
		goto out;

	if (nl80211_send_station(msg, info->snd_pid, info->snd_seq, 0,
1495
				 dev, mac_addr, &sinfo) < 0)
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
		goto out_free;

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

 out_free:
	nlmsg_free(msg);
 out:
	cfg80211_put_dev(drv);
	dev_put(dev);
Johannes Berg's avatar
Johannes Berg committed
1506
1507
1508
 out_rtnl:
	rtnl_unlock();

1509
	return err;