2008-02-23 17:17:15 +03:00
/*
2009-11-10 02:46:58 +03:00
* Copyright ( c ) 2008 , 2009 open80211s Ltd .
2008-02-23 17:17:15 +03:00
* 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"
2009-11-10 02:46:45 +03:00
# ifdef CONFIG_MAC80211_VERBOSE_MHWMP_DEBUG
# define mhwmp_dbg(fmt, args...) printk(KERN_DEBUG "Mesh HWMP: " fmt, ##args)
# else
# define mhwmp_dbg(fmt, args...) do { (void)(0); } while (0)
# endif
2008-02-23 17:17:15 +03:00
# 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
2009-11-10 02:46:50 +03:00
/* Unknown Sequence Number */
# define MP_F_USN 0x01
/* Reason code Present */
# define MP_F_RCODE 0x02
2008-02-23 17:17:15 +03:00
2009-11-11 03:01:31 +03:00
static void mesh_queue_preq ( struct mesh_path * , u8 ) ;
2008-03-01 04:07:54 +03:00
static inline u32 u32_field_get ( u8 * preq_elem , int offset , bool ae )
{
if ( ae )
offset + = 6 ;
2008-05-02 09:19:33 +04:00
return get_unaligned_le32 ( preq_elem + offset ) ;
2008-03-01 04:07:54 +03:00
}
2009-11-10 02:46:50 +03:00
static inline u32 u16_field_get ( u8 * preq_elem , int offset , bool ae )
{
if ( ae )
offset + = 6 ;
return get_unaligned_le16 ( preq_elem + offset ) ;
}
2008-02-23 17:17:15 +03:00
/* HWMP IE processing macros */
2008-03-01 04:07:54 +03: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)
2009-11-10 02:46:55 +03:00
# define PREQ_IE_ORIG_SN(x) u32_field_get(x, 13, 0);
2008-03-01 04:07:54 +03:00
# 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));
2009-11-10 02:46:55 +03:00
# define PREQ_IE_TARGET_F(x) (*(AE_F_SET(x) ? x + 32 : x + 26))
# define PREQ_IE_TARGET_ADDR(x) (AE_F_SET(x) ? x + 33 : x + 27)
# define PREQ_IE_TARGET_SN(x) u32_field_get(x, 33, AE_F_SET(x));
2008-03-01 04:07:54 +03:00
# 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)
2009-11-10 02:46:55 +03:00
# define PREP_IE_ORIG_SN(x) u32_field_get(x, 9, 0);
2008-03-01 04:07:54 +03:00
# 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));
2009-11-10 02:46:55 +03:00
# define PREP_IE_TARGET_ADDR(x) (AE_F_SET(x) ? x + 27 : x + 21)
# define PREP_IE_TARGET_SN(x) u32_field_get(x, 27, AE_F_SET(x));
2008-03-01 04:07:54 +03:00
2009-11-10 02:46:50 +03:00
# define PERR_IE_TTL(x) (*(x))
2009-11-10 02:46:55 +03:00
# define PERR_IE_TARGET_FLAGS(x) (*(x + 2))
# define PERR_IE_TARGET_ADDR(x) (x + 3)
# define PERR_IE_TARGET_SN(x) u32_field_get(x, 9, 0);
# define PERR_IE_TARGET_RCODE(x) u16_field_get(x, 13, 0);
2008-02-23 17:17:15 +03:00
# define MSEC_TO_TU(x) (x*1000 / 1024)
2009-11-10 02:46:55 +03:00
# define SN_GT(x, y) ((long) (y) - (long) (x) < 0)
# define SN_LT(x, y) ((long) (x) - (long) (y) < 0)
2008-02-23 17:17:15 +03:00
# define net_traversal_jiffies(s) \
2008-09-11 02:01:49 +04:00
msecs_to_jiffies ( s - > u . mesh . mshcfg . dot11MeshHWMPnetDiameterTraversalTime )
2008-02-23 17:17:15 +03:00
# define default_lifetime(s) \
2008-09-11 02:01:49 +04:00
MSEC_TO_TU ( s - > u . mesh . mshcfg . dot11MeshHWMPactivePathTimeout )
2008-02-23 17:17:15 +03:00
# define min_preq_int_jiff(s) \
2008-09-11 02:01:49 +04:00
( msecs_to_jiffies ( s - > u . mesh . mshcfg . dot11MeshHWMPpreqMinInterval ) )
# define max_preq_retries(s) (s->u.mesh.mshcfg.dot11MeshHWMPmaxPREQretries)
2008-02-23 17:17:15 +03:00
# define disc_timeout_jiff(s) \
2008-09-11 02:01:49 +04:00
msecs_to_jiffies ( sdata - > u . mesh . mshcfg . min_discovery_timeout )
2008-02-23 17:17:15 +03:00
enum mpath_frame_type {
MPATH_PREQ = 0 ,
MPATH_PREP ,
2009-11-11 03:01:31 +03:00
MPATH_PERR ,
MPATH_RANN
2008-02-23 17:17:15 +03:00
} ;
2009-11-17 15:34:04 +03:00
static const u8 broadcast_addr [ ETH_ALEN ] = { 0xff , 0xff , 0xff , 0xff , 0xff , 0xff } ;
2008-02-23 17:17:15 +03:00
static int mesh_path_sel_frame_tx ( enum mpath_frame_type action , u8 flags ,
2009-11-10 02:46:55 +03:00
u8 * orig_addr , __le32 orig_sn , u8 target_flags , u8 * target ,
2009-11-17 15:34:04 +03:00
__le32 target_sn , const u8 * da , u8 hop_count , u8 ttl ,
__le32 lifetime , __le32 metric , __le32 preq_id ,
2009-11-10 02:46:55 +03:00
struct ieee80211_sub_if_data * sdata )
2008-02-23 17:17:15 +03:00
{
2008-08-03 04:04:37 +04:00
struct ieee80211_local * local = sdata - > local ;
2008-02-23 17:17:15 +03: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-16 05:44:13 +04:00
mgmt - > frame_control = cpu_to_le16 ( IEEE80211_FTYPE_MGMT |
IEEE80211_STYPE_ACTION ) ;
2008-02-23 17:17:15 +03:00
memcpy ( mgmt - > da , da , ETH_ALEN ) ;
2009-11-25 19:46:19 +03:00
memcpy ( mgmt - > sa , sdata - > vif . addr , ETH_ALEN ) ;
2009-11-11 03:01:31 +03:00
/* BSSID == SA */
2009-11-25 19:46:19 +03:00
memcpy ( mgmt - > bssid , sdata - > vif . addr , ETH_ALEN ) ;
2008-02-23 17:17:15 +03:00
mgmt - > u . action . category = MESH_PATH_SEL_CATEGORY ;
2009-11-10 02:46:44 +03:00
mgmt - > u . action . u . mesh_action . action_code = MESH_PATH_SEL_ACTION ;
2008-02-23 17:17:15 +03:00
switch ( action ) {
case MPATH_PREQ :
2009-11-10 02:46:55 +03:00
mhwmp_dbg ( " sending PREQ to %pM \n " , target ) ;
2008-02-23 17:17:15 +03:00
ie_len = 37 ;
pos = skb_put ( skb , 2 + ie_len ) ;
* pos + + = WLAN_EID_PREQ ;
break ;
case MPATH_PREP :
2009-11-10 02:46:55 +03:00
mhwmp_dbg ( " sending PREP to %pM \n " , target ) ;
2008-02-23 17:17:15 +03:00
ie_len = 31 ;
pos = skb_put ( skb , 2 + ie_len ) ;
* pos + + = WLAN_EID_PREP ;
break ;
2009-11-11 03:01:31 +03:00
case MPATH_RANN :
mhwmp_dbg ( " sending RANN from %pM \n " , orig_addr ) ;
ie_len = sizeof ( struct ieee80211_rann_ie ) ;
pos = skb_put ( skb , 2 + ie_len ) ;
* pos + + = WLAN_EID_RANN ;
break ;
2008-02-23 17:17:15 +03:00
default :
2008-05-06 14:52:07 +04:00
kfree_skb ( skb ) ;
2008-02-23 17:17:15 +03: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 ;
2009-11-10 02:46:55 +03:00
memcpy ( pos , & orig_sn , 4 ) ;
2008-02-23 17:17:15 +03:00
pos + = 4 ;
2009-11-11 03:01:31 +03:00
if ( action ! = MPATH_RANN ) {
memcpy ( pos , & lifetime , 4 ) ;
pos + = 4 ;
}
2008-02-23 17:17:15 +03:00
memcpy ( pos , & metric , 4 ) ;
pos + = 4 ;
if ( action = = MPATH_PREQ ) {
/* destination count */
* pos + + = 1 ;
2009-11-10 02:46:55 +03:00
* pos + + = target_flags ;
2008-02-23 17:17:15 +03:00
}
2009-11-11 03:01:31 +03:00
if ( action ! = MPATH_RANN ) {
2009-11-10 02:46:55 +03:00
memcpy ( pos , target , ETH_ALEN ) ;
2009-11-11 03:01:31 +03:00
pos + = ETH_ALEN ;
2009-11-10 02:46:55 +03:00
memcpy ( pos , & target_sn , 4 ) ;
2009-11-11 03:01:31 +03:00
}
2008-02-23 17:17:15 +03:00
2009-11-18 20:42:05 +03:00
ieee80211_tx_skb ( sdata , skb ) ;
2008-02-23 17:17:15 +03:00
return 0 ;
}
/**
* mesh_send_path error - Sends a PERR mesh management frame
*
2009-11-10 02:46:55 +03:00
* @ target : broken destination
* @ target_sn : SN of the broken destination
* @ target_rcode : reason code for this PERR
2008-02-23 17:17:15 +03:00
* @ ra : node this frame is addressed to
*/
2009-11-10 02:46:55 +03:00
int mesh_path_error_tx ( u8 ttl , u8 * target , __le32 target_sn ,
2009-11-17 15:34:04 +03:00
__le16 target_rcode , const u8 * ra ,
struct ieee80211_sub_if_data * sdata )
2008-02-23 17:17:15 +03:00
{
2008-08-03 04:04:37 +04:00
struct ieee80211_local * local = sdata - > local ;
2008-02-23 17:17:15 +03: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-16 05:44:13 +04:00
mgmt - > frame_control = cpu_to_le16 ( IEEE80211_FTYPE_MGMT |
IEEE80211_STYPE_ACTION ) ;
2008-02-23 17:17:15 +03:00
memcpy ( mgmt - > da , ra , ETH_ALEN ) ;
2009-11-25 19:46:19 +03:00
memcpy ( mgmt - > sa , sdata - > vif . addr , ETH_ALEN ) ;
2008-02-23 17:17:15 +03:00
/* BSSID is left zeroed, wildcard value */
mgmt - > u . action . category = MESH_PATH_SEL_CATEGORY ;
2009-11-10 02:46:46 +03:00
mgmt - > u . action . u . mesh_action . action_code = MESH_PATH_SEL_ACTION ;
2009-11-10 02:46:50 +03:00
ie_len = 15 ;
2008-02-23 17:17:15 +03:00
pos = skb_put ( skb , 2 + ie_len ) ;
* pos + + = WLAN_EID_PERR ;
* pos + + = ie_len ;
2009-11-10 02:46:50 +03:00
/* ttl */
* pos + + = MESH_TTL ;
2008-02-23 17:17:15 +03:00
/* number of destinations */
* pos + + = 1 ;
2009-11-10 02:46:50 +03:00
/*
* flags bit , bit 1 is unset if we know the sequence number and
* bit 2 is set if we have a reason code
*/
* pos = 0 ;
2009-11-10 02:46:55 +03:00
if ( ! target_sn )
2009-11-10 02:46:50 +03:00
* pos | = MP_F_USN ;
2009-11-10 02:46:55 +03:00
if ( target_rcode )
2009-11-10 02:46:50 +03:00
* pos | = MP_F_RCODE ;
pos + + ;
2009-11-10 02:46:55 +03:00
memcpy ( pos , target , ETH_ALEN ) ;
2008-02-23 17:17:15 +03:00
pos + = ETH_ALEN ;
2009-11-10 02:46:55 +03:00
memcpy ( pos , & target_sn , 4 ) ;
2009-11-10 02:46:50 +03:00
pos + = 4 ;
2009-11-10 02:46:55 +03:00
memcpy ( pos , & target_rcode , 2 ) ;
2008-02-23 17:17:15 +03:00
2009-11-18 20:42:05 +03:00
ieee80211_tx_skb ( sdata , skb ) ;
2008-02-23 17:17:15 +03:00
return 0 ;
}
2009-08-18 04:15:55 +04:00
void ieee80211s_update_metric ( struct ieee80211_local * local ,
struct sta_info * stainfo , struct sk_buff * skb )
{
struct ieee80211_tx_info * txinfo = IEEE80211_SKB_CB ( skb ) ;
struct ieee80211_hdr * hdr = ( struct ieee80211_hdr * ) skb - > data ;
int failed ;
if ( ! ieee80211_is_data ( hdr - > frame_control ) )
return ;
failed = ! ( txinfo - > flags & IEEE80211_TX_STAT_ACK ) ;
/* moving average, scaled to 100 */
stainfo - > fail_avg = ( ( 80 * stainfo - > fail_avg + 5 ) / 100 + 20 * failed ) ;
if ( stainfo - > fail_avg > 95 )
mesh_plink_broken ( stainfo ) ;
}
2008-02-23 17:17:15 +03:00
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 14:40:02 +04:00
if ( sta - > last_tx_rate . flags & IEEE80211_TX_RC_MCS )
return MAX_METRIC ;
2008-02-23 17:17:15 +03: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 14:40:02 +04:00
rate = sband - > bitrates [ sta - > last_tx_rate . idx ] . bitrate ;
2008-02-23 17:17:15 +03: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 04:04:37 +04:00
* @ sdata : local mesh subif
2008-02-23 17:17:15 +03: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
2009-10-20 23:17:34 +04:00
* transmitter of a HWMP PREQ or PREP frame .
2008-02-23 17:17:15 +03:00
*
* 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 04:04:37 +04:00
static u32 hwmp_route_info_get ( struct ieee80211_sub_if_data * sdata ,
2008-02-23 17:17:15 +03:00
struct ieee80211_mgmt * mgmt ,
2009-11-10 02:46:46 +03:00
u8 * hwmp_ie , enum mpath_frame_type action )
2008-02-23 17:17:15 +03:00
{
2008-08-03 04:04:37 +04:00
struct ieee80211_local * local = sdata - > local ;
2008-02-23 17:17:15 +03:00
struct mesh_path * mpath ;
struct sta_info * sta ;
bool fresh_info ;
u8 * orig_addr , * ta ;
2009-11-10 02:46:55 +03:00
u32 orig_sn , orig_metric ;
2008-02-23 17:17:15 +03:00
unsigned long orig_lifetime , exp_time ;
u32 last_hop_metric , new_metric ;
bool process = true ;
rcu_read_lock ( ) ;
2009-11-25 19:46:18 +03:00
sta = sta_info_get ( sdata , mgmt - > sa ) ;
2008-02-23 17:17:20 +03:00
if ( ! sta ) {
rcu_read_unlock ( ) ;
2008-02-23 17:17:15 +03:00
return 0 ;
2008-02-23 17:17:20 +03:00
}
2008-02-23 17:17:15 +03: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 ) ;
2009-11-10 02:46:55 +03:00
orig_sn = PREQ_IE_ORIG_SN ( hwmp_ie ) ;
2008-02-23 17:17:15 +03:00
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 ) ;
2009-11-10 02:46:55 +03:00
orig_sn = PREP_IE_ORIG_SN ( hwmp_ie ) ;
2008-02-23 17:17:15 +03:00
orig_lifetime = PREP_IE_LIFETIME ( hwmp_ie ) ;
orig_metric = PREP_IE_METRIC ( hwmp_ie ) ;
break ;
default :
2008-02-23 17:17:20 +03:00
rcu_read_unlock ( ) ;
2008-02-23 17:17:15 +03: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 ) ;
2009-11-25 19:46:19 +03:00
if ( memcmp ( orig_addr , sdata - > vif . addr , ETH_ALEN ) = = 0 ) {
2008-02-23 17:17:15 +03: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 04:04:37 +04:00
mpath = mesh_path_lookup ( orig_addr , sdata ) ;
2008-02-23 17:17:15 +03: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 ) & &
2009-11-10 02:46:55 +03:00
( mpath - > flags & MESH_PATH_SN_VALID ) ) {
if ( SN_GT ( mpath - > sn , orig_sn ) | |
( mpath - > sn = = orig_sn & &
2008-02-23 17:17:15 +03:00
action = = MPATH_PREQ & &
2010-02-24 11:53:13 +03:00
new_metric > = mpath - > metric ) ) {
2008-02-23 17:17:15 +03:00
process = false ;
fresh_info = false ;
}
}
} else {
2008-08-03 04:04:37 +04:00
mesh_path_add ( orig_addr , sdata ) ;
mpath = mesh_path_lookup ( orig_addr , sdata ) ;
2008-02-23 17:17:15 +03:00
if ( ! mpath ) {
rcu_read_unlock ( ) ;
return 0 ;
}
spin_lock_bh ( & mpath - > state_lock ) ;
}
if ( fresh_info ) {
mesh_path_assign_nexthop ( mpath , sta ) ;
2009-11-10 02:46:55 +03:00
mpath - > flags | = MESH_PATH_SN_VALID ;
2008-02-23 17:17:15 +03:00
mpath - > metric = new_metric ;
2009-11-10 02:46:55 +03:00
mpath - > sn = orig_sn ;
2008-02-23 17:17:15 +03:00
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 04:04:37 +04:00
mpath = mesh_path_lookup ( ta , sdata ) ;
2008-02-23 17:17:15 +03: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 04:04:37 +04:00
mesh_path_add ( ta , sdata ) ;
mpath = mesh_path_lookup ( ta , sdata ) ;
2008-02-23 17:17:15 +03:00
if ( ! mpath ) {
rcu_read_unlock ( ) ;
return 0 ;
}
spin_lock_bh ( & mpath - > state_lock ) ;
}
if ( fresh_info ) {
mesh_path_assign_nexthop ( mpath , sta ) ;
2009-11-10 02:46:55 +03:00
mpath - > flags & = ~ MESH_PATH_SN_VALID ;
2008-02-23 17:17:15 +03:00
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 04:04:37 +04:00
static void hwmp_preq_frame_process ( struct ieee80211_sub_if_data * sdata ,
2008-02-23 17:17:15 +03:00
struct ieee80211_mgmt * mgmt ,
2009-08-12 22:03:43 +04:00
u8 * preq_elem , u32 metric )
{
2008-09-11 02:01:49 +04:00
struct ieee80211_if_mesh * ifmsh = & sdata - > u . mesh ;
2008-02-23 17:17:15 +03:00
struct mesh_path * mpath ;
2009-11-10 02:46:55 +03:00
u8 * target_addr , * orig_addr ;
u8 target_flags , ttl ;
u32 orig_sn , target_sn , lifetime ;
2008-02-23 17:17:15 +03:00
bool reply = false ;
bool forward = true ;
2009-11-10 02:46:55 +03:00
/* Update target SN, if present */
target_addr = PREQ_IE_TARGET_ADDR ( preq_elem ) ;
2008-02-23 17:17:15 +03:00
orig_addr = PREQ_IE_ORIG_ADDR ( preq_elem ) ;
2009-11-10 02:46:55 +03:00
target_sn = PREQ_IE_TARGET_SN ( preq_elem ) ;
orig_sn = PREQ_IE_ORIG_SN ( preq_elem ) ;
target_flags = PREQ_IE_TARGET_F ( preq_elem ) ;
2008-02-23 17:17:15 +03:00
2009-11-10 02:46:47 +03:00
mhwmp_dbg ( " received PREQ from %pM \n " , orig_addr ) ;
2009-11-10 02:46:45 +03:00
2009-11-25 19:46:19 +03:00
if ( memcmp ( target_addr , sdata - > vif . addr , ETH_ALEN ) = = 0 ) {
2009-11-10 02:46:46 +03:00
mhwmp_dbg ( " PREQ is for us \n " ) ;
2008-02-23 17:17:15 +03:00
forward = false ;
reply = true ;
metric = 0 ;
2009-11-10 02:46:55 +03:00
if ( time_after ( jiffies , ifmsh - > last_sn_update +
2008-02-23 17:17:15 +03:00
net_traversal_jiffies ( sdata ) ) | |
2009-11-10 02:46:55 +03:00
time_before ( jiffies , ifmsh - > last_sn_update ) ) {
target_sn = + + ifmsh - > sn ;
ifmsh - > last_sn_update = jiffies ;
2008-02-23 17:17:15 +03:00
}
} else {
rcu_read_lock ( ) ;
2009-11-10 02:46:55 +03:00
mpath = mesh_path_lookup ( target_addr , sdata ) ;
2008-02-23 17:17:15 +03:00
if ( mpath ) {
2009-11-10 02:46:55 +03:00
if ( ( ! ( mpath - > flags & MESH_PATH_SN_VALID ) ) | |
SN_LT ( mpath - > sn , target_sn ) ) {
mpath - > sn = target_sn ;
mpath - > flags | = MESH_PATH_SN_VALID ;
} else if ( ( ! ( target_flags & MP_F_DO ) ) & &
2008-02-23 17:17:15 +03:00
( mpath - > flags & MESH_PATH_ACTIVE ) ) {
reply = true ;
metric = mpath - > metric ;
2009-11-10 02:46:55 +03:00
target_sn = mpath - > sn ;
if ( target_flags & MP_F_RF )
target_flags | = MP_F_DO ;
2008-02-23 17:17:15 +03:00
else
forward = false ;
}
}
rcu_read_unlock ( ) ;
}
if ( reply ) {
lifetime = PREQ_IE_LIFETIME ( preq_elem ) ;
2008-09-11 02:01:49 +04:00
ttl = ifmsh - > mshcfg . dot11MeshTTL ;
2009-11-10 02:46:45 +03:00
if ( ttl ! = 0 ) {
mhwmp_dbg ( " replying to the PREQ \n " ) ;
2009-11-10 02:46:55 +03:00
mesh_path_sel_frame_tx ( MPATH_PREP , 0 , target_addr ,
cpu_to_le32 ( target_sn ) , 0 , orig_addr ,
cpu_to_le32 ( orig_sn ) , mgmt - > sa , 0 , ttl ,
2008-03-01 01:30:32 +03:00
cpu_to_le32 ( lifetime ) , cpu_to_le32 ( metric ) ,
2008-08-03 04:04:37 +04:00
0 , sdata ) ;
2009-11-10 02:46:45 +03:00
} else
2008-09-11 02:01:49 +04:00
ifmsh - > mshstats . dropped_frames_ttl + + ;
2008-02-23 17:17:15 +03: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 02:01:49 +04:00
ifmsh - > mshstats . dropped_frames_ttl + + ;
2008-02-23 17:17:15 +03:00
return ;
}
2009-11-10 02:46:47 +03:00
mhwmp_dbg ( " forwarding the PREQ from %pM \n " , orig_addr ) ;
2008-02-23 17:17:15 +03:00
- - 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 ,
2009-11-10 02:46:55 +03:00
cpu_to_le32 ( orig_sn ) , target_flags , target_addr ,
2009-11-17 15:34:04 +03:00
cpu_to_le32 ( target_sn ) , broadcast_addr ,
2008-03-01 01:30:32 +03:00
hopcount , ttl , cpu_to_le32 ( lifetime ) ,
cpu_to_le32 ( metric ) , cpu_to_le32 ( preq_id ) ,
2008-08-03 04:04:37 +04:00
sdata ) ;
2009-08-18 21:59:00 +04:00
ifmsh - > mshstats . fwded_mcast + + ;
2008-09-11 02:01:49 +04:00
ifmsh - > mshstats . fwded_frames + + ;
2008-02-23 17:17:15 +03:00
}
}
2008-08-03 04:04:37 +04:00
static void hwmp_prep_frame_process ( struct ieee80211_sub_if_data * sdata ,
2008-02-23 17:17:15 +03:00
struct ieee80211_mgmt * mgmt ,
u8 * prep_elem , u32 metric )
{
struct mesh_path * mpath ;
2009-11-10 02:46:55 +03:00
u8 * target_addr , * orig_addr ;
2008-02-23 17:17:15 +03:00
u8 ttl , hopcount , flags ;
u8 next_hop [ ETH_ALEN ] ;
2009-11-10 02:46:55 +03:00
u32 target_sn , orig_sn , lifetime ;
2008-02-23 17:17:15 +03:00
2009-11-10 02:46:47 +03:00
mhwmp_dbg ( " received PREP from %pM \n " , PREP_IE_ORIG_ADDR ( prep_elem ) ) ;
2009-11-10 02:46:46 +03:00
2008-02-23 17:17:15 +03:00
/* 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
*/
2009-11-10 02:46:55 +03:00
target_addr = PREP_IE_TARGET_ADDR ( prep_elem ) ;
2009-11-25 19:46:19 +03:00
if ( memcmp ( target_addr , sdata - > vif . addr , ETH_ALEN ) = = 0 )
2008-02-23 17:17:15 +03:00
/* destination, no forwarding required */
return ;
ttl = PREP_IE_TTL ( prep_elem ) ;
if ( ttl < = 1 ) {
2008-09-11 02:01:49 +04:00
sdata - > u . mesh . mshstats . dropped_frames_ttl + + ;
2008-02-23 17:17:15 +03:00
return ;
}
rcu_read_lock ( ) ;
2009-11-10 02:46:55 +03:00
mpath = mesh_path_lookup ( target_addr , sdata ) ;
2008-02-23 17:17:15 +03: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 02:02:02 +04:00
memcpy ( next_hop , mpath - > next_hop - > sta . addr , ETH_ALEN ) ;
2008-02-23 17:17:15 +03: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 ) ;
2009-11-10 02:46:55 +03:00
target_sn = PREP_IE_TARGET_SN ( prep_elem ) ;
orig_sn = PREP_IE_ORIG_SN ( prep_elem ) ;
2008-02-23 17:17:15 +03:00
mesh_path_sel_frame_tx ( MPATH_PREP , flags , orig_addr ,
2009-11-10 02:46:55 +03:00
cpu_to_le32 ( orig_sn ) , 0 , target_addr ,
2010-02-24 11:53:13 +03:00
cpu_to_le32 ( target_sn ) , next_hop , hopcount ,
2009-11-10 02:46:55 +03:00
ttl , cpu_to_le32 ( lifetime ) , cpu_to_le32 ( metric ) ,
2008-08-03 04:04:37 +04:00
0 , sdata ) ;
2008-02-23 17:17:15 +03:00
rcu_read_unlock ( ) ;
2009-08-18 21:59:00 +04:00
sdata - > u . mesh . mshstats . fwded_unicast + + ;
2008-09-11 02:01:49 +04:00
sdata - > u . mesh . mshstats . fwded_frames + + ;
2008-02-23 17:17:15 +03:00
return ;
fail :
rcu_read_unlock ( ) ;
2008-09-11 02:01:49 +04:00
sdata - > u . mesh . mshstats . dropped_frames_no_route + + ;
2008-02-23 17:17:15 +03:00
return ;
}
2008-08-03 04:04:37 +04:00
static void hwmp_perr_frame_process ( struct ieee80211_sub_if_data * sdata ,
2008-02-23 17:17:15 +03:00
struct ieee80211_mgmt * mgmt , u8 * perr_elem )
{
2009-11-10 02:46:50 +03:00
struct ieee80211_if_mesh * ifmsh = & sdata - > u . mesh ;
2008-02-23 17:17:15 +03:00
struct mesh_path * mpath ;
2009-11-10 02:46:50 +03:00
u8 ttl ;
2009-11-10 02:46:55 +03:00
u8 * ta , * target_addr ;
u8 target_flags ;
u32 target_sn ;
u16 target_rcode ;
2008-02-23 17:17:15 +03:00
ta = mgmt - > sa ;
2009-11-10 02:46:50 +03:00
ttl = PERR_IE_TTL ( perr_elem ) ;
if ( ttl < = 1 ) {
ifmsh - > mshstats . dropped_frames_ttl + + ;
return ;
}
ttl - - ;
2009-11-10 02:46:55 +03:00
target_flags = PERR_IE_TARGET_FLAGS ( perr_elem ) ;
target_addr = PERR_IE_TARGET_ADDR ( perr_elem ) ;
target_sn = PERR_IE_TARGET_SN ( perr_elem ) ;
target_rcode = PERR_IE_TARGET_RCODE ( perr_elem ) ;
2009-11-10 02:46:50 +03:00
2008-02-23 17:17:15 +03:00
rcu_read_lock ( ) ;
2009-11-10 02:46:55 +03:00
mpath = mesh_path_lookup ( target_addr , sdata ) ;
2008-02-23 17:17:15 +03:00
if ( mpath ) {
spin_lock_bh ( & mpath - > state_lock ) ;
if ( mpath - > flags & MESH_PATH_ACTIVE & &
2008-09-11 02:02:02 +04:00
memcmp ( ta , mpath - > next_hop - > sta . addr , ETH_ALEN ) = = 0 & &
2009-11-10 02:46:55 +03:00
( ! ( mpath - > flags & MESH_PATH_SN_VALID ) | |
SN_GT ( target_sn , mpath - > sn ) ) ) {
2008-02-23 17:17:15 +03:00
mpath - > flags & = ~ MESH_PATH_ACTIVE ;
2009-11-10 02:46:55 +03:00
mpath - > sn = target_sn ;
2008-02-23 17:17:15 +03:00
spin_unlock_bh ( & mpath - > state_lock ) ;
2009-11-10 02:46:55 +03:00
mesh_path_error_tx ( ttl , target_addr , cpu_to_le32 ( target_sn ) ,
cpu_to_le16 ( target_rcode ) ,
2009-11-17 15:34:04 +03:00
broadcast_addr , sdata ) ;
2008-02-23 17:17:15 +03:00
} else
spin_unlock_bh ( & mpath - > state_lock ) ;
}
rcu_read_unlock ( ) ;
}
2009-11-11 03:01:31 +03:00
static void hwmp_rann_frame_process ( struct ieee80211_sub_if_data * sdata ,
struct ieee80211_mgmt * mgmt ,
struct ieee80211_rann_ie * rann )
{
struct ieee80211_if_mesh * ifmsh = & sdata - > u . mesh ;
struct mesh_path * mpath ;
u8 * ta ;
u8 ttl , flags , hopcount ;
u8 * orig_addr ;
2009-11-10 02:46:55 +03:00
u32 orig_sn , metric ;
2009-11-11 03:01:31 +03:00
ta = mgmt - > sa ;
ttl = rann - > rann_ttl ;
if ( ttl < = 1 ) {
ifmsh - > mshstats . dropped_frames_ttl + + ;
return ;
}
ttl - - ;
flags = rann - > rann_flags ;
orig_addr = rann - > rann_addr ;
2009-11-10 02:46:55 +03:00
orig_sn = rann - > rann_seq ;
2009-11-11 03:01:31 +03:00
hopcount = rann - > rann_hopcount ;
2009-11-10 02:46:51 +03:00
hopcount + + ;
2009-11-11 03:01:31 +03:00
metric = rann - > rann_metric ;
mhwmp_dbg ( " received RANN from %pM \n " , orig_addr ) ;
rcu_read_lock ( ) ;
mpath = mesh_path_lookup ( orig_addr , sdata ) ;
if ( ! mpath ) {
mesh_path_add ( orig_addr , sdata ) ;
mpath = mesh_path_lookup ( orig_addr , sdata ) ;
if ( ! mpath ) {
rcu_read_unlock ( ) ;
sdata - > u . mesh . mshstats . dropped_frames_no_route + + ;
return ;
}
mesh_queue_preq ( mpath ,
PREQ_Q_F_START | PREQ_Q_F_REFRESH ) ;
}
2009-11-10 02:46:55 +03:00
if ( mpath - > sn < orig_sn ) {
2009-11-11 03:01:31 +03:00
mesh_path_sel_frame_tx ( MPATH_RANN , flags , orig_addr ,
2009-11-10 02:46:55 +03:00
cpu_to_le32 ( orig_sn ) ,
2009-11-17 15:34:04 +03:00
0 , NULL , 0 , broadcast_addr ,
2009-11-10 02:46:51 +03:00
hopcount , ttl , 0 ,
cpu_to_le32 ( metric + mpath - > metric ) ,
2009-11-11 03:01:31 +03:00
0 , sdata ) ;
2009-11-10 02:46:55 +03:00
mpath - > sn = orig_sn ;
2009-11-11 03:01:31 +03:00
}
rcu_read_unlock ( ) ;
}
2008-02-23 17:17:15 +03:00
2008-08-03 04:04:37 +04:00
void mesh_rx_path_sel_frame ( struct ieee80211_sub_if_data * sdata ,
2008-02-23 17:17:15 +03:00
struct ieee80211_mgmt * mgmt ,
size_t len )
{
struct ieee802_11_elems elems ;
size_t baselen ;
u32 last_hop_metric ;
2008-09-08 17:41:59 +04:00
/* need action_code */
if ( len < IEEE80211_MIN_ACTION_SIZE + 1 )
return ;
2008-02-23 17:17:15 +03: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 ) ;
2009-11-10 02:46:46 +03:00
if ( elems . preq ) {
if ( elems . preq_len ! = 37 )
2008-02-23 17:17:15 +03:00
/* Right now we support just 1 destination and no AE */
return ;
2009-11-10 02:46:46 +03:00
last_hop_metric = hwmp_route_info_get ( sdata , mgmt , elems . preq ,
MPATH_PREQ ) ;
if ( last_hop_metric )
hwmp_preq_frame_process ( sdata , mgmt , elems . preq ,
last_hop_metric ) ;
}
if ( elems . prep ) {
if ( elems . prep_len ! = 31 )
2008-02-23 17:17:15 +03:00
/* Right now we support no AE */
return ;
2009-11-10 02:46:46 +03:00
last_hop_metric = hwmp_route_info_get ( sdata , mgmt , elems . prep ,
MPATH_PREP ) ;
if ( last_hop_metric )
hwmp_prep_frame_process ( sdata , mgmt , elems . prep ,
last_hop_metric ) ;
}
if ( elems . perr ) {
2009-11-10 02:46:50 +03:00
if ( elems . perr_len ! = 15 )
2008-02-23 17:17:15 +03:00
/* Right now we support only one destination per PERR */
return ;
2008-08-03 04:04:37 +04:00
hwmp_perr_frame_process ( sdata , mgmt , elems . perr ) ;
2008-02-23 17:17:15 +03:00
}
2009-11-11 03:01:31 +03:00
if ( elems . rann )
hwmp_rann_frame_process ( sdata , mgmt , elems . rann ) ;
2008-02-23 17:17:15 +03:00
}
/**
* 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 04:04:37 +04:00
struct ieee80211_sub_if_data * sdata = mpath - > sdata ;
2008-09-11 02:01:49 +04:00
struct ieee80211_if_mesh * ifmsh = & sdata - > u . mesh ;
2008-02-23 17:17:15 +03:00
struct mesh_preq_queue * preq_node ;
2009-06-26 03:07:42 +04:00
preq_node = kmalloc ( sizeof ( struct mesh_preq_queue ) , GFP_ATOMIC ) ;
2008-02-23 17:17:15 +03:00
if ( ! preq_node ) {
2009-11-10 02:46:45 +03:00
mhwmp_dbg ( " could not allocate PREQ node \n " ) ;
2008-02-23 17:17:15 +03:00
return ;
}
2008-09-11 02:01:49 +04: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 17:17:15 +03:00
kfree ( preq_node ) ;
if ( printk_ratelimit ( ) )
2009-11-10 02:46:45 +03:00
mhwmp_dbg ( " PREQ node queue full \n " ) ;
2008-02-23 17:17:15 +03:00
return ;
}
memcpy ( preq_node - > dst , mpath - > dst , ETH_ALEN ) ;
preq_node - > flags = flags ;
2008-09-11 02:01:49 +04: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 17:17:15 +03:00
2008-09-11 02:01:49 +04:00
if ( time_after ( jiffies , ifmsh - > last_preq + min_preq_int_jiff ( sdata ) ) )
2009-08-05 02:06:26 +04:00
ieee80211_queue_work ( & sdata - > local - > hw , & ifmsh - > work ) ;
2008-02-23 17:17:15 +03:00
2008-09-11 02:01:49 +04:00
else if ( time_before ( jiffies , ifmsh - > last_preq ) ) {
2008-02-23 17:17:15 +03:00
/* avoid long wait if did not send preqs for a long time
* and jiffies wrapped around
*/
2008-09-11 02:01:49 +04:00
ifmsh - > last_preq = jiffies - min_preq_int_jiff ( sdata ) - 1 ;
2009-08-05 02:06:26 +04:00
ieee80211_queue_work ( & sdata - > local - > hw , & ifmsh - > work ) ;
2008-02-23 17:17:15 +03:00
} else
2008-09-11 02:01:49 +04:00
mod_timer ( & ifmsh - > mesh_path_timer , ifmsh - > last_preq +
2008-02-23 17:17:15 +03:00
min_preq_int_jiff ( sdata ) ) ;
}
/**
* mesh_path_start_discovery - launch a path discovery from the PREQ queue
*
2008-08-03 04:04:37 +04:00
* @ sdata : local mesh subif
2008-02-23 17:17:15 +03:00
*/
2008-08-03 04:04:37 +04:00
void mesh_path_start_discovery ( struct ieee80211_sub_if_data * sdata )
2008-02-23 17:17:15 +03:00
{
2008-09-11 02:01:49 +04:00
struct ieee80211_if_mesh * ifmsh = & sdata - > u . mesh ;
2008-02-23 17:17:15 +03:00
struct mesh_preq_queue * preq_node ;
struct mesh_path * mpath ;
2009-11-10 02:46:55 +03:00
u8 ttl , target_flags ;
2008-02-23 17:17:15 +03:00
u32 lifetime ;
2009-07-10 13:39:26 +04:00
spin_lock_bh ( & ifmsh - > mesh_preq_queue_lock ) ;
2008-09-11 02:01:49 +04:00
if ( ! ifmsh - > preq_queue_len | |
time_before ( jiffies , ifmsh - > last_preq +
2008-02-23 17:17:15 +03:00
min_preq_int_jiff ( sdata ) ) ) {
2009-07-10 13:39:26 +04:00
spin_unlock_bh ( & ifmsh - > mesh_preq_queue_lock ) ;
2008-02-23 17:17:15 +03:00
return ;
}
2008-09-11 02:01:49 +04:00
preq_node = list_first_entry ( & ifmsh - > preq_queue . list ,
2008-02-23 17:17:15 +03:00
struct mesh_preq_queue , list ) ;
list_del ( & preq_node - > list ) ;
2008-09-11 02:01:49 +04:00
- - ifmsh - > preq_queue_len ;
2009-07-10 13:39:26 +04:00
spin_unlock_bh ( & ifmsh - > mesh_preq_queue_lock ) ;
2008-02-23 17:17:15 +03:00
rcu_read_lock ( ) ;
2008-08-03 04:04:37 +04:00
mpath = mesh_path_lookup ( preq_node - > dst , sdata ) ;
2008-02-23 17:17:15 +03: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 02:01:49 +04:00
ifmsh - > last_preq = jiffies ;
2008-02-23 17:17:15 +03:00
2009-11-10 02:46:55 +03:00
if ( time_after ( jiffies , ifmsh - > last_sn_update +
2008-02-23 17:17:15 +03:00
net_traversal_jiffies ( sdata ) ) | |
2009-11-10 02:46:55 +03:00
time_before ( jiffies , ifmsh - > last_sn_update ) ) {
+ + ifmsh - > sn ;
sdata - > u . mesh . last_sn_update = jiffies ;
2008-02-23 17:17:15 +03:00
}
lifetime = default_lifetime ( sdata ) ;
2008-09-11 02:01:49 +04:00
ttl = sdata - > u . mesh . mshcfg . dot11MeshTTL ;
2008-02-23 17:17:15 +03:00
if ( ttl = = 0 ) {
2008-09-11 02:01:49 +04:00
sdata - > u . mesh . mshstats . dropped_frames_ttl + + ;
2008-02-23 17:17:15 +03:00
spin_unlock_bh ( & mpath - > state_lock ) ;
goto enddiscovery ;
}
if ( preq_node - > flags & PREQ_Q_F_REFRESH )
2009-11-10 02:46:55 +03:00
target_flags = MP_F_DO ;
2008-02-23 17:17:15 +03:00
else
2009-11-10 02:46:55 +03:00
target_flags = MP_F_RF ;
2008-02-23 17:17:15 +03:00
spin_unlock_bh ( & mpath - > state_lock ) ;
2009-11-25 19:46:19 +03:00
mesh_path_sel_frame_tx ( MPATH_PREQ , 0 , sdata - > vif . addr ,
2009-11-10 02:46:55 +03:00
cpu_to_le32 ( ifmsh - > sn ) , target_flags , mpath - > dst ,
2009-11-17 15:34:04 +03:00
cpu_to_le32 ( mpath - > sn ) , broadcast_addr , 0 ,
2008-03-01 01:30:32 +03:00
ttl , cpu_to_le32 ( lifetime ) , 0 ,
2008-09-11 02:01:49 +04:00
cpu_to_le32 ( ifmsh - > preq_id + + ) , sdata ) ;
2008-02-23 17:17:15 +03:00
mod_timer ( & mpath - > timer , jiffies + mpath - > discovery_timeout ) ;
enddiscovery :
rcu_read_unlock ( ) ;
kfree ( preq_node ) ;
}
/**
2009-01-19 14:50:37 +03:00
* mesh_nexthop_lookup - put the appropriate next hop on a mesh frame
2008-02-23 17:17:15 +03:00
*
2008-08-05 21:34:52 +04:00
* @ skb : 802.11 frame to be sent
2008-08-03 04:04:37 +04:00
* @ sdata : network subif the frame will be sent through
2008-02-23 17:17:15 +03: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 04:04:37 +04:00
int mesh_nexthop_lookup ( struct sk_buff * skb ,
struct ieee80211_sub_if_data * sdata )
2008-02-23 17:17:15 +03:00
{
struct sk_buff * skb_to_free = NULL ;
struct mesh_path * mpath ;
2008-08-05 21:34:52 +04:00
struct ieee80211_hdr * hdr = ( struct ieee80211_hdr * ) skb - > data ;
2009-11-10 02:46:55 +03:00
u8 * target_addr = hdr - > addr3 ;
2008-02-23 17:17:15 +03:00
int err = 0 ;
rcu_read_lock ( ) ;
2009-11-10 02:46:55 +03:00
mpath = mesh_path_lookup ( target_addr , sdata ) ;
2008-02-23 17:17:15 +03:00
if ( ! mpath ) {
2009-11-10 02:46:55 +03:00
mesh_path_add ( target_addr , sdata ) ;
mpath = mesh_path_lookup ( target_addr , sdata ) ;
2008-02-23 17:17:15 +03:00
if ( ! mpath ) {
2008-09-11 02:01:49 +04:00
sdata - > u . mesh . mshstats . dropped_frames_no_route + + ;
2008-02-23 17:17:15 +03:00
err = - ENOSPC ;
goto endlookup ;
}
}
if ( mpath - > flags & MESH_PATH_ACTIVE ) {
2009-11-30 03:55:45 +03:00
if ( time_after ( jiffies ,
2009-12-10 05:43:01 +03:00
mpath - > exp_time -
2009-11-30 03:55:45 +03:00
msecs_to_jiffies ( sdata - > u . mesh . mshcfg . path_refresh_time ) ) & &
2009-11-25 19:46:19 +03:00
! memcmp ( sdata - > vif . addr , hdr - > addr4 , ETH_ALEN ) & &
2009-11-30 03:55:45 +03:00
! ( mpath - > flags & MESH_PATH_RESOLVING ) & &
! ( mpath - > flags & MESH_PATH_FIXED ) ) {
2008-02-23 17:17:15 +03:00
mesh_queue_preq ( mpath ,
PREQ_Q_F_START | PREQ_Q_F_REFRESH ) ;
}
2009-11-30 03:55:45 +03:00
memcpy ( hdr - > addr1 , mpath - > next_hop - > sta . addr , ETH_ALEN ) ;
2008-02-23 17:17:15 +03:00
} else {
2009-07-07 21:55:03 +04:00
struct ieee80211_tx_info * info = IEEE80211_SKB_CB ( skb ) ;
2008-02-23 17:17:15 +03:00
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 ) > =
2009-08-10 23:15:46 +04:00
MESH_FRAME_QUEUE_LEN )
skb_to_free = skb_dequeue ( & mpath - > frame_queue ) ;
2008-02-23 17:17:15 +03:00
2009-07-07 21:55:03 +04:00
info - > flags | = IEEE80211_TX_INTFL_NEED_TXPROCESSING ;
2008-02-23 17:17:15 +03:00
skb_queue_tail ( & mpath - > frame_queue , skb ) ;
if ( skb_to_free )
2008-08-03 04:04:37 +04:00
mesh_path_discard_frame ( skb_to_free , sdata ) ;
2008-02-23 17:17:15 +03: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 ;
2008-08-03 04:04:37 +04:00
sdata = mpath - > sdata ;
2009-05-17 13:40:42 +04:00
if ( sdata - > local - > quiescing ) {
rcu_read_unlock ( ) ;
return ;
}
spin_lock_bh ( & mpath - > state_lock ) ;
2008-03-01 02:04:13 +03:00
if ( mpath - > flags & MESH_PATH_RESOLVED | |
2008-02-23 17:17:15 +03: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 ( ) ;
}
2009-11-10 02:46:56 +03:00
void
mesh_path_tx_root_frame ( struct ieee80211_sub_if_data * sdata )
{
struct ieee80211_if_mesh * ifmsh = & sdata - > u . mesh ;
2009-11-25 19:46:19 +03:00
mesh_path_sel_frame_tx ( MPATH_RANN , 0 , sdata - > vif . addr ,
2009-11-10 02:46:56 +03:00
cpu_to_le32 ( + + ifmsh - > sn ) ,
2009-11-17 15:34:04 +03:00
0 , NULL , 0 , broadcast_addr ,
2009-11-10 02:46:56 +03:00
0 , MESH_TTL , 0 , 0 , 0 , sdata ) ;
}