blob: a78c4f845f66a88f3a2f95fb59adc8790c6af723 [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
Larry Fingerd94606e2006-03-03 16:21:55 -060045static char *ieee80211_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;
Zhu Yi09593042006-04-13 17:17:26 +080053 char *current_val; /* For rates */
54 u8 rate;
Jeff Garzikb4538722005-05-12 22:48:20 -040055
56 /* First entry *MUST* be the AP MAC address */
57 iwe.cmd = SIOCGIWAP;
58 iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
59 memcpy(iwe.u.ap_addr.sa_data, network->bssid, ETH_ALEN);
60 start = iwe_stream_add_event(start, stop, &iwe, IW_EV_ADDR_LEN);
61
62 /* Remaining entries will be displayed in the order we provide them */
63
64 /* Add the ESSID */
65 iwe.cmd = SIOCGIWESSID;
66 iwe.u.data.flags = 1;
67 if (network->flags & NETWORK_EMPTY_ESSID) {
68 iwe.u.data.length = sizeof("<hidden>");
69 start = iwe_stream_add_point(start, stop, &iwe, "<hidden>");
70 } else {
Jeff Garzik0edd5b42005-09-07 00:48:31 -040071 iwe.u.data.length = min(network->ssid_len, (u8) 32);
Jeff Garzikb4538722005-05-12 22:48:20 -040072 start = iwe_stream_add_point(start, stop, &iwe, network->ssid);
73 }
74
75 /* Add the protocol name */
76 iwe.cmd = SIOCGIWNAME;
Jeff Garzik0edd5b42005-09-07 00:48:31 -040077 snprintf(iwe.u.name, IFNAMSIZ, "IEEE 802.11%s",
78 ieee80211_modes[network->mode]);
Jeff Garzikb4538722005-05-12 22:48:20 -040079 start = iwe_stream_add_event(start, stop, &iwe, IW_EV_CHAR_LEN);
80
Jeff Garzik0edd5b42005-09-07 00:48:31 -040081 /* Add mode */
82 iwe.cmd = SIOCGIWMODE;
83 if (network->capability & (WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS)) {
Jeff Garzik1b5cca32005-08-15 00:32:15 -040084 if (network->capability & WLAN_CAPABILITY_ESS)
Jeff Garzikb4538722005-05-12 22:48:20 -040085 iwe.u.mode = IW_MODE_MASTER;
86 else
87 iwe.u.mode = IW_MODE_ADHOC;
88
Jeff Garzik0edd5b42005-09-07 00:48:31 -040089 start = iwe_stream_add_event(start, stop, &iwe, IW_EV_UINT_LEN);
Jeff Garzikb4538722005-05-12 22:48:20 -040090 }
91
Jeff Garzik0edd5b42005-09-07 00:48:31 -040092 /* Add frequency/channel */
Jeff Garzikb4538722005-05-12 22:48:20 -040093 iwe.cmd = SIOCGIWFREQ;
94/* iwe.u.freq.m = ieee80211_frequency(network->channel, network->mode);
95 iwe.u.freq.e = 3; */
96 iwe.u.freq.m = network->channel;
97 iwe.u.freq.e = 0;
98 iwe.u.freq.i = 0;
99 start = iwe_stream_add_event(start, stop, &iwe, IW_EV_FREQ_LEN);
100
101 /* Add encryption capability */
102 iwe.cmd = SIOCGIWENCODE;
103 if (network->capability & WLAN_CAPABILITY_PRIVACY)
104 iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
105 else
106 iwe.u.data.flags = IW_ENCODE_DISABLED;
107 iwe.u.data.length = 0;
108 start = iwe_stream_add_point(start, stop, &iwe, network->ssid);
109
110 /* Add basic and extended rates */
Zhu Yi09593042006-04-13 17:17:26 +0800111 /* Rate : stuffing multiple values in a single event require a bit
112 * more of magic - Jean II */
113 current_val = start + IW_EV_LCP_LEN;
114 iwe.cmd = SIOCGIWRATE;
115 /* Those two flags are ignored... */
116 iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0;
117
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400118 for (i = 0, j = 0; i < network->rates_len;) {
Jeff Garzikb4538722005-05-12 22:48:20 -0400119 if (j < network->rates_ex_len &&
120 ((network->rates_ex[j] & 0x7F) <
121 (network->rates[i] & 0x7F)))
122 rate = network->rates_ex[j++] & 0x7F;
123 else
124 rate = network->rates[i++] & 0x7F;
Zhu Yi09593042006-04-13 17:17:26 +0800125 /* Bit rate given in 500 kb/s units (+ 0x80) */
126 iwe.u.bitrate.value = ((rate & 0x7f) * 500000);
127 /* Add new value to event */
128 current_val = iwe_stream_add_value(start, current_val, stop, &iwe, IW_EV_PARAM_LEN);
Jeff Garzikb4538722005-05-12 22:48:20 -0400129 }
130 for (; j < network->rates_ex_len; j++) {
131 rate = network->rates_ex[j] & 0x7F;
Zhu Yi09593042006-04-13 17:17:26 +0800132 /* Bit rate given in 500 kb/s units (+ 0x80) */
133 iwe.u.bitrate.value = ((rate & 0x7f) * 500000);
134 /* Add new value to event */
135 current_val = iwe_stream_add_value(start, current_val, stop, &iwe, IW_EV_PARAM_LEN);
Jeff Garzikb4538722005-05-12 22:48:20 -0400136 }
Zhu Yi09593042006-04-13 17:17:26 +0800137 /* Check if we added any rate */
138 if((current_val - start) > IW_EV_LCP_LEN)
139 start = current_val;
Jeff Garzikb4538722005-05-12 22:48:20 -0400140
141 /* Add quality statistics */
Jeff Garzikb4538722005-05-12 22:48:20 -0400142 iwe.cmd = IWEVQUAL;
James Ketrenosb1b508e2005-09-13 17:27:19 -0500143 iwe.u.qual.updated = IW_QUAL_QUAL_UPDATED | IW_QUAL_LEVEL_UPDATED |
144 IW_QUAL_NOISE_UPDATED;
145
146 if (!(network->stats.mask & IEEE80211_STATMASK_RSSI)) {
147 iwe.u.qual.updated |= IW_QUAL_QUAL_INVALID |
148 IW_QUAL_LEVEL_INVALID;
149 iwe.u.qual.qual = 0;
James Ketrenosb1b508e2005-09-13 17:27:19 -0500150 } else {
Jiri Benc757d18f2005-10-10 19:16:53 +0200151 if (ieee->perfect_rssi == ieee->worst_rssi)
152 iwe.u.qual.qual = 100;
153 else
154 iwe.u.qual.qual =
155 (100 *
156 (ieee->perfect_rssi - ieee->worst_rssi) *
157 (ieee->perfect_rssi - ieee->worst_rssi) -
158 (ieee->perfect_rssi - network->stats.rssi) *
159 (15 * (ieee->perfect_rssi - ieee->worst_rssi) +
James Ketrenos81f87522005-10-24 10:20:53 -0500160 62 * (ieee->perfect_rssi -
161 network->stats.rssi))) /
162 ((ieee->perfect_rssi -
163 ieee->worst_rssi) * (ieee->perfect_rssi -
164 ieee->worst_rssi));
James Ketrenosb1b508e2005-09-13 17:27:19 -0500165 if (iwe.u.qual.qual > 100)
166 iwe.u.qual.qual = 100;
167 else if (iwe.u.qual.qual < 1)
168 iwe.u.qual.qual = 0;
169 }
170
171 if (!(network->stats.mask & IEEE80211_STATMASK_NOISE)) {
Jeff Garzikb4538722005-05-12 22:48:20 -0400172 iwe.u.qual.updated |= IW_QUAL_NOISE_INVALID;
James Ketrenosb1b508e2005-09-13 17:27:19 -0500173 iwe.u.qual.noise = 0;
174 } else {
175 iwe.u.qual.noise = network->stats.noise;
176 }
Jeff Garzikb4538722005-05-12 22:48:20 -0400177
Zhu Yi7bd64362006-01-19 16:21:54 +0800178 if (!(network->stats.mask & IEEE80211_STATMASK_SIGNAL)) {
179 iwe.u.qual.updated |= IW_QUAL_LEVEL_INVALID;
180 iwe.u.qual.level = 0;
181 } else {
182 iwe.u.qual.level = network->stats.signal;
183 }
184
Jeff Garzikb4538722005-05-12 22:48:20 -0400185 start = iwe_stream_add_event(start, stop, &iwe, IW_EV_QUAL_LEN);
186
187 iwe.cmd = IWEVCUSTOM;
188 p = custom;
189
190 iwe.u.data.length = p - custom;
191 if (iwe.u.data.length)
192 start = iwe_stream_add_point(start, stop, &iwe, custom);
193
Zhu Yi47168082006-02-13 13:37:03 +0800194 memset(&iwe, 0, sizeof(iwe));
James Ketrenos20d64712005-09-21 11:53:43 -0500195 if (network->wpa_ie_len) {
Zhu Yi47168082006-02-13 13:37:03 +0800196 char buf[MAX_WPA_IE_LEN];
197 memcpy(buf, network->wpa_ie, network->wpa_ie_len);
198 iwe.cmd = IWEVGENIE;
199 iwe.u.data.length = network->wpa_ie_len;
Jeff Garzikb4538722005-05-12 22:48:20 -0400200 start = iwe_stream_add_point(start, stop, &iwe, buf);
201 }
202
Zhu Yi47168082006-02-13 13:37:03 +0800203 memset(&iwe, 0, sizeof(iwe));
James Ketrenos20d64712005-09-21 11:53:43 -0500204 if (network->rsn_ie_len) {
Zhu Yi47168082006-02-13 13:37:03 +0800205 char buf[MAX_WPA_IE_LEN];
206 memcpy(buf, network->rsn_ie, network->rsn_ie_len);
207 iwe.cmd = IWEVGENIE;
208 iwe.u.data.length = network->rsn_ie_len;
Jeff Garzikb4538722005-05-12 22:48:20 -0400209 start = iwe_stream_add_point(start, stop, &iwe, buf);
210 }
211
212 /* Add EXTRA: Age to display seconds since last beacon/probe response
213 * for given network. */
214 iwe.cmd = IWEVCUSTOM;
215 p = custom;
216 p += snprintf(p, MAX_CUSTOM_LEN - (p - custom),
James Ketrenos42e349f2005-09-21 11:54:07 -0500217 " Last beacon: %dms ago",
218 jiffies_to_msecs(jiffies - network->last_scanned));
Jeff Garzikb4538722005-05-12 22:48:20 -0400219 iwe.u.data.length = p - custom;
220 if (iwe.u.data.length)
221 start = iwe_stream_add_point(start, stop, &iwe, custom);
222
Zhu Yi7bd64362006-01-19 16:21:54 +0800223 /* Add spectrum management information */
224 iwe.cmd = -1;
225 p = custom;
226 p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), " Channel flags: ");
227
228 if (ieee80211_get_channel_flags(ieee, network->channel) &
229 IEEE80211_CH_INVALID) {
230 iwe.cmd = IWEVCUSTOM;
231 p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), "INVALID ");
232 }
233
234 if (ieee80211_get_channel_flags(ieee, network->channel) &
235 IEEE80211_CH_RADAR_DETECT) {
236 iwe.cmd = IWEVCUSTOM;
237 p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), "DFS ");
238 }
239
240 if (iwe.cmd == IWEVCUSTOM) {
241 iwe.u.data.length = p - custom;
242 start = iwe_stream_add_point(start, stop, &iwe, custom);
243 }
244
Jeff Garzikb4538722005-05-12 22:48:20 -0400245 return start;
246}
247
Zhu Yi55cd94a2006-01-19 16:20:59 +0800248#define SCAN_ITEM_SIZE 128
249
Jeff Garzikb4538722005-05-12 22:48:20 -0400250int ieee80211_wx_get_scan(struct ieee80211_device *ieee,
251 struct iw_request_info *info,
252 union iwreq_data *wrqu, char *extra)
253{
254 struct ieee80211_network *network;
255 unsigned long flags;
Zhu Yi55cd94a2006-01-19 16:20:59 +0800256 int err = 0;
Jeff Garzikb4538722005-05-12 22:48:20 -0400257
258 char *ev = extra;
Zhu Yi55cd94a2006-01-19 16:20:59 +0800259 char *stop = ev + wrqu->data.length;
Jeff Garzikb4538722005-05-12 22:48:20 -0400260 int i = 0;
261
262 IEEE80211_DEBUG_WX("Getting scan\n");
263
264 spin_lock_irqsave(&ieee->lock, flags);
265
266 list_for_each_entry(network, &ieee->network_list, list) {
267 i++;
Zhu Yi55cd94a2006-01-19 16:20:59 +0800268 if (stop - ev < SCAN_ITEM_SIZE) {
269 err = -E2BIG;
270 break;
271 }
272
Jeff Garzikb4538722005-05-12 22:48:20 -0400273 if (ieee->scan_age == 0 ||
274 time_after(network->last_scanned + ieee->scan_age, jiffies))
Larry Fingerd94606e2006-03-03 16:21:55 -0600275 ev = ieee80211_translate_scan(ieee, ev, stop, network);
Jeff Garzikb4538722005-05-12 22:48:20 -0400276 else
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400277 IEEE80211_DEBUG_SCAN("Not showing network '%s ("
James Ketrenos42e349f2005-09-21 11:54:07 -0500278 MAC_FMT ")' due to age (%dms).\n",
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400279 escape_essid(network->ssid,
280 network->ssid_len),
281 MAC_ARG(network->bssid),
James Ketrenos42e349f2005-09-21 11:54:07 -0500282 jiffies_to_msecs(jiffies -
283 network->
284 last_scanned));
Jeff Garzikb4538722005-05-12 22:48:20 -0400285 }
286
287 spin_unlock_irqrestore(&ieee->lock, flags);
288
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400289 wrqu->data.length = ev - extra;
Jeff Garzikb4538722005-05-12 22:48:20 -0400290 wrqu->data.flags = 0;
291
292 IEEE80211_DEBUG_WX("exit: %d networks returned.\n", i);
293
Zhu Yi55cd94a2006-01-19 16:20:59 +0800294 return err;
Jeff Garzikb4538722005-05-12 22:48:20 -0400295}
296
297int ieee80211_wx_set_encode(struct ieee80211_device *ieee,
298 struct iw_request_info *info,
299 union iwreq_data *wrqu, char *keybuf)
300{
301 struct iw_point *erq = &(wrqu->encoding);
302 struct net_device *dev = ieee->dev;
303 struct ieee80211_security sec = {
304 .flags = 0
305 };
306 int i, key, key_provided, len;
307 struct ieee80211_crypt_data **crypt;
Johannes Berga4bf26f2005-12-31 11:35:20 +0100308 int host_crypto = ieee->host_encrypt || ieee->host_decrypt || ieee->host_build_iv;
Jeff Garzikb4538722005-05-12 22:48:20 -0400309
310 IEEE80211_DEBUG_WX("SET_ENCODE\n");
311
312 key = erq->flags & IW_ENCODE_INDEX;
313 if (key) {
314 if (key > WEP_KEYS)
315 return -EINVAL;
316 key--;
317 key_provided = 1;
318 } else {
319 key_provided = 0;
320 key = ieee->tx_keyidx;
321 }
322
323 IEEE80211_DEBUG_WX("Key: %d [%s]\n", key, key_provided ?
324 "provided" : "default");
325
326 crypt = &ieee->crypt[key];
327
328 if (erq->flags & IW_ENCODE_DISABLED) {
329 if (key_provided && *crypt) {
330 IEEE80211_DEBUG_WX("Disabling encryption on key %d.\n",
331 key);
332 ieee80211_crypt_delayed_deinit(ieee, crypt);
333 } else
334 IEEE80211_DEBUG_WX("Disabling encryption.\n");
335
336 /* Check all the keys to see if any are still configured,
337 * and if no key index was provided, de-init them all */
338 for (i = 0; i < WEP_KEYS; i++) {
339 if (ieee->crypt[i] != NULL) {
340 if (key_provided)
341 break;
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400342 ieee80211_crypt_delayed_deinit(ieee,
343 &ieee->crypt[i]);
Jeff Garzikb4538722005-05-12 22:48:20 -0400344 }
345 }
346
347 if (i == WEP_KEYS) {
348 sec.enabled = 0;
James Ketrenosf1bf6632005-09-21 11:53:54 -0500349 sec.encrypt = 0;
Jeff Garzikb4538722005-05-12 22:48:20 -0400350 sec.level = SEC_LEVEL_0;
James Ketrenos259bf1f2005-09-21 11:54:22 -0500351 sec.flags |= SEC_ENABLED | SEC_LEVEL | SEC_ENCRYPT;
Jeff Garzikb4538722005-05-12 22:48:20 -0400352 }
353
354 goto done;
355 }
356
Jeff Garzikb4538722005-05-12 22:48:20 -0400357 sec.enabled = 1;
James Ketrenosf1bf6632005-09-21 11:53:54 -0500358 sec.encrypt = 1;
James Ketrenos259bf1f2005-09-21 11:54:22 -0500359 sec.flags |= SEC_ENABLED | SEC_ENCRYPT;
Jeff Garzikb4538722005-05-12 22:48:20 -0400360
361 if (*crypt != NULL && (*crypt)->ops != NULL &&
362 strcmp((*crypt)->ops->name, "WEP") != 0) {
363 /* changing to use WEP; deinit previously used algorithm
364 * on this key */
365 ieee80211_crypt_delayed_deinit(ieee, crypt);
366 }
367
James Ketrenosf1bf6632005-09-21 11:53:54 -0500368 if (*crypt == NULL && host_crypto) {
Jeff Garzikb4538722005-05-12 22:48:20 -0400369 struct ieee80211_crypt_data *new_crypt;
370
371 /* take WEP into use */
372 new_crypt = kmalloc(sizeof(struct ieee80211_crypt_data),
373 GFP_KERNEL);
374 if (new_crypt == NULL)
375 return -ENOMEM;
376 memset(new_crypt, 0, sizeof(struct ieee80211_crypt_data));
377 new_crypt->ops = ieee80211_get_crypto_ops("WEP");
378 if (!new_crypt->ops) {
379 request_module("ieee80211_crypt_wep");
380 new_crypt->ops = ieee80211_get_crypto_ops("WEP");
381 }
382
383 if (new_crypt->ops && try_module_get(new_crypt->ops->owner))
James Ketrenos6eb6edf2005-09-22 10:34:15 +0000384 new_crypt->priv = new_crypt->ops->init(key);
Jeff Garzikb4538722005-05-12 22:48:20 -0400385
386 if (!new_crypt->ops || !new_crypt->priv) {
387 kfree(new_crypt);
388 new_crypt = NULL;
389
390 printk(KERN_WARNING "%s: could not initialize WEP: "
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400391 "load module ieee80211_crypt_wep\n", dev->name);
Jeff Garzikb4538722005-05-12 22:48:20 -0400392 return -EOPNOTSUPP;
393 }
394 *crypt = new_crypt;
395 }
396
397 /* If a new key was provided, set it up */
398 if (erq->length > 0) {
399 len = erq->length <= 5 ? 5 : 13;
400 memcpy(sec.keys[key], keybuf, erq->length);
401 if (len > erq->length)
402 memset(sec.keys[key] + erq->length, 0,
403 len - erq->length);
404 IEEE80211_DEBUG_WX("Setting key %d to '%s' (%d:%d bytes)\n",
405 key, escape_essid(sec.keys[key], len),
406 erq->length, len);
407 sec.key_sizes[key] = len;
James Ketrenosf1bf6632005-09-21 11:53:54 -0500408 if (*crypt)
409 (*crypt)->ops->set_key(sec.keys[key], len, NULL,
410 (*crypt)->priv);
Jeff Garzikb4538722005-05-12 22:48:20 -0400411 sec.flags |= (1 << key);
412 /* This ensures a key will be activated if no key is
413 * explicitely set */
414 if (key == sec.active_key)
415 sec.flags |= SEC_ACTIVE_KEY;
Jeff Garzikb4538722005-05-12 22:48:20 -0400416
James Ketrenosf1bf6632005-09-21 11:53:54 -0500417 } else {
418 if (host_crypto) {
419 len = (*crypt)->ops->get_key(sec.keys[key], WEP_KEY_LEN,
420 NULL, (*crypt)->priv);
421 if (len == 0) {
422 /* Set a default key of all 0 */
423 IEEE80211_DEBUG_WX("Setting key %d to all "
424 "zero.\n", key);
425 memset(sec.keys[key], 0, 13);
426 (*crypt)->ops->set_key(sec.keys[key], 13, NULL,
427 (*crypt)->priv);
428 sec.key_sizes[key] = 13;
429 sec.flags |= (1 << key);
430 }
431 }
Jeff Garzikb4538722005-05-12 22:48:20 -0400432 /* No key data - just set the default TX key index */
433 if (key_provided) {
James Ketrenosf1bf6632005-09-21 11:53:54 -0500434 IEEE80211_DEBUG_WX("Setting key %d to default Tx "
435 "key.\n", key);
Jeff Garzikb4538722005-05-12 22:48:20 -0400436 ieee->tx_keyidx = key;
437 sec.active_key = key;
438 sec.flags |= SEC_ACTIVE_KEY;
439 }
440 }
James Ketrenos7dc888f2005-09-21 11:58:38 -0500441 if (erq->flags & (IW_ENCODE_OPEN | IW_ENCODE_RESTRICTED)) {
442 ieee->open_wep = !(erq->flags & IW_ENCODE_RESTRICTED);
443 sec.auth_mode = ieee->open_wep ? WLAN_AUTH_OPEN :
444 WLAN_AUTH_SHARED_KEY;
445 sec.flags |= SEC_AUTH_MODE;
446 IEEE80211_DEBUG_WX("Auth: %s\n",
447 sec.auth_mode == WLAN_AUTH_OPEN ?
448 "OPEN" : "SHARED KEY");
449 }
Jeff Garzikb4538722005-05-12 22:48:20 -0400450
451 /* For now we just support WEP, so only set that security level...
452 * TODO: When WPA is added this is one place that needs to change */
453 sec.flags |= SEC_LEVEL;
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400454 sec.level = SEC_LEVEL_1; /* 40 and 104 bit WEP */
James Ketrenose0d369d2005-09-21 11:54:30 -0500455 sec.encode_alg[key] = SEC_ALG_WEP;
Jeff Garzikb4538722005-05-12 22:48:20 -0400456
James Ketrenos259bf1f2005-09-21 11:54:22 -0500457 done:
Jeff Garzikb4538722005-05-12 22:48:20 -0400458 if (ieee->set_security)
459 ieee->set_security(dev, &sec);
460
461 /* Do not reset port if card is in Managed mode since resetting will
462 * generate new IEEE 802.11 authentication which may end up in looping
463 * with IEEE 802.1X. If your hardware requires a reset after WEP
464 * configuration (for example... Prism2), implement the reset_port in
465 * the callbacks structures used to initialize the 802.11 stack. */
466 if (ieee->reset_on_keychange &&
467 ieee->iw_mode != IW_MODE_INFRA &&
468 ieee->reset_port && ieee->reset_port(dev)) {
469 printk(KERN_DEBUG "%s: reset_port failed\n", dev->name);
470 return -EINVAL;
471 }
472 return 0;
473}
474
475int ieee80211_wx_get_encode(struct ieee80211_device *ieee,
476 struct iw_request_info *info,
477 union iwreq_data *wrqu, char *keybuf)
478{
479 struct iw_point *erq = &(wrqu->encoding);
480 int len, key;
481 struct ieee80211_crypt_data *crypt;
James Ketrenosf1bf6632005-09-21 11:53:54 -0500482 struct ieee80211_security *sec = &ieee->sec;
Jeff Garzikb4538722005-05-12 22:48:20 -0400483
484 IEEE80211_DEBUG_WX("GET_ENCODE\n");
485
486 key = erq->flags & IW_ENCODE_INDEX;
487 if (key) {
488 if (key > WEP_KEYS)
489 return -EINVAL;
490 key--;
491 } else
492 key = ieee->tx_keyidx;
493
494 crypt = ieee->crypt[key];
495 erq->flags = key + 1;
496
James Ketrenosf1bf6632005-09-21 11:53:54 -0500497 if (!sec->enabled) {
Jeff Garzikb4538722005-05-12 22:48:20 -0400498 erq->length = 0;
499 erq->flags |= IW_ENCODE_DISABLED;
500 return 0;
501 }
502
James Ketrenosf1bf6632005-09-21 11:53:54 -0500503 len = sec->key_sizes[key];
504 memcpy(keybuf, sec->keys[key], len);
Jeff Garzikb4538722005-05-12 22:48:20 -0400505
Adrian Bunk62741152006-04-27 02:33:42 -0700506 erq->length = len;
Jeff Garzikb4538722005-05-12 22:48:20 -0400507 erq->flags |= IW_ENCODE_ENABLED;
508
509 if (ieee->open_wep)
510 erq->flags |= IW_ENCODE_OPEN;
511 else
512 erq->flags |= IW_ENCODE_RESTRICTED;
513
514 return 0;
515}
516
James Ketrenose0d369d2005-09-21 11:54:30 -0500517int ieee80211_wx_set_encodeext(struct ieee80211_device *ieee,
518 struct iw_request_info *info,
519 union iwreq_data *wrqu, char *extra)
520{
521 struct net_device *dev = ieee->dev;
522 struct iw_point *encoding = &wrqu->encoding;
523 struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
524 int i, idx, ret = 0;
James Ketrenosccd0fda2005-09-21 11:58:32 -0500525 int group_key = 0;
James Ketrenose0d369d2005-09-21 11:54:30 -0500526 const char *alg, *module;
527 struct ieee80211_crypto_ops *ops;
528 struct ieee80211_crypt_data **crypt;
529
530 struct ieee80211_security sec = {
531 .flags = 0,
532 };
533
534 idx = encoding->flags & IW_ENCODE_INDEX;
535 if (idx) {
536 if (idx < 1 || idx > WEP_KEYS)
537 return -EINVAL;
538 idx--;
539 } else
540 idx = ieee->tx_keyidx;
541
James Ketrenosccd0fda2005-09-21 11:58:32 -0500542 if (ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) {
James Ketrenose0d369d2005-09-21 11:54:30 -0500543 crypt = &ieee->crypt[idx];
James Ketrenosccd0fda2005-09-21 11:58:32 -0500544 group_key = 1;
545 } else {
Volker Braune1892772005-10-24 10:15:36 -0500546 /* some Cisco APs use idx>0 for unicast in dynamic WEP */
547 if (idx != 0 && ext->alg != IW_ENCODE_ALG_WEP)
James Ketrenose0d369d2005-09-21 11:54:30 -0500548 return -EINVAL;
549 if (ieee->iw_mode == IW_MODE_INFRA)
550 crypt = &ieee->crypt[idx];
551 else
552 return -EINVAL;
553 }
554
555 sec.flags |= SEC_ENABLED | SEC_ENCRYPT;
556 if ((encoding->flags & IW_ENCODE_DISABLED) ||
557 ext->alg == IW_ENCODE_ALG_NONE) {
558 if (*crypt)
559 ieee80211_crypt_delayed_deinit(ieee, crypt);
560
561 for (i = 0; i < WEP_KEYS; i++)
562 if (ieee->crypt[i] != NULL)
563 break;
564
565 if (i == WEP_KEYS) {
566 sec.enabled = 0;
567 sec.encrypt = 0;
568 sec.level = SEC_LEVEL_0;
569 sec.flags |= SEC_LEVEL;
570 }
571 goto done;
572 }
573
574 sec.enabled = 1;
575 sec.encrypt = 1;
576
James Ketrenosccd0fda2005-09-21 11:58:32 -0500577 if (group_key ? !ieee->host_mc_decrypt :
578 !(ieee->host_encrypt || ieee->host_decrypt ||
579 ieee->host_encrypt_msdu))
James Ketrenose0d369d2005-09-21 11:54:30 -0500580 goto skip_host_crypt;
581
582 switch (ext->alg) {
583 case IW_ENCODE_ALG_WEP:
584 alg = "WEP";
585 module = "ieee80211_crypt_wep";
586 break;
587 case IW_ENCODE_ALG_TKIP:
588 alg = "TKIP";
589 module = "ieee80211_crypt_tkip";
590 break;
591 case IW_ENCODE_ALG_CCMP:
592 alg = "CCMP";
593 module = "ieee80211_crypt_ccmp";
594 break;
595 default:
596 IEEE80211_DEBUG_WX("%s: unknown crypto alg %d\n",
597 dev->name, ext->alg);
598 ret = -EINVAL;
599 goto done;
600 }
601
602 ops = ieee80211_get_crypto_ops(alg);
603 if (ops == NULL) {
604 request_module(module);
605 ops = ieee80211_get_crypto_ops(alg);
606 }
607 if (ops == NULL) {
608 IEEE80211_DEBUG_WX("%s: unknown crypto alg %d\n",
609 dev->name, ext->alg);
610 ret = -EINVAL;
611 goto done;
612 }
613
614 if (*crypt == NULL || (*crypt)->ops != ops) {
615 struct ieee80211_crypt_data *new_crypt;
616
617 ieee80211_crypt_delayed_deinit(ieee, crypt);
618
619 new_crypt = (struct ieee80211_crypt_data *)
620 kmalloc(sizeof(*new_crypt), GFP_KERNEL);
621 if (new_crypt == NULL) {
622 ret = -ENOMEM;
623 goto done;
624 }
625 memset(new_crypt, 0, sizeof(struct ieee80211_crypt_data));
626 new_crypt->ops = ops;
627 if (new_crypt->ops && try_module_get(new_crypt->ops->owner))
James Ketrenos6eb6edf2005-09-22 10:34:15 +0000628 new_crypt->priv = new_crypt->ops->init(idx);
James Ketrenose0d369d2005-09-21 11:54:30 -0500629 if (new_crypt->priv == NULL) {
630 kfree(new_crypt);
631 ret = -EINVAL;
632 goto done;
633 }
634 *crypt = new_crypt;
635 }
636
637 if (ext->key_len > 0 && (*crypt)->ops->set_key &&
638 (*crypt)->ops->set_key(ext->key, ext->key_len, ext->rx_seq,
639 (*crypt)->priv) < 0) {
640 IEEE80211_DEBUG_WX("%s: key setting failed\n", dev->name);
641 ret = -EINVAL;
642 goto done;
643 }
644
645 skip_host_crypt:
646 if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) {
647 ieee->tx_keyidx = idx;
648 sec.active_key = idx;
649 sec.flags |= SEC_ACTIVE_KEY;
650 }
651
652 if (ext->alg != IW_ENCODE_ALG_NONE) {
653 memcpy(sec.keys[idx], ext->key, ext->key_len);
654 sec.key_sizes[idx] = ext->key_len;
655 sec.flags |= (1 << idx);
656 if (ext->alg == IW_ENCODE_ALG_WEP) {
657 sec.encode_alg[idx] = SEC_ALG_WEP;
658 sec.flags |= SEC_LEVEL;
659 sec.level = SEC_LEVEL_1;
660 } else if (ext->alg == IW_ENCODE_ALG_TKIP) {
661 sec.encode_alg[idx] = SEC_ALG_TKIP;
662 sec.flags |= SEC_LEVEL;
663 sec.level = SEC_LEVEL_2;
664 } else if (ext->alg == IW_ENCODE_ALG_CCMP) {
665 sec.encode_alg[idx] = SEC_ALG_CCMP;
666 sec.flags |= SEC_LEVEL;
667 sec.level = SEC_LEVEL_3;
668 }
James Ketrenosccd0fda2005-09-21 11:58:32 -0500669 /* Don't set sec level for group keys. */
670 if (group_key)
671 sec.flags &= ~SEC_LEVEL;
James Ketrenose0d369d2005-09-21 11:54:30 -0500672 }
673 done:
674 if (ieee->set_security)
675 ieee->set_security(ieee->dev, &sec);
676
677 /*
678 * Do not reset port if card is in Managed mode since resetting will
679 * generate new IEEE 802.11 authentication which may end up in looping
680 * with IEEE 802.1X. If your hardware requires a reset after WEP
681 * configuration (for example... Prism2), implement the reset_port in
682 * the callbacks structures used to initialize the 802.11 stack.
683 */
684 if (ieee->reset_on_keychange &&
685 ieee->iw_mode != IW_MODE_INFRA &&
686 ieee->reset_port && ieee->reset_port(dev)) {
687 IEEE80211_DEBUG_WX("%s: reset_port failed\n", dev->name);
688 return -EINVAL;
689 }
690
691 return ret;
692}
693
694int ieee80211_wx_get_encodeext(struct ieee80211_device *ieee,
695 struct iw_request_info *info,
696 union iwreq_data *wrqu, char *extra)
697{
698 struct iw_point *encoding = &wrqu->encoding;
699 struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
700 struct ieee80211_security *sec = &ieee->sec;
701 int idx, max_key_len;
702
703 max_key_len = encoding->length - sizeof(*ext);
704 if (max_key_len < 0)
705 return -EINVAL;
706
707 idx = encoding->flags & IW_ENCODE_INDEX;
708 if (idx) {
709 if (idx < 1 || idx > WEP_KEYS)
710 return -EINVAL;
711 idx--;
712 } else
713 idx = ieee->tx_keyidx;
714
Volker Braune1892772005-10-24 10:15:36 -0500715 if (!ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY &&
716 ext->alg != IW_ENCODE_ALG_WEP)
James Ketrenose0d369d2005-09-21 11:54:30 -0500717 if (idx != 0 || ieee->iw_mode != IW_MODE_INFRA)
718 return -EINVAL;
719
720 encoding->flags = idx + 1;
721 memset(ext, 0, sizeof(*ext));
722
723 if (!sec->enabled) {
724 ext->alg = IW_ENCODE_ALG_NONE;
725 ext->key_len = 0;
726 encoding->flags |= IW_ENCODE_DISABLED;
727 } else {
728 if (sec->encode_alg[idx] == SEC_ALG_WEP)
729 ext->alg = IW_ENCODE_ALG_WEP;
730 else if (sec->encode_alg[idx] == SEC_ALG_TKIP)
731 ext->alg = IW_ENCODE_ALG_TKIP;
732 else if (sec->encode_alg[idx] == SEC_ALG_CCMP)
733 ext->alg = IW_ENCODE_ALG_CCMP;
734 else
735 return -EINVAL;
736
737 ext->key_len = sec->key_sizes[idx];
738 memcpy(ext->key, sec->keys[idx], ext->key_len);
739 encoding->flags |= IW_ENCODE_ENABLED;
740 if (ext->key_len &&
741 (ext->alg == IW_ENCODE_ALG_TKIP ||
742 ext->alg == IW_ENCODE_ALG_CCMP))
743 ext->ext_flags |= IW_ENCODE_EXT_TX_SEQ_VALID;
744
745 }
746
747 return 0;
748}
749
Larry Fingerdd5eeb42006-01-30 13:12:50 +0100750int ieee80211_wx_set_auth(struct net_device *dev,
751 struct iw_request_info *info,
752 union iwreq_data *wrqu,
753 char *extra)
754{
755 struct ieee80211_device *ieee = netdev_priv(dev);
756 unsigned long flags;
757 int err = 0;
758
759 spin_lock_irqsave(&ieee->lock, flags);
760
761 switch (wrqu->param.flags & IW_AUTH_INDEX) {
762 case IW_AUTH_WPA_VERSION:
763 case IW_AUTH_CIPHER_PAIRWISE:
764 case IW_AUTH_CIPHER_GROUP:
765 case IW_AUTH_KEY_MGMT:
766 /*
767 * Host AP driver does not use these parameters and allows
768 * wpa_supplicant to control them internally.
769 */
770 break;
771 case IW_AUTH_TKIP_COUNTERMEASURES:
772 break; /* FIXME */
773 case IW_AUTH_DROP_UNENCRYPTED:
774 ieee->drop_unencrypted = !!wrqu->param.value;
775 break;
776 case IW_AUTH_80211_AUTH_ALG:
777 break; /* FIXME */
778 case IW_AUTH_WPA_ENABLED:
779 ieee->privacy_invoked = ieee->wpa_enabled = !!wrqu->param.value;
780 break;
781 case IW_AUTH_RX_UNENCRYPTED_EAPOL:
782 ieee->ieee802_1x = !!wrqu->param.value;
783 break;
784 case IW_AUTH_PRIVACY_INVOKED:
785 ieee->privacy_invoked = !!wrqu->param.value;
786 break;
787 default:
788 err = -EOPNOTSUPP;
789 break;
790 }
791 spin_unlock_irqrestore(&ieee->lock, flags);
792 return err;
793}
794
795int ieee80211_wx_get_auth(struct net_device *dev,
796 struct iw_request_info *info,
797 union iwreq_data *wrqu,
798 char *extra)
799{
800 struct ieee80211_device *ieee = netdev_priv(dev);
801 unsigned long flags;
802 int err = 0;
803
804 spin_lock_irqsave(&ieee->lock, flags);
805
806 switch (wrqu->param.flags & IW_AUTH_INDEX) {
807 case IW_AUTH_WPA_VERSION:
808 case IW_AUTH_CIPHER_PAIRWISE:
809 case IW_AUTH_CIPHER_GROUP:
810 case IW_AUTH_KEY_MGMT:
811 case IW_AUTH_TKIP_COUNTERMEASURES: /* FIXME */
812 case IW_AUTH_80211_AUTH_ALG: /* FIXME */
813 /*
814 * Host AP driver does not use these parameters and allows
815 * wpa_supplicant to control them internally.
816 */
817 err = -EOPNOTSUPP;
818 break;
819 case IW_AUTH_DROP_UNENCRYPTED:
820 wrqu->param.value = ieee->drop_unencrypted;
821 break;
822 case IW_AUTH_WPA_ENABLED:
823 wrqu->param.value = ieee->wpa_enabled;
824 break;
825 case IW_AUTH_RX_UNENCRYPTED_EAPOL:
826 wrqu->param.value = ieee->ieee802_1x;
827 break;
828 default:
829 err = -EOPNOTSUPP;
830 break;
831 }
832 spin_unlock_irqrestore(&ieee->lock, flags);
833 return err;
834}
835
James Ketrenose0d369d2005-09-21 11:54:30 -0500836EXPORT_SYMBOL(ieee80211_wx_set_encodeext);
837EXPORT_SYMBOL(ieee80211_wx_get_encodeext);
James Ketrenose0d369d2005-09-21 11:54:30 -0500838
Jeff Garzikb4538722005-05-12 22:48:20 -0400839EXPORT_SYMBOL(ieee80211_wx_get_scan);
840EXPORT_SYMBOL(ieee80211_wx_set_encode);
841EXPORT_SYMBOL(ieee80211_wx_get_encode);
Larry Fingerdd5eeb42006-01-30 13:12:50 +0100842
843EXPORT_SYMBOL_GPL(ieee80211_wx_set_auth);
844EXPORT_SYMBOL_GPL(ieee80211_wx_get_auth);