use std::fmt;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Backend {
Xdp,
Sendmmsg,
Pnet,
}
impl fmt::Display for Backend {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(match self {
Backend::Xdp => "xdp",
Backend::Sendmmsg => "sendmmsg",
Backend::Pnet => "pnet",
})
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ProbeReport {
pub xdp_compiled: bool,
pub sendmmsg_compiled: bool,
pub pnet_compiled: bool,
pub kernel: Option<(u32, u32)>,
pub cap_net_raw: bool,
pub libbpf_present: bool,
pub selected: Backend,
}
impl ProbeReport {
pub fn render_table(&self) -> String {
let yes = "✓";
let no = "✗";
let yn = |b: bool| if b { yes } else { no };
let kernel = self
.kernel
.map(|(maj, min)| format!("{maj}.{min}"))
.unwrap_or_else(|| "n/a".to_string());
format!(
concat!(
"engine probe:\n",
" selected backend: {selected}\n",
" kernel: {kernel}\n",
" CAP_NET_RAW: {cnr}\n",
" libbpf present: {bpf}\n",
" features: xdp={xdp} sendmmsg={smm} pnet={pnet}\n",
),
selected = self.selected,
kernel = kernel,
cnr = yn(self.cap_net_raw),
bpf = yn(self.libbpf_present),
xdp = yn(self.xdp_compiled),
smm = yn(self.sendmmsg_compiled),
pnet = yn(self.pnet_compiled),
)
}
}
fn detect_kernel() -> Option<(u32, u32)> {
if !cfg!(target_os = "linux") {
return None;
}
let raw = std::fs::read_to_string("/proc/sys/kernel/osrelease").ok()?;
let head = raw.trim().split('-').next()?;
let mut parts = head.split('.');
let maj: u32 = parts.next()?.parse().ok()?;
let min: u32 = parts.next()?.parse().ok()?;
Some((maj, min))
}
fn detect_cap_net_raw() -> bool {
#[cfg(target_os = "linux")]
unsafe {
libc::geteuid() == 0
}
#[cfg(not(target_os = "linux"))]
{
false
}
}
fn detect_libbpf() -> bool {
for path in [
"/usr/lib/x86_64-linux-gnu/libbpf.so.1",
"/usr/lib/x86_64-linux-gnu/libbpf.so.0",
"/usr/lib64/libbpf.so.1",
"/usr/lib64/libbpf.so.0",
] {
if std::path::Path::new(path).exists() {
return true;
}
}
false
}
pub fn probe() -> ProbeReport {
let xdp_compiled = cfg!(feature = "xdp");
let sendmmsg_compiled = cfg!(target_os = "linux");
let pnet_compiled = true;
let kernel = detect_kernel();
let cap_net_raw = detect_cap_net_raw();
let libbpf_present = detect_libbpf();
let xdp_runnable = xdp_compiled
&& cap_net_raw
&& libbpf_present
&& kernel.map_or(false, |(maj, min)| maj > 5 || (maj == 5 && min >= 10));
let sendmmsg_runnable = sendmmsg_compiled && cap_net_raw;
let selected = if xdp_runnable {
Backend::Xdp
} else if sendmmsg_runnable {
Backend::Sendmmsg
} else {
Backend::Pnet
};
ProbeReport {
xdp_compiled,
sendmmsg_compiled,
pnet_compiled,
kernel,
cap_net_raw,
libbpf_present,
selected,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn probe_does_not_panic() {
let _ = probe();
}
#[test]
fn render_table_contains_all_rows() {
let r = probe();
let s = r.render_table();
for needle in [
"selected backend",
"kernel",
"CAP_NET_RAW",
"libbpf present",
"features",
] {
assert!(s.contains(needle), "table missing row `{needle}`: {s}");
}
}
#[test]
fn unprivileged_never_picks_xdp() {
let r = probe();
if !r.cap_net_raw {
assert_ne!(r.selected, Backend::Xdp);
}
}
#[test]
fn pnet_is_the_universal_fallback() {
let r = probe();
if !r.cap_net_raw {
assert_eq!(r.selected, Backend::Pnet);
}
}
#[test]
fn selected_backend_displays_lowercase() {
assert_eq!(format!("{}", Backend::Xdp), "xdp");
assert_eq!(format!("{}", Backend::Sendmmsg), "sendmmsg");
assert_eq!(format!("{}", Backend::Pnet), "pnet");
}
#[test]
fn kernel_parsing_handles_release_strings() {
let r = probe();
if cfg!(target_os = "linux") {
assert!(r.kernel.is_some(), "kernel must parse on linux");
} else {
assert!(r.kernel.is_none(), "kernel must be None off linux");
}
}
}