use std::time::{Duration, SystemTime, UNIX_EPOCH};
use std::{io, mem, slice};
#[derive(Clone, Copy, PartialEq, Eq)]
pub(crate) struct FrameIndex {
pub blocks_index: usize,
pub frame_offset: Option<usize>,
}
#[derive(Clone, Copy)]
pub struct BlockConfig {
block_size: u32,
block_cnt: u32,
frame_size: u32,
frame_cnt: u32,
map_length: usize,
}
impl BlockConfig {
pub fn new(block_size: u32, block_cnt: u32, frame_size: u32) -> io::Result<Self> {
let Some(map_length) = (block_size as usize).checked_mul(block_cnt as usize) else {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"overflowing total ring size",
));
};
if map_length.checked_mul(2).is_none() {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"overflowing total ring size",
));
}
let Some(frame_cnt_usize) = map_length.checked_div(frame_size as usize) else {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"overflowing total frame count",
));
};
let Ok(frame_cnt) = u32::try_from(frame_cnt_usize) else {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"overflowing total frame size",
));
};
Ok(BlockConfig {
block_size,
block_cnt,
frame_size,
frame_cnt,
map_length,
})
}
#[inline]
pub fn block_size(&self) -> u32 {
self.block_size
}
#[inline]
pub fn block_cnt(&self) -> u32 {
self.block_cnt
}
#[inline]
pub fn frame_size(&self) -> u32 {
self.frame_size
}
#[inline]
pub fn frame_cnt(&self) -> u32 {
self.frame_cnt
}
#[inline]
pub fn map_length(&self) -> usize {
self.map_length
}
}
#[derive(Clone, Copy, PartialEq, Eq)]
pub(crate) enum OsiLayer {
L2,
L3,
}
pub struct PacketTxRing {
ring_start: *mut u8,
block_size: usize,
blocks: Vec<PacketTxBlock>,
}
impl PacketTxRing {
pub(crate) unsafe fn new(ring_start: *mut u8, config: BlockConfig) -> Self {
let frame_size = config.frame_size as usize;
let block_cnt = config.block_cnt as usize;
let block_size = config.block_size as usize;
debug_assert!(block_size >= mem::size_of::<crate::linux::tpacket_block_desc>());
let mut blocks = Vec::new();
for i in 0..block_cnt {
let block_slice = slice::from_raw_parts_mut(ring_start.add(block_size * i), block_size);
let (description_bytes, block_slice) =
block_slice.split_at_mut(mem::size_of::<crate::linux::tpacket_block_desc>());
let block_description = unsafe {
(description_bytes.as_mut_ptr() as *mut crate::linux::tpacket_block_desc)
.as_mut()
.unwrap()
};
debug_assert!(block_description.version == 1);
let first_frame_offset =
unsafe { block_description.hdr.bh1.offset_to_first_pkt as usize };
let frames = &mut block_slice[first_frame_offset..];
blocks.push(PacketTxBlock {
description: block_description,
frames,
frame_size,
});
}
Self {
ring_start,
blocks,
block_size,
}
}
#[inline]
pub(crate) unsafe fn mapped_start(&mut self) -> *mut u8 {
self.ring_start
}
#[inline]
pub(crate) fn mapped_size(&self) -> usize {
self.block_size * self.blocks.len()
}
#[inline]
pub fn blocks(&mut self) -> &mut [PacketTxBlock] {
&mut self.blocks
}
#[inline]
pub(crate) fn blocks_cnt(&self) -> usize {
self.blocks.len()
}
pub(crate) fn next_frame(&mut self, mut index: FrameIndex) -> (TxFrameVariant<'_>, FrameIndex) {
let frame_offset = match index.frame_offset {
Some(o) => o,
None => {
index.blocks_index = (index.blocks_index + 1) % self.blocks.len();
let block = &mut self.blocks[index.blocks_index];
block.block_header().offset_to_first_pkt as usize
}
};
let block = &mut self.blocks[index.blocks_index];
let (frame, new_offset) =
PacketTxFrameIter::next_with_offset(block.frames, block.frame_size, frame_offset);
index.frame_offset = new_offset;
(frame, index)
}
}
pub struct PacketTxBlock {
description: &'static mut crate::linux::tpacket_block_desc,
frames: &'static mut [u8],
frame_size: usize,
}
impl PacketTxBlock {
#[inline]
fn block_header(&self) -> &crate::linux::tpacket_hdr_v1 {
unsafe { &self.description.hdr.bh1 }
}
#[inline]
pub fn seq(&self) -> u64 {
self.block_header().seq_num
}
#[inline]
pub fn packet_cnt(&self) -> usize {
self.block_header().num_pkts as usize
}
#[inline]
pub fn packets(&mut self) -> PacketTxFrameIter<'_> {
let first_offset = self.block_header().offset_to_first_pkt as usize;
PacketTxFrameIter {
frames: self.frames,
frame_size: self.frame_size,
curr_offset: Some(first_offset),
}
}
}
pub struct PacketTxFrameIter<'a> {
frames: &'a mut [u8],
frame_size: usize,
curr_offset: Option<usize>,
}
impl<'a> PacketTxFrameIter<'a> {
#[inline]
pub fn next_frame(&'a mut self) -> Option<TxFrameVariant<'a>> {
let (frame, new_offset) =
Self::next_with_offset(self.frames, self.frame_size, self.curr_offset?);
if let TxFrameVariant::Available(_) = frame {
self.curr_offset = new_offset;
}
Some(frame)
}
pub fn has_remaining_frames(&self) -> bool {
self.curr_offset.is_some()
}
fn next_with_offset(
frames: &'a mut [u8],
frame_size: usize,
offset: usize,
) -> (TxFrameVariant<'a>, Option<usize>) {
let frames_len = frames.len();
let curr_frame_data = &mut frames[offset..];
let (header_data, rem) = curr_frame_data
.split_at_mut(tpacket_align(mem::size_of::<crate::linux::tpacket3_hdr>()));
let header = unsafe {
(header_data.as_mut_ptr() as *mut crate::linux::tpacket3_hdr)
.as_mut()
.unwrap()
};
let frame_end = offset + frame_size;
let next_offset = if frame_end < frames_len {
Some(frame_end)
} else {
None
};
let packet = &mut rem[..frame_end];
let frame_variant = match header.tp_status {
crate::linux::TP_STATUS_AVAILABLE => {
header.tp_next_offset = 0;
header.tp_len = 0;
header.tp_snaplen = 0;
TxFrameVariant::Available(TxFrame { header, packet })
}
crate::linux::TP_STATUS_SEND_REQUEST => TxFrameVariant::SendRequest,
crate::linux::TP_STATUS_SENDING => TxFrameVariant::Sending,
crate::linux::TP_STATUS_WRONG_FORMAT => {
TxFrameVariant::WrongFormat(InvalidTxFrame { header, packet })
}
_ => TxFrameVariant::WrongFormat(InvalidTxFrame { header, packet }),
};
(frame_variant, next_offset)
}
}
pub enum TxFrameVariant<'a> {
Available(TxFrame<'a>),
SendRequest,
Sending,
WrongFormat(InvalidTxFrame<'a>),
}
pub struct TxFrame<'a> {
header: &'a mut crate::linux::tpacket3_hdr,
packet: &'a mut [u8],
}
impl TxFrame<'_> {
#[inline]
pub fn data(&mut self) -> &mut [u8] {
self.packet
}
#[inline]
pub fn send(self, packet_length: u32) {
assert!(packet_length as usize <= self.packet.len());
self.header.tp_len = packet_length;
self.header.tp_snaplen = packet_length;
self.header.tp_status = crate::linux::TP_STATUS_SEND_REQUEST;
}
}
pub struct InvalidTxFrame<'a> {
header: &'a mut crate::linux::tpacket3_hdr,
packet: &'a mut [u8],
}
impl InvalidTxFrame<'_> {
#[inline]
pub fn data(&self) -> &[u8] {
self.packet
}
}
impl Drop for InvalidTxFrame<'_> {
fn drop(&mut self) {
self.header.tp_len = 0;
self.header.tp_snaplen = 0;
self.header.tp_status = crate::linux::TP_STATUS_AVAILABLE;
}
}
#[derive(Clone, Copy, PartialEq, Eq)]
pub enum PacketRxStatus {
Kernel,
User,
}
pub struct PacketRxRing {
ring_start: *mut u8,
block_size: usize,
blocks: Vec<PacketRxBlock>,
}
impl PacketRxRing {
pub(crate) unsafe fn new(
ring_start: *mut u8,
config: BlockConfig,
priv_size: usize,
osi_layer: OsiLayer,
) -> Self {
let frame_size = config.frame_size as usize;
let block_cnt = config.block_cnt as usize;
let block_size = config.block_size as usize;
debug_assert!(frame_size >= mem::size_of::<crate::linux::tpacket_block_desc>());
let mut blocks = Vec::new();
for i in 0..block_cnt {
let block_slice = slice::from_raw_parts_mut(ring_start.add(block_size * i), block_size);
let (description_bytes, block_slice) =
block_slice.split_at_mut(mem::size_of::<crate::linux::tpacket_block_desc>());
let block_description = unsafe {
(description_bytes.as_mut_ptr() as *mut crate::linux::tpacket_block_desc)
.as_mut()
.unwrap()
};
debug_assert!(block_description.version == 1);
let priv_offset = block_description.offset_to_priv as usize
- mem::size_of::<crate::linux::tpacket_block_desc>();
let first_frame_offset =
unsafe { block_description.hdr.bh1.offset_to_first_pkt as usize };
let (uninit_priv_data, frames) = block_slice.split_at_mut(first_frame_offset);
let priv_data = &mut uninit_priv_data[priv_offset..priv_offset + priv_size];
blocks.push(PacketRxBlock {
description: block_description,
frames,
priv_data,
osi_layer,
});
}
Self {
ring_start,
blocks,
block_size,
}
}
#[inline]
pub(crate) unsafe fn mapped_start(&mut self) -> *mut u8 {
self.ring_start
}
#[inline]
pub(crate) fn mapped_size(&self) -> usize {
self.block_size * self.blocks.len()
}
#[inline]
pub fn blocks(&mut self) -> &mut [PacketRxBlock] {
&mut self.blocks
}
#[inline]
pub(crate) fn blocks_cnt(&self) -> usize {
self.blocks.len()
}
pub(crate) fn next_frame(
&mut self,
mut index: FrameIndex,
) -> Option<(RxFrame<'_>, FrameIndex)> {
let frame_offset = match index.frame_offset {
Some(o) => o,
None => {
let block_index = (index.blocks_index + 1) % self.blocks.len();
let block = &mut self.blocks[index.blocks_index];
if (block.block_header().block_status & crate::linux::TP_STATUS_USER) == 0 {
return None; }
let frame_offset = block.block_header().offset_to_first_pkt as usize;
index.blocks_index = block_index;
index.frame_offset = Some(frame_offset);
frame_offset
}
};
let block = &mut self.blocks[index.blocks_index];
let (frame, new_offset) = PacketRxFrameIter::next_with_offset(
block.frames,
block.packet_cnt(),
block.block_header().offset_to_first_pkt as usize,
frame_offset,
block.osi_layer,
)?;
let next_index = FrameIndex {
blocks_index: index.blocks_index,
frame_offset: new_offset,
};
Some((frame, next_index))
}
}
pub struct PacketRxBlock {
description: &'static mut crate::linux::tpacket_block_desc,
priv_data: &'static mut [u8],
frames: &'static mut [u8],
osi_layer: OsiLayer,
}
impl PacketRxBlock {
#[inline]
fn block_header(&self) -> &crate::linux::tpacket_hdr_v1 {
unsafe { &self.description.hdr.bh1 }
}
#[inline]
pub fn seq(&self) -> u64 {
self.block_header().seq_num
}
#[inline]
pub fn status(&self) -> PacketRxStatus {
if (self.block_header().block_status & crate::linux::TP_STATUS_USER) != 0 {
PacketRxStatus::User
} else {
PacketRxStatus::Kernel
}
}
#[inline]
pub fn packet_cnt(&self) -> usize {
self.block_header().num_pkts as usize
}
pub fn packets(&mut self) -> PacketRxFrameIter<'_> {
let remainder = self.block_header().num_pkts as usize;
let init_offset = self.block_header().offset_to_first_pkt as usize;
PacketRxFrameIter {
frames: self.frames,
frame_cnt: remainder,
init_offset,
curr_offset: Some(init_offset),
pkt_layer: self.osi_layer,
}
}
#[inline]
pub fn private_data(&mut self) -> &mut [u8] {
self.priv_data
}
}
pub struct PacketRxFrameIter<'a> {
frames: &'a mut [u8],
frame_cnt: usize,
init_offset: usize,
curr_offset: Option<usize>,
pkt_layer: OsiLayer,
}
impl<'a> PacketRxFrameIter<'a> {
pub fn next_frame(&'a mut self) -> Option<RxFrame<'a>> {
let (frame, new_offset) = Self::next_with_offset(
self.frames,
self.frame_cnt,
self.init_offset,
self.curr_offset?,
self.pkt_layer,
)?;
self.frame_cnt -= 1;
self.curr_offset = new_offset;
Some(frame)
}
fn next_with_offset(
frames: &'a mut [u8],
frame_cnt: usize,
init_offset: usize,
curr_offset: usize,
pkt_layer: OsiLayer,
) -> Option<(RxFrame<'a>, Option<usize>)> {
if frame_cnt == 0 {
return None;
}
let full_offset = init_offset
+ curr_offset
+ tpacket_align(mem::size_of::<crate::linux::tpacket3_hdr>())
+ mem::size_of::<libc::sockaddr_ll>();
let curr_frame_data = &mut frames[curr_offset..];
let (header_data, rem) = curr_frame_data
.split_at_mut(tpacket_align(mem::size_of::<crate::linux::tpacket3_hdr>()));
let header = unsafe {
(header_data.as_mut_ptr() as *mut crate::linux::tpacket3_hdr)
.as_mut()
.unwrap()
};
let (sockaddr_data, rem) = rem.split_at_mut(mem::size_of::<libc::sockaddr_ll>());
let sockaddr = unsafe {
(sockaddr_data.as_mut_ptr() as *mut libc::sockaddr_ll)
.as_mut()
.unwrap()
};
let pkt_offset = match pkt_layer {
OsiLayer::L2 => header.tp_mac as usize - full_offset,
OsiLayer::L3 => header.tp_net as usize - full_offset,
};
let (padding, rem) = rem.split_at_mut(pkt_offset);
let packet = &mut rem[..header.tp_len as usize];
let new_offset = if (header.tp_next_offset as usize) < full_offset {
None
} else {
Some(header.tp_next_offset as usize)
};
Some((
RxFrame {
header,
sockaddr,
padding,
packet,
},
new_offset,
))
}
}
pub struct RxFrame<'a> {
header: &'a mut crate::linux::tpacket3_hdr,
sockaddr: &'a mut libc::sockaddr_ll,
padding: &'a mut [u8],
packet: &'a mut [u8],
}
impl RxFrame<'_> {
#[inline]
pub fn data(&self) -> &[u8] {
self.packet
}
#[inline]
pub fn data_mut(&mut self) -> &mut [u8] {
self.packet
}
#[inline]
pub fn padding(&mut self) -> &mut [u8] {
self.padding
}
#[inline]
pub fn timestamp(&self) -> SystemTime {
UNIX_EPOCH + Duration::new(self.header.tp_sec as u64, self.header.tp_nsec)
}
#[inline]
pub fn sockaddr_ll(&self) -> libc::sockaddr_ll {
*self.sockaddr
}
#[inline]
pub fn is_truncated(&self) -> bool {
self.header.tp_len != self.header.tp_snaplen
}
#[inline]
pub fn snaplen(&self) -> usize {
self.header.tp_snaplen as usize
}
#[inline]
pub fn is_copied(&self) -> bool {
(self.header.tp_status & crate::linux::TP_STATUS_COPY) != 0
}
#[inline]
pub fn dropped_packets(&self) -> bool {
(self.header.tp_status & crate::linux::TP_STATUS_LOSING) != 0
}
#[inline]
pub fn offloaded_checksum(&self) -> bool {
(self.header.tp_status & crate::linux::TP_STATUS_CSUMNOTREADY) != 0
}
#[inline]
pub fn checksum_valid(&self) -> bool {
(self.header.tp_status & crate::linux::TP_STATUS_CSUM_VALID) != 0
}
#[inline]
pub fn vlan_tci(&self) -> Option<u32> {
if (self.header.tp_status & crate::linux::TP_STATUS_VLAN_VALID) != 0 {
Some(self.header.hv1.tp_vlan_tci)
} else {
None
}
}
#[inline]
pub fn vlan_tpid(&self) -> Option<u16> {
if (self.header.tp_status & crate::linux::TP_STATUS_VLAN_TPID_VALID) != 0 {
Some(self.header.hv1.tp_vlan_tpid)
} else {
None
}
}
#[inline]
pub fn rx_hash(&self) -> u32 {
self.header.hv1.tp_rxhash
}
}
const fn tpacket_align(len: usize) -> usize {
(len + crate::linux::TPACKET_ALIGNMENT - 1) & !(crate::linux::TPACKET_ALIGNMENT - 1)
}