diff options
Diffstat (limited to 'net/ieee80211/ieee80211_wx.c')
-rw-r--r-- | net/ieee80211/ieee80211_wx.c | 372 |
1 files changed, 317 insertions, 55 deletions
diff --git a/net/ieee80211/ieee80211_wx.c b/net/ieee80211/ieee80211_wx.c index 94882f39b072..1ce7af9bec35 100644 --- a/net/ieee80211/ieee80211_wx.c +++ b/net/ieee80211/ieee80211_wx.c | |||
@@ -1,6 +1,6 @@ | |||
1 | /****************************************************************************** | 1 | /****************************************************************************** |
2 | 2 | ||
3 | Copyright(c) 2004 Intel Corporation. All rights reserved. | 3 | Copyright(c) 2004-2005 Intel Corporation. All rights reserved. |
4 | 4 | ||
5 | Portions of this file are based on the WEP enablement code provided by the | 5 | Portions of this file are based on the WEP enablement code provided by the |
6 | Host AP project hostap-drivers v0.1.3 | 6 | Host AP project hostap-drivers v0.1.3 |
@@ -32,6 +32,7 @@ | |||
32 | 32 | ||
33 | #include <linux/kmod.h> | 33 | #include <linux/kmod.h> |
34 | #include <linux/module.h> | 34 | #include <linux/module.h> |
35 | #include <linux/jiffies.h> | ||
35 | 36 | ||
36 | #include <net/ieee80211.h> | 37 | #include <net/ieee80211.h> |
37 | #include <linux/wireless.h> | 38 | #include <linux/wireless.h> |
@@ -140,18 +141,41 @@ static inline char *ipw2100_translate_scan(struct ieee80211_device *ieee, | |||
140 | start = iwe_stream_add_point(start, stop, &iwe, custom); | 141 | start = iwe_stream_add_point(start, stop, &iwe, custom); |
141 | 142 | ||
142 | /* Add quality statistics */ | 143 | /* Add quality statistics */ |
143 | /* TODO: Fix these values... */ | ||
144 | iwe.cmd = IWEVQUAL; | 144 | iwe.cmd = IWEVQUAL; |
145 | iwe.u.qual.qual = network->stats.signal; | 145 | iwe.u.qual.updated = IW_QUAL_QUAL_UPDATED | IW_QUAL_LEVEL_UPDATED | |
146 | iwe.u.qual.level = network->stats.rssi; | 146 | IW_QUAL_NOISE_UPDATED; |
147 | iwe.u.qual.noise = network->stats.noise; | 147 | |
148 | iwe.u.qual.updated = network->stats.mask & IEEE80211_STATMASK_WEMASK; | 148 | if (!(network->stats.mask & IEEE80211_STATMASK_RSSI)) { |
149 | if (!(network->stats.mask & IEEE80211_STATMASK_RSSI)) | 149 | iwe.u.qual.updated |= IW_QUAL_QUAL_INVALID | |
150 | iwe.u.qual.updated |= IW_QUAL_LEVEL_INVALID; | 150 | IW_QUAL_LEVEL_INVALID; |
151 | if (!(network->stats.mask & IEEE80211_STATMASK_NOISE)) | 151 | iwe.u.qual.qual = 0; |
152 | iwe.u.qual.level = 0; | ||
153 | } else { | ||
154 | iwe.u.qual.level = network->stats.rssi; | ||
155 | if (ieee->perfect_rssi == ieee->worst_rssi) | ||
156 | iwe.u.qual.qual = 100; | ||
157 | else | ||
158 | iwe.u.qual.qual = | ||
159 | (100 * | ||
160 | (ieee->perfect_rssi - ieee->worst_rssi) * | ||
161 | (ieee->perfect_rssi - ieee->worst_rssi) - | ||
162 | (ieee->perfect_rssi - network->stats.rssi) * | ||
163 | (15 * (ieee->perfect_rssi - ieee->worst_rssi) + | ||
164 | 62 * (ieee->perfect_rssi - network->stats.rssi))) / | ||
165 | ((ieee->perfect_rssi - ieee->worst_rssi) * | ||
166 | (ieee->perfect_rssi - ieee->worst_rssi)); | ||
167 | if (iwe.u.qual.qual > 100) | ||
168 | iwe.u.qual.qual = 100; | ||
169 | else if (iwe.u.qual.qual < 1) | ||
170 | iwe.u.qual.qual = 0; | ||
171 | } | ||
172 | |||
173 | if (!(network->stats.mask & IEEE80211_STATMASK_NOISE)) { | ||
152 | iwe.u.qual.updated |= IW_QUAL_NOISE_INVALID; | 174 | iwe.u.qual.updated |= IW_QUAL_NOISE_INVALID; |
153 | if (!(network->stats.mask & IEEE80211_STATMASK_SIGNAL)) | 175 | iwe.u.qual.noise = 0; |
154 | iwe.u.qual.updated |= IW_QUAL_QUAL_INVALID; | 176 | } else { |
177 | iwe.u.qual.noise = network->stats.noise; | ||
178 | } | ||
155 | 179 | ||
156 | start = iwe_stream_add_event(start, stop, &iwe, IW_EV_QUAL_LEN); | 180 | start = iwe_stream_add_event(start, stop, &iwe, IW_EV_QUAL_LEN); |
157 | 181 | ||
@@ -162,7 +186,7 @@ static inline char *ipw2100_translate_scan(struct ieee80211_device *ieee, | |||
162 | if (iwe.u.data.length) | 186 | if (iwe.u.data.length) |
163 | start = iwe_stream_add_point(start, stop, &iwe, custom); | 187 | start = iwe_stream_add_point(start, stop, &iwe, custom); |
164 | 188 | ||
165 | if (ieee->wpa_enabled && network->wpa_ie_len) { | 189 | if (network->wpa_ie_len) { |
166 | char buf[MAX_WPA_IE_LEN * 2 + 30]; | 190 | char buf[MAX_WPA_IE_LEN * 2 + 30]; |
167 | 191 | ||
168 | u8 *p = buf; | 192 | u8 *p = buf; |
@@ -177,7 +201,7 @@ static inline char *ipw2100_translate_scan(struct ieee80211_device *ieee, | |||
177 | start = iwe_stream_add_point(start, stop, &iwe, buf); | 201 | start = iwe_stream_add_point(start, stop, &iwe, buf); |
178 | } | 202 | } |
179 | 203 | ||
180 | if (ieee->wpa_enabled && network->rsn_ie_len) { | 204 | if (network->rsn_ie_len) { |
181 | char buf[MAX_WPA_IE_LEN * 2 + 30]; | 205 | char buf[MAX_WPA_IE_LEN * 2 + 30]; |
182 | 206 | ||
183 | u8 *p = buf; | 207 | u8 *p = buf; |
@@ -197,8 +221,8 @@ static inline char *ipw2100_translate_scan(struct ieee80211_device *ieee, | |||
197 | iwe.cmd = IWEVCUSTOM; | 221 | iwe.cmd = IWEVCUSTOM; |
198 | p = custom; | 222 | p = custom; |
199 | p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), | 223 | p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), |
200 | " Last beacon: %lums ago", | 224 | " Last beacon: %dms ago", |
201 | (jiffies - network->last_scanned) / (HZ / 100)); | 225 | jiffies_to_msecs(jiffies - network->last_scanned)); |
202 | iwe.u.data.length = p - custom; | 226 | iwe.u.data.length = p - custom; |
203 | if (iwe.u.data.length) | 227 | if (iwe.u.data.length) |
204 | start = iwe_stream_add_point(start, stop, &iwe, custom); | 228 | start = iwe_stream_add_point(start, stop, &iwe, custom); |
@@ -228,13 +252,13 @@ int ieee80211_wx_get_scan(struct ieee80211_device *ieee, | |||
228 | ev = ipw2100_translate_scan(ieee, ev, stop, network); | 252 | ev = ipw2100_translate_scan(ieee, ev, stop, network); |
229 | else | 253 | else |
230 | IEEE80211_DEBUG_SCAN("Not showing network '%s (" | 254 | IEEE80211_DEBUG_SCAN("Not showing network '%s (" |
231 | MAC_FMT ")' due to age (%lums).\n", | 255 | MAC_FMT ")' due to age (%dms).\n", |
232 | escape_essid(network->ssid, | 256 | escape_essid(network->ssid, |
233 | network->ssid_len), | 257 | network->ssid_len), |
234 | MAC_ARG(network->bssid), | 258 | MAC_ARG(network->bssid), |
235 | (jiffies - | 259 | jiffies_to_msecs(jiffies - |
236 | network->last_scanned) / (HZ / | 260 | network-> |
237 | 100)); | 261 | last_scanned)); |
238 | } | 262 | } |
239 | 263 | ||
240 | spin_unlock_irqrestore(&ieee->lock, flags); | 264 | spin_unlock_irqrestore(&ieee->lock, flags); |
@@ -258,6 +282,7 @@ int ieee80211_wx_set_encode(struct ieee80211_device *ieee, | |||
258 | }; | 282 | }; |
259 | int i, key, key_provided, len; | 283 | int i, key, key_provided, len; |
260 | struct ieee80211_crypt_data **crypt; | 284 | struct ieee80211_crypt_data **crypt; |
285 | int host_crypto = ieee->host_encrypt || ieee->host_decrypt; | ||
261 | 286 | ||
262 | IEEE80211_DEBUG_WX("SET_ENCODE\n"); | 287 | IEEE80211_DEBUG_WX("SET_ENCODE\n"); |
263 | 288 | ||
@@ -298,15 +323,17 @@ int ieee80211_wx_set_encode(struct ieee80211_device *ieee, | |||
298 | 323 | ||
299 | if (i == WEP_KEYS) { | 324 | if (i == WEP_KEYS) { |
300 | sec.enabled = 0; | 325 | sec.enabled = 0; |
326 | sec.encrypt = 0; | ||
301 | sec.level = SEC_LEVEL_0; | 327 | sec.level = SEC_LEVEL_0; |
302 | sec.flags |= SEC_ENABLED | SEC_LEVEL; | 328 | sec.flags |= SEC_ENABLED | SEC_LEVEL | SEC_ENCRYPT; |
303 | } | 329 | } |
304 | 330 | ||
305 | goto done; | 331 | goto done; |
306 | } | 332 | } |
307 | 333 | ||
308 | sec.enabled = 1; | 334 | sec.enabled = 1; |
309 | sec.flags |= SEC_ENABLED; | 335 | sec.encrypt = 1; |
336 | sec.flags |= SEC_ENABLED | SEC_ENCRYPT; | ||
310 | 337 | ||
311 | if (*crypt != NULL && (*crypt)->ops != NULL && | 338 | if (*crypt != NULL && (*crypt)->ops != NULL && |
312 | strcmp((*crypt)->ops->name, "WEP") != 0) { | 339 | strcmp((*crypt)->ops->name, "WEP") != 0) { |
@@ -315,7 +342,7 @@ int ieee80211_wx_set_encode(struct ieee80211_device *ieee, | |||
315 | ieee80211_crypt_delayed_deinit(ieee, crypt); | 342 | ieee80211_crypt_delayed_deinit(ieee, crypt); |
316 | } | 343 | } |
317 | 344 | ||
318 | if (*crypt == NULL) { | 345 | if (*crypt == NULL && host_crypto) { |
319 | struct ieee80211_crypt_data *new_crypt; | 346 | struct ieee80211_crypt_data *new_crypt; |
320 | 347 | ||
321 | /* take WEP into use */ | 348 | /* take WEP into use */ |
@@ -355,49 +382,56 @@ int ieee80211_wx_set_encode(struct ieee80211_device *ieee, | |||
355 | key, escape_essid(sec.keys[key], len), | 382 | key, escape_essid(sec.keys[key], len), |
356 | erq->length, len); | 383 | erq->length, len); |
357 | sec.key_sizes[key] = len; | 384 | sec.key_sizes[key] = len; |
358 | (*crypt)->ops->set_key(sec.keys[key], len, NULL, | 385 | if (*crypt) |
359 | (*crypt)->priv); | 386 | (*crypt)->ops->set_key(sec.keys[key], len, NULL, |
387 | (*crypt)->priv); | ||
360 | sec.flags |= (1 << key); | 388 | sec.flags |= (1 << key); |
361 | /* This ensures a key will be activated if no key is | 389 | /* This ensures a key will be activated if no key is |
362 | * explicitely set */ | 390 | * explicitely set */ |
363 | if (key == sec.active_key) | 391 | if (key == sec.active_key) |
364 | sec.flags |= SEC_ACTIVE_KEY; | 392 | sec.flags |= SEC_ACTIVE_KEY; |
393 | |||
365 | } else { | 394 | } else { |
366 | len = (*crypt)->ops->get_key(sec.keys[key], WEP_KEY_LEN, | 395 | if (host_crypto) { |
367 | NULL, (*crypt)->priv); | 396 | len = (*crypt)->ops->get_key(sec.keys[key], WEP_KEY_LEN, |
368 | if (len == 0) { | 397 | NULL, (*crypt)->priv); |
369 | /* Set a default key of all 0 */ | 398 | if (len == 0) { |
370 | IEEE80211_DEBUG_WX("Setting key %d to all zero.\n", | 399 | /* Set a default key of all 0 */ |
371 | key); | 400 | IEEE80211_DEBUG_WX("Setting key %d to all " |
372 | memset(sec.keys[key], 0, 13); | 401 | "zero.\n", key); |
373 | (*crypt)->ops->set_key(sec.keys[key], 13, NULL, | 402 | memset(sec.keys[key], 0, 13); |
374 | (*crypt)->priv); | 403 | (*crypt)->ops->set_key(sec.keys[key], 13, NULL, |
375 | sec.key_sizes[key] = 13; | 404 | (*crypt)->priv); |
376 | sec.flags |= (1 << key); | 405 | sec.key_sizes[key] = 13; |
406 | sec.flags |= (1 << key); | ||
407 | } | ||
377 | } | 408 | } |
378 | |||
379 | /* No key data - just set the default TX key index */ | 409 | /* No key data - just set the default TX key index */ |
380 | if (key_provided) { | 410 | if (key_provided) { |
381 | IEEE80211_DEBUG_WX | 411 | IEEE80211_DEBUG_WX("Setting key %d to default Tx " |
382 | ("Setting key %d to default Tx key.\n", key); | 412 | "key.\n", key); |
383 | ieee->tx_keyidx = key; | 413 | ieee->tx_keyidx = key; |
384 | sec.active_key = key; | 414 | sec.active_key = key; |
385 | sec.flags |= SEC_ACTIVE_KEY; | 415 | sec.flags |= SEC_ACTIVE_KEY; |
386 | } | 416 | } |
387 | } | 417 | } |
388 | 418 | if (erq->flags & (IW_ENCODE_OPEN | IW_ENCODE_RESTRICTED)) { | |
389 | done: | 419 | ieee->open_wep = !(erq->flags & IW_ENCODE_RESTRICTED); |
390 | ieee->open_wep = !(erq->flags & IW_ENCODE_RESTRICTED); | 420 | sec.auth_mode = ieee->open_wep ? WLAN_AUTH_OPEN : |
391 | sec.auth_mode = ieee->open_wep ? WLAN_AUTH_OPEN : WLAN_AUTH_SHARED_KEY; | 421 | WLAN_AUTH_SHARED_KEY; |
392 | sec.flags |= SEC_AUTH_MODE; | 422 | sec.flags |= SEC_AUTH_MODE; |
393 | IEEE80211_DEBUG_WX("Auth: %s\n", sec.auth_mode == WLAN_AUTH_OPEN ? | 423 | IEEE80211_DEBUG_WX("Auth: %s\n", |
394 | "OPEN" : "SHARED KEY"); | 424 | sec.auth_mode == WLAN_AUTH_OPEN ? |
425 | "OPEN" : "SHARED KEY"); | ||
426 | } | ||
395 | 427 | ||
396 | /* For now we just support WEP, so only set that security level... | 428 | /* For now we just support WEP, so only set that security level... |
397 | * TODO: When WPA is added this is one place that needs to change */ | 429 | * TODO: When WPA is added this is one place that needs to change */ |
398 | sec.flags |= SEC_LEVEL; | 430 | sec.flags |= SEC_LEVEL; |
399 | sec.level = SEC_LEVEL_1; /* 40 and 104 bit WEP */ | 431 | sec.level = SEC_LEVEL_1; /* 40 and 104 bit WEP */ |
432 | sec.encode_alg[key] = SEC_ALG_WEP; | ||
400 | 433 | ||
434 | done: | ||
401 | if (ieee->set_security) | 435 | if (ieee->set_security) |
402 | ieee->set_security(dev, &sec); | 436 | ieee->set_security(dev, &sec); |
403 | 437 | ||
@@ -422,6 +456,7 @@ int ieee80211_wx_get_encode(struct ieee80211_device *ieee, | |||
422 | struct iw_point *erq = &(wrqu->encoding); | 456 | struct iw_point *erq = &(wrqu->encoding); |
423 | int len, key; | 457 | int len, key; |
424 | struct ieee80211_crypt_data *crypt; | 458 | struct ieee80211_crypt_data *crypt; |
459 | struct ieee80211_security *sec = &ieee->sec; | ||
425 | 460 | ||
426 | IEEE80211_DEBUG_WX("GET_ENCODE\n"); | 461 | IEEE80211_DEBUG_WX("GET_ENCODE\n"); |
427 | 462 | ||
@@ -436,23 +471,16 @@ int ieee80211_wx_get_encode(struct ieee80211_device *ieee, | |||
436 | crypt = ieee->crypt[key]; | 471 | crypt = ieee->crypt[key]; |
437 | erq->flags = key + 1; | 472 | erq->flags = key + 1; |
438 | 473 | ||
439 | if (crypt == NULL || crypt->ops == NULL) { | 474 | if (!sec->enabled) { |
440 | erq->length = 0; | 475 | erq->length = 0; |
441 | erq->flags |= IW_ENCODE_DISABLED; | 476 | erq->flags |= IW_ENCODE_DISABLED; |
442 | return 0; | 477 | return 0; |
443 | } | 478 | } |
444 | 479 | ||
445 | if (strcmp(crypt->ops->name, "WEP") != 0) { | 480 | len = sec->key_sizes[key]; |
446 | /* only WEP is supported with wireless extensions, so just | 481 | memcpy(keybuf, sec->keys[key], len); |
447 | * report that encryption is used */ | ||
448 | erq->length = 0; | ||
449 | erq->flags |= IW_ENCODE_ENABLED; | ||
450 | return 0; | ||
451 | } | ||
452 | 482 | ||
453 | len = crypt->ops->get_key(keybuf, WEP_KEY_LEN, NULL, crypt->priv); | ||
454 | erq->length = (len >= 0 ? len : 0); | 483 | erq->length = (len >= 0 ? len : 0); |
455 | |||
456 | erq->flags |= IW_ENCODE_ENABLED; | 484 | erq->flags |= IW_ENCODE_ENABLED; |
457 | 485 | ||
458 | if (ieee->open_wep) | 486 | if (ieee->open_wep) |
@@ -463,6 +491,240 @@ int ieee80211_wx_get_encode(struct ieee80211_device *ieee, | |||
463 | return 0; | 491 | return 0; |
464 | } | 492 | } |
465 | 493 | ||
494 | int ieee80211_wx_set_encodeext(struct ieee80211_device *ieee, | ||
495 | struct iw_request_info *info, | ||
496 | union iwreq_data *wrqu, char *extra) | ||
497 | { | ||
498 | struct net_device *dev = ieee->dev; | ||
499 | struct iw_point *encoding = &wrqu->encoding; | ||
500 | struct iw_encode_ext *ext = (struct iw_encode_ext *)extra; | ||
501 | int i, idx, ret = 0; | ||
502 | int group_key = 0; | ||
503 | const char *alg, *module; | ||
504 | struct ieee80211_crypto_ops *ops; | ||
505 | struct ieee80211_crypt_data **crypt; | ||
506 | |||
507 | struct ieee80211_security sec = { | ||
508 | .flags = 0, | ||
509 | }; | ||
510 | |||
511 | idx = encoding->flags & IW_ENCODE_INDEX; | ||
512 | if (idx) { | ||
513 | if (idx < 1 || idx > WEP_KEYS) | ||
514 | return -EINVAL; | ||
515 | idx--; | ||
516 | } else | ||
517 | idx = ieee->tx_keyidx; | ||
518 | |||
519 | if (ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) { | ||
520 | crypt = &ieee->crypt[idx]; | ||
521 | group_key = 1; | ||
522 | } else { | ||
523 | if (idx != 0) | ||
524 | return -EINVAL; | ||
525 | if (ieee->iw_mode == IW_MODE_INFRA) | ||
526 | crypt = &ieee->crypt[idx]; | ||
527 | else | ||
528 | return -EINVAL; | ||
529 | } | ||
530 | |||
531 | sec.flags |= SEC_ENABLED | SEC_ENCRYPT; | ||
532 | if ((encoding->flags & IW_ENCODE_DISABLED) || | ||
533 | ext->alg == IW_ENCODE_ALG_NONE) { | ||
534 | if (*crypt) | ||
535 | ieee80211_crypt_delayed_deinit(ieee, crypt); | ||
536 | |||
537 | for (i = 0; i < WEP_KEYS; i++) | ||
538 | if (ieee->crypt[i] != NULL) | ||
539 | break; | ||
540 | |||
541 | if (i == WEP_KEYS) { | ||
542 | sec.enabled = 0; | ||
543 | sec.encrypt = 0; | ||
544 | sec.level = SEC_LEVEL_0; | ||
545 | sec.flags |= SEC_LEVEL; | ||
546 | } | ||
547 | goto done; | ||
548 | } | ||
549 | |||
550 | sec.enabled = 1; | ||
551 | sec.encrypt = 1; | ||
552 | |||
553 | if (group_key ? !ieee->host_mc_decrypt : | ||
554 | !(ieee->host_encrypt || ieee->host_decrypt || | ||
555 | ieee->host_encrypt_msdu)) | ||
556 | goto skip_host_crypt; | ||
557 | |||
558 | switch (ext->alg) { | ||
559 | case IW_ENCODE_ALG_WEP: | ||
560 | alg = "WEP"; | ||
561 | module = "ieee80211_crypt_wep"; | ||
562 | break; | ||
563 | case IW_ENCODE_ALG_TKIP: | ||
564 | alg = "TKIP"; | ||
565 | module = "ieee80211_crypt_tkip"; | ||
566 | break; | ||
567 | case IW_ENCODE_ALG_CCMP: | ||
568 | alg = "CCMP"; | ||
569 | module = "ieee80211_crypt_ccmp"; | ||
570 | break; | ||
571 | default: | ||
572 | IEEE80211_DEBUG_WX("%s: unknown crypto alg %d\n", | ||
573 | dev->name, ext->alg); | ||
574 | ret = -EINVAL; | ||
575 | goto done; | ||
576 | } | ||
577 | |||
578 | ops = ieee80211_get_crypto_ops(alg); | ||
579 | if (ops == NULL) { | ||
580 | request_module(module); | ||
581 | ops = ieee80211_get_crypto_ops(alg); | ||
582 | } | ||
583 | if (ops == NULL) { | ||
584 | IEEE80211_DEBUG_WX("%s: unknown crypto alg %d\n", | ||
585 | dev->name, ext->alg); | ||
586 | ret = -EINVAL; | ||
587 | goto done; | ||
588 | } | ||
589 | |||
590 | if (*crypt == NULL || (*crypt)->ops != ops) { | ||
591 | struct ieee80211_crypt_data *new_crypt; | ||
592 | |||
593 | ieee80211_crypt_delayed_deinit(ieee, crypt); | ||
594 | |||
595 | new_crypt = (struct ieee80211_crypt_data *) | ||
596 | kmalloc(sizeof(*new_crypt), GFP_KERNEL); | ||
597 | if (new_crypt == NULL) { | ||
598 | ret = -ENOMEM; | ||
599 | goto done; | ||
600 | } | ||
601 | memset(new_crypt, 0, sizeof(struct ieee80211_crypt_data)); | ||
602 | new_crypt->ops = ops; | ||
603 | if (new_crypt->ops && try_module_get(new_crypt->ops->owner)) | ||
604 | new_crypt->priv = new_crypt->ops->init(idx); | ||
605 | if (new_crypt->priv == NULL) { | ||
606 | kfree(new_crypt); | ||
607 | ret = -EINVAL; | ||
608 | goto done; | ||
609 | } | ||
610 | *crypt = new_crypt; | ||
611 | } | ||
612 | |||
613 | if (ext->key_len > 0 && (*crypt)->ops->set_key && | ||
614 | (*crypt)->ops->set_key(ext->key, ext->key_len, ext->rx_seq, | ||
615 | (*crypt)->priv) < 0) { | ||
616 | IEEE80211_DEBUG_WX("%s: key setting failed\n", dev->name); | ||
617 | ret = -EINVAL; | ||
618 | goto done; | ||
619 | } | ||
620 | |||
621 | skip_host_crypt: | ||
622 | if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) { | ||
623 | ieee->tx_keyidx = idx; | ||
624 | sec.active_key = idx; | ||
625 | sec.flags |= SEC_ACTIVE_KEY; | ||
626 | } | ||
627 | |||
628 | if (ext->alg != IW_ENCODE_ALG_NONE) { | ||
629 | memcpy(sec.keys[idx], ext->key, ext->key_len); | ||
630 | sec.key_sizes[idx] = ext->key_len; | ||
631 | sec.flags |= (1 << idx); | ||
632 | if (ext->alg == IW_ENCODE_ALG_WEP) { | ||
633 | sec.encode_alg[idx] = SEC_ALG_WEP; | ||
634 | sec.flags |= SEC_LEVEL; | ||
635 | sec.level = SEC_LEVEL_1; | ||
636 | } else if (ext->alg == IW_ENCODE_ALG_TKIP) { | ||
637 | sec.encode_alg[idx] = SEC_ALG_TKIP; | ||
638 | sec.flags |= SEC_LEVEL; | ||
639 | sec.level = SEC_LEVEL_2; | ||
640 | } else if (ext->alg == IW_ENCODE_ALG_CCMP) { | ||
641 | sec.encode_alg[idx] = SEC_ALG_CCMP; | ||
642 | sec.flags |= SEC_LEVEL; | ||
643 | sec.level = SEC_LEVEL_3; | ||
644 | } | ||
645 | /* Don't set sec level for group keys. */ | ||
646 | if (group_key) | ||
647 | sec.flags &= ~SEC_LEVEL; | ||
648 | } | ||
649 | done: | ||
650 | if (ieee->set_security) | ||
651 | ieee->set_security(ieee->dev, &sec); | ||
652 | |||
653 | /* | ||
654 | * Do not reset port if card is in Managed mode since resetting will | ||
655 | * generate new IEEE 802.11 authentication which may end up in looping | ||
656 | * with IEEE 802.1X. If your hardware requires a reset after WEP | ||
657 | * configuration (for example... Prism2), implement the reset_port in | ||
658 | * the callbacks structures used to initialize the 802.11 stack. | ||
659 | */ | ||
660 | if (ieee->reset_on_keychange && | ||
661 | ieee->iw_mode != IW_MODE_INFRA && | ||
662 | ieee->reset_port && ieee->reset_port(dev)) { | ||
663 | IEEE80211_DEBUG_WX("%s: reset_port failed\n", dev->name); | ||
664 | return -EINVAL; | ||
665 | } | ||
666 | |||
667 | return ret; | ||
668 | } | ||
669 | |||
670 | int ieee80211_wx_get_encodeext(struct ieee80211_device *ieee, | ||
671 | struct iw_request_info *info, | ||
672 | union iwreq_data *wrqu, char *extra) | ||
673 | { | ||
674 | struct iw_point *encoding = &wrqu->encoding; | ||
675 | struct iw_encode_ext *ext = (struct iw_encode_ext *)extra; | ||
676 | struct ieee80211_security *sec = &ieee->sec; | ||
677 | int idx, max_key_len; | ||
678 | |||
679 | max_key_len = encoding->length - sizeof(*ext); | ||
680 | if (max_key_len < 0) | ||
681 | return -EINVAL; | ||
682 | |||
683 | idx = encoding->flags & IW_ENCODE_INDEX; | ||
684 | if (idx) { | ||
685 | if (idx < 1 || idx > WEP_KEYS) | ||
686 | return -EINVAL; | ||
687 | idx--; | ||
688 | } else | ||
689 | idx = ieee->tx_keyidx; | ||
690 | |||
691 | if (!ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) | ||
692 | if (idx != 0 || ieee->iw_mode != IW_MODE_INFRA) | ||
693 | return -EINVAL; | ||
694 | |||
695 | encoding->flags = idx + 1; | ||
696 | memset(ext, 0, sizeof(*ext)); | ||
697 | |||
698 | if (!sec->enabled) { | ||
699 | ext->alg = IW_ENCODE_ALG_NONE; | ||
700 | ext->key_len = 0; | ||
701 | encoding->flags |= IW_ENCODE_DISABLED; | ||
702 | } else { | ||
703 | if (sec->encode_alg[idx] == SEC_ALG_WEP) | ||
704 | ext->alg = IW_ENCODE_ALG_WEP; | ||
705 | else if (sec->encode_alg[idx] == SEC_ALG_TKIP) | ||
706 | ext->alg = IW_ENCODE_ALG_TKIP; | ||
707 | else if (sec->encode_alg[idx] == SEC_ALG_CCMP) | ||
708 | ext->alg = IW_ENCODE_ALG_CCMP; | ||
709 | else | ||
710 | return -EINVAL; | ||
711 | |||
712 | ext->key_len = sec->key_sizes[idx]; | ||
713 | memcpy(ext->key, sec->keys[idx], ext->key_len); | ||
714 | encoding->flags |= IW_ENCODE_ENABLED; | ||
715 | if (ext->key_len && | ||
716 | (ext->alg == IW_ENCODE_ALG_TKIP || | ||
717 | ext->alg == IW_ENCODE_ALG_CCMP)) | ||
718 | ext->ext_flags |= IW_ENCODE_EXT_TX_SEQ_VALID; | ||
719 | |||
720 | } | ||
721 | |||
722 | return 0; | ||
723 | } | ||
724 | |||
725 | EXPORT_SYMBOL(ieee80211_wx_set_encodeext); | ||
726 | EXPORT_SYMBOL(ieee80211_wx_get_encodeext); | ||
727 | |||
466 | EXPORT_SYMBOL(ieee80211_wx_get_scan); | 728 | EXPORT_SYMBOL(ieee80211_wx_get_scan); |
467 | EXPORT_SYMBOL(ieee80211_wx_set_encode); | 729 | EXPORT_SYMBOL(ieee80211_wx_set_encode); |
468 | EXPORT_SYMBOL(ieee80211_wx_get_encode); | 730 | EXPORT_SYMBOL(ieee80211_wx_get_encode); |