Skip to content

Fallible encoding with sink - #315

Merged
digizeph merged 7 commits into
bgpkit:mainfrom
ties:feat/fallible-encoding-sink
Jul 30, 2026
Merged

Fallible encoding with sink#315
digizeph merged 7 commits into
bgpkit:mainfrom
ties:feat/fallible-encoding-sink

Conversation

@ties

@ties ties commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

LLM-driven (Fable) approach of an alternative to the approach in #314 (comment). Follows the plan steps in fallible-encoding-redesign.md

ties added 6 commits July 29, 2026 21:35
EncodingError carries ValueTooLarge {field, actual, max} and Unencodable
{field, reason}. All length-prefixed wire fields will be written through
with_u8_len/with_u16_len (back-patched prefixes) or put_u8/u16_len_slice,
so every capacity check lives in one place and non-power-of-two bounds
are explicit via check_max. Groundwork for fallible encoding (issue bgpkit#313).

Assisted-by: Claude Code
Leaf attribute encoders that can overflow a wire length field now write
into a shared BytesMut and return Result<(), EncodingError>:
- AS_PATH rejects segments with >255 ASes (was: silent u8 truncation)
- Tunnel Encap, BGP-LS, SFP, BFD Discriminator, Prefix-SID and BIER
  TLV value lengths are checked (was: silent u8/u16 truncation)
- FlowSpec NLRI length is bounded at 0x0FFF, matching the 12-bit wire
  field that parse_length reads (was: unchecked cast to u16, corrupting
  any NLRI of 4096..=65535 bytes)
- encode_nlri propagates labeled-prefix errors and now encodes
  flowspec_nlris (was: silently dropped)

Attribute::encode_to writes the value through with_u8_len/with_u16_len
according to the attribute's EXTENDED_LENGTH flag, so an oversized value
yields ValueTooLarge instead of a truncated length byte. ATTR_SET
returns Unencodable instead of Ok + empty value. Attributes::encode now
returns Result; the two call sites in BgpUpdateMessage and RibEntry
carry temporary expect()s until they are converted next.

Tlv::length()/SubTlv::length() (silently saturating, dead on production
paths) are removed.

Assisted-by: Claude Code
All encode() methods from BgpOpenMessage up to MrtRecord now return
Result<Bytes, EncodingError>; no silent truncation and no panicking
wrappers remain on any encode path:

- BgpOpenMessage rejects optional parameter type 255 (reserved by
  RFC 9072 as the extended-length marker; emitting it round-trips to a
  structurally different message) and returns an error instead of
  debug_assert when extended parameters exceed u16::MAX
- BgpUpdateMessage writes withdrawn-routes and path-attribute sections
  through checked length prefixes
- BgpMessage checks the total message length against the u16 wire field
- TableDumpMessage uses the shared Attributes::encode_to (removing a
  hand-rolled duplicate loop); RibAfiEntries checks the entry count
  (was: wrapping cast) and propagates per-entry errors
- PeerIndexTable/GeoPeerTable check view-name length and peer count
- MrtMessage::encode returns Unencodable for RIB_GENERIC instead of
  todo!(); Bgp4MpMessage, MrtRecord, and both MRT encoders'
  export_bytes() propagate errors

Assisted-by: Claude Code
PeerIndexTable::add_peer returns Result<u16, EncodingError> and refuses
the 65536th distinct peer (the wire Peer Count field is 16-bit), leaving
the table unmodified instead of silently wrapping the id and aliasing
routes onto an existing peer. MrtRibEncoder::process_elem propagates the
error so a writer learns about the overflow when it happens, not after
the in-memory table is already corrupted.

Assisted-by: Claude Code
deny(unused_must_use) at the crate root so a dropped Result from an
encode_to() call — which would silently skip wire data — fails the
build.

New tests cover one error path per wire bound: non-extended and
extended attribute value lengths, ATTR_SET (Unencodable), MP_REACH
labeled prefix with an empty label stack (previously swallowed with a
log line), RIB entry attribute overflow propagation (previously the
entry vanished from the record), RIB entry count, OPEN parameter type
255, BGP message total length, tunnel-encap sub-TLV length, and the
FlowSpec 4096..=65535 range that fits a u16 but not the 12-bit wire
length field.

Assisted-by: Claude Code
@codecov

codecov Bot commented Jul 29, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 97.31801% with 14 lines in your changes missing coverage. Please review.
✅ Project coverage is 90.51%. Comparing base (7d9268b) to head (b44eccc).

Files with missing lines Patch % Lines
src/parser/mrt/messages/mod.rs 66.66% 4 Missing ⚠️
src/parser/bgp/attributes/attr_14_15_nlri.rs 92.00% 2 Missing ⚠️
src/parser/bgp/messages.rs 96.22% 2 Missing ⚠️
...arser/mrt/messages/table_dump_v2/geo_peer_table.rs 83.33% 2 Missing ⚠️
src/parser/bgp/attributes/attr_23_tunnel_encap.rs 94.11% 1 Missing ⚠️
src/parser/bgp/attributes/attr_29_linkstate.rs 95.45% 1 Missing ⚠️
...ser/mrt/messages/table_dump_v2/peer_index_table.rs 98.03% 1 Missing ⚠️
src/parser/mrt/mrt_record.rs 92.30% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #315      +/-   ##
==========================================
+ Coverage   90.31%   90.51%   +0.20%     
==========================================
  Files          91       92       +1     
  Lines       19306    19519     +213     
==========================================
+ Hits        17436    17668     +232     
+ Misses       1870     1851      -19     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@digizeph
digizeph merged commit abe4214 into bgpkit:main Jul 30, 2026
8 of 9 checks passed
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