Skip to content

server: expose raw NOTIFICATION error code/subcode on PeerState - #3543

Open
Sreedhar-cp wants to merge 2 commits into
osrg:masterfrom
Sreedhar-cp:sreedhar-cp/notification_code_subcode
Open

server: expose raw NOTIFICATION error code/subcode on PeerState#3543
Sreedhar-cp wants to merge 2 commits into
osrg:masterfrom
Sreedhar-cp:sreedhar-cp/notification_code_subcode

Conversation

@Sreedhar-cp

Copy link
Copy Markdown
Contributor

Summary

PeerState.DisconnectReason/DisconnectMessage (added for peer disconnect reason reporting) describe why a session went down as an enum plus a formatted string, but don't expose the underlying RFC 4271 §6 NOTIFICATION error code/subcode as typed integers. A caller that needs the raw numeric values today has to fragile-parse the message string.

This PR adds PeerState.notification_code / notification_subcode, threaded through from the FSM's underlying *bgp.BGPNotification whenever one is present. This is intentionally not limited to a specific DisconnectReason (e.g. NOTIFICATION_SENT/_RECEIVED) — fsmBadPeerAS, fsmInvalidMsg, fsmHoldTimerExpired, and fsmAdminDown can all carry a real notification too, so any of them should surface a numeric code/subcode when one is actually present. Both fields are 0/0 when no notification was involved.

Commits

  1. server: fix wrong message wired into fsmStateReason on bad-peer-AS/invalid-msg — while adding a test for the feature above, found that handleOpen() builds the actual outgoing NOTIFICATION in notif and correctly sends it on the wire, but passes the received OPEN message (m, bound by the outer type switch) into newfsmStateReason(...) instead of notif, for both the bad-peer-AS and generic invalid-message paths. Every other FSM path in this file (hold-timer-expired, admin-down) correctly passes the actual notification. Besides being simply wrong, this also means bmp.go's BMP Peer Down Notification embeds the wrong message content for a peer torn down via a bad-peer-AS or invalid-message OPEN rejection. Fixed to match the convention used everywhere else in the file.
  2. api, server: expose raw NOTIFICATION error code/subcode on PeerState — the actual feature: new proto fields + regenerated Go, apiutil.PeerState fields, and the extractNotificationCodeSubcode helper wired into WatchEvent's peer-event builder.

Test plan

  • New test TestWatchEventPeerNotificationCodeSubcode drives a genuine bad-peer-AS NOTIFICATION exchange between two live *BgpServer instances and asserts the resulting PeerState carries the real RFC 4271 OPEN Message Error / Bad Peer AS code/subcode.
  • go build ./..., go vet ./... clean.
  • go test ./... passes across every package (two pre-existing tests, TestEBGPRouteStuck and TestRTCDeferralTimerRaceCondition, require loopback IP aliases not configured on macOS by default and are unaffected by this change — verified they fail identically on unmodified master).

…valid-msg

handleOpen() builds the actual outgoing NOTIFICATION in notif via
bgp.NewBGPNotificationMessage() and correctly sends it on the wire
(sendNotification(fsm.conn, notif)), but passed the received OPEN
message (m, bound by the outer type switch) into
newfsmStateReason(...) instead of notif, for both the bad-peer-AS and
generic invalid-message paths.

This means fsmStateReason.BGPNotification held a *bgp.BGPOpen rather
than a *bgp.BGPNotification for these two transitions, silently
diverging from every other FSM path in this file (hold-timer-expired,
admin-down) which correctly pass the actual notification. Besides
being simply wrong, this also meant bmp.go's BMP Peer Down
Notification embedded the wrong message content for a peer torn down
via a bad-peer-AS or invalid-message OPEN rejection.

Pass notif instead, matching the convention used everywhere else in
this file.
DisconnectReason/DisconnectMessage (added for peer disconnect reason
reporting) describe why a session went down as an enum plus a
formatted string, but don't expose the underlying RFC 4271 §6
NOTIFICATION error code/subcode as typed integers -- callers that need
the raw numeric values have to fragile-parse the message string.

Add PeerState.notification_code / notification_subcode (proto +
generated Go), threaded through apiutil.PeerState and the WatchEvent
peer-event builder via a new extractNotificationCodeSubcode helper.
This is intentionally not gated on any particular DisconnectReason:
fsmBadPeerAS, fsmInvalidMsg, fsmHoldTimerExpired, and fsmAdminDown can
all carry a real notification, so any of them should surface a
numeric code/subcode when one is actually present. Both fields are
0/0 when no notification was involved.

Adds a test driving a genuine bad-peer-AS NOTIFICATION exchange
between two live *BgpServer instances and asserting the resulting
PeerState carries the real RFC 4271 OPEN Message Error / Bad Peer AS
code/subcode.
Comment thread proto/api/gobgp.proto
// disconnect_reason/disconnect_message alone cannot expose these as typed
// integers without fragile string-parsing.
uint32 notification_code = 24;
uint32 notification_subcode = 25;

@fujita fujita Aug 30, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// A BGP NOTIFICATION message, as defined in RFC 4271 section 4.5.
message BgpNotification {
  uint32 error_code = 1;
  uint32 error_subcode = 2;
  bytes data = 3;
}

Let's create a new type for notification because we might need data later.

@fujita

fujita commented Aug 30, 2026

Copy link
Copy Markdown
Member

Thanks!

The first commit is a fix. So I've already merged it.
Please update the second commit as I commented and rebase against the current master.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants