From: Vipin Kumar <[email protected]>

COMMAND:

'update-delay <max-delay in seconds> [<establish-wait in seconds>]'

DESCRIPTION:

This feature is used to enable read-only mode on BGP process restart or when
BGP process is cleared using 'clear ip bgp *'. When applicable, read-only mode
would begin as soon as the first peer reaches Established state and a timer
for <max-delay> seconds is started.

During this mode BGP doesn't run any best-path or generate any updates to its
peers. This mode continues until:

1. All the configured peers, except the shutdown peers, have sent explicit EOR
(End-Of-RIB) or an implicit-EOR. The first keep-alive after BGP has reached
Established is considered an implicit-EOR.
   If the <establish-wait> optional value is given, then BGP will wait for
   peers to reach establish from the begining of the update-delay till the
   establish-wait period is over, i.e. the minimum set of established peers for
   which EOR is expected would be peers established during the establish-wait
   window, not necessarily all the configured neighbors.
2. max-delay period is over.

On hitting any of the above two conditions, BGP resumes the decision process
and generates updates to its peers.

Default <max-delay> is 0, i.e. the feature is off by default.

This feature can be useful in reducing CPU/network used as BGP restarts/clears.
Particularly useful in the topologies where BGP learns a prefix from many peers.
Intermediate bestpaths are possible for the same prefix as peers get established
and start receiving updates at different times. This feature should offer a
value-add if the network has a high number of such prefixes.

IMPLEMENTATION OBJECTIVES:

Given this is an optional feature, minimized the code-churn. Used existing
constructs wherever possible (existing queue-plug/unplug were used to achieve
delay and resume of best-paths/update-generation). As a result, no new
data-structure(s) had to be defined and allocated. When the feature is disabled,
the new node is not exercised for the most part.

Signed-off-by: Vipin Kumar <[email protected]>
Reviewed-by: Pradosh Mohapatra <[email protected]>
             Dinesh Dutt <[email protected]>
Signed-off-by: Donald Sharp <[email protected]>
---
 bgpd/bgp_fsm.c    | 175 ++++++++++++++++++++++++++++++++++++++++++++++++++++--
 bgpd/bgp_fsm.h    |   1 +
 bgpd/bgp_packet.c | 156 ++++++++++++++++++++++++++++++++++++++++++++++--
 bgpd/bgp_packet.h |   4 +-
 bgpd/bgp_route.c  |  21 +++++--
 bgpd/bgp_route.h  |   1 +
 bgpd/bgp_vty.c    | 138 ++++++++++++++++++++++++++++++++++++++++++
 bgpd/bgp_vty.h    |   3 +
 bgpd/bgpd.c       |   4 ++
 bgpd/bgpd.h       |  21 +++++++
 doc/bgpd.texi     |  24 ++++++++
 11 files changed, 533 insertions(+), 15 deletions(-)

diff --git a/bgpd/bgp_fsm.c b/bgpd/bgp_fsm.c
index 1823336..f1781e6 100644
--- a/bgpd/bgp_fsm.c
+++ b/bgpd/bgp_fsm.c
@@ -41,6 +41,7 @@ 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"
 #ifdef HAVE_SNMP
 #include "bgpd/bgp_snmp.h"
 #endif /* HAVE_SNMP */
@@ -389,6 +390,157 @@ 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)
+{
+  struct listnode *node, *nnode;
+  struct peer *peer;
+
+  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));
+
+  /* Route announcements were postponed for all the peers during read-only 
mode,
+     send those now. */
+  for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer))
+    bgp_announce_route_all (peer);
+
+  /* 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);
+}
+
+/* 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
@@ -421,7 +573,12 @@ 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 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 (normal, NORMAL))
     zlog_debug ("%s went from %s to %s",
                peer->host,
@@ -771,6 +928,17 @@ bgp_fsm_open (struct peer *peer)
 static int
 bgp_fsm_keepalive_expire (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;
+      }
+
   bgp_keepalive_send (peer);
   return 0;
 }
@@ -892,9 +1060,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++)
@@ -914,6 +1079,8 @@ bgp_establish (struct peer *peer)
 static int
 bgp_fsm_keepalive (struct peer *peer)
 {
+  bgp_update_implicit_eors(peer);
+
   /* peer count update */
   peer->keepalive_in++;
 
