diff options
author | Chris Hall <GMCH@hestia.halldom.com> | 2010-01-08 00:51:19 +0000 |
---|---|---|
committer | Chris Hall <GMCH@hestia.halldom.com> | 2010-01-08 00:51:19 +0000 |
commit | 6b5024b3c14b0acf5d4ec05f7ea78a7323c96d59 (patch) | |
tree | 89956e840ea5de81b5b564732032d93b95638af0 | |
parent | 18874dc91124118a678984241b18953beb8bb9d9 (diff) | |
download | quagga-6b5024b3c14b0acf5d4ec05f7ea78a7323c96d59.tar.bz2 quagga-6b5024b3c14b0acf5d4ec05f7ea78a7323c96d59.tar.xz |
Continuing work-in-progress
modified: bgpd/Makefile.am
modified: bgpd/bgp.h
modified: bgpd/bgp_common.h
modified: bgpd/bgp_connection.c
modified: bgpd/bgp_connection.h
modified: bgpd/bgp_engine.h
modified: bgpd/bgp_fsm.c
modified: bgpd/bgp_network.c
modified: bgpd/bgp_network.h
new file: bgpd/bgp_peer_index.c
new file: bgpd/bgp_peer_index.h
modified: bgpd/bgp_session.c
modified: bgpd/bgp_session.h
modified: lib/memtypes.c
modified: lib/mqueue.h
-rw-r--r-- | bgpd/Makefile.am | 4 | ||||
-rw-r--r-- | bgpd/bgp.h | 2 | ||||
-rw-r--r-- | bgpd/bgp_common.h | 62 | ||||
-rw-r--r-- | bgpd/bgp_connection.c | 31 | ||||
-rw-r--r-- | bgpd/bgp_connection.h | 12 | ||||
-rw-r--r-- | bgpd/bgp_engine.h | 19 | ||||
-rw-r--r-- | bgpd/bgp_fsm.c | 353 | ||||
-rw-r--r-- | bgpd/bgp_network.c | 78 | ||||
-rw-r--r-- | bgpd/bgp_network.h | 7 | ||||
-rw-r--r-- | bgpd/bgp_peer_index.c | 329 | ||||
-rw-r--r-- | bgpd/bgp_peer_index.h | 89 | ||||
-rw-r--r-- | bgpd/bgp_session.c | 298 | ||||
-rw-r--r-- | bgpd/bgp_session.h | 172 | ||||
-rw-r--r-- | lib/memtypes.c | 1 | ||||
-rw-r--r-- | lib/mqueue.h | 2 |
15 files changed, 1191 insertions, 268 deletions
diff --git a/bgpd/Makefile.am b/bgpd/Makefile.am index f1968c9c..f903c7ba 100644 --- a/bgpd/Makefile.am +++ b/bgpd/Makefile.am @@ -17,7 +17,7 @@ libbgp_a_SOURCES = \ bgp_dump.c bgp_snmp.c bgp_ecommunity.c bgp_mplsvpn.c bgp_nexthop.c \ bgp_peer.c bgp_damp.c bgp_table.c bgp_advertise.c bgp_vty.c \ bgp_engine.c bgp_session.c bgp_connection.c \ - bgp_common.c bgp_notification.c + bgp_common.c bgp_notification.c bgp_peer_index.c noinst_HEADERS = \ bgp_aspath.h bgp_attr.h bgp_community.h bgp_debug.h bgp_fsm.h \ @@ -26,7 +26,7 @@ noinst_HEADERS = \ bgp_ecommunity.h bgp_mplsvpn.h bgp_nexthop.h bgp_damp.h bgp_table.h \ bgp_peer.h bgp_advertise.h bgp_snmp.h bgp_vty.h \ bgp_engine.h bgp_session.h bgp_connection.h \ - bgp_common.h bgp_notification.h bgp.h + bgp_common.h bgp_notification.h bgp_peer_index.h bgp.h bgpd_SOURCES = bgp_main.c bgpd_LDADD = libbgp.a ../lib/libzebra.la @LIBCAP@ @LIBM@ @@ -91,7 +91,7 @@ * RFC4264 BGP Wedgies * RFC4098 Terminology for Benchmarking BGP Device Convergence in the * Control Plane - * RFC3822 Configuring BGP to BLock Denial-of-Service Attack + * RFC3822 Configuring BGP to Block Denial-of-Service Attack * RFC3765 NOPEER Community * RFC3392 ...see RFC5492 -- obsoletes RFC2842 * RFC3345 BGP Persistent Route Oscillation Condition diff --git a/bgpd/bgp_common.h b/bgpd/bgp_common.h index b3b58cac..15fc026a 100644 --- a/bgpd/bgp_common.h +++ b/bgpd/bgp_common.h @@ -48,8 +48,9 @@ typedef struct bgp_connection* bgp_connection ; typedef struct bgp_open_state* bgp_open_state ; /*============================================================================== - * Both session and connection require this. + * Both session and connection require these */ + typedef enum bgp_connection_ordinal bgp_connection_ordinal_t ; enum bgp_connection_ordinal { @@ -59,31 +60,54 @@ enum bgp_connection_ordinal bgp_connection_count = 2 } ; -/*============================================================================== - * Both session and connection require this. - */ -typedef enum bgp_stopped_causes bgp_stopped_cause_t ; -enum bgp_stopped_causes +typedef enum bgp_session_states bgp_session_state_t ; +enum bgp_session_states +{ + bgp_session_min_state = 0, + + bgp_session_sIdle = 0, /* session contents "unset" */ + + bgp_session_sEnabled = 1, /* attempting to connect */ + bgp_session_sEstablished = 2, + + bgp_session_sStopped = 3, /* for whatever reason */ + + bgp_session_max_state = 3 +} ; + +typedef enum bgp_session_events bgp_session_event_t ; +enum bgp_session_events { - bgp_stopped_min_cause = 0, + bgp_session_min_event = 0, + bgp_session_null_event = 0, + + bgp_session_eEnabled, /* enabled by Peering Engine */ + + bgp_session_eStart, /* coming out of fsm_Idle */ + bgp_session_eRetry, /* loop round in Connect/Accept */ + + bgp_session_eTCP_connect, /* successful Connect/Accept */ + + bgp_session_eCollision, /* connection closed to resolve collision */ - bgp_stopped_not = 0, /* not stopped (yet) */ + bgp_session_eOpen_accept, /* accepted an OPEN message */ + bgp_session_eOpen_reject, /* had to reject an OPEN message */ + bgp_session_eEstablished, /* session state -> sEstablished */ - bgp_stopped_admin = 1, /* Routeing Engine Stop */ - /* Sent Cease NOTIFICATION */ + bgp_session_eInvalid_msg, /* BGP message invalid */ + bgp_session_eFSM_error, /* unexpected BGP message received */ + bgp_session_eNOM_recv, /* NOTIFICATION message received */ - bgp_stopped_collision = 2, /* Collision Resolution Stop */ - bgp_stopped_loser = 3, /* Loser in race to Established state */ + bgp_session_eTCP_failed, /* TCP connection failed to come up */ + bgp_session_eTCP_dropped, /* TCP connection dropped */ + bgp_session_eTCP_error, /* some socket level error */ - bgp_stopped_error = 4, /* */ - bgp_stopped_recv_nom = 5, /* Received NOTIFICATION */ + bgp_session_eExpired, /* HoldTime expired */ - bgp_stopped_connect_fail = 5, - bgo_stopped_connect_drop = 6, - bgp_stopped_fatal_error = 8, - bgp_stopped_invalid = 9, /* some internal error */ + bgp_session_eInvalid, /* invalid internal event */ + bgp_session_eDisabled, /* disabled by Peering Engine */ - bgp_stopped_max_cause = 8 + bgp_session_max_event = bgp_session_eDisabled } ; /*============================================================================== diff --git a/bgpd/bgp_connection.c b/bgpd/bgp_connection.c index 8517ccc0..445d6d18 100644 --- a/bgpd/bgp_connection.c +++ b/bgpd/bgp_connection.c @@ -441,7 +441,7 @@ bgp_connection_open(bgp_connection connection, int fd) /* If this is the secondary connection, do not accept any more. */ if (connection->ordinal == bgp_connection_secondary) - session->accept = 0 ; + bgp_connection_disable_accept(connection) ; /* Set the file going */ qps_add_file(p_bgp_engine->selection, &connection->qf, fd, connection) ; @@ -465,6 +465,26 @@ bgp_connection_open(bgp_connection connection, int fd) } ; /*------------------------------------------------------------------------------ + * Enable connection for accept() + * + */ +extern void +bgp_connection_enable_accept(bgp_connection connection) +{ + connection->session->index_entry->accept = connection->session ; +} ; + +/*------------------------------------------------------------------------------ + * Disable connection for accept() + * + */ +extern void +bgp_connection_disable_accept(bgp_connection connection) +{ + connection->session->index_entry->accept = NULL ; +} ; + +/*------------------------------------------------------------------------------ * Close connection. * * * if there is an fd, close it @@ -473,6 +493,7 @@ bgp_connection_open(bgp_connection connection, int fd) * * unset any timers * * reset all buffering to empty * * empties the pending queue -- destroying all messages + * * for secondary connection: disable accept * * The following remain: * @@ -505,9 +526,13 @@ bgp_connection_close(bgp_connection connection) if (fd != fd_undef) shutdown(fd, SHUT_RDWR) ; + /* If this is the secondary connection, do not accept any more. */ + if (connection->ordinal == bgp_connection_secondary) + bgp_connection_disable_accept(connection) ; + /* forget any addresses */ - sockunion_clear(&connection->su_local) ; - sockunion_clear(&connection->su_remote) ; + sockunion_clear(connection->su_local) ; + sockunion_clear(connection->su_remote) ; /* Unset all the timers */ qtimer_unset(&connection->hold_timer) ; diff --git a/bgpd/bgp_connection.h b/bgpd/bgp_connection.h index d293f1e3..5e1ce349 100644 --- a/bgpd/bgp_connection.h +++ b/bgpd/bgp_connection.h @@ -131,13 +131,13 @@ struct bgp_connection int fsm_active ; /* active in fsm count */ bgp_fsm_event_t post ; /* event raised within FSM */ - bgp_stopped_cause_t stopped ; /* why stopped */ - bgp_notify notification ; /* if any sent/received */ + bgp_session_event_t except ; /* exception */ + bgp_notify notification ; /* if any sent/received */ + int err ; /* erno, if any */ bgp_open_state open_recv ; /* the open received. */ 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 */ @@ -179,6 +179,12 @@ bgp_connection_reset(bgp_connection connection, int free_structure) ; extern void bgp_connection_open(bgp_connection connection, int fd) ; +extern void +bgp_connection_enable_accept(bgp_connection connection) ; + +extern void +bgp_connection_disable_accept(bgp_connection connection) ; + extern bgp_connection bgp_connection_get_sibling(bgp_connection connection) ; diff --git a/bgpd/bgp_engine.h b/bgpd/bgp_engine.h index 9245d21a..57780dc5 100644 --- a/bgpd/bgp_engine.h +++ b/bgpd/bgp_engine.h @@ -46,5 +46,24 @@ bgp_engine_start(void) ; +/*============================================================================== + * + */ + +/* Send given message to the BGP Engine -- ordinary + */ +Inline void +bgp_to_engine(mqueue_block mqb) +{ + mqueue_enqueue(p_bgp_engine->queue, mqb, 0) ; +} ; + +/* Send given message to the BGP Engine -- priority + */ +Inline void +bgp_to_engine_priority(mqueue_block mqb) +{ + mqueue_enqueue(p_bgp_engine->queue, mqb, 1) ; +} ; #endif /* QUAGGA_BGP_ENGINE_H */ diff --git a/bgpd/bgp_fsm.c b/bgpd/bgp_fsm.c index f2b89576..bd2ef82a 100644 --- a/bgpd/bgp_fsm.c +++ b/bgpd/bgp_fsm.c @@ -158,10 +158,7 @@ * OPEN messages from a given remote end IP address MUST MATCH ! * *------------------------------------------------------------------------------ - * Error Handling, NOTIFICATIONs and Collision Resolution. - * - * I/O and other operations may fail. The other end may close an open TCP - * connection, with or without a NOTIFICATION message. + * Exception Handling. * * The FSM proceeds in three basic phases: * @@ -214,9 +211,9 @@ * 2) attempting to establish a BGP session: OpenSent/OpenConfirm * * If something goes wrong, or the other end closes the connection (with - * or without notification) it will loop back to Idle state. Also, when - * collision resolution closes one connection it too loops back to Idle - * (see above). + * or without notification) the FSM will loop back to Idle state. Also, + * when collision resolution closes one connection it too loops back to + * Idle (see above). * * Both connections may reach OpenSent. Only one at once can reach * OpenConfirm -- collision resolution sees to that. @@ -231,10 +228,13 @@ * * When things do go wrong, one of the following events is generated: * - * a. BGP_Stop + * a. BGP_Stop -- general exception + * + * The function bgp_fsm_exception() sets the reason for the exception and + * raises an BGP_Stop event. * - * The functions bgp_fsm_stop_connection() and bgp_fsm_stop_session() - * set the cause of stop and generate a BGP_Stop event for one or both + * Within the FSM, bgp_fsm_set_exception() sets the reason for the + * exception and ..... * connections. These may be used, for example, to signal that an UPDATE * message is invalid. * @@ -278,14 +278,16 @@ * * The function bgp_fsm_io_fatal_error() will generate a TCP_fatal_error. * - * e. + * Things may also go wrong withing the FSM. + * + * The procedure for dealing with an exception * *------------------------------------------------------------------------------ * FSM errors * - * If the FSM receives an event that cannot be raised in the current state, - * it will terminate the session, sending an FSM Error NOTIFICATION (if a - * TCP connection is up). See bgp_fsm_invalid(). + * Invalid events: if the FSM receives an event that cannot be raised in the + * current state, it will terminate the session, sending an FSM Error + * NOTIFICATION (if a TCP connection is up). See bgp_fsm_invalid(). * * If the FSM receives a message type that is not expected in the current, * state, it will close the connection (if OpenSent or OpenConfirm) or stop @@ -345,6 +347,39 @@ * * When the HoldTimer expires close the connection completely (whether or not * the NOTIFICATION has cleared the write buffer). + * + *------------------------------------------------------------------------------ + * Communication with the Routeing Engine + * + * The FSM sends the following messages to the Routeing Engine: + * + * * bgp_session_event messages + * + * These keep the Routeing Engine up to date with the progress and state of + * the FSM. + * + * In particular, these event messages tell the Routeing Engine when the + * session enters and leaves sEstablished -- which is what really matters + * to it ! + * + * * bgp_session_update + * + * Each time an update message arrives from the peer, it is forwarded. + * + * TODO: flow control for incoming updates ?? + * + * Three things bring the FSM to a dead stop, and stop the session: + * + * 1) administrative Stop -- ie the Routeing Engine disabling the session. + * + * 2) invalid events -- which are assumed to be bugs, really. + * + * 3) anything that stops the session while in Established state. + * + * This means that the FSM will plough on trying to establish connections with + * configured peers, even in circumstances when the likelihood of success + * appears slim to vanishing. However, the Routeing Engine and the operator + * are responsible for the decision to start and to stop trying to connect. */ /*============================================================================== @@ -360,10 +395,8 @@ bgp_fsm_enable_connection(bgp_connection connection) bgp_fsm_event(connection, bgp_fsm_BGP_Start) ; } ; - - /*============================================================================= - * BGP_Stop Events + * Raising exceptions. * * Before generating a BGP_Stop event the cause of the stop MUST be set for * the connection. @@ -390,26 +423,11 @@ bgp_fsm_enable_connection(bgp_connection connection) */ static void -bgp_fsm_set_stopping(bgp_connection connection, bgp_stopped_cause_t cause, - bgp_notify notification, int both) ; +bgp_fsm_throw_exception(bgp_connection connection, bgp_session_event_t except, + bgp_notify notification, int err, bgp_fsm_event_t event) ; /*------------------------------------------------------------------------------ - * Bring given connection to a stop. - * - * If is the only connection for the session, then the session is stopped. - * - * Is given the reasons for the stop. This function looks after releasing the - * notification once it is finished with it. - * - * Records the reasons for the stop, and then generates a BGP_Stop event. - * - * This may be used to stop: - * - * * as requested by Routeing Engine. The notification, if any, should be a - * Cease. - * - * * because a problem with a BGP packet has arisen. The notification, if - * any, will describe the problem. + * Raise a general exception -- not I/O related. * * Note that I/O problems are signalled by bgp_fsm_io_error(). * @@ -418,49 +436,43 @@ bgp_fsm_set_stopping(bgp_connection connection, bgp_stopped_cause_t cause, * NB: locks and unlocks the session. */ extern void -bgp_fsm_stop_connection(bgp_connection connection, bgp_stopped_cause_t cause, +bgp_fsm_raise_exception(bgp_connection connection, bgp_session_event_t except, bgp_notify notification) { - bgp_fsm_set_stopping(connection, cause, notification, 0) ; - bgp_fsm_event(connection, bgp_fsm_BGP_Stop) ; + bgp_fsm_throw_exception(connection, except, notification, 0, + bgp_fsm_BGP_Stop) ; } ; /*------------------------------------------------------------------------------ - * Bring given connection to a stop, and the other connection (if any), and - * then the session. + * Raise a NOTIFICATION received exception * - * See bgp_fsm_stop_connection, above. + * NB: locks and unlocks the session. */ extern void -bgp_fsm_stop_session(bgp_connection connection, bgp_stopped_cause_t cause, +bgp_fsm_notification_exception(bgp_connection connection, bgp_notify notification) { - bgp_fsm_set_stopping(connection, cause, notification, 1) ; - bgp_fsm_event(connection, bgp_fsm_BGP_Stop) ; + bgp_fsm_throw_exception(connection, bgp_session_eNOM_recv, notification, 0, + bgp_fsm_Receive_NOTIFICATION_message) ; } ; -/*============================================================================== - * Functions to signal I/O error(s) and connect()/accept() completion. - */ - /*------------------------------------------------------------------------------ - * Signal a fatal I/O error on the given connection. + * Raise a "fatal I/O error" exception on the given connection. * * Error to be reported as "TCP_fatal_error". */ extern void bgp_fsm_io_fatal_error(bgp_connection connection, int err) { - connection->err = err ; - plog_err (connection->log, "%s [Error] bgp IO error: %s", connection->host, safe_strerror(err)) ; - bgp_fsm_event(connection, bgp_fsm_TCP_fatal_error) ; + bgp_fsm_throw_exception(connection, bgp_session_eTCP_error, NULL, err, + bgp_fsm_TCP_fatal_error) ; } ; /*------------------------------------------------------------------------------ - * Signal an I/O error on the given connection. + * Raise an "I/O error" exception on the given connection. * * This is used by read/write operations -- so not until the TCP connection * is up (which implies OpenSent state or later). @@ -479,8 +491,6 @@ bgp_fsm_io_fatal_error(bgp_connection connection, int err) extern void bgp_fsm_io_error(bgp_connection connection, int err) { - connection->err = err ; - if ( (err == 0) || (err == ECONNRESET) || (err == ENETDOWN) @@ -501,7 +511,8 @@ bgp_fsm_io_error(bgp_connection connection, int err) safe_strerror(err)) ; } ; - bgp_fsm_event(connection, bgp_fsm_TCP_connection_closed) ; + bgp_fsm_throw_exception(connection, bgp_session_eTCP_dropped, NULL, err, + bgp_fsm_TCP_connection_closed) ; } else bgp_fsm_io_fatal_error(connection, err) ; @@ -530,8 +541,6 @@ bgp_fsm_connect_completed(bgp_connection connection, int err, union sockunion* su_local, union sockunion* su_remote) { - connection->err = err ; - if (err == 0) { bgp_fsm_event(connection, bgp_fsm_TCP_connection_open) ; @@ -543,11 +552,35 @@ bgp_fsm_connect_completed(bgp_connection connection, int err, || (err == ECONNRESET) || (err == EHOSTUNREACH) || (err == ETIMEDOUT) ) - bgp_fsm_event(connection, bgp_fsm_TCP_connection_open_failed) ; + bgp_fsm_throw_exception(connection, bgp_session_eTCP_failed, NULL, err, + bgp_fsm_TCP_connection_open_failed) ; else bgp_fsm_io_fatal_error(connection, err) ; } ; +/*------------------------------------------------------------------------------ + * Post the given exception. + */ +static void +bgp_fsm_post_exception(bgp_connection connection, bgp_session_event_t except, + bgp_notify notification, int err) +{ + connection->except = except ; + connection->notification = notification ; + connection->err = err ; +} ; + +/*------------------------------------------------------------------------------ + * Post the given exception and raise the given event. + */ +static void +bgp_fsm_throw_exception(bgp_connection connection, bgp_session_event_t except, + bgp_notify notification, int err, bgp_fsm_event_t event) +{ + bgp_fsm_post_exception(connection, except,notification, err) ; + bgp_fsm_event(connection, event) ; +} ; + /*============================================================================== * For debug... */ @@ -565,7 +598,8 @@ bgp_fsm_connect_completed(bgp_connection connection, int err, typedef bgp_fsm_action(bgp_fsm_action_func) ; -struct bgp_fsm { +struct bgp_fsm +{ bgp_fsm_action_func* action ; bgp_fsm_state_t next_state ; } ; @@ -1413,7 +1447,16 @@ bgp_fsm_event(bgp_connection connection, bgp_fsm_event_t event) session = connection->session ; if (session != NULL) - BGP_SESSION_LOCK(session) ; /*<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ + { + BGP_SESSION_LOCK(session) ; /*<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ + if (session->event != bgp_session_null_event) + { + session->event = bgp_session_null_event ; + session->err = 0 ; + if (session->notification != NULL) + bgp_notify_free(session->notification) ; + } ; + } ; do { @@ -1459,6 +1502,11 @@ bgp_fsm_event(bgp_connection connection, bgp_fsm_event_t event) } while (--connection->fsm_active != 0) ; + if (session->event != bgp_session_null_event) + { + + } + if (session != NULL) BGP_SESSION_UNLOCK(session) ; /*<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ } ; @@ -1479,6 +1527,9 @@ bgp_fsm_get_sibling(bgp_connection connection) ; static bgp_fsm_state_t bgp_fsm_send_notification(bgp_connection connection, bgp_notify notification) ; +static bgp_fsm_state_t +bgp_fsm_catch_exception(bgp_connection connection, bgp_fsm_state_t next_state) ; + /*------------------------------------------------------------------------------ * Null action -- do nothing at all. */ @@ -1497,6 +1548,11 @@ static bgp_fsm_action(bgp_fsm_null) */ static bgp_fsm_action(bgp_fsm_enter) { + if (connection->ordinal == bgp_connection_secondary) + bgp_prepare_to_accept(connection) ; + + bgp_fsm_post_session_event(connection, bgp_session_eEnabled, NULL, 0) ; + return next_state ; } ; @@ -1520,41 +1576,6 @@ static bgp_fsm_action(bgp_fsm_enter) */ static bgp_fsm_action(bgp_fsm_stop) { - if (connection->stopped == bgp_stopped_not) - return bgp_fsm_invalid(connection, next_state, event) ; - - /* If there is a NOTIFICATION to send, now is the time to do that. - * - * NB: can only be notification pending if TCP connection is up (OpenSent, - * OpenConfirm or Established. - * - * If do send N - * - * Otherwise, close the connection. - */ - if (connection->notification_pending) - next_state = bgp_fsm_send_notification(connection, - connection->notification) ; - else - bgp_connection_close(connection) ; - - /* If current state is Established, or if are stopped because of any of: - * - * bgp_stopped_admin -- stopped by Routeing Engine - * bgp_stopped_loser -- other connection won race to Established state - * bgp_stopped_invalid -- invalid event or action - * - * then we force transition to Stopping state. - */ - - if ( (connection->state == bgp_fsm_Established) - || (connection->stopped == bgp_stopped_admin) - || (connection->stopped == bgp_stopped_loser) - || (connection->stopped == bgp_stopped_invalid) ) - next_state = bgp_fsm_Stopping ; /* force transition */ - - /* Done */ - return next_state ; } ; @@ -1573,10 +1594,10 @@ static bgp_fsm_action(bgp_fsm_invalid) plog_debug(connection->log, "%s [FSM] invalid event %d in state %d", connection->host, event, connection->state) ; - bgp_fsm_set_stopping(connection, bgp_stopped_invalid, - bgp_notify_new(BGP_NOMC_FSM, BGP_NOMS_UNSPECIFIC, 0), 1) ; + bgp_fsm_post_exception(connection, bgp_session_eInvalid, + bgp_notify_new(BGP_NOMC_FSM, BGP_NOMS_UNSPECIFIC, 0), 0) ; - return bgp_fsm_Stopping ; /* unconditionally */ + return bgp_fsm_catch_exception(connection, next_state) ; } ; /*------------------------------------------------------------------------------ @@ -1610,9 +1631,11 @@ static bgp_fsm_action(bgp_fsm_start) else { next_state = bgp_fsm_Active ; - connection->session->accept = 1 ; + bgp_connection_enable_accept(connection) ; } ; + bgp_fsm_post_session_event(connection, bgp_session_eStart, NULL, 0) ; + return next_state ; } ; @@ -1644,15 +1667,18 @@ static bgp_fsm_action(bgp_fsm_send_open) bgp_msg_send_open(connection, connection->session->open_send) ; + bgp_fsm_post_session_event(connection, bgp_session_eTCP_connect, NULL, 0) ; + return next_state ; } ; /*------------------------------------------------------------------------------ * TCP connection has failed to come up -- Connect/Active states. * - * Close the connection (doesn't do much if secondary connection). + * This is in response to TCP_connection_open_failed, which has posted the + * exception -- so now need to deal with it. * - * If secondary connection, disable accept. + * Close the connection -- if secondary connection, disable accept. * * Will stay in Connect/Active states. * @@ -1662,20 +1688,16 @@ static bgp_fsm_action(bgp_fsm_failed) { bgp_connection_close(connection) ; - if (connection->ordinal == bgp_connection_secondary) - connection->session->accept = 0 ; - - return next_state ; + return bgp_fsm_catch_exception(connection, next_state) ; } ; /*------------------------------------------------------------------------------ * Fatal I/O error -- any state (other than Idle and Stopping). * - * Close the connection (if any). + * Close the connection (if any) -- if secondary connection, disable accept. * - * If secondary connection, disable accept. - * - * If about to stop: set bgp_stopped_fatal_error + * This is in response to TCP_fatal_error, which has posted the + * exception -- so now need to deal with it. * * NB: the session is locked. */ @@ -1683,13 +1705,7 @@ static bgp_fsm_action(bgp_fsm_fatal) { bgp_connection_close(connection) ; - if (connection->ordinal == bgp_connection_secondary) - connection->session->accept = 0 ; - - if (next_state == bgp_fsm_Stopping) - connection->stopped = bgp_stopped_fatal_error ; - - return next_state ; + return bgp_fsm_catch_exception(connection, next_state) ; } ; /*------------------------------------------------------------------------------ @@ -1710,8 +1726,7 @@ static bgp_fsm_action(bgp_fsm_retry) { bgp_connection_close(connection) ; - if (connection->ordinal == bgp_connection_secondary) - connection->session->accept = 0 ; + bgp_fsm_post_session_event(connection, bgp_session_eRetry, NULL, 0) ; return bgp_fsm_start(connection, next_state, event) ; } ; @@ -1719,7 +1734,8 @@ static bgp_fsm_action(bgp_fsm_retry) /*------------------------------------------------------------------------------ * TCP connection has closed -- OpenSent/OpenConfirm/Established states * - * This is used when a TCP connection has come up, but has simply closed. + * This is in response to TCP_connection_closed, which has posted the + * exception -- so now need to deal with it. * * NB: the session is locked. */ @@ -1727,10 +1743,7 @@ static bgp_fsm_action(bgp_fsm_closed) { bgp_connection_close(connection) ; - if (connection->ordinal == bgp_connection_secondary) - connection->session->accept = 0 ; - - return next_state ; + return bgp_fsm_catch_exception(connection, next_state) ; } ; /*------------------------------------------------------------------------------ @@ -1753,10 +1766,11 @@ static bgp_fsm_action(bgp_fsm_expire) return next_state ; } ; - /* Otherwise: send NOTIFICATION */ - bgp_fsm_send_notification(connection, - bgp_notify_new(BGP_NOMC_HOLD_EXP, BGP_NOMS_UNSPECIFIC, 0)) ; - return next_state ; + /* Otherwise: raise exception */ + bgp_fsm_post_exception(connection, bgp_session_eExpired, + bgp_notify_new(BGP_NOMC_HOLD_EXP, BGP_NOMS_UNSPECIFIC, 0), 0) ; + + return bgp_fsm_catch_exception(connection, next_state) ; } ; /*------------------------------------------------------------------------------ @@ -1818,6 +1832,8 @@ static bgp_fsm_action(bgp_fsm_recv_open) /* If all is well, send a KEEPALIVE message to acknowledge the OPEN */ bgp_msg_send_keepalive(connection) ; + bgp_fsm_post_session_event(connection, bgp_session_eOpen_accept, NULL, 0) ; + /* Transition to OpenConfirm state */ return next_state ; } @@ -1838,31 +1854,27 @@ static bgp_fsm_action(bgp_fsm_recv_open) */ static bgp_fsm_action(bgp_fsm_error) { - bgp_fsm_send_notification(connection, - bgp_notify_new(BGP_NOMC_FSM, BGP_NOMS_UNSPECIFIC, 0)) ; - return next_state ; + bgp_fsm_post_exception(connection, bgp_session_eFSM_error, + bgp_notify_new(BGP_NOMC_FSM, BGP_NOMS_UNSPECIFIC, 0), 0) ; + + return bgp_fsm_catch_exception(connection, next_state) ; } ; /*------------------------------------------------------------------------------ * Receive NOTIFICATION from far end -- OpenSent/OpenConfirm/Established * - * For OpenSent/OpenConfirm will be going Idle. + * This is in response to Receive_NOTIFICATION_message, which has posted the + * exception -- so now need to deal with it. * - * For Established will be going Stopping -- bgp_stopped_recv_nom - * - * Next state will be same as current, except for Established, when will be - * Stopping. + * Next state will be Idle, except for Established, when will be Stopping. * * NB: the session is locked. */ static bgp_fsm_action(bgp_fsm_recv_nom) { - if (next_state == bgp_fsm_Stopping) - connection->stopped = bgp_stopped_recv_nom ; - bgp_connection_close(connection) ; - return next_state ; + return bgp_fsm_catch_exception(connection, next_state) ; } ; /*------------------------------------------------------------------------------ @@ -1916,7 +1928,7 @@ static bgp_fsm_action(bgp_fsm_establish) /* TODO: now would be a good time to withdraw the password from listener ? */ /* Set the session state -- tell the Routeing Engine the news */ - bgp_session_set_state(session, bgp_session_Established) ; + /*>>>>>>> signal event <<<<<*/ return next_state ; } ; @@ -1963,23 +1975,64 @@ static bgp_fsm_action(bgp_fsm_exit) } /*============================================================================== - * The FSM stopping management. + * Catching FSM Exceptions. + * + * Throwing/Posting Exceptions sets: + * + * connection->except ) + * connection->err ) which define the exception + * connection->notification ) + * + * The FSM adds the expected next_state. + * + * From all the above... + * * - * There are many ways in which a connection may be required to stop. + */ +static bgp_fsm_state_t +bgp_fsm_catch_exception(bgp_connection connection, bgp_fsm_state_t next_state) +{ + +if (connection->stopped == bgp_stopped_not) + return bgp_fsm_invalid(connection, next_state, event) ; + +/* If there is a NOTIFICATION to send, now is the time to do that. + * + * NB: can only be notification pending if TCP connection is up (OpenSent, + * OpenConfirm or Established. * - * The stopping of one connection may mean that the entire session should be - * stopped -- either because there is only one connection (eg when in - * Established state), or because this is an administrative stop, or because - * there has been a serious error, or because a notification received, or ... + * If do send N * + * Otherwise, close the connection. + */ +if (connection->notification_pending) + next_state = bgp_fsm_send_notification(connection, + connection->notification) ; +else + bgp_connection_close(connection) ; + +/* If current state is Established, or if are stopped because of any of: * + * bgp_stopped_admin -- stopped by Routeing Engine + * bgp_stopped_loser -- other connection won race to Established state + * bgp_stopped_invalid -- invalid event or action * + * then we force transition to Stopping state. */ +if ( (connection->state == bgp_fsm_Established) + || (connection->stopped == bgp_stopped_admin) + || (connection->stopped == bgp_stopped_loser) + || (connection->stopped == bgp_stopped_invalid) ) + next_state = bgp_fsm_Stopping ; /* force transition */ + +/* Done */ + + static void -bgp_fsm_set_stopping(bgp_connection connection, bgp_stopped_cause_t cause, - bgp_notify notification, int both) +bgp_fsm_set_exception(bgp_connection connection, bgp_session_event_t except, + bgp_notify notification, int err) ; { bgp_session session ; bgp_connection sibling ; @@ -2012,6 +2065,8 @@ bgp_fsm_set_stopping(bgp_connection connection, bgp_stopped_cause_t cause, BGP_CONNECTION_SESSION_UNLOCK(connection) ; /*>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ } ; +} ; + /*============================================================================== * The BGP connections timers handling. * diff --git a/bgpd/bgp_network.c b/bgpd/bgp_network.c index 1a10d8ae..3c1cf753 100644 --- a/bgpd/bgp_network.c +++ b/bgpd/bgp_network.c @@ -60,6 +60,9 @@ bgp_getsockname(int fd, union sockunion* su_local, union sockunion* su_remote) ; static int bgp_socket_set_common_options(int fd, union sockunion* su, int ttl, const char* password) ; +static int +bgp_md5_set_listeners(union sockunion* su, const char* password) ; + /*============================================================================== * Open and close the listeners. * @@ -334,16 +337,54 @@ bgp_init_listener(int sock, struct sockaddr *sa, socklen_t salen) /*------------------------------------------------------------------------------ * Prepare to accept() connection * - * Sets session->accept true -- so accept() action will accept the connection. + * If the session has a password, then this is where the listener(s) for the + * appropriate address family are told about the password. + * + * This is done shortly before the session is first enabled for accept(). * + * The effect is (probably) that the peer's attempts to connect with MD5 signed + * packets will simply have been ignored up to this point. From this point + * forward they will be accepted, but closed until accept is enabled. + * + * NB: requires the session mutex LOCKED. + */ +extern void +bgp_prepare_to_accept(bgp_connection connection) +{ + int ret ; + + if (connection->session->password != NULL) + { + ret = bgp_md5_set_listeners(connection->session->su_peer, + connection->session->password) ; + +/* TODO: failure to set password in bgp_prepare_to_accept ? */ + } ; + + return ; +} ; + +/*------------------------------------------------------------------------------ + * No longer prepared to accept() connection * + * If the session has a password, then this is where it is withdrawn from the + * listener(s) for the appropriate address family. * * NB: requires the session mutex LOCKED. */ extern void -bgp_open_accept(bgp_connection connection) +bgp_not_prepared_to_accept(bgp_connection connection) { + int ret ; + + if (connection->session->password != NULL) + { + ret = bgp_md5_set_listeners(connection->session->su_peer, NULL) ; + +/* TODO: failure to clear password in bgp_not_prepared_to_accept ? */ + } ; + return ; } ; /*------------------------------------------------------------------------------ @@ -420,7 +461,7 @@ bgp_accept_action(qps_file qf, void* file_info) /* See if we are ready to accept connections from the connecting party */ session = bgp_session_lookup(&su_remote, &exists) ; - if (bgp_session_is_accepting(session)) + if (session != NULL) { if (BGP_DEBUG(events, EVENTS)) zlog_debug(exists @@ -501,12 +542,12 @@ bgp_open_connect(bgp_connection connection) /* Make socket for the connect connection. */ fd = sockunion_socket(su) ; - if (fd < 0) - return errno ; /* give up immediately if cannot create socket */ + ret = (fd >= 0) ? 0 : errno ; /* Set the common options. */ - ret = bgp_socket_set_common_options(fd, su, connection->session->ttl, - connection->session->password) ; + if (ret == 0) + ret = bgp_socket_set_common_options(fd, su, connection->session->ttl, + connection->session->password) ; /* Bind socket. */ if (ret == 0) @@ -536,7 +577,8 @@ bgp_open_connect(bgp_connection connection) if (ret != 0) { - close(fd) ; + if (fd >= 0) + close(fd) ; bgp_fsm_connect_completed(connection, ret, NULL, NULL) ; @@ -895,29 +937,28 @@ bgp_md5_set_socket(int fd, union sockunion *su, const char *password) } ; /*------------------------------------------------------------------------------ - * Set MD5 password for given peer in the listener(s) for the peer's address - * family. - * - * NB: requires the session mutex LOCKED. + * Set (or clear) MD5 password for given peer in the listener(s) for the peer's + * address family. * * This allows system to accept MD5 "signed" incoming connections from the * given address. * + * NULL password clears the password for the given peer. + * * Returns: 0 => OK * otherwise: errno -- the first error encountered. * * NB: peer address must be AF_INET or (if supported) AF_INET6 * - * NB: if there are no listeners in the required + * NB: does nothing and returns "OK" if there are no listeners in the + * address family -- wanting to set MD5 makes no difference to this ! */ -extern int -bgp_md5_set_listeners(bgp_connection connection) +static int +bgp_md5_set_listeners(union sockunion* su, const char* password) { bgp_listener listener ; int ret ; - union sockunion* su = connection->session->su_peer ; - #ifdef HAVE_IPV6 assert((su->sa.sa_family == AF_INET) || (su->sa.sa_family == AF_INET6)) ; #else @@ -928,8 +969,7 @@ bgp_md5_set_listeners(bgp_connection connection) while (listener != NULL) { - ret = bgp_md5_set_socket(qps_file_fd(&listener->qf), su, - connection->session->password) ; + ret = bgp_md5_set_socket(qps_file_fd(&listener->qf), su, password) ; if (ret != 0) return ret ; } ; diff --git a/bgpd/bgp_network.h b/bgpd/bgp_network.h index d9966d7d..1edc2e0e 100644 --- a/bgpd/bgp_network.h +++ b/bgpd/bgp_network.h @@ -33,7 +33,10 @@ bgp_close_listeners(void) ; extern void bgp_open_connect(bgp_connection connection) ; -extern int -bgp_md5_set_listeners(bgp_connection connection) ; +extern void +bgp_prepare_to_accept(bgp_connection connection) ; + +extern void +bgp_not_prepared_to_accept(bgp_connection connection) ; #endif /* _QUAGGA_BGP_NETWORK_H */ diff --git a/bgpd/bgp_peer_index.c b/bgpd/bgp_peer_index.c new file mode 100644 index 00000000..2967d11f --- /dev/null +++ b/bgpd/bgp_peer_index.c @@ -0,0 +1,329 @@ +/* BGP Peer Index -- header + * Copyright (C) 2009 Chris Hall (GMCH), Highwayman + * + * This file is part of GNU Zebra. + * + * GNU Zebra is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * GNU Zebra is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Zebra; see the file COPYING. If not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "lib/zassert.h" + +#include "bgpd/bgp_peer_index.h" +#include "bgpd/bgp_peer.h" +#include "bgpd/bgp_session.h" + +#include "lib/symtab.h" +#include "lib/qpthreads.h" +#include "lib/sockunion.h" +#include "lib/memory.h" + +/*============================================================================== + * BGP Peer Index + * + * When peers are created, they are registered in the bgp_peer_index. When + * they are destroyed, they are removed. This is done by the Routeing Engine. + * + * The peer index is used by the Routeing Engine to lookup peers either by + * name (IP address) or by peer_id. + * + * The BGP Engine needs to lookup sessions when a listening socket accepts a + * connection -- first, to decide whether to continue with the connection, and + * second, to tie the connection to the right session. It uses the peer index + * to do this. + * + * A mutex is used to coordinate access to the index. + */ + +static struct symbol_table bgp_peer_index ; +static qpt_mutex_t bgp_peer_index_mutex ; + +static bgp_peer_index_entry bgp_peer_id_table = NULL ; +static bgp_peer_id_t bgp_peer_id_last = 0 ; + +CONFIRM(bgp_peer_id_null == 0) ; + +enum { bgp_peer_id_unit = 64 } ; /* allocate 64 at a time */ + +inline static void BGP_PEER_INDEX_LOCK(void) +{ + qpt_mutex_lock(&bgp_peer_index_mutex) ; +} ; + +inline static void BGP_PEER_INDEX_UNLOCK(void) +{ + qpt_mutex_unlock(&bgp_peer_index_mutex) ; +} ; + +/* Uses the entry zero in the bgp_peer_id_table to point at head and tail of + * list of free bgp_peer_id's. + * + * Uses the peer pointer in free entries to point to then next free. + */ +#define bgp_peer_id_table_free_head bgp_peer_id_table->peer +#define bgp_peer_id_table_free_tail bgp_peer_id_table->accept + +#define bgp_peer_id_table_free_next(entry) ((bgp_peer_index_entry)entry)->peer + +/* Forward references */ +static void bgp_peer_id_table_free_entry(bgp_peer_index_entry entry) ; +static void bgp_peer_id_table_free_ids(bgp_peer_id_t f, bgp_peer_id_t l) ; +static void bgp_peer_id_table_make_ids(void) ; + +/*------------------------------------------------------------------------------ + * Initialise the bgp_peer_index. + * + * This must be done before any peers are configured ! + */ +extern void +bgp_peer_index_init(void* parent) +{ + symbol_table_init_new( + &bgp_peer_index, + parent, + 10, /* start ready for a few sessions */ + 200, /* allow to be quite dense */ + sockunion_symbol_hash, /* "name" is an IP Address */ + NULL) ; /* no value change call-back */ + + bgp_peer_id_table = XCALLOC(MTYPE_BGP_PEER_ID_TABLE, + sizeof(struct bgp_peer_index_entry) * bgp_peer_id_unit) ; + + bgp_peer_id_table_free_head = NULL ; + + bgp_peer_id_last = bgp_peer_id_unit - 1 ; + bgp_peer_id_table_free_ids(1, bgp_peer_id_unit) ; +} ; + +/*------------------------------------------------------------------------------ + * Initialise the bgp_peer_index_mutex. + * + * This must be done as soon as any qpthreads are enabled. + */ +extern void +bgp_peer_index_mutex_init(void* parent) +{ + qpt_mutex_init(&bgp_peer_index_mutex, qpt_mutex_recursive) ; +} ; + +/*------------------------------------------------------------------------------ + * Register a peer in the peer index. + * + * For use by the Routeing Engine. + * + * NB: it is a FATAL error to register a peer for an address which is already + * registered. + */ +extern void +bgp_peer_index_register(bgp_peer peer, union sockunion* su) +{ + bgp_peer_index_entry entry ; + + BGP_PEER_INDEX_LOCK() ; /*<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ + + /* First need an entry, which allocates a peer_id. May need to extend */ + /* the bgp_peer_id_table -- so need to be locked for this. */ + + if (bgp_peer_id_table_free_head == NULL) + bgp_peer_id_table_make_ids() ; + + entry = (void*)bgp_peer_id_table_free_head ; + bgp_peer_id_table_free_head = (void*)bgp_peer_id_table_free_next(entry) ; + + /* Initialise the entry -- the id is already set */ + entry->peer = peer ; + entry->accept = NULL ; + assert(entry->id == (entry - bgp_peer_id_table)) ; + + /* Insert the new entry into the symbol table. */ + entry = symbol_set_value(symbol_find(&bgp_peer_index, su), entry) ; + + BGP_PEER_INDEX_UNLOCK() ; /*>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ + + passert(entry != NULL) ; +} ; + +/*------------------------------------------------------------------------------ + * Deregister a peer from the peer index. + * + * For use by the Routeing Engine. + * + * NB: The peer MUST NOT be deregistered if any of the following apply: + * + * * there is an active session + * + * * the peer_id is still in use (anywhere at all) + * + * NB: it is a FATAL error to deregister a peer which is not registered. + */ +extern void +bgp_peer_index_deregister(bgp_peer peer, union sockunion* su) +{ + bgp_peer_index_entry entry ; + symbol sym ; + + BGP_PEER_INDEX_LOCK() ; /*<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ + + sym = symbol_seek(&bgp_peer_index, su) ; + passert(sym != NULL) ; + + entry = symbol_delete(sym) ; + + passert((entry != NULL) && (entry->peer == peer) && (entry->accept == NULL)) ; + + bgp_peer_id_table_free_entry(entry) ; + + BGP_PEER_INDEX_UNLOCK() ; /*>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ +} ; + +/*------------------------------------------------------------------------------ + * Lookup a peer -- do nothing if does not exist + * + * For use by the Routeing Engine. + * + * Returns the bgp_peer -- NULL if not found. + */ +extern bgp_peer +bgp_peer_index_seek(union sockunion* su) +{ + bgp_peer_index_entry entry ; + + /* Only the Routing Engine can add/delete entries -- so no lock required */ + + entry = bgp_peer_index_seek_entry(su) ; + + return (entry != NULL) ? entry->peer : NULL ; +} ; + +/*------------------------------------------------------------------------------ + * Lookup a peer's peer index entry -- do nothing if does not exist + * + * For use by the Routeing Engine. + * + * Returns the bgp_peer_index_entry -- NULL if not found. + */ +extern bgp_peer_index_entry +bgp_peer_index_seek_entry(union sockunion* su) +{ + /* Only the Routing Engine can add/delete entries -- so no lock required */ + + return symbol_get_value(symbol_seek(&bgp_peer_index, su)) ; +} ; + +/*------------------------------------------------------------------------------ + * Lookup a session -- do nothing if does not exist + * + * For use by the BGP Engine. + * + * Returns: bgp_session if: peer with given address is configured + * and: the session is prepared to accept() + * + * or: NULL otherwise + * + * Sets *p_found <=> a peer with the given address is configured. + * + * NB: the BGP Engine may not access the bgp_session structure if it is not + * active (sEnabled or sEstablished), so the accept pointer in the peer + * index entry will be NULL under those conditions. + */ +extern bgp_session +bgp_session_index_seek(union sockunion* su, int* p_found) +{ + bgp_session accept ; + bgp_peer_index_entry entry ; + + BGP_PEER_INDEX_LOCK() ; /*<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ + + entry = symbol_get_value(symbol_seek(&bgp_peer_index, su)) ; + + *p_found = (entry != NULL) ; + accept = *p_found ? entry->accept : NULL ; + + BGP_PEER_INDEX_UNLOCK() ; /*>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ + + assert((accept == NULL) || (bgp_session_is_active(accept))) ; + + return accept ; +} ; + +/*============================================================================== + * Extending the bgp_peer_id_table and adding free entries to it. + */ + +/*------------------------------------------------------------------------------ + * Free the given peer index entry and release its peer_id. + */ +static void +bgp_peer_id_table_free_entry(bgp_peer_index_entry entry) +{ + assert((entry - bgp_peer_id_table) == entry->id) ; + + bgp_peer_id_table_free_ids(entry->id, entry->id) ; +} ; + +/*------------------------------------------------------------------------------ + * Free a range of peer_ids -- may be just one (of course) + * + * Adds to end of the peer_id free list, in peer_id order. + * + * This means that when individual peers are deleted, the ids will be re-used + * in the order they were deleted (but after any pool of ids has been used up. + */ +static void +bgp_peer_id_table_free_ids(bgp_peer_id_t f, bgp_peer_id_t l) +{ + bgp_peer_id_t id ; + bgp_peer_index_entry e ; + + assert(l <= bgp_peer_id_last) ; + + for (id = f ; id <= l ; ++id) + { + e = &bgp_peer_id_table[id] ; + + e->peer = NULL ; /* being tidy */ + e->accept = NULL ; + e->id = id ; /* for initial creation */ + + if (bgp_peer_id_table_free_head == NULL) + bgp_peer_id_table_free_head = (void*)e ; + else + bgp_peer_id_table_free_next(bgp_peer_id_table_free_tail) = (void*)e ; + + bgp_peer_id_table_free_tail = (void*)e ; + bgp_peer_id_table_free_next(e) = NULL ; + } ; +} ; + +/*------------------------------------------------------------------------------ + * Make a new set of free bgp_peer_ids. + * + * NB: the bgp_peer_id_table may well move around in memory ! + */ +static void +bgp_peer_id_table_make_ids(void) +{ + bgp_peer_id_t id_new ; + + id_new = bgp_peer_id_last + 1 ; + bgp_peer_id_last += bgp_peer_id_unit ; + + bgp_peer_id_table = XREALLOC(MTYPE_BGP_PEER_ID_TABLE, bgp_peer_id_table, + sizeof(struct bgp_peer_index_entry) * (bgp_peer_id_last + 1)) ; + + bgp_peer_id_table_free_ids(id_new, bgp_peer_id_last) ; +} ; + + diff --git a/bgpd/bgp_peer_index.h b/bgpd/bgp_peer_index.h new file mode 100644 index 00000000..ae41de24 --- /dev/null +++ b/bgpd/bgp_peer_index.h @@ -0,0 +1,89 @@ +/* BGP Peer Index -- header + * Copyright (C) 2009 Chris Hall (GMCH), Highwayman + * + * This file is part of GNU Zebra. + * + * GNU Zebra is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * GNU Zebra is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Zebra; see the file COPYING. If not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef _QUAGGA_BGP_PEER_INDEX_H +#define _QUAGGA_BGP_PEER_INDEX_H + +#include "bgpd/bgp_common.h" + +#include "lib/sockunion.h" + +/*============================================================================== + * The Peer Index maps: + * + * * IP address (name of peer) + * * peer_id (ordinal of peer) + * + * To the bgp_peer_index_entry. + */ +typedef struct bgp_peer_index_entry* bgp_peer_index_entry ; + +typedef unsigned bgp_peer_id_t ; + +struct bgp_peer_index_entry +{ + bgp_peer peer ; /* used by Peering Engine */ + + /* The accept pointer is used by the listening socket(s) to find the + * session when it is prepared to accept a connection. + * + * This pointer MUST be NULL when not sEnabled or sEstablished. It + * will be set by the BGP Engine when it decides to accept connections, + * and cleared by it otherwise (and when a session stops). + * + * An active session contains a pointer to the peer index entry to + * facilitate this. + */ + + bgp_session accept ; /* used by BGP Engine */ + + bgp_peer_id_t id ; /* maps IP address to peer_id */ +} ; + +enum { bgp_peer_id_null = 0 } ; /* no peer can have id == 0 */ + +/*============================================================================== + * + */ + +extern void +bgp_peer_index_init(void* parent) ; + +extern void +bgp_peer_index_mutex_init(void* parent) ; + +extern void +bgp_peer_index_register(bgp_peer peer, union sockunion* su) ; + +extern void +bgp_peer_index_deregister(bgp_peer peer, union sockunion* su) ; + +extern bgp_peer +bgp_peer_index_seek(union sockunion* su) ; + +extern bgp_peer_index_entry +bgp_peer_index_seek_entry(union sockunion* su) ; + +extern bgp_session +bgp_session_index_seek(union sockunion* su, int* p_found) ; + +#endif /* _QUAGGA_BGP_PEER_INDEX_H */ + diff --git a/bgpd/bgp_session.c b/bgpd/bgp_session.c index f21743ca..f77e9e34 100644 --- a/bgpd/bgp_session.c +++ b/bgpd/bgp_session.c @@ -21,6 +21,8 @@ #include "bgpd/bgp_session.h" #include "bgpd/bgp_peer.h" +#include "bgpd/bgp_engine.h" +#include "bgpd/bgp_peer_index.h" #include "lib/memory.h" #include "lib/sockunion.h" @@ -66,12 +68,25 @@ * */ -/* Initialise new session structure -- allocate if required. +/*------------------------------------------------------------------------------ + * Initialise new session structure -- allocate if required. * + * Ties peer and session together. Sets session sIdle, initialises mutex. + * + * Unsets everything else -- mostly by zeroising it. + * + * NB: if not allocating, the existing session MUST be sIdle/sStopped OR never + * been kissed. + * + * NB: in any event, the peer's peer index entry MUST have a NULL session + * pointer. */ extern bgp_session -bgp_session_init_new(bgp_session session) +bgp_session_init_new(bgp_session session, bgp_peer peer) { + assert(peer->session == NULL) ; + assert(peer->index_entry->session == NULL) ; + if (session == NULL) session = XCALLOC(MTYPE_BGP_SESSION, sizeof(struct bgp_session)) ; else @@ -79,8 +94,50 @@ bgp_session_init_new(bgp_session session) qpt_mutex_init_new(&session->mutex, qpt_mutex_recursive) ; - - + peer->session = session ; + session->peer = peer ; + session->state = bgp_session_sIdle ; + + session->index_entry = peer->index_entry ; + + /* Zeroising the structure has set: + * + * made -- false, not yet sEstablished + * + * event -- bgp_session_null_event + * notification -- NULL -- none + * err -- 0 -- none + * + * open_send -- NULL -- none + * open_recv -- NULL -- none + * + * connect -- unset, false + * listen -- unset, false + * + * ttl -- unset + * port -- unset + * su_peer -- NULL -- none + * + * log -- NULL -- none + * host -- NULL -- none + * password -- NULL -- none + * + * idle_hold_timer_interval ) + * connect_retry_timer_interval ) + * open_hold_timer_interval ) unset + * hold_timer_interval ) + * keepalive_timer_interval ) + * + * as4 -- unset, false + * + * su_local -- NULL -- none + * su_remote -- NULL -- none + * + * connections[] -- NULL -- none + */ + confirm(bgp_session_null_event == 0) ; + + return session ; } ; /* Look up session @@ -91,35 +148,46 @@ bgp_session_lookup(union sockunion* su, int* exists) ; - -/* Enable session for given peer -- allocate session if required. +/*============================================================================== + * Enable session for given peer -- allocate session if required. * * Sets up the session given the current state of the peer. If the state * changes, then.... * * */ +static void +bgp_session_do_enable(mqueue_block mqb, mqb_flag_t flag) ; -extern bgp_session -bgp_session_enable(bgp_session session, bgp_peer peer) +extern void +bgp_session_enable(bgp_peer peer) { - if (session == NULL) - session = bgp_session_init_new(session) ; + bgp_session session ; + mqueue_block mqb ; + + /* Set up session if required. Check session if already exists. + * + * Only the Peering Engine creates sessions, so it is safe to pick up the + * peer->session pointer and test it. + * + * If session exists, it MUST be inactive. + * + * Peering Engine does not require the mutex while the session is inactive. + */ + session = peer->session ; - /* Tie back to peer and set state of session. */ - - assert((session->peer == NULL) || (session->peer == peer)) ; - assert((session->state != bgp_session_Enabled) && - (session->state != bgp_session_Established)) ; - - session->peer = peer ; - session->state = bgp_session_Enabled ; + if (session == NULL) + session = bgp_session_init_new(NULL, peer) ; + else + { + assert(session->peer == peer) ; + assert(!bgp_session_is_active(session)) ; + } ; /* Initialise what we need to make and run connections */ session->connect = (peer->flags & PEER_FLAG_PASSIVE) != 0 ; session->listen = 1 ; - session->accept = 0 ; session->ttl = peer->ttl ; session->port = peer->port ; @@ -127,7 +195,7 @@ bgp_session_enable(bgp_session session, bgp_peer peer) session->su_peer = sockunion_dup(&peer->su) ; session->log = peer->log ; - session->host = peer->host ; + session->host = peer->host ; /* TODO: duplicate string ? */ session->idle_hold_timer_interval = peer->v_start ; session->connect_retry_timer_interval = peer->v_connect ; @@ -140,51 +208,205 @@ bgp_session_enable(bgp_session session, bgp_peer peer) /*....*/ + /* Routeing Engine does the state change now. */ + session->state = bgp_session_sEnabled ; + /* Now pass the session to the BGP Engine, which will set about */ /* making and running a connection to the peer. */ + mqb = mqb_init_new(NULL, bgp_session_do_enable, session) ; + confirm(sizeof(struct bgp_session_enable_args) == 0) ; + bgp_to_engine(mqb) ; } ; +/*------------------------------------------------------------------------------ + * BGP Engine: session enable message action + */ +static void +bgp_session_do_enable(mqueue_block mqb, mqb_flag_t flag) +{ + if (flag == mqb_action) + { + + + } ; + + mqb_free(mqb) ; +} ; /*============================================================================== - * Session data access functions. + * Disable session for given peer -- if enabled (!). + * + * Passes any bgp_notify to the BGP Engine, which will dispose of it in due + * course. * + * If no bgp_notify provided, will send Cease/Administrative Shutdown (2). * + * When session has been brought to a stop, BGP Engine will respond with an + * eDisabled event (unless session stopped of its own accord first). */ +static void +bgp_session_do_disable(mqueue_block mqb, mqb_flag_t flag) ; -extern int -bgp_session_is_active(bgp_session session) +extern void +bgp_session_disable(bgp_peer peer, bgp_notify notification) { - int ret ; + bgp_session session ; + mqueue_block mqb ; + + session = peer->session ; + assert((session != NULL) && (session->peer == peer)) ; + + /* Should do nothing if session is not active. */ + + if (!bgp_session_is_active(session)) + { + bgp_notify_free(notification) ; /* discard any bgp_notify */ + return ; + } ; + + /* Now ask the BGP engine to disable the session. + * + * NB: it is, of course, possible that the session will stop between + * issuing the disable and it being processed by the BGP Engine. + * + * The BGP Engine quietly discards disable messages for sessions which + * are not active. + * + * NB: The BGP Engine will discard any outstanding work for the session. + * + * The Peering Engine should discard all further messages for this + * session up to the event message that tells it the session has + * stopped. + */ + mqb = mqb_init_new(NULL, bgp_session_do_disable, session) ; + + confirm(sizeof(struct bgp_session_enable_args) == 0) ; + + bgp_to_engine_priority(mqb) ; +} ; - if (session == NULL) - return 0 ; /* NULL session is implicitly not active */ +/*------------------------------------------------------------------------------ + * BGP Engine: session enable message action + */ +static void +bgp_session_do_disable(mqueue_block mqb, mqb_flag_t flag) +{ + if (flag == mqb_action) + { - BGP_SESSION_LOCK(session) ; /*<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ - ret = ( (session->state == bgp_session_Enabled) - || (session->state == bgp_session_Established) ) ; + } ; - BGP_SESSION_UNLOCK(session) ; /*>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ + mqb_free(mqb) ; +} ; + +/*============================================================================== + * Send session event signal from BGP Engine to Routeing Engine + * + * + */ +extern void +bgp_session_event(bgp_session session, bgp_session_event_t event, + bgp_notify notification, + bgp_session_state_t state) +{ + struct bgp_session_event_args* args ; + mqueue_block mqb ; + + mqb = mqb_init_new(NULL, bgp_session_do_event, session) ; - return ret ; + args = mqb_get_args(mqb) ; + + args->event = event ; + args->notification = notification ; + args->state = state ; + + bgp_to_peering(mqb) ; +} + +/*============================================================================== + * Dispatch update to peer -- Peering Engine -> BGP Engine + * + * PRO TEM -- this is being passed the pre-packaged BGP message. + * + * The BGP Engine takes care of discarding the stream block once it's been + * dealt with. + */ +extern void +bgp_session_update_send(bgp_session session, struct stream* upd) +{ + struct bgp_session_update_args* args ; + mqueue_block mqb ; + + mqb = mqb_init_new(NULL, bgp_session_do_update_send, session) ; + + args = mqb_get_args(mqb) ; + + args->buf = upd ; + + bgp_to_engine(mqb) ; } ; -extern int -bgp_session_is_accepting(bgp_session session) +/*============================================================================== + * Forward incoming update -- BGP Engine -> Peering Engine + * + * PRO TEM -- this is being passed the raw BGP message. + * + * The Peering Engine takes care of discarding the stream block once it's been + * dealt with. + */ +extern void +bgp_session_update_recv(bgp_session session, struct stream* upd) { - int ret ; + struct bgp_session_update_args* args ; + mqueue_block mqb ; - if (session == NULL) - return 0 ; /* NULL session is implicitly not accepting */ + mqb = mqb_init_new(NULL, bgp_session_do_update_recv, session) ; + + args = mqb_get_args(mqb) ; + + args->buf = upd ; + + bgp_to_peering(mqb) ; +} ; + + + +/*============================================================================== + * Session data access functions. + * + * + */ + +/*------------------------------------------------------------------------------ + * See if session exists and is active. + * + * Ensure that if exists and is not active, that the peer index entry accept + * pointer is NULL -- this is largely paranoia, but it would be a grave + * mistake for the listening socket(s) to find a session which is not active ! + */ +extern int +bgp_session_is_active(bgp_session session) +{ + int active ; BGP_SESSION_LOCK(session) ; /*<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ - ret = session->accept ; + if (session == NULL) + active = 0 ; + else + { + active = ( (session->state == bgp_session_sEnabled) + || (session->state == bgp_session_sEstablished) ) ; + + if (!active) + assert(session->index_entry->accept == NULL) ; + } ; BGP_SESSION_UNLOCK(session) ; /*>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ - return ret ; + return active ; } ; diff --git a/bgpd/bgp_session.h b/bgpd/bgp_session.h index 7b8bcbd7..4b5f8ca7 100644 --- a/bgpd/bgp_session.h +++ b/bgpd/bgp_session.h @@ -28,10 +28,12 @@ #include "bgpd/bgp_engine.h" #include "bgpd/bgp_connection.h" #include "bgpd/bgp_notification.h" +#include "bgpd/bgp_peer_index.h" #include "lib/qtimers.h" #include "lib/qpthreads.h" #include "lib/sockunion.h" +#include "lib/mqueue.h" #ifndef Inline #define Inline static inline @@ -64,47 +66,84 @@ * */ -typedef enum bgp_session_states bgp_session_state_t ; -enum bgp_session_states -{ - bgp_session_min_state = 0, - - bgp_session_Idle = 0, - - bgp_session_Enabled = 1, /* attempting to connect */ - bgp_session_Established = 2, - - bgp_session_Stopped = 3, /* for whatever reason */ - - bgp_session_max_state = 3 -} ; struct bgp_session { bgp_peer peer ; /* peer whose session this is */ - qpt_mutex_t mutex ; /* for access to the rest */ + bgp_peer_index_entry index_entry ; /* and its index entry */ - bgp_session_state_t state ; /* as above */ + qpt_mutex_t mutex ; /* for access to the rest */ - bgp_stopped_cause_t stopped ; /* why stopped */ + /* While sIdle and sStopped: + * + * the session belongs to the Peering Engine. + * + * The BGP Engine will not touch a session in these states and the + * Peering Engine may do what it likes with it. + * + * The Peering Engine may change the state: + * + * sIdle -> sEnabled + * sStopped -> sEnabled + * sStopped -> sIdle + * + * While sEnabled and sEstablished: + * + * the session belongs to the BGP Engine. + * + * A (very) few items in the session may be accessed by the Peering Engine, + * as noted below. (Subject to the mutex.) + * + * The BGP Engine may change the state: + * + * sEnabled -> sEstablished + * sEnabled -> sStopped + * sEstablished -> sStopped + * + * Only the Peering Engine creates and destroys sessions. The BGP Engine + * assumes that a session will not be destroyed while it is sEnabled or + * sEstablished. + * + * The made flag is cleared by the Peering Engine before enabling a session, + * and is set by the BGP Engine when the session becomes sEstablished. + * + * The Peering Engine may use this flag in sStopped state to see if the + * session was ever established. + */ + bgp_session_state_t state ; + int made ; /* set when -> sEstablished */ + + /* The BGP Engine records the last event, NOTIFICATION and errno here. + * + * This is only really useful when the session -> sStopped. + */ + bgp_session_event_t event ; /* last event */ bgp_notify notification ; /* if any sent/received */ - + int err ; /* errno, if any */ + + /* The Routeing Engine sets open_send and clears open_recv before enabling + * the session, and may not change them while sEnabled/sEstablished. + * + * The BGP Engine sets open_recv before setting the session sEstablished, + * and will not touch it thereafter. + * + * So: the Routeing Engine may use open_recv once the session is + * sEstablished. + */ bgp_open_state open_send ; /* how to open the session */ bgp_open_state open_recv ; /* set when session Established */ + /* The following are set by the Routeing Engine before a session is + * enabled, and not changed at any other time by either engine. + */ int connect ; /* initiate connections */ int listen ; /* listen for connections */ - int accept ; /* accept connections */ - 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_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 */ @@ -113,19 +152,73 @@ struct bgp_session unsigned idle_hold_timer_interval ; /* in seconds */ unsigned connect_retry_timer_interval ; unsigned open_hold_timer_interval ; + + /* These are set by the Routeing Engine before a session is enabled, + * and may be changed by the BGP Engine when the session is established. + * + * The Routeing Engine may read these once sEstablished (under mutex). + * + * In sStopped state these reflect the last state of the session. + */ unsigned hold_timer_interval ; /* subject to negotiation */ unsigned keepalive_timer_interval ; /* subject to negotiation */ - /* NB: these values are private to the BGP Engine, which accesses them */ - /* *without* worrying about the mutex. */ - /* */ - /* The BGP Engine uses these while the session is Enabled or */ - /* Established -- so the session must not be freed in these states. */ + int as4 ; + + /* These are cleared by the Routeing Engine before a session is enabled, + * and set by the BGP Engine when the session is established. + * + * In sStopped state these reflect the last state of the session. + */ + union sockunion* su_local ; /* set when session Established */ + union sockunion* su_remote ; /* set when session Established */ + /* These values are are private to the BGP Engine. + * + * They must be cleared before the session is enabled, but may not be + * touched by the Routeing Engine at any other time. + * + * Before stopping a session the BGP Engine unlinks any connections from + * the session. + */ bgp_connection connections[bgp_connection_count] ; } ; /*============================================================================== + * Mqueue messages related to sessions + * + * In all these messages arg0 is the session. + */ + +struct bgp_session_enable_args /* to BGP Engine */ +{ + /* no further arguments */ +} ; +MQB_ARGS_SIZE_OK(bgp_session_enable_args) ; + +struct bgp_session_disable_args /* to BGP Engine */ +{ + bgp_notify notification ; /* NOTIFICATION to send */ +} ; +MQB_ARGS_SIZE_OK(bgp_session_enable_args) ; + +struct bgp_session_update_args /* to and from BGP Engine */ +{ + struct stream* buf ; +} ; +MQB_ARGS_SIZE_OK(bgp_session_enable_args) ; + +struct bgp_session_event_args /* to Routeing Engine */ +{ + bgp_session_event_t event ; + bgp_notify notification ; /* sent or received (if any) */ + int err ; /* errno if any */ + + bgp_session_state_t state ; /* after the event */ +} ; +MQB_ARGS_SIZE_OK(bgp_session_enable_args) ; + +/*============================================================================== * Session mutex lock/unlock */ @@ -140,15 +233,32 @@ inline static void BGP_SESSION_UNLOCK(bgp_session session) } ; /*============================================================================== - * + * Functions */ extern bgp_session -bgp_session_init_new(bgp_session session) ; +bgp_session_init_new(bgp_session session, bgp_peer peer) ; extern bgp_session bgp_session_lookup(union sockunion* su, int* exists) ; +extern void +bgp_session_enable(bgp_peer peer) ; + +extern void +bgp_session_disable(bgp_peer peer, bgp_notify notification) ; + +extern void +bgp_session_event(bgp_session session, bgp_session_event_t event, + bgp_notify notification, + bgp_session_state_t state) ; + +extern void +bgp_session_update_send(bgp_session session, struct stream* upd) ; + +extern void +bgp_session_update_recv(bgp_session session, struct stream* upd) ; + /*============================================================================== * Session data access functions. * @@ -158,7 +268,5 @@ bgp_session_lookup(union sockunion* su, int* exists) ; extern int bgp_session_is_active(bgp_session session) ; -extern int -bgp_session_is_accepting(bgp_session session) ; #endif /* QUAGGA_BGP_SESSION_H */ diff --git a/lib/memtypes.c b/lib/memtypes.c index 9479feb2..7c4c6f61 100644 --- a/lib/memtypes.c +++ b/lib/memtypes.c @@ -115,6 +115,7 @@ struct memory_list memory_list_bgp[] = { MTYPE_PEER_GROUP, "Peer group" }, { MTYPE_PEER_DESC, "Peer description" }, { MTYPE_PEER_PASSWORD, "Peer password string" }, + { MTYPE_BGP_PEER_ID_TABLE, "Peer ID table" }, { MTYPE_BGP_SESSION, "BGP session" }, { MTYPE_BGP_CONNECTION, "BGP connection" }, { MTYPE_BGP_NOTIFY, "BGP notification" }, diff --git a/lib/mqueue.h b/lib/mqueue.h index 32ac599e..b1332e68 100644 --- a/lib/mqueue.h +++ b/lib/mqueue.h @@ -96,6 +96,8 @@ struct args char data[mqb_args_size_max] ; /* empty space */ } ; +#define MQB_ARGS_SIZE_OK(s) CONFIRM(sizeof(struct s) <= mqb_args_size_max) + struct mqueue_block { struct args args ; /* user structure */ |