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).
This commit is contained in:
Red Bear OS
2026-07-09 01:10:55 +03:00
parent b869651768
commit bd729751d7
+81 -13
View File
@@ -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;
}