wext.c 24.8 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
/*
 * Copyright 2002-2005, Instant802 Networks, Inc.
 * Copyright 2005-2006, Devicescape Software, Inc.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 */

#include <linux/module.h>
#include <linux/init.h>
#include <linux/netdevice.h>
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/skbuff.h>
#include <linux/etherdevice.h>
#include <linux/if_arp.h>
#include <linux/wireless.h>
#include <net/iw_handler.h>
#include <asm/uaccess.h>

#include <net/mac80211.h>
#include "ieee80211_i.h"
J
Johannes Berg 已提交
24 25
#include "led.h"
#include "rate.h"
26 27 28
#include "wpa.h"
#include "aes_ccm.h"

J
Johannes Berg 已提交
29

30
static int ieee80211_set_encryption(struct ieee80211_sub_if_data *sdata, u8 *sta_addr,
J
Johannes Berg 已提交
31 32 33
				    int idx, int alg, int remove,
				    int set_tx_key, const u8 *_key,
				    size_t key_len)
34
{
35
	struct ieee80211_local *local = sdata->local;
36
	struct sta_info *sta;
J
Johannes Berg 已提交
37
	struct ieee80211_key *key;
38
	int err;
39

40 41 42 43 44 45 46 47
	if (alg == ALG_AES_CMAC) {
		if (idx < NUM_DEFAULT_KEYS ||
		    idx >= NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS) {
			printk(KERN_DEBUG "%s: set_encrypt - invalid idx=%d "
			       "(BIP)\n", sdata->dev->name, idx);
			return -EINVAL;
		}
	} else if (idx < 0 || idx >= NUM_DEFAULT_KEYS) {
48
		printk(KERN_DEBUG "%s: set_encrypt - invalid idx=%d\n",
49
		       sdata->dev->name, idx);
50 51 52
		return -EINVAL;
	}

53
	if (remove) {
54 55 56 57
		rcu_read_lock();

		err = 0;

58 59 60 61
		if (is_broadcast_ether_addr(sta_addr)) {
			key = sdata->keys[idx];
		} else {
			sta = sta_info_get(local, sta_addr);
62 63 64 65
			if (!sta) {
				err = -ENOENT;
				goto out_unlock;
			}
66
			key = sta->key;
67 68
		}

69
		ieee80211_key_free(key);
70 71 72 73 74
	} else {
		key = ieee80211_key_alloc(alg, idx, key_len, _key);
		if (!key)
			return -ENOMEM;

75
		sta = NULL;
76 77 78
		err = 0;

		rcu_read_lock();
79

80 81 82 83 84 85 86 87 88
		if (!is_broadcast_ether_addr(sta_addr)) {
			set_tx_key = 0;
			/*
			 * According to the standard, the key index of a
			 * pairwise key must be zero. However, some AP are
			 * broken when it comes to WEP key indices, so we
			 * work around this.
			 */
			if (idx != 0 && alg != ALG_WEP) {
89
				ieee80211_key_free(key);
90 91
				err = -EINVAL;
				goto out_unlock;
92
			}
93

94 95
			sta = sta_info_get(local, sta_addr);
			if (!sta) {
96
				ieee80211_key_free(key);
97 98
				err = -ENOENT;
				goto out_unlock;
99
			}
100 101
		}

102 103 104 105 106 107 108
		if (alg == ALG_WEP &&
			key_len != LEN_WEP40 && key_len != LEN_WEP104) {
			ieee80211_key_free(key);
			err = -EINVAL;
			goto out_unlock;
		}

109
		ieee80211_key_link(key, sdata, sta);
110

111 112
		if (set_tx_key || (!sta && !sdata->default_key && key))
			ieee80211_set_default_key(sdata, idx);
113 114 115
		if (alg == ALG_AES_CMAC &&
		    (set_tx_key || (!sta && !sdata->default_mgmt_key && key)))
			ieee80211_set_default_mgmt_key(sdata, idx);
116
	}
117

118 119 120 121
 out_unlock:
	rcu_read_unlock();

	return err;
122 123 124 125 126 127 128 129
}

static int ieee80211_ioctl_siwgenie(struct net_device *dev,
				    struct iw_request_info *info,
				    struct iw_point *data, char *extra)
{
	struct ieee80211_sub_if_data *sdata;

130 131
	sdata = IEEE80211_DEV_TO_SUB_IF(dev);

132
	if (sdata->vif.type == NL80211_IFTYPE_STATION) {
133
		int ret = ieee80211_sta_set_extra_ie(sdata, extra, data->length);
134 135
		if (ret)
			return ret;
136
		sdata->u.mgd.flags &= ~IEEE80211_STA_AUTO_BSSID_SEL;
137
		sdata->u.mgd.flags &= ~IEEE80211_STA_EXT_SME;
138
		ieee80211_sta_req_auth(sdata);
139 140 141 142 143 144 145 146 147 148 149 150
		return 0;
	}

