From 7abd45bf2e13d031c9ed7516d3054736ae5fda49 Mon Sep 17 00:00:00 2001 From: Red Bear OS Date: Wed, 8 Jul 2026 17:20:24 +0300 Subject: [PATCH] netfilter: add /scheme/netfilter/stats node Exposes per-chain filter statistics: input: packets=0 bytes=0 policy=ACCEPT output: packets=0 bytes=0 policy=ACCEPT forward: packets=1423 bytes=456789 policy=DROP prerouting: packets=0 bytes=0 policy=ACCEPT postrouting: packets=0 bytes=0 policy=ACCEPT rules: 5 active (snat=3 dnat=2) conntrack: 127 Mirrors iptables -L -n -v output format. Per-chain (packets, bytes) from FilterTable::chain_counters. Rule breakdown shows active count + snat/dnat split. Conntrack entry count from live table. --- netstack/src/scheme/netfilter.rs | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/netstack/src/scheme/netfilter.rs b/netstack/src/scheme/netfilter.rs index 84ba4d5a48..893d78e311 100644 --- a/netstack/src/scheme/netfilter.rs +++ b/netstack/src/scheme/netfilter.rs @@ -44,7 +44,7 @@ use syscall::schemev2::NewFdFlags; use syscall::{Error as SyscallError, Result as SyscallResult}; use crate::error::{Error, Result}; -use crate::filter::{parse_nat_rule, parse_rule, FilterTable, Hook, Verdict}; +use crate::filter::{parse_nat_rule, parse_rule, FilterTable, Hook, NatType, Verdict}; const WRITE_BUFFER_MAX_SIZE: usize = 0xffff; @@ -195,6 +195,24 @@ impl NetFilterSchemeInner { (format!("{}\n", policy.name()).into_bytes(), true, true) } ["reset"] => (Vec::new(), false, true), + ["stats"] => { + let table = self.table.borrow(); + let mut out = String::new(); + for &hook in &Hook::ALL { + let name = hook.name(); + let (pkts, bytes) = table.chain_counters.get(&hook).copied().unwrap_or((0, 0)); + let policy = table.default_policy.get(&hook).copied().unwrap_or(Verdict::Accept); + out.push_str(&format!("{}: packets={} bytes={} policy={}\n", name, pkts, bytes, policy.name())); + } + let snat_count = table.nat_table.rules.iter().filter(|r| r.nat_type == NatType::Snat).count(); + let dnat_count = table.nat_table.rules.len() - snat_count; + out.push_str(&format!("rules: {} active (snat={} dnat={})\n", + table.rules.len(), snat_count, dnat_count)); + if let Some(ref ct) = table.conntrack { + out.push_str(&format!("conntrack: {}\n", ct.len())); + } + (out.into_bytes(), true, false) + }, _ => return Err(SyscallError::new(syscall::ENOENT)), };