Skip to content

Raw Sockets & Packet Anatomy Expert

Xây dựng và phân tích network packets từ Layer 2

1. Tổng quan Raw Sockets

Raw Socket là gì?

Raw socket cho phép ứng dụng bypass các protocol layers của kernel và làm việc trực tiếp với network packets. Thay vì để kernel xử lý TCP handshake, IP routing — bạn tự xây dựng và parse từng byte.

┌─────────────────────────────────────────────────────────────────┐
│                     APPLICATION LAYER                           │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│    Normal Socket (SOCK_STREAM)      Raw Socket (SOCK_RAW)      │
│    ────────────────────────────     ──────────────────────     │
│                                                                 │
│    ┌─────────────────┐              ┌─────────────────┐        │
│    │   User Data     │              │  Full Packet    │        │
│    └────────┬────────┘              │  ┌───────────┐  │        │
│             │                       │  │ Eth Header│  │        │
│             ▼                       │  ├───────────┤  │        │
│    ┌─────────────────┐              │  │ IP Header │  │        │
│    │ Kernel handles: │              │  ├───────────┤  │        │
│    │ • TCP/UDP       │              │  │TCP Header │  │        │
│    │ • IP routing    │              │  ├───────────┤  │        │
│    │ • Ethernet      │              │  │  Payload  │  │        │
│    └─────────────────┘              │  └───────────┘  │        │
│                                     └─────────────────┘        │
│                                           │                    │
│                                           ▼                    │
│                                     Direct to NIC              │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

Use Cases

Use CaseMô tả
Packet SniffersWireshark, tcpdump — capture all traffic
Network ScannersNmap — craft custom probe packets
Custom ProtocolsImplement protocols không có trong kernel
Performance TestingInject packets để stress test
Security ResearchAnalyze malformed packets, fuzzing

2. Tạo Raw Socket trong Rust

Sử dụng libc Bindings

rust
use libc::{
    socket, bind, recvfrom, sendto, close,
    AF_PACKET, SOCK_RAW, ETH_P_ALL, ETH_P_IP,
    sockaddr_ll, socklen_t, c_void,
};
use std::mem;
use std::io::{self, Error};

/// Tạo raw socket để capture tất cả Ethernet frames
/// Yêu cầu: CAP_NET_RAW capability hoặc root privileges
pub fn create_raw_socket() -> io::Result<i32> {
    // ETH_P_ALL: Capture tất cả protocols
    // ETH_P_IP: Chỉ capture IP packets
    let protocol = (ETH_P_ALL as u16).to_be() as i32;
    
    let sock = unsafe {
        socket(
            AF_PACKET,      // Address family: Packet interface
            SOCK_RAW,       // Socket type: Raw packets
            protocol        // Protocol: All Ethernet frames
        )
    };
    
    if sock < 0 {
        return Err(Error::last_os_error());
    }
    
    Ok(sock)
}

/// Bind socket vào network interface cụ thể
pub fn bind_to_interface(sock: i32, if_index: i32) -> io::Result<()> {
    let mut addr: sockaddr_ll = unsafe { mem::zeroed() };
    addr.sll_family = AF_PACKET as u16;
    addr.sll_protocol = (ETH_P_ALL as u16).to_be();
    addr.sll_ifindex = if_index;
    
    let result = unsafe {
        bind(
            sock,
            &addr as *const sockaddr_ll as *const _,
            mem::size_of::<sockaddr_ll>() as socklen_t
        )
    };
    
    if result < 0 {
        return Err(Error::last_os_error());
    }
    
    Ok(())
}

Với socket2 Crate (Safer API)

rust
use socket2::{Socket, Domain, Type, Protocol};
use std::io;

/// Tạo raw socket với socket2 — API an toàn hơn
pub fn create_raw_socket_safe() -> io::Result<Socket> {
    // Domain::PACKET = AF_PACKET (Linux)
    // Type::RAW = SOCK_RAW
    let socket = Socket::new(
        Domain::PACKET,
        Type::RAW,
        Some(Protocol::from(libc::ETH_P_ALL))
    )?;
    
    // Set socket options
    socket.set_nonblocking(true)?;
    
    Ok(socket)
}

