blob: 89a81062ab4b01818482d3c32dbb76725e0370be [file] [log] [blame]
Jeff Garzikb4538722005-05-12 22:48:20 -04001/******************************************************************************
2
James Ketrenosebeaddc2005-09-21 11:58:43 -05003 Copyright(c) 2004-2005 Intel Corporation. All rights reserved.
Jeff Garzikb4538722005-05-12 22:48:20 -04004
5 Portions of this file are based on the WEP enablement code provided by the
6 Host AP project hostap-drivers v0.1.3
7 Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen
Jouni Malinen85d32e72007-03-24 17:15:30 -07008 <j@w1.fi>
9 Copyright (c) 2002-2003, Jouni Malinen <j@w1.fi>
Jeff Garzikb4538722005-05-12 22:48:20 -040010
11 This program is free software; you can redistribute it and/or modify it
12 under the terms of version 2 of the GNU General Public License as
13 published by the Free Software Foundation.
14
15 This program is distributed in the hope that it will be useful, but WITHOUT
16 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
17 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
18 more details.
19
20 You should have received a copy of the GNU General Public License along with
21 this program; if not, write to the Free Software Foundation, Inc., 59
22 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23
24 The full GNU General Public License is included in this distribution in the
25 file called LICENSE.
26
27 Contact Information:
28 James P. Ketrenos <ipw2100-admin@linux.intel.com>
29 Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
30
31******************************************************************************/
Jeff Garzikbbeec902005-09-07 00:27:54 -040032
Jeff Garzikb4538722005-05-12 22:48:20 -040033#include <linux/kmod.h>
34#include <linux/module.h>
James Ketrenos42e349f2005-09-21 11:54:07 -050035#include <linux/jiffies.h>
Jeff Garzikb4538722005-05-12 22:48:20 -040036
37#include <net/ieee80211.h>
Jeff Garzikbbeec902005-09-07 00:27:54 -040038#include <linux/wireless.h>
39
Jeff Garzikb4538722005-05-12 22:48:20 -040040static const char *ieee80211_modes[] = {
41 "?", "a", "b", "ab", "g", "ag", "bg", "abg"
42};
43
44#define MAX_CUSTOM_LEN 64
Larry Fingerd94606e2006-03-03 16:21:55 -060045static char *ieee80211_translate_scan(struct ieee80211_device *ieee,
David S. Millerccc58052008-06-16 18:50:49 -070046 char *start, char *stop,
47 struct ieee80211_network *network,
48 struct iw_request_info *info)
Jeff Garzikb4538722005-05-12 22:48:20 -040049{
50 char custom[MAX_CUSTOM_LEN];
51 char *p;
52 struct iw_event iwe;
53 int i, j;
Zhu Yi09593042006-04-13 17:17:26 +080054 char *current_val; /* For rates */
55 u8 rate;
Jeff Garzikb4538722005-05-12 22:48:20 -040056
57 /* First entry *MUST* be the AP MAC address */
58 iwe.cmd = SIOCGIWAP;
59 iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
60 memcpy(iwe.u.ap_addr.sa_data, network->bssid, ETH_ALEN);
David S. Millerccc58052008-06-16 18:50:49 -070061 start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_ADDR_LEN);
Jeff Garzikb4538722005-05-12 22:48:20 -040062
63 /* Remaining entries will be displayed in the order we provide them */
64
65 /* Add the ESSID */
66 iwe.cmd = SIOCGIWESSID;
67 iwe.u.data.flags = 1;
68 if (network->flags & NETWORK_EMPTY_ESSID) {
69 iwe.u.data.length = sizeof("<hidden>");
David S. Millerccc58052008-06-16 18:50:49 -070070 start = iwe_stream_add_point(info, start, stop,
71 &iwe, "<hidden>");
Jeff Garzikb4538722005-05-12 22:48:20 -040072 } else {
Jeff Garzik0edd5b42005-09-07 00:48:31 -040073 iwe.u.data.length = min(network->ssid_len, (u8) 32);
David S. Millerccc58052008-06-16 18:50:49 -070074 start = iwe_stream_add_point(info, start, stop,
75 &iwe, network->ssid);
Jeff Garzikb4538722005-05-12 22:48:20 -040076 }
77
78 /* Add the protocol name */
79 iwe.cmd = SIOCGIWNAME;
Jeff Garzik0edd5b42005-09-07 00:48:31 -040080 snprintf(iwe.u.name, IFNAMSIZ, "IEEE 802.11%s",
81 ieee80211_modes[network->mode]);
David S. Millerccc58052008-06-16 18:50:49 -070082 start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_CHAR_LEN);
Jeff Garzikb4538722005-05-12 22:48:20 -040083
Jeff Garzik0edd5b42005-09-07 00:48:31 -040084 /* Add mode */
85 iwe.cmd = SIOCGIWMODE;
86 if (network->capability & (WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS)) {
Jeff Garzik1b5cca32005-08-15 00:32:15 -040087 if (network->capability & WLAN_CAPABILITY_ESS)
Jeff Garzikb4538722005-05-12 22:48:20 -040088 iwe.u.mode = IW_MODE_MASTER;
89 else
90 iwe.u.mode = IW_MODE_ADHOC;
91
David S. Millerccc58052008-06-16 18:50:49 -070092 start = iwe_stream_add_event(info, start, stop,
93 &iwe, IW_EV_UINT_LEN);
Jeff Garzikb4538722005-05-12 22:48:20 -040094 }
95
Larry Finger93afe3d2007-04-21 17:56:43 -050096 /* Add channel and frequency */
Jean Tourrilhes90869b22007-07-10 15:51:14 -050097 /* Note : userspace automatically computes channel using iwrange */
Jeff Garzikb4538722005-05-12 22:48:20 -040098 iwe.cmd = SIOCGIWFREQ;
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;
Jean Tourrilhes90869b22007-07-10 15:51:14 -0500101 iwe.u.freq.i = 0;
David S. Millerccc58052008-06-16 18:50:49 -0700102 start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_FREQ_LEN);
Larry Finger93afe3d2007-04-21 17:56:43 -0500103
Jeff Garzikb4538722005-05-12 22:48:20 -0400104 /* Add encryption capability */
105 iwe.cmd = SIOCGIWENCODE;
106 if (network->capability & WLAN_CAPABILITY_PRIVACY)
107 iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
108 else
109 iwe.u.data.flags = IW_ENCODE_DISABLED;
110 iwe.u.data.length = 0;
David S. Millerccc58052008-06-16 18:50:49 -0700111 start = iwe_stream_add_point(info, start, stop,
112 &iwe, network->ssid);
Jeff Garzikb4538722005-05-12 22:48:20 -0400113
114 /* Add basic and extended rates */
Zhu Yi09593042006-04-13 17:17:26 +0800115 /* Rate : stuffing multiple values in a single event require a bit
116 * more of magic - Jean II */
David S. Millerccc58052008-06-16 18:50:49 -0700117 current_val = start + iwe_stream_lcp_len(info);
Zhu Yi09593042006-04-13 17:17:26 +0800118 iwe.cmd = SIOCGIWRATE;
119 /* Those two flags are ignored... */
120 iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0;
121
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400122 for (i = 0, j = 0; i < network->rates_len;) {
Jeff Garzikb4538722005-05-12 22:48:20 -0400123 if (j < network->rates_ex_len &&
124 ((network->rates_ex[j] & 0x7F) <
125 (network->rates[i] & 0x7F)))
126 rate = network->rates_ex[j++] & 0x7F;
127 else
128 rate = network->rates[i++] & 0x7F;
Zhu Yi09593042006-04-13 17:17:26 +0800129 /* Bit rate given in 500 kb/s units (+ 0x80) */
130 iwe.u.bitrate.value = ((rate & 0x7f) * 500000);
131 /* Add new value to event */
David S. Millerccc58052008-06-16 18:50:49 -0700132 current_val = iwe_stream_add_value(info, start, current_val,
133 stop, &iwe, IW_EV_PARAM_LEN);
Jeff Garzikb4538722005-05-12 22:48:20 -0400134 }
135 for (; j < network->rates_ex_len; j++) {
136 rate = network->rates_ex[j] & 0x7F;
Zhu Yi09593042006-04-13 17:17:26 +0800137 /* Bit rate given in 500 kb/s units (+ 0x80) */
138 iwe.u.bitrate.value = ((rate & 0x7f) * 500000);
139 /* Add new value to event */
David S. Millerccc58052008-06-16 18:50:49 -0700140 current_val = iwe_stream_add_value(info, start, current_val,
141 stop, &iwe, IW_EV_PARAM_LEN);
Jeff Garzikb4538722005-05-12 22:48:20 -0400142 }
Zhu Yi09593042006-04-13 17:17:26 +0800143 /* Check if we added any rate */
David S. Millerccc58052008-06-16 18:50:49 -0700144 if ((current_val - start) > iwe_stream_lcp_len(info))
Zhu Yi09593042006-04-13 17:17:26 +0800145 start = current_val;
Jeff Garzikb4538722005-05-12 22:48:20 -0400146
147 /* Add quality statistics */
Jeff Garzikb4538722005-05-12 22:48:20 -0400148 iwe.cmd = IWEVQUAL;
James Ketrenosb1b508e2005-09-13 17:27:19 -0500149 iwe.u.qual.updated = IW_QUAL_QUAL_UPDATED | IW_QUAL_LEVEL_UPDATED |
150 IW_QUAL_NOISE_UPDATED;
151
152 if (!(network->stats.mask & IEEE80211_STATMASK_RSSI)) {
153 iwe.u.qual.updated |= IW_QUAL_QUAL_INVALID |
154 IW_QUAL_LEVEL_INVALID;
155 iwe.u.qual.qual = 0;
James Ketrenosb1b508e2005-09-13 17:27:19 -0500156 } else {
Jiri Benc757d18f2005-10-10 19:16:53 +0200157 if (ieee->perfect_rssi == ieee->worst_rssi)
158 iwe.u.qual.qual = 100;
159 else
160 iwe.u.qual.qual =
161 (100 *
162 (ieee->perfect_rssi - ieee->worst_rssi) *
163 (ieee->perfect_rssi - ieee->worst_rssi) -
164 (ieee->perfect_rssi - network->stats.rssi) *
165 (15 * (ieee->perfect_rssi - ieee->worst_rssi) +
James Ketrenos81f87522005-10-24 10:20:53 -0500166 62 * (ieee->perfect_rssi -
167 network->stats.rssi))) /
168 ((ieee->perfect_rssi -
169 ieee->worst_rssi) * (ieee->perfect_rssi -
170 ieee->worst_rssi));
James Ketrenosb1b508e2005-09-13 17:27:19 -0500171 if (iwe.u.qual.qual > 100)
172 iwe.u.qual.qual = 100;
173 else if (iwe.u.qual.qual < 1)
174 iwe.u.qual.qual = 0;
175 }
176
177 if (!(network->stats.mask & IEEE80211_STATMASK_NOISE)) {
Jeff Garzikb4538722005-05-12 22:48:20 -0400178 iwe.u.qual.updated |= IW_QUAL_NOISE_INVALID;
James Ketrenosb1b508e2005-09-13 17:27:19 -0500179 iwe.u.qual.noise = 0;
180 } else {
181 iwe.u.qual.noise = network->stats.noise;
182 }
Jeff Garzikb4538722005-05-12 22:48:20 -0400183
Zhu Yi7bd64362006-01-19 16:21:54 +0800184 if (!(network->stats.mask & IEEE80211_STATMASK_SIGNAL)) {
185 iwe.u.qual.updated |= IW_QUAL_LEVEL_INVALID;
186 iwe.u.qual.level = 0;
187 } else {
188 iwe.u.qual.level = network->stats.signal;
189 }
190
David S. Millerccc58052008-06-16 18:50:49 -0700191 start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_QUAL_LEN);
Jeff Garzikb4538722005-05-12 22:48:20 -0400192
193 iwe.cmd = IWEVCUSTOM;
194 p = custom;
195
196 iwe.u.data.length = p - custom;
197 if (iwe.u.data.length)
David S. Millerccc58052008-06-16 18:50:49 -0700198 start = iwe_stream_add_point(info, start, stop, &iwe, custom);
Jeff Garzikb4538722005-05-12 22:48:20 -0400199
Zhu Yi47168082006-02-13 13:37:03 +0800200 memset(&iwe, 0, sizeof(iwe));
James Ketrenos20d64712005-09-21 11:53:43 -0500201 if (network->wpa_ie_len) {
Zhu Yi47168082006-02-13 13:37:03 +0800202 char buf[MAX_WPA_IE_LEN];
203 memcpy(buf, network->wpa_ie, network->wpa_ie_len);
204 iwe.cmd = IWEVGENIE;
205 iwe.u.data.length = network->wpa_ie_len;
David S. Millerccc58052008-06-16 18:50:49 -0700206 start = iwe_stream_add_point(info, start, stop, &iwe, buf);
Jeff Garzikb4538722005-05-12 22:48:20 -0400207 }
208
Zhu Yi47168082006-02-13 13:37:03 +0800209 memset(&iwe, 0, sizeof(iwe));
James Ketrenos20d64712005-09-21 11:53:43 -0500210 if (network->rsn_ie_len) {
Zhu Yi47168082006-02-13 13:37:03 +0800211 char buf[MAX_WPA_IE_LEN];
212 memcpy(buf, network->rsn_ie, network->rsn_ie_len);
213 iwe.cmd = IWEVGENIE;
214 iwe.u.data.length = network->rsn_ie_len;
David S. Millerccc58052008-06-16 18:50:49 -0700215 start = iwe_stream_add_point(info, start, stop, &iwe, buf);
Jeff Garzikb4538722005-05-12 22:48:20 -0400216 }
217
218 /* Add EXTRA: Age to display seconds since last beacon/probe response
219 * for given network. */
220 iwe.cmd = IWEVCUSTOM;
221 p = custom;
222 p += snprintf(p, MAX_CUSTOM_LEN - (p - custom),
James Ketrenos42e349f2005-09-21 11:54:07 -0500223 " Last beacon: %dms ago",
224 jiffies_to_msecs(jiffies - network->last_scanned));
Jeff Garzikb4538722005-05-12 22:48:20 -0400225 iwe.u.data.length = p - custom;
226 if (iwe.u.data.length)
David S. Millerccc58052008-06-16 18:50:49 -0700227 start = iwe_stream_add_point(info, start, stop, &iwe, custom);
Jeff Garzikb4538722005-05-12 22:48:20 -0400228
Zhu Yi7bd64362006-01-19 16:21:54 +0800229 /* Add spectrum management information */
230 iwe.cmd = -1;
231 p = custom;
232 p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), " Channel flags: ");
233
234 if (ieee80211_get_channel_flags(ieee, network->channel) &
235 IEEE80211_CH_INVALID) {
236 iwe.cmd = IWEVCUSTOM;
237 p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), "INVALID ");
238 }
239
240 if (ieee80211_get_channel_flags(ieee, network->channel) &
241 IEEE80211_CH_RADAR_DETECT) {
242 iwe.cmd = IWEVCUSTOM;
243 p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), "DFS ");
244 }
245
246 if (iwe.cmd == IWEVCUSTOM) {
247 iwe.u.data.length = p - custom;
David S. Millerccc58052008-06-16 18:50:49 -0700248 start = iwe_stream_add_point(info, start, stop, &iwe, custom);
Zhu Yi7bd64362006-01-19 16:21:54 +0800249 }
250
Jeff Garzikb4538722005-05-12 22:48:20 -0400251 return start;
252}
253
Zhu Yi55cd94a2006-01-19 16:20:59 +0800254#define SCAN_ITEM_SIZE 128
255
Jeff Garzikb4538722005-05-12 22:48:20 -0400256int ieee80211_wx_get_scan(struct ieee80211_device *ieee,
257 struct iw_request_info *info,
258 union iwreq_data *wrqu, char *extra)
259{
260 struct ieee80211_network *network;
261 unsigned long flags;
Zhu Yi55cd94a2006-01-19 16:20:59 +0800262 int err = 0;
Jeff Garzikb4538722005-05-12 22:48:20 -0400263
264 char *ev = extra;
Zhu Yi55cd94a2006-01-19 16:20:59 +0800265 char *stop = ev + wrqu->data.length;
Jeff Garzikb4538722005-05-12 22:48:20 -0400266 int i = 0;
267
268 IEEE80211_DEBUG_WX("Getting scan\n");
269
270 spin_lock_irqsave(&ieee->lock, flags);
271
272 list_for_each_entry(network, &ieee->network_list, list) {
273 i++;
Zhu Yi55cd94a2006-01-19 16:20:59 +0800274 if (stop - ev < SCAN_ITEM_SIZE) {
275 err = -E2BIG;
276 break;
277 }
278
Jeff Garzikb4538722005-05-12 22:48:20 -0400279 if (ieee->scan_age == 0 ||
280 time_after(network->last_scanned + ieee->scan_age, jiffies))
David S. Millerccc58052008-06-16 18:50:49 -0700281 ev = ieee80211_translate_scan(ieee, ev, stop, network,
282 info);
Jeff Garzikb4538722005-05-12 22:48:20 -0400283 else
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400284 IEEE80211_DEBUG_SCAN("Not showing network '%s ("
Johannes Berge1749612008-10-27 15:59:26 -0700285 "%pM)' due to age (%dms).\n",
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400286 escape_essid(network->ssid,
287 network->ssid_len),
Johannes Berge1749612008-10-27 15:59:26 -0700288 network->bssid,
James Ketrenos42e349f2005-09-21 11:54:07 -0500289 jiffies_to_msecs(jiffies -
290 network->
291 last_scanned));
Jeff Garzikb4538722005-05-12 22:48:20 -0400292 }
293
294 spin_unlock_irqrestore(&ieee->lock, flags);
295
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400296 wrqu->data.length = ev - extra;
Jeff Garzikb4538722005-05-12 22:48:20 -0400297 wrqu->data.flags = 0;
298
299 IEEE80211_DEBUG_WX("exit: %d networks returned.\n", i);
300
Zhu Yi55cd94a2006-01-19 16:20:59 +0800301 return err;
Jeff Garzikb4538722005-05-12 22:48:20 -0400302}
303
304int ieee80211_wx_set_encode(struct ieee80211_device *ieee,
305 struct iw_request_info *info,
306 union iwreq_data *wrqu, char *keybuf)
307{
308 struct iw_point *erq = &(wrqu->encoding);
309 struct net_device *dev = ieee->dev;
310 struct ieee80211_security sec = {
311 .flags = 0
312 };
313 int i, key, key_provided, len;
314 struct ieee80211_crypt_data **crypt;
Johannes Berga4bf26f2005-12-31 11:35:20 +0100315 int host_crypto = ieee->host_encrypt || ieee->host_decrypt || ieee->host_build_iv;
Jeff Garzikb4538722005-05-12 22:48:20 -0400316
317 IEEE80211_DEBUG_WX("SET_ENCODE\n");
318
319 key = erq->flags & IW_ENCODE_INDEX;
320 if (key) {
321 if (key > WEP_KEYS)
322 return -EINVAL;
323 key--;
324 key_provided = 1;
325 } else {
326 key_provided = 0;
327 key = ieee->tx_keyidx;
328 }
329
330 IEEE80211_DEBUG_WX("Key: %d [%s]\n", key, key_provided ?
331 "provided" : "default");
332
333 crypt = &ieee->crypt[key];
334
335 if (erq->flags & IW_ENCODE_DISABLED) {
336 if (key_provided && *crypt) {
337 IEEE80211_DEBUG_WX("Disabling encryption on key %d.\n",
338 key);
339 ieee80211_crypt_delayed_deinit(ieee, crypt);
340 } else
341 IEEE80211_DEBUG_WX("Disabling encryption.\n");
342
343 /* Check all the keys to see if any are still configured,
344 * and if no key index was provided, de-init them all */
345 for (i = 0; i < WEP_KEYS; i++) {
346 if (ieee->crypt[i] != NULL) {
347 if (key_provided)
348 break;
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400349 ieee80211_crypt_delayed_deinit(ieee,
350 &ieee->crypt[i]);
Jeff Garzikb4538722005-05-12 22:48:20 -0400351 }
352 }
353
354 if (i == WEP_KEYS) {
355 sec.enabled = 0;
James Ketrenosf1bf6632005-09-21 11:53:54 -0500356 sec.encrypt = 0;
Jeff Garzikb4538722005-05-12 22:48:20 -0400357 sec.level = SEC_LEVEL_0;
James Ketrenos259bf1f2005-09-21 11:54:22 -0500358 sec.flags |= SEC_ENABLED | SEC_LEVEL | SEC_ENCRYPT;
Jeff Garzikb4538722005-05-12 22:48:20 -0400359 }
360
361 goto done;
362 }
363
Jeff Garzikb4538722005-05-12 22:48:20 -0400364 sec.enabled = 1;
James Ketrenosf1bf6632005-09-21 11:53:54 -0500365 sec.encrypt = 1;
James Ketrenos259bf1f2005-09-21 11:54:22 -0500366 sec.flags |= SEC_ENABLED | SEC_ENCRYPT;
Jeff Garzikb4538722005-05-12 22:48:20 -0400367
368 if (*crypt != NULL && (*crypt)->ops != NULL &&
369 strcmp((*crypt)->ops->name, "WEP") != 0) {
370 /* changing to use WEP; deinit previously used algorithm
371 * on this key */
372 ieee80211_crypt_delayed_deinit(ieee, crypt);
373 }
374
James Ketrenosf1bf6632005-09-21 11:53:54 -0500375 if (*crypt == NULL && host_crypto) {
Jeff Garzikb4538722005-05-12 22:48:20 -0400376 struct ieee80211_crypt_data *new_crypt;
377
378 /* take WEP into use */
Panagiotis Issaris0da974f2006-07-21 14:51:30 -0700379 new_crypt = kzalloc(sizeof(struct ieee80211_crypt_data),
Jeff Garzikb4538722005-05-12 22:48:20 -0400380 GFP_KERNEL);
381 if (new_crypt == NULL)
382 return -ENOMEM;
Jeff Garzikb4538722005-05-12 22:48:20 -0400383 new_crypt->ops = ieee80211_get_crypto_ops("WEP");
384 if (!new_crypt->ops) {
385 request_module("ieee80211_crypt_wep");
386 new_crypt->ops = ieee80211_get_crypto_ops("WEP");
387 }
388
389 if (new_crypt->ops && try_module_get(new_crypt->ops->owner))
James Ketrenos6eb6edf2005-09-22 10:34:15 +0000390 new_crypt->priv = new_crypt->ops->init(key);
Jeff Garzikb4538722005-05-12 22:48:20 -0400391
392 if (!new_crypt->ops || !new_crypt->priv) {
393 kfree(new_crypt);
394 new_crypt = NULL;
395
396 printk(KERN_WARNING "%s: could not initialize WEP: "
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400397 "load module ieee80211_crypt_wep\n", dev->name);
Jeff Garzikb4538722005-05-12 22:48:20 -0400398 return -EOPNOTSUPP;
399 }
400 *crypt = new_crypt;
401 }
402
403 /* If a new key was provided, set it up */
404 if (erq->length > 0) {
405 len = erq->length <= 5 ? 5 : 13;
406 memcpy(sec.keys[key], keybuf, erq->length);
407 if (len > erq->length)
408 memset(sec.keys[key] + erq->length, 0,
409 len - erq->length);
410 IEEE80211_DEBUG_WX("Setting key %d to '%s' (%d:%d bytes)\n",
411 key, escape_essid(sec.keys[key], len),
412 erq->length, len);
413 sec.key_sizes[key] = len;
James Ketrenosf1bf6632005-09-21 11:53:54 -0500414 if (*crypt)
415 (*crypt)->ops->set_key(sec.keys[key], len, NULL,
416 (*crypt)->priv);
Jeff Garzikb4538722005-05-12 22:48:20 -0400417 sec.flags |= (1 << key);
418 /* This ensures a key will be activated if no key is
Jean Delvarec03983a2007-10-19 23:22:55 +0200419 * explicitly set */
Jeff Garzikb4538722005-05-12 22:48:20 -0400420 if (key == sec.active_key)
421 sec.flags |= SEC_ACTIVE_KEY;
Jeff Garzikb4538722005-05-12 22:48:20 -0400422
James Ketrenosf1bf6632005-09-21 11:53:54 -0500423 } else {
424 if (host_crypto) {
425 len = (*crypt)->ops->get_key(sec.keys[key], WEP_KEY_LEN,
426 NULL, (*crypt)->priv);
427 if (len == 0) {
428 /* Set a default key of all 0 */
429 IEEE80211_DEBUG_WX("Setting key %d to all "
430 "zero.\n", key);
431 memset(sec.keys[key], 0, 13);
432 (*crypt)->ops->set_key(sec.keys[key], 13, NULL,
433 (*crypt)->priv);
434 sec.key_sizes[key] = 13;
435 sec.flags |= (1 << key);
436 }
437 }
Jeff Garzikb4538722005-05-12 22:48:20 -0400438 /* No key data - just set the default TX key index */
439 if (key_provided) {
James Ketrenosf1bf6632005-09-21 11:53:54 -0500440 IEEE80211_DEBUG_WX("Setting key %d to default Tx "
441 "key.\n", key);
Jeff Garzikb4538722005-05-12 22:48:20 -0400442 ieee->tx_keyidx = key;
443 sec.active_key = key;
444 sec.flags |= SEC_ACTIVE_KEY;
445 }
446 }
James Ketrenos7dc888f2005-09-21 11:58:38 -0500447 if (erq->flags & (IW_ENCODE_OPEN | IW_ENCODE_RESTRICTED)) {
448 ieee->open_wep = !(erq->flags & IW_ENCODE_RESTRICTED);
449 sec.auth_mode = ieee->open_wep ? WLAN_AUTH_OPEN :
450 WLAN_AUTH_SHARED_KEY;
451 sec.flags |= SEC_AUTH_MODE;
452 IEEE80211_DEBUG_WX("Auth: %s\n",
453 sec.auth_mode == WLAN_AUTH_OPEN ?
454 "OPEN" : "SHARED KEY");
455 }
Jeff Garzikb4538722005-05-12 22:48:20 -0400456
457 /* For now we just support WEP, so only set that security level...
458 * TODO: When WPA is added this is one place that needs to change */
459 sec.flags |= SEC_LEVEL;
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400460 sec.level = SEC_LEVEL_1; /* 40 and 104 bit WEP */
James Ketrenose0d369d2005-09-21 11:54:30 -0500461 sec.encode_alg[key] = SEC_ALG_WEP;
Jeff Garzikb4538722005-05-12 22:48:20 -0400462
James Ketrenos259bf1f2005-09-21 11:54:22 -0500463 done:
Jeff Garzikb4538722005-05-12 22:48:20 -0400464 if (ieee->set_security)
465 ieee->set_security(dev, &sec);
466
467 /* Do not reset port if card is in Managed mode since resetting will
468 * generate new IEEE 802.11 authentication which may end up in looping
469 * with IEEE 802.1X. If your hardware requires a reset after WEP
470 * configuration (for example... Prism2), implement the reset_port in
471 * the callbacks structures used to initialize the 802.11 stack. */
472 if (ieee->reset_on_keychange &&
473 ieee->iw_mode != IW_MODE_INFRA &&
474 ieee->reset_port && ieee->reset_port(dev)) {
475 printk(KERN_DEBUG "%s: reset_port failed\n", dev->name);
476 return -EINVAL;
477 }
478 return 0;
479}
480
481int ieee80211_wx_get_encode(struct ieee80211_device *ieee,
482 struct iw_request_info *info,
483 union iwreq_data *wrqu, char *keybuf)
484{
485 struct iw_point *erq = &(wrqu->encoding);
486 int len, key;
487 struct ieee80211_crypt_data *crypt;
James Ketrenosf1bf6632005-09-21 11:53:54 -0500488 struct ieee80211_security *sec = &ieee->sec;
Jeff Garzikb4538722005-05-12 22:48:20 -0400489
490 IEEE80211_DEBUG_WX("GET_ENCODE\n");
491
492 key = erq->flags & IW_ENCODE_INDEX;
493 if (key) {
494 if (key > WEP_KEYS)
495 return -EINVAL;
496 key--;
497 } else
498 key = ieee->tx_keyidx;
499
500 crypt = ieee->crypt[key];
501 erq->flags = key + 1;
502
James Ketrenosf1bf6632005-09-21 11:53:54 -0500503 if (!sec->enabled) {
Jeff Garzikb4538722005-05-12 22:48:20 -0400504 erq->length = 0;
505 erq->flags |= IW_ENCODE_DISABLED;
506 return 0;
507 }
508
James Ketrenosf1bf6632005-09-21 11:53:54 -0500509 len = sec->key_sizes[key];
510 memcpy(keybuf, sec->keys[key], len);
Jeff Garzikb4538722005-05-12 22:48:20 -0400511
Adrian Bunk62741152006-04-27 02:33:42 -0700512 erq->length = len;
Jeff Garzikb4538722005-05-12 22:48:20 -0400513 erq->flags |= IW_ENCODE_ENABLED;
514
515 if (ieee->open_wep)
516 erq->flags |= IW_ENCODE_OPEN;
517 else
518 erq->flags |= IW_ENCODE_RESTRICTED;
519
520 return 0;
521}
522
James Ketrenose0d369d2005-09-21 11:54:30 -0500523int ieee80211_wx_set_encodeext(struct ieee80211_device *ieee,
524 struct iw_request_info *info,
525 union iwreq_data *wrqu, char *extra)
526{
527 struct net_device *dev = ieee->dev;
528 struct iw_point *encoding = &wrqu->encoding;
529 struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
530 int i, idx, ret = 0;
James Ketrenosccd0fda2005-09-21 11:58:32 -0500531 int group_key = 0;
James Ketrenose0d369d2005-09-21 11:54:30 -0500532 const char *alg, *module;
533 struct ieee80211_crypto_ops *ops;
534 struct ieee80211_crypt_data **crypt;
535
536 struct ieee80211_security sec = {
537 .flags = 0,
538 };
539
540 idx = encoding->flags & IW_ENCODE_INDEX;
541 if (idx) {
542 if (idx < 1 || idx > WEP_KEYS)
543 return -EINVAL;
544 idx--;
545 } else
546 idx = ieee->tx_keyidx;
547
James Ketrenosccd0fda2005-09-21 11:58:32 -0500548 if (ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) {
James Ketrenose0d369d2005-09-21 11:54:30 -0500549 crypt = &ieee->crypt[idx];
James Ketrenosccd0fda2005-09-21 11:58:32 -0500550 group_key = 1;
551 } else {
Volker Braune1892772005-10-24 10:15:36 -0500552 /* some Cisco APs use idx>0 for unicast in dynamic WEP */
553 if (idx != 0 && ext->alg != IW_ENCODE_ALG_WEP)
James Ketrenose0d369d2005-09-21 11:54:30 -0500554 return -EINVAL;
555 if (ieee->iw_mode == IW_MODE_INFRA)
556 crypt = &ieee->crypt[idx];
557 else
558 return -EINVAL;
559 }
560
561 sec.flags |= SEC_ENABLED | SEC_ENCRYPT;
562 if ((encoding->flags & IW_ENCODE_DISABLED) ||
563 ext->alg == IW_ENCODE_ALG_NONE) {
564 if (*crypt)
565 ieee80211_crypt_delayed_deinit(ieee, crypt);
566
567 for (i = 0; i < WEP_KEYS; i++)
568 if (ieee->crypt[i] != NULL)
569 break;
570
571 if (i == WEP_KEYS) {
572 sec.enabled = 0;
573 sec.encrypt = 0;
574 sec.level = SEC_LEVEL_0;
575 sec.flags |= SEC_LEVEL;
576 }
577 goto done;
578 }
579
580 sec.enabled = 1;
581 sec.encrypt = 1;
582
James Ketrenosccd0fda2005-09-21 11:58:32 -0500583 if (group_key ? !ieee->host_mc_decrypt :
584 !(ieee->host_encrypt || ieee->host_decrypt ||
585 ieee->host_encrypt_msdu))
James Ketrenose0d369d2005-09-21 11:54:30 -0500586 goto skip_host_crypt;
587
588 switch (ext->alg) {
589 case IW_ENCODE_ALG_WEP:
590 alg = "WEP";
591 module = "ieee80211_crypt_wep";
592 break;
593 case IW_ENCODE_ALG_TKIP:
594 alg = "TKIP";
595 module = "ieee80211_crypt_tkip";
596 break;
597 case IW_ENCODE_ALG_CCMP:
598 alg = "CCMP";
599 module = "ieee80211_crypt_ccmp";
600 break;
601 default:
602 IEEE80211_DEBUG_WX("%s: unknown crypto alg %d\n",
603 dev->name, ext->alg);
604 ret = -EINVAL;
605 goto done;
606 }
607
608 ops = ieee80211_get_crypto_ops(alg);
609 if (ops == NULL) {
610 request_module(module);
611 ops = ieee80211_get_crypto_ops(alg);
612 }
613 if (ops == NULL) {
614 IEEE80211_DEBUG_WX("%s: unknown crypto alg %d\n",
615 dev->name, ext->alg);
616 ret = -EINVAL;
617 goto done;
618 }
619
620 if (*crypt == NULL || (*crypt)->ops != ops) {
621 struct ieee80211_crypt_data *new_crypt;
622
623 ieee80211_crypt_delayed_deinit(ieee, crypt);
624
Panagiotis Issaris0da974f2006-07-21 14:51:30 -0700625 new_crypt = kzalloc(sizeof(*new_crypt), GFP_KERNEL);
James Ketrenose0d369d2005-09-21 11:54:30 -0500626 if (new_crypt == NULL) {
627 ret = -ENOMEM;
628 goto done;
629 }
James Ketrenose0d369d2005-09-21 11:54:30 -0500630 new_crypt->ops = ops;
631 if (new_crypt->ops && try_module_get(new_crypt->ops->owner))
James Ketrenos6eb6edf2005-09-22 10:34:15 +0000632 new_crypt->priv = new_crypt->ops->init(idx);
James Ketrenose0d369d2005-09-21 11:54:30 -0500633 if (new_crypt->priv == NULL) {
634 kfree(new_crypt);
635 ret = -EINVAL;
636 goto done;
637 }
638 *crypt = new_crypt;
639 }
640
641 if (ext->key_len > 0 && (*crypt)->ops->set_key &&
642 (*crypt)->ops->set_key(ext->key, ext->key_len, ext->rx_seq,
643 (*crypt)->priv) < 0) {
644 IEEE80211_DEBUG_WX("%s: key setting failed\n", dev->name);
645 ret = -EINVAL;
646 goto done;
647 }
648
649 skip_host_crypt:
650 if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) {
651 ieee->tx_keyidx = idx;
652 sec.active_key = idx;
653 sec.flags |= SEC_ACTIVE_KEY;
654 }
655
656 if (ext->alg != IW_ENCODE_ALG_NONE) {
657 memcpy(sec.keys[idx], ext->key, ext->key_len);
658 sec.key_sizes[idx] = ext->key_len;
659 sec.flags |= (1 << idx);
660 if (ext->alg == IW_ENCODE_ALG_WEP) {
661 sec.encode_alg[idx] = SEC_ALG_WEP;
662 sec.flags |= SEC_LEVEL;
663 sec.level = SEC_LEVEL_1;
664 } else if (ext->alg == IW_ENCODE_ALG_TKIP) {
665 sec.encode_alg[idx] = SEC_ALG_TKIP;
666 sec.flags |= SEC_LEVEL;
667 sec.level = SEC_LEVEL_2;
668 } else if (ext->alg == IW_ENCODE_ALG_CCMP) {
669 sec.encode_alg[idx] = SEC_ALG_CCMP;
670 sec.flags |= SEC_LEVEL;
671 sec.level = SEC_LEVEL_3;
672 }
James Ketrenosccd0fda2005-09-21 11:58:32 -0500673 /* Don't set sec level for group keys. */
674 if (group_key)
675 sec.flags &= ~SEC_LEVEL;
James Ketrenose0d369d2005-09-21 11:54:30 -0500676 }
677 done:
678 if (ieee->set_security)
679 ieee->set_security(ieee->dev, &sec);
680
681 /*
682 * Do not reset port if card is in Managed mode since resetting will
683 * generate new IEEE 802.11 authentication which may end up in looping
684 * with IEEE 802.1X. If your hardware requires a reset after WEP
685 * configuration (for example... Prism2), implement the reset_port in
686 * the callbacks structures used to initialize the 802.11 stack.
687 */
688 if (ieee->reset_on_keychange &&
689 ieee->iw_mode != IW_MODE_INFRA &&
690 ieee->reset_port && ieee->reset_port(dev)) {
691 IEEE80211_DEBUG_WX("%s: reset_port failed\n", dev->name);
692 return -EINVAL;
693 }
694
695 return ret;
696}
697
698int ieee80211_wx_get_encodeext(struct ieee80211_device *ieee,
699 struct iw_request_info *info,
700 union iwreq_data *wrqu, char *extra)
701{
702 struct iw_point *encoding = &wrqu->encoding;
703 struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
704 struct ieee80211_security *sec = &ieee->sec;
705 int idx, max_key_len;
706
707 max_key_len = encoding->length - sizeof(*ext);
708 if (max_key_len < 0)
709 return -EINVAL;
710
711 idx = encoding->flags & IW_ENCODE_INDEX;
712 if (idx) {
713 if (idx < 1 || idx > WEP_KEYS)
714 return -EINVAL;
715 idx--;
716 } else
717 idx = ieee->tx_keyidx;
718
Roel Kluinf59d9782007-10-26 21:51:26 +0200719 if (!(ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) &&
Volker Braune1892772005-10-24 10:15:36 -0500720 ext->alg != IW_ENCODE_ALG_WEP)
James Ketrenose0d369d2005-09-21 11:54:30 -0500721 if (idx != 0 || ieee->iw_mode != IW_MODE_INFRA)
722 return -EINVAL;
723
724 encoding->flags = idx + 1;
725 memset(ext, 0, sizeof(*ext));
726
727 if (!sec->enabled) {
728 ext->alg = IW_ENCODE_ALG_NONE;
729 ext->key_len = 0;
730 encoding->flags |= IW_ENCODE_DISABLED;
731 } else {
732 if (sec->encode_alg[idx] == SEC_ALG_WEP)
733 ext->alg = IW_ENCODE_ALG_WEP;
734 else if (sec->encode_alg[idx] == SEC_ALG_TKIP)
735 ext->alg = IW_ENCODE_ALG_TKIP;
736 else if (sec->encode_alg[idx] == SEC_ALG_CCMP)
737 ext->alg = IW_ENCODE_ALG_CCMP;
738 else
739 return -EINVAL;
740
741 ext->key_len = sec->key_sizes[idx];
742 memcpy(ext->key, sec->keys[idx], ext->key_len);
743 encoding->flags |= IW_ENCODE_ENABLED;
744 if (ext->key_len &&
745 (ext->alg == IW_ENCODE_ALG_TKIP ||
746 ext->alg == IW_ENCODE_ALG_CCMP))
747 ext->ext_flags |= IW_ENCODE_EXT_TX_SEQ_VALID;
748
749 }
750
751 return 0;
752}
753
754EXPORT_SYMBOL(ieee80211_wx_set_encodeext);
755EXPORT_SYMBOL(ieee80211_wx_get_encodeext);
James Ketrenose0d369d2005-09-21 11:54:30 -0500756
Jeff Garzikb4538722005-05-12 22:48:20 -0400757EXPORT_SYMBOL(ieee80211_wx_get_scan);
758EXPORT_SYMBOL(ieee80211_wx_set_encode);
759EXPORT_SYMBOL(ieee80211_wx_get_encode);