libipw_wx.c 21.1 KB
Newer Older
J
Jeff Garzik 已提交
1 2
/******************************************************************************

3
  Copyright(c) 2004-2005 Intel Corporation. All rights reserved.
J
Jeff Garzik 已提交
4 5 6 7

  Portions of this file are based on the WEP enablement code provided by the
  Host AP project hostap-drivers v0.1.3
  Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen
8 9
  <j@w1.fi>
  Copyright (c) 2002-2003, Jouni Malinen <j@w1.fi>
J
Jeff Garzik 已提交
10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27

  This program is free software; you can redistribute it and/or modify it
  under the terms of version 2 of the GNU General Public License as
  published by the Free Software Foundation.

  This program is distributed in the hope that it will be useful, but WITHOUT
  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
  more details.

  You should have received a copy of the GNU General Public License along with
  this program; if not, write to the Free Software Foundation, Inc., 59
  Temple Place - Suite 330, Boston, MA  02111-1307, USA.

  The full GNU General Public License is included in this distribution in the
  file called LICENSE.

  Contact Information:
28
  Intel Linux Wireless <ilw@linux.intel.com>
J
Jeff Garzik 已提交
29 30 31
  Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497

******************************************************************************/
32

J
Jeff Garzik 已提交
33 34
#include <linux/kmod.h>
#include <linux/module.h>
35
#include <linux/jiffies.h>
J
Jeff Garzik 已提交
36

37
#include <net/lib80211.h>
38 39
#include <linux/wireless.h>

40
#include "libipw.h"
41

42
static const char *libipw_modes[] = {
J
Jeff Garzik 已提交
43 44 45
	"?", "a", "b", "ab", "g", "ag", "bg", "abg"
};

46 47 48 49 50 51 52 53 54 55
static inline unsigned int elapsed_jiffies_msecs(unsigned long start)
{
	unsigned long end = jiffies;

	if (end >= start)
		return jiffies_to_msecs(end - start);

	return jiffies_to_msecs(end + (MAX_JIFFY_OFFSET - start) + 1);
}

J
Jeff Garzik 已提交
56
#define MAX_CUSTOM_LEN 64
57
static char *libipw_translate_scan(struct libipw_device *ieee,
58
				      char *start, char *stop,
59
				      struct libipw_network *network,
60
				      struct iw_request_info *info)
J
Jeff Garzik 已提交
61 62 63 64 65
{
	char custom[MAX_CUSTOM_LEN];
	char *p;
	struct iw_event iwe;
	int i, j;
66 67
	char *current_val;	/* For rates */
	u8 rate;
J
Jeff Garzik 已提交
68 69 70 71 72

	/* First entry *MUST* be the AP MAC address */
	iwe.cmd = SIOCGIWAP;
	iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
	memcpy(iwe.u.ap_addr.sa_data, network->bssid, ETH_ALEN);
73
	start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_ADDR_LEN);
J
Jeff Garzik 已提交
74 75 76 77 78 79

	/* Remaining entries will be displayed in the order we provide them */

	/* Add the ESSID */
	iwe.cmd = SIOCGIWESSID;
	iwe.u.data.flags = 1;
80 81 82
	iwe.u.data.length = min(network->ssid_len, (u8) 32);
	start = iwe_stream_add_point(info, start, stop,
				     &iwe, network->ssid);
J
Jeff Garzik 已提交
83 84 85

	/* Add the protocol name */
	iwe.cmd = SIOCGIWNAME;
86
	snprintf(iwe.u.name, IFNAMSIZ, "IEEE 802.11%s",
87
		 libipw_modes[network->mode]);
88
	start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_CHAR_LEN);
J
Jeff Garzik 已提交
89

90 91 92
	/* Add mode */
	iwe.cmd = SIOCGIWMODE;
	if (network->capability & (WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS)) {
93
		if (network->capability & WLAN_CAPABILITY_ESS)
J
Jeff Garzik 已提交
94 95 96 97
			iwe.u.mode = IW_MODE_MASTER;
		else
			iwe.u.mode = IW_MODE_ADHOC;

98 99
		start = iwe_stream_add_event(info, start, stop,
					     &iwe, IW_EV_UINT_LEN);
J
Jeff Garzik 已提交
100 101
	}

102
	/* Add channel and frequency */
103
	/* Note : userspace automatically computes channel using iwrange */
