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