pub mod opt;
use std::fmt::{self, Formatter};
use std::ops::Deref;
use smol_str::{SmolStr, SmolStrBuilder};
use zerocopy::byteorder::{BigEndian, U16, U32};
use zerocopy::{FromBytes, IntoBytes, Unaligned};
use crate::packet::tcp::opt::TcpOptionsIter;
use crate::packet::{HeaderParser, PacketHeader};
#[repr(C, packed)]
#[derive(
FromBytes, IntoBytes, Unaligned, Debug, Clone, Copy, zerocopy::KnownLayout, zerocopy::Immutable,
)]
pub struct TcpHeader {
src_port: U16<BigEndian>,
dst_port: U16<BigEndian>,
sequence_number: U32<BigEndian>,
acknowledgment_number: U32<BigEndian>,
data_offset_flags: U16<BigEndian>,
window_size: U16<BigEndian>,
checksum: U16<BigEndian>,
urgent_pointer: U16<BigEndian>,
}
impl TcpHeader {
pub const FLAG_FIN: u8 = 0x01;
pub const FLAG_SYN: u8 = 0x02;
pub const FLAG_RST: u8 = 0x04;
pub const FLAG_PSH: u8 = 0x08;
pub const FLAG_ACK: u8 = 0x10;
pub const FLAG_URG: u8 = 0x20;
pub const FLAG_ECE: u8 = 0x40;
pub const FLAG_CWR: u8 = 0x80;
#[allow(unused)]
const NAME: &'static str = "TcpHeader";
#[inline]
pub fn data_offset(&self) -> u8 {
(self.data_offset_flags.get() >> 12) as u8
}
#[inline]
pub fn flags(&self) -> u8 {
(self.data_offset_flags.get() & 0x00FF) as u8
}
#[inline]
pub fn has_fin(&self) -> bool {
self.flags() & Self::FLAG_FIN != 0
}
#[inline]
pub fn has_syn(&self) -> bool {
self.flags() & Self::FLAG_SYN != 0
}
#[inline]
pub fn has_rst(&self) -> bool {
self.flags() & Self::FLAG_RST != 0
}
#[inline]
pub fn has_psh(&self) -> bool {
self.flags() & Self::FLAG_PSH != 0
}
#[inline]
pub fn has_ack(&self) -> bool {
self.flags() & Self::FLAG_ACK != 0
}
#[inline]
pub fn has_urg(&self) -> bool {
self.flags() & Self::FLAG_URG != 0
}
#[inline]
pub fn has_ece(&self) -> bool {
self.flags() & Self::FLAG_ECE != 0
}
#[inline]
pub fn has_cwr(&self) -> bool {
self.flags() & Self::FLAG_CWR != 0
}
#[inline]
pub fn src_port(&self) -> u16 {
self.src_port.get()
}
#[inline]
pub fn dst_port(&self) -> u16 {
self.dst_port.get()
}
#[inline]
pub fn sequence_number(&self) -> u32 {
self.sequence_number.get()
}
#[inline]
fn is_valid(&self) -> bool {
let offset = self.data_offset();
(5..=15).contains(&offset)
}
#[inline]
pub fn acknowledgment_number(&self) -> u32 {
self.acknowledgment_number.get()
}
#[inline]
pub fn window_size(&self) -> u16 {
self.window_size.get()
}
#[inline]
pub fn urgent_pointer(&self) -> u16 {
self.urgent_pointer.get()
}
pub fn flags_string(&self) -> SmolStr {
let mut result = SmolStrBuilder::new();
if self.has_fin() {
result.push('F');
}
if self.has_syn() {
result.push('S');
}
if self.has_rst() {
result.push('R');
}
if self.has_psh() {
result.push('P');
}
if self.has_ack() {
result.push('A');
}
if self.has_urg() {
result.push('U');
}
if self.has_ece() {
result.push('E');
}
if self.has_cwr() {
result.push('C');
}
result.finish()
}
}
#[derive(Debug, Clone)]
pub struct TcpHeaderOpt<'a> {
pub header: &'a TcpHeader,
pub raw_options: &'a [u8],
}
impl<'a> TcpHeaderOpt<'a> {
pub fn options(&'a self) -> TcpOptionsIter<'a> {
TcpOptionsIter::new(self.raw_options)
}
}
impl Deref for TcpHeaderOpt<'_> {
type Target = TcpHeader;
#[inline]
fn deref(&self) -> &Self::Target {
self.header
}
}
impl PacketHeader for TcpHeader {
const NAME: &'static str = "TcpHeader";
type InnerType = ();
#[inline]
fn inner_type(&self) -> Self::InnerType {}
#[inline]
fn total_len(&self, _buf: &[u8]) -> usize {
(self.data_offset() as usize) * 4
}
#[inline]
fn is_valid(&self) -> bool {
self.is_valid()
}
}
impl HeaderParser for TcpHeader {
type Output<'a> = TcpHeaderOpt<'a>;
#[inline]
fn into_view<'a>(header: &'a Self, raw_options: &'a [u8]) -> Self::Output<'a> {
TcpHeaderOpt {
header,
raw_options,
}
}
}
impl fmt::Display for TcpHeader {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(
f,
"TCP {} -> {} [{}] seq={} ack={} win={}",
self.src_port(),
self.dst_port(),
self.flags_string(),
self.sequence_number(),
self.acknowledgment_number(),
self.window_size()
)?;
if self.data_offset() > 5 {
write!(f, " +opts")?;
}
Ok(())
}
}
impl fmt::Display for TcpHeaderOpt<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.header)?;
if !self.raw_options.is_empty() {
write!(f, " opts=[")?;
let mut first = true;
for opt in self.options().flatten() {
if !first {
write!(f, ",")?;
}
first = false;
write!(f, "{}", opt)?;
}
write!(f, "]")?;
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_tcp_flags() {
let header = TcpHeader {
src_port: U16::new(80),
dst_port: U16::new(12345),
sequence_number: U32::new(0),
acknowledgment_number: U32::new(0),
data_offset_flags: U16::new((5 << 12) | TcpHeader::FLAG_SYN as u16),
window_size: U16::new(65535),
checksum: U16::new(0),
urgent_pointer: U16::new(0),
};
let buf = [0u8; 20];
assert_eq!(header.data_offset(), 5);
assert_eq!(header.total_len(&buf), 20);
assert!(header.has_syn());
assert!(!header.has_ack());
assert!(header.is_valid());
}
#[test]
fn test_tcp_header_validation() {
let mut header = TcpHeader {
src_port: U16::new(80),
dst_port: U16::new(12345),
sequence_number: U32::new(0),
acknowledgment_number: U32::new(0),
data_offset_flags: U16::new(4 << 12), window_size: U16::new(65535),
checksum: U16::new(0),
urgent_pointer: U16::new(0),
};
assert!(!header.is_valid());
header.data_offset_flags = U16::new(5 << 12);
assert!(header.is_valid());
}
#[test]
fn test_tcp_header_size() {
assert_eq!(std::mem::size_of::<TcpHeader>(), 20);
assert_eq!(TcpHeader::FIXED_LEN, 20);
}
#[test]
fn test_tcp_all_flags() {
let mut header = create_test_header();
header.data_offset_flags = U16::new((5 << 12) | TcpHeader::FLAG_FIN as u16);
assert!(header.has_fin());
assert!(!header.has_syn());
header.data_offset_flags = U16::new((5 << 12) | TcpHeader::FLAG_SYN as u16);
assert!(header.has_syn());
assert!(!header.has_fin());
header.data_offset_flags = U16::new((5 << 12) | TcpHeader::FLAG_RST as u16);
assert!(header.has_rst());
header.data_offset_flags = U16::new((5 << 12) | TcpHeader::FLAG_PSH as u16);
assert!(header.has_psh());
header.data_offset_flags = U16::new((5 << 12) | TcpHeader::FLAG_ACK as u16);
assert!(header.has_ack());
header.data_offset_flags = U16::new((5 << 12) | TcpHeader::FLAG_URG as u16);
assert!(header.has_urg());
header.data_offset_flags = U16::new((5 << 12) | TcpHeader::FLAG_ECE as u16);
assert!(header.has_ece());
header.data_offset_flags = U16::new((5 << 12) | TcpHeader::FLAG_CWR as u16);
assert!(header.has_cwr());
header.data_offset_flags =
U16::new((5 << 12) | (TcpHeader::FLAG_SYN | TcpHeader::FLAG_ACK) as u16);
assert!(header.has_syn());
assert!(header.has_ack());
assert_eq!(header.flags_string(), "SA");
}
#[test]
fn test_tcp_parsing_basic() {
let packet = create_test_packet();
let result = TcpHeader::from_bytes(&packet);
assert!(result.is_ok());
let (header, payload) = result.unwrap();
assert_eq!(header.src_port(), 54321);
assert_eq!(header.dst_port(), 80);
assert_eq!(header.data_offset(), 5);
assert!(header.has_syn());
assert!(header.is_valid());
assert_eq!(payload.len(), 0); }
#[test]
fn test_tcp_parsing_too_small() {
let packet = vec![0u8; 19];
let result = TcpHeader::from_bytes(&packet);
assert!(result.is_err());
}
#[test]
fn test_tcp_total_len_no_options() {
let packet = create_test_packet();
let (header, _) = TcpHeader::from_bytes(&packet).unwrap();
assert_eq!(header.total_len(&packet), 20);
assert_eq!(header.data_offset(), 5);
}
#[test]
fn test_tcp_with_mss_option() {
let mut packet = create_test_packet();
packet[12] = 0x60; packet[13] = 0x02;
packet.push(0x02); packet.push(0x04); packet.extend_from_slice(&1460u16.to_be_bytes());
let (header, _) = TcpHeader::from_bytes(&packet).unwrap();
assert_eq!(header.data_offset(), 6);
assert_eq!(header.total_len(&packet), 24); assert!(header.is_valid());
let options_len = (header.data_offset() as usize * 4) - TcpHeader::FIXED_LEN;
let options_start = TcpHeader::FIXED_LEN;
let raw_options = &packet[options_start..options_start + options_len];
assert_eq!(raw_options.len(), 4);
assert_eq!(raw_options[0], 0x02); assert_eq!(raw_options[1], 0x04); }
#[test]
fn test_tcp_with_window_scale_option() {
let mut packet = create_test_packet();
packet[12] = 0x60;
packet[13] = 0x02;
packet.push(0x03); packet.push(0x03); packet.push(0x07); packet.push(0x01);
let (header, _) = TcpHeader::from_bytes(&packet).unwrap();
assert_eq!(header.data_offset(), 6);
assert_eq!(header.total_len(&packet), 24);
let options_len = (header.data_offset() as usize * 4) - TcpHeader::FIXED_LEN;
let options_start = TcpHeader::FIXED_LEN;
let raw_options = &packet[options_start..options_start + options_len];
assert_eq!(raw_options.len(), 4);
assert_eq!(raw_options[0], 0x03); assert_eq!(raw_options[2], 0x07); }
#[test]
fn test_tcp_with_timestamp_option() {
let mut packet = create_test_packet();
packet[12] = 0x80;
packet[13] = 0x02;
packet.push(0x08); packet.push(0x0A); packet.extend_from_slice(&12345678u32.to_be_bytes()); packet.extend_from_slice(&87654321u32.to_be_bytes());
packet.push(0x01); packet.push(0x01);
let (header, _) = TcpHeader::from_bytes(&packet).unwrap();
assert_eq!(header.data_offset(), 8);
assert_eq!(header.total_len(&packet), 32);
let options_len = (header.data_offset() as usize * 4) - TcpHeader::FIXED_LEN;
let options_start = TcpHeader::FIXED_LEN;
let raw_options = &packet[options_start..options_start + options_len];
assert_eq!(raw_options.len(), 12);
assert_eq!(raw_options[0], 0x08); assert_eq!(raw_options[1], 0x0A); }
#[test]
fn test_tcp_with_sack_permitted_option() {
let mut packet = create_test_packet();
packet[12] = 0x60;
packet[13] = 0x02;
packet.push(0x04); packet.push(0x02); packet.push(0x01); packet.push(0x01);
let (header, _) = TcpHeader::from_bytes(&packet).unwrap();
assert_eq!(header.data_offset(), 6);
assert_eq!(header.total_len(&packet), 24);
let options_len = (header.data_offset() as usize * 4) - TcpHeader::FIXED_LEN;
let options_start = TcpHeader::FIXED_LEN;
let raw_options = &packet[options_start..options_start + options_len];
assert_eq!(raw_options[0], 0x04); }
#[test]
fn test_tcp_with_multiple_options() {
let mut packet = create_test_packet();
packet[12] = 0xA0;
packet[13] = 0x02;
packet.push(0x02); packet.push(0x04); packet.extend_from_slice(&1460u16.to_be_bytes());
packet.push(0x04); packet.push(0x02);
packet.push(0x08); packet.push(0x0A); packet.extend_from_slice(&12345u32.to_be_bytes()); packet.extend_from_slice(&0u32.to_be_bytes());
packet.push(0x03); packet.push(0x03); packet.push(0x07); packet.push(0x01);
let (header, _) = TcpHeader::from_bytes(&packet).unwrap();
assert_eq!(header.data_offset(), 10);
assert_eq!(header.total_len(&packet), 40);
let options_len = (header.data_offset() as usize * 4) - TcpHeader::FIXED_LEN;
let options_start = TcpHeader::FIXED_LEN;
let raw_options = &packet[options_start..options_start + options_len];
assert_eq!(raw_options.len(), 20);
assert_eq!(raw_options[0], 0x02); assert_eq!(raw_options[4], 0x04); assert_eq!(raw_options[6], 0x08); assert_eq!(raw_options[16], 0x03); }
#[test]
fn test_tcp_from_bytes_with_options_and_payload() {
let mut packet = create_test_packet();
packet[12] = 0x70;
packet[13] = 0x18;
packet.push(0x02); packet.push(0x04); packet.extend_from_slice(&1460u16.to_be_bytes());
packet.extend_from_slice(&[0x01, 0x01, 0x01, 0x01]);
let payload_data = b"HTTP/1.1 200 OK\r\n";
packet.extend_from_slice(payload_data);
let result = TcpHeader::from_bytes(&packet);
assert!(result.is_ok());
let (header, payload) = result.unwrap();
assert_eq!(payload.len(), payload_data.len());
assert_eq!(payload, payload_data);
assert_eq!(header.data_offset(), 7);
assert_eq!(header.total_len(&packet), 28);
assert!(header.has_psh());
assert!(header.has_ack());
}
#[test]
fn test_tcp_total_len_includes_options() {
let mut packet = create_test_packet();
let (header, _) = TcpHeader::from_bytes(&packet).unwrap();
assert_eq!(header.total_len(&packet), 20);
assert_eq!(header.data_offset(), 5);
packet = create_test_packet();
packet[12] = 0x60; packet.extend_from_slice(&[0x01, 0x01, 0x01, 0x01]); let (header, _) = TcpHeader::from_bytes(&packet).unwrap();
assert_eq!(header.total_len(&packet), 24); assert_eq!(header.data_offset(), 6);
packet = create_test_packet();
packet[12] = 0x70;
packet.extend_from_slice(&[0x01; 8]); let (header, _) = TcpHeader::from_bytes(&packet).unwrap();
assert_eq!(header.total_len(&packet), 28); assert_eq!(header.data_offset(), 7);
packet = create_test_packet();
packet[12] = 0xF0; packet.extend_from_slice(&[0x01; 40]); let (header, _) = TcpHeader::from_bytes(&packet).unwrap();
assert_eq!(header.total_len(&packet), 60); assert_eq!(header.data_offset(), 15);
}
#[test]
fn test_tcp_options_extraction() {
let mut packet = create_test_packet();
let (header, _) = TcpHeader::from_bytes(&packet).unwrap();
let options_len = (header.data_offset() as usize * 4) - TcpHeader::FIXED_LEN;
assert_eq!(options_len, 0);
packet = create_test_packet();
packet[12] = 0x60;
packet.extend_from_slice(&[0x02, 0x04, 0x05, 0xb4]); let (header, _) = TcpHeader::from_bytes(&packet).unwrap();
let options_len = (header.data_offset() as usize * 4) - TcpHeader::FIXED_LEN;
let options_start = TcpHeader::FIXED_LEN;
let raw_options = &packet[options_start..options_start + options_len];
assert_eq!(raw_options.len(), 4);
}
#[test]
fn test_tcp_flags_string() {
let mut header = create_test_header();
header.data_offset_flags = U16::new(5 << 12);
assert_eq!(header.flags_string(), "");
header.data_offset_flags = U16::new((5 << 12) | TcpHeader::FLAG_SYN as u16);
assert_eq!(header.flags_string(), "S");
header.data_offset_flags =
U16::new((5 << 12) | (TcpHeader::FLAG_SYN | TcpHeader::FLAG_ACK) as u16);
assert_eq!(header.flags_string(), "SA");
header.data_offset_flags = U16::new(
(5 << 12) | (TcpHeader::FLAG_FIN | TcpHeader::FLAG_PSH | TcpHeader::FLAG_ACK) as u16,
);
assert_eq!(header.flags_string(), "FPA");
header.data_offset_flags = U16::new((5 << 12) | 0xFF);
assert_eq!(header.flags_string(), "FSRPAUEC");
}
#[test]
fn test_tcp_sequence_and_ack_numbers() {
let mut header = create_test_header();
header.sequence_number = U32::new(1000);
header.acknowledgment_number = U32::new(2000);
assert_eq!(header.sequence_number(), 1000);
assert_eq!(header.acknowledgment_number(), 2000);
}
#[test]
fn test_tcp_window_and_urgent() {
let mut header = create_test_header();
header.window_size = U16::new(65535);
header.urgent_pointer = U16::new(100);
assert_eq!(header.window_size(), 65535);
assert_eq!(header.urgent_pointer(), 100);
}
fn create_test_header() -> TcpHeader {
TcpHeader {
src_port: U16::new(80),
dst_port: U16::new(12345),
sequence_number: U32::new(0),
acknowledgment_number: U32::new(0),
data_offset_flags: U16::new(5 << 12), window_size: U16::new(65535),
checksum: U16::new(0),
urgent_pointer: U16::new(0),
}
}
fn create_test_packet() -> Vec<u8> {
let mut packet = Vec::new();
packet.extend_from_slice(&54321u16.to_be_bytes());
packet.extend_from_slice(&80u16.to_be_bytes());
packet.extend_from_slice(&1000u32.to_be_bytes());
packet.extend_from_slice(&0u32.to_be_bytes());
packet.extend_from_slice(&0x5002u16.to_be_bytes());
packet.extend_from_slice(&65535u16.to_be_bytes());
packet.extend_from_slice(&0u16.to_be_bytes());
packet.extend_from_slice(&0u16.to_be_bytes());
packet
}
}