diff --git a/bgpd/bgp_fsm.h b/bgpd/bgp_fsm.h
index 752d6e2..2144fd9 100644
--- a/bgpd/bgp_fsm.h
+++ b/bgpd/bgp_fsm.h
@@ -77,5 +77,6 @@ extern int bgp_stop (struct peer *peer);
 extern void bgp_timer_set (struct peer *);
 extern void bgp_fsm_change_status (struct peer *peer, int status);
 extern const char *peer_down_str[];
+extern void bgp_update_delay_end (struct bgp *);
 
 #endif /* _QUAGGA_BGP_FSM_H */
diff --git a/bgpd/bgp_packet.c b/bgpd/bgp_packet.c
index a90e56b..3aa7735 100644
--- a/bgpd/bgp_packet.c
+++ b/bgpd/bgp_packet.c
@@ -435,6 +435,9 @@ bgp_default_update_send (struct peer *peer, struct attr 
*attr,
   if (DISABLE_BGP_ANNOUNCE)
     return;
 
+  if (bgp_update_delay_active(peer->bgp))
+    return;
+
   if (afi == AFI_IP)
     str2prefix ("0.0.0.0/0", &p);
 #ifdef HAVE_IPV6
@@ -504,6 +507,9 @@ bgp_default_withdraw_send (struct peer *peer, afi_t afi, 
safi_t safi)
   if (DISABLE_BGP_ANNOUNCE)
     return;
 
+  if (bgp_update_delay_active(peer->bgp))
+    return;
+
   if (afi == AFI_IP)
     str2prefix ("0.0.0.0/0", &p);
 #ifdef HAVE_IPV6
@@ -1583,6 +1589,116 @@ bgp_open_receive (struct peer *peer, bgp_size_t size)
   return 0;
 }
 
