2014-07-05 01:34:38 +04:00
/* Copyright 2011-2014 Autronica Fire and Security AS
2013-10-31 00:10:47 +04:00
*
* This program is free software ; you can redistribute it and / or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation ; either version 2 of the License , or ( at your option )
* any later version .
*
* Author ( s ) :
2014-07-05 01:34:38 +04:00
* 2011 - 2014 Arvid Brodin , arvid . brodin @ alten . se
2013-10-31 00:10:47 +04:00
*
* The HSR spec says never to forward the same frame twice on the same
* interface . A frame is identified by its source MAC address and its HSR
* sequence number . This code keeps track of senders and their sequence numbers
* to allow filtering of duplicate frames , and to detect HSR ring errors .
*/
# include <linux/if_ether.h>
# include <linux/etherdevice.h>
# include <linux/slab.h>
# include <linux/rculist.h>
# include "hsr_main.h"
# include "hsr_framereg.h"
# include "hsr_netlink.h"
2014-07-05 01:34:38 +04:00
struct hsr_node {
struct list_head mac_list ;
unsigned char MacAddressA [ ETH_ALEN ] ;
unsigned char MacAddressB [ ETH_ALEN ] ;
2014-07-05 01:38:05 +04:00
/* Local slave through which AddrB frames are received from this node */
enum hsr_port_type AddrB_port ;
unsigned long time_in [ HSR_PT_PORTS ] ;
bool time_in_stale [ HSR_PT_PORTS ] ;
u16 seq_out [ HSR_PT_PORTS ] ;
2014-07-05 01:34:38 +04:00
struct rcu_head rcu_head ;
2013-10-31 00:10:47 +04:00
} ;
2014-07-05 01:41:03 +04:00
/* TODO: use hash lists for mac addresses (linux/jhash.h)? */
2013-10-31 00:10:47 +04:00
2014-07-05 01:41:03 +04:00
/* seq_nr_after(a, b) - return true if a is after (higher in sequence than) b,
* false otherwise .
2013-10-31 00:10:47 +04:00
*/
2014-07-05 01:41:03 +04:00
static bool seq_nr_after ( u16 a , u16 b )
2013-10-31 00:10:47 +04:00
{
2014-07-05 01:41:03 +04:00
/* Remove inconsistency where
* seq_nr_after ( a , b ) = = seq_nr_before ( a , b )
*/
if ( ( int ) b - a = = 32768 )
return false ;
2013-10-31 00:10:47 +04:00
2014-07-05 01:41:03 +04:00
return ( ( ( s16 ) ( b - a ) ) < 0 ) ;
2013-10-31 00:10:47 +04:00
}
2014-07-05 01:41:03 +04:00
# define seq_nr_before(a, b) seq_nr_after((b), (a))
# define seq_nr_after_or_eq(a, b) (!seq_nr_before((a), (b)))
# define seq_nr_before_or_eq(a, b) (!seq_nr_after((a), (b)))
2013-10-31 00:10:47 +04:00
2014-07-05 01:41:03 +04:00
bool hsr_addr_is_self ( struct hsr_priv * hsr , unsigned char * addr )
2013-10-31 00:10:47 +04:00
{
2014-07-05 01:34:38 +04:00
struct hsr_node * node ;
2013-10-31 00:10:47 +04:00
2014-07-05 01:41:03 +04:00
node = list_first_or_null_rcu ( & hsr - > self_node_db , struct hsr_node ,
mac_list ) ;
if ( ! node ) {
WARN_ONCE ( 1 , " HSR: No self node \n " ) ;
return false ;
2013-10-31 00:10:47 +04:00
}
2014-07-05 01:41:03 +04:00
if ( ether_addr_equal ( addr , node - > MacAddressA ) )
return true ;
if ( ether_addr_equal ( addr , node - > MacAddressB ) )
return true ;
2013-10-31 00:10:47 +04:00
2014-07-05 01:41:03 +04:00
return false ;
}
2013-10-31 00:10:47 +04:00
/* Search for mac entry. Caller must hold rcu read lock.
*/
2014-07-05 01:41:03 +04:00
static struct hsr_node * find_node_by_AddrA ( struct list_head * node_db ,
const unsigned char addr [ ETH_ALEN ] )
2013-10-31 00:10:47 +04:00
{
2014-07-05 01:34:38 +04:00
struct hsr_node * node ;
2013-10-31 00:10:47 +04:00
list_for_each_entry_rcu ( node , node_db , mac_list ) {
2014-07-05 01:41:03 +04:00
if ( ether_addr_equal ( node - > MacAddressA , addr ) )
2013-10-31 00:10:47 +04:00
return node ;
}
return NULL ;
}
/* Helper for device init; the self_node_db is used in hsr_rcv() to recognize
* frames from self that ' s been looped over the HSR ring .
*/
int hsr_create_self_node ( struct list_head * self_node_db ,
unsigned char addr_a [ ETH_ALEN ] ,
unsigned char addr_b [ ETH_ALEN ] )
{
2014-07-05 01:34:38 +04:00
struct hsr_node * node , * oldnode ;
2013-10-31 00:10:47 +04:00
node = kmalloc ( sizeof ( * node ) , GFP_KERNEL ) ;
if ( ! node )
return - ENOMEM ;
2014-02-18 22:37:20 +04:00
ether_addr_copy ( node - > MacAddressA , addr_a ) ;
ether_addr_copy ( node - > MacAddressB , addr_b ) ;
2013-10-31 00:10:47 +04:00
rcu_read_lock ( ) ;
oldnode = list_first_or_null_rcu ( self_node_db ,
2014-07-05 01:34:38 +04:00
struct hsr_node , mac_list ) ;
2013-10-31 00:10:47 +04:00
if ( oldnode ) {
list_replace_rcu ( & oldnode - > mac_list , & node - > mac_list ) ;
rcu_read_unlock ( ) ;
synchronize_rcu ( ) ;
kfree ( oldnode ) ;
} else {
rcu_read_unlock ( ) ;
list_add_tail_rcu ( & node - > mac_list , self_node_db ) ;
}
return 0 ;
}
2014-07-05 01:41:03 +04:00
/* Allocate an hsr_node and add it to node_db. 'addr' is the node's AddressA;
* seq_out is used to initialize filtering of outgoing duplicate frames
* originating from the newly added node .
2013-10-31 00:10:47 +04:00
*/
2014-07-05 01:41:03 +04:00
struct hsr_node * hsr_add_node ( struct list_head * node_db , unsigned char addr [ ] ,
u16 seq_out )
2013-10-31 00:10:47 +04:00
{
2014-07-05 01:41:03 +04:00
struct hsr_node * node ;
2013-10-31 00:10:47 +04:00
unsigned long now ;
2014-07-05 01:41:03 +04:00
int i ;
2013-10-31 00:10:47 +04:00
node = kzalloc ( sizeof ( * node ) , GFP_ATOMIC ) ;
if ( ! node )
return NULL ;
2014-07-05 01:41:03 +04:00
ether_addr_copy ( node - > MacAddressA , addr ) ;
2013-10-31 00:10:47 +04:00
/* We are only interested in time diffs here, so use current jiffies
* as initialization . ( 0 could trigger an spurious ring error warning ) .
*/
now = jiffies ;
2014-07-05 01:38:05 +04:00
for ( i = 0 ; i < HSR_PT_PORTS ; i + + )
2013-10-31 00:10:47 +04:00
node - > time_in [ i ] = now ;
2014-07-05 01:38:05 +04:00
for ( i = 0 ; i < HSR_PT_PORTS ; i + + )
2014-07-05 01:41:03 +04:00
node - > seq_out [ i ] = seq_out ;
2013-10-31 00:10:47 +04:00
2014-07-05 01:41:03 +04:00
list_add_tail_rcu ( & node - > mac_list , node_db ) ;
2013-10-31 00:10:47 +04:00
return node ;
}
2014-07-05 01:41:03 +04:00
/* Get the hsr_node from which 'skb' was sent.
*/
struct hsr_node * hsr_get_node ( struct list_head * node_db , struct sk_buff * skb ,
bool is_sup )
{
struct hsr_node * node ;
struct ethhdr * ethhdr ;
u16 seq_out ;
if ( ! skb_mac_header_was_set ( skb ) )
return NULL ;
ethhdr = ( struct ethhdr * ) skb_mac_header ( skb ) ;
list_for_each_entry_rcu ( node , node_db , mac_list ) {
if ( ether_addr_equal ( node - > MacAddressA , ethhdr - > h_source ) )
return node ;
if ( ether_addr_equal ( node - > MacAddressB , ethhdr - > h_source ) )
return node ;
}
if ( ! is_sup )
return NULL ; /* Only supervision frame may create node entry */
if ( ethhdr - > h_proto = = htons ( ETH_P_PRP ) ) {
/* Use the existing sequence_nr from the tag as starting point
* for filtering duplicate frames .
*/
seq_out = hsr_get_skb_sequence_nr ( skb ) - 1 ;
} else {
WARN_ONCE ( 1 , " %s: Non-HSR frame \n " , __func__ ) ;
seq_out = 0 ;
}
return hsr_add_node ( node_db , ethhdr - > h_source , seq_out ) ;
}
/* Use the Supervision frame's info about an eventual MacAddressB for merging
* nodes that has previously had their MacAddressB registered as a separate
* node .
*/
void hsr_handle_sup_frame ( struct sk_buff * skb , struct hsr_node * node_curr ,
struct hsr_port * port_rcv )
{
struct hsr_node * node_real ;
struct hsr_sup_payload * hsr_sp ;
struct list_head * node_db ;
int i ;
skb_pull ( skb , sizeof ( struct hsr_ethhdr_sp ) ) ;
hsr_sp = ( struct hsr_sup_payload * ) skb - > data ;
if ( ether_addr_equal ( eth_hdr ( skb ) - > h_source , hsr_sp - > MacAddressA ) )
/* Not sent from MacAddressB of a PICS_SUBS capable node */
goto done ;
/* Merge node_curr (registered on MacAddressB) into node_real */
node_db = & port_rcv - > hsr - > node_db ;
node_real = find_node_by_AddrA ( node_db , hsr_sp - > MacAddressA ) ;
if ( ! node_real )
/* No frame received from AddrA of this node yet */
node_real = hsr_add_node ( node_db , hsr_sp - > MacAddressA ,
HSR_SEQNR_START - 1 ) ;
if ( ! node_real )
goto done ; /* No mem */
if ( node_real = = node_curr )
/* Node has already been merged */
goto done ;
ether_addr_copy ( node_real - > MacAddressB , eth_hdr ( skb ) - > h_source ) ;
for ( i = 0 ; i < HSR_PT_PORTS ; i + + ) {
if ( ! node_curr - > time_in_stale [ i ] & &
time_after ( node_curr - > time_in [ i ] , node_real - > time_in [ i ] ) ) {
node_real - > time_in [ i ] = node_curr - > time_in [ i ] ;
node_real - > time_in_stale [ i ] = node_curr - > time_in_stale [ i ] ;
}
if ( seq_nr_after ( node_curr - > seq_out [ i ] , node_real - > seq_out [ i ] ) )
node_real - > seq_out [ i ] = node_curr - > seq_out [ i ] ;
}
node_real - > AddrB_port = port_rcv - > type ;
list_del_rcu ( & node_curr - > mac_list ) ;
kfree_rcu ( node_curr , rcu_head ) ;
done :
skb_push ( skb , sizeof ( struct hsr_ethhdr_sp ) ) ;
}
2013-10-31 00:10:47 +04:00
/* 'skb' is a frame meant for this host, that is to be passed to upper layers.
*
2014-07-05 01:41:03 +04:00
* If the frame was sent by a node ' s B interface , replace the source
2013-10-31 00:10:47 +04:00
* address with that node ' s " official " address ( MacAddressA ) so that upper
* layers recognize where it came from .
*/
2014-07-05 01:41:03 +04:00
void hsr_addr_subst_source ( struct hsr_node * node , struct sk_buff * skb )
2013-10-31 00:10:47 +04:00
{
if ( ! skb_mac_header_was_set ( skb ) ) {
WARN_ONCE ( 1 , " %s: Mac header not set \n " , __func__ ) ;
return ;
}
2014-07-05 01:41:03 +04:00
memcpy ( & eth_hdr ( skb ) - > h_source , node - > MacAddressA , ETH_ALEN ) ;
2013-10-31 00:10:47 +04:00
}
/* 'skb' is a frame meant for another host.
2014-07-05 01:41:03 +04:00
* ' port ' is the outgoing interface
2013-10-31 00:10:47 +04:00
*
* Substitute the target ( dest ) MAC address if necessary , so the it matches the
* recipient interface MAC address , regardless of whether that is the
* recipient ' s A or B interface .
* This is needed to keep the packets flowing through switches that learn on
* which " side " the different interfaces are .
*/
2014-07-05 01:41:03 +04:00
void hsr_addr_subst_dest ( struct hsr_node * node_src , struct sk_buff * skb ,
2014-07-05 01:38:05 +04:00
struct hsr_port * port )
2013-10-31 00:10:47 +04:00
{
2014-07-05 01:41:03 +04:00
struct hsr_node * node_dst ;
2013-10-31 00:10:47 +04:00
2014-07-05 01:41:03 +04:00
if ( ! skb_mac_header_was_set ( skb ) ) {
WARN_ONCE ( 1 , " %s: Mac header not set \n " , __func__ ) ;
return ;
}
2013-10-31 00:10:47 +04:00
2014-07-05 01:41:03 +04:00
if ( ! is_unicast_ether_addr ( eth_hdr ( skb ) - > h_dest ) )
return ;
2013-10-31 00:10:47 +04:00
2014-07-05 01:41:03 +04:00
node_dst = find_node_by_AddrA ( & port - > hsr - > node_db , eth_hdr ( skb ) - > h_dest ) ;
if ( ! node_dst ) {
WARN_ONCE ( 1 , " %s: Unknown node \n " , __func__ ) ;
return ;
}
if ( port - > type ! = node_dst - > AddrB_port )
return ;
2013-10-31 00:10:47 +04:00
2014-07-05 01:41:03 +04:00
ether_addr_copy ( eth_hdr ( skb ) - > h_dest , node_dst - > MacAddressB ) ;
2013-10-31 00:10:47 +04:00
}
2014-07-05 01:41:03 +04:00
void hsr_register_frame_in ( struct hsr_node * node , struct hsr_port * port ,
u16 sequence_nr )
2013-10-31 00:10:47 +04:00
{
2014-07-05 01:41:03 +04:00
/* Don't register incoming frames without a valid sequence number. This
* ensures entries of restarted nodes gets pruned so that they can
* re - register and resume communications .
*/
if ( seq_nr_before ( sequence_nr , node - > seq_out [ port - > type ] ) )
return ;
2014-07-05 01:38:05 +04:00
node - > time_in [ port - > type ] = jiffies ;
node - > time_in_stale [ port - > type ] = false ;
2013-10-31 00:10:47 +04:00
}
/* 'skb' is a HSR Ethernet frame (with a HSR tag inserted), with a valid
* ethhdr - > h_source address and skb - > mac_header set .
*
* Return :
* 1 if frame can be shown to have been sent recently on this interface ,
* 0 otherwise , or
* negative error code on error
*/
2014-07-05 01:41:03 +04:00
int hsr_register_frame_out ( struct hsr_port * port , struct hsr_node * node ,
u16 sequence_nr )
2013-10-31 00:10:47 +04:00
{
2014-07-05 01:38:05 +04:00
if ( seq_nr_before_or_eq ( sequence_nr , node - > seq_out [ port - > type ] ) )
2013-10-31 00:10:47 +04:00
return 1 ;
2014-07-05 01:38:05 +04:00
node - > seq_out [ port - > type ] = sequence_nr ;
2013-10-31 00:10:47 +04:00
return 0 ;
}
2014-07-05 01:38:05 +04:00
static struct hsr_port * get_late_port ( struct hsr_priv * hsr ,
struct hsr_node * node )
2013-10-31 00:10:47 +04:00
{
2014-07-05 01:38:05 +04:00
if ( node - > time_in_stale [ HSR_PT_SLAVE_A ] )
return hsr_port_get_hsr ( hsr , HSR_PT_SLAVE_A ) ;
if ( node - > time_in_stale [ HSR_PT_SLAVE_B ] )
return hsr_port_get_hsr ( hsr , HSR_PT_SLAVE_B ) ;
if ( time_after ( node - > time_in [ HSR_PT_SLAVE_B ] ,
node - > time_in [ HSR_PT_SLAVE_A ] +
msecs_to_jiffies ( MAX_SLAVE_DIFF ) ) )
return hsr_port_get_hsr ( hsr , HSR_PT_SLAVE_A ) ;
if ( time_after ( node - > time_in [ HSR_PT_SLAVE_A ] ,
node - > time_in [ HSR_PT_SLAVE_B ] +
msecs_to_jiffies ( MAX_SLAVE_DIFF ) ) )
return hsr_port_get_hsr ( hsr , HSR_PT_SLAVE_B ) ;
2013-10-31 00:10:47 +04:00
2014-07-05 01:38:05 +04:00
return NULL ;
2013-10-31 00:10:47 +04:00
}
/* Remove stale sequence_nr records. Called by timer every
* HSR_LIFE_CHECK_INTERVAL ( two seconds or so ) .
*/
2014-07-05 01:35:47 +04:00
void hsr_prune_nodes ( unsigned long data )
2013-10-31 00:10:47 +04:00
{
2014-07-05 01:35:47 +04:00
struct hsr_priv * hsr ;
2014-07-05 01:34:38 +04:00
struct hsr_node * node ;
2014-07-05 01:38:05 +04:00
struct hsr_port * port ;
2013-10-31 00:10:47 +04:00
unsigned long timestamp ;
unsigned long time_a , time_b ;
2014-07-05 01:35:47 +04:00
hsr = ( struct hsr_priv * ) data ;
2013-10-31 00:10:47 +04:00
rcu_read_lock ( ) ;
2014-07-05 01:34:38 +04:00
list_for_each_entry_rcu ( node , & hsr - > node_db , mac_list ) {
2013-10-31 00:10:47 +04:00
/* Shorthand */
2014-07-05 01:38:05 +04:00
time_a = node - > time_in [ HSR_PT_SLAVE_A ] ;
time_b = node - > time_in [ HSR_PT_SLAVE_B ] ;
2013-10-31 00:10:47 +04:00
/* Check for timestamps old enough to risk wrap-around */
if ( time_after ( jiffies , time_a + MAX_JIFFY_OFFSET / 2 ) )
2014-07-05 01:38:05 +04:00
node - > time_in_stale [ HSR_PT_SLAVE_A ] = true ;
2013-10-31 00:10:47 +04:00
if ( time_after ( jiffies , time_b + MAX_JIFFY_OFFSET / 2 ) )
2014-07-05 01:38:05 +04:00
node - > time_in_stale [ HSR_PT_SLAVE_B ] = true ;
2013-10-31 00:10:47 +04:00
/* Get age of newest frame from node.
* At least one time_in is OK here ; nodes get pruned long
* before both time_ins can get stale
*/
timestamp = time_a ;
2014-07-05 01:38:05 +04:00
if ( node - > time_in_stale [ HSR_PT_SLAVE_A ] | |
( ! node - > time_in_stale [ HSR_PT_SLAVE_B ] & &
2013-10-31 00:10:47 +04:00
time_after ( time_b , time_a ) ) )
timestamp = time_b ;
/* Warn of ring error only as long as we get frames at all */
if ( time_is_after_jiffies ( timestamp +
msecs_to_jiffies ( 1.5 * MAX_SLAVE_DIFF ) ) ) {
2014-07-05 01:38:05 +04:00
rcu_read_lock ( ) ;
port = get_late_port ( hsr , node ) ;
if ( port ! = NULL )
hsr_nl_ringerror ( hsr , node - > MacAddressA , port ) ;
rcu_read_unlock ( ) ;
2013-10-31 00:10:47 +04:00
}
/* Prune old entries */
if ( time_is_before_jiffies ( timestamp +
msecs_to_jiffies ( HSR_NODE_FORGET_TIME ) ) ) {
2014-07-05 01:34:38 +04:00
hsr_nl_nodedown ( hsr , node - > MacAddressA ) ;
2013-10-31 00:10:47 +04:00
list_del_rcu ( & node - > mac_list ) ;
/* Note that we need to free this entry later: */
2013-12-16 10:05:50 +04:00
kfree_rcu ( node , rcu_head ) ;
2013-10-31 00:10:47 +04:00
}
}
rcu_read_unlock ( ) ;
}
2014-07-05 01:34:38 +04:00
void * hsr_get_next_node ( struct hsr_priv * hsr , void * _pos ,
2013-10-31 00:10:47 +04:00
unsigned char addr [ ETH_ALEN ] )
{
2014-07-05 01:34:38 +04:00
struct hsr_node * node ;
2013-10-31 00:10:47 +04:00
if ( ! _pos ) {
2014-07-05 01:34:38 +04:00
node = list_first_or_null_rcu ( & hsr - > node_db ,
struct hsr_node , mac_list ) ;
2013-10-31 00:10:47 +04:00
if ( node )
2014-02-18 22:37:20 +04:00
ether_addr_copy ( addr , node - > MacAddressA ) ;
2013-10-31 00:10:47 +04:00
return node ;
}
node = _pos ;
2014-07-05 01:34:38 +04:00
list_for_each_entry_continue_rcu ( node , & hsr - > node_db , mac_list ) {
2014-02-18 22:37:20 +04:00
ether_addr_copy ( addr , node - > MacAddressA ) ;
2013-10-31 00:10:47 +04:00
return node ;
}
return NULL ;
}
2014-07-05 01:34:38 +04:00
int hsr_get_node_data ( struct hsr_priv * hsr ,
2013-10-31 00:10:47 +04:00
const unsigned char * addr ,
unsigned char addr_b [ ETH_ALEN ] ,
unsigned int * addr_b_ifindex ,
int * if1_age ,
u16 * if1_seq ,
int * if2_age ,
u16 * if2_seq )
{
2014-07-05 01:34:38 +04:00
struct hsr_node * node ;
2014-07-05 01:38:05 +04:00
struct hsr_port * port ;
2013-10-31 00:10:47 +04:00
unsigned long tdiff ;
rcu_read_lock ( ) ;
2014-07-05 01:34:38 +04:00
node = find_node_by_AddrA ( & hsr - > node_db , addr ) ;
2013-10-31 00:10:47 +04:00
if ( ! node ) {
rcu_read_unlock ( ) ;
return - ENOENT ; /* No such entry */
}
2014-02-18 22:37:20 +04:00
ether_addr_copy ( addr_b , node - > MacAddressB ) ;
2013-10-31 00:10:47 +04:00
2014-07-05 01:38:05 +04:00
tdiff = jiffies - node - > time_in [ HSR_PT_SLAVE_A ] ;
if ( node - > time_in_stale [ HSR_PT_SLAVE_A ] )
2013-10-31 00:10:47 +04:00
* if1_age = INT_MAX ;
# if HZ <= MSEC_PER_SEC
else if ( tdiff > msecs_to_jiffies ( INT_MAX ) )
* if1_age = INT_MAX ;
# endif
else
* if1_age = jiffies_to_msecs ( tdiff ) ;
2014-07-05 01:38:05 +04:00
tdiff = jiffies - node - > time_in [ HSR_PT_SLAVE_B ] ;
if ( node - > time_in_stale [ HSR_PT_SLAVE_B ] )
2013-10-31 00:10:47 +04:00
* if2_age = INT_MAX ;
# if HZ <= MSEC_PER_SEC
else if ( tdiff > msecs_to_jiffies ( INT_MAX ) )
* if2_age = INT_MAX ;
# endif
else
* if2_age = jiffies_to_msecs ( tdiff ) ;
/* Present sequence numbers as if they were incoming on interface */
2014-07-05 01:38:05 +04:00
* if1_seq = node - > seq_out [ HSR_PT_SLAVE_B ] ;
* if2_seq = node - > seq_out [ HSR_PT_SLAVE_A ] ;
2013-10-31 00:10:47 +04:00
2014-07-05 01:38:05 +04:00
if ( node - > AddrB_port ! = HSR_PT_NONE ) {
port = hsr_port_get_hsr ( hsr , node - > AddrB_port ) ;
* addr_b_ifindex = port - > dev - > ifindex ;
} else {
2013-10-31 00:10:47 +04:00
* addr_b_ifindex = - 1 ;
2014-07-05 01:38:05 +04:00
}
2013-10-31 00:10:47 +04:00
rcu_read_unlock ( ) ;
return 0 ;
}