	return -EOPNOTSUPP;
}

static int ieee80211_ioctl_siwfreq(struct net_device *dev,
				   struct iw_request_info *info,
				   struct iw_freq *freq, char *extra)
{
	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);

151
	if (sdata->vif.type == NL80211_IFTYPE_ADHOC)
152
		return cfg80211_ibss_wext_siwfreq(dev, info, freq, extra);
153 154
	else if (sdata->vif.type == NL80211_IFTYPE_STATION)
		sdata->u.mgd.flags &= ~IEEE80211_STA_AUTO_CHANNEL_SEL;
155 156 157 158

	/* freq->e == 0: freq->m = channel; otherwise freq = m * 10^e */
	if (freq->e == 0) {
		if (freq->m < 0) {
159
			if (sdata->vif.type == NL80211_IFTYPE_STATION)
160
				sdata->u.mgd.flags |=
161
					IEEE80211_STA_AUTO_CHANNEL_SEL;
162 163
			return 0;
		} else
164
			return ieee80211_set_freq(sdata,
165
				ieee80211_channel_to_frequency(freq->m));
166 167 168 169 170
	} else {
		int i, div = 1000000;
		for (i = 0; i < freq->e; i++)
			div /= 10;
		if (div > 0)
171
			return ieee80211_set_freq(sdata, freq->m / div);
172 173 174 175 176 177 178 179 180 181 182
		else
			return -EINVAL;
	}
}


static int ieee80211_ioctl_giwfreq(struct net_device *dev,
				   struct iw_request_info *info,
				   struct iw_freq *freq, char *extra)
{
	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
183 184 185 186
	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);

	if (sdata->vif.type == NL80211_IFTYPE_ADHOC)
		return cfg80211_ibss_wext_giwfreq(dev, info, freq, extra);
187

188
	freq->m = local->oper_channel->center_freq;
189 190 191 192 193 194 195 196 197 198
	freq->e = 6;

	return 0;
}


static int ieee80211_ioctl_siwessid(struct net_device *dev,
				    struct iw_request_info *info,
				    struct iw_point *data, char *ssid)
{
199
	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
200
	size_t len = data->length;
201
	int ret;
202

203 204 205
	if (sdata->vif.type == NL80211_IFTYPE_ADHOC)
		return cfg80211_ibss_wext_siwessid(dev, info, data, ssid);

206 207 208 209
	/* iwconfig uses nul termination in SSID.. */
	if (len > 0 && ssid[len - 1] == '\0')
		len--;

210
	if (sdata->vif.type == NL80211_IFTYPE_STATION) {
211
		if (data->flags)
212
			sdata->u.mgd.flags &= ~IEEE80211_STA_AUTO_SSID_SEL;
213
		else
214 215
			sdata->u.mgd.flags |= IEEE80211_STA_AUTO_SSID_SEL;

216
		ret = ieee80211_sta_set_ssid(sdata, ssid, len);
217 218
		if (ret)
			return ret;
219

220
		sdata->u.mgd.flags &= ~IEEE80211_STA_EXT_SME;
221
		ieee80211_sta_req_auth(sdata);
222
		return 0;
223
	}
224 225 226 227 228 229 230 231 232 233 234

	return -EOPNOTSUPP;
}


static int ieee80211_ioctl_giwessid(struct net_device *dev,
				    struct iw_request_info *info,
				    struct iw_point *data, char *ssid)
{
	size_t len;
	struct ieee80211_sub_if_data *sdata;
235

236
	sdata = IEEE80211_DEV_TO_SUB_IF(dev);
237 238 239 240

	if (sdata->vif.type == NL80211_IFTYPE_ADHOC)
		return cfg80211_ibss_wext_giwessid(dev, info, data, ssid);

241
	if (sdata->vif.type == NL80211_IFTYPE_STATION) {
242
		int res = ieee80211_sta_get_ssid(sdata, ssid, &len);
243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258
		if (res == 0) {
			data->length = len;
			data->flags = 1;
		} else
			data->flags = 0;
		return res;
	}

	return -EOPNOTSUPP;
}


static int ieee80211_ioctl_siwap(struct net_device *dev,
				 struct iw_request_info *info,
				 struct sockaddr *ap_addr, char *extra)
{
259 260 261 262
	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);

	if (sdata->vif.type == NL80211_IFTYPE_ADHOC)
		return cfg80211_ibss_wext_siwap(dev, info, ap_addr, extra);
263