⚠️ QUYỀN HẠN

Raw sockets yêu cầu root privileges hoặc CAP_NET_RAW capability trên Linux:

bash
# Option 1: Run as root
sudo ./my_sniffer

# Option 2: Add capability (preferred)
sudo setcap cap_net_raw+ep ./my_sniffer

3. Ethernet Frame Structure

Layout chi tiết

                         ETHERNET FRAME (14 bytes header)
┌─────────────────────────────────────────────────────────────────────────┐
│                                                                         │
│  0                   1                   2                   3          │
│  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1        │
│ ├─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┤        │
│ │                                                               │        │
│ │                    Destination MAC Address                    │        │
│ │                         (6 bytes)                             │        │
│ │                                                               │        │
│ ├───────────────────────────────────────────────────────────────┤        │
│ │                                                               │        │
│ │                      Source MAC Address                       │        │
│ │                         (6 bytes)                             │        │
│ │                                                               │        │
│ ├───────────────────────────────────────────────────────────────┤        │
│ │         EtherType (2 bytes)                                   │        │
│ │    0x0800 = IPv4  │  0x0806 = ARP  │  0x86DD = IPv6           │        │
│ ├───────────────────────────────────────────────────────────────┤        │
│ │                                                               │        │
│ │                        Payload                                │        │
│ │                    (46 - 1500 bytes)                          │        │
│ │                                                               │        │
│ └───────────────────────────────────────────────────────────────┘        │
│                                                                         │
└─────────────────────────────────────────────────────────────────────────┘

Rust Implementation

rust
/// Ethernet header structure (14 bytes)
/// Network byte order (Big Endian)
#[repr(C, packed)]
#[derive(Debug, Clone, Copy)]
pub struct EthernetHeader {
    pub dst_mac: [u8; 6],      // Destination MAC address
    pub src_mac: [u8; 6],      // Source MAC address
    pub ether_type: u16,       // Protocol type (Big Endian!)
}

impl EthernetHeader {
    pub const SIZE: usize = 14;
    
    /// Parse từ raw bytes (zero-copy)
    pub fn from_bytes(data: &[u8]) -> Option<&Self> {
        if data.len() < Self::SIZE {
            return None;
        }
        
        // SAFETY: Data đủ dài và EthernetHeader là packed
        Some(unsafe { &*(data.as_ptr() as *const Self) })
    }
    
    /// Lấy EtherType (convert từ network byte order)
    pub fn get_ether_type(&self) -> u16 {
        u16::from_be(self.ether_type)
    }
    
    /// Kiểm tra có phải IPv4 packet không
    pub fn is_ipv4(&self) -> bool {
        self.get_ether_type() == 0x0800
    }
    
    /// Kiểm tra có phải ARP packet không
    pub fn is_arp(&self) -> bool {
        self.get_ether_type() == 0x0806
    }
    
    /// Format MAC address thành string
    pub fn format_mac(mac: &[u8; 6]) -> String {
        format!(
            "{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}",
            mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]
        )
    }
}

/// EtherType constants
pub mod ether_type {
    pub const IPV4: u16 = 0x0800;
    pub const ARP: u16 = 0x0806;
    pub const IPV6: u16 = 0x86DD;
    pub const VLAN: u16 = 0x8100;
}

4. IPv4 Header Structure

Layout chi tiết (20-60 bytes)

                            IPv4 HEADER
