conntrack: fix SynRecv→Established transition direction

The TCP three-way handshake completion (initiator's ACK after
SYN-ACK) arrives on the ORIGINAL direction, not the reply
direction. Previously the SynRecv→Established transition was
gated on is_orig==false, which meant it would never fire after
the initial SYN-ACK reply. Connections would stay in SynRecv
state forever with ConnState::New.

Fix: move SynRecv→Established from reply to orig direction, per
Linux nf_conntrack_proto_tcp.c (TCP_CONNTRACK_SYN_RECV fires on
IP_CT_DIR_ORIGINAL packets).

Reply direction now only handles:
- SYN-ACK from responder (SynSent→SynRecv)
- FIN from responder (Established→FinWait)
- Second FIN from responder (FinWait→TimeWait)
- TimeWait timeout extension

Orig direction now handles:
- ACK from initiator (SynRecv→Established)
- FIN from initiator (Established→FinWait)
- Second FIN from initiator (FinWait→TimeWait)

RST still closes from either direction.

All 31 tests pass.
This commit is contained in:
Red Bear OS
2026-07-09 01:28:08 +03:00
parent b0e3f8e9ad
commit 102d00b516
+8 -8
View File
@@ -184,8 +184,14 @@ fn advance_entry_state(entry: &mut ConnEntry, is_orig: bool, ctx: &PacketContext
let res_fin = is_fin(flags);
if is_orig {
// Original direction: track FIN as connection teardown.
// Original direction: handshake completion and FIN teardown.
match entry.tcp_state {
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);
@@ -199,7 +205,7 @@ fn advance_entry_state(entry: &mut ConnEntry, is_orig: bool, ctx: &PacketContext
_ => {}
}
} else {
// Reply direction: handshake establishment and close.
// Reply direction: handshake initiation and close.
match entry.tcp_state {
TcpTracking::None if (flags & 0x12) == 0x12 => {
entry.tcp_state = TcpTracking::SynRecv;
@@ -211,12 +217,6 @@ fn advance_entry_state(entry: &mut ConnEntry, is_orig: bool, ctx: &PacketContext
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);