Skip to main content

profile_bee_aya/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_skb;
67pub mod sock_ops;
68pub mod socket_filter;
69pub mod tc;
70pub mod tp_btf;
71pub mod trace_point;
72pub mod uprobe;
73pub mod xdp;
74
75use std::{
76    borrow::Cow,
77    ffi::CString,
78    io,
79    os::fd::{AsFd, BorrowedFd},
80    path::{Path, PathBuf},
81    sync::Arc,
82};
83
84use aya_obj::{
85    VerifierLog,
86    btf::BtfError,
87    generated::{bpf_attach_type, bpf_prog_info, bpf_prog_type},
88    programs::XdpAttachType,
89};
90use info::impl_info;
91pub use info::{LsmAttachType, ProgramInfo, ProgramType, loaded_programs};
92use libc::ENOSPC;
93use tc::SchedClassifierLink;
94use thiserror::Error;
95
96// re-export the main items needed to load and attach
97pub use crate::programs::{
98    cgroup_device::CgroupDevice,
99    cgroup_skb::{CgroupSkb, CgroupSkbAttachType},
100    cgroup_sock::{CgroupSock, CgroupSockAttachType},
101    cgroup_sock_addr::{CgroupSockAddr, CgroupSockAddrAttachType},
102    cgroup_sockopt::{CgroupSockopt, CgroupSockoptAttachType},
103    cgroup_sysctl::CgroupSysctl,
104    extension::{Extension, ExtensionError},
105    fentry::FEntry,
106    fexit::FExit,
107    flow_dissector::FlowDissector,
108    iter::Iter,
109    kprobe::{KProbe, KProbeError},
110    links::{CgroupAttachMode, Link, LinkOrder},
111    lirc_mode2::LircMode2,
112    lsm::Lsm,
113    lsm_cgroup::LsmCgroup,
114    perf_event::PerfEvent,
115    probe::ProbeKind,
116    raw_trace_point::RawTracePoint,
117    sk_lookup::SkLookup,
118    sk_msg::SkMsg,
119    sk_skb::{SkSkb, SkSkbKind},
120    sock_ops::SockOps,
121    socket_filter::{SocketFilter, SocketFilterError},
122    tc::{SchedClassifier, TcAttachType, TcError},
123    tp_btf::BtfTracePoint,
124    trace_point::{TracePoint, TracePointError},
125    uprobe::{UProbe, UProbeError},
126    xdp::{Xdp, XdpError, XdpFlags},
127};
128use crate::{
129    VerifierLogLevel,
130    maps::MapError,
131    pin::PinError,
132    programs::{
133        links::{
134            FdLink, FdLinkId, LinkError, LinkInfo, Links, ProgAttachLink, ProgAttachLinkId,
135            define_link_wrapper, id_as_key, impl_try_into_fdlink,
136        },
137        perf_attach::{PerfLinkIdInner, PerfLinkInner, perf_attach, perf_attach_debugfs},
138    },
139    sys::{
140        EbpfLoadProgramAttrs, NetlinkError, ProgQueryTarget, SyscallError, bpf_btf_get_fd_by_id,
141        bpf_get_object, bpf_link_get_fd_by_id, bpf_load_program, bpf_pin_object,
142        bpf_prog_get_fd_by_id, bpf_prog_query, iter_link_ids, retry_with_verifier_logs,
143    },
144    util::KernelVersion,
145};
146
147/// Error type returned when working with programs.
148#[derive(Debug, Error)]
149pub enum ProgramError {
150    /// The program is already loaded.
151    #[error("the program is already loaded")]
152    AlreadyLoaded,
153
154    /// The program is not loaded.
155    #[error("the program is not loaded")]
156    NotLoaded,
157
158    /// The program is already attached.
159    #[error("the program was already attached")]
160    AlreadyAttached,
161
162    /// The program is not attached.
163    #[error("the program is not attached")]
164    NotAttached,
165
166    /// Loading the program failed.
167    #[error("the BPF_PROG_LOAD syscall returned {io_error}. Verifier output: {verifier_log}")]
168    LoadError {
169        /// The [`io::Error`] returned by the `BPF_PROG_LOAD` syscall.
170        #[source]
171        io_error: io::Error,
172        /// The error log produced by the kernel verifier.
173        verifier_log: VerifierLog,
174    },
175
176    /// A syscall failed.
177    #[error(transparent)]
178    SyscallError(#[from] SyscallError),
179
180    /// The network interface does not exist.
181    #[error("unknown network interface {name}")]
182    UnknownInterface {
183        /// interface name
184        name: String,
185    },
186
187    /// The program is not of the expected type.
188    #[error("unexpected program type")]
189    UnexpectedProgramType,
190
191    /// A map error occurred while loading or attaching a program.
192    #[error(transparent)]
193    MapError(#[from] MapError),
194
195    /// An error occurred while working with a [`KProbe`].
196    #[error(transparent)]
197    KProbeError(#[from] KProbeError),
198
199    /// An error occurred while working with an [`UProbe`].
200    #[error(transparent)]
201    UProbeError(#[from] UProbeError),
202
203    /// An error occurred while working with a [`TracePoint`].
204    #[error(transparent)]
205    TracePointError(#[from] TracePointError),
206
207    /// An error occurred while working with a [`SocketFilter`].
208    #[error(transparent)]
209    SocketFilterError(#[from] SocketFilterError),
210
211    /// An error occurred while working with an [`Xdp`] program.
212    #[error(transparent)]
213    XdpError(#[from] XdpError),
214
215    /// An error occurred while working with a TC program.
216    #[error(transparent)]
217    TcError(#[from] TcError),
218
219    /// An error occurred while working with an [`Extension`] program.
220    #[error(transparent)]
221    ExtensionError(#[from] ExtensionError),
222
223    /// An error occurred while working with BTF.
224    #[error(transparent)]
225    Btf(#[from] BtfError),
226
227    /// The program is not attached.
228    #[error("the program name `{name}` is invalid")]
229    InvalidName {
230        /// program name
231        name: String,
232    },
233
234    /// An error occurred while working with IO.
235    #[error(transparent)]
236    IOError(#[from] io::Error),
237
238    /// Providing an attach cookie is not supported.
239    #[error("providing an attach cookie is not supported")]
240    AttachCookieNotSupported,
241
242    /// An error occurred while working with Netlink.
243    #[error(transparent)]
244    NetlinkError(#[from] NetlinkError),
245}
246
247/// A [`Program`] file descriptor.
248#[derive(Debug)]
249pub struct ProgramFd(crate::MockableFd);
250
251impl ProgramFd {
252    /// Creates a new instance that shares the same underlying file description as [`self`].
253    pub fn try_clone(&self) -> io::Result<Self> {
254        let Self(inner) = self;
255        let inner = inner.try_clone()?;
256        Ok(Self(inner))
257    }
258}
259
260impl AsFd for ProgramFd {
261    fn as_fd(&self) -> BorrowedFd<'_> {
262        let Self(fd) = self;
263        fd.as_fd()
264    }
265}
266
267/// A [`Program`] identifier.
268pub struct ProgramId(u32);
269
270impl ProgramId {
271    /// Create a new program id.  
272    ///  
273    /// # Safety
274    ///
275    /// This method is unsafe since it doesn't check that the given `id` is a
276    /// valid program id.
277    pub const unsafe fn new(id: u32) -> Self {
278        Self(id)
279    }
280}
281
282/// eBPF program type.
283#[derive(Debug)]
284pub enum Program {
285    /// A [`KProbe`] program
286    KProbe(KProbe),
287    /// A [`UProbe`] program
288    UProbe(UProbe),
289    /// A [`TracePoint`] program
290    TracePoint(TracePoint),
291    /// A [`SocketFilter`] program
292    SocketFilter(SocketFilter),
293    /// A [`Xdp`] program
294    Xdp(Xdp),
295    /// A [`SkMsg`] program
296    SkMsg(SkMsg),
297    /// A [`SkSkb`] program
298    SkSkb(SkSkb),
299    /// A [`CgroupSockAddr`] program
300    CgroupSockAddr(CgroupSockAddr),
301    /// A [`SockOps`] program
302    SockOps(SockOps),
303    /// A [`SchedClassifier`] program
304    SchedClassifier(SchedClassifier),
305    /// A [`CgroupSkb`] program
306    CgroupSkb(CgroupSkb),
307    /// A [`CgroupSysctl`] program
308    CgroupSysctl(CgroupSysctl),
309    /// A [`CgroupSockopt`] program
310    CgroupSockopt(CgroupSockopt),
311    /// A [`LircMode2`] program
312    LircMode2(LircMode2),
313    /// A [`PerfEvent`] program
314    PerfEvent(PerfEvent),
315    /// A [`RawTracePoint`] program
316    RawTracePoint(RawTracePoint),
317    /// A [`Lsm`] program
318    Lsm(Lsm),
319    /// A [`LsmCgroup`] program
320    LsmCgroup(LsmCgroup),
321    /// A [`BtfTracePoint`] program
322    BtfTracePoint(BtfTracePoint),
323    /// A [`FEntry`] program
324    FEntry(FEntry),
325    /// A [`FExit`] program
326    FExit(FExit),
327    /// A [`FlowDissector`] program
328    FlowDissector(FlowDissector),
329    /// A [`Extension`] program
330    Extension(Extension),
331    /// A [`SkLookup`] program
332    SkLookup(SkLookup),
333    /// A [`CgroupSock`] program
334    CgroupSock(CgroupSock),
335    /// A [`CgroupDevice`] program
336    CgroupDevice(CgroupDevice),
337    /// An [`Iter`] program
338    Iter(Iter),
339}
340
341impl Program {
342    /// Returns the program type.
343    pub const fn prog_type(&self) -> ProgramType {
344        match self {
345            Self::KProbe(_) | Self::UProbe(_) => ProgramType::KProbe,
346            Self::TracePoint(_) => ProgramType::TracePoint,
347            Self::SocketFilter(_) => ProgramType::SocketFilter,
348            Self::Xdp(_) => ProgramType::Xdp,
349            Self::SkMsg(_) => ProgramType::SkMsg,
350            Self::SkSkb(_) => ProgramType::SkSkb,
351            Self::SockOps(_) => ProgramType::SockOps,
352            Self::SchedClassifier(_) => ProgramType::SchedClassifier,
353            Self::CgroupSkb(_) => ProgramType::CgroupSkb,
354            Self::CgroupSysctl(_) => ProgramType::CgroupSysctl,
355            Self::CgroupSockopt(_) => ProgramType::CgroupSockopt,
356            Self::LircMode2(_) => ProgramType::LircMode2,
357            Self::PerfEvent(_) => ProgramType::PerfEvent,
358            Self::RawTracePoint(_) => ProgramType::RawTracePoint,
359            Self::Lsm(_) => ProgramType::Lsm(LsmAttachType::Mac),
360            Self::LsmCgroup(_) => ProgramType::Lsm(LsmAttachType::Cgroup),
361            // The following program types are a subset of `TRACING` programs:
362            //
363            // - `BPF_TRACE_RAW_TP` (`BtfTracePoint`)
364            // - `BTF_TRACE_FENTRY` (`FEntry`)
365            // - `BPF_MODIFY_RETURN` (not supported yet in Aya)
366            // - `BPF_TRACE_FEXIT` (`FExit`)
367            // - `BPF_TRACE_ITER` (`Iter`)
368            //
369            // https://github.com/torvalds/linux/blob/v6.12/kernel/bpf/syscall.c#L3935-L3940
370            Self::BtfTracePoint(_) | Self::FEntry(_) | Self::FExit(_) | Self::Iter(_) => {
371                ProgramType::Tracing
372            }
373            Self::Extension(_) => ProgramType::Extension,
374            Self::CgroupSockAddr(_) => ProgramType::CgroupSockAddr,
375            Self::SkLookup(_) => ProgramType::SkLookup,
376            Self::CgroupSock(_) => ProgramType::CgroupSock,
377            Self::CgroupDevice(_) => ProgramType::CgroupDevice,
378            Self::FlowDissector(_) => ProgramType::FlowDissector,
379        }
380    }
381
382    /// Pin the program to the provided path
383    pub fn pin<P: AsRef<Path>>(&mut self, path: P) -> Result<(), PinError> {
384        match self {
385            Self::KProbe(p) => p.pin(path),
386            Self::UProbe(p) => p.pin(path),
387            Self::TracePoint(p) => p.pin(path),
388            Self::SocketFilter(p) => p.pin(path),
389            Self::Xdp(p) => p.pin(path),
390            Self::SkMsg(p) => p.pin(path),
391            Self::SkSkb(p) => p.pin(path),
392            Self::SockOps(p) => p.pin(path),
393            Self::SchedClassifier(p) => p.pin(path),
394            Self::CgroupSkb(p) => p.pin(path),
395            Self::CgroupSysctl(p) => p.pin(path),
396            Self::CgroupSockopt(p) => p.pin(path),
397            Self::LircMode2(p) => p.pin(path),
398            Self::PerfEvent(p) => p.pin(path),
399            Self::RawTracePoint(p) => p.pin(path),
400            Self::Lsm(p) => p.pin(path),
401            Self::LsmCgroup(p) => p.pin(path),
402            Self::BtfTracePoint(p) => p.pin(path),
403            Self::FEntry(p) => p.pin(path),
404            Self::FExit(p) => p.pin(path),
405            Self::FlowDissector(p) => p.pin(path),
406            Self::Extension(p) => p.pin(path),
407            Self::CgroupSockAddr(p) => p.pin(path),
408            Self::SkLookup(p) => p.pin(path),
409            Self::CgroupSock(p) => p.pin(path),
410            Self::CgroupDevice(p) => p.pin(path),
411            Self::Iter(p) => p.pin(path),
412        }
413    }
414
415    /// Unloads the program from the kernel.
416    pub fn unload(self) -> Result<(), ProgramError> {
417        match self {
418            Self::KProbe(mut p) => p.unload(),
419            Self::UProbe(mut p) => p.unload(),
420            Self::TracePoint(mut p) => p.unload(),
421            Self::SocketFilter(mut p) => p.unload(),
422            Self::Xdp(mut p) => p.unload(),
423            Self::SkMsg(mut p) => p.unload(),
424            Self::SkSkb(mut p) => p.unload(),
425            Self::SockOps(mut p) => p.unload(),
426            Self::SchedClassifier(mut p) => p.unload(),
427            Self::CgroupSkb(mut p) => p.unload(),
428            Self::CgroupSysctl(mut p) => p.unload(),
429            Self::CgroupSockopt(mut p) => p.unload(),
430            Self::LircMode2(mut p) => p.unload(),
431            Self::PerfEvent(mut p) => p.unload(),
432            Self::RawTracePoint(mut p) => p.unload(),
433            Self::Lsm(mut p) => p.unload(),
434            Self::LsmCgroup(mut p) => p.unload(),
435            Self::BtfTracePoint(mut p) => p.unload(),
436            Self::FEntry(mut p) => p.unload(),
437            Self::FExit(mut p) => p.unload(),
438            Self::FlowDissector(mut p) => p.unload(),
439            Self::Extension(mut p) => p.unload(),
440            Self::CgroupSockAddr(mut p) => p.unload(),
441            Self::SkLookup(mut p) => p.unload(),
442            Self::CgroupSock(mut p) => p.unload(),
443            Self::CgroupDevice(mut p) => p.unload(),
444            Self::Iter(mut p) => p.unload(),
445        }
446    }
447
448    /// Returns the file descriptor of a program.
449    ///
450    /// Can be used to add a program to a [`crate::maps::ProgramArray`] or attach an [`Extension`] program.
451    pub fn fd(&self) -> Result<&ProgramFd, ProgramError> {
452        match self {
453            Self::KProbe(p) => p.fd(),
454            Self::UProbe(p) => p.fd(),
455            Self::TracePoint(p) => p.fd(),
456            Self::SocketFilter(p) => p.fd(),
457            Self::Xdp(p) => p.fd(),
458            Self::SkMsg(p) => p.fd(),
459            Self::SkSkb(p) => p.fd(),
460            Self::SockOps(p) => p.fd(),
461            Self::SchedClassifier(p) => p.fd(),
462            Self::CgroupSkb(p) => p.fd(),
463            Self::CgroupSysctl(p) => p.fd(),
464            Self::CgroupSockopt(p) => p.fd(),
465            Self::LircMode2(p) => p.fd(),
466            Self::PerfEvent(p) => p.fd(),
467            Self::RawTracePoint(p) => p.fd(),
468            Self::Lsm(p) => p.fd(),
469            Self::LsmCgroup(p) => p.fd(),
470            Self::BtfTracePoint(p) => p.fd(),
471            Self::FEntry(p) => p.fd(),
472            Self::FExit(p) => p.fd(),
473            Self::FlowDissector(p) => p.fd(),
474            Self::Extension(p) => p.fd(),
475            Self::CgroupSockAddr(p) => p.fd(),
476            Self::SkLookup(p) => p.fd(),
477            Self::CgroupSock(p) => p.fd(),
478            Self::CgroupDevice(p) => p.fd(),
479            Self::Iter(p) => p.fd(),
480        }
481    }
482
483    /// Returns information about a loaded program with the [`ProgramInfo`] structure.
484    ///
485    /// This information is populated at load time by the kernel and can be used
486    /// to get kernel details for a given [`Program`].
487    pub fn info(&self) -> Result<ProgramInfo, ProgramError> {
488        match self {
489            Self::KProbe(p) => p.info(),
490            Self::UProbe(p) => p.info(),
491            Self::TracePoint(p) => p.info(),
492            Self::SocketFilter(p) => p.info(),
493            Self::Xdp(p) => p.info(),
494            Self::SkMsg(p) => p.info(),
495            Self::SkSkb(p) => p.info(),
496            Self::SockOps(p) => p.info(),
497            Self::SchedClassifier(p) => p.info(),
498            Self::CgroupSkb(p) => p.info(),
499            Self::CgroupSysctl(p) => p.info(),
500            Self::CgroupSockopt(p) => p.info(),
501            Self::LircMode2(p) => p.info(),
502            Self::PerfEvent(p) => p.info(),
503            Self::RawTracePoint(p) => p.info(),
504            Self::Lsm(p) => p.info(),
505            Self::LsmCgroup(p) => p.info(),
506            Self::BtfTracePoint(p) => p.info(),
507            Self::FEntry(p) => p.info(),
508            Self::FExit(p) => p.info(),
509            Self::FlowDissector(p) => p.info(),
510            Self::Extension(p) => p.info(),
511            Self::CgroupSockAddr(p) => p.info(),
512            Self::SkLookup(p) => p.info(),
513            Self::CgroupSock(p) => p.info(),
514            Self::CgroupDevice(p) => p.info(),
515            Self::Iter(p) => p.info(),
516        }
517    }
518}
519
520#[derive(Debug)]
521pub(crate) struct ProgramData<T: Link> {
522    pub(crate) name: Option<Cow<'static, str>>,
523    pub(crate) obj: Option<(aya_obj::Program, aya_obj::Function)>,
524    pub(crate) fd: Option<ProgramFd>,
525    pub(crate) links: Links<T>,
526    pub(crate) expected_attach_type: Option<bpf_attach_type>,
527    pub(crate) attach_btf_obj_fd: Option<crate::MockableFd>,
528    pub(crate) attach_btf_id: Option<u32>,
529    pub(crate) attach_prog_fd: Option<ProgramFd>,
530    pub(crate) btf_fd: Option<Arc<crate::MockableFd>>,
531    pub(crate) verifier_log_level: VerifierLogLevel,
532    pub(crate) path: Option<PathBuf>,
533    pub(crate) flags: u32,
534}
535
536impl<T: Link> ProgramData<T> {
537    pub(crate) fn new(
538        name: Option<Cow<'static, str>>,
539        obj: (aya_obj::Program, aya_obj::Function),
540        btf_fd: Option<Arc<crate::MockableFd>>,
541        verifier_log_level: VerifierLogLevel,
542    ) -> Self {
543        Self {
544            name,
545            obj: Some(obj),
546            fd: None,
547            links: Links::new(),
548            expected_attach_type: None,
549            attach_btf_obj_fd: None,
550            attach_btf_id: None,
551            attach_prog_fd: None,
552            btf_fd,
553            verifier_log_level,
554            path: None,
555            flags: 0,
556        }
557    }
558
559    pub(crate) fn from_bpf_prog_info(
560        name: Option<Cow<'static, str>>,
561        fd: crate::MockableFd,
562        path: &Path,
563        info: bpf_prog_info,
564        verifier_log_level: VerifierLogLevel,
565    ) -> Result<Self, ProgramError> {
566        let attach_btf_id = (info.attach_btf_id > 0).then_some(info.attach_btf_id);
567        let attach_btf_obj_fd = (info.attach_btf_obj_id != 0)
568            .then(|| bpf_btf_get_fd_by_id(info.attach_btf_obj_id))
569            .transpose()?;
570
571        Ok(Self {
572            name,
573            obj: None,
574            fd: Some(ProgramFd(fd)),
575            links: Links::new(),
576            expected_attach_type: None,
577            attach_btf_obj_fd,
578            attach_btf_id,
579            attach_prog_fd: None,
580            btf_fd: None,
581            verifier_log_level,
582            path: Some(path.to_path_buf()),
583            flags: 0,
584        })
585    }
586
587    pub(crate) fn from_pinned_path<P: AsRef<Path>>(
588        path: P,
589        verifier_log_level: VerifierLogLevel,
590    ) -> Result<Self, ProgramError> {
591        use std::os::unix::ffi::OsStrExt as _;
592
593        // TODO: avoid this unwrap by adding a new error variant.
594        let path_string = CString::new(path.as_ref().as_os_str().as_bytes()).unwrap();
595        let fd = bpf_get_object(&path_string).map_err(|io_error| SyscallError {
596            call: "bpf_obj_get",
597            io_error,
598        })?;
599
600        let info = ProgramInfo::new_from_fd(fd.as_fd())?;
601        let name = info.name_as_str().map(ToOwned::to_owned).map(Into::into);
602        Self::from_bpf_prog_info(name, fd, path.as_ref(), info.0, verifier_log_level)
603    }
604}
605
606impl<T: Link> ProgramData<T> {
607    fn fd(&self) -> Result<&ProgramFd, ProgramError> {
608        self.fd.as_ref().ok_or(ProgramError::NotLoaded)
609    }
610}
611
612fn unload_program<T: Link>(data: &mut ProgramData<T>) -> Result<(), ProgramError> {
613    data.links.remove_all()?;
614    data.fd
615        .take()
616        .ok_or(ProgramError::NotLoaded)
617        .map(|ProgramFd { .. }| ())
618}
619
620fn pin_program<T: Link, P: AsRef<Path>>(data: &ProgramData<T>, path: P) -> Result<(), PinError> {
621    use std::os::unix::ffi::OsStrExt as _;
622
623    let fd = data.fd.as_ref().ok_or_else(|| PinError::NoFd {
624        name: data
625            .name
626            .as_deref()
627            .unwrap_or("<unknown program>")
628            .to_string(),
629    })?;
630    let path = path.as_ref();
631    let path_string =
632        CString::new(path.as_os_str().as_bytes()).map_err(|error| PinError::InvalidPinPath {
633            path: path.into(),
634            error,
635        })?;
636    bpf_pin_object(fd.as_fd(), &path_string).map_err(|io_error| SyscallError {
637        call: "BPF_OBJ_PIN",
638        io_error,
639    })?;
640    Ok(())
641}
642
643fn load_program<T: Link>(
644    prog_type: bpf_prog_type,
645    data: &mut ProgramData<T>,
646) -> Result<(), ProgramError> {
647    let ProgramData {
648        name,
649        obj,
650        fd,
651        links: _,
652        expected_attach_type,
653        attach_btf_obj_fd,
654        attach_btf_id,
655        attach_prog_fd,
656        btf_fd,
657        verifier_log_level,
658        path: _,
659        flags,
660    } = data;
661    if fd.is_some() {
662        return Err(ProgramError::AlreadyLoaded);
663    }
664    if obj.is_none() {
665        // This program was loaded from a pin in bpffs
666        return Err(ProgramError::AlreadyLoaded);
667    }
668    let obj = obj.as_ref().unwrap();
669    let (
670        aya_obj::Program {
671            license,
672            kernel_version,
673            ..
674        },
675        aya_obj::Function {
676            instructions,
677            func_info,
678            line_info,
679            func_info_rec_size,
680            line_info_rec_size,
681            ..
682        },
683    ) = obj;
684
685    let target_kernel_version =
686        kernel_version.unwrap_or_else(|| KernelVersion::current().map_or(0, KernelVersion::code));
687
688    let prog_name = if let Some(name) = name.as_deref() {
689        let prog_name = CString::new(name).map_err(|err @ std::ffi::NulError { .. }| {
690            let name = err.into_vec();
691            let name = unsafe { String::from_utf8_unchecked(name) };
692            ProgramError::InvalidName { name }
693        })?;
694        Some(prog_name)
695    } else {
696        None
697    };
698
699    let attr = EbpfLoadProgramAttrs {
700        name: prog_name,
701        ty: prog_type,
702        insns: instructions,
703        license,
704        kernel_version: target_kernel_version,
705        expected_attach_type: *expected_attach_type,
706        prog_btf_fd: btf_fd.as_ref().map(|f| f.as_fd()),
707        attach_btf_obj_fd: attach_btf_obj_fd.as_ref().map(|fd| fd.as_fd()),
708        attach_btf_id: *attach_btf_id,
709        attach_prog_fd: attach_prog_fd.as_ref().map(|fd| fd.as_fd()),
710        func_info_rec_size: *func_info_rec_size,
711        func_info: func_info.clone(),
712        line_info_rec_size: *line_info_rec_size,
713        line_info: line_info.clone(),
714        flags: *flags,
715    };
716
717    let (ret, verifier_log) = retry_with_verifier_logs(10, |logger| {
718        bpf_load_program(&attr, logger, *verifier_log_level)
719    });
720
721    match ret {
722        Ok(prog_fd) => {
723            *fd = Some(ProgramFd(prog_fd));
724            Ok(())
725        }
726        Err(io_error) => Err(ProgramError::LoadError {
727            io_error,
728            verifier_log,
729        }),
730    }
731}
732
733pub(crate) fn query(
734    target: ProgQueryTarget<'_>,
735    attach_type: bpf_attach_type,
736    query_flags: u32,
737    attach_flags: &mut Option<u32>,
738) -> Result<(u64, Vec<u32>), ProgramError> {
739    let mut prog_ids = vec![0u32; 64];
740    let mut prog_cnt = prog_ids.len() as u32;
741    let mut revision = 0;
742
743    let mut retries = 0;
744
745    loop {
746        match bpf_prog_query(
747            &target,
748            attach_type,
749            query_flags,
750            attach_flags.as_mut(),
751            &mut prog_ids,
752            &mut prog_cnt,
753            &mut revision,
754        ) {
755            Ok(()) => {
756                prog_ids.resize(prog_cnt as usize, 0);
757                return Ok((revision, prog_ids));
758            }
759            Err(io_error) => {
760                if retries == 0 && io_error.raw_os_error() == Some(ENOSPC) {
761                    prog_ids.resize(prog_cnt as usize, 0);
762                    retries += 1;
763                } else {
764                    return Err(SyscallError {
765                        call: "bpf_prog_query",
766                        io_error,
767                    }
768                    .into());
769                }
770            }
771        }
772    }
773}
774
775macro_rules! impl_program_unload {
776    ($($struct_name:ident),+ $(,)?) => {
777        $(
778            impl $struct_name {
779                /// Unloads the program from the kernel.
780                ///
781                /// Links will be detached before unloading the program.  Note
782                /// that owned links obtained using `take_link()` will not be
783                /// detached.
784                pub fn unload(&mut self) -> Result<(), ProgramError> {
785                    unload_program(&mut self.data)
786                }
787            }
788
789            impl Drop for $struct_name {
790                fn drop(&mut self) {
791                    let _unused: Result<(), ProgramError> = self.unload();
792                }
793            }
794        )+
795    }
796}
797
798impl_program_unload!(
799    KProbe,
800    UProbe,
801    TracePoint,
802    SocketFilter,
803    Xdp,
804    SkMsg,
805    SkSkb,
806    SchedClassifier,
807    CgroupSkb,
808    CgroupSysctl,
809    CgroupSockopt,
810    LircMode2,
811    PerfEvent,
812    Lsm,
813    LsmCgroup,
814    RawTracePoint,
815    BtfTracePoint,
816    FEntry,
817    FExit,
818    FlowDissector,
819    Extension,
820    CgroupSockAddr,
821    SkLookup,
822    SockOps,
823    CgroupSock,
824    CgroupDevice,
825    Iter,
826);
827
828macro_rules! impl_fd {
829    ($($struct_name:ident),+ $(,)?) => {
830        $(
831            impl $struct_name {
832                /// Returns the file descriptor of this Program.
833                pub fn fd(&self) -> Result<&ProgramFd, ProgramError> {
834                    self.data.fd()
835                }
836            }
837        )+
838    }
839}
840
841impl_fd!(
842    KProbe,
843    UProbe,
844    TracePoint,
845    SocketFilter,
846    Xdp,
847    SkMsg,
848    SkSkb,
849    SchedClassifier,
850    CgroupSkb,
851    CgroupSysctl,
852    CgroupSockopt,
853    LircMode2,
854    PerfEvent,
855    Lsm,
856    LsmCgroup,
857    RawTracePoint,
858    BtfTracePoint,
859    FEntry,
860    FExit,
861    FlowDissector,
862    Extension,
863    CgroupSockAddr,
864    SkLookup,
865    SockOps,
866    CgroupSock,
867    CgroupDevice,
868    Iter,
869);
870
871/// Trait implemented by the [`Program`] types which support the kernel's
872/// [generic multi-prog API](https://github.com/torvalds/linux/commit/053c8e1f235dc3f69d13375b32f4209228e1cb96).
873///
874/// # Minimum kernel version
875///
876/// The minimum kernel version required to use this feature is 6.6.0.
877pub trait MultiProgram {
878    /// Borrows the file descriptor.
879    fn fd(&self) -> Result<BorrowedFd<'_>, ProgramError>;
880}
881
882macro_rules! impl_multiprog_fd {
883    ($($struct_name:ident),+ $(,)?) => {
884        $(
885            impl MultiProgram for $struct_name {
886                fn fd(&self) -> Result<BorrowedFd<'_>, ProgramError> {
887                    Ok(self.fd()?.as_fd())
888                }
889            }
890        )+
891    }
892}
893
894impl_multiprog_fd!(SchedClassifier);
895
896/// Trait implemented by the [`Link`] types which support the kernel's
897/// [generic multi-prog API](https://github.com/torvalds/linux/commit/053c8e1f235dc3f69d13375b32f4209228e1cb96).
898///
899/// # Minimum kernel version
900///
901/// The minimum kernel version required to use this feature is 6.6.0.
902pub trait MultiProgLink {
903    /// Borrows the file descriptor.
904    fn fd(&self) -> Result<BorrowedFd<'_>, LinkError>;
905}
906
907macro_rules! impl_multiproglink_fd {
908    ($($struct_name:ident),+ $(,)?) => {
909        $(
910            impl MultiProgLink for $struct_name {
911                fn fd(&self) -> Result<BorrowedFd<'_>, LinkError> {
912                    let link: &FdLink = self.try_into()?;
913                    Ok(link.fd.as_fd())
914                }
915            }
916        )+
917    }
918}
919
920impl_multiproglink_fd!(SchedClassifierLink);
921
922macro_rules! impl_program_pin{
923    ($($struct_name:ident),+ $(,)?) => {
924        $(
925            impl $struct_name {
926                /// Pins the program to a BPF filesystem.
927                ///
928                /// When a BPF object is pinned to a BPF filesystem it will remain loaded after
929                /// Aya has unloaded the program.
930                /// To remove the program, the file on the BPF filesystem must be removed.
931                /// Any directories in the the path provided should have been created by the caller.
932                pub fn pin<P: AsRef<Path>>(&mut self, path: P) -> Result<(), PinError> {
933                    self.data.path = Some(path.as_ref().to_path_buf());
934                    pin_program(&self.data, path)
935                }
936
937                /// Removes the pinned link from the filesystem.
938                pub fn unpin(&mut self) -> Result<(), io::Error> {
939                    if let Some(path) = self.data.path.take() {
940                        std::fs::remove_file(path)?;
941                    }
942                    Ok(())
943                }
944            }
945        )+
946    }
947}
948
949impl_program_pin!(
950    KProbe,
951    UProbe,
952    TracePoint,
953    SocketFilter,
954    Xdp,
955    SkMsg,
956    SkSkb,
957    SchedClassifier,
958    CgroupSkb,
959    CgroupSysctl,
960    CgroupSockopt,
961    LircMode2,
962    PerfEvent,
963    Lsm,
964    LsmCgroup,
965    RawTracePoint,
966    BtfTracePoint,
967    FEntry,
968    FExit,
969    FlowDissector,
970    Extension,
971    CgroupSockAddr,
972    SkLookup,
973    SockOps,
974    CgroupSock,
975    CgroupDevice,
976    Iter,
977);
978
979macro_rules! impl_from_pin {
980    ($($struct_name:ident),+ $(,)?) => {
981        $(
982            impl $struct_name {
983                /// Creates a program from a pinned entry on a bpffs.
984                ///
985                /// Existing links will not be populated. To work with existing links you should use [`crate::programs::links::PinnedLink`].
986                ///
987                /// On drop, any managed links are detached and the program is unloaded. This will not result in
988                /// the program being unloaded from the kernel if it is still pinned.
989                pub fn from_pin<P: AsRef<Path>>(path: P) -> Result<Self, ProgramError> {
990                    let data = ProgramData::from_pinned_path(path, VerifierLogLevel::default())?;
991                    Ok(Self { data })
992                }
993            }
994        )+
995    }
996}
997
998// Use impl_from_pin if the program doesn't require additional data
999impl_from_pin!(
1000    TracePoint,
1001    SocketFilter,
1002    SkMsg,
1003    CgroupSysctl,
1004    LircMode2,
1005    Lsm,
1006    PerfEvent,
1007    RawTracePoint,
1008    BtfTracePoint,
1009    FEntry,
1010    FExit,
1011    FlowDissector,
1012    Extension,
1013    SkLookup,
1014    SockOps,
1015    CgroupDevice,
1016    Iter,
1017);
1018
1019macro_rules! impl_from_prog_info {
1020    (
1021        $(#[$doc:meta])*
1022        @safety
1023            [$($safety:tt)?]
1024        @rest
1025            $struct_name:ident $($var:ident : $var_ty:ty)?
1026    ) => {
1027        impl $struct_name {
1028            /// Constructs an instance of a [`Self`] from a [`ProgramInfo`].
1029            ///
1030            /// This allows the caller to get a handle to an already loaded
1031            /// program from the kernel without having to load it again.
1032            ///
1033            /// # Errors
1034            ///
1035            /// - If the program type reported by the kernel does not match
1036            ///   [`Self::PROGRAM_TYPE`].
1037            /// - If the file descriptor of the program cannot be cloned.
1038            $(#[$doc])*
1039            pub $($safety)?
1040            fn from_program_info(
1041                info: ProgramInfo,
1042                name: Cow<'static, str>,
1043                $($var: $var_ty,)?
1044            ) -> Result<Self, ProgramError> {
1045                if info.program_type() != Self::PROGRAM_TYPE.into() {
1046                    return Err(ProgramError::UnexpectedProgramType {});
1047                }
1048                let ProgramInfo(bpf_progam_info) = info;
1049                let fd = info.fd()?;
1050                let fd = fd.as_fd().try_clone_to_owned()?;
1051
1052                Ok(Self {
1053                    data: ProgramData::from_bpf_prog_info(
1054                        Some(name),
1055                        crate::MockableFd::from_fd(fd),
1056                        Path::new(""),
1057                        bpf_progam_info,
1058                        VerifierLogLevel::default(),
1059                    )?,
1060                    $($var,)?
1061                })
1062            }
1063        }
1064    };
1065
1066    // Handle unsafe cases and pass a safety doc section
1067    (
1068        unsafe $struct_name:ident $($var:ident : $var_ty:ty)? $(, $($rest:tt)*)?
1069    ) => {
1070        impl_from_prog_info! {
1071            ///
1072            /// # Safety
1073            ///
1074            /// The runtime type of this program, as used by the kernel, is
1075            /// overloaded. We assert the program type matches the runtime type
1076            /// but we're unable to perform further checks. Therefore, the caller
1077            /// must ensure that the program type is correct or the behavior is
1078            /// undefined.
1079            @safety [unsafe]
1080            @rest $struct_name $($var : $var_ty)?
1081        }
1082        $( impl_from_prog_info!($($rest)*); )?
1083    };
1084
1085    // Handle non-unsafe cases and omit safety doc section
1086    (
1087        $struct_name:ident $($var:ident : $var_ty:ty)? $(, $($rest:tt)*)?
1088    ) => {
1089        impl_from_prog_info! {
1090            @safety []
1091            @rest $struct_name $($var : $var_ty)?
1092        }
1093        $( impl_from_prog_info!($($rest)*); )?
1094    };
1095
1096    // Handle trailing comma
1097    (
1098        $(,)?
1099    ) => {};
1100}
1101
1102impl_from_prog_info!(
1103    unsafe KProbe kind : ProbeKind,
1104    unsafe UProbe kind : ProbeKind,
1105    TracePoint,
1106    Xdp attach_type : XdpAttachType,
1107    SkMsg,
1108    SkSkb kind : SkSkbKind,
1109    SockOps,
1110    SchedClassifier,
1111    CgroupSkb attach_type : Option<CgroupSkbAttachType>,
1112    CgroupSysctl,
1113    CgroupSockopt attach_type : CgroupSockoptAttachType,
1114    LircMode2,
1115    PerfEvent,
1116    Lsm,
1117    RawTracePoint,
1118    unsafe BtfTracePoint,
1119    unsafe FEntry,
1120    unsafe FExit,
1121    Extension,
1122    SkLookup,
1123    CgroupDevice,
1124    Iter,
1125);
1126
1127macro_rules! impl_try_from_program {
1128    ($($ty:ident),+ $(,)?) => {
1129        $(
1130            impl<'a> TryFrom<&'a Program> for &'a $ty {
1131                type Error = ProgramError;
1132
1133                fn try_from(program: &'a Program) -> Result<&'a $ty, ProgramError> {
1134                    match program {
1135                        Program::$ty(p) => Ok(p),
1136                        _ => Err(ProgramError::UnexpectedProgramType),
1137                    }
1138                }
1139            }
1140
1141            impl<'a> TryFrom<&'a mut Program> for &'a mut $ty {
1142                type Error = ProgramError;
1143
1144                fn try_from(program: &'a mut Program) -> Result<&'a mut $ty, ProgramError> {
1145                    match program {
1146                        Program::$ty(p) => Ok(p),
1147                        _ => Err(ProgramError::UnexpectedProgramType),
1148                    }
1149                }
1150            }
1151        )+
1152    }
1153}
1154
1155impl_try_from_program!(
1156    KProbe,
1157    UProbe,
1158    TracePoint,
1159    SocketFilter,
1160    Xdp,
1161    SkMsg,
1162    SkSkb,
1163    SockOps,
1164    SchedClassifier,
1165    CgroupSkb,
1166    CgroupSysctl,
1167    CgroupSockopt,
1168    LircMode2,
1169    PerfEvent,
1170    Lsm,
1171    LsmCgroup,
1172    RawTracePoint,
1173    BtfTracePoint,
1174    FEntry,
1175    FExit,
1176    FlowDissector,
1177    Extension,
1178    CgroupSockAddr,
1179    SkLookup,
1180    CgroupSock,
1181    CgroupDevice,
1182    Iter,
1183);
1184
1185impl_info!(
1186    KProbe,
1187    UProbe,
1188    TracePoint,
1189    SocketFilter,
1190    Xdp,
1191    SkMsg,
1192    SkSkb,
1193    SchedClassifier,
1194    CgroupSkb,
1195    CgroupSysctl,
1196    CgroupSockopt,
1197    LircMode2,
1198    PerfEvent,
1199    Lsm,
1200    LsmCgroup,
1201    RawTracePoint,
1202    BtfTracePoint,
1203    FEntry,
1204    FExit,
1205    FlowDissector,
1206    Extension,
1207    CgroupSockAddr,
1208    SkLookup,
1209    SockOps,
1210    CgroupSock,
1211    CgroupDevice,
1212    Iter,
1213);
1214
1215/// Returns an iterator over all loaded links.
1216///
1217/// This function is useful for debugging and inspecting the state of
1218/// loaded links in the kernel. It can be used to check which links are
1219/// currently active and to gather information about them.
1220///
1221/// # Errors
1222///
1223/// The returned iterator may yield an error if link information cannot be
1224/// retrieved from the kernel.
1225///
1226/// # Example
1227///
1228/// ```no_run
1229/// use aya::programs::loaded_links;
1230///
1231/// for info in loaded_links() {
1232///    let info = info.unwrap();
1233///    println!("loaded link: {}", info.id());
1234/// }
1235/// ```
1236pub fn loaded_links() -> impl Iterator<Item = Result<LinkInfo, LinkError>> {
1237    iter_link_ids()
1238        .map(|id| {
1239            let id = id?;
1240            bpf_link_get_fd_by_id(id)
1241        })
1242        .map(|fd| {
1243            let fd = fd?;
1244            LinkInfo::new_from_fd(fd.as_fd())
1245        })
1246}