Skip to main content

aya_friday/programs/
mod.rs

1//! eBPF program types.
2//!
3//! eBPF programs are loaded inside the kernel and attached to one or more hook
4//! points. Whenever the hook points are reached, the programs are executed.
5//!
6//! # Loading and attaching programs
7//!
8//! When you call [`Ebpf::load_file`] or [`Ebpf::load`], all the programs included
9//! in the object code are parsed and relocated. Programs are not loaded
10//! automatically though, since often you will need to do some application
11//! specific setup before you can actually load them.
12//!
13//! In order to load and attach a program, you need to retrieve it using [`Ebpf::program_mut`],
14//! then call the `load()` and `attach()` methods, for example:
15//!
16//! ```no_run
17//! use aya::{Ebpf, programs::KProbe};
18//!
19//! let mut bpf = Ebpf::load_file("ebpf_programs.o")?;
20//! // intercept_wakeups is the name of the program we want to load
21//! let program: &mut KProbe = bpf.program_mut("intercept_wakeups").unwrap().try_into()?;
22//! program.load()?;
23//! // intercept_wakeups will be called every time try_to_wake_up() is called
24//! // inside the kernel
25//! program.attach("try_to_wake_up", 0)?;
26//! # Ok::<(), aya::EbpfError>(())
27//! ```
28//!
29//! The signature of the `attach()` method varies depending on what kind of
30//! program you're trying to attach.
31//!
32//! [`Ebpf::load_file`]: crate::Ebpf::load_file
33//! [`Ebpf::load`]: crate::Ebpf::load
34//! [`Ebpf::programs`]: crate::Ebpf::programs
35//! [`Ebpf::program`]: crate::Ebpf::program
36//! [`Ebpf::program_mut`]: crate::Ebpf::program_mut
37//! [`maps`]: crate::maps
38
39// modules we don't export
40mod info;
41mod probe;
42mod utils;
43
44// modules we explicitly export so their pub items (Links etc) get exported too
45pub mod cgroup_device;
46pub mod cgroup_skb;
47pub mod cgroup_sock;
48pub mod cgroup_sock_addr;
49pub mod cgroup_sockopt;
50pub mod cgroup_sysctl;
51pub mod extension;
52pub mod fentry;
53pub mod fexit;
54pub mod flow_dissector;
55pub mod iter;
56pub mod kprobe;
57pub mod links;
58pub mod lirc_mode2;
59pub mod lsm;
60pub mod lsm_cgroup;
61pub mod perf_attach;
62pub mod perf_event;
63pub mod raw_trace_point;
64pub mod sk_lookup;
65pub mod sk_msg;
66pub mod sk_reuseport;
67pub mod sk_skb;
68pub mod sock_ops;
69pub mod socket_filter;
70pub mod tc;
71pub mod tp_btf;
72pub mod trace_point;
73pub mod uprobe;
74pub mod xdp;
75
76use std::{
77    borrow::Cow,
78    ffi::CString,
79    io,
80    os::fd::{AsFd, BorrowedFd},
81    path::{Path, PathBuf},
82    sync::Arc,
83};
84
85use aya_obj::{
86    VerifierLog,
87    btf::BtfError,
88    generated::{BPF_F_TEST_XDP_LIVE_FRAMES, bpf_attach_type, bpf_prog_info, bpf_prog_type},
89    programs::XdpAttachType,
90};
91use info::impl_info;
92pub use info::{LsmAttachType, ProgramInfo, ProgramType, loaded_programs};
93use libc::ENOSPC;
94use tc::SchedClassifierLink;
95use thiserror::Error;
96
97// re-export the main items needed to load and attach
98pub use crate::programs::{
99    cgroup_device::CgroupDevice,
100    cgroup_skb::{CgroupSkb, CgroupSkbAttachType},
101    cgroup_sock::{CgroupSock, CgroupSockAttachType},
102    cgroup_sock_addr::{CgroupSockAddr, CgroupSockAddrAttachType},
103    cgroup_sockopt::{CgroupSockopt, CgroupSockoptAttachType},
104    cgroup_sysctl::CgroupSysctl,
105    extension::{Extension, ExtensionError},
106    fentry::FEntry,
107    fexit::FExit,
108    flow_dissector::FlowDissector,
109    iter::Iter,
110    kprobe::{KProbe, KProbeError},
111    links::{CgroupAttachMode, Link, LinkOrder},
112    lirc_mode2::LircMode2,
113    lsm::Lsm,
114    lsm_cgroup::LsmCgroup,
115    perf_event::PerfEvent,
116    probe::ProbeKind,
117    raw_trace_point::RawTracePoint,
118    sk_lookup::SkLookup,
119    sk_msg::SkMsg,
120    sk_reuseport::{SkReuseport, SkReuseportAttachType, SkReuseportError},
121    sk_skb::{SkSkb, SkSkbKind},
122    sock_ops::SockOps,
123    socket_filter::{SocketFilter, SocketFilterError},
124    tc::{SchedClassifier, TcAttachType, TcError, TcHandle},
125    tp_btf::BtfTracePoint,
126    trace_point::{TracePoint, TracePointError},
127    uprobe::{UProbe, UProbeError},
128    xdp::{Xdp, XdpError, XdpMode},
129};
130use crate::{
131    VerifierLogLevel,
132    maps::MapError,
133    pin::PinError,
134    programs::{
135        links::{
136            FdLink, FdLinkId, LinkError, LinkInfo, Links, ProgAttachLink, ProgAttachLinkId,
137            define_link_wrapper, id_as_key, impl_try_from_fdlink, impl_try_into_fdlink,
138        },
139        perf_attach::{PerfLinkIdInner, PerfLinkInner, perf_attach, perf_attach_debugfs},
140    },
141    sys::{
142        EbpfLoadProgramAttrs, NetlinkError, ProgQueryTarget, SyscallError, bpf_btf_get_fd_by_id,
143        bpf_get_object, bpf_link_get_fd_by_id, bpf_load_program, bpf_pin_object,
144        bpf_prog_get_fd_by_id, bpf_prog_query, bpf_prog_test_run, bpf_prog_test_run_raw_tp,
145        bpf_prog_test_run_tracing, iter_link_ids, retry_with_verifier_logs,
146    },
147    util::KernelVersion,
148};
149
150/// Error type returned when working with programs.
151#[derive(Debug, Error)]
152pub enum ProgramError {
153    /// The program is already loaded.
154    #[error("the program is already loaded")]
155    AlreadyLoaded,
156
157    /// The program is not loaded.
158    #[error("the program is not loaded")]
159    NotLoaded,
160
161    /// The program is already attached.
162    #[error("the program was already attached")]
163    AlreadyAttached,
164
165    /// The program is not attached.
166    #[error("the program is not attached")]
167    NotAttached,
168
169    /// Loading the program failed.
170    #[error("the BPF_PROG_LOAD syscall returned {io_error}. Verifier output: {verifier_log}")]
171    LoadError {
172        /// The [`io::Error`] returned by the `BPF_PROG_LOAD` syscall.
173        #[source]
174        io_error: io::Error,
175        /// The error log produced by the kernel verifier.
176        verifier_log: VerifierLog,
177    },
178
179    /// A syscall failed.
180    #[error(transparent)]
181    SyscallError(#[from] SyscallError),
182
183    /// The network interface does not exist.
184    #[error("unknown network interface {name}")]
185    UnknownInterface {
186        /// interface name
187        name: String,
188    },
189
190    /// The program is not of the expected type.
191    #[error("unexpected program type")]
192    UnexpectedProgramType,
193
194    /// A map error occurred while loading or attaching a program.
195    #[error(transparent)]
196    MapError(#[from] MapError),
197
198    /// An error occurred while working with a [`KProbe`].
199    #[error(transparent)]
200    KProbeError(#[from] KProbeError),
201
202    /// An error occurred while working with an [`UProbe`].
203    #[error(transparent)]
204    UProbeError(#[from] UProbeError),
205
206    /// An error occurred while working with a [`TracePoint`].
207    #[error(transparent)]
208    TracePointError(#[from] TracePointError),
209
210    /// An error occurred while working with a [`SocketFilter`].
211    #[error(transparent)]
212    SocketFilterError(#[from] SocketFilterError),
213
214    /// An error occurred while working with a [`SkReuseport`] program.
215    #[error(transparent)]
216    SkReuseportError(#[from] SkReuseportError),
217
218    /// An error occurred while working with an [`Xdp`] program.
219    #[error(transparent)]
220    XdpError(#[from] XdpError),
221
222    /// An error occurred while working with a TC program.
223    #[error(transparent)]
224    TcError(#[from] TcError),
225
226    /// An error occurred while working with an [`Extension`] program.
227    #[error(transparent)]
228    ExtensionError(#[from] ExtensionError),
229
230    /// An error occurred while working with BTF.
231    #[error(transparent)]
232    Btf(#[from] BtfError),
233
234    /// The program is not attached.
235    #[error("the program name `{name}` is invalid")]
236    InvalidName {
237        /// program name
238        name: String,
239    },
240
241    /// An error occurred while working with IO.
242    #[error(transparent)]
243    IOError(#[from] io::Error),
244
245    /// Providing an attach cookie is not supported.
246    #[error("providing an attach cookie is not supported")]
247    AttachCookieNotSupported,
248
249    /// An error occurred while working with Netlink.
250    #[error(transparent)]
251    NetlinkError(#[from] NetlinkError),
252}
253
254/// A [`Program`] file descriptor.
255#[derive(Debug)]
256pub struct ProgramFd(crate::MockableFd);
257
258impl ProgramFd {
259    /// Creates a new instance that shares the same underlying file description as [`self`].
260    pub fn try_clone(&self) -> io::Result<Self> {
261        let Self(inner) = self;
262        let inner = inner.try_clone()?;
263        Ok(Self(inner))
264    }
265}
266
267impl AsFd for ProgramFd {
268    fn as_fd(&self) -> BorrowedFd<'_> {
269        let Self(fd) = self;
270        fd.as_fd()
271    }
272}
273
274/// A [`Program`] identifier.
275pub struct ProgramId(u32);
276
277impl ProgramId {
278    /// Create a new program id.  
279    ///  
280    /// # Safety
281    ///
282    /// This method is unsafe since it doesn't check that the given `id` is a
283    /// valid program id.
284    pub const unsafe fn new(id: u32) -> Self {
285        Self(id)
286    }
287}
288
289/// eBPF program type.
290#[derive(Debug)]
291pub enum Program {
292    /// A [`KProbe`] program
293    KProbe(KProbe),
294    /// A [`UProbe`] program
295    UProbe(UProbe),
296    /// A [`TracePoint`] program
297    TracePoint(TracePoint),
298    /// A [`SocketFilter`] program
299    SocketFilter(SocketFilter),
300    /// A [`Xdp`] program
301    Xdp(Xdp),
302    /// A [`SkMsg`] program
303    SkMsg(SkMsg),
304    /// A [`SkSkb`] program
305    SkSkb(SkSkb),
306    /// A [`CgroupSockAddr`] program
307    CgroupSockAddr(CgroupSockAddr),
308    /// A [`SockOps`] program
309    SockOps(SockOps),
310    /// A [`SchedClassifier`] program
311    SchedClassifier(SchedClassifier),
312    /// A [`CgroupSkb`] program
313    CgroupSkb(CgroupSkb),
314    /// A [`CgroupSysctl`] program
315    CgroupSysctl(CgroupSysctl),
316    /// A [`CgroupSockopt`] program
317    CgroupSockopt(CgroupSockopt),
318    /// A [`LircMode2`] program
319    LircMode2(LircMode2),
320    /// A [`PerfEvent`] program
321    PerfEvent(PerfEvent),
322    /// A [`RawTracePoint`] program
323    RawTracePoint(RawTracePoint),
324    /// A [`Lsm`] program
325    Lsm(Lsm),
326    /// A [`LsmCgroup`] program
327    LsmCgroup(LsmCgroup),
328    /// A [`BtfTracePoint`] program
329    BtfTracePoint(BtfTracePoint),
330    /// A [`FEntry`] program
331    FEntry(FEntry),
332    /// A [`FExit`] program
333    FExit(FExit),
334    /// A [`FlowDissector`] program
335    FlowDissector(FlowDissector),
336    /// A [`Extension`] program
337    Extension(Extension),
338    /// A [`SkLookup`] program
339    SkLookup(SkLookup),
340    /// A [`SkReuseport`] program
341    SkReuseport(SkReuseport),
342    /// A [`CgroupSock`] program
343    CgroupSock(CgroupSock),
344    /// A [`CgroupDevice`] program
345    CgroupDevice(CgroupDevice),
346    /// An [`Iter`] program
347    Iter(Iter),
348}
349
350impl Program {
351    /// Returns the program type.
352    pub const fn prog_type(&self) -> ProgramType {
353        match self {
354            Self::KProbe(_) | Self::UProbe(_) => ProgramType::KProbe,
355            Self::TracePoint(_) => ProgramType::TracePoint,
356            Self::SocketFilter(_) => ProgramType::SocketFilter,
357            Self::Xdp(_) => ProgramType::Xdp,
358            Self::SkMsg(_) => ProgramType::SkMsg,
359            Self::SkSkb(_) => ProgramType::SkSkb,
360            Self::SockOps(_) => ProgramType::SockOps,
361            Self::SchedClassifier(_) => ProgramType::SchedClassifier,
362            Self::CgroupSkb(_) => ProgramType::CgroupSkb,
363            Self::CgroupSysctl(_) => ProgramType::CgroupSysctl,
364            Self::CgroupSockopt(_) => ProgramType::CgroupSockopt,
365            Self::LircMode2(_) => ProgramType::LircMode2,
366            Self::PerfEvent(_) => ProgramType::PerfEvent,
367            Self::RawTracePoint(_) => ProgramType::RawTracePoint,
368            Self::Lsm(_) => ProgramType::Lsm(LsmAttachType::Mac),
369            Self::LsmCgroup(_) => ProgramType::Lsm(LsmAttachType::Cgroup),
370            // The following program types are a subset of `TRACING` programs:
371            //
372            // - `BPF_TRACE_RAW_TP` (`BtfTracePoint`)
373            // - `BTF_TRACE_FENTRY` (`FEntry`)
374            // - `BPF_MODIFY_RETURN` (not supported yet in Aya)
375            // - `BPF_TRACE_FEXIT` (`FExit`)
376            // - `BPF_TRACE_ITER` (`Iter`)
377            //
378            // https://github.com/torvalds/linux/blob/v6.12/kernel/bpf/syscall.c#L3935-L3940
379            Self::BtfTracePoint(_) | Self::FEntry(_) | Self::FExit(_) | Self::Iter(_) => {
380                ProgramType::Tracing
381            }
382            Self::Extension(_) => ProgramType::Extension,
383            Self::CgroupSockAddr(_) => ProgramType::CgroupSockAddr,
384            Self::SkLookup(_) => ProgramType::SkLookup,
385            Self::SkReuseport(_) => ProgramType::SkReuseport,
386            Self::CgroupSock(_) => ProgramType::CgroupSock,
387            Self::CgroupDevice(_) => ProgramType::CgroupDevice,
388            Self::FlowDissector(_) => ProgramType::FlowDissector,
389        }
390    }
391
392    /// Pin the program to the provided path
393    pub fn pin<P: AsRef<Path>>(&mut self, path: P) -> Result<(), PinError> {
394        match self {
395            Self::KProbe(p) => p.pin(path),
396            Self::UProbe(p) => p.pin(path),
397            Self::TracePoint(p) => p.pin(path),
398            Self::SocketFilter(p) => p.pin(path),
399            Self::Xdp(p) => p.pin(path),
400            Self::SkMsg(p) => p.pin(path),
401            Self::SkSkb(p) => p.pin(path),
402            Self::SockOps(p) => p.pin(path),
403            Self::SchedClassifier(p) => p.pin(path),
404            Self::CgroupSkb(p) => p.pin(path),
405            Self::CgroupSysctl(p) => p.pin(path),
406            Self::CgroupSockopt(p) => p.pin(path),
407            Self::LircMode2(p) => p.pin(path),
408            Self::PerfEvent(p) => p.pin(path),
409            Self::RawTracePoint(p) => p.pin(path),
410            Self::Lsm(p) => p.pin(path),
411            Self::LsmCgroup(p) => p.pin(path),
412            Self::BtfTracePoint(p) => p.pin(path),
413            Self::FEntry(p) => p.pin(path),
414            Self::FExit(p) => p.pin(path),
415            Self::FlowDissector(p) => p.pin(path),
416            Self::Extension(p) => p.pin(path),
417            Self::CgroupSockAddr(p) => p.pin(path),
418            Self::SkLookup(p) => p.pin(path),
419            Self::SkReuseport(p) => p.pin(path),
420            Self::CgroupSock(p) => p.pin(path),
421            Self::CgroupDevice(p) => p.pin(path),
422            Self::Iter(p) => p.pin(path),
423        }
424    }
425
426    /// Unloads the program from the kernel.
427    pub fn unload(self) -> Result<(), ProgramError> {
428        match self {
429            Self::KProbe(mut p) => p.unload(),
430            Self::UProbe(mut p) => p.unload(),
431            Self::TracePoint(mut p) => p.unload(),
432            Self::SocketFilter(mut p) => p.unload(),
433            Self::Xdp(mut p) => p.unload(),
434            Self::SkMsg(mut p) => p.unload(),
435            Self::SkSkb(mut p) => p.unload(),
436            Self::SockOps(mut p) => p.unload(),
437            Self::SchedClassifier(mut p) => p.unload(),
438            Self::CgroupSkb(mut p) => p.unload(),
439            Self::CgroupSysctl(mut p) => p.unload(),
440            Self::CgroupSockopt(mut p) => p.unload(),
441            Self::LircMode2(mut p) => p.unload(),
442            Self::PerfEvent(mut p) => p.unload(),
443            Self::RawTracePoint(mut p) => p.unload(),
444            Self::Lsm(mut p) => p.unload(),
445            Self::LsmCgroup(mut p) => p.unload(),
446            Self::BtfTracePoint(mut p) => p.unload(),
447            Self::FEntry(mut p) => p.unload(),
448            Self::FExit(mut p) => p.unload(),
449            Self::FlowDissector(mut p) => p.unload(),
450            Self::Extension(mut p) => p.unload(),
451            Self::CgroupSockAddr(mut p) => p.unload(),
452            Self::SkLookup(mut p) => p.unload(),
453            Self::SkReuseport(mut p) => p.unload(),
454            Self::CgroupSock(mut p) => p.unload(),
455            Self::CgroupDevice(mut p) => p.unload(),
456            Self::Iter(mut p) => p.unload(),
457        }
458    }
459
460    /// Returns the file descriptor of a program.
461    ///
462    /// Can be used to add a program to a [`crate::maps::ProgramArray`] or attach an [`Extension`] program.
463    pub fn fd(&self) -> Result<&ProgramFd, ProgramError> {
464        match self {
465            Self::KProbe(p) => p.fd(),
466            Self::UProbe(p) => p.fd(),
467            Self::TracePoint(p) => p.fd(),
468            Self::SocketFilter(p) => p.fd(),
469            Self::Xdp(p) => p.fd(),
470            Self::SkMsg(p) => p.fd(),
471            Self::SkSkb(p) => p.fd(),
472            Self::SockOps(p) => p.fd(),
473            Self::SchedClassifier(p) => p.fd(),
474            Self::CgroupSkb(p) => p.fd(),
475            Self::CgroupSysctl(p) => p.fd(),
476            Self::CgroupSockopt(p) => p.fd(),
477            Self::LircMode2(p) => p.fd(),
478            Self::PerfEvent(p) => p.fd(),
479            Self::RawTracePoint(p) => p.fd(),
480            Self::Lsm(p) => p.fd(),
481            Self::LsmCgroup(p) => p.fd(),
482            Self::BtfTracePoint(p) => p.fd(),
483            Self::FEntry(p) => p.fd(),
484            Self::FExit(p) => p.fd(),
485            Self::FlowDissector(p) => p.fd(),
486            Self::Extension(p) => p.fd(),
487            Self::CgroupSockAddr(p) => p.fd(),
488            Self::SkLookup(p) => p.fd(),
489            Self::SkReuseport(p) => p.fd(),
490            Self::CgroupSock(p) => p.fd(),
491            Self::CgroupDevice(p) => p.fd(),
492            Self::Iter(p) => p.fd(),
493        }
494    }
495
496    /// Returns information about a loaded program with the [`ProgramInfo`] structure.
497    ///
498    /// This information is populated at load time by the kernel and can be used
499    /// to get kernel details for a given [`Program`].
500    pub fn info(&self) -> Result<ProgramInfo, ProgramError> {
501        match self {
502            Self::KProbe(p) => p.info(),
503            Self::UProbe(p) => p.info(),
504            Self::TracePoint(p) => p.info(),
505            Self::SocketFilter(p) => p.info(),
506            Self::Xdp(p) => p.info(),
507            Self::SkMsg(p) => p.info(),
508            Self::SkSkb(p) => p.info(),
509            Self::SockOps(p) => p.info(),
510            Self::SchedClassifier(p) => p.info(),
511            Self::CgroupSkb(p) => p.info(),
512            Self::CgroupSysctl(p) => p.info(),
513            Self::CgroupSockopt(p) => p.info(),
514            Self::LircMode2(p) => p.info(),
515            Self::PerfEvent(p) => p.info(),
516            Self::RawTracePoint(p) => p.info(),
517            Self::Lsm(p) => p.info(),
518            Self::LsmCgroup(p) => p.info(),
519            Self::BtfTracePoint(p) => p.info(),
520            Self::FEntry(p) => p.info(),
521            Self::FExit(p) => p.info(),
522            Self::FlowDissector(p) => p.info(),
523            Self::Extension(p) => p.info(),
524            Self::CgroupSockAddr(p) => p.info(),
525            Self::SkLookup(p) => p.info(),
526            Self::SkReuseport(p) => p.info(),
527            Self::CgroupSock(p) => p.info(),
528            Self::CgroupDevice(p) => p.info(),
529            Self::Iter(p) => p.info(),
530        }
531    }
532}
533
534#[derive(Debug)]
535pub(crate) struct ProgramData<T: Link> {
536    pub(crate) name: Option<Cow<'static, str>>,
537    pub(crate) obj: Option<(aya_obj::Program, aya_obj::Function)>,
538    pub(crate) fd: Option<ProgramFd>,
539    pub(crate) links: Links<T>,
540    pub(crate) attach_btf_obj_fd: Option<crate::MockableFd>,
541    pub(crate) attach_btf_id: Option<u32>,
542    pub(crate) attach_prog_fd: Option<ProgramFd>,
543    pub(crate) btf_fd: Option<Arc<crate::MockableFd>>,
544    pub(crate) verifier_log_level: VerifierLogLevel,
545    pub(crate) path: Option<PathBuf>,
546    pub(crate) flags: u32,
547}
548
549impl<T: Link> ProgramData<T> {
550    pub(crate) fn new(
551        name: Option<Cow<'static, str>>,
552        obj: (aya_obj::Program, aya_obj::Function),
553        btf_fd: Option<Arc<crate::MockableFd>>,
554        verifier_log_level: VerifierLogLevel,
555    ) -> Self {
556        Self {
557            name,
558            obj: Some(obj),
559            fd: None,
560            links: Links::new(),
561            attach_btf_obj_fd: None,
562            attach_btf_id: None,
563            attach_prog_fd: None,
564            btf_fd,
565            verifier_log_level,
566            path: None,
567            flags: 0,
568        }
569    }
570
571    pub(crate) fn from_bpf_prog_info(
572        name: Option<Cow<'static, str>>,
573        fd: crate::MockableFd,
574        path: &Path,
575        info: bpf_prog_info,
576        verifier_log_level: VerifierLogLevel,
577    ) -> Result<Self, ProgramError> {
578        let attach_btf_id = (info.attach_btf_id > 0).then_some(info.attach_btf_id);
579        let attach_btf_obj_fd = (info.attach_btf_obj_id != 0)
580            .then(|| bpf_btf_get_fd_by_id(info.attach_btf_obj_id))
581            .transpose()?;
582
583        Ok(Self {
584            name,
585            obj: None,
586            fd: Some(ProgramFd(fd)),
587            links: Links::new(),
588            attach_btf_obj_fd,
589            attach_btf_id,
590            attach_prog_fd: None,
591            btf_fd: None,
592            verifier_log_level,
593            path: Some(path.to_path_buf()),
594            flags: 0,
595        })
596    }
597
598    pub(crate) fn from_pinned_path<P: AsRef<Path>>(
599        path: P,
600        verifier_log_level: VerifierLogLevel,
601    ) -> Result<Self, ProgramError> {
602        use std::os::unix::ffi::OsStrExt as _;
603
604        // TODO: avoid this unwrap by adding a new error variant.
605        let path_string = CString::new(path.as_ref().as_os_str().as_bytes()).unwrap();
606        let fd = bpf_get_object(&path_string).map_err(|io_error| SyscallError {
607            call: "bpf_obj_get",
608            io_error,
609        })?;
610
611        let info = ProgramInfo::new_from_fd(fd.as_fd())?;
612        let name = info.name_as_str().map(ToOwned::to_owned).map(Into::into);
613        Self::from_bpf_prog_info(name, fd, path.as_ref(), info.0, verifier_log_level)
614    }
615}
616
617impl<T: Link> ProgramData<T> {
618    fn fd(&self) -> Result<&ProgramFd, ProgramError> {
619        self.fd.as_ref().ok_or(ProgramError::NotLoaded)
620    }
621}
622
623fn test_run<T: Link>(
624    data: &ProgramData<T>,
625    opts: TestRunOptions<'_>,
626) -> Result<TestRunResult, ProgramError> {
627    let fd = data.fd()?.as_fd();
628    bpf_prog_test_run(fd, opts).map_err(Into::into)
629}
630
631fn test_run_raw_tp<T: Link>(
632    data: &ProgramData<T>,
633    opts: RawTracePointRunOptions,
634) -> Result<RawTracePointTestRunResult, ProgramError> {
635    let fd = data.fd()?.as_fd();
636    bpf_prog_test_run_raw_tp(fd, opts).map_err(Into::into)
637}
638
639fn test_run_tracing<T: Link>(data: &ProgramData<T>) -> Result<(), ProgramError> {
640    let fd = data.fd()?.as_fd();
641    bpf_prog_test_run_tracing(fd).map_err(Into::into)
642}
643
644fn unload_program<T: Link>(data: &mut ProgramData<T>) -> Result<(), ProgramError> {
645    data.links.remove_all()?;
646    data.fd
647        .take()
648        .ok_or(ProgramError::NotLoaded)
649        .map(|ProgramFd { .. }| ())
650}
651
652fn pin_program<T: Link, P: AsRef<Path>>(data: &ProgramData<T>, path: P) -> Result<(), PinError> {
653    use std::os::unix::ffi::OsStrExt as _;
654
655    let fd = data.fd.as_ref().ok_or_else(|| PinError::NoFd {
656        name: data
657            .name
658            .as_deref()
659            .unwrap_or("<unknown program>")
660            .to_string(),
661    })?;
662    let path = path.as_ref();
663    let path_string =
664        CString::new(path.as_os_str().as_bytes()).map_err(|error| PinError::InvalidPinPath {
665            path: path.into(),
666            error,
667        })?;
668    bpf_pin_object(fd.as_fd(), &path_string).map_err(|io_error| SyscallError {
669        call: "BPF_OBJ_PIN",
670        io_error,
671    })?;
672    Ok(())
673}
674
675fn load_program_without_attach_type<T: Link>(
676    prog_type: bpf_prog_type,
677    data: &mut ProgramData<T>,
678) -> Result<(), ProgramError> {
679    load_program(prog_type, None, data)
680}
681
682fn load_program_with_attach_type<A: Into<bpf_attach_type>, T: Link>(
683    prog_type: bpf_prog_type,
684    expected_attach_type: A,
685    data: &mut ProgramData<T>,
686) -> Result<(), ProgramError> {
687    load_program(prog_type, Some(expected_attach_type.into()), data)
688}
689
690fn load_program<T: Link>(
691    prog_type: bpf_prog_type,
692    expected_attach_type: Option<bpf_attach_type>,
693    data: &mut ProgramData<T>,
694) -> Result<(), ProgramError> {
695    let ProgramData {
696        name,
697        obj,
698        fd,
699        links: _,
700        attach_btf_obj_fd,
701        attach_btf_id,
702        attach_prog_fd,
703        btf_fd,
704        verifier_log_level,
705        path: _,
706        flags,
707    } = data;
708    if fd.is_some() {
709        return Err(ProgramError::AlreadyLoaded);
710    }
711    if obj.is_none() {
712        // This program was loaded from a pin in bpffs
713        return Err(ProgramError::AlreadyLoaded);
714    }
715    let obj = obj.as_ref().unwrap();
716    let (
717        aya_obj::Program {
718            license,
719            kernel_version,
720            ..
721        },
722        aya_obj::Function {
723            instructions,
724            func_info,
725            line_info,
726            func_info_rec_size,
727            line_info_rec_size,
728            ..
729        },
730    ) = obj;
731
732    let target_kernel_version =
733        kernel_version.unwrap_or_else(|| KernelVersion::current().map_or(0, KernelVersion::code));
734
735    let prog_name = if let Some(name) = name.as_deref() {
736        let prog_name = CString::new(name).map_err(|err @ std::ffi::NulError { .. }| {
737            let name = err.into_vec();
738            let name = unsafe { String::from_utf8_unchecked(name) };
739            ProgramError::InvalidName { name }
740        })?;
741        Some(prog_name)
742    } else {
743        None
744    };
745
746    let attr = EbpfLoadProgramAttrs {
747        name: prog_name,
748        ty: prog_type,
749        insns: instructions,
750        license,
751        kernel_version: target_kernel_version,
752        expected_attach_type,
753        prog_btf_fd: btf_fd.as_ref().map(|f| f.as_fd()),
754        attach_btf_obj_fd: attach_btf_obj_fd.as_ref().map(|fd| fd.as_fd()),
755        attach_btf_id: *attach_btf_id,
756        attach_prog_fd: attach_prog_fd.as_ref().map(|fd| fd.as_fd()),
757        func_info_rec_size: *func_info_rec_size,
758        func_info: func_info.clone(),
759        line_info_rec_size: *line_info_rec_size,
760        line_info: line_info.clone(),
761        flags: *flags,
762    };
763
764    let (ret, verifier_log) = retry_with_verifier_logs(10, |logger| {
765        bpf_load_program(&attr, logger, *verifier_log_level)
766    });
767
768    match ret {
769        Ok(prog_fd) => {
770            *fd = Some(ProgramFd(prog_fd));
771            Ok(())
772        }
773        Err(io_error) => Err(ProgramError::LoadError {
774            io_error,
775            verifier_log,
776        }),
777    }
778}
779
780pub(crate) fn query(
781    target: ProgQueryTarget<'_>,
782    attach_type: bpf_attach_type,
783    query_flags: u32,
784    attach_flags: &mut Option<u32>,
785) -> Result<(u64, Vec<u32>), ProgramError> {
786    let mut prog_ids = vec![0u32; 64];
787    let mut prog_cnt = prog_ids.len() as u32;
788    let mut revision = 0;
789
790    let mut retries = 0;
791
792    loop {
793        match bpf_prog_query(
794            &target,
795            attach_type,
796            query_flags,
797            attach_flags.as_mut(),
798            &mut prog_ids,
799            &mut prog_cnt,
800            &mut revision,
801        ) {
802            Ok(()) => {
803                prog_ids.resize(prog_cnt as usize, 0);
804                return Ok((revision, prog_ids));
805            }
806            Err(io_error) => {
807                if retries == 0 && io_error.raw_os_error() == Some(ENOSPC) {
808                    prog_ids.resize(prog_cnt as usize, 0);
809                    retries += 1;
810                } else {
811                    return Err(SyscallError {
812                        call: "bpf_prog_query",
813                        io_error,
814                    }
815                    .into());
816                }
817            }
818        }
819    }
820}
821
822macro_rules! impl_program_unload {
823    ($($struct_name:ident),+ $(,)?) => {
824        $(
825            impl $struct_name {
826                /// Unloads the program from the kernel.
827                ///
828                /// Tracked links will be detached before unloading the program.
829                /// Attachment mechanisms that do not create tracked links are
830                /// not affected. Note that owned links obtained using
831                /// `take_link()` will not be detached.
832                pub fn unload(&mut self) -> Result<(), ProgramError> {
833                    unload_program(&mut self.data)
834                }
835            }
836
837            impl Drop for $struct_name {
838                fn drop(&mut self) {
839                    let _unused: Result<(), ProgramError> = self.unload();
840                }
841            }
842        )+
843    }
844}
845
846impl_program_unload!(
847    KProbe,
848    UProbe,
849    TracePoint,
850    SocketFilter,
851    Xdp,
852    SkMsg,
853    SkSkb,
854    SchedClassifier,
855    CgroupSkb,
856    CgroupSysctl,
857    CgroupSockopt,
858    LircMode2,
859    PerfEvent,
860    Lsm,
861    LsmCgroup,
862    RawTracePoint,
863    BtfTracePoint,
864    FEntry,
865    FExit,
866    FlowDissector,
867    Extension,
868    CgroupSockAddr,
869    SkLookup,
870    SkReuseport,
871    SockOps,
872    CgroupSock,
873    CgroupDevice,
874    Iter,
875);
876
877macro_rules! impl_fd {
878    ($($struct_name:ident),+ $(,)?) => {
879        $(
880            impl $struct_name {
881                /// Returns the file descriptor of this Program.
882                pub fn fd(&self) -> Result<&ProgramFd, ProgramError> {
883                    self.data.fd()
884                }
885            }
886        )+
887    }
888}
889
890impl_fd!(
891    KProbe,
892    UProbe,
893    TracePoint,
894    SocketFilter,
895    Xdp,
896    SkMsg,
897    SkSkb,
898    SchedClassifier,
899    CgroupSkb,
900    CgroupSysctl,
901    CgroupSockopt,
902    LircMode2,
903    PerfEvent,
904    Lsm,
905    LsmCgroup,
906    RawTracePoint,
907    BtfTracePoint,
908    FEntry,
909    FExit,
910    FlowDissector,
911    Extension,
912    CgroupSockAddr,
913    SkLookup,
914    SkReuseport,
915    SockOps,
916    CgroupSock,
917    CgroupDevice,
918    Iter,
919);
920
921/// Kernel-side execution attributes for [`TestRunOptions`].
922///
923/// Controls *how* the kernel runs the test (XDP batch size, and
924/// the associated flag bits), as opposed to *what* data is passed.
925#[derive(Debug)]
926pub struct TestRunAttrs {
927    pub(crate) batch_size: u32,
928    pub(crate) flags: u32,
929}
930
931impl TestRunAttrs {
932    /// Creates a new `TestRunAttrs` with default values.
933    pub const fn new() -> Self {
934        Self {
935            batch_size: 0,
936            flags: 0,
937        }
938    }
939
940    /// Sets the batch size for XDP live frames testing.
941    ///
942    /// This automatically sets the `BPF_F_TEST_XDP_LIVE_FRAMES` flag.
943    /// This option only works with `XDP` programs.
944    #[must_use]
945    pub const fn xdp_live_frames(mut self, batch_size: u32) -> Self {
946        self.batch_size = batch_size;
947        self.flags |= BPF_F_TEST_XDP_LIVE_FRAMES;
948        self
949    }
950}
951
952impl Default for TestRunAttrs {
953    fn default() -> Self {
954        Self::new()
955    }
956}
957
958/// Options for running a BPF program test.
959///
960/// See [kernel doc](https://docs.kernel.org/bpf/bpf_prog_run.html)
961/// and [ebpf.io](https://docs.ebpf.io/linux/syscall/BPF_PROG_TEST_RUN/) for detailed usage.
962#[derive(Debug)]
963pub struct TestRunOptions<'a> {
964    /// Input packet data to pass to the program.
965    pub data_in: Option<&'a [u8]>,
966    /// Output buffer for packet data modified by the program.
967    pub data_out: Option<&'a mut [u8]>,
968    /// Input context to pass to the program.
969    pub ctx_in: Option<&'a [u8]>,
970    /// Output buffer for context written back by the program.
971    pub ctx_out: Option<&'a mut [u8]>,
972    /// Number of times to repeat the test. Defaults to `1`.
973    pub repeat: u32,
974    /// Kernel execution attributes (XDP batch size).
975    pub attrs: TestRunAttrs,
976}
977
978impl Default for TestRunOptions<'_> {
979    fn default() -> Self {
980        Self::new()
981    }
982}
983
984impl TestRunOptions<'_> {
985    /// Creates a new `TestRunOptions` with default values.
986    pub const fn new() -> Self {
987        Self {
988            data_in: None,
989            data_out: None,
990            ctx_in: None,
991            ctx_out: None,
992            repeat: 1,
993            attrs: TestRunAttrs::new(),
994        }
995    }
996}
997
998/// Options for running a [`RawTracePoint`] program test via `BPF_PROG_TEST_RUN`.
999///
1000/// The kernel's `bpf_prog_test_run_raw_tp` handler (v5.10, commit `b84e6faeed1`)
1001/// is far more restrictive than the skb/XDP path:
1002///
1003/// - `data_in`, `data_out`, `ctx_out`, `repeat`, `batch_size` must all be zero/NULL;
1004///   passing any of them returns `EINVAL`.
1005/// - `ctx_in` is the only data path: a packed array of `u64` tracepoint arguments.
1006/// - CPU pinning via `BPF_F_TEST_RUN_ON_CPU` is supported and is the primary
1007///   reason to use `BPF_PROG_TEST_RUN` with raw tracepoints.
1008///
1009/// This struct deliberately omits the forbidden fields so misuse is a
1010/// compile-time error rather than a runtime `EINVAL`.
1011#[derive(Debug, Default)]
1012pub struct RawTracePointRunOptions {
1013    /// Fake tracepoint arguments, up to 12 `u64` values.
1014    ///
1015    /// Each element corresponds to one tracepoint argument in order. The kernel
1016    /// copies the entire array into `ctx_in` before calling the program, which
1017    /// reads them via `ctx.arg(n)`. Unused slots default to zero.
1018    ///
1019    /// The array size of 12 matches the kernel's maximum: the longest tracepoint
1020    /// in the kernel takes 12 arguments. See
1021    /// [`net/bpf/test_run.c`](https://github.com/torvalds/linux/blob/d91a46d680/net/bpf/test_run.c#L762).
1022    pub args: [u64; 12],
1023    /// If `Some(cpu)`, pin execution to that CPU via `BPF_F_TEST_RUN_ON_CPU`.
1024    ///
1025    /// The only flag the kernel accepts for raw tracepoints; `batch_size` and
1026    /// all other [`TestRunAttrs`] knobs return `EINVAL`.
1027    pub cpu: Option<u32>,
1028}
1029
1030impl RawTracePointRunOptions {
1031    /// Creates a new `RawTracePointRunOptions` with all arguments zeroed.
1032    pub const fn new() -> Self {
1033        Self {
1034            args: [0; 12],
1035            cpu: None,
1036        }
1037    }
1038}
1039
1040/// Result of running a BPF program test.
1041#[derive(Debug)]
1042pub struct TestRunResult {
1043    /// Return value from the program.
1044    pub return_value: u32,
1045    /// Duration of the test run.
1046    pub duration: std::time::Duration,
1047    /// Size of data written to `data_out`.
1048    pub data_size_out: u32,
1049    /// Size of context written to `ctx_out`.
1050    pub ctx_size_out: u32,
1051}
1052
1053/// Result of running a [`RawTracePoint`] program test via `BPF_PROG_TEST_RUN`.
1054///
1055/// Only `return_value` is returned by the kernel; `duration`, `data_size_out`,
1056/// and `ctx_size_out` are omitted.
1057#[derive(Debug)]
1058pub struct RawTracePointTestRunResult {
1059    /// Return value from the program.
1060    pub return_value: u32,
1061}
1062
1063/// Trait for BPF programs that support test execution via `BPF_PROG_TEST_RUN`.
1064pub trait TestRun {
1065    /// The options type used to configure a single test invocation.
1066    ///
1067    /// Different program types require different options: skb/XDP programs use
1068    /// [`TestRunOptions`], raw tracepoint programs use
1069    /// [`RawTracePointRunOptions`], and [`FExit`] programs use `()`. See
1070    /// [`FExit`] for the tracing-specific test-run semantics.
1071    type Opts<'a>;
1072
1073    /// The Result type for a single test invocation.
1074    type Result;
1075
1076    /// Runs the program with test input data and returns the result.
1077    ///
1078    /// This function uses the kernel's `BPF_PROG_TEST_RUN` command to execute
1079    /// the program in a test environment with provided input data.
1080    ///
1081    /// # Arguments
1082    ///
1083    /// * `opts` - Test run options including input/output data and context
1084    ///
1085    /// # Returns
1086    ///
1087    /// Returns a [`Self::Result`] containing the program-specific test output.
1088    ///
1089    /// For most program types this is [`crate::TestRunResult`]. For
1090    /// [`RawTracePoint`] it is [`RawTracePointTestRunResult`]. For [`FExit`]
1091    /// it is `()`; see [`FExit`] for what a successful tracing test run means.
1092    ///
1093    /// # Errors
1094    ///
1095    /// Returns [`ProgramError::SyscallError`] if the underlying syscall fails.
1096    /// Common errors include `-ENOSPC` if output buffers are too small.
1097    ///
1098    /// # Example
1099    ///
1100    /// ```ignore
1101    /// let input_data = [0u8; 64];
1102    /// let mut output_data = [0u8; 64];
1103    ///
1104    /// let opts = TestRunOptions {
1105    ///     data_in: Some(&input_data),
1106    ///     data_out: Some(&mut output_data),
1107    ///     repeat: 1,
1108    ///     ..Default::default()
1109    /// };
1110    ///
1111    /// let result = program.test_run(opts)?;
1112    /// println!("Program returned: {}, took {} ns", result.return_value, result.duration.as_nanos());
1113    /// # Ok::<(), Box<dyn std::error::Error>>(())
1114    /// ```
1115    fn test_run(&self, opts: Self::Opts<'_>) -> Result<Self::Result, ProgramError>;
1116}
1117
1118macro_rules! impl_program_test_run {
1119    ($($struct_name:ident), + $(,)?) => {
1120        $(
1121            impl TestRun for $struct_name {
1122                type Opts<'a> = TestRunOptions<'a>;
1123                type Result = TestRunResult;
1124
1125                fn test_run(&self, opts: Self::Opts<'_>) -> Result<Self::Result, ProgramError> {
1126                    test_run(&self.data, opts)
1127                }
1128            }
1129        )+
1130    }
1131}
1132
1133impl_program_test_run!(SocketFilter, SchedClassifier, Xdp, CgroupSkb, FlowDissector);
1134
1135impl TestRun for RawTracePoint {
1136    type Opts<'a> = RawTracePointRunOptions;
1137    type Result = RawTracePointTestRunResult;
1138
1139    fn test_run(&self, opts: Self::Opts<'_>) -> Result<Self::Result, ProgramError> {
1140        test_run_raw_tp(&self.data, opts)
1141    }
1142}
1143
1144impl TestRun for FExit {
1145    // The kernel tracing test-run handler uses a fixed synthetic fentry/fexit
1146    // call sequence; packet data, context data, repeat count, CPU pinning, and
1147    // batch flags do not apply.
1148    // https://github.com/torvalds/linux/blob/v7.1-rc4/net/bpf/test_run.c#L690-L735
1149    type Opts<'a> = ();
1150    // For fentry/fexit, test-run success only reports that the kernel's fixed
1151    // synthetic call sequence ran.
1152    type Result = ();
1153
1154    fn test_run(&self, _opts: Self::Opts<'_>) -> Result<Self::Result, ProgramError> {
1155        test_run_tracing(&self.data)
1156    }
1157}
1158
1159/// Trait implemented by the [`Program`] types which support the kernel's
1160/// [generic multi-prog API](https://github.com/torvalds/linux/commit/053c8e1f235dc3f69d13375b32f4209228e1cb96).
1161///
1162/// # Minimum kernel version
1163///
1164/// The minimum kernel version required to use this feature is 6.6.0.
1165pub trait MultiProgram {
1166    /// Borrows the file descriptor.
1167    fn fd(&self) -> Result<BorrowedFd<'_>, ProgramError>;
1168}
1169
1170macro_rules! impl_multiprog_fd {
1171    ($($struct_name:ident),+ $(,)?) => {
1172        $(
1173            impl MultiProgram for $struct_name {
1174                fn fd(&self) -> Result<BorrowedFd<'_>, ProgramError> {
1175                    Ok(self.fd()?.as_fd())
1176                }
1177            }
1178        )+
1179    }
1180}
1181
1182impl_multiprog_fd!(SchedClassifier);
1183
1184/// Trait implemented by the [`Link`] types which support the kernel's
1185/// [generic multi-prog API](https://github.com/torvalds/linux/commit/053c8e1f235dc3f69d13375b32f4209228e1cb96).
1186///
1187/// # Minimum kernel version
1188///
1189/// The minimum kernel version required to use this feature is 6.6.0.
1190pub trait MultiProgLink {
1191    /// Borrows the file descriptor.
1192    fn fd(&self) -> Result<BorrowedFd<'_>, LinkError>;
1193}
1194
1195macro_rules! impl_multiproglink_fd {
1196    ($($struct_name:ident),+ $(,)?) => {
1197        $(
1198            impl MultiProgLink for $struct_name {
1199                fn fd(&self) -> Result<BorrowedFd<'_>, LinkError> {
1200                    let link: &FdLink = self.try_into()?;
1201                    Ok(link.fd.as_fd())
1202                }
1203            }
1204        )+
1205    }
1206}
1207
1208impl_multiproglink_fd!(SchedClassifierLink);
1209
1210macro_rules! impl_program_pin{
1211    ($($struct_name:ident),+ $(,)?) => {
1212        $(
1213            impl $struct_name {
1214                /// Pins the program to a BPF filesystem.
1215                ///
1216                /// When a BPF object is pinned to a BPF filesystem it will remain loaded after
1217                /// Aya has unloaded the program.
1218                /// To remove the program, the file on the BPF filesystem must be removed.
1219                /// Any directories in the the path provided should have been created by the caller.
1220                pub fn pin<P: AsRef<Path>>(&mut self, path: P) -> Result<(), PinError> {
1221                    self.data.path = Some(path.as_ref().to_path_buf());
1222                    pin_program(&self.data, path)
1223                }
1224
1225                /// Removes the pinned link from the filesystem.
1226                pub fn unpin(&mut self) -> Result<(), io::Error> {
1227                    if let Some(path) = self.data.path.take() {
1228                        std::fs::remove_file(path)?;
1229                    }
1230                    Ok(())
1231                }
1232            }
1233        )+
1234    }
1235}
1236
1237impl_program_pin!(
1238    KProbe,
1239    UProbe,
1240    TracePoint,
1241    SocketFilter,
1242    Xdp,
1243    SkMsg,
1244    SkSkb,
1245    SchedClassifier,
1246    CgroupSkb,
1247    CgroupSysctl,
1248    CgroupSockopt,
1249    LircMode2,
1250    PerfEvent,
1251    Lsm,
1252    LsmCgroup,
1253    RawTracePoint,
1254    BtfTracePoint,
1255    FEntry,
1256    FExit,
1257    FlowDissector,
1258    Extension,
1259    CgroupSockAddr,
1260    SkLookup,
1261    SkReuseport,
1262    SockOps,
1263    CgroupSock,
1264    CgroupDevice,
1265    Iter,
1266);
1267
1268macro_rules! impl_from_pin {
1269    ($($struct_name:ident),+ $(,)?) => {
1270        $(
1271            impl $struct_name {
1272                /// Creates a program from a pinned entry on a bpffs.
1273                ///
1274                /// Existing links will not be populated. To work with existing links you should use [`crate::programs::links::PinnedLink`].
1275                ///
1276                /// On drop, any managed links are detached and the program is unloaded. This will not result in
1277                /// the program being unloaded from the kernel if it is still pinned.
1278                pub fn from_pin<P: AsRef<Path>>(path: P) -> Result<Self, ProgramError> {
1279                    let data = ProgramData::from_pinned_path(path, VerifierLogLevel::default())?;
1280                    Ok(Self { data })
1281                }
1282            }
1283        )+
1284    }
1285}
1286
1287// Use impl_from_pin if the program doesn't require additional data
1288impl_from_pin!(
1289    TracePoint,
1290    SkMsg,
1291    CgroupSysctl,
1292    LircMode2,
1293    Lsm,
1294    PerfEvent,
1295    RawTracePoint,
1296    BtfTracePoint,
1297    FEntry,
1298    FExit,
1299    FlowDissector,
1300    Extension,
1301    SkLookup,
1302    SockOps,
1303    CgroupDevice,
1304    Iter,
1305);
1306
1307macro_rules! impl_from_prog_info {
1308    (
1309        $(#[$doc:meta])*
1310        @safety
1311            [$($safety:tt)?]
1312        @rest
1313            $struct_name:ident $($var:ident : $var_ty:ty)?
1314    ) => {
1315        impl $struct_name {
1316            /// Constructs an instance of a [`Self`] from a [`ProgramInfo`].
1317            ///
1318            /// This allows the caller to get a handle to an already loaded
1319            /// program from the kernel without having to load it again.
1320            ///
1321            /// # Errors
1322            ///
1323            /// - If the program type reported by the kernel does not match
1324            ///   [`Self::PROGRAM_TYPE`].
1325            /// - If the file descriptor of the program cannot be cloned.
1326            $(#[$doc])*
1327            pub $($safety)?
1328            fn from_program_info(
1329                info: ProgramInfo,
1330                name: Cow<'static, str>,
1331                $($var: $var_ty,)?
1332            ) -> Result<Self, ProgramError> {
1333                if info.program_type() != Self::PROGRAM_TYPE.into() {
1334                    return Err(ProgramError::UnexpectedProgramType {});
1335                }
1336                let ProgramInfo(bpf_program_info) = info;
1337                let fd = info.fd()?;
1338                let fd = fd.as_fd().try_clone_to_owned()?;
1339
1340                Ok(Self {
1341                    data: ProgramData::from_bpf_prog_info(
1342                        Some(name),
1343                        crate::MockableFd::from_fd(fd),
1344                        Path::new(""),
1345                        bpf_program_info,
1346                        VerifierLogLevel::default(),
1347                    )?,
1348                    $($var,)?
1349                })
1350            }
1351        }
1352    };
1353
1354    // Handle unsafe cases and pass a safety doc section
1355    (
1356        unsafe $struct_name:ident $($var:ident : $var_ty:ty)? $(, $($rest:tt)*)?
1357    ) => {
1358        impl_from_prog_info! {
1359            ///
1360            /// # Safety
1361            ///
1362            /// The runtime type of this program, as used by the kernel, is
1363            /// overloaded. We assert the program type matches the runtime type
1364            /// but we're unable to perform further checks. Therefore, the caller
1365            /// must ensure that the program type is correct or the behavior is
1366            /// undefined.
1367            @safety [unsafe]
1368            @rest $struct_name $($var : $var_ty)?
1369        }
1370        $( impl_from_prog_info!($($rest)*); )?
1371    };
1372
1373    // Handle non-unsafe cases and omit safety doc section
1374    (
1375        $struct_name:ident $($var:ident : $var_ty:ty)? $(, $($rest:tt)*)?
1376    ) => {
1377        impl_from_prog_info! {
1378            @safety []
1379            @rest $struct_name $($var : $var_ty)?
1380        }
1381        $( impl_from_prog_info!($($rest)*); )?
1382    };
1383
1384    // Handle trailing comma
1385    (
1386        $(,)?
1387    ) => {};
1388}
1389
1390impl_from_prog_info!(
1391    unsafe KProbe kind : ProbeKind,
1392    unsafe UProbe kind : ProbeKind,
1393    TracePoint,
1394    Xdp attach_type : XdpAttachType,
1395    SkMsg,
1396    SkSkb kind : SkSkbKind,
1397    SockOps,
1398    SchedClassifier,
1399    CgroupSkb attach_type : Option<CgroupSkbAttachType>,
1400    CgroupSysctl,
1401    CgroupSockopt attach_type : CgroupSockoptAttachType,
1402    LircMode2,
1403    PerfEvent,
1404    Lsm,
1405    RawTracePoint,
1406    unsafe BtfTracePoint,
1407    unsafe FEntry,
1408    unsafe FExit,
1409    Extension,
1410    SkLookup,
1411    SkReuseport attach_type : SkReuseportAttachType,
1412    CgroupDevice,
1413    Iter,
1414);
1415
1416macro_rules! impl_try_from_program {
1417    ($($ty:ident),+ $(,)?) => {
1418        $(
1419            impl<'a> TryFrom<&'a Program> for &'a $ty {
1420                type Error = ProgramError;
1421
1422                fn try_from(program: &'a Program) -> Result<&'a $ty, ProgramError> {
1423                    match program {
1424                        Program::$ty(p) => Ok(p),
1425                        _ => Err(ProgramError::UnexpectedProgramType),
1426                    }
1427                }
1428            }
1429
1430            impl<'a> TryFrom<&'a mut Program> for &'a mut $ty {
1431                type Error = ProgramError;
1432
1433                fn try_from(program: &'a mut Program) -> Result<&'a mut $ty, ProgramError> {
1434                    match program {
1435                        Program::$ty(p) => Ok(p),
1436                        _ => Err(ProgramError::UnexpectedProgramType),
1437                    }
1438                }
1439            }
1440        )+
1441    }
1442}
1443
1444impl_try_from_program!(
1445    KProbe,
1446    UProbe,
1447    TracePoint,
1448    SocketFilter,
1449    Xdp,
1450    SkMsg,
1451    SkSkb,
1452    SockOps,
1453    SchedClassifier,
1454    CgroupSkb,
1455    CgroupSysctl,
1456    CgroupSockopt,
1457    LircMode2,
1458    PerfEvent,
1459    Lsm,
1460    LsmCgroup,
1461    RawTracePoint,
1462    BtfTracePoint,
1463    FEntry,
1464    FExit,
1465    FlowDissector,
1466    Extension,
1467    CgroupSockAddr,
1468    SkLookup,
1469    SkReuseport,
1470    CgroupSock,
1471    CgroupDevice,
1472    Iter,
1473);
1474
1475impl_info!(
1476    KProbe,
1477    UProbe,
1478    TracePoint,
1479    SocketFilter,
1480    Xdp,
1481    SkMsg,
1482    SkSkb,
1483    SchedClassifier,
1484    CgroupSkb,
1485    CgroupSysctl,
1486    CgroupSockopt,
1487    LircMode2,
1488    PerfEvent,
1489    Lsm,
1490    LsmCgroup,
1491    RawTracePoint,
1492    BtfTracePoint,
1493    FEntry,
1494    FExit,
1495    FlowDissector,
1496    Extension,
1497    CgroupSockAddr,
1498    SkLookup,
1499    SkReuseport,
1500    SockOps,
1501    CgroupSock,
1502    CgroupDevice,
1503    Iter,
1504);
1505
1506/// Returns an iterator over all loaded links.
1507///
1508/// This function is useful for debugging and inspecting the state of
1509/// loaded links in the kernel. It can be used to check which links are
1510/// currently active and to gather information about them.
1511///
1512/// # Errors
1513///
1514/// The returned iterator may yield an error if link information cannot be
1515/// retrieved from the kernel.
1516///
1517/// # Example
1518///
1519/// ```no_run
1520/// use aya::programs::loaded_links;
1521///
1522/// for info in loaded_links() {
1523///    let info = info.unwrap();
1524///    println!("loaded link: {}", info.id());
1525/// }
1526/// ```
1527pub fn loaded_links() -> impl Iterator<Item = Result<LinkInfo, LinkError>> {
1528    iter_link_ids()
1529        .map(|id| {
1530            let id = id?;
1531            bpf_link_get_fd_by_id(id)
1532        })
1533        .map(|fd| {
1534            let fd = fd?;
1535            LinkInfo::new_from_fd(fd.as_fd())
1536        })
1537}