浏览代码

feat(aya): Add iterator program type

BPF iterators[0] are a way to dump kernel data into user-space and an
alternative to `/proc` filesystem.

This change adds support for BPF iterators on the user-space side. It
provides a possibility to retrieve the outputs of BPF iterator programs
both from sync and async Rust code.

[0] https://docs.kernel.org/bpf/bpf_iterators.html
Michal Rostecki 5 月之前
父节点
当前提交
bf2164c92f

+ 5 - 0
aya-obj/src/obj.rs

@@ -288,6 +288,9 @@ pub enum ProgramSection {
         attach_type: CgroupSockAttachType,
     },
     CgroupDevice,
+    Iter {
+        sleepable: bool,
+    },
 }
 
 impl FromStr for ProgramSection {
@@ -439,6 +442,8 @@ impl FromStr for ProgramSection {
             "fexit.s" => FExit { sleepable: true },
             "freplace" => Extension,
             "sk_lookup" => SkLookup,
+            "iter" => Iter { sleepable: false },
+            "iter.s" => Iter { sleepable: true },
             _ => {
                 return Err(ParseError::InvalidProgramSection {
                     section: section.to_owned(),

+ 13 - 4
aya/src/bpf.rs

@@ -31,9 +31,9 @@ use crate::{
     },
     programs::{
         BtfTracePoint, CgroupDevice, CgroupSkb, CgroupSkbAttachType, CgroupSock, CgroupSockAddr,
-        CgroupSockopt, CgroupSysctl, Extension, FEntry, FExit, KProbe, LircMode2, Lsm, PerfEvent,
-        ProbeKind, Program, ProgramData, ProgramError, RawTracePoint, SchedClassifier, SkLookup,
-        SkMsg, SkSkb, SkSkbKind, SockOps, SocketFilter, TracePoint, UProbe, Xdp,
+        CgroupSockopt, CgroupSysctl, Extension, FEntry, FExit, Iter, KProbe, LircMode2, Lsm,
+        PerfEvent, ProbeKind, Program, ProgramData, ProgramError, RawTracePoint, SchedClassifier,
+        SkLookup, SkMsg, SkSkb, SkSkbKind, SockOps, SocketFilter, TracePoint, UProbe, Xdp,
     },
     sys::{
         bpf_load_btf, is_bpf_cookie_supported, is_bpf_global_data_supported,
@@ -410,7 +410,8 @@ impl<'a> EbpfLoader<'a> {
                                 | ProgramSection::FEntry { sleepable: _ }
                                 | ProgramSection::FExit { sleepable: _ }
                                 | ProgramSection::Lsm { sleepable: _ }
-                                | ProgramSection::BtfTracePoint => {
+                                | ProgramSection::BtfTracePoint
+                                | ProgramSection::Iter { sleepable: _ } => {
                                     return Err(EbpfError::BtfError(err))
                                 }
                                 ProgramSection::KRetProbe
@@ -688,6 +689,14 @@ impl<'a> EbpfLoader<'a> {
                         ProgramSection::CgroupDevice => Program::CgroupDevice(CgroupDevice {
                             data: ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level),
                         }),
+                        ProgramSection::Iter { sleepable } => {
+                            let mut data =
+                                ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level);
+                            if *sleepable {
+                                data.flags = BPF_F_SLEEPABLE;
+                            }
+                            Program::Iter(Iter { data })
+                        }
                     }
                 };
                 (name, program)

+ 164 - 0
aya/src/programs/iter.rs

@@ -0,0 +1,164 @@
+//! Iterators.
+use std::{
+    fs::File,
+    os::fd::{AsFd, BorrowedFd},
+};
+
+use crate::{
+    generated::{
+        bpf_attach_type::BPF_TRACE_ITER, bpf_link_type::BPF_LINK_TYPE_ITER,
+        bpf_prog_type::BPF_PROG_TYPE_TRACING,
+    },
+    obj::btf::{Btf, BtfKind},
+    programs::{
+        define_link_wrapper, load_program, FdLink, LinkError, PerfLinkIdInner, PerfLinkInner,
+        ProgramData, ProgramError,
+    },
+    sys::{bpf_create_iter, bpf_link_create, bpf_link_get_info_by_fd, LinkTarget, SyscallError},
+};
+
+/// A BPF iterator which allows to dump data from the kernel-space into the
+/// user-space.
+///
+/// It can be seen as an alternative to `/proc` filesystem as it offers more
+/// flexibility about what information should be retrieved and how it should be
+/// formatted.
+///
+/// # Minimum kernel version
+///
+/// The minimum kernel version required to use this feature is 5.8.
+///
+/// # Example
+///
+/// ```no_run
+/// use std::io::{BufRead, BufReader};
+/// use aya::{programs::{Iter, ProgramError}, BtfError, Btf, Ebpf};
+/// # let mut ebpf = Ebpf::load_file("ebpf_programs.o")?;
+///
+/// let btf = Btf::from_sys_fs()?;
+/// let program: &mut Iter = ebpf.program_mut("iter_prog").unwrap().try_into()?;
+/// program.load("task", &btf)?;
+///
+/// let link_id = program.attach()?;
+/// let link = program.take_link(link_id)?;
+/// let file = link.into_file()?;
+/// let reader = BufReader::new(file);
+///
+/// let mut lines = reader.lines();
+/// for line in lines {
+///     let line = line?;
+///     println!("{line}");
+/// }
+/// # Ok::<(), Box<dyn std::error::Error>>(())
+/// ```
+#[derive(Debug)]
+pub struct Iter {
+    pub(crate) data: ProgramData<IterLink>,
+}
+
+impl Iter {
+    /// Loads the program inside the kernel.
+    pub fn load(&mut self, iter_type: &str, btf: &Btf) -> Result<(), ProgramError> {
+        self.data.expected_attach_type = Some(BPF_TRACE_ITER);
+        let type_name = format!("bpf_iter_{iter_type}");
+        self.data.attach_btf_id =
+            Some(btf.id_by_type_name_kind(type_name.as_str(), BtfKind::Func)?);
+        load_program(BPF_PROG_TYPE_TRACING, &mut self.data)
+    }
+
+    /// Attaches the program.
+    ///
+    /// The returned value can be used to detach, see [`Self::detach`].
+    pub fn attach(&mut self) -> Result<IterLinkId, ProgramError> {
+        let prog_fd = self.fd()?;
+        let prog_fd = prog_fd.as_fd();
+        let link_fd = bpf_link_create(prog_fd, LinkTarget::Iter, BPF_TRACE_ITER, None, 0, None)
+            .map_err(|(_, io_error)| SyscallError {
+                call: "bpf_link_create",
+                io_error,
+            })?;
+
+        self.data
+            .links
+            .insert(IterLink::new(PerfLinkInner::FdLink(FdLink::new(link_fd))))
+    }
+
+    /// Detaches the program.
+    ///
+    /// See [`Self::attach`].
+    pub fn detach(&mut self, link_id: IterLinkId) -> Result<(), ProgramError> {
+        self.data.links.remove(link_id)
+    }
+
+    /// Takes ownership of the link referenced by the provided `link_id`.
+    ///
+    /// The caller takes the responsibility of managing the lifetime of the
+    /// link. When the returned [`IterLink`] is dropped, the link is detached.
+    pub fn take_link(&mut self, link_id: IterLinkId) -> Result<IterLink, ProgramError> {
+        self.data.take_link(link_id)
+    }
+}
+
+/// An iterator descriptor.
+#[derive(Debug)]
+pub struct IterFd {
+    fd: crate::MockableFd,
+}
+
+impl AsFd for IterFd {
+    fn as_fd(&self) -> BorrowedFd<'_> {
+        let Self { fd } = self;
+        fd.as_fd()
+    }
+}
+
+impl TryFrom<IterLink> for FdLink {
+    type Error = LinkError;
+
+    fn try_from(value: IterLink) -> Result<Self, Self::Error> {
+        if let PerfLinkInner::FdLink(fd) = value.into_inner() {
+            Ok(fd)
+        } else {
+            Err(LinkError::InvalidLink)
+        }
+    }
+}
+
+impl TryFrom<FdLink> for IterLink {
+    type Error = LinkError;
+
+    fn try_from(fd_link: FdLink) -> Result<Self, Self::Error> {
+        let info = bpf_link_get_info_by_fd(fd_link.fd.as_fd())?;
+        if info.type_ == (BPF_LINK_TYPE_ITER as u32) {
+            return Ok(Self::new(PerfLinkInner::FdLink(fd_link)));
+        }
+        Err(LinkError::InvalidLink)
+    }
+}
+
+define_link_wrapper!(
+    /// The link used by [`Iter`] programs.
+    IterLink,
+    /// The type returned by [`Iter::attach`]. Can be passed to [`Iter::detach`].
+    IterLinkId,
+    PerfLinkInner,
+    PerfLinkIdInner
+);
+
+impl IterLink {
+    /// Converts [`IterLink`] into a [`File`] that can be used to retrieve the
+    /// outputs of the iterator program.
+    pub fn into_file(self) -> Result<File, LinkError> {
+        if let PerfLinkInner::FdLink(fd) = self.into_inner() {
+            let fd = bpf_create_iter(fd.fd.as_fd()).map_err(|(_, error)| {
+                LinkError::SyscallError(SyscallError {
+                    call: "bpf_iter_create",
+                    io_error: error,
+                })
+            })?;
+            Ok(fd.into_inner().into())
+        } else {
+            Err(LinkError::InvalidLink)
+        }
+    }
+}

+ 26 - 1
aya/src/programs/mod.rs

@@ -51,6 +51,7 @@ pub mod cgroup_sysctl;
 pub mod extension;
 pub mod fentry;
 pub mod fexit;
+pub mod iter;
 pub mod kprobe;
 pub mod links;
 pub mod lirc_mode2;
@@ -94,6 +95,7 @@ pub use crate::programs::{
     extension::{Extension, ExtensionError},
     fentry::FEntry,
     fexit::FExit,
+    iter::Iter,
     kprobe::{KProbe, KProbeError},
     links::{CgroupAttachMode, Link, LinkOrder},
     lirc_mode2::LircMode2,
@@ -303,6 +305,8 @@ pub enum Program {
     CgroupSock(CgroupSock),
     /// A [`CgroupDevice`] program
     CgroupDevice(CgroupDevice),
+    /// An [`Iter`] program
+    Iter(Iter),
 }
 
 impl Program {
@@ -324,7 +328,18 @@ impl Program {
             Self::PerfEvent(_) => ProgramType::PerfEvent,
             Self::RawTracePoint(_) => ProgramType::RawTracePoint,
             Self::Lsm(_) => ProgramType::Lsm,
-            Self::BtfTracePoint(_) | Self::FEntry(_) | Self::FExit(_) => ProgramType::Tracing,
+            // The following program types are a subset of `TRACING` programs:
+            //
+            // - `BPF_TRACE_RAW_TP` (`BtfTracePoint`)
+            // - `BTF_TRACE_FENTRY` (`FEntry`)
+            // - `BPF_MODIFY_RETURN` (not supported yet in Aya)
+            // - `BPF_TRACE_FEXIT` (`FExit`)
+            // - `BPF_TRACE_ITER` (`Iter`)
+            //
+            // https://github.com/torvalds/linux/blob/v6.12/kernel/bpf/syscall.c#L3935-L3940
+            Self::BtfTracePoint(_) | Self::FEntry(_) | Self::FExit(_) | Self::Iter(_) => {
+                ProgramType::Tracing
+            }
             Self::Extension(_) => ProgramType::Extension,
             Self::CgroupSockAddr(_) => ProgramType::CgroupSockAddr,
             Self::SkLookup(_) => ProgramType::SkLookup,
@@ -360,6 +375,7 @@ impl Program {
             Self::SkLookup(p) => p.pin(path),
             Self::CgroupSock(p) => p.pin(path),
             Self::CgroupDevice(p) => p.pin(path),
+            Self::Iter(p) => p.pin(path),
         }
     }
 
@@ -390,6 +406,7 @@ impl Program {
             Self::SkLookup(mut p) => p.unload(),
             Self::CgroupSock(mut p) => p.unload(),
             Self::CgroupDevice(mut p) => p.unload(),
+            Self::Iter(mut p) => p.unload(),
         }
     }
 
@@ -422,6 +439,7 @@ impl Program {
             Self::SkLookup(p) => p.fd(),
             Self::CgroupSock(p) => p.fd(),
             Self::CgroupDevice(p) => p.fd(),
+            Self::Iter(p) => p.fd(),
         }
     }
 
@@ -455,6 +473,7 @@ impl Program {
             Self::SkLookup(p) => p.info(),
             Self::CgroupSock(p) => p.info(),
             Self::CgroupDevice(p) => p.info(),
+            Self::Iter(p) => p.info(),
         }
     }
 }
@@ -771,6 +790,7 @@ impl_program_unload!(
     SockOps,
     CgroupSock,
     CgroupDevice,
+    Iter,
 );
 
 macro_rules! impl_fd {
@@ -811,6 +831,7 @@ impl_fd!(
     SockOps,
     CgroupSock,
     CgroupDevice,
+    Iter,
 );
 
 /// Trait implemented by the [`Program`] types which support the kernel's
@@ -916,6 +937,7 @@ impl_program_pin!(
     SockOps,
     CgroupSock,
     CgroupDevice,
+    Iter,
 );
 
 macro_rules! impl_from_pin {
@@ -954,6 +976,7 @@ impl_from_pin!(
     SkLookup,
     SockOps,
     CgroupDevice,
+    Iter,
 );
 
 macro_rules! impl_try_from_program {
@@ -1009,6 +1032,7 @@ impl_try_from_program!(
     SkLookup,
     CgroupSock,
     CgroupDevice,
+    Iter,
 );
 
 impl_info!(
@@ -1036,6 +1060,7 @@ impl_info!(
     SockOps,
     CgroupSock,
     CgroupDevice,
+    Iter,
 );
 
 // TODO(https://github.com/aya-rs/aya/issues/645): this API is currently used in tests. Stabilize

+ 16 - 0
aya/src/sys/bpf.rs

@@ -36,6 +36,16 @@ use crate::{
     Btf, Pod, VerifierLogLevel, BPF_OBJ_NAME_LEN, FEATURES,
 };
 
+pub(crate) fn bpf_create_iter(link_fd: BorrowedFd<'_>) -> SysResult<crate::MockableFd> {
+    let mut attr = unsafe { mem::zeroed::<bpf_attr>() };
+
+    let u = unsafe { &mut attr.iter_create };
+    u.link_fd = link_fd.as_raw_fd() as u32;
+
+    // SAFETY: BPF_ITER_CREATE returns a new file descriptor.
+    unsafe { fd_sys_bpf(bpf_cmd::BPF_ITER_CREATE, &mut attr) }
+}
+
 pub(crate) fn bpf_create_map(
     name: &CStr,
     def: &obj::Map,
@@ -377,6 +387,7 @@ pub(crate) fn bpf_map_freeze(fd: BorrowedFd<'_>) -> SysResult<i64> {
 pub(crate) enum LinkTarget<'f> {
     Fd(BorrowedFd<'f>),
     IfIndex(u32),
+    Iter,
 }
 
 // since kernel 5.7
@@ -399,6 +410,11 @@ pub(crate) fn bpf_link_create(
         LinkTarget::IfIndex(ifindex) => {
             attr.link_create.__bindgen_anon_2.target_ifindex = ifindex;
         }
+        // When attaching to an iterator program, no target FD is needed. In
+        // fact, the kernel explicitly rejects non-zero target FDs for
+        // iterators:
+        // https://github.com/torvalds/linux/blob/v6.12/kernel/bpf/bpf_iter.c#L517-L518
+        LinkTarget::Iter => {}
     };
     attr.link_create.attach_type = attach_type as u32;
 

+ 23 - 0
test/integration-test/bpf/iter.bpf.c

@@ -0,0 +1,23 @@
+// clang-format off
+#include <vmlinux.h>
+#include <bpf/bpf_helpers.h>
+// clang-format on
+
+char _license[] SEC("license") = "GPL";
+
+SEC("iter/task")
+int iter_task(struct bpf_iter__task *ctx) {
+  struct seq_file *seq = ctx->meta->seq;
+  struct task_struct *task = ctx->task;
+  // Verifier requires this check.
+  if (task == NULL) {
+    return 0;
+  }
+
+  if (ctx->meta->seq_num == 0) {
+    BPF_SEQ_PRINTF(seq, "tgid     pid      name\n");
+  }
+  BPF_SEQ_PRINTF(seq, "%-8d %-8d %s\n", task->tgid, task->pid, task->comm);
+
+  return 0;
+}

+ 1 - 0
test/integration-test/build.rs

@@ -67,6 +67,7 @@ fn main() {
 
     const C_BPF: &[(&str, bool)] = &[
         ("ext.bpf.c", false),
+        ("iter.bpf.c", true),
         ("main.bpf.c", false),
         ("multimap-btf.bpf.c", false),
         ("reloc.bpf.c", true),

+ 1 - 0
test/integration-test/src/lib.rs

@@ -1,6 +1,7 @@
 use aya::include_bytes_aligned;
 
 pub const EXT: &[u8] = include_bytes_aligned!(concat!(env!("OUT_DIR"), "/ext.bpf.o"));
+pub const ITER_TASK: &[u8] = include_bytes_aligned!(concat!(env!("OUT_DIR"), "/iter.bpf.o"));
 pub const MAIN: &[u8] = include_bytes_aligned!(concat!(env!("OUT_DIR"), "/main.bpf.o"));
 pub const MULTIMAP_BTF: &[u8] =
     include_bytes_aligned!(concat!(env!("OUT_DIR"), "/multimap-btf.bpf.o"));

+ 1 - 0
test/integration-test/src/tests.rs

@@ -2,6 +2,7 @@ mod bpf_probe_read;
 mod btf_relocations;
 mod elf;
 mod info;
+mod iter;
 mod load;
 mod log;
 mod rbpf;

+ 30 - 0
test/integration-test/src/tests/iter.rs

@@ -0,0 +1,30 @@
+use std::io::BufRead;
+
+use aya::{programs::Iter, Btf, Ebpf};
+use test_log::test;
+
+#[test]
+fn iter_task() {
+    let mut ebpf = Ebpf::load(crate::ITER_TASK).unwrap();
+    let btf = Btf::from_sys_fs().unwrap();
+    let prog: &mut Iter = ebpf.program_mut("iter_task").unwrap().try_into().unwrap();
+    prog.load("task", &btf).unwrap();
+
+    let link_id = prog.attach().unwrap();
+    let link = prog.take_link(link_id).unwrap();
+    let file = link.into_file().unwrap();
+    let reader = std::io::BufReader::new(file);
+
+    let mut lines = reader.lines();
+    let line_title = lines.next().unwrap().unwrap();
+    let line_init = lines.next().unwrap().unwrap();
+
+    assert_eq!(line_title, "tgid     pid      name");
+    let expected_values = ["1        1        init", "1        1        systemd"];
+    assert!(
+        expected_values.contains(&line_init.as_str()),
+        "Unexpected line_init value: '{}', expected one of: {:?}",
+        line_init,
+        expected_values
+    );
+}

+ 4 - 0
xtask/public-api/aya-obj.txt

@@ -7190,6 +7190,8 @@ pub aya_obj::obj::ProgramSection::FEntry
 pub aya_obj::obj::ProgramSection::FEntry::sleepable: bool
 pub aya_obj::obj::ProgramSection::FExit
 pub aya_obj::obj::ProgramSection::FExit::sleepable: bool
+pub aya_obj::obj::ProgramSection::Iter
+pub aya_obj::obj::ProgramSection::Iter::sleepable: bool
 pub aya_obj::obj::ProgramSection::KProbe
 pub aya_obj::obj::ProgramSection::KRetProbe
 pub aya_obj::obj::ProgramSection::LircMode2
@@ -8050,6 +8052,8 @@ pub aya_obj::ProgramSection::FEntry
 pub aya_obj::ProgramSection::FEntry::sleepable: bool
 pub aya_obj::ProgramSection::FExit
 pub aya_obj::ProgramSection::FExit::sleepable: bool
+pub aya_obj::ProgramSection::Iter
+pub aya_obj::ProgramSection::Iter::sleepable: bool
 pub aya_obj::ProgramSection::KProbe
 pub aya_obj::ProgramSection::KRetProbe
 pub aya_obj::ProgramSection::LircMode2

+ 219 - 0
xtask/public-api/aya.txt

@@ -3633,6 +3633,155 @@ impl<T> core::borrow::BorrowMut<T> for aya::programs::fexit::FExitLinkId where T
 pub fn aya::programs::fexit::FExitLinkId::borrow_mut(&mut self) -> &mut T
 impl<T> core::convert::From<T> for aya::programs::fexit::FExitLinkId
 pub fn aya::programs::fexit::FExitLinkId::from(t: T) -> T
+pub mod aya::programs::iter
+pub struct aya::programs::iter::Iter
+impl aya::programs::iter::Iter
+pub fn aya::programs::iter::Iter::attach(&mut self) -> core::result::Result<aya::programs::iter::IterLinkId, aya::programs::ProgramError>
+pub fn aya::programs::iter::Iter::detach(&mut self, link_id: aya::programs::iter::IterLinkId) -> core::result::Result<(), aya::programs::ProgramError>
+pub fn aya::programs::iter::Iter::load(&mut self, iter_type: &str, btf: &aya_obj::btf::btf::Btf) -> core::result::Result<(), aya::programs::ProgramError>
+pub fn aya::programs::iter::Iter::take_link(&mut self, link_id: aya::programs::iter::IterLinkId) -> core::result::Result<aya::programs::iter::IterLink, aya::programs::ProgramError>
+impl aya::programs::iter::Iter
+pub fn aya::programs::iter::Iter::fd(&self) -> core::result::Result<&aya::programs::ProgramFd, aya::programs::ProgramError>
+impl aya::programs::iter::Iter
+pub fn aya::programs::iter::Iter::from_pin<P: core::convert::AsRef<std::path::Path>>(path: P) -> core::result::Result<Self, aya::programs::ProgramError>
+impl aya::programs::iter::Iter
+pub fn aya::programs::iter::Iter::info(&self) -> core::result::Result<aya::programs::ProgramInfo, aya::programs::ProgramError>
+impl aya::programs::iter::Iter
+pub fn aya::programs::iter::Iter::pin<P: core::convert::AsRef<std::path::Path>>(&mut self, path: P) -> core::result::Result<(), aya::pin::PinError>
+pub fn aya::programs::iter::Iter::unpin(self) -> core::result::Result<(), std::io::error::Error>
+impl aya::programs::iter::Iter
+pub fn aya::programs::iter::Iter::unload(&mut self) -> core::result::Result<(), aya::programs::ProgramError>
+impl core::fmt::Debug for aya::programs::iter::Iter
+pub fn aya::programs::iter::Iter::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result
+impl core::ops::drop::Drop for aya::programs::iter::Iter
+pub fn aya::programs::iter::Iter::drop(&mut self)
+impl<'a> core::convert::TryFrom<&'a aya::programs::Program> for &'a aya::programs::iter::Iter
+pub type &'a aya::programs::iter::Iter::Error = aya::programs::ProgramError
+pub fn &'a aya::programs::iter::Iter::try_from(program: &'a aya::programs::Program) -> core::result::Result<&'a aya::programs::iter::Iter, aya::programs::ProgramError>
+impl<'a> core::convert::TryFrom<&'a mut aya::programs::Program> for &'a mut aya::programs::iter::Iter
+pub type &'a mut aya::programs::iter::Iter::Error = aya::programs::ProgramError
+pub fn &'a mut aya::programs::iter::Iter::try_from(program: &'a mut aya::programs::Program) -> core::result::Result<&'a mut aya::programs::iter::Iter, aya::programs::ProgramError>
+impl core::marker::Freeze for aya::programs::iter::Iter
+impl core::marker::Send for aya::programs::iter::Iter
+impl core::marker::Sync for aya::programs::iter::Iter
+impl core::marker::Unpin for aya::programs::iter::Iter
+impl core::panic::unwind_safe::RefUnwindSafe for aya::programs::iter::Iter
+impl core::panic::unwind_safe::UnwindSafe for aya::programs::iter::Iter
+impl<T, U> core::convert::Into<U> for aya::programs::iter::Iter where U: core::convert::From<T>
+pub fn aya::programs::iter::Iter::into(self) -> U
+impl<T, U> core::convert::TryFrom<U> for aya::programs::iter::Iter where U: core::convert::Into<T>
+pub type aya::programs::iter::Iter::Error = core::convert::Infallible
+pub fn aya::programs::iter::Iter::try_from(value: U) -> core::result::Result<T, <T as core::convert::TryFrom<U>>::Error>
+impl<T, U> core::convert::TryInto<U> for aya::programs::iter::Iter where U: core::convert::TryFrom<T>
+pub type aya::programs::iter::Iter::Error = <U as core::convert::TryFrom<T>>::Error
+pub fn aya::programs::iter::Iter::try_into(self) -> core::result::Result<U, <U as core::convert::TryFrom<T>>::Error>
+impl<T> core::any::Any for aya::programs::iter::Iter where T: 'static + ?core::marker::Sized
+pub fn aya::programs::iter::Iter::type_id(&self) -> core::any::TypeId
+impl<T> core::borrow::Borrow<T> for aya::programs::iter::Iter where T: ?core::marker::Sized
+pub fn aya::programs::iter::Iter::borrow(&self) -> &T
+impl<T> core::borrow::BorrowMut<T> for aya::programs::iter::Iter where T: ?core::marker::Sized
+pub fn aya::programs::iter::Iter::borrow_mut(&mut self) -> &mut T
+impl<T> core::convert::From<T> for aya::programs::iter::Iter
+pub fn aya::programs::iter::Iter::from(t: T) -> T
+pub struct aya::programs::iter::IterFd
+impl core::fmt::Debug for aya::programs::iter::IterFd
+pub fn aya::programs::iter::IterFd::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result
+impl std::os::fd::owned::AsFd for aya::programs::iter::IterFd
+pub fn aya::programs::iter::IterFd::as_fd(&self) -> std::os::fd::owned::BorrowedFd<'_>
+impl core::marker::Freeze for aya::programs::iter::IterFd
+impl core::marker::Send for aya::programs::iter::IterFd
+impl core::marker::Sync for aya::programs::iter::IterFd
+impl core::marker::Unpin for aya::programs::iter::IterFd
+impl core::panic::unwind_safe::RefUnwindSafe for aya::programs::iter::IterFd
+impl core::panic::unwind_safe::UnwindSafe for aya::programs::iter::IterFd
+impl<T, U> core::convert::Into<U> for aya::programs::iter::IterFd where U: core::convert::From<T>
+pub fn aya::programs::iter::IterFd::into(self) -> U
+impl<T, U> core::convert::TryFrom<U> for aya::programs::iter::IterFd where U: core::convert::Into<T>
+pub type aya::programs::iter::IterFd::Error = core::convert::Infallible
+pub fn aya::programs::iter::IterFd::try_from(value: U) -> core::result::Result<T, <T as core::convert::TryFrom<U>>::Error>
+impl<T, U> core::convert::TryInto<U> for aya::programs::iter::IterFd where U: core::convert::TryFrom<T>
+pub type aya::programs::iter::IterFd::Error = <U as core::convert::TryFrom<T>>::Error
+pub fn aya::programs::iter::IterFd::try_into(self) -> core::result::Result<U, <U as core::convert::TryFrom<T>>::Error>
+impl<T> core::any::Any for aya::programs::iter::IterFd where T: 'static + ?core::marker::Sized
+pub fn aya::programs::iter::IterFd::type_id(&self) -> core::any::TypeId
+impl<T> core::borrow::Borrow<T> for aya::programs::iter::IterFd where T: ?core::marker::Sized
+pub fn aya::programs::iter::IterFd::borrow(&self) -> &T
+impl<T> core::borrow::BorrowMut<T> for aya::programs::iter::IterFd where T: ?core::marker::Sized
+pub fn aya::programs::iter::IterFd::borrow_mut(&mut self) -> &mut T
+impl<T> core::convert::From<T> for aya::programs::iter::IterFd
+pub fn aya::programs::iter::IterFd::from(t: T) -> T
+pub struct aya::programs::iter::IterLink(_)
+impl aya::programs::iter::IterLink
+pub fn aya::programs::iter::IterLink::into_file(self) -> core::result::Result<std::fs::File, aya::programs::links::LinkError>
+impl aya::programs::links::Link for aya::programs::iter::IterLink
+pub type aya::programs::iter::IterLink::Id = aya::programs::iter::IterLinkId
+pub fn aya::programs::iter::IterLink::detach(self) -> core::result::Result<(), aya::programs::ProgramError>
+pub fn aya::programs::iter::IterLink::id(&self) -> Self::Id
+impl core::convert::TryFrom<aya::programs::iter::IterLink> 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::iter::IterLink) -> core::result::Result<Self, Self::Error>
+impl core::convert::TryFrom<aya::programs::links::FdLink> for aya::programs::iter::IterLink
+pub type aya::programs::iter::IterLink::Error = aya::programs::links::LinkError
+pub fn aya::programs::iter::IterLink::try_from(fd_link: aya::programs::links::FdLink) -> core::result::Result<Self, Self::Error>
+impl core::fmt::Debug for aya::programs::iter::IterLink
+pub fn aya::programs::iter::IterLink::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result
+impl core::ops::drop::Drop for aya::programs::iter::IterLink
+pub fn aya::programs::iter::IterLink::drop(&mut self)
+impl core::marker::Freeze for aya::programs::iter::IterLink
+impl core::marker::Send for aya::programs::iter::IterLink
+impl core::marker::Sync for aya::programs::iter::IterLink
+impl core::marker::Unpin for aya::programs::iter::IterLink
+impl core::panic::unwind_safe::RefUnwindSafe for aya::programs::iter::IterLink
+impl core::panic::unwind_safe::UnwindSafe for aya::programs::iter::IterLink
+impl<T, U> core::convert::Into<U> for aya::programs::iter::IterLink where U: core::convert::From<T>
+pub fn aya::programs::iter::IterLink::into(self) -> U
+impl<T, U> core::convert::TryFrom<U> for aya::programs::iter::IterLink where U: core::convert::Into<T>
+pub type aya::programs::iter::IterLink::Error = core::convert::Infallible
+pub fn aya::programs::iter::IterLink::try_from(value: U) -> core::result::Result<T, <T as core::convert::TryFrom<U>>::Error>
+impl<T, U> core::convert::TryInto<U> for aya::programs::iter::IterLink where U: core::convert::TryFrom<T>
+pub type aya::programs::iter::IterLink::Error = <U as core::convert::TryFrom<T>>::Error
+pub fn aya::programs::iter::IterLink::try_into(self) -> core::result::Result<U, <U as core::convert::TryFrom<T>>::Error>
+impl<T> core::any::Any for aya::programs::iter::IterLink where T: 'static + ?core::marker::Sized
+pub fn aya::programs::iter::IterLink::type_id(&self) -> core::any::TypeId
+impl<T> core::borrow::Borrow<T> for aya::programs::iter::IterLink where T: ?core::marker::Sized
+pub fn aya::programs::iter::IterLink::borrow(&self) -> &T
+impl<T> core::borrow::BorrowMut<T> for aya::programs::iter::IterLink where T: ?core::marker::Sized
+pub fn aya::programs::iter::IterLink::borrow_mut(&mut self) -> &mut T
+impl<T> core::convert::From<T> for aya::programs::iter::IterLink
+pub fn aya::programs::iter::IterLink::from(t: T) -> T
+pub struct aya::programs::iter::IterLinkId(_)
+impl core::cmp::Eq for aya::programs::iter::IterLinkId
+impl core::cmp::PartialEq for aya::programs::iter::IterLinkId
+pub fn aya::programs::iter::IterLinkId::eq(&self, other: &aya::programs::iter::IterLinkId) -> bool
+impl core::fmt::Debug for aya::programs::iter::IterLinkId
+pub fn aya::programs::iter::IterLinkId::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result
+impl core::hash::Hash for aya::programs::iter::IterLinkId
+pub fn aya::programs::iter::IterLinkId::hash<__H: core::hash::Hasher>(&self, state: &mut __H)
+impl core::marker::StructuralPartialEq for aya::programs::iter::IterLinkId
+impl core::marker::Freeze for aya::programs::iter::IterLinkId
+impl core::marker::Send for aya::programs::iter::IterLinkId
+impl core::marker::Sync for aya::programs::iter::IterLinkId
+impl core::marker::Unpin for aya::programs::iter::IterLinkId
+impl core::panic::unwind_safe::RefUnwindSafe for aya::programs::iter::IterLinkId
+impl core::panic::unwind_safe::UnwindSafe for aya::programs::iter::IterLinkId
+impl<Q, K> equivalent::Equivalent<K> for aya::programs::iter::IterLinkId where Q: core::cmp::Eq + ?core::marker::Sized, K: core::borrow::Borrow<Q> + ?core::marker::Sized
+pub fn aya::programs::iter::IterLinkId::equivalent(&self, key: &K) -> bool
+impl<T, U> core::convert::Into<U> for aya::programs::iter::IterLinkId where U: core::convert::From<T>
+pub fn aya::programs::iter::IterLinkId::into(self) -> U
+impl<T, U> core::convert::TryFrom<U> for aya::programs::iter::IterLinkId where U: core::convert::Into<T>
+pub type aya::programs::iter::IterLinkId::Error = core::convert::Infallible
+pub fn aya::programs::iter::IterLinkId::try_from(value: U) -> core::result::Result<T, <T as core::convert::TryFrom<U>>::Error>
+impl<T, U> core::convert::TryInto<U> for aya::programs::iter::IterLinkId where U: core::convert::TryFrom<T>
+pub type aya::programs::iter::IterLinkId::Error = <U as core::convert::TryFrom<T>>::Error
+pub fn aya::programs::iter::IterLinkId::try_into(self) -> core::result::Result<U, <U as core::convert::TryFrom<T>>::Error>
+impl<T> core::any::Any for aya::programs::iter::IterLinkId where T: 'static + ?core::marker::Sized
+pub fn aya::programs::iter::IterLinkId::type_id(&self) -> core::any::TypeId
+impl<T> core::borrow::Borrow<T> for aya::programs::iter::IterLinkId where T: ?core::marker::Sized
+pub fn aya::programs::iter::IterLinkId::borrow(&self) -> &T
+impl<T> core::borrow::BorrowMut<T> for aya::programs::iter::IterLinkId where T: ?core::marker::Sized
+pub fn aya::programs::iter::IterLinkId::borrow_mut(&mut self) -> &mut T
+impl<T> core::convert::From<T> for aya::programs::iter::IterLinkId
+pub fn aya::programs::iter::IterLinkId::from(t: T) -> T
 pub mod aya::programs::kprobe
 pub enum aya::programs::kprobe::KProbeError
 pub aya::programs::kprobe::KProbeError::FileError
@@ -3903,9 +4052,15 @@ impl core::convert::From<aya::programs::sk_lookup::SkLookupLink> for aya::progra
 pub fn aya::programs::links::FdLink::from(w: aya::programs::sk_lookup::SkLookupLink) -> aya::programs::links::FdLink
 impl core::convert::From<aya::programs::tp_btf::BtfTracePointLink> for aya::programs::links::FdLink
 pub fn aya::programs::links::FdLink::from(w: aya::programs::tp_btf::BtfTracePointLink) -> aya::programs::links::FdLink
+impl core::convert::TryFrom<aya::programs::iter::IterLink> 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::iter::IterLink) -> core::result::Result<Self, Self::Error>
 impl core::convert::TryFrom<aya::programs::kprobe::KProbeLink> 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::kprobe::KProbeLink) -> core::result::Result<Self, Self::Error>
+impl core::convert::TryFrom<aya::programs::links::FdLink> for aya::programs::iter::IterLink
+pub type aya::programs::iter::IterLink::Error = aya::programs::links::LinkError
+pub fn aya::programs::iter::IterLink::try_from(fd_link: aya::programs::links::FdLink) -> core::result::Result<Self, Self::Error>
 impl core::convert::TryFrom<aya::programs::links::FdLink> for aya::programs::kprobe::KProbeLink
 pub type aya::programs::kprobe::KProbeLink::Error = aya::programs::links::LinkError
 pub fn aya::programs::kprobe::KProbeLink::try_from(fd_link: aya::programs::links::FdLink) -> core::result::Result<Self, Self::Error>
@@ -4175,6 +4330,10 @@ impl aya::programs::links::Link for aya::programs::fexit::FExitLink
 pub type aya::programs::fexit::FExitLink::Id = aya::programs::fexit::FExitLinkId
 pub fn aya::programs::fexit::FExitLink::detach(self) -> core::result::Result<(), aya::programs::ProgramError>
 pub fn aya::programs::fexit::FExitLink::id(&self) -> Self::Id
+impl aya::programs::links::Link for aya::programs::iter::IterLink
+pub type aya::programs::iter::IterLink::Id = aya::programs::iter::IterLinkId
+pub fn aya::programs::iter::IterLink::detach(self) -> core::result::Result<(), aya::programs::ProgramError>
+pub fn aya::programs::iter::IterLink::id(&self) -> Self::Id
 impl aya::programs::links::Link for aya::programs::kprobe::KProbeLink
 pub type aya::programs::kprobe::KProbeLink::Id = aya::programs::kprobe::KProbeLinkId
 pub fn aya::programs::kprobe::KProbeLink::detach(self) -> core::result::Result<(), aya::programs::ProgramError>
@@ -6819,6 +6978,7 @@ pub aya::programs::Program::CgroupSysctl(aya::programs::cgroup_sysctl::CgroupSys
 pub aya::programs::Program::Extension(aya::programs::extension::Extension)
 pub aya::programs::Program::FEntry(aya::programs::fentry::FEntry)
 pub aya::programs::Program::FExit(aya::programs::fexit::FExit)
+pub aya::programs::Program::Iter(aya::programs::iter::Iter)
 pub aya::programs::Program::KProbe(aya::programs::kprobe::KProbe)
 pub aya::programs::Program::LircMode2(aya::programs::lirc_mode2::LircMode2)
 pub aya::programs::Program::Lsm(aya::programs::lsm::Lsm)
@@ -6868,6 +7028,9 @@ pub fn &'a aya::programs::fentry::FEntry::try_from(program: &'a aya::programs::P
 impl<'a> core::convert::TryFrom<&'a aya::programs::Program> for &'a aya::programs::fexit::FExit
 pub type &'a aya::programs::fexit::FExit::Error = aya::programs::ProgramError
 pub fn &'a aya::programs::fexit::FExit::try_from(program: &'a aya::programs::Program) -> core::result::Result<&'a aya::programs::fexit::FExit, aya::programs::ProgramError>
+impl<'a> core::convert::TryFrom<&'a aya::programs::Program> for &'a aya::programs::iter::Iter
+pub type &'a aya::programs::iter::Iter::Error = aya::programs::ProgramError
+pub fn &'a aya::programs::iter::Iter::try_from(program: &'a aya::programs::Program) -> core::result::Result<&'a aya::programs::iter::Iter, aya::programs::ProgramError>
 impl<'a> core::convert::TryFrom<&'a aya::programs::Program> for &'a aya::programs::kprobe::KProbe
 pub type &'a aya::programs::kprobe::KProbe::Error = aya::programs::ProgramError
 pub fn &'a aya::programs::kprobe::KProbe::try_from(program: &'a aya::programs::Program) -> core::result::Result<&'a aya::programs::kprobe::KProbe, aya::programs::ProgramError>
@@ -6940,6 +7103,9 @@ pub fn &'a mut aya::programs::fentry::FEntry::try_from(program: &'a mut aya::pro
 impl<'a> core::convert::TryFrom<&'a mut aya::programs::Program> for &'a mut aya::programs::fexit::FExit
 pub type &'a mut aya::programs::fexit::FExit::Error = aya::programs::ProgramError
 pub fn &'a mut aya::programs::fexit::FExit::try_from(program: &'a mut aya::programs::Program) -> core::result::Result<&'a mut aya::programs::fexit::FExit, aya::programs::ProgramError>
+impl<'a> core::convert::TryFrom<&'a mut aya::programs::Program> for &'a mut aya::programs::iter::Iter
+pub type &'a mut aya::programs::iter::Iter::Error = aya::programs::ProgramError
+pub fn &'a mut aya::programs::iter::Iter::try_from(program: &'a mut aya::programs::Program) -> core::result::Result<&'a mut aya::programs::iter::Iter, aya::programs::ProgramError>
 impl<'a> core::convert::TryFrom<&'a mut aya::programs::Program> for &'a mut aya::programs::kprobe::KProbe
 pub type &'a mut aya::programs::kprobe::KProbe::Error = aya::programs::ProgramError
 pub fn &'a mut aya::programs::kprobe::KProbe::try_from(program: &'a mut aya::programs::Program) -> core::result::Result<&'a mut aya::programs::kprobe::KProbe, aya::programs::ProgramError>
