2005-05-13 06:48:20 +04:00
/******************************************************************************
2005-09-21 20:58:43 +04:00
Copyright ( c ) 2004 - 2005 Intel Corporation . All rights reserved .
2005-05-13 06:48:20 +04:00
Portions of this file are based on the WEP enablement code provided by the
Host AP project hostap - drivers v0 .1 .3
Copyright ( c ) 2001 - 2002 , SSH Communications Security Corp and Jouni Malinen
2007-03-25 04:15:30 +04:00
< j @ w1 . fi >
Copyright ( c ) 2002 - 2003 , Jouni Malinen < j @ w1 . fi >
2005-05-13 06:48:20 +04:00
This program is free software ; you can redistribute it and / or modify it
under the terms of version 2 of the GNU General Public License as
published by the Free Software Foundation .
This program is distributed in the hope that it will be useful , but WITHOUT
ANY WARRANTY ; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE . See the GNU General Public License for
more details .
You should have received a copy of the GNU General Public License along with
this program ; if not , write to the Free Software Foundation , Inc . , 59
Temple Place - Suite 330 , Boston , MA 02111 - 1307 , USA .
The full GNU General Public License is included in this distribution in the
file called LICENSE .
Contact Information :
James P . Ketrenos < ipw2100 - admin @ linux . intel . com >
Intel Corporation , 5200 N . E . Elam Young Parkway , Hillsboro , OR 97124 - 6497
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2005-09-07 08:27:54 +04:00
2005-05-13 06:48:20 +04:00
# include <linux/kmod.h>
# include <linux/module.h>
2005-09-21 20:54:07 +04:00
# include <linux/jiffies.h>
2005-05-13 06:48:20 +04:00
2008-10-01 04:59:05 +04:00
# include <net/lib80211.h>
2005-09-07 08:27:54 +04:00
# include <linux/wireless.h>
2009-02-12 20:32:55 +03:00
# include "ieee80211.h"
2005-05-13 06:48:20 +04:00
static const char * ieee80211_modes [ ] = {
" ? " , " a " , " b " , " ab " , " g " , " ag " , " bg " , " abg "
} ;
2009-02-11 21:26:06 +03:00
static inline unsigned int elapsed_jiffies_msecs ( unsigned long start )
{
unsigned long end = jiffies ;
if ( end > = start )
return jiffies_to_msecs ( end - start ) ;
return jiffies_to_msecs ( end + ( MAX_JIFFY_OFFSET - start ) + 1 ) ;
}
2005-05-13 06:48:20 +04:00
# define MAX_CUSTOM_LEN 64
2006-03-04 01:21:55 +03:00
static char * ieee80211_translate_scan ( struct ieee80211_device * ieee ,
2008-06-17 05:50:49 +04:00
char * start , char * stop ,
struct ieee80211_network * network ,
struct iw_request_info * info )
2005-05-13 06:48:20 +04:00
{
char custom [ MAX_CUSTOM_LEN ] ;
char * p ;
struct iw_event iwe ;
int i , j ;
2006-04-13 13:17:26 +04:00
char * current_val ; /* For rates */
u8 rate ;
2005-05-13 06:48:20 +04:00
/* First entry *MUST* be the AP MAC address */
iwe . cmd = SIOCGIWAP ;
iwe . u . ap_addr . sa_family = ARPHRD_ETHER ;
memcpy ( iwe . u . ap_addr . sa_data , network - > bssid , ETH_ALEN ) ;
2008-06-17 05:50:49 +04:00
start = iwe_stream_add_event ( info , start , stop , & iwe , IW_EV_ADDR_LEN ) ;
2005-05-13 06:48:20 +04:00
/* Remaining entries will be displayed in the order we provide them */
/* Add the ESSID */
iwe . cmd = SIOCGIWESSID ;
iwe . u . data . flags = 1 ;
2008-10-01 01:17:26 +04:00
iwe . u . data . length = min ( network - > ssid_len , ( u8 ) 32 ) ;
start = iwe_stream_add_point ( info , start , stop ,
& iwe , network - > ssid ) ;
2005-05-13 06:48:20 +04:00
/* Add the protocol name */
iwe . cmd = SIOCGIWNAME ;
2005-09-07 08:48:31 +04:00
snprintf ( iwe . u . name , IFNAMSIZ , " IEEE 802.11%s " ,
ieee80211_modes [ network - > mode ] ) ;
2008-06-17 05:50:49 +04:00
start = iwe_stream_add_event ( info , start , stop , & iwe , IW_EV_CHAR_LEN ) ;
2005-05-13 06:48:20 +04:00
2005-09-07 08:48:31 +04:00
/* Add mode */
iwe . cmd = SIOCGIWMODE ;
if ( network - > capability & ( WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS ) ) {
2005-08-15 08:32:15 +04:00
if ( network - > capability & WLAN_CAPABILITY_ESS )
2005-05-13 06:48:20 +04:00
iwe . u . mode = IW_MODE_MASTER ;
else
iwe . u . mode = IW_MODE_ADHOC ;
2008-06-17 05:50:49 +04:00
start = iwe_stream_add_event ( info , start , stop ,
& iwe , IW_EV_UINT_LEN ) ;
2005-05-13 06:48:20 +04:00
}
2007-04-22 02:56:43 +04:00
/* Add channel and frequency */
2007-07-11 00:51:14 +04:00
/* Note : userspace automatically computes channel using iwrange */
2005-05-13 06:48:20 +04:00
iwe . cmd = SIOCGIWFREQ ;
2007-04-22 02:56:43 +04:00
iwe . u . freq . m = ieee80211_channel_to_freq ( ieee , network - > channel ) ;
iwe . u . freq . e = 6 ;
2007-07-11 00:51:14 +04:00
iwe . u . freq . i = 0 ;
2008-06-17 05:50:49 +04:00
start = iwe_stream_add_event ( info , start , stop , & iwe , IW_EV_FREQ_LEN ) ;
2007-04-22 02:56:43 +04:00
2005-05-13 06:48:20 +04:00
/* Add encryption capability */
iwe . cmd = SIOCGIWENCODE ;
if ( network - > capability & WLAN_CAPABILITY_PRIVACY )
iwe . u . data . flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY ;
else
iwe . u . data . flags = IW_ENCODE_DISABLED ;
iwe . u . data . length = 0 ;
2008-06-17 05:50:49 +04:00
start = iwe_stream_add_point ( info , start , stop ,
& iwe , network - > ssid ) ;
2005-05-13 06:48:20 +04:00
/* Add basic and extended rates */
2006-04-13 13:17:26 +04:00
/* Rate : stuffing multiple values in a single event require a bit
* more of magic - Jean II */
2008-06-17 05:50:49 +04:00
current_val = start + iwe_stream_lcp_len ( info ) ;
2006-04-13 13:17:26 +04:00
iwe . cmd = SIOCGIWRATE ;
/* Those two flags are ignored... */
iwe . u . bitrate . fixed = iwe . u . bitrate . disabled = 0 ;
2005-09-07 08:48:31 +04:00
for ( i = 0 , j = 0 ; i < network - > rates_len ; ) {
2005-05-13 06:48:20 +04:00
if ( j < network - > rates_ex_len & &
( ( network - > rates_ex [ j ] & 0x7F ) <
( network - > rates [ i ] & 0x7F ) ) )
rate = network - > rates_ex [ j + + ] & 0x7F ;
else
rate = network - > rates [ i + + ] & 0x7F ;
2006-04-13 13:17:26 +04:00
/* Bit rate given in 500 kb/s units (+ 0x80) */
iwe . u . bitrate . value = ( ( rate & 0x7f ) * 500000 ) ;
/* Add new value to event */
2008-06-17 05:50:49 +04:00
current_val = iwe_stream_add_value ( info , start , current_val ,
stop , & iwe , IW_EV_PARAM_LEN ) ;
2005-05-13 06:48:20 +04:00
}
for ( ; j < network - > rates_ex_len ; j + + ) {
rate = network - > rates_ex [ j ] & 0x7F ;
2006-04-13 13:17:26 +04:00
/* Bit rate given in 500 kb/s units (+ 0x80) */
iwe . u . bitrate . value = ( ( rate & 0x7f ) * 500000 ) ;
/* Add new value to event */
2008-06-17 05:50:49 +04:00
current_val = iwe_stream_add_value ( info , start , current_val ,
stop , & iwe , IW_EV_PARAM_LEN ) ;
2005-05-13 06:48:20 +04:00
}
2006-04-13 13:17:26 +04:00
/* Check if we added any rate */
2008-06-17 05:50:49 +04:00
if ( ( current_val - start ) > iwe_stream_lcp_len ( info ) )
2006-04-13 13:17:26 +04:00
start = current_val ;
2005-05-13 06:48:20 +04:00
/* Add quality statistics */
iwe . cmd = IWEVQUAL ;
2005-09-14 02:27:19 +04:00
iwe . u . qual . updated = IW_QUAL_QUAL_UPDATED | IW_QUAL_LEVEL_UPDATED |
IW_QUAL_NOISE_UPDATED ;
if ( ! ( network - > stats . mask & IEEE80211_STATMASK_RSSI ) ) {
iwe . u . qual . updated | = IW_QUAL_QUAL_INVALID |
IW_QUAL_LEVEL_INVALID ;
iwe . u . qual . qual = 0 ;
} else {
2005-10-10 21:16:53 +04:00
if ( ieee - > perfect_rssi = = ieee - > worst_rssi )
iwe . u . qual . qual = 100 ;
else
iwe . u . qual . qual =
( 100 *
( ieee - > perfect_rssi - ieee - > worst_rssi ) *
( ieee - > perfect_rssi - ieee - > worst_rssi ) -
( ieee - > perfect_rssi - network - > stats . rssi ) *
( 15 * ( ieee - > perfect_rssi - ieee - > worst_rssi ) +
2005-10-24 19:20:53 +04:00
62 * ( ieee - > perfect_rssi -
network - > stats . rssi ) ) ) /
( ( ieee - > perfect_rssi -
ieee - > worst_rssi ) * ( ieee - > perfect_rssi -
ieee - > worst_rssi ) ) ;
2005-09-14 02:27:19 +04:00
if ( iwe . u . qual . qual > 100 )
iwe . u . qual . qual = 100 ;
else if ( iwe . u . qual . qual < 1 )
iwe . u . qual . qual = 0 ;
}
if ( ! ( network - > stats . mask & IEEE80211_STATMASK_NOISE ) ) {
2005-05-13 06:48:20 +04:00
iwe . u . qual . updated | = IW_QUAL_NOISE_INVALID ;
2005-09-14 02:27:19 +04:00
iwe . u . qual . noise = 0 ;
} else {
iwe . u . qual . noise = network - > stats . noise ;
}
2005-05-13 06:48:20 +04:00
2006-01-19 11:21:54 +03:00
if ( ! ( network - > stats . mask & IEEE80211_STATMASK_SIGNAL ) ) {
iwe . u . qual . updated | = IW_QUAL_LEVEL_INVALID ;
iwe . u . qual . level = 0 ;
} else {
iwe . u . qual . level = network - > stats . signal ;
}
2008-06-17 05:50:49 +04:00
start = iwe_stream_add_event ( info , start , stop , & iwe , IW_EV_QUAL_LEN ) ;
2005-05-13 06:48:20 +04:00
iwe . cmd = IWEVCUSTOM ;
p = custom ;
iwe . u . data . length = p - custom ;
if ( iwe . u . data . length )
2008-06-17 05:50:49 +04:00
start = iwe_stream_add_point ( info , start , stop , & iwe , custom ) ;
2005-05-13 06:48:20 +04:00
2006-02-13 08:37:03 +03:00
memset ( & iwe , 0 , sizeof ( iwe ) ) ;
2005-09-21 20:53:43 +04:00
if ( network - > wpa_ie_len ) {
2006-02-13 08:37:03 +03:00
char buf [ MAX_WPA_IE_LEN ] ;
memcpy ( buf , network - > wpa_ie , network - > wpa_ie_len ) ;
iwe . cmd = IWEVGENIE ;
iwe . u . data . length = network - > wpa_ie_len ;
2008-06-17 05:50:49 +04:00
start = iwe_stream_add_point ( info , start , stop , & iwe , buf ) ;
2005-05-13 06:48:20 +04:00
}
2006-02-13 08:37:03 +03:00
memset ( & iwe , 0 , sizeof ( iwe ) ) ;
2005-09-21 20:53:43 +04:00
if ( network - > rsn_ie_len ) {
2006-02-13 08:37:03 +03:00
char buf [ MAX_WPA_IE_LEN ] ;
memcpy ( buf , network - > rsn_ie , network - > rsn_ie_len ) ;
iwe . cmd = IWEVGENIE ;
iwe . u . data . length = network - > rsn_ie_len ;
2008-06-17 05:50:49 +04:00
start = iwe_stream_add_point ( info , start , stop , & iwe , buf ) ;
2005-05-13 06:48:20 +04:00
}
/* Add EXTRA: Age to display seconds since last beacon/probe response
* for given network . */
iwe . cmd = IWEVCUSTOM ;
p = custom ;
p + = snprintf ( p , MAX_CUSTOM_LEN - ( p - custom ) ,
2009-02-11 21:26:06 +03:00
" Last beacon: %ums ago " ,
elapsed_jiffies_msecs ( network - > last_scanned ) ) ;
2005-05-13 06:48:20 +04:00
iwe . u . data . length = p - custom ;
if ( iwe . u . data . length )
2008-06-17 05:50:49 +04:00
start = iwe_stream_add_point ( info , start , stop , & iwe , custom ) ;
2005-05-13 06:48:20 +04:00
2006-01-19 11:21:54 +03:00
/* Add spectrum management information */
iwe . cmd = - 1 ;
p = custom ;
p + = snprintf ( p , MAX_CUSTOM_LEN - ( p - custom ) , " Channel flags: " ) ;
if ( ieee80211_get_channel_flags ( ieee , network - > channel ) &
IEEE80211_CH_INVALID ) {
iwe . cmd = IWEVCUSTOM ;
p + = snprintf ( p , MAX_CUSTOM_LEN - ( p - custom ) , " INVALID " ) ;
}
if ( ieee80211_get_channel_flags ( ieee , network - > channel ) &
IEEE80211_CH_RADAR_DETECT ) {
iwe . cmd = IWEVCUSTOM ;
p + = snprintf ( p , MAX_CUSTOM_LEN - ( p - custom ) , " DFS " ) ;
}
if ( iwe . cmd = = IWEVCUSTOM ) {
iwe . u . data . length = p - custom ;
2008-06-17 05:50:49 +04:00
start = iwe_stream_add_point ( info , start , stop , & iwe , custom ) ;
2006-01-19 11:21:54 +03:00
}
2005-05-13 06:48:20 +04:00
return start ;
}
2006-01-19 11:20:59 +03:00
# define SCAN_ITEM_SIZE 128
2005-05-13 06:48:20 +04:00
int ieee80211_wx_get_scan ( struct ieee80211_device * ieee ,
struct iw_request_info * info ,
union iwreq_data * wrqu , char * extra )
{
struct ieee80211_network * network ;
unsigned long flags ;
2006-01-19 11:20:59 +03:00
int err = 0 ;
2005-05-13 06:48:20 +04:00
char * ev = extra ;
2006-01-19 11:20:59 +03:00
char * stop = ev + wrqu - > data . length ;
2005-05-13 06:48:20 +04:00
int i = 0 ;
2008-10-01 04:59:05 +04:00
DECLARE_SSID_BUF ( ssid ) ;
2005-05-13 06:48:20 +04:00
IEEE80211_DEBUG_WX ( " Getting scan \n " ) ;
spin_lock_irqsave ( & ieee - > lock , flags ) ;
list_for_each_entry ( network , & ieee - > network_list , list ) {
i + + ;
2006-01-19 11:20:59 +03:00
if ( stop - ev < SCAN_ITEM_SIZE ) {
err = - E2BIG ;
break ;
}
2005-05-13 06:48:20 +04:00
if ( ieee - > scan_age = = 0 | |
time_after ( network - > last_scanned + ieee - > scan_age , jiffies ) )
2008-06-17 05:50:49 +04:00
ev = ieee80211_translate_scan ( ieee , ev , stop , network ,
info ) ;
2009-02-11 21:26:06 +03:00
else {
2005-09-07 08:48:31 +04:00
IEEE80211_DEBUG_SCAN ( " Not showing network '%s ( "
2009-02-11 21:26:06 +03:00
" %pM)' due to age (%ums). \n " ,
2008-10-01 04:59:05 +04:00
print_ssid ( ssid , network - > ssid ,
2008-09-25 02:13:14 +04:00
network - > ssid_len ) ,
2008-10-28 01:59:26 +03:00
network - > bssid ,
2009-02-11 21:26:06 +03:00
elapsed_jiffies_msecs (
network - > last_scanned ) ) ;
}
2005-05-13 06:48:20 +04:00
}
spin_unlock_irqrestore ( & ieee - > lock , flags ) ;
2005-09-07 08:48:31 +04:00
wrqu - > data . length = ev - extra ;
2005-05-13 06:48:20 +04:00
wrqu - > data . flags = 0 ;
IEEE80211_DEBUG_WX ( " exit: %d networks returned. \n " , i ) ;
2006-01-19 11:20:59 +03:00
return err ;
2005-05-13 06:48:20 +04:00
}
int ieee80211_wx_set_encode ( struct ieee80211_device * ieee ,
struct iw_request_info * info ,
union iwreq_data * wrqu , char * keybuf )
{
struct iw_point * erq = & ( wrqu - > encoding ) ;
struct net_device * dev = ieee - > dev ;
struct ieee80211_security sec = {
. flags = 0
} ;
int i , key , key_provided , len ;
2008-10-29 18:35:05 +03:00
struct lib80211_crypt_data * * crypt ;
2005-12-31 13:35:20 +03:00
int host_crypto = ieee - > host_encrypt | | ieee - > host_decrypt | | ieee - > host_build_iv ;
2008-10-01 04:59:05 +04:00
DECLARE_SSID_BUF ( ssid ) ;
2005-05-13 06:48:20 +04:00
IEEE80211_DEBUG_WX ( " SET_ENCODE \n " ) ;
key = erq - > flags & IW_ENCODE_INDEX ;
if ( key ) {
if ( key > WEP_KEYS )
return - EINVAL ;
key - - ;
key_provided = 1 ;
} else {
key_provided = 0 ;
2008-10-29 18:35:05 +03:00
key = ieee - > crypt_info . tx_keyidx ;
2005-05-13 06:48:20 +04:00
}
IEEE80211_DEBUG_WX ( " Key: %d [%s] \n " , key , key_provided ?
" provided " : " default " ) ;
2008-10-29 18:35:05 +03:00
crypt = & ieee - > crypt_info . crypt [ key ] ;
2005-05-13 06:48:20 +04:00
if ( erq - > flags & IW_ENCODE_DISABLED ) {
if ( key_provided & & * crypt ) {
IEEE80211_DEBUG_WX ( " Disabling encryption on key %d. \n " ,
key ) ;
2008-10-29 18:35:05 +03:00
lib80211_crypt_delayed_deinit ( & ieee - > crypt_info , crypt ) ;
2005-05-13 06:48:20 +04:00
} else
IEEE80211_DEBUG_WX ( " Disabling encryption. \n " ) ;
/* Check all the keys to see if any are still configured,
* and if no key index was provided , de - init them all */
for ( i = 0 ; i < WEP_KEYS ; i + + ) {
2008-10-29 18:35:05 +03:00
if ( ieee - > crypt_info . crypt [ i ] ! = NULL ) {
2005-05-13 06:48:20 +04:00
if ( key_provided )
break ;
2008-10-29 18:35:05 +03:00
lib80211_crypt_delayed_deinit ( & ieee - > crypt_info ,
& ieee - > crypt_info . crypt [ i ] ) ;
2005-05-13 06:48:20 +04:00
}
}
if ( i = = WEP_KEYS ) {
sec . enabled = 0 ;
2005-09-21 20:53:54 +04:00
sec . encrypt = 0 ;
2005-05-13 06:48:20 +04:00
sec . level = SEC_LEVEL_0 ;
2005-09-21 20:54:22 +04:00
sec . flags | = SEC_ENABLED | SEC_LEVEL | SEC_ENCRYPT ;
2005-05-13 06:48:20 +04:00
}
goto done ;
}
sec . enabled = 1 ;
2005-09-21 20:53:54 +04:00
sec . encrypt = 1 ;
2005-09-21 20:54:22 +04:00
sec . flags | = SEC_ENABLED | SEC_ENCRYPT ;
2005-05-13 06:48:20 +04:00
if ( * crypt ! = NULL & & ( * crypt ) - > ops ! = NULL & &
strcmp ( ( * crypt ) - > ops - > name , " WEP " ) ! = 0 ) {
/* changing to use WEP; deinit previously used algorithm
* on this key */
2008-10-29 18:35:05 +03:00
lib80211_crypt_delayed_deinit ( & ieee - > crypt_info , crypt ) ;
2005-05-13 06:48:20 +04:00
}
2005-09-21 20:53:54 +04:00
if ( * crypt = = NULL & & host_crypto ) {
2008-10-29 18:35:05 +03:00
struct lib80211_crypt_data * new_crypt ;
2005-05-13 06:48:20 +04:00
/* take WEP into use */
2008-10-29 18:35:05 +03:00
new_crypt = kzalloc ( sizeof ( struct lib80211_crypt_data ) ,
2005-05-13 06:48:20 +04:00
GFP_KERNEL ) ;
if ( new_crypt = = NULL )
return - ENOMEM ;
2008-10-29 18:35:05 +03:00
new_crypt - > ops = lib80211_get_crypto_ops ( " WEP " ) ;
2005-05-13 06:48:20 +04:00
if ( ! new_crypt - > ops ) {
2008-10-29 18:35:05 +03:00
request_module ( " lib80211_crypt_wep " ) ;
new_crypt - > ops = lib80211_get_crypto_ops ( " WEP " ) ;
2005-05-13 06:48:20 +04:00
}
if ( new_crypt - > ops & & try_module_get ( new_crypt - > ops - > owner ) )
2005-09-22 14:34:15 +04:00
new_crypt - > priv = new_crypt - > ops - > init ( key ) ;
2005-05-13 06:48:20 +04:00
if ( ! new_crypt - > ops | | ! new_crypt - > priv ) {
kfree ( new_crypt ) ;
new_crypt = NULL ;
printk ( KERN_WARNING " %s: could not initialize WEP: "
2008-10-29 18:35:05 +03:00
" load module lib80211_crypt_wep \n " , dev - > name ) ;
2005-05-13 06:48:20 +04:00
return - EOPNOTSUPP ;
}
* crypt = new_crypt ;
}
/* If a new key was provided, set it up */
if ( erq - > length > 0 ) {
2008-10-29 11:43:50 +03:00
# ifdef CONFIG_IEEE80211_DEBUG
DECLARE_SSID_BUF ( ssid ) ;
# endif
2005-05-13 06:48:20 +04:00
len = erq - > length < = 5 ? 5 : 13 ;
memcpy ( sec . keys [ key ] , keybuf , erq - > length ) ;
if ( len > erq - > length )
memset ( sec . keys [ key ] + erq - > length , 0 ,
len - erq - > length ) ;
IEEE80211_DEBUG_WX ( " Setting key %d to '%s' (%d:%d bytes) \n " ,
2008-10-01 04:59:05 +04:00
key , print_ssid ( ssid , sec . keys [ key ] , len ) ,
2005-05-13 06:48:20 +04:00
erq - > length , len ) ;
sec . key_sizes [ key ] = len ;
2005-09-21 20:53:54 +04:00
if ( * crypt )
( * crypt ) - > ops - > set_key ( sec . keys [ key ] , len , NULL ,
( * crypt ) - > priv ) ;
2005-05-13 06:48:20 +04:00
sec . flags | = ( 1 < < key ) ;
/* This ensures a key will be activated if no key is
2007-10-20 01:22:55 +04:00
* explicitly set */
2005-05-13 06:48:20 +04:00
if ( key = = sec . active_key )
sec . flags | = SEC_ACTIVE_KEY ;
2005-09-21 20:53:54 +04:00
2005-05-13 06:48:20 +04:00
} else {
2005-09-21 20:53:54 +04:00
if ( host_crypto ) {
len = ( * crypt ) - > ops - > get_key ( sec . keys [ key ] , WEP_KEY_LEN ,
NULL , ( * crypt ) - > priv ) ;
if ( len = = 0 ) {
/* Set a default key of all 0 */
IEEE80211_DEBUG_WX ( " Setting key %d to all "
" zero. \n " , key ) ;
memset ( sec . keys [ key ] , 0 , 13 ) ;
( * crypt ) - > ops - > set_key ( sec . keys [ key ] , 13 , NULL ,
( * crypt ) - > priv ) ;
sec . key_sizes [ key ] = 13 ;
sec . flags | = ( 1 < < key ) ;
}
2005-05-13 06:48:20 +04:00
}
/* No key data - just set the default TX key index */
if ( key_provided ) {
2005-09-21 20:53:54 +04:00
IEEE80211_DEBUG_WX ( " Setting key %d to default Tx "
" key. \n " , key ) ;
2008-10-29 18:35:05 +03:00
ieee - > crypt_info . tx_keyidx = key ;
2005-05-13 06:48:20 +04:00
sec . active_key = key ;
sec . flags | = SEC_ACTIVE_KEY ;
}
}
2005-09-21 20:58:38 +04:00
if ( erq - > flags & ( IW_ENCODE_OPEN | IW_ENCODE_RESTRICTED ) ) {
ieee - > open_wep = ! ( erq - > flags & IW_ENCODE_RESTRICTED ) ;
sec . auth_mode = ieee - > open_wep ? WLAN_AUTH_OPEN :
WLAN_AUTH_SHARED_KEY ;
sec . flags | = SEC_AUTH_MODE ;
IEEE80211_DEBUG_WX ( " Auth: %s \n " ,
sec . auth_mode = = WLAN_AUTH_OPEN ?
" OPEN " : " SHARED KEY " ) ;
}
2005-05-13 06:48:20 +04:00
/* For now we just support WEP, so only set that security level...
* TODO : When WPA is added this is one place that needs to change */
sec . flags | = SEC_LEVEL ;
2005-09-07 08:48:31 +04:00
sec . level = SEC_LEVEL_1 ; /* 40 and 104 bit WEP */
2005-09-21 20:54:30 +04:00
sec . encode_alg [ key ] = SEC_ALG_WEP ;
2005-05-13 06:48:20 +04:00
2005-09-21 20:54:22 +04:00
done :
2005-05-13 06:48:20 +04:00
if ( ieee - > set_security )
ieee - > set_security ( dev , & sec ) ;
/* Do not reset port if card is in Managed mode since resetting will
* generate new IEEE 802.11 authentication which may end up in looping
* with IEEE 802.1 X . If your hardware requires a reset after WEP
* configuration ( for example . . . Prism2 ) , implement the reset_port in
* the callbacks structures used to initialize the 802.11 stack . */
if ( ieee - > reset_on_keychange & &
ieee - > iw_mode ! = IW_MODE_INFRA & &
ieee - > reset_port & & ieee - > reset_port ( dev ) ) {
printk ( KERN_DEBUG " %s: reset_port failed \n " , dev - > name ) ;
return - EINVAL ;
}
return 0 ;
}
int ieee80211_wx_get_encode ( struct ieee80211_device * ieee ,
struct iw_request_info * info ,
union iwreq_data * wrqu , char * keybuf )
{
struct iw_point * erq = & ( wrqu - > encoding ) ;
int len , key ;
2008-10-29 18:35:05 +03:00
struct lib80211_crypt_data * crypt ;
2005-09-21 20:53:54 +04:00
struct ieee80211_security * sec = & ieee - > sec ;
2005-05-13 06:48:20 +04:00
IEEE80211_DEBUG_WX ( " GET_ENCODE \n " ) ;
key = erq - > flags & IW_ENCODE_INDEX ;
if ( key ) {
if ( key > WEP_KEYS )
return - EINVAL ;
key - - ;
} else
2008-10-29 18:35:05 +03:00
key = ieee - > crypt_info . tx_keyidx ;
2005-05-13 06:48:20 +04:00
2008-10-29 18:35:05 +03:00
crypt = ieee - > crypt_info . crypt [ key ] ;
2005-05-13 06:48:20 +04:00
erq - > flags = key + 1 ;
2005-09-21 20:53:54 +04:00
if ( ! sec - > enabled ) {
2005-05-13 06:48:20 +04:00
erq - > length = 0 ;
erq - > flags | = IW_ENCODE_DISABLED ;
return 0 ;
}
2005-09-21 20:53:54 +04:00
len = sec - > key_sizes [ key ] ;
memcpy ( keybuf , sec - > keys [ key ] , len ) ;
2005-05-13 06:48:20 +04:00
2006-04-27 13:33:42 +04:00
erq - > length = len ;
2005-05-13 06:48:20 +04:00
erq - > flags | = IW_ENCODE_ENABLED ;
if ( ieee - > open_wep )
erq - > flags | = IW_ENCODE_OPEN ;
else
erq - > flags | = IW_ENCODE_RESTRICTED ;
return 0 ;
}
2005-09-21 20:54:30 +04:00
int ieee80211_wx_set_encodeext ( struct ieee80211_device * ieee ,
struct iw_request_info * info ,
union iwreq_data * wrqu , char * extra )
{
struct net_device * dev = ieee - > dev ;
struct iw_point * encoding = & wrqu - > encoding ;
struct iw_encode_ext * ext = ( struct iw_encode_ext * ) extra ;
int i , idx , ret = 0 ;
2005-09-21 20:58:32 +04:00
int group_key = 0 ;
2005-09-21 20:54:30 +04:00
const char * alg , * module ;
2008-10-29 18:35:05 +03:00
struct lib80211_crypto_ops * ops ;
struct lib80211_crypt_data * * crypt ;
2005-09-21 20:54:30 +04:00
struct ieee80211_security sec = {
. flags = 0 ,
} ;
idx = encoding - > flags & IW_ENCODE_INDEX ;
if ( idx ) {
if ( idx < 1 | | idx > WEP_KEYS )
return - EINVAL ;
idx - - ;
} else
2008-10-29 18:35:05 +03:00
idx = ieee - > crypt_info . tx_keyidx ;
2005-09-21 20:54:30 +04:00
2005-09-21 20:58:32 +04:00
if ( ext - > ext_flags & IW_ENCODE_EXT_GROUP_KEY ) {
2008-10-29 18:35:05 +03:00
crypt = & ieee - > crypt_info . crypt [ idx ] ;
2005-09-21 20:58:32 +04:00
group_key = 1 ;
} else {
2005-10-24 19:15:36 +04:00
/* some Cisco APs use idx>0 for unicast in dynamic WEP */
if ( idx ! = 0 & & ext - > alg ! = IW_ENCODE_ALG_WEP )
2005-09-21 20:54:30 +04:00
return - EINVAL ;
if ( ieee - > iw_mode = = IW_MODE_INFRA )
2008-10-29 18:35:05 +03:00
crypt = & ieee - > crypt_info . crypt [ idx ] ;
2005-09-21 20:54:30 +04:00
else
return - EINVAL ;
}
sec . flags | = SEC_ENABLED | SEC_ENCRYPT ;
if ( ( encoding - > flags & IW_ENCODE_DISABLED ) | |
ext - > alg = = IW_ENCODE_ALG_NONE ) {
if ( * crypt )
2008-10-29 18:35:05 +03:00
lib80211_crypt_delayed_deinit ( & ieee - > crypt_info , crypt ) ;
2005-09-21 20:54:30 +04:00
for ( i = 0 ; i < WEP_KEYS ; i + + )
2008-10-29 18:35:05 +03:00
if ( ieee - > crypt_info . crypt [ i ] ! = NULL )
2005-09-21 20:54:30 +04:00
break ;
if ( i = = WEP_KEYS ) {
sec . enabled = 0 ;
sec . encrypt = 0 ;
sec . level = SEC_LEVEL_0 ;
sec . flags | = SEC_LEVEL ;
}
goto done ;
}
sec . enabled = 1 ;
sec . encrypt = 1 ;
2005-09-21 20:58:32 +04:00
if ( group_key ? ! ieee - > host_mc_decrypt :
! ( ieee - > host_encrypt | | ieee - > host_decrypt | |
ieee - > host_encrypt_msdu ) )
2005-09-21 20:54:30 +04:00
goto skip_host_crypt ;
switch ( ext - > alg ) {
case IW_ENCODE_ALG_WEP :
alg = " WEP " ;
2008-10-29 18:35:05 +03:00
module = " lib80211_crypt_wep " ;
2005-09-21 20:54:30 +04:00
break ;
case IW_ENCODE_ALG_TKIP :
alg = " TKIP " ;
2008-10-29 18:35:05 +03:00
module = " lib80211_crypt_tkip " ;
2005-09-21 20:54:30 +04:00
break ;
case IW_ENCODE_ALG_CCMP :
alg = " CCMP " ;
2008-10-29 18:35:05 +03:00
module = " lib80211_crypt_ccmp " ;
2005-09-21 20:54:30 +04:00
break ;
default :
IEEE80211_DEBUG_WX ( " %s: unknown crypto alg %d \n " ,
dev - > name , ext - > alg ) ;
ret = - EINVAL ;
goto done ;
}
2008-10-29 18:35:05 +03:00
ops = lib80211_get_crypto_ops ( alg ) ;
2005-09-21 20:54:30 +04:00
if ( ops = = NULL ) {
request_module ( module ) ;
2008-10-29 18:35:05 +03:00
ops = lib80211_get_crypto_ops ( alg ) ;
2005-09-21 20:54:30 +04:00
}
if ( ops = = NULL ) {
IEEE80211_DEBUG_WX ( " %s: unknown crypto alg %d \n " ,
dev - > name , ext - > alg ) ;
ret = - EINVAL ;
goto done ;
}
if ( * crypt = = NULL | | ( * crypt ) - > ops ! = ops ) {
2008-10-29 18:35:05 +03:00
struct lib80211_crypt_data * new_crypt ;
2005-09-21 20:54:30 +04:00
2008-10-29 18:35:05 +03:00
lib80211_crypt_delayed_deinit ( & ieee - > crypt_info , crypt ) ;
2005-09-21 20:54:30 +04:00
2006-07-22 01:51:30 +04:00
new_crypt = kzalloc ( sizeof ( * new_crypt ) , GFP_KERNEL ) ;
2005-09-21 20:54:30 +04:00
if ( new_crypt = = NULL ) {
ret = - ENOMEM ;
goto done ;
}
new_crypt - > ops = ops ;
if ( new_crypt - > ops & & try_module_get ( new_crypt - > ops - > owner ) )
2005-09-22 14:34:15 +04:00
new_crypt - > priv = new_crypt - > ops - > init ( idx ) ;
2005-09-21 20:54:30 +04:00
if ( new_crypt - > priv = = NULL ) {
kfree ( new_crypt ) ;
ret = - EINVAL ;
goto done ;
}
* crypt = new_crypt ;
}
if ( ext - > key_len > 0 & & ( * crypt ) - > ops - > set_key & &
( * crypt ) - > ops - > set_key ( ext - > key , ext - > key_len , ext - > rx_seq ,
( * crypt ) - > priv ) < 0 ) {
IEEE80211_DEBUG_WX ( " %s: key setting failed \n " , dev - > name ) ;
ret = - EINVAL ;
goto done ;
}
skip_host_crypt :
if ( ext - > ext_flags & IW_ENCODE_EXT_SET_TX_KEY ) {
2008-10-29 18:35:05 +03:00
ieee - > crypt_info . tx_keyidx = idx ;
2005-09-21 20:54:30 +04:00
sec . active_key = idx ;
sec . flags | = SEC_ACTIVE_KEY ;
}
if ( ext - > alg ! = IW_ENCODE_ALG_NONE ) {
memcpy ( sec . keys [ idx ] , ext - > key , ext - > key_len ) ;
sec . key_sizes [ idx ] = ext - > key_len ;
sec . flags | = ( 1 < < idx ) ;
if ( ext - > alg = = IW_ENCODE_ALG_WEP ) {
sec . encode_alg [ idx ] = SEC_ALG_WEP ;
sec . flags | = SEC_LEVEL ;
sec . level = SEC_LEVEL_1 ;
} else if ( ext - > alg = = IW_ENCODE_ALG_TKIP ) {
sec . encode_alg [ idx ] = SEC_ALG_TKIP ;
sec . flags | = SEC_LEVEL ;
sec . level = SEC_LEVEL_2 ;
} else if ( ext - > alg = = IW_ENCODE_ALG_CCMP ) {
sec . encode_alg [ idx ] = SEC_ALG_CCMP ;
sec . flags | = SEC_LEVEL ;
sec . level = SEC_LEVEL_3 ;
}
2005-09-21 20:58:32 +04:00
/* Don't set sec level for group keys. */
if ( group_key )
sec . flags & = ~ SEC_LEVEL ;
2005-09-21 20:54:30 +04:00
}
done :
if ( ieee - > set_security )
ieee - > set_security ( ieee - > dev , & sec ) ;
/*
* Do not reset port if card is in Managed mode since resetting will
* generate new IEEE 802.11 authentication which may end up in looping
* with IEEE 802.1 X . If your hardware requires a reset after WEP
* configuration ( for example . . . Prism2 ) , implement the reset_port in
* the callbacks structures used to initialize the 802.11 stack .
*/
if ( ieee - > reset_on_keychange & &
ieee - > iw_mode ! = IW_MODE_INFRA & &
ieee - > reset_port & & ieee - > reset_port ( dev ) ) {
IEEE80211_DEBUG_WX ( " %s: reset_port failed \n " , dev - > name ) ;
return - EINVAL ;
}
return ret ;
}
int ieee80211_wx_get_encodeext ( struct ieee80211_device * ieee ,
struct iw_request_info * info ,
union iwreq_data * wrqu , char * extra )
{
struct iw_point * encoding = & wrqu - > encoding ;
struct iw_encode_ext * ext = ( struct iw_encode_ext * ) extra ;
struct ieee80211_security * sec = & ieee - > sec ;
int idx , max_key_len ;
max_key_len = encoding - > length - sizeof ( * ext ) ;
if ( max_key_len < 0 )
return - EINVAL ;
idx = encoding - > flags & IW_ENCODE_INDEX ;
if ( idx ) {
if ( idx < 1 | | idx > WEP_KEYS )
return - EINVAL ;
idx - - ;
} else
2008-10-29 18:35:05 +03:00
idx = ieee - > crypt_info . tx_keyidx ;
2005-09-21 20:54:30 +04:00
2007-10-26 23:51:26 +04:00
if ( ! ( ext - > ext_flags & IW_ENCODE_EXT_GROUP_KEY ) & &
2005-10-24 19:15:36 +04:00
ext - > alg ! = IW_ENCODE_ALG_WEP )
2005-09-21 20:54:30 +04:00
if ( idx ! = 0 | | ieee - > iw_mode ! = IW_MODE_INFRA )
return - EINVAL ;
encoding - > flags = idx + 1 ;
memset ( ext , 0 , sizeof ( * ext ) ) ;
if ( ! sec - > enabled ) {
ext - > alg = IW_ENCODE_ALG_NONE ;
ext - > key_len = 0 ;
encoding - > flags | = IW_ENCODE_DISABLED ;
} else {
if ( sec - > encode_alg [ idx ] = = SEC_ALG_WEP )
ext - > alg = IW_ENCODE_ALG_WEP ;
else if ( sec - > encode_alg [ idx ] = = SEC_ALG_TKIP )
ext - > alg = IW_ENCODE_ALG_TKIP ;
else if ( sec - > encode_alg [ idx ] = = SEC_ALG_CCMP )
ext - > alg = IW_ENCODE_ALG_CCMP ;
else
return - EINVAL ;
ext - > key_len = sec - > key_sizes [ idx ] ;
memcpy ( ext - > key , sec - > keys [ idx ] , ext - > key_len ) ;
encoding - > flags | = IW_ENCODE_ENABLED ;
if ( ext - > key_len & &
( ext - > alg = = IW_ENCODE_ALG_TKIP | |
ext - > alg = = IW_ENCODE_ALG_CCMP ) )
ext - > ext_flags | = IW_ENCODE_EXT_TX_SEQ_VALID ;
}
return 0 ;
}
EXPORT_SYMBOL ( ieee80211_wx_set_encodeext ) ;
EXPORT_SYMBOL ( ieee80211_wx_get_encodeext ) ;
2005-05-13 06:48:20 +04:00
EXPORT_SYMBOL ( ieee80211_wx_get_scan ) ;
EXPORT_SYMBOL ( ieee80211_wx_set_encode ) ;
EXPORT_SYMBOL ( ieee80211_wx_get_encode ) ;