mirror of
https://github.com/jmcorgan/fips.git
synced 2026-08-11 17:17:54 +00:00
Add fipstop TUI monitoring tool with smoothed metrics and quality indices
fipstop: ratatui-based TUI for real-time monitoring of a running FIPS daemon. Tabs and navigation: - 8 navigable tabs: Node, Peers, Transports, Sessions, Tree, Filters, Performance, Routing - Tab/BackTab navigation with group separators in tab bar - Table views with selectable rows, detail drill-down panels, and scrollbars Node tab: - Runtime info: pid, exe path, uptime, control socket path, TUN adapter name - Identity: npub, node_addr, ipv6 address - State summary with peer/session/link/transport/connection counts - TUN IPv6 traffic and forwarded transit traffic counters Peers tab: - Table with Name, Address, Conn, Depth, SRTT, Loss, LQI, Pkts Tx/Rx - Detail panel: identity, connection info, transport cross-reference, tree/bloom state, link stats, MMP metrics with LQI Sessions tab: - Table with Name, Remote Addr, State, Role, SRTT, Loss, SQI, Path MTU, Last Activity - Detail panel: identity, session info, traffic stats, MMP metrics with SQI Transports tab: - Hierarchical tree view: expandable transport parents with nested links (▼/▶ indicators, ├─/└─ tree chars, Space/Arrow to expand/collapse) - Transport detail: type-specific stats (UDP/TCP/Ethernet) - Link detail: peer cross-reference with MMP metrics and LQI Performance tab: - Link-layer MMP: SRTT, loss, ETX, LQI, goodput per peer - Session-layer MMP: SRTT, loss, ETX, SQI, path MTU per session - Trend indicators (rising/falling/stable) with context-aware coloring Routing tab: - Routing state: cache sizes, pending lookups, recent requests - Coordinate cache: entries, fill ratio, TTL, expiry, avg age - Statistics: forwarding, discovery request/response, error signal counters Tree tab: - Spanning tree position with 16 announce stats (inbound/outbound/cumulative) Filters tab: - Bloom filter announce stats, per-peer fill ratio and estimated node count MMP metrics enhancements: - Add etx_trend DualEwma for smoothed ETX tracking - Add smoothed_loss() and smoothed_etx() accessors (long-term EWMA) - LQI (Link Quality Index) = smoothed_etx * (1 + srtt_ms / 100) - SQI (Session Quality Index) = same formula for session layer - All loss/ETX displays prefer smoothed values with raw fallback Control socket: - Add smoothed_loss, smoothed_etx, lqi/sqi to show_peers, show_sessions, and show_mmp JSON responses - Rename fips_address to ipv6_addr in show_status and show_peers - Add tun_name and control_socket to show_status - FHS-compliant 3-tier default path: $XDG_RUNTIME_DIR, /run/fips, /tmp Node extensions: - Add started_at/uptime() to Node - Add tun_name() accessor Docker sidecar updates: - TCP transport support via FIPS_PEER_TRANSPORT env var - Build scripts include fipstop binary
This commit is contained in:
@@ -263,11 +263,12 @@ impl Node {
|
||||
}
|
||||
}
|
||||
|
||||
// Only application data resets the idle timer — MMP reports
|
||||
// (SenderReport, ReceiverReport, PathMtuNotification) do not.
|
||||
// Only application data resets the idle timer and traffic counters —
|
||||
// MMP reports (SenderReport, ReceiverReport, PathMtuNotification) do not.
|
||||
if msg_type == SessionMessageType::DataPacket.to_byte()
|
||||
&& let Some(entry) = self.sessions.get_mut(src_addr)
|
||||
{
|
||||
entry.record_recv(rest.len());
|
||||
entry.touch(Self::now_ms());
|
||||
}
|
||||
|
||||
@@ -1026,6 +1027,7 @@ impl Node {
|
||||
|
||||
// Re-borrow after send (which borrowed &mut self)
|
||||
if let Some(entry) = self.sessions.get_mut(dest_addr) {
|
||||
entry.record_sent(plaintext.len());
|
||||
if let Some(mmp) = entry.mmp_mut() {
|
||||
mmp.sender.record_sent(counter, timestamp, ciphertext.len());
|
||||
}
|
||||
|
||||
@@ -216,6 +216,9 @@ pub struct Node {
|
||||
/// Exchanged inside Noise handshake messages so peers can detect restarts.
|
||||
startup_epoch: [u8; 8],
|
||||
|
||||
/// Instant when the node was created, for uptime reporting.
|
||||
started_at: std::time::Instant,
|
||||
|
||||
// === Configuration ===
|
||||
/// Loaded configuration.
|
||||
config: Config,
|
||||
@@ -415,6 +418,7 @@ impl Node {
|
||||
Ok(Self {
|
||||
identity,
|
||||
startup_epoch,
|
||||
started_at: std::time::Instant::now(),
|
||||
config,
|
||||
state: NodeState::Created,
|
||||
is_leaf_only,
|
||||
@@ -509,6 +513,7 @@ impl Node {
|
||||
Self {
|
||||
identity,
|
||||
startup_epoch,
|
||||
started_at: std::time::Instant::now(),
|
||||
config,
|
||||
state: NodeState::Created,
|
||||
is_leaf_only: false,
|
||||
@@ -764,6 +769,11 @@ impl Node {
|
||||
self.state
|
||||
}
|
||||
|
||||
/// Get the node uptime.
|
||||
pub fn uptime(&self) -> std::time::Duration {
|
||||
self.started_at.elapsed()
|
||||
}
|
||||
|
||||
/// Check if node is operational.
|
||||
pub fn is_running(&self) -> bool {
|
||||
self.state.is_operational()
|
||||
@@ -829,6 +839,11 @@ impl Node {
|
||||
self.tun_state
|
||||
}
|
||||
|
||||
/// Get the TUN interface name, if active.
|
||||
pub fn tun_name(&self) -> Option<&str> {
|
||||
self.tun_name.as_deref()
|
||||
}
|
||||
|
||||
|
||||
// === Resource Limits ===
|
||||
|
||||
|
||||
@@ -73,6 +73,16 @@ pub(crate) struct SessionEntry {
|
||||
/// Session-layer MMP state. Initialized on Established transition.
|
||||
mmp: Option<MmpSessionState>,
|
||||
|
||||
// === Traffic Counters ===
|
||||
/// Total data packets sent on this session.
|
||||
packets_sent: u64,
|
||||
/// Total data packets received on this session.
|
||||
packets_recv: u64,
|
||||
/// Total data bytes sent on this session (FSP payload).
|
||||
bytes_sent: u64,
|
||||
/// Total data bytes received on this session (FSP payload).
|
||||
bytes_recv: u64,
|
||||
|
||||
// === Handshake Resend ===
|
||||
/// Encoded session-layer payload for resend (SessionSetup or SessionAck).
|
||||
/// Cleared on Established transition.
|
||||
@@ -102,6 +112,10 @@ impl SessionEntry {
|
||||
coords_warmup_remaining: 0,
|
||||
is_initiator,
|
||||
mmp: None,
|
||||
packets_sent: 0,
|
||||
packets_recv: 0,
|
||||
bytes_sent: 0,
|
||||
bytes_recv: 0,
|
||||
handshake_payload: None,
|
||||
resend_count: 0,
|
||||
next_resend_at_ms: 0,
|
||||
@@ -219,6 +233,25 @@ impl SessionEntry {
|
||||
self.mmp = Some(MmpSessionState::new(config, self.is_initiator));
|
||||
}
|
||||
|
||||
// === Traffic Counters ===
|
||||
|
||||
/// Record a sent data packet.
|
||||
pub(crate) fn record_sent(&mut self, bytes: usize) {
|
||||
self.packets_sent += 1;
|
||||
self.bytes_sent += bytes as u64;
|
||||
}
|
||||
|
||||
/// Record a received data packet.
|
||||
pub(crate) fn record_recv(&mut self, bytes: usize) {
|
||||
self.packets_recv += 1;
|
||||
self.bytes_recv += bytes as u64;
|
||||
}
|
||||
|
||||
/// Get traffic counters: (packets_sent, packets_recv, bytes_sent, bytes_recv).
|
||||
pub(crate) fn traffic_counters(&self) -> (u64, u64, u64, u64) {
|
||||
(self.packets_sent, self.packets_recv, self.bytes_sent, self.bytes_recv)
|
||||
}
|
||||
|
||||
// === Handshake Resend ===
|
||||
|
||||
/// Store the encoded session-layer payload for potential resend.
|
||||
|
||||
Reference in New Issue
Block a user