2005-05-13 06:48:20 +04:00
/*
* Original code based Host AP ( software wireless LAN access point ) driver
* for Intersil Prism2 / 2.5 / 3 - hostap . o module , common routines
*
* Copyright ( c ) 2001 - 2002 , SSH Communications Security Corp and Jouni Malinen
* < jkmaline @ cc . hut . fi >
* Copyright ( c ) 2002 - 2003 , Jouni Malinen < jkmaline @ cc . hut . fi >
* Copyright ( c ) 2004 , Intel Corporation
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation . See README and COPYING for
* more details .
*/
# include <linux/compiler.h>
# include <linux/config.h>
# include <linux/errno.h>
# include <linux/if_arp.h>
# include <linux/in6.h>
# include <linux/in.h>
# include <linux/ip.h>
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/netdevice.h>
# include <linux/proc_fs.h>
# include <linux/skbuff.h>
# include <linux/slab.h>
# include <linux/tcp.h>
# include <linux/types.h>
# include <linux/version.h>
# include <linux/wireless.h>
# include <linux/etherdevice.h>
# include <asm/uaccess.h>
# include <linux/ctype.h>
# include <net/ieee80211.h>
static inline void ieee80211_monitor_rx ( struct ieee80211_device * ieee ,
struct sk_buff * skb ,
struct ieee80211_rx_stats * rx_stats )
{
struct ieee80211_hdr * hdr = ( struct ieee80211_hdr * ) skb - > data ;
u16 fc = le16_to_cpu ( hdr - > frame_ctl ) ;
skb - > dev = ieee - > dev ;
skb - > mac . raw = skb - > data ;
skb_pull ( skb , ieee80211_get_hdrlen ( fc ) ) ;
skb - > pkt_type = PACKET_OTHERHOST ;
skb - > protocol = __constant_htons ( ETH_P_80211_RAW ) ;
memset ( skb - > cb , 0 , sizeof ( skb - > cb ) ) ;
netif_rx ( skb ) ;
}
/* Called only as a tasklet (software IRQ) */
2005-09-07 08:48:31 +04:00
static struct ieee80211_frag_entry * ieee80211_frag_cache_find ( struct
ieee80211_device
* ieee ,
unsigned int seq ,
unsigned int frag ,
u8 * src ,
u8 * dst )
2005-05-13 06:48:20 +04:00
{
struct ieee80211_frag_entry * entry ;
int i ;
for ( i = 0 ; i < IEEE80211_FRAG_CACHE_LEN ; i + + ) {
entry = & ieee - > frag_cache [ i ] ;
if ( entry - > skb ! = NULL & &
time_after ( jiffies , entry - > first_frag_time + 2 * HZ ) ) {
2005-09-07 08:48:31 +04:00
IEEE80211_DEBUG_FRAG ( " expiring fragment cache entry "
" seq=%u last_frag=%u \n " ,
entry - > seq , entry - > last_frag ) ;
2005-05-13 06:48:20 +04:00
dev_kfree_skb_any ( entry - > skb ) ;
entry - > skb = NULL ;
}
if ( entry - > skb ! = NULL & & entry - > seq = = seq & &
( entry - > last_frag + 1 = = frag | | frag = = - 1 ) & &
memcmp ( entry - > src_addr , src , ETH_ALEN ) = = 0 & &
memcmp ( entry - > dst_addr , dst , ETH_ALEN ) = = 0 )
return entry ;
}
return NULL ;
}
/* Called only as a tasklet (software IRQ) */
2005-09-07 08:48:31 +04:00
static struct sk_buff * ieee80211_frag_cache_get ( struct ieee80211_device * ieee ,
2005-09-21 20:54:36 +04:00
struct ieee80211_hdr_4addr * hdr )
2005-05-13 06:48:20 +04:00
{
struct sk_buff * skb = NULL ;
u16 sc ;
unsigned int frag , seq ;
struct ieee80211_frag_entry * entry ;
sc = le16_to_cpu ( hdr - > seq_ctl ) ;
frag = WLAN_GET_SEQ_FRAG ( sc ) ;
seq = WLAN_GET_SEQ_SEQ ( sc ) ;
if ( frag = = 0 ) {
/* Reserve enough space to fit maximum frame length */
skb = dev_alloc_skb ( ieee - > dev - > mtu +
2005-09-21 20:54:36 +04:00
sizeof ( struct ieee80211_hdr_4addr ) +
2005-09-07 08:48:31 +04:00
8 /* LLC */ +
2 /* alignment */ +
8 /* WEP */ + ETH_ALEN /* WDS */ ) ;
2005-05-13 06:48:20 +04:00
if ( skb = = NULL )
return NULL ;
entry = & ieee - > frag_cache [ ieee - > frag_next_idx ] ;
ieee - > frag_next_idx + + ;
if ( ieee - > frag_next_idx > = IEEE80211_FRAG_CACHE_LEN )
ieee - > frag_next_idx = 0 ;
if ( entry - > skb ! = NULL )
dev_kfree_skb_any ( entry - > skb ) ;
entry - > first_frag_time = jiffies ;
entry - > seq = seq ;
entry - > last_frag = frag ;
entry - > skb = skb ;
memcpy ( entry - > src_addr , hdr - > addr2 , ETH_ALEN ) ;
memcpy ( entry - > dst_addr , hdr - > addr1 , ETH_ALEN ) ;
} else {
/* received a fragment of a frame for which the head fragment
* should have already been received */
entry = ieee80211_frag_cache_find ( ieee , seq , frag , hdr - > addr2 ,
hdr - > addr1 ) ;
if ( entry ! = NULL ) {
entry - > last_frag = frag ;
skb = entry - > skb ;
}
}
return skb ;
}
/* Called only as a tasklet (software IRQ) */
static int ieee80211_frag_cache_invalidate ( struct ieee80211_device * ieee ,
2005-09-21 20:54:36 +04:00
struct ieee80211_hdr_4addr * hdr )
2005-05-13 06:48:20 +04:00
{
u16 sc ;
unsigned int seq ;
struct ieee80211_frag_entry * entry ;
sc = le16_to_cpu ( hdr - > seq_ctl ) ;
seq = WLAN_GET_SEQ_SEQ ( sc ) ;
entry = ieee80211_frag_cache_find ( ieee , seq , - 1 , hdr - > addr2 ,
hdr - > addr1 ) ;
if ( entry = = NULL ) {
2005-09-07 08:48:31 +04:00
IEEE80211_DEBUG_FRAG ( " could not invalidate fragment cache "
" entry (seq=%u) \n " , seq ) ;
2005-05-13 06:48:20 +04:00
return - 1 ;
}
entry - > skb = NULL ;
return 0 ;
}
# ifdef NOT_YET
/* ieee80211_rx_frame_mgtmt
*
* Responsible for handling management control frames
*
* Called by ieee80211_rx */
static inline int
ieee80211_rx_frame_mgmt ( struct ieee80211_device * ieee , struct sk_buff * skb ,
struct ieee80211_rx_stats * rx_stats , u16 type ,
u16 stype )
{
if ( ieee - > iw_mode = = IW_MODE_MASTER ) {
printk ( KERN_DEBUG " %s: Master mode not yet suppported. \n " ,
ieee - > dev - > name ) ;
return 0 ;
/*
2005-09-21 20:54:36 +04:00
hostap_update_sta_ps ( ieee , ( struct hostap_ieee80211_hdr_4addr * )
2005-05-13 06:48:20 +04:00
skb - > data ) ; */
}
if ( ieee - > hostapd & & type = = WLAN_FC_TYPE_MGMT ) {
if ( stype = = WLAN_FC_STYPE_BEACON & &
ieee - > iw_mode = = IW_MODE_MASTER ) {
struct sk_buff * skb2 ;
/* Process beacon frames also in kernel driver to
* update STA ( AP ) table statistics */
skb2 = skb_clone ( skb , GFP_ATOMIC ) ;
if ( skb2 )
hostap_rx ( skb2 - > dev , skb2 , rx_stats ) ;
}
/* send management frames to the user space daemon for
* processing */
ieee - > apdevstats . rx_packets + + ;
ieee - > apdevstats . rx_bytes + = skb - > len ;
prism2_rx_80211 ( ieee - > apdev , skb , rx_stats , PRISM2_RX_MGMT ) ;
return 0 ;
}
2005-09-07 08:48:31 +04:00
if ( ieee - > iw_mode = = IW_MODE_MASTER ) {
2005-05-13 06:48:20 +04:00
if ( type ! = WLAN_FC_TYPE_MGMT & & type ! = WLAN_FC_TYPE_CTRL ) {
printk ( KERN_DEBUG " %s: unknown management frame "
" (type=0x%02x, stype=0x%02x) dropped \n " ,
skb - > dev - > name , type , stype ) ;
return - 1 ;
}
hostap_rx ( skb - > dev , skb , rx_stats ) ;
return 0 ;
}
printk ( KERN_DEBUG " %s: hostap_rx_frame_mgmt: management frame "
" received in non-Host AP mode \n " , skb - > dev - > name ) ;
return - 1 ;
}
# endif
/* See IEEE 802.1H for LLC/SNAP encapsulation/decapsulation */
/* Ethernet-II snap header (RFC1042 for most EtherTypes) */
2005-09-07 08:48:31 +04:00
static unsigned char rfc1042_header [ ] = { 0xaa , 0xaa , 0x03 , 0x00 , 0x00 , 0x00 } ;
2005-05-13 06:48:20 +04:00
/* Bridge-Tunnel header (for EtherTypes ETH_P_AARP and ETH_P_IPX) */
static unsigned char bridge_tunnel_header [ ] =
2005-09-07 08:48:31 +04:00
{ 0xaa , 0xaa , 0x03 , 0x00 , 0x00 , 0xf8 } ;
2005-05-13 06:48:20 +04:00
/* No encapsulation header if EtherType < 0x600 (=length) */
/* Called by ieee80211_rx_frame_decrypt */
static int ieee80211_is_eapol_frame ( struct ieee80211_device * ieee ,
struct sk_buff * skb )
{
struct net_device * dev = ieee - > dev ;
u16 fc , ethertype ;
2005-09-21 20:54:36 +04:00
struct ieee80211_hdr_3addr * hdr ;
2005-05-13 06:48:20 +04:00
u8 * pos ;
if ( skb - > len < 24 )
return 0 ;
2005-09-21 20:54:36 +04:00
hdr = ( struct ieee80211_hdr_3addr * ) skb - > data ;
2005-05-13 06:48:20 +04:00
fc = le16_to_cpu ( hdr - > frame_ctl ) ;
/* check that the frame is unicast frame to us */
if ( ( fc & ( IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS ) ) = =
IEEE80211_FCTL_TODS & &
memcmp ( hdr - > addr1 , dev - > dev_addr , ETH_ALEN ) = = 0 & &
memcmp ( hdr - > addr3 , dev - > dev_addr , ETH_ALEN ) = = 0 ) {
/* ToDS frame with own addr BSSID and DA */
} else if ( ( fc & ( IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS ) ) = =
IEEE80211_FCTL_FROMDS & &
memcmp ( hdr - > addr1 , dev - > dev_addr , ETH_ALEN ) = = 0 ) {
/* FromDS frame with own addr as DA */
} else
return 0 ;
if ( skb - > len < 24 + 8 )
return 0 ;
/* check for port access entity Ethernet type */
pos = skb - > data + 24 ;
ethertype = ( pos [ 6 ] < < 8 ) | pos [ 7 ] ;
if ( ethertype = = ETH_P_PAE )
return 1 ;
return 0 ;
}
/* Called only as a tasklet (software IRQ), by ieee80211_rx */
static inline int
2005-09-07 08:48:31 +04:00
ieee80211_rx_frame_decrypt ( struct ieee80211_device * ieee , struct sk_buff * skb ,
2005-05-13 06:48:20 +04:00
struct ieee80211_crypt_data * crypt )
{
2005-09-21 20:54:36 +04:00
struct ieee80211_hdr_3addr * hdr ;
2005-05-13 06:48:20 +04:00
int res , hdrlen ;
if ( crypt = = NULL | | crypt - > ops - > decrypt_mpdu = = NULL )
return 0 ;
2005-09-21 20:54:36 +04:00
hdr = ( struct ieee80211_hdr_3addr * ) skb - > data ;
2005-05-13 06:48:20 +04:00
hdrlen = ieee80211_get_hdrlen ( le16_to_cpu ( hdr - > frame_ctl ) ) ;
atomic_inc ( & crypt - > refcnt ) ;
res = crypt - > ops - > decrypt_mpdu ( skb , hdrlen , crypt - > priv ) ;
atomic_dec ( & crypt - > refcnt ) ;
if ( res < 0 ) {
2005-09-07 08:48:31 +04:00
IEEE80211_DEBUG_DROP ( " decryption failed (SA= " MAC_FMT
" ) res=%d \n " , MAC_ARG ( hdr - > addr2 ) , res ) ;
2005-05-13 06:48:20 +04:00
if ( res = = - 2 )
IEEE80211_DEBUG_DROP ( " Decryption failed ICV "
" mismatch (key %d) \n " ,
skb - > data [ hdrlen + 3 ] > > 6 ) ;
ieee - > ieee_stats . rx_discards_undecryptable + + ;
return - 1 ;
}
return res ;
}
/* Called only as a tasklet (software IRQ), by ieee80211_rx */
static inline int
2005-09-07 08:48:31 +04:00
ieee80211_rx_frame_decrypt_msdu ( struct ieee80211_device * ieee ,
struct sk_buff * skb , int keyidx ,
struct ieee80211_crypt_data * crypt )
2005-05-13 06:48:20 +04:00
{
2005-09-21 20:54:36 +04:00
struct ieee80211_hdr_3addr * hdr ;
2005-05-13 06:48:20 +04:00
int res , hdrlen ;
if ( crypt = = NULL | | crypt - > ops - > decrypt_msdu = = NULL )
return 0 ;
2005-09-21 20:54:36 +04:00
hdr = ( struct ieee80211_hdr_3addr * ) skb - > data ;
2005-05-13 06:48:20 +04:00
hdrlen = ieee80211_get_hdrlen ( le16_to_cpu ( hdr - > frame_ctl ) ) ;
atomic_inc ( & crypt - > refcnt ) ;
res = crypt - > ops - > decrypt_msdu ( skb , keyidx , hdrlen , crypt - > priv ) ;
atomic_dec ( & crypt - > refcnt ) ;
if ( res < 0 ) {
printk ( KERN_DEBUG " %s: MSDU decryption/MIC verification failed "
" (SA= " MAC_FMT " keyidx=%d) \n " ,
ieee - > dev - > name , MAC_ARG ( hdr - > addr2 ) , keyidx ) ;
return - 1 ;
}
return 0 ;
}
/* All received frames are sent to this function. @skb contains the frame in
* IEEE 802.11 format , i . e . , in the format it was sent over air .
* This function is called only as a tasklet ( software IRQ ) . */
int ieee80211_rx ( struct ieee80211_device * ieee , struct sk_buff * skb ,
struct ieee80211_rx_stats * rx_stats )
{
struct net_device * dev = ieee - > dev ;
2005-09-21 20:54:36 +04:00
struct ieee80211_hdr_4addr * hdr ;
2005-05-13 06:48:20 +04:00
size_t hdrlen ;
u16 fc , type , stype , sc ;
struct net_device_stats * stats ;
unsigned int frag ;
u8 * payload ;
u16 ethertype ;
# ifdef NOT_YET
struct net_device * wds = NULL ;
struct sk_buff * skb2 = NULL ;
struct net_device * wds = NULL ;
int frame_authorized = 0 ;
int from_assoc_ap = 0 ;
void * sta = NULL ;
# endif
u8 dst [ ETH_ALEN ] ;
u8 src [ ETH_ALEN ] ;
struct ieee80211_crypt_data * crypt = NULL ;
int keyidx = 0 ;
2005-09-21 20:54:36 +04:00
hdr = ( struct ieee80211_hdr_4addr * ) skb - > data ;
2005-05-13 06:48:20 +04:00
stats = & ieee - > stats ;
if ( skb - > len < 10 ) {
2005-09-07 08:48:31 +04:00
printk ( KERN_INFO " %s: SKB length < 10 \n " , dev - > name ) ;
2005-05-13 06:48:20 +04:00
goto rx_dropped ;
}
fc = le16_to_cpu ( hdr - > frame_ctl ) ;
type = WLAN_FC_GET_TYPE ( fc ) ;
stype = WLAN_FC_GET_STYPE ( fc ) ;
sc = le16_to_cpu ( hdr - > seq_ctl ) ;
frag = WLAN_GET_SEQ_FRAG ( sc ) ;
hdrlen = ieee80211_get_hdrlen ( fc ) ;
/* Put this code here so that we avoid duplicating it in all
* Rx paths . - Jean II */
# ifdef IW_WIRELESS_SPY /* defined in iw_handler.h */
/* If spy monitoring on */
2005-09-14 02:35:21 +04:00
if ( ieee - > spy_data . spy_number > 0 ) {
2005-05-13 06:48:20 +04:00
struct iw_quality wstats ;
2005-09-14 02:35:21 +04:00
wstats . updated = 0 ;
if ( rx_stats - > mask & IEEE80211_STATMASK_RSSI ) {
wstats . level = rx_stats - > rssi ;
wstats . updated | = IW_QUAL_LEVEL_UPDATED ;
} else
wstats . updated | = IW_QUAL_LEVEL_INVALID ;
if ( rx_stats - > mask & IEEE80211_STATMASK_NOISE ) {
wstats . noise = rx_stats - > noise ;
wstats . updated | = IW_QUAL_NOISE_UPDATED ;
} else
wstats . updated | = IW_QUAL_NOISE_INVALID ;
if ( rx_stats - > mask & IEEE80211_STATMASK_SIGNAL ) {
wstats . qual = rx_stats - > signal ;
wstats . updated | = IW_QUAL_QUAL_UPDATED ;
} else
wstats . updated | = IW_QUAL_QUAL_INVALID ;
2005-05-13 06:48:20 +04:00
/* Update spy records */
2005-09-14 02:35:21 +04:00
wireless_spy_update ( ieee - > dev , hdr - > addr2 , & wstats ) ;
2005-05-13 06:48:20 +04:00
}
2005-09-07 08:48:31 +04:00
# endif /* IW_WIRELESS_SPY */
2005-09-14 02:35:21 +04:00
# ifdef NOT_YET
2005-05-13 06:48:20 +04:00
hostap_update_rx_stats ( local - > ap , hdr , rx_stats ) ;
# endif
if ( ieee - > iw_mode = = IW_MODE_MONITOR ) {
ieee80211_monitor_rx ( ieee , skb , rx_stats ) ;
stats - > rx_packets + + ;
stats - > rx_bytes + = skb - > len ;
return 1 ;
}
2005-09-21 20:58:32 +04:00
if ( is_multicast_ether_addr ( hdr - > addr1 ) ? ieee - > host_mc_decrypt :
ieee - > host_decrypt ) {
2005-05-13 06:48:20 +04:00
int idx = 0 ;
if ( skb - > len > = hdrlen + 3 )
idx = skb - > data [ hdrlen + 3 ] > > 6 ;
crypt = ieee - > crypt [ idx ] ;
# ifdef NOT_YET
sta = NULL ;
/* Use station specific key to override default keys if the
* receiver address is a unicast address ( " individual RA " ) . If
* bcrx_sta_key parameter is set , station specific key is used
* even with broad / multicast targets ( this is against IEEE
* 802.11 , but makes it easier to use different keys with
* stations that do not support WEP key mapping ) . */
if ( ! ( hdr - > addr1 [ 0 ] & 0x01 ) | | local - > bcrx_sta_key )
2005-09-07 08:48:31 +04:00
( void ) hostap_handle_sta_crypto ( local , hdr , & crypt ,
& sta ) ;
2005-05-13 06:48:20 +04:00
# endif
/* allow NULL decrypt to indicate an station specific override
* for default encryption */
if ( crypt & & ( crypt - > ops = = NULL | |
crypt - > ops - > decrypt_mpdu = = NULL ) )
crypt = NULL ;
2005-08-26 04:11:46 +04:00
if ( ! crypt & & ( fc & IEEE80211_FCTL_PROTECTED ) ) {
2005-05-13 06:48:20 +04:00
/* This seems to be triggered by some (multicast?)
* frames from other than current BSS , so just drop the
* frames silently instead of filling system log with
* these reports . */
IEEE80211_DEBUG_DROP ( " Decryption failed (not set) "
" (SA= " MAC_FMT " ) \n " ,
MAC_ARG ( hdr - > addr2 ) ) ;
ieee - > ieee_stats . rx_discards_undecryptable + + ;
goto rx_dropped ;
}
}
# ifdef NOT_YET
if ( type ! = WLAN_FC_TYPE_DATA ) {
if ( type = = WLAN_FC_TYPE_MGMT & & stype = = WLAN_FC_STYPE_AUTH & &
2005-08-26 04:11:46 +04:00
fc & IEEE80211_FCTL_PROTECTED & & ieee - > host_decrypt & &
2005-09-07 08:48:31 +04:00
( keyidx = hostap_rx_frame_decrypt ( ieee , skb , crypt ) ) < 0 ) {
2005-05-13 06:48:20 +04:00
printk ( KERN_DEBUG " %s: failed to decrypt mgmt::auth "
" from " MAC_FMT " \n " , dev - > name ,
MAC_ARG ( hdr - > addr2 ) ) ;
/* TODO: could inform hostapd about this so that it
* could send auth failure report */
goto rx_dropped ;
}
if ( ieee80211_rx_frame_mgmt ( ieee , skb , rx_stats , type , stype ) )
goto rx_dropped ;
else
goto rx_exit ;
}
# endif
/* Data frame - extract src/dst addresses */
2005-05-24 17:10:18 +04:00
if ( skb - > len < IEEE80211_3ADDR_LEN )
2005-05-13 06:48:20 +04:00
goto rx_dropped ;
switch ( fc & ( IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS ) ) {
case IEEE80211_FCTL_FROMDS :
memcpy ( dst , hdr - > addr1 , ETH_ALEN ) ;
memcpy ( src , hdr - > addr3 , ETH_ALEN ) ;
break ;
case IEEE80211_FCTL_TODS :
memcpy ( dst , hdr - > addr3 , ETH_ALEN ) ;
memcpy ( src , hdr - > addr2 , ETH_ALEN ) ;
break ;
case IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS :
2005-05-24 17:10:18 +04:00
if ( skb - > len < IEEE80211_4ADDR_LEN )
2005-05-13 06:48:20 +04:00
goto rx_dropped ;
memcpy ( dst , hdr - > addr3 , ETH_ALEN ) ;
memcpy ( src , hdr - > addr4 , ETH_ALEN ) ;
break ;
case 0 :
memcpy ( dst , hdr - > addr1 , ETH_ALEN ) ;
memcpy ( src , hdr - > addr2 , ETH_ALEN ) ;
break ;
}
# ifdef NOT_YET
if ( hostap_rx_frame_wds ( ieee , hdr , fc , & wds ) )
goto rx_dropped ;
if ( wds ) {
skb - > dev = dev = wds ;
stats = hostap_get_stats ( dev ) ;
}
if ( ieee - > iw_mode = = IW_MODE_MASTER & & ! wds & &
2005-09-07 08:48:31 +04:00
( fc & ( IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS ) ) = =
IEEE80211_FCTL_FROMDS & & ieee - > stadev
& & memcmp ( hdr - > addr2 , ieee - > assoc_ap_addr , ETH_ALEN ) = = 0 ) {
2005-05-13 06:48:20 +04:00
/* Frame from BSSID of the AP for which we are a client */
skb - > dev = dev = ieee - > stadev ;
stats = hostap_get_stats ( dev ) ;
from_assoc_ap = 1 ;
}
# endif
dev - > last_rx = jiffies ;
# ifdef NOT_YET
if ( ( ieee - > iw_mode = = IW_MODE_MASTER | |
2005-09-07 08:48:31 +04:00
ieee - > iw_mode = = IW_MODE_REPEAT ) & & ! from_assoc_ap ) {
2005-05-13 06:48:20 +04:00
switch ( hostap_handle_sta_rx ( ieee , dev , skb , rx_stats ,
wds ! = NULL ) ) {
case AP_RX_CONTINUE_NOT_AUTHORIZED :
frame_authorized = 0 ;
break ;
case AP_RX_CONTINUE :
frame_authorized = 1 ;
break ;
case AP_RX_DROP :
goto rx_dropped ;
case AP_RX_EXIT :
goto rx_exit ;
}
}
# endif
/* Nullfunc frames may have PS-bit set, so they must be passed to
* hostap_handle_sta_rx ( ) before being dropped here . */
2005-09-21 20:56:33 +04:00
stype & = ~ IEEE80211_STYPE_QOS_DATA ;
2005-05-13 06:48:20 +04:00
if ( stype ! = IEEE80211_STYPE_DATA & &
stype ! = IEEE80211_STYPE_DATA_CFACK & &
stype ! = IEEE80211_STYPE_DATA_CFPOLL & &
stype ! = IEEE80211_STYPE_DATA_CFACKPOLL ) {
if ( stype ! = IEEE80211_STYPE_NULLFUNC )
2005-09-07 08:48:31 +04:00
IEEE80211_DEBUG_DROP ( " RX: dropped data frame "
" with no data (type=0x%02x, "
" subtype=0x%02x, len=%d) \n " ,
type , stype , skb - > len ) ;
2005-05-13 06:48:20 +04:00
goto rx_dropped ;
}
/* skb: hdr + (possibly fragmented, possibly encrypted) payload */
2005-08-26 04:11:46 +04:00
if ( ieee - > host_decrypt & & ( fc & IEEE80211_FCTL_PROTECTED ) & &
2005-05-13 06:48:20 +04:00
( keyidx = ieee80211_rx_frame_decrypt ( ieee , skb , crypt ) ) < 0 )
goto rx_dropped ;
2005-09-21 20:54:36 +04:00
hdr = ( struct ieee80211_hdr_4addr * ) skb - > data ;
2005-05-13 06:48:20 +04:00
/* skb: hdr + (possibly fragmented) plaintext payload */
// PR: FIXME: hostap has additional conditions in the "if" below:
2005-08-26 04:11:46 +04:00
// ieee->host_decrypt && (fc & IEEE80211_FCTL_PROTECTED) &&
2005-05-13 06:48:20 +04:00
if ( ( frag ! = 0 | | ( fc & IEEE80211_FCTL_MOREFRAGS ) ) ) {
int flen ;
struct sk_buff * frag_skb = ieee80211_frag_cache_get ( ieee , hdr ) ;
IEEE80211_DEBUG_FRAG ( " Rx Fragment received (%u) \n " , frag ) ;
if ( ! frag_skb ) {
IEEE80211_DEBUG ( IEEE80211_DL_RX | IEEE80211_DL_FRAG ,
" Rx cannot get skb from fragment "
" cache (morefrag=%d seq=%u frag=%u) \n " ,
( fc & IEEE80211_FCTL_MOREFRAGS ) ! = 0 ,
WLAN_GET_SEQ_SEQ ( sc ) , frag ) ;
goto rx_dropped ;
}
flen = skb - > len ;
if ( frag ! = 0 )
flen - = hdrlen ;
if ( frag_skb - > tail + flen > frag_skb - > end ) {
printk ( KERN_WARNING " %s: host decrypted and "
" reassembled frame did not fit skb \n " ,
dev - > name ) ;
ieee80211_frag_cache_invalidate ( ieee , hdr ) ;
goto rx_dropped ;
}
if ( frag = = 0 ) {
/* copy first fragment (including full headers) into
* beginning of the fragment cache skb */
memcpy ( skb_put ( frag_skb , flen ) , skb - > data , flen ) ;
} else {
/* append frame payload to the end of the fragment
* cache skb */
memcpy ( skb_put ( frag_skb , flen ) , skb - > data + hdrlen ,
flen ) ;
}
dev_kfree_skb_any ( skb ) ;
skb = NULL ;
if ( fc & IEEE80211_FCTL_MOREFRAGS ) {
/* more fragments expected - leave the skb in fragment
* cache for now ; it will be delivered to upper layers
* after all fragments have been received */
goto rx_exit ;
}
/* this was the last fragment and the frame will be
* delivered , so remove skb from fragment cache */
skb = frag_skb ;
2005-09-21 20:54:36 +04:00
hdr = ( struct ieee80211_hdr_4addr * ) skb - > data ;
2005-05-13 06:48:20 +04:00
ieee80211_frag_cache_invalidate ( ieee , hdr ) ;
}
/* skb: hdr + (possible reassembled) full MSDU payload; possibly still
* encrypted / authenticated */
2005-08-26 04:11:46 +04:00
if ( ieee - > host_decrypt & & ( fc & IEEE80211_FCTL_PROTECTED ) & &
2005-05-13 06:48:20 +04:00
ieee80211_rx_frame_decrypt_msdu ( ieee , skb , keyidx , crypt ) )
goto rx_dropped ;
2005-09-21 20:54:36 +04:00
hdr = ( struct ieee80211_hdr_4addr * ) skb - > data ;
2005-08-26 04:11:46 +04:00
if ( crypt & & ! ( fc & IEEE80211_FCTL_PROTECTED ) & & ! ieee - > open_wep ) {
2005-09-07 08:48:31 +04:00
if ( /*ieee->ieee802_1x && */
ieee80211_is_eapol_frame ( ieee , skb ) ) {
2005-05-13 06:48:20 +04:00
/* pass unencrypted EAPOL frames even if encryption is
* configured */
} else {
2005-09-07 08:48:31 +04:00
IEEE80211_DEBUG_DROP ( " encryption configured, but RX "
" frame not encrypted (SA= " MAC_FMT
" ) \n " , MAC_ARG ( hdr - > addr2 ) ) ;
2005-05-13 06:48:20 +04:00
goto rx_dropped ;
}
}
2005-08-26 04:11:46 +04:00
if ( crypt & & ! ( fc & IEEE80211_FCTL_PROTECTED ) & & ! ieee - > open_wep & &
2005-05-13 06:48:20 +04:00
! ieee80211_is_eapol_frame ( ieee , skb ) ) {
2005-09-07 08:48:31 +04:00
IEEE80211_DEBUG_DROP ( " dropped unencrypted RX data "
" frame from " MAC_FMT
" (drop_unencrypted=1) \n " ,
MAC_ARG ( hdr - > addr2 ) ) ;
2005-05-13 06:48:20 +04:00
goto rx_dropped ;
}
/* skb: hdr + (possible reassembled) full plaintext payload */
payload = skb - > data + hdrlen ;
ethertype = ( payload [ 6 ] < < 8 ) | payload [ 7 ] ;
# ifdef NOT_YET
/* If IEEE 802.1X is used, check whether the port is authorized to send
* the received frame . */
if ( ieee - > ieee802_1x & & ieee - > iw_mode = = IW_MODE_MASTER ) {
if ( ethertype = = ETH_P_PAE ) {
printk ( KERN_DEBUG " %s: RX: IEEE 802.1X frame \n " ,
dev - > name ) ;
if ( ieee - > hostapd & & ieee - > apdev ) {
/* Send IEEE 802.1X frames to the user
* space daemon for processing */
prism2_rx_80211 ( ieee - > apdev , skb , rx_stats ,
PRISM2_RX_MGMT ) ;
ieee - > apdevstats . rx_packets + + ;
ieee - > apdevstats . rx_bytes + = skb - > len ;
goto rx_exit ;
}
} else if ( ! frame_authorized ) {
printk ( KERN_DEBUG " %s: dropped frame from "
" unauthorized port (IEEE 802.1X): "
2005-09-07 08:48:31 +04:00
" ethertype=0x%04x \n " , dev - > name , ethertype ) ;
2005-05-13 06:48:20 +04:00
goto rx_dropped ;
}
}
# endif
/* convert hdr + possible LLC headers into Ethernet header */
if ( skb - > len - hdrlen > = 8 & &
( ( memcmp ( payload , rfc1042_header , SNAP_SIZE ) = = 0 & &
ethertype ! = ETH_P_AARP & & ethertype ! = ETH_P_IPX ) | |
memcmp ( payload , bridge_tunnel_header , SNAP_SIZE ) = = 0 ) ) {
/* remove RFC1042 or Bridge-Tunnel encapsulation and
* replace EtherType */
skb_pull ( skb , hdrlen + SNAP_SIZE ) ;
memcpy ( skb_push ( skb , ETH_ALEN ) , src , ETH_ALEN ) ;
memcpy ( skb_push ( skb , ETH_ALEN ) , dst , ETH_ALEN ) ;
} else {
u16 len ;
/* Leave Ethernet header part of hdr and full payload */
skb_pull ( skb , hdrlen ) ;
len = htons ( skb - > len ) ;
memcpy ( skb_push ( skb , 2 ) , & len , 2 ) ;
memcpy ( skb_push ( skb , ETH_ALEN ) , src , ETH_ALEN ) ;
memcpy ( skb_push ( skb , ETH_ALEN ) , dst , ETH_ALEN ) ;
}
# ifdef NOT_YET
if ( wds & & ( ( fc & ( IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS ) ) = =
2005-09-07 08:48:31 +04:00
IEEE80211_FCTL_TODS ) & & skb - > len > = ETH_HLEN + ETH_ALEN ) {
2005-05-13 06:48:20 +04:00
/* Non-standard frame: get addr4 from its bogus location after
* the payload */
memcpy ( skb - > data + ETH_ALEN ,
skb - > data + skb - > len - ETH_ALEN , ETH_ALEN ) ;
skb_trim ( skb , skb - > len - ETH_ALEN ) ;
}
# endif
stats - > rx_packets + + ;
stats - > rx_bytes + = skb - > len ;
# ifdef NOT_YET
2005-09-07 08:48:31 +04:00
if ( ieee - > iw_mode = = IW_MODE_MASTER & & ! wds & & ieee - > ap - > bridge_packets ) {
2005-05-13 06:48:20 +04:00
if ( dst [ 0 ] & 0x01 ) {
/* copy multicast frame both to the higher layers and
* to the wireless media */
ieee - > ap - > bridged_multicast + + ;
skb2 = skb_clone ( skb , GFP_ATOMIC ) ;
if ( skb2 = = NULL )
printk ( KERN_DEBUG " %s: skb_clone failed for "
" multicast frame \n " , dev - > name ) ;
} else if ( hostap_is_sta_assoc ( ieee - > ap , dst ) ) {
/* send frame directly to the associated STA using
* wireless media and not passing to higher layers */
ieee - > ap - > bridged_unicast + + ;
skb2 = skb ;
skb = NULL ;
}
}
if ( skb2 ! = NULL ) {
/* send to wireless media */
skb2 - > protocol = __constant_htons ( ETH_P_802_3 ) ;
skb2 - > mac . raw = skb2 - > nh . raw = skb2 - > data ;
/* skb2->nh.raw = skb2->data + ETH_HLEN; */
skb2 - > dev = dev ;
dev_queue_xmit ( skb2 ) ;
}
# endif
if ( skb ) {
skb - > protocol = eth_type_trans ( skb , dev ) ;
memset ( skb - > cb , 0 , sizeof ( skb - > cb ) ) ;
skb - > dev = dev ;
2005-09-07 08:48:31 +04:00
skb - > ip_summed = CHECKSUM_NONE ; /* 802.11 crc not sufficient */
2005-05-13 06:48:20 +04:00
netif_rx ( skb ) ;
}
2005-09-07 08:48:31 +04:00
rx_exit :
2005-05-13 06:48:20 +04:00
# ifdef NOT_YET
if ( sta )
hostap_handle_sta_release ( sta ) ;
# endif
return 1 ;
2005-09-07 08:48:31 +04:00
rx_dropped :
2005-05-13 06:48:20 +04:00
stats - > rx_dropped + + ;
/* Returning 0 indicates to caller that we have not handled the SKB--
* so it is still allocated and can be used again by underlying
* hardware as a DMA target */
return 0 ;
}
# define MGMT_FRAME_FIXED_PART_LENGTH 0x24
2005-09-21 20:56:33 +04:00
static u8 qos_oui [ QOS_OUI_LEN ] = { 0x00 , 0x50 , 0xF2 } ;
/*
* Make ther structure we read from the beacon packet has
* the right values
*/
static int ieee80211_verify_qos_info ( struct ieee80211_qos_information_element
* info_element , int sub_type )
{
if ( info_element - > qui_subtype ! = sub_type )
return - 1 ;
if ( memcmp ( info_element - > qui , qos_oui , QOS_OUI_LEN ) )
return - 1 ;
if ( info_element - > qui_type ! = QOS_OUI_TYPE )
return - 1 ;
if ( info_element - > version ! = QOS_VERSION_1 )
return - 1 ;
return 0 ;
}
/*
* Parse a QoS parameter element
*/
static int ieee80211_read_qos_param_element ( struct ieee80211_qos_parameter_info
* element_param , struct ieee80211_info_element
* info_element )
{
int ret = 0 ;
u16 size = sizeof ( struct ieee80211_qos_parameter_info ) - 2 ;
if ( ( info_element = = NULL ) | | ( element_param = = NULL ) )
return - 1 ;
if ( info_element - > id = = QOS_ELEMENT_ID & & info_element - > len = = size ) {
memcpy ( element_param - > info_element . qui , info_element - > data ,
info_element - > len ) ;
element_param - > info_element . elementID = info_element - > id ;
element_param - > info_element . length = info_element - > len ;
} else
ret = - 1 ;
if ( ret = = 0 )
ret = ieee80211_verify_qos_info ( & element_param - > info_element ,
QOS_OUI_PARAM_SUB_TYPE ) ;
return ret ;
}
/*
* Parse a QoS information element
*/
static int ieee80211_read_qos_info_element ( struct
ieee80211_qos_information_element
* element_info , struct ieee80211_info_element
* info_element )
{
int ret = 0 ;
u16 size = sizeof ( struct ieee80211_qos_information_element ) - 2 ;
if ( element_info = = NULL )
return - 1 ;
if ( info_element = = NULL )
return - 1 ;
if ( ( info_element - > id = = QOS_ELEMENT_ID ) & & ( info_element - > len = = size ) ) {
memcpy ( element_info - > qui , info_element - > data ,
info_element - > len ) ;
element_info - > elementID = info_element - > id ;
element_info - > length = info_element - > len ;
} else
ret = - 1 ;
if ( ret = = 0 )
ret = ieee80211_verify_qos_info ( element_info ,
QOS_OUI_INFO_SUB_TYPE ) ;
return ret ;
}
/*
* Write QoS parameters from the ac parameters .
*/
static int ieee80211_qos_convert_ac_to_parameters ( struct
ieee80211_qos_parameter_info
* param_elm , struct
ieee80211_qos_parameters
* qos_param )
{
int rc = 0 ;
int i ;
struct ieee80211_qos_ac_parameter * ac_params ;
u32 txop ;
u8 cw_min ;
u8 cw_max ;
for ( i = 0 ; i < QOS_QUEUE_NUM ; i + + ) {
ac_params = & ( param_elm - > ac_params_record [ i ] ) ;
qos_param - > aifs [ i ] = ( ac_params - > aci_aifsn ) & 0x0F ;
qos_param - > aifs [ i ] - = ( qos_param - > aifs [ i ] < 2 ) ? 0 : 2 ;
cw_min = ac_params - > ecw_min_max & 0x0F ;
qos_param - > cw_min [ i ] = ( u16 ) ( ( 1 < < cw_min ) - 1 ) ;
cw_max = ( ac_params - > ecw_min_max & 0xF0 ) > > 4 ;
qos_param - > cw_max [ i ] = ( u16 ) ( ( 1 < < cw_max ) - 1 ) ;
qos_param - > flag [ i ] =
( ac_params - > aci_aifsn & 0x10 ) ? 0x01 : 0x00 ;
txop = le16_to_cpu ( ac_params - > tx_op_limit ) * 32 ;
qos_param - > tx_op_limit [ i ] = ( u16 ) txop ;
}
return rc ;
}
/*
* we have a generic data element which it may contain QoS information or
* parameters element . check the information element length to decide
* which type to read
*/
static int ieee80211_parse_qos_info_param_IE ( struct ieee80211_info_element
* info_element ,
struct ieee80211_network * network )
{
int rc = 0 ;
struct ieee80211_qos_parameters * qos_param = NULL ;
struct ieee80211_qos_information_element qos_info_element ;
rc = ieee80211_read_qos_info_element ( & qos_info_element , info_element ) ;
if ( rc = = 0 ) {
network - > qos_data . param_count = qos_info_element . ac_info & 0x0F ;
network - > flags | = NETWORK_HAS_QOS_INFORMATION ;
} else {
struct ieee80211_qos_parameter_info param_element ;
rc = ieee80211_read_qos_param_element ( & param_element ,
info_element ) ;
if ( rc = = 0 ) {
qos_param = & ( network - > qos_data . parameters ) ;
ieee80211_qos_convert_ac_to_parameters ( & param_element ,
qos_param ) ;
network - > flags | = NETWORK_HAS_QOS_PARAMETERS ;
network - > qos_data . param_count =
param_element . info_element . ac_info & 0x0F ;
}
}
if ( rc = = 0 ) {
IEEE80211_DEBUG_QOS ( " QoS is supported \n " ) ;
network - > qos_data . supported = 1 ;
}
return rc ;
}
static int ieee80211_handle_assoc_resp ( struct ieee80211_device * ieee , struct ieee80211_assoc_response
* frame , struct ieee80211_rx_stats * stats )
{
struct ieee80211_network network_resp ;
struct ieee80211_network * network = & network_resp ;
struct ieee80211_info_element * info_element ;
struct net_device * dev = ieee - > dev ;
u16 left ;
network - > flags = 0 ;
network - > qos_data . active = 0 ;
network - > qos_data . supported = 0 ;
network - > qos_data . param_count = 0 ;
network - > qos_data . old_param_count = 0 ;
//network->atim_window = le16_to_cpu(frame->aid) & (0x3FFF);
network - > atim_window = le16_to_cpu ( frame - > aid ) ;
network - > listen_interval = le16_to_cpu ( frame - > status ) ;
info_element = frame - > info_element ;
left = stats - > len - sizeof ( * frame ) ;
while ( left > = sizeof ( struct ieee80211_info_element ) ) {
if ( sizeof ( struct ieee80211_info_element ) +
info_element - > len > left ) {
IEEE80211_DEBUG_QOS ( " ASSOC RESP: parse failed: "
" info_element->len + 2 > left : "
" info_element->len+2=%zd left=%d, id=%d. \n " ,
info_element - > len +
sizeof ( struct
ieee80211_info_element ) ,
left , info_element - > id ) ;
return 1 ;
}
switch ( info_element - > id ) {
case MFIE_TYPE_SSID :
if ( ieee80211_is_empty_essid ( info_element - > data ,
info_element - > len ) ) {
network - > flags | = NETWORK_EMPTY_ESSID ;
break ;
}
network - > ssid_len = min ( info_element - > len ,
( u8 ) IW_ESSID_MAX_SIZE ) ;
memcpy ( network - > ssid , info_element - > data ,
network - > ssid_len ) ;
if ( network - > ssid_len < IW_ESSID_MAX_SIZE )
memset ( network - > ssid + network - > ssid_len , 0 ,
IW_ESSID_MAX_SIZE - network - > ssid_len ) ;
IEEE80211_DEBUG_QOS ( " MFIE_TYPE_SSID: '%s' len=%d. \n " ,
network - > ssid , network - > ssid_len ) ;
break ;
case MFIE_TYPE_TIM :
IEEE80211_DEBUG_QOS ( " MFIE_TYPE_TIM: ignored \n " ) ;
break ;
case MFIE_TYPE_IBSS_SET :
IEEE80211_DEBUG_QOS ( " MFIE_TYPE_IBSS_SET: ignored \n " ) ;
break ;
case MFIE_TYPE_CHALLENGE :
IEEE80211_DEBUG_QOS ( " MFIE_TYPE_CHALLENGE: ignored \n " ) ;
break ;
case MFIE_TYPE_GENERIC :
IEEE80211_DEBUG_QOS ( " MFIE_TYPE_GENERIC: %d bytes \n " ,
info_element - > len ) ;
ieee80211_parse_qos_info_param_IE ( info_element ,
network ) ;
break ;
case MFIE_TYPE_RSN :
IEEE80211_DEBUG_QOS ( " MFIE_TYPE_RSN: %d bytes \n " ,
info_element - > len ) ;
break ;
case MFIE_TYPE_QOS_PARAMETER :
printk ( " QoS Error need to parse QOS_PARAMETER IE \n " ) ;
break ;
default :
IEEE80211_DEBUG_QOS ( " unsupported IE %d \n " ,
info_element - > id ) ;
break ;
}
left - = sizeof ( struct ieee80211_info_element ) +
info_element - > len ;
info_element = ( struct ieee80211_info_element * )
& info_element - > data [ info_element - > len ] ;
}
if ( ieee - > handle_assoc_response ! = NULL )
ieee - > handle_assoc_response ( dev , frame , network ) ;
return 0 ;
}
/***************************************************/
2005-05-13 06:48:20 +04:00
static inline int ieee80211_is_ofdm_rate ( u8 rate )
{
switch ( rate & ~ IEEE80211_BASIC_RATE_MASK ) {
case IEEE80211_OFDM_RATE_6MB :
case IEEE80211_OFDM_RATE_9MB :
case IEEE80211_OFDM_RATE_12MB :
case IEEE80211_OFDM_RATE_18MB :
case IEEE80211_OFDM_RATE_24MB :
case IEEE80211_OFDM_RATE_36MB :
case IEEE80211_OFDM_RATE_48MB :
case IEEE80211_OFDM_RATE_54MB :
return 1 ;
}
2005-09-07 08:48:31 +04:00
return 0 ;
2005-05-13 06:48:20 +04:00
}
2005-09-14 02:35:21 +04:00
static inline int ieee80211_network_init ( struct ieee80211_device * ieee , struct ieee80211_probe_response
2005-09-07 08:48:31 +04:00
* beacon ,
struct ieee80211_network * network ,
struct ieee80211_rx_stats * stats )
2005-05-13 06:48:20 +04:00
{
# ifdef CONFIG_IEEE80211_DEBUG
char rates_str [ 64 ] ;
char * p ;
# endif
struct ieee80211_info_element * info_element ;
2005-09-07 08:48:31 +04:00
u16 left ;
2005-05-13 06:48:20 +04:00
u8 i ;
2005-09-21 20:56:33 +04:00
network - > qos_data . active = 0 ;
network - > qos_data . supported = 0 ;
network - > qos_data . param_count = 0 ;
2005-05-13 06:48:20 +04:00
/* Pull out fixed field data */
memcpy ( network - > bssid , beacon - > header . addr3 , ETH_ALEN ) ;
2005-09-14 02:25:51 +04:00
network - > capability = le16_to_cpu ( beacon - > capability ) ;
2005-05-13 06:48:20 +04:00
network - > last_scanned = jiffies ;
2005-09-14 02:25:51 +04:00
network - > time_stamp [ 0 ] = le32_to_cpu ( beacon - > time_stamp [ 0 ] ) ;
network - > time_stamp [ 1 ] = le32_to_cpu ( beacon - > time_stamp [ 1 ] ) ;
network - > beacon_interval = le16_to_cpu ( beacon - > beacon_interval ) ;
2005-09-07 08:48:31 +04:00
/* Where to pull this? beacon->listen_interval; */
2005-05-13 06:48:20 +04:00
network - > listen_interval = 0x0A ;
network - > rates_len = network - > rates_ex_len = 0 ;
network - > last_associate = 0 ;
network - > ssid_len = 0 ;
network - > flags = 0 ;
network - > atim_window = 0 ;
2005-09-21 20:58:29 +04:00
network - > erp_value = ( network - > capability & WLAN_CAPABILITY_IBSS ) ?
2005-09-21 20:58:32 +04:00
0x3 : 0x0 ;
2005-05-13 06:48:20 +04:00
if ( stats - > freq = = IEEE80211_52GHZ_BAND ) {
/* for A band (No DS info) */
network - > channel = stats - > received_channel ;
} else
network - > flags | = NETWORK_HAS_CCK ;
2005-09-07 08:48:31 +04:00
network - > wpa_ie_len = 0 ;
network - > rsn_ie_len = 0 ;
2005-05-13 06:48:20 +04:00
2005-09-14 02:37:22 +04:00
info_element = beacon - > info_element ;
2005-09-14 02:38:13 +04:00
left = stats - > len - sizeof ( * beacon ) ;
2005-09-21 20:56:33 +04:00
while ( left > = sizeof ( * info_element ) ) {
if ( sizeof ( * info_element ) + info_element - > len > left ) {
2005-09-07 08:48:31 +04:00
IEEE80211_DEBUG_SCAN
( " SCAN: parse failed: info_element->len + 2 > left : info_element->len+2=%Zd left=%d. \n " ,
2005-09-21 20:56:33 +04:00
info_element - > len + sizeof ( * info_element ) , left ) ;
2005-05-13 06:48:20 +04:00
return 1 ;
2005-09-07 08:48:31 +04:00
}
2005-05-13 06:48:20 +04:00
switch ( info_element - > id ) {
case MFIE_TYPE_SSID :
if ( ieee80211_is_empty_essid ( info_element - > data ,
info_element - > len ) ) {
network - > flags | = NETWORK_EMPTY_ESSID ;
break ;
}
network - > ssid_len = min ( info_element - > len ,
2005-09-07 08:48:31 +04:00
( u8 ) IW_ESSID_MAX_SIZE ) ;
memcpy ( network - > ssid , info_element - > data ,
network - > ssid_len ) ;
if ( network - > ssid_len < IW_ESSID_MAX_SIZE )
memset ( network - > ssid + network - > ssid_len , 0 ,
2005-05-13 06:48:20 +04:00
IW_ESSID_MAX_SIZE - network - > ssid_len ) ;
IEEE80211_DEBUG_SCAN ( " MFIE_TYPE_SSID: '%s' len=%d. \n " ,
network - > ssid , network - > ssid_len ) ;
break ;
case MFIE_TYPE_RATES :
# ifdef CONFIG_IEEE80211_DEBUG
p = rates_str ;
# endif
2005-09-21 20:56:33 +04:00
network - > rates_len = min ( info_element - > len ,
MAX_RATES_LENGTH ) ;
2005-05-13 06:48:20 +04:00
for ( i = 0 ; i < network - > rates_len ; i + + ) {
network - > rates [ i ] = info_element - > data [ i ] ;
# ifdef CONFIG_IEEE80211_DEBUG
2005-09-21 20:56:33 +04:00
p + = snprintf ( p , sizeof ( rates_str ) -
( p - rates_str ) , " %02X " ,
network - > rates [ i ] ) ;
2005-05-13 06:48:20 +04:00
# endif
2005-09-07 08:48:31 +04:00
if ( ieee80211_is_ofdm_rate
( info_element - > data [ i ] ) ) {
2005-05-13 06:48:20 +04:00
network - > flags | = NETWORK_HAS_OFDM ;
if ( info_element - > data [ i ] &
IEEE80211_BASIC_RATE_MASK )
network - > flags & =
2005-09-07 08:48:31 +04:00
~ NETWORK_HAS_CCK ;
2005-05-13 06:48:20 +04:00
}
}
IEEE80211_DEBUG_SCAN ( " MFIE_TYPE_RATES: '%s' (%d) \n " ,
rates_str , network - > rates_len ) ;
break ;
case MFIE_TYPE_RATES_EX :
# ifdef CONFIG_IEEE80211_DEBUG
p = rates_str ;
# endif
2005-09-21 20:56:33 +04:00
network - > rates_ex_len = min ( info_element - > len ,
MAX_RATES_EX_LENGTH ) ;
2005-05-13 06:48:20 +04:00
for ( i = 0 ; i < network - > rates_ex_len ; i + + ) {
network - > rates_ex [ i ] = info_element - > data [ i ] ;
# ifdef CONFIG_IEEE80211_DEBUG
2005-09-21 20:56:33 +04:00
p + = snprintf ( p , sizeof ( rates_str ) -
( p - rates_str ) , " %02X " ,
network - > rates [ i ] ) ;
2005-05-13 06:48:20 +04:00
# endif
2005-09-07 08:48:31 +04:00
if ( ieee80211_is_ofdm_rate
( info_element - > data [ i ] ) ) {
2005-05-13 06:48:20 +04:00
network - > flags | = NETWORK_HAS_OFDM ;
if ( info_element - > data [ i ] &
IEEE80211_BASIC_RATE_MASK )
network - > flags & =
2005-09-07 08:48:31 +04:00
~ NETWORK_HAS_CCK ;
2005-05-13 06:48:20 +04:00
}
}
IEEE80211_DEBUG_SCAN ( " MFIE_TYPE_RATES_EX: '%s' (%d) \n " ,
rates_str , network - > rates_ex_len ) ;
break ;
case MFIE_TYPE_DS_SET :
2005-09-07 08:48:31 +04:00
IEEE80211_DEBUG_SCAN ( " MFIE_TYPE_DS_SET: %d \n " ,
2005-05-13 06:48:20 +04:00
info_element - > data [ 0 ] ) ;
if ( stats - > freq = = IEEE80211_24GHZ_BAND )
network - > channel = info_element - > data [ 0 ] ;
break ;
2005-09-07 08:48:31 +04:00
case MFIE_TYPE_FH_SET :
IEEE80211_DEBUG_SCAN ( " MFIE_TYPE_FH_SET: ignored \n " ) ;
2005-05-13 06:48:20 +04:00
break ;
case MFIE_TYPE_CF_SET :
IEEE80211_DEBUG_SCAN ( " MFIE_TYPE_CF_SET: ignored \n " ) ;
break ;
case MFIE_TYPE_TIM :
IEEE80211_DEBUG_SCAN ( " MFIE_TYPE_TIM: ignored \n " ) ;
break ;
2005-09-21 20:58:29 +04:00
case MFIE_TYPE_ERP_INFO :
network - > erp_value = info_element - > data [ 0 ] ;
IEEE80211_DEBUG_SCAN ( " MFIE_TYPE_ERP_SET: %d \n " ,
network - > erp_value ) ;
break ;
2005-05-13 06:48:20 +04:00
case MFIE_TYPE_IBSS_SET :
2005-09-21 20:58:29 +04:00
network - > atim_window = info_element - > data [ 0 ] ;
IEEE80211_DEBUG_SCAN ( " MFIE_TYPE_IBSS_SET: %d \n " ,
network - > atim_window ) ;
2005-05-13 06:48:20 +04:00
break ;
case MFIE_TYPE_CHALLENGE :
IEEE80211_DEBUG_SCAN ( " MFIE_TYPE_CHALLENGE: ignored \n " ) ;
break ;
case MFIE_TYPE_GENERIC :
IEEE80211_DEBUG_SCAN ( " MFIE_TYPE_GENERIC: %d bytes \n " ,
info_element - > len ) ;
2005-09-21 20:56:33 +04:00
if ( ! ieee80211_parse_qos_info_param_IE ( info_element ,
network ) )
break ;
2005-09-07 08:48:31 +04:00
if ( info_element - > len > = 4 & &
2005-05-13 06:48:20 +04:00
info_element - > data [ 0 ] = = 0x00 & &
info_element - > data [ 1 ] = = 0x50 & &
info_element - > data [ 2 ] = = 0xf2 & &
info_element - > data [ 3 ] = = 0x01 ) {
network - > wpa_ie_len = min ( info_element - > len + 2 ,
2005-09-07 08:48:31 +04:00
MAX_WPA_IE_LEN ) ;
2005-05-13 06:48:20 +04:00
memcpy ( network - > wpa_ie , info_element ,
network - > wpa_ie_len ) ;
}
break ;
case MFIE_TYPE_RSN :
IEEE80211_DEBUG_SCAN ( " MFIE_TYPE_RSN: %d bytes \n " ,
info_element - > len ) ;
network - > rsn_ie_len = min ( info_element - > len + 2 ,
2005-09-07 08:48:31 +04:00
MAX_WPA_IE_LEN ) ;
2005-05-13 06:48:20 +04:00
memcpy ( network - > rsn_ie , info_element ,
network - > rsn_ie_len ) ;
break ;
2005-09-21 20:56:33 +04:00
case MFIE_TYPE_QOS_PARAMETER :
printk ( KERN_ERR
" QoS Error need to parse QOS_PARAMETER IE \n " ) ;
break ;
2005-05-13 06:48:20 +04:00
default :
IEEE80211_DEBUG_SCAN ( " unsupported IE %d \n " ,
info_element - > id ) ;
2005-09-07 08:48:31 +04:00
break ;
}
2005-05-13 06:48:20 +04:00
2005-09-21 20:56:33 +04:00
left - = sizeof ( * info_element ) + info_element - > len ;
2005-05-13 06:48:20 +04:00
info_element = ( struct ieee80211_info_element * )
2005-09-07 08:48:31 +04:00
& info_element - > data [ info_element - > len ] ;
}
2005-05-13 06:48:20 +04:00
network - > mode = 0 ;
if ( stats - > freq = = IEEE80211_52GHZ_BAND )
network - > mode = IEEE_A ;
else {
if ( network - > flags & NETWORK_HAS_OFDM )
network - > mode | = IEEE_G ;
if ( network - > flags & NETWORK_HAS_CCK )
network - > mode | = IEEE_B ;
}
if ( network - > mode = = 0 ) {
IEEE80211_DEBUG_SCAN ( " Filtered out '%s ( " MAC_FMT " )' "
" network. \n " ,
escape_essid ( network - > ssid ,
network - > ssid_len ) ,
MAC_ARG ( network - > bssid ) ) ;
return 1 ;
}
if ( ieee80211_is_empty_essid ( network - > ssid , network - > ssid_len ) )
network - > flags | = NETWORK_EMPTY_ESSID ;
memcpy ( & network - > stats , stats , sizeof ( network - > stats ) ) ;
return 0 ;
}
static inline int is_same_network ( struct ieee80211_network * src ,
struct ieee80211_network * dst )
{
/* A network is only a duplicate if the channel, BSSID, and ESSID
* all match . We treat all < hidden > with the same BSSID and channel
* as one network */
return ( ( src - > ssid_len = = dst - > ssid_len ) & &
( src - > channel = = dst - > channel ) & &
! memcmp ( src - > bssid , dst - > bssid , ETH_ALEN ) & &
! memcmp ( src - > ssid , dst - > ssid , src - > ssid_len ) ) ;
}
static inline void update_network ( struct ieee80211_network * dst ,
struct ieee80211_network * src )
{
2005-09-21 20:56:33 +04:00
int qos_active ;
u8 old_param ;
2005-05-13 06:48:20 +04:00
memcpy ( & dst - > stats , & src - > stats , sizeof ( struct ieee80211_rx_stats ) ) ;
dst - > capability = src - > capability ;
memcpy ( dst - > rates , src - > rates , src - > rates_len ) ;
dst - > rates_len = src - > rates_len ;
memcpy ( dst - > rates_ex , src - > rates_ex , src - > rates_ex_len ) ;
dst - > rates_ex_len = src - > rates_ex_len ;
dst - > mode = src - > mode ;
dst - > flags = src - > flags ;
dst - > time_stamp [ 0 ] = src - > time_stamp [ 0 ] ;
dst - > time_stamp [ 1 ] = src - > time_stamp [ 1 ] ;
dst - > beacon_interval = src - > beacon_interval ;
dst - > listen_interval = src - > listen_interval ;
dst - > atim_window = src - > atim_window ;
2005-09-21 20:58:29 +04:00
dst - > erp_value = src - > erp_value ;
2005-05-13 06:48:20 +04:00
memcpy ( dst - > wpa_ie , src - > wpa_ie , src - > wpa_ie_len ) ;
dst - > wpa_ie_len = src - > wpa_ie_len ;
memcpy ( dst - > rsn_ie , src - > rsn_ie , src - > rsn_ie_len ) ;
dst - > rsn_ie_len = src - > rsn_ie_len ;
dst - > last_scanned = jiffies ;
2005-09-21 20:56:33 +04:00
qos_active = src - > qos_data . active ;
old_param = dst - > qos_data . old_param_count ;
if ( dst - > flags & NETWORK_HAS_QOS_MASK )
memcpy ( & dst - > qos_data , & src - > qos_data ,
sizeof ( struct ieee80211_qos_data ) ) ;
else {
dst - > qos_data . supported = src - > qos_data . supported ;
dst - > qos_data . param_count = src - > qos_data . param_count ;
}
if ( dst - > qos_data . supported = = 1 ) {
if ( dst - > ssid_len )
IEEE80211_DEBUG_QOS
( " QoS the network %s is QoS supported \n " ,
dst - > ssid ) ;
else
IEEE80211_DEBUG_QOS
( " QoS the network is QoS supported \n " ) ;
}
dst - > qos_data . active = qos_active ;
dst - > qos_data . old_param_count = old_param ;
2005-05-13 06:48:20 +04:00
/* dst->last_associate is not overwritten */
}
2005-09-21 20:54:47 +04:00
static inline int is_beacon ( int fc )
{
return ( WLAN_FC_GET_STYPE ( le16_to_cpu ( fc ) ) = = IEEE80211_STYPE_BEACON ) ;
}
2005-09-07 08:48:31 +04:00
static inline void ieee80211_process_probe_response ( struct ieee80211_device
2005-09-14 02:35:21 +04:00
* ieee , struct
2005-09-07 08:48:31 +04:00
ieee80211_probe_response
2005-09-14 02:35:21 +04:00
* beacon , struct ieee80211_rx_stats
2005-09-07 08:48:31 +04:00
* stats )
2005-05-13 06:48:20 +04:00
{
2005-09-21 20:54:47 +04:00
struct net_device * dev = ieee - > dev ;
2005-05-13 06:48:20 +04:00
struct ieee80211_network network ;
struct ieee80211_network * target ;
struct ieee80211_network * oldest = NULL ;
# ifdef CONFIG_IEEE80211_DEBUG
2005-09-14 02:37:22 +04:00
struct ieee80211_info_element * info_element = beacon - > info_element ;
2005-05-13 06:48:20 +04:00
# endif
unsigned long flags ;
2005-09-07 08:48:31 +04:00
IEEE80211_DEBUG_SCAN ( " '%s' ( " MAC_FMT
" ): %c%c%c%c %c%c%c%c-%c%c%c%c %c%c%c%c \n " ,
escape_essid ( info_element - > data ,
info_element - > len ) ,
MAC_ARG ( beacon - > header . addr3 ) ,
( beacon - > capability & ( 1 < < 0xf ) ) ? ' 1 ' : ' 0 ' ,
( beacon - > capability & ( 1 < < 0xe ) ) ? ' 1 ' : ' 0 ' ,
( beacon - > capability & ( 1 < < 0xd ) ) ? ' 1 ' : ' 0 ' ,
( beacon - > capability & ( 1 < < 0xc ) ) ? ' 1 ' : ' 0 ' ,
( beacon - > capability & ( 1 < < 0xb ) ) ? ' 1 ' : ' 0 ' ,
( beacon - > capability & ( 1 < < 0xa ) ) ? ' 1 ' : ' 0 ' ,
( beacon - > capability & ( 1 < < 0x9 ) ) ? ' 1 ' : ' 0 ' ,
( beacon - > capability & ( 1 < < 0x8 ) ) ? ' 1 ' : ' 0 ' ,
( beacon - > capability & ( 1 < < 0x7 ) ) ? ' 1 ' : ' 0 ' ,
( beacon - > capability & ( 1 < < 0x6 ) ) ? ' 1 ' : ' 0 ' ,
( beacon - > capability & ( 1 < < 0x5 ) ) ? ' 1 ' : ' 0 ' ,
( beacon - > capability & ( 1 < < 0x4 ) ) ? ' 1 ' : ' 0 ' ,
( beacon - > capability & ( 1 < < 0x3 ) ) ? ' 1 ' : ' 0 ' ,
( beacon - > capability & ( 1 < < 0x2 ) ) ? ' 1 ' : ' 0 ' ,
( beacon - > capability & ( 1 < < 0x1 ) ) ? ' 1 ' : ' 0 ' ,
( beacon - > capability & ( 1 < < 0x0 ) ) ? ' 1 ' : ' 0 ' ) ;
2005-05-13 06:48:20 +04:00
if ( ieee80211_network_init ( ieee , beacon , & network , stats ) ) {
IEEE80211_DEBUG_SCAN ( " Dropped '%s' ( " MAC_FMT " ) via %s. \n " ,
escape_essid ( info_element - > data ,
info_element - > len ) ,
MAC_ARG ( beacon - > header . addr3 ) ,
2005-09-21 20:54:47 +04:00
is_beacon ( le16_to_cpu
( beacon - > header .
frame_ctl ) ) ?
" BEACON " : " PROBE RESPONSE " ) ;
2005-05-13 06:48:20 +04:00
return ;
}
/* The network parsed correctly -- so now we scan our known networks
* to see if we can find it in our list .
*
* NOTE : This search is definitely not optimized . Once its doing
* the " right thing " we ' ll optimize it for efficiency if
* necessary */
/* Search for this entry in the list and update it if it is
* already there . */
spin_lock_irqsave ( & ieee - > lock , flags ) ;
list_for_each_entry ( target , & ieee - > network_list , list ) {
if ( is_same_network ( target , & network ) )
break ;
if ( ( oldest = = NULL ) | |
( target - > last_scanned < oldest - > last_scanned ) )
oldest = target ;
}
/* If we didn't find a match, then get a new network slot to initialize
* with this beacon ' s information */
if ( & target - > list = = & ieee - > network_list ) {
if ( list_empty ( & ieee - > network_free_list ) ) {
/* If there are no more slots, expire the oldest */
list_del ( & oldest - > list ) ;
target = oldest ;
IEEE80211_DEBUG_SCAN ( " Expired '%s' ( " MAC_FMT " ) from "
" network list. \n " ,
escape_essid ( target - > ssid ,
target - > ssid_len ) ,
MAC_ARG ( target - > bssid ) ) ;
} else {
/* Otherwise just pull from the free list */
target = list_entry ( ieee - > network_free_list . next ,
struct ieee80211_network , list ) ;
list_del ( ieee - > network_free_list . next ) ;
}
# ifdef CONFIG_IEEE80211_DEBUG
IEEE80211_DEBUG_SCAN ( " Adding '%s' ( " MAC_FMT " ) via %s. \n " ,
escape_essid ( network . ssid ,
network . ssid_len ) ,
MAC_ARG ( network . bssid ) ,
2005-09-21 20:54:47 +04:00
is_beacon ( le16_to_cpu
( beacon - > header .
frame_ctl ) ) ?
" BEACON " : " PROBE RESPONSE " ) ;
2005-05-13 06:48:20 +04:00
# endif
memcpy ( target , & network , sizeof ( * target ) ) ;
list_add_tail ( & target - > list , & ieee - > network_list ) ;
} else {
IEEE80211_DEBUG_SCAN ( " Updating '%s' ( " MAC_FMT " ) via %s. \n " ,
escape_essid ( target - > ssid ,
target - > ssid_len ) ,
MAC_ARG ( target - > bssid ) ,
2005-09-21 20:54:47 +04:00
is_beacon ( le16_to_cpu
( beacon - > header .
frame_ctl ) ) ?
" BEACON " : " PROBE RESPONSE " ) ;
2005-05-13 06:48:20 +04:00
update_network ( target , & network ) ;
}
spin_unlock_irqrestore ( & ieee - > lock , flags ) ;
2005-09-21 20:54:47 +04:00
if ( is_beacon ( le16_to_cpu ( beacon - > header . frame_ctl ) ) ) {
if ( ieee - > handle_beacon ! = NULL )
ieee - > handle_beacon ( dev , beacon , & network ) ;
} else {
if ( ieee - > handle_probe_response ! = NULL )
ieee - > handle_probe_response ( dev , beacon , & network ) ;
}
2005-05-13 06:48:20 +04:00
}
void ieee80211_rx_mgt ( struct ieee80211_device * ieee ,
2005-09-21 20:54:36 +04:00
struct ieee80211_hdr_4addr * header ,
2005-05-13 06:48:20 +04:00
struct ieee80211_rx_stats * stats )
{
2005-09-14 02:25:51 +04:00
switch ( WLAN_FC_GET_STYPE ( le16_to_cpu ( header - > frame_ctl ) ) ) {
2005-05-13 06:48:20 +04:00
case IEEE80211_STYPE_ASSOC_RESP :
IEEE80211_DEBUG_MGMT ( " received ASSOCIATION RESPONSE (%d) \n " ,
2005-09-14 02:25:51 +04:00
WLAN_FC_GET_STYPE ( le16_to_cpu
( header - > frame_ctl ) ) ) ;
2005-09-21 20:56:33 +04:00
ieee80211_handle_assoc_resp ( ieee ,
( struct ieee80211_assoc_response * )
header , stats ) ;
2005-05-13 06:48:20 +04:00
break ;
case IEEE80211_STYPE_REASSOC_RESP :
IEEE80211_DEBUG_MGMT ( " received REASSOCIATION RESPONSE (%d) \n " ,
2005-09-14 02:25:51 +04:00
WLAN_FC_GET_STYPE ( le16_to_cpu
( header - > frame_ctl ) ) ) ;
2005-05-13 06:48:20 +04:00
break ;
2005-09-21 20:58:29 +04:00
case IEEE80211_STYPE_PROBE_REQ :
IEEE80211_DEBUG_MGMT ( " recieved auth (%d) \n " ,
WLAN_FC_GET_STYPE ( le16_to_cpu
( header - > frame_ctl ) ) ) ;
if ( ieee - > handle_probe_request ! = NULL )
ieee - > handle_probe_request ( ieee - > dev ,
( struct
ieee80211_probe_request * )
header , stats ) ;
break ;
2005-05-13 06:48:20 +04:00
case IEEE80211_STYPE_PROBE_RESP :
IEEE80211_DEBUG_MGMT ( " received PROBE RESPONSE (%d) \n " ,
2005-09-14 02:25:51 +04:00
WLAN_FC_GET_STYPE ( le16_to_cpu
( header - > frame_ctl ) ) ) ;
2005-05-13 06:48:20 +04:00
IEEE80211_DEBUG_SCAN ( " Probe response \n " ) ;
2005-09-07 08:48:31 +04:00
ieee80211_process_probe_response ( ieee ,
( struct
ieee80211_probe_response * )
header , stats ) ;
2005-05-13 06:48:20 +04:00
break ;
case IEEE80211_STYPE_BEACON :
IEEE80211_DEBUG_MGMT ( " received BEACON (%d) \n " ,
2005-09-14 02:25:51 +04:00
WLAN_FC_GET_STYPE ( le16_to_cpu
( header - > frame_ctl ) ) ) ;
2005-05-13 06:48:20 +04:00
IEEE80211_DEBUG_SCAN ( " Beacon \n " ) ;
2005-09-07 08:48:31 +04:00
ieee80211_process_probe_response ( ieee ,
( struct
ieee80211_probe_response * )
header , stats ) ;
2005-05-13 06:48:20 +04:00
break ;
2005-09-21 20:54:47 +04:00
case IEEE80211_STYPE_AUTH :
IEEE80211_DEBUG_MGMT ( " recieved auth (%d) \n " ,
WLAN_FC_GET_STYPE ( le16_to_cpu
( header - > frame_ctl ) ) ) ;
if ( ieee - > handle_auth ! = NULL )
ieee - > handle_auth ( ieee - > dev ,
( struct ieee80211_auth * ) header ) ;
break ;
case IEEE80211_STYPE_DISASSOC :
if ( ieee - > handle_disassoc ! = NULL )
ieee - > handle_disassoc ( ieee - > dev ,
( struct ieee80211_disassoc * )
header ) ;
break ;
2005-05-13 06:48:20 +04:00
default :
IEEE80211_DEBUG_MGMT ( " received UNKNOWN (%d) \n " ,
2005-09-14 02:25:51 +04:00
WLAN_FC_GET_STYPE ( le16_to_cpu
( header - > frame_ctl ) ) ) ;
2005-05-13 06:48:20 +04:00
IEEE80211_WARNING ( " %s: Unknown management packet: %d \n " ,
ieee - > dev - > name ,
2005-09-14 02:25:51 +04:00
WLAN_FC_GET_STYPE ( le16_to_cpu
( header - > frame_ctl ) ) ) ;
2005-05-13 06:48:20 +04:00
break ;
}
}
EXPORT_SYMBOL ( ieee80211_rx_mgt ) ;
EXPORT_SYMBOL ( ieee80211_rx ) ;