[PATCH] ieee80211: Add 802.11h information element parsing
[linux-2.6] / net / ieee80211 / ieee80211_wx.c
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   <jkmaline@cc.hut.fi>
9   Copyright (c) 2002-2003, Jouni Malinen <jkmaline@cc.hut.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   James P. Ketrenos <ipw2100-admin@linux.intel.com>
29   Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
30
31 ******************************************************************************/
32
33 #include <linux/kmod.h>
34 #include <linux/module.h>
35 #include <linux/jiffies.h>
36
37 #include <net/ieee80211.h>
38 #include <linux/wireless.h>
39
40 static const char *ieee80211_modes[] = {
41         "?", "a", "b", "ab", "g", "ag", "bg", "abg"
42 };
43
44 #define MAX_CUSTOM_LEN 64
45 static char *ipw2100_translate_scan(struct ieee80211_device *ieee,
46                                            char *start, char *stop,
47                                            struct ieee80211_network *network)
48 {
49         char custom[MAX_CUSTOM_LEN];
50         char *p;
51         struct iw_event iwe;
52         int i, j;
53         u8 max_rate, rate;
54
55         /* First entry *MUST* be the AP MAC address */
56         iwe.cmd = SIOCGIWAP;
57         iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
58         memcpy(iwe.u.ap_addr.sa_data, network->bssid, ETH_ALEN);
59         start = iwe_stream_add_event(start, stop, &iwe, IW_EV_ADDR_LEN);
60
61         /* Remaining entries will be displayed in the order we provide them */
62
63         /* Add the ESSID */
64         iwe.cmd = SIOCGIWESSID;
65         iwe.u.data.flags = 1;
66         if (network->flags & NETWORK_EMPTY_ESSID) {
67                 iwe.u.data.length = sizeof("<hidden>");
68                 start = iwe_stream_add_point(start, stop, &iwe, "<hidden>");
69         } else {
70                 iwe.u.data.length = min(network->ssid_len, (u8) 32);
71                 start = iwe_stream_add_point(start, stop, &iwe, network->ssid);
72         }
73
74         /* Add the protocol name */
75         iwe.cmd = SIOCGIWNAME;
76         snprintf(iwe.u.name, IFNAMSIZ, "IEEE 802.11%s",
77                  ieee80211_modes[network->mode]);
78         start = iwe_stream_add_event(start, stop, &iwe, IW_EV_CHAR_LEN);
79
80         /* Add mode */
81         iwe.cmd = SIOCGIWMODE;
82         if (network->capability & (WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS)) {
83                 if (network->capability & WLAN_CAPABILITY_ESS)
84                         iwe.u.mode = IW_MODE_MASTER;
85                 else
86                         iwe.u.mode = IW_MODE_ADHOC;
87
88                 start = iwe_stream_add_event(start, stop, &iwe, IW_EV_UINT_LEN);
89         }
90
91         /* Add frequency/channel */
92         iwe.cmd = SIOCGIWFREQ;
93 /*      iwe.u.freq.m = ieee80211_frequency(network->channel, network->mode);
94         iwe.u.freq.e = 3; */
95         iwe.u.freq.m = network->channel;
96         iwe.u.freq.e = 0;
97         iwe.u.freq.i = 0;
98         start = iwe_stream_add_event(start, stop, &iwe, IW_EV_FREQ_LEN);
99
100         /* Add encryption capability */
101         iwe.cmd = SIOCGIWENCODE;
102         if (network->capability & WLAN_CAPABILITY_PRIVACY)
103                 iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
104         else
105                 iwe.u.data.flags = IW_ENCODE_DISABLED;
106         iwe.u.data.length = 0;
107         start = iwe_stream_add_point(start, stop, &iwe, network->ssid);
108
109         /* Add basic and extended rates */
110         max_rate = 0;
111         p = custom;
112         p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), " Rates (Mb/s): ");
113         for (i = 0, j = 0; i < network->rates_len;) {
114                 if (j < network->rates_ex_len &&
115                     ((network->rates_ex[j] & 0x7F) <
116                      (network->rates[i] & 0x7F)))
117                         rate = network->rates_ex[j++] & 0x7F;
118                 else
119                         rate = network->rates[i++] & 0x7F;
120                 if (rate > max_rate)
121                         max_rate = rate;
122                 p += snprintf(p, MAX_CUSTOM_LEN - (p - custom),
123                               "%d%s ", rate >> 1, (rate & 1) ? ".5" : "");
124         }
125         for (; j < network->rates_ex_len; j++) {
126                 rate = network->rates_ex[j] & 0x7F;
127                 p += snprintf(p, MAX_CUSTOM_LEN - (p - custom),
128                               "%d%s ", rate >> 1, (rate & 1) ? ".5" : "");
129                 if (rate > max_rate)
130                         max_rate = rate;
131         }
132
133         iwe.cmd = SIOCGIWRATE;
134         iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0;
135         iwe.u.bitrate.value = max_rate * 500000;
136         start = iwe_stream_add_event(start, stop, &iwe, IW_EV_PARAM_LEN);
137
138         iwe.cmd = IWEVCUSTOM;
139         iwe.u.data.length = p - custom;
140         if (iwe.u.data.length)
141                 start = iwe_stream_add_point(start, stop, &iwe, custom);
142
143         /* Add quality statistics */
144         iwe.cmd = IWEVQUAL;
145         iwe.u.qual.updated = IW_QUAL_QUAL_UPDATED | IW_QUAL_LEVEL_UPDATED |
146             IW_QUAL_NOISE_UPDATED;
147
148         if (!(network->stats.mask & IEEE80211_STATMASK_RSSI)) {
149                 iwe.u.qual.updated |= IW_QUAL_QUAL_INVALID |
150                     IW_QUAL_LEVEL_INVALID;
151                 iwe.u.qual.qual = 0;
152         } else {
153                 if (ieee->perfect_rssi == ieee->worst_rssi)
154                         iwe.u.qual.qual = 100;
155                 else
156                         iwe.u.qual.qual =
157                             (100 *
158                              (ieee->perfect_rssi - ieee->worst_rssi) *
159                              (ieee->perfect_rssi - ieee->worst_rssi) -
160                              (ieee->perfect_rssi - network->stats.rssi) *
161                              (15 * (ieee->perfect_rssi - ieee->worst_rssi) +
162                               62 * (ieee->perfect_rssi -
163                                     network->stats.rssi))) /
164                             ((ieee->perfect_rssi -
165                               ieee->worst_rssi) * (ieee->perfect_rssi -
166                                                    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)) {
174                 iwe.u.qual.updated |= IW_QUAL_NOISE_INVALID;
175                 iwe.u.qual.noise = 0;
176         } else {
177                 iwe.u.qual.noise = network->stats.noise;
178         }
179
180         if (!(network->stats.mask & IEEE80211_STATMASK_SIGNAL)) {
181                 iwe.u.qual.updated |= IW_QUAL_LEVEL_INVALID;
182                 iwe.u.qual.level = 0;
183         } else {
184                 iwe.u.qual.level = network->stats.signal;
185         }
186
187         start = iwe_stream_add_event(start, stop, &iwe, IW_EV_QUAL_LEN);
188
189         iwe.cmd = IWEVCUSTOM;
190         p = custom;
191
192         iwe.u.data.length = p - custom;
193         if (iwe.u.data.length)
194                 start = iwe_stream_add_point(start, stop, &iwe, custom);
195
196         if (network->wpa_ie_len) {
197                 char buf[MAX_WPA_IE_LEN * 2 + 30];
198
199                 u8 *p = buf;
200                 p += sprintf(p, "wpa_ie=");
201                 for (i = 0; i < network->wpa_ie_len; i++) {
202                         p += sprintf(p, "%02x", network->wpa_ie[i]);
203                 }
204
205                 memset(&iwe, 0, sizeof(iwe));
206                 iwe.cmd = IWEVCUSTOM;
207                 iwe.u.data.length = strlen(buf);
208                 start = iwe_stream_add_point(start, stop, &iwe, buf);
209         }
210
211         if (network->rsn_ie_len) {
212                 char buf[MAX_WPA_IE_LEN * 2 + 30];
213
214                 u8 *p = buf;
215                 p += sprintf(p, "rsn_ie=");
216                 for (i = 0; i < network->rsn_ie_len; i++) {
217                         p += sprintf(p, "%02x", network->rsn_ie[i]);
218                 }
219
220                 memset(&iwe, 0, sizeof(iwe));
221                 iwe.cmd = IWEVCUSTOM;
222                 iwe.u.data.length = strlen(buf);
223                 start = iwe_stream_add_point(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: %dms ago",
232                       jiffies_to_msecs(jiffies - network->last_scanned));
233         iwe.u.data.length = p - custom;
234         if (iwe.u.data.length)
235                 start = iwe_stream_add_point(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 (ieee80211_get_channel_flags(ieee, network->channel) &
243             IEEE80211_CH_INVALID) {
244                 iwe.cmd = IWEVCUSTOM;
245                 p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), "INVALID ");
246         }
247
248         if (ieee80211_get_channel_flags(ieee, network->channel) &
249             IEEE80211_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(start, stop, &iwe, custom);
257         }
258
259         return start;
260 }
261
262 #define SCAN_ITEM_SIZE 128
263
264 int ieee80211_wx_get_scan(struct ieee80211_device *ieee,
265                           struct iw_request_info *info,
266                           union iwreq_data *wrqu, char *extra)
267 {
268         struct ieee80211_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         IEEE80211_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 = ipw2100_translate_scan(ieee, ev, stop, network);
290                 else
291                         IEEE80211_DEBUG_SCAN("Not showing network '%s ("
292                                              MAC_FMT ")' due to age (%dms).\n",
293                                              escape_essid(network->ssid,
294                                                           network->ssid_len),
295                                              MAC_ARG(network->bssid),
296                                              jiffies_to_msecs(jiffies -
297                                                               network->
298                                                               last_scanned));
299         }
300
301         spin_unlock_irqrestore(&ieee->lock, flags);
302
303         wrqu->data.length = ev - extra;
304         wrqu->data.flags = 0;
305
306         IEEE80211_DEBUG_WX("exit: %d networks returned.\n", i);
307
308         return err;
309 }
310
311 int ieee80211_wx_set_encode(struct ieee80211_device *ieee,
312                             struct iw_request_info *info,
313                             union iwreq_data *wrqu, char *keybuf)
314 {
315         struct iw_point *erq = &(wrqu->encoding);
316         struct net_device *dev = ieee->dev;
317         struct ieee80211_security sec = {
318                 .flags = 0
319         };
320         int i, key, key_provided, len;
321         struct ieee80211_crypt_data **crypt;
322         int host_crypto = ieee->host_encrypt || ieee->host_decrypt || ieee->host_build_iv;
323
324         IEEE80211_DEBUG_WX("SET_ENCODE\n");
325
326         key = erq->flags & IW_ENCODE_INDEX;
327         if (key) {
328                 if (key > WEP_KEYS)
329                         return -EINVAL;
330                 key--;
331                 key_provided = 1;
332         } else {
333                 key_provided = 0;
334                 key = ieee->tx_keyidx;
335         }
336
337         IEEE80211_DEBUG_WX("Key: %d [%s]\n", key, key_provided ?
338                            "provided" : "default");
339
340         crypt = &ieee->crypt[key];
341
342         if (erq->flags & IW_ENCODE_DISABLED) {
343                 if (key_provided && *crypt) {
344                         IEEE80211_DEBUG_WX("Disabling encryption on key %d.\n",
345                                            key);
346                         ieee80211_crypt_delayed_deinit(ieee, crypt);
347                 } else
348                         IEEE80211_DEBUG_WX("Disabling encryption.\n");
349
350                 /* Check all the keys to see if any are still configured,
351                  * and if no key index was provided, de-init them all */
352                 for (i = 0; i < WEP_KEYS; i++) {
353                         if (ieee->crypt[i] != NULL) {
354                                 if (key_provided)
355                                         break;
356                                 ieee80211_crypt_delayed_deinit(ieee,
357                                                                &ieee->crypt[i]);
358                         }
359                 }
360
361                 if (i == WEP_KEYS) {
362                         sec.enabled = 0;
363                         sec.encrypt = 0;
364                         sec.level = SEC_LEVEL_0;
365                         sec.flags |= SEC_ENABLED | SEC_LEVEL | SEC_ENCRYPT;
366                 }
367
368                 goto done;
369         }
370
371         sec.enabled = 1;
372         sec.encrypt = 1;
373         sec.flags |= SEC_ENABLED | SEC_ENCRYPT;
374
375         if (*crypt != NULL && (*crypt)->ops != NULL &&
376             strcmp((*crypt)->ops->name, "WEP") != 0) {
377                 /* changing to use WEP; deinit previously used algorithm
378                  * on this key */
379                 ieee80211_crypt_delayed_deinit(ieee, crypt);
380         }
381
382         if (*crypt == NULL && host_crypto) {
383                 struct ieee80211_crypt_data *new_crypt;
384
385                 /* take WEP into use */
386                 new_crypt = kmalloc(sizeof(struct ieee80211_crypt_data),
387                                     GFP_KERNEL);
388                 if (new_crypt == NULL)
389                         return -ENOMEM;
390                 memset(new_crypt, 0, sizeof(struct ieee80211_crypt_data));
391                 new_crypt->ops = ieee80211_get_crypto_ops("WEP");
392                 if (!new_crypt->ops) {
393                         request_module("ieee80211_crypt_wep");
394                         new_crypt->ops = ieee80211_get_crypto_ops("WEP");
395                 }
396
397                 if (new_crypt->ops && try_module_get(new_crypt->ops->owner))
398                         new_crypt->priv = new_crypt->ops->init(key);
399
400                 if (!new_crypt->ops || !new_crypt->priv) {
401                         kfree(new_crypt);
402                         new_crypt = NULL;
403
404                         printk(KERN_WARNING "%s: could not initialize WEP: "
405                                "load module ieee80211_crypt_wep\n", dev->name);
406                         return -EOPNOTSUPP;
407                 }
408                 *crypt = new_crypt;
409         }
410
411         /* If a new key was provided, set it up */
412         if (erq->length > 0) {
413                 len = erq->length <= 5 ? 5 : 13;
414                 memcpy(sec.keys[key], keybuf, erq->length);
415                 if (len > erq->length)
416                         memset(sec.keys[key] + erq->length, 0,
417                                len - erq->length);
418                 IEEE80211_DEBUG_WX("Setting key %d to '%s' (%d:%d bytes)\n",
419                                    key, escape_essid(sec.keys[key], len),
420                                    erq->length, len);
421                 sec.key_sizes[key] = len;
422                 if (*crypt)
423                         (*crypt)->ops->set_key(sec.keys[key], len, NULL,
424                                                (*crypt)->priv);
425                 sec.flags |= (1 << key);
426                 /* This ensures a key will be activated if no key is
427                  * explicitely set */
428                 if (key == sec.active_key)
429                         sec.flags |= SEC_ACTIVE_KEY;
430
431         } else {
432                 if (host_crypto) {
433                         len = (*crypt)->ops->get_key(sec.keys[key], WEP_KEY_LEN,
434                                                      NULL, (*crypt)->priv);
435                         if (len == 0) {
436                                 /* Set a default key of all 0 */
437                                 IEEE80211_DEBUG_WX("Setting key %d to all "
438                                                    "zero.\n", key);
439                                 memset(sec.keys[key], 0, 13);
440                                 (*crypt)->ops->set_key(sec.keys[key], 13, NULL,
441                                                        (*crypt)->priv);
442                                 sec.key_sizes[key] = 13;
443                                 sec.flags |= (1 << key);
444                         }
445                 }
446                 /* No key data - just set the default TX key index */
447                 if (key_provided) {
448                         IEEE80211_DEBUG_WX("Setting key %d to default Tx "
449                                            "key.\n", key);
450                         ieee->tx_keyidx = key;
451                         sec.active_key = key;
452                         sec.flags |= SEC_ACTIVE_KEY;
453                 }
454         }
455         if (erq->flags & (IW_ENCODE_OPEN | IW_ENCODE_RESTRICTED)) {
456                 ieee->open_wep = !(erq->flags & IW_ENCODE_RESTRICTED);
457                 sec.auth_mode = ieee->open_wep ? WLAN_AUTH_OPEN :
458                     WLAN_AUTH_SHARED_KEY;
459                 sec.flags |= SEC_AUTH_MODE;
460                 IEEE80211_DEBUG_WX("Auth: %s\n",
461                                    sec.auth_mode == WLAN_AUTH_OPEN ?
462                                    "OPEN" : "SHARED KEY");
463         }
464
465         /* For now we just support WEP, so only set that security level...
466          * TODO: When WPA is added this is one place that needs to change */
467         sec.flags |= SEC_LEVEL;
468         sec.level = SEC_LEVEL_1;        /* 40 and 104 bit WEP */
469         sec.encode_alg[key] = SEC_ALG_WEP;
470
471       done:
472         if (ieee->set_security)
473                 ieee->set_security(dev, &sec);
474
475         /* Do not reset port if card is in Managed mode since resetting will
476          * generate new IEEE 802.11 authentication which may end up in looping
477          * with IEEE 802.1X.  If your hardware requires a reset after WEP
478          * configuration (for example... Prism2), implement the reset_port in
479          * the callbacks structures used to initialize the 802.11 stack. */
480         if (ieee->reset_on_keychange &&
481             ieee->iw_mode != IW_MODE_INFRA &&
482             ieee->reset_port && ieee->reset_port(dev)) {
483                 printk(KERN_DEBUG "%s: reset_port failed\n", dev->name);
484                 return -EINVAL;
485         }
486         return 0;
487 }
488
489 int ieee80211_wx_get_encode(struct ieee80211_device *ieee,
490                             struct iw_request_info *info,
491                             union iwreq_data *wrqu, char *keybuf)
492 {
493         struct iw_point *erq = &(wrqu->encoding);
494         int len, key;
495         struct ieee80211_crypt_data *crypt;
496         struct ieee80211_security *sec = &ieee->sec;
497
498         IEEE80211_DEBUG_WX("GET_ENCODE\n");
499
500         key = erq->flags & IW_ENCODE_INDEX;
501         if (key) {
502                 if (key > WEP_KEYS)
503                         return -EINVAL;
504                 key--;
505         } else
506                 key = ieee->tx_keyidx;
507
508         crypt = ieee->crypt[key];
509         erq->flags = key + 1;
510
511         if (!sec->enabled) {
512                 erq->length = 0;
513                 erq->flags |= IW_ENCODE_DISABLED;
514                 return 0;
515         }
516
517         len = sec->key_sizes[key];
518         memcpy(keybuf, sec->keys[key], len);
519
520         erq->length = (len >= 0 ? len : 0);
521         erq->flags |= IW_ENCODE_ENABLED;
522
523         if (ieee->open_wep)
524                 erq->flags |= IW_ENCODE_OPEN;
525         else
526                 erq->flags |= IW_ENCODE_RESTRICTED;
527
528         return 0;
529 }
530
531 int ieee80211_wx_set_encodeext(struct ieee80211_device *ieee,
532                                struct iw_request_info *info,
533                                union iwreq_data *wrqu, char *extra)
534 {
535         struct net_device *dev = ieee->dev;
536         struct iw_point *encoding = &wrqu->encoding;
537         struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
538         int i, idx, ret = 0;
539         int group_key = 0;
540         const char *alg, *module;
541         struct ieee80211_crypto_ops *ops;
542         struct ieee80211_crypt_data **crypt;
543
544         struct ieee80211_security sec = {
545                 .flags = 0,
546         };
547
548         idx = encoding->flags & IW_ENCODE_INDEX;
549         if (idx) {
550                 if (idx < 1 || idx > WEP_KEYS)
551                         return -EINVAL;
552                 idx--;
553         } else
554                 idx = ieee->tx_keyidx;
555
556         if (ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) {
557                 crypt = &ieee->crypt[idx];
558                 group_key = 1;
559         } else {
560                 /* some Cisco APs use idx>0 for unicast in dynamic WEP */
561                 if (idx != 0 && ext->alg != IW_ENCODE_ALG_WEP)
562                         return -EINVAL;
563                 if (ieee->iw_mode == IW_MODE_INFRA)
564                         crypt = &ieee->crypt[idx];
565                 else
566                         return -EINVAL;
567         }
568
569         sec.flags |= SEC_ENABLED | SEC_ENCRYPT;
570         if ((encoding->flags & IW_ENCODE_DISABLED) ||
571             ext->alg == IW_ENCODE_ALG_NONE) {
572                 if (*crypt)
573                         ieee80211_crypt_delayed_deinit(ieee, crypt);
574
575                 for (i = 0; i < WEP_KEYS; i++)
576                         if (ieee->crypt[i] != NULL)
577                                 break;
578
579                 if (i == WEP_KEYS) {
580                         sec.enabled = 0;
581                         sec.encrypt = 0;
582                         sec.level = SEC_LEVEL_0;
583                         sec.flags |= SEC_LEVEL;
584                 }
585                 goto done;
586         }
587
588         sec.enabled = 1;
589         sec.encrypt = 1;
590
591         if (group_key ? !ieee->host_mc_decrypt :
592             !(ieee->host_encrypt || ieee->host_decrypt ||
593               ieee->host_encrypt_msdu))
594                 goto skip_host_crypt;
595
596         switch (ext->alg) {
597         case IW_ENCODE_ALG_WEP:
598                 alg = "WEP";
599                 module = "ieee80211_crypt_wep";
600                 break;
601         case IW_ENCODE_ALG_TKIP:
602                 alg = "TKIP";
603                 module = "ieee80211_crypt_tkip";
604                 break;
605         case IW_ENCODE_ALG_CCMP:
606                 alg = "CCMP";
607                 module = "ieee80211_crypt_ccmp";
608                 break;
609         default:
610                 IEEE80211_DEBUG_WX("%s: unknown crypto alg %d\n",
611                                    dev->name, ext->alg);
612                 ret = -EINVAL;
613                 goto done;
614         }
615
616         ops = ieee80211_get_crypto_ops(alg);
617         if (ops == NULL) {
618                 request_module(module);
619                 ops = ieee80211_get_crypto_ops(alg);
620         }
621         if (ops == NULL) {
622                 IEEE80211_DEBUG_WX("%s: unknown crypto alg %d\n",
623                                    dev->name, ext->alg);
624                 ret = -EINVAL;
625                 goto done;
626         }
627
628         if (*crypt == NULL || (*crypt)->ops != ops) {
629                 struct ieee80211_crypt_data *new_crypt;
630
631                 ieee80211_crypt_delayed_deinit(ieee, crypt);
632
633                 new_crypt = (struct ieee80211_crypt_data *)
634                     kmalloc(sizeof(*new_crypt), GFP_KERNEL);
635                 if (new_crypt == NULL) {
636                         ret = -ENOMEM;
637                         goto done;
638                 }
639                 memset(new_crypt, 0, sizeof(struct ieee80211_crypt_data));
640                 new_crypt->ops = ops;
641                 if (new_crypt->ops && try_module_get(new_crypt->ops->owner))
642                         new_crypt->priv = new_crypt->ops->init(idx);
643                 if (new_crypt->priv == NULL) {
644                         kfree(new_crypt);
645                         ret = -EINVAL;
646                         goto done;
647                 }
648                 *crypt = new_crypt;
649         }
650
651         if (ext->key_len > 0 && (*crypt)->ops->set_key &&
652             (*crypt)->ops->set_key(ext->key, ext->key_len, ext->rx_seq,
653                                    (*crypt)->priv) < 0) {
654                 IEEE80211_DEBUG_WX("%s: key setting failed\n", dev->name);
655                 ret = -EINVAL;
656                 goto done;
657         }
658
659       skip_host_crypt:
660         if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) {
661                 ieee->tx_keyidx = idx;
662                 sec.active_key = idx;
663                 sec.flags |= SEC_ACTIVE_KEY;
664         }
665
666         if (ext->alg != IW_ENCODE_ALG_NONE) {
667                 memcpy(sec.keys[idx], ext->key, ext->key_len);
668                 sec.key_sizes[idx] = ext->key_len;
669                 sec.flags |= (1 << idx);
670                 if (ext->alg == IW_ENCODE_ALG_WEP) {
671                         sec.encode_alg[idx] = SEC_ALG_WEP;
672                         sec.flags |= SEC_LEVEL;
673                         sec.level = SEC_LEVEL_1;
674                 } else if (ext->alg == IW_ENCODE_ALG_TKIP) {
675                         sec.encode_alg[idx] = SEC_ALG_TKIP;
676                         sec.flags |= SEC_LEVEL;
677                         sec.level = SEC_LEVEL_2;
678                 } else if (ext->alg == IW_ENCODE_ALG_CCMP) {
679                         sec.encode_alg[idx] = SEC_ALG_CCMP;
680                         sec.flags |= SEC_LEVEL;
681                         sec.level = SEC_LEVEL_3;
682                 }
683                 /* Don't set sec level for group keys. */
684                 if (group_key)
685                         sec.flags &= ~SEC_LEVEL;
686         }
687       done:
688         if (ieee->set_security)
689                 ieee->set_security(ieee->dev, &sec);
690
691         /*
692          * Do not reset port if card is in Managed mode since resetting will
693          * generate new IEEE 802.11 authentication which may end up in looping
694          * with IEEE 802.1X. If your hardware requires a reset after WEP
695          * configuration (for example... Prism2), implement the reset_port in
696          * the callbacks structures used to initialize the 802.11 stack.
697          */
698         if (ieee->reset_on_keychange &&
699             ieee->iw_mode != IW_MODE_INFRA &&
700             ieee->reset_port && ieee->reset_port(dev)) {
701                 IEEE80211_DEBUG_WX("%s: reset_port failed\n", dev->name);
702                 return -EINVAL;
703         }
704
705         return ret;
706 }
707
708 int ieee80211_wx_get_encodeext(struct ieee80211_device *ieee,
709                                struct iw_request_info *info,
710                                union iwreq_data *wrqu, char *extra)
711 {
712         struct iw_point *encoding = &wrqu->encoding;
713         struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
714         struct ieee80211_security *sec = &ieee->sec;
715         int idx, max_key_len;
716
717         max_key_len = encoding->length - sizeof(*ext);
718         if (max_key_len < 0)
719                 return -EINVAL;
720
721         idx = encoding->flags & IW_ENCODE_INDEX;
722         if (idx) {
723                 if (idx < 1 || idx > WEP_KEYS)
724                         return -EINVAL;
725                 idx--;
726         } else
727                 idx = ieee->tx_keyidx;
728
729         if (!ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY &&
730             ext->alg != IW_ENCODE_ALG_WEP)
731                 if (idx != 0 || ieee->iw_mode != IW_MODE_INFRA)
732                         return -EINVAL;
733
734         encoding->flags = idx + 1;
735         memset(ext, 0, sizeof(*ext));
736
737         if (!sec->enabled) {
738                 ext->alg = IW_ENCODE_ALG_NONE;
739                 ext->key_len = 0;
740                 encoding->flags |= IW_ENCODE_DISABLED;
741         } else {
742                 if (sec->encode_alg[idx] == SEC_ALG_WEP)
743                         ext->alg = IW_ENCODE_ALG_WEP;
744                 else if (sec->encode_alg[idx] == SEC_ALG_TKIP)
745                         ext->alg = IW_ENCODE_ALG_TKIP;
746                 else if (sec->encode_alg[idx] == SEC_ALG_CCMP)
747                         ext->alg = IW_ENCODE_ALG_CCMP;
748                 else
749                         return -EINVAL;
750
751                 ext->key_len = sec->key_sizes[idx];
752                 memcpy(ext->key, sec->keys[idx], ext->key_len);
753                 encoding->flags |= IW_ENCODE_ENABLED;
754                 if (ext->key_len &&
755                     (ext->alg == IW_ENCODE_ALG_TKIP ||
756                      ext->alg == IW_ENCODE_ALG_CCMP))
757                         ext->ext_flags |= IW_ENCODE_EXT_TX_SEQ_VALID;
758
759         }
760
761         return 0;
762 }
763
764 EXPORT_SYMBOL(ieee80211_wx_set_encodeext);
765 EXPORT_SYMBOL(ieee80211_wx_get_encodeext);
766
767 EXPORT_SYMBOL(ieee80211_wx_get_scan);
768 EXPORT_SYMBOL(ieee80211_wx_set_encode);
769 EXPORT_SYMBOL(ieee80211_wx_get_encode);