Linux Audio

Check our new training course

Loading...
v6.8
  1// SPDX-License-Identifier: GPL-2.0-only
  2/******************************************************************************
  3
  4  Copyright(c) 2004-2005 Intel Corporation. All rights reserved.
  5
  6  Portions of this file are based on the WEP enablement code provided by the
  7  Host AP project hostap-drivers v0.1.3
  8  Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen
  9  <j@w1.fi>
 10  Copyright (c) 2002-2003, Jouni Malinen <j@w1.fi>
 11
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 12
 13  Contact Information:
 14  Intel Linux Wireless <ilw@linux.intel.com>
 15  Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
 16
 17******************************************************************************/
 18
 19#include <linux/hardirq.h>
 20#include <linux/kmod.h>
 21#include <linux/slab.h>
 22#include <linux/module.h>
 23#include <linux/jiffies.h>
 24
 25#include <net/lib80211.h>
 26#include <linux/wireless.h>
 27
 28#include "libipw.h"
 29
 30static const char *libipw_modes[] = {
 31	"?", "a", "b", "ab", "g", "ag", "bg", "abg"
 32};
 33
 34static inline unsigned int elapsed_jiffies_msecs(unsigned long start)
 35{
 36	unsigned long end = jiffies;
 37
 38	if (end >= start)
 39		return jiffies_to_msecs(end - start);
 40
 41	return jiffies_to_msecs(end + (MAX_JIFFY_OFFSET - start) + 1);
 42}
 43
 44#define MAX_CUSTOM_LEN 64
 45static char *libipw_translate_scan(struct libipw_device *ieee,
 46				      char *start, char *stop,
 47				      struct libipw_network *network,
 48				      struct iw_request_info *info)
 49{
 50	char custom[MAX_CUSTOM_LEN];
 51	char *p;
 52	struct iw_event iwe;
 53	int i, j;
 54	char *current_val;	/* For rates */
 55	u8 rate;
 56
 57	/* First entry *MUST* be the AP MAC address */
 58	iwe.cmd = SIOCGIWAP;
 59	iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
 60	memcpy(iwe.u.ap_addr.sa_data, network->bssid, ETH_ALEN);
 61	start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_ADDR_LEN);
 62
 63	/* Remaining entries will be displayed in the order we provide them */
 64
 65	/* Add the ESSID */
 66	iwe.cmd = SIOCGIWESSID;
 67	iwe.u.data.flags = 1;
 68	iwe.u.data.length = min(network->ssid_len, (u8) 32);
 69	start = iwe_stream_add_point(info, start, stop,
 70				     &iwe, network->ssid);
 71
 72	/* Add the protocol name */
 73	iwe.cmd = SIOCGIWNAME;
 74	snprintf(iwe.u.name, IFNAMSIZ, "IEEE 802.11%s",
 75		 libipw_modes[network->mode]);
 76	start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_CHAR_LEN);
 77
 78	/* Add mode */
 79	iwe.cmd = SIOCGIWMODE;
 80	if (network->capability & (WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS)) {
 81		if (network->capability & WLAN_CAPABILITY_ESS)
 82			iwe.u.mode = IW_MODE_MASTER;
 83		else
 84			iwe.u.mode = IW_MODE_ADHOC;
 85
 86		start = iwe_stream_add_event(info, start, stop,
 87					     &iwe, IW_EV_UINT_LEN);
 88	}
 89
 90	/* Add channel and frequency */
 91	/* Note : userspace automatically computes channel using iwrange */
 92	iwe.cmd = SIOCGIWFREQ;
 93	iwe.u.freq.m = libipw_channel_to_freq(ieee, network->channel);
 94	iwe.u.freq.e = 6;
 95	iwe.u.freq.i = 0;
 96	start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_FREQ_LEN);
 97
 98	/* Add encryption capability */
 99	iwe.cmd = SIOCGIWENCODE;
