nl80211.c 91.1 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 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_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 74 75 76 77 78 79

	[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 },
80 81 82 83 84 85 86

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

99 100 101
	[NL80211_ATTR_REG_ALPHA2] = { .type = NLA_STRING, .len = 2 },
	[NL80211_ATTR_REG_RULES] = { .type = NLA_NESTED },

102 103 104
	[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 },
105 106
	[NL80211_ATTR_BSS_BASIC_RATES] = { .type = NLA_BINARY,
					   .len = NL80211_MAX_SUPP_RATES },
107

108 109
	[NL80211_ATTR_MESH_PARAMS] = { .type = NLA_NESTED },

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

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

	[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
123
	[NL80211_ATTR_FREQ_FIXED] = { .type = NLA_FLAG },
124
	[NL80211_ATTR_TIMED_OUT] = { .type = NLA_FLAG },
125
	[NL80211_ATTR_USE_MFP] = { .type = NLA_U32 },
126 127 128
	[NL80211_ATTR_STA_FLAGS2] = {
		.len = sizeof(struct nl80211_sta_flag_update),
	},
129 130
};

131 132 133 134 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
/* 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;
}

161 162 163 164 165 166 167 168
/* 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);
}

169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192
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;
}

193 194 195 196 197 198
/* 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;
199 200 201
	struct nlattr *nl_bands, *nl_band;
	struct nlattr *nl_freqs, *nl_freq;
	struct nlattr *nl_rates, *nl_rate;
202
	struct nlattr *nl_modes;
203
	struct nlattr *nl_cmds;
204 205 206 207
	enum ieee80211_band band;
	struct ieee80211_channel *chan;
	struct ieee80211_rate *rate;
	int i;
208
	u16 ifmodes = dev->wiphy.interface_modes;
209 210 211 212 213

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

214
	NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, dev->wiphy_idx);
215
	NLA_PUT_STRING(msg, NL80211_ATTR_WIPHY_NAME, wiphy_name(&dev->wiphy));
216 217 218 219 220 221 222 223 224 225

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

226 227
	NLA_PUT_U8(msg, NL80211_ATTR_MAX_NUM_SCAN_SSIDS,
		   dev->wiphy.max_scan_ssids);
228 229
	NLA_PUT_U16(msg, NL80211_ATTR_MAX_SCAN_IE_LEN,
		    dev->wiphy.max_scan_ie_len);
230

231 232 233 234
	NLA_PUT(msg, NL80211_ATTR_CIPHER_SUITES,
		sizeof(u32) * dev->wiphy.n_cipher_suites,
		dev->wiphy.cipher_suites);

235 236 237 238 239 240 241 242 243 244 245 246 247 248
	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);

249 250 251 252 253 254 255 256 257 258 259 260
	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;

261 262 263 264 265 266 267 268 269 270 271 272 273
		/* 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);
		}

274 275 276 277 278 279 280 281 282 283 284
		/* 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];
285 286 287

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

289 290 291 292 293 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
			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);

320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340
	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);
341 342 343 344
	CMD(auth, AUTHENTICATE);
	CMD(assoc, ASSOCIATE);
	CMD(deauth, DEAUTHENTICATE);
	CMD(disassoc, DISASSOCIATE);
Johannes Berg's avatar
Johannes Berg committed
345
	CMD(join_ibss, JOIN_IBSS);
346 347 348 349

#undef CMD
	nla_nest_end(msg, nl_cmds);

350 351 352
	return genlmsg_end(msg, hdr);

 nla_put_failure:
353 354
	genlmsg_cancel(msg, hdr);
	return -EMSGSIZE;
355 356 357 358 359 360 361 362
}

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;

363
	mutex_lock(&cfg80211_mutex);
364
	list_for_each_entry(dev, &cfg80211_drv_list, list) {
365
		if (++idx <= start)
366 367 368
			continue;
		if (nl80211_send_wiphy(skb, NETLINK_CB(cb->skb).pid,
				       cb->nlh->nlmsg_seq, NLM_F_MULTI,
369 370
				       dev) < 0) {
			idx--;
371
			break;
372
		}
373
	}
374
	mutex_unlock(&cfg80211_mutex);
375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407

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

408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432
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;
}

433 434 435
static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
{
	struct cfg80211_registered_device *rdev;
436 437
	int result = 0, rem_txq_params = 0;
	struct nlattr *nl_txq_params;
438 439 440
	u32 changed;
	u8 retry_short = 0, retry_long = 0;
	u32 frag_threshold = 0, rts_threshold = 0;
441

442
	rtnl_lock();
443

444 445 446 447 448 449 450 451 452 453 454
	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])
455 456
		result = cfg80211_dev_rename(
			rdev, nla_data(info->attrs[NL80211_ATTR_WIPHY_NAME]));
457 458 459 460 461

	mutex_unlock(&cfg80211_mutex);

	if (result)
		goto bad_res;
462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488

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

490
	if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
Sujith's avatar
Sujith committed
491
		enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT;
492
		struct ieee80211_channel *chan;
493
		struct ieee80211_sta_ht_cap *ht_cap;
494 495 496 497 498 499 500
		u32 freq, sec_freq;

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

501 502
		result = -EINVAL;

Sujith's avatar
Sujith committed
503 504 505 506 507 508 509
		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)
510 511 512 513 514
				goto bad_res;
		}

		freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]);
		chan = ieee80211_get_channel(&rdev->wiphy, freq);
515 516 517

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

Sujith's avatar
Sujith committed
520
		if (channel_type == NL80211_CHAN_HT40MINUS)
521
			sec_freq = freq - 20;
Sujith's avatar
Sujith committed
522
		else if (channel_type == NL80211_CHAN_HT40PLUS)
523 524 525 526
			sec_freq = freq + 20;
		else
			sec_freq = 0;

527 528 529
		ht_cap = &rdev->wiphy.bands[chan->band]->ht_cap;

		/* no HT capabilities */
