blob: 31ea3abfc3277cb672d930007992dd504276186e [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;
John W. Linville274bfb82008-10-29 11:35:05 -0400310 struct lib80211_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;
John W. Linville274bfb82008-10-29 11:35:05 -0400324 key = ieee->crypt_info.tx_keyidx;
Jeff Garzikb4538722005-05-12 22:48:20 -0400325 }
326
327 IEEE80211_DEBUG_WX("Key: %d [%s]\n", key, key_provided ?
328 "provided" : "default");
329
John W. Linville274bfb82008-10-29 11:35:05 -0400330 crypt = &ieee->crypt_info.crypt[key];
Jeff Garzikb4538722005-05-12 22:48:20 -0400331
332 if (erq->flags & IW_ENCODE_DISABLED) {
333 if (key_provided && *crypt) {
334 IEEE80211_DEBUG_WX("Disabling encryption on key %d.\n",
335 key);
John W. Linville274bfb82008-10-29 11:35:05 -0400336 lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt);
Jeff Garzikb4538722005-05-12 22:48:20 -0400337 } 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++) {
John W. Linville274bfb82008-10-29 11:35:05 -0400343 if (ieee->crypt_info.crypt[i] != NULL) {
Jeff Garzikb4538722005-05-12 22:48:20 -0400344 if (key_provided)
345 break;
John W. Linville274bfb82008-10-29 11:35:05 -0400346 lib80211_crypt_delayed_deinit(&ieee->crypt_info,
347 &ieee->crypt_info.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 */
John W. Linville274bfb82008-10-29 11:35:05 -0400369 lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt);
Jeff Garzikb4538722005-05-12 22:48:20 -0400370 }
371
James Ketrenosf1bf6632005-09-21 11:53:54 -0500372 if (*crypt == NULL && host_crypto) {
John W. Linville274bfb82008-10-29 11:35:05 -0400373 struct lib80211_crypt_data *new_crypt;
Jeff Garzikb4538722005-05-12 22:48:20 -0400374
375 /* take WEP into use */
John W. Linville274bfb82008-10-29 11:35:05 -0400376 new_crypt = kzalloc(sizeof(struct lib80211_crypt_data),
Jeff Garzikb4538722005-05-12 22:48:20 -0400377 GFP_KERNEL);
378 if (new_crypt == NULL)
379 return -ENOMEM;
John W. Linville274bfb82008-10-29 11:35:05 -0400380 new_crypt->ops = lib80211_get_crypto_ops("WEP");
Jeff Garzikb4538722005-05-12 22:48:20 -0400381 if (!new_crypt->ops) {
John W. Linville274bfb82008-10-29 11:35:05 -0400382 request_module("lib80211_crypt_wep");
383 new_crypt->ops = lib80211_get_crypto_ops("WEP");
Jeff Garzikb4538722005-05-12 22:48:20 -0400384 }
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: "
John W. Linville274bfb82008-10-29 11:35:05 -0400394 "load module lib80211_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) {
Holger Schurig2a941ec2008-10-29 09:43:50 +0100402#ifdef CONFIG_IEEE80211_DEBUG
403 DECLARE_SSID_BUF(ssid);
404#endif
405
Jeff Garzikb4538722005-05-12 22:48:20 -0400406 len = erq->length <= 5 ? 5 : 13;
407 memcpy(sec.keys[key], keybuf, erq->length);
408 if (len > erq->length)
409 memset(sec.keys[key] + erq->length, 0,
410 len - erq->length);
411 IEEE80211_DEBUG_WX("Setting key %d to '%s' (%d:%d bytes)\n",
John W. Linville9387b7c2008-09-30 20:59:05 -0400412 key, print_ssid(ssid, sec.keys[key], len),
Jeff Garzikb4538722005-05-12 22:48:20 -0400413 erq->length, len);
414 sec.key_sizes[key] = len;
James Ketrenosf1bf6632005-09-21 11:53:54 -0500415 if (*crypt)
416 (*crypt)->ops->set_key(sec.keys[key], len, NULL,
417 (*crypt)->priv);
Jeff Garzikb4538722005-05-12 22:48:20 -0400418 sec.flags |= (1 << key);
419 /* This ensures a key will be activated if no key is
Jean Delvarec03983a2007-10-19 23:22:55 +0200420 * explicitly set */
Jeff Garzikb4538722005-05-12 22:48:20 -0400421 if (key == sec.active_key)
422 sec.flags |= SEC_ACTIVE_KEY;
Jeff Garzikb4538722005-05-12 22:48:20 -0400423
James Ketrenosf1bf6632005-09-21 11:53:54 -0500424 } else {
425 if (host_crypto) {
426 len = (*crypt)->ops->get_key(sec.keys[key], WEP_KEY_LEN,
427 NULL, (*crypt)->priv);
428 if (len == 0) {
429 /* Set a default key of all 0 */
430 IEEE80211_DEBUG_WX("Setting key %d to all "
431 "zero.\n", key);
432 memset(sec.keys[key], 0, 13);
433 (*crypt)->ops->set_key(sec.keys[key], 13, NULL,
434 (*crypt)->priv);
435 sec.key_sizes[key] = 13;
436 sec.flags |= (1 << key);
437 }
438 }
Jeff Garzikb4538722005-05-12 22:48:20 -0400439 /* No key data - just set the default TX key index */
440 if (key_provided) {
James Ketrenosf1bf6632005-09-21 11:53:54 -0500441 IEEE80211_DEBUG_WX("Setting key %d to default Tx "
442 "key.\n", key);
John W. Linville274bfb82008-10-29 11:35:05 -0400443 ieee->crypt_info.tx_keyidx = key;
Jeff Garzikb4538722005-05-12 22:48:20 -0400444 sec.active_key = key;
445 sec.flags |= SEC_ACTIVE_KEY;
446 }
447 }
James Ketrenos7dc888f2005-09-21 11:58:38 -0500448 if (erq->flags & (IW_ENCODE_OPEN | IW_ENCODE_RESTRICTED)) {
449 ieee->open_wep = !(erq->flags & IW_ENCODE_RESTRICTED);
450 sec.auth_mode = ieee->open_wep ? WLAN_AUTH_OPEN :
451 WLAN_AUTH_SHARED_KEY;
452 sec.flags |= SEC_AUTH_MODE;
453 IEEE80211_DEBUG_WX("Auth: %s\n",
454 sec.auth_mode == WLAN_AUTH_OPEN ?
455 "OPEN" : "SHARED KEY");
456 }
Jeff Garzikb4538722005-05-12 22:48:20 -0400457
458 /* For now we just support WEP, so only set that security level...
459 * TODO: When WPA is added this is one place that needs to change */
460 sec.flags |= SEC_LEVEL;
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400461 sec.level = SEC_LEVEL_1; /* 40 and 104 bit WEP */
James Ketrenose0d369d2005-09-21 11:54:30 -0500462 sec.encode_alg[key] = SEC_ALG_WEP;
Jeff Garzikb4538722005-05-12 22:48:20 -0400463
James Ketrenos259bf1f2005-09-21 11:54:22 -0500464 done:
Jeff Garzikb4538722005-05-12 22:48:20 -0400465 if (ieee->set_security)
466 ieee->set_security(dev, &sec);
467
468 /* Do not reset port if card is in Managed mode since resetting will
469 * generate new IEEE 802.11 authentication which may end up in looping
470 * with IEEE 802.1X. If your hardware requires a reset after WEP
471 * configuration (for example... Prism2), implement the reset_port in
472 * the callbacks structures used to initialize the 802.11 stack. */
473 if (ieee->reset_on_keychange &&
474 ieee->iw_mode != IW_MODE_INFRA &&
475 ieee->reset_port && ieee->reset_port(dev)) {
476 printk(KERN_DEBUG "%s: reset_port failed\n", dev->name);
477 return -EINVAL;
478 }
479 return 0;
480}
481
482int ieee80211_wx_get_encode(struct ieee80211_device *ieee,
483 struct iw_request_info *info,
484 union iwreq_data *wrqu, char *keybuf)
485{
486 struct iw_point *erq = &(wrqu->encoding);
487 int len, key;
John W. Linville274bfb82008-10-29 11:35:05 -0400488 struct lib80211_crypt_data *crypt;
James Ketrenosf1bf6632005-09-21 11:53:54 -0500489 struct ieee80211_security *sec = &ieee->sec;
Jeff Garzikb4538722005-05-12 22:48:20 -0400490
491 IEEE80211_DEBUG_WX("GET_ENCODE\n");
492
493 key = erq->flags & IW_ENCODE_INDEX;
494 if (key) {
495 if (key > WEP_KEYS)
496 return -EINVAL;
497 key--;
498 } else
John W. Linville274bfb82008-10-29 11:35:05 -0400499 key = ieee->crypt_info.tx_keyidx;
Jeff Garzikb4538722005-05-12 22:48:20 -0400500
John W. Linville274bfb82008-10-29 11:35:05 -0400501 crypt = ieee->crypt_info.crypt[key];
Jeff Garzikb4538722005-05-12 22:48:20 -0400502 erq->flags = key + 1;
503
James Ketrenosf1bf6632005-09-21 11:53:54 -0500504 if (!sec->enabled) {
Jeff Garzikb4538722005-05-12 22:48:20 -0400505 erq->length = 0;
506 erq->flags |= IW_ENCODE_DISABLED;
507 return 0;
508 }
509
James Ketrenosf1bf6632005-09-21 11:53:54 -0500510 len = sec->key_sizes[key];
511 memcpy(keybuf, sec->keys[key], len);
Jeff Garzikb4538722005-05-12 22:48:20 -0400512
Adrian Bunk62741152006-04-27 02:33:42 -0700513 erq->length = len;
Jeff Garzikb4538722005-05-12 22:48:20 -0400514 erq->flags |= IW_ENCODE_ENABLED;
515
516 if (ieee->open_wep)
517 erq->flags |= IW_ENCODE_OPEN;
518 else
519 erq->flags |= IW_ENCODE_RESTRICTED;
520
521 return 0;
522}
523
James Ketrenose0d369d2005-09-21 11:54:30 -0500524int ieee80211_wx_set_encodeext(struct ieee80211_device *ieee,
525 struct iw_request_info *info,
526 union iwreq_data *wrqu, char *extra)
527{
528 struct net_device *dev = ieee->dev;
529 struct iw_point *encoding = &wrqu->encoding;
530 struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
531 int i, idx, ret = 0;
James Ketrenosccd0fda2005-09-21 11:58:32 -0500532 int group_key = 0;
James Ketrenose0d369d2005-09-21 11:54:30 -0500533 const char *alg, *module;
John W. Linville274bfb82008-10-29 11:35:05 -0400534 struct lib80211_crypto_ops *ops;
535 struct lib80211_crypt_data **crypt;
James Ketrenose0d369d2005-09-21 11:54:30 -0500536
537 struct ieee80211_security sec = {
538 .flags = 0,
539 };
540
541 idx = encoding->flags & IW_ENCODE_INDEX;
542 if (idx) {
543 if (idx < 1 || idx > WEP_KEYS)
544 return -EINVAL;
545 idx--;
546 } else
John W. Linville274bfb82008-10-29 11:35:05 -0400547 idx = ieee->crypt_info.tx_keyidx;
James Ketrenose0d369d2005-09-21 11:54:30 -0500548
James Ketrenosccd0fda2005-09-21 11:58:32 -0500549 if (ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) {
John W. Linville274bfb82008-10-29 11:35:05 -0400550 crypt = &ieee->crypt_info.crypt[idx];
James Ketrenosccd0fda2005-09-21 11:58:32 -0500551 group_key = 1;
552 } else {
Volker Braune1892772005-10-24 10:15:36 -0500553 /* some Cisco APs use idx>0 for unicast in dynamic WEP */
554 if (idx != 0 && ext->alg != IW_ENCODE_ALG_WEP)
James Ketrenose0d369d2005-09-21 11:54:30 -0500555 return -EINVAL;
556 if (ieee->iw_mode == IW_MODE_INFRA)
John W. Linville274bfb82008-10-29 11:35:05 -0400557 crypt = &ieee->crypt_info.crypt[idx];
James Ketrenose0d369d2005-09-21 11:54:30 -0500558 else
559 return -EINVAL;
560 }
561
562 sec.flags |= SEC_ENABLED | SEC_ENCRYPT;
563 if ((encoding->flags & IW_ENCODE_DISABLED) ||
564 ext->alg == IW_ENCODE_ALG_NONE) {
565 if (*crypt)
John W. Linville274bfb82008-10-29 11:35:05 -0400566 lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt);
James Ketrenose0d369d2005-09-21 11:54:30 -0500567
568 for (i = 0; i < WEP_KEYS; i++)
John W. Linville274bfb82008-10-29 11:35:05 -0400569 if (ieee->crypt_info.crypt[i] != NULL)
James Ketrenose0d369d2005-09-21 11:54:30 -0500570 break;
571
572 if (i == WEP_KEYS) {
573 sec.enabled = 0;
574 sec.encrypt = 0;
575 sec.level = SEC_LEVEL_0;
576 sec.flags |= SEC_LEVEL;
577 }
578 goto done;
579 }
580
581 sec.enabled = 1;
582 sec.encrypt = 1;
583
James Ketrenosccd0fda2005-09-21 11:58:32 -0500584 if (group_key ? !ieee->host_mc_decrypt :
585 !(ieee->host_encrypt || ieee->host_decrypt ||
586 ieee->host_encrypt_msdu))
James Ketrenose0d369d2005-09-21 11:54:30 -0500587 goto skip_host_crypt;
588
589 switch (ext->alg) {
590 case IW_ENCODE_ALG_WEP:
591 alg = "WEP";
John W. Linville274bfb82008-10-29 11:35:05 -0400592 module = "lib80211_crypt_wep";
James Ketrenose0d369d2005-09-21 11:54:30 -0500593 break;
594 case IW_ENCODE_ALG_TKIP:
595 alg = "TKIP";
John W. Linville274bfb82008-10-29 11:35:05 -0400596 module = "lib80211_crypt_tkip";
James Ketrenose0d369d2005-09-21 11:54:30 -0500597 break;
598 case IW_ENCODE_ALG_CCMP:
599 alg = "CCMP";
John W. Linville274bfb82008-10-29 11:35:05 -0400600 module = "lib80211_crypt_ccmp";
James Ketrenose0d369d2005-09-21 11:54:30 -0500601 break;
602 default:
603 IEEE80211_DEBUG_WX("%s: unknown crypto alg %d\n",
604 dev->name, ext->alg);
605 ret = -EINVAL;
606 goto done;
607 }
608
John W. Linville274bfb82008-10-29 11:35:05 -0400609 ops = lib80211_get_crypto_ops(alg);
James Ketrenose0d369d2005-09-21 11:54:30 -0500610 if (ops == NULL) {
611 request_module(module);
John W. Linville274bfb82008-10-29 11:35:05 -0400612 ops = lib80211_get_crypto_ops(alg);
James Ketrenose0d369d2005-09-21 11:54:30 -0500613 }
614 if (ops == NULL) {
615 IEEE80211_DEBUG_WX("%s: unknown crypto alg %d\n",
616 dev->name, ext->alg);
617 ret = -EINVAL;
618 goto done;
619 }
620
621 if (*crypt == NULL || (*crypt)->ops != ops) {
John W. Linville274bfb82008-10-29 11:35:05 -0400622 struct lib80211_crypt_data *new_crypt;
James Ketrenose0d369d2005-09-21 11:54:30 -0500623
John W. Linville274bfb82008-10-29 11:35:05 -0400624 lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt);
James Ketrenose0d369d2005-09-21 11:54:30 -0500625
Panagiotis Issaris0da974f2006-07-21 14:51:30 -0700626 new_crypt = kzalloc(sizeof(*new_crypt), GFP_KERNEL);
James Ketrenose0d369d2005-09-21 11:54:30 -0500627 if (new_crypt == NULL) {
628 ret = -ENOMEM;
629 goto done;
630 }
James Ketrenose0d369d2005-09-21 11:54:30 -0500631 new_crypt->ops = ops;
632 if (new_crypt->ops && try_module_get(new_crypt->ops->owner))
James Ketrenos6eb6edf2005-09-22 10:34:15 +0000633 new_crypt->priv = new_crypt->ops->init(idx);
James Ketrenose0d369d2005-09-21 11:54:30 -0500634 if (new_crypt->priv == NULL) {
635 kfree(new_crypt);
636 ret = -EINVAL;
637 goto done;
638 }
639 *crypt = new_crypt;
640 }
641
642 if (ext->key_len > 0 && (*crypt)->ops->set_key &&
643 (*crypt)->ops->set_key(ext->key, ext->key_len, ext->rx_seq,
644 (*crypt)->priv) < 0) {
645 IEEE80211_DEBUG_WX("%s: key setting failed\n", dev->name);
646 ret = -EINVAL;
647 goto done;
648 }
649
650 skip_host_crypt:
651 if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) {
John W. Linville274bfb82008-10-29 11:35:05 -0400652 ieee->crypt_info.tx_keyidx = idx;
James Ketrenose0d369d2005-09-21 11:54:30 -0500653 sec.active_key = idx;
654 sec.flags |= SEC_ACTIVE_KEY;
655 }
656
657 if (ext->alg != IW_ENCODE_ALG_NONE) {
658 memcpy(sec.keys[idx], ext->key, ext->key_len);
659 sec.key_sizes[idx] = ext->key_len;
660 sec.flags |= (1 << idx);
661 if (ext->alg == IW_ENCODE_ALG_WEP) {
662 sec.encode_alg[idx] = SEC_ALG_WEP;
663 sec.flags |= SEC_LEVEL;
664 sec.level = SEC_LEVEL_1;
665 } else if (ext->alg == IW_ENCODE_ALG_TKIP) {
666 sec.encode_alg[idx] = SEC_ALG_TKIP;
667 sec.flags |= SEC_LEVEL;
668 sec.level = SEC_LEVEL_2;
669 } else if (ext->alg == IW_ENCODE_ALG_CCMP) {
670 sec.encode_alg[idx] = SEC_ALG_CCMP;
671 sec.flags |= SEC_LEVEL;
672 sec.level = SEC_LEVEL_3;
673 }
James Ketrenosccd0fda2005-09-21 11:58:32 -0500674 /* Don't set sec level for group keys. */
675 if (group_key)
676 sec.flags &= ~SEC_LEVEL;
James Ketrenose0d369d2005-09-21 11:54:30 -0500677 }
678 done:
679 if (ieee->set_security)
680 ieee->set_security(ieee->dev, &sec);
681
682 /*
683 * Do not reset port if card is in Managed mode since resetting will
684 * generate new IEEE 802.11 authentication which may end up in looping
685 * with IEEE 802.1X. If your hardware requires a reset after WEP
686 * configuration (for example... Prism2), implement the reset_port in
687 * the callbacks structures used to initialize the 802.11 stack.
688 */
689 if (ieee->reset_on_keychange &&
690 ieee->iw_mode != IW_MODE_INFRA &&
691 ieee->reset_port && ieee->reset_port(dev)) {
692 IEEE80211_DEBUG_WX("%s: reset_port failed\n", dev->name);
693 return -EINVAL;
694 }
695
696 return ret;
697}
698
699int ieee80211_wx_get_encodeext(struct ieee80211_device *ieee,
700 struct iw_request_info *info,
701 union iwreq_data *wrqu, char *extra)
702{
703 struct iw_point *encoding = &wrqu->encoding;
704 struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
705 struct ieee80211_security *sec = &ieee->sec;
706 int idx, max_key_len;
707
708 max_key_len = encoding->length - sizeof(*ext);
709 if (max_key_len < 0)
710 return -EINVAL;
711
712 idx = encoding->flags & IW_ENCODE_INDEX;
713 if (idx) {
714 if (idx < 1 || idx > WEP_KEYS)
715 return -EINVAL;
716 idx--;
717 } else
John W. Linville274bfb82008-10-29 11:35:05 -0400718 idx = ieee->crypt_info.tx_keyidx;
James Ketrenose0d369d2005-09-21 11:54:30 -0500719
Roel Kluinf59d9782007-10-26 21:51:26 +0200720 if (!(ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) &&
Volker Braune1892772005-10-24 10:15:36 -0500721 ext->alg != IW_ENCODE_ALG_WEP)
James Ketrenose0d369d2005-09-21 11:54:30 -0500722 if (idx != 0 || ieee->iw_mode != IW_MODE_INFRA)
723 return -EINVAL;
724
725 encoding->flags = idx + 1;
726 memset(ext, 0, sizeof(*ext));
727
728 if (!sec->enabled) {
729 ext->alg = IW_ENCODE_ALG_NONE;
730 ext->key_len = 0;
731 encoding->flags |= IW_ENCODE_DISABLED;
732 } else {
733 if (sec->encode_alg[idx] == SEC_ALG_WEP)
734 ext->alg = IW_ENCODE_ALG_WEP;
735 else if (sec->encode_alg[idx] == SEC_ALG_TKIP)
736 ext->alg = IW_ENCODE_ALG_TKIP;
737 else if (sec->encode_alg[idx] == SEC_ALG_CCMP)
738 ext->alg = IW_ENCODE_ALG_CCMP;
739 else
740 return -EINVAL;
741
742 ext->key_len = sec->key_sizes[idx];
743 memcpy(ext->key, sec->keys[idx], ext->key_len);
744 encoding->flags |= IW_ENCODE_ENABLED;
745 if (ext->key_len &&
746 (ext->alg == IW_ENCODE_ALG_TKIP ||
747 ext->alg == IW_ENCODE_ALG_CCMP))
748 ext->ext_flags |= IW_ENCODE_EXT_TX_SEQ_VALID;
749
750 }
751
752 return 0;
753}
754
755EXPORT_SYMBOL(ieee80211_wx_set_encodeext);
756EXPORT_SYMBOL(ieee80211_wx_get_encodeext);
James Ketrenose0d369d2005-09-21 11:54:30 -0500757
Jeff Garzikb4538722005-05-12 22:48:20 -0400758EXPORT_SYMBOL(ieee80211_wx_get_scan);
759EXPORT_SYMBOL(ieee80211_wx_set_encode);
760EXPORT_SYMBOL(ieee80211_wx_get_encode);