[PATCH] Changed 802.11 headers to use ieee80211_info_element[0]
[linux-2.6] / net / ieee80211 / ieee80211_wx.c
1 /******************************************************************************
2
3   Copyright(c) 2004 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
36 #include <net/ieee80211.h>
37 #include <linux/wireless.h>
38
39 static const char *ieee80211_modes[] = {
40         "?", "a", "b", "ab", "g", "ag", "bg", "abg"
41 };
42
43 #define MAX_CUSTOM_LEN 64
44 static inline char *ipw2100_translate_scan(struct ieee80211_device *ieee,
45                                            char *start, char *stop,
46                                            struct ieee80211_network *network)
47 {
48         char custom[MAX_CUSTOM_LEN];
49         char *p;
50         struct iw_event iwe;
51         int i, j;
52         u8 max_rate, rate;
53
54         /* First entry *MUST* be the AP MAC address */
55         iwe.cmd = SIOCGIWAP;
56         iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
57         memcpy(iwe.u.ap_addr.sa_data, network->bssid, ETH_ALEN);
58         start = iwe_stream_add_event(start, stop, &iwe, IW_EV_ADDR_LEN);
59
60         /* Remaining entries will be displayed in the order we provide them */
61
62         /* Add the ESSID */
63         iwe.cmd = SIOCGIWESSID;
64         iwe.u.data.flags = 1;
65         if (network->flags & NETWORK_EMPTY_ESSID) {
66                 iwe.u.data.length = sizeof("<hidden>");
67                 start = iwe_stream_add_point(start, stop, &iwe, "<hidden>");
68         } else {
69                 iwe.u.data.length = min(network->ssid_len, (u8) 32);
70                 start = iwe_stream_add_point(start, stop, &iwe, network->ssid);
71         }
72
73         /* Add the protocol name */
74         iwe.cmd = SIOCGIWNAME;
75         snprintf(iwe.u.name, IFNAMSIZ, "IEEE 802.11%s",
76                  ieee80211_modes[network->mode]);
77         start = iwe_stream_add_event(start, stop, &iwe, IW_EV_CHAR_LEN);
78
79         /* Add mode */
80         iwe.cmd = SIOCGIWMODE;
81         if (network->capability & (WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS)) {
82                 if (network->capability & WLAN_CAPABILITY_ESS)
83                         iwe.u.mode = IW_MODE_MASTER;
84                 else
85                         iwe.u.mode = IW_MODE_ADHOC;
86
87                 start = iwe_stream_add_event(start, stop, &iwe, IW_EV_UINT_LEN);
88         }
89
90         /* Add frequency/channel */
91         iwe.cmd = SIOCGIWFREQ;
92 /*      iwe.u.freq.m = ieee80211_frequency(network->channel, network->mode);
93         iwe.u.freq.e = 3; */
94         iwe.u.freq.m = network->channel;
95         iwe.u.freq.e = 0;
96         iwe.u.freq.i = 0;
97         start = iwe_stream_add_event(start, stop, &iwe, IW_EV_FREQ_LEN);
98
99         /* Add encryption capability */
100         iwe.cmd = SIOCGIWENCODE;
101         if (network->capability & WLAN_CAPABILITY_PRIVACY)
102                 iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
103         else
104                 iwe.u.data.flags = IW_ENCODE_DISABLED;
105         iwe.u.data.length = 0;
106         start = iwe_stream_add_point(start, stop, &iwe, network->ssid);
107
108         /* Add basic and extended rates */
109         max_rate = 0;
110         p = custom;
111         p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), " Rates (Mb/s): ");
112         for (i = 0, j = 0; i < network->rates_len;) {
113                 if (j < network->rates_ex_len &&
114                     ((network->rates_ex[j] & 0x7F) <
115                      (network->rates[i] & 0x7F)))
116                         rate = network->rates_ex[j++] & 0x7F;
117                 else
118                         rate = network->rates[i++] & 0x7F;
119                 if (rate > max_rate)
120                         max_rate = rate;
121                 p += snprintf(p, MAX_CUSTOM_LEN - (p - custom),
122                               "%d%s ", rate >> 1, (rate & 1) ? ".5" : "");
123         }
124         for (; j < network->rates_ex_len; j++) {
125                 rate = network->rates_ex[j] & 0x7F;
126                 p += snprintf(p, MAX_CUSTOM_LEN - (p - custom),
127                               "%d%s ", rate >> 1, (rate & 1) ? ".5" : "");
128                 if (rate > max_rate)
129                         max_rate = rate;
130         }
131
132         iwe.cmd = SIOCGIWRATE;
133         iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0;
134         iwe.u.bitrate.value = max_rate * 500000;
135         start = iwe_stream_add_event(start, stop, &iwe, IW_EV_PARAM_LEN);
136
137         iwe.cmd = IWEVCUSTOM;
138         iwe.u.data.length = p - custom;
139         if (iwe.u.data.length)
140                 start = iwe_stream_add_point(start, stop, &iwe, custom);
141
142         /* Add quality statistics */
143         iwe.cmd = IWEVQUAL;
144         iwe.u.qual.updated = IW_QUAL_QUAL_UPDATED | IW_QUAL_LEVEL_UPDATED |
145             IW_QUAL_NOISE_UPDATED;
146
147         if (!(network->stats.mask & IEEE80211_STATMASK_RSSI)) {
148                 iwe.u.qual.updated |= IW_QUAL_QUAL_INVALID |
149                     IW_QUAL_LEVEL_INVALID;
150                 iwe.u.qual.qual = 0;
151                 iwe.u.qual.level = 0;
152         } else {
153                 iwe.u.qual.level = network->stats.rssi;
154                 iwe.u.qual.qual =
155                     (100 *
156                      (ieee->perfect_rssi - ieee->worst_rssi) *
157                      (ieee->perfect_rssi - ieee->worst_rssi) -
158                      (ieee->perfect_rssi - network->stats.rssi) *
159                      (15 * (ieee->perfect_rssi - ieee->worst_rssi) +
160                       62 * (ieee->perfect_rssi - network->stats.rssi))) /
161                     ((ieee->perfect_rssi - ieee->worst_rssi) *
162                      (ieee->perfect_rssi - ieee->worst_rssi));
163                 if (iwe.u.qual.qual > 100)
164                         iwe.u.qual.qual = 100;
165                 else if (iwe.u.qual.qual < 1)
166                         iwe.u.qual.qual = 0;
167         }
168
169         if (!(network->stats.mask & IEEE80211_STATMASK_NOISE)) {
170                 iwe.u.qual.updated |= IW_QUAL_NOISE_INVALID;
171                 iwe.u.qual.noise = 0;
172         } else {
173                 iwe.u.qual.noise = network->stats.noise;
174         }
175
176         start = iwe_stream_add_event(start, stop, &iwe, IW_EV_QUAL_LEN);
177
178         iwe.cmd = IWEVCUSTOM;
179         p = custom;
180
181         iwe.u.data.length = p - custom;
182         if (iwe.u.data.length)
183                 start = iwe_stream_add_point(start, stop, &iwe, custom);
184
185         if (ieee->wpa_enabled && network->wpa_ie_len) {
186                 char buf[MAX_WPA_IE_LEN * 2 + 30];
187
188                 u8 *p = buf;
189                 p += sprintf(p, "wpa_ie=");
190                 for (i = 0; i < network->wpa_ie_len; i++) {
191                         p += sprintf(p, "%02x", network->wpa_ie[i]);
192                 }
193
194                 memset(&iwe, 0, sizeof(iwe));
195                 iwe.cmd = IWEVCUSTOM;
196                 iwe.u.data.length = strlen(buf);
197                 start = iwe_stream_add_point(start, stop, &iwe, buf);
198         }
199
200         if (ieee->wpa_enabled && network->rsn_ie_len) {
201                 char buf[MAX_WPA_IE_LEN * 2 + 30];
202
203                 u8 *p = buf;
204                 p += sprintf(p, "rsn_ie=");
205                 for (i = 0; i < network->rsn_ie_len; i++) {
206                         p += sprintf(p, "%02x", network->rsn_ie[i]);
207                 }
208
209                 memset(&iwe, 0, sizeof(iwe));
210                 iwe.cmd = IWEVCUSTOM;
211                 iwe.u.data.length = strlen(buf);
212                 start = iwe_stream_add_point(start, stop, &iwe, buf);
213         }
214
215         /* Add EXTRA: Age to display seconds since last beacon/probe response
216          * for given network. */
217         iwe.cmd = IWEVCUSTOM;
218         p = custom;
219         p += snprintf(p, MAX_CUSTOM_LEN - (p - custom),
220                       " Last beacon: %lums ago",
221                       (jiffies - network->last_scanned) / (HZ / 100));
222         iwe.u.data.length = p - custom;
223         if (iwe.u.data.length)
224                 start = iwe_stream_add_point(start, stop, &iwe, custom);
225
226         return start;
227 }
228
229 int ieee80211_wx_get_scan(struct ieee80211_device *ieee,
230                           struct iw_request_info *info,
231                           union iwreq_data *wrqu, char *extra)
232 {
233         struct ieee80211_network *network;
234         unsigned long flags;
235
236         char *ev = extra;
237         char *stop = ev + IW_SCAN_MAX_DATA;
238         int i = 0;
239
240         IEEE80211_DEBUG_WX("Getting scan\n");
241
242         spin_lock_irqsave(&ieee->lock, flags);
243
244         list_for_each_entry(network, &ieee->network_list, list) {
245                 i++;
246                 if (ieee->scan_age == 0 ||
247                     time_after(network->last_scanned + ieee->scan_age, jiffies))
248                         ev = ipw2100_translate_scan(ieee, ev, stop, network);
249                 else
250                         IEEE80211_DEBUG_SCAN("Not showing network '%s ("
251                                              MAC_FMT ")' due to age (%lums).\n",
252                                              escape_essid(network->ssid,
253                                                           network->ssid_len),
254                                              MAC_ARG(network->bssid),
255                                              (jiffies -
256                                               network->last_scanned) / (HZ /
257                                                                         100));
258         }
259
260         spin_unlock_irqrestore(&ieee->lock, flags);
261
262         wrqu->data.length = ev - extra;
263         wrqu->data.flags = 0;
264
265         IEEE80211_DEBUG_WX("exit: %d networks returned.\n", i);
266
267         return 0;
268 }
269
270 int ieee80211_wx_set_encode(struct ieee80211_device *ieee,
271                             struct iw_request_info *info,
272                             union iwreq_data *wrqu, char *keybuf)
273 {
274         struct iw_point *erq = &(wrqu->encoding);
275         struct net_device *dev = ieee->dev;
276         struct ieee80211_security sec = {
277                 .flags = 0
278         };
279         int i, key, key_provided, len;
280         struct ieee80211_crypt_data **crypt;
281
282         IEEE80211_DEBUG_WX("SET_ENCODE\n");
283
284         key = erq->flags & IW_ENCODE_INDEX;
285         if (key) {
286                 if (key > WEP_KEYS)
287                         return -EINVAL;
288                 key--;
289                 key_provided = 1;
290         } else {
291                 key_provided = 0;
292                 key = ieee->tx_keyidx;
293         }
294
295         IEEE80211_DEBUG_WX("Key: %d [%s]\n", key, key_provided ?
296                            "provided" : "default");
297
298         crypt = &ieee->crypt[key];
299
300         if (erq->flags & IW_ENCODE_DISABLED) {
301                 if (key_provided && *crypt) {
302                         IEEE80211_DEBUG_WX("Disabling encryption on key %d.\n",
303                                            key);
304                         ieee80211_crypt_delayed_deinit(ieee, crypt);
305                 } else
306                         IEEE80211_DEBUG_WX("Disabling encryption.\n");
307
308                 /* Check all the keys to see if any are still configured,
309                  * and if no key index was provided, de-init them all */
310                 for (i = 0; i < WEP_KEYS; i++) {
311                         if (ieee->crypt[i] != NULL) {
312                                 if (key_provided)
313                                         break;
314                                 ieee80211_crypt_delayed_deinit(ieee,
315                                                                &ieee->crypt[i]);
316                         }
317                 }
318
319                 if (i == WEP_KEYS) {
320                         sec.enabled = 0;
321                         sec.level = SEC_LEVEL_0;
322                         sec.flags |= SEC_ENABLED | SEC_LEVEL;
323                 }
324
325                 goto done;
326         }
327
328         sec.enabled = 1;
329         sec.flags |= SEC_ENABLED;
330
331         if (*crypt != NULL && (*crypt)->ops != NULL &&
332             strcmp((*crypt)->ops->name, "WEP") != 0) {
333                 /* changing to use WEP; deinit previously used algorithm
334                  * on this key */
335                 ieee80211_crypt_delayed_deinit(ieee, crypt);
336         }
337
338         if (*crypt == NULL) {
339                 struct ieee80211_crypt_data *new_crypt;
340
341                 /* take WEP into use */
342                 new_crypt = kmalloc(sizeof(struct ieee80211_crypt_data),
343                                     GFP_KERNEL);
344                 if (new_crypt == NULL)
345                         return -ENOMEM;
346                 memset(new_crypt, 0, sizeof(struct ieee80211_crypt_data));
347                 new_crypt->ops = ieee80211_get_crypto_ops("WEP");
348                 if (!new_crypt->ops) {
349                         request_module("ieee80211_crypt_wep");
350                         new_crypt->ops = ieee80211_get_crypto_ops("WEP");
351                 }
352
353                 if (new_crypt->ops && try_module_get(new_crypt->ops->owner))
354                         new_crypt->priv = new_crypt->ops->init(key);
355
356                 if (!new_crypt->ops || !new_crypt->priv) {
357                         kfree(new_crypt);
358                         new_crypt = NULL;
359
360                         printk(KERN_WARNING "%s: could not initialize WEP: "
361                                "load module ieee80211_crypt_wep\n", dev->name);
362                         return -EOPNOTSUPP;
363                 }
364                 *crypt = new_crypt;
365         }
366
367         /* If a new key was provided, set it up */
368         if (erq->length > 0) {
369                 len = erq->length <= 5 ? 5 : 13;
370                 memcpy(sec.keys[key], keybuf, erq->length);
371                 if (len > erq->length)
372                         memset(sec.keys[key] + erq->length, 0,
373                                len - erq->length);
374                 IEEE80211_DEBUG_WX("Setting key %d to '%s' (%d:%d bytes)\n",
375                                    key, escape_essid(sec.keys[key], len),
376                                    erq->length, len);
377                 sec.key_sizes[key] = len;
378                 (*crypt)->ops->set_key(sec.keys[key], len, NULL,
379                                        (*crypt)->priv);
380                 sec.flags |= (1 << key);
381                 /* This ensures a key will be activated if no key is
382                  * explicitely set */
383                 if (key == sec.active_key)
384                         sec.flags |= SEC_ACTIVE_KEY;
385         } else {
386                 len = (*crypt)->ops->get_key(sec.keys[key], WEP_KEY_LEN,
387                                              NULL, (*crypt)->priv);
388                 if (len == 0) {
389                         /* Set a default key of all 0 */
390                         IEEE80211_DEBUG_WX("Setting key %d to all zero.\n",
391                                            key);
392                         memset(sec.keys[key], 0, 13);
393                         (*crypt)->ops->set_key(sec.keys[key], 13, NULL,
394                                                (*crypt)->priv);
395                         sec.key_sizes[key] = 13;
396                         sec.flags |= (1 << key);
397                 }
398
399                 /* No key data - just set the default TX key index */
400                 if (key_provided) {
401                         IEEE80211_DEBUG_WX
402                             ("Setting key %d to default Tx key.\n", key);
403                         ieee->tx_keyidx = key;
404                         sec.active_key = key;
405                         sec.flags |= SEC_ACTIVE_KEY;
406                 }
407         }
408
409       done:
410         ieee->open_wep = !(erq->flags & IW_ENCODE_RESTRICTED);
411         sec.auth_mode = ieee->open_wep ? WLAN_AUTH_OPEN : WLAN_AUTH_SHARED_KEY;
412         sec.flags |= SEC_AUTH_MODE;
413         IEEE80211_DEBUG_WX("Auth: %s\n", sec.auth_mode == WLAN_AUTH_OPEN ?
414                            "OPEN" : "SHARED KEY");
415
416         /* For now we just support WEP, so only set that security level...
417          * TODO: When WPA is added this is one place that needs to change */
418         sec.flags |= SEC_LEVEL;
419         sec.level = SEC_LEVEL_1;        /* 40 and 104 bit WEP */
420
421         if (ieee->set_security)
422                 ieee->set_security(dev, &sec);
423
424         /* Do not reset port if card is in Managed mode since resetting will
425          * generate new IEEE 802.11 authentication which may end up in looping
426          * with IEEE 802.1X.  If your hardware requires a reset after WEP
427          * configuration (for example... Prism2), implement the reset_port in
428          * the callbacks structures used to initialize the 802.11 stack. */
429         if (ieee->reset_on_keychange &&
430             ieee->iw_mode != IW_MODE_INFRA &&
431             ieee->reset_port && ieee->reset_port(dev)) {
432                 printk(KERN_DEBUG "%s: reset_port failed\n", dev->name);
433                 return -EINVAL;
434         }
435         return 0;
436 }
437
438 int ieee80211_wx_get_encode(struct ieee80211_device *ieee,
439                             struct iw_request_info *info,
440                             union iwreq_data *wrqu, char *keybuf)
441 {
442         struct iw_point *erq = &(wrqu->encoding);
443         int len, key;
444         struct ieee80211_crypt_data *crypt;
445
446         IEEE80211_DEBUG_WX("GET_ENCODE\n");
447
448         key = erq->flags & IW_ENCODE_INDEX;
449         if (key) {
450                 if (key > WEP_KEYS)
451                         return -EINVAL;
452                 key--;
453         } else
454                 key = ieee->tx_keyidx;
455
456         crypt = ieee->crypt[key];
457         erq->flags = key + 1;
458
459         if (crypt == NULL || crypt->ops == NULL) {
460                 erq->length = 0;
461                 erq->flags |= IW_ENCODE_DISABLED;
462                 return 0;
463         }
464
465         if (strcmp(crypt->ops->name, "WEP") != 0) {
466                 /* only WEP is supported with wireless extensions, so just
467                  * report that encryption is used */
468                 erq->length = 0;
469                 erq->flags |= IW_ENCODE_ENABLED;
470                 return 0;
471         }
472
473         len = crypt->ops->get_key(keybuf, WEP_KEY_LEN, NULL, crypt->priv);
474         erq->length = (len >= 0 ? len : 0);
475
476         erq->flags |= IW_ENCODE_ENABLED;
477
478         if (ieee->open_wep)
479                 erq->flags |= IW_ENCODE_OPEN;
480         else
481                 erq->flags |= IW_ENCODE_RESTRICTED;
482
483         return 0;
484 }
485
486 EXPORT_SYMBOL(ieee80211_wx_get_scan);
487 EXPORT_SYMBOL(ieee80211_wx_set_encode);
488 EXPORT_SYMBOL(ieee80211_wx_get_encode);