Sujith's avatar
Sujith committed
530
		if (channel_type != NL80211_CHAN_NO_HT &&
531 532 533
		    !ht_cap->ht_supported)
			goto bad_res;

534 535
		if (sec_freq) {
			struct ieee80211_channel *schan;
536 537 538 539 540 541

			/* 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;

542
			schan = ieee80211_get_channel(&rdev->wiphy, sec_freq);
543 544 545

			/* Secondary channel not allowed */
			if (!schan || schan->flags & IEEE80211_CHAN_DISABLED)
546 547 548 549
				goto bad_res;
		}

		result = rdev->ops->set_channel(&rdev->wiphy, chan,
Sujith's avatar
Sujith committed
550
						channel_type);
551 552 553 554
		if (result)
			goto bad_res;
	}

555 556 557 558 559 560 561 562 563 564 565 566 567 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
	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;
		}
	}
633

634
 bad_res:
635 636 637
	mutex_unlock(&rdev->mtx);
 unlock:
	rtnl_unlock();
638 639 640 641 642
	return result;
}


static int nl80211_send_iface(struct sk_buff *msg, u32 pid, u32 seq, int flags,
643
			      struct cfg80211_registered_device *rdev,
644 645 646 647 648 649 650 651 652
			      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);
653
	NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
654
	NLA_PUT_STRING(msg, NL80211_ATTR_IFNAME, dev->name);
Johannes Berg's avatar
Johannes Berg committed
655
	NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, dev->ieee80211_ptr->iftype);
656 657 658
	return genlmsg_end(msg, hdr);

 nla_put_failure:
659 660
	genlmsg_cancel(msg, hdr);
	return -EMSGSIZE;
661 662 663 664 665 666 667 668 669 670 671
}

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;

672
	mutex_lock(&cfg80211_mutex);
