Skip to content

feat(parse): add Cisco sh ip bgp text dump support#143

Merged
digizeph merged 2 commits into
mainfrom
feat/parse-cisco-text-dump
Jul 21, 2026
Merged

feat(parse): add Cisco sh ip bgp text dump support#143
digizeph merged 2 commits into
mainfrom
feat/parse-cisco-text-dump

Conversation

@digizeph

@digizeph digizeph commented Jul 21, 2026

Copy link
Copy Markdown
Member

Summary

Adds auto-detection and parsing of Cisco sh ip bgp fixed-width text dumps to the monocle parse command. The parser extracts all standard BGP attributes, supports multipath continuations, and outputs via all existing formats (JSON, table, MRT).

Usage

# Parse a remote PCH text dump directly (auto-detected, no flags needed)
monocle parse https://downloads.pch.net/files/Routing_Data/IPv4_daily_snapshots/2026/07/route-collector.bom2.pch.net/route-collector.bom2.pch.net-ipv4_bgp_routes.2026.07.01.gz

# Filter by origin ASN, select fields, export as MRT RIB
monocle parse https://downloads.pch.net/files/Routing_Data/IPv4_daily_snapshots/2026/07/route-collector.bom2.pch.net/route-collector.bom2.pch.net-ipv4_bgp_routes.2026.07.01.gz \
  -o 13335 -f prefix,as_path,next_hop --json -M /tmp/cloudflare.rib.gz

Changes

New file

  • src/lens/parse/text_dump.rs — full parser: format detection, header parsing, route-line parsing, BgpElem construction, 13 unit tests

Modified files

  • src/lens/parse/mod.rs — added MrtType enum (Rib/Updates) with infer() method; wired text_dump module
  • src/bin/commands/parse.rs — format auto-detection branch (peek 256 bytes), --mrt-type flag, text dump → parse_text_dump path

Key design decisions

  • Header column detection uses whitespace tokenization with multi-word merging ("Next" + "Hop" → "Next Hop") instead of find() substring matching
  • Route line parsing retains byte-position slicing from header-derived column positions (whitespace-only split would collapse empty numeric columns like LocPrf)
  • MRT export auto-infers rib for text dumps (RIB snapshots) and updates for MRT files

Test plan

  • cargo fmt --check
  • cargo clippy --all-features -- -D warnings
  • cargo test --all-features — 302 passed, 0 failed ✓
  • Live PCH bom2 dumps (2026-07-01/02/03) via remote URL — 97,546 / 97,402 / 89,572 prefixes ✓
  • MRT RIB export → 609KB, round-trip re-parse = 37,381 unique prefixes ✓
  • MRT Updates export → 97,546 messages ✓
  • Origin filter (-o 13335) → 5,092 correct results ✓
  • Field selection (-f prefix,next_hop,as_path) ✓

Closes #142

- Auto-detect Cisco fixed-width BGP table dumps via preamble
- Parse prefix, next-hop, metric, local-pref, weight, AS path, origin
- Handle multi-path continuation lines and wrapped-prefix layouts
- Support all parse filters, output formats, and MRT export (--mrt-type)
- Whitespace-split header column detection (replaces fixed find())
- MrtType::infer() auto-selects Rib for text dumps, Updates for MRT

@digizeph digizeph left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Code Review Summary

Verdict: Approve — solid implementation, well-tested, clean architecture.

✅ Looks Good

  • Whitespace-split header detection with multi-word merging ("Next"+"Hop") is robust and avoids hardcoded positions — correctly handles PCH format variations
  • Byte-position slicing for route-line columns keeps numeric attributes (metric, local-pref) distinct from AS-path ASNs even when columns are blank — the right call vs. whitespace split
  • ParseFilters::filter_specs() refactor extracts shared filter logic into one method that both to_parser() and to_filters() consume — clean DRY, no duplication
  • MrtType enum with infer() is forward-looking — RIB for text dumps, Updates for MRT, with explicit --mrt-type override
  • Auto-detection peeks 256 bytes then re-opens for full parse — clean separation, no MRT parser breakage
  • 13 focused unit tests cover detection, headers, continuations, wrapped prefixes, all origin codes, empty numeric attrs, round-trip BgpElem construction
  • Streaming fast path preserved for ordinary MRT parse when no buffering/export needed — no perf regression
  • CI: cargo fmt --check ✓, cargo clippy --all-features -- -D warnings ✓, cargo test --all-features — 304 passed ✓

💡 Suggestions (non-blocking)

  1. text_dump.rs:200 — Document wrapped-prefix IPv4-only limitation. parse_wrapped_prefix_line checks line.len() > next_hop_start to detect prefix-only lines. This works for IPv4 prefixes (< 21 chars at standard PCH column offset) but would miss longer IPv6 wrapped prefixes. Not a blocker since the primary target is PCH IPv4 dumps, but worth a doc comment.

  2. text_dump.rs:364 — Hardcoded timestamp = 0.0. A real timestamp (extracted from the PCH URL date component, e.g. 2026/07/01 → Unix) would make the exported MRT data more useful for time-range queries. Consider a follow-up PR.

  3. text_dump.rs:264 — AS-set flattening. {/} tokens are silently dropped, converting AS-sets into flat AS-sequences. This is reasonable for most use cases, but a one-line doc comment on as_path_from_tokens would make the trade-off explicit.

🔍 Architecture note

The text dump path always materializes into Vec<BgpElem> (RIBs are inherently full-snapshot). For the 97k-prefix PCH dumps tested, this is fine. If much larger dumps appear in the future, a streaming adapter that yields BgpElem from parse_text_dump would be the natural refactor — the internal structure already supports it.


Reviewed by Scouty (Hermes Agent)

))
}

fn parse_wrapped_prefix_line(line: &str, next_hop_start: usize) -> Option<String> {

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

💡 Suggestion: This detection (line.len() > next_hop_start) works for IPv4 wrapped prefixes (< 21 chars at standard PCH column offset) but would miss longer IPv6 prefixes. A doc comment noting this limitation would help future readers. Not a blocker — the primary target is PCH IPv4 dumps.

fn as_path_from_tokens(tokens: &[String]) -> AsPath {
let mut asns: Vec<Asn> = Vec::new();
for token in tokens {
if token == "{" || token == "}" {

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

💡 Suggestion: { / } AS-set delimiters are silently dropped, flattening sets into sequences. A short doc comment would make this deliberate trade-off explicit for future readers.

Comment thread src/lens/parse/text_dump.rs Outdated
}
};

let timestamp = 0.0_f64;

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

💡 Suggestion: Hardcoded 0.0 timestamp. Consider extracting a date from the PCH URL path (e.g. 2026/07/01 → Unix) so exported MRT data is usable in time-range queries. Fine for the initial merge.

- Fix parse_wrapped_prefix_line to handle IPv6 and long prefixes by
  checking whether route data exists after the next-hop column instead
  of using a line-length heuristic; swap loop order so route-line
  parsing is tried before wrapped-prefix fallback
- Add infer_timestamp_from_path() that extracts YYYY.MM.DD from
  PCH-style filenames/URLs and converts to Unix timestamp
- Add parse_text_dump_with_timestamp() to accept an explicit timestamp;
  CLI passes inferred timestamp, falls back to 0.0 when undetectable
- Document AS-set {/} flattening in as_path_from_tokens
@digizeph
digizeph merged commit 8e8ee2d into main Jul 21, 2026
1 check 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.

parse: auto-detect sh ip bgp text dumps and support --mrt-type for RIB/updates MRT output

1 participant