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 44 static const char *libipw_modes[] = { 45 "?", "a", "b", "ab", "g", "ag", "bg", "abg" 46 }; 47 48 static 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 59 static 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 264 int 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 310 int 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 476 int 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 libipw_security *sec = &ieee->sec; 483 484 LIBIPW_DEBUG_WX("GET_ENCODE\n"); 485 486 key = erq->flags & IW_ENCODE_INDEX; 487 if (key) { 488 if (key > WEP_KEYS) 489 return -EINVAL; 490 key--; 491 } else 492 key = ieee->crypt_info.tx_keyidx; 493 494 erq->flags = key + 1; 495 496 if (!sec->enabled) { 497 erq->length = 0; 498 erq->flags |= IW_ENCODE_DISABLED; 499 return 0; 500 } 501 502 len = sec->key_sizes[key]; 503 memcpy(keybuf, sec->keys[key], len); 504 505 erq->length = len; 506 erq->flags |= IW_ENCODE_ENABLED; 507 508 if (ieee->open_wep) 509 erq->flags |= IW_ENCODE_OPEN; 510 else 511 erq->flags |= IW_ENCODE_RESTRICTED; 512 513 return 0; 514 } 515 516 int libipw_wx_set_encodeext(struct libipw_device *ieee, 517 struct iw_request_info *info, 518 union iwreq_data *wrqu, char *extra) 519 { 520 struct net_device *dev = ieee->dev; 521 struct iw_point *encoding = &wrqu->encoding; 522 struct iw_encode_ext *ext = (struct iw_encode_ext *)extra; 523 int i, idx, ret = 0; 524 int group_key = 0; 525 const char *alg, *module; 526 struct lib80211_crypto_ops *ops; 527 struct lib80211_crypt_data **crypt; 528 529 struct libipw_security sec = { 530 .flags = 0, 531 }; 532 533 idx = encoding->flags & IW_ENCODE_INDEX; 534 if (idx) { 535 if (idx < 1 || idx > WEP_KEYS) 536 return -EINVAL; 537 idx--; 538 } else 539 idx = ieee->crypt_info.tx_keyidx; 540 541 if (ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) { 542 crypt = &ieee->crypt_info.crypt[idx]; 543 group_key = 1; 544 } else { 545 /* some Cisco APs use idx>0 for unicast in dynamic WEP */ 546 if (idx != 0 && ext->alg != IW_ENCODE_ALG_WEP) 547 return -EINVAL; 548 if (ieee->iw_mode == IW_MODE_INFRA) 549 crypt = &ieee->crypt_info.crypt[idx]; 550 else 551 return -EINVAL; 552 } 553 554 sec.flags |= SEC_ENABLED | SEC_ENCRYPT; 555 if ((encoding->flags & IW_ENCODE_DISABLED) || 556 ext->alg == IW_ENCODE_ALG_NONE) { 557 if (*crypt) 558 lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt); 559 560 for (i = 0; i < WEP_KEYS; i++) 561 if (ieee->crypt_info.crypt[i] != NULL) 562 break; 563 564 if (i == WEP_KEYS) { 565 sec.enabled = 0; 566 sec.encrypt = 0; 567 sec.level = SEC_LEVEL_0; 568 sec.flags |= SEC_LEVEL; 569 } 570 goto done; 571 } 572 573 sec.enabled = 1; 574 sec.encrypt = 1; 575 576 if (group_key ? !ieee->host_mc_decrypt : 577 !(ieee->host_encrypt || ieee->host_decrypt || 578 ieee->host_encrypt_msdu)) 579 goto skip_host_crypt; 580 581 switch (ext->alg) { 582 case IW_ENCODE_ALG_WEP: 583 alg = "WEP"; 584 module = "lib80211_crypt_wep"; 585 break; 586 case IW_ENCODE_ALG_TKIP: 587 alg = "TKIP"; 588 module = "lib80211_crypt_tkip"; 589 break; 590 case IW_ENCODE_ALG_CCMP: 591 alg = "CCMP"; 592 module = "lib80211_crypt_ccmp"; 593 break; 594 default: 595 LIBIPW_DEBUG_WX("%s: unknown crypto alg %d\n", 596 dev->name, ext->alg); 597 ret = -EINVAL; 598 goto done; 599 } 600 601 ops = lib80211_get_crypto_ops(alg); 602 if (ops == NULL) { 603 request_module(module); 604 ops = lib80211_get_crypto_ops(alg); 605 } 606 if (ops == NULL) { 607 LIBIPW_DEBUG_WX("%s: unknown crypto alg %d\n", 608 dev->name, ext->alg); 609 ret = -EINVAL; 610 goto done; 611 } 612 613 if (*crypt == NULL || (*crypt)->ops != ops) { 614 struct lib80211_crypt_data *new_crypt; 615 616 lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt); 617 618 new_crypt = kzalloc(sizeof(*new_crypt), GFP_KERNEL); 619 if (new_crypt == NULL) { 620 ret = -ENOMEM; 621 goto done; 622 } 623 new_crypt->ops = ops; 624 if (new_crypt->ops && try_module_get(new_crypt->ops->owner)) 625 new_crypt->priv = new_crypt->ops->init(idx); 626 if (new_crypt->priv == NULL) { 627 kfree(new_crypt); 628 ret = -EINVAL; 629 goto done; 630 } 631 *crypt = new_crypt; 632 } 633 634 if (ext->key_len > 0 && (*crypt)->ops->set_key && 635 (*crypt)->ops->set_key(ext->key, ext->key_len, ext->rx_seq, 636 (*crypt)->priv) < 0) { 637 LIBIPW_DEBUG_WX("%s: key setting failed\n", dev->name); 638 ret = -EINVAL; 639 goto done; 640 } 641 642 skip_host_crypt: 643 if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) { 644 ieee->crypt_info.tx_keyidx = idx; 645 sec.active_key = idx; 646 sec.flags |= SEC_ACTIVE_KEY; 647 } 648 649 if (ext->alg != IW_ENCODE_ALG_NONE) { 650 memcpy(sec.keys[idx], ext->key, ext->key_len); 651 sec.key_sizes[idx] = ext->key_len; 652 sec.flags |= (1 << idx); 653 if (ext->alg == IW_ENCODE_ALG_WEP) { 654 sec.encode_alg[idx] = SEC_ALG_WEP; 655 sec.flags |= SEC_LEVEL; 656 sec.level = SEC_LEVEL_1; 657 } else if (ext->alg == IW_ENCODE_ALG_TKIP) { 658 sec.encode_alg[idx] = SEC_ALG_TKIP; 659 sec.flags |= SEC_LEVEL; 660 sec.level = SEC_LEVEL_2; 661 } else if (ext->alg == IW_ENCODE_ALG_CCMP) { 662 sec.encode_alg[idx] = SEC_ALG_CCMP; 663 sec.flags |= SEC_LEVEL; 664 sec.level = SEC_LEVEL_3; 665 } 666 /* Don't set sec level for group keys. */ 667 if (group_key) 668 sec.flags &= ~SEC_LEVEL; 669 } 670 done: 671 if (ieee->set_security) 672 ieee->set_security(dev, &sec); 673 674 return ret; 675 } 676 677 int libipw_wx_get_encodeext(struct libipw_device *ieee, 678 struct iw_request_info *info, 679 union iwreq_data *wrqu, char *extra) 680 { 681 struct iw_point *encoding = &wrqu->encoding; 682 struct iw_encode_ext *ext = (struct iw_encode_ext *)extra; 683 struct libipw_security *sec = &ieee->sec; 684 int idx, max_key_len; 685 686 max_key_len = encoding->length - sizeof(*ext); 687 if (max_key_len < 0) 688 return -EINVAL; 689 690 idx = encoding->flags & IW_ENCODE_INDEX; 691 if (idx) { 692 if (idx < 1 || idx > WEP_KEYS) 693 return -EINVAL; 694 idx--; 695 } else 696 idx = ieee->crypt_info.tx_keyidx; 697 698 if (!(ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) && 699 ext->alg != IW_ENCODE_ALG_WEP) 700 if (idx != 0 || ieee->iw_mode != IW_MODE_INFRA) 701 return -EINVAL; 702 703 encoding->flags = idx + 1; 704 memset(ext, 0, sizeof(*ext)); 705 706 if (!sec->enabled) { 707 ext->alg = IW_ENCODE_ALG_NONE; 708 ext->key_len = 0; 709 encoding->flags |= IW_ENCODE_DISABLED; 710 } else { 711 if (sec->encode_alg[idx] == SEC_ALG_WEP) 712 ext->alg = IW_ENCODE_ALG_WEP; 713 else if (sec->encode_alg[idx] == SEC_ALG_TKIP) 714 ext->alg = IW_ENCODE_ALG_TKIP; 715 else if (sec->encode_alg[idx] == SEC_ALG_CCMP) 716 ext->alg = IW_ENCODE_ALG_CCMP; 717 else 718 return -EINVAL; 719 720 ext->key_len = sec->key_sizes[idx]; 721 memcpy(ext->key, sec->keys[idx], ext->key_len); 722 encoding->flags |= IW_ENCODE_ENABLED; 723 if (ext->key_len && 724 (ext->alg == IW_ENCODE_ALG_TKIP || 725 ext->alg == IW_ENCODE_ALG_CCMP)) 726 ext->ext_flags |= IW_ENCODE_EXT_TX_SEQ_VALID; 727 728 } 729 730 return 0; 731 } 732 733 EXPORT_SYMBOL(libipw_wx_set_encodeext); 734 EXPORT_SYMBOL(libipw_wx_get_encodeext); 735 736 EXPORT_SYMBOL(libipw_wx_get_scan); 737 EXPORT_SYMBOL(libipw_wx_set_encode); 738 EXPORT_SYMBOL(libipw_wx_get_encode); 739