blob: dfbadb3b9bd527b682882e6f6ff5546ef1795470 [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 Garzikbbeec902005-09-07 00:27:54 -040038#include <linux/wireless.h>
39
Dan Williamsf3734ee2009-02-12 12:32:55 -050040#include "ieee80211.h"
41
Jeff Garzikb4538722005-05-12 22:48:20 -040042static const char *ieee80211_modes[] = {
43 "?", "a", "b", "ab", "g", "ag", "bg", "abg"
44};
45
46#define MAX_CUSTOM_LEN 64
Larry Fingerd94606e2006-03-03 16:21:55 -060047static char *ieee80211_translate_scan(struct ieee80211_device *ieee,
David S. Millerccc58052008-06-16 18:50:49 -070048 char *start, char *stop,
49 struct ieee80211_network *network,
50 struct iw_request_info *info)
Jeff Garzikb4538722005-05-12 22:48:20 -040051{
52 char custom[MAX_CUSTOM_LEN];
53 char *p;
54 struct iw_event iwe;
55 int i, j;
Zhu Yi09593042006-04-13 17:17:26 +080056 char *current_val; /* For rates */
57 u8 rate;
Jeff Garzikb4538722005-05-12 22:48:20 -040058
59 /* First entry *MUST* be the AP MAC address */
60 iwe.cmd = SIOCGIWAP;
61 iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
62 memcpy(iwe.u.ap_addr.sa_data, network->bssid, ETH_ALEN);
David S. Millerccc58052008-06-16 18:50:49 -070063 start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_ADDR_LEN);
Jeff Garzikb4538722005-05-12 22:48:20 -040064
65 /* Remaining entries will be displayed in the order we provide them */
66
67 /* Add the ESSID */
68 iwe.cmd = SIOCGIWESSID;
69 iwe.u.data.flags = 1;
John W. Linvillec5d3dce2008-09-30 17:17:26 -040070 iwe.u.data.length = min(network->ssid_len, (u8) 32);
71 start = iwe_stream_add_point(info, start, stop,
72 &iwe, network->ssid);
Jeff Garzikb4538722005-05-12 22:48:20 -040073
74 /* Add the protocol name */
75 iwe.cmd = SIOCGIWNAME;
Jeff Garzik0edd5b42005-09-07 00:48:31 -040076 snprintf(iwe.u.name, IFNAMSIZ, "IEEE 802.11%s",
77 ieee80211_modes[network->mode]);
David S. Millerccc58052008-06-16 18:50:49 -070078 start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_CHAR_LEN);
Jeff Garzikb4538722005-05-12 22:48:20 -040079
Jeff Garzik0edd5b42005-09-07 00:48:31 -040080 /* Add mode */
81 iwe.cmd = SIOCGIWMODE;
82 if (network->capability & (WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS)) {
Jeff Garzik1b5cca32005-08-15 00:32:15 -040083 if (network->capability & WLAN_CAPABILITY_ESS)
Jeff Garzikb4538722005-05-12 22:48:20 -040084 iwe.u.mode = IW_MODE_MASTER;
85 else
86 iwe.u.mode = IW_MODE_ADHOC;
87
David S. Millerccc58052008-06-16 18:50:49 -070088 start = iwe_stream_add_event(info, start, stop,
89 &iwe, IW_EV_UINT_LEN);
Jeff Garzikb4538722005-05-12 22:48:20 -040090 }
91
Larry Finger93afe3d2007-04-21 17:56:43 -050092 /* Add channel and frequency */
Jean Tourrilhes90869b22007-07-10 15:51:14 -050093 /* Note : userspace automatically computes channel using iwrange */
Jeff Garzikb4538722005-05-12 22:48:20 -040094 iwe.cmd = SIOCGIWFREQ;
Larry Finger93afe3d2007-04-21 17:56:43 -050095 iwe.u.freq.m = ieee80211_channel_to_freq(ieee, network->channel);
96 iwe.u.freq.e = 6;
Jean Tourrilhes90869b22007-07-10 15:51:14 -050097 iwe.u.freq.i = 0;
David S. Millerccc58052008-06-16 18:50:49 -070098 start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_FREQ_LEN);
Larry Finger93afe3d2007-04-21 17:56:43 -050099
Jeff Garzikb4538722005-05-12 22:48:20 -0400100 /* Add encryption capability */
101 iwe.cmd = SIOCGIWENCODE;
102 if (network->capability & WLAN_CAPABILITY_PRIVACY)
103 iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
104 else
105 iwe.u.data.flags = IW_ENCODE_DISABLED;
106 iwe.u.data.length = 0;
David S. Millerccc58052008-06-16 18:50:49 -0700107 start = iwe_stream_add_point(info, start, stop,
108 &iwe, network->ssid);
Jeff Garzikb4538722005-05-12 22:48:20 -0400109
110 /* Add basic and extended rates */
Zhu Yi09593042006-04-13 17:17:26 +0800111 /* Rate : stuffing multiple values in a single event require a bit
112 * more of magic - Jean II */
David S. Millerccc58052008-06-16 18:50:49 -0700113 current_val = start + iwe_stream_lcp_len(info);
Zhu Yi09593042006-04-13 17:17:26 +0800114 iwe.cmd = SIOCGIWRATE;
115 /* Those two flags are ignored... */
116 iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0;
117
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400118 for (i = 0, j = 0; i < network->rates_len;) {
Jeff Garzikb4538722005-05-12 22:48:20 -0400119 if (j < network->rates_ex_len &&
120 ((network->rates_ex[j] & 0x7F) <
121 (network->rates[i] & 0x7F)))
122 rate = network->rates_ex[j++] & 0x7F;
123 else
124 rate = network->rates[i++] & 0x7F;
Zhu Yi09593042006-04-13 17:17:26 +0800125 /* Bit rate given in 500 kb/s units (+ 0x80) */
126 iwe.u.bitrate.value = ((rate & 0x7f) * 500000);
127 /* Add new value to event */
David S. Millerccc58052008-06-16 18:50:49 -0700128 current_val = iwe_stream_add_value(info, start, current_val,
129 stop, &iwe, IW_EV_PARAM_LEN);
Jeff Garzikb4538722005-05-12 22:48:20 -0400130 }
131 for (; j < network->rates_ex_len; j++) {
132 rate = network->rates_ex[j] & 0x7F;
Zhu Yi09593042006-04-13 17:17:26 +0800133 /* Bit rate given in 500 kb/s units (+ 0x80) */
134 iwe.u.bitrate.value = ((rate & 0x7f) * 500000);
135 /* Add new value to event */
David S. Millerccc58052008-06-16 18:50:49 -0700136 current_val = iwe_stream_add_value(info, start, current_val,
137 stop, &iwe, IW_EV_PARAM_LEN);
Jeff Garzikb4538722005-05-12 22:48:20 -0400138 }
Zhu Yi09593042006-04-13 17:17:26 +0800139 /* Check if we added any rate */
David S. Millerccc58052008-06-16 18:50:49 -0700140 if ((current_val - start) > iwe_stream_lcp_len(info))
Zhu Yi09593042006-04-13 17:17:26 +0800141 start = current_val;
Jeff Garzikb4538722005-05-12 22:48:20 -0400142
143 /* Add quality statistics */
Jeff Garzikb4538722005-05-12 22:48:20 -0400144 iwe.cmd = IWEVQUAL;
James Ketrenosb1b508e2005-09-13 17:27:19 -0500145 iwe.u.qual.updated = IW_QUAL_QUAL_UPDATED | IW_QUAL_LEVEL_UPDATED |
146 IW_QUAL_NOISE_UPDATED;
147
148 if (!(network->stats.mask & IEEE80211_STATMASK_RSSI)) {
149 iwe.u.qual.updated |= IW_QUAL_QUAL_INVALID |
150 IW_QUAL_LEVEL_INVALID;
151 iwe.u.qual.qual = 0;
James Ketrenosb1b508e2005-09-13 17:27:19 -0500152 } else {
Jiri Benc757d18f2005-10-10 19:16:53 +0200153 if (ieee->perfect_rssi == ieee->worst_rssi)
154 iwe.u.qual.qual = 100;
155 else
156 iwe.u.qual.qual =
157 (100 *
158 (ieee->perfect_rssi - ieee->worst_rssi) *
159 (ieee->perfect_rssi - ieee->worst_rssi) -
160 (ieee->perfect_rssi - network->stats.rssi) *
161 (15 * (ieee->perfect_rssi - ieee->worst_rssi) +
James Ketrenos81f87522005-10-24 10:20:53 -0500162 62 * (ieee->perfect_rssi -
163 network->stats.rssi))) /
164 ((ieee->perfect_rssi -
165 ieee->worst_rssi) * (ieee->perfect_rssi -
166 ieee->worst_rssi));
James Ketrenosb1b508e2005-09-13 17:27:19 -0500167 if (iwe.u.qual.qual > 100)
168 iwe.u.qual.qual = 100;
169 else if (iwe.u.qual.qual < 1)
170 iwe.u.qual.qual = 0;
171 }
172
173 if (!(network->stats.mask & IEEE80211_STATMASK_NOISE)) {
Jeff Garzikb4538722005-05-12 22:48:20 -0400174 iwe.u.qual.updated |= IW_QUAL_NOISE_INVALID;
James Ketrenosb1b508e2005-09-13 17:27:19 -0500175 iwe.u.qual.noise = 0;
176 } else {
177 iwe.u.qual.noise = network->stats.noise;
178 }
Jeff Garzikb4538722005-05-12 22:48:20 -0400179
Zhu Yi7bd64362006-01-19 16:21:54 +0800180 if (!(network->stats.mask & IEEE80211_STATMASK_SIGNAL)) {
181 iwe.u.qual.updated |= IW_QUAL_LEVEL_INVALID;
182 iwe.u.qual.level = 0;
183 } else {
184 iwe.u.qual.level = network->stats.signal;
185 }
186
David S. Millerccc58052008-06-16 18:50:49 -0700187 start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_QUAL_LEN);
Jeff Garzikb4538722005-05-12 22:48:20 -0400188
189 iwe.cmd = IWEVCUSTOM;
190 p = custom;
191
192 iwe.u.data.length = p - custom;
193 if (iwe.u.data.length)
David S. Millerccc58052008-06-16 18:50:49 -0700194 start = iwe_stream_add_point(info, start, stop, &iwe, custom);
Jeff Garzikb4538722005-05-12 22:48:20 -0400195
Zhu Yi47168082006-02-13 13:37:03 +0800196 memset(&iwe, 0, sizeof(iwe));
James Ketrenos20d64712005-09-21 11:53:43 -0500197 if (network->wpa_ie_len) {
Zhu Yi47168082006-02-13 13:37:03 +0800198 char buf[MAX_WPA_IE_LEN];
199 memcpy(buf, network->wpa_ie, network->wpa_ie_len);
200 iwe.cmd = IWEVGENIE;
201 iwe.u.data.length = network->wpa_ie_len;
David S. Millerccc58052008-06-16 18:50:49 -0700202 start = iwe_stream_add_point(info, start, stop, &iwe, buf);
Jeff Garzikb4538722005-05-12 22:48:20 -0400203 }
204
Zhu Yi47168082006-02-13 13:37:03 +0800205 memset(&iwe, 0, sizeof(iwe));
James Ketrenos20d64712005-09-21 11:53:43 -0500206 if (network->rsn_ie_len) {
Zhu Yi47168082006-02-13 13:37:03 +0800207 char buf[MAX_WPA_IE_LEN];
208 memcpy(buf, network->rsn_ie, network->rsn_ie_len);
209 iwe.cmd = IWEVGENIE;
210 iwe.u.data.length = network->rsn_ie_len;
David S. Millerccc58052008-06-16 18:50:49 -0700211 start = iwe_stream_add_point(info, start, stop, &iwe, buf);
Jeff Garzikb4538722005-05-12 22:48:20 -0400212 }
213
214 /* Add EXTRA: Age to display seconds since last beacon/probe response
215 * for given network. */
216 iwe.cmd = IWEVCUSTOM;
217 p = custom;
218 p += snprintf(p, MAX_CUSTOM_LEN - (p - custom),
James Ketrenos42e349f2005-09-21 11:54:07 -0500219 " Last beacon: %dms ago",
220 jiffies_to_msecs(jiffies - network->last_scanned));
Jeff Garzikb4538722005-05-12 22:48:20 -0400221 iwe.u.data.length = p - custom;
222 if (iwe.u.data.length)
David S. Millerccc58052008-06-16 18:50:49 -0700223 start = iwe_stream_add_point(info, start, stop, &iwe, custom);
Jeff Garzikb4538722005-05-12 22:48:20 -0400224
Zhu Yi7bd64362006-01-19 16:21:54 +0800225 /* Add spectrum management information */
226 iwe.cmd = -1;
227 p = custom;
228 p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), " Channel flags: ");
229
230 if (ieee80211_get_channel_flags(ieee, network->channel) &
231 IEEE80211_CH_INVALID) {
232 iwe.cmd = IWEVCUSTOM;
233 p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), "INVALID ");
234 }
235
236 if (ieee80211_get_channel_flags(ieee, network->channel) &
237 IEEE80211_CH_RADAR_DETECT) {
238 iwe.cmd = IWEVCUSTOM;
239 p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), "DFS ");
240 }
241
242 if (iwe.cmd == IWEVCUSTOM) {
243 iwe.u.data.length = p - custom;
David S. Millerccc58052008-06-16 18:50:49 -0700244 start = iwe_stream_add_point(info, start, stop, &iwe, custom);
Zhu Yi7bd64362006-01-19 16:21:54 +0800245 }
246
Jeff Garzikb4538722005-05-12 22:48:20 -0400247 return start;
248}
249
Zhu Yi55cd94a2006-01-19 16:20:59 +0800250#define SCAN_ITEM_SIZE 128
251
Jeff Garzikb4538722005-05-12 22:48:20 -0400252int ieee80211_wx_get_scan(struct ieee80211_device *ieee,
253 struct iw_request_info *info,
254 union iwreq_data *wrqu, char *extra)
255{
256 struct ieee80211_network *network;
257 unsigned long flags;
Zhu Yi55cd94a2006-01-19 16:20:59 +0800258 int err = 0;
Jeff Garzikb4538722005-05-12 22:48:20 -0400259
260 char *ev = extra;
Zhu Yi55cd94a2006-01-19 16:20:59 +0800261 char *stop = ev + wrqu->data.length;
Jeff Garzikb4538722005-05-12 22:48:20 -0400262 int i = 0;
John W. Linville9387b7c2008-09-30 20:59:05 -0400263 DECLARE_SSID_BUF(ssid);
Jeff Garzikb4538722005-05-12 22:48:20 -0400264
265 IEEE80211_DEBUG_WX("Getting scan\n");
266
267 spin_lock_irqsave(&ieee->lock, flags);
268
269 list_for_each_entry(network, &ieee->network_list, list) {
270 i++;
Zhu Yi55cd94a2006-01-19 16:20:59 +0800271 if (stop - ev < SCAN_ITEM_SIZE) {
272 err = -E2BIG;
273 break;
274 }
275
Jeff Garzikb4538722005-05-12 22:48:20 -0400276 if (ieee->scan_age == 0 ||
277 time_after(network->last_scanned + ieee->scan_age, jiffies))
David S. Millerccc58052008-06-16 18:50:49 -0700278 ev = ieee80211_translate_scan(ieee, ev, stop, network,
279 info);
Jeff Garzikb4538722005-05-12 22:48:20 -0400280 else
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400281 IEEE80211_DEBUG_SCAN("Not showing network '%s ("
Johannes Berge1749612008-10-27 15:59:26 -0700282 "%pM)' due to age (%dms).\n",
John W. Linville9387b7c2008-09-30 20:59:05 -0400283 print_ssid(ssid, network->ssid,
John W. Linville7e272fc2008-09-24 18:13:14 -0400284 network->ssid_len),
Johannes Berge1749612008-10-27 15:59:26 -0700285 network->bssid,
James Ketrenos42e349f2005-09-21 11:54:07 -0500286 jiffies_to_msecs(jiffies -
287 network->
288 last_scanned));
Jeff Garzikb4538722005-05-12 22:48:20 -0400289 }
290
291 spin_unlock_irqrestore(&ieee->lock, flags);
292
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400293 wrqu->data.length = ev - extra;
Jeff Garzikb4538722005-05-12 22:48:20 -0400294 wrqu->data.flags = 0;
295
296 IEEE80211_DEBUG_WX("exit: %d networks returned.\n", i);
297
Zhu Yi55cd94a2006-01-19 16:20:59 +0800298 return err;
Jeff Garzikb4538722005-05-12 22:48:20 -0400299}
300
301int ieee80211_wx_set_encode(struct ieee80211_device *ieee,
302 struct iw_request_info *info,
303 union iwreq_data *wrqu, char *keybuf)
304{
305 struct iw_point *erq = &(wrqu->encoding);
306 struct net_device *dev = ieee->dev;
307 struct ieee80211_security sec = {
308 .flags = 0
309 };
310 int i, key, key_provided, len;
John W. Linville274bfb82008-10-29 11:35:05 -0400311 struct lib80211_crypt_data **crypt;
Johannes Berga4bf26f2005-12-31 11:35:20 +0100312 int host_crypto = ieee->host_encrypt || ieee->host_decrypt || ieee->host_build_iv;
John W. Linville9387b7c2008-09-30 20:59:05 -0400313 DECLARE_SSID_BUF(ssid);
Jeff Garzikb4538722005-05-12 22:48:20 -0400314
315 IEEE80211_DEBUG_WX("SET_ENCODE\n");
316
317 key = erq->flags & IW_ENCODE_INDEX;
318 if (key) {
319 if (key > WEP_KEYS)
320 return -EINVAL;
321 key--;
322 key_provided = 1;
323 } else {
324 key_provided = 0;
John W. Linville274bfb82008-10-29 11:35:05 -0400325 key = ieee->crypt_info.tx_keyidx;
Jeff Garzikb4538722005-05-12 22:48:20 -0400326 }
327
328 IEEE80211_DEBUG_WX("Key: %d [%s]\n", key, key_provided ?
329 "provided" : "default");
330
John W. Linville274bfb82008-10-29 11:35:05 -0400331 crypt = &ieee->crypt_info.crypt[key];
Jeff Garzikb4538722005-05-12 22:48:20 -0400332
333 if (erq->flags & IW_ENCODE_DISABLED) {
334 if (key_provided && *crypt) {
335 IEEE80211_DEBUG_WX("Disabling encryption on key %d.\n",
336 key);
John W. Linville274bfb82008-10-29 11:35:05 -0400337 lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt);
Jeff Garzikb4538722005-05-12 22:48:20 -0400338 } else
339 IEEE80211_DEBUG_WX("Disabling encryption.\n");
340
341 /* Check all the keys to see if any are still configured,
342 * and if no key index was provided, de-init them all */
343 for (i = 0; i < WEP_KEYS; i++) {
John W. Linville274bfb82008-10-29 11:35:05 -0400344 if (ieee->crypt_info.crypt[i] != NULL) {
Jeff Garzikb4538722005-05-12 22:48:20 -0400345 if (key_provided)
346 break;
John W. Linville274bfb82008-10-29 11:35:05 -0400347 lib80211_crypt_delayed_deinit(&ieee->crypt_info,
348 &ieee->crypt_info.crypt[i]);
Jeff Garzikb4538722005-05-12 22:48:20 -0400349 }
350 }
351
352 if (i == WEP_KEYS) {
353 sec.enabled = 0;
James Ketrenosf1bf6632005-09-21 11:53:54 -0500354 sec.encrypt = 0;
Jeff Garzikb4538722005-05-12 22:48:20 -0400355 sec.level = SEC_LEVEL_0;
James Ketrenos259bf1f2005-09-21 11:54:22 -0500356 sec.flags |= SEC_ENABLED | SEC_LEVEL | SEC_ENCRYPT;
Jeff Garzikb4538722005-05-12 22:48:20 -0400357 }
358
359 goto done;
360 }
361
Jeff Garzikb4538722005-05-12 22:48:20 -0400362 sec.enabled = 1;
James Ketrenosf1bf6632005-09-21 11:53:54 -0500363 sec.encrypt = 1;
James Ketrenos259bf1f2005-09-21 11:54:22 -0500364 sec.flags |= SEC_ENABLED | SEC_ENCRYPT;
Jeff Garzikb4538722005-05-12 22:48:20 -0400365
366 if (*crypt != NULL && (*crypt)->ops != NULL &&
367 strcmp((*crypt)->ops->name, "WEP") != 0) {
368 /* changing to use WEP; deinit previously used algorithm
369 * on this key */
John W. Linville274bfb82008-10-29 11:35:05 -0400370 lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt);
Jeff Garzikb4538722005-05-12 22:48:20 -0400371 }
372
James Ketrenosf1bf6632005-09-21 11:53:54 -0500373 if (*crypt == NULL && host_crypto) {
John W. Linville274bfb82008-10-29 11:35:05 -0400374 struct lib80211_crypt_data *new_crypt;
Jeff Garzikb4538722005-05-12 22:48:20 -0400375
376 /* take WEP into use */
John W. Linville274bfb82008-10-29 11:35:05 -0400377 new_crypt = kzalloc(sizeof(struct lib80211_crypt_data),
Jeff Garzikb4538722005-05-12 22:48:20 -0400378 GFP_KERNEL);
379 if (new_crypt == NULL)
380 return -ENOMEM;
John W. Linville274bfb82008-10-29 11:35:05 -0400381 new_crypt->ops = lib80211_get_crypto_ops("WEP");
Jeff Garzikb4538722005-05-12 22:48:20 -0400382 if (!new_crypt->ops) {
John W. Linville274bfb82008-10-29 11:35:05 -0400383 request_module("lib80211_crypt_wep");
384 new_crypt->ops = lib80211_get_crypto_ops("WEP");
Jeff Garzikb4538722005-05-12 22:48:20 -0400385 }
386
387 if (new_crypt->ops && try_module_get(new_crypt->ops->owner))
James Ketrenos6eb6edf2005-09-22 10:34:15 +0000388 new_crypt->priv = new_crypt->ops->init(key);
Jeff Garzikb4538722005-05-12 22:48:20 -0400389
390 if (!new_crypt->ops || !new_crypt->priv) {
391 kfree(new_crypt);
392 new_crypt = NULL;
393
394 printk(KERN_WARNING "%s: could not initialize WEP: "
John W. Linville274bfb82008-10-29 11:35:05 -0400395 "load module lib80211_crypt_wep\n", dev->name);
Jeff Garzikb4538722005-05-12 22:48:20 -0400396 return -EOPNOTSUPP;
397 }
398 *crypt = new_crypt;
399 }
400
401 /* If a new key was provided, set it up */
402 if (erq->length > 0) {
Holger Schurig2a941ec2008-10-29 09:43:50 +0100403#ifdef CONFIG_IEEE80211_DEBUG
404 DECLARE_SSID_BUF(ssid);
405#endif
406
Jeff Garzikb4538722005-05-12 22:48:20 -0400407 len = erq->length <= 5 ? 5 : 13;
408 memcpy(sec.keys[key], keybuf, erq->length);
409 if (len > erq->length)
410 memset(sec.keys[key] + erq->length, 0,
411 len - erq->length);
412 IEEE80211_DEBUG_WX("Setting key %d to '%s' (%d:%d bytes)\n",
John W. Linville9387b7c2008-09-30 20:59:05 -0400413 key, print_ssid(ssid, sec.keys[key], len),
Jeff Garzikb4538722005-05-12 22:48:20 -0400414 erq->length, len);
415 sec.key_sizes[key] = len;
James Ketrenosf1bf6632005-09-21 11:53:54 -0500416 if (*crypt)
417 (*crypt)->ops->set_key(sec.keys[key], len, NULL,
418 (*crypt)->priv);
Jeff Garzikb4538722005-05-12 22:48:20 -0400419 sec.flags |= (1 << key);
420 /* This ensures a key will be activated if no key is
Jean Delvarec03983a2007-10-19 23:22:55 +0200421 * explicitly set */
Jeff Garzikb4538722005-05-12 22:48:20 -0400422 if (key == sec.active_key)
423 sec.flags |= SEC_ACTIVE_KEY;
Jeff Garzikb4538722005-05-12 22:48:20 -0400424
James Ketrenosf1bf6632005-09-21 11:53:54 -0500425 } else {
426 if (host_crypto) {
427 len = (*crypt)->ops->get_key(sec.keys[key], WEP_KEY_LEN,
428 NULL, (*crypt)->priv);
429 if (len == 0) {
430 /* Set a default key of all 0 */
431 IEEE80211_DEBUG_WX("Setting key %d to all "
432 "zero.\n", key);
433 memset(sec.keys[key], 0, 13);
434 (*crypt)->ops->set_key(sec.keys[key], 13, NULL,
435 (*crypt)->priv);
436 sec.key_sizes[key] = 13;
437 sec.flags |= (1 << key);
438 }
439 }
Jeff Garzikb4538722005-05-12 22:48:20 -0400440 /* No key data - just set the default TX key index */
441 if (key_provided) {
James Ketrenosf1bf6632005-09-21 11:53:54 -0500442 IEEE80211_DEBUG_WX("Setting key %d to default Tx "
443 "key.\n", key);
John W. Linville274bfb82008-10-29 11:35:05 -0400444 ieee->crypt_info.tx_keyidx = key;
Jeff Garzikb4538722005-05-12 22:48:20 -0400445 sec.active_key = key;
446 sec.flags |= SEC_ACTIVE_KEY;
447 }
448 }
James Ketrenos7dc888f2005-09-21 11:58:38 -0500449 if (erq->flags & (IW_ENCODE_OPEN | IW_ENCODE_RESTRICTED)) {
450 ieee->open_wep = !(erq->flags & IW_ENCODE_RESTRICTED);
451 sec.auth_mode = ieee->open_wep ? WLAN_AUTH_OPEN :
452 WLAN_AUTH_SHARED_KEY;
453 sec.flags |= SEC_AUTH_MODE;
454 IEEE80211_DEBUG_WX("Auth: %s\n",
455 sec.auth_mode == WLAN_AUTH_OPEN ?
456 "OPEN" : "SHARED KEY");
457 }
Jeff Garzikb4538722005-05-12 22:48:20 -0400458
459 /* For now we just support WEP, so only set that security level...
460 * TODO: When WPA is added this is one place that needs to change */
461 sec.flags |= SEC_LEVEL;
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400462 sec.level = SEC_LEVEL_1; /* 40 and 104 bit WEP */
James Ketrenose0d369d2005-09-21 11:54:30 -0500463 sec.encode_alg[key] = SEC_ALG_WEP;
Jeff Garzikb4538722005-05-12 22:48:20 -0400464
James Ketrenos259bf1f2005-09-21 11:54:22 -0500465 done:
Jeff Garzikb4538722005-05-12 22:48:20 -0400466 if (ieee->set_security)
467 ieee->set_security(dev, &sec);
468
469 /* Do not reset port if card is in Managed mode since resetting will
470 * generate new IEEE 802.11 authentication which may end up in looping
471 * with IEEE 802.1X. If your hardware requires a reset after WEP
472 * configuration (for example... Prism2), implement the reset_port in
473 * the callbacks structures used to initialize the 802.11 stack. */
474 if (ieee->reset_on_keychange &&
475 ieee->iw_mode != IW_MODE_INFRA &&
476 ieee->reset_port && ieee->reset_port(dev)) {
477 printk(KERN_DEBUG "%s: reset_port failed\n", dev->name);
478 return -EINVAL;
479 }
480 return 0;
481}
482
483int ieee80211_wx_get_encode(struct ieee80211_device *ieee,
484 struct iw_request_info *info,
485 union iwreq_data *wrqu, char *keybuf)
486{
487 struct iw_point *erq = &(wrqu->encoding);
488 int len, key;
John W. Linville274bfb82008-10-29 11:35:05 -0400489 struct lib80211_crypt_data *crypt;
James Ketrenosf1bf6632005-09-21 11:53:54 -0500490 struct ieee80211_security *sec = &ieee->sec;
Jeff Garzikb4538722005-05-12 22:48:20 -0400491
492 IEEE80211_DEBUG_WX("GET_ENCODE\n");
493
494 key = erq->flags & IW_ENCODE_INDEX;
495 if (key) {
496 if (key > WEP_KEYS)
497 return -EINVAL;
498 key--;
499 } else
John W. Linville274bfb82008-10-29 11:35:05 -0400500 key = ieee->crypt_info.tx_keyidx;
Jeff Garzikb4538722005-05-12 22:48:20 -0400501
John W. Linville274bfb82008-10-29 11:35:05 -0400502 crypt = ieee->crypt_info.crypt[key];
Jeff Garzikb4538722005-05-12 22:48:20 -0400503 erq->flags = key + 1;
504
James Ketrenosf1bf6632005-09-21 11:53:54 -0500505 if (!sec->enabled) {
Jeff Garzikb4538722005-05-12 22:48:20 -0400506 erq->length = 0;
507 erq->flags |= IW_ENCODE_DISABLED;
508 return 0;
509 }
510
James Ketrenosf1bf6632005-09-21 11:53:54 -0500511 len = sec->key_sizes[key];
512 memcpy(keybuf, sec->keys[key], len);
Jeff Garzikb4538722005-05-12 22:48:20 -0400513
Adrian Bunk62741152006-04-27 02:33:42 -0700514 erq->length = len;
Jeff Garzikb4538722005-05-12 22:48:20 -0400515 erq->flags |= IW_ENCODE_ENABLED;
516
517 if (ieee->open_wep)
518 erq->flags |= IW_ENCODE_OPEN;
519 else
520 erq->flags |= IW_ENCODE_RESTRICTED;
521
522 return 0;
523}
524
James Ketrenose0d369d2005-09-21 11:54:30 -0500525int ieee80211_wx_set_encodeext(struct ieee80211_device *ieee,
526 struct iw_request_info *info,
527 union iwreq_data *wrqu, char *extra)
528{
529 struct net_device *dev = ieee->dev;
530 struct iw_point *encoding = &wrqu->encoding;
531 struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
532 int i, idx, ret = 0;
James Ketrenosccd0fda2005-09-21 11:58:32 -0500533 int group_key = 0;
James Ketrenose0d369d2005-09-21 11:54:30 -0500534 const char *alg, *module;
John W. Linville274bfb82008-10-29 11:35:05 -0400535 struct lib80211_crypto_ops *ops;
536 struct lib80211_crypt_data **crypt;
James Ketrenose0d369d2005-09-21 11:54:30 -0500537
538 struct ieee80211_security sec = {
539 .flags = 0,
540 };
541
542 idx = encoding->flags & IW_ENCODE_INDEX;
543 if (idx) {
544 if (idx < 1 || idx > WEP_KEYS)
545 return -EINVAL;
546 idx--;
547 } else
John W. Linville274bfb82008-10-29 11:35:05 -0400548 idx = ieee->crypt_info.tx_keyidx;
James Ketrenose0d369d2005-09-21 11:54:30 -0500549
James Ketrenosccd0fda2005-09-21 11:58:32 -0500550 if (ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) {
John W. Linville274bfb82008-10-29 11:35:05 -0400551 crypt = &ieee->crypt_info.crypt[idx];
James Ketrenosccd0fda2005-09-21 11:58:32 -0500552 group_key = 1;
553 } else {
Volker Braune1892772005-10-24 10:15:36 -0500554 /* some Cisco APs use idx>0 for unicast in dynamic WEP */
555 if (idx != 0 && ext->alg != IW_ENCODE_ALG_WEP)
James Ketrenose0d369d2005-09-21 11:54:30 -0500556 return -EINVAL;
557 if (ieee->iw_mode == IW_MODE_INFRA)
John W. Linville274bfb82008-10-29 11:35:05 -0400558 crypt = &ieee->crypt_info.crypt[idx];
James Ketrenose0d369d2005-09-21 11:54:30 -0500559 else
560 return -EINVAL;
561 }
562
563 sec.flags |= SEC_ENABLED | SEC_ENCRYPT;
564 if ((encoding->flags & IW_ENCODE_DISABLED) ||
565 ext->alg == IW_ENCODE_ALG_NONE) {
566 if (*crypt)
John W. Linville274bfb82008-10-29 11:35:05 -0400567 lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt);
James Ketrenose0d369d2005-09-21 11:54:30 -0500568
569 for (i = 0; i < WEP_KEYS; i++)
John W. Linville274bfb82008-10-29 11:35:05 -0400570 if (ieee->crypt_info.crypt[i] != NULL)
James Ketrenose0d369d2005-09-21 11:54:30 -0500571 break;
572
573 if (i == WEP_KEYS) {
574 sec.enabled = 0;
575 sec.encrypt = 0;
576 sec.level = SEC_LEVEL_0;
577 sec.flags |= SEC_LEVEL;
578 }
579 goto done;
580 }
581
582 sec.enabled = 1;
583 sec.encrypt = 1;
584
James Ketrenosccd0fda2005-09-21 11:58:32 -0500585 if (group_key ? !ieee->host_mc_decrypt :
586 !(ieee->host_encrypt || ieee->host_decrypt ||
587 ieee->host_encrypt_msdu))
James Ketrenose0d369d2005-09-21 11:54:30 -0500588 goto skip_host_crypt;
589
590 switch (ext->alg) {
591 case IW_ENCODE_ALG_WEP:
592 alg = "WEP";
John W. Linville274bfb82008-10-29 11:35:05 -0400593 module = "lib80211_crypt_wep";
James Ketrenose0d369d2005-09-21 11:54:30 -0500594 break;
595 case IW_ENCODE_ALG_TKIP:
596 alg = "TKIP";
John W. Linville274bfb82008-10-29 11:35:05 -0400597 module = "lib80211_crypt_tkip";
James Ketrenose0d369d2005-09-21 11:54:30 -0500598 break;
599 case IW_ENCODE_ALG_CCMP:
600 alg = "CCMP";
John W. Linville274bfb82008-10-29 11:35:05 -0400601 module = "lib80211_crypt_ccmp";
James Ketrenose0d369d2005-09-21 11:54:30 -0500602 break;
603 default:
604 IEEE80211_DEBUG_WX("%s: unknown crypto alg %d\n",
605 dev->name, ext->alg);
606 ret = -EINVAL;
607 goto done;
608 }
609
John W. Linville274bfb82008-10-29 11:35:05 -0400610 ops = lib80211_get_crypto_ops(alg);
James Ketrenose0d369d2005-09-21 11:54:30 -0500611 if (ops == NULL) {
612 request_module(module);
John W. Linville274bfb82008-10-29 11:35:05 -0400613 ops = lib80211_get_crypto_ops(alg);
James Ketrenose0d369d2005-09-21 11:54:30 -0500614 }
615 if (ops == NULL) {
616 IEEE80211_DEBUG_WX("%s: unknown crypto alg %d\n",
617 dev->name, ext->alg);
618 ret = -EINVAL;
619 goto done;
620 }
621
622 if (*crypt == NULL || (*crypt)->ops != ops) {
John W. Linville274bfb82008-10-29 11:35:05 -0400623 struct lib80211_crypt_data *new_crypt;
James Ketrenose0d369d2005-09-21 11:54:30 -0500624
John W. Linville274bfb82008-10-29 11:35:05 -0400625 lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt);
James Ketrenose0d369d2005-09-21 11:54:30 -0500626
Panagiotis Issaris0da974f2006-07-21 14:51:30 -0700627 new_crypt = kzalloc(sizeof(*new_crypt), GFP_KERNEL);
James Ketrenose0d369d2005-09-21 11:54:30 -0500628 if (new_crypt == NULL) {
629 ret = -ENOMEM;
630 goto done;
631 }
James Ketrenose0d369d2005-09-21 11:54:30 -0500632 new_crypt->ops = ops;
633 if (new_crypt->ops && try_module_get(new_crypt->ops->owner))
James Ketrenos6eb6edf2005-09-22 10:34:15 +0000634 new_crypt->priv = new_crypt->ops->init(idx);
James Ketrenose0d369d2005-09-21 11:54:30 -0500635 if (new_crypt->priv == NULL) {
636 kfree(new_crypt);
637 ret = -EINVAL;
638 goto done;
639 }
640 *crypt = new_crypt;
641 }
642
643 if (ext->key_len > 0 && (*crypt)->ops->set_key &&
644 (*crypt)->ops->set_key(ext->key, ext->key_len, ext->rx_seq,
645 (*crypt)->priv) < 0) {
646 IEEE80211_DEBUG_WX("%s: key setting failed\n", dev->name);
647 ret = -EINVAL;
648 goto done;
649 }
650
651 skip_host_crypt:
652 if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) {
John W. Linville274bfb82008-10-29 11:35:05 -0400653 ieee->crypt_info.tx_keyidx = idx;
James Ketrenose0d369d2005-09-21 11:54:30 -0500654 sec.active_key = idx;
655 sec.flags |= SEC_ACTIVE_KEY;
656 }
657
658 if (ext->alg != IW_ENCODE_ALG_NONE) {
659 memcpy(sec.keys[idx], ext->key, ext->key_len);
660 sec.key_sizes[idx] = ext->key_len;
661 sec.flags |= (1 << idx);
662 if (ext->alg == IW_ENCODE_ALG_WEP) {
663 sec.encode_alg[idx] = SEC_ALG_WEP;
664 sec.flags |= SEC_LEVEL;
665 sec.level = SEC_LEVEL_1;
666 } else if (ext->alg == IW_ENCODE_ALG_TKIP) {
667 sec.encode_alg[idx] = SEC_ALG_TKIP;
668 sec.flags |= SEC_LEVEL;
669 sec.level = SEC_LEVEL_2;
670 } else if (ext->alg == IW_ENCODE_ALG_CCMP) {
671 sec.encode_alg[idx] = SEC_ALG_CCMP;
672 sec.flags |= SEC_LEVEL;
673 sec.level = SEC_LEVEL_3;
674 }
James Ketrenosccd0fda2005-09-21 11:58:32 -0500675 /* Don't set sec level for group keys. */
676 if (group_key)
677 sec.flags &= ~SEC_LEVEL;
James Ketrenose0d369d2005-09-21 11:54:30 -0500678 }
679 done:
680 if (ieee->set_security)
681 ieee->set_security(ieee->dev, &sec);
682
683 /*
684 * Do not reset port if card is in Managed mode since resetting will
685 * generate new IEEE 802.11 authentication which may end up in looping
686 * with IEEE 802.1X. If your hardware requires a reset after WEP
687 * configuration (for example... Prism2), implement the reset_port in
688 * the callbacks structures used to initialize the 802.11 stack.
689 */
690 if (ieee->reset_on_keychange &&
691 ieee->iw_mode != IW_MODE_INFRA &&
692 ieee->reset_port && ieee->reset_port(dev)) {
693 IEEE80211_DEBUG_WX("%s: reset_port failed\n", dev->name);
694 return -EINVAL;
695 }
696
697 return ret;
698}
699
700int ieee80211_wx_get_encodeext(struct ieee80211_device *ieee,
701 struct iw_request_info *info,
702 union iwreq_data *wrqu, char *extra)
703{
704 struct iw_point *encoding = &wrqu->encoding;
705 struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
706 struct ieee80211_security *sec = &ieee->sec;
707 int idx, max_key_len;
708
709 max_key_len = encoding->length - sizeof(*ext);
710 if (max_key_len < 0)
711 return -EINVAL;
712
713 idx = encoding->flags & IW_ENCODE_INDEX;
714 if (idx) {
715 if (idx < 1 || idx > WEP_KEYS)
716 return -EINVAL;
717 idx--;
718 } else
John W. Linville274bfb82008-10-29 11:35:05 -0400719 idx = ieee->crypt_info.tx_keyidx;
James Ketrenose0d369d2005-09-21 11:54:30 -0500720
Roel Kluinf59d9782007-10-26 21:51:26 +0200721 if (!(ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) &&
Volker Braune1892772005-10-24 10:15:36 -0500722 ext->alg != IW_ENCODE_ALG_WEP)
James Ketrenose0d369d2005-09-21 11:54:30 -0500723 if (idx != 0 || ieee->iw_mode != IW_MODE_INFRA)
724 return -EINVAL;
725
726 encoding->flags = idx + 1;
727 memset(ext, 0, sizeof(*ext));
728
729 if (!sec->enabled) {
730 ext->alg = IW_ENCODE_ALG_NONE;
731 ext->key_len = 0;
732 encoding->flags |= IW_ENCODE_DISABLED;
733 } else {
734 if (sec->encode_alg[idx] == SEC_ALG_WEP)
735 ext->alg = IW_ENCODE_ALG_WEP;
736 else if (sec->encode_alg[idx] == SEC_ALG_TKIP)
737 ext->alg = IW_ENCODE_ALG_TKIP;
738 else if (sec->encode_alg[idx] == SEC_ALG_CCMP)
739 ext->alg = IW_ENCODE_ALG_CCMP;
740 else
741 return -EINVAL;
742
743 ext->key_len = sec->key_sizes[idx];
744 memcpy(ext->key, sec->keys[idx], ext->key_len);
745 encoding->flags |= IW_ENCODE_ENABLED;
746 if (ext->key_len &&
747 (ext->alg == IW_ENCODE_ALG_TKIP ||
748 ext->alg == IW_ENCODE_ALG_CCMP))
749 ext->ext_flags |= IW_ENCODE_EXT_TX_SEQ_VALID;
750
751 }
752
753 return 0;
754}
755
756EXPORT_SYMBOL(ieee80211_wx_set_encodeext);
757EXPORT_SYMBOL(ieee80211_wx_get_encodeext);
James Ketrenose0d369d2005-09-21 11:54:30 -0500758
Jeff Garzikb4538722005-05-12 22:48:20 -0400759EXPORT_SYMBOL(ieee80211_wx_get_scan);
760EXPORT_SYMBOL(ieee80211_wx_set_encode);
761EXPORT_SYMBOL(ieee80211_wx_get_encode);