+/* Called when there is a change in the EOR(implicit or explicit) status of a 
peer.
+   Ends the update-delay if all expected peers are done with EORs. */
+void
+bgp_check_update_delay(struct bgp *bgp)
+{
+  struct listnode *node, *nnode;
+  struct peer *peer;
+
+  if (BGP_DEBUG (normal, NORMAL))
+    zlog_debug ("Checking update delay, T: %d R: %d I:%d E: %d", 
bgp->established,
+                bgp->restarted_peers, bgp->implicit_eors, bgp->explicit_eors);
+
+  if (bgp->established <=
+      bgp->restarted_peers + bgp->implicit_eors + bgp->explicit_eors)
+    {
+      /* This is an extra sanity check to make sure we wait for all the
+         eligible configured peers. This check is performed if establish wait
+         timer is on, or establish wait option is not given with the
+         update-delay command */
+      if (bgp->t_establish_wait ||
+          (bgp->v_establish_wait == bgp->v_update_delay))
+        for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer))
+          {
+            if (!CHECK_FLAG (peer->flags, PEER_FLAG_SHUTDOWN)
+                && !peer->update_delay_over)
+              {
+                if (BGP_DEBUG (normal, NORMAL))
+                  zlog_debug (" Peer %s pending, continuing read-only mode",
+                              peer->host);
+                return;
+              }
+          }
+
+      zlog_info ("Update delay ended, restarted: %d, EORs implicit: %d, 
explicit: %d",
+                 bgp->restarted_peers, bgp->implicit_eors, bgp->explicit_eors);
+      bgp_update_delay_end(bgp);
+    }
+}
+
+/* Called if peer is known to have restarted. The restart-state bit in
+   Graceful-Restart capability is used for that */
+void
+bgp_update_restarted_peers (struct peer *peer)
+{
+  if (!bgp_update_delay_active(peer->bgp)) return; /* BGP update delay has 
ended */
+  if (peer->update_delay_over) return; /* This peer has already been 
considered */
+
+  if (BGP_DEBUG (normal, NORMAL))
+    zlog_debug ("Peer %s: Checking restarted", peer->host);
+
+  if (peer->status == Established)
+    {
+      peer->update_delay_over = 1;
+      peer->bgp->restarted_peers++;
+      bgp_check_update_delay(peer->bgp);
+    }
+}
+
+/* Called as peer receives a keep-alive. Determines if this occurence can be
+   taken as an implicit EOR for this peer.
+   NOTE: The very first keep-alive after the Established state of a peer is
+         considered implicit EOR for the update-delay purposes */
+void
+bgp_update_implicit_eors (struct peer *peer)
+{
+  if (!bgp_update_delay_active(peer->bgp)) return; /* BGP update delay has 
ended */
+  if (peer->update_delay_over) return; /* This peer has already been 
considered */
+
+  if (BGP_DEBUG (normal, NORMAL))
+    zlog_debug ("Peer %s: Checking implicit EORs", peer->host);
+
+  if (peer->status == Established)
+    {
+      peer->update_delay_over = 1;
+      peer->bgp->implicit_eors++;
+      bgp_check_update_delay(peer->bgp);
+    }
+}
+
+/* Should be called only when there is a change in the EOR_RECEIVED status
+   for any afi/safi on a peer */
+static void
+bgp_update_explicit_eors (struct peer *peer)
+{
+  afi_t afi;
+  safi_t safi;
+
+  if (!bgp_update_delay_active(peer->bgp)) return; /* BGP update delay has 
ended */
+  if (peer->update_delay_over) return; /* This peer has already been 
considered */
+
+  if (BGP_DEBUG (normal, NORMAL))
+    zlog_debug ("Peer %s: Checking explicit EORs", peer->host);
+
+  for (afi = AFI_IP; afi < AFI_MAX; afi++)
+    for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++)
+      {
+        if (peer->afc_nego[afi][safi] &&
+            !CHECK_FLAG(peer->af_sflags[afi][safi], PEER_STATUS_EOR_RECEIVED))
+          {
+            if (BGP_DEBUG (normal, NORMAL))
+              zlog_debug ("   afi %d safi %d didnt receive EOR", afi, safi);
+            return;
+          }
+      }
+
+  peer->update_delay_over = 1;
+  peer->bgp->explicit_eors++;
+  bgp_check_update_delay(peer->bgp);
+}
+
 /* Parse BGP Update packet and make attribute object. */
 static int
 bgp_update_receive (struct peer *peer, bgp_size_t size)
@@ -1766,7 +1882,7 @@ bgp_update_receive (struct peer *peer, bgp_size_t size)
        bgp_nlri_parse (peer, NULL, &withdraw);
 
       if (update.length)
