]> git.proxmox.com Git - mirror_frr.git/commitdiff
bgpd: bgpd-update-delay.patch
authorDonald Sharp <sharpd@cumulusnetworks.com>
Wed, 20 May 2015 00:40:33 +0000 (17:40 -0700)
committerDonald Sharp <sharpd@cumulusnetworks.com>
Wed, 20 May 2015 00:40:33 +0000 (17:40 -0700)
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 <vipin@cumulusnetworks.com>
Reviewed-by: Pradosh Mohapatra <pmohapat@cumulusnetworks.com>
             Dinesh Dutt <ddutt@cumulusnetworks.com>

bgpd/bgp_fsm.c
bgpd/bgp_fsm.h
bgpd/bgp_packet.c
bgpd/bgp_packet.h
bgpd/bgp_route.c
bgpd/bgp_route.h
bgpd/bgp_vty.c
bgpd/bgp_vty.h
bgpd/bgpd.c
bgpd/bgpd.h
doc/bgpd.texi

index 112c34a198db9520a05f144c386cb49a5299c582..653dd7b1be026a642438d4239e6ad2312ae6af3d 100644 (file)
@@ -30,6 +30,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 #include "stream.h"
 #include "memory.h"
 #include "plist.h"
+#include "workqueue.h"
 
 #include "bgpd/bgpd.h"
 #include "bgpd/bgp_attr.h"
@@ -40,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 */
@@ -395,6 +397,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 (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 (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
@@ -411,7 +564,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,
@@ -762,6 +920,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;
 }
@@ -883,9 +1052,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++)
@@ -905,6 +1071,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++;
 
index a749f8ea31d35419868d05cf57b0f778721f7d39..bef5e0a596bebaf030536d71a9bdf8c19785555b 100644 (file)
@@ -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 */
index 62ef86166fc3ba7ea52197a1e136c43fdf5a05a7..674892515bf855c8ce25dff00f5855587d853038 100644 (file)
@@ -439,6 +439,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
@@ -512,6 +515,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
@@ -1597,6 +1603,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)
@@ -1806,8 +1922,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])
@@ -1836,8 +1957,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])
@@ -1866,7 +1992,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])
@@ -1895,6 +2026,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])
@@ -1923,6 +2061,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",
index 8f0ebe318c618b07b33483aaaaffa47077e019fe..79390ec8a15d0ea8e96ee9d829e7790f6e84f9f3 100644 (file)
@@ -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 */
index 6397c550903aa7792e3484c5ae3c5d0ccbaa0f54..15c3b6eab27d66a47d8af7ce22eddb5e09f01be7 100644 (file)
@@ -1664,7 +1664,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
@@ -2605,9 +2605,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;
@@ -2659,6 +2669,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);
index 3d2eea51b509e8a6916caeb9a73ce2ff6f8f6255..fea18dd2996a6a4c5a9589d28daf13c3beebe8d2 100644 (file)
@@ -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);
index 6ca595c294acd52b9b4163578be23a988d8b11a3..5e854d6fb5551a938912aafd165620bcf6c2253f 100644 (file)
@@ -692,6 +692,108 @@ bgp_maxpaths_config_vty (struct vty *vty, int peer_type, 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,
@@ -4353,6 +4455,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;
     }
 
@@ -6964,6 +7071,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,
@@ -7045,6 +7176,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;
 }
 
@@ -9169,6 +9301,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);
index 2df8aaa5dec270dd02f410c24c92d53a19bd9d44..e9dc09a061de6d0fdcd1644dad85f21794a0611d 100644 (file)
@@ -25,5 +25,6 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 
 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 */
index 140cb189cc685e52933d38ab3dbe86f3ec8d20e4..3c2f82c6bed2aadae6cfab4844a88a75ba32090f 100644 (file)
@@ -1973,6 +1973,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;
@@ -5307,6 +5308,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",
index 73f2849ef564367f16f77dc4b348a83b13bc4730..4fb6afefc0cb0f49f944bb05c8429b1e80be4f76 100644 (file)
@@ -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)
@@ -507,6 +523,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;
@@ -899,6 +918,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);
index de709707a7d5de86d3e0e435e150ffba45bafb9f..10fe13ca22ebf837af341d4ca9b1bc9c470df8b3 100644 (file)
@@ -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