diff options
author | John W. Linville <linville@tuxdriver.com> | 2008-11-11 16:45:38 -0500 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2008-11-21 11:08:18 -0500 |
commit | beb2a7f331361bfe81e71acdb0739eae570475a2 (patch) | |
tree | de7ee004296ae3bd5aeb85c19c82d419074f4f29 /drivers/net/wireless/ipw2x00/libipw_wx.c | |
parent | 0795cd29b6fe05107b40080cb1fccadb96320c96 (diff) |
net/ieee80211 -> drivers/net/ipw2x00/libipw_* rename
The old ieee80211 code only remains as a support library for the ipw2100
and ipw2200 drivers. So, move the code and rename it appropriately to
reflects it's true purpose and status.
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/net/wireless/ipw2x00/libipw_wx.c')
-rw-r--r-- | drivers/net/wireless/ipw2x00/libipw_wx.c | 760 |
1 files changed, 760 insertions, 0 deletions
diff --git a/drivers/net/wireless/ipw2x00/libipw_wx.c b/drivers/net/wireless/ipw2x00/libipw_wx.c new file mode 100644 index 000000000000..31ea3abfc327 --- /dev/null +++ b/drivers/net/wireless/ipw2x00/libipw_wx.c | |||
@@ -0,0 +1,760 @@ | |||
1 | /****************************************************************************** | ||
2 | |||
3 | Copyright(c) 2004-2005 Intel Corporation. All rights reserved. | ||
4 | |||
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 | ||
8 | <j@w1.fi> | ||
9 | Copyright (c) 2002-2003, Jouni Malinen <j@w1.fi> | ||
10 | |||
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 | ******************************************************************************/ | ||
32 | |||
33 | #include <linux/kmod.h> | ||
34 | #include <linux/module.h> | ||
35 | #include <linux/jiffies.h> | ||
36 | |||
37 | #include <net/lib80211.h> | ||
38 | #include <net/ieee80211.h> | ||
39 | #include <linux/wireless.h> | ||
40 | |||
41 | static const char *ieee80211_modes[] = { | ||
42 | "?", "a", "b", "ab", "g", "ag", "bg", "abg" | ||
43 | }; | ||
44 | |||
45 | #define MAX_CUSTOM_LEN 64 | ||
46 | static char *ieee80211_translate_scan(struct ieee80211_device *ieee, | ||
47 | char *start, char *stop, | ||
48 | struct ieee80211_network *network, | ||
49 | struct iw_request_info *info) | ||
50 | { | ||
51 | char custom[MAX_CUSTOM_LEN]; | ||
52 | char *p; | ||
53 | struct iw_event iwe; | ||
54 | int i, j; | ||
55 | char *current_val; /* For rates */ | ||
56 | u8 rate; | ||
57 | |||
58 | /* First entry *MUST* be the AP MAC address */ | ||
59 | iwe.cmd = SIOCGIWAP; | ||
60 | iwe.u.ap_addr.sa_family = ARPHRD_ETHER; | ||
61 | memcpy(iwe.u.ap_addr.sa_data, network->bssid, ETH_ALEN); | ||
62 | start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_ADDR_LEN); | ||
63 | |||
64 | /* Remaining entries will be displayed in the order we provide them */ | ||
65 | |||
66 | /* Add the ESSID */ | ||
67 | iwe.cmd = SIOCGIWESSID; | ||
68 | iwe.u.data.flags = 1; | ||
69 | iwe.u.data.length = min(network->ssid_len, (u8) 32); | ||
70 | start = iwe_stream_add_point(info, start, stop, | ||
71 | &iwe, network->ssid); | ||
72 | |||
73 | /* Add the protocol name */ | ||
74 | iwe.cmd = SIOCGIWNAME; | ||
75 | snprintf(iwe.u.name, IFNAMSIZ, "IEEE 802.11%s", | ||
76 | ieee80211_modes[network->mode]); | ||
77 | start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_CHAR_LEN); | ||
78 | |||
79 | /* Add mode */ | ||
80 | iwe.cmd = SIOCGIWMODE; | ||
81 | if (network->capability & (WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS)) { | ||
82 | if (network->capability & WLAN_CAPABILITY_ESS) | ||
83 | iwe.u.mode = IW_MODE_MASTER; | ||
84 | else | ||
85 | iwe.u.mode = IW_MODE_ADHOC; | ||
86 | |||
87 | start = iwe_stream_add_event(info, start, stop, | ||
88 | &iwe, IW_EV_UINT_LEN); | ||
89 | } | ||
90 | |||
91 | /* Add channel and frequency */ | ||
92 | /* Note : userspace automatically computes channel using iwrange */ | ||
93 | iwe.cmd = SIOCGIWFREQ; | ||
94 | iwe.u.freq.m = ieee80211_channel_to_freq(ieee, network->channel); | ||
95 | iwe.u.freq.e = 6; | ||
96 | iwe.u.freq.i = 0; | ||
97 | start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_FREQ_LEN); | ||
98 | |||
99 | /* Add encryption capability */ | ||
100 | iwe.cmd = SIOCGIWENCODE; | ||
101 | if (network->capability & WLAN_CAPABILITY_PRIVACY) | ||
102 | iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY; | ||
103 | else | ||
104 | iwe.u.data.flags = IW_ENCODE_DISABLED; | ||
105 | iwe.u.data.length = 0; | ||
106 | start = iwe_stream_add_point(info, start, stop, | ||
107 | &iwe, network->ssid); | ||
108 | |||
109 | /* Add basic and extended rates */ | ||
110 | /* Rate : stuffing multiple values in a single event require a bit | ||
111 | * more of magic - Jean II */ | ||
112 | current_val = start + iwe_stream_lcp_len(info); | ||
113 | iwe.cmd = SIOCGIWRATE; | ||
114 | /* Those two flags are ignored... */ | ||
115 | iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0; | ||
116 | |||
117 | for (i = 0, j = 0; i < network->rates_len;) { | ||
118 | if (j < network->rates_ex_len && | ||
119 | ((network->rates_ex[j] & 0x7F) < | ||
120 | (network->rates[i] & 0x7F))) | ||
121 | rate = network->rates_ex[j++] & 0x7F; | ||
122 | else | ||
123 | rate = network->rates[i++] & 0x7F; | ||
124 | /* Bit rate given in 500 kb/s units (+ 0x80) */ | ||
125 | iwe.u.bitrate.value = ((rate & 0x7f) * 500000); | ||
126 | /* Add new value to event */ | ||
127 | current_val = iwe_stream_add_value(info, start, current_val, | ||
128 | stop, &iwe, IW_EV_PARAM_LEN); | ||
129 | } | ||
130 | for (; j < network->rates_ex_len; j++) { | ||
131 | rate = network->rates_ex[j] & 0x7F; | ||
132 | /* Bit rate given in 500 kb/s units (+ 0x80) */ | ||
133 | iwe.u.bitrate.value = ((rate & 0x7f) * 500000); | ||
134 | /* Add new value to event */ | ||
135 | current_val = iwe_stream_add_value(info, start, current_val, | ||
136 | stop, &iwe, IW_EV_PARAM_LEN); | ||
137 | } | ||
138 | /* Check if we added any rate */ | ||
139 | if ((current_val - start) > iwe_stream_lcp_len(info)) | ||
140 | start = current_val; | ||
141 | |||
142 | /* Add quality statistics */ | ||
143 | iwe.cmd = IWEVQUAL; | ||
144 | iwe.u.qual.updated = IW_QUAL_QUAL_UPDATED | IW_QUAL_LEVEL_UPDATED | | ||
145 | IW_QUAL_NOISE_UPDATED; | ||
146 | |||
147 | if (!(network->stats.mask & IEEE80211_STATMASK_RSSI)) { | ||
148 | iwe.u.qual.updated |= IW_QUAL_QUAL_INVALID | | ||
149 | IW_QUAL_LEVEL_INVALID; | ||
150 | iwe.u.qual.qual = 0; | ||
151 | } else { | ||
152 | if (ieee->perfect_rssi == ieee->worst_rssi) | ||
153 | iwe.u.qual.qual = 100; | ||
154 | else | ||
155 | iwe.u.qual.qual = | ||
156 | (100 * | ||
157 | (ieee->perfect_rssi - ieee->worst_rssi) * | ||
158 | (ieee->perfect_rssi - ieee->worst_rssi) - | ||
159 | (ieee->perfect_rssi - network->stats.rssi) * | ||
160 | (15 * (ieee->perfect_rssi - ieee->worst_rssi) + | ||
161 | 62 * (ieee->perfect_rssi - | ||
162 | network->stats.rssi))) / | ||
163 | ((ieee->perfect_rssi - | ||
164 | ieee->worst_rssi) * (ieee->perfect_rssi - | ||
165 | ieee->worst_rssi)); | ||
166 | if (iwe.u.qual.qual > 100) | ||
167 | iwe.u.qual.qual = 100; | ||
168 | else if (iwe.u.qual.qual < 1) | ||
169 | iwe.u.qual.qual = 0; | ||
170 | } | ||
171 | |||
172 | if (!(network->stats.mask & IEEE80211_STATMASK_NOISE)) { | ||
173 | iwe.u.qual.updated |= IW_QUAL_NOISE_INVALID; | ||
174 | iwe.u.qual.noise = 0; | ||
175 | } else { | ||
176 | iwe.u.qual.noise = network->stats.noise; | ||
177 | } | ||
178 | |||
179 | if (!(network->stats.mask & IEEE80211_STATMASK_SIGNAL)) { | ||
180 | iwe.u.qual.updated |= IW_QUAL_LEVEL_INVALID; | ||
181 | iwe.u.qual.level = 0; | ||
182 | } else { | ||
183 | iwe.u.qual.level = network->stats.signal; | ||
184 | } | ||
185 | |||
186 | start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_QUAL_LEN); | ||
187 | |||
188 | iwe.cmd = IWEVCUSTOM; | ||
189 | p = custom; | ||
190 | |||
191 | iwe.u.data.length = p - custom; | ||
192 | if (iwe.u.data.length) | ||
193 | start = iwe_stream_add_point(info, start, stop, &iwe, custom); | ||
194 | |||
195 | memset(&iwe, 0, sizeof(iwe)); | ||
196 | if (network->wpa_ie_len) { | ||
197 | char buf[MAX_WPA_IE_LEN]; | ||
198 | memcpy(buf, network->wpa_ie, network->wpa_ie_len); | ||
199 | iwe.cmd = IWEVGENIE; | ||
200 | iwe.u.data.length = network->wpa_ie_len; | ||
201 | start = iwe_stream_add_point(info, start, stop, &iwe, buf); | ||
202 | } | ||
203 | |||
204 | memset(&iwe, 0, sizeof(iwe)); | ||
205 | if (network->rsn_ie_len) { | ||
206 | char buf[MAX_WPA_IE_LEN]; | ||
207 | memcpy(buf, network->rsn_ie, network->rsn_ie_len); | ||
208 | iwe.cmd = IWEVGENIE; | ||
209 | iwe.u.data.length = network->rsn_ie_len; | ||
210 | start = iwe_stream_add_point(info, start, stop, &iwe, buf); | ||
211 | } | ||
212 | |||
213 | /* Add EXTRA: Age to display seconds since last beacon/probe response | ||
214 | * for given network. */ | ||
215 | iwe.cmd = IWEVCUSTOM; | ||
216 | p = custom; | ||
217 | p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), | ||
218 | " Last beacon: %dms ago", | ||
219 | jiffies_to_msecs(jiffies - network->last_scanned)); | ||
220 | iwe.u.data.length = p - custom; | ||
221 | if (iwe.u.data.length) | ||
222 | start = iwe_stream_add_point(info, start, stop, &iwe, custom); | ||
223 | |||
224 | /* Add spectrum management information */ | ||
225 | iwe.cmd = -1; | ||
226 | p = custom; | ||
227 | p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), " Channel flags: "); | ||
228 | |||
229 | if (ieee80211_get_channel_flags(ieee, network->channel) & | ||
230 | IEEE80211_CH_INVALID) { | ||
231 | iwe.cmd = IWEVCUSTOM; | ||
232 | p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), "INVALID "); | ||
233 | } | ||
234 | |||
235 | if (ieee80211_get_channel_flags(ieee, network->channel) & | ||
236 | IEEE80211_CH_RADAR_DETECT) { | ||
237 | iwe.cmd = IWEVCUSTOM; | ||
238 | p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), "DFS "); | ||
239 | } | ||
240 | |||
241 | if (iwe.cmd == IWEVCUSTOM) { | ||
242 | iwe.u.data.length = p - custom; | ||
243 | start = iwe_stream_add_point(info, start, stop, &iwe, custom); | ||
244 | } | ||
245 | |||
246 | return start; | ||
247 | } | ||
248 | |||
249 | #define SCAN_ITEM_SIZE 128 | ||
250 | |||
251 | int ieee80211_wx_get_scan(struct ieee80211_device *ieee, | ||
252 | struct iw_request_info *info, | ||
253 | union iwreq_data *wrqu, char *extra) | ||
254 | { | ||
255 | struct ieee80211_network *network; | ||
256 | unsigned long flags; | ||
257 | int err = 0; | ||
258 | |||
259 | char *ev = extra; | ||
260 | char *stop = ev + wrqu->data.length; | ||
261 | int i = 0; | ||
262 | DECLARE_SSID_BUF(ssid); | ||
263 | |||
264 | IEEE80211_DEBUG_WX("Getting scan\n"); | ||
265 | |||
266 | spin_lock_irqsave(&ieee->lock, flags); | ||
267 | |||
268 | list_for_each_entry(network, &ieee->network_list, list) { | ||
269 | i++; | ||
270 | if (stop - ev < SCAN_ITEM_SIZE) { | ||
271 | err = -E2BIG; | ||
272 | break; | ||
273 | } | ||
274 | |||
275 | if (ieee->scan_age == 0 || | ||
276 | time_after(network->last_scanned + ieee->scan_age, jiffies)) | ||
277 | ev = ieee80211_translate_scan(ieee, ev, stop, network, | ||
278 | info); | ||
279 | else | ||
280 | IEEE80211_DEBUG_SCAN("Not showing network '%s (" | ||
281 | "%pM)' due to age (%dms).\n", | ||
282 | print_ssid(ssid, network->ssid, | ||
283 | network->ssid_len), | ||
284 | network->bssid, | ||
285 | jiffies_to_msecs(jiffies - | ||
286 | network-> | ||
287 | last_scanned)); | ||
288 | } | ||
289 | |||
290 | spin_unlock_irqrestore(&ieee->lock, flags); | ||
291 | |||
292 | wrqu->data.length = ev - extra; | ||
293 | wrqu->data.flags = 0; | ||
294 | |||
295 | IEEE80211_DEBUG_WX("exit: %d networks returned.\n", i); | ||
296 | |||
297 | return err; | ||
298 | } | ||
299 | |||
300 | int ieee80211_wx_set_encode(struct ieee80211_device *ieee, | ||
301 | struct iw_request_info *info, | ||
302 | union iwreq_data *wrqu, char *keybuf) | ||
303 | { | ||
304 | struct iw_point *erq = &(wrqu->encoding); | ||
305 | struct net_device *dev = ieee->dev; | ||
306 | struct ieee80211_security sec = { | ||
307 | .flags = 0 | ||
308 | }; | ||
309 | int i, key, key_provided, len; | ||
310 | struct lib80211_crypt_data **crypt; | ||
311 | int host_crypto = ieee->host_encrypt || ieee->host_decrypt || ieee->host_build_iv; | ||
312 | DECLARE_SSID_BUF(ssid); | ||
313 | |||
314 | IEEE80211_DEBUG_WX("SET_ENCODE\n"); | ||
315 | |||
316 | key = erq->flags & IW_ENCODE_INDEX; | ||
317 | if (key) { | ||
318 | if (key > WEP_KEYS) | ||
319 | return -EINVAL; | ||
320 | key--; | ||
321 | key_provided = 1; | ||
322 | } else { | ||
323 | key_provided = 0; | ||
324 | key = ieee->crypt_info.tx_keyidx; | ||
325 | } | ||
326 | |||
327 | IEEE80211_DEBUG_WX("Key: %d [%s]\n", key, key_provided ? | ||
328 | "provided" : "default"); | ||
329 | |||
330 | crypt = &ieee->crypt_info.crypt[key]; | ||
331 | |||
332 | if (erq->flags & IW_ENCODE_DISABLED) { | ||
333 | if (key_provided && *crypt) { | ||
334 | IEEE80211_DEBUG_WX("Disabling encryption on key %d.\n", | ||
335 | key); | ||
336 | lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt); | ||
337 | } else | ||
338 | IEEE80211_DEBUG_WX("Disabling encryption.\n"); | ||
339 | |||
340 | /* Check all the keys to see if any are still configured, | ||
341 | * and if no key index was provided, de-init them all */ | ||
342 | for (i = 0; i < WEP_KEYS; i++) { | ||
343 | if (ieee->crypt_info.crypt[i] != NULL) { | ||
344 | if (key_provided) | ||
345 | break; | ||
346 | lib80211_crypt_delayed_deinit(&ieee->crypt_info, | ||
347 | &ieee->crypt_info.crypt[i]); | ||
348 | } | ||
349 | } | ||
350 | |||
351 | if (i == WEP_KEYS) { | ||
352 | sec.enabled = 0; | ||
353 | sec.encrypt = 0; | ||
354 | sec.level = SEC_LEVEL_0; | ||
355 | sec.flags |= SEC_ENABLED | SEC_LEVEL | SEC_ENCRYPT; | ||
356 | } | ||
357 | |||
358 | goto done; | ||
359 | } | ||
360 | |||
361 | sec.enabled = 1; | ||
362 | sec.encrypt = 1; | ||
363 | sec.flags |= SEC_ENABLED | SEC_ENCRYPT; | ||
364 | |||
365 | if (*crypt != NULL && (*crypt)->ops != NULL && | ||
366 | strcmp((*crypt)->ops->name, "WEP") != 0) { | ||
367 | /* changing to use WEP; deinit previously used algorithm | ||
368 | * on this key */ | ||
369 | lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt); | ||
370 | } | ||
371 | |||
372 | if (*crypt == NULL && host_crypto) { | ||
373 | struct lib80211_crypt_data *new_crypt; | ||
374 | |||
375 | /* take WEP into use */ | ||
376 | new_crypt = kzalloc(sizeof(struct lib80211_crypt_data), | ||
377 | GFP_KERNEL); | ||
378 | if (new_crypt == NULL) | ||
379 | return -ENOMEM; | ||
380 | new_crypt->ops = lib80211_get_crypto_ops("WEP"); | ||
381 | if (!new_crypt->ops) { | ||
382 | request_module("lib80211_crypt_wep"); | ||
383 | new_crypt->ops = lib80211_get_crypto_ops("WEP"); | ||
384 | } | ||
385 | |||
386 | if (new_crypt->ops && try_module_get(new_crypt->ops->owner)) | ||
387 | new_crypt->priv = new_crypt->ops->init(key); | ||
388 | |||
389 | if (!new_crypt->ops || !new_crypt->priv) { | ||
390 | kfree(new_crypt); | ||
391 | new_crypt = NULL; | ||
392 | |||
393 | printk(KERN_WARNING "%s: could not initialize WEP: " | ||
394 | "load module lib80211_crypt_wep\n", dev->name); | ||
395 | return -EOPNOTSUPP; | ||
396 | } | ||
397 | *crypt = new_crypt; | ||
398 | } | ||
399 | |||
400 | /* If a new key was provided, set it up */ | ||
401 | if (erq->length > 0) { | ||
402 | #ifdef CONFIG_IEEE80211_DEBUG | ||
403 | DECLARE_SSID_BUF(ssid); | ||
404 | #endif | ||
405 | |||
406 | len = erq->length <= 5 ? 5 : 13; | ||
407 | memcpy(sec.keys[key], keybuf, erq->length); | ||
408 | if (len > erq->length) | ||
409 | memset(sec.keys[key] + erq->length, 0, | ||
410 | len - erq->length); | ||
411 | IEEE80211_DEBUG_WX("Setting key %d to '%s' (%d:%d bytes)\n", | ||
412 | key, print_ssid(ssid, sec.keys[key], len), | ||
413 | erq->length, len); | ||
414 | sec.key_sizes[key] = len; | ||
415 | if (*crypt) | ||
416 | (*crypt)->ops->set_key(sec.keys[key], len, NULL, | ||
417 | (*crypt)->priv); | ||
418 | sec.flags |= (1 << key); | ||
419 | /* This ensures a key will be activated if no key is | ||
420 | * explicitly set */ | ||
421 | if (key == sec.active_key) | ||
422 | sec.flags |= SEC_ACTIVE_KEY; | ||
423 | |||
424 | } else { | ||
425 | if (host_crypto) { | ||
426 | len = (*crypt)->ops->get_key(sec.keys[key], WEP_KEY_LEN, | ||
427 | NULL, (*crypt)->priv); | ||
428 | if (len == 0) { | ||
429 | /* Set a default key of all 0 */ | ||
430 | IEEE80211_DEBUG_WX("Setting key %d to all " | ||
431 | "zero.\n", key); | ||
432 | memset(sec.keys[key], 0, 13); | ||
433 | (*crypt)->ops->set_key(sec.keys[key], 13, NULL, | ||
434 | (*crypt)->priv); | ||
435 | sec.key_sizes[key] = 13; | ||
436 | sec.flags |= (1 << key); | ||
437 | } | ||
438 | } | ||
439 | /* No key data - just set the default TX key index */ | ||
440 | if (key_provided) { | ||
441 | IEEE80211_DEBUG_WX("Setting key %d to default Tx " | ||
442 | "key.\n", key); | ||
443 | ieee->crypt_info.tx_keyidx = key; | ||
444 | sec.active_key = key; | ||
445 | sec.flags |= SEC_ACTIVE_KEY; | ||
446 | } | ||
447 | } | ||
448 | if (erq->flags & (IW_ENCODE_OPEN | IW_ENCODE_RESTRICTED)) { | ||
449 | ieee->open_wep = !(erq->flags & IW_ENCODE_RESTRICTED); | ||
450 | sec.auth_mode = ieee->open_wep ? WLAN_AUTH_OPEN : | ||
451 | WLAN_AUTH_SHARED_KEY; | ||
452 | sec.flags |= SEC_AUTH_MODE; | ||
453 | IEEE80211_DEBUG_WX("Auth: %s\n", | ||
454 | sec.auth_mode == WLAN_AUTH_OPEN ? | ||
455 | "OPEN" : "SHARED KEY"); | ||
456 | } | ||
457 | |||
458 | /* For now we just support WEP, so only set that security level... | ||
459 | * TODO: When WPA is added this is one place that needs to change */ | ||
460 | sec.flags |= SEC_LEVEL; | ||
461 | sec.level = SEC_LEVEL_1; /* 40 and 104 bit WEP */ | ||
462 | sec.encode_alg[key] = SEC_ALG_WEP; | ||
463 | |||
464 | done: | ||
465 | if (ieee->set_security) | ||
466 | ieee->set_security(dev, &sec); | ||
467 | |||
468 | /* Do not reset port if card is in Managed mode since resetting will | ||
469 | * generate new IEEE 802.11 authentication which may end up in looping | ||
470 | * with IEEE 802.1X. If your hardware requires a reset after WEP | ||
471 | * configuration (for example... Prism2), implement the reset_port in | ||
472 | * the callbacks structures used to initialize the 802.11 stack. */ | ||
473 | if (ieee->reset_on_keychange && | ||
474 | ieee->iw_mode != IW_MODE_INFRA && | ||
475 | ieee->reset_port && ieee->reset_port(dev)) { | ||
476 | printk(KERN_DEBUG "%s: reset_port failed\n", dev->name); | ||
477 | return -EINVAL; | ||
478 | } | ||
479 | return 0; | ||
480 | } | ||
481 | |||
482 | int ieee80211_wx_get_encode(struct ieee80211_device *ieee, | ||
483 | struct iw_request_info *info, | ||
484 | union iwreq_data *wrqu, char *keybuf) | ||
485 | { | ||
486 | struct iw_point *erq = &(wrqu->encoding); | ||
487 | int len, key; | ||
488 | struct lib80211_crypt_data *crypt; | ||
489 | struct ieee80211_security *sec = &ieee->sec; | ||
490 | |||
491 | IEEE80211_DEBUG_WX("GET_ENCODE\n"); | ||
492 | |||
493 | key = erq->flags & IW_ENCODE_INDEX; | ||
494 | if (key) { | ||
495 | if (key > WEP_KEYS) | ||
496 | return -EINVAL; | ||
497 | key--; | ||
498 | } else | ||
499 | key = ieee->crypt_info.tx_keyidx; | ||
500 | |||
501 | crypt = ieee->crypt_info.crypt[key]; | ||
502 | erq->flags = key + 1; | ||
503 | |||
504 | if (!sec->enabled) { | ||
505 | erq->length = 0; | ||
506 | erq->flags |= IW_ENCODE_DISABLED; | ||
507 | return 0; | ||
508 | } | ||
509 | |||
510 | len = sec->key_sizes[key]; | ||
511 | memcpy(keybuf, sec->keys[key], len); | ||
512 | |||
513 | erq->length = len; | ||
514 | erq->flags |= IW_ENCODE_ENABLED; | ||
515 | |||
516 | if (ieee->open_wep) | ||
517 | erq->flags |= IW_ENCODE_OPEN; | ||
518 | else | ||
519 | erq->flags |= IW_ENCODE_RESTRICTED; | ||
520 | |||
521 | return 0; | ||
522 | } | ||
523 | |||
524 | int ieee80211_wx_set_encodeext(struct ieee80211_device *ieee, | ||
525 | struct iw_request_info *info, | ||
526 | union iwreq_data *wrqu, char *extra) | ||
527 | { | ||
528 | struct net_device *dev = ieee->dev; | ||
529 | struct iw_point *encoding = &wrqu->encoding; | ||
530 | struct iw_encode_ext *ext = (struct iw_encode_ext *)extra; | ||
531 | int i, idx, ret = 0; | ||
532 | int group_key = 0; | ||
533 | const char *alg, *module; | ||
534 | struct lib80211_crypto_ops *ops; | ||
535 | struct lib80211_crypt_data **crypt; | ||
536 | |||
537 | struct ieee80211_security sec = { | ||
538 | .flags = 0, | ||
539 | }; | ||
540 | |||
541 | idx = encoding->flags & IW_ENCODE_INDEX; | ||
542 | if (idx) { | ||
543 | if (idx < 1 || idx > WEP_KEYS) | ||
544 | return -EINVAL; | ||
545 | idx--; | ||
546 | } else | ||
547 | idx = ieee->crypt_info.tx_keyidx; | ||
548 | |||
549 | if (ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) { | ||
550 | crypt = &ieee->crypt_info.crypt[idx]; | ||
551 | group_key = 1; | ||
552 | } else { | ||
553 | /* some Cisco APs use idx>0 for unicast in dynamic WEP */ | ||
554 | if (idx != 0 && ext->alg != IW_ENCODE_ALG_WEP) | ||
555 | return -EINVAL; | ||
556 | if (ieee->iw_mode == IW_MODE_INFRA) | ||
557 | crypt = &ieee->crypt_info.crypt[idx]; | ||
558 | else | ||
559 | return -EINVAL; | ||
560 | } | ||
561 | |||
562 | sec.flags |= SEC_ENABLED | SEC_ENCRYPT; | ||
563 | if ((encoding->flags & IW_ENCODE_DISABLED) || | ||
564 | ext->alg == IW_ENCODE_ALG_NONE) { | ||
565 | if (*crypt) | ||
566 | lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt); | ||
567 | |||
568 | for (i = 0; i < WEP_KEYS; i++) | ||
569 | if (ieee->crypt_info.crypt[i] != NULL) | ||
570 | break; | ||
571 | |||
572 | if (i == WEP_KEYS) { | ||
573 | sec.enabled = 0; | ||
574 | sec.encrypt = 0; | ||
575 | sec.level = SEC_LEVEL_0; | ||
576 | sec.flags |= SEC_LEVEL; | ||
577 | } | ||
578 | goto done; | ||
579 | } | ||
580 | |||
581 | sec.enabled = 1; | ||
582 | sec.encrypt = 1; | ||
583 | |||
584 | if (group_key ? !ieee->host_mc_decrypt : | ||
585 | !(ieee->host_encrypt || ieee->host_decrypt || | ||
586 | ieee->host_encrypt_msdu)) | ||
587 | goto skip_host_crypt; | ||
588 | |||
589 | switch (ext->alg) { | ||
590 | case IW_ENCODE_ALG_WEP: | ||
591 | alg = "WEP"; | ||
592 | module = "lib80211_crypt_wep"; | ||
593 | break; | ||
594 | case IW_ENCODE_ALG_TKIP: | ||
595 | alg = "TKIP"; | ||
596 | module = "lib80211_crypt_tkip"; | ||
597 | break; | ||
598 | case IW_ENCODE_ALG_CCMP: | ||
599 | alg = "CCMP"; | ||
600 | module = "lib80211_crypt_ccmp"; | ||
601 | break; | ||
602 | default: | ||
603 | IEEE80211_DEBUG_WX("%s: unknown crypto alg %d\n", | ||
604 | dev->name, ext->alg); | ||
605 | ret = -EINVAL; | ||
606 | goto done; | ||
607 | } | ||
608 | |||
609 | ops = lib80211_get_crypto_ops(alg); | ||
610 | if (ops == NULL) { | ||
611 | request_module(module); | ||
612 | ops = lib80211_get_crypto_ops(alg); | ||
613 | } | ||
614 | if (ops == NULL) { | ||
615 | IEEE80211_DEBUG_WX("%s: unknown crypto alg %d\n", | ||
616 | dev->name, ext->alg); | ||
617 | ret = -EINVAL; | ||
618 | goto done; | ||
619 | } | ||
620 | |||
621 | if (*crypt == NULL || (*crypt)->ops != ops) { | ||
622 | struct lib80211_crypt_data *new_crypt; | ||
623 | |||
624 | lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt); | ||
625 | |||
626 | new_crypt = kzalloc(sizeof(*new_crypt), GFP_KERNEL); | ||
627 | if (new_crypt == NULL) { | ||
628 | ret = -ENOMEM; | ||
629 | goto done; | ||
630 | } | ||
631 | new_crypt->ops = ops; | ||
632 | if (new_crypt->ops && try_module_get(new_crypt->ops->owner)) | ||
633 | new_crypt->priv = new_crypt->ops->init(idx); | ||
634 | if (new_crypt->priv == NULL) { | ||
635 | kfree(new_crypt); | ||
636 | ret = -EINVAL; | ||
637 | goto done; | ||
638 | } | ||
639 | *crypt = new_crypt; | ||
640 | } | ||
641 | |||
642 | if (ext->key_len > 0 && (*crypt)->ops->set_key && | ||
643 | (*crypt)->ops->set_key(ext->key, ext->key_len, ext->rx_seq, | ||
644 | (*crypt)->priv) < 0) { | ||
645 | IEEE80211_DEBUG_WX("%s: key setting failed\n", dev->name); | ||
646 | ret = -EINVAL; | ||
647 | goto done; | ||
648 | } | ||
649 | |||
650 | skip_host_crypt: | ||
651 | if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) { | ||
652 | ieee->crypt_info.tx_keyidx = idx; | ||
653 | sec.active_key = idx; | ||
654 | sec.flags |= SEC_ACTIVE_KEY; | ||
655 | } | ||
656 | |||
657 | if (ext->alg != IW_ENCODE_ALG_NONE) { | ||
658 | memcpy(sec.keys[idx], ext->key, ext->key_len); | ||
659 | sec.key_sizes[idx] = ext->key_len; | ||
660 | sec.flags |= (1 << idx); | ||
661 | if (ext->alg == IW_ENCODE_ALG_WEP) { | ||
662 | sec.encode_alg[idx] = SEC_ALG_WEP; | ||
663 | sec.flags |= SEC_LEVEL; | ||
664 | sec.level = SEC_LEVEL_1; | ||
665 | } else if (ext->alg == IW_ENCODE_ALG_TKIP) { | ||
666 | sec.encode_alg[idx] = SEC_ALG_TKIP; | ||
667 | sec.flags |= SEC_LEVEL; | ||
668 | sec.level = SEC_LEVEL_2; | ||
669 | } else if (ext->alg == IW_ENCODE_ALG_CCMP) { | ||
670 | sec.encode_alg[idx] = SEC_ALG_CCMP; | ||
671 | sec.flags |= SEC_LEVEL; | ||
672 | sec.level = SEC_LEVEL_3; | ||
673 | } | ||
674 | /* Don't set sec level for group keys. */ | ||
675 | if (group_key) | ||
676 | sec.flags &= ~SEC_LEVEL; | ||
677 | } | ||
678 | done: | ||
679 | if (ieee->set_security) | ||
680 | ieee->set_security(ieee->dev, &sec); | ||
681 | |||
682 | /* | ||
683 | * Do not reset port if card is in Managed mode since resetting will | ||
684 | * generate new IEEE 802.11 authentication which may end up in looping | ||
685 | * with IEEE 802.1X. If your hardware requires a reset after WEP | ||
686 | * configuration (for example... Prism2), implement the reset_port in | ||
687 | * the callbacks structures used to initialize the 802.11 stack. | ||
688 | */ | ||
689 | if (ieee->reset_on_keychange && | ||
690 | ieee->iw_mode != IW_MODE_INFRA && | ||
691 | ieee->reset_port && ieee->reset_port(dev)) { | ||
692 | IEEE80211_DEBUG_WX("%s: reset_port failed\n", dev->name); | ||
693 | return -EINVAL; | ||
694 | } | ||
695 | |||
696 | return ret; | ||
697 | } | ||
698 | |||
699 | int ieee80211_wx_get_encodeext(struct ieee80211_device *ieee, | ||
700 | struct iw_request_info *info, | ||
701 | union iwreq_data *wrqu, char *extra) | ||
702 | { | ||
703 | struct iw_point *encoding = &wrqu->encoding; | ||
704 | struct iw_encode_ext *ext = (struct iw_encode_ext *)extra; | ||
705 | struct ieee80211_security *sec = &ieee->sec; | ||
706 | int idx, max_key_len; | ||
707 | |||
708 | max_key_len = encoding->length - sizeof(*ext); | ||
709 | if (max_key_len < 0) | ||
710 | return -EINVAL; | ||
711 | |||
712 | idx = encoding->flags & IW_ENCODE_INDEX; | ||
713 | if (idx) { | ||
714 | if (idx < 1 || idx > WEP_KEYS) | ||
715 | return -EINVAL; | ||
716 | idx--; | ||
717 | } else | ||
718 | idx = ieee->crypt_info.tx_keyidx; | ||
719 | |||
720 | if (!(ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) && | ||
721 | ext->alg != IW_ENCODE_ALG_WEP) | ||
722 | if (idx != 0 || ieee->iw_mode != IW_MODE_INFRA) | ||
723 | return -EINVAL; | ||
724 | |||
725 | encoding->flags = idx + 1; | ||
726 | memset(ext, 0, sizeof(*ext)); | ||
727 | |||
728 | if (!sec->enabled) { | ||
729 | ext->alg = IW_ENCODE_ALG_NONE; | ||
730 | ext->key_len = 0; | ||
731 | encoding->flags |= IW_ENCODE_DISABLED; | ||
732 | } else { | ||
733 | if (sec->encode_alg[idx] == SEC_ALG_WEP) | ||
734 | ext->alg = IW_ENCODE_ALG_WEP; | ||
735 | else if (sec->encode_alg[idx] == SEC_ALG_TKIP) | ||
736 | ext->alg = IW_ENCODE_ALG_TKIP; | ||
737 | else if (sec->encode_alg[idx] == SEC_ALG_CCMP) | ||
738 | ext->alg = IW_ENCODE_ALG_CCMP; | ||
739 | else | ||
740 | return -EINVAL; | ||
741 | |||
742 | ext->key_len = sec->key_sizes[idx]; | ||
743 | memcpy(ext->key, sec->keys[idx], ext->key_len); | ||
744 | encoding->flags |= IW_ENCODE_ENABLED; | ||
745 | if (ext->key_len && | ||
746 | (ext->alg == IW_ENCODE_ALG_TKIP || | ||
747 | ext->alg == IW_ENCODE_ALG_CCMP)) | ||
748 | ext->ext_flags |= IW_ENCODE_EXT_TX_SEQ_VALID; | ||
749 | |||
750 | } | ||
751 | |||
752 | return 0; | ||
753 | } | ||
754 | |||
755 | EXPORT_SYMBOL(ieee80211_wx_set_encodeext); | ||
756 | EXPORT_SYMBOL(ieee80211_wx_get_encodeext); | ||
757 | |||
758 | EXPORT_SYMBOL(ieee80211_wx_get_scan); | ||
759 | EXPORT_SYMBOL(ieee80211_wx_set_encode); | ||
760 | EXPORT_SYMBOL(ieee80211_wx_get_encode); | ||