-         bgp_nlri_parse (peer, NLRI_ATTR_ARG, &update);
+       bgp_nlri_parse (peer, NLRI_ATTR_ARG, &update);
 
       if (mp_update.length
          && mp_update.afi == AFI_IP 
@@ -1781,8 +1897,13 @@ bgp_update_receive (struct peer *peer, bgp_size_t size)
       if (! attribute_len && ! withdraw_len)
        {
          /* End-of-RIB received */
-         SET_FLAG (peer->af_sflags[AFI_IP][SAFI_UNICAST],
-                   PEER_STATUS_EOR_RECEIVED);
+         if (!CHECK_FLAG(peer->af_sflags[AFI_IP][SAFI_UNICAST],
+                         PEER_STATUS_EOR_RECEIVED))
+           {
+             SET_FLAG (peer->af_sflags[AFI_IP][SAFI_UNICAST],
+                       PEER_STATUS_EOR_RECEIVED);
+             bgp_update_explicit_eors(peer);
+           }
 
          /* NSF delete stale route */
          if (peer->nsf[AFI_IP][SAFI_UNICAST])
@@ -1811,8 +1932,13 @@ bgp_update_receive (struct peer *peer, bgp_size_t size)
          && mp_withdraw.length == 0)
        {
          /* End-of-RIB received */
-         SET_FLAG (peer->af_sflags[AFI_IP][SAFI_MULTICAST],
-                   PEER_STATUS_EOR_RECEIVED);
+         if (!CHECK_FLAG (peer->af_sflags[AFI_IP][SAFI_MULTICAST],
+                           PEER_STATUS_EOR_RECEIVED))
+           {
+             SET_FLAG (peer->af_sflags[AFI_IP][SAFI_MULTICAST],
+                       PEER_STATUS_EOR_RECEIVED);
+             bgp_update_explicit_eors(peer);
+           }
 
          /* NSF delete stale route */
          if (peer->nsf[AFI_IP][SAFI_MULTICAST])
@@ -1841,7 +1967,12 @@ bgp_update_receive (struct peer *peer, bgp_size_t size)
          && mp_withdraw.length == 0)
        {
          /* End-of-RIB received */
-         SET_FLAG (peer->af_sflags[AFI_IP6][SAFI_UNICAST], 
PEER_STATUS_EOR_RECEIVED);
+    if (!CHECK_FLAG (peer->af_sflags[AFI_IP6][SAFI_UNICAST],
+                           PEER_STATUS_EOR_RECEIVED))
+      {
+             SET_FLAG (peer->af_sflags[AFI_IP6][SAFI_UNICAST], 
PEER_STATUS_EOR_RECEIVED);
+        bgp_update_explicit_eors(peer);
+      }
 
          /* NSF delete stale route */
          if (peer->nsf[AFI_IP6][SAFI_UNICAST])
@@ -1870,6 +2001,13 @@ bgp_update_receive (struct peer *peer, bgp_size_t size)
          && mp_withdraw.length == 0)
        {
          /* End-of-RIB received */
+    if (!CHECK_FLAG (peer->af_sflags[AFI_IP6][SAFI_MULTICAST],
+                           PEER_STATUS_EOR_RECEIVED))
+      {
+             SET_FLAG (peer->af_sflags[AFI_IP6][SAFI_MULTICAST], 
PEER_STATUS_EOR_RECEIVED);
+        bgp_update_explicit_eors(peer);
+      }
+
 
          /* NSF delete stale route */
          if (peer->nsf[AFI_IP6][SAFI_MULTICAST])
@@ -1898,6 +2036,12 @@ bgp_update_receive (struct peer *peer, bgp_size_t size)
          && mp_withdraw.length == 0)
        {
          /* End-of-RIB received */
+    if (!CHECK_FLAG (peer->af_sflags[AFI_IP][SAFI_MPLS_VPN],
+                           PEER_STATUS_EOR_RECEIVED))
+      {
+             SET_FLAG (peer->af_sflags[AFI_IP][SAFI_MPLS_VPN], 
PEER_STATUS_EOR_RECEIVED);
+        bgp_update_explicit_eors(peer);
+      }
 
          if (BGP_DEBUG (update, UPDATE_IN))
            zlog (peer->log, LOG_DEBUG, "rcvd End-of-RIB for VPNv4 Unicast from 
%s",
diff --git a/bgpd/bgp_packet.h b/bgpd/bgp_packet.h
index 8f0ebe3..79390ec 100644
--- a/bgpd/bgp_packet.h
+++ b/bgpd/bgp_packet.h
@@ -53,5 +53,7 @@ extern void bgp_default_update_send (struct peer *, struct 
attr *,
 extern void bgp_default_withdraw_send (struct peer *, afi_t, safi_t);
 
 extern int bgp_capability_receive (struct peer *, bgp_size_t);
-
+extern void bgp_update_restarted_peers (struct peer *);
+extern void bgp_update_implicit_eors (struct peer *);
+extern void bgp_check_update_delay (struct bgp *);
 #endif /* _QUAGGA_BGP_PACKET_H */
diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c
index 24c17d6..6783c60 100644
--- a/bgpd/bgp_route.c
+++ b/bgpd/bgp_route.c
@@ -1695,7 +1695,7 @@ bgp_processq_del (struct work_queue *wq, void *data)
   XFREE (MTYPE_BGP_PROCESS_QUEUE, pq);
 }
 
-static void
+void
 bgp_process_queue_init (void)
 {
   bm->process_main_queue
@@ -2649,9 +2649,19 @@ bgp_announce_table (struct peer *peer, afi_t afi, safi_t 
safi,
   if (! table)
     table = (rsclient) ? peer->rib[afi][safi] : peer->bgp->rib[afi][safi];
 
-  if (safi != SAFI_MPLS_VPN
-      && CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_DEFAULT_ORIGINATE))
-    bgp_default_originate (peer, afi, safi, 0);
+  if (safi != SAFI_MPLS_VPN)
+    {
+      if (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_DEFAULT_ORIGINATE))
+        {
+          bgp_default_originate (peer, afi, safi, 0);
+        }
+      else
+        {
+          /* Send the withdraw if it was postponed during read-only mode. */
+          if (CHECK_FLAG (peer->af_flags[afi][safi], 
PEER_STATUS_DEFAULT_ORIGINATE))
+            bgp_default_originate (peer, afi, safi, 1);
+        }
+    }
 
   /* It's initialized in bgp_announce_[check|check_rsclient]() */
   attr.extra = &extra;
@@ -2703,6 +2713,9 @@ bgp_announce_route_all (struct peer *peer)
   afi_t afi;
   safi_t safi;
   
+  if (bgp_update_delay_active(peer->bgp))
+    return;
+
   for (afi = AFI_IP; afi < AFI_MAX; afi++)
     for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++)
       bgp_announce_route (peer, afi, safi);
diff --git a/bgpd/bgp_route.h b/bgpd/bgp_route.h
index 3d2eea5..fea18dd 100644
--- a/bgpd/bgp_route.h
+++ b/bgpd/bgp_route.h
@@ -170,6 +170,7 @@ enum bgp_clear_route_type
 };
 
 /* Prototypes. */
+extern void bgp_process_queue_init (void);
 extern void bgp_route_init (void);
 extern void bgp_route_finish (void);
 extern void bgp_cleanup_routes (void);
diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c
index 59427bf..b95be72 100644
--- a/bgpd/bgp_vty.c
+++ b/bgpd/bgp_vty.c
@@ -697,6 +697,108 @@ bgp_maxpaths_config_vty (struct vty *vty, int peer_type, 
const char *mpaths,
   return CMD_SUCCESS;
 }
 
+static int
+bgp_update_delay_config_vty (struct vty *vty, const char *delay,
+                             const char *wait)
+{
+  struct bgp *bgp;
+  u_int16_t update_delay;
+  u_int16_t establish_wait;
+
+
+  bgp = vty->index;
+
+  VTY_GET_INTEGER_RANGE ("update-delay", update_delay, delay,
+                         BGP_UPDATE_DELAY_MIN, BGP_UPDATE_DELAY_MAX);
+
+  if (!wait) /* update-delay <delay> */
+    {
+      bgp->v_update_delay = update_delay;
+      bgp->v_establish_wait = bgp->v_update_delay;
+      return CMD_SUCCESS;
+    }
+
+  /* update-delay <delay> <establish-wait> */
+  establish_wait = atoi (wait);
+  if (update_delay < establish_wait)
+    {
+      vty_out (vty, "%%Failed: update-delay less than the establish-wait!%s",
+               VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  bgp->v_update_delay = update_delay;
+  bgp->v_establish_wait = establish_wait;
+
+  return CMD_SUCCESS;
+}
+
+static int
+bgp_update_delay_deconfig_vty (struct vty *vty)
+{
+  struct bgp *bgp;
+
+  bgp = vty->index;
+
+  bgp->v_update_delay = BGP_UPDATE_DELAY_DEF;
+  bgp->v_establish_wait = bgp->v_update_delay;
+
+  return CMD_SUCCESS;
+}
+
+int
+bgp_config_write_update_delay (struct vty *vty, struct bgp *bgp)
+{
+  if (bgp->v_update_delay != BGP_UPDATE_DELAY_DEF)
+    {
+      vty_out (vty, " update-delay %d", bgp->v_update_delay);
+      if (bgp->v_update_delay != bgp->v_establish_wait)
+        vty_out (vty, " %d", bgp->v_establish_wait);
+      vty_out (vty, "%s", VTY_NEWLINE);
+    }
+
+  return 0;
+}
+
+
+/* Update-delay configuration */
+DEFUN (bgp_update_delay,
+       bgp_update_delay_cmd,
+       "update-delay <0-3600>",
+       "Force initial delay for best-path and updates\n"
+       "Seconds\n")
+{
+  return bgp_update_delay_config_vty(vty, argv[0], NULL);
+}
+
+DEFUN (bgp_update_delay_establish_wait,
+       bgp_update_delay_establish_wait_cmd,
+       "update-delay <0-3600> <1-3600>",
+       "Force initial delay for best-path and updates\n"
+       "Seconds\n"
+       "Wait for peers to be established\n"
+       "Seconds\n")
+{
+  return bgp_update_delay_config_vty(vty, argv[0], argv[1]);
+}
+
+/* Update-delay deconfiguration */
+DEFUN (no_bgp_update_delay,
+       no_bgp_update_delay_cmd,
+       "no update-delay <0-3600>",
+       "Force initial delay for best-path and updates\n"
+       "Seconds\n")
+{
+  return bgp_update_delay_deconfig_vty(vty);
+}
+
+ALIAS (no_bgp_update_delay,
+       no_bgp_update_delay_establish_wait_cmd,
+       "no update-delay <0-3600> <1-3600>",
+       "Force initial delay for best-path and updates\n"
+       "Seconds\n"
+       "Wait for peers to be established\n"
+       "Seconds\n")
 
 /* Maximum-paths configuration */
 DEFUN (bgp_maxpaths,
@@ -4347,6 +4449,11 @@ bgp_clear (struct vty *vty, struct bgp *bgp,  afi_t afi, 
safi_t safi,
          if (ret < 0)
            bgp_clear_vty_error (vty, peer, afi, safi, ret);
        }
+
+      /* This is to apply read-only mode on this clear. */
+      if (stype == BGP_CLEAR_SOFT_NONE)
+        bgp->update_delay_over = 0;
+
       return CMD_SUCCESS;
     }
 
@@ -6958,6 +7065,30 @@ bgp_show_summary (struct vty *vty, struct bgp *bgp, int 
afi, int safi)
               vty_out (vty,
                        "BGP router identifier %s, local AS number %u%s",
                        inet_ntoa (bgp->router_id), bgp->as, VTY_NEWLINE);
+              if (bgp_update_delay_configured(bgp))
+                {
+                  vty_out (vty, "Read-only mode update-delay limit: %d 
seconds%s",
+                           bgp->v_update_delay, VTY_NEWLINE);
+                  if (bgp->v_update_delay != bgp->v_establish_wait)
+                    vty_out (vty, "                   Establish wait: %d 
seconds%s",
+                             bgp->v_establish_wait, VTY_NEWLINE);
+                  if (bgp_update_delay_active(bgp))
+                    {
+                      vty_out (vty, "  First neighbor established: %s%s",
+                               bgp->update_delay_begin_time, VTY_NEWLINE);
+                      vty_out (vty, "  Delay in progress%s", VTY_NEWLINE);
+                    }
+                  else
+                    {
+                      if (bgp->update_delay_over)
+                        {
+                          vty_out (vty, "  First neighbor established: %s%s",
+                                   bgp->update_delay_begin_time, VTY_NEWLINE);
+                          vty_out (vty, "  Best-paths/updates resumed: %s%s",
+                                   bgp->update_delay_end_time, VTY_NEWLINE);
+                        }
+                    }
+                }
 
               ents = bgp_table_count (bgp->rib[afi][safi]);
               vty_out (vty, "RIB entries %ld, using %s of memory%s", ents,
@@ -7039,6 +7170,7 @@ bgp_show_summary (struct vty *vty, struct bgp *bgp, int 
afi, int safi)
   else
     vty_out (vty, "No %s neighbor is configured%s",
             afi == AFI_IP ? "IPv4" : "IPv6", VTY_NEWLINE);
+
   return CMD_SUCCESS;
 }
 
@@ -9168,6 +9300,12 @@ bgp_vty_init (void)
   install_element (BGP_NODE, &bgp_confederation_peers_cmd);
   install_element (BGP_NODE, &no_bgp_confederation_peers_cmd);
 
+  /* bgp update-delay command */
+  install_element (BGP_NODE, &bgp_update_delay_cmd);
+  install_element (BGP_NODE, &no_bgp_update_delay_cmd);
+  install_element (BGP_NODE, &bgp_update_delay_establish_wait_cmd);
+  install_element (BGP_NODE, &no_bgp_update_delay_establish_wait_cmd);
+
   /* "maximum-paths" commands. */
   install_element (BGP_NODE, &bgp_maxpaths_cmd);
   install_element (BGP_NODE, &no_bgp_maxpaths_cmd);
diff --git a/bgpd/bgp_vty.h b/bgpd/bgp_vty.h
index 2df8aaa..61c114e 100644
--- a/bgpd/bgp_vty.h
+++ b/bgpd/bgp_vty.h
@@ -21,9 +21,12 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, 
Boston, MA
 #ifndef _QUAGGA_BGP_VTY_H
 #define _QUAGGA_BGP_VTY_H
 
+#include "bgpd/bgpd.h"
+
 #define CMD_AS_RANGE "<1-4294967295>"
 
 extern void bgp_vty_init (void);
 extern const char *afi_safi_print (afi_t, safi_t);
+extern int bgp_config_write_update_delay (struct vty *, struct bgp *);
 
 #endif /* _QUAGGA_BGP_VTY_H */
diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c
index 2325f07..7a761d2 100644
--- a/bgpd/bgpd.c
+++ b/bgpd/bgpd.c
@@ -2031,6 +2031,7 @@ bgp_create (as_t *as, const char *name)
        bgp->maxpaths[afi][safi].maxpaths_ibgp = BGP_DEFAULT_MAXPATHS;
       }
 
+  bgp->v_update_delay = BGP_UPDATE_DELAY_DEF;
   bgp->default_local_pref = BGP_DEFAULT_LOCAL_PREF;
   bgp->default_holdtime = BGP_DEFAULT_HOLDTIME;
   bgp->default_keepalive = BGP_DEFAULT_KEEPALIVE;
@@ -5439,6 +5440,9 @@ bgp_config_write (struct vty *vty)
       if (bgp_flag_check (bgp, BGP_FLAG_DETERMINISTIC_MED))
        vty_out (vty, " bgp deterministic-med%s", VTY_NEWLINE);
 
+      /* BGP update-delay. */
+      bgp_config_write_update_delay (vty, bgp);
+
       /* BGP graceful-restart. */
       if (bgp->stalepath_time != BGP_DEFAULT_STALEPATH_TIME)
        vty_out (vty, " bgp graceful-restart stalepath-time %d%s",
diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h
index db9445d..c4e9e4d 100644
--- a/bgpd/bgpd.h
+++ b/bgpd/bgpd.h
@@ -106,6 +106,22 @@ struct bgp
 
   struct thread *t_startup;
 
+  /* BGP update delay on startup */
+  struct thread *t_update_delay;
+  struct thread *t_establish_wait;
+  u_char update_delay_over;
+  u_int16_t v_update_delay;
+  u_int16_t v_establish_wait;
+  char update_delay_begin_time[64];
+  char update_delay_end_time[64];
+  u_int32_t established;
+  u_int32_t restarted_peers;
+  u_int32_t implicit_eors;
+  u_int32_t explicit_eors;
+#define BGP_UPDATE_DELAY_DEF              0
+#define BGP_UPDATE_DELAY_MIN              0
+#define BGP_UPDATE_DELAY_MAX              3600
+
   /* BGP flags. */
   u_int16_t flags;
 #define BGP_FLAG_ALWAYS_COMPARE_MED       (1 << 0)
@@ -506,6 +522,9 @@ struct peer
   u_int32_t established;       /* Established */
   u_int32_t dropped;           /* Dropped */
 
+  /* Update delay related fields */
+  u_char    update_delay_over;  /* When this is set, BGP is no more waiting 
for EOR */
+
   /* Syncronization list and time.  */
   struct bgp_synchronize *sync[AFI_MAX][SAFI_MAX];
   time_t synctime;
@@ -903,6 +922,8 @@ extern int bgp_timers_unset (struct bgp *);
 extern int bgp_default_local_preference_set (struct bgp *, u_int32_t);
 extern int bgp_default_local_preference_unset (struct bgp *);
 
+extern int bgp_update_delay_active (struct bgp *);
+extern int bgp_update_delay_configured (struct bgp *);
 extern int peer_rsclient_active (struct peer *);
 
 extern int peer_remote_as (struct bgp *, union sockunion *, as_t *, afi_t, 
safi_t);
diff --git a/doc/bgpd.texi b/doc/bgpd.texi
index 5eb0851..ac8ebdd 100644
--- a/doc/bgpd.texi
+++ b/doc/bgpd.texi
@@ -223,6 +223,30 @@ Redistribute RIP route to BGP process.
 Redistribute OSPF route to BGP process.
 @end deffn
 
+@deffn {BGP} {update-delay @var{max-delay}} {}
+@deffnx {BGP} {update-delay @var{max-delay} @var{establish-wait}} {}
+This feature is used to enable read-only mode on BGP process restart or when
+BGP process is cleared using 'clear ip bgp *'. When applicable, read-only mode
+would begin as soon as the first peer reaches Established status and a timer
+for max-delay seconds is started.
+
+During this mode BGP doesn't run any best-path or generate any updates to its
+peers. This mode continues until:
+1. All the configured peers, except the shutdown peers, have sent explicit EOR
+(End-Of-RIB) or an implicit-EOR. The first keep-alive after BGP has reached
+Established is considered an implicit-EOR.
+   If the establish-wait optional value is given, then BGP will wait for
+   peers to reach established from the begining of the update-delay till the
+   establish-wait period is over, i.e. the minimum set of established peers for
+   which EOR is expected would be peers established during the establish-wait
+   window, not necessarily all the configured neighbors.
+2. max-delay period is over.
+On hitting any of the above two conditions, BGP resumes the decision process
+and generates updates to its peers.
+
+Default max-delay is 0, i.e. the feature is off by default.
+@end deffn
+
 @node BGP Peer
 @section BGP Peer
 
-- 
1.9.1


_______________________________________________
Quagga-dev mailing list
[email protected]
https://lists.quagga.net/mailman/listinfo/quagga-dev

Reply via email to