cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

libipw_wx.c (19636B)


      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);