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:
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user