From 4c974d33a3b5728b49c82183666153aa20e14b30 Mon Sep 17 00:00:00 2001 From: Tamir Duberstein Date: Tue, 14 Oct 2025 11:12:34 -0400 Subject: [PATCH] xdp: replace kernel version check with fallback --- aya/src/programs/xdp.rs | 85 ++++++++++++++++++++--------------------- 1 file changed, 41 insertions(+), 44 deletions(-) diff --git a/aya/src/programs/xdp.rs b/aya/src/programs/xdp.rs index 55ada66e..3e95d639 100644 --- a/aya/src/programs/xdp.rs +++ b/aya/src/programs/xdp.rs @@ -14,7 +14,6 @@ use aya_obj::{ }, programs::XdpAttachType, }; -use libc::if_nametoindex; use thiserror::Error; use crate::{ @@ -102,14 +101,12 @@ impl Xdp { /// If the given `interface` does not exist /// [`ProgramError::UnknownInterface`] is returned. /// - /// When attaching fails, [`ProgramError::SyscallError`] is returned for - /// kernels `>= 5.9.0`, and instead - /// [`XdpError::NetlinkError`] is returned for older - /// kernels. + /// When `bpf_link_create` is unavailable or rejects the request, the call + /// transparently falls back to the legacy netlink-based attach path. pub fn attach(&mut self, interface: &str, flags: XdpFlags) -> Result { // TODO: avoid this unwrap by adding a new error variant. let c_interface = CString::new(interface).unwrap(); - let if_index = unsafe { if_nametoindex(c_interface.as_ptr()) }; + let if_index = unsafe { libc::if_nametoindex(c_interface.as_ptr()) }; if if_index == 0 { return Err(ProgramError::UnknownInterface { name: interface.to_string(), @@ -124,10 +121,8 @@ impl Xdp { /// /// # Errors /// - /// When attaching fails, [`ProgramError::SyscallError`] is returned for - /// kernels `>= 5.9.0`, and instead - /// [`XdpError::NetlinkError`] is returned for older - /// kernels. + /// When `bpf_link_create` is unavailable or rejects the request, the call + /// transparently falls back to the legacy netlink-based attach path. pub fn attach_to_if_index( &mut self, if_index: u32, @@ -136,43 +131,45 @@ impl Xdp { let prog_fd = self.fd()?; let prog_fd = prog_fd.as_fd(); - if KernelVersion::at_least(5, 9, 0) { - // Unwrap safety: the function starts with `self.fd()?` that will succeed if and only - // if the program has been loaded, i.e. there is an fd. We get one by: - // - Using `Xdp::from_pin` that sets `expected_attach_type` - // - Calling `Xdp::attach` that sets `expected_attach_type`, as geting an `Xdp` - // instance through `Xdp:try_from(Program)` does not set any fd. - // So, in all cases where we have an fd, we have an expected_attach_type. Thus, if we - // reach this point, expected_attach_type is guaranteed to be Some(_). - let attach_type = self.data.expected_attach_type.unwrap(); - let link_fd = bpf_link_create( - prog_fd, - LinkTarget::IfIndex(if_index), - attach_type, - flags.bits(), - None, - ) - .map_err(|io_error| SyscallError { - call: "bpf_link_create", - io_error, - })?; - self.data - .links - .insert(XdpLink::new(XdpLinkInner::Fd(FdLink::new(link_fd)))) - } else { - let if_index = if_index as i32; - unsafe { netlink_set_xdp_fd(if_index, Some(prog_fd), None, flags.bits()) } - .map_err(XdpError::NetlinkError)?; - - let prog_fd = prog_fd.as_raw_fd(); - self.data - .links - .insert(XdpLink::new(XdpLinkInner::NlLink(NlLink { + // Unwrap safety: the function starts with `self.fd()?` that will succeed if and only + // if the program has been loaded, i.e. there is an fd. We get one by: + // - Using `Xdp::from_pin` that sets `expected_attach_type` + // - Calling `Xdp::attach` that sets `expected_attach_type`, as geting an `Xdp` + // instance through `Xdp:try_from(Program)` does not set any fd. + // So, in all cases where we have an fd, we have an expected_attach_type. Thus, if we + // reach this point, expected_attach_type is guaranteed to be Some(_). + let attach_type = self.data.expected_attach_type.unwrap(); + let link = match bpf_link_create( + prog_fd, + LinkTarget::IfIndex(if_index), + attach_type, + flags.bits(), + None, + ) { + Ok(link_fd) => XdpLinkInner::Fd(FdLink::new(link_fd)), + Err(io_error) => { + if io_error.raw_os_error() != Some(libc::EINVAL) { + return Err(ProgramError::SyscallError(SyscallError { + call: "bpf_link_create", + io_error, + })); + } + + // Fall back to netlink-based attachment. + + let if_index = if_index as i32; + unsafe { netlink_set_xdp_fd(if_index, Some(prog_fd), None, flags.bits()) } + .map_err(XdpError::NetlinkError)?; + + let prog_fd = prog_fd.as_raw_fd(); + XdpLinkInner::NlLink(NlLink { if_index, prog_fd, flags, - }))) - } + }) + } + }; + self.data.links.insert(XdpLink::new(link)) } /// Creates a program from a pinned entry on a bpffs.