use crate::error::NetError;
use crate::packet::{IpVersion, TcpHeader};
use crate::source_admission::IpAddr;
use crate::transport::acceptor::BindTable;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum ConnectionState {
Closed,
Listen,
SynSent,
SynReceived,
Established,
FinWait1,
FinWait2,
CloseWait,
Closing,
TimeWait,
LastAck,
}
impl ConnectionState {
#[inline]
pub fn is_active(&self) -> bool {
!matches!(self, ConnectionState::Closed)
}
#[inline]
pub fn can_receive_data(&self) -> bool {
matches!(self, ConnectionState::Established | ConnectionState::CloseWait)
}
#[inline]
pub fn can_send_data(&self) -> bool {
matches!(self, ConnectionState::Established)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct ConnectionKey {
pub src_ip: IpAddr,
pub src_port: u16,
pub dst_ip: IpAddr,
pub dst_port: u16,
pub ip_version: IpVersion,
}
impl ConnectionKey {
#[inline]
pub fn new(
src_ip: IpAddr,
src_port: u16,
dst_ip: IpAddr,
dst_port: u16,
ip_version: IpVersion,
) -> Self {
Self {
src_ip,
src_port,
dst_ip,
dst_port,
ip_version,
}
}
#[inline]
pub fn hash(&self) -> u64 {
super::hash_flow_tuple(
&self.src_ip,
self.src_port,
&self.dst_ip,
self.dst_port,
self.ip_version,
)
}
#[inline]
pub fn reversed(&self) -> Self {
Self {
src_ip: self.dst_ip,
src_port: self.dst_port,
dst_ip: self.src_ip,
dst_port: self.src_port,
ip_version: self.ip_version,
}
}
}
#[derive(Debug, Clone)]
pub struct TcpConnection {
pub key: ConnectionKey,
pub state: ConnectionState,
pub snd_nxt: u32,
pub snd_una: u32,
pub rcv_nxt: u32,
pub snd_wnd: u16,
pub rcv_wnd: u16,
pub cwnd: u32,
pub iss: u32,
pub irs: u32,
pub last_active: u64,
pub retransmit_count: u8,
pub in_fast_recovery: bool,
pub created_at: u64,
pub timer_handles: [Option<u32>; crate::transport::timer::TIMER_TYPE_COUNT],
pub srtt_us: u64,
pub rttvar_us: u64,
pub last_syn_ts: u64,
pub syn_ack_ts: u64,
pub has_rtt_sample: bool,
}
impl TcpConnection {
#[inline]
pub fn empty() -> Self {
Self {
key: ConnectionKey::new(IpAddr::V4([0; 4]), 0, IpAddr::V4([0; 4]), 0, IpVersion::V4),
state: ConnectionState::Closed,
snd_nxt: 0,
snd_una: 0,
rcv_nxt: 0,
snd_wnd: 0,
rcv_wnd: 0,
cwnd: 0,
iss: 0,
irs: 0,
last_active: 0,
retransmit_count: 0,
in_fast_recovery: false,
created_at: 0,
timer_handles: [None; crate::transport::timer::TIMER_TYPE_COUNT],
srtt_us: 0,
rttvar_us: 0,
last_syn_ts: 0,
syn_ack_ts: 0,
has_rtt_sample: false,
}
}
#[inline]
pub fn is_free(&self) -> bool {
self.state == ConnectionState::Closed
}
#[inline]
pub fn is_time_wait(&self) -> bool {
self.state == ConnectionState::TimeWait
}
#[inline]
pub fn init(&mut self, key: ConnectionKey, iss: u32, irs: u32, now: u64) {
self.key = key;
self.state = ConnectionState::Listen;
self.iss = iss;
self.irs = irs;
self.snd_nxt = iss;
self.snd_una = iss;
self.rcv_nxt = irs;
self.snd_wnd = 0xFFFF;
self.rcv_wnd = 0xFFFF;
self.cwnd = 64;
self.last_active = now;
self.created_at = now;
self.retransmit_count = 0;
self.in_fast_recovery = false;
self.srtt_us = 0;
self.rttvar_us = 0;
self.last_syn_ts = 0;
self.syn_ack_ts = 0;
self.has_rtt_sample = false;
}
#[inline]
pub fn is_seq_in_rcv_window(&self, seq: u32) -> bool {
let diff = seq.wrapping_sub(self.rcv_nxt);
diff < self.rcv_wnd as u32
}
#[inline]
pub fn is_seq_in_snd_window(&self, seq: u32) -> bool {
let diff = seq.wrapping_sub(self.snd_nxt);
diff < self.snd_wnd as u32
}
#[inline]
pub fn seq_lt(a: u32, b: u32) -> bool {
(b.wrapping_sub(a) as i32) > 0
}
#[inline]
pub fn seq_le(a: u32, b: u32) -> bool {
(b.wrapping_sub(a) as i32) >= 0
}
#[inline]
pub fn seq_gt(a: u32, b: u32) -> bool {
(a.wrapping_sub(b) as i32) > 0
}
#[inline]
pub fn seq_ge(a: u32, b: u32) -> bool {
(a.wrapping_sub(b) as i32) >= 0
}
pub fn update_rtt(&mut self, r_us: u64) {
if !self.has_rtt_sample {
self.srtt_us = r_us;
self.rttvar_us = r_us / 2;
self.has_rtt_sample = true;
} else {
let diff = if self.srtt_us > r_us {
self.srtt_us - r_us
} else {
r_us - self.srtt_us
};
self.rttvar_us = (self.rttvar_us * 3 + diff) / 4;
self.srtt_us = (self.srtt_us * 7 + r_us) / 8;
}
}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct TcpStats {
pub total_connections: u64,
pub active_connections: u64,
pub syn_received: u64,
pub syn_ack_sent: u64,
pub ack_received: u64,
pub rst_received: u64,
pub fin_received: u64,
pub retransmits: u64,
pub timeouts: u64,
pub connection_timeouts: u64,
pub rejected: u64,
pub orphan_fin_dropped: u64,
}
pub const MAX_CONNECTIONS: usize = 65536;
pub const HASH_BUCKETS: usize = 16384;
#[derive(Debug)]
pub struct ConnectionTable {
slots: Vec<TcpConnection>,
buckets: Vec<u32>,
free_stack: Vec<u32>,
count: usize,
capacity: usize,
}
impl ConnectionTable {
pub fn new(capacity: usize) -> Self {
let cap = capacity.clamp(1, MAX_CONNECTIONS);
let mut slots = Vec::with_capacity(cap);
for _ in 0..cap {
slots.push(TcpConnection::empty());
}
let buckets = vec![0u32; HASH_BUCKETS];
let mut free_stack = Vec::with_capacity(cap);
for i in (0..cap).rev() {
free_stack.push(i as u32);
}
Self {
slots,
buckets,
free_stack,
count: 0,
capacity: cap,
}
}
#[inline]
pub fn count(&self) -> usize {
self.count
}
#[inline]
pub fn capacity(&self) -> usize {
self.capacity
}
#[inline]
pub fn find(&self, key: &ConnectionKey) -> Option<usize> {
let bucket_idx = (key.hash() as usize) & (HASH_BUCKETS - 1);
let mut i = bucket_idx;
let mut probe = 0;
loop {
let raw = self.buckets[i];
if raw == 0 {
return None;
}
if raw == 0xFFFF_FFFFu32 {
probe += 1;
if probe >= HASH_BUCKETS {
return None;
}
i = (i + 1) & (HASH_BUCKETS - 1);
continue;
}
let real_idx = (raw as usize) - 1;
let slot = &self.slots[real_idx];
if slot.state != ConnectionState::Closed && slot.key == *key {
return Some(real_idx);
}
probe += 1;
if probe >= HASH_BUCKETS {
return None;
}
i = (i + 1) & (HASH_BUCKETS - 1);
}
}
#[inline]
pub fn get_mut(&mut self, idx: usize) -> Option<&mut TcpConnection> {
if idx < self.slots.len() {
Some(&mut self.slots[idx])
} else {
None
}
}
#[inline]
pub fn get(&self, idx: usize) -> Option<&TcpConnection> {
if idx < self.slots.len() {
Some(&self.slots[idx])
} else {
None
}
}
pub fn insert(
&mut self,
key: ConnectionKey,
iss: u32,
irs: u32,
now: u64,
) -> Result<usize, NetError> {
if self.count >= self.capacity {
return Err(NetError::ConnectionTableFull {
capacity: self.capacity,
});
}
if self.find(&key).is_some() {
return Err(NetError::ConnectionExists {
key: format!("{:?}", key),
});
}
let slot_idx = self.find_free_slot().ok_or(NetError::ConnectionTableFull {
capacity: self.capacity,
})?;
let hash = key.hash();
let bucket_idx = (hash as usize) & (HASH_BUCKETS - 1);
let mut i = bucket_idx;
let mut probe = 0;
loop {
let existing_slot_idx = self.buckets[i];
if existing_slot_idx == 0 || existing_slot_idx == 0xFFFF_FFFFu32 {
self.buckets[i] = (slot_idx as u32) + 1;
break;
}
let existing_slot = &self.slots[(existing_slot_idx as usize) - 1];
if existing_slot.state == ConnectionState::Closed {
self.buckets[i] = (slot_idx as u32) + 1;
break;
}
probe += 1;
if probe >= HASH_BUCKETS {
return Err(NetError::HashTableFull);
}
i = (i + 1) & (HASH_BUCKETS - 1);
}
self.slots[slot_idx].init(key, iss, irs, now);
self.count += 1;
Ok(slot_idx)
}
#[inline]
fn find_free_slot(&mut self) -> Option<usize> {
if let Some(idx) = self.free_stack.pop() {
return Some(idx as usize);
}
(0..self.capacity).find(|&i| self.slots[i].is_free())
}
pub fn remove(&mut self, key: &ConnectionKey) -> bool {
if let Some(idx) = self.find(key) {
self.slots[idx].state = ConnectionState::Closed;
self.free_stack.push(idx as u32);
let bucket_idx = (key.hash() as usize) & (HASH_BUCKETS - 1);
let mut i = bucket_idx;
let mut probe = 0;
loop {
let slot_idx = self.buckets[i];
if slot_idx == 0 {
break;
}
if slot_idx as usize == idx + 1 {
self.buckets[i] = 0xFFFF_FFFFu32; break;
}
probe += 1;
if probe >= HASH_BUCKETS {
break;
}
i = (i + 1) & (HASH_BUCKETS - 1);
}
self.count -= 1;
true
} else {
false
}
}
pub fn close_by_index(&mut self, idx: usize) -> bool {
if idx >= self.slots.len() || !self.slots[idx].state.is_active() {
return false;
}
let key = self.slots[idx].key;
self.slots[idx].state = ConnectionState::Closed;
self.free_stack.push(idx as u32);
self.count = self.count.saturating_sub(1);
let bucket_idx = (key.hash() as usize) & (HASH_BUCKETS - 1);
let mut i = bucket_idx;
let mut probe = 0;
loop {
let slot_idx = self.buckets[i];
if slot_idx == 0 {
break;
}
if slot_idx as usize == idx + 1 {
self.buckets[i] = 0xFFFF_FFFFu32;
break;
}
probe += 1;
if probe >= HASH_BUCKETS {
break;
}
i = (i + 1) & (HASH_BUCKETS - 1);
}
true
}
pub fn scan_timeouts(&mut self, now: u64, timeout_ms: u64) -> Vec<ConnectionKey> {
let mut timed_out = Vec::new();
for (i, slot) in self.slots.iter_mut().enumerate() {
if slot.state.is_active() && now.saturating_sub(slot.last_active) > timeout_ms {
timed_out.push(slot.key);
slot.state = ConnectionState::Closed;
self.free_stack.push(i as u32);
}
}
self.rebuild_hash();
self.count = self.count.saturating_sub(timed_out.len());
timed_out
}
fn rebuild_hash(&mut self) {
self.buckets.fill(0);
for i in 0..self.slots.len() {
if self.slots[i].state.is_active() {
let key = self.slots[i].key;
let bucket_idx = (key.hash() as usize) & (HASH_BUCKETS - 1);
let mut idx = bucket_idx;
let mut probe = 0;
loop {
if self.buckets[idx] == 0 {
self.buckets[idx] = (i as u32) + 1;
break;
}
probe += 1;
if probe >= HASH_BUCKETS {
break;
}
idx = (idx + 1) & (HASH_BUCKETS - 1);
}
}
}
}
pub fn iter_active(&self) -> impl Iterator<Item = &TcpConnection> {
self.slots.iter().filter(|s| s.state.is_active())
}
pub fn iter_active_mut(&mut self) -> impl Iterator<Item = &mut TcpConnection> {
self.slots.iter_mut().filter(|s| s.state.is_active())
}
pub fn slots(&self) -> &[TcpConnection] {
&self.slots
}
}
#[derive(Debug)]
pub struct TcpStateMachine {
table: ConnectionTable,
stats: TcpStats,
timer_wheel: crate::transport::timer::TimerWheel,
bind_table: Option<BindTable>,
}
impl TcpStateMachine {
pub fn new(max_connections: usize) -> Self {
Self {
table: ConnectionTable::new(max_connections),
stats: TcpStats::default(),
timer_wheel: crate::transport::timer::TimerWheel::new(1),
bind_table: None,
}
}
#[inline]
pub fn attach_bind_table(&mut self, table: BindTable) {
self.bind_table = Some(table);
}
#[inline]
pub fn bind_table(&self) -> Option<&BindTable> {
self.bind_table.as_ref()
}
#[inline]
pub fn bind_table_mut(&mut self) -> Option<&mut BindTable> {
self.bind_table.as_mut()
}
#[inline]
pub fn timer_wheel(&self) -> &crate::transport::timer::TimerWheel {
&self.timer_wheel
}
#[inline]
pub fn timer_wheel_mut(&mut self) -> &mut crate::transport::timer::TimerWheel {
&mut self.timer_wheel
}
#[inline]
pub fn table(&self) -> &ConnectionTable {
&self.table
}
#[inline]
pub fn table_mut(&mut self) -> &mut ConnectionTable {
&mut self.table
}
#[inline]
pub fn stats(&self) -> TcpStats {
self.stats
}
pub fn run_tick(&mut self, elapsed_ms: u64) -> Vec<crate::transport::timer::TimerAction> {
use crate::transport::timer::TimerType;
let expired = self.timer_wheel.advance(elapsed_ms);
for action in &expired {
match action.timer_type {
TimerType::TcpConnectionTimeout => {
if self.table.close_by_index(action.target_idx) {
self.stats.connection_timeouts += 1;
self.stats.active_connections =
self.stats.active_connections.saturating_sub(1);
}
}
TimerType::TcpRetransmit => {
if let Some(conn) = self.table.get_mut(action.target_idx) {
conn.retransmit_count = conn.retransmit_count.saturating_add(1);
conn.cwnd = conn.cwnd.saturating_mul(2).max(1);
self.stats.retransmits += 1;
}
}
TimerType::TcpTimeWait => {
if self.table.close_by_index(action.target_idx) {
self.stats.active_connections =
self.stats.active_connections.saturating_sub(1);
}
}
TimerType::TcpFinWait2 => {
if self.table.close_by_index(action.target_idx) {
self.stats.active_connections =
self.stats.active_connections.saturating_sub(1);
}
}
TimerType::UdpSessionTimeout => {
}
TimerType::QuicPto | TimerType::QuicCloseTimeout | TimerType::QuicHandshakeTimeout => {
}
}
}
expired.to_vec()
}
pub fn add_timer(
&mut self,
duration_ms: u64,
timer_type: crate::transport::timer::TimerType,
connection_idx: usize,
) -> Option<u32> {
let handle = self.timer_wheel.add_timer(duration_ms, timer_type, connection_idx)?;
if let Some(conn) = self.table.get_mut(connection_idx) {
conn.timer_handles[timer_type.as_index()] = Some(handle);
}
Some(handle)
}
pub fn remove_timers_for(&mut self, connection_idx: usize) {
if let Some(conn) = self.table.get_mut(connection_idx) {
for slot in conn.timer_handles.iter_mut() {
if let Some(h) = slot.take() {
let _ = self.timer_wheel.remove_by_handle(h);
}
}
}
}
#[inline]
fn generate_isn(&self, key: &ConnectionKey) -> u32 {
(zenith_foundation::random::random_u64() as u32) ^ (key.hash() as u32)
}
pub fn handle_packet(
&mut self,
key: &ConnectionKey,
tcp: &TcpHeader,
payload_len: usize,
now: u64,
) -> TcpAction {
if let Some(idx) = self.table.find(key) {
return self.handle_existing_connection(idx, key, tcp, payload_len, now);
}
let reversed = key.reversed();
if let Some(idx) = self.table.find(&reversed) {
return self.handle_server_side(idx, key, tcp, payload_len, now);
}
if tcp.syn() && !tcp.ack() {
return self.handle_new_syn(key, tcp, now);
}
if tcp.fin() {
self.stats.orphan_fin_dropped += 1;
self.stats.rejected += 1;
return TcpAction::Drop;
}
self.stats.rejected += 1;
TcpAction::SendRst
}
fn handle_new_syn(
&mut self,
key: &ConnectionKey,
tcp: &TcpHeader,
now: u64,
) -> TcpAction {
if let Some(bind) = &self.bind_table {
let dst = crate::NetAddr::from_ip_addr(key.dst_ip, key.dst_port);
if !bind.is_listening(dst) {
self.stats.rejected += 1;
return TcpAction::Drop;
}
}
if self.table.count() >= self.table.capacity() {
self.stats.rejected += 1;
return TcpAction::Drop;
}
let server_key = key.reversed();
let iss = self.generate_isn(&server_key);
let irs = tcp.seq_num().wrapping_add(1);
match self.table.insert(server_key, iss, irs, now) {
Ok(idx) => {
if let Some(conn) = self.table.get_mut(idx) {
conn.state = ConnectionState::SynReceived;
conn.snd_nxt = iss.wrapping_add(1); conn.last_active = now;
conn.last_syn_ts = now;
}
self.stats.syn_received += 1;
self.stats.active_connections += 1;
self.stats.total_connections += 1;
TcpAction::SendSynAck {
syn_seq: iss,
ack_seq: irs,
window: 0xFFFF,
}
}
Err(_) => {
self.stats.rejected += 1;
TcpAction::Drop
}
}
}
fn handle_server_side(
&mut self,
idx: usize,
_key: &ConnectionKey,
tcp: &TcpHeader,
payload_len: usize,
now: u64,
) -> TcpAction {
let rst_acceptable = match self.table.get(idx) {
Some(c) => c.is_seq_in_rcv_window(tcp.seq_num()),
None => false,
};
if tcp.rst() {
if rst_acceptable {
self.stats.rst_received += 1;
if self.table.close_by_index(idx) {
self.stats.active_connections =
self.stats.active_connections.saturating_sub(1);
}
}
return TcpAction::Drop;
}
let last_ack_closed = match self.table.get(idx) {
Some(c) => {
c.state == ConnectionState::LastAck
&& tcp.ack()
&& TcpConnection::seq_ge(c.snd_una, c.snd_nxt)
}
None => false,
};
if last_ack_closed {
if self.table.close_by_index(idx) {
self.stats.active_connections = self.stats.active_connections.saturating_sub(1);
}
return TcpAction::Drop;
}
let conn = match self.table.get_mut(idx) {
Some(c) => c,
None => return TcpAction::Drop,
};
conn.last_active = now;
if tcp.ack() && conn.state != ConnectionState::SynReceived {
self.stats.ack_received += 1;
let ack_num = tcp.ack_num();
if TcpConnection::seq_gt(ack_num, conn.snd_nxt) {
return TcpAction::Ack {
ack_seq: conn.rcv_nxt,
window: 0xFFFF,
};
}
if TcpConnection::seq_gt(ack_num, conn.snd_una) {
conn.snd_una = ack_num;
conn.snd_nxt = ack_num;
}
conn.snd_wnd = tcp.window_size();
}
if tcp.fin() && conn.is_seq_in_rcv_window(tcp.seq_num()) {
conn.rcv_nxt = conn.rcv_nxt.wrapping_add(1);
conn.state = ConnectionState::CloseWait;
self.stats.fin_received += 1;
}
if payload_len > 0 && conn.state.can_receive_data() {
let next_seq = tcp.seq_num().wrapping_add(payload_len as u32);
if TcpConnection::seq_gt(next_seq, conn.rcv_nxt) {
conn.rcv_nxt = next_seq;
}
}
match conn.state {
ConnectionState::SynReceived => {
if tcp.ack()
&& TcpConnection::seq_gt(tcp.ack_num(), conn.snd_una)
&& TcpConnection::seq_le(tcp.ack_num(), conn.snd_nxt)
{
conn.state = ConnectionState::Established;
conn.snd_una = tcp.ack_num();
let r = now.saturating_sub(conn.last_syn_ts);
conn.update_rtt(r);
self.stats.ack_received += 1;
TcpAction::Established
} else if tcp.ack() {
TcpAction::Ack {
ack_seq: conn.rcv_nxt,
window: 0xFFFF,
}
} else {
TcpAction::Drop
}
}
ConnectionState::Established => TcpAction::Ack {
ack_seq: conn.rcv_nxt,
window: 0xFFFF,
},
ConnectionState::CloseWait => TcpAction::Close,
_ => TcpAction::Drop,
}
}
fn handle_existing_connection(
&mut self,
idx: usize,
_key: &ConnectionKey,
tcp: &TcpHeader,
payload_len: usize,
now: u64,
) -> TcpAction {
let rst_acceptable = match self.table.get(idx) {
Some(c) => c.is_seq_in_rcv_window(tcp.seq_num()),
None => false,
};
if tcp.rst() {
if rst_acceptable {
self.stats.rst_received += 1;
if self.table.close_by_index(idx) {
self.stats.active_connections =
self.stats.active_connections.saturating_sub(1);
}
}
return TcpAction::Drop;
}
let last_ack_closed = match self.table.get(idx) {
Some(c) => {
c.state == ConnectionState::LastAck
&& tcp.ack()
&& TcpConnection::seq_ge(c.snd_una, c.snd_nxt)
}
None => false,
};
if last_ack_closed {
if self.table.close_by_index(idx) {
self.stats.active_connections = self.stats.active_connections.saturating_sub(1);
}
return TcpAction::Drop;
}
let conn = match self.table.get_mut(idx) {
Some(c) => c,
None => return TcpAction::Drop,
};
conn.last_active = now;
if tcp.fin() && conn.is_seq_in_rcv_window(tcp.seq_num()) {
conn.rcv_nxt = conn.rcv_nxt.wrapping_add(1);
conn.state = match conn.state {
ConnectionState::FinWait1 => ConnectionState::Closing,
ConnectionState::FinWait2 => ConnectionState::TimeWait,
_ => ConnectionState::CloseWait,
};
self.stats.fin_received += 1;
}
if tcp.ack()
&& !matches!(
conn.state,
ConnectionState::SynReceived | ConnectionState::SynSent
)
{
self.stats.ack_received += 1;
let ack_num = tcp.ack_num();
if TcpConnection::seq_gt(ack_num, conn.snd_nxt) {
return TcpAction::Ack {
ack_seq: conn.rcv_nxt,
window: 0xFFFF,
};
}
if TcpConnection::seq_gt(ack_num, conn.snd_una) {
conn.snd_una = ack_num;
conn.snd_nxt = ack_num;
}
conn.snd_wnd = tcp.window_size();
}
if payload_len > 0 && conn.state.can_receive_data() {
let next_seq = tcp.seq_num().wrapping_add(payload_len as u32);
if TcpConnection::seq_gt(next_seq, conn.rcv_nxt) {
conn.rcv_nxt = next_seq;
}
}
match conn.state {
ConnectionState::SynSent => {
if tcp.syn()
&& tcp.ack()
&& tcp.ack_num() == conn.snd_nxt.wrapping_add(1)
{
conn.state = ConnectionState::Established;
conn.rcv_nxt = tcp.seq_num().wrapping_add(1);
conn.snd_nxt = tcp.ack_num();
let r = now.saturating_sub(conn.last_syn_ts);
conn.update_rtt(r);
self.stats.ack_received += 1;
TcpAction::Established
} else {
TcpAction::Drop
}
}
ConnectionState::SynReceived => {
if tcp.ack() {
let ack_num = tcp.ack_num();
if TcpConnection::seq_gt(ack_num, conn.snd_una)
&& TcpConnection::seq_le(ack_num, conn.snd_nxt)
{
conn.state = ConnectionState::Established;
let r = now.saturating_sub(conn.last_syn_ts);
conn.update_rtt(r);
self.stats.ack_received += 1;
conn.snd_una = ack_num;
TcpAction::Established
} else {
TcpAction::Drop
}
} else {
TcpAction::Drop
}
}
ConnectionState::Established => TcpAction::Ack {
ack_seq: conn.rcv_nxt,
window: 0xFFFF,
},
ConnectionState::FinWait1 | ConnectionState::FinWait2 => {
if conn.state == ConnectionState::FinWait1
&& tcp.ack()
&& TcpConnection::seq_ge(conn.snd_una, conn.snd_nxt.wrapping_sub(1))
{
conn.state = ConnectionState::FinWait2;
}
TcpAction::Ack {
ack_seq: conn.rcv_nxt,
window: 0xFFFF,
}
}
ConnectionState::Closing => {
if tcp.ack()
&& TcpConnection::seq_ge(conn.snd_una, conn.snd_nxt.wrapping_sub(1))
{
conn.state = ConnectionState::TimeWait;
}
TcpAction::Ack {
ack_seq: conn.rcv_nxt,
window: 0xFFFF,
}
}
ConnectionState::LastAck => TcpAction::Drop,
_ => TcpAction::Drop,
}
}
pub fn send_syn(
&mut self,
key: ConnectionKey,
now: u64,
) -> Result<usize, NetError> {
let iss = self.generate_isn(&key);
let irs = 0; let idx = self.table.insert(key, iss, irs, now)?;
if let Some(conn) = self.table.get_mut(idx) {
conn.state = ConnectionState::SynSent;
conn.snd_nxt = iss;
conn.last_active = now;
conn.last_syn_ts = now;
}
self.stats.active_connections += 1;
self.stats.total_connections += 1;
Ok(idx)
}
pub fn close_connection(
&mut self,
key: &ConnectionKey,
now: u64,
) -> TcpAction {
if let Some(idx) = self.table.find(key) {
let can_send = self.table.get(idx).map(|c| c.state.can_send_data()).unwrap_or(false);
if can_send {
if let Some(conn) = self.table.get_mut(idx) {
conn.state = ConnectionState::FinWait1;
conn.snd_nxt = conn.snd_nxt.wrapping_add(1);
conn.last_active = now;
}
return TcpAction::SendFin;
} else {
if self.table.close_by_index(idx) {
self.stats.active_connections = self.stats.active_connections.saturating_sub(1);
}
return TcpAction::Drop;
}
}
self.stats.rejected += 1;
TcpAction::Drop
}
pub fn passive_close(
&mut self,
key: &ConnectionKey,
now: u64,
) -> TcpAction {
if let Some(idx) = self.table.find(key)
&& let Some(conn) = self.table.get_mut(idx) {
conn.state = ConnectionState::LastAck;
conn.snd_nxt = conn.snd_nxt.wrapping_add(1);
conn.last_active = now;
return TcpAction::SendFin;
}
TcpAction::Drop
}
pub fn handle_timeouts(&mut self, now: u64, timeout_ms: u64) {
let expired = self.table.scan_timeouts(now, timeout_ms);
self.stats.timeouts += expired.len() as u64;
self.stats.active_connections = self.stats.active_connections.saturating_sub(expired.len() as u64);
}
#[inline]
pub fn get_connection(&self, key: &ConnectionKey) -> Option<&TcpConnection> {
let idx = self.table.find(key)?;
self.table.get(idx)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TcpAction {
Drop,
SendRst,
SendSynAck {
syn_seq: u32,
ack_seq: u32,
window: u16,
},
Ack {
ack_seq: u32,
window: u16,
},
SendFin,
Established,
Close,
}
#[cfg(test)]
mod tests {
use super::*;
fn make_key() -> ConnectionKey {
ConnectionKey::new(
IpAddr::V4([10, 0, 0, 1]),
8080,
IpAddr::V4([10, 0, 0, 2]),
12345,
IpVersion::V4,
)
}
#[test]
fn test_connection_state() {
assert!(ConnectionState::Established.can_receive_data());
assert!(ConnectionState::Established.can_send_data());
assert!(!ConnectionState::Closed.is_active());
assert!(!ConnectionState::Listen.can_send_data());
}
#[test]
fn test_connection_key_hash() {
let key = make_key();
let hash1 = key.hash();
assert_ne!(hash1, 0);
let reversed = key.reversed();
assert_ne!(key, reversed);
assert_ne!(key.hash(), reversed.hash());
}
#[test]
fn test_connection_table_insert_find() {
let mut table = ConnectionTable::new(1024);
let key = make_key();
let idx = table.insert(key, 100, 200, 1000).unwrap();
assert_eq!(table.count(), 1);
let found = table.find(&key);
assert_eq!(found, Some(idx));
let result = table.insert(key, 101, 201, 1000);
assert!(result.is_err());
}
#[test]
fn test_connection_table_remove() {
let mut table = ConnectionTable::new(1024);
let key = make_key();
table.insert(key, 100, 200, 1000).unwrap();
assert_eq!(table.count(), 1);
let removed = table.remove(&key);
assert!(removed);
assert_eq!(table.count(), 0);
assert!(table.find(&key).is_none());
}
#[test]
fn test_tcp_state_machine_new_syn() {
let mut sm = TcpStateMachine::new(1024);
let key = make_key();
let mut data = [0u8; 20];
data[0] = 0x00; data[1] = 0x50; data[2] = 0x30; data[3] = 0x39; data[4] = 0x00;
data[5] = 0x00;
data[6] = 0x00;
data[7] = 0x64; data[12] = 0x50;
data[13] = 0x02;
let tcp = TcpHeader::parse(&data).unwrap();
let action = sm.handle_packet(&key, tcp, 0, 1000);
assert!(matches!(action, TcpAction::SendSynAck { .. }));
assert_eq!(sm.stats().syn_received, 1);
assert_eq!(sm.stats().active_connections, 1);
}
#[test]
fn test_generate_isn_csprng_unpredictable() {
let sm = TcpStateMachine::new(16);
let key = make_key();
assert_ne!(sm.generate_isn(&key), sm.generate_isn(&key));
let mut seen = std::collections::HashSet::new();
for _ in 0..16 {
seen.insert(sm.generate_isn(&key));
}
assert!(seen.len() > 8, "ISN 序列不得可预测/重复");
let reversed = key.reversed();
assert_ne!(key.hash() as u32, reversed.hash() as u32);
}
#[test]
fn test_tcp_state_machine_handshake() {
let mut sm = TcpStateMachine::new(1024);
let key = make_key();
let server_key = key.reversed();
let mut syn_data = [0u8; 20];
syn_data[4] = 0x00;
syn_data[5] = 0x00;
syn_data[6] = 0x00;
syn_data[7] = 0x64; syn_data[12] = 0x50;
syn_data[13] = 0x02; let syn = TcpHeader::parse(&syn_data).unwrap();
let action1 = sm.handle_packet(&key, syn, 0, 1000);
assert!(matches!(action1, TcpAction::SendSynAck { .. }));
let conn = sm.get_connection(&server_key).unwrap();
assert_eq!(conn.state, ConnectionState::SynReceived);
let server_snd_nxt = conn.snd_nxt;
let mut ack_data = [0u8; 20];
ack_data[4] = 0x00;
ack_data[5] = 0x00;
ack_data[6] = 0x00;
ack_data[7] = 0x65; ack_data[8] = (server_snd_nxt >> 24) as u8;
ack_data[9] = (server_snd_nxt >> 16) as u8;
ack_data[10] = (server_snd_nxt >> 8) as u8;
ack_data[11] = server_snd_nxt as u8;
ack_data[12] = 0x50;
ack_data[13] = 0x10; let ack = TcpHeader::parse(&ack_data).unwrap();
let action2 = sm.handle_packet(&key, ack, 0, 1001);
assert!(matches!(action2, TcpAction::Established));
let conn = sm.get_connection(&server_key).unwrap();
assert_eq!(conn.state, ConnectionState::Established);
}
#[test]
fn test_synsent_validates_synack_isn() {
fn build_synack(ack: u32) -> TcpHeader {
let mut d = [0u8; 20];
d[7] = 0x50; d[8] = (ack >> 24) as u8;
d[9] = (ack >> 16) as u8;
d[10] = (ack >> 8) as u8;
d[11] = ack as u8;
d[12] = 0x50;
d[13] = 0x12; *TcpHeader::parse(&d).unwrap()
}
let mut sm = TcpStateMachine::new(16);
let key = make_key();
let idx = sm.send_syn(key, 10_000).unwrap();
let iss = sm.table().get(idx).unwrap().snd_nxt; let action = sm.handle_packet(&key, &build_synack(iss.wrapping_add(1)), 0, 10_001);
assert!(matches!(action, TcpAction::Established));
assert_eq!(
sm.get_connection(&key).unwrap().state,
ConnectionState::Established
);
let mut sm2 = TcpStateMachine::new(16);
let key2 = make_key();
let idx2 = sm2.send_syn(key2, 20_000).unwrap();
let iss2 = sm2.table().get(idx2).unwrap().snd_nxt;
let forged = iss2.wrapping_add(1234); let action2 = sm2.handle_packet(&key2, &build_synack(forged), 0, 20_001);
assert!(
matches!(action2, TcpAction::Drop),
"伪造 ack_num 的 SYN-ACK 必须被丢弃(NET-028)"
);
assert_ne!(
sm2.get_connection(&key2).unwrap().state,
ConnectionState::Established,
"ISN 校验失败不得完成握手"
);
}
#[test]
fn test_sequence_number_wrap() {
let mut conn = TcpConnection::empty();
conn.init(make_key(), 0xFFFFFFFE, 1000, 100);
assert!(conn.is_seq_in_snd_window(0xFFFFFFFF)); assert!(conn.is_seq_in_snd_window(0x00000000));
assert!(!conn.is_seq_in_snd_window(0x00010000));
let diff = 0xFFFFFFFFu32.wrapping_sub(conn.snd_nxt);
assert_eq!(diff, 1);
let diff2 = 0x00000000u32.wrapping_sub(conn.snd_nxt);
assert_eq!(diff2, 2);
}
#[test]
fn test_connection_table_full() {
let mut table = ConnectionTable::new(4);
for i in 0..4u16 {
let key = ConnectionKey::new(
IpAddr::V4([10, 0, 0, i as u8]),
8080 + i,
IpAddr::V4([10, 0, 0, 2]),
12345,
IpVersion::V4,
);
table.insert(key, 100, 200, 1000).unwrap();
}
assert_eq!(table.count(), 4);
let extra_key = ConnectionKey::new(
IpAddr::V4([10, 0, 0, 5]),
8085,
IpAddr::V4([10, 0, 0, 2]),
12345,
IpVersion::V4,
);
let result = table.insert(extra_key, 100, 200, 1000);
assert!(result.is_err());
}
#[test]
fn test_timeout_scan() {
let mut table = ConnectionTable::new(1024);
let key = make_key();
table.insert(key, 100, 200, 1000).unwrap();
let expired = table.scan_timeouts(2000, 50);
assert_eq!(expired.len(), 1);
assert_eq!(expired[0], key);
assert_eq!(table.count(), 0);
}
#[test]
fn test_tcp_state_machine_close() {
let mut sm = TcpStateMachine::new(1024);
let key = make_key();
sm.table_mut().insert(key, 1000, 2000, 100).unwrap();
let idx = sm.table().find(&key).unwrap();
if let Some(conn) = sm.table_mut().get_mut(idx) {
conn.state = ConnectionState::Established;
}
sm.stats.active_connections = 1;
sm.stats.total_connections = 1;
let action = sm.close_connection(&key, 200);
assert!(matches!(action, TcpAction::SendFin));
let conn = sm.get_connection(&key).unwrap();
assert_eq!(conn.state, ConnectionState::FinWait1);
}
#[test]
fn test_connection_key_reversed() {
let key = make_key();
let reversed = key.reversed();
assert_eq!(reversed.src_ip, key.dst_ip);
assert_eq!(reversed.dst_ip, key.src_ip);
assert_eq!(reversed.src_port, key.dst_port);
assert_eq!(reversed.dst_port, key.src_port);
assert_eq!(reversed.ip_version, key.ip_version);
let double_reversed = reversed.reversed();
assert_eq!(double_reversed, key);
}
#[test]
fn test_connection_key_hash_consistency() {
let key1 = make_key();
let key2 = make_key();
assert_eq!(key1.hash(), key2.hash());
let key3 = ConnectionKey::new(
IpAddr::V4([10, 0, 0, 1]),
8081,
IpAddr::V4([10, 0, 0, 2]),
12345,
IpVersion::V4,
);
assert_ne!(key1.hash(), key3.hash());
}
#[test]
fn test_tcp_connection_empty() {
let conn = TcpConnection::empty();
assert!(conn.is_free());
assert!(!conn.is_time_wait());
assert_eq!(conn.state, ConnectionState::Closed);
assert_eq!(conn.snd_nxt, 0);
assert_eq!(conn.rcv_nxt, 0);
assert_eq!(conn.retransmit_count, 0);
assert!(!conn.in_fast_recovery);
}
#[test]
fn test_tcp_connection_init() {
let mut conn = TcpConnection::empty();
let key = make_key();
conn.init(key, 100, 200, 1000);
assert_eq!(conn.key, key);
assert_eq!(conn.state, ConnectionState::Listen);
assert_eq!(conn.snd_nxt, 100);
assert_eq!(conn.rcv_nxt, 200);
assert_eq!(conn.iss, 100);
assert_eq!(conn.irs, 200);
assert_eq!(conn.last_active, 1000);
assert_eq!(conn.created_at, 1000);
assert!(!conn.is_free());
}
#[test]
fn test_sequence_window_boundaries() {
let mut conn = TcpConnection::empty();
conn.init(make_key(), 1000, 2000, 100);
conn.rcv_wnd = 100;
conn.snd_wnd = 100;
assert!(conn.is_seq_in_rcv_window(2000));
assert!(conn.is_seq_in_rcv_window(2099));
assert!(!conn.is_seq_in_rcv_window(2100));
assert!(conn.is_seq_in_snd_window(1000));
assert!(conn.is_seq_in_snd_window(1099));
assert!(!conn.is_seq_in_snd_window(1100));
}
#[test]
fn test_sequence_number_wrap_around() {
let mut conn = TcpConnection::empty();
conn.init(make_key(), 0xFFFFFFF0, 0xFFFFFFF0, 100);
conn.rcv_wnd = 32;
conn.snd_wnd = 32;
assert!(conn.is_seq_in_rcv_window(0xFFFFFFF0));
assert!(conn.is_seq_in_rcv_window(0xFFFFFFFF));
assert!(conn.is_seq_in_rcv_window(0x00000000));
assert!(conn.is_seq_in_rcv_window(0x0000000F));
assert!(!conn.is_seq_in_rcv_window(0x00000010));
}
#[test]
fn test_connection_table_find_reverse_key() {
let mut table = ConnectionTable::new(1024);
let key = make_key();
let reverse_key = key.reversed();
table.insert(key, 100, 200, 1000).unwrap();
assert!(table.find(&key).is_some());
assert!(table.find(&reverse_key).is_none());
}
#[test]
fn test_connection_table_get_out_of_bounds() {
let table = ConnectionTable::new(1024);
assert!(table.get(1024).is_none());
assert!(table.get(9999).is_none());
}
#[test]
fn test_connection_table_get_mut_out_of_bounds() {
let mut table = ConnectionTable::new(1024);
assert!(table.get_mut(1024).is_none());
assert!(table.get_mut(9999).is_none());
}
#[test]
fn test_connection_table_iter_active() {
let mut table = ConnectionTable::new(1024);
for i in 0..5u16 {
let key = ConnectionKey::new(
IpAddr::V4([10, 0, 0, i as u8]),
8080 + i,
IpAddr::V4([10, 0, 0, 2]),
12345,
IpVersion::V4,
);
table.insert(key, 100, 200, 1000).unwrap();
}
let active: Vec<_> = table.iter_active().collect();
assert_eq!(active.len(), 5);
}
#[test]
fn test_connection_table_scan_timeouts_partial() {
let mut table = ConnectionTable::new(1024);
for i in 0..5u16 {
let key = ConnectionKey::new(
IpAddr::V4([10, 0, 0, i as u8]),
8080 + i,
IpAddr::V4([10, 0, 0, 2]),
12345,
IpVersion::V4,
);
table.insert(key, 100, 200, i as u64 * 100).unwrap();
}
assert_eq!(table.count(), 5);
let expired = table.scan_timeouts(250, 100);
assert_eq!(expired.len(), 2);
assert_eq!(table.count(), 3);
}
#[test]
fn test_connection_state_all_variants() {
let states = vec![
ConnectionState::Closed,
ConnectionState::Listen,
ConnectionState::SynSent,
ConnectionState::SynReceived,
ConnectionState::Established,
ConnectionState::FinWait1,
ConnectionState::FinWait2,
ConnectionState::CloseWait,
ConnectionState::Closing,
ConnectionState::TimeWait,
];
for state in &states {
let _ = format!("{:?}", state);
}
assert!(ConnectionState::Established.is_active());
assert!(ConnectionState::SynSent.is_active());
assert!(ConnectionState::TimeWait.is_active());
assert!(!ConnectionState::Closed.is_active());
assert!(ConnectionState::Established.can_receive_data());
assert!(ConnectionState::CloseWait.can_receive_data());
assert!(!ConnectionState::Listen.can_receive_data());
assert!(ConnectionState::Established.can_send_data());
assert!(!ConnectionState::CloseWait.can_send_data());
}
#[test]
fn test_tcp_stats_default() {
let stats = TcpStats::default();
assert_eq!(stats.total_connections, 0);
assert_eq!(stats.active_connections, 0);
assert_eq!(stats.syn_received, 0);
assert_eq!(stats.rst_received, 0);
assert_eq!(stats.timeouts, 0);
}
#[test]
fn test_connection_table_min_capacity() {
let table = ConnectionTable::new(0);
assert_eq!(table.capacity(), 1);
assert_eq!(table.count(), 0);
}
#[test]
fn test_connection_table_max_capacity() {
let table = ConnectionTable::new(MAX_CONNECTIONS + 100);
assert_eq!(table.capacity(), MAX_CONNECTIONS);
}
#[test]
fn test_tcp_state_machine_stats() {
let sm = TcpStateMachine::new(1024);
let stats = sm.stats();
assert_eq!(stats.total_connections, 0);
assert_eq!(stats.active_connections, 0);
}
#[test]
fn test_tcp_state_machine_get_connection_nonexistent() {
let sm = TcpStateMachine::new(1024);
let key = make_key();
assert!(sm.get_connection(&key).is_none());
}
#[test]
fn test_connection_table_remove_nonexistent() {
let mut table = ConnectionTable::new(1024);
let key = make_key();
assert!(!table.remove(&key));
}
#[test]
fn test_tcp_connection_time_wait() {
let mut conn = TcpConnection::empty();
conn.state = ConnectionState::TimeWait;
assert!(conn.is_time_wait());
assert!(!conn.is_free());
}
#[test]
fn test_hash_ip_addr_v4_and_v6() {
let v4_key = ConnectionKey::new(
IpAddr::V4([1, 2, 3, 4]),
80,
IpAddr::V4([5, 6, 7, 8]),
443,
IpVersion::V4,
);
let v6_key = ConnectionKey::new(
IpAddr::V6([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]),
80,
IpAddr::V6([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2]),
443,
IpVersion::V6,
);
let h1 = v4_key.hash();
let h2 = v6_key.hash();
assert_ne!(h1, h2);
}
#[test]
fn test_tcp_state_machine_handle_packet_on_closed() {
let mut sm = TcpStateMachine::new(1024);
let key = make_key();
let mut data = [0u8; 20];
data[12] = 0x50;
data[13] = 0x12;
let tcp = TcpHeader::parse(&data).unwrap();
let action = sm.handle_packet(&key, tcp, 0, 1000);
assert!(matches!(action, TcpAction::SendRst | TcpAction::Drop));
}
#[test]
fn test_insert_off_by_one_boundary() {
let mut table = ConnectionTable::new(2);
let key1 = ConnectionKey::new(
IpAddr::V4([10, 0, 0, 1]),
80,
IpAddr::V4([10, 0, 0, 2]),
12345,
IpVersion::V4,
);
let key2 = ConnectionKey::new(
IpAddr::V4([10, 0, 0, 3]),
80,
IpAddr::V4([10, 0, 0, 2]),
12345,
IpVersion::V4,
);
table.insert(key1, 100, 200, 1000).unwrap();
table.insert(key2, 100, 200, 1000).unwrap();
assert_eq!(table.count(), 2);
table.slots[1].state = ConnectionState::Closed;
table.count = 1;
table.free_stack.push(1);
let result = table.insert(key2, 100, 200, 1000);
assert!(result.is_ok(), "边界槽插入不应 panic");
assert_eq!(table.find(&key2), Some(1));
}
#[test]
fn test_insert_hash_collision_reuse_bucket() {
let mut table = ConnectionTable::new(4);
let key1 = ConnectionKey::new(
IpAddr::V4([10, 0, 0, 1]),
80,
IpAddr::V4([10, 0, 0, 2]),
12345,
IpVersion::V4,
);
let key2 = ConnectionKey::new(
IpAddr::V4([10, 0, 0, 3]),
90,
IpAddr::V4([10, 0, 0, 2]),
12345,
IpVersion::V4,
);
table.insert(key1, 100, 200, 1000).unwrap();
let slot1 = table.find(&key1).unwrap();
let bucket_idx = (key1.hash() as usize) & (HASH_BUCKETS - 1);
let original_bucket_val = table.buckets[bucket_idx];
assert_eq!(original_bucket_val, (slot1 as u32) + 1);
table.slots[slot1].state = ConnectionState::Closed;
table.count = 0;
table.free_stack.push(slot1 as u32);
table.insert(key1, 100, 200, 1000).unwrap();
let new_slot = table.find(&key1).unwrap();
assert_eq!(new_slot, slot1, "应复用同一槽");
assert_eq!(table.buckets[bucket_idx], (new_slot as u32) + 1);
table.insert(key2, 100, 200, 1000).unwrap();
assert!(table.find(&key2).is_some());
}
#[test]
fn test_find_free_slot_returns_correct_indices() {
let mut table = ConnectionTable::new(4);
for i in 0..4u16 {
let key = ConnectionKey::new(
IpAddr::V4([10, 0, 0, i as u8]),
8080 + i,
IpAddr::V4([10, 0, 0, 2]),
12345,
IpVersion::V4,
);
let idx = table.insert(key, 100, 200, 1000).unwrap();
assert_eq!(idx, i as usize, "free_stack 应按 0,1,2,3 顺序分配");
}
assert_eq!(table.count(), 4);
}
#[test]
fn test_remove_then_insert_reuses_slot() {
let mut table = ConnectionTable::new(4);
let key1 = ConnectionKey::new(
IpAddr::V4([10, 0, 0, 1]),
80,
IpAddr::V4([10, 0, 0, 2]),
12345,
IpVersion::V4,
);
let key2 = ConnectionKey::new(
IpAddr::V4([10, 0, 0, 3]),
80,
IpAddr::V4([10, 0, 0, 2]),
12345,
IpVersion::V4,
);
let key3 = ConnectionKey::new(
IpAddr::V4([10, 0, 0, 5]),
80,
IpAddr::V4([10, 0, 0, 2]),
12345,
IpVersion::V4,
);
let idx1 = table.insert(key1, 100, 200, 1000).unwrap();
let idx2 = table.insert(key2, 100, 200, 1000).unwrap();
assert!(table.remove(&key1));
assert_eq!(table.count(), 1);
let idx3 = table.insert(key3, 100, 200, 1000).unwrap();
assert_eq!(idx3, idx1, "应复用刚释放的槽");
assert_eq!(table.count(), 2);
assert_eq!(table.find(&key2), Some(idx2));
assert_eq!(table.find(&key3), Some(idx1));
assert!(table.find(&key1).is_none());
}
#[test]
fn test_high_churn_insert_remove_cycles() {
let mut table = ConnectionTable::new(16);
for round in 0..100u16 {
let base = round * 16;
for i in 0..16u16 {
let key = ConnectionKey::new(
IpAddr::V4([(base + i) as u8, 0, 0, 1]),
8080 + i,
IpAddr::V4([10, 0, 0, 2]),
12345,
IpVersion::V4,
);
assert!(table.insert(key, 100, 200, 1000).is_ok());
}
assert_eq!(table.count(), 16, "第 {} 轮:填满后 count 应为 16", round);
let extra_key = ConnectionKey::new(
IpAddr::V4([255, 0, 0, 1]),
9999,
IpAddr::V4([10, 0, 0, 2]),
12345,
IpVersion::V4,
);
assert!(table.insert(extra_key, 100, 200, 1000).is_err());
for i in 0..16u16 {
let key = ConnectionKey::new(
IpAddr::V4([(base + i) as u8, 0, 0, 1]),
8080 + i,
IpAddr::V4([10, 0, 0, 2]),
12345,
IpVersion::V4,
);
assert!(table.remove(&key), "第 {} 轮第 {} 个移除应成功", round, i);
}
assert_eq!(table.count(), 0, "第 {} 轮:移除后 count 应为 0", round);
}
for i in 0..16u16 {
let key = ConnectionKey::new(
IpAddr::V4([200, 0, 0, i as u8]),
8080 + i,
IpAddr::V4([10, 0, 0, 2]),
12345,
IpVersion::V4,
);
assert!(table.insert(key, 100, 200, 1000).is_ok());
}
assert_eq!(table.count(), 16);
}
#[test]
fn test_net020_reject_syn_on_non_listening_port() {
use crate::transport::acceptor::BindTable;
use crate::NetAddr;
let mut bind = BindTable::new();
bind.register(NetAddr::new_ipv4([10, 0, 0, 2], 8080), 128)
.unwrap();
let mut sm = TcpStateMachine::new(1024);
sm.attach_bind_table(bind);
let listening_key = ConnectionKey::new(
IpAddr::V4([10, 0, 0, 1]),
1000,
IpAddr::V4([10, 0, 0, 2]),
8080,
IpVersion::V4,
);
let mut syn = [0u8; 20];
syn[12] = 0x50;
syn[13] = 0x02; let tcp = TcpHeader::parse(&syn).unwrap();
let action = sm.handle_packet(&listening_key, tcp, 0, 1000);
assert!(matches!(action, TcpAction::SendSynAck { .. }));
let non_listening_key = ConnectionKey::new(
IpAddr::V4([10, 0, 0, 1]),
1001,
IpAddr::V4([10, 0, 0, 2]),
9999,
IpVersion::V4,
);
let action2 = sm.handle_packet(&non_listening_key, tcp, 0, 1000);
assert!(matches!(action2, TcpAction::Drop));
assert_eq!(sm.stats().rejected, 1);
}
#[test]
fn test_net021_offpath_fin_respects_rcv_window() {
let mut sm = TcpStateMachine::new(1024);
let ckey = make_key();
let skey = ckey.reversed();
let mut syn = [0u8; 20];
syn[7] = 0x64; syn[12] = 0x50;
syn[13] = 0x02; let tcp_syn = TcpHeader::parse(&syn).unwrap();
assert!(matches!(
sm.handle_packet(&ckey, tcp_syn, 0, 1000),
TcpAction::SendSynAck { .. }
));
let server_snd_nxt = sm.get_connection(&skey).unwrap().snd_nxt;
let mut ack = [0u8; 20];
ack[7] = 0x65; ack[8] = (server_snd_nxt >> 24) as u8;
ack[9] = (server_snd_nxt >> 16) as u8;
ack[10] = (server_snd_nxt >> 8) as u8;
ack[11] = server_snd_nxt as u8;
ack[12] = 0x50;
ack[13] = 0x10; let tcp_ack = TcpHeader::parse(&ack).unwrap();
assert!(matches!(
sm.handle_packet(&ckey, tcp_ack, 0, 1001),
TcpAction::Established
));
let conn = sm.get_connection(&skey).unwrap();
assert_eq!(conn.state, ConnectionState::Established);
let rcv_nxt = conn.rcv_nxt;
let bad_seq = rcv_nxt.wrapping_add(0x1_0000);
let mut fin_bad = [0u8; 20];
fin_bad[4] = (bad_seq >> 24) as u8;
fin_bad[5] = (bad_seq >> 16) as u8;
fin_bad[6] = (bad_seq >> 8) as u8;
fin_bad[7] = bad_seq as u8;
fin_bad[12] = 0x50;
fin_bad[13] = 0x01; let tcp_fin_bad = TcpHeader::parse(&fin_bad).unwrap();
let _ = sm.handle_packet(&ckey, tcp_fin_bad, 0, 1002);
assert_eq!(
sm.get_connection(&skey).unwrap().state,
ConnectionState::Established,
"off-path FIN 不得触发状态迁移(NET-021)"
);
let mut fin_ok = [0u8; 20];
fin_ok[4] = (rcv_nxt >> 24) as u8;
fin_ok[5] = (rcv_nxt >> 16) as u8;
fin_ok[6] = (rcv_nxt >> 8) as u8;
fin_ok[7] = rcv_nxt as u8;
fin_ok[12] = 0x50;
fin_ok[13] = 0x01; let tcp_fin_ok = TcpHeader::parse(&fin_ok).unwrap();
let _ = sm.handle_packet(&ckey, tcp_fin_ok, 0, 1003);
assert_eq!(
sm.get_connection(&skey).unwrap().state,
ConnectionState::CloseWait,
"合法 FIN 应迁移到 CloseWait"
);
}
}