From: Donald Sharp Date: Wed, 20 May 2015 00:40:33 +0000 (-0700) Subject: bgpd: bgpd-update-delay.patch X-Git-Tag: frr-2.0-rc1~1546 X-Git-Url: https://git.puffer.fish/?a=commitdiff_plain;h=f188f2c424e8cf37517e68c09ca93329aa4297aa;p=matthieu%2Ffrr.git bgpd: bgpd-update-delay.patch COMMAND: 'update-delay []' 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 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 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 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 Reviewed-by: Pradosh Mohapatra Dinesh Dutt --- diff --git a/bgpd/bgp_fsm.c b/bgpd/bgp_fsm.c index 112c34a198..653dd7b1be 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 a749f8ea31..bef5e0a596 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 62ef86166f..674892515b 100644 --- a/bgpd/bgp_packet.c +++ b/bgpd/bgp_packet.c @@ -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", diff --git a/bgpd/bgp_packet.h b/bgpd/bgp_packet.h index 8f0ebe318c..79390ec8a1 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 6397c55090..15c3b6eab2 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -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); diff --git a/bgpd/bgp_route.h b/bgpd/bgp_route.h index 3d2eea51b5..fea18dd299 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 6ca595c294..5e854d6fb5 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -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 */ + { + bgp->v_update_delay = update_delay; + bgp->v_establish_wait = bgp->v_update_delay; + return CMD_SUCCESS; + } + + /* update-delay */ + 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); diff --git a/bgpd/bgp_vty.h b/bgpd/bgp_vty.h index 2df8aaa5de..e9dc09a061 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 140cb189cc..3c2f82c6be 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; @@ -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", diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h index 73f2849ef5..4fb6afefc0 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) @@ -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); diff --git a/doc/bgpd.texi b/doc/bgpd.texi index de709707a7..10fe13ca22 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