673
	list_for_each_entry(dev, &cfg80211_drv_list, list) {
Johannes Berg's avatar
Johannes Berg committed
674 675
		if (wp_idx < wp_start) {
			wp_idx++;
676
			continue;
Johannes Berg's avatar
Johannes Berg committed
677
		}
678 679 680 681
		if_idx = 0;

		mutex_lock(&dev->devlist_mtx);
		list_for_each_entry(wdev, &dev->netdev_list, list) {
Johannes Berg's avatar
Johannes Berg committed
682 683
			if (if_idx < if_start) {
				if_idx++;
684
				continue;
Johannes Berg's avatar
Johannes Berg committed
685
			}
686 687
			if (nl80211_send_iface(skb, NETLINK_CB(cb->skb).pid,
					       cb->nlh->nlmsg_seq, NLM_F_MULTI,
688
					       dev, wdev->netdev) < 0) {
Johannes Berg's avatar
Johannes Berg committed
689 690 691 692
				mutex_unlock(&dev->devlist_mtx);
				goto out;
			}
			if_idx++;
693 694
		}
		mutex_unlock(&dev->devlist_mtx);
Johannes Berg's avatar
Johannes Berg committed
695 696

		wp_idx++;
697
	}
Johannes Berg's avatar
Johannes Berg committed
698
 out:
699
	mutex_unlock(&cfg80211_mutex);
700 701 702 703 704 705 706 707 708 709 710 711 712 713

	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
714
	err = get_drv_dev_by_info_ifindex(info->attrs, &dev, &netdev);
715 716 717 718 719 720 721
	if (err)
		return err;

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

722 723
	if (nl80211_send_iface(msg, info->snd_pid, info->snd_seq, 0,
			       dev, netdev) < 0)
724 725 726 727 728 729 730 731 732 733 734 735 736 737 738
		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;
}

739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767
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;
}

768 769 770
static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info)
{
	struct cfg80211_registered_device *drv;
771
	struct vif_params params;
772
	int err, ifindex;
Johannes Berg's avatar
Johannes Berg committed
773
	enum nl80211_iftype otype, ntype;
774
	struct net_device *dev;
775
	u32 _flags, *flags = NULL;
776
	bool change = false;
777

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

Johannes Berg's avatar
Johannes Berg committed
780 781
	rtnl_lock();

Johannes Berg's avatar
Johannes Berg committed
782
	err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
783
	if (err)
Johannes Berg's avatar
Johannes Berg committed
784 785
		goto unlock_rtnl;

786
	ifindex = dev->ifindex;
Johannes Berg's avatar
Johannes Berg committed
787
	otype = ntype = dev->ieee80211_ptr->iftype;
788 789
	dev_put(dev);

790
	if (info->attrs[NL80211_ATTR_IFTYPE]) {
791
		ntype = nla_get_u32(info->attrs[NL80211_ATTR_IFTYPE]);
Johannes Berg's avatar
Johannes Berg committed
792
		if (otype != ntype)
793
			change = true;
Johannes Berg's avatar
Johannes Berg committed
794
		if (ntype > NL80211_IFTYPE_MAX) {
795
			err = -EINVAL;
796
			goto unlock;
797
		}
798 799
	}

800
	if (!drv->ops->change_virtual_intf ||
Johannes Berg's avatar
Johannes Berg committed
801
	    !(drv->wiphy.interface_modes & (1 << ntype))) {
802 803 804 805
		err = -EOPNOTSUPP;
		goto unlock;
	}

806
	if (info->attrs[NL80211_ATTR_MESH_ID]) {
Johannes Berg's avatar
Johannes Berg committed
807
		if (ntype != NL80211_IFTYPE_MESH_POINT) {
808 809 810
			err = -EINVAL;
			goto unlock;
		}
811 812
		params.mesh_id = nla_data(info->attrs[NL80211_ATTR_MESH_ID]);
		params.mesh_id_len = nla_len(info->attrs[NL80211_ATTR_MESH_ID]);
813
		change = true;
814 815
	}

816
	if (info->attrs[NL80211_ATTR_MNTR_FLAGS]) {
Johannes Berg's avatar
Johannes Berg committed
817
		if (ntype != NL80211_IFTYPE_MONITOR) {
818 819 820 821 822
			err = -EINVAL;
			goto unlock;
		}
		err = parse_monitor_flags(info->attrs[NL80211_ATTR_MNTR_FLAGS],
					  &_flags);
823 824 825 826 827
		if (err)
			goto unlock;

		flags = &_flags;
		change = true;
828
	}
Johannes Berg's avatar
Johannes Berg committed
829

830 831
	if (change)
		err = drv->ops->change_virtual_intf(&drv->wiphy, ifindex,
Johannes Berg's avatar
Johannes Berg committed
832
						    ntype, flags, &params);
833 834
	else
		err = 0;
Johannes Berg's avatar
Johannes Berg committed
835 836

	dev = __dev_get_by_index(&init_net, ifindex);
Johannes Berg's avatar
Johannes Berg committed
837 838 839 840
	WARN_ON(!dev || (!err && dev->ieee80211_ptr->iftype != ntype));

	if (dev && !err && (ntype != otype)) {
		if (otype == NL80211_IFTYPE_ADHOC)
841
			cfg80211_clear_ibss(dev, false);
Johannes Berg's avatar
Johannes Berg committed
842
	}
Johannes Berg's avatar
Johannes Berg committed
843

844 845
 unlock:
	cfg80211_put_dev(drv);
Johannes Berg's avatar
Johannes Berg committed
846 847
 unlock_rtnl:
	rtnl_unlock();
848 849 850 851 852 853
	return err;
}

