From bd729751d78ad033fa3506ee627393a89ae1360f Mon Sep 17 00:00:00 2001 From: Red Bear OS Date: Thu, 9 Jul 2026 01:10:55 +0300 Subject: [PATCH] conntrack: complete advance_entry_state with TCP flags helpers Add the missing tcp_flags(), is_fin(), is_rst() helpers and upgrade advance_entry_state from a 3-parameter stub to a 4-parameter function that takes &PacketContext. This completes the WIP conntrack state machine with proper TCP tracking (SynSent, SynRecv, Established, FinWait, TimeWait, Close) and RST/session-timeout handling. Reference: Linux 7.x net/netfilter/nf_conntrack_proto_tcp.c (tcp_packet, nf_conntrack_tcp_packet state machine). --- netstack/src/filter/conntrack.rs | 94 +++++++++++++++++++++++++++----- 1 file changed, 81 insertions(+), 13 deletions(-) diff --git a/netstack/src/filter/conntrack.rs b/netstack/src/filter/conntrack.rs index 31277d1fb4..dfa88502c3 100644 --- a/netstack/src/filter/conntrack.rs +++ b/netstack/src/filter/conntrack.rs @@ -31,6 +31,22 @@ fn is_syn(ctx: &PacketContext, l3num: u8) -> bool { (flags & 0x02) != 0 && (flags & 0x10) == 0 } +fn tcp_flags(ctx: &PacketContext, l3num: u8) -> u8 { + let tcp_offset = if l3num == 4 { 20 } else { 40 }; + if ctx.packet.len() <= tcp_offset + 13 { + return 0; + } + ctx.packet[tcp_offset + 13] +} + +fn is_fin(flags: u8) -> bool { + (flags & 0x01) != 0 +} + +fn is_rst(flags: u8) -> bool { + (flags & 0x04) != 0 +} + /// ICMP echo request detection (Type 8 for ICMPv4, Type 128 for ICMPv6). /// Returns true if the packet is an ICMP echo request (Type 8 for ICMPv4, /// Type 128 for ICMPv6). The packet in `ctx.packet` is the IP packet, so the @@ -150,22 +166,74 @@ pub struct ConntrackTable { over_limit_count: u64, } -fn advance_entry_state(entry: &mut ConnEntry, is_orig: bool, now: Instant) { - if entry.key.l4proto != 6 || is_orig { - return; +fn advance_entry_state(entry: &mut ConnEntry, is_orig: bool, ctx: &PacketContext, now: Instant) -> bool { + if entry.key.l4proto != 6 { + return false; + } + let flags = tcp_flags(ctx, entry.key.l3num); + let res_rst = is_rst(flags); + + // RST closes the connection immediately. + if res_rst { + entry.tcp_state = TcpTracking::Close; + entry.state = ConnState::New; + entry.timeout = now + Duration::from_secs(10); + return true; } - match entry.tcp_state { - TcpTracking::SynSent => { - entry.tcp_state = TcpTracking::SynRecv; + let res_fin = is_fin(flags); + + if is_orig { + // Original direction: track FIN as connection teardown. + match entry.tcp_state { + TcpTracking::Established if res_fin => { + entry.tcp_state = TcpTracking::FinWait; + entry.timeout = now + Duration::from_secs(120); + return true; + } + TcpTracking::FinWait if res_fin => { + entry.tcp_state = TcpTracking::TimeWait; + entry.timeout = now + Duration::from_secs(120); + return true; + } + _ => {} } - TcpTracking::SynRecv => { - entry.tcp_state = TcpTracking::Established; - entry.state = ConnState::Established; - entry.timeout = now + Duration::from_secs(432000); + } else { + // Reply direction: handshake establishment and close. + match entry.tcp_state { + TcpTracking::None if (flags & 0x12) == 0x12 => { + entry.tcp_state = TcpTracking::SynRecv; + entry.timeout = now + Duration::from_secs(60); + return true; + } + TcpTracking::SynSent => { + entry.tcp_state = TcpTracking::SynRecv; + entry.timeout = now + Duration::from_secs(60); + return true; + } + TcpTracking::SynRecv => { + entry.tcp_state = TcpTracking::Established; + entry.state = ConnState::Established; + entry.timeout = now + Duration::from_secs(432000); + return true; + } + TcpTracking::Established if res_fin => { + entry.tcp_state = TcpTracking::FinWait; + entry.timeout = now + Duration::from_secs(120); + return true; + } + TcpTracking::FinWait if res_fin => { + entry.tcp_state = TcpTracking::TimeWait; + entry.timeout = now + Duration::from_secs(120); + return true; + } + TcpTracking::TimeWait => { + entry.timeout = now + Duration::from_secs(120); + } + _ => {} } - _ => {} } + false } impl ConntrackTable { @@ -211,7 +279,7 @@ impl ConntrackTable { let (is_orig, entry_key) = if let Some(entry) = self.entries.get_mut(&reply_key) { entry.reply_packets = entry.reply_packets.saturating_add(1); entry.reply_bytes = entry.reply_bytes.saturating_add(ctx.packet.len() as u64); - advance_entry_state(entry, false, now); + advance_entry_state(entry, false, ctx, now); return entry.state; } else { (true, key.clone()) @@ -220,7 +288,7 @@ impl ConntrackTable { if let Some(entry) = self.entries.get_mut(&entry_key) { entry.orig_packets = entry.orig_packets.saturating_add(1); entry.orig_bytes = entry.orig_bytes.saturating_add(ctx.packet.len() as u64); - advance_entry_state(entry, true, now); + advance_entry_state(entry, true, ctx, now); return entry.state; }