summaryrefslogtreecommitdiffstats
path: root/bgpd/bgp_session.c
diff options
context:
space:
mode:
Diffstat (limited to 'bgpd/bgp_session.c')
-rw-r--r--bgpd/bgp_session.c1069
1 files changed, 1069 insertions, 0 deletions
diff --git a/bgpd/bgp_session.c b/bgpd/bgp_session.c
new file mode 100644
index 00000000..99077907
--- /dev/null
+++ b/bgpd/bgp_session.c
@@ -0,0 +1,1069 @@
+/* BGP Session -- functions
+ * 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 "bgpd/bgp_session.h"
+#include "bgpd/bgp_common.h"
+#include "bgpd/bgp_peer.h"
+#include "bgpd/bgp_engine.h"
+#include "bgpd/bgp_peer_index.h"
+#include "bgpd/bgp_fsm.h"
+#include "bgpd/bgp_open_state.h"
+#include "bgpd/bgp_route_refresh.h"
+#include "bgpd/bgp_msg_write.h"
+#include "bgpd/bgp_network.h"
+
+#include "bgpd/bgp_packet.h"
+#include "bgp_debug.h"
+
+#include "lib/memory.h"
+#include "lib/sockunion.h"
+#include "lib/log.h"
+#include "lib/mqueue.h"
+#include "lib/zassert.h"
+
+/* prototypes */
+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);
+static void bgp_session_do_end_of_rib_send(mqueue_block mqb, mqb_flag_t flag);
+static void bgp_session_do_route_refresh_send(mqueue_block mqb,
+ mqb_flag_t flag);
+static void bgp_session_do_disable(mqueue_block mqb, mqb_flag_t flag) ;
+static void bgp_session_XON(bgp_session session);
+static void bgp_session_do_XON(mqueue_block mqb, mqb_flag_t flag);
+static void bgp_session_do_set_ttl(mqueue_block mqb, mqb_flag_t flag);
+static void bgp_session_do_route_refresh_recv(mqueue_block mqb, mqb_flag_t flag);
+
+/*==============================================================================
+ * BGP Session.
+ *
+ * Every bgp_peer has (at most) one bgp_session associated with it.
+ *
+ * A session is shared by the Routeing Engine and the BGP Engine -- so there
+ * is a mutex to coordinate access.
+ *
+ * A session is created some time before it is enabled, and may be destroyed
+ * once the session is disabled.
+ *
+ * A session may be in one of the states:
+ *
+ * * bgp_session_sIdle -- not doing anything
+ * * bgp_session_sEnabled -- the BGP Engine is trying to connect
+ * * bgp_session_sEstablished -- the BGP Engine is exchanging updates etc
+ * * bgp_session_sLimping -- in the process of being disabled
+ * * bgp_session_sDisabled -- completely disabled
+ *
+ * NB: in sIdle and sDisabled states the BGP Engine has no interest in the
+ * session. These are known as the "inactive" states.
+ *
+ * NB: in sEnabled, sEstablished and sLimping states the BGP Engine is running
+ * connection(s) for the session. These are known as the "active" states.
+ *
+ * While the session is active the Routeing Engine should not attempt to
+ * change any shared item in the session, except under the mutex. And
+ * even then it may make no sense !
+ *
+ * NB: a session reaches eDisabled when the Routing Engine has sent a disable
+ * request to the BGP Engine, AND an eDisabled event has come back.
+ *
+ * While the Routing Engine is waiting for the eDisabled event, the session
+ * is in sLimping state.
+ *
+ * The BGP Engine's primary interest is in its (private) bgp_connection
+ * structure(s), which (while a session is sEnabled, sEstablished or sLimping)
+ * are pointed to by their associated session.
+ */
+
+/*==============================================================================
+ * BGP Session handling.
+ *
+ */
+
+/*------------------------------------------------------------------------------
+ * Allocate & initialise new session structure.
+ *
+ * Ties peer and session together. Sets session sIdle, initialises mutex.
+ *
+ * Unsets everything else -- mostly by zeroising it.
+ *
+ * NB: if not allocating, the existing session MUST be sIdle/sDisabled OR never
+ * been kissed.
+ *
+ * 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_peer peer)
+{
+ bgp_session session ;
+
+ assert(peer->session == NULL) ;
+
+ session = XCALLOC(MTYPE_BGP_SESSION, sizeof(struct bgp_session)) ;
+
+ qpt_mutex_init_new(&session->mutex, qpt_mutex_recursive) ;
+
+ session->peer = peer ;
+ bgp_peer_lock(peer) ; /* Account for the session->peer pointer */
+
+ 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
+ * ordinal -- 0 -- unset
+ *
+ * open_send -- NULL -- none
+ * open_recv -- NULL -- none
+ *
+ * connect -- unset, false
+ * listen -- unset, false
+ *
+ * cap_suppress -- unset, false
+ * cap_override -- unset, false
+ * cap_strict -- unset, false
+ *
+ * ttl -- unset
+ * port -- unset
+ * as_peer -- unset
+ * su_peer -- NULL -- none
+ *
+ * ifname -- NULL -- none
+ * ifindex -- 0 -- none
+ * ifaddress -- NULL -- none
+ *
+ * log -- NULL -- none
+ * host -- NULL -- none
+ * password -- NULL -- none
+ *
+ * idle_hold_timer_interval )
+ * connect_retry_timer_interval )
+ * open_hold_timer_interval ) unset
+ * hold_timer_interval )
+ * keepalive_timer_interval )
+ *
+ * as4 -- unset, false
+ * route_refresh_pre -- unset, false
+ *
+ * su_local -- NULL -- none
+ * su_remote -- NULL -- none
+ *
+ * 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 ;
+} ;
+
+/*==============================================================================
+ * 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 void
+bgp_session_delete(bgp_peer peer)
+{
+ bgp_session session = peer->session ;
+
+ if (session == 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) ; /*<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
+
+ 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);
+ if (session->ifname != NULL)
+ free(session->ifname) ;
+ sockunion_unset(&session->ifaddress) ;
+ sockunion_unset(&session->su_peer) ;
+ if (session->host != NULL)
+ XFREE(MTYPE_BGP_PEER_HOST, session->host);
+ if (session->password != NULL)
+ XFREE(MTYPE_PEER_PASSWORD, session->password);
+ 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);
+} ;
+
+/*==============================================================================
+ * Routing Engine: enable session for given peer -- allocate if required.
+ *
+ * Sets up the session given the current state of the peer. If the state
+ * changes, then need to disable the session and re-enable it again with new
+ * parameters -- unless something more cunning is devised.
+ */
+extern void
+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
+ * peer->session pointer and test it.
+ *
+ * If session exists, it MUST be inactive.
+ *
+ * Routing Engine does not require the mutex while the session is inactive.
+ */
+ session = peer->session ;
+
+ if (session == NULL)
+ session = bgp_session_init_new(peer) ;
+ else
+ {
+ assert(session->peer == peer) ;
+ assert(!bgp_session_is_active(session)) ;
+ } ;
+
+ /* Initialise what we need to make and run connections */
+ 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->connect = (peer->flags & PEER_FLAG_PASSIVE) == 0 ;
+ session->listen = true ;
+
+ session->ttl = peer->ttl ;
+ session->gtsm = peer->gtsm ;
+ session->port = peer->port ;
+
+ if (session->ifname != NULL)
+ free(session->ifname) ;
+ session->ifindex = 0 ;
+
+ if (peer->ifname != NULL)
+ {
+ session->ifname = strdup(peer->ifname) ;
+ session->ifindex = if_nametoindex(peer->ifname) ;
+ } ;
+
+ sockunion_unset(&session->ifaddress) ;
+ if (peer->update_source != NULL)
+ session->ifaddress = sockunion_dup(peer->update_source) ;
+ else if (peer->update_if != NULL)
+ session->ifaddress = bgp_peer_get_ifaddress(peer, peer->update_if,
+ peer->su.sa.sa_family) ;
+ session->as_peer = peer->as ;
+ sockunion_set_dup(&session->su_peer, &peer->su) ;
+
+ session->log = peer->log ;
+
+ /* take copies of host and password */
+ if (session->host != NULL)
+ XFREE(MTYPE_BGP_PEER_HOST, session->host);
+ session->host = (peer->host != NULL)
+ ? XSTRDUP(MTYPE_BGP_PEER_HOST, peer->host)
+ : NULL;
+ if (session->password != NULL)
+ XFREE(MTYPE_PEER_PASSWORD, session->password);
+ session->password = (peer->password != NULL)
+ ? XSTRDUP(MTYPE_PEER_PASSWORD, peer->password)
+ : NULL;
+
+ session->idle_hold_timer_interval = peer->v_start ;
+ session->connect_retry_timer_interval = peer->v_connect ;
+ /* TODO: proper value for open_hold_timer_interval */
+ session->open_hold_timer_interval = 4 * 60;
+ session->hold_timer_interval = peer->v_holdtime ;
+ session->keepalive_timer_interval = peer->v_keepalive ;
+
+ 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 */
+ /* making and running a connection to the peer. */
+
+ mqb = mqb_init_new(NULL, bgp_session_do_enable, session) ;
+
+ confirm(sizeof(struct bgp_session_enable_args) == 0) ;
+
+ session->state = bgp_session_sEnabled;
+
+ ++bgp_engine_queue_stats.event ;
+
+ bgp_to_bgp_engine(mqb, mqb_ordinary) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * BGP Engine: session enable message action
+ */
+static void
+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) ; /*<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
+
+ session->active = true ;
+ bgp_fsm_enable_session(session) ;
+
+ BGP_SESSION_UNLOCK(session) ; /*>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
+ } ;
+
+ mqb_free(mqb) ;
+} ;
+
+/*==============================================================================
+ * 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.
+ *
+ * If no bgp_notify provided, no notify will be sent.
+ *
+ * The BGP Engine will stop the session -- unless it is already stopped due to
+ * some event in the BGP Engine. In any case, the BGP Engine will respond with
+ * an eDisabled.
+ *
+ * NB: is taking responsibility for the notification, which is either freed
+ * here or passed to the BGP Engine.
+ */
+extern void
+bgp_session_disable(bgp_peer peer, bgp_notify notification)
+{
+ bgp_session session ;
+ mqueue_block mqb ;
+ struct bgp_session_disable_args* args ;
+
+ session = peer->session ;
+ assert((session != NULL) && (session->peer == peer)) ;
+
+ /* Do nothing if session is not active, or is already limping. */
+
+ if ( (session->state != bgp_session_sEnabled) &&
+ (session->state != bgp_session_sEstablished) )
+ {
+ bgp_notify_free(notification) ; /* discard any bgp_notify */
+ return ;
+ } ;
+
+ /* Can revoke whatever may be queued already. Will revoke again when the
+ * disable is acknowledged to finally clear the session out of the queue.
+ */
+ mqueue_revoke(routing_nexus->queue, session) ;
+
+ /* Now change to limping state */
+ session->state = bgp_session_sLimping;
+
+ /* Ask the BGP engine to disable the session.
+ *
+ * NB: the session may already be stopped when the BGP Engine sees this
+ * message:
+ *
+ * * the disable is being issued in response to a stopped event from
+ * the BGP Engine.
+ *
+ * * the session is stopped, but the message to the Routing Engine is
+ * still in its message queue.
+ *
+ * * the session is stopped while the disable message is in the
+ * BGP Engine queue.
+ *
+ * in any case, the BGP Engine responds with an eDisabled message to
+ * acknowledge the disable request -- and the session will then be
+ * disabled.
+ *
+ * NB: The BGP Engine will discard any outstanding work for the session.
+ *
+ * The Routing Engine should discard all further messages for this
+ * session up to the eDisabled, and must then discard any other
+ * messages for the session.
+ *
+ * NB: the Routing Engine MUST not issue any further messages until it sees
+ * the returned eDisabled event.
+ */
+ mqb = mqb_init_new(NULL, bgp_session_do_disable, session) ;
+
+ args = mqb_get_args(mqb) ;
+ args->notification = notification ;
+
+ ++bgp_engine_queue_stats.event ;
+
+ bgp_to_bgp_engine(mqb, mqb_priority) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * BGP Engine: session disable message action
+ *
+ * NB: either passes the notification to the FSM or frees it here.
+ */
+static void
+bgp_session_do_disable(mqueue_block mqb, mqb_flag_t flag)
+{
+ bgp_session session = mqb_get_arg0(mqb) ;
+ struct bgp_session_disable_args* args = mqb_get_args(mqb) ;
+
+ if (flag == mqb_action)
+ {
+ /* Immediately discard any other messages for this session. */
+ mqueue_revoke(bgp_nexus->queue, session) ;
+
+ /* Get the FSM to send any notification and close connections */
+ bgp_fsm_disable_session(session, args->notification) ;
+ }
+ else
+ bgp_notify_free(args->notification) ;
+
+ mqb_free(mqb) ;
+}
+
+/*==============================================================================
+ * BGP Engine: send session event signal to Routeing Engine
+ *
+ * NB: is passing responsibility for the notification to the Routing Engine.
+ */
+extern void
+bgp_session_event(bgp_session session, bgp_session_event_t event,
+ bgp_notify notification,
+ int err,
+ bgp_connection_ord_t ordinal,
+ bool stopped)
+{
+ struct bgp_session_event_args* args ;
+ mqueue_block mqb ;
+
+ if (stopped)
+ {
+ session->active = false ; /* ignore updates etc */
+ session->accept = false ; /* for completeness */
+ } ;
+
+ mqb = mqb_init_new(NULL, bgp_session_do_event, session) ;
+
+ args = mqb_get_args(mqb) ;
+
+ args->event = event ;
+ args->notification = notification ;
+ args->err = err ;
+ args->ordinal = ordinal ;
+ args->stopped = stopped,
+
+ ++routing_engine_queue_stats.event ;
+
+ bgp_to_routing_engine(mqb, stopped ? mqb_priority : mqb_ordinary) ;
+} ;
+
+/*==============================================================================
+ * Routing Engine: dispatch update(s) to peer -> BGP Engine
+ *
+ * PRO TEM -- this is being passed the pre-packaged BGP message(s).
+ *
+ * The BGP Engine takes care of discarding the stream block(s) once dealt with.
+ */
+extern void
+bgp_session_update_send(bgp_session session, struct stream_fifo* fifo)
+{
+ struct bgp_session_update_args* args ;
+ mqueue_block mqb ;
+
+ mqb = mqb_init_new(NULL, bgp_session_do_update_send, session) ;
+
+ args = mqb_get_args(mqb) ;
+ args->buf = stream_fifo_head(fifo) ;
+ args->is_pending = NULL ;
+ args->xon_kick = (session->flow_control == BGP_XON_KICK);
+
+ ++bgp_engine_queue_stats.update ;
+
+ bgp_to_bgp_engine(mqb, mqb_ordinary) ;
+
+ stream_fifo_reset(fifo) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * BGP Engine: write given BGP update message(s) -- mqb action function.
+ *
+ * Each connection has a pending queue associated with it, onto which messages
+ * are put if the connection's write buffer is unable to absorb any further
+ * messages.
+ *
+ * This function is called both when the mqb is received from the Routing
+ * Engine, and when the BGP Engine is trying to empty the connection's pending
+ * queue.
+ *
+ * When the mqb is received from the Routing Engine, then:
+ *
+ * -- if the connection's pending queue is empty, try to send the message(s).
+ *
+ * When the mqb is from connection's pending queue, then:
+ *
+ * -- try to send the message(s).
+ *
+ * In any case, if cannot send all the message(s), add it (back) to the
+ * connection's pending queue.
+ *
+ * If the mqb has been dealt with, it is freed, along with the stream buffer.
+ * Also, update the flow control counter, and issue XON if required.
+ */
+static void
+bgp_session_do_update_send(mqueue_block mqb, mqb_flag_t flag)
+{
+ struct bgp_session_update_args* args = mqb_get_args(mqb) ;
+ bgp_session session = mqb_get_arg0(mqb) ;
+
+ while (args->buf != NULL)
+ {
+ struct stream* buf ;
+
+ if ((flag == mqb_action) && session->active)
+ {
+ bgp_connection connection ;
+
+ connection = session->connections[bgp_connection_primary] ;
+ assert(connection != NULL) ;
+
+ /* If established, try and send. */
+ if (connection->state == bgp_fsm_sEstablished)
+ {
+ int ret ;
+ ret = bgp_connection_no_pending(connection, &args->is_pending) ;
+
+ if (ret != 0)
+ ret = bgp_msg_send_update(connection, args->buf) ;
+
+ if (ret == 0)
+ {
+ /* Either there is already a pending queue, or the message
+ * could not be sent (and has not failed) -- so add to the
+ * pending queue.
+ */
+ bgp_connection_add_pending(connection, mqb,
+ &args->is_pending) ;
+ return ; /* Quit now, with message intact. */
+ }
+ } ;
+ } ;
+
+ buf = args->buf ;
+ args->buf = buf->next ;
+
+ stream_free(buf) ;
+ } ;
+
+ /* If gets to here, then has dealt with all message(s). */
+ if ((flag == mqb_action) && (args->xon_kick))
+ bgp_session_XON(session) ;
+
+ mqb_free(mqb) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Routing Engine: are we in XON state ?
+ */
+extern int
+bgp_session_is_XON(bgp_peer peer)
+{
+ int result = 0;
+ bgp_session session = peer->session;
+
+ result = (session->flow_control > 0);
+
+ return result;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Count down flow control -- signal if reached XON point.
+ */
+extern int
+bgp_session_dec_flow_count(bgp_peer peer)
+{
+ bgp_session session = peer->session;
+
+ assert(session->flow_control > 0) ;
+ return (--session->flow_control == BGP_XON_KICK) ;
+} ;
+
+/*==============================================================================
+ * Routing Engine: dispatch Route Refresh to peer -> BGP Engine
+ *
+ * The BGP Engine takes care of discarding the bgp_route_refresh once it's been
+ * dealt with.
+ */
+extern void
+bgp_session_route_refresh_send(bgp_session session, bgp_route_refresh rr)
+{
+ struct bgp_session_route_refresh_args* args ;
+ mqueue_block mqb ;
+
+ mqb = mqb_init_new(NULL, bgp_session_do_route_refresh_send, session) ;
+
+ args = mqb_get_args(mqb) ;
+ args->rr = rr ;
+ args->is_pending = NULL ;
+
+ ++bgp_engine_queue_stats.event ;
+
+ bgp_to_bgp_engine(mqb, mqb_ordinary) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * BGP Engine: write given BGP route refresh message -- mqb action function.
+ *
+ * The logic here is the same as for bgp_session_do_update_send -- except that
+ * there is no flow control (!).
+ */
+static void
+bgp_session_do_route_refresh_send(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->active)
+ {
+ bgp_connection connection = session->connections[bgp_connection_primary] ;
+ assert(connection != NULL) ;
+
+ /* If established, try and send. */
+ if (connection->state == bgp_fsm_sEstablished)
+ {
+ int ret = bgp_connection_no_pending(connection, &args->is_pending) ;
+
+ if (ret != 0)
+ ret = bgp_msg_send_route_refresh(connection, args->rr) ;
+
+ if (ret == 0)
+ {
+ /* Either there is already a pending queue, or the message
+ * could not be sent (and has not failed) -- so add to the
+ * pending queue.
+ */
+ bgp_connection_add_pending(connection, mqb, &args->is_pending) ;
+ return ; /* Quit now, with message intact. */
+ } ;
+ } ;
+ } ;
+
+ bgp_route_refresh_free(args->rr) ;
+ mqb_free(mqb) ;
+} ;
+
+/*==============================================================================
+ * Routing Engine: dispatch End-of-RIB to peer -> BGP Engine
+ */
+extern void
+bgp_session_end_of_rib_send(bgp_session session, qAFI_t afi, qSAFI_t safi)
+{
+ struct bgp_session_end_of_rib_args* args ;
+ mqueue_block mqb ;
+ qafx_num_t qafx ;
+
+ qafx = qafx_num_from_qAFI_qSAFI(afi, safi) ;
+
+ mqb = mqb_init_new(NULL, bgp_session_do_end_of_rib_send, session) ;
+
+ args = mqb_get_args(mqb) ;
+ args->afi = get_iAFI(qafx) ;
+ args->safi = get_iSAFI(qafx) ;
+ args->is_pending = NULL ;
+
+ ++bgp_engine_queue_stats.xon ;
+
+ bgp_to_bgp_engine(mqb, mqb_ordinary) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * BGP Engine: write given BGP end-of-RIB message -- mqb action function.
+ *
+ * The logic here is the same as for bgp_session_do_update_send -- except that
+ * there is no flow control (!).
+ */
+static void
+bgp_session_do_end_of_rib_send(mqueue_block mqb, mqb_flag_t flag)
+{
+ struct bgp_session_end_of_rib_args* args = mqb_get_args(mqb) ;
+ bgp_session session = mqb_get_arg0(mqb) ;
+
+ if ((flag == mqb_action) && session->active)
+ {
+ bgp_connection connection = session->connections[bgp_connection_primary] ;
+ assert(connection != NULL) ;
+
+ /* If established, try and send. */
+ if (connection->state == bgp_fsm_sEstablished)
+ {
+ int ret = bgp_connection_no_pending(connection, &args->is_pending) ;
+
+ if (ret != 0)
+ ret = bgp_msg_send_end_of_rib(connection, args->afi, args->safi) ;
+
+ if (ret == 0)
+ {
+ /* Either there is already a pending queue, or the message
+ * could not be sent (and has not failed) -- so add to the
+ * pending queue.
+ */
+ bgp_connection_add_pending(connection, mqb, &args->is_pending) ;
+
+ return ; /* Quit now, with message intact. */
+ } ;
+ } ;
+ } ;
+
+ mqb_free(mqb) ;
+} ;
+
+/*==============================================================================
+ * BGP Engine: forward incoming update -> Routing Engine
+ *
+ * PRO TEM -- this is being passed the raw BGP message.
+ *
+ * The Routing Engine takes care of discarding the stream block once it's been
+ * dealt with.
+ */
+extern void
+bgp_session_update_recv(bgp_session session, struct stream* buf, bgp_size_t size)
+{
+ struct bgp_session_update_args* args ;
+ mqueue_block mqb ;
+
+ mqb = mqb_init_new(NULL, bgp_session_do_update_recv, session) ;
+
+ args = mqb_get_args(mqb) ;
+ args->buf = stream_dup(buf) ;
+ args->size = size;
+ args->xon_kick = 0;
+
+ ++routing_engine_queue_stats.update ;
+
+ bgp_to_routing_engine(mqb, mqb_ordinary) ;
+}
+
+/*------------------------------------------------------------------------------
+ * 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)
+{
+ 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) )
+ {
+ bgp_peer peer = session->peer;
+ stream_free(peer->ibuf);
+ peer->ibuf = args->buf;
+ bgp_update_receive (peer, args->size);
+ }
+ else
+ stream_free(args->buf) ;
+
+ mqb_free(mqb) ;
+}
+
+/*==============================================================================
+ * BGP Engine: received Route Refresh to peer
+ *
+ * The Routing Engine takes care of discarding the bgp_route_refresh once
+ * it's been dealt with.
+ */
+extern void
+bgp_session_route_refresh_recv(bgp_session session, bgp_route_refresh rr)
+{
+ struct bgp_session_route_refresh_args* args ;
+ mqueue_block mqb ;
+
+ mqb = mqb_init_new(NULL, bgp_session_do_route_refresh_recv, session) ;
+
+ args = mqb_get_args(mqb) ;
+ args->rr = rr ;
+ args->is_pending = NULL ;
+
+ bgp_to_routing_engine(mqb, mqb_ordinary) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Routing Engine: receive given BGP route refresh message -- mqb action
+ * function.
+ */
+static void
+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) ;
+
+ bgp_route_refresh_free(args->rr);
+ mqb_free(mqb);
+}
+
+/*==============================================================================
+ * BGP Engine: send XON message to Routing Engine
+ *
+ * Can be sent more packets now
+ */
+static void
+bgp_session_XON(bgp_session session)
+{
+ mqueue_block mqb ;
+
+ mqb = mqb_init_new(NULL, bgp_session_do_XON, session) ;
+
+ confirm(sizeof(struct bgp_session_XON_args) == 0) ;
+
+ ++routing_engine_queue_stats.xon ;
+
+ bgp_to_routing_engine(mqb, mqb_ordinary) ;
+}
+
+/*------------------------------------------------------------------------------
+ * Routing Engine: process incoming XON message -- mqb action function.
+ */
+static void
+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) )
+ {
+ int xoff = (session->flow_control <= 0);
+
+ session->flow_control = BGP_XON_REFRESH;
+ if (xoff)
+ bgp_write (session->peer, NULL) ;
+ }
+
+ mqb_free(mqb) ;
+}
+
+/*==============================================================================
+ * Routing Engine: send set ttl message to BGP Engine, if session is active.
+ */
+void
+bgp_session_set_ttl(bgp_session session, int ttl, bool gtsm)
+{
+ mqueue_block mqb ;
+ struct bgp_session_ttl_args *args;
+
+ if (bgp_session_is_active(session))
+ {
+ mqb = mqb_init_new(NULL, bgp_session_do_set_ttl, session) ;
+
+ args = mqb_get_args(mqb) ;
+ args->ttl = ttl ;
+ args->gtsm = gtsm ;
+
+ ++bgp_engine_queue_stats.event ;
+
+ bgp_to_bgp_engine(mqb, mqb_ordinary) ;
+ } ;
+}
+
+/*------------------------------------------------------------------------------
+ * BGP Engine: process set ttl message -- mqb action function.
+ */
+static void
+bgp_session_do_set_ttl(mqueue_block mqb, mqb_flag_t flag)
+{
+
+ if (flag == mqb_action)
+ {
+ bgp_session session = mqb_get_arg0(mqb) ;
+ struct bgp_session_ttl_args *args = mqb_get_args(mqb) ;
+
+ BGP_SESSION_LOCK(session) ; /*<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
+
+ session->ttl = args->ttl ;
+ session->gtsm = args->gtsm ;
+
+ bgp_set_new_ttl(session->connections[bgp_connection_primary],
+ session->ttl, session->gtsm) ;
+ bgp_set_new_ttl(session->connections[bgp_connection_secondary],
+ session->ttl, session->gtsm) ;
+
+ BGP_SESSION_UNLOCK(session) ; /*>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
+ }
+
+ mqb_free(mqb) ;
+}
+
+/*==============================================================================
+ * Session data access functions.
+ *
+ *
+ */
+
+/*------------------------------------------------------------------------------
+ * Routing Engine: see if session exists and is active.
+ *
+ * If exists then performs a few checks, just to make sure things are straight.
+ *
+ * NB: accessing Routing Engine "private" variable -- no lock required.
+ *
+ * checks session->active, only when not active -- no lock required.
+ */
+extern bool
+bgp_session_is_active(bgp_session session)
+{
+ bool active ;
+
+ if (session == NULL)
+ active = false ;
+ else
+ {
+ 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 ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Get a copy of the session statistics, copied all at once so
+ * forms a consistent snapshot
+ */
+void
+bgp_session_get_stats(bgp_session session, struct bgp_session_stats *stats)
+{
+ if (session == NULL)
+ {
+ memset(stats, 0, sizeof(struct bgp_session_stats)) ;
+ return;
+ }
+
+ BGP_SESSION_LOCK(session) ; /*<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
+
+ *stats = session->stats;
+
+ BGP_SESSION_UNLOCK(session) ; /*>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
+}