264
	if (sdata->vif.type == NL80211_IFTYPE_STATION) {
265
		int ret;
266

267
		if (is_zero_ether_addr((u8 *) &ap_addr->sa_data))
268
			sdata->u.mgd.flags |= IEEE80211_STA_AUTO_BSSID_SEL |
269 270
				IEEE80211_STA_AUTO_CHANNEL_SEL;
		else if (is_broadcast_ether_addr((u8 *) &ap_addr->sa_data))
271
			sdata->u.mgd.flags |= IEEE80211_STA_AUTO_BSSID_SEL;
272
		else
273
			sdata->u.mgd.flags &= ~IEEE80211_STA_AUTO_BSSID_SEL;
274
		ret = ieee80211_sta_set_bssid(sdata, (u8 *) &ap_addr->sa_data);
275 276
		if (ret)
			return ret;
277
		sdata->u.mgd.flags &= ~IEEE80211_STA_EXT_SME;
278
		ieee80211_sta_req_auth(sdata);
279
		return 0;
280
	} else if (sdata->vif.type == NL80211_IFTYPE_WDS) {
281 282 283 284 285 286 287 288 289 290 291 292 293 294
		/*
		 * If it is necessary to update the WDS peer address
		 * while the interface is running, then we need to do
		 * more work here, namely if it is running we need to
		 * add a new and remove the old STA entry, this is
		 * normally handled by _open() and _stop().
		 */
		if (netif_running(dev))
			return -EBUSY;

		memcpy(&sdata->u.wds.remote_addr, (u8 *) &ap_addr->sa_data,
		       ETH_ALEN);

		return 0;
295 296 297 298 299 300 301 302 303 304
	}

	return -EOPNOTSUPP;
}


static int ieee80211_ioctl_giwap(struct net_device *dev,
				 struct iw_request_info *info,
				 struct sockaddr *ap_addr, char *extra)
{
305 306 307 308
	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);

	if (sdata->vif.type == NL80211_IFTYPE_ADHOC)
		return cfg80211_ibss_wext_giwap(dev, info, ap_addr, extra);
309

310 311
	if (sdata->vif.type == NL80211_IFTYPE_STATION) {
		if (sdata->u.mgd.state == IEEE80211_STA_MLME_ASSOCIATED) {
312
			ap_addr->sa_family = ARPHRD_ETHER;
313 314
			memcpy(&ap_addr->sa_data, sdata->u.mgd.bssid, ETH_ALEN);
		} else
315
			memset(&ap_addr->sa_data, 0, ETH_ALEN);
316
		return 0;
317
	} else if (sdata->vif.type == NL80211_IFTYPE_WDS) {
318 319 320 321 322 323 324 325 326
		ap_addr->sa_family = ARPHRD_ETHER;
		memcpy(&ap_addr->sa_data, sdata->u.wds.remote_addr, ETH_ALEN);
		return 0;
	}

	return -EOPNOTSUPP;
}


327 328 329 330 331
static int ieee80211_ioctl_siwrate(struct net_device *dev,
				  struct iw_request_info *info,
				  struct iw_param *rate, char *extra)
{
	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
332
	int i, err = -EINVAL;
333 334
	u32 target_rate = rate->value / 100000;
	struct ieee80211_sub_if_data *sdata;
335
	struct ieee80211_supported_band *sband;
336 337

	sdata = IEEE80211_DEV_TO_SUB_IF(dev);
338 339 340

	sband = local->hw.wiphy->bands[local->hw.conf.channel->band];

341 342 343
	/* target_rate = -1, rate->fixed = 0 means auto only, so use all rates
	 * target_rate = X, rate->fixed = 1 means only rate X
	 * target_rate = X, rate->fixed = 0 means all rates <= X */
344 345
	sdata->max_ratectrl_rateidx = -1;
	sdata->force_unicast_rateidx = -1;
346 347
	if (rate->value < 0)
		return 0;
348 349 350 351

	for (i=0; i< sband->n_bitrates; i++) {
		struct ieee80211_rate *brate = &sband->bitrates[i];
		int this_rate = brate->bitrate;
352 353

		if (target_rate == this_rate) {
354
			sdata->max_ratectrl_rateidx = i;
355
			if (rate->fixed)
356
				sdata->force_unicast_rateidx = i;
357 358
			err = 0;
			break;
359 360
		}
	}
361
	return err;
362 363
}

364 365 366 367 368 369 370
static int ieee80211_ioctl_giwrate(struct net_device *dev,
				  struct iw_request_info *info,
				  struct iw_param *rate, char *extra)
{
	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
	struct sta_info *sta;
	struct ieee80211_sub_if_data *sdata;
371
	struct ieee80211_supported_band *sband;
372 373

	sdata = IEEE80211_DEV_TO_SUB_IF(dev);
374

375
	if (sdata->vif.type != NL80211_IFTYPE_STATION)
376
		return -EOPNOTSUPP;
377 378 379

