blob: 1ce7af9bec35a167b8e17512ec7ab716a2013549 [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;
Jiri Benc757d18f2005-10-10 19:16:53 +0200155 if (ieee->perfect_rssi == ieee->worst_rssi)
156 iwe.u.qual.qual = 100;
157 else
158 iwe.u.qual.qual =
159 (100 *
160 (ieee->perfect_rssi - ieee->worst_rssi) *
161 (ieee->perfect_rssi - ieee->worst_rssi) -
162 (ieee->perfect_rssi - network->stats.rssi) *
163 (15 * (ieee->perfect_rssi - ieee->worst_rssi) +
164 62 * (ieee->perfect_rssi - network->stats.rssi))) /
165 ((ieee->perfect_rssi - ieee->worst_rssi) *
166 (ieee->perfect_rssi - ieee->worst_rssi));
James Ketrenosb1b508e2005-09-13 17:27:19 -0500167 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)) {
Jeff Garzikb4538722005-05-12 22:48:20 -0400174 iwe.u.qual.updated |= IW_QUAL_NOISE_INVALID;
James Ketrenosb1b508e2005-09-13 17:27:19 -0500175 iwe.u.qual.noise = 0;
176 } else {
177 iwe.u.qual.noise = network->stats.noise;
178 }
Jeff Garzikb4538722005-05-12 22:48:20 -0400179
180 start = iwe_stream_add_event(start, stop, &iwe, IW_EV_QUAL_LEN);
181
182 iwe.cmd = IWEVCUSTOM;
183 p = custom;
184
185 iwe.u.data.length = p - custom;
186 if (iwe.u.data.length)
187 start = iwe_stream_add_point(start, stop, &iwe, custom);
188
James Ketrenos20d64712005-09-21 11:53:43 -0500189 if (network->wpa_ie_len) {
Jeff Garzikb4538722005-05-12 22:48:20 -0400190 char buf[MAX_WPA_IE_LEN * 2 + 30];
191
192 u8 *p = buf;
193 p += sprintf(p, "wpa_ie=");
194 for (i = 0; i < network->wpa_ie_len; i++) {
195 p += sprintf(p, "%02x", network->wpa_ie[i]);
196 }
197
198 memset(&iwe, 0, sizeof(iwe));
199 iwe.cmd = IWEVCUSTOM;
200 iwe.u.data.length = strlen(buf);
201 start = iwe_stream_add_point(start, stop, &iwe, buf);
202 }
203
James Ketrenos20d64712005-09-21 11:53:43 -0500204 if (network->rsn_ie_len) {
Jeff Garzikb4538722005-05-12 22:48:20 -0400205 char buf[MAX_WPA_IE_LEN * 2 + 30];
206
207 u8 *p = buf;
208 p += sprintf(p, "rsn_ie=");
209 for (i = 0; i < network->rsn_ie_len; i++) {
210 p += sprintf(p, "%02x", network->rsn_ie[i]);
211 }
212
213 memset(&iwe, 0, sizeof(iwe));
214 iwe.cmd = IWEVCUSTOM;
215 iwe.u.data.length = strlen(buf);
216 start = iwe_stream_add_point(start, stop, &iwe, buf);
217 }
218
219 /* Add EXTRA: Age to display seconds since last beacon/probe response
220 * for given network. */
221 iwe.cmd = IWEVCUSTOM;
222 p = custom;
223 p += snprintf(p, MAX_CUSTOM_LEN - (p - custom),
James Ketrenos42e349f2005-09-21 11:54:07 -0500224 " Last beacon: %dms ago",
225 jiffies_to_msecs(jiffies - network->last_scanned));
Jeff Garzikb4538722005-05-12 22:48:20 -0400226 iwe.u.data.length = p - custom;
227 if (iwe.u.data.length)
228 start = iwe_stream_add_point(start, stop, &iwe, custom);
229
Jeff Garzikb4538722005-05-12 22:48:20 -0400230 return start;
231}
232
233int ieee80211_wx_get_scan(struct ieee80211_device *ieee,
234 struct iw_request_info *info,
235 union iwreq_data *wrqu, char *extra)
236{
237 struct ieee80211_network *network;
238 unsigned long flags;
239
240 char *ev = extra;
241 char *stop = ev + IW_SCAN_MAX_DATA;
242 int i = 0;
243
244 IEEE80211_DEBUG_WX("Getting scan\n");
245
246 spin_lock_irqsave(&ieee->lock, flags);
247
248 list_for_each_entry(network, &ieee->network_list, list) {
249 i++;
250 if (ieee->scan_age == 0 ||
251 time_after(network->last_scanned + ieee->scan_age, jiffies))
252 ev = ipw2100_translate_scan(ieee, ev, stop, network);
253 else
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400254 IEEE80211_DEBUG_SCAN("Not showing network '%s ("
James Ketrenos42e349f2005-09-21 11:54:07 -0500255 MAC_FMT ")' due to age (%dms).\n",
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400256 escape_essid(network->ssid,
257 network->ssid_len),
258 MAC_ARG(network->bssid),
James Ketrenos42e349f2005-09-21 11:54:07 -0500259 jiffies_to_msecs(jiffies -
260 network->
261 last_scanned));
Jeff Garzikb4538722005-05-12 22:48:20 -0400262 }
263
264 spin_unlock_irqrestore(&ieee->lock, flags);
265
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400266 wrqu->data.length = ev - extra;
Jeff Garzikb4538722005-05-12 22:48:20 -0400267 wrqu->data.flags = 0;
268
269 IEEE80211_DEBUG_WX("exit: %d networks returned.\n", i);
270
271 return 0;
272}
273
274int ieee80211_wx_set_encode(struct ieee80211_device *ieee,
275 struct iw_request_info *info,
276 union iwreq_data *wrqu, char *keybuf)
277{
278 struct iw_point *erq = &(wrqu->encoding);
279 struct net_device *dev = ieee->dev;
280 struct ieee80211_security sec = {
281 .flags = 0
282 };
283 int i, key, key_provided, len;
284 struct ieee80211_crypt_data **crypt;
James Ketrenosf1bf6632005-09-21 11:53:54 -0500285 int host_crypto = ieee->host_encrypt || ieee->host_decrypt;
Jeff Garzikb4538722005-05-12 22:48:20 -0400286
287 IEEE80211_DEBUG_WX("SET_ENCODE\n");
288
289 key = erq->flags & IW_ENCODE_INDEX;
290 if (key) {
291 if (key > WEP_KEYS)
292 return -EINVAL;
293 key--;
294 key_provided = 1;
295 } else {
296 key_provided = 0;
297 key = ieee->tx_keyidx;
298 }
299
300 IEEE80211_DEBUG_WX("Key: %d [%s]\n", key, key_provided ?
301 "provided" : "default");
302
303 crypt = &ieee->crypt[key];
304
305 if (erq->flags & IW_ENCODE_DISABLED) {
306 if (key_provided && *crypt) {
307 IEEE80211_DEBUG_WX("Disabling encryption on key %d.\n",
308 key);
309 ieee80211_crypt_delayed_deinit(ieee, crypt);
310 } else
311 IEEE80211_DEBUG_WX("Disabling encryption.\n");
312
313 /* Check all the keys to see if any are still configured,
314 * and if no key index was provided, de-init them all */
315 for (i = 0; i < WEP_KEYS; i++) {
316 if (ieee->crypt[i] != NULL) {
317 if (key_provided)
318 break;
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400319 ieee80211_crypt_delayed_deinit(ieee,
320 &ieee->crypt[i]);
Jeff Garzikb4538722005-05-12 22:48:20 -0400321 }
322 }
323
324 if (i == WEP_KEYS) {
325 sec.enabled = 0;
James Ketrenosf1bf6632005-09-21 11:53:54 -0500326 sec.encrypt = 0;
Jeff Garzikb4538722005-05-12 22:48:20 -0400327 sec.level = SEC_LEVEL_0;
James Ketrenos259bf1f2005-09-21 11:54:22 -0500328 sec.flags |= SEC_ENABLED | SEC_LEVEL | SEC_ENCRYPT;
Jeff Garzikb4538722005-05-12 22:48:20 -0400329 }
330
331 goto done;
332 }
333
Jeff Garzikb4538722005-05-12 22:48:20 -0400334 sec.enabled = 1;
James Ketrenosf1bf6632005-09-21 11:53:54 -0500335 sec.encrypt = 1;
James Ketrenos259bf1f2005-09-21 11:54:22 -0500336 sec.flags |= SEC_ENABLED | SEC_ENCRYPT;
Jeff Garzikb4538722005-05-12 22:48:20 -0400337
338 if (*crypt != NULL && (*crypt)->ops != NULL &&
339 strcmp((*crypt)->ops->name, "WEP") != 0) {
340 /* changing to use WEP; deinit previously used algorithm
341 * on this key */
342 ieee80211_crypt_delayed_deinit(ieee, crypt);
343 }
344
James Ketrenosf1bf6632005-09-21 11:53:54 -0500345 if (*crypt == NULL && host_crypto) {
Jeff Garzikb4538722005-05-12 22:48:20 -0400346 struct ieee80211_crypt_data *new_crypt;
347
348 /* take WEP into use */
349 new_crypt = kmalloc(sizeof(struct ieee80211_crypt_data),
350 GFP_KERNEL);
351 if (new_crypt == NULL)
352 return -ENOMEM;
353 memset(new_crypt, 0, sizeof(struct ieee80211_crypt_data));
354 new_crypt->ops = ieee80211_get_crypto_ops("WEP");
355 if (!new_crypt->ops) {
356 request_module("ieee80211_crypt_wep");
357 new_crypt->ops = ieee80211_get_crypto_ops("WEP");
358 }
359
360 if (new_crypt->ops && try_module_get(new_crypt->ops->owner))
James Ketrenos6eb6edf2005-09-22 10:34:15 +0000361 new_crypt->priv = new_crypt->ops->init(key);
Jeff Garzikb4538722005-05-12 22:48:20 -0400362
363 if (!new_crypt->ops || !new_crypt->priv) {
364 kfree(new_crypt);
365 new_crypt = NULL;
366
367 printk(KERN_WARNING "%s: could not initialize WEP: "
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400368 "load module ieee80211_crypt_wep\n", dev->name);
Jeff Garzikb4538722005-05-12 22:48:20 -0400369 return -EOPNOTSUPP;
370 }
371 *crypt = new_crypt;
372 }
373
374 /* If a new key was provided, set it up */
375 if (erq->length > 0) {
376 len = erq->length <= 5 ? 5 : 13;
377 memcpy(sec.keys[key], keybuf, erq->length);
378 if (len > erq->length)
379 memset(sec.keys[key] + erq->length, 0,
380 len - erq->length);
381 IEEE80211_DEBUG_WX("Setting key %d to '%s' (%d:%d bytes)\n",
382 key, escape_essid(sec.keys[key], len),
383 erq->length, len);
384 sec.key_sizes[key] = len;
James Ketrenosf1bf6632005-09-21 11:53:54 -0500385 if (*crypt)
386 (*crypt)->ops->set_key(sec.keys[key], len, NULL,
387 (*crypt)->priv);
Jeff Garzikb4538722005-05-12 22:48:20 -0400388 sec.flags |= (1 << key);
389 /* This ensures a key will be activated if no key is
390 * explicitely set */
391 if (key == sec.active_key)
392 sec.flags |= SEC_ACTIVE_KEY;
Jeff Garzikb4538722005-05-12 22:48:20 -0400393
James Ketrenosf1bf6632005-09-21 11:53:54 -0500394 } else {
395 if (host_crypto) {
396 len = (*crypt)->ops->get_key(sec.keys[key], WEP_KEY_LEN,
397 NULL, (*crypt)->priv);
398 if (len == 0) {
399 /* Set a default key of all 0 */
400 IEEE80211_DEBUG_WX("Setting key %d to all "
401 "zero.\n", key);
402 memset(sec.keys[key], 0, 13);
403 (*crypt)->ops->set_key(sec.keys[key], 13, NULL,
404 (*crypt)->priv);
405 sec.key_sizes[key] = 13;
406 sec.flags |= (1 << key);
407 }
408 }
Jeff Garzikb4538722005-05-12 22:48:20 -0400409 /* No key data - just set the default TX key index */
410 if (key_provided) {
James Ketrenosf1bf6632005-09-21 11:53:54 -0500411 IEEE80211_DEBUG_WX("Setting key %d to default Tx "
412 "key.\n", key);
Jeff Garzikb4538722005-05-12 22:48:20 -0400413 ieee->tx_keyidx = key;
414 sec.active_key = key;
415 sec.flags |= SEC_ACTIVE_KEY;
416 }
417 }
James Ketrenos7dc888f2005-09-21 11:58:38 -0500418 if (erq->flags & (IW_ENCODE_OPEN | IW_ENCODE_RESTRICTED)) {
419 ieee->open_wep = !(erq->flags & IW_ENCODE_RESTRICTED);
420 sec.auth_mode = ieee->open_wep ? WLAN_AUTH_OPEN :
421 WLAN_AUTH_SHARED_KEY;
422 sec.flags |= SEC_AUTH_MODE;
423 IEEE80211_DEBUG_WX("Auth: %s\n",
424 sec.auth_mode == WLAN_AUTH_OPEN ?
425 "OPEN" : "SHARED KEY");
426 }
Jeff Garzikb4538722005-05-12 22:48:20 -0400427
428 /* For now we just support WEP, so only set that security level...
429 * TODO: When WPA is added this is one place that needs to change */
430 sec.flags |= SEC_LEVEL;
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400431 sec.level = SEC_LEVEL_1; /* 40 and 104 bit WEP */
James Ketrenose0d369d2005-09-21 11:54:30 -0500432 sec.encode_alg[key] = SEC_ALG_WEP;
Jeff Garzikb4538722005-05-12 22:48:20 -0400433
James Ketrenos259bf1f2005-09-21 11:54:22 -0500434 done:
Jeff Garzikb4538722005-05-12 22:48:20 -0400435 if (ieee->set_security)
436 ieee->set_security(dev, &sec);
437
438 /* Do not reset port if card is in Managed mode since resetting will
439 * generate new IEEE 802.11 authentication which may end up in looping
440 * with IEEE 802.1X. If your hardware requires a reset after WEP
441 * configuration (for example... Prism2), implement the reset_port in
442 * the callbacks structures used to initialize the 802.11 stack. */
443 if (ieee->reset_on_keychange &&
444 ieee->iw_mode != IW_MODE_INFRA &&
445 ieee->reset_port && ieee->reset_port(dev)) {
446 printk(KERN_DEBUG "%s: reset_port failed\n", dev->name);
447 return -EINVAL;
448 }
449 return 0;
450}
451
452int ieee80211_wx_get_encode(struct ieee80211_device *ieee,
453 struct iw_request_info *info,
454 union iwreq_data *wrqu, char *keybuf)
455{
456 struct iw_point *erq = &(wrqu->encoding);
457 int len, key;
458 struct ieee80211_crypt_data *crypt;
James Ketrenosf1bf6632005-09-21 11:53:54 -0500459 struct ieee80211_security *sec = &ieee->sec;
Jeff Garzikb4538722005-05-12 22:48:20 -0400460
461 IEEE80211_DEBUG_WX("GET_ENCODE\n");
462
463 key = erq->flags & IW_ENCODE_INDEX;
464 if (key) {
465 if (key > WEP_KEYS)
466 return -EINVAL;
467 key--;
468 } else
469 key = ieee->tx_keyidx;
470
471 crypt = ieee->crypt[key];
472 erq->flags = key + 1;
473
James Ketrenosf1bf6632005-09-21 11:53:54 -0500474 if (!sec->enabled) {
Jeff Garzikb4538722005-05-12 22:48:20 -0400475 erq->length = 0;
476 erq->flags |= IW_ENCODE_DISABLED;
477 return 0;
478 }
479
James Ketrenosf1bf6632005-09-21 11:53:54 -0500480 len = sec->key_sizes[key];
481 memcpy(keybuf, sec->keys[key], len);
Jeff Garzikb4538722005-05-12 22:48:20 -0400482
James Ketrenosf1bf6632005-09-21 11:53:54 -0500483 erq->length = (len >= 0 ? len : 0);
Jeff Garzikb4538722005-05-12 22:48:20 -0400484 erq->flags |= IW_ENCODE_ENABLED;
485
486 if (ieee->open_wep)
487 erq->flags |= IW_ENCODE_OPEN;
488 else
489 erq->flags |= IW_ENCODE_RESTRICTED;
490
491 return 0;
492}
493
James Ketrenose0d369d2005-09-21 11:54:30 -0500494int ieee80211_wx_set_encodeext(struct ieee80211_device *ieee,
495 struct iw_request_info *info,
496 union iwreq_data *wrqu, char *extra)
497{
498 struct net_device *dev = ieee->dev;
499 struct iw_point *encoding = &wrqu->encoding;
500 struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
501 int i, idx, ret = 0;
James Ketrenosccd0fda2005-09-21 11:58:32 -0500502 int group_key = 0;
James Ketrenose0d369d2005-09-21 11:54:30 -0500503 const char *alg, *module;
504 struct ieee80211_crypto_ops *ops;
505 struct ieee80211_crypt_data **crypt;
506
507 struct ieee80211_security sec = {
508 .flags = 0,
509 };
510
511 idx = encoding->flags & IW_ENCODE_INDEX;
512 if (idx) {
513 if (idx < 1 || idx > WEP_KEYS)
514 return -EINVAL;
515 idx--;
516 } else
517 idx = ieee->tx_keyidx;
518
James Ketrenosccd0fda2005-09-21 11:58:32 -0500519 if (ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) {
James Ketrenose0d369d2005-09-21 11:54:30 -0500520 crypt = &ieee->crypt[idx];
James Ketrenosccd0fda2005-09-21 11:58:32 -0500521 group_key = 1;
522 } else {
James Ketrenose0d369d2005-09-21 11:54:30 -0500523 if (idx != 0)
524 return -EINVAL;
525 if (ieee->iw_mode == IW_MODE_INFRA)
526 crypt = &ieee->crypt[idx];
527 else
528 return -EINVAL;
529 }
530
531 sec.flags |= SEC_ENABLED | SEC_ENCRYPT;
532 if ((encoding->flags & IW_ENCODE_DISABLED) ||
533 ext->alg == IW_ENCODE_ALG_NONE) {
534 if (*crypt)
535 ieee80211_crypt_delayed_deinit(ieee, crypt);
536
537 for (i = 0; i < WEP_KEYS; i++)
538 if (ieee->crypt[i] != NULL)
539 break;
540
541 if (i == WEP_KEYS) {
542 sec.enabled = 0;
543 sec.encrypt = 0;
544 sec.level = SEC_LEVEL_0;
545 sec.flags |= SEC_LEVEL;
546 }
547 goto done;
548 }
549
550 sec.enabled = 1;
551 sec.encrypt = 1;
552
James Ketrenosccd0fda2005-09-21 11:58:32 -0500553 if (group_key ? !ieee->host_mc_decrypt :
554 !(ieee->host_encrypt || ieee->host_decrypt ||
555 ieee->host_encrypt_msdu))
James Ketrenose0d369d2005-09-21 11:54:30 -0500556 goto skip_host_crypt;
557
558 switch (ext->alg) {
559 case IW_ENCODE_ALG_WEP:
560 alg = "WEP";
561 module = "ieee80211_crypt_wep";
562 break;
563 case IW_ENCODE_ALG_TKIP:
564 alg = "TKIP";
565 module = "ieee80211_crypt_tkip";
566 break;
567 case IW_ENCODE_ALG_CCMP:
568 alg = "CCMP";
569 module = "ieee80211_crypt_ccmp";
570 break;
571 default:
572 IEEE80211_DEBUG_WX("%s: unknown crypto alg %d\n",
573 dev->name, ext->alg);
574 ret = -EINVAL;
575 goto done;
576 }
577
578 ops = ieee80211_get_crypto_ops(alg);
579 if (ops == NULL) {
580 request_module(module);
581 ops = ieee80211_get_crypto_ops(alg);
582 }
583 if (ops == NULL) {
584 IEEE80211_DEBUG_WX("%s: unknown crypto alg %d\n",
585 dev->name, ext->alg);
586 ret = -EINVAL;
587 goto done;
588 }
589
590 if (*crypt == NULL || (*crypt)->ops != ops) {
591 struct ieee80211_crypt_data *new_crypt;
592
593 ieee80211_crypt_delayed_deinit(ieee, crypt);
594
595 new_crypt = (struct ieee80211_crypt_data *)
596 kmalloc(sizeof(*new_crypt), GFP_KERNEL);
597 if (new_crypt == NULL) {
598 ret = -ENOMEM;
599 goto done;
600 }
601 memset(new_crypt, 0, sizeof(struct ieee80211_crypt_data));
602 new_crypt->ops = ops;
603 if (new_crypt->ops && try_module_get(new_crypt->ops->owner))
James Ketrenos6eb6edf2005-09-22 10:34:15 +0000604 new_crypt->priv = new_crypt->ops->init(idx);
James Ketrenose0d369d2005-09-21 11:54:30 -0500605 if (new_crypt->priv == NULL) {
606 kfree(new_crypt);
607 ret = -EINVAL;
608 goto done;
609 }
610 *crypt = new_crypt;
611 }
612
613 if (ext->key_len > 0 && (*crypt)->ops->set_key &&
614 (*crypt)->ops->set_key(ext->key, ext->key_len, ext->rx_seq,
615 (*crypt)->priv) < 0) {
616 IEEE80211_DEBUG_WX("%s: key setting failed\n", dev->name);
617 ret = -EINVAL;
618 goto done;
619 }
620
621 skip_host_crypt:
622 if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) {
623 ieee->tx_keyidx = idx;
624 sec.active_key = idx;
625 sec.flags |= SEC_ACTIVE_KEY;
626 }
627
628 if (ext->alg != IW_ENCODE_ALG_NONE) {
629 memcpy(sec.keys[idx], ext->key, ext->key_len);
630 sec.key_sizes[idx] = ext->key_len;
631 sec.flags |= (1 << idx);
632 if (ext->alg == IW_ENCODE_ALG_WEP) {
633 sec.encode_alg[idx] = SEC_ALG_WEP;
634 sec.flags |= SEC_LEVEL;
635 sec.level = SEC_LEVEL_1;
636 } else if (ext->alg == IW_ENCODE_ALG_TKIP) {
637 sec.encode_alg[idx] = SEC_ALG_TKIP;
638 sec.flags |= SEC_LEVEL;
639 sec.level = SEC_LEVEL_2;
640 } else if (ext->alg == IW_ENCODE_ALG_CCMP) {
641 sec.encode_alg[idx] = SEC_ALG_CCMP;
642 sec.flags |= SEC_LEVEL;
643 sec.level = SEC_LEVEL_3;
644 }
James Ketrenosccd0fda2005-09-21 11:58:32 -0500645 /* Don't set sec level for group keys. */
646 if (group_key)
647 sec.flags &= ~SEC_LEVEL;
James Ketrenose0d369d2005-09-21 11:54:30 -0500648 }
649 done:
650 if (ieee->set_security)
651 ieee->set_security(ieee->dev, &sec);
652
653 /*
654 * Do not reset port if card is in Managed mode since resetting will
655 * generate new IEEE 802.11 authentication which may end up in looping
656 * with IEEE 802.1X. If your hardware requires a reset after WEP
657 * configuration (for example... Prism2), implement the reset_port in
658 * the callbacks structures used to initialize the 802.11 stack.
659 */
660 if (ieee->reset_on_keychange &&
661 ieee->iw_mode != IW_MODE_INFRA &&
662 ieee->reset_port && ieee->reset_port(dev)) {
663 IEEE80211_DEBUG_WX("%s: reset_port failed\n", dev->name);
664 return -EINVAL;
665 }
666
667 return ret;
668}
669
670int ieee80211_wx_get_encodeext(struct ieee80211_device *ieee,
671 struct iw_request_info *info,
672 union iwreq_data *wrqu, char *extra)
673{
674 struct iw_point *encoding = &wrqu->encoding;
675 struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
676 struct ieee80211_security *sec = &ieee->sec;
677 int idx, max_key_len;
678
679 max_key_len = encoding->length - sizeof(*ext);
680 if (max_key_len < 0)
681 return -EINVAL;
682
683 idx = encoding->flags & IW_ENCODE_INDEX;
684 if (idx) {
685 if (idx < 1 || idx > WEP_KEYS)
686 return -EINVAL;
687 idx--;
688 } else
689 idx = ieee->tx_keyidx;
690
691 if (!ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY)
692 if (idx != 0 || ieee->iw_mode != IW_MODE_INFRA)
693 return -EINVAL;
694
695 encoding->flags = idx + 1;
696 memset(ext, 0, sizeof(*ext));
697
698 if (!sec->enabled) {
699 ext->alg = IW_ENCODE_ALG_NONE;
700 ext->key_len = 0;
701 encoding->flags |= IW_ENCODE_DISABLED;
702 } else {
703 if (sec->encode_alg[idx] == SEC_ALG_WEP)
704 ext->alg = IW_ENCODE_ALG_WEP;
705 else if (sec->encode_alg[idx] == SEC_ALG_TKIP)
706 ext->alg = IW_ENCODE_ALG_TKIP;
707 else if (sec->encode_alg[idx] == SEC_ALG_CCMP)
708 ext->alg = IW_ENCODE_ALG_CCMP;
709 else
710 return -EINVAL;
711
712 ext->key_len = sec->key_sizes[idx];
713 memcpy(ext->key, sec->keys[idx], ext->key_len);
714 encoding->flags |= IW_ENCODE_ENABLED;
715 if (ext->key_len &&
716 (ext->alg == IW_ENCODE_ALG_TKIP ||
717 ext->alg == IW_ENCODE_ALG_CCMP))
718 ext->ext_flags |= IW_ENCODE_EXT_TX_SEQ_VALID;
719
720 }
721
722 return 0;
723}
724
725EXPORT_SYMBOL(ieee80211_wx_set_encodeext);
726EXPORT_SYMBOL(ieee80211_wx_get_encodeext);
James Ketrenose0d369d2005-09-21 11:54:30 -0500727
Jeff Garzikb4538722005-05-12 22:48:20 -0400728EXPORT_SYMBOL(ieee80211_wx_get_scan);
729EXPORT_SYMBOL(ieee80211_wx_set_encode);
730EXPORT_SYMBOL(ieee80211_wx_get_encode);