summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChris Hall <GMCH@hestia.halldom.com>2010-01-06 12:06:43 +0000
committerChris Hall <GMCH@hestia.halldom.com>2010-01-06 12:06:43 +0000
commitf2c33e324e1d6194f3f5ca6f3b8f6d08cce8fb69 (patch)
tree74b5d39c8831ff84dc53e0c0c0d73f40b0e178a8
parent4a6acac1becf0af0be0deb9fc17d4cdcf8595071 (diff)
downloadquagga-f2c33e324e1d6194f3f5ca6f3b8f6d08cce8fb69.tar.bz2
quagga-f2c33e324e1d6194f3f5ca6f3b8f6d08cce8fb69.tar.xz
Work in progress on BGP Engine
modified: bgpd/bgp_common.h modified: bgpd/bgp_connection.c modified: bgpd/bgp_connection.h modified: bgpd/bgp_fsm.c modified: bgpd/bgp_fsm.h modified: bgpd/bgp_network.c modified: bgpd/bgp_open_state.h modified: bgpd/bgp_session.c modified: bgpd/bgp_session.h
-rw-r--r--bgpd/bgp_common.h21
-rw-r--r--bgpd/bgp_connection.c66
-rw-r--r--bgpd/bgp_connection.h17
-rw-r--r--bgpd/bgp_fsm.c1169
-rw-r--r--bgpd/bgp_fsm.h7
-rw-r--r--bgpd/bgp_network.c30
-rw-r--r--bgpd/bgp_open_state.h2
-rw-r--r--bgpd/bgp_session.c5
-rw-r--r--bgpd/bgp_session.h7
9 files changed, 907 insertions, 417 deletions
diff --git a/bgpd/bgp_common.h b/bgpd/bgp_common.h
index fd73bc51..b3b58cac 100644
--- a/bgpd/bgp_common.h
+++ b/bgpd/bgp_common.h
@@ -69,17 +69,21 @@ enum bgp_stopped_causes
bgp_stopped_not = 0, /* not stopped (yet) */
-
bgp_stopped_admin = 1, /* Routeing Engine Stop */
+ /* Sent Cease NOTIFICATION */
- bgp_stopped_notification = 2, /* Received NOTIFICATION */
+ bgp_stopped_collision = 2, /* Collision Resolution Stop */
+ bgp_stopped_loser = 3, /* Loser in race to Established state */
- bgp_stopped_collision = 3,
+ bgp_stopped_error = 4, /* */
+ bgp_stopped_recv_nom = 5, /* Received NOTIFICATION */
- bgp_stopped_invalid = 4, /* some internal error */
- bgp_stopped_unknown = 5, /* some unknown reason */
+ bgp_stopped_connect_fail = 5,
+ bgo_stopped_connect_drop = 6,
+ bgp_stopped_fatal_error = 8,
+ bgp_stopped_invalid = 9, /* some internal error */
- bgp_stopped_max_cause = 4
+ bgp_stopped_max_cause = 8
} ;
/*==============================================================================
@@ -91,7 +95,12 @@ typedef uint32_t as_t ;
typedef uint16_t as16_t ; /* we may still encounter 16 Bit asnums */
/* BGP Identifier -- usually an IPv4 address ! */
+#ifndef _GMCH_BGP_H
typedef uint32_t bgp_id_t ;
+#endif
+
+typedef bgp_id_t bgp_id_ht ; /* in host order */
+typedef bgp_id_t bgp_id_nt ; /* in network order */
/* Size of BGP packets or thing in such */
typedef uint16_t bgp_size_t;
diff --git a/bgpd/bgp_connection.c b/bgpd/bgp_connection.c
index 28d584f0..8517ccc0 100644
--- a/bgpd/bgp_connection.c
+++ b/bgpd/bgp_connection.c
@@ -116,6 +116,7 @@ bgp_connection_init_new(bgp_connection connection, bgp_session session,
/* Structure is zeroised, so the following are implictly initialised:
*
* * state bgp_fsm_Initial
+ * * comatose not comatose
* * next NULL -- not on the connection queue
* * prev NULL -- not on the connection queue
* * post bgp_fsm_null_event
@@ -123,8 +124,8 @@ bgp_connection_init_new(bgp_connection connection, bgp_session session,
* * stopped bgp_stopped_not
* * notification NULL -- none received or sent
* * err no error, so far
- * * su_local no address, yet
- * * su_remote no address, yet
+ * * su_local NULL -- no address, yet
+ * * su_remote NULL -- no address, yet
* * hold_timer_interval none -- set when connection is opened
* * keepalive_timer_interval none -- set when connection is opened
* * read_pending nothing pending
@@ -191,6 +192,67 @@ bgp_connection_init_host(bgp_connection connection, const char* tag)
} ;
/*------------------------------------------------------------------------------
+ * Get sibling (if any) for given connection.
+ *
+ * NB: requires the session to be LOCKED.
+ */
+extern bgp_connection
+bgp_connection_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] ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Make given connection the primary.
+ *
+ * Expects the given connection to be the only remaining connection.
+ *
+ * NB: requires the session to be LOCKED.
+ */
+extern void
+bgp_connection_make_primary(bgp_connection connection)
+{
+ bgp_session session = connection->session ;
+
+ /* Deal with the connection ordinal. */
+ if (connection->ordinal != bgp_connection_primary)
+ {
+ connection->ordinal = bgp_connection_primary ;
+ session->connections[bgp_connection_primary] = connection ;
+ } ;
+
+ session->connections[bgp_connection_secondary] = NULL ;
+
+ /* Move the open_state to the session.
+ * Change the connection host to drop the primary/secondary distinction.
+ * Copy the negotiated hold_timer_interval and keepalive_timer_interval
+ * Copy the su_local and su_remote
+ */
+
+ session->open_recv = connection->open_recv ;
+ connection->open_recv = NULL ; /* no longer interested in this */
+
+ XFREE(MTYPE_BGP_PEER_HOST, connection->host) ;
+ bgp_connection_init_host(connection, "") ;
+
+ session->hold_timer_interval = connection->hold_timer_interval ;
+ session->keepalive_timer_interval = session->keepalive_timer_interval ;
+
+ session->su_local = connection->su_local ;
+ connection->su_local = NULL ;
+ session->su_remote = connection->su_remote ;
+ connection->su_remote = NULL ;
+} ;
+
+/*------------------------------------------------------------------------------
* Free connection.
*
* Connection must be Stopping -- no longer attached to a session.
diff --git a/bgpd/bgp_connection.h b/bgpd/bgp_connection.h
index 037f9a77..d293f1e3 100644
--- a/bgpd/bgp_connection.h
+++ b/bgpd/bgp_connection.h
@@ -117,12 +117,13 @@ struct bgp_connection
bgp_session session ; /* session connection belongs to */
/* NULL if connection stopping */
qpt_mutex p_mutex ; /* session mutex* */
- /* NULL if connection stopping */
+ /* (avoids incomplete type issue) */
bgp_connection_ordinal_t ordinal ; /* primary/secondary connection */
int accepted ; /* came via accept() */
bgp_fsm_state_t state ; /* FSM state of connection */
+ int comatose ; /* Idle and no timer set */
bgp_connection next ; /* for the connection queue */
bgp_connection prev ; /* NULL <=> not on the queue */
@@ -138,8 +139,8 @@ struct bgp_connection
struct qps_file qf ; /* qpselect file structure */
int err ; /* error number -- if any */
- union sockunion su_local ; /* address of the near end */
- union sockunion su_remote ; /* address of the far end */
+ union sockunion* su_local ; /* address of the near end */
+ union sockunion* su_remote ; /* address of the far end */
char* host ; /* peer "name" + Connect/Listen */
struct zlog* log ; /* where to log to */
@@ -178,6 +179,12 @@ bgp_connection_reset(bgp_connection connection, int free_structure) ;
extern void
bgp_connection_open(bgp_connection connection, int fd) ;
+extern bgp_connection
+bgp_connection_get_sibling(bgp_connection connection) ;
+
+extern void
+bgp_connection_make_primary(bgp_connection connection) ;
+
extern void
bgp_connection_close(bgp_connection connection) ;
@@ -198,14 +205,14 @@ bgp_connection_write(bgp_connection connection) ;
Inline void
BGP_CONNECTION_SESSION_LOCK(bgp_connection connection)
{
- if (connection->p_mutex != NULL)
+ if (connection->session != NULL)
qpt_mutex_lock(connection->p_mutex) ;
} ;
Inline void
BGP_CONNECTION_SESSION_UNLOCK(bgp_connection connection)
{
- if (connection->p_mutex != NULL)
+ if (connection->session != NULL)
qpt_mutex_unlock(connection->p_mutex) ;
} ;
diff --git a/bgpd/bgp_fsm.c b/bgpd/bgp_fsm.c
index f5755cfa..f2b89576 100644
--- a/bgpd/bgp_fsm.c
+++ b/bgpd/bgp_fsm.c
@@ -34,6 +34,7 @@
#include "bgpd/bgp_fsm.h"
#include "lib/qtimers.h"
+#include "lib/sockunion.h"
#include "bgpd/bgp_debug.h"
#include "bgpd/bgp_packet.h"
@@ -72,9 +73,9 @@
* 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.
+ * events. This avoids the problem of the state of the connection being
+ * "out of date" until its event queue is emptied, and the problem of the state
+ * changing between the raising of an event and dealing with same.
*
* 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
@@ -96,30 +97,148 @@
*
* 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).
+ * If two connections are made, only one will reach OpenConfirm state and
+ * hence Established.
*
* 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.
+ * accepted (or if something goes wrong while waiting for or making an accept()
+ * connection).
*
* When a session is enabled, the allowed connections are initialised and
* a BGP_Start event issued for each one.
*
+ * 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). In Established state, the primary
+ * connection is the one that won the race -- any other connection is snuffed
+ * out.
+ *
+ * As per the RFC, collision detection/resolution is performed when an OPEN
+ * message is received -- that is, as the connection attempts to advance to
+ * OpenConfirm state. At that point, if the sibling is in OpenConfirm state,
+ * then one of the two connections is closed (and will go Idle once the
+ * NOTIFICATION has been sent).
+ *
+ * See below for a discussion of the fall back to Idle -- the losing connection
+ * will remain comatose until the winner either reaches Established (when the
+ * loser is snuffed out) or the winner falls back to Idle (when the
+ * IdleHoldTimer for the loser is set, and it will be awoken in due course).
+ *
+ * NB: the RFC talks of matching source/destination and destination/source
+ * addresses of connections in order to detect collisions. This code
+ * uses only the far end address to detect collisions. It does so
+ * implicitly because the in-bound connection is matched with the out-
+ * bound one using the peer's known IP address -- effectively its name.
+ *
+ * [It is not deemed relevant if the local addresses for the in- and out-
+ * bound connections are different.]
+ *
+ * The RFC further says "the local system MUST examine all of its
+ * connections that are in OpenConfirm state" ... "If, among these
+ * connections, there is a connection to a remote BGP speaker whose BGP
+ * identifier equals the one in the OPEN message, and this connection
+ * collides with [it]" ... then must resolve the collision.
+ *
+ * This code almost does this, but:
+ *
+ * * there can only be one connection that collides (i.e. only one other
+ * which has the same remote end address), and that is the sibling
+ * connection.
+ *
+ * So there's not a lot of "examining" to be done.
+ *
+ * * the RFC seems to accept that there could be two distinct connections
+ * with the same remote end address, but *different* BGP Identifiers.
+ *
+ * As far as Quagga is concerned, that is impossible. The remote end
+ * IP address is the name of the peering session, and there cannot
+ * be two peering sessions with the same name. It follows that Quagga
+ * requires that the "My AS" and the "BGP Identifier" entries in the
+ * OPEN messages from a given remote end IP address MUST MATCH !
+ *
*------------------------------------------------------------------------------
- * Error Handling.
+ * 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.
+ *
+ * The FSM proceeds in three basic phases:
+ *
+ * 1) attempting to establish a TCP connection: Idle/Active/Connect
+ *
+ * In this phase there is no connection for the other end to close !
+ *
+ * Idle is a "stutter step" which becomes longer each time the FSM falls
+ * back to Idle, which it does if the process fails in OpenSent or
+ * OpenConfirm.
+ *
+ * Cannot fail in Idle !
+ *
+ * In Active/Connect any failure causes the FSM to stop trying to connect,
+ * then it does nothing further until the end of the ConnectRetryTimer
+ * interval -- at which point it will try again, re-charging the timer.
+ * (That is usually 120 seconds (less jitter) -- so in the worst case, it
+ * will try to do something impossible every 90-120 seconds.)
+ *
+ * A connection may fall back to Idle from OpenSent/OpenConfirm (see
+ * below). While one connection is OpenSent or OpenConfirm don't really
+ * want to start another TCP connection in competition. So, on entry
+ * to Idle:
+ *
+ * * if sibling exists and is in OpenSent or OpenConfirm:
+ *
+ * - do not change the IdleHoldTimer interval.
+ * - do not set the IdleHoldTimer (with jitter).
+ * - set self "comatose".
+ *
+ * * otherwise:
*
- * I/O and other operations may fail. When they do one of four events may
- * be generated:
+ * - increase the IdleHoldTimer interval.
+ * - set the IdleHoldTimer.
+ *
+ * and if sibling exists and is comatose:
+ *
+ * - set *its* IdleHoldTimer (with jitter).
+ * - clear *its* comatose flag.
+ *
+ * The effect is that if both connections make it to OpenSent, then only
+ * when *both* fall back to Idle will the FSM try to make any new TCP
+ * connections.
+ *
+ * The IdleHoldTimer increases up to 120 seconds. In the worst case, the
+ * far end repeatedly makes outgoing connection attempts, and immediately
+ * drops them. In which case, the IdleHoldTimer grows, and the disruption
+ * reduces to once every 90-120 seconds !
+ *
+ * 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).
+ *
+ * Both connections may reach OpenSent. Only one at once can reach
+ * OpenConfirm -- collision resolution sees to that.
+ *
+ * Note that while a NOTIFICATION is being sent the connection stays
+ * in OpenSent/OpenConfirm state.
+ *
+ * 3) BGP session established
+ *
+ * If something goes wrong, or the other end closes the connection
+ * (with or without notification) will stop the session.
+ *
+ * When things do go wrong, one of the following events is 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.
+ * connections. These may be used, for example, to signal that an UPDATE
+ * message is invalid.
+ *
+ * See below for further discussion of BGP_Stop.
*
* (The FSM itself uses bgp_fsm_set_stopping() before moving to
* Stopping state.)
@@ -158,23 +277,82 @@
* Raised by unexpected errors in connect/accept/read/write
*
* The function bgp_fsm_io_fatal_error() will generate a TCP_fatal_error.
+ *
+ * e.
+ *
+ *------------------------------------------------------------------------------
+ * 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().
+ *
+ * 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
+ * the session (if Established), also sending an FSM Error NOTIFICATION.
+ * See bgp_fsm_error().
+ *
+ *------------------------------------------------------------------------------
+ * Sending NOTIFICATION message
+ *
+ * In OpenSent, OpenConfirm and Established states may send a NOTIFICATION
+ * message.
+ *
+ * The procedure for sending a NOTIFICATION is:
+ *
+ * -- close the connection for reading and clear read buffers.
+ *
+ * This ensures that no further read I/O can occur and no related events.
+ *
+ * Note that anything sent from the other end is acknowledged, but
+ * quietly discarded.
+ *
+ * -- purge the write buffer of all output except any partly sent message.
+ *
+ * This ensures there is room in the write buffer at the very least.
+ *
+ * For OpenSent and OpenConfirm states there should be zero chance of
+ * there being anything to purge, and probably no write buffer in any
+ * case.
+ *
+ * -- purge any pending write messages for the connection (for Established).
+ *
+ * -- set notification_pending = 1 (write pending)
+ *
+ * -- write the NOTIFICATION message.
+ *
+ * For Established, the message will at the very least be written to the
+ * write buffer. For OpenSent and OpenConfirm expect it to go directly
+ * to the TCP buffer.
+ *
+ * -- set HoldTimer to a waiting to clear buffer time -- say 20 secs.
+ *
+ * Don't expect to need to wait at all in OpenSent/OpenConfirm states.
+ *
+ * -- when the NOTIFICATION message clears the write buffer, that will
+ * generate a Sent_NOTIFICATION_message event.
+ *
+ * After sending the NOTIFICATION, OpenSent & OpenConfirm stay in their
+ * respective states. Established goes to Stopping State.
+ *
+ * When the Sent_NOTIFICATION_message event occurs, set the HoldTimer to
+ * a "courtesy" time of 5 seconds. Remain in the current state.
+ *
+ * During the "courtesy" time the socket will continue to acknowledge, but
+ * discard input. In the case of Collision Resolution this gives a little time
+ * for the other end to send its NOTIFICATION message. In all cases, it gives
+ * a little time before the connection is slammed shut.
+ *
+ * When the HoldTimer expires close the connection completely (whether or not
+ * the NOTIFICATION has cleared the write buffer).
*/
-
/*==============================================================================
- * Functions to enable, stop, signal I/O events to, etc. the FSM
- */
-
-static void
-bgp_fsm_set_stopping(bgp_connection connection, bgp_stopped_cause_t cause,
- bgp_notify notification, int both) ;
-
-/*------------------------------------------------------------------------------
* Enable the given connection -- which must be newly initialised.
*
* This is the first step in the FSM, and the connection advances to Idle.
+ *
*/
-
extern void
bgp_fsm_enable_connection(bgp_connection connection)
{
@@ -182,6 +360,39 @@ bgp_fsm_enable_connection(bgp_connection connection)
bgp_fsm_event(connection, bgp_fsm_BGP_Start) ;
} ;
+
+
+ /*=============================================================================
+ * BGP_Stop Events
+ *
+ * Before generating a BGP_Stop event the cause of the stop MUST be set for
+ * the connection.
+ *
+ * In Established state any BGP_Stop closes the connection and stops the
+ * session -- sending a NOTIFICATION.
+ *
+ * In other states, the cause affects the outcome:
+ *
+ * * bgp_stopped_admin -- send NOTIFICATION to all connections
+ * go to Stopping state and stop the session.
+ * (once any NOTIFICATION has cleared, terminates
+ * the each connection.)
+ *
+ * * bgp_stopped_collision -- send NOTIFICATION
+ * close connection & fall back to Idle
+ * (can only happen in OpenSent/OpenConfirm)
+ *
+ * * otherwise -- if TCP connection up:
+ * send NOTIFICATION
+ * close connection & fall back to Idle
+ * otherwise
+ * do nothing (stay in Idle/Connect/Active)
+ */
+
+static void
+bgp_fsm_set_stopping(bgp_connection connection, bgp_stopped_cause_t cause,
+ bgp_notify notification, int both) ;
+
/*------------------------------------------------------------------------------
* Bring given connection to a stop.
*
@@ -228,6 +439,10 @@ bgp_fsm_stop_session(bgp_connection connection, bgp_stopped_cause_t cause,
bgp_fsm_event(connection, bgp_fsm_BGP_Stop) ;
} ;
+/*==============================================================================
+ * Functions to signal I/O error(s) and connect()/accept() completion.
+ */
+
/*------------------------------------------------------------------------------
* Signal a fatal I/O error on the given connection.
*
@@ -274,15 +489,17 @@ bgp_fsm_io_error(bgp_connection connection, int err)
|| (err == ETIMEDOUT) )
{
if (BGP_DEBUG(events, EVENTS))
- if (err == 0)
- plog_debug(connection->log,
+ {
+ 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,
+ 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) ;
}
@@ -296,7 +513,8 @@ bgp_fsm_io_error(bgp_connection connection, int err)
* 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 == 0, then all is well: copy the local and remote sockunions
+ * and generate TCP_connection_open event
*
* If err is one of:
*
@@ -308,12 +526,19 @@ bgp_fsm_io_error(bgp_connection connection, int err)
* Other errors are reported as TCP_fatal_error.
*/
extern void
-bgp_fsm_connect_completed(bgp_connection connection, int err)
+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) ;
+ {
+ bgp_fsm_event(connection, bgp_fsm_TCP_connection_open) ;
+
+ connection->su_local = sockunion_dup(su_local) ;
+ connection->su_remote = sockunion_dup(su_remote) ;
+ }
else if ( (err == ECONNREFUSED)
|| (err == ECONNRESET)
|| (err == EHOSTUNREACH)
@@ -331,45 +556,43 @@ bgp_fsm_connect_completed(bgp_connection connection, int err)
plog_debug (connection->log, "%s [FSM] " message, connection->host)
/*==============================================================================
- * The FSM table and the finite state machine actions.
+ * The FSM table
*/
-typedef bgp_fsm_state_t
-bgp_fsm_action(bgp_connection connection, bgp_fsm_state_t next_state,
- bgp_fsm_event_t event) ;
+#define bgp_fsm_action(FUNCNAME) \
+ bgp_fsm_state_t FUNCNAME(bgp_connection connection, \
+ bgp_fsm_state_t next_state, bgp_fsm_event_t event)
+
+typedef bgp_fsm_action(bgp_fsm_action_func) ;
struct bgp_fsm {
- bgp_fsm_action* action ;
- bgp_fsm_state_t next_state ;
+ bgp_fsm_action_func* 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 ;
-
-static void
-bgp_fsm_state_change(bgp_connection connection, bgp_fsm_state_t new_state) ;
+static bgp_fsm_action(bgp_fsm_null) ;
+static bgp_fsm_action(bgp_fsm_enter) ;
+static bgp_fsm_action(bgp_fsm_stop) ;
+static bgp_fsm_action(bgp_fsm_invalid) ;
+static bgp_fsm_action(bgp_fsm_start) ;
+static bgp_fsm_action(bgp_fsm_send_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_closed) ;
+static bgp_fsm_action(bgp_fsm_expire) ;
+static bgp_fsm_action(bgp_fsm_recv_open) ;
+static bgp_fsm_action(bgp_fsm_error) ;
+static bgp_fsm_action(bgp_fsm_recv_nom) ;
+static bgp_fsm_action(bgp_fsm_sent_nom) ;
+static bgp_fsm_action(bgp_fsm_send_kal) ;
+static bgp_fsm_action(bgp_fsm_establish) ;
+static bgp_fsm_action(bgp_fsm_recv_kal) ;
+static bgp_fsm_action(bgp_fsm_update) ;
+static bgp_fsm_action(bgp_fsm_exit) ;
/*------------------------------------------------------------------------------
- * Finite State Machine events
+ * Finite State Machine events
*
* 0. null_event
*
@@ -404,8 +627,7 @@ bgp_fsm_state_change(bgp_connection connection, bgp_fsm_state_t new_state) ;
* -- 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.
+ * See above for further discussion.
*
* 3. TCP_connection_open
*
@@ -442,21 +664,39 @@ bgp_fsm_state_change(bgp_connection connection, bgp_fsm_state_t new_state) ;
*
* 5. TCP_connection_open_failed ("soft" error)
*
- * a. in Connect State
+ * a. in Connect or Active states
+ *
+ * Close the connection. For Active state, disable accept.
*
- * 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.
+ * Stay in Connect/Active (until ConnectRetryTimer expires).
+ *
+ * Cannot happen at any other time.
*
* 6. TCP_fatal_error ("hard" error)
*
- * a. in all states other than Initial
+ * a. in Connect or Active states
+ *
+ * Close the connection. For Active state, disable accept.
*
- * raised by unexpected errors in connect/accept/read/write
+ * Stay in Connect/Active (until ConnectRetryTimer expires).
*
- * Stops the connection and disables the type of connection. So,
- * for the remains of this session, will not attempt to <<<<<<<<<<<<<<<<
+ * b. in OpenSent/OpenConfirm states
+ *
+ * Close the connection.
+ *
+ * Fall back to Idle.
+ *
+ * c. in Established state
+ *
+ * Close the connection and the session.
+ *
+ * Go to Stopping state.
+ *
+ * d. in Stopping state.
+ *
+ * Close the connection.
+ *
+ * Cannot happen at any other time (ie Idle).
*
* 7. ConnectRetry_timer_expired
*
@@ -472,21 +712,24 @@ bgp_fsm_state_change(bgp_connection connection, bgp_fsm_state_t new_state) ;
* a. in OpenSent state
*
* Time to give up waiting for an OPEN (or NOTIFICATION) from the
- * other end.
+ * other end. For this wait the RFC recommends a "large" value for
+ * the hold time -- and suggests 4 minutes.
*
- * Fall back to Idle.
+ * Or, if the connection was stopped by the collision resolution
+ * process, time to close the connection.
*
- * For this state the RFC recommends a "large" value for the hold
- * time -- and suggests 4 minutes.
+ * Close the connection. Fall back to Idle.
*
* b. in OpenConfirm state
*
* Time to give up waiting for a KEEPALIVE to confirm the connection.
+ * For this wait the hold time used is that negotiated in the OPEN
+ * messages that have been exchanged.
*
- * Fall back to Idle.
+ * Or, if the connection was stopped by the collision resolution
+ * process, time to close the connection.
*
- * In this state the hold time used is that negotiated in the OPEN
- * messages that have been exchanged.
+ * Close the connection. Fall back to Idle.
*
* c. in Established state
*
@@ -528,9 +771,9 @@ bgp_fsm_state_change(bgp_connection connection, bgp_fsm_state_t new_state) ;
*
* c. in Established state -- FSM error
*
- * Terminate session.
+ * Send NOTIFICATION. Terminate session.
*
- * Cannot happen at any other time (connection not up).
+ * Cannot happen at any other time (connection not up or read closed).
*
* 11. Receive_KEEPALIVE_message
*
@@ -538,13 +781,13 @@ bgp_fsm_state_change(bgp_connection connection, bgp_fsm_state_t new_state) ;
*
* a. in OpenSent state -- FSM error
*
- * Fall
+ * Send NOTIFICATION. Fall back to Idle.
*
* b. in OpenConfirm state -- the expected response
*
* c. in Established state -- expected
*
- * Cannot happen at any other time (connection not up).
+ * Cannot happen at any other time (connection not up or read closed).
*
* 12. Receive_UPDATE_message
*
@@ -552,9 +795,11 @@ bgp_fsm_state_change(bgp_connection connection, bgp_fsm_state_t new_state) ;
*
* a. in OpenSent and OpenConfirm states -- FSM error
*
+ * Send NOTIFICATION. Fall back to Idle.
+ *
* b. in Established state -- expected
*
- * Cannot happen at any other time (connection not up).
+ * Cannot happen at any other time (connection not up or read closed).
*
* 13. Receive_NOTIFICATION_message
*
@@ -563,7 +808,10 @@ bgp_fsm_state_change(bgp_connection connection, bgp_fsm_state_t new_state) ;
* a. in OpenSent, OpenConfirm and Established states -- give up
* on the session.
*
- * Cannot happen at any other time (connection not up).
+ * a. in OpenSent, OpenConfirm and Established states -- give up
+ * on the session.
+ *
+ * Cannot happen at any other time (connection not up or read closed).
*
* 14. Sent_NOTIFICATION_message
*
@@ -577,7 +825,7 @@ bgp_fsm_state_change(bgp_connection connection, bgp_fsm_state_t new_state) ;
*/
/*------------------------------------------------------------------------------
- * Finite State Machine structure
+ * Finite State Machine Table
*/
static const struct bgp_fsm
@@ -598,7 +846,7 @@ bgp_fsm[bgp_fsm_last_state + 1][bgp_fsm_last_event + 1] =
*/
{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_stop, bgp_fsm_Initial}, /* 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 */
@@ -660,7 +908,7 @@ bgp_fsm[bgp_fsm_last_state + 1][bgp_fsm_last_event + 1] =
*/
{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_stop, bgp_fsm_Idle}, /* 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 */
@@ -710,11 +958,11 @@ bgp_fsm[bgp_fsm_last_state + 1][bgp_fsm_last_event + 1] =
*/
{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_stop, bgp_fsm_Connect}, /* BGP_Stop */
+ {bgp_fsm_send_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_fatal, bgp_fsm_Connect}, /* 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 */
@@ -739,6 +987,12 @@ bgp_fsm[bgp_fsm_last_state + 1][bgp_fsm_last_event + 1] =
* 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
*
* Bring connection and session to a dead stop.
@@ -753,11 +1007,11 @@ bgp_fsm[bgp_fsm_last_state + 1][bgp_fsm_last_event + 1] =
*/
{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_stop, bgp_fsm_Active}, /* BGP_Stop */
+ {bgp_fsm_send_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_failed, bgp_fsm_Active}, /* TCP_connection_open_failed */
+ {bgp_fsm_fatal, bgp_fsm_Active}, /* 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 */
@@ -820,19 +1074,19 @@ bgp_fsm[bgp_fsm_last_state + 1][bgp_fsm_last_event + 1] =
*/
{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_stop, bgp_fsm_Idle}, /* BGP_Stop */
{bgp_fsm_invalid, bgp_fsm_Stopping}, /* TCP_connection_open */
- {bgp_fsm_restart, bgp_fsm_Idle}, /* TCP_connection_closed */
+ {bgp_fsm_closed, 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_fatal, bgp_fsm_Idle}, /* TCP_fatal_error */
{bgp_fsm_invalid, bgp_fsm_Stopping}, /* ConnectRetry_timer_expired */
- {bgp_fsm_restart, bgp_fsm_Idle}, /* Hold_Timer_expired */
+ {bgp_fsm_expire, 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_recv_open, bgp_fsm_OpenConfirm}, /* Receive_OPEN_message */
+ {bgp_fsm_error, bgp_fsm_OpenSent}, /* Receive_KEEPALIVE_message */
+ {bgp_fsm_error, bgp_fsm_OpenSent}, /* Receive_UPDATE_message */
+ {bgp_fsm_recv_nom, bgp_fsm_Idle}, /* Receive_NOTIFICATION_message */
+ {bgp_fsm_sent_nom, bgp_fsm_OpenSent}, /* Sent NOTIFICATION message */
},
{
/* bgp_fsm_OpenConfirm: Opens sent and received, waiting for KeepAlive......
@@ -900,19 +1154,19 @@ bgp_fsm[bgp_fsm_last_state + 1][bgp_fsm_last_event + 1] =
*/
{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_stop, bgp_fsm_Idle}, /* BGP_Stop */
{bgp_fsm_invalid, bgp_fsm_Stopping}, /* TCP_connection_open */
- {bgp_fsm_restart, bgp_fsm_Idle}, /* TCP_connection_closed */
+ {bgp_fsm_closed, 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_fatal, bgp_fsm_Idle}, /* 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_expire, bgp_fsm_Idle}, /* Hold_Timer_expired */
+ {bgp_fsm_send_kal, bgp_fsm_OpenConfirm}, /* KeepAlive_timer_expired */
+ {bgp_fsm_error, bgp_fsm_OpenConfirm}, /* 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_error, bgp_fsm_OpenConfirm}, /* Receive_UPDATE_message */
+ {bgp_fsm_recv_nom, bgp_fsm_Idle}, /* Receive_NOTIFICATION_message */
+ {bgp_fsm_sent_nom, bgp_fsm_OpenConfirm}, /* Sent NOTIFICATION message */
},
{
/* bgp_fsm_Established: session is up and running...........................
@@ -970,11 +1224,11 @@ bgp_fsm[bgp_fsm_last_state + 1][bgp_fsm_last_event + 1] =
{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_send_kal, 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_recv_kal, 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_recv_nom, bgp_fsm_Stopping}, /* Receive_NOTIFICATION_message */
{bgp_fsm_invalid, bgp_fsm_Stopping}, /* Sent NOTIFICATION message */
},
{
@@ -983,17 +1237,23 @@ bgp_fsm[bgp_fsm_last_state + 1][bgp_fsm_last_event + 1] =
* 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:
+ * There are three ways to arrive in Stopping state:
*
- * * stop-idle
+ * a) administrative Stop -- that is, the Routeing Engine is stopping the
+ * session.
*
- * Close the connection, then fall back to Idle state.
+ * Both connections must be stopped.
*
- * * stop-dead
+ * b) the sibling has reached Established state and is snuffing out
+ * its rival.
*
- * Close and terminate the connection (cut loose from session).
+ * Only the current connection must be stopped.
*
- * If this is the only connection, stop the session.
+ * c) the session was Established, but is now stopping.
+ *
+ * There is only one connection, and that must be stopped.
+ *
+ * Before the transition to Stopping state,
*
* The complication is the possible need to send a NOTIFICATION message
* before closing the connection.
@@ -1076,17 +1336,17 @@ bgp_fsm[bgp_fsm_last_state + 1][bgp_fsm_last_event + 1] =
{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_exit, 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_exit, 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_exit, 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 */
+ {bgp_fsm_sent_nom, bgp_fsm_Stopping}, /* Sent NOTIFICATION message */
},
} ;
@@ -1109,12 +1369,23 @@ static const char *bgp_event_str[] =
"Sent_NOTIFICATION_message",
} ;
+/*==============================================================================
+ * Signal FSM event.
+ */
+
+static void
+bgp_fsm_state_change(bgp_connection connection, bgp_fsm_state_t new_state) ;
+
/*------------------------------------------------------------------------------
- * Deal with the given event for the given connection.
+ * Signal event to FSM for the given connection.
+ *
+ *
+ *
*/
extern void
bgp_fsm_event(bgp_connection connection, bgp_fsm_event_t event)
{
+ bgp_session session ;
bgp_fsm_state_t next_state ;
const struct bgp_fsm* fsm ;
@@ -1139,7 +1410,10 @@ bgp_fsm_event(bgp_connection connection, bgp_fsm_event_t event)
*
* The session lock does nothing if no session is attached.
*/
- BGP_CONNECTION_SESSION_LOCK(connection) ; /*<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
+ session = connection->session ;
+
+ if (session != NULL)
+ BGP_SESSION_LOCK(session) ; /*<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
do
{
@@ -1185,81 +1459,124 @@ bgp_fsm_event(bgp_connection connection, bgp_fsm_event_t event)
} while (--connection->fsm_active != 0) ;
- BGP_CONNECTION_SESSION_UNLOCK(connection) ; /*>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
+ if (session != NULL)
+ BGP_SESSION_UNLOCK(session) ; /*<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
+} ;
- /* Connections which are Stopping are no longer linked to a session. */
- /* There is no way out of Stopping state
+/*==============================================================================
+ * The BGP FSM Action Functions
+ */
- if (connection->state == bgp_fsm_Stopping)
- {
- /* Sever link with session -- after mutex unlock the first time */
+static void
+bgp_hold_timer_set(bgp_connection connection, unsigned secs) ;
- session->connections[connection->ordinal] = NULL ;
+static void
+bgp_hold_timer_recharge(bgp_connection connection) ;
- connection->session = NULL ;
- connection->p_mutex = NULL ;
- } ;
-} ;
+static bgp_connection
+bgp_fsm_get_sibling(bgp_connection connection) ;
+
+static bgp_fsm_state_t
+bgp_fsm_send_notification(bgp_connection connection, bgp_notify notification) ;
/*------------------------------------------------------------------------------
* 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)
+static bgp_fsm_action(bgp_fsm_null)
{
return next_state ;
} ;
/*------------------------------------------------------------------------------
- * Invalid event -- cannot occur in current state.
- *
- * Brings down the session -- next state is bgp_fsm_stopping.
+ * 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_invalid(bgp_connection connection, bgp_fsm_state_t next_state,
- bgp_fsm_event_t event)
+static bgp_fsm_action(bgp_fsm_enter)
{
- 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 ;
+ return next_state ;
} ;
/*------------------------------------------------------------------------------
- * This is empty event -- should not really happen...
+ * Stop BGP Connection
+ *
+ * The reason should already have been set: bgp_fsm_set_stopping().
+ *
+ * If no reason set => treat as invalid.
+ *
+ * NB: the default new_state is used unless:
+ *
+ * * the current state is Established -- all stops -> Stopping
+ *
+ * * there is a NOTIFICATION to send (in OpenSent or OpenConfirm) -- stays
+ * in the current state while dealing with the NOTIFICATION.
+ *
+ * * the stop reason => must -> Stopping
*
* 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)
+static bgp_fsm_action(bgp_fsm_stop)
{
- BGP_FSM_DEBUG(connection, "bgp_ignore called") ;
+ 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 ;
} ;
/*------------------------------------------------------------------------------
- * Entry point to FSM.
+ * Invalid event -- cannot occur in current state.
*
- * This is the first thing to happen to the FSM, and takes it from Initial
- * state to Idle, with Idle Hold Timer running.
+ * Brings down the session, sending an FSM error NOTIFICATION.
+ *
+ * Forces transition to Stopping state for this connection and any sibling.
*
* 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)
+static bgp_fsm_action(bgp_fsm_invalid)
{
- return next_state ;
+ 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 ; /* unconditionally */
} ;
/*------------------------------------------------------------------------------
@@ -1283,14 +1600,12 @@ bgp_fsm_enter(bgp_connection connection, bgp_fsm_state_t next_state,
*
* 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)
+static bgp_fsm_action(bgp_fsm_start)
{
if (connection->ordinal == bgp_connection_primary)
{
next_state = bgp_fsm_Connect ;
- connection->post = bgp_open_connect(connection) ;
+ bgp_open_connect(connection) ;
}
else
{
@@ -1302,114 +1617,83 @@ bgp_fsm_start(bgp_connection connection, bgp_fsm_state_t 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.
+ * TCP connection open has come up -- connect() or accept()
*
- * 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.
+ * Send BGP Open Message to peer.
*
* 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)
+static bgp_fsm_action(bgp_fsm_send_open)
{
- unsigned* p_interval = &connection->session->idle_hold_timer_interval ;
+ char buf_l[SU_ADDRSTRLEN] ;
+ char buf_r[SU_ADDRSTRLEN] ;
+ const char* how ;
- *p_interval *= 2 ;
+ if (BGP_DEBUG (normal, NORMAL))
+ {
+ if (connection->ordinal == bgp_connection_primary)
+ how = "connect" ;
+ else
+ how = "accept" ;
- if (*p_interval < 4)
- *p_interval = 4 ;
- else if (*p_interval > 120)
- *p_interval = 120 ;
+ zlog_debug("%s open %s(), local address %s",
+ sockunion2str(connection->su_remote, buf_r, SU_ADDRSTRLEN),
+ how,
+ sockunion2str(connection->su_local, buf_l, SU_ADDRSTRLEN)) ;
+ } ;
- bgp_connection_close(connection) ;
+ bgp_msg_send_open(connection, connection->session->open_send) ;
return next_state ;
} ;
/*------------------------------------------------------------------------------
- * Stop BGP Connection
+ * TCP connection has failed to come up -- Connect/Active states.
*
- * The reason should already have been set: bgp_fsm_set_stopping().
- * But, if not, set unknown reason.
+ * Close the connection (doesn't do much if secondary connection).
*
- * If no notification to be sent, close the connection now.
- * If notification to be sent, try to send it now.
+ * If secondary connection, disable accept.
+ *
+ * Will stay in Connect/Active states.
*
* 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)
+static bgp_fsm_action(bgp_fsm_failed)
{
- if (connection->stopped == bgp_stopped_not)
- bgp_fsm_set_stopping(connection, bgp_stopped_unknown, NULL, 0) ;
-
- /* */
- if (connection->notification_pending)
- {
- bgp_msg_write_notification(connection) ;
-
-
- nothing pending
- }
- * * notification_pending nothing pending
- * * notification_written not written
-)
-
- } ;
+ bgp_connection_close(connection) ;
- /* If are still waiting for the
- if (connection->notification_pending || connection->notification_written)
- bgp_connection_read_close(connection) ;
- else
- bgp_connection_close(connection) ;
+ if (connection->ordinal == bgp_connection_secondary)
+ connection->session->accept = 0 ;
return next_state ;
-}
+} ;
/*------------------------------------------------------------------------------
- * TCP connection open.
+ * Fatal I/O error -- any state (other than Idle and Stopping).
*
- * Send BGP Open Message to peer.
+ * Close the connection (if any).
+ *
+ * If secondary connection, disable accept.
+ *
+ * If about to stop: set bgp_stopped_fatal_error
+ *
+ * NB: the session is locked.
*/
-static bgp_fsm_state_t
-bgp_fsm_open(bgp_connection connection, bgp_fsm_state_t next_state,
- bgp_fsm_event_t event)
+static bgp_fsm_action(bgp_fsm_fatal)
{
- char buf1[BUFSIZ];
-
- if (connection->fd < 0)
- {
- zlog_err ("bgp_connect_success peer's fd is negative value %d",
- connection->fd);
- return -1;
- }
-
- bgp_getsockname(connection) ;
+ bgp_connection_close(connection) ;
- if (BGP_DEBUG (normal, NORMAL))
- {
- 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
- zlog_debug ("%s passive open", peer->host);
- }
+ if (connection->ordinal == bgp_connection_secondary)
+ connection->session->accept = 0 ;
- bgp_send_open(connection) ;
+ if (next_state == bgp_fsm_Stopping)
+ connection->stopped = bgp_stopped_fatal_error ;
return next_state ;
} ;
/*------------------------------------------------------------------------------
- * Connect retry timer is expired when in Connect or Active states.
+ * ConnectRetryTimer expired -- Connect/Active states.
*
* For primary connection:
*
@@ -1419,65 +1703,66 @@ bgp_fsm_open(bgp_connection connection, bgp_fsm_state_t next_state,
* For secondary connection:
*
* * close the existing connection (easy, 'cos never opened !)
- * * continue waiting to accept()
+ * * re-enable accept (if has been cleared) and wait for same
*
*/
-static bgp_fsm_state_t
-bgp_fsm_retry(bgp_connection connection, bgp_fsm_state_t next_state,
- bgp_fsm_event_t event)
+static bgp_fsm_action(bgp_fsm_retry)
{
bgp_connection_close(connection) ;
+
+ if (connection->ordinal == bgp_connection_secondary)
+ connection->session->accept = 0 ;
+
return bgp_fsm_start(connection, next_state, event) ;
} ;
/*------------------------------------------------------------------------------
- * Error:
+ * TCP connection has closed -- OpenSent/OpenConfirm/Established states
*
+ * This is used when a TCP connection has come up, but has simply closed.
*
+ * NB: the session is locked.
*/
-static bgp_fsm_state_t
-bgp_fsm_error(bgp_connection connection, bgp_fsm_state_t next_state,
- bgp_fsm_event_t event)
+static bgp_fsm_action(bgp_fsm_closed)
{
- /* Double start timer. */
- peer->v_start *= 2;
+ bgp_connection_close(connection) ;
- /* Overflow check. */
- if (peer->v_start >= (60 * 2))
- peer->v_start = (60 * 2);
+ if (connection->ordinal == bgp_connection_secondary)
+ connection->session->accept = 0 ;
- return bgp_stop(peer, next_state);
+ return next_state ;
} ;
/*------------------------------------------------------------------------------
- * Hold timer expire. This is error of BGP connection. So cut the
- * peer and change to Idle status.
+ * Hold timer expire -- OpenSent/OpenConfirm/Stopping
+ *
+ * This means either: have finished sending NOTIFICATION (end of "courtesy"
+ * wait time)
+ *
+ * or: can wait no longer for something from the other end.
+ *
+ * NB: the session is locked.
*/
-static bgp_fsm_state_t
-bgp_fsm_expire(bgp_connection connection, bgp_fsm_state_t next_state,
- bgp_fsm_event_t event)
+static bgp_fsm_action(bgp_fsm_expire)
{
- BGP_FSM_DEBUG(connection, "Hold timer expire") ;
-
- /* Send notify to remote peer. */
- bgp_notify_send (peer, BGP_NOTIFY_HOLD_ERR, 0);
-
- /* Sweep if it is temporary peer. */
- if (CHECK_FLAG (peer->sflags, PEER_STATUS_ACCEPT_PEER))
+ /* The process of sending a NOTIFICATION comes to an end here. */
+ if (connection->notification_pending)
{
- zlog_info ("%s [Event] Accepting BGP peer is deleted", peer->host);
- peer_delete (peer);
- return -1;
- }
+ bgp_connection_close(connection) ;
+
+ return next_state ;
+ } ;
- /* bgp_stop needs to be invoked while in Established state */
- return bgp_stop(peer, next_state) ;
+ /* Otherwise: send NOTIFICATION */
+ bgp_fsm_send_notification(connection,
+ bgp_notify_new(BGP_NOMC_HOLD_EXP, BGP_NOMS_UNSPECIFIC, 0)) ;
+ return next_state ;
} ;
/*------------------------------------------------------------------------------
- * Received an acceptable Open Message
+ * Received an acceptable OPEN Message
*
- * The next state is OpenConfirm.
+ * The next state is expected to be OpenConfirm.
*
* However: this is where we do Collision Resolution.
*
@@ -1490,56 +1775,145 @@ bgp_fsm_expire(bgp_connection connection, bgp_fsm_state_t next_state,
* 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
+ * The connection that is closed will fall back to Idle -- so that if the
+ * connection that wins the race to OpenConfirm fails there, then both will be
+ * back in Idle state.
*
- * Immediately respond with a keepalive......
+ * If makes it past Collision Resolution, respond with a KEEPALIVE (to "ack"
+ * the OPEN message).
*
+ * NB: the session is locked.
*/
-static bgp_fsm_state_t
-bgp_fsm_opened(bgp_connection connection, bgp_fsm_state_t next_state,
- bgp_fsm_event_t event)
+static bgp_fsm_action(bgp_fsm_recv_open)
{
- bgp_send_keepalive(connection) ;
+ bgp_session session = connection->session ;
+ bgp_connection sibling = bgp_fsm_get_sibling(connection) ;
+
+ assert(session != NULL) ;
+
+ /* If there is a sibling, and it is in OpenConfirm state, then now must do
+ * collision resolution.
+ */
+ if ((sibling != NULL) && (sibling->state == bgp_fsm_OpenConfirm))
+ {
+ bgp_connection loser ;
+
+ /* NB: bgp_id in open_state is in *host* order */
+ loser = (session->open_send->bgp_id < sibling->open_recv->bgp_id)
+ ? connection
+ : sibling ;
+
+ /* Set reason for stopping */
+ bgp_fsm_set_stopping(loser, bgp_stopped_collision,
+ bgp_notify_new(BGP_NOMC_CEASE, BGP_NOMS_C_COLLISION, 0), 0) ;
+
+ /* If self is the loser, treat this as a BGP_Stop event ! */
+ /* Otherwise, issue BGP_Stop event for sibling. */
+ if (loser == connection)
+ return bgp_fsm_stop(connection, connection->state, event) ;
+ else
+ bgp_fsm_event(sibling, bgp_fsm_BGP_Stop) ;
+ } ;
+
+ /* If all is well, send a KEEPALIVE message to acknowledge the OPEN */
+ bgp_msg_send_keepalive(connection) ;
+ /* Transition to OpenConfirm state */
return next_state ;
}
/*------------------------------------------------------------------------------
- * Status goes to Established.
+ * FSM error -- received wrong type of message !
*
- * TODO: do we need to send a KEEPALIVE on entry to established ?
+ * For example, an OPEN message while in Established state.
*
+ * For use in: OpenSent, OpenConfirm and Established states.
*
- * On transition
+ * Sends NOTIFICATION.
+ *
+ * Next state will be same as current, except for Established, when will be
+ * Stopping.
+ *
+ * NB: the session is locked.
*/
-static bgp_fsm_state_t
-bgp_fsm_establish(bgp_connection connection, bgp_fsm_state_t next_state,
- bgp_fsm_event_t event)
+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 ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Receive NOTIFICATION from far end -- OpenSent/OpenConfirm/Established
+ *
+ * For OpenSent/OpenConfirm will be going Idle.
+ *
+ * For Established will be going Stopping -- bgp_stopped_recv_nom
+ *
+ * Next state will be same as current, 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 ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Pending NOTIFICATION has cleared write buffers
+ * -- OpenSent/OpenConfirm/Stopping
+ *
+ * Set the "courtesy" HoldTimer. Expect to stay in current state.
+ *
+ * NB: the session is locked.
+ */
+static bgp_fsm_action(bgp_fsm_sent_nom)
+{
+ bgp_hold_timer_set(connection, 5) ;
+ return next_state ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Seed Keepalive to peer.
+ *
+ * NB: the session is locked.
+ */
+static bgp_fsm_action(bgp_fsm_send_kal)
+{
+ bgp_msg_send_keepalive(connection) ;
+ return next_state ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Session Established !
+ *
+ * If there is another connection, that is now snuffed out and this connection
+ * becomes the primary.
+ *
+ * NB: the session is locked.
+ */
+static bgp_fsm_action(bgp_fsm_establish)
{
bgp_session session = connection->session ;
bgp_connection sibling = bgp_fsm_get_sibling(connection) ;
assert(session != NULL) ;
- /* The first thing to do is to kill off any sibling and establish
- * self as the primary connection.
- */
+ /* The first thing to do is to snuff off any sibling */
if (sibling != NULL)
- {
- bgp_fsm_stop_connection(sibling, bgp_stopped_collision,
+ bgp_fsm_stop_connection(sibling, bgp_stopped_loser,
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 ;
- } ;
- } ;
+ /* Establish self as primary and copy state up to session */
+ bgp_connection_make_primary(connection) ;
- /* 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) ;
@@ -1548,54 +1922,43 @@ bgp_fsm_establish(bgp_connection connection, bgp_fsm_state_t 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.
+ * Keepalive packet is received -- OpenConfirm/Established
+ *
+ * NB: the session is locked.
*/
-static bgp_fsm_state_t
-bgp_fsm_kal_recv(bgp_connection connection, bgp_fsm_state_t next_state,
- bgp_fsm_event_t event)
+static bgp_fsm_action(bgp_fsm_recv_kal)
{
/* peer count update */
- peer->keepalive_in++;
+//peer->keepalive_in++;
bgp_hold_timer_recharge(connection) ;
return next_state ;
-}
+} ;
/*------------------------------------------------------------------------------
* Update packet is received.
+ *
+ * NB: the session is locked.
*/
-static bgp_fsm_state_t
-bgp_fsm_update(bgp_connection connection, bgp_fsm_state_t next_state,
- bgp_fsm_event_t event)
+static bgp_fsm_action(bgp_fsm_update)
{
bgp_hold_timer_recharge(connection) ;
return next_state ;
}
+
+
+
/*------------------------------------------------------------------------------
- * We're done with this connection.
+ * Connection exit
*
- * May have been waiting to get a NOTIFICATION message away, and that has
- * either succeeded or timed out.
+ * Ring down the curtain. Connection structure will be freed by the BGP Engine.
*
- * Shut the socket and tear down the connection.
+ * NB: the session is locked.
*/
-static bgp_fsm_state_t
-bgp_fsm_done(bgp_connection connection, bgp_fsm_state_t next_state,
- bgp_fsm_event_t event)
+static bgp_fsm_action(bgp_fsm_exit)
{
- bgp_tear_down(connection);
+ bgp_connection_exit(connection) ;
return next_state ;
}
@@ -1649,27 +2012,6 @@ bgp_fsm_set_stopping(bgp_connection connection, bgp_stopped_cause_t cause,
BGP_CONNECTION_SESSION_UNLOCK(connection) ; /*>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
} ;
-
-
-
-
-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.
*
@@ -1743,7 +2085,7 @@ bgp_fsm_get_sibling(bgp_connection connection)
*/
/* Forward reference */
-static inline void
+static void
bgp_timer_set(bgp_connection connection, qtimer timer, unsigned secs,
int jitter, qtimer_action* action) ;
@@ -1759,36 +2101,80 @@ static qtimer_action bgp_keepalive_timer_action ;
* 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.
- *
+ * connection->state == current (soon to be old) state
*
+ * Set and unset all the connection timers as required by the new state of
+ * the connection -- which may depend on the current state.
*
* 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 ;
+ bgp_connection sibling ;
+ unsigned interval ;
+ bgp_session session = connection->session ;
switch (new_state)
{
/* 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.
+ * In Idle state:
+ *
+ * either: 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.
+ * or: the connection is comatose, in which case will stay that way
+ * until sibling connection also falls back to Idle (from
+ * OpenSent/OpenConfirm.
+ *
+ * When entering Idle from anything other than Initial state, and not
+ * falling into a coma, extend the IdleHoldTimer.
*
* In Idle state refuses connections.
*/
case bgp_fsm_Idle:
- bgp_timer_set(connection, &connection->hold_timer,
- (session->idle_hold_timer_interval | 1), 1,
+ interval = session->idle_hold_timer_interval ;
+ sibling = bgp_connection_get_sibling(connection) ;
+
+ if (connection->state == bgp_fsm_Initial)
+ interval = (interval > 0) ? interval : 1 ; /* may not be zero */
+ else
+ {
+ if ( (sibling != NULL)
+ && ( (sibling->state == bgp_fsm_OpenSent)
+ || (sibling->state == bgp_fsm_OpenConfirm) ) )
+ {
+ interval = 0 ; /* unset the HoldTimer */
+ connection->comatose = 1 ; /* so now comatose */
+ }
+ else
+ {
+ /* increase the IdleHoldTimer interval */
+ interval *= 2 ;
+
+ if (interval < 4) /* enforce this minimum */
+ interval = 4 ;
+ else if (interval > 120)
+ interval = 120 ;
+
+ session->idle_hold_timer_interval = interval ;
+
+ /* if sibling is comatose, set time for it to come round */
+
+ if ((sibling != NULL) && (sibling->comatose))
+ {
+ connection->comatose = 0 ; /* no longer comatose */
+ bgp_timer_set(sibling, &sibling->hold_timer, interval, 1,
+ bgp_idle_hold_timer_action) ;
+ } ;
+ } ;
+ } ;
+
+ bgp_timer_set(connection, &connection->hold_timer, interval, 1,
bgp_idle_hold_timer_action) ;
+
qtimer_unset(&connection->keepalive_timer) ;
break;
@@ -1825,9 +2211,7 @@ bgp_fsm_state_change(bgp_connection connection, bgp_fsm_state_t new_state)
* * 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) ;
+ bgp_hold_timer_set(connection, session->open_hold_timer_interval) ;
qtimer_unset(&connection->keepalive_timer) ;
break;
@@ -1858,24 +2242,25 @@ bgp_fsm_state_change(bgp_connection connection, bgp_fsm_state_t new_state)
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) ;
+ bgp_hold_timer_set(connection, connection->hold_timer_interval) ;
break;
/* The connection is coming to an dead stop.
*
+ * If not sending a NOTIFICATION then stop HoldTimer now.
+ *
+ * Connections which are Stopping are no longer linked to a session.
*/
-
case bgp_fsm_Stopping:
- if (connection->notification_pending)
- bgp_timer_set(connection, &connection->hold_timer,
- 60, 1, bgp_hold_timer_action) ;
- else
+ if (!connection->notification_pending)
qtimer_unset(&connection->hold_timer) ;
qtimer_unset(&connection->keepalive_timer) ;
+ session->connections[connection->ordinal] = NULL ;
+ connection->session = NULL ;
+ connection->p_mutex = NULL ;
+
break ;
default:
@@ -1895,7 +2280,7 @@ bgp_fsm_state_change(bgp_connection connection, bgp_fsm_state_t new_state)
*
* If the interval is zero, unset the timer.
*/
-static inline void
+static void
bgp_timer_set(bgp_connection connection, qtimer timer, unsigned secs,
int jitter, qtimer_action* action)
{
@@ -1903,13 +2288,37 @@ bgp_timer_set(bgp_connection connection, qtimer timer, unsigned secs,
qtimer_unset(timer) ;
else
{
+ secs *= 40 ; /* a bit of resolution for jitter */
if (jitter)
secs -= ((rand() % ((int)secs + 1)) / 4) ;
- qtimer_set_interval(timer, QTIME(secs), action) ;
+ qtimer_set_interval(timer, QTIME(secs) / 40, action) ;
} ;
} ;
/*------------------------------------------------------------------------------
+ * Set HoldTimer with given time (without jitter) so will generate a
+ * Hold_Timer_expired event.
+ *
+ * Setting 0 will unset the HoldTimer.
+ */
+static void
+bgp_hold_timer_set(bgp_connection connection, unsigned secs)
+{
+ bgp_timer_set(connection, &connection->hold_timer, secs, 0,
+ bgp_hold_timer_action) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Recharge the HoldTimer
+ */
+
+static void
+bgp_hold_timer_recharge(bgp_connection connection)
+{
+ bgp_hold_timer_set(connection, connection->hold_timer_interval) ;
+} ;
+
+/*------------------------------------------------------------------------------
* BGP start timer action => bgp_fsm_BGP_Start event
*
* The timer is automatically unset, which is fine.
diff --git a/bgpd/bgp_fsm.h b/bgpd/bgp_fsm.h
index 1d530ee9..a606dd05 100644
--- a/bgpd/bgp_fsm.h
+++ b/bgpd/bgp_fsm.h
@@ -35,10 +35,15 @@ extern void
bgp_fsm_event(bgp_connection connection, bgp_fsm_event_t event) ;
extern void
+bgp_fsm_io_fatal_error(bgp_connection connection, int err) ;
+
+extern void
bgp_fsm_io_error(bgp_connection connection, int err) ;
extern void
-bgp_fsm_fd_closed(bgp_connection connection) ;
+bgp_fsm_connect_completed(bgp_connection connection, int err,
+ union sockunion* su_local,
+ union sockunion* su_remote) ;
extern void
bgp_fsm_stop_session(bgp_connection connection, bgp_stopped_cause_t cause,
diff --git a/bgpd/bgp_network.c b/bgpd/bgp_network.c
index d857c856..1a10d8ae 100644
--- a/bgpd/bgp_network.c
+++ b/bgpd/bgp_network.c
@@ -461,19 +461,14 @@ bgp_accept_action(qps_file qf, void* file_info)
connection = session->connections[bgp_connection_secondary] ;
if (ret == 0)
- {
- bgp_connection_open(connection, fd) ;
-
- memcpy(&connection->su_local, &su_local, sizeof(union sockunion)) ;
- memcpy(&connection->su_remote, &su_remote, sizeof(union sockunion)) ;
- }
+ bgp_connection_open(connection, fd) ;
else
close(fd) ;
BGP_SESSION_UNLOCK(session) ; /*<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
/* Now kick the FSM in an appropriate fashion */
- bgp_fsm_connect_completed(connection, ret) ;
+ bgp_fsm_connect_completed(connection, ret, &su_local, &su_remote) ;
} ;
/*==============================================================================
@@ -502,16 +497,16 @@ bgp_open_connect(bgp_connection connection)
unsigned int ifindex = 0 ;
int fd ;
int ret ;
- union sockunion* p_su = &connection->session->su_peer ;
+ union sockunion* su = connection->session->su_peer ;
/* Make socket for the connect connection. */
- fd = sockunion_socket(p_su) ;
+ fd = sockunion_socket(su) ;
if (fd < 0)
return errno ; /* give up immediately if cannot create socket */
/* Set the common options. */
- ret = bgp_socket_set_common_options(fd, p_su, connection->session->ttl,
- connection->session->password) ;
+ ret = bgp_socket_set_common_options(fd, su, connection->session->ttl,
+ connection->session->password) ;
/* Bind socket. */
if (ret == 0)
@@ -534,7 +529,7 @@ bgp_open_connect(bgp_connection connection)
/* Connect to the remote peer. */
if (ret == 0)
- ret = sockunion_connect(fd, p_su, connection->session->port, ifindex) ;
+ ret = sockunion_connect(fd, su, connection->session->port, ifindex) ;
/* does not report EINPROGRESS as an error. */
/* If not OK now, close the fd and signal the error */
@@ -543,7 +538,7 @@ bgp_open_connect(bgp_connection connection)
{
close(fd) ;
- bgp_fsm_connect_completed(connection, ret) ;
+ bgp_fsm_connect_completed(connection, ret, NULL, NULL) ;
return ;
} ;
@@ -607,6 +602,8 @@ bgp_connect_action(qps_file qf, void* file_info)
bgp_connection connection ;
int ret, err ;
socklen_t len = sizeof(err) ;
+ union sockunion su_remote ;
+ union sockunion su_local ;
connection = file_info ;
@@ -621,14 +618,13 @@ bgp_connect_action(qps_file qf, void* file_info)
else if (err != 0)
ret = err ;
else
- ret = bgp_getsockname(qps_file_fd(qf), &connection->su_local,
- &connection->su_remote) ;
+ ret = bgp_getsockname(qps_file_fd(qf), &su_local, &su_remote) ;
/* In any case, disable both read and write for this file. */
qps_disable_modes(qf, qps_write_mbit | qps_read_mbit) ;
/* Now kick the FSM in an appropriate fashion */
- bgp_fsm_connect_completed(connection, ret) ;
+ bgp_fsm_connect_completed(connection, ret, &su_local, &su_remote) ;
} ;
/*==============================================================================
@@ -920,7 +916,7 @@ bgp_md5_set_listeners(bgp_connection connection)
bgp_listener listener ;
int ret ;
- union sockunion* su = &connection->session->su_peer ;
+ union sockunion* su = connection->session->su_peer ;
#ifdef HAVE_IPV6
assert((su->sa.sa_family == AF_INET) || (su->sa.sa_family == AF_INET6)) ;
diff --git a/bgpd/bgp_open_state.h b/bgpd/bgp_open_state.h
index 8dc4fb3c..6715f416 100644
--- a/bgpd/bgp_open_state.h
+++ b/bgpd/bgp_open_state.h
@@ -55,7 +55,7 @@ struct bgp_open_state
{
as_t my_as ; /* generic ASN */
unsigned holdtime ; /* in seconds */
- bgp_id_t bgp_id ; /* eg an IPv4 address as integer */
+ bgp_id_ht bgp_id ; /* an IPv4 address as *host* uint32_t */
int can_capability ; /* false => don't send capabilities */
diff --git a/bgpd/bgp_session.c b/bgpd/bgp_session.c
index 4d19308f..f21743ca 100644
--- a/bgpd/bgp_session.c
+++ b/bgpd/bgp_session.c
@@ -23,6 +23,7 @@
#include "bgpd/bgp_peer.h"
#include "lib/memory.h"
+#include "lib/sockunion.h"
/*==============================================================================
* BGP Session.
@@ -123,7 +124,7 @@ bgp_session_enable(bgp_session session, bgp_peer peer)
session->ttl = peer->ttl ;
session->port = peer->port ;
-//session->su = peer->su ;
+ session->su_peer = sockunion_dup(&peer->su) ;
session->log = peer->log ;
session->host = peer->host ;
@@ -137,7 +138,7 @@ bgp_session_enable(bgp_session session, bgp_peer peer)
/* Initialise the BGP Open negotiating position */
- session->router_id = peer->local_id ;
+ /*....*/
/* Now pass the session to the BGP Engine, which will set about */
/* making and running a connection to the peer. */
diff --git a/bgpd/bgp_session.h b/bgpd/bgp_session.h
index 3942ae7d..7b8bcbd7 100644
--- a/bgpd/bgp_session.h
+++ b/bgpd/bgp_session.h
@@ -91,7 +91,7 @@ struct bgp_session
bgp_notify notification ; /* if any sent/received */
bgp_open_state open_send ; /* how to open the session */
- bgp_open_state open_recv ; /* how session was opened */
+ bgp_open_state open_recv ; /* set when session Established */
int connect ; /* initiate connections */
int listen ; /* listen for connections */
@@ -100,9 +100,10 @@ struct bgp_session
int ttl ; /* TTL to set, if not zero */
unsigned short port ; /* destination port for peer */
- union sockunion su_peer ; /* Sockunion address of the peer */
+ union sockunion* su_peer ; /* Sockunion address of the peer */
- struct in_addr router_id ;
+ union sockunion* su_local ; /* set when session Established */
+ union sockunion* su_remote ; /* set when session Established */
struct zlog* log ; /* where to log to */
char* host ; /* copy of printable peer's addr */