J
Jeff Garzik 已提交
104
	iwe.cmd = SIOCGIWFREQ;
105
	iwe.u.freq.m = libipw_channel_to_freq(ieee, network->channel);
106
	iwe.u.freq.e = 6;
107
	iwe.u.freq.i = 0;
108
	start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_FREQ_LEN);
109

J
Jeff Garzik 已提交
110 111 112 113 114 115 116
	/* Add encryption capability */
	iwe.cmd = SIOCGIWENCODE;
	if (network->capability & WLAN_CAPABILITY_PRIVACY)
		iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
	else
		iwe.u.data.flags = IW_ENCODE_DISABLED;
	iwe.u.data.length = 0;
117 118
	start = iwe_stream_add_point(info, start, stop,
				     &iwe, network->ssid);
J
Jeff Garzik 已提交
119 120

	/* Add basic and extended rates */
121 122
	/* Rate : stuffing multiple values in a single event require a bit
	 * more of magic - Jean II */
123
	current_val = start + iwe_stream_lcp_len(info);
124 125 126 127
	iwe.cmd = SIOCGIWRATE;
	/* Those two flags are ignored... */
	iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0;

128
	for (i = 0, j = 0; i < network->rates_len;) {
J
Jeff Garzik 已提交
129 130 131 132 133 134
		if (j < network->rates_ex_len &&
		    ((network->rates_ex[j] & 0x7F) <
		     (network->rates[i] & 0x7F)))
			rate = network->rates_ex[j++] & 0x7F;
		else
			rate = network->rates[i++] & 0x7F;
135 136 137
		/* Bit rate given in 500 kb/s units (+ 0x80) */
		iwe.u.bitrate.value = ((rate & 0x7f) * 500000);
		/* Add new value to event */
138 139
		current_val = iwe_stream_add_value(info, start, current_val,
						   stop, &iwe, IW_EV_PARAM_LEN);
J
Jeff Garzik 已提交
140 141 142
	}
	for (; j < network->rates_ex_len; j++) {
		rate = network->rates_ex[j] & 0x7F;
143 144 145
		/* Bit rate given in 500 kb/s units (+ 0x80) */
		iwe.u.bitrate.value = ((rate & 0x7f) * 500000);
		/* Add new value to event */
146 147
		current_val = iwe_stream_add_value(info, start, current_val,
						   stop, &iwe, IW_EV_PARAM_LEN);
J
Jeff Garzik 已提交
148
	}
149
	/* Check if we added any rate */
150
	if ((current_val - start) > iwe_stream_lcp_len(info))
151
		start = current_val;
J
Jeff Garzik 已提交
152 153 154

	/* Add quality statistics */
	iwe.cmd = IWEVQUAL;
155 156 157
	iwe.u.qual.updated = IW_QUAL_QUAL_UPDATED | IW_QUAL_LEVEL_UPDATED |
	    IW_QUAL_NOISE_UPDATED;

158
	if (!(network->stats.mask & LIBIPW_STATMASK_RSSI)) {
159 160 161 162
		iwe.u.qual.updated |= IW_QUAL_QUAL_INVALID |
		    IW_QUAL_LEVEL_INVALID;
		iwe.u.qual.qual = 0;
	} else {
163 164 165 166 167 168 169 170 171
		if (ieee->perfect_rssi == ieee->worst_rssi)
			iwe.u.qual.qual = 100;
		else
			iwe.u.qual.qual =
			    (100 *
			     (ieee->perfect_rssi - ieee->worst_rssi) *
			     (ieee->perfect_rssi - ieee->worst_rssi) -
			     (ieee->perfect_rssi - network->stats.rssi) *
			     (15 * (ieee->perfect_rssi - ieee->worst_rssi) +
172 173 174 175 176
			      62 * (ieee->perfect_rssi -
				    network->stats.rssi))) /
			    ((ieee->perfect_rssi -
			      ieee->worst_rssi) * (ieee->perfect_rssi -
						   ieee->worst_rssi));
177 178 179 180 181 182
		if (iwe.u.qual.qual > 100)
			iwe.u.qual.qual = 100;
		else if (iwe.u.qual.qual < 1)
			iwe.u.qual.qual = 0;
	}