static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info)
{
	struct cfg80211_registered_device *drv;
854
	struct vif_params params;
855 856
	int err;
	enum nl80211_iftype type = NL80211_IFTYPE_UNSPECIFIED;
857
	u32 flags;
858

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

861 862 863 864 865 866 867 868 869
	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
870 871
	rtnl_lock();

872
	drv = cfg80211_get_dev_from_info(info);
Johannes Berg's avatar
Johannes Berg committed
873 874 875 876
	if (IS_ERR(drv)) {
		err = PTR_ERR(drv);
		goto unlock_rtnl;
	}
877

878 879
	if (!drv->ops->add_virtual_intf ||
	    !(drv->wiphy.interface_modes & (1 << type))) {
880 881 882 883
		err = -EOPNOTSUPP;
		goto unlock;
	}

884 885 886 887 888 889
	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]);
	}

890 891 892
	err = parse_monitor_flags(type == NL80211_IFTYPE_MONITOR ?
				  info->attrs[NL80211_ATTR_MNTR_FLAGS] : NULL,
				  &flags);
893
	err = drv->ops->add_virtual_intf(&drv->wiphy,
894
		nla_data(info->attrs[NL80211_ATTR_IFNAME]),
895 896
		type, err ? NULL : &flags, &params);

897 898
 unlock:
	cfg80211_put_dev(drv);
Johannes Berg's avatar
Johannes Berg committed
899 900
 unlock_rtnl:
	rtnl_unlock();
901 902 903 904 905 906 907 908 909
	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
910 911
	rtnl_lock();

Johannes Berg's avatar
Johannes Berg committed
912
	err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
913
	if (err)
Johannes Berg's avatar
Johannes Berg committed
914
		goto unlock_rtnl;
915 916 917 918 919 920 921 922 923 924 925 926
	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
927 928
 unlock_rtnl:
	rtnl_unlock();
929 930 931
	return err;
}

932 933 934 935 936 937 938 939 940 941 942 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
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]);

974
	if (key_idx > 5)
975 976 977 978 979
		return -EINVAL;

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

