Files
fips/src/control/snapshots/show_routing.json
T
Johnathan Corgan 4654e41f87 Keep verified coordinates on a single PathBroken and demote them only on reports from two links
A PathBroken signal carries no end-to-end authentication, yet one admitted
signal deleted the destination's cached coordinates even when a lookup had
verified them. That removed the only thing refusing the next forged
coordinate warm, so one forged PathBroken followed by one forged SessionSetup
moved a node's route to any destination it had a session with.

An admitted PathBroken now treats a verified entry differently from a hint:

- A hint, or a verification that has aged out, is still removed.
- A verified entry is kept while the lookup re-validates it. It is demoted
  to a hint, keeping its value, only when signals naming the destination
  arrive over two different links within 15 seconds.
- The re-lookup now runs on every admitted PathBroken, not only when the
  destination's identity is cached.

The vote is the authenticated link peer a signal arrived over, not the
reporter it names: the reporter is plaintext the sender chooses, so every
signal from one neighbour, forged or relayed, is one vote however many
reporters it names. A node whose signals all arrive over one link, such as
a leaf with a single peer, never reaches the quorum; a stale verified entry
there lasts until the lookup each signal starts answers and replaces it, or
at most until its verification ages out after five minutes, when the next
signal removes it as it would a hint.

The link quorum is a new sans-IO module with an injected clock. A lookup
that verifies a destination again clears its quorum, so a report about the
old path cannot combine with one about the new. When the path-MTU release
fires, a kept entry also forgets the path MTU stored with it, as the removed
entry used to; when the release is rate limited, a kept entry keeps it.

New error-signal counters broken_below_quorum and broken_demoted count the
two outcomes for a verified entry. Two advisory counters measure how often an
admitted PathBroken looks implausible, without refusing anything:

- broken_link_mismatch: the signal arrived over a link other than the one
  this node would forward to the destination on. The check uses the
  non-touching next-hop preview, so it does not refresh the cache entry.
- broken_reporter_mismatch: the reporter is this node or the destination, or
  its known coordinates (a direct peer's from the tree, otherwise the
  coordinate cache) are not strictly closer to the destination than this
  node's.

Both have a non-zero healthy floor: a genuine report can arrive off the
forward link, and a reporter's view of the destination can differ from this
node's. Most reporters' coordinates are not known here and are not counted.
They size the forged-signal problem; they are not alarms, and the signal is
acted on in full either way. The handler now receives the authenticated link
peer the datagram arrived over, which the quorum and the link check need.

The source-recovery steps in the mesh operation design described a PathBroken
as removing the destination's cached coordinates and discovery as depending
on a cached identity; they now describe the behaviour above.
2026-10-01 22:40:40 +00:00

126 lines
3.5 KiB
JSON

{
"data": {
"congestion": {
"ce_forwarded": 0,
"ce_received": 0,
"congestion_detected": 0,
"kernel_drop_events": 0
},
"coord_cache_entries": 0,
"discovery": {
"req_backoff_suppressed": 0,
"req_bloom_miss": 0,
"req_decode_error": 0,
"req_dedup_cache_full": 0,
"req_dedup_evicted": 0,
"req_deduplicated": 0,
"req_duplicate": 0,
"req_fallback_forwarded": 0,
"req_forward_rate_limited": 0,
"req_forwarded": 0,
"req_initiated": 0,
"req_no_tree_peer": 0,
"req_own_loopback": 0,
"req_received": 0,
"req_sign_rate_limited": 0,
"req_target_is_us": 0,
"req_ttl_exhausted": 0,
"resp_accepted": 0,
"resp_decode_error": 0,
"resp_forwarded": 0,
"resp_identity_miss": 0,
"resp_no_route": 0,
"resp_proof_failed": 0,
"resp_received": 0,
"resp_timed_out": 0,
"resp_unsolicited": 0
},
"error_signals": {
"broken_below_quorum": 0,
"broken_demoted": 0,
"broken_link_mismatch": 0,
"broken_reporter_mismatch": 0,
"coords_required": 0,
"emit_limiter_at_capacity": 0,
"emit_over_dest_interval": 0,
"emit_over_peer_budget": 0,
"lookup_resp_mtu_below_floor": 0,
"mtu_exceeded": 0,
"mtu_exceeded_below_floor": 0,
"mtu_exceeded_uncorroborated": 0,
"path_broken": 0,
"path_mtu_notif_below_floor": 0,
"unbound_broken": 0,
"unbound_coords": 0,
"unbound_forged": 0,
"unbound_mtu": 0
},
"forwarding": {
"coord_hint_changed": 0,
"coord_hint_rejected": 0,
"coord_warm_foreign_root": 0,
"coord_warm_key_mismatch": 0,
"decode_error_bytes": 0,
"decode_error_packets": 0,
"delivered_bytes": 0,
"delivered_packets": 0,
"drop_mtu_exceeded_bytes": 0,
"drop_mtu_exceeded_packets": 0,
"drop_no_route_bytes": 0,
"drop_no_route_packets": 0,
"drop_send_error_bytes": 0,
"drop_send_error_packets": 0,
"forwarded_bytes": 0,
"forwarded_packets": 0,
"originated_bytes": 0,
"originated_packets": 0,
"received_bytes": 0,
"received_packets": 0,
"route_crosslink_ascend": 0,
"route_crosslink_descend": 0,
"route_direct_peer": 0,
"route_tree_down": 0,
"route_tree_down_cross": 0,
"route_tree_up": 0,
"ttl_exhausted_bytes": 0,
"ttl_exhausted_packets": 0,
"warm_malformed_bytes": 0,
"warm_malformed_packets": 0
},
"identity_cache_entries": 0,
"lookup": {
"req_backoff_suppressed": 0,
"req_bloom_miss": 0,
"req_decode_error": 0,
"req_dedup_cache_full": 0,
"req_dedup_evicted": 0,
"req_deduplicated": 0,
"req_duplicate": 0,
"req_fallback_forwarded": 0,
"req_forward_rate_limited": 0,
"req_forwarded": 0,
"req_initiated": 0,
"req_no_tree_peer": 0,
"req_own_loopback": 0,
"req_received": 0,
"req_sign_rate_limited": 0,
"req_target_is_us": 0,
"req_ttl_exhausted": 0,
"resp_accepted": 0,
"resp_decode_error": 0,
"resp_forwarded": 0,
"resp_identity_miss": 0,
"resp_no_route": 0,
"resp_proof_failed": 0,
"resp_received": 0,
"resp_timed_out": 0,
"resp_unsolicited": 0
},
"pending_lookups": [],
"pending_tun_destinations": 0,
"pending_tun_packets": 0,
"recent_requests": 0,
"retries": []
},
"status": "ok"
}