use std;
use std::io::{self, Error};
use std::mem;
use libc::{bind, c_int, c_void, getpid, mmap, poll, pollfd, sockaddr, sockaddr_ll, socklen_t,
AF_PACKET, ETH_ALEN, ETH_P_IP, MAP_LOCKED, MAP_NORESERVE, MAP_SHARED, POLLERR, POLLIN,
PROT_READ, PROT_WRITE};
use socket::{self, Socket, IFF_PROMISC};
use tpacket3;
const PACKET_RX_RING: c_int = 5;
const PACKET_STATISTICS: c_int = 6;
const PACKET_VERSION: c_int = 10;
const PACKET_FANOUT: c_int = 18;
pub const PACKET_FANOUT_HASH: c_int = 0;
pub const PACKET_FANOUT_LB: c_int = 1;
const PACKET_HOST: u8 = 0;
const PACKET_BROADCAST: u8 = 1;
const PACKET_MULTICAST: u8 = 2;
const PACKET_OTHERHOST: u8 = 3;
const PACKET_OUTGOING: u8 = 4;
#[derive(Clone, Debug)]
pub struct RingSettings {
pub if_name: String,
pub fanout_method: c_int,
pub ring_settings: tpacket3::TpacketReq3,
}
impl Default for RingSettings {
fn default() -> RingSettings {
RingSettings {
if_name: String::from("eth0"),
fanout_method: PACKET_FANOUT_HASH,
ring_settings: tpacket3::TpacketReq3::default(),
}
}
}
#[derive(Clone, Debug)]
pub struct Ring {
pub socket: Socket,
mmap: Option<*mut u8>,
opts: tpacket3::TpacketReq3,
}
#[derive(Debug)]
pub struct Block<'a> {
block_desc: tpacket3::TpacketBlockDesc,
packets: Vec<RawPacket<'a>>,
raw_data: &'a mut [u8],
}
#[derive(Debug)]
pub struct RawPacket<'a> {
pub tpacket3_hdr: tpacket3::Tpacket3Hdr,
pub data: &'a [u8],
}
impl<'a> Block<'a> {
#[inline]
pub fn mark_as_consumed(&mut self) {
self.raw_data[tpacket3::TP_BLK_STATUS_OFFSET] = tpacket3::TP_STATUS_KERNEL;
self.raw_data[tpacket3::TP_BLK_STATUS_OFFSET + 1] = 0;
self.raw_data[tpacket3::TP_BLK_STATUS_OFFSET + 2] = 0;
self.raw_data[tpacket3::TP_BLK_STATUS_OFFSET + 3] = 0;
}
#[inline]
fn is_ready(&self) -> bool {
(self.raw_data[tpacket3::TP_BLK_STATUS_OFFSET] & tpacket3::TP_STATUS_USER) != 0
}
#[inline]
pub fn get_raw_packets(&self) -> Vec<RawPacket> {
let mut packets = Vec::<RawPacket>::new();
let mut next_offset = 48;
let count = self.block_desc.hdr.num_pkts;
for x in 0..count {
let this_offset = next_offset;
let mut tpacket3_hdr = match tpacket3::get_tpacket3_hdr(&self.raw_data[next_offset..]) {
Ok(x) => x,
Err(_) => {
continue;
}
};
if x < count - 1 {
next_offset = this_offset + tpacket3_hdr.1.tp_next_offset as usize;
} else {
next_offset = self.raw_data.len();
tpacket3_hdr.1.tp_next_offset = 0;
}
packets.push(RawPacket {
tpacket3_hdr: tpacket3_hdr.1,
data: &self.raw_data[this_offset..next_offset],
});
}
packets
}
}
impl Ring {
pub fn from_if_name(if_name: &str) -> io::Result<Ring> {
let mut ring = Ring {
socket: Socket::from_if_name(if_name, socket::PF_PACKET)?,
mmap: None,
opts: tpacket3::TpacketReq3::default(),
};
ring.socket.set_flag(IFF_PROMISC as u64)?;
ring.socket.setsockopt(
PACKET_VERSION,
tpacket3::TPACKET_V3,
)?;
ring.socket
.setsockopt(PACKET_RX_RING, ring.opts.clone())?;
ring.mmap_rx_ring()?;
ring.bind_rx_ring()?;
let fanout = (unsafe { getpid() } & 0xFFFF) | (PACKET_FANOUT_HASH << 16);
ring.socket
.setsockopt(PACKET_FANOUT, fanout)?;
Ok(ring)
}
pub fn new(settings: RingSettings) -> io::Result<Ring> {
let mut ring = Ring {
socket: Socket::from_if_name(&settings.if_name, socket::PF_PACKET)?,
mmap: None,
opts: settings.ring_settings,
};
ring.socket.set_flag(IFF_PROMISC as u64)?;
ring.socket.setsockopt(
PACKET_VERSION,
tpacket3::TPACKET_V3)?;
ring.socket
.setsockopt(PACKET_RX_RING, ring.opts.clone())?;
ring.mmap_rx_ring()?;
ring.bind_rx_ring()?;
let fanout = (unsafe { getpid() } & 0xFFFF) | (settings.fanout_method << 16);
ring.socket
.setsockopt(PACKET_FANOUT, fanout)?;
Ok(ring)
}
#[inline]
pub fn get_block(&mut self) -> Block {
loop {
self.wait_for_block();
for i in 0..self.opts.tp_block_nr {
if let Some(mut block) = self.get_single_block(i) {
if block.is_ready() {
return block;
}
}
}
}
}
fn mmap_rx_ring(&mut self) -> io::Result<()> {
match unsafe {
mmap(
std::ptr::null_mut(),
(self.opts.tp_block_size * self.opts.tp_block_nr) as usize,
PROT_READ | PROT_WRITE,
MAP_SHARED | MAP_LOCKED | MAP_NORESERVE,
self.socket.fd,
0,
)
} as isize
{
-1 => Err(io::Error::last_os_error()),
map => {
self.mmap = Some(map as *mut u8);
Ok(())
}
}
}
fn bind_rx_ring(&mut self) -> io::Result<()> {
let mut sa = sockaddr_ll {
sll_family: AF_PACKET as u16,
sll_protocol: ETH_P_IP.to_be() as u16,
sll_ifindex: self.socket.if_index as c_int,
sll_hatype: 519,
sll_pkttype: (PACKET_HOST | PACKET_BROADCAST | PACKET_MULTICAST | PACKET_OTHERHOST
| PACKET_OUTGOING),
sll_halen: ETH_ALEN as u8,
sll_addr: [0; 8],
};
let size = mem::size_of_val(&sa);
let addr_ptr = unsafe { mem::transmute::<*mut sockaddr_ll, *mut sockaddr>(&mut sa) };
match unsafe { bind(self.socket.fd, addr_ptr, size as socklen_t) } {
0 => Ok(()),
_ => Err(io::Error::last_os_error()),
}
}
#[inline]
fn wait_for_block(&self) {
let mut pfd = pollfd {
fd: self.socket.fd,
events: POLLIN | POLLERR,
revents: 0,
};
unsafe {
poll(&mut pfd, 1, -1);
}
}
#[inline]
fn get_single_block<'a>(&mut self, count: u32) -> Option<Block<'a>> {
let offset = count as isize * self.opts.tp_block_size as isize;
let block = unsafe {
std::slice::from_raw_parts_mut(
self.mmap?.offset(offset),
self.opts.tp_block_size as usize,
)
};
let block_desc = match tpacket3::get_tpacket_block_desc(&block[..]) {
Ok(x) => x,
Err(_) => {
return None;
}
};
let blk = Block {
block_desc: block_desc.1,
packets: Vec::new(),
raw_data: &mut block[..],
};
Some(blk)
}
}
unsafe impl Send for Ring {}
#[inline]
pub fn get_rx_statistics(fd: i32) -> Result<tpacket3::TpacketStatsV3, Error> {
let mut optval = tpacket3::TpacketStatsV3 {
tp_packets: 0,
tp_drops: 0,
tp_freeze_q_cnt: 0,
};
socket::get_sock_opt(
fd,
PACKET_STATISTICS,
&(&mut optval as *mut _ as *mut c_void),
)?;
Ok(optval)
}