diff --git a/netstack/src/filter/conntrack.rs b/netstack/src/filter/conntrack.rs index 035bfdb96f..16acd67f82 100644 --- a/netstack/src/filter/conntrack.rs +++ b/netstack/src/filter/conntrack.rs @@ -440,6 +440,55 @@ impl ConntrackTable { self.entries.len() } + /// Per-protocol and per-state connection breakdown. + /// Returns lines like: + /// tcp_entries: N (est=N syn=N syn_recv=N fin=N tw=N close=N) + /// udp_entries: N + /// icmp_entries: N + /// over_limit: N + /// total_entries: N + pub fn stats(&self) -> alloc::string::String { + let mut tcp = 0u32; + let mut tcp_est = 0u32; + let mut tcp_syn = 0u32; + let mut tcp_syn_recv = 0u32; + let mut tcp_fin = 0u32; + let mut tcp_tw = 0u32; + let mut tcp_close = 0u32; + let mut udp = 0u32; + let mut icmp = 0u32; + + for entry in self.entries.values() { + match entry.key.l4proto { + 6 => { + tcp += 1; + match entry.tcp_state { + TcpTracking::None => {} + TcpTracking::SynSent => tcp_syn += 1, + TcpTracking::SynRecv => tcp_syn_recv += 1, + TcpTracking::Established => tcp_est += 1, + TcpTracking::FinWait => tcp_fin += 1, + TcpTracking::TimeWait => tcp_tw += 1, + TcpTracking::Close => tcp_close += 1, + } + } + 17 => udp += 1, + 1 | 58 => icmp += 1, + _ => {} + } + } + + let mut out = alloc::format!( + "tcp_entries: {} (est={} syn={} syn_recv={} fin={} tw={} close={})\n", + tcp, tcp_est, tcp_syn, tcp_syn_recv, tcp_fin, tcp_tw, tcp_close + ); + out.push_str(&alloc::format!("udp_entries: {}\n", udp)); + out.push_str(&alloc::format!("icmp_entries: {}\n", icmp)); + out.push_str(&alloc::format!("over_limit: {}\n", self.over_limit_count)); + out.push_str(&alloc::format!("total_entries: {}\n", self.entries.len())); + out + } + fn check_syn_limit(&mut self, src: IpAddress, now: Instant) -> bool { const SYN_LIMIT: u32 = 100; const SYN_WINDOW: Duration = Duration::from_secs(1); diff --git a/netstack/src/scheme/netcfg/mod.rs b/netstack/src/scheme/netcfg/mod.rs index edfeb4c44e..6edac9fae7 100644 --- a/netstack/src/scheme/netcfg/mod.rs +++ b/netstack/src/scheme/netcfg/mod.rs @@ -140,6 +140,10 @@ fn mk_root_node( } out.push_str("filter chains:\n"); out.push_str(&filter_table.borrow().chain_summary()); + if let Some(ref ct) = filter_table.borrow().conntrack { + out.push_str("conntrack stats:\n"); + out.push_str(&ct.stats()); + } out } }, diff --git a/netstack/src/scheme/netfilter.rs b/netstack/src/scheme/netfilter.rs index 59a154de8b..e26ff6d04a 100644 --- a/netstack/src/scheme/netfilter.rs +++ b/netstack/src/scheme/netfilter.rs @@ -173,6 +173,12 @@ impl NetFilterSchemeInner { .unwrap_or_else(|| b"conntrack: not enabled\n".to_vec()), true, false, ), + ["conntrack", "stats"] => ( + self.table.borrow().conntrack.as_ref() + .map(|ct| ct.stats().into_bytes()) + .unwrap_or_else(|| b"conntrack: not enabled\n".to_vec()), + true, false, + ), ["log"] => { let buf = &self.table.borrow().log_buffer; let mut out = String::new(); @@ -210,6 +216,7 @@ impl NetFilterSchemeInner { table.rules.len(), snat_count, dnat_count)); if let Some(ref ct) = table.conntrack { out.push_str(&format!("conntrack: {}\n", ct.len())); + out.push_str(&ct.stats()); } (out.into_bytes(), true, false) },