blob: 3025140ae7211ddc0d1b12a707bc3ac0f5ce872a [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
Jouni Malinen85d32e72007-03-24 17:15:30 -07008 <j@w1.fi>
9 Copyright (c) 2002-2003, Jouni Malinen <j@w1.fi>
Jeff Garzikb4538722005-05-12 22:48:20 -040010
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,
David S. Millerccc58052008-06-16 18:50:49 -070046 char *start, char *stop,
47 struct ieee80211_network *network,
48 struct iw_request_info *info)
Jeff Garzikb4538722005-05-12 22:48:20 -040049{
50 char custom[MAX_CUSTOM_LEN];
51 char *p;
52 struct iw_event iwe;
53 int i, j;
Zhu Yi09593042006-04-13 17:17:26 +080054 char *current_val; /* For rates */
55 u8 rate;
Jeff Garzikb4538722005-05-12 22:48:20 -040056
57 /* First entry *MUST* be the AP MAC address */
58 iwe.cmd = SIOCGIWAP;
59 iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
60 memcpy(iwe.u.ap_addr.sa_data, network->bssid, ETH_ALEN);
David S. Millerccc58052008-06-16 18:50:49 -070061 start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_ADDR_LEN);
Jeff Garzikb4538722005-05-12 22:48:20 -040062
63 /* Remaining entries will be displayed in the order we provide them */
64
65 /* Add the ESSID */
66 iwe.cmd = SIOCGIWESSID;
67 iwe.u.data.flags = 1;
John W. Linvillec5d3dce2008-09-30 17:17:26 -040068 iwe.u.data.length = min(network->ssid_len, (u8) 32);
69 start = iwe_stream_add_point(info, start, stop,
70 &iwe, network->ssid);
Jeff Garzikb4538722005-05-12 22:48:20 -040071
72 /* Add the protocol name */
73 iwe.cmd = SIOCGIWNAME;
Jeff Garzik0edd5b42005-09-07 00:48:31 -040074 snprintf(iwe.u.name, IFNAMSIZ, "IEEE 802.11%s",
75 ieee80211_modes[network->mode]);
David S. Millerccc58052008-06-16 18:50:49 -070076 start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_CHAR_LEN);
Jeff Garzikb4538722005-05-12 22:48:20 -040077
Jeff Garzik0edd5b42005-09-07 00:48:31 -040078 /* Add mode */
79 iwe.cmd = SIOCGIWMODE;
80 if (network->capability & (WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS)) {
Jeff Garzik1b5cca32005-08-15 00:32:15 -040081 if (network->capability & WLAN_CAPABILITY_ESS)
Jeff Garzikb4538722005-05-12 22:48:20 -040082 iwe.u.mode = IW_MODE_MASTER;
83 else
84 iwe.u.mode = IW_MODE_ADHOC;
85
David S. Millerccc58052008-06-16 18:50:49 -070086 start = iwe_stream_add_event(info, start, stop,
87 &iwe, IW_EV_UINT_LEN);
Jeff Garzikb4538722005-05-12 22:48:20 -040088 }
89
Larry Finger93afe3d2007-04-21 17:56:43 -050090 /* Add channel and frequency */
Jean Tourrilhes90869b22007-07-10 15:51:14 -050091 /* Note : userspace automatically computes channel using iwrange */
Jeff Garzikb4538722005-05-12 22:48:20 -040092 iwe.cmd = SIOCGIWFREQ;
Larry Finger93afe3d2007-04-21 17:56:43 -050093 iwe.u.freq.m = ieee80211_channel_to_freq(ieee, network->channel);
94 iwe.u.freq.e = 6;
Jean Tourrilhes90869b22007-07-10 15:51:14 -050095 iwe.u.freq.i = 0;
David S. Millerccc58052008-06-16 18:50:49 -070096 start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_FREQ_LEN);
Larry Finger93afe3d2007-04-21 17:56:43 -050097
Jeff Garzikb4538722005-05-12 22:48:20 -040098 /* Add encryption capability */
99 iwe.cmd = SIOCGIWENCODE;
100 if (network->capability & WLAN_CAPABILITY_PRIVACY)
101 iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
102 else
103 iwe.u.data.flags = IW_ENCODE_DISABLED;
104 iwe.u.data.length = 0;
David S. Millerccc58052008-06-16 18:50:49 -0700105 start = iwe_stream_add_point(info, start, stop,
106 &iwe, network->ssid);
Jeff Garzikb4538722005-05-12 22:48:20 -0400107
108 /* Add basic and extended rates */
Zhu Yi09593042006-04-13 17:17:26 +0800109 /* Rate : stuffing multiple values in a single event require a bit
110 * more of magic - Jean II */
David S. Millerccc58052008-06-16 18:50:49 -0700111 current_val = start + iwe_stream_lcp_len(info);
Zhu Yi09593042006-04-13 17:17:26 +0800112 iwe.cmd = SIOCGIWRATE;
113 /* Those two flags are ignored... */
114 iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0;
115
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400116 for (i = 0, j = 0; i < network->rates_len;) {
Jeff Garzikb4538722005-05-12 22:48:20 -0400117 if (j < network->rates_ex_len &&
118 ((network->rates_ex[j] & 0x7F) <
119 (network->rates[i] & 0x7F)))
120 rate = network->rates_ex[j++] & 0x7F;
121 else
122 rate = network->rates[i++] & 0x7F;
Zhu Yi09593042006-04-13 17:17:26 +0800123 /* Bit rate given in 500 kb/s units (+ 0x80) */
124 iwe.u.bitrate.value = ((rate & 0x7f) * 500000);
125 /* Add new value to event */
David S. Millerccc58052008-06-16 18:50:49 -0700126 current_val = iwe_stream_add_value(info, start, current_val,
127 stop, &iwe, IW_EV_PARAM_LEN);
Jeff Garzikb4538722005-05-12 22:48:20 -0400128 }
129 for (; j < network->rates_ex_len; j++) {
130 rate = network->rates_ex[j] & 0x7F;
Zhu Yi09593042006-04-13 17:17:26 +0800131 /* Bit rate given in 500 kb/s units (+ 0x80) */
132 iwe.u.bitrate.value = ((rate & 0x7f) * 500000);
133 /* Add new value to event */
David S. Millerccc58052008-06-16 18:50:49 -0700134 current_val = iwe_stream_add_value(info, start, current_val,
135 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 */
David S. Millerccc58052008-06-16 18:50:49 -0700138 if ((current_val - start) > iwe_stream_lcp_len(info))
Zhu Yi09593042006-04-13 17:17:26 +0800139 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
David S. Millerccc58052008-06-16 18:50:49 -0700185 start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_QUAL_LEN);
Jeff Garzikb4538722005-05-12 22:48:20 -0400186
187 iwe.cmd = IWEVCUSTOM;
188 p = custom;
189
190 iwe.u.data.length = p - custom;
191 if (iwe.u.data.length)
David S. Millerccc58052008-06-16 18:50:49 -0700192 start = iwe_stream_add_point(info, start, stop, &iwe, custom);
Jeff Garzikb4538722005-05-12 22:48:20 -0400193
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;
David S. Millerccc58052008-06-16 18:50:49 -0700200 start = iwe_stream_add_point(info, start, stop, &iwe, buf);
Jeff Garzikb4538722005-05-12 22:48:20 -0400201 }
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;
David S. Millerccc58052008-06-16 18:50:49 -0700209 start = iwe_stream_add_point(info, start, stop, &iwe, buf);
Jeff Garzikb4538722005-05-12 22:48:20 -0400210 }
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)
David S. Millerccc58052008-06-16 18:50:49 -0700221 start = iwe_stream_add_point(info, start, stop, &iwe, custom);
Jeff Garzikb4538722005-05-12 22:48:20 -0400222
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;
David S. Millerccc58052008-06-16 18:50:49 -0700242 start = iwe_stream_add_point(info, start, stop, &iwe, custom);
Zhu Yi7bd64362006-01-19 16:21:54 +0800243 }
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))
David S. Millerccc58052008-06-16 18:50:49 -0700275 ev = ieee80211_translate_scan(ieee, ev, stop, network,
276 info);
Jeff Garzikb4538722005-05-12 22:48:20 -0400277 else
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400278 IEEE80211_DEBUG_SCAN("Not showing network '%s ("
Johannes Berge1749612008-10-27 15:59:26 -0700279 "%pM)' due to age (%dms).\n",
John W. Linville7e272fc2008-09-24 18:13:14 -0400280 escape_ssid(network->ssid,
281 network->ssid_len),
Johannes Berge1749612008-10-27 15:59:26 -0700282 network->bssid,
James Ketrenos42e349f2005-09-21 11:54:07 -0500283 jiffies_to_msecs(jiffies -
284 network->
285 last_scanned));
Jeff Garzikb4538722005-05-12 22:48:20 -0400286 }
287
288 spin_unlock_irqrestore(&ieee->lock, flags);
289
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400290 wrqu->data.length = ev - extra;
Jeff Garzikb4538722005-05-12 22:48:20 -0400291 wrqu->data.flags = 0;
292
293 IEEE80211_DEBUG_WX("exit: %d networks returned.\n", i);
294
Zhu Yi55cd94a2006-01-19 16:20:59 +0800295 return err;
Jeff Garzikb4538722005-05-12 22:48:20 -0400296}
297
298int ieee80211_wx_set_encode(struct ieee80211_device *ieee,
299 struct iw_request_info *info,
300 union iwreq_data *wrqu, char *keybuf)
301{
302 struct iw_point *erq = &(wrqu->encoding);
303 struct net_device *dev = ieee->dev;
304 struct ieee80211_security sec = {
305 .flags = 0
306 };
307 int i, key, key_provided, len;
308 struct ieee80211_crypt_data **crypt;
Johannes Berga4bf26f2005-12-31 11:35:20 +0100309 int host_crypto = ieee->host_encrypt || ieee->host_decrypt || ieee->host_build_iv;
Jeff Garzikb4538722005-05-12 22:48:20 -0400310
311 IEEE80211_DEBUG_WX("SET_ENCODE\n");
312
313 key = erq->flags & IW_ENCODE_INDEX;
314 if (key) {
315 if (key > WEP_KEYS)
316 return -EINVAL;
317 key--;
318 key_provided = 1;
319 } else {
320 key_provided = 0;
321 key = ieee->tx_keyidx;
322 }
323
324 IEEE80211_DEBUG_WX("Key: %d [%s]\n", key, key_provided ?
325 "provided" : "default");
326
327 crypt = &ieee->crypt[key];
328
329 if (erq->flags & IW_ENCODE_DISABLED) {
330 if (key_provided && *crypt) {
331 IEEE80211_DEBUG_WX("Disabling encryption on key %d.\n",
332 key);
333 ieee80211_crypt_delayed_deinit(ieee, crypt);
334 } else
335 IEEE80211_DEBUG_WX("Disabling encryption.\n");
336
337 /* Check all the keys to see if any are still configured,
338 * and if no key index was provided, de-init them all */
339 for (i = 0; i < WEP_KEYS; i++) {
340 if (ieee->crypt[i] != NULL) {
341 if (key_provided)
342 break;
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400343 ieee80211_crypt_delayed_deinit(ieee,
344 &ieee->crypt[i]);
Jeff Garzikb4538722005-05-12 22:48:20 -0400345 }
346 }
347
348 if (i == WEP_KEYS) {
349 sec.enabled = 0;
James Ketrenosf1bf6632005-09-21 11:53:54 -0500350 sec.encrypt = 0;
Jeff Garzikb4538722005-05-12 22:48:20 -0400351 sec.level = SEC_LEVEL_0;
James Ketrenos259bf1f2005-09-21 11:54:22 -0500352 sec.flags |= SEC_ENABLED | SEC_LEVEL | SEC_ENCRYPT;
Jeff Garzikb4538722005-05-12 22:48:20 -0400353 }
354
355 goto done;
356 }
357
Jeff Garzikb4538722005-05-12 22:48:20 -0400358 sec.enabled = 1;
James Ketrenosf1bf6632005-09-21 11:53:54 -0500359 sec.encrypt = 1;
James Ketrenos259bf1f2005-09-21 11:54:22 -0500360 sec.flags |= SEC_ENABLED | SEC_ENCRYPT;
Jeff Garzikb4538722005-05-12 22:48:20 -0400361
362 if (*crypt != NULL && (*crypt)->ops != NULL &&
363 strcmp((*crypt)->ops->name, "WEP") != 0) {
364 /* changing to use WEP; deinit previously used algorithm
365 * on this key */
366 ieee80211_crypt_delayed_deinit(ieee, crypt);
367 }
368
James Ketrenosf1bf6632005-09-21 11:53:54 -0500369 if (*crypt == NULL && host_crypto) {
Jeff Garzikb4538722005-05-12 22:48:20 -0400370 struct ieee80211_crypt_data *new_crypt;
371
372 /* take WEP into use */
Panagiotis Issaris0da974f2006-07-21 14:51:30 -0700373 new_crypt = kzalloc(sizeof(struct ieee80211_crypt_data),
Jeff Garzikb4538722005-05-12 22:48:20 -0400374 GFP_KERNEL);
375 if (new_crypt == NULL)
376 return -ENOMEM;
Jeff Garzikb4538722005-05-12 22:48:20 -0400377 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",
John W. Linville7e272fc2008-09-24 18:13:14 -0400405 key, escape_ssid(sec.keys[key], len),
Jeff Garzikb4538722005-05-12 22:48:20 -0400406 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
Jean Delvarec03983a2007-10-19 23:22:55 +0200413 * explicitly set */
Jeff Garzikb4538722005-05-12 22:48:20 -0400414 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
Panagiotis Issaris0da974f2006-07-21 14:51:30 -0700619 new_crypt = kzalloc(sizeof(*new_crypt), GFP_KERNEL);
James Ketrenose0d369d2005-09-21 11:54:30 -0500620 if (new_crypt == NULL) {
621 ret = -ENOMEM;
622 goto done;
623 }
James Ketrenose0d369d2005-09-21 11:54:30 -0500624 new_crypt->ops = ops;
625 if (new_crypt->ops && try_module_get(new_crypt->ops->owner))
James Ketrenos6eb6edf2005-09-22 10:34:15 +0000626 new_crypt->priv = new_crypt->ops->init(idx);
James Ketrenose0d369d2005-09-21 11:54:30 -0500627 if (new_crypt->priv == NULL) {
628 kfree(new_crypt);
629 ret = -EINVAL;
630 goto done;
631 }
632 *crypt = new_crypt;
633 }
634
635 if (ext->key_len > 0 && (*crypt)->ops->set_key &&
636 (*crypt)->ops->set_key(ext->key, ext->key_len, ext->rx_seq,
637 (*crypt)->priv) < 0) {
638 IEEE80211_DEBUG_WX("%s: key setting failed\n", dev->name);
639 ret = -EINVAL;
640 goto done;
641 }
642
643 skip_host_crypt:
644 if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) {
645 ieee->tx_keyidx = idx;
646 sec.active_key = idx;
647 sec.flags |= SEC_ACTIVE_KEY;
648 }
649
650 if (ext->alg != IW_ENCODE_ALG_NONE) {
651 memcpy(sec.keys[idx], ext->key, ext->key_len);
652 sec.key_sizes[idx] = ext->key_len;
653 sec.flags |= (1 << idx);
654 if (ext->alg == IW_ENCODE_ALG_WEP) {
655 sec.encode_alg[idx] = SEC_ALG_WEP;
656 sec.flags |= SEC_LEVEL;
657 sec.level = SEC_LEVEL_1;
658 } else if (ext->alg == IW_ENCODE_ALG_TKIP) {
659 sec.encode_alg[idx] = SEC_ALG_TKIP;
660 sec.flags |= SEC_LEVEL;
661 sec.level = SEC_LEVEL_2;
662 } else if (ext->alg == IW_ENCODE_ALG_CCMP) {
663 sec.encode_alg[idx] = SEC_ALG_CCMP;
664 sec.flags |= SEC_LEVEL;
665 sec.level = SEC_LEVEL_3;
666 }
James Ketrenosccd0fda2005-09-21 11:58:32 -0500667 /* Don't set sec level for group keys. */
668 if (group_key)
669 sec.flags &= ~SEC_LEVEL;
James Ketrenose0d369d2005-09-21 11:54:30 -0500670 }
671 done:
672 if (ieee->set_security)
673 ieee->set_security(ieee->dev, &sec);
674
675 /*
676 * Do not reset port if card is in Managed mode since resetting will
677 * generate new IEEE 802.11 authentication which may end up in looping
678 * with IEEE 802.1X. If your hardware requires a reset after WEP
679 * configuration (for example... Prism2), implement the reset_port in
680 * the callbacks structures used to initialize the 802.11 stack.
681 */
682 if (ieee->reset_on_keychange &&
683 ieee->iw_mode != IW_MODE_INFRA &&
684 ieee->reset_port && ieee->reset_port(dev)) {
685 IEEE80211_DEBUG_WX("%s: reset_port failed\n", dev->name);
686 return -EINVAL;
687 }
688
689 return ret;
690}
691
692int ieee80211_wx_get_encodeext(struct ieee80211_device *ieee,
693 struct iw_request_info *info,
694 union iwreq_data *wrqu, char *extra)
695{
696 struct iw_point *encoding = &wrqu->encoding;
697 struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
698 struct ieee80211_security *sec = &ieee->sec;
699 int idx, max_key_len;
700
701 max_key_len = encoding->length - sizeof(*ext);
702 if (max_key_len < 0)
703 return -EINVAL;
704
705 idx = encoding->flags & IW_ENCODE_INDEX;
706 if (idx) {
707 if (idx < 1 || idx > WEP_KEYS)
708 return -EINVAL;
709 idx--;
710 } else
711 idx = ieee->tx_keyidx;
712
Roel Kluinf59d9782007-10-26 21:51:26 +0200713 if (!(ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) &&
Volker Braune1892772005-10-24 10:15:36 -0500714 ext->alg != IW_ENCODE_ALG_WEP)
James Ketrenose0d369d2005-09-21 11:54:30 -0500715 if (idx != 0 || ieee->iw_mode != IW_MODE_INFRA)
716 return -EINVAL;
717
718 encoding->flags = idx + 1;
719 memset(ext, 0, sizeof(*ext));
720
721 if (!sec->enabled) {
722 ext->alg = IW_ENCODE_ALG_NONE;
723 ext->key_len = 0;
724 encoding->flags |= IW_ENCODE_DISABLED;
725 } else {
726 if (sec->encode_alg[idx] == SEC_ALG_WEP)
727 ext->alg = IW_ENCODE_ALG_WEP;
728 else if (sec->encode_alg[idx] == SEC_ALG_TKIP)
729 ext->alg = IW_ENCODE_ALG_TKIP;
730 else if (sec->encode_alg[idx] == SEC_ALG_CCMP)
731 ext->alg = IW_ENCODE_ALG_CCMP;
732 else
733 return -EINVAL;
734
735 ext->key_len = sec->key_sizes[idx];
736 memcpy(ext->key, sec->keys[idx], ext->key_len);
737 encoding->flags |= IW_ENCODE_ENABLED;
738 if (ext->key_len &&
739 (ext->alg == IW_ENCODE_ALG_TKIP ||
740 ext->alg == IW_ENCODE_ALG_CCMP))
741 ext->ext_flags |= IW_ENCODE_EXT_TX_SEQ_VALID;
742
743 }
744
745 return 0;
746}
747
748EXPORT_SYMBOL(ieee80211_wx_set_encodeext);
749EXPORT_SYMBOL(ieee80211_wx_get_encodeext);
James Ketrenose0d369d2005-09-21 11:54:30 -0500750
Jeff Garzikb4538722005-05-12 22:48:20 -0400751EXPORT_SYMBOL(ieee80211_wx_get_scan);
752EXPORT_SYMBOL(ieee80211_wx_set_encode);
753EXPORT_SYMBOL(ieee80211_wx_get_encode);