183
	if (!(network->stats.mask & LIBIPW_STATMASK_NOISE)) {
J
Jeff Garzik 已提交
184
		iwe.u.qual.updated |= IW_QUAL_NOISE_INVALID;
185 186 187 188
		iwe.u.qual.noise = 0;
	} else {
		iwe.u.qual.noise = network->stats.noise;
	}
J
Jeff Garzik 已提交
189

190
	if (!(network->stats.mask & LIBIPW_STATMASK_SIGNAL)) {
191 192 193 194 195 196
		iwe.u.qual.updated |= IW_QUAL_LEVEL_INVALID;
		iwe.u.qual.level = 0;
	} else {
		iwe.u.qual.level = network->stats.signal;
	}

197
	start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_QUAL_LEN);
J
Jeff Garzik 已提交
198 199 200 201 202 203

	iwe.cmd = IWEVCUSTOM;
	p = custom;

	iwe.u.data.length = p - custom;
	if (iwe.u.data.length)
204
		start = iwe_stream_add_point(info, start, stop, &iwe, custom);
J
Jeff Garzik 已提交
205

206
	memset(&iwe, 0, sizeof(iwe));
207
	if (network->wpa_ie_len) {
208 209 210 211
		char buf[MAX_WPA_IE_LEN];
		memcpy(buf, network->wpa_ie, network->wpa_ie_len);
		iwe.cmd = IWEVGENIE;
		iwe.u.data.length = network->wpa_ie_len;
212
		start = iwe_stream_add_point(info, start, stop, &iwe, buf);
J
Jeff Garzik 已提交
213 214
	}

215
	memset(&iwe, 0, sizeof(iwe));
216
	if (network->rsn_ie_len) {
217 218 219 220
		char buf[MAX_WPA_IE_LEN];
		memcpy(buf, network->rsn_ie, network->rsn_ie_len);
		iwe.cmd = IWEVGENIE;
		iwe.u.data.length = network->rsn_ie_len;
221
		start = iwe_stream_add_point(info, start, stop, &iwe, buf);
J
Jeff Garzik 已提交
222 223 224 225 226 227 228
	}

	/* Add EXTRA: Age to display seconds since last beacon/probe response
	 * for given network. */
	iwe.cmd = IWEVCUSTOM;
	p = custom;
	p += snprintf(p, MAX_CUSTOM_LEN - (p - custom),
229 230
		      " Last beacon: %ums ago",
		      elapsed_jiffies_msecs(network->last_scanned));
J
Jeff Garzik 已提交
231 232
	iwe.u.data.length = p - custom;
	if (iwe.u.data.length)
233
		start = iwe_stream_add_point(info, start, stop, &iwe, custom);
J
Jeff Garzik 已提交
234

235 236 237 238 239
	/* Add spectrum management information */
	iwe.cmd = -1;
	p = custom;
	p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), " Channel flags: ");

240 241
	if (libipw_get_channel_flags(ieee, network->channel) &
	    LIBIPW_CH_INVALID) {
242 243 244 245
		iwe.cmd = IWEVCUSTOM;
		p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), "INVALID ");
	}

246 247
	if (libipw_get_channel_flags(ieee, network->channel) &
	    LIBIPW_CH_RADAR_DETECT) {
248 249 250 251 252 253
		iwe.cmd = IWEVCUSTOM;
		p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), "DFS ");
	}

	if (iwe.cmd == IWEVCUSTOM) {
		iwe.u.data.length = p - custom;
254
		start = iwe_stream_add_point(info, start, stop, &iwe, custom);
255 256
	}

J
Jeff Garzik 已提交
257 258 259
	return start;
}

260 261
#define SCAN_ITEM_SIZE 128

262
int libipw_wx_get_scan(struct libipw_device *ieee,
J
Jeff Garzik 已提交
263 264 265
			  struct iw_request_info *info,
			  union iwreq_data *wrqu, char *extra)
{
266
	struct libipw_network *network;
J
Jeff Garzik 已提交
267
	unsigned long flags;
268
	int err = 0;
J
Jeff Garzik 已提交
269 270

	char *ev = extra;
271
	char *stop = ev + wrqu->data.length;
J
Jeff Garzik 已提交
272
	int i = 0;
273
	DECLARE_SSID_BUF(ssid);
J
Jeff Garzik 已提交
274

275
	LIBIPW_DEBUG_WX("Getting scan\n");
J
Jeff Garzik 已提交
276 277 278 279 280

	spin_lock_irqsave(&ieee->lock, flags);

