blob: 54aba474443867f463714e117f14a605ba251803 [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:
Reinette Chatrec1eb2c82009-08-21 13:34:26 -070028 Intel Linux Wireless <ilw@linux.intel.com>
Jeff Garzikb4538722005-05-12 22:48:20 -040029 Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
30
31******************************************************************************/
Jeff Garzikbbeec902005-09-07 00:27:54 -040032
Alexey Dobriyana6b7a402011-06-06 10:43:46 +000033#include <linux/hardirq.h>
Jeff Garzikb4538722005-05-12 22:48:20 -040034#include <linux/kmod.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090035#include <linux/slab.h>
Jeff Garzikb4538722005-05-12 22:48:20 -040036#include <linux/module.h>
James Ketrenos42e349f2005-09-21 11:54:07 -050037#include <linux/jiffies.h>
Jeff Garzikb4538722005-05-12 22:48:20 -040038
John W. Linville9387b7c2008-09-30 20:59:05 -040039#include <net/lib80211.h>
Jeff Garzikbbeec902005-09-07 00:27:54 -040040#include <linux/wireless.h>
41
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -040042#include "libipw.h"
Dan Williamsf3734ee2009-02-12 12:32:55 -050043
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -040044static const char *libipw_modes[] = {
Jeff Garzikb4538722005-05-12 22:48:20 -040045 "?", "a", "b", "ab", "g", "ag", "bg", "abg"
46};
47
Dan Williamsc3d72b92009-02-11 13:26:06 -050048static inline unsigned int elapsed_jiffies_msecs(unsigned long start)
49{
50 unsigned long end = jiffies;
51
52 if (end >= start)
53 return jiffies_to_msecs(end - start);
54
55 return jiffies_to_msecs(end + (MAX_JIFFY_OFFSET - start) + 1);
56}
57
Jeff Garzikb4538722005-05-12 22:48:20 -040058#define MAX_CUSTOM_LEN 64
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -040059static char *libipw_translate_scan(struct libipw_device *ieee,
David S. Millerccc58052008-06-16 18:50:49 -070060 char *start, char *stop,
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -040061 struct libipw_network *network,
David S. Millerccc58052008-06-16 18:50:49 -070062 struct iw_request_info *info)
Jeff Garzikb4538722005-05-12 22:48:20 -040063{
64 char custom[MAX_CUSTOM_LEN];
65 char *p;
66 struct iw_event iwe;
67 int i, j;
Zhu Yi09593042006-04-13 17:17:26 +080068 char *current_val; /* For rates */
69 u8 rate;
Jeff Garzikb4538722005-05-12 22:48:20 -040070
71 /* First entry *MUST* be the AP MAC address */
72 iwe.cmd = SIOCGIWAP;
73 iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
74 memcpy(iwe.u.ap_addr.sa_data, network->bssid, ETH_ALEN);
David S. Millerccc58052008-06-16 18:50:49 -070075 start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_ADDR_LEN);
Jeff Garzikb4538722005-05-12 22:48:20 -040076
77 /* Remaining entries will be displayed in the order we provide them */
78
79 /* Add the ESSID */
80 iwe.cmd = SIOCGIWESSID;
81 iwe.u.data.flags = 1;
John W. Linvillec5d3dce2008-09-30 17:17:26 -040082 iwe.u.data.length = min(network->ssid_len, (u8) 32);
83 start = iwe_stream_add_point(info, start, stop,
84 &iwe, network->ssid);
Jeff Garzikb4538722005-05-12 22:48:20 -040085
86 /* Add the protocol name */
87 iwe.cmd = SIOCGIWNAME;
Jeff Garzik0edd5b42005-09-07 00:48:31 -040088 snprintf(iwe.u.name, IFNAMSIZ, "IEEE 802.11%s",
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -040089 libipw_modes[network->mode]);
David S. Millerccc58052008-06-16 18:50:49 -070090 start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_CHAR_LEN);
Jeff Garzikb4538722005-05-12 22:48:20 -040091
Jeff Garzik0edd5b42005-09-07 00:48:31 -040092 /* Add mode */
93 iwe.cmd = SIOCGIWMODE;
94 if (network->capability & (WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS)) {
Jeff Garzik1b5cca32005-08-15 00:32:15 -040095 if (network->capability & WLAN_CAPABILITY_ESS)
Jeff Garzikb4538722005-05-12 22:48:20 -040096 iwe.u.mode = IW_MODE_MASTER;
97 else
98 iwe.u.mode = IW_MODE_ADHOC;
99
David S. Millerccc58052008-06-16 18:50:49 -0700100 start = iwe_stream_add_event(info, start, stop,
101 &iwe, IW_EV_UINT_LEN);
Jeff Garzikb4538722005-05-12 22:48:20 -0400102 }
103
Larry Finger93afe3d2007-04-21 17:56:43 -0500104 /* Add channel and frequency */
Jean Tourrilhes90869b22007-07-10 15:51:14 -0500105 /* Note : userspace automatically computes channel using iwrange */
Jeff Garzikb4538722005-05-12 22:48:20 -0400106 iwe.cmd = SIOCGIWFREQ;
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -0400107 iwe.u.freq.m = libipw_channel_to_freq(ieee, network->channel);
Larry Finger93afe3d2007-04-21 17:56:43 -0500108 iwe.u.freq.e = 6;
Jean Tourrilhes90869b22007-07-10 15:51:14 -0500109 iwe.u.freq.i = 0;
David S. Millerccc58052008-06-16 18:50:49 -0700110 start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_FREQ_LEN);
Larry Finger93afe3d2007-04-21 17:56:43 -0500111
Jeff Garzikb4538722005-05-12 22:48:20 -0400112 /* Add encryption capability */
113 iwe.cmd = SIOCGIWENCODE;
114 if (network->capability & WLAN_CAPABILITY_PRIVACY)
115 iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
116 else
117 iwe.u.data.flags = IW_ENCODE_DISABLED;
118 iwe.u.data.length = 0;
David S. Millerccc58052008-06-16 18:50:49 -0700119 start = iwe_stream_add_point(info, start, stop,
120 &iwe, network->ssid);
Jeff Garzikb4538722005-05-12 22:48:20 -0400121
122 /* Add basic and extended rates */
Zhu Yi09593042006-04-13 17:17:26 +0800123 /* Rate : stuffing multiple values in a single event require a bit
124 * more of magic - Jean II */
David S. Millerccc58052008-06-16 18:50:49 -0700125 current_val = start + iwe_stream_lcp_len(info);
Zhu Yi09593042006-04-13 17:17:26 +0800126 iwe.cmd = SIOCGIWRATE;
127 /* Those two flags are ignored... */
128 iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0;
129
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400130 for (i = 0, j = 0; i < network->rates_len;) {
Jeff Garzikb4538722005-05-12 22:48:20 -0400131 if (j < network->rates_ex_len &&
132 ((network->rates_ex[j] & 0x7F) <
133 (network->rates[i] & 0x7F)))
134 rate = network->rates_ex[j++] & 0x7F;
135 else
136 rate = network->rates[i++] & 0x7F;
Zhu Yi09593042006-04-13 17:17:26 +0800137 /* Bit rate given in 500 kb/s units (+ 0x80) */
138 iwe.u.bitrate.value = ((rate & 0x7f) * 500000);
139 /* Add new value to event */
David S. Millerccc58052008-06-16 18:50:49 -0700140 current_val = iwe_stream_add_value(info, start, current_val,
141 stop, &iwe, IW_EV_PARAM_LEN);
Jeff Garzikb4538722005-05-12 22:48:20 -0400142 }
143 for (; j < network->rates_ex_len; j++) {
144 rate = network->rates_ex[j] & 0x7F;
Zhu Yi09593042006-04-13 17:17:26 +0800145 /* Bit rate given in 500 kb/s units (+ 0x80) */
146 iwe.u.bitrate.value = ((rate & 0x7f) * 500000);
147 /* Add new value to event */
David S. Millerccc58052008-06-16 18:50:49 -0700148 current_val = iwe_stream_add_value(info, start, current_val,
149 stop, &iwe, IW_EV_PARAM_LEN);
Jeff Garzikb4538722005-05-12 22:48:20 -0400150 }
Zhu Yi09593042006-04-13 17:17:26 +0800151 /* Check if we added any rate */
David S. Millerccc58052008-06-16 18:50:49 -0700152 if ((current_val - start) > iwe_stream_lcp_len(info))
Zhu Yi09593042006-04-13 17:17:26 +0800153 start = current_val;
Jeff Garzikb4538722005-05-12 22:48:20 -0400154
155 /* Add quality statistics */
Jeff Garzikb4538722005-05-12 22:48:20 -0400156 iwe.cmd = IWEVQUAL;
James Ketrenosb1b508e2005-09-13 17:27:19 -0500157 iwe.u.qual.updated = IW_QUAL_QUAL_UPDATED | IW_QUAL_LEVEL_UPDATED |
158 IW_QUAL_NOISE_UPDATED;
159
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -0400160 if (!(network->stats.mask & LIBIPW_STATMASK_RSSI)) {
James Ketrenosb1b508e2005-09-13 17:27:19 -0500161 iwe.u.qual.updated |= IW_QUAL_QUAL_INVALID |
162 IW_QUAL_LEVEL_INVALID;
163 iwe.u.qual.qual = 0;
James Ketrenosb1b508e2005-09-13 17:27:19 -0500164 } else {
Jiri Benc757d18f2005-10-10 19:16:53 +0200165 if (ieee->perfect_rssi == ieee->worst_rssi)
166 iwe.u.qual.qual = 100;
167 else
168 iwe.u.qual.qual =
169 (100 *
170 (ieee->perfect_rssi - ieee->worst_rssi) *
171 (ieee->perfect_rssi - ieee->worst_rssi) -
172 (ieee->perfect_rssi - network->stats.rssi) *
173 (15 * (ieee->perfect_rssi - ieee->worst_rssi) +
James Ketrenos81f87522005-10-24 10:20:53 -0500174 62 * (ieee->perfect_rssi -
175 network->stats.rssi))) /
176 ((ieee->perfect_rssi -
177 ieee->worst_rssi) * (ieee->perfect_rssi -
178 ieee->worst_rssi));
James Ketrenosb1b508e2005-09-13 17:27:19 -0500179 if (iwe.u.qual.qual > 100)
180 iwe.u.qual.qual = 100;
181 else if (iwe.u.qual.qual < 1)
182 iwe.u.qual.qual = 0;
183 }
184
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -0400185 if (!(network->stats.mask & LIBIPW_STATMASK_NOISE)) {
Jeff Garzikb4538722005-05-12 22:48:20 -0400186 iwe.u.qual.updated |= IW_QUAL_NOISE_INVALID;
James Ketrenosb1b508e2005-09-13 17:27:19 -0500187 iwe.u.qual.noise = 0;
188 } else {
189 iwe.u.qual.noise = network->stats.noise;
190 }
Jeff Garzikb4538722005-05-12 22:48:20 -0400191
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -0400192 if (!(network->stats.mask & LIBIPW_STATMASK_SIGNAL)) {
Zhu Yi7bd64362006-01-19 16:21:54 +0800193 iwe.u.qual.updated |= IW_QUAL_LEVEL_INVALID;
194 iwe.u.qual.level = 0;
195 } else {
196 iwe.u.qual.level = network->stats.signal;
197 }
198
David S. Millerccc58052008-06-16 18:50:49 -0700199 start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_QUAL_LEN);
Jeff Garzikb4538722005-05-12 22:48:20 -0400200
201 iwe.cmd = IWEVCUSTOM;
202 p = custom;
203
204 iwe.u.data.length = p - custom;
205 if (iwe.u.data.length)
David S. Millerccc58052008-06-16 18:50:49 -0700206 start = iwe_stream_add_point(info, start, stop, &iwe, custom);
Jeff Garzikb4538722005-05-12 22:48:20 -0400207
Zhu Yi47168082006-02-13 13:37:03 +0800208 memset(&iwe, 0, sizeof(iwe));
James Ketrenos20d64712005-09-21 11:53:43 -0500209 if (network->wpa_ie_len) {
Zhu Yi47168082006-02-13 13:37:03 +0800210 char buf[MAX_WPA_IE_LEN];
211 memcpy(buf, network->wpa_ie, network->wpa_ie_len);
212 iwe.cmd = IWEVGENIE;
213 iwe.u.data.length = network->wpa_ie_len;
David S. Millerccc58052008-06-16 18:50:49 -0700214 start = iwe_stream_add_point(info, start, stop, &iwe, buf);
Jeff Garzikb4538722005-05-12 22:48:20 -0400215 }
216
Zhu Yi47168082006-02-13 13:37:03 +0800217 memset(&iwe, 0, sizeof(iwe));
James Ketrenos20d64712005-09-21 11:53:43 -0500218 if (network->rsn_ie_len) {
Zhu Yi47168082006-02-13 13:37:03 +0800219 char buf[MAX_WPA_IE_LEN];
220 memcpy(buf, network->rsn_ie, network->rsn_ie_len);
221 iwe.cmd = IWEVGENIE;
222 iwe.u.data.length = network->rsn_ie_len;
David S. Millerccc58052008-06-16 18:50:49 -0700223 start = iwe_stream_add_point(info, start, stop, &iwe, buf);
Jeff Garzikb4538722005-05-12 22:48:20 -0400224 }
225
226 /* Add EXTRA: Age to display seconds since last beacon/probe response
227 * for given network. */
228 iwe.cmd = IWEVCUSTOM;
229 p = custom;
230 p += snprintf(p, MAX_CUSTOM_LEN - (p - custom),
Dan Williamsc3d72b92009-02-11 13:26:06 -0500231 " Last beacon: %ums ago",
232 elapsed_jiffies_msecs(network->last_scanned));
Jeff Garzikb4538722005-05-12 22:48:20 -0400233 iwe.u.data.length = p - custom;
234 if (iwe.u.data.length)
David S. Millerccc58052008-06-16 18:50:49 -0700235 start = iwe_stream_add_point(info, start, stop, &iwe, custom);
Jeff Garzikb4538722005-05-12 22:48:20 -0400236
Zhu Yi7bd64362006-01-19 16:21:54 +0800237 /* Add spectrum management information */
238 iwe.cmd = -1;
239 p = custom;
240 p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), " Channel flags: ");
241
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -0400242 if (libipw_get_channel_flags(ieee, network->channel) &
243 LIBIPW_CH_INVALID) {
Zhu Yi7bd64362006-01-19 16:21:54 +0800244 iwe.cmd = IWEVCUSTOM;
245 p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), "INVALID ");
246 }
247
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -0400248 if (libipw_get_channel_flags(ieee, network->channel) &
249 LIBIPW_CH_RADAR_DETECT) {
Zhu Yi7bd64362006-01-19 16:21:54 +0800250 iwe.cmd = IWEVCUSTOM;
251 p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), "DFS ");
252 }
253
254 if (iwe.cmd == IWEVCUSTOM) {
255 iwe.u.data.length = p - custom;
David S. Millerccc58052008-06-16 18:50:49 -0700256 start = iwe_stream_add_point(info, start, stop, &iwe, custom);
Zhu Yi7bd64362006-01-19 16:21:54 +0800257 }
258
Jeff Garzikb4538722005-05-12 22:48:20 -0400259 return start;
260}
261
Zhu Yi55cd94a2006-01-19 16:20:59 +0800262#define SCAN_ITEM_SIZE 128
263
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -0400264int libipw_wx_get_scan(struct libipw_device *ieee,
Jeff Garzikb4538722005-05-12 22:48:20 -0400265 struct iw_request_info *info,
266 union iwreq_data *wrqu, char *extra)
267{
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -0400268 struct libipw_network *network;
Jeff Garzikb4538722005-05-12 22:48:20 -0400269 unsigned long flags;
Zhu Yi55cd94a2006-01-19 16:20:59 +0800270 int err = 0;
Jeff Garzikb4538722005-05-12 22:48:20 -0400271
272 char *ev = extra;
Zhu Yi55cd94a2006-01-19 16:20:59 +0800273 char *stop = ev + wrqu->data.length;
Jeff Garzikb4538722005-05-12 22:48:20 -0400274 int i = 0;
John W. Linville9387b7c2008-09-30 20:59:05 -0400275 DECLARE_SSID_BUF(ssid);
Jeff Garzikb4538722005-05-12 22:48:20 -0400276
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -0400277 LIBIPW_DEBUG_WX("Getting scan\n");
Jeff Garzikb4538722005-05-12 22:48:20 -0400278
279 spin_lock_irqsave(&ieee->lock, flags);
280
281 list_for_each_entry(network, &ieee->network_list, list) {
282 i++;
Zhu Yi55cd94a2006-01-19 16:20:59 +0800283 if (stop - ev < SCAN_ITEM_SIZE) {
284 err = -E2BIG;
285 break;
286 }
287
Jeff Garzikb4538722005-05-12 22:48:20 -0400288 if (ieee->scan_age == 0 ||
289 time_after(network->last_scanned + ieee->scan_age, jiffies))
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -0400290 ev = libipw_translate_scan(ieee, ev, stop, network,
David S. Millerccc58052008-06-16 18:50:49 -0700291 info);
Dan Williamsc3d72b92009-02-11 13:26:06 -0500292 else {
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -0400293 LIBIPW_DEBUG_SCAN("Not showing network '%s ("
Dan Williamsc3d72b92009-02-11 13:26:06 -0500294 "%pM)' due to age (%ums).\n",
John W. Linville9387b7c2008-09-30 20:59:05 -0400295 print_ssid(ssid, network->ssid,
John W. Linville7e272fc2008-09-24 18:13:14 -0400296 network->ssid_len),
Johannes Berge1749612008-10-27 15:59:26 -0700297 network->bssid,
Dan Williamsc3d72b92009-02-11 13:26:06 -0500298 elapsed_jiffies_msecs(
299 network->last_scanned));
300 }
Jeff Garzikb4538722005-05-12 22:48:20 -0400301 }
302
303 spin_unlock_irqrestore(&ieee->lock, flags);
304
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400305 wrqu->data.length = ev - extra;
Jeff Garzikb4538722005-05-12 22:48:20 -0400306 wrqu->data.flags = 0;
307
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -0400308 LIBIPW_DEBUG_WX("exit: %d networks returned.\n", i);
Jeff Garzikb4538722005-05-12 22:48:20 -0400309
Zhu Yi55cd94a2006-01-19 16:20:59 +0800310 return err;
Jeff Garzikb4538722005-05-12 22:48:20 -0400311}
312
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -0400313int libipw_wx_set_encode(struct libipw_device *ieee,
Jeff Garzikb4538722005-05-12 22:48:20 -0400314 struct iw_request_info *info,
315 union iwreq_data *wrqu, char *keybuf)
316{
317 struct iw_point *erq = &(wrqu->encoding);
318 struct net_device *dev = ieee->dev;
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -0400319 struct libipw_security sec = {
Jeff Garzikb4538722005-05-12 22:48:20 -0400320 .flags = 0
321 };
322 int i, key, key_provided, len;
John W. Linville274bfb82008-10-29 11:35:05 -0400323 struct lib80211_crypt_data **crypt;
John W. Linville3289a832010-07-22 16:31:48 -0400324 int host_crypto = ieee->host_encrypt || ieee->host_decrypt;
John W. Linville9387b7c2008-09-30 20:59:05 -0400325 DECLARE_SSID_BUF(ssid);
Jeff Garzikb4538722005-05-12 22:48:20 -0400326
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -0400327 LIBIPW_DEBUG_WX("SET_ENCODE\n");
Jeff Garzikb4538722005-05-12 22:48:20 -0400328
329 key = erq->flags & IW_ENCODE_INDEX;
330 if (key) {
331 if (key > WEP_KEYS)
332 return -EINVAL;
333 key--;
334 key_provided = 1;
335 } else {
336 key_provided = 0;
John W. Linville274bfb82008-10-29 11:35:05 -0400337 key = ieee->crypt_info.tx_keyidx;
Jeff Garzikb4538722005-05-12 22:48:20 -0400338 }
339
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -0400340 LIBIPW_DEBUG_WX("Key: %d [%s]\n", key, key_provided ?
Jeff Garzikb4538722005-05-12 22:48:20 -0400341 "provided" : "default");
342
John W. Linville274bfb82008-10-29 11:35:05 -0400343 crypt = &ieee->crypt_info.crypt[key];
Jeff Garzikb4538722005-05-12 22:48:20 -0400344
345 if (erq->flags & IW_ENCODE_DISABLED) {
346 if (key_provided && *crypt) {
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -0400347 LIBIPW_DEBUG_WX("Disabling encryption on key %d.\n",
Jeff Garzikb4538722005-05-12 22:48:20 -0400348 key);
John W. Linville274bfb82008-10-29 11:35:05 -0400349 lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt);
Jeff Garzikb4538722005-05-12 22:48:20 -0400350 } else
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -0400351 LIBIPW_DEBUG_WX("Disabling encryption.\n");
Jeff Garzikb4538722005-05-12 22:48:20 -0400352
353 /* Check all the keys to see if any are still configured,
354 * and if no key index was provided, de-init them all */
355 for (i = 0; i < WEP_KEYS; i++) {
John W. Linville274bfb82008-10-29 11:35:05 -0400356 if (ieee->crypt_info.crypt[i] != NULL) {
Jeff Garzikb4538722005-05-12 22:48:20 -0400357 if (key_provided)
358 break;
John W. Linville274bfb82008-10-29 11:35:05 -0400359 lib80211_crypt_delayed_deinit(&ieee->crypt_info,
360 &ieee->crypt_info.crypt[i]);
Jeff Garzikb4538722005-05-12 22:48:20 -0400361 }
362 }
363
364 if (i == WEP_KEYS) {
365 sec.enabled = 0;
James Ketrenosf1bf6632005-09-21 11:53:54 -0500366 sec.encrypt = 0;
Jeff Garzikb4538722005-05-12 22:48:20 -0400367 sec.level = SEC_LEVEL_0;
James Ketrenos259bf1f2005-09-21 11:54:22 -0500368 sec.flags |= SEC_ENABLED | SEC_LEVEL | SEC_ENCRYPT;
Jeff Garzikb4538722005-05-12 22:48:20 -0400369 }
370
371 goto done;
372 }
373
Jeff Garzikb4538722005-05-12 22:48:20 -0400374 sec.enabled = 1;
James Ketrenosf1bf6632005-09-21 11:53:54 -0500375 sec.encrypt = 1;
James Ketrenos259bf1f2005-09-21 11:54:22 -0500376 sec.flags |= SEC_ENABLED | SEC_ENCRYPT;
Jeff Garzikb4538722005-05-12 22:48:20 -0400377
378 if (*crypt != NULL && (*crypt)->ops != NULL &&
379 strcmp((*crypt)->ops->name, "WEP") != 0) {
380 /* changing to use WEP; deinit previously used algorithm
381 * on this key */
John W. Linville274bfb82008-10-29 11:35:05 -0400382 lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt);
Jeff Garzikb4538722005-05-12 22:48:20 -0400383 }
384
James Ketrenosf1bf6632005-09-21 11:53:54 -0500385 if (*crypt == NULL && host_crypto) {
John W. Linville274bfb82008-10-29 11:35:05 -0400386 struct lib80211_crypt_data *new_crypt;
Jeff Garzikb4538722005-05-12 22:48:20 -0400387
388 /* take WEP into use */
John W. Linville274bfb82008-10-29 11:35:05 -0400389 new_crypt = kzalloc(sizeof(struct lib80211_crypt_data),
Jeff Garzikb4538722005-05-12 22:48:20 -0400390 GFP_KERNEL);
391 if (new_crypt == NULL)
392 return -ENOMEM;
John W. Linville274bfb82008-10-29 11:35:05 -0400393 new_crypt->ops = lib80211_get_crypto_ops("WEP");
Jeff Garzikb4538722005-05-12 22:48:20 -0400394 if (!new_crypt->ops) {
John W. Linville274bfb82008-10-29 11:35:05 -0400395 request_module("lib80211_crypt_wep");
396 new_crypt->ops = lib80211_get_crypto_ops("WEP");
Jeff Garzikb4538722005-05-12 22:48:20 -0400397 }
398
399 if (new_crypt->ops && try_module_get(new_crypt->ops->owner))
James Ketrenos6eb6edf2005-09-22 10:34:15 +0000400 new_crypt->priv = new_crypt->ops->init(key);
Jeff Garzikb4538722005-05-12 22:48:20 -0400401
402 if (!new_crypt->ops || !new_crypt->priv) {
403 kfree(new_crypt);
404 new_crypt = NULL;
405
406 printk(KERN_WARNING "%s: could not initialize WEP: "
John W. Linville274bfb82008-10-29 11:35:05 -0400407 "load module lib80211_crypt_wep\n", dev->name);
Jeff Garzikb4538722005-05-12 22:48:20 -0400408 return -EOPNOTSUPP;
409 }
410 *crypt = new_crypt;
411 }
412
413 /* If a new key was provided, set it up */
414 if (erq->length > 0) {
415 len = erq->length <= 5 ? 5 : 13;
416 memcpy(sec.keys[key], keybuf, erq->length);
417 if (len > erq->length)
418 memset(sec.keys[key] + erq->length, 0,
419 len - erq->length);
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -0400420 LIBIPW_DEBUG_WX("Setting key %d to '%s' (%d:%d bytes)\n",
John W. Linville9387b7c2008-09-30 20:59:05 -0400421 key, print_ssid(ssid, sec.keys[key], len),
Jeff Garzikb4538722005-05-12 22:48:20 -0400422 erq->length, len);
423 sec.key_sizes[key] = len;
James Ketrenosf1bf6632005-09-21 11:53:54 -0500424 if (*crypt)
425 (*crypt)->ops->set_key(sec.keys[key], len, NULL,
426 (*crypt)->priv);
Jeff Garzikb4538722005-05-12 22:48:20 -0400427 sec.flags |= (1 << key);
428 /* This ensures a key will be activated if no key is
Jean Delvarec03983a2007-10-19 23:22:55 +0200429 * explicitly set */
Jeff Garzikb4538722005-05-12 22:48:20 -0400430 if (key == sec.active_key)
431 sec.flags |= SEC_ACTIVE_KEY;
Jeff Garzikb4538722005-05-12 22:48:20 -0400432
James Ketrenosf1bf6632005-09-21 11:53:54 -0500433 } else {
434 if (host_crypto) {
435 len = (*crypt)->ops->get_key(sec.keys[key], WEP_KEY_LEN,
436 NULL, (*crypt)->priv);
437 if (len == 0) {
438 /* Set a default key of all 0 */
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -0400439 LIBIPW_DEBUG_WX("Setting key %d to all "
James Ketrenosf1bf6632005-09-21 11:53:54 -0500440 "zero.\n", key);
441 memset(sec.keys[key], 0, 13);
442 (*crypt)->ops->set_key(sec.keys[key], 13, NULL,
443 (*crypt)->priv);
444 sec.key_sizes[key] = 13;
445 sec.flags |= (1 << key);
446 }
447 }
Jeff Garzikb4538722005-05-12 22:48:20 -0400448 /* No key data - just set the default TX key index */
449 if (key_provided) {
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -0400450 LIBIPW_DEBUG_WX("Setting key %d to default Tx "
James Ketrenosf1bf6632005-09-21 11:53:54 -0500451 "key.\n", key);
John W. Linville274bfb82008-10-29 11:35:05 -0400452 ieee->crypt_info.tx_keyidx = key;
Jeff Garzikb4538722005-05-12 22:48:20 -0400453 sec.active_key = key;
454 sec.flags |= SEC_ACTIVE_KEY;
455 }
456 }
James Ketrenos7dc888f2005-09-21 11:58:38 -0500457 if (erq->flags & (IW_ENCODE_OPEN | IW_ENCODE_RESTRICTED)) {
458 ieee->open_wep = !(erq->flags & IW_ENCODE_RESTRICTED);
459 sec.auth_mode = ieee->open_wep ? WLAN_AUTH_OPEN :
460 WLAN_AUTH_SHARED_KEY;
461 sec.flags |= SEC_AUTH_MODE;
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -0400462 LIBIPW_DEBUG_WX("Auth: %s\n",
James Ketrenos7dc888f2005-09-21 11:58:38 -0500463 sec.auth_mode == WLAN_AUTH_OPEN ?
464 "OPEN" : "SHARED KEY");
465 }
Jeff Garzikb4538722005-05-12 22:48:20 -0400466
467 /* For now we just support WEP, so only set that security level...
468 * TODO: When WPA is added this is one place that needs to change */
469 sec.flags |= SEC_LEVEL;
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400470 sec.level = SEC_LEVEL_1; /* 40 and 104 bit WEP */
James Ketrenose0d369d2005-09-21 11:54:30 -0500471 sec.encode_alg[key] = SEC_ALG_WEP;
Jeff Garzikb4538722005-05-12 22:48:20 -0400472
James Ketrenos259bf1f2005-09-21 11:54:22 -0500473 done:
Jeff Garzikb4538722005-05-12 22:48:20 -0400474 if (ieee->set_security)
475 ieee->set_security(dev, &sec);
476
Jeff Garzikb4538722005-05-12 22:48:20 -0400477 return 0;
478}
479
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -0400480int libipw_wx_get_encode(struct libipw_device *ieee,
Jeff Garzikb4538722005-05-12 22:48:20 -0400481 struct iw_request_info *info,
482 union iwreq_data *wrqu, char *keybuf)
483{
484 struct iw_point *erq = &(wrqu->encoding);
485 int len, key;
John W. Linville274bfb82008-10-29 11:35:05 -0400486 struct lib80211_crypt_data *crypt;
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -0400487 struct libipw_security *sec = &ieee->sec;
Jeff Garzikb4538722005-05-12 22:48:20 -0400488
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -0400489 LIBIPW_DEBUG_WX("GET_ENCODE\n");
Jeff Garzikb4538722005-05-12 22:48:20 -0400490
491 key = erq->flags & IW_ENCODE_INDEX;
492 if (key) {
493 if (key > WEP_KEYS)
494 return -EINVAL;
495 key--;
496 } else
John W. Linville274bfb82008-10-29 11:35:05 -0400497 key = ieee->crypt_info.tx_keyidx;
Jeff Garzikb4538722005-05-12 22:48:20 -0400498
John W. Linville274bfb82008-10-29 11:35:05 -0400499 crypt = ieee->crypt_info.crypt[key];
Jeff Garzikb4538722005-05-12 22:48:20 -0400500 erq->flags = key + 1;
501
James Ketrenosf1bf6632005-09-21 11:53:54 -0500502 if (!sec->enabled) {
Jeff Garzikb4538722005-05-12 22:48:20 -0400503 erq->length = 0;
504 erq->flags |= IW_ENCODE_DISABLED;
505 return 0;
506 }
507
James Ketrenosf1bf6632005-09-21 11:53:54 -0500508 len = sec->key_sizes[key];
509 memcpy(keybuf, sec->keys[key], len);
Jeff Garzikb4538722005-05-12 22:48:20 -0400510
Adrian Bunk62741152006-04-27 02:33:42 -0700511 erq->length = len;
Jeff Garzikb4538722005-05-12 22:48:20 -0400512 erq->flags |= IW_ENCODE_ENABLED;
513
514 if (ieee->open_wep)
515 erq->flags |= IW_ENCODE_OPEN;
516 else
517 erq->flags |= IW_ENCODE_RESTRICTED;
518
519 return 0;
520}
521
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -0400522int libipw_wx_set_encodeext(struct libipw_device *ieee,
James Ketrenose0d369d2005-09-21 11:54:30 -0500523 struct iw_request_info *info,
524 union iwreq_data *wrqu, char *extra)
525{
526 struct net_device *dev = ieee->dev;
527 struct iw_point *encoding = &wrqu->encoding;
528 struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
529 int i, idx, ret = 0;
James Ketrenosccd0fda2005-09-21 11:58:32 -0500530 int group_key = 0;
James Ketrenose0d369d2005-09-21 11:54:30 -0500531 const char *alg, *module;
John W. Linville274bfb82008-10-29 11:35:05 -0400532 struct lib80211_crypto_ops *ops;
533 struct lib80211_crypt_data **crypt;
James Ketrenose0d369d2005-09-21 11:54:30 -0500534
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -0400535 struct libipw_security sec = {
James Ketrenose0d369d2005-09-21 11:54:30 -0500536 .flags = 0,
537 };
538
539 idx = encoding->flags & IW_ENCODE_INDEX;
540 if (idx) {
541 if (idx < 1 || idx > WEP_KEYS)
542 return -EINVAL;
543 idx--;
544 } else
John W. Linville274bfb82008-10-29 11:35:05 -0400545 idx = ieee->crypt_info.tx_keyidx;
James Ketrenose0d369d2005-09-21 11:54:30 -0500546
James Ketrenosccd0fda2005-09-21 11:58:32 -0500547 if (ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) {
John W. Linville274bfb82008-10-29 11:35:05 -0400548 crypt = &ieee->crypt_info.crypt[idx];
James Ketrenosccd0fda2005-09-21 11:58:32 -0500549 group_key = 1;
550 } else {
Volker Braune1892772005-10-24 10:15:36 -0500551 /* some Cisco APs use idx>0 for unicast in dynamic WEP */
552 if (idx != 0 && ext->alg != IW_ENCODE_ALG_WEP)
James Ketrenose0d369d2005-09-21 11:54:30 -0500553 return -EINVAL;
554 if (ieee->iw_mode == IW_MODE_INFRA)
John W. Linville274bfb82008-10-29 11:35:05 -0400555 crypt = &ieee->crypt_info.crypt[idx];
James Ketrenose0d369d2005-09-21 11:54:30 -0500556 else
557 return -EINVAL;
558 }
559
560 sec.flags |= SEC_ENABLED | SEC_ENCRYPT;
561 if ((encoding->flags & IW_ENCODE_DISABLED) ||
562 ext->alg == IW_ENCODE_ALG_NONE) {
563 if (*crypt)
John W. Linville274bfb82008-10-29 11:35:05 -0400564 lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt);
James Ketrenose0d369d2005-09-21 11:54:30 -0500565
566 for (i = 0; i < WEP_KEYS; i++)
John W. Linville274bfb82008-10-29 11:35:05 -0400567 if (ieee->crypt_info.crypt[i] != NULL)
James Ketrenose0d369d2005-09-21 11:54:30 -0500568 break;
569
570 if (i == WEP_KEYS) {
571 sec.enabled = 0;
572 sec.encrypt = 0;
573 sec.level = SEC_LEVEL_0;
574 sec.flags |= SEC_LEVEL;
575 }
576 goto done;
577 }
578
579 sec.enabled = 1;
580 sec.encrypt = 1;
581
James Ketrenosccd0fda2005-09-21 11:58:32 -0500582 if (group_key ? !ieee->host_mc_decrypt :
583 !(ieee->host_encrypt || ieee->host_decrypt ||
584 ieee->host_encrypt_msdu))
James Ketrenose0d369d2005-09-21 11:54:30 -0500585 goto skip_host_crypt;
586
587 switch (ext->alg) {
588 case IW_ENCODE_ALG_WEP:
589 alg = "WEP";
John W. Linville274bfb82008-10-29 11:35:05 -0400590 module = "lib80211_crypt_wep";
James Ketrenose0d369d2005-09-21 11:54:30 -0500591 break;
592 case IW_ENCODE_ALG_TKIP:
593 alg = "TKIP";
John W. Linville274bfb82008-10-29 11:35:05 -0400594 module = "lib80211_crypt_tkip";
James Ketrenose0d369d2005-09-21 11:54:30 -0500595 break;
596 case IW_ENCODE_ALG_CCMP:
597 alg = "CCMP";
John W. Linville274bfb82008-10-29 11:35:05 -0400598 module = "lib80211_crypt_ccmp";
James Ketrenose0d369d2005-09-21 11:54:30 -0500599 break;
600 default:
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -0400601 LIBIPW_DEBUG_WX("%s: unknown crypto alg %d\n",
James Ketrenose0d369d2005-09-21 11:54:30 -0500602 dev->name, ext->alg);
603 ret = -EINVAL;
604 goto done;
605 }
606
John W. Linville274bfb82008-10-29 11:35:05 -0400607 ops = lib80211_get_crypto_ops(alg);
James Ketrenose0d369d2005-09-21 11:54:30 -0500608 if (ops == NULL) {
609 request_module(module);
John W. Linville274bfb82008-10-29 11:35:05 -0400610 ops = lib80211_get_crypto_ops(alg);
James Ketrenose0d369d2005-09-21 11:54:30 -0500611 }
612 if (ops == NULL) {
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -0400613 LIBIPW_DEBUG_WX("%s: unknown crypto alg %d\n",
James Ketrenose0d369d2005-09-21 11:54:30 -0500614 dev->name, ext->alg);
615 ret = -EINVAL;
616 goto done;
617 }
618
619 if (*crypt == NULL || (*crypt)->ops != ops) {
John W. Linville274bfb82008-10-29 11:35:05 -0400620 struct lib80211_crypt_data *new_crypt;
James Ketrenose0d369d2005-09-21 11:54:30 -0500621
John W. Linville274bfb82008-10-29 11:35:05 -0400622 lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt);
James Ketrenose0d369d2005-09-21 11:54:30 -0500623
Panagiotis Issaris0da974f2006-07-21 14:51:30 -0700624 new_crypt = kzalloc(sizeof(*new_crypt), GFP_KERNEL);
James Ketrenose0d369d2005-09-21 11:54:30 -0500625 if (new_crypt == NULL) {
626 ret = -ENOMEM;
627 goto done;
628 }
James Ketrenose0d369d2005-09-21 11:54:30 -0500629 new_crypt->ops = ops;
630 if (new_crypt->ops && try_module_get(new_crypt->ops->owner))
James Ketrenos6eb6edf2005-09-22 10:34:15 +0000631 new_crypt->priv = new_crypt->ops->init(idx);
James Ketrenose0d369d2005-09-21 11:54:30 -0500632 if (new_crypt->priv == NULL) {
633 kfree(new_crypt);
634 ret = -EINVAL;
635 goto done;
636 }
637 *crypt = new_crypt;
638 }
639
640 if (ext->key_len > 0 && (*crypt)->ops->set_key &&
641 (*crypt)->ops->set_key(ext->key, ext->key_len, ext->rx_seq,
642 (*crypt)->priv) < 0) {
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -0400643 LIBIPW_DEBUG_WX("%s: key setting failed\n", dev->name);
James Ketrenose0d369d2005-09-21 11:54:30 -0500644 ret = -EINVAL;
645 goto done;
646 }
647
648 skip_host_crypt:
649 if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) {
John W. Linville274bfb82008-10-29 11:35:05 -0400650 ieee->crypt_info.tx_keyidx = idx;
James Ketrenose0d369d2005-09-21 11:54:30 -0500651 sec.active_key = idx;
652 sec.flags |= SEC_ACTIVE_KEY;
653 }
654
655 if (ext->alg != IW_ENCODE_ALG_NONE) {
656 memcpy(sec.keys[idx], ext->key, ext->key_len);
657 sec.key_sizes[idx] = ext->key_len;
658 sec.flags |= (1 << idx);
659 if (ext->alg == IW_ENCODE_ALG_WEP) {
660 sec.encode_alg[idx] = SEC_ALG_WEP;
661 sec.flags |= SEC_LEVEL;
662 sec.level = SEC_LEVEL_1;
663 } else if (ext->alg == IW_ENCODE_ALG_TKIP) {
664 sec.encode_alg[idx] = SEC_ALG_TKIP;
665 sec.flags |= SEC_LEVEL;
666 sec.level = SEC_LEVEL_2;
667 } else if (ext->alg == IW_ENCODE_ALG_CCMP) {
668 sec.encode_alg[idx] = SEC_ALG_CCMP;
669 sec.flags |= SEC_LEVEL;
670 sec.level = SEC_LEVEL_3;
671 }
James Ketrenosccd0fda2005-09-21 11:58:32 -0500672 /* Don't set sec level for group keys. */
673 if (group_key)
674 sec.flags &= ~SEC_LEVEL;
James Ketrenose0d369d2005-09-21 11:54:30 -0500675 }
676 done:
677 if (ieee->set_security)
Paul Bolle3fc7bc82012-09-21 12:02:02 +0200678 ieee->set_security(dev, &sec);
James Ketrenose0d369d2005-09-21 11:54:30 -0500679
James Ketrenose0d369d2005-09-21 11:54:30 -0500680 return ret;
681}
682
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -0400683int libipw_wx_get_encodeext(struct libipw_device *ieee,
James Ketrenose0d369d2005-09-21 11:54:30 -0500684 struct iw_request_info *info,
685 union iwreq_data *wrqu, char *extra)
686{
687 struct iw_point *encoding = &wrqu->encoding;
688 struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -0400689 struct libipw_security *sec = &ieee->sec;
James Ketrenose0d369d2005-09-21 11:54:30 -0500690 int idx, max_key_len;
691
692 max_key_len = encoding->length - sizeof(*ext);
693 if (max_key_len < 0)
694 return -EINVAL;
695
696 idx = encoding->flags & IW_ENCODE_INDEX;
697 if (idx) {
698 if (idx < 1 || idx > WEP_KEYS)
699 return -EINVAL;
700 idx--;
701 } else
John W. Linville274bfb82008-10-29 11:35:05 -0400702 idx = ieee->crypt_info.tx_keyidx;
James Ketrenose0d369d2005-09-21 11:54:30 -0500703
Roel Kluinf59d9782007-10-26 21:51:26 +0200704 if (!(ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) &&
Volker Braune1892772005-10-24 10:15:36 -0500705 ext->alg != IW_ENCODE_ALG_WEP)
James Ketrenose0d369d2005-09-21 11:54:30 -0500706 if (idx != 0 || ieee->iw_mode != IW_MODE_INFRA)
707 return -EINVAL;
708
709 encoding->flags = idx + 1;
710 memset(ext, 0, sizeof(*ext));
711
712 if (!sec->enabled) {
713 ext->alg = IW_ENCODE_ALG_NONE;
714 ext->key_len = 0;
715 encoding->flags |= IW_ENCODE_DISABLED;
716 } else {
717 if (sec->encode_alg[idx] == SEC_ALG_WEP)
718 ext->alg = IW_ENCODE_ALG_WEP;
719 else if (sec->encode_alg[idx] == SEC_ALG_TKIP)
720 ext->alg = IW_ENCODE_ALG_TKIP;
721 else if (sec->encode_alg[idx] == SEC_ALG_CCMP)
722 ext->alg = IW_ENCODE_ALG_CCMP;
723 else
724 return -EINVAL;
725
726 ext->key_len = sec->key_sizes[idx];
727 memcpy(ext->key, sec->keys[idx], ext->key_len);
728 encoding->flags |= IW_ENCODE_ENABLED;
729 if (ext->key_len &&
730 (ext->alg == IW_ENCODE_ALG_TKIP ||
731 ext->alg == IW_ENCODE_ALG_CCMP))
732 ext->ext_flags |= IW_ENCODE_EXT_TX_SEQ_VALID;
733
734 }
735
736 return 0;
737}
738
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -0400739EXPORT_SYMBOL(libipw_wx_set_encodeext);
740EXPORT_SYMBOL(libipw_wx_get_encodeext);
James Ketrenose0d369d2005-09-21 11:54:30 -0500741
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -0400742EXPORT_SYMBOL(libipw_wx_get_scan);
743EXPORT_SYMBOL(libipw_wx_set_encode);
744EXPORT_SYMBOL(libipw_wx_get_encode);