#![allow(unsafe_code)]
#![deny(missing_debug_implementations)]
#![warn(missing_docs)]
use crate::error::AttachError;
use std::os::fd::{AsFd, AsRawFd};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum XdpAttachMode {
Auto,
Drv,
Skb,
Hw,
}
impl XdpAttachMode {
#[inline]
fn to_flags(self) -> u32 {
match self {
XdpAttachMode::Auto => 0,
XdpAttachMode::Drv => libbpf_sys::XDP_FLAGS_DRV_MODE,
XdpAttachMode::Skb => libbpf_sys::XDP_FLAGS_SKB_MODE,
XdpAttachMode::Hw => libbpf_sys::XDP_FLAGS_HW_MODE,
}
}
pub fn as_str(self) -> &'static str {
match self {
XdpAttachMode::Auto => "auto",
XdpAttachMode::Drv => "drv",
XdpAttachMode::Skb => "skb",
XdpAttachMode::Hw => "hw",
}
}
fn fallback_chain() -> &'static [XdpAttachMode] {
&[
XdpAttachMode::Hw, XdpAttachMode::Drv, XdpAttachMode::Skb, ]
}
}
pub fn query_xdp_mode(ifindex: i32) -> Result<Option<XdpAttachMode>, AttachError> {
for mode in XdpAttachMode::fallback_chain() {
let mut prog_id: u32 = 0;
let ret = unsafe {
libbpf_sys::bpf_xdp_query_id(
ifindex,
mode.to_flags() as i32,
&mut prog_id as *mut u32,
)
};
if ret == 0 && prog_id != 0 {
return Ok(Some(*mode));
}
}
Ok(None)
}
pub(crate) fn attach_xdp_raw(
ifindex: i32,
prog_fd: i32,
mode: XdpAttachMode,
) -> Result<XdpAttachMode, AttachError> {
let modes_to_try: Vec<XdpAttachMode> = if mode == XdpAttachMode::Auto {
XdpAttachMode::fallback_chain().to_vec()
} else {
vec![mode]
};
let mut last_err = String::new();
for &m in &modes_to_try {
let flags = m.to_flags();
let ret = unsafe {
libbpf_sys::bpf_xdp_attach(
ifindex,
prog_fd,
flags,
std::ptr::null(),
)
};
if ret == 0 {
return Ok(m);
}
let err = std::io::Error::last_os_error();
last_err = format!("{} 模式失败: {} (errno={})", m.as_str(), err, err.raw_os_error().unwrap_or(0));
}
Err(AttachError::Libbpf(format!(
"所有 XDP attach 模式均失败。最后错误: {}",
last_err
)))
}
pub(crate) fn detach_xdp_raw(ifindex: i32, mode: XdpAttachMode) -> Result<(), AttachError> {
let flags = mode.to_flags();
let ret = unsafe {
libbpf_sys::bpf_xdp_detach(
ifindex,
flags,
std::ptr::null(),
)
};
if ret == 0 {
Ok(())
} else {
let err = std::io::Error::last_os_error();
Err(AttachError::Libbpf(format!(
"detach 失败 (mode={}): {} (errno={})",
mode.as_str(),
err,
err.raw_os_error().unwrap_or(0)
)))
}
}
pub(crate) fn get_prog_fd(
obj: &mut libbpf_rs::Object,
prog_name: &str,
) -> Result<i32, AttachError> {
if prog_name.is_empty()
|| !prog_name
.chars()
.all(|c| c.is_ascii_alphanumeric() || c == '_')
{
return Err(AttachError::Libbpf(format!(
"无效的程序名称(仅允许 [A-Za-z0-9_]):{}",
prog_name
)));
}
let prog = obj
.progs_mut()
.find(|p| p.name().to_str().unwrap_or("") == prog_name)
.ok_or_else(|| {
AttachError::Libbpf(format!("程序 {} 未找到", prog_name))
})?;
Ok(prog.as_fd().as_raw_fd())
}
pub(crate) fn unpin(pin_path: &std::path::Path) {
let _ = std::fs::remove_file(pin_path);
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_xdp_attach_mode_flags() {
assert_eq!(XdpAttachMode::Drv.to_flags(), libbpf_sys::XDP_FLAGS_DRV_MODE);
assert_eq!(XdpAttachMode::Skb.to_flags(), libbpf_sys::XDP_FLAGS_SKB_MODE);
assert_eq!(XdpAttachMode::Hw.to_flags(), libbpf_sys::XDP_FLAGS_HW_MODE);
assert_eq!(XdpAttachMode::Auto.to_flags(), 0);
}
#[test]
fn test_xdp_attach_mode_as_str() {
assert_eq!(XdpAttachMode::Auto.as_str(), "auto");
assert_eq!(XdpAttachMode::Drv.as_str(), "drv");
assert_eq!(XdpAttachMode::Skb.as_str(), "skb");
assert_eq!(XdpAttachMode::Hw.as_str(), "hw");
}
#[test]
fn test_fallback_chain_order() {
let chain = XdpAttachMode::fallback_chain();
assert_eq!(chain[0], XdpAttachMode::Hw);
assert_eq!(chain[1], XdpAttachMode::Drv);
assert_eq!(chain[2], XdpAttachMode::Skb);
}
}