	list_for_each_entry(network, &ieee->network_list, list) {
		i++;
281 282 283 284 285
		if (stop - ev < SCAN_ITEM_SIZE) {
			err = -E2BIG;
			break;
		}

J
Jeff Garzik 已提交
286 287
		if (ieee->scan_age == 0 ||
		    time_after(network->last_scanned + ieee->scan_age, jiffies))
288
			ev = libipw_translate_scan(ieee, ev, stop, network,
289
						      info);
290
		else {
291
			LIBIPW_DEBUG_SCAN("Not showing network '%s ("
292
					     "%pM)' due to age (%ums).\n",
293
					     print_ssid(ssid, network->ssid,
294
							 network->ssid_len),
J
Johannes Berg 已提交
295
					     network->bssid,
296 297 298
					     elapsed_jiffies_msecs(
					               network->last_scanned));
		}
J
Jeff Garzik 已提交
299 300 301 302
	}

	spin_unlock_irqrestore(&ieee->lock, flags);

303
	wrqu->data.length = ev - extra;
J
Jeff Garzik 已提交
304 305
	wrqu->data.flags = 0;

306
	LIBIPW_DEBUG_WX("exit: %d networks returned.\n", i);
J
Jeff Garzik 已提交
307

308
	return err;
J
Jeff Garzik 已提交
309 310
}

311
int libipw_wx_set_encode(struct libipw_device *ieee,
J
Jeff Garzik 已提交
312 313 314 315 316
			    struct iw_request_info *info,
			    union iwreq_data *wrqu, char *keybuf)
{
	struct iw_point *erq = &(wrqu->encoding);
	struct net_device *dev = ieee->dev;
317
	struct libipw_security sec = {
J
Jeff Garzik 已提交
318 319 320
		.flags = 0
	};
	int i, key, key_provided, len;
321
	struct lib80211_crypt_data **crypt;
322
	int host_crypto = ieee->host_encrypt || ieee->host_decrypt || ieee->host_build_iv;
323
	DECLARE_SSID_BUF(ssid);
J
Jeff Garzik 已提交
324

325
	LIBIPW_DEBUG_WX("SET_ENCODE\n");
J
Jeff Garzik 已提交
326 327 328 329 330 331 332 333 334

	key = erq->flags & IW_ENCODE_INDEX;
	if (key) {
		if (key > WEP_KEYS)
			return -EINVAL;
		key--;
		key_provided = 1;
	} else {
		key_provided = 0;
335
		key = ieee->crypt_info.tx_keyidx;
J
Jeff Garzik 已提交
336 337
	}

338
	LIBIPW_DEBUG_WX("Key: %d [%s]\n", key, key_provided ?
J
Jeff Garzik 已提交
339 340
			   "provided" : "default");

341
	crypt = &ieee->crypt_info.crypt[key];
J
Jeff Garzik 已提交
342 343 344

	if (erq->flags & IW_ENCODE_DISABLED) {
		if (key_provided && *crypt) {
345
			LIBIPW_DEBUG_WX("Disabling encryption on key %d.\n",
J
Jeff Garzik 已提交
346
					   key);
347
			lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt);
J
Jeff Garzik 已提交
348
		} else
349
			LIBIPW_DEBUG_WX("Disabling encryption.\n");
J
Jeff Garzik 已提交
350 351 352 353

		/* Check all the keys to see if any are still configured,
		 * and if no key index was provided, de-init them all */
		for (i = 0; i < WEP_KEYS; i++) {
354
			if (ieee->crypt_info.crypt[i] != NULL) {
J
Jeff Garzik 已提交
355 356
				if (key_provided)
					break;
357 358
				lib80211_crypt_delayed_deinit(&ieee->crypt_info,
							       &ieee->crypt_info.crypt[i]);
J
Jeff Garzik 已提交
359 360 361 362 363
			}
		}

		if (i == WEP_KEYS) {
			sec.enabled = 0;
364
			sec.encrypt = 0;
J
Jeff Garzik 已提交
365
			sec.level = SEC_LEVEL_0;
366
			sec.flags |= SEC_ENABLED | SEC_LEVEL | SEC_ENCRYPT;
J
Jeff Garzik 已提交
367 368 369 370 371 372
		}

		goto done;
	}

	sec.enabled = 1;
373
	sec.encrypt = 1;
374
	sec.flags |= SEC_ENABLED | SEC_ENCRYPT;