┌─────────────────────────────────────────────────────────────────┐
│  0                   1                   2                   3  │
│  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1│
│ ├─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┤│
│ │Version│  IHL  │    DSCP     │ECN│         Total Length        ││
│ │ (4)   │ (4)   │    (6)      │(2)│           (16)              ││
│ ├───────┴───────┼─────────────┴───┼─────────────────────────────┤│
│ │         Identification          │Flags│    Fragment Offset    ││
│ │             (16)                │ (3) │        (13)           ││
│ ├─────────────────┬───────────────┼─────┴───────────────────────┤│
│ │       TTL       │   Protocol    │       Header Checksum       ││
│ │       (8)       │     (8)       │           (16)              ││
│ ├─────────────────┴───────────────┴─────────────────────────────┤│
│ │                       Source IP Address                       ││
│ │                            (32)                               ││
│ ├───────────────────────────────────────────────────────────────┤│
│ │                    Destination IP Address                     ││
│ │                            (32)                               ││
│ ├───────────────────────────────────────────────────────────────┤│
│ │                    Options (if IHL > 5)                       ││
│ │                         (variable)                            ││
│ └───────────────────────────────────────────────────────────────┘│
│                                                                 │
│  Protocol Field Values:                                         │
│    1  = ICMP        6  = TCP        17 = UDP        47 = GRE    │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

Rust Implementation với Bitwise Operations

rust
use std::net::Ipv4Addr;

/// IPv4 header (minimum 20 bytes, max 60 bytes with options)
#[repr(C, packed)]
#[derive(Clone, Copy)]
pub struct Ipv4Header {
    pub version_ihl: u8,       // Version (4 bits) + IHL (4 bits)
    pub dscp_ecn: u8,          // DSCP (6 bits) + ECN (2 bits)
    pub total_length: u16,     // Total packet length (Big Endian)
    pub identification: u16,   // Packet ID
    pub flags_fragment: u16,   // Flags (3 bits) + Fragment Offset (13 bits)
    pub ttl: u8,               // Time To Live
    pub protocol: u8,          // Upper layer protocol (TCP=6, UDP=17)
    pub checksum: u16,         // Header checksum
    pub src_ip: [u8; 4],       // Source IP address
    pub dst_ip: [u8; 4],       // Destination IP address
}

impl Ipv4Header {
    pub const MIN_SIZE: usize = 20;
    
    /// Parse từ raw bytes
    pub fn from_bytes(data: &[u8]) -> Option<&Self> {
        if data.len() < Self::MIN_SIZE {
            return None;
        }
        Some(unsafe { &*(data.as_ptr() as *const Self) })
    }
    
    /// Lấy IP version (should be 4)
    #[inline]
    pub fn version(&self) -> u8 {
        (self.version_ihl >> 4) & 0x0F
    }
    
    /// Lấy Internet Header Length (in 32-bit words)
    /// Header size = IHL * 4 bytes
    #[inline]
    pub fn ihl(&self) -> u8 {
        self.version_ihl & 0x0F
    }
    
    /// Header length in bytes
    #[inline]
    pub fn header_length(&self) -> usize {
        (self.ihl() as usize) * 4
    }
    
    /// Total packet length
    #[inline]
    pub fn total_length(&self) -> u16 {
        u16::from_be(self.total_length)
    }
    
    /// Payload length = Total - Header
    #[inline]
    pub fn payload_length(&self) -> usize {
        self.total_length() as usize - self.header_length()
    }
    
    /// Lấy Source IP
    pub fn src_addr(&self) -> Ipv4Addr {
        Ipv4Addr::from(self.src_ip)
    }
    
    /// Lấy Destination IP
    pub fn dst_addr(&self) -> Ipv4Addr {
        Ipv4Addr::from(self.dst_ip)
    }
    
    /// Kiểm tra có phải TCP packet không
    pub fn is_tcp(&self) -> bool {
        self.protocol == 6
    }
    
    /// Kiểm tra có phải UDP packet không
    pub fn is_udp(&self) -> bool {
        self.protocol == 17
    }
    
    /// Tính IP header checksum
    pub fn calculate_checksum(&self) -> u16 {
        let header_bytes = self.header_length();
        let data = unsafe {
            std::slice::from_raw_parts(
                self as *const Self as *const u8,
                header_bytes
            )
        };
        
        let mut sum: u32 = 0;
        
        // Sum 16-bit words
        for chunk in data.chunks(2) {
            let word = if chunk.len() == 2 {
                u16::from_be_bytes([chunk[0], chunk[1]])
            } else {
                (chunk[0] as u16) << 8
            };
            
            // Skip checksum field (bytes 10-11)
            if chunk.as_ptr() as usize - data.as_ptr() as usize == 10 {
                continue;
            }
            
            sum += word as u32;
        }
        
        // Fold 32-bit sum to 16 bits
        while sum >> 16 != 0 {
            sum = (sum & 0xFFFF) + (sum >> 16);
        }
        
        !(sum as u16)
    }
}

