diff --git a/libbpf-rs/src/lib.rs b/libbpf-rs/src/lib.rs index 3d96043e5..50a8e89cb 100644 --- a/libbpf-rs/src/lib.rs +++ b/libbpf-rs/src/lib.rs @@ -93,6 +93,7 @@ mod ringbuf; mod skeleton; mod tc; mod util; +mod xdp; pub use libbpf_sys; @@ -142,6 +143,8 @@ pub use crate::tc::TC_H_MIN_EGRESS; pub use crate::tc::TC_H_MIN_INGRESS; pub use crate::tc::TC_INGRESS; pub use crate::util::num_possible_cpus; +pub use crate::xdp::Xdp; +pub use crate::xdp::XdpFlags; /// Used for skeleton -- an end user may not consider this API stable #[doc(hidden)] diff --git a/libbpf-rs/src/xdp.rs b/libbpf-rs/src/xdp.rs new file mode 100644 index 000000000..81188bc0d --- /dev/null +++ b/libbpf-rs/src/xdp.rs @@ -0,0 +1,96 @@ +use bitflags::bitflags; +use std::mem::size_of; +use std::os::unix::io::AsRawFd; +use std::os::unix::io::BorrowedFd; + +use crate::util; +use crate::Error; +use crate::Result; + +bitflags! { + /// Flags to configure the `XDP` operations + pub struct XdpFlags: u32 { + /// See [`libbpf_sys::XDP_FLAGS_UPDATE_IF_NOEXIST`]. + const UPDATE_IF_NOEXIST = libbpf_sys::XDP_FLAGS_UPDATE_IF_NOEXIST as _; + /// See [`libbpf_sys::XDP_FLAGS_SKB_MODE`]. + const SKB_MODE = libbpf_sys::XDP_FLAGS_SKB_MODE as _; + /// See [`libbpf_sys::XDP_FLAGS_DRV_MODE`]. + const DRV_MODE = libbpf_sys::XDP_FLAGS_DRV_MODE as _; + /// See [`libbpf_sys::XDP_FLAGS_HW_MODE`]. + const HW_MODE = libbpf_sys::XDP_FLAGS_HW_MODE as _; + /// See [`libbpf_sys::XDP_FLAGS_REPLACE`]. + const REPLACE = libbpf_sys::XDP_FLAGS_REPLACE as _; + /// See [`libbpf_sys::XDP_FLAGS_MODES`]. + const MODES = libbpf_sys::XDP_FLAGS_MODES as _; + /// See [`libbpf_sys::XDP_FLAGS_MASK`]. + const MASK = libbpf_sys::XDP_FLAGS_MASK as _; + } + +} + +/// Represents a XDP program. +/// +/// This struct exposes operations to attach, detach and query a XDP program +#[derive(Debug)] +pub struct Xdp<'fd> { + fd: BorrowedFd<'fd>, + attach_opts: libbpf_sys::bpf_xdp_attach_opts, + query_opts: libbpf_sys::bpf_xdp_query_opts, +} + +impl<'fd> Xdp<'fd> { + /// Create a new xdp instance with the given file descriptor of the `SEC("xdp")` [`Program`][crate::Program]. + pub fn new(fd: BorrowedFd<'fd>) -> Self { + let mut xdp = Xdp { + fd, + attach_opts: libbpf_sys::bpf_xdp_attach_opts::default(), + query_opts: libbpf_sys::bpf_xdp_query_opts::default(), + }; + xdp.attach_opts.sz = size_of::() as libbpf_sys::size_t; + xdp.query_opts.sz = size_of::() as libbpf_sys::size_t; + xdp + } + + /// Attach the XDP program to the given interface to start processing the packets + pub fn attach(&self, ifindex: i32, flags: XdpFlags) -> Result<()> { + let ret = unsafe { + libbpf_sys::bpf_xdp_attach( + ifindex, + self.fd.as_raw_fd(), + flags.bits(), + &self.attach_opts, + ) + }; + util::parse_ret(ret) + } + + /// Detach the XDP program from the interface + pub fn detach(&self, ifindex: i32, flags: XdpFlags) -> Result<()> { + let ret = unsafe { libbpf_sys::bpf_xdp_detach(ifindex, flags.bits(), &self.attach_opts) }; + util::parse_ret(ret) + } + + /// Query to inspect the program + pub fn query(&self, ifindex: i32, flags: XdpFlags) -> Result { + let mut opts = self.query_opts; + let err = unsafe { libbpf_sys::bpf_xdp_query(ifindex, flags.bits() as i32, &mut opts) }; + if err != 0 { + Err(Error::from_raw_os_error(err)) + } else { + Ok(opts) + } + } + + /// Query to inspect the program identifier (prog_id) + pub fn query_id(&self, ifindex: i32, flags: XdpFlags) -> Result { + let mut prog_id: u32 = 0; + let err = unsafe { + libbpf_sys::bpf_xdp_query_id(ifindex, flags.bits() as i32, (&mut prog_id) as *mut u32) + }; + if err != 0 { + Err(Error::from_raw_os_error(err)) + } else { + Ok(prog_id) + } + } +} diff --git a/libbpf-rs/tests/bin/src/xdp.bpf.c b/libbpf-rs/tests/bin/src/xdp.bpf.c new file mode 100644 index 000000000..3bc730830 --- /dev/null +++ b/libbpf-rs/tests/bin/src/xdp.bpf.c @@ -0,0 +1,7 @@ +#include "vmlinux.h" +#include + +SEC("xdp") +int xdp_filter(struct xdp_md *ctx) { + return XDP_PASS; +} \ No newline at end of file diff --git a/libbpf-rs/tests/bin/xdp.bpf.o b/libbpf-rs/tests/bin/xdp.bpf.o new file mode 100644 index 000000000..64c542a4d Binary files /dev/null and b/libbpf-rs/tests/bin/xdp.bpf.o differ diff --git a/libbpf-rs/tests/test_xdp.rs b/libbpf-rs/tests/test_xdp.rs new file mode 100644 index 000000000..8a9dafe38 --- /dev/null +++ b/libbpf-rs/tests/test_xdp.rs @@ -0,0 +1,40 @@ +mod test; +use std::os::fd::AsFd; +use test::bump_rlimit_mlock; +use test::get_test_object; + +use libbpf_rs::Xdp; +use libbpf_rs::XdpFlags; + +const LO_IFINDEX: i32 = 1; + +#[test] +fn test_sudo_xdp() { + bump_rlimit_mlock(); + + let obj = get_test_object("xdp.bpf.o"); + let fd = obj.prog("xdp_filter").unwrap().as_fd(); + + let xdp_prog = Xdp::new(fd); + + assert!(xdp_prog + .attach(LO_IFINDEX, XdpFlags::UPDATE_IF_NOEXIST) + .is_ok()); + + // Second attach should fail as a prog is already loaded + assert!(xdp_prog + .attach(LO_IFINDEX, XdpFlags::UPDATE_IF_NOEXIST) + .is_err()); + + assert!(xdp_prog + .query_id(LO_IFINDEX, XdpFlags::UPDATE_IF_NOEXIST) + .is_ok()); + + assert!(xdp_prog + .query(LO_IFINDEX, XdpFlags::UPDATE_IF_NOEXIST) + .is_ok()); + + assert!(xdp_prog + .detach(LO_IFINDEX, XdpFlags::UPDATE_IF_NOEXIST) + .is_ok()); +}