diff options
-rw-r--r-- | bgpd/Makefile.am | 5 | ||||
-rw-r--r-- | bgpd/bgp_common.h | 27 | ||||
-rw-r--r-- | bgpd/bgp_connection.c | 84 | ||||
-rw-r--r-- | bgpd/bgp_connection.h | 24 | ||||
-rw-r--r-- | bgpd/bgp_engine.c | 26 | ||||
-rw-r--r-- | bgpd/bgp_fsm.c | 434 | ||||
-rw-r--r-- | bgpd/bgp_fsm.h | 13 | ||||
-rw-r--r-- | bgpd/bgp_msg_write.c | 2476 | ||||
-rw-r--r-- | bgpd/bgp_msg_write.h | 57 | ||||
-rw-r--r-- | bgpd/bgp_notification.c | 36 | ||||
-rw-r--r-- | bgpd/bgp_notification.h | 7 | ||||
-rw-r--r-- | bgpd/bgp_session.c | 26 | ||||
-rw-r--r-- | bgpd/bgp_session.h | 6 | ||||
-rw-r--r-- | lib/sockunion.h | 2 |
14 files changed, 2944 insertions, 279 deletions
diff --git a/bgpd/Makefile.am b/bgpd/Makefile.am index f903c7ba..3788e0f3 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_peer_index.c + bgp_common.c bgp_notification.c bgp_peer_index.c bgp_msg_write.c noinst_HEADERS = \ bgp_aspath.h bgp_attr.h bgp_community.h bgp_debug.h bgp_fsm.h \ @@ -26,7 +26,8 @@ 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_peer_index.h bgp.h + bgp_common.h bgp_notification.h bgp_peer_index.h bgp_msg_write.h + bgp.h bgpd_SOURCES = bgp_main.c bgpd_LDADD = libbgp.a ../lib/libzebra.la @LIBCAP@ @LIBM@ diff --git a/bgpd/bgp_common.h b/bgpd/bgp_common.h index 15fc026a..a5f66318 100644 --- a/bgpd/bgp_common.h +++ b/bgpd/bgp_common.h @@ -51,8 +51,8 @@ typedef struct bgp_open_state* bgp_open_state ; * Both session and connection require these */ -typedef enum bgp_connection_ordinal bgp_connection_ordinal_t ; -enum bgp_connection_ordinal +typedef enum bgp_connection_ord bgp_connection_ord_t ; +enum bgp_connection_ord { bgp_connection_primary = 0, bgp_connection_secondary = 1, @@ -78,22 +78,15 @@ enum bgp_session_states typedef enum bgp_session_events bgp_session_event_t ; enum bgp_session_events { - bgp_session_min_event = 0, - bgp_session_null_event = 0, + bgp_session_min_event = 0, + bgp_session_null_event = 0, - bgp_session_eEnabled, /* enabled by Peering Engine */ + bgp_session_eEstablished, /* session state -> sEstablished */ + bgp_session_eDisabled, /* disabled 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_session_eOpen_accept, /* accepted an OPEN message */ bgp_session_eOpen_reject, /* had to reject an OPEN message */ - bgp_session_eEstablished, /* session state -> sEstablished */ - bgp_session_eInvalid_msg, /* BGP message invalid */ bgp_session_eFSM_error, /* unexpected BGP message received */ bgp_session_eNOM_recv, /* NOTIFICATION message received */ @@ -105,9 +98,13 @@ enum bgp_session_events bgp_session_eExpired, /* HoldTime expired */ bgp_session_eInvalid, /* invalid internal event */ - bgp_session_eDisabled, /* disabled by Peering Engine */ - bgp_session_max_event = bgp_session_eDisabled + bgp_session_max_event = bgp_session_eInvalid, + + /* These are used by the FSM, but are not reported to the Routeing Engine */ + + bgp_session_eCollision, /* given way to sibling */ + bgp_session_eDiscard, /* discarded by sibling */ } ; /*============================================================================== diff --git a/bgpd/bgp_connection.c b/bgpd/bgp_connection.c index 445d6d18..35e76871 100644 --- a/bgpd/bgp_connection.c +++ b/bgpd/bgp_connection.c @@ -96,18 +96,16 @@ bgp_write_buffer_init_new(bgp_wbuffer wb, size_t size) ; * * * - * NB: acquires and releases the session mutex. + * NB: requires the session LOCKED */ extern bgp_connection bgp_connection_init_new(bgp_connection connection, bgp_session session, - bgp_connection_ordinal_t ordinal) + bgp_connection_ord_t ordinal) { assert( (ordinal == bgp_connection_primary) || (ordinal == bgp_connection_secondary) ) ; assert(session->connections[ordinal] == NULL) ; - BGP_SESSION_LOCK(session) ; /*<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ - if (connection == NULL) connection = XCALLOC(MTYPE_BGP_CONNECTION, sizeof(struct bgp_connection)) ; else @@ -121,7 +119,6 @@ bgp_connection_init_new(bgp_connection connection, bgp_session session, * * prev NULL -- not on the connection queue * * post bgp_fsm_null_event * * fsm_active not active - * * stopped bgp_stopped_not * * notification NULL -- none received or sent * * err no error, so far * * su_local NULL -- no address, yet @@ -137,7 +134,6 @@ bgp_connection_init_new(bgp_connection connection, bgp_session session, confirm(bgp_fsm_Initial == 0) ; confirm(bgp_fsm_null_event == 0) ; - confirm(bgp_stopped_not == 0) ; /* Link back to session, point at its mutex and point session here */ connection->session = session ; @@ -170,8 +166,6 @@ bgp_connection_init_new(bgp_connection connection, bgp_session session, /* Ensure mqueue_local_queue is empty. */ mqueue_local_init_new(&connection->pending_queue) ; - BGP_SESSION_UNLOCK(session) ; /*>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ - return connection ; } ; @@ -253,19 +247,40 @@ bgp_connection_make_primary(bgp_connection connection) } ; /*------------------------------------------------------------------------------ + * Exit connection + * + * Make sure the connection is closed, then queue it to be reaped. + */ +extern void +bgp_connection_exit(bgp_connection connection) +{ + bgp_connection_close(connection) ; /* make sure */ + + assert( (connection->state == bgp_fsm_Stopping) + && (connection->session == NULL) ) ; + + /* Add the connection to the connection queue, in Stopped state. + * + * When BGP Engine gets round to it, will free the structure. This avoids + * freeing the connection structure somewhere inside the FSM, and having to + * cope with the possibility of having dangling references to it. + */ + bgp_connection_queue_add(connection) ; +} ; + +/*------------------------------------------------------------------------------ * Free connection. * * Connection must be Stopping -- no longer attached to a session. * - * The FSM will have + * * * */ -extern bgp_connection +static void bgp_connection_free(bgp_connection connection) { - - return connection ; + XFREE(MTYPE_BGP_CONNECTION, connection) ; } ; /*------------------------------------------------------------------------------ @@ -389,7 +404,7 @@ bgp_connection_queue_process(void) /* Reap the connection if it is now stopped. */ if (connection->state == bgp_fsm_Stopping) { - bgp_connection_reset(connection) ; + bgp_connection_free(connection) ; } ; /* ..... */ @@ -416,7 +431,7 @@ bgp_connection_queue_process(void) * * * if accept() clears the session accept flag * * sets the qfile and fd ready for use - * * clears err and stopped + * * clears except, notification and err * * discards any open_state and notification * * copies hold_timer_interval and keep_alive_timer_interval from session * @@ -448,12 +463,14 @@ bgp_connection_open(bgp_connection connection, int fd) /* Clear sundry state is clear */ connection->post = bgp_fsm_null_event ; /* no post event event */ + + connection->except = bgp_session_null_event ; + bgp_notify_free(&connection->notification) ; connection->err = 0 ; /* so far, so good */ - connection->stopped = bgp_stopped_not ; /* up and running */ /* These accept NULL arguments */ connection->open_recv = bgp_open_state_free(connection->open_recv) ; - connection->notification = bgp_notify_free(connection->notification) ; + bgp_notify_free(&connection->notification) ; /* Copy the original hold_timer_interval and keepalive_timer_interval * Assume these have sensible initial values. @@ -504,7 +521,7 @@ bgp_connection_disable_accept(bgp_connection connection) * * logging and host string * * any open_state that has been received * * any notification sent/received - * * the stopped cause (if any) + * * the exception state and any error * * Once closed, the only further possible actions are: * @@ -550,8 +567,9 @@ bgp_connection_close(bgp_connection connection) connection->wbuff.p_out = connection->wbuff.base ; connection->wbuff.full = 0 ; - /* Empty out the pending queue */ + /* Empty out the pending queue and remove from connection queue */ mqueue_local_reset_keep(&connection->pending_queue) ; + bgp_connection_queue_del(connection) ; } ; /*------------------------------------------------------------------------------ @@ -623,12 +641,13 @@ bgp_connection_part_close(bgp_connection connection) wb->full = bgp_write_buffer_full(wb) ; assert(!wb->full) ; - /* Empty out the pending queue */ + /* Empty out the pending queue and remove from connection queue */ mqueue_local_reset_keep(&connection->pending_queue) ; + bgp_connection_queue_del(connection) ; } ; /*============================================================================== - * Writing to BGP connection. + * Writing to BGP connection -- once TCP connection has come up. * * All writing is done by preparing a BGP message in the "obuf" buffer, * and then calling bgp_connection_write(). @@ -776,10 +795,29 @@ bgp_connection_write_action(qps_file qf, void* file_info) } ; /*============================================================================== - * Read Action for bgp connection. + * Reading from BGP connection -- once the TCP connection has come up. + * + * Nothing is read directly -- all reading is qpselect driven. * - * Don't directly read -- all reading is done in response to the socket - * becoming readable. + * Sets the qfile readable -- and leaves it there for the duration. + * + * TODO: implement some read flow control ?? + */ + +static void +bgp_connection_read_action(qps_file qf, void* file_info) ; + +/*------------------------------------------------------------------------------ + * Enable reading on the given connection. + */ +extern void +bgp_connection_read_enable(bgp_connection connection) +{ + qps_enable_mode(&connection->qf, qps_read_mnum, bgp_connection_read_action) ; +} ; + +/*------------------------------------------------------------------------------ + * Read Action for BGP connection * * Reads one BGP message into the ibuf and dispatches it. * diff --git a/bgpd/bgp_connection.h b/bgpd/bgp_connection.h index 5e1ce349..b4882a2f 100644 --- a/bgpd/bgp_connection.h +++ b/bgpd/bgp_connection.h @@ -119,7 +119,7 @@ struct bgp_connection qpt_mutex p_mutex ; /* session mutex* */ /* (avoids incomplete type issue) */ - bgp_connection_ordinal_t ordinal ; /* primary/secondary connection */ + bgp_connection_ord_t ordinal ; /* primary/secondary connection */ int accepted ; /* came via accept() */ bgp_fsm_state_t state ; /* FSM state of connection */ @@ -171,11 +171,7 @@ struct bgp_connection extern bgp_connection bgp_connection_init_new(bgp_connection connection, bgp_session session, - bgp_connection_ordinal_t ordinal) ; -extern bgp_connection -bgp_connection_reset(bgp_connection connection, int free_structure) ; - - + bgp_connection_ord_t ordinal) ; extern void bgp_connection_open(bgp_connection connection, int fd) ; @@ -195,11 +191,25 @@ extern void bgp_connection_close(bgp_connection connection) ; extern void -bgp_connection_read_close(bgp_connection connection) ; +bgp_connection_part_close(bgp_connection connection) ; + +extern void +bgp_connection_exit(bgp_connection connection) ; + +extern void +bgp_connection_read_enable(bgp_connection connection) ; extern int bgp_connection_write(bgp_connection connection) ; +extern void +bgp_connection_queue_add(bgp_connection connection) ; + +extern void +bgp_connection_queue_del(bgp_connection connection) ; + +extern void +bgp_connection_queue_process(void) ; /*============================================================================== * Access functions via bgp_connection for bgp_session attributes. diff --git a/bgpd/bgp_engine.c b/bgpd/bgp_engine.c index 31919b27..3e297849 100644 --- a/bgpd/bgp_engine.c +++ b/bgpd/bgp_engine.c @@ -145,32 +145,6 @@ bgp_engine_start(void) -/*============================================================================== - * BGP Session Handling - */ - - -/* BGP Engine Action: enable given session - * - * arg0 -- bgp_session to be enabled - * - * - */ - -extern void -bgp_session_enable_action(mqueue_block mqb, mqb_flag_t flag) -{ - bgp_session session ; - bgp_connection connection ; - - /* Construct bgp_connection for the new session. */ - - session = mqb_get_arg0(mqb) ; - connection = bgp_connection_init_new(NULL, session, 0) ; - - /* */ - -} ; /*============================================================================== diff --git a/bgpd/bgp_fsm.c b/bgpd/bgp_fsm.c index bd2ef82a..a5e3c58e 100644 --- a/bgpd/bgp_fsm.c +++ b/bgpd/bgp_fsm.c @@ -383,16 +383,54 @@ */ /*============================================================================== - * Enable the given connection -- which must be newly initialised. + * Enable the given session -- which must be newly initialised. * * This is the first step in the FSM, and the connection advances to Idle. * + * Returns in something of a hurry if not enabled for connect() or for accept(). + * + * NB: requires the session LOCKED */ extern void -bgp_fsm_enable_connection(bgp_connection connection) +bgp_fsm_enable_session(bgp_session session) { - assert(connection->state == bgp_fsm_Initial) ; - bgp_fsm_event(connection, bgp_fsm_BGP_Start) ; + bgp_connection connection ; + + /* Proceed instantly to a dead stop if neither connect nor listen ! */ + if (!(session->connect || session->listen)) + { + session->event = bgp_session_eInvalid ; + session->state = bgp_session_sStopped ; + + bgp_session_event(session) ; + + return ; + } ; + + /* Primary connection -- if connect allowed + * + * NB: the start event for the primary connection is guaranteed to succeed, + * and nothing further will happen until the initial IdleHoldTimer + * expires -- always has a small, non-zero time. + * + * This ensures that the secondary connection can be started before + * there's any change of the session being torn down !! + */ + if (session->connect) + { + connection = bgp_connection_init_new(NULL, session, + bgp_connection_primary) ; + bgp_fsm_event(connection, bgp_fsm_BGP_Start) ; + } ; + + /* Secondary connection -- if listen allowed + */ + if (session->listen) + { + connection = bgp_connection_init_new(NULL, session, + bgp_connection_secondary) ; + bgp_fsm_event(connection, bgp_fsm_BGP_Start) ; + } ; } ; /*============================================================================= @@ -426,14 +464,35 @@ static void bgp_fsm_throw_exception(bgp_connection connection, bgp_session_event_t except, bgp_notify notification, int err, bgp_fsm_event_t event) ; +static bgp_fsm_state_t +bgp_fsm_catch(bgp_connection connection, bgp_fsm_state_t next_state) ; + +/*------------------------------------------------------------------------------ + * Ultimate exception -- disable the session + * + * NB: requires the session LOCKED + */ +extern void +bgp_fsm_disable_session(bgp_session session, bgp_notify notification) +{ + bgp_connection connection ; + + connection = session->connections[bgp_connection_primary] ; + if (connection == NULL) + connection = session->connections[bgp_connection_secondary] ; + if (connection == NULL) + return ; + + bgp_fsm_throw_exception(connection, bgp_session_eDisabled, notification, 0, + bgp_fsm_BGP_Stop) ; +} ; + /*------------------------------------------------------------------------------ * Raise a general exception -- not I/O related. * * Note that I/O problems are signalled by bgp_fsm_io_error(). * * NB: may NOT be used within the FSM. - * - * NB: locks and unlocks the session. */ extern void bgp_fsm_raise_exception(bgp_connection connection, bgp_session_event_t except, @@ -444,9 +503,29 @@ bgp_fsm_raise_exception(bgp_connection connection, bgp_session_event_t except, } ; /*------------------------------------------------------------------------------ - * Raise a NOTIFICATION received exception + * Raise a discard exception for sibling. + * + * A connection will discard any sibling if: + * + * * the session is being disabled (by the Peering Engine) + * + * * an invalid event is bringing down the session * - * NB: locks and unlocks the session. + * * it has reached Established state, and is snuffing out the sibling. + * + * + * + * NB: requires the session LOCKED + */ +static void +bgp_fsm_discard_sibling(bgp_connection sibling, bgp_notify notification) +{ + bgp_fsm_throw_exception(sibling, bgp_session_eDiscard, + notification, 0, bgp_fsm_BGP_Stop) ; +} ; + +/*------------------------------------------------------------------------------ + * Raise a NOTIFICATION received exception */ extern void bgp_fsm_notification_exception(bgp_connection connection, @@ -560,13 +639,23 @@ bgp_fsm_connect_completed(bgp_connection connection, int err, /*------------------------------------------------------------------------------ * Post the given exception. + * + * Forget the notification if not OpenSent/OpenConfirm/Established. Cannot + * send notification in any other state -- nor receive one. */ 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 ; + + if ( (connection->state != bgp_fsm_OpenSent) + && (connection->state != bgp_fsm_OpenConfirm) + && (connection->state != bgp_fsm_Established) ) + bgp_notify_free(¬ification) ; + + bgp_notify_set(&connection->notification, notification) ; + connection->err = err ; } ; @@ -581,6 +670,21 @@ bgp_fsm_throw_exception(bgp_connection connection, bgp_session_event_t except, bgp_fsm_event(connection, event) ; } ; +/*------------------------------------------------------------------------------ + * Post and immediately catch a non-I/O exception. + * + * For use WITHIN the FSM. + * + * NB: requires the session LOCKED + */ +static bgp_fsm_state_t +bgp_fsm_post_catch(bgp_connection connection, bgp_session_event_t except, + bgp_notify notification, bgp_fsm_state_t next_state) +{ + bgp_fsm_post_exception(connection, except, notification, 0) ; + return bgp_fsm_catch(connection, next_state) ; +} ; + /*============================================================================== * For debug... */ @@ -1447,16 +1551,7 @@ bgp_fsm_event(bgp_connection connection, bgp_fsm_event_t event) session = connection->session ; if (session != NULL) - { - 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) ; - } ; - } ; + BGP_SESSION_LOCK(session) ; /*<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ do { @@ -1502,9 +1597,26 @@ bgp_fsm_event(bgp_connection connection, bgp_fsm_event_t event) } while (--connection->fsm_active != 0) ; - if (session->event != bgp_session_null_event) + /* If required, post session event. */ + + if ((connection->except != bgp_session_null_event) && (session != NULL)) { + /* Some exceptions are no reported to the Routeing Engine */ + if (connection->except <= bgp_session_max_event) + { + session->event = connection->except ; + bgp_notify_set_mov(&session->notification, &connection->notification); + session->err = connection->err ; + session->ordinal = connection->ordinal ; + bgp_session_event(session) ; + } + else + bgp_notify_free(&connection->notification) ; /* if any */ + + /* Tidy up -- notification already cleared */ + connection->except = bgp_session_null_event ; + connection->err = 0 ; } if (session != NULL) @@ -1521,15 +1633,9 @@ bgp_hold_timer_set(bgp_connection connection, unsigned secs) ; static void bgp_hold_timer_recharge(bgp_connection connection) ; -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) ; -static bgp_fsm_state_t -bgp_fsm_catch_exception(bgp_connection connection, bgp_fsm_state_t next_state) ; - /*------------------------------------------------------------------------------ * Null action -- do nothing at all. */ @@ -1542,7 +1648,10 @@ static bgp_fsm_action(bgp_fsm_null) * 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. + * state to Idle, with IdleHoldTimer running. + * + * NB: the IdleHoldTimer is always a finite time. So the start up event for + * the primary connection *cannot* fail. * * NB: the session is locked. */ @@ -1551,32 +1660,29 @@ 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 ; } ; /*------------------------------------------------------------------------------ - * Stop BGP Connection - * - * The reason should already have been set: bgp_fsm_set_stopping(). + * Stop BGP Connection -- general exception event. * - * If no reason set => treat as invalid. + * An exception should have been raised, treat as invalid if not. * - * NB: the default new_state is used unless: + * If is eDisabled, set next_state == Stopping. + * If is eDiscard, set next_state == Stopping. * - * * 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. + * NB: requires the session LOCKED */ static bgp_fsm_action(bgp_fsm_stop) { - return next_state ; + if (connection->except == bgp_session_null_event) + return bgp_fsm_invalid(connection, bgp_fsm_Stopping, event) ; + + if ( (connection->except == bgp_session_eDisabled) + || (connection->except == bgp_session_eDiscard) ) + next_state = bgp_fsm_Stopping ; + + return bgp_fsm_catch(connection, next_state) ; } ; /*------------------------------------------------------------------------------ @@ -1586,7 +1692,9 @@ static bgp_fsm_action(bgp_fsm_stop) * * Forces transition to Stopping state for this connection and any sibling. * - * NB: the session is locked. + * If already in Stopping state, force exit. + * + * NB: requires the session LOCKED */ static bgp_fsm_action(bgp_fsm_invalid) { @@ -1594,10 +1702,12 @@ 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_post_exception(connection, bgp_session_eInvalid, - bgp_notify_new(BGP_NOMC_FSM, BGP_NOMS_UNSPECIFIC, 0), 0) ; - - return bgp_fsm_catch_exception(connection, next_state) ; + if (connection->state != bgp_fsm_Stopping) + return bgp_fsm_post_catch(connection, bgp_session_eInvalid, + bgp_notify_new(BGP_NOMC_FSM, BGP_NOMS_UNSPECIFIC, 0), + bgp_fsm_Stopping) ; + else + return bgp_fsm_exit(connection, bgp_fsm_Stopping, event) ; } ; /*------------------------------------------------------------------------------ @@ -1619,7 +1729,7 @@ static bgp_fsm_action(bgp_fsm_invalid) * * If this is the secondary connection, enables the session for accept(). * - * NB: the session is locked. + * NB: requires the session LOCKED */ static bgp_fsm_action(bgp_fsm_start) { @@ -1634,8 +1744,6 @@ static bgp_fsm_action(bgp_fsm_start) bgp_connection_enable_accept(connection) ; } ; - bgp_fsm_post_session_event(connection, bgp_session_eStart, NULL, 0) ; - return next_state ; } ; @@ -1644,7 +1752,7 @@ static bgp_fsm_action(bgp_fsm_start) * * Send BGP Open Message to peer. * - * NB: the session is locked. + * NB: requires the session LOCKED */ static bgp_fsm_action(bgp_fsm_send_open) { @@ -1665,9 +1773,9 @@ static bgp_fsm_action(bgp_fsm_send_open) sockunion2str(connection->su_local, buf_l, SU_ADDRSTRLEN)) ; } ; - bgp_msg_send_open(connection, connection->session->open_send) ; + bgp_connection_read_enable(connection) ; - bgp_fsm_post_session_event(connection, bgp_session_eTCP_connect, NULL, 0) ; + bgp_msg_send_open(connection, connection->session->open_send) ; return next_state ; } ; @@ -1682,13 +1790,11 @@ static bgp_fsm_action(bgp_fsm_send_open) * * Will stay in Connect/Active states. * - * NB: the session is locked. + * NB: requires the session LOCKED */ static bgp_fsm_action(bgp_fsm_failed) { - bgp_connection_close(connection) ; - - return bgp_fsm_catch_exception(connection, next_state) ; + return bgp_fsm_catch(connection, next_state) ; } ; /*------------------------------------------------------------------------------ @@ -1699,13 +1805,11 @@ static bgp_fsm_action(bgp_fsm_failed) * 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. + * NB: requires the session LOCKED */ static bgp_fsm_action(bgp_fsm_fatal) { - bgp_connection_close(connection) ; - - return bgp_fsm_catch_exception(connection, next_state) ; + return bgp_fsm_catch(connection, next_state) ; } ; /*------------------------------------------------------------------------------ @@ -1721,12 +1825,13 @@ static bgp_fsm_action(bgp_fsm_fatal) * * close the existing connection (easy, 'cos never opened !) * * re-enable accept (if has been cleared) and wait for same * + * NB: requires the session LOCKED */ static bgp_fsm_action(bgp_fsm_retry) { bgp_connection_close(connection) ; - bgp_fsm_post_session_event(connection, bgp_session_eRetry, NULL, 0) ; + bgp_fsm_post_exception(connection, bgp_session_eRetry, NULL, 0) ; return bgp_fsm_start(connection, next_state, event) ; } ; @@ -1737,13 +1842,11 @@ static bgp_fsm_action(bgp_fsm_retry) * 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. + * NB: requires the session LOCKED */ static bgp_fsm_action(bgp_fsm_closed) { - bgp_connection_close(connection) ; - - return bgp_fsm_catch_exception(connection, next_state) ; + return bgp_fsm_catch(connection, next_state) ; } ; /*------------------------------------------------------------------------------ @@ -1754,7 +1857,7 @@ static bgp_fsm_action(bgp_fsm_closed) * * or: can wait no longer for something from the other end. * - * NB: the session is locked. + * NB: requires the session LOCKED */ static bgp_fsm_action(bgp_fsm_expire) { @@ -1766,11 +1869,10 @@ static bgp_fsm_action(bgp_fsm_expire) 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) ; + /* Otherwise: post and immediately catch exception. */ + return bgp_fsm_post_catch(connection, bgp_session_eExpired, + bgp_notify_new(BGP_NOMC_HOLD_EXP, BGP_NOMS_UNSPECIFIC, 0), + next_state) ; } ; /*------------------------------------------------------------------------------ @@ -1796,12 +1898,12 @@ static bgp_fsm_action(bgp_fsm_expire) * If makes it past Collision Resolution, respond with a KEEPALIVE (to "ack" * the OPEN message). * - * NB: the session is locked. + * NB: requires the session LOCKED */ static bgp_fsm_action(bgp_fsm_recv_open) { bgp_session session = connection->session ; - bgp_connection sibling = bgp_fsm_get_sibling(connection) ; + bgp_connection sibling = bgp_connection_get_sibling(connection) ; assert(session != NULL) ; @@ -1818,22 +1920,20 @@ static bgp_fsm_action(bgp_fsm_recv_open) : sibling ; /* Set reason for stopping */ - bgp_fsm_set_stopping(loser, bgp_stopped_collision, + bgp_fsm_post_exception(loser, bgp_session_eCollision, 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) ; + return bgp_fsm_catch(connection, next_state) ; else bgp_fsm_event(sibling, bgp_fsm_BGP_Stop) ; } ; - /* If all is well, send a KEEPALIVE message to acknowledge the OPEN */ + /* 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 ; } @@ -1850,14 +1950,13 @@ static bgp_fsm_action(bgp_fsm_recv_open) * Next state will be same as current, except for Established, when will be * Stopping. * - * NB: the session is locked. + * NB: requires the session LOCKED */ static bgp_fsm_action(bgp_fsm_error) { - 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) ; + return bgp_fsm_post_catch(connection, bgp_session_eFSM_error, + bgp_notify_new(BGP_NOMC_FSM, BGP_NOMS_UNSPECIFIC, 0), + next_state) ; } ; /*------------------------------------------------------------------------------ @@ -1868,13 +1967,11 @@ static bgp_fsm_action(bgp_fsm_error) * * Next state will be Idle, except for Established, when will be Stopping. * - * NB: the session is locked. + * NB: requires the session LOCKED */ static bgp_fsm_action(bgp_fsm_recv_nom) { - bgp_connection_close(connection) ; - - return bgp_fsm_catch_exception(connection, next_state) ; + return bgp_fsm_catch(connection, next_state) ; } ; /*------------------------------------------------------------------------------ @@ -1883,7 +1980,7 @@ static bgp_fsm_action(bgp_fsm_recv_nom) * * Set the "courtesy" HoldTimer. Expect to stay in current state. * - * NB: the session is locked. + * NB: requires the session LOCKED */ static bgp_fsm_action(bgp_fsm_sent_nom) { @@ -1894,7 +1991,7 @@ static bgp_fsm_action(bgp_fsm_sent_nom) /*------------------------------------------------------------------------------ * Seed Keepalive to peer. * - * NB: the session is locked. + * NB: requires the session LOCKED */ static bgp_fsm_action(bgp_fsm_send_kal) { @@ -1908,27 +2005,30 @@ static bgp_fsm_action(bgp_fsm_send_kal) * If there is another connection, that is now snuffed out and this connection * becomes the primary. * - * NB: the session is locked. + * NB: requires the session LOCKED */ static bgp_fsm_action(bgp_fsm_establish) { bgp_session session = connection->session ; - bgp_connection sibling = bgp_fsm_get_sibling(connection) ; + bgp_connection sibling = bgp_connection_get_sibling(connection) ; assert(session != NULL) ; - /* The first thing to do is to snuff off any sibling */ + /* The first thing to do is to snuff out any sibling */ if (sibling != NULL) - bgp_fsm_stop_connection(sibling, bgp_stopped_loser, + bgp_fsm_discard_sibling(sibling, bgp_notify_new(BGP_NOMC_CEASE, BGP_NOMS_C_COLLISION, 0)) ; /* Establish self as primary and copy state up to session */ bgp_connection_make_primary(connection) ; - /* TODO: now would be a good time to withdraw the password from listener ? */ + /* Change the session state and post event */ + assert(session->state == bgp_session_sEnabled) ; - /* Set the session state -- tell the Routeing Engine the news */ - /*>>>>>>> signal event <<<<<*/ + session->state = bgp_session_sEstablished ; + bgp_fsm_post_exception(connection, bgp_session_eEstablished, NULL, 0) ; + + /* TODO: now would be a good time to withdraw the password from listener ? */ return next_state ; } ; @@ -1936,7 +2036,7 @@ static bgp_fsm_action(bgp_fsm_establish) /*------------------------------------------------------------------------------ * Keepalive packet is received -- OpenConfirm/Established * - * NB: the session is locked. + * NB: requires the session LOCKED */ static bgp_fsm_action(bgp_fsm_recv_kal) { @@ -1950,7 +2050,7 @@ static bgp_fsm_action(bgp_fsm_recv_kal) /*------------------------------------------------------------------------------ * Update packet is received. * - * NB: the session is locked. + * NB: requires the session LOCKED */ static bgp_fsm_action(bgp_fsm_update) { @@ -1958,21 +2058,21 @@ static bgp_fsm_action(bgp_fsm_update) return next_state ; } - - - /*------------------------------------------------------------------------------ * Connection exit * * Ring down the curtain. Connection structure will be freed by the BGP Engine. * - * NB: the session is locked. + * NB: requires the session LOCKED */ static bgp_fsm_action(bgp_fsm_exit) { + assert(connection->state == bgp_fsm_Stopping) ; + bgp_connection_exit(connection) ; - return next_state ; -} + + return bgp_fsm_Stopping ; +} ; /*============================================================================== * Catching FSM Exceptions. @@ -1983,88 +2083,68 @@ static bgp_fsm_action(bgp_fsm_exit) * connection->err ) which define the exception * connection->notification ) * - * The FSM adds the expected next_state. + * An event has been raised, and the FSM has a (default next_state). * - * From all the above... + * 1a) notification & not eNOM_recv * + * Start sending the NOTIFICATION message. * - */ -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: won't be a notification unless OpenSent/OpenConfirm/Established. * - * NB: can only be notification pending if TCP connection is up (OpenSent, - * OpenConfirm or Established. + * For OpenSent/OpenConfirm, override the next_state to stay where it is + * until NOTIFICATION process completes. * - * If do send N + * Sending NOTIFICATION closes the connection for reading. * - * 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: + * 1b) otherwise: close the connection. + * + * 2) if next state is Stopping, and not eDiscard + * + * This means we bring down the session, so discard any sibling. * - * bgp_stopped_admin -- stopped by Routeing Engine - * bgp_stopped_loser -- other connection won race to Established state - * bgp_stopped_invalid -- invalid event or action + * The sibling will send any notification, and proceed immediately to + * Stopping. * - * then we force transition to Stopping state. + * (The sibling will be eDiscard -- so no deadly embrace here.) + * + * The state machine takes care of the rest: + * + * * complete entry to new state (for Stopping will cut connection loose). + * + * * send message to Routeing Engine + * + * NB: requires the session LOCKED */ - -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_exception(bgp_connection connection, bgp_session_event_t except, - bgp_notify notification, int err) ; +static bgp_fsm_state_t +bgp_fsm_catch(bgp_connection connection, bgp_fsm_state_t next_state) { - bgp_session session ; - bgp_connection sibling ; - - /* If not connected to session, then already stopping and can do no more. */ - session = connection->session ; - if (session == NULL) - return ; - - BGP_CONNECTION_SESSION_LOCK(connection) ; /*<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ - - /* Get "sibling" and set stopping same as us, if required. */ - /* Won't be there if one-time sibling is already stopping/stopped. */ - if (both && ((sibling = bgp_fsm_get_sibling(connection)) != NULL)) - bgp_fsm_stop_connection(sibling, cause, bgp_notify_dup(notification)) ; - - /* If have been passed a notification, then that should be sent to */ - /* the other end, and reported with the session stop cause. */ - - connection->notification_pending = (notification != NULL) ; - - if (connection->notification_pending) - bgp_notify_set(&connection->notification, notification) ; + /* If there is a NOTIFICATION to send, now is the time to do that. + * Otherwise, close the connection. + */ + if ( (connection->notification != NULL) + && (connection->except != bgp_session_eNOM_recv) ) + { + next_state = bgp_fsm_send_notification(connection, + connection->notification) ; + } + else + bgp_connection_close(connection) ; - /* Set the session stopping cause and copy the notification there */ + /* If stopping and not eDiscard, do in any sibling */ + if ( (next_state == bgp_fsm_Stopping) + && (connection->except != bgp_session_eDiscard) ) + { + bgp_connection sibling ; - session->stopped = cause ; - bgp_notify_set_dup(&session->notification, connection->notification) ; + sibling = bgp_connection_get_sibling(connection) ; /* ... if any */ - BGP_CONNECTION_SESSION_UNLOCK(connection) ; /*>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ -} ; + if (sibling != NULL) + bgp_fsm_discard_sibling(sibling, + bgp_notify_dup(connection->notification)) ; + } ; + /* Return the (possibly adjusted) next_state */ + return next_state ; } ; /*============================================================================== @@ -2161,7 +2241,7 @@ static qtimer_action bgp_keepalive_timer_action ; * 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. + * NB: requires the session LOCKED */ static void bgp_fsm_state_change(bgp_connection connection, bgp_fsm_state_t new_state) @@ -2304,7 +2384,7 @@ bgp_fsm_state_change(bgp_connection connection, bgp_fsm_state_t new_state) * * If not sending a NOTIFICATION then stop HoldTimer now. * - * Connections which are Stopping are no longer linked to a session. + * Unlink connection from session. */ case bgp_fsm_Stopping: if (!connection->notification_pending) @@ -2316,6 +2396,8 @@ bgp_fsm_state_change(bgp_connection connection, bgp_fsm_state_t new_state) connection->session = NULL ; connection->p_mutex = NULL ; + session->state = bgp_session_sStopped ; + break ; default: diff --git a/bgpd/bgp_fsm.h b/bgpd/bgp_fsm.h index a606dd05..bbf9ebca 100644 --- a/bgpd/bgp_fsm.h +++ b/bgpd/bgp_fsm.h @@ -27,14 +27,20 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA /* Prototypes. */ +extern void +bgp_fsm_enable_session(bgp_session session) ; extern void -bgp_fsm_enable_connection(bgp_connection connection) ; +bgp_fsm_disable_session(bgp_session session, bgp_notify notification) ; extern void bgp_fsm_event(bgp_connection connection, bgp_fsm_event_t event) ; extern void +bgp_fsm_raise_exception(bgp_connection connection, bgp_session_event_t except, + bgp_notify notification) ; + +extern void bgp_fsm_io_fatal_error(bgp_connection connection, int err) ; extern void @@ -46,10 +52,7 @@ bgp_fsm_connect_completed(bgp_connection connection, int err, union sockunion* su_remote) ; extern void -bgp_fsm_stop_session(bgp_connection connection, bgp_stopped_cause_t cause, - bgp_notify notification) ; -extern void -bgp_fsm_stop_connection(bgp_connection connection, bgp_stopped_cause_t cause, +bgp_fsm_notification_exception(bgp_connection connection, bgp_notify notification) ; diff --git a/bgpd/bgp_msg_write.c b/bgpd/bgp_msg_write.c new file mode 100644 index 00000000..d15f1a26 --- /dev/null +++ b/bgpd/bgp_msg_write.c @@ -0,0 +1,2476 @@ +/* BGP message writing -- in BGP Engine + * Copyright (C) 1999 Kunihiro Ishiguro + * + * Recast for pthreaded bgpd: 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 <zebra.h> + +#include "thread.h" +#include "stream.h" +#include "network.h" +#include "prefix.h" +#include "command.h" +#include "log.h" +#include "memory.h" +#include "sockunion.h" /* for inet_ntop () */ +#include "linklist.h" +#include "plist.h" + +#include "bgpd/bgpd.h" + +#include "bgpd/bgp_peer.h" + +#include "bgpd/bgp_table.h" +#include "bgpd/bgp_dump.h" +#include "bgpd/bgp_attr.h" +#include "bgpd/bgp_debug.h" +#include "bgpd/bgp_fsm.h" +#include "bgpd/bgp_route.h" +#include "bgpd/bgp_packet.h" +#include "bgpd/bgp_open.h" +#include "bgpd/bgp_aspath.h" +#include "bgpd/bgp_community.h" +#include "bgpd/bgp_ecommunity.h" +#include "bgpd/bgp_network.h" +#include "bgpd/bgp_mplsvpn.h" +#include "bgpd/bgp_advertise.h" +#include "bgpd/bgp_vty.h" + +int stream_put_prefix (struct stream *, struct prefix *); + +/* Set up BGP packet marker and packet type. */ +static int +bgp_packet_set_marker (struct stream *s, u_char type) +{ + int i; + + /* Fill in marker. */ + for (i = 0; i < BGP_MARKER_SIZE; i++) + stream_putc (s, 0xff); + + /* Dummy total length. This field is should be filled in later on. */ + stream_putw (s, 0); + + /* BGP packet type. */ + stream_putc (s, type); + + /* Return current stream size. */ + return stream_get_endp (s); +} + +/* Set BGP packet header size entry. If size is zero then use current + stream size. */ +static int +bgp_packet_set_size (struct stream *s) +{ + int cp; + + /* Preserve current pointer. */ + cp = stream_get_endp (s); + stream_putw_at (s, BGP_MARKER_SIZE, cp); + + return cp; +} + +/* Add new packet to the peer. */ +static void +bgp_packet_add (struct peer *peer, struct stream *s) +{ + /* Add packet to the end of list. */ + stream_fifo_push (peer->obuf, s); +} + +/* Free first packet. */ +static void +bgp_packet_delete (struct peer *peer) +{ + stream_free (stream_fifo_pop (peer->obuf)); +} + +/* Check file descriptor whether connect is established. */ +static void +bgp_connect_check (struct peer *peer) +{ + int status; + socklen_t slen; + int ret; + + /* Anyway I have to reset read and write thread. */ + BGP_READ_OFF (peer->t_read); + BGP_WRITE_OFF (peer->t_write); + + /* Check file descriptor. */ + slen = sizeof (status); + ret = getsockopt(peer->fd, SOL_SOCKET, SO_ERROR, (void *) &status, &slen); + + /* If getsockopt is fail, this is fatal error. */ + if (ret < 0) + { + zlog (peer->log, LOG_INFO, "can't get sockopt for nonblocking connect"); + BGP_EVENT_ADD (peer, TCP_fatal_error); + return; + } + + /* When status is 0 then TCP connection is established. */ + if (status == 0) + { + BGP_EVENT_ADD (peer, TCP_connection_open); + } + else + { + if (BGP_DEBUG (events, EVENTS)) + plog_debug (peer->log, "%s [Event] Connect failed (%s)", + peer->host, safe_strerror (errno)); + BGP_EVENT_ADD (peer, TCP_connection_open_failed); + } +} + +/* Make BGP update packet. */ +static struct stream * +bgp_update_packet (struct peer *peer, afi_t afi, safi_t safi) +{ + struct stream *s; + struct bgp_adj_out *adj; + struct bgp_advertise *adv; + struct stream *packet; + struct bgp_node *rn = NULL; + struct bgp_info *binfo = NULL; + bgp_size_t total_attr_len = 0; + unsigned long pos; + char buf[BUFSIZ]; + + s = peer->work; + stream_reset (s); + + adv = FIFO_HEAD (&peer->sync[afi][safi]->update); + + while (adv) + { + assert (adv->rn); + rn = adv->rn; + adj = adv->adj; + if (adv->binfo) + binfo = adv->binfo; + + /* When remaining space can't include NLRI and it's length. */ + if (STREAM_REMAIN (s) <= BGP_NLRI_LENGTH + PSIZE (rn->p.prefixlen)) + break; + + /* If packet is empty, set attribute. */ + if (stream_empty (s)) + { + struct prefix_rd *prd = NULL; + u_char *tag = NULL; + struct peer *from = NULL; + + if (rn->prn) + prd = (struct prefix_rd *) &rn->prn->p; + if (binfo && binfo->extra) + { + tag = binfo->extra->tag; + from = binfo->peer; + } + + bgp_packet_set_marker (s, BGP_MSG_UPDATE); + stream_putw (s, 0); + pos = stream_get_endp (s); + stream_putw (s, 0); + total_attr_len = bgp_packet_attribute (NULL, peer, s, + adv->baa->attr, + &rn->p, afi, safi, + from, prd, tag); + stream_putw_at (s, pos, total_attr_len); + } + + if (afi == AFI_IP && safi == SAFI_UNICAST) + stream_put_prefix (s, &rn->p); + + if (BGP_DEBUG (update, UPDATE_OUT)) + zlog (peer->log, LOG_DEBUG, "%s send UPDATE %s/%d", + peer->host, + inet_ntop (rn->p.family, &(rn->p.u.prefix), buf, BUFSIZ), + rn->p.prefixlen); + + /* Synchnorize attribute. */ + if (adj->attr) + bgp_attr_unintern (adj->attr); + else + peer->scount[afi][safi]++; + + adj->attr = bgp_attr_intern (adv->baa->attr); + + adv = bgp_advertise_clean (peer, adj, afi, safi); + + if (! (afi == AFI_IP && safi == SAFI_UNICAST)) + break; + } + + if (! stream_empty (s)) + { + bgp_packet_set_size (s); + packet = stream_dup (s); + bgp_packet_add (peer, packet); + BGP_WRITE_ON (peer->t_write, bgp_write, peer->fd); + stream_reset (s); + return packet; + } + return NULL; +} + +static struct stream * +bgp_update_packet_eor (struct peer *peer, afi_t afi, safi_t safi) +{ + struct stream *s; + struct stream *packet; + + if (DISABLE_BGP_ANNOUNCE) + return NULL; + + if (BGP_DEBUG (normal, NORMAL)) + zlog_debug ("send End-of-RIB for %s to %s", afi_safi_print (afi, safi), peer->host); + + s = stream_new (BGP_MAX_PACKET_SIZE); + + /* Make BGP update packet. */ + bgp_packet_set_marker (s, BGP_MSG_UPDATE); + + /* Unfeasible Routes Length */ + stream_putw (s, 0); + + if (afi == AFI_IP && safi == SAFI_UNICAST) + { + /* Total Path Attribute Length */ + stream_putw (s, 0); + } + else + { + /* Total Path Attribute Length */ + stream_putw (s, 6); + stream_putc (s, BGP_ATTR_FLAG_OPTIONAL); + stream_putc (s, BGP_ATTR_MP_UNREACH_NLRI); + stream_putc (s, 3); + stream_putw (s, afi); + stream_putc (s, safi); + } + + bgp_packet_set_size (s); + packet = stream_dup (s); + bgp_packet_add (peer, packet); + stream_free (s); + return packet; +} + +/* Make BGP withdraw packet. */ +static struct stream * +bgp_withdraw_packet (struct peer *peer, afi_t afi, safi_t safi) +{ + struct stream *s; + struct stream *packet; + struct bgp_adj_out *adj; + struct bgp_advertise *adv; + struct bgp_node *rn; + unsigned long pos; + bgp_size_t unfeasible_len; + bgp_size_t total_attr_len; + char buf[BUFSIZ]; + + s = peer->work; + stream_reset (s); + + while ((adv = FIFO_HEAD (&peer->sync[afi][safi]->withdraw)) != NULL) + { + assert (adv->rn); + adj = adv->adj; + rn = adv->rn; + + if (STREAM_REMAIN (s) + < (BGP_NLRI_LENGTH + BGP_TOTAL_ATTR_LEN + PSIZE (rn->p.prefixlen))) + break; + + if (stream_empty (s)) + { + bgp_packet_set_marker (s, BGP_MSG_UPDATE); + stream_putw (s, 0); + } + + if (afi == AFI_IP && safi == SAFI_UNICAST) + stream_put_prefix (s, &rn->p); + else + { + struct prefix_rd *prd = NULL; + + if (rn->prn) + prd = (struct prefix_rd *) &rn->prn->p; + pos = stream_get_endp (s); + stream_putw (s, 0); + total_attr_len + = bgp_packet_withdraw (peer, s, &rn->p, afi, safi, prd, NULL); + + /* Set total path attribute length. */ + stream_putw_at (s, pos, total_attr_len); + } + + if (BGP_DEBUG (update, UPDATE_OUT)) + zlog (peer->log, LOG_DEBUG, "%s send UPDATE %s/%d -- unreachable", + peer->host, + inet_ntop (rn->p.family, &(rn->p.u.prefix), buf, BUFSIZ), + rn->p.prefixlen); + + peer->scount[afi][safi]--; + + bgp_adj_out_remove (rn, adj, peer, afi, safi); + bgp_unlock_node (rn); + + if (! (afi == AFI_IP && safi == SAFI_UNICAST)) + break; + } + + if (! stream_empty (s)) + { + if (afi == AFI_IP && safi == SAFI_UNICAST) + { + unfeasible_len + = stream_get_endp (s) - BGP_HEADER_SIZE - BGP_UNFEASIBLE_LEN; + stream_putw_at (s, BGP_HEADER_SIZE, unfeasible_len); + stream_putw (s, 0); + } + bgp_packet_set_size (s); + packet = stream_dup (s); + bgp_packet_add (peer, packet); + stream_reset (s); + return packet; + } + + return NULL; +} + +void +bgp_default_update_send (struct peer *peer, struct attr *attr, + afi_t afi, safi_t safi, struct peer *from) +{ + struct stream *s; + struct stream *packet; + struct prefix p; + unsigned long pos; + bgp_size_t total_attr_len; + char attrstr[BUFSIZ]; + char buf[BUFSIZ]; + + if (DISABLE_BGP_ANNOUNCE) + return; + + if (afi == AFI_IP) + str2prefix ("0.0.0.0/0", &p); +#ifdef HAVE_IPV6 + else + str2prefix ("::/0", &p); +#endif /* HAVE_IPV6 */ + + /* Logging the attribute. */ + if (BGP_DEBUG (update, UPDATE_OUT)) + { + bgp_dump_attr (peer, attr, attrstr, BUFSIZ); + zlog (peer->log, LOG_DEBUG, "%s send UPDATE %s/%d %s", + peer->host, inet_ntop(p.family, &(p.u.prefix), buf, BUFSIZ), + p.prefixlen, attrstr); + } + + s = stream_new (BGP_MAX_PACKET_SIZE); + + /* Make BGP update packet. */ + bgp_packet_set_marker (s, BGP_MSG_UPDATE); + + /* Unfeasible Routes Length. */ + stream_putw (s, 0); + + /* Make place for total attribute length. */ + pos = stream_get_endp (s); + stream_putw (s, 0); + total_attr_len = bgp_packet_attribute (NULL, peer, s, attr, &p, afi, safi, from, NULL, NULL); + + /* Set Total Path Attribute Length. */ + stream_putw_at (s, pos, total_attr_len); + + /* NLRI set. */ + if (p.family == AF_INET && safi == SAFI_UNICAST) + stream_put_prefix (s, &p); + + /* Set size. */ + bgp_packet_set_size (s); + + packet = stream_dup (s); + stream_free (s); + + /* Dump packet if debug option is set. */ +#ifdef DEBUG + /* bgp_packet_dump (packet); */ +#endif /* DEBUG */ + + /* Add packet to the peer. */ + bgp_packet_add (peer, packet); + + BGP_WRITE_ON (peer->t_write, bgp_write, peer->fd); +} + +void +bgp_default_withdraw_send (struct peer *peer, afi_t afi, safi_t safi) +{ + struct stream *s; + struct stream *packet; + struct prefix p; + unsigned long pos; + unsigned long cp; + bgp_size_t unfeasible_len; + bgp_size_t total_attr_len; + char buf[BUFSIZ]; + + if (DISABLE_BGP_ANNOUNCE) + return; + + if (afi == AFI_IP) + str2prefix ("0.0.0.0/0", &p); +#ifdef HAVE_IPV6 + else + str2prefix ("::/0", &p); +#endif /* HAVE_IPV6 */ + + total_attr_len = 0; + pos = 0; + + if (BGP_DEBUG (update, UPDATE_OUT)) + zlog (peer->log, LOG_DEBUG, "%s send UPDATE %s/%d -- unreachable", + peer->host, inet_ntop(p.family, &(p.u.prefix), buf, BUFSIZ), + p.prefixlen); + + s = stream_new (BGP_MAX_PACKET_SIZE); + + /* Make BGP update packet. */ + bgp_packet_set_marker (s, BGP_MSG_UPDATE); + + /* Unfeasible Routes Length. */; + cp = stream_get_endp (s); + stream_putw (s, 0); + + /* Withdrawn Routes. */ + if (p.family == AF_INET && safi == SAFI_UNICAST) + { + stream_put_prefix (s, &p); + + unfeasible_len = stream_get_endp (s) - cp - 2; + + /* Set unfeasible len. */ + stream_putw_at (s, cp, unfeasible_len); + + /* Set total path attribute length. */ + stream_putw (s, 0); + } + else + { + pos = stream_get_endp (s); + stream_putw (s, 0); + total_attr_len = bgp_packet_withdraw (peer, s, &p, afi, safi, NULL, NULL); + + /* Set total path attribute length. */ + stream_putw_at (s, pos, total_attr_len); + } + + bgp_packet_set_size (s); + + packet = stream_dup (s); + stream_free (s); + + /* Add packet to the peer. */ + bgp_packet_add (peer, packet); + + BGP_WRITE_ON (peer->t_write, bgp_write, peer->fd); +} + +/* Get next packet to be written. */ +static struct stream * +bgp_write_packet (struct peer *peer) +{ + afi_t afi; + safi_t safi; + struct stream *s = NULL; + struct bgp_advertise *adv; + + s = stream_fifo_head (peer->obuf); + if (s) + return s; + + for (afi = AFI_IP; afi < AFI_MAX; afi++) + for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++) + { + adv = FIFO_HEAD (&peer->sync[afi][safi]->withdraw); + if (adv) + { + s = bgp_withdraw_packet (peer, afi, safi); + if (s) + return s; + } + } + + for (afi = AFI_IP; afi < AFI_MAX; afi++) + for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++) + { + adv = FIFO_HEAD (&peer->sync[afi][safi]->update); + if (adv) + { + if (adv->binfo && adv->binfo->uptime < peer->synctime) + { + if (CHECK_FLAG (adv->binfo->peer->cap, PEER_CAP_RESTART_RCV) + && CHECK_FLAG (adv->binfo->peer->cap, PEER_CAP_RESTART_ADV) + && ! CHECK_FLAG (adv->binfo->flags, BGP_INFO_STALE) + && safi != SAFI_MPLS_VPN) + { + if (CHECK_FLAG (adv->binfo->peer->af_sflags[afi][safi], + PEER_STATUS_EOR_RECEIVED)) + s = bgp_update_packet (peer, afi, safi); + } + else + s = bgp_update_packet (peer, afi, safi); + } + + if (s) + return s; + } + + if (CHECK_FLAG (peer->cap, PEER_CAP_RESTART_RCV)) + { + if (peer->afc_nego[afi][safi] && peer->synctime + && ! CHECK_FLAG (peer->af_sflags[afi][safi], PEER_STATUS_EOR_SEND) + && safi != SAFI_MPLS_VPN) + { + SET_FLAG (peer->af_sflags[afi][safi], PEER_STATUS_EOR_SEND); + return bgp_update_packet_eor (peer, afi, safi); + } + } + } + + return NULL; +} + +/* Is there partially written packet or updates we can send right + now. */ +static int +bgp_write_proceed (struct peer *peer) +{ + afi_t afi; + safi_t safi; + struct bgp_advertise *adv; + + if (stream_fifo_head (peer->obuf)) + return 1; + + for (afi = AFI_IP; afi < AFI_MAX; afi++) + for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++) + if (FIFO_HEAD (&peer->sync[afi][safi]->withdraw)) + return 1; + + for (afi = AFI_IP; afi < AFI_MAX; afi++) + for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++) + if ((adv = FIFO_HEAD (&peer->sync[afi][safi]->update)) != NULL) + if (adv->binfo->uptime < peer->synctime) + return 1; + + return 0; +} + +/* Write packet to the peer. */ +int +bgp_write (struct thread *thread) +{ + struct peer *peer; + u_char type; + struct stream *s; + int num; + unsigned int count = 0; + int write_errno; + + /* Yes first of all get peer pointer. */ + peer = THREAD_ARG (thread); + peer->t_write = NULL; + + /* For non-blocking IO check. */ + if (peer->status == Connect) + { + bgp_connect_check (peer); + return 0; + } + + /* Nonblocking write until TCP output buffer is full. */ + while (1) + { + int writenum; + int val; + + s = bgp_write_packet (peer); + if (! s) + return 0; + + /* XXX: FIXME, the socket should be NONBLOCK from the start + * status shouldnt need to be toggled on each write + */ + val = fcntl (peer->fd, F_GETFL, 0); + fcntl (peer->fd, F_SETFL, val|O_NONBLOCK); + + /* Number of bytes to be sent. */ + writenum = stream_get_endp (s) - stream_get_getp (s); + + /* Call write() system call. */ + num = write (peer->fd, STREAM_PNT (s), writenum); + write_errno = errno; + fcntl (peer->fd, F_SETFL, val); + if (num <= 0) + { + /* Partial write. */ + if (write_errno == EWOULDBLOCK || write_errno == EAGAIN) + break; + + BGP_EVENT_ADD (peer, TCP_fatal_error); + return 0; + } + if (num != writenum) + { + stream_forward_getp (s, num); + + if (write_errno == EAGAIN) + break; + + continue; + } + + /* Retrieve BGP packet type. */ + stream_set_getp (s, BGP_MARKER_SIZE + 2); + type = stream_getc (s); + + switch (type) + { + case BGP_MSG_OPEN: + peer->open_out++; + break; + case BGP_MSG_UPDATE: + peer->update_out++; + break; + case BGP_MSG_NOTIFY: + peer->notify_out++; + /* Double start timer. */ + peer->v_start *= 2; + + /* Overflow check. */ + if (peer->v_start >= (60 * 2)) + peer->v_start = (60 * 2); + + /* Flush any existing events */ + BGP_EVENT_ADD (peer, BGP_Stop); + return 0; + case BGP_MSG_KEEPALIVE: + peer->keepalive_out++; + break; + case BGP_MSG_ROUTE_REFRESH_NEW: + case BGP_MSG_ROUTE_REFRESH_OLD: + peer->refresh_out++; + break; + case BGP_MSG_CAPABILITY: + peer->dynamic_cap_out++; + break; + } + + /* OK we send packet so delete it. */ + bgp_packet_delete (peer); + + if (++count >= BGP_WRITE_PACKET_MAX) + break; + } + + if (bgp_write_proceed (peer)) + BGP_WRITE_ON (peer->t_write, bgp_write, peer->fd); + + return 0; +} + +/* This is only for sending NOTIFICATION message to neighbor. */ +static int +bgp_write_notify (struct peer *peer) +{ + int ret; + u_char type; + struct stream *s; + + /* There should be at least one packet. */ + s = stream_fifo_head (peer->obuf); + if (!s) + return 0; + assert (stream_get_endp (s) >= BGP_HEADER_SIZE); + + /* I'm not sure fd is writable. */ + ret = writen (peer->fd, STREAM_DATA (s), stream_get_endp (s)); + if (ret <= 0) + { + BGP_EVENT_ADD (peer, TCP_fatal_error); + return 0; + } + + /* Retrieve BGP packet type. */ + stream_set_getp (s, BGP_MARKER_SIZE + 2); + type = stream_getc (s); + + assert (type == BGP_MSG_NOTIFY); + + /* Type should be notify. */ + peer->notify_out++; + + /* Double start timer. */ + peer->v_start *= 2; + + /* Overflow check. */ + if (peer->v_start >= (60 * 2)) + peer->v_start = (60 * 2); + + BGP_EVENT_ADD (peer, BGP_Stop); + + return 0; +} + +/* Make keepalive packet and send it to the peer. */ +void +bgp_keepalive_send (struct peer *peer) +{ + struct stream *s; + int length; + + s = stream_new (BGP_MAX_PACKET_SIZE); + + /* Make keepalive packet. */ + bgp_packet_set_marker (s, BGP_MSG_KEEPALIVE); + + /* Set packet size. */ + length = bgp_packet_set_size (s); + + /* Dump packet if debug option is set. */ + /* bgp_packet_dump (s); */ + + if (BGP_DEBUG (keepalive, KEEPALIVE)) + zlog_debug ("%s sending KEEPALIVE", peer->host); + if (BGP_DEBUG (normal, NORMAL)) + zlog_debug ("%s send message type %d, length (incl. header) %d", + peer->host, BGP_MSG_KEEPALIVE, length); + + /* Add packet to the peer. */ + bgp_packet_add (peer, s); + + BGP_WRITE_ON (peer->t_write, bgp_write, peer->fd); +} + +/* Make open packet and send it to the peer. */ +void +bgp_open_send (struct peer *peer) +{ + struct stream *s; + int length; + u_int16_t send_holdtime; + as_t local_as; + + if (CHECK_FLAG (peer->config, PEER_CONFIG_TIMER)) + send_holdtime = peer->holdtime; + else + send_holdtime = peer->bgp->default_holdtime; + + /* local-as Change */ + if (peer->change_local_as) + local_as = peer->change_local_as; + else + local_as = peer->local_as; + + s = stream_new (BGP_MAX_PACKET_SIZE); + + /* Make open packet. */ + bgp_packet_set_marker (s, BGP_MSG_OPEN); + + /* Set open packet values. */ + stream_putc (s, BGP_VERSION_4); /* BGP version */ + stream_putw (s, (local_as <= BGP_AS_MAX) ? (u_int16_t) local_as + : BGP_AS_TRANS); + stream_putw (s, send_holdtime); /* Hold Time */ + stream_put_in_addr (s, &peer->local_id); /* BGP Identifier */ + + /* Set capability code. */ + bgp_open_capability (s, peer); + + /* Set BGP packet length. */ + length = bgp_packet_set_size (s); + + if (BGP_DEBUG (normal, NORMAL)) + zlog_debug ("%s sending OPEN, version %d, my as %u, holdtime %d, id %s", + peer->host, BGP_VERSION_4, local_as, + send_holdtime, inet_ntoa (peer->local_id)); + + if (BGP_DEBUG (normal, NORMAL)) + zlog_debug ("%s send message type %d, length (incl. header) %d", + peer->host, BGP_MSG_OPEN, length); + + /* Dump packet if debug option is set. */ + /* bgp_packet_dump (s); */ + + /* Add packet to the peer. */ + bgp_packet_add (peer, s); + + BGP_WRITE_ON (peer->t_write, bgp_write, peer->fd); +} + +/* Send BGP notify packet with data potion. */ +void +bgp_notify_send_with_data (struct peer *peer, u_char code, u_char sub_code, + u_char *data, size_t datalen) +{ + struct stream *s; + int length; + + /* Allocate new stream. */ + s = stream_new (BGP_MAX_PACKET_SIZE); + + /* Make nitify packet. */ + bgp_packet_set_marker (s, BGP_MSG_NOTIFY); + + /* Set notify packet values. */ + stream_putc (s, code); /* BGP notify code */ + stream_putc (s, sub_code); /* BGP notify sub_code */ + + /* If notify data is present. */ + if (data) + stream_write (s, data, datalen); + + /* Set BGP packet length. */ + length = bgp_packet_set_size (s); + + /* Add packet to the peer. */ + stream_fifo_clean (peer->obuf); + bgp_packet_add (peer, s); + + /* For debug */ + { + struct bgp_notify bgp_notify; + int first = 0; + int i; + char c[4]; + + bgp_notify.code = code; + bgp_notify.subcode = sub_code; + bgp_notify.data = NULL; + bgp_notify.length = length - BGP_MSG_NOTIFY_MIN_SIZE; + + if (bgp_notify.length) + { + bgp_notify.data = XMALLOC (MTYPE_TMP, bgp_notify.length * 3); + for (i = 0; i < bgp_notify.length; i++) + if (first) + { + sprintf (c, " %02x", data[i]); + strcat (bgp_notify.data, c); + } + else + { + first = 1; + sprintf (c, "%02x", data[i]); + strcpy (bgp_notify.data, c); + } + } + bgp_notify_print (peer, &bgp_notify, "sending"); + if (bgp_notify.data) + XFREE (MTYPE_TMP, bgp_notify.data); + } + + if (BGP_DEBUG (normal, NORMAL)) + zlog_debug ("%s send message type %d, length (incl. header) %d", + peer->host, BGP_MSG_NOTIFY, length); + + /* peer reset cause */ + if (sub_code != BGP_NOTIFY_CEASE_CONFIG_CHANGE) + { + if (sub_code == BGP_NOTIFY_CEASE_ADMIN_RESET) + peer->last_reset = PEER_DOWN_USER_RESET; + else if (sub_code == BGP_NOTIFY_CEASE_ADMIN_SHUTDOWN) + peer->last_reset = PEER_DOWN_USER_SHUTDOWN; + else + peer->last_reset = PEER_DOWN_NOTIFY_SEND; + } + + /* Call imidiately. */ + BGP_WRITE_OFF (peer->t_write); + + bgp_write_notify (peer); +} + +/* Send BGP notify packet. */ +void +bgp_notify_send (struct peer *peer, u_char code, u_char sub_code) +{ + bgp_notify_send_with_data (peer, code, sub_code, NULL, 0); +} + +/* Send route refresh message to the peer. */ +void +bgp_route_refresh_send (struct peer *peer, afi_t afi, safi_t safi, + u_char orf_type, u_char when_to_refresh, int remove) +{ + struct stream *s; + struct stream *packet; + int length; + struct bgp_filter *filter; + int orf_refresh = 0; + + if (DISABLE_BGP_ANNOUNCE) + return; + + filter = &peer->filter[afi][safi]; + + /* Adjust safi code. */ + if (safi == SAFI_MPLS_VPN) + safi = BGP_SAFI_VPNV4; + + s = stream_new (BGP_MAX_PACKET_SIZE); + + /* Make BGP update packet. */ + if (CHECK_FLAG (peer->cap, PEER_CAP_REFRESH_NEW_RCV)) + bgp_packet_set_marker (s, BGP_MSG_ROUTE_REFRESH_NEW); + else + bgp_packet_set_marker (s, BGP_MSG_ROUTE_REFRESH_OLD); + + /* Encode Route Refresh message. */ + stream_putw (s, afi); + stream_putc (s, 0); + stream_putc (s, safi); + + if (orf_type == ORF_TYPE_PREFIX + || orf_type == ORF_TYPE_PREFIX_OLD) + if (remove || filter->plist[FILTER_IN].ref) + { + u_int16_t orf_len; + unsigned long orfp; + + orf_refresh = 1; + stream_putc (s, when_to_refresh); + stream_putc (s, orf_type); + orfp = stream_get_endp (s); + stream_putw (s, 0); + + if (remove) + { + UNSET_FLAG (peer->af_sflags[afi][safi], PEER_STATUS_ORF_PREFIX_SEND); + stream_putc (s, ORF_COMMON_PART_REMOVE_ALL); + if (BGP_DEBUG (normal, NORMAL)) + zlog_debug ("%s sending REFRESH_REQ to remove ORF(%d) (%s) for afi/safi: %d/%d", + peer->host, orf_type, + (when_to_refresh == REFRESH_DEFER ? "defer" : "immediate"), + afi, safi); + } + else + { + SET_FLAG (peer->af_sflags[afi][safi], PEER_STATUS_ORF_PREFIX_SEND); + prefix_bgp_orf_entry (s, filter->plist[FILTER_IN].ref, + ORF_COMMON_PART_ADD, ORF_COMMON_PART_PERMIT, + ORF_COMMON_PART_DENY); + if (BGP_DEBUG (normal, NORMAL)) + zlog_debug ("%s sending REFRESH_REQ with pfxlist ORF(%d) (%s) for afi/safi: %d/%d", + peer->host, orf_type, + (when_to_refresh == REFRESH_DEFER ? "defer" : "immediate"), + afi, safi); + } + + /* Total ORF Entry Len. */ + orf_len = stream_get_endp (s) - orfp - 2; + stream_putw_at (s, orfp, orf_len); + } + + /* Set packet size. */ + length = bgp_packet_set_size (s); + + if (BGP_DEBUG (normal, NORMAL)) + { + if (! orf_refresh) + zlog_debug ("%s sending REFRESH_REQ for afi/safi: %d/%d", + peer->host, afi, safi); + zlog_debug ("%s send message type %d, length (incl. header) %d", + peer->host, CHECK_FLAG (peer->cap, PEER_CAP_REFRESH_NEW_RCV) ? + BGP_MSG_ROUTE_REFRESH_NEW : BGP_MSG_ROUTE_REFRESH_OLD, length); + } + + /* Make real packet. */ + packet = stream_dup (s); + stream_free (s); + + /* Add packet to the peer. */ + bgp_packet_add (peer, packet); + + BGP_WRITE_ON (peer->t_write, bgp_write, peer->fd); +} + +/* Send capability message to the peer. */ +void +bgp_capability_send (struct peer *peer, afi_t afi, safi_t safi, + int capability_code, int action) +{ + struct stream *s; + struct stream *packet; + int length; + + /* Adjust safi code. */ + if (safi == SAFI_MPLS_VPN) + safi = BGP_SAFI_VPNV4; + + s = stream_new (BGP_MAX_PACKET_SIZE); + + /* Make BGP update packet. */ + bgp_packet_set_marker (s, BGP_MSG_CAPABILITY); + + /* Encode MP_EXT capability. */ + if (capability_code == CAPABILITY_CODE_MP) + { + stream_putc (s, action); + stream_putc (s, CAPABILITY_CODE_MP); + stream_putc (s, CAPABILITY_CODE_MP_LEN); + stream_putw (s, afi); + stream_putc (s, 0); + stream_putc (s, safi); + + if (BGP_DEBUG (normal, NORMAL)) + zlog_debug ("%s sending CAPABILITY has %s MP_EXT CAP for afi/safi: %d/%d", + peer->host, action == CAPABILITY_ACTION_SET ? + "Advertising" : "Removing", afi, safi); + } + + /* Set packet size. */ + length = bgp_packet_set_size (s); + + /* Make real packet. */ + packet = stream_dup (s); + stream_free (s); + + /* Add packet to the peer. */ + bgp_packet_add (peer, packet); + + if (BGP_DEBUG (normal, NORMAL)) + zlog_debug ("%s send message type %d, length (incl. header) %d", + peer->host, BGP_MSG_CAPABILITY, length); + + BGP_WRITE_ON (peer->t_write, bgp_write, peer->fd); +} + +/* RFC1771 6.8 Connection collision detection. */ +static int +bgp_collision_detect (struct peer *new, struct in_addr remote_id) +{ + struct peer *peer; + struct listnode *node, *nnode; + struct bgp *bgp; + + bgp = bgp_get_default (); + if (! bgp) + return 0; + + /* Upon receipt of an OPEN message, the local system must examine + all of its connections that are in the OpenConfirm state. A BGP + speaker may also examine connections in an OpenSent state if it + knows the BGP Identifier of the peer by means outside of the + protocol. If among these connections there is a connection to a + remote BGP speaker whose BGP Identifier equals the one in the + OPEN message, then the local system performs the following + collision resolution procedure: */ + + for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer)) + { + /* Under OpenConfirm status, local peer structure already hold + remote router ID. */ + + if (peer != new + && (peer->status == OpenConfirm || peer->status == OpenSent) + && sockunion_same (&peer->su, &new->su)) + { + /* 1. The BGP Identifier of the local system is compared to + the BGP Identifier of the remote system (as specified in + the OPEN message). */ + + if (ntohl (peer->local_id.s_addr) < ntohl (remote_id.s_addr)) + { + /* 2. If the value of the local BGP Identifier is less + than the remote one, the local system closes BGP + connection that already exists (the one that is + already in the OpenConfirm state), and accepts BGP + connection initiated by the remote system. */ + + if (peer->fd >= 0) + bgp_notify_send (peer, BGP_NOTIFY_CEASE, BGP_NOTIFY_CEASE_COLLISION_RESOLUTION); + return 1; + } + else + { + /* 3. Otherwise, the local system closes newly created + BGP connection (the one associated with the newly + received OPEN message), and continues to use the + existing one (the one that is already in the + OpenConfirm state). */ + + if (new->fd >= 0) + bgp_notify_send (new, BGP_NOTIFY_CEASE, + BGP_NOTIFY_CEASE_COLLISION_RESOLUTION); + return -1; + } + } + } + return 0; +} + +static int +bgp_open_receive (struct peer *peer, bgp_size_t size) +{ + int ret; + u_char version; + u_char optlen; + u_int16_t holdtime; + u_int16_t send_holdtime; + as_t remote_as; + as_t as4 = 0; + struct peer *realpeer; + struct in_addr remote_id; + int capability; + u_int8_t notify_data_remote_as[2]; + u_int8_t notify_data_remote_id[4]; + + realpeer = NULL; + + /* Parse open packet. */ + version = stream_getc (peer->ibuf); + memcpy (notify_data_remote_as, stream_pnt (peer->ibuf), 2); + remote_as = stream_getw (peer->ibuf); + holdtime = stream_getw (peer->ibuf); + memcpy (notify_data_remote_id, stream_pnt (peer->ibuf), 4); + remote_id.s_addr = stream_get_ipv4 (peer->ibuf); + + /* Receive OPEN message log */ + if (BGP_DEBUG (normal, NORMAL)) + zlog_debug ("%s rcv OPEN, version %d, remote-as (in open) %u," + " holdtime %d, id %s", + peer->host, version, remote_as, holdtime, + inet_ntoa (remote_id)); + + /* BEGIN to read the capability here, but dont do it yet */ + capability = 0; + optlen = stream_getc (peer->ibuf); + + if (optlen != 0) + { + /* We need the as4 capability value *right now* because + * if it is there, we have not got the remote_as yet, and without + * that we do not know which peer is connecting to us now. + */ + as4 = peek_for_as4_capability (peer, optlen); + } + + /* Just in case we have a silly peer who sends AS4 capability set to 0 */ + if (CHECK_FLAG (peer->cap, PEER_CAP_AS4_RCV) && !as4) + { + zlog_err ("%s bad OPEN, got AS4 capability, but AS4 set to 0", + peer->host); + bgp_notify_send (peer, BGP_NOTIFY_OPEN_ERR, + BGP_NOTIFY_OPEN_BAD_PEER_AS); + return -1; + } + + if (remote_as == BGP_AS_TRANS) + { + /* Take the AS4 from the capability. We must have received the + * capability now! Otherwise we have a asn16 peer who uses + * BGP_AS_TRANS, for some unknown reason. + */ + if (as4 == BGP_AS_TRANS) + { + zlog_err ("%s [AS4] NEW speaker using AS_TRANS for AS4, not allowed", + peer->host); + bgp_notify_send (peer, BGP_NOTIFY_OPEN_ERR, + BGP_NOTIFY_OPEN_BAD_PEER_AS); + return -1; + } + + if (!as4 && BGP_DEBUG (as4, AS4)) + zlog_debug ("%s [AS4] OPEN remote_as is AS_TRANS, but no AS4." + " Odd, but proceeding.", peer->host); + else if (as4 < BGP_AS_MAX && BGP_DEBUG (as4, AS4)) + zlog_debug ("%s [AS4] OPEN remote_as is AS_TRANS, but AS4 (%u) fits " + "in 2-bytes, very odd peer.", peer->host, as4); + if (as4) + remote_as = as4; + } + else + { + /* We may have a partner with AS4 who has an asno < BGP_AS_MAX */ + /* If we have got the capability, peer->as4cap must match remote_as */ + if (CHECK_FLAG (peer->cap, PEER_CAP_AS4_RCV) + && as4 != remote_as) + { + /* raise error, log this, close session */ + zlog_err ("%s bad OPEN, got AS4 capability, but remote_as %u" + " mismatch with 16bit 'myasn' %u in open", + peer->host, as4, remote_as); + bgp_notify_send (peer, BGP_NOTIFY_OPEN_ERR, + BGP_NOTIFY_OPEN_BAD_PEER_AS); + return -1; + } + } + + /* Lookup peer from Open packet. */ + if (CHECK_FLAG (peer->sflags, PEER_STATUS_ACCEPT_PEER)) + { + int as = 0; + + realpeer = peer_lookup_with_open (&peer->su, remote_as, &remote_id, &as); + + if (! realpeer) + { + /* Peer's source IP address is check in bgp_accept(), so this + must be AS number mismatch or remote-id configuration + mismatch. */ + if (as) + { + if (BGP_DEBUG (normal, NORMAL)) + zlog_debug ("%s bad OPEN, wrong router identifier %s", + peer->host, inet_ntoa (remote_id)); + bgp_notify_send_with_data (peer, BGP_NOTIFY_OPEN_ERR, + BGP_NOTIFY_OPEN_BAD_BGP_IDENT, + notify_data_remote_id, 4); + } + else + { + if (BGP_DEBUG (normal, NORMAL)) + zlog_debug ("%s bad OPEN, remote AS is %u, expected %u", + peer->host, remote_as, peer->as); + bgp_notify_send_with_data (peer, BGP_NOTIFY_OPEN_ERR, + BGP_NOTIFY_OPEN_BAD_PEER_AS, + notify_data_remote_as, 2); + } + return -1; + } + } + + /* When collision is detected and this peer is closed. Retrun + immidiately. */ + ret = bgp_collision_detect (peer, remote_id); + if (ret < 0) + return ret; + + /* Hack part. */ + if (CHECK_FLAG (peer->sflags, PEER_STATUS_ACCEPT_PEER)) + { + if (realpeer->status == Established + && CHECK_FLAG (realpeer->sflags, PEER_STATUS_NSF_MODE)) + { + realpeer->last_reset = PEER_DOWN_NSF_CLOSE_SESSION; + SET_FLAG (realpeer->sflags, PEER_STATUS_NSF_WAIT); + } + else if (ret == 0 && realpeer->status != Active + && realpeer->status != OpenSent + && realpeer->status != OpenConfirm + && realpeer->status != Connect) + { + /* XXX: This is an awful problem.. + * + * According to the RFC we should just let this connection (of the + * accepted 'peer') continue on to Established if the other + * connection (the 'realpeer' one) is in state Connect, and deal + * with the more larval FSM as/when it gets far enough to receive + * an Open. We don't do that though, we instead close the (more + * developed) accepted connection. + * + * This means there's a race, which if hit, can loop: + * + * FSM for A FSM for B + * realpeer accept-peer realpeer accept-peer + * + * Connect Connect + * Active + * OpenSent OpenSent + * <arrive here, + * Notify, delete> + * Idle Active + * OpenSent OpenSent + * <arrive here, + * Notify, delete> + * Idle + * <wait> <wait> + * Connect Connect + * + * + * If both sides are Quagga, they're almost certain to wait for + * the same amount of time of course (which doesn't preclude other + * implementations also waiting for same time). The race is + * exacerbated by high-latency (in bgpd and/or the network). + * + * The reason we do this is because our FSM is tied to our peer + * structure, which carries our configuration information, etc. + * I.e. we can't let the accepted-peer FSM continue on as it is, + * cause it's not associated with any actual peer configuration - + * it's just a dummy. + * + * It's possible we could hack-fix this by just bgp_stop'ing the + * realpeer and continueing on with the 'transfer FSM' below. + * Ideally, we need to seperate FSMs from struct peer. + * + * Setting one side to passive avoids the race, as a workaround. + */ + if (BGP_DEBUG (events, EVENTS)) + zlog_debug ("%s peer status is %s close connection", + realpeer->host, LOOKUP (bgp_status_msg, + realpeer->status)); + bgp_notify_send (peer, BGP_NOTIFY_CEASE, + BGP_NOTIFY_CEASE_CONNECT_REJECT); + + return -1; + } + + if (BGP_DEBUG (events, EVENTS)) + zlog_debug ("%s [Event] Transfer accept BGP peer to real (state %s)", + peer->host, + LOOKUP (bgp_status_msg, realpeer->status)); + + bgp_stop (realpeer); + + /* Transfer file descriptor. */ + realpeer->fd = peer->fd; + peer->fd = -1; + + /* Transfer input buffer. */ + stream_free (realpeer->ibuf); + realpeer->ibuf = peer->ibuf; + realpeer->packet_size = peer->packet_size; + peer->ibuf = NULL; + + /* Transfer status. */ + realpeer->status = peer->status; + bgp_stop (peer); + + /* peer pointer change. Open packet send to neighbor. */ + peer = realpeer; + bgp_open_send (peer); + if (peer->fd < 0) + { + zlog_err ("bgp_open_receive peer's fd is negative value %d", + peer->fd); + return -1; + } + BGP_READ_ON (peer->t_read, bgp_read, peer->fd); + } + + /* remote router-id check. */ + if (remote_id.s_addr == 0 + || ntohl (remote_id.s_addr) >= 0xe0000000 + || ntohl (peer->local_id.s_addr) == ntohl (remote_id.s_addr)) + { + if (BGP_DEBUG (normal, NORMAL)) + zlog_debug ("%s bad OPEN, wrong router identifier %s", + peer->host, inet_ntoa (remote_id)); + bgp_notify_send_with_data (peer, + BGP_NOTIFY_OPEN_ERR, + BGP_NOTIFY_OPEN_BAD_BGP_IDENT, + notify_data_remote_id, 4); + return -1; + } + + /* Set remote router-id */ + peer->remote_id = remote_id; + + /* Peer BGP version check. */ + if (version != BGP_VERSION_4) + { + u_int8_t maxver = BGP_VERSION_4; + if (BGP_DEBUG (normal, NORMAL)) + zlog_debug ("%s bad protocol version, remote requested %d, local request %d", + peer->host, version, BGP_VERSION_4); + bgp_notify_send_with_data (peer, + BGP_NOTIFY_OPEN_ERR, + BGP_NOTIFY_OPEN_UNSUP_VERSION, + &maxver, 1); + return -1; + } + + /* Check neighbor as number. */ + if (remote_as != peer->as) + { + if (BGP_DEBUG (normal, NORMAL)) + zlog_debug ("%s bad OPEN, remote AS is %u, expected %u", + peer->host, remote_as, peer->as); + bgp_notify_send_with_data (peer, + BGP_NOTIFY_OPEN_ERR, + BGP_NOTIFY_OPEN_BAD_PEER_AS, + notify_data_remote_as, 2); + return -1; + } + + /* From the rfc: Upon receipt of an OPEN message, a BGP speaker MUST + calculate the value of the Hold Timer by using the smaller of its + configured Hold Time and the Hold Time received in the OPEN message. + The Hold Time MUST be either zero or at least three seconds. An + implementation may reject connections on the basis of the Hold Time. */ + + if (holdtime < 3 && holdtime != 0) + { + bgp_notify_send (peer, + BGP_NOTIFY_OPEN_ERR, + BGP_NOTIFY_OPEN_UNACEP_HOLDTIME); + return -1; + } + + /* From the rfc: A reasonable maximum time between KEEPALIVE messages + would be one third of the Hold Time interval. KEEPALIVE messages + MUST NOT be sent more frequently than one per second. An + implementation MAY adjust the rate at which it sends KEEPALIVE + messages as a function of the Hold Time interval. */ + + if (CHECK_FLAG (peer->config, PEER_CONFIG_TIMER)) + send_holdtime = peer->holdtime; + else + send_holdtime = peer->bgp->default_holdtime; + + if (holdtime < send_holdtime) + peer->v_holdtime = holdtime; + else + peer->v_holdtime = send_holdtime; + + peer->v_keepalive = peer->v_holdtime / 3; + + /* Open option part parse. */ + if (optlen != 0) + { + ret = bgp_open_option_parse (peer, optlen, &capability); + if (ret < 0) + return ret; + } + else + { + if (BGP_DEBUG (normal, NORMAL)) + zlog_debug ("%s rcvd OPEN w/ OPTION parameter len: 0", + peer->host); + } + + /* Override capability. */ + if (! capability || CHECK_FLAG (peer->flags, PEER_FLAG_OVERRIDE_CAPABILITY)) + { + peer->afc_nego[AFI_IP][SAFI_UNICAST] = peer->afc[AFI_IP][SAFI_UNICAST]; + peer->afc_nego[AFI_IP][SAFI_MULTICAST] = peer->afc[AFI_IP][SAFI_MULTICAST]; + peer->afc_nego[AFI_IP6][SAFI_UNICAST] = peer->afc[AFI_IP6][SAFI_UNICAST]; + peer->afc_nego[AFI_IP6][SAFI_MULTICAST] = peer->afc[AFI_IP6][SAFI_MULTICAST]; + } + + /* Get sockname. */ + bgp_getsockname (peer); + + BGP_EVENT_ADD (peer, Receive_OPEN_message); + + peer->packet_size = 0; + if (peer->ibuf) + stream_reset (peer->ibuf); + + return 0; +} + +/* Parse BGP Update packet and make attribute object. */ +static int +bgp_update_receive (struct peer *peer, bgp_size_t size) +{ + int ret; + u_char *end; + struct stream *s; + struct attr attr; + bgp_size_t attribute_len; + bgp_size_t update_len; + bgp_size_t withdraw_len; + struct bgp_nlri update; + struct bgp_nlri withdraw; + struct bgp_nlri mp_update; + struct bgp_nlri mp_withdraw; + char attrstr[BUFSIZ] = ""; + + /* Status must be Established. */ + if (peer->status != Established) + { + zlog_err ("%s [FSM] Update packet received under status %s", + peer->host, LOOKUP (bgp_status_msg, peer->status)); + bgp_notify_send (peer, BGP_NOTIFY_FSM_ERR, 0); + return -1; + } + + /* Set initial values. */ + memset (&attr, 0, sizeof (struct attr)); + memset (&update, 0, sizeof (struct bgp_nlri)); + memset (&withdraw, 0, sizeof (struct bgp_nlri)); + memset (&mp_update, 0, sizeof (struct bgp_nlri)); + memset (&mp_withdraw, 0, sizeof (struct bgp_nlri)); + + s = peer->ibuf; + end = stream_pnt (s) + size; + + /* RFC1771 6.3 If the Unfeasible Routes Length or Total Attribute + Length is too large (i.e., if Unfeasible Routes Length + Total + Attribute Length + 23 exceeds the message Length), then the Error + Subcode is set to Malformed Attribute List. */ + if (stream_pnt (s) + 2 > end) + { + zlog_err ("%s [Error] Update packet error" + " (packet length is short for unfeasible length)", + peer->host); + bgp_notify_send (peer, BGP_NOTIFY_UPDATE_ERR, + BGP_NOTIFY_UPDATE_MAL_ATTR); + return -1; + } + + /* Unfeasible Route Length. */ + withdraw_len = stream_getw (s); + + /* Unfeasible Route Length check. */ + if (stream_pnt (s) + withdraw_len > end) + { + zlog_err ("%s [Error] Update packet error" + " (packet unfeasible length overflow %d)", + peer->host, withdraw_len); + bgp_notify_send (peer, BGP_NOTIFY_UPDATE_ERR, + BGP_NOTIFY_UPDATE_MAL_ATTR); + return -1; + } + + /* Unfeasible Route packet format check. */ + if (withdraw_len > 0) + { + ret = bgp_nlri_sanity_check (peer, AFI_IP, stream_pnt (s), withdraw_len); + if (ret < 0) + return -1; + + if (BGP_DEBUG (packet, PACKET_RECV)) + zlog_debug ("%s [Update:RECV] Unfeasible NLRI received", peer->host); + + withdraw.afi = AFI_IP; + withdraw.safi = SAFI_UNICAST; + withdraw.nlri = stream_pnt (s); + withdraw.length = withdraw_len; + stream_forward_getp (s, withdraw_len); + } + + /* Attribute total length check. */ + if (stream_pnt (s) + 2 > end) + { + zlog_warn ("%s [Error] Packet Error" + " (update packet is short for attribute length)", + peer->host); + bgp_notify_send (peer, BGP_NOTIFY_UPDATE_ERR, + BGP_NOTIFY_UPDATE_MAL_ATTR); + return -1; + } + + /* Fetch attribute total length. */ + attribute_len = stream_getw (s); + + /* Attribute length check. */ + if (stream_pnt (s) + attribute_len > end) + { + zlog_warn ("%s [Error] Packet Error" + " (update packet attribute length overflow %d)", + peer->host, attribute_len); + bgp_notify_send (peer, BGP_NOTIFY_UPDATE_ERR, + BGP_NOTIFY_UPDATE_MAL_ATTR); + return -1; + } + + /* Parse attribute when it exists. */ + if (attribute_len) + { + ret = bgp_attr_parse (peer, &attr, attribute_len, + &mp_update, &mp_withdraw); + if (ret < 0) + return -1; + } + + /* Logging the attribute. */ + if (BGP_DEBUG (update, UPDATE_IN)) + { + ret= bgp_dump_attr (peer, &attr, attrstr, BUFSIZ); + + if (ret) + zlog (peer->log, LOG_DEBUG, "%s rcvd UPDATE w/ attr: %s", + peer->host, attrstr); + } + + /* Network Layer Reachability Information. */ + update_len = end - stream_pnt (s); + + if (update_len) + { + /* Check NLRI packet format and prefix length. */ + ret = bgp_nlri_sanity_check (peer, AFI_IP, stream_pnt (s), update_len); + if (ret < 0) + return -1; + + /* Set NLRI portion to structure. */ + update.afi = AFI_IP; + update.safi = SAFI_UNICAST; + update.nlri = stream_pnt (s); + update.length = update_len; + stream_forward_getp (s, update_len); + } + + /* NLRI is processed only when the peer is configured specific + Address Family and Subsequent Address Family. */ + if (peer->afc[AFI_IP][SAFI_UNICAST]) + { + if (withdraw.length) + bgp_nlri_parse (peer, NULL, &withdraw); + + if (update.length) + { + /* We check well-known attribute only for IPv4 unicast + update. */ + ret = bgp_attr_check (peer, &attr); + if (ret < 0) + return -1; + + bgp_nlri_parse (peer, &attr, &update); + } + + if (mp_update.length + && mp_update.afi == AFI_IP + && mp_update.safi == SAFI_UNICAST) + bgp_nlri_parse (peer, &attr, &mp_update); + + if (mp_withdraw.length + && mp_withdraw.afi == AFI_IP + && mp_withdraw.safi == SAFI_UNICAST) + bgp_nlri_parse (peer, NULL, &mp_withdraw); + + if (! attribute_len && ! withdraw_len) + { + /* End-of-RIB received */ + SET_FLAG (peer->af_sflags[AFI_IP][SAFI_UNICAST], + PEER_STATUS_EOR_RECEIVED); + + /* NSF delete stale route */ + if (peer->nsf[AFI_IP][SAFI_UNICAST]) + bgp_clear_stale_route (peer, AFI_IP, SAFI_UNICAST); + + if (BGP_DEBUG (normal, NORMAL)) + zlog (peer->log, LOG_DEBUG, "rcvd End-of-RIB for IPv4 Unicast from %s", + peer->host); + } + } + if (peer->afc[AFI_IP][SAFI_MULTICAST]) + { + if (mp_update.length + && mp_update.afi == AFI_IP + && mp_update.safi == SAFI_MULTICAST) + bgp_nlri_parse (peer, &attr, &mp_update); + + if (mp_withdraw.length + && mp_withdraw.afi == AFI_IP + && mp_withdraw.safi == SAFI_MULTICAST) + bgp_nlri_parse (peer, NULL, &mp_withdraw); + + if (! withdraw_len + && mp_withdraw.afi == AFI_IP + && mp_withdraw.safi == SAFI_MULTICAST + && mp_withdraw.length == 0) + { + /* End-of-RIB received */ + SET_FLAG (peer->af_sflags[AFI_IP][SAFI_MULTICAST], + PEER_STATUS_EOR_RECEIVED); + + /* NSF delete stale route */ + if (peer->nsf[AFI_IP][SAFI_MULTICAST]) + bgp_clear_stale_route (peer, AFI_IP, SAFI_MULTICAST); + + if (BGP_DEBUG (normal, NORMAL)) + zlog (peer->log, LOG_DEBUG, "rcvd End-of-RIB for IPv4 Multicast from %s", + peer->host); + } + } + if (peer->afc[AFI_IP6][SAFI_UNICAST]) + { + if (mp_update.length + && mp_update.afi == AFI_IP6 + && mp_update.safi == SAFI_UNICAST) + bgp_nlri_parse (peer, &attr, &mp_update); + + if (mp_withdraw.length + && mp_withdraw.afi == AFI_IP6 + && mp_withdraw.safi == SAFI_UNICAST) + bgp_nlri_parse (peer, NULL, &mp_withdraw); + + if (! withdraw_len + && mp_withdraw.afi == AFI_IP6 + && mp_withdraw.safi == SAFI_UNICAST + && mp_withdraw.length == 0) + { + /* End-of-RIB received */ + SET_FLAG (peer->af_sflags[AFI_IP6][SAFI_UNICAST], PEER_STATUS_EOR_RECEIVED); + + /* NSF delete stale route */ + if (peer->nsf[AFI_IP6][SAFI_UNICAST]) + bgp_clear_stale_route (peer, AFI_IP6, SAFI_UNICAST); + + if (BGP_DEBUG (normal, NORMAL)) + zlog (peer->log, LOG_DEBUG, "rcvd End-of-RIB for IPv6 Unicast from %s", + peer->host); + } + } + if (peer->afc[AFI_IP6][SAFI_MULTICAST]) + { + if (mp_update.length + && mp_update.afi == AFI_IP6 + && mp_update.safi == SAFI_MULTICAST) + bgp_nlri_parse (peer, &attr, &mp_update); + + if (mp_withdraw.length + && mp_withdraw.afi == AFI_IP6 + && mp_withdraw.safi == SAFI_MULTICAST) + bgp_nlri_parse (peer, NULL, &mp_withdraw); + + if (! withdraw_len + && mp_withdraw.afi == AFI_IP6 + && mp_withdraw.safi == SAFI_MULTICAST + && mp_withdraw.length == 0) + { + /* End-of-RIB received */ + + /* NSF delete stale route */ + if (peer->nsf[AFI_IP6][SAFI_MULTICAST]) + bgp_clear_stale_route (peer, AFI_IP6, SAFI_MULTICAST); + + if (BGP_DEBUG (update, UPDATE_IN)) + zlog (peer->log, LOG_DEBUG, "rcvd End-of-RIB for IPv6 Multicast from %s", + peer->host); + } + } + if (peer->afc[AFI_IP][SAFI_MPLS_VPN]) + { + if (mp_update.length + && mp_update.afi == AFI_IP + && mp_update.safi == BGP_SAFI_VPNV4) + bgp_nlri_parse_vpnv4 (peer, &attr, &mp_update); + + if (mp_withdraw.length + && mp_withdraw.afi == AFI_IP + && mp_withdraw.safi == BGP_SAFI_VPNV4) + bgp_nlri_parse_vpnv4 (peer, NULL, &mp_withdraw); + + if (! withdraw_len + && mp_withdraw.afi == AFI_IP + && mp_withdraw.safi == BGP_SAFI_VPNV4 + && mp_withdraw.length == 0) + { + /* End-of-RIB received */ + + if (BGP_DEBUG (update, UPDATE_IN)) + zlog (peer->log, LOG_DEBUG, "rcvd End-of-RIB for VPNv4 Unicast from %s", + peer->host); + } + } + + /* Everything is done. We unintern temporary structures which + interned in bgp_attr_parse(). */ + if (attr.aspath) + aspath_unintern (attr.aspath); + if (attr.community) + community_unintern (attr.community); + if (attr.extra) + { + if (attr.extra->ecommunity) + ecommunity_unintern (attr.extra->ecommunity); + if (attr.extra->cluster) + cluster_unintern (attr.extra->cluster); + if (attr.extra->transit) + transit_unintern (attr.extra->transit); + bgp_attr_extra_free (&attr); + } + + /* If peering is stopped due to some reason, do not generate BGP + event. */ + if (peer->status != Established) + return 0; + + /* Increment packet counter. */ + peer->update_in++; + peer->update_time = time (NULL); + + /* Generate BGP event. */ + BGP_EVENT_ADD (peer, Receive_UPDATE_message); + + return 0; +} + +/* Notify message treatment function. */ +static void +bgp_notify_receive (struct peer *peer, bgp_size_t size) +{ + struct bgp_notify bgp_notify; + + if (peer->notify.data) + { + XFREE (MTYPE_TMP, peer->notify.data); + peer->notify.data = NULL; + peer->notify.length = 0; + } + + bgp_notify.code = stream_getc (peer->ibuf); + bgp_notify.subcode = stream_getc (peer->ibuf); + bgp_notify.length = size - 2; + bgp_notify.data = NULL; + + /* Preserv notify code and sub code. */ + peer->notify.code = bgp_notify.code; + peer->notify.subcode = bgp_notify.subcode; + /* For further diagnostic record returned Data. */ + if (bgp_notify.length) + { + peer->notify.length = size - 2; + peer->notify.data = XMALLOC (MTYPE_TMP, size - 2); + memcpy (peer->notify.data, stream_pnt (peer->ibuf), size - 2); + } + + /* For debug */ + { + int i; + int first = 0; + char c[4]; + + if (bgp_notify.length) + { + bgp_notify.data = XMALLOC (MTYPE_TMP, bgp_notify.length * 3); + for (i = 0; i < bgp_notify.length; i++) + if (first) + { + sprintf (c, " %02x", stream_getc (peer->ibuf)); + strcat (bgp_notify.data, c); + } + else + { + first = 1; + sprintf (c, "%02x", stream_getc (peer->ibuf)); + strcpy (bgp_notify.data, c); + } + } + + bgp_notify_print(peer, &bgp_notify, "received"); + if (bgp_notify.data) + XFREE (MTYPE_TMP, bgp_notify.data); + } + + /* peer count update */ + peer->notify_in++; + + if (peer->status == Established) + peer->last_reset = PEER_DOWN_NOTIFY_RECEIVED; + + /* We have to check for Notify with Unsupported Optional Parameter. + in that case we fallback to open without the capability option. + But this done in bgp_stop. We just mark it here to avoid changing + the fsm tables. */ + if (bgp_notify.code == BGP_NOTIFY_OPEN_ERR && + bgp_notify.subcode == BGP_NOTIFY_OPEN_UNSUP_PARAM ) + UNSET_FLAG (peer->sflags, PEER_STATUS_CAPABILITY_OPEN); + + /* Also apply to Unsupported Capability until remote router support + capability. */ + if (bgp_notify.code == BGP_NOTIFY_OPEN_ERR && + bgp_notify.subcode == BGP_NOTIFY_OPEN_UNSUP_CAPBL) + UNSET_FLAG (peer->sflags, PEER_STATUS_CAPABILITY_OPEN); + + BGP_EVENT_ADD (peer, Receive_NOTIFICATION_message); +} + +/* Keepalive treatment function -- get keepalive send keepalive */ +static void +bgp_keepalive_receive (struct peer *peer, bgp_size_t size) +{ + if (BGP_DEBUG (keepalive, KEEPALIVE)) + zlog_debug ("%s KEEPALIVE rcvd", peer->host); + + BGP_EVENT_ADD (peer, Receive_KEEPALIVE_message); +} + +/* Route refresh message is received. */ +static void +bgp_route_refresh_receive (struct peer *peer, bgp_size_t size) +{ + afi_t afi; + safi_t safi; + u_char reserved; + struct stream *s; + + /* If peer does not have the capability, send notification. */ + if (! CHECK_FLAG (peer->cap, PEER_CAP_REFRESH_ADV)) + { + plog_err (peer->log, "%s [Error] BGP route refresh is not enabled", + peer->host); + bgp_notify_send (peer, + BGP_NOTIFY_HEADER_ERR, + BGP_NOTIFY_HEADER_BAD_MESTYPE); + return; + } + + /* Status must be Established. */ + if (peer->status != Established) + { + plog_err (peer->log, + "%s [Error] Route refresh packet received under status %s", + peer->host, LOOKUP (bgp_status_msg, peer->status)); + bgp_notify_send (peer, BGP_NOTIFY_FSM_ERR, 0); + return; + } + + s = peer->ibuf; + + /* Parse packet. */ + afi = stream_getw (s); + reserved = stream_getc (s); + safi = stream_getc (s); + + if (BGP_DEBUG (normal, NORMAL)) + zlog_debug ("%s rcvd REFRESH_REQ for afi/safi: %d/%d", + peer->host, afi, safi); + + /* Check AFI and SAFI. */ + if ((afi != AFI_IP && afi != AFI_IP6) + || (safi != SAFI_UNICAST && safi != SAFI_MULTICAST + && safi != BGP_SAFI_VPNV4)) + { + if (BGP_DEBUG (normal, NORMAL)) + { + zlog_debug ("%s REFRESH_REQ for unrecognized afi/safi: %d/%d - ignored", + peer->host, afi, safi); + } + return; + } + + /* Adjust safi code. */ + if (safi == BGP_SAFI_VPNV4) + safi = SAFI_MPLS_VPN; + + if (size != BGP_MSG_ROUTE_REFRESH_MIN_SIZE - BGP_HEADER_SIZE) + { + u_char *end; + u_char when_to_refresh; + u_char orf_type; + u_int16_t orf_len; + + if (size - (BGP_MSG_ROUTE_REFRESH_MIN_SIZE - BGP_HEADER_SIZE) < 5) + { + zlog_info ("%s ORF route refresh length error", peer->host); + bgp_notify_send (peer, BGP_NOTIFY_CEASE, 0); + return; + } + + when_to_refresh = stream_getc (s); + end = stream_pnt (s) + (size - 5); + + while ((stream_pnt (s) + 2) < end) + { + orf_type = stream_getc (s); + orf_len = stream_getw (s); + + /* orf_len in bounds? */ + if ((stream_pnt (s) + orf_len) > end) + break; /* XXX: Notify instead?? */ + if (orf_type == ORF_TYPE_PREFIX + || orf_type == ORF_TYPE_PREFIX_OLD) + { + u_char *p_pnt = stream_pnt (s); + u_char *p_end = stream_pnt (s) + orf_len; + struct orf_prefix orfp; + u_char common = 0; + u_int32_t seq; + int psize; + char name[BUFSIZ]; + char buf[BUFSIZ]; + int ret; + + if (BGP_DEBUG (normal, NORMAL)) + { + zlog_debug ("%s rcvd Prefixlist ORF(%d) length %d", + peer->host, orf_type, orf_len); + } + + /* we're going to read at least 1 byte of common ORF header, + * and 7 bytes of ORF Address-filter entry from the stream + */ + if (orf_len < 7) + break; + + /* ORF prefix-list name */ + sprintf (name, "%s.%d.%d", peer->host, afi, safi); + + while (p_pnt < p_end) + { + memset (&orfp, 0, sizeof (struct orf_prefix)); + common = *p_pnt++; + if (common & ORF_COMMON_PART_REMOVE_ALL) + { + if (BGP_DEBUG (normal, NORMAL)) + zlog_debug ("%s rcvd Remove-All pfxlist ORF request", peer->host); + prefix_bgp_orf_remove_all (name); + break; + } + memcpy (&seq, p_pnt, sizeof (u_int32_t)); + p_pnt += sizeof (u_int32_t); + orfp.seq = ntohl (seq); + orfp.ge = *p_pnt++; + orfp.le = *p_pnt++; + orfp.p.prefixlen = *p_pnt++; + orfp.p.family = afi2family (afi); + psize = PSIZE (orfp.p.prefixlen); + memcpy (&orfp.p.u.prefix, p_pnt, psize); + p_pnt += psize; + + if (BGP_DEBUG (normal, NORMAL)) + zlog_debug ("%s rcvd %s %s seq %u %s/%d ge %d le %d", + peer->host, + (common & ORF_COMMON_PART_REMOVE ? "Remove" : "Add"), + (common & ORF_COMMON_PART_DENY ? "deny" : "permit"), + orfp.seq, + inet_ntop (orfp.p.family, &orfp.p.u.prefix, buf, BUFSIZ), + orfp.p.prefixlen, orfp.ge, orfp.le); + + ret = prefix_bgp_orf_set (name, afi, &orfp, + (common & ORF_COMMON_PART_DENY ? 0 : 1 ), + (common & ORF_COMMON_PART_REMOVE ? 0 : 1)); + + if (ret != CMD_SUCCESS) + { + if (BGP_DEBUG (normal, NORMAL)) + zlog_debug ("%s Received misformatted prefixlist ORF. Remove All pfxlist", peer->host); + prefix_bgp_orf_remove_all (name); + break; + } + } + peer->orf_plist[afi][safi] = + prefix_list_lookup (AFI_ORF_PREFIX, name); + } + stream_forward_getp (s, orf_len); + } + if (BGP_DEBUG (normal, NORMAL)) + zlog_debug ("%s rcvd Refresh %s ORF request", peer->host, + when_to_refresh == REFRESH_DEFER ? "Defer" : "Immediate"); + if (when_to_refresh == REFRESH_DEFER) + return; + } + + /* First update is deferred until ORF or ROUTE-REFRESH is received */ + if (CHECK_FLAG (peer->af_sflags[afi][safi], PEER_STATUS_ORF_WAIT_REFRESH)) + UNSET_FLAG (peer->af_sflags[afi][safi], PEER_STATUS_ORF_WAIT_REFRESH); + + /* Perform route refreshment to the peer */ + bgp_announce_route (peer, afi, safi); +} + +static int +bgp_capability_msg_parse (struct peer *peer, u_char *pnt, bgp_size_t length) +{ + u_char *end; + struct capability_mp_data mpc; + struct capability_header *hdr; + u_char action; + struct bgp *bgp; + afi_t afi; + safi_t safi; + + bgp = peer->bgp; + end = pnt + length; + + while (pnt < end) + { + /* We need at least action, capability code and capability length. */ + if (pnt + 3 > end) + { + zlog_info ("%s Capability length error", peer->host); + bgp_notify_send (peer, BGP_NOTIFY_CEASE, 0); + return -1; + } + action = *pnt; + hdr = (struct capability_header *)(pnt + 1); + + /* Action value check. */ + if (action != CAPABILITY_ACTION_SET + && action != CAPABILITY_ACTION_UNSET) + { + zlog_info ("%s Capability Action Value error %d", + peer->host, action); + bgp_notify_send (peer, BGP_NOTIFY_CEASE, 0); + return -1; + } + + if (BGP_DEBUG (normal, NORMAL)) + zlog_debug ("%s CAPABILITY has action: %d, code: %u, length %u", + peer->host, action, hdr->code, hdr->length); + + /* Capability length check. */ + if ((pnt + hdr->length + 3) > end) + { + zlog_info ("%s Capability length error", peer->host); + bgp_notify_send (peer, BGP_NOTIFY_CEASE, 0); + return -1; + } + + /* Fetch structure to the byte stream. */ + memcpy (&mpc, pnt + 3, sizeof (struct capability_mp_data)); + + /* We know MP Capability Code. */ + if (hdr->code == CAPABILITY_CODE_MP) + { + afi = ntohs (mpc.afi); + safi = mpc.safi; + + /* Ignore capability when override-capability is set. */ + if (CHECK_FLAG (peer->flags, PEER_FLAG_OVERRIDE_CAPABILITY)) + continue; + + if (!bgp_afi_safi_valid_indices (afi, &safi)) + { + if (BGP_DEBUG (normal, NORMAL)) + zlog_debug ("%s Dynamic Capability MP_EXT afi/safi invalid " + "(%u/%u)", peer->host, afi, safi); + continue; + } + + /* Address family check. */ + if (BGP_DEBUG (normal, NORMAL)) + zlog_debug ("%s CAPABILITY has %s MP_EXT CAP for afi/safi: %u/%u", + peer->host, + action == CAPABILITY_ACTION_SET + ? "Advertising" : "Removing", + ntohs(mpc.afi) , mpc.safi); + + if (action == CAPABILITY_ACTION_SET) + { + peer->afc_recv[afi][safi] = 1; + if (peer->afc[afi][safi]) + { + peer->afc_nego[afi][safi] = 1; + bgp_announce_route (peer, afi, safi); + } + } + else + { + peer->afc_recv[afi][safi] = 0; + peer->afc_nego[afi][safi] = 0; + + if (peer_active_nego (peer)) + bgp_clear_route (peer, afi, safi, BGP_CLEAR_ROUTE_NORMAL); + else + BGP_EVENT_ADD (peer, BGP_Stop); + } + } + else + { + zlog_warn ("%s unrecognized capability code: %d - ignored", + peer->host, hdr->code); + } + pnt += hdr->length + 3; + } + return 0; +} + +/* Dynamic Capability is received. + * + * This is exported for unit-test purposes + */ extern int bgp_capability_receive(struct peer*, bgp_size_t) ; +int +bgp_capability_receive (struct peer *peer, bgp_size_t size) +{ + u_char *pnt; + + /* Fetch pointer. */ + pnt = stream_pnt (peer->ibuf); + + if (BGP_DEBUG (normal, NORMAL)) + zlog_debug ("%s rcv CAPABILITY", peer->host); + + /* If peer does not have the capability, send notification. */ + if (! CHECK_FLAG (peer->cap, PEER_CAP_DYNAMIC_ADV)) + { + plog_err (peer->log, "%s [Error] BGP dynamic capability is not enabled", + peer->host); + bgp_notify_send (peer, + BGP_NOTIFY_HEADER_ERR, + BGP_NOTIFY_HEADER_BAD_MESTYPE); + return -1; + } + + /* Status must be Established. */ + if (peer->status != Established) + { + plog_err (peer->log, + "%s [Error] Dynamic capability packet received under status %s", peer->host, LOOKUP (bgp_status_msg, peer->status)); + bgp_notify_send (peer, BGP_NOTIFY_FSM_ERR, 0); + return -1; + } + + /* Parse packet. */ + return bgp_capability_msg_parse (peer, pnt, size); +} + +/* BGP read utility function. */ +static int +bgp_read_packet (struct peer *peer) +{ + int nbytes; + int readsize; + + readsize = peer->packet_size - stream_get_endp (peer->ibuf); + + /* If size is zero then return. */ + if (! readsize) + return 0; + + /* Read packet from fd. */ + nbytes = stream_read_unblock (peer->ibuf, peer->fd, readsize); + + /* If read byte is smaller than zero then error occured. */ + if (nbytes < 0) + { + if (errno == EAGAIN) + return -1; + + plog_err (peer->log, "%s [Error] bgp_read_packet error: %s", + peer->host, safe_strerror (errno)); + + if (peer->status == Established) + { + if (CHECK_FLAG (peer->sflags, PEER_STATUS_NSF_MODE)) + { + peer->last_reset = PEER_DOWN_NSF_CLOSE_SESSION; + SET_FLAG (peer->sflags, PEER_STATUS_NSF_WAIT); + } + else + peer->last_reset = PEER_DOWN_CLOSE_SESSION; + } + + BGP_EVENT_ADD (peer, TCP_fatal_error); + return -1; + } + + /* When read byte is zero : clear bgp peer and return */ + if (nbytes == 0) + { + if (BGP_DEBUG (events, EVENTS)) + plog_debug (peer->log, "%s [Event] BGP connection closed fd %d", + peer->host, peer->fd); + + if (peer->status == Established) + { + if (CHECK_FLAG (peer->sflags, PEER_STATUS_NSF_MODE)) + { + peer->last_reset = PEER_DOWN_NSF_CLOSE_SESSION; + SET_FLAG (peer->sflags, PEER_STATUS_NSF_WAIT); + } + else + peer->last_reset = PEER_DOWN_CLOSE_SESSION; + } + + BGP_EVENT_ADD (peer, TCP_connection_closed); + return -1; + } + + /* We read partial packet. */ + if (stream_get_endp (peer->ibuf) != peer->packet_size) + return -1; + + return 0; +} + +/* Marker check. */ +static int +bgp_marker_all_one (struct stream *s, int length) +{ + int i; + + for (i = 0; i < length; i++) + if (s->data[i] != 0xff) + return 0; + + return 1; +} + +/* Starting point of packet process function. */ +int +bgp_read (struct thread *thread) +{ + int ret; + u_char type = 0; + struct peer *peer; + bgp_size_t size; + char notify_data_length[2]; + + /* Yes first of all get peer pointer. */ + peer = THREAD_ARG (thread); + peer->t_read = NULL; + + /* For non-blocking IO check. */ + if (peer->status == Connect) + { + bgp_connect_check (peer); + goto done; + } + else + { + if (peer->fd < 0) + { + zlog_err ("bgp_read peer's fd is negative value %d", peer->fd); + return -1; + } + BGP_READ_ON (peer->t_read, bgp_read, peer->fd); + } + + /* Read packet header to determine type of the packet */ + if (peer->packet_size == 0) + peer->packet_size = BGP_HEADER_SIZE; + + if (stream_get_endp (peer->ibuf) < BGP_HEADER_SIZE) + { + ret = bgp_read_packet (peer); + + /* Header read error or partial read packet. */ + if (ret < 0) + goto done; + + /* Get size and type. */ + stream_forward_getp (peer->ibuf, BGP_MARKER_SIZE); + memcpy (notify_data_length, stream_pnt (peer->ibuf), 2); + size = stream_getw (peer->ibuf); + type = stream_getc (peer->ibuf); + + if (BGP_DEBUG (normal, NORMAL) && type != 2 && type != 0) + zlog_debug ("%s rcv message type %d, length (excl. header) %d", + peer->host, type, size - BGP_HEADER_SIZE); + + /* Marker check */ + if (((type == BGP_MSG_OPEN) || (type == BGP_MSG_KEEPALIVE)) + && ! bgp_marker_all_one (peer->ibuf, BGP_MARKER_SIZE)) + { + bgp_notify_send (peer, + BGP_NOTIFY_HEADER_ERR, + BGP_NOTIFY_HEADER_NOT_SYNC); + goto done; + } + + /* BGP type check. */ + if (type != BGP_MSG_OPEN && type != BGP_MSG_UPDATE + && type != BGP_MSG_NOTIFY && type != BGP_MSG_KEEPALIVE + && type != BGP_MSG_ROUTE_REFRESH_NEW + && type != BGP_MSG_ROUTE_REFRESH_OLD + && type != BGP_MSG_CAPABILITY) + { + if (BGP_DEBUG (normal, NORMAL)) + plog_debug (peer->log, + "%s unknown message type 0x%02x", + peer->host, type); + bgp_notify_send_with_data (peer, + BGP_NOTIFY_HEADER_ERR, + BGP_NOTIFY_HEADER_BAD_MESTYPE, + &type, 1); + goto done; + } + /* Mimimum packet length check. */ + if ((size < BGP_HEADER_SIZE) + || (size > BGP_MAX_PACKET_SIZE) + || (type == BGP_MSG_OPEN && size < BGP_MSG_OPEN_MIN_SIZE) + || (type == BGP_MSG_UPDATE && size < BGP_MSG_UPDATE_MIN_SIZE) + || (type == BGP_MSG_NOTIFY && size < BGP_MSG_NOTIFY_MIN_SIZE) + || (type == BGP_MSG_KEEPALIVE && size != BGP_MSG_KEEPALIVE_MIN_SIZE) + || (type == BGP_MSG_ROUTE_REFRESH_NEW && size < BGP_MSG_ROUTE_REFRESH_MIN_SIZE) + || (type == BGP_MSG_ROUTE_REFRESH_OLD && size < BGP_MSG_ROUTE_REFRESH_MIN_SIZE) + || (type == BGP_MSG_CAPABILITY && size < BGP_MSG_CAPABILITY_MIN_SIZE)) + { + if (BGP_DEBUG (normal, NORMAL)) + plog_debug (peer->log, + "%s bad message length - %d for %s", + peer->host, size, + type == 128 ? "ROUTE-REFRESH" : + bgp_type_str[(int) type]); + bgp_notify_send_with_data (peer, + BGP_NOTIFY_HEADER_ERR, + BGP_NOTIFY_HEADER_BAD_MESLEN, + (u_char *) notify_data_length, 2); + goto done; + } + + /* Adjust size to message length. */ + peer->packet_size = size; + } + + ret = bgp_read_packet (peer); + if (ret < 0) + goto done; + + /* Get size and type again. */ + size = stream_getw_from (peer->ibuf, BGP_MARKER_SIZE); + type = stream_getc_from (peer->ibuf, BGP_MARKER_SIZE + 2); + + /* BGP packet dump function. */ + bgp_dump_packet (peer, type, peer->ibuf); + + size = (peer->packet_size - BGP_HEADER_SIZE); + + /* Read rest of the packet and call each sort of packet routine */ + switch (type) + { + case BGP_MSG_OPEN: + peer->open_in++; + bgp_open_receive (peer, size); /* XXX return value ignored! */ + break; + case BGP_MSG_UPDATE: + peer->readtime = time(NULL); /* Last read timer reset */ + bgp_update_receive (peer, size); + break; + case BGP_MSG_NOTIFY: + bgp_notify_receive (peer, size); + break; + case BGP_MSG_KEEPALIVE: + peer->readtime = time(NULL); /* Last read timer reset */ + bgp_keepalive_receive (peer, size); + break; + case BGP_MSG_ROUTE_REFRESH_NEW: + case BGP_MSG_ROUTE_REFRESH_OLD: + peer->refresh_in++; + bgp_route_refresh_receive (peer, size); + break; + case BGP_MSG_CAPABILITY: + peer->dynamic_cap_in++; + bgp_capability_receive (peer, size); + break; + } + + /* Clear input buffer. */ + peer->packet_size = 0; + if (peer->ibuf) + stream_reset (peer->ibuf); + + done: + if (CHECK_FLAG (peer->sflags, PEER_STATUS_ACCEPT_PEER)) + { + if (BGP_DEBUG (events, EVENTS)) + zlog_debug ("%s [Event] Accepting BGP peer delete", peer->host); + peer_delete (peer); + } + return 0; +} diff --git a/bgpd/bgp_msg_write.h b/bgpd/bgp_msg_write.h new file mode 100644 index 00000000..8f0ebe31 --- /dev/null +++ b/bgpd/bgp_msg_write.h @@ -0,0 +1,57 @@ +/* BGP packet management header. + Copyright (C) 1999 Kunihiro Ishiguro + +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_PACKET_H +#define _QUAGGA_BGP_PACKET_H + +#define BGP_NLRI_LENGTH 1U +#define BGP_TOTAL_ATTR_LEN 2U +#define BGP_UNFEASIBLE_LEN 2U +#define BGP_WRITE_PACKET_MAX 10U + +/* When to refresh */ +#define REFRESH_IMMEDIATE 1 +#define REFRESH_DEFER 2 + +/* ORF Common part flag */ +#define ORF_COMMON_PART_ADD 0x00 +#define ORF_COMMON_PART_REMOVE 0x80 +#define ORF_COMMON_PART_REMOVE_ALL 0xC0 +#define ORF_COMMON_PART_PERMIT 0x00 +#define ORF_COMMON_PART_DENY 0x20 + +/* Packet send and receive function prototypes. */ +extern int bgp_read (struct thread *); +extern int bgp_write (struct thread *); + +extern void bgp_keepalive_send (struct peer *); +extern void bgp_open_send (struct peer *); +extern void bgp_notify_send (struct peer *, u_int8_t, u_int8_t); +extern void bgp_notify_send_with_data (struct peer *, u_int8_t, u_int8_t, + u_int8_t *, size_t); +extern void bgp_route_refresh_send (struct peer *, afi_t, safi_t, u_char, u_char, int); +extern void bgp_capability_send (struct peer *, afi_t, safi_t, int, int); +extern void bgp_default_update_send (struct peer *, struct attr *, + afi_t, safi_t, struct peer *); +extern void bgp_default_withdraw_send (struct peer *, afi_t, safi_t); + +extern int bgp_capability_receive (struct peer *, bgp_size_t); + +#endif /* _QUAGGA_BGP_PACKET_H */ diff --git a/bgpd/bgp_notification.c b/bgpd/bgp_notification.c index 49165626..3f6ecad9 100644 --- a/bgpd/bgp_notification.c +++ b/bgpd/bgp_notification.c @@ -91,6 +91,11 @@ bgp_notify_dup(bgp_notify notification) /*------------------------------------------------------------------------------ * Set notification (if any) + * + * Frees any existing notification at the destination. + * + * NB: copies the source pointer -- so must be clear about responsibility + * for the notification structure. */ extern void bgp_notify_set(bgp_notify* p_dst, bgp_notify src) @@ -100,7 +105,9 @@ bgp_notify_set(bgp_notify* p_dst, bgp_notify src) } ; /*------------------------------------------------------------------------------ - * Set notification (if any) + * Set notification (if any) to a *copy* of the source. + * + * Frees any existing notification at the destination. */ extern void bgp_notify_set_dup(bgp_notify* p_dst, bgp_notify src) @@ -109,19 +116,30 @@ bgp_notify_set_dup(bgp_notify* p_dst, bgp_notify src) } ; /*------------------------------------------------------------------------------ - * Free notification structure + * Set notification (if any) and set source pointer NULL * - * Does nothing if there is no structure. + * Frees any existing notification at the destination. * - * Returns: NULL + * NB: responsibility for the notification structure passes to the destination. */ -extern bgp_notify -bgp_notify_free(bgp_notify notification) +extern void +bgp_notify_set_mov(bgp_notify* p_dst, bgp_notify* p_src) { - if (notification != NULL) - XFREE(MTYPE_BGP_NOTIFY, notification) ; + bgp_notify_free(*p_dst) ; + *p_dst = *p_src ; + *p_src = NULL ; +} ; - return NULL ; +/*------------------------------------------------------------------------------ + * Free notification structure + * + * Does nothing if there is no structure. + */ +extern void +bgp_notify_free(bgp_notify* p_notification) +{ + if (*p_notification != NULL) + XFREE(MTYPE_BGP_NOTIFY, *p_notification) ; /* sets *p_notification NULL */ } ; /*============================================================================== diff --git a/bgpd/bgp_notification.h b/bgpd/bgp_notification.h index 80a6cffd..72608534 100644 --- a/bgpd/bgp_notification.h +++ b/bgpd/bgp_notification.h @@ -224,8 +224,8 @@ struct bgp_notify extern bgp_notify bgp_notify_new(bgp_nom_code_t code, bgp_nom_subcode_t subcode, bgp_size_t size) ; -extern bgp_notify -bgp_notify_free(bgp_notify notification) ; +extern void +bgp_notify_free(bgp_notify* p_notification) ; extern bgp_notify bgp_notify_dup(bgp_notify notification) ; @@ -236,6 +236,9 @@ bgp_notify_set(bgp_notify* p_dst, bgp_notify src) ; extern void bgp_notify_set_dup(bgp_notify* p_dst, bgp_notify src) ; +extern void +bgp_notify_set_mov(bgp_notify* p_dst, bgp_notify* p_src) ; + /*============================================================================== * Access Functions -- mostly inline * diff --git a/bgpd/bgp_session.c b/bgpd/bgp_session.c index f77e9e34..214829ef 100644 --- a/bgpd/bgp_session.c +++ b/bgpd/bgp_session.c @@ -107,6 +107,7 @@ bgp_session_init_new(bgp_session session, bgp_peer peer) * event -- bgp_session_null_event * notification -- NULL -- none * err -- 0 -- none + * ordinal -- 0 -- unset * * open_send -- NULL -- none * open_recv -- NULL -- none @@ -205,8 +206,7 @@ bgp_session_enable(bgp_peer peer) session->keepalive_timer_interval = peer->v_keepalive ; /* Initialise the BGP Open negotiating position */ - - /*....*/ + /* TODO: set up open_state in bgp_session_enable.... */ /* Routeing Engine does the state change now. */ session->state = bgp_session_sEnabled ; @@ -229,8 +229,14 @@ bgp_session_do_enable(mqueue_block mqb, mqb_flag_t flag) { if (flag == mqb_action) { + bgp_session session = mqb_get_arg0(mqb) ; + + BGP_SESSION_LOCK(session) ; /*<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ + assert(session->state == bgp_session_sEnabled) ; + bgp_fsm_enable_session(session) ; + BGP_SESSION_UNLOCK(session) ; /*>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ } ; mqb_free(mqb) ; @@ -263,7 +269,7 @@ bgp_session_disable(bgp_peer peer, bgp_notify notification) if (!bgp_session_is_active(session)) { - bgp_notify_free(notification) ; /* discard any bgp_notify */ + bgp_notify_free(¬ification) ; /* discard any bgp_notify */ return ; } ; @@ -306,12 +312,11 @@ bgp_session_do_disable(mqueue_block mqb, mqb_flag_t flag) /*============================================================================== * Send session event signal from BGP Engine to Routeing Engine * - * + * The event has been posted to the session, and now is dispatched to the + * Routeing Engine. */ extern void -bgp_session_event(bgp_session session, bgp_session_event_t event, - bgp_notify notification, - bgp_session_state_t state) +bgp_session_event(bgp_session session) { struct bgp_session_event_args* args ; mqueue_block mqb ; @@ -320,9 +325,10 @@ bgp_session_event(bgp_session session, bgp_session_event_t event, args = mqb_get_args(mqb) ; - args->event = event ; - args->notification = notification ; - args->state = state ; + args->event = session->event ; + args->notification = bgp_notify_dup(session->notification) ; + args->state = session->state ; + args->ordinal = session->ordinal ; bgp_to_peering(mqb) ; } diff --git a/bgpd/bgp_session.h b/bgpd/bgp_session.h index 4b5f8ca7..416a1876 100644 --- a/bgpd/bgp_session.h +++ b/bgpd/bgp_session.h @@ -121,6 +121,7 @@ struct bgp_session bgp_session_event_t event ; /* last event */ bgp_notify notification ; /* if any sent/received */ int err ; /* errno, if any */ + bgp_connection_ord_t ordinal ; /* primary/secondary connection */ /* The Routeing Engine sets open_send and clears open_recv before enabling * the session, and may not change them while sEnabled/sEstablished. @@ -213,6 +214,7 @@ 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_connection_ord_t ordinal ; /* primary/secondary connection */ bgp_session_state_t state ; /* after the event */ } ; @@ -249,9 +251,7 @@ 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) ; +bgp_session_event(bgp_session session) ; extern void bgp_session_update_send(bgp_session session, struct stream* upd) ; diff --git a/lib/sockunion.h b/lib/sockunion.h index 97fd8b72..8fcaded4 100644 --- a/lib/sockunion.h +++ b/lib/sockunion.h @@ -23,8 +23,8 @@ #ifndef _ZEBRA_SOCKUNION_H #define _ZEBRA_SOCKUNION_H +#include "zebra.h" #include "symtab.h" -#include "zassert.h" #if 0 union sockunion { |