blob: 9496918e6106310fc1cf1d915e39e765f01b6354 [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
Arjan van de Ven858119e2006-01-14 13:20:43 -080045static 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;
James Ketrenosb1b508e2005-09-13 17:27:19 -0500152 } else {
Jiri Benc757d18f2005-10-10 19:16:53 +0200153 if (ieee->perfect_rssi == ieee->worst_rssi)
154 iwe.u.qual.qual = 100;
155 else
156 iwe.u.qual.qual =
157 (100 *
158 (ieee->perfect_rssi - ieee->worst_rssi) *
159 (ieee->perfect_rssi - ieee->worst_rssi) -
160 (ieee->perfect_rssi - network->stats.rssi) *
161 (15 * (ieee->perfect_rssi - ieee->worst_rssi) +
James Ketrenos81f87522005-10-24 10:20:53 -0500162 62 * (ieee->perfect_rssi -
163 network->stats.rssi))) /
164 ((ieee->perfect_rssi -
165 ieee->worst_rssi) * (ieee->perfect_rssi -
166 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
Zhu Yi7bd64362006-01-19 16:21:54 +0800180 if (!(network->stats.mask & IEEE80211_STATMASK_SIGNAL)) {
181 iwe.u.qual.updated |= IW_QUAL_LEVEL_INVALID;
182 iwe.u.qual.level = 0;
183 } else {
184 iwe.u.qual.level = network->stats.signal;
185 }
186
Jeff Garzikb4538722005-05-12 22:48:20 -0400187 start = iwe_stream_add_event(start, stop, &iwe, IW_EV_QUAL_LEN);
188
189 iwe.cmd = IWEVCUSTOM;
190 p = custom;
191
192 iwe.u.data.length = p - custom;
193 if (iwe.u.data.length)
194 start = iwe_stream_add_point(start, stop, &iwe, custom);
195
James Ketrenos20d64712005-09-21 11:53:43 -0500196 if (network->wpa_ie_len) {
Jeff Garzikb4538722005-05-12 22:48:20 -0400197 char buf[MAX_WPA_IE_LEN * 2 + 30];
198
199 u8 *p = buf;
200 p += sprintf(p, "wpa_ie=");
201 for (i = 0; i < network->wpa_ie_len; i++) {
202 p += sprintf(p, "%02x", network->wpa_ie[i]);
203 }
204
205 memset(&iwe, 0, sizeof(iwe));
206 iwe.cmd = IWEVCUSTOM;
207 iwe.u.data.length = strlen(buf);
208 start = iwe_stream_add_point(start, stop, &iwe, buf);
209 }
210
James Ketrenos20d64712005-09-21 11:53:43 -0500211 if (network->rsn_ie_len) {
Jeff Garzikb4538722005-05-12 22:48:20 -0400212 char buf[MAX_WPA_IE_LEN * 2 + 30];
213
214 u8 *p = buf;
215 p += sprintf(p, "rsn_ie=");
216 for (i = 0; i < network->rsn_ie_len; i++) {
217 p += sprintf(p, "%02x", network->rsn_ie[i]);
218 }
219
220 memset(&iwe, 0, sizeof(iwe));
221 iwe.cmd = IWEVCUSTOM;
222 iwe.u.data.length = strlen(buf);
223 start = iwe_stream_add_point(start, stop, &iwe, buf);
224 }
225
226 /* Add EXTRA: Age to display seconds since last beacon/probe response
227 * for given network. */
228 iwe.cmd = IWEVCUSTOM;
229 p = custom;
230 p += snprintf(p, MAX_CUSTOM_LEN - (p - custom),
James Ketrenos42e349f2005-09-21 11:54:07 -0500231 " Last beacon: %dms ago",
232 jiffies_to_msecs(jiffies - network->last_scanned));
Jeff Garzikb4538722005-05-12 22:48:20 -0400233 iwe.u.data.length = p - custom;
234 if (iwe.u.data.length)
235 start = iwe_stream_add_point(start, stop, &iwe, custom);
236
Zhu Yi7bd64362006-01-19 16:21:54 +0800237 /* Add spectrum management information */
238 iwe.cmd = -1;
239 p = custom;
240 p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), " Channel flags: ");
241
242 if (ieee80211_get_channel_flags(ieee, network->channel) &
243 IEEE80211_CH_INVALID) {
244 iwe.cmd = IWEVCUSTOM;
245 p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), "INVALID ");
246 }
247
248 if (ieee80211_get_channel_flags(ieee, network->channel) &
249 IEEE80211_CH_RADAR_DETECT) {
250 iwe.cmd = IWEVCUSTOM;
251 p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), "DFS ");
252 }
253
254 if (iwe.cmd == IWEVCUSTOM) {
255 iwe.u.data.length = p - custom;
256 start = iwe_stream_add_point(start, stop, &iwe, custom);
257 }
258
Jeff Garzikb4538722005-05-12 22:48:20 -0400259 return start;
260}
261
Zhu Yi55cd94a2006-01-19 16:20:59 +0800262#define SCAN_ITEM_SIZE 128
263
Jeff Garzikb4538722005-05-12 22:48:20 -0400264int ieee80211_wx_get_scan(struct ieee80211_device *ieee,
265 struct iw_request_info *info,
266 union iwreq_data *wrqu, char *extra)
267{
268 struct ieee80211_network *network;
269 unsigned long flags;
Zhu Yi55cd94a2006-01-19 16:20:59 +0800270 int err = 0;
Jeff Garzikb4538722005-05-12 22:48:20 -0400271
272 char *ev = extra;
Zhu Yi55cd94a2006-01-19 16:20:59 +0800273 char *stop = ev + wrqu->data.length;
Jeff Garzikb4538722005-05-12 22:48:20 -0400274 int i = 0;
275
276 IEEE80211_DEBUG_WX("Getting scan\n");
277
278 spin_lock_irqsave(&ieee->lock, flags);
279
280 list_for_each_entry(network, &ieee->network_list, list) {
281 i++;
Zhu Yi55cd94a2006-01-19 16:20:59 +0800282 if (stop - ev < SCAN_ITEM_SIZE) {
283 err = -E2BIG;
284 break;
285 }
286
Jeff Garzikb4538722005-05-12 22:48:20 -0400287 if (ieee->scan_age == 0 ||
288 time_after(network->last_scanned + ieee->scan_age, jiffies))
289 ev = ipw2100_translate_scan(ieee, ev, stop, network);
290 else
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400291 IEEE80211_DEBUG_SCAN("Not showing network '%s ("
James Ketrenos42e349f2005-09-21 11:54:07 -0500292 MAC_FMT ")' due to age (%dms).\n",
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400293 escape_essid(network->ssid,
294 network->ssid_len),
295 MAC_ARG(network->bssid),
James Ketrenos42e349f2005-09-21 11:54:07 -0500296 jiffies_to_msecs(jiffies -
297 network->
298 last_scanned));
Jeff Garzikb4538722005-05-12 22:48:20 -0400299 }
300
301 spin_unlock_irqrestore(&ieee->lock, flags);
302
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400303 wrqu->data.length = ev - extra;
Jeff Garzikb4538722005-05-12 22:48:20 -0400304 wrqu->data.flags = 0;
305
306 IEEE80211_DEBUG_WX("exit: %d networks returned.\n", i);
307
Zhu Yi55cd94a2006-01-19 16:20:59 +0800308 return err;
Jeff Garzikb4538722005-05-12 22:48:20 -0400309}
310
311int ieee80211_wx_set_encode(struct ieee80211_device *ieee,
312 struct iw_request_info *info,
313 union iwreq_data *wrqu, char *keybuf)
314{
315 struct iw_point *erq = &(wrqu->encoding);
316 struct net_device *dev = ieee->dev;
317 struct ieee80211_security sec = {
318 .flags = 0
319 };
320 int i, key, key_provided, len;
321 struct ieee80211_crypt_data **crypt;
Johannes Berga4bf26f2005-12-31 11:35:20 +0100322 int host_crypto = ieee->host_encrypt || ieee->host_decrypt || ieee->host_build_iv;
Jeff Garzikb4538722005-05-12 22:48:20 -0400323
324 IEEE80211_DEBUG_WX("SET_ENCODE\n");
325
326 key = erq->flags & IW_ENCODE_INDEX;
327 if (key) {
328 if (key > WEP_KEYS)
329 return -EINVAL;
330 key--;
331 key_provided = 1;
332 } else {
333 key_provided = 0;
334 key = ieee->tx_keyidx;
335 }
336
337 IEEE80211_DEBUG_WX("Key: %d [%s]\n", key, key_provided ?
338 "provided" : "default");
339
340 crypt = &ieee->crypt[key];
341
342 if (erq->flags & IW_ENCODE_DISABLED) {
343 if (key_provided && *crypt) {
344 IEEE80211_DEBUG_WX("Disabling encryption on key %d.\n",
345 key);
346 ieee80211_crypt_delayed_deinit(ieee, crypt);
347 } else
348 IEEE80211_DEBUG_WX("Disabling encryption.\n");
349
350 /* Check all the keys to see if any are still configured,
351 * and if no key index was provided, de-init them all */
352 for (i = 0; i < WEP_KEYS; i++) {
353 if (ieee->crypt[i] != NULL) {
354 if (key_provided)
355 break;
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400356 ieee80211_crypt_delayed_deinit(ieee,
357 &ieee->crypt[i]);
Jeff Garzikb4538722005-05-12 22:48:20 -0400358 }
359 }
360
361 if (i == WEP_KEYS) {
362 sec.enabled = 0;
James Ketrenosf1bf6632005-09-21 11:53:54 -0500363 sec.encrypt = 0;
Jeff Garzikb4538722005-05-12 22:48:20 -0400364 sec.level = SEC_LEVEL_0;
James Ketrenos259bf1f2005-09-21 11:54:22 -0500365 sec.flags |= SEC_ENABLED | SEC_LEVEL | SEC_ENCRYPT;
Jeff Garzikb4538722005-05-12 22:48:20 -0400366 }
367
368 goto done;
369 }
370
Jeff Garzikb4538722005-05-12 22:48:20 -0400371 sec.enabled = 1;
James Ketrenosf1bf6632005-09-21 11:53:54 -0500372 sec.encrypt = 1;
James Ketrenos259bf1f2005-09-21 11:54:22 -0500373 sec.flags |= SEC_ENABLED | SEC_ENCRYPT;
Jeff Garzikb4538722005-05-12 22:48:20 -0400374
375 if (*crypt != NULL && (*crypt)->ops != NULL &&
376 strcmp((*crypt)->ops->name, "WEP") != 0) {
377 /* changing to use WEP; deinit previously used algorithm
378 * on this key */
379 ieee80211_crypt_delayed_deinit(ieee, crypt);
380 }
381
James Ketrenosf1bf6632005-09-21 11:53:54 -0500382 if (*crypt == NULL && host_crypto) {
Jeff Garzikb4538722005-05-12 22:48:20 -0400383 struct ieee80211_crypt_data *new_crypt;
384
385 /* take WEP into use */
386 new_crypt = kmalloc(sizeof(struct ieee80211_crypt_data),
387 GFP_KERNEL);
388 if (new_crypt == NULL)
389 return -ENOMEM;
390 memset(new_crypt, 0, sizeof(struct ieee80211_crypt_data));
391 new_crypt->ops = ieee80211_get_crypto_ops("WEP");
392 if (!new_crypt->ops) {
393 request_module("ieee80211_crypt_wep");
394 new_crypt->ops = ieee80211_get_crypto_ops("WEP");
395 }
396
397 if (new_crypt->ops && try_module_get(new_crypt->ops->owner))
James Ketrenos6eb6edf2005-09-22 10:34:15 +0000398 new_crypt->priv = new_crypt->ops->init(key);
Jeff Garzikb4538722005-05-12 22:48:20 -0400399
400 if (!new_crypt->ops || !new_crypt->priv) {
401 kfree(new_crypt);
402 new_crypt = NULL;
403
404 printk(KERN_WARNING "%s: could not initialize WEP: "
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400405 "load module ieee80211_crypt_wep\n", dev->name);
Jeff Garzikb4538722005-05-12 22:48:20 -0400406 return -EOPNOTSUPP;
407 }
408 *crypt = new_crypt;
409 }
410
411 /* If a new key was provided, set it up */
412 if (erq->length > 0) {
413 len = erq->length <= 5 ? 5 : 13;
414 memcpy(sec.keys[key], keybuf, erq->length);
415 if (len > erq->length)
416 memset(sec.keys[key] + erq->length, 0,
417 len - erq->length);
418 IEEE80211_DEBUG_WX("Setting key %d to '%s' (%d:%d bytes)\n",
419 key, escape_essid(sec.keys[key], len),
420 erq->length, len);
421 sec.key_sizes[key] = len;
James Ketrenosf1bf6632005-09-21 11:53:54 -0500422 if (*crypt)
423 (*crypt)->ops->set_key(sec.keys[key], len, NULL,
424 (*crypt)->priv);
Jeff Garzikb4538722005-05-12 22:48:20 -0400425 sec.flags |= (1 << key);
426 /* This ensures a key will be activated if no key is
427 * explicitely set */
428 if (key == sec.active_key)
429 sec.flags |= SEC_ACTIVE_KEY;
Jeff Garzikb4538722005-05-12 22:48:20 -0400430
James Ketrenosf1bf6632005-09-21 11:53:54 -0500431 } else {
432 if (host_crypto) {
433 len = (*crypt)->ops->get_key(sec.keys[key], WEP_KEY_LEN,
434 NULL, (*crypt)->priv);
435 if (len == 0) {
436 /* Set a default key of all 0 */
437 IEEE80211_DEBUG_WX("Setting key %d to all "
438 "zero.\n", key);
439 memset(sec.keys[key], 0, 13);
440 (*crypt)->ops->set_key(sec.keys[key], 13, NULL,
441 (*crypt)->priv);
442 sec.key_sizes[key] = 13;
443 sec.flags |= (1 << key);
444 }
445 }
Jeff Garzikb4538722005-05-12 22:48:20 -0400446 /* No key data - just set the default TX key index */
447 if (key_provided) {
James Ketrenosf1bf6632005-09-21 11:53:54 -0500448 IEEE80211_DEBUG_WX("Setting key %d to default Tx "
449 "key.\n", key);
Jeff Garzikb4538722005-05-12 22:48:20 -0400450 ieee->tx_keyidx = key;
451 sec.active_key = key;
452 sec.flags |= SEC_ACTIVE_KEY;
453 }
454 }
James Ketrenos7dc888f2005-09-21 11:58:38 -0500455 if (erq->flags & (IW_ENCODE_OPEN | IW_ENCODE_RESTRICTED)) {
456 ieee->open_wep = !(erq->flags & IW_ENCODE_RESTRICTED);
457 sec.auth_mode = ieee->open_wep ? WLAN_AUTH_OPEN :
458 WLAN_AUTH_SHARED_KEY;
459 sec.flags |= SEC_AUTH_MODE;
460 IEEE80211_DEBUG_WX("Auth: %s\n",
461 sec.auth_mode == WLAN_AUTH_OPEN ?
462 "OPEN" : "SHARED KEY");
463 }
Jeff Garzikb4538722005-05-12 22:48:20 -0400464
465 /* For now we just support WEP, so only set that security level...
466 * TODO: When WPA is added this is one place that needs to change */
467 sec.flags |= SEC_LEVEL;
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400468 sec.level = SEC_LEVEL_1; /* 40 and 104 bit WEP */
James Ketrenose0d369d2005-09-21 11:54:30 -0500469 sec.encode_alg[key] = SEC_ALG_WEP;
Jeff Garzikb4538722005-05-12 22:48:20 -0400470
James Ketrenos259bf1f2005-09-21 11:54:22 -0500471 done:
Jeff Garzikb4538722005-05-12 22:48:20 -0400472 if (ieee->set_security)
473 ieee->set_security(dev, &sec);
474
475 /* Do not reset port if card is in Managed mode since resetting will
476 * generate new IEEE 802.11 authentication which may end up in looping
477 * with IEEE 802.1X. If your hardware requires a reset after WEP
478 * configuration (for example... Prism2), implement the reset_port in
479 * the callbacks structures used to initialize the 802.11 stack. */
480 if (ieee->reset_on_keychange &&
481 ieee->iw_mode != IW_MODE_INFRA &&
482 ieee->reset_port && ieee->reset_port(dev)) {
483 printk(KERN_DEBUG "%s: reset_port failed\n", dev->name);
484 return -EINVAL;
485 }
486 return 0;
487}
488
489int ieee80211_wx_get_encode(struct ieee80211_device *ieee,
490 struct iw_request_info *info,
491 union iwreq_data *wrqu, char *keybuf)
492{
493 struct iw_point *erq = &(wrqu->encoding);
494 int len, key;
495 struct ieee80211_crypt_data *crypt;
James Ketrenosf1bf6632005-09-21 11:53:54 -0500496 struct ieee80211_security *sec = &ieee->sec;
Jeff Garzikb4538722005-05-12 22:48:20 -0400497
498 IEEE80211_DEBUG_WX("GET_ENCODE\n");
499
500 key = erq->flags & IW_ENCODE_INDEX;
501 if (key) {
502 if (key > WEP_KEYS)
503 return -EINVAL;
504 key--;
505 } else
506 key = ieee->tx_keyidx;
507
508 crypt = ieee->crypt[key];
509 erq->flags = key + 1;
510
James Ketrenosf1bf6632005-09-21 11:53:54 -0500511 if (!sec->enabled) {
Jeff Garzikb4538722005-05-12 22:48:20 -0400512 erq->length = 0;
513 erq->flags |= IW_ENCODE_DISABLED;
514 return 0;
515 }
516
James Ketrenosf1bf6632005-09-21 11:53:54 -0500517 len = sec->key_sizes[key];
518 memcpy(keybuf, sec->keys[key], len);
Jeff Garzikb4538722005-05-12 22:48:20 -0400519
James Ketrenosf1bf6632005-09-21 11:53:54 -0500520 erq->length = (len >= 0 ? len : 0);
Jeff Garzikb4538722005-05-12 22:48:20 -0400521 erq->flags |= IW_ENCODE_ENABLED;
522
523 if (ieee->open_wep)
524 erq->flags |= IW_ENCODE_OPEN;
525 else
526 erq->flags |= IW_ENCODE_RESTRICTED;
527
528 return 0;
529}
530
James Ketrenose0d369d2005-09-21 11:54:30 -0500531int ieee80211_wx_set_encodeext(struct ieee80211_device *ieee,
532 struct iw_request_info *info,
533 union iwreq_data *wrqu, char *extra)
534{
535 struct net_device *dev = ieee->dev;
536 struct iw_point *encoding = &wrqu->encoding;
537 struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
538 int i, idx, ret = 0;
James Ketrenosccd0fda2005-09-21 11:58:32 -0500539 int group_key = 0;
James Ketrenose0d369d2005-09-21 11:54:30 -0500540 const char *alg, *module;
541 struct ieee80211_crypto_ops *ops;
542 struct ieee80211_crypt_data **crypt;
543
544 struct ieee80211_security sec = {
545 .flags = 0,
546 };
547
548 idx = encoding->flags & IW_ENCODE_INDEX;
549 if (idx) {
550 if (idx < 1 || idx > WEP_KEYS)
551 return -EINVAL;
552 idx--;
553 } else
554 idx = ieee->tx_keyidx;
555
James Ketrenosccd0fda2005-09-21 11:58:32 -0500556 if (ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) {
James Ketrenose0d369d2005-09-21 11:54:30 -0500557 crypt = &ieee->crypt[idx];
James Ketrenosccd0fda2005-09-21 11:58:32 -0500558 group_key = 1;
559 } else {
Volker Braune1892772005-10-24 10:15:36 -0500560 /* some Cisco APs use idx>0 for unicast in dynamic WEP */
561 if (idx != 0 && ext->alg != IW_ENCODE_ALG_WEP)
James Ketrenose0d369d2005-09-21 11:54:30 -0500562 return -EINVAL;
563 if (ieee->iw_mode == IW_MODE_INFRA)
564 crypt = &ieee->crypt[idx];
565 else
566 return -EINVAL;
567 }
568
569 sec.flags |= SEC_ENABLED | SEC_ENCRYPT;
570 if ((encoding->flags & IW_ENCODE_DISABLED) ||
571 ext->alg == IW_ENCODE_ALG_NONE) {
572 if (*crypt)
573 ieee80211_crypt_delayed_deinit(ieee, crypt);
574
575 for (i = 0; i < WEP_KEYS; i++)
576 if (ieee->crypt[i] != NULL)
577 break;
578
579 if (i == WEP_KEYS) {
580 sec.enabled = 0;
581 sec.encrypt = 0;
582 sec.level = SEC_LEVEL_0;
583 sec.flags |= SEC_LEVEL;
584 }
585 goto done;
586 }
587
588 sec.enabled = 1;
589 sec.encrypt = 1;
590
James Ketrenosccd0fda2005-09-21 11:58:32 -0500591 if (group_key ? !ieee->host_mc_decrypt :
592 !(ieee->host_encrypt || ieee->host_decrypt ||
593 ieee->host_encrypt_msdu))
James Ketrenose0d369d2005-09-21 11:54:30 -0500594 goto skip_host_crypt;
595
596 switch (ext->alg) {
597 case IW_ENCODE_ALG_WEP:
598 alg = "WEP";
599 module = "ieee80211_crypt_wep";
600 break;
601 case IW_ENCODE_ALG_TKIP:
602 alg = "TKIP";
603 module = "ieee80211_crypt_tkip";
604 break;
605 case IW_ENCODE_ALG_CCMP:
606 alg = "CCMP";
607 module = "ieee80211_crypt_ccmp";
608 break;
609 default:
610 IEEE80211_DEBUG_WX("%s: unknown crypto alg %d\n",
611 dev->name, ext->alg);
612 ret = -EINVAL;
613 goto done;
614 }
615
616 ops = ieee80211_get_crypto_ops(alg);
617 if (ops == NULL) {
618 request_module(module);
619 ops = ieee80211_get_crypto_ops(alg);
620 }
621 if (ops == NULL) {
622 IEEE80211_DEBUG_WX("%s: unknown crypto alg %d\n",
623 dev->name, ext->alg);
624 ret = -EINVAL;
625 goto done;
626 }
627
628 if (*crypt == NULL || (*crypt)->ops != ops) {
629 struct ieee80211_crypt_data *new_crypt;
630
631 ieee80211_crypt_delayed_deinit(ieee, crypt);
632
633 new_crypt = (struct ieee80211_crypt_data *)
634 kmalloc(sizeof(*new_crypt), GFP_KERNEL);
635 if (new_crypt == NULL) {
636 ret = -ENOMEM;
637 goto done;
638 }
639 memset(new_crypt, 0, sizeof(struct ieee80211_crypt_data));
640 new_crypt->ops = ops;
641 if (new_crypt->ops && try_module_get(new_crypt->ops->owner))
James Ketrenos6eb6edf2005-09-22 10:34:15 +0000642 new_crypt->priv = new_crypt->ops->init(idx);
James Ketrenose0d369d2005-09-21 11:54:30 -0500643 if (new_crypt->priv == NULL) {
644 kfree(new_crypt);
645 ret = -EINVAL;
646 goto done;
647 }
648 *crypt = new_crypt;
649 }
650
651 if (ext->key_len > 0 && (*crypt)->ops->set_key &&
652 (*crypt)->ops->set_key(ext->key, ext->key_len, ext->rx_seq,
653 (*crypt)->priv) < 0) {
654 IEEE80211_DEBUG_WX("%s: key setting failed\n", dev->name);
655 ret = -EINVAL;
656 goto done;
657 }
658
659 skip_host_crypt:
660 if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) {
661 ieee->tx_keyidx = idx;
662 sec.active_key = idx;
663 sec.flags |= SEC_ACTIVE_KEY;
664 }
665
666 if (ext->alg != IW_ENCODE_ALG_NONE) {
667 memcpy(sec.keys[idx], ext->key, ext->key_len);
668 sec.key_sizes[idx] = ext->key_len;
669 sec.flags |= (1 << idx);
670 if (ext->alg == IW_ENCODE_ALG_WEP) {
671 sec.encode_alg[idx] = SEC_ALG_WEP;
672 sec.flags |= SEC_LEVEL;
673 sec.level = SEC_LEVEL_1;
674 } else if (ext->alg == IW_ENCODE_ALG_TKIP) {
675 sec.encode_alg[idx] = SEC_ALG_TKIP;
676 sec.flags |= SEC_LEVEL;
677 sec.level = SEC_LEVEL_2;
678 } else if (ext->alg == IW_ENCODE_ALG_CCMP) {
679 sec.encode_alg[idx] = SEC_ALG_CCMP;
680 sec.flags |= SEC_LEVEL;
681 sec.level = SEC_LEVEL_3;
682 }
James Ketrenosccd0fda2005-09-21 11:58:32 -0500683 /* Don't set sec level for group keys. */
684 if (group_key)
685 sec.flags &= ~SEC_LEVEL;
James Ketrenose0d369d2005-09-21 11:54:30 -0500686 }
687 done:
688 if (ieee->set_security)
689 ieee->set_security(ieee->dev, &sec);
690
691 /*
692 * Do not reset port if card is in Managed mode since resetting will
693 * generate new IEEE 802.11 authentication which may end up in looping
694 * with IEEE 802.1X. If your hardware requires a reset after WEP
695 * configuration (for example... Prism2), implement the reset_port in
696 * the callbacks structures used to initialize the 802.11 stack.
697 */
698 if (ieee->reset_on_keychange &&
699 ieee->iw_mode != IW_MODE_INFRA &&
700 ieee->reset_port && ieee->reset_port(dev)) {
701 IEEE80211_DEBUG_WX("%s: reset_port failed\n", dev->name);
702 return -EINVAL;
703 }
704
705 return ret;
706}
707
708int ieee80211_wx_get_encodeext(struct ieee80211_device *ieee,
709 struct iw_request_info *info,
710 union iwreq_data *wrqu, char *extra)
711{
712 struct iw_point *encoding = &wrqu->encoding;
713 struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
714 struct ieee80211_security *sec = &ieee->sec;
715 int idx, max_key_len;
716
717 max_key_len = encoding->length - sizeof(*ext);
718 if (max_key_len < 0)
719 return -EINVAL;
720
721 idx = encoding->flags & IW_ENCODE_INDEX;
722 if (idx) {
723 if (idx < 1 || idx > WEP_KEYS)
724 return -EINVAL;
725 idx--;
726 } else
727 idx = ieee->tx_keyidx;
728
Volker Braune1892772005-10-24 10:15:36 -0500729 if (!ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY &&
730 ext->alg != IW_ENCODE_ALG_WEP)
James Ketrenose0d369d2005-09-21 11:54:30 -0500731 if (idx != 0 || ieee->iw_mode != IW_MODE_INFRA)
732 return -EINVAL;
733
734 encoding->flags = idx + 1;
735 memset(ext, 0, sizeof(*ext));
736
737 if (!sec->enabled) {
738 ext->alg = IW_ENCODE_ALG_NONE;
739 ext->key_len = 0;
740 encoding->flags |= IW_ENCODE_DISABLED;
741 } else {
742 if (sec->encode_alg[idx] == SEC_ALG_WEP)
743 ext->alg = IW_ENCODE_ALG_WEP;
744 else if (sec->encode_alg[idx] == SEC_ALG_TKIP)
745 ext->alg = IW_ENCODE_ALG_TKIP;
746 else if (sec->encode_alg[idx] == SEC_ALG_CCMP)
747 ext->alg = IW_ENCODE_ALG_CCMP;
748 else
749 return -EINVAL;
750
751 ext->key_len = sec->key_sizes[idx];
752 memcpy(ext->key, sec->keys[idx], ext->key_len);
753 encoding->flags |= IW_ENCODE_ENABLED;
754 if (ext->key_len &&
755 (ext->alg == IW_ENCODE_ALG_TKIP ||
756 ext->alg == IW_ENCODE_ALG_CCMP))
757 ext->ext_flags |= IW_ENCODE_EXT_TX_SEQ_VALID;
758
759 }
760
761 return 0;
762}
763
764EXPORT_SYMBOL(ieee80211_wx_set_encodeext);
765EXPORT_SYMBOL(ieee80211_wx_get_encodeext);
James Ketrenose0d369d2005-09-21 11:54:30 -0500766
Jeff Garzikb4538722005-05-12 22:48:20 -0400767EXPORT_SYMBOL(ieee80211_wx_get_scan);
768EXPORT_SYMBOL(ieee80211_wx_set_encode);
769EXPORT_SYMBOL(ieee80211_wx_get_encode);