mirror of
https://github.com/jmcorgan/fips.git
synced 2026-08-10 00:26:59 +00:00
454 lines
14 KiB
Rust
454 lines
14 KiB
Rust
//! Node-level statistics for routing, forwarding, and discovery operations.
|
|
//!
|
|
//! Unlike `EthernetStats` (which uses `AtomicU64` + `Arc` for cross-task
|
|
//! sharing), these counters use plain `u64` because `Node` handlers run
|
|
//! on a single `&mut self` context. A `snapshot()` method produces a
|
|
//! copyable struct for control socket queries.
|
|
|
|
use serde::Serialize;
|
|
|
|
/// Forwarding statistics — packets and bytes for each outcome.
|
|
#[derive(Default)]
|
|
pub struct ForwardingStats {
|
|
pub received_packets: u64,
|
|
pub received_bytes: u64,
|
|
pub decode_error_packets: u64,
|
|
pub decode_error_bytes: u64,
|
|
pub ttl_exhausted_packets: u64,
|
|
pub ttl_exhausted_bytes: u64,
|
|
pub delivered_packets: u64,
|
|
pub delivered_bytes: u64,
|
|
pub forwarded_packets: u64,
|
|
pub forwarded_bytes: u64,
|
|
pub drop_no_route_packets: u64,
|
|
pub drop_no_route_bytes: u64,
|
|
pub drop_mtu_exceeded_packets: u64,
|
|
pub drop_mtu_exceeded_bytes: u64,
|
|
pub drop_send_error_packets: u64,
|
|
pub drop_send_error_bytes: u64,
|
|
pub originated_packets: u64,
|
|
pub originated_bytes: u64,
|
|
}
|
|
|
|
impl ForwardingStats {
|
|
pub fn record_received(&mut self, bytes: usize) {
|
|
self.received_packets += 1;
|
|
self.received_bytes += bytes as u64;
|
|
}
|
|
|
|
pub fn record_decode_error(&mut self, bytes: usize) {
|
|
self.decode_error_packets += 1;
|
|
self.decode_error_bytes += bytes as u64;
|
|
}
|
|
|
|
pub fn record_ttl_exhausted(&mut self, bytes: usize) {
|
|
self.ttl_exhausted_packets += 1;
|
|
self.ttl_exhausted_bytes += bytes as u64;
|
|
}
|
|
|
|
pub fn record_delivered(&mut self, bytes: usize) {
|
|
self.delivered_packets += 1;
|
|
self.delivered_bytes += bytes as u64;
|
|
}
|
|
|
|
pub fn record_forwarded(&mut self, bytes: usize) {
|
|
self.forwarded_packets += 1;
|
|
self.forwarded_bytes += bytes as u64;
|
|
}
|
|
|
|
pub fn record_drop_no_route(&mut self, bytes: usize) {
|
|
self.drop_no_route_packets += 1;
|
|
self.drop_no_route_bytes += bytes as u64;
|
|
}
|
|
|
|
pub fn record_drop_mtu_exceeded(&mut self, bytes: usize) {
|
|
self.drop_mtu_exceeded_packets += 1;
|
|
self.drop_mtu_exceeded_bytes += bytes as u64;
|
|
}
|
|
|
|
pub fn record_drop_send_error(&mut self, bytes: usize) {
|
|
self.drop_send_error_packets += 1;
|
|
self.drop_send_error_bytes += bytes as u64;
|
|
}
|
|
|
|
pub fn record_originated(&mut self, bytes: usize) {
|
|
self.originated_packets += 1;
|
|
self.originated_bytes += bytes as u64;
|
|
}
|
|
|
|
pub fn snapshot(&self) -> ForwardingStatsSnapshot {
|
|
ForwardingStatsSnapshot {
|
|
received_packets: self.received_packets,
|
|
received_bytes: self.received_bytes,
|
|
decode_error_packets: self.decode_error_packets,
|
|
decode_error_bytes: self.decode_error_bytes,
|
|
ttl_exhausted_packets: self.ttl_exhausted_packets,
|
|
ttl_exhausted_bytes: self.ttl_exhausted_bytes,
|
|
delivered_packets: self.delivered_packets,
|
|
delivered_bytes: self.delivered_bytes,
|
|
forwarded_packets: self.forwarded_packets,
|
|
forwarded_bytes: self.forwarded_bytes,
|
|
drop_no_route_packets: self.drop_no_route_packets,
|
|
drop_no_route_bytes: self.drop_no_route_bytes,
|
|
drop_mtu_exceeded_packets: self.drop_mtu_exceeded_packets,
|
|
drop_mtu_exceeded_bytes: self.drop_mtu_exceeded_bytes,
|
|
drop_send_error_packets: self.drop_send_error_packets,
|
|
drop_send_error_bytes: self.drop_send_error_bytes,
|
|
originated_packets: self.originated_packets,
|
|
originated_bytes: self.originated_bytes,
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Discovery statistics — packet counts for request and response handling.
|
|
#[derive(Default)]
|
|
pub struct DiscoveryStats {
|
|
// Request counters
|
|
pub req_received: u64,
|
|
pub req_decode_error: u64,
|
|
pub req_duplicate: u64,
|
|
pub req_target_is_us: u64,
|
|
pub req_forwarded: u64,
|
|
pub req_ttl_exhausted: u64,
|
|
pub req_initiated: u64,
|
|
pub req_deduplicated: u64,
|
|
pub req_backoff_suppressed: u64,
|
|
pub req_forward_rate_limited: u64,
|
|
pub req_bloom_miss: u64,
|
|
pub req_no_tree_peer: u64,
|
|
pub req_fallback_forwarded: u64,
|
|
// Response counters
|
|
pub resp_received: u64,
|
|
pub resp_decode_error: u64,
|
|
pub resp_forwarded: u64,
|
|
pub resp_identity_miss: u64,
|
|
pub resp_proof_failed: u64,
|
|
pub resp_accepted: u64,
|
|
pub resp_timed_out: u64,
|
|
}
|
|
|
|
impl DiscoveryStats {
|
|
pub fn snapshot(&self) -> DiscoveryStatsSnapshot {
|
|
DiscoveryStatsSnapshot {
|
|
req_received: self.req_received,
|
|
req_decode_error: self.req_decode_error,
|
|
req_duplicate: self.req_duplicate,
|
|
req_target_is_us: self.req_target_is_us,
|
|
req_forwarded: self.req_forwarded,
|
|
req_ttl_exhausted: self.req_ttl_exhausted,
|
|
req_initiated: self.req_initiated,
|
|
req_deduplicated: self.req_deduplicated,
|
|
req_backoff_suppressed: self.req_backoff_suppressed,
|
|
req_forward_rate_limited: self.req_forward_rate_limited,
|
|
req_bloom_miss: self.req_bloom_miss,
|
|
req_no_tree_peer: self.req_no_tree_peer,
|
|
req_fallback_forwarded: self.req_fallback_forwarded,
|
|
resp_received: self.resp_received,
|
|
resp_decode_error: self.resp_decode_error,
|
|
resp_forwarded: self.resp_forwarded,
|
|
resp_identity_miss: self.resp_identity_miss,
|
|
resp_proof_failed: self.resp_proof_failed,
|
|
resp_accepted: self.resp_accepted,
|
|
resp_timed_out: self.resp_timed_out,
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Spanning tree statistics — announce handling and parent tracking.
|
|
#[derive(Default)]
|
|
pub struct TreeStats {
|
|
// Inbound announce handling
|
|
pub received: u64,
|
|
pub decode_error: u64,
|
|
pub unknown_peer: u64,
|
|
pub addr_mismatch: u64,
|
|
pub sig_failed: u64,
|
|
pub stale: u64,
|
|
pub accepted: u64,
|
|
pub parent_switched: u64,
|
|
pub loop_detected: u64,
|
|
pub ancestry_changed: u64,
|
|
// Outbound announce sending
|
|
pub sent: u64,
|
|
pub rate_limited: u64,
|
|
pub send_failed: u64,
|
|
// Cumulative events
|
|
pub parent_switches: u64,
|
|
pub parent_losses: u64,
|
|
pub flap_dampened: u64,
|
|
}
|
|
|
|
impl TreeStats {
|
|
pub fn snapshot(&self) -> TreeStatsSnapshot {
|
|
TreeStatsSnapshot {
|
|
received: self.received,
|
|
decode_error: self.decode_error,
|
|
unknown_peer: self.unknown_peer,
|
|
addr_mismatch: self.addr_mismatch,
|
|
sig_failed: self.sig_failed,
|
|
stale: self.stale,
|
|
accepted: self.accepted,
|
|
parent_switched: self.parent_switched,
|
|
loop_detected: self.loop_detected,
|
|
ancestry_changed: self.ancestry_changed,
|
|
sent: self.sent,
|
|
rate_limited: self.rate_limited,
|
|
send_failed: self.send_failed,
|
|
parent_switches: self.parent_switches,
|
|
parent_losses: self.parent_losses,
|
|
flap_dampened: self.flap_dampened,
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Bloom filter statistics — filter announce handling.
|
|
#[derive(Default)]
|
|
pub struct BloomStats {
|
|
// Inbound announce handling
|
|
pub received: u64,
|
|
pub decode_error: u64,
|
|
pub invalid: u64,
|
|
pub unknown_peer: u64,
|
|
pub stale: u64,
|
|
pub fill_exceeded: u64,
|
|
pub accepted: u64,
|
|
// Outbound announce sending
|
|
pub sent: u64,
|
|
pub debounce_suppressed: u64,
|
|
pub send_failed: u64,
|
|
// Delta compression
|
|
pub deltas_sent: u64,
|
|
pub full_sends: u64,
|
|
pub nacks_sent: u64,
|
|
pub nacks_received: u64,
|
|
// Adaptive sizing
|
|
pub size_changes: u64,
|
|
// Compression tracking
|
|
pub total_compressed_bytes: u64,
|
|
pub total_raw_bytes: u64,
|
|
}
|
|
|
|
impl BloomStats {
|
|
pub fn snapshot(&self) -> BloomStatsSnapshot {
|
|
BloomStatsSnapshot {
|
|
received: self.received,
|
|
decode_error: self.decode_error,
|
|
invalid: self.invalid,
|
|
unknown_peer: self.unknown_peer,
|
|
stale: self.stale,
|
|
fill_exceeded: self.fill_exceeded,
|
|
accepted: self.accepted,
|
|
sent: self.sent,
|
|
debounce_suppressed: self.debounce_suppressed,
|
|
send_failed: self.send_failed,
|
|
deltas_sent: self.deltas_sent,
|
|
full_sends: self.full_sends,
|
|
nacks_sent: self.nacks_sent,
|
|
nacks_received: self.nacks_received,
|
|
size_changes: self.size_changes,
|
|
total_compressed_bytes: self.total_compressed_bytes,
|
|
total_raw_bytes: self.total_raw_bytes,
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Error signal statistics — counts of each error signal type received.
|
|
#[derive(Default)]
|
|
pub struct ErrorSignalStats {
|
|
pub coords_required: u64,
|
|
pub path_broken: u64,
|
|
pub mtu_exceeded: u64,
|
|
}
|
|
|
|
impl ErrorSignalStats {
|
|
pub fn snapshot(&self) -> ErrorSignalStatsSnapshot {
|
|
ErrorSignalStatsSnapshot {
|
|
coords_required: self.coords_required,
|
|
path_broken: self.path_broken,
|
|
mtu_exceeded: self.mtu_exceeded,
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Congestion event statistics — ECN CE tracking and detection triggers.
|
|
#[derive(Default)]
|
|
pub struct CongestionStats {
|
|
/// Packets forwarded with the CE flag set (incoming CE or locally detected).
|
|
pub ce_forwarded: u64,
|
|
/// CE-flagged packets received at this node as final destination.
|
|
pub ce_received: u64,
|
|
/// Number of times detect_congestion() returned true.
|
|
pub congestion_detected: u64,
|
|
/// Rising-edge transport kernel drop events (not-dropping → dropping).
|
|
pub kernel_drop_events: u64,
|
|
}
|
|
|
|
impl CongestionStats {
|
|
pub fn record_ce_forwarded(&mut self) {
|
|
self.ce_forwarded += 1;
|
|
}
|
|
|
|
pub fn record_ce_received(&mut self) {
|
|
self.ce_received += 1;
|
|
}
|
|
|
|
pub fn record_congestion_detected(&mut self) {
|
|
self.congestion_detected += 1;
|
|
}
|
|
|
|
pub fn record_kernel_drop_event(&mut self) {
|
|
self.kernel_drop_events += 1;
|
|
}
|
|
|
|
pub fn snapshot(&self) -> CongestionStatsSnapshot {
|
|
CongestionStatsSnapshot {
|
|
ce_forwarded: self.ce_forwarded,
|
|
ce_received: self.ce_received,
|
|
congestion_detected: self.congestion_detected,
|
|
kernel_drop_events: self.kernel_drop_events,
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Aggregate node statistics.
|
|
#[derive(Default)]
|
|
pub struct NodeStats {
|
|
pub forwarding: ForwardingStats,
|
|
pub discovery: DiscoveryStats,
|
|
pub tree: TreeStats,
|
|
pub bloom: BloomStats,
|
|
pub errors: ErrorSignalStats,
|
|
pub congestion: CongestionStats,
|
|
}
|
|
|
|
impl NodeStats {
|
|
pub fn new() -> Self {
|
|
Self::default()
|
|
}
|
|
|
|
pub fn snapshot(&self) -> NodeStatsSnapshot {
|
|
NodeStatsSnapshot {
|
|
forwarding: self.forwarding.snapshot(),
|
|
discovery: self.discovery.snapshot(),
|
|
tree: self.tree.snapshot(),
|
|
bloom: self.bloom.snapshot(),
|
|
errors: self.errors.snapshot(),
|
|
congestion: self.congestion.snapshot(),
|
|
}
|
|
}
|
|
}
|
|
|
|
// --- Snapshot types (copyable, serializable) ---
|
|
|
|
#[derive(Clone, Debug, Default, Serialize)]
|
|
pub struct ForwardingStatsSnapshot {
|
|
pub received_packets: u64,
|
|
pub received_bytes: u64,
|
|
pub decode_error_packets: u64,
|
|
pub decode_error_bytes: u64,
|
|
pub ttl_exhausted_packets: u64,
|
|
pub ttl_exhausted_bytes: u64,
|
|
pub delivered_packets: u64,
|
|
pub delivered_bytes: u64,
|
|
pub forwarded_packets: u64,
|
|
pub forwarded_bytes: u64,
|
|
pub drop_no_route_packets: u64,
|
|
pub drop_no_route_bytes: u64,
|
|
pub drop_mtu_exceeded_packets: u64,
|
|
pub drop_mtu_exceeded_bytes: u64,
|
|
pub drop_send_error_packets: u64,
|
|
pub drop_send_error_bytes: u64,
|
|
pub originated_packets: u64,
|
|
pub originated_bytes: u64,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Default, Serialize)]
|
|
pub struct DiscoveryStatsSnapshot {
|
|
pub req_received: u64,
|
|
pub req_decode_error: u64,
|
|
pub req_duplicate: u64,
|
|
pub req_target_is_us: u64,
|
|
pub req_forwarded: u64,
|
|
pub req_ttl_exhausted: u64,
|
|
pub req_initiated: u64,
|
|
pub req_deduplicated: u64,
|
|
pub req_backoff_suppressed: u64,
|
|
pub req_forward_rate_limited: u64,
|
|
pub req_bloom_miss: u64,
|
|
pub req_no_tree_peer: u64,
|
|
pub req_fallback_forwarded: u64,
|
|
pub resp_received: u64,
|
|
pub resp_decode_error: u64,
|
|
pub resp_forwarded: u64,
|
|
pub resp_identity_miss: u64,
|
|
pub resp_proof_failed: u64,
|
|
pub resp_accepted: u64,
|
|
pub resp_timed_out: u64,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Default, Serialize)]
|
|
pub struct TreeStatsSnapshot {
|
|
pub received: u64,
|
|
pub decode_error: u64,
|
|
pub unknown_peer: u64,
|
|
pub addr_mismatch: u64,
|
|
pub sig_failed: u64,
|
|
pub stale: u64,
|
|
pub accepted: u64,
|
|
pub parent_switched: u64,
|
|
pub loop_detected: u64,
|
|
pub ancestry_changed: u64,
|
|
pub sent: u64,
|
|
pub rate_limited: u64,
|
|
pub send_failed: u64,
|
|
pub parent_switches: u64,
|
|
pub parent_losses: u64,
|
|
pub flap_dampened: u64,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Default, Serialize)]
|
|
pub struct BloomStatsSnapshot {
|
|
pub received: u64,
|
|
pub decode_error: u64,
|
|
pub invalid: u64,
|
|
pub unknown_peer: u64,
|
|
pub stale: u64,
|
|
pub fill_exceeded: u64,
|
|
pub accepted: u64,
|
|
pub sent: u64,
|
|
pub debounce_suppressed: u64,
|
|
pub send_failed: u64,
|
|
pub deltas_sent: u64,
|
|
pub full_sends: u64,
|
|
pub nacks_sent: u64,
|
|
pub nacks_received: u64,
|
|
pub size_changes: u64,
|
|
pub total_compressed_bytes: u64,
|
|
pub total_raw_bytes: u64,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Default, Serialize)]
|
|
pub struct ErrorSignalStatsSnapshot {
|
|
pub coords_required: u64,
|
|
pub path_broken: u64,
|
|
pub mtu_exceeded: u64,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Default, Serialize)]
|
|
pub struct CongestionStatsSnapshot {
|
|
pub ce_forwarded: u64,
|
|
pub ce_received: u64,
|
|
pub congestion_detected: u64,
|
|
pub kernel_drop_events: u64,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Default, Serialize)]
|
|
pub struct NodeStatsSnapshot {
|
|
pub forwarding: ForwardingStatsSnapshot,
|
|
pub discovery: DiscoveryStatsSnapshot,
|
|
pub tree: TreeStatsSnapshot,
|
|
pub bloom: BloomStatsSnapshot,
|
|
pub errors: ErrorSignalStatsSnapshot,
|
|
pub congestion: CongestionStatsSnapshot,
|
|
}
|