	sband = local->hw.wiphy->bands[local->hw.conf.channel->band];

380 381
	rcu_read_lock();

382
	sta = sta_info_get(local, sdata->u.mgd.bssid);
383

384 385
	if (sta && !(sta->last_tx_rate.flags & IEEE80211_TX_RC_MCS))
		rate->value = sband->bitrates[sta->last_tx_rate.idx].bitrate;
386 387
	else
		rate->value = 0;
388 389 390 391 392 393

	rcu_read_unlock();

	if (!sta)
		return -ENODEV;

394
	rate->value *= 100000;
395

396 397 398
	return 0;
}

399 400 401 402 403
static int ieee80211_ioctl_siwtxpower(struct net_device *dev,
				      struct iw_request_info *info,
				      union iwreq_data *data, char *extra)
{
	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
404
	struct ieee80211_channel* chan = local->hw.conf.channel;
405
	bool reconf = false;
406
	u32 reconf_flags = 0;
407
	int new_power_level;
408 409 410 411 412

	if ((data->txpower.flags & IW_TXPOW_TYPE) != IW_TXPOW_DBM)
		return -EINVAL;
	if (data->txpower.flags & IW_TXPOW_RANGE)
		return -EINVAL;
413 414
	if (!chan)
		return -EINVAL;
415

416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440
	/* only change when not disabling */
	if (!data->txpower.disabled) {
		if (data->txpower.fixed) {
			if (data->txpower.value < 0)
				return -EINVAL;
			new_power_level = data->txpower.value;
			/*
			 * Debatable, but we cannot do a fixed power
			 * level above the regulatory constraint.
			 * Use "iwconfig wlan0 txpower 15dBm" instead.
			 */
			if (new_power_level > chan->max_power)
				return -EINVAL;
		} else {
			/*
			 * Automatic power level setting, max being the value
			 * passed in from userland.
			 */
			if (data->txpower.value < 0)
				new_power_level = -1;
			else
				new_power_level = data->txpower.value;
		}

		reconf = true;
441

442 443 444 445 446 447
		/*
		 * ieee80211_hw_config() will limit to the channel's
		 * max power and possibly power constraint from AP.
		 */
		local->user_power_level = new_power_level;
	}
448

449 450
	if (local->hw.conf.radio_enabled != !(data->txpower.disabled)) {
		local->hw.conf.radio_enabled = !(data->txpower.disabled);
451
		reconf_flags |= IEEE80211_CONF_CHANGE_RADIO_ENABLED;
I
Ivo van Doorn 已提交
452
		ieee80211_led_radio(local, local->hw.conf.radio_enabled);
453
	}
454

455
	if (reconf || reconf_flags)
456
		ieee80211_hw_config(local, reconf_flags);
457 458 459 460

	return 0;
}

461 462 463 464 465 466 467 468 469 470 471 472 473 474
static int ieee80211_ioctl_giwtxpower(struct net_device *dev,
				   struct iw_request_info *info,
				   union iwreq_data *data, char *extra)
{
	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);

	data->txpower.fixed = 1;
	data->txpower.disabled = !(local->hw.conf.radio_enabled);
	data->txpower.value = local->hw.conf.power_level;
	data->txpower.flags = IW_TXPOW_DBM;

	return 0;
}

475 476 477 478 479 480 481
static int ieee80211_ioctl_siwencode(struct net_device *dev,
				     struct iw_request_info *info,
				     struct iw_point *erq, char *keybuf)
{
	struct ieee80211_sub_if_data *sdata;
	int idx, i, alg = ALG_WEP;
	u8 bcaddr[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
482
	int remove = 0, ret;
483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500

	sdata = IEEE80211_DEV_TO_SUB_IF(dev);

	idx = erq->flags & IW_ENCODE_INDEX;
	if (idx == 0) {
		if (sdata->default_key)
			for (i = 0; i < NUM_DEFAULT_KEYS; i++) {
				if (sdata->default_key == sdata->keys[i]) {
					idx = i;
					break;
				}
			}
	} else if (idx < 1 || idx > 4)
		return -EINVAL;
	else
		idx--;

	if (erq->flags & IW_ENCODE_DISABLED)
J
Johannes Berg 已提交
501
		remove = 1;
502 503
	else if (erq->length == 0) {
		/* No key data - just set the default TX key index */
J
Johannes Berg 已提交
504
		ieee80211_set_default_key(sdata, idx);
505 506 507
		return 0;
	}

508
	ret = ieee80211_set_encryption(
509
		sdata, bcaddr,
J
Johannes Berg 已提交
510
		idx, alg, remove,
511 512
		!sdata->default_key,
		keybuf, erq->length);
513

514
	if (!ret && sdata->vif.type == NL80211_IFTYPE_STATION) {
515 516 517 518 519 520 521
		if (remove)
			sdata->u.mgd.flags &= ~IEEE80211_STA_TKIP_WEP_USED;
		else
			sdata->u.mgd.flags |= IEEE80211_STA_TKIP_WEP_USED;
	}

	return ret;
522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557
}


static int ieee80211_ioctl_giwencode(struct net_device *dev,
				     struct iw_request_info *info,
				     struct iw_point *erq, char *key)
{
	struct ieee80211_sub_if_data *sdata;
	int idx, i;

