File Index
Files and directories
Sign in
FILE

libipw_wx.c

File preview with syntax highlighting.

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