diff --git a/aya/src/programs/cgroup_device.rs b/aya/src/programs/cgroup_device.rs index 78b01d05..87c5d4a2 100644 --- a/aya/src/programs/cgroup_device.rs +++ b/aya/src/programs/cgroup_device.rs @@ -8,7 +8,7 @@ use crate::{ bpf_prog_get_fd_by_id, define_link_wrapper, load_program, query, CgroupAttachMode, FdLink, Link, ProgAttachLink, ProgramData, ProgramError, ProgramFd, }, - sys::{bpf_link_create, LinkTarget, SyscallError}, + sys::{bpf_link_create, LinkTarget, ProgQueryTarget, SyscallError}, util::KernelVersion, }; @@ -77,6 +77,7 @@ impl CgroupDevice { BPF_CGROUP_DEVICE, None, mode.into(), + None, ) .map_err(|(_, io_error)| SyscallError { call: "bpf_link_create", @@ -119,7 +120,12 @@ impl CgroupDevice { /// Queries the cgroup for attached programs. pub fn query(target_fd: T) -> Result, ProgramError> { let target_fd = target_fd.as_fd(); - let prog_ids = query(target_fd, BPF_CGROUP_DEVICE, 0, &mut None)?; + let (_, prog_ids) = query( + ProgQueryTarget::Fd(target_fd), + BPF_CGROUP_DEVICE, + 0, + &mut None, + )?; prog_ids .into_iter() diff --git a/aya/src/programs/cgroup_skb.rs b/aya/src/programs/cgroup_skb.rs index 5054a259..637e897a 100644 --- a/aya/src/programs/cgroup_skb.rs +++ b/aya/src/programs/cgroup_skb.rs @@ -105,6 +105,7 @@ impl CgroupSkb { attach_type, None, mode.into(), + None, ) .map_err(|(_, io_error)| SyscallError { call: "bpf_link_create", diff --git a/aya/src/programs/cgroup_sock.rs b/aya/src/programs/cgroup_sock.rs index 878c617f..cc5db22f 100644 --- a/aya/src/programs/cgroup_sock.rs +++ b/aya/src/programs/cgroup_sock.rs @@ -83,6 +83,7 @@ impl CgroupSock { attach_type, None, mode.into(), + None, ) .map_err(|(_, io_error)| SyscallError { call: "bpf_link_create", diff --git a/aya/src/programs/cgroup_sock_addr.rs b/aya/src/programs/cgroup_sock_addr.rs index 876037b3..a31d8cfd 100644 --- a/aya/src/programs/cgroup_sock_addr.rs +++ b/aya/src/programs/cgroup_sock_addr.rs @@ -84,6 +84,7 @@ impl CgroupSockAddr { attach_type, None, mode.into(), + None, ) .map_err(|(_, io_error)| SyscallError { call: "bpf_link_create", diff --git a/aya/src/programs/cgroup_sockopt.rs b/aya/src/programs/cgroup_sockopt.rs index c67dcc5b..ee9d40b2 100644 --- a/aya/src/programs/cgroup_sockopt.rs +++ b/aya/src/programs/cgroup_sockopt.rs @@ -81,6 +81,7 @@ impl CgroupSockopt { attach_type, None, mode.into(), + None, ) .map_err(|(_, io_error)| SyscallError { call: "bpf_link_create", diff --git a/aya/src/programs/cgroup_sysctl.rs b/aya/src/programs/cgroup_sysctl.rs index 94ccd47f..fd997aa1 100644 --- a/aya/src/programs/cgroup_sysctl.rs +++ b/aya/src/programs/cgroup_sysctl.rs @@ -76,6 +76,7 @@ impl CgroupSysctl { BPF_CGROUP_SYSCTL, None, mode.into(), + None, ) .map_err(|(_, io_error)| SyscallError { call: "bpf_link_create", diff --git a/aya/src/programs/extension.rs b/aya/src/programs/extension.rs index 558bef05..68d77f38 100644 --- a/aya/src/programs/extension.rs +++ b/aya/src/programs/extension.rs @@ -103,6 +103,7 @@ impl Extension { BPF_CGROUP_INET_INGRESS, Some(btf_id), 0, + None, ) .map_err(|(_, io_error)| SyscallError { call: "bpf_link_create", @@ -140,6 +141,7 @@ impl Extension { BPF_CGROUP_INET_INGRESS, Some(btf_id), 0, + None, ) .map_err(|(_, io_error)| SyscallError { call: "bpf_link_create", diff --git a/aya/src/programs/links.rs b/aya/src/programs/links.rs index e9afaa87..9e8ac1c0 100644 --- a/aya/src/programs/links.rs +++ b/aya/src/programs/links.rs @@ -10,9 +10,12 @@ use std::{ use thiserror::Error; use crate::{ - generated::{bpf_attach_type, BPF_F_ALLOW_MULTI, BPF_F_ALLOW_OVERRIDE}, + generated::{ + bpf_attach_type, BPF_F_AFTER, BPF_F_ALLOW_MULTI, BPF_F_ALLOW_OVERRIDE, BPF_F_BEFORE, + BPF_F_ID, BPF_F_LINK, BPF_F_REPLACE, + }, pin::PinError, - programs::{ProgramError, ProgramFd}, + programs::{MultiProgLink, MultiProgram, ProgramError, ProgramFd, ProgramId}, sys::{bpf_get_object, bpf_pin_object, bpf_prog_attach, bpf_prog_detach, SyscallError}, }; @@ -116,7 +119,7 @@ pub struct FdLinkId(pub(crate) RawFd); /// /// # Example /// -///```no_run +/// ```no_run /// # let mut bpf = Ebpf::load_file("ebpf_programs.o")?; /// use aya::{Ebpf, programs::{links::FdLink, KProbe}}; /// @@ -329,7 +332,7 @@ macro_rules! define_link_wrapper { pub struct $wrapper(Option<$base>); #[allow(dead_code)] - // allow dead code since currently XDP is the only consumer of inner and + // allow dead code since currently XDP/TC are the only consumers of inner and // into_inner impl $wrapper { fn new(base: $base) -> $wrapper { @@ -394,6 +397,125 @@ pub enum LinkError { SyscallError(#[from] SyscallError), } +#[derive(Debug)] +pub(crate) enum LinkRef { + Id(u32), + Fd(RawFd), +} + +bitflags::bitflags! { + /// Flags which are use to build a set of MprogOptions. + #[derive(Clone, Copy, Debug, Default)] + pub(crate) struct MprogFlags: u32 { + const REPLACE = BPF_F_REPLACE; + const BEFORE = BPF_F_BEFORE; + const AFTER = BPF_F_AFTER; + const ID = BPF_F_ID; + const LINK = BPF_F_LINK; + } +} + +/// Arguments required for interacting with the kernel's multi-prog API. +/// +/// # Minimum kernel version +/// +/// The minimum kernel version required to use this feature is 6.6.0. +/// +/// # Example +/// +/// ```no_run +/// # let mut bpf = aya::Ebpf::load(&[])?; +/// use aya::programs::{tc, SchedClassifier, TcAttachType, tc::TcAttachOptions, LinkOrder}; +/// +/// 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)?; +/// +/// # Ok::<(), aya::EbpfError>(()) +/// ``` +#[derive(Debug)] +pub struct LinkOrder { + pub(crate) link_ref: LinkRef, + pub(crate) flags: MprogFlags, +} + +/// Ensure that default link ordering is to be attached last. +impl Default for LinkOrder { + fn default() -> Self { + Self { + link_ref: LinkRef::Fd(0), + flags: MprogFlags::AFTER, + } + } +} + +impl LinkOrder { + /// Attach before all other links. + pub fn first() -> Self { + Self { + link_ref: LinkRef::Id(0), + flags: MprogFlags::BEFORE, + } + } + + /// Attach after all other links. + pub fn last() -> Self { + Self { + link_ref: LinkRef::Id(0), + flags: MprogFlags::AFTER, + } + } + + /// Attach before the given link. + pub fn before_link(link: &L) -> Result { + Ok(Self { + link_ref: LinkRef::Fd(link.fd()?.as_raw_fd()), + flags: MprogFlags::BEFORE | MprogFlags::LINK, + }) + } + + /// Attach after the given link. + pub fn after_link(link: &L) -> Result { + Ok(Self { + link_ref: LinkRef::Fd(link.fd()?.as_raw_fd()), + flags: MprogFlags::AFTER | MprogFlags::LINK, + }) + } + + /// Attach before the given program. + pub fn before_program(program: &P) -> Result { + Ok(Self { + link_ref: LinkRef::Fd(program.fd()?.as_raw_fd()), + flags: MprogFlags::BEFORE, + }) + } + + /// Attach after the given program. + pub fn after_program(program: &P) -> Result { + Ok(Self { + link_ref: LinkRef::Fd(program.fd()?.as_raw_fd()), + flags: MprogFlags::AFTER, + }) + } + + /// Attach before the program with the given id. + pub fn before_program_id(id: ProgramId) -> Self { + Self { + link_ref: LinkRef::Id(id.0), + flags: MprogFlags::BEFORE | MprogFlags::ID, + } + } + + /// Attach after the program with the given id. + pub fn after_program_id(id: ProgramId) -> Self { + Self { + link_ref: LinkRef::Id(id.0), + flags: MprogFlags::AFTER | MprogFlags::ID, + } + } +} + #[cfg(test)] mod tests { use std::{cell::RefCell, fs::File, rc::Rc}; diff --git a/aya/src/programs/lirc_mode2.rs b/aya/src/programs/lirc_mode2.rs index cc035d1c..fecd261d 100644 --- a/aya/src/programs/lirc_mode2.rs +++ b/aya/src/programs/lirc_mode2.rs @@ -7,7 +7,7 @@ use crate::{ load_program, query, CgroupAttachMode, Link, ProgramData, ProgramError, ProgramFd, ProgramInfo, }, - sys::{bpf_prog_attach, bpf_prog_detach, bpf_prog_get_fd_by_id}, + sys::{bpf_prog_attach, bpf_prog_detach, bpf_prog_get_fd_by_id, ProgQueryTarget}, }; /// A program used to decode IR into key events for a lirc device. @@ -100,7 +100,7 @@ impl LircMode2 { /// Queries the lirc device for attached programs. pub fn query(target_fd: T) -> Result, ProgramError> { let target_fd = target_fd.as_fd(); - let prog_ids = query(target_fd, BPF_LIRC_MODE2, 0, &mut None)?; + let (_, prog_ids) = query(ProgQueryTarget::Fd(target_fd), BPF_LIRC_MODE2, 0, &mut None)?; prog_ids .into_iter() diff --git a/aya/src/programs/mod.rs b/aya/src/programs/mod.rs index fcca69fa..00d61b01 100644 --- a/aya/src/programs/mod.rs +++ b/aya/src/programs/mod.rs @@ -72,7 +72,7 @@ pub mod xdp; use std::{ ffi::CString, io, - os::fd::{AsFd, AsRawFd, BorrowedFd}, + os::fd::{AsFd, BorrowedFd}, path::{Path, PathBuf}, sync::Arc, }; @@ -80,6 +80,7 @@ use std::{ use info::impl_info; pub use info::{loaded_programs, ProgramInfo, ProgramType}; use libc::ENOSPC; +use tc::SchedClassifierLink; use thiserror::Error; // re-export the main items needed to load and attach @@ -94,7 +95,7 @@ pub use crate::programs::{ fentry::FEntry, fexit::FExit, kprobe::{KProbe, KProbeError}, - links::{CgroupAttachMode, Link}, + links::{CgroupAttachMode, Link, LinkOrder}, lirc_mode2::LircMode2, lsm::Lsm, perf_event::{PerfEvent, PerfEventScope, PerfTypeId, SamplePolicy}, @@ -120,7 +121,7 @@ use crate::{ sys::{ bpf_btf_get_fd_by_id, bpf_get_object, bpf_link_get_fd_by_id, bpf_link_get_info_by_fd, bpf_load_program, bpf_pin_object, bpf_prog_get_fd_by_id, bpf_prog_query, iter_link_ids, - retry_with_verifier_logs, EbpfLoadProgramAttrs, SyscallError, + retry_with_verifier_logs, EbpfLoadProgramAttrs, ProgQueryTarget, SyscallError, }, util::KernelVersion, VerifierLogLevel, @@ -238,7 +239,20 @@ impl AsFd for ProgramFd { } } -/// The various eBPF programs. +/// A [`Program`] identifier. +pub struct ProgramId(u32); + +impl ProgramId { + /// Create a new program id. + /// + /// This method is unsafe since it doesn't check that the given `id` is a + /// valid program id. + pub unsafe fn new(id: u32) -> Self { + Self(id) + } +} + +/// eBPF program type. #[derive(Debug)] pub enum Program { /// A [`KProbe`] program @@ -668,28 +682,30 @@ fn load_program( } pub(crate) fn query( - target_fd: BorrowedFd<'_>, + target: ProgQueryTarget<'_>, attach_type: bpf_attach_type, query_flags: u32, attach_flags: &mut Option, -) -> Result, ProgramError> { +) -> Result<(u64, Vec), ProgramError> { let mut prog_ids = vec![0u32; 64]; let mut prog_cnt = prog_ids.len() as u32; + let mut revision = 0; let mut retries = 0; loop { match bpf_prog_query( - target_fd.as_fd().as_raw_fd(), + &target, attach_type, query_flags, attach_flags.as_mut(), &mut prog_ids, &mut prog_cnt, + &mut revision, ) { Ok(_) => { prog_ids.resize(prog_cnt as usize, 0); - return Ok(prog_ids); + return Ok((revision, prog_ids)); } Err((_, io_error)) => { if retries == 0 && io_error.raw_os_error() == Some(ENOSPC) { @@ -797,6 +813,57 @@ impl_fd!( CgroupDevice, ); +/// Trait implemented by the [`Program`] types which support the kernel's +/// [generic multi-prog API](https://github.com/torvalds/linux/commit/053c8e1f235dc3f69d13375b32f4209228e1cb96). +/// +/// # Minimum kernel version +/// +/// The minimum kernel version required to use this feature is 6.6.0. +pub trait MultiProgram { + /// Borrows the file descriptor. + fn fd(&self) -> Result, ProgramError>; +} + +macro_rules! impl_multiprog_fd { + ($($struct_name:ident),+ $(,)?) => { + $( + impl MultiProgram for $struct_name { + fn fd(&self) -> Result, ProgramError> { + Ok(self.fd()?.as_fd()) + } + } + )+ + } +} + +impl_multiprog_fd!(SchedClassifier); + +/// Trait implemented by the [`Link`] types which support the kernel's +/// [generic multi-prog API](https://github.com/torvalds/linux/commit/053c8e1f235dc3f69d13375b32f4209228e1cb96). +/// +/// # Minimum kernel version +/// +/// The minimum kernel version required to use this feature is 6.6.0. +pub trait MultiProgLink { + /// Borrows the file descriptor. + fn fd(&self) -> Result, LinkError>; +} + +macro_rules! impl_multiproglink_fd { + ($($struct_name:ident),+ $(,)?) => { + $( + impl MultiProgLink for $struct_name { + fn fd(&self) -> Result, LinkError> { + let link: &FdLink = self.try_into()?; + Ok(link.fd.as_fd()) + } + } + )+ + } +} + +impl_multiproglink_fd!(SchedClassifierLink); + macro_rules! impl_program_pin{ ($($struct_name:ident),+ $(,)?) => { $( diff --git a/aya/src/programs/perf_attach.rs b/aya/src/programs/perf_attach.rs index f5818944..89f68ace 100644 --- a/aya/src/programs/perf_attach.rs +++ b/aya/src/programs/perf_attach.rs @@ -75,11 +75,18 @@ pub(crate) fn perf_attach( fd: crate::MockableFd, ) -> Result { if FEATURES.bpf_perf_link() { - let link_fd = bpf_link_create(prog_fd, LinkTarget::Fd(fd.as_fd()), BPF_PERF_EVENT, None, 0) - .map_err(|(_, io_error)| SyscallError { - call: "bpf_link_create", - io_error, - })?; + let link_fd = bpf_link_create( + prog_fd, + LinkTarget::Fd(fd.as_fd()), + BPF_PERF_EVENT, + None, + 0, + None, + ) + .map_err(|(_, io_error)| SyscallError { + call: "bpf_link_create", + io_error, + })?; Ok(PerfLinkInner::FdLink(FdLink::new(link_fd))) } else { perf_attach_either(prog_fd, fd, None) diff --git a/aya/src/programs/sk_lookup.rs b/aya/src/programs/sk_lookup.rs index dab0ebc7..065001ce 100644 --- a/aya/src/programs/sk_lookup.rs +++ b/aya/src/programs/sk_lookup.rs @@ -65,11 +65,18 @@ impl SkLookup { let prog_fd = prog_fd.as_fd(); let netns_fd = netns.as_fd(); - let link_fd = bpf_link_create(prog_fd, LinkTarget::Fd(netns_fd), BPF_SK_LOOKUP, None, 0) - .map_err(|(_, io_error)| SyscallError { - call: "bpf_link_create", - io_error, - })?; + let link_fd = bpf_link_create( + prog_fd, + LinkTarget::Fd(netns_fd), + BPF_SK_LOOKUP, + None, + 0, + None, + ) + .map_err(|(_, io_error)| SyscallError { + call: "bpf_link_create", + io_error, + })?; self.data .links .insert(SkLookupLink::new(FdLink::new(link_fd))) diff --git a/aya/src/programs/sock_ops.rs b/aya/src/programs/sock_ops.rs index 60f4e08f..62573215 100644 --- a/aya/src/programs/sock_ops.rs +++ b/aya/src/programs/sock_ops.rs @@ -75,6 +75,7 @@ impl SockOps { attach_type, None, mode.into(), + None, ) .map_err(|(_, io_error)| SyscallError { call: "bpf_link_create", diff --git a/aya/src/programs/tc.rs b/aya/src/programs/tc.rs index c2dd5a13..9f55fe92 100644 --- a/aya/src/programs/tc.rs +++ b/aya/src/programs/tc.rs @@ -8,16 +8,24 @@ use std::{ use thiserror::Error; +use super::{FdLink, ProgramInfo}; use crate::{ generated::{ - bpf_prog_type::BPF_PROG_TYPE_SCHED_CLS, TC_H_CLSACT, TC_H_MIN_EGRESS, TC_H_MIN_INGRESS, + bpf_attach_type::{self, BPF_TCX_EGRESS, BPF_TCX_INGRESS}, + bpf_link_type, + bpf_prog_type::BPF_PROG_TYPE_SCHED_CLS, + TC_H_CLSACT, TC_H_MIN_EGRESS, TC_H_MIN_INGRESS, + }, + programs::{ + define_link_wrapper, load_program, query, Link, LinkError, LinkOrder, ProgramData, + ProgramError, }, - programs::{define_link_wrapper, load_program, Link, ProgramData, ProgramError}, sys::{ + bpf_link_create, bpf_link_get_info_by_fd, bpf_link_update, bpf_prog_get_fd_by_id, netlink_find_filter_with_name, netlink_qdisc_add_clsact, netlink_qdisc_attach, - netlink_qdisc_detach, + netlink_qdisc_detach, LinkTarget, ProgQueryTarget, SyscallError, }, - util::{ifindex_from_ifname, tc_handler_make}, + util::{ifindex_from_ifname, tc_handler_make, KernelVersion}, VerifierLogLevel, }; @@ -89,21 +97,49 @@ 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")] + InvalidLinkOperation, } impl TcAttachType { - pub(crate) fn parent(&self) -> u32 { + pub(crate) fn tc_parent(&self) -> u32 { match self { Self::Custom(parent) => *parent, Self::Ingress => tc_handler_make(TC_H_CLSACT, TC_H_MIN_INGRESS), Self::Egress => tc_handler_make(TC_H_CLSACT, TC_H_MIN_EGRESS), } } + + pub(crate) 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), } -/// Options for SchedClassifier attach -#[derive(Default)] -pub struct TcOptions { +/// Options for SchedClassifier attach via netlink. +#[derive(Debug, Default, Hash, Eq, PartialEq)] +pub struct NlOptions { /// Priority assigned to tc program with lower number = higher priority. /// If set to default (0), the system chooses the next highest priority or 49152 if no filters exist yet pub priority: u16, @@ -118,25 +154,46 @@ impl SchedClassifier { load_program(BPF_PROG_TYPE_SCHED_CLS, &mut self.data) } - /// Attaches the program to the given `interface` using the default options. + /// 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`]. /// /// The returned value can be used to detach, see [SchedClassifier::detach]. /// /// # 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`] + /// When attaching fails, [`ProgramError::SyscallError`] is returned for + /// kernels `>= 6.6.0`, and [`TcError::NetlinkError`] is returned for + /// older kernels. A common cause of netlink attachment failure is not having added + /// the `clsact` qdisc to the given interface, seeĀ [`qdisc_add_clsact`] /// pub fn attach( &mut self, interface: &str, attach_type: TcAttachType, ) -> Result { - self.attach_with_options(interface, attach_type, TcOptions::default()) + if !matches!(attach_type, TcAttachType::Custom(_)) + && KernelVersion::current().unwrap() >= KernelVersion::new(6, 6, 0) + { + self.attach_with_options( + interface, + attach_type, + TcAttachOptions::TcxOrder(LinkOrder::default()), + ) + } else { + self.attach_with_options( + interface, + attach_type, + TcAttachOptions::Netlink(NlOptions::default()), + ) + } } - /// Attaches the program to the given `interface` with options defined in [`TcOptions`]. + /// Attaches the program to the given `interface` with options defined in [`TcAttachOptions`]. /// /// The returned value can be used to detach, see [SchedClassifier::detach]. /// @@ -150,11 +207,11 @@ impl SchedClassifier { &mut self, interface: &str, attach_type: TcAttachType, - options: TcOptions, + options: TcAttachOptions, ) -> Result { let if_index = ifindex_from_ifname(interface) .map_err(|io_error| TcError::NetlinkError { io_error })?; - self.do_attach(if_index as i32, attach_type, options, true) + self.do_attach(if_index, attach_type, options, true) } /// Atomically replaces the program referenced by the provided link. @@ -164,46 +221,98 @@ impl SchedClassifier { &mut self, link: SchedClassifierLink, ) -> Result { - let TcLink { - if_index, - attach_type, - priority, - handle, - } = link.into_inner(); - self.do_attach(if_index, attach_type, TcOptions { priority, handle }, false) + let prog_fd = self.fd()?; + let prog_fd = prog_fd.as_fd(); + match link.into_inner() { + TcLinkInner::FdLink(link) => { + let fd = link.fd; + let link_fd = fd.as_fd(); + + bpf_link_update(link_fd.as_fd(), prog_fd, None, 0).map_err(|(_, io_error)| { + SyscallError { + call: "bpf_link_update", + io_error, + } + })?; + + self.data + .links + .insert(SchedClassifierLink::new(TcLinkInner::FdLink(FdLink::new( + fd, + )))) + } + TcLinkInner::NlLink(NlLink { + if_index, + attach_type, + priority, + handle, + }) => self.do_attach( + if_index, + attach_type, + TcAttachOptions::Netlink(NlOptions { priority, handle }), + false, + ), + } } fn do_attach( &mut self, - if_index: i32, + if_index: u32, attach_type: TcAttachType, - options: TcOptions, + options: TcAttachOptions, create: bool, ) -> Result { let prog_fd = self.fd()?; let prog_fd = prog_fd.as_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, - &attach_type, - prog_fd, - &name, - options.priority, - options.handle, - create, - ) - } - .map_err(|io_error| TcError::NetlinkError { io_error })?; - self.data.links.insert(SchedClassifierLink::new(TcLink { - if_index, - attach_type, - priority, - handle, - })) + 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, + create, + ) + } + .map_err(|io_error| TcError::NetlinkError { io_error })?; + + self.data + .links + .insert(SchedClassifierLink::new(TcLinkInner::NlLink(NlLink { + if_index, + attach_type, + priority, + handle, + }))) + } + TcAttachOptions::TcxOrder(options) => { + let link_fd = bpf_link_create( + prog_fd, + LinkTarget::IfIndex(if_index), + attach_type.tcx_attach_type()?, + None, + options.flags.bits(), + Some(&options.link_ref), + ) + .map_err(|(_, io_error)| SyscallError { + call: "bpf_mprog_attach", + io_error, + })?; + + self.data + .links + .insert(SchedClassifierLink::new(TcLinkInner::FdLink(FdLink::new( + link_fd, + )))) + } + } } /// Detaches the program. @@ -234,42 +343,153 @@ impl SchedClassifier { let data = ProgramData::from_pinned_path(path, VerifierLogLevel::default())?; Ok(Self { data }) } + + /// Queries a given interface for attached TCX programs. + /// + /// # Example + /// + /// ```no_run + /// # use aya::programs::tc::{TcAttachType, SchedClassifier}; + /// # #[derive(Debug, thiserror::Error)] + /// # enum Error { + /// # #[error(transparent)] + /// # Program(#[from] aya::programs::ProgramError), + /// # } + /// let (revision, programs) = SchedClassifier::query_tcx("eth0", TcAttachType::Ingress)?; + /// # Ok::<(), Error>(()) + /// ``` + pub fn query_tcx( + interface: &str, + attach_type: TcAttachType, + ) -> Result<(u64, Vec), ProgramError> { + let if_index = ifindex_from_ifname(interface) + .map_err(|io_error| TcError::NetlinkError { io_error })?; + + let (revision, prog_ids) = query( + ProgQueryTarget::IfIndex(if_index), + attach_type.tcx_attach_type()?, + 0, + &mut None, + )?; + + let prog_infos = prog_ids + .into_iter() + .map(|prog_id| { + let prog_fd = bpf_prog_get_fd_by_id(prog_id)?; + let prog_info = ProgramInfo::new_from_fd(prog_fd.as_fd())?; + Ok::(prog_info) + }) + .collect::>()?; + + Ok((revision, prog_infos)) + } } #[derive(Debug, Hash, Eq, PartialEq)] -pub(crate) struct TcLinkId(i32, TcAttachType, u16, u32); +pub(crate) struct NlLinkId(u32, TcAttachType, u16, u32); #[derive(Debug)] -struct TcLink { - if_index: i32, +pub(crate) struct NlLink { + if_index: u32, attach_type: TcAttachType, priority: u16, handle: u32, } -impl Link for TcLink { - type Id = TcLinkId; +impl Link for NlLink { + type Id = NlLinkId; fn id(&self) -> Self::Id { - TcLinkId(self.if_index, self.attach_type, self.priority, self.handle) + NlLinkId(self.if_index, self.attach_type, self.priority, self.handle) } fn detach(self) -> Result<(), ProgramError> { unsafe { - netlink_qdisc_detach(self.if_index, &self.attach_type, self.priority, self.handle) + netlink_qdisc_detach( + self.if_index as i32, + &self.attach_type, + self.priority, + self.handle, + ) } .map_err(|io_error| TcError::NetlinkError { io_error })?; Ok(()) } } +#[derive(Debug, Hash, Eq, PartialEq)] +pub(crate) enum TcLinkIdInner { + FdLinkId(::Id), + NlLinkId(::Id), +} + +#[derive(Debug)] +pub(crate) enum TcLinkInner { + FdLink(FdLink), + NlLink(NlLink), +} + +impl Link for TcLinkInner { + type Id = TcLinkIdInner; + + fn id(&self) -> Self::Id { + match self { + Self::FdLink(link) => TcLinkIdInner::FdLinkId(link.id()), + Self::NlLink(link) => TcLinkIdInner::NlLinkId(link.id()), + } + } + + fn detach(self) -> Result<(), ProgramError> { + match self { + Self::FdLink(link) => link.detach(), + Self::NlLink(link) => link.detach(), + } + } +} + +impl<'a> TryFrom<&'a SchedClassifierLink> for &'a FdLink { + type Error = LinkError; + + fn try_from(value: &'a SchedClassifierLink) -> Result { + if let TcLinkInner::FdLink(fd) = value.inner() { + Ok(fd) + } else { + Err(LinkError::InvalidLink) + } + } +} + +impl TryFrom for FdLink { + type Error = LinkError; + + fn try_from(value: SchedClassifierLink) -> Result { + if let TcLinkInner::FdLink(fd) = value.into_inner() { + Ok(fd) + } else { + Err(LinkError::InvalidLink) + } + } +} + +impl TryFrom for SchedClassifierLink { + type Error = LinkError; + + fn try_from(fd_link: FdLink) -> Result { + let info = bpf_link_get_info_by_fd(fd_link.fd.as_fd())?; + if info.type_ == (bpf_link_type::BPF_LINK_TYPE_TCX as u32) { + return Ok(Self::new(TcLinkInner::FdLink(fd_link))); + } + Err(LinkError::InvalidLink) + } +} + define_link_wrapper!( /// The link used by [SchedClassifier] programs. SchedClassifierLink, /// The type returned by [SchedClassifier::attach]. Can be passed to [SchedClassifier::detach]. SchedClassifierLinkId, - TcLink, - TcLinkId + TcLinkInner, + TcLinkIdInner ); impl SchedClassifierLink { @@ -311,27 +531,39 @@ impl SchedClassifierLink { handle: u32, ) -> Result { let if_index = ifindex_from_ifname(if_name)?; - Ok(Self(Some(TcLink { - if_index: if_index as i32, + Ok(Self(Some(TcLinkInner::NlLink(NlLink { + if_index, attach_type, priority, handle, - }))) + })))) } /// Returns the attach type. - pub fn attach_type(&self) -> TcAttachType { - self.inner().attach_type + pub fn attach_type(&self) -> Result { + if let TcLinkInner::NlLink(n) = self.inner() { + Ok(n.attach_type) + } else { + Err(TcError::InvalidLinkOperation.into()) + } } /// Returns the allocated priority. If none was provided at attach time, this was allocated for you. - pub fn priority(&self) -> u16 { - self.inner().priority + pub fn priority(&self) -> Result { + if let TcLinkInner::NlLink(n) = self.inner() { + Ok(n.priority) + } else { + Err(TcError::InvalidLinkOperation.into()) + } } /// Returns the assigned handle. If none was provided at attach time, this was allocated for you. - pub fn handle(&self) -> u32 { - self.inner().handle + pub fn handle(&self) -> Result { + if let TcLinkInner::NlLink(n) = self.inner() { + Ok(n.handle) + } else { + Err(TcError::InvalidLinkOperation.into()) + } } } diff --git a/aya/src/programs/xdp.rs b/aya/src/programs/xdp.rs index 5d49f4f1..66b32b32 100644 --- a/aya/src/programs/xdp.rs +++ b/aya/src/programs/xdp.rs @@ -140,7 +140,7 @@ impl Xdp { // 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 trhough `Xdp:try_from(Program)` does not set any fd. + // 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(); @@ -150,6 +150,7 @@ impl Xdp { attach_type, None, flags.bits(), + None, ) .map_err(|(_, io_error)| SyscallError { call: "bpf_link_create", diff --git a/aya/src/sys/bpf.rs b/aya/src/sys/bpf.rs index 2d305246..a405b3be 100644 --- a/aya/src/sys/bpf.rs +++ b/aya/src/sys/bpf.rs @@ -30,6 +30,7 @@ use crate::{ }, copy_instructions, }, + programs::links::LinkRef, sys::{syscall, SysResult, Syscall, SyscallError}, util::KernelVersion, Btf, Pod, VerifierLogLevel, BPF_OBJ_NAME_LEN, FEATURES, @@ -385,6 +386,7 @@ pub(crate) fn bpf_link_create( attach_type: bpf_attach_type, btf_id: Option, flags: u32, + link_ref: Option<&LinkRef>, ) -> SysResult { let mut attr = unsafe { mem::zeroed::() }; @@ -399,11 +401,32 @@ pub(crate) fn bpf_link_create( } }; attr.link_create.attach_type = attach_type as u32; - attr.link_create.flags = flags; + if let Some(btf_id) = btf_id { attr.link_create.__bindgen_anon_3.target_btf_id = btf_id; } + attr.link_create.flags = flags; + + // since kernel 6.6 + match link_ref { + Some(LinkRef::Fd(fd)) => { + attr.link_create + .__bindgen_anon_3 + .tcx + .__bindgen_anon_1 + .relative_fd = fd.to_owned() as u32; + } + Some(LinkRef::Id(id)) => { + attr.link_create + .__bindgen_anon_3 + .tcx + .__bindgen_anon_1 + .relative_id = id.to_owned(); + } + None => {} + }; + // SAFETY: BPF_LINK_CREATE returns a new file descriptor. unsafe { fd_sys_bpf(bpf_cmd::BPF_LINK_CREATE, &mut attr) } } @@ -475,25 +498,39 @@ pub(crate) fn bpf_prog_detach( Ok(()) } +#[derive(Debug)] +pub(crate) enum ProgQueryTarget<'a> { + Fd(BorrowedFd<'a>), + IfIndex(u32), +} + pub(crate) fn bpf_prog_query( - target_fd: RawFd, + target: &ProgQueryTarget<'_>, attach_type: bpf_attach_type, query_flags: u32, attach_flags: Option<&mut u32>, prog_ids: &mut [u32], prog_cnt: &mut u32, + revision: &mut u64, ) -> SysResult { let mut attr = unsafe { mem::zeroed::() }; - attr.query.__bindgen_anon_1.target_fd = target_fd as u32; + match target { + ProgQueryTarget::Fd(fd) => { + attr.query.__bindgen_anon_1.target_fd = fd.as_raw_fd() as u32; + } + ProgQueryTarget::IfIndex(ifindex) => { + attr.query.__bindgen_anon_1.target_ifindex = *ifindex; + } + } attr.query.attach_type = attach_type as u32; attr.query.query_flags = query_flags; attr.query.__bindgen_anon_2.prog_cnt = prog_ids.len() as u32; attr.query.prog_ids = prog_ids.as_mut_ptr() as u64; - let ret = sys_bpf(bpf_cmd::BPF_PROG_QUERY, &mut attr); *prog_cnt = unsafe { attr.query.__bindgen_anon_2.prog_cnt }; + *revision = unsafe { attr.query.revision }; if let Some(attach_flags) = attach_flags { *attach_flags = unsafe { attr.query.attach_flags }; @@ -826,7 +863,7 @@ pub(crate) fn is_perf_link_supported() -> bool { let fd = fd.as_fd(); matches!( // Uses an invalid target FD so we get EBADF if supported. - bpf_link_create(fd, LinkTarget::IfIndex(u32::MAX), bpf_attach_type::BPF_PERF_EVENT, None, 0), + bpf_link_create(fd, LinkTarget::IfIndex(u32::MAX), bpf_attach_type::BPF_PERF_EVENT, None, 0, None), // Returns EINVAL if unsupported. EBADF if supported. Err((_, e)) if e.raw_os_error() == Some(libc::EBADF), ) diff --git a/aya/src/sys/netlink.rs b/aya/src/sys/netlink.rs index 52215555..11b99471 100644 --- a/aya/src/sys/netlink.rs +++ b/aya/src/sys/netlink.rs @@ -146,7 +146,7 @@ pub(crate) unsafe fn netlink_qdisc_attach( req.tc_info.tcm_family = AF_UNSPEC as u8; req.tc_info.tcm_handle = handle; // auto-assigned, if zero req.tc_info.tcm_ifindex = if_index; - req.tc_info.tcm_parent = attach_type.parent(); + req.tc_info.tcm_parent = attach_type.tc_parent(); req.tc_info.tcm_info = tc_handler_make((priority as u32) << 16, htons(ETH_P_ALL as u16) as u32); let attrs_buf = request_attributes(&mut req, nlmsg_len); @@ -207,7 +207,7 @@ pub(crate) unsafe fn netlink_qdisc_detach( req.tc_info.tcm_family = AF_UNSPEC as u8; req.tc_info.tcm_handle = handle; // auto-assigned, if zero req.tc_info.tcm_info = tc_handler_make((priority as u32) << 16, htons(ETH_P_ALL as u16) as u32); - req.tc_info.tcm_parent = attach_type.parent(); + req.tc_info.tcm_parent = attach_type.tc_parent(); req.tc_info.tcm_ifindex = if_index; sock.send(&bytes_of(&req)[..req.header.nlmsg_len as usize])?; @@ -236,7 +236,7 @@ pub(crate) unsafe fn netlink_find_filter_with_name( req.tc_info.tcm_family = AF_UNSPEC as u8; req.tc_info.tcm_handle = 0; // auto-assigned, if zero req.tc_info.tcm_ifindex = if_index; - req.tc_info.tcm_parent = attach_type.parent(); + req.tc_info.tcm_parent = attach_type.tc_parent(); let sock = NetlinkSocket::open()?; sock.send(&bytes_of(&req)[..req.header.nlmsg_len as usize])?; diff --git a/test/integration-ebpf/Cargo.toml b/test/integration-ebpf/Cargo.toml index 102198ae..2649871b 100644 --- a/test/integration-ebpf/Cargo.toml +++ b/test/integration-ebpf/Cargo.toml @@ -37,6 +37,10 @@ path = "src/pass.rs" name = "test" path = "src/test.rs" +[[bin]] +name = "tcx" +path = "src/tcx.rs" + [[bin]] name = "relocations" path = "src/relocations.rs" diff --git a/test/integration-ebpf/src/tcx.rs b/test/integration-ebpf/src/tcx.rs new file mode 100644 index 00000000..5ed3c211 --- /dev/null +++ b/test/integration-ebpf/src/tcx.rs @@ -0,0 +1,15 @@ +#![no_std] +#![no_main] + +use aya_ebpf::{bindings::tcx_action_base::TCX_NEXT, macros::classifier, programs::TcContext}; + +#[classifier] +pub fn tcx_next(_ctx: TcContext) -> i32 { + TCX_NEXT +} + +#[cfg(not(test))] +#[panic_handler] +fn panic(_info: &core::panic::PanicInfo) -> ! { + loop {} +} diff --git a/test/integration-test/src/lib.rs b/test/integration-test/src/lib.rs index fc31e6c2..c20926dd 100644 --- a/test/integration-test/src/lib.rs +++ b/test/integration-test/src/lib.rs @@ -14,6 +14,7 @@ pub const LOG: &[u8] = include_bytes_aligned!(concat!(env!("OUT_DIR"), "/log")); pub const MAP_TEST: &[u8] = include_bytes_aligned!(concat!(env!("OUT_DIR"), "/map_test")); pub const NAME_TEST: &[u8] = include_bytes_aligned!(concat!(env!("OUT_DIR"), "/name_test")); pub const PASS: &[u8] = include_bytes_aligned!(concat!(env!("OUT_DIR"), "/pass")); +pub const TCX: &[u8] = include_bytes_aligned!(concat!(env!("OUT_DIR"), "/tcx")); pub const TEST: &[u8] = include_bytes_aligned!(concat!(env!("OUT_DIR"), "/test")); pub const RELOCATIONS: &[u8] = include_bytes_aligned!(concat!(env!("OUT_DIR"), "/relocations")); pub const TWO_PROGS: &[u8] = include_bytes_aligned!(concat!(env!("OUT_DIR"), "/two_progs")); diff --git a/test/integration-test/src/tests.rs b/test/integration-test/src/tests.rs index db99e095..61f490cf 100644 --- a/test/integration-test/src/tests.rs +++ b/test/integration-test/src/tests.rs @@ -8,4 +8,5 @@ mod rbpf; mod relocations; mod ring_buf; mod smoke; +mod tcx; mod xdp; diff --git a/test/integration-test/src/tests/tcx.rs b/test/integration-test/src/tests/tcx.rs new file mode 100644 index 00000000..a58565d0 --- /dev/null +++ b/test/integration-test/src/tests/tcx.rs @@ -0,0 +1,102 @@ +use aya::{ + programs::{tc::TcAttachOptions, LinkOrder, ProgramId, SchedClassifier, TcAttachType}, + util::KernelVersion, + Ebpf, +}; +use test_log::test; + +use crate::utils::NetNsGuard; + +#[test] +fn tcx() { + let kernel_version = KernelVersion::current().unwrap(); + if kernel_version < KernelVersion::new(6, 6, 0) { + eprintln!("skipping tcx_attach test on kernel {kernel_version:?}"); + return; + } + + let _netns = NetNsGuard::new(); + + // We need a dedicated `Ebpf` instance for each program that we load + // since TCX does not allow the same program ID to be attached multiple + // times to the same interface/direction. + // + // Variables declared within this macro are within a closure scope to avoid + // variable name conflicts. + // + // Yields a tuple of the `Ebpf` which must remain in scope for the duration + // of the test, and the link ID of the attached program. + 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_with_options( + "lo", + TcAttachType::Ingress, + TcAttachOptions::TcxOrder($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( + "lo", + TcAttachType::Ingress, + TcAttachOptions::TcxOrder($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_tcx("lo", TcAttachType::Ingress).unwrap(); + assert_eq!(revision, (expected_order.len() + 1) as u64); + assert_eq!( + got_order.iter().map(|p| p.id()).collect::>(), + expected_order + ); +} diff --git a/xtask/public-api/aya.txt b/xtask/public-api/aya.txt index 52ebb6bb..3d5c5874 100644 --- a/xtask/public-api/aya.txt +++ b/xtask/public-api/aya.txt @@ -3907,6 +3907,9 @@ pub fn aya::programs::kprobe::KProbeLink::try_from(fd_link: aya::programs::links impl core::convert::TryFrom for aya::programs::perf_event::PerfEventLink pub type aya::programs::perf_event::PerfEventLink::Error = aya::programs::links::LinkError pub fn aya::programs::perf_event::PerfEventLink::try_from(fd_link: aya::programs::links::FdLink) -> core::result::Result +impl core::convert::TryFrom for aya::programs::tc::SchedClassifierLink +pub type aya::programs::tc::SchedClassifierLink::Error = aya::programs::links::LinkError +pub fn aya::programs::tc::SchedClassifierLink::try_from(fd_link: aya::programs::links::FdLink) -> core::result::Result impl core::convert::TryFrom for aya::programs::trace_point::TracePointLink pub type aya::programs::trace_point::TracePointLink::Error = aya::programs::links::LinkError pub fn aya::programs::trace_point::TracePointLink::try_from(fd_link: aya::programs::links::FdLink) -> core::result::Result @@ -3919,6 +3922,9 @@ pub fn aya::programs::xdp::XdpLink::try_from(fd_link: aya::programs::links::FdLi impl core::convert::TryFrom for aya::programs::links::FdLink pub type aya::programs::links::FdLink::Error = aya::programs::links::LinkError pub fn aya::programs::links::FdLink::try_from(value: aya::programs::perf_event::PerfEventLink) -> core::result::Result +impl core::convert::TryFrom for aya::programs::links::FdLink +pub type aya::programs::links::FdLink::Error = aya::programs::links::LinkError +pub fn aya::programs::links::FdLink::try_from(value: aya::programs::tc::SchedClassifierLink) -> core::result::Result impl core::convert::TryFrom for aya::programs::links::FdLink pub type aya::programs::links::FdLink::Error = aya::programs::links::LinkError pub fn aya::programs::links::FdLink::try_from(value: aya::programs::trace_point::TracePointLink) -> core::result::Result @@ -3930,6 +3936,9 @@ pub type aya::programs::links::FdLink::Error = aya::programs::links::LinkError pub fn aya::programs::links::FdLink::try_from(value: aya::programs::xdp::XdpLink) -> core::result::Result impl core::fmt::Debug for aya::programs::links::FdLink pub fn aya::programs::links::FdLink::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result +impl<'a> core::convert::TryFrom<&'a aya::programs::tc::SchedClassifierLink> for &'a aya::programs::links::FdLink +pub type &'a aya::programs::links::FdLink::Error = aya::programs::links::LinkError +pub fn &'a aya::programs::links::FdLink::try_from(value: &'a aya::programs::tc::SchedClassifierLink) -> core::result::Result impl core::marker::Freeze for aya::programs::links::FdLink impl core::marker::Send for aya::programs::links::FdLink impl core::marker::Sync for aya::programs::links::FdLink @@ -3985,6 +3994,42 @@ impl core::borrow::BorrowMut for aya::programs::links::FdLinkId where T: c pub fn aya::programs::links::FdLinkId::borrow_mut(&mut self) -> &mut T impl core::convert::From for aya::programs::links::FdLinkId pub fn aya::programs::links::FdLinkId::from(t: T) -> T +pub struct aya::programs::links::LinkOrder +impl aya::programs::links::LinkOrder +pub fn aya::programs::links::LinkOrder::after_link(link: &L) -> core::result::Result +pub fn aya::programs::links::LinkOrder::after_program(program: &P) -> core::result::Result +pub fn aya::programs::links::LinkOrder::after_program_id(id: aya::programs::ProgramId) -> Self +pub fn aya::programs::links::LinkOrder::before_link(link: &L) -> core::result::Result +pub fn aya::programs::links::LinkOrder::before_program(program: &P) -> core::result::Result +pub fn aya::programs::links::LinkOrder::before_program_id(id: aya::programs::ProgramId) -> Self +pub fn aya::programs::links::LinkOrder::first() -> Self +pub fn aya::programs::links::LinkOrder::last() -> Self +impl core::default::Default for aya::programs::links::LinkOrder +pub fn aya::programs::links::LinkOrder::default() -> Self +impl core::fmt::Debug for aya::programs::links::LinkOrder +pub fn aya::programs::links::LinkOrder::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result +impl core::marker::Freeze for aya::programs::links::LinkOrder +impl core::marker::Send for aya::programs::links::LinkOrder +impl core::marker::Sync for aya::programs::links::LinkOrder +impl core::marker::Unpin for aya::programs::links::LinkOrder +impl core::panic::unwind_safe::RefUnwindSafe for aya::programs::links::LinkOrder +impl core::panic::unwind_safe::UnwindSafe for aya::programs::links::LinkOrder +impl core::convert::Into for aya::programs::links::LinkOrder where U: core::convert::From +pub fn aya::programs::links::LinkOrder::into(self) -> U +impl core::convert::TryFrom for aya::programs::links::LinkOrder where U: core::convert::Into +pub type aya::programs::links::LinkOrder::Error = core::convert::Infallible +pub fn aya::programs::links::LinkOrder::try_from(value: U) -> core::result::Result>::Error> +impl core::convert::TryInto for aya::programs::links::LinkOrder where U: core::convert::TryFrom +pub type aya::programs::links::LinkOrder::Error = >::Error +pub fn aya::programs::links::LinkOrder::try_into(self) -> core::result::Result>::Error> +impl core::any::Any for aya::programs::links::LinkOrder where T: 'static + core::marker::Sized +pub fn aya::programs::links::LinkOrder::type_id(&self) -> core::any::TypeId +impl core::borrow::Borrow for aya::programs::links::LinkOrder where T: core::marker::Sized +pub fn aya::programs::links::LinkOrder::borrow(&self) -> &T +impl core::borrow::BorrowMut for aya::programs::links::LinkOrder where T: core::marker::Sized +pub fn aya::programs::links::LinkOrder::borrow_mut(&mut self) -> &mut T +impl core::convert::From for aya::programs::links::LinkOrder +pub fn aya::programs::links::LinkOrder::from(t: T) -> T pub struct aya::programs::links::PinnedLink impl aya::programs::links::PinnedLink pub fn aya::programs::links::PinnedLink::from_pin>(path: P) -> core::result::Result @@ -5504,6 +5549,33 @@ pub fn aya::programs::socket_filter::SocketFilterLinkId::borrow_mut(&mut self) - impl core::convert::From for aya::programs::socket_filter::SocketFilterLinkId pub fn aya::programs::socket_filter::SocketFilterLinkId::from(t: T) -> T 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::panic::unwind_safe::RefUnwindSafe for aya::programs::tc::TcAttachOptions +impl core::panic::unwind_safe::UnwindSafe for aya::programs::tc::TcAttachOptions +impl core::convert::Into for aya::programs::tc::TcAttachOptions where U: core::convert::From +pub fn aya::programs::tc::TcAttachOptions::into(self) -> U +impl core::convert::TryFrom for aya::programs::tc::TcAttachOptions where U: core::convert::Into +pub type aya::programs::tc::TcAttachOptions::Error = core::convert::Infallible +pub fn aya::programs::tc::TcAttachOptions::try_from(value: U) -> core::result::Result>::Error> +impl core::convert::TryInto for aya::programs::tc::TcAttachOptions where U: core::convert::TryFrom +pub type aya::programs::tc::TcAttachOptions::Error = >::Error +pub fn aya::programs::tc::TcAttachOptions::try_into(self) -> core::result::Result>::Error> +impl core::any::Any for aya::programs::tc::TcAttachOptions where T: 'static + core::marker::Sized +pub fn aya::programs::tc::TcAttachOptions::type_id(&self) -> core::any::TypeId +impl core::borrow::Borrow for aya::programs::tc::TcAttachOptions where T: core::marker::Sized +pub fn aya::programs::tc::TcAttachOptions::borrow(&self) -> &T +impl core::borrow::BorrowMut for aya::programs::tc::TcAttachOptions where T: core::marker::Sized +pub fn aya::programs::tc::TcAttachOptions::borrow_mut(&mut self) -> &mut T +impl core::convert::From for aya::programs::tc::TcAttachOptions +pub fn aya::programs::tc::TcAttachOptions::from(t: T) -> T pub enum aya::programs::tc::TcAttachType pub aya::programs::tc::TcAttachType::Custom(u32) pub aya::programs::tc::TcAttachType::Egress @@ -5551,6 +5623,8 @@ impl core::convert::From for aya::programs::tc::TcAttachType pub fn aya::programs::tc::TcAttachType::from(t: T) -> T 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::NetlinkError pub aya::programs::tc::TcError::NetlinkError::io_error: std::io::error::Error impl core::convert::From for aya::programs::ProgramError @@ -5585,14 +5659,52 @@ impl core::borrow::BorrowMut for aya::programs::tc::TcError where T: core: pub fn aya::programs::tc::TcError::borrow_mut(&mut self) -> &mut T impl core::convert::From for aya::programs::tc::TcError pub fn aya::programs::tc::TcError::from(t: T) -> T +pub struct aya::programs::tc::NlOptions +pub aya::programs::tc::NlOptions::handle: u32 +pub aya::programs::tc::NlOptions::priority: u16 +impl core::cmp::Eq for aya::programs::tc::NlOptions +impl core::cmp::PartialEq for aya::programs::tc::NlOptions +pub fn aya::programs::tc::NlOptions::eq(&self, other: &aya::programs::tc::NlOptions) -> bool +impl core::default::Default for aya::programs::tc::NlOptions +pub fn aya::programs::tc::NlOptions::default() -> aya::programs::tc::NlOptions +impl core::fmt::Debug for aya::programs::tc::NlOptions +pub fn aya::programs::tc::NlOptions::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result +impl core::hash::Hash for aya::programs::tc::NlOptions +pub fn aya::programs::tc::NlOptions::hash<__H: core::hash::Hasher>(&self, state: &mut __H) +impl core::marker::StructuralPartialEq for aya::programs::tc::NlOptions +impl core::marker::Freeze for aya::programs::tc::NlOptions +impl core::marker::Send for aya::programs::tc::NlOptions +impl core::marker::Sync for aya::programs::tc::NlOptions +impl core::marker::Unpin for aya::programs::tc::NlOptions +impl core::panic::unwind_safe::RefUnwindSafe for aya::programs::tc::NlOptions +impl core::panic::unwind_safe::UnwindSafe for aya::programs::tc::NlOptions +impl equivalent::Equivalent for aya::programs::tc::NlOptions where Q: core::cmp::Eq + core::marker::Sized, K: core::borrow::Borrow + core::marker::Sized +pub fn aya::programs::tc::NlOptions::equivalent(&self, key: &K) -> bool +impl core::convert::Into for aya::programs::tc::NlOptions where U: core::convert::From +pub fn aya::programs::tc::NlOptions::into(self) -> U +impl core::convert::TryFrom for aya::programs::tc::NlOptions where U: core::convert::Into +pub type aya::programs::tc::NlOptions::Error = core::convert::Infallible +pub fn aya::programs::tc::NlOptions::try_from(value: U) -> core::result::Result>::Error> +impl core::convert::TryInto for aya::programs::tc::NlOptions where U: core::convert::TryFrom +pub type aya::programs::tc::NlOptions::Error = >::Error +pub fn aya::programs::tc::NlOptions::try_into(self) -> core::result::Result>::Error> +impl core::any::Any for aya::programs::tc::NlOptions where T: 'static + core::marker::Sized +pub fn aya::programs::tc::NlOptions::type_id(&self) -> core::any::TypeId +impl core::borrow::Borrow for aya::programs::tc::NlOptions where T: core::marker::Sized +pub fn aya::programs::tc::NlOptions::borrow(&self) -> &T +impl core::borrow::BorrowMut for aya::programs::tc::NlOptions where T: core::marker::Sized +pub fn aya::programs::tc::NlOptions::borrow_mut(&mut self) -> &mut T +impl core::convert::From for aya::programs::tc::NlOptions +pub fn aya::programs::tc::NlOptions::from(t: T) -> T pub struct aya::programs::tc::SchedClassifier impl aya::programs::tc::SchedClassifier 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_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::TcOptions) -> 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::detach(&mut self, link_id: aya::programs::tc::SchedClassifierLinkId) -> core::result::Result<(), aya::programs::ProgramError> 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::take_link(&mut self, link_id: aya::programs::tc::SchedClassifierLinkId) -> core::result::Result impl aya::programs::tc::SchedClassifier pub fn aya::programs::tc::SchedClassifier::fd(&self) -> core::result::Result<&aya::programs::ProgramFd, aya::programs::ProgramError> @@ -5603,6 +5715,8 @@ pub fn aya::programs::tc::SchedClassifier::pin core::result::Result<(), std::io::error::Error> impl aya::programs::tc::SchedClassifier pub fn aya::programs::tc::SchedClassifier::unload(&mut self) -> core::result::Result<(), aya::programs::ProgramError> +impl aya::programs::MultiProgram for aya::programs::tc::SchedClassifier +pub fn aya::programs::tc::SchedClassifier::fd(&self) -> core::result::Result, aya::programs::ProgramError> impl core::fmt::Debug for aya::programs::tc::SchedClassifier pub fn aya::programs::tc::SchedClassifier::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result impl core::ops::drop::Drop for aya::programs::tc::SchedClassifier @@ -5637,18 +5751,29 @@ impl core::convert::From for aya::programs::tc::SchedClassifier pub fn aya::programs::tc::SchedClassifier::from(t: T) -> T pub struct aya::programs::tc::SchedClassifierLink(_) impl aya::programs::tc::SchedClassifierLink -pub fn aya::programs::tc::SchedClassifierLink::attach_type(&self) -> aya::programs::tc::TcAttachType +pub fn aya::programs::tc::SchedClassifierLink::attach_type(&self) -> core::result::Result pub fn aya::programs::tc::SchedClassifierLink::attached(if_name: &str, attach_type: aya::programs::tc::TcAttachType, priority: u16, handle: u32) -> core::result::Result -pub fn aya::programs::tc::SchedClassifierLink::handle(&self) -> u32 -pub fn aya::programs::tc::SchedClassifierLink::priority(&self) -> u16 +pub fn aya::programs::tc::SchedClassifierLink::handle(&self) -> core::result::Result +pub fn aya::programs::tc::SchedClassifierLink::priority(&self) -> core::result::Result +impl aya::programs::MultiProgLink for aya::programs::tc::SchedClassifierLink +pub fn aya::programs::tc::SchedClassifierLink::fd(&self) -> core::result::Result, aya::programs::links::LinkError> impl aya::programs::links::Link for aya::programs::tc::SchedClassifierLink pub type aya::programs::tc::SchedClassifierLink::Id = aya::programs::tc::SchedClassifierLinkId pub fn aya::programs::tc::SchedClassifierLink::detach(self) -> core::result::Result<(), aya::programs::ProgramError> pub fn aya::programs::tc::SchedClassifierLink::id(&self) -> Self::Id +impl core::convert::TryFrom for aya::programs::tc::SchedClassifierLink +pub type aya::programs::tc::SchedClassifierLink::Error = aya::programs::links::LinkError +pub fn aya::programs::tc::SchedClassifierLink::try_from(fd_link: aya::programs::links::FdLink) -> core::result::Result +impl core::convert::TryFrom for aya::programs::links::FdLink +pub type aya::programs::links::FdLink::Error = aya::programs::links::LinkError +pub fn aya::programs::links::FdLink::try_from(value: aya::programs::tc::SchedClassifierLink) -> core::result::Result impl core::fmt::Debug for aya::programs::tc::SchedClassifierLink pub fn aya::programs::tc::SchedClassifierLink::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result impl core::ops::drop::Drop for aya::programs::tc::SchedClassifierLink pub fn aya::programs::tc::SchedClassifierLink::drop(&mut self) +impl<'a> core::convert::TryFrom<&'a aya::programs::tc::SchedClassifierLink> for &'a aya::programs::links::FdLink +pub type &'a aya::programs::links::FdLink::Error = aya::programs::links::LinkError +pub fn &'a aya::programs::links::FdLink::try_from(value: &'a aya::programs::tc::SchedClassifierLink) -> core::result::Result impl core::marker::Freeze for aya::programs::tc::SchedClassifierLink impl core::marker::Send for aya::programs::tc::SchedClassifierLink impl core::marker::Sync for aya::programs::tc::SchedClassifierLink @@ -5704,33 +5829,6 @@ impl core::borrow::BorrowMut for aya::programs::tc::SchedClassifierLinkId pub fn aya::programs::tc::SchedClassifierLinkId::borrow_mut(&mut self) -> &mut T impl core::convert::From for aya::programs::tc::SchedClassifierLinkId pub fn aya::programs::tc::SchedClassifierLinkId::from(t: T) -> T -pub struct aya::programs::tc::TcOptions -pub aya::programs::tc::TcOptions::handle: u32 -pub aya::programs::tc::TcOptions::priority: u16 -impl core::default::Default for aya::programs::tc::TcOptions -pub fn aya::programs::tc::TcOptions::default() -> aya::programs::tc::TcOptions -impl core::marker::Freeze for aya::programs::tc::TcOptions -impl core::marker::Send for aya::programs::tc::TcOptions -impl core::marker::Sync for aya::programs::tc::TcOptions -impl core::marker::Unpin for aya::programs::tc::TcOptions -impl core::panic::unwind_safe::RefUnwindSafe for aya::programs::tc::TcOptions -impl core::panic::unwind_safe::UnwindSafe for aya::programs::tc::TcOptions -impl core::convert::Into for aya::programs::tc::TcOptions where U: core::convert::From -pub fn aya::programs::tc::TcOptions::into(self) -> U -impl core::convert::TryFrom for aya::programs::tc::TcOptions where U: core::convert::Into -pub type aya::programs::tc::TcOptions::Error = core::convert::Infallible -pub fn aya::programs::tc::TcOptions::try_from(value: U) -> core::result::Result>::Error> -impl core::convert::TryInto for aya::programs::tc::TcOptions where U: core::convert::TryFrom -pub type aya::programs::tc::TcOptions::Error = >::Error -pub fn aya::programs::tc::TcOptions::try_into(self) -> core::result::Result>::Error> -impl core::any::Any for aya::programs::tc::TcOptions where T: 'static + core::marker::Sized -pub fn aya::programs::tc::TcOptions::type_id(&self) -> core::any::TypeId -impl core::borrow::Borrow for aya::programs::tc::TcOptions where T: core::marker::Sized -pub fn aya::programs::tc::TcOptions::borrow(&self) -> &T -impl core::borrow::BorrowMut for aya::programs::tc::TcOptions where T: core::marker::Sized -pub fn aya::programs::tc::TcOptions::borrow_mut(&mut self) -> &mut T -impl core::convert::From for aya::programs::tc::TcOptions -pub fn aya::programs::tc::TcOptions::from(t: T) -> T pub fn aya::programs::tc::qdisc_add_clsact(if_name: &str) -> core::result::Result<(), std::io::error::Error> pub fn aya::programs::tc::qdisc_detach_program(if_name: &str, attach_type: aya::programs::tc::TcAttachType, name: &str) -> core::result::Result<(), std::io::error::Error> pub mod aya::programs::tp_btf @@ -7208,6 +7306,8 @@ impl core::convert::From for aya::programs::tc::TcAttachType pub fn aya::programs::tc::TcAttachType::from(t: T) -> T 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::NetlinkError pub aya::programs::TcError::NetlinkError::io_error: std::io::error::Error impl core::convert::From for aya::programs::ProgramError @@ -7894,6 +7994,42 @@ impl core::borrow::BorrowMut for aya::programs::kprobe::KProbe where T: co pub fn aya::programs::kprobe::KProbe::borrow_mut(&mut self) -> &mut T impl core::convert::From for aya::programs::kprobe::KProbe pub fn aya::programs::kprobe::KProbe::from(t: T) -> T +pub struct aya::programs::LinkOrder +impl aya::programs::links::LinkOrder +pub fn aya::programs::links::LinkOrder::after_link(link: &L) -> core::result::Result +pub fn aya::programs::links::LinkOrder::after_program(program: &P) -> core::result::Result +pub fn aya::programs::links::LinkOrder::after_program_id(id: aya::programs::ProgramId) -> Self +pub fn aya::programs::links::LinkOrder::before_link(link: &L) -> core::result::Result +pub fn aya::programs::links::LinkOrder::before_program(program: &P) -> core::result::Result +pub fn aya::programs::links::LinkOrder::before_program_id(id: aya::programs::ProgramId) -> Self +pub fn aya::programs::links::LinkOrder::first() -> Self +pub fn aya::programs::links::LinkOrder::last() -> Self +impl core::default::Default for aya::programs::links::LinkOrder +pub fn aya::programs::links::LinkOrder::default() -> Self +impl core::fmt::Debug for aya::programs::links::LinkOrder +pub fn aya::programs::links::LinkOrder::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result +impl core::marker::Freeze for aya::programs::links::LinkOrder +impl core::marker::Send for aya::programs::links::LinkOrder +impl core::marker::Sync for aya::programs::links::LinkOrder +impl core::marker::Unpin for aya::programs::links::LinkOrder +impl core::panic::unwind_safe::RefUnwindSafe for aya::programs::links::LinkOrder +impl core::panic::unwind_safe::UnwindSafe for aya::programs::links::LinkOrder +impl core::convert::Into for aya::programs::links::LinkOrder where U: core::convert::From +pub fn aya::programs::links::LinkOrder::into(self) -> U +impl core::convert::TryFrom for aya::programs::links::LinkOrder where U: core::convert::Into +pub type aya::programs::links::LinkOrder::Error = core::convert::Infallible +pub fn aya::programs::links::LinkOrder::try_from(value: U) -> core::result::Result>::Error> +impl core::convert::TryInto for aya::programs::links::LinkOrder where U: core::convert::TryFrom +pub type aya::programs::links::LinkOrder::Error = >::Error +pub fn aya::programs::links::LinkOrder::try_into(self) -> core::result::Result>::Error> +impl core::any::Any for aya::programs::links::LinkOrder where T: 'static + core::marker::Sized +pub fn aya::programs::links::LinkOrder::type_id(&self) -> core::any::TypeId +impl core::borrow::Borrow for aya::programs::links::LinkOrder where T: core::marker::Sized +pub fn aya::programs::links::LinkOrder::borrow(&self) -> &T +impl core::borrow::BorrowMut for aya::programs::links::LinkOrder where T: core::marker::Sized +pub fn aya::programs::links::LinkOrder::borrow_mut(&mut self) -> &mut T +impl core::convert::From for aya::programs::links::LinkOrder +pub fn aya::programs::links::LinkOrder::from(t: T) -> T pub struct aya::programs::LircMode2 impl aya::programs::lirc_mode2::LircMode2 pub fn aya::programs::lirc_mode2::LircMode2::attach(&mut self, lircdev: T) -> core::result::Result @@ -8071,6 +8207,31 @@ impl core::borrow::BorrowMut for aya::programs::ProgramFd where T: core::m pub fn aya::programs::ProgramFd::borrow_mut(&mut self) -> &mut T impl core::convert::From for aya::programs::ProgramFd pub fn aya::programs::ProgramFd::from(t: T) -> T +pub struct aya::programs::ProgramId(_) +impl aya::programs::ProgramId +pub unsafe fn aya::programs::ProgramId::new(id: u32) -> Self +impl core::marker::Freeze for aya::programs::ProgramId +impl core::marker::Send for aya::programs::ProgramId +impl core::marker::Sync for aya::programs::ProgramId +impl core::marker::Unpin for aya::programs::ProgramId +impl core::panic::unwind_safe::RefUnwindSafe for aya::programs::ProgramId +impl core::panic::unwind_safe::UnwindSafe for aya::programs::ProgramId +impl core::convert::Into for aya::programs::ProgramId where U: core::convert::From +pub fn aya::programs::ProgramId::into(self) -> U +impl core::convert::TryFrom for aya::programs::ProgramId where U: core::convert::Into +pub type aya::programs::ProgramId::Error = core::convert::Infallible +pub fn aya::programs::ProgramId::try_from(value: U) -> core::result::Result>::Error> +impl core::convert::TryInto for aya::programs::ProgramId where U: core::convert::TryFrom +pub type aya::programs::ProgramId::Error = >::Error +pub fn aya::programs::ProgramId::try_into(self) -> core::result::Result>::Error> +impl core::any::Any for aya::programs::ProgramId where T: 'static + core::marker::Sized +pub fn aya::programs::ProgramId::type_id(&self) -> core::any::TypeId +impl core::borrow::Borrow for aya::programs::ProgramId where T: core::marker::Sized +pub fn aya::programs::ProgramId::borrow(&self) -> &T +impl core::borrow::BorrowMut for aya::programs::ProgramId where T: core::marker::Sized +pub fn aya::programs::ProgramId::borrow_mut(&mut self) -> &mut T +impl core::convert::From for aya::programs::ProgramId +pub fn aya::programs::ProgramId::from(t: T) -> T pub struct aya::programs::ProgramInfo(_) impl aya::programs::ProgramInfo pub fn aya::programs::ProgramInfo::btf_id(&self) -> core::option::Option @@ -8168,10 +8329,11 @@ pub struct aya::programs::SchedClassifier impl aya::programs::tc::SchedClassifier 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_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::TcOptions) -> 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::detach(&mut self, link_id: aya::programs::tc::SchedClassifierLinkId) -> core::result::Result<(), aya::programs::ProgramError> 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::take_link(&mut self, link_id: aya::programs::tc::SchedClassifierLinkId) -> core::result::Result impl aya::programs::tc::SchedClassifier pub fn aya::programs::tc::SchedClassifier::fd(&self) -> core::result::Result<&aya::programs::ProgramFd, aya::programs::ProgramError> @@ -8182,6 +8344,8 @@ pub fn aya::programs::tc::SchedClassifier::pin core::result::Result<(), std::io::error::Error> impl aya::programs::tc::SchedClassifier pub fn aya::programs::tc::SchedClassifier::unload(&mut self) -> core::result::Result<(), aya::programs::ProgramError> +impl aya::programs::MultiProgram for aya::programs::tc::SchedClassifier +pub fn aya::programs::tc::SchedClassifier::fd(&self) -> core::result::Result, aya::programs::ProgramError> impl core::fmt::Debug for aya::programs::tc::SchedClassifier pub fn aya::programs::tc::SchedClassifier::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result impl core::ops::drop::Drop for aya::programs::tc::SchedClassifier @@ -8831,6 +8995,14 @@ impl aya::programs::links::Link for aya::programs::xdp::XdpLink pub type aya::programs::xdp::XdpLink::Id = aya::programs::xdp::XdpLinkId pub fn aya::programs::xdp::XdpLink::detach(self) -> core::result::Result<(), aya::programs::ProgramError> pub fn aya::programs::xdp::XdpLink::id(&self) -> Self::Id +pub trait aya::programs::MultiProgLink +pub fn aya::programs::MultiProgLink::fd(&self) -> core::result::Result, aya::programs::links::LinkError> +impl aya::programs::MultiProgLink for aya::programs::tc::SchedClassifierLink +pub fn aya::programs::tc::SchedClassifierLink::fd(&self) -> core::result::Result, aya::programs::links::LinkError> +pub trait aya::programs::MultiProgram +pub fn aya::programs::MultiProgram::fd(&self) -> core::result::Result, aya::programs::ProgramError> +impl aya::programs::MultiProgram for aya::programs::tc::SchedClassifier +pub fn aya::programs::tc::SchedClassifier::fd(&self) -> core::result::Result, aya::programs::ProgramError> pub fn aya::programs::loaded_programs() -> impl core::iter::traits::iterator::Iterator> pub mod aya::sys #[non_exhaustive] pub enum aya::sys::Stats