J
Jeff Garzik 已提交
375 376 377 378 379

	if (*crypt != NULL && (*crypt)->ops != NULL &&
	    strcmp((*crypt)->ops->name, "WEP") != 0) {
		/* changing to use WEP; deinit previously used algorithm
		 * on this key */
380
		lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt);
J
Jeff Garzik 已提交
381 382
	}

383
	if (*crypt == NULL && host_crypto) {
384
		struct lib80211_crypt_data *new_crypt;
J
Jeff Garzik 已提交
385 386

		/* take WEP into use */
387
		new_crypt = kzalloc(sizeof(struct lib80211_crypt_data),
J
Jeff Garzik 已提交
388 389 390
				    GFP_KERNEL);
		if (new_crypt == NULL)
			return -ENOMEM;
391
		new_crypt->ops = lib80211_get_crypto_ops("WEP");
J
Jeff Garzik 已提交
392
		if (!new_crypt->ops) {
393 394
			request_module("lib80211_crypt_wep");
			new_crypt->ops = lib80211_get_crypto_ops("WEP");
J
Jeff Garzik 已提交
395 396 397
		}

		if (new_crypt->ops && try_module_get(new_crypt->ops->owner))
398
			new_crypt->priv = new_crypt->ops->init(key);
J
Jeff Garzik 已提交
399 400 401 402 403 404

		if (!new_crypt->ops || !new_crypt->priv) {
			kfree(new_crypt);
			new_crypt = NULL;

			printk(KERN_WARNING "%s: could not initialize WEP: "
405
			       "load module lib80211_crypt_wep\n", dev->name);
J
Jeff Garzik 已提交
406 407 408 409 410 411 412
			return -EOPNOTSUPP;
		}
		*crypt = new_crypt;
	}

	/* If a new key was provided, set it up */
	if (erq->length > 0) {
413
#ifdef CONFIG_LIBIPW_DEBUG
414 415 416
		DECLARE_SSID_BUF(ssid);
#endif

J
Jeff Garzik 已提交
417 418 419 420 421
		len = erq->length <= 5 ? 5 : 13;
		memcpy(sec.keys[key], keybuf, erq->length);
		if (len > erq->length)
			memset(sec.keys[key] + erq->length, 0,
			       len - erq->length);
422
		LIBIPW_DEBUG_WX("Setting key %d to '%s' (%d:%d bytes)\n",
423
				   key, print_ssid(ssid, sec.keys[key], len),
J
Jeff Garzik 已提交
424 425
				   erq->length, len);
		sec.key_sizes[key] = len;
426 427 428
		if (*crypt)
			(*crypt)->ops->set_key(sec.keys[key], len, NULL,
					       (*crypt)->priv);
J
Jeff Garzik 已提交
429 430
		sec.flags |= (1 << key);
		/* This ensures a key will be activated if no key is
J
Jean Delvare 已提交
431
		 * explicitly set */
J
Jeff Garzik 已提交
432 433
		if (key == sec.active_key)
			sec.flags |= SEC_ACTIVE_KEY;
434

J
Jeff Garzik 已提交
435
	} else {
436 437 438 439 440
		if (host_crypto) {
			len = (*crypt)->ops->get_key(sec.keys[key], WEP_KEY_LEN,
						     NULL, (*crypt)->priv);
			if (len == 0) {
				/* Set a default key of all 0 */
441
				LIBIPW_DEBUG_WX("Setting key %d to all "
442 443 444 445 446 447 448
						   "zero.\n", key);
				memset(sec.keys[key], 0, 13);
				(*crypt)->ops->set_key(sec.keys[key], 13, NULL,
						       (*crypt)->priv);
				sec.key_sizes[key] = 13;
				sec.flags |= (1 << key);
			}
J
Jeff Garzik 已提交
449 450 451
		}
		/* No key data - just set the default TX key index */
		if (key_provided) {
452
			LIBIPW_DEBUG_WX("Setting key %d to default Tx "
453
					   "key.\n", key);
454
			ieee->crypt_info.tx_keyidx = key;
J
Jeff Garzik 已提交
455 456 457 458
			sec.active_key = key;
			sec.flags |= SEC_ACTIVE_KEY;
		}
	}
