diff options
Diffstat (limited to 'bgpd/bgp_fsm.c')
-rw-r--r-- | bgpd/bgp_fsm.c | 353 |
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. * |