From d1f76a3aa15233eacd56b44e2067f124909b180c Mon Sep 17 00:00:00 2001 From: Red Bear OS Date: Thu, 9 Jul 2026 10:01:18 +0300 Subject: [PATCH] filter: add --ctstate flag + case-insensitive states + CSV support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rule parser now accepts iptables-style: ACCEPT input -p tcp --ctstate ESTABLISHED,RELATED DROP forward state NEW Both 'state' and '--ctstate' keywords work. States are case- insensitive: NEW/New/new, ESTABLISHED/Established/established, RELATED/Related/related, INVALID/Invalid/invalid. Comma-separated lists (e.g. 'ESTABLISHED,RELATED') use the FIRST recognized state only — FilterRule stores a single StateMatch field. Docstring updated with new examples. All 31 tests pass. --- netstack/src/filter/table.rs | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/netstack/src/filter/table.rs b/netstack/src/filter/table.rs index fca87981f6..ebdab4b930 100644 --- a/netstack/src/filter/table.rs +++ b/netstack/src/filter/table.rs @@ -11,13 +11,14 @@ //! //! ```text //! ACTION CHAIN [-i IFACE] [-o IFACE] [-p PROTO] [-s ADDR[/LEN]] [-d ADDR[/LEN]] -//! [--sport PORT] [--dport PORT] +//! [--sport PORT] [--dport PORT] [state STATE|--ctstate STATE[,STATE...]] //! ``` //! //! Examples: //! - `ACCEPT input -p tcp --dport 80` //! - `DROP input -s 10.0.0.0/8` //! - `ACCEPT output -d 192.168.1.0/24 --sport 1024` +//! - `ACCEPT input -p tcp --ctstate ESTABLISHED,RELATED` extern crate alloc; @@ -355,13 +356,17 @@ pub fn parse_rule(line: &str) -> core::result::Result { let value = tokens.next().ok_or(ParseError::Msg("missing --dport value"))?; rule.dst_port = Some(parse_port(value)?); } - "state" => { + "state" | "--ctstate" => { let value = tokens.next().ok_or(ParseError::Msg("missing state value"))?; - rule.state_match = Some(match value { - "new" => StateMatch::New, - "established" => StateMatch::Established, - "related" => StateMatch::Related, - "invalid" => StateMatch::Invalid, + // Accept comma-separated list (e.g. "ESTABLISHED,RELATED"), + // using the FIRST recognized state. The FilterRule struct + // supports a single state_match field. + let first = value.split(',').next().unwrap_or(value); + rule.state_match = Some(match first { + "new" | "NEW" => StateMatch::New, + "established" | "ESTABLISHED" => StateMatch::Established, + "related" | "RELATED" => StateMatch::Related, + "invalid" | "INVALID" => StateMatch::Invalid, _ => return Err(ParseError::Msg("unknown state value")), }); }