2008-02-23 15:17:15 +01:00
/*
* Copyright ( c ) 2008 open80211s Ltd .
* Author : Luis Carlos Cobo < luisca @ cozybit . com >
*
* 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 .
*/
# include "mesh.h"
# define TEST_FRAME_LEN 8192
# define MAX_METRIC 0xffffffff
# define ARITH_SHIFT 8
/* Number of frames buffered per destination for unresolved destinations */
# define MESH_FRAME_QUEUE_LEN 10
# define MAX_PREQ_QUEUE_LEN 64
/* Destination only */
# define MP_F_DO 0x1
/* Reply and forward */
# define MP_F_RF 0x2
2008-02-29 17:07:54 -08:00
static inline u32 u32_field_get ( u8 * preq_elem , int offset , bool ae )
{
if ( ae )
offset + = 6 ;
2008-05-01 22:19:33 -07:00
return get_unaligned_le32 ( preq_elem + offset ) ;
2008-02-29 17:07:54 -08:00
}
2008-02-23 15:17:15 +01:00
/* HWMP IE processing macros */
2008-02-29 17:07:54 -08:00
# define AE_F (1<<6)
# define AE_F_SET(x) (*x & AE_F)
# define PREQ_IE_FLAGS(x) (*(x))
# define PREQ_IE_HOPCOUNT(x) (*(x + 1))
# define PREQ_IE_TTL(x) (*(x + 2))
# define PREQ_IE_PREQ_ID(x) u32_field_get(x, 3, 0)
# define PREQ_IE_ORIG_ADDR(x) (x + 7)
# define PREQ_IE_ORIG_DSN(x) u32_field_get(x, 13, 0);
# define PREQ_IE_LIFETIME(x) u32_field_get(x, 17, AE_F_SET(x));
# define PREQ_IE_METRIC(x) u32_field_get(x, 21, AE_F_SET(x));
# define PREQ_IE_DST_F(x) (*(AE_F_SET(x) ? x + 32 : x + 26))
# define PREQ_IE_DST_ADDR(x) (AE_F_SET(x) ? x + 33 : x + 27)
# define PREQ_IE_DST_DSN(x) u32_field_get(x, 33, AE_F_SET(x));
# define PREP_IE_FLAGS(x) PREQ_IE_FLAGS(x)
# define PREP_IE_HOPCOUNT(x) PREQ_IE_HOPCOUNT(x)
# define PREP_IE_TTL(x) PREQ_IE_TTL(x)
# define PREP_IE_ORIG_ADDR(x) (x + 3)
# define PREP_IE_ORIG_DSN(x) u32_field_get(x, 9, 0);
# define PREP_IE_LIFETIME(x) u32_field_get(x, 13, AE_F_SET(x));
# define PREP_IE_METRIC(x) u32_field_get(x, 17, AE_F_SET(x));
# define PREP_IE_DST_ADDR(x) (AE_F_SET(x) ? x + 27 : x + 21)
# define PREP_IE_DST_DSN(x) u32_field_get(x, 27, AE_F_SET(x));
# define PERR_IE_DST_ADDR(x) (x + 2)
# define PERR_IE_DST_DSN(x) u32_field_get(x, 8, 0);
2008-02-23 15:17:15 +01:00
# define TU_TO_EXP_TIME(x) (jiffies + msecs_to_jiffies(x * 1024 / 1000))
# define MSEC_TO_TU(x) (x*1000 / 1024)
# define DSN_GT(x, y) ((long) (y) - (long) (x) < 0)
# define DSN_LT(x, y) ((long) (x) - (long) (y) < 0)
# define net_traversal_jiffies(s) \
2008-09-11 00:01:49 +02:00
msecs_to_jiffies ( s - > u . mesh . mshcfg . dot11MeshHWMPnetDiameterTraversalTime )
2008-02-23 15:17:15 +01:00
# define default_lifetime(s) \
2008-09-11 00:01:49 +02:00
MSEC_TO_TU ( s - > u . mesh . mshcfg . dot11MeshHWMPactivePathTimeout )
2008-02-23 15:17:15 +01:00
# define min_preq_int_jiff(s) \
2008-09-11 00:01:49 +02:00
( msecs_to_jiffies ( s - > u . mesh . mshcfg . dot11MeshHWMPpreqMinInterval ) )
# define max_preq_retries(s) (s->u.mesh.mshcfg.dot11MeshHWMPmaxPREQretries)
2008-02-23 15:17:15 +01:00
# define disc_timeout_jiff(s) \
2008-09-11 00:01:49 +02:00
msecs_to_jiffies ( sdata - > u . mesh . mshcfg . min_discovery_timeout )
2008-02-23 15:17:15 +01:00
enum mpath_frame_type {
MPATH_PREQ = 0 ,
MPATH_PREP ,
MPATH_PERR
} ;
static int mesh_path_sel_frame_tx ( enum mpath_frame_type action , u8 flags ,
u8 * orig_addr , __le32 orig_dsn , u8 dst_flags , u8 * dst ,
__le32 dst_dsn , u8 * da , u8 hop_count , u8 ttl , __le32 lifetime ,
2008-08-03 12:04:37 +12:00
__le32 metric , __le32 preq_id , struct ieee80211_sub_if_data * sdata )
2008-02-23 15:17:15 +01:00
{
2008-08-03 12:04:37 +12:00
struct ieee80211_local * local = sdata - > local ;
2008-02-23 15:17:15 +01:00
struct sk_buff * skb = dev_alloc_skb ( local - > hw . extra_tx_headroom + 400 ) ;
struct ieee80211_mgmt * mgmt ;
u8 * pos ;
int ie_len ;
if ( ! skb )
return - 1 ;
skb_reserve ( skb , local - > hw . extra_tx_headroom ) ;
/* 25 is the size of the common mgmt part (24) plus the size of the
* common action part ( 1 )
*/
mgmt = ( struct ieee80211_mgmt * )
skb_put ( skb , 25 + sizeof ( mgmt - > u . action . u . mesh_action ) ) ;
memset ( mgmt , 0 , 25 + sizeof ( mgmt - > u . action . u . mesh_action ) ) ;
2008-07-15 18:44:13 -07:00
mgmt - > frame_control = cpu_to_le16 ( IEEE80211_FTYPE_MGMT |
IEEE80211_STYPE_ACTION ) ;
2008-02-23 15:17:15 +01:00
memcpy ( mgmt - > da , da , ETH_ALEN ) ;
2008-08-03 12:04:37 +12:00
memcpy ( mgmt - > sa , sdata - > dev - > dev_addr , ETH_ALEN ) ;
2008-02-23 15:17:15 +01:00
/* BSSID is left zeroed, wildcard value */
mgmt - > u . action . category = MESH_PATH_SEL_CATEGORY ;
mgmt - > u . action . u . mesh_action . action_code = action ;
switch ( action ) {
case MPATH_PREQ :
ie_len = 37 ;
pos = skb_put ( skb , 2 + ie_len ) ;
* pos + + = WLAN_EID_PREQ ;
break ;
case MPATH_PREP :
ie_len = 31 ;
pos = skb_put ( skb , 2 + ie_len ) ;
* pos + + = WLAN_EID_PREP ;
break ;
default :
2008-05-06 12:52:07 +02:00
kfree_skb ( skb ) ;
2008-02-23 15:17:15 +01:00
return - ENOTSUPP ;
break ;
}
* pos + + = ie_len ;
* pos + + = flags ;
* pos + + = hop_count ;
* pos + + = ttl ;
if ( action = = MPATH_PREQ ) {
memcpy ( pos , & preq_id , 4 ) ;
pos + = 4 ;
}
memcpy ( pos , orig_addr , ETH_ALEN ) ;
pos + = ETH_ALEN ;
memcpy ( pos , & orig_dsn , 4 ) ;
pos + = 4 ;
memcpy ( pos , & lifetime , 4 ) ;
pos + = 4 ;
memcpy ( pos , & metric , 4 ) ;
pos + = 4 ;
if ( action = = MPATH_PREQ ) {
/* destination count */
* pos + + = 1 ;
* pos + + = dst_flags ;
}
memcpy ( pos , dst , ETH_ALEN ) ;
pos + = ETH_ALEN ;
memcpy ( pos , & dst_dsn , 4 ) ;
2009-01-08 13:32:07 +02:00
ieee80211_tx_skb ( sdata , skb , 1 ) ;
2008-02-23 15:17:15 +01:00
return 0 ;
}
/**
* mesh_send_path error - Sends a PERR mesh management frame
*
* @ dst : broken destination
* @ dst_dsn : dsn of the broken destination
* @ ra : node this frame is addressed to
*/
int mesh_path_error_tx ( u8 * dst , __le32 dst_dsn , u8 * ra ,
2008-08-03 12:04:37 +12:00
struct ieee80211_sub_if_data * sdata )
2008-02-23 15:17:15 +01:00
{
2008-08-03 12:04:37 +12:00
struct ieee80211_local * local = sdata - > local ;
2008-02-23 15:17:15 +01:00
struct sk_buff * skb = dev_alloc_skb ( local - > hw . extra_tx_headroom + 400 ) ;
struct ieee80211_mgmt * mgmt ;
u8 * pos ;
int ie_len ;
if ( ! skb )
return - 1 ;
skb_reserve ( skb , local - > hw . extra_tx_headroom ) ;
/* 25 is the size of the common mgmt part (24) plus the size of the
* common action part ( 1 )
*/
mgmt = ( struct ieee80211_mgmt * )
skb_put ( skb , 25 + sizeof ( mgmt - > u . action . u . mesh_action ) ) ;
memset ( mgmt , 0 , 25 + sizeof ( mgmt - > u . action . u . mesh_action ) ) ;
2008-07-15 18:44:13 -07:00
mgmt - > frame_control = cpu_to_le16 ( IEEE80211_FTYPE_MGMT |
IEEE80211_STYPE_ACTION ) ;
2008-02-23 15:17:15 +01:00
memcpy ( mgmt - > da , ra , ETH_ALEN ) ;
2008-08-03 12:04:37 +12:00
memcpy ( mgmt - > sa , sdata - > dev - > dev_addr , ETH_ALEN ) ;
2008-02-23 15:17:15 +01:00
/* BSSID is left zeroed, wildcard value */
mgmt - > u . action . category = MESH_PATH_SEL_CATEGORY ;
mgmt - > u . action . u . mesh_action . action_code = MPATH_PERR ;
ie_len = 12 ;
pos = skb_put ( skb , 2 + ie_len ) ;
* pos + + = WLAN_EID_PERR ;
* pos + + = ie_len ;
/* mode flags, reserved */
* pos + + = 0 ;
/* number of destinations */
* pos + + = 1 ;
memcpy ( pos , dst , ETH_ALEN ) ;
pos + = ETH_ALEN ;
memcpy ( pos , & dst_dsn , 4 ) ;
2009-01-08 13:32:07 +02:00
ieee80211_tx_skb ( sdata , skb , 1 ) ;
2008-02-23 15:17:15 +01:00
return 0 ;
}
static u32 airtime_link_metric_get ( struct ieee80211_local * local ,
struct sta_info * sta )
{
struct ieee80211_supported_band * sband ;
/* This should be adjusted for each device */
int device_constant = 1 < < ARITH_SHIFT ;
int test_frame_len = TEST_FRAME_LEN < < ARITH_SHIFT ;
int s_unit = 1 < < ARITH_SHIFT ;
int rate , err ;
u32 tx_time , estimated_retx ;
u64 result ;
sband = local - > hw . wiphy - > bands [ local - > hw . conf . channel - > band ] ;
if ( sta - > fail_avg > = 100 )
return MAX_METRIC ;
2008-10-21 12:40:02 +02:00
if ( sta - > last_tx_rate . flags & IEEE80211_TX_RC_MCS )
return MAX_METRIC ;
2008-02-23 15:17:15 +01:00
err = ( sta - > fail_avg < < ARITH_SHIFT ) / 100 ;
/* bitrate is in units of 100 Kbps, while we need rate in units of
* 1 Mbps . This will be corrected on tx_time computation .
*/
2008-10-21 12:40:02 +02:00
rate = sband - > bitrates [ sta - > last_tx_rate . idx ] . bitrate ;
2008-02-23 15:17:15 +01:00
tx_time = ( device_constant + 10 * test_frame_len / rate ) ;
estimated_retx = ( ( 1 < < ( 2 * ARITH_SHIFT ) ) / ( s_unit - err ) ) ;
result = ( tx_time * estimated_retx ) > > ( 2 * ARITH_SHIFT ) ;
return ( u32 ) result ;
}
/**
* hwmp_route_info_get - Update routing info to originator and transmitter
*
2008-08-03 12:04:37 +12:00
* @ sdata : local mesh subif
2008-02-23 15:17:15 +01:00
* @ mgmt : mesh management frame
* @ hwmp_ie : hwmp information element ( PREP or PREQ )
*
* This function updates the path routing information to the originator and the
* transmitter of a HWMP PREQ or PREP fram .
*
* Returns : metric to frame originator or 0 if the frame should not be further
* processed
*
* Notes : this function is the only place ( besides user - provided info ) where
* path routing information is updated .
*/
2008-08-03 12:04:37 +12:00
static u32 hwmp_route_info_get ( struct ieee80211_sub_if_data * sdata ,
2008-02-23 15:17:15 +01:00
struct ieee80211_mgmt * mgmt ,
u8 * hwmp_ie )
{
2008-08-03 12:04:37 +12:00
struct ieee80211_local * local = sdata - > local ;
2008-02-23 15:17:15 +01:00
struct mesh_path * mpath ;
struct sta_info * sta ;
bool fresh_info ;
u8 * orig_addr , * ta ;
u32 orig_dsn , orig_metric ;
unsigned long orig_lifetime , exp_time ;
u32 last_hop_metric , new_metric ;
bool process = true ;
u8 action = mgmt - > u . action . u . mesh_action . action_code ;
rcu_read_lock ( ) ;
sta = sta_info_get ( local , mgmt - > sa ) ;
2008-02-23 15:17:20 +01:00
if ( ! sta ) {
rcu_read_unlock ( ) ;
2008-02-23 15:17:15 +01:00
return 0 ;
2008-02-23 15:17:20 +01:00
}
2008-02-23 15:17:15 +01:00
last_hop_metric = airtime_link_metric_get ( local , sta ) ;
/* Update and check originator routing info */
fresh_info = true ;
switch ( action ) {
case MPATH_PREQ :
orig_addr = PREQ_IE_ORIG_ADDR ( hwmp_ie ) ;
orig_dsn = PREQ_IE_ORIG_DSN ( hwmp_ie ) ;
orig_lifetime = PREQ_IE_LIFETIME ( hwmp_ie ) ;
orig_metric = PREQ_IE_METRIC ( hwmp_ie ) ;
break ;
case MPATH_PREP :
/* Originator here refers to the MP that was the destination in
* the Path Request . The draft refers to that MP as the
* destination address , even though usually it is the origin of
* the PREP frame . We divert from the nomenclature in the draft
* so that we can easily use a single function to gather path
* information from both PREQ and PREP frames .
*/
orig_addr = PREP_IE_ORIG_ADDR ( hwmp_ie ) ;
orig_dsn = PREP_IE_ORIG_DSN ( hwmp_ie ) ;
orig_lifetime = PREP_IE_LIFETIME ( hwmp_ie ) ;
orig_metric = PREP_IE_METRIC ( hwmp_ie ) ;
break ;
default :
2008-02-23 15:17:20 +01:00
rcu_read_unlock ( ) ;
2008-02-23 15:17:15 +01:00
return 0 ;
}
new_metric = orig_metric + last_hop_metric ;
if ( new_metric < orig_metric )
new_metric = MAX_METRIC ;
exp_time = TU_TO_EXP_TIME ( orig_lifetime ) ;
2008-08-03 12:04:37 +12:00
if ( memcmp ( orig_addr , sdata - > dev - > dev_addr , ETH_ALEN ) = = 0 ) {
2008-02-23 15:17:15 +01:00
/* This MP is the originator, we are not interested in this
* frame , except for updating transmitter ' s path info .
*/
process = false ;
fresh_info = false ;
} else {
2008-08-03 12:04:37 +12:00
mpath = mesh_path_lookup ( orig_addr , sdata ) ;
2008-02-23 15:17:15 +01:00
if ( mpath ) {
spin_lock_bh ( & mpath - > state_lock ) ;
if ( mpath - > flags & MESH_PATH_FIXED )
fresh_info = false ;
else if ( ( mpath - > flags & MESH_PATH_ACTIVE ) & &
( mpath - > flags & MESH_PATH_DSN_VALID ) ) {
if ( DSN_GT ( mpath - > dsn , orig_dsn ) | |
( mpath - > dsn = = orig_dsn & &
action = = MPATH_PREQ & &
new_metric > mpath - > metric ) ) {
process = false ;
fresh_info = false ;
}
}
} else {
2008-08-03 12:04:37 +12:00
mesh_path_add ( orig_addr , sdata ) ;
mpath = mesh_path_lookup ( orig_addr , sdata ) ;
2008-02-23 15:17:15 +01:00
if ( ! mpath ) {
rcu_read_unlock ( ) ;
return 0 ;
}
spin_lock_bh ( & mpath - > state_lock ) ;
}
if ( fresh_info ) {
mesh_path_assign_nexthop ( mpath , sta ) ;
mpath - > flags | = MESH_PATH_DSN_VALID ;
mpath - > metric = new_metric ;
mpath - > dsn = orig_dsn ;
mpath - > exp_time = time_after ( mpath - > exp_time , exp_time )
? mpath - > exp_time : exp_time ;
mesh_path_activate ( mpath ) ;
spin_unlock_bh ( & mpath - > state_lock ) ;
mesh_path_tx_pending ( mpath ) ;
/* draft says preq_id should be saved to, but there does
* not seem to be any use for it , skipping by now
*/
} else
spin_unlock_bh ( & mpath - > state_lock ) ;
}
/* Update and check transmitter routing info */
ta = mgmt - > sa ;
if ( memcmp ( orig_addr , ta , ETH_ALEN ) = = 0 )
fresh_info = false ;
else {
fresh_info = true ;
2008-08-03 12:04:37 +12:00
mpath = mesh_path_lookup ( ta , sdata ) ;
2008-02-23 15:17:15 +01:00
if ( mpath ) {
spin_lock_bh ( & mpath - > state_lock ) ;
if ( ( mpath - > flags & MESH_PATH_FIXED ) | |
( ( mpath - > flags & MESH_PATH_ACTIVE ) & &
( last_hop_metric > mpath - > metric ) ) )
fresh_info = false ;
} else {
2008-08-03 12:04:37 +12:00
mesh_path_add ( ta , sdata ) ;
mpath = mesh_path_lookup ( ta , sdata ) ;
2008-02-23 15:17:15 +01:00
if ( ! mpath ) {
rcu_read_unlock ( ) ;
return 0 ;
}
spin_lock_bh ( & mpath - > state_lock ) ;
}
if ( fresh_info ) {
mesh_path_assign_nexthop ( mpath , sta ) ;
mpath - > flags & = ~ MESH_PATH_DSN_VALID ;
mpath - > metric = last_hop_metric ;
mpath - > exp_time = time_after ( mpath - > exp_time , exp_time )
? mpath - > exp_time : exp_time ;
mesh_path_activate ( mpath ) ;
spin_unlock_bh ( & mpath - > state_lock ) ;
mesh_path_tx_pending ( mpath ) ;
} else
spin_unlock_bh ( & mpath - > state_lock ) ;
}
rcu_read_unlock ( ) ;
return process ? new_metric : 0 ;
}
2008-08-03 12:04:37 +12:00
static void hwmp_preq_frame_process ( struct ieee80211_sub_if_data * sdata ,
2008-02-23 15:17:15 +01:00
struct ieee80211_mgmt * mgmt ,
u8 * preq_elem , u32 metric ) {
2008-09-11 00:01:49 +02:00
struct ieee80211_if_mesh * ifmsh = & sdata - > u . mesh ;
2008-02-23 15:17:15 +01:00
struct mesh_path * mpath ;
u8 * dst_addr , * orig_addr ;
u8 dst_flags , ttl ;
u32 orig_dsn , dst_dsn , lifetime ;
bool reply = false ;
bool forward = true ;
/* Update destination DSN, if present */
dst_addr = PREQ_IE_DST_ADDR ( preq_elem ) ;
orig_addr = PREQ_IE_ORIG_ADDR ( preq_elem ) ;
dst_dsn = PREQ_IE_DST_DSN ( preq_elem ) ;
orig_dsn = PREQ_IE_ORIG_DSN ( preq_elem ) ;
dst_flags = PREQ_IE_DST_F ( preq_elem ) ;
2008-08-03 12:04:37 +12:00
if ( memcmp ( dst_addr , sdata - > dev - > dev_addr , ETH_ALEN ) = = 0 ) {
2008-02-23 15:17:15 +01:00
forward = false ;
reply = true ;
metric = 0 ;
2008-09-11 00:01:49 +02:00
if ( time_after ( jiffies , ifmsh - > last_dsn_update +
2008-02-23 15:17:15 +01:00
net_traversal_jiffies ( sdata ) ) | |
2008-09-11 00:01:49 +02:00
time_before ( jiffies , ifmsh - > last_dsn_update ) ) {
dst_dsn = + + ifmsh - > dsn ;
ifmsh - > last_dsn_update = jiffies ;
2008-02-23 15:17:15 +01:00
}
} else {
rcu_read_lock ( ) ;
2008-08-03 12:04:37 +12:00
mpath = mesh_path_lookup ( dst_addr , sdata ) ;
2008-02-23 15:17:15 +01:00
if ( mpath ) {
if ( ( ! ( mpath - > flags & MESH_PATH_DSN_VALID ) ) | |
DSN_LT ( mpath - > dsn , dst_dsn ) ) {
mpath - > dsn = dst_dsn ;
mpath - > flags & = MESH_PATH_DSN_VALID ;
} else if ( ( ! ( dst_flags & MP_F_DO ) ) & &
( mpath - > flags & MESH_PATH_ACTIVE ) ) {
reply = true ;
metric = mpath - > metric ;
dst_dsn = mpath - > dsn ;
if ( dst_flags & MP_F_RF )
dst_flags | = MP_F_DO ;
else
forward = false ;
}
}
rcu_read_unlock ( ) ;
}
if ( reply ) {
lifetime = PREQ_IE_LIFETIME ( preq_elem ) ;
2008-09-11 00:01:49 +02:00
ttl = ifmsh - > mshcfg . dot11MeshTTL ;
2008-02-23 15:17:15 +01:00
if ( ttl ! = 0 )
mesh_path_sel_frame_tx ( MPATH_PREP , 0 , dst_addr ,
2008-02-29 14:30:32 -08:00
cpu_to_le32 ( dst_dsn ) , 0 , orig_addr ,
cpu_to_le32 ( orig_dsn ) , mgmt - > sa , 0 , ttl ,
cpu_to_le32 ( lifetime ) , cpu_to_le32 ( metric ) ,
2008-08-03 12:04:37 +12:00
0 , sdata ) ;
2008-02-23 15:17:15 +01:00
else
2008-09-11 00:01:49 +02:00
ifmsh - > mshstats . dropped_frames_ttl + + ;
2008-02-23 15:17:15 +01:00
}
if ( forward ) {
u32 preq_id ;
u8 hopcount , flags ;
ttl = PREQ_IE_TTL ( preq_elem ) ;
lifetime = PREQ_IE_LIFETIME ( preq_elem ) ;
if ( ttl < = 1 ) {
2008-09-11 00:01:49 +02:00
ifmsh - > mshstats . dropped_frames_ttl + + ;
2008-02-23 15:17:15 +01:00
return ;
}
- - ttl ;
flags = PREQ_IE_FLAGS ( preq_elem ) ;
preq_id = PREQ_IE_PREQ_ID ( preq_elem ) ;
hopcount = PREQ_IE_HOPCOUNT ( preq_elem ) + 1 ;
mesh_path_sel_frame_tx ( MPATH_PREQ , flags , orig_addr ,
2008-02-29 14:30:32 -08:00
cpu_to_le32 ( orig_dsn ) , dst_flags , dst_addr ,
2008-08-03 12:04:37 +12:00
cpu_to_le32 ( dst_dsn ) , sdata - > dev - > broadcast ,
2008-02-29 14:30:32 -08:00
hopcount , ttl , cpu_to_le32 ( lifetime ) ,
cpu_to_le32 ( metric ) , cpu_to_le32 ( preq_id ) ,
2008-08-03 12:04:37 +12:00
sdata ) ;
2008-09-11 00:01:49 +02:00
ifmsh - > mshstats . fwded_frames + + ;
2008-02-23 15:17:15 +01:00
}
}
2008-08-03 12:04:37 +12:00
static void hwmp_prep_frame_process ( struct ieee80211_sub_if_data * sdata ,
2008-02-23 15:17:15 +01:00
struct ieee80211_mgmt * mgmt ,
u8 * prep_elem , u32 metric )
{
struct mesh_path * mpath ;
u8 * dst_addr , * orig_addr ;
u8 ttl , hopcount , flags ;
u8 next_hop [ ETH_ALEN ] ;
u32 dst_dsn , orig_dsn , lifetime ;
/* Note that we divert from the draft nomenclature and denominate
* destination to what the draft refers to as origininator . So in this
* function destnation refers to the final destination of the PREP ,
* which corresponds with the originator of the PREQ which this PREP
* replies
*/
dst_addr = PREP_IE_DST_ADDR ( prep_elem ) ;
2008-08-03 12:04:37 +12:00
if ( memcmp ( dst_addr , sdata - > dev - > dev_addr , ETH_ALEN ) = = 0 )
2008-02-23 15:17:15 +01:00
/* destination, no forwarding required */
return ;
ttl = PREP_IE_TTL ( prep_elem ) ;
if ( ttl < = 1 ) {
2008-09-11 00:01:49 +02:00
sdata - > u . mesh . mshstats . dropped_frames_ttl + + ;
2008-02-23 15:17:15 +01:00
return ;
}
rcu_read_lock ( ) ;
2008-08-03 12:04:37 +12:00
mpath = mesh_path_lookup ( dst_addr , sdata ) ;
2008-02-23 15:17:15 +01:00
if ( mpath )
spin_lock_bh ( & mpath - > state_lock ) ;
else
goto fail ;
if ( ! ( mpath - > flags & MESH_PATH_ACTIVE ) ) {
spin_unlock_bh ( & mpath - > state_lock ) ;
goto fail ;
}
2008-09-11 00:02:02 +02:00
memcpy ( next_hop , mpath - > next_hop - > sta . addr , ETH_ALEN ) ;
2008-02-23 15:17:15 +01:00
spin_unlock_bh ( & mpath - > state_lock ) ;
- - ttl ;
flags = PREP_IE_FLAGS ( prep_elem ) ;
lifetime = PREP_IE_LIFETIME ( prep_elem ) ;
hopcount = PREP_IE_HOPCOUNT ( prep_elem ) + 1 ;
orig_addr = PREP_IE_ORIG_ADDR ( prep_elem ) ;
dst_dsn = PREP_IE_DST_DSN ( prep_elem ) ;
orig_dsn = PREP_IE_ORIG_DSN ( prep_elem ) ;
mesh_path_sel_frame_tx ( MPATH_PREP , flags , orig_addr ,
2008-02-29 14:30:32 -08:00
cpu_to_le32 ( orig_dsn ) , 0 , dst_addr ,
2008-09-11 00:02:02 +02:00
cpu_to_le32 ( dst_dsn ) , mpath - > next_hop - > sta . addr , hopcount , ttl ,
2008-02-29 14:30:32 -08:00
cpu_to_le32 ( lifetime ) , cpu_to_le32 ( metric ) ,
2008-08-03 12:04:37 +12:00
0 , sdata ) ;
2008-02-23 15:17:15 +01:00
rcu_read_unlock ( ) ;
2008-09-11 00:01:49 +02:00
sdata - > u . mesh . mshstats . fwded_frames + + ;
2008-02-23 15:17:15 +01:00
return ;
fail :
rcu_read_unlock ( ) ;
2008-09-11 00:01:49 +02:00
sdata - > u . mesh . mshstats . dropped_frames_no_route + + ;
2008-02-23 15:17:15 +01:00
return ;
}
2008-08-03 12:04:37 +12:00
static void hwmp_perr_frame_process ( struct ieee80211_sub_if_data * sdata ,
2008-02-23 15:17:15 +01:00
struct ieee80211_mgmt * mgmt , u8 * perr_elem )
{
struct mesh_path * mpath ;
u8 * ta , * dst_addr ;
u32 dst_dsn ;
ta = mgmt - > sa ;
dst_addr = PERR_IE_DST_ADDR ( perr_elem ) ;
dst_dsn = PERR_IE_DST_DSN ( perr_elem ) ;
rcu_read_lock ( ) ;
2008-08-03 12:04:37 +12:00
mpath = mesh_path_lookup ( dst_addr , sdata ) ;
2008-02-23 15:17:15 +01:00
if ( mpath ) {
spin_lock_bh ( & mpath - > state_lock ) ;
if ( mpath - > flags & MESH_PATH_ACTIVE & &
2008-09-11 00:02:02 +02:00
memcmp ( ta , mpath - > next_hop - > sta . addr , ETH_ALEN ) = = 0 & &
2008-02-23 15:17:15 +01:00
( ! ( mpath - > flags & MESH_PATH_DSN_VALID ) | |
DSN_GT ( dst_dsn , mpath - > dsn ) ) ) {
mpath - > flags & = ~ MESH_PATH_ACTIVE ;
mpath - > dsn = dst_dsn ;
spin_unlock_bh ( & mpath - > state_lock ) ;
2008-02-29 14:30:32 -08:00
mesh_path_error_tx ( dst_addr , cpu_to_le32 ( dst_dsn ) ,
2008-08-03 12:04:37 +12:00
sdata - > dev - > broadcast , sdata ) ;
2008-02-23 15:17:15 +01:00
} else
spin_unlock_bh ( & mpath - > state_lock ) ;
}
rcu_read_unlock ( ) ;
}
2008-08-03 12:04:37 +12:00
void mesh_rx_path_sel_frame ( struct ieee80211_sub_if_data * sdata ,
2008-02-23 15:17:15 +01:00
struct ieee80211_mgmt * mgmt ,
size_t len )
{
struct ieee802_11_elems elems ;
size_t baselen ;
u32 last_hop_metric ;
2008-09-08 15:41:59 +02:00
/* need action_code */
if ( len < IEEE80211_MIN_ACTION_SIZE + 1 )
return ;
2008-02-23 15:17:15 +01:00
baselen = ( u8 * ) mgmt - > u . action . u . mesh_action . variable - ( u8 * ) mgmt ;
ieee802_11_parse_elems ( mgmt - > u . action . u . mesh_action . variable ,
len - baselen , & elems ) ;
switch ( mgmt - > u . action . u . mesh_action . action_code ) {
case MPATH_PREQ :
if ( ! elems . preq | | elems . preq_len ! = 37 )
/* Right now we support just 1 destination and no AE */
return ;
2008-08-03 12:04:37 +12:00
last_hop_metric = hwmp_route_info_get ( sdata , mgmt , elems . preq ) ;
2008-02-23 15:17:15 +01:00
if ( ! last_hop_metric )
return ;
2008-08-03 12:04:37 +12:00
hwmp_preq_frame_process ( sdata , mgmt , elems . preq , last_hop_metric ) ;
2008-02-23 15:17:15 +01:00
break ;
case MPATH_PREP :
if ( ! elems . prep | | elems . prep_len ! = 31 )
/* Right now we support no AE */
return ;
2008-08-03 12:04:37 +12:00
last_hop_metric = hwmp_route_info_get ( sdata , mgmt , elems . prep ) ;
2008-02-23 15:17:15 +01:00
if ( ! last_hop_metric )
return ;
2008-08-03 12:04:37 +12:00
hwmp_prep_frame_process ( sdata , mgmt , elems . prep , last_hop_metric ) ;
2008-02-23 15:17:15 +01:00
break ;
case MPATH_PERR :
if ( ! elems . perr | | elems . perr_len ! = 12 )
/* Right now we support only one destination per PERR */
return ;
2008-08-03 12:04:37 +12:00
hwmp_perr_frame_process ( sdata , mgmt , elems . perr ) ;
2008-02-23 15:17:15 +01:00
default :
return ;
}
}
/**
* mesh_queue_preq - queue a PREQ to a given destination
*
* @ mpath : mesh path to discover
* @ flags : special attributes of the PREQ to be sent
*
* Locking : the function must be called from within a rcu read lock block .
*
*/
static void mesh_queue_preq ( struct mesh_path * mpath , u8 flags )
{
2008-08-03 12:04:37 +12:00
struct ieee80211_sub_if_data * sdata = mpath - > sdata ;
2008-09-11 00:01:49 +02:00
struct ieee80211_if_mesh * ifmsh = & sdata - > u . mesh ;
2008-02-23 15:17:15 +01:00
struct mesh_preq_queue * preq_node ;
preq_node = kmalloc ( sizeof ( struct mesh_preq_queue ) , GFP_KERNEL ) ;
if ( ! preq_node ) {
printk ( KERN_DEBUG " Mesh HWMP: could not allocate PREQ node \n " ) ;
return ;
}
2008-09-11 00:01:49 +02:00
spin_lock ( & ifmsh - > mesh_preq_queue_lock ) ;
if ( ifmsh - > preq_queue_len = = MAX_PREQ_QUEUE_LEN ) {
spin_unlock ( & ifmsh - > mesh_preq_queue_lock ) ;
2008-02-23 15:17:15 +01:00
kfree ( preq_node ) ;
if ( printk_ratelimit ( ) )
printk ( KERN_DEBUG " Mesh HWMP: PREQ node queue full \n " ) ;
return ;
}
memcpy ( preq_node - > dst , mpath - > dst , ETH_ALEN ) ;
preq_node - > flags = flags ;
2008-09-11 00:01:49 +02:00
list_add_tail ( & preq_node - > list , & ifmsh - > preq_queue . list ) ;
+ + ifmsh - > preq_queue_len ;
spin_unlock ( & ifmsh - > mesh_preq_queue_lock ) ;
2008-02-23 15:17:15 +01:00
2008-09-11 00:01:49 +02:00
if ( time_after ( jiffies , ifmsh - > last_preq + min_preq_int_jiff ( sdata ) ) )
queue_work ( sdata - > local - > hw . workqueue , & ifmsh - > work ) ;
2008-02-23 15:17:15 +01:00
2008-09-11 00:01:49 +02:00
else if ( time_before ( jiffies , ifmsh - > last_preq ) ) {
2008-02-23 15:17:15 +01:00
/* avoid long wait if did not send preqs for a long time
* and jiffies wrapped around
*/
2008-09-11 00:01:49 +02:00
ifmsh - > last_preq = jiffies - min_preq_int_jiff ( sdata ) - 1 ;
queue_work ( sdata - > local - > hw . workqueue , & ifmsh - > work ) ;
2008-02-23 15:17:15 +01:00
} else
2008-09-11 00:01:49 +02:00
mod_timer ( & ifmsh - > mesh_path_timer , ifmsh - > last_preq +
2008-02-23 15:17:15 +01:00
min_preq_int_jiff ( sdata ) ) ;
}
/**
* mesh_path_start_discovery - launch a path discovery from the PREQ queue
*
2008-08-03 12:04:37 +12:00
* @ sdata : local mesh subif
2008-02-23 15:17:15 +01:00
*/
2008-08-03 12:04:37 +12:00
void mesh_path_start_discovery ( struct ieee80211_sub_if_data * sdata )
2008-02-23 15:17:15 +01:00
{
2008-09-11 00:01:49 +02:00
struct ieee80211_if_mesh * ifmsh = & sdata - > u . mesh ;
2008-02-23 15:17:15 +01:00
struct mesh_preq_queue * preq_node ;
struct mesh_path * mpath ;
u8 ttl , dst_flags ;
u32 lifetime ;
2008-09-11 00:01:49 +02:00
spin_lock ( & ifmsh - > mesh_preq_queue_lock ) ;
if ( ! ifmsh - > preq_queue_len | |
time_before ( jiffies , ifmsh - > last_preq +
2008-02-23 15:17:15 +01:00
min_preq_int_jiff ( sdata ) ) ) {
2008-09-11 00:01:49 +02:00
spin_unlock ( & ifmsh - > mesh_preq_queue_lock ) ;
2008-02-23 15:17:15 +01:00
return ;
}
2008-09-11 00:01:49 +02:00
preq_node = list_first_entry ( & ifmsh - > preq_queue . list ,
2008-02-23 15:17:15 +01:00
struct mesh_preq_queue , list ) ;
list_del ( & preq_node - > list ) ;
2008-09-11 00:01:49 +02:00
- - ifmsh - > preq_queue_len ;
spin_unlock ( & ifmsh - > mesh_preq_queue_lock ) ;
2008-02-23 15:17:15 +01:00
rcu_read_lock ( ) ;
2008-08-03 12:04:37 +12:00
mpath = mesh_path_lookup ( preq_node - > dst , sdata ) ;
2008-02-23 15:17:15 +01:00
if ( ! mpath )
goto enddiscovery ;
spin_lock_bh ( & mpath - > state_lock ) ;
if ( preq_node - > flags & PREQ_Q_F_START ) {
if ( mpath - > flags & MESH_PATH_RESOLVING ) {
spin_unlock_bh ( & mpath - > state_lock ) ;
goto enddiscovery ;
} else {
mpath - > flags & = ~ MESH_PATH_RESOLVED ;
mpath - > flags | = MESH_PATH_RESOLVING ;
mpath - > discovery_retries = 0 ;
mpath - > discovery_timeout = disc_timeout_jiff ( sdata ) ;
}
} else if ( ! ( mpath - > flags & MESH_PATH_RESOLVING ) | |
mpath - > flags & MESH_PATH_RESOLVED ) {
mpath - > flags & = ~ MESH_PATH_RESOLVING ;
spin_unlock_bh ( & mpath - > state_lock ) ;
goto enddiscovery ;
}
2008-09-11 00:01:49 +02:00
ifmsh - > last_preq = jiffies ;
2008-02-23 15:17:15 +01:00
2008-09-11 00:01:49 +02:00
if ( time_after ( jiffies , ifmsh - > last_dsn_update +
2008-02-23 15:17:15 +01:00
net_traversal_jiffies ( sdata ) ) | |
2008-09-11 00:01:49 +02:00
time_before ( jiffies , ifmsh - > last_dsn_update ) ) {
+ + ifmsh - > dsn ;
sdata - > u . mesh . last_dsn_update = jiffies ;
2008-02-23 15:17:15 +01:00
}
lifetime = default_lifetime ( sdata ) ;
2008-09-11 00:01:49 +02:00
ttl = sdata - > u . mesh . mshcfg . dot11MeshTTL ;
2008-02-23 15:17:15 +01:00
if ( ttl = = 0 ) {
2008-09-11 00:01:49 +02:00
sdata - > u . mesh . mshstats . dropped_frames_ttl + + ;
2008-02-23 15:17:15 +01:00
spin_unlock_bh ( & mpath - > state_lock ) ;
goto enddiscovery ;
}
if ( preq_node - > flags & PREQ_Q_F_REFRESH )
dst_flags = MP_F_DO ;
else
dst_flags = MP_F_RF ;
spin_unlock_bh ( & mpath - > state_lock ) ;
2008-08-03 12:04:37 +12:00
mesh_path_sel_frame_tx ( MPATH_PREQ , 0 , sdata - > dev - > dev_addr ,
2008-09-11 00:01:49 +02:00
cpu_to_le32 ( ifmsh - > dsn ) , dst_flags , mpath - > dst ,
2008-08-03 12:04:37 +12:00
cpu_to_le32 ( mpath - > dsn ) , sdata - > dev - > broadcast , 0 ,
2008-02-29 14:30:32 -08:00
ttl , cpu_to_le32 ( lifetime ) , 0 ,
2008-09-11 00:01:49 +02:00
cpu_to_le32 ( ifmsh - > preq_id + + ) , sdata ) ;
2008-02-23 15:17:15 +01:00
mod_timer ( & mpath - > timer , jiffies + mpath - > discovery_timeout ) ;
enddiscovery :
rcu_read_unlock ( ) ;
kfree ( preq_node ) ;
}
/**
* ieee80211s_lookup_nexthop - put the appropriate next hop on a mesh frame
*
2008-08-05 19:34:52 +02:00
* @ skb : 802.11 frame to be sent
2008-08-03 12:04:37 +12:00
* @ sdata : network subif the frame will be sent through
2008-02-23 15:17:15 +01:00
*
* Returns : 0 if the next hop was found . Nonzero otherwise . If no next hop is
* found , the function will start a path discovery and queue the frame so it is
* sent when the path is resolved . This means the caller must not free the skb
* in this case .
*/
2008-08-03 12:04:37 +12:00
int mesh_nexthop_lookup ( struct sk_buff * skb ,
struct ieee80211_sub_if_data * sdata )
2008-02-23 15:17:15 +01:00
{
struct sk_buff * skb_to_free = NULL ;
struct mesh_path * mpath ;
2008-08-05 19:34:52 +02:00
struct ieee80211_hdr * hdr = ( struct ieee80211_hdr * ) skb - > data ;
u8 * dst_addr = hdr - > addr3 ;
2008-02-23 15:17:15 +01:00
int err = 0 ;
rcu_read_lock ( ) ;
2008-08-03 12:04:37 +12:00
mpath = mesh_path_lookup ( dst_addr , sdata ) ;
2008-02-23 15:17:15 +01:00
if ( ! mpath ) {
2008-08-03 12:04:37 +12:00
mesh_path_add ( dst_addr , sdata ) ;
mpath = mesh_path_lookup ( dst_addr , sdata ) ;
2008-02-23 15:17:15 +01:00
if ( ! mpath ) {
dev_kfree_skb ( skb ) ;
2008-09-11 00:01:49 +02:00
sdata - > u . mesh . mshstats . dropped_frames_no_route + + ;
2008-02-23 15:17:15 +01:00
err = - ENOSPC ;
goto endlookup ;
}
}
if ( mpath - > flags & MESH_PATH_ACTIVE ) {
if ( time_after ( jiffies , mpath - > exp_time -
2008-09-11 00:01:49 +02:00
msecs_to_jiffies ( sdata - > u . mesh . mshcfg . path_refresh_time ) )
2008-08-03 12:04:37 +12:00
& & ! memcmp ( sdata - > dev - > dev_addr , hdr - > addr4 ,
ETH_ALEN )
2008-02-23 15:17:15 +01:00
& & ! ( mpath - > flags & MESH_PATH_RESOLVING )
& & ! ( mpath - > flags & MESH_PATH_FIXED ) ) {
mesh_queue_preq ( mpath ,
PREQ_Q_F_START | PREQ_Q_F_REFRESH ) ;
}
2008-09-11 00:02:02 +02:00
memcpy ( hdr - > addr1 , mpath - > next_hop - > sta . addr ,
2008-02-23 15:17:15 +01:00
ETH_ALEN ) ;
} else {
if ( ! ( mpath - > flags & MESH_PATH_RESOLVING ) ) {
/* Start discovery only if it is not running yet */
mesh_queue_preq ( mpath , PREQ_Q_F_START ) ;
}
if ( skb_queue_len ( & mpath - > frame_queue ) > =
MESH_FRAME_QUEUE_LEN ) {
skb_to_free = mpath - > frame_queue . next ;
skb_unlink ( skb_to_free , & mpath - > frame_queue ) ;
}
skb_queue_tail ( & mpath - > frame_queue , skb ) ;
if ( skb_to_free )
2008-08-03 12:04:37 +12:00
mesh_path_discard_frame ( skb_to_free , sdata ) ;
2008-02-23 15:17:15 +01:00
err = - ENOENT ;
}
endlookup :
rcu_read_unlock ( ) ;
return err ;
}
void mesh_path_timer ( unsigned long data )
{
struct ieee80211_sub_if_data * sdata ;
struct mesh_path * mpath ;
rcu_read_lock ( ) ;
mpath = ( struct mesh_path * ) data ;
mpath = rcu_dereference ( mpath ) ;
if ( ! mpath )
goto endmpathtimer ;
spin_lock_bh ( & mpath - > state_lock ) ;
2008-08-03 12:04:37 +12:00
sdata = mpath - > sdata ;
2008-02-29 15:04:13 -08:00
if ( mpath - > flags & MESH_PATH_RESOLVED | |
2008-02-23 15:17:15 +01:00
( ! ( mpath - > flags & MESH_PATH_RESOLVING ) ) )
mpath - > flags & = ~ ( MESH_PATH_RESOLVING | MESH_PATH_RESOLVED ) ;
else if ( mpath - > discovery_retries < max_preq_retries ( sdata ) ) {
+ + mpath - > discovery_retries ;
mpath - > discovery_timeout * = 2 ;
mesh_queue_preq ( mpath , 0 ) ;
} else {
mpath - > flags = 0 ;
mpath - > exp_time = jiffies ;
mesh_path_flush_pending ( mpath ) ;
}
spin_unlock_bh ( & mpath - > state_lock ) ;
endmpathtimer :
rcu_read_unlock ( ) ;
}