459 460 461 462 463
	if (erq->flags & (IW_ENCODE_OPEN | IW_ENCODE_RESTRICTED)) {
		ieee->open_wep = !(erq->flags & IW_ENCODE_RESTRICTED);
		sec.auth_mode = ieee->open_wep ? WLAN_AUTH_OPEN :
		    WLAN_AUTH_SHARED_KEY;
		sec.flags |= SEC_AUTH_MODE;
464
		LIBIPW_DEBUG_WX("Auth: %s\n",
465 466 467
				   sec.auth_mode == WLAN_AUTH_OPEN ?
				   "OPEN" : "SHARED KEY");
	}
J
Jeff Garzik 已提交
468 469 470 471

	/* For now we just support WEP, so only set that security level...
	 * TODO: When WPA is added this is one place that needs to change */
	sec.flags |= SEC_LEVEL;
472
	sec.level = SEC_LEVEL_1;	/* 40 and 104 bit WEP */
473
	sec.encode_alg[key] = SEC_ALG_WEP;
J
Jeff Garzik 已提交
474

475
      done:
J
Jeff Garzik 已提交
476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492
	if (ieee->set_security)
		ieee->set_security(dev, &sec);

	/* Do not reset port if card is in Managed mode since resetting will
	 * generate new IEEE 802.11 authentication which may end up in looping
	 * with IEEE 802.1X.  If your hardware requires a reset after WEP
	 * configuration (for example... Prism2), implement the reset_port in
	 * the callbacks structures used to initialize the 802.11 stack. */
	if (ieee->reset_on_keychange &&
	    ieee->iw_mode != IW_MODE_INFRA &&
	    ieee->reset_port && ieee->reset_port(dev)) {
		printk(KERN_DEBUG "%s: reset_port failed\n", dev->name);
		return -EINVAL;
	}
	return 0;
}

493
int libipw_wx_get_encode(struct libipw_device *ieee,
J
Jeff Garzik 已提交
494 495 496 497 498
			    struct iw_request_info *info,
			    union iwreq_data *wrqu, char *keybuf)
{
	struct iw_point *erq = &(wrqu->encoding);
	int len, key;
499
	struct lib80211_crypt_data *crypt;
500
	struct libipw_security *sec = &ieee->sec;
J
Jeff Garzik 已提交
501

502
	LIBIPW_DEBUG_WX("GET_ENCODE\n");
J
Jeff Garzik 已提交
503 504 505 506 507 508 509

	key = erq->flags & IW_ENCODE_INDEX;
	if (key) {
		if (key > WEP_KEYS)
			return -EINVAL;
		key--;
	} else
510
		key = ieee->crypt_info.tx_keyidx;
J
Jeff Garzik 已提交
511

512
	crypt = ieee->crypt_info.crypt[key];
J
Jeff Garzik 已提交
513 514
	erq->flags = key + 1;

515
	if (!sec->enabled) {
J
Jeff Garzik 已提交
516 517 518 519 520
		erq->length = 0;
		erq->flags |= IW_ENCODE_DISABLED;
		return 0;
	}

521 522
	len = sec->key_sizes[key];
	memcpy(keybuf, sec->keys[key], len);
J
Jeff Garzik 已提交
523

524
	erq->length = len;
J
Jeff Garzik 已提交
525 526 527 528 529 530 531 532 533 534
	erq->flags |= IW_ENCODE_ENABLED;

	if (ieee->open_wep)
		erq->flags |= IW_ENCODE_OPEN;
	else
		erq->flags |= IW_ENCODE_RESTRICTED;

	return 0;
}

535
int libipw_wx_set_encodeext(struct libipw_device *ieee,
536 537 538 539 540 541 542
			       struct iw_request_info *info,
			       union iwreq_data *wrqu, char *extra)
{
	struct net_device *dev = ieee->dev;
	struct iw_point *encoding = &wrqu->encoding;
	struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
	int i, idx, ret = 0;
543
	int group_key = 0;
544
	const char *alg, *module;
545 546
	struct lib80211_crypto_ops *ops;
	struct lib80211_crypt_data **crypt;
547

548
	struct libipw_security sec = {
549 550 551 552 553 554 555 556 557
		.flags = 0,
	};

