use crate::afpacket::ffi;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum FanoutMode {
Hash,
LoadBalance,
Cpu,
Rollover,
Random,
QueueMapping,
Ebpf,
}
impl FanoutMode {
pub(crate) const fn as_raw(self) -> u32 {
match self {
Self::Hash => ffi::PACKET_FANOUT_HASH,
Self::LoadBalance => ffi::PACKET_FANOUT_LB,
Self::Cpu => ffi::PACKET_FANOUT_CPU,
Self::Rollover => ffi::PACKET_FANOUT_ROLLOVER,
Self::Random => ffi::PACKET_FANOUT_RND,
Self::QueueMapping => ffi::PACKET_FANOUT_QM,
Self::Ebpf => ffi::PACKET_FANOUT_EBPF,
}
}
}
bitflags::bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct FanoutFlags: u16 {
const ROLLOVER = 0x1000;
const UNIQUE_ID = 0x2000;
const IGNORE_OUTGOING = 0x4000;
const DEFRAG = 0x8000;
}
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub enum TimestampSource {
#[default]
Software,
RawHardware,
SysHardware,
}
impl TimestampSource {
pub(crate) const fn as_raw(self) -> libc::c_int {
match self {
Self::Software => 0,
Self::RawHardware => 1,
Self::SysHardware => 2,
}
}
}
#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct BpfInsn {
pub code: u16,
pub jt: u8,
pub jf: u8,
pub k: u32,
}
impl From<BpfInsn> for libc::sock_filter {
fn from(insn: BpfInsn) -> Self {
libc::sock_filter {
code: insn.code,
jt: insn.jt,
jf: insn.jf,
k: insn.k,
}
}
}
impl From<libc::sock_filter> for BpfInsn {
fn from(sf: libc::sock_filter) -> Self {
Self {
code: sf.code,
jt: sf.jt,
jf: sf.jf,
k: sf.k,
}
}
}
#[derive(Debug, Clone)]
pub struct BpfFilter {
instructions: Vec<BpfInsn>,
}
impl BpfFilter {
pub fn new(instructions: Vec<BpfInsn>) -> Self {
Self { instructions }
}
pub fn instructions(&self) -> &[BpfInsn] {
&self.instructions
}
pub fn len(&self) -> usize {
self.instructions.len()
}
pub fn is_empty(&self) -> bool {
self.instructions.is_empty()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum RingProfile {
Default,
HighThroughput,
LowLatency,
LowMemory,
JumboFrames,
}
impl RingProfile {
#[inline]
pub(crate) fn params(self) -> (usize, usize, usize, u32) {
match self {
Self::Default => (1 << 22, 64, 2048, 60),
Self::HighThroughput => (1 << 22, 256, 2048, 60),
Self::LowLatency => (1 << 18, 64, 2048, 1),
Self::LowMemory => (1 << 20, 16, 2048, 100),
Self::JumboFrames => (1 << 22, 64, 65536, 60),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn fanout_mode_as_raw() {
assert_eq!(FanoutMode::Hash.as_raw(), 0);
assert_eq!(FanoutMode::LoadBalance.as_raw(), 1);
assert_eq!(FanoutMode::Cpu.as_raw(), 2);
assert_eq!(FanoutMode::Rollover.as_raw(), 3);
assert_eq!(FanoutMode::Random.as_raw(), 4);
assert_eq!(FanoutMode::QueueMapping.as_raw(), 5);
}
#[test]
fn fanout_flags_bitwise() {
let flags = FanoutFlags::ROLLOVER | FanoutFlags::DEFRAG;
assert_eq!(flags.bits(), 0x9000);
assert!(flags.contains(FanoutFlags::ROLLOVER));
assert!(flags.contains(FanoutFlags::DEFRAG));
assert!(!flags.contains(FanoutFlags::UNIQUE_ID));
}
#[test]
fn timestamp_source_default() {
assert_eq!(TimestampSource::default(), TimestampSource::Software);
assert_eq!(TimestampSource::Software.as_raw(), 0);
}
#[test]
fn bpf_insn_matches_sock_filter() {
assert_eq!(
std::mem::size_of::<BpfInsn>(),
std::mem::size_of::<libc::sock_filter>()
);
}
#[test]
fn bpf_insn_roundtrip() {
let insn = BpfInsn {
code: 0x28,
jt: 0,
jf: 0,
k: 12,
};
let sf: libc::sock_filter = insn.into();
let back: BpfInsn = sf.into();
assert_eq!(insn, back);
}
#[test]
fn bpf_filter_accessors() {
let insns = vec![
BpfInsn {
code: 0x28,
jt: 0,
jf: 0,
k: 12,
},
BpfInsn {
code: 0x06,
jt: 0,
jf: 0,
k: 0xFFFF,
},
];
let filter = BpfFilter::new(insns.clone());
assert_eq!(filter.len(), 2);
assert!(!filter.is_empty());
assert_eq!(filter.instructions(), &insns);
}
#[test]
fn ring_profile_params_valid() {
for profile in [
RingProfile::Default,
RingProfile::HighThroughput,
RingProfile::LowLatency,
RingProfile::LowMemory,
RingProfile::JumboFrames,
] {
let (block_size, block_count, frame_size, timeout_ms) = profile.params();
assert!(block_size.is_power_of_two(), "{profile:?} block_size");
assert!(block_size % 4096 == 0, "{profile:?} page-aligned");
assert!(block_count > 0, "{profile:?} block_count");
assert!(
frame_size >= 68,
"{profile:?} frame_size >= TPACKET3_HDRLEN"
);
assert!(frame_size % 16 == 0, "{profile:?} frame_size aligned");
assert!(
frame_size <= block_size,
"{profile:?} frame_size <= block_size"
);
let _ = timeout_ms;
}
}
}