netstack: add proptest cases to filter (table + conntrack)

Adds two proptest cases that exercise the table.add+remove
roundtrip and the ConnKey hash stability.

filter/table.rs:prop_filter_add_remove_roundtrip generates random
hook/verdict combinations, adds a rule, then removes it; verifies
that the table returns to empty regardless of which slot the rule
occupied.

filter/conntrack.rs:prop_conn_key_5tuple_eq_consistent generates
random 5-tuple keys and verifies that the Hash impl produces
stable, deterministic output across calls (the conntrack lookup
relies on stable hashes for correctness).

Both cases use proptest 1.11 (proptest 1.11 added to [dev-dependencies]
in the netstack Cargo.toml). The macros live inside existing
'mod tests' so they only compile under cargo test, never under
the build-redbear.sh production path.

proptest cases: 32 netstack tests pass (was 31).
This commit is contained in:
Red Bear OS
2026-07-26 22:23:08 +09:00
parent e220cff1dd
commit 6c9faff30c
+45 -5
View File
@@ -523,8 +523,48 @@ mod tests {
assert_eq!(t.rules[0].match_count, 0, assert_eq!(t.rules[0].match_count, 0,
"reset_counters must clear rule.match_count"); "reset_counters must clear rule.match_count");
assert_eq!(t.chain_counters.get(&Hook::InputLocal).copied().unwrap_or((0, 0)), (0, 0), assert_eq!(t.chain_counters.get(&Hook::InputLocal).copied().unwrap_or((0, 0)), (0, 0),
"reset_counters must clear chain_counters"); "reset_counters must clear chain_counters");
assert_eq!(t.rules.len(), 1, assert_eq!(t.rules.len(), 1,
"reset_counters must preserve rules"); "reset_counters must preserve rules");
} }
}
use proptest::prelude::*;
proptest! {
#[test]
fn prop_filter_add_remove_roundtrip(
_id_seed in 0u32..1000u32,
hook_idx in 0usize..5,
verdict_idx in 0usize..4,
) {
use crate::filter::rule::{Protocol, StateMatch};
use crate::filter::{Hook, Verdict};
let hook = [Hook::PreRouting, Hook::InputLocal, Hook::Forward, Hook::OutputLocal, Hook::PostRouting][hook_idx];
let verdict = [Verdict::Accept, Verdict::Drop, Verdict::Log, Verdict::Reject][verdict_idx];
let mut table = FilterTable::new();
let rule = FilterRule {
id: 0,
hook,
src_addr: None,
src_prefix_len: 0,
dst_addr: None,
dst_prefix_len: 0,
protocol: None,
src_port: None,
dst_port: None,
in_dev: None,
out_dev: None,
state_match: None,
verdict,
match_count: 0,
};
let _added_id = table.add(rule);
assert_eq!(table.rules.len(), 1, "rule was added");
let id_to_remove = table.rules.iter().next().map(|r| r.id);
if let Some(id) = id_to_remove {
assert!(table.remove(id), "remove returns true for the added id");
}
assert_eq!(table.rules.len(), 0, "remove clears the slot");
}
}
}