From 0f5a4f59f42955b3146dc3f35d4d4a0b32b12134 Mon Sep 17 00:00:00 2001 From: Red Bear OS Date: Thu, 9 Jul 2026 00:52:14 +0300 Subject: [PATCH] review: parse_port rejects ranges, document IPv6 ext header limit parse_port now rejects port ranges ('1024:65535') with a clear error message instead of silently using only the first number. FilterRule uses a single u16 field and cannot represent ranges. infer_context now documents the hardcoded 40-byte IPv6 header limitation: smoltcp's next_header() chases extension headers to return the final protocol, but the transport offset is not computed. Port extraction silently returns None/None for packets with extension headers, which is safe but means port-based filter rules won't match for such packets. --- netstack/src/filter/table.rs | 11 +++++++++-- netstack/src/router/mod.rs | 8 ++++++++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/netstack/src/filter/table.rs b/netstack/src/filter/table.rs index 7ead7c842e..fca87981f6 100644 --- a/netstack/src/filter/table.rs +++ b/netstack/src/filter/table.rs @@ -410,8 +410,15 @@ fn parse_protocol(value: &str) -> core::result::Result { } fn parse_port(value: &str) -> core::result::Result { - let port_str = value.split(':').next().unwrap_or(value); - port_str + // Accept single port (e.g. "80") but reject port ranges (e.g. + // "1024:65535"). The FilterRule struct uses a single u16 field + // and does not support ranges. Silently accepting the first + // number would make --sport 1024:65535 match only port 1024, + // which is both wrong and misleading. + if value.contains(':') { + return Err(ParseError::Msg("port ranges not supported (use a single port number)")); + } + value .parse::() .map_err(|_| ParseError::BadPort(value.to_string())) } diff --git a/netstack/src/router/mod.rs b/netstack/src/router/mod.rs index 0936636d6e..884b7e6091 100644 --- a/netstack/src/router/mod.rs +++ b/netstack/src/router/mod.rs @@ -515,6 +515,14 @@ fn infer_context( if let Ok(ipv6) = Ipv6Packet::new_checked(packet) { let nh = ipv6.next_header(); let payload_start = 40; // fixed IPv6 header + // Extension headers (Hop-by-Hop, Routing, Fragment, etc.) + // would shift the transport header to a higher offset. + // smoltcp's next_header() chases extension headers to return + // the final protocol, but we don't compute the actual + // transport offset. For packets with extension headers the + // port extraction below will read the wrong bytes and return + // None/None, which is safe: the filter matches on IP+protocol + // but silently skips port matching. let payload = if packet.len() > payload_start { &packet[payload_start..] } else {