Merge master.kernel.org:/pub/scm/linux/kernel/git/roland/infiniband
[deliverable/linux.git] / 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 /* TODO: Fix these values... */
144 iwe.cmd = IWEVQUAL;
145 iwe.u.qual.qual = network->stats.signal;
146 iwe.u.qual.level = network->stats.rssi;
147 iwe.u.qual.noise = network->stats.noise;
148 iwe.u.qual.updated = network->stats.mask & IEEE80211_STATMASK_WEMASK;
149 if (!(network->stats.mask & IEEE80211_STATMASK_RSSI))
150 iwe.u.qual.updated |= IW_QUAL_LEVEL_INVALID;
151 if (!(network->stats.mask & IEEE80211_STATMASK_NOISE))
152 iwe.u.qual.updated |= IW_QUAL_NOISE_INVALID;
153 if (!(network->stats.mask & IEEE80211_STATMASK_SIGNAL))
154 iwe.u.qual.updated |= IW_QUAL_QUAL_INVALID;
155
156 start = iwe_stream_add_event(start, stop, &iwe, IW_EV_QUAL_LEN);
157
158 iwe.cmd = IWEVCUSTOM;
159 p = custom;
160
161 iwe.u.data.length = p - custom;
162 if (iwe.u.data.length)
163 start = iwe_stream_add_point(start, stop, &iwe, custom);
164
165 if (ieee->wpa_enabled && network->wpa_ie_len) {
166 char buf[MAX_WPA_IE_LEN * 2 + 30];
167
168 u8 *p = buf;
169 p += sprintf(p, "wpa_ie=");
170 for (i = 0; i < network->wpa_ie_len; i++) {
171 p += sprintf(p, "%02x", network->wpa_ie[i]);
172 }
173
174 memset(&iwe, 0, sizeof(iwe));
175 iwe.cmd = IWEVCUSTOM;
176 iwe.u.data.length = strlen(buf);
177 start = iwe_stream_add_point(start, stop, &iwe, buf);
178 }
179
180 if (ieee->wpa_enabled && network->rsn_ie_len) {
181 char buf[MAX_WPA_IE_LEN * 2 + 30];
182
183 u8 *p = buf;
184 p += sprintf(p, "rsn_ie=");
185 for (i = 0; i < network->rsn_ie_len; i++) {
186 p += sprintf(p, "%02x", network->rsn_ie[i]);
187 }
188
189 memset(&iwe, 0, sizeof(iwe));
190 iwe.cmd = IWEVCUSTOM;
191 iwe.u.data.length = strlen(buf);
192 start = iwe_stream_add_point(start, stop, &iwe, buf);
193 }
194
195 /* Add EXTRA: Age to display seconds since last beacon/probe response
196 * for given network. */
197 iwe.cmd = IWEVCUSTOM;
198 p = custom;
199 p += snprintf(p, MAX_CUSTOM_LEN - (p - custom),
200 " Last beacon: %lums ago",
201 (jiffies - network->last_scanned) / (HZ / 100));
202 iwe.u.data.length = p - custom;
203 if (iwe.u.data.length)
204 start = iwe_stream_add_point(start, stop, &iwe, custom);
205
206 return start;
207 }
208
209 int ieee80211_wx_get_scan(struct ieee80211_device *ieee,
210 struct iw_request_info *info,
211 union iwreq_data *wrqu, char *extra)
212 {
213 struct ieee80211_network *network;
214 unsigned long flags;
215
216 char *ev = extra;
217 char *stop = ev + IW_SCAN_MAX_DATA;
218 int i = 0;
219
220 IEEE80211_DEBUG_WX("Getting scan\n");
221
222 spin_lock_irqsave(&ieee->lock, flags);
223
224 list_for_each_entry(network, &ieee->network_list, list) {
225 i++;
226 if (ieee->scan_age == 0 ||
227 time_after(network->last_scanned + ieee->scan_age, jiffies))
228 ev = ipw2100_translate_scan(ieee, ev, stop, network);
229 else
230 IEEE80211_DEBUG_SCAN("Not showing network '%s ("
231 MAC_FMT ")' due to age (%lums).\n",
232 escape_essid(network->ssid,
233 network->ssid_len),
234 MAC_ARG(network->bssid),
235 (jiffies -
236 network->last_scanned) / (HZ /
237 100));
238 }
239
240 spin_unlock_irqrestore(&ieee->lock, flags);
241
242 wrqu->data.length = ev - extra;
243 wrqu->data.flags = 0;
244
245 IEEE80211_DEBUG_WX("exit: %d networks returned.\n", i);
246
247 return 0;
248 }
249
250 int ieee80211_wx_set_encode(struct ieee80211_device *ieee,
251 struct iw_request_info *info,
252 union iwreq_data *wrqu, char *keybuf)
253 {
254 struct iw_point *erq = &(wrqu->encoding);
255 struct net_device *dev = ieee->dev;
256 struct ieee80211_security sec = {
257 .flags = 0
258 };
259 int i, key, key_provided, len;
260 struct ieee80211_crypt_data **crypt;
261
262 IEEE80211_DEBUG_WX("SET_ENCODE\n");
263
264 key = erq->flags & IW_ENCODE_INDEX;
265 if (key) {
266 if (key > WEP_KEYS)
267 return -EINVAL;
268 key--;
269 key_provided = 1;
270 } else {
271 key_provided = 0;
272 key = ieee->tx_keyidx;
273 }
274
275 IEEE80211_DEBUG_WX("Key: %d [%s]\n", key, key_provided ?
276 "provided" : "default");
277
278 crypt = &ieee->crypt[key];
279
280 if (erq->flags & IW_ENCODE_DISABLED) {
281 if (key_provided && *crypt) {
282 IEEE80211_DEBUG_WX("Disabling encryption on key %d.\n",
283 key);
284 ieee80211_crypt_delayed_deinit(ieee, crypt);
285 } else
286 IEEE80211_DEBUG_WX("Disabling encryption.\n");
287
288 /* Check all the keys to see if any are still configured,
289 * and if no key index was provided, de-init them all */
290 for (i = 0; i < WEP_KEYS; i++) {
291 if (ieee->crypt[i] != NULL) {
292 if (key_provided)
293 break;
294 ieee80211_crypt_delayed_deinit(ieee,
295 &ieee->crypt[i]);
296 }
297 }
298
299 if (i == WEP_KEYS) {
300 sec.enabled = 0;
301 sec.level = SEC_LEVEL_0;
302 sec.flags |= SEC_ENABLED | SEC_LEVEL;
303 }
304
305 goto done;
306 }
307
308 sec.enabled = 1;
309 sec.flags |= SEC_ENABLED;
310
311 if (*crypt != NULL && (*crypt)->ops != NULL &&
312 strcmp((*crypt)->ops->name, "WEP") != 0) {
313 /* changing to use WEP; deinit previously used algorithm
314 * on this key */
315 ieee80211_crypt_delayed_deinit(ieee, crypt);
316 }
317
318 if (*crypt == NULL) {
319 struct ieee80211_crypt_data *new_crypt;
320
321 /* take WEP into use */
322 new_crypt = kmalloc(sizeof(struct ieee80211_crypt_data),
323 GFP_KERNEL);
324 if (new_crypt == NULL)
325 return -ENOMEM;
326 memset(new_crypt, 0, sizeof(struct ieee80211_crypt_data));
327 new_crypt->ops = ieee80211_get_crypto_ops("WEP");
328 if (!new_crypt->ops) {
329 request_module("ieee80211_crypt_wep");
330 new_crypt->ops = ieee80211_get_crypto_ops("WEP");
331 }
332
333 if (new_crypt->ops && try_module_get(new_crypt->ops->owner))
334 new_crypt->priv = new_crypt->ops->init(key);
335
336 if (!new_crypt->ops || !new_crypt->priv) {
337 kfree(new_crypt);
338 new_crypt = NULL;
339
340 printk(KERN_WARNING "%s: could not initialize WEP: "
341 "load module ieee80211_crypt_wep\n", dev->name);
342 return -EOPNOTSUPP;
343 }
344 *crypt = new_crypt;
345 }
346
347 /* If a new key was provided, set it up */
348 if (erq->length > 0) {
349 len = erq->length <= 5 ? 5 : 13;
350 memcpy(sec.keys[key], keybuf, erq->length);
351 if (len > erq->length)
352 memset(sec.keys[key] + erq->length, 0,
353 len - erq->length);
354 IEEE80211_DEBUG_WX("Setting key %d to '%s' (%d:%d bytes)\n",
355 key, escape_essid(sec.keys[key], len),
356 erq->length, len);
357 sec.key_sizes[key] = len;
358 (*crypt)->ops->set_key(sec.keys[key], len, NULL,
359 (*crypt)->priv);
360 sec.flags |= (1 << key);
361 /* This ensures a key will be activated if no key is
362 * explicitely set */
363 if (key == sec.active_key)
364 sec.flags |= SEC_ACTIVE_KEY;
365 } else {
366 len = (*crypt)->ops->get_key(sec.keys[key], WEP_KEY_LEN,
367 NULL, (*crypt)->priv);
368 if (len == 0) {
369 /* Set a default key of all 0 */
370 IEEE80211_DEBUG_WX("Setting key %d to all zero.\n",
371 key);
372 memset(sec.keys[key], 0, 13);
373 (*crypt)->ops->set_key(sec.keys[key], 13, NULL,
374 (*crypt)->priv);
375 sec.key_sizes[key] = 13;
376 sec.flags |= (1 << key);
377 }
378
379 /* No key data - just set the default TX key index */
380 if (key_provided) {
381 IEEE80211_DEBUG_WX
382 ("Setting key %d to default Tx key.\n", key);
383 ieee->tx_keyidx = key;
384 sec.active_key = key;
385 sec.flags |= SEC_ACTIVE_KEY;
386 }
387 }
388
389 done:
390 ieee->open_wep = !(erq->flags & IW_ENCODE_RESTRICTED);
391 sec.auth_mode = ieee->open_wep ? WLAN_AUTH_OPEN : WLAN_AUTH_SHARED_KEY;
392 sec.flags |= SEC_AUTH_MODE;
393 IEEE80211_DEBUG_WX("Auth: %s\n", sec.auth_mode == WLAN_AUTH_OPEN ?
394 "OPEN" : "SHARED KEY");
395
396 /* 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 */
398 sec.flags |= SEC_LEVEL;
399 sec.level = SEC_LEVEL_1; /* 40 and 104 bit WEP */
400
401 if (ieee->set_security)
402 ieee->set_security(dev, &sec);
403
404 /* Do not reset port if card is in Managed mode since resetting will
405 * generate new IEEE 802.11 authentication which may end up in looping
406 * with IEEE 802.1X. If your hardware requires a reset after WEP
407 * configuration (for example... Prism2), implement the reset_port in
408 * the callbacks structures used to initialize the 802.11 stack. */
409 if (ieee->reset_on_keychange &&
410 ieee->iw_mode != IW_MODE_INFRA &&
411 ieee->reset_port && ieee->reset_port(dev)) {
412 printk(KERN_DEBUG "%s: reset_port failed\n", dev->name);
413 return -EINVAL;
414 }
415 return 0;
416 }
417
418 int ieee80211_wx_get_encode(struct ieee80211_device *ieee,
419 struct iw_request_info *info,
420 union iwreq_data *wrqu, char *keybuf)
421 {
422 struct iw_point *erq = &(wrqu->encoding);
423 int len, key;
424 struct ieee80211_crypt_data *crypt;
425
426 IEEE80211_DEBUG_WX("GET_ENCODE\n");
427
428 key = erq->flags & IW_ENCODE_INDEX;
429 if (key) {
430 if (key > WEP_KEYS)
431 return -EINVAL;
432 key--;
433 } else
434 key = ieee->tx_keyidx;
435
436 crypt = ieee->crypt[key];
437 erq->flags = key + 1;
438
439 if (crypt == NULL || crypt->ops == NULL) {
440 erq->length = 0;
441 erq->flags |= IW_ENCODE_DISABLED;
442 return 0;
443 }
444
445 if (strcmp(crypt->ops->name, "WEP") != 0) {
446 /* only WEP is supported with wireless extensions, so just
447 * report that encryption is used */
448 erq->length = 0;
449 erq->flags |= IW_ENCODE_ENABLED;
450 return 0;
451 }
452
453 len = crypt->ops->get_key(keybuf, WEP_KEY_LEN, NULL, crypt->priv);
454 erq->length = (len >= 0 ? len : 0);
455
456 erq->flags |= IW_ENCODE_ENABLED;
457
458 if (ieee->open_wep)
459 erq->flags |= IW_ENCODE_OPEN;
460 else
461 erq->flags |= IW_ENCODE_RESTRICTED;
462
463 return 0;
464 }
465
466 EXPORT_SYMBOL(ieee80211_wx_get_scan);
467 EXPORT_SYMBOL(ieee80211_wx_set_encode);
468 EXPORT_SYMBOL(ieee80211_wx_get_encode);
This page took 0.04306 seconds and 5 git commands to generate.