blob: e28648e70cf161ad33d3fc0555d3514ce293c80c [file] [log] [blame]
Jeff Garzikb4538722005-05-12 22:48:20 -04001/******************************************************************************
2
James Ketrenosebeaddc2005-09-21 11:58:43 -05003 Copyright(c) 2004-2005 Intel Corporation. All rights reserved.
Jeff Garzikb4538722005-05-12 22:48:20 -04004
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******************************************************************************/
Jeff Garzikbbeec902005-09-07 00:27:54 -040032
Jeff Garzikb4538722005-05-12 22:48:20 -040033#include <linux/kmod.h>
34#include <linux/module.h>
James Ketrenos42e349f2005-09-21 11:54:07 -050035#include <linux/jiffies.h>
Jeff Garzikb4538722005-05-12 22:48:20 -040036
37#include <net/ieee80211.h>
Jeff Garzikbbeec902005-09-07 00:27:54 -040038#include <linux/wireless.h>
39
Jeff Garzikb4538722005-05-12 22:48:20 -040040static const char *ieee80211_modes[] = {
41 "?", "a", "b", "ab", "g", "ag", "bg", "abg"
42};
43
44#define MAX_CUSTOM_LEN 64
45static inline char *ipw2100_translate_scan(struct ieee80211_device *ieee,
Jeff Garzik0edd5b42005-09-07 00:48:31 -040046 char *start, char *stop,
Jeff Garzikb4538722005-05-12 22:48:20 -040047 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 {
Jeff Garzik0edd5b42005-09-07 00:48:31 -040070 iwe.u.data.length = min(network->ssid_len, (u8) 32);
Jeff Garzikb4538722005-05-12 22:48:20 -040071 start = iwe_stream_add_point(start, stop, &iwe, network->ssid);
72 }
73
74 /* Add the protocol name */
75 iwe.cmd = SIOCGIWNAME;
Jeff Garzik0edd5b42005-09-07 00:48:31 -040076 snprintf(iwe.u.name, IFNAMSIZ, "IEEE 802.11%s",
77 ieee80211_modes[network->mode]);
Jeff Garzikb4538722005-05-12 22:48:20 -040078 start = iwe_stream_add_event(start, stop, &iwe, IW_EV_CHAR_LEN);
79
Jeff Garzik0edd5b42005-09-07 00:48:31 -040080 /* Add mode */
81 iwe.cmd = SIOCGIWMODE;
82 if (network->capability & (WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS)) {
Jeff Garzik1b5cca32005-08-15 00:32:15 -040083 if (network->capability & WLAN_CAPABILITY_ESS)
Jeff Garzikb4538722005-05-12 22:48:20 -040084 iwe.u.mode = IW_MODE_MASTER;
85 else
86 iwe.u.mode = IW_MODE_ADHOC;
87
Jeff Garzik0edd5b42005-09-07 00:48:31 -040088 start = iwe_stream_add_event(start, stop, &iwe, IW_EV_UINT_LEN);
Jeff Garzikb4538722005-05-12 22:48:20 -040089 }
90
Jeff Garzik0edd5b42005-09-07 00:48:31 -040091 /* Add frequency/channel */
Jeff Garzikb4538722005-05-12 22:48:20 -040092 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): ");
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400113 for (i = 0, j = 0; i < network->rates_len;) {
Jeff Garzikb4538722005-05-12 22:48:20 -0400114 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;
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400136 start = iwe_stream_add_event(start, stop, &iwe, IW_EV_PARAM_LEN);
Jeff Garzikb4538722005-05-12 22:48:20 -0400137
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 */
Jeff Garzikb4538722005-05-12 22:48:20 -0400144 iwe.cmd = IWEVQUAL;
James Ketrenosb1b508e2005-09-13 17:27:19 -0500145 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 iwe.u.qual.level = 0;
153 } else {
154 iwe.u.qual.level = network->stats.rssi;
155 iwe.u.qual.qual =
156 (100 *
157 (ieee->perfect_rssi - ieee->worst_rssi) *
158 (ieee->perfect_rssi - ieee->worst_rssi) -
159 (ieee->perfect_rssi - network->stats.rssi) *
160 (15 * (ieee->perfect_rssi - ieee->worst_rssi) +
161 62 * (ieee->perfect_rssi - network->stats.rssi))) /
162 ((ieee->perfect_rssi - ieee->worst_rssi) *
163 (ieee->perfect_rssi - ieee->worst_rssi));
164 if (iwe.u.qual.qual > 100)
165 iwe.u.qual.qual = 100;
166 else if (iwe.u.qual.qual < 1)
167 iwe.u.qual.qual = 0;
168 }
169
170 if (!(network->stats.mask & IEEE80211_STATMASK_NOISE)) {
Jeff Garzikb4538722005-05-12 22:48:20 -0400171 iwe.u.qual.updated |= IW_QUAL_NOISE_INVALID;
James Ketrenosb1b508e2005-09-13 17:27:19 -0500172 iwe.u.qual.noise = 0;
173 } else {
174 iwe.u.qual.noise = network->stats.noise;
175 }
Jeff Garzikb4538722005-05-12 22:48:20 -0400176
177 start = iwe_stream_add_event(start, stop, &iwe, IW_EV_QUAL_LEN);
178
179 iwe.cmd = IWEVCUSTOM;
180 p = custom;
181
182 iwe.u.data.length = p - custom;
183 if (iwe.u.data.length)
184 start = iwe_stream_add_point(start, stop, &iwe, custom);
185
James Ketrenos20d64712005-09-21 11:53:43 -0500186 if (network->wpa_ie_len) {
Jeff Garzikb4538722005-05-12 22:48:20 -0400187 char buf[MAX_WPA_IE_LEN * 2 + 30];
188
189 u8 *p = buf;
190 p += sprintf(p, "wpa_ie=");
191 for (i = 0; i < network->wpa_ie_len; i++) {
192 p += sprintf(p, "%02x", network->wpa_ie[i]);
193 }
194
195 memset(&iwe, 0, sizeof(iwe));
196 iwe.cmd = IWEVCUSTOM;
197 iwe.u.data.length = strlen(buf);
198 start = iwe_stream_add_point(start, stop, &iwe, buf);
199 }
200
James Ketrenos20d64712005-09-21 11:53:43 -0500201 if (network->rsn_ie_len) {
Jeff Garzikb4538722005-05-12 22:48:20 -0400202 char buf[MAX_WPA_IE_LEN * 2 + 30];
203
204 u8 *p = buf;
205 p += sprintf(p, "rsn_ie=");
206 for (i = 0; i < network->rsn_ie_len; i++) {
207 p += sprintf(p, "%02x", network->rsn_ie[i]);
208 }
209
210 memset(&iwe, 0, sizeof(iwe));
211 iwe.cmd = IWEVCUSTOM;
212 iwe.u.data.length = strlen(buf);
213 start = iwe_stream_add_point(start, stop, &iwe, buf);
214 }
215
216 /* Add EXTRA: Age to display seconds since last beacon/probe response
217 * for given network. */
218 iwe.cmd = IWEVCUSTOM;
219 p = custom;
220 p += snprintf(p, MAX_CUSTOM_LEN - (p - custom),
James Ketrenos42e349f2005-09-21 11:54:07 -0500221 " Last beacon: %dms ago",
222 jiffies_to_msecs(jiffies - network->last_scanned));
Jeff Garzikb4538722005-05-12 22:48:20 -0400223 iwe.u.data.length = p - custom;
224 if (iwe.u.data.length)
225 start = iwe_stream_add_point(start, stop, &iwe, custom);
226
Jeff Garzikb4538722005-05-12 22:48:20 -0400227 return start;
228}
229
230int ieee80211_wx_get_scan(struct ieee80211_device *ieee,
231 struct iw_request_info *info,
232 union iwreq_data *wrqu, char *extra)
233{
234 struct ieee80211_network *network;
235 unsigned long flags;
236
237 char *ev = extra;
238 char *stop = ev + IW_SCAN_MAX_DATA;
239 int i = 0;
240
241 IEEE80211_DEBUG_WX("Getting scan\n");
242
243 spin_lock_irqsave(&ieee->lock, flags);
244
245 list_for_each_entry(network, &ieee->network_list, list) {
246 i++;
247 if (ieee->scan_age == 0 ||
248 time_after(network->last_scanned + ieee->scan_age, jiffies))
249 ev = ipw2100_translate_scan(ieee, ev, stop, network);
250 else
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400251 IEEE80211_DEBUG_SCAN("Not showing network '%s ("
James Ketrenos42e349f2005-09-21 11:54:07 -0500252 MAC_FMT ")' due to age (%dms).\n",
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400253 escape_essid(network->ssid,
254 network->ssid_len),
255 MAC_ARG(network->bssid),
James Ketrenos42e349f2005-09-21 11:54:07 -0500256 jiffies_to_msecs(jiffies -
257 network->
258 last_scanned));
Jeff Garzikb4538722005-05-12 22:48:20 -0400259 }
260
261 spin_unlock_irqrestore(&ieee->lock, flags);
262
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400263 wrqu->data.length = ev - extra;
Jeff Garzikb4538722005-05-12 22:48:20 -0400264 wrqu->data.flags = 0;
265
266 IEEE80211_DEBUG_WX("exit: %d networks returned.\n", i);
267
268 return 0;
269}
270
271int ieee80211_wx_set_encode(struct ieee80211_device *ieee,
272 struct iw_request_info *info,
273 union iwreq_data *wrqu, char *keybuf)
274{
275 struct iw_point *erq = &(wrqu->encoding);
276 struct net_device *dev = ieee->dev;
277 struct ieee80211_security sec = {
278 .flags = 0
279 };
280 int i, key, key_provided, len;
281 struct ieee80211_crypt_data **crypt;
James Ketrenosf1bf6632005-09-21 11:53:54 -0500282 int host_crypto = ieee->host_encrypt || ieee->host_decrypt;
Jeff Garzikb4538722005-05-12 22:48:20 -0400283
284 IEEE80211_DEBUG_WX("SET_ENCODE\n");
285
286 key = erq->flags & IW_ENCODE_INDEX;
287 if (key) {
288 if (key > WEP_KEYS)
289 return -EINVAL;
290 key--;
291 key_provided = 1;
292 } else {
293 key_provided = 0;
294 key = ieee->tx_keyidx;
295 }
296
297 IEEE80211_DEBUG_WX("Key: %d [%s]\n", key, key_provided ?
298 "provided" : "default");
299
300 crypt = &ieee->crypt[key];
301
302 if (erq->flags & IW_ENCODE_DISABLED) {
303 if (key_provided && *crypt) {
304 IEEE80211_DEBUG_WX("Disabling encryption on key %d.\n",
305 key);
306 ieee80211_crypt_delayed_deinit(ieee, crypt);
307 } else
308 IEEE80211_DEBUG_WX("Disabling encryption.\n");
309
310 /* Check all the keys to see if any are still configured,
311 * and if no key index was provided, de-init them all */
312 for (i = 0; i < WEP_KEYS; i++) {
313 if (ieee->crypt[i] != NULL) {
314 if (key_provided)
315 break;
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400316 ieee80211_crypt_delayed_deinit(ieee,
317 &ieee->crypt[i]);
Jeff Garzikb4538722005-05-12 22:48:20 -0400318 }
319 }
320
321 if (i == WEP_KEYS) {
322 sec.enabled = 0;
James Ketrenosf1bf6632005-09-21 11:53:54 -0500323 sec.encrypt = 0;
Jeff Garzikb4538722005-05-12 22:48:20 -0400324 sec.level = SEC_LEVEL_0;
James Ketrenos259bf1f2005-09-21 11:54:22 -0500325 sec.flags |= SEC_ENABLED | SEC_LEVEL | SEC_ENCRYPT;
Jeff Garzikb4538722005-05-12 22:48:20 -0400326 }
327
328 goto done;
329 }
330
Jeff Garzikb4538722005-05-12 22:48:20 -0400331 sec.enabled = 1;
James Ketrenosf1bf6632005-09-21 11:53:54 -0500332 sec.encrypt = 1;
James Ketrenos259bf1f2005-09-21 11:54:22 -0500333 sec.flags |= SEC_ENABLED | SEC_ENCRYPT;
Jeff Garzikb4538722005-05-12 22:48:20 -0400334
335 if (*crypt != NULL && (*crypt)->ops != NULL &&
336 strcmp((*crypt)->ops->name, "WEP") != 0) {
337 /* changing to use WEP; deinit previously used algorithm
338 * on this key */
339 ieee80211_crypt_delayed_deinit(ieee, crypt);
340 }
341
James Ketrenosf1bf6632005-09-21 11:53:54 -0500342 if (*crypt == NULL && host_crypto) {
Jeff Garzikb4538722005-05-12 22:48:20 -0400343 struct ieee80211_crypt_data *new_crypt;
344
345 /* take WEP into use */
346 new_crypt = kmalloc(sizeof(struct ieee80211_crypt_data),
347 GFP_KERNEL);
348 if (new_crypt == NULL)
349 return -ENOMEM;
350 memset(new_crypt, 0, sizeof(struct ieee80211_crypt_data));
351 new_crypt->ops = ieee80211_get_crypto_ops("WEP");
352 if (!new_crypt->ops) {
353 request_module("ieee80211_crypt_wep");
354 new_crypt->ops = ieee80211_get_crypto_ops("WEP");
355 }
356
357 if (new_crypt->ops && try_module_get(new_crypt->ops->owner))
James Ketrenos20d64712005-09-21 11:53:43 -0500358 new_crypt->priv = new_crypt->ops->init(ieee, key);
Jeff Garzikb4538722005-05-12 22:48:20 -0400359
360 if (!new_crypt->ops || !new_crypt->priv) {
361 kfree(new_crypt);
362 new_crypt = NULL;
363
364 printk(KERN_WARNING "%s: could not initialize WEP: "
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400365 "load module ieee80211_crypt_wep\n", dev->name);
Jeff Garzikb4538722005-05-12 22:48:20 -0400366 return -EOPNOTSUPP;
367 }
368 *crypt = new_crypt;
369 }
370
371 /* If a new key was provided, set it up */
372 if (erq->length > 0) {
373 len = erq->length <= 5 ? 5 : 13;
374 memcpy(sec.keys[key], keybuf, erq->length);
375 if (len > erq->length)
376 memset(sec.keys[key] + erq->length, 0,
377 len - erq->length);
378 IEEE80211_DEBUG_WX("Setting key %d to '%s' (%d:%d bytes)\n",
379 key, escape_essid(sec.keys[key], len),
380 erq->length, len);
381 sec.key_sizes[key] = len;
James Ketrenosf1bf6632005-09-21 11:53:54 -0500382 if (*crypt)
383 (*crypt)->ops->set_key(sec.keys[key], len, NULL,
384 (*crypt)->priv);
Jeff Garzikb4538722005-05-12 22:48:20 -0400385 sec.flags |= (1 << key);
386 /* This ensures a key will be activated if no key is
387 * explicitely set */
388 if (key == sec.active_key)
389 sec.flags |= SEC_ACTIVE_KEY;
Jeff Garzikb4538722005-05-12 22:48:20 -0400390
James Ketrenosf1bf6632005-09-21 11:53:54 -0500391 } else {
392 if (host_crypto) {
393 len = (*crypt)->ops->get_key(sec.keys[key], WEP_KEY_LEN,
394 NULL, (*crypt)->priv);
395 if (len == 0) {
396 /* Set a default key of all 0 */
397 IEEE80211_DEBUG_WX("Setting key %d to all "
398 "zero.\n", key);
399 memset(sec.keys[key], 0, 13);
400 (*crypt)->ops->set_key(sec.keys[key], 13, NULL,
401 (*crypt)->priv);
402 sec.key_sizes[key] = 13;
403 sec.flags |= (1 << key);
404 }
405 }
Jeff Garzikb4538722005-05-12 22:48:20 -0400406 /* No key data - just set the default TX key index */
407 if (key_provided) {
James Ketrenosf1bf6632005-09-21 11:53:54 -0500408 IEEE80211_DEBUG_WX("Setting key %d to default Tx "
409 "key.\n", key);
Jeff Garzikb4538722005-05-12 22:48:20 -0400410 ieee->tx_keyidx = key;
411 sec.active_key = key;
412 sec.flags |= SEC_ACTIVE_KEY;
413 }
414 }
James Ketrenos7dc888f2005-09-21 11:58:38 -0500415 if (erq->flags & (IW_ENCODE_OPEN | IW_ENCODE_RESTRICTED)) {
416 ieee->open_wep = !(erq->flags & IW_ENCODE_RESTRICTED);
417 sec.auth_mode = ieee->open_wep ? WLAN_AUTH_OPEN :
418 WLAN_AUTH_SHARED_KEY;
419 sec.flags |= SEC_AUTH_MODE;
420 IEEE80211_DEBUG_WX("Auth: %s\n",
421 sec.auth_mode == WLAN_AUTH_OPEN ?
422 "OPEN" : "SHARED KEY");
423 }
Jeff Garzikb4538722005-05-12 22:48:20 -0400424
425 /* For now we just support WEP, so only set that security level...
426 * TODO: When WPA is added this is one place that needs to change */
427 sec.flags |= SEC_LEVEL;
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400428 sec.level = SEC_LEVEL_1; /* 40 and 104 bit WEP */
James Ketrenose0d369d2005-09-21 11:54:30 -0500429 sec.encode_alg[key] = SEC_ALG_WEP;
Jeff Garzikb4538722005-05-12 22:48:20 -0400430
James Ketrenos259bf1f2005-09-21 11:54:22 -0500431 done:
Jeff Garzikb4538722005-05-12 22:48:20 -0400432 if (ieee->set_security)
433 ieee->set_security(dev, &sec);
434
435 /* Do not reset port if card is in Managed mode since resetting will
436 * generate new IEEE 802.11 authentication which may end up in looping
437 * with IEEE 802.1X. If your hardware requires a reset after WEP
438 * configuration (for example... Prism2), implement the reset_port in
439 * the callbacks structures used to initialize the 802.11 stack. */
440 if (ieee->reset_on_keychange &&
441 ieee->iw_mode != IW_MODE_INFRA &&
442 ieee->reset_port && ieee->reset_port(dev)) {
443 printk(KERN_DEBUG "%s: reset_port failed\n", dev->name);
444 return -EINVAL;
445 }
446 return 0;
447}
448
449int ieee80211_wx_get_encode(struct ieee80211_device *ieee,
450 struct iw_request_info *info,
451 union iwreq_data *wrqu, char *keybuf)
452{
453 struct iw_point *erq = &(wrqu->encoding);
454 int len, key;
455 struct ieee80211_crypt_data *crypt;
James Ketrenosf1bf6632005-09-21 11:53:54 -0500456 struct ieee80211_security *sec = &ieee->sec;
Jeff Garzikb4538722005-05-12 22:48:20 -0400457
458 IEEE80211_DEBUG_WX("GET_ENCODE\n");
459
460 key = erq->flags & IW_ENCODE_INDEX;
461 if (key) {
462 if (key > WEP_KEYS)
463 return -EINVAL;
464 key--;
465 } else
466 key = ieee->tx_keyidx;
467
468 crypt = ieee->crypt[key];
469 erq->flags = key + 1;
470
James Ketrenosf1bf6632005-09-21 11:53:54 -0500471 if (!sec->enabled) {
Jeff Garzikb4538722005-05-12 22:48:20 -0400472 erq->length = 0;
473 erq->flags |= IW_ENCODE_DISABLED;
474 return 0;
475 }
476
James Ketrenosf1bf6632005-09-21 11:53:54 -0500477 len = sec->key_sizes[key];
478 memcpy(keybuf, sec->keys[key], len);
Jeff Garzikb4538722005-05-12 22:48:20 -0400479
James Ketrenosf1bf6632005-09-21 11:53:54 -0500480 erq->length = (len >= 0 ? len : 0);
Jeff Garzikb4538722005-05-12 22:48:20 -0400481 erq->flags |= IW_ENCODE_ENABLED;
482
483 if (ieee->open_wep)
484 erq->flags |= IW_ENCODE_OPEN;
485 else
486 erq->flags |= IW_ENCODE_RESTRICTED;
487
488 return 0;
489}
490
James Ketrenose0d369d2005-09-21 11:54:30 -0500491#if WIRELESS_EXT > 17
492int ieee80211_wx_set_encodeext(struct ieee80211_device *ieee,
493 struct iw_request_info *info,
494 union iwreq_data *wrqu, char *extra)
495{
496 struct net_device *dev = ieee->dev;
497 struct iw_point *encoding = &wrqu->encoding;
498 struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
499 int i, idx, ret = 0;
James Ketrenosccd0fda2005-09-21 11:58:32 -0500500 int group_key = 0;
James Ketrenose0d369d2005-09-21 11:54:30 -0500501 const char *alg, *module;
502 struct ieee80211_crypto_ops *ops;
503 struct ieee80211_crypt_data **crypt;
504
505 struct ieee80211_security sec = {
506 .flags = 0,
507 };
508
509 idx = encoding->flags & IW_ENCODE_INDEX;
510 if (idx) {
511 if (idx < 1 || idx > WEP_KEYS)
512 return -EINVAL;
513 idx--;
514 } else
515 idx = ieee->tx_keyidx;
516
James Ketrenosccd0fda2005-09-21 11:58:32 -0500517 if (ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) {
James Ketrenose0d369d2005-09-21 11:54:30 -0500518 crypt = &ieee->crypt[idx];
James Ketrenosccd0fda2005-09-21 11:58:32 -0500519 group_key = 1;
520 } else {
James Ketrenose0d369d2005-09-21 11:54:30 -0500521 if (idx != 0)
522 return -EINVAL;
523 if (ieee->iw_mode == IW_MODE_INFRA)
524 crypt = &ieee->crypt[idx];
525 else
526 return -EINVAL;
527 }
528
529 sec.flags |= SEC_ENABLED | SEC_ENCRYPT;
530 if ((encoding->flags & IW_ENCODE_DISABLED) ||
531 ext->alg == IW_ENCODE_ALG_NONE) {
532 if (*crypt)
533 ieee80211_crypt_delayed_deinit(ieee, crypt);
534
535 for (i = 0; i < WEP_KEYS; i++)
536 if (ieee->crypt[i] != NULL)
537 break;
538
539 if (i == WEP_KEYS) {
540 sec.enabled = 0;
541 sec.encrypt = 0;
542 sec.level = SEC_LEVEL_0;
543 sec.flags |= SEC_LEVEL;
544 }
545 goto done;
546 }
547
548 sec.enabled = 1;
549 sec.encrypt = 1;
550
James Ketrenosccd0fda2005-09-21 11:58:32 -0500551 if (group_key ? !ieee->host_mc_decrypt :
552 !(ieee->host_encrypt || ieee->host_decrypt ||
553 ieee->host_encrypt_msdu))
James Ketrenose0d369d2005-09-21 11:54:30 -0500554 goto skip_host_crypt;
555
556 switch (ext->alg) {
557 case IW_ENCODE_ALG_WEP:
558 alg = "WEP";
559 module = "ieee80211_crypt_wep";
560 break;
561 case IW_ENCODE_ALG_TKIP:
562 alg = "TKIP";
563 module = "ieee80211_crypt_tkip";
564 break;
565 case IW_ENCODE_ALG_CCMP:
566 alg = "CCMP";
567 module = "ieee80211_crypt_ccmp";
568 break;
569 default:
570 IEEE80211_DEBUG_WX("%s: unknown crypto alg %d\n",
571 dev->name, ext->alg);
572 ret = -EINVAL;
573 goto done;
574 }
575
576 ops = ieee80211_get_crypto_ops(alg);
577 if (ops == NULL) {
578 request_module(module);
579 ops = ieee80211_get_crypto_ops(alg);
580 }
581 if (ops == NULL) {
582 IEEE80211_DEBUG_WX("%s: unknown crypto alg %d\n",
583 dev->name, ext->alg);
584 ret = -EINVAL;
585 goto done;
586 }
587
588 if (*crypt == NULL || (*crypt)->ops != ops) {
589 struct ieee80211_crypt_data *new_crypt;
590
591 ieee80211_crypt_delayed_deinit(ieee, crypt);
592
593 new_crypt = (struct ieee80211_crypt_data *)
594 kmalloc(sizeof(*new_crypt), GFP_KERNEL);
595 if (new_crypt == NULL) {
596 ret = -ENOMEM;
597 goto done;
598 }
599 memset(new_crypt, 0, sizeof(struct ieee80211_crypt_data));
600 new_crypt->ops = ops;
601 if (new_crypt->ops && try_module_get(new_crypt->ops->owner))
602 new_crypt->priv = new_crypt->ops->init(ieee, idx);
603 if (new_crypt->priv == NULL) {
604 kfree(new_crypt);
605 ret = -EINVAL;
606 goto done;
607 }
608 *crypt = new_crypt;
609 }
610
611 if (ext->key_len > 0 && (*crypt)->ops->set_key &&
612 (*crypt)->ops->set_key(ext->key, ext->key_len, ext->rx_seq,
613 (*crypt)->priv) < 0) {
614 IEEE80211_DEBUG_WX("%s: key setting failed\n", dev->name);
615 ret = -EINVAL;
616 goto done;
617 }
618
619 skip_host_crypt:
620 if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) {
621 ieee->tx_keyidx = idx;
622 sec.active_key = idx;
623 sec.flags |= SEC_ACTIVE_KEY;
624 }
625
626 if (ext->alg != IW_ENCODE_ALG_NONE) {
627 memcpy(sec.keys[idx], ext->key, ext->key_len);
628 sec.key_sizes[idx] = ext->key_len;
629 sec.flags |= (1 << idx);
630 if (ext->alg == IW_ENCODE_ALG_WEP) {
631 sec.encode_alg[idx] = SEC_ALG_WEP;
632 sec.flags |= SEC_LEVEL;
633 sec.level = SEC_LEVEL_1;
634 } else if (ext->alg == IW_ENCODE_ALG_TKIP) {
635 sec.encode_alg[idx] = SEC_ALG_TKIP;
636 sec.flags |= SEC_LEVEL;
637 sec.level = SEC_LEVEL_2;
638 } else if (ext->alg == IW_ENCODE_ALG_CCMP) {
639 sec.encode_alg[idx] = SEC_ALG_CCMP;
640 sec.flags |= SEC_LEVEL;
641 sec.level = SEC_LEVEL_3;
642 }
James Ketrenosccd0fda2005-09-21 11:58:32 -0500643 /* Don't set sec level for group keys. */
644 if (group_key)
645 sec.flags &= ~SEC_LEVEL;
James Ketrenose0d369d2005-09-21 11:54:30 -0500646 }
647 done:
648 if (ieee->set_security)
649 ieee->set_security(ieee->dev, &sec);
650
651 /*
652 * Do not reset port if card is in Managed mode since resetting will
653 * generate new IEEE 802.11 authentication which may end up in looping
654 * with IEEE 802.1X. If your hardware requires a reset after WEP
655 * configuration (for example... Prism2), implement the reset_port in
656 * the callbacks structures used to initialize the 802.11 stack.
657 */
658 if (ieee->reset_on_keychange &&
659 ieee->iw_mode != IW_MODE_INFRA &&
660 ieee->reset_port && ieee->reset_port(dev)) {
661 IEEE80211_DEBUG_WX("%s: reset_port failed\n", dev->name);
662 return -EINVAL;
663 }
664
665 return ret;
666}
667
668int ieee80211_wx_get_encodeext(struct ieee80211_device *ieee,
669 struct iw_request_info *info,
670 union iwreq_data *wrqu, char *extra)
671{
672 struct iw_point *encoding = &wrqu->encoding;
673 struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
674 struct ieee80211_security *sec = &ieee->sec;
675 int idx, max_key_len;
676
677 max_key_len = encoding->length - sizeof(*ext);
678 if (max_key_len < 0)
679 return -EINVAL;
680
681 idx = encoding->flags & IW_ENCODE_INDEX;
682 if (idx) {
683 if (idx < 1 || idx > WEP_KEYS)
684 return -EINVAL;
685 idx--;
686 } else
687 idx = ieee->tx_keyidx;
688
689 if (!ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY)
690 if (idx != 0 || ieee->iw_mode != IW_MODE_INFRA)
691 return -EINVAL;
692
693 encoding->flags = idx + 1;
694 memset(ext, 0, sizeof(*ext));
695
696 if (!sec->enabled) {
697 ext->alg = IW_ENCODE_ALG_NONE;
698 ext->key_len = 0;
699 encoding->flags |= IW_ENCODE_DISABLED;
700 } else {
701 if (sec->encode_alg[idx] == SEC_ALG_WEP)
702 ext->alg = IW_ENCODE_ALG_WEP;
703 else if (sec->encode_alg[idx] == SEC_ALG_TKIP)
704 ext->alg = IW_ENCODE_ALG_TKIP;
705 else if (sec->encode_alg[idx] == SEC_ALG_CCMP)
706 ext->alg = IW_ENCODE_ALG_CCMP;
707 else
708 return -EINVAL;
709
710 ext->key_len = sec->key_sizes[idx];
711 memcpy(ext->key, sec->keys[idx], ext->key_len);
712 encoding->flags |= IW_ENCODE_ENABLED;
713 if (ext->key_len &&
714 (ext->alg == IW_ENCODE_ALG_TKIP ||
715 ext->alg == IW_ENCODE_ALG_CCMP))
716 ext->ext_flags |= IW_ENCODE_EXT_TX_SEQ_VALID;
717
718 }
719
720 return 0;
721}
722
723EXPORT_SYMBOL(ieee80211_wx_set_encodeext);
724EXPORT_SYMBOL(ieee80211_wx_get_encodeext);
725#endif
726
Jeff Garzikb4538722005-05-12 22:48:20 -0400727EXPORT_SYMBOL(ieee80211_wx_get_scan);
728EXPORT_SYMBOL(ieee80211_wx_set_encode);
729EXPORT_SYMBOL(ieee80211_wx_get_encode);