diff --git a/bgpd/bgp_fsm.c b/bgpd/bgp_fsm.c index cc3505333b..ba018d3f56 100644 --- a/bgpd/bgp_fsm.c +++ b/bgpd/bgp_fsm.c @@ -1961,6 +1961,15 @@ static int bgp_fsm_holdtime_expire(struct peer *peer) if (bgp_debug_neighbor_events(peer)) zlog_debug("%s [FSM] Hold timer expire", peer->host); + /* RFC8538 updates RFC 4724 by defining an extension that permits + * the Graceful Restart procedures to be performed when the BGP + * speaker receives a BGP NOTIFICATION message or the Hold Time expires. + */ + if (peer_established(peer) && + bgp_has_graceful_restart_notification(peer)) + if (CHECK_FLAG(peer->sflags, PEER_STATUS_NSF_MODE)) + SET_FLAG(peer->sflags, PEER_STATUS_NSF_WAIT); + return bgp_stop_with_notify(peer, BGP_NOTIFY_HOLD_ERR, 0); } diff --git a/bgpd/bgp_packet.c b/bgpd/bgp_packet.c index 9a1216a031..29db23fbbd 100644 --- a/bgpd/bgp_packet.c +++ b/bgpd/bgp_packet.c @@ -748,6 +748,13 @@ struct bgp_notify bgp_notify_decapsulate_hard_reset(struct bgp_notify *notify) return bn; } +/* Check if Graceful-Restart N-bit is exchanged */ +bool bgp_has_graceful_restart_notification(struct peer *peer) +{ + return CHECK_FLAG(peer->cap, PEER_CAP_GRACEFUL_RESTART_N_BIT_RCV) && + CHECK_FLAG(peer->cap, PEER_CAP_GRACEFUL_RESTART_N_BIT_ADV); +} + /* * Check if to send BGP CEASE Notification/Hard Reset? */ @@ -757,8 +764,7 @@ bool bgp_notify_send_hard_reset(struct peer *peer, uint8_t code, /* When the "N" bit has been exchanged, a Hard Reset message is used to * indicate to the peer that the session is to be fully terminated. */ - if (!CHECK_FLAG(peer->cap, PEER_CAP_GRACEFUL_RESTART_N_BIT_RCV) || - !CHECK_FLAG(peer->cap, PEER_CAP_GRACEFUL_RESTART_N_BIT_ADV)) + if (!bgp_has_graceful_restart_notification(peer)) return false; /* @@ -797,8 +803,7 @@ bool bgp_notify_received_hard_reset(struct peer *peer, uint8_t code, /* When the "N" bit has been exchanged, a Hard Reset message is used to * indicate to the peer that the session is to be fully terminated. */ - if (!CHECK_FLAG(peer->cap, PEER_CAP_GRACEFUL_RESTART_N_BIT_RCV) || - !CHECK_FLAG(peer->cap, PEER_CAP_GRACEFUL_RESTART_N_BIT_ADV)) + if (!bgp_has_graceful_restart_notification(peer)) return false; if (code == BGP_NOTIFY_CEASE && subcode == BGP_NOTIFY_CEASE_HARD_RESET) @@ -2094,6 +2099,13 @@ static int bgp_notify_receive(struct peer *peer, bgp_size_t size) inner.subcode == BGP_NOTIFY_OPEN_UNSUP_PARAM) UNSET_FLAG(peer->sflags, PEER_STATUS_CAPABILITY_OPEN); + /* If Graceful-Restart N-bit (Notification) is exchanged, + * and it's not a Hard Reset, let's retain the routes. + */ + if (bgp_has_graceful_restart_notification(peer) && !hard_reset && + CHECK_FLAG(peer->sflags, PEER_STATUS_NSF_MODE)) + SET_FLAG(peer->sflags, PEER_STATUS_NSF_WAIT); + bgp_peer_gr_flags_update(peer); BGP_GR_ROUTER_DETECT_AND_SEND_CAPABILITY_TO_ZEBRA(peer->bgp, peer->bgp->peer); diff --git a/bgpd/bgp_packet.h b/bgpd/bgp_packet.h index 92c7f60ac7..a0eb579db7 100644 --- a/bgpd/bgp_packet.h +++ b/bgpd/bgp_packet.h @@ -88,6 +88,7 @@ extern void bgp_send_delayed_eor(struct bgp *bgp); void bgp_packet_process_error(struct thread *thread); extern struct bgp_notify bgp_notify_decapsulate_hard_reset(struct bgp_notify *notify); +extern bool bgp_has_graceful_restart_notification(struct peer *peer); extern bool bgp_notify_send_hard_reset(struct peer *peer, uint8_t code, uint8_t subcode); extern bool bgp_notify_received_hard_reset(struct peer *peer, uint8_t code,