diff options
Diffstat (limited to 'bgpd/bgp_session.c')
-rw-r--r-- | bgpd/bgp_session.c | 229 |
1 files changed, 147 insertions, 82 deletions
diff --git a/bgpd/bgp_session.c b/bgpd/bgp_session.c index 8d8bfea2..9de8678a 100644 --- a/bgpd/bgp_session.c +++ b/bgpd/bgp_session.c @@ -40,7 +40,6 @@ #include "lib/zassert.h" /* prototypes */ -static int bgp_session_defer_if_limping(bgp_session session); static void bgp_session_do_enable(mqueue_block mqb, mqb_flag_t flag) ; static void bgp_session_do_update_recv(mqueue_block mqb, mqb_flag_t flag); static void bgp_session_do_update_send(mqueue_block mqb, mqb_flag_t flag); @@ -99,7 +98,7 @@ static void bgp_session_do_route_refresh_recv(mqueue_block mqb, mqb_flag_t flag) */ /*------------------------------------------------------------------------------ - * Initialise new session structure -- allocate if required. + * Allocate & initialise new session structure. * * Ties peer and session together. Sets session sIdle, initialises mutex. * @@ -108,30 +107,34 @@ static void bgp_session_do_route_refresh_recv(mqueue_block mqb, mqb_flag_t flag) * NB: if not allocating, the existing session MUST be sIdle/sDisabled OR never * been kissed. * - * NB: in any event, the peer's peer index entry MUST have a NULL accept - * pointer. + * NB: peer MUST NOT have a session set up: + * + * (a) because if there was a session, there would have to be code here + * to worry about its state, and tearing it down etc. + * + * (b) so that do not have to worry about BGP Engine reaching the old + * session while it was being replaced or whatever. */ extern bgp_session -bgp_session_init_new(bgp_session session, bgp_peer peer) +bgp_session_init_new(bgp_peer peer) { + bgp_session session ; + assert(peer->session == NULL) ; - assert(peer->index_entry->accept == NULL) ; - if (session == NULL) - session = XCALLOC(MTYPE_BGP_SESSION, sizeof(struct bgp_session)) ; - else - memset(session, 0, sizeof(struct bgp_session)) ; + session = XCALLOC(MTYPE_BGP_SESSION, sizeof(struct bgp_session)) ; qpt_mutex_init_new(&session->mutex, qpt_mutex_recursive) ; - peer->session = session ; session->peer = peer ; - session->state = bgp_session_sIdle ; + bgp_peer_lock(peer) ; /* Account for the session->peer pointer */ - session->index_entry = peer->index_entry ; + session->state = bgp_session_sIdle ; /* Zeroising the structure has set: * + * delete_me -- 0 -- false + * * event -- bgp_session_null_event * notification -- NULL -- none * err -- 0 -- none @@ -173,25 +176,73 @@ bgp_session_init_new(bgp_session session, bgp_peer peer) * * connections[] -- NULL -- none * active -- false, not yet active + * accept -- false, not yet ready to accept() */ confirm(bgp_session_null_event == 0) ; + /* Once the session is fully initialised, can set peer->session pointer. + * + * NB: this is done last and under the Peer Index Mutex, so that the + * accept() code does not trip over. + */ + bgp_peer_index_set_session(peer, session) ; + return session ; } ; -/* Free session structure +/*============================================================================== + * Routing Engine: delete session for given peer. + * + * This is for use when the peer itself is being deleted. (Peer MUST be in + * pDeleting state.) + * + * Does nothing if there is no session ! + * + * If the session is active, simply sets delete_me flag, which will be honoured + * when the session goes dDisabled. Note, it is the callers responsibility + * to arrange for that to happen. + * + * If the session is not active, it is immediately freed. * + * NB: if the session is freed, the peer may vanish at the same time ! */ -extern bgp_session -bgp_session_free(bgp_session session) +extern void +bgp_session_delete(bgp_peer peer) { + bgp_session session = peer->session ; + if (session == NULL) - return NULL; + return ; /* easy if no session anyway */ + + assert(peer == session->peer) ; + + /* If is active, set flag so that session is deleted when next it becomes + * sDisabled. + */ + if (bgp_session_is_active(session)) + { + session->delete_me = true ; + return ; + } ; + + /*--------------------------------------------------------------------------*/ + /* Proceed to free the session structure. */ + + /* Make sure that the BGP Engine has, in fact, let go of the session. + * + * The LOCK/UNLOCK makes sure that the BGP Engine has unlocked the session. + * + * Without this, the qpt_mutex_destroy() can fail horribly, if the BGP + * Engine sends the disable acknowledge before finally unlocking the session. + */ + BGP_SESSION_LOCK(session) ; /*<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ - assert(!bgp_session_is_active(session)) ; + BGP_SESSION_UNLOCK(session) ; /*>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ qpt_mutex_destroy(&session->mutex, 0) ; + /* Proceed to dismantle the session. */ + bgp_notify_unset(&session->notification); bgp_open_state_free(session->open_send); bgp_open_state_free(session->open_recv); @@ -206,12 +257,24 @@ bgp_session_free(bgp_session session) sockunion_unset(&session->su_local) ; sockunion_unset(&session->su_remote) ; + /* Drop the peer->session and session->peer pointers + * + * NB: the peer->session pointer is cleared under the Peer Index Mutex, + * so that the accept() code does not trip over. + * + * NB: at this point it is possible for the peer structure to suddenly + * vanish -- if peer has been deleted, and has been waiting for the + * session to go sDisabled. + */ + bgp_peer_index_set_session(peer, NULL) ; + + session->peer = NULL ; + bgp_peer_unlock(peer) ; /* NB: peer->session == NULL */ + /* Zeroize to catch dangling references asap */ memset(session, 0, sizeof(struct bgp_session)) ; XFREE(MTYPE_BGP_SESSION, session); - - return NULL; -} +} ; /*============================================================================== * Routing Engine: enable session for given peer -- allocate if required. @@ -226,6 +289,8 @@ bgp_session_enable(bgp_peer peer) bgp_session session ; mqueue_block mqb ; + assert(peer->state = bgp_peer_pIdle) ; + /* Set up session if required. Check session if already exists. * * Only the Routing Engine creates sessions, so it is safe to pick up the @@ -238,27 +303,24 @@ bgp_session_enable(bgp_peer peer) session = peer->session ; if (session == NULL) - session = bgp_session_init_new(NULL, peer) ; + session = bgp_session_init_new(peer) ; else { assert(session->peer == peer) ; - /* if session is limping then defer the enable */ - if (bgp_session_defer_if_limping(session)) - return; assert(!bgp_session_is_active(session)) ; } ; /* Initialise what we need to make and run connections */ - session->state = bgp_session_sIdle; - session->defer_enable = 0; - session->flow_control = 0; - session->event = bgp_session_null_event; - bgp_notify_unset(&session->notification); - session->err = 0; - session->ordinal = 0; + session->state = bgp_session_sIdle ; + session->delete_me = false ; + session->flow_control = 0 ; + session->event = bgp_session_null_event ; + bgp_notify_unset(&session->notification) ; + session->err = 0 ; + session->ordinal = 0 ; - session->open_send = bgp_peer_open_state_init_new(session->open_send, peer); - bgp_open_state_unset(&session->open_recv); + session->open_send = bgp_peer_open_state_init_new(session->open_send, peer) ; + bgp_open_state_unset(&session->open_recv) ; session->connect = (peer->flags & PEER_FLAG_PASSIVE) == 0 ; session->listen = 1 ; @@ -306,12 +368,22 @@ bgp_session_enable(bgp_peer peer) session->hold_timer_interval = peer->v_holdtime ; session->keepalive_timer_interval = peer->v_keepalive ; - session->as4 = 0; - session->route_refresh_pre = 0; + session->as4 = false ; + session->route_refresh_pre = false ; + session->orf_prefix_pre = false ; /* su_local set when session Established */ /* su_remote set when session Established */ + /* TODO: check whether session stats should persist */ + memset(&session->stats, 0, sizeof(struct bgp_session_stats)) ; + + memset(&session->connections, 0, + sizeof(bgp_connection) * bgp_connection_count) ; + + session->active = false ; + session->accept = false ; + /* Routeing Engine does the state change now. */ /* Now pass the session to the BGP Engine, which will set about */ @@ -340,7 +412,7 @@ bgp_session_do_enable(mqueue_block mqb, mqb_flag_t flag) BGP_SESSION_LOCK(session) ; /*<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ - session->active = 1 ; + session->active = true ; bgp_fsm_enable_session(session) ; BGP_SESSION_UNLOCK(session) ; /*>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ @@ -352,6 +424,8 @@ bgp_session_do_enable(mqueue_block mqb, mqb_flag_t flag) /*============================================================================== * Routing Engine: disable session for given peer -- if enabled (!). * + * Does nothing if the session is not sEnabled or sEstablished. + * * Passes any bgp_notify to the BGP Engine, which will dispose of it in due * course. * @@ -390,7 +464,6 @@ bgp_session_disable(bgp_peer peer, bgp_notify notification) /* Now change to limping state */ session->state = bgp_session_sLimping; - session->defer_enable = 0; /* Ask the BGP engine to disable the session. * @@ -470,7 +543,10 @@ bgp_session_event(bgp_session session, bgp_session_event_t event, mqueue_block mqb ; if (stopped) - session->active = 0 ; /* ignore updates etc */ + { + session->active = false ; /* ignore updates etc */ + session->accept = false ; /* for completeness */ + } ; mqb = mqb_init_new(NULL, bgp_session_do_event, session) ; @@ -774,6 +850,8 @@ bgp_session_update_recv(bgp_session session, struct stream* buf, bgp_size_t size /*------------------------------------------------------------------------------ * Routing Engine: process incoming update message -- mqb action function. + * + * Discard the update if the session is not sEstablished. */ static void bgp_session_do_update_recv(mqueue_block mqb, mqb_flag_t flag) @@ -781,10 +859,9 @@ bgp_session_do_update_recv(mqueue_block mqb, mqb_flag_t flag) bgp_session session = mqb_get_arg0(mqb) ; struct bgp_session_update_args* args = mqb_get_args(mqb) ; - if ((flag == mqb_action) && (session->state == bgp_session_sEstablished)) + if ( (flag == mqb_action) && (session->state == bgp_session_sEstablished) ) { - bgp_peer peer = session->peer; - + bgp_peer peer = session->peer; stream_free(peer->ibuf); peer->ibuf = args->buf; bgp_update_receive (peer, args->size); @@ -826,8 +903,8 @@ bgp_session_do_route_refresh_recv(mqueue_block mqb, mqb_flag_t flag) struct bgp_session_route_refresh_args* args = mqb_get_args(mqb) ; bgp_session session = mqb_get_arg0(mqb) ; - if ((flag == mqb_action) && (session->state == bgp_session_sEstablished)) - bgp_route_refresh_recv(session->peer, args->rr); + if ( (flag == mqb_action) && (session->state == bgp_session_sEstablished) ) + bgp_route_refresh_recv(session->peer, args->rr) ; bgp_route_refresh_free(args->rr); mqb_free(mqb); @@ -860,9 +937,10 @@ bgp_session_do_XON(mqueue_block mqb, mqb_flag_t flag) { bgp_session session = mqb_get_arg0(mqb) ; - if ((flag == mqb_action) && (session->state == bgp_session_sEstablished)) + if ( (flag == mqb_action) && (session->state == bgp_session_sEstablished) ) { int xoff = (session->flow_control <= 0); + session->flow_control = BGP_XON_REFRESH; if (xoff) bgp_write (session->peer, NULL) ; @@ -923,59 +1001,46 @@ bgp_session_do_set_ttl(mqueue_block mqb, mqb_flag_t flag) */ /*------------------------------------------------------------------------------ - * See if session exists and is active. + * Routing Engine: see if session exists and is active. * - * Ensure that if exists and is not active, that the peer index entry accept - * pointer is NULL -- this is largely paranoia, but it would be a grave - * mistake for the listening socket(s) to find a session which is not active ! + * If exists then performs a few checks, just to make sure things are straight. * - * NB: accessing Routing Engine "private" variable -- no lock required. + * NB: accessing Routing Engine "private" variable -- no lock required. * - * accessing index_entry when not active -- no lock required. + * checks session->active, only when not active -- no lock required. */ -extern int +extern bool bgp_session_is_active(bgp_session session) { - int active ; + bool active ; if (session == NULL) - active = 0 ; + active = false ; else { - active = ( (session->state == bgp_session_sEnabled) - || (session->state == bgp_session_sEstablished) - || (session->state == bgp_session_sLimping) ) ; - - if (!active) - assert(session->index_entry->accept == NULL) ; + switch (session->state) + { + case bgp_session_sIdle: + case bgp_session_sDisabled: + assert(!session->active) ; + active = false ; + break ; + + case bgp_session_sEnabled: + case bgp_session_sEstablished: + case bgp_session_sLimping: + active = true ; + break ; + + default: + zabort("invalid session->state") ; + } ; } ; return active ; } ; /*------------------------------------------------------------------------------ - * Routing Engine: if session is limping we defer re-enabling the session - * until it is disabled. - * - * returns 1 if limping and defer - * returns 0 if not limping - * - * NB: accessing Routing Engine "private" variable -- no lock required. - */ -static int -bgp_session_defer_if_limping(bgp_session session) -{ - int defer_enable = 0 ; - - if (session == NULL) - defer_enable = 0 ; - else - defer_enable = (session->state == bgp_session_sLimping) ; - - return session->defer_enable = defer_enable ; -} ; - -/*------------------------------------------------------------------------------ * Get a copy of the session statistics, copied all at once so * forms a consistent snapshot */ |