brintos

brintos / linux-shallow public Read only

0
0
Text · 19.2 KiB · dbc7153 Raw
727 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/******************************************************************************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 the7  Host AP project hostap-drivers v0.1.38  Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen9  <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-649716 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 6445static 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		else84			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	else103		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 bit110	 * 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		else122			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		else154			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 response213	 * 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 128249 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 = 0304	};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		} else333			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 algorithm363		 * 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 is411		 * 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	} else478		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	else497		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	const 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	} else525		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		else537			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	} else684		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		else706			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);727