diff options
-rw-r--r-- | bgpd/bgp_common.h | 21 | ||||
-rw-r--r-- | bgpd/bgp_connection.c | 66 | ||||
-rw-r--r-- | bgpd/bgp_connection.h | 17 | ||||
-rw-r--r-- | bgpd/bgp_fsm.c | 1169 | ||||
-rw-r--r-- | bgpd/bgp_fsm.h | 7 | ||||
-rw-r--r-- | bgpd/bgp_network.c | 30 | ||||
-rw-r--r-- | bgpd/bgp_open_state.h | 2 | ||||
-rw-r--r-- | bgpd/bgp_session.c | 5 | ||||
-rw-r--r-- | bgpd/bgp_session.h | 7 |
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 */ |