	sdata = IEEE80211_DEV_TO_SUB_IF(dev);

	idx = erq->flags & IW_ENCODE_INDEX;
	if (idx < 1 || idx > 4) {
		idx = -1;
		if (!sdata->default_key)
			idx = 0;
		else for (i = 0; i < NUM_DEFAULT_KEYS; i++) {
			if (sdata->default_key == sdata->keys[i]) {
				idx = i;
				break;
			}
		}
		if (idx < 0)
			return -EINVAL;
	} else
		idx--;

	erq->flags = idx + 1;

	if (!sdata->keys[idx]) {
		erq->length = 0;
		erq->flags |= IW_ENCODE_DISABLED;
		return 0;
	}

558
	memcpy(key, sdata->keys[idx]->conf.key,
J
Johannes Berg 已提交
559
	       min_t(int, erq->length, sdata->keys[idx]->conf.keylen));
560
	erq->length = sdata->keys[idx]->conf.keylen;
561 562
	erq->flags |= IW_ENCODE_ENABLED;

563
	if (sdata->vif.type == NL80211_IFTYPE_STATION) {
564
		switch (sdata->u.mgd.auth_alg) {
565 566 567 568 569 570 571 572 573 574
		case WLAN_AUTH_OPEN:
		case WLAN_AUTH_LEAP:
			erq->flags |= IW_ENCODE_OPEN;
			break;
		case WLAN_AUTH_SHARED_KEY:
			erq->flags |= IW_ENCODE_RESTRICTED;
			break;
		}
	}

575 576 577
	return 0;
}

578 579 580 581 582
static int ieee80211_ioctl_siwpower(struct net_device *dev,
				    struct iw_request_info *info,
				    struct iw_param *wrq,
				    char *extra)
{
583
	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
584 585
	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
	struct ieee80211_conf *conf = &local->hw.conf;
586
	int timeout = 0;
587 588
	bool ps;

589 590 591
	if (!(local->hw.flags & IEEE80211_HW_SUPPORTS_PS))
		return -EOPNOTSUPP;

592 593
	if (sdata->vif.type != NL80211_IFTYPE_STATION)
		return -EINVAL;
594 595

	if (wrq->disabled) {
596
		ps = false;
597
		timeout = 0;
598
		goto set;
599 600 601 602 603 604
	}

	switch (wrq->flags & IW_POWER_MODE) {
	case IW_POWER_ON:       /* If not specified */
	case IW_POWER_MODE:     /* If set all mask */
	case IW_POWER_ALL_R:    /* If explicitely state all */
605
		ps = true;
606
		break;
607
	default:                /* Otherwise we ignore */
608
		return -EINVAL;
609 610
	}

611 612 613
	if (wrq->flags & ~(IW_POWER_MODE | IW_POWER_TIMEOUT))
		return -EINVAL;

614 615
	if (wrq->flags & IW_POWER_TIMEOUT)
		timeout = wrq->value / 1000;
616

617
 set:
618 619
	if (ps == sdata->u.mgd.powersave && timeout == conf->dynamic_ps_timeout)
		return 0;
620

621
	sdata->u.mgd.powersave = ps;
622
	conf->dynamic_ps_timeout = timeout;
623

624
	if (local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_PS)
625
		ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
626

627
	ieee80211_recalc_ps(local, -1);
628

629
	return 0;
630 631 632 633 634 635 636
}

static int ieee80211_ioctl_giwpower(struct net_device *dev,
				    struct iw_request_info *info,
				    union iwreq_data *wrqu,
				    char *extra)
{
637
	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
638

639
	wrqu->power.disabled = !sdata->u.mgd.powersave;
640 641 642 643

	return 0;
}

644 645 646 647 648 649 650 651 652 653 654 655 656
static int ieee80211_ioctl_siwauth(struct net_device *dev,
				   struct iw_request_info *info,
				   struct iw_param *data, char *extra)
{
	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
	int ret = 0;

