From a2a2cfec9b4357a174aa958ab3e47a48681bacd9 Mon Sep 17 00:00:00 2001 From: hou Date: Tue, 26 Nov 2019 19:26:16 -0800 Subject: [PATCH] Add BGP dynamic capability support for graceful restart: When graceful-retart is enabled, trigger graceful restart capability in BGP capability message, so that neighbors with dynamic capability support can update graceful restart status, including forwarding state bit for each address-family without resetting adjacency. --- bgpd/bgp_open.h | 11 ++- bgpd/bgp_packet.c | 191 +++++++++++++++++++++++++++++++++++++++++++++- bgpd/bgp_vty.c | 47 +++++++++++- 3 files changed, 238 insertions(+), 11 deletions(-) diff --git a/bgpd/bgp_open.h b/bgpd/bgp_open.h index 8359f59a41..b996fcbf85 100644 --- a/bgpd/bgp_open.h +++ b/bgpd/bgp_open.h @@ -24,7 +24,7 @@ /* Standard header for capability TLV */ struct capability_header { uint8_t code; - uint8_t length; + uint8_t length; /* Note, extra one more oct in dynamic capabilities */ }; /* Generic MP capability data */ @@ -35,11 +35,16 @@ struct capability_mp_data { }; struct graceful_restart_af { - afi_t afi; - safi_t safi; + uint16_t afi; + uint8_t safi; uint8_t flag; }; +struct capability_gr { + uint16_t restart_flag_time; + struct graceful_restart_af gr[]; +}; + /* Capability Code */ #define CAPABILITY_CODE_MP 1 /* Multiprotocol Extensions */ #define CAPABILITY_CODE_REFRESH 2 /* Route Refresh Capability */ diff --git a/bgpd/bgp_packet.c b/bgpd/bgp_packet.c index 130e06a6cf..b8aadc6a41 100644 --- a/bgpd/bgp_packet.c +++ b/bgpd/bgp_packet.c @@ -863,6 +863,65 @@ void bgp_route_refresh_send(struct peer *peer, afi_t afi, safi_t safi, bgp_writes_on(peer); } +static void bgp_restart_dyn_capability_send (struct peer *peer, + int action, + struct stream *s) +{ + afi_t afi; + safi_t safi; + iana_afi_t pkt_afi; + iana_safi_t pkt_safi; + uint8_t len; + unsigned long rcapp; + uint32_t restart_time; + + stream_putc(s, action); + stream_putc(s, CAPABILITY_CODE_RESTART); + + /* Copy from optional capability */ + 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); + + 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); + } + + if (bgp_debug_neighbor_events(peer)) + zlog_debug( + "%s sending CAPABILITY has %s Graceful Restart CAP for afi/safi: %d/%d " + "is%spreserved", + peer->host, + action == CAPABILITY_ACTION_SET ? "Advertising" + : "Removing", + pkt_afi, pkt_safi, + bgp_flag_check(peer->bgp, + BGP_FLAG_GR_PRESERVE_FWD) ? + " " : "not"); + } + } + + /* Total Graceful restart capability Len. */ + len = stream_get_endp(s) - rcapp - 1; + stream_putc_at(s, rcapp, len); +} + /* * Create a BGP Capability packet and append it to the peer's output queue. * @@ -903,6 +962,8 @@ void bgp_capability_send(struct peer *peer, afi_t afi, safi_t safi, action == CAPABILITY_ACTION_SET ? "Advertising" : "Removing", pkt_afi, pkt_safi); + } else if (capability_code == CAPABILITY_CODE_RESTART) { + bgp_restart_dyn_capability_send(peer, action, s); } /* Set packet size. */ @@ -2001,6 +2062,115 @@ static int bgp_route_refresh_receive(struct peer *peer, bgp_size_t size) return BGP_PACKET_NOOP; } +static int bgp_restart_dyn_capability_parse (struct peer *peer, + u_char action, + struct capability_header *caphdr, + u_char *end) +{ + u_int16_t pkt_time, restart_flag_time; + u_char *pnt; + + /* Verify length is a multiple of 4 */ + if ((caphdr->length - 2) % 4) { + zlog_warn( + "Restart Cap: Received invalid length %d, non-multiple of 4", + caphdr->length); + return -1; + } + + SET_FLAG(peer->cap, PEER_CAP_RESTART_RCV); + pnt = (u_char *)caphdr; + pnt += sizeof(struct capability_header); /* Adjust it to value of restart capability */ + + memcpy(&pkt_time, pnt, sizeof(u_int16_t)); + restart_flag_time = ntohs(pkt_time); + pnt += sizeof(u_int16_t); + + if (CHECK_FLAG(restart_flag_time, RESTART_R_BIT)) + SET_FLAG(peer->cap, PEER_CAP_RESTART_BIT_RCV); + + UNSET_FLAG(restart_flag_time, 0xF000); + peer->v_gr_restart = restart_flag_time; + + if (bgp_debug_neighbor_events(peer)) { + zlog_debug("%s Capability has Graceful Restart capability", + peer->host); + zlog_debug("%s Peer has%srestarted. Restart Time : %d", + peer->host, + CHECK_FLAG(peer->cap, PEER_CAP_RESTART_BIT_RCV) + ? " " + : " not ", + peer->v_gr_restart); + } + + + while (pnt + 4 <= end) { + struct graceful_restart_af grc; + iana_afi_t pkt_afi; + iana_safi_t pkt_safi; + uint8_t flag; + afi_t afi; + safi_t safi; + + memcpy(&grc, pnt, sizeof(struct graceful_restart_af)); + pnt += sizeof(struct graceful_restart_af); + pkt_afi = ntohs(grc.afi); + pkt_safi = grc.safi; + flag = grc.flag; + + /* Convert AFI, SAFI to internal values, check. */ + if (bgp_map_afi_safi_iana2int(pkt_afi, pkt_safi, &afi, &safi)) { + if (bgp_debug_neighbor_events(peer)) + zlog_debug( + "%s Addr-family %d/%d(afi/safi) not supported." + " Ignore the Graceful Restart capability for this AFI/SAFI", + peer->host, pkt_afi, pkt_safi); + } else if (!peer->afc[afi][safi]) { + if (bgp_debug_neighbor_events(peer)) + zlog_debug( + "%s Addr-family %d/%d(afi/safi) not enabled." + " Ignore the Graceful Restart capability", + peer->host, pkt_afi, pkt_safi); + } else { + if (bgp_debug_neighbor_events(peer)) + zlog_debug( + "%s Address family %s was%spreserved", + peer->host, afi_safi_print(afi, safi), + CHECK_FLAG(peer->af_cap[afi][safi], + PEER_CAP_RESTART_AF_PRESERVE_RCV) ? + " " : " not "); + + if (action == CAPABILITY_ACTION_SET) { + SET_FLAG(peer->af_cap[afi][safi], + PEER_CAP_RESTART_AF_RCV); + if (CHECK_FLAG(flag, RESTART_F_BIT)) { + SET_FLAG(peer->af_cap[afi][safi], + PEER_CAP_RESTART_AF_PRESERVE_RCV); + } else { + UNSET_FLAG(peer->af_cap[afi][safi], + PEER_CAP_RESTART_AF_PRESERVE_RCV); + } + } else { /* + * Reset AF specifict bit if removing + * graceful-restart capability dynamically + */ + UNSET_FLAG(peer->af_cap[afi][safi], + PEER_CAP_RESTART_AF_RCV); + UNSET_FLAG(peer->af_cap[afi][safi], + PEER_CAP_RESTART_AF_PRESERVE_RCV); + } + if (bgp_debug_neighbor_events(peer)) + zlog_debug( + "%s Address family %s now is%spreserved", + peer->host, afi_safi_print(afi, safi), + CHECK_FLAG(peer->af_cap[afi][safi], + PEER_CAP_RESTART_AF_PRESERVE_RCV) ? + " " : " not "); + } + } + return 0; +} + /** * Parse BGP CAPABILITY message for peer. * @@ -2011,7 +2181,7 @@ static int bgp_route_refresh_receive(struct peer *peer, bgp_size_t size) static int bgp_capability_msg_parse(struct peer *peer, uint8_t *pnt, bgp_size_t length) { - uint8_t *end; + uint8_t *end, *start_val; struct capability_mp_data mpc; struct capability_header *hdr; uint8_t action; @@ -2054,12 +2224,13 @@ static int bgp_capability_msg_parse(struct peer *peer, uint8_t *pnt, return BGP_Stop; } - /* Fetch structure to the byte stream. */ - memcpy(&mpc, pnt + 3, sizeof(struct capability_mp_data)); - pnt += hdr->length + 3; + start_val = pnt + 3; /* Get start of value */ + pnt = start_val + hdr->length; /* Move to next dynamic cap */ /* We know MP Capability Code. */ if (hdr->code == CAPABILITY_CODE_MP) { + /* Fetch structure to the byte stream. */ + memcpy(&mpc, start_val, sizeof(struct capability_mp_data)); pkt_afi = ntohs(mpc.afi); pkt_safi = mpc.safi; @@ -2104,6 +2275,18 @@ static int bgp_capability_msg_parse(struct peer *peer, uint8_t *pnt, else return BGP_Stop; } + } else if (hdr->code == CAPABILITY_CODE_RESTART) { + if (bgp_debug_neighbor_events(peer)) + zlog_debug( + "%s CAPABILITY has %s RESTART CAP", + peer->host, + action == CAPABILITY_ACTION_SET + ? "Advertising" + : "Removing"); + + if (bgp_restart_dyn_capability_parse(peer, action, hdr, end) < 0) { + return BGP_Stop; + } } else { flog_warn( EC_BGP_UNRECOGNIZED_CAPABILITY, diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c index 01144f5c78..3cf96804e8 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -1946,6 +1946,23 @@ DEFUN (no_bgp_deterministic_med, return CMD_SUCCESS; } +static void bgp_restart_dyn_capability_update (struct bgp *bgp, int set) +{ + struct peer *peer; + struct listnode *node, *nnode; + + for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) { + if ((peer->status == Established) && + (CHECK_FLAG(peer->cap, PEER_CAP_DYNAMIC_RCV))) { + bgp_capability_send(peer, AFI_MAX, SAFI_MAX, + CAPABILITY_CODE_RESTART, + set ? + CAPABILITY_ACTION_SET : + CAPABILITY_ACTION_UNSET); + } + } +} + /* "bgp graceful-restart" configuration. */ DEFUN (bgp_graceful_restart, bgp_graceful_restart_cmd, @@ -1954,7 +1971,10 @@ DEFUN (bgp_graceful_restart, "Graceful restart capability parameters\n") { VTY_DECLVAR_CONTEXT(bgp, bgp); - bgp_flag_set(bgp, BGP_FLAG_GRACEFUL_RESTART); + if (!bgp_flag_check(bgp, BGP_FLAG_GRACEFUL_RESTART)) { + bgp_flag_set(bgp, BGP_FLAG_GRACEFUL_RESTART); + bgp_restart_dyn_capability_update(bgp, 1); + } return CMD_SUCCESS; } @@ -1966,7 +1986,10 @@ DEFUN (no_bgp_graceful_restart, "Graceful restart capability parameters\n") { VTY_DECLVAR_CONTEXT(bgp, bgp); - bgp_flag_unset(bgp, BGP_FLAG_GRACEFUL_RESTART); + if (bgp_flag_check(bgp, BGP_FLAG_GRACEFUL_RESTART)) { + bgp_flag_unset(bgp, BGP_FLAG_GRACEFUL_RESTART); + bgp_restart_dyn_capability_update(bgp, 0); + } return CMD_SUCCESS; } @@ -2001,6 +2024,9 @@ DEFUN (bgp_graceful_restart_restart_time, restart = strtoul(argv[idx_number]->arg, NULL, 10); bgp->restart_time = restart; + if (bgp_flag_check(bgp, BGP_FLAG_GRACEFUL_RESTART)) { + bgp_restart_dyn_capability_update(bgp, 1); + } return CMD_SUCCESS; } @@ -2031,6 +2057,9 @@ DEFUN (no_bgp_graceful_restart_restart_time, VTY_DECLVAR_CONTEXT(bgp, bgp); bgp->restart_time = BGP_DEFAULT_RESTART_TIME; + if (bgp_flag_check(bgp, BGP_FLAG_GRACEFUL_RESTART)) { + bgp_restart_dyn_capability_update(bgp, 1); + } return CMD_SUCCESS; } @@ -2042,7 +2071,12 @@ DEFUN (bgp_graceful_restart_preserve_fw, "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); + if (!bgp_flag_check(bgp, BGP_FLAG_GR_PRESERVE_FWD)) { + bgp_flag_set(bgp, BGP_FLAG_GR_PRESERVE_FWD); + if (bgp_flag_check(bgp, BGP_FLAG_GRACEFUL_RESTART)) { + bgp_restart_dyn_capability_update(bgp, 1); + } + } return CMD_SUCCESS; } @@ -2055,7 +2089,12 @@ DEFUN (no_bgp_graceful_restart_preserve_fw, "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); + if (bgp_flag_check(bgp, BGP_FLAG_GR_PRESERVE_FWD)) { + bgp_flag_unset(bgp, BGP_FLAG_GR_PRESERVE_FWD); + if (bgp_flag_check(bgp, BGP_FLAG_GRACEFUL_RESTART)) { + bgp_restart_dyn_capability_update(bgp, 1); + } + } return CMD_SUCCESS; }