summaryrefslogtreecommitdiffstats
path: root/bgpd/bgp_fsm.c
diff options
context:
space:
mode:
Diffstat (limited to 'bgpd/bgp_fsm.c')
-rw-r--r--bgpd/bgp_fsm.c828
1 files changed, 734 insertions, 94 deletions
diff --git a/bgpd/bgp_fsm.c b/bgpd/bgp_fsm.c
index c4cfd58e..7dd0b8e2 100644
--- a/bgpd/bgp_fsm.c
+++ b/bgpd/bgp_fsm.c
@@ -42,6 +42,8 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
#include "bgpd/bgp_route.h"
#include "bgpd/bgp_dump.h"
#include "bgpd/bgp_open.h"
+#include "bgpd/bgp_advertise.h"
+#include "bgpd/bgp_nht.h"
#ifdef HAVE_SNMP
#include "bgpd/bgp_snmp.h"
#endif /* HAVE_SNMP */
@@ -63,11 +65,101 @@ static int bgp_keepalive_timer (struct thread *);
/* BGP FSM functions. */
static int bgp_start (struct peer *);
-/* BGP start timer jitter. */
-static int
-bgp_start_jitter (int time)
+static void
+peer_xfer_stats (struct peer *peer_dst, struct peer *peer_src)
{
- return ((random () % (time + 1)) - (time / 2));
+ /* Copy stats over. These are only the pre-established state stats */
+ peer_dst->open_in += peer_src->open_in;
+ peer_dst->open_out += peer_src->open_out;
+ peer_dst->keepalive_in += peer_src->keepalive_in;
+ peer_dst->keepalive_out += peer_src->keepalive_out;
+ peer_dst->notify_in += peer_src->notify_in;
+ peer_dst->notify_out += peer_src->notify_out;
+ peer_dst->dynamic_cap_in += peer_src->dynamic_cap_in;
+ peer_dst->dynamic_cap_out += peer_src->dynamic_cap_out;
+}
+
+static struct peer *
+peer_xfer_conn(struct peer *from_peer)
+{
+ struct peer *peer;
+ afi_t afi;
+ safi_t safi;
+ int fd;
+ int status, pstatus;
+
+ assert(from_peer != NULL);
+
+ peer = from_peer->doppelganger;
+
+ if (!peer || !CHECK_FLAG(peer->flags, PEER_FLAG_CONFIG_NODE))
+ return from_peer;
+
+ BGP_WRITE_OFF(peer->t_write);
+ BGP_READ_OFF(peer->t_read);
+ BGP_WRITE_OFF(from_peer->t_write);
+ BGP_READ_OFF(from_peer->t_read);
+
+ fd = peer->fd;
+ peer->fd = from_peer->fd;
+ from_peer->fd = fd;
+ stream_reset(peer->ibuf);
+ stream_fifo_clean(peer->obuf);
+ stream_fifo_clean(from_peer->obuf);
+
+ peer->v_holdtime = from_peer->v_holdtime;
+ peer->v_keepalive = from_peer->v_keepalive;
+ peer->routeadv = from_peer->routeadv;
+ peer->v_routeadv = from_peer->v_routeadv;
+ peer->v_gr_restart = from_peer->v_gr_restart;
+ peer->cap = from_peer->cap;
+ status = peer->status;
+ pstatus = peer->ostatus;
+ peer->status = from_peer->status;
+ peer->ostatus = from_peer->ostatus;
+ from_peer->status = status;
+ from_peer->ostatus = pstatus;
+ peer->remote_id = from_peer->remote_id;
+
+ for (afi = AFI_IP; afi < AFI_MAX; afi++)
+ for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++)
+ {
+ peer->af_flags[afi][safi] = from_peer->af_flags[afi][safi];
+ peer->af_sflags[afi][safi] = from_peer->af_sflags[afi][safi];
+ peer->af_cap[afi][safi] = from_peer->af_cap[afi][safi];
+ peer->afc_nego[afi][safi] = from_peer->afc_nego[afi][safi];
+ peer->afc_adv[afi][safi] = from_peer->afc_adv[afi][safi];
+ peer->afc_recv[afi][safi] = from_peer->afc_recv[afi][safi];
+ }
+
+ if (bgp_getsockname(peer) < 0)
+ {
+ zlog_err ("%%bgp_getsockname() failed for %s peer %s fd %d (from_peer fd %d)",
+ (CHECK_FLAG (peer->sflags, PEER_STATUS_ACCEPT_PEER) ? "accept" : ""),
+ peer->host, peer->fd, from_peer->fd);
+ bgp_stop(peer);
+ bgp_stop(from_peer);
+ return NULL;
+ }
+ if (from_peer->status > Active)
+ {
+ if (bgp_getsockname(from_peer) < 0)
+ {
+ zlog_err ("%%bgp_getsockname() failed for %s from_peer %s fd %d (peer fd %d)",
+ (CHECK_FLAG (from_peer->sflags, PEER_STATUS_ACCEPT_PEER) ? "accept" : ""),
+ from_peer->host, from_peer->fd, peer->fd);
+ bgp_stop(from_peer);
+ from_peer = NULL;
+ }
+ }
+
+ BGP_READ_ON(peer->t_read, bgp_read, peer->fd);
+ BGP_WRITE_ON(peer->t_write, bgp_write, peer->fd);
+
+ if (from_peer)
+ peer_xfer_stats(peer, from_peer);
+
+ return(peer);
}
/* Check if suppress start/restart of sessions to peer. */
@@ -81,8 +173,6 @@ bgp_start_jitter (int time)
void
bgp_timer_set (struct peer *peer)
{
- int jitter = 0;
-
switch (peer->status)
{
case Idle:
@@ -95,9 +185,8 @@ bgp_timer_set (struct peer *peer)
}
else
{
- jitter = bgp_start_jitter (peer->v_start);
BGP_TIMER_ON (peer->t_start, bgp_start_timer,
- peer->v_start + jitter);
+ peer->v_start);
}
BGP_TIMER_OFF (peer->t_connect);
BGP_TIMER_OFF (peer->t_holdtime);
@@ -106,7 +195,7 @@ bgp_timer_set (struct peer *peer)
break;
case Connect:
- /* After start timer is expired, the peer moves to Connnect
+ /* After start timer is expired, the peer moves to Connect
status. Make sure start timer is off and connect timer is
on. */
BGP_TIMER_OFF (peer->t_start);
@@ -205,6 +294,7 @@ bgp_timer_set (struct peer *peer)
BGP_TIMER_OFF (peer->t_holdtime);
BGP_TIMER_OFF (peer->t_keepalive);
BGP_TIMER_OFF (peer->t_routeadv);
+ break;
}
}
@@ -218,9 +308,8 @@ bgp_start_timer (struct thread *thread)
peer = THREAD_ARG (thread);
peer->t_start = NULL;
- if (BGP_DEBUG (fsm, FSM))
- zlog (peer->log, LOG_DEBUG,
- "%s [FSM] Timer (start timer expire).", peer->host);
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug("%s [FSM] Timer (start timer expire).", peer->host);
THREAD_VAL (thread) = BGP_Start;
bgp_event (thread); /* bgp_event unlocks peer */
@@ -233,18 +322,27 @@ static int
bgp_connect_timer (struct thread *thread)
{
struct peer *peer;
+ int ret;
peer = THREAD_ARG (thread);
peer->t_connect = NULL;
- if (BGP_DEBUG (fsm, FSM))
- zlog (peer->log, LOG_DEBUG, "%s [FSM] Timer (connect timer expire)",
- peer->host);
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug("%s [FSM] Timer (connect timer expire)", peer->host);
- THREAD_VAL (thread) = ConnectRetry_timer_expired;
- bgp_event (thread); /* bgp_event unlocks peer */
+ if (CHECK_FLAG(peer->sflags, PEER_STATUS_ACCEPT_PEER))
+ {
+ bgp_stop(peer);
+ ret = -1;
+ }
+ else
+ {
+ THREAD_VAL (thread) = ConnectRetry_timer_expired;
+ bgp_event (thread); /* bgp_event unlocks peer */
+ ret = 0;
+ }
- return 0;
+ return ret;
}
/* BGP holdtime timer. */
@@ -256,10 +354,8 @@ bgp_holdtime_timer (struct thread *thread)
peer = THREAD_ARG (thread);
peer->t_holdtime = NULL;
- if (BGP_DEBUG (fsm, FSM))
- zlog (peer->log, LOG_DEBUG,
- "%s [FSM] Timer (holdtime timer expire)",
- peer->host);
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug ("%s [FSM] Timer (holdtime timer expire)", peer->host);
THREAD_VAL (thread) = Hold_Timer_expired;
bgp_event (thread); /* bgp_event unlocks peer */
@@ -276,10 +372,8 @@ bgp_keepalive_timer (struct thread *thread)
peer = THREAD_ARG (thread);
peer->t_keepalive = NULL;
- if (BGP_DEBUG (fsm, FSM))
- zlog (peer->log, LOG_DEBUG,
- "%s [FSM] Timer (keepalive timer expire)",
- peer->host);
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug ("%s [FSM] Timer (keepalive timer expire)", peer->host);
THREAD_VAL (thread) = KeepAlive_timer_expired;
bgp_event (thread); /* bgp_event unlocks peer */
@@ -288,24 +382,40 @@ bgp_keepalive_timer (struct thread *thread)
}
static int
+bgp_routeq_empty (struct peer *peer)
+{
+ afi_t afi;
+ safi_t safi;
+
+ for (afi = AFI_IP; afi < AFI_MAX; afi++)
+ for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++)
+ {
+ if (!FIFO_EMPTY(&peer->sync[afi][safi]->withdraw) ||
+ !FIFO_EMPTY(&peer->sync[afi][safi]->update))
+ return 0;
+ }
+ return 1;
+}
+
+static int
bgp_routeadv_timer (struct thread *thread)
{
struct peer *peer;
peer = THREAD_ARG (thread);
peer->t_routeadv = NULL;
+ peer->radv_adjusted = 0;
- if (BGP_DEBUG (fsm, FSM))
- zlog (peer->log, LOG_DEBUG,
- "%s [FSM] Timer (routeadv timer expire)",
- peer->host);
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug ("%s [FSM] Timer (routeadv timer expire)", peer->host);
peer->synctime = bgp_clock ();
BGP_WRITE_ON (peer->t_write, bgp_write, peer->fd);
- BGP_TIMER_ON (peer->t_routeadv, bgp_routeadv_timer,
- peer->v_routeadv);
+ /* MRAI timer is no longer restarted here, it would be done
+ * when the FIFO is built.
+ */
return 0;
}
@@ -357,7 +467,7 @@ bgp_graceful_restart_timer_expire (struct thread *thread)
UNSET_FLAG (peer->sflags, PEER_STATUS_NSF_WAIT);
BGP_TIMER_OFF (peer->t_gr_stale);
- if (BGP_DEBUG (events, EVENTS))
+ if (bgp_debug_neighbor_events(peer))
{
zlog_debug ("%s graceful restart timer expired", peer->host);
zlog_debug ("%s graceful restart stalepath timer stopped", peer->host);
@@ -378,7 +488,7 @@ bgp_graceful_stale_timer_expire (struct thread *thread)
peer = THREAD_ARG (thread);
peer->t_gr_stale = NULL;
- if (BGP_DEBUG (events, EVENTS))
+ if (bgp_debug_neighbor_events(peer))
zlog_debug ("%s graceful restart stalepath timer expired", peer->host);
/* NSF delete stale route */
@@ -390,11 +500,396 @@ bgp_graceful_stale_timer_expire (struct thread *thread)
return 0;
}
+static int
+bgp_update_delay_applicable (struct bgp *bgp)
+{
+ /* update_delay_over flag should be reset (set to 0) for any new
+ applicability of the update-delay during BGP process lifetime.
+ And it should be set after an occurence of the update-delay is over)*/
+ if (!bgp->update_delay_over)
+ return 1;
+
+ return 0;
+}
+
+int
+bgp_update_delay_active (struct bgp *bgp)
+{
+ if (bgp->t_update_delay)
+ return 1;
+
+ return 0;
+}
+
+int
+bgp_update_delay_configured (struct bgp *bgp)
+{
+ if (bgp->v_update_delay)
+ return 1;
+
+ return 0;
+}
+
+/* Do the post-processing needed when bgp comes out of the read-only mode
+ on ending the update delay. */
+void
+bgp_update_delay_end (struct bgp *bgp)
+{
+ THREAD_TIMER_OFF (bgp->t_update_delay);
+ THREAD_TIMER_OFF (bgp->t_establish_wait);
+
+ /* Reset update-delay related state */
+ bgp->update_delay_over = 1;
+ bgp->established = 0;
+ bgp->restarted_peers = 0;
+ bgp->implicit_eors = 0;
+ bgp->explicit_eors = 0;
+
+ quagga_timestamp(3, bgp->update_delay_end_time,
+ sizeof(bgp->update_delay_end_time));
+
+ /*
+ * Add an end-of-initial-update marker to the main process queues so that
+ * the route advertisement timer for the peers can be started. Also set
+ * the zebra and peer update hold flags. These flags are used to achieve
+ * three stages in the update-delay post processing:
+ * 1. Finish best-path selection for all the prefixes held on the queues.
+ * (routes in BGP are updated, and peers sync queues are populated too)
+ * 2. As the eoiu mark is reached in the bgp process routine, ship all the
+ * routes to zebra. With that zebra should see updates from BGP close
+ * to each other.
+ * 3. Unblock the peer update writes. With that peer update packing with
+ * the prefixes should be at its maximum.
+ */
+ bgp_add_eoiu_mark(bgp, BGP_TABLE_MAIN);
+ bgp_add_eoiu_mark(bgp, BGP_TABLE_RSCLIENT);
+ bgp->main_zebra_update_hold = 1;
+ bgp->main_peers_update_hold = 1;
+ bgp->rsclient_peers_update_hold = 1;
+
+ /* Resume the queue processing. This should trigger the event that would take
+ care of processing any work that was queued during the read-only mode. */
+ work_queue_unplug(bm->process_main_queue);
+ work_queue_unplug(bm->process_rsclient_queue);
+}
+
+/**
+ * see bgp_fsm.h
+ */
+void
+bgp_start_routeadv (struct bgp *bgp)
+{
+ struct listnode *node, *nnode;
+ struct peer *peer;
+
+ zlog_info("bgp_start_routeadv(), update hold status - main: %d, rsclient: %d",
+ bgp->main_peers_update_hold, bgp->rsclient_peers_update_hold);
+
+ if (bgp->main_peers_update_hold || bgp->rsclient_peers_update_hold)
+ return;
+
+ quagga_timestamp(3, bgp->update_delay_peers_resume_time,
+ sizeof(bgp->update_delay_peers_resume_time));
+
+ for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer))
+ {
+ if (peer->status != Established)
+ continue;
+ BGP_TIMER_OFF(peer->t_routeadv);
+ BGP_TIMER_ON(peer->t_routeadv, bgp_routeadv_timer, 0);
+ }
+}
+
+/**
+ * see bgp_fsm.h
+ */
+void
+bgp_adjust_routeadv (struct peer *peer)
+{
+ time_t nowtime = bgp_clock();
+ double diff;
+ unsigned long remain;
+
+ /* Bypass checks for special case of MRAI being 0 */
+ if (peer->v_routeadv == 0)
+ {
+ /* Stop existing timer, just in case it is running for a different
+ * duration and schedule write thread immediately.
+ */
+ if (peer->t_routeadv)
+ BGP_TIMER_OFF(peer->t_routeadv);
+
+ peer->synctime = bgp_clock ();
+ BGP_WRITE_ON (peer->t_write, bgp_write, peer->fd);
+ return;
+ }
+
+ /* Mark that we've adjusted the timer */
+ peer->radv_adjusted = 1;
+
+
+ /*
+ * CASE I:
+ * If the last update was written more than MRAI back, expire the timer
+ * instantly so that we can send the update out sooner.
+ *
+ * <------- MRAI --------->
+ * |-----------------|-----------------------|
+ * <------------- m ------------>
+ * ^ ^ ^
+ * | | |
+ * | | current time
+ * | timer start
+ * last write
+ *
+ * m > MRAI
+ */
+ diff = difftime(nowtime, peer->last_write);
+ if (diff > (double) peer->v_routeadv)
+ {
+ BGP_TIMER_OFF(peer->t_routeadv);
+ BGP_TIMER_ON(peer->t_routeadv, bgp_routeadv_timer, 0);
+ if (bgp_debug_update(peer, NULL, 0))
+ zlog_debug ("%s: MRAI timer to expire instantly", peer->host);
+ return;
+ }
+
+ /*
+ * CASE II:
+ * - Find when to expire the MRAI timer.
+ * If MRAI timer is not active, assume we can start it now.
+ *
+ * <------- MRAI --------->
+ * |------------|-----------------------|
+ * <-------- m ----------><----- r ----->
+ * ^ ^ ^
+ * | | |
+ * | | current time
+ * | timer start
+ * last write
+ *
+ * (MRAI - m) < r
+ */
+ if (peer->t_routeadv)
+ remain = thread_timer_remain_second(peer->t_routeadv);
+ else
+ remain = peer->v_routeadv;
+ diff = peer->v_routeadv - diff;
+ if (diff <= (double) remain)
+ {
+ BGP_TIMER_OFF(peer->t_routeadv);
+ BGP_TIMER_ON(peer->t_routeadv, bgp_routeadv_timer, diff);
+ if (bgp_debug_update(peer, NULL, 0))
+ zlog_debug ("%s: MRAI timer to expire in %f secs", peer->host, diff);
+ }
+}
+
+static int
+bgp_maxmed_onstartup_applicable (struct bgp *bgp)
+{
+ if (!bgp->maxmed_onstartup_over)
+ return 1;
+
+ return 0;
+}
+
+int
+bgp_maxmed_onstartup_configured (struct bgp *bgp)
+{
+ if (bgp->v_maxmed_onstartup != BGP_MAXMED_ONSTARTUP_UNCONFIGURED)
+ return 1;
+
+ return 0;
+}
+
+int
+bgp_maxmed_onstartup_active (struct bgp *bgp)
+{
+ if (bgp->t_maxmed_onstartup)
+ return 1;
+
+ return 0;
+}
+
+void
+bgp_maxmed_update (struct bgp *bgp)
+{
+ struct listnode *node, *nnode;
+ struct peer *peer;
+ u_char maxmed_active;
+ u_int32_t maxmed_value;
+
+ if (bgp->v_maxmed_admin)
+ {
+ maxmed_active = 1;
+ maxmed_value = bgp->maxmed_admin_value;
+ }
+ else if (bgp->t_maxmed_onstartup)
+ {
+ maxmed_active = 1;
+ maxmed_value = bgp->maxmed_onstartup_value;
+ }
+ else
+ {
+ maxmed_active = 0;
+ maxmed_value = BGP_MAXMED_VALUE_DEFAULT;
+ }
+
+ if (bgp->maxmed_active != maxmed_active ||
+ bgp->maxmed_value != maxmed_value)
+ {
+ bgp->maxmed_active = maxmed_active;
+ bgp->maxmed_value = maxmed_value;
+
+ for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer))
+ bgp_announce_route_all (peer);
+ }
+}
+
+/* The maxmed onstartup timer expiry callback. */
+static int
+bgp_maxmed_onstartup_timer (struct thread *thread)
+{
+ struct bgp *bgp;
+
+ zlog_info ("Max med on startup ended - timer expired.");
+
+ bgp = THREAD_ARG (thread);
+ THREAD_TIMER_OFF (bgp->t_maxmed_onstartup);
+ bgp->maxmed_onstartup_over = 1;
+
+ bgp_maxmed_update(bgp);
+
+ return 0;
+}
+
+static void
+bgp_maxmed_onstartup_begin (struct bgp *bgp)
+{
+ /* Applicable only once in the process lifetime on the startup */
+ if (bgp->maxmed_onstartup_over)
+ return;
+
+ zlog_info ("Begin maxmed onstartup mode - timer %d seconds",
+ bgp->v_maxmed_onstartup);
+
+ THREAD_TIMER_ON (bm->master, bgp->t_maxmed_onstartup,
+ bgp_maxmed_onstartup_timer,
+ bgp, bgp->v_maxmed_onstartup);
+
+ if (!bgp->v_maxmed_admin)
+ {
+ bgp->maxmed_active = 1;
+ bgp->maxmed_value = bgp->maxmed_onstartup_value;
+ }
+
+ /* Route announce to all peers should happen after this in bgp_establish() */
+}
+
+static void
+bgp_maxmed_onstartup_process_status_change(struct peer *peer)
+{
+ if (peer->status == Established && !peer->bgp->established)
+ {
+ bgp_maxmed_onstartup_begin(peer->bgp);
+ }
+}
+
+/* The update delay timer expiry callback. */
+static int
+bgp_update_delay_timer (struct thread *thread)
+{
+ struct bgp *bgp;
+
+ zlog_info ("Update delay ended - timer expired.");
+
+ bgp = THREAD_ARG (thread);
+ THREAD_TIMER_OFF (bgp->t_update_delay);
+ bgp_update_delay_end(bgp);
+
+ return 0;
+}
+
+/* The establish wait timer expiry callback. */
+static int
+bgp_establish_wait_timer (struct thread *thread)
+{
+ struct bgp *bgp;
+
+ zlog_info ("Establish wait - timer expired.");
+
+ bgp = THREAD_ARG (thread);
+ THREAD_TIMER_OFF (bgp->t_establish_wait);
+ bgp_check_update_delay(bgp);
+
+ return 0;
+}
+
+/* Steps to begin the update delay:
+ - initialize queues if needed
+ - stop the queue processing
+ - start the timer */
+static void
+bgp_update_delay_begin (struct bgp *bgp)
+{
+ struct listnode *node, *nnode;
+ struct peer *peer;
+
+ if ((bm->process_main_queue == NULL) ||
+ (bm->process_rsclient_queue == NULL))
+ bgp_process_queue_init();
+
+ /* Stop the processing of queued work. Enqueue shall continue */
+ work_queue_plug(bm->process_main_queue);
+ work_queue_plug(bm->process_rsclient_queue);
+
+ for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer))
+ peer->update_delay_over = 0;
+
+ /* Start the update-delay timer */
+ THREAD_TIMER_ON (bm->master, bgp->t_update_delay, bgp_update_delay_timer,
+ bgp, bgp->v_update_delay);
+
+ if (bgp->v_establish_wait != bgp->v_update_delay)
+ THREAD_TIMER_ON (bm->master, bgp->t_establish_wait, bgp_establish_wait_timer,
+ bgp, bgp->v_establish_wait);
+
+ quagga_timestamp(3, bgp->update_delay_begin_time,
+ sizeof(bgp->update_delay_begin_time));
+}
+
+static void
+bgp_update_delay_process_status_change(struct peer *peer)
+{
+ if (peer->status == Established)
+ {
+ if (!peer->bgp->established++)
+ {
+ bgp_update_delay_begin(peer->bgp);
+ zlog_info ("Begin read-only mode - update-delay timer %d seconds",
+ peer->bgp->v_update_delay);
+ }
+ if (CHECK_FLAG (peer->cap, PEER_CAP_RESTART_BIT_RCV))
+ bgp_update_restarted_peers(peer);
+ }
+ if (peer->ostatus == Established && bgp_update_delay_active(peer->bgp))
+ {
+ /* Adjust the update-delay state to account for this flap.
+ NOTE: Intentionally skipping adjusting implicit_eors or explicit_eors
+ counters. Extra sanity check in bgp_check_update_delay() should
+ be enough to take care of any additive discrepancy in bgp eor
+ counters */
+ peer->bgp->established--;
+ peer->update_delay_over = 0;
+ }
+}
+
/* Called after event occured, this function change status and reset
read/write and timer thread. */
void
bgp_fsm_change_status (struct peer *peer, int status)
{
+
bgp_dump_state (peer, peer->status, status);
/* Transition into Clearing or Deleted must /always/ clear all routes..
@@ -422,8 +917,26 @@ bgp_fsm_change_status (struct peer *peer, int status)
/* Preserve old status and change into new status. */
peer->ostatus = peer->status;
peer->status = status;
-
- if (BGP_DEBUG (normal, NORMAL))
+
+ if (status == Established)
+ UNSET_FLAG(peer->sflags, PEER_STATUS_ACCEPT_PEER);
+
+ /* If max-med processing is applicable, do the necessary. */
+ if (status == Established)
+ {
+ if (bgp_maxmed_onstartup_configured(peer->bgp) &&
+ bgp_maxmed_onstartup_applicable(peer->bgp))
+ bgp_maxmed_onstartup_process_status_change(peer);
+ else
+ peer->bgp->maxmed_onstartup_over = 1;
+ }
+
+ /* If update-delay processing is applicable, do the necessary. */
+ if (bgp_update_delay_configured(peer->bgp) &&
+ bgp_update_delay_applicable(peer->bgp))
+ bgp_update_delay_process_status_change(peer);
+
+ if (bgp_debug_neighbor_events(peer))
zlog_debug ("%s went from %s to %s",
peer->host,
LOOKUP (bgp_status_msg, peer->ostatus),
@@ -435,7 +948,9 @@ static int
bgp_clearing_completed (struct peer *peer)
{
int rc = bgp_stop(peer);
- BGP_EVENT_FLUSH (peer);
+
+ if (rc >= 0)
+ BGP_EVENT_FLUSH (peer);
return rc;
}
@@ -448,6 +963,7 @@ bgp_stop (struct peer *peer)
afi_t afi;
safi_t safi;
char orf_name[BUFSIZ];
+ int ret = 0;
/* Can't do this in Clearing; events are used for state transitions */
if (peer->status != Clearing)
@@ -470,12 +986,12 @@ bgp_stop (struct peer *peer)
if (peer->t_gr_stale)
{
BGP_TIMER_OFF (peer->t_gr_stale);
- if (BGP_DEBUG (events, EVENTS))
+ if (bgp_debug_neighbor_events(peer))
zlog_debug ("%s graceful restart stalepath timer stopped", peer->host);
}
if (CHECK_FLAG (peer->sflags, PEER_STATUS_NSF_WAIT))
{
- if (BGP_DEBUG (events, EVENTS))
+ if (bgp_debug_neighbor_events(peer))
{
zlog_debug ("%s graceful restart timer started for %d sec",
peer->host, peer->v_gr_restart);
@@ -553,9 +1069,11 @@ bgp_stop (struct peer *peer)
/* Received ORF prefix-filter */
peer->orf_plist[afi][safi] = NULL;
- /* ORF received prefix-filter pnt */
- sprintf (orf_name, "%s.%d.%d", peer->host, afi, safi);
- prefix_bgp_orf_remove_all (afi, orf_name);
+ if ((peer->status == OpenConfirm) || (peer->status == Established)) {
+ /* ORF received prefix-filter pnt */
+ sprintf (orf_name, "%s.%d.%d", peer->host, afi, safi);
+ prefix_bgp_orf_remove_all (afi, orf_name);
+ }
}
/* Reset keepalive and holdtime */
@@ -583,7 +1101,18 @@ bgp_stop (struct peer *peer)
peer->pcount[AFI_IP6][SAFI_MULTICAST] = 0;
#endif /* 0 */
- return 0;
+ if (!CHECK_FLAG(peer->flags, PEER_FLAG_CONFIG_NODE) &&
+ !(CHECK_FLAG(peer->flags, PEER_FLAG_DELETE)))
+ {
+ peer_delete(peer);
+ ret = -1;
+ }
+ else
+ {
+ bgp_peer_conf_if_to_su_update(peer);
+ }
+
+ return ret;
}
/* BGP peer is stoped by the error. */
@@ -597,9 +1126,7 @@ bgp_stop_with_error (struct peer *peer)
if (peer->v_start >= (60 * 2))
peer->v_start = (60 * 2);
- bgp_stop (peer);
-
- return 0;
+ return(bgp_stop (peer));
}
@@ -610,21 +1137,10 @@ bgp_stop_with_notify (struct peer *peer, u_char code, u_char sub_code)
/* Send notify to remote peer */
bgp_notify_send (peer, code, sub_code);
- /* Sweep if it is temporary peer. */
- if (CHECK_FLAG (peer->sflags, PEER_STATUS_ACCEPT_PEER))
- {
- zlog_info ("%s [Event] Accepting BGP peer is deleted", peer->host);
- peer_delete (peer);
- return -1;
- }
-
/* Clear start timer value to default. */
peer->v_start = BGP_INIT_START_TIMER;
- /* bgp_stop needs to be invoked while in Established state */
- bgp_stop(peer);
-
- return 0;
+ return(bgp_stop(peer));
}
@@ -637,14 +1153,21 @@ bgp_connect_success (struct peer *peer)
{
zlog_err ("bgp_connect_success peer's fd is negative value %d",
peer->fd);
+ bgp_stop(peer);
return -1;
}
- BGP_READ_ON (peer->t_read, bgp_read, peer->fd);
- if (! CHECK_FLAG (peer->sflags, PEER_STATUS_ACCEPT_PEER))
- bgp_getsockname (peer);
+ if (bgp_getsockname (peer) < 0)
+ {
+ zlog_err ("%s: bgp_getsockname(): failed for peer %s", __FUNCTION__,
+ peer->host);
+ bgp_notify_send(peer, BGP_NOTIFY_FSM_ERR, 0); /* internal error */
+ return -1;
+ }
- if (BGP_DEBUG (normal, NORMAL))
+ BGP_READ_ON (peer->t_read, bgp_read, peer->fd);
+
+ if (bgp_debug_neighbor_events(peer))
{
char buf1[SU_ADDRSTRLEN];
@@ -655,8 +1178,7 @@ bgp_connect_success (struct peer *peer)
zlog_debug ("%s passive open", peer->host);
}
- if (! CHECK_FLAG (peer->sflags, PEER_STATUS_ACCEPT_PEER))
- bgp_open_send (peer);
+ bgp_open_send (peer);
return 0;
}
@@ -665,8 +1187,7 @@ bgp_connect_success (struct peer *peer)
static int
bgp_connect_fail (struct peer *peer)
{
- bgp_stop (peer);
- return 0;
+ return (bgp_stop (peer));
}
/* This function is the first starting point of all BGP connection. It
@@ -675,11 +1196,14 @@ int
bgp_start (struct peer *peer)
{
int status;
+ int connected = 0;
+
+ bgp_peer_conf_if_to_su_update(peer);
if (BGP_PEER_START_SUPPRESSED (peer))
{
- if (BGP_DEBUG (fsm, FSM))
- plog_err (peer->log, "%s [FSM] Trying to start suppressed peer"
+ if (bgp_debug_neighbor_events(peer))
+ zlog_err ("%s [FSM] Trying to start suppressed peer"
" - this is never supposed to happen!", peer->host);
return -1;
}
@@ -713,26 +1237,32 @@ bgp_start (struct peer *peer)
return 0;
}
+ /* Register to be notified on peer up */
+ if ((peer->ttl == 1) || (peer->gtsm_hops == 1))
+ connected = 1;
+
+ bgp_find_or_add_nexthop(family2afi(peer->su.sa.sa_family), NULL, peer,
+ connected);
status = bgp_connect (peer);
switch (status)
{
case connect_error:
- if (BGP_DEBUG (fsm, FSM))
- plog_debug (peer->log, "%s [FSM] Connect error", peer->host);
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug ("%s [FSM] Connect error", peer->host);
BGP_EVENT_ADD (peer, TCP_connection_open_failed);
break;
case connect_success:
- if (BGP_DEBUG (fsm, FSM))
- plog_debug (peer->log, "%s [FSM] Connect immediately success",
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug ("%s [FSM] Connect immediately success",
peer->host);
BGP_EVENT_ADD (peer, TCP_connection_open);
break;
case connect_in_progress:
/* To check nonblocking connect, we wait until socket is
readable or writable. */
- if (BGP_DEBUG (fsm, FSM))
- plog_debug (peer->log, "%s [FSM] Non blocking connect waiting result",
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug ("%s [FSM] Non blocking connect waiting result",
peer->host);
if (peer->fd < 0)
{
@@ -751,9 +1281,14 @@ bgp_start (struct peer *peer)
static int
bgp_reconnect (struct peer *peer)
{
- bgp_stop (peer);
- bgp_start (peer);
- return 0;
+ int ret = 0;
+
+ if (bgp_stop (peer) > 0)
+ bgp_start (peer);
+ else
+ ret = -1;
+
+ return ret;
}
static int
@@ -772,6 +1307,13 @@ bgp_fsm_open (struct peer *peer)
static int
bgp_fsm_keepalive_expire (struct peer *peer)
{
+ /*
+ * If there are UPDATE messages to send, no need to send keepalive. The
+ * peer will note our progress through the UPDATEs.
+ */
+ if (!bgp_routeq_empty(peer))
+ return 0;
+
bgp_keepalive_send (peer);
return 0;
}
@@ -781,7 +1323,7 @@ bgp_fsm_keepalive_expire (struct peer *peer)
static int
bgp_fsm_event_error (struct peer *peer)
{
- plog_err (peer->log, "%s [FSM] unexpected packet received in state %s",
+ zlog_err ("%s [FSM] unexpected packet received in state %s",
peer->host, LOOKUP (bgp_status_msg, peer->status));
return bgp_stop_with_notify (peer, BGP_NOTIFY_FSM_ERR, 0);
@@ -792,8 +1334,8 @@ bgp_fsm_event_error (struct peer *peer)
static int
bgp_fsm_holdtime_expire (struct peer *peer)
{
- if (BGP_DEBUG (fsm, FSM))
- plog_debug (peer->log, "%s [FSM] Hold timer expire", peer->host);
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug ("%s [FSM] Hold timer expire", peer->host);
return bgp_stop_with_notify (peer, BGP_NOTIFY_HOLD_ERR, 0);
}
@@ -807,6 +1349,19 @@ bgp_establish (struct peer *peer)
afi_t afi;
safi_t safi;
int nsf_af_count = 0;
+ int ret = 0;
+ struct peer *other;
+
+ other = peer->doppelganger;
+ peer = peer_xfer_conn(peer);
+ if (!peer)
+ {
+ zlog_err ("%%Neighbor failed in xfer_conn");
+ return -1;
+ }
+
+ if (other == peer)
+ ret = 1; /* bgp_establish specific code when xfer_conn happens. */
/* Reset capability open status flag. */
if (! CHECK_FLAG (peer->sflags, PEER_STATUS_CAPABILITY_OPEN))
@@ -861,7 +1416,7 @@ bgp_establish (struct peer *peer)
if (peer->t_gr_stale)
{
BGP_TIMER_OFF (peer->t_gr_stale);
- if (BGP_DEBUG (events, EVENTS))
+ if (bgp_debug_neighbor_events(peer))
zlog_debug ("%s graceful restart stalepath timer stopped", peer->host);
}
}
@@ -869,7 +1424,7 @@ bgp_establish (struct peer *peer)
if (peer->t_gr_restart)
{
BGP_TIMER_OFF (peer->t_gr_restart);
- if (BGP_DEBUG (events, EVENTS))
+ if (bgp_debug_neighbor_events(peer))
zlog_debug ("%s graceful restart timer stopped", peer->host);
}
@@ -893,9 +1448,6 @@ bgp_establish (struct peer *peer)
REFRESH_IMMEDIATE, 0);
}
- if (peer->v_keepalive)
- bgp_keepalive_send (peer);
-
/* First update is deferred until ORF or ROUTE-REFRESH is received */
for (afi = AFI_IP ; afi < AFI_MAX ; afi++)
for (safi = SAFI_UNICAST ; safi < SAFI_MAX ; safi++)
@@ -906,15 +1458,34 @@ bgp_establish (struct peer *peer)
bgp_announce_route_all (peer);
- BGP_TIMER_ON (peer->t_routeadv, bgp_routeadv_timer, 1);
+ /* Start the route advertisement timer to send updates to the peer - if BGP
+ * is not in read-only mode. If it is, the timer will be started at the end
+ * of read-only mode.
+ */
+ if (!bgp_update_delay_active(peer->bgp))
+ BGP_TIMER_ON (peer->t_routeadv, bgp_routeadv_timer, 0);
- return 0;
+ if (peer->doppelganger && (peer->doppelganger->status != Deleted))
+ {
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug("[Event] Deleting stub connection for peer %s", peer->host);
+
+ if (peer->doppelganger->status > Active)
+ bgp_notify_send (peer->doppelganger, BGP_NOTIFY_CEASE,
+ BGP_NOTIFY_CEASE_COLLISION_RESOLUTION);
+ else
+ peer_delete(peer->doppelganger);
+ }
+
+ return ret;
}
/* Keepalive packet is received. */
static int
bgp_fsm_keepalive (struct peer *peer)
{
+ bgp_update_implicit_eors(peer);
+
/* peer count update */
peer->keepalive_in++;
@@ -934,11 +1505,50 @@ bgp_fsm_update (struct peer *peer)
static int
bgp_ignore (struct peer *peer)
{
- if (BGP_DEBUG (fsm, FSM))
- zlog (peer->log, LOG_DEBUG, "%s [FSM] bgp_ignore called", peer->host);
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug ("%s [FSM] bgp_ignore called", peer->host);
return 0;
}
+void
+bgp_fsm_nht_update(struct peer *peer, int valid)
+{
+ int ret = 0;
+
+ if (!peer)
+ return;
+
+ switch (peer->status)
+ {
+ case Idle:
+ if (valid)
+ BGP_EVENT_ADD(peer, BGP_Start);
+ break;
+ case Connect:
+ ret = bgp_connect_check(peer, 0);
+ if (!ret && valid)
+ {
+ BGP_TIMER_OFF(peer->t_connect);
+ BGP_EVENT_ADD(peer, ConnectRetry_timer_expired);
+ }
+ break;
+ case Active:
+ if (valid)
+ {
+ BGP_TIMER_OFF(peer->t_connect);
+ BGP_EVENT_ADD(peer, ConnectRetry_timer_expired);
+ }
+ case OpenSent:
+ case OpenConfirm:
+ case Established:
+ case Clearing:
+ case Deleted:
+ default:
+ break;
+ }
+}
+
+
/* Finite State Machine structure */
static const struct {
int (*func) (struct peer *);
@@ -1108,19 +1718,34 @@ static const char *bgp_event_str[] =
int
bgp_event (struct thread *thread)
{
- int ret = 0;
int event;
- int next;
struct peer *peer;
+ int ret;
peer = THREAD_ARG (thread);
event = THREAD_VAL (thread);
+ ret = bgp_event_update(peer, event);
+
+ return (ret);
+}
+
+int
+bgp_event_update (struct peer *peer, int event)
+{
+ int next;
+ int ret = 0;
+ struct peer *other;
+ int passive_conn = 0;
+
+ other = peer->doppelganger;
+ passive_conn = (CHECK_FLAG(peer->sflags, PEER_STATUS_ACCEPT_PEER)) ? 1 : 0;
+
/* Logging this event. */
next = FSM [peer->status -1][event - 1].next_state;
- if (BGP_DEBUG (fsm, FSM) && peer->status != next)
- plog_debug (peer->log, "%s [FSM] %s (%s->%s)", peer->host,
+ if (bgp_debug_neighbor_events(peer) && peer->status != next)
+ zlog_debug ("%s [FSM] %s (%s->%s)", peer->host,
bgp_event_str[event],
LOOKUP (bgp_status_msg, peer->status),
LOOKUP (bgp_status_msg, next));
@@ -1132,13 +1757,28 @@ bgp_event (struct thread *thread)
/* When function do not want proceed next job return -1. */
if (ret >= 0)
{
+ if (ret == 1 && next == Established)
+ {
+ /* The case when doppelganger swap accurred in bgp_establish.
+ Update the peer pointer accordingly */
+ peer = other;
+ }
+
/* If status is changed. */
if (next != peer->status)
bgp_fsm_change_status (peer, next);
/* Make sure timer is set. */
bgp_timer_set (peer);
+
+ }
+ else if (!passive_conn && peer->bgp)
+ {
+ /* If we got a return value of -1, that means there was an error, restart
+ * the FSM. If the peer structure was deleted
+ */
+ bgp_fsm_change_status(peer, Idle);
+ bgp_timer_set(peer);
}
-
return ret;
}