Skip to main content

libbpf_rs/
program.rs

1// `rustdoc` is buggy, claiming that we have some links to private items
2// when they are actually public.
3#![allow(rustdoc::private_intra_doc_links)]
4
5use std::ffi::c_void;
6use std::ffi::CStr;
7use std::ffi::CString;
8use std::ffi::OsStr;
9use std::io::Read;
10use std::marker::PhantomData;
11use std::mem;
12use std::mem::size_of;
13use std::mem::size_of_val;
14use std::mem::transmute;
15use std::ops::Deref;
16use std::os::unix::ffi::OsStrExt as _;
17use std::os::unix::io::AsFd;
18use std::os::unix::io::AsRawFd;
19use std::os::unix::io::BorrowedFd;
20use std::os::unix::io::FromRawFd;
21use std::os::unix::io::OwnedFd;
22use std::path::Path;
23use std::ptr;
24use std::ptr::NonNull;
25use std::slice;
26use std::time::Duration;
27
28use libbpf_sys::bpf_func_id;
29
30use crate::netfilter;
31use crate::streams::Stream;
32use crate::util;
33use crate::util::validate_bpf_ret;
34use crate::util::BpfObjectType;
35use crate::AsRawLibbpf;
36use crate::Error;
37use crate::ErrorExt as _;
38use crate::Link;
39use crate::Mut;
40use crate::RawTracepointOpts;
41use crate::Result;
42use crate::TracepointCategory;
43use crate::TracepointOpts;
44
45/// Options to optionally be provided when attaching to a uprobe.
46#[derive(Clone, Debug, Default)]
47pub struct UprobeOpts {
48    /// Offset of kernel reference counted USDT semaphore.
49    pub ref_ctr_offset: usize,
50    /// Custom user-provided value accessible through `bpf_get_attach_cookie`.
51    pub cookie: u64,
52    /// uprobe is return probe, invoked at function return time.
53    pub retprobe: bool,
54    /// Function name to attach to.
55    ///
56    /// Could be an unqualified ("abc") or library-qualified "abc@LIBXYZ" name.
57    /// To specify function entry, `func_name` should be set while `func_offset`
58    /// argument to should be 0. To trace an offset within a function, specify
59    /// `func_name` and use `func_offset` argument to specify offset within the
60    /// function. Shared library functions must specify the shared library path.
61    ///
62    /// If `func_name` is `None`, `func_offset` will be treated as the
63    /// absolute offset of the symbol to attach to, rather than a
64    /// relative one.
65    pub func_name: Option<String>,
66    #[doc(hidden)]
67    pub _non_exhaustive: (),
68}
69
70/// Options to optionally be provided when attaching to a uprobe.
71#[derive(Clone, Debug, Default)]
72pub struct UprobeMultiOpts {
73    /// Optional, array of function symbols to attach to
74    pub syms: Vec<String>,
75    /// Optional, array of function addresses to attach to
76    pub offsets: Vec<usize>,
77    /// Optional, array of associated ref counter offsets
78    pub ref_ctr_offsets: Vec<usize>,
79    /// Optional, array of associated BPF cookies
80    pub cookies: Vec<u64>,
81    /// Create return uprobes
82    pub retprobe: bool,
83    /// Create session uprobes
84    pub session: bool,
85    #[doc(hidden)]
86    pub _non_exhaustive: (),
87}
88
89/// Options to optionally be provided when attaching to a USDT.
90#[derive(Clone, Debug, Default)]
91pub struct UsdtOpts {
92    /// Custom user-provided value accessible through `bpf_usdt_cookie`.
93    pub cookie: u64,
94    #[doc(hidden)]
95    pub _non_exhaustive: (),
96}
97
98impl From<UsdtOpts> for libbpf_sys::bpf_usdt_opts {
99    fn from(opts: UsdtOpts) -> Self {
100        let UsdtOpts {
101            cookie,
102            _non_exhaustive,
103        } = opts;
104        #[allow(clippy::needless_update)]
105        Self {
106            sz: size_of::<Self>() as _,
107            usdt_cookie: cookie,
108            // bpf_usdt_opts might have padding fields on some platform
109            ..Default::default()
110        }
111    }
112}
113
114/// Options to optionally be provided when attaching to a kprobe.
115#[derive(Clone, Debug, Default)]
116pub struct KprobeOpts {
117    /// Custom user-provided value accessible through `bpf_get_attach_cookie`.
118    pub cookie: u64,
119    #[doc(hidden)]
120    pub _non_exhaustive: (),
121}
122
123impl From<KprobeOpts> for libbpf_sys::bpf_kprobe_opts {
124    fn from(opts: KprobeOpts) -> Self {
125        let KprobeOpts {
126            cookie,
127            _non_exhaustive,
128        } = opts;
129
130        #[allow(clippy::needless_update)]
131        Self {
132            sz: size_of::<Self>() as _,
133            bpf_cookie: cookie,
134            // bpf_kprobe_opts might have padding fields on some platform
135            ..Default::default()
136        }
137    }
138}
139
140/// Options to optionally be provided when attaching to multiple kprobes.
141#[derive(Clone, Debug, Default)]
142pub struct KprobeMultiOpts {
143    /// List of symbol names to attach to.
144    pub symbols: Vec<String>,
145    /// Array of custom user-provided values accessible through `bpf_get_attach_cookie`.
146    pub cookies: Vec<u64>,
147    /// kprobes are return probes, invoked at function return time.
148    pub retprobe: bool,
149    #[doc(hidden)]
150    pub _non_exhaustive: (),
151}
152
153/// Options to optionally be provided when attaching to a perf event.
154#[derive(Clone, Debug, Default)]
155pub struct PerfEventOpts {
156    /// Custom user-provided value accessible through `bpf_get_attach_cookie`.
157    pub cookie: u64,
158    /// Force use of the old style ioctl attachment instead of the newer BPF link method.
159    pub force_ioctl_attach: bool,
160    #[doc(hidden)]
161    pub _non_exhaustive: (),
162}
163
164impl From<PerfEventOpts> for libbpf_sys::bpf_perf_event_opts {
165    fn from(opts: PerfEventOpts) -> Self {
166        let PerfEventOpts {
167            cookie,
168            force_ioctl_attach,
169            _non_exhaustive,
170        } = opts;
171
172        #[allow(clippy::needless_update)]
173        Self {
174            sz: size_of::<Self>() as _,
175            bpf_cookie: cookie,
176            force_ioctl_attach,
177            // bpf_perf_event_opts might have padding fields on some platform
178            ..Default::default()
179        }
180    }
181}
182
183
184/// Options used when iterating over a map.
185#[derive(Clone, Debug)]
186pub struct MapIterOpts<'fd> {
187    /// The file descriptor of the map.
188    pub fd: BorrowedFd<'fd>,
189    #[doc(hidden)]
190    pub _non_exhaustive: (),
191}
192
193impl<'fd> MapIterOpts<'fd> {
194    /// Create a [`MapIterOpts`] object using the given file descriptor.
195    pub fn from_fd(fd: BorrowedFd<'fd>) -> Self {
196        Self {
197            fd,
198            _non_exhaustive: (),
199        }
200    }
201}
202
203
204/// Iteration order for cgroups.
205#[non_exhaustive]
206#[repr(u32)]
207#[derive(Clone, Debug, Default)]
208pub enum CgroupIterOrder {
209    /// Use the default iteration order.
210    #[default]
211    Default = libbpf_sys::BPF_CGROUP_ITER_ORDER_UNSPEC,
212    /// Process only a single object.
213    SelfOnly = libbpf_sys::BPF_CGROUP_ITER_SELF_ONLY,
214    /// Walk descendants in pre-order.
215    DescendantsPre = libbpf_sys::BPF_CGROUP_ITER_DESCENDANTS_PRE,
216    /// Walk descendants in post-order.
217    DescendantsPost = libbpf_sys::BPF_CGROUP_ITER_DESCENDANTS_POST,
218    /// Walk ancestors upward.
219    AncestorsUp = libbpf_sys::BPF_CGROUP_ITER_ANCESTORS_UP,
220}
221
222/// Options used when iterating over a cgroup.
223#[derive(Clone, Debug)]
224pub struct CgroupIterOpts<'fd> {
225    /// The file descriptor of the cgroup.
226    pub fd: BorrowedFd<'fd>,
227    /// The iteration order to use on the cgroup.
228    pub order: CgroupIterOrder,
229    #[doc(hidden)]
230    pub _non_exhaustive: (),
231}
232
233impl<'fd> CgroupIterOpts<'fd> {
234    /// Create a [`CgroupIterOpts`] object using the given file descriptor.
235    pub fn from_fd(fd: BorrowedFd<'fd>) -> Self {
236        Self {
237            fd,
238            order: CgroupIterOrder::default(),
239            _non_exhaustive: (),
240        }
241    }
242}
243
244
245/// Options to optionally be provided when attaching to an iterator.
246#[non_exhaustive]
247#[derive(Clone, Debug)]
248pub enum IterOpts<'fd> {
249    /// Iterate over a map.
250    Map(MapIterOpts<'fd>),
251    /// Iterate over a group.
252    Cgroup(CgroupIterOpts<'fd>),
253}
254
255impl From<IterOpts<'_>> for libbpf_sys::bpf_iter_link_info {
256    fn from(opts: IterOpts) -> Self {
257        let mut linkinfo = Self::default();
258        match opts {
259            IterOpts::Map(map_opts) => {
260                let MapIterOpts {
261                    fd,
262                    _non_exhaustive: (),
263                } = map_opts;
264
265                linkinfo.map.map_fd = fd.as_raw_fd() as _;
266            }
267            IterOpts::Cgroup(cgroup_opts) => {
268                let CgroupIterOpts {
269                    fd,
270                    order,
271                    _non_exhaustive: (),
272                } = cgroup_opts;
273
274                linkinfo.cgroup.cgroup_fd = fd.as_raw_fd() as _;
275                linkinfo.cgroup.order = order as libbpf_sys::bpf_cgroup_iter_order;
276            }
277        };
278        linkinfo
279    }
280}
281
282
283/// An immutable parsed but not yet loaded BPF program.
284pub type OpenProgram<'obj> = OpenProgramImpl<'obj>;
285/// A mutable parsed but not yet loaded BPF program.
286pub type OpenProgramMut<'obj> = OpenProgramImpl<'obj, Mut>;
287
288
289/// Represents a parsed but not yet loaded BPF program.
290///
291/// This object exposes operations that need to happen before the program is loaded.
292#[derive(Debug)]
293#[repr(transparent)]
294pub struct OpenProgramImpl<'obj, T = ()> {
295    ptr: NonNull<libbpf_sys::bpf_program>,
296    _phantom: PhantomData<&'obj T>,
297}
298
299impl<'obj> OpenProgram<'obj> {
300    /// Create a new [`OpenProgram`] from a ptr to a `libbpf_sys::bpf_program`.
301    pub fn new(prog: &'obj libbpf_sys::bpf_program) -> Self {
302        // SAFETY: We inferred the address from a reference, which is always
303        //         valid.
304        Self {
305            ptr: unsafe { NonNull::new_unchecked(prog as *const _ as *mut _) },
306            _phantom: PhantomData,
307        }
308    }
309
310    /// The `ProgramType` of this `OpenProgram`.
311    pub fn prog_type(&self) -> ProgramType {
312        ProgramType::from(unsafe { libbpf_sys::bpf_program__type(self.ptr.as_ptr()) })
313    }
314
315    /// Retrieve the name of this `OpenProgram`.
316    pub fn name(&self) -> &'obj OsStr {
317        let name_ptr = unsafe { libbpf_sys::bpf_program__name(self.ptr.as_ptr()) };
318        let name_c_str = unsafe { CStr::from_ptr(name_ptr) };
319        // SAFETY: `bpf_program__name` always returns a non-NULL pointer.
320        OsStr::from_bytes(name_c_str.to_bytes())
321    }
322
323    /// Retrieve the name of the section this `OpenProgram` belongs to.
324    pub fn section(&self) -> &'obj OsStr {
325        // SAFETY: The program is always valid.
326        let p = unsafe { libbpf_sys::bpf_program__section_name(self.ptr.as_ptr()) };
327        // SAFETY: `bpf_program__section_name` will always return a non-NULL
328        //         pointer.
329        let section_c_str = unsafe { CStr::from_ptr(p) };
330        let section = OsStr::from_bytes(section_c_str.to_bytes());
331        section
332    }
333
334    /// Returns the number of instructions that form the program.
335    ///
336    /// Note: Keep in mind, libbpf can modify the program's instructions
337    /// and consequently its instruction count, as it processes the BPF object file.
338    /// So [`OpenProgram::insn_cnt`] and [`Program::insn_cnt`] may return different values.
339    pub fn insn_cnt(&self) -> usize {
340        unsafe { libbpf_sys::bpf_program__insn_cnt(self.ptr.as_ptr()) as usize }
341    }
342
343    /// Gives read-only access to BPF program's underlying BPF instructions.
344    ///
345    /// Keep in mind, libbpf can modify and append/delete BPF program's
346    /// instructions as it processes BPF object file and prepares everything for
347    /// uploading into the kernel. So [`OpenProgram::insns`] and [`Program::insns`] may return
348    /// different sets of instructions. As an example, during BPF object load phase BPF program
349    /// instructions will be CO-RE-relocated, BPF subprograms instructions will be appended, ldimm64
350    /// instructions will have FDs embedded, etc. So instructions returned before load and after it
351    /// might be quite different.
352    pub fn insns(&self) -> &'obj [libbpf_sys::bpf_insn] {
353        let count = self.insn_cnt();
354        let ptr = unsafe { libbpf_sys::bpf_program__insns(self.ptr.as_ptr()) };
355        unsafe { slice::from_raw_parts(ptr, count) }
356    }
357
358    /// Return `true` if the bpf program is set to autoload, `false` otherwise.
359    pub fn autoload(&self) -> bool {
360        unsafe { libbpf_sys::bpf_program__autoload(self.ptr.as_ptr()) }
361    }
362}
363
364impl<'obj> OpenProgramMut<'obj> {
365    /// Create a new [`OpenProgram`] from a ptr to a `libbpf_sys::bpf_program`.
366    pub fn new_mut(prog: &'obj mut libbpf_sys::bpf_program) -> Self {
367        Self {
368            ptr: unsafe { NonNull::new_unchecked(prog as *mut _) },
369            _phantom: PhantomData,
370        }
371    }
372
373    /// Set the program type.
374    pub fn set_prog_type(&mut self, prog_type: ProgramType) {
375        let rc = unsafe { libbpf_sys::bpf_program__set_type(self.ptr.as_ptr(), prog_type as u32) };
376        debug_assert!(util::parse_ret(rc).is_ok(), "{rc}");
377    }
378
379    /// Set the attachment type of the program.
380    pub fn set_attach_type(&mut self, attach_type: ProgramAttachType) {
381        let rc = unsafe {
382            libbpf_sys::bpf_program__set_expected_attach_type(self.ptr.as_ptr(), attach_type as u32)
383        };
384        debug_assert!(util::parse_ret(rc).is_ok(), "{rc}");
385    }
386
387    /// Bind the program to a particular network device.
388    ///
389    /// Currently only used for hardware offload and certain XDP features such like HW metadata.
390    pub fn set_ifindex(&mut self, idx: u32) {
391        unsafe { libbpf_sys::bpf_program__set_ifindex(self.ptr.as_ptr(), idx) }
392    }
393
394    /// Set the log level for the bpf program.
395    ///
396    /// The log level is interpreted by bpf kernel code and interpretation may
397    /// change with newer kernel versions. Refer to the kernel source code for
398    /// details.
399    ///
400    /// In general, a value of `0` disables logging while values `> 0` enables
401    /// it.
402    pub fn set_log_level(&mut self, log_level: u32) {
403        let rc = unsafe { libbpf_sys::bpf_program__set_log_level(self.ptr.as_ptr(), log_level) };
404        debug_assert!(util::parse_ret(rc).is_ok(), "{rc}");
405    }
406
407    /// Set whether a bpf program should be automatically loaded by default
408    /// when the bpf object is loaded.
409    pub fn set_autoload(&mut self, autoload: bool) {
410        let rc = unsafe { libbpf_sys::bpf_program__set_autoload(self.ptr.as_ptr(), autoload) };
411        debug_assert!(util::parse_ret(rc).is_ok(), "{rc}");
412    }
413
414    /// Set whether a bpf program should be automatically attached by default
415    /// when the bpf object is loaded.
416    pub fn set_autoattach(&mut self, autoattach: bool) {
417        unsafe { libbpf_sys::bpf_program__set_autoattach(self.ptr.as_ptr(), autoattach) };
418    }
419
420    #[expect(missing_docs)]
421    pub fn set_attach_target(
422        &mut self,
423        attach_prog_fd: i32,
424        attach_func_name: Option<String>,
425    ) -> Result<()> {
426        let name_c = if let Some(name) = attach_func_name {
427            Some(util::str_to_cstring(&name)?)
428        } else {
429            None
430        };
431        let name_ptr = name_c.as_ref().map_or(ptr::null(), |name| name.as_ptr());
432        let ret = unsafe {
433            libbpf_sys::bpf_program__set_attach_target(self.ptr.as_ptr(), attach_prog_fd, name_ptr)
434        };
435        util::parse_ret(ret)
436    }
437
438    /// Set flags on the program.
439    pub fn set_flags(&mut self, flags: u32) {
440        let rc = unsafe { libbpf_sys::bpf_program__set_flags(self.ptr.as_ptr(), flags) };
441        debug_assert!(util::parse_ret(rc).is_ok(), "{rc}");
442    }
443}
444
445impl<'obj> Deref for OpenProgramMut<'obj> {
446    type Target = OpenProgram<'obj>;
447
448    fn deref(&self) -> &Self::Target {
449        // SAFETY: `OpenProgramImpl` is `repr(transparent)` and so
450        //         in-memory representation of both types is the same.
451        unsafe { transmute::<&OpenProgramMut<'obj>, &OpenProgram<'obj>>(self) }
452    }
453}
454
455impl<T> AsRawLibbpf for OpenProgramImpl<'_, T> {
456    type LibbpfType = libbpf_sys::bpf_program;
457
458    /// Retrieve the underlying [`libbpf_sys::bpf_program`].
459    fn as_libbpf_object(&self) -> NonNull<Self::LibbpfType> {
460        self.ptr
461    }
462}
463
464/// Type of a [`Program`]. Maps to `enum bpf_prog_type` in kernel uapi.
465#[non_exhaustive]
466#[repr(u32)]
467#[derive(Copy, Clone, PartialEq, Eq, Debug)]
468// TODO: Document variants.
469#[expect(missing_docs)]
470pub enum ProgramType {
471    Unspec = 0,
472    SocketFilter = libbpf_sys::BPF_PROG_TYPE_SOCKET_FILTER,
473    Kprobe = libbpf_sys::BPF_PROG_TYPE_KPROBE,
474    SchedCls = libbpf_sys::BPF_PROG_TYPE_SCHED_CLS,
475    SchedAct = libbpf_sys::BPF_PROG_TYPE_SCHED_ACT,
476    Tracepoint = libbpf_sys::BPF_PROG_TYPE_TRACEPOINT,
477    Xdp = libbpf_sys::BPF_PROG_TYPE_XDP,
478    PerfEvent = libbpf_sys::BPF_PROG_TYPE_PERF_EVENT,
479    CgroupSkb = libbpf_sys::BPF_PROG_TYPE_CGROUP_SKB,
480    CgroupSock = libbpf_sys::BPF_PROG_TYPE_CGROUP_SOCK,
481    LwtIn = libbpf_sys::BPF_PROG_TYPE_LWT_IN,
482    LwtOut = libbpf_sys::BPF_PROG_TYPE_LWT_OUT,
483    LwtXmit = libbpf_sys::BPF_PROG_TYPE_LWT_XMIT,
484    SockOps = libbpf_sys::BPF_PROG_TYPE_SOCK_OPS,
485    SkSkb = libbpf_sys::BPF_PROG_TYPE_SK_SKB,
486    CgroupDevice = libbpf_sys::BPF_PROG_TYPE_CGROUP_DEVICE,
487    SkMsg = libbpf_sys::BPF_PROG_TYPE_SK_MSG,
488    RawTracepoint = libbpf_sys::BPF_PROG_TYPE_RAW_TRACEPOINT,
489    CgroupSockAddr = libbpf_sys::BPF_PROG_TYPE_CGROUP_SOCK_ADDR,
490    LwtSeg6local = libbpf_sys::BPF_PROG_TYPE_LWT_SEG6LOCAL,
491    LircMode2 = libbpf_sys::BPF_PROG_TYPE_LIRC_MODE2,
492    SkReuseport = libbpf_sys::BPF_PROG_TYPE_SK_REUSEPORT,
493    FlowDissector = libbpf_sys::BPF_PROG_TYPE_FLOW_DISSECTOR,
494    CgroupSysctl = libbpf_sys::BPF_PROG_TYPE_CGROUP_SYSCTL,
495    RawTracepointWritable = libbpf_sys::BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE,
496    CgroupSockopt = libbpf_sys::BPF_PROG_TYPE_CGROUP_SOCKOPT,
497    Tracing = libbpf_sys::BPF_PROG_TYPE_TRACING,
498    StructOps = libbpf_sys::BPF_PROG_TYPE_STRUCT_OPS,
499    Ext = libbpf_sys::BPF_PROG_TYPE_EXT,
500    Lsm = libbpf_sys::BPF_PROG_TYPE_LSM,
501    SkLookup = libbpf_sys::BPF_PROG_TYPE_SK_LOOKUP,
502    Syscall = libbpf_sys::BPF_PROG_TYPE_SYSCALL,
503    Netfilter = libbpf_sys::BPF_PROG_TYPE_NETFILTER,
504    /// See [`MapType::Unknown`][crate::MapType::Unknown]
505    Unknown = u32::MAX,
506}
507
508impl ProgramType {
509    /// Detects if host kernel supports this BPF program type
510    ///
511    /// Make sure the process has required set of CAP_* permissions (or runs as
512    /// root) when performing feature checking.
513    pub fn is_supported(&self) -> Result<bool> {
514        let ret = unsafe { libbpf_sys::libbpf_probe_bpf_prog_type(*self as u32, ptr::null()) };
515        match ret {
516            0 => Ok(false),
517            1 => Ok(true),
518            _ => Err(Error::from_raw_os_error(-ret)),
519        }
520    }
521
522    /// Detects if host kernel supports the use of a given BPF helper from this BPF program type.
523    /// * `helper_id` - BPF helper ID (enum `bpf_func_id`) to check support for
524    ///
525    /// Make sure the process has required set of CAP_* permissions (or runs as
526    /// root) when performing feature checking.
527    pub fn is_helper_supported(&self, helper_id: bpf_func_id) -> Result<bool> {
528        let ret =
529            unsafe { libbpf_sys::libbpf_probe_bpf_helper(*self as u32, helper_id, ptr::null()) };
530        match ret {
531            0 => Ok(false),
532            1 => Ok(true),
533            _ => Err(Error::from_raw_os_error(-ret)),
534        }
535    }
536}
537
538impl From<u32> for ProgramType {
539    fn from(value: u32) -> Self {
540        use ProgramType::*;
541
542        match value {
543            x if x == Unspec as u32 => Unspec,
544            x if x == SocketFilter as u32 => SocketFilter,
545            x if x == Kprobe as u32 => Kprobe,
546            x if x == SchedCls as u32 => SchedCls,
547            x if x == SchedAct as u32 => SchedAct,
548            x if x == Tracepoint as u32 => Tracepoint,
549            x if x == Xdp as u32 => Xdp,
550            x if x == PerfEvent as u32 => PerfEvent,
551            x if x == CgroupSkb as u32 => CgroupSkb,
552            x if x == CgroupSock as u32 => CgroupSock,
553            x if x == LwtIn as u32 => LwtIn,
554            x if x == LwtOut as u32 => LwtOut,
555            x if x == LwtXmit as u32 => LwtXmit,
556            x if x == SockOps as u32 => SockOps,
557            x if x == SkSkb as u32 => SkSkb,
558            x if x == CgroupDevice as u32 => CgroupDevice,
559            x if x == SkMsg as u32 => SkMsg,
560            x if x == RawTracepoint as u32 => RawTracepoint,
561            x if x == CgroupSockAddr as u32 => CgroupSockAddr,
562            x if x == LwtSeg6local as u32 => LwtSeg6local,
563            x if x == LircMode2 as u32 => LircMode2,
564            x if x == SkReuseport as u32 => SkReuseport,
565            x if x == FlowDissector as u32 => FlowDissector,
566            x if x == CgroupSysctl as u32 => CgroupSysctl,
567            x if x == RawTracepointWritable as u32 => RawTracepointWritable,
568            x if x == CgroupSockopt as u32 => CgroupSockopt,
569            x if x == Tracing as u32 => Tracing,
570            x if x == StructOps as u32 => StructOps,
571            x if x == Ext as u32 => Ext,
572            x if x == Lsm as u32 => Lsm,
573            x if x == SkLookup as u32 => SkLookup,
574            x if x == Syscall as u32 => Syscall,
575            x if x == Netfilter as u32 => Netfilter,
576            _ => Unknown,
577        }
578    }
579}
580
581/// Attach type of a [`Program`]. Maps to `enum bpf_attach_type` in kernel uapi.
582#[non_exhaustive]
583#[repr(u32)]
584#[derive(Clone, Debug)]
585// TODO: Document variants.
586#[expect(missing_docs)]
587pub enum ProgramAttachType {
588    CgroupInetIngress = libbpf_sys::BPF_CGROUP_INET_INGRESS,
589    CgroupInetEgress = libbpf_sys::BPF_CGROUP_INET_EGRESS,
590    CgroupInetSockCreate = libbpf_sys::BPF_CGROUP_INET_SOCK_CREATE,
591    CgroupSockOps = libbpf_sys::BPF_CGROUP_SOCK_OPS,
592    SkSkbStreamParser = libbpf_sys::BPF_SK_SKB_STREAM_PARSER,
593    SkSkbStreamVerdict = libbpf_sys::BPF_SK_SKB_STREAM_VERDICT,
594    CgroupDevice = libbpf_sys::BPF_CGROUP_DEVICE,
595    SkMsgVerdict = libbpf_sys::BPF_SK_MSG_VERDICT,
596    CgroupInet4Bind = libbpf_sys::BPF_CGROUP_INET4_BIND,
597    CgroupInet6Bind = libbpf_sys::BPF_CGROUP_INET6_BIND,
598    CgroupInet4Connect = libbpf_sys::BPF_CGROUP_INET4_CONNECT,
599    CgroupInet6Connect = libbpf_sys::BPF_CGROUP_INET6_CONNECT,
600    CgroupInet4PostBind = libbpf_sys::BPF_CGROUP_INET4_POST_BIND,
601    CgroupInet6PostBind = libbpf_sys::BPF_CGROUP_INET6_POST_BIND,
602    CgroupUdp4Sendmsg = libbpf_sys::BPF_CGROUP_UDP4_SENDMSG,
603    CgroupUdp6Sendmsg = libbpf_sys::BPF_CGROUP_UDP6_SENDMSG,
604    LircMode2 = libbpf_sys::BPF_LIRC_MODE2,
605    FlowDissector = libbpf_sys::BPF_FLOW_DISSECTOR,
606    CgroupSysctl = libbpf_sys::BPF_CGROUP_SYSCTL,
607    CgroupUdp4Recvmsg = libbpf_sys::BPF_CGROUP_UDP4_RECVMSG,
608    CgroupUdp6Recvmsg = libbpf_sys::BPF_CGROUP_UDP6_RECVMSG,
609    CgroupGetsockopt = libbpf_sys::BPF_CGROUP_GETSOCKOPT,
610    CgroupSetsockopt = libbpf_sys::BPF_CGROUP_SETSOCKOPT,
611    TraceRawTp = libbpf_sys::BPF_TRACE_RAW_TP,
612    TraceFentry = libbpf_sys::BPF_TRACE_FENTRY,
613    TraceFexit = libbpf_sys::BPF_TRACE_FEXIT,
614    ModifyReturn = libbpf_sys::BPF_MODIFY_RETURN,
615    LsmMac = libbpf_sys::BPF_LSM_MAC,
616    TraceIter = libbpf_sys::BPF_TRACE_ITER,
617    CgroupInet4Getpeername = libbpf_sys::BPF_CGROUP_INET4_GETPEERNAME,
618    CgroupInet6Getpeername = libbpf_sys::BPF_CGROUP_INET6_GETPEERNAME,
619    CgroupInet4Getsockname = libbpf_sys::BPF_CGROUP_INET4_GETSOCKNAME,
620    CgroupInet6Getsockname = libbpf_sys::BPF_CGROUP_INET6_GETSOCKNAME,
621    XdpDevmap = libbpf_sys::BPF_XDP_DEVMAP,
622    CgroupInetSockRelease = libbpf_sys::BPF_CGROUP_INET_SOCK_RELEASE,
623    XdpCpumap = libbpf_sys::BPF_XDP_CPUMAP,
624    SkLookup = libbpf_sys::BPF_SK_LOOKUP,
625    Xdp = libbpf_sys::BPF_XDP,
626    SkSkbVerdict = libbpf_sys::BPF_SK_SKB_VERDICT,
627    SkReuseportSelect = libbpf_sys::BPF_SK_REUSEPORT_SELECT,
628    SkReuseportSelectOrMigrate = libbpf_sys::BPF_SK_REUSEPORT_SELECT_OR_MIGRATE,
629    PerfEvent = libbpf_sys::BPF_PERF_EVENT,
630    KprobeMulti = libbpf_sys::BPF_TRACE_KPROBE_MULTI,
631    NetkitPeer = libbpf_sys::BPF_NETKIT_PEER,
632    TraceUprobeMulti = libbpf_sys::BPF_TRACE_UPROBE_MULTI,
633    LsmCgroup = libbpf_sys::BPF_LSM_CGROUP,
634    TraceKprobeSession = libbpf_sys::BPF_TRACE_KPROBE_SESSION,
635    TcxIngress = libbpf_sys::BPF_TCX_INGRESS,
636    TcxEgress = libbpf_sys::BPF_TCX_EGRESS,
637    Netfilter = libbpf_sys::BPF_NETFILTER,
638    CgroupUnixGetsockname = libbpf_sys::BPF_CGROUP_UNIX_GETSOCKNAME,
639    CgroupUnixSendmsg = libbpf_sys::BPF_CGROUP_UNIX_SENDMSG,
640    NetkitPrimary = libbpf_sys::BPF_NETKIT_PRIMARY,
641    CgroupUnixRecvmsg = libbpf_sys::BPF_CGROUP_UNIX_RECVMSG,
642    CgroupUnixConnect = libbpf_sys::BPF_CGROUP_UNIX_CONNECT,
643    CgroupUnixGetpeername = libbpf_sys::BPF_CGROUP_UNIX_GETPEERNAME,
644    StructOps = libbpf_sys::BPF_STRUCT_OPS,
645    /// See [`MapType::Unknown`][crate::MapType::Unknown]
646    Unknown = u32::MAX,
647}
648
649impl From<u32> for ProgramAttachType {
650    fn from(value: u32) -> Self {
651        use ProgramAttachType::*;
652
653        match value {
654            x if x == CgroupInetIngress as u32 => CgroupInetIngress,
655            x if x == CgroupInetEgress as u32 => CgroupInetEgress,
656            x if x == CgroupInetSockCreate as u32 => CgroupInetSockCreate,
657            x if x == CgroupSockOps as u32 => CgroupSockOps,
658            x if x == SkSkbStreamParser as u32 => SkSkbStreamParser,
659            x if x == SkSkbStreamVerdict as u32 => SkSkbStreamVerdict,
660            x if x == CgroupDevice as u32 => CgroupDevice,
661            x if x == SkMsgVerdict as u32 => SkMsgVerdict,
662            x if x == CgroupInet4Bind as u32 => CgroupInet4Bind,
663            x if x == CgroupInet6Bind as u32 => CgroupInet6Bind,
664            x if x == CgroupInet4Connect as u32 => CgroupInet4Connect,
665            x if x == CgroupInet6Connect as u32 => CgroupInet6Connect,
666            x if x == CgroupInet4PostBind as u32 => CgroupInet4PostBind,
667            x if x == CgroupInet6PostBind as u32 => CgroupInet6PostBind,
668            x if x == CgroupUdp4Sendmsg as u32 => CgroupUdp4Sendmsg,
669            x if x == CgroupUdp6Sendmsg as u32 => CgroupUdp6Sendmsg,
670            x if x == LircMode2 as u32 => LircMode2,
671            x if x == FlowDissector as u32 => FlowDissector,
672            x if x == CgroupSysctl as u32 => CgroupSysctl,
673            x if x == CgroupUdp4Recvmsg as u32 => CgroupUdp4Recvmsg,
674            x if x == CgroupUdp6Recvmsg as u32 => CgroupUdp6Recvmsg,
675            x if x == CgroupGetsockopt as u32 => CgroupGetsockopt,
676            x if x == CgroupSetsockopt as u32 => CgroupSetsockopt,
677            x if x == TraceRawTp as u32 => TraceRawTp,
678            x if x == TraceFentry as u32 => TraceFentry,
679            x if x == TraceFexit as u32 => TraceFexit,
680            x if x == ModifyReturn as u32 => ModifyReturn,
681            x if x == LsmMac as u32 => LsmMac,
682            x if x == TraceIter as u32 => TraceIter,
683            x if x == CgroupInet4Getpeername as u32 => CgroupInet4Getpeername,
684            x if x == CgroupInet6Getpeername as u32 => CgroupInet6Getpeername,
685            x if x == CgroupInet4Getsockname as u32 => CgroupInet4Getsockname,
686            x if x == CgroupInet6Getsockname as u32 => CgroupInet6Getsockname,
687            x if x == XdpDevmap as u32 => XdpDevmap,
688            x if x == CgroupInetSockRelease as u32 => CgroupInetSockRelease,
689            x if x == XdpCpumap as u32 => XdpCpumap,
690            x if x == SkLookup as u32 => SkLookup,
691            x if x == Xdp as u32 => Xdp,
692            x if x == SkSkbVerdict as u32 => SkSkbVerdict,
693            x if x == SkReuseportSelect as u32 => SkReuseportSelect,
694            x if x == SkReuseportSelectOrMigrate as u32 => SkReuseportSelectOrMigrate,
695            x if x == PerfEvent as u32 => PerfEvent,
696            x if x == KprobeMulti as u32 => KprobeMulti,
697            x if x == NetkitPeer as u32 => NetkitPeer,
698            x if x == TraceUprobeMulti as u32 => TraceUprobeMulti,
699            x if x == LsmCgroup as u32 => LsmCgroup,
700            x if x == TraceKprobeSession as u32 => TraceKprobeSession,
701            x if x == TcxIngress as u32 => TcxIngress,
702            x if x == TcxEgress as u32 => TcxEgress,
703            x if x == Netfilter as u32 => Netfilter,
704            x if x == CgroupUnixGetsockname as u32 => CgroupUnixGetsockname,
705            x if x == CgroupUnixSendmsg as u32 => CgroupUnixSendmsg,
706            x if x == NetkitPrimary as u32 => NetkitPrimary,
707            x if x == CgroupUnixRecvmsg as u32 => CgroupUnixRecvmsg,
708            x if x == CgroupUnixConnect as u32 => CgroupUnixConnect,
709            x if x == CgroupUnixGetpeername as u32 => CgroupUnixGetpeername,
710            x if x == StructOps as u32 => StructOps,
711            _ => Unknown,
712        }
713    }
714}
715
716/// The input a program accepts.
717///
718/// This type is mostly used in conjunction with the [`Program::test_run`]
719/// facility.
720#[derive(Debug, Default)]
721pub struct Input<'dat> {
722    /// The input context to provide.
723    ///
724    /// The input is mutable because the kernel may modify it.
725    pub context_in: Option<&'dat mut [u8]>,
726    /// The output context buffer provided to the program.
727    pub context_out: Option<&'dat mut [u8]>,
728    /// Additional data to provide to the program.
729    pub data_in: Option<&'dat [u8]>,
730    /// The output data buffer provided to the program.
731    pub data_out: Option<&'dat mut [u8]>,
732    /// The 'cpu' value passed to the kernel.
733    pub cpu: u32,
734    /// The 'flags' value passed to the kernel.
735    pub flags: u32,
736    /// How many times to repeat the test run. A value of 0 will result in 1 run.
737    // 0 being forced to 1 by the kernel: https://elixir.bootlin.com/linux/v6.2.11/source/net/bpf/test_run.c#L352
738    pub repeat: u32,
739    /// The struct is non-exhaustive and open to extension.
740    #[doc(hidden)]
741    pub _non_exhaustive: (),
742}
743
744/// The output a program produces.
745///
746/// This type is mostly used in conjunction with the [`Program::test_run`]
747/// facility.
748#[derive(Debug)]
749pub struct Output<'dat> {
750    /// The value returned by the program.
751    pub return_value: u32,
752    /// The output context filled by the program/kernel.
753    pub context: Option<&'dat mut [u8]>,
754    /// Output data filled by the program.
755    pub data: Option<&'dat mut [u8]>,
756    /// Average duration per repetition.
757    pub duration: Duration,
758    /// The struct is non-exhaustive and open to extension.
759    #[doc(hidden)]
760    pub _non_exhaustive: (),
761}
762
763/// An immutable loaded BPF program.
764pub type Program<'obj> = ProgramImpl<'obj>;
765/// A mutable loaded BPF program.
766pub type ProgramMut<'obj> = ProgramImpl<'obj, Mut>;
767
768/// Represents a loaded [`Program`].
769///
770/// This struct is not safe to clone because the underlying libbpf resource cannot currently
771/// be protected from data races.
772///
773/// If you attempt to attach a `Program` with the wrong attach method, the `attach_*`
774/// method will fail with the appropriate error.
775#[derive(Debug)]
776#[repr(transparent)]
777pub struct ProgramImpl<'obj, T = ()> {
778    pub(crate) ptr: NonNull<libbpf_sys::bpf_program>,
779    _phantom: PhantomData<&'obj T>,
780}
781
782impl<'obj> Program<'obj> {
783    /// Create a [`Program`] from a [`libbpf_sys::bpf_program`]
784    pub fn new(prog: &'obj libbpf_sys::bpf_program) -> Self {
785        // SAFETY: We inferred the address from a reference, which is always
786        //         valid.
787        Self {
788            ptr: unsafe { NonNull::new_unchecked(prog as *const _ as *mut _) },
789            _phantom: PhantomData,
790        }
791    }
792
793    /// Retrieve the name of this `Program`.
794    pub fn name(&self) -> &'obj OsStr {
795        let name_ptr = unsafe { libbpf_sys::bpf_program__name(self.ptr.as_ptr()) };
796        let name_c_str = unsafe { CStr::from_ptr(name_ptr) };
797        // SAFETY: `bpf_program__name` always returns a non-NULL pointer.
798        OsStr::from_bytes(name_c_str.to_bytes())
799    }
800
801    /// Retrieve the name of the section this `Program` belongs to.
802    pub fn section(&self) -> &'obj OsStr {
803        // SAFETY: The program is always valid.
804        let p = unsafe { libbpf_sys::bpf_program__section_name(self.ptr.as_ptr()) };
805        // SAFETY: `bpf_program__section_name` will always return a non-NULL
806        //         pointer.
807        let section_c_str = unsafe { CStr::from_ptr(p) };
808        let section = OsStr::from_bytes(section_c_str.to_bytes());
809        section
810    }
811
812    /// Retrieve the type of the program.
813    pub fn prog_type(&self) -> ProgramType {
814        ProgramType::from(unsafe { libbpf_sys::bpf_program__type(self.ptr.as_ptr()) })
815    }
816
817    #[deprecated = "renamed to Program::fd_from_id"]
818    #[expect(missing_docs)]
819    #[inline]
820    pub fn get_fd_by_id(id: u32) -> Result<OwnedFd> {
821        Self::fd_from_id(id)
822    }
823
824    /// Returns program file descriptor given a program ID.
825    pub fn fd_from_id(id: u32) -> Result<OwnedFd> {
826        let ret = unsafe { libbpf_sys::bpf_prog_get_fd_by_id(id) };
827        let fd = util::parse_ret_i32(ret)?;
828        // SAFETY
829        // A file descriptor coming from the bpf_prog_get_fd_by_id function is always suitable for
830        // ownership and can be cleaned up with close.
831        Ok(unsafe { OwnedFd::from_raw_fd(fd) })
832    }
833
834    /// Returns program ID given a file descriptor.
835    pub fn id_from_fd(fd: BorrowedFd<'_>) -> Result<u32> {
836        let mut prog_info = libbpf_sys::bpf_prog_info::default();
837        let prog_info_ptr: *mut libbpf_sys::bpf_prog_info = &mut prog_info;
838        let mut len = size_of::<libbpf_sys::bpf_prog_info>() as u32;
839        let ret = unsafe {
840            libbpf_sys::bpf_obj_get_info_by_fd(
841                fd.as_raw_fd(),
842                prog_info_ptr as *mut c_void,
843                &mut len,
844            )
845        };
846        util::parse_ret(ret)?;
847        Ok(prog_info.id)
848    }
849
850    /// Returns fd of a previously pinned program
851    ///
852    /// Returns error, if the pinned path doesn't represent an eBPF program.
853    pub fn fd_from_pinned_path<P: AsRef<Path>>(path: P) -> Result<OwnedFd> {
854        let path_c = util::path_to_cstring(&path)?;
855        let path_ptr = path_c.as_ptr();
856
857        let fd = unsafe { libbpf_sys::bpf_obj_get(path_ptr) };
858        let fd = util::parse_ret_i32(fd).with_context(|| {
859            format!(
860                "failed to retrieve BPF object from pinned path `{}`",
861                path.as_ref().display()
862            )
863        })?;
864        let fd = unsafe { OwnedFd::from_raw_fd(fd) };
865
866        // A pinned path may represent an object of any kind, including map
867        // and link. This may cause unexpected behaviour for following functions,
868        // like bpf_*_get_info_by_fd(), which allow objects of any type.
869        let fd_type = util::object_type_from_fd(fd.as_fd())?;
870        match fd_type {
871            BpfObjectType::Program => Ok(fd),
872            other => Err(Error::with_invalid_data(format!(
873                "retrieved BPF fd is not a program fd: {other:#?}"
874            ))),
875        }
876    }
877
878    /// Returns flags that have been set for the program.
879    pub fn flags(&self) -> u32 {
880        unsafe { libbpf_sys::bpf_program__flags(self.ptr.as_ptr()) }
881    }
882
883    /// Retrieve the attach type of the program.
884    pub fn attach_type(&self) -> ProgramAttachType {
885        ProgramAttachType::from(unsafe {
886            libbpf_sys::bpf_program__expected_attach_type(self.ptr.as_ptr())
887        })
888    }
889
890    /// Return `true` if the bpf program is set to autoload, `false` otherwise.
891    pub fn autoload(&self) -> bool {
892        unsafe { libbpf_sys::bpf_program__autoload(self.ptr.as_ptr()) }
893    }
894
895    /// Return the bpf program's log level.
896    pub fn log_level(&self) -> u32 {
897        unsafe { libbpf_sys::bpf_program__log_level(self.ptr.as_ptr()) }
898    }
899
900    /// Returns the number of instructions that form the program.
901    ///
902    /// Please see note in [`OpenProgram::insn_cnt`].
903    pub fn insn_cnt(&self) -> usize {
904        unsafe { libbpf_sys::bpf_program__insn_cnt(self.ptr.as_ptr()) as usize }
905    }
906
907    /// Gives read-only access to BPF program's underlying BPF instructions.
908    ///
909    /// Please see note in [`OpenProgram::insns`].
910    pub fn insns(&self) -> &'obj [libbpf_sys::bpf_insn] {
911        let count = self.insn_cnt();
912        let ptr = unsafe { libbpf_sys::bpf_program__insns(self.ptr.as_ptr()) };
913        unsafe { slice::from_raw_parts(ptr, count) }
914    }
915}
916
917impl<'obj> ProgramMut<'obj> {
918    /// Create a [`Program`] from a [`libbpf_sys::bpf_program`]
919    pub fn new_mut(prog: &'obj mut libbpf_sys::bpf_program) -> Self {
920        Self {
921            ptr: unsafe { NonNull::new_unchecked(prog as *mut _) },
922            _phantom: PhantomData,
923        }
924    }
925
926    /// [Pin](https://facebookmicrosites.github.io/bpf/blog/2018/08/31/object-lifetime.html#bpffs)
927    /// this program to bpffs.
928    pub fn pin<P: AsRef<Path>>(&mut self, path: P) -> Result<()> {
929        let path_c = util::path_to_cstring(path)?;
930        let path_ptr = path_c.as_ptr();
931
932        let ret = unsafe { libbpf_sys::bpf_program__pin(self.ptr.as_ptr(), path_ptr) };
933        util::parse_ret(ret)
934    }
935
936    /// [Unpin](https://facebookmicrosites.github.io/bpf/blog/2018/08/31/object-lifetime.html#bpffs)
937    /// this program from bpffs
938    pub fn unpin<P: AsRef<Path>>(&mut self, path: P) -> Result<()> {
939        let path_c = util::path_to_cstring(path)?;
940        let path_ptr = path_c.as_ptr();
941
942        let ret = unsafe { libbpf_sys::bpf_program__unpin(self.ptr.as_ptr(), path_ptr) };
943        util::parse_ret(ret)
944    }
945
946    /// Auto-attach based on prog section
947    pub fn attach(&self) -> Result<Link> {
948        let ptr = unsafe { libbpf_sys::bpf_program__attach(self.ptr.as_ptr()) };
949        let ptr = validate_bpf_ret(ptr).context("failed to attach BPF program")?;
950        // SAFETY: the pointer came from libbpf and has been checked for errors.
951        let link = unsafe { Link::new(ptr) };
952        Ok(link)
953    }
954
955    /// Attach this program to a
956    /// [cgroup](https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v2.html).
957    pub fn attach_cgroup(&self, cgroup_fd: i32) -> Result<Link> {
958        let ptr = unsafe { libbpf_sys::bpf_program__attach_cgroup(self.ptr.as_ptr(), cgroup_fd) };
959        let ptr = validate_bpf_ret(ptr).context("failed to attach cgroup")?;
960        // SAFETY: the pointer came from libbpf and has been checked for errors.
961        let link = unsafe { Link::new(ptr) };
962        Ok(link)
963    }
964
965    /// Attach this program to a [perf event](https://linux.die.net/man/2/perf_event_open).
966    pub fn attach_perf_event(&self, pfd: i32) -> Result<Link> {
967        let ptr = unsafe { libbpf_sys::bpf_program__attach_perf_event(self.ptr.as_ptr(), pfd) };
968        let ptr = validate_bpf_ret(ptr).context("failed to attach perf event")?;
969        // SAFETY: the pointer came from libbpf and has been checked for errors.
970        let link = unsafe { Link::new(ptr) };
971        Ok(link)
972    }
973
974    /// Attach this program to a [perf event](https://linux.die.net/man/2/perf_event_open),
975    /// providing additional options.
976    pub fn attach_perf_event_with_opts(&self, pfd: i32, opts: PerfEventOpts) -> Result<Link> {
977        let libbpf_opts = libbpf_sys::bpf_perf_event_opts::from(opts);
978        let ptr = unsafe {
979            libbpf_sys::bpf_program__attach_perf_event_opts(self.ptr.as_ptr(), pfd, &libbpf_opts)
980        };
981        let ptr = validate_bpf_ret(ptr).context("failed to attach perf event")?;
982        // SAFETY: the pointer came from libbpf and has been checked for errors.
983        let link = unsafe { Link::new(ptr) };
984        Ok(link)
985    }
986
987    /// Attach this program to a [userspace
988    /// probe](https://www.kernel.org/doc/html/latest/trace/uprobetracer.html).
989    pub fn attach_uprobe<T: AsRef<Path>>(
990        &self,
991        retprobe: bool,
992        pid: i32,
993        binary_path: T,
994        func_offset: usize,
995    ) -> Result<Link> {
996        let path = util::path_to_cstring(binary_path)?;
997        let path_ptr = path.as_ptr();
998        let ptr = unsafe {
999            libbpf_sys::bpf_program__attach_uprobe(
1000                self.ptr.as_ptr(),
1001                retprobe,
1002                pid,
1003                path_ptr,
1004                func_offset as libbpf_sys::size_t,
1005            )
1006        };
1007        let ptr = validate_bpf_ret(ptr).context("failed to attach uprobe")?;
1008        // SAFETY: the pointer came from libbpf and has been checked for errors.
1009        let link = unsafe { Link::new(ptr) };
1010        Ok(link)
1011    }
1012
1013    /// Attach this program to a [userspace
1014    /// probe](https://www.kernel.org/doc/html/latest/trace/uprobetracer.html),
1015    /// providing additional options.
1016    pub fn attach_uprobe_with_opts(
1017        &self,
1018        pid: i32,
1019        binary_path: impl AsRef<Path>,
1020        func_offset: usize,
1021        opts: UprobeOpts,
1022    ) -> Result<Link> {
1023        let path = util::path_to_cstring(binary_path)?;
1024        let path_ptr = path.as_ptr();
1025        let UprobeOpts {
1026            ref_ctr_offset,
1027            cookie,
1028            retprobe,
1029            func_name,
1030            _non_exhaustive,
1031        } = opts;
1032
1033        let func_name: Option<CString> = if let Some(func_name) = func_name {
1034            Some(util::str_to_cstring(&func_name)?)
1035        } else {
1036            None
1037        };
1038        let ptr = func_name
1039            .as_ref()
1040            .map_or(ptr::null(), |func_name| func_name.as_ptr());
1041        let opts = libbpf_sys::bpf_uprobe_opts {
1042            sz: size_of::<libbpf_sys::bpf_uprobe_opts>() as _,
1043            ref_ctr_offset: ref_ctr_offset as libbpf_sys::size_t,
1044            bpf_cookie: cookie,
1045            retprobe,
1046            func_name: ptr,
1047            ..Default::default()
1048        };
1049
1050        let ptr = unsafe {
1051            libbpf_sys::bpf_program__attach_uprobe_opts(
1052                self.ptr.as_ptr(),
1053                pid,
1054                path_ptr,
1055                func_offset as libbpf_sys::size_t,
1056                &opts as *const _,
1057            )
1058        };
1059        let ptr = validate_bpf_ret(ptr).context("failed to attach uprobe")?;
1060        // SAFETY: the pointer came from libbpf and has been checked for errors.
1061        let link = unsafe { Link::new(ptr) };
1062        Ok(link)
1063    }
1064
1065    /// Attach this program to multiple
1066    /// [uprobes](https://www.kernel.org/doc/html/latest/trace/uprobetracer.html) at once.
1067    pub fn attach_uprobe_multi(
1068        &self,
1069        pid: i32,
1070        binary_path: impl AsRef<Path>,
1071        func_pattern: impl AsRef<str>,
1072        retprobe: bool,
1073        session: bool,
1074    ) -> Result<Link> {
1075        let opts = UprobeMultiOpts {
1076            syms: Vec::new(),
1077            offsets: Vec::new(),
1078            ref_ctr_offsets: Vec::new(),
1079            cookies: Vec::new(),
1080            retprobe,
1081            session,
1082            _non_exhaustive: (),
1083        };
1084
1085        self.attach_uprobe_multi_with_opts(pid, binary_path, func_pattern, opts)
1086    }
1087
1088    /// Attach this program to multiple
1089    /// [uprobes](https://www.kernel.org/doc/html/latest/trace/uprobetracer.html)
1090    /// at once, providing additional options.
1091    pub fn attach_uprobe_multi_with_opts(
1092        &self,
1093        pid: i32,
1094        binary_path: impl AsRef<Path>,
1095        func_pattern: impl AsRef<str>,
1096        opts: UprobeMultiOpts,
1097    ) -> Result<Link> {
1098        let path = util::path_to_cstring(binary_path)?;
1099        let path_ptr = path.as_ptr();
1100
1101        let UprobeMultiOpts {
1102            syms,
1103            offsets,
1104            ref_ctr_offsets,
1105            cookies,
1106            retprobe,
1107            session,
1108            _non_exhaustive,
1109        } = opts;
1110
1111        let pattern = util::str_to_cstring(func_pattern.as_ref())?;
1112        // TODO: We should push optionality into method signature.
1113        let pattern_ptr = if pattern.is_empty() {
1114            ptr::null()
1115        } else {
1116            pattern.as_ptr()
1117        };
1118
1119        let syms_cstrings = syms
1120            .iter()
1121            .map(|s| util::str_to_cstring(s))
1122            .collect::<Result<Vec<_>>>()?;
1123        let syms_ptrs = syms_cstrings
1124            .iter()
1125            .map(|cs| cs.as_ptr())
1126            .collect::<Vec<_>>();
1127        let syms_ptr = if !syms_ptrs.is_empty() {
1128            syms_ptrs.as_ptr()
1129        } else {
1130            ptr::null()
1131        };
1132        let offsets_ptr = if !offsets.is_empty() {
1133            offsets.as_ptr()
1134        } else {
1135            ptr::null()
1136        };
1137        let ref_ctr_offsets_ptr = if !ref_ctr_offsets.is_empty() {
1138            ref_ctr_offsets.as_ptr()
1139        } else {
1140            ptr::null()
1141        };
1142        let cookies_ptr = if !cookies.is_empty() {
1143            cookies.as_ptr()
1144        } else {
1145            ptr::null()
1146        };
1147        let cnt = if !syms.is_empty() {
1148            syms.len()
1149        } else if !offsets.is_empty() {
1150            offsets.len()
1151        } else {
1152            0
1153        };
1154
1155        let c_opts = libbpf_sys::bpf_uprobe_multi_opts {
1156            sz: size_of::<libbpf_sys::bpf_uprobe_multi_opts>() as _,
1157            syms: syms_ptr.cast_mut(),
1158            offsets: offsets_ptr.cast(),
1159            ref_ctr_offsets: ref_ctr_offsets_ptr.cast(),
1160            cookies: cookies_ptr.cast(),
1161            cnt: cnt as libbpf_sys::size_t,
1162            retprobe,
1163            session,
1164            ..Default::default()
1165        };
1166
1167        let ptr = unsafe {
1168            libbpf_sys::bpf_program__attach_uprobe_multi(
1169                self.ptr.as_ptr(),
1170                pid,
1171                path_ptr,
1172                pattern_ptr,
1173                &c_opts as *const _,
1174            )
1175        };
1176
1177        let ptr = validate_bpf_ret(ptr).context("failed to attach uprobe multi")?;
1178        // SAFETY: the pointer came from libbpf and has been checked for errors.
1179        let link = unsafe { Link::new(ptr) };
1180        Ok(link)
1181    }
1182
1183    /// Attach this program to a [kernel
1184    /// probe](https://www.kernel.org/doc/html/latest/trace/kprobetrace.html).
1185    pub fn attach_kprobe<T: AsRef<str>>(&self, retprobe: bool, func_name: T) -> Result<Link> {
1186        let func_name = util::str_to_cstring(func_name.as_ref())?;
1187        let func_name_ptr = func_name.as_ptr();
1188        let ptr = unsafe {
1189            libbpf_sys::bpf_program__attach_kprobe(self.ptr.as_ptr(), retprobe, func_name_ptr)
1190        };
1191        let ptr = validate_bpf_ret(ptr).context("failed to attach kprobe")?;
1192        // SAFETY: the pointer came from libbpf and has been checked for errors.
1193        let link = unsafe { Link::new(ptr) };
1194        Ok(link)
1195    }
1196
1197    /// Attach this program to a [kernel
1198    /// probe](https://www.kernel.org/doc/html/latest/trace/kprobetrace.html),
1199    /// providing additional options.
1200    pub fn attach_kprobe_with_opts<T: AsRef<str>>(
1201        &self,
1202        retprobe: bool,
1203        func_name: T,
1204        opts: KprobeOpts,
1205    ) -> Result<Link> {
1206        let func_name = util::str_to_cstring(func_name.as_ref())?;
1207        let func_name_ptr = func_name.as_ptr();
1208
1209        let mut opts = libbpf_sys::bpf_kprobe_opts::from(opts);
1210        opts.retprobe = retprobe;
1211
1212        let ptr = unsafe {
1213            libbpf_sys::bpf_program__attach_kprobe_opts(
1214                self.ptr.as_ptr(),
1215                func_name_ptr,
1216                &opts as *const _,
1217            )
1218        };
1219        let ptr = validate_bpf_ret(ptr).context("failed to attach kprobe")?;
1220        // SAFETY: the pointer came from libbpf and has been checked for errors.
1221        let link = unsafe { Link::new(ptr) };
1222        Ok(link)
1223    }
1224
1225    fn check_kprobe_multi_args<T: AsRef<str>>(symbols: &[T], cookies: &[u64]) -> Result<usize> {
1226        if symbols.is_empty() {
1227            return Err(Error::with_invalid_input("Symbols list cannot be empty"));
1228        }
1229
1230        if !cookies.is_empty() && symbols.len() != cookies.len() {
1231            return Err(Error::with_invalid_input(
1232                "Symbols and cookies list must have the same size",
1233            ));
1234        }
1235
1236        Ok(symbols.len())
1237    }
1238
1239    fn attach_kprobe_multi_impl(&self, opts: libbpf_sys::bpf_kprobe_multi_opts) -> Result<Link> {
1240        let ptr = unsafe {
1241            libbpf_sys::bpf_program__attach_kprobe_multi_opts(
1242                self.ptr.as_ptr(),
1243                ptr::null(),
1244                &opts as *const _,
1245            )
1246        };
1247        let ptr = validate_bpf_ret(ptr).context("failed to attach kprobe multi")?;
1248        // SAFETY: the pointer came from libbpf and has been checked for errors.
1249        let link = unsafe { Link::new(ptr) };
1250        Ok(link)
1251    }
1252
1253    /// Attach this program to multiple [kernel
1254    /// probes](https://www.kernel.org/doc/html/latest/trace/kprobetrace.html)
1255    /// at once.
1256    pub fn attach_kprobe_multi<T: AsRef<str>>(
1257        &self,
1258        retprobe: bool,
1259        symbols: Vec<T>,
1260    ) -> Result<Link> {
1261        let cnt = Self::check_kprobe_multi_args(&symbols, &[])?;
1262
1263        let csyms = symbols
1264            .iter()
1265            .map(|s| util::str_to_cstring(s.as_ref()))
1266            .collect::<Result<Vec<_>>>()?;
1267        let mut syms = csyms.iter().map(|s| s.as_ptr()).collect::<Vec<_>>();
1268
1269        let opts = libbpf_sys::bpf_kprobe_multi_opts {
1270            sz: size_of::<libbpf_sys::bpf_kprobe_multi_opts>() as _,
1271            syms: syms.as_mut_ptr() as _,
1272            cnt: cnt as libbpf_sys::size_t,
1273            retprobe,
1274            // bpf_kprobe_multi_opts might have padding fields on some platform
1275            ..Default::default()
1276        };
1277
1278        self.attach_kprobe_multi_impl(opts)
1279    }
1280
1281    /// Attach this program to multiple [kernel
1282    /// probes](https://www.kernel.org/doc/html/latest/trace/kprobetrace.html)
1283    /// at once, providing additional options.
1284    pub fn attach_kprobe_multi_with_opts(&self, opts: KprobeMultiOpts) -> Result<Link> {
1285        let KprobeMultiOpts {
1286            symbols,
1287            mut cookies,
1288            retprobe,
1289            _non_exhaustive,
1290        } = opts;
1291
1292        let cnt = Self::check_kprobe_multi_args(&symbols, &cookies)?;
1293
1294        let csyms = symbols
1295            .iter()
1296            .map(|s| util::str_to_cstring(s.as_ref()))
1297            .collect::<Result<Vec<_>>>()?;
1298        let mut syms = csyms.iter().map(|s| s.as_ptr()).collect::<Vec<_>>();
1299
1300        let opts = libbpf_sys::bpf_kprobe_multi_opts {
1301            sz: size_of::<libbpf_sys::bpf_kprobe_multi_opts>() as _,
1302            syms: syms.as_mut_ptr() as _,
1303            cookies: if !cookies.is_empty() {
1304                cookies.as_mut_ptr() as _
1305            } else {
1306                ptr::null()
1307            },
1308            cnt: cnt as libbpf_sys::size_t,
1309            retprobe,
1310            // bpf_kprobe_multi_opts might have padding fields on some platform
1311            ..Default::default()
1312        };
1313
1314        self.attach_kprobe_multi_impl(opts)
1315    }
1316
1317    /// Attach this program to the specified syscall
1318    pub fn attach_ksyscall<T: AsRef<str>>(&self, retprobe: bool, syscall_name: T) -> Result<Link> {
1319        let opts = libbpf_sys::bpf_ksyscall_opts {
1320            sz: size_of::<libbpf_sys::bpf_ksyscall_opts>() as _,
1321            retprobe,
1322            ..Default::default()
1323        };
1324
1325        let syscall_name = util::str_to_cstring(syscall_name.as_ref())?;
1326        let syscall_name_ptr = syscall_name.as_ptr();
1327        let ptr = unsafe {
1328            libbpf_sys::bpf_program__attach_ksyscall(self.ptr.as_ptr(), syscall_name_ptr, &opts)
1329        };
1330        let ptr = validate_bpf_ret(ptr).context("failed to attach ksyscall")?;
1331        // SAFETY: the pointer came from libbpf and has been checked for errors.
1332        let link = unsafe { Link::new(ptr) };
1333        Ok(link)
1334    }
1335
1336    fn attach_tracepoint_impl(
1337        &self,
1338        tp_category: &str,
1339        tp_name: &str,
1340        tp_opts: Option<TracepointOpts>,
1341    ) -> Result<Link> {
1342        let tp_category = util::str_to_cstring(tp_category)?;
1343        let tp_category_ptr = tp_category.as_ptr();
1344        let tp_name = util::str_to_cstring(tp_name)?;
1345        let tp_name_ptr = tp_name.as_ptr();
1346
1347        let tp_opts = tp_opts.map(libbpf_sys::bpf_tracepoint_opts::from);
1348        let opts = tp_opts.as_ref().map_or(ptr::null(), |opts| opts);
1349        let ptr = unsafe {
1350            libbpf_sys::bpf_program__attach_tracepoint_opts(
1351                self.ptr.as_ptr(),
1352                tp_category_ptr,
1353                tp_name_ptr,
1354                opts as *const _,
1355            )
1356        };
1357
1358        let ptr = validate_bpf_ret(ptr).context("failed to attach tracepoint")?;
1359        // SAFETY: the pointer came from libbpf and has been checked for errors.
1360        let link = unsafe { Link::new(ptr) };
1361        Ok(link)
1362    }
1363
1364    /// Attach this program to a [kernel
1365    /// tracepoint](https://www.kernel.org/doc/html/latest/trace/tracepoints.html).
1366    pub fn attach_tracepoint(
1367        &self,
1368        tp_category: TracepointCategory,
1369        tp_name: impl AsRef<str>,
1370    ) -> Result<Link> {
1371        self.attach_tracepoint_impl(tp_category.as_ref(), tp_name.as_ref(), None)
1372    }
1373
1374    /// Attach this program to a [kernel
1375    /// tracepoint](https://www.kernel.org/doc/html/latest/trace/tracepoints.html),
1376    /// providing additional options.
1377    pub fn attach_tracepoint_with_opts(
1378        &self,
1379        tp_category: TracepointCategory,
1380        tp_name: impl AsRef<str>,
1381        tp_opts: TracepointOpts,
1382    ) -> Result<Link> {
1383        self.attach_tracepoint_impl(tp_category.as_ref(), tp_name.as_ref(), Some(tp_opts))
1384    }
1385
1386    /// Attach this program to a [raw kernel
1387    /// tracepoint](https://lwn.net/Articles/748352/).
1388    pub fn attach_raw_tracepoint<T: AsRef<str>>(&self, tp_name: T) -> Result<Link> {
1389        let tp_name = util::str_to_cstring(tp_name.as_ref())?;
1390        let tp_name_ptr = tp_name.as_ptr();
1391        let ptr = unsafe {
1392            libbpf_sys::bpf_program__attach_raw_tracepoint(self.ptr.as_ptr(), tp_name_ptr)
1393        };
1394        let ptr = validate_bpf_ret(ptr).context("failed to attach raw tracepoint")?;
1395        // SAFETY: the pointer came from libbpf and has been checked for errors.
1396        let link = unsafe { Link::new(ptr) };
1397        Ok(link)
1398    }
1399
1400    /// Attach this program to a [raw kernel
1401    /// tracepoint](https://lwn.net/Articles/748352/), providing additional
1402    /// options.
1403    pub fn attach_raw_tracepoint_with_opts<T: AsRef<str>>(
1404        &self,
1405        tp_name: T,
1406        tp_opts: RawTracepointOpts,
1407    ) -> Result<Link> {
1408        let tp_name = util::str_to_cstring(tp_name.as_ref())?;
1409        let tp_name_ptr = tp_name.as_ptr();
1410        let mut tp_opts = libbpf_sys::bpf_raw_tracepoint_opts::from(tp_opts);
1411        let ptr = unsafe {
1412            libbpf_sys::bpf_program__attach_raw_tracepoint_opts(
1413                self.ptr.as_ptr(),
1414                tp_name_ptr,
1415                &mut tp_opts as *mut _,
1416            )
1417        };
1418        let ptr = validate_bpf_ret(ptr).context("failed to attach raw tracepoint")?;
1419        // SAFETY: the pointer came from libbpf and has been checked for errors.
1420        let link = unsafe { Link::new(ptr) };
1421        Ok(link)
1422    }
1423
1424    /// Attach to an [LSM](https://en.wikipedia.org/wiki/Linux_Security_Modules) hook
1425    pub fn attach_lsm(&self) -> Result<Link> {
1426        let ptr = unsafe { libbpf_sys::bpf_program__attach_lsm(self.ptr.as_ptr()) };
1427        let ptr = validate_bpf_ret(ptr).context("failed to attach LSM")?;
1428        // SAFETY: the pointer came from libbpf and has been checked for errors.
1429        let link = unsafe { Link::new(ptr) };
1430        Ok(link)
1431    }
1432
1433    /// Attach to a [fentry/fexit kernel probe](https://lwn.net/Articles/801479/)
1434    pub fn attach_trace(&self) -> Result<Link> {
1435        let ptr = unsafe { libbpf_sys::bpf_program__attach_trace(self.ptr.as_ptr()) };
1436        let ptr = validate_bpf_ret(ptr).context("failed to attach fentry/fexit kernel probe")?;
1437        // SAFETY: the pointer came from libbpf and has been checked for errors.
1438        let link = unsafe { Link::new(ptr) };
1439        Ok(link)
1440    }
1441
1442    /// Attach a verdict/parser to a [sockmap/sockhash](https://lwn.net/Articles/731133/)
1443    pub fn attach_sockmap(&self, map_fd: i32) -> Result<()> {
1444        let err = unsafe {
1445            libbpf_sys::bpf_prog_attach(
1446                self.as_fd().as_raw_fd(),
1447                map_fd,
1448                self.attach_type() as u32,
1449                0,
1450            )
1451        };
1452        util::parse_ret(err)
1453    }
1454
1455    /// Attach this program to [XDP](https://lwn.net/Articles/825998/)
1456    pub fn attach_xdp(&self, ifindex: i32) -> Result<Link> {
1457        let ptr = unsafe { libbpf_sys::bpf_program__attach_xdp(self.ptr.as_ptr(), ifindex) };
1458        let ptr = validate_bpf_ret(ptr).context("failed to attach XDP program")?;
1459        // SAFETY: the pointer came from libbpf and has been checked for errors.
1460        let link = unsafe { Link::new(ptr) };
1461        Ok(link)
1462    }
1463
1464    /// Attach this program to [netns-based programs](https://lwn.net/Articles/819618/)
1465    pub fn attach_netns(&self, netns_fd: i32) -> Result<Link> {
1466        let ptr = unsafe { libbpf_sys::bpf_program__attach_netns(self.ptr.as_ptr(), netns_fd) };
1467        let ptr = validate_bpf_ret(ptr).context("failed to attach network namespace program")?;
1468        // SAFETY: the pointer came from libbpf and has been checked for errors.
1469        let link = unsafe { Link::new(ptr) };
1470        Ok(link)
1471    }
1472
1473    /// Attach this program to [netfilter programs](https://lwn.net/Articles/925082/)
1474    pub fn attach_netfilter_with_opts(
1475        &self,
1476        netfilter_opt: netfilter::NetfilterOpts,
1477    ) -> Result<Link> {
1478        let netfilter_opts = libbpf_sys::bpf_netfilter_opts::from(netfilter_opt);
1479
1480        let ptr = unsafe {
1481            libbpf_sys::bpf_program__attach_netfilter(
1482                self.ptr.as_ptr(),
1483                &netfilter_opts as *const _,
1484            )
1485        };
1486
1487        let ptr = validate_bpf_ret(ptr).context("failed to attach netfilter program")?;
1488        // SAFETY: the pointer came from libbpf and has been checked for errors.
1489        let link = unsafe { Link::new(ptr) };
1490        Ok(link)
1491    }
1492
1493    fn attach_usdt_impl(
1494        &self,
1495        pid: i32,
1496        binary_path: &Path,
1497        usdt_provider: &str,
1498        usdt_name: &str,
1499        usdt_opts: Option<UsdtOpts>,
1500    ) -> Result<Link> {
1501        let path = util::path_to_cstring(binary_path)?;
1502        let path_ptr = path.as_ptr();
1503        let usdt_provider = util::str_to_cstring(usdt_provider)?;
1504        let usdt_provider_ptr = usdt_provider.as_ptr();
1505        let usdt_name = util::str_to_cstring(usdt_name)?;
1506        let usdt_name_ptr = usdt_name.as_ptr();
1507        let usdt_opts = usdt_opts.map(libbpf_sys::bpf_usdt_opts::from);
1508        let usdt_opts_ptr = usdt_opts
1509            .as_ref()
1510            .map(|opts| opts as *const _)
1511            .unwrap_or_else(ptr::null);
1512
1513        let ptr = unsafe {
1514            libbpf_sys::bpf_program__attach_usdt(
1515                self.ptr.as_ptr(),
1516                pid,
1517                path_ptr,
1518                usdt_provider_ptr,
1519                usdt_name_ptr,
1520                usdt_opts_ptr,
1521            )
1522        };
1523        let ptr = validate_bpf_ret(ptr).context("failed to attach USDT")?;
1524        // SAFETY: the pointer came from libbpf and has been checked for errors.
1525        let link = unsafe { Link::new(ptr) };
1526        Ok(link)
1527    }
1528
1529    /// Attach this program to a [USDT](https://lwn.net/Articles/753601/) probe
1530    /// point. The entry point of the program must be defined with
1531    /// `SEC("usdt")`.
1532    pub fn attach_usdt(
1533        &self,
1534        pid: i32,
1535        binary_path: impl AsRef<Path>,
1536        usdt_provider: impl AsRef<str>,
1537        usdt_name: impl AsRef<str>,
1538    ) -> Result<Link> {
1539        self.attach_usdt_impl(
1540            pid,
1541            binary_path.as_ref(),
1542            usdt_provider.as_ref(),
1543            usdt_name.as_ref(),
1544            None,
1545        )
1546    }
1547
1548    /// Attach this program to a [USDT](https://lwn.net/Articles/753601/) probe
1549    /// point, providing additional options. The entry point of the program must
1550    /// be defined with `SEC("usdt")`.
1551    pub fn attach_usdt_with_opts(
1552        &self,
1553        pid: i32,
1554        binary_path: impl AsRef<Path>,
1555        usdt_provider: impl AsRef<str>,
1556        usdt_name: impl AsRef<str>,
1557        usdt_opts: UsdtOpts,
1558    ) -> Result<Link> {
1559        self.attach_usdt_impl(
1560            pid,
1561            binary_path.as_ref(),
1562            usdt_provider.as_ref(),
1563            usdt_name.as_ref(),
1564            Some(usdt_opts),
1565        )
1566    }
1567
1568    /// Attach this program to a
1569    /// [BPF Iterator](https://www.kernel.org/doc/html/latest/bpf/bpf_iterators.html).
1570    /// The entry point of the program must be defined with `SEC("iter")` or `SEC("iter.s")`.
1571    pub fn attach_iter(&self, map_fd: BorrowedFd<'_>) -> Result<Link> {
1572        let map_opts = MapIterOpts {
1573            fd: map_fd,
1574            _non_exhaustive: (),
1575        };
1576        self.attach_iter_with_opts(IterOpts::Map(map_opts))
1577    }
1578
1579    /// Attach this program to a
1580    /// [BPF Iterator](https://www.kernel.org/doc/html/latest/bpf/bpf_iterators.html),
1581    /// providing additional options.
1582    ///
1583    /// The entry point of the program must be defined with `SEC("iter")` or `SEC("iter.s")`.
1584    pub fn attach_iter_with_opts(&self, opts: IterOpts<'_>) -> Result<Link> {
1585        let mut linkinfo = libbpf_sys::bpf_iter_link_info::from(opts);
1586        let attach_opt = libbpf_sys::bpf_iter_attach_opts {
1587            link_info: &raw mut linkinfo,
1588            link_info_len: size_of::<libbpf_sys::bpf_iter_link_info>() as _,
1589            sz: size_of::<libbpf_sys::bpf_iter_attach_opts>() as _,
1590            ..Default::default()
1591        };
1592        let ptr = unsafe {
1593            libbpf_sys::bpf_program__attach_iter(
1594                self.ptr.as_ptr(),
1595                &attach_opt as *const libbpf_sys::bpf_iter_attach_opts,
1596            )
1597        };
1598
1599        let ptr = validate_bpf_ret(ptr).context("failed to attach iterator")?;
1600        // SAFETY: the pointer came from libbpf and has been checked for errors.
1601        let link = unsafe { Link::new(ptr) };
1602        Ok(link)
1603    }
1604
1605    /// Test run the program with the given input data.
1606    ///
1607    /// This function uses the
1608    /// [BPF_PROG_RUN](https://www.kernel.org/doc/html/latest/bpf/bpf_prog_run.html)
1609    /// facility.
1610    pub fn test_run<'dat>(&self, input: Input<'dat>) -> Result<Output<'dat>> {
1611        unsafe fn slice_from_array<'t, T>(items: *mut T, num_items: usize) -> Option<&'t mut [T]> {
1612            if items.is_null() {
1613                None
1614            } else {
1615                Some(unsafe { slice::from_raw_parts_mut(items, num_items) })
1616            }
1617        }
1618
1619        let Input {
1620            context_in,
1621            mut context_out,
1622            data_in,
1623            mut data_out,
1624            cpu,
1625            flags,
1626            repeat,
1627            _non_exhaustive: (),
1628        } = input;
1629
1630        let mut opts = unsafe { mem::zeroed::<libbpf_sys::bpf_test_run_opts>() };
1631        opts.sz = size_of_val(&opts) as _;
1632        opts.ctx_in = context_in
1633            .as_ref()
1634            .map(|data| data.as_ptr().cast())
1635            .unwrap_or_else(ptr::null);
1636        opts.ctx_size_in = context_in.map(|data| data.len() as _).unwrap_or(0);
1637        opts.ctx_out = context_out
1638            .as_mut()
1639            .map(|data| data.as_mut_ptr().cast())
1640            .unwrap_or_else(ptr::null_mut);
1641        opts.ctx_size_out = context_out.map(|data| data.len() as _).unwrap_or(0);
1642        opts.data_in = data_in
1643            .map(|data| data.as_ptr().cast())
1644            .unwrap_or_else(ptr::null);
1645        opts.data_size_in = data_in.map(|data| data.len() as _).unwrap_or(0);
1646        opts.data_out = data_out
1647            .as_mut()
1648            .map(|data| data.as_mut_ptr().cast())
1649            .unwrap_or_else(ptr::null_mut);
1650        opts.data_size_out = data_out.map(|data| data.len() as _).unwrap_or(0);
1651        opts.cpu = cpu;
1652        opts.flags = flags;
1653        // safe to cast back to an i32. While the API uses an `int`: https://elixir.bootlin.com/linux/v6.2.11/source/tools/lib/bpf/bpf.h#L446
1654        // the kernel user api uses __u32: https://elixir.bootlin.com/linux/v6.2.11/source/include/uapi/linux/bpf.h#L1430
1655        opts.repeat = repeat as i32;
1656
1657        let rc = unsafe { libbpf_sys::bpf_prog_test_run_opts(self.as_fd().as_raw_fd(), &mut opts) };
1658        let () = util::parse_ret(rc)?;
1659        let output = Output {
1660            return_value: opts.retval,
1661            context: unsafe { slice_from_array(opts.ctx_out.cast(), opts.ctx_size_out as _) },
1662            data: unsafe { slice_from_array(opts.data_out.cast(), opts.data_size_out as _) },
1663            duration: Duration::from_nanos(opts.duration.into()),
1664            _non_exhaustive: (),
1665        };
1666        Ok(output)
1667    }
1668
1669    /// Get the stdout BPF stream of the program.
1670    pub fn stdout(&self) -> impl Read + '_ {
1671        Stream::new(self.as_fd(), Stream::BPF_STDOUT)
1672    }
1673
1674    /// Get the stderr BPF stream of the program.
1675    pub fn stderr(&self) -> impl Read + '_ {
1676        Stream::new(self.as_fd(), Stream::BPF_STDERR)
1677    }
1678}
1679
1680impl<'obj> Deref for ProgramMut<'obj> {
1681    type Target = Program<'obj>;
1682
1683    fn deref(&self) -> &Self::Target {
1684        // SAFETY: `ProgramImpl` is `repr(transparent)` and so in-memory
1685        //         representation of both types is the same.
1686        unsafe { transmute::<&ProgramMut<'obj>, &Program<'obj>>(self) }
1687    }
1688}
1689
1690impl<T> AsFd for ProgramImpl<'_, T> {
1691    fn as_fd(&self) -> BorrowedFd<'_> {
1692        let fd = unsafe { libbpf_sys::bpf_program__fd(self.ptr.as_ptr()) };
1693        unsafe { BorrowedFd::borrow_raw(fd) }
1694    }
1695}
1696
1697impl<T> AsRawLibbpf for ProgramImpl<'_, T> {
1698    type LibbpfType = libbpf_sys::bpf_program;
1699
1700    /// Retrieve the underlying [`libbpf_sys::bpf_program`].
1701    fn as_libbpf_object(&self) -> NonNull<Self::LibbpfType> {
1702        self.ptr
1703    }
1704}
1705
1706#[cfg(test)]
1707mod tests {
1708    use super::*;
1709
1710    use std::mem::discriminant;
1711
1712    #[test]
1713    fn program_type() {
1714        use ProgramType::*;
1715
1716        for t in [
1717            Unspec,
1718            SocketFilter,
1719            Kprobe,
1720            SchedCls,
1721            SchedAct,
1722            Tracepoint,
1723            Xdp,
1724            PerfEvent,
1725            CgroupSkb,
1726            CgroupSock,
1727            LwtIn,
1728            LwtOut,
1729            LwtXmit,
1730            SockOps,
1731            SkSkb,
1732            CgroupDevice,
1733            SkMsg,
1734            RawTracepoint,
1735            CgroupSockAddr,
1736            LwtSeg6local,
1737            LircMode2,
1738            SkReuseport,
1739            FlowDissector,
1740            CgroupSysctl,
1741            RawTracepointWritable,
1742            CgroupSockopt,
1743            Tracing,
1744            StructOps,
1745            Ext,
1746            Lsm,
1747            SkLookup,
1748            Syscall,
1749            Netfilter,
1750            Unknown,
1751        ] {
1752            // check if discriminants match after a roundtrip conversion
1753            assert_eq!(discriminant(&t), discriminant(&ProgramType::from(t as u32)));
1754        }
1755    }
1756
1757    #[test]
1758    fn program_attach_type() {
1759        use ProgramAttachType::*;
1760
1761        for t in [
1762            CgroupInetIngress,
1763            CgroupInetEgress,
1764            CgroupInetSockCreate,
1765            CgroupSockOps,
1766            SkSkbStreamParser,
1767            SkSkbStreamVerdict,
1768            CgroupDevice,
1769            SkMsgVerdict,
1770            CgroupInet4Bind,
1771            CgroupInet6Bind,
1772            CgroupInet4Connect,
1773            CgroupInet6Connect,
1774            CgroupInet4PostBind,
1775            CgroupInet6PostBind,
1776            CgroupUdp4Sendmsg,
1777            CgroupUdp6Sendmsg,
1778            LircMode2,
1779            FlowDissector,
1780            CgroupSysctl,
1781            CgroupUdp4Recvmsg,
1782            CgroupUdp6Recvmsg,
1783            CgroupGetsockopt,
1784            CgroupSetsockopt,
1785            TraceRawTp,
1786            TraceFentry,
1787            TraceFexit,
1788            ModifyReturn,
1789            LsmMac,
1790            TraceIter,
1791            CgroupInet4Getpeername,
1792            CgroupInet6Getpeername,
1793            CgroupInet4Getsockname,
1794            CgroupInet6Getsockname,
1795            XdpDevmap,
1796            CgroupInetSockRelease,
1797            XdpCpumap,
1798            SkLookup,
1799            Xdp,
1800            SkSkbVerdict,
1801            SkReuseportSelect,
1802            SkReuseportSelectOrMigrate,
1803            PerfEvent,
1804            Unknown,
1805        ] {
1806            // check if discriminants match after a roundtrip conversion
1807            assert_eq!(
1808                discriminant(&t),
1809                discriminant(&ProgramAttachType::from(t as u32))
1810            );
1811        }
1812    }
1813}