blob: 523a137d49dd2b083406ee8517939f4c7e29c542 [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 */
Jeff Garzikb4538722005-05-12 22:48:20 -040093 iwe.cmd = SIOCGIWFREQ;
Jeff Garzikb4538722005-05-12 22:48:20 -040094 iwe.u.freq.m = network->channel;
95 iwe.u.freq.e = 0;
96 iwe.u.freq.i = 0;
97 start = iwe_stream_add_event(start, stop, &iwe, IW_EV_FREQ_LEN);
98
Larry Finger93afe3d2007-04-21 17:56:43 -050099 iwe.u.freq.m = ieee80211_channel_to_freq(ieee, network->channel);
100 iwe.u.freq.e = 6;
101 start = iwe_stream_add_event(start, stop, &iwe, IW_EV_FREQ_LEN);
102
Jeff Garzikb4538722005-05-12 22:48:20 -0400103 /* Add encryption capability */
104 iwe.cmd = SIOCGIWENCODE;
105 if (network->capability & WLAN_CAPABILITY_PRIVACY)
106 iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
107 else
108 iwe.u.data.flags = IW_ENCODE_DISABLED;
109 iwe.u.data.length = 0;
110 start = iwe_stream_add_point(start, stop, &iwe, network->ssid);
111
112 /* Add basic and extended rates */
Zhu Yi09593042006-04-13 17:17:26 +0800113 /* Rate : stuffing multiple values in a single event require a bit
114 * more of magic - Jean II */
115 current_val = start + IW_EV_LCP_LEN;
116 iwe.cmd = SIOCGIWRATE;
117 /* Those two flags are ignored... */
118 iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0;
119
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400120 for (i = 0, j = 0; i < network->rates_len;) {
Jeff Garzikb4538722005-05-12 22:48:20 -0400121 if (j < network->rates_ex_len &&
122 ((network->rates_ex[j] & 0x7F) <
123 (network->rates[i] & 0x7F)))
124 rate = network->rates_ex[j++] & 0x7F;
125 else
126 rate = network->rates[i++] & 0x7F;
Zhu Yi09593042006-04-13 17:17:26 +0800127 /* Bit rate given in 500 kb/s units (+ 0x80) */
128 iwe.u.bitrate.value = ((rate & 0x7f) * 500000);
129 /* Add new value to event */
130 current_val = iwe_stream_add_value(start, current_val, stop, &iwe, IW_EV_PARAM_LEN);
Jeff Garzikb4538722005-05-12 22:48:20 -0400131 }
132 for (; j < network->rates_ex_len; j++) {
133 rate = network->rates_ex[j] & 0x7F;
Zhu Yi09593042006-04-13 17:17:26 +0800134 /* Bit rate given in 500 kb/s units (+ 0x80) */
135 iwe.u.bitrate.value = ((rate & 0x7f) * 500000);
136 /* Add new value to event */
137 current_val = iwe_stream_add_value(start, current_val, stop, &iwe, IW_EV_PARAM_LEN);
Jeff Garzikb4538722005-05-12 22:48:20 -0400138 }
Zhu Yi09593042006-04-13 17:17:26 +0800139 /* Check if we added any rate */
140 if((current_val - start) > IW_EV_LCP_LEN)
141 start = current_val;
Jeff Garzikb4538722005-05-12 22:48:20 -0400142
143 /* Add quality statistics */
Jeff Garzikb4538722005-05-12 22:48:20 -0400144 iwe.cmd = IWEVQUAL;
James Ketrenosb1b508e2005-09-13 17:27:19 -0500145 iwe.u.qual.updated = IW_QUAL_QUAL_UPDATED | IW_QUAL_LEVEL_UPDATED |
146 IW_QUAL_NOISE_UPDATED;
147
148 if (!(network->stats.mask & IEEE80211_STATMASK_RSSI)) {
149 iwe.u.qual.updated |= IW_QUAL_QUAL_INVALID |
150 IW_QUAL_LEVEL_INVALID;
151 iwe.u.qual.qual = 0;
James Ketrenosb1b508e2005-09-13 17:27:19 -0500152 } else {
Jiri Benc757d18f2005-10-10 19:16:53 +0200153 if (ieee->perfect_rssi == ieee->worst_rssi)
154 iwe.u.qual.qual = 100;
155 else
156 iwe.u.qual.qual =
157 (100 *
158 (ieee->perfect_rssi - ieee->worst_rssi) *
159 (ieee->perfect_rssi - ieee->worst_rssi) -
160 (ieee->perfect_rssi - network->stats.rssi) *
161 (15 * (ieee->perfect_rssi - ieee->worst_rssi) +
James Ketrenos81f87522005-10-24 10:20:53 -0500162 62 * (ieee->perfect_rssi -
163 network->stats.rssi))) /
164 ((ieee->perfect_rssi -
165 ieee->worst_rssi) * (ieee->perfect_rssi -
166 ieee->worst_rssi));
James Ketrenosb1b508e2005-09-13 17:27:19 -0500167 if (iwe.u.qual.qual > 100)
168 iwe.u.qual.qual = 100;
169 else if (iwe.u.qual.qual < 1)
170 iwe.u.qual.qual = 0;
171 }
172
173 if (!(network->stats.mask & IEEE80211_STATMASK_NOISE)) {
Jeff Garzikb4538722005-05-12 22:48:20 -0400174 iwe.u.qual.updated |= IW_QUAL_NOISE_INVALID;
James Ketrenosb1b508e2005-09-13 17:27:19 -0500175 iwe.u.qual.noise = 0;
176 } else {
177 iwe.u.qual.noise = network->stats.noise;
178 }
Jeff Garzikb4538722005-05-12 22:48:20 -0400179
Zhu Yi7bd64362006-01-19 16:21:54 +0800180 if (!(network->stats.mask & IEEE80211_STATMASK_SIGNAL)) {
181 iwe.u.qual.updated |= IW_QUAL_LEVEL_INVALID;
182 iwe.u.qual.level = 0;
183 } else {
184 iwe.u.qual.level = network->stats.signal;
185 }
186
Jeff Garzikb4538722005-05-12 22:48:20 -0400187 start = iwe_stream_add_event(start, stop, &iwe, IW_EV_QUAL_LEN);
188
189 iwe.cmd = IWEVCUSTOM;
190 p = custom;
191
192 iwe.u.data.length = p - custom;
193 if (iwe.u.data.length)
194 start = iwe_stream_add_point(start, stop, &iwe, custom);
195
Zhu Yi47168082006-02-13 13:37:03 +0800196 memset(&iwe, 0, sizeof(iwe));
James Ketrenos20d64712005-09-21 11:53:43 -0500197 if (network->wpa_ie_len) {
Zhu Yi47168082006-02-13 13:37:03 +0800198 char buf[MAX_WPA_IE_LEN];
199 memcpy(buf, network->wpa_ie, network->wpa_ie_len);
200 iwe.cmd = IWEVGENIE;
201 iwe.u.data.length = network->wpa_ie_len;
Jeff Garzikb4538722005-05-12 22:48:20 -0400202 start = iwe_stream_add_point(start, stop, &iwe, buf);
203 }
204
Zhu Yi47168082006-02-13 13:37:03 +0800205 memset(&iwe, 0, sizeof(iwe));
James Ketrenos20d64712005-09-21 11:53:43 -0500206 if (network->rsn_ie_len) {
Zhu Yi47168082006-02-13 13:37:03 +0800207 char buf[MAX_WPA_IE_LEN];
208 memcpy(buf, network->rsn_ie, network->rsn_ie_len);
209 iwe.cmd = IWEVGENIE;
210 iwe.u.data.length = network->rsn_ie_len;
Jeff Garzikb4538722005-05-12 22:48:20 -0400211 start = iwe_stream_add_point(start, stop, &iwe, buf);
212 }
213
214 /* Add EXTRA: Age to display seconds since last beacon/probe response
215 * for given network. */
216 iwe.cmd = IWEVCUSTOM;
217 p = custom;
218 p += snprintf(p, MAX_CUSTOM_LEN - (p - custom),
James Ketrenos42e349f2005-09-21 11:54:07 -0500219 " Last beacon: %dms ago",
220 jiffies_to_msecs(jiffies - network->last_scanned));
Jeff Garzikb4538722005-05-12 22:48:20 -0400221 iwe.u.data.length = p - custom;
222 if (iwe.u.data.length)
223 start = iwe_stream_add_point(start, stop, &iwe, custom);
224
Zhu Yi7bd64362006-01-19 16:21:54 +0800225 /* Add spectrum management information */
226 iwe.cmd = -1;
227 p = custom;
228 p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), " Channel flags: ");
229
230 if (ieee80211_get_channel_flags(ieee, network->channel) &
231 IEEE80211_CH_INVALID) {
232 iwe.cmd = IWEVCUSTOM;
233 p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), "INVALID ");
234 }
235
236 if (ieee80211_get_channel_flags(ieee, network->channel) &
237 IEEE80211_CH_RADAR_DETECT) {
238 iwe.cmd = IWEVCUSTOM;
239 p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), "DFS ");
240 }
241
242 if (iwe.cmd == IWEVCUSTOM) {
243 iwe.u.data.length = p - custom;
244 start = iwe_stream_add_point(start, stop, &iwe, custom);
245 }
246
Jeff Garzikb4538722005-05-12 22:48:20 -0400247 return start;
248}
249
Zhu Yi55cd94a2006-01-19 16:20:59 +0800250#define SCAN_ITEM_SIZE 128
251
Jeff Garzikb4538722005-05-12 22:48:20 -0400252int ieee80211_wx_get_scan(struct ieee80211_device *ieee,
253 struct iw_request_info *info,
254 union iwreq_data *wrqu, char *extra)
255{
256 struct ieee80211_network *network;
257 unsigned long flags;
Zhu Yi55cd94a2006-01-19 16:20:59 +0800258 int err = 0;
Jeff Garzikb4538722005-05-12 22:48:20 -0400259
260 char *ev = extra;
Zhu Yi55cd94a2006-01-19 16:20:59 +0800261 char *stop = ev + wrqu->data.length;
Jeff Garzikb4538722005-05-12 22:48:20 -0400262 int i = 0;
263
264 IEEE80211_DEBUG_WX("Getting scan\n");
265
266 spin_lock_irqsave(&ieee->lock, flags);
267
268 list_for_each_entry(network, &ieee->network_list, list) {
269 i++;
Zhu Yi55cd94a2006-01-19 16:20:59 +0800270 if (stop - ev < SCAN_ITEM_SIZE) {
271 err = -E2BIG;
272 break;
273 }
274
Jeff Garzikb4538722005-05-12 22:48:20 -0400275 if (ieee->scan_age == 0 ||
276 time_after(network->last_scanned + ieee->scan_age, jiffies))
Larry Fingerd94606e2006-03-03 16:21:55 -0600277 ev = ieee80211_translate_scan(ieee, ev, stop, network);
Jeff Garzikb4538722005-05-12 22:48:20 -0400278 else
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400279 IEEE80211_DEBUG_SCAN("Not showing network '%s ("
James Ketrenos42e349f2005-09-21 11:54:07 -0500280 MAC_FMT ")' due to age (%dms).\n",
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400281 escape_essid(network->ssid,
282 network->ssid_len),
283 MAC_ARG(network->bssid),
James Ketrenos42e349f2005-09-21 11:54:07 -0500284 jiffies_to_msecs(jiffies -
285 network->
286 last_scanned));
Jeff Garzikb4538722005-05-12 22:48:20 -0400287 }
288
289 spin_unlock_irqrestore(&ieee->lock, flags);
290
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400291 wrqu->data.length = ev - extra;
Jeff Garzikb4538722005-05-12 22:48:20 -0400292 wrqu->data.flags = 0;
293
294 IEEE80211_DEBUG_WX("exit: %d networks returned.\n", i);
295
Zhu Yi55cd94a2006-01-19 16:20:59 +0800296 return err;
Jeff Garzikb4538722005-05-12 22:48:20 -0400297}
298
299int ieee80211_wx_set_encode(struct ieee80211_device *ieee,
300 struct iw_request_info *info,
301 union iwreq_data *wrqu, char *keybuf)
302{
303 struct iw_point *erq = &(wrqu->encoding);
304 struct net_device *dev = ieee->dev;
305 struct ieee80211_security sec = {
306 .flags = 0
307 };
308 int i, key, key_provided, len;
309 struct ieee80211_crypt_data **crypt;
Johannes Berga4bf26f2005-12-31 11:35:20 +0100310 int host_crypto = ieee->host_encrypt || ieee->host_decrypt || ieee->host_build_iv;
Jeff Garzikb4538722005-05-12 22:48:20 -0400311
312 IEEE80211_DEBUG_WX("SET_ENCODE\n");
313
314 key = erq->flags & IW_ENCODE_INDEX;
315 if (key) {
316 if (key > WEP_KEYS)
317 return -EINVAL;
318 key--;
319 key_provided = 1;
320 } else {
321 key_provided = 0;
322 key = ieee->tx_keyidx;
323 }
324
325 IEEE80211_DEBUG_WX("Key: %d [%s]\n", key, key_provided ?
326 "provided" : "default");
327
328 crypt = &ieee->crypt[key];
329
330 if (erq->flags & IW_ENCODE_DISABLED) {
331 if (key_provided && *crypt) {
332 IEEE80211_DEBUG_WX("Disabling encryption on key %d.\n",
333 key);
334 ieee80211_crypt_delayed_deinit(ieee, crypt);
335 } else
336 IEEE80211_DEBUG_WX("Disabling encryption.\n");
337
338 /* Check all the keys to see if any are still configured,
339 * and if no key index was provided, de-init them all */
340 for (i = 0; i < WEP_KEYS; i++) {
341 if (ieee->crypt[i] != NULL) {
342 if (key_provided)
343 break;
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400344 ieee80211_crypt_delayed_deinit(ieee,
345 &ieee->crypt[i]);
Jeff Garzikb4538722005-05-12 22:48:20 -0400346 }
347 }
348
349 if (i == WEP_KEYS) {
350 sec.enabled = 0;
James Ketrenosf1bf6632005-09-21 11:53:54 -0500351 sec.encrypt = 0;
Jeff Garzikb4538722005-05-12 22:48:20 -0400352 sec.level = SEC_LEVEL_0;
James Ketrenos259bf1f2005-09-21 11:54:22 -0500353 sec.flags |= SEC_ENABLED | SEC_LEVEL | SEC_ENCRYPT;
Jeff Garzikb4538722005-05-12 22:48:20 -0400354 }
355
356 goto done;
357 }
358
Jeff Garzikb4538722005-05-12 22:48:20 -0400359 sec.enabled = 1;
James Ketrenosf1bf6632005-09-21 11:53:54 -0500360 sec.encrypt = 1;
James Ketrenos259bf1f2005-09-21 11:54:22 -0500361 sec.flags |= SEC_ENABLED | SEC_ENCRYPT;
Jeff Garzikb4538722005-05-12 22:48:20 -0400362
363 if (*crypt != NULL && (*crypt)->ops != NULL &&
364 strcmp((*crypt)->ops->name, "WEP") != 0) {
365 /* changing to use WEP; deinit previously used algorithm
366 * on this key */
367 ieee80211_crypt_delayed_deinit(ieee, crypt);
368 }
369
James Ketrenosf1bf6632005-09-21 11:53:54 -0500370 if (*crypt == NULL && host_crypto) {
Jeff Garzikb4538722005-05-12 22:48:20 -0400371 struct ieee80211_crypt_data *new_crypt;
372
373 /* take WEP into use */
Panagiotis Issaris0da974f2006-07-21 14:51:30 -0700374 new_crypt = kzalloc(sizeof(struct ieee80211_crypt_data),
Jeff Garzikb4538722005-05-12 22:48:20 -0400375 GFP_KERNEL);
376 if (new_crypt == NULL)
377 return -ENOMEM;
Jeff Garzikb4538722005-05-12 22:48:20 -0400378 new_crypt->ops = ieee80211_get_crypto_ops("WEP");
379 if (!new_crypt->ops) {
380 request_module("ieee80211_crypt_wep");
381 new_crypt->ops = ieee80211_get_crypto_ops("WEP");
382 }
383
384 if (new_crypt->ops && try_module_get(new_crypt->ops->owner))
James Ketrenos6eb6edf2005-09-22 10:34:15 +0000385 new_crypt->priv = new_crypt->ops->init(key);
Jeff Garzikb4538722005-05-12 22:48:20 -0400386
387 if (!new_crypt->ops || !new_crypt->priv) {
388 kfree(new_crypt);
389 new_crypt = NULL;
390
391 printk(KERN_WARNING "%s: could not initialize WEP: "
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400392 "load module ieee80211_crypt_wep\n", dev->name);
Jeff Garzikb4538722005-05-12 22:48:20 -0400393 return -EOPNOTSUPP;
394 }
395 *crypt = new_crypt;
396 }
397
398 /* If a new key was provided, set it up */
399 if (erq->length > 0) {
400 len = erq->length <= 5 ? 5 : 13;
401 memcpy(sec.keys[key], keybuf, erq->length);
402 if (len > erq->length)
403 memset(sec.keys[key] + erq->length, 0,
404 len - erq->length);
405 IEEE80211_DEBUG_WX("Setting key %d to '%s' (%d:%d bytes)\n",
406 key, escape_essid(sec.keys[key], len),
407 erq->length, len);
408 sec.key_sizes[key] = len;
James Ketrenosf1bf6632005-09-21 11:53:54 -0500409 if (*crypt)
410 (*crypt)->ops->set_key(sec.keys[key], len, NULL,
411 (*crypt)->priv);
Jeff Garzikb4538722005-05-12 22:48:20 -0400412 sec.flags |= (1 << key);
413 /* This ensures a key will be activated if no key is
414 * explicitely set */
415 if (key == sec.active_key)
416 sec.flags |= SEC_ACTIVE_KEY;
Jeff Garzikb4538722005-05-12 22:48:20 -0400417
James Ketrenosf1bf6632005-09-21 11:53:54 -0500418 } else {
419 if (host_crypto) {
420 len = (*crypt)->ops->get_key(sec.keys[key], WEP_KEY_LEN,
421 NULL, (*crypt)->priv);
422 if (len == 0) {
423 /* Set a default key of all 0 */
424 IEEE80211_DEBUG_WX("Setting key %d to all "
425 "zero.\n", key);
426 memset(sec.keys[key], 0, 13);
427 (*crypt)->ops->set_key(sec.keys[key], 13, NULL,
428 (*crypt)->priv);
429 sec.key_sizes[key] = 13;
430 sec.flags |= (1 << key);
431 }
432 }
Jeff Garzikb4538722005-05-12 22:48:20 -0400433 /* No key data - just set the default TX key index */
434 if (key_provided) {
James Ketrenosf1bf6632005-09-21 11:53:54 -0500435 IEEE80211_DEBUG_WX("Setting key %d to default Tx "
436 "key.\n", key);
Jeff Garzikb4538722005-05-12 22:48:20 -0400437 ieee->tx_keyidx = key;
438 sec.active_key = key;
439 sec.flags |= SEC_ACTIVE_KEY;
440 }
441 }
James Ketrenos7dc888f2005-09-21 11:58:38 -0500442 if (erq->flags & (IW_ENCODE_OPEN | IW_ENCODE_RESTRICTED)) {
443 ieee->open_wep = !(erq->flags & IW_ENCODE_RESTRICTED);
444 sec.auth_mode = ieee->open_wep ? WLAN_AUTH_OPEN :
445 WLAN_AUTH_SHARED_KEY;
446 sec.flags |= SEC_AUTH_MODE;
447 IEEE80211_DEBUG_WX("Auth: %s\n",
448 sec.auth_mode == WLAN_AUTH_OPEN ?
449 "OPEN" : "SHARED KEY");
450 }
Jeff Garzikb4538722005-05-12 22:48:20 -0400451
452 /* For now we just support WEP, so only set that security level...
453 * TODO: When WPA is added this is one place that needs to change */
454 sec.flags |= SEC_LEVEL;
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400455 sec.level = SEC_LEVEL_1; /* 40 and 104 bit WEP */
James Ketrenose0d369d2005-09-21 11:54:30 -0500456 sec.encode_alg[key] = SEC_ALG_WEP;
Jeff Garzikb4538722005-05-12 22:48:20 -0400457
James Ketrenos259bf1f2005-09-21 11:54:22 -0500458 done:
Jeff Garzikb4538722005-05-12 22:48:20 -0400459 if (ieee->set_security)
460 ieee->set_security(dev, &sec);
461
462 /* Do not reset port if card is in Managed mode since resetting will
463 * generate new IEEE 802.11 authentication which may end up in looping
464 * with IEEE 802.1X. If your hardware requires a reset after WEP
465 * configuration (for example... Prism2), implement the reset_port in
466 * the callbacks structures used to initialize the 802.11 stack. */
467 if (ieee->reset_on_keychange &&
468 ieee->iw_mode != IW_MODE_INFRA &&
469 ieee->reset_port && ieee->reset_port(dev)) {
470 printk(KERN_DEBUG "%s: reset_port failed\n", dev->name);
471 return -EINVAL;
472 }
473 return 0;
474}
475
476int ieee80211_wx_get_encode(struct ieee80211_device *ieee,
477 struct iw_request_info *info,
478 union iwreq_data *wrqu, char *keybuf)
479{
480 struct iw_point *erq = &(wrqu->encoding);
481 int len, key;
482 struct ieee80211_crypt_data *crypt;
James Ketrenosf1bf6632005-09-21 11:53:54 -0500483 struct ieee80211_security *sec = &ieee->sec;
Jeff Garzikb4538722005-05-12 22:48:20 -0400484
485 IEEE80211_DEBUG_WX("GET_ENCODE\n");
486
487 key = erq->flags & IW_ENCODE_INDEX;
488 if (key) {
489 if (key > WEP_KEYS)
490 return -EINVAL;
491 key--;
492 } else
493 key = ieee->tx_keyidx;
494
495 crypt = ieee->crypt[key];
496 erq->flags = key + 1;
497
James Ketrenosf1bf6632005-09-21 11:53:54 -0500498 if (!sec->enabled) {
Jeff Garzikb4538722005-05-12 22:48:20 -0400499 erq->length = 0;
500 erq->flags |= IW_ENCODE_DISABLED;
501 return 0;
502 }
503
James Ketrenosf1bf6632005-09-21 11:53:54 -0500504 len = sec->key_sizes[key];
505 memcpy(keybuf, sec->keys[key], len);
Jeff Garzikb4538722005-05-12 22:48:20 -0400506
Adrian Bunk62741152006-04-27 02:33:42 -0700507 erq->length = len;
Jeff Garzikb4538722005-05-12 22:48:20 -0400508 erq->flags |= IW_ENCODE_ENABLED;
509
510 if (ieee->open_wep)
511 erq->flags |= IW_ENCODE_OPEN;
512 else
513 erq->flags |= IW_ENCODE_RESTRICTED;
514
515 return 0;
516}
517
James Ketrenose0d369d2005-09-21 11:54:30 -0500518int ieee80211_wx_set_encodeext(struct ieee80211_device *ieee,
519 struct iw_request_info *info,
520 union iwreq_data *wrqu, char *extra)
521{
522 struct net_device *dev = ieee->dev;
523 struct iw_point *encoding = &wrqu->encoding;
524 struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
525 int i, idx, ret = 0;
James Ketrenosccd0fda2005-09-21 11:58:32 -0500526 int group_key = 0;
James Ketrenose0d369d2005-09-21 11:54:30 -0500527 const char *alg, *module;
528 struct ieee80211_crypto_ops *ops;
529 struct ieee80211_crypt_data **crypt;
530
531 struct ieee80211_security sec = {
532 .flags = 0,
533 };
534
535 idx = encoding->flags & IW_ENCODE_INDEX;
536 if (idx) {
537 if (idx < 1 || idx > WEP_KEYS)
538 return -EINVAL;
539 idx--;
540 } else
541 idx = ieee->tx_keyidx;
542
James Ketrenosccd0fda2005-09-21 11:58:32 -0500543 if (ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) {
James Ketrenose0d369d2005-09-21 11:54:30 -0500544 crypt = &ieee->crypt[idx];
James Ketrenosccd0fda2005-09-21 11:58:32 -0500545 group_key = 1;
546 } else {
Volker Braune1892772005-10-24 10:15:36 -0500547 /* some Cisco APs use idx>0 for unicast in dynamic WEP */
548 if (idx != 0 && ext->alg != IW_ENCODE_ALG_WEP)
James Ketrenose0d369d2005-09-21 11:54:30 -0500549 return -EINVAL;
550 if (ieee->iw_mode == IW_MODE_INFRA)
551 crypt = &ieee->crypt[idx];
552 else
553 return -EINVAL;
554 }
555
556 sec.flags |= SEC_ENABLED | SEC_ENCRYPT;
557 if ((encoding->flags & IW_ENCODE_DISABLED) ||
558 ext->alg == IW_ENCODE_ALG_NONE) {
559 if (*crypt)
560 ieee80211_crypt_delayed_deinit(ieee, crypt);
561
562 for (i = 0; i < WEP_KEYS; i++)
563 if (ieee->crypt[i] != NULL)
564 break;
565
566 if (i == WEP_KEYS) {
567 sec.enabled = 0;
568 sec.encrypt = 0;
569 sec.level = SEC_LEVEL_0;
570 sec.flags |= SEC_LEVEL;
571 }
572 goto done;
573 }
574
575 sec.enabled = 1;
576 sec.encrypt = 1;
577
James Ketrenosccd0fda2005-09-21 11:58:32 -0500578 if (group_key ? !ieee->host_mc_decrypt :
579 !(ieee->host_encrypt || ieee->host_decrypt ||
580 ieee->host_encrypt_msdu))
James Ketrenose0d369d2005-09-21 11:54:30 -0500581 goto skip_host_crypt;
582
583 switch (ext->alg) {
584 case IW_ENCODE_ALG_WEP:
585 alg = "WEP";
586 module = "ieee80211_crypt_wep";
587 break;
588 case IW_ENCODE_ALG_TKIP:
589 alg = "TKIP";
590 module = "ieee80211_crypt_tkip";
591 break;
592 case IW_ENCODE_ALG_CCMP:
593 alg = "CCMP";
594 module = "ieee80211_crypt_ccmp";
595 break;
596 default:
597 IEEE80211_DEBUG_WX("%s: unknown crypto alg %d\n",
598 dev->name, ext->alg);
599 ret = -EINVAL;
600 goto done;
601 }
602
603 ops = ieee80211_get_crypto_ops(alg);
604 if (ops == NULL) {
605 request_module(module);
606 ops = ieee80211_get_crypto_ops(alg);
607 }
608 if (ops == NULL) {
609 IEEE80211_DEBUG_WX("%s: unknown crypto alg %d\n",
610 dev->name, ext->alg);
611 ret = -EINVAL;
612 goto done;
613 }
614
615 if (*crypt == NULL || (*crypt)->ops != ops) {
616 struct ieee80211_crypt_data *new_crypt;
617
618 ieee80211_crypt_delayed_deinit(ieee, crypt);
619
Panagiotis Issaris0da974f2006-07-21 14:51:30 -0700620 new_crypt = kzalloc(sizeof(*new_crypt), GFP_KERNEL);
James Ketrenose0d369d2005-09-21 11:54:30 -0500621 if (new_crypt == NULL) {
622 ret = -ENOMEM;
623 goto done;
624 }
James Ketrenose0d369d2005-09-21 11:54:30 -0500625 new_crypt->ops = ops;
626 if (new_crypt->ops && try_module_get(new_crypt->ops->owner))
James Ketrenos6eb6edf2005-09-22 10:34:15 +0000627 new_crypt->priv = new_crypt->ops->init(idx);
James Ketrenose0d369d2005-09-21 11:54:30 -0500628 if (new_crypt->priv == NULL) {
629 kfree(new_crypt);
630 ret = -EINVAL;
631 goto done;
632 }
633 *crypt = new_crypt;
634 }
635
636 if (ext->key_len > 0 && (*crypt)->ops->set_key &&
637 (*crypt)->ops->set_key(ext->key, ext->key_len, ext->rx_seq,
638 (*crypt)->priv) < 0) {
639 IEEE80211_DEBUG_WX("%s: key setting failed\n", dev->name);
640 ret = -EINVAL;
641 goto done;
642 }
643
644 skip_host_crypt:
645 if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) {
646 ieee->tx_keyidx = idx;
647 sec.active_key = idx;
648 sec.flags |= SEC_ACTIVE_KEY;
649 }
650
651 if (ext->alg != IW_ENCODE_ALG_NONE) {
652 memcpy(sec.keys[idx], ext->key, ext->key_len);
653 sec.key_sizes[idx] = ext->key_len;
654 sec.flags |= (1 << idx);
655 if (ext->alg == IW_ENCODE_ALG_WEP) {
656 sec.encode_alg[idx] = SEC_ALG_WEP;
657 sec.flags |= SEC_LEVEL;
658 sec.level = SEC_LEVEL_1;
659 } else if (ext->alg == IW_ENCODE_ALG_TKIP) {
660 sec.encode_alg[idx] = SEC_ALG_TKIP;
661 sec.flags |= SEC_LEVEL;
662 sec.level = SEC_LEVEL_2;
663 } else if (ext->alg == IW_ENCODE_ALG_CCMP) {
664 sec.encode_alg[idx] = SEC_ALG_CCMP;
665 sec.flags |= SEC_LEVEL;
666 sec.level = SEC_LEVEL_3;
667 }
James Ketrenosccd0fda2005-09-21 11:58:32 -0500668 /* Don't set sec level for group keys. */
669 if (group_key)
670 sec.flags &= ~SEC_LEVEL;
James Ketrenose0d369d2005-09-21 11:54:30 -0500671 }
672 done:
673 if (ieee->set_security)
674 ieee->set_security(ieee->dev, &sec);
675
676 /*
677 * Do not reset port if card is in Managed mode since resetting will
678 * generate new IEEE 802.11 authentication which may end up in looping
679 * with IEEE 802.1X. If your hardware requires a reset after WEP
680 * configuration (for example... Prism2), implement the reset_port in
681 * the callbacks structures used to initialize the 802.11 stack.
682 */
683 if (ieee->reset_on_keychange &&
684 ieee->iw_mode != IW_MODE_INFRA &&
685 ieee->reset_port && ieee->reset_port(dev)) {
686 IEEE80211_DEBUG_WX("%s: reset_port failed\n", dev->name);
687 return -EINVAL;
688 }
689
690 return ret;
691}
692
693int ieee80211_wx_get_encodeext(struct ieee80211_device *ieee,
694 struct iw_request_info *info,
695 union iwreq_data *wrqu, char *extra)
696{
697 struct iw_point *encoding = &wrqu->encoding;
698 struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
699 struct ieee80211_security *sec = &ieee->sec;
700 int idx, max_key_len;
701
702 max_key_len = encoding->length - sizeof(*ext);
703 if (max_key_len < 0)
704 return -EINVAL;
705
706 idx = encoding->flags & IW_ENCODE_INDEX;
707 if (idx) {
708 if (idx < 1 || idx > WEP_KEYS)
709 return -EINVAL;
710 idx--;
711 } else
712 idx = ieee->tx_keyidx;
713
Volker Braune1892772005-10-24 10:15:36 -0500714 if (!ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY &&
715 ext->alg != IW_ENCODE_ALG_WEP)
James Ketrenose0d369d2005-09-21 11:54:30 -0500716 if (idx != 0 || ieee->iw_mode != IW_MODE_INFRA)
717 return -EINVAL;
718
719 encoding->flags = idx + 1;
720 memset(ext, 0, sizeof(*ext));
721
722 if (!sec->enabled) {
723 ext->alg = IW_ENCODE_ALG_NONE;
724 ext->key_len = 0;
725 encoding->flags |= IW_ENCODE_DISABLED;
726 } else {
727 if (sec->encode_alg[idx] == SEC_ALG_WEP)
728 ext->alg = IW_ENCODE_ALG_WEP;
729 else if (sec->encode_alg[idx] == SEC_ALG_TKIP)
730 ext->alg = IW_ENCODE_ALG_TKIP;
731 else if (sec->encode_alg[idx] == SEC_ALG_CCMP)
732 ext->alg = IW_ENCODE_ALG_CCMP;
733 else
734 return -EINVAL;
735
736 ext->key_len = sec->key_sizes[idx];
737 memcpy(ext->key, sec->keys[idx], ext->key_len);
738 encoding->flags |= IW_ENCODE_ENABLED;
739 if (ext->key_len &&
740 (ext->alg == IW_ENCODE_ALG_TKIP ||
741 ext->alg == IW_ENCODE_ALG_CCMP))
742 ext->ext_flags |= IW_ENCODE_EXT_TX_SEQ_VALID;
743
744 }
745
746 return 0;
747}
748
Larry Fingerdd5eeb42006-01-30 13:12:50 +0100749int ieee80211_wx_set_auth(struct net_device *dev,
750 struct iw_request_info *info,
751 union iwreq_data *wrqu,
752 char *extra)
753{
754 struct ieee80211_device *ieee = netdev_priv(dev);
755 unsigned long flags;
756 int err = 0;
757
758 spin_lock_irqsave(&ieee->lock, flags);
YOSHIFUJI Hideaki64265652007-02-09 23:24:46 +0900759
Larry Fingerdd5eeb42006-01-30 13:12:50 +0100760 switch (wrqu->param.flags & IW_AUTH_INDEX) {
761 case IW_AUTH_WPA_VERSION:
762 case IW_AUTH_CIPHER_PAIRWISE:
763 case IW_AUTH_CIPHER_GROUP:
764 case IW_AUTH_KEY_MGMT:
765 /*
766 * Host AP driver does not use these parameters and allows
767 * wpa_supplicant to control them internally.
768 */
769 break;
770 case IW_AUTH_TKIP_COUNTERMEASURES:
771 break; /* FIXME */
772 case IW_AUTH_DROP_UNENCRYPTED:
773 ieee->drop_unencrypted = !!wrqu->param.value;
774 break;
775 case IW_AUTH_80211_AUTH_ALG:
776 break; /* FIXME */
777 case IW_AUTH_WPA_ENABLED:
778 ieee->privacy_invoked = ieee->wpa_enabled = !!wrqu->param.value;
779 break;
780 case IW_AUTH_RX_UNENCRYPTED_EAPOL:
781 ieee->ieee802_1x = !!wrqu->param.value;
782 break;
783 case IW_AUTH_PRIVACY_INVOKED:
784 ieee->privacy_invoked = !!wrqu->param.value;
785 break;
786 default:
787 err = -EOPNOTSUPP;
788 break;
789 }
790 spin_unlock_irqrestore(&ieee->lock, flags);
791 return err;
792}
793
794int ieee80211_wx_get_auth(struct net_device *dev,
795 struct iw_request_info *info,
796 union iwreq_data *wrqu,
797 char *extra)
798{
799 struct ieee80211_device *ieee = netdev_priv(dev);
800 unsigned long flags;
801 int err = 0;
802
803 spin_lock_irqsave(&ieee->lock, flags);
YOSHIFUJI Hideaki64265652007-02-09 23:24:46 +0900804
Larry Fingerdd5eeb42006-01-30 13:12:50 +0100805 switch (wrqu->param.flags & IW_AUTH_INDEX) {
806 case IW_AUTH_WPA_VERSION:
807 case IW_AUTH_CIPHER_PAIRWISE:
808 case IW_AUTH_CIPHER_GROUP:
809 case IW_AUTH_KEY_MGMT:
810 case IW_AUTH_TKIP_COUNTERMEASURES: /* FIXME */
811 case IW_AUTH_80211_AUTH_ALG: /* FIXME */
812 /*
813 * Host AP driver does not use these parameters and allows
814 * wpa_supplicant to control them internally.
815 */
816 err = -EOPNOTSUPP;
817 break;
818 case IW_AUTH_DROP_UNENCRYPTED:
819 wrqu->param.value = ieee->drop_unencrypted;
820 break;
821 case IW_AUTH_WPA_ENABLED:
822 wrqu->param.value = ieee->wpa_enabled;
823 break;
824 case IW_AUTH_RX_UNENCRYPTED_EAPOL:
825 wrqu->param.value = ieee->ieee802_1x;
826 break;
827 default:
828 err = -EOPNOTSUPP;
829 break;
830 }
831 spin_unlock_irqrestore(&ieee->lock, flags);
832 return err;
833}
834
James Ketrenose0d369d2005-09-21 11:54:30 -0500835EXPORT_SYMBOL(ieee80211_wx_set_encodeext);
836EXPORT_SYMBOL(ieee80211_wx_get_encodeext);
James Ketrenose0d369d2005-09-21 11:54:30 -0500837
Jeff Garzikb4538722005-05-12 22:48:20 -0400838EXPORT_SYMBOL(ieee80211_wx_get_scan);
839EXPORT_SYMBOL(ieee80211_wx_set_encode);
840EXPORT_SYMBOL(ieee80211_wx_get_encode);
Larry Fingerdd5eeb42006-01-30 13:12:50 +0100841
842EXPORT_SYMBOL_GPL(ieee80211_wx_set_auth);
843EXPORT_SYMBOL_GPL(ieee80211_wx_get_auth);