2007-07-10 21:29:38 +04:00
/*
* Radiotap parser
*
* Copyright 2007 Andy Green < andy @ warmcat . com >
*/
# include <net/cfg80211.h>
# include <net/ieee80211_radiotap.h>
# include <asm/unaligned.h>
/* function prototypes and related defs are in include/net/cfg80211.h */
/**
* ieee80211_radiotap_iterator_init - radiotap parser iterator initialization
* @ iterator : radiotap_iterator to initialize
* @ radiotap_header : radiotap header to parse
* @ max_length : total length we can parse into ( eg , whole packet length )
*
* Returns : 0 or a negative error code if there is a problem .
*
* This function initializes an opaque iterator struct which can then
* be passed to ieee80211_radiotap_iterator_next ( ) to visit every radiotap
* argument which is present in the header . It knows about extended
* present headers and handles them .
*
* How to use :
* call __ieee80211_radiotap_iterator_init ( ) to init a semi - opaque iterator
* struct ieee80211_radiotap_iterator ( no need to init the struct beforehand )
* checking for a good 0 return code . Then loop calling
* __ieee80211_radiotap_iterator_next ( ) . . . it returns either 0 ,
* - ENOENT if there are no more args to parse , or - EINVAL if there is a problem .
* The iterator ' s @ this_arg member points to the start of the argument
* associated with the current argument index that is present , which can be
* found in the iterator ' s @ this_arg_index member . This arg index corresponds
* to the IEEE80211_RADIOTAP_ . . . defines .
*
* Radiotap header length :
* You can find the CPU - endian total radiotap header length in
* iterator - > max_length after executing ieee80211_radiotap_iterator_init ( )
* successfully .
*
* Alignment Gotcha :
* You must take care when dereferencing iterator . this_arg
* for multibyte types . . . the pointer is not aligned . Use
* get_unaligned ( ( type * ) iterator . this_arg ) to dereference
* iterator . this_arg for type " type " safely on all arches .
*
* Example code :
* See Documentation / networking / radiotap - headers . txt
*/
int ieee80211_radiotap_iterator_init (
struct ieee80211_radiotap_iterator * iterator ,
struct ieee80211_radiotap_header * radiotap_header ,
int max_length )
{
/* Linux only supports version 0 radiotap format */
if ( radiotap_header - > it_version )
return - EINVAL ;
/* sanity check for allowed length and radiotap length field */
if ( max_length < le16_to_cpu ( get_unaligned ( & radiotap_header - > it_len ) ) )
return - EINVAL ;
iterator - > rtheader = radiotap_header ;
iterator - > max_length = le16_to_cpu ( get_unaligned (
& radiotap_header - > it_len ) ) ;
iterator - > arg_index = 0 ;
iterator - > bitmap_shifter = le32_to_cpu ( get_unaligned (
& radiotap_header - > it_present ) ) ;
iterator - > arg = ( u8 * ) radiotap_header + sizeof ( * radiotap_header ) ;
iterator - > this_arg = NULL ;
/* find payload start allowing for extended bitmap(s) */
if ( unlikely ( iterator - > bitmap_shifter & ( 1 < < IEEE80211_RADIOTAP_EXT ) ) ) {
while ( le32_to_cpu ( get_unaligned ( ( __le32 * ) iterator - > arg ) ) &
( 1 < < IEEE80211_RADIOTAP_EXT ) ) {
iterator - > arg + = sizeof ( u32 ) ;
/*
* check for insanity where the present bitmaps
* keep claiming to extend up to or even beyond the
* stated radiotap header length
*/
if ( ( ( ulong ) iterator - > arg -
( ulong ) iterator - > rtheader ) > iterator - > max_length )
return - EINVAL ;
}
iterator - > arg + = sizeof ( u32 ) ;
/*
* no need to check again for blowing past stated radiotap
* header length , because ieee80211_radiotap_iterator_next
* checks it before it is dereferenced
*/
}
/* we are all initialized happily */
return 0 ;
}
EXPORT_SYMBOL ( ieee80211_radiotap_iterator_init ) ;
/**
* ieee80211_radiotap_iterator_next - return next radiotap parser iterator arg
* @ iterator : radiotap_iterator to move to next arg ( if any )
*
* Returns : 0 if there is an argument to handle ,
* - ENOENT if there are no more args or - EINVAL
* if there is something else wrong .
*
* This function provides the next radiotap arg index ( IEEE80211_RADIOTAP_ * )
* in @ this_arg_index and sets @ this_arg to point to the
* payload for the field . It takes care of alignment handling and extended
* present fields . @ this_arg can be changed by the caller ( eg ,
* incremented to move inside a compound argument like
* IEEE80211_RADIOTAP_CHANNEL ) . The args pointed to are in
* little - endian format whatever the endianess of your CPU .
*
* Alignment Gotcha :
* You must take care when dereferencing iterator . this_arg
* for multibyte types . . . the pointer is not aligned . Use
* get_unaligned ( ( type * ) iterator . this_arg ) to dereference
* iterator . this_arg for type " type " safely on all arches .
*/
int ieee80211_radiotap_iterator_next (
struct ieee80211_radiotap_iterator * iterator )
{
/*
* small length lookup table for all radiotap types we heard of
* starting from b0 in the bitmap , so we can walk the payload
* area of the radiotap header
*
* There is a requirement to pad args , so that args
* of a given length must begin at a boundary of that length
* - - but note that compound args are allowed ( eg , 2 x u16
* for IEEE80211_RADIOTAP_CHANNEL ) so total arg length is not
* a reliable indicator of alignment requirement .
*
* upper nybble : content alignment for arg
* lower nybble : content length for arg
*/
static const u8 rt_sizes [ ] = {
[ IEEE80211_RADIOTAP_TSFT ] = 0x88 ,
[ IEEE80211_RADIOTAP_FLAGS ] = 0x11 ,
[ IEEE80211_RADIOTAP_RATE ] = 0x11 ,
[ IEEE80211_RADIOTAP_CHANNEL ] = 0x24 ,
[ IEEE80211_RADIOTAP_FHSS ] = 0x22 ,
[ IEEE80211_RADIOTAP_DBM_ANTSIGNAL ] = 0x11 ,
[ IEEE80211_RADIOTAP_DBM_ANTNOISE ] = 0x11 ,
[ IEEE80211_RADIOTAP_LOCK_QUALITY ] = 0x22 ,
[ IEEE80211_RADIOTAP_TX_ATTENUATION ] = 0x22 ,
[ IEEE80211_RADIOTAP_DB_TX_ATTENUATION ] = 0x22 ,
[ IEEE80211_RADIOTAP_DBM_TX_POWER ] = 0x11 ,
[ IEEE80211_RADIOTAP_ANTENNA ] = 0x11 ,
[ IEEE80211_RADIOTAP_DB_ANTSIGNAL ] = 0x11 ,
2007-08-13 16:04:30 +04:00
[ IEEE80211_RADIOTAP_DB_ANTNOISE ] = 0x11 ,
[ IEEE80211_RADIOTAP_RX_FLAGS ] = 0x22 ,
[ IEEE80211_RADIOTAP_TX_FLAGS ] = 0x22 ,
[ IEEE80211_RADIOTAP_RTS_RETRIES ] = 0x11 ,
[ IEEE80211_RADIOTAP_DATA_RETRIES ] = 0x11 ,
2007-07-10 21:29:38 +04:00
/*
* add more here as they are defined in
* include / net / ieee80211_radiotap . h
*/
} ;
/*
* for every radiotap entry we can at
* least skip ( by knowing the length ) . . .
*/
while ( iterator - > arg_index < sizeof ( rt_sizes ) ) {
int hit = 0 ;
int pad ;
if ( ! ( iterator - > bitmap_shifter & 1 ) )
goto next_entry ; /* arg not present */
/*
* arg is present , account for alignment padding
* 8 - bit args can be at any alignment
* 16 - bit args must start on 16 - bit boundary
* 32 - bit args must start on 32 - bit boundary
* 64 - bit args must start on 64 - bit boundary
*
* note that total arg size can differ from alignment of
* elements inside arg , so we use upper nybble of length
* table to base alignment on
*
* also note : these alignments are * * relative to the
* start of the radiotap header * * . There is no guarantee
* that the radiotap header itself is aligned on any
* kind of boundary .
*
* the above is why get_unaligned ( ) is used to dereference
* multibyte elements from the radiotap area
*/
pad = ( ( ( ulong ) iterator - > arg ) -
( ( ulong ) iterator - > rtheader ) ) &
( ( rt_sizes [ iterator - > arg_index ] > > 4 ) - 1 ) ;
if ( pad )
iterator - > arg + =
( rt_sizes [ iterator - > arg_index ] > > 4 ) - pad ;
/*
* this is what we will return to user , but we need to
* move on first so next call has something fresh to test
*/
iterator - > this_arg_index = iterator - > arg_index ;
iterator - > this_arg = iterator - > arg ;
hit = 1 ;
/* internally move on the size of this arg */
iterator - > arg + = rt_sizes [ iterator - > arg_index ] & 0x0f ;
/*
* check for insanity where we are given a bitmap that
* claims to have more arg content than the length of the
* radiotap section . We will normally end up equalling this
* max_length on the last arg , never exceeding it .
*/
if ( ( ( ulong ) iterator - > arg - ( ulong ) iterator - > rtheader ) >
iterator - > max_length )
return - EINVAL ;
next_entry :
iterator - > arg_index + + ;
if ( unlikely ( ( iterator - > arg_index & 31 ) = = 0 ) ) {
/* completed current u32 bitmap */
if ( iterator - > bitmap_shifter & 1 ) {
/* b31 was set, there is more */
/* move to next u32 bitmap */
iterator - > bitmap_shifter = le32_to_cpu (
get_unaligned ( iterator - > next_bitmap ) ) ;
iterator - > next_bitmap + + ;
} else
/* no more bitmaps: end */
iterator - > arg_index = sizeof ( rt_sizes ) ;
} else /* just try the next bit */
iterator - > bitmap_shifter > > = 1 ;
/* if we found a valid arg earlier, return it now */
if ( hit )
return 0 ;
}
/* we don't know how to handle any more args, we're done */
return - ENOENT ;
}
EXPORT_SYMBOL ( ieee80211_radiotap_iterator_next ) ;