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
elapsed_jiffies_msecs(unsigned long start)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
libipw_translate_scan(struct libipw_device * ieee,char * start,char * stop,struct libipw_network * network,struct iw_request_info * info)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
libipw_wx_get_scan(struct libipw_device * ieee,struct iw_request_info * info,union iwreq_data * wrqu,char * extra)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 DECLARE_SSID_BUF(ssid);
276
277 LIBIPW_DEBUG_WX("Getting scan\n");
278
279 spin_lock_irqsave(&ieee->lock, flags);
280
281 list_for_each_entry(network, &ieee->network_list, list) {
282 i++;
283 if (stop - ev < SCAN_ITEM_SIZE) {
284 err = -E2BIG;
285 break;
286 }
287
288 if (ieee->scan_age == 0 ||
289 time_after(network->last_scanned + ieee->scan_age, jiffies))
290 ev = libipw_translate_scan(ieee, ev, stop, network,
291 info);
292 else {
293 LIBIPW_DEBUG_SCAN("Not showing network '%s ("
294 "%pM)' due to age (%ums).\n",
295 print_ssid(ssid, network->ssid,
296 network->ssid_len),
297 network->bssid,
298 elapsed_jiffies_msecs(
299 network->last_scanned));
300 }
301 }
302
303 spin_unlock_irqrestore(&ieee->lock, flags);
304
305 wrqu->data.length = ev - extra;
306 wrqu->data.flags = 0;
307
308 LIBIPW_DEBUG_WX("exit: %d networks returned.\n", i);
309
310 return err;
311 }
312
libipw_wx_set_encode(struct libipw_device * ieee,struct iw_request_info * info,union iwreq_data * wrqu,char * keybuf)313 int libipw_wx_set_encode(struct libipw_device *ieee,
314 struct iw_request_info *info,
315 union iwreq_data *wrqu, char *keybuf)
316 {
317 struct iw_point *erq = &(wrqu->encoding);
318 struct net_device *dev = ieee->dev;
319 struct libipw_security sec = {
320 .flags = 0
321 };
322 int i, key, key_provided, len;
323 struct lib80211_crypt_data **crypt;
324 int host_crypto = ieee->host_encrypt || ieee->host_decrypt;
325 DECLARE_SSID_BUF(ssid);
326
327 LIBIPW_DEBUG_WX("SET_ENCODE\n");
328
329 key = erq->flags & IW_ENCODE_INDEX;
330 if (key) {
331 if (key > WEP_KEYS)
332 return -EINVAL;
333 key--;
334 key_provided = 1;
335 } else {
336 key_provided = 0;
337 key = ieee->crypt_info.tx_keyidx;
338 }
339
340 LIBIPW_DEBUG_WX("Key: %d [%s]\n", key, key_provided ?
341 "provided" : "default");
342
343 crypt = &ieee->crypt_info.crypt[key];
344
345 if (erq->flags & IW_ENCODE_DISABLED) {
346 if (key_provided && *crypt) {
347 LIBIPW_DEBUG_WX("Disabling encryption on key %d.\n",
348 key);
349 lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt);
350 } else
351 LIBIPW_DEBUG_WX("Disabling encryption.\n");
352
353 /* Check all the keys to see if any are still configured,
354 * and if no key index was provided, de-init them all */
355 for (i = 0; i < WEP_KEYS; i++) {
356 if (ieee->crypt_info.crypt[i] != NULL) {
357 if (key_provided)
358 break;
359 lib80211_crypt_delayed_deinit(&ieee->crypt_info,
360 &ieee->crypt_info.crypt[i]);
361 }
362 }
363
364 if (i == WEP_KEYS) {
365 sec.enabled = 0;
366 sec.encrypt = 0;
367 sec.level = SEC_LEVEL_0;
368 sec.flags |= SEC_ENABLED | SEC_LEVEL | SEC_ENCRYPT;
369 }
370
371 goto done;
372 }
373
374 sec.enabled = 1;
375 sec.encrypt = 1;
376 sec.flags |= SEC_ENABLED | SEC_ENCRYPT;
377
378 if (*crypt != NULL && (*crypt)->ops != NULL &&
379 strcmp((*crypt)->ops->name, "WEP") != 0) {
380 /* changing to use WEP; deinit previously used algorithm
381 * on this key */
382 lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt);
383 }
384
385 if (*crypt == NULL && host_crypto) {
386 struct lib80211_crypt_data *new_crypt;
387
388 /* take WEP into use */
389 new_crypt = kzalloc(sizeof(struct lib80211_crypt_data),
390 GFP_KERNEL);
391 if (new_crypt == NULL)
392 return -ENOMEM;
393 new_crypt->ops = lib80211_get_crypto_ops("WEP");
394 if (!new_crypt->ops) {
395 request_module("lib80211_crypt_wep");
396 new_crypt->ops = lib80211_get_crypto_ops("WEP");
397 }
398
399 if (new_crypt->ops && try_module_get(new_crypt->ops->owner))
400 new_crypt->priv = new_crypt->ops->init(key);
401
402 if (!new_crypt->ops || !new_crypt->priv) {
403 kfree(new_crypt);
404 new_crypt = NULL;
405
406 printk(KERN_WARNING "%s: could not initialize WEP: "
407 "load module lib80211_crypt_wep\n", dev->name);
408 return -EOPNOTSUPP;
409 }
410 *crypt = new_crypt;
411 }
412
413 /* If a new key was provided, set it up */
414 if (erq->length > 0) {
415 len = erq->length <= 5 ? 5 : 13;
416 memcpy(sec.keys[key], keybuf, erq->length);
417 if (len > erq->length)
418 memset(sec.keys[key] + erq->length, 0,
419 len - erq->length);
420 LIBIPW_DEBUG_WX("Setting key %d to '%s' (%d:%d bytes)\n",
421 key, print_ssid(ssid, sec.keys[key], len),
422 erq->length, len);
423 sec.key_sizes[key] = len;
424 if (*crypt)
425 (*crypt)->ops->set_key(sec.keys[key], len, NULL,
426 (*crypt)->priv);
427 sec.flags |= (1 << key);
428 /* This ensures a key will be activated if no key is
429 * explicitly set */
430 if (key == sec.active_key)
431 sec.flags |= SEC_ACTIVE_KEY;
432
433 } else {
434 if (host_crypto) {
435 len = (*crypt)->ops->get_key(sec.keys[key], WEP_KEY_LEN,
436 NULL, (*crypt)->priv);
437 if (len == 0) {
438 /* Set a default key of all 0 */
439 LIBIPW_DEBUG_WX("Setting key %d to all "
440 "zero.\n", key);
441 memset(sec.keys[key], 0, 13);
442 (*crypt)->ops->set_key(sec.keys[key], 13, NULL,
443 (*crypt)->priv);
444 sec.key_sizes[key] = 13;
445 sec.flags |= (1 << key);
446 }
447 }
448 /* No key data - just set the default TX key index */
449 if (key_provided) {
450 LIBIPW_DEBUG_WX("Setting key %d to default Tx "
451 "key.\n", key);
452 ieee->crypt_info.tx_keyidx = key;
453 sec.active_key = key;
454 sec.flags |= SEC_ACTIVE_KEY;
455 }
456 }
457 if (erq->flags & (IW_ENCODE_OPEN | IW_ENCODE_RESTRICTED)) {
458 ieee->open_wep = !(erq->flags & IW_ENCODE_RESTRICTED);
459 sec.auth_mode = ieee->open_wep ? WLAN_AUTH_OPEN :
460 WLAN_AUTH_SHARED_KEY;
461 sec.flags |= SEC_AUTH_MODE;
462 LIBIPW_DEBUG_WX("Auth: %s\n",
463 sec.auth_mode == WLAN_AUTH_OPEN ?
464 "OPEN" : "SHARED KEY");
465 }
466
467 /* For now we just support WEP, so only set that security level...
468 * TODO: When WPA is added this is one place that needs to change */
469 sec.flags |= SEC_LEVEL;
470 sec.level = SEC_LEVEL_1; /* 40 and 104 bit WEP */
471 sec.encode_alg[key] = SEC_ALG_WEP;
472
473 done:
474 if (ieee->set_security)
475 ieee->set_security(dev, &sec);
476
477 return 0;
478 }
479
libipw_wx_get_encode(struct libipw_device * ieee,struct iw_request_info * info,union iwreq_data * wrqu,char * keybuf)480 int libipw_wx_get_encode(struct libipw_device *ieee,
481 struct iw_request_info *info,
482 union iwreq_data *wrqu, char *keybuf)
483 {
484 struct iw_point *erq = &(wrqu->encoding);
485 int len, key;
486 struct lib80211_crypt_data *crypt;
487 struct libipw_security *sec = &ieee->sec;
488
489 LIBIPW_DEBUG_WX("GET_ENCODE\n");
490
491 key = erq->flags & IW_ENCODE_INDEX;
492 if (key) {
493 if (key > WEP_KEYS)
494 return -EINVAL;
495 key--;
496 } else
497 key = ieee->crypt_info.tx_keyidx;
498
499 crypt = ieee->crypt_info.crypt[key];
500 erq->flags = key + 1;
501
502 if (!sec->enabled) {
503 erq->length = 0;
504 erq->flags |= IW_ENCODE_DISABLED;
505 return 0;
506 }
507
508 len = sec->key_sizes[key];
509 memcpy(keybuf, sec->keys[key], len);
510
511 erq->length = len;
512 erq->flags |= IW_ENCODE_ENABLED;
513
514 if (ieee->open_wep)
515 erq->flags |= IW_ENCODE_OPEN;
516 else
517 erq->flags |= IW_ENCODE_RESTRICTED;
518
519 return 0;
520 }
521
libipw_wx_set_encodeext(struct libipw_device * ieee,struct iw_request_info * info,union iwreq_data * wrqu,char * extra)522 int libipw_wx_set_encodeext(struct libipw_device *ieee,
523 struct iw_request_info *info,
524 union iwreq_data *wrqu, char *extra)
525 {
526 struct net_device *dev = ieee->dev;
527 struct iw_point *encoding = &wrqu->encoding;
528 struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
529 int i, idx, ret = 0;
530 int group_key = 0;
531 const char *alg, *module;
532 struct lib80211_crypto_ops *ops;
533 struct lib80211_crypt_data **crypt;
534
535 struct libipw_security sec = {
536 .flags = 0,
537 };
538
539 idx = encoding->flags & IW_ENCODE_INDEX;
540 if (idx) {
541 if (idx < 1 || idx > WEP_KEYS)
542 return -EINVAL;
543 idx--;
544 } else
545 idx = ieee->crypt_info.tx_keyidx;
546
547 if (ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) {
548 crypt = &ieee->crypt_info.crypt[idx];
549 group_key = 1;
550 } else {
551 /* some Cisco APs use idx>0 for unicast in dynamic WEP */
552 if (idx != 0 && ext->alg != IW_ENCODE_ALG_WEP)
553 return -EINVAL;
554 if (ieee->iw_mode == IW_MODE_INFRA)
555 crypt = &ieee->crypt_info.crypt[idx];
556 else
557 return -EINVAL;
558 }
559
560 sec.flags |= SEC_ENABLED | SEC_ENCRYPT;
561 if ((encoding->flags & IW_ENCODE_DISABLED) ||
562 ext->alg == IW_ENCODE_ALG_NONE) {
563 if (*crypt)
564 lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt);
565
566 for (i = 0; i < WEP_KEYS; i++)
567 if (ieee->crypt_info.crypt[i] != NULL)
568 break;
569
570 if (i == WEP_KEYS) {
571 sec.enabled = 0;
572 sec.encrypt = 0;
573 sec.level = SEC_LEVEL_0;
574 sec.flags |= SEC_LEVEL;
575 }
576 goto done;
577 }
578
579 sec.enabled = 1;
580 sec.encrypt = 1;
581
582 if (group_key ? !ieee->host_mc_decrypt :
583 !(ieee->host_encrypt || ieee->host_decrypt ||
584 ieee->host_encrypt_msdu))
585 goto skip_host_crypt;
586
587 switch (ext->alg) {
588 case IW_ENCODE_ALG_WEP:
589 alg = "WEP";
590 module = "lib80211_crypt_wep";
591 break;
592 case IW_ENCODE_ALG_TKIP:
593 alg = "TKIP";
594 module = "lib80211_crypt_tkip";
595 break;
596 case IW_ENCODE_ALG_CCMP:
597 alg = "CCMP";
598 module = "lib80211_crypt_ccmp";
599 break;
600 default:
601 LIBIPW_DEBUG_WX("%s: unknown crypto alg %d\n",
602 dev->name, ext->alg);
603 ret = -EINVAL;
604 goto done;
605 }
606
607 ops = lib80211_get_crypto_ops(alg);
608 if (ops == NULL) {
609 request_module(module);
610 ops = lib80211_get_crypto_ops(alg);
611 }
612 if (ops == NULL) {
613 LIBIPW_DEBUG_WX("%s: unknown crypto alg %d\n",
614 dev->name, ext->alg);
615 ret = -EINVAL;
616 goto done;
617 }
618
619 if (*crypt == NULL || (*crypt)->ops != ops) {
620 struct lib80211_crypt_data *new_crypt;
621
622 lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt);
623
624 new_crypt = kzalloc(sizeof(*new_crypt), GFP_KERNEL);
625 if (new_crypt == NULL) {
626 ret = -ENOMEM;
627 goto done;
628 }
629 new_crypt->ops = ops;
630 if (new_crypt->ops && try_module_get(new_crypt->ops->owner))
631 new_crypt->priv = new_crypt->ops->init(idx);
632 if (new_crypt->priv == NULL) {
633 kfree(new_crypt);
634 ret = -EINVAL;
635 goto done;
636 }
637 *crypt = new_crypt;
638 }
639
640 if (ext->key_len > 0 && (*crypt)->ops->set_key &&
641 (*crypt)->ops->set_key(ext->key, ext->key_len, ext->rx_seq,
642 (*crypt)->priv) < 0) {
643 LIBIPW_DEBUG_WX("%s: key setting failed\n", dev->name);
644 ret = -EINVAL;
645 goto done;
646 }
647
648 skip_host_crypt:
649 if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) {
650 ieee->crypt_info.tx_keyidx = idx;
651 sec.active_key = idx;
652 sec.flags |= SEC_ACTIVE_KEY;
653 }
654
655 if (ext->alg != IW_ENCODE_ALG_NONE) {
656 memcpy(sec.keys[idx], ext->key, ext->key_len);
657 sec.key_sizes[idx] = ext->key_len;
658 sec.flags |= (1 << idx);
659 if (ext->alg == IW_ENCODE_ALG_WEP) {
660 sec.encode_alg[idx] = SEC_ALG_WEP;
661 sec.flags |= SEC_LEVEL;
662 sec.level = SEC_LEVEL_1;
663 } else if (ext->alg == IW_ENCODE_ALG_TKIP) {
664 sec.encode_alg[idx] = SEC_ALG_TKIP;
665 sec.flags |= SEC_LEVEL;
666 sec.level = SEC_LEVEL_2;
667 } else if (ext->alg == IW_ENCODE_ALG_CCMP) {
668 sec.encode_alg[idx] = SEC_ALG_CCMP;
669 sec.flags |= SEC_LEVEL;
670 sec.level = SEC_LEVEL_3;
671 }
672 /* Don't set sec level for group keys. */
673 if (group_key)
674 sec.flags &= ~SEC_LEVEL;
675 }
676 done:
677 if (ieee->set_security)
678 ieee->set_security(ieee->dev, &sec);
679
680 return ret;
681 }
682
libipw_wx_get_encodeext(struct libipw_device * ieee,struct iw_request_info * info,union iwreq_data * wrqu,char * extra)683 int libipw_wx_get_encodeext(struct libipw_device *ieee,
684 struct iw_request_info *info,
685 union iwreq_data *wrqu, char *extra)
686 {
687 struct iw_point *encoding = &wrqu->encoding;
688 struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
689 struct libipw_security *sec = &ieee->sec;
690 int idx, max_key_len;
691
692 max_key_len = encoding->length - sizeof(*ext);
693 if (max_key_len < 0)
694 return -EINVAL;
695
696 idx = encoding->flags & IW_ENCODE_INDEX;
697 if (idx) {
698 if (idx < 1 || idx > WEP_KEYS)
699 return -EINVAL;
700 idx--;
701 } else
702 idx = ieee->crypt_info.tx_keyidx;
703
704 if (!(ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) &&
705 ext->alg != IW_ENCODE_ALG_WEP)
706 if (idx != 0 || ieee->iw_mode != IW_MODE_INFRA)
707 return -EINVAL;
708
709 encoding->flags = idx + 1;
710 memset(ext, 0, sizeof(*ext));
711
712 if (!sec->enabled) {
713 ext->alg = IW_ENCODE_ALG_NONE;
714 ext->key_len = 0;
715 encoding->flags |= IW_ENCODE_DISABLED;
716 } else {
717 if (sec->encode_alg[idx] == SEC_ALG_WEP)
718 ext->alg = IW_ENCODE_ALG_WEP;
719 else if (sec->encode_alg[idx] == SEC_ALG_TKIP)
720 ext->alg = IW_ENCODE_ALG_TKIP;
721 else if (sec->encode_alg[idx] == SEC_ALG_CCMP)
722 ext->alg = IW_ENCODE_ALG_CCMP;
723 else
724 return -EINVAL;
725
726 ext->key_len = sec->key_sizes[idx];
727 memcpy(ext->key, sec->keys[idx], ext->key_len);
728 encoding->flags |= IW_ENCODE_ENABLED;
729 if (ext->key_len &&
730 (ext->alg == IW_ENCODE_ALG_TKIP ||
731 ext->alg == IW_ENCODE_ALG_CCMP))
732 ext->ext_flags |= IW_ENCODE_EXT_TX_SEQ_VALID;
733
734 }
735
736 return 0;
737 }
738
739 EXPORT_SYMBOL(libipw_wx_set_encodeext);
740 EXPORT_SYMBOL(libipw_wx_get_encodeext);
741
742 EXPORT_SYMBOL(libipw_wx_get_scan);
743 EXPORT_SYMBOL(libipw_wx_set_encode);
744 EXPORT_SYMBOL(libipw_wx_get_encode);
745