diff --git a/netstack/src/filter/conntrack.rs b/netstack/src/filter/conntrack.rs index fe686fb66e..53febed445 100644 --- a/netstack/src/filter/conntrack.rs +++ b/netstack/src/filter/conntrack.rs @@ -856,116 +856,3 @@ fn make_v6_ctx<'a>(protocol: u8, packet: &'a [u8]) -> PacketContext<'a> { "TimeWait state should keep the connection alive for 120s"); } } - - -#[cfg(test)] -mod proptest_tests { - extern crate proptest; - use super::*; - use proptest::prelude::*; - use crate::filter::Hook; - use smoltcp::time::Instant; - use core::hash::{Hash, Hasher}; - - fn ipv4_addr() -> impl Strategy { - (any::(), any::(), any::(), any::()) - .prop_map(|(a, b, c, d)| IpAddress::v4(a, b, c, d)) - } - - fn ipv6_addr() -> impl Strategy { - (any::<[u8; 16]>()) - .prop_map(|octets| IpAddress::from(smoltcp::wire::Ipv6Address::from_bytes(&octets))) - } - - fn any_ip_addr() -> impl Strategy { - prop_oneof![ipv4_addr(), ipv6_addr()] - } - - fn conn_key_strat() -> impl Strategy { - ( - prop_oneof![Just(4u8), Just(6u8)], - prop_oneof![Just(6u8), Just(17u8), Just(1u8), Just(58u8)], - any_ip_addr(), - any_ip_addr(), - any::(), - any::(), - ) - .prop_map(|(l3num, l4proto, src_addr, dst_addr, src_port, dst_port)| ConnKey { - l3num, - l4proto, - src_addr, - dst_addr, - src_port, - dst_port, - }) - } - - proptest! { - #[test] - fn prop_conntrack_insert_lookup_no_panic(key in conn_key_strat()) { - let reply_key = key.reply(); - let double_reply = reply_key.reply(); - assert_eq!(key, double_reply, - "reply(reply(key)) must equal the original key"); - - use std::collections::hash_map::DefaultHasher; - let mut h1 = DefaultHasher::new(); - let mut h2 = DefaultHasher::new(); - key.hash(&mut h1); - key.hash(&mut h2); - assert_eq!(h1.finish(), h2.finish(), - "repeated hash must be deterministic"); - - use std::collections::BTreeMap; - let mut map: BTreeMap = BTreeMap::new(); - map.insert(key.clone(), 1); - assert!(map.contains_key(&key), "inserted key must be found"); - assert_eq!(map.get(&key), Some(&1), "value must be retrievable"); - - if key.src_addr != key.dst_addr || key.src_port != key.dst_port { - assert_ne!(key, reply_key, - "reply key must differ from original when asymmetric"); - } - } - - #[test] - fn prop_conntrack_bulk_insert_no_panic( - keys in proptest::collection::vec(conn_key_strat(), 0..200) - ) { - for key in &keys { - let _reply = key.reply(); - for other in &keys { - if key == other { - assert_eq!(key.reply(), other.reply(), - "equal keys must have equal replies"); - } - } - } - } - - #[test] - fn prop_rate_limiters_independent( - syn_count in 0u32..50u32, - icmp_count in 0u32..10u32, - ) { - let mut ct = ConntrackTable::new(); - let now = Instant::from_secs(0); - for _i in 0..syn_count { - let _ = ct.check_syn_limit(IpAddress::v4(10, 0, 0, 1), now); - } - for _i in 0..icmp_count { - let limited = ct.check_icmp_limit(IpAddress::v4(10, 0, 0, 2), now); - assert!(!limited || icmp_count > 20, - "ICMP should not be limited at count {} (limit is 20/sec)", icmp_count); - } - } - - #[test] - fn prop_clean_expired_no_panic() { - let mut ct = ConntrackTable::new(); - let now = Instant::from_secs(0); - ct.clean_expired(now); - assert_eq!(ct.len(), 0, "empty table cleanup should not panic"); - } - } -} diff --git a/netstack/src/filter/table.rs b/netstack/src/filter/table.rs index 958bedd1a7..ebdab4b930 100644 --- a/netstack/src/filter/table.rs +++ b/netstack/src/filter/table.rs @@ -527,195 +527,4 @@ mod tests { assert_eq!(t.rules.len(), 1, "reset_counters must preserve rules"); } -} - -#[cfg(test)] -mod proptest_tests { - extern crate proptest; - use super::*; - use proptest::prelude::*; - use alloc::rc::Rc; - use smoltcp::time::Instant; - - fn ipv4_addr_strat() -> impl Strategy { - (any::(), any::(), any::(), any::()) - .prop_map(|(a, b, c, d)| IpAddress::v4(a, b, c, d)) - } - - fn hook_strat() -> impl Strategy { - prop_oneof![ - Just(Hook::PreRouting), - Just(Hook::InputLocal), - Just(Hook::Forward), - Just(Hook::OutputLocal), - Just(Hook::PostRouting), - ] - } - - fn verdict_strat() -> impl Strategy { - prop_oneof![ - Just(Verdict::Accept), - Just(Verdict::Drop), - Just(Verdict::Log), - Just(Verdict::Reject), - ] - } - - fn filter_rule_strat() -> impl Strategy { - ( - any::(), - hook_strat(), - proptest::option::of(ipv4_addr_strat()), - any::(), - proptest::option::of(ipv4_addr_strat()), - any::(), - proptest::option::of(any::().prop_map(Protocol)), - proptest::option::of(any::()), - proptest::option::of(any::()), - verdict_strat(), - ) - .prop_map( - |(id, hook, src_addr, src_prefix_len, dst_addr, dst_prefix_len, - protocol, src_port, dst_port, verdict)| { - FilterRule { - id, - hook, - src_addr, - src_prefix_len: src_prefix_len.min(32), - dst_addr, - dst_prefix_len: dst_prefix_len.min(32), - protocol, - src_port, - dst_port, - in_dev: None, - out_dev: None, - state_match: None, - verdict, - match_count: 0, - } - }, - ) - } - - fn packet_ctx_strat() -> impl Strategy> { - (hook_strat(), ipv4_addr_strat(), ipv4_addr_strat(), any::(), - proptest::option::of(any::()), - proptest::option::of(any::())) - .prop_map(|(hook, src_addr, dst_addr, protocol, src_port, dst_port)| { - PacketContext { - hook, - in_dev: None, - out_dev: None, - src_addr, - dst_addr, - protocol, - src_port, - dst_port, - packet: &[], - } - }) - } - - proptest! { - #[test] - fn prop_filter_table_evaluate_no_panic( - rules in proptest::collection::vec(filter_rule_strat(), 0..20), - ctx in packet_ctx_strat(), - ) { - let mut table = FilterTable::new(); - let now = Instant::from_millis(0); - - for mut rule in rules { - table.add(rule); - } - - let verdict = table.evaluate(&ctx, now); - let _ = verdict.as_u32(); - let _ = verdict.name(); - assert!(Verdict::from_u32(verdict.as_u32()).is_some(), - "evaluate must return a valid verdict"); - - let _summary = table.chain_summary(); - let _format = table.format(); - } - - #[test] - fn prop_filter_table_add_remove_roundtrip( - rules in proptest::collection::vec(filter_rule_strat(), 0..10), - ) { - let mut table = FilterTable::new(); - let mut ids = Vec::new(); - - for mut rule in rules { - let id = table.add(rule); - ids.push(id); - } - - for &id in &ids { - assert!(table.remove(id), - "rule {} must be removable just after being added", id); - } - for &id in &ids { - assert!(!table.remove(id), - "removing already-removed rule {} must return false", id); - } - } - - #[test] - fn prop_parse_rule_no_panic(input in "\PC*") { - let result = parse_rule(&input); - match result { - Ok(rule) => { - let _ = rule.id; - let _ = rule.verdict.name(); - assert!(Hook::from_u32(rule.hook.as_u32()).is_some(), - "parsed hook must be valid"); - } - Err(e) => { - let _ = e.to_string(); - } - } - } - - #[test] - fn prop_reset_counters_preserves_rules( - rules in proptest::collection::vec(filter_rule_strat(), 1..10), - ) { - let mut table = FilterTable::new(); - let count = rules.len(); - for mut rule in rules { - table.add(rule); - } - - table.set_default_policy(Hook::InputLocal, Verdict::Drop); - let now = Instant::from_millis(0); - let ctx = PacketContext { - hook: Hook::InputLocal, - in_dev: None, out_dev: None, - src_addr: IpAddress::v4(10, 0, 0, 1), - dst_addr: IpAddress::v4(10, 0, 0, 2), - protocol: 6, - src_port: Some(1234), dst_port: Some(80), - packet: &[], - }; - let _ = table.evaluate(&ctx, now); - table.reset_counters(); - - assert_eq!(table.rules.len(), count, - "reset_counters must preserve all rules"); - assert_eq!( - table.default_policy.get(&Hook::InputLocal).copied(), - Some(Verdict::Drop), - "reset_counters must preserve default policy" - ); - for (_hook, (pkts, bytes)) in &table.chain_counters { - assert_eq!(*pkts, 0, "pkts must be zero after reset"); - assert_eq!(*bytes, 0, "bytes must be zero after reset"); - } - for rule in &table.rules { - assert_eq!(rule.match_count, 0, - "rule {} match_count must be zero after reset", rule.id); - } - } - } -} +} \ No newline at end of file