blob: 29eb41695a82f1745898c94b4356a0bb742a4092 [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
John W. Linville9387b7c2008-09-30 20:59:05 -040037#include <net/lib80211.h>
Jeff Garzikb4538722005-05-12 22:48:20 -040038#include <net/ieee80211.h>
Jeff Garzikbbeec902005-09-07 00:27:54 -040039#include <linux/wireless.h>
40
Jeff Garzikb4538722005-05-12 22:48:20 -040041static const char *ieee80211_modes[] = {
42 "?", "a", "b", "ab", "g", "ag", "bg", "abg"
43};
44
45#define MAX_CUSTOM_LEN 64
Larry Fingerd94606e2006-03-03 16:21:55 -060046static char *ieee80211_translate_scan(struct ieee80211_device *ieee,
David S. Millerccc58052008-06-16 18:50:49 -070047 char *start, char *stop,
48 struct ieee80211_network *network,
49 struct iw_request_info *info)
Jeff Garzikb4538722005-05-12 22:48:20 -040050{
51 char custom[MAX_CUSTOM_LEN];
52 char *p;
53 struct iw_event iwe;
54 int i, j;
Zhu Yi09593042006-04-13 17:17:26 +080055 char *current_val; /* For rates */
56 u8 rate;
Jeff Garzikb4538722005-05-12 22:48:20 -040057
58 /* First entry *MUST* be the AP MAC address */
59 iwe.cmd = SIOCGIWAP;
60 iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
61 memcpy(iwe.u.ap_addr.sa_data, network->bssid, ETH_ALEN);
David S. Millerccc58052008-06-16 18:50:49 -070062 start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_ADDR_LEN);
Jeff Garzikb4538722005-05-12 22:48:20 -040063
64 /* Remaining entries will be displayed in the order we provide them */
65
66 /* Add the ESSID */
67 iwe.cmd = SIOCGIWESSID;
68 iwe.u.data.flags = 1;
John W. Linvillec5d3dce2008-09-30 17:17:26 -040069 iwe.u.data.length = min(network->ssid_len, (u8) 32);
70 start = iwe_stream_add_point(info, start, stop,
71 &iwe, network->ssid);
Jeff Garzikb4538722005-05-12 22:48:20 -040072
73 /* Add the protocol name */
74 iwe.cmd = SIOCGIWNAME;
Jeff Garzik0edd5b42005-09-07 00:48:31 -040075 snprintf(iwe.u.name, IFNAMSIZ, "IEEE 802.11%s",
76 ieee80211_modes[network->mode]);
David S. Millerccc58052008-06-16 18:50:49 -070077 start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_CHAR_LEN);
Jeff Garzikb4538722005-05-12 22:48:20 -040078
Jeff Garzik0edd5b42005-09-07 00:48:31 -040079 /* Add mode */
80 iwe.cmd = SIOCGIWMODE;
81 if (network->capability & (WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS)) {
Jeff Garzik1b5cca32005-08-15 00:32:15 -040082 if (network->capability & WLAN_CAPABILITY_ESS)
Jeff Garzikb4538722005-05-12 22:48:20 -040083 iwe.u.mode = IW_MODE_MASTER;
84 else
85 iwe.u.mode = IW_MODE_ADHOC;
86
David S. Millerccc58052008-06-16 18:50:49 -070087 start = iwe_stream_add_event(info, start, stop,
88 &iwe, IW_EV_UINT_LEN);
Jeff Garzikb4538722005-05-12 22:48:20 -040089 }
90
Larry Finger93afe3d2007-04-21 17:56:43 -050091 /* Add channel and frequency */
Jean Tourrilhes90869b22007-07-10 15:51:14 -050092 /* Note : userspace automatically computes channel using iwrange */
Jeff Garzikb4538722005-05-12 22:48:20 -040093 iwe.cmd = SIOCGIWFREQ;
Larry Finger93afe3d2007-04-21 17:56:43 -050094 iwe.u.freq.m = ieee80211_channel_to_freq(ieee, network->channel);
95 iwe.u.freq.e = 6;
Jean Tourrilhes90869b22007-07-10 15:51:14 -050096 iwe.u.freq.i = 0;
David S. Millerccc58052008-06-16 18:50:49 -070097 start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_FREQ_LEN);
Larry Finger93afe3d2007-04-21 17:56:43 -050098
Jeff Garzikb4538722005-05-12 22:48:20 -040099 /* Add encryption capability */
100 iwe.cmd = SIOCGIWENCODE;
101 if (network->capability & WLAN_CAPABILITY_PRIVACY)
102 iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
103 else
104 iwe.u.data.flags = IW_ENCODE_DISABLED;
105 iwe.u.data.length = 0;
David S. Millerccc58052008-06-16 18:50:49 -0700106 start = iwe_stream_add_point(info, start, stop,
107 &iwe, network->ssid);
Jeff Garzikb4538722005-05-12 22:48:20 -0400108
109 /* Add basic and extended rates */
Zhu Yi09593042006-04-13 17:17:26 +0800110 /* Rate : stuffing multiple values in a single event require a bit
111 * more of magic - Jean II */
David S. Millerccc58052008-06-16 18:50:49 -0700112 current_val = start + iwe_stream_lcp_len(info);
Zhu Yi09593042006-04-13 17:17:26 +0800113 iwe.cmd = SIOCGIWRATE;
114 /* Those two flags are ignored... */
115 iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0;
116
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400117 for (i = 0, j = 0; i < network->rates_len;) {
Jeff Garzikb4538722005-05-12 22:48:20 -0400118 if (j < network->rates_ex_len &&
119 ((network->rates_ex[j] & 0x7F) <
120 (network->rates[i] & 0x7F)))
121 rate = network->rates_ex[j++] & 0x7F;
122 else
123 rate = network->rates[i++] & 0x7F;
Zhu Yi09593042006-04-13 17:17:26 +0800124 /* Bit rate given in 500 kb/s units (+ 0x80) */
125 iwe.u.bitrate.value = ((rate & 0x7f) * 500000);
126 /* Add new value to event */
David S. Millerccc58052008-06-16 18:50:49 -0700127 current_val = iwe_stream_add_value(info, start, current_val,
128 stop, &iwe, IW_EV_PARAM_LEN);
Jeff Garzikb4538722005-05-12 22:48:20 -0400129 }
130 for (; j < network->rates_ex_len; j++) {
131 rate = network->rates_ex[j] & 0x7F;
Zhu Yi09593042006-04-13 17:17:26 +0800132 /* Bit rate given in 500 kb/s units (+ 0x80) */
133 iwe.u.bitrate.value = ((rate & 0x7f) * 500000);
134 /* Add new value to event */
David S. Millerccc58052008-06-16 18:50:49 -0700135 current_val = iwe_stream_add_value(info, start, current_val,
136 stop, &iwe, IW_EV_PARAM_LEN);
Jeff Garzikb4538722005-05-12 22:48:20 -0400137 }
Zhu Yi09593042006-04-13 17:17:26 +0800138 /* Check if we added any rate */
David S. Millerccc58052008-06-16 18:50:49 -0700139 if ((current_val - start) > iwe_stream_lcp_len(info))
Zhu Yi09593042006-04-13 17:17:26 +0800140 start = current_val;
Jeff Garzikb4538722005-05-12 22:48:20 -0400141
142 /* Add quality statistics */
Jeff Garzikb4538722005-05-12 22:48:20 -0400143 iwe.cmd = IWEVQUAL;
James Ketrenosb1b508e2005-09-13 17:27:19 -0500144 iwe.u.qual.updated = IW_QUAL_QUAL_UPDATED | IW_QUAL_LEVEL_UPDATED |
145 IW_QUAL_NOISE_UPDATED;
146
147 if (!(network->stats.mask & IEEE80211_STATMASK_RSSI)) {
148 iwe.u.qual.updated |= IW_QUAL_QUAL_INVALID |
149 IW_QUAL_LEVEL_INVALID;
150 iwe.u.qual.qual = 0;
James Ketrenosb1b508e2005-09-13 17:27:19 -0500151 } else {
Jiri Benc757d18f2005-10-10 19:16:53 +0200152 if (ieee->perfect_rssi == ieee->worst_rssi)
153 iwe.u.qual.qual = 100;
154 else
155 iwe.u.qual.qual =
156 (100 *
157 (ieee->perfect_rssi - ieee->worst_rssi) *
158 (ieee->perfect_rssi - ieee->worst_rssi) -
159 (ieee->perfect_rssi - network->stats.rssi) *
160 (15 * (ieee->perfect_rssi - ieee->worst_rssi) +
James Ketrenos81f87522005-10-24 10:20:53 -0500161 62 * (ieee->perfect_rssi -
162 network->stats.rssi))) /
163 ((ieee->perfect_rssi -
164 ieee->worst_rssi) * (ieee->perfect_rssi -
165 ieee->worst_rssi));
James Ketrenosb1b508e2005-09-13 17:27:19 -0500166 if (iwe.u.qual.qual > 100)
167 iwe.u.qual.qual = 100;
168 else if (iwe.u.qual.qual < 1)
169 iwe.u.qual.qual = 0;
170 }
171
172 if (!(network->stats.mask & IEEE80211_STATMASK_NOISE)) {
Jeff Garzikb4538722005-05-12 22:48:20 -0400173 iwe.u.qual.updated |= IW_QUAL_NOISE_INVALID;
James Ketrenosb1b508e2005-09-13 17:27:19 -0500174 iwe.u.qual.noise = 0;
175 } else {
176 iwe.u.qual.noise = network->stats.noise;
177 }
Jeff Garzikb4538722005-05-12 22:48:20 -0400178
Zhu Yi7bd64362006-01-19 16:21:54 +0800179 if (!(network->stats.mask & IEEE80211_STATMASK_SIGNAL)) {
180 iwe.u.qual.updated |= IW_QUAL_LEVEL_INVALID;
181 iwe.u.qual.level = 0;
182 } else {
183 iwe.u.qual.level = network->stats.signal;
184 }
185
David S. Millerccc58052008-06-16 18:50:49 -0700186 start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_QUAL_LEN);
Jeff Garzikb4538722005-05-12 22:48:20 -0400187
188 iwe.cmd = IWEVCUSTOM;
189 p = custom;
190
191 iwe.u.data.length = p - custom;
192 if (iwe.u.data.length)
David S. Millerccc58052008-06-16 18:50:49 -0700193 start = iwe_stream_add_point(info, start, stop, &iwe, custom);
Jeff Garzikb4538722005-05-12 22:48:20 -0400194
Zhu Yi47168082006-02-13 13:37:03 +0800195 memset(&iwe, 0, sizeof(iwe));
James Ketrenos20d64712005-09-21 11:53:43 -0500196 if (network->wpa_ie_len) {
Zhu Yi47168082006-02-13 13:37:03 +0800197 char buf[MAX_WPA_IE_LEN];
198 memcpy(buf, network->wpa_ie, network->wpa_ie_len);
199 iwe.cmd = IWEVGENIE;
200 iwe.u.data.length = network->wpa_ie_len;
David S. Millerccc58052008-06-16 18:50:49 -0700201 start = iwe_stream_add_point(info, start, stop, &iwe, buf);
Jeff Garzikb4538722005-05-12 22:48:20 -0400202 }
203
Zhu Yi47168082006-02-13 13:37:03 +0800204 memset(&iwe, 0, sizeof(iwe));
James Ketrenos20d64712005-09-21 11:53:43 -0500205 if (network->rsn_ie_len) {
Zhu Yi47168082006-02-13 13:37:03 +0800206 char buf[MAX_WPA_IE_LEN];
207 memcpy(buf, network->rsn_ie, network->rsn_ie_len);
208 iwe.cmd = IWEVGENIE;
209 iwe.u.data.length = network->rsn_ie_len;
David S. Millerccc58052008-06-16 18:50:49 -0700210 start = iwe_stream_add_point(info, start, stop, &iwe, buf);
Jeff Garzikb4538722005-05-12 22:48:20 -0400211 }
212
213 /* Add EXTRA: Age to display seconds since last beacon/probe response
214 * for given network. */
215 iwe.cmd = IWEVCUSTOM;
216 p = custom;
217 p += snprintf(p, MAX_CUSTOM_LEN - (p - custom),
James Ketrenos42e349f2005-09-21 11:54:07 -0500218 " Last beacon: %dms ago",
219 jiffies_to_msecs(jiffies - network->last_scanned));
Jeff Garzikb4538722005-05-12 22:48:20 -0400220 iwe.u.data.length = p - custom;
221 if (iwe.u.data.length)
David S. Millerccc58052008-06-16 18:50:49 -0700222 start = iwe_stream_add_point(info, start, stop, &iwe, custom);
Jeff Garzikb4538722005-05-12 22:48:20 -0400223
Zhu Yi7bd64362006-01-19 16:21:54 +0800224 /* Add spectrum management information */
225 iwe.cmd = -1;
226 p = custom;
227 p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), " Channel flags: ");
228
229 if (ieee80211_get_channel_flags(ieee, network->channel) &
230 IEEE80211_CH_INVALID) {
231 iwe.cmd = IWEVCUSTOM;
232 p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), "INVALID ");
233 }
234
235 if (ieee80211_get_channel_flags(ieee, network->channel) &
236 IEEE80211_CH_RADAR_DETECT) {
237 iwe.cmd = IWEVCUSTOM;
238 p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), "DFS ");
239 }
240
241 if (iwe.cmd == IWEVCUSTOM) {
242 iwe.u.data.length = p - custom;
David S. Millerccc58052008-06-16 18:50:49 -0700243 start = iwe_stream_add_point(info, start, stop, &iwe, custom);
Zhu Yi7bd64362006-01-19 16:21:54 +0800244 }
245
Jeff Garzikb4538722005-05-12 22:48:20 -0400246 return start;
247}
248
Zhu Yi55cd94a2006-01-19 16:20:59 +0800249#define SCAN_ITEM_SIZE 128
250
Jeff Garzikb4538722005-05-12 22:48:20 -0400251int ieee80211_wx_get_scan(struct ieee80211_device *ieee,
252 struct iw_request_info *info,
253 union iwreq_data *wrqu, char *extra)
254{
255 struct ieee80211_network *network;
256 unsigned long flags;
Zhu Yi55cd94a2006-01-19 16:20:59 +0800257 int err = 0;
Jeff Garzikb4538722005-05-12 22:48:20 -0400258
259 char *ev = extra;
Zhu Yi55cd94a2006-01-19 16:20:59 +0800260 char *stop = ev + wrqu->data.length;
Jeff Garzikb4538722005-05-12 22:48:20 -0400261 int i = 0;
John W. Linville9387b7c2008-09-30 20:59:05 -0400262 DECLARE_SSID_BUF(ssid);
Jeff Garzikb4538722005-05-12 22:48:20 -0400263
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))
David S. Millerccc58052008-06-16 18:50:49 -0700277 ev = ieee80211_translate_scan(ieee, ev, stop, network,
278 info);
Jeff Garzikb4538722005-05-12 22:48:20 -0400279 else
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400280 IEEE80211_DEBUG_SCAN("Not showing network '%s ("
Johannes Berge1749612008-10-27 15:59:26 -0700281 "%pM)' due to age (%dms).\n",
John W. Linville9387b7c2008-09-30 20:59:05 -0400282 print_ssid(ssid, network->ssid,
John W. Linville7e272fc2008-09-24 18:13:14 -0400283 network->ssid_len),
Johannes Berge1749612008-10-27 15:59:26 -0700284 network->bssid,
James Ketrenos42e349f2005-09-21 11:54:07 -0500285 jiffies_to_msecs(jiffies -
286 network->
287 last_scanned));
Jeff Garzikb4538722005-05-12 22:48:20 -0400288 }
289
290 spin_unlock_irqrestore(&ieee->lock, flags);
291
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400292 wrqu->data.length = ev - extra;
Jeff Garzikb4538722005-05-12 22:48:20 -0400293 wrqu->data.flags = 0;
294
295 IEEE80211_DEBUG_WX("exit: %d networks returned.\n", i);
296
Zhu Yi55cd94a2006-01-19 16:20:59 +0800297 return err;
Jeff Garzikb4538722005-05-12 22:48:20 -0400298}
299
300int ieee80211_wx_set_encode(struct ieee80211_device *ieee,
301 struct iw_request_info *info,
302 union iwreq_data *wrqu, char *keybuf)
303{
304 struct iw_point *erq = &(wrqu->encoding);
305 struct net_device *dev = ieee->dev;
306 struct ieee80211_security sec = {
307 .flags = 0
308 };
309 int i, key, key_provided, len;
310 struct ieee80211_crypt_data **crypt;
Johannes Berga4bf26f2005-12-31 11:35:20 +0100311 int host_crypto = ieee->host_encrypt || ieee->host_decrypt || ieee->host_build_iv;
John W. Linville9387b7c2008-09-30 20:59:05 -0400312 DECLARE_SSID_BUF(ssid);
Jeff Garzikb4538722005-05-12 22:48:20 -0400313
314 IEEE80211_DEBUG_WX("SET_ENCODE\n");
315
316 key = erq->flags & IW_ENCODE_INDEX;
317 if (key) {
318 if (key > WEP_KEYS)
319 return -EINVAL;
320 key--;
321 key_provided = 1;
322 } else {
323 key_provided = 0;
324 key = ieee->tx_keyidx;
325 }
326
327 IEEE80211_DEBUG_WX("Key: %d [%s]\n", key, key_provided ?
328 "provided" : "default");
329
330 crypt = &ieee->crypt[key];
331
332 if (erq->flags & IW_ENCODE_DISABLED) {
333 if (key_provided && *crypt) {
334 IEEE80211_DEBUG_WX("Disabling encryption on key %d.\n",
335 key);
336 ieee80211_crypt_delayed_deinit(ieee, crypt);
337 } else
338 IEEE80211_DEBUG_WX("Disabling encryption.\n");
339
340 /* Check all the keys to see if any are still configured,
341 * and if no key index was provided, de-init them all */
342 for (i = 0; i < WEP_KEYS; i++) {
343 if (ieee->crypt[i] != NULL) {
344 if (key_provided)
345 break;
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400346 ieee80211_crypt_delayed_deinit(ieee,
347 &ieee->crypt[i]);
Jeff Garzikb4538722005-05-12 22:48:20 -0400348 }
349 }
350
351 if (i == WEP_KEYS) {
352 sec.enabled = 0;
James Ketrenosf1bf6632005-09-21 11:53:54 -0500353 sec.encrypt = 0;
Jeff Garzikb4538722005-05-12 22:48:20 -0400354 sec.level = SEC_LEVEL_0;
James Ketrenos259bf1f2005-09-21 11:54:22 -0500355 sec.flags |= SEC_ENABLED | SEC_LEVEL | SEC_ENCRYPT;
Jeff Garzikb4538722005-05-12 22:48:20 -0400356 }
357
358 goto done;
359 }
360
Jeff Garzikb4538722005-05-12 22:48:20 -0400361 sec.enabled = 1;
James Ketrenosf1bf6632005-09-21 11:53:54 -0500362 sec.encrypt = 1;
James Ketrenos259bf1f2005-09-21 11:54:22 -0500363 sec.flags |= SEC_ENABLED | SEC_ENCRYPT;
Jeff Garzikb4538722005-05-12 22:48:20 -0400364
365 if (*crypt != NULL && (*crypt)->ops != NULL &&
366 strcmp((*crypt)->ops->name, "WEP") != 0) {
367 /* changing to use WEP; deinit previously used algorithm
368 * on this key */
369 ieee80211_crypt_delayed_deinit(ieee, crypt);
370 }
371
James Ketrenosf1bf6632005-09-21 11:53:54 -0500372 if (*crypt == NULL && host_crypto) {
Jeff Garzikb4538722005-05-12 22:48:20 -0400373 struct ieee80211_crypt_data *new_crypt;
374
375 /* take WEP into use */
Panagiotis Issaris0da974f2006-07-21 14:51:30 -0700376 new_crypt = kzalloc(sizeof(struct ieee80211_crypt_data),
Jeff Garzikb4538722005-05-12 22:48:20 -0400377 GFP_KERNEL);
378 if (new_crypt == NULL)
379 return -ENOMEM;
Jeff Garzikb4538722005-05-12 22:48:20 -0400380 new_crypt->ops = ieee80211_get_crypto_ops("WEP");
381 if (!new_crypt->ops) {
382 request_module("ieee80211_crypt_wep");
383 new_crypt->ops = ieee80211_get_crypto_ops("WEP");
384 }
385
386 if (new_crypt->ops && try_module_get(new_crypt->ops->owner))
James Ketrenos6eb6edf2005-09-22 10:34:15 +0000387 new_crypt->priv = new_crypt->ops->init(key);
Jeff Garzikb4538722005-05-12 22:48:20 -0400388
389 if (!new_crypt->ops || !new_crypt->priv) {
390 kfree(new_crypt);
391 new_crypt = NULL;
392
393 printk(KERN_WARNING "%s: could not initialize WEP: "
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400394 "load module ieee80211_crypt_wep\n", dev->name);
Jeff Garzikb4538722005-05-12 22:48:20 -0400395 return -EOPNOTSUPP;
396 }
397 *crypt = new_crypt;
398 }
399
400 /* If a new key was provided, set it up */
401 if (erq->length > 0) {
402 len = erq->length <= 5 ? 5 : 13;
403 memcpy(sec.keys[key], keybuf, erq->length);
404 if (len > erq->length)
405 memset(sec.keys[key] + erq->length, 0,
406 len - erq->length);
407 IEEE80211_DEBUG_WX("Setting key %d to '%s' (%d:%d bytes)\n",
John W. Linville9387b7c2008-09-30 20:59:05 -0400408 key, print_ssid(ssid, sec.keys[key], len),
Jeff Garzikb4538722005-05-12 22:48:20 -0400409 erq->length, len);
410 sec.key_sizes[key] = len;
James Ketrenosf1bf6632005-09-21 11:53:54 -0500411 if (*crypt)
412 (*crypt)->ops->set_key(sec.keys[key], len, NULL,
413 (*crypt)->priv);
Jeff Garzikb4538722005-05-12 22:48:20 -0400414 sec.flags |= (1 << key);
415 /* This ensures a key will be activated if no key is
Jean Delvarec03983a2007-10-19 23:22:55 +0200416 * explicitly set */
Jeff Garzikb4538722005-05-12 22:48:20 -0400417 if (key == sec.active_key)
418 sec.flags |= SEC_ACTIVE_KEY;
Jeff Garzikb4538722005-05-12 22:48:20 -0400419
James Ketrenosf1bf6632005-09-21 11:53:54 -0500420 } else {
421 if (host_crypto) {
422 len = (*crypt)->ops->get_key(sec.keys[key], WEP_KEY_LEN,
423 NULL, (*crypt)->priv);
424 if (len == 0) {
425 /* Set a default key of all 0 */
426 IEEE80211_DEBUG_WX("Setting key %d to all "
427 "zero.\n", key);
428 memset(sec.keys[key], 0, 13);
429 (*crypt)->ops->set_key(sec.keys[key], 13, NULL,
430 (*crypt)->priv);
431 sec.key_sizes[key] = 13;
432 sec.flags |= (1 << key);
433 }
434 }
Jeff Garzikb4538722005-05-12 22:48:20 -0400435 /* No key data - just set the default TX key index */
436 if (key_provided) {
James Ketrenosf1bf6632005-09-21 11:53:54 -0500437 IEEE80211_DEBUG_WX("Setting key %d to default Tx "
438 "key.\n", key);
Jeff Garzikb4538722005-05-12 22:48:20 -0400439 ieee->tx_keyidx = key;
440 sec.active_key = key;
441 sec.flags |= SEC_ACTIVE_KEY;
442 }
443 }
James Ketrenos7dc888f2005-09-21 11:58:38 -0500444 if (erq->flags & (IW_ENCODE_OPEN | IW_ENCODE_RESTRICTED)) {
445 ieee->open_wep = !(erq->flags & IW_ENCODE_RESTRICTED);
446 sec.auth_mode = ieee->open_wep ? WLAN_AUTH_OPEN :
447 WLAN_AUTH_SHARED_KEY;
448 sec.flags |= SEC_AUTH_MODE;
449 IEEE80211_DEBUG_WX("Auth: %s\n",
450 sec.auth_mode == WLAN_AUTH_OPEN ?
451 "OPEN" : "SHARED KEY");
452 }
Jeff Garzikb4538722005-05-12 22:48:20 -0400453
454 /* For now we just support WEP, so only set that security level...
455 * TODO: When WPA is added this is one place that needs to change */
456 sec.flags |= SEC_LEVEL;
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400457 sec.level = SEC_LEVEL_1; /* 40 and 104 bit WEP */
James Ketrenose0d369d2005-09-21 11:54:30 -0500458 sec.encode_alg[key] = SEC_ALG_WEP;
Jeff Garzikb4538722005-05-12 22:48:20 -0400459
James Ketrenos259bf1f2005-09-21 11:54:22 -0500460 done:
Jeff Garzikb4538722005-05-12 22:48:20 -0400461 if (ieee->set_security)
462 ieee->set_security(dev, &sec);
463
464 /* Do not reset port if card is in Managed mode since resetting will
465 * generate new IEEE 802.11 authentication which may end up in looping
466 * with IEEE 802.1X. If your hardware requires a reset after WEP
467 * configuration (for example... Prism2), implement the reset_port in
468 * the callbacks structures used to initialize the 802.11 stack. */
469 if (ieee->reset_on_keychange &&
470 ieee->iw_mode != IW_MODE_INFRA &&
471 ieee->reset_port && ieee->reset_port(dev)) {
472 printk(KERN_DEBUG "%s: reset_port failed\n", dev->name);
473 return -EINVAL;
474 }
475 return 0;
476}
477
478int ieee80211_wx_get_encode(struct ieee80211_device *ieee,
479 struct iw_request_info *info,
480 union iwreq_data *wrqu, char *keybuf)
481{
482 struct iw_point *erq = &(wrqu->encoding);
483 int len, key;
484 struct ieee80211_crypt_data *crypt;
James Ketrenosf1bf6632005-09-21 11:53:54 -0500485 struct ieee80211_security *sec = &ieee->sec;
Jeff Garzikb4538722005-05-12 22:48:20 -0400486
487 IEEE80211_DEBUG_WX("GET_ENCODE\n");
488
489 key = erq->flags & IW_ENCODE_INDEX;
490 if (key) {
491 if (key > WEP_KEYS)
492 return -EINVAL;
493 key--;
494 } else
495 key = ieee->tx_keyidx;
496
497 crypt = ieee->crypt[key];
498 erq->flags = key + 1;
499
James Ketrenosf1bf6632005-09-21 11:53:54 -0500500 if (!sec->enabled) {
Jeff Garzikb4538722005-05-12 22:48:20 -0400501 erq->length = 0;
502 erq->flags |= IW_ENCODE_DISABLED;
503 return 0;
504 }
505
James Ketrenosf1bf6632005-09-21 11:53:54 -0500506 len = sec->key_sizes[key];
507 memcpy(keybuf, sec->keys[key], len);
Jeff Garzikb4538722005-05-12 22:48:20 -0400508
Adrian Bunk62741152006-04-27 02:33:42 -0700509 erq->length = len;
Jeff Garzikb4538722005-05-12 22:48:20 -0400510 erq->flags |= IW_ENCODE_ENABLED;
511
512 if (ieee->open_wep)
513 erq->flags |= IW_ENCODE_OPEN;
514 else
515 erq->flags |= IW_ENCODE_RESTRICTED;
516
517 return 0;
518}
519
James Ketrenose0d369d2005-09-21 11:54:30 -0500520int ieee80211_wx_set_encodeext(struct ieee80211_device *ieee,
521 struct iw_request_info *info,
522 union iwreq_data *wrqu, char *extra)
523{
524 struct net_device *dev = ieee->dev;
525 struct iw_point *encoding = &wrqu->encoding;
526 struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
527 int i, idx, ret = 0;
James Ketrenosccd0fda2005-09-21 11:58:32 -0500528 int group_key = 0;
James Ketrenose0d369d2005-09-21 11:54:30 -0500529 const char *alg, *module;
530 struct ieee80211_crypto_ops *ops;
531 struct ieee80211_crypt_data **crypt;
532
533 struct ieee80211_security sec = {
534 .flags = 0,
535 };
536
537 idx = encoding->flags & IW_ENCODE_INDEX;
538 if (idx) {
539 if (idx < 1 || idx > WEP_KEYS)
540 return -EINVAL;
541 idx--;
542 } else
543 idx = ieee->tx_keyidx;
544
James Ketrenosccd0fda2005-09-21 11:58:32 -0500545 if (ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) {
James Ketrenose0d369d2005-09-21 11:54:30 -0500546 crypt = &ieee->crypt[idx];
James Ketrenosccd0fda2005-09-21 11:58:32 -0500547 group_key = 1;
548 } else {
Volker Braune1892772005-10-24 10:15:36 -0500549 /* some Cisco APs use idx>0 for unicast in dynamic WEP */
550 if (idx != 0 && ext->alg != IW_ENCODE_ALG_WEP)
James Ketrenose0d369d2005-09-21 11:54:30 -0500551 return -EINVAL;
552 if (ieee->iw_mode == IW_MODE_INFRA)
553 crypt = &ieee->crypt[idx];
554 else
555 return -EINVAL;
556 }
557
558 sec.flags |= SEC_ENABLED | SEC_ENCRYPT;
559 if ((encoding->flags & IW_ENCODE_DISABLED) ||
560 ext->alg == IW_ENCODE_ALG_NONE) {
561 if (*crypt)
562 ieee80211_crypt_delayed_deinit(ieee, crypt);
563
564 for (i = 0; i < WEP_KEYS; i++)
565 if (ieee->crypt[i] != NULL)
566 break;
567
568 if (i == WEP_KEYS) {
569 sec.enabled = 0;
570 sec.encrypt = 0;
571 sec.level = SEC_LEVEL_0;
572 sec.flags |= SEC_LEVEL;
573 }
574 goto done;
575 }
576
577 sec.enabled = 1;
578 sec.encrypt = 1;
579
James Ketrenosccd0fda2005-09-21 11:58:32 -0500580 if (group_key ? !ieee->host_mc_decrypt :
581 !(ieee->host_encrypt || ieee->host_decrypt ||
582 ieee->host_encrypt_msdu))
James Ketrenose0d369d2005-09-21 11:54:30 -0500583 goto skip_host_crypt;
584
585 switch (ext->alg) {
586 case IW_ENCODE_ALG_WEP:
587 alg = "WEP";
588 module = "ieee80211_crypt_wep";
589 break;
590 case IW_ENCODE_ALG_TKIP:
591 alg = "TKIP";
592 module = "ieee80211_crypt_tkip";
593 break;
594 case IW_ENCODE_ALG_CCMP:
595 alg = "CCMP";
596 module = "ieee80211_crypt_ccmp";
597 break;
598 default:
599 IEEE80211_DEBUG_WX("%s: unknown crypto alg %d\n",
600 dev->name, ext->alg);
601 ret = -EINVAL;
602 goto done;
603 }
604
605 ops = ieee80211_get_crypto_ops(alg);
606 if (ops == NULL) {
607 request_module(module);
608 ops = ieee80211_get_crypto_ops(alg);
609 }
610 if (ops == NULL) {
611 IEEE80211_DEBUG_WX("%s: unknown crypto alg %d\n",
612 dev->name, ext->alg);
613 ret = -EINVAL;
614 goto done;
615 }
616
617 if (*crypt == NULL || (*crypt)->ops != ops) {
618 struct ieee80211_crypt_data *new_crypt;
619
620 ieee80211_crypt_delayed_deinit(ieee, crypt);
621
Panagiotis Issaris0da974f2006-07-21 14:51:30 -0700622 new_crypt = kzalloc(sizeof(*new_crypt), GFP_KERNEL);
James Ketrenose0d369d2005-09-21 11:54:30 -0500623 if (new_crypt == NULL) {
624 ret = -ENOMEM;
625 goto done;
626 }
James Ketrenose0d369d2005-09-21 11:54:30 -0500627 new_crypt->ops = ops;
628 if (new_crypt->ops && try_module_get(new_crypt->ops->owner))
James Ketrenos6eb6edf2005-09-22 10:34:15 +0000629 new_crypt->priv = new_crypt->ops->init(idx);
James Ketrenose0d369d2005-09-21 11:54:30 -0500630 if (new_crypt->priv == NULL) {
631 kfree(new_crypt);
632 ret = -EINVAL;
633 goto done;
634 }
635 *crypt = new_crypt;
636 }
637
638 if (ext->key_len > 0 && (*crypt)->ops->set_key &&
639 (*crypt)->ops->set_key(ext->key, ext->key_len, ext->rx_seq,
640 (*crypt)->priv) < 0) {
641 IEEE80211_DEBUG_WX("%s: key setting failed\n", dev->name);
642 ret = -EINVAL;
643 goto done;
644 }
645
646 skip_host_crypt:
647 if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) {
648 ieee->tx_keyidx = idx;
649 sec.active_key = idx;
650 sec.flags |= SEC_ACTIVE_KEY;
651 }
652
653 if (ext->alg != IW_ENCODE_ALG_NONE) {
654 memcpy(sec.keys[idx], ext->key, ext->key_len);
655 sec.key_sizes[idx] = ext->key_len;
656 sec.flags |= (1 << idx);
657 if (ext->alg == IW_ENCODE_ALG_WEP) {
658 sec.encode_alg[idx] = SEC_ALG_WEP;
659 sec.flags |= SEC_LEVEL;
660 sec.level = SEC_LEVEL_1;
661 } else if (ext->alg == IW_ENCODE_ALG_TKIP) {
662 sec.encode_alg[idx] = SEC_ALG_TKIP;
663 sec.flags |= SEC_LEVEL;
664 sec.level = SEC_LEVEL_2;
665 } else if (ext->alg == IW_ENCODE_ALG_CCMP) {
666 sec.encode_alg[idx] = SEC_ALG_CCMP;
667 sec.flags |= SEC_LEVEL;
668 sec.level = SEC_LEVEL_3;
669 }
James Ketrenosccd0fda2005-09-21 11:58:32 -0500670 /* Don't set sec level for group keys. */
671 if (group_key)
672 sec.flags &= ~SEC_LEVEL;
James Ketrenose0d369d2005-09-21 11:54:30 -0500673 }
674 done:
675 if (ieee->set_security)
676 ieee->set_security(ieee->dev, &sec);
677
678 /*
679 * Do not reset port if card is in Managed mode since resetting will
680 * generate new IEEE 802.11 authentication which may end up in looping
681 * with IEEE 802.1X. If your hardware requires a reset after WEP
682 * configuration (for example... Prism2), implement the reset_port in
683 * the callbacks structures used to initialize the 802.11 stack.
684 */
685 if (ieee->reset_on_keychange &&
686 ieee->iw_mode != IW_MODE_INFRA &&
687 ieee->reset_port && ieee->reset_port(dev)) {
688 IEEE80211_DEBUG_WX("%s: reset_port failed\n", dev->name);
689 return -EINVAL;
690 }
691
692 return ret;
693}
694
695int ieee80211_wx_get_encodeext(struct ieee80211_device *ieee,
696 struct iw_request_info *info,
697 union iwreq_data *wrqu, char *extra)
698{
699 struct iw_point *encoding = &wrqu->encoding;
700 struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
701 struct ieee80211_security *sec = &ieee->sec;
702 int idx, max_key_len;
703
704 max_key_len = encoding->length - sizeof(*ext);
705 if (max_key_len < 0)
706 return -EINVAL;
707
708 idx = encoding->flags & IW_ENCODE_INDEX;
709 if (idx) {
710 if (idx < 1 || idx > WEP_KEYS)
711 return -EINVAL;
712 idx--;
713 } else
714 idx = ieee->tx_keyidx;
715
Roel Kluinf59d9782007-10-26 21:51:26 +0200716 if (!(ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) &&
Volker Braune1892772005-10-24 10:15:36 -0500717 ext->alg != IW_ENCODE_ALG_WEP)
James Ketrenose0d369d2005-09-21 11:54:30 -0500718 if (idx != 0 || ieee->iw_mode != IW_MODE_INFRA)
719 return -EINVAL;
720
721 encoding->flags = idx + 1;
722 memset(ext, 0, sizeof(*ext));
723
724 if (!sec->enabled) {
725 ext->alg = IW_ENCODE_ALG_NONE;
726 ext->key_len = 0;
727 encoding->flags |= IW_ENCODE_DISABLED;
728 } else {
729 if (sec->encode_alg[idx] == SEC_ALG_WEP)
730 ext->alg = IW_ENCODE_ALG_WEP;
731 else if (sec->encode_alg[idx] == SEC_ALG_TKIP)
732 ext->alg = IW_ENCODE_ALG_TKIP;
733 else if (sec->encode_alg[idx] == SEC_ALG_CCMP)
734 ext->alg = IW_ENCODE_ALG_CCMP;
735 else
736 return -EINVAL;
737
738 ext->key_len = sec->key_sizes[idx];
739 memcpy(ext->key, sec->keys[idx], ext->key_len);
740 encoding->flags |= IW_ENCODE_ENABLED;
741 if (ext->key_len &&
742 (ext->alg == IW_ENCODE_ALG_TKIP ||
743 ext->alg == IW_ENCODE_ALG_CCMP))
744 ext->ext_flags |= IW_ENCODE_EXT_TX_SEQ_VALID;
745
746 }
747
748 return 0;
749}
750
751EXPORT_SYMBOL(ieee80211_wx_set_encodeext);
752EXPORT_SYMBOL(ieee80211_wx_get_encodeext);
James Ketrenose0d369d2005-09-21 11:54:30 -0500753
Jeff Garzikb4538722005-05-12 22:48:20 -0400754EXPORT_SYMBOL(ieee80211_wx_get_scan);
755EXPORT_SYMBOL(ieee80211_wx_set_encode);
756EXPORT_SYMBOL(ieee80211_wx_get_encode);