Johannes Berg's avatar
Johannes Berg committed
980 981
	rtnl_lock();

Johannes Berg's avatar
Johannes Berg committed
982
	err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
983
	if (err)
Johannes Berg's avatar
Johannes Berg committed
984
		goto unlock_rtnl;
985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 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

	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
1031 1032 1033
 unlock_rtnl:
	rtnl_unlock();

1034 1035 1036 1037 1038 1039 1040 1041 1042
	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;
1043 1044
	int (*func)(struct wiphy *wiphy, struct net_device *netdev,
		    u8 key_index);
1045 1046 1047 1048 1049 1050

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

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

1051 1052 1053 1054
	if (info->attrs[NL80211_ATTR_KEY_DEFAULT_MGMT]) {
		if (key_idx < 4 || key_idx > 5)
			return -EINVAL;
	} else if (key_idx > 3)
1055 1056 1057
		return -EINVAL;

	/* currently only support setting default key */
1058 1059
	if (!info->attrs[NL80211_ATTR_KEY_DEFAULT] &&
	    !info->attrs[NL80211_ATTR_KEY_DEFAULT_MGMT])
1060 1061
		return -EINVAL;

Johannes Berg's avatar
Johannes Berg committed
1062 1063
	rtnl_lock();

Johannes Berg's avatar
Johannes Berg committed
1064
	err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
1065
	if (err)
Johannes Berg's avatar
Johannes Berg committed
1066
		goto unlock_rtnl;
1067

1068 1069 1070 1071 1072 1073
	if (info->attrs[NL80211_ATTR_KEY_DEFAULT])
		func = drv->ops->set_default_key;
	else
		func = drv->ops->set_default_mgmt_key;

	if (!func) {
1074 1075 1076 1077
		err = -EOPNOTSUPP;
		goto out;
	}

1078
	err = func(&drv->wiphy, dev, key_idx);
1079 1080 1081 1082 1083 1084 1085 1086
#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
1087 1088 1089 1090

 out:
	cfg80211_put_dev(drv);
	dev_put(dev);
Johannes Berg's avatar
Johannes Berg committed
1091 1092 1093 1094

 unlock_rtnl:
	rtnl_unlock();

1095 1096 1097 1098 1099 1100
	return err;
}

static int nl80211_new_key(struct sk_buff *skb, struct genl_info *info)
{
	struct cfg80211_registered_device *drv;
1101
	int err, i;
1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124
	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]);

1125
	if (cfg80211_validate_key_settings(&params, key_idx, mac_addr))
1126 1127
		return -EINVAL;

Johannes Berg's avatar
Johannes Berg committed
1128 1129
	rtnl_lock();

Johannes Berg's avatar
Johannes Berg committed
1130
	err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
1131
	if (err)
Johannes Berg's avatar
Johannes Berg committed
1132
		goto unlock_rtnl;
1133

1134 1135 1136 1137 1138 1139 1140 1141
	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;
	}

1142 1143 1144 1145 1146 1147 1148 1149 1150 1151
	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
1152 1153 1154
 unlock_rtnl:
	rtnl_unlock();

1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168
	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]);

1169
	if (key_idx > 5)
1170 1171 1172 1173 1174
		return -EINVAL;

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

Johannes Berg's avatar
Johannes Berg committed
1175 1176
	rtnl_lock();

Johannes Berg's avatar
Johannes Berg committed
1177
	err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
1178
	if (err)
Johannes Berg's avatar
Johannes Berg committed
1179
		goto unlock_rtnl;
1180 1181 1182 1183 1184 1185 1186 1187

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

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

1188 1189 1190 1191 1192 1193 1194 1195 1196
#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

1197 1198 1199
 out:
	cfg80211_put_dev(drv);
	dev_put(dev);
Johannes Berg's avatar
Johannes Berg committed
1200 1201 1202 1203

 unlock_rtnl:
	rtnl_unlock();

1204 1205 1206
	return err;
}