抓包分析器 Day 7:TCP 三次握手状态机跟踪与连接生命周期解析

今天是 9 月 7 日,抓包分析器(PacketAnalyzer CLI)正式进入第二周(W2)实战开发的第一天。
在上周的工作中,我们实现了单个报文的无拷贝切片解码。但网络世界中的报文从来不是孤立存在的。在传输控制协议(TCP)中,通信双方必须通过三次握手(Three-way Handshake)建立连接,并在随后的全双工通信中维护严格的状态机(TCP State Machine)。
如果抓包分析器只能看单包,它就无法判断一个连接是否已经成功建立、无法计算握手往返时延(RTT)、更无法识别恶意的半开连接扫描(SYN Flood)。
今天我们的任务是:在 packet-filter 模块中实现一个工业级的 TCP 状态机跟踪引擎(TCP State Tracker),实时跟踪每个四元组会话的连接建立、数据传输与异常重置。
1. TCP 三次握手状态机理论模型
根据 RFC 793 标准,经典的客户端-服务端三次握手时序如下:
[ 客户端 (Client) ] [ 服务端 (Server) ]
│ │
│ ── 1. 发送 SYN (Seq = X, Flags=[SYN]) ───────────────> │ 状态迁移: LISTEN -> SYN_RECEIVED
│ │
│ <── 2. 回复 SYN+ACK (Seq=Y, Ack=X+1, Flags=[SYN|ACK]) ── │ 状态迁移: SYN_SENT -> SYN_ACK_RECEIVED
│ │
│ ── 3. 发送 ACK (Seq = X+1, Ack=Y+1, Flags=[ACK]) ────> │ 状态迁移: SYN_RECEIVED -> ESTABLISHED
▼ ▼
[ 状态: ESTABLISHED ] [ 状态: ESTABLISHED ]
2. 定义强类型 TCP 状态机枚举与四元组流标识
在 crates/packet-core/src/flow_key.rs 中定义连接标识:
// crates/packet-core/src/flow_key.rs
use std::net::Ipv4Addr;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct TcpFourTuple {
pub src_ip: Ipv4Addr,
pub dst_ip: Ipv4Addr,
pub src_port: u16,
pub dst_port: u16,
}
impl TcpFourTuple {
pub fn new(src_ip: Ipv4Addr, dst_ip: Ipv4Addr, src_port: u16, dst_port: u16) -> Self {
Self { src_ip, dst_ip, src_port, dst_port }
}
/// 获取反向流四元组(用于匹配对端回包)
pub fn reverse(&self) -> Self {
Self {
src_ip: self.dst_ip,
dst_ip: self.src_ip,
src_port: self.dst_port,
dst_port: self.src_port,
}
}
}
在 crates/packet-filter/src/tcp_state.rs 中定义状态机:
// crates/packet-filter/src/tcp_state.rs
use std::time::Instant;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TcpConnectionState {
/// 仅收到客户端的 SYN
SynSent,
/// 收到服务端的 SYN+ACK
SynReceived,
/// 握手完成,连接已正式建立
Established,
/// 收到 FIN,进入连接关闭阶段
Closing,
/// 收到 RST,连接被异常重置
Reset,
/// 握手超时或无响应
TimedOut,
}
#[derive(Debug, Clone)]
pub struct TcpSessionEntry {
pub client_isn: u32, // 客户端初始序列号 (Initial Sequence Number)
pub server_isn: Option<u32>,// 服务端初始序列号
pub state: TcpConnectionState,
pub syn_time: Instant,
pub handshake_rtt_ms: Option<f64>, // 握手耗时
pub total_packets: usize,
pub total_bytes: usize,
}
3. 实现状态机迁移跟踪引擎
在 crates/packet-filter/src/session_tracker.rs 中:
// crates/packet-filter/src/session_tracker.rs
use crate::tcp_state::{TcpConnectionState, TcpSessionEntry};
use packet_core::flow_key::TcpFourTuple;
use packet_core::tcp::TcpHeader;
use std::collections::HashMap;
use std::time::Instant;
pub struct TcpSessionTracker {
sessions: HashMap<TcpFourTuple, TcpSessionEntry>,
}
impl TcpSessionTracker {
pub fn new() -> Self {
Self {
sessions: HashMap::new(),
}
}
/// 处理一个新到达的 TCP 报文,推进状态机迁移
pub fn process_packet(
&mut self,
tuple: TcpFourTuple,
tcp: &TcpHeader,
payload_len: usize,
) -> Option<TcpConnectionState> {
let now = Instant::now();
// 场景 1:收到客户端初始 SYN 报文(握手第一步)
if tcp.flags.syn && !tcp.flags.ack {
let entry = TcpSessionEntry {
client_isn: tcp.seq_num,
server_isn: None,
state: TcpConnectionState::SynSent,
syn_time: now,
handshake_rtt_ms: None,
total_packets: 1,
total_bytes: payload_len,
};
self.sessions.insert(tuple, entry);
return Some(TcpConnectionState::SynSent);
}
// 场景 2:检查是否为服务端的 SYN+ACK(握手第二步,查找反向流)
let reverse_tuple = tuple.reverse();
if let Some(session) = self.sessions.get_mut(&reverse_tuple) {
if session.state == TcpConnectionState::SynSent && tcp.flags.syn && tcp.flags.ack {
// 校验确认号是否正确确认了客户端的 SYN (Ack == Client ISN + 1)
if tcp.ack_num == session.client_isn.wrapping_add(1) {
session.server_isn = Some(tcp.seq_num);
session.state = TcpConnectionState::SynReceived;
session.total_packets += 1;
return Some(TcpConnectionState::SynReceived);
}
}
}
// 场景 3:检查是否为客户端最后的 ACK(握手第三步)
if let Some(session) = self.sessions.get_mut(&tuple) {
if session.state == TcpConnectionState::SynReceived && tcp.flags.ack && !tcp.flags.syn {
if let Some(srv_isn) = session.server_isn {
if tcp.ack_num == srv_isn.wrapping_add(1) {
// 握手成功完成!计算握手 RTT
session.state = TcpConnectionState::Established;
session.handshake_rtt_ms = Some(session.syn_time.elapsed().as_secs_f64() * 1000.0);
session.total_packets += 1;
return Some(TcpConnectionState::Established);
}
}
}
// 场景 4:异常 RST 处理
if tcp.flags.rst {
session.state = TcpConnectionState::Reset;
return Some(TcpConnectionState::Reset);
}
session.total_packets += 1;
session.total_bytes += payload_len;
}
None
}
/// 获取当前活跃连接总数
pub fn active_connections_count(&self) -> usize {
self.sessions.values().filter(|s| s.state == TcpConnectionState::Established).count()
}
}
4. 单元测试驱动状态机覆盖
#[cfg(test)]
mod tests {
use super::*;
use packet_core::tcp_flags::TcpFlags;
use std::net::Ipv4Addr;
#[test]
fn test_tcp_three_way_handshake_flow() {
let mut tracker = TcpSessionTracker::new();
let client_ip = Ipv4Addr::new(192, 168, 1, 100);
let server_ip = Ipv4Addr::new(10, 0, 0, 1);
let tuple = TcpFourTuple::new(client_ip, server_ip, 54321, 80);
// 1. Client 发送 SYN (Seq=1000)
let syn_pkt = TcpHeader {
src_port: 54321, dst_port: 80, seq_num: 1000, ack_num: 0,
data_offset_bytes: 20, flags: TcpFlags { syn: true, ..Default::default() },
window_size: 64240, checksum: 0, urgent_ptr: 0,
};
let s1 = tracker.process_packet(tuple, &syn_pkt, 0);
assert_eq!(s1, Some(TcpConnectionState::SynSent));
// 2. Server 回复 SYN+ACK (Seq=8000, Ack=1001)
let syn_ack_pkt = TcpHeader {
src_port: 80, dst_port: 54321, seq_num: 8000, ack_num: 1001,
data_offset_bytes: 20, flags: TcpFlags { syn: true, ack: true, ..Default::default() },
window_size: 64240, checksum: 0, urgent_ptr: 0,
};
let s2 = tracker.process_packet(tuple.reverse(), &syn_ack_pkt, 0);
assert_eq!(s2, Some(TcpConnectionState::SynReceived));
// 3. Client 发送 ACK (Seq=1001, Ack=8001)
let ack_pkt = TcpHeader {
src_port: 54321, dst_port: 80, seq_num: 1001, ack_num: 8001,
data_offset_bytes: 20, flags: TcpFlags { ack: true, ..Default::default() },
window_size: 64240, checksum: 0, urgent_ptr: 0,
};
let s3 = tracker.process_packet(tuple, &ack_pkt, 0);
assert_eq!(s3, Some(TcpConnectionState::Established));
assert_eq!(tracker.active_connections_count(), 1);
}
}
总结与明日规划
今天成功实现了网络排障的基石——TCP 三次握手状态机追踪:
- 严格按照 RFC 793 实现了初始序列号(ISN)递增校验;
- 支持双向流(Bidi-Flow)自动对齐与精确的握手 RTT 计算;
- 为后续识别连接超时、SYN 洪水攻击提供了核心数据源。
明天(Day 8),我们将攻克 TCP 最具挑战性的核心难关——基于滑动窗口与序列号的 TCP 分片乱序重组引擎(TCP Stream Reassembly)!


