2008-02-23 17:17:13 +03: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 .
*/
2008-02-23 17:17:19 +03:00
# include <linux/kernel.h>
# include <linux/random.h>
2008-02-23 17:17:13 +03:00
# include "ieee80211_i.h"
2008-04-08 23:14:40 +04:00
# include "rate.h"
2008-02-23 17:17:13 +03:00
# include "mesh.h"
# ifdef CONFIG_MAC80211_VERBOSE_MPL_DEBUG
# define mpl_dbg(fmt, args...) printk(KERN_DEBUG fmt, ##args)
# else
# define mpl_dbg(fmt, args...) do { (void)(0); } while (0)
# endif
# define PLINK_GET_FRAME_SUBTYPE(p) (p)
# define PLINK_GET_LLID(p) (p + 1)
# define PLINK_GET_PLID(p) (p + 3)
# define mod_plink_timer(s, t) (mod_timer(&s->plink_timer, \
jiffies + HZ * t / 1000 ) )
/* Peer link cancel reasons, all subject to ANA approval */
# define MESH_LINK_CANCELLED 2
# define MESH_MAX_NEIGHBORS 3
# define MESH_CAPABILITY_POLICY_VIOLATION 4
# define MESH_CLOSE_RCVD 5
# define MESH_MAX_RETRIES 6
# define MESH_CONFIRM_TIMEOUT 7
# define MESH_SECURITY_ROLE_NEGOTIATION_DIFFERS 8
# define MESH_SECURITY_AUTHENTICATION_IMPOSSIBLE 9
# define MESH_SECURITY_FAILED_VERIFICATION 10
2008-09-11 02:01:49 +04:00
# define dot11MeshMaxRetries(s) (s->u.mesh.mshcfg.dot11MeshMaxRetries)
# define dot11MeshRetryTimeout(s) (s->u.mesh.mshcfg.dot11MeshRetryTimeout)
# define dot11MeshConfirmTimeout(s) (s->u.mesh.mshcfg.dot11MeshConfirmTimeout)
# define dot11MeshHoldingTimeout(s) (s->u.mesh.mshcfg.dot11MeshHoldingTimeout)
# define dot11MeshMaxPeerLinks(s) (s->u.mesh.mshcfg.dot11MeshMaxPeerLinks)
2008-02-23 17:17:13 +03:00
enum plink_frame_type {
PLINK_OPEN = 0 ,
PLINK_CONFIRM ,
PLINK_CLOSE
} ;
enum plink_event {
PLINK_UNDEFINED ,
OPN_ACPT ,
OPN_RJCT ,
OPN_IGNR ,
CNF_ACPT ,
CNF_RJCT ,
CNF_IGNR ,
CLS_ACPT ,
CLS_IGNR
} ;
static inline
void mesh_plink_inc_estab_count ( struct ieee80211_sub_if_data * sdata )
{
2008-09-11 02:01:49 +04:00
atomic_inc ( & sdata - > u . mesh . mshstats . estab_plinks ) ;
2008-02-25 18:27:46 +03:00
mesh_accept_plinks_update ( sdata ) ;
2008-02-23 17:17:13 +03:00
}
static inline
void mesh_plink_dec_estab_count ( struct ieee80211_sub_if_data * sdata )
{
2008-09-11 02:01:49 +04:00
atomic_dec ( & sdata - > u . mesh . mshstats . estab_plinks ) ;
2008-02-25 18:27:46 +03:00
mesh_accept_plinks_update ( sdata ) ;
2008-02-23 17:17:13 +03:00
}
/**
* mesh_plink_fsm_restart - restart a mesh peer link finite state machine
*
* @ sta : mes peer link to restart
*
2008-05-03 03:02:02 +04:00
* Locking : this function must be called holding sta - > lock
2008-02-23 17:17:13 +03:00
*/
static inline void mesh_plink_fsm_restart ( struct sta_info * sta )
{
2008-03-01 02:46:08 +03:00
sta - > plink_state = PLINK_LISTEN ;
2008-02-29 23:13:38 +03:00
sta - > llid = sta - > plid = sta - > reason = 0 ;
sta - > plink_retries = 0 ;
2008-02-23 17:17:13 +03:00
}
2008-04-01 17:21:00 +04:00
/*
* NOTE : This is just an alias for sta_info_alloc ( ) , see notes
* on it in the lifecycle management section !
*/
2008-02-27 11:56:40 +03:00
static struct sta_info * mesh_plink_alloc ( struct ieee80211_sub_if_data * sdata ,
u8 * hw_addr , u64 rates )
2008-02-23 17:17:13 +03:00
{
2008-02-25 18:27:46 +03:00
struct ieee80211_local * local = sdata - > local ;
2008-02-23 17:17:13 +03:00
struct sta_info * sta ;
if ( local - > num_sta > = MESH_MAX_PLINKS )
2008-02-25 18:27:47 +03:00
return NULL ;
2008-02-23 17:17:13 +03:00
2008-02-27 11:56:40 +03:00
sta = sta_info_alloc ( sdata , hw_addr , GFP_ATOMIC ) ;
2008-02-25 18:27:47 +03:00
if ( ! sta )
return NULL ;
2008-02-23 17:17:13 +03:00
2008-05-03 03:02:02 +04:00
sta - > flags = WLAN_STA_AUTHORIZED ;
2008-09-11 04:45:11 +04:00
sta - > sta . supp_rates [ local - > hw . conf . channel - > band ] = rates ;
2008-02-23 17:17:13 +03:00
return sta ;
}
/**
2008-02-25 23:36:27 +03:00
* mesh_plink_deactivate - deactivate mesh peer link
2008-02-23 17:17:13 +03:00
*
* @ sta : mesh peer link to deactivate
*
* All mesh paths with this peer as next hop will be flushed
*
2008-05-03 03:02:02 +04:00
* Locking : the caller must hold sta - > lock
2008-02-23 17:17:13 +03:00
*/
2008-02-23 17:17:19 +03:00
static void __mesh_plink_deactivate ( struct sta_info * sta )
2008-02-23 17:17:13 +03:00
{
2008-02-25 18:27:46 +03:00
struct ieee80211_sub_if_data * sdata = sta - > sdata ;
2008-03-01 02:46:08 +03:00
if ( sta - > plink_state = = PLINK_ESTAB )
2008-02-23 17:17:13 +03:00
mesh_plink_dec_estab_count ( sdata ) ;
2008-03-01 02:46:08 +03:00
sta - > plink_state = PLINK_BLOCKED ;
2008-02-23 17:17:13 +03:00
mesh_path_flush_by_nexthop ( sta ) ;
}
2008-02-23 17:17:19 +03:00
/**
* __mesh_plink_deactivate - deactivate mesh peer link
*
* @ sta : mesh peer link to deactivate
*
* All mesh paths with this peer as next hop will be flushed
*/
void mesh_plink_deactivate ( struct sta_info * sta )
{
2008-05-03 03:02:02 +04:00
spin_lock_bh ( & sta - > lock ) ;
2008-02-23 17:17:19 +03:00
__mesh_plink_deactivate ( sta ) ;
2008-05-03 03:02:02 +04:00
spin_unlock_bh ( & sta - > lock ) ;
2008-02-23 17:17:19 +03:00
}
2008-08-03 04:04:37 +04:00
static int mesh_plink_frame_tx ( struct ieee80211_sub_if_data * sdata ,
2008-02-23 17:17:13 +03:00
enum plink_frame_type action , u8 * da , __le16 llid , __le16 plid ,
__le16 reason ) {
2008-08-03 04:04:37 +04:00
struct ieee80211_local * local = sdata - > local ;
2008-02-23 17:17:13 +03:00
struct sk_buff * skb = dev_alloc_skb ( local - > hw . extra_tx_headroom + 400 ) ;
struct ieee80211_mgmt * mgmt ;
bool include_plid = false ;
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 . plink_action ) ) ;
memset ( mgmt , 0 , 25 + sizeof ( mgmt - > u . action . u . plink_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:13 +03:00
memcpy ( mgmt - > da , da , ETH_ALEN ) ;
2008-08-03 04:04:37 +04:00
memcpy ( mgmt - > sa , sdata - > dev - > dev_addr , ETH_ALEN ) ;
2008-02-23 17:17:13 +03:00
/* BSSID is left zeroed, wildcard value */
mgmt - > u . action . category = PLINK_CATEGORY ;
mgmt - > u . action . u . plink_action . action_code = action ;
if ( action = = PLINK_CLOSE )
mgmt - > u . action . u . plink_action . aux = reason ;
else {
mgmt - > u . action . u . plink_action . aux = cpu_to_le16 ( 0x0 ) ;
if ( action = = PLINK_CONFIRM ) {
pos = skb_put ( skb , 4 ) ;
/* two-byte status code followed by two-byte AID */
memset ( pos , 0 , 4 ) ;
}
2008-08-03 04:04:37 +04:00
mesh_mgmt_ies_add ( skb , sdata ) ;
2008-02-23 17:17:13 +03:00
}
/* Add Peer Link Management element */
switch ( action ) {
case PLINK_OPEN :
ie_len = 3 ;
break ;
case PLINK_CONFIRM :
ie_len = 5 ;
include_plid = true ;
break ;
case PLINK_CLOSE :
default :
if ( ! plid )
ie_len = 5 ;
else {
ie_len = 7 ;
include_plid = true ;
}
break ;
}
pos = skb_put ( skb , 2 + ie_len ) ;
* pos + + = WLAN_EID_PEER_LINK ;
* pos + + = ie_len ;
* pos + + = action ;
memcpy ( pos , & llid , 2 ) ;
if ( include_plid ) {
pos + = 2 ;
memcpy ( pos , & plid , 2 ) ;
}
if ( action = = PLINK_CLOSE ) {
pos + = 2 ;
memcpy ( pos , & reason , 2 ) ;
}
2008-09-09 17:07:09 +04:00
ieee80211_tx_skb ( sdata , skb , 0 ) ;
2008-02-23 17:17:13 +03:00
return 0 ;
}
2008-08-03 04:04:37 +04:00
void mesh_neighbour_update ( u8 * hw_addr , u64 rates , struct ieee80211_sub_if_data * sdata ,
2008-02-23 17:17:13 +03:00
bool peer_accepting_plinks )
{
2008-08-03 04:04:37 +04:00
struct ieee80211_local * local = sdata - > local ;
2008-02-23 17:17:13 +03:00
struct sta_info * sta ;
2008-02-25 18:27:46 +03:00
rcu_read_lock ( ) ;
2008-02-23 17:17:13 +03:00
sta = sta_info_get ( local , hw_addr ) ;
if ( ! sta ) {
2008-02-27 11:56:40 +03:00
sta = mesh_plink_alloc ( sdata , hw_addr , rates ) ;
2008-02-25 18:27:47 +03:00
if ( ! sta ) {
rcu_read_unlock ( ) ;
return ;
}
if ( sta_info_insert ( sta ) ) {
2008-02-25 18:27:46 +03:00
rcu_read_unlock ( ) ;
2008-02-23 17:17:13 +03:00
return ;
2008-02-25 18:27:46 +03:00
}
2008-02-23 17:17:13 +03:00
}
sta - > last_rx = jiffies ;
2008-09-11 04:45:11 +04:00
sta - > sta . supp_rates [ local - > hw . conf . channel - > band ] = rates ;
2008-03-01 02:46:08 +03:00
if ( peer_accepting_plinks & & sta - > plink_state = = PLINK_LISTEN & &
2008-09-11 02:01:49 +04:00
sdata - > u . mesh . accepting_plinks & &
sdata - > u . mesh . mshcfg . auto_open_plinks )
2008-02-23 17:17:13 +03:00
mesh_plink_open ( sta ) ;
2008-02-25 18:27:46 +03:00
rcu_read_unlock ( ) ;
2008-02-23 17:17:13 +03:00
}
static void mesh_plink_timer ( unsigned long data )
{
struct sta_info * sta ;
__le16 llid , plid , reason ;
struct ieee80211_sub_if_data * sdata ;
# ifdef CONFIG_MAC80211_VERBOSE_MPL_DEBUG
DECLARE_MAC_BUF ( mac ) ;
# endif
2008-02-25 18:27:46 +03:00
/*
* This STA is valid because sta_info_destroy ( ) will
* del_timer_sync ( ) this timer after having made sure
* it cannot be readded ( by deleting the plink . )
*/
2008-02-23 17:17:13 +03:00
sta = ( struct sta_info * ) data ;
2008-05-03 03:02:02 +04:00
spin_lock_bh ( & sta - > lock ) ;
2008-02-23 17:17:13 +03:00
if ( sta - > ignore_plink_timer ) {
sta - > ignore_plink_timer = false ;
2008-05-03 03:02:02 +04:00
spin_unlock_bh ( & sta - > lock ) ;
2008-02-23 17:17:13 +03:00
return ;
}
mpl_dbg ( " Mesh plink timer for %s fired on state %d \n " ,
2008-09-11 02:02:02 +04:00
print_mac ( mac , sta - > sta . addr ) , sta - > plink_state ) ;
2008-02-23 17:17:13 +03:00
reason = 0 ;
llid = sta - > llid ;
plid = sta - > plid ;
2008-02-25 18:27:46 +03:00
sdata = sta - > sdata ;
2008-02-23 17:17:13 +03:00
switch ( sta - > plink_state ) {
2008-03-01 02:46:08 +03:00
case PLINK_OPN_RCVD :
case PLINK_OPN_SNT :
2008-02-23 17:17:13 +03:00
/* retry timer */
if ( sta - > plink_retries < dot11MeshMaxRetries ( sdata ) ) {
u32 rand ;
mpl_dbg ( " Mesh plink for %s (retry, timeout): %d %d \n " ,
2008-09-11 02:02:02 +04:00
print_mac ( mac , sta - > sta . addr ) ,
2008-02-23 17:17:13 +03:00
sta - > plink_retries , sta - > plink_timeout ) ;
get_random_bytes ( & rand , sizeof ( u32 ) ) ;
sta - > plink_timeout = sta - > plink_timeout +
rand % sta - > plink_timeout ;
+ + sta - > plink_retries ;
2008-02-25 18:27:46 +03:00
mod_plink_timer ( sta , sta - > plink_timeout ) ;
2008-05-03 03:02:02 +04:00
spin_unlock_bh ( & sta - > lock ) ;
2008-09-11 02:02:02 +04:00
mesh_plink_frame_tx ( sdata , PLINK_OPEN , sta - > sta . addr , llid ,
2008-02-23 17:17:13 +03:00
0 , 0 ) ;
break ;
}
reason = cpu_to_le16 ( MESH_MAX_RETRIES ) ;
/* fall through on else */
2008-03-01 02:46:08 +03:00
case PLINK_CNF_RCVD :
2008-02-23 17:17:13 +03:00
/* confirm timer */
if ( ! reason )
reason = cpu_to_le16 ( MESH_CONFIRM_TIMEOUT ) ;
2008-03-01 02:46:08 +03:00
sta - > plink_state = PLINK_HOLDING ;
2008-02-25 18:27:46 +03:00
mod_plink_timer ( sta , dot11MeshHoldingTimeout ( sdata ) ) ;
2008-05-03 03:02:02 +04:00
spin_unlock_bh ( & sta - > lock ) ;
2008-09-11 02:02:02 +04:00
mesh_plink_frame_tx ( sdata , PLINK_CLOSE , sta - > sta . addr , llid , plid ,
2008-02-23 17:17:13 +03:00
reason ) ;
break ;
2008-03-01 02:46:08 +03:00
case PLINK_HOLDING :
2008-02-23 17:17:13 +03:00
/* holding timer */
2008-02-25 18:27:46 +03:00
del_timer ( & sta - > plink_timer ) ;
2008-02-23 17:17:13 +03:00
mesh_plink_fsm_restart ( sta ) ;
2008-05-03 03:02:02 +04:00
spin_unlock_bh ( & sta - > lock ) ;
2008-02-23 17:17:13 +03:00
break ;
default :
2008-05-03 03:02:02 +04:00
spin_unlock_bh ( & sta - > lock ) ;
2008-02-23 17:17:13 +03:00
break ;
}
}
static inline void mesh_plink_timer_set ( struct sta_info * sta , int timeout )
{
sta - > plink_timer . expires = jiffies + ( HZ * timeout / 1000 ) ;
sta - > plink_timer . data = ( unsigned long ) sta ;
sta - > plink_timer . function = mesh_plink_timer ;
sta - > plink_timeout = timeout ;
add_timer ( & sta - > plink_timer ) ;
}
int mesh_plink_open ( struct sta_info * sta )
{
__le16 llid ;
2008-02-25 18:27:46 +03:00
struct ieee80211_sub_if_data * sdata = sta - > sdata ;
2008-02-23 17:17:13 +03:00
# ifdef CONFIG_MAC80211_VERBOSE_MPL_DEBUG
DECLARE_MAC_BUF ( mac ) ;
# endif
2008-05-03 03:02:02 +04:00
spin_lock_bh ( & sta - > lock ) ;
2008-02-23 17:17:13 +03:00
get_random_bytes ( & llid , 2 ) ;
sta - > llid = llid ;
2008-03-01 02:46:08 +03:00
if ( sta - > plink_state ! = PLINK_LISTEN ) {
2008-05-03 03:02:02 +04:00
spin_unlock_bh ( & sta - > lock ) ;
2008-02-23 17:17:13 +03:00
return - EBUSY ;
}
2008-03-01 02:46:08 +03:00
sta - > plink_state = PLINK_OPN_SNT ;
2008-02-23 17:17:13 +03:00
mesh_plink_timer_set ( sta , dot11MeshRetryTimeout ( sdata ) ) ;
2008-05-03 03:02:02 +04:00
spin_unlock_bh ( & sta - > lock ) ;
2008-02-23 17:17:13 +03:00
mpl_dbg ( " Mesh plink: starting establishment with %s \n " ,
2008-09-11 02:02:02 +04:00
print_mac ( mac , sta - > sta . addr ) ) ;
2008-02-23 17:17:13 +03:00
2008-08-03 04:04:37 +04:00
return mesh_plink_frame_tx ( sdata , PLINK_OPEN ,
2008-09-11 02:02:02 +04:00
sta - > sta . addr , llid , 0 , 0 ) ;
2008-02-23 17:17:13 +03:00
}
void mesh_plink_block ( struct sta_info * sta )
{
# ifdef CONFIG_MAC80211_VERBOSE_MPL_DEBUG
DECLARE_MAC_BUF ( mac ) ;
# endif
2008-05-03 03:02:02 +04:00
spin_lock_bh ( & sta - > lock ) ;
2008-02-23 17:17:19 +03:00
__mesh_plink_deactivate ( sta ) ;
2008-03-01 02:46:08 +03:00
sta - > plink_state = PLINK_BLOCKED ;
2008-05-03 03:02:02 +04:00
spin_unlock_bh ( & sta - > lock ) ;
2008-02-23 17:17:13 +03:00
}
int mesh_plink_close ( struct sta_info * sta )
{
2008-02-25 18:27:46 +03:00
struct ieee80211_sub_if_data * sdata = sta - > sdata ;
2008-02-29 23:13:38 +03:00
__le16 llid , plid , reason ;
2008-02-23 17:17:13 +03:00
# ifdef CONFIG_MAC80211_VERBOSE_MPL_DEBUG
DECLARE_MAC_BUF ( mac ) ;
# endif
mpl_dbg ( " Mesh plink: closing link with %s \n " ,
2008-09-11 02:02:02 +04:00
print_mac ( mac , sta - > sta . addr ) ) ;
2008-05-03 03:02:02 +04:00
spin_lock_bh ( & sta - > lock ) ;
2008-02-23 17:17:13 +03:00
sta - > reason = cpu_to_le16 ( MESH_LINK_CANCELLED ) ;
reason = sta - > reason ;
2008-03-01 02:46:08 +03:00
if ( sta - > plink_state = = PLINK_LISTEN | |
sta - > plink_state = = PLINK_BLOCKED ) {
2008-02-23 17:17:13 +03:00
mesh_plink_fsm_restart ( sta ) ;
2008-05-03 03:02:02 +04:00
spin_unlock_bh ( & sta - > lock ) ;
2008-02-23 17:17:13 +03:00
return 0 ;
2008-03-01 02:46:08 +03:00
} else if ( sta - > plink_state = = PLINK_ESTAB ) {
2008-02-23 17:17:19 +03:00
__mesh_plink_deactivate ( sta ) ;
2008-02-23 17:17:13 +03:00
/* The timer should not be running */
2008-02-25 18:27:46 +03:00
mod_plink_timer ( sta , dot11MeshHoldingTimeout ( sdata ) ) ;
2008-02-23 17:17:13 +03:00
} else if ( ! mod_plink_timer ( sta , dot11MeshHoldingTimeout ( sdata ) ) )
sta - > ignore_plink_timer = true ;
2008-03-01 02:46:08 +03:00
sta - > plink_state = PLINK_HOLDING ;
2008-02-23 17:17:13 +03:00
llid = sta - > llid ;
plid = sta - > plid ;
2008-05-03 03:02:02 +04:00
spin_unlock_bh ( & sta - > lock ) ;
2008-09-11 02:02:02 +04:00
mesh_plink_frame_tx ( sta - > sdata , PLINK_CLOSE , sta - > sta . addr , llid ,
2008-02-25 18:27:46 +03:00
plid , reason ) ;
2008-02-23 17:17:13 +03:00
return 0 ;
}
2008-08-03 04:04:37 +04:00
void mesh_rx_plink_frame ( struct ieee80211_sub_if_data * sdata , struct ieee80211_mgmt * mgmt ,
2008-02-23 17:17:13 +03:00
size_t len , struct ieee80211_rx_status * rx_status )
{
2008-02-25 18:27:46 +03:00
struct ieee80211_local * local = sdata - > local ;
2008-02-23 17:17:13 +03:00
struct ieee802_11_elems elems ;
struct sta_info * sta ;
enum plink_event event ;
enum plink_frame_type ftype ;
size_t baselen ;
u8 ie_len ;
u8 * baseaddr ;
__le16 plid , llid , reason ;
# ifdef CONFIG_MAC80211_VERBOSE_MPL_DEBUG
DECLARE_MAC_BUF ( mac ) ;
# endif
2008-09-08 17:41:59 +04:00
/* need action_code, aux */
if ( len < IEEE80211_MIN_ACTION_SIZE + 3 )
return ;
2008-02-23 17:17:13 +03:00
if ( is_multicast_ether_addr ( mgmt - > da ) ) {
mpl_dbg ( " Mesh plink: ignore frame from multicast address " ) ;
return ;
}
baseaddr = mgmt - > u . action . u . plink_action . variable ;
baselen = ( u8 * ) mgmt - > u . action . u . plink_action . variable - ( u8 * ) mgmt ;
if ( mgmt - > u . action . u . plink_action . action_code = = PLINK_CONFIRM ) {
baseaddr + = 4 ;
baselen - = 4 ;
}
ieee802_11_parse_elems ( baseaddr , len - baselen , & elems ) ;
if ( ! elems . peer_link ) {
mpl_dbg ( " Mesh plink: missing necessary peer link ie \n " ) ;
return ;
}
ftype = * ( ( u8 * ) PLINK_GET_FRAME_SUBTYPE ( elems . peer_link ) ) ;
ie_len = elems . peer_link_len ;
if ( ( ftype = = PLINK_OPEN & & ie_len ! = 3 ) | |
( ftype = = PLINK_CONFIRM & & ie_len ! = 5 ) | |
( ftype = = PLINK_CLOSE & & ie_len ! = 5 & & ie_len ! = 7 ) ) {
mpl_dbg ( " Mesh plink: incorrect plink ie length \n " ) ;
return ;
}
if ( ftype ! = PLINK_CLOSE & & ( ! elems . mesh_id | | ! elems . mesh_config ) ) {
mpl_dbg ( " Mesh plink: missing necessary ie \n " ) ;
return ;
}
/* Note the lines below are correct, the llid in the frame is the plid
* from the point of view of this host .
*/
memcpy ( & plid , PLINK_GET_LLID ( elems . peer_link ) , 2 ) ;
if ( ftype = = PLINK_CONFIRM | | ( ftype = = PLINK_CLOSE & & ie_len = = 7 ) )
memcpy ( & llid , PLINK_GET_PLID ( elems . peer_link ) , 2 ) ;
2008-02-25 18:27:46 +03:00
rcu_read_lock ( ) ;
2008-02-23 17:17:13 +03:00
sta = sta_info_get ( local , mgmt - > sa ) ;
if ( ! sta & & ftype ! = PLINK_OPEN ) {
mpl_dbg ( " Mesh plink: cls or cnf from unknown peer \n " ) ;
2008-02-25 18:27:46 +03:00
rcu_read_unlock ( ) ;
2008-02-23 17:17:13 +03:00
return ;
}
2008-03-01 02:46:08 +03:00
if ( sta & & sta - > plink_state = = PLINK_BLOCKED ) {
2008-02-25 18:27:46 +03:00
rcu_read_unlock ( ) ;
2008-02-23 17:17:13 +03:00
return ;
}
/* Now we will figure out the appropriate event... */
event = PLINK_UNDEFINED ;
2008-08-03 04:04:37 +04:00
if ( ftype ! = PLINK_CLOSE & & ( ! mesh_matches_local ( & elems , sdata ) ) ) {
2008-02-23 17:17:13 +03:00
switch ( ftype ) {
case PLINK_OPEN :
event = OPN_RJCT ;
break ;
case PLINK_CONFIRM :
event = CNF_RJCT ;
break ;
case PLINK_CLOSE :
/* avoid warning */
break ;
}
2008-05-03 03:02:02 +04:00
spin_lock_bh ( & sta - > lock ) ;
2008-02-23 17:17:13 +03:00
} else if ( ! sta ) {
/* ftype == PLINK_OPEN */
u64 rates ;
if ( ! mesh_plink_free_count ( sdata ) ) {
mpl_dbg ( " Mesh plink error: no more free plinks \n " ) ;
2008-02-25 18:27:46 +03:00
rcu_read_unlock ( ) ;
2008-02-23 17:17:13 +03:00
return ;
}
rates = ieee80211_sta_get_rates ( local , & elems , rx_status - > band ) ;
2008-02-27 11:56:40 +03:00
sta = mesh_plink_alloc ( sdata , mgmt - > sa , rates ) ;
2008-02-25 18:27:47 +03:00
if ( ! sta ) {
2008-02-23 17:17:13 +03:00
mpl_dbg ( " Mesh plink error: plink table full \n " ) ;
2008-02-25 18:27:46 +03:00
rcu_read_unlock ( ) ;
2008-02-23 17:17:13 +03:00
return ;
}
2008-02-25 18:27:47 +03:00
if ( sta_info_insert ( sta ) ) {
rcu_read_unlock ( ) ;
return ;
}
2008-02-23 17:17:13 +03:00
event = OPN_ACPT ;
2008-05-03 03:02:02 +04:00
spin_lock_bh ( & sta - > lock ) ;
2008-02-23 17:17:13 +03:00
} else {
2008-05-03 03:02:02 +04:00
spin_lock_bh ( & sta - > lock ) ;
2008-02-23 17:17:13 +03:00
switch ( ftype ) {
case PLINK_OPEN :
if ( ! mesh_plink_free_count ( sdata ) | |
2008-02-25 18:27:46 +03:00
( sta - > plid & & sta - > plid ! = plid ) )
2008-02-23 17:17:13 +03:00
event = OPN_IGNR ;
else
event = OPN_ACPT ;
break ;
case PLINK_CONFIRM :
if ( ! mesh_plink_free_count ( sdata ) | |
2008-02-25 18:27:46 +03:00
( sta - > llid ! = llid | | sta - > plid ! = plid ) )
2008-02-23 17:17:13 +03:00
event = CNF_IGNR ;
else
event = CNF_ACPT ;
break ;
case PLINK_CLOSE :
2008-03-01 02:46:08 +03:00
if ( sta - > plink_state = = PLINK_ESTAB )
2008-02-23 17:17:13 +03:00
/* Do not check for llid or plid. This does not
* follow the standard but since multiple plinks
* per sta are not supported , it is necessary in
* order to avoid a livelock when MP A sees an
* establish peer link to MP B but MP B does not
* see it . This can be caused by a timeout in
* B ' s peer link establishment or B beign
* restarted .
*/
event = CLS_ACPT ;
else if ( sta - > plid ! = plid )
event = CLS_IGNR ;
else if ( ie_len = = 7 & & sta - > llid ! = llid )
event = CLS_IGNR ;
else
event = CLS_ACPT ;
break ;
default :
mpl_dbg ( " Mesh plink: unknown frame subtype \n " ) ;
2008-05-03 03:02:02 +04:00
spin_unlock_bh ( & sta - > lock ) ;
2008-02-25 18:27:46 +03:00
rcu_read_unlock ( ) ;
2008-02-23 17:17:13 +03:00
return ;
}
}
mpl_dbg ( " Mesh plink (peer, state, llid, plid, event): %s %d %d %d %d \n " ,
print_mac ( mac , mgmt - > sa ) , sta - > plink_state ,
2008-03-01 01:30:32 +03:00
le16_to_cpu ( sta - > llid ) , le16_to_cpu ( sta - > plid ) ,
2008-02-23 17:17:13 +03:00
event ) ;
reason = 0 ;
switch ( sta - > plink_state ) {
/* spin_unlock as soon as state is updated at each case */
2008-03-01 02:46:08 +03:00
case PLINK_LISTEN :
2008-02-23 17:17:13 +03:00
switch ( event ) {
case CLS_ACPT :
mesh_plink_fsm_restart ( sta ) ;
2008-05-03 03:02:02 +04:00
spin_unlock_bh ( & sta - > lock ) ;
2008-02-23 17:17:13 +03:00
break ;
case OPN_ACPT :
2008-03-01 02:46:08 +03:00
sta - > plink_state = PLINK_OPN_RCVD ;
2008-02-23 17:17:13 +03:00
sta - > plid = plid ;
get_random_bytes ( & llid , 2 ) ;
sta - > llid = llid ;
mesh_plink_timer_set ( sta , dot11MeshRetryTimeout ( sdata ) ) ;
2008-05-03 03:02:02 +04:00
spin_unlock_bh ( & sta - > lock ) ;
2008-09-11 02:02:02 +04:00
mesh_plink_frame_tx ( sdata , PLINK_OPEN , sta - > sta . addr , llid ,
2008-02-23 17:17:13 +03:00
0 , 0 ) ;
2008-09-11 02:02:02 +04:00
mesh_plink_frame_tx ( sdata , PLINK_CONFIRM , sta - > sta . addr ,
2008-02-23 17:17:13 +03:00
llid , plid , 0 ) ;
break ;
default :
2008-05-03 03:02:02 +04:00
spin_unlock_bh ( & sta - > lock ) ;
2008-02-23 17:17:13 +03:00
break ;
}
break ;
2008-03-01 02:46:08 +03:00
case PLINK_OPN_SNT :
2008-02-23 17:17:13 +03:00
switch ( event ) {
case OPN_RJCT :
case CNF_RJCT :
reason = cpu_to_le16 ( MESH_CAPABILITY_POLICY_VIOLATION ) ;
case CLS_ACPT :
if ( ! reason )
reason = cpu_to_le16 ( MESH_CLOSE_RCVD ) ;
sta - > reason = reason ;
2008-03-01 02:46:08 +03:00
sta - > plink_state = PLINK_HOLDING ;
2008-02-23 17:17:13 +03:00
if ( ! mod_plink_timer ( sta ,
dot11MeshHoldingTimeout ( sdata ) ) )
sta - > ignore_plink_timer = true ;
llid = sta - > llid ;
2008-05-03 03:02:02 +04:00
spin_unlock_bh ( & sta - > lock ) ;
2008-09-11 02:02:02 +04:00
mesh_plink_frame_tx ( sdata , PLINK_CLOSE , sta - > sta . addr , llid ,
2008-02-23 17:17:13 +03:00
plid , reason ) ;
break ;
case OPN_ACPT :
/* retry timer is left untouched */
2008-03-01 02:46:08 +03:00
sta - > plink_state = PLINK_OPN_RCVD ;
2008-02-23 17:17:13 +03:00
sta - > plid = plid ;
llid = sta - > llid ;
2008-05-03 03:02:02 +04:00
spin_unlock_bh ( & sta - > lock ) ;
2008-09-11 02:02:02 +04:00
mesh_plink_frame_tx ( sdata , PLINK_CONFIRM , sta - > sta . addr , llid ,
2008-02-23 17:17:13 +03:00
plid , 0 ) ;
break ;
case CNF_ACPT :
2008-03-01 02:46:08 +03:00
sta - > plink_state = PLINK_CNF_RCVD ;
2008-02-23 17:17:13 +03:00
if ( ! mod_plink_timer ( sta ,
dot11MeshConfirmTimeout ( sdata ) ) )
sta - > ignore_plink_timer = true ;
2008-05-03 03:02:02 +04:00
spin_unlock_bh ( & sta - > lock ) ;
2008-02-23 17:17:13 +03:00
break ;
default :
2008-05-03 03:02:02 +04:00
spin_unlock_bh ( & sta - > lock ) ;
2008-02-23 17:17:13 +03:00
break ;
}
break ;
2008-03-01 02:46:08 +03:00
case PLINK_OPN_RCVD :
2008-02-23 17:17:13 +03:00
switch ( event ) {
case OPN_RJCT :
case CNF_RJCT :
reason = cpu_to_le16 ( MESH_CAPABILITY_POLICY_VIOLATION ) ;
case CLS_ACPT :
if ( ! reason )
reason = cpu_to_le16 ( MESH_CLOSE_RCVD ) ;
sta - > reason = reason ;
2008-03-01 02:46:08 +03:00
sta - > plink_state = PLINK_HOLDING ;
2008-02-23 17:17:13 +03:00
if ( ! mod_plink_timer ( sta ,
dot11MeshHoldingTimeout ( sdata ) ) )
sta - > ignore_plink_timer = true ;
llid = sta - > llid ;
2008-05-03 03:02:02 +04:00
spin_unlock_bh ( & sta - > lock ) ;
2008-09-11 02:02:02 +04:00
mesh_plink_frame_tx ( sdata , PLINK_CLOSE , sta - > sta . addr , llid ,
2008-02-23 17:17:13 +03:00
plid , reason ) ;
break ;
case OPN_ACPT :
llid = sta - > llid ;
2008-05-03 03:02:02 +04:00
spin_unlock_bh ( & sta - > lock ) ;
2008-09-11 02:02:02 +04:00
mesh_plink_frame_tx ( sdata , PLINK_CONFIRM , sta - > sta . addr , llid ,
2008-02-23 17:17:13 +03:00
plid , 0 ) ;
break ;
case CNF_ACPT :
2008-02-25 18:27:46 +03:00
del_timer ( & sta - > plink_timer ) ;
2008-03-01 02:46:08 +03:00
sta - > plink_state = PLINK_ESTAB ;
2008-02-23 17:17:13 +03:00
mesh_plink_inc_estab_count ( sdata ) ;
2008-05-03 03:02:02 +04:00
spin_unlock_bh ( & sta - > lock ) ;
2008-02-23 17:17:13 +03:00
mpl_dbg ( " Mesh plink with %s ESTABLISHED \n " ,
2008-09-11 02:02:02 +04:00
print_mac ( mac , sta - > sta . addr ) ) ;
2008-02-23 17:17:13 +03:00
break ;
default :
2008-05-03 03:02:02 +04:00
spin_unlock_bh ( & sta - > lock ) ;
2008-02-23 17:17:13 +03:00
break ;
}
break ;
2008-03-01 02:46:08 +03:00
case PLINK_CNF_RCVD :
2008-02-23 17:17:13 +03:00
switch ( event ) {
case OPN_RJCT :
case CNF_RJCT :
reason = cpu_to_le16 ( MESH_CAPABILITY_POLICY_VIOLATION ) ;
case CLS_ACPT :
if ( ! reason )
reason = cpu_to_le16 ( MESH_CLOSE_RCVD ) ;
sta - > reason = reason ;
2008-03-01 02:46:08 +03:00
sta - > plink_state = PLINK_HOLDING ;
2008-02-23 17:17:13 +03:00
if ( ! mod_plink_timer ( sta ,
dot11MeshHoldingTimeout ( sdata ) ) )
sta - > ignore_plink_timer = true ;
llid = sta - > llid ;
2008-05-03 03:02:02 +04:00
spin_unlock_bh ( & sta - > lock ) ;
2008-09-11 02:02:02 +04:00
mesh_plink_frame_tx ( sdata , PLINK_CLOSE , sta - > sta . addr , llid ,
2008-02-23 17:17:13 +03:00
plid , reason ) ;
2008-02-25 12:11:50 +03:00
break ;
2008-02-23 17:17:13 +03:00
case OPN_ACPT :
2008-02-25 18:27:46 +03:00
del_timer ( & sta - > plink_timer ) ;
2008-03-01 02:46:08 +03:00
sta - > plink_state = PLINK_ESTAB ;
2008-02-23 17:17:13 +03:00
mesh_plink_inc_estab_count ( sdata ) ;
2008-05-03 03:02:02 +04:00
spin_unlock_bh ( & sta - > lock ) ;
2008-02-23 17:17:13 +03:00
mpl_dbg ( " Mesh plink with %s ESTABLISHED \n " ,
2008-09-11 02:02:02 +04:00
print_mac ( mac , sta - > sta . addr ) ) ;
mesh_plink_frame_tx ( sdata , PLINK_CONFIRM , sta - > sta . addr , llid ,
2008-02-23 17:17:13 +03:00
plid , 0 ) ;
break ;
default :
2008-05-03 03:02:02 +04:00
spin_unlock_bh ( & sta - > lock ) ;
2008-02-23 17:17:13 +03:00
break ;
}
break ;
2008-03-01 02:46:08 +03:00
case PLINK_ESTAB :
2008-02-23 17:17:13 +03:00
switch ( event ) {
case CLS_ACPT :
reason = cpu_to_le16 ( MESH_CLOSE_RCVD ) ;
sta - > reason = reason ;
2008-02-23 17:17:19 +03:00
__mesh_plink_deactivate ( sta ) ;
2008-03-01 02:46:08 +03:00
sta - > plink_state = PLINK_HOLDING ;
2008-02-23 17:17:13 +03:00
llid = sta - > llid ;
2008-02-25 18:27:46 +03:00
mod_plink_timer ( sta , dot11MeshHoldingTimeout ( sdata ) ) ;
2008-05-03 03:02:02 +04:00
spin_unlock_bh ( & sta - > lock ) ;
2008-09-11 02:02:02 +04:00
mesh_plink_frame_tx ( sdata , PLINK_CLOSE , sta - > sta . addr , llid ,
2008-02-23 17:17:13 +03:00
plid , reason ) ;
break ;
case OPN_ACPT :
llid = sta - > llid ;
2008-05-03 03:02:02 +04:00
spin_unlock_bh ( & sta - > lock ) ;
2008-09-11 02:02:02 +04:00
mesh_plink_frame_tx ( sdata , PLINK_CONFIRM , sta - > sta . addr , llid ,
2008-02-23 17:17:13 +03:00
plid , 0 ) ;
break ;
default :
2008-05-03 03:02:02 +04:00
spin_unlock_bh ( & sta - > lock ) ;
2008-02-23 17:17:13 +03:00
break ;
}
break ;
2008-03-01 02:46:08 +03:00
case PLINK_HOLDING :
2008-02-23 17:17:13 +03:00
switch ( event ) {
case CLS_ACPT :
2008-02-25 18:27:46 +03:00
if ( del_timer ( & sta - > plink_timer ) )
2008-02-23 17:17:13 +03:00
sta - > ignore_plink_timer = 1 ;
mesh_plink_fsm_restart ( sta ) ;
2008-05-03 03:02:02 +04:00
spin_unlock_bh ( & sta - > lock ) ;
2008-02-23 17:17:13 +03:00
break ;
case OPN_ACPT :
case CNF_ACPT :
case OPN_RJCT :
case CNF_RJCT :
llid = sta - > llid ;
reason = sta - > reason ;
2008-05-03 03:02:02 +04:00
spin_unlock_bh ( & sta - > lock ) ;
2008-09-11 02:02:02 +04:00
mesh_plink_frame_tx ( sdata , PLINK_CLOSE , sta - > sta . addr ,
llid , plid , reason ) ;
2008-02-23 17:17:13 +03:00
break ;
default :
2008-05-03 03:02:02 +04:00
spin_unlock_bh ( & sta - > lock ) ;
2008-02-23 17:17:13 +03:00
}
break ;
default :
2008-03-01 02:46:08 +03:00
/* should not get here, PLINK_BLOCKED is dealt with at the
* beggining of the function
2008-02-23 17:17:13 +03:00
*/
2008-05-03 03:02:02 +04:00
spin_unlock_bh ( & sta - > lock ) ;
2008-02-23 17:17:13 +03:00
break ;
}
2008-02-25 18:27:46 +03:00
rcu_read_unlock ( ) ;
2008-02-23 17:17:13 +03:00
}