summaryrefslogtreecommitdiffstats
path: root/bgpd/bgp_peer.c
diff options
context:
space:
mode:
Diffstat (limited to 'bgpd/bgp_peer.c')
-rw-r--r--bgpd/bgp_peer.c384
1 files changed, 356 insertions, 28 deletions
diff --git a/bgpd/bgp_peer.c b/bgpd/bgp_peer.c
index 03439928..3d686171 100644
--- a/bgpd/bgp_peer.c
+++ b/bgpd/bgp_peer.c
@@ -123,7 +123,7 @@ inline static void BGP_PEER_INDEX_UNLOCK(void)
*
* This must be done before any peers are configured !
*/
-extern void
+void
bgp_peer_index_init(void* parent)
{
symbol_table_init_new(
@@ -133,18 +133,18 @@ bgp_peer_index_init(void* parent)
200, /* allow to be quite dense */
sockunion_symbol_hash, /* "name" is an IP Address */
NULL) ; /* no value change call-back */
-} ;
+}
/*------------------------------------------------------------------------------
* Initialise the bgp_peer_index_mutex.
*
* This must be done before the BGP Engine is started.
*/
-extern void
+void
bgp_peer_index_mutex_init(void* parent)
{
qpt_mutex_init(&bgp_peer_index_mutex, qpt_mutex_recursive) ;
-} ;
+}
/*------------------------------------------------------------------------------
* Lookup a peer -- do nothing if does not exist
@@ -153,7 +153,7 @@ bgp_peer_index_mutex_init(void* parent)
*
* Returns the bgp_peer -- NULL if not found.
*/
-extern bgp_peer
+bgp_peer
bgp_peer_index_seek(union sockunion* su)
{
bgp_peer peer ;
@@ -165,7 +165,7 @@ bgp_peer_index_seek(union sockunion* su)
BGP_PEER_INDEX_UNLOCK() ; /*>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
return peer ;
-} ;
+}
/*------------------------------------------------------------------------------
* Register a peer in the peer index.
@@ -175,7 +175,7 @@ bgp_peer_index_seek(union sockunion* su)
* NB: it is a FATAL error to register a peer for an address which is already
* registered.
*/
-extern void
+void
bgp_peer_index_register(bgp_peer peer, union sockunion* su)
{
BGP_PEER_INDEX_LOCK() ; /*<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
@@ -184,8 +184,39 @@ bgp_peer_index_register(bgp_peer peer, union sockunion* su)
BGP_PEER_INDEX_UNLOCK() ; /*>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
- passert(peer != NULL) ;
-} ;
+ passert(peer == NULL) ;
+}
+
+/*------------------------------------------------------------------------------
+ * Unregister a peer in the peer index.
+ *
+ * returns 1 if successfully unregistered
+ * returns 0 if wasn't registered
+ * FATAL error if a different peer is registered.
+ */
+int
+bgp_peer_index_unregister(bgp_peer peer, union sockunion* su)
+{
+ int found = 0;
+ int right_peer = 1;
+ symbol s = NULL;
+
+ BGP_PEER_INDEX_LOCK() ; /*<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
+
+ s = symbol_seek(&bgp_peer_index, su);
+ found = (s != NULL);
+ if (found)
+ {
+ right_peer = (peer == symbol_get_value(s));
+ if (right_peer)
+ symbol_delete(s);
+ }
+
+ BGP_PEER_INDEX_UNLOCK() ; /*>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
+
+ passert(right_peer) ;
+ return found;
+}
/*------------------------------------------------------------------------------
* Lookup a session -- do nothing if does not exist
@@ -222,7 +253,7 @@ bgp_session_index_seek(union sockunion* su, int* p_found)
BGP_PEER_INDEX_UNLOCK() ; /*>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
return session ;
-} ;
+}
/*------------------------------------------------------------------------------
* Set peer's session pointer.
@@ -237,8 +268,8 @@ bgp_session_index_seek(union sockunion* su, int* p_found)
*
*/
-extern bgp_session
-bgp_session_index_seek(bgp_peer peer, bgp_session new_session)
+bgp_session
+bgp_peer_new_session(bgp_peer peer, bgp_session new_session)
{
bgp_session old_session ;
@@ -252,7 +283,7 @@ bgp_session_index_seek(bgp_peer peer, bgp_session new_session)
BGP_PEER_INDEX_UNLOCK() ; /*>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
return old_session ;
-} ;
+}
@@ -281,33 +312,34 @@ bgp_peer_new_session_state(mqueue_block mqb, mqb_flag_t flag)
{
bgp_session_lock(session) ;
-
-
switch(new_state)
{
/* If now Enabled, then the BGP Engine is attempting to connect */
/* (may be waiting for the Idle Hold Time to expire. */
case bgp_session_Enabled:
+ bgp_session_enable(session, peer);
break ;
/* If now Established, then the BGP Engine has exchanged BGP Open */
/* messages, and received the KeepAlive that acknowledges our Open. */
case bgp_session_Established:
+ bgp_session_has_established(peer);
break ;
/* If now Stopped, then for some reason the BGP Engine has either */
/* stopped trying to connect, or the session has been stopped. */
case bgp_session_Stopped:
+ bgp_session_has_stopped(peer);
break ;
default:
- } ;
+ }
bgp_session_unlock(session) ;
- } ;
+ }
mqb_free(mqb) ;
-} ;
+}
/* BGP Session has been Established.
*
@@ -323,6 +355,9 @@ bgp_session_has_established(bgp_peer peer)
safi_t safi;
int nsf_af_count = 0;
+ /* update peer state from received open */
+ bgp_peer_open_state_receive(peer);
+
/* Reset capability open status flag. */
if (! CHECK_FLAG (peer->sflags, PEER_STATUS_CAPABILITY_OPEN))
SET_FLAG (peer->sflags, PEER_STATUS_CAPABILITY_OPEN);
@@ -428,7 +463,7 @@ bgp_session_has_established(bgp_peer peer)
/* Administrative BGP peer stop event. */
/* May be called multiple times for the same peer */
static int
-bgp_session_has_stopped(bgp_peer *peer)
+bgp_session_has_stopped(bgp_peer peer)
{
afi_t afi;
safi_t safi;
@@ -555,8 +590,6 @@ bgp_session_has_stopped(bgp_peer *peer)
return 0;
}
-
-
/* Stop all timers for the given peer
*/
static void
@@ -570,11 +603,6 @@ bgp_peer_timers_stop(bgp_peer peer)
BGP_TIMER_OFF (peer->t_pmax_restart);
} ;
-
-
-
-
-
void
bgp_timer_set (struct peer *peer)
{
@@ -632,7 +660,6 @@ bgp_timer_set (struct peer *peer)
}
}
-
static int
bgp_routeadv_timer (struct thread *thread)
{
@@ -744,7 +771,7 @@ bgp_graceful_stale_timer_expire (struct thread *thread)
}
-/* BGP peer is stoped by the error. */
+/* BGP peer is stopped by the error. */
static int
bgp_stop_with_error (struct peer *peer)
{
@@ -760,3 +787,304 @@ bgp_stop_with_error (struct peer *peer)
return 0;
}
+/* Allocate new peer object, implicitly locked. */
+struct peer *
+peer_new (struct bgp *bgp)
+{
+ afi_t afi;
+ safi_t safi;
+ struct peer *peer;
+ struct servent *sp;
+
+ /* bgp argument is absolutely required */
+ assert (bgp);
+ if (!bgp)
+ return NULL;
+
+ /* Allocate new peer. */
+ peer = XCALLOC (MTYPE_BGP_PEER, sizeof (struct peer));
+
+ /* Set default value. */
+ peer->fd = -1;
+ peer->v_start = BGP_INIT_START_TIMER;
+ peer->v_connect = BGP_DEFAULT_CONNECT_RETRY;
+ peer->v_asorig = BGP_DEFAULT_ASORIGINATE;
+ peer->status = Idle;
+ peer->ostatus = Idle;
+ peer->weight = 0;
+ peer->password = NULL;
+ peer->bgp = bgp;
+ peer = peer_lock (peer); /* initial reference */
+ bgp_lock (bgp);
+
+ /* Set default flags. */
+ for (afi = AFI_IP; afi < AFI_MAX; afi++)
+ for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++)
+ {
+ if (! bgp_option_check (BGP_OPT_CONFIG_CISCO))
+ {
+ SET_FLAG (peer->af_flags[afi][safi], PEER_FLAG_SEND_COMMUNITY);
+ SET_FLAG (peer->af_flags[afi][safi], PEER_FLAG_SEND_EXT_COMMUNITY);
+ }
+ peer->orf_plist[afi][safi] = NULL;
+ }
+ SET_FLAG (peer->sflags, PEER_STATUS_CAPABILITY_OPEN);
+
+ /* Create buffers. */
+ peer->ibuf = stream_new (BGP_MAX_PACKET_SIZE);
+ peer->obuf = stream_fifo_new ();
+ peer->work = stream_new (BGP_MAX_PACKET_SIZE);
+
+ bgp_sync_init (peer);
+
+ /* Get service port number. */
+ sp = getservbyname ("bgp", "tcp");
+ peer->port = (sp == NULL) ? BGP_PORT_DEFAULT : ntohs (sp->s_port);
+
+ return peer;
+}
+
+/* Create new BGP peer. */
+struct peer *
+peer_create (union sockunion *su, struct bgp *bgp, as_t local_as,
+ as_t remote_as, afi_t afi, safi_t safi)
+{
+ int active;
+ struct peer *peer;
+ char buf[SU_ADDRSTRLEN];
+
+ peer = peer_new (bgp);
+ peer->su = *su;
+ peer->local_as = local_as;
+ peer->as = remote_as;
+ peer->local_id = bgp->router_id;
+ peer->v_holdtime = bgp->default_holdtime;
+ peer->v_keepalive = bgp->default_keepalive;
+ if (peer_sort (peer) == BGP_PEER_IBGP)
+ peer->v_routeadv = BGP_DEFAULT_IBGP_ROUTEADV;
+ else
+ peer->v_routeadv = BGP_DEFAULT_EBGP_ROUTEADV;
+
+ peer = peer_lock (peer); /* bgp peer list reference */
+ listnode_add_sort (bgp->peer, peer);
+
+ active = peer_active (peer);
+
+ if (afi && safi)
+ peer->afc[afi][safi] = 1;
+
+ /* Last read time set */
+ peer->readtime = time (NULL);
+
+ /* Last reset time set */
+ peer->resettime = time (NULL);
+
+ /* Default TTL set. */
+ peer->ttl = (peer_sort (peer) == BGP_PEER_IBGP ? 255 : 1);
+
+ /* Make peer's address string. */
+ sockunion2str (su, buf, SU_ADDRSTRLEN);
+ peer->host = XSTRDUP (MTYPE_BGP_PEER_HOST, buf);
+
+ /* Set up peer's events and timers. */
+ if (! active && peer_active (peer))
+ bgp_timer_set (peer);
+
+ /* session */
+ peer->session = bgp_session_init_new(NULL);
+
+ /* register */
+ bgp_peer_index_register(peer, &peer->su);
+
+ return peer;
+}
+
+/* Delete peer from configuration.
+ *
+ * The peer is moved to a dead-end "Deleted" neighbour-state, to allow
+ * it to "cool off" and refcounts to hit 0, at which state it is freed.
+ *
+ * This function /should/ take care to be idempotent, to guard against
+ * it being called multiple times through stray events that come in
+ * that happen to result in this function being called again. That
+ * said, getting here for a "Deleted" peer is a bug in the neighbour
+ * FSM.
+ */
+int
+peer_delete (struct peer *peer)
+{
+ int i;
+ afi_t afi;
+ safi_t safi;
+ struct bgp *bgp;
+ struct bgp_filter *filter;
+ struct listnode *pn;
+
+ assert (peer->status != Deleted);
+
+ bgp = peer->bgp;
+
+ if (CHECK_FLAG (peer->sflags, PEER_STATUS_NSF_WAIT))
+ peer_nsf_stop (peer);
+
+ /* If this peer belongs to peer group, clear up the
+ relationship. */
+ if (peer->group)
+ {
+ if ((pn = listnode_lookup (peer->group->peer, peer)))
+ {
+ peer = peer_unlock (peer); /* group->peer list reference */
+ list_delete_node (peer->group->peer, pn);
+ }
+ peer->group = NULL;
+ }
+
+ /* Withdraw all information from routing table. We can not use
+ * BGP_EVENT_ADD (peer, BGP_Stop) at here. Because the event is
+ * executed after peer structure is deleted.
+ */
+ peer->last_reset = PEER_DOWN_NEIGHBOR_DELETE;
+ bgp_stop (peer);
+ bgp_fsm_change_status (peer, Deleted);
+
+ /* Password configuration */
+ if (peer->password)
+ {
+ XFREE (MTYPE_PEER_PASSWORD, peer->password);
+ peer->password = NULL;
+
+ if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
+ bgp_md5_set (peer);
+ }
+
+ bgp_timer_set (peer); /* stops all timers for Deleted */
+
+ /* Delete from all peer list. */
+ if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)
+ && (pn = listnode_lookup (bgp->peer, peer)))
+ {
+ peer_unlock (peer); /* bgp peer list reference */
+ list_delete_node (bgp->peer, pn);
+ }
+
+ if (peer_rsclient_active (peer)
+ && (pn = listnode_lookup (bgp->rsclient, peer)))
+ {
+ peer_unlock (peer); /* rsclient list reference */
+ list_delete_node (bgp->rsclient, pn);
+
+ /* Clear our own rsclient ribs. */
+ for (afi = AFI_IP; afi < AFI_MAX; afi++)
+ for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++)
+ if (CHECK_FLAG(peer->af_flags[afi][safi],
+ PEER_FLAG_RSERVER_CLIENT))
+ bgp_clear_route (peer, afi, safi, BGP_CLEAR_ROUTE_MY_RSCLIENT);
+ }
+
+ /* Free RIB for any family in which peer is RSERVER_CLIENT, and is not
+ member of a peer_group. */
+ for (afi = AFI_IP; afi < AFI_MAX; afi++)
+ for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++)
+ if (peer->rib[afi][safi] && ! peer->af_group[afi][safi])
+ bgp_table_finish (&peer->rib[afi][safi]);
+
+ /* Buffers. */
+ if (peer->ibuf)
+ stream_free (peer->ibuf);
+ if (peer->obuf)
+ stream_fifo_free (peer->obuf);
+ if (peer->work)
+ stream_free (peer->work);
+ peer->obuf = NULL;
+ peer->work = peer->ibuf = NULL;
+
+ /* Local and remote addresses. */
+ if (peer->su_local)
+ sockunion_free (peer->su_local);
+ if (peer->su_remote)
+ sockunion_free (peer->su_remote);
+ peer->su_local = peer->su_remote = NULL;
+
+ /* Free filter related memory. */
+ for (afi = AFI_IP; afi < AFI_MAX; afi++)
+ for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++)
+ {
+ filter = &peer->filter[afi][safi];
+
+ for (i = FILTER_IN; i < FILTER_MAX; i++)
+ {
+ if (filter->dlist[i].name)
+ free (filter->dlist[i].name);
+ if (filter->aslist[i].name)
+ free (filter->aslist[i].name);
+ filter->dlist[i].name = NULL;
+ prefix_list_unset_ref(&filter->plist[i].ref) ;
+ filter->aslist[i].name = NULL;
+ }
+ for (i = RMAP_IN; i < RMAP_MAX; i++)
+ {
+ if (filter->map[i].name)
+ free (filter->map[i].name);
+ filter->map[i].name = NULL;
+ }
+
+ if (filter->usmap.name)
+ free (filter->usmap.name);
+
+ if (peer->default_rmap[afi][safi].name)
+ free (peer->default_rmap[afi][safi].name);
+
+ filter->usmap.name = NULL;
+ peer->default_rmap[afi][safi].name = NULL;
+ }
+
+ /* unregister */
+ if (peer->su)
+ bgp_peer_index_unregister(peer, &peer->su);
+
+ peer_unlock (peer); /* initial reference */
+
+ return 0;
+}
+
+void
+peer_free (struct peer *peer)
+{
+ assert (peer->status == Deleted);
+
+ bgp_unlock(peer->bgp);
+
+ /* this /ought/ to have been done already through bgp_stop earlier,
+ * but just to be sure..
+ */
+ bgp_timer_set (peer);
+ BGP_READ_OFF (peer->t_read);
+ BGP_WRITE_OFF (peer->t_write);
+ BGP_EVENT_FLUSH (peer);
+
+ if (peer->desc)
+ XFREE (MTYPE_PEER_DESC, peer->desc);
+
+ /* Free allocated host character. */
+ if (peer->host)
+ XFREE (MTYPE_BGP_PEER_HOST, peer->host);
+
+ /* Update source configuration. */
+ if (peer->update_source)
+ sockunion_free (peer->update_source);
+
+ if (peer->update_if)
+ XFREE (MTYPE_PEER_UPDATE_SOURCE, peer->update_if);
+
+ if (peer->clear_node_queue)
+ work_queue_free (peer->clear_node_queue);
+
+ /* session */
+ if (peer->session)
+ bgp_session_free(peer->session);
+
+ bgp_sync_delete (peer);
+ memset (peer, 0, sizeof (struct peer));
+
+ XFREE (MTYPE_BGP_PEER, peer);
+}