	idx = encoding->flags & IW_ENCODE_INDEX;
	if (idx) {
		if (idx < 1 || idx > WEP_KEYS)
			return -EINVAL;
		idx--;
	} else
558
		idx = ieee->crypt_info.tx_keyidx;
559

560
	if (ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) {
561
		crypt = &ieee->crypt_info.crypt[idx];
562 563
		group_key = 1;
	} else {
564 565
		/* some Cisco APs use idx>0 for unicast in dynamic WEP */
		if (idx != 0 && ext->alg != IW_ENCODE_ALG_WEP)
566 567
			return -EINVAL;
		if (ieee->iw_mode == IW_MODE_INFRA)
568
			crypt = &ieee->crypt_info.crypt[idx];
569 570 571 572 573 574 575 576
		else
			return -EINVAL;
	}

	sec.flags |= SEC_ENABLED | SEC_ENCRYPT;
	if ((encoding->flags & IW_ENCODE_DISABLED) ||
	    ext->alg == IW_ENCODE_ALG_NONE) {
		if (*crypt)
577
			lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt);
578 579

		for (i = 0; i < WEP_KEYS; i++)
580
			if (ieee->crypt_info.crypt[i] != NULL)
581 582 583 584 585 586 587 588 589 590 591 592 593 594
				break;

		if (i == WEP_KEYS) {
			sec.enabled = 0;
			sec.encrypt = 0;
			sec.level = SEC_LEVEL_0;
			sec.flags |= SEC_LEVEL;
		}
		goto done;
	}

	sec.enabled = 1;
	sec.encrypt = 1;

595 596 597
	if (group_key ? !ieee->host_mc_decrypt :
	    !(ieee->host_encrypt || ieee->host_decrypt ||
	      ieee->host_encrypt_msdu))
598 599 600 601 602
		goto skip_host_crypt;

	switch (ext->alg) {
	case IW_ENCODE_ALG_WEP:
		alg = "WEP";
603
		module = "lib80211_crypt_wep";
604 605 606
		break;
	case IW_ENCODE_ALG_TKIP:
		alg = "TKIP";
607
		module = "lib80211_crypt_tkip";
608 609 610
		break;
	case IW_ENCODE_ALG_CCMP:
		alg = "CCMP";
611
		module = "lib80211_crypt_ccmp";
612 613
		break;
	default:
614
		LIBIPW_DEBUG_WX("%s: unknown crypto alg %d\n",
615 616 617 618 619
				   dev->name, ext->alg);
		ret = -EINVAL;
		goto done;
	}

620
	ops = lib80211_get_crypto_ops(alg);
621 622
	if (ops == NULL) {
		request_module(module);
623
		ops = lib80211_get_crypto_ops(alg);
624 625
	}
	if (ops == NULL) {
626
		LIBIPW_DEBUG_WX("%s: unknown crypto alg %d\n",
627 628 629 630 631 632
				   dev->name, ext->alg);
		ret = -EINVAL;
		goto done;
	}

	if (*crypt == NULL || (*crypt)->ops != ops) {
633
		struct lib80211_crypt_data *new_crypt;
634

635
		lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt);
636

637
		new_crypt = kzalloc(sizeof(*new_crypt), GFP_KERNEL);
638 639 640 641 642 643
		if (new_crypt == NULL) {
			ret = -ENOMEM;
			goto done;
		}
		new_crypt->ops = ops;
		if (new_crypt->ops && try_module_get(new_crypt->ops->owner))
644
			new_crypt->priv = new_crypt->ops->init(idx);