	switch (data->flags & IW_AUTH_INDEX) {
	case IW_AUTH_WPA_VERSION:
	case IW_AUTH_CIPHER_GROUP:
	case IW_AUTH_WPA_ENABLED:
	case IW_AUTH_RX_UNENCRYPTED_EAPOL:
	case IW_AUTH_KEY_MGMT:
657
	case IW_AUTH_CIPHER_GROUP_MGMT:
658
		break;
659 660 661 662
	case IW_AUTH_CIPHER_PAIRWISE:
		if (sdata->vif.type == NL80211_IFTYPE_STATION) {
			if (data->value & (IW_AUTH_CIPHER_WEP40 |
			    IW_AUTH_CIPHER_WEP104 | IW_AUTH_CIPHER_TKIP))
663
				sdata->u.mgd.flags |=
664 665
					IEEE80211_STA_TKIP_WEP_USED;
			else
666
				sdata->u.mgd.flags &=
667 668 669
					~IEEE80211_STA_TKIP_WEP_USED;
		}
		break;
670 671 672
	case IW_AUTH_DROP_UNENCRYPTED:
		sdata->drop_unencrypted = !!data->value;
		break;
673
	case IW_AUTH_PRIVACY_INVOKED:
674
		if (sdata->vif.type != NL80211_IFTYPE_STATION)
675 676
			ret = -EINVAL;
		else {
677
			sdata->u.mgd.flags &= ~IEEE80211_STA_PRIVACY_INVOKED;
678
			/*
679 680 681
			 * Privacy invoked by wpa_supplicant, store the
			 * value and allow associating to a protected
			 * network without having a key up front.
682
			 */
683
			if (data->value)
684
				sdata->u.mgd.flags |=
685
					IEEE80211_STA_PRIVACY_INVOKED;
686 687 688
		}
		break;
	case IW_AUTH_80211_AUTH_ALG:
689 690
		if (sdata->vif.type == NL80211_IFTYPE_STATION)
			sdata->u.mgd.auth_algs = data->value;
691 692 693
		else
			ret = -EOPNOTSUPP;
		break;
694
	case IW_AUTH_MFP:
695 696 697 698
		if (!(sdata->local->hw.flags & IEEE80211_HW_MFP_CAPABLE)) {
			ret = -EOPNOTSUPP;
			break;
		}
699
		if (sdata->vif.type == NL80211_IFTYPE_STATION) {
700 701
			switch (data->value) {
			case IW_AUTH_MFP_DISABLED:
702
				sdata->u.mgd.mfp = IEEE80211_MFP_DISABLED;
703 704
				break;
			case IW_AUTH_MFP_OPTIONAL:
705
				sdata->u.mgd.mfp = IEEE80211_MFP_OPTIONAL;
706 707
				break;
			case IW_AUTH_MFP_REQUIRED:
708
				sdata->u.mgd.mfp = IEEE80211_MFP_REQUIRED;
709 710 711 712 713
				break;
			default:
				ret = -EINVAL;
			}
		} else
714 715
			ret = -EOPNOTSUPP;
		break;
716 717 718 719 720 721 722 723 724 725 726 727 728 729 730
	default:
		ret = -EOPNOTSUPP;
		break;
	}
	return ret;
}

/* Get wireless statistics.  Called by /proc/net/wireless and by SIOCGIWSTATS */
static struct iw_statistics *ieee80211_get_wireless_stats(struct net_device *dev)
{
	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
	struct iw_statistics *wstats = &local->wstats;
	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
	struct sta_info *sta = NULL;

J
Johannes Berg 已提交
731 732
	rcu_read_lock();

733 734 735
	if (sdata->vif.type == NL80211_IFTYPE_STATION)
		sta = sta_info_get(local, sdata->u.mgd.bssid);

736 737 738 739 740 741 742 743
	if (!sta) {
		wstats->discard.fragment = 0;
		wstats->discard.misc = 0;
		wstats->qual.qual = 0;
		wstats->qual.level = 0;
		wstats->qual.noise = 0;
		wstats->qual.updated = IW_QUAL_ALL_INVALID;
	} else {
744 745 746 747 748 749 750 751 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 781 782
		wstats->qual.updated = 0;
		/*
		 * mirror what cfg80211 does for iwrange/scan results,
		 * otherwise userspace gets confused.
		 */
		if (local->hw.flags & (IEEE80211_HW_SIGNAL_UNSPEC |
				       IEEE80211_HW_SIGNAL_DBM)) {
			wstats->qual.updated |= IW_QUAL_LEVEL_UPDATED;
			wstats->qual.updated |= IW_QUAL_QUAL_UPDATED;
		} else {
			wstats->qual.updated |= IW_QUAL_LEVEL_INVALID;
			wstats->qual.updated |= IW_QUAL_QUAL_INVALID;
		}

		if (local->hw.flags & IEEE80211_HW_SIGNAL_UNSPEC) {
			wstats->qual.level = sta->last_signal;
			wstats->qual.qual = sta->last_signal;
		} else if (local->hw.flags & IEEE80211_HW_SIGNAL_DBM) {
			int sig = sta->last_signal;

			wstats->qual.updated |= IW_QUAL_DBM;
			wstats->qual.level = sig;
			if (sig < -110)
				sig = -110;
			else if (sig > -40)
				sig = -40;
			wstats->qual.qual = sig + 110;
		}

		if (local->hw.flags & IEEE80211_HW_NOISE_DBM) {
			/*
			 * This assumes that if driver reports noise, it also
			 * reports signal in dBm.
			 */
			wstats->qual.noise = sta->last_noise;
			wstats->qual.updated |= IW_QUAL_NOISE_UPDATED;
		} else {
			wstats->qual.updated |= IW_QUAL_NOISE_INVALID;
		}
783
	}
J
Johannes Berg 已提交
784 785 786

