use std::os::fd::RawFd;
use aya::{
Ebpf,
maps::XskMap,
programs::{Xdp, XdpFlags},
};
static XDP_PROG: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/dns_xdp.o"));
pub struct XdpHandle {
bpf: Ebpf,
}
impl XdpHandle {
pub fn load(iface: &str) -> Result<Self, String> {
let words = (XDP_PROG.len() + 7) / 8;
let mut storage: Vec<u64> = vec![0u64; words];
unsafe {
std::ptr::copy_nonoverlapping(
XDP_PROG.as_ptr(),
storage.as_mut_ptr() as *mut u8,
XDP_PROG.len(),
);
}
let aligned = unsafe {
std::slice::from_raw_parts(storage.as_ptr() as *const u8, XDP_PROG.len())
};
let mut bpf = Ebpf::load(aligned)
.map_err(|e| format!("BPF ELF load failed: {e}"))?;
let program: &mut Xdp = bpf
.program_mut("dns_xdp")
.ok_or_else(|| "dns_xdp program section not found in ELF".to_string())?
.try_into()
.map_err(|e| format!("program type mismatch: {e}"))?;
program.load()
.map_err(|e| format!("XDP prog load failed: {e}"))?;
let attach = program
.attach(iface, XdpFlags::DRV_MODE)
.or_else(|_| program.attach(iface, XdpFlags::SKB_MODE))
.map_err(|e| format!("XDP attach to {iface} failed: {e}"))?;
tracing::info!(iface = %iface, link_id = ?attach, "XDP program attached");
Ok(XdpHandle { bpf })
}
pub fn register_socket(&mut self, queue_id: u32, sock_fd: RawFd) -> Result<(), String> {
let map = self.bpf
.map_mut("XSKS")
.ok_or_else(|| "XSKS map not found in BPF object".to_string())?;
let mut xsk_map = XskMap::try_from(map)
.map_err(|e| format!("XSKS is not an XskMap: {e}"))?;
xsk_map
.set(queue_id, sock_fd, 0)
.map_err(|e| format!("XskMap::set q={queue_id} fd={sock_fd}: {e}"))
}
}