aboutsummaryrefslogtreecommitdiffstats
path: root/net/ieee80211/ieee80211_wx.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/ieee80211/ieee80211_wx.c')
-rw-r--r--net/ieee80211/ieee80211_wx.c471
1 files changed, 471 insertions, 0 deletions
diff --git a/net/ieee80211/ieee80211_wx.c b/net/ieee80211/ieee80211_wx.c
new file mode 100644
index 000000000000..25a868c2e2f7
--- /dev/null
+++ b/net/ieee80211/ieee80211_wx.c
@@ -0,0 +1,471 @@
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#include <linux/wireless.h>
33#include <linux/version.h>
34#include <linux/kmod.h>
35#include <linux/module.h>
36
37#include <net/ieee80211.h>
38static const char *ieee80211_modes[] = {
39 "?", "a", "b", "ab", "g", "ag", "bg", "abg"
40};
41
42#define MAX_CUSTOM_LEN 64
43static inline char *ipw2100_translate_scan(struct ieee80211_device *ieee,
44 char *start, char *stop,
45 struct ieee80211_network *network)
46{
47 char custom[MAX_CUSTOM_LEN];
48 char *p;
49 struct iw_event iwe;
50 int i, j;
51 u8 max_rate, rate;
52
53 /* First entry *MUST* be the AP MAC address */
54 iwe.cmd = SIOCGIWAP;
55 iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
56 memcpy(iwe.u.ap_addr.sa_data, network->bssid, ETH_ALEN);
57 start = iwe_stream_add_event(start, stop, &iwe, IW_EV_ADDR_LEN);
58
59 /* Remaining entries will be displayed in the order we provide them */
60
61 /* Add the ESSID */
62 iwe.cmd = SIOCGIWESSID;
63 iwe.u.data.flags = 1;
64 if (network->flags & NETWORK_EMPTY_ESSID) {
65 iwe.u.data.length = sizeof("<hidden>");
66 start = iwe_stream_add_point(start, stop, &iwe, "<hidden>");
67 } else {
68 iwe.u.data.length = min(network->ssid_len, (u8)32);
69 start = iwe_stream_add_point(start, stop, &iwe, network->ssid);
70 }
71
72 /* Add the protocol name */
73 iwe.cmd = SIOCGIWNAME;
74 snprintf(iwe.u.name, IFNAMSIZ, "IEEE 802.11%s", ieee80211_modes[network->mode]);
75 start = iwe_stream_add_event(start, stop, &iwe, IW_EV_CHAR_LEN);
76
77 /* Add mode */
78 iwe.cmd = SIOCGIWMODE;
79 if (network->capability &
80 (WLAN_CAPABILITY_BSS | WLAN_CAPABILITY_IBSS)) {
81 if (network->capability & WLAN_CAPABILITY_BSS)
82 iwe.u.mode = IW_MODE_MASTER;
83 else
84 iwe.u.mode = IW_MODE_ADHOC;
85
86 start = iwe_stream_add_event(start, stop, &iwe,
87 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,
136 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 /* TODO: Fix these values... */
145 iwe.cmd = IWEVQUAL;
146 iwe.u.qual.qual = network->stats.signal;
147 iwe.u.qual.level = network->stats.rssi;
148 iwe.u.qual.noise = network->stats.noise;
149 iwe.u.qual.updated = network->stats.mask & IEEE80211_STATMASK_WEMASK;
150 if (!(network->stats.mask & IEEE80211_STATMASK_RSSI))
151 iwe.u.qual.updated |= IW_QUAL_LEVEL_INVALID;
152 if (!(network->stats.mask & IEEE80211_STATMASK_NOISE))
153 iwe.u.qual.updated |= IW_QUAL_NOISE_INVALID;
154 if (!(network->stats.mask & IEEE80211_STATMASK_SIGNAL))
155 iwe.u.qual.updated |= IW_QUAL_QUAL_INVALID;
156
157 start = iwe_stream_add_event(start, stop, &iwe, IW_EV_QUAL_LEN);
158
159 iwe.cmd = IWEVCUSTOM;
160 p = custom;
161
162 iwe.u.data.length = p - custom;
163 if (iwe.u.data.length)
164 start = iwe_stream_add_point(start, stop, &iwe, custom);
165
166 if (ieee->wpa_enabled && network->wpa_ie_len){
167 char buf[MAX_WPA_IE_LEN * 2 + 30];
168
169 u8 *p = buf;
170 p += sprintf(p, "wpa_ie=");
171 for (i = 0; i < network->wpa_ie_len; i++) {
172 p += sprintf(p, "%02x", network->wpa_ie[i]);
173 }
174
175 memset(&iwe, 0, sizeof(iwe));
176 iwe.cmd = IWEVCUSTOM;
177 iwe.u.data.length = strlen(buf);
178 start = iwe_stream_add_point(start, stop, &iwe, buf);
179 }
180
181 if (ieee->wpa_enabled && network->rsn_ie_len){
182 char buf[MAX_WPA_IE_LEN * 2 + 30];
183
184 u8 *p = buf;
185 p += sprintf(p, "rsn_ie=");
186 for (i = 0; i < network->rsn_ie_len; i++) {
187 p += sprintf(p, "%02x", network->rsn_ie[i]);
188 }
189
190 memset(&iwe, 0, sizeof(iwe));
191 iwe.cmd = IWEVCUSTOM;
192 iwe.u.data.length = strlen(buf);
193 start = iwe_stream_add_point(start, stop, &iwe, buf);
194 }
195
196 /* Add EXTRA: Age to display seconds since last beacon/probe response
197 * for given network. */
198 iwe.cmd = IWEVCUSTOM;
199 p = custom;
200 p += snprintf(p, MAX_CUSTOM_LEN - (p - custom),
201 " Last beacon: %lums ago", (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
207 return start;
208}
209
210int ieee80211_wx_get_scan(struct ieee80211_device *ieee,
211 struct iw_request_info *info,
212 union iwreq_data *wrqu, char *extra)
213{
214 struct ieee80211_network *network;
215 unsigned long flags;
216
217 char *ev = extra;
218 char *stop = ev + IW_SCAN_MAX_DATA;
219 int i = 0;
220
221 IEEE80211_DEBUG_WX("Getting scan\n");
222
223 spin_lock_irqsave(&ieee->lock, flags);
224
225 list_for_each_entry(network, &ieee->network_list, list) {
226 i++;
227 if (ieee->scan_age == 0 ||
228 time_after(network->last_scanned + ieee->scan_age, jiffies))
229 ev = ipw2100_translate_scan(ieee, ev, stop, network);
230 else
231 IEEE80211_DEBUG_SCAN(
232 "Not showing network '%s ("
233 MAC_FMT ")' due to age (%lums).\n",
234 escape_essid(network->ssid,
235 network->ssid_len),
236 MAC_ARG(network->bssid),
237 (jiffies - network->last_scanned) / (HZ / 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
250int 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(
295 ieee, &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
309
310 sec.enabled = 1;
311 sec.flags |= SEC_ENABLED;
312
313 if (*crypt != NULL && (*crypt)->ops != NULL &&
314 strcmp((*crypt)->ops->name, "WEP") != 0) {
315 /* changing to use WEP; deinit previously used algorithm
316 * on this key */
317 ieee80211_crypt_delayed_deinit(ieee, crypt);
318 }
319
320 if (*crypt == NULL) {
321 struct ieee80211_crypt_data *new_crypt;
322
323 /* take WEP into use */
324 new_crypt = kmalloc(sizeof(struct ieee80211_crypt_data),
325 GFP_KERNEL);
326 if (new_crypt == NULL)
327 return -ENOMEM;
328 memset(new_crypt, 0, sizeof(struct ieee80211_crypt_data));
329 new_crypt->ops = ieee80211_get_crypto_ops("WEP");
330 if (!new_crypt->ops) {
331 request_module("ieee80211_crypt_wep");
332 new_crypt->ops = ieee80211_get_crypto_ops("WEP");
333 }
334
335 if (new_crypt->ops && try_module_get(new_crypt->ops->owner))
336 new_crypt->priv = new_crypt->ops->init(key);
337
338 if (!new_crypt->ops || !new_crypt->priv) {
339 kfree(new_crypt);
340 new_crypt = NULL;
341
342 printk(KERN_WARNING "%s: could not initialize WEP: "
343 "load module ieee80211_crypt_wep\n",
344 dev->name);
345 return -EOPNOTSUPP;
346 }
347 *crypt = new_crypt;
348 }
349
350 /* If a new key was provided, set it up */
351 if (erq->length > 0) {
352 len = erq->length <= 5 ? 5 : 13;
353 memcpy(sec.keys[key], keybuf, erq->length);
354 if (len > erq->length)
355 memset(sec.keys[key] + erq->length, 0,
356 len - erq->length);
357 IEEE80211_DEBUG_WX("Setting key %d to '%s' (%d:%d bytes)\n",
358 key, escape_essid(sec.keys[key], len),
359 erq->length, len);
360 sec.key_sizes[key] = len;
361 (*crypt)->ops->set_key(sec.keys[key], len, NULL,
362 (*crypt)->priv);
363 sec.flags |= (1 << key);
364 /* This ensures a key will be activated if no key is
365 * explicitely set */
366 if (key == sec.active_key)
367 sec.flags |= SEC_ACTIVE_KEY;
368 } else {
369 len = (*crypt)->ops->get_key(sec.keys[key], WEP_KEY_LEN,
370 NULL, (*crypt)->priv);
371 if (len == 0) {
372 /* Set a default key of all 0 */
373 IEEE80211_DEBUG_WX("Setting key %d to all zero.\n",
374 key);
375 memset(sec.keys[key], 0, 13);
376 (*crypt)->ops->set_key(sec.keys[key], 13, NULL,
377 (*crypt)->priv);
378 sec.key_sizes[key] = 13;
379 sec.flags |= (1 << key);
380 }
381
382 /* No key data - just set the default TX key index */
383 if (key_provided) {
384 IEEE80211_DEBUG_WX(
385 "Setting key %d to default Tx key.\n", key);
386 ieee->tx_keyidx = key;
387 sec.active_key = key;
388 sec.flags |= SEC_ACTIVE_KEY;
389 }
390 }
391
392 done:
393 ieee->open_wep = !(erq->flags & IW_ENCODE_RESTRICTED);
394 sec.auth_mode = ieee->open_wep ? WLAN_AUTH_OPEN : WLAN_AUTH_SHARED_KEY;
395 sec.flags |= SEC_AUTH_MODE;
396 IEEE80211_DEBUG_WX("Auth: %s\n", sec.auth_mode == WLAN_AUTH_OPEN ?
397 "OPEN" : "SHARED KEY");
398
399 /* For now we just support WEP, so only set that security level...
400 * TODO: When WPA is added this is one place that needs to change */
401 sec.flags |= SEC_LEVEL;
402 sec.level = SEC_LEVEL_1; /* 40 and 104 bit WEP */
403
404 if (ieee->set_security)
405 ieee->set_security(dev, &sec);
406
407 /* Do not reset port if card is in Managed mode since resetting will
408 * generate new IEEE 802.11 authentication which may end up in looping
409 * with IEEE 802.1X. If your hardware requires a reset after WEP
410 * configuration (for example... Prism2), implement the reset_port in
411 * the callbacks structures used to initialize the 802.11 stack. */
412 if (ieee->reset_on_keychange &&
413 ieee->iw_mode != IW_MODE_INFRA &&
414 ieee->reset_port && ieee->reset_port(dev)) {
415 printk(KERN_DEBUG "%s: reset_port failed\n", dev->name);
416 return -EINVAL;
417 }
418 return 0;
419}
420
421int ieee80211_wx_get_encode(struct ieee80211_device *ieee,
422 struct iw_request_info *info,
423 union iwreq_data *wrqu, char *keybuf)
424{
425 struct iw_point *erq = &(wrqu->encoding);
426 int len, key;
427 struct ieee80211_crypt_data *crypt;
428
429 IEEE80211_DEBUG_WX("GET_ENCODE\n");
430
431 key = erq->flags & IW_ENCODE_INDEX;
432 if (key) {
433 if (key > WEP_KEYS)
434 return -EINVAL;
435 key--;
436 } else
437 key = ieee->tx_keyidx;
438
439 crypt = ieee->crypt[key];
440 erq->flags = key + 1;
441
442 if (crypt == NULL || crypt->ops == NULL) {
443 erq->length = 0;
444 erq->flags |= IW_ENCODE_DISABLED;
445 return 0;
446 }
447
448 if (strcmp(crypt->ops->name, "WEP") != 0) {
449 /* only WEP is supported with wireless extensions, so just
450 * report that encryption is used */
451 erq->length = 0;
452 erq->flags |= IW_ENCODE_ENABLED;
453 return 0;
454 }
455
456 len = crypt->ops->get_key(keybuf, WEP_KEY_LEN, NULL, crypt->priv);
457 erq->length = (len >= 0 ? len : 0);
458
459 erq->flags |= IW_ENCODE_ENABLED;
460
461 if (ieee->open_wep)
462 erq->flags |= IW_ENCODE_OPEN;
463 else
464 erq->flags |= IW_ENCODE_RESTRICTED;
465
466 return 0;
467}
468
469EXPORT_SYMBOL(ieee80211_wx_get_scan);
470EXPORT_SYMBOL(ieee80211_wx_set_encode);
471EXPORT_SYMBOL(ieee80211_wx_get_encode);