blob: 3c0812db030a0389c2400f933dc8ff1c069cce4d [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
Dan Williamsc3d72b92009-02-11 13:26:06 -050046static inline unsigned int elapsed_jiffies_msecs(unsigned long start)
47{
48 unsigned long end = jiffies;
49
50 if (end >= start)
51 return jiffies_to_msecs(end - start);
52
53 return jiffies_to_msecs(end + (MAX_JIFFY_OFFSET - start) + 1);
54}
55
Jeff Garzikb4538722005-05-12 22:48:20 -040056#define MAX_CUSTOM_LEN 64
Larry Fingerd94606e2006-03-03 16:21:55 -060057static char *ieee80211_translate_scan(struct ieee80211_device *ieee,
David S. Millerccc58052008-06-16 18:50:49 -070058 char *start, char *stop,
59 struct ieee80211_network *network,
60 struct iw_request_info *info)
Jeff Garzikb4538722005-05-12 22:48:20 -040061{
62 char custom[MAX_CUSTOM_LEN];
63 char *p;
64 struct iw_event iwe;
65 int i, j;
Zhu Yi09593042006-04-13 17:17:26 +080066 char *current_val; /* For rates */
67 u8 rate;
Jeff Garzikb4538722005-05-12 22:48:20 -040068
69 /* First entry *MUST* be the AP MAC address */
70 iwe.cmd = SIOCGIWAP;
71 iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
72 memcpy(iwe.u.ap_addr.sa_data, network->bssid, ETH_ALEN);
David S. Millerccc58052008-06-16 18:50:49 -070073 start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_ADDR_LEN);
Jeff Garzikb4538722005-05-12 22:48:20 -040074
75 /* Remaining entries will be displayed in the order we provide them */
76
77 /* Add the ESSID */
78 iwe.cmd = SIOCGIWESSID;
79 iwe.u.data.flags = 1;
John W. Linvillec5d3dce2008-09-30 17:17:26 -040080 iwe.u.data.length = min(network->ssid_len, (u8) 32);
81 start = iwe_stream_add_point(info, start, stop,
82 &iwe, network->ssid);
Jeff Garzikb4538722005-05-12 22:48:20 -040083
84 /* Add the protocol name */
85 iwe.cmd = SIOCGIWNAME;
Jeff Garzik0edd5b42005-09-07 00:48:31 -040086 snprintf(iwe.u.name, IFNAMSIZ, "IEEE 802.11%s",
87 ieee80211_modes[network->mode]);
David S. Millerccc58052008-06-16 18:50:49 -070088 start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_CHAR_LEN);
Jeff Garzikb4538722005-05-12 22:48:20 -040089
Jeff Garzik0edd5b42005-09-07 00:48:31 -040090 /* Add mode */
91 iwe.cmd = SIOCGIWMODE;
92 if (network->capability & (WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS)) {
Jeff Garzik1b5cca32005-08-15 00:32:15 -040093 if (network->capability & WLAN_CAPABILITY_ESS)
Jeff Garzikb4538722005-05-12 22:48:20 -040094 iwe.u.mode = IW_MODE_MASTER;
95 else
96 iwe.u.mode = IW_MODE_ADHOC;
97
David S. Millerccc58052008-06-16 18:50:49 -070098 start = iwe_stream_add_event(info, start, stop,
99 &iwe, IW_EV_UINT_LEN);
Jeff Garzikb4538722005-05-12 22:48:20 -0400100 }
101
Larry Finger93afe3d2007-04-21 17:56:43 -0500102 /* Add channel and frequency */
Jean Tourrilhes90869b22007-07-10 15:51:14 -0500103 /* Note : userspace automatically computes channel using iwrange */
Jeff Garzikb4538722005-05-12 22:48:20 -0400104 iwe.cmd = SIOCGIWFREQ;
Larry Finger93afe3d2007-04-21 17:56:43 -0500105 iwe.u.freq.m = ieee80211_channel_to_freq(ieee, network->channel);
106 iwe.u.freq.e = 6;
Jean Tourrilhes90869b22007-07-10 15:51:14 -0500107 iwe.u.freq.i = 0;
David S. Millerccc58052008-06-16 18:50:49 -0700108 start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_FREQ_LEN);
Larry Finger93afe3d2007-04-21 17:56:43 -0500109
Jeff Garzikb4538722005-05-12 22:48:20 -0400110 /* Add encryption capability */
111 iwe.cmd = SIOCGIWENCODE;
112 if (network->capability & WLAN_CAPABILITY_PRIVACY)
113 iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
114 else
115 iwe.u.data.flags = IW_ENCODE_DISABLED;
116 iwe.u.data.length = 0;
David S. Millerccc58052008-06-16 18:50:49 -0700117 start = iwe_stream_add_point(info, start, stop,
118 &iwe, network->ssid);
Jeff Garzikb4538722005-05-12 22:48:20 -0400119
120 /* Add basic and extended rates */
Zhu Yi09593042006-04-13 17:17:26 +0800121 /* Rate : stuffing multiple values in a single event require a bit
122 * more of magic - Jean II */
David S. Millerccc58052008-06-16 18:50:49 -0700123 current_val = start + iwe_stream_lcp_len(info);
Zhu Yi09593042006-04-13 17:17:26 +0800124 iwe.cmd = SIOCGIWRATE;
125 /* Those two flags are ignored... */
126 iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0;
127
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400128 for (i = 0, j = 0; i < network->rates_len;) {
Jeff Garzikb4538722005-05-12 22:48:20 -0400129 if (j < network->rates_ex_len &&
130 ((network->rates_ex[j] & 0x7F) <
131 (network->rates[i] & 0x7F)))
132 rate = network->rates_ex[j++] & 0x7F;
133 else
134 rate = network->rates[i++] & 0x7F;
Zhu Yi09593042006-04-13 17:17:26 +0800135 /* Bit rate given in 500 kb/s units (+ 0x80) */
136 iwe.u.bitrate.value = ((rate & 0x7f) * 500000);
137 /* Add new value to event */
David S. Millerccc58052008-06-16 18:50:49 -0700138 current_val = iwe_stream_add_value(info, start, current_val,
139 stop, &iwe, IW_EV_PARAM_LEN);
Jeff Garzikb4538722005-05-12 22:48:20 -0400140 }
141 for (; j < network->rates_ex_len; j++) {
142 rate = network->rates_ex[j] & 0x7F;
Zhu Yi09593042006-04-13 17:17:26 +0800143 /* Bit rate given in 500 kb/s units (+ 0x80) */
144 iwe.u.bitrate.value = ((rate & 0x7f) * 500000);
145 /* Add new value to event */
David S. Millerccc58052008-06-16 18:50:49 -0700146 current_val = iwe_stream_add_value(info, start, current_val,
147 stop, &iwe, IW_EV_PARAM_LEN);
Jeff Garzikb4538722005-05-12 22:48:20 -0400148 }
Zhu Yi09593042006-04-13 17:17:26 +0800149 /* Check if we added any rate */
David S. Millerccc58052008-06-16 18:50:49 -0700150 if ((current_val - start) > iwe_stream_lcp_len(info))
Zhu Yi09593042006-04-13 17:17:26 +0800151 start = current_val;
Jeff Garzikb4538722005-05-12 22:48:20 -0400152
153 /* Add quality statistics */
Jeff Garzikb4538722005-05-12 22:48:20 -0400154 iwe.cmd = IWEVQUAL;
James Ketrenosb1b508e2005-09-13 17:27:19 -0500155 iwe.u.qual.updated = IW_QUAL_QUAL_UPDATED | IW_QUAL_LEVEL_UPDATED |
156 IW_QUAL_NOISE_UPDATED;
157
158 if (!(network->stats.mask & IEEE80211_STATMASK_RSSI)) {
159 iwe.u.qual.updated |= IW_QUAL_QUAL_INVALID |
160 IW_QUAL_LEVEL_INVALID;
161 iwe.u.qual.qual = 0;
James Ketrenosb1b508e2005-09-13 17:27:19 -0500162 } else {
Jiri Benc757d18f2005-10-10 19:16:53 +0200163 if (ieee->perfect_rssi == ieee->worst_rssi)
164 iwe.u.qual.qual = 100;
165 else
166 iwe.u.qual.qual =
167 (100 *
168 (ieee->perfect_rssi - ieee->worst_rssi) *
169 (ieee->perfect_rssi - ieee->worst_rssi) -
170 (ieee->perfect_rssi - network->stats.rssi) *
171 (15 * (ieee->perfect_rssi - ieee->worst_rssi) +
James Ketrenos81f87522005-10-24 10:20:53 -0500172 62 * (ieee->perfect_rssi -
173 network->stats.rssi))) /
174 ((ieee->perfect_rssi -
175 ieee->worst_rssi) * (ieee->perfect_rssi -
176 ieee->worst_rssi));
James Ketrenosb1b508e2005-09-13 17:27:19 -0500177 if (iwe.u.qual.qual > 100)
178 iwe.u.qual.qual = 100;
179 else if (iwe.u.qual.qual < 1)
180 iwe.u.qual.qual = 0;
181 }
182
183 if (!(network->stats.mask & IEEE80211_STATMASK_NOISE)) {
Jeff Garzikb4538722005-05-12 22:48:20 -0400184 iwe.u.qual.updated |= IW_QUAL_NOISE_INVALID;
James Ketrenosb1b508e2005-09-13 17:27:19 -0500185 iwe.u.qual.noise = 0;
186 } else {
187 iwe.u.qual.noise = network->stats.noise;
188 }
Jeff Garzikb4538722005-05-12 22:48:20 -0400189
Zhu Yi7bd64362006-01-19 16:21:54 +0800190 if (!(network->stats.mask & IEEE80211_STATMASK_SIGNAL)) {
191 iwe.u.qual.updated |= IW_QUAL_LEVEL_INVALID;
192 iwe.u.qual.level = 0;
193 } else {
194 iwe.u.qual.level = network->stats.signal;
195 }
196
David S. Millerccc58052008-06-16 18:50:49 -0700197 start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_QUAL_LEN);
Jeff Garzikb4538722005-05-12 22:48:20 -0400198
199 iwe.cmd = IWEVCUSTOM;
200 p = custom;
201
202 iwe.u.data.length = p - custom;
203 if (iwe.u.data.length)
David S. Millerccc58052008-06-16 18:50:49 -0700204 start = iwe_stream_add_point(info, start, stop, &iwe, custom);
Jeff Garzikb4538722005-05-12 22:48:20 -0400205
Zhu Yi47168082006-02-13 13:37:03 +0800206 memset(&iwe, 0, sizeof(iwe));
James Ketrenos20d64712005-09-21 11:53:43 -0500207 if (network->wpa_ie_len) {
Zhu Yi47168082006-02-13 13:37:03 +0800208 char buf[MAX_WPA_IE_LEN];
209 memcpy(buf, network->wpa_ie, network->wpa_ie_len);
210 iwe.cmd = IWEVGENIE;
211 iwe.u.data.length = network->wpa_ie_len;
David S. Millerccc58052008-06-16 18:50:49 -0700212 start = iwe_stream_add_point(info, start, stop, &iwe, buf);
Jeff Garzikb4538722005-05-12 22:48:20 -0400213 }
214
Zhu Yi47168082006-02-13 13:37:03 +0800215 memset(&iwe, 0, sizeof(iwe));
James Ketrenos20d64712005-09-21 11:53:43 -0500216 if (network->rsn_ie_len) {
Zhu Yi47168082006-02-13 13:37:03 +0800217 char buf[MAX_WPA_IE_LEN];
218 memcpy(buf, network->rsn_ie, network->rsn_ie_len);
219 iwe.cmd = IWEVGENIE;
220 iwe.u.data.length = network->rsn_ie_len;
David S. Millerccc58052008-06-16 18:50:49 -0700221 start = iwe_stream_add_point(info, start, stop, &iwe, buf);
Jeff Garzikb4538722005-05-12 22:48:20 -0400222 }
223
224 /* Add EXTRA: Age to display seconds since last beacon/probe response
225 * for given network. */
226 iwe.cmd = IWEVCUSTOM;
227 p = custom;
228 p += snprintf(p, MAX_CUSTOM_LEN - (p - custom),
Dan Williamsc3d72b92009-02-11 13:26:06 -0500229 " Last beacon: %ums ago",
230 elapsed_jiffies_msecs(network->last_scanned));
Jeff Garzikb4538722005-05-12 22:48:20 -0400231 iwe.u.data.length = p - custom;
232 if (iwe.u.data.length)
David S. Millerccc58052008-06-16 18:50:49 -0700233 start = iwe_stream_add_point(info, start, stop, &iwe, custom);
Jeff Garzikb4538722005-05-12 22:48:20 -0400234
Zhu Yi7bd64362006-01-19 16:21:54 +0800235 /* Add spectrum management information */
236 iwe.cmd = -1;
237 p = custom;
238 p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), " Channel flags: ");
239
240 if (ieee80211_get_channel_flags(ieee, network->channel) &
241 IEEE80211_CH_INVALID) {
242 iwe.cmd = IWEVCUSTOM;
243 p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), "INVALID ");
244 }
245
246 if (ieee80211_get_channel_flags(ieee, network->channel) &
247 IEEE80211_CH_RADAR_DETECT) {
248 iwe.cmd = IWEVCUSTOM;
249 p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), "DFS ");
250 }
251
252 if (iwe.cmd == IWEVCUSTOM) {
253 iwe.u.data.length = p - custom;
David S. Millerccc58052008-06-16 18:50:49 -0700254 start = iwe_stream_add_point(info, start, stop, &iwe, custom);
Zhu Yi7bd64362006-01-19 16:21:54 +0800255 }
256
Jeff Garzikb4538722005-05-12 22:48:20 -0400257 return start;
258}
259
Zhu Yi55cd94a2006-01-19 16:20:59 +0800260#define SCAN_ITEM_SIZE 128
261
Jeff Garzikb4538722005-05-12 22:48:20 -0400262int ieee80211_wx_get_scan(struct ieee80211_device *ieee,
263 struct iw_request_info *info,
264 union iwreq_data *wrqu, char *extra)
265{
266 struct ieee80211_network *network;
267 unsigned long flags;
Zhu Yi55cd94a2006-01-19 16:20:59 +0800268 int err = 0;
Jeff Garzikb4538722005-05-12 22:48:20 -0400269
270 char *ev = extra;
Zhu Yi55cd94a2006-01-19 16:20:59 +0800271 char *stop = ev + wrqu->data.length;
Jeff Garzikb4538722005-05-12 22:48:20 -0400272 int i = 0;
John W. Linville9387b7c2008-09-30 20:59:05 -0400273 DECLARE_SSID_BUF(ssid);
Jeff Garzikb4538722005-05-12 22:48:20 -0400274
275 IEEE80211_DEBUG_WX("Getting scan\n");
276
277 spin_lock_irqsave(&ieee->lock, flags);
278
279 list_for_each_entry(network, &ieee->network_list, list) {
280 i++;
Zhu Yi55cd94a2006-01-19 16:20:59 +0800281 if (stop - ev < SCAN_ITEM_SIZE) {
282 err = -E2BIG;
283 break;
284 }
285
Jeff Garzikb4538722005-05-12 22:48:20 -0400286 if (ieee->scan_age == 0 ||
287 time_after(network->last_scanned + ieee->scan_age, jiffies))
David S. Millerccc58052008-06-16 18:50:49 -0700288 ev = ieee80211_translate_scan(ieee, ev, stop, network,
289 info);
Dan Williamsc3d72b92009-02-11 13:26:06 -0500290 else {
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400291 IEEE80211_DEBUG_SCAN("Not showing network '%s ("
Dan Williamsc3d72b92009-02-11 13:26:06 -0500292 "%pM)' due to age (%ums).\n",
John W. Linville9387b7c2008-09-30 20:59:05 -0400293 print_ssid(ssid, network->ssid,
John W. Linville7e272fc2008-09-24 18:13:14 -0400294 network->ssid_len),
Johannes Berge1749612008-10-27 15:59:26 -0700295 network->bssid,
Dan Williamsc3d72b92009-02-11 13:26:06 -0500296 elapsed_jiffies_msecs(
297 network->last_scanned));
298 }
Jeff Garzikb4538722005-05-12 22:48:20 -0400299 }
300
301 spin_unlock_irqrestore(&ieee->lock, flags);
302
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400303 wrqu->data.length = ev - extra;
Jeff Garzikb4538722005-05-12 22:48:20 -0400304 wrqu->data.flags = 0;
305
306 IEEE80211_DEBUG_WX("exit: %d networks returned.\n", i);
307
Zhu Yi55cd94a2006-01-19 16:20:59 +0800308 return err;
Jeff Garzikb4538722005-05-12 22:48:20 -0400309}
310
311int ieee80211_wx_set_encode(struct ieee80211_device *ieee,
312 struct iw_request_info *info,
313 union iwreq_data *wrqu, char *keybuf)
314{
315 struct iw_point *erq = &(wrqu->encoding);
316 struct net_device *dev = ieee->dev;
317 struct ieee80211_security sec = {
318 .flags = 0
319 };
320 int i, key, key_provided, len;
John W. Linville274bfb82008-10-29 11:35:05 -0400321 struct lib80211_crypt_data **crypt;
Johannes Berga4bf26f2005-12-31 11:35:20 +0100322 int host_crypto = ieee->host_encrypt || ieee->host_decrypt || ieee->host_build_iv;
John W. Linville9387b7c2008-09-30 20:59:05 -0400323 DECLARE_SSID_BUF(ssid);
Jeff Garzikb4538722005-05-12 22:48:20 -0400324
325 IEEE80211_DEBUG_WX("SET_ENCODE\n");
326
327 key = erq->flags & IW_ENCODE_INDEX;
328 if (key) {
329 if (key > WEP_KEYS)
330 return -EINVAL;
331 key--;
332 key_provided = 1;
333 } else {
334 key_provided = 0;
John W. Linville274bfb82008-10-29 11:35:05 -0400335 key = ieee->crypt_info.tx_keyidx;
Jeff Garzikb4538722005-05-12 22:48:20 -0400336 }
337
338 IEEE80211_DEBUG_WX("Key: %d [%s]\n", key, key_provided ?
339 "provided" : "default");
340
John W. Linville274bfb82008-10-29 11:35:05 -0400341 crypt = &ieee->crypt_info.crypt[key];
Jeff Garzikb4538722005-05-12 22:48:20 -0400342
343 if (erq->flags & IW_ENCODE_DISABLED) {
344 if (key_provided && *crypt) {
345 IEEE80211_DEBUG_WX("Disabling encryption on key %d.\n",
346 key);
John W. Linville274bfb82008-10-29 11:35:05 -0400347 lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt);
Jeff Garzikb4538722005-05-12 22:48:20 -0400348 } else
349 IEEE80211_DEBUG_WX("Disabling encryption.\n");
350
351 /* Check all the keys to see if any are still configured,
352 * and if no key index was provided, de-init them all */
353 for (i = 0; i < WEP_KEYS; i++) {
John W. Linville274bfb82008-10-29 11:35:05 -0400354 if (ieee->crypt_info.crypt[i] != NULL) {
Jeff Garzikb4538722005-05-12 22:48:20 -0400355 if (key_provided)
356 break;
John W. Linville274bfb82008-10-29 11:35:05 -0400357 lib80211_crypt_delayed_deinit(&ieee->crypt_info,
358 &ieee->crypt_info.crypt[i]);
Jeff Garzikb4538722005-05-12 22:48:20 -0400359 }
360 }
361
362 if (i == WEP_KEYS) {
363 sec.enabled = 0;
James Ketrenosf1bf6632005-09-21 11:53:54 -0500364 sec.encrypt = 0;
Jeff Garzikb4538722005-05-12 22:48:20 -0400365 sec.level = SEC_LEVEL_0;
James Ketrenos259bf1f2005-09-21 11:54:22 -0500366 sec.flags |= SEC_ENABLED | SEC_LEVEL | SEC_ENCRYPT;
Jeff Garzikb4538722005-05-12 22:48:20 -0400367 }
368
369 goto done;
370 }
371
Jeff Garzikb4538722005-05-12 22:48:20 -0400372 sec.enabled = 1;
James Ketrenosf1bf6632005-09-21 11:53:54 -0500373 sec.encrypt = 1;
James Ketrenos259bf1f2005-09-21 11:54:22 -0500374 sec.flags |= SEC_ENABLED | SEC_ENCRYPT;
Jeff Garzikb4538722005-05-12 22:48:20 -0400375
376 if (*crypt != NULL && (*crypt)->ops != NULL &&
377 strcmp((*crypt)->ops->name, "WEP") != 0) {
378 /* changing to use WEP; deinit previously used algorithm
379 * on this key */
John W. Linville274bfb82008-10-29 11:35:05 -0400380 lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt);
Jeff Garzikb4538722005-05-12 22:48:20 -0400381 }
382
James Ketrenosf1bf6632005-09-21 11:53:54 -0500383 if (*crypt == NULL && host_crypto) {
John W. Linville274bfb82008-10-29 11:35:05 -0400384 struct lib80211_crypt_data *new_crypt;
Jeff Garzikb4538722005-05-12 22:48:20 -0400385
386 /* take WEP into use */
John W. Linville274bfb82008-10-29 11:35:05 -0400387 new_crypt = kzalloc(sizeof(struct lib80211_crypt_data),
Jeff Garzikb4538722005-05-12 22:48:20 -0400388 GFP_KERNEL);
389 if (new_crypt == NULL)
390 return -ENOMEM;
John W. Linville274bfb82008-10-29 11:35:05 -0400391 new_crypt->ops = lib80211_get_crypto_ops("WEP");
Jeff Garzikb4538722005-05-12 22:48:20 -0400392 if (!new_crypt->ops) {
John W. Linville274bfb82008-10-29 11:35:05 -0400393 request_module("lib80211_crypt_wep");
394 new_crypt->ops = lib80211_get_crypto_ops("WEP");
Jeff Garzikb4538722005-05-12 22:48:20 -0400395 }
396
397 if (new_crypt->ops && try_module_get(new_crypt->ops->owner))
James Ketrenos6eb6edf2005-09-22 10:34:15 +0000398 new_crypt->priv = new_crypt->ops->init(key);
Jeff Garzikb4538722005-05-12 22:48:20 -0400399
400 if (!new_crypt->ops || !new_crypt->priv) {
401 kfree(new_crypt);
402 new_crypt = NULL;
403
404 printk(KERN_WARNING "%s: could not initialize WEP: "
John W. Linville274bfb82008-10-29 11:35:05 -0400405 "load module lib80211_crypt_wep\n", dev->name);
Jeff Garzikb4538722005-05-12 22:48:20 -0400406 return -EOPNOTSUPP;
407 }
408 *crypt = new_crypt;
409 }
410
411 /* If a new key was provided, set it up */
412 if (erq->length > 0) {
Holger Schurig2a941ec2008-10-29 09:43:50 +0100413#ifdef CONFIG_IEEE80211_DEBUG
414 DECLARE_SSID_BUF(ssid);
415#endif
416
Jeff Garzikb4538722005-05-12 22:48:20 -0400417 len = erq->length <= 5 ? 5 : 13;
418 memcpy(sec.keys[key], keybuf, erq->length);
419 if (len > erq->length)
420 memset(sec.keys[key] + erq->length, 0,
421 len - erq->length);
422 IEEE80211_DEBUG_WX("Setting key %d to '%s' (%d:%d bytes)\n",
John W. Linville9387b7c2008-09-30 20:59:05 -0400423 key, print_ssid(ssid, sec.keys[key], len),
Jeff Garzikb4538722005-05-12 22:48:20 -0400424 erq->length, len);
425 sec.key_sizes[key] = len;
James Ketrenosf1bf6632005-09-21 11:53:54 -0500426 if (*crypt)
427 (*crypt)->ops->set_key(sec.keys[key], len, NULL,
428 (*crypt)->priv);
Jeff Garzikb4538722005-05-12 22:48:20 -0400429 sec.flags |= (1 << key);
430 /* This ensures a key will be activated if no key is
Jean Delvarec03983a2007-10-19 23:22:55 +0200431 * explicitly set */
Jeff Garzikb4538722005-05-12 22:48:20 -0400432 if (key == sec.active_key)
433 sec.flags |= SEC_ACTIVE_KEY;
Jeff Garzikb4538722005-05-12 22:48:20 -0400434
James Ketrenosf1bf6632005-09-21 11:53:54 -0500435 } else {
436 if (host_crypto) {
437 len = (*crypt)->ops->get_key(sec.keys[key], WEP_KEY_LEN,
438 NULL, (*crypt)->priv);
439 if (len == 0) {
440 /* Set a default key of all 0 */
441 IEEE80211_DEBUG_WX("Setting key %d to all "
442 "zero.\n", key);
443 memset(sec.keys[key], 0, 13);
444 (*crypt)->ops->set_key(sec.keys[key], 13, NULL,
445 (*crypt)->priv);
446 sec.key_sizes[key] = 13;
447 sec.flags |= (1 << key);
448 }
449 }
Jeff Garzikb4538722005-05-12 22:48:20 -0400450 /* No key data - just set the default TX key index */
451 if (key_provided) {
James Ketrenosf1bf6632005-09-21 11:53:54 -0500452 IEEE80211_DEBUG_WX("Setting key %d to default Tx "
453 "key.\n", key);
John W. Linville274bfb82008-10-29 11:35:05 -0400454 ieee->crypt_info.tx_keyidx = key;
Jeff Garzikb4538722005-05-12 22:48:20 -0400455 sec.active_key = key;
456 sec.flags |= SEC_ACTIVE_KEY;
457 }
458 }
James Ketrenos7dc888f2005-09-21 11:58:38 -0500459 if (erq->flags & (IW_ENCODE_OPEN | IW_ENCODE_RESTRICTED)) {
460 ieee->open_wep = !(erq->flags & IW_ENCODE_RESTRICTED);
461 sec.auth_mode = ieee->open_wep ? WLAN_AUTH_OPEN :
462 WLAN_AUTH_SHARED_KEY;
463 sec.flags |= SEC_AUTH_MODE;
464 IEEE80211_DEBUG_WX("Auth: %s\n",
465 sec.auth_mode == WLAN_AUTH_OPEN ?
466 "OPEN" : "SHARED KEY");
467 }
Jeff Garzikb4538722005-05-12 22:48:20 -0400468
469 /* For now we just support WEP, so only set that security level...
470 * TODO: When WPA is added this is one place that needs to change */
471 sec.flags |= SEC_LEVEL;
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400472 sec.level = SEC_LEVEL_1; /* 40 and 104 bit WEP */
James Ketrenose0d369d2005-09-21 11:54:30 -0500473 sec.encode_alg[key] = SEC_ALG_WEP;
Jeff Garzikb4538722005-05-12 22:48:20 -0400474
James Ketrenos259bf1f2005-09-21 11:54:22 -0500475 done:
Jeff Garzikb4538722005-05-12 22:48:20 -0400476 if (ieee->set_security)
477 ieee->set_security(dev, &sec);
478
479 /* Do not reset port if card is in Managed mode since resetting will
480 * generate new IEEE 802.11 authentication which may end up in looping
481 * with IEEE 802.1X. If your hardware requires a reset after WEP
482 * configuration (for example... Prism2), implement the reset_port in
483 * the callbacks structures used to initialize the 802.11 stack. */
484 if (ieee->reset_on_keychange &&
485 ieee->iw_mode != IW_MODE_INFRA &&
486 ieee->reset_port && ieee->reset_port(dev)) {
487 printk(KERN_DEBUG "%s: reset_port failed\n", dev->name);
488 return -EINVAL;
489 }
490 return 0;
491}
492
493int ieee80211_wx_get_encode(struct ieee80211_device *ieee,
494 struct iw_request_info *info,
495 union iwreq_data *wrqu, char *keybuf)
496{
497 struct iw_point *erq = &(wrqu->encoding);
498 int len, key;
John W. Linville274bfb82008-10-29 11:35:05 -0400499 struct lib80211_crypt_data *crypt;
James Ketrenosf1bf6632005-09-21 11:53:54 -0500500 struct ieee80211_security *sec = &ieee->sec;
Jeff Garzikb4538722005-05-12 22:48:20 -0400501
502 IEEE80211_DEBUG_WX("GET_ENCODE\n");
503
504 key = erq->flags & IW_ENCODE_INDEX;
505 if (key) {
506 if (key > WEP_KEYS)
507 return -EINVAL;
508 key--;
509 } else
John W. Linville274bfb82008-10-29 11:35:05 -0400510 key = ieee->crypt_info.tx_keyidx;
Jeff Garzikb4538722005-05-12 22:48:20 -0400511
John W. Linville274bfb82008-10-29 11:35:05 -0400512 crypt = ieee->crypt_info.crypt[key];
Jeff Garzikb4538722005-05-12 22:48:20 -0400513 erq->flags = key + 1;
514
James Ketrenosf1bf6632005-09-21 11:53:54 -0500515 if (!sec->enabled) {
Jeff Garzikb4538722005-05-12 22:48:20 -0400516 erq->length = 0;
517 erq->flags |= IW_ENCODE_DISABLED;
518 return 0;
519 }
520
James Ketrenosf1bf6632005-09-21 11:53:54 -0500521 len = sec->key_sizes[key];
522 memcpy(keybuf, sec->keys[key], len);
Jeff Garzikb4538722005-05-12 22:48:20 -0400523
Adrian Bunk62741152006-04-27 02:33:42 -0700524 erq->length = len;
Jeff Garzikb4538722005-05-12 22:48:20 -0400525 erq->flags |= IW_ENCODE_ENABLED;
526
527 if (ieee->open_wep)
528 erq->flags |= IW_ENCODE_OPEN;
529 else
530 erq->flags |= IW_ENCODE_RESTRICTED;
531
532 return 0;
533}
534
James Ketrenose0d369d2005-09-21 11:54:30 -0500535int ieee80211_wx_set_encodeext(struct ieee80211_device *ieee,
536 struct iw_request_info *info,
537 union iwreq_data *wrqu, char *extra)
538{
539 struct net_device *dev = ieee->dev;
540 struct iw_point *encoding = &wrqu->encoding;
541 struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
542 int i, idx, ret = 0;
James Ketrenosccd0fda2005-09-21 11:58:32 -0500543 int group_key = 0;
James Ketrenose0d369d2005-09-21 11:54:30 -0500544 const char *alg, *module;
John W. Linville274bfb82008-10-29 11:35:05 -0400545 struct lib80211_crypto_ops *ops;
546 struct lib80211_crypt_data **crypt;
James Ketrenose0d369d2005-09-21 11:54:30 -0500547
548 struct ieee80211_security sec = {
549 .flags = 0,
550 };
551
552 idx = encoding->flags & IW_ENCODE_INDEX;
553 if (idx) {
554 if (idx < 1 || idx > WEP_KEYS)
555 return -EINVAL;
556 idx--;
557 } else
John W. Linville274bfb82008-10-29 11:35:05 -0400558 idx = ieee->crypt_info.tx_keyidx;
James Ketrenose0d369d2005-09-21 11:54:30 -0500559
James Ketrenosccd0fda2005-09-21 11:58:32 -0500560 if (ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) {
John W. Linville274bfb82008-10-29 11:35:05 -0400561 crypt = &ieee->crypt_info.crypt[idx];
James Ketrenosccd0fda2005-09-21 11:58:32 -0500562 group_key = 1;
563 } else {
Volker Braune1892772005-10-24 10:15:36 -0500564 /* some Cisco APs use idx>0 for unicast in dynamic WEP */
565 if (idx != 0 && ext->alg != IW_ENCODE_ALG_WEP)
James Ketrenose0d369d2005-09-21 11:54:30 -0500566 return -EINVAL;
567 if (ieee->iw_mode == IW_MODE_INFRA)
John W. Linville274bfb82008-10-29 11:35:05 -0400568 crypt = &ieee->crypt_info.crypt[idx];
James Ketrenose0d369d2005-09-21 11:54:30 -0500569 else
570 return -EINVAL;
571 }
572
573 sec.flags |= SEC_ENABLED | SEC_ENCRYPT;
574 if ((encoding->flags & IW_ENCODE_DISABLED) ||
575 ext->alg == IW_ENCODE_ALG_NONE) {
576 if (*crypt)
John W. Linville274bfb82008-10-29 11:35:05 -0400577 lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt);
James Ketrenose0d369d2005-09-21 11:54:30 -0500578
579 for (i = 0; i < WEP_KEYS; i++)
John W. Linville274bfb82008-10-29 11:35:05 -0400580 if (ieee->crypt_info.crypt[i] != NULL)
James Ketrenose0d369d2005-09-21 11:54:30 -0500581 break;
582
583 if (i == WEP_KEYS) {
584 sec.enabled = 0;
585 sec.encrypt = 0;
586 sec.level = SEC_LEVEL_0;
587 sec.flags |= SEC_LEVEL;
588 }
589 goto done;
590 }
591
592 sec.enabled = 1;
593 sec.encrypt = 1;
594
James Ketrenosccd0fda2005-09-21 11:58:32 -0500595 if (group_key ? !ieee->host_mc_decrypt :
596 !(ieee->host_encrypt || ieee->host_decrypt ||
597 ieee->host_encrypt_msdu))
James Ketrenose0d369d2005-09-21 11:54:30 -0500598 goto skip_host_crypt;
599
600 switch (ext->alg) {
601 case IW_ENCODE_ALG_WEP:
602 alg = "WEP";
John W. Linville274bfb82008-10-29 11:35:05 -0400603 module = "lib80211_crypt_wep";
James Ketrenose0d369d2005-09-21 11:54:30 -0500604 break;
605 case IW_ENCODE_ALG_TKIP:
606 alg = "TKIP";
John W. Linville274bfb82008-10-29 11:35:05 -0400607 module = "lib80211_crypt_tkip";
James Ketrenose0d369d2005-09-21 11:54:30 -0500608 break;
609 case IW_ENCODE_ALG_CCMP:
610 alg = "CCMP";
John W. Linville274bfb82008-10-29 11:35:05 -0400611 module = "lib80211_crypt_ccmp";
James Ketrenose0d369d2005-09-21 11:54:30 -0500612 break;
613 default:
614 IEEE80211_DEBUG_WX("%s: unknown crypto alg %d\n",
615 dev->name, ext->alg);
616 ret = -EINVAL;
617 goto done;
618 }
619
John W. Linville274bfb82008-10-29 11:35:05 -0400620 ops = lib80211_get_crypto_ops(alg);
James Ketrenose0d369d2005-09-21 11:54:30 -0500621 if (ops == NULL) {
622 request_module(module);
John W. Linville274bfb82008-10-29 11:35:05 -0400623 ops = lib80211_get_crypto_ops(alg);
James Ketrenose0d369d2005-09-21 11:54:30 -0500624 }
625 if (ops == NULL) {
626 IEEE80211_DEBUG_WX("%s: unknown crypto alg %d\n",
627 dev->name, ext->alg);
628 ret = -EINVAL;
629 goto done;
630 }
631
632 if (*crypt == NULL || (*crypt)->ops != ops) {
John W. Linville274bfb82008-10-29 11:35:05 -0400633 struct lib80211_crypt_data *new_crypt;
James Ketrenose0d369d2005-09-21 11:54:30 -0500634
John W. Linville274bfb82008-10-29 11:35:05 -0400635 lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt);
James Ketrenose0d369d2005-09-21 11:54:30 -0500636
Panagiotis Issaris0da974f2006-07-21 14:51:30 -0700637 new_crypt = kzalloc(sizeof(*new_crypt), GFP_KERNEL);
James Ketrenose0d369d2005-09-21 11:54:30 -0500638 if (new_crypt == NULL) {
639 ret = -ENOMEM;
640 goto done;
641 }
James Ketrenose0d369d2005-09-21 11:54:30 -0500642 new_crypt->ops = ops;
643 if (new_crypt->ops && try_module_get(new_crypt->ops->owner))
James Ketrenos6eb6edf2005-09-22 10:34:15 +0000644 new_crypt->priv = new_crypt->ops->init(idx);
James Ketrenose0d369d2005-09-21 11:54:30 -0500645 if (new_crypt->priv == NULL) {
646 kfree(new_crypt);
647 ret = -EINVAL;
648 goto done;
649 }
650 *crypt = new_crypt;
651 }
652
653 if (ext->key_len > 0 && (*crypt)->ops->set_key &&
654 (*crypt)->ops->set_key(ext->key, ext->key_len, ext->rx_seq,
655 (*crypt)->priv) < 0) {
656 IEEE80211_DEBUG_WX("%s: key setting failed\n", dev->name);
657 ret = -EINVAL;
658 goto done;
659 }
660
661 skip_host_crypt:
662 if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) {
John W. Linville274bfb82008-10-29 11:35:05 -0400663 ieee->crypt_info.tx_keyidx = idx;
James Ketrenose0d369d2005-09-21 11:54:30 -0500664 sec.active_key = idx;
665 sec.flags |= SEC_ACTIVE_KEY;
666 }
667
668 if (ext->alg != IW_ENCODE_ALG_NONE) {
669 memcpy(sec.keys[idx], ext->key, ext->key_len);
670 sec.key_sizes[idx] = ext->key_len;
671 sec.flags |= (1 << idx);
672 if (ext->alg == IW_ENCODE_ALG_WEP) {
673 sec.encode_alg[idx] = SEC_ALG_WEP;
674 sec.flags |= SEC_LEVEL;
675 sec.level = SEC_LEVEL_1;
676 } else if (ext->alg == IW_ENCODE_ALG_TKIP) {
677 sec.encode_alg[idx] = SEC_ALG_TKIP;
678 sec.flags |= SEC_LEVEL;
679 sec.level = SEC_LEVEL_2;
680 } else if (ext->alg == IW_ENCODE_ALG_CCMP) {
681 sec.encode_alg[idx] = SEC_ALG_CCMP;
682 sec.flags |= SEC_LEVEL;
683 sec.level = SEC_LEVEL_3;
684 }
James Ketrenosccd0fda2005-09-21 11:58:32 -0500685 /* Don't set sec level for group keys. */
686 if (group_key)
687 sec.flags &= ~SEC_LEVEL;
James Ketrenose0d369d2005-09-21 11:54:30 -0500688 }
689 done:
690 if (ieee->set_security)
691 ieee->set_security(ieee->dev, &sec);
692
693 /*
694 * Do not reset port if card is in Managed mode since resetting will
695 * generate new IEEE 802.11 authentication which may end up in looping
696 * with IEEE 802.1X. If your hardware requires a reset after WEP
697 * configuration (for example... Prism2), implement the reset_port in
698 * the callbacks structures used to initialize the 802.11 stack.
699 */
700 if (ieee->reset_on_keychange &&
701 ieee->iw_mode != IW_MODE_INFRA &&
702 ieee->reset_port && ieee->reset_port(dev)) {
703 IEEE80211_DEBUG_WX("%s: reset_port failed\n", dev->name);
704 return -EINVAL;
705 }
706
707 return ret;
708}
709
710int ieee80211_wx_get_encodeext(struct ieee80211_device *ieee,
711 struct iw_request_info *info,
712 union iwreq_data *wrqu, char *extra)
713{
714 struct iw_point *encoding = &wrqu->encoding;
715 struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
716 struct ieee80211_security *sec = &ieee->sec;
717 int idx, max_key_len;
718
719 max_key_len = encoding->length - sizeof(*ext);
720 if (max_key_len < 0)
721 return -EINVAL;
722
723 idx = encoding->flags & IW_ENCODE_INDEX;
724 if (idx) {
725 if (idx < 1 || idx > WEP_KEYS)
726 return -EINVAL;
727 idx--;
728 } else
John W. Linville274bfb82008-10-29 11:35:05 -0400729 idx = ieee->crypt_info.tx_keyidx;
James Ketrenose0d369d2005-09-21 11:54:30 -0500730
Roel Kluinf59d9782007-10-26 21:51:26 +0200731 if (!(ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) &&
Volker Braune1892772005-10-24 10:15:36 -0500732 ext->alg != IW_ENCODE_ALG_WEP)
James Ketrenose0d369d2005-09-21 11:54:30 -0500733 if (idx != 0 || ieee->iw_mode != IW_MODE_INFRA)
734 return -EINVAL;
735
736 encoding->flags = idx + 1;
737 memset(ext, 0, sizeof(*ext));
738
739 if (!sec->enabled) {
740 ext->alg = IW_ENCODE_ALG_NONE;
741 ext->key_len = 0;
742 encoding->flags |= IW_ENCODE_DISABLED;
743 } else {
744 if (sec->encode_alg[idx] == SEC_ALG_WEP)
745 ext->alg = IW_ENCODE_ALG_WEP;
746 else if (sec->encode_alg[idx] == SEC_ALG_TKIP)
747 ext->alg = IW_ENCODE_ALG_TKIP;
748 else if (sec->encode_alg[idx] == SEC_ALG_CCMP)
749 ext->alg = IW_ENCODE_ALG_CCMP;
750 else
751 return -EINVAL;
752
753 ext->key_len = sec->key_sizes[idx];
754 memcpy(ext->key, sec->keys[idx], ext->key_len);
755 encoding->flags |= IW_ENCODE_ENABLED;
756 if (ext->key_len &&
757 (ext->alg == IW_ENCODE_ALG_TKIP ||
758 ext->alg == IW_ENCODE_ALG_CCMP))
759 ext->ext_flags |= IW_ENCODE_EXT_TX_SEQ_VALID;
760
761 }
762
763 return 0;
764}
765
766EXPORT_SYMBOL(ieee80211_wx_set_encodeext);
767EXPORT_SYMBOL(ieee80211_wx_get_encodeext);
James Ketrenose0d369d2005-09-21 11:54:30 -0500768
Jeff Garzikb4538722005-05-12 22:48:20 -0400769EXPORT_SYMBOL(ieee80211_wx_get_scan);
770EXPORT_SYMBOL(ieee80211_wx_set_encode);
771EXPORT_SYMBOL(ieee80211_wx_get_encode);