impl std::fmt::Debug for Ipv4Header {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("Ipv4Header")
            .field("version", &self.version())
            .field("ihl", &self.ihl())
            .field("total_length", &self.total_length())
            .field("ttl", &self.ttl)
            .field("protocol", &self.protocol)
            .field("src", &self.src_addr())
            .field("dst", &self.dst_addr())
            .finish()
    }
}

/// IP Protocol constants
pub mod ip_protocol {
    pub const ICMP: u8 = 1;
    pub const TCP: u8 = 6;
    pub const UDP: u8 = 17;
    pub const GRE: u8 = 47;
}

5. TCP Header Structure

Layout chi tiết (20-60 bytes)

                              TCP HEADER
┌─────────────────────────────────────────────────────────────────┐
│  0                   1                   2                   3  │
│  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1│
│ ├─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┤│
│ │           Source Port           │       Destination Port      ││
│ │             (16)                │           (16)              ││
│ ├─────────────────────────────────┴─────────────────────────────┤│
│ │                        Sequence Number                        ││
│ │                             (32)                              ││
│ ├───────────────────────────────────────────────────────────────┤│
│ │                     Acknowledgment Number                     ││
│ │                             (32)                              ││
│ ├───────┬───────┬─┬─┬─┬─┬─┬─┬───────────────────────────────────┤│
│ │  Data │       │U│A│P│R│S│F│                                   ││
│ │Offset │Reservd│R│C│S│S│Y│I│          Window Size              ││
│ │  (4)  │  (4)  │G│K│H│T│N│N│            (16)                   ││
│ ├───────┴───────┴─┴─┴─┴─┴─┴─┼───────────────────────────────────┤│
│ │          Checksum          │         Urgent Pointer           ││
│ │            (16)            │            (16)                  ││
│ ├────────────────────────────┴──────────────────────────────────┤│
│ │                    Options (if Data Offset > 5)               ││
│ │                          (variable)                           ││
│ └───────────────────────────────────────────────────────────────┘│
│                                                                 │
│  TCP Flags:                                                     │
│    SYN = 0x02   ACK = 0x10   FIN = 0x01   RST = 0x04            │
│    PSH = 0x08   URG = 0x20   ECE = 0x40   CWR = 0x80            │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

Rust Implementation

rust
/// TCP header (minimum 20 bytes)
#[repr(C, packed)]
#[derive(Clone, Copy)]
pub struct TcpHeader {
    pub src_port: u16,          // Source port (Big Endian)
    pub dst_port: u16,          // Destination port (Big Endian)
    pub seq_num: u32,           // Sequence number
    pub ack_num: u32,           // Acknowledgment number
    pub data_offset_flags: u16, // Data offset (4 bits) + Reserved + Flags
    pub window_size: u16,       // Window size
    pub checksum: u16,          // TCP checksum
    pub urgent_ptr: u16,        // Urgent pointer
}

impl TcpHeader {
    pub const MIN_SIZE: usize = 20;
    
    pub fn from_bytes(data: &[u8]) -> Option<&Self> {
        if data.len() < Self::MIN_SIZE {
            return None;
        }
        Some(unsafe { &*(data.as_ptr() as *const Self) })
    }
    
    /// Source port (host byte order)
    #[inline]
    pub fn src_port(&self) -> u16 {
        u16::from_be(self.src_port)
    }
    
    /// Destination port (host byte order)
    #[inline]
    pub fn dst_port(&self) -> u16 {
        u16::from_be(self.dst_port)
    }
    
    /// Sequence number
    #[inline]
    pub fn seq(&self) -> u32 {
        u32::from_be(self.seq_num)
    }
    
    /// Acknowledgment number
    #[inline]
    pub fn ack(&self) -> u32 {
        u32::from_be(self.ack_num)
    }
    
    /// Data offset (header length in 32-bit words)
    #[inline]
    pub fn data_offset(&self) -> u8 {
        let raw = u16::from_be(self.data_offset_flags);
        ((raw >> 12) & 0x0F) as u8
    }
    
    /// Header length in bytes
    #[inline]
    pub fn header_length(&self) -> usize {
        (self.data_offset() as usize) * 4
    }
    
    /// TCP Flags
    #[inline]
    pub fn flags(&self) -> TcpFlags {
        let raw = u16::from_be(self.data_offset_flags);
        TcpFlags::from_bits_truncate((raw & 0x01FF) as u8)
    }
    
    /// Window size
    #[inline]
    pub fn window(&self) -> u16 {
        u16::from_be(self.window_size)
    }
}

bitflags::bitflags! {
    /// TCP Flags
    #[derive(Debug, Clone, Copy, PartialEq, Eq)]
    pub struct TcpFlags: u8 {
        const FIN = 0x01;
        const SYN = 0x02;
        const RST = 0x04;
        const PSH = 0x08;
        const ACK = 0x10;
        const URG = 0x20;
        const ECE = 0x40;
        const CWR = 0x80;
    }
}

impl std::fmt::Debug for TcpHeader {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("TcpHeader")
            .field("src_port", &self.src_port())
            .field("dst_port", &self.dst_port())
            .field("seq", &self.seq())
            .field("ack", &self.ack())
            .field("flags", &self.flags())
            .field("window", &self.window())
            .finish()
    }
}

6. Zero-Copy Packet Sniffer Blueprint

Kiến trúc High-Performance

                    ZERO-COPY PACKET SNIFFER ARCHITECTURE
┌─────────────────────────────────────────────────────────────────────┐
│                                                                     │
│   ┌─────────────────────────────────────────────────────────────┐   │
│   │                        NIC (Network Card)                   │   │
│   └───────────────────────────┬─────────────────────────────────┘   │
│                               │ DMA                                 │
│                               ▼                                     │
│   ┌─────────────────────────────────────────────────────────────┐   │
│   │                    Kernel Ring Buffer                       │   │
│   │   ┌─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┐         │   │
│   │   │Pkt 1│Pkt 2│Pkt 3│ ... │ ... │ ... │Pkt N│     │         │   │
│   │   └──┬──┴──┬──┴──┬──┴─────┴─────┴─────┴──┬──┴─────┘         │   │
│   │      │     │     │                       │                  │   │
│   └──────┼─────┼─────┼───────────────────────┼──────────────────┘   │
│          │     │     │                       │                      │
│   ┌──────┼─────┼─────┼───────────────────────┼──────────────────┐   │
│   │      ▼     ▼     ▼     mmap()            ▼                  │   │
│   │   ┌─────────────────────────────────────────────────────┐   │   │
│   │   │              User-Space Mapped Memory               │   │   │
│   │   │           (Zero-copy access to packets)             │   │   │
│   │   └─────────────────────────────────────────────────────┘   │   │
│   │                                                             │   │
│   │                      USERSPACE SNIFFER                      │   │
│   └─────────────────────────────────────────────────────────────┘   │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

Triển khai với PACKET_MMAP

rust
use libc::{
    mmap, munmap, setsockopt, getsockopt,
    PROT_READ, PROT_WRITE, MAP_SHARED,
    SOL_PACKET, PACKET_RX_RING, PACKET_TX_RING,
    c_void,
};
use std::io;
use std::ptr;

/// Ring buffer block descriptor
#[repr(C)]
pub struct TpacketBlockDesc {
    pub version: u32,
    pub offset_to_priv: u32,
    pub hdr: TpacketHdrV1,
}

#[repr(C)]
pub struct TpacketHdrV1 {
    pub block_status: u32,
    pub num_pkts: u32,
    pub offset_to_first_pkt: u32,
    pub blk_len: u32,
    // ... more fields
}

/// PACKET_MMAP ring buffer
pub struct PacketRing {
    socket: i32,
    ring_buffer: *mut u8,
    ring_size: usize,
    block_size: usize,
    block_count: usize,
    current_block: usize,
}

impl PacketRing {
    /// Tạo ring buffer với PACKET_MMAP
    pub fn new(socket: i32, block_size: usize, block_count: usize) -> io::Result<Self> {
        let ring_size = block_size * block_count;
        
        // Configure ring buffer
        let req = tpacket_req3 {
            tp_block_size: block_size as u32,
            tp_block_nr: block_count as u32,
            tp_frame_size: 2048, // Maximum frame size
            tp_frame_nr: (block_size / 2048 * block_count) as u32,
            tp_retire_blk_tov: 60,   // Timeout ms
            tp_sizeof_priv: 0,
            tp_feature_req_word: 0,
        };
        
        let result = unsafe {
            setsockopt(
                socket,
                SOL_PACKET,
                PACKET_RX_RING,
                &req as *const _ as *const c_void,
                std::mem::size_of::<tpacket_req3>() as u32
            )
        };
        
        if result < 0 {
            return Err(io::Error::last_os_error());
        }
        
        // Memory map the ring buffer
        let ring_buffer = unsafe {
            mmap(
                ptr::null_mut(),
                ring_size,
                PROT_READ | PROT_WRITE,
                MAP_SHARED,
                socket,
                0
            )
        };
        
        if ring_buffer == libc::MAP_FAILED {
            return Err(io::Error::last_os_error());
        }
        
        Ok(Self {
            socket,
            ring_buffer: ring_buffer as *mut u8,
            ring_size,
            block_size,
            block_count,
            current_block: 0,
        })
    }
    
    /// Đọc packet tiếp theo (zero-copy)
    pub fn next_packet(&mut self) -> Option<&[u8]> {
        let block_ptr = unsafe {
            self.ring_buffer.add(self.current_block * self.block_size)
        };
        
        let block_desc = unsafe { &*(block_ptr as *const TpacketBlockDesc) };
        
        // Check if block is ready
        if block_desc.hdr.block_status & 0x01 == 0 {
            return None; // No packet available
        }
        
        let packet_offset = block_desc.hdr.offset_to_first_pkt as usize;
        let packet_len = block_desc.hdr.blk_len as usize;
        
        let packet = unsafe {
            std::slice::from_raw_parts(
                block_ptr.add(packet_offset),
                packet_len
            )
        };
        
        // Move to next block
        self.current_block = (self.current_block + 1) % self.block_count;
        
        Some(packet)
    }
}

impl Drop for PacketRing {
    fn drop(&mut self) {
        unsafe {
            munmap(self.ring_buffer as *mut c_void, self.ring_size);
        }
    }
}

#[repr(C)]
struct tpacket_req3 {
    tp_block_size: u32,
    tp_block_nr: u32,
    tp_frame_size: u32,
    tp_frame_nr: u32,
    tp_retire_blk_tov: u32,
    tp_sizeof_priv: u32,
    tp_feature_req_word: u32,
}

7. Complete Packet Parser

Unified Packet Struct

rust
use std::net::Ipv4Addr;

/// Parsed packet với zero-copy references
pub struct ParsedPacket<'a> {
    pub raw: &'a [u8],
    pub ethernet: &'a EthernetHeader,
    pub ip: Option<&'a Ipv4Header>,
    pub tcp: Option<&'a TcpHeader>,
    pub payload: &'a [u8],
}

impl<'a> ParsedPacket<'a> {
    /// Parse raw bytes thành structured packet
    pub fn parse(data: &'a [u8]) -> Option<Self> {
        // Parse Ethernet header
        let ethernet = EthernetHeader::from_bytes(data)?;
        let mut offset = EthernetHeader::SIZE;
        
        // Parse IP header (if IPv4)
        let ip = if ethernet.is_ipv4() {
            let ip_header = Ipv4Header::from_bytes(&data[offset..])?;
            offset += ip_header.header_length();
            Some(ip_header)
        } else {
            None
        };
        
        // Parse TCP header (if TCP)
        let tcp = if let Some(ip) = ip {
            if ip.is_tcp() {
                let tcp_header = TcpHeader::from_bytes(&data[offset..])?;
                offset += tcp_header.header_length();
                Some(tcp_header)
            } else {
                None
            }
        } else {
            None
        };
        
        let payload = &data[offset..];
        
        Some(Self {
            raw: data,
            ethernet,
            ip,
            tcp,
            payload,
        })
    }
    
    /// Kiểm tra có phải HTTP request không
    pub fn is_http(&self) -> bool {
        if let Some(tcp) = self.tcp {
            let port = tcp.dst_port();
            if port == 80 || port == 8080 {
                return self.payload.starts_with(b"GET ") 
                    || self.payload.starts_with(b"POST ")
                    || self.payload.starts_with(b"HTTP/");
            }
        }
        false
    }
    
    /// Summary cho logging
    pub fn summary(&self) -> String {
        let mut s = String::new();
        
        if let Some(ip) = self.ip {
            s.push_str(&format!(
                "{} -> {}",
                ip.src_addr(),
                ip.dst_addr()
            ));
            
            if let Some(tcp) = self.tcp {
                s.push_str(&format!(
                    " [TCP {}:{} -> {}] {:?}",
                    tcp.src_port(),
                    tcp.dst_port(),
                    tcp.seq(),
                    tcp.flags()
                ));
            }
        }
        
        s
    }
}

// === USAGE: Simple Packet Sniffer ===

fn main() -> io::Result<()> {
    let sock = create_raw_socket()?;
    let mut buffer = [0u8; 65535];
    
    println!("🔍 Packet Sniffer Started...");
    
    loop {
        let len = unsafe {
            libc::recvfrom(
                sock,
                buffer.as_mut_ptr() as *mut c_void,
                buffer.len(),
                0,
                std::ptr::null_mut(),
                std::ptr::null_mut()
            )
        };
        
        if len < 0 {
            eprintln!("recvfrom error: {}", io::Error::last_os_error());
            continue;
        }
        
        let data = &buffer[..len as usize];
        
        if let Some(packet) = ParsedPacket::parse(data) {
            println!("{}", packet.summary());
            
            if packet.is_http() {
                println!("  HTTP: {:?}", 
                    String::from_utf8_lossy(&packet.payload[..64.min(packet.payload.len())])
                );
            }
        }
    }
}

8. Performance Considerations

Bottlenecks và Solutions

BottleneckVấn đềGiải pháp
System callsrecvfrom mỗi packetPACKET_MMAP ring buffer
Memory copiesKernel → UserspaceZero-copy với mmap
Parsing overheadAllocationsStatic buffers, packed structs
Cache missesRandom accessPrefetching, aligned data
Interrupt overheadPer-packet IRQNAPI batching

Benchmark Template

rust
use std::time::Instant;

fn benchmark_parser(data: &[u8], iterations: usize) {
    let start = Instant::now();
    
    for _ in 0..iterations {
        let _ = ParsedPacket::parse(data);
    }
    
    let elapsed = start.elapsed();
    let per_packet = elapsed / iterations as u32;
    
    println!(
        "Parsed {} packets in {:?} ({:?}/packet, {:.2} Mpps)",
        iterations,
        elapsed,
        per_packet,
        iterations as f64 / elapsed.as_secs_f64() / 1_000_000.0
    );
}

🎯 Best Practices

  1. Sử dụng #[repr(C, packed)] cho network structures — đảm bảo layout đúng với wire format
  2. Big Endian awareness — Network byte order là Big Endian, host có thể là Little Endian
  3. Zero-copy parsing — Cast pointers thay vì copy bytes
  4. Batch processing — Xử lý nhiều packets trong 1 system call
  5. Preallocate buffers — Tránh heap allocations trong hot path

⚠️ SECURITY WARNING

Raw sockets có thể capture tất cả traffic trên network interface. Chỉ sử dụng cho:

  • Network debugging/analysis
  • Security research (với authorization)
  • Custom protocol development

KHÔNG sử dụng cho mục đích sniffing trái phép.