blob: 465b73d505321e0fefecbb499d963f90ac58f4ea [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,
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
Larry Finger93afe3d2007-04-21 17:56:43 -050092 /* Add channel and frequency */
Jean Tourrilhes90869b22007-07-10 15:51:14 -050093 /* Note : userspace automatically computes channel using iwrange */
Jeff Garzikb4538722005-05-12 22:48:20 -040094 iwe.cmd = SIOCGIWFREQ;
Larry Finger93afe3d2007-04-21 17:56:43 -050095 iwe.u.freq.m = ieee80211_channel_to_freq(ieee, network->channel);
96 iwe.u.freq.e = 6;
Jean Tourrilhes90869b22007-07-10 15:51:14 -050097 iwe.u.freq.i = 0;
Larry Finger93afe3d2007-04-21 17:56:43 -050098 start = iwe_stream_add_event(start, stop, &iwe, IW_EV_FREQ_LEN);
99
Jeff Garzikb4538722005-05-12 22:48:20 -0400100 /* Add encryption capability */
101 iwe.cmd = SIOCGIWENCODE;
102 if (network->capability & WLAN_CAPABILITY_PRIVACY)
103 iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
104 else
105 iwe.u.data.flags = IW_ENCODE_DISABLED;
106 iwe.u.data.length = 0;
107 start = iwe_stream_add_point(start, stop, &iwe, network->ssid);
108
109 /* Add basic and extended rates */
Zhu Yi09593042006-04-13 17:17:26 +0800110 /* Rate : stuffing multiple values in a single event require a bit
111 * more of magic - Jean II */
112 current_val = start + IW_EV_LCP_LEN;
113 iwe.cmd = SIOCGIWRATE;
114 /* Those two flags are ignored... */
115 iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0;
116
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400117 for (i = 0, j = 0; i < network->rates_len;) {
Jeff Garzikb4538722005-05-12 22:48:20 -0400118 if (j < network->rates_ex_len &&
119 ((network->rates_ex[j] & 0x7F) <
120 (network->rates[i] & 0x7F)))
121 rate = network->rates_ex[j++] & 0x7F;
122 else
123 rate = network->rates[i++] & 0x7F;
Zhu Yi09593042006-04-13 17:17:26 +0800124 /* Bit rate given in 500 kb/s units (+ 0x80) */
125 iwe.u.bitrate.value = ((rate & 0x7f) * 500000);
126 /* Add new value to event */
127 current_val = iwe_stream_add_value(start, current_val, 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 */
134 current_val = iwe_stream_add_value(start, current_val, stop, &iwe, IW_EV_PARAM_LEN);
Jeff Garzikb4538722005-05-12 22:48:20 -0400135 }
Zhu Yi09593042006-04-13 17:17:26 +0800136 /* Check if we added any rate */
137 if((current_val - start) > IW_EV_LCP_LEN)
138 start = current_val;
Jeff Garzikb4538722005-05-12 22:48:20 -0400139
140 /* Add quality statistics */
Jeff Garzikb4538722005-05-12 22:48:20 -0400141 iwe.cmd = IWEVQUAL;
James Ketrenosb1b508e2005-09-13 17:27:19 -0500142 iwe.u.qual.updated = IW_QUAL_QUAL_UPDATED | IW_QUAL_LEVEL_UPDATED |
143 IW_QUAL_NOISE_UPDATED;
144
145 if (!(network->stats.mask & IEEE80211_STATMASK_RSSI)) {
146 iwe.u.qual.updated |= IW_QUAL_QUAL_INVALID |
147 IW_QUAL_LEVEL_INVALID;
148 iwe.u.qual.qual = 0;
James Ketrenosb1b508e2005-09-13 17:27:19 -0500149 } else {
Jiri Benc757d18f2005-10-10 19:16:53 +0200150 if (ieee->perfect_rssi == ieee->worst_rssi)
151 iwe.u.qual.qual = 100;
152 else
153 iwe.u.qual.qual =
154 (100 *
155 (ieee->perfect_rssi - ieee->worst_rssi) *
156 (ieee->perfect_rssi - ieee->worst_rssi) -
157 (ieee->perfect_rssi - network->stats.rssi) *
158 (15 * (ieee->perfect_rssi - ieee->worst_rssi) +
James Ketrenos81f87522005-10-24 10:20:53 -0500159 62 * (ieee->perfect_rssi -
160 network->stats.rssi))) /
161 ((ieee->perfect_rssi -
162 ieee->worst_rssi) * (ieee->perfect_rssi -
163 ieee->worst_rssi));
James Ketrenosb1b508e2005-09-13 17:27:19 -0500164 if (iwe.u.qual.qual > 100)
165 iwe.u.qual.qual = 100;
166 else if (iwe.u.qual.qual < 1)
167 iwe.u.qual.qual = 0;
168 }
169
170 if (!(network->stats.mask & IEEE80211_STATMASK_NOISE)) {
Jeff Garzikb4538722005-05-12 22:48:20 -0400171 iwe.u.qual.updated |= IW_QUAL_NOISE_INVALID;
James Ketrenosb1b508e2005-09-13 17:27:19 -0500172 iwe.u.qual.noise = 0;
173 } else {
174 iwe.u.qual.noise = network->stats.noise;
175 }
Jeff Garzikb4538722005-05-12 22:48:20 -0400176
Zhu Yi7bd64362006-01-19 16:21:54 +0800177 if (!(network->stats.mask & IEEE80211_STATMASK_SIGNAL)) {
178 iwe.u.qual.updated |= IW_QUAL_LEVEL_INVALID;
179 iwe.u.qual.level = 0;
180 } else {
181 iwe.u.qual.level = network->stats.signal;
182 }
183
Jeff Garzikb4538722005-05-12 22:48:20 -0400184 start = iwe_stream_add_event(start, stop, &iwe, IW_EV_QUAL_LEN);
185
186 iwe.cmd = IWEVCUSTOM;
187 p = custom;
188
189 iwe.u.data.length = p - custom;
190 if (iwe.u.data.length)
191 start = iwe_stream_add_point(start, stop, &iwe, custom);
192
Zhu Yi47168082006-02-13 13:37:03 +0800193 memset(&iwe, 0, sizeof(iwe));
James Ketrenos20d64712005-09-21 11:53:43 -0500194 if (network->wpa_ie_len) {
Zhu Yi47168082006-02-13 13:37:03 +0800195 char buf[MAX_WPA_IE_LEN];
196 memcpy(buf, network->wpa_ie, network->wpa_ie_len);
197 iwe.cmd = IWEVGENIE;
198 iwe.u.data.length = network->wpa_ie_len;
Jeff Garzikb4538722005-05-12 22:48:20 -0400199 start = iwe_stream_add_point(start, stop, &iwe, buf);
200 }
201
Zhu Yi47168082006-02-13 13:37:03 +0800202 memset(&iwe, 0, sizeof(iwe));
James Ketrenos20d64712005-09-21 11:53:43 -0500203 if (network->rsn_ie_len) {
Zhu Yi47168082006-02-13 13:37:03 +0800204 char buf[MAX_WPA_IE_LEN];
205 memcpy(buf, network->rsn_ie, network->rsn_ie_len);
206 iwe.cmd = IWEVGENIE;
207 iwe.u.data.length = network->rsn_ie_len;
Jeff Garzikb4538722005-05-12 22:48:20 -0400208 start = iwe_stream_add_point(start, stop, &iwe, buf);
209 }
210
211 /* Add EXTRA: Age to display seconds since last beacon/probe response
212 * for given network. */
213 iwe.cmd = IWEVCUSTOM;
214 p = custom;
215 p += snprintf(p, MAX_CUSTOM_LEN - (p - custom),
James Ketrenos42e349f2005-09-21 11:54:07 -0500216 " Last beacon: %dms ago",
217 jiffies_to_msecs(jiffies - network->last_scanned));
Jeff Garzikb4538722005-05-12 22:48:20 -0400218 iwe.u.data.length = p - custom;
219 if (iwe.u.data.length)
220 start = iwe_stream_add_point(start, stop, &iwe, custom);
221
Zhu Yi7bd64362006-01-19 16:21:54 +0800222 /* Add spectrum management information */
223 iwe.cmd = -1;
224 p = custom;
225 p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), " Channel flags: ");
226
227 if (ieee80211_get_channel_flags(ieee, network->channel) &
228 IEEE80211_CH_INVALID) {
229 iwe.cmd = IWEVCUSTOM;
230 p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), "INVALID ");
231 }
232
233 if (ieee80211_get_channel_flags(ieee, network->channel) &
234 IEEE80211_CH_RADAR_DETECT) {
235 iwe.cmd = IWEVCUSTOM;
236 p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), "DFS ");
237 }
238
239 if (iwe.cmd == IWEVCUSTOM) {
240 iwe.u.data.length = p - custom;
241 start = iwe_stream_add_point(start, stop, &iwe, custom);
242 }
243
Jeff Garzikb4538722005-05-12 22:48:20 -0400244 return start;
245}
246
Zhu Yi55cd94a2006-01-19 16:20:59 +0800247#define SCAN_ITEM_SIZE 128
248
Jeff Garzikb4538722005-05-12 22:48:20 -0400249int ieee80211_wx_get_scan(struct ieee80211_device *ieee,
250 struct iw_request_info *info,
251 union iwreq_data *wrqu, char *extra)
252{
253 struct ieee80211_network *network;
254 unsigned long flags;
Zhu Yi55cd94a2006-01-19 16:20:59 +0800255 int err = 0;
Jeff Garzikb4538722005-05-12 22:48:20 -0400256
257 char *ev = extra;
Zhu Yi55cd94a2006-01-19 16:20:59 +0800258 char *stop = ev + wrqu->data.length;
Jeff Garzikb4538722005-05-12 22:48:20 -0400259 int i = 0;
260
261 IEEE80211_DEBUG_WX("Getting scan\n");
262
263 spin_lock_irqsave(&ieee->lock, flags);
264
265 list_for_each_entry(network, &ieee->network_list, list) {
266 i++;
Zhu Yi55cd94a2006-01-19 16:20:59 +0800267 if (stop - ev < SCAN_ITEM_SIZE) {
268 err = -E2BIG;
269 break;
270 }
271
Jeff Garzikb4538722005-05-12 22:48:20 -0400272 if (ieee->scan_age == 0 ||
273 time_after(network->last_scanned + ieee->scan_age, jiffies))
Larry Fingerd94606e2006-03-03 16:21:55 -0600274 ev = ieee80211_translate_scan(ieee, ev, stop, network);
Jeff Garzikb4538722005-05-12 22:48:20 -0400275 else
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400276 IEEE80211_DEBUG_SCAN("Not showing network '%s ("
James Ketrenos42e349f2005-09-21 11:54:07 -0500277 MAC_FMT ")' due to age (%dms).\n",
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400278 escape_essid(network->ssid,
279 network->ssid_len),
280 MAC_ARG(network->bssid),
James Ketrenos42e349f2005-09-21 11:54:07 -0500281 jiffies_to_msecs(jiffies -
282 network->
283 last_scanned));
Jeff Garzikb4538722005-05-12 22:48:20 -0400284 }
285
286 spin_unlock_irqrestore(&ieee->lock, flags);
287
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400288 wrqu->data.length = ev - extra;
Jeff Garzikb4538722005-05-12 22:48:20 -0400289 wrqu->data.flags = 0;
290
291 IEEE80211_DEBUG_WX("exit: %d networks returned.\n", i);
292
Zhu Yi55cd94a2006-01-19 16:20:59 +0800293 return err;
Jeff Garzikb4538722005-05-12 22:48:20 -0400294}
295
296int ieee80211_wx_set_encode(struct ieee80211_device *ieee,
297 struct iw_request_info *info,
298 union iwreq_data *wrqu, char *keybuf)
299{
300 struct iw_point *erq = &(wrqu->encoding);
301 struct net_device *dev = ieee->dev;
302 struct ieee80211_security sec = {
303 .flags = 0
304 };
305 int i, key, key_provided, len;
306 struct ieee80211_crypt_data **crypt;
Johannes Berga4bf26f2005-12-31 11:35:20 +0100307 int host_crypto = ieee->host_encrypt || ieee->host_decrypt || ieee->host_build_iv;
Jeff Garzikb4538722005-05-12 22:48:20 -0400308
309 IEEE80211_DEBUG_WX("SET_ENCODE\n");
310
311 key = erq->flags & IW_ENCODE_INDEX;
312 if (key) {
313 if (key > WEP_KEYS)
314 return -EINVAL;
315 key--;
316 key_provided = 1;
317 } else {
318 key_provided = 0;
319 key = ieee->tx_keyidx;
320 }
321
322 IEEE80211_DEBUG_WX("Key: %d [%s]\n", key, key_provided ?
323 "provided" : "default");
324
325 crypt = &ieee->crypt[key];
326
327 if (erq->flags & IW_ENCODE_DISABLED) {
328 if (key_provided && *crypt) {
329 IEEE80211_DEBUG_WX("Disabling encryption on key %d.\n",
330 key);
331 ieee80211_crypt_delayed_deinit(ieee, crypt);
332 } else
333 IEEE80211_DEBUG_WX("Disabling encryption.\n");
334
335 /* Check all the keys to see if any are still configured,
336 * and if no key index was provided, de-init them all */
337 for (i = 0; i < WEP_KEYS; i++) {
338 if (ieee->crypt[i] != NULL) {
339 if (key_provided)
340 break;
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400341 ieee80211_crypt_delayed_deinit(ieee,
342 &ieee->crypt[i]);
Jeff Garzikb4538722005-05-12 22:48:20 -0400343 }
344 }
345
346 if (i == WEP_KEYS) {
347 sec.enabled = 0;
James Ketrenosf1bf6632005-09-21 11:53:54 -0500348 sec.encrypt = 0;
Jeff Garzikb4538722005-05-12 22:48:20 -0400349 sec.level = SEC_LEVEL_0;
James Ketrenos259bf1f2005-09-21 11:54:22 -0500350 sec.flags |= SEC_ENABLED | SEC_LEVEL | SEC_ENCRYPT;
Jeff Garzikb4538722005-05-12 22:48:20 -0400351 }
352
353 goto done;
354 }
355
Jeff Garzikb4538722005-05-12 22:48:20 -0400356 sec.enabled = 1;
James Ketrenosf1bf6632005-09-21 11:53:54 -0500357 sec.encrypt = 1;
James Ketrenos259bf1f2005-09-21 11:54:22 -0500358 sec.flags |= SEC_ENABLED | SEC_ENCRYPT;
Jeff Garzikb4538722005-05-12 22:48:20 -0400359
360 if (*crypt != NULL && (*crypt)->ops != NULL &&
361 strcmp((*crypt)->ops->name, "WEP") != 0) {
362 /* changing to use WEP; deinit previously used algorithm
363 * on this key */
364 ieee80211_crypt_delayed_deinit(ieee, crypt);
365 }
366
James Ketrenosf1bf6632005-09-21 11:53:54 -0500367 if (*crypt == NULL && host_crypto) {
Jeff Garzikb4538722005-05-12 22:48:20 -0400368 struct ieee80211_crypt_data *new_crypt;
369
370 /* take WEP into use */
Panagiotis Issaris0da974f2006-07-21 14:51:30 -0700371 new_crypt = kzalloc(sizeof(struct ieee80211_crypt_data),
Jeff Garzikb4538722005-05-12 22:48:20 -0400372 GFP_KERNEL);
373 if (new_crypt == NULL)
374 return -ENOMEM;
Jeff Garzikb4538722005-05-12 22:48:20 -0400375 new_crypt->ops = ieee80211_get_crypto_ops("WEP");
376 if (!new_crypt->ops) {
377 request_module("ieee80211_crypt_wep");
378 new_crypt->ops = ieee80211_get_crypto_ops("WEP");
379 }
380
381 if (new_crypt->ops && try_module_get(new_crypt->ops->owner))
James Ketrenos6eb6edf2005-09-22 10:34:15 +0000382 new_crypt->priv = new_crypt->ops->init(key);
Jeff Garzikb4538722005-05-12 22:48:20 -0400383
384 if (!new_crypt->ops || !new_crypt->priv) {
385 kfree(new_crypt);
386 new_crypt = NULL;
387
388 printk(KERN_WARNING "%s: could not initialize WEP: "
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400389 "load module ieee80211_crypt_wep\n", dev->name);
Jeff Garzikb4538722005-05-12 22:48:20 -0400390 return -EOPNOTSUPP;
391 }
392 *crypt = new_crypt;
393 }
394
395 /* If a new key was provided, set it up */
396 if (erq->length > 0) {
397 len = erq->length <= 5 ? 5 : 13;
398 memcpy(sec.keys[key], keybuf, erq->length);
399 if (len > erq->length)
400 memset(sec.keys[key] + erq->length, 0,
401 len - erq->length);
402 IEEE80211_DEBUG_WX("Setting key %d to '%s' (%d:%d bytes)\n",
403 key, escape_essid(sec.keys[key], len),
404 erq->length, len);
405 sec.key_sizes[key] = len;
James Ketrenosf1bf6632005-09-21 11:53:54 -0500406 if (*crypt)
407 (*crypt)->ops->set_key(sec.keys[key], len, NULL,
408 (*crypt)->priv);
Jeff Garzikb4538722005-05-12 22:48:20 -0400409 sec.flags |= (1 << key);
410 /* This ensures a key will be activated if no key is
411 * explicitely set */
412 if (key == sec.active_key)
413 sec.flags |= SEC_ACTIVE_KEY;
Jeff Garzikb4538722005-05-12 22:48:20 -0400414
James Ketrenosf1bf6632005-09-21 11:53:54 -0500415 } else {
416 if (host_crypto) {
417 len = (*crypt)->ops->get_key(sec.keys[key], WEP_KEY_LEN,
418 NULL, (*crypt)->priv);
419 if (len == 0) {
420 /* Set a default key of all 0 */
421 IEEE80211_DEBUG_WX("Setting key %d to all "
422 "zero.\n", key);
423 memset(sec.keys[key], 0, 13);
424 (*crypt)->ops->set_key(sec.keys[key], 13, NULL,
425 (*crypt)->priv);
426 sec.key_sizes[key] = 13;
427 sec.flags |= (1 << key);
428 }
429 }
Jeff Garzikb4538722005-05-12 22:48:20 -0400430 /* No key data - just set the default TX key index */
431 if (key_provided) {
James Ketrenosf1bf6632005-09-21 11:53:54 -0500432 IEEE80211_DEBUG_WX("Setting key %d to default Tx "
433 "key.\n", key);
Jeff Garzikb4538722005-05-12 22:48:20 -0400434 ieee->tx_keyidx = key;
435 sec.active_key = key;
436 sec.flags |= SEC_ACTIVE_KEY;
437 }
438 }
James Ketrenos7dc888f2005-09-21 11:58:38 -0500439 if (erq->flags & (IW_ENCODE_OPEN | IW_ENCODE_RESTRICTED)) {
440 ieee->open_wep = !(erq->flags & IW_ENCODE_RESTRICTED);
441 sec.auth_mode = ieee->open_wep ? WLAN_AUTH_OPEN :
442 WLAN_AUTH_SHARED_KEY;
443 sec.flags |= SEC_AUTH_MODE;
444 IEEE80211_DEBUG_WX("Auth: %s\n",
445 sec.auth_mode == WLAN_AUTH_OPEN ?
446 "OPEN" : "SHARED KEY");
447 }
Jeff Garzikb4538722005-05-12 22:48:20 -0400448
449 /* For now we just support WEP, so only set that security level...
450 * TODO: When WPA is added this is one place that needs to change */
451 sec.flags |= SEC_LEVEL;
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400452 sec.level = SEC_LEVEL_1; /* 40 and 104 bit WEP */
James Ketrenose0d369d2005-09-21 11:54:30 -0500453 sec.encode_alg[key] = SEC_ALG_WEP;
Jeff Garzikb4538722005-05-12 22:48:20 -0400454
James Ketrenos259bf1f2005-09-21 11:54:22 -0500455 done:
Jeff Garzikb4538722005-05-12 22:48:20 -0400456 if (ieee->set_security)
457 ieee->set_security(dev, &sec);
458
459 /* Do not reset port if card is in Managed mode since resetting will
460 * generate new IEEE 802.11 authentication which may end up in looping
461 * with IEEE 802.1X. If your hardware requires a reset after WEP
462 * configuration (for example... Prism2), implement the reset_port in
463 * the callbacks structures used to initialize the 802.11 stack. */
464 if (ieee->reset_on_keychange &&
465 ieee->iw_mode != IW_MODE_INFRA &&
466 ieee->reset_port && ieee->reset_port(dev)) {
467 printk(KERN_DEBUG "%s: reset_port failed\n", dev->name);
468 return -EINVAL;
469 }
470 return 0;
471}
472
473int ieee80211_wx_get_encode(struct ieee80211_device *ieee,
474 struct iw_request_info *info,
475 union iwreq_data *wrqu, char *keybuf)
476{
477 struct iw_point *erq = &(wrqu->encoding);
478 int len, key;
479 struct ieee80211_crypt_data *crypt;
James Ketrenosf1bf6632005-09-21 11:53:54 -0500480 struct ieee80211_security *sec = &ieee->sec;
Jeff Garzikb4538722005-05-12 22:48:20 -0400481
482 IEEE80211_DEBUG_WX("GET_ENCODE\n");
483
484 key = erq->flags & IW_ENCODE_INDEX;
485 if (key) {
486 if (key > WEP_KEYS)
487 return -EINVAL;
488 key--;
489 } else
490 key = ieee->tx_keyidx;
491
492 crypt = ieee->crypt[key];
493 erq->flags = key + 1;
494
James Ketrenosf1bf6632005-09-21 11:53:54 -0500495 if (!sec->enabled) {
Jeff Garzikb4538722005-05-12 22:48:20 -0400496 erq->length = 0;
497 erq->flags |= IW_ENCODE_DISABLED;
498 return 0;
499 }
500
James Ketrenosf1bf6632005-09-21 11:53:54 -0500501 len = sec->key_sizes[key];
502 memcpy(keybuf, sec->keys[key], len);
Jeff Garzikb4538722005-05-12 22:48:20 -0400503
Adrian Bunk62741152006-04-27 02:33:42 -0700504 erq->length = len;
Jeff Garzikb4538722005-05-12 22:48:20 -0400505 erq->flags |= IW_ENCODE_ENABLED;
506
507 if (ieee->open_wep)
508 erq->flags |= IW_ENCODE_OPEN;
509 else
510 erq->flags |= IW_ENCODE_RESTRICTED;
511
512 return 0;
513}
514
James Ketrenose0d369d2005-09-21 11:54:30 -0500515int ieee80211_wx_set_encodeext(struct ieee80211_device *ieee,
516 struct iw_request_info *info,
517 union iwreq_data *wrqu, char *extra)
518{
519 struct net_device *dev = ieee->dev;
520 struct iw_point *encoding = &wrqu->encoding;
521 struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
522 int i, idx, ret = 0;
James Ketrenosccd0fda2005-09-21 11:58:32 -0500523 int group_key = 0;
James Ketrenose0d369d2005-09-21 11:54:30 -0500524 const char *alg, *module;
525 struct ieee80211_crypto_ops *ops;
526 struct ieee80211_crypt_data **crypt;
527
528 struct ieee80211_security sec = {
529 .flags = 0,
530 };
531
532 idx = encoding->flags & IW_ENCODE_INDEX;
533 if (idx) {
534 if (idx < 1 || idx > WEP_KEYS)
535 return -EINVAL;
536 idx--;
537 } else
538 idx = ieee->tx_keyidx;
539
James Ketrenosccd0fda2005-09-21 11:58:32 -0500540 if (ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) {
James Ketrenose0d369d2005-09-21 11:54:30 -0500541 crypt = &ieee->crypt[idx];
James Ketrenosccd0fda2005-09-21 11:58:32 -0500542 group_key = 1;
543 } else {
Volker Braune1892772005-10-24 10:15:36 -0500544 /* some Cisco APs use idx>0 for unicast in dynamic WEP */
545 if (idx != 0 && ext->alg != IW_ENCODE_ALG_WEP)
James Ketrenose0d369d2005-09-21 11:54:30 -0500546 return -EINVAL;
547 if (ieee->iw_mode == IW_MODE_INFRA)
548 crypt = &ieee->crypt[idx];
549 else
550 return -EINVAL;
551 }
552
553 sec.flags |= SEC_ENABLED | SEC_ENCRYPT;
554 if ((encoding->flags & IW_ENCODE_DISABLED) ||
555 ext->alg == IW_ENCODE_ALG_NONE) {
556 if (*crypt)
557 ieee80211_crypt_delayed_deinit(ieee, crypt);
558
559 for (i = 0; i < WEP_KEYS; i++)
560 if (ieee->crypt[i] != NULL)
561 break;
562
563 if (i == WEP_KEYS) {
564 sec.enabled = 0;
565 sec.encrypt = 0;
566 sec.level = SEC_LEVEL_0;
567 sec.flags |= SEC_LEVEL;
568 }
569 goto done;
570 }
571
572 sec.enabled = 1;
573 sec.encrypt = 1;
574
James Ketrenosccd0fda2005-09-21 11:58:32 -0500575 if (group_key ? !ieee->host_mc_decrypt :
576 !(ieee->host_encrypt || ieee->host_decrypt ||
577 ieee->host_encrypt_msdu))
James Ketrenose0d369d2005-09-21 11:54:30 -0500578 goto skip_host_crypt;
579
580 switch (ext->alg) {
581 case IW_ENCODE_ALG_WEP:
582 alg = "WEP";
583 module = "ieee80211_crypt_wep";
584 break;
585 case IW_ENCODE_ALG_TKIP:
586 alg = "TKIP";
587 module = "ieee80211_crypt_tkip";
588 break;
589 case IW_ENCODE_ALG_CCMP:
590 alg = "CCMP";
591 module = "ieee80211_crypt_ccmp";
592 break;
593 default:
594 IEEE80211_DEBUG_WX("%s: unknown crypto alg %d\n",
595 dev->name, ext->alg);
596 ret = -EINVAL;
597 goto done;
598 }
599
600 ops = ieee80211_get_crypto_ops(alg);
601 if (ops == NULL) {
602 request_module(module);
603 ops = ieee80211_get_crypto_ops(alg);
604 }
605 if (ops == NULL) {
606 IEEE80211_DEBUG_WX("%s: unknown crypto alg %d\n",
607 dev->name, ext->alg);
608 ret = -EINVAL;
609 goto done;
610 }
611
612 if (*crypt == NULL || (*crypt)->ops != ops) {
613 struct ieee80211_crypt_data *new_crypt;
614
615 ieee80211_crypt_delayed_deinit(ieee, crypt);
616
Panagiotis Issaris0da974f2006-07-21 14:51:30 -0700617 new_crypt = kzalloc(sizeof(*new_crypt), GFP_KERNEL);
James Ketrenose0d369d2005-09-21 11:54:30 -0500618 if (new_crypt == NULL) {
619 ret = -ENOMEM;
620 goto done;
621 }
James Ketrenose0d369d2005-09-21 11:54:30 -0500622 new_crypt->ops = ops;
623 if (new_crypt->ops && try_module_get(new_crypt->ops->owner))
James Ketrenos6eb6edf2005-09-22 10:34:15 +0000624 new_crypt->priv = new_crypt->ops->init(idx);
James Ketrenose0d369d2005-09-21 11:54:30 -0500625 if (new_crypt->priv == NULL) {
626 kfree(new_crypt);
627 ret = -EINVAL;
628 goto done;
629 }
630 *crypt = new_crypt;
631 }
632
633 if (ext->key_len > 0 && (*crypt)->ops->set_key &&
634 (*crypt)->ops->set_key(ext->key, ext->key_len, ext->rx_seq,
635 (*crypt)->priv) < 0) {
636 IEEE80211_DEBUG_WX("%s: key setting failed\n", dev->name);
637 ret = -EINVAL;
638 goto done;
639 }
640
641 skip_host_crypt:
642 if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) {
643 ieee->tx_keyidx = idx;
644 sec.active_key = idx;
645 sec.flags |= SEC_ACTIVE_KEY;
646 }
647
648 if (ext->alg != IW_ENCODE_ALG_NONE) {
649 memcpy(sec.keys[idx], ext->key, ext->key_len);
650 sec.key_sizes[idx] = ext->key_len;
651 sec.flags |= (1 << idx);
652 if (ext->alg == IW_ENCODE_ALG_WEP) {
653 sec.encode_alg[idx] = SEC_ALG_WEP;
654 sec.flags |= SEC_LEVEL;
655 sec.level = SEC_LEVEL_1;
656 } else if (ext->alg == IW_ENCODE_ALG_TKIP) {
657 sec.encode_alg[idx] = SEC_ALG_TKIP;
658 sec.flags |= SEC_LEVEL;
659 sec.level = SEC_LEVEL_2;
660 } else if (ext->alg == IW_ENCODE_ALG_CCMP) {
661 sec.encode_alg[idx] = SEC_ALG_CCMP;
662 sec.flags |= SEC_LEVEL;
663 sec.level = SEC_LEVEL_3;
664 }
James Ketrenosccd0fda2005-09-21 11:58:32 -0500665 /* Don't set sec level for group keys. */
666 if (group_key)
667 sec.flags &= ~SEC_LEVEL;
James Ketrenose0d369d2005-09-21 11:54:30 -0500668 }
669 done:
670 if (ieee->set_security)
671 ieee->set_security(ieee->dev, &sec);
672
673 /*
674 * Do not reset port if card is in Managed mode since resetting will
675 * generate new IEEE 802.11 authentication which may end up in looping
676 * with IEEE 802.1X. If your hardware requires a reset after WEP
677 * configuration (for example... Prism2), implement the reset_port in
678 * the callbacks structures used to initialize the 802.11 stack.
679 */
680 if (ieee->reset_on_keychange &&
681 ieee->iw_mode != IW_MODE_INFRA &&
682 ieee->reset_port && ieee->reset_port(dev)) {
683 IEEE80211_DEBUG_WX("%s: reset_port failed\n", dev->name);
684 return -EINVAL;
685 }
686
687 return ret;
688}
689
690int ieee80211_wx_get_encodeext(struct ieee80211_device *ieee,
691 struct iw_request_info *info,
692 union iwreq_data *wrqu, char *extra)
693{
694 struct iw_point *encoding = &wrqu->encoding;
695 struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
696 struct ieee80211_security *sec = &ieee->sec;
697 int idx, max_key_len;
698
699 max_key_len = encoding->length - sizeof(*ext);
700 if (max_key_len < 0)
701 return -EINVAL;
702
703 idx = encoding->flags & IW_ENCODE_INDEX;
704 if (idx) {
705 if (idx < 1 || idx > WEP_KEYS)
706 return -EINVAL;
707 idx--;
708 } else
709 idx = ieee->tx_keyidx;
710
Volker Braune1892772005-10-24 10:15:36 -0500711 if (!ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY &&
712 ext->alg != IW_ENCODE_ALG_WEP)
James Ketrenose0d369d2005-09-21 11:54:30 -0500713 if (idx != 0 || ieee->iw_mode != IW_MODE_INFRA)
714 return -EINVAL;
715
716 encoding->flags = idx + 1;
717 memset(ext, 0, sizeof(*ext));
718
719 if (!sec->enabled) {
720 ext->alg = IW_ENCODE_ALG_NONE;
721 ext->key_len = 0;
722 encoding->flags |= IW_ENCODE_DISABLED;
723 } else {
724 if (sec->encode_alg[idx] == SEC_ALG_WEP)
725 ext->alg = IW_ENCODE_ALG_WEP;
726 else if (sec->encode_alg[idx] == SEC_ALG_TKIP)
727 ext->alg = IW_ENCODE_ALG_TKIP;
728 else if (sec->encode_alg[idx] == SEC_ALG_CCMP)
729 ext->alg = IW_ENCODE_ALG_CCMP;
730 else
731 return -EINVAL;
732
733 ext->key_len = sec->key_sizes[idx];
734 memcpy(ext->key, sec->keys[idx], ext->key_len);
735 encoding->flags |= IW_ENCODE_ENABLED;
736 if (ext->key_len &&
737 (ext->alg == IW_ENCODE_ALG_TKIP ||
738 ext->alg == IW_ENCODE_ALG_CCMP))
739 ext->ext_flags |= IW_ENCODE_EXT_TX_SEQ_VALID;
740
741 }
742
743 return 0;
744}
745
Larry Fingerdd5eeb42006-01-30 13:12:50 +0100746int ieee80211_wx_set_auth(struct net_device *dev,
747 struct iw_request_info *info,
748 union iwreq_data *wrqu,
749 char *extra)
750{
751 struct ieee80211_device *ieee = netdev_priv(dev);
752 unsigned long flags;
753 int err = 0;
754
755 spin_lock_irqsave(&ieee->lock, flags);
YOSHIFUJI Hideaki64265652007-02-09 23:24:46 +0900756
Larry Fingerdd5eeb42006-01-30 13:12:50 +0100757 switch (wrqu->param.flags & IW_AUTH_INDEX) {
758 case IW_AUTH_WPA_VERSION:
759 case IW_AUTH_CIPHER_PAIRWISE:
760 case IW_AUTH_CIPHER_GROUP:
761 case IW_AUTH_KEY_MGMT:
762 /*
763 * Host AP driver does not use these parameters and allows
764 * wpa_supplicant to control them internally.
765 */
766 break;
767 case IW_AUTH_TKIP_COUNTERMEASURES:
768 break; /* FIXME */
769 case IW_AUTH_DROP_UNENCRYPTED:
770 ieee->drop_unencrypted = !!wrqu->param.value;
771 break;
772 case IW_AUTH_80211_AUTH_ALG:
773 break; /* FIXME */
774 case IW_AUTH_WPA_ENABLED:
775 ieee->privacy_invoked = ieee->wpa_enabled = !!wrqu->param.value;
776 break;
777 case IW_AUTH_RX_UNENCRYPTED_EAPOL:
778 ieee->ieee802_1x = !!wrqu->param.value;
779 break;
780 case IW_AUTH_PRIVACY_INVOKED:
781 ieee->privacy_invoked = !!wrqu->param.value;
782 break;
783 default:
784 err = -EOPNOTSUPP;
785 break;
786 }
787 spin_unlock_irqrestore(&ieee->lock, flags);
788 return err;
789}
790
791int ieee80211_wx_get_auth(struct net_device *dev,
792 struct iw_request_info *info,
793 union iwreq_data *wrqu,
794 char *extra)
795{
796 struct ieee80211_device *ieee = netdev_priv(dev);
797 unsigned long flags;
798 int err = 0;
799
800 spin_lock_irqsave(&ieee->lock, flags);
YOSHIFUJI Hideaki64265652007-02-09 23:24:46 +0900801
Larry Fingerdd5eeb42006-01-30 13:12:50 +0100802 switch (wrqu->param.flags & IW_AUTH_INDEX) {
803 case IW_AUTH_WPA_VERSION:
804 case IW_AUTH_CIPHER_PAIRWISE:
805 case IW_AUTH_CIPHER_GROUP:
806 case IW_AUTH_KEY_MGMT:
807 case IW_AUTH_TKIP_COUNTERMEASURES: /* FIXME */
808 case IW_AUTH_80211_AUTH_ALG: /* FIXME */
809 /*
810 * Host AP driver does not use these parameters and allows
811 * wpa_supplicant to control them internally.
812 */
813 err = -EOPNOTSUPP;
814 break;
815 case IW_AUTH_DROP_UNENCRYPTED:
816 wrqu->param.value = ieee->drop_unencrypted;
817 break;
818 case IW_AUTH_WPA_ENABLED:
819 wrqu->param.value = ieee->wpa_enabled;
820 break;
821 case IW_AUTH_RX_UNENCRYPTED_EAPOL:
822 wrqu->param.value = ieee->ieee802_1x;
823 break;
824 default:
825 err = -EOPNOTSUPP;
826 break;
827 }
828 spin_unlock_irqrestore(&ieee->lock, flags);
829 return err;
830}
831
James Ketrenose0d369d2005-09-21 11:54:30 -0500832EXPORT_SYMBOL(ieee80211_wx_set_encodeext);
833EXPORT_SYMBOL(ieee80211_wx_get_encodeext);
James Ketrenose0d369d2005-09-21 11:54:30 -0500834
Jeff Garzikb4538722005-05-12 22:48:20 -0400835EXPORT_SYMBOL(ieee80211_wx_get_scan);
836EXPORT_SYMBOL(ieee80211_wx_set_encode);
837EXPORT_SYMBOL(ieee80211_wx_get_encode);
Larry Fingerdd5eeb42006-01-30 13:12:50 +0100838
839EXPORT_SYMBOL_GPL(ieee80211_wx_set_auth);
840EXPORT_SYMBOL_GPL(ieee80211_wx_get_auth);