use std::os::fd::{RawFd, BorrowedFd, AsFd, OwnedFd, FromRawFd, AsRawFd};
use std::io::Error;
use libc::*;
pub struct RxRing {
fd: OwnedFd,
ring: *mut u8,
ring_size: usize,
req: tpacket_req,
current: usize
}
fn attach_filter(sockfd: RawFd, filter: &[sock_filter]) -> Result<(), Error> {
let prog = sock_fprog {
len: filter.len() as u16,
filter: filter.as_ptr() as *mut sock_filter,
};
let ret = unsafe {
setsockopt(sockfd, SOL_SOCKET, SO_ATTACH_FILTER,
&prog as *const _ as *const _,
std::mem::size_of::<sock_fprog>() as socklen_t)
};
if ret < 0 {
return Err(Error::last_os_error());
}
Ok(())
}
fn setup_rxring(sockfd: RawFd) -> Result<tpacket_req, Error> {
const BLOCK_SIZE: u32 = 4096 * 4; const BLOCK_NR: u32 = 4;
const FRAME_SIZE: u32 = 256;
let req = tpacket_req {
tp_block_size: BLOCK_SIZE,
tp_block_nr: BLOCK_NR,
tp_frame_size: FRAME_SIZE,
tp_frame_nr: BLOCK_SIZE / FRAME_SIZE * BLOCK_NR,
};
let ret = unsafe {
setsockopt(sockfd, SOL_PACKET, PACKET_RX_RING,
&req as *const _ as *const _,
std::mem::size_of::<tpacket_req>() as socklen_t)
};
if ret < 0 {
return Err(Error::last_os_error());
}
Ok(req)
}
impl RxRing {
pub fn new(filter: &[libc::sock_filter]) -> Result<Self, Error> {
let raw = unsafe {
socket(
AF_PACKET,
SOCK_RAW,
(ETH_P_ALL as u16).to_be() as i32 )
};
if raw < 0 { return Err(Error::last_os_error()); }
let fd = unsafe { OwnedFd::from_raw_fd(raw) };
attach_filter(fd.as_raw_fd(), filter)?;
let req = setup_rxring(fd.as_raw_fd())?;
let ring_size = (req.tp_block_size * req.tp_block_nr) as usize;
let ring = unsafe {
mmap(
std::ptr::null_mut(),
ring_size,
PROT_READ | PROT_WRITE,
MAP_SHARED | MAP_LOCKED,
fd.as_raw_fd(),
0
)
};
if ring == MAP_FAILED { return Err(Error::last_os_error()); }
Ok(RxRing {
fd,
ring: ring as *mut u8,
ring_size,
req,
current: 0,
})
}
fn current_frame(&self) -> *mut tpacket_hdr {
let frame_size = self.req.tp_frame_size as usize;
unsafe { self.ring.add(self.current * frame_size) as *mut tpacket_hdr }
}
pub fn current_packet(&self) -> Option<&[u8]> {
let hdr = unsafe { &*(self.current_frame()) };
if hdr.tp_status & TP_STATUS_USER as u64 == 0 {
return None;
}
let data = unsafe {
let ptr = (hdr as *const tpacket_hdr as *const u8).add(hdr.tp_net as usize);
std::slice::from_raw_parts(ptr, (*hdr).tp_snaplen as usize)
};
Some(data)
}
pub fn advance(&mut self) {
unsafe { (*self.current_frame()).tp_status = TP_STATUS_KERNEL as u64; }
self.current = (self.current + 1) % self.req.tp_frame_nr as usize;
}
}
impl AsFd for RxRing {
fn as_fd(&self) -> BorrowedFd<'_> {
self.fd.as_fd()
}
}
impl AsRawFd for RxRing {
fn as_raw_fd(&self) -> RawFd {
self.fd.as_raw_fd()
}
}
impl Drop for RxRing {
fn drop(&mut self) {
unsafe {
libc::munmap(self.ring as *mut _, self.ring_size);
}
}
}