Repository navigation
fix(mrt): encode one next hop for received IPv4 routes - #2977
Merged
Merged
Conversation
The RIB keeps the received NEXT_HOP among an IPv4 unicast route's attributes, and import next-hop self updates only the route's next hop. MRT RIB entries, warm checkpoints and BMP Loc-RIB announcements emitted the route's next hop and the stored attribute, so each entry carried NEXT_HOP twice and warm checkpoint publication failed its recovery check. These encoders now emit only the route's post-policy next hop.
There was a problem hiding this comment.
🟡 Changes recommended
BMP encoding introduces repeated temporary allocations on the RIB hot path by losing scratch-buffer reuse.
1 open finding
What changed in this PR
Fixes duplicate IPv4 NEXT_HOP attributes when stored RIB routes are re-encoded, allowing warm checkpoint publication and preserving the post-policy next hop.
Changes:
- Shares stored-attribute filtering across MRT and BMP encoders.
- Adds regression tests for received routes, policy rewrites, checkpoint recovery, and IPv6 preservation.
- Documents the fix and compatibility behavior.
| File | Description |
|---|---|
| crates/transport/src/session/tests/mrt_next_hop.rs | Adds end-to-end next-hop regression tests. |
| crates/transport/src/session/tests/mod.rs | Registers the new tests. |
| crates/transport/Cargo.toml | Adds test dependencies. |
| crates/rib/src/route.rs | Adds shared next-hop filtering. |
| crates/rib/src/bmp_sync.rs | Filters stored next hops during BMP synthesis. |
| crates/mrt/src/codec.rs | Prevents duplicate next hops in MRT encoding. |
| changelog.d/fixed-mrt-single-ipv4-next-hop.md | Documents operational impact and compatibility. |
| Cargo.lock | Records test dependencies. |
🧠 Review effort: Balanced
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Splicing the synthesized next hop into the BMP Loc-RIB announcement encoded each attribute with its own call, allocating a fresh value scratch per attribute. Encode the spliced borrowed attributes once through UpdateMessage::try_build_from_attribute_iter instead.
lance0
marked this pull request as ready for review
October 7, 2026 23:38
lance0
added a commit
that referenced
this pull request
Oct 8, 2026
Add a debug-only check that an encoded path-attribute list carries each attribute type at most once, so an encoder that copies stored attributes and also synthesizes one of the same type fails in tests rather than on the wire. The wire attribute encoder, which every export UPDATE (per-session and update-group) and BMP message passes through, compiles the check only under the new non-default strict-encode-invariants feature with debug assertions; this workspace's transport, rib and mrt test builds enable it, while default, release and embedder builds are unchanged. MRT RIB entries get an equivalent walk that skips blocks it cannot read faithfully, so oversized entries still report FieldTooLarge. Reintroducing the duplicate NEXT_HOP fixed in #2977, or removing export's existing-NEXT_HOP check, trips the guard in the affected tests.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Problem
The RIB keeps the received
NEXT_HOPamong an IPv4 unicast route's stored attributes, and route injection and import policy keep one too. Three encoders re-emit stored routes, and each addedNEXT_HOPfromRoute::next_hopwithout skipping the stored copy:synthesize_attributes.As a result:
NEXT_HOPattributes.warm bundle MRT recovery discarded N path attributes), so a daemon with any such route never published a checkpoint.next-hop self, the second copy held the stale received address.This was reproduced on the official v0.75.0 binary and on main.
Change
Route::attributes_except_next_hop()defines which stored attributes these encoders may copy. The next hop comes only fromRoute::next_hop, the post-import-policy next hop the RIB selects and installs with. That is also the value an MRT TABLE_DUMP_V2 entry for an Adj-RIB-In post-policy or Loc-RIB view should record.synthesize_attributesand the BMP Loc-RIB synthesizer use it. The BMP helper now encodes attributes one at a time from an iterator rather than splitting a slice.NEXT_HOP, and export already rewrites it. Stripping it at every producer would be a broader change to the hot path and to interning.Validation
crates/transport/src/session/tests/mrt_next_hop.rsbuild the route throughPeerSession::process_update, as inbound stores it:NEXT_HOP, and nothing is discarded.next-hop selfand for a specific next hop.NEXT_HOP.MP_REACHkeeps its global and link-local next hop.NEXT_HOP.discarded 1(the MRT dump tests);write_warm_bundle(the warm checkpoint test);duplicate attribute type 3(the BMP test).synthesize_attributes: the equivalence test.view_route_counts[2]), and its GR marker carries the checkpoint generation.rbgp mrt-dumpentries and the checkpoint snapshot each carry oneNEXT_HOP(192.0.2.3).cargo fmt --checkand the commit hooks pass.just gateexited 0 (10502 passed, 0 failed, 19 ignored across 155 test suites, plus links, contracts, strict Clippy and rustdoc).just gate-ribexited 0.Compatibility