Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sending Cease/Hard Reset notification #2800

Merged
merged 2 commits into from
Apr 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions docs/sources/graceful-restart.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Graceful Restart

This page explains how to configure [Graceful Restart](https://tools.ietf.org/html/rfc4724),
[Graceful Restart Notification Support](https://tools.ietf.org/html/draft-ietf-idr-bgp-gr-notification-07) and
[Graceful Restart Notification Support](https://tools.ietf.org/html/rfc8538) and
[Long Lived Graceful Restart](https://tools.ietf.org/html/draft-uttaro-idr-bgp-persistence-02).
Graceful Restart has two sides. One is restarting speaker which does restart,
the other is receiving speaker (helper speaker) which helps a restarting speaker
Expand Down Expand Up @@ -153,7 +153,7 @@ Default value of `restart-time` is equal to `hold-time`.
[RFC4724](https://tools.ietf.org/html/rfc4724) specifies gracful restart procedures are triggered only when
the BGP session between graceful restart capable peers turns down without
a notification message for backward compatibility.
[Graceful Restart Notification Support](https://tools.ietf.org/html/draft-ietf-idr-bgp-gr-notification-07)
[Graceful Restart Notification Support](https://tools.ietf.org/html/rfc8538)
expands this to trigger graceful restart procedures also with a notification message.
To turn on this feature, add `notification-enabled = true` to configuration like below.

Expand Down
30 changes: 29 additions & 1 deletion pkg/packet/bgp/bgp.go
Original file line number Diff line number Diff line change
Expand Up @@ -9877,7 +9877,7 @@ const (
BGP_ERROR_SUB_OTHER_CONFIGURATION_CHANGE
BGP_ERROR_SUB_CONNECTION_COLLISION_RESOLUTION
BGP_ERROR_SUB_OUT_OF_RESOURCES
BGP_ERROR_SUB_HARD_RESET // draft-ietf-idr-bgp-gr-notification-07
BGP_ERROR_SUB_HARD_RESET // RFC8538
)

// Constants for BGP_ERROR_SUB_ADMINISTRATIVE_SHUTDOWN and BGP_ERROR_SUB_ADMINISTRATIVE_RESET
Expand Down Expand Up @@ -14647,6 +14647,34 @@ func NewBGPNotificationMessage(errcode uint8, errsubcode uint8, data []byte) *BG
}
}

// RFC8538 makes a suggestion that which Cease notification subcodes should be
// mapped to the Hard Reset. This function takes a subcode and returns true if
// the subcode should be treated as a Hard Reset. Otherwise, it returns false.
//
// The second argument is a boolean value that indicates whether the Hard Reset
// should be performed on the Admin Reset. This reflects the RFC8538's
// suggestion that the implementation should provide a control to treat the
// Admin Reset as a Hard Reset. When the second argument is true, the function
// returns true if the subcode is BGP_ERROR_SUB_ADMINISTRATIVE_RESET.
// Otherwise, it returns false.
//
// As RFC8538 states, it is not mandatory to follow this suggestion. You can
// use this function when you want to follow the suggestion.
func ShouldHardReset(subcode uint8, hardResetOnAdminReset bool) bool {
switch subcode {
case BGP_ERROR_SUB_MAXIMUM_NUMBER_OF_PREFIXES_REACHED,
BGP_ERROR_SUB_ADMINISTRATIVE_SHUTDOWN,
BGP_ERROR_SUB_PEER_DECONFIGURED,
BGP_ERROR_SUB_HARD_RESET:
return true
default:
if hardResetOnAdminReset && subcode == BGP_ERROR_SUB_ADMINISTRATIVE_RESET {
return true
}
return false
}
}

type BGPKeepAlive struct {
}

Expand Down
30 changes: 30 additions & 0 deletions pkg/server/fsm.go
Original file line number Diff line number Diff line change
Expand Up @@ -1659,6 +1659,22 @@ func (h *fsmHandler) sendMessageloop(ctx context.Context, wg *sync.WaitGroup) er
table.UpdatePathAttrs2ByteAs(m.Body.(*bgp.BGPUpdate))
table.UpdatePathAggregator2ByteAs(m.Body.(*bgp.BGPUpdate))
}

// RFC8538 defines a Hard Reset notification subcode which
// indicates that the BGP speaker wants to reset the session
// without triggering graceful restart procedures. Here we map
// notification subcodes to the Hard Reset subcode following
// the RFC8538 suggestion.
//
// We check Status instead of Config because RFC8538 states
// that A BGP speaker SHOULD NOT send a Hard Reset to a peer
// from which it has not received the "N" bit.
if fsm.pConf.GracefulRestart.State.NotificationEnabled && m.Header.Type == bgp.BGP_MSG_NOTIFICATION {
if body := m.Body.(*bgp.BGPNotification); body.ErrorCode == bgp.BGP_ERROR_CEASE && bgp.ShouldHardReset(body.ErrorSubcode, false) {
body.ErrorSubcode = bgp.BGP_ERROR_SUB_HARD_RESET
}
}

b, err := m.Serialize(h.fsm.marshallingOptions)
fsm.lock.RUnlock()
if err != nil {
Expand Down Expand Up @@ -1834,6 +1850,20 @@ func (h *fsmHandler) established(ctx context.Context) (bgp.FSMState, *fsmStateRe
case <-ctx.Done():
select {
case m := <-fsm.notification:
// RFC8538 defines a Hard Reset notification subcode which
// indicates that the BGP speaker wants to reset the session
// without triggering graceful restart procedures. Here we map
// notification subcodes to the Hard Reset subcode following
// the RFC8538 suggestion.
//
// We check Status instead of Config because RFC8538 states
// that A BGP speaker SHOULD NOT send a Hard Reset to a peer
// from which it has not received the "N" bit.
if fsm.pConf.GracefulRestart.State.NotificationEnabled {
if body := m.Body.(*bgp.BGPNotification); body.ErrorCode == bgp.BGP_ERROR_CEASE && bgp.ShouldHardReset(body.ErrorSubcode, false) {
body.ErrorSubcode = bgp.BGP_ERROR_SUB_HARD_RESET
}
}
b, _ := m.Serialize(h.fsm.marshallingOptions)
h.conn.Write(b)
default:
Expand Down
Loading