Thanks David.

This feature works just fine for what its designed for.

However, I would like to point out (a late finding) that the command mode
for this feature isn't quite right for the current BGP code/feature set.

Quagga BGP has multi-instance feature coded with 'views', all handled by
the same BGP daemon which has global process level queues catering to all
the views (struct bgp is the main container per view)

This patch configures update-delay in 'router bgp <asn> [view <name>]'
mode, while the implementation is working off global process level queues,
by stopping and resuming those. This needs some resolution.

So, IMO, we have a couple of choices:

(A)
Make this feature truly global to BGP deamon by moving the update-delay
config at the level where 'bgp multi-instance' is configured.

Unfortunately, there is a catch in this approach, the feature is currently
supported for 'clear ip bgp *' too, which can clear one view at a time. So,
with the current patch clearing one view can temporarily hold other views
best-paths/updates.

OR

(B)
Keep the config where it is, but fix the implementation to make it per view.

This option means some surgery of the BGP code to make the process queues
per view etc. Which may (in future) anyways be needed for fairness reasons
across views.

Given the best use case of this feature is at the daemon start-up time, I
would say we can go with with option (A) for now.

Let us know what your thoughts are about this.

Regards
Vipin



On Thu, May 14, 2015 at 2:02 PM, David Lamparter <
[email protected]> wrote:

> From: Vipin Kumar <[email protected]>
>
> Add support for update-delay option to speed up BGP startup convergence.
>
> 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.
> MPLEMENTATION 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]>
> Reviewed-by: Dinesh Dutt <[email protected]>
> ---
>  bgpd/bgp_fsm.c    | 176
> ++++++++++++++++++++++++++++++++++++++++++++++++++++--
>  bgpd/bgp_fsm.h    |   1 +
>  bgpd/bgp_packet.c | 154 +++++++++++++++++++++++++++++++++++++++++++++--
>  bgpd/bgp_packet.h |   4 +-
>  bgpd/bgp_route.c  |  21 +++++--
>  bgpd/bgp_route.h  |   1 +
>  bgpd/bgp_vty.c    | 138 ++++++++++++++++++++++++++++++++++++++++++
>  bgpd/bgp_vty.h    |   1 +
>  bgpd/bgpd.c       |   4 ++
>  bgpd/bgpd.h       |  21 +++++++
>  doc/bgpd.texi     |  24 ++++++++
>  11 files changed, 531 insertions(+), 14 deletions(-)
>
> diff --git a/bgpd/bgp_fsm.c b/bgpd/bgp_fsm.c
> index d261bb5..9fd8528 100644
> --- a/bgpd/bgp_fsm.c
> +++ b/bgpd/bgp_fsm.c
> @@ -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++;
>
> diff --git a/bgpd/bgp_fsm.h b/bgpd/bgp_fsm.h
> index a749f8e..bef5e0a 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 18114ad..f7af4ee 100644
> --- a/bgpd/bgp_packet.c
> +++ b/bgpd/bgp_packet.c
> @@ -438,6 +438,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
> @@ -511,6 +514,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
> @@ -1596,6 +1602,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)
> @@ -1794,8 +1910,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])
> @@ -1824,8 +1945,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])
> @@ -1854,7 +1980,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])
> @@ -1883,6 +2014,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])
> @@ -1911,6 +2049,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 34ba1ab..a136d38 100644
> --- a/bgpd/bgp_route.c
> +++ b/bgpd/bgp_route.c
> @@ -1647,7 +1647,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
> @@ -2601,9 +2601,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;
> @@ -2655,6 +2665,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 00d766d..cddd4a6 100644
> --- a/bgpd/bgp_vty.c
> +++ b/bgpd/bgp_vty.c
> @@ -794,6 +794,108 @@ bgp_config_write_maxpaths (struct vty *vty, struct
> bgp *bgp, afi_t afi,
>    return 0;
>  }
>
> +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")
> +
>  /* BGP timers.  */
>
>  DEFUN (bgp_timers,
> @@ -4341,6 +4443,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;
>      }
>
> @@ -6952,6 +7059,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,
> @@ -7033,6 +7164,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;
>  }
>
> @@ -9157,6 +9289,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..e9dc09a 100644
> --- a/bgpd/bgp_vty.h
> +++ b/bgpd/bgp_vty.h
> @@ -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 */
> diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c
> index 4de854e..ae24dd3 100644
> --- a/bgpd/bgpd.c
> +++ b/bgpd/bgpd.c
> @@ -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;
> @@ -5309,6 +5310,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 40c381c..dad5980 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)
> @@ -505,6 +521,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;
> @@ -897,6 +916,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 7d92b5e..46d7491 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
>
> --
> 2.0.4
>
>
_______________________________________________
Quagga-dev mailing list
[email protected]
https://lists.quagga.net/mailman/listinfo/quagga-dev

Reply via email to