blob: b885fd189403e8da67ae0dd967b94a7f45ae9df9 [file] [log] [blame]
Jeff Garzikb4538722005-05-12 22:48:20 -04001/******************************************************************************
2
James Ketrenosebeaddc2005-09-21 11:58:43 -05003 Copyright(c) 2004-2005 Intel Corporation. All rights reserved.
Jeff Garzikb4538722005-05-12 22:48:20 -04004
5 Portions of this file are based on the WEP enablement code provided by the
6 Host AP project hostap-drivers v0.1.3
7 Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen
8 <jkmaline@cc.hut.fi>
9 Copyright (c) 2002-2003, Jouni Malinen <jkmaline@cc.hut.fi>
10
11 This program is free software; you can redistribute it and/or modify it
12 under the terms of version 2 of the GNU General Public License as
13 published by the Free Software Foundation.
14
15 This program is distributed in the hope that it will be useful, but WITHOUT
16 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
17 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
18 more details.
19
20 You should have received a copy of the GNU General Public License along with
21 this program; if not, write to the Free Software Foundation, Inc., 59
22 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23
24 The full GNU General Public License is included in this distribution in the
25 file called LICENSE.
26
27 Contact Information:
28 James P. Ketrenos <ipw2100-admin@linux.intel.com>
29 Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
30
31******************************************************************************/
Jeff Garzikbbeec902005-09-07 00:27:54 -040032
Jeff Garzikb4538722005-05-12 22:48:20 -040033#include <linux/kmod.h>
34#include <linux/module.h>
James Ketrenos42e349f2005-09-21 11:54:07 -050035#include <linux/jiffies.h>
Jeff Garzikb4538722005-05-12 22:48:20 -040036
37#include <net/ieee80211.h>
Jeff Garzikbbeec902005-09-07 00:27:54 -040038#include <linux/wireless.h>
39
Jeff Garzikb4538722005-05-12 22:48:20 -040040static const char *ieee80211_modes[] = {
41 "?", "a", "b", "ab", "g", "ag", "bg", "abg"
42};
43
44#define MAX_CUSTOM_LEN 64
Larry Fingerd94606e2006-03-03 16:21:55 -060045static char *ieee80211_translate_scan(struct ieee80211_device *ieee,
Jeff Garzik0edd5b42005-09-07 00:48:31 -040046 char *start, char *stop,
Jeff Garzikb4538722005-05-12 22:48:20 -040047 struct ieee80211_network *network)
48{
49 char custom[MAX_CUSTOM_LEN];
50 char *p;
51 struct iw_event iwe;
52 int i, j;
53 u8 max_rate, rate;
54
55 /* First entry *MUST* be the AP MAC address */
56 iwe.cmd = SIOCGIWAP;
57 iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
58 memcpy(iwe.u.ap_addr.sa_data, network->bssid, ETH_ALEN);
59 start = iwe_stream_add_event(start, stop, &iwe, IW_EV_ADDR_LEN);
60
61 /* Remaining entries will be displayed in the order we provide them */
62
63 /* Add the ESSID */
64 iwe.cmd = SIOCGIWESSID;
65 iwe.u.data.flags = 1;
66 if (network->flags & NETWORK_EMPTY_ESSID) {
67 iwe.u.data.length = sizeof("<hidden>");
68 start = iwe_stream_add_point(start, stop, &iwe, "<hidden>");
69 } else {
Jeff Garzik0edd5b42005-09-07 00:48:31 -040070 iwe.u.data.length = min(network->ssid_len, (u8) 32);
Jeff Garzikb4538722005-05-12 22:48:20 -040071 start = iwe_stream_add_point(start, stop, &iwe, network->ssid);
72 }
73
74 /* Add the protocol name */
75 iwe.cmd = SIOCGIWNAME;
Jeff Garzik0edd5b42005-09-07 00:48:31 -040076 snprintf(iwe.u.name, IFNAMSIZ, "IEEE 802.11%s",
77 ieee80211_modes[network->mode]);
Jeff Garzikb4538722005-05-12 22:48:20 -040078 start = iwe_stream_add_event(start, stop, &iwe, IW_EV_CHAR_LEN);
79
Jeff Garzik0edd5b42005-09-07 00:48:31 -040080 /* Add mode */
81 iwe.cmd = SIOCGIWMODE;
82 if (network->capability & (WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS)) {
Jeff Garzik1b5cca32005-08-15 00:32:15 -040083 if (network->capability & WLAN_CAPABILITY_ESS)
Jeff Garzikb4538722005-05-12 22:48:20 -040084 iwe.u.mode = IW_MODE_MASTER;
85 else
86 iwe.u.mode = IW_MODE_ADHOC;
87
Jeff Garzik0edd5b42005-09-07 00:48:31 -040088 start = iwe_stream_add_event(start, stop, &iwe, IW_EV_UINT_LEN);
Jeff Garzikb4538722005-05-12 22:48:20 -040089 }
90
Jeff Garzik0edd5b42005-09-07 00:48:31 -040091 /* Add frequency/channel */
Jeff Garzikb4538722005-05-12 22:48:20 -040092 iwe.cmd = SIOCGIWFREQ;
93/* iwe.u.freq.m = ieee80211_frequency(network->channel, network->mode);
94 iwe.u.freq.e = 3; */
95 iwe.u.freq.m = network->channel;
96 iwe.u.freq.e = 0;
97 iwe.u.freq.i = 0;
98 start = iwe_stream_add_event(start, stop, &iwe, IW_EV_FREQ_LEN);
99
100 /* 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 */
110 max_rate = 0;
111 p = custom;
112 p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), " Rates (Mb/s): ");
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400113 for (i = 0, j = 0; i < network->rates_len;) {
Jeff Garzikb4538722005-05-12 22:48:20 -0400114 if (j < network->rates_ex_len &&
115 ((network->rates_ex[j] & 0x7F) <
116 (network->rates[i] & 0x7F)))
117 rate = network->rates_ex[j++] & 0x7F;
118 else
119 rate = network->rates[i++] & 0x7F;
120 if (rate > max_rate)
121 max_rate = rate;
122 p += snprintf(p, MAX_CUSTOM_LEN - (p - custom),
123 "%d%s ", rate >> 1, (rate & 1) ? ".5" : "");
124 }
125 for (; j < network->rates_ex_len; j++) {
126 rate = network->rates_ex[j] & 0x7F;
127 p += snprintf(p, MAX_CUSTOM_LEN - (p - custom),
128 "%d%s ", rate >> 1, (rate & 1) ? ".5" : "");
129 if (rate > max_rate)
130 max_rate = rate;
131 }
132
133 iwe.cmd = SIOCGIWRATE;
134 iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0;
135 iwe.u.bitrate.value = max_rate * 500000;
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400136 start = iwe_stream_add_event(start, stop, &iwe, IW_EV_PARAM_LEN);
Jeff Garzikb4538722005-05-12 22:48:20 -0400137
138 iwe.cmd = IWEVCUSTOM;
139 iwe.u.data.length = p - custom;
140 if (iwe.u.data.length)
141 start = iwe_stream_add_point(start, stop, &iwe, custom);
142
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 */
374 new_crypt = kmalloc(sizeof(struct ieee80211_crypt_data),
375 GFP_KERNEL);
376 if (new_crypt == NULL)
377 return -ENOMEM;
378 memset(new_crypt, 0, sizeof(struct ieee80211_crypt_data));
379 new_crypt->ops = ieee80211_get_crypto_ops("WEP");
380 if (!new_crypt->ops) {
381 request_module("ieee80211_crypt_wep");
382 new_crypt->ops = ieee80211_get_crypto_ops("WEP");
383 }
384
385 if (new_crypt->ops && try_module_get(new_crypt->ops->owner))
James Ketrenos6eb6edf2005-09-22 10:34:15 +0000386 new_crypt->priv = new_crypt->ops->init(key);
Jeff Garzikb4538722005-05-12 22:48:20 -0400387
388 if (!new_crypt->ops || !new_crypt->priv) {
389 kfree(new_crypt);
390 new_crypt = NULL;
391
392 printk(KERN_WARNING "%s: could not initialize WEP: "
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400393 "load module ieee80211_crypt_wep\n", dev->name);
Jeff Garzikb4538722005-05-12 22:48:20 -0400394 return -EOPNOTSUPP;
395 }
396 *crypt = new_crypt;
397 }
398
399 /* If a new key was provided, set it up */
400 if (erq->length > 0) {
401 len = erq->length <= 5 ? 5 : 13;
402 memcpy(sec.keys[key], keybuf, erq->length);
403 if (len > erq->length)
404 memset(sec.keys[key] + erq->length, 0,
405 len - erq->length);
406 IEEE80211_DEBUG_WX("Setting key %d to '%s' (%d:%d bytes)\n",
407 key, escape_essid(sec.keys[key], len),
408 erq->length, len);
409 sec.key_sizes[key] = len;
James Ketrenosf1bf6632005-09-21 11:53:54 -0500410 if (*crypt)
411 (*crypt)->ops->set_key(sec.keys[key], len, NULL,
412 (*crypt)->priv);
Jeff Garzikb4538722005-05-12 22:48:20 -0400413 sec.flags |= (1 << key);
414 /* This ensures a key will be activated if no key is
415 * explicitely set */
416 if (key == sec.active_key)
417 sec.flags |= SEC_ACTIVE_KEY;
Jeff Garzikb4538722005-05-12 22:48:20 -0400418
James Ketrenosf1bf6632005-09-21 11:53:54 -0500419 } else {
420 if (host_crypto) {
421 len = (*crypt)->ops->get_key(sec.keys[key], WEP_KEY_LEN,
422 NULL, (*crypt)->priv);
423 if (len == 0) {
424 /* Set a default key of all 0 */
425 IEEE80211_DEBUG_WX("Setting key %d to all "
426 "zero.\n", key);
427 memset(sec.keys[key], 0, 13);
428 (*crypt)->ops->set_key(sec.keys[key], 13, NULL,
429 (*crypt)->priv);
430 sec.key_sizes[key] = 13;
431 sec.flags |= (1 << key);
432 }
433 }
Jeff Garzikb4538722005-05-12 22:48:20 -0400434 /* No key data - just set the default TX key index */
435 if (key_provided) {
James Ketrenosf1bf6632005-09-21 11:53:54 -0500436 IEEE80211_DEBUG_WX("Setting key %d to default Tx "
437 "key.\n", key);
Jeff Garzikb4538722005-05-12 22:48:20 -0400438 ieee->tx_keyidx = key;
439 sec.active_key = key;
440 sec.flags |= SEC_ACTIVE_KEY;
441 }
442 }
James Ketrenos7dc888f2005-09-21 11:58:38 -0500443 if (erq->flags & (IW_ENCODE_OPEN | IW_ENCODE_RESTRICTED)) {
444 ieee->open_wep = !(erq->flags & IW_ENCODE_RESTRICTED);
445 sec.auth_mode = ieee->open_wep ? WLAN_AUTH_OPEN :
446 WLAN_AUTH_SHARED_KEY;
447 sec.flags |= SEC_AUTH_MODE;
448 IEEE80211_DEBUG_WX("Auth: %s\n",
449 sec.auth_mode == WLAN_AUTH_OPEN ?
450 "OPEN" : "SHARED KEY");
451 }
Jeff Garzikb4538722005-05-12 22:48:20 -0400452
453 /* For now we just support WEP, so only set that security level...
454 * TODO: When WPA is added this is one place that needs to change */
455 sec.flags |= SEC_LEVEL;
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400456 sec.level = SEC_LEVEL_1; /* 40 and 104 bit WEP */
James Ketrenose0d369d2005-09-21 11:54:30 -0500457 sec.encode_alg[key] = SEC_ALG_WEP;
Jeff Garzikb4538722005-05-12 22:48:20 -0400458
James Ketrenos259bf1f2005-09-21 11:54:22 -0500459 done:
Jeff Garzikb4538722005-05-12 22:48:20 -0400460 if (ieee->set_security)
461 ieee->set_security(dev, &sec);
462
463 /* Do not reset port if card is in Managed mode since resetting will
464 * generate new IEEE 802.11 authentication which may end up in looping
465 * with IEEE 802.1X. If your hardware requires a reset after WEP
466 * configuration (for example... Prism2), implement the reset_port in
467 * the callbacks structures used to initialize the 802.11 stack. */
468 if (ieee->reset_on_keychange &&
469 ieee->iw_mode != IW_MODE_INFRA &&
470 ieee->reset_port && ieee->reset_port(dev)) {
471 printk(KERN_DEBUG "%s: reset_port failed\n", dev->name);
472 return -EINVAL;
473 }
474 return 0;
475}
476
477int ieee80211_wx_get_encode(struct ieee80211_device *ieee,
478 struct iw_request_info *info,
479 union iwreq_data *wrqu, char *keybuf)
480{
481 struct iw_point *erq = &(wrqu->encoding);
482 int len, key;
483 struct ieee80211_crypt_data *crypt;
James Ketrenosf1bf6632005-09-21 11:53:54 -0500484 struct ieee80211_security *sec = &ieee->sec;
Jeff Garzikb4538722005-05-12 22:48:20 -0400485
486 IEEE80211_DEBUG_WX("GET_ENCODE\n");
487
488 key = erq->flags & IW_ENCODE_INDEX;
489 if (key) {
490 if (key > WEP_KEYS)
491 return -EINVAL;
492 key--;
493 } else
494 key = ieee->tx_keyidx;
495
496 crypt = ieee->crypt[key];
497 erq->flags = key + 1;
498
James Ketrenosf1bf6632005-09-21 11:53:54 -0500499 if (!sec->enabled) {
Jeff Garzikb4538722005-05-12 22:48:20 -0400500 erq->length = 0;
501 erq->flags |= IW_ENCODE_DISABLED;
502 return 0;
503 }
504
James Ketrenosf1bf6632005-09-21 11:53:54 -0500505 len = sec->key_sizes[key];
506 memcpy(keybuf, sec->keys[key], len);
Jeff Garzikb4538722005-05-12 22:48:20 -0400507
James Ketrenosf1bf6632005-09-21 11:53:54 -0500508 erq->length = (len >= 0 ? len : 0);
Jeff Garzikb4538722005-05-12 22:48:20 -0400509 erq->flags |= IW_ENCODE_ENABLED;
510
511 if (ieee->open_wep)
512 erq->flags |= IW_ENCODE_OPEN;
513 else
514 erq->flags |= IW_ENCODE_RESTRICTED;
515
516 return 0;
517}
518
James Ketrenose0d369d2005-09-21 11:54:30 -0500519int ieee80211_wx_set_encodeext(struct ieee80211_device *ieee,
520 struct iw_request_info *info,
521 union iwreq_data *wrqu, char *extra)
522{
523 struct net_device *dev = ieee->dev;
524 struct iw_point *encoding = &wrqu->encoding;
525 struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
526 int i, idx, ret = 0;
James Ketrenosccd0fda2005-09-21 11:58:32 -0500527 int group_key = 0;
James Ketrenose0d369d2005-09-21 11:54:30 -0500528 const char *alg, *module;
529 struct ieee80211_crypto_ops *ops;
530 struct ieee80211_crypt_data **crypt;
531
532 struct ieee80211_security sec = {
533 .flags = 0,
534 };
535
536 idx = encoding->flags & IW_ENCODE_INDEX;
537 if (idx) {
538 if (idx < 1 || idx > WEP_KEYS)
539 return -EINVAL;
540 idx--;
541 } else
542 idx = ieee->tx_keyidx;
543
James Ketrenosccd0fda2005-09-21 11:58:32 -0500544 if (ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) {
James Ketrenose0d369d2005-09-21 11:54:30 -0500545 crypt = &ieee->crypt[idx];
James Ketrenosccd0fda2005-09-21 11:58:32 -0500546 group_key = 1;
547 } else {
Volker Braune1892772005-10-24 10:15:36 -0500548 /* some Cisco APs use idx>0 for unicast in dynamic WEP */
549 if (idx != 0 && ext->alg != IW_ENCODE_ALG_WEP)
James Ketrenose0d369d2005-09-21 11:54:30 -0500550 return -EINVAL;
551 if (ieee->iw_mode == IW_MODE_INFRA)
552 crypt = &ieee->crypt[idx];
553 else
554 return -EINVAL;
555 }
556
557 sec.flags |= SEC_ENABLED | SEC_ENCRYPT;
558 if ((encoding->flags & IW_ENCODE_DISABLED) ||
559 ext->alg == IW_ENCODE_ALG_NONE) {
560 if (*crypt)
561 ieee80211_crypt_delayed_deinit(ieee, crypt);
562
563 for (i = 0; i < WEP_KEYS; i++)
564 if (ieee->crypt[i] != NULL)
565 break;
566
567 if (i == WEP_KEYS) {
568 sec.enabled = 0;
569 sec.encrypt = 0;
570 sec.level = SEC_LEVEL_0;
571 sec.flags |= SEC_LEVEL;
572 }
573 goto done;
574 }
575
576 sec.enabled = 1;
577 sec.encrypt = 1;
578
James Ketrenosccd0fda2005-09-21 11:58:32 -0500579 if (group_key ? !ieee->host_mc_decrypt :
580 !(ieee->host_encrypt || ieee->host_decrypt ||
581 ieee->host_encrypt_msdu))
James Ketrenose0d369d2005-09-21 11:54:30 -0500582 goto skip_host_crypt;
583
584 switch (ext->alg) {
585 case IW_ENCODE_ALG_WEP:
586 alg = "WEP";
587 module = "ieee80211_crypt_wep";
588 break;
589 case IW_ENCODE_ALG_TKIP:
590 alg = "TKIP";
591 module = "ieee80211_crypt_tkip";
592 break;
593 case IW_ENCODE_ALG_CCMP:
594 alg = "CCMP";
595 module = "ieee80211_crypt_ccmp";
596 break;
597 default:
598 IEEE80211_DEBUG_WX("%s: unknown crypto alg %d\n",
599 dev->name, ext->alg);
600 ret = -EINVAL;
601 goto done;
602 }
603
604 ops = ieee80211_get_crypto_ops(alg);
605 if (ops == NULL) {
606 request_module(module);
607 ops = ieee80211_get_crypto_ops(alg);
608 }
609 if (ops == NULL) {
610 IEEE80211_DEBUG_WX("%s: unknown crypto alg %d\n",
611 dev->name, ext->alg);
612 ret = -EINVAL;
613 goto done;
614 }
615
616 if (*crypt == NULL || (*crypt)->ops != ops) {
617 struct ieee80211_crypt_data *new_crypt;
618
619 ieee80211_crypt_delayed_deinit(ieee, crypt);
620
621 new_crypt = (struct ieee80211_crypt_data *)
622 kmalloc(sizeof(*new_crypt), GFP_KERNEL);
623 if (new_crypt == NULL) {
624 ret = -ENOMEM;
625 goto done;
626 }
627 memset(new_crypt, 0, sizeof(struct ieee80211_crypt_data));
628 new_crypt->ops = ops;
629 if (new_crypt->ops && try_module_get(new_crypt->ops->owner))
James Ketrenos6eb6edf2005-09-22 10:34:15 +0000630 new_crypt->priv = new_crypt->ops->init(idx);
James Ketrenose0d369d2005-09-21 11:54:30 -0500631 if (new_crypt->priv == NULL) {
632 kfree(new_crypt);
633 ret = -EINVAL;
634 goto done;
635 }
636 *crypt = new_crypt;
637 }
638
639 if (ext->key_len > 0 && (*crypt)->ops->set_key &&
640 (*crypt)->ops->set_key(ext->key, ext->key_len, ext->rx_seq,
641 (*crypt)->priv) < 0) {
642 IEEE80211_DEBUG_WX("%s: key setting failed\n", dev->name);
643 ret = -EINVAL;
644 goto done;
645 }
646
647 skip_host_crypt:
648 if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) {
649 ieee->tx_keyidx = idx;
650 sec.active_key = idx;
651 sec.flags |= SEC_ACTIVE_KEY;
652 }
653
654 if (ext->alg != IW_ENCODE_ALG_NONE) {
655 memcpy(sec.keys[idx], ext->key, ext->key_len);
656 sec.key_sizes[idx] = ext->key_len;
657 sec.flags |= (1 << idx);
658 if (ext->alg == IW_ENCODE_ALG_WEP) {
659 sec.encode_alg[idx] = SEC_ALG_WEP;
660 sec.flags |= SEC_LEVEL;
661 sec.level = SEC_LEVEL_1;
662 } else if (ext->alg == IW_ENCODE_ALG_TKIP) {
663 sec.encode_alg[idx] = SEC_ALG_TKIP;
664 sec.flags |= SEC_LEVEL;
665 sec.level = SEC_LEVEL_2;
666 } else if (ext->alg == IW_ENCODE_ALG_CCMP) {
667 sec.encode_alg[idx] = SEC_ALG_CCMP;
668 sec.flags |= SEC_LEVEL;
669 sec.level = SEC_LEVEL_3;
670 }
James Ketrenosccd0fda2005-09-21 11:58:32 -0500671 /* Don't set sec level for group keys. */
672 if (group_key)
673 sec.flags &= ~SEC_LEVEL;
James Ketrenose0d369d2005-09-21 11:54:30 -0500674 }
675 done:
676 if (ieee->set_security)
677 ieee->set_security(ieee->dev, &sec);
678
679 /*
680 * Do not reset port if card is in Managed mode since resetting will
681 * generate new IEEE 802.11 authentication which may end up in looping
682 * with IEEE 802.1X. If your hardware requires a reset after WEP
683 * configuration (for example... Prism2), implement the reset_port in
684 * the callbacks structures used to initialize the 802.11 stack.
685 */
686 if (ieee->reset_on_keychange &&
687 ieee->iw_mode != IW_MODE_INFRA &&
688 ieee->reset_port && ieee->reset_port(dev)) {
689 IEEE80211_DEBUG_WX("%s: reset_port failed\n", dev->name);
690 return -EINVAL;
691 }
692
693 return ret;
694}
695
696int ieee80211_wx_get_encodeext(struct ieee80211_device *ieee,
697 struct iw_request_info *info,
698 union iwreq_data *wrqu, char *extra)
699{
700 struct iw_point *encoding = &wrqu->encoding;
701 struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
702 struct ieee80211_security *sec = &ieee->sec;
703 int idx, max_key_len;
704
705 max_key_len = encoding->length - sizeof(*ext);
706 if (max_key_len < 0)
707 return -EINVAL;
708
709 idx = encoding->flags & IW_ENCODE_INDEX;
710 if (idx) {
711 if (idx < 1 || idx > WEP_KEYS)
712 return -EINVAL;
713 idx--;
714 } else
715 idx = ieee->tx_keyidx;
716
Volker Braune1892772005-10-24 10:15:36 -0500717 if (!ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY &&
718 ext->alg != IW_ENCODE_ALG_WEP)
James Ketrenose0d369d2005-09-21 11:54:30 -0500719 if (idx != 0 || ieee->iw_mode != IW_MODE_INFRA)
720 return -EINVAL;
721
722 encoding->flags = idx + 1;
723 memset(ext, 0, sizeof(*ext));
724
725 if (!sec->enabled) {
726 ext->alg = IW_ENCODE_ALG_NONE;
727 ext->key_len = 0;
728 encoding->flags |= IW_ENCODE_DISABLED;
729 } else {
730 if (sec->encode_alg[idx] == SEC_ALG_WEP)
731 ext->alg = IW_ENCODE_ALG_WEP;
732 else if (sec->encode_alg[idx] == SEC_ALG_TKIP)
733 ext->alg = IW_ENCODE_ALG_TKIP;
734 else if (sec->encode_alg[idx] == SEC_ALG_CCMP)
735 ext->alg = IW_ENCODE_ALG_CCMP;
736 else
737 return -EINVAL;
738
739 ext->key_len = sec->key_sizes[idx];
740 memcpy(ext->key, sec->keys[idx], ext->key_len);
741 encoding->flags |= IW_ENCODE_ENABLED;
742 if (ext->key_len &&
743 (ext->alg == IW_ENCODE_ALG_TKIP ||
744 ext->alg == IW_ENCODE_ALG_CCMP))
745 ext->ext_flags |= IW_ENCODE_EXT_TX_SEQ_VALID;
746
747 }
748
749 return 0;
750}
751
Larry Fingerdd5eeb42006-01-30 13:12:50 +0100752int ieee80211_wx_set_auth(struct net_device *dev,
753 struct iw_request_info *info,
754 union iwreq_data *wrqu,
755 char *extra)
756{
757 struct ieee80211_device *ieee = netdev_priv(dev);
758 unsigned long flags;
759 int err = 0;
760
761 spin_lock_irqsave(&ieee->lock, flags);
762
763 switch (wrqu->param.flags & IW_AUTH_INDEX) {
764 case IW_AUTH_WPA_VERSION:
765 case IW_AUTH_CIPHER_PAIRWISE:
766 case IW_AUTH_CIPHER_GROUP:
767 case IW_AUTH_KEY_MGMT:
768 /*
769 * Host AP driver does not use these parameters and allows
770 * wpa_supplicant to control them internally.
771 */
772 break;
773 case IW_AUTH_TKIP_COUNTERMEASURES:
774 break; /* FIXME */
775 case IW_AUTH_DROP_UNENCRYPTED:
776 ieee->drop_unencrypted = !!wrqu->param.value;
777 break;
778 case IW_AUTH_80211_AUTH_ALG:
779 break; /* FIXME */
780 case IW_AUTH_WPA_ENABLED:
781 ieee->privacy_invoked = ieee->wpa_enabled = !!wrqu->param.value;
782 break;
783 case IW_AUTH_RX_UNENCRYPTED_EAPOL:
784 ieee->ieee802_1x = !!wrqu->param.value;
785 break;
786 case IW_AUTH_PRIVACY_INVOKED:
787 ieee->privacy_invoked = !!wrqu->param.value;
788 break;
789 default:
790 err = -EOPNOTSUPP;
791 break;
792 }
793 spin_unlock_irqrestore(&ieee->lock, flags);
794 return err;
795}
796
797int ieee80211_wx_get_auth(struct net_device *dev,
798 struct iw_request_info *info,
799 union iwreq_data *wrqu,
800 char *extra)
801{
802 struct ieee80211_device *ieee = netdev_priv(dev);
803 unsigned long flags;
804 int err = 0;
805
806 spin_lock_irqsave(&ieee->lock, flags);
807
808 switch (wrqu->param.flags & IW_AUTH_INDEX) {
809 case IW_AUTH_WPA_VERSION:
810 case IW_AUTH_CIPHER_PAIRWISE:
811 case IW_AUTH_CIPHER_GROUP:
812 case IW_AUTH_KEY_MGMT:
813 case IW_AUTH_TKIP_COUNTERMEASURES: /* FIXME */
814 case IW_AUTH_80211_AUTH_ALG: /* FIXME */
815 /*
816 * Host AP driver does not use these parameters and allows
817 * wpa_supplicant to control them internally.
818 */
819 err = -EOPNOTSUPP;
820 break;
821 case IW_AUTH_DROP_UNENCRYPTED:
822 wrqu->param.value = ieee->drop_unencrypted;
823 break;
824 case IW_AUTH_WPA_ENABLED:
825 wrqu->param.value = ieee->wpa_enabled;
826 break;
827 case IW_AUTH_RX_UNENCRYPTED_EAPOL:
828 wrqu->param.value = ieee->ieee802_1x;
829 break;
830 default:
831 err = -EOPNOTSUPP;
832 break;
833 }
834 spin_unlock_irqrestore(&ieee->lock, flags);
835 return err;
836}
837
James Ketrenose0d369d2005-09-21 11:54:30 -0500838EXPORT_SYMBOL(ieee80211_wx_set_encodeext);
839EXPORT_SYMBOL(ieee80211_wx_get_encodeext);
James Ketrenose0d369d2005-09-21 11:54:30 -0500840
Jeff Garzikb4538722005-05-12 22:48:20 -0400841EXPORT_SYMBOL(ieee80211_wx_get_scan);
842EXPORT_SYMBOL(ieee80211_wx_set_encode);
843EXPORT_SYMBOL(ieee80211_wx_get_encode);
Larry Fingerdd5eeb42006-01-30 13:12:50 +0100844
845EXPORT_SYMBOL_GPL(ieee80211_wx_set_auth);
846EXPORT_SYMBOL_GPL(ieee80211_wx_get_auth);