Giao diện
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 Case | Mô tả |
|---|---|
| Packet Sniffers | Wireshark, tcpdump — capture all traffic |
| Network Scanners | Nmap — craft custom probe packets |
| Custom Protocols | Implement protocols không có trong kernel |
| Performance Testing | Inject packets để stress test |
| Security Research | Analyze 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_sniffer3. 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
| Bottleneck | Vấn đề | Giải pháp |
|---|---|---|
| System calls | recvfrom mỗi packet | PACKET_MMAP ring buffer |
| Memory copies | Kernel → Userspace | Zero-copy với mmap |
| Parsing overhead | Allocations | Static buffers, packed structs |
| Cache misses | Random access | Prefetching, aligned data |
| Interrupt overhead | Per-packet IRQ | NAPI 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
- Sử dụng
#[repr(C, packed)]cho network structures — đảm bảo layout đúng với wire format - Big Endian awareness — Network byte order là Big Endian, host có thể là Little Endian
- Zero-copy parsing — Cast pointers thay vì copy bytes
- Batch processing — Xử lý nhiều packets trong 1 system call
- 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.