use addr::L2Protocol;
use l2::{L2MappedSocket, L2Socket};
use mapped::{BlockConfig, RxFrame};
#[cfg(not(target_os = "windows"))]
use std::os::fd::{AsFd, AsRawFd, BorrowedFd, RawFd};
use std::{cmp, io};
use crate::{filter::PacketFilter, Interface};
pub mod addr;
pub mod l2;
pub mod l3;
pub mod l4;
pub mod mapped;
#[doc(hidden)]
pub mod prelude;
mod sndrcv;
pub(crate) const PACKET_RX_RING: libc::c_int = 5;
pub(crate) const PACKET_STATISTICS: libc::c_int = 6;
pub(crate) const PACKET_VERSION: libc::c_int = 10;
pub(crate) const PACKET_RESERVE: libc::c_int = 12;
pub(crate) const PACKET_TX_RING: libc::c_int = 13;
pub(crate) const PACKET_TIMESTAMP: libc::c_int = 17;
pub(crate) const PACKET_FANOUT: libc::c_int = 18;
pub(crate) const PACKET_QDISC_BYPASS: libc::c_int = 20;
pub(crate) const PACKET_FANOUT_HASH: libc::c_uint = 0;
pub(crate) const PACKET_FANOUT_LB: libc::c_uint = 1;
pub(crate) const PACKET_FANOUT_CPU: libc::c_uint = 2;
pub(crate) const PACKET_FANOUT_ROLLOVER: libc::c_uint = 3;
pub(crate) const PACKET_FANOUT_RND: libc::c_uint = 4;
pub(crate) const PACKET_FANOUT_QM: libc::c_uint = 5;
pub(crate) const PACKET_FANOUT_FLAG_ROLLOVER: libc::c_uint = 0x1000;
pub(crate) const PACKET_FANOUT_FLAG_DEFRAG: libc::c_uint = 0x8000;
pub(crate) const TP_STATUS_USER: u32 = 1 << 0;
pub(crate) const TP_STATUS_COPY: u32 = 1 << 1;
pub(crate) const TP_STATUS_LOSING: u32 = 1 << 2;
pub(crate) const TP_STATUS_CSUMNOTREADY: u32 = 1 << 3;
pub(crate) const TP_STATUS_VLAN_VALID: u32 = 1 << 4;
pub(crate) const TP_STATUS_VLAN_TPID_VALID: u32 = 1 << 6;
pub(crate) const TP_STATUS_CSUM_VALID: u32 = 1 << 7;
pub(crate) const TP_STATUS_AVAILABLE: u32 = 0;
pub(crate) const TP_STATUS_SEND_REQUEST: u32 = 1 << 0;
pub(crate) const TP_STATUS_SENDING: u32 = 1 << 1;
pub(crate) const TP_STATUS_WRONG_FORMAT: u32 = 1 << 2;
pub(crate) const TPACKET_ALIGNMENT: usize = 16;
#[repr(align(8))]
#[repr(C)]
#[allow(non_camel_case_types)]
#[derive(Clone, Copy)]
pub(crate) struct tpacket_hdr_v1 {
pub block_status: u32,
pub num_pkts: u32,
pub offset_to_first_pkt: u32,
pub blk_len: u32,
pub seq_num: u64,
pub ts_first_pkt: tpacket_bd_ts,
pub ts_last_pkt: tpacket_bd_ts,
}
#[repr(u32)]
#[allow(non_camel_case_types)]
pub(crate) enum tpacket_versions {
#[allow(unused)]
TPACKET_V1,
#[allow(unused)]
TPACKET_V2,
TPACKET_V3,
}
#[repr(C)]
#[allow(non_camel_case_types)]
pub(crate) struct tpacket_hdr_variant1 {
pub tp_rxhash: u32,
pub tp_vlan_tci: u32,
pub tp_vlan_tpid: u16,
pub tp_padding: u16,
}
#[repr(C)]
#[allow(non_camel_case_types)]
#[derive(Clone, Copy)]
pub(crate) struct tpacket_req {
pub tp_block_size: libc::c_uint,
pub tp_block_nr: libc::c_uint,
pub tp_frame_size: libc::c_uint,
pub tp_frame_nr: libc::c_uint,
}
#[repr(C)]
#[allow(non_camel_case_types)]
#[derive(Clone, Copy)]
pub(crate) struct tpacket_req3 {
pub tp_block_size: libc::c_uint,
pub tp_block_nr: libc::c_uint,
pub tp_frame_size: libc::c_uint,
pub tp_frame_nr: libc::c_uint,
pub tp_retire_blk_tov: libc::c_uint,
pub tp_sizeof_priv: libc::c_uint,
pub tp_feature_req_word: libc::c_uint,
}
#[repr(C)]
#[allow(non_camel_case_types)]
pub(crate) struct tpacket_stats {
pub tp_packets: libc::c_uint,
pub tp_drops: libc::c_uint,
}
#[repr(C)]
#[allow(non_camel_case_types)]
pub(crate) struct tpacket3_hdr {
pub tp_next_offset: u32,
pub tp_sec: u32,
pub tp_nsec: u32,
pub tp_snaplen: u32,
pub tp_len: u32,
pub tp_status: u32,
pub tp_mac: u16,
pub tp_net: u16,
pub hv1: tpacket_hdr_variant1,
pub tp_padding: [u8; 8],
}
#[repr(C)]
#[allow(non_camel_case_types)]
#[derive(Clone, Copy)]
pub(crate) struct tpacket_bd_ts {
pub ts_sec: libc::c_uint,
pub ts_usec: libc::c_uint,
}
#[repr(C)]
#[allow(non_camel_case_types)]
pub(crate) union tpacket_bd_header_u {
pub bh1: tpacket_hdr_v1,
}
#[repr(C)]
#[allow(non_camel_case_types)]
pub(crate) struct tpacket_block_desc {
pub version: u32,
pub offset_to_priv: u32,
pub hdr: tpacket_bd_header_u,
}
#[derive(Clone, Copy, PartialEq, Eq)]
pub enum TxTimestamping {
Hardware,
Software,
Sched,
}
#[derive(Clone, Copy, PartialEq, Eq)]
pub enum RxTimestamping {
Hardware,
Software,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FanoutAlgorithm {
Hash,
RoundRobin,
Cpu,
Rollover,
Random,
QueueMapping,
}
pub(crate) const DEFAULT_DRIVER_BUFFER: usize = 2 * 1024 * 1024;
pub(crate) struct SnifferImpl {
socket: L2MappedSocket,
}
impl SnifferImpl {
#[inline]
pub fn new(if_name: Interface) -> io::Result<Self> {
Self::new_with_size(if_name, DEFAULT_DRIVER_BUFFER)
}
#[inline]
pub fn new_with_size(if_name: Interface, ring_size: usize) -> io::Result<Self> {
let individual_ring_size = ring_size / 2;
let units = individual_ring_size / (131072 * 2);
if units == 0 {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"ring_size must be >= 524288 (512 KiB)",
));
}
let Ok(units) = u32::try_from(units) else {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"specified ring size was too big to represent for internal integers",
));
};
let mut block_size = 131072;
let mut block_cnt = 2;
if units > 1 {
let raw_exp = units.ilog2();
let exp = cmp::max(1, raw_exp / 3); let remainder = units % 2u32.pow(exp);
let units = units / 2u32.pow(exp);
block_size *= 2u32.pow(exp);
block_cnt = (block_cnt * units) + remainder;
}
let socket = L2Socket::new()?;
let config = BlockConfig::new(block_size, block_cnt, 131072)?; let mapped_socket = socket.packet_ring(config, None, None)?;
mapped_socket.set_filter(&mut PacketFilter::reject_all())?;
mapped_socket.bind(if_name, L2Protocol::All)?;
Ok(Self {
socket: mapped_socket,
})
}
#[inline]
pub fn activate(&mut self, filter: Option<PacketFilter>) -> io::Result<()> {
self.socket.flush()?;
match filter {
None => self.socket.clear_filter(),
Some(mut filter) => self.socket.set_filter(&mut filter),
}
}
#[inline]
pub fn deactivate(&mut self) -> io::Result<()> {
self.socket.set_filter(&mut PacketFilter::reject_all())
}
#[inline]
pub fn nonblocking(&self) -> io::Result<bool> {
self.socket.nonblocking()
}
#[inline]
pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
self.socket.set_nonblocking(nonblocking)
}
#[inline]
pub fn send(&self, buf: &[u8]) -> io::Result<usize> {
self.socket.send(buf)
}
#[inline]
pub fn recv(&self, buf: &mut [u8]) -> io::Result<usize> {
self.socket.recv(buf)
}
#[inline]
pub fn mapped_recv(&mut self) -> Option<RxFrameImpl<'_>> {
Some(RxFrameImpl {
frame: self.socket.mapped_recv()?,
})
}
}
impl AsRawFd for SnifferImpl {
fn as_raw_fd(&self) -> RawFd {
self.socket.as_raw_fd()
}
}
#[cfg(not(target_os = "windows"))]
impl AsFd for SnifferImpl {
fn as_fd(&self) -> BorrowedFd<'_> {
self.socket.as_fd()
}
}
pub(crate) struct RxFrameImpl<'a> {
frame: RxFrame<'a>,
}
impl RxFrameImpl<'_> {
pub fn data(&self) -> &[u8] {
self.frame.data()
}
pub fn data_mut(&mut self) -> &mut [u8] {
self.frame.data_mut()
}
}