blob: f5b805355000c798d7e3aa0792966ed2094a95a3 [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) +
James Ketrenos81f87522005-10-24 10:20:53 -0500164 62 * (ieee->perfect_rssi -
165 network->stats.rssi))) /
166 ((ieee->perfect_rssi -
167 ieee->worst_rssi) * (ieee->perfect_rssi -
168 ieee->worst_rssi));
James Ketrenosb1b508e2005-09-13 17:27:19 -0500169 if (iwe.u.qual.qual > 100)
170 iwe.u.qual.qual = 100;
171 else if (iwe.u.qual.qual < 1)
172 iwe.u.qual.qual = 0;
173 }
174
175 if (!(network->stats.mask & IEEE80211_STATMASK_NOISE)) {
Jeff Garzikb4538722005-05-12 22:48:20 -0400176 iwe.u.qual.updated |= IW_QUAL_NOISE_INVALID;
James Ketrenosb1b508e2005-09-13 17:27:19 -0500177 iwe.u.qual.noise = 0;
178 } else {
179 iwe.u.qual.noise = network->stats.noise;
180 }
Jeff Garzikb4538722005-05-12 22:48:20 -0400181
182 start = iwe_stream_add_event(start, stop, &iwe, IW_EV_QUAL_LEN);
183
184 iwe.cmd = IWEVCUSTOM;
185 p = custom;
186
187 iwe.u.data.length = p - custom;
188 if (iwe.u.data.length)
189 start = iwe_stream_add_point(start, stop, &iwe, custom);
190
James Ketrenos20d64712005-09-21 11:53:43 -0500191 if (network->wpa_ie_len) {
Jeff Garzikb4538722005-05-12 22:48:20 -0400192 char buf[MAX_WPA_IE_LEN * 2 + 30];
193
194 u8 *p = buf;
195 p += sprintf(p, "wpa_ie=");
196 for (i = 0; i < network->wpa_ie_len; i++) {
197 p += sprintf(p, "%02x", network->wpa_ie[i]);
198 }
199
200 memset(&iwe, 0, sizeof(iwe));
201 iwe.cmd = IWEVCUSTOM;
202 iwe.u.data.length = strlen(buf);
203 start = iwe_stream_add_point(start, stop, &iwe, buf);
204 }
205
James Ketrenos20d64712005-09-21 11:53:43 -0500206 if (network->rsn_ie_len) {
Jeff Garzikb4538722005-05-12 22:48:20 -0400207 char buf[MAX_WPA_IE_LEN * 2 + 30];
208
209 u8 *p = buf;
210 p += sprintf(p, "rsn_ie=");
211 for (i = 0; i < network->rsn_ie_len; i++) {
212 p += sprintf(p, "%02x", network->rsn_ie[i]);
213 }
214
215 memset(&iwe, 0, sizeof(iwe));
216 iwe.cmd = IWEVCUSTOM;
217 iwe.u.data.length = strlen(buf);
218 start = iwe_stream_add_point(start, stop, &iwe, buf);
219 }
220
221 /* Add EXTRA: Age to display seconds since last beacon/probe response
222 * for given network. */
223 iwe.cmd = IWEVCUSTOM;
224 p = custom;
225 p += snprintf(p, MAX_CUSTOM_LEN - (p - custom),
James Ketrenos42e349f2005-09-21 11:54:07 -0500226 " Last beacon: %dms ago",
227 jiffies_to_msecs(jiffies - network->last_scanned));
Jeff Garzikb4538722005-05-12 22:48:20 -0400228 iwe.u.data.length = p - custom;
229 if (iwe.u.data.length)
230 start = iwe_stream_add_point(start, stop, &iwe, custom);
231
Jeff Garzikb4538722005-05-12 22:48:20 -0400232 return start;
233}
234
235int ieee80211_wx_get_scan(struct ieee80211_device *ieee,
236 struct iw_request_info *info,
237 union iwreq_data *wrqu, char *extra)
238{
239 struct ieee80211_network *network;
240 unsigned long flags;
241
242 char *ev = extra;
243 char *stop = ev + IW_SCAN_MAX_DATA;
244 int i = 0;
245
246 IEEE80211_DEBUG_WX("Getting scan\n");
247
248 spin_lock_irqsave(&ieee->lock, flags);
249
250 list_for_each_entry(network, &ieee->network_list, list) {
251 i++;
252 if (ieee->scan_age == 0 ||
253 time_after(network->last_scanned + ieee->scan_age, jiffies))
254 ev = ipw2100_translate_scan(ieee, ev, stop, network);
255 else
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400256 IEEE80211_DEBUG_SCAN("Not showing network '%s ("
James Ketrenos42e349f2005-09-21 11:54:07 -0500257 MAC_FMT ")' due to age (%dms).\n",
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400258 escape_essid(network->ssid,
259 network->ssid_len),
260 MAC_ARG(network->bssid),
James Ketrenos42e349f2005-09-21 11:54:07 -0500261 jiffies_to_msecs(jiffies -
262 network->
263 last_scanned));
Jeff Garzikb4538722005-05-12 22:48:20 -0400264 }
265
266 spin_unlock_irqrestore(&ieee->lock, flags);
267
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400268 wrqu->data.length = ev - extra;
Jeff Garzikb4538722005-05-12 22:48:20 -0400269 wrqu->data.flags = 0;
270
271 IEEE80211_DEBUG_WX("exit: %d networks returned.\n", i);
272
273 return 0;
274}
275
276int ieee80211_wx_set_encode(struct ieee80211_device *ieee,
277 struct iw_request_info *info,
278 union iwreq_data *wrqu, char *keybuf)
279{
280 struct iw_point *erq = &(wrqu->encoding);
281 struct net_device *dev = ieee->dev;
282 struct ieee80211_security sec = {
283 .flags = 0
284 };
285 int i, key, key_provided, len;
286 struct ieee80211_crypt_data **crypt;
James Ketrenosf1bf6632005-09-21 11:53:54 -0500287 int host_crypto = ieee->host_encrypt || ieee->host_decrypt;
Jeff Garzikb4538722005-05-12 22:48:20 -0400288
289 IEEE80211_DEBUG_WX("SET_ENCODE\n");
290
291 key = erq->flags & IW_ENCODE_INDEX;
292 if (key) {
293 if (key > WEP_KEYS)
294 return -EINVAL;
295 key--;
296 key_provided = 1;
297 } else {
298 key_provided = 0;
299 key = ieee->tx_keyidx;
300 }
301
302 IEEE80211_DEBUG_WX("Key: %d [%s]\n", key, key_provided ?
303 "provided" : "default");
304
305 crypt = &ieee->crypt[key];
306
307 if (erq->flags & IW_ENCODE_DISABLED) {
308 if (key_provided && *crypt) {
309 IEEE80211_DEBUG_WX("Disabling encryption on key %d.\n",
310 key);
311 ieee80211_crypt_delayed_deinit(ieee, crypt);
312 } else
313 IEEE80211_DEBUG_WX("Disabling encryption.\n");
314
315 /* Check all the keys to see if any are still configured,
316 * and if no key index was provided, de-init them all */
317 for (i = 0; i < WEP_KEYS; i++) {
318 if (ieee->crypt[i] != NULL) {
319 if (key_provided)
320 break;
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400321 ieee80211_crypt_delayed_deinit(ieee,
322 &ieee->crypt[i]);
Jeff Garzikb4538722005-05-12 22:48:20 -0400323 }
324 }
325
326 if (i == WEP_KEYS) {
327 sec.enabled = 0;
James Ketrenosf1bf6632005-09-21 11:53:54 -0500328 sec.encrypt = 0;
Jeff Garzikb4538722005-05-12 22:48:20 -0400329 sec.level = SEC_LEVEL_0;
James Ketrenos259bf1f2005-09-21 11:54:22 -0500330 sec.flags |= SEC_ENABLED | SEC_LEVEL | SEC_ENCRYPT;
Jeff Garzikb4538722005-05-12 22:48:20 -0400331 }
332
333 goto done;
334 }
335
Jeff Garzikb4538722005-05-12 22:48:20 -0400336 sec.enabled = 1;
James Ketrenosf1bf6632005-09-21 11:53:54 -0500337 sec.encrypt = 1;
James Ketrenos259bf1f2005-09-21 11:54:22 -0500338 sec.flags |= SEC_ENABLED | SEC_ENCRYPT;
Jeff Garzikb4538722005-05-12 22:48:20 -0400339
340 if (*crypt != NULL && (*crypt)->ops != NULL &&
341 strcmp((*crypt)->ops->name, "WEP") != 0) {
342 /* changing to use WEP; deinit previously used algorithm
343 * on this key */
344 ieee80211_crypt_delayed_deinit(ieee, crypt);
345 }
346
James Ketrenosf1bf6632005-09-21 11:53:54 -0500347 if (*crypt == NULL && host_crypto) {
Jeff Garzikb4538722005-05-12 22:48:20 -0400348 struct ieee80211_crypt_data *new_crypt;
349
350 /* take WEP into use */
351 new_crypt = kmalloc(sizeof(struct ieee80211_crypt_data),
352 GFP_KERNEL);
353 if (new_crypt == NULL)
354 return -ENOMEM;
355 memset(new_crypt, 0, sizeof(struct ieee80211_crypt_data));
356 new_crypt->ops = ieee80211_get_crypto_ops("WEP");
357 if (!new_crypt->ops) {
358 request_module("ieee80211_crypt_wep");
359 new_crypt->ops = ieee80211_get_crypto_ops("WEP");
360 }
361
362 if (new_crypt->ops && try_module_get(new_crypt->ops->owner))
James Ketrenos6eb6edf2005-09-22 10:34:15 +0000363 new_crypt->priv = new_crypt->ops->init(key);
Jeff Garzikb4538722005-05-12 22:48:20 -0400364
365 if (!new_crypt->ops || !new_crypt->priv) {
366 kfree(new_crypt);
367 new_crypt = NULL;
368
369 printk(KERN_WARNING "%s: could not initialize WEP: "
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400370 "load module ieee80211_crypt_wep\n", dev->name);
Jeff Garzikb4538722005-05-12 22:48:20 -0400371 return -EOPNOTSUPP;
372 }
373 *crypt = new_crypt;
374 }
375
376 /* If a new key was provided, set it up */
377 if (erq->length > 0) {
378 len = erq->length <= 5 ? 5 : 13;
379 memcpy(sec.keys[key], keybuf, erq->length);
380 if (len > erq->length)
381 memset(sec.keys[key] + erq->length, 0,
382 len - erq->length);
383 IEEE80211_DEBUG_WX("Setting key %d to '%s' (%d:%d bytes)\n",
384 key, escape_essid(sec.keys[key], len),
385 erq->length, len);
386 sec.key_sizes[key] = len;
James Ketrenosf1bf6632005-09-21 11:53:54 -0500387 if (*crypt)
388 (*crypt)->ops->set_key(sec.keys[key], len, NULL,
389 (*crypt)->priv);
Jeff Garzikb4538722005-05-12 22:48:20 -0400390 sec.flags |= (1 << key);
391 /* This ensures a key will be activated if no key is
392 * explicitely set */
393 if (key == sec.active_key)
394 sec.flags |= SEC_ACTIVE_KEY;
Jeff Garzikb4538722005-05-12 22:48:20 -0400395
James Ketrenosf1bf6632005-09-21 11:53:54 -0500396 } else {
397 if (host_crypto) {
398 len = (*crypt)->ops->get_key(sec.keys[key], WEP_KEY_LEN,
399 NULL, (*crypt)->priv);
400 if (len == 0) {
401 /* Set a default key of all 0 */
402 IEEE80211_DEBUG_WX("Setting key %d to all "
403 "zero.\n", key);
404 memset(sec.keys[key], 0, 13);
405 (*crypt)->ops->set_key(sec.keys[key], 13, NULL,
406 (*crypt)->priv);
407 sec.key_sizes[key] = 13;
408 sec.flags |= (1 << key);
409 }
410 }
Jeff Garzikb4538722005-05-12 22:48:20 -0400411 /* No key data - just set the default TX key index */
412 if (key_provided) {
James Ketrenosf1bf6632005-09-21 11:53:54 -0500413 IEEE80211_DEBUG_WX("Setting key %d to default Tx "
414 "key.\n", key);
Jeff Garzikb4538722005-05-12 22:48:20 -0400415 ieee->tx_keyidx = key;
416 sec.active_key = key;
417 sec.flags |= SEC_ACTIVE_KEY;
418 }
419 }
James Ketrenos7dc888f2005-09-21 11:58:38 -0500420 if (erq->flags & (IW_ENCODE_OPEN | IW_ENCODE_RESTRICTED)) {
421 ieee->open_wep = !(erq->flags & IW_ENCODE_RESTRICTED);
422 sec.auth_mode = ieee->open_wep ? WLAN_AUTH_OPEN :
423 WLAN_AUTH_SHARED_KEY;
424 sec.flags |= SEC_AUTH_MODE;
425 IEEE80211_DEBUG_WX("Auth: %s\n",
426 sec.auth_mode == WLAN_AUTH_OPEN ?
427 "OPEN" : "SHARED KEY");
428 }
Jeff Garzikb4538722005-05-12 22:48:20 -0400429
430 /* For now we just support WEP, so only set that security level...
431 * TODO: When WPA is added this is one place that needs to change */
432 sec.flags |= SEC_LEVEL;
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400433 sec.level = SEC_LEVEL_1; /* 40 and 104 bit WEP */
James Ketrenose0d369d2005-09-21 11:54:30 -0500434 sec.encode_alg[key] = SEC_ALG_WEP;
Jeff Garzikb4538722005-05-12 22:48:20 -0400435
James Ketrenos259bf1f2005-09-21 11:54:22 -0500436 done:
Jeff Garzikb4538722005-05-12 22:48:20 -0400437 if (ieee->set_security)
438 ieee->set_security(dev, &sec);
439
440 /* Do not reset port if card is in Managed mode since resetting will
441 * generate new IEEE 802.11 authentication which may end up in looping
442 * with IEEE 802.1X. If your hardware requires a reset after WEP
443 * configuration (for example... Prism2), implement the reset_port in
444 * the callbacks structures used to initialize the 802.11 stack. */
445 if (ieee->reset_on_keychange &&
446 ieee->iw_mode != IW_MODE_INFRA &&
447 ieee->reset_port && ieee->reset_port(dev)) {
448 printk(KERN_DEBUG "%s: reset_port failed\n", dev->name);
449 return -EINVAL;
450 }
451 return 0;
452}
453
454int ieee80211_wx_get_encode(struct ieee80211_device *ieee,
455 struct iw_request_info *info,
456 union iwreq_data *wrqu, char *keybuf)
457{
458 struct iw_point *erq = &(wrqu->encoding);
459 int len, key;
460 struct ieee80211_crypt_data *crypt;
James Ketrenosf1bf6632005-09-21 11:53:54 -0500461 struct ieee80211_security *sec = &ieee->sec;
Jeff Garzikb4538722005-05-12 22:48:20 -0400462
463 IEEE80211_DEBUG_WX("GET_ENCODE\n");
464
465 key = erq->flags & IW_ENCODE_INDEX;
466 if (key) {
467 if (key > WEP_KEYS)
468 return -EINVAL;
469 key--;
470 } else
471 key = ieee->tx_keyidx;
472
473 crypt = ieee->crypt[key];
474 erq->flags = key + 1;
475
James Ketrenosf1bf6632005-09-21 11:53:54 -0500476 if (!sec->enabled) {
Jeff Garzikb4538722005-05-12 22:48:20 -0400477 erq->length = 0;
478 erq->flags |= IW_ENCODE_DISABLED;
479 return 0;
480 }
481
James Ketrenosf1bf6632005-09-21 11:53:54 -0500482 len = sec->key_sizes[key];
483 memcpy(keybuf, sec->keys[key], len);
Jeff Garzikb4538722005-05-12 22:48:20 -0400484
James Ketrenosf1bf6632005-09-21 11:53:54 -0500485 erq->length = (len >= 0 ? len : 0);
Jeff Garzikb4538722005-05-12 22:48:20 -0400486 erq->flags |= IW_ENCODE_ENABLED;
487
488 if (ieee->open_wep)
489 erq->flags |= IW_ENCODE_OPEN;
490 else
491 erq->flags |= IW_ENCODE_RESTRICTED;
492
493 return 0;
494}
495
James Ketrenose0d369d2005-09-21 11:54:30 -0500496int ieee80211_wx_set_encodeext(struct ieee80211_device *ieee,
497 struct iw_request_info *info,
498 union iwreq_data *wrqu, char *extra)
499{
500 struct net_device *dev = ieee->dev;
501 struct iw_point *encoding = &wrqu->encoding;
502 struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
503 int i, idx, ret = 0;
James Ketrenosccd0fda2005-09-21 11:58:32 -0500504 int group_key = 0;
James Ketrenose0d369d2005-09-21 11:54:30 -0500505 const char *alg, *module;
506 struct ieee80211_crypto_ops *ops;
507 struct ieee80211_crypt_data **crypt;
508
509 struct ieee80211_security sec = {
510 .flags = 0,
511 };
512
513 idx = encoding->flags & IW_ENCODE_INDEX;
514 if (idx) {
515 if (idx < 1 || idx > WEP_KEYS)
516 return -EINVAL;
517 idx--;
518 } else
519 idx = ieee->tx_keyidx;
520
James Ketrenosccd0fda2005-09-21 11:58:32 -0500521 if (ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) {
James Ketrenose0d369d2005-09-21 11:54:30 -0500522 crypt = &ieee->crypt[idx];
James Ketrenosccd0fda2005-09-21 11:58:32 -0500523 group_key = 1;
524 } else {
James Ketrenose0d369d2005-09-21 11:54:30 -0500525 if (idx != 0)
526 return -EINVAL;
527 if (ieee->iw_mode == IW_MODE_INFRA)
528 crypt = &ieee->crypt[idx];
529 else
530 return -EINVAL;
531 }
532
533 sec.flags |= SEC_ENABLED | SEC_ENCRYPT;
534 if ((encoding->flags & IW_ENCODE_DISABLED) ||
535 ext->alg == IW_ENCODE_ALG_NONE) {
536 if (*crypt)
537 ieee80211_crypt_delayed_deinit(ieee, crypt);
538
539 for (i = 0; i < WEP_KEYS; i++)
540 if (ieee->crypt[i] != NULL)
541 break;
542
543 if (i == WEP_KEYS) {
544 sec.enabled = 0;
545 sec.encrypt = 0;
546 sec.level = SEC_LEVEL_0;
547 sec.flags |= SEC_LEVEL;
548 }
549 goto done;
550 }
551
552 sec.enabled = 1;
553 sec.encrypt = 1;
554
James Ketrenosccd0fda2005-09-21 11:58:32 -0500555 if (group_key ? !ieee->host_mc_decrypt :
556 !(ieee->host_encrypt || ieee->host_decrypt ||
557 ieee->host_encrypt_msdu))
James Ketrenose0d369d2005-09-21 11:54:30 -0500558 goto skip_host_crypt;
559
560 switch (ext->alg) {
561 case IW_ENCODE_ALG_WEP:
562 alg = "WEP";
563 module = "ieee80211_crypt_wep";
564 break;
565 case IW_ENCODE_ALG_TKIP:
566 alg = "TKIP";
567 module = "ieee80211_crypt_tkip";
568 break;
569 case IW_ENCODE_ALG_CCMP:
570 alg = "CCMP";
571 module = "ieee80211_crypt_ccmp";
572 break;
573 default:
574 IEEE80211_DEBUG_WX("%s: unknown crypto alg %d\n",
575 dev->name, ext->alg);
576 ret = -EINVAL;
577 goto done;
578 }
579
580 ops = ieee80211_get_crypto_ops(alg);
581 if (ops == NULL) {
582 request_module(module);
583 ops = ieee80211_get_crypto_ops(alg);
584 }
585 if (ops == NULL) {
586 IEEE80211_DEBUG_WX("%s: unknown crypto alg %d\n",
587 dev->name, ext->alg);
588 ret = -EINVAL;
589 goto done;
590 }
591
592 if (*crypt == NULL || (*crypt)->ops != ops) {
593 struct ieee80211_crypt_data *new_crypt;
594
595 ieee80211_crypt_delayed_deinit(ieee, crypt);
596
597 new_crypt = (struct ieee80211_crypt_data *)
598 kmalloc(sizeof(*new_crypt), GFP_KERNEL);
599 if (new_crypt == NULL) {
600 ret = -ENOMEM;
601 goto done;
602 }
603 memset(new_crypt, 0, sizeof(struct ieee80211_crypt_data));
604 new_crypt->ops = ops;
605 if (new_crypt->ops && try_module_get(new_crypt->ops->owner))
James Ketrenos6eb6edf2005-09-22 10:34:15 +0000606 new_crypt->priv = new_crypt->ops->init(idx);
James Ketrenose0d369d2005-09-21 11:54:30 -0500607 if (new_crypt->priv == NULL) {
608 kfree(new_crypt);
609 ret = -EINVAL;
610 goto done;
611 }
612 *crypt = new_crypt;
613 }
614
615 if (ext->key_len > 0 && (*crypt)->ops->set_key &&
616 (*crypt)->ops->set_key(ext->key, ext->key_len, ext->rx_seq,
617 (*crypt)->priv) < 0) {
618 IEEE80211_DEBUG_WX("%s: key setting failed\n", dev->name);
619 ret = -EINVAL;
620 goto done;
621 }
622
623 skip_host_crypt:
624 if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) {
625 ieee->tx_keyidx = idx;
626 sec.active_key = idx;
627 sec.flags |= SEC_ACTIVE_KEY;
628 }
629
630 if (ext->alg != IW_ENCODE_ALG_NONE) {
631 memcpy(sec.keys[idx], ext->key, ext->key_len);
632 sec.key_sizes[idx] = ext->key_len;
633 sec.flags |= (1 << idx);
634 if (ext->alg == IW_ENCODE_ALG_WEP) {
635 sec.encode_alg[idx] = SEC_ALG_WEP;
636 sec.flags |= SEC_LEVEL;
637 sec.level = SEC_LEVEL_1;
638 } else if (ext->alg == IW_ENCODE_ALG_TKIP) {
639 sec.encode_alg[idx] = SEC_ALG_TKIP;
640 sec.flags |= SEC_LEVEL;
641 sec.level = SEC_LEVEL_2;
642 } else if (ext->alg == IW_ENCODE_ALG_CCMP) {
643 sec.encode_alg[idx] = SEC_ALG_CCMP;
644 sec.flags |= SEC_LEVEL;
645 sec.level = SEC_LEVEL_3;
646 }
James Ketrenosccd0fda2005-09-21 11:58:32 -0500647 /* Don't set sec level for group keys. */
648 if (group_key)
649 sec.flags &= ~SEC_LEVEL;
James Ketrenose0d369d2005-09-21 11:54:30 -0500650 }
651 done:
652 if (ieee->set_security)
653 ieee->set_security(ieee->dev, &sec);
654
655 /*
656 * Do not reset port if card is in Managed mode since resetting will
657 * generate new IEEE 802.11 authentication which may end up in looping
658 * with IEEE 802.1X. If your hardware requires a reset after WEP
659 * configuration (for example... Prism2), implement the reset_port in
660 * the callbacks structures used to initialize the 802.11 stack.
661 */
662 if (ieee->reset_on_keychange &&
663 ieee->iw_mode != IW_MODE_INFRA &&
664 ieee->reset_port && ieee->reset_port(dev)) {
665 IEEE80211_DEBUG_WX("%s: reset_port failed\n", dev->name);
666 return -EINVAL;
667 }
668
669 return ret;
670}
671
672int ieee80211_wx_get_encodeext(struct ieee80211_device *ieee,
673 struct iw_request_info *info,
674 union iwreq_data *wrqu, char *extra)
675{
676 struct iw_point *encoding = &wrqu->encoding;
677 struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
678 struct ieee80211_security *sec = &ieee->sec;
679 int idx, max_key_len;
680
681 max_key_len = encoding->length - sizeof(*ext);
682 if (max_key_len < 0)
683 return -EINVAL;
684
685 idx = encoding->flags & IW_ENCODE_INDEX;
686 if (idx) {
687 if (idx < 1 || idx > WEP_KEYS)
688 return -EINVAL;
689 idx--;
690 } else
691 idx = ieee->tx_keyidx;
692
693 if (!ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY)
694 if (idx != 0 || ieee->iw_mode != IW_MODE_INFRA)
695 return -EINVAL;
696
697 encoding->flags = idx + 1;
698 memset(ext, 0, sizeof(*ext));
699
700 if (!sec->enabled) {
701 ext->alg = IW_ENCODE_ALG_NONE;
702 ext->key_len = 0;
703 encoding->flags |= IW_ENCODE_DISABLED;
704 } else {
705 if (sec->encode_alg[idx] == SEC_ALG_WEP)
706 ext->alg = IW_ENCODE_ALG_WEP;
707 else if (sec->encode_alg[idx] == SEC_ALG_TKIP)
708 ext->alg = IW_ENCODE_ALG_TKIP;
709 else if (sec->encode_alg[idx] == SEC_ALG_CCMP)
710 ext->alg = IW_ENCODE_ALG_CCMP;
711 else
712 return -EINVAL;
713
714 ext->key_len = sec->key_sizes[idx];
715 memcpy(ext->key, sec->keys[idx], ext->key_len);
716 encoding->flags |= IW_ENCODE_ENABLED;
717 if (ext->key_len &&
718 (ext->alg == IW_ENCODE_ALG_TKIP ||
719 ext->alg == IW_ENCODE_ALG_CCMP))
720 ext->ext_flags |= IW_ENCODE_EXT_TX_SEQ_VALID;
721
722 }
723
724 return 0;
725}
726
727EXPORT_SYMBOL(ieee80211_wx_set_encodeext);
728EXPORT_SYMBOL(ieee80211_wx_get_encodeext);
James Ketrenose0d369d2005-09-21 11:54:30 -0500729
Jeff Garzikb4538722005-05-12 22:48:20 -0400730EXPORT_SYMBOL(ieee80211_wx_get_scan);
731EXPORT_SYMBOL(ieee80211_wx_set_encode);
732EXPORT_SYMBOL(ieee80211_wx_get_encode);