Skip to content

packet/bgp: reject KEEPALIVE messages that are not 19 octets - #3567

Merged
fujita merged 1 commit into
osrg:masterfrom
Sonike:fix/issue-3449
Sep 1, 2026
Merged

packet/bgp: reject KEEPALIVE messages that are not 19 octets#3567
fujita merged 1 commit into
osrg:masterfrom
Sonike:fix/issue-3449

Conversation

@Sonike

@Sonike Sonike commented Aug 30, 2026

Copy link
Copy Markdown
Contributor

What

BGPKeepAlive.DecodeFromBytes returned nil without inspecting its input, so
both ParseBGPMessage callers and the FSM accepted a KEEPALIVE whose declared
length was not 19 octets. RFC 4271 Sections 4.4 and 6.1 require the length to be
exactly 19.

How

parseBody passes the bytes declared after the 19-octet header to the body
decoder. A conforming KEEPALIVE therefore arrives as an empty slice; any
non-empty slice is rejected with Bad Message Length.

RFC 4271 Section 6.1 also requires the NOTIFICATION Data field for this error
to contain the erroneous two-octet Length field. The decoder now records that
big-endian length in MessageError.Data.

Scope

This deliberately covers only the KEEPALIVE item from #3449:

  • The OPEN checks for Hold Time, BGP Identifier and Version already live in
    ValidateOpenMsg. Moving them into the decoder would change validation
    boundaries and should be a separate maintainer decision.
  • The general message-length ceiling is session-dependent because RFC 8654
    raises it for UPDATE, NOTIFICATION and ROUTE-REFRESH after Extended Message
    Capability negotiation. The session layer has that state; the standalone
    parser does not.

Testing

  • go test ./pkg/packet/bgp -run '^TestParseBGPMessageKeepAliveLength$' -count=1
  • go test -race -timeout 240s ./pkg/packet/bgp
  • go build ./...
  • go vet ./...
  • golangci-lint run ./pkg/packet/bgp/...
  • golangci-lint run --new-from-rev=upstream/master

The full go test -race -timeout 240s ./... run passed outside pkg/server.
TestEBGPRouteStuck and TestRTCDeferralTimerRaceCondition cannot bind
127.0.0.100 and 127.0.0.201 on this macOS host; these are unchanged
environment limitations and unrelated to the packet decoder.

Refs #3449

@fujita

fujita commented Aug 30, 2026

Copy link
Copy Markdown
Member

I think that bird does not reject an over-long KEEPALIVE.

Comment thread pkg/packet/bgp/bgp.go Outdated
// header and has a length of exactly 19 octets, so the body that
// parseBody hands us must be empty.
if len(data) != 0 {
return NewMessageError(BGP_ERROR_MESSAGE_HEADER_ERROR, BGP_ERROR_SUB_BAD_MESSAGE_LENGTH, nil, fmt.Sprintf("KEEPALIVE length must be %d", BGP_HEADER_LENGTH))

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.

You can't use nil here. RFC 4271 Section 6.1 says that The Data field MUST contain the erroneous Type field..

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Good catch on the empty Data field. I added a regression assertion and now populate it in e37c5a2. Because this is Bad Message Length, RFC 4271 Section 6.1 requires the Data field to carry the erroneous two-octet Length field; the following sentence about Type applies to Bad Message Type. The test now checks the code, subcode, and big-endian length for 20, 100, and 4096.

RFC 4271 Sections 4.4 and 6.1 require a KEEPALIVE to be exactly 19 octets. A Bad Message Length notification must also carry the erroneous two-octet Length field.

BGPKeepAlive.DecodeFromBytes did not inspect its input, so ParseBGPMessage accepted KEEPALIVEs with a body. Reject non-empty bodies and populate MessageError.Data with the declared length.

Add table-driven coverage for valid length 19 and invalid lengths 20, 100, and 4096, including the error code, subcode, and Data.

This deliberately covers only the KEEPALIVE part of osrg#3449. OPEN validation remains in ValidateOpenMsg, while the general length ceiling is session-dependent when RFC 8654 is negotiated.

Refs osrg#3449

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Signed-off-by: Sonike <1700162+Sonike@users.noreply.github.com>
@Sonike

Sonike commented Sep 1, 2026

Copy link
Copy Markdown
Contributor Author

You are right about BIRD: its current receive path checks only the general 19-to-session-maximum range and dispatches KEEPALIVE without a type-specific length check. I removed that comparison from the PR body and amended commit message. The change is now justified directly by RFC 4271 Sections 4.4 and 6.1.

@fujita
fujita merged commit e37c5a2 into osrg:master Sep 1, 2026
42 checks passed
@fujita

fujita commented Sep 1, 2026

Copy link
Copy Markdown
Member

Thanks.

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