	rcu_read_unlock();

787 788 789 790 791 792 793 794 795 796 797 798
	return wstats;
}

static int ieee80211_ioctl_giwauth(struct net_device *dev,
				   struct iw_request_info *info,
				   struct iw_param *data, char *extra)
{
	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
	int ret = 0;

	switch (data->flags & IW_AUTH_INDEX) {
	case IW_AUTH_80211_AUTH_ALG:
799 800
		if (sdata->vif.type == NL80211_IFTYPE_STATION)
			data->value = sdata->u.mgd.auth_algs;
801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817
		else
			ret = -EOPNOTSUPP;
		break;
	default:
		ret = -EOPNOTSUPP;
		break;
	}
	return ret;
}


static int ieee80211_ioctl_siwencodeext(struct net_device *dev,
					struct iw_request_info *info,
					struct iw_point *erq, char *extra)
{
	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
	struct iw_encode_ext *ext = (struct iw_encode_ext *) extra;
J
Johannes Berg 已提交
818
	int uninitialized_var(alg), idx, i, remove = 0;
819 820 821

	switch (ext->alg) {
	case IW_ENCODE_ALG_NONE:
J
Johannes Berg 已提交
822
		remove = 1;
823 824 825 826 827 828 829 830 831 832
		break;
	case IW_ENCODE_ALG_WEP:
		alg = ALG_WEP;
		break;
	case IW_ENCODE_ALG_TKIP:
		alg = ALG_TKIP;
		break;
	case IW_ENCODE_ALG_CCMP:
		alg = ALG_CCMP;
		break;
833 834 835
	case IW_ENCODE_ALG_AES_CMAC:
		alg = ALG_AES_CMAC;
		break;
836 837 838 839 840
	default:
		return -EOPNOTSUPP;
	}

	if (erq->flags & IW_ENCODE_DISABLED)
J
Johannes Berg 已提交
841
		remove = 1;
842 843

	idx = erq->flags & IW_ENCODE_INDEX;
844 845 846 847 848 849 850 851 852 853 854 855 856 857
	if (alg == ALG_AES_CMAC) {
		if (idx < NUM_DEFAULT_KEYS + 1 ||
		    idx > NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS) {
			idx = -1;
			if (!sdata->default_mgmt_key)
				idx = 0;
			else for (i = NUM_DEFAULT_KEYS;
				  i < NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS;
				  i++) {
				if (sdata->default_mgmt_key == sdata->keys[i])
				{
					idx = i;
					break;
				}
858
			}
859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878
			if (idx < 0)
				return -EINVAL;
		} else
			idx--;
	} else {
		if (idx < 1 || idx > 4) {
			idx = -1;
			if (!sdata->default_key)
				idx = 0;
			else for (i = 0; i < NUM_DEFAULT_KEYS; i++) {
				if (sdata->default_key == sdata->keys[i]) {
					idx = i;
					break;
				}
			}
			if (idx < 0)
				return -EINVAL;
		} else
			idx--;
	}
879

880
	return ieee80211_set_encryption(sdata, ext->addr.sa_data, idx, alg,
J
Johannes Berg 已提交
881
					remove,
882 883 884 885 886 887 888 889 890 891 892
					ext->ext_flags &
					IW_ENCODE_EXT_SET_TX_KEY,
					ext->key, ext->key_len);
}


/* Structures to export the Wireless Handlers */

static const iw_handler ieee80211_handler[] =
{
	(iw_handler) NULL,				/* SIOCSIWCOMMIT */
J
Johannes Berg 已提交
893
	(iw_handler) cfg80211_wext_giwname,		/* SIOCGIWNAME */
894 895 896 897
	(iw_handler) NULL,				/* SIOCSIWNWID */
	(iw_handler) NULL,				/* SIOCGIWNWID */
	(iw_handler) ieee80211_ioctl_siwfreq,		/* SIOCSIWFREQ */
	(iw_handler) ieee80211_ioctl_giwfreq,		/* SIOCGIWFREQ */
898 899
	(iw_handler) cfg80211_wext_siwmode,		/* SIOCSIWMODE */
	(iw_handler) cfg80211_wext_giwmode,		/* SIOCGIWMODE */
900 901 902
	(iw_handler) NULL,				/* SIOCSIWSENS */
	(iw_handler) NULL,				/* SIOCGIWSENS */
	(iw_handler) NULL /* not used */,		/* SIOCSIWRANGE */
903
	(iw_handler) cfg80211_wext_giwrange,		/* SIOCGIWRANGE */
904 905 906 907
	(iw_handler) NULL /* not used */,		/* SIOCSIWPRIV */
	(iw_handler) NULL /* kernel code */,		/* SIOCGIWPRIV */
	(iw_handler) NULL /* not used */,		/* SIOCSIWSTATS */
	(iw_handler) NULL /* kernel code */,		/* SIOCGIWSTATS */
J
Johannes Berg 已提交
908 909 910 911
	(iw_handler) NULL,				/* SIOCSIWSPY */
	(iw_handler) NULL,				/* SIOCGIWSPY */
	(iw_handler) NULL,				/* SIOCSIWTHRSPY */
	(iw_handler) NULL,				/* SIOCGIWTHRSPY */
912 913
	(iw_handler) ieee80211_ioctl_siwap,		/* SIOCSIWAP */
	(iw_handler) ieee80211_ioctl_giwap,		/* SIOCGIWAP */
914
	(iw_handler) cfg80211_wext_siwmlme,		/* SIOCSIWMLME */
915
	(iw_handler) NULL,				/* SIOCGIWAPLIST */
916 917
	(iw_handler) cfg80211_wext_siwscan,		/* SIOCSIWSCAN */
	(iw_handler) cfg80211_wext_giwscan,		/* SIOCGIWSCAN */
918 919 920 921 922 923
	(iw_handler) ieee80211_ioctl_siwessid,		/* SIOCSIWESSID */
	(iw_handler) ieee80211_ioctl_giwessid,		/* SIOCGIWESSID */
	(iw_handler) NULL,				/* SIOCSIWNICKN */
	(iw_handler) NULL,				/* SIOCGIWNICKN */
	(iw_handler) NULL,				/* -- hole -- */
	(iw_handler) NULL,				/* -- hole -- */
924
	(iw_handler) ieee80211_ioctl_siwrate,		/* SIOCSIWRATE */
925
	(iw_handler) ieee80211_ioctl_giwrate,		/* SIOCGIWRATE */
926 927 928 929
	(iw_handler) cfg80211_wext_siwrts,		/* SIOCSIWRTS */
	(iw_handler) cfg80211_wext_giwrts,		/* SIOCGIWRTS */
	(iw_handler) cfg80211_wext_siwfrag,		/* SIOCSIWFRAG */
	(iw_handler) cfg80211_wext_giwfrag,		/* SIOCGIWFRAG */
930
	(iw_handler) ieee80211_ioctl_siwtxpower,	/* SIOCSIWTXPOW */
931
	(iw_handler) ieee80211_ioctl_giwtxpower,	/* SIOCGIWTXPOW */
932 933
	(iw_handler) cfg80211_wext_siwretry,		/* SIOCSIWRETRY */
	(iw_handler) cfg80211_wext_giwretry,		/* SIOCGIWRETRY */
934 935
	(iw_handler) ieee80211_ioctl_siwencode,		/* SIOCSIWENCODE */
	(iw_handler) ieee80211_ioctl_giwencode,		/* SIOCGIWENCODE */
936 937
	(iw_handler) ieee80211_ioctl_siwpower,		/* SIOCSIWPOWER */
	(iw_handler) ieee80211_ioctl_giwpower,		/* SIOCGIWPOWER */
938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955
	(iw_handler) NULL,				/* -- hole -- */
	(iw_handler) NULL,				/* -- hole -- */
	(iw_handler) ieee80211_ioctl_siwgenie,		/* SIOCSIWGENIE */
	(iw_handler) NULL,				/* SIOCGIWGENIE */
	(iw_handler) ieee80211_ioctl_siwauth,		/* SIOCSIWAUTH */
	(iw_handler) ieee80211_ioctl_giwauth,		/* SIOCGIWAUTH */
	(iw_handler) ieee80211_ioctl_siwencodeext,	/* SIOCSIWENCODEEXT */
	(iw_handler) NULL,				/* SIOCGIWENCODEEXT */
	(iw_handler) NULL,				/* SIOCSIWPMKSA */
	(iw_handler) NULL,				/* -- hole -- */
};

const struct iw_handler_def ieee80211_iw_handler_def =
{
	.num_standard	= ARRAY_SIZE(ieee80211_handler),
	.standard	= (iw_handler *) ieee80211_handler,
	.get_wireless_stats = ieee80211_get_wireless_stats,
};