From dd838eec9a8458ccec1fbb80a71c1c79e08520b8 Mon Sep 17 00:00:00 2001 From: Rayhaan Jaufeerally Date: Sat, 19 Sep 2026 22:20:37 +0200 Subject: [PATCH] feature: decoded hover for UPDATE bytes; opt-in auto-refresh - Hovering (or focusing) a byte segment opens a popup that decodes it: header fields, the two length fields, and each path attribute with its flags spelled out and its value in readable form. AS_PATH shows segment type, ASNs, path length and ASN width; COMMUNITIES shows well-known names; LARGE_COMMUNITIES shows the SwissIX meaning of known values; MP_REACH/UNREACH show address family, next hop and prefixes; NLRI and withdrawn routes list prefixes. Decoding happens on hover from the bytes themselves. /api/routes/local/wire gains four_octet_asn so the AS_PATH width is known. - The page is now a snapshot by default. ?refresh= (1..3600) turns on reloading of all tables at that interval and connects the event log; the header offers 2s / 5s / 30s, "turn off" and "reload now". Events only feed the log, so the tables never change faster than asked. The reader's own announce / withdraw still shows its effect at once. - Removed the "tables refresh on every event" footer text. Co-Authored-By: Claude Fable 5.1 --- crates/integration-tests/tests/http_api.rs | 1 + crates/web/src/api.rs | 3 + crates/web/static/statusz.html | 182 +++++++++++++++++---- 3 files changed, 156 insertions(+), 30 deletions(-) diff --git a/crates/integration-tests/tests/http_api.rs b/crates/integration-tests/tests/http_api.rs index 4c18cee..fd88d69 100644 --- a/crates/integration-tests/tests/http_api.rs +++ b/crates/integration-tests/tests/http_api.rs @@ -113,6 +113,7 @@ async fn test_announce_over_http_reaches_peer() -> Result<()> { let wire = get_json(&client, &format!("{base}/api/routes/local/wire?prefix=10.100.1.0/24")).await?; assert_eq!(wire.as_array().unwrap().len(), 1); assert_eq!(wire[0]["peer_asn"], PEER_ASN); + assert_eq!(wire[0]["four_octet_asn"], true); assert_eq!(wire[0]["summary"]["as_path"], json!([LOCAL_ASN, LOCAL_ASN])); let hex = wire[0]["hex"].as_str().unwrap(); let ctx = ParserContext { four_octet_asn: peer.ctx.four_octet_asn, address_family: None }; diff --git a/crates/web/src/api.rs b/crates/web/src/api.rs index 6dc151e..e1dc1fd 100644 --- a/crates/web/src/api.rs +++ b/crates/web/src/api.rs @@ -256,6 +256,8 @@ pub struct WireView { pub peer_addr: String, pub peer_asn: u32, pub next_hop: String, + /// Whether AS numbers in AS_PATH are 4 bytes wide on this session. + pub four_octet_asn: bool, /// Complete BGP message including the 19-byte header, as lowercase hex. pub hex: String, pub length: usize, @@ -426,6 +428,7 @@ pub async fn wire( peer_addr: session.peer.addr.to_string(), peer_asn: session.peer.asn, next_hop: session.local_addr.ip().to_string(), + four_octet_asn: session.four_octet_asn, hex: buf.iter().map(|b| format!("{b:02x}")).collect(), length: buf.len(), summary: summarize(&update.path_attributes), diff --git a/crates/web/static/statusz.html b/crates/web/static/statusz.html index afd9c68..2437351 100644 --- a/crates/web/static/statusz.html +++ b/crates/web/static/statusz.html @@ -34,6 +34,14 @@ pre.bytes { border: 1px solid #99a; background: #fff; padding: 4px; margin: 4px .hh { background: #dde; } .hl { background: #fcc; } .ha { background: #cfc; } .ha2 { background: #ada; } .hn { background: #ffc; } #event-log { font-family: monospace; font-size: 12px; max-height: 22em; overflow-y: auto; } #event-log div { white-space: pre-wrap; } +pre.bytes span[data-kind] { cursor: help; } +pre.bytes span[data-kind]:hover, pre.bytes span[data-kind]:focus { outline: 1px solid #00c; } +#tip { position: absolute; z-index: 10; max-width: 46em; background: #fff; border: 1px solid #99a; font-size: 12px; } +#tip .tip-title { background: #eef; font-weight: bold; text-align: center; padding: 1px 6px; border-bottom: 1px solid #99a; } +#tip table { border-collapse: collapse; } +#tip td { padding: 1px 6px; vertical-align: top; } +#tip td:first-child { color: #666; white-space: nowrap; } +#tip td.mono { word-break: break-word; } address { font-size: smaller; margin-top: 2em; color: #444; } @@ -50,7 +58,7 @@ Router ID ..., local AS ...
Running on ...
View: variables, status json, peers json, local routes json, received routes json
-Live updates: connecting
+
@@ -95,9 +103,11 @@ Live updates: connecting

Event log

+
+ -
ferrix_bgp · /statusz /metricz /api/events · tables refresh on every event
+
ferrix_bgp · /statusz /metricz /api/events