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.c353
1 files changed, 204 insertions, 149 deletions
diff --git a/bgpd/bgp_fsm.c b/bgpd/bgp_fsm.c
index f2b89576..bd2ef82a 100644
--- a/bgpd/bgp_fsm.c
+++ b/bgpd/bgp_fsm.c
@@ -158,10 +158,7 @@
* OPEN messages from a given remote end IP address MUST MATCH !
*
*------------------------------------------------------------------------------
- * Error Handling, NOTIFICATIONs and Collision Resolution.
- *
- * I/O and other operations may fail. The other end may close an open TCP
- * connection, with or without a NOTIFICATION message.
+ * Exception Handling.
*
* The FSM proceeds in three basic phases:
*
@@ -214,9 +211,9 @@
* 2) attempting to establish a BGP session: OpenSent/OpenConfirm
*
* If something goes wrong, or the other end closes the connection (with
- * or without notification) it will loop back to Idle state. Also, when
- * collision resolution closes one connection it too loops back to Idle
- * (see above).
+ * or without notification) the FSM will loop back to Idle state. Also,
+ * when collision resolution closes one connection it too loops back to
+ * Idle (see above).
*
* Both connections may reach OpenSent. Only one at once can reach
* OpenConfirm -- collision resolution sees to that.
@@ -231,10 +228,13 @@
*
* When things do go wrong, one of the following events is generated:
*
- * a. BGP_Stop
+ * a. BGP_Stop -- general exception
+ *
+ * The function bgp_fsm_exception() sets the reason for the exception and
+ * raises an BGP_Stop event.
*
- * 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
+ * Within the FSM, bgp_fsm_set_exception() sets the reason for the
+ * exception and .....
* connections. These may be used, for example, to signal that an UPDATE
* message is invalid.
*
@@ -278,14 +278,16 @@
*
* The function bgp_fsm_io_fatal_error() will generate a TCP_fatal_error.
*
- * e.
+ * Things may also go wrong withing the FSM.
+ *
+ * The procedure for dealing with an exception
*
*------------------------------------------------------------------------------
* FSM errors
*
- * If the FSM receives an event that cannot be raised in the current state,
- * it will terminate the session, sending an FSM Error NOTIFICATION (if a
- * TCP connection is up). See bgp_fsm_invalid().
+ * Invalid events: if the FSM receives an event that cannot be raised in the
+ * current state, it will terminate the session, sending an FSM Error
+ * NOTIFICATION (if a TCP connection is up). See bgp_fsm_invalid().
*
* If the FSM receives a message type that is not expected in the current,
* state, it will close the connection (if OpenSent or OpenConfirm) or stop
@@ -345,6 +347,39 @@
*
* When the HoldTimer expires close the connection completely (whether or not
* the NOTIFICATION has cleared the write buffer).
+ *
+ *------------------------------------------------------------------------------
+ * Communication with the Routeing Engine
+ *
+ * The FSM sends the following messages to the Routeing Engine:
+ *
+ * * bgp_session_event messages
+ *
+ * These keep the Routeing Engine up to date with the progress and state of
+ * the FSM.
+ *
+ * In particular, these event messages tell the Routeing Engine when the
+ * session enters and leaves sEstablished -- which is what really matters
+ * to it !
+ *
+ * * bgp_session_update
+ *
+ * Each time an update message arrives from the peer, it is forwarded.
+ *
+ * TODO: flow control for incoming updates ??
+ *
+ * Three things bring the FSM to a dead stop, and stop the session:
+ *
+ * 1) administrative Stop -- ie the Routeing Engine disabling the session.
+ *
+ * 2) invalid events -- which are assumed to be bugs, really.
+ *
+ * 3) anything that stops the session while in Established state.
+ *
+ * This means that the FSM will plough on trying to establish connections with
+ * configured peers, even in circumstances when the likelihood of success
+ * appears slim to vanishing. However, the Routeing Engine and the operator
+ * are responsible for the decision to start and to stop trying to connect.
*/
/*==============================================================================
@@ -360,10 +395,8 @@ bgp_fsm_enable_connection(bgp_connection connection)
bgp_fsm_event(connection, bgp_fsm_BGP_Start) ;
} ;
-
-
/*=============================================================================
- * BGP_Stop Events
+ * Raising exceptions.
*
* Before generating a BGP_Stop event the cause of the stop MUST be set for
* the connection.
@@ -390,26 +423,11 @@ bgp_fsm_enable_connection(bgp_connection connection)
*/
static void
-bgp_fsm_set_stopping(bgp_connection connection, bgp_stopped_cause_t cause,
- bgp_notify notification, int both) ;
+bgp_fsm_throw_exception(bgp_connection connection, bgp_session_event_t except,
+ bgp_notify notification, int err, bgp_fsm_event_t event) ;
/*------------------------------------------------------------------------------
- * 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.
+ * Raise a general exception -- not I/O related.
*
* Note that I/O problems are signalled by bgp_fsm_io_error().
*
@@ -418,49 +436,43 @@ bgp_fsm_set_stopping(bgp_connection connection, bgp_stopped_cause_t cause,
* NB: locks and unlocks the session.
*/
extern void
-bgp_fsm_stop_connection(bgp_connection connection, bgp_stopped_cause_t cause,
+bgp_fsm_raise_exception(bgp_connection connection, bgp_session_event_t except,
bgp_notify notification)
{
- bgp_fsm_set_stopping(connection, cause, notification, 0) ;
- bgp_fsm_event(connection, bgp_fsm_BGP_Stop) ;
+ bgp_fsm_throw_exception(connection, except, notification, 0,
+ bgp_fsm_BGP_Stop) ;
} ;
/*------------------------------------------------------------------------------
- * Bring given connection to a stop, and the other connection (if any), and
- * then the session.
+ * Raise a NOTIFICATION received exception
*
- * See bgp_fsm_stop_connection, above.
+ * NB: locks and unlocks the session.
*/
extern void
-bgp_fsm_stop_session(bgp_connection connection, bgp_stopped_cause_t cause,
+bgp_fsm_notification_exception(bgp_connection connection,
bgp_notify notification)
{
- bgp_fsm_set_stopping(connection, cause, notification, 1) ;
- bgp_fsm_event(connection, bgp_fsm_BGP_Stop) ;
+ bgp_fsm_throw_exception(connection, bgp_session_eNOM_recv, notification, 0,
+ bgp_fsm_Receive_NOTIFICATION_message) ;
} ;
-/*==============================================================================
- * Functions to signal I/O error(s) and connect()/accept() completion.
- */
-
/*------------------------------------------------------------------------------
- * Signal a fatal I/O error on the given connection.
+ * Raise a "fatal I/O error" exception 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) ;
+ bgp_fsm_throw_exception(connection, bgp_session_eTCP_error, NULL, err,
+ bgp_fsm_TCP_fatal_error) ;
} ;
/*------------------------------------------------------------------------------
- * Signal an I/O error on the given connection.
+ * Raise an "I/O error" exception 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).
@@ -479,8 +491,6 @@ bgp_fsm_io_fatal_error(bgp_connection connection, int err)
extern void
bgp_fsm_io_error(bgp_connection connection, int err)
{
- connection->err = err ;
-
if ( (err == 0)
|| (err == ECONNRESET)
|| (err == ENETDOWN)
@@ -501,7 +511,8 @@ bgp_fsm_io_error(bgp_connection connection, int err)
safe_strerror(err)) ;
} ;
- bgp_fsm_event(connection, bgp_fsm_TCP_connection_closed) ;
+ bgp_fsm_throw_exception(connection, bgp_session_eTCP_dropped, NULL, err,
+ bgp_fsm_TCP_connection_closed) ;
}
else
bgp_fsm_io_fatal_error(connection, err) ;
@@ -530,8 +541,6 @@ bgp_fsm_connect_completed(bgp_connection connection, int err,
union sockunion* su_local,
union sockunion* su_remote)
{
- connection->err = err ;
-
if (err == 0)
{
bgp_fsm_event(connection, bgp_fsm_TCP_connection_open) ;
@@ -543,11 +552,35 @@ bgp_fsm_connect_completed(bgp_connection connection, int err,
|| (err == ECONNRESET)
|| (err == EHOSTUNREACH)
|| (err == ETIMEDOUT) )
- bgp_fsm_event(connection, bgp_fsm_TCP_connection_open_failed) ;
+ bgp_fsm_throw_exception(connection, bgp_session_eTCP_failed, NULL, err,
+ bgp_fsm_TCP_connection_open_failed) ;
else
bgp_fsm_io_fatal_error(connection, err) ;
} ;
+/*------------------------------------------------------------------------------
+ * Post the given exception.
+ */
+static void
+bgp_fsm_post_exception(bgp_connection connection, bgp_session_event_t except,
+ bgp_notify notification, int err)
+{
+ connection->except = except ;
+ connection->notification = notification ;
+ connection->err = err ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Post the given exception and raise the given event.
+ */
+static void
+bgp_fsm_throw_exception(bgp_connection connection, bgp_session_event_t except,
+ bgp_notify notification, int err, bgp_fsm_event_t event)
+{
+ bgp_fsm_post_exception(connection, except,notification, err) ;
+ bgp_fsm_event(connection, event) ;
+} ;
+
/*==============================================================================
* For debug...
*/
@@ -565,7 +598,8 @@ bgp_fsm_connect_completed(bgp_connection connection, int err,
typedef bgp_fsm_action(bgp_fsm_action_func) ;
-struct bgp_fsm {
+struct bgp_fsm
+{
bgp_fsm_action_func* action ;
bgp_fsm_state_t next_state ;
} ;
@@ -1413,7 +1447,16 @@ bgp_fsm_event(bgp_connection connection, bgp_fsm_event_t event)
session = connection->session ;
if (session != NULL)
- BGP_SESSION_LOCK(session) ; /*<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
+ {
+ BGP_SESSION_LOCK(session) ; /*<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
+ if (session->event != bgp_session_null_event)
+ {
+ session->event = bgp_session_null_event ;
+ session->err = 0 ;
+ if (session->notification != NULL)
+ bgp_notify_free(session->notification) ;
+ } ;
+ } ;
do
{
@@ -1459,6 +1502,11 @@ bgp_fsm_event(bgp_connection connection, bgp_fsm_event_t event)
} while (--connection->fsm_active != 0) ;
+ if (session->event != bgp_session_null_event)
+ {
+
+ }
+
if (session != NULL)
BGP_SESSION_UNLOCK(session) ; /*<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
} ;
@@ -1479,6 +1527,9 @@ bgp_fsm_get_sibling(bgp_connection connection) ;
static bgp_fsm_state_t
bgp_fsm_send_notification(bgp_connection connection, bgp_notify notification) ;
+static bgp_fsm_state_t
+bgp_fsm_catch_exception(bgp_connection connection, bgp_fsm_state_t next_state) ;
+
/*------------------------------------------------------------------------------
* Null action -- do nothing at all.
*/
@@ -1497,6 +1548,11 @@ static bgp_fsm_action(bgp_fsm_null)
*/
static bgp_fsm_action(bgp_fsm_enter)
{
+ if (connection->ordinal == bgp_connection_secondary)
+ bgp_prepare_to_accept(connection) ;
+
+ bgp_fsm_post_session_event(connection, bgp_session_eEnabled, NULL, 0) ;
+
return next_state ;
} ;
@@ -1520,41 +1576,6 @@ static bgp_fsm_action(bgp_fsm_enter)
*/
static bgp_fsm_action(bgp_fsm_stop)
{
- if (connection->stopped == bgp_stopped_not)
- return bgp_fsm_invalid(connection, next_state, event) ;
-
- /* If there is a NOTIFICATION to send, now is the time to do that.
- *
- * NB: can only be notification pending if TCP connection is up (OpenSent,
- * OpenConfirm or Established.
- *
- * If do send N
- *
- * Otherwise, close the connection.
- */
- if (connection->notification_pending)
- next_state = bgp_fsm_send_notification(connection,
- connection->notification) ;
- else
- bgp_connection_close(connection) ;
-
- /* If current state is Established, or if are stopped because of any of:
- *
- * bgp_stopped_admin -- stopped by Routeing Engine
- * bgp_stopped_loser -- other connection won race to Established state
- * bgp_stopped_invalid -- invalid event or action
- *
- * then we force transition to Stopping state.
- */
-
- if ( (connection->state == bgp_fsm_Established)
- || (connection->stopped == bgp_stopped_admin)
- || (connection->stopped == bgp_stopped_loser)
- || (connection->stopped == bgp_stopped_invalid) )
- next_state = bgp_fsm_Stopping ; /* force transition */
-
- /* Done */
-
return next_state ;
} ;
@@ -1573,10 +1594,10 @@ static bgp_fsm_action(bgp_fsm_invalid)
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) ;
+ bgp_fsm_post_exception(connection, bgp_session_eInvalid,
+ bgp_notify_new(BGP_NOMC_FSM, BGP_NOMS_UNSPECIFIC, 0), 0) ;
- return bgp_fsm_Stopping ; /* unconditionally */
+ return bgp_fsm_catch_exception(connection, next_state) ;
} ;
/*------------------------------------------------------------------------------
@@ -1610,9 +1631,11 @@ static bgp_fsm_action(bgp_fsm_start)
else
{
next_state = bgp_fsm_Active ;
- connection->session->accept = 1 ;
+ bgp_connection_enable_accept(connection) ;
} ;
+ bgp_fsm_post_session_event(connection, bgp_session_eStart, NULL, 0) ;
+
return next_state ;
} ;
@@ -1644,15 +1667,18 @@ static bgp_fsm_action(bgp_fsm_send_open)
bgp_msg_send_open(connection, connection->session->open_send) ;
+ bgp_fsm_post_session_event(connection, bgp_session_eTCP_connect, NULL, 0) ;
+
return next_state ;
} ;
/*------------------------------------------------------------------------------
* TCP connection has failed to come up -- Connect/Active states.
*
- * Close the connection (doesn't do much if secondary connection).
+ * This is in response to TCP_connection_open_failed, which has posted the
+ * exception -- so now need to deal with it.
*
- * If secondary connection, disable accept.
+ * Close the connection -- if secondary connection, disable accept.
*
* Will stay in Connect/Active states.
*
@@ -1662,20 +1688,16 @@ static bgp_fsm_action(bgp_fsm_failed)
{
bgp_connection_close(connection) ;
- if (connection->ordinal == bgp_connection_secondary)
- connection->session->accept = 0 ;
-
- return next_state ;
+ return bgp_fsm_catch_exception(connection, next_state) ;
} ;
/*------------------------------------------------------------------------------
* Fatal I/O error -- any state (other than Idle and Stopping).
*
- * Close the connection (if any).
+ * Close the connection (if any) -- if secondary connection, disable accept.
*
- * If secondary connection, disable accept.
- *
- * If about to stop: set bgp_stopped_fatal_error
+ * This is in response to TCP_fatal_error, which has posted the
+ * exception -- so now need to deal with it.
*
* NB: the session is locked.
*/
@@ -1683,13 +1705,7 @@ static bgp_fsm_action(bgp_fsm_fatal)
{
bgp_connection_close(connection) ;
- if (connection->ordinal == bgp_connection_secondary)
- connection->session->accept = 0 ;
-
- if (next_state == bgp_fsm_Stopping)
- connection->stopped = bgp_stopped_fatal_error ;
-
- return next_state ;
+ return bgp_fsm_catch_exception(connection, next_state) ;
} ;
/*------------------------------------------------------------------------------
@@ -1710,8 +1726,7 @@ static bgp_fsm_action(bgp_fsm_retry)
{
bgp_connection_close(connection) ;
- if (connection->ordinal == bgp_connection_secondary)
- connection->session->accept = 0 ;
+ bgp_fsm_post_session_event(connection, bgp_session_eRetry, NULL, 0) ;
return bgp_fsm_start(connection, next_state, event) ;
} ;
@@ -1719,7 +1734,8 @@ static bgp_fsm_action(bgp_fsm_retry)
/*------------------------------------------------------------------------------
* TCP connection has closed -- OpenSent/OpenConfirm/Established states
*
- * This is used when a TCP connection has come up, but has simply closed.
+ * This is in response to TCP_connection_closed, which has posted the
+ * exception -- so now need to deal with it.
*
* NB: the session is locked.
*/
@@ -1727,10 +1743,7 @@ static bgp_fsm_action(bgp_fsm_closed)
{
bgp_connection_close(connection) ;
- if (connection->ordinal == bgp_connection_secondary)
- connection->session->accept = 0 ;
-
- return next_state ;
+ return bgp_fsm_catch_exception(connection, next_state) ;
} ;
/*------------------------------------------------------------------------------
@@ -1753,10 +1766,11 @@ static bgp_fsm_action(bgp_fsm_expire)
return next_state ;
} ;
- /* Otherwise: send NOTIFICATION */
- bgp_fsm_send_notification(connection,
- bgp_notify_new(BGP_NOMC_HOLD_EXP, BGP_NOMS_UNSPECIFIC, 0)) ;
- return next_state ;
+ /* Otherwise: raise exception */
+ bgp_fsm_post_exception(connection, bgp_session_eExpired,
+ bgp_notify_new(BGP_NOMC_HOLD_EXP, BGP_NOMS_UNSPECIFIC, 0), 0) ;
+
+ return bgp_fsm_catch_exception(connection, next_state) ;
} ;
/*------------------------------------------------------------------------------
@@ -1818,6 +1832,8 @@ static bgp_fsm_action(bgp_fsm_recv_open)
/* If all is well, send a KEEPALIVE message to acknowledge the OPEN */
bgp_msg_send_keepalive(connection) ;
+ bgp_fsm_post_session_event(connection, bgp_session_eOpen_accept, NULL, 0) ;
+
/* Transition to OpenConfirm state */
return next_state ;
}
@@ -1838,31 +1854,27 @@ static bgp_fsm_action(bgp_fsm_recv_open)
*/
static bgp_fsm_action(bgp_fsm_error)
{
- bgp_fsm_send_notification(connection,
- bgp_notify_new(BGP_NOMC_FSM, BGP_NOMS_UNSPECIFIC, 0)) ;
- return next_state ;
+ bgp_fsm_post_exception(connection, bgp_session_eFSM_error,
+ bgp_notify_new(BGP_NOMC_FSM, BGP_NOMS_UNSPECIFIC, 0), 0) ;
+
+ return bgp_fsm_catch_exception(connection, next_state) ;
} ;
/*------------------------------------------------------------------------------
* Receive NOTIFICATION from far end -- OpenSent/OpenConfirm/Established
*
- * For OpenSent/OpenConfirm will be going Idle.
+ * This is in response to Receive_NOTIFICATION_message, which has posted the
+ * exception -- so now need to deal with it.
*
- * For Established will be going Stopping -- bgp_stopped_recv_nom
- *
- * Next state will be same as current, except for Established, when will be
- * Stopping.
+ * Next state will be Idle, except for Established, when will be Stopping.
*
* NB: the session is locked.
*/
static bgp_fsm_action(bgp_fsm_recv_nom)
{
- if (next_state == bgp_fsm_Stopping)
- connection->stopped = bgp_stopped_recv_nom ;
-
bgp_connection_close(connection) ;
- return next_state ;
+ return bgp_fsm_catch_exception(connection, next_state) ;
} ;
/*------------------------------------------------------------------------------
@@ -1916,7 +1928,7 @@ static bgp_fsm_action(bgp_fsm_establish)
/* TODO: now would be a good time to withdraw the password from listener ? */
/* Set the session state -- tell the Routeing Engine the news */
- bgp_session_set_state(session, bgp_session_Established) ;
+ /*>>>>>>> signal event <<<<<*/
return next_state ;
} ;
@@ -1963,23 +1975,64 @@ static bgp_fsm_action(bgp_fsm_exit)
}
/*==============================================================================
- * The FSM stopping management.
+ * Catching FSM Exceptions.
+ *
+ * Throwing/Posting Exceptions sets:
+ *
+ * connection->except )
+ * connection->err ) which define the exception
+ * connection->notification )
+ *
+ * The FSM adds the expected next_state.
+ *
+ * From all the above...
+ *
*
- * There are many ways in which a connection may be required to stop.
+ */
+static bgp_fsm_state_t
+bgp_fsm_catch_exception(bgp_connection connection, bgp_fsm_state_t next_state)
+{
+
+if (connection->stopped == bgp_stopped_not)
+ return bgp_fsm_invalid(connection, next_state, event) ;
+
+/* If there is a NOTIFICATION to send, now is the time to do that.
+ *
+ * NB: can only be notification pending if TCP connection is up (OpenSent,
+ * OpenConfirm or Established.
*
- * 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 ...
+ * If do send N
*
+ * Otherwise, close the connection.
+ */
+if (connection->notification_pending)
+ next_state = bgp_fsm_send_notification(connection,
+ connection->notification) ;
+else
+ bgp_connection_close(connection) ;
+
+/* If current state is Established, or if are stopped because of any of:
*
+ * bgp_stopped_admin -- stopped by Routeing Engine
+ * bgp_stopped_loser -- other connection won race to Established state
+ * bgp_stopped_invalid -- invalid event or action
*
+ * then we force transition to Stopping state.
*/
+if ( (connection->state == bgp_fsm_Established)
+ || (connection->stopped == bgp_stopped_admin)
+ || (connection->stopped == bgp_stopped_loser)
+ || (connection->stopped == bgp_stopped_invalid) )
+ next_state = bgp_fsm_Stopping ; /* force transition */
+
+/* Done */
+
+
static void
-bgp_fsm_set_stopping(bgp_connection connection, bgp_stopped_cause_t cause,
- bgp_notify notification, int both)
+bgp_fsm_set_exception(bgp_connection connection, bgp_session_event_t except,
+ bgp_notify notification, int err) ;
{
bgp_session session ;
bgp_connection sibling ;
@@ -2012,6 +2065,8 @@ bgp_fsm_set_stopping(bgp_connection connection, bgp_stopped_cause_t cause,
BGP_CONNECTION_SESSION_UNLOCK(connection) ; /*>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
} ;
+} ;
+
/*==============================================================================
* The BGP connections timers handling.
*