2013-09-16 21:57:40 +00:00
/*
* BIRD - - Bidirectional Forwarding Detection ( BFD )
*
* Can be freely distributed and used under the terms of the GNU GPL .
*/
2013-09-10 10:09:36 +00:00
2013-11-19 21:33:48 +00:00
/**
* DOC : Bidirectional Forwarding Detection
*
* The BFD protocol is implemented in three files : | bfd . c | containing the
* protocol logic and the protocol glue with BIRD core , | packets . c | handling BFD
* packet processing , RX , TX and protocol sockets . | io . c | then contains generic
* code for the event loop , threads and event sources ( sockets , microsecond
* timers ) . This generic code will be merged to the main BIRD I / O code in the
* future .
*
* The BFD implementation uses a separate thread with an internal event loop for
* handling the protocol logic , which requires high - res and low - latency timing ,
* so it is not affected by the rest of BIRD , which has several low - granularity
* hooks in the main loop , uses second - based timers and cannot offer good
* latency . The core of BFD protocol ( the code related to BFD sessions ,
* interfaces and packets ) runs in the BFD thread , while the rest ( the code
* related to BFD requests , BFD neighbors and the protocol glue ) runs in the
* main thread .
*
* BFD sessions are represented by structure & bfd_session that contains a state
* related to the session and two timers ( TX timer for periodic packets and hold
* timer for session timeout ) . These sessions are allocated from @ session_slab
* and are accessible by two hash tables , @ session_hash_id ( by session ID ) and
2021-01-10 14:29:02 +00:00
* @ session_hash_ip ( by IP addresses of neighbors and associated interfaces ) .
* Slab and both hashes are in the main protocol structure & bfd_proto . The
* protocol logic related to BFD sessions is implemented in internal functions
* bfd_session_ * ( ) , which are expected to be called from the context of BFD
* thread , and external functions bfd_add_session ( ) , bfd_remove_session ( ) and
* bfd_reconfigure_session ( ) , which form an interface to the BFD core for the
* rest and are expected to be called from the context of main thread .
2013-11-19 21:33:48 +00:00
*
* Each BFD session has an associated BFD interface , represented by structure
* & bfd_iface . A BFD interface contains a socket used for TX ( the one for RX is
* shared in & bfd_proto ) , an interface configuration and reference counter .
* Compared to interface structures of other protocols , these structures are not
* created and removed based on interface notification events , but according to
* the needs of BFD sessions . When a new session is created , it requests a
* proper BFD interface by function bfd_get_iface ( ) , which either finds an
* existing one in & iface_list ( from & bfd_proto ) or allocates a new one . When a
2015-07-19 09:39:24 +00:00
* session is removed , an associated iface is discharged by bfd_free_iface ( ) .
2013-11-19 21:33:48 +00:00
*
* BFD requests are the external API for the other protocols . When a protocol
* wants a BFD session , it calls bfd_request_session ( ) , which creates a
* structure & bfd_request containing approprite information and an notify hook .
* This structure is a resource associated with the caller ' s resource pool . When
* a BFD protocol is available , a BFD request is submitted to the protocol , an
* appropriate BFD session is found or created and the request is attached to
* the session . When a session changes state , all attached requests ( and related
* protocols ) are notified . Note that BFD requests do not depend on BFD protocol
* running . When the BFD protocol is stopped or removed ( or not available from
* beginning ) , related BFD requests are stored in @ bfd_wait_list , where waits
* for a new protocol .
*
* BFD neighbors are just a way to statically configure BFD sessions without
* requests from other protocol . Structures & bfd_neighbor are part of BFD
* configuration ( like static routes in the static protocol ) . BFD neighbors are
* handled by BFD protocol like it is a BFD client - - when a BFD neighbor is
* ready , the protocol just creates a BFD request like any other protocol .
2015-07-19 09:39:24 +00:00
*
2013-11-19 21:33:48 +00:00
* The protocol uses a new generic event loop ( structure & birdloop ) from | io . c | ,
2017-11-28 16:43:20 +00:00
* which supports sockets , timers and events like the main loop . A birdloop is
* associated with a thread ( field @ thread ) in which event hooks are executed .
* Most functions for setting event sources ( like sk_start ( ) or tm_start ( ) ) must
* be called from the context of that thread . Birdloop allows to temporarily
* acquire the context of that thread for the main thread by calling
* birdloop_enter ( ) and then birdloop_leave ( ) , which also ensures mutual
* exclusion with all event hooks . Note that resources associated with a
* birdloop ( like timers ) should be attached to the independent resource pool ,
* detached from the main resource tree .
2013-11-19 21:33:48 +00:00
*
* There are two kinds of interaction between the BFD core ( running in the BFD
* thread ) and the rest of BFD ( running in the main thread ) . The first kind are
* configuration calls from main thread to the BFD thread ( like bfd_add_session ( ) ) .
* These calls are synchronous and use birdloop_enter ( ) mechanism for mutual
* exclusion . The second kind is a notification about session changes from the
* BFD thread to the main thread . This is done in an asynchronous way , sesions
* with pending notifications are linked ( in the BFD thread ) to @ notify_list in
* & bfd_proto , and then bfd_notify_hook ( ) in the main thread is activated using
* bfd_notify_kick ( ) and a pipe . The hook then processes scheduled sessions and
* calls hooks from associated BFD requests . This @ notify_list ( and state fields
* in structure & bfd_session ) is protected by a spinlock in & bfd_proto and
* functions bfd_lock_sessions ( ) / bfd_unlock_sessions ( ) .
*
* There are few data races ( accessing @ p - > p . debug from TRACE ( ) from the BFD
* thread and accessing some some private fields of % bfd_session from
* bfd_show_sessions ( ) from the main thread , but these are harmless ( i hope ) .
*
* TODO : document functions and access restrictions for fields in BFD structures .
*
* Supported standards :
* - RFC 5880 - main BFD standard
* - RFC 5881 - BFD for IP links
* - RFC 5882 - generic application of BFD
* - RFC 5883 - BFD for multihop paths
*/
2013-09-10 10:09:36 +00:00
# include "bfd.h"
2013-09-16 21:57:40 +00:00
# define HASH_ID_KEY(n) n->loc_id
# define HASH_ID_NEXT(n) n->next_id
2013-11-26 21:37:24 +00:00
# define HASH_ID_EQ(a,b) a == b
# define HASH_ID_FN(k) k
2013-09-10 10:09:36 +00:00
2021-01-10 14:29:02 +00:00
# define HASH_IP_KEY(n) n->addr, n->ifindex
2013-09-16 21:57:40 +00:00
# define HASH_IP_NEXT(n) n->next_ip
2021-01-10 14:29:02 +00:00
# define HASH_IP_EQ(a1,n1,a2,n2) ipa_equal(a1, a2) && n1 == n2
# define HASH_IP_FN(a,n) ipa_hash(a) ^ u32_hash(n)
2013-09-10 10:09:36 +00:00
2022-03-18 21:05:50 +00:00
static list STATIC_LIST_INIT ( bfd_proto_list ) ;
static list STATIC_LIST_INIT ( bfd_wait_list ) ;
2013-10-05 18:12:28 +00:00
const char * bfd_state_names [ ] = { " AdminDown " , " Down " , " Init " , " Up " } ;
2024-01-16 10:02:22 +00:00
const char * bfd_diag_names [ ] = { " Nothing " , " Timeout " , " Echo failed " , " Neighbor down " , " Fwd reset " , " Path down " , " C path down " , " Admin down " , " RC path down " } ;
2013-10-05 18:12:28 +00:00
2013-11-19 21:33:48 +00:00
static void bfd_session_set_min_tx ( struct bfd_session * s , u32 val ) ;
static struct bfd_iface * bfd_get_iface ( struct bfd_proto * p , ip_addr local , struct iface * iface ) ;
static void bfd_free_iface ( struct bfd_iface * ifa ) ;
2013-09-16 21:57:40 +00:00
static inline void bfd_notify_kick ( struct bfd_proto * p ) ;
2013-09-10 10:09:36 +00:00
2013-11-19 21:33:48 +00:00
/*
* BFD sessions
*/
2020-11-08 14:33:22 +00:00
static inline struct bfd_session_config
bfd_merge_options ( const struct bfd_iface_config * cf , const struct bfd_options * opts )
{
return ( struct bfd_session_config ) {
. min_rx_int = opts - > min_rx_int ? : cf - > min_rx_int ,
. min_tx_int = opts - > min_tx_int ? : cf - > min_tx_int ,
. idle_tx_int = opts - > idle_tx_int ? : cf - > idle_tx_int ,
. multiplier = opts - > multiplier ? : cf - > multiplier ,
2024-01-22 05:29:14 +00:00
. passive = opts - > passive_set ? opts - > passive : cf - > passive ,
2020-11-08 14:33:22 +00:00
} ;
}
2015-07-19 09:39:24 +00:00
static void
2013-09-16 21:57:40 +00:00
bfd_session_update_state ( struct bfd_session * s , uint state , uint diag )
2013-09-10 10:09:36 +00:00
{
2013-11-19 21:33:48 +00:00
struct bfd_proto * p = s - > ifa - > bfd ;
2015-07-19 09:39:24 +00:00
uint old_state = s - > loc_state ;
2013-09-16 21:57:40 +00:00
int notify ;
2013-09-10 10:09:36 +00:00
2013-11-19 21:33:48 +00:00
if ( state = = old_state )
2013-09-16 21:57:40 +00:00
return ;
2013-09-10 10:09:36 +00:00
2013-11-19 21:33:48 +00:00
TRACE ( D_EVENTS , " Session to %I changed state from %s to %s " ,
s - > addr , bfd_state_names [ old_state ] , bfd_state_names [ state ] ) ;
2013-09-16 21:57:40 +00:00
bfd_lock_sessions ( p ) ;
s - > loc_state = state ;
s - > loc_diag = diag ;
2017-06-06 14:47:30 +00:00
s - > last_state_change = current_time ( ) ;
2013-09-10 10:09:36 +00:00
2013-09-16 21:57:40 +00:00
notify = ! NODE_VALID ( & s - > n ) ;
if ( notify )
add_tail ( & p - > notify_list , & s - > n ) ;
bfd_unlock_sessions ( p ) ;
2013-09-10 10:09:36 +00:00
2013-11-19 21:33:48 +00:00
if ( state = = BFD_STATE_UP )
2020-11-08 14:33:22 +00:00
bfd_session_set_min_tx ( s , s - > cf . min_tx_int ) ;
2013-09-10 10:09:36 +00:00
2013-11-19 21:33:48 +00:00
if ( old_state = = BFD_STATE_UP )
2020-11-08 14:33:22 +00:00
bfd_session_set_min_tx ( s , s - > cf . idle_tx_int ) ;
2013-09-10 10:09:36 +00:00
2013-11-19 21:33:48 +00:00
if ( notify )
bfd_notify_kick ( p ) ;
2013-09-10 10:09:36 +00:00
}
static void
2013-09-16 21:57:40 +00:00
bfd_session_update_tx_interval ( struct bfd_session * s )
2013-09-10 10:09:36 +00:00
{
2013-09-16 21:57:40 +00:00
u32 tx_int = MAX ( s - > des_min_tx_int , s - > rem_min_rx_int ) ;
u32 tx_int_l = tx_int - ( tx_int / 4 ) ; // 75 %
u32 tx_int_h = tx_int - ( tx_int / 10 ) ; // 90 %
2013-09-10 10:09:36 +00:00
2013-09-16 21:57:40 +00:00
s - > tx_timer - > recurrent = tx_int_l ;
s - > tx_timer - > randomize = tx_int_h - tx_int_l ;
2013-09-10 10:09:36 +00:00
2013-09-16 21:57:40 +00:00
/* Do not set timer if no previous event */
if ( ! s - > last_tx )
return ;
2013-09-10 10:09:36 +00:00
2013-09-16 21:57:40 +00:00
/* Set timer relative to last tx_timer event */
2017-11-28 16:43:20 +00:00
tm_set ( s - > tx_timer , s - > last_tx + tx_int_l ) ;
2013-09-10 10:09:36 +00:00
}
static void
2013-09-16 21:57:40 +00:00
bfd_session_update_detection_time ( struct bfd_session * s , int kick )
2013-09-10 10:09:36 +00:00
{
2013-09-16 21:57:40 +00:00
btime timeout = ( btime ) MAX ( s - > req_min_rx_int , s - > rem_min_tx_int ) * s - > rem_detect_mult ;
2013-09-10 10:09:36 +00:00
2013-09-16 21:57:40 +00:00
if ( kick )
s - > last_rx = current_time ( ) ;
2013-09-10 10:09:36 +00:00
2013-09-16 21:57:40 +00:00
if ( ! s - > last_rx )
return ;
2013-09-10 10:09:36 +00:00
2017-11-28 16:43:20 +00:00
tm_set ( s - > hold_timer , s - > last_rx + timeout ) ;
2013-09-10 10:09:36 +00:00
}
static void
2013-11-19 21:33:48 +00:00
bfd_session_control_tx_timer ( struct bfd_session * s , int reset )
2013-09-10 10:09:36 +00:00
{
2013-11-19 21:33:48 +00:00
// if (!s->opened) goto stop;
2013-09-10 10:09:36 +00:00
if ( s - > passive & & ( s - > rem_id = = 0 ) )
goto stop ;
2015-07-19 09:39:24 +00:00
if ( s - > rem_demand_mode & &
! s - > poll_active & &
2013-09-10 10:09:36 +00:00
( s - > loc_state = = BFD_STATE_UP ) & &
( s - > rem_state = = BFD_STATE_UP ) )
goto stop ;
if ( s - > rem_min_rx_int = = 0 )
goto stop ;
/* So TX timer should run */
2017-11-28 16:43:20 +00:00
if ( reset | | ! tm_active ( s - > tx_timer ) )
2013-11-19 21:33:48 +00:00
{
s - > last_tx = 0 ;
2017-11-28 16:43:20 +00:00
tm_start ( s - > tx_timer , 0 ) ;
2013-11-19 21:33:48 +00:00
}
2013-09-10 10:09:36 +00:00
return ;
2024-01-22 05:29:14 +00:00
stop :
2017-11-28 16:43:20 +00:00
tm_stop ( s - > tx_timer ) ;
2013-09-10 10:09:36 +00:00
s - > last_tx = 0 ;
}
static void
bfd_session_request_poll ( struct bfd_session * s , u8 request )
{
2013-11-19 21:33:48 +00:00
/* Not sure about this, but doing poll in this case does not make sense */
if ( s - > rem_id = = 0 )
return ;
2013-09-10 10:09:36 +00:00
s - > poll_scheduled | = request ;
if ( s - > poll_active )
return ;
s - > poll_active = s - > poll_scheduled ;
s - > poll_scheduled = 0 ;
2013-11-19 21:33:48 +00:00
bfd_session_control_tx_timer ( s , 1 ) ;
2013-09-10 10:09:36 +00:00
}
2013-09-16 21:57:40 +00:00
static void
2013-09-10 10:09:36 +00:00
bfd_session_terminate_poll ( struct bfd_session * s )
{
u8 poll_done = s - > poll_active & ~ s - > poll_scheduled ;
if ( poll_done & BFD_POLL_TX )
s - > des_min_tx_int = s - > des_min_tx_new ;
if ( poll_done & BFD_POLL_RX )
s - > req_min_rx_int = s - > req_min_rx_new ;
2013-11-19 21:33:48 +00:00
s - > poll_active = s - > poll_scheduled ;
s - > poll_scheduled = 0 ;
2013-09-10 10:09:36 +00:00
/* Timers are updated by caller - bfd_session_process_ctl() */
}
void
2013-09-16 21:57:40 +00:00
bfd_session_process_ctl ( struct bfd_session * s , u8 flags , u32 old_tx_int , u32 old_rx_int )
2013-09-10 10:09:36 +00:00
{
if ( s - > poll_active & & ( flags & BFD_FLAG_FINAL ) )
bfd_session_terminate_poll ( s ) ;
2013-09-16 21:57:40 +00:00
if ( ( s - > des_min_tx_int ! = old_tx_int ) | | ( s - > rem_min_rx_int ! = old_rx_int ) )
2013-09-10 10:09:36 +00:00
bfd_session_update_tx_interval ( s ) ;
bfd_session_update_detection_time ( s , 1 ) ;
/* Update session state */
int next_state = 0 ;
int diag = BFD_DIAG_NOTHING ;
switch ( s - > loc_state )
{
case BFD_STATE_ADMIN_DOWN :
return ;
case BFD_STATE_DOWN :
if ( s - > rem_state = = BFD_STATE_DOWN ) next_state = BFD_STATE_INIT ;
else if ( s - > rem_state = = BFD_STATE_INIT ) next_state = BFD_STATE_UP ;
break ;
case BFD_STATE_INIT :
if ( s - > rem_state = = BFD_STATE_ADMIN_DOWN ) next_state = BFD_STATE_DOWN , diag = BFD_DIAG_NEIGHBOR_DOWN ;
else if ( s - > rem_state > = BFD_STATE_INIT ) next_state = BFD_STATE_UP ;
break ;
case BFD_STATE_UP :
if ( s - > rem_state < = BFD_STATE_DOWN ) next_state = BFD_STATE_DOWN , diag = BFD_DIAG_NEIGHBOR_DOWN ;
break ;
}
if ( next_state )
bfd_session_update_state ( s , next_state , diag ) ;
2013-11-19 21:33:48 +00:00
bfd_session_control_tx_timer ( s , 0 ) ;
2013-09-10 10:09:36 +00:00
if ( flags & BFD_FLAG_POLL )
2013-11-19 21:33:48 +00:00
bfd_send_ctl ( s - > ifa - > bfd , s , 1 ) ;
}
2015-07-19 09:39:24 +00:00
static void
2013-11-19 21:33:48 +00:00
bfd_session_timeout ( struct bfd_session * s )
{
struct bfd_proto * p = s - > ifa - > bfd ;
TRACE ( D_EVENTS , " Session to %I expired " , s - > addr ) ;
s - > rem_state = BFD_STATE_DOWN ;
s - > rem_id = 0 ;
s - > rem_min_tx_int = 0 ;
s - > rem_min_rx_int = 1 ;
s - > rem_demand_mode = 0 ;
s - > rem_detect_mult = 0 ;
2016-10-30 22:51:23 +00:00
s - > rx_csn_known = 0 ;
2013-11-19 21:33:48 +00:00
s - > poll_active = 0 ;
s - > poll_scheduled = 0 ;
bfd_session_update_state ( s , BFD_STATE_DOWN , BFD_DIAG_TIMEOUT ) ;
bfd_session_control_tx_timer ( s , 1 ) ;
2013-09-10 10:09:36 +00:00
}
static void
bfd_session_set_min_tx ( struct bfd_session * s , u32 val )
{
/* Note that des_min_tx_int <= des_min_tx_new */
if ( val = = s - > des_min_tx_new )
return ;
s - > des_min_tx_new = val ;
/* Postpone timer update if des_min_tx_int increases and the session is up */
if ( ( s - > loc_state ! = BFD_STATE_UP ) | | ( val < s - > des_min_tx_int ) )
{
s - > des_min_tx_int = val ;
bfd_session_update_tx_interval ( s ) ;
}
bfd_session_request_poll ( s , BFD_POLL_TX ) ;
}
static void
bfd_session_set_min_rx ( struct bfd_session * s , u32 val )
{
/* Note that req_min_rx_int >= req_min_rx_new */
if ( val = = s - > req_min_rx_new )
return ;
2015-07-19 09:39:24 +00:00
s - > req_min_rx_new = val ;
2013-09-10 10:09:36 +00:00
/* Postpone timer update if req_min_rx_int decreases and the session is up */
if ( ( s - > loc_state ! = BFD_STATE_UP ) | | ( val > s - > req_min_rx_int ) )
{
s - > req_min_rx_int = val ;
bfd_session_update_detection_time ( s , 0 ) ;
}
bfd_session_request_poll ( s , BFD_POLL_RX ) ;
}
2013-09-16 21:57:40 +00:00
struct bfd_session *
bfd_find_session_by_id ( struct bfd_proto * p , u32 id )
{
return HASH_FIND ( p - > session_hash_id , HASH_ID , id ) ;
}
struct bfd_session *
2021-01-10 14:29:02 +00:00
bfd_find_session_by_addr ( struct bfd_proto * p , ip_addr addr , uint ifindex )
2013-09-16 21:57:40 +00:00
{
2021-01-10 14:29:02 +00:00
return HASH_FIND ( p - > session_hash_ip , HASH_IP , addr , ifindex ) ;
2013-09-16 21:57:40 +00:00
}
static void
2017-11-28 16:43:20 +00:00
bfd_tx_timer_hook ( timer * t )
2013-09-16 21:57:40 +00:00
{
struct bfd_session * s = t - > data ;
s - > last_tx = current_time ( ) ;
2013-11-19 21:33:48 +00:00
bfd_send_ctl ( s - > ifa - > bfd , s , 0 ) ;
2013-09-16 21:57:40 +00:00
}
static void
2017-11-28 16:43:20 +00:00
bfd_hold_timer_hook ( timer * t )
2013-09-16 21:57:40 +00:00
{
bfd_session_timeout ( t - > data ) ;
}
static u32
bfd_get_free_id ( struct bfd_proto * p )
{
u32 id ;
for ( id = random_u32 ( ) ; 1 ; id + + )
if ( id & & ! bfd_find_session_by_id ( p , id ) )
break ;
return id ;
}
static struct bfd_session *
2020-11-08 14:33:22 +00:00
bfd_add_session ( struct bfd_proto * p , ip_addr addr , ip_addr local , struct iface * iface , struct bfd_options * opts )
2013-09-16 21:57:40 +00:00
{
birdloop_enter ( p - > loop ) ;
2013-11-19 21:33:48 +00:00
struct bfd_iface * ifa = bfd_get_iface ( p , local , iface ) ;
2020-11-24 02:21:44 +00:00
struct bfd_session * s = sl_allocz ( p - > session_slab ) ;
2013-09-16 21:57:40 +00:00
s - > addr = addr ;
2013-11-19 21:33:48 +00:00
s - > ifa = ifa ;
2021-01-10 14:29:02 +00:00
s - > ifindex = iface ? iface - > index : 0 ;
2013-09-16 21:57:40 +00:00
s - > loc_id = bfd_get_free_id ( p ) ;
2013-11-19 21:33:48 +00:00
2013-09-16 21:57:40 +00:00
HASH_INSERT ( p - > session_hash_id , HASH_ID , s ) ;
HASH_INSERT ( p - > session_hash_ip , HASH_IP , s ) ;
2013-11-19 21:33:48 +00:00
2020-11-08 14:33:22 +00:00
s - > cf = bfd_merge_options ( ifa - > cf , opts ) ;
2013-09-16 21:57:40 +00:00
/* Initialization of state variables - see RFC 5880 6.8.1 */
s - > loc_state = BFD_STATE_DOWN ;
s - > rem_state = BFD_STATE_DOWN ;
2020-11-08 14:33:22 +00:00
s - > des_min_tx_int = s - > des_min_tx_new = s - > cf . idle_tx_int ;
s - > req_min_rx_int = s - > req_min_rx_new = s - > cf . min_rx_int ;
2013-09-16 21:57:40 +00:00
s - > rem_min_rx_int = 1 ;
2020-11-08 14:33:22 +00:00
s - > detect_mult = s - > cf . multiplier ;
s - > passive = s - > cf . passive ;
2016-10-30 22:51:23 +00:00
s - > tx_csn = random_u32 ( ) ;
2013-09-16 21:57:40 +00:00
2017-11-28 16:43:20 +00:00
s - > tx_timer = tm_new_init ( p - > tpool , bfd_tx_timer_hook , s , 0 , 0 ) ;
s - > hold_timer = tm_new_init ( p - > tpool , bfd_hold_timer_hook , s , 0 , 0 ) ;
2013-09-16 21:57:40 +00:00
bfd_session_update_tx_interval ( s ) ;
2013-11-19 21:33:48 +00:00
bfd_session_control_tx_timer ( s , 1 ) ;
init_list ( & s - > request_list ) ;
2017-06-06 14:47:30 +00:00
s - > last_state_change = current_time ( ) ;
2013-11-19 21:33:48 +00:00
TRACE ( D_EVENTS , " Session to %I added " , s - > addr ) ;
2013-09-16 21:57:40 +00:00
birdloop_leave ( p - > loop ) ;
return s ;
}
2013-11-19 21:33:48 +00:00
/*
2013-09-16 21:57:40 +00:00
static void
bfd_open_session ( struct bfd_proto * p , struct bfd_session * s , ip_addr local , struct iface * ifa )
{
birdloop_enter ( p - > loop ) ;
s - > opened = 1 ;
bfd_session_control_tx_timer ( s ) ;
birdloop_leave ( p - > loop ) ;
}
static void
bfd_close_session ( struct bfd_proto * p , struct bfd_session * s )
{
birdloop_enter ( p - > loop ) ;
s - > opened = 0 ;
bfd_session_update_state ( s , BFD_STATE_DOWN , BFD_DIAG_PATH_DOWN ) ;
bfd_session_control_tx_timer ( s ) ;
birdloop_leave ( p - > loop ) ;
}
2013-11-19 21:33:48 +00:00
*/
2013-09-16 21:57:40 +00:00
static void
bfd_remove_session ( struct bfd_proto * p , struct bfd_session * s )
{
2013-11-19 21:33:48 +00:00
ip_addr ip = s - > addr ;
2014-04-02 17:58:23 +00:00
/* Caller should ensure that request list is empty */
2013-09-16 21:57:40 +00:00
birdloop_enter ( p - > loop ) ;
2014-04-02 17:58:23 +00:00
/* Remove session from notify list if scheduled for notification */
/* No need for bfd_lock_sessions(), we are already protected by birdloop_enter() */
if ( NODE_VALID ( & s - > n ) )
rem_node ( & s - > n ) ;
2013-11-19 21:33:48 +00:00
bfd_free_iface ( s - > ifa ) ;
2013-09-16 21:57:40 +00:00
rfree ( s - > tx_timer ) ;
rfree ( s - > hold_timer ) ;
HASH_REMOVE ( p - > session_hash_id , HASH_ID , s ) ;
HASH_REMOVE ( p - > session_hash_ip , HASH_IP , s ) ;
2022-04-04 18:31:14 +00:00
sl_free ( s ) ;
2013-09-16 21:57:40 +00:00
2013-11-19 21:33:48 +00:00
TRACE ( D_EVENTS , " Session to %I removed " , ip ) ;
2013-09-16 21:57:40 +00:00
birdloop_leave ( p - > loop ) ;
}
static void
2013-11-19 21:33:48 +00:00
bfd_reconfigure_session ( struct bfd_proto * p , struct bfd_session * s )
2013-09-16 21:57:40 +00:00
{
2020-11-08 14:33:22 +00:00
if ( EMPTY_LIST ( s - > request_list ) )
return ;
2013-09-16 21:57:40 +00:00
birdloop_enter ( p - > loop ) ;
2020-11-08 14:33:22 +00:00
struct bfd_request * req = SKIP_BACK ( struct bfd_request , n , HEAD ( s - > request_list ) ) ;
s - > cf = bfd_merge_options ( s - > ifa - > cf , & req - > opts ) ;
2013-09-16 21:57:40 +00:00
2020-11-08 14:33:22 +00:00
u32 tx = ( s - > loc_state = = BFD_STATE_UP ) ? s - > cf . min_tx_int : s - > cf . idle_tx_int ;
2013-11-19 21:33:48 +00:00
bfd_session_set_min_tx ( s , tx ) ;
2020-11-08 14:33:22 +00:00
bfd_session_set_min_rx ( s , s - > cf . min_rx_int ) ;
s - > detect_mult = s - > cf . multiplier ;
s - > passive = s - > cf . passive ;
2013-09-16 21:57:40 +00:00
2013-11-19 21:33:48 +00:00
bfd_session_control_tx_timer ( s , 0 ) ;
2013-09-16 21:57:40 +00:00
birdloop_leave ( p - > loop ) ;
2013-11-19 21:33:48 +00:00
TRACE ( D_EVENTS , " Session to %I reconfigured " , s - > addr ) ;
}
/*
* BFD interfaces
*/
static struct bfd_iface_config bfd_default_iface = {
. min_rx_int = BFD_DEFAULT_MIN_RX_INT ,
. min_tx_int = BFD_DEFAULT_MIN_TX_INT ,
. idle_tx_int = BFD_DEFAULT_IDLE_TX_INT ,
2024-01-22 05:29:14 +00:00
. multiplier = BFD_DEFAULT_MULTIPLIER ,
2013-11-19 21:33:48 +00:00
} ;
static inline struct bfd_iface_config *
bfd_find_iface_config ( struct bfd_config * cf , struct iface * iface )
{
struct bfd_iface_config * ic ;
ic = iface ? ( void * ) iface_patt_find ( & cf - > patt_list , iface , NULL ) : cf - > multihop ;
return ic ? ic : & bfd_default_iface ;
}
static struct bfd_iface *
bfd_get_iface ( struct bfd_proto * p , ip_addr local , struct iface * iface )
{
struct bfd_iface * ifa ;
WALK_LIST ( ifa , p - > iface_list )
if ( ipa_equal ( ifa - > local , local ) & & ( ifa - > iface = = iface ) )
return ifa - > uc + + , ifa ;
struct bfd_config * cf = ( struct bfd_config * ) ( p - > p . cf ) ;
struct bfd_iface_config * ic = bfd_find_iface_config ( cf , iface ) ;
ifa = mb_allocz ( p - > tpool , sizeof ( struct bfd_iface ) ) ;
ifa - > local = local ;
ifa - > iface = iface ;
ifa - > cf = ic ;
ifa - > bfd = p ;
ifa - > sk = bfd_open_tx_sk ( p , local , iface ) ;
ifa - > uc = 1 ;
2022-04-07 17:33:40 +00:00
if ( cf - > strict_bind )
ifa - > rx = bfd_open_rx_sk_bound ( p , local , iface ) ;
2013-11-19 21:33:48 +00:00
add_tail ( & p - > iface_list , & ifa - > n ) ;
return ifa ;
}
static void
bfd_free_iface ( struct bfd_iface * ifa )
{
if ( ! ifa | | - - ifa - > uc )
return ;
2015-07-19 09:39:24 +00:00
if ( ifa - > sk )
{
sk_stop ( ifa - > sk ) ;
rfree ( ifa - > sk ) ;
}
2022-04-07 17:33:40 +00:00
if ( ifa - > rx )
{
sk_stop ( ifa - > rx ) ;
rfree ( ifa - > rx ) ;
}
2013-11-19 21:33:48 +00:00
rem_node ( & ifa - > n ) ;
mb_free ( ifa ) ;
}
static void
bfd_reconfigure_iface ( struct bfd_proto * p , struct bfd_iface * ifa , struct bfd_config * nc )
{
2020-11-12 01:37:42 +00:00
struct bfd_iface_config * new = bfd_find_iface_config ( nc , ifa - > iface ) ;
struct bfd_iface_config * old = ifa - > cf ;
/* Check options that are handled in bfd_reconfigure_session() */
ifa - > changed =
( new - > min_rx_int ! = old - > min_rx_int ) | |
( new - > min_tx_int ! = old - > min_tx_int ) | |
( new - > idle_tx_int ! = old - > idle_tx_int ) | |
( new - > multiplier ! = old - > multiplier ) | |
( new - > passive ! = old - > passive ) ;
2013-11-19 21:33:48 +00:00
/* This should be probably changed to not access ifa->cf from the BFD thread */
birdloop_enter ( p - > loop ) ;
2020-11-12 01:37:42 +00:00
ifa - > cf = new ;
2013-11-19 21:33:48 +00:00
birdloop_leave ( p - > loop ) ;
}
/*
* BFD requests
*/
static void
2023-08-29 16:23:29 +00:00
bfd_request_notify ( struct bfd_request * req , u8 state , u8 remote , u8 diag )
2013-11-19 21:33:48 +00:00
{
u8 old_state = req - > state ;
if ( state = = old_state )
return ;
req - > state = state ;
req - > diag = diag ;
req - > old_state = old_state ;
2023-08-29 16:23:29 +00:00
req - > down = ( old_state = = BFD_STATE_UP ) & & ( state = = BFD_STATE_DOWN ) & & ( remote ! = BFD_STATE_ADMIN_DOWN ) ;
2013-11-19 21:33:48 +00:00
if ( req - > hook )
req - > hook ( req ) ;
}
static int
bfd_add_request ( struct bfd_proto * p , struct bfd_request * req )
{
2020-01-28 17:07:25 +00:00
struct bfd_config * cf = ( struct bfd_config * ) ( p - > p . cf ) ;
2019-07-24 13:08:03 +00:00
if ( p - > p . vrf_set & & ( p - > p . vrf ! = req - > vrf ) )
2019-07-17 14:20:35 +00:00
return 0 ;
2020-01-28 17:07:25 +00:00
if ( ipa_is_ip4 ( req - > addr ) ? ! cf - > accept_ipv4 : ! cf - > accept_ipv6 )
return 0 ;
if ( req - > iface ? ! cf - > accept_direct : ! cf - > accept_multihop )
return 0 ;
2021-01-10 14:29:02 +00:00
uint ifindex = req - > iface ? req - > iface - > index : 0 ;
struct bfd_session * s = bfd_find_session_by_addr ( p , req - > addr , ifindex ) ;
2023-08-29 16:23:29 +00:00
u8 loc_state , rem_state , diag ;
2013-11-19 21:33:48 +00:00
if ( ! s )
2020-11-08 14:33:22 +00:00
s = bfd_add_session ( p , req - > addr , req - > local , req - > iface , & req - > opts ) ;
2013-11-19 21:33:48 +00:00
rem_node ( & req - > n ) ;
add_tail ( & s - > request_list , & req - > n ) ;
req - > session = s ;
bfd_lock_sessions ( p ) ;
2023-08-29 16:23:29 +00:00
loc_state = s - > loc_state ;
rem_state = s - > rem_state ;
2013-11-19 21:33:48 +00:00
diag = s - > loc_diag ;
bfd_unlock_sessions ( p ) ;
2023-08-29 16:23:29 +00:00
bfd_request_notify ( req , loc_state , rem_state , diag ) ;
2013-11-19 21:33:48 +00:00
return 1 ;
}
static void
bfd_submit_request ( struct bfd_request * req )
{
node * n ;
WALK_LIST ( n , bfd_proto_list )
if ( bfd_add_request ( SKIP_BACK ( struct bfd_proto , bfd_node , n ) , req ) )
return ;
rem_node ( & req - > n ) ;
add_tail ( & bfd_wait_list , & req - > n ) ;
req - > session = NULL ;
2023-08-29 16:23:29 +00:00
bfd_request_notify ( req , BFD_STATE_ADMIN_DOWN , BFD_STATE_ADMIN_DOWN , 0 ) ;
2013-11-19 21:33:48 +00:00
}
static void
bfd_take_requests ( struct bfd_proto * p )
{
node * n , * nn ;
WALK_LIST_DELSAFE ( n , nn , bfd_wait_list )
bfd_add_request ( p , SKIP_BACK ( struct bfd_request , n , n ) ) ;
}
static void
bfd_drop_requests ( struct bfd_proto * p )
{
node * n ;
HASH_WALK ( p - > session_hash_id , next_id , s )
{
/* We assume that p is not in bfd_proto_list */
WALK_LIST_FIRST ( n , s - > request_list )
bfd_submit_request ( SKIP_BACK ( struct bfd_request , n , n ) ) ;
}
HASH_WALK_END ;
}
static struct resclass bfd_request_class ;
struct bfd_request *
2019-07-17 14:20:35 +00:00
bfd_request_session ( pool * p , ip_addr addr , ip_addr local ,
struct iface * iface , struct iface * vrf ,
2020-11-08 14:33:22 +00:00
void ( * hook ) ( struct bfd_request * ) , void * data ,
const struct bfd_options * opts )
2013-11-19 21:33:48 +00:00
{
struct bfd_request * req = ralloc ( p , & bfd_request_class ) ;
/* Hack: self-link req->n, we will call rem_node() on it */
req - > n . prev = req - > n . next = & req - > n ;
req - > addr = addr ;
req - > local = local ;
req - > iface = iface ;
2019-07-17 14:20:35 +00:00
req - > vrf = vrf ;
2013-11-19 21:33:48 +00:00
2020-11-08 14:33:22 +00:00
if ( opts )
req - > opts = * opts ;
2013-11-19 21:33:48 +00:00
bfd_submit_request ( req ) ;
req - > hook = hook ;
req - > data = data ;
return req ;
}
2020-11-08 14:33:22 +00:00
void
bfd_update_request ( struct bfd_request * req , const struct bfd_options * opts )
{
struct bfd_session * s = req - > session ;
if ( ! memcmp ( opts , & req - > opts , sizeof ( const struct bfd_options ) ) )
return ;
req - > opts = * opts ;
if ( s )
bfd_reconfigure_session ( s - > ifa - > bfd , s ) ;
}
2013-11-19 21:33:48 +00:00
static void
bfd_request_free ( resource * r )
{
struct bfd_request * req = ( struct bfd_request * ) r ;
struct bfd_session * s = req - > session ;
rem_node ( & req - > n ) ;
/* Remove the session if there is no request for it. Skip that if
inside notify hooks , will be handled by bfd_notify_hook ( ) itself */
if ( s & & EMPTY_LIST ( s - > request_list ) & & ! s - > notify_running )
bfd_remove_session ( s - > ifa - > bfd , s ) ;
}
static void
bfd_request_dump ( resource * r )
{
struct bfd_request * req = ( struct bfd_request * ) r ;
debug ( " (code %p, data %p) \n " , req - > hook , req - > data ) ;
}
static struct resclass bfd_request_class = {
" BFD request " ,
sizeof ( struct bfd_request ) ,
bfd_request_free ,
bfd_request_dump ,
NULL ,
2024-01-22 05:29:14 +00:00
NULL ,
2013-11-19 21:33:48 +00:00
} ;
/*
* BFD neighbors
*/
static void
bfd_neigh_notify ( struct neighbor * nb )
{
struct bfd_proto * p = ( struct bfd_proto * ) nb - > proto ;
struct bfd_neighbor * n = nb - > data ;
if ( ! n )
return ;
if ( ( nb - > scope > 0 ) & & ! n - > req )
{
2013-12-02 10:54:32 +00:00
ip_addr local = ipa_nonzero ( n - > local ) ? n - > local : nb - > ifa - > ip ;
2020-11-08 14:33:22 +00:00
n - > req = bfd_request_session ( p - > p . pool , n - > addr , local , nb - > iface , p - > p . vrf , NULL , NULL , NULL ) ;
2013-11-19 21:33:48 +00:00
}
if ( ( nb - > scope < = 0 ) & & n - > req )
{
rfree ( n - > req ) ;
n - > req = NULL ;
}
2013-09-16 21:57:40 +00:00
}
2013-09-10 10:09:36 +00:00
static void
bfd_start_neighbor ( struct bfd_proto * p , struct bfd_neighbor * n )
{
2013-11-19 21:33:48 +00:00
n - > active = 1 ;
2013-09-10 10:09:36 +00:00
2013-11-19 21:33:48 +00:00
if ( n - > multihop )
2013-09-10 10:09:36 +00:00
{
2020-11-08 14:33:22 +00:00
n - > req = bfd_request_session ( p - > p . pool , n - > addr , n - > local , NULL , p - > p . vrf , NULL , NULL , NULL ) ;
2013-09-10 10:09:36 +00:00
return ;
}
2018-06-27 14:51:53 +00:00
struct neighbor * nb = neigh_find ( & p - > p , n - > addr , n - > iface , NEF_STICKY ) ;
2013-09-10 10:09:36 +00:00
if ( ! nb )
{
log ( L_ERR " %s: Invalid remote address %I%J " , p - > p . name , n - > addr , n - > iface ) ;
return ;
}
if ( nb - > data )
{
2013-11-19 21:33:48 +00:00
log ( L_ERR " %s: Duplicate neighbor %I " , p - > p . name , n - > addr ) ;
2013-09-10 10:09:36 +00:00
return ;
}
2013-11-19 21:33:48 +00:00
n - > neigh = nb ;
nb - > data = n ;
2013-09-10 10:09:36 +00:00
if ( nb - > scope > 0 )
2013-11-19 21:33:48 +00:00
bfd_neigh_notify ( nb ) ;
2013-09-10 10:09:36 +00:00
else
2013-09-16 21:57:40 +00:00
TRACE ( D_EVENTS , " Waiting for %I%J to become my neighbor " , n - > addr , n - > iface ) ;
2013-09-10 10:09:36 +00:00
}
static void
2016-10-14 13:37:04 +00:00
bfd_stop_neighbor ( struct bfd_proto * p UNUSED , struct bfd_neighbor * n )
2013-09-10 10:09:36 +00:00
{
2013-11-19 21:33:48 +00:00
if ( n - > neigh )
n - > neigh - > data = NULL ;
n - > neigh = NULL ;
2013-09-10 10:09:36 +00:00
2013-11-19 21:33:48 +00:00
rfree ( n - > req ) ;
n - > req = NULL ;
2013-09-10 10:09:36 +00:00
}
2013-11-19 21:33:48 +00:00
static inline int
bfd_same_neighbor ( struct bfd_neighbor * x , struct bfd_neighbor * y )
2013-09-10 10:09:36 +00:00
{
2013-11-19 21:33:48 +00:00
return ipa_equal ( x - > addr , y - > addr ) & & ipa_equal ( x - > local , y - > local ) & &
( x - > iface = = y - > iface ) & & ( x - > multihop = = y - > multihop ) ;
}
2013-09-10 10:09:36 +00:00
2013-11-19 21:33:48 +00:00
static void
bfd_reconfigure_neighbors ( struct bfd_proto * p , struct bfd_config * new )
{
struct bfd_config * old = ( struct bfd_config * ) ( p - > p . cf ) ;
struct bfd_neighbor * on , * nn ;
2013-09-10 10:09:36 +00:00
2013-11-19 21:33:48 +00:00
WALK_LIST ( on , old - > neigh_list )
{
WALK_LIST ( nn , new - > neigh_list )
if ( bfd_same_neighbor ( nn , on ) )
{
nn - > neigh = on - > neigh ;
if ( nn - > neigh )
nn - > neigh - > data = nn ;
nn - > req = on - > req ;
nn - > active = 1 ;
2019-09-30 17:10:14 +00:00
goto next ;
2013-11-19 21:33:48 +00:00
}
bfd_stop_neighbor ( p , on ) ;
2019-09-30 17:10:14 +00:00
next : ;
2013-11-19 21:33:48 +00:00
}
2013-09-10 10:09:36 +00:00
2013-11-19 21:33:48 +00:00
WALK_LIST ( nn , new - > neigh_list )
if ( ! nn - > active )
bfd_start_neighbor ( p , nn ) ;
2013-09-10 10:09:36 +00:00
}
2013-11-19 21:33:48 +00:00
/*
* BFD notify socket
*/
2013-09-16 21:57:40 +00:00
/* This core notify code should be replaced after main loop transition to birdloop */
int pipe ( int pipefd [ 2 ] ) ;
void pipe_drain ( int fd ) ;
void pipe_kick ( int fd ) ;
static int
2016-10-14 13:37:04 +00:00
bfd_notify_hook ( sock * sk , uint len UNUSED )
2013-09-16 21:57:40 +00:00
{
struct bfd_proto * p = sk - > data ;
struct bfd_session * s ;
list tmp_list ;
2023-08-29 16:23:29 +00:00
u8 loc_state , rem_state , diag ;
2013-11-19 21:33:48 +00:00
node * n , * nn ;
2013-09-16 21:57:40 +00:00
pipe_drain ( sk - > fd ) ;
bfd_lock_sessions ( p ) ;
init_list ( & tmp_list ) ;
add_tail_list ( & tmp_list , & p - > notify_list ) ;
init_list ( & p - > notify_list ) ;
bfd_unlock_sessions ( p ) ;
WALK_LIST_FIRST ( s , tmp_list )
{
bfd_lock_sessions ( p ) ;
2016-03-22 12:35:40 +00:00
rem_node ( & s - > n ) ;
2023-08-29 16:23:29 +00:00
loc_state = s - > loc_state ;
rem_state = s - > rem_state ;
2013-11-19 21:33:48 +00:00
diag = s - > loc_diag ;
2013-09-16 21:57:40 +00:00
bfd_unlock_sessions ( p ) ;
2013-11-19 21:33:48 +00:00
s - > notify_running = 1 ;
WALK_LIST_DELSAFE ( n , nn , s - > request_list )
2023-08-29 16:23:29 +00:00
bfd_request_notify ( SKIP_BACK ( struct bfd_request , n , n ) , loc_state , rem_state , diag ) ;
2013-11-19 21:33:48 +00:00
s - > notify_running = 0 ;
/* Remove the session if all requests were removed in notify hooks */
if ( EMPTY_LIST ( s - > request_list ) )
bfd_remove_session ( p , s ) ;
2013-09-16 21:57:40 +00:00
}
return 0 ;
}
static inline void
bfd_notify_kick ( struct bfd_proto * p )
{
pipe_kick ( p - > notify_ws - > fd ) ;
}
static void
bfd_noterr_hook ( sock * sk , int err )
{
struct bfd_proto * p = sk - > data ;
log ( L_ERR " %s: Notify socket error: %m " , p - > p . name , err ) ;
}
static void
bfd_notify_init ( struct bfd_proto * p )
{
int pfds [ 2 ] ;
sock * sk ;
int rv = pipe ( pfds ) ;
if ( rv < 0 )
die ( " pipe: %m " ) ;
sk = sk_new ( p - > p . pool ) ;
sk - > type = SK_MAGIC ;
sk - > rx_hook = bfd_notify_hook ;
sk - > err_hook = bfd_noterr_hook ;
sk - > fd = pfds [ 0 ] ;
sk - > data = p ;
if ( sk_open ( sk ) < 0 )
die ( " bfd: sk_open failed " ) ;
p - > notify_rs = sk ;
/* The write sock is not added to any event loop */
sk = sk_new ( p - > p . pool ) ;
sk - > type = SK_MAGIC ;
sk - > fd = pfds [ 1 ] ;
sk - > data = p ;
sk - > flags = SKF_THREAD ;
if ( sk_open ( sk ) < 0 )
die ( " bfd: sk_open failed " ) ;
p - > notify_ws = sk ;
}
2013-09-10 10:09:36 +00:00
2013-11-19 21:33:48 +00:00
/*
* BFD protocol glue
*/
2013-09-10 10:09:36 +00:00
static struct proto *
bfd_init ( struct proto_config * c )
{
2016-01-26 10:48:58 +00:00
struct proto * p = proto_new ( c ) ;
2013-09-10 10:09:36 +00:00
2013-09-16 21:57:40 +00:00
p - > neigh_notify = bfd_neigh_notify ;
2013-09-10 10:09:36 +00:00
return p ;
}
static int
bfd_start ( struct proto * P )
{
struct bfd_proto * p = ( struct bfd_proto * ) P ;
struct bfd_config * cf = ( struct bfd_config * ) ( P - > cf ) ;
2013-11-19 21:33:48 +00:00
p - > loop = birdloop_new ( ) ;
2013-09-16 21:57:40 +00:00
p - > tpool = rp_new ( NULL , " BFD thread root " ) ;
pthread_spin_init ( & p - > lock , PTHREAD_PROCESS_PRIVATE ) ;
2013-09-10 10:09:36 +00:00
p - > session_slab = sl_new ( P - > pool , sizeof ( struct bfd_session ) ) ;
2013-11-19 21:33:48 +00:00
HASH_INIT ( p - > session_hash_id , P - > pool , 8 ) ;
HASH_INIT ( p - > session_hash_ip , P - > pool , 8 ) ;
2013-09-16 21:57:40 +00:00
2013-11-19 21:33:48 +00:00
init_list ( & p - > iface_list ) ;
2013-09-16 21:57:40 +00:00
init_list ( & p - > notify_list ) ;
bfd_notify_init ( p ) ;
2013-11-19 21:33:48 +00:00
add_tail ( & bfd_proto_list , & p - > bfd_node ) ;
2013-09-16 21:57:40 +00:00
birdloop_enter ( p - > loop ) ;
2020-01-28 17:07:25 +00:00
2022-04-07 17:33:40 +00:00
if ( ! cf - > strict_bind )
{
if ( cf - > accept_ipv4 & & cf - > accept_direct )
p - > rx4_1 = bfd_open_rx_sk ( p , 0 , SK_IPV4 ) ;
2020-01-28 17:07:25 +00:00
2022-04-07 17:33:40 +00:00
if ( cf - > accept_ipv4 & & cf - > accept_multihop )
p - > rx4_m = bfd_open_rx_sk ( p , 1 , SK_IPV4 ) ;
2020-01-28 17:07:25 +00:00
2022-04-07 17:33:40 +00:00
if ( cf - > accept_ipv6 & & cf - > accept_direct )
p - > rx6_1 = bfd_open_rx_sk ( p , 0 , SK_IPV6 ) ;
2020-01-28 17:07:25 +00:00
2022-04-07 17:33:40 +00:00
if ( cf - > accept_ipv6 & & cf - > accept_multihop )
p - > rx6_m = bfd_open_rx_sk ( p , 1 , SK_IPV6 ) ;
}
2020-01-28 17:07:25 +00:00
2013-09-16 21:57:40 +00:00
birdloop_leave ( p - > loop ) ;
2013-09-10 10:09:36 +00:00
2013-11-19 21:33:48 +00:00
bfd_take_requests ( p ) ;
2013-09-10 10:09:36 +00:00
struct bfd_neighbor * n ;
2013-09-16 21:57:40 +00:00
WALK_LIST ( n , cf - > neigh_list )
2013-09-10 10:09:36 +00:00
bfd_start_neighbor ( p , n ) ;
2013-10-05 18:12:28 +00:00
birdloop_start ( p - > loop ) ;
2013-09-16 21:57:40 +00:00
2013-09-10 10:09:36 +00:00
return PS_UP ;
}
static int
bfd_shutdown ( struct proto * P )
{
struct bfd_proto * p = ( struct bfd_proto * ) P ;
2013-11-19 21:33:48 +00:00
struct bfd_config * cf = ( struct bfd_config * ) ( P - > cf ) ;
rem_node ( & p - > bfd_node ) ;
2013-09-10 10:09:36 +00:00
2013-10-05 18:12:28 +00:00
birdloop_stop ( p - > loop ) ;
2013-11-19 21:33:48 +00:00
struct bfd_neighbor * n ;
WALK_LIST ( n , cf - > neigh_list )
bfd_stop_neighbor ( p , n ) ;
bfd_drop_requests ( p ) ;
2013-10-05 18:12:28 +00:00
/* FIXME: This is hack */
birdloop_enter ( p - > loop ) ;
rfree ( p - > tpool ) ;
birdloop_leave ( p - > loop ) ;
2013-11-19 21:33:48 +00:00
birdloop_free ( p - > loop ) ;
2013-09-10 10:09:36 +00:00
2013-11-19 21:33:48 +00:00
return PS_DOWN ;
2013-09-10 10:09:36 +00:00
}
static int
bfd_reconfigure ( struct proto * P , struct proto_config * c )
{
struct bfd_proto * p = ( struct bfd_proto * ) P ;
2020-01-28 17:07:25 +00:00
struct bfd_config * old = ( struct bfd_config * ) ( P - > cf ) ;
2013-09-10 10:09:36 +00:00
struct bfd_config * new = ( struct bfd_config * ) c ;
2013-11-19 21:33:48 +00:00
struct bfd_iface * ifa ;
2013-09-10 10:09:36 +00:00
2020-01-28 17:07:25 +00:00
/* TODO: Improve accept reconfiguration */
if ( ( new - > accept_ipv4 ! = old - > accept_ipv4 ) | |
( new - > accept_ipv6 ! = old - > accept_ipv6 ) | |
( new - > accept_direct ! = old - > accept_direct ) | |
2022-04-07 17:33:40 +00:00
( new - > accept_multihop ! = old - > accept_multihop ) | |
( new - > strict_bind ! = old - > strict_bind ) )
2020-01-28 17:07:25 +00:00
return 0 ;
2013-09-16 21:57:40 +00:00
birdloop_mask_wakeups ( p - > loop ) ;
2013-11-19 21:33:48 +00:00
WALK_LIST ( ifa , p - > iface_list )
bfd_reconfigure_iface ( p , ifa , new ) ;
HASH_WALK ( p - > session_hash_id , next_id , s )
{
if ( s - > ifa - > changed )
bfd_reconfigure_session ( p , s ) ;
}
HASH_WALK_END ;
2013-09-10 10:09:36 +00:00
2013-11-19 21:33:48 +00:00
bfd_reconfigure_neighbors ( p , new ) ;
2013-09-10 10:09:36 +00:00
2013-09-16 21:57:40 +00:00
birdloop_unmask_wakeups ( p - > loop ) ;
2013-09-10 10:09:36 +00:00
return 1 ;
}
static void
2016-10-14 13:37:04 +00:00
bfd_copy_config ( struct proto_config * dest , struct proto_config * src UNUSED )
2013-09-10 10:09:36 +00:00
{
struct bfd_config * d = ( struct bfd_config * ) dest ;
2013-09-16 21:57:40 +00:00
// struct bfd_config *s = (struct bfd_config *) src;
2013-09-10 10:09:36 +00:00
2013-11-19 21:33:48 +00:00
/* We clean up patt_list and neigh_list, neighbors and ifaces are non-sharable */
2014-10-02 08:59:34 +00:00
init_list ( & d - > patt_list ) ;
2013-09-16 21:57:40 +00:00
init_list ( & d - > neigh_list ) ;
2013-09-10 10:09:36 +00:00
}
2024-01-16 10:02:22 +00:00
void bfd_show_details ( struct bfd_session * s )
{
cli_msg ( - 1020 , " IP address: %I " , s - > addr ) ;
cli_msg ( - 1020 , " Interface: %s " , ( s - > ifa & & s - > ifa - > iface ) ? s - > ifa - > iface - > name : " --- " ) ;
cli_msg ( - 1020 , " Role: %s " , ( s - > passive ) ? " Passive " : " Active " ) ;
cli_msg ( - 1020 , " Local session: " ) ;
cli_msg ( - 1020 , " State: %s " , bfd_state_names [ s - > loc_state ] ) ;
cli_msg ( - 1020 , " Session ID: %u " , s - > loc_id ) ;
if ( s - > loc_diag | | s - > rem_diag )
cli_msg ( - 1020 , " Issue: %s " , bfd_diag_names [ s - > loc_diag ] ) ;
cli_msg ( - 1020 , " Remote session: " ) ;
cli_msg ( - 1020 , " State: %s " , bfd_state_names [ s - > rem_state ] ) ;
cli_msg ( - 1020 , " Session ID: %u " , s - > loc_id , s - > rem_id ) ;
if ( s - > loc_diag | | s - > rem_diag )
cli_msg ( - 1020 , " Issue: %s " , bfd_diag_names [ s - > rem_diag ] ) ;
cli_msg ( - 1020 , " Session mode: %s " , ( s - > rem_demand_mode ) ? " Demand " : " Asynchronous " ) ;
if ( ! s - > rem_demand_mode )
{
cli_msg ( - 1020 , " Local intervals: " ) ;
cli_msg ( - 1020 , " Desired min tx: %t " , s - > des_min_tx_int ) ;
cli_msg ( - 1020 , " Required min rx: %t " , s - > req_min_rx_int ) ;
cli_msg ( - 1020 , " Remote intervals: " ) ;
cli_msg ( - 1020 , " Desired min tx: %t " , s - > rem_min_tx_int ) ;
cli_msg ( - 1020 , " Required min rx: %t " , s - > rem_min_rx_int ) ;
cli_msg ( - 1020 , " Timers: " ) ;
cli_msg ( - 1020 , " Hold timer remains %t/%t " , tm_remains ( s - > hold_timer ) , MAX ( s - > req_min_rx_int , s - > rem_min_tx_int ) * s - > rem_detect_mult ) ; // The total time is just copied from timers setings. I hope it is not (and will not) be problem.
cli_msg ( - 1020 , " TX timer remains %t " , tm_remains ( s - > tx_timer ) ) ;
}
else if ( tm_remains ( s - > hold_timer ) > 0 )
{
cli_msg ( - 1020 , " Hold timer remains %t " , tm_remains ( s - > hold_timer ) ) ;
}
cli_msg ( - 1020 , " Latest actions: " ) ;
cli_msg ( - 1020 , " Last received valid control packet before %t " , current_time ( ) - s - > last_rx ) ;
cli_msg ( - 1020 , " Last sent periodic control packet before %t " , current_time ( ) - s - > last_tx ) ;
btime tim = ( btime ) ( ( ( u64 ) s - > tx_csn_time ) < < 20 ) ;
if ( tim > 0 )
cli_msg ( - 1020 , " Last csn change before %t " , current_time ( ) - tim ) ;
if ( s - > poll_active | | s - > poll_scheduled )
cli_msg ( - 1020 , " Poll %s%s " , ( s - > poll_active ) ? " , poll active " : " " , ( s - > poll_scheduled ) ? " , poll scheduled " : " " ) ;
else
cli_msg ( - 1020 , " Poll inactive " ) ;
cli_msg ( - 1020 , " " ) ;
}
2013-09-16 21:57:40 +00:00
void
2024-01-16 10:02:22 +00:00
bfd_show_sessions ( struct proto * P , int details )
2013-09-16 21:57:40 +00:00
{
2013-11-19 21:33:48 +00:00
byte tbuf [ TM_DATETIME_BUFFER_SIZE ] ;
2013-09-16 21:57:40 +00:00
struct bfd_proto * p = ( struct bfd_proto * ) P ;
2014-10-02 08:59:34 +00:00
uint state , diag UNUSED ;
2017-06-13 14:52:21 +00:00
btime tx_int , timeout ;
2013-09-16 21:57:40 +00:00
const char * ifname ;
if ( p - > p . proto_state ! = PS_UP )
{
2013-11-25 10:06:00 +00:00
cli_msg ( - 1020 , " %s: is not up " , p - > p . name ) ;
2013-09-16 21:57:40 +00:00
return ;
}
2013-11-25 10:06:00 +00:00
cli_msg ( - 1020 , " %s: " , p - > p . name ) ;
2024-01-16 10:02:22 +00:00
if ( ! details )
cli_msg ( - 1020 , " %-25s %-10s %-10s %-12s %8s %8s " ,
2013-11-19 21:33:48 +00:00
" IP address " , " Interface " , " State " , " Since " , " Interval " , " Timeout " ) ;
2013-09-16 21:57:40 +00:00
HASH_WALK ( p - > session_hash_id , next_id , s )
{
2013-11-19 21:33:48 +00:00
/* FIXME: this is thread-unsafe, but perhaps harmless */
2024-01-16 10:02:22 +00:00
if ( ! details )
{
state = s - > loc_state ;
diag = s - > loc_diag ;
ifname = ( s - > ifa & & s - > ifa - > iface ) ? s - > ifa - > iface - > name : " --- " ;
tx_int = s - > last_tx ? MAX ( s - > des_min_tx_int , s - > rem_min_rx_int ) : 0 ;
timeout = ( btime ) MAX ( s - > req_min_rx_int , s - > rem_min_tx_int ) * s - > rem_detect_mult ;
state = ( state < 4 ) ? state : 0 ;
tm_format_time ( tbuf , & config - > tf_proto , s - > last_state_change ) ;
cli_msg ( - 1020 , " %-25I %-10s %-10s %-12s %7t %7t " ,
2017-06-13 14:52:21 +00:00
s - > addr , ifname , bfd_state_names [ state ] , tbuf , tx_int , timeout ) ;
2024-01-16 10:02:22 +00:00
}
else
{
bfd_show_details ( s ) ;
}
2013-09-16 21:57:40 +00:00
}
HASH_WALK_END ;
}
2013-09-10 10:09:36 +00:00
struct protocol proto_bfd = {
. name = " BFD " ,
. template = " bfd%d " ,
2018-05-07 12:47:00 +00:00
. class = PROTOCOL_BFD ,
2016-01-26 10:48:58 +00:00
. proto_size = sizeof ( struct bfd_proto ) ,
2015-02-21 20:08:23 +00:00
. config_size = sizeof ( struct bfd_config ) ,
2013-09-10 10:09:36 +00:00
. init = bfd_init ,
. start = bfd_start ,
. shutdown = bfd_shutdown ,
. reconfigure = bfd_reconfigure ,
. copy_config = bfd_copy_config ,
} ;
2022-03-18 21:05:50 +00:00
void
bfd_build ( void )
{
proto_build ( & proto_bfd ) ;
}