From a2fe63fab4821806c8e6f9e3e7850d0e1ae79797 Mon Sep 17 00:00:00 2001 From: Carson Anderson Date: Wed, 22 Apr 2026 15:38:33 -0600 Subject: [PATCH 1/2] aya: add support for netkit attachments Adds support for netkit primary and peer attachments. This refactors the attachments from one that covers tc and tcx and hiding the attachment type from the user to making the user explicitly choose which attachment they are using. Closes: #1540 --- aya/src/programs/links.rs | 11 +- aya/src/programs/mod.rs | 5 +- aya/src/programs/tc.rs | 479 +++++++++++------- aya/src/sys/bpf.rs | 21 + test/integration-test/src/tests/load.rs | 11 +- test/integration-test/src/tests/tc_netlink.rs | 38 +- test/integration-test/src/tests/tcx.rs | 22 +- xtask/public-api/aya.txt | 144 +++++- 8 files changed, 498 insertions(+), 233 deletions(-) diff --git a/aya/src/programs/links.rs b/aya/src/programs/links.rs index 62f40102d4..0a8823c8a3 100644 --- a/aya/src/programs/links.rs +++ b/aya/src/programs/links.rs @@ -648,12 +648,17 @@ bitflags::bitflags! { /// /// ```no_run /// # let mut bpf = aya::Ebpf::load(&[])?; -/// use aya::programs::{tc, SchedClassifier, TcAttachType, tc::TcAttachOptions, LinkOrder}; +/// use aya::programs::{LinkOrder, SchedClassifier, SchedClassifierAttachment, TcxAttachType}; /// /// let prog: &mut SchedClassifier = bpf.program_mut("redirect_ingress").unwrap().try_into()?; /// prog.load()?; -/// let options = TcAttachOptions::TcxOrder(LinkOrder::first()); -/// prog.attach_with_options("eth0", TcAttachType::Ingress, options)?; +/// prog.attach( +/// "eth0", +/// SchedClassifierAttachment::Tcx { +/// attach_type: TcxAttachType::Ingress, +/// link_order: LinkOrder::first(), +/// }, +/// )?; /// /// # Ok::<(), aya::EbpfError>(()) /// ``` diff --git a/aya/src/programs/mod.rs b/aya/src/programs/mod.rs index 14e3d31a7e..668cc55511 100644 --- a/aya/src/programs/mod.rs +++ b/aya/src/programs/mod.rs @@ -121,7 +121,10 @@ pub use crate::programs::{ sk_skb::{SkSkb, SkSkbKind}, sock_ops::SockOps, socket_filter::{SocketFilter, SocketFilterError}, - tc::{SchedClassifier, TcAttachType, TcError, TcHandle}, + tc::{ + NetkitAttachType, SchedClassifier, SchedClassifierAttachment, TcAttachType, TcError, + TcHandle, TcxAttachType, + }, tp_btf::BtfTracePoint, trace_point::{TracePoint, TracePointError}, uprobe::{UProbe, UProbeError}, diff --git a/aya/src/programs/tc.rs b/aya/src/programs/tc.rs index a6eb16bf8e..09bcaae2f2 100644 --- a/aya/src/programs/tc.rs +++ b/aya/src/programs/tc.rs @@ -3,7 +3,7 @@ use std::{ffi::CString, io, os::fd::AsFd as _, path::Path}; use aya_obj::generated::{ TC_H_CLSACT, TC_H_MIN_EGRESS, TC_H_MIN_INGRESS, - bpf_attach_type::{self, BPF_TCX_EGRESS, BPF_TCX_INGRESS}, + bpf_attach_type::{self, BPF_NETKIT_PEER, BPF_NETKIT_PRIMARY, BPF_TCX_EGRESS, BPF_TCX_INGRESS}, bpf_link_type, bpf_prog_type::BPF_PROG_TYPE_SCHED_CLS, }; @@ -14,18 +14,42 @@ use crate::{ VerifierLogLevel, programs::{ Link, LinkError, LinkOrder, ProgramData, ProgramError, ProgramType, define_link_wrapper, - id_as_key, impl_try_from_fdlink, impl_try_into_fdlink, load_program_without_attach_type, - query, + id_as_key, impl_try_into_fdlink, load_program_without_attach_type, query, }, sys::{ BpfLinkCreateArgs, LinkTarget, NetlinkError, NetlinkSocket, ProgQueryTarget, SyscallError, bpf_link_create, bpf_link_update, bpf_prog_get_fd_by_id, netlink_find_filter_with_name, netlink_qdisc_add_clsact, netlink_qdisc_attach, netlink_qdisc_detach, }, - util::{KernelVersion, ifindex_from_ifname, tc_handler_make}, + util::{ifindex_from_ifname, tc_handler_make}, }; -/// Traffic control attach type. +/// Attachment configuration for [`SchedClassifier`] programs. +pub enum SchedClassifierAttachment { + /// Attach as a legacy TC filter using netlink. + Tc { + /// The legacy TC hook to attach to. + attach_type: TcAttachType, + /// Netlink attach options. + options: NlOptions, + }, + /// Attach to TCX using the eBPF link interface. + Tcx { + /// The TCX hook to attach to. + attach_type: TcxAttachType, + /// Multi-prog link ordering. + link_order: LinkOrder, + }, + /// Attach to a Netkit device using the eBPF link interface. + Netkit { + /// The Netkit hook to attach to. + attach_type: NetkitAttachType, + /// Multi-prog link ordering. + link_order: LinkOrder, + }, +} + +/// Traffic control attach type using netlink. #[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)] pub enum TcAttachType { /// Attach to ingress. @@ -36,6 +60,45 @@ pub enum TcAttachType { Custom(u32), } +/// Traffic control attach type using eBPF link interface. +/// Requires kernels >= 6.6.0. +#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)] +pub enum TcxAttachType { + /// Attach to ingress. + Ingress, + /// Attach to egress. + Egress, +} + +impl TcxAttachType { + pub(crate) const fn bpf_attach_type(self) -> bpf_attach_type { + match self { + Self::Ingress => BPF_TCX_INGRESS, + Self::Egress => BPF_TCX_EGRESS, + } + } +} + +/// Netkit attach type using eBPF link interface. +/// Requires kernels >= 6.7.0. +/// Can only be attached to netkit devices. +#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)] +pub enum NetkitAttachType { + /// Attach to primary. + Primary, + /// Attach to peer. + Peer, +} + +impl NetkitAttachType { + pub(crate) const fn bpf_attach_type(self) -> bpf_attach_type { + match self { + Self::Primary => BPF_NETKIT_PRIMARY, + Self::Peer => BPF_NETKIT_PEER, + } + } +} + /// A network traffic control classifier. /// /// [`SchedClassifier`] programs can be used to inspect, filter or redirect @@ -47,7 +110,13 @@ pub enum TcAttachType { /// /// # Minimum kernel version /// -/// The minimum kernel version required to use this feature is 4.1. +/// Legacy TC attachments require kernel 4.1 or later. TCX attachments require +/// kernel 6.6 or later. Netkit attachments require kernel 6.7 or later and a +/// netkit device. +/// +/// # Legacy TC attachment +/// +/// Legacy TC attachments use netlink and require the `clsact` qdisc. /// /// ```no_run /// # #[derive(Debug, thiserror::Error)] @@ -64,15 +133,100 @@ pub enum TcAttachType { /// # Ebpf(#[from] aya::EbpfError) /// # } /// # let mut bpf = aya::Ebpf::load(&[])?; -/// use aya::programs::{tc, SchedClassifier, TcAttachType}; +/// use aya::programs::{tc::{self, NlOptions}, SchedClassifier, SchedClassifierAttachment, TcAttachType}; /// /// // the clsact qdisc needs to be added before SchedClassifier programs can be -/// // attached +/// // attached with legacy TC /// tc::qdisc_add_clsact("eth0")?; /// /// let prog: &mut SchedClassifier = bpf.program_mut("redirect_ingress").unwrap().try_into()?; /// prog.load()?; -/// prog.attach("eth0", TcAttachType::Ingress)?; +/// prog.attach( +/// "eth0", +/// SchedClassifierAttachment::Tc { +/// attach_type: TcAttachType::Ingress, +/// options: NlOptions::default(), +/// }, +/// )?; +/// +/// # Ok::<(), Error>(()) +/// ``` +/// +/// # TCX attachment +/// +/// TCX attachments use eBPF links and support multi-prog ordering. +/// +/// ```no_run +/// # #[derive(Debug, thiserror::Error)] +/// # enum Error { +/// # #[error(transparent)] +/// # IO(#[from] std::io::Error), +/// # #[error(transparent)] +/// # Map(#[from] aya::maps::MapError), +/// # #[error(transparent)] +/// # Program(#[from] aya::programs::ProgramError), +/// # #[error(transparent)] +/// # Tc(#[from] aya::programs::tc::TcError), +/// # #[error(transparent)] +/// # Ebpf(#[from] aya::EbpfError) +/// # } +/// # let mut bpf = aya::Ebpf::load(&[])?; +/// use aya::programs::{LinkOrder, SchedClassifier, SchedClassifierAttachment, TcxAttachType}; +/// +/// let prog: &mut SchedClassifier = bpf.program_mut("redirect_ingress").unwrap().try_into()?; +/// prog.load()?; +/// prog.attach( +/// "eth0", +/// SchedClassifierAttachment::Tcx { +/// attach_type: TcxAttachType::Ingress, +/// link_order: LinkOrder::default(), +/// }, +/// )?; +/// +/// # Ok::<(), Error>(()) +/// ``` +/// +/// # Netkit attachment +/// +/// Netkit attachments use eBPF links and support multi-prog ordering. The +/// interface must be a netkit device. +/// +/// ```no_run +/// # #[derive(Debug, thiserror::Error)] +/// # enum Error { +/// # #[error(transparent)] +/// # IO(#[from] std::io::Error), +/// # #[error(transparent)] +/// # Map(#[from] aya::maps::MapError), +/// # #[error(transparent)] +/// # Program(#[from] aya::programs::ProgramError), +/// # #[error(transparent)] +/// # Tc(#[from] aya::programs::tc::TcError), +/// # #[error(transparent)] +/// # Ebpf(#[from] aya::EbpfError) +/// # } +/// # let mut bpf = aya::Ebpf::load(&[])?; +/// use aya::programs::{LinkOrder, NetkitAttachType, SchedClassifier, SchedClassifierAttachment}; +/// +/// let primary_prog: &mut SchedClassifier = bpf.program_mut("primary").unwrap().try_into()?; +/// primary_prog.load()?; +/// primary_prog.attach( +/// "nk0", +/// SchedClassifierAttachment::Netkit { +/// attach_type: NetkitAttachType::Primary, +/// link_order: LinkOrder::default(), +/// }, +/// )?; +/// let peer_prog: &mut SchedClassifier = bpf.program_mut("peer").unwrap().try_into()?; +/// peer_prog.load()?; +/// // Peer attachment still occurs on the primary interface +/// peer_prog.attach( +/// "nk0", +/// SchedClassifierAttachment::Netkit { +/// attach_type: NetkitAttachType::Peer, +/// link_order: LinkOrder::default(), +/// }, +/// )?; /// /// # Ok::<(), Error>(()) /// ``` @@ -97,13 +251,8 @@ pub enum TcError { /// the clsact qdisc is already attached. #[error("the clsact qdisc is already attached")] AlreadyAttached, - /// tcx links can only be attached to ingress or egress, custom attachment is not supported. - #[error( - "tcx links can only be attached to ingress or egress, custom attachment: {0} is not supported" - )] - InvalidTcxAttach(u32), - /// operation not supported for programs loaded via tcx. - #[error("operation not supported for programs loaded via tcx")] + /// operation not supported for programs loaded via tcx or netkit. + #[error("operation not supported for fd-backed TC links")] InvalidLinkOperation, } @@ -115,28 +264,6 @@ impl TcAttachType { Self::Egress => tc_handler_make(TC_H_CLSACT, TC_H_MIN_EGRESS), } } - - pub(crate) const fn tcx_attach_type(self) -> Result { - match self { - Self::Ingress => Ok(BPF_TCX_INGRESS), - Self::Egress => Ok(BPF_TCX_EGRESS), - Self::Custom(tcx_attach_type) => Err(TcError::InvalidTcxAttach(tcx_attach_type)), - } - } -} - -/// Options for a [`SchedClassifier`] attach operation. -/// -/// The options vary based on what is supported by the current kernel. Kernels -/// older than 6.6.0 must utilize netlink for attachments, while newer kernels -/// can utilize the modern TCX eBPF link type which supports the kernel's -/// multi-prog API. -#[derive(Debug)] -pub enum TcAttachOptions { - /// Netlink attach options. - Netlink(NlOptions), - /// Tcx attach options. - TcxOrder(LinkOrder), } /// A TC filter handle in `major:minor` form. @@ -217,11 +344,7 @@ impl SchedClassifier { /// Attaches the program to the given `interface`. /// - /// On kernels >= 6.6.0, it will attempt to use the TCX interface and attach as - /// the last TCX program. On older kernels, it will fallback to using the - /// legacy netlink interface. - /// - /// For finer grained control over link ordering use [`SchedClassifier::attach_with_options`]. + /// TCX requires kernels >= 6.6.0 and netkit requires kernels >= 6.7.0. /// /// The returned value can be used to detach, see [`SchedClassifier::detach`]. /// @@ -235,86 +358,35 @@ impl SchedClassifier { pub fn attach( &mut self, interface: &str, - attach_type: TcAttachType, + attachment: SchedClassifierAttachment, ) -> Result { - if !matches!(attach_type, TcAttachType::Custom(_)) && KernelVersion::at_least(6, 6, 0) { - self.attach_with_options( - interface, + let if_index = ifindex_from_ifname(interface).map_err(TcError::IoError)?; + match attachment { + SchedClassifierAttachment::Tc { attach_type, - TcAttachOptions::TcxOrder(LinkOrder::default()), - ) - } else { - self.attach_with_options( - interface, + options, + } => self.do_netlink_attach(if_index, attach_type, options, true), + SchedClassifierAttachment::Tcx { attach_type, - TcAttachOptions::Netlink(NlOptions::default()), - ) + link_order, + } => self.do_bpf_link_attach( + if_index, + attach_type.bpf_attach_type(), + &link_order, + Some(BpfLinkCreateArgs::Tcx(&link_order.link_ref)), + ), + SchedClassifierAttachment::Netkit { + attach_type, + link_order, + } => self.do_bpf_link_attach( + if_index, + attach_type.bpf_attach_type(), + &link_order, + Some(BpfLinkCreateArgs::Netkit(&link_order.link_ref)), + ), } } - /// Attaches the program to the given `interface` with options defined in [`TcAttachOptions`]. - /// - /// The returned value can be used to detach, see [`SchedClassifier::detach`]. - /// - /// # Link Pinning (TCX mode, kernel >= 6.6) - /// - /// Links can be pinned to bpffs for atomic replacement across process restarts. - /// - /// ```no_run - /// # use std::{io, path::Path}; - /// - /// # use aya::{ - /// # programs::{ - /// # LinkOrder, SchedClassifier, TcAttachType, - /// # links::{FdLink, LinkError, PinnedLink}, - /// # tc::TcAttachOptions, - /// # }, - /// # sys::SyscallError, - /// # }; - /// - /// # let mut bpf = aya::Ebpf::load(&[])?; - /// # let prog: &mut SchedClassifier = bpf.program_mut("prog").unwrap().try_into()?; - /// # prog.load()?; - /// let pin_path = "/sys/fs/bpf/my_link"; - /// - /// let link_id = match PinnedLink::from_pin(pin_path) { - /// Ok(old) => { - /// let link = FdLink::from(old).try_into()?; - /// prog.attach_to_link(link)? - /// } - /// Err(LinkError::SyscallError(SyscallError { io_error, .. })) - /// if io_error.kind() == io::ErrorKind::NotFound => - /// { - /// prog.attach_with_options( - /// "eth0", - /// TcAttachType::Ingress, - /// TcAttachOptions::TcxOrder(LinkOrder::default()), - /// )? - /// } - /// Err(e) => return Err(e.into()), - /// }; - /// - /// let link = prog.take_link(link_id)?; - /// let fd_link: FdLink = link.try_into()?; - /// fd_link.pin(pin_path)?; - /// # Ok::<(), Box>(()) - /// ``` - /// - /// # Errors - /// - /// [`TcError::NetlinkError`] is returned if attaching fails. A common cause - /// of failure is not having added the `clsact` qdisc to the given - /// interface, see [`qdisc_add_clsact`]. - pub fn attach_with_options( - &mut self, - interface: &str, - attach_type: TcAttachType, - options: TcAttachOptions, - ) -> Result { - let if_index = ifindex_from_ifname(interface).map_err(TcError::IoError)?; - self.do_attach(if_index, attach_type, options, true) - } - /// Atomically replaces the program referenced by the provided link. /// /// Ownership of the link will transfer to this program. @@ -346,78 +418,84 @@ impl SchedClassifier { priority, handle, classid, - }) => self.do_attach( + }) => self.do_netlink_attach( if_index, attach_type, - TcAttachOptions::Netlink(NlOptions { + NlOptions { priority, handle, classid, - }), + }, false, ), } } - fn do_attach( + fn do_bpf_link_attach( &mut self, if_index: u32, - attach_type: TcAttachType, - options: TcAttachOptions, - create: bool, + attach_type: bpf_attach_type, + link_order: &LinkOrder, + bpf_link_create_args: Option>, ) -> Result { let prog_fd = self.fd()?; let prog_fd = prog_fd.as_fd(); - match options { - TcAttachOptions::Netlink(options) => { - let name = self.data.name.as_deref().unwrap_or_default(); - // TODO: avoid this unwrap by adding a new error variant. - let name = CString::new(name).unwrap(); - let (priority, handle) = unsafe { - netlink_qdisc_attach( - if_index as i32, - &attach_type, - prog_fd, - &name, - options.priority, - options.handle, - options.classid, - create, - ) - } - .map_err(TcError::NetlinkError)?; + let link_fd = bpf_link_create( + prog_fd, + LinkTarget::IfIndex(if_index), + attach_type, + link_order.flags.bits(), + bpf_link_create_args, + ) + .map_err(|io_error| SyscallError { + call: "bpf_mprog_attach", + io_error, + })?; + + self.data + .links + .insert(SchedClassifierLink::new(TcLinkInner::Fd(FdLink::new( + link_fd, + )))) + } - self.data - .links - .insert(SchedClassifierLink::new(TcLinkInner::NlLink(NlLink { - if_index, - attach_type, - priority, - handle, - classid: options.classid, - }))) - } - TcAttachOptions::TcxOrder(options) => { - let link_fd = bpf_link_create( - prog_fd, - LinkTarget::IfIndex(if_index), - attach_type.tcx_attach_type()?, - options.flags.bits(), - Some(BpfLinkCreateArgs::Tcx(&options.link_ref)), - ) - .map_err(|io_error| SyscallError { - call: "bpf_mprog_attach", - io_error, - })?; + fn do_netlink_attach( + &mut self, + if_index: u32, + attach_type: TcAttachType, + options: NlOptions, + create: bool, + ) -> Result { + let prog_fd = self.fd()?; + let prog_fd = prog_fd.as_fd(); - self.data - .links - .insert(SchedClassifierLink::new(TcLinkInner::Fd(FdLink::new( - link_fd, - )))) - } + let name = self.data.name.as_deref().unwrap_or_default(); + // TODO: avoid this unwrap by adding a new error variant. + let name = CString::new(name).unwrap(); + let (priority, handle) = unsafe { + netlink_qdisc_attach( + if_index as i32, + &attach_type, + prog_fd, + &name, + options.priority, + options.handle, + options.classid, + create, + ) } + .map_err(TcError::NetlinkError)?; + + self.data + .links + .insert(SchedClassifierLink::new(TcLinkInner::NlLink(NlLink { + if_index, + attach_type, + priority, + handle, + classid: options.classid, + }))) } /// Creates a program from a pinned entry on a bpffs. @@ -436,24 +514,52 @@ impl SchedClassifier { /// # Example /// /// ```no_run - /// # use aya::programs::tc::{TcAttachType, SchedClassifier}; + /// # use aya::programs::tc::{TcxAttachType, SchedClassifier}; /// # #[derive(Debug, thiserror::Error)] /// # enum Error { /// # #[error(transparent)] /// # Program(#[from] aya::programs::ProgramError), /// # } - /// let (revision, programs) = SchedClassifier::query_tcx("eth0", TcAttachType::Ingress)?; + /// let (revision, programs) = SchedClassifier::query_tcx("eth0", TcxAttachType::Ingress)?; /// # Ok::<(), Error>(()) /// ``` pub fn query_tcx( interface: &str, - attach_type: TcAttachType, + attach_type: TcxAttachType, + ) -> Result<(u64, Vec), ProgramError> { + Self::query_bpf_links(interface, attach_type.bpf_attach_type()) + } + + /// Queries a given interface for attached Netkit programs. + /// + /// # Example + /// + /// ```no_run + /// # use aya::programs::tc::{NetkitAttachType, SchedClassifier}; + /// # #[derive(Debug, thiserror::Error)] + /// # enum Error { + /// # #[error(transparent)] + /// # Program(#[from] aya::programs::ProgramError), + /// # } + /// let (revision, programs) = SchedClassifier::query_netkit("nk0", NetkitAttachType::Primary)?; + /// # Ok::<(), Error>(()) + /// ``` + pub fn query_netkit( + interface: &str, + attach_type: NetkitAttachType, + ) -> Result<(u64, Vec), ProgramError> { + Self::query_bpf_links(interface, attach_type.bpf_attach_type()) + } + + fn query_bpf_links( + interface: &str, + attach_type: bpf_attach_type, ) -> Result<(u64, Vec), ProgramError> { let if_index = ifindex_from_ifname(interface).map_err(TcError::IoError)?; let (revision, prog_ids) = query( ProgQueryTarget::IfIndex(if_index), - attach_type.tcx_attach_type()?, + attach_type, 0, &mut None, )?; @@ -551,11 +657,24 @@ impl<'a> TryFrom<&'a SchedClassifierLink> for &'a FdLink { } impl_try_into_fdlink!(SchedClassifierLink, TcLinkInner); -impl_try_from_fdlink!( - SchedClassifierLink, - TcLinkInner, - bpf_link_type::BPF_LINK_TYPE_TCX -); + +impl TryFrom for SchedClassifierLink { + type Error = LinkError; + + fn try_from(fd_link: FdLink) -> Result { + let info = crate::sys::bpf_link_get_info_by_fd(fd_link.fd.as_fd())?; + + match info.type_ { + link_type + if link_type == bpf_link_type::BPF_LINK_TYPE_TCX as u32 + || link_type == bpf_link_type::BPF_LINK_TYPE_NETKIT as u32 => + { + Ok(Self::new(TcLinkInner::Fd(fd_link))) + } + _ => Err(LinkError::InvalidLink), + } + } +} define_link_wrapper!( SchedClassifierLink, @@ -653,7 +772,7 @@ impl SchedClassifierLink { } } -/// Add the `clasct` qdisc to the given interface. +/// Add the `clsact` qdisc to the given interface. /// /// The `clsact` qdisc must be added to an interface before [`SchedClassifier`] /// programs can be attached. diff --git a/aya/src/sys/bpf.rs b/aya/src/sys/bpf.rs index 24ea0431a8..14ee1c3bd4 100644 --- a/aya/src/sys/bpf.rs +++ b/aya/src/sys/bpf.rs @@ -430,6 +430,8 @@ pub(crate) enum BpfLinkCreateArgs<'a> { PerfEvent { bpf_cookie: u64 }, // since kernel 6.6 Tcx(&'a LinkRef), + // since kernel 6.7 + Netkit(&'a LinkRef), } // since kernel 5.7 @@ -487,6 +489,25 @@ pub(crate) fn bpf_link_create( ); }, }, + BpfLinkCreateArgs::Netkit(link_ref) => match link_ref { + LinkRef::Fd(fd) => { + attr.link_create + .__bindgen_anon_3 + .netkit + .__bindgen_anon_1 + .relative_fd = fd.to_owned() as u32; + } + LinkRef::Id(id) => unsafe { + id.clone_into( + &mut attr + .link_create + .__bindgen_anon_3 + .netkit + .__bindgen_anon_1 + .relative_id, + ); + }, + }, } } diff --git a/test/integration-test/src/tests/load.rs b/test/integration-test/src/tests/load.rs index 86fa92047c..38092d20e1 100644 --- a/test/integration-test/src/tests/load.rs +++ b/test/integration-test/src/tests/load.rs @@ -9,12 +9,11 @@ use aya::{ pin::PinError, programs::{ FlowDissector, KProbe, LinkOrder, ProbeKind, Program, ProgramError, SchedClassifier, - TcAttachType, TracePoint, UProbe, Xdp, XdpMode, + SchedClassifierAttachment, TcxAttachType, TracePoint, UProbe, Xdp, XdpMode, flow_dissector::{FlowDissectorLink, FlowDissectorLinkId}, kprobe::{KProbeLink, KProbeLinkId}, links::{FdLink, LinkError, PinnedLink}, loaded_links, loaded_programs, - tc::TcAttachOptions, trace_point::{TracePointLink, TracePointLinkId}, uprobe::{UProbeLink, UProbeLinkId, UProbeScope}, xdp::{XdpLink, XdpLinkId}, @@ -457,10 +456,12 @@ fn pin_tcx_link() { prog.load().unwrap(); let link_id = prog - .attach_with_options( + .attach( "lo", - TcAttachType::Ingress, - TcAttachOptions::TcxOrder(LinkOrder::default()), + SchedClassifierAttachment::Tcx { + attach_type: TcxAttachType::Ingress, + link_order: LinkOrder::default(), + }, ) .unwrap(); let link = prog.take_link(link_id).unwrap(); diff --git a/test/integration-test/src/tests/tc_netlink.rs b/test/integration-test/src/tests/tc_netlink.rs index 2f367ad756..60c1568297 100644 --- a/test/integration-test/src/tests/tc_netlink.rs +++ b/test/integration-test/src/tests/tc_netlink.rs @@ -2,7 +2,7 @@ use aya::{ Ebpf, programs::{ SchedClassifier, TcAttachType, - tc::{NlOptions, TcAttachOptions, TcHandle, qdisc_add_clsact}, + tc::{NlOptions, SchedClassifierAttachment, TcHandle, qdisc_add_clsact}, }, util::KernelVersion, }; @@ -54,13 +54,15 @@ fn netlink_attach_to_link_preserves_classid() { let classid = TcHandle::new(1, 1); let link_id = prog - .attach_with_options( + .attach( "lo", - TcAttachType::Ingress, - TcAttachOptions::Netlink(NlOptions { - classid: Some(classid), - ..Default::default() - }), + SchedClassifierAttachment::Tc { + attach_type: TcAttachType::Ingress, + options: NlOptions { + classid: Some(classid), + ..Default::default() + }, + }, ) .unwrap(); @@ -89,10 +91,12 @@ fn netlink_attach_auto_assigns_handle() { prog.load().unwrap(); let link_id = prog - .attach_with_options( + .attach( "lo", - TcAttachType::Ingress, - TcAttachOptions::Netlink(NlOptions::default()), + SchedClassifierAttachment::Tc { + attach_type: TcAttachType::Ingress, + options: NlOptions::default(), + }, ) .unwrap(); @@ -118,13 +122,15 @@ fn netlink_attach_preserves_explicit_handle() { let handle = TcHandle::new(1, 0xfffe); let link_id = prog - .attach_with_options( + .attach( "lo", - TcAttachType::Ingress, - TcAttachOptions::Netlink(NlOptions { - handle, - ..Default::default() - }), + SchedClassifierAttachment::Tc { + attach_type: TcAttachType::Ingress, + options: NlOptions { + handle, + ..Default::default() + }, + }, ) .unwrap(); diff --git a/test/integration-test/src/tests/tcx.rs b/test/integration-test/src/tests/tcx.rs index 774a9b80af..a65c99ae4c 100644 --- a/test/integration-test/src/tests/tcx.rs +++ b/test/integration-test/src/tests/tcx.rs @@ -1,6 +1,8 @@ use aya::{ Ebpf, - programs::{LinkOrder, ProgramId, SchedClassifier, TcAttachType, tc::TcAttachOptions}, + programs::{ + LinkOrder, ProgramId, SchedClassifier, TcxAttachType, tc::SchedClassifierAttachment, + }, util::KernelVersion, }; @@ -37,20 +39,24 @@ fn tcx() { ($program_name:ident, $link_order:expr) => { attach_program_with_link_order_inner!($program_name, $link_order); $program_name - .attach_with_options( + .attach( "lo", - TcAttachType::Ingress, - TcAttachOptions::TcxOrder($link_order), + SchedClassifierAttachment::Tcx { + attach_type: TcxAttachType::Ingress, + link_order: $link_order, + }, ) .unwrap(); }; ($program_name:ident, $link_id_name:ident, $link_order:expr) => { attach_program_with_link_order_inner!($program_name, $link_order); let $link_id_name = $program_name - .attach_with_options( + .attach( "lo", - TcAttachType::Ingress, - TcAttachOptions::TcxOrder($link_order), + SchedClassifierAttachment::Tcx { + attach_type: TcxAttachType::Ingress, + link_order: $link_order, + }, ) .unwrap(); }; @@ -92,7 +98,7 @@ fn tcx() { .map(|program| program.info().unwrap().id()) .collect::>(); - let (revision, got_order) = SchedClassifier::query_tcx("lo", TcAttachType::Ingress).unwrap(); + let (revision, got_order) = SchedClassifier::query_tcx("lo", TcxAttachType::Ingress).unwrap(); assert_eq!(revision, (expected_order.len() + 1) as u64); assert_eq!( got_order diff --git a/xtask/public-api/aya.txt b/xtask/public-api/aya.txt index 17b4f1318e..fe01df3aa4 100644 --- a/xtask/public-api/aya.txt +++ b/xtask/public-api/aya.txt @@ -4519,18 +4519,44 @@ impl core::marker::UnsafeUnpin for aya::programs::socket_filter::SocketFilterLin impl core::panic::unwind_safe::RefUnwindSafe for aya::programs::socket_filter::SocketFilterLinkId impl core::panic::unwind_safe::UnwindSafe for aya::programs::socket_filter::SocketFilterLinkId pub mod aya::programs::tc -pub enum aya::programs::tc::TcAttachOptions -pub aya::programs::tc::TcAttachOptions::Netlink(aya::programs::tc::NlOptions) -pub aya::programs::tc::TcAttachOptions::TcxOrder(aya::programs::links::LinkOrder) -impl core::fmt::Debug for aya::programs::tc::TcAttachOptions -pub fn aya::programs::tc::TcAttachOptions::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result -impl core::marker::Freeze for aya::programs::tc::TcAttachOptions -impl core::marker::Send for aya::programs::tc::TcAttachOptions -impl core::marker::Sync for aya::programs::tc::TcAttachOptions -impl core::marker::Unpin for aya::programs::tc::TcAttachOptions -impl core::marker::UnsafeUnpin for aya::programs::tc::TcAttachOptions -impl core::panic::unwind_safe::RefUnwindSafe for aya::programs::tc::TcAttachOptions -impl core::panic::unwind_safe::UnwindSafe for aya::programs::tc::TcAttachOptions +pub enum aya::programs::tc::NetkitAttachType +pub aya::programs::tc::NetkitAttachType::Peer +pub aya::programs::tc::NetkitAttachType::Primary +impl core::clone::Clone for aya::programs::tc::NetkitAttachType +pub fn aya::programs::tc::NetkitAttachType::clone(&self) -> aya::programs::tc::NetkitAttachType +impl core::cmp::Eq for aya::programs::tc::NetkitAttachType +impl core::cmp::PartialEq for aya::programs::tc::NetkitAttachType +pub fn aya::programs::tc::NetkitAttachType::eq(&self, other: &aya::programs::tc::NetkitAttachType) -> bool +impl core::fmt::Debug for aya::programs::tc::NetkitAttachType +pub fn aya::programs::tc::NetkitAttachType::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result +impl core::hash::Hash for aya::programs::tc::NetkitAttachType +pub fn aya::programs::tc::NetkitAttachType::hash<__H: core::hash::Hasher>(&self, state: &mut __H) +impl core::marker::Copy for aya::programs::tc::NetkitAttachType +impl core::marker::StructuralPartialEq for aya::programs::tc::NetkitAttachType +impl core::marker::Freeze for aya::programs::tc::NetkitAttachType +impl core::marker::Send for aya::programs::tc::NetkitAttachType +impl core::marker::Sync for aya::programs::tc::NetkitAttachType +impl core::marker::Unpin for aya::programs::tc::NetkitAttachType +impl core::marker::UnsafeUnpin for aya::programs::tc::NetkitAttachType +impl core::panic::unwind_safe::RefUnwindSafe for aya::programs::tc::NetkitAttachType +impl core::panic::unwind_safe::UnwindSafe for aya::programs::tc::NetkitAttachType +pub enum aya::programs::tc::SchedClassifierAttachment +pub aya::programs::tc::SchedClassifierAttachment::Netkit +pub aya::programs::tc::SchedClassifierAttachment::Netkit::attach_type: aya::programs::tc::NetkitAttachType +pub aya::programs::tc::SchedClassifierAttachment::Netkit::link_order: aya::programs::links::LinkOrder +pub aya::programs::tc::SchedClassifierAttachment::Tc +pub aya::programs::tc::SchedClassifierAttachment::Tc::attach_type: aya::programs::tc::TcAttachType +pub aya::programs::tc::SchedClassifierAttachment::Tc::options: aya::programs::tc::NlOptions +pub aya::programs::tc::SchedClassifierAttachment::Tcx +pub aya::programs::tc::SchedClassifierAttachment::Tcx::attach_type: aya::programs::tc::TcxAttachType +pub aya::programs::tc::SchedClassifierAttachment::Tcx::link_order: aya::programs::links::LinkOrder +impl core::marker::Freeze for aya::programs::tc::SchedClassifierAttachment +impl core::marker::Send for aya::programs::tc::SchedClassifierAttachment +impl core::marker::Sync for aya::programs::tc::SchedClassifierAttachment +impl core::marker::Unpin for aya::programs::tc::SchedClassifierAttachment +impl core::marker::UnsafeUnpin for aya::programs::tc::SchedClassifierAttachment +impl core::panic::unwind_safe::RefUnwindSafe for aya::programs::tc::SchedClassifierAttachment +impl core::panic::unwind_safe::UnwindSafe for aya::programs::tc::SchedClassifierAttachment pub enum aya::programs::tc::TcAttachType pub aya::programs::tc::TcAttachType::Custom(u32) pub aya::programs::tc::TcAttachType::Egress @@ -4556,7 +4582,6 @@ impl core::panic::unwind_safe::UnwindSafe for aya::programs::tc::TcAttachType pub enum aya::programs::tc::TcError pub aya::programs::tc::TcError::AlreadyAttached pub aya::programs::tc::TcError::InvalidLinkOperation -pub aya::programs::tc::TcError::InvalidTcxAttach(u32) pub aya::programs::tc::TcError::IoError(std::io::error::Error) pub aya::programs::tc::TcError::NetlinkError(aya::sys::netlink::NetlinkError) pub aya::programs::tc::TcError::NulError(alloc::ffi::c_str::NulError) @@ -4579,6 +4604,27 @@ impl core::marker::Unpin for aya::programs::tc::TcError impl core::marker::UnsafeUnpin for aya::programs::tc::TcError impl !core::panic::unwind_safe::RefUnwindSafe for aya::programs::tc::TcError impl !core::panic::unwind_safe::UnwindSafe for aya::programs::tc::TcError +pub enum aya::programs::tc::TcxAttachType +pub aya::programs::tc::TcxAttachType::Egress +pub aya::programs::tc::TcxAttachType::Ingress +impl core::clone::Clone for aya::programs::tc::TcxAttachType +pub fn aya::programs::tc::TcxAttachType::clone(&self) -> aya::programs::tc::TcxAttachType +impl core::cmp::Eq for aya::programs::tc::TcxAttachType +impl core::cmp::PartialEq for aya::programs::tc::TcxAttachType +pub fn aya::programs::tc::TcxAttachType::eq(&self, other: &aya::programs::tc::TcxAttachType) -> bool +impl core::fmt::Debug for aya::programs::tc::TcxAttachType +pub fn aya::programs::tc::TcxAttachType::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result +impl core::hash::Hash for aya::programs::tc::TcxAttachType +pub fn aya::programs::tc::TcxAttachType::hash<__H: core::hash::Hasher>(&self, state: &mut __H) +impl core::marker::Copy for aya::programs::tc::TcxAttachType +impl core::marker::StructuralPartialEq for aya::programs::tc::TcxAttachType +impl core::marker::Freeze for aya::programs::tc::TcxAttachType +impl core::marker::Send for aya::programs::tc::TcxAttachType +impl core::marker::Sync for aya::programs::tc::TcxAttachType +impl core::marker::Unpin for aya::programs::tc::TcxAttachType +impl core::marker::UnsafeUnpin for aya::programs::tc::TcxAttachType +impl core::panic::unwind_safe::RefUnwindSafe for aya::programs::tc::TcxAttachType +impl core::panic::unwind_safe::UnwindSafe for aya::programs::tc::TcxAttachType pub struct aya::programs::tc::NlOptions pub aya::programs::tc::NlOptions::classid: core::option::Option pub aya::programs::tc::NlOptions::handle: aya::programs::tc::TcHandle @@ -4603,12 +4649,12 @@ impl core::panic::unwind_safe::UnwindSafe for aya::programs::tc::NlOptions pub struct aya::programs::tc::SchedClassifier impl aya::programs::tc::SchedClassifier pub const aya::programs::tc::SchedClassifier::PROGRAM_TYPE: aya::programs::ProgramType -pub fn aya::programs::tc::SchedClassifier::attach(&mut self, interface: &str, attach_type: aya::programs::tc::TcAttachType) -> core::result::Result +pub fn aya::programs::tc::SchedClassifier::attach(&mut self, interface: &str, attachment: aya::programs::tc::SchedClassifierAttachment) -> core::result::Result pub fn aya::programs::tc::SchedClassifier::attach_to_link(&mut self, link: aya::programs::tc::SchedClassifierLink) -> core::result::Result -pub fn aya::programs::tc::SchedClassifier::attach_with_options(&mut self, interface: &str, attach_type: aya::programs::tc::TcAttachType, options: aya::programs::tc::TcAttachOptions) -> core::result::Result pub fn aya::programs::tc::SchedClassifier::from_pin>(path: P) -> core::result::Result pub fn aya::programs::tc::SchedClassifier::load(&mut self) -> core::result::Result<(), aya::programs::ProgramError> -pub fn aya::programs::tc::SchedClassifier::query_tcx(interface: &str, attach_type: aya::programs::tc::TcAttachType) -> core::result::Result<(u64, alloc::vec::Vec), aya::programs::ProgramError> +pub fn aya::programs::tc::SchedClassifier::query_netkit(interface: &str, attach_type: aya::programs::tc::NetkitAttachType) -> core::result::Result<(u64, alloc::vec::Vec), aya::programs::ProgramError> +pub fn aya::programs::tc::SchedClassifier::query_tcx(interface: &str, attach_type: aya::programs::tc::TcxAttachType) -> core::result::Result<(u64, alloc::vec::Vec), aya::programs::ProgramError> impl aya::programs::tc::SchedClassifier pub fn aya::programs::tc::SchedClassifier::detach(&mut self, link_id: aya::programs::tc::SchedClassifierLinkId) -> core::result::Result<(), aya::programs::ProgramError> pub fn aya::programs::tc::SchedClassifier::take_link(&mut self, link_id: aya::programs::tc::SchedClassifierLinkId) -> core::result::Result @@ -5307,6 +5353,27 @@ impl core::marker::Unpin for aya::programs::LsmAttachType impl core::marker::UnsafeUnpin for aya::programs::LsmAttachType impl core::panic::unwind_safe::RefUnwindSafe for aya::programs::LsmAttachType impl core::panic::unwind_safe::UnwindSafe for aya::programs::LsmAttachType +pub enum aya::programs::NetkitAttachType +pub aya::programs::NetkitAttachType::Peer +pub aya::programs::NetkitAttachType::Primary +impl core::clone::Clone for aya::programs::tc::NetkitAttachType +pub fn aya::programs::tc::NetkitAttachType::clone(&self) -> aya::programs::tc::NetkitAttachType +impl core::cmp::Eq for aya::programs::tc::NetkitAttachType +impl core::cmp::PartialEq for aya::programs::tc::NetkitAttachType +pub fn aya::programs::tc::NetkitAttachType::eq(&self, other: &aya::programs::tc::NetkitAttachType) -> bool +impl core::fmt::Debug for aya::programs::tc::NetkitAttachType +pub fn aya::programs::tc::NetkitAttachType::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result +impl core::hash::Hash for aya::programs::tc::NetkitAttachType +pub fn aya::programs::tc::NetkitAttachType::hash<__H: core::hash::Hasher>(&self, state: &mut __H) +impl core::marker::Copy for aya::programs::tc::NetkitAttachType +impl core::marker::StructuralPartialEq for aya::programs::tc::NetkitAttachType +impl core::marker::Freeze for aya::programs::tc::NetkitAttachType +impl core::marker::Send for aya::programs::tc::NetkitAttachType +impl core::marker::Sync for aya::programs::tc::NetkitAttachType +impl core::marker::Unpin for aya::programs::tc::NetkitAttachType +impl core::marker::UnsafeUnpin for aya::programs::tc::NetkitAttachType +impl core::panic::unwind_safe::RefUnwindSafe for aya::programs::tc::NetkitAttachType +impl core::panic::unwind_safe::UnwindSafe for aya::programs::tc::NetkitAttachType pub enum aya::programs::ProbeKind pub aya::programs::ProbeKind::Entry pub aya::programs::ProbeKind::Return @@ -5651,6 +5718,23 @@ impl core::marker::Unpin for aya::programs::ProgramType impl core::marker::UnsafeUnpin for aya::programs::ProgramType impl core::panic::unwind_safe::RefUnwindSafe for aya::programs::ProgramType impl core::panic::unwind_safe::UnwindSafe for aya::programs::ProgramType +pub enum aya::programs::SchedClassifierAttachment +pub aya::programs::SchedClassifierAttachment::Netkit +pub aya::programs::SchedClassifierAttachment::Netkit::attach_type: aya::programs::tc::NetkitAttachType +pub aya::programs::SchedClassifierAttachment::Netkit::link_order: aya::programs::links::LinkOrder +pub aya::programs::SchedClassifierAttachment::Tc +pub aya::programs::SchedClassifierAttachment::Tc::attach_type: aya::programs::tc::TcAttachType +pub aya::programs::SchedClassifierAttachment::Tc::options: aya::programs::tc::NlOptions +pub aya::programs::SchedClassifierAttachment::Tcx +pub aya::programs::SchedClassifierAttachment::Tcx::attach_type: aya::programs::tc::TcxAttachType +pub aya::programs::SchedClassifierAttachment::Tcx::link_order: aya::programs::links::LinkOrder +impl core::marker::Freeze for aya::programs::tc::SchedClassifierAttachment +impl core::marker::Send for aya::programs::tc::SchedClassifierAttachment +impl core::marker::Sync for aya::programs::tc::SchedClassifierAttachment +impl core::marker::Unpin for aya::programs::tc::SchedClassifierAttachment +impl core::marker::UnsafeUnpin for aya::programs::tc::SchedClassifierAttachment +impl core::panic::unwind_safe::RefUnwindSafe for aya::programs::tc::SchedClassifierAttachment +impl core::panic::unwind_safe::UnwindSafe for aya::programs::tc::SchedClassifierAttachment pub enum aya::programs::SkReuseportError pub aya::programs::SkReuseportError::SetsockoptError pub aya::programs::SkReuseportError::SetsockoptError::io_error: std::io::error::Error @@ -5713,7 +5797,6 @@ impl core::panic::unwind_safe::UnwindSafe for aya::programs::tc::TcAttachType pub enum aya::programs::TcError pub aya::programs::TcError::AlreadyAttached pub aya::programs::TcError::InvalidLinkOperation -pub aya::programs::TcError::InvalidTcxAttach(u32) pub aya::programs::TcError::IoError(std::io::error::Error) pub aya::programs::TcError::NetlinkError(aya::sys::netlink::NetlinkError) pub aya::programs::TcError::NulError(alloc::ffi::c_str::NulError) @@ -5736,6 +5819,27 @@ impl core::marker::Unpin for aya::programs::tc::TcError impl core::marker::UnsafeUnpin for aya::programs::tc::TcError impl !core::panic::unwind_safe::RefUnwindSafe for aya::programs::tc::TcError impl !core::panic::unwind_safe::UnwindSafe for aya::programs::tc::TcError +pub enum aya::programs::TcxAttachType +pub aya::programs::TcxAttachType::Egress +pub aya::programs::TcxAttachType::Ingress +impl core::clone::Clone for aya::programs::tc::TcxAttachType +pub fn aya::programs::tc::TcxAttachType::clone(&self) -> aya::programs::tc::TcxAttachType +impl core::cmp::Eq for aya::programs::tc::TcxAttachType +impl core::cmp::PartialEq for aya::programs::tc::TcxAttachType +pub fn aya::programs::tc::TcxAttachType::eq(&self, other: &aya::programs::tc::TcxAttachType) -> bool +impl core::fmt::Debug for aya::programs::tc::TcxAttachType +pub fn aya::programs::tc::TcxAttachType::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result +impl core::hash::Hash for aya::programs::tc::TcxAttachType +pub fn aya::programs::tc::TcxAttachType::hash<__H: core::hash::Hasher>(&self, state: &mut __H) +impl core::marker::Copy for aya::programs::tc::TcxAttachType +impl core::marker::StructuralPartialEq for aya::programs::tc::TcxAttachType +impl core::marker::Freeze for aya::programs::tc::TcxAttachType +impl core::marker::Send for aya::programs::tc::TcxAttachType +impl core::marker::Sync for aya::programs::tc::TcxAttachType +impl core::marker::Unpin for aya::programs::tc::TcxAttachType +impl core::marker::UnsafeUnpin for aya::programs::tc::TcxAttachType +impl core::panic::unwind_safe::RefUnwindSafe for aya::programs::tc::TcxAttachType +impl core::panic::unwind_safe::UnwindSafe for aya::programs::tc::TcxAttachType pub enum aya::programs::TracePointError pub aya::programs::TracePointError::FileError pub aya::programs::TracePointError::FileError::filename: std::path::PathBuf @@ -6615,12 +6719,12 @@ impl core::panic::unwind_safe::UnwindSafe for aya::programs::RawTracePointTestRu pub struct aya::programs::SchedClassifier impl aya::programs::tc::SchedClassifier pub const aya::programs::tc::SchedClassifier::PROGRAM_TYPE: aya::programs::ProgramType -pub fn aya::programs::tc::SchedClassifier::attach(&mut self, interface: &str, attach_type: aya::programs::tc::TcAttachType) -> core::result::Result +pub fn aya::programs::tc::SchedClassifier::attach(&mut self, interface: &str, attachment: aya::programs::tc::SchedClassifierAttachment) -> core::result::Result pub fn aya::programs::tc::SchedClassifier::attach_to_link(&mut self, link: aya::programs::tc::SchedClassifierLink) -> core::result::Result -pub fn aya::programs::tc::SchedClassifier::attach_with_options(&mut self, interface: &str, attach_type: aya::programs::tc::TcAttachType, options: aya::programs::tc::TcAttachOptions) -> core::result::Result pub fn aya::programs::tc::SchedClassifier::from_pin>(path: P) -> core::result::Result pub fn aya::programs::tc::SchedClassifier::load(&mut self) -> core::result::Result<(), aya::programs::ProgramError> -pub fn aya::programs::tc::SchedClassifier::query_tcx(interface: &str, attach_type: aya::programs::tc::TcAttachType) -> core::result::Result<(u64, alloc::vec::Vec), aya::programs::ProgramError> +pub fn aya::programs::tc::SchedClassifier::query_netkit(interface: &str, attach_type: aya::programs::tc::NetkitAttachType) -> core::result::Result<(u64, alloc::vec::Vec), aya::programs::ProgramError> +pub fn aya::programs::tc::SchedClassifier::query_tcx(interface: &str, attach_type: aya::programs::tc::TcxAttachType) -> core::result::Result<(u64, alloc::vec::Vec), aya::programs::ProgramError> impl aya::programs::tc::SchedClassifier pub fn aya::programs::tc::SchedClassifier::detach(&mut self, link_id: aya::programs::tc::SchedClassifierLinkId) -> core::result::Result<(), aya::programs::ProgramError> pub fn aya::programs::tc::SchedClassifier::take_link(&mut self, link_id: aya::programs::tc::SchedClassifierLinkId) -> core::result::Result From 29d185fa6d0b3f667d2b4117c3ecb6d0939831b5 Mon Sep 17 00:00:00 2001 From: Carson Anderson Date: Sun, 26 Apr 2026 22:08:14 -0600 Subject: [PATCH 2/2] test: add netkit integration tests Adds netkit and ifindex helpers for netkit related tests. Adds query_netkit to match query_tcx used in integration tests. --- test/integration-test/src/tests.rs | 1 + test/integration-test/src/tests/load.rs | 64 ++++++++++++- test/integration-test/src/tests/netkit.rs | 108 ++++++++++++++++++++++ test/integration-test/src/utils.rs | 34 ++++++- 4 files changed, 203 insertions(+), 4 deletions(-) create mode 100644 test/integration-test/src/tests/netkit.rs diff --git a/test/integration-test/src/tests.rs b/test/integration-test/src/tests.rs index 88355697d1..179c70c7e5 100644 --- a/test/integration-test/src/tests.rs +++ b/test/integration-test/src/tests.rs @@ -26,6 +26,7 @@ mod lpm_trie; mod lsm; mod map_pin; mod maps_disjoint; +mod netkit; mod per_cpu_array; mod perf_event_array; mod perf_event_bp; diff --git a/test/integration-test/src/tests/load.rs b/test/integration-test/src/tests/load.rs index 38092d20e1..9144d5a56b 100644 --- a/test/integration-test/src/tests/load.rs +++ b/test/integration-test/src/tests/load.rs @@ -8,8 +8,9 @@ use aya::{ maps::{Array, RingBuf}, pin::PinError, programs::{ - FlowDissector, KProbe, LinkOrder, ProbeKind, Program, ProgramError, SchedClassifier, - SchedClassifierAttachment, TcxAttachType, TracePoint, UProbe, Xdp, XdpMode, + FlowDissector, KProbe, LinkOrder, NetkitAttachType, ProbeKind, Program, ProgramError, + SchedClassifier, SchedClassifierAttachment, TcxAttachType, TracePoint, UProbe, Xdp, + XdpMode, flow_dissector::{FlowDissectorLink, FlowDissectorLinkId}, kprobe::{KProbeLink, KProbeLinkId}, links::{FdLink, LinkError, PinnedLink}, @@ -23,6 +24,8 @@ use aya::{ use aya_obj::programs::XdpAttachType; use test_case::test_case; +use crate::utils::NetNsGuard; + const MAX_RETRIES: usize = 100; pub(crate) const RETRY_DURATION: Duration = Duration::from_millis(10); @@ -446,7 +449,6 @@ fn pin_tcx_link() { return; } - use crate::utils::NetNsGuard; let _netns = NetNsGuard::new(); let program_name = "tcx_next"; @@ -491,6 +493,62 @@ fn pin_tcx_link() { assert_unloaded(program_name); } +#[test_log::test] +fn pin_netkit_link() { + let kernel_version = KernelVersion::current().unwrap(); + if kernel_version < KernelVersion::new(6, 7, 0) { + eprintln!("skipping pin_netkit_link test on kernel {kernel_version:?}"); + return; + } + + use crate::utils::create_netkit_link; + let _netns = NetNsGuard::new(); + if let Err(err) = create_netkit_link("nk-aya-0", "nk-aya-1") { + eprintln!("skipping pin_netkit_link test: {err}"); + return; + } + + let program_name = "tcx_next"; + let pin_path = "/sys/fs/bpf/aya-netkit-test-nk0"; + let mut bpf = Ebpf::load(crate::TCX).unwrap(); + let prog: &mut SchedClassifier = bpf.program_mut(program_name).unwrap().try_into().unwrap(); + prog.load().unwrap(); + + let link_id = prog + .attach( + "nk0", + SchedClassifierAttachment::Netkit { + attach_type: NetkitAttachType::Primary, + link_order: LinkOrder::default(), + }, + ) + .unwrap(); + let link = prog.take_link(link_id).unwrap(); + assert_loaded(program_name); + + let fd_link: FdLink = link.try_into().unwrap(); + fd_link.pin(pin_path).unwrap(); + + // Because of the pin, the program is still attached. + prog.unload().unwrap(); + assert_loaded(program_name); + + // Load a new program and atomically replace the old one using attach_to_link. + let mut bpf = Ebpf::load(crate::TCX).unwrap(); + let prog: &mut SchedClassifier = bpf.program_mut(program_name).unwrap().try_into().unwrap(); + prog.load().unwrap(); + + let old_link = PinnedLink::from_pin(pin_path).unwrap(); + let link = FdLink::from(old_link).try_into().unwrap(); + let _link_id = prog.attach_to_link(link).unwrap(); + + assert_loaded(program_name); + + remove_file(pin_path).unwrap(); + drop(bpf); + assert_unloaded(program_name); +} + trait PinProgramOps { fn pin>(&mut self, path: P) -> Result<(), PinError>; fn unpin(&mut self) -> Result<(), std::io::Error>; diff --git a/test/integration-test/src/tests/netkit.rs b/test/integration-test/src/tests/netkit.rs new file mode 100644 index 0000000000..9ccd6a0399 --- /dev/null +++ b/test/integration-test/src/tests/netkit.rs @@ -0,0 +1,108 @@ +use aya::{ + Ebpf, + programs::{ + LinkOrder, NetkitAttachType, ProgramId, SchedClassifier, tc::SchedClassifierAttachment, + }, + util::KernelVersion, +}; + +use crate::utils::{NetNsGuard, create_netkit_link}; + +#[test_log::test] +fn netkit() { + let kernel_version = KernelVersion::current().unwrap(); + if kernel_version < KernelVersion::new(6, 7, 0) { + eprintln!("skipping netkit_attach test on kernel {kernel_version:?}"); + return; + } + let primary = "nk-aya-0"; + let peer = "nk-aya-1"; + + let _netns = NetNsGuard::new(); + if let Err(err) = create_netkit_link(primary, peer) { + eprintln!("skipping netkit_attach test: {err}"); + return; + } + + macro_rules! attach_program_with_link_order_inner { + ($program_name:ident, $link_order:expr) => { + let mut ebpf = Ebpf::load(crate::TCX).unwrap(); + let $program_name: &mut SchedClassifier = + ebpf.program_mut("tcx_next").unwrap().try_into().unwrap(); + $program_name.load().unwrap(); + }; + } + macro_rules! attach_program_with_link_order { + ($program_name:ident, $link_order:expr) => { + attach_program_with_link_order_inner!($program_name, $link_order); + $program_name + .attach( + primary, + SchedClassifierAttachment::Netkit { + attach_type: NetkitAttachType::Primary, + link_order: $link_order, + }, + ) + .unwrap(); + }; + ($program_name:ident, $link_id_name:ident, $link_order:expr) => { + attach_program_with_link_order_inner!($program_name, $link_order); + let $link_id_name = $program_name + .attach( + primary, + SchedClassifierAttachment::Netkit { + attach_type: NetkitAttachType::Primary, + link_order: $link_order, + }, + ) + .unwrap(); + }; + } + + attach_program_with_link_order!(default, LinkOrder::default()); + attach_program_with_link_order!(first, LinkOrder::first()); + attach_program_with_link_order!(last, last_link_id, LinkOrder::last()); + + let last_link = last.take_link(last_link_id).unwrap(); + + attach_program_with_link_order!(before_last, LinkOrder::before_link(&last_link).unwrap()); + attach_program_with_link_order!(after_last, LinkOrder::after_link(&last_link).unwrap()); + + attach_program_with_link_order!(before_default, LinkOrder::before_program(default).unwrap()); + attach_program_with_link_order!(after_default, LinkOrder::after_program(default).unwrap()); + + attach_program_with_link_order!( + before_first, + LinkOrder::before_program_id(unsafe { ProgramId::new(first.info().unwrap().id()) }) + ); + attach_program_with_link_order!( + after_first, + LinkOrder::after_program_id(unsafe { ProgramId::new(first.info().unwrap().id()) }) + ); + + let expected_order = [ + before_first, + first, + after_first, + before_default, + default, + after_default, + before_last, + last, + after_last, + ] + .iter() + .map(|program| program.info().unwrap().id()) + .collect::>(); + + let (revision, got_order) = + SchedClassifier::query_netkit(primary, NetkitAttachType::Primary).unwrap(); + assert_eq!(revision, (expected_order.len() + 1) as u64); + assert_eq!( + got_order + .iter() + .map(aya::programs::ProgramInfo::id) + .collect::>(), + expected_order + ); +} diff --git a/test/integration-test/src/utils.rs b/test/integration-test/src/utils.rs index c0efe3765d..60e81cb06a 100644 --- a/test/integration-test/src/utils.rs +++ b/test/integration-test/src/utils.rs @@ -8,7 +8,7 @@ use std::{ io::{self, Write as _}, os::fd::{AsFd, BorrowedFd}, path::Path, - process, + process::{self, Command}, sync::atomic::{AtomicU64, Ordering}, }; @@ -282,3 +282,35 @@ macro_rules! kernel_assert_eq { } pub(crate) use kernel_assert_eq; + +pub(crate) fn ifindex(if_name: &str) -> io::Result { + let if_name = + CString::new(if_name).map_err(|err| io::Error::new(io::ErrorKind::InvalidInput, err))?; + let idx = unsafe { if_nametoindex(if_name.as_ptr()) }; + if idx == 0 { + Err(io::Error::last_os_error()) + } else { + Ok(idx) + } +} + +pub(crate) fn create_netkit_link(primary: &str, peer: &str) -> io::Result<()> { + let output = Command::new("ip") + .args([ + "link", "add", "name", primary, "type", "netkit", "peer", "name", peer, + ]) + .output()?; + if !output.status.success() { + return Err(io::Error::other(format!( + "`ip link add name {primary} type netkit peer name {peer}` failed: {}", + String::from_utf8_lossy(&output.stderr) + ))); + } + + for if_name in [primary, peer] { + let idx = ifindex(if_name)?; + unsafe { netlink_set_link_up(idx as i32) }.map_err(io::Error::other)?; + } + + Ok(()) +}