diff options
Diffstat (limited to 'bgpd/bgp_fsm.c')
-rw-r--r-- | bgpd/bgp_fsm.c | 2870 |
1 files changed, 1877 insertions, 993 deletions
diff --git a/bgpd/bgp_fsm.c b/bgpd/bgp_fsm.c index c815f9a1..f5755cfa 100644 --- a/bgpd/bgp_fsm.c +++ b/bgpd/bgp_fsm.c @@ -1,1123 +1,2007 @@ -/* BGP-4 Finite State Machine - From RFC1771 [A Border Gateway Protocol 4 (BGP-4)] - Copyright (C) 1996, 97, 98 Kunihiro Ishiguro - -This file is part of GNU Zebra. - -GNU Zebra 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, or (at your option) any -later version. - -GNU Zebra is distributed in the hope that it will be useful, but -WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GNU Zebra; see the file COPYING. If not, write to the Free -Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA -02111-1307, USA. */ +/* BGP-4 Finite State Machine + * From RFC1771 [A Border Gateway Protocol 4 (BGP-4)] + * Copyright (C) 1996, 97, 98 Kunihiro Ishiguro + * + * Recast for pthreaded bgpd: Copyright (C) Chris Hall (GMCH), Highwayman + * + * This file is part of GNU Zebra. + * + * GNU Zebra 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, or (at your + * option) any later version. + * + * GNU Zebra is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Zebra; see the file COPYING. If not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ #include <zebra.h> +#include "bgpd/bgp.h" -#include "linklist.h" -#include "prefix.h" -#include "vty.h" -#include "sockunion.h" -#include "thread.h" #include "log.h" -#include "stream.h" -#include "memory.h" -#include "plist.h" -#include "bgpd/bgpd.h" -#include "bgpd/bgp_attr.h" -#include "bgpd/bgp_debug.h" +#include "bgpd/bgp_engine.h" +#include "bgpd/bgp_session.h" +#include "bgpd/bgp_connection.h" +#include "bgpd/bgp_notification.h" #include "bgpd/bgp_fsm.h" + +#include "lib/qtimers.h" + +#include "bgpd/bgp_debug.h" #include "bgpd/bgp_packet.h" #include "bgpd/bgp_network.h" -#include "bgpd/bgp_route.h" #include "bgpd/bgp_dump.h" #include "bgpd/bgp_open.h" #ifdef HAVE_SNMP #include "bgpd/bgp_snmp.h" #endif /* HAVE_SNMP */ - -/* BGP FSM (finite state machine) has three types of functions. Type - one is thread functions. Type two is event functions. Type three - is FSM functions. Timer functions are set by bgp_timer_set - function. */ - -/* BGP event function. */ -int bgp_event (struct thread *); - -/* BGP thread functions. */ -static int bgp_start_timer (struct thread *); -static int bgp_connect_timer (struct thread *); -static int bgp_holdtime_timer (struct thread *); -static int bgp_keepalive_timer (struct thread *); - -/* BGP FSM functions. */ -static int bgp_start (struct peer *); - -/* BGP start timer jitter. */ -static int -bgp_start_jitter (int time) -{ - return ((rand () % (time + 1)) - (time / 2)); -} - -/* Check if suppress start/restart of sessions to peer. */ -#define BGP_PEER_START_SUPPRESSED(P) \ - (CHECK_FLAG ((P)->flags, PEER_FLAG_SHUTDOWN) \ - || CHECK_FLAG ((P)->sflags, PEER_STATUS_PREFIX_OVERFLOW)) -/* Hook function called after bgp event is occered. And vty's - neighbor command invoke this function after making neighbor - structure. */ -void -bgp_timer_set (struct peer *peer) -{ - int jitter = 0; - - switch (peer->status) - { - case Idle: - /* First entry point of peer's finite state machine. In Idle - status start timer is on unless peer is shutdown or peer is - inactive. All other timer must be turned off */ - if (BGP_PEER_START_SUPPRESSED (peer) || ! peer_active (peer)) - { - BGP_TIMER_OFF (peer->t_start); - } - else - { - jitter = bgp_start_jitter (peer->v_start); - BGP_TIMER_ON (peer->t_start, bgp_start_timer, - peer->v_start + jitter); - } - BGP_TIMER_OFF (peer->t_connect); - BGP_TIMER_OFF (peer->t_holdtime); - BGP_TIMER_OFF (peer->t_keepalive); - BGP_TIMER_OFF (peer->t_asorig); - BGP_TIMER_OFF (peer->t_routeadv); - break; - - case Connect: - /* After start timer is expired, the peer moves to Connnect - status. Make sure start timer is off and connect timer is - on. */ - BGP_TIMER_OFF (peer->t_start); - BGP_TIMER_ON (peer->t_connect, bgp_connect_timer, peer->v_connect); - BGP_TIMER_OFF (peer->t_holdtime); - BGP_TIMER_OFF (peer->t_keepalive); - BGP_TIMER_OFF (peer->t_asorig); - BGP_TIMER_OFF (peer->t_routeadv); - break; - - case Active: - /* Active is waiting connection from remote peer. And if - connect timer is expired, change status to Connect. */ - BGP_TIMER_OFF (peer->t_start); - /* If peer is passive mode, do not set connect timer. */ - if (CHECK_FLAG (peer->flags, PEER_FLAG_PASSIVE) - || CHECK_FLAG (peer->sflags, PEER_STATUS_NSF_WAIT)) - { - BGP_TIMER_OFF (peer->t_connect); - } - else - { - BGP_TIMER_ON (peer->t_connect, bgp_connect_timer, peer->v_connect); - } - BGP_TIMER_OFF (peer->t_holdtime); - BGP_TIMER_OFF (peer->t_keepalive); - BGP_TIMER_OFF (peer->t_asorig); - BGP_TIMER_OFF (peer->t_routeadv); - break; +/*============================================================================== + * The BGP Finite State Machine + * + * The state machine is represented as a table, indexed by [state, event], + * giving an action to be performed to deal with the event and the state that + * will advance to (or stay at). + * + * In some cases the action routine may override the the default new state. + * + * When a new state is entered, bgp_fsm_state_change() is called to complete + * the transition (in particular to set/unset timers). + * + * The fsm action functions are called with the session locked. + * + *------------------------------------------------------------------------------ + * FSM "events" + * + * These are raised when: + * + * * the BGP Engine receives instructions from the Routeing Engine + * + * * some I/O operations complete + * + * * timers go off + * + * and the mechanism is to call bgp_fsm_event(). + * + * Note that the event is dealt with *immediately* -- there is no queueing of + * events. The problem with queueing events is that the state of the connection + * is "out of date" until its event queue is emptied, so decisions made in the + * meantime may be wrong. + * + * The downside is that need to deal with the possibility of events being + * raised while the FSM is in some indeterminate state while processing an + * event for the current connection. This requires a little care. + * + * Timers, messages, and qpselect actions all occur outside the FSM. So there + * is no problem there. + * + * However, the FSM does some I/O operations -- notably write() and connect(). + * These may complete immediately, and may need to trigger a new event. To + * handle this, the connection can set a "post" event, to be processed at the + * tail end of the current event processing. + * + * Note that there is only one level of "post" event. The FSM only ever issues + * one I/O operation per event. (It's a RULE.) + * + *------------------------------------------------------------------------------ + * Primary and Secondary Connections + * + * To support BGP's "symmetrical" open strategy, this code allows for two + * connections to be made for a session -- one connect() and one accept(). + * If two connections are made, only one will reach Established state. + * + * Up to Established state, the primary connection will be the out-bound + * connect() connection (if allowed) and the secondary will be the in-bound + * accept() connection (if allowed). + * + * The session->accept flag is set iff the secondary connection is prepared to + * accept a connection. The flag is cleared as soon as a connection is + * accepted. + * + * When a session is enabled, the allowed connections are initialised and + * a BGP_Start event issued for each one. + * + *------------------------------------------------------------------------------ + * Error Handling. + * + * I/O and other operations may fail. When they do one of four events may + * be generated: + * + * a. BGP_Stop + * + * The functions bgp_fsm_stop_connection() and bgp_fsm_stop_session() + * set the cause of stop and generate a BGP_Stop event for one or both + * connections. The session is stopped if no connections remain. + * + * (The FSM itself uses bgp_fsm_set_stopping() before moving to + * Stopping state.) + * + * b. TCP_connection_closed ("soft" error) + * + * A read or write operation finds that the connection has been closed. + * + * This is raised when a read operation returns 0 bytes. + * + * Is also raised when read or write see the errors: + * + * ECONNRESET, ENETDOWN, ENETUNREACH, EPIPE or ETIMEDOUT + * + * Other errors are reported as TCP_fatal_error. + * + * The function bgp_fsm_io_error() is used by read and write operations to + * signal an error -- it decides which event to generate. (Error == 0 is + * used to signal a read operation that has returned 0.) + * + * c. TCP_connection_open_failed ("soft" error) + * + * A connect() operation has failed: + * + * ECONNREFUSED, ECONNRESET, EHOSTUNREACH or ETIMEDOUT + * + * Other errors are reported as TCP_fatal_error. + * + * The function bgp_fsm_connect_completed() decides what event to generate. + * (It will generate TCP_connection_open if there is no error.) + * + * All errors that accept() may raise are fatal. + * + * d. TCP_fatal_error ("hard" error) + * + * Raised by unexpected errors in connect/accept/read/write + * + * The function bgp_fsm_io_fatal_error() will generate a TCP_fatal_error. + */ + + +/*============================================================================== + * Functions to enable, stop, signal I/O events to, etc. the FSM + */ - case OpenSent: - /* OpenSent status. */ - BGP_TIMER_OFF (peer->t_start); - BGP_TIMER_OFF (peer->t_connect); - if (peer->v_holdtime != 0) - { - BGP_TIMER_ON (peer->t_holdtime, bgp_holdtime_timer, - peer->v_holdtime); - } - else - { - BGP_TIMER_OFF (peer->t_holdtime); - } - BGP_TIMER_OFF (peer->t_keepalive); - BGP_TIMER_OFF (peer->t_asorig); - BGP_TIMER_OFF (peer->t_routeadv); - break; +static void +bgp_fsm_set_stopping(bgp_connection connection, bgp_stopped_cause_t cause, + bgp_notify notification, int both) ; - case OpenConfirm: - /* OpenConfirm status. */ - BGP_TIMER_OFF (peer->t_start); - BGP_TIMER_OFF (peer->t_connect); - - /* If the negotiated Hold Time value is zero, then the Hold Time - timer and KeepAlive timers are not started. */ - if (peer->v_holdtime == 0) - { - BGP_TIMER_OFF (peer->t_holdtime); - BGP_TIMER_OFF (peer->t_keepalive); - } - else - { - BGP_TIMER_ON (peer->t_holdtime, bgp_holdtime_timer, - peer->v_holdtime); - BGP_TIMER_ON (peer->t_keepalive, bgp_keepalive_timer, - peer->v_keepalive); - } - BGP_TIMER_OFF (peer->t_asorig); - BGP_TIMER_OFF (peer->t_routeadv); - break; +/*------------------------------------------------------------------------------ + * Enable the given connection -- which must be newly initialised. + * + * This is the first step in the FSM, and the connection advances to Idle. + */ - case Established: - /* In Established status start and connect timer is turned - off. */ - BGP_TIMER_OFF (peer->t_start); - BGP_TIMER_OFF (peer->t_connect); - - /* Same as OpenConfirm, if holdtime is zero then both holdtime - and keepalive must be turned off. */ - if (peer->v_holdtime == 0) - { - BGP_TIMER_OFF (peer->t_holdtime); - BGP_TIMER_OFF (peer->t_keepalive); - } - else - { - BGP_TIMER_ON (peer->t_holdtime, bgp_holdtime_timer, - peer->v_holdtime); - BGP_TIMER_ON (peer->t_keepalive, bgp_keepalive_timer, - peer->v_keepalive); - } - BGP_TIMER_OFF (peer->t_asorig); - break; - case Deleted: - BGP_TIMER_OFF (peer->t_gr_restart); - BGP_TIMER_OFF (peer->t_gr_stale); - BGP_TIMER_OFF (peer->t_pmax_restart); - case Clearing: - BGP_TIMER_OFF (peer->t_start); - BGP_TIMER_OFF (peer->t_connect); - BGP_TIMER_OFF (peer->t_holdtime); - BGP_TIMER_OFF (peer->t_keepalive); - BGP_TIMER_OFF (peer->t_asorig); - BGP_TIMER_OFF (peer->t_routeadv); +extern void +bgp_fsm_enable_connection(bgp_connection connection) +{ + assert(connection->state == bgp_fsm_Initial) ; + bgp_fsm_event(connection, bgp_fsm_BGP_Start) ; +} ; + +/*------------------------------------------------------------------------------ + * Bring given connection to a stop. + * + * If is the only connection for the session, then the session is stopped. + * + * Is given the reasons for the stop. This function looks after releasing the + * notification once it is finished with it. + * + * Records the reasons for the stop, and then generates a BGP_Stop event. + * + * This may be used to stop: + * + * * as requested by Routeing Engine. The notification, if any, should be a + * Cease. + * + * * because a problem with a BGP packet has arisen. The notification, if + * any, will describe the problem. + * + * Note that I/O problems are signalled by bgp_fsm_io_error(). + * + * NB: may NOT be used within the FSM. + * + * NB: locks and unlocks the session. + */ +extern void +bgp_fsm_stop_connection(bgp_connection connection, bgp_stopped_cause_t cause, + bgp_notify notification) +{ + bgp_fsm_set_stopping(connection, cause, notification, 0) ; + bgp_fsm_event(connection, bgp_fsm_BGP_Stop) ; +} ; + +/*------------------------------------------------------------------------------ + * Bring given connection to a stop, and the other connection (if any), and + * then the session. + * + * See bgp_fsm_stop_connection, above. + */ +extern void +bgp_fsm_stop_session(bgp_connection connection, bgp_stopped_cause_t cause, + bgp_notify notification) +{ + bgp_fsm_set_stopping(connection, cause, notification, 1) ; + bgp_fsm_event(connection, bgp_fsm_BGP_Stop) ; +} ; + +/*------------------------------------------------------------------------------ + * Signal a fatal I/O error on the given connection. + * + * Error to be reported as "TCP_fatal_error". + */ +extern void +bgp_fsm_io_fatal_error(bgp_connection connection, int err) +{ + connection->err = err ; + + plog_err (connection->log, "%s [Error] bgp IO error: %s", + connection->host, safe_strerror(err)) ; + + bgp_fsm_event(connection, bgp_fsm_TCP_fatal_error) ; +} ; + +/*------------------------------------------------------------------------------ + * Signal an I/O error on the given connection. + * + * This is used by read/write operations -- so not until the TCP connection + * is up (which implies OpenSent state or later). + * + * It is assumed that the read/write code deals with EAGAIN/EWOULDBLOCK/EINTR + * itself -- so only real errors are reported here. + * + * A read operation that returns zero is reported here as err == 0. + * + * The following are reported as "TCP_connection_closed": + * + * 0, ECONNRESET, ENETDOWN, ENETUNREACH, EPIPE or ETIMEDOUT + * + * All other errors are reported as "TCP_fatal_error". + */ +extern void +bgp_fsm_io_error(bgp_connection connection, int err) +{ + connection->err = err ; + + if ( (err == 0) + || (err == ECONNRESET) + || (err == ENETDOWN) + || (err == ENETUNREACH) + || (err == EPIPE) + || (err == ETIMEDOUT) ) + { + if (BGP_DEBUG(events, EVENTS)) + if (err == 0) + plog_debug(connection->log, + "%s [Event] BGP connection closed fd %d", + connection->host, qps_file_fd(&connection->qf)) ; + else + plog_debug(connection->log, + "%s [Event] BGP connection closed fd %d (%s)", + connection->host, qps_file_fd(&connection->qf), + safe_strerror(err)) ; + + bgp_fsm_event(connection, bgp_fsm_TCP_connection_closed) ; } -} - -/* BGP start timer. This function set BGP_Start event to thread value - and process event. */ -static int -bgp_start_timer (struct thread *thread) + else + bgp_fsm_io_fatal_error(connection, err) ; +} ; + +/*------------------------------------------------------------------------------ + * Signal completion of a connect() or an accept() for the given connection. + * + * This is used by the connect() and accept() qpselect actions. It is also + * used if a connect() attempt fails immediately. + * + * If err == 0, then all is well: generate TCP_connection_open event. + * + * If err is one of: + * + * ECONNREFUSED, ECONNRESET, EHOSTUNREACH or ETIMEDOUT + * + * generate TCP_connection_open_failed event. (accept() does not return any of + * these errors.) + * + * Other errors are reported as TCP_fatal_error. + */ +extern void +bgp_fsm_connect_completed(bgp_connection connection, int err) { - struct peer *peer; - - peer = THREAD_ARG (thread); - peer->t_start = NULL; - - if (BGP_DEBUG (fsm, FSM)) - zlog (peer->log, LOG_DEBUG, - "%s [FSM] Timer (start timer expire).", peer->host); - - THREAD_VAL (thread) = BGP_Start; - bgp_event (thread); /* bgp_event unlocks peer */ - - return 0; -} + connection->err = err ; + + if (err == 0) + bgp_fsm_event(connection, bgp_fsm_TCP_connection_open) ; + else if ( (err == ECONNREFUSED) + || (err == ECONNRESET) + || (err == EHOSTUNREACH) + || (err == ETIMEDOUT) ) + bgp_fsm_event(connection, bgp_fsm_TCP_connection_open_failed) ; + else + bgp_fsm_io_fatal_error(connection, err) ; +} ; + +/*============================================================================== + * For debug... + */ +#define BGP_FSM_DEBUG(connection, message) \ + if (BGP_DEBUG (fsm, FSM)) \ + plog_debug (connection->log, "%s [FSM] " message, connection->host) + +/*============================================================================== + * The FSM table and the finite state machine actions. + */ + +typedef bgp_fsm_state_t +bgp_fsm_action(bgp_connection connection, bgp_fsm_state_t next_state, + bgp_fsm_event_t event) ; + +struct bgp_fsm { + bgp_fsm_action* action ; + bgp_fsm_state_t next_state ; +} ; + +static bgp_fsm_action bgp_fsm_null ; +static bgp_fsm_action bgp_fsm_invalid ; +static bgp_fsm_action bgp_fsm_ignore ; +static bgp_fsm_action bgp_fsm_enter ; +static bgp_fsm_action bgp_fsm_start ; +static bgp_fsm_action bgp_fsm_restart ; +static bgp_fsm_action bgp_fsm_stop ; +static bgp_fsm_action bgp_fsm_open ; +static bgp_fsm_action bgp_fsm_failed ; +static bgp_fsm_action bgp_fsm_fatal ; +static bgp_fsm_action bgp_fsm_retry ; +static bgp_fsm_action bgp_fsm_error ; +static bgp_fsm_action bgp_fsm_expire ; +static bgp_fsm_action bgp_fsm_opened ; +static bgp_fsm_action bgp_fsm_establish ; +static bgp_fsm_action bgp_fsm_closed ; +static bgp_fsm_action bgp_fsm_kal_send ; +static bgp_fsm_action bgp_fsm_kal_recv ; +static bgp_fsm_action bgp_fsm_update ; +static bgp_fsm_action bgp_fsm_notified ; +static bgp_fsm_action bgp_fsm_done ; -/* BGP connect retry timer. */ -static int -bgp_connect_timer (struct thread *thread) +static void +bgp_fsm_state_change(bgp_connection connection, bgp_fsm_state_t new_state) ; + +/*------------------------------------------------------------------------------ + * Finite State Machine events + * + * 0. null_event + * + * Do nothing. As quietly as possible. + * + * Never generated, so should not be seen ! + * + * 1. BGP_Start + * + * a. in Initial state (-> Idle) + * + * raised immediately after creating the connection. + * + * b. in Idle state + * + * raised on expiry of IdleHoldTime. + * + * primary connection: proceed to Connect + * + * secondary connection: proceed to Accept + * + * Cannot happen at any other time. + * + * 2. BGP_Stop + * + * a. in all states: + * + * -- from Routeing Engine -- at any time. + * + * -- internally in the event of collision resolution. + * + * -- internally, in the event of some protocol error -- once + * connection is up and packets are being transferred. + * + * The reason for stopping is set in the connection before the event is + * generated. + * + * 3. TCP_connection_open + * + * a. primary connection: in Connect state (-> OpenSent) + * + * raised when a connect() connection succeeds + * + * b. secondary connection: in Active state (-> OpenSent) + * + * raised when an accept() connection is accepted. + * + * Cannot happen at any other time. + * + * 4. TCP_connection_closed + * + * Raised by "EOF" on read or by EPIPE and some other errors. + * + * a. in OpenSent and OpenConfirm states + * + * This may be because the the other end has detected a collision. + * It may be because the other end is being vexatious. + * + * Fall back to Idle. + * + * b. and Established state + * + * Stop the session. + * + * NB: any errors generated when the OPEN message is sent (on exit from + * Connect or Active states) are not delivered until has entered + * OpenSent state. + * + * Cannot happen at any other time. + * + * 5. TCP_connection_open_failed ("soft" error) + * + * a. in Connect State + * + * raised if connect() fails eg: ECONNREFUSED or ETIMEDOUT + * + * Cannot happen at any other time. In particular, any errors during an + * accept() are reported as TCP_fatal_error. + * + * 6. TCP_fatal_error ("hard" error) + * + * a. in all states other than Initial + * + * raised by unexpected errors in connect/accept/read/write + * + * Stops the connection and disables the type of connection. So, + * for the remains of this session, will not attempt to <<<<<<<<<<<<<<<< + * + * 7. ConnectRetry_timer_expired + * + * a. in either Connect or Active states ONLY. + * + * Time to give up current connection attempt(s), and start trying + * to connect all over again. + * + * Cannot happen at any other time. + * + * 8. Hold_Timer_expired + * + * a. in OpenSent state + * + * Time to give up waiting for an OPEN (or NOTIFICATION) from the + * other end. + * + * Fall back to Idle. + * + * For this state the RFC recommends a "large" value for the hold + * time -- and suggests 4 minutes. + * + * b. in OpenConfirm state + * + * Time to give up waiting for a KEEPALIVE to confirm the connection. + * + * Fall back to Idle. + * + * In this state the hold time used is that negotiated in the OPEN + * messages that have been exchanged. + * + * c. in Established state + * + * The session has failed. Stop. + * + * In this state the hold time used is that negotiated in the OPEN + * messages that have been exchanged. + * + * d. in Stopping state + * + * Time to give up trying to send NOTIFICATION and terminate the + * connection. + * + * Cannot happen at any other time. + * + * 9. KeepAlive_timer_expired + * + * a. in OpenConfirm and Established states + * + * Time to send a KEEPALIVE message. + * + * In these states the keepalive time used is that which follows + * from the hold time negotiated in the OPEN messages that have been + * exchanged. + * + * Cannot happen at any other time. + * + * 10. Receive_OPEN_message + * + * Generated by read action. + * + * a. in OpenSent state -- the expected response + * + * Proceed (via collision resolution) to OpenConfirm or Stopping. + * + * b. in OpenConfirm state -- FSM error + * + * Send NOTIFICATION. Fall back to Idle. + * + * c. in Established state -- FSM error + * + * Terminate session. + * + * Cannot happen at any other time (connection not up). + * + * 11. Receive_KEEPALIVE_message + * + * Generated by read action. + * + * a. in OpenSent state -- FSM error + * + * Fall + * + * b. in OpenConfirm state -- the expected response + * + * c. in Established state -- expected + * + * Cannot happen at any other time (connection not up). + * + * 12. Receive_UPDATE_message + * + * Generated by read action. + * + * a. in OpenSent and OpenConfirm states -- FSM error + * + * b. in Established state -- expected + * + * Cannot happen at any other time (connection not up). + * + * 13. Receive_NOTIFICATION_message + * + * Generated by read action. + * + * a. in OpenSent, OpenConfirm and Established states -- give up + * on the session. + * + * Cannot happen at any other time (connection not up). + * + * 14. Sent_NOTIFICATION_message + * + * Generated by write action when completed sending the message. + * + * a. in Stopping state -- the desired outcome + * + * Terminate the connection. + * + * Cannot happen at any other time. + */ + +/*------------------------------------------------------------------------------ + * Finite State Machine structure + */ + +static const struct bgp_fsm +bgp_fsm[bgp_fsm_last_state + 1][bgp_fsm_last_event + 1] = { - struct peer *peer; - - peer = THREAD_ARG (thread); - peer->t_connect = NULL; - - if (BGP_DEBUG (fsm, FSM)) - zlog (peer->log, LOG_DEBUG, "%s [FSM] Timer (connect timer expire)", - peer->host); + { + /* bgp_fsm_Initial: initialised in this state............................... + * + * Expect only a BGP_Start event, which arms the IdleHoldTimer and advances + * to the Idle state. + * + * Could (just) get a bgp_fsm_Stop if other connection stops immediately ! + * + * A connection should be in this state for a brief period between being + * initialised and set going. + * + * All other events (other than null) are invalid (should not happen). + */ + {bgp_fsm_null, bgp_fsm_Initial}, /* null event */ + {bgp_fsm_enter, bgp_fsm_Idle}, /* BGP_Start */ + {bgp_fsm_stop, bgp_fsm_Stopping}, /* BGP_Stop */ + {bgp_fsm_invalid, bgp_fsm_Stopping}, /* TCP_connection_open */ + {bgp_fsm_invalid, bgp_fsm_Stopping}, /* TCP_connection_closed */ + {bgp_fsm_invalid, bgp_fsm_Stopping}, /* TCP_connection_open_failed */ + {bgp_fsm_invalid, bgp_fsm_Stopping}, /* TCP_fatal_error */ + {bgp_fsm_invalid, bgp_fsm_Stopping}, /* ConnectRetry_timer_expired */ + {bgp_fsm_invalid, bgp_fsm_Stopping}, /* Hold_Timer_expired */ + {bgp_fsm_invalid, bgp_fsm_Stopping}, /* KeepAlive_timer_expired */ + {bgp_fsm_invalid, bgp_fsm_Stopping}, /* Receive_OPEN_message */ + {bgp_fsm_invalid, bgp_fsm_Stopping}, /* Receive_KEEPALIVE_message */ + {bgp_fsm_invalid, bgp_fsm_Stopping}, /* Receive_UPDATE_message */ + {bgp_fsm_invalid, bgp_fsm_Stopping}, /* Receive_NOTIFICATION_message */ + {bgp_fsm_invalid, bgp_fsm_Stopping}, /* Sent NOTIFICATION message */ + }, + { + /* bgp_fsm_Idle: waiting for IdleHoldTimer.................................. + * + * When a session is enabled both its connections start in this state. + * (Noting that an accept() only session starts only the secondary + * connection and a connect() only session starts only the primary.) + * + * While in this state is waiting for the IdleHoldTimer to expire. This + * timer becomes longer if the peer misbehaves. + * + * If a connection stops at OpenState or OpenConfirm, may loop back through + * Idle, with an increased IdleHoldTimer. + * + * In Idle state the connection is dormant. (While the secondary is Idle, + * no connections will be accepted.) + * + * If the peer keeps making or accepting TCP connections, and then dropping + * them, then the IdleHoldTimer will grow to slow down the rate of vexatious + * connections. + * + * When a connection falls back to Idle it will have been closed. + * + * The expected events are: + * + * * BGP_Start -- generated by IdleHoldTimer expired + * + * For primary connection: + * + * Causes a connect() to be attempted. + * + * * Connect state -- if connect() OK, or failed "soft" + * + * * Stopping state -- if connect() failed "hard" + * + * Bring connection and session to a dead stop. + * + * For secondary connection: + * + * Enables session->accept, and goes to "Active" state. + * + * Note that bgp_fsm_start() decides on the appropriate next state. + * + * * BGP_Stop -- for whatever reason + * + * All other events (other than null) are invalid (should not happen). + */ + {bgp_fsm_null, bgp_fsm_Idle}, /* null event */ + {bgp_fsm_start, bgp_fsm_Connect}, /* BGP_Start */ + {bgp_fsm_stop, bgp_fsm_Stopping}, /* BGP_Stop */ + {bgp_fsm_invalid, bgp_fsm_Stopping}, /* TCP_connection_open */ + {bgp_fsm_invalid, bgp_fsm_Stopping}, /* TCP_connection_closed */ + {bgp_fsm_invalid, bgp_fsm_Stopping}, /* TCP_connection_open_failed */ + {bgp_fsm_invalid, bgp_fsm_Stopping}, /* TCP_fatal_error */ + {bgp_fsm_invalid, bgp_fsm_Stopping}, /* ConnectRetry_timer_expired */ + {bgp_fsm_invalid, bgp_fsm_Stopping}, /* Hold_Timer_expired */ + {bgp_fsm_invalid, bgp_fsm_Stopping}, /* KeepAlive_timer_expired */ + {bgp_fsm_invalid, bgp_fsm_Stopping}, /* Receive_OPEN_message */ + {bgp_fsm_invalid, bgp_fsm_Stopping}, /* Receive_KEEPALIVE_message */ + {bgp_fsm_invalid, bgp_fsm_Stopping}, /* Receive_UPDATE_message */ + {bgp_fsm_invalid, bgp_fsm_Stopping}, /* Receive_NOTIFICATION_message */ + {bgp_fsm_invalid, bgp_fsm_Stopping}, /* Sent NOTIFICATION message */ + }, + { + /* bgp_fsm_Connect: waiting for connect (and listen)........................ + * + * Only the primary connection can be in this state. + * + * While in this state is waiting for connection to succeed or fail, or for + * the ConnectRetryTimer to expire. + * + * The expected events are: + * + * * TCP_connection_open + * + * Send BGP OPEN message, arm the HoldTimer ("large" value) and advance + * to OpenSent. + * + * * TCP_connection_open_fail ("soft" error) + * + * Shut down the connection. Stay in Connect state. + * + * The ConnectRetryTimer is left running. + * + * * TCP_fatal_error ("hard" error) + * + * Bring connection and session to a dead stop. + * + * * ConnectRetry_timer_expired + * + * Shut down the connection. Retry opening a connection. Stay in + * Connect state. Refresh the ConnectRetryTimer. + * + * * BGP_Stop -- for whatever reason + * + * All other events (other than null) are invalid (should not happen). + */ + {bgp_fsm_null, bgp_fsm_Connect}, /* null event */ + {bgp_fsm_invalid, bgp_fsm_Stopping}, /* BGP_Start */ + {bgp_fsm_stop, bgp_fsm_Stopping}, /* BGP_Stop */ + {bgp_fsm_open, bgp_fsm_OpenSent}, /* TCP_connection_open */ + {bgp_fsm_invalid, bgp_fsm_Stopping}, /* TCP_connection_closed */ + {bgp_fsm_failed, bgp_fsm_Connect}, /* TCP_connection_open_failed */ + {bgp_fsm_fatal, bgp_fsm_Stopping}, /* TCP_fatal_error */ + {bgp_fsm_retry, bgp_fsm_Connect}, /* ConnectRetry_timer_expired */ + {bgp_fsm_invalid, bgp_fsm_Stopping}, /* Hold_Timer_expired */ + {bgp_fsm_invalid, bgp_fsm_Stopping}, /* KeepAlive_timer_expired */ + {bgp_fsm_invalid, bgp_fsm_Stopping}, /* Receive_OPEN_message */ + {bgp_fsm_invalid, bgp_fsm_Stopping}, /* Receive_KEEPALIVE_message */ + {bgp_fsm_invalid, bgp_fsm_Stopping}, /* Receive_UPDATE_message */ + {bgp_fsm_invalid, bgp_fsm_Stopping}, /* Receive_NOTIFICATION_message */ + {bgp_fsm_invalid, bgp_fsm_Stopping}, /* Sent NOTIFICATION message */ + }, + { + /* bgp_fsm_Active: waiting for listen (only)................................ + * + * Only the secondary connection can be in this state. + * + * While in this state is waiting for an incoming connection to succeed or + * for the ConnectRetryTimer to expire. + * + * The expected events are: + * + * * TCP_connection_open + * + * Send BGP OPEN message, arm the HoldTimer ("large" value) and advance + * to OpenSent. + * + * * TCP_fatal_error + * + * Bring connection and session to a dead stop. + * + * * ConnectRetry_timer_expired + * + * Stay in Active state. Refresh the ConnectRetryTimer. + * + * * BGP_Stop -- for whatever reason + * + * All other events (other than null) are invalid (should not happen). + */ + {bgp_fsm_null, bgp_fsm_Active}, /* null event */ + {bgp_fsm_invalid, bgp_fsm_Stopping}, /* BGP_Start */ + {bgp_fsm_stop, bgp_fsm_Stopping}, /* BGP_Stop */ + {bgp_fsm_open, bgp_fsm_OpenSent}, /* TCP_connection_open */ + {bgp_fsm_invalid, bgp_fsm_Stopping}, /* TCP_connection_closed */ + {bgp_fsm_invalid, bgp_fsm_Stopping}, /* TCP_connection_open_failed */ + {bgp_fsm_fatal, bgp_fsm_Stopping}, /* TCP_fatal_error */ + {bgp_fsm_retry, bgp_fsm_Active}, /* ConnectRetry_timer_expired */ + {bgp_fsm_invalid, bgp_fsm_Stopping}, /* Hold_Timer_expired */ + {bgp_fsm_invalid, bgp_fsm_Stopping}, /* KeepAlive_timer_expired */ + {bgp_fsm_invalid, bgp_fsm_Stopping}, /* Receive_OPEN_message */ + {bgp_fsm_invalid, bgp_fsm_Stopping}, /* Receive_KEEPALIVE_message */ + {bgp_fsm_invalid, bgp_fsm_Stopping}, /* Receive_UPDATE_message */ + {bgp_fsm_invalid, bgp_fsm_Stopping}, /* Receive_NOTIFICATION_message */ + {bgp_fsm_invalid, bgp_fsm_Stopping}, /* Sent NOTIFICATION message */ + }, + { + /* bgp_fsm_OpenSent: waiting for Open from the other end.................... + * + * Both primary and secondary connections can be in this state. + * + * While in this state is waiting for a BGP OPEN to arrive or for the + * HoldTimer ("large" value) to expire. + * + * The expected events are: + * + * * Receive_OPEN_message + * + * This means has received a satisfactory BGP OPEN from the other end, + * so the session is very nearly up. + * + * If there is another connection, and it is in OpenConfirm state, + * then must now choose between the two -- terminating one or the + * other with a "Connection Collision Resolution" NOTIFICATION message. + * + * If proceeding, send a BGP KEEPALIVE message (effectively ACK), arm + * HoldTimer and KeepliveTimer (as per negotiated values) and advance + * to OpenConfirm state. + * + * * Receive_UPDATE_message + * + * FSM error -- bring connection to a dead stop. + * + * * Receive_KEEPALIVE_message + * + * FSM error -- bring connection to a dead stop. + * + * * Receive_NOTIFICATION_message + * + * Bring connection to a dead stop. + * + * * TCP_connection_closed + * + * Close connection, + * + * * TCP_fatal_error + * + * Bring connection and session to a dead stop. + * + * * Hold_Timer_expired + * + * If primary, promote the secondary. If no secondary... + * + * * BGP_Stop -- for whatever reason + * + * All other events (other than null) are invalid (should not happen). + */ + {bgp_fsm_null, bgp_fsm_OpenSent}, /* null event */ + {bgp_fsm_invalid, bgp_fsm_Stopping}, /* BGP_Start */ + {bgp_fsm_stop, bgp_fsm_Stopping}, /* BGP_Stop */ + {bgp_fsm_invalid, bgp_fsm_Stopping}, /* TCP_connection_open */ + {bgp_fsm_restart, bgp_fsm_Idle}, /* TCP_connection_closed */ + {bgp_fsm_invalid, bgp_fsm_Stopping}, /* TCP_connection_open_failed */ + {bgp_fsm_fatal, bgp_fsm_Stopping}, /* TCP_fatal_error */ + {bgp_fsm_invalid, bgp_fsm_Stopping}, /* ConnectRetry_timer_expired */ + {bgp_fsm_restart, bgp_fsm_Idle}, /* Hold_Timer_expired */ + {bgp_fsm_invalid, bgp_fsm_Stopping}, /* KeepAlive_timer_expired */ + {bgp_fsm_opened, bgp_fsm_OpenConfirm}, /* Receive_OPEN_message */ + {bgp_fsm_error, bgp_fsm_Stopping}, /* Receive_KEEPALIVE_message */ + {bgp_fsm_error, bgp_fsm_Stopping}, /* Receive_UPDATE_message */ + {bgp_fsm_notified, bgp_fsm_Stopping}, /* Receive_NOTIFICATION_message */ + {bgp_fsm_invalid, bgp_fsm_Stopping}, /* Sent NOTIFICATION message */ + }, + { + /* bgp_fsm_OpenConfirm: Opens sent and received, waiting for KeepAlive...... + * + * Only one of the two connections can reach this state. + * + * While in this state is waiting for a BGP KEEPALIVE to arrive or for the + * HoldTimer to expire, or for the KeepaliveTimer to prompt sending of + * another KEEPALIVE message. + * + * The expected events are: + * + * * Receive_KEEPALIVE_message + * + * This means that the other end is acknowledging the OPEN, and the + * session is now Established. + * + * If there is another connection, now is the time to kill it off. + * + * This connection becomes the primary and only connection. + * + * Arm HoldTimer and KeepliveTimer (as per negotiated values) and + * advance to Established state. + * + * Pass a session established message to the Routeing Engine, complete + * with the bgp_open_state for the successful connection. + * + * * Receive_OPEN_message + * + * FSM error -- bring connection to a dead stop. + * + * If primary, promote the secondary. If no secondary... + * + * * Receive_UPDATE_message + * + * FSM error -- bring connection to a dead stop. + * + * If primary, promote the secondary. If no secondary... + * + * * Receive_NOTIFICATION_message + * + * Bring connection to a dead stop. + * + * If primary, promote the secondary. If no secondary... + * + * * TCP_connection_closed + * + * If primary, promote the secondary. If no secondary... + * + * * TCP_fatal_error + * + * Bring connection and session to a dead stop. + * + * * KeepAlive_Timer_expired + * + * Send KEEPALIVE message and recharge KeepaliveTimer. + * + * * Hold_Timer_expired + * + * If primary, promote the secondary. If no secondary... + * + * * BGP_Stop -- for whatever reason + * + * All other events (other than null) are invalid (should not happen). + */ + {bgp_fsm_null, bgp_fsm_OpenConfirm}, /* null event */ + {bgp_fsm_invalid, bgp_fsm_Stopping}, /* BGP_Start */ + {bgp_fsm_stop, bgp_fsm_Stopping}, /* BGP_Stop */ + {bgp_fsm_invalid, bgp_fsm_Stopping}, /* TCP_connection_open */ + {bgp_fsm_restart, bgp_fsm_Idle}, /* TCP_connection_closed */ + {bgp_fsm_invalid, bgp_fsm_Stopping}, /* TCP_connection_open_failed */ + {bgp_fsm_fatal, bgp_fsm_Stopping}, /* TCP_fatal_error */ + {bgp_fsm_invalid, bgp_fsm_Stopping}, /* ConnectRetry_timer_expired */ + {bgp_fsm_restart, bgp_fsm_Idle}, /* Hold_Timer_expired */ + {bgp_fsm_kal_send, bgp_fsm_OpenConfirm}, /* KeepAlive_timer_expired */ + {bgp_fsm_error, bgp_fsm_Stopping}, /* Receive_OPEN_message */ + {bgp_fsm_establish, bgp_fsm_Established}, /* Receive_KEEPALIVE_message */ + {bgp_fsm_error, bgp_fsm_Stopping}, /* Receive_UPDATE_message */ + {bgp_fsm_notified, bgp_fsm_Stopping}, /* Receive_NOTIFICATION_message */ + {bgp_fsm_invalid, bgp_fsm_Stopping}, /* Sent NOTIFICATION message */ + }, + { + /* bgp_fsm_Established: session is up and running........................... + * + * Only the primary connection exists in this state. + * + * While in this state is waiting for a BGP UPDATE (or KEEPALIVE) messages + * to arrive or for the HoldTimer to expire, or for the KeepaliveTimer to + * prompt sending of another KEEPALIVE message. + * + * The expected events are: + * + * * Receive_OPEN_message + * + * FSM error -- bring connection and session to a dead stop. + * + * * Receive_UPDATE_message + * + * Restart the HoldTimer. + * + * * Receive_KEEPALIVE_message + * + * Restart the HoldTimer. + * + * * Receive_NOTIFICATION_message + * + * Bring connection and session to a dead stop. + * + * * TCP_connection_closed + * + * Bring connection and session to a dead stop. + * + * * TCP_fatal_error + * + * Bring connection and session to a dead stop. + * + * * KeepAlive_Timer_expired + * + * Send KEEPALIVE message and recharge KeepaliveTimer. + * + * * Hold_Timer_expired + * + * If primary, promote the secondary. If no secondary... + * + * * BGP_Stop -- for whatever reason + * + * All other events (other than null) are invalid (should not happen). + */ + {bgp_fsm_null, bgp_fsm_Established}, /* null event */ + {bgp_fsm_invalid, bgp_fsm_Stopping}, /* BGP_Start */ + {bgp_fsm_stop, bgp_fsm_Stopping}, /* BGP_Stop */ + {bgp_fsm_invalid, bgp_fsm_Stopping}, /* TCP_connection_open */ + {bgp_fsm_closed, bgp_fsm_Stopping}, /* TCP_connection_closed */ + {bgp_fsm_invalid, bgp_fsm_Stopping}, /* TCP_connection_open_failed */ + {bgp_fsm_fatal, bgp_fsm_Stopping}, /* TCP_fatal_error */ + {bgp_fsm_invalid, bgp_fsm_Stopping}, /* ConnectRetry_timer_expired */ + {bgp_fsm_expire, bgp_fsm_Stopping}, /* Hold_Timer_expired */ + {bgp_fsm_kal_send, bgp_fsm_Established}, /* KeepAlive_timer_expired */ + {bgp_fsm_error, bgp_fsm_Stopping}, /* Receive_OPEN_message */ + {bgp_fsm_kal_recv, bgp_fsm_Established}, /* Receive_KEEPALIVE_message */ + {bgp_fsm_update, bgp_fsm_Established}, /* Receive_UPDATE_message */ + {bgp_fsm_notified, bgp_fsm_Stopping}, /* Receive_NOTIFICATION_message */ + {bgp_fsm_invalid, bgp_fsm_Stopping}, /* Sent NOTIFICATION message */ + }, + { + /* bgp_fsm_Stopping: waiting (briefly) to send Notification................. + * + * Before a connection is sent to Stopping state the reasons for stopping + * are set. (See bgp_fsm_set_stopping.) + * + * There are two flavours of stop: + * + * * stop-idle + * + * Close the connection, then fall back to Idle state. + * + * * stop-dead + * + * Close and terminate the connection (cut loose from session). + * + * If this is the only connection, stop the session. + * + * The complication is the possible need to send a NOTIFICATION message + * before closing the connection. + * + * Once a connection has reached Established state, the TCP write buffers + * may be full, so it may not be possible immediately to send the + * NOTIFICATION. Note that stopping from Established state is always + * stop-dead. + * + * In other states there should be plenty of room in the TCP write buffers. + * + * On entry to Stopping: + * + * 1) if this is stop-dead -- unlink self from session. + * + * NB: this clears the pointer from session to connection. + * + * .... + * + * 2) if there is a NOTIFICATION message (notification_pending): + * + * * close the connection for reading and purge read buffers + * * purge the write buffering and any pending writes + * * stop all timers + * * send the NOTIFICATION + * + * if the NOTIFICATION immediately clears the buffers (or fails), + * clear the notification_pending flag. + * + * 3) if the notification_pending flag is still set: + * + * * for stop-idle set a short time-out (5 seconds) + * * for stop-dead set a longer time-out (30 seconds) + * + * stays in Stopping state, waiting for NOTIFICATION to be sent, or + * to fail, or for the timeout. + * + * (Should not really need the time-out for stop-idle, but seems + * neater than crash closing the connection.) + * + * While in Stopping state, any further event will clear the + * notification-pending flag. + * + * When the notification-pending flag is not set: + * + * * close the connection + * * purge all buffering + * * stop all timers + * + * * for stop-idle: proceed to Idle state + + + + + + * In this state the connection is no longer associated with a session. + * + * This state exists only to allow the TCP output buffer to drain + * sufficiently to allow the tail end of one BGP message to be sent, + * followed by a NOTIFICATION message. + * + * When entering this state, if there is no NOTIFICATION to send, then + * will terminate the session. + * + * While in this state is waiting for the NOTIFICATION message to have been + * sent, or for the HoldTimer to expire (does not wait indefinitely). + * + * The expected events are: + * + * * Sent NOTIFICATION message + * * Hold_Timer_expired + * * TCP_fatal_error + * * TCP_connection_closed + * + * Clear NOTIFICATION pending, so connection will then be terminated. + * + * All other events (other than null) are invalid (should not happen). + */ + {bgp_fsm_null, bgp_fsm_Stopping}, /* null event */ + {bgp_fsm_invalid, bgp_fsm_Stopping}, /* BGP_Start */ + {bgp_fsm_invalid, bgp_fsm_Stopping}, /* BGP_Stop */ + {bgp_fsm_invalid, bgp_fsm_Stopping}, /* TCP_connection_open */ + {bgp_fsm_done, bgp_fsm_Stopping}, /* TCP_connection_closed */ + {bgp_fsm_invalid, bgp_fsm_Stopping}, /* TCP_connection_open_failed */ + {bgp_fsm_done, bgp_fsm_Stopping}, /* TCP_fatal_error */ + {bgp_fsm_invalid, bgp_fsm_Stopping}, /* ConnectRetry_timer_expired */ + {bgp_fsm_done, bgp_fsm_Stopping}, /* Hold_Timer_expired */ + {bgp_fsm_invalid, bgp_fsm_Stopping}, /* KeepAlive_timer_expired */ + {bgp_fsm_invalid, bgp_fsm_Stopping}, /* Receive_OPEN_message */ + {bgp_fsm_invalid, bgp_fsm_Stopping}, /* Receive_KEEPALIVE_message */ + {bgp_fsm_invalid, bgp_fsm_Stopping}, /* Receive_UPDATE_message */ + {bgp_fsm_invalid, bgp_fsm_Stopping}, /* Receive_NOTIFICATION_message */ + {bgp_fsm_done, bgp_fsm_Stopping}, /* Sent NOTIFICATION message */ + }, +} ; - THREAD_VAL (thread) = ConnectRetry_timer_expired; - bgp_event (thread); /* bgp_event unlocks peer */ +static const char *bgp_event_str[] = +{ + "NULL", + "BGP_Start", + "BGP_Stop", + "TCP_connection_open", + "TCP_connection_closed", + "TCP_connection_open_failed", + "TCP_fatal_error", + "ConnectRetry_timer_expired", + "Hold_Timer_expired", + "KeepAlive_timer_expired", + "Receive_OPEN_message", + "Receive_KEEPALIVE_message", + "Receive_UPDATE_message", + "Receive_NOTIFICATION_message", + "Sent_NOTIFICATION_message", +} ; + +/*------------------------------------------------------------------------------ + * Deal with the given event for the given connection. + */ +extern void +bgp_fsm_event(bgp_connection connection, bgp_fsm_event_t event) +{ + bgp_fsm_state_t next_state ; + const struct bgp_fsm* fsm ; - return 0; -} + dassert( (event >= bgp_fsm_null_event) + && (event <= bgp_fsm_last_event)) ; + dassert( (connection->state >= bgp_fsm_first_state) + && (connection->state >= bgp_fsm_last_state) ) ; -/* BGP holdtime timer. */ -static int -bgp_holdtime_timer (struct thread *thread) -{ - struct peer *peer; + /* Watch out for recursing through the FSM for this connection. */ + ++connection->fsm_active ; - peer = THREAD_ARG (thread); - peer->t_holdtime = NULL; + if (connection->fsm_active == 2) + { + connection->post = event ; + return ; + } ; + + /* Lock the session for the convenience of the event handlers. + * + * NB: if the current state is Stopping, then connection is no longer + * attached to session -- so connection->session is NULL -- BEWARE ! + * + * The session lock does nothing if no session is attached. + */ + BGP_CONNECTION_SESSION_LOCK(connection) ; /*<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ - if (BGP_DEBUG (fsm, FSM)) - zlog (peer->log, LOG_DEBUG, - "%s [FSM] Timer (holdtime timer expire)", - peer->host); + do + { + assert(connection->fsm_active == 1) ; - THREAD_VAL (thread) = Hold_Timer_expired; - bgp_event (thread); /* bgp_event unlocks peer */ + fsm = &bgp_fsm[connection->state][event] ; + next_state = fsm->next_state ; - return 0; -} + /* Call function. */ + next_state = fsm->action(connection, next_state, event) ; -/* BGP keepalive fire ! */ -static int -bgp_keepalive_timer (struct thread *thread) -{ - struct peer *peer; + dassert( (next_state >= bgp_fsm_first_state) + && (next_state <= bgp_fsm_last_state) ) ; - peer = THREAD_ARG (thread); - peer->t_keepalive = NULL; + /* If state is changed. */ + if (next_state != connection->state) + { + bgp_fsm_state_t prev_state = connection->state ; - if (BGP_DEBUG (fsm, FSM)) - zlog (peer->log, LOG_DEBUG, - "%s [FSM] Timer (keepalive timer expire)", - peer->host); + /* Complete the state change */ + bgp_fsm_state_change(connection, next_state) ; - THREAD_VAL (thread) = KeepAlive_timer_expired; - bgp_event (thread); /* bgp_event unlocks peer */ + /* Log state change as required. */ + if (BGP_DEBUG(fsm, FSM)) + plog_debug(connection->log, + "%s [FSM] %s (%s->%s)", + connection->host, + bgp_event_str[event], + LOOKUP (bgp_status_msg, prev_state), + LOOKUP (bgp_status_msg, next_state)) ; - return 0; -} + if (BGP_DEBUG(normal, NORMAL)) + zlog_debug ("%s on %s went from %s to %s", + connection->host, + bgp_event_str[event], + LOOKUP (bgp_status_msg, prev_state), + LOOKUP (bgp_status_msg, next_state)); + } ; -static int -bgp_routeadv_timer (struct thread *thread) -{ - struct peer *peer; + /* Pick up post event -- if any */ + event = connection->post ; + connection->post = bgp_fsm_null_event ; - peer = THREAD_ARG (thread); - peer->t_routeadv = NULL; + } while (--connection->fsm_active != 0) ; - if (BGP_DEBUG (fsm, FSM)) - zlog (peer->log, LOG_DEBUG, - "%s [FSM] Timer (routeadv timer expire)", - peer->host); + BGP_CONNECTION_SESSION_UNLOCK(connection) ; /*>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ - peer->synctime = time (NULL); + /* Connections which are Stopping are no longer linked to a session. */ + /* There is no way out of Stopping state - BGP_WRITE_ON (peer->t_write, bgp_write, peer->fd); + if (connection->state == bgp_fsm_Stopping) + { + /* Sever link with session -- after mutex unlock the first time */ - BGP_TIMER_ON (peer->t_routeadv, bgp_routeadv_timer, - peer->v_routeadv); + session->connections[connection->ordinal] = NULL ; - return 0; -} + connection->session = NULL ; + connection->p_mutex = NULL ; + } ; +} ; -/* Reset bgp update timer */ -static void -bgp_uptime_reset (struct peer *peer) +/*------------------------------------------------------------------------------ + * Null action -- do nothing at all. + */ +static bgp_fsm_state_t +bgp_fsm_null(bgp_connection connection, bgp_fsm_state_t next_state, + bgp_fsm_event_t event) { - peer->uptime = time (NULL); -} - -/* BGP Peer Down Cause */ -const char *peer_down_str[] = + return next_state ; +} ; + +/*------------------------------------------------------------------------------ + * Invalid event -- cannot occur in current state. + * + * Brings down the session -- next state is bgp_fsm_stopping. + * + * + * NB: the session is locked. + */ +static bgp_fsm_state_t +bgp_fsm_invalid(bgp_connection connection, bgp_fsm_state_t next_state, + bgp_fsm_event_t event) { - "", - "Router ID changed", - "Remote AS changed", - "Local AS change", - "Cluster ID changed", - "Confederation identifier changed", - "Confederation peer changed", - "RR client config change", - "RS client config change", - "Update source change", - "Address family activated", - "Admin. shutdown", - "User reset", - "BGP Notification received", - "BGP Notification send", - "Peer closed the session", - "Neighbor deleted", - "Peer-group add member", - "Peer-group delete member", - "Capability changed", - "Passive config change", - "Multihop config change", - "NSF peer closed the session" -}; - -static int -bgp_graceful_restart_timer_expire (struct thread *thread) + if (BGP_DEBUG (fsm, FSM)) \ + plog_debug (connection->log, "%s [FSM] invalid event %d in state %d", + connection->host, event, connection->state) ; + + bgp_fsm_set_stopping(connection, bgp_stopped_invalid, + bgp_notify_new(BGP_NOMC_FSM, BGP_NOMS_UNSPECIFIC, 0), 1) ; + + return bgp_fsm_Stopping ; +} ; + +/*------------------------------------------------------------------------------ + * This is empty event -- should not really happen... + * + * NB: the session is locked. + */ +static bgp_fsm_state_t +bgp_fsm_ignore(bgp_connection connection, bgp_fsm_state_t next_state, + bgp_fsm_event_t event) { - struct peer *peer; - afi_t afi; - safi_t safi; - - peer = THREAD_ARG (thread); - peer->t_gr_restart = NULL; - - /* NSF delete stale route */ - for (afi = AFI_IP ; afi < AFI_MAX ; afi++) - for (safi = SAFI_UNICAST ; safi < SAFI_UNICAST_MULTICAST ; safi++) - if (peer->nsf[afi][safi]) - bgp_clear_stale_route (peer, afi, safi); - - UNSET_FLAG (peer->sflags, PEER_STATUS_NSF_WAIT); - BGP_TIMER_OFF (peer->t_gr_stale); - - if (BGP_DEBUG (events, EVENTS)) + BGP_FSM_DEBUG(connection, "bgp_ignore called") ; + + return next_state ; +} ; + +/*------------------------------------------------------------------------------ + * Entry point to FSM. + * + * This is the first thing to happen to the FSM, and takes it from Initial + * state to Idle, with Idle Hold Timer running. + * + * NB: the session is locked. + */ +static bgp_fsm_state_t +bgp_fsm_enter(bgp_connection connection, bgp_fsm_state_t next_state, + bgp_fsm_event_t event) +{ + return next_state ; +} ; + +/*------------------------------------------------------------------------------ + * Start up BGP Connection + * + * This is used: + * + * * to change from Idle to Connect or Active -- when the IdleHoldTimer + * expires. + * + * * to loop back to Connect or Active -- when the ConnectRetryTimer expires. + * + * The state entered depends on whether this is the primary or secondary + * connection. + * + * If this is the primary connection, then kicks a connect() into life, + * before the state change. Note that if that fails, then post an event to + * be processed as soon as completes the state transition. + * + * If this is the secondary connection, enables the session for accept(). + * + * NB: the session is locked. + */ +static bgp_fsm_state_t +bgp_fsm_start(bgp_connection connection, bgp_fsm_state_t next_state, + bgp_fsm_event_t event) +{ + if (connection->ordinal == bgp_connection_primary) { - zlog_debug ("%s graceful restart timer expired", peer->host); - zlog_debug ("%s graceful restart stalepath timer stopped", peer->host); + next_state = bgp_fsm_Connect ; + connection->post = bgp_open_connect(connection) ; } - - bgp_timer_set (peer); - - return 0; -} - -static int -bgp_graceful_stale_timer_expire (struct thread *thread) + else + { + next_state = bgp_fsm_Active ; + connection->session->accept = 1 ; + } ; + + return next_state ; +} ; + +/*------------------------------------------------------------------------------ + * Restart BGP Connection + * + * This is used when a TCP connection has come up, but has stopped -- for some + * reason (such as the connection simply closed) which suggests that the other + * end might still be prepared to make a connection. + * + * Extends the IdleHoldTimer for the session (up to a maximum of 120 secs) and + * changes to Idle state. + * + * Note that this works equally for the primary and the secondary connection. + * + * NB: the session is locked. + */ +static bgp_fsm_state_t +bgp_fsm_restart(bgp_connection connection, bgp_fsm_state_t next_state, + bgp_fsm_event_t event) { - struct peer *peer; - afi_t afi; - safi_t safi; + unsigned* p_interval = &connection->session->idle_hold_timer_interval ; + + *p_interval *= 2 ; + + if (*p_interval < 4) + *p_interval = 4 ; + else if (*p_interval > 120) + *p_interval = 120 ; + + bgp_connection_close(connection) ; + + return next_state ; +} ; + +/*------------------------------------------------------------------------------ + * Stop BGP Connection + * + * The reason should already have been set: bgp_fsm_set_stopping(). + * But, if not, set unknown reason. + * + * If no notification to be sent, close the connection now. + * If notification to be sent, try to send it now. + * + * NB: the session is locked. + */ +static bgp_fsm_state_t +bgp_fsm_stop(bgp_connection connection, bgp_fsm_state_t next_state, + bgp_fsm_event_t event) +{ + if (connection->stopped == bgp_stopped_not) + bgp_fsm_set_stopping(connection, bgp_stopped_unknown, NULL, 0) ; - peer = THREAD_ARG (thread); - peer->t_gr_stale = NULL; + /* */ + if (connection->notification_pending) + { + bgp_msg_write_notification(connection) ; - if (BGP_DEBUG (events, EVENTS)) - zlog_debug ("%s graceful restart stalepath timer expired", peer->host); - /* NSF delete stale route */ - for (afi = AFI_IP ; afi < AFI_MAX ; afi++) - for (safi = SAFI_UNICAST ; safi < SAFI_UNICAST_MULTICAST ; safi++) - if (peer->nsf[afi][safi]) - bgp_clear_stale_route (peer, afi, safi); + nothing pending + } + * * notification_pending nothing pending + * * notification_written not written +) - return 0; -} - -/* Called after event occured, this function change status and reset - read/write and timer thread. */ -void -bgp_fsm_change_status (struct peer *peer, int status) -{ - bgp_dump_state (peer, peer->status, status); + } ; - /* Transition into Clearing or Deleted must /always/ clear all routes.. - * (and must do so before actually changing into Deleted.. - */ - if (status >= Clearing) - bgp_clear_route_all (peer); - - /* Preserve old status and change into new status. */ - peer->ostatus = peer->status; - peer->status = status; - - if (BGP_DEBUG (normal, NORMAL)) - zlog_debug ("%s went from %s to %s", - peer->host, - LOOKUP (bgp_status_msg, peer->ostatus), - LOOKUP (bgp_status_msg, peer->status)); -} - -/* Flush the event queue and ensure the peer is shut down */ -static int -bgp_clearing_completed (struct peer *peer) -{ - int rc = bgp_stop(peer); - BGP_EVENT_FLUSH (peer); + /* If are still waiting for the + if (connection->notification_pending || connection->notification_written) + bgp_connection_read_close(connection) ; + else + bgp_connection_close(connection) ; - return rc; + return next_state ; } -/* Administrative BGP peer stop event. */ -/* May be called multiple times for the same peer */ -int -bgp_stop (struct peer *peer) +/*------------------------------------------------------------------------------ + * TCP connection open. + * + * Send BGP Open Message to peer. + */ +static bgp_fsm_state_t +bgp_fsm_open(bgp_connection connection, bgp_fsm_state_t next_state, + bgp_fsm_event_t event) { - afi_t afi; - safi_t safi; - char orf_name[BUFSIZ]; + char buf1[BUFSIZ]; - /* Can't do this in Clearing; events are used for state transitions */ - if (peer->status != Clearing) + if (connection->fd < 0) { - /* Delete all existing events of the peer */ - BGP_EVENT_FLUSH (peer); + zlog_err ("bgp_connect_success peer's fd is negative value %d", + connection->fd); + return -1; } - /* Increment Dropped count. */ - if (peer->status == Established) + bgp_getsockname(connection) ; + + if (BGP_DEBUG (normal, NORMAL)) { - peer->dropped++; - - /* bgp log-neighbor-changes of neighbor Down */ - if (bgp_flag_check (peer->bgp, BGP_FLAG_LOG_NEIGHBOR_CHANGES)) - zlog_info ("%%ADJCHANGE: neighbor %s Down %s", peer->host, - peer_down_str [(int) peer->last_reset]); - - /* graceful restart */ - if (peer->t_gr_stale) - { - BGP_TIMER_OFF (peer->t_gr_stale); - if (BGP_DEBUG (events, EVENTS)) - zlog_debug ("%s graceful restart stalepath timer stopped", peer->host); - } - if (CHECK_FLAG (peer->sflags, PEER_STATUS_NSF_WAIT)) - { - if (BGP_DEBUG (events, EVENTS)) - { - zlog_debug ("%s graceful restart timer started for %d sec", - peer->host, peer->v_gr_restart); - zlog_debug ("%s graceful restart stalepath timer started for %d sec", - peer->host, peer->bgp->stalepath_time); - } - BGP_TIMER_ON (peer->t_gr_restart, bgp_graceful_restart_timer_expire, - peer->v_gr_restart); - BGP_TIMER_ON (peer->t_gr_stale, bgp_graceful_stale_timer_expire, - peer->bgp->stalepath_time); - } + if (! connection->listenerCHECK_FLAG (peer->sflags, PEER_STATUS_ACCEPT_PEER)) + zlog_debug ("%s open active, local address %s", peer->host, + sockunion2str (peer->su_local, buf1, SU_ADDRSTRLEN)); else - { - UNSET_FLAG (peer->sflags, PEER_STATUS_NSF_MODE); + zlog_debug ("%s passive open", peer->host); + } - for (afi = AFI_IP ; afi < AFI_MAX ; afi++) - for (safi = SAFI_UNICAST ; safi < SAFI_UNICAST_MULTICAST ; safi++) - peer->nsf[afi][safi] = 0; - } + bgp_send_open(connection) ; + + return next_state ; +} ; + +/*------------------------------------------------------------------------------ + * Connect retry timer is expired when in Connect or Active states. + * + * For primary connection: + * + * * close the existing connection (may already be closed if failed) + * * start the connect() attempt again + * + * For secondary connection: + * + * * close the existing connection (easy, 'cos never opened !) + * * continue waiting to accept() + * + */ +static bgp_fsm_state_t +bgp_fsm_retry(bgp_connection connection, bgp_fsm_state_t next_state, + bgp_fsm_event_t event) +{ + bgp_connection_close(connection) ; + return bgp_fsm_start(connection, next_state, event) ; +} ; + +/*------------------------------------------------------------------------------ + * Error: + * + * + */ +static bgp_fsm_state_t +bgp_fsm_error(bgp_connection connection, bgp_fsm_state_t next_state, + bgp_fsm_event_t event) +{ + /* Double start timer. */ + peer->v_start *= 2; - /* set last reset time */ - peer->resettime = time (NULL); - /* Reset uptime. */ - bgp_uptime_reset (peer); + /* Overflow check. */ + if (peer->v_start >= (60 * 2)) + peer->v_start = (60 * 2); -#ifdef HAVE_SNMP - bgpTrapBackwardTransition (peer); -#endif /* HAVE_SNMP */ + return bgp_stop(peer, next_state); +} ; - /* Reset uptime. */ - bgp_uptime_reset (peer); +/*------------------------------------------------------------------------------ + * Hold timer expire. This is error of BGP connection. So cut the + * peer and change to Idle status. + */ +static bgp_fsm_state_t +bgp_fsm_expire(bgp_connection connection, bgp_fsm_state_t next_state, + bgp_fsm_event_t event) +{ + BGP_FSM_DEBUG(connection, "Hold timer expire") ; - /* Reset peer synctime */ - peer->synctime = 0; - } + /* Send notify to remote peer. */ + bgp_notify_send (peer, BGP_NOTIFY_HOLD_ERR, 0); - /* Stop read and write threads when exists. */ - BGP_READ_OFF (peer->t_read); - BGP_WRITE_OFF (peer->t_write); - - /* Stop all timers. */ - BGP_TIMER_OFF (peer->t_start); - BGP_TIMER_OFF (peer->t_connect); - BGP_TIMER_OFF (peer->t_holdtime); - BGP_TIMER_OFF (peer->t_keepalive); - BGP_TIMER_OFF (peer->t_asorig); - BGP_TIMER_OFF (peer->t_routeadv); - - /* Stream reset. */ - peer->packet_size = 0; - - /* Clear input and output buffer. */ - if (peer->ibuf) - stream_reset (peer->ibuf); - if (peer->work) - stream_reset (peer->work); - if (peer->obuf) - stream_fifo_clean (peer->obuf); - - /* Close of file descriptor. */ - if (peer->fd >= 0) + /* Sweep if it is temporary peer. */ + if (CHECK_FLAG (peer->sflags, PEER_STATUS_ACCEPT_PEER)) { - close (peer->fd); - peer->fd = -1; + zlog_info ("%s [Event] Accepting BGP peer is deleted", peer->host); + peer_delete (peer); + return -1; } - for (afi = AFI_IP ; afi < AFI_MAX ; afi++) - for (safi = SAFI_UNICAST ; safi < SAFI_MAX ; safi++) - { - /* Reset all negotiated variables */ - peer->afc_nego[afi][safi] = 0; - peer->afc_adv[afi][safi] = 0; - peer->afc_recv[afi][safi] = 0; - - /* peer address family capability flags*/ - peer->af_cap[afi][safi] = 0; + /* bgp_stop needs to be invoked while in Established state */ + return bgp_stop(peer, next_state) ; +} ; + +/*------------------------------------------------------------------------------ + * Received an acceptable Open Message + * + * The next state is OpenConfirm. + * + * However: this is where we do Collision Resolution. + * + * If the sibling connection has reached OpenConfirm before this one, then now + * this one either closes its sibling, or itself. + * + * As soon as a connection reaches Established, it immediately kills off any + * sibling -- so the farthest two connections can get is to OpenSent. + * + * The connection that is closed should send a Cease/Collision Resolution + * NOTIFICATION. The other end should do likewise. + * + * The connection that is closed is not stopped. It falls + * + * Immediately respond with a keepalive...... + * + */ +static bgp_fsm_state_t +bgp_fsm_opened(bgp_connection connection, bgp_fsm_state_t next_state, + bgp_fsm_event_t event) +{ + bgp_send_keepalive(connection) ; - /* peer address family status flags*/ - peer->af_sflags[afi][safi] = 0; + return next_state ; +} - /* Received ORF prefix-filter */ - peer->orf_plist[afi][safi] = NULL; +/*------------------------------------------------------------------------------ + * Status goes to Established. + * + * TODO: do we need to send a KEEPALIVE on entry to established ? + * + * + * On transition + */ +static bgp_fsm_state_t +bgp_fsm_establish(bgp_connection connection, bgp_fsm_state_t next_state, + bgp_fsm_event_t event) +{ + bgp_session session = connection->session ; + bgp_connection sibling = bgp_fsm_get_sibling(connection) ; - /* ORF received prefix-filter pnt */ - sprintf (orf_name, "%s.%d.%d", peer->host, afi, safi); - prefix_bgp_orf_remove_all (orf_name); - } + assert(session != NULL) ; - /* Reset keepalive and holdtime */ - if (CHECK_FLAG (peer->config, PEER_CONFIG_TIMER)) - { - peer->v_keepalive = peer->keepalive; - peer->v_holdtime = peer->holdtime; - } - else + /* The first thing to do is to kill off any sibling and establish + * self as the primary connection. + */ + if (sibling != NULL) { - peer->v_keepalive = peer->bgp->default_keepalive; - peer->v_holdtime = peer->bgp->default_holdtime; - } - - peer->update_time = 0; - - /* Until we are sure that there is no problem about prefix count - this should be commented out.*/ -#if 0 - /* Reset prefix count */ - peer->pcount[AFI_IP][SAFI_UNICAST] = 0; - peer->pcount[AFI_IP][SAFI_MULTICAST] = 0; - peer->pcount[AFI_IP][SAFI_MPLS_VPN] = 0; - peer->pcount[AFI_IP6][SAFI_UNICAST] = 0; - peer->pcount[AFI_IP6][SAFI_MULTICAST] = 0; -#endif /* 0 */ + bgp_fsm_stop_connection(sibling, bgp_stopped_collision, + bgp_notify_new(BGP_NOMC_CEASE, BGP_NOMS_C_COLLISION, 0)) ; + + if (connection->ordinal != bgp_connection_primary) + { + connection->ordinal = bgp_connection_primary ; + session->connections[bgp_connection_primary] = connection ; + session->connections[bgp_connection_secondary] = NULL ; + } ; + } ; + + /* Whatever else happens, will no longer accept connections. */ + /* TODO: now would be a good time to withdraw the password from listener ? */ + session->accept = 0 ; + + /* Set the session state -- tell the Routeing Engine the news */ + bgp_session_set_state(session, bgp_session_Established) ; + + return next_state ; +} ; + +/*------------------------------------------------------------------------------ + * Keepalive send to peer. + */ +static bgp_fsm_state_t +bgp_fsm_kal_send(bgp_connection connection, bgp_fsm_state_t next_state, + bgp_fsm_event_t event) +{ + bgp_keepalive_send(connection) ; + return next_state ; +} ; + +/*------------------------------------------------------------------------------ + * Keepalive packet is received. + */ +static bgp_fsm_state_t +bgp_fsm_kal_recv(bgp_connection connection, bgp_fsm_state_t next_state, + bgp_fsm_event_t event) +{ + /* peer count update */ + peer->keepalive_in++; - return 0; + bgp_hold_timer_recharge(connection) ; + return next_state ; } -/* BGP peer is stoped by the error. */ -static int -bgp_stop_with_error (struct peer *peer) +/*------------------------------------------------------------------------------ + * Update packet is received. + */ +static bgp_fsm_state_t +bgp_fsm_update(bgp_connection connection, bgp_fsm_state_t next_state, + bgp_fsm_event_t event) { - /* Double start timer. */ - peer->v_start *= 2; + bgp_hold_timer_recharge(connection) ; + return next_state ; +} - /* Overflow check. */ - if (peer->v_start >= (60 * 2)) - peer->v_start = (60 * 2); +/*------------------------------------------------------------------------------ + * We're done with this connection. + * + * May have been waiting to get a NOTIFICATION message away, and that has + * either succeeded or timed out. + * + * Shut the socket and tear down the connection. + */ +static bgp_fsm_state_t +bgp_fsm_done(bgp_connection connection, bgp_fsm_state_t next_state, + bgp_fsm_event_t event) +{ + bgp_tear_down(connection); + return next_state ; +} - bgp_stop (peer); +/*============================================================================== + * The FSM stopping management. + * + * There are many ways in which a connection may be required to stop. + * + * The stopping of one connection may mean that the entire session should be + * stopped -- either because there is only one connection (eg when in + * Established state), or because this is an administrative stop, or because + * there has been a serious error, or because a notification received, or ... + * + * + * + */ - return 0; -} -/* TCP connection open. Next we send open message to remote peer. And - add read thread for reading open message. */ -static int -bgp_connect_success (struct peer *peer) +static void +bgp_fsm_set_stopping(bgp_connection connection, bgp_stopped_cause_t cause, + bgp_notify notification, int both) { - char buf1[BUFSIZ]; + bgp_session session ; + bgp_connection sibling ; - if (peer->fd < 0) - { - zlog_err ("bgp_connect_success peer's fd is negative value %d", - peer->fd); - return -1; - } - BGP_READ_ON (peer->t_read, bgp_read, peer->fd); + /* If not connected to session, then already stopping and can do no more. */ + session = connection->session ; + if (session == NULL) + return ; - if (! CHECK_FLAG (peer->sflags, PEER_STATUS_ACCEPT_PEER)) - bgp_getsockname (peer); + BGP_CONNECTION_SESSION_LOCK(connection) ; /*<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ - if (BGP_DEBUG (normal, NORMAL)) - { - if (! CHECK_FLAG (peer->sflags, PEER_STATUS_ACCEPT_PEER)) - zlog_debug ("%s open active, local address %s", peer->host, - sockunion2str (peer->su_local, buf1, SU_ADDRSTRLEN)); - else - zlog_debug ("%s passive open", peer->host); - } + /* Get "sibling" and set stopping same as us, if required. */ + /* Won't be there if one-time sibling is already stopping/stopped. */ + if (both && ((sibling = bgp_fsm_get_sibling(connection)) != NULL)) + bgp_fsm_stop_connection(sibling, cause, bgp_notify_dup(notification)) ; - if (! CHECK_FLAG (peer->sflags, PEER_STATUS_ACCEPT_PEER)) - bgp_open_send (peer); + /* If have been passed a notification, then that should be sent to */ + /* the other end, and reported with the session stop cause. */ - return 0; -} + connection->notification_pending = (notification != NULL) ; -/* TCP connect fail */ -static int -bgp_connect_fail (struct peer *peer) -{ - bgp_stop (peer); - return 0; -} + if (connection->notification_pending) + bgp_notify_set(&connection->notification, notification) ; -/* This function is the first starting point of all BGP connection. It - try to connect to remote peer with non-blocking IO. */ -int -bgp_start (struct peer *peer) -{ - int status; + /* Set the session stopping cause and copy the notification there */ - if (BGP_PEER_START_SUPPRESSED (peer)) - { - if (BGP_DEBUG (fsm, FSM)) - plog_err (peer->log, "%s [FSM] Trying to start suppressed peer" - " - this is never supposed to happen!", peer->host); - return -1; - } + session->stopped = cause ; + bgp_notify_set_dup(&session->notification, connection->notification) ; + + BGP_CONNECTION_SESSION_UNLOCK(connection) ; /*>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ +} ; - /* Scrub some information that might be left over from a previous, - * session - */ - /* Connection information. */ - if (peer->su_local) - { - sockunion_free (peer->su_local); - peer->su_local = NULL; - } - if (peer->su_remote) - { - sockunion_free (peer->su_remote); - peer->su_remote = NULL; - } - /* Clear remote router-id. */ - peer->remote_id.s_addr = 0; - /* Clear peer capability flag. */ - peer->cap = 0; - - /* If the peer is passive mode, force to move to Active mode. */ - if (CHECK_FLAG (peer->flags, PEER_FLAG_PASSIVE)) - { - BGP_EVENT_ADD (peer, TCP_connection_open_failed); - return 0; - } - status = bgp_connect (peer); +static bgp_connection +bgp_fsm_get_sibling(bgp_connection connection) +{ + bgp_session session = connection->session ; + + if (session == NULL) + return NULL ; /* no sibling if no session */ + + confirm(bgp_connection_primary == (bgp_connection_secondary ^ 1)) ; + confirm(bgp_connection_secondary == (bgp_connection_primary ^ 1)) ; + + return session->connections[connection->ordinal ^ 1] ; +} ; + + + + +/*============================================================================== + * The BGP connections timers handling. + * + * The FSM has four timers: + * + * * IdleHoldTimer -- uses connection.hold_timer with jitter + * + * This runs while in Idle state, and is a period in which no connections + * are started, and none will be accepted. + * + * The purpose of this timer is to slow down re-making connections with + * peers who are flapping or otherwise proving a nuisance. + * + * This is a one shot timer, which generates a bgp_fsm_BGP_Start event. + * + * * ConnectRetryTimer -- uses connection.hold_timer with jitter + * + * This runs while in Connect or Active state, and is the period for which + * the connection is prepared to wait between attempts to connect. + * + * When trying to make a connect connection: + * + * The FSM will be in Connect state. + * + * If listen connections are enabled, will be listening. + * + * If nothing happens before the ConnectRetryTimer expires, then + * the connection attempt will be abandoned, and another started. + * + * If the connection attempt fails, moves to Active state -- with the + * timer still running. + * + * If nothing further happens before the ConnectRetryTimer expires, + * another connect will be started and the FSM returns to Connect state. + * + * When only listening is enabled: + * + * The FSM will be in Active state (!). + * + * If nothing happens before the ConnectRetryTimer expires, then the + * FSM will loop round back into Active state. + * + * This timer is recharged each time it goes off, and generates a + * bgp_fsm_ConnectRetry_timer_expired event. + * + * * HoldTimer -- uses connection.hold_timer *without* jitter + * + * This timer is used in OpenSent state, and limits the time will wait for + * an Open to appear from the other end. RFC4271 calls for this to be a + * "large value" -- suggesting 240 seconds. + * + * This timer is also used in OpenConfirm and Established states, and limits + * the time the connection will be held if hear nothing from the other end. + * In these states the timer is set to the negotiated HoldTime. If this is + * zero, then the HoldTime is infinite. + * + * This is a one shot timer, which generates a bgp_fsm_Hold_Timer_expired + * event. + * + * * KeepaliveTimer -- uses connection.keepalive_timer with jitter. + * + * This timer is used in OpenConfirm and Established states only. + * + * The default KeepalineTimer is 1/3 the HoldTimer, and is set from the + * negotiated HoldTime. If that is zero, then the KeepaliveTime is also + * infinite, and no KEEPALIVE messages will be sent (other than the "ack" + * of the OPEN message). + * + * This timer is recharged each time it goes off, and generates a + * bgp_fsm_KeepAlive_timer_expired event. + */ + +/* Forward reference */ +static inline void +bgp_timer_set(bgp_connection connection, qtimer timer, unsigned secs, + int jitter, qtimer_action* action) ; + +/* Forward reference the action functions */ +static qtimer_action bgp_idle_hold_timer_action ; +static qtimer_action bgp_connect_retry_timer_action ; +static qtimer_action bgp_hold_timer_action ; +static qtimer_action bgp_keepalive_timer_action ; + +/*============================================================================== + * Completion of State Change + * + * This performs fixed changes associated with the entry to each state from + * *another* state. + * + * Set and unset all the connection timers as required by the new state of + * the connection. + * + * + * + * NB: requires the session to be LOCKED. + */ +static void +bgp_fsm_state_change(bgp_connection connection, bgp_fsm_state_t new_state) +{ + bgp_session session = connection->session ; - switch (status) + switch (new_state) { - case connect_error: - if (BGP_DEBUG (fsm, FSM)) - plog_debug (peer->log, "%s [FSM] Connect error", peer->host); - BGP_EVENT_ADD (peer, TCP_connection_open_failed); - break; - case connect_success: - if (BGP_DEBUG (fsm, FSM)) - plog_debug (peer->log, "%s [FSM] Connect immediately success", - peer->host); - BGP_EVENT_ADD (peer, TCP_connection_open); - break; - case connect_in_progress: - /* To check nonblocking connect, we wait until socket is - readable or writable. */ - if (BGP_DEBUG (fsm, FSM)) - plog_debug (peer->log, "%s [FSM] Non blocking connect waiting result", - peer->host); - if (peer->fd < 0) - { - zlog_err ("bgp_start peer's fd is negative value %d", - peer->fd); - return -1; - } - BGP_READ_ON (peer->t_read, bgp_read, peer->fd); - BGP_WRITE_ON (peer->t_write, bgp_write, peer->fd); + /* Base state of connection's finite state machine -- when a session has + * been enabled. Falls back to Idle in the event of various errors. + * + * In Idle state the IdleHoldTimer is running, at the end of which the + * BGP Engine will try to connect. + * + * Note that don't allow a zero (ie infinite) IdleHoldTimer -- by forcing + * a 1 second minimum time. As a side effect we make the time odd just + * before we jitter it. + * + * In Idle state refuses connections. + */ + case bgp_fsm_Idle: + bgp_timer_set(connection, &connection->hold_timer, + (session->idle_hold_timer_interval | 1), 1, + bgp_idle_hold_timer_action) ; + qtimer_unset(&connection->keepalive_timer) ; + break; - } - return 0; -} -/* Connect retry timer is expired when the peer status is Connect. */ -static int -bgp_reconnect (struct peer *peer) -{ - bgp_stop (peer); - bgp_start (peer); - return 0; -} + /* In Connect state the BGP Engine is attempting to make a connection + * with the peer and may be listening for a connection. + * + * In Active state the BGP Engine is only listening (!). + * + * In both cases, waits for the connect_hold_timer_interval. + * + * The ConnectRetryTimer automatically recharges, because will loop back + * round into the same state. + */ + case bgp_fsm_Connect: + case bgp_fsm_Active: + bgp_timer_set(connection, &connection->hold_timer, + session->connect_retry_timer_interval, 1, + bgp_connect_retry_timer_action) ; + qtimer_unset(&connection->keepalive_timer) ; + break; -static int -bgp_fsm_open (struct peer *peer) -{ - /* Send keepalive and make keepalive timer */ - bgp_keepalive_send (peer); + /* In OpenSent state is waiting for an OPEN from the other end, before + * proceeding to OpenConfirm state. + * + * Prepared to wait for quite a long time for this. + * + * Note that session->accept is left as it is. If have reached OpenSent + * on: + * + * * a connect() connection, then session->accept will be true and will + * still accept in-bound connections. + * + * * an accept() connection, then session->accept will be false. + */ + case bgp_fsm_OpenSent: + bgp_timer_set(connection, &connection->hold_timer, + session->open_hold_timer_interval, 0, + bgp_hold_timer_action) ; + qtimer_unset(&connection->keepalive_timer) ; + break; - /* Reset holdtimer value. */ - BGP_TIMER_OFF (peer->t_holdtime); + /* In OpenConfirm state is waiting for an "ack" before proceeding to + * Established. Session->accept is left as it is. If have reached + * OpenConfirm on: + * + * * a connect() connection, then session->accept may still be true and + * will still accept in-bound connections. (Collision detection may + * have discarded an accept() connection already.) + * + * * an accept() connection, then session->accept will be false. + * + * There is only one way into Established, and that is from OpenConfirm. + * OpenConfirm starts the KeepaliveTimer. It would be wrong to reset the + * timer on entry to Established. + * + * In both cases have just received a message, so can restart the HoldTimer. + * + * Both use the negotiated Hold Time and Keepalive Time. May send further + * KEEPALIVE messages in OpenConfirm. + * + * If the negotiated Hold Time value is zero, then the Keepalive Time + * value will also be zero, and this will unset both timers. + */ + case bgp_fsm_OpenConfirm: + bgp_timer_set(connection, &connection->keepalive_timer, + connection->keepalive_timer_interval, 1, + bgp_keepalive_timer_action) ; + case bgp_fsm_Established: + bgp_timer_set(connection, &connection->hold_timer, + connection->hold_timer_interval, 0, + bgp_hold_timer_action) ; + break; - return 0; -} + /* The connection is coming to an dead stop. + * + */ -/* Keepalive send to peer. */ -static int -bgp_fsm_keepalive_expire (struct peer *peer) -{ - bgp_keepalive_send (peer); - return 0; -} + case bgp_fsm_Stopping: + if (connection->notification_pending) + bgp_timer_set(connection, &connection->hold_timer, + 60, 1, bgp_hold_timer_action) ; + else + qtimer_unset(&connection->hold_timer) ; -/* Hold timer expire. This is error of BGP connection. So cut the - peer and change to Idle status. */ -static int -bgp_fsm_holdtime_expire (struct peer *peer) -{ - if (BGP_DEBUG (fsm, FSM)) - zlog (peer->log, LOG_DEBUG, "%s [FSM] Hold timer expire", peer->host); + qtimer_unset(&connection->keepalive_timer) ; - /* Send notify to remote peer. */ - bgp_notify_send (peer, BGP_NOTIFY_HOLD_ERR, 0); + break ; - /* Sweep if it is temporary peer. */ - if (CHECK_FLAG (peer->sflags, PEER_STATUS_ACCEPT_PEER)) - { - zlog_info ("%s [Event] Accepting BGP peer is deleted", peer->host); - peer_delete (peer); - return -1; - } + default: + zabort("Unknown bgp_fsm_state") ; + } ; - /* bgp_stop needs to be invoked while in Established state */ - bgp_stop(peer); + /* Finally: set the new state */ + connection->state = new_state ; +} ; - return 0; -} +/*============================================================================== + * Timer set and Timer Action Functions + */ -/* Status goes to Established. Send keepalive packet then make first - update information. */ -static int -bgp_establish (struct peer *peer) +/*------------------------------------------------------------------------------ + * Start or reset given qtimer with given interval, in seconds. + * + * If the interval is zero, unset the timer. + */ +static inline void +bgp_timer_set(bgp_connection connection, qtimer timer, unsigned secs, + int jitter, qtimer_action* action) { - struct bgp_notify *notify; - afi_t afi; - safi_t safi; - int nsf_af_count = 0; - - /* Reset capability open status flag. */ - if (! CHECK_FLAG (peer->sflags, PEER_STATUS_CAPABILITY_OPEN)) - SET_FLAG (peer->sflags, PEER_STATUS_CAPABILITY_OPEN); - - /* Clear last notification data. */ - notify = &peer->notify; - if (notify->data) - XFREE (MTYPE_TMP, notify->data); - memset (notify, 0, sizeof (struct bgp_notify)); - - /* Clear start timer value to default. */ - peer->v_start = BGP_INIT_START_TIMER; - - /* Increment established count. */ - peer->established++; - bgp_fsm_change_status (peer, Established); - - /* bgp log-neighbor-changes of neighbor Up */ - if (bgp_flag_check (peer->bgp, BGP_FLAG_LOG_NEIGHBOR_CHANGES)) - zlog_info ("%%ADJCHANGE: neighbor %s Up", peer->host); - - /* graceful restart */ - UNSET_FLAG (peer->sflags, PEER_STATUS_NSF_WAIT); - for (afi = AFI_IP ; afi < AFI_MAX ; afi++) - for (safi = SAFI_UNICAST ; safi < SAFI_UNICAST_MULTICAST ; safi++) - { - if (peer->afc_nego[afi][safi] - && CHECK_FLAG (peer->cap, PEER_CAP_RESTART_ADV) - && CHECK_FLAG (peer->af_cap[afi][safi], PEER_CAP_RESTART_AF_RCV)) - { - if (peer->nsf[afi][safi] - && ! CHECK_FLAG (peer->af_cap[afi][safi], PEER_CAP_RESTART_AF_PRESERVE_RCV)) - bgp_clear_stale_route (peer, afi, safi); - - peer->nsf[afi][safi] = 1; - nsf_af_count++; - } - else - { - if (peer->nsf[afi][safi]) - bgp_clear_stale_route (peer, afi, safi); - peer->nsf[afi][safi] = 0; - } - } - - if (nsf_af_count) - SET_FLAG (peer->sflags, PEER_STATUS_NSF_MODE); + if (secs == 0) + qtimer_unset(timer) ; else { - UNSET_FLAG (peer->sflags, PEER_STATUS_NSF_MODE); - if (peer->t_gr_stale) - { - BGP_TIMER_OFF (peer->t_gr_stale); - if (BGP_DEBUG (events, EVENTS)) - zlog_debug ("%s graceful restart stalepath timer stopped", peer->host); - } - } - - if (peer->t_gr_restart) - { - BGP_TIMER_OFF (peer->t_gr_restart); - if (BGP_DEBUG (events, EVENTS)) - zlog_debug ("%s graceful restart timer stopped", peer->host); - } + if (jitter) + secs -= ((rand() % ((int)secs + 1)) / 4) ; + qtimer_set_interval(timer, QTIME(secs), action) ; + } ; +} ; + +/*------------------------------------------------------------------------------ + * BGP start timer action => bgp_fsm_BGP_Start event + * + * The timer is automatically unset, which is fine. + */ +static void +bgp_idle_hold_timer_action(qtimer qtr, void* timer_info, qtime_mono_t when) +{ + bgp_connection connection = timer_info ; -#ifdef HAVE_SNMP - bgpTrapEstablished (peer); -#endif /* HAVE_SNMP */ + BGP_FSM_DEBUG(connection, "Timer (start timer expire)") ; - /* Reset uptime, send keepalive, send current table. */ - bgp_uptime_reset (peer); - - /* Send route-refresh when ORF is enabled */ - for (afi = AFI_IP ; afi < AFI_MAX ; afi++) - for (safi = SAFI_UNICAST ; safi < SAFI_MAX ; safi++) - if (CHECK_FLAG (peer->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_SM_ADV)) - { - if (CHECK_FLAG (peer->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_RM_RCV)) - bgp_route_refresh_send (peer, afi, safi, ORF_TYPE_PREFIX, - REFRESH_IMMEDIATE, 0); - else if (CHECK_FLAG (peer->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_RM_OLD_RCV)) - bgp_route_refresh_send (peer, afi, safi, ORF_TYPE_PREFIX_OLD, - REFRESH_IMMEDIATE, 0); - } - - if (peer->v_keepalive) - bgp_keepalive_send (peer); - - /* First update is deferred until ORF or ROUTE-REFRESH is received */ - for (afi = AFI_IP ; afi < AFI_MAX ; afi++) - for (safi = SAFI_UNICAST ; safi < SAFI_MAX ; safi++) - if (CHECK_FLAG (peer->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_RM_ADV)) - if (CHECK_FLAG (peer->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_SM_RCV) - || CHECK_FLAG (peer->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_SM_OLD_RCV)) - SET_FLAG (peer->af_sflags[afi][safi], PEER_STATUS_ORF_WAIT_REFRESH); - - bgp_announce_route_all (peer); - - BGP_TIMER_ON (peer->t_routeadv, bgp_routeadv_timer, 1); - - return 0; -} + bgp_fsm_event(connection, bgp_fsm_BGP_Start) ; +} ; -/* Keepalive packet is received. */ -static int -bgp_fsm_keepalive (struct peer *peer) +/*------------------------------------------------------------------------------ + * BGP connect retry timer => bgp_fsm_ConnectRetry_timer_expired event + * + * The timer is recharged here, applying a new "jitter", but that may be + * overridden by the bgp_event() handling. + */ +static void +bgp_connect_retry_timer_action(qtimer qtr, void* timer_info, qtime_mono_t when) { - /* peer count update */ - peer->keepalive_in++; + bgp_connection connection = timer_info ; - BGP_TIMER_OFF (peer->t_holdtime); - return 0; -} + BGP_FSM_DEBUG(connection, "Timer (connect timer expire)") ; -/* Update packet is received. */ -static int -bgp_fsm_update (struct peer *peer) -{ - BGP_TIMER_OFF (peer->t_holdtime); - return 0; -} + bgp_timer_set(connection, &connection->hold_timer, + connection->session->connect_retry_timer_interval, 1, NULL) ; -/* This is empty event. */ -static int -bgp_ignore (struct peer *peer) -{ - if (BGP_DEBUG (fsm, FSM)) - zlog (peer->log, LOG_DEBUG, "%s [FSM] bgp_ignore called", peer->host); - return 0; -} - -/* Finite State Machine structure */ -static const struct { - int (*func) (struct peer *); - int next_state; -} FSM [BGP_STATUS_MAX - 1][BGP_EVENTS_MAX - 1] = -{ - { - /* Idle state: In Idle state, all events other than BGP_Start is - ignored. With BGP_Start event, finite state machine calls - bgp_start(). */ - {bgp_start, Connect}, /* BGP_Start */ - {bgp_stop, Idle}, /* BGP_Stop */ - {bgp_stop, Idle}, /* TCP_connection_open */ - {bgp_stop, Idle}, /* TCP_connection_closed */ - {bgp_ignore, Idle}, /* TCP_connection_open_failed */ - {bgp_stop, Idle}, /* TCP_fatal_error */ - {bgp_ignore, Idle}, /* ConnectRetry_timer_expired */ - {bgp_ignore, Idle}, /* Hold_Timer_expired */ - {bgp_ignore, Idle}, /* KeepAlive_timer_expired */ - {bgp_ignore, Idle}, /* Receive_OPEN_message */ - {bgp_ignore, Idle}, /* Receive_KEEPALIVE_message */ - {bgp_ignore, Idle}, /* Receive_UPDATE_message */ - {bgp_ignore, Idle}, /* Receive_NOTIFICATION_message */ - {bgp_ignore, Idle}, /* Clearing_Completed */ - }, - { - /* Connect */ - {bgp_ignore, Connect}, /* BGP_Start */ - {bgp_stop, Idle}, /* BGP_Stop */ - {bgp_connect_success, OpenSent}, /* TCP_connection_open */ - {bgp_stop, Idle}, /* TCP_connection_closed */ - {bgp_connect_fail, Active}, /* TCP_connection_open_failed */ - {bgp_connect_fail, Idle}, /* TCP_fatal_error */ - {bgp_reconnect, Connect}, /* ConnectRetry_timer_expired */ - {bgp_ignore, Idle}, /* Hold_Timer_expired */ - {bgp_ignore, Idle}, /* KeepAlive_timer_expired */ - {bgp_ignore, Idle}, /* Receive_OPEN_message */ - {bgp_ignore, Idle}, /* Receive_KEEPALIVE_message */ - {bgp_ignore, Idle}, /* Receive_UPDATE_message */ - {bgp_stop, Idle}, /* Receive_NOTIFICATION_message */ - {bgp_ignore, Idle}, /* Clearing_Completed */ - }, - { - /* Active, */ - {bgp_ignore, Active}, /* BGP_Start */ - {bgp_stop, Idle}, /* BGP_Stop */ - {bgp_connect_success, OpenSent}, /* TCP_connection_open */ - {bgp_stop, Idle}, /* TCP_connection_closed */ - {bgp_ignore, Active}, /* TCP_connection_open_failed */ - {bgp_ignore, Idle}, /* TCP_fatal_error */ - {bgp_start, Connect}, /* ConnectRetry_timer_expired */ - {bgp_ignore, Idle}, /* Hold_Timer_expired */ - {bgp_ignore, Idle}, /* KeepAlive_timer_expired */ - {bgp_ignore, Idle}, /* Receive_OPEN_message */ - {bgp_ignore, Idle}, /* Receive_KEEPALIVE_message */ - {bgp_ignore, Idle}, /* Receive_UPDATE_message */ - {bgp_stop_with_error, Idle}, /* Receive_NOTIFICATION_message */ - {bgp_ignore, Idle}, /* Clearing_Completed */ - }, - { - /* OpenSent, */ - {bgp_ignore, OpenSent}, /* BGP_Start */ - {bgp_stop, Idle}, /* BGP_Stop */ - {bgp_stop, Active}, /* TCP_connection_open */ - {bgp_stop, Active}, /* TCP_connection_closed */ - {bgp_stop, Active}, /* TCP_connection_open_failed */ - {bgp_stop, Active}, /* TCP_fatal_error */ - {bgp_ignore, Idle}, /* ConnectRetry_timer_expired */ - {bgp_fsm_holdtime_expire, Idle}, /* Hold_Timer_expired */ - {bgp_ignore, Idle}, /* KeepAlive_timer_expired */ - {bgp_fsm_open, OpenConfirm}, /* Receive_OPEN_message */ - {bgp_ignore, Idle}, /* Receive_KEEPALIVE_message */ - {bgp_ignore, Idle}, /* Receive_UPDATE_message */ - {bgp_stop_with_error, Idle}, /* Receive_NOTIFICATION_message */ - {bgp_ignore, Idle}, /* Clearing_Completed */ - }, - { - /* OpenConfirm, */ - {bgp_ignore, OpenConfirm}, /* BGP_Start */ - {bgp_stop, Idle}, /* BGP_Stop */ - {bgp_stop, Idle}, /* TCP_connection_open */ - {bgp_stop, Idle}, /* TCP_connection_closed */ - {bgp_stop, Idle}, /* TCP_connection_open_failed */ - {bgp_stop, Idle}, /* TCP_fatal_error */ - {bgp_ignore, Idle}, /* ConnectRetry_timer_expired */ - {bgp_fsm_holdtime_expire, Idle}, /* Hold_Timer_expired */ - {bgp_ignore, OpenConfirm}, /* KeepAlive_timer_expired */ - {bgp_ignore, Idle}, /* Receive_OPEN_message */ - {bgp_establish, Established}, /* Receive_KEEPALIVE_message */ - {bgp_ignore, Idle}, /* Receive_UPDATE_message */ - {bgp_stop_with_error, Idle}, /* Receive_NOTIFICATION_message */ - {bgp_ignore, Idle}, /* Clearing_Completed */ - }, - { - /* Established, */ - {bgp_ignore, Established}, /* BGP_Start */ - {bgp_stop, Clearing}, /* BGP_Stop */ - {bgp_stop, Clearing}, /* TCP_connection_open */ - {bgp_stop, Clearing}, /* TCP_connection_closed */ - {bgp_stop, Clearing}, /* TCP_connection_open_failed */ - {bgp_stop, Clearing}, /* TCP_fatal_error */ - {bgp_stop, Clearing}, /* ConnectRetry_timer_expired */ - {bgp_fsm_holdtime_expire, Clearing}, /* Hold_Timer_expired */ - {bgp_fsm_keepalive_expire, Established}, /* KeepAlive_timer_expired */ - {bgp_stop, Clearing}, /* Receive_OPEN_message */ - {bgp_fsm_keepalive, Established}, /* Receive_KEEPALIVE_message */ - {bgp_fsm_update, Established}, /* Receive_UPDATE_message */ - {bgp_stop_with_error, Clearing}, /* Receive_NOTIFICATION_message */ - {bgp_ignore, Idle}, /* Clearing_Completed */ - }, - { - /* Clearing, */ - {bgp_ignore, Clearing}, /* BGP_Start */ - {bgp_stop, Clearing}, /* BGP_Stop */ - {bgp_stop, Clearing}, /* TCP_connection_open */ - {bgp_stop, Clearing}, /* TCP_connection_closed */ - {bgp_stop, Clearing}, /* TCP_connection_open_failed */ - {bgp_stop, Clearing}, /* TCP_fatal_error */ - {bgp_stop, Clearing}, /* ConnectRetry_timer_expired */ - {bgp_stop, Clearing}, /* Hold_Timer_expired */ - {bgp_stop, Clearing}, /* KeepAlive_timer_expired */ - {bgp_stop, Clearing}, /* Receive_OPEN_message */ - {bgp_stop, Clearing}, /* Receive_KEEPALIVE_message */ - {bgp_stop, Clearing}, /* Receive_UPDATE_message */ - {bgp_stop, Clearing}, /* Receive_NOTIFICATION_message */ - {bgp_clearing_completed, Idle}, /* Clearing_Completed */ - }, - { - /* Deleted, */ - {bgp_ignore, Deleted}, /* BGP_Start */ - {bgp_ignore, Deleted}, /* BGP_Stop */ - {bgp_ignore, Deleted}, /* TCP_connection_open */ - {bgp_ignore, Deleted}, /* TCP_connection_closed */ - {bgp_ignore, Deleted}, /* TCP_connection_open_failed */ - {bgp_ignore, Deleted}, /* TCP_fatal_error */ - {bgp_ignore, Deleted}, /* ConnectRetry_timer_expired */ - {bgp_ignore, Deleted}, /* Hold_Timer_expired */ - {bgp_ignore, Deleted}, /* KeepAlive_timer_expired */ - {bgp_ignore, Deleted}, /* Receive_OPEN_message */ - {bgp_ignore, Deleted}, /* Receive_KEEPALIVE_message */ - {bgp_ignore, Deleted}, /* Receive_UPDATE_message */ - {bgp_ignore, Deleted}, /* Receive_NOTIFICATION_message */ - {bgp_ignore, Deleted}, /* Clearing_Completed */ - }, -}; + bgp_fsm_event(connection, bgp_fsm_ConnectRetry_timer_expired) ; +} ; -static const char *bgp_event_str[] = +/*------------------------------------------------------------------------------ + * BGP hold timer => bgp_fsm_Hold_Timer_expired event + * + * The timer is automatically unset, which is fine. + */ +static void +bgp_hold_timer_action(qtimer qtr, void* timer_info, qtime_mono_t when) { - NULL, - "BGP_Start", - "BGP_Stop", - "TCP_connection_open", - "TCP_connection_closed", - "TCP_connection_open_failed", - "TCP_fatal_error", - "ConnectRetry_timer_expired", - "Hold_Timer_expired", - "KeepAlive_timer_expired", - "Receive_OPEN_message", - "Receive_KEEPALIVE_message", - "Receive_UPDATE_message", - "Receive_NOTIFICATION_message", - "Clearing_Completed", -}; + bgp_connection connection = timer_info ; -/* Execute event process. */ -int -bgp_event (struct thread *thread) -{ - int ret = 0; - int event; - int next; - struct peer *peer; + BGP_FSM_DEBUG(connection, "Timer (holdtime timer expire)") ; - peer = THREAD_ARG (thread); - event = THREAD_VAL (thread); + bgp_fsm_event(connection, bgp_fsm_Hold_Timer_expired) ; +} ; - /* Logging this event. */ - next = FSM [peer->status -1][event - 1].next_state; +/*------------------------------------------------------------------------------ + * BGP keepalive fire => bgp_fsm_KeepAlive_timer_expired + * + * The timer is recharged here, applying a new "jitter", but that may be + * overridden by the bgp_event() handling. + */ +static void +bgp_keepalive_timer_action(qtimer qtr, void* timer_info, qtime_mono_t when) +{ + bgp_connection connection = timer_info ; - if (BGP_DEBUG (fsm, FSM) && peer->status != next) - plog_debug (peer->log, "%s [FSM] %s (%s->%s)", peer->host, - bgp_event_str[event], - LOOKUP (bgp_status_msg, peer->status), - LOOKUP (bgp_status_msg, next)); + BGP_FSM_DEBUG(connection, "Timer (keepalive timer expire)") ; - /* Call function. */ - if (FSM [peer->status -1][event - 1].func) - ret = (*(FSM [peer->status - 1][event - 1].func))(peer); + bgp_timer_set(connection, &connection->keepalive_timer, + connection->session->keepalive_timer_interval, 1, NULL) ; - /* When function do not want proceed next job return -1. */ - if (ret >= 0) - { - /* If status is changed. */ - if (next != peer->status) - bgp_fsm_change_status (peer, next); - - /* Make sure timer is set. */ - bgp_timer_set (peer); - } - - return ret; -} + bgp_fsm_event(connection, bgp_fsm_KeepAlive_timer_expired) ; +} ; + +/*============================================================================*/ +/* BGP Peer Down Cause */ +const char *peer_down_str[] = +{ + "", + "Router ID changed", + "Remote AS changed", + "Local AS change", + "Cluster ID changed", + "Confederation identifier changed", + "Confederation peer changed", + "RR client config change", + "RS client config change", + "Update source change", + "Address family activated", + "Admin. shutdown", + "User reset", + "BGP Notification received", + "BGP Notification send", + "Peer closed the session", + "Neighbor deleted", + "Peer-group add member", + "Peer-group delete member", + "Capability changed", + "Passive config change", + "Multihop config change", + "NSF peer closed the session" +}; |