100	if (network->capability & WLAN_CAPABILITY_PRIVACY)
101		iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
102	else
103		iwe.u.data.flags = IW_ENCODE_DISABLED;
104	iwe.u.data.length = 0;
105	start = iwe_stream_add_point(info, start, stop,
106				     &iwe, network->ssid);
107
108	/* Add basic and extended rates */
109	/* Rate : stuffing multiple values in a single event require a bit
110	 * more of magic - Jean II */
111	current_val = start + iwe_stream_lcp_len(info);
112	iwe.cmd = SIOCGIWRATE;
113	/* Those two flags are ignored... */
114	iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0;
115
116	for (i = 0, j = 0; i < network->rates_len;) {
117		if (j < network->rates_ex_len &&
118		    ((network->rates_ex[j] & 0x7F) <
119		     (network->rates[i] & 0x7F)))
120			rate = network->rates_ex[j++] & 0x7F;
121		else
122			rate = network->rates[i++] & 0x7F;
123		/* Bit rate given in 500 kb/s units (+ 0x80) */
124		iwe.u.bitrate.value = ((rate & 0x7f) * 500000);
125		/* Add new value to event */
126		current_val = iwe_stream_add_value(info, start, current_val,
127						   stop, &iwe, IW_EV_PARAM_LEN);
128	}
129	for (; j < network->rates_ex_len; j++) {
130		rate = network->rates_ex[j] & 0x7F;
131		/* Bit rate given in 500 kb/s units (+ 0x80) */
132		iwe.u.bitrate.value = ((rate & 0x7f) * 500000);
133		/* Add new value to event */
134		current_val = iwe_stream_add_value(info, start, current_val,
135						   stop, &iwe, IW_EV_PARAM_LEN);
136	}
137	/* Check if we added any rate */
138	if ((current_val - start) > iwe_stream_lcp_len(info))
139		start = current_val;
140
141	/* Add quality statistics */
142	iwe.cmd = IWEVQUAL;
143	iwe.u.qual.updated = IW_QUAL_QUAL_UPDATED | IW_QUAL_LEVEL_UPDATED |
144	    IW_QUAL_NOISE_UPDATED;
145
146	if (!(network->stats.mask & LIBIPW_STATMASK_RSSI)) {
147		iwe.u.qual.updated |= IW_QUAL_QUAL_INVALID |
148		    IW_QUAL_LEVEL_INVALID;
149		iwe.u.qual.qual = 0;
150	} else {
151		if (ieee->perfect_rssi == ieee->worst_rssi)
152			iwe.u.qual.qual = 100;
153		else
154			iwe.u.qual.qual =
155			    (100 *
156			     (ieee->perfect_rssi - ieee->worst_rssi) *
157			     (ieee->perfect_rssi - ieee->worst_rssi) -
158			     (ieee->perfect_rssi - network->stats.rssi) *
159			     (15 * (ieee->perfect_rssi - ieee->worst_rssi) +
160			      62 * (ieee->perfect_rssi -
161				    network->stats.rssi))) /
162			    ((ieee->perfect_rssi -
163			      ieee->worst_rssi) * (ieee->perfect_rssi -
164						   ieee->worst_rssi));
165		if (iwe.u.qual.qual > 100)
166			iwe.u.qual.qual = 100;
167		else if (iwe.u.qual.qual < 1)
168			iwe.u.qual.qual = 0;
169	}
170
171	if (!(network->stats.mask & LIBIPW_STATMASK_NOISE)) {
172		iwe.u.qual.updated |= IW_QUAL_NOISE_INVALID;
173		iwe.u.qual.noise = 0;
174	} else {
175		iwe.u.qual.noise = network->stats.noise;
176	}
177
178	if (!(network->stats.mask & LIBIPW_STATMASK_SIGNAL)) {
179		iwe.u.qual.updated |= IW_QUAL_LEVEL_INVALID;
180		iwe.u.qual.level = 0;
181	} else {
182		iwe.u.qual.level = network->stats.signal;
183	}
184
185	start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_QUAL_LEN);
186
187	iwe.cmd = IWEVCUSTOM;
188	p = custom;
189
190	iwe.u.data.length = p - custom;
191	if (iwe.u.data.length)
192		start = iwe_stream_add_point(info, start, stop, &iwe, custom);
193
194	memset(&iwe, 0, sizeof(iwe));
195	if (network->wpa_ie_len) {
196		char buf[MAX_WPA_IE_LEN];
197		memcpy(buf, network->wpa_ie, network->wpa_ie_len);
198		iwe.cmd = IWEVGENIE;
199		iwe.u.data.length = network->wpa_ie_len;
200		start = iwe_stream_add_point(info, start, stop, &iwe, buf);
201	}
202
203	memset(&iwe, 0, sizeof(iwe));
204	if (network->rsn_ie_len) {
205		char buf[MAX_WPA_IE_LEN];
206		memcpy(buf, network->rsn_ie, network->rsn_ie_len);
207		iwe.cmd = IWEVGENIE;
208		iwe.u.data.length = network->rsn_ie_len;
209		start = iwe_stream_add_point(info, start, stop, &iwe, buf);
210	}
211
212	/* Add EXTRA: Age to display seconds since last beacon/probe response
213	 * for given network. */
214	iwe.cmd = IWEVCUSTOM;
215	p = custom;
216	p += scnprintf(p, MAX_CUSTOM_LEN - (p - custom),
217		      " Last beacon: %ums ago",
218		      elapsed_jiffies_msecs(network->last_scanned));
219	iwe.u.data.length = p - custom;
220	if (iwe.u.data.length)
221		start = iwe_stream_add_point(info, start, stop, &iwe, custom);
222
223	/* Add spectrum management information */
224	iwe.cmd = -1;
225	p = custom;
226	p += scnprintf(p, MAX_CUSTOM_LEN - (p - custom), " Channel flags: ");
227
228	if (libipw_get_channel_flags(ieee, network->channel) &
229	    LIBIPW_CH_INVALID) {
230		iwe.cmd = IWEVCUSTOM;
231		p += scnprintf(p, MAX_CUSTOM_LEN - (p - custom), "INVALID ");
232	}
233
234	if (libipw_get_channel_flags(ieee, network->channel) &
235	    LIBIPW_CH_RADAR_DETECT) {
236		iwe.cmd = IWEVCUSTOM;
237		p += scnprintf(p, MAX_CUSTOM_LEN - (p - custom), "DFS ");
238	}
239
240	if (iwe.cmd == IWEVCUSTOM) {
241		iwe.u.data.length = p - custom;
242		start = iwe_stream_add_point(info, start, stop, &iwe, custom);
243	}
244
245	return start;
246}
247
248#define SCAN_ITEM_SIZE 128
249
250int libipw_wx_get_scan(struct libipw_device *ieee,
251			  struct iw_request_info *info,
252			  union iwreq_data *wrqu, char *extra)
253{
254	struct libipw_network *network;
255	unsigned long flags;
256	int err = 0;
257
258	char *ev = extra;
259	char *stop = ev + wrqu->data.length;
260	int i = 0;
261
262	LIBIPW_DEBUG_WX("Getting scan\n");
263
264	spin_lock_irqsave(&ieee->lock, flags);
265
266	list_for_each_entry(network, &ieee->network_list, list) {
267		i++;
268		if (stop - ev < SCAN_ITEM_SIZE) {
269			err = -E2BIG;
270			break;
271		}
272
273		if (ieee->scan_age == 0 ||
274		    time_after(network->last_scanned + ieee->scan_age, jiffies))
275			ev = libipw_translate_scan(ieee, ev, stop, network,
276						      info);
277		else {
278			LIBIPW_DEBUG_SCAN("Not showing network '%*pE (%pM)' due to age (%ums).\n",
279					  network->ssid_len, network->ssid,
280					  network->bssid,
281					  elapsed_jiffies_msecs(
282					               network->last_scanned));
283		}
284	}
285
286	spin_unlock_irqrestore(&ieee->lock, flags);
287
288	wrqu->data.length = ev - extra;
289	wrqu->data.flags = 0;
290
291	LIBIPW_DEBUG_WX("exit: %d networks returned.\n", i);
292
293	return err;
294}
295
296int libipw_wx_set_encode(struct libipw_device *ieee,
297			    struct iw_request_info *info,
298			    union iwreq_data *wrqu, char *keybuf)
299{
300	struct iw_point *erq = &(wrqu->encoding);
301	struct net_device *dev = ieee->dev;
302	struct libipw_security sec = {
303		.flags = 0
304	};
305	int i, key, key_provided, len;
306	struct lib80211_crypt_data **crypt;
307	int host_crypto = ieee->host_encrypt || ieee->host_decrypt;
308
309	LIBIPW_DEBUG_WX("SET_ENCODE\n");
310
311	key = erq->flags & IW_ENCODE_INDEX;
312	if (key) {
313		if (key > WEP_KEYS)
314			return -EINVAL;
315		key--;
316		key_provided = 1;
317	} else {
318		key_provided = 0;
319		key = ieee->crypt_info.tx_keyidx;
320	}
321
322	LIBIPW_DEBUG_WX("Key: %d [%s]\n", key, key_provided ?
323			   "provided" : "default");
324
325	crypt = &ieee->crypt_info.crypt[key];
326
327	if (erq->flags & IW_ENCODE_DISABLED) {
328		if (key_provided && *crypt) {
329			LIBIPW_DEBUG_WX("Disabling encryption on key %d.\n",
330					   key);
331			lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt);
332		} else
333			LIBIPW_DEBUG_WX("Disabling encryption.\n");
334
335		/* Check all the keys to see if any are still configured,
336		 * and if no key index was provided, de-init them all */
337		for (i = 0; i < WEP_KEYS; i++) {
338			if (ieee->crypt_info.crypt[i] != NULL) {
339				if (key_provided)
340					break;
341				lib80211_crypt_delayed_deinit(&ieee->crypt_info,
342							       &ieee->crypt_info.crypt[i]);
343			}
344		}
345
346		if (i == WEP_KEYS) {
347			sec.enabled = 0;
348			sec.encrypt = 0;
349			sec.level = SEC_LEVEL_0;
350			sec.flags |= SEC_ENABLED | SEC_LEVEL | SEC_ENCRYPT;
351		}
352
353		goto done;
354	}
355
356	sec.enabled = 1;
357	sec.encrypt = 1;
358	sec.flags |= SEC_ENABLED | SEC_ENCRYPT;
359
360	if (*crypt != NULL && (*crypt)->ops != NULL &&
361	    strcmp((*crypt)->ops->name, "WEP") != 0) {
362		/* changing to use WEP; deinit previously used algorithm
363		 * on this key */
364		lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt);
365	}
366
367	if (*crypt == NULL && host_crypto) {
368		struct lib80211_crypt_data *new_crypt;
369
370		/* take WEP into use */
371		new_crypt = kzalloc(sizeof(struct lib80211_crypt_data),
372				    GFP_KERNEL);
373		if (new_crypt == NULL)
374			return -ENOMEM;
375		new_crypt->ops = lib80211_get_crypto_ops("WEP");
376		if (!new_crypt->ops) {
377			request_module("lib80211_crypt_wep");
378			new_crypt->ops = lib80211_get_crypto_ops("WEP");
379		}
380
381		if (new_crypt->ops && try_module_get(new_crypt->ops->owner))
382			new_crypt->priv = new_crypt->ops->init(key);
383
384		if (!new_crypt->ops || !new_crypt->priv) {
385			kfree(new_crypt);
386			new_crypt = NULL;
387
388			printk(KERN_WARNING "%s: could not initialize WEP: "
389			       "load module lib80211_crypt_wep\n", dev->name);
390			return -EOPNOTSUPP;
391		}
392		*crypt = new_crypt;
393	}
394
395	/* If a new key was provided, set it up */
396	if (erq->length > 0) {
397		len = erq->length <= 5 ? 5 : 13;
398		memcpy(sec.keys[key], keybuf, erq->length);
399		if (len > erq->length)
400			memset(sec.keys[key] + erq->length, 0,
401			       len - erq->length);
402		LIBIPW_DEBUG_WX("Setting key %d to '%*pE' (%d:%d bytes)\n",
403				   key, len, sec.keys[key],
404				   erq->length, len);
405		sec.key_sizes[key] = len;
406		if (*crypt)
407			(*crypt)->ops->set_key(sec.keys[key], len, NULL,
408					       (*crypt)->priv);
409		sec.flags |= (1 << key);
410		/* This ensures a key will be activated if no key is
411		 * explicitly set */
412		if (key == sec.active_key)
413			sec.flags |= SEC_ACTIVE_KEY;
414
415	} else {
416		if (host_crypto) {
417			len = (*crypt)->ops->get_key(sec.keys[key], WEP_KEY_LEN,
418						     NULL, (*crypt)->priv);
419			if (len == 0) {
420				/* Set a default key of all 0 */
421				LIBIPW_DEBUG_WX("Setting key %d to all "
422						   "zero.\n", key);
423				memset(sec.keys[key], 0, 13);
424				(*crypt)->ops->set_key(sec.keys[key], 13, NULL,
425						       (*crypt)->priv);
426				sec.key_sizes[key] = 13;
427				sec.flags |= (1 << key);
428			}
429		}
430		/* No key data - just set the default TX key index */
431		if (key_provided) {
432			LIBIPW_DEBUG_WX("Setting key %d to default Tx "
433					   "key.\n", key);
434			ieee->crypt_info.tx_keyidx = key;
435			sec.active_key = key;
436			sec.flags |= SEC_ACTIVE_KEY;
437		}
438	}
439	if (erq->flags & (IW_ENCODE_OPEN | IW_ENCODE_RESTRICTED)) {
440		ieee->open_wep = !(erq->flags & IW_ENCODE_RESTRICTED);
441		sec.auth_mode = ieee->open_wep ? WLAN_AUTH_OPEN :
442		    WLAN_AUTH_SHARED_KEY;
443		sec.flags |= SEC_AUTH_MODE;
444		LIBIPW_DEBUG_WX("Auth: %s\n",
445				   sec.auth_mode == WLAN_AUTH_OPEN ?
446				   "OPEN" : "SHARED KEY");
447	}
448
449	/* For now we just support WEP, so only set that security level...
450	 * TODO: When WPA is added this is one place that needs to change */
451	sec.flags |= SEC_LEVEL;
452	sec.level = SEC_LEVEL_1;	/* 40 and 104 bit WEP */
453	sec.encode_alg[key] = SEC_ALG_WEP;
454
455      done:
456	if (ieee->set_security)
457		ieee->set_security(dev, &sec);
458
459	return 0;
460}
461
462int libipw_wx_get_encode(struct libipw_device *ieee,
463			    struct iw_request_info *info,
464			    union iwreq_data *wrqu, char *keybuf)
465{
466	struct iw_point *erq = &(wrqu->encoding);
467	int len, key;
 
468	struct libipw_security *sec = &ieee->sec;
469
470	LIBIPW_DEBUG_WX("GET_ENCODE\n");
471
472	key = erq->flags & IW_ENCODE_INDEX;
473	if (key) {
474		if (key > WEP_KEYS)
475			return -EINVAL;
476		key--;
477	} else
478		key = ieee->crypt_info.tx_keyidx;
479
 
480	erq->flags = key + 1;
481
482	if (!sec->enabled) {
483		erq->length = 0;
484		erq->flags |= IW_ENCODE_DISABLED;
485		return 0;
486	}
487
488	len = sec->key_sizes[key];
489	memcpy(keybuf, sec->keys[key], len);
490
491	erq->length = len;
492	erq->flags |= IW_ENCODE_ENABLED;
493
494	if (ieee->open_wep)
495		erq->flags |= IW_ENCODE_OPEN;
496	else
497		erq->flags |= IW_ENCODE_RESTRICTED;
498
499	return 0;
500}
501
502int libipw_wx_set_encodeext(struct libipw_device *ieee,
503			       struct iw_request_info *info,
504			       union iwreq_data *wrqu, char *extra)
505{
506	struct net_device *dev = ieee->dev;
507	struct iw_point *encoding = &wrqu->encoding;
508	struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
509	int i, idx, ret = 0;
510	int group_key = 0;
511	const char *alg, *module;
512	struct lib80211_crypto_ops *ops;
513	struct lib80211_crypt_data **crypt;
514
515	struct libipw_security sec = {
516		.flags = 0,
517	};
518
519	idx = encoding->flags & IW_ENCODE_INDEX;
520	if (idx) {
521		if (idx < 1 || idx > WEP_KEYS)
522			return -EINVAL;
523		idx--;
524	} else
525		idx = ieee->crypt_info.tx_keyidx;
526
527	if (ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) {
528		crypt = &ieee->crypt_info.crypt[idx];
529		group_key = 1;
530	} else {
531		/* some Cisco APs use idx>0 for unicast in dynamic WEP */
532		if (idx != 0 && ext->alg != IW_ENCODE_ALG_WEP)
533			return -EINVAL;
534		if (ieee->iw_mode == IW_MODE_INFRA)
535			crypt = &ieee->crypt_info.crypt[idx];
536		else
537			return -EINVAL;
538	}
539
540	sec.flags |= SEC_ENABLED | SEC_ENCRYPT;
541	if ((encoding->flags & IW_ENCODE_DISABLED) ||
542	    ext->alg == IW_ENCODE_ALG_NONE) {
543		if (*crypt)
544			lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt);
545
546		for (i = 0; i < WEP_KEYS; i++)
547			if (ieee->crypt_info.crypt[i] != NULL)
548				break;
549
550		if (i == WEP_KEYS) {
551			sec.enabled = 0;
552			sec.encrypt = 0;
553			sec.level = SEC_LEVEL_0;
554			sec.flags |= SEC_LEVEL;
555		}
556		goto done;
557	}
558
559	sec.enabled = 1;
560	sec.encrypt = 1;
561
562	if (group_key ? !ieee->host_mc_decrypt :
563	    !(ieee->host_encrypt || ieee->host_decrypt ||
564	      ieee->host_encrypt_msdu))
565		goto skip_host_crypt;
566
567	switch (ext->alg) {
568	case IW_ENCODE_ALG_WEP:
569		alg = "WEP";
570		module = "lib80211_crypt_wep";
571		break;
572	case IW_ENCODE_ALG_TKIP:
573		alg = "TKIP";
574		module = "lib80211_crypt_tkip";
575		break;
576	case IW_ENCODE_ALG_CCMP:
577		alg = "CCMP";
578		module = "lib80211_crypt_ccmp";
579		break;
580	default:
581		LIBIPW_DEBUG_WX("%s: unknown crypto alg %d\n",
582				   dev->name, ext->alg);
583		ret = -EINVAL;
584		goto done;
585	}
586
587	ops = lib80211_get_crypto_ops(alg);
588	if (ops == NULL) {
589		request_module(module);
590		ops = lib80211_get_crypto_ops(alg);
591	}
592	if (ops == NULL) {
593		LIBIPW_DEBUG_WX("%s: unknown crypto alg %d\n",
594				   dev->name, ext->alg);
595		ret = -EINVAL;
596		goto done;
597	}
598
599	if (*crypt == NULL || (*crypt)->ops != ops) {
600		struct lib80211_crypt_data *new_crypt;
601
602		lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt);
603
604		new_crypt = kzalloc(sizeof(*new_crypt), GFP_KERNEL);
605		if (new_crypt == NULL) {
606			ret = -ENOMEM;
607			goto done;
608		}
609		new_crypt->ops = ops;
610		if (new_crypt->ops && try_module_get(new_crypt->ops->owner))
611			new_crypt->priv = new_crypt->ops->init(idx);
612		if (new_crypt->priv == NULL) {
613			kfree(new_crypt);
614			ret = -EINVAL;
615			goto done;
616		}
617		*crypt = new_crypt;
618	}
619
620	if (ext->key_len > 0 && (*crypt)->ops->set_key &&
621	    (*crypt)->ops->set_key(ext->key, ext->key_len, ext->rx_seq,
622				   (*crypt)->priv) < 0) {
623		LIBIPW_DEBUG_WX("%s: key setting failed\n", dev->name);
624		ret = -EINVAL;
625		goto done;
626	}
627
628      skip_host_crypt:
629	if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) {
630		ieee->crypt_info.tx_keyidx = idx;
631		sec.active_key = idx;
632		sec.flags |= SEC_ACTIVE_KEY;
633	}
634
635	if (ext->alg != IW_ENCODE_ALG_NONE) {
636		int key_len = clamp_val(ext->key_len, 0, SCM_KEY_LEN);
637
638		memcpy(sec.keys[idx], ext->key, key_len);
639		sec.key_sizes[idx] = key_len;
640		sec.flags |= (1 << idx);
641		if (ext->alg == IW_ENCODE_ALG_WEP) {
642			sec.encode_alg[idx] = SEC_ALG_WEP;
643			sec.flags |= SEC_LEVEL;
644			sec.level = SEC_LEVEL_1;
645		} else if (ext->alg == IW_ENCODE_ALG_TKIP) {
646			sec.encode_alg[idx] = SEC_ALG_TKIP;
647			sec.flags |= SEC_LEVEL;
648			sec.level = SEC_LEVEL_2;
649		} else if (ext->alg == IW_ENCODE_ALG_CCMP) {
650			sec.encode_alg[idx] = SEC_ALG_CCMP;
651			sec.flags |= SEC_LEVEL;
652			sec.level = SEC_LEVEL_3;
653		}
654		/* Don't set sec level for group keys. */
655		if (group_key)
656			sec.flags &= ~SEC_LEVEL;
657	}
658      done:
659	if (ieee->set_security)
660		ieee->set_security(dev, &sec);
661
662	return ret;
663}
664
665int libipw_wx_get_encodeext(struct libipw_device *ieee,
666			       struct iw_request_info *info,
667			       union iwreq_data *wrqu, char *extra)
668{
669	struct iw_point *encoding = &wrqu->encoding;
670	struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
671	struct libipw_security *sec = &ieee->sec;
672	int idx, max_key_len;
673
674	max_key_len = encoding->length - sizeof(*ext);
675	if (max_key_len < 0)
676		return -EINVAL;
677
678	idx = encoding->flags & IW_ENCODE_INDEX;
679	if (idx) {
680		if (idx < 1 || idx > WEP_KEYS)
681			return -EINVAL;
682		idx--;
683	} else
684		idx = ieee->crypt_info.tx_keyidx;
685
686	if (!(ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) &&
687	    ext->alg != IW_ENCODE_ALG_WEP)
688		if (idx != 0 || ieee->iw_mode != IW_MODE_INFRA)
689			return -EINVAL;
690
691	encoding->flags = idx + 1;
692	memset(ext, 0, sizeof(*ext));
693
694	if (!sec->enabled) {
695		ext->alg = IW_ENCODE_ALG_NONE;
696		ext->key_len = 0;
697		encoding->flags |= IW_ENCODE_DISABLED;
698	} else {
699		if (sec->encode_alg[idx] == SEC_ALG_WEP)
700			ext->alg = IW_ENCODE_ALG_WEP;
701		else if (sec->encode_alg[idx] == SEC_ALG_TKIP)
702			ext->alg = IW_ENCODE_ALG_TKIP;
703		else if (sec->encode_alg[idx] == SEC_ALG_CCMP)
704			ext->alg = IW_ENCODE_ALG_CCMP;
705		else
706			return -EINVAL;
707
708		ext->key_len = sec->key_sizes[idx];
709		memcpy(ext->key, sec->keys[idx], ext->key_len);
710		encoding->flags |= IW_ENCODE_ENABLED;
711		if (ext->key_len &&
712		    (ext->alg == IW_ENCODE_ALG_TKIP ||
713		     ext->alg == IW_ENCODE_ALG_CCMP))
714			ext->ext_flags |= IW_ENCODE_EXT_TX_SEQ_VALID;
715
716	}
717
718	return 0;
719}
720
721EXPORT_SYMBOL(libipw_wx_set_encodeext);
722EXPORT_SYMBOL(libipw_wx_get_encodeext);
723
724EXPORT_SYMBOL(libipw_wx_get_scan);
725EXPORT_SYMBOL(libipw_wx_set_encode);
726EXPORT_SYMBOL(libipw_wx_get_encode);
v4.17
 
  1/******************************************************************************
  2
  3  Copyright(c) 2004-2005 Intel Corporation. All rights reserved.
  4
  5  Portions of this file are based on the WEP enablement code provided by the
  6  Host AP project hostap-drivers v0.1.3
  7  Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen
  8  <j@w1.fi>
  9  Copyright (c) 2002-2003, Jouni Malinen <j@w1.fi>
 10
 11  This program is free software; you can redistribute it and/or modify it
 12  under the terms of version 2 of the GNU General Public License as
 13  published by the Free Software Foundation.
 14
 15  This program is distributed in the hope that it will be useful, but WITHOUT
 16  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 17  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 18  more details.
 19
 20  You should have received a copy of the GNU General Public License along with
 21  this program; if not, write to the Free Software Foundation, Inc., 59
 22  Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 23
 24  The full GNU General Public License is included in this distribution in the
 25  file called LICENSE.
 26
 27  Contact Information:
 28  Intel Linux Wireless <ilw@linux.intel.com>
 29  Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
 30
 31******************************************************************************/
 32
 33#include <linux/hardirq.h>
 34#include <linux/kmod.h>
 35#include <linux/slab.h>
 36#include <linux/module.h>
 37#include <linux/jiffies.h>
 38
 39#include <net/lib80211.h>
 40#include <linux/wireless.h>
 41
 42#include "libipw.h"
 43
 44static const char *libipw_modes[] = {
 45	"?", "a", "b", "ab", "g", "ag", "bg", "abg"
 46};
 47
 48static inline unsigned int elapsed_jiffies_msecs(unsigned long start)
 49{
 50	unsigned long end = jiffies;
 51
 52	if (end >= start)
 53		return jiffies_to_msecs(end - start);
 54
 55	return jiffies_to_msecs(end + (MAX_JIFFY_OFFSET - start) + 1);
 56}
 57
 58#define MAX_CUSTOM_LEN 64
 59static char *libipw_translate_scan(struct libipw_device *ieee,
 60				      char *start, char *stop,
 61				      struct libipw_network *network,
 62				      struct iw_request_info *info)
 63{
 64	char custom[MAX_CUSTOM_LEN];
 65	char *p;
 66	struct iw_event iwe;
 67	int i, j;
 68	char *current_val;	/* For rates */
 69	u8 rate;
 70
 71	/* First entry *MUST* be the AP MAC address */
 72	iwe.cmd = SIOCGIWAP;
 73	iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
 74	memcpy(iwe.u.ap_addr.sa_data, network->bssid, ETH_ALEN);
 75	start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_ADDR_LEN);
 76
 77	/* Remaining entries will be displayed in the order we provide them */
 78
 79	/* Add the ESSID */
 80	iwe.cmd = SIOCGIWESSID;
 81	iwe.u.data.flags = 1;
 82	iwe.u.data.length = min(network->ssid_len, (u8) 32);
 83	start = iwe_stream_add_point(info, start, stop,
 84				     &iwe, network->ssid);
 85
 86	/* Add the protocol name */
 87	iwe.cmd = SIOCGIWNAME;
 88	snprintf(iwe.u.name, IFNAMSIZ, "IEEE 802.11%s",
 89		 libipw_modes[network->mode]);
 90	start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_CHAR_LEN);
 91
 92	/* Add mode */
 93	iwe.cmd = SIOCGIWMODE;
 94	if (network->capability & (WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS)) {
 95		if (network->capability & WLAN_CAPABILITY_ESS)
 96			iwe.u.mode = IW_MODE_MASTER;
 97		else
 98			iwe.u.mode = IW_MODE_ADHOC;
 99
100		start = iwe_stream_add_event(info, start, stop,
101					     &iwe, IW_EV_UINT_LEN);
102	}
103
104	/* Add channel and frequency */
105	/* Note : userspace automatically computes channel using iwrange */
106	iwe.cmd = SIOCGIWFREQ;
107	iwe.u.freq.m = libipw_channel_to_freq(ieee, network->channel);
108	iwe.u.freq.e = 6;
109	iwe.u.freq.i = 0;
110	start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_FREQ_LEN);
111
112	/* Add encryption capability */
113	iwe.cmd = SIOCGIWENCODE;
114	if (network->capability & WLAN_CAPABILITY_PRIVACY)
115		iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
116	else
117		iwe.u.data.flags = IW_ENCODE_DISABLED;
118	iwe.u.data.length = 0;
119	start = iwe_stream_add_point(info, start, stop,
120				     &iwe, network->ssid);
121
122	/* Add basic and extended rates */
123	/* Rate : stuffing multiple values in a single event require a bit
124	 * more of magic - Jean II */
125	current_val = start + iwe_stream_lcp_len(info);
126	iwe.cmd = SIOCGIWRATE;
127	/* Those two flags are ignored... */
128	iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0;
129
130	for (i = 0, j = 0; i < network->rates_len;) {
131		if (j < network->rates_ex_len &&
132		    ((network->rates_ex[j] & 0x7F) <
133		     (network->rates[i] & 0x7F)))
134			rate = network->rates_ex[j++] & 0x7F;
135		else
136			rate = network->rates[i++] & 0x7F;
137		/* Bit rate given in 500 kb/s units (+ 0x80) */
138		iwe.u.bitrate.value = ((rate & 0x7f) * 500000);
139		/* Add new value to event */
140		current_val = iwe_stream_add_value(info, start, current_val,
141						   stop, &iwe, IW_EV_PARAM_LEN);
142	}
143	for (; j < network->rates_ex_len; j++) {
144		rate = network->rates_ex[j] & 0x7F;
145		/* Bit rate given in 500 kb/s units (+ 0x80) */
146		iwe.u.bitrate.value = ((rate & 0x7f) * 500000);
147		/* Add new value to event */
148		current_val = iwe_stream_add_value(info, start, current_val,
149						   stop, &iwe, IW_EV_PARAM_LEN);
150	}
151	/* Check if we added any rate */
152	if ((current_val - start) > iwe_stream_lcp_len(info))
153		start = current_val;
154
155	/* Add quality statistics */
156	iwe.cmd = IWEVQUAL;
157	iwe.u.qual.updated = IW_QUAL_QUAL_UPDATED | IW_QUAL_LEVEL_UPDATED |
158	    IW_QUAL_NOISE_UPDATED;
159
160	if (!(network->stats.mask & LIBIPW_STATMASK_RSSI)) {
161		iwe.u.qual.updated |= IW_QUAL_QUAL_INVALID |
162		    IW_QUAL_LEVEL_INVALID;
163		iwe.u.qual.qual = 0;
164	} else {
165		if (ieee->perfect_rssi == ieee->worst_rssi)
166			iwe.u.qual.qual = 100;
167		else
168			iwe.u.qual.qual =
169			    (100 *
170			     (ieee->perfect_rssi - ieee->worst_rssi) *
171			     (ieee->perfect_rssi - ieee->worst_rssi) -
172			     (ieee->perfect_rssi - network->stats.rssi) *
173			     (15 * (ieee->perfect_rssi - ieee->worst_rssi) +
174			      62 * (ieee->perfect_rssi -
175				    network->stats.rssi))) /
176			    ((ieee->perfect_rssi -
177			      ieee->worst_rssi) * (ieee->perfect_rssi -
178						   ieee->worst_rssi));
179		if (iwe.u.qual.qual > 100)
180			iwe.u.qual.qual = 100;
181		else if (iwe.u.qual.qual < 1)
182			iwe.u.qual.qual = 0;
183	}
184
185	if (!(network->stats.mask & LIBIPW_STATMASK_NOISE)) {
186		iwe.u.qual.updated |= IW_QUAL_NOISE_INVALID;
187		iwe.u.qual.noise = 0;
188	} else {
189		iwe.u.qual.noise = network->stats.noise;
190	}
191
192	if (!(network->stats.mask & LIBIPW_STATMASK_SIGNAL)) {
193		iwe.u.qual.updated |= IW_QUAL_LEVEL_INVALID;
194		iwe.u.qual.level = 0;
195	} else {
196		iwe.u.qual.level = network->stats.signal;
197	}
198
199	start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_QUAL_LEN);
200
201	iwe.cmd = IWEVCUSTOM;
202	p = custom;
203
204	iwe.u.data.length = p - custom;
205	if (iwe.u.data.length)
206		start = iwe_stream_add_point(info, start, stop, &iwe, custom);
207
208	memset(&iwe, 0, sizeof(iwe));
209	if (network->wpa_ie_len) {
210		char buf[MAX_WPA_IE_LEN];
211		memcpy(buf, network->wpa_ie, network->wpa_ie_len);
212		iwe.cmd = IWEVGENIE;
213		iwe.u.data.length = network->wpa_ie_len;
214		start = iwe_stream_add_point(info, start, stop, &iwe, buf);
215	}
216
217	memset(&iwe, 0, sizeof(iwe));
218	if (network->rsn_ie_len) {
219		char buf[MAX_WPA_IE_LEN];
220		memcpy(buf, network->rsn_ie, network->rsn_ie_len);
221		iwe.cmd = IWEVGENIE;
222		iwe.u.data.length = network->rsn_ie_len;
223		start = iwe_stream_add_point(info, start, stop, &iwe, buf);
224	}
225
226	/* Add EXTRA: Age to display seconds since last beacon/probe response
227	 * for given network. */
228	iwe.cmd = IWEVCUSTOM;
229	p = custom;
230	p += snprintf(p, MAX_CUSTOM_LEN - (p - custom),
231		      " Last beacon: %ums ago",
232		      elapsed_jiffies_msecs(network->last_scanned));
233	iwe.u.data.length = p - custom;
234	if (iwe.u.data.length)
235		start = iwe_stream_add_point(info, start, stop, &iwe, custom);
236
237	/* Add spectrum management information */
238	iwe.cmd = -1;
239	p = custom;
240	p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), " Channel flags: ");
241
242	if (libipw_get_channel_flags(ieee, network->channel) &
243	    LIBIPW_CH_INVALID) {
244		iwe.cmd = IWEVCUSTOM;
245		p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), "INVALID ");
246	}
247
248	if (libipw_get_channel_flags(ieee, network->channel) &
249	    LIBIPW_CH_RADAR_DETECT) {
250		iwe.cmd = IWEVCUSTOM;
251		p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), "DFS ");
252	}
253
254	if (iwe.cmd == IWEVCUSTOM) {
255		iwe.u.data.length = p - custom;
256		start = iwe_stream_add_point(info, start, stop, &iwe, custom);
257	}
258
259	return start;
260}
261
262#define SCAN_ITEM_SIZE 128
263
264int libipw_wx_get_scan(struct libipw_device *ieee,
265			  struct iw_request_info *info,
266			  union iwreq_data *wrqu, char *extra)
267{
268	struct libipw_network *network;
269	unsigned long flags;
270	int err = 0;
271
272	char *ev = extra;
273	char *stop = ev + wrqu->data.length;
274	int i = 0;
275
276	LIBIPW_DEBUG_WX("Getting scan\n");
277
278	spin_lock_irqsave(&ieee->lock, flags);
279
280	list_for_each_entry(network, &ieee->network_list, list) {
281		i++;
282		if (stop - ev < SCAN_ITEM_SIZE) {
283			err = -E2BIG;
284			break;
285		}
286
287		if (ieee->scan_age == 0 ||
288		    time_after(network->last_scanned + ieee->scan_age, jiffies))
289			ev = libipw_translate_scan(ieee, ev, stop, network,
290						      info);
291		else {
292			LIBIPW_DEBUG_SCAN("Not showing network '%*pE (%pM)' due to age (%ums).\n",
293					  network->ssid_len, network->ssid,
294					  network->bssid,
295					  elapsed_jiffies_msecs(
296					               network->last_scanned));
297		}
298	}
299
300	spin_unlock_irqrestore(&ieee->lock, flags);
301
302	wrqu->data.length = ev - extra;
303	wrqu->data.flags = 0;
304
305	LIBIPW_DEBUG_WX("exit: %d networks returned.\n", i);
306
307	return err;
308}
309
310int libipw_wx_set_encode(struct libipw_device *ieee,
311			    struct iw_request_info *info,
312			    union iwreq_data *wrqu, char *keybuf)
313{
314	struct iw_point *erq = &(wrqu->encoding);
315	struct net_device *dev = ieee->dev;
316	struct libipw_security sec = {
317		.flags = 0
318	};
319	int i, key, key_provided, len;
320	struct lib80211_crypt_data **crypt;
321	int host_crypto = ieee->host_encrypt || ieee->host_decrypt;
322
323	LIBIPW_DEBUG_WX("SET_ENCODE\n");
324
325	key = erq->flags & IW_ENCODE_INDEX;
326	if (key) {
327		if (key > WEP_KEYS)
328			return -EINVAL;
329		key--;
330		key_provided = 1;
331	} else {
332		key_provided = 0;
333		key = ieee->crypt_info.tx_keyidx;
334	}
335
336	LIBIPW_DEBUG_WX("Key: %d [%s]\n", key, key_provided ?
337			   "provided" : "default");
338
339	crypt = &ieee->crypt_info.crypt[key];
340
341	if (erq->flags & IW_ENCODE_DISABLED) {
342		if (key_provided && *crypt) {
343			LIBIPW_DEBUG_WX("Disabling encryption on key %d.\n",
344					   key);
345			lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt);
346		} else
347			LIBIPW_DEBUG_WX("Disabling encryption.\n");
348
349		/* Check all the keys to see if any are still configured,
350		 * and if no key index was provided, de-init them all */
351		for (i = 0; i < WEP_KEYS; i++) {
352			if (ieee->crypt_info.crypt[i] != NULL) {
353				if (key_provided)
354					break;
355				lib80211_crypt_delayed_deinit(&ieee->crypt_info,
356							       &ieee->crypt_info.crypt[i]);
357			}
358		}
359
360		if (i == WEP_KEYS) {
361			sec.enabled = 0;
362			sec.encrypt = 0;
363			sec.level = SEC_LEVEL_0;
364			sec.flags |= SEC_ENABLED | SEC_LEVEL | SEC_ENCRYPT;
365		}
366
367		goto done;
368	}
369
370	sec.enabled = 1;
371	sec.encrypt = 1;
372	sec.flags |= SEC_ENABLED | SEC_ENCRYPT;
373
374	if (*crypt != NULL && (*crypt)->ops != NULL &&
375	    strcmp((*crypt)->ops->name, "WEP") != 0) {
376		/* changing to use WEP; deinit previously used algorithm
377		 * on this key */
378		lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt);
379	}
380
381	if (*crypt == NULL && host_crypto) {
382		struct lib80211_crypt_data *new_crypt;
383
384		/* take WEP into use */
385		new_crypt = kzalloc(sizeof(struct lib80211_crypt_data),
386				    GFP_KERNEL);
387		if (new_crypt == NULL)
388			return -ENOMEM;
389		new_crypt->ops = lib80211_get_crypto_ops("WEP");
390		if (!new_crypt->ops) {
391			request_module("lib80211_crypt_wep");
392			new_crypt->ops = lib80211_get_crypto_ops("WEP");
393		}
394
395		if (new_crypt->ops && try_module_get(new_crypt->ops->owner))
396			new_crypt->priv = new_crypt->ops->init(key);
397
398		if (!new_crypt->ops || !new_crypt->priv) {
399			kfree(new_crypt);
400			new_crypt = NULL;
401
402			printk(KERN_WARNING "%s: could not initialize WEP: "
403			       "load module lib80211_crypt_wep\n", dev->name);
404			return -EOPNOTSUPP;
405		}
406		*crypt = new_crypt;
407	}
408
409	/* If a new key was provided, set it up */
410	if (erq->length > 0) {
411		len = erq->length <= 5 ? 5 : 13;
412		memcpy(sec.keys[key], keybuf, erq->length);
413		if (len > erq->length)
414			memset(sec.keys[key] + erq->length, 0,
415			       len - erq->length);
416		LIBIPW_DEBUG_WX("Setting key %d to '%*pE' (%d:%d bytes)\n",
417				   key, len, sec.keys[key],
418				   erq->length, len);
419		sec.key_sizes[key] = len;
420		if (*crypt)
421			(*crypt)->ops->set_key(sec.keys[key], len, NULL,
422					       (*crypt)->priv);
423		sec.flags |= (1 << key);
424		/* This ensures a key will be activated if no key is
425		 * explicitly set */
426		if (key == sec.active_key)
427			sec.flags |= SEC_ACTIVE_KEY;
428
429	} else {
430		if (host_crypto) {
431			len = (*crypt)->ops->get_key(sec.keys[key], WEP_KEY_LEN,
432						     NULL, (*crypt)->priv);
433			if (len == 0) {
434				/* Set a default key of all 0 */
435				LIBIPW_DEBUG_WX("Setting key %d to all "
436						   "zero.\n", key);
437				memset(sec.keys[key], 0, 13);
438				(*crypt)->ops->set_key(sec.keys[key], 13, NULL,
439						       (*crypt)->priv);
440				sec.key_sizes[key] = 13;
441				sec.flags |= (1 << key);
442			}
443		}
444		/* No key data - just set the default TX key index */
445		if (key_provided) {
446			LIBIPW_DEBUG_WX("Setting key %d to default Tx "
447					   "key.\n", key);
448			ieee->crypt_info.tx_keyidx = key;
449			sec.active_key = key;
450			sec.flags |= SEC_ACTIVE_KEY;
451		}
452	}
453	if (erq->flags & (IW_ENCODE_OPEN | IW_ENCODE_RESTRICTED)) {
454		ieee->open_wep = !(erq->flags & IW_ENCODE_RESTRICTED);
455		sec.auth_mode = ieee->open_wep ? WLAN_AUTH_OPEN :
456		    WLAN_AUTH_SHARED_KEY;
457		sec.flags |= SEC_AUTH_MODE;
458		LIBIPW_DEBUG_WX("Auth: %s\n",
459				   sec.auth_mode == WLAN_AUTH_OPEN ?
460				   "OPEN" : "SHARED KEY");
461	}
462
463	/* For now we just support WEP, so only set that security level...
464	 * TODO: When WPA is added this is one place that needs to change */
465	sec.flags |= SEC_LEVEL;
466	sec.level = SEC_LEVEL_1;	/* 40 and 104 bit WEP */
467	sec.encode_alg[key] = SEC_ALG_WEP;
468
469      done:
470	if (ieee->set_security)
471		ieee->set_security(dev, &sec);
472
473	return 0;
474}
475
476int libipw_wx_get_encode(struct libipw_device *ieee,
477			    struct iw_request_info *info,
478			    union iwreq_data *wrqu, char *keybuf)
479{
480	struct iw_point *erq = &(wrqu->encoding);
481	int len, key;
482	struct lib80211_crypt_data *crypt;
483	struct libipw_security *sec = &ieee->sec;
484
485	LIBIPW_DEBUG_WX("GET_ENCODE\n");
486
487	key = erq->flags & IW_ENCODE_INDEX;
488	if (key) {
489		if (key > WEP_KEYS)
490			return -EINVAL;
491		key--;
492	} else
493		key = ieee->crypt_info.tx_keyidx;
494
495	crypt = ieee->crypt_info.crypt[key];
496	erq->flags = key + 1;
497
498	if (!sec->enabled) {
499		erq->length = 0;
500		erq->flags |= IW_ENCODE_DISABLED;
501		return 0;
502	}
503
504	len = sec->key_sizes[key];
505	memcpy(keybuf, sec->keys[key], len);
506
507	erq->length = len;
508	erq->flags |= IW_ENCODE_ENABLED;
509
510	if (ieee->open_wep)
511		erq->flags |= IW_ENCODE_OPEN;
512	else
513		erq->flags |= IW_ENCODE_RESTRICTED;
514
515	return 0;
516}
517
518int libipw_wx_set_encodeext(struct libipw_device *ieee,
519			       struct iw_request_info *info,
520			       union iwreq_data *wrqu, char *extra)
521{
522	struct net_device *dev = ieee->dev;
523	struct iw_point *encoding = &wrqu->encoding;
524	struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
525	int i, idx, ret = 0;
526	int group_key = 0;
527	const char *alg, *module;
528	struct lib80211_crypto_ops *ops;
529	struct lib80211_crypt_data **crypt;
530
531	struct libipw_security sec = {
532		.flags = 0,
533	};
534
535	idx = encoding->flags & IW_ENCODE_INDEX;
536	if (idx) {
537		if (idx < 1 || idx > WEP_KEYS)
538			return -EINVAL;
539		idx--;
540	} else
541		idx = ieee->crypt_info.tx_keyidx;
542
543	if (ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) {
544		crypt = &ieee->crypt_info.crypt[idx];
545		group_key = 1;
546	} else {
547		/* some Cisco APs use idx>0 for unicast in dynamic WEP */
548		if (idx != 0 && ext->alg != IW_ENCODE_ALG_WEP)
549			return -EINVAL;
550		if (ieee->iw_mode == IW_MODE_INFRA)
551			crypt = &ieee->crypt_info.crypt[idx];
552		else
553			return -EINVAL;
554	}
555
556	sec.flags |= SEC_ENABLED | SEC_ENCRYPT;
557	if ((encoding->flags & IW_ENCODE_DISABLED) ||
558	    ext->alg == IW_ENCODE_ALG_NONE) {
559		if (*crypt)
560			lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt);
561
562		for (i = 0; i < WEP_KEYS; i++)
563			if (ieee->crypt_info.crypt[i] != NULL)
564				break;
565
566		if (i == WEP_KEYS) {
567			sec.enabled = 0;
568			sec.encrypt = 0;
569			sec.level = SEC_LEVEL_0;
570			sec.flags |= SEC_LEVEL;
571		}
572		goto done;
573	}
574
575	sec.enabled = 1;
576	sec.encrypt = 1;
577
578	if (group_key ? !ieee->host_mc_decrypt :
579	    !(ieee->host_encrypt || ieee->host_decrypt ||
580	      ieee->host_encrypt_msdu))
581		goto skip_host_crypt;
582
583	switch (ext->alg) {
584	case IW_ENCODE_ALG_WEP:
585		alg = "WEP";
586		module = "lib80211_crypt_wep";
587		break;
588	case IW_ENCODE_ALG_TKIP:
589		alg = "TKIP";
590		module = "lib80211_crypt_tkip";
591		break;
592	case IW_ENCODE_ALG_CCMP:
593		alg = "CCMP";
594		module = "lib80211_crypt_ccmp";
595		break;
596	default:
597		LIBIPW_DEBUG_WX("%s: unknown crypto alg %d\n",
598				   dev->name, ext->alg);
599		ret = -EINVAL;
600		goto done;
601	}
602
603	ops = lib80211_get_crypto_ops(alg);
604	if (ops == NULL) {
605		request_module(module);
606		ops = lib80211_get_crypto_ops(alg);
607	}
608	if (ops == NULL) {
609		LIBIPW_DEBUG_WX("%s: unknown crypto alg %d\n",
610				   dev->name, ext->alg);
611		ret = -EINVAL;
612		goto done;
613	}
614
615	if (*crypt == NULL || (*crypt)->ops != ops) {
616		struct lib80211_crypt_data *new_crypt;
617
618		lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt);
619
620		new_crypt = kzalloc(sizeof(*new_crypt), GFP_KERNEL);
621		if (new_crypt == NULL) {
622			ret = -ENOMEM;
623			goto done;
624		}
625		new_crypt->ops = ops;
626		if (new_crypt->ops && try_module_get(new_crypt->ops->owner))
627			new_crypt->priv = new_crypt->ops->init(idx);
628		if (new_crypt->priv == NULL) {
629			kfree(new_crypt);
630			ret = -EINVAL;
631			goto done;
632		}
633		*crypt = new_crypt;
634	}
635
636	if (ext->key_len > 0 && (*crypt)->ops->set_key &&
637	    (*crypt)->ops->set_key(ext->key, ext->key_len, ext->rx_seq,
638				   (*crypt)->priv) < 0) {
639		LIBIPW_DEBUG_WX("%s: key setting failed\n", dev->name);
640		ret = -EINVAL;
641		goto done;
642	}
643
644      skip_host_crypt:
645	if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) {
646		ieee->crypt_info.tx_keyidx = idx;
647		sec.active_key = idx;
648		sec.flags |= SEC_ACTIVE_KEY;
649	}
650
651	if (ext->alg != IW_ENCODE_ALG_NONE) {
652		memcpy(sec.keys[idx], ext->key, ext->key_len);
653		sec.key_sizes[idx] = ext->key_len;
 
 
654		sec.flags |= (1 << idx);
655		if (ext->alg == IW_ENCODE_ALG_WEP) {
656			sec.encode_alg[idx] = SEC_ALG_WEP;
657			sec.flags |= SEC_LEVEL;
658			sec.level = SEC_LEVEL_1;
659		} else if (ext->alg == IW_ENCODE_ALG_TKIP) {
660			sec.encode_alg[idx] = SEC_ALG_TKIP;
661			sec.flags |= SEC_LEVEL;
662			sec.level = SEC_LEVEL_2;
663		} else if (ext->alg == IW_ENCODE_ALG_CCMP) {
664			sec.encode_alg[idx] = SEC_ALG_CCMP;
665			sec.flags |= SEC_LEVEL;
666			sec.level = SEC_LEVEL_3;
667		}
668		/* Don't set sec level for group keys. */
669		if (group_key)
670			sec.flags &= ~SEC_LEVEL;
671	}
672      done:
673	if (ieee->set_security)
674		ieee->set_security(dev, &sec);
675
676	return ret;
677}
678
679int libipw_wx_get_encodeext(struct libipw_device *ieee,
680			       struct iw_request_info *info,
681			       union iwreq_data *wrqu, char *extra)
682{
683	struct iw_point *encoding = &wrqu->encoding;
684	struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
685	struct libipw_security *sec = &ieee->sec;
686	int idx, max_key_len;
687
688	max_key_len = encoding->length - sizeof(*ext);
689	if (max_key_len < 0)
690		return -EINVAL;
691
692	idx = encoding->flags & IW_ENCODE_INDEX;
693	if (idx) {
694		if (idx < 1 || idx > WEP_KEYS)
695			return -EINVAL;
696		idx--;
697	} else
698		idx = ieee->crypt_info.tx_keyidx;
699
700	if (!(ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) &&
701	    ext->alg != IW_ENCODE_ALG_WEP)
702		if (idx != 0 || ieee->iw_mode != IW_MODE_INFRA)
703			return -EINVAL;
704
705	encoding->flags = idx + 1;
706	memset(ext, 0, sizeof(*ext));
707
708	if (!sec->enabled) {
709		ext->alg = IW_ENCODE_ALG_NONE;
710		ext->key_len = 0;
711		encoding->flags |= IW_ENCODE_DISABLED;
712	} else {
713		if (sec->encode_alg[idx] == SEC_ALG_WEP)
714			ext->alg = IW_ENCODE_ALG_WEP;
715		else if (sec->encode_alg[idx] == SEC_ALG_TKIP)
716			ext->alg = IW_ENCODE_ALG_TKIP;
717		else if (sec->encode_alg[idx] == SEC_ALG_CCMP)
718			ext->alg = IW_ENCODE_ALG_CCMP;
719		else
720			return -EINVAL;
721
722		ext->key_len = sec->key_sizes[idx];
723		memcpy(ext->key, sec->keys[idx], ext->key_len);
724		encoding->flags |= IW_ENCODE_ENABLED;
725		if (ext->key_len &&
726		    (ext->alg == IW_ENCODE_ALG_TKIP ||
727		     ext->alg == IW_ENCODE_ALG_CCMP))
728			ext->ext_flags |= IW_ENCODE_EXT_TX_SEQ_VALID;
729
730	}
731
732	return 0;
733}
734
735EXPORT_SYMBOL(libipw_wx_set_encodeext);
736EXPORT_SYMBOL(libipw_wx_get_encodeext);
737
738EXPORT_SYMBOL(libipw_wx_get_scan);
739EXPORT_SYMBOL(libipw_wx_set_encode);
740EXPORT_SYMBOL(libipw_wx_get_encode);