645 646 647 648 649 650 651 652 653 654 655
		if (new_crypt->priv == NULL) {
			kfree(new_crypt);
			ret = -EINVAL;
			goto done;
		}
		*crypt = new_crypt;
	}

	if (ext->key_len > 0 && (*crypt)->ops->set_key &&
	    (*crypt)->ops->set_key(ext->key, ext->key_len, ext->rx_seq,
				   (*crypt)->priv) < 0) {
656
		LIBIPW_DEBUG_WX("%s: key setting failed\n", dev->name);
657 658 659 660 661 662
		ret = -EINVAL;
		goto done;
	}

      skip_host_crypt:
	if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) {
663
		ieee->crypt_info.tx_keyidx = idx;
664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684
		sec.active_key = idx;
		sec.flags |= SEC_ACTIVE_KEY;
	}

	if (ext->alg != IW_ENCODE_ALG_NONE) {
		memcpy(sec.keys[idx], ext->key, ext->key_len);
		sec.key_sizes[idx] = ext->key_len;
		sec.flags |= (1 << idx);
		if (ext->alg == IW_ENCODE_ALG_WEP) {
			sec.encode_alg[idx] = SEC_ALG_WEP;
			sec.flags |= SEC_LEVEL;
			sec.level = SEC_LEVEL_1;
		} else if (ext->alg == IW_ENCODE_ALG_TKIP) {
			sec.encode_alg[idx] = SEC_ALG_TKIP;
			sec.flags |= SEC_LEVEL;
			sec.level = SEC_LEVEL_2;
		} else if (ext->alg == IW_ENCODE_ALG_CCMP) {
			sec.encode_alg[idx] = SEC_ALG_CCMP;
			sec.flags |= SEC_LEVEL;
			sec.level = SEC_LEVEL_3;
		}
685 686 687
		/* Don't set sec level for group keys. */
		if (group_key)
			sec.flags &= ~SEC_LEVEL;
688 689 690 691 692 693 694 695 696 697 698 699 700 701 702
	}
      done:
	if (ieee->set_security)
		ieee->set_security(ieee->dev, &sec);

	/*
	 * Do not reset port if card is in Managed mode since resetting will
	 * generate new IEEE 802.11 authentication which may end up in looping
	 * with IEEE 802.1X. If your hardware requires a reset after WEP
	 * configuration (for example... Prism2), implement the reset_port in
	 * the callbacks structures used to initialize the 802.11 stack.
	 */
	if (ieee->reset_on_keychange &&
	    ieee->iw_mode != IW_MODE_INFRA &&
	    ieee->reset_port && ieee->reset_port(dev)) {
703
		LIBIPW_DEBUG_WX("%s: reset_port failed\n", dev->name);
704 705 706 707 708 709
		return -EINVAL;
	}

	return ret;
}

710
int libipw_wx_get_encodeext(struct libipw_device *ieee,
711 712 713 714 715
			       struct iw_request_info *info,
			       union iwreq_data *wrqu, char *extra)
{
	struct iw_point *encoding = &wrqu->encoding;
	struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
716
	struct libipw_security *sec = &ieee->sec;
717 718 719 720 721 722 723 724 725 726 727 728
	int idx, max_key_len;

	max_key_len = encoding->length - sizeof(*ext);
	if (max_key_len < 0)
		return -EINVAL;

	idx = encoding->flags & IW_ENCODE_INDEX;
	if (idx) {
		if (idx < 1 || idx > WEP_KEYS)
			return -EINVAL;
		idx--;
	} else
729
		idx = ieee->crypt_info.tx_keyidx;
730

R
Roel Kluin 已提交
731
	if (!(ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) &&
732
	    ext->alg != IW_ENCODE_ALG_WEP)
733 734 735 736 737 738 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
		if (idx != 0 || ieee->iw_mode != IW_MODE_INFRA)
			return -EINVAL;

	encoding->flags = idx + 1;
	memset(ext, 0, sizeof(*ext));

	if (!sec->enabled) {
		ext->alg = IW_ENCODE_ALG_NONE;
		ext->key_len = 0;
		encoding->flags |= IW_ENCODE_DISABLED;
	} else {
		if (sec->encode_alg[idx] == SEC_ALG_WEP)
			ext->alg = IW_ENCODE_ALG_WEP;
		else if (sec->encode_alg[idx] == SEC_ALG_TKIP)
			ext->alg = IW_ENCODE_ALG_TKIP;
		else if (sec->encode_alg[idx] == SEC_ALG_CCMP)
			ext->alg = IW_ENCODE_ALG_CCMP;
		else
			return -EINVAL;

		ext->key_len = sec->key_sizes[idx];
		memcpy(ext->key, sec->keys[idx], ext->key_len);
		encoding->flags |= IW_ENCODE_ENABLED;
		if (ext->key_len &&
		    (ext->alg == IW_ENCODE_ALG_TKIP ||
		     ext->alg == IW_ENCODE_ALG_CCMP))
			ext->ext_flags |= IW_ENCODE_EXT_TX_SEQ_VALID;

	}

	return 0;
}

766 767
EXPORT_SYMBOL(libipw_wx_set_encodeext);
EXPORT_SYMBOL(libipw_wx_get_encodeext);
768

769 770 771
EXPORT_SYMBOL(libipw_wx_get_scan);
EXPORT_SYMBOL(libipw_wx_set_encode);
EXPORT_SYMBOL(libipw_wx_get_encode);