diff options
| -rw-r--r-- | bgpd/bgp_debug.c | 55 | ||||
| -rw-r--r-- | bgpd/bgp_debug.h | 4 | ||||
| -rw-r--r-- | bgpd/bgp_fsm.c | 702 | ||||
| -rw-r--r-- | bgpd/bgp_fsm.h | 77 | ||||
| -rw-r--r-- | bgpd/bgp_io.c | 16 | ||||
| -rw-r--r-- | bgpd/bgp_main.c | 1 | ||||
| -rw-r--r-- | bgpd/bgp_network.c | 28 | ||||
| -rw-r--r-- | bgpd/bgp_open.c | 145 | ||||
| -rw-r--r-- | bgpd/bgp_packet.c | 108 | ||||
| -rw-r--r-- | bgpd/bgp_packet.h | 9 | ||||
| -rw-r--r-- | bgpd/bgp_route.c | 237 | ||||
| -rw-r--r-- | bgpd/bgp_route.h | 1 | ||||
| -rw-r--r-- | bgpd/bgp_table.c | 37 | ||||
| -rw-r--r-- | bgpd/bgp_table.h | 7 | ||||
| -rw-r--r-- | bgpd/bgp_vty.c | 1387 | ||||
| -rw-r--r-- | bgpd/bgp_vty.h | 106 | ||||
| -rw-r--r-- | bgpd/bgp_zebra.c | 155 | ||||
| -rw-r--r-- | bgpd/bgp_zebra.h | 7 | ||||
| -rw-r--r-- | bgpd/bgpd.c | 193 | ||||
| -rw-r--r-- | bgpd/bgpd.h | 175 | ||||
| -rw-r--r-- | doc/user/bgp.rst | 175 | ||||
| -rw-r--r-- | lib/command.h | 17 | ||||
| -rw-r--r-- | lib/zclient.c | 29 | ||||
| -rw-r--r-- | lib/zclient.h | 21 | ||||
| -rw-r--r-- | tests/bgpd/test_mpath.c | 20 |
25 files changed, 3546 insertions, 166 deletions
diff --git a/bgpd/bgp_debug.c b/bgpd/bgp_debug.c index f716c4f308..498a871ce7 100644 --- a/bgpd/bgp_debug.c +++ b/bgpd/bgp_debug.c @@ -63,6 +63,7 @@ unsigned long conf_bgp_debug_vpn; unsigned long conf_bgp_debug_flowspec; unsigned long conf_bgp_debug_labelpool; unsigned long conf_bgp_debug_pbr; +unsigned long conf_bgp_debug_graceful_restart; unsigned long term_bgp_debug_as4; unsigned long term_bgp_debug_neighbor_events; @@ -80,6 +81,7 @@ unsigned long term_bgp_debug_vpn; unsigned long term_bgp_debug_flowspec; unsigned long term_bgp_debug_labelpool; unsigned long term_bgp_debug_pbr; +unsigned long term_bgp_debug_graceful_restart; struct list *bgp_debug_neighbor_events_peers = NULL; struct list *bgp_debug_keepalive_peers = NULL; @@ -1644,6 +1646,23 @@ DEFUN (debug_bgp_zebra, return CMD_SUCCESS; } +DEFUN (debug_bgp_graceful_restart, + debug_bgp_graceful_restart_cmd, + "debug bgp graceful-restart", + DEBUG_STR + BGP_STR + GR_DEBUG) +{ + if (vty->node == CONFIG_NODE) { + DEBUG_ON(graceful_restart, GRACEFUL_RESTART); + } else { + TERM_DEBUG_ON(graceful_restart, GRACEFUL_RESTART); + vty_out(vty, "BGP Graceful Restart debugging is on\n"); + } + return CMD_SUCCESS; +} + + DEFUN (debug_bgp_zebra_prefix, debug_bgp_zebra_prefix_cmd, "debug bgp zebra prefix <A.B.C.D/M|X:X::X:X/M>", @@ -1702,6 +1721,23 @@ DEFUN (no_debug_bgp_zebra, return CMD_SUCCESS; } +DEFUN (no_debug_bgp_graceful_restart, + no_debug_bgp_graceful_restart_cmd, + "no debug bgp graceful-restart", + DEBUG_STR + BGP_STR + GR_DEBUG + NO_STR) +{ + if (vty->node == CONFIG_NODE) { + DEBUG_OFF(graceful_restart, GRACEFUL_RESTART); + } else { + TERM_DEBUG_OFF(graceful_restart, GRACEFUL_RESTART); + vty_out(vty, "BGP Graceful Restart debugging is off\n"); + } + return CMD_SUCCESS; +} + DEFUN (no_debug_bgp_zebra_prefix, no_debug_bgp_zebra_prefix_cmd, "no debug bgp zebra prefix <A.B.C.D/M|X:X::X:X/M>", @@ -2039,6 +2075,8 @@ DEFUN (no_debug_bgp, TERM_DEBUG_OFF(labelpool, LABELPOOL); TERM_DEBUG_OFF(pbr, PBR); TERM_DEBUG_OFF(pbr, PBR_ERROR); + TERM_DEBUG_OFF(graceful_restart, GRACEFUL_RESTART); + vty_out(vty, "All possible debugging has been turned off\n"); return CMD_SUCCESS; @@ -2094,7 +2132,11 @@ DEFUN_NOSH (show_debugging_bgp, if (BGP_DEBUG(zebra, ZEBRA)) bgp_debug_list_print(vty, " BGP zebra debugging is on", - bgp_debug_zebra_prefixes); + bgp_debug_zebra_prefixes); + + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + vty_out(vty, + " BGP graceful-restart debugging is on"); if (BGP_DEBUG(allow_martians, ALLOW_MARTIANS)) vty_out(vty, " BGP allow martian next hop debugging is on\n"); @@ -2229,6 +2271,11 @@ static int bgp_config_write_debug(struct vty *vty) vty_out(vty, "debug bgp pbr error\n"); write++; } + + if (CONF_BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) { + vty_out(vty, "debug bgp graceful-restart\n"); + write++; + } return write; } @@ -2262,6 +2309,9 @@ void bgp_debug_init(void) install_element(ENABLE_NODE, &debug_bgp_bestpath_prefix_cmd); install_element(CONFIG_NODE, &debug_bgp_bestpath_prefix_cmd); + install_element(ENABLE_NODE, &debug_bgp_graceful_restart_cmd); + install_element(CONFIG_NODE, &debug_bgp_graceful_restart_cmd); + /* debug bgp updates (in|out) */ install_element(ENABLE_NODE, &debug_bgp_update_direct_cmd); install_element(CONFIG_NODE, &debug_bgp_update_direct_cmd); @@ -2327,6 +2377,9 @@ void bgp_debug_init(void) install_element(ENABLE_NODE, &no_debug_bgp_bestpath_prefix_cmd); install_element(CONFIG_NODE, &no_debug_bgp_bestpath_prefix_cmd); + install_element(ENABLE_NODE, &no_debug_bgp_graceful_restart_cmd); + install_element(CONFIG_NODE, &no_debug_bgp_graceful_restart_cmd); + install_element(ENABLE_NODE, &debug_bgp_vpn_cmd); install_element(CONFIG_NODE, &debug_bgp_vpn_cmd); install_element(ENABLE_NODE, &no_debug_bgp_vpn_cmd); diff --git a/bgpd/bgp_debug.h b/bgpd/bgp_debug.h index e1072c3df2..1e6482e969 100644 --- a/bgpd/bgp_debug.h +++ b/bgpd/bgp_debug.h @@ -76,6 +76,7 @@ extern unsigned long conf_bgp_debug_vpn; extern unsigned long conf_bgp_debug_flowspec; extern unsigned long conf_bgp_debug_labelpool; extern unsigned long conf_bgp_debug_pbr; +extern unsigned long conf_bgp_debug_graceful_restart; extern unsigned long term_bgp_debug_as4; extern unsigned long term_bgp_debug_neighbor_events; @@ -91,6 +92,7 @@ extern unsigned long term_bgp_debug_vpn; extern unsigned long term_bgp_debug_flowspec; extern unsigned long term_bgp_debug_labelpool; extern unsigned long term_bgp_debug_pbr; +extern unsigned long term_bgp_debug_graceful_restart; extern struct list *bgp_debug_neighbor_events_peers; extern struct list *bgp_debug_keepalive_peers; @@ -131,6 +133,8 @@ struct bgp_debug_filter { #define BGP_DEBUG_PACKET_SEND 0x01 #define BGP_DEBUG_PACKET_SEND_DETAIL 0x02 +#define BGP_DEBUG_GRACEFUL_RESTART 0x01 + #define CONF_DEBUG_ON(a, b) (conf_bgp_debug_ ## a |= (BGP_DEBUG_ ## b)) #define CONF_DEBUG_OFF(a, b) (conf_bgp_debug_ ## a &= ~(BGP_DEBUG_ ## b)) diff --git a/bgpd/bgp_fsm.c b/bgpd/bgp_fsm.c index 3667dae83d..c5b46487a5 100644 --- a/bgpd/bgp_fsm.c +++ b/bgpd/bgp_fsm.c @@ -35,7 +35,7 @@ #include "filter.h" #include "command.h" #include "lib_errors.h" - +#include "zclient.h" #include "lib/json.h" #include "bgpd/bgpd.h" #include "bgpd/bgp_attr.h" @@ -58,7 +58,8 @@ DEFINE_HOOK(peer_backward_transition, (struct peer * peer), (peer)) DEFINE_HOOK(peer_status_changed, (struct peer * peer), (peer)) - +extern const char *get_afi_safi_str(afi_t afi, + safi_t safi, bool for_json); /* Definition of display strings corresponding to FSM events. This should be * kept consistent with the events defined in bgpd.h */ @@ -250,6 +251,24 @@ static struct peer *peer_xfer_conn(struct peer *from_peer) peer->remote_id = from_peer->remote_id; peer->last_reset = from_peer->last_reset; + peer->peer_gr_present_state = from_peer->peer_gr_present_state; + peer->peer_gr_new_status_flag = from_peer->peer_gr_new_status_flag; + bgp_peer_gr_flags_update(peer); + + BGP_GR_ROUTER_DETECT_AND_SEND_CAPABILITY_TO_ZEBRA( + peer->bgp, + peer->bgp->peer); + + if (bgp_peer_gr_mode_get(peer) == PEER_DISABLE) { + + UNSET_FLAG(peer->sflags, PEER_STATUS_NSF_MODE); + + if (CHECK_FLAG(peer->sflags, + PEER_STATUS_NSF_WAIT)) { + peer_nsf_stop(peer); + } + } + if (from_peer->hostname != NULL) { if (peer->hostname) { XFREE(MTYPE_BGP_PEER_HOST, peer->hostname); @@ -613,6 +632,33 @@ static int bgp_graceful_stale_timer_expire(struct thread *thread) return 0; } +/* Selection deferral timer processing function */ +static int bgp_graceful_deferral_timer_expire(struct thread *thread) +{ + struct afi_safi_info *info; + afi_t afi; + safi_t safi; + struct bgp *bgp; + + info = THREAD_ARG(thread); + afi = info->afi; + safi = info->safi; + bgp = info->bgp; + + if (BGP_DEBUG(update, UPDATE_OUT)) + zlog_debug("afi %d, safi %d : graceful restart deferral timer expired", + afi, safi); + + bgp->gr_info[afi][safi].t_select_deferral = NULL; + + bgp->gr_info[afi][safi].eor_required = 0; + bgp->gr_info[afi][safi].eor_received = 0; + XFREE(MTYPE_TMP, info); + + /* Best path selection */ + return bgp_best_path_select_defer(bgp, afi, safi); +} + static int bgp_update_delay_applicable(struct bgp *bgp) { /* update_delay_over flag should be reset (set to 0) for any new @@ -1076,6 +1122,10 @@ int bgp_stop(struct peer *peer) safi_t safi; char orf_name[BUFSIZ]; int ret = 0; + struct bgp *bgp = peer->bgp; + struct graceful_restart_info *gr_info = NULL; + + peer->nsf_af_count = 0; if (peer_dynamic_neighbor(peer) && !(CHECK_FLAG(peer->flags, PEER_FLAG_DELETE))) { @@ -1098,6 +1148,7 @@ int bgp_stop(struct peer *peer) /* bgp log-neighbor-changes of neighbor Down */ if (bgp_flag_check(peer->bgp, BGP_FLAG_LOG_NEIGHBOR_CHANGES)) { struct vrf *vrf = vrf_lookup_by_id(peer->bgp->vrf_id); + zlog_info( "%%ADJCHANGE: neighbor %s(%s) in vrf %s Down %s", peer->host, @@ -1141,6 +1192,38 @@ int bgp_stop(struct peer *peer) peer->nsf[afi][safi] = 0; } + /* If peer reset before receiving EOR, decrement EOR count and + * cancel the selection deferral timer if there are no + * pending EOR messages to be received + */ + if (BGP_PEER_GRACEFUL_RESTART_CAPABLE(peer)) { + FOREACH_AFI_SAFI (afi, safi) { + if (peer->afc_nego[afi][safi] && + !CHECK_FLAG(peer->af_sflags[afi][safi], + PEER_STATUS_EOR_RECEIVED)) { + gr_info = &bgp->gr_info[afi][safi]; + + if (gr_info && (gr_info->eor_required)) + gr_info->eor_required--; + + if (gr_info && BGP_DEBUG(update, + UPDATE_OUT)) + zlog_debug( + "peer %s, EOR_required %d", + peer->host, + gr_info->eor_required); + + /* There is no pending EOR message */ + if (gr_info && gr_info->eor_required + == 0) { + BGP_TIMER_OFF( + gr_info->t_select_deferral); + gr_info->eor_received = 0; + } + } + } + } + /* set last reset time */ peer->resettime = peer->uptime = bgp_clock(); @@ -1249,7 +1332,6 @@ int bgp_stop(struct peer *peer) } else { bgp_peer_conf_if_to_su_update(peer); } - return ret; } @@ -1539,6 +1621,10 @@ static int bgp_reconnect(struct peer *peer) if (bgp_stop(peer) < 0) return -1; + /* Send graceful restart capabilty */ + BGP_GR_ROUTER_DETECT_AND_SEND_CAPABILITY_TO_ZEBRA( + peer->bgp, peer->bgp->peer); + bgp_start(peer); return 0; } @@ -1574,6 +1660,91 @@ static int bgp_fsm_holdtime_expire(struct peer *peer) return bgp_stop_with_notify(peer, BGP_NOTIFY_HOLD_ERR, 0); } +/* Start the selection deferral timer thread for the specified AFI, SAFI */ +static int bgp_start_deferral_timer(struct bgp *bgp, afi_t afi, safi_t safi, + struct graceful_restart_info *gr_info) +{ + struct afi_safi_info *thread_info; + + /* If the deferral timer is active, then increment eor count */ + if (gr_info->t_select_deferral) { + gr_info->eor_required++; + return 0; + } + + /* Start the deferral timer when the first peer enabled for the graceful + * restart is established + */ + if (gr_info->eor_required == 0) { + thread_info = XMALLOC(MTYPE_TMP, sizeof(struct afi_safi_info)); + if (thread_info == NULL) { + if (BGP_DEBUG(update, UPDATE_OUT)) + zlog_debug("%s : Error allocating thread info", + __func__); + return -1; + } + + thread_info->afi = afi; + thread_info->safi = safi; + thread_info->bgp = bgp; + + thread_add_timer(bm->master, + bgp_graceful_deferral_timer_expire, + thread_info, bgp->select_defer_time, + &gr_info->t_select_deferral); + if (gr_info->t_select_deferral == NULL) { + if (BGP_DEBUG(update, UPDATE_OUT)) + zlog_debug("Error starting deferral timer for %s", + get_afi_safi_str(afi, safi, false)); + return -1; + } + } + gr_info->eor_required++; + /* Send message to RIB indicating route update pending */ + if (gr_info->af_enabled[afi][safi] == false) { + gr_info->af_enabled[afi][safi] = true; + /* Send message to RIB */ + bgp_zebra_update(afi, safi, bgp->vrf_id, + ZEBRA_CLIENT_ROUTE_UPDATE_PENDING); + } + if (BGP_DEBUG(update, UPDATE_OUT)) + zlog_debug("Started the deferral timer for %s eor_required %d", + get_afi_safi_str(afi, safi, false), + gr_info->eor_required); + return 0; +} + +/* Update the graceful restart information for the specified AFI, SAFI */ +static int bgp_update_gr_info(struct peer *peer, afi_t afi, safi_t safi) +{ + struct graceful_restart_info *gr_info; + struct bgp *bgp = peer->bgp; + int ret = 0; + + if ((afi < AFI_IP) || (afi >= AFI_MAX)) { + if (BGP_DEBUG(update, UPDATE_OUT)) + zlog_debug("%s : invalid afi %d", __func__, afi); + return -1; + } + + if ((safi < SAFI_UNICAST) || (safi > SAFI_MPLS_VPN)) { + if (BGP_DEBUG(update, UPDATE_OUT)) + zlog_debug("%s : invalid safi %d", __func__, safi); + return -1; + } + + /* Restarting router */ + if (BGP_PEER_GRACEFUL_RESTART_CAPABLE(peer) && + BGP_PEER_RESTARTING_MODE(peer)) { + /* Check if the forwarding state is preserved */ + if (bgp_flag_check(bgp, BGP_FLAG_GR_PRESERVE_FWD)) { + gr_info = &(bgp->gr_info[afi][safi]); + ret = bgp_start_deferral_timer(bgp, afi, safi, gr_info); + } + } + return ret; +} + /** * Transition to Established state. * @@ -1587,6 +1758,7 @@ static int bgp_establish(struct peer *peer) int nsf_af_count = 0; int ret = 0; struct peer *other; + int status; other = peer->doppelganger; peer = peer_xfer_conn(peer); @@ -1626,6 +1798,14 @@ static int bgp_establish(struct peer *peer) /* graceful restart */ UNSET_FLAG(peer->sflags, PEER_STATUS_NSF_WAIT); + if (bgp_debug_neighbor_events(peer)) { + if (BGP_PEER_RESTARTING_MODE(peer)) + zlog_debug("peer %s BGP_RESTARTING_MODE", + peer->host); + else if (BGP_PEER_HELPER_MODE(peer)) + zlog_debug("peer %s BGP_HELPER_MODE", + peer->host); + } for (afi = AFI_IP; afi < AFI_MAX; afi++) for (safi = SAFI_UNICAST; safi <= SAFI_MPLS_VPN; safi++) { if (peer->afc_nego[afi][safi] @@ -1645,8 +1825,29 @@ static int bgp_establish(struct peer *peer) bgp_clear_stale_route(peer, afi, safi); peer->nsf[afi][safi] = 0; } + /* Update the graceful restart information */ + if (peer->afc_nego[afi][safi]) { + if (!BGP_SELECT_DEFER_DISABLE(peer->bgp)) { + status = bgp_update_gr_info(peer, afi, + safi); + if (status < 0) + zlog_err("Error in updating graceful restart for %s", + get_afi_safi_str(afi, + safi, false)); + } else { + if (BGP_PEER_GRACEFUL_RESTART_CAPABLE( + peer) && + BGP_PEER_RESTARTING_MODE(peer) + && bgp_flag_check(peer->bgp, + BGP_FLAG_GR_PRESERVE_FWD)) + peer->bgp->gr_info[afi][safi] + .eor_required++; + } + } } + peer->nsf_af_count = nsf_af_count; + if (nsf_af_count) SET_FLAG(peer->sflags, PEER_STATUS_NSF_MODE); else { @@ -2063,3 +2264,498 @@ int bgp_event_update(struct peer *peer, int event) return ret; } +/* BGP GR Code */ + +int bgp_gr_lookup_n_update_all_peer(struct bgp *bgp, + enum global_mode global_new_state, + enum global_mode global_old_state) +{ + struct peer *peer = {0}; + struct listnode *node = {0}; + struct listnode *nnode = {0}; + enum peer_mode peer_old_state = PEER_INVALID; + + for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) { + + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug( + "%s [BGP_GR] Peer: (%s) :", + __func__, peer->host); + + peer_old_state = bgp_peer_gr_mode_get(peer); + + if (peer_old_state == PEER_GLOBAL_INHERIT) { + + /* + *Reset only these peers and send a + *new open message with the change capabilities. + *Considering the mode to be "global_new_state" and + *do all operation accordingly + */ + + switch (global_new_state) { + case GLOBAL_HELPER: + BGP_PEER_GR_HELPER_ENABLE(peer); + break; + case GLOBAL_GR: + BGP_PEER_GR_ENABLE(peer); + break; + case GLOBAL_DISABLE: + BGP_PEER_GR_DISABLE(peer); + break; + case GLOBAL_INVALID: + zlog_debug( + "%s [BGP_GR] GLOBAL_INVALID", + __func__); + return BGP_ERR_GR_OPERATION_FAILED; + default: + zlog_debug( + "%s [BGP_GR] Global unknown ERROR", + __func__); + return BGP_ERR_GR_OPERATION_FAILED; + } + } + } + + bgp->global_gr_present_state = global_new_state; + + return BGP_GR_SUCCESS; +} + +int bgp_gr_update_all(struct bgp *bgp, int global_gr_cmd) +{ + enum global_mode global_new_state = GLOBAL_INVALID; + enum global_mode global_old_state = GLOBAL_INVALID; + + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug( + "%s [BGP_GR]START: global_gr_cmd :%s:", + __func__, print_global_gr_cmd(global_gr_cmd)); + + global_old_state = bgp_global_gr_mode_get(bgp); + + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug( + "[BGP_GR] global_old_gr_state :%s:", + print_global_gr_mode(global_old_state)); + + if (global_old_state != GLOBAL_INVALID) { + global_new_state = + bgp->GLOBAL_GR_FSM[global_old_state][global_gr_cmd]; + + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug( + "[BGP_GR] global_new_gr_state :%s:", + print_global_gr_mode(global_new_state)); + } else { + zlog_err( + "%s [BGP_GR] global_old_state == GLOBAL_INVALID", + __func__); + return BGP_ERR_GR_OPERATION_FAILED; + } + + if (global_new_state == GLOBAL_INVALID) { + zlog_err( + "%s [BGP_GR] global_new_state == GLOBAL_INVALID", + __func__); + return BGP_ERR_GR_INVALID_CMD; + } + if (global_new_state == global_old_state) { + /* Trace msg */ + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug( + "%s [BGP_GR] global_new_state == global_old_state :%s", + __func__, print_global_gr_mode(global_new_state)); + return BGP_GR_NO_OPERATION; + } + + return bgp_gr_lookup_n_update_all_peer(bgp, + global_new_state, + global_old_state); +} + +const char *print_peer_gr_mode(enum peer_mode pr_mode) +{ + const char *peer_gr_mode = NULL; + + switch (pr_mode) { + case PEER_HELPER: + peer_gr_mode = "PEER_HELPER"; + break; + case PEER_GR: + peer_gr_mode = "PEER_GR"; + break; + case PEER_DISABLE: + peer_gr_mode = "PEER_DISABLE"; + break; + case PEER_INVALID: + peer_gr_mode = "PEER_INVALID"; + break; + case PEER_GLOBAL_INHERIT: + peer_gr_mode = "PEER_GLOBAL_INHERIT"; + break; + } + + return peer_gr_mode; +} + +const char *print_peer_gr_cmd(enum peer_gr_command pr_gr_cmd) +{ + const char *peer_gr_cmd = NULL; + + switch (pr_gr_cmd) { + case PEER_GR_CMD: + peer_gr_cmd = "PEER_GR_CMD"; + break; + case NO_PEER_GR_CMD: + peer_gr_cmd = "NO_PEER_GR_CMD"; + break; + case PEER_DISABLE_CMD: + peer_gr_cmd = "PEER_GR_CMD"; + break; + case NO_PEER_DISABLE_CMD: + peer_gr_cmd = "NO_PEER_GR_CMD"; + break; + case PEER_HELPER_CMD: + peer_gr_cmd = "PEER_HELPER_CMD"; + break; + case NO_PEER_HELPER_CMD: + peer_gr_cmd = "NO_PEER_HELPER_CMD"; + break; + } + + return peer_gr_cmd; +} + +const char *print_global_gr_mode(enum global_mode gl_mode) +{ + const char *global_gr_mode = NULL; + + switch (gl_mode) { + case GLOBAL_HELPER: + global_gr_mode = "GLOBAL_HELPER"; + break; + case GLOBAL_GR: + global_gr_mode = "GLOBAL_GR"; + break; + case GLOBAL_DISABLE: + global_gr_mode = "GLOBAL_DISABLE"; + break; + case GLOBAL_INVALID: + global_gr_mode = "GLOBAL_INVALID"; + break; + } + + return global_gr_mode; +} + +const char *print_global_gr_cmd(enum global_gr_command gl_gr_cmd) +{ + const char *global_gr_cmd = NULL; + + switch (gl_gr_cmd) { + case GLOBAL_GR_CMD: + global_gr_cmd = "GLOBAL_GR_CMD"; + break; + case NO_GLOBAL_GR_CMD: + global_gr_cmd = "NO_GLOBAL_GR_CMD"; + break; + case GLOBAL_DISABLE_CMD: + global_gr_cmd = "GLOBAL_DISABLE_CMD"; + break; + case NO_GLOBAL_DISABLE_CMD: + global_gr_cmd = "NO_GLOBAL_DISABLE_CMD"; + break; + } + + return global_gr_cmd; +} + +enum global_mode bgp_global_gr_mode_get(struct bgp *bgp) +{ + return bgp->global_gr_present_state; +} + +enum peer_mode bgp_peer_gr_mode_get(struct peer *peer) +{ + return peer->peer_gr_present_state; +} + +int bgp_neighbor_graceful_restart(struct peer *peer, + int peer_gr_cmd) +{ + enum peer_mode peer_new_state = PEER_INVALID; + enum peer_mode peer_old_state = PEER_INVALID; + struct bgp_peer_gr peer_state; + int result = BGP_GR_FAILURE; + + /* + * fetch peer_old_state from peer structure also + * fetch global_old_state from bgp structure, + * peer had a back pointer to bgpo struct ; + */ + + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug( + "%s [BGP_GR] START:Peer: (%s) : peer_gr_cmd :%s:", + __func__, peer->host, print_peer_gr_cmd(peer_gr_cmd)); + + peer_old_state = bgp_peer_gr_mode_get(peer); + + if (peer_old_state == PEER_INVALID) { + zlog_debug( + "[BGP_GR] peer_old_state == Invalid state !"); + return BGP_ERR_GR_OPERATION_FAILED; + } + + peer_state = peer->PEER_GR_FSM[peer_old_state][peer_gr_cmd]; + peer_new_state = peer_state.next_state; + + if (peer_new_state == PEER_INVALID) { + zlog_debug( + "[BGP_GR] Invalid bgp graceful restart command used !"); + return BGP_ERR_GR_INVALID_CMD; + } + + if (peer_new_state != peer_old_state) { + result = peer_state.action_fun(peer, + peer_old_state, + peer_new_state); + } else { + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug( + "[BGP_GR] peer_old_state == peer_new_state !"); + return BGP_GR_NO_OPERATION; + } + + if (result == BGP_GR_SUCCESS) { + + /* Update the mode i.e peer_new_state into the peer structure */ + peer->peer_gr_present_state = peer_new_state; + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug("[BGP_GR] Succesfully change the state of the peer to : %s : !", + print_peer_gr_mode(peer_new_state)); + + return BGP_GR_SUCCESS; + } + + return result; +} + +unsigned int bgp_peer_gr_action(struct peer *peer, + int old_peer_state, int new_peer_state) +{ + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug( + "%s [BGP_GR] Move peer from old_peer_state :%s: to new_peer_state :%s: !!!!", + __func__, print_peer_gr_mode(old_peer_state), + print_peer_gr_mode(new_peer_state)); + + int bgp_gr_global_mode = GLOBAL_INVALID; + unsigned int ret = BGP_GR_FAILURE; + + if (old_peer_state == new_peer_state) { + /* Nothing to do over here as the present and old state is the same */ + return BGP_GR_NO_OPERATION; + } + if ((old_peer_state == PEER_INVALID) || + (new_peer_state == PEER_INVALID)) { + /* something bad happend , print error message */ + return BGP_ERR_GR_INVALID_CMD; + } + + bgp_gr_global_mode = bgp_global_gr_mode_get(peer->bgp); + + if ((old_peer_state == PEER_GLOBAL_INHERIT) && + (new_peer_state != PEER_GLOBAL_INHERIT)) { + + /* fetch the Mode running in the Global state machine + *from the bgp structure into a variable called + *bgp_gr_global_mode + */ + + /* Here we are checking if the + *1. peer_new_state == global_mode == helper_mode + *2. peer_new_state == global_mode == GR_mode + *3. peer_new_state == global_mode == disabled_mode + */ + + BGP_PEER_GR_GLOBAL_INHERIT_UNSET(peer); + + if (new_peer_state == bgp_gr_global_mode) { + /*This is incremental updates i.e no tear down + *of the existing session + *as the peer is already working in the same mode. + */ + ret = BGP_GR_SUCCESS; + } else { + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug( + "[BGP_GR] Peer state changed from :%s ", + print_peer_gr_mode(old_peer_state)); + + bgp_peer_move_to_gr_mode(peer, new_peer_state); + + ret = BGP_GR_SUCCESS; + } + } + /* In the case below peer is going into Global inherit mode i.e. + * the peer would work as the mode configured at the global level + */ + else if ((new_peer_state == PEER_GLOBAL_INHERIT) && + (old_peer_state != PEER_GLOBAL_INHERIT)) { + /* Here in this case it would be destructive + * in all the cases except one case when, + * Global GR is configured Disabled + * and present_peer_state is not disable + */ + + BGP_PEER_GR_GLOBAL_INHERIT_SET(peer); + + if (old_peer_state == bgp_gr_global_mode) { + + /* This is incremental updates + *i.e no tear down of the existing session + *as the peer is already working in the same mode. + */ + ret = BGP_GR_SUCCESS; + } else { + /* Destructive always */ + /* Tear down the old session + * and send the new capability + * as per the bgp_gr_global_mode + */ + + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug("[BGP_GR] Peer state changed from :%s", + print_peer_gr_mode(old_peer_state)); + + bgp_peer_move_to_gr_mode(peer, bgp_gr_global_mode); + + ret = BGP_GR_SUCCESS; + } + } else { + /* + *This else case, it include all the cases except --> + *(new_peer_state != Peer_Global) && + *( old_peer_state != Peer_Global ) + */ + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug("[BGP_GR] Peer state changed from :%s", + print_peer_gr_mode(old_peer_state)); + + bgp_peer_move_to_gr_mode(peer, new_peer_state); + + ret = BGP_GR_SUCCESS; + } + + return ret; +} + +inline void bgp_peer_move_to_gr_mode(struct peer *peer, int new_state) + +{ + int bgp_global_gr_mode = bgp_global_gr_mode_get(peer->bgp); + + switch (new_state) { + case PEER_HELPER: + BGP_PEER_GR_HELPER_ENABLE(peer); + break; + case PEER_GR: + BGP_PEER_GR_ENABLE(peer); + break; + case PEER_DISABLE: + BGP_PEER_GR_DISABLE(peer); + break; + case PEER_GLOBAL_INHERIT: + BGP_PEER_GR_GLOBAL_INHERIT_SET(peer); + + if (bgp_global_gr_mode == GLOBAL_HELPER) { + BGP_PEER_GR_HELPER_ENABLE(peer); + } else if (bgp_global_gr_mode == GLOBAL_GR) { + BGP_PEER_GR_ENABLE(peer); + } else if (bgp_global_gr_mode == GLOBAL_DISABLE) { + BGP_PEER_GR_DISABLE(peer); + } else { + zlog_err( + "[BGP_GR] Default switch inherit mode ::: SOMETHING IS WRONG !!!"); + } + break; + default: + zlog_err("[BGP_GR] Default switch mode ::: SOMETHING IS WRONG !!!"); + break; + } + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug("[BGP_GR] Peer state changed --to--> : %d : !", + new_state); +} + +void bgp_peer_gr_flags_update(struct peer *peer) +{ + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug( + "%s [BGP_GR] called !", + __func__); + if (CHECK_FLAG(peer->peer_gr_new_status_flag, + PEER_GRACEFUL_RESTART_NEW_STATE_HELPER)) + SET_FLAG(peer->flags, + PEER_FLAG_GRACEFUL_RESTART_HELPER); + else + UNSET_FLAG(peer->flags, + PEER_FLAG_GRACEFUL_RESTART_HELPER); + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug( + "[BGP_GR] Peer %s Flag PEER_FLAG_GRACEFUL_RESTART_HELPER : %s : !", + peer->host, + (CHECK_FLAG(peer->flags, + PEER_FLAG_GRACEFUL_RESTART_HELPER) ? + "Set" : "UnSet")); + if (CHECK_FLAG(peer->peer_gr_new_status_flag, + PEER_GRACEFUL_RESTART_NEW_STATE_RESTART)) + SET_FLAG(peer->flags, + PEER_FLAG_GRACEFUL_RESTART); + else + UNSET_FLAG(peer->flags, + PEER_FLAG_GRACEFUL_RESTART); + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug( + "[BGP_GR] Peer %s Flag PEER_FLAG_GRACEFUL_RESTART : %s : !", + peer->host, + (CHECK_FLAG(peer->flags, + PEER_FLAG_GRACEFUL_RESTART) ? + "Set" : "UnSet")); + if (CHECK_FLAG(peer->peer_gr_new_status_flag, + PEER_GRACEFUL_RESTART_NEW_STATE_INHERIT)) + SET_FLAG(peer->flags, + PEER_FLAG_GRACEFUL_RESTART_GLOBAL_INHERIT); + else + UNSET_FLAG(peer->flags, + PEER_FLAG_GRACEFUL_RESTART_GLOBAL_INHERIT); + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug( + "[BGP_GR] Peer %s Flag PEER_FLAG_GRACEFUL_RESTART_GLOBAL_INHERIT : %s : !", + peer->host, + (CHECK_FLAG(peer->flags, + PEER_FLAG_GRACEFUL_RESTART_GLOBAL_INHERIT) ? + "Set" : "UnSet")); + + if (!CHECK_FLAG(peer->flags, + PEER_FLAG_GRACEFUL_RESTART) && + !CHECK_FLAG(peer->flags, + PEER_FLAG_GRACEFUL_RESTART_HELPER)){ + zlog_debug( + "[BGP_GR] Peer %s UNSET PEER_STATUS_NSF_MODE!", + peer->host); + + UNSET_FLAG(peer->sflags, PEER_STATUS_NSF_MODE); + + if (CHECK_FLAG(peer->sflags, + PEER_STATUS_NSF_WAIT)) { + + peer_nsf_stop(peer); + zlog_debug( + "[BGP_GR] Peer %s UNSET PEER_STATUS_NSF_WAIT!", + peer->host); + } + } +} diff --git a/bgpd/bgp_fsm.h b/bgpd/bgp_fsm.h index 6f955c71be..b6abaf3e92 100644 --- a/bgpd/bgp_fsm.h +++ b/bgpd/bgp_fsm.h @@ -56,6 +56,58 @@ #define FSM_PEER_TRANSFERRED 2 #define FSM_PEER_TRANSITIONED 3 +#define BGP_PEER_GR_HELPER_ENABLE(peer) \ + do { \ + UNSET_FLAG( \ + peer->peer_gr_new_status_flag, \ + PEER_GRACEFUL_RESTART_NEW_STATE_RESTART); \ + SET_FLAG( \ + peer->peer_gr_new_status_flag, \ + PEER_GRACEFUL_RESTART_NEW_STATE_HELPER);\ + } while (0) + +#define BGP_PEER_GR_ENABLE(peer)\ + do { \ + SET_FLAG( \ + peer->peer_gr_new_status_flag, \ + PEER_GRACEFUL_RESTART_NEW_STATE_RESTART); \ + UNSET_FLAG( \ + peer->peer_gr_new_status_flag, \ + PEER_GRACEFUL_RESTART_NEW_STATE_HELPER);\ + } while (0) + +#define BGP_PEER_GR_DISABLE(peer)\ + do { \ + UNSET_FLAG( \ + peer->peer_gr_new_status_flag, \ + PEER_GRACEFUL_RESTART_NEW_STATE_RESTART);\ + UNSET_FLAG(\ + peer->peer_gr_new_status_flag, \ + PEER_GRACEFUL_RESTART_NEW_STATE_HELPER);\ + } while (0) + +#define BGP_PEER_GR_GLOBAL_INHERIT_SET(peer) \ + SET_FLAG(peer->peer_gr_new_status_flag, \ + PEER_GRACEFUL_RESTART_NEW_STATE_INHERIT) + +#define BGP_PEER_GR_GLOBAL_INHERIT_UNSET(peer) \ + UNSET_FLAG(peer->peer_gr_new_status_flag, \ + PEER_GRACEFUL_RESTART_NEW_STATE_INHERIT) + +#define BGP_PEER_GRACEFUL_RESTART_CAPABLE(peer) \ + (CHECK_FLAG(peer->cap, PEER_CAP_RESTART_ADV) && \ + CHECK_FLAG(peer->cap, PEER_CAP_RESTART_RCV)) + +#define BGP_PEER_RESTARTING_MODE(peer)\ + (CHECK_FLAG(peer->flags, PEER_FLAG_GRACEFUL_RESTART) && \ + CHECK_FLAG(peer->cap, PEER_CAP_RESTART_BIT_ADV) && \ + !CHECK_FLAG(peer->cap, PEER_CAP_RESTART_BIT_RCV)) + +#define BGP_PEER_HELPER_MODE(peer)\ + (CHECK_FLAG(peer->flags, PEER_FLAG_GRACEFUL_RESTART_HELPER) && \ + CHECK_FLAG(peer->cap, PEER_CAP_RESTART_BIT_RCV) && \ + !CHECK_FLAG(peer->cap, PEER_CAP_RESTART_BIT_ADV)) + /* Prototypes. */ extern void bgp_fsm_event_update(struct peer *peer, int valid); extern int bgp_event(struct thread *); @@ -87,6 +139,29 @@ extern void bgp_start_routeadv(struct bgp *); extern void bgp_adjust_routeadv(struct peer *); #include "hook.h" -DECLARE_HOOK(peer_backward_transition, (struct peer * peer), (peer)) +DECLARE_HOOK(peer_backward_transition, (struct peer *peer), (peer)) +DECLARE_HOOK(peer_established, (struct peer *peer), (peer)) +int bgp_gr_update_all(struct bgp *bgp, int global_gr_cmd); +int bgp_neighbor_graceful_restart(struct peer *peer, + int peer_gr_cmd); +unsigned int bgp_peer_gr_action(struct peer *peer, + int old_peer_state, int new_peer_state); +void bgp_peer_move_to_gr_mode(struct peer *peer, int new_state); +unsigned int bgp_peer_gr_helper_enable(struct peer *peer); +unsigned int bgp_peer_gr_enable(struct peer *peer); +unsigned int bgp_peer_gr_global_inherit(struct peer *peer); +unsigned int bgp_peer_gr_disable(struct peer *peer); +enum peer_mode bgp_peer_gr_mode_get(struct peer *peer); +enum global_mode bgp_global_gr_mode_get(struct bgp *bgp); +enum peer_mode bgp_get_peer_gr_mode_from_flags(struct peer *peer); +unsigned int bgp_peer_gr_global_inherit_unset(struct peer *peer); +int bgp_gr_lookup_n_update_all_peer(struct bgp *bgp, + enum global_mode global_new_state, + enum global_mode global_old_state); +void bgp_peer_gr_flags_update(struct peer *peer); +const char *print_peer_gr_mode(enum peer_mode pr_mode); +const char *print_peer_gr_cmd(enum peer_gr_command pr_gr_cmd); +const char *print_global_gr_mode(enum global_mode gl_mode); +const char *print_global_gr_cmd(enum global_gr_command gl_gr_cmd); #endif /* _QUAGGA_BGP_FSM_H */ diff --git a/bgpd/bgp_io.c b/bgpd/bgp_io.c index fed34e5b65..626c36ff05 100644 --- a/bgpd/bgp_io.c +++ b/bgpd/bgp_io.c @@ -462,7 +462,12 @@ static uint16_t bgp_read(struct peer *peer) safe_strerror(errno)); if (peer->status == Established) { - if (CHECK_FLAG(peer->sflags, PEER_STATUS_NSF_MODE)) { + if ((CHECK_FLAG(peer->flags, + PEER_FLAG_GRACEFUL_RESTART) || + CHECK_FLAG(peer->flags, + PEER_FLAG_GRACEFUL_RESTART_HELPER)) && + CHECK_FLAG(peer->sflags, + PEER_STATUS_NSF_MODE)) { peer->last_reset = PEER_DOWN_NSF_CLOSE_SESSION; SET_FLAG(peer->sflags, PEER_STATUS_NSF_WAIT); } else @@ -475,10 +480,15 @@ static uint16_t bgp_read(struct peer *peer) } else if (nbytes == 0) { if (bgp_debug_neighbor_events(peer)) zlog_debug("%s [Event] BGP connection closed fd %d", - peer->host, peer->fd); + peer->host, peer->fd); if (peer->status == Established) { - if (CHECK_FLAG(peer->sflags, PEER_STATUS_NSF_MODE)) { + if ((CHECK_FLAG(peer->flags, + PEER_FLAG_GRACEFUL_RESTART) || + CHECK_FLAG(peer->flags, + PEER_FLAG_GRACEFUL_RESTART_HELPER)) && + CHECK_FLAG(peer->sflags, + PEER_STATUS_NSF_MODE)) { peer->last_reset = PEER_DOWN_NSF_CLOSE_SESSION; SET_FLAG(peer->sflags, PEER_STATUS_NSF_WAIT); } else diff --git a/bgpd/bgp_main.c b/bgpd/bgp_main.c index 9cb3957a86..fab2a584c0 100644 --- a/bgpd/bgp_main.c +++ b/bgpd/bgp_main.c @@ -487,6 +487,7 @@ int main(int argc, char **argv) frr_config_fork(); /* must be called after fork() */ + bgp_gr_apply_running_config(); bgp_pthreads_run(); frr_run(bm->master); diff --git a/bgpd/bgp_network.c b/bgpd/bgp_network.c index 4031d2dfde..d7989af553 100644 --- a/bgpd/bgp_network.c +++ b/bgpd/bgp_network.c @@ -487,6 +487,22 @@ static int bgp_accept(struct thread *thread) hash_get(peer->bgp->peerhash, peer, hash_alloc_intern); peer_xfer_config(peer, peer1); + bgp_peer_gr_flags_update(peer); + + BGP_GR_ROUTER_DETECT_AND_SEND_CAPABILITY_TO_ZEBRA( + peer->bgp, + peer->bgp->peer); + + if (bgp_peer_gr_mode_get(peer) == PEER_DISABLE) { + + UNSET_FLAG(peer->sflags, PEER_STATUS_NSF_MODE); + + if (CHECK_FLAG(peer->sflags, + PEER_STATUS_NSF_WAIT)) { + peer_nsf_stop(peer); + } + } + UNSET_FLAG(peer->flags, PEER_FLAG_CONFIG_NODE); peer->doppelganger = peer1; @@ -497,10 +513,9 @@ static int bgp_accept(struct thread *thread) BGP_TIMER_OFF(peer->t_start); /* created in peer_create() */ SET_FLAG(peer->sflags, PEER_STATUS_ACCEPT_PEER); - /* Make dummy peer until read Open packet. */ if (peer1->status == Established - && CHECK_FLAG(peer1->sflags, PEER_STATUS_NSF_MODE)) { + && CHECK_FLAG(peer1->sflags, PEER_STATUS_NSF_MODE)) { /* If we have an existing established connection with graceful * restart * capability announced with one or more address families, then @@ -508,7 +523,14 @@ static int bgp_accept(struct thread *thread) * existing established connection and move state to connect. */ peer1->last_reset = PEER_DOWN_NSF_CLOSE_SESSION; - SET_FLAG(peer1->sflags, PEER_STATUS_NSF_WAIT); + + if (CHECK_FLAG(peer1->flags, + PEER_FLAG_GRACEFUL_RESTART) || + CHECK_FLAG(peer1->flags, + PEER_FLAG_GRACEFUL_RESTART_HELPER)) + SET_FLAG(peer1->sflags, + PEER_STATUS_NSF_WAIT); + bgp_event_update(peer1, TCP_connection_closed); } diff --git a/bgpd/bgp_open.c b/bgpd/bgp_open.c index 23b893c1c8..e743fdda8f 100644 --- a/bgpd/bgp_open.c +++ b/bgpd/bgp_open.c @@ -462,6 +462,8 @@ static int bgp_capability_restart(struct peer *peer, restart_flag_time = stream_getw(s); if (CHECK_FLAG(restart_flag_time, RESTART_R_BIT)) SET_FLAG(peer->cap, PEER_CAP_RESTART_BIT_RCV); + else + UNSET_FLAG(peer->cap, PEER_CAP_RESTART_BIT_RCV); UNSET_FLAG(restart_flag_time, 0xF000); peer->v_gr_restart = restart_flag_time; @@ -828,6 +830,7 @@ static int bgp_capability_parse(struct peer *peer, size_t length, int ret; struct stream *s = BGP_INPUT(peer); size_t end = stream_get_getp(s) + length; + uint16_t restart_flag_time = 0; assert(STREAM_READABLE(s) >= length); @@ -1004,6 +1007,12 @@ static int bgp_capability_parse(struct peer *peer, size_t length, caphdr.length); stream_set_getp(s, start + caphdr.length); } + + if (!CHECK_FLAG(peer->cap, PEER_CAP_RESTART_RCV)) { + UNSET_FLAG(restart_flag_time, 0xF000); + peer->v_gr_restart = restart_flag_time; + } + } return 0; } @@ -1299,6 +1308,96 @@ static void bgp_open_capability_orf(struct stream *s, struct peer *peer, stream_putc_at(s, capp, cap_len); } +static void bgp_peer_send_gr_capability(struct stream *s, struct peer *peer, + unsigned long cp) +{ + int len; + iana_afi_t pkt_afi; + afi_t afi; + safi_t safi; + iana_safi_t pkt_safi; + uint32_t restart_time; + unsigned long capp = 0; + unsigned long rcapp = 0; + + if ((CHECK_FLAG(peer->flags, + PEER_FLAG_GRACEFUL_RESTART)) || + (CHECK_FLAG(peer->flags, + PEER_FLAG_GRACEFUL_RESTART_HELPER))) { + + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug( + "[BGP_GR] Sending helper Capability for Peer :%s :", + peer->host); + + SET_FLAG(peer->cap, PEER_CAP_RESTART_ADV); + stream_putc(s, BGP_OPEN_OPT_CAP); + capp = stream_get_endp(s); /* Set Capability Len Pointer */ + stream_putc(s, 0); /* Capability Length */ + stream_putc(s, CAPABILITY_CODE_RESTART); + /* Set Restart Capability Len Pointer */ + rcapp = stream_get_endp(s); + stream_putc(s, 0); + restart_time = peer->bgp->restart_time; + if (peer->bgp->t_startup) { + SET_FLAG(restart_time, RESTART_R_BIT); + SET_FLAG(peer->cap, PEER_CAP_RESTART_BIT_ADV); + + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug( + "[BGP_GR] Sending R-Bit for Peer :%s :", + peer->host); + } + + stream_putw(s, restart_time); + + /* Send address-family specific graceful-restart capability + * only when GR config is present + */ + if (CHECK_FLAG(peer->flags, PEER_FLAG_GRACEFUL_RESTART)) { + + if (bgp_flag_check(peer->bgp, + BGP_FLAG_GR_PRESERVE_FWD) && + BGP_DEBUG(graceful_restart, + GRACEFUL_RESTART)) + zlog_debug("[BGP_GR] F bit Set"); + + FOREACH_AFI_SAFI (afi, safi) { + if (peer->afc[afi][safi]) { + if (BGP_DEBUG(graceful_restart, + GRACEFUL_RESTART)) + zlog_debug( + "[BGP_GR] Sending GR Capability for AFI :%d :, SAFI :%d:", + afi, safi); + + /* Convert AFI, SAFI to values for + * packet. + */ + bgp_map_afi_safi_int2iana(afi, + safi, &pkt_afi, + &pkt_safi); + stream_putw(s, pkt_afi); + stream_putc(s, pkt_safi); + if (bgp_flag_check(peer->bgp, + BGP_FLAG_GR_PRESERVE_FWD)) { + stream_putc(s, RESTART_F_BIT); + + } else { + stream_putc(s, 0); + } + } + } + } + /* Total Graceful restart capability Len. */ + len = stream_get_endp(s) - rcapp - 1; + stream_putc_at(s, rcapp, len); + + /* Total Capability Len. */ + len = stream_get_endp(s) - capp - 1; + stream_putc_at(s, capp, len); + } +} + /* Fill in capability open option to the packet. */ void bgp_open_capability(struct stream *s, struct peer *peer) { @@ -1309,7 +1408,6 @@ void bgp_open_capability(struct stream *s, struct peer *peer) safi_t safi; iana_safi_t pkt_safi; as_t local_as; - uint32_t restart_time; uint8_t afi_safi_count = 0; int adv_addpath_tx = 0; @@ -1502,50 +1600,7 @@ void bgp_open_capability(struct stream *s, struct peer *peer) cmd_domainname_get()); } - /* Sending base graceful-restart capability irrespective of the config - */ - SET_FLAG(peer->cap, PEER_CAP_RESTART_ADV); - stream_putc(s, BGP_OPEN_OPT_CAP); - capp = stream_get_endp(s); /* Set Capability Len Pointer */ - stream_putc(s, 0); /* Capability Length */ - stream_putc(s, CAPABILITY_CODE_RESTART); - rcapp = stream_get_endp(s); /* Set Restart Capability Len Pointer */ - stream_putc(s, 0); - restart_time = peer->bgp->restart_time; - if (peer->bgp->t_startup) { - SET_FLAG(restart_time, RESTART_R_BIT); - SET_FLAG(peer->cap, PEER_CAP_RESTART_BIT_ADV); - } - stream_putw(s, restart_time); - - /* Send address-family specific graceful-restart capability only when GR - config - is present */ - if (bgp_flag_check(peer->bgp, BGP_FLAG_GRACEFUL_RESTART)) { - FOREACH_AFI_SAFI (afi, safi) { - if (peer->afc[afi][safi]) { - /* Convert AFI, SAFI to values for - * packet. */ - bgp_map_afi_safi_int2iana(afi, safi, &pkt_afi, - &pkt_safi); - stream_putw(s, pkt_afi); - stream_putc(s, pkt_safi); - if (bgp_flag_check(peer->bgp, - BGP_FLAG_GR_PRESERVE_FWD)) - stream_putc(s, RESTART_F_BIT); - else - stream_putc(s, 0); - } - } - } - - /* Total Graceful restart capability Len. */ - len = stream_get_endp(s) - rcapp - 1; - stream_putc_at(s, rcapp, len); - - /* Total Capability Len. */ - len = stream_get_endp(s) - capp - 1; - stream_putc_at(s, capp, len); + bgp_peer_send_gr_capability(s, peer, cp); /* Total Opt Parm Len. */ len = stream_get_endp(s) - cp - 1; diff --git a/bgpd/bgp_packet.c b/bgpd/bgp_packet.c index b7e9af002b..e77194a624 100644 --- a/bgpd/bgp_packet.c +++ b/bgpd/bgp_packet.c @@ -439,28 +439,41 @@ int bgp_generate_updgrp_packets(struct thread *thread) */ if (!next_pkt || !next_pkt->buffer) { if (CHECK_FLAG(peer->cap, - PEER_CAP_RESTART_RCV)) { + PEER_CAP_RESTART_RCV)) { if (!(PAF_SUBGRP(paf))->t_coalesce - && peer->afc_nego[afi][safi] - && peer->synctime - && !CHECK_FLAG( - peer->af_sflags[afi] - [safi], - PEER_STATUS_EOR_SEND)) { - SET_FLAG(peer->af_sflags[afi] - [safi], - PEER_STATUS_EOR_SEND); - - if ((s = bgp_update_packet_eor( - peer, afi, - safi))) { - bgp_packet_add(peer, s); + && peer->afc_nego[afi][safi] + && peer->synctime + && !CHECK_FLAG( + peer->af_sflags[afi][safi], + PEER_STATUS_EOR_SEND)) { + /* If EOR is disabled, + * the message is not sent + */ + if (BGP_SEND_EOR(peer->bgp, + afi, safi)) { + SET_FLAG( + peer->af_sflags + [afi][safi], + PEER_STATUS_EOR_SEND); + + /* Update EOR + * send time + */ + peer->eor_stime + [afi][safi] = + monotime(NULL); + + BGP_UPDATE_EOR_PKT( + peer, afi, + safi, s); } } } continue; } + /* Update packet send time */ + peer->pkt_stime[afi][safi] = monotime(NULL); /* Found a packet template to send, overwrite * packet with appropriate attributes from peer @@ -723,11 +736,14 @@ void bgp_notify_send_with_data(struct peer *peer, uint8_t code, if (first) { snprintf(c, sizeof(c), " %02x", data[i]); + strlcat(bgp_notify.data, c, bgp_notify.length); + } else { first = 1; snprintf(c, sizeof(c), "%02x", data[i]); + strlcpy(bgp_notify.data, c, bgp_notify.length); } @@ -755,6 +771,11 @@ void bgp_notify_send_with_data(struct peer *peer, uint8_t code, /* Add packet to peer's output queue */ stream_fifo_push(peer->obuf, s); + bgp_peer_gr_flags_update(peer); + BGP_GR_ROUTER_DETECT_AND_SEND_CAPABILITY_TO_ZEBRA( + peer->bgp, + peer->bgp->peer); + bgp_write_notify(peer); } @@ -1404,6 +1425,7 @@ static int bgp_update_receive(struct peer *peer, bgp_size_t size) bgp_size_t attribute_len; bgp_size_t update_len; bgp_size_t withdraw_len; + bool restart = false; enum NLRI_TYPES { NLRI_UPDATE, @@ -1626,6 +1648,12 @@ static int bgp_update_receive(struct peer *peer, bgp_size_t size) || (attr_parse_ret == BGP_ATTR_PARSE_EOR)) { afi_t afi = 0; safi_t safi; + struct graceful_restart_info *gr_info; + + /* Restarting router */ + if (BGP_PEER_GRACEFUL_RESTART_CAPABLE(peer) && + BGP_PEER_RESTARTING_MODE(peer)) + restart = true; /* Non-MP IPv4/Unicast is a completely emtpy UPDATE - already * checked @@ -1652,6 +1680,32 @@ static int bgp_update_receive(struct peer *peer, bgp_size_t size) SET_FLAG(peer->af_sflags[afi][safi], PEER_STATUS_EOR_RECEIVED); bgp_update_explicit_eors(peer); + /* Update graceful restart information */ + gr_info = &(peer->bgp->gr_info[afi][safi]); + if (restart) + gr_info->eor_received++; + /* If EOR received from all peers and selection + * deferral timer is running, cancel the timer + * and invoke the best path calculation + */ + if (gr_info->eor_required == + gr_info->eor_received) { + if (bgp_debug_neighbor_events( + peer)) + zlog_debug("%s %d, %s %d", + "EOR REQ", + gr_info->eor_required, + "EOR RCV", + gr_info->eor_received); + BGP_TIMER_OFF( + gr_info->t_select_deferral); + gr_info->eor_required = 0; + gr_info->eor_received = 0; + /* Best path selection */ + if (bgp_best_path_select_defer( + peer->bgp, afi, safi) < 0) + return BGP_Stop; + } } /* NSF delete stale route */ @@ -1723,14 +1777,18 @@ static int bgp_notify_receive(struct peer *peer, bgp_size_t size) if (first) { snprintf(c, sizeof(c), " %02x", stream_getc(peer->curr)); + strlcat(bgp_notify.data, c, - bgp_notify.length); + bgp_notify.length * 3); + } else { first = 1; snprintf(c, sizeof(c), "%02x", stream_getc(peer->curr)); + strlcpy(bgp_notify.data, c, - bgp_notify.length); + bgp_notify.length * 3); + } bgp_notify.raw_data = (uint8_t *)peer->notify.data; } @@ -1756,6 +1814,11 @@ static int bgp_notify_receive(struct peer *peer, bgp_size_t size) && bgp_notify.subcode == BGP_NOTIFY_OPEN_UNSUP_PARAM) UNSET_FLAG(peer->sflags, PEER_STATUS_CAPABILITY_OPEN); + bgp_peer_gr_flags_update(peer); + BGP_GR_ROUTER_DETECT_AND_SEND_CAPABILITY_TO_ZEBRA( + peer->bgp, + peer->bgp->peer); + return Receive_NOTIFICATION_message; } @@ -2374,3 +2437,14 @@ int bgp_process_packet(struct thread *thread) return 0; } + +/* Send EOR when routes are processed by selection deferral timer */ +void bgp_send_delayed_eor(struct bgp *bgp) +{ + struct peer *peer; + struct listnode *node, *nnode; + + /* EOR message sent in bgp_write_proceed_actions */ + for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) + bgp_write_proceed_actions(peer); +} diff --git a/bgpd/bgp_packet.h b/bgpd/bgp_packet.h index 49e401790f..0242abab26 100644 --- a/bgpd/bgp_packet.h +++ b/bgpd/bgp_packet.h @@ -48,6 +48,14 @@ DECLARE_HOOK(bgp_packet_send, #define ORF_COMMON_PART_PERMIT 0x00 #define ORF_COMMON_PART_DENY 0x20 +#define BGP_UPDATE_EOR_PKT(_peer, _afi, _safi, _s) \ + do { \ + _s = bgp_update_packet_eor(_peer, _afi, _safi); \ + if (_s) { \ + bgp_packet_add(_peer, _s); \ + } \ + } while (0) + /* Packet send and receive function prototypes. */ extern void bgp_keepalive_send(struct peer *); extern void bgp_open_send(struct peer *); @@ -73,4 +81,5 @@ extern int bgp_packet_set_size(struct stream *s); extern int bgp_generate_updgrp_packets(struct thread *); extern int bgp_process_packet(struct thread *); +extern void bgp_send_delayed_eor(struct bgp *bgp); #endif /* _QUAGGA_BGP_PACKET_H */ diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index 5f4486b800..f7ace6aede 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -40,7 +40,7 @@ #include "memory.h" #include "lib/json.h" #include "lib_errors.h" - +#include "zclient.h" #include "bgpd/bgpd.h" #include "bgpd/bgp_table.h" #include "bgpd/bgp_route.h" @@ -89,7 +89,8 @@ /* Extern from bgp_dump.c */ extern const char *bgp_origin_str[]; extern const char *bgp_origin_long_str[]; - +const char *get_afi_safi_str(afi_t afi, + safi_t safi, bool for_json); /* PMSI strings. */ #define PMSI_TNLTYPE_STR_NO_INFO "No info" #define PMSI_TNLTYPE_STR_DEFAULT PMSI_TNLTYPE_STR_NO_INFO @@ -295,6 +296,85 @@ struct bgp_path_info *bgp_path_info_unlock(struct bgp_path_info *path) return path; } +/* This function sets flag BGP_NODE_SELECT_DEFER based on condition */ +static int bgp_node_set_defer_flag(struct bgp_node *rn, bool delete) +{ + struct peer *peer; + struct bgp_path_info *old_pi, *nextpi; + bool set_flag = 0; + struct bgp *bgp = NULL; + struct bgp_table *table = NULL; + afi_t afi = 0; + safi_t safi = 0; + char buf[PREFIX2STR_BUFFER]; + + /* If the flag BGP_NODE_SELECT_DEFER is set and new path is added + * then the route selection is deferred + */ + if (CHECK_FLAG(rn->flags, BGP_NODE_SELECT_DEFER) && (!delete)) + return 0; + + if (CHECK_FLAG(rn->flags, BGP_NODE_PROCESS_SCHEDULED)) { + if (BGP_DEBUG(update, UPDATE_OUT)) { + prefix2str(&rn->p, buf, PREFIX2STR_BUFFER); + zlog_debug("Route %s is in workqueue and being processed, not deferred.", + buf); + } + return 0; + } + + table = bgp_node_table(rn); + if (table) { + bgp = table->bgp; + afi = table->afi; + safi = table->safi; + } + + for (old_pi = bgp_node_get_bgp_path_info(rn); + (old_pi != NULL) && (nextpi = old_pi->next, 1); old_pi = nextpi) { + if (CHECK_FLAG(old_pi->flags, BGP_PATH_SELECTED)) + continue; + + /* Route selection is deferred if there is a stale path which + * which indicates peer is in restart mode + */ + if (CHECK_FLAG(old_pi->flags, BGP_PATH_STALE) && + (old_pi->sub_type == BGP_ROUTE_NORMAL)) { + set_flag = 1; + } else { + /* If the peer is graceful restart capable and peer is + * restarting mode, set the flag BGP_NODE_SELECT_DEFER + */ + peer = old_pi->peer; + if (BGP_PEER_GRACEFUL_RESTART_CAPABLE(peer) && + BGP_PEER_RESTARTING_MODE(peer) && + (old_pi && old_pi->sub_type == BGP_ROUTE_NORMAL)) { + set_flag = 1; + } + } + if (set_flag) + break; + } + + /* Set the flag BGP_NODE_SELECT_DEFER if route selection deferral timer + * is active + */ + if (set_flag && table) { + if (bgp && (bgp->gr_info[afi][safi].t_select_deferral)) { + SET_FLAG(rn->flags, BGP_NODE_SELECT_DEFER); + prefix2str(&rn->p, buf, PREFIX2STR_BUFFER); + if (rn->rt_node == NULL) + rn->rt_node = listnode_add( + bgp->gr_info[afi][safi].route_list, rn); + if (BGP_DEBUG(update, UPDATE_OUT)) + zlog_debug("DEFER route %s, rn %p, node %p", + buf, rn, rn->rt_node); + return 0; + } + } + return -1; +} + void bgp_path_info_add(struct bgp_node *rn, struct bgp_path_info *pi) { struct bgp_path_info *top; @@ -310,6 +390,7 @@ void bgp_path_info_add(struct bgp_node *rn, struct bgp_path_info *pi) bgp_path_info_lock(pi); bgp_lock_node(rn); peer_lock(pi->peer); /* bgp_path_info peer reference */ + bgp_node_set_defer_flag(rn, false); } /* Do the actual removal of info from RIB, for use by bgp_process @@ -1973,6 +2054,30 @@ int subgroup_announce_check(struct bgp_node *rn, struct bgp_path_info *pi, return 1; } +static int bgp_route_select_timer_expire(struct thread *thread) +{ + struct afi_safi_info *info; + afi_t afi; + safi_t safi; + struct bgp *bgp; + + info = THREAD_ARG(thread); + afi = info->afi; + safi = info->safi; + bgp = info->bgp; + + if (BGP_DEBUG(update, UPDATE_OUT)) + zlog_debug("afi %d, safi %d : route select timer expired", + afi, safi); + + bgp->gr_info[afi][safi].t_route_select = NULL; + + XFREE(MTYPE_TMP, info); + + /* Best path selection */ + return bgp_best_path_select_defer(bgp, afi, safi); +} + void bgp_best_selection(struct bgp *bgp, struct bgp_node *rn, struct bgp_maxpaths_cfg *mpath_cfg, struct bgp_path_info_pair *result, afi_t afi, @@ -2376,6 +2481,15 @@ static void bgp_process_main_one(struct bgp *bgp, struct bgp_node *rn, afi2str(afi), safi2str(safi)); } + /* The best path calculation for the route is deferred if + * BGP_NODE_SELECT_DEFER is set + */ + if (CHECK_FLAG(rn->flags, BGP_NODE_SELECT_DEFER)) { + if (BGP_DEBUG(update, UPDATE_OUT)) + zlog_debug("SELECT_DEFER falg set for route %p", rn); + return; + } + /* Best path selection. */ bgp_best_selection(bgp, rn, &bgp->maxpaths[afi][safi], &old_and_new, afi, safi); @@ -2603,6 +2717,78 @@ static void bgp_process_main_one(struct bgp *bgp, struct bgp_node *rn, return; } +/* Process the routes with the flag BGP_NODE_SELECT_DEFER set */ +int bgp_best_path_select_defer(struct bgp *bgp, afi_t afi, safi_t safi) +{ + struct bgp_node *rn; + int cnt = 0; + struct afi_safi_info *thread_info; + struct listnode *node = NULL, *nnode = NULL; + + if (bgp->gr_info[afi][safi].t_route_select) + BGP_TIMER_OFF(bgp->gr_info[afi][safi].t_route_select); + + if (BGP_DEBUG(update, UPDATE_OUT)) { + zlog_debug("%s: processing route for %s : cnt %d", + __func__, get_afi_safi_str(afi, safi, false), + listcount(bgp->gr_info[afi][safi].route_list)); + } + + /* Process the route list */ + node = listhead(bgp->gr_info[afi][safi].route_list); + while (node) { + rn = listgetdata(node); + nnode = node->next; + list_delete_node(bgp->gr_info[afi][safi].route_list, node); + rn->rt_node = NULL; + + if (CHECK_FLAG(rn->flags, BGP_NODE_SELECT_DEFER)) { + UNSET_FLAG(rn->flags, BGP_NODE_SELECT_DEFER); + bgp_process_main_one(bgp, rn, afi, safi); + cnt++; + if (cnt >= BGP_MAX_BEST_ROUTE_SELECT) + break; + } + node = nnode; + } + + /* Send EOR message when all routes are processed */ + if (list_isempty(bgp->gr_info[afi][safi].route_list)) { + bgp_send_delayed_eor(bgp); + /* Send route processing complete message to RIB */ + bgp_zebra_update(afi, safi, bgp->vrf_id, + ZEBRA_CLIENT_ROUTE_UPDATE_COMPLETE); + return 0; + } + + thread_info = XMALLOC(MTYPE_TMP, sizeof(struct afi_safi_info)); + if (thread_info == NULL) { + if (BGP_DEBUG(update, UPDATE_OUT)) + zlog_debug("%s : error allocating thread info", + __func__); + return -1; + } + + thread_info->afi = afi; + thread_info->safi = safi; + thread_info->bgp = bgp; + + /* If there are more routes to be processed, start the + * selection timer + */ + thread_add_timer(bm->master, bgp_route_select_timer_expire, thread_info, + BGP_ROUTE_SELECT_DELAY, + &bgp->gr_info[afi][safi].t_route_select); + if (bgp->gr_info[afi][safi].t_route_select == NULL) { + if (BGP_DEBUG(update, UPDATE_OUT)) + zlog_debug("%s : error starting selection thread for %s", + __func__, get_afi_safi_str(afi, + safi, false)); + return -1; + } + return 0; +} + static wq_item_status bgp_process_wq(struct work_queue *wq, void *data) { struct bgp_process_queue *pqnode = data; @@ -2681,6 +2867,16 @@ void bgp_process(struct bgp *bgp, struct bgp_node *rn, afi_t afi, safi_t safi) if (CHECK_FLAG(rn->flags, BGP_NODE_PROCESS_SCHEDULED)) return; + /* If the flag BGP_NODE_SELECT_DEFER is set, do not add route to + * the workqueue + */ + if (CHECK_FLAG(rn->flags, BGP_NODE_SELECT_DEFER)) { + if (BGP_DEBUG(update, UPDATE_OUT)) + zlog_debug("BGP_NODE_SELECT_DEFER set for route %p", + rn); + return; + } + if (wq == NULL) return; @@ -2844,13 +3040,41 @@ int bgp_maximum_prefix_overflow(struct peer *peer, afi_t afi, safi_t safi, void bgp_rib_remove(struct bgp_node *rn, struct bgp_path_info *pi, struct peer *peer, afi_t afi, safi_t safi) { + + struct bgp *bgp = NULL; + bool delete_route = false; + bgp_aggregate_decrement(peer->bgp, &rn->p, pi, afi, safi); - if (!CHECK_FLAG(pi->flags, BGP_PATH_HISTORY)) + if (!CHECK_FLAG(pi->flags, BGP_PATH_HISTORY)) { bgp_path_info_delete(rn, pi); /* keep historical info */ - hook_call(bgp_process, peer->bgp, afi, safi, rn, peer, true); + /* If the selected path is removed, reset BGP_NODE_SELECT_DEFER + * flag + */ + if (CHECK_FLAG(pi->flags, BGP_PATH_SELECTED)) + delete_route = true; + else + if (bgp_node_set_defer_flag(rn, true) < 0) + delete_route = true; + if (delete_route) { + if (CHECK_FLAG(rn->flags, BGP_NODE_SELECT_DEFER)) { + UNSET_FLAG(rn->flags, BGP_NODE_SELECT_DEFER); + bgp = pi->peer->bgp; + if ((rn->rt_node) && + (bgp->gr_info[afi][safi] + .route_list)) { + list_delete_node( + bgp->gr_info[afi][safi] + .route_list, + rn->rt_node); + rn->rt_node = NULL; + } + } + } + } + hook_call(bgp_process, peer->bgp, afi, safi, rn, peer, true); bgp_process(peer->bgp, rn, afi, safi); } @@ -3302,6 +3526,7 @@ int bgp_update(struct peer *peer, struct prefix *p, uint32_t addpath_id, if (CHECK_FLAG(pi->flags, BGP_PATH_STALE)) { bgp_path_info_unset_flag( rn, pi, BGP_PATH_STALE); + bgp_node_set_defer_flag(rn, false); bgp_process(bgp, rn, afi, safi); } } @@ -3337,8 +3562,10 @@ int bgp_update(struct peer *peer, struct prefix *p, uint32_t addpath_id, } /* graceful restart STALE flag unset. */ - if (CHECK_FLAG(pi->flags, BGP_PATH_STALE)) + if (CHECK_FLAG(pi->flags, BGP_PATH_STALE)) { bgp_path_info_unset_flag(rn, pi, BGP_PATH_STALE); + bgp_node_set_defer_flag(rn, false); + } /* The attribute is changed. */ bgp_path_info_set_flag(rn, pi, BGP_PATH_ATTR_CHANGED); diff --git a/bgpd/bgp_route.h b/bgpd/bgp_route.h index b9f3f3f762..e335c39fb1 100644 --- a/bgpd/bgp_route.h +++ b/bgpd/bgp_route.h @@ -635,4 +635,5 @@ extern int bgp_show_table_rd(struct vty *vty, struct bgp *bgp, safi_t safi, struct bgp_table *table, struct prefix_rd *prd, enum bgp_show_type type, void *output_arg, bool use_json); +extern int bgp_best_path_select_defer(struct bgp *bgp, afi_t afi, safi_t safi); #endif /* _QUAGGA_BGP_ROUTE_H */ diff --git a/bgpd/bgp_table.c b/bgpd/bgp_table.c index b75246b172..4868686305 100644 --- a/bgpd/bgp_table.c +++ b/bgpd/bgp_table.c @@ -127,6 +127,43 @@ struct bgp_table *bgp_table_init(struct bgp *bgp, afi_t afi, safi_t safi) return rt; } +/* Delete the route node from the selection deferral route list */ +void bgp_delete_listnode(struct bgp_node *node) +{ + struct route_node *rn = NULL; + struct bgp_table *table = NULL; + struct bgp *bgp = NULL; + afi_t afi; + safi_t safi; + + /* If the route to be deleted is selection pending, update the + * route node in gr_info + */ + if (CHECK_FLAG(node->flags, BGP_NODE_SELECT_DEFER)) { + table = bgp_node_table(node); + + if (table) { + bgp = table->bgp; + afi = table->afi; + safi = table->safi; + } else + return; + + rn = bgp_node_to_rnode(node); + + if (bgp && rn && rn->lock == 1) { + /* Delete the route from the selection pending list */ + if ((node->rt_node) && + (bgp->gr_info[afi][safi].route_list)) { + list_delete_node( + bgp->gr_info[afi][safi].route_list, + node->rt_node); + node->rt_node = NULL; + } + } + } +} + static struct bgp_node * bgp_route_next_until_maxlen(struct bgp_node *node, const struct bgp_node *limit, const uint8_t maxlen) diff --git a/bgpd/bgp_table.h b/bgpd/bgp_table.h index b3542e7848..69cca9eee4 100644 --- a/bgpd/bgp_table.h +++ b/bgpd/bgp_table.h @@ -28,6 +28,8 @@ #include "bgpd.h" #include "bgp_advertise.h" +extern void bgp_delete_listnode(struct bgp_node *node); + struct bgp_table { /* table belongs to this instance */ struct bgp *bgp; @@ -95,7 +97,9 @@ struct bgp_node { #define BGP_NODE_USER_CLEAR (1 << 1) #define BGP_NODE_LABEL_CHANGED (1 << 2) #define BGP_NODE_REGISTERED_FOR_LABEL (1 << 3) - +#define BGP_NODE_SELECT_DEFER (1 << 4) + /* list node pointer */ + struct listnode *rt_node; struct bgp_addpath_node_data tx_addpath; enum bgp_path_selection_reason reason; @@ -162,6 +166,7 @@ static inline struct bgp_node *bgp_node_parent_nolock(struct bgp_node *node) */ static inline void bgp_unlock_node(struct bgp_node *node) { + bgp_delete_listnode(node); route_unlock_node(bgp_node_to_rnode(node)); } diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c index 9dc6549d9c..eff5b3b7bc 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -105,9 +105,39 @@ DEFINE_HOOK(bgp_inst_config_write, (struct bgp *bgp, struct vty *vty), (bgp, vty)) +#define GR_NO_OPER "The Graceful Restart No Operation was executed as cmd same as previous one." +#define GR_INVALID "The Graceful Restart command used is not valid at this moment." static struct peer_group *listen_range_exists(struct bgp *bgp, struct prefix *range, int exact); +/* Show BGP peer's information. */ +enum show_type { + show_all, + show_peer, + show_ipv4_all, + show_ipv6_all, + show_ipv4_peer, + show_ipv6_peer +}; + +static struct peer_group *listen_range_exists( + struct bgp *bgp, + struct prefix *range, + int exact); + +static void bgp_show_global_graceful_restart_mode_vty( + struct vty *vty, + struct bgp *bgp, + bool use_json, + json_object *json); + +static int bgp_show_neighbor_graceful_restart_afi_all( + struct vty *vty, + enum show_type type, + const char *ip_str, + afi_t afi, + bool use_json); + static enum node_type bgp_node_type(afi_t afi, safi_t safi) { switch (afi) { @@ -690,7 +720,16 @@ int bgp_vty_return(struct vty *vty, int ret) str = "Operation not allowed on a directly connected neighbor"; break; case BGP_ERR_PEER_SAFI_CONFLICT: - str = "Cannot activate peer for both 'ipv4 unicast' and 'ipv4 labeled-unicast'"; + str = GR_INVALID; + break; + case BGP_ERR_GR_INVALID_CMD: + str = "The Graceful Restart command used is not valid at this moment."; + break; + case BGP_ERR_GR_OPERATION_FAILED: + str = "The Graceful Restart Operation failed due to an err."; + break; + case BGP_GR_NO_OPERATION: + str = GR_NO_OPER; break; } if (str) { @@ -785,7 +824,8 @@ static int bgp_clear(struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi, int ret = 0; bool found = false; struct peer *peer; - struct listnode *node, *nnode; + + VTY_BGP_GR_DEFINE_LOOP_VARIABLE; /* Clear all neighbors. */ /* @@ -795,11 +835,27 @@ static int bgp_clear(struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi, */ if (sort == clear_all) { for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) { + + bgp_peer_gr_flags_update(peer); + + if (CHECK_FLAG(peer->flags, + PEER_FLAG_GRACEFUL_RESTART)) + gr_router_detected = true; + ret = bgp_peer_clear(peer, afi, safi, nnode, stype); if (ret < 0) bgp_clear_vty_error(vty, peer, afi, safi, ret); + + } + + if (gr_router_detected && + bgp->present_zebra_gr_state == ZEBRA_GR_DISABLE) { + bgp_zebra_send_capabilities(bgp, false); + } else if (!gr_router_detected && + bgp->present_zebra_gr_state == ZEBRA_GR_ENABLE) { + bgp_zebra_send_capabilities(bgp, true); } /* This is to apply read-only mode on this clear. */ @@ -836,6 +892,9 @@ static int bgp_clear(struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi, } } + VTY_BGP_GR_ROUTER_DETECT(bgp, peer, peer->bgp->peer); + VTY_SEND_BGP_GR_CAPABILITY_TO_ZEBRA(peer->bgp, ret); + ret = bgp_peer_clear(peer, afi, safi, NULL, stype); /* if afi/safi not defined for this peer, let caller know */ @@ -881,6 +940,12 @@ static int bgp_clear(struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi, if (peer->sort == BGP_PEER_IBGP) continue; + bgp_peer_gr_flags_update(peer); + + if (CHECK_FLAG(peer->flags, + PEER_FLAG_GRACEFUL_RESTART)) + gr_router_detected = true; + ret = bgp_peer_clear(peer, afi, safi, nnode, stype); if (ret < 0) @@ -889,6 +954,14 @@ static int bgp_clear(struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi, found = true; } + if (gr_router_detected && + bgp->present_zebra_gr_state == ZEBRA_GR_DISABLE) { + bgp_zebra_send_capabilities(bgp, false); + } else if (!gr_router_detected && + bgp->present_zebra_gr_state == ZEBRA_GR_ENABLE) { + bgp_zebra_send_capabilities(bgp, true); + } + if (!found) vty_out(vty, "%%BGP: No external %s peer is configured\n", @@ -905,6 +978,12 @@ static int bgp_clear(struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi, if (peer->as != as) continue; + bgp_peer_gr_flags_update(peer); + + if (CHECK_FLAG(peer->flags, + PEER_FLAG_GRACEFUL_RESTART)) + gr_router_detected = true; + ret = bgp_peer_clear(peer, afi, safi, nnode, stype); if (ret < 0) @@ -913,6 +992,14 @@ static int bgp_clear(struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi, found = true; } + if (gr_router_detected && + bgp->present_zebra_gr_state == ZEBRA_GR_DISABLE) { + bgp_zebra_send_capabilities(bgp, false); + } else if (!gr_router_detected && + bgp->present_zebra_gr_state == ZEBRA_GR_ENABLE) { + bgp_zebra_send_capabilities(bgp, true); + } + if (!found) vty_out(vty, "%%BGP: No %s peer is configured with AS %s\n", @@ -2093,37 +2180,68 @@ DEFUN (no_bgp_deterministic_med, return CMD_SUCCESS; } -/* "bgp graceful-restart" configuration. */ +/* "bgp graceful-restart mode" configuration. */ DEFUN (bgp_graceful_restart, - bgp_graceful_restart_cmd, - "bgp graceful-restart", - "BGP specific commands\n" - "Graceful restart capability parameters\n") + bgp_graceful_restart_cmd, + "bgp graceful-restart", + "BGP specific commands\n" + GR_CMD + ) { + int ret = BGP_GR_FAILURE; + + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug("[BGP_GR] bgp_graceful_restart_cmd : START "); + VTY_DECLVAR_CONTEXT(bgp, bgp); - bgp_flag_set(bgp, BGP_FLAG_GRACEFUL_RESTART); - return CMD_SUCCESS; + + ret = bgp_gr_update_all(bgp, GLOBAL_GR_CMD); + + VTY_BGP_GR_ROUTER_DETECT_AND_SEND_CAPABILITY_TO_ZEBRA(bgp, + bgp->peer, ret); + + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug("[BGP_GR] bgp_graceful_restart_cmd : END "); + vty_out(vty, + "Graceful restart configuration changed, reset all peers to take effect\n"); + return bgp_vty_return(vty, ret); } DEFUN (no_bgp_graceful_restart, - no_bgp_graceful_restart_cmd, - "no bgp graceful-restart", - NO_STR - "BGP specific commands\n" - "Graceful restart capability parameters\n") + no_bgp_graceful_restart_cmd, + "no bgp graceful-restart", + NO_STR + "BGP specific commands\n" + NO_GR_CMD + ) { VTY_DECLVAR_CONTEXT(bgp, bgp); - bgp_flag_unset(bgp, BGP_FLAG_GRACEFUL_RESTART); - return CMD_SUCCESS; + + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug("[BGP_GR] no_bgp_graceful_restart_cmd : START "); + + int ret = BGP_GR_FAILURE; + + ret = bgp_gr_update_all(bgp, NO_GLOBAL_GR_CMD); + + VTY_BGP_GR_ROUTER_DETECT_AND_SEND_CAPABILITY_TO_ZEBRA(bgp, + bgp->peer, ret); + + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug("[BGP_GR] no_bgp_graceful_restart_cmd : END "); + vty_out(vty, + "Graceful restart configuration changed, reset all peers to take effect\n"); + + return bgp_vty_return(vty, ret); } DEFUN (bgp_graceful_restart_stalepath_time, - bgp_graceful_restart_stalepath_time_cmd, - "bgp graceful-restart stalepath-time (1-4095)", - "BGP specific commands\n" - "Graceful restart capability parameters\n" - "Set the max time to hold onto restarting peer's stale paths\n" - "Delay value (seconds)\n") + bgp_graceful_restart_stalepath_time_cmd, + "bgp graceful-restart stalepath-time (1-4095)", + "BGP specific commands\n" + "Graceful restart capability parameters\n" + "Set the max time to hold onto restarting peer's stale paths\n" + "Delay value (seconds)\n") { VTY_DECLVAR_CONTEXT(bgp, bgp); int idx_number = 3; @@ -2135,12 +2253,12 @@ DEFUN (bgp_graceful_restart_stalepath_time, } DEFUN (bgp_graceful_restart_restart_time, - bgp_graceful_restart_restart_time_cmd, - "bgp graceful-restart restart-time (1-4095)", - "BGP specific commands\n" - "Graceful restart capability parameters\n" - "Set the time to wait to delete stale routes before a BGP open message is received\n" - "Delay value (seconds)\n") + bgp_graceful_restart_restart_time_cmd, + "bgp graceful-restart restart-time (1-4095)", + "BGP specific commands\n" + "Graceful restart capability parameters\n" + "Set the time to wait to delete stale routes before a BGP open message is received\n" + "Delay value (seconds)\n") { VTY_DECLVAR_CONTEXT(bgp, bgp); int idx_number = 3; @@ -2151,14 +2269,36 @@ DEFUN (bgp_graceful_restart_restart_time, return CMD_SUCCESS; } -DEFUN (no_bgp_graceful_restart_stalepath_time, - no_bgp_graceful_restart_stalepath_time_cmd, - "no bgp graceful-restart stalepath-time [(1-4095)]", - NO_STR +DEFUN (bgp_graceful_restart_select_defer_time, + bgp_graceful_restart_select_defer_time_cmd, + "bgp graceful-restart select-defer-time (0-3600)", "BGP specific commands\n" "Graceful restart capability parameters\n" - "Set the max time to hold onto restarting peer's stale paths\n" - "Delay value (seconds)\n") + "Set the time to defer the BGP route selection after restart\n" + "Delay value (seconds, 0 - disable)\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + int idx_number = 3; + uint32_t defer_time; + + defer_time = strtoul(argv[idx_number]->arg, NULL, 10); + bgp->select_defer_time = defer_time; + if (defer_time == 0) + bgp_flag_set(bgp, BGP_FLAG_SELECT_DEFER_DISABLE); + else + bgp_flag_unset(bgp, BGP_FLAG_SELECT_DEFER_DISABLE); + + return CMD_SUCCESS; +} + +DEFUN (no_bgp_graceful_restart_stalepath_time, + no_bgp_graceful_restart_stalepath_time_cmd, + "no bgp graceful-restart stalepath-time [(1-4095)]", + NO_STR + "BGP specific commands\n" + "Graceful restart capability parameters\n" + "Set the max time to hold onto restarting peer's stale paths\n" + "Delay value (seconds)\n") { VTY_DECLVAR_CONTEXT(bgp, bgp); @@ -2167,26 +2307,43 @@ DEFUN (no_bgp_graceful_restart_stalepath_time, } DEFUN (no_bgp_graceful_restart_restart_time, - no_bgp_graceful_restart_restart_time_cmd, - "no bgp graceful-restart restart-time [(1-4095)]", + no_bgp_graceful_restart_restart_time_cmd, + "no bgp graceful-restart restart-time [(1-4095)]", + NO_STR + "BGP specific commands\n" + "Graceful restart capability parameters\n" + "Set the time to wait to delete stale routes before a BGP open message is received\n" + "Delay value (seconds)\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + + bgp->restart_time = BGP_DEFAULT_RESTART_TIME; + return CMD_SUCCESS; +} + +DEFUN (no_bgp_graceful_restart_select_defer_time, + no_bgp_graceful_restart_select_defer_time_cmd, + "no bgp graceful-restart select-defer-time [(0-3600)]", NO_STR "BGP specific commands\n" "Graceful restart capability parameters\n" - "Set the time to wait to delete stale routes before a BGP open message is received\n" + "Set the time to defer the BGP route selection after restart\n" "Delay value (seconds)\n") { VTY_DECLVAR_CONTEXT(bgp, bgp); - bgp->restart_time = BGP_DEFAULT_RESTART_TIME; + bgp->select_defer_time = BGP_DEFAULT_SELECT_DEFERRAL_TIME; + bgp_flag_unset(bgp, BGP_FLAG_SELECT_DEFER_DISABLE); + return CMD_SUCCESS; } DEFUN (bgp_graceful_restart_preserve_fw, - bgp_graceful_restart_preserve_fw_cmd, - "bgp graceful-restart preserve-fw-state", - "BGP specific commands\n" - "Graceful restart capability parameters\n" - "Sets F-bit indication that fib is preserved while doing Graceful Restart\n") + bgp_graceful_restart_preserve_fw_cmd, + "bgp graceful-restart preserve-fw-state", + "BGP specific commands\n" + "Graceful restart capability parameters\n" + "Sets F-bit indication that fib is preserved while doing Graceful Restart\n") { VTY_DECLVAR_CONTEXT(bgp, bgp); bgp_flag_set(bgp, BGP_FLAG_GR_PRESERVE_FWD); @@ -2194,15 +2351,366 @@ DEFUN (bgp_graceful_restart_preserve_fw, } DEFUN (no_bgp_graceful_restart_preserve_fw, - no_bgp_graceful_restart_preserve_fw_cmd, - "no bgp graceful-restart preserve-fw-state", + no_bgp_graceful_restart_preserve_fw_cmd, + "no bgp graceful-restart preserve-fw-state", + NO_STR + "BGP specific commands\n" + "Graceful restart capability parameters\n" + "Unsets F-bit indication that fib is preserved while doing Graceful Restart\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + bgp_flag_unset(bgp, BGP_FLAG_GR_PRESERVE_FWD); + return CMD_SUCCESS; +} + +DEFUN (bgp_graceful_restart_disable, + bgp_graceful_restart_disable_cmd, + "bgp graceful-restart-disable", + "BGP specific commands\n" + GR_DISABLE) +{ + int ret = BGP_GR_FAILURE; + + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug( + "[BGP_GR] bgp_graceful_restart_disable_cmd : START "); + + VTY_DECLVAR_CONTEXT(bgp, bgp); + + ret = bgp_gr_update_all(bgp, GLOBAL_DISABLE_CMD); + + VTY_BGP_GR_ROUTER_DETECT_AND_SEND_CAPABILITY_TO_ZEBRA(bgp, + bgp->peer, ret); + + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug( + "[BGP_GR] bgp_graceful_restart_disable_cmd : END "); + vty_out(vty, + "Graceful restart configuration changed, reset all peers to take effect\n"); + + return bgp_vty_return(vty, ret); +} + +DEFUN (no_bgp_graceful_restart_disable, + no_bgp_graceful_restart_disable_cmd, + "no bgp graceful-restart-disable", + NO_STR + "BGP specific commands\n" + NO_GR_DISABLE + ) +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug( + "[BGP_GR] no_bgp_graceful_restart_disable_cmd : START "); + + int ret = BGP_GR_FAILURE; + + ret = bgp_gr_update_all(bgp, NO_GLOBAL_DISABLE_CMD); + + VTY_BGP_GR_ROUTER_DETECT_AND_SEND_CAPABILITY_TO_ZEBRA(bgp, + bgp->peer, ret); + + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug( + "[BGP_GR] no_bgp_graceful_restart_disable_cmd : END "); + vty_out(vty, + "Graceful restart configuration changed, reset all peers to take effect\n"); + + return bgp_vty_return(vty, ret); +} + +DEFUN (bgp_neighbor_graceful_restart_set, + bgp_neighbor_graceful_restart_set_cmd, + "neighbor <A.B.C.D|X:X::X:X|WORD> graceful-restart", + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + GR_NEIGHBOR_CMD + ) +{ + int idx_peer = 1; + struct peer *peer; + int ret = BGP_GR_FAILURE; + + VTY_BGP_GR_DEFINE_LOOP_VARIABLE; + + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug( + "[BGP_GR] bgp_neighbor_graceful_restart_set_cmd : START "); + + peer = peer_and_group_lookup_vty(vty, argv[idx_peer]->arg); + if (!peer) + return CMD_WARNING_CONFIG_FAILED; + + ret = bgp_neighbor_graceful_restart(peer, PEER_GR_CMD); + + VTY_BGP_GR_ROUTER_DETECT(bgp, peer, peer->bgp->peer); + VTY_SEND_BGP_GR_CAPABILITY_TO_ZEBRA(peer->bgp, ret); + + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug( + "[BGP_GR] bgp_neighbor_graceful_restart_set_cmd : END "); + vty_out(vty, + "Graceful restart configuration changed, reset this peer to take effect\n"); + + return bgp_vty_return(vty, ret); +} + +DEFUN (no_bgp_neighbor_graceful_restart, + no_bgp_neighbor_graceful_restart_set_cmd, + "no neighbor <A.B.C.D|X:X::X:X|WORD> graceful-restart", + NO_STR + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + NO_GR_NEIGHBOR_CMD + ) +{ + int idx_peer = 2; + int ret = BGP_GR_FAILURE; + struct peer *peer; + + VTY_BGP_GR_DEFINE_LOOP_VARIABLE; + + peer = peer_and_group_lookup_vty(vty, argv[idx_peer]->arg); + if (!peer) + return CMD_WARNING_CONFIG_FAILED; + + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug( + "[BGP_GR] no_bgp_neighbor_graceful_restart_set_cmd : START "); + + ret = bgp_neighbor_graceful_restart(peer, NO_PEER_GR_CMD); + + VTY_BGP_GR_ROUTER_DETECT(bgp, peer, peer->bgp->peer); + VTY_SEND_BGP_GR_CAPABILITY_TO_ZEBRA(peer->bgp, ret); + + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug( + "[BGP_GR] no_bgp_neighbor_graceful_restart_set_cmd : END "); + vty_out(vty, + "Graceful restart configuration changed, reset this peer to take effect\n"); + + return bgp_vty_return(vty, ret); +} + +DEFUN (bgp_neighbor_graceful_restart_helper_set, + bgp_neighbor_graceful_restart_helper_set_cmd, + "neighbor <A.B.C.D|X:X::X:X|WORD> graceful-restart-helper", + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + GR_NEIGHBOR_HELPER_CMD + ) +{ + int idx_peer = 1; + struct peer *peer; + int ret = BGP_GR_FAILURE; + + VTY_BGP_GR_DEFINE_LOOP_VARIABLE; + + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug( + "[BGP_GR] bgp_neighbor_graceful_restart_helper_set_cmd : START "); + + peer = peer_and_group_lookup_vty(vty, argv[idx_peer]->arg); + + if (!peer) + return CMD_WARNING_CONFIG_FAILED; + + + ret = bgp_neighbor_graceful_restart(peer, PEER_HELPER_CMD); + + VTY_BGP_GR_ROUTER_DETECT(bgp, peer, peer->bgp->peer); + VTY_SEND_BGP_GR_CAPABILITY_TO_ZEBRA(peer->bgp, ret); + + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug( + "[BGP_GR] bgp_neighbor_graceful_restart_helper_set_cmd : END "); + vty_out(vty, + "Graceful restart configuration changed, reset this peer to take effect\n"); + + return bgp_vty_return(vty, ret); +} + +DEFUN (no_bgp_neighbor_graceful_restart_helper, + no_bgp_neighbor_graceful_restart_helper_set_cmd, + "no neighbor <A.B.C.D|X:X::X:X|WORD> graceful-restart-helper", + NO_STR + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + NO_GR_NEIGHBOR_HELPER_CMD + ) +{ + int idx_peer = 2; + int ret = BGP_GR_FAILURE; + struct peer *peer; + + VTY_BGP_GR_DEFINE_LOOP_VARIABLE; + + peer = peer_and_group_lookup_vty(vty, argv[idx_peer]->arg); + if (!peer) + return CMD_WARNING_CONFIG_FAILED; + + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug( + "[BGP_GR] no_bgp_neighbor_graceful_restart_helper_set_cmd : START "); + + ret = bgp_neighbor_graceful_restart(peer, + NO_PEER_HELPER_CMD); + + VTY_BGP_GR_ROUTER_DETECT(bgp, peer, peer->bgp->peer); + VTY_SEND_BGP_GR_CAPABILITY_TO_ZEBRA(peer->bgp, ret); + + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug( + "[BGP_GR] no_bgp_neighbor_graceful_restart_helper_set_cmd : END "); + vty_out(vty, + "Graceful restart configuration changed, reset this peer to take effect\n"); + + return bgp_vty_return(vty, ret); +} + +DEFUN (bgp_neighbor_graceful_restart_disable_set, + bgp_neighbor_graceful_restart_disable_set_cmd, + "neighbor <A.B.C.D|X:X::X:X|WORD> graceful-restart-disable", + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + GR_NEIGHBOR_DISABLE_CMD + ) +{ + int idx_peer = 1; + struct peer *peer; + int ret = BGP_GR_FAILURE; + + VTY_BGP_GR_DEFINE_LOOP_VARIABLE; + + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug( + "[BGP_GR] bgp_neighbor_graceful_restart_disable_set_cmd : START "); + + peer = peer_and_group_lookup_vty(vty, argv[idx_peer]->arg); + if (!peer) + return CMD_WARNING_CONFIG_FAILED; + + ret = bgp_neighbor_graceful_restart(peer, + PEER_DISABLE_CMD); + + if (peer->bgp->t_startup) + bgp_peer_gr_flags_update(peer); + + VTY_BGP_GR_ROUTER_DETECT(bgp, peer, peer->bgp->peer); + VTY_SEND_BGP_GR_CAPABILITY_TO_ZEBRA(peer->bgp, ret); + + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug( + "[BGP_GR]bgp_neighbor_graceful_restart_disable_set_cmd : END "); + vty_out(vty, + "Graceful restart configuration changed, reset this peer to take effect\n"); + + return bgp_vty_return(vty, ret); +} + +DEFUN (no_bgp_neighbor_graceful_restart_disable, + no_bgp_neighbor_graceful_restart_disable_set_cmd, + "no neighbor <A.B.C.D|X:X::X:X|WORD> graceful-restart-disable", + NO_STR + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + NO_GR_NEIGHBOR_DISABLE_CMD + ) +{ + int idx_peer = 2; + int ret = BGP_GR_FAILURE; + struct peer *peer; + + VTY_BGP_GR_DEFINE_LOOP_VARIABLE; + + peer = peer_and_group_lookup_vty(vty, argv[idx_peer]->arg); + if (!peer) + return CMD_WARNING_CONFIG_FAILED; + + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug( + "[BGP_GR] no_bgp_neighbor_graceful_restart_disable_set_cmd : START "); + + ret = bgp_neighbor_graceful_restart(peer, NO_PEER_DISABLE_CMD); + + VTY_BGP_GR_ROUTER_DETECT(bgp, peer, peer->bgp->peer); + VTY_SEND_BGP_GR_CAPABILITY_TO_ZEBRA(peer->bgp, ret); + + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug( + "[BGP_GR] no_bgp_neighbor_graceful_restart_disable_set_cmd : END "); + vty_out(vty, + "Graceful restart configuration changed, reset this peer to take effect\n"); + + return bgp_vty_return(vty, ret); +} + +DEFUN_HIDDEN (bgp_graceful_restart_disable_eor, + bgp_graceful_restart_disable_eor_cmd, + "bgp graceful-restart disable-eor", + "BGP specific commands\n" + "Graceful restart configuration parameters\n" + "Disable EOR Check\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + bgp_flag_set(bgp, BGP_FLAG_GR_DISABLE_EOR); + + return CMD_SUCCESS; +} + +DEFUN_HIDDEN (no_bgp_graceful_restart_disable_eor, + no_bgp_graceful_restart_disable_eor_cmd, + "no bgp graceful-restart disable-eor", + NO_STR + "BGP specific commands\n" + "Graceful restart configuration parameters\n" + "Disable EOR Check\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + bgp_flag_unset(bgp, BGP_FLAG_GR_DISABLE_EOR); + + return CMD_SUCCESS; +} + +DEFUN (bgp_graceful_restart_rib_stale_time, + bgp_graceful_restart_rib_stale_time_cmd, + "bgp graceful-restart rib-stale-time (1-3600)", + "BGP specific commands\n" + "Graceful restart configuration parameters\n" + "Specify the stale route removal timer in rib\n" + "Delay value (seconds)\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + int idx_number = 3; + uint32_t stale_time; + + stale_time = strtoul(argv[idx_number]->arg, NULL, 10); + bgp->rib_stale_time = stale_time; + /* Send the stale timer update message to RIB */ + if (bgp_zebra_stale_timer_update(bgp)) + return CMD_WARNING; + + return CMD_SUCCESS; +} + +DEFUN (no_bgp_graceful_restart_rib_stale_time, + no_bgp_graceful_restart_rib_stale_time_cmd, + "no bgp graceful-restart rib-stale-time [(1-3600)]", NO_STR "BGP specific commands\n" - "Graceful restart capability parameters\n" - "Unsets F-bit indication that fib is preserved while doing Graceful Restart\n") + "Graceful restart configuration parameters\n" + "Specify the stale route removal timer in rib\n" + "Delay value (seconds)\n") { VTY_DECLVAR_CONTEXT(bgp, bgp); - bgp_flag_unset(bgp, BGP_FLAG_GR_PRESERVE_FWD); + + bgp->rib_stale_time = BGP_DEFAULT_RIB_STALE_TIME; + /* Send the stale timer update message to RIB */ + if (bgp_zebra_stale_timer_update(bgp)) + return CMD_WARNING; + return CMD_SUCCESS; } @@ -8882,8 +9390,6 @@ const char *get_afi_safi_str(afi_t afi, safi_t safi, bool for_json) return get_afi_safi_vty_str(afi, safi); } -/* Show BGP peer's information. */ -enum show_type { show_all, show_peer, show_ipv4_all, show_ipv6_all, show_ipv4_peer, show_ipv6_peer }; static void bgp_show_peer_afi_orf_cap(struct vty *vty, struct peer *p, afi_t afi, safi_t safi, @@ -8948,6 +9454,429 @@ static void bgp_show_peer_afi_orf_cap(struct vty *vty, struct peer *p, } } +static void bgp_show_neighnor_graceful_restart_rbit( + struct vty *vty, + struct peer *p, + bool use_json, + json_object *json) +{ + bool rbit_status = 0; + + if (!use_json) + vty_out(vty, "\n R bit : "); + + if (CHECK_FLAG(p->cap, PEER_CAP_RESTART_ADV) && + (CHECK_FLAG(p->cap, PEER_CAP_RESTART_RCV)) && + (p->status == Established)) { + + if (CHECK_FLAG(p->cap, PEER_CAP_RESTART_BIT_RCV)) + rbit_status = 1; + else + rbit_status = 0; + } + + if (rbit_status) { + if (use_json) + json_object_boolean_true_add( + json, "rBit"); + else + vty_out(vty, "True\n"); + } else { + if (use_json) + json_object_boolean_false_add( + json, "rBit"); + else + vty_out(vty, "False\n"); + } +} + +static void bgp_show_neighbor_graceful_restart_remote_mode( + struct vty *vty, + struct peer *peer, + bool use_json, + json_object *json) +{ + const char *mode = "NotApplicable"; + + if (!use_json) + vty_out(vty, "\n Remote GR Mode : "); + + if (CHECK_FLAG(peer->cap, PEER_CAP_RESTART_ADV) && + (peer->status == Established)) { + + if ((peer->nsf_af_count == 0) && + !CHECK_FLAG(peer->cap, PEER_CAP_RESTART_RCV)) { + + mode = "Disable"; + + } else if (peer->nsf_af_count == 0 && + CHECK_FLAG(peer->cap, PEER_CAP_RESTART_RCV)) { + + mode = "Helper"; + + } else if (peer->nsf_af_count != 0 && + CHECK_FLAG(peer->cap, PEER_CAP_RESTART_RCV)) { + + mode = "Restart"; + + } + } + + if (use_json) { + json_object_string_add(json, + "remoteGrMode", mode); + } else + vty_out(vty, mode, "\n"); +} + +static void bgp_show_neighbor_graceful_restart_local_mode(struct vty *vty, + struct peer *p, + bool use_json, + json_object *json) +{ + const char *mode = "Invalid"; + + if (!use_json) + vty_out(vty, " Local GR Mode : "); + + if (bgp_peer_gr_mode_get(p) == PEER_HELPER) + mode = "Helper"; + else if (bgp_peer_gr_mode_get(p) == PEER_GR) + mode = "Restart"; + else if (bgp_peer_gr_mode_get(p) == PEER_DISABLE) + mode = "Disable"; + else if (bgp_peer_gr_mode_get(p) == PEER_GLOBAL_INHERIT) { + if (bgp_global_gr_mode_get(p->bgp) == GLOBAL_HELPER) + mode = "Helper*"; + else if (bgp_global_gr_mode_get(p->bgp) == GLOBAL_GR) + mode = "Restart*"; + else if (bgp_global_gr_mode_get(p->bgp) == GLOBAL_DISABLE) + mode = "Disable*"; + else + mode = "Invalid*"; + } + + if (use_json) { + json_object_string_add(json, + "localGrMode", mode); + } else { + vty_out(vty, mode, "\n"); + } +} + +static void bgp_show_neighbor_graceful_restart_capability_per_afi_safi( + struct vty *vty, struct peer *peer, + bool use_json, json_object *json) +{ + afi_t afi; + safi_t safi; + json_object *json_afi_safi = NULL; + json_object *json_timer = NULL; + json_object *json_endofrib_status = NULL; + bool eor_flag = false; + + for (afi = AFI_IP; afi < AFI_MAX; afi++) { + for (safi = SAFI_UNICAST; safi <= SAFI_MPLS_VPN; safi++) { + if (peer->afc[afi][safi] + && CHECK_FLAG(peer->cap, PEER_CAP_RESTART_ADV) + && CHECK_FLAG(peer->cap, PEER_CAP_RESTART_RCV) + ) { + if (use_json) { + json_afi_safi = + json_object_new_object(); + json_endofrib_status = + json_object_new_object(); + json_timer = + json_object_new_object(); + } + + if (peer->eor_stime[afi][safi] >= + peer->pkt_stime[afi][safi]) + eor_flag = true; + else + eor_flag = false; + + if (!use_json) { + vty_out(vty, " %s :\n", + get_afi_safi_str(afi, safi, false)); + + vty_out(vty, + " F bit : "); + } else + get_afi_safi_str(afi, safi, true); + + if (peer->nsf[afi][safi] + && CHECK_FLAG( + peer->af_cap[afi][safi], + PEER_CAP_RESTART_AF_PRESERVE_RCV)) { + + if (use_json) { + json_object_boolean_true_add( + json_afi_safi, "fBit"); + } else { + vty_out(vty, + "True\n"); + } + + } else { + + if (use_json) { + json_object_boolean_false_add( + json_afi_safi, "fBit"); + } else { + vty_out(vty, + "False\n"); + } + + } + + if (!use_json) + vty_out(vty, + " End-of-RIB Received : "); + + if (CHECK_FLAG(peer->af_sflags[afi][safi], + PEER_STATUS_EOR_RECEIVED)) { + + if (use_json) { + json_object_boolean_true_add( + json_endofrib_status, + "endOfRibRecv"); + } else { + vty_out(vty, "Yes\n"); + } + + } else { + if (use_json) { + json_object_boolean_false_add( + json_endofrib_status, + "endOfRibRecv"); + } else { + vty_out(vty, "No\n"); + } + } + + if (!use_json) + vty_out(vty, + " End-of-RIB Send : "); + + if (CHECK_FLAG(peer->af_sflags[afi][safi], + PEER_STATUS_EOR_SEND)) { + if (use_json) { + json_object_boolean_true_add( + json_endofrib_status, + "endOfRibSend"); + + PRINT_EOR_JSON(eor_flag); + } else { + vty_out(vty, "Yes\n"); + vty_out(vty, + " EoRSentAfterUpdate : "); + + PRINT_EOR(eor_flag); + } + } else { + if (use_json) { + json_object_boolean_false_add( + json_endofrib_status, + "endOfRibSend"); + json_object_boolean_false_add( + json_endofrib_status, + "endOfRibSentAfterUpdate"); + } else { + vty_out(vty, "No\n"); + vty_out(vty, + " EoRSentAfterUpdate : "); + vty_out(vty, "No\n"); + } + } + + if (use_json) { + + json_object_int_add(json_timer, + "stalePathTimer", + peer->bgp->stalepath_time); + + if (peer->t_gr_stale != NULL) { + + json_object_int_add( + json_timer, + "stalePathTimerRemaining", + thread_timer_remain_second( + peer->t_gr_stale)); + } + + /* Display Configured Selection + * Deferral only when when + * Gr mode is enabled. + */ + if (CHECK_FLAG(peer->flags, + PEER_FLAG_GRACEFUL_RESTART)) { + + json_object_int_add( + json_timer, + "selectionDeferralTimer", + peer->bgp->stalepath_time); + } + + if (peer->bgp->gr_info[afi][safi] + .t_select_deferral != NULL) { + + json_object_int_add( + json_timer, + "selectionDeferralTimerRemaining", + thread_timer_remain_second( + peer->bgp + ->gr_info[afi][safi] + .t_select_deferral)); + } + + } else { + + vty_out(vty, " Timers:\n"); + + vty_out(vty, "%*s", 6, ""); + vty_out(vty, + "Configured Stale Path Time(sec)%*s: %u\n", + 8, "", + peer->bgp->stalepath_time); + + if (peer->t_gr_stale != NULL) { + vty_out(vty, "%*s", 6, ""); + vty_out(vty, + "Stale Path Remaining(sec)%*s: %ld\n", + 14, "", + thread_timer_remain_second( + peer->t_gr_stale)); + } + /* Display Configured Selection + * Deferral only when when + * Gr mode is enabled. + */ + if (CHECK_FLAG(peer->flags, + PEER_FLAG_GRACEFUL_RESTART)) { + vty_out(vty, "%*s", 6, ""); + vty_out(vty, + "Configured Selection Deferral Time(sec): %u\n", + peer->bgp->select_defer_time); + } + + if (peer->bgp + ->gr_info[afi][safi] + .t_select_deferral != NULL) { + + vty_out(vty, "%*s", 6, ""); + vty_out(vty, + "Selection Deferral Time Remaining(sec) : %ld\n", + thread_timer_remain_second( + peer->bgp + ->gr_info[afi][safi] + .t_select_deferral)); + } + + } + if (use_json) { + json_object_object_add(json_afi_safi, + "endOfRibStatus", + json_endofrib_status); + json_object_object_add(json_afi_safi, + "timers", + json_timer); + json_object_object_add(json, + get_afi_safi_str(afi, safi, true), + json_afi_safi); + } + } + } + } +} + +static void bgp_show_neighbor_graceful_restart_time(struct vty *vty, + struct peer *p, + bool use_json, + json_object *json) +{ + if (use_json) { + json_object *json_timer = NULL; + + json_timer = json_object_new_object(); + + json_object_int_add(json_timer, + "configuredRestartTimer", + p->bgp->restart_time); + + json_object_int_add(json_timer, + "receivedRestartTimer", + p->v_gr_restart); + + if (p->t_gr_restart != NULL) { + json_object_int_add(json_timer, + "restartTimerRemaining", + thread_timer_remain_second( + p->t_gr_restart) + ); + } + + json_object_object_add(json, "timers", json_timer); + } else { + + vty_out(vty, " Timers :\n"); + vty_out(vty, + " Configured Restart Time(sec) : %u\n", + p->bgp->restart_time); + + vty_out(vty, + " Received Restart Time(sec) : %u\n", + p->v_gr_restart); + if (p->t_gr_restart != NULL) { + vty_out(vty, + " Restart Time Remaining(sec) : %ld\n", + thread_timer_remain_second( + p->t_gr_restart)); + } + } +} + +static void bgp_show_peer_gr_status(struct vty *vty, struct peer *p, + bool use_json, + json_object *json) +{ + char buf[SU_ADDRSTRLEN] = {0}; + char dn_flag[2] = {0}; + char neighborAddr[INET6_ADDRSTRLEN] = {0}; + + if (!p->conf_if && peer_dynamic_neighbor(p)) + dn_flag[0] = '*'; + + if (p->conf_if) { + if (use_json) + json_object_string_add(json, "neighborAddr", + BGP_PEER_SU_UNSPEC(p) + ? "none" + : sockunion2str(&p->su, buf, + SU_ADDRSTRLEN)); + else + vty_out(vty, "BGP neighbor on %s: %s\n", + p->conf_if, + BGP_PEER_SU_UNSPEC(p) + ? "none" + : sockunion2str(&p->su, buf, + SU_ADDRSTRLEN)); + } else { + sprintf(neighborAddr, "%s%s", dn_flag, p->host); + + if (use_json) + json_object_string_add( + json, "neighborAddr", + neighborAddr); + else + vty_out(vty, "BGP neighbor is %s\n", + neighborAddr); + } + + /* more gr info in new format */ + BGP_SHOW_PEER_GR_CAPABILITY(vty, p, use_json, json); +} + static void bgp_show_peer_afi(struct vty *vty, struct peer *p, afi_t afi, safi_t safi, bool use_json, json_object *json_neigh) @@ -9262,7 +10191,9 @@ static void bgp_show_peer_afi(struct vty *vty, struct peer *p, afi_t afi, "prefixAllowedRestartIntervalMsecs", p->pmax_restart[afi][safi] * 60000); } - json_object_object_add(json_neigh, get_afi_safi_str(afi, safi, true), + json_object_object_add(json_neigh, + get_afi_safi_str(afi, + safi, true), json_addr); } else { @@ -10701,14 +11632,12 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json, vty_out(vty, "none"); vty_out(vty, "\n"); } - } + } /* Gracefull Restart */ } } } /* graceful restart information */ - if (CHECK_FLAG(p->cap, PEER_CAP_RESTART_RCV) || p->t_gr_restart - || p->t_gr_stale) { json_object *json_grace = NULL; json_object *json_grace_send = NULL; json_object *json_grace_recv = NULL; @@ -10720,10 +11649,11 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json, json_grace_send = json_object_new_object(); json_grace_recv = json_object_new_object(); - if (p->status == Established) { + if ((p->status == Established) && + CHECK_FLAG(p->cap, PEER_CAP_RESTART_RCV)) { FOREACH_AFI_SAFI (afi, safi) { if (CHECK_FLAG(p->af_sflags[afi][safi], - PEER_STATUS_EOR_SEND)) { + PEER_STATUS_EOR_SEND)) { json_object_boolean_true_add( json_grace_send, get_afi_safi_str(afi, @@ -10734,8 +11664,8 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json, } FOREACH_AFI_SAFI (afi, safi) { if (CHECK_FLAG( - p->af_sflags[afi][safi], - PEER_STATUS_EOR_RECEIVED)) { + p->af_sflags[afi][safi], + PEER_STATUS_EOR_RECEIVED)) { json_object_boolean_true_add( json_grace_recv, get_afi_safi_str(afi, @@ -10745,11 +11675,13 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json, } } } + json_object_object_add(json_grace, + "endOfRibSend", + json_grace_send); + json_object_object_add(json_grace, + "endOfRibRecv", + json_grace_recv); - json_object_object_add(json_grace, "endOfRibSend", - json_grace_send); - json_object_object_add(json_grace, "endOfRibRecv", - json_grace_recv); if (p->t_gr_restart) json_object_int_add(json_grace, @@ -10765,12 +11697,16 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json, thread_timer_remain_second( p->t_gr_stale) * 1000); - + /* more gr info in new format */ + BGP_SHOW_PEER_GR_CAPABILITY(vty, p, use_json, + json_grace); json_object_object_add( json_neigh, "gracefulRestartInfo", json_grace); } else { - vty_out(vty, " Graceful restart information:\n"); - if (p->status == Established) { + vty_out(vty, " Graceful restart informations:\n"); + if ((p->status == Established) && + CHECK_FLAG(p->cap, PEER_CAP_RESTART_RCV)) { + vty_out(vty, " End-of-RIB send: "); FOREACH_AFI_SAFI (afi, safi) { if (CHECK_FLAG(p->af_sflags[afi][safi], @@ -10779,8 +11715,8 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json, eor_send_af_count ? ", " : "", get_afi_safi_str(afi, - safi, - false)); + safi, + false)); eor_send_af_count++; } } @@ -10814,8 +11750,11 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json, " The remaining time of stalepath timer is %ld\n", thread_timer_remain_second( p->t_gr_stale)); + + /* more gr info in new format */ + BGP_SHOW_PEER_GR_CAPABILITY(vty, p, use_json, NULL); } - } + if (use_json) { json_object *json_stat = NULL; json_stat = json_object_new_object(); @@ -11229,6 +12168,84 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json, } } +static int bgp_show_neighbor_graceful_restart(struct vty *vty, + struct bgp *bgp, + enum show_type type, + union sockunion *su, + const char *conf_if, afi_t afi, + bool use_json, json_object *json) +{ + struct listnode *node, *nnode; + struct peer *peer; + int find = 0; + safi_t safi = SAFI_UNICAST; + json_object *json_neighbor = NULL; + + for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) { + + if (!CHECK_FLAG(peer->flags, PEER_FLAG_CONFIG_NODE)) + continue; + + if ((peer->afc[afi][safi]) == 0) + continue; + + if (use_json) + json_neighbor = json_object_new_object(); + + if (type == show_all) { + bgp_show_peer_gr_status(vty, peer, use_json, + json_neighbor); + + if (use_json) + json_object_object_add(json, + peer->host, json_neighbor); + + } else if (type == show_peer) { + if (conf_if) { + if ((peer->conf_if + && !strcmp(peer->conf_if, conf_if)) + || (peer->hostname + && !strcmp(peer->hostname, conf_if))) { + find = 1; + bgp_show_peer_gr_status(vty, + peer, use_json, + json_neighbor); + } + } else { + if (sockunion_same(&peer->su, su)) { + find = 1; + bgp_show_peer_gr_status(vty, + peer, use_json, + json_neighbor); + } + } + if (use_json && find) { + json_object_object_add(json, + peer->host, json_neighbor); + } + } + + if (find) + break; + } + + if (type == show_peer && !find) { + if (use_json) + json_object_boolean_true_add(json, + "bgpNoSuchNeighbor"); + else + vty_out(vty, "%% No such neighbor\n"); + } + if (use_json) { + vty_out(vty, "%s\n", json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + } else { + vty_out(vty, "\n"); + } + + return CMD_SUCCESS; +} + static int bgp_show_neighbor(struct vty *vty, struct bgp *bgp, enum show_type type, union sockunion *su, const char *conf_if, bool use_json, @@ -11334,6 +12351,46 @@ static int bgp_show_neighbor(struct vty *vty, struct bgp *bgp, return CMD_SUCCESS; } +static void bgp_show_neighbor_graceful_restart_vty(struct vty *vty, + enum show_type type, const char *ip_str, + afi_t afi, bool use_json) +{ + + int ret; + struct bgp *bgp; + union sockunion su; + json_object *json = NULL; + + bgp = bgp_get_default(); + + if (bgp) { + + if (!use_json) { + bgp_show_global_graceful_restart_mode_vty(vty, bgp, + use_json, NULL); + } + + json = json_object_new_object(); + if (ip_str) { + ret = str2sockunion(ip_str, &su); + if (ret < 0) + bgp_show_neighbor_graceful_restart(vty, + bgp, type, NULL, ip_str, + afi, use_json, json); + else + bgp_show_neighbor_graceful_restart(vty, + bgp, type, &su, NULL, + afi, use_json, json); + } else { + bgp_show_neighbor_graceful_restart(vty, bgp, + type, NULL, NULL, afi, + use_json, json); + } + json_object_free(json); + } + +} + static void bgp_show_all_instances_neighbors_vty(struct vty *vty, enum show_type type, const char *ip_str, @@ -11471,6 +12528,51 @@ static int bgp_show_neighbor_vty(struct vty *vty, const char *name, return CMD_SUCCESS; } + + +/* "show [ip] bgp neighbors graceful-restart" commands. */ +DEFUN (show_ip_bgp_neighbors_gracrful_restart, + show_ip_bgp_neighbors_graceful_restart_cmd, + "show bgp [<ipv4|ipv6>] neighbors [<A.B.C.D|X:X::X:X|WORD>] graceful-restart [json]", + SHOW_STR + BGP_STR + IP_STR + IPV6_STR + NEIGHBOR_STR + "Neighbor to display information about\n" + "Neighbor to display information about\n" + "Neighbor on BGP configured interface\n" + GR_SHOW + JSON_STR) +{ + char *sh_arg = NULL; + enum show_type sh_type; + int idx = 0; + afi_t afi = AFI_MAX; + bool uj = use_json(argc, argv); + + if (!argv_find_and_parse_afi(argv, argc, &idx, &afi)) + afi = AFI_MAX; + + idx++; + + if (argv_find(argv, argc, "A.B.C.D", &idx) + || argv_find(argv, argc, "X:X::X:X", &idx) + || argv_find(argv, argc, "WORD", &idx)) { + sh_type = show_peer; + sh_arg = argv[idx]->arg; + } else + sh_type = show_all; + + if (!argv_find(argv, argc, "graceful-restart", &idx)) + return CMD_SUCCESS; + + + return bgp_show_neighbor_graceful_restart_afi_all(vty, + sh_type, sh_arg, + afi, uj); +} + /* "show [ip] bgp neighbors" commands. */ DEFUN (show_ip_bgp_neighbors, show_ip_bgp_neighbors_cmd, @@ -11609,7 +12711,72 @@ DEFUN (show_ip_bgp_lcommunity_info, return CMD_SUCCESS; } +/* Graceful Restart */ + +static void bgp_show_global_graceful_restart_mode_vty(struct vty *vty, + struct bgp *bgp, + bool use_json, + json_object *json) +{ + + vty_out(vty, "\n%s", SHOW_GR_HEADER); + + int bgp_global_gr_mode = bgp_global_gr_mode_get(bgp); + + switch (bgp_global_gr_mode) { + + case GLOBAL_HELPER: + vty_out(vty, + "Global BGP GR Mode : Helper\n"); + break; + + case GLOBAL_GR: + vty_out(vty, + "Global BGP GR Mode : Restart\n"); + break; + + case GLOBAL_DISABLE: + vty_out(vty, + "Global BGP GR Mode : Disable\n"); + break; + + case GLOBAL_INVALID: + default: + vty_out(vty, + "Global BGP GR Mode Invalid\n"); + break; + } + vty_out(vty, "\n"); +} + +static int bgp_show_neighbor_graceful_restart_afi_all(struct vty *vty, + enum show_type type, + const char *ip_str, + afi_t afi, + bool use_json) +{ + if ((afi == AFI_MAX) && (ip_str == NULL)) { + afi = AFI_IP; + + while ((afi != AFI_L2VPN) && (afi < AFI_MAX)) { + + bgp_show_neighbor_graceful_restart_vty(vty, + type, ip_str, + afi, use_json); + afi++; + } + } else if (afi != AFI_MAX) { + bgp_show_neighbor_graceful_restart_vty(vty, + type, ip_str, + afi, use_json); + } else { + return CMD_ERR_INCOMPLETE; + } + + return CMD_SUCCESS; +} +/* Graceful Restart */ DEFUN (show_ip_bgp_attr_info, show_ip_bgp_attr_info_cmd, @@ -13437,6 +14604,27 @@ static void bgp_config_write_peer_global(struct vty *vty, struct bgp *bgp, if (peer->as_path_loop_detection) vty_out(vty, " neighbor %s sender-as-path-loop-detection\n", addr); + + if (!CHECK_FLAG(peer->peer_gr_new_status_flag, + PEER_GRACEFUL_RESTART_NEW_STATE_INHERIT)) { + + if (CHECK_FLAG(peer->peer_gr_new_status_flag, + PEER_GRACEFUL_RESTART_NEW_STATE_HELPER)) { + vty_out(vty, + " neighbor %s graceful-restart-helper\n", addr); + } else if (CHECK_FLAG(peer->peer_gr_new_status_flag, + PEER_GRACEFUL_RESTART_NEW_STATE_RESTART)) { + vty_out(vty, + " neighbor %s graceful-restart\n", addr); + } else if ((!(CHECK_FLAG(peer->peer_gr_new_status_flag, + PEER_GRACEFUL_RESTART_NEW_STATE_HELPER)) + && !(CHECK_FLAG(peer->peer_gr_new_status_flag, + PEER_GRACEFUL_RESTART_NEW_STATE_RESTART)))) { + vty_out(vty, + " neighbor %s graceful-restart-disable\n", + addr); + } + } } /* BGP peer configuration display function. */ @@ -13963,12 +15151,22 @@ int bgp_config_write(struct vty *vty) vty_out(vty, " bgp graceful-restart stalepath-time %u\n", bgp->stalepath_time); + if (bgp->restart_time != BGP_DEFAULT_RESTART_TIME) vty_out(vty, " bgp graceful-restart restart-time %u\n", bgp->restart_time); - if (bgp_flag_check(bgp, BGP_FLAG_GRACEFUL_RESTART)) + + if (bgp->select_defer_time != BGP_DEFAULT_SELECT_DEFERRAL_TIME) + vty_out(vty, + " bgp graceful-restart select-defer-time %u\n", + bgp->select_defer_time); + + if (bgp_global_gr_mode_get(bgp) == GLOBAL_GR) vty_out(vty, " bgp graceful-restart\n"); + if (bgp_global_gr_mode_get(bgp) == GLOBAL_DISABLE) + vty_out(vty, " bgp graceful-restart-disable\n"); + /* BGP graceful-shutdown */ if (bgp_flag_check(bgp, BGP_FLAG_GRACEFUL_SHUTDOWN)) vty_out(vty, " bgp graceful-shutdown\n"); @@ -13978,6 +15176,12 @@ int bgp_config_write(struct vty *vty) vty_out(vty, " bgp graceful-restart preserve-fw-state\n"); + /* Stale timer for RIB */ + if (bgp->rib_stale_time != BGP_DEFAULT_RIB_STALE_TIME) + vty_out(vty, + " bgp graceful-restart rib-stale-time %u\n", + bgp->rib_stale_time); + /* BGP bestpath method. */ if (bgp_flag_check(bgp, BGP_FLAG_ASPATH_IGNORE)) vty_out(vty, " bgp bestpath as-path ignore\n"); @@ -14346,17 +15550,51 @@ void bgp_vty_init(void) install_element(BGP_NODE, &bgp_deterministic_med_cmd); install_element(BGP_NODE, &no_bgp_deterministic_med_cmd); - /* "bgp graceful-restart" commands */ - install_element(BGP_NODE, &bgp_graceful_restart_cmd); - install_element(BGP_NODE, &no_bgp_graceful_restart_cmd); + /* "bgp graceful-restart" command */ + install_element(BGP_NODE, + &bgp_graceful_restart_cmd); + install_element(BGP_NODE, + &no_bgp_graceful_restart_cmd); + + /* "bgp graceful-restart-disable" command */ + install_element(BGP_NODE, + &bgp_graceful_restart_disable_cmd); + install_element(BGP_NODE, + &no_bgp_graceful_restart_disable_cmd); + + /* "neighbor a:b:c:d graceful-restart" command */ + install_element(BGP_NODE, + &bgp_neighbor_graceful_restart_set_cmd); + install_element(BGP_NODE, + &no_bgp_neighbor_graceful_restart_set_cmd); + + /* "neighbor a:b:c:d graceful-restart-disable" command */ + install_element(BGP_NODE, + &bgp_neighbor_graceful_restart_disable_set_cmd); + install_element(BGP_NODE, + &no_bgp_neighbor_graceful_restart_disable_set_cmd); + + /* "neighbor a:b:c:d graceful-restart-helper" command */ + install_element(BGP_NODE, + &bgp_neighbor_graceful_restart_helper_set_cmd); + install_element(BGP_NODE, + &no_bgp_neighbor_graceful_restart_helper_set_cmd); + install_element(BGP_NODE, &bgp_graceful_restart_stalepath_time_cmd); install_element(BGP_NODE, &no_bgp_graceful_restart_stalepath_time_cmd); install_element(BGP_NODE, &bgp_graceful_restart_restart_time_cmd); install_element(BGP_NODE, &no_bgp_graceful_restart_restart_time_cmd); - + install_element(BGP_NODE, &bgp_graceful_restart_select_defer_time_cmd); + install_element(BGP_NODE, + &no_bgp_graceful_restart_select_defer_time_cmd); install_element(BGP_NODE, &bgp_graceful_restart_preserve_fw_cmd); install_element(BGP_NODE, &no_bgp_graceful_restart_preserve_fw_cmd); + install_element(BGP_NODE, &bgp_graceful_restart_disable_eor_cmd); + install_element(BGP_NODE, &no_bgp_graceful_restart_disable_eor_cmd); + install_element(BGP_NODE, &bgp_graceful_restart_rib_stale_time_cmd); + install_element(BGP_NODE, &no_bgp_graceful_restart_rib_stale_time_cmd); + /* "bgp graceful-shutdown" commands */ install_element(BGP_NODE, &bgp_graceful_shutdown_cmd); install_element(BGP_NODE, &no_bgp_graceful_shutdown_cmd); @@ -15351,6 +16589,9 @@ void bgp_vty_init(void) /* "show [ip] bgp neighbors" commands. */ install_element(VIEW_NODE, &show_ip_bgp_neighbors_cmd); + install_element(VIEW_NODE, + &show_ip_bgp_neighbors_graceful_restart_cmd); + /* "show [ip] bgp peer-group" commands. */ install_element(VIEW_NODE, &show_ip_bgp_peer_groups_cmd); diff --git a/bgpd/bgp_vty.h b/bgpd/bgp_vty.h index 5f3ce9cd8e..2e33ed59b9 100644 --- a/bgpd/bgp_vty.h +++ b/bgpd/bgp_vty.h @@ -22,7 +22,7 @@ #define _QUAGGA_BGP_VTY_H #include "bgpd/bgpd.h" - +#include "stream.h" struct bgp; #define BGP_INSTANCE_HELP_STR "BGP view\nBGP VRF\nView/VRF name\n" @@ -46,6 +46,110 @@ struct bgp; "Address Family modifier\n" \ "Address Family modifier\n" +#define SHOW_GR_HEADER \ + "Codes: GR - Graceful Restart," \ + " * - Inheriting Global GR Config,\n" \ + " Restart - GR Mode-Restarting," \ + " Helper - GR Mode-Helper,\n" \ + " Disable - GR Mode-Disable.\n\n" + +#define BGP_SHOW_PEER_GR_CAPABILITY( \ + vty, p, use_json, json) \ + do { \ + bgp_show_neighbor_graceful_restart_local_mode( \ + vty, p, use_json, json); \ + bgp_show_neighbor_graceful_restart_remote_mode( \ + vty, p, use_json, json); \ + bgp_show_neighnor_graceful_restart_rbit( \ + vty, p, use_json, json); \ + bgp_show_neighbor_graceful_restart_time( \ + vty, p, use_json, json); \ + bgp_show_neighbor_graceful_restart_capability_per_afi_safi(\ + vty, p, use_json, json); \ + } while (0) + +#define VTY_BGP_GR_DEFINE_LOOP_VARIABLE \ + struct peer *peer_loop = NULL; \ + struct listnode *node = NULL; \ + struct listnode *nnode = NULL; \ + bool gr_router_detected = false + +#define VTY_BGP_GR_ROUTER_DETECT( \ + _bgp, _peer, _peer_list) \ + do { \ + if (_peer->bgp->t_startup) \ + bgp_peer_gr_flags_update(_peer); \ + for (ALL_LIST_ELEMENTS(_peer_list, \ + node, nnode, peer_loop)) { \ + if (CHECK_FLAG(peer_loop->flags,\ + PEER_FLAG_GRACEFUL_RESTART)) \ + gr_router_detected = true; \ + } \ + } while (0) + + +#define VTY_SEND_BGP_GR_CAPABILITY_TO_ZEBRA(_bgp, _ret) \ + do { \ + if (gr_router_detected && \ + _bgp->present_zebra_gr_state == ZEBRA_GR_DISABLE) { \ + if (bgp_zebra_send_capabilities(_bgp, false)) \ + _ret = BGP_ERR_INVALID_VALUE;\ + } else if (!gr_router_detected && \ + _bgp->present_zebra_gr_state == ZEBRA_GR_ENABLE) { \ + if (bgp_zebra_send_capabilities(_bgp, true)) \ + _ret = BGP_ERR_INVALID_VALUE;\ + } \ + } while (0) + +#define VTY_BGP_GR_ROUTER_DETECT_AND_SEND_CAPABILITY_TO_ZEBRA( \ + _bgp, _peer_list, _ret) \ + do { \ + struct peer *peer_loop; \ + bool gr_router_detected = false; \ + struct listnode *node = {0}; \ + struct listnode *nnode = {0}; \ + for (ALL_LIST_ELEMENTS( \ + _peer_list, node, \ + nnode, peer_loop)) { \ + if (peer_loop->bgp->t_startup) \ + bgp_peer_gr_flags_update(peer_loop); \ + if (CHECK_FLAG( \ + peer_loop->flags, \ + PEER_FLAG_GRACEFUL_RESTART)) \ + gr_router_detected = true; \ + } \ + if (gr_router_detected && \ + _bgp->present_zebra_gr_state == ZEBRA_GR_DISABLE) { \ + if (bgp_zebra_send_capabilities(_bgp, false)) \ + _ret = BGP_ERR_INVALID_VALUE;\ + } else if (!gr_router_detected && \ + _bgp->present_zebra_gr_state == ZEBRA_GR_ENABLE) { \ + if (bgp_zebra_send_capabilities(_bgp, true)) \ + _ret = BGP_ERR_INVALID_VALUE;\ + } \ + } while (0) + + +#define PRINT_EOR(_eor_flag) \ + do { \ + if (eor_flag) \ + vty_out(vty, "Yes\n"); \ + else \ + vty_out(vty, "No\n"); \ + } while (0) + +#define PRINT_EOR_JSON(_eor_flag) \ + do { \ + if (eor_flag) \ + json_object_boolean_true_add( \ + json_endofrib_status, \ + "endOfRibSentAfterUpdate"); \ + else \ + json_object_boolean_false_add( \ + json_endofrib_status, \ + "endOfRibSentAfterUpdate"); \ + } while (0) + extern void bgp_vty_init(void); extern const char *get_afi_safi_str(afi_t afi, safi_t safi, bool for_json); extern int bgp_get_vty(struct bgp **bgp, as_t *as, const char *name, diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c index c99ddaf0a6..03d5cd984f 100644 --- a/bgpd/bgp_zebra.c +++ b/bgpd/bgp_zebra.c @@ -2437,6 +2437,7 @@ static void bgp_zebra_connected(struct zclient *zclient) /* TODO - What if we have peers and networks configured, do we have to * kick-start them? */ + BGP_GR_ROUTER_DETECT_AND_SEND_CAPABILITY_TO_ZEBRA(bgp, bgp->peer); } static int bgp_zebra_process_local_es(ZAPI_CALLBACK_ARGS) @@ -3016,3 +3017,157 @@ void bgp_zebra_announce_default(struct bgp *bgp, struct nexthop *nh, return; } } + +/* Send capabilities to RIB */ +int bgp_zebra_send_capabilities(struct bgp *bgp, bool disable) +{ + struct zapi_cap api; + int ret = BGP_GR_SUCCESS; + + if (zclient == NULL) { + if (BGP_DEBUG(zebra, ZEBRA)) + zlog_debug("zclient invalid"); + return BGP_GR_FAILURE; + } + + /* Check if the client is connected */ + if ((zclient->sock < 0) || (zclient->t_connect)) { + if (BGP_DEBUG(zebra, ZEBRA)) + zlog_debug("client not connected"); + return BGP_GR_FAILURE; + } + + /* Check if capability is already sent. If the flag force is set + * send the capability since this can be initial bgp configuration + */ + memset(&api, 0, sizeof(struct zapi_cap)); + if (disable) { + api.cap = ZEBRA_CLIENT_GR_DISABLE; + api.vrf_id = bgp->vrf_id; + } else { + api.cap = ZEBRA_CLIENT_GR_CAPABILITIES; + api.stale_removal_time = bgp->rib_stale_time; + api.vrf_id = bgp->vrf_id; + } + + if (zclient_capabilities_send(ZEBRA_CLIENT_CAPABILITIES, + zclient, &api) < 0) { + zlog_err("error sending capability"); + ret = BGP_GR_FAILURE; + } else { + if (disable) + bgp->present_zebra_gr_state = ZEBRA_GR_DISABLE; + else + bgp->present_zebra_gr_state = ZEBRA_GR_ENABLE; + + if (BGP_DEBUG(zebra, ZEBRA)) + zlog_debug("send capabilty success"); + ret = BGP_GR_SUCCESS; + } + return ret; +} + +/* Send route update pesding or completed status to RIB for the + * specific AFI, SAFI + */ +int bgp_zebra_update(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type) +{ + struct zapi_cap api = {0}; + + if (zclient == NULL) { + if (BGP_DEBUG(zebra, ZEBRA)) + zlog_debug("zclient == NULL, invalid"); + return BGP_GR_FAILURE; + } + + /* Check if the client is connected */ + if ((zclient->sock < 0) || (zclient->t_connect)) { + if (BGP_DEBUG(zebra, ZEBRA)) + zlog_debug("client not connected"); + return BGP_GR_FAILURE; + } + + api.afi = afi; + api.safi = safi; + api.vrf_id = vrf_id; + api.cap = type; + + if (zclient_capabilities_send(ZEBRA_CLIENT_CAPABILITIES, + zclient, &api) < 0) { + if (BGP_DEBUG(zebra, ZEBRA)) + zlog_debug("error sending capability"); + return BGP_GR_FAILURE; + } + return BGP_GR_SUCCESS; +} + + +int zclient_capabilities_send(uint32_t cmd, struct zclient *zclient, + struct zapi_cap *api) +{ + struct stream *s; + + if (zclient == NULL) + return -1; + + s = zclient->obuf; + stream_reset(s); + zclient_create_header(s, cmd, 0); + stream_putl(s, api->cap); + switch (api->cap) { + case ZEBRA_CLIENT_GR_CAPABILITIES: + case ZEBRA_CLIENT_RIB_STALE_TIME: + stream_putl(s, api->stale_removal_time); + stream_putl(s, api->vrf_id); + break; + case ZEBRA_CLIENT_ROUTE_UPDATE_COMPLETE: + case ZEBRA_CLIENT_ROUTE_UPDATE_PENDING: + stream_putl(s, api->afi); + stream_putl(s, api->safi); + stream_putl(s, api->vrf_id); + break; + case ZEBRA_CLIENT_GR_DISABLE: + stream_putl(s, api->vrf_id); + break; + default: + break; + } + + /* Put length at the first point of the stream */ + stream_putw_at(s, 0, stream_get_endp(s)); + + return zclient_send_message(zclient); +} + +/* Send RIB stale timer update */ +int bgp_zebra_stale_timer_update(struct bgp *bgp) +{ + struct zapi_cap api; + + if (zclient == NULL) { + if (BGP_DEBUG(zebra, ZEBRA)) + zlog_debug("zclient invalid"); + return BGP_GR_FAILURE; + } + + /* Check if the client is connected */ + if ((zclient->sock < 0) || (zclient->t_connect)) { + if (BGP_DEBUG(zebra, ZEBRA)) + zlog_debug("client not connected"); + return BGP_GR_FAILURE; + } + + memset(&api, 0, sizeof(struct zapi_cap)); + api.cap = ZEBRA_CLIENT_RIB_STALE_TIME; + api.stale_removal_time = bgp->rib_stale_time; + api.vrf_id = bgp->vrf_id; + if (zclient_capabilities_send(ZEBRA_CLIENT_CAPABILITIES, + zclient, &api) < 0) { + if (BGP_DEBUG(zebra, ZEBRA)) + zlog_debug("error sending capability"); + return BGP_GR_FAILURE; + } + if (BGP_DEBUG(zebra, ZEBRA)) + zlog_debug("send capabilty success"); + return BGP_GR_SUCCESS; +} diff --git a/bgpd/bgp_zebra.h b/bgpd/bgp_zebra.h index 62c311cc1d..c0e305286e 100644 --- a/bgpd/bgp_zebra.h +++ b/bgpd/bgp_zebra.h @@ -80,11 +80,11 @@ extern int bgp_zebra_num_connects(void); extern bool bgp_zebra_nexthop_set(union sockunion *, union sockunion *, struct bgp_nexthop *, struct peer *); - struct bgp_pbr_action; struct bgp_pbr_match; struct bgp_pbr_rule; struct bgp_pbr_match_entry; + extern void bgp_send_pbr_rule_action(struct bgp_pbr_action *pbra, struct bgp_pbr_rule *pbr, bool install); @@ -98,5 +98,8 @@ extern void bgp_send_pbr_iptable(struct bgp_pbr_action *pba, extern void bgp_zebra_announce_default(struct bgp *bgp, struct nexthop *nh, afi_t afi, uint32_t table_id, bool announce); - +extern int bgp_zebra_send_capabilities(struct bgp *bgp, bool disable); +extern int bgp_zebra_update(afi_t afi, safi_t safi, vrf_id_t vrf_id, + int type); +extern int bgp_zebra_stale_timer_update(struct bgp *bgp); #endif /* _QUAGGA_BGP_ZEBRA_H */ diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index 9b0e81491a..d866ba7f5d 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -1102,6 +1102,119 @@ struct peer *peer_unlock_with_caller(const char *name, struct peer *peer) return peer; } +/* BGP GR changes */ + +int bgp_global_gr_init(struct bgp *bgp) +{ + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug("%s called ..", __func__); + + int local_GLOBAL_GR_FSM[BGP_GLOBAL_GR_MODE][BGP_GLOBAL_GR_EVENT_CMD] = { + /* GLOBAL_HELPER Mode */ + { + /*Event -> */ + /*GLOBAL_GR_cmd*/ /*no_Global_GR_cmd*/ + GLOBAL_GR, GLOBAL_INVALID, + /*GLOBAL_DISABLE_cmd*/ /*no_Global_Disable_cmd*/ + GLOBAL_DISABLE, GLOBAL_INVALID + }, + /* GLOBAL_GR Mode */ + { + /*Event -> */ + /*GLOBAL_GR_cmd*/ /*no_Global_GR_cmd*/ + GLOBAL_INVALID, GLOBAL_HELPER, + /*GLOBAL_DISABLE_cmd*/ /*no_Global_Disable_cmd*/ + GLOBAL_DISABLE, GLOBAL_INVALID + }, + /* GLOBAL_DISABLE Mode */ + { + /*Event -> */ + /*GLOBAL_GR_cmd */ /*no_Global_GR_cmd*/ + GLOBAL_GR, GLOBAL_INVALID, + /*GLOBAL_DISABLE_cmd*//*no_Global_Disable_cmd*/ + GLOBAL_INVALID, GLOBAL_HELPER + }, + /* GLOBAL_INVALID Mode */ + { + /*Event -> */ + /*GLOBAL_GR_cmd*/ /*no_Global_GR_cmd*/ + GLOBAL_INVALID, GLOBAL_INVALID, + /*GLOBAL_DISABLE_cmd*/ /*no_Global_Disable_cmd*/ + GLOBAL_INVALID, GLOBAL_INVALID + } + }; + memcpy(bgp->GLOBAL_GR_FSM, local_GLOBAL_GR_FSM, + sizeof(local_GLOBAL_GR_FSM)); + + bgp->global_gr_present_state = GLOBAL_HELPER; + bgp->present_zebra_gr_state = ZEBRA_GR_DISABLE; + + return BGP_GR_SUCCESS; +} + +int bgp_peer_gr_init(struct peer *peer) +{ + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug("%s called ..", __func__); + + struct bgp_peer_gr local_Peer_GR_FSM[BGP_PEER_GR_MODE] + [BGP_PEER_GR_EVENT_CMD] = { + { + /* PEER_HELPER Mode */ + /* Event-> */ /* PEER_GR_CMD */ /* NO_PEER_GR_CMD */ + { PEER_GR, bgp_peer_gr_action }, {PEER_INVALID, NULL }, + /* Event-> */ /* PEER_DISABLE_CMD */ /* NO_PEER_DISABLE_CMD */ + {PEER_DISABLE, bgp_peer_gr_action }, {PEER_INVALID, NULL }, + /* Event-> */ /* PEER_HELPER_cmd */ /* NO_PEER_HELPER_CMD */ + { PEER_INVALID, NULL }, {PEER_GLOBAL_INHERIT, + bgp_peer_gr_action } + }, + { + /* PEER_GR Mode */ + /* Event-> */ /* PEER_GR_CMD */ /* NO_PEER_GR_CMD */ + { PEER_INVALID, NULL }, { PEER_GLOBAL_INHERIT, + bgp_peer_gr_action }, + /* Event-> */ /* PEER_DISABLE_CMD */ /* NO_PEER_DISABLE_CMD */ + {PEER_DISABLE, bgp_peer_gr_action }, { PEER_INVALID, NULL }, + /* Event-> */ /* PEER_HELPER_cmd */ /* NO_PEER_HELPER_CMD */ + { PEER_HELPER, bgp_peer_gr_action }, { PEER_INVALID, NULL } + }, + { + /* PEER_DISABLE Mode */ + /* Event-> */ /* PEER_GR_CMD */ /* NO_PEER_GR_CMD */ + { PEER_GR, bgp_peer_gr_action }, { PEER_INVALID, NULL }, + /* Event-> */ /* PEER_DISABLE_CMD */ /* NO_PEER_DISABLE_CMD */ + { PEER_INVALID, NULL }, { PEER_GLOBAL_INHERIT, + bgp_peer_gr_action }, + /* Event-> */ /* PEER_HELPER_cmd */ /* NO_PEER_HELPER_CMD */ + { PEER_HELPER, bgp_peer_gr_action }, { PEER_INVALID, NULL } + }, + { + /* PEER_INVALID Mode */ + /* Event-> */ /* PEER_GR_CMD */ /* NO_PEER_GR_CMD */ + { PEER_INVALID, NULL }, { PEER_INVALID, NULL }, + /* Event-> */ /* PEER_DISABLE_CMD */ /* NO_PEER_DISABLE_CMD */ + { PEER_INVALID, NULL }, { PEER_INVALID, NULL }, + /* Event-> */ /* PEER_HELPER_cmd */ /* NO_PEER_HELPER_CMD */ + { PEER_INVALID, NULL }, { PEER_INVALID, NULL }, + }, + { + /* PEER_GLOBAL_INHERIT Mode */ + /* Event-> */ /* PEER_GR_CMD */ /* NO_PEER_GR_CMD */ + { PEER_GR, bgp_peer_gr_action }, { PEER_INVALID, NULL }, + /* Event-> */ /* PEER_DISABLE_CMD */ /* NO_PEER_DISABLE_CMD */ + { PEER_DISABLE, bgp_peer_gr_action}, { PEER_INVALID, NULL }, + /* Event-> */ /* PEER_HELPER_cmd */ /* NO_PEER_HELPER_CMD */ + { PEER_HELPER, bgp_peer_gr_action }, { PEER_INVALID, NULL } + } + }; + memcpy(&peer->PEER_GR_FSM, local_Peer_GR_FSM, + sizeof(local_Peer_GR_FSM)); + peer->peer_gr_present_state = PEER_GLOBAL_INHERIT; + bgp_peer_move_to_gr_mode(peer, PEER_GLOBAL_INHERIT); + + return BGP_GR_SUCCESS; +} /* Allocate new peer object, implicitely locked. */ struct peer *peer_new(struct bgp *bgp) @@ -1153,6 +1266,9 @@ struct peer *peer_new(struct bgp *bgp) SET_FLAG(peer->sflags, PEER_STATUS_CAPABILITY_OPEN); + /* Initialize per peer bgp GR FSM */ + bgp_peer_gr_init(peer); + /* Create buffers. */ peer->ibuf = stream_fifo_new(); peer->obuf = stream_fifo_new(); @@ -1212,6 +1328,9 @@ void peer_xfer_config(struct peer *peer_dst, struct peer *peer_src) peer_dst->flags = peer_src->flags; peer_dst->cap = peer_src->cap; + peer_dst->peer_gr_present_state = peer_src->peer_gr_present_state; + peer_dst->peer_gr_new_status_flag = peer_src->peer_gr_new_status_flag; + peer_dst->local_as = peer_src->local_as; peer_dst->port = peer_src->port; (void)peer_sort(peer_dst); @@ -1534,6 +1653,11 @@ struct peer *peer_create(union sockunion *su, const char *conf_if, else if (!active && peer_active(peer)) bgp_timer_set(peer); + bgp_peer_gr_flags_update(peer); + BGP_GR_ROUTER_DETECT_AND_SEND_CAPABILITY_TO_ZEBRA( + bgp, + bgp->peer); + return peer; } @@ -2098,7 +2222,7 @@ int peer_afc_set(struct peer *peer, afi_t afi, safi_t safi, int enable) return peer_deactivate(peer, afi, safi); } -static void peer_nsf_stop(struct peer *peer) +void peer_nsf_stop(struct peer *peer) { afi_t afi; safi_t safi; @@ -2902,6 +3026,12 @@ static struct bgp *bgp_create(as_t *as, const char *name, multipath_num, 0); bgp_maximum_paths_set(bgp, afi, safi, BGP_PEER_IBGP, multipath_num, 0); + /* Initialize graceful restart info */ + bgp->gr_info[afi][safi].eor_required = 0; + bgp->gr_info[afi][safi].eor_received = 0; + bgp->gr_info[afi][safi].t_select_deferral = NULL; + bgp->gr_info[afi][safi].t_route_select = NULL; + bgp->gr_info[afi][safi].route_list = list_new(); } bgp->v_update_delay = BGP_UPDATE_DELAY_DEF; @@ -2911,6 +3041,8 @@ static struct bgp *bgp_create(as_t *as, const char *name, bgp_timers_unset(bgp); bgp->restart_time = BGP_DEFAULT_RESTART_TIME; bgp->stalepath_time = BGP_DEFAULT_STALEPATH_TIME; + bgp->select_defer_time = BGP_DEFAULT_SELECT_DEFERRAL_TIME; + bgp->rib_stale_time = BGP_DEFAULT_RIB_STALE_TIME; bgp->dynamic_neighbors_limit = BGP_DYNAMIC_NEIGHBORS_LIMIT_DEFAULT; bgp->dynamic_neighbors_count = 0; bgp->ebgp_requires_policy = DEFAULT_EBGP_POLICY_DISABLED; @@ -2941,14 +3073,11 @@ static struct bgp *bgp_create(as_t *as, const char *name, bgp->vpn_policy[afi].export_vrf->del = bgp_vrf_string_name_delete; } - if (name) { + if (name) bgp->name = XSTRDUP(MTYPE_BGP, name); - } else { - /* TODO - The startup timer needs to be run for the whole of BGP - */ - thread_add_timer(bm->master, bgp_startup_timer_expire, bgp, - bgp->restart_time, &bgp->t_startup); - } + + thread_add_timer(bm->master, bgp_startup_timer_expire, bgp, + bgp->restart_time, &bgp->t_startup); /* printable name we can use in debug messages */ if (inst_type == BGP_INSTANCE_TYPE_DEFAULT) { @@ -2990,6 +3119,9 @@ static struct bgp *bgp_create(as_t *as, const char *name, bgp_evpn_init(bgp); bgp_pbr_init(bgp); + + /*initilize global GR FSM */ + bgp_global_gr_init(bgp); return bgp; } @@ -3233,7 +3365,9 @@ int bgp_delete(struct bgp *bgp) struct listnode *node, *next; struct vrf *vrf; afi_t afi; + safi_t safi; int i; + struct graceful_restart_info *gr_info; assert(bgp); @@ -3247,6 +3381,19 @@ int bgp_delete(struct bgp *bgp) /* Set flag indicating bgp instance delete in progress */ bgp_flag_set(bgp, BGP_FLAG_DELETE_IN_PROGRESS); + /* Delete the graceful restart info */ + FOREACH_AFI_SAFI (afi, safi) { + gr_info = &bgp->gr_info[afi][safi]; + if (gr_info) { + BGP_TIMER_OFF(gr_info->t_select_deferral); + gr_info->t_select_deferral = NULL; + BGP_TIMER_OFF(gr_info->t_route_select); + gr_info->t_route_select = NULL; + if (gr_info->route_list) + list_delete(&gr_info->route_list); + } + } + if (BGP_DEBUG(zebra, ZEBRA)) { if (bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT) zlog_debug("Deleting Default VRF"); @@ -7099,3 +7246,33 @@ struct peer *peer_lookup_in_view(struct vty *vty, struct bgp *bgp, return peer; } +void bgp_gr_apply_running_config(void) +{ + struct peer *peer = NULL; + struct bgp *bgp = NULL; + struct listnode *node, *nnode; + bool gr_router_detected = false; + + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug("[BGP_GR] %s called !", + __func__); + + for (ALL_LIST_ELEMENTS(bm->bgp, node, nnode, bgp)) { + for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) { + bgp_peer_gr_flags_update(peer); + if (CHECK_FLAG(peer->flags, + PEER_FLAG_GRACEFUL_RESTART)) + gr_router_detected = true; + } + + if (gr_router_detected && + bgp->present_zebra_gr_state == ZEBRA_GR_DISABLE) { + bgp_zebra_send_capabilities(bgp, true); + } else if (!gr_router_detected && + bgp->present_zebra_gr_state == ZEBRA_GR_ENABLE) { + bgp_zebra_send_capabilities(bgp, false); + } + + gr_router_detected = false; + } +} diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h index 4c4787ed5b..3f33139ef2 100644 --- a/bgpd/bgpd.h +++ b/bgpd/bgpd.h @@ -65,6 +65,12 @@ enum { AS_UNSPECIFIED = 0, AS_EXTERNAL, }; +/* Zebra Gracaful Restart states */ +enum zebra_gr_mode { + ZEBRA_GR_DISABLE = 0, + ZEBRA_GR_ENABLE +}; + /* Typedef BGP specific types. */ typedef uint32_t as_t; typedef uint16_t as16_t; /* we may still encounter 16 Bit asnums */ @@ -232,6 +238,52 @@ enum bgp_instance_type { BGP_INSTANCE_TYPE_VIEW }; +#define BGP_SEND_EOR(bgp, afi, safi) \ + (!bgp_flag_check(bgp, BGP_FLAG_GR_DISABLE_EOR) && \ + ((bgp->gr_info[afi][safi].t_select_deferral == NULL) || \ + (bgp->gr_info[afi][safi].eor_required == \ + bgp->gr_info[afi][safi].eor_received))) + +/* BGP GR Global ds */ + +#define BGP_GLOBAL_GR_MODE 4 +#define BGP_GLOBAL_GR_EVENT_CMD 4 + +/* Graceful restart selection deferral timer info */ +struct graceful_restart_info { + /* Count of EOR message expected */ + uint32_t eor_required; + /* Count of EOR received */ + uint32_t eor_received; + /* Deferral Timer */ + struct thread *t_select_deferral; + /* Route list */ + struct list *route_list; + /* Best route select */ + struct thread *t_route_select; + /* AFI, SAFI enabled */ + bool af_enabled[AFI_MAX][SAFI_MAX]; + /* Route update completed */ + bool route_sync[AFI_MAX][SAFI_MAX]; +}; + +enum global_mode { + GLOBAL_HELPER = 0, /* This is the default mode */ + GLOBAL_GR, + GLOBAL_DISABLE, + GLOBAL_INVALID +}; + +enum global_gr_command { + GLOBAL_GR_CMD = 0, + NO_GLOBAL_GR_CMD, + GLOBAL_DISABLE_CMD, + NO_GLOBAL_DISABLE_CMD +}; + +#define BGP_GR_SUCCESS 0 +#define BGP_GR_FAILURE 1 + /* BGP instance structure. */ struct bgp { /* AS number of this BGP instance. */ @@ -356,7 +408,10 @@ struct bgp { #define BGP_FLAG_IMPORT_CHECK (1 << 9) #define BGP_FLAG_NO_FAST_EXT_FAILOVER (1 << 10) #define BGP_FLAG_LOG_NEIGHBOR_CHANGES (1 << 11) + +/* This flag is set when we have full BGP Graceful-Restart mode enable */ #define BGP_FLAG_GRACEFUL_RESTART (1 << 12) + #define BGP_FLAG_ASPATH_CONFED (1 << 13) #define BGP_FLAG_ASPATH_MULTIPATH_RELAX (1 << 14) #define BGP_FLAG_RR_ALLOW_OUTBOUND_POLICY (1 << 15) @@ -367,6 +422,17 @@ struct bgp { #define BGP_FLAG_GR_PRESERVE_FWD (1 << 20) #define BGP_FLAG_GRACEFUL_SHUTDOWN (1 << 21) #define BGP_FLAG_DELETE_IN_PROGRESS (1 << 22) +#define BGP_FLAG_SELECT_DEFER_DISABLE (1 << 23) +#define BGP_FLAG_GR_DISABLE_EOR (1 << 24) + + enum global_mode GLOBAL_GR_FSM[BGP_GLOBAL_GR_MODE] + [BGP_GLOBAL_GR_EVENT_CMD]; + enum global_mode global_gr_present_state; + + /* This variable stores the current Graceful Restart state of Zebra + * - ZEBRA_GR_ENABLE / ZEBRA_GR_DISABLE + */ + enum zebra_gr_mode present_zebra_gr_state; /* BGP Per AF flags */ uint16_t af_flags[AFI_MAX][SAFI_MAX]; @@ -462,7 +528,12 @@ struct bgp { /* BGP graceful restart */ uint32_t restart_time; uint32_t stalepath_time; + uint32_t select_defer_time; + struct graceful_restart_info gr_info[AFI_MAX][SAFI_MAX]; + uint32_t rib_stale_time; +#define BGP_ROUTE_SELECT_DELAY 1 +#define BGP_MAX_BEST_ROUTE_SELECT 10000 /* Maximum-paths configuration */ struct bgp_maxpaths_cfg { uint16_t maxpaths_ebgp; @@ -548,7 +619,6 @@ struct bgp { #define BGP_VRF_RD_CFGD (1 << 3) #define BGP_VRF_L3VNI_PREFIX_ROUTES_ONLY (1 << 4) - /* unique ID for auto derivation of RD for this vrf */ uint16_t vrf_rd_id; @@ -589,6 +659,13 @@ DECLARE_HOOK(bgp_inst_config_write, (struct bgp *bgp, struct vty *vty), (bgp, vty)) + /* Thread callback information */ + struct afi_safi_info { + afi_t afi; + safi_t safi; + struct bgp *bgp; + }; + #define BGP_ROUTE_ADV_HOLD(bgp) (bgp->main_peers_update_hold) #define IS_BGP_INST_KNOWN_TO_ZEBRA(bgp) \ @@ -596,6 +673,9 @@ DECLARE_HOOK(bgp_inst_config_write, || (bgp->inst_type == BGP_INSTANCE_TYPE_VRF \ && bgp->vrf_id != VRF_UNKNOWN)) +#define BGP_SELECT_DEFER_DISABLE(bgp) \ + (bgp_flag_check(bgp, BGP_FLAG_SELECT_DEFER_DISABLE)) + /* BGP peer-group support. */ struct peer_group { /* Name of the peer-group. */ @@ -726,6 +806,36 @@ struct peer_af { safi_t safi; int afid; }; +/* BGP GR per peer ds */ + +#define BGP_PEER_GR_MODE 5 +#define BGP_PEER_GR_EVENT_CMD 6 + +enum peer_mode { + PEER_HELPER = 0, + PEER_GR, + PEER_DISABLE, + PEER_INVALID, + PEER_GLOBAL_INHERIT /* This is the default mode */ + +}; + +enum peer_gr_command { + PEER_GR_CMD = 0, + NO_PEER_GR_CMD, + PEER_DISABLE_CMD, + NO_PEER_DISABLE_CMD, + PEER_HELPER_CMD, + NO_PEER_HELPER_CMD +}; + +typedef unsigned int (*bgp_peer_gr_action_ptr)(struct peer *, int, int); + +struct bgp_peer_gr { + enum peer_mode next_state; + bgp_peer_gr_action_ptr action_fun; +}; + /* BGP neighbor structure. */ struct peer { @@ -947,11 +1057,36 @@ struct peer { #define PEER_FLAG_LOCAL_AS (1 << 21) /* local-as */ #define PEER_FLAG_UPDATE_SOURCE (1 << 22) /* update-source */ + /* BGP-GR Peer related flags */ +#define PEER_FLAG_GRACEFUL_RESTART_HELPER (1 << 23) /* Helper */ +#define PEER_FLAG_GRACEFUL_RESTART (1 << 24) /* Graceful Restart */ +#define PEER_FLAG_GRACEFUL_RESTART_GLOBAL_INHERIT (1 << 25) /* Global-Inherit */ + + /* + *GR-Disabled mode means unset PEER_FLAG_GRACEFUL_RESTART + *& PEER_FLAG_GRACEFUL_RESTART_HELPER + *and PEER_FLAG_GRACEFUL_RESTART_GLOBAL_INHERIT + */ + + struct bgp_peer_gr PEER_GR_FSM[BGP_PEER_GR_MODE][BGP_PEER_GR_EVENT_CMD]; + enum peer_mode peer_gr_present_state; + /* Non stop forwarding afi-safi count for BGP gr feature*/ + uint8_t nsf_af_count; + + uint8_t peer_gr_new_status_flag; +#define PEER_GRACEFUL_RESTART_NEW_STATE_HELPER (1 << 0) +#define PEER_GRACEFUL_RESTART_NEW_STATE_RESTART (1 << 1) +#define PEER_GRACEFUL_RESTART_NEW_STATE_INHERIT (1 << 2) + /* outgoing message sent in CEASE_ADMIN_SHUTDOWN notify */ char *tx_shutdown_message; /* NSF mode (graceful restart) */ uint8_t nsf[AFI_MAX][SAFI_MAX]; + /* EOR Send time */ + time_t eor_stime[AFI_MAX][SAFI_MAX]; + /* Last update packet sent time */ + time_t pkt_stime[AFI_MAX][SAFI_MAX]; /* Peer Per AF flags */ /* @@ -1456,6 +1591,8 @@ struct bgp_nlri { /* BGP graceful restart */ #define BGP_DEFAULT_RESTART_TIME 120 #define BGP_DEFAULT_STALEPATH_TIME 360 +#define BGP_DEFAULT_SELECT_DEFERRAL_TIME 360 +#define BGP_DEFAULT_RIB_STALE_TIME 500 /* BGP uptime string length. */ #define BGP_UPTIME_LEN 25 @@ -1521,6 +1658,11 @@ enum bgp_clear_type { #define BGP_ERR_INVALID_FOR_DIRECT_PEER -34 #define BGP_ERR_PEER_SAFI_CONFLICT -35 +/* BGP GR ERRORS */ +#define BGP_ERR_GR_INVALID_CMD -36 +#define BGP_ERR_GR_OPERATION_FAILED -37 +#define BGP_GR_NO_OPERATION -38 + /* * Enumeration of different policy kinds a peer can be configured with. */ @@ -1773,6 +1915,36 @@ extern int peer_af_delete(struct peer *, afi_t, safi_t); extern void bgp_close(void); extern void bgp_free(struct bgp *); +void bgp_gr_apply_running_config(void); + +/* BGP GR */ +int bgp_global_gr_init(struct bgp *bgp); +int bgp_peer_gr_init(struct peer *peer); + + +#define BGP_GR_ROUTER_DETECT_AND_SEND_CAPABILITY_TO_ZEBRA( \ + _bgp, _peer_list) \ +do { \ + struct peer *peer_loop; \ + bool gr_router_detected = false; \ + struct listnode *node = {0}; \ + struct listnode *nnode = {0}; \ + for (ALL_LIST_ELEMENTS( \ + _peer_list, node, \ + nnode, peer_loop)) { \ + if (CHECK_FLAG( \ + peer_loop->flags, \ + PEER_FLAG_GRACEFUL_RESTART)) \ + gr_router_detected = true; \ + } \ + if (gr_router_detected && \ + _bgp->present_zebra_gr_state == ZEBRA_GR_DISABLE) { \ + bgp_zebra_send_capabilities(_bgp, false); \ + } else if (!gr_router_detected && \ + _bgp->present_zebra_gr_state == ZEBRA_GR_ENABLE) { \ + bgp_zebra_send_capabilities(_bgp, true); \ + } \ +} while (0) static inline struct bgp *bgp_lock(struct bgp *bgp) { @@ -1962,5 +2134,6 @@ extern struct peer *peer_lookup_in_view(struct vty *vty, struct bgp *bgp, /* Hooks */ DECLARE_HOOK(peer_status_changed, (struct peer * peer), (peer)) +void peer_nsf_stop(struct peer *peer); #endif /* _QUAGGA_BGPD_H */ diff --git a/doc/user/bgp.rst b/doc/user/bgp.rst index d3ac4b22ab..81b4e34647 100644 --- a/doc/user/bgp.rst +++ b/doc/user/bgp.rst @@ -694,6 +694,181 @@ from eBGP peers, :ref:`bgp-route-selection`. MED as an intra-AS metric to steer equal-length AS_PATH routes to, e.g., desired exit points. + +.. _bgp-graceful-restart: + +Graceful Restart +---------------- + +BGP graceful restart functionality as defined in +`RFC-4724 <https://tools.ietf.org/html/rfc4724/>`_ defines the mechanisms that +allows BGP speaker to continue to forward data packets along known routes +while the routing protocol information is being restored. + + +Usually, when BGP on a router restarts, all the BGP peers detect that the +session went down and then came up. This "down/up" transition results in a +"routing flap" and causes BGP route re-computation, generation of BGP routing +updates, and unnecessary churn to the forwarding tables. + +The following functionality is provided by graceful restart: + +1. The feature allows the restarting router to indicate to the helping peer the + routes it can preserve in its forwarding plane during control plane restart + by sending graceful restart capability in the OPEN message sent during + session establishment. +2. The feature allows helping router to advertise to all other peers the routes + received from the restarting router which are preserved in the forwarding + plane of the restarting router during control plane restart. + + +:: + + + + (R1)-----------------------------------------------------------------(R2) + + 1. BGP Graceful Restart Capability exchanged between R1 & R2. + + <---------------------------------------------------------------------> + + 2. Kill BGP Process at R1. + + ----------------------------------------------------------------------> + + 3. R2 Detects the above BGP Restart & verifies BGP Restarting + Capability of R1. + + 4. Start BGP Process at R1. + + 5. Re-establish the BGP session between R1 & R2. + + <---------------------------------------------------------------------> + + 6. R2 Send initial route updates, followed by End-Of-Rib. + + <---------------------------------------------------------------------- + + 7. R1 was waiting for End-Of-Rib from R2 & which has been received + now. + + 8. R1 now runs BGP Best-Path algorithm. Send Initial BGP Update, + followed by End-Of Rib + + <---------------------------------------------------------------------> + + +.. _bgp-end-of-rib-message: + +End-of-RIB (EOR) message +^^^^^^^^^^^^^^^^^^^^^^^^ + +An UPDATE message with no reachable Network Layer Reachability Information +(NLRI) and empty withdrawn NLRI is specified as the End-of-RIB marker that can +be used by a BGP speaker to indicate to its peer the completion of the initial +routing update after the session is established. + +For the IPv4 unicast address family, the End-of-RIB marker is an UPDATE message +with the minimum length. For any other address family, it is an UPDATE message +that contains only the MP_UNREACH_NLRI attribute with no withdrawn routes for +that <AFI, SAFI>. + +Although the End-of-RIB marker is specified for the purpose of BGP graceful +restart, it is noted that the generation of such a marker upon completion of +the initial update would be useful for routing convergence in general, and thus +the practice is recommended. + +.. _bgp-route-selection-deferral-timer: + +Route Selection Deferral Timer +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Specifies the time the restarting router defers the route selection process +after restart. + +Restarting Router : The usage of route election deferral timer is specified +in https://tools.ietf.org/html/rfc4724#section-4.1 + +Once the session between the Restarting Speaker and the Receiving Speaker is +re-established, the Restarting Speaker will receive and process BGP messages +from its peers. + +However, it MUST defer route selection for an address family until it either. + +1. Receives the End-of-RIB marker from all its peers (excluding the ones with + the "Restart State" bit set in the received capability and excluding the ones + that do not advertise the graceful restart capability). +2. The Selection_Deferral_Timer timeout. + +.. index:: bgp graceful-restart select-defer-time (0-3600) +.. clicmd:: bgp graceful-restart select-defer-time (0-3600) + + This is command, will set deferral time to value specified. + + +.. index:: bgp graceful-restart rib-stale-time (1-3600) +.. clicmd:: bgp graceful-restart rib-stale-time (1-3600) + + This is command, will set the time for which stale routes are kept in RIB. + +.. _bgp-per-peer-graceful-restart: + +BGP Per Peer Graceful Restart +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Ability to enable and disable graceful restart, helper and no GR at all mode +functionality at peer level. + +So bgp graceful restart can be enabled at modes global BGP level or at per +peer level. There are two FSM, one for BGP GR global mode and other for peer +per GR. + +Default global mode is helper and default peer per mode is inherit from global. +If per peer mode is configured, the GR mode of this particular peer will +override the global mode. + +.. _bgp-GR-global-mode-cmd: + +BGP GR Global Mode Commands +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. index:: bgp graceful-restart +.. clicmd:: bgp graceful-restart + + This command will enable BGP graceful restart ifunctionality at the global + level. + +.. index:: bgp graceful-restart disable +.. clicmd:: bgp graceful-restart disable + + This command will disable both the functionality graceful restart and helper + mode. + + +.. _bgp-GR-peer-mode-cmd: + +BGP GR Peer Mode Commands +^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. index:: neighbor A.B.C.D graceful-restart +.. clicmd:: neighbor A.B.C.D graceful-restart + + This command will enable BGP graceful restart ifunctionality at the peer + level. + +.. index:: neighbor A.B.C.D graceful-restart-helper +.. clicmd:: neighbor A.B.C.D graceful-restart-helper + + This command will enable BGP graceful restart helper only functionality + at the peer level. + +.. index:: neighbor A.B.C.D graceful-restart-disable +.. clicmd:: neighbor A.B.C.D graceful-restart-disable + + This command will disable the entire BGP graceful restart functionality + at the peer level. + + .. _bgp-network: Networks diff --git a/lib/command.h b/lib/command.h index c8dd04604d..ea8a76a964 100644 --- a/lib/command.h +++ b/lib/command.h @@ -418,8 +418,23 @@ struct cmd_node { #define DAEMONS_LIST \ "<zebra|ripd|ripngd|ospfd|ospf6d|bgpd|isisd|pbrd|fabricd|pimd|staticd|sharpd|vrrpd|ldpd>" +/* Graceful Restart cli help strings */ +#define GR_CMD "Global Graceful Restart command\n" +#define NO_GR_CMD "Undo Global Graceful Restart command\n" +#define GR "Global Graceful Restart - GR Mode\n" +#define GR_DISABLE "Global Graceful Restart - Disable Mode\n" +#define NO_GR_DISABLE "Undo Global Graceful Restart - Disable Mode\n" +#define GR_DEBUG "Graceful Restart - Enable Debug Logs\n" +#define GR_SHOW "Graceful Restart - Show command for Global and all neighbor mode\n" +#define GR_NEIGHBOR_CMD "Graceful Restart command for a neighbor\n" +#define NO_GR_NEIGHBOR_CMD "Undo Graceful Restart command for a neighbor\n" +#define GR_NEIGHBOR_DISABLE_CMD "Graceful Restart Disable command for a neighbor\n" +#define NO_GR_NEIGHBOR_DISABLE_CMD "Undo Graceful Restart Disable command for a neighbor\n" +#define GR_NEIGHBOR_HELPER_CMD "Graceful Restart Helper command for a neighbor\n" +#define NO_GR_NEIGHBOR_HELPER_CMD "Undo Graceful Restart Helper command for a neighbor\n" + /* Prototypes. */ -extern void install_node(struct cmd_node *, int (*)(struct vty *)); +extern void install_node(struct cmd_node *node, int (*)(struct vty *)); extern void install_default(enum node_type); extern void install_element(enum node_type, const struct cmd_element *); diff --git a/lib/zclient.c b/lib/zclient.c index b2c74cd0b9..7ddf0085de 100644 --- a/lib/zclient.c +++ b/lib/zclient.c @@ -3299,3 +3299,32 @@ void zclient_interface_set_master(struct zclient *client, stream_putw_at(s, 0, stream_get_endp(s)); zclient_send_message(client); } + +/* Process capabilities message from zebra */ +int zapi_capabilities_decode(struct stream *s, struct zapi_cap *api) +{ + memset(api, 0, sizeof(*api)); + + STREAM_GETL(s, api->cap); + switch (api->cap) { + case ZEBRA_CLIENT_GR_CAPABILITIES: + case ZEBRA_CLIENT_RIB_STALE_TIME: + STREAM_GETL(s, api->stale_removal_time); + STREAM_GETL(s, api->vrf_id); + break; + case ZEBRA_CLIENT_ROUTE_UPDATE_COMPLETE: + case ZEBRA_CLIENT_ROUTE_UPDATE_PENDING: + STREAM_GETL(s, api->afi); + STREAM_GETL(s, api->safi); + STREAM_GETL(s, api->vrf_id); + break; + case ZEBRA_CLIENT_GR_DISABLE: + STREAM_GETL(s, api->vrf_id); + break; + default: + break; + } + +stream_failure: + return 0; +} diff --git a/lib/zclient.h b/lib/zclient.h index d1aa42da6d..bbc70c3835 100644 --- a/lib/zclient.h +++ b/lib/zclient.h @@ -73,6 +73,14 @@ typedef uint16_t zebra_size_t; #define ZEBRA_FEC_REGISTER_LABEL 0x1 #define ZEBRA_FEC_REGISTER_LABEL_INDEX 0x2 +/* Client Graceful Restart */ +#define ZEBRA_CLIENT_GR_CAPABILITIES 0x1 +#define ZEBRA_CLIENT_ROUTE_UPDATE_COMPLETE 0x2 +#define ZEBRA_CLIENT_ROUTE_UPDATE_PENDING 0x3 +#define ZEBRA_CLIENT_GR_DISABLE 0x4 +#define ZEBRA_CLIENT_RIB_STALE_TIME 0x5 +#define ZEBRA_CLIENT_GR_ENABLED(X) (X & ZEBRA_CLIENT_GR_CAPABILITIES) + extern struct sockaddr_storage zclient_addr; extern socklen_t zclient_addr_len; @@ -184,6 +192,7 @@ typedef enum { ZEBRA_MLAG_CLIENT_UNREGISTER, ZEBRA_MLAG_FORWARD_MSG, ZEBRA_ERROR, + ZEBRA_CLIENT_CAPABILITIES } zebra_message_types_t; enum zebra_error_types { @@ -222,6 +231,15 @@ struct zclient_capabilities { enum mlag_role role; }; +/* Graceful Restart Capabilities message */ +struct zapi_cap { + uint32_t cap; + uint32_t stale_removal_time; + afi_t afi; + safi_t safi; + vrf_id_t vrf_id; +}; + /* Structure for the zebra client. */ struct zclient { /* The thread master we schedule ourselves on */ @@ -776,4 +794,7 @@ extern void zclient_send_mlag_deregister(struct zclient *client); extern void zclient_send_mlag_data(struct zclient *client, struct stream *client_s); +extern int zclient_capabilities_send(uint32_t cmd, struct zclient *zclient, + struct zapi_cap *api); +extern int zapi_capabilities_decode(struct stream *s, struct zapi_cap *api); #endif /* _ZEBRA_ZCLIENT_H */ diff --git a/tests/bgpd/test_mpath.c b/tests/bgpd/test_mpath.c index 21f4b38773..a51ce4c6bc 100644 --- a/tests/bgpd/test_mpath.c +++ b/tests/bgpd/test_mpath.c @@ -297,7 +297,25 @@ struct bgp_node test_rn; static int setup_bgp_path_info_mpath_update(testcase_t *t) { int i; + struct bgp *bgp; + struct bgp_table *rt; + struct route_node *rt_node; + as_t asn = 1; + + t->tmp_data = bgp_create_fake(&asn, NULL); + if (!t->tmp_data) + return -1; + + bgp = t->tmp_data; + rt = bgp->rib[AFI_IP][SAFI_UNICAST]; + + if (!rt) + return -1; + str2prefix("42.1.1.0/24", &test_rn.p); + rt_node = bgp_node_to_rnode(&test_rn); + memcpy((struct route_table *)&rt_node->table, &rt->route_table, + sizeof(struct route_table)); setup_bgp_mp_list(t); for (i = 0; i < test_mp_list_info_count; i++) bgp_path_info_add(&test_rn, &test_mp_list_info[i]); @@ -352,7 +370,7 @@ static int cleanup_bgp_path_info_mpath_update(testcase_t *t) for (i = 0; i < test_mp_list_peer_count; i++) sockunion_free(test_mp_list_peer[i].su_remote); - return 0; + return bgp_delete((struct bgp *)t->tmp_data); } testcase_t test_bgp_path_info_mpath_update = { |
