blob: 5cb9cfd35397dadc60c66a0048945ec88a57d8a0 [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 */
Panagiotis Issaris0da974f2006-07-21 14:51:30 -0700372 new_crypt = kzalloc(sizeof(struct ieee80211_crypt_data),
Jeff Garzikb4538722005-05-12 22:48:20 -0400373 GFP_KERNEL);
374 if (new_crypt == NULL)
375 return -ENOMEM;
Jeff Garzikb4538722005-05-12 22:48:20 -0400376 new_crypt->ops = ieee80211_get_crypto_ops("WEP");
377 if (!new_crypt->ops) {
378 request_module("ieee80211_crypt_wep");
379 new_crypt->ops = ieee80211_get_crypto_ops("WEP");
380 }
381
382 if (new_crypt->ops && try_module_get(new_crypt->ops->owner))
James Ketrenos6eb6edf2005-09-22 10:34:15 +0000383 new_crypt->priv = new_crypt->ops->init(key);
Jeff Garzikb4538722005-05-12 22:48:20 -0400384
385 if (!new_crypt->ops || !new_crypt->priv) {
386 kfree(new_crypt);
387 new_crypt = NULL;
388
389 printk(KERN_WARNING "%s: could not initialize WEP: "
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400390 "load module ieee80211_crypt_wep\n", dev->name);
Jeff Garzikb4538722005-05-12 22:48:20 -0400391 return -EOPNOTSUPP;
392 }
393 *crypt = new_crypt;
394 }
395
396 /* If a new key was provided, set it up */
397 if (erq->length > 0) {
398 len = erq->length <= 5 ? 5 : 13;
399 memcpy(sec.keys[key], keybuf, erq->length);
400 if (len > erq->length)
401 memset(sec.keys[key] + erq->length, 0,
402 len - erq->length);
403 IEEE80211_DEBUG_WX("Setting key %d to '%s' (%d:%d bytes)\n",
404 key, escape_essid(sec.keys[key], len),
405 erq->length, len);
406 sec.key_sizes[key] = len;
James Ketrenosf1bf6632005-09-21 11:53:54 -0500407 if (*crypt)
408 (*crypt)->ops->set_key(sec.keys[key], len, NULL,
409 (*crypt)->priv);
Jeff Garzikb4538722005-05-12 22:48:20 -0400410 sec.flags |= (1 << key);
411 /* This ensures a key will be activated if no key is
412 * explicitely set */
413 if (key == sec.active_key)
414 sec.flags |= SEC_ACTIVE_KEY;
Jeff Garzikb4538722005-05-12 22:48:20 -0400415
James Ketrenosf1bf6632005-09-21 11:53:54 -0500416 } else {
417 if (host_crypto) {
418 len = (*crypt)->ops->get_key(sec.keys[key], WEP_KEY_LEN,
419 NULL, (*crypt)->priv);
420 if (len == 0) {
421 /* Set a default key of all 0 */
422 IEEE80211_DEBUG_WX("Setting key %d to all "
423 "zero.\n", key);
424 memset(sec.keys[key], 0, 13);
425 (*crypt)->ops->set_key(sec.keys[key], 13, NULL,
426 (*crypt)->priv);
427 sec.key_sizes[key] = 13;
428 sec.flags |= (1 << key);
429 }
430 }
Jeff Garzikb4538722005-05-12 22:48:20 -0400431 /* No key data - just set the default TX key index */
432 if (key_provided) {
James Ketrenosf1bf6632005-09-21 11:53:54 -0500433 IEEE80211_DEBUG_WX("Setting key %d to default Tx "
434 "key.\n", key);
Jeff Garzikb4538722005-05-12 22:48:20 -0400435 ieee->tx_keyidx = key;
436 sec.active_key = key;
437 sec.flags |= SEC_ACTIVE_KEY;
438 }
439 }
James Ketrenos7dc888f2005-09-21 11:58:38 -0500440 if (erq->flags & (IW_ENCODE_OPEN | IW_ENCODE_RESTRICTED)) {
441 ieee->open_wep = !(erq->flags & IW_ENCODE_RESTRICTED);
442 sec.auth_mode = ieee->open_wep ? WLAN_AUTH_OPEN :
443 WLAN_AUTH_SHARED_KEY;
444 sec.flags |= SEC_AUTH_MODE;
445 IEEE80211_DEBUG_WX("Auth: %s\n",
446 sec.auth_mode == WLAN_AUTH_OPEN ?
447 "OPEN" : "SHARED KEY");
448 }
Jeff Garzikb4538722005-05-12 22:48:20 -0400449
450 /* For now we just support WEP, so only set that security level...
451 * TODO: When WPA is added this is one place that needs to change */
452 sec.flags |= SEC_LEVEL;
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400453 sec.level = SEC_LEVEL_1; /* 40 and 104 bit WEP */
James Ketrenose0d369d2005-09-21 11:54:30 -0500454 sec.encode_alg[key] = SEC_ALG_WEP;
Jeff Garzikb4538722005-05-12 22:48:20 -0400455
James Ketrenos259bf1f2005-09-21 11:54:22 -0500456 done:
Jeff Garzikb4538722005-05-12 22:48:20 -0400457 if (ieee->set_security)
458 ieee->set_security(dev, &sec);
459
460 /* Do not reset port if card is in Managed mode since resetting will
461 * generate new IEEE 802.11 authentication which may end up in looping
462 * with IEEE 802.1X. If your hardware requires a reset after WEP
463 * configuration (for example... Prism2), implement the reset_port in
464 * the callbacks structures used to initialize the 802.11 stack. */
465 if (ieee->reset_on_keychange &&
466 ieee->iw_mode != IW_MODE_INFRA &&
467 ieee->reset_port && ieee->reset_port(dev)) {
468 printk(KERN_DEBUG "%s: reset_port failed\n", dev->name);
469 return -EINVAL;
470 }
471 return 0;
472}
473
474int ieee80211_wx_get_encode(struct ieee80211_device *ieee,
475 struct iw_request_info *info,
476 union iwreq_data *wrqu, char *keybuf)
477{
478 struct iw_point *erq = &(wrqu->encoding);
479 int len, key;
480 struct ieee80211_crypt_data *crypt;
James Ketrenosf1bf6632005-09-21 11:53:54 -0500481 struct ieee80211_security *sec = &ieee->sec;
Jeff Garzikb4538722005-05-12 22:48:20 -0400482
483 IEEE80211_DEBUG_WX("GET_ENCODE\n");
484
485 key = erq->flags & IW_ENCODE_INDEX;
486 if (key) {
487 if (key > WEP_KEYS)
488 return -EINVAL;
489 key--;
490 } else
491 key = ieee->tx_keyidx;
492
493 crypt = ieee->crypt[key];
494 erq->flags = key + 1;
495
James Ketrenosf1bf6632005-09-21 11:53:54 -0500496 if (!sec->enabled) {
Jeff Garzikb4538722005-05-12 22:48:20 -0400497 erq->length = 0;
498 erq->flags |= IW_ENCODE_DISABLED;
499 return 0;
500 }
501
James Ketrenosf1bf6632005-09-21 11:53:54 -0500502 len = sec->key_sizes[key];
503 memcpy(keybuf, sec->keys[key], len);
Jeff Garzikb4538722005-05-12 22:48:20 -0400504
Adrian Bunk62741152006-04-27 02:33:42 -0700505 erq->length = len;
Jeff Garzikb4538722005-05-12 22:48:20 -0400506 erq->flags |= IW_ENCODE_ENABLED;
507
508 if (ieee->open_wep)
509 erq->flags |= IW_ENCODE_OPEN;
510 else
511 erq->flags |= IW_ENCODE_RESTRICTED;
512
513 return 0;
514}
515
James Ketrenose0d369d2005-09-21 11:54:30 -0500516int ieee80211_wx_set_encodeext(struct ieee80211_device *ieee,
517 struct iw_request_info *info,
518 union iwreq_data *wrqu, char *extra)
519{
520 struct net_device *dev = ieee->dev;
521 struct iw_point *encoding = &wrqu->encoding;
522 struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
523 int i, idx, ret = 0;
James Ketrenosccd0fda2005-09-21 11:58:32 -0500524 int group_key = 0;
James Ketrenose0d369d2005-09-21 11:54:30 -0500525 const char *alg, *module;
526 struct ieee80211_crypto_ops *ops;
527 struct ieee80211_crypt_data **crypt;
528
529 struct ieee80211_security sec = {
530 .flags = 0,
531 };
532
533 idx = encoding->flags & IW_ENCODE_INDEX;
534 if (idx) {
535 if (idx < 1 || idx > WEP_KEYS)
536 return -EINVAL;
537 idx--;
538 } else
539 idx = ieee->tx_keyidx;
540
James Ketrenosccd0fda2005-09-21 11:58:32 -0500541 if (ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) {
James Ketrenose0d369d2005-09-21 11:54:30 -0500542 crypt = &ieee->crypt[idx];
James Ketrenosccd0fda2005-09-21 11:58:32 -0500543 group_key = 1;
544 } else {
Volker Braune1892772005-10-24 10:15:36 -0500545 /* some Cisco APs use idx>0 for unicast in dynamic WEP */
546 if (idx != 0 && ext->alg != IW_ENCODE_ALG_WEP)
James Ketrenose0d369d2005-09-21 11:54:30 -0500547 return -EINVAL;
548 if (ieee->iw_mode == IW_MODE_INFRA)
549 crypt = &ieee->crypt[idx];
550 else
551 return -EINVAL;
552 }
553
554 sec.flags |= SEC_ENABLED | SEC_ENCRYPT;
555 if ((encoding->flags & IW_ENCODE_DISABLED) ||
556 ext->alg == IW_ENCODE_ALG_NONE) {
557 if (*crypt)
558 ieee80211_crypt_delayed_deinit(ieee, crypt);
559
560 for (i = 0; i < WEP_KEYS; i++)
561 if (ieee->crypt[i] != NULL)
562 break;
563
564 if (i == WEP_KEYS) {
565 sec.enabled = 0;
566 sec.encrypt = 0;
567 sec.level = SEC_LEVEL_0;
568 sec.flags |= SEC_LEVEL;
569 }
570 goto done;
571 }
572
573 sec.enabled = 1;
574 sec.encrypt = 1;
575
James Ketrenosccd0fda2005-09-21 11:58:32 -0500576 if (group_key ? !ieee->host_mc_decrypt :
577 !(ieee->host_encrypt || ieee->host_decrypt ||
578 ieee->host_encrypt_msdu))
James Ketrenose0d369d2005-09-21 11:54:30 -0500579 goto skip_host_crypt;
580
581 switch (ext->alg) {
582 case IW_ENCODE_ALG_WEP:
583 alg = "WEP";
584 module = "ieee80211_crypt_wep";
585 break;
586 case IW_ENCODE_ALG_TKIP:
587 alg = "TKIP";
588 module = "ieee80211_crypt_tkip";
589 break;
590 case IW_ENCODE_ALG_CCMP:
591 alg = "CCMP";
592 module = "ieee80211_crypt_ccmp";
593 break;
594 default:
595 IEEE80211_DEBUG_WX("%s: unknown crypto alg %d\n",
596 dev->name, ext->alg);
597 ret = -EINVAL;
598 goto done;
599 }
600
601 ops = ieee80211_get_crypto_ops(alg);
602 if (ops == NULL) {
603 request_module(module);
604 ops = ieee80211_get_crypto_ops(alg);
605 }
606 if (ops == NULL) {
607 IEEE80211_DEBUG_WX("%s: unknown crypto alg %d\n",
608 dev->name, ext->alg);
609 ret = -EINVAL;
610 goto done;
611 }
612
613 if (*crypt == NULL || (*crypt)->ops != ops) {
614 struct ieee80211_crypt_data *new_crypt;
615
616 ieee80211_crypt_delayed_deinit(ieee, crypt);
617
Panagiotis Issaris0da974f2006-07-21 14:51:30 -0700618 new_crypt = kzalloc(sizeof(*new_crypt), GFP_KERNEL);
James Ketrenose0d369d2005-09-21 11:54:30 -0500619 if (new_crypt == NULL) {
620 ret = -ENOMEM;
621 goto done;
622 }
James Ketrenose0d369d2005-09-21 11:54:30 -0500623 new_crypt->ops = ops;
624 if (new_crypt->ops && try_module_get(new_crypt->ops->owner))
James Ketrenos6eb6edf2005-09-22 10:34:15 +0000625 new_crypt->priv = new_crypt->ops->init(idx);
James Ketrenose0d369d2005-09-21 11:54:30 -0500626 if (new_crypt->priv == NULL) {
627 kfree(new_crypt);
628 ret = -EINVAL;
629 goto done;
630 }
631 *crypt = new_crypt;
632 }
633
634 if (ext->key_len > 0 && (*crypt)->ops->set_key &&
635 (*crypt)->ops->set_key(ext->key, ext->key_len, ext->rx_seq,
636 (*crypt)->priv) < 0) {
637 IEEE80211_DEBUG_WX("%s: key setting failed\n", dev->name);
638 ret = -EINVAL;
639 goto done;
640 }
641
642 skip_host_crypt:
643 if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) {
644 ieee->tx_keyidx = idx;
645 sec.active_key = idx;
646 sec.flags |= SEC_ACTIVE_KEY;
647 }
648
649 if (ext->alg != IW_ENCODE_ALG_NONE) {
650 memcpy(sec.keys[idx], ext->key, ext->key_len);
651 sec.key_sizes[idx] = ext->key_len;
652 sec.flags |= (1 << idx);
653 if (ext->alg == IW_ENCODE_ALG_WEP) {
654 sec.encode_alg[idx] = SEC_ALG_WEP;
655 sec.flags |= SEC_LEVEL;
656 sec.level = SEC_LEVEL_1;
657 } else if (ext->alg == IW_ENCODE_ALG_TKIP) {
658 sec.encode_alg[idx] = SEC_ALG_TKIP;
659 sec.flags |= SEC_LEVEL;
660 sec.level = SEC_LEVEL_2;
661 } else if (ext->alg == IW_ENCODE_ALG_CCMP) {
662 sec.encode_alg[idx] = SEC_ALG_CCMP;
663 sec.flags |= SEC_LEVEL;
664 sec.level = SEC_LEVEL_3;
665 }
James Ketrenosccd0fda2005-09-21 11:58:32 -0500666 /* Don't set sec level for group keys. */
667 if (group_key)
668 sec.flags &= ~SEC_LEVEL;
James Ketrenose0d369d2005-09-21 11:54:30 -0500669 }
670 done:
671 if (ieee->set_security)
672 ieee->set_security(ieee->dev, &sec);
673
674 /*
675 * Do not reset port if card is in Managed mode since resetting will
676 * generate new IEEE 802.11 authentication which may end up in looping
677 * with IEEE 802.1X. If your hardware requires a reset after WEP
678 * configuration (for example... Prism2), implement the reset_port in
679 * the callbacks structures used to initialize the 802.11 stack.
680 */
681 if (ieee->reset_on_keychange &&
682 ieee->iw_mode != IW_MODE_INFRA &&
683 ieee->reset_port && ieee->reset_port(dev)) {
684 IEEE80211_DEBUG_WX("%s: reset_port failed\n", dev->name);
685 return -EINVAL;
686 }
687
688 return ret;
689}
690
691int ieee80211_wx_get_encodeext(struct ieee80211_device *ieee,
692 struct iw_request_info *info,
693 union iwreq_data *wrqu, char *extra)
694{
695 struct iw_point *encoding = &wrqu->encoding;
696 struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
697 struct ieee80211_security *sec = &ieee->sec;
698 int idx, max_key_len;
699
700 max_key_len = encoding->length - sizeof(*ext);
701 if (max_key_len < 0)
702 return -EINVAL;
703
704 idx = encoding->flags & IW_ENCODE_INDEX;
705 if (idx) {
706 if (idx < 1 || idx > WEP_KEYS)
707 return -EINVAL;
708 idx--;
709 } else
710 idx = ieee->tx_keyidx;
711
Volker Braune1892772005-10-24 10:15:36 -0500712 if (!ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY &&
713 ext->alg != IW_ENCODE_ALG_WEP)
James Ketrenose0d369d2005-09-21 11:54:30 -0500714 if (idx != 0 || ieee->iw_mode != IW_MODE_INFRA)
715 return -EINVAL;
716
717 encoding->flags = idx + 1;
718 memset(ext, 0, sizeof(*ext));
719
720 if (!sec->enabled) {
721 ext->alg = IW_ENCODE_ALG_NONE;
722 ext->key_len = 0;
723 encoding->flags |= IW_ENCODE_DISABLED;
724 } else {
725 if (sec->encode_alg[idx] == SEC_ALG_WEP)
726 ext->alg = IW_ENCODE_ALG_WEP;
727 else if (sec->encode_alg[idx] == SEC_ALG_TKIP)
728 ext->alg = IW_ENCODE_ALG_TKIP;
729 else if (sec->encode_alg[idx] == SEC_ALG_CCMP)
730 ext->alg = IW_ENCODE_ALG_CCMP;
731 else
732 return -EINVAL;
733
734 ext->key_len = sec->key_sizes[idx];
735 memcpy(ext->key, sec->keys[idx], ext->key_len);
736 encoding->flags |= IW_ENCODE_ENABLED;
737 if (ext->key_len &&
738 (ext->alg == IW_ENCODE_ALG_TKIP ||
739 ext->alg == IW_ENCODE_ALG_CCMP))
740 ext->ext_flags |= IW_ENCODE_EXT_TX_SEQ_VALID;
741
742 }
743
744 return 0;
745}
746
Larry Fingerdd5eeb42006-01-30 13:12:50 +0100747int ieee80211_wx_set_auth(struct net_device *dev,
748 struct iw_request_info *info,
749 union iwreq_data *wrqu,
750 char *extra)
751{
752 struct ieee80211_device *ieee = netdev_priv(dev);
753 unsigned long flags;
754 int err = 0;
755
756 spin_lock_irqsave(&ieee->lock, flags);
757
758 switch (wrqu->param.flags & IW_AUTH_INDEX) {
759 case IW_AUTH_WPA_VERSION:
760 case IW_AUTH_CIPHER_PAIRWISE:
761 case IW_AUTH_CIPHER_GROUP:
762 case IW_AUTH_KEY_MGMT:
763 /*
764 * Host AP driver does not use these parameters and allows
765 * wpa_supplicant to control them internally.
766 */
767 break;
768 case IW_AUTH_TKIP_COUNTERMEASURES:
769 break; /* FIXME */
770 case IW_AUTH_DROP_UNENCRYPTED:
771 ieee->drop_unencrypted = !!wrqu->param.value;
772 break;
773 case IW_AUTH_80211_AUTH_ALG:
774 break; /* FIXME */
775 case IW_AUTH_WPA_ENABLED:
776 ieee->privacy_invoked = ieee->wpa_enabled = !!wrqu->param.value;
777 break;
778 case IW_AUTH_RX_UNENCRYPTED_EAPOL:
779 ieee->ieee802_1x = !!wrqu->param.value;
780 break;
781 case IW_AUTH_PRIVACY_INVOKED:
782 ieee->privacy_invoked = !!wrqu->param.value;
783 break;
784 default:
785 err = -EOPNOTSUPP;
786 break;
787 }
788 spin_unlock_irqrestore(&ieee->lock, flags);
789 return err;
790}
791
792int ieee80211_wx_get_auth(struct net_device *dev,
793 struct iw_request_info *info,
794 union iwreq_data *wrqu,
795 char *extra)
796{
797 struct ieee80211_device *ieee = netdev_priv(dev);
798 unsigned long flags;
799 int err = 0;
800
801 spin_lock_irqsave(&ieee->lock, flags);
802
803 switch (wrqu->param.flags & IW_AUTH_INDEX) {
804 case IW_AUTH_WPA_VERSION:
805 case IW_AUTH_CIPHER_PAIRWISE:
806 case IW_AUTH_CIPHER_GROUP:
807 case IW_AUTH_KEY_MGMT:
808 case IW_AUTH_TKIP_COUNTERMEASURES: /* FIXME */
809 case IW_AUTH_80211_AUTH_ALG: /* FIXME */
810 /*
811 * Host AP driver does not use these parameters and allows
812 * wpa_supplicant to control them internally.
813 */
814 err = -EOPNOTSUPP;
815 break;
816 case IW_AUTH_DROP_UNENCRYPTED:
817 wrqu->param.value = ieee->drop_unencrypted;
818 break;
819 case IW_AUTH_WPA_ENABLED:
820 wrqu->param.value = ieee->wpa_enabled;
821 break;
822 case IW_AUTH_RX_UNENCRYPTED_EAPOL:
823 wrqu->param.value = ieee->ieee802_1x;
824 break;
825 default:
826 err = -EOPNOTSUPP;
827 break;
828 }
829 spin_unlock_irqrestore(&ieee->lock, flags);
830 return err;
831}
832
James Ketrenose0d369d2005-09-21 11:54:30 -0500833EXPORT_SYMBOL(ieee80211_wx_set_encodeext);
834EXPORT_SYMBOL(ieee80211_wx_get_encodeext);
James Ketrenose0d369d2005-09-21 11:54:30 -0500835
Jeff Garzikb4538722005-05-12 22:48:20 -0400836EXPORT_SYMBOL(ieee80211_wx_get_scan);
837EXPORT_SYMBOL(ieee80211_wx_set_encode);
838EXPORT_SYMBOL(ieee80211_wx_get_encode);
Larry Fingerdd5eeb42006-01-30 13:12:50 +0100839
840EXPORT_SYMBOL_GPL(ieee80211_wx_set_auth);
841EXPORT_SYMBOL_GPL(ieee80211_wx_get_auth);