summaryrefslogtreecommitdiffstats
path: root/bgpd/bgp_fsm.c
diff options
context:
space:
mode:
Diffstat (limited to 'bgpd/bgp_fsm.c')
-rw-r--r--bgpd/bgp_fsm.c2870
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"
+};