@@ -7950,6 +8116,55 @@ impl<T> core::borrow::BorrowMut<T> for aya::programs::fexit::FExit where T: ?cor
 pub fn aya::programs::fexit::FExit::borrow_mut(&mut self) -> &mut T
 impl<T> core::convert::From<T> for aya::programs::fexit::FExit
 pub fn aya::programs::fexit::FExit::from(t: T) -> T
+pub struct aya::programs::Iter
+impl aya::programs::iter::Iter
+pub fn aya::programs::iter::Iter::attach(&mut self) -> core::result::Result<aya::programs::iter::IterLinkId, aya::programs::ProgramError>
+pub fn aya::programs::iter::Iter::detach(&mut self, link_id: aya::programs::iter::IterLinkId) -> core::result::Result<(), aya::programs::ProgramError>
+pub fn aya::programs::iter::Iter::load(&mut self, iter_type: &str, btf: &aya_obj::btf::btf::Btf) -> core::result::Result<(), aya::programs::ProgramError>
+pub fn aya::programs::iter::Iter::take_link(&mut self, link_id: aya::programs::iter::IterLinkId) -> core::result::Result<aya::programs::iter::IterLink, aya::programs::ProgramError>
+impl aya::programs::iter::Iter
+pub fn aya::programs::iter::Iter::fd(&self) -> core::result::Result<&aya::programs::ProgramFd, aya::programs::ProgramError>
+impl aya::programs::iter::Iter
+pub fn aya::programs::iter::Iter::from_pin<P: core::convert::AsRef<std::path::Path>>(path: P) -> core::result::Result<Self, aya::programs::ProgramError>
+impl aya::programs::iter::Iter
+pub fn aya::programs::iter::Iter::info(&self) -> core::result::Result<aya::programs::ProgramInfo, aya::programs::ProgramError>
+impl aya::programs::iter::Iter
+pub fn aya::programs::iter::Iter::pin<P: core::convert::AsRef<std::path::Path>>(&mut self, path: P) -> core::result::Result<(), aya::pin::PinError>
+pub fn aya::programs::iter::Iter::unpin(self) -> core::result::Result<(), std::io::error::Error>
+impl aya::programs::iter::Iter
+pub fn aya::programs::iter::Iter::unload(&mut self) -> core::result::Result<(), aya::programs::ProgramError>
+impl core::fmt::Debug for aya::programs::iter::Iter
+pub fn aya::programs::iter::Iter::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result
+impl core::ops::drop::Drop for aya::programs::iter::Iter
+pub fn aya::programs::iter::Iter::drop(&mut self)
+impl<'a> core::convert::TryFrom<&'a aya::programs::Program> for &'a aya::programs::iter::Iter
+pub type &'a aya::programs::iter::Iter::Error = aya::programs::ProgramError
+pub fn &'a aya::programs::iter::Iter::try_from(program: &'a aya::programs::Program) -> core::result::Result<&'a aya::programs::iter::Iter, aya::programs::ProgramError>
+impl<'a> core::convert::TryFrom<&'a mut aya::programs::Program> for &'a mut aya::programs::iter::Iter
+pub type &'a mut aya::programs::iter::Iter::Error = aya::programs::ProgramError
+pub fn &'a mut aya::programs::iter::Iter::try_from(program: &'a mut aya::programs::Program) -> core::result::Result<&'a mut aya::programs::iter::Iter, aya::programs::ProgramError>
+impl core::marker::Freeze for aya::programs::iter::Iter
+impl core::marker::Send for aya::programs::iter::Iter
+impl core::marker::Sync for aya::programs::iter::Iter
+impl core::marker::Unpin for aya::programs::iter::Iter
+impl core::panic::unwind_safe::RefUnwindSafe for aya::programs::iter::Iter
+impl core::panic::unwind_safe::UnwindSafe for aya::programs::iter::Iter
+impl<T, U> core::convert::Into<U> for aya::programs::iter::Iter where U: core::convert::From<T>
+pub fn aya::programs::iter::Iter::into(self) -> U
+impl<T, U> core::convert::TryFrom<U> for aya::programs::iter::Iter where U: core::convert::Into<T>
+pub type aya::programs::iter::Iter::Error = core::convert::Infallible
+pub fn aya::programs::iter::Iter::try_from(value: U) -> core::result::Result<T, <T as core::convert::TryFrom<U>>::Error>
+impl<T, U> core::convert::TryInto<U> for aya::programs::iter::Iter where U: core::convert::TryFrom<T>
+pub type aya::programs::iter::Iter::Error = <U as core::convert::TryFrom<T>>::Error
+pub fn aya::programs::iter::Iter::try_into(self) -> core::result::Result<U, <U as core::convert::TryFrom<T>>::Error>
+impl<T> core::any::Any for aya::programs::iter::Iter where T: 'static + ?core::marker::Sized
+pub fn aya::programs::iter::Iter::type_id(&self) -> core::any::TypeId
+impl<T> core::borrow::Borrow<T> for aya::programs::iter::Iter where T: ?core::marker::Sized
+pub fn aya::programs::iter::Iter::borrow(&self) -> &T
+impl<T> core::borrow::BorrowMut<T> for aya::programs::iter::Iter where T: ?core::marker::Sized
+pub fn aya::programs::iter::Iter::borrow_mut(&mut self) -> &mut T
+impl<T> core::convert::From<T> for aya::programs::iter::Iter
+pub fn aya::programs::iter::Iter::from(t: T) -> T
 pub struct aya::programs::KProbe
 impl aya::programs::kprobe::KProbe
 pub fn aya::programs::kprobe::KProbe::attach<T: core::convert::AsRef<std::ffi::os_str::OsStr>>(&mut self, fn_name: T, offset: u64) -> core::result::Result<aya::programs::kprobe::KProbeLinkId, aya::programs::ProgramError>
@@ -8928,6 +9143,10 @@ impl aya::programs::links::Link for aya::programs::fexit::FExitLink
 pub type aya::programs::fexit::FExitLink::Id = aya::programs::fexit::FExitLinkId
 pub fn aya::programs::fexit::FExitLink::detach(self) -> core::result::Result<(), aya::programs::ProgramError>
 pub fn aya::programs::fexit::FExitLink::id(&self) -> Self::Id
+impl aya::programs::links::Link for aya::programs::iter::IterLink
+pub type aya::programs::iter::IterLink::Id = aya::programs::iter::IterLinkId
+pub fn aya::programs::iter::IterLink::detach(self) -> core::result::Result<(), aya::programs::ProgramError>
+pub fn aya::programs::iter::IterLink::id(&self) -> Self::Id
 impl aya::programs::links::Link for aya::programs::kprobe::KProbeLink
 pub type aya::programs::kprobe::KProbeLink::Id = aya::programs::kprobe::KProbeLinkId
 pub fn aya::programs::kprobe::KProbeLink::detach(self) -> core::result::Result<(), aya::programs::ProgramError>