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, 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    /// See [`MapType::Unknown`][crate::MapType::Unknown]
504    Unknown = u32::MAX,
505}
506
507impl ProgramType {
508    /// Detects if host kernel supports this BPF program type
509    ///
510    /// Make sure the process has required set of CAP_* permissions (or runs as
511    /// root) when performing feature checking.
512    pub fn is_supported(&self) -> Result<bool> {
513        let ret = unsafe { libbpf_sys::libbpf_probe_bpf_prog_type(*self as u32, ptr::null()) };
514        match ret {
515            0 => Ok(false),
516            1 => Ok(true),
517            _ => Err(Error::from_raw_os_error(-ret)),
518        }
519    }
520
521    /// Detects if host kernel supports the use of a given BPF helper from this BPF program type.
522    /// * `helper_id` - BPF helper ID (enum `bpf_func_id`) to check support for
523    ///
524    /// Make sure the process has required set of CAP_* permissions (or runs as
525    /// root) when performing feature checking.
526    pub fn is_helper_supported(&self, helper_id: bpf_func_id) -> Result<bool> {
527        let ret =
528            unsafe { libbpf_sys::libbpf_probe_bpf_helper(*self as u32, helper_id, ptr::null()) };
529        match ret {
530            0 => Ok(false),
531            1 => Ok(true),
532            _ => Err(Error::from_raw_os_error(-ret)),
533        }
534    }
535}
536
537impl From<u32> for ProgramType {
538    fn from(value: u32) -> Self {
539        use ProgramType::*;
540
541        match value {
542            x if x == Unspec as u32 => Unspec,
543            x if x == SocketFilter as u32 => SocketFilter,
544            x if x == Kprobe as u32 => Kprobe,
545            x if x == SchedCls as u32 => SchedCls,
546            x if x == SchedAct as u32 => SchedAct,
547            x if x == Tracepoint as u32 => Tracepoint,
548            x if x == Xdp as u32 => Xdp,
549            x if x == PerfEvent as u32 => PerfEvent,
550            x if x == CgroupSkb as u32 => CgroupSkb,
551            x if x == CgroupSock as u32 => CgroupSock,
552            x if x == LwtIn as u32 => LwtIn,
553            x if x == LwtOut as u32 => LwtOut,
554            x if x == LwtXmit as u32 => LwtXmit,
555            x if x == SockOps as u32 => SockOps,
556            x if x == SkSkb as u32 => SkSkb,
557            x if x == CgroupDevice as u32 => CgroupDevice,
558            x if x == SkMsg as u32 => SkMsg,
559            x if x == RawTracepoint as u32 => RawTracepoint,
560            x if x == CgroupSockAddr as u32 => CgroupSockAddr,
561            x if x == LwtSeg6local as u32 => LwtSeg6local,
562            x if x == LircMode2 as u32 => LircMode2,
563            x if x == SkReuseport as u32 => SkReuseport,
564            x if x == FlowDissector as u32 => FlowDissector,
565            x if x == CgroupSysctl as u32 => CgroupSysctl,
566            x if x == RawTracepointWritable as u32 => RawTracepointWritable,
567            x if x == CgroupSockopt as u32 => CgroupSockopt,
568            x if x == Tracing as u32 => Tracing,
569            x if x == StructOps as u32 => StructOps,
570            x if x == Ext as u32 => Ext,
571            x if x == Lsm as u32 => Lsm,
572            x if x == SkLookup as u32 => SkLookup,
573            x if x == Syscall as u32 => Syscall,
574            _ => Unknown,
575        }
576    }
577}
578
579/// Attach type of a [`Program`]. Maps to `enum bpf_attach_type` in kernel uapi.
580#[non_exhaustive]
581#[repr(u32)]
582#[derive(Clone, Debug)]
583// TODO: Document variants.
584#[expect(missing_docs)]
585pub enum ProgramAttachType {
586    CgroupInetIngress = libbpf_sys::BPF_CGROUP_INET_INGRESS,
587    CgroupInetEgress = libbpf_sys::BPF_CGROUP_INET_EGRESS,
588    CgroupInetSockCreate = libbpf_sys::BPF_CGROUP_INET_SOCK_CREATE,
589    CgroupSockOps = libbpf_sys::BPF_CGROUP_SOCK_OPS,
590    SkSkbStreamParser = libbpf_sys::BPF_SK_SKB_STREAM_PARSER,
591    SkSkbStreamVerdict = libbpf_sys::BPF_SK_SKB_STREAM_VERDICT,
592    CgroupDevice = libbpf_sys::BPF_CGROUP_DEVICE,
593    SkMsgVerdict = libbpf_sys::BPF_SK_MSG_VERDICT,
594    CgroupInet4Bind = libbpf_sys::BPF_CGROUP_INET4_BIND,
595    CgroupInet6Bind = libbpf_sys::BPF_CGROUP_INET6_BIND,
596    CgroupInet4Connect = libbpf_sys::BPF_CGROUP_INET4_CONNECT,
597    CgroupInet6Connect = libbpf_sys::BPF_CGROUP_INET6_CONNECT,
598    CgroupInet4PostBind = libbpf_sys::BPF_CGROUP_INET4_POST_BIND,
599    CgroupInet6PostBind = libbpf_sys::BPF_CGROUP_INET6_POST_BIND,
600    CgroupUdp4Sendmsg = libbpf_sys::BPF_CGROUP_UDP4_SENDMSG,
601    CgroupUdp6Sendmsg = libbpf_sys::BPF_CGROUP_UDP6_SENDMSG,
602    LircMode2 = libbpf_sys::BPF_LIRC_MODE2,
603    FlowDissector = libbpf_sys::BPF_FLOW_DISSECTOR,
604    CgroupSysctl = libbpf_sys::BPF_CGROUP_SYSCTL,
605    CgroupUdp4Recvmsg = libbpf_sys::BPF_CGROUP_UDP4_RECVMSG,
606    CgroupUdp6Recvmsg = libbpf_sys::BPF_CGROUP_UDP6_RECVMSG,
607    CgroupGetsockopt = libbpf_sys::BPF_CGROUP_GETSOCKOPT,
608    CgroupSetsockopt = libbpf_sys::BPF_CGROUP_SETSOCKOPT,
609    TraceRawTp = libbpf_sys::BPF_TRACE_RAW_TP,
610    TraceFentry = libbpf_sys::BPF_TRACE_FENTRY,
611    TraceFexit = libbpf_sys::BPF_TRACE_FEXIT,
612    ModifyReturn = libbpf_sys::BPF_MODIFY_RETURN,
613    LsmMac = libbpf_sys::BPF_LSM_MAC,
614    TraceIter = libbpf_sys::BPF_TRACE_ITER,
615    CgroupInet4Getpeername = libbpf_sys::BPF_CGROUP_INET4_GETPEERNAME,
616    CgroupInet6Getpeername = libbpf_sys::BPF_CGROUP_INET6_GETPEERNAME,
617    CgroupInet4Getsockname = libbpf_sys::BPF_CGROUP_INET4_GETSOCKNAME,
618    CgroupInet6Getsockname = libbpf_sys::BPF_CGROUP_INET6_GETSOCKNAME,
619    XdpDevmap = libbpf_sys::BPF_XDP_DEVMAP,
620    CgroupInetSockRelease = libbpf_sys::BPF_CGROUP_INET_SOCK_RELEASE,
621    XdpCpumap = libbpf_sys::BPF_XDP_CPUMAP,
622    SkLookup = libbpf_sys::BPF_SK_LOOKUP,
623    Xdp = libbpf_sys::BPF_XDP,
624    SkSkbVerdict = libbpf_sys::BPF_SK_SKB_VERDICT,
625    SkReuseportSelect = libbpf_sys::BPF_SK_REUSEPORT_SELECT,
626    SkReuseportSelectOrMigrate = libbpf_sys::BPF_SK_REUSEPORT_SELECT_OR_MIGRATE,
627    PerfEvent = libbpf_sys::BPF_PERF_EVENT,
628    KprobeMulti = libbpf_sys::BPF_TRACE_KPROBE_MULTI,
629    NetkitPeer = libbpf_sys::BPF_NETKIT_PEER,
630    TraceUprobeMulti = libbpf_sys::BPF_TRACE_UPROBE_MULTI,
631    LsmCgroup = libbpf_sys::BPF_LSM_CGROUP,
632    TraceKprobeSession = libbpf_sys::BPF_TRACE_KPROBE_SESSION,
633    TcxIngress = libbpf_sys::BPF_TCX_INGRESS,
634    TcxEgress = libbpf_sys::BPF_TCX_EGRESS,
635    Netfilter = libbpf_sys::BPF_NETFILTER,
636    CgroupUnixGetsockname = libbpf_sys::BPF_CGROUP_UNIX_GETSOCKNAME,
637    CgroupUnixSendmsg = libbpf_sys::BPF_CGROUP_UNIX_SENDMSG,
638    NetkitPrimary = libbpf_sys::BPF_NETKIT_PRIMARY,
639    CgroupUnixRecvmsg = libbpf_sys::BPF_CGROUP_UNIX_RECVMSG,
640    CgroupUnixConnect = libbpf_sys::BPF_CGROUP_UNIX_CONNECT,
641    CgroupUnixGetpeername = libbpf_sys::BPF_CGROUP_UNIX_GETPEERNAME,
642    StructOps = libbpf_sys::BPF_STRUCT_OPS,
643    /// See [`MapType::Unknown`][crate::MapType::Unknown]
644    Unknown = u32::MAX,
645}
646
647impl From<u32> for ProgramAttachType {
648    fn from(value: u32) -> Self {
649        use ProgramAttachType::*;
650
651        match value {
652            x if x == CgroupInetIngress as u32 => CgroupInetIngress,
653            x if x == CgroupInetEgress as u32 => CgroupInetEgress,
654            x if x == CgroupInetSockCreate as u32 => CgroupInetSockCreate,
655            x if x == CgroupSockOps as u32 => CgroupSockOps,
656            x if x == SkSkbStreamParser as u32 => SkSkbStreamParser,
657            x if x == SkSkbStreamVerdict as u32 => SkSkbStreamVerdict,
658            x if x == CgroupDevice as u32 => CgroupDevice,
659            x if x == SkMsgVerdict as u32 => SkMsgVerdict,
660            x if x == CgroupInet4Bind as u32 => CgroupInet4Bind,
661            x if x == CgroupInet6Bind as u32 => CgroupInet6Bind,
662            x if x == CgroupInet4Connect as u32 => CgroupInet4Connect,
663            x if x == CgroupInet6Connect as u32 => CgroupInet6Connect,
664            x if x == CgroupInet4PostBind as u32 => CgroupInet4PostBind,
665            x if x == CgroupInet6PostBind as u32 => CgroupInet6PostBind,
666            x if x == CgroupUdp4Sendmsg as u32 => CgroupUdp4Sendmsg,
667            x if x == CgroupUdp6Sendmsg as u32 => CgroupUdp6Sendmsg,
668            x if x == LircMode2 as u32 => LircMode2,
669            x if x == FlowDissector as u32 => FlowDissector,
670            x if x == CgroupSysctl as u32 => CgroupSysctl,
671            x if x == CgroupUdp4Recvmsg as u32 => CgroupUdp4Recvmsg,
672            x if x == CgroupUdp6Recvmsg as u32 => CgroupUdp6Recvmsg,
673            x if x == CgroupGetsockopt as u32 => CgroupGetsockopt,
674            x if x == CgroupSetsockopt as u32 => CgroupSetsockopt,
675            x if x == TraceRawTp as u32 => TraceRawTp,
676            x if x == TraceFentry as u32 => TraceFentry,
677            x if x == TraceFexit as u32 => TraceFexit,
678            x if x == ModifyReturn as u32 => ModifyReturn,
679            x if x == LsmMac as u32 => LsmMac,
680            x if x == TraceIter as u32 => TraceIter,
681            x if x == CgroupInet4Getpeername as u32 => CgroupInet4Getpeername,
682            x if x == CgroupInet6Getpeername as u32 => CgroupInet6Getpeername,
683            x if x == CgroupInet4Getsockname as u32 => CgroupInet4Getsockname,
684            x if x == CgroupInet6Getsockname as u32 => CgroupInet6Getsockname,
685            x if x == XdpDevmap as u32 => XdpDevmap,
686            x if x == CgroupInetSockRelease as u32 => CgroupInetSockRelease,
687            x if x == XdpCpumap as u32 => XdpCpumap,
688            x if x == SkLookup as u32 => SkLookup,
689            x if x == Xdp as u32 => Xdp,
690            x if x == SkSkbVerdict as u32 => SkSkbVerdict,
691            x if x == SkReuseportSelect as u32 => SkReuseportSelect,
692            x if x == SkReuseportSelectOrMigrate as u32 => SkReuseportSelectOrMigrate,
693            x if x == PerfEvent as u32 => PerfEvent,
694            x if x == KprobeMulti as u32 => KprobeMulti,
695            x if x == NetkitPeer as u32 => NetkitPeer,
696            x if x == TraceUprobeMulti as u32 => TraceUprobeMulti,
697            x if x == LsmCgroup as u32 => LsmCgroup,
698            x if x == TraceKprobeSession as u32 => TraceKprobeSession,
699            x if x == TcxIngress as u32 => TcxIngress,
700            x if x == TcxEgress as u32 => TcxEgress,
701            x if x == Netfilter as u32 => Netfilter,
702            x if x == CgroupUnixGetsockname as u32 => CgroupUnixGetsockname,
703            x if x == CgroupUnixSendmsg as u32 => CgroupUnixSendmsg,
704            x if x == NetkitPrimary as u32 => NetkitPrimary,
705            x if x == CgroupUnixRecvmsg as u32 => CgroupUnixRecvmsg,
706            x if x == CgroupUnixConnect as u32 => CgroupUnixConnect,
707            x if x == CgroupUnixGetpeername as u32 => CgroupUnixGetpeername,
708            x if x == StructOps as u32 => StructOps,
709            _ => Unknown,
710        }
711    }
712}
713
714/// The input a program accepts.
715///
716/// This type is mostly used in conjunction with the [`Program::test_run`]
717/// facility.
718#[derive(Debug, Default)]
719pub struct Input<'dat> {
720    /// The input context to provide.
721    ///
722    /// The input is mutable because the kernel may modify it.
723    pub context_in: Option<&'dat mut [u8]>,
724    /// The output context buffer provided to the program.
725    pub context_out: Option<&'dat mut [u8]>,
726    /// Additional data to provide to the program.
727    pub data_in: Option<&'dat [u8]>,
728    /// The output data buffer provided to the program.
729    pub data_out: Option<&'dat mut [u8]>,
730    /// The 'cpu' value passed to the kernel.
731    pub cpu: u32,
732    /// The 'flags' value passed to the kernel.
733    pub flags: u32,
734    /// How many times to repeat the test run. A value of 0 will result in 1 run.
735    // 0 being forced to 1 by the kernel: https://elixir.bootlin.com/linux/v6.2.11/source/net/bpf/test_run.c#L352
736    pub repeat: u32,
737    /// The struct is non-exhaustive and open to extension.
738    #[doc(hidden)]
739    pub _non_exhaustive: (),
740}
741
742/// The output a program produces.
743///
744/// This type is mostly used in conjunction with the [`Program::test_run`]
745/// facility.
746#[derive(Debug)]
747pub struct Output<'dat> {
748    /// The value returned by the program.
749    pub return_value: u32,
750    /// The output context filled by the program/kernel.
751    pub context: Option<&'dat mut [u8]>,
752    /// Output data filled by the program.
753    pub data: Option<&'dat mut [u8]>,
754    /// Average duration per repetition.
755    pub duration: Duration,
756    /// The struct is non-exhaustive and open to extension.
757    #[doc(hidden)]
758    pub _non_exhaustive: (),
759}
760
761/// An immutable loaded BPF program.
762pub type Program<'obj> = ProgramImpl<'obj>;
763/// A mutable loaded BPF program.
764pub type ProgramMut<'obj> = ProgramImpl<'obj, Mut>;
765
766/// Represents a loaded [`Program`].
767///
768/// This struct is not safe to clone because the underlying libbpf resource cannot currently
769/// be protected from data races.
770///
771/// If you attempt to attach a `Program` with the wrong attach method, the `attach_*`
772/// method will fail with the appropriate error.
773#[derive(Debug)]
774#[repr(transparent)]
775pub struct ProgramImpl<'obj, T = ()> {
776    pub(crate) ptr: NonNull<libbpf_sys::bpf_program>,
777    _phantom: PhantomData<&'obj T>,
778}
779
780impl<'obj> Program<'obj> {
781    /// Create a [`Program`] from a [`libbpf_sys::bpf_program`]
782    pub fn new(prog: &'obj libbpf_sys::bpf_program) -> Self {
783        // SAFETY: We inferred the address from a reference, which is always
784        //         valid.
785        Self {
786            ptr: unsafe { NonNull::new_unchecked(prog as *const _ as *mut _) },
787            _phantom: PhantomData,
788        }
789    }
790
791    /// Retrieve the name of this `Program`.
792    pub fn name(&self) -> &'obj OsStr {
793        let name_ptr = unsafe { libbpf_sys::bpf_program__name(self.ptr.as_ptr()) };
794        let name_c_str = unsafe { CStr::from_ptr(name_ptr) };
795        // SAFETY: `bpf_program__name` always returns a non-NULL pointer.
796        OsStr::from_bytes(name_c_str.to_bytes())
797    }
798
799    /// Retrieve the name of the section this `Program` belongs to.
800    pub fn section(&self) -> &'obj OsStr {
801        // SAFETY: The program is always valid.
802        let p = unsafe { libbpf_sys::bpf_program__section_name(self.ptr.as_ptr()) };
803        // SAFETY: `bpf_program__section_name` will always return a non-NULL
804        //         pointer.
805        let section_c_str = unsafe { CStr::from_ptr(p) };
806        let section = OsStr::from_bytes(section_c_str.to_bytes());
807        section
808    }
809
810    /// Retrieve the type of the program.
811    pub fn prog_type(&self) -> ProgramType {
812        ProgramType::from(unsafe { libbpf_sys::bpf_program__type(self.ptr.as_ptr()) })
813    }
814
815    #[deprecated = "renamed to Program::fd_from_id"]
816    #[expect(missing_docs)]
817    #[inline]
818    pub fn get_fd_by_id(id: u32) -> Result<OwnedFd> {
819        Self::fd_from_id(id)
820    }
821
822    /// Returns program file descriptor given a program ID.
823    pub fn fd_from_id(id: u32) -> Result<OwnedFd> {
824        let ret = unsafe { libbpf_sys::bpf_prog_get_fd_by_id(id) };
825        let fd = util::parse_ret_i32(ret)?;
826        // SAFETY
827        // A file descriptor coming from the bpf_prog_get_fd_by_id function is always suitable for
828        // ownership and can be cleaned up with close.
829        Ok(unsafe { OwnedFd::from_raw_fd(fd) })
830    }
831
832    /// Returns program ID given a file descriptor.
833    pub fn id_from_fd(fd: BorrowedFd<'_>) -> Result<u32> {
834        let mut prog_info = libbpf_sys::bpf_prog_info::default();
835        let prog_info_ptr: *mut libbpf_sys::bpf_prog_info = &mut prog_info;
836        let mut len = size_of::<libbpf_sys::bpf_prog_info>() as u32;
837        let ret = unsafe {
838            libbpf_sys::bpf_obj_get_info_by_fd(
839                fd.as_raw_fd(),
840                prog_info_ptr as *mut c_void,
841                &mut len,
842            )
843        };
844        util::parse_ret(ret)?;
845        Ok(prog_info.id)
846    }
847
848    /// Returns fd of a previously pinned program
849    ///
850    /// Returns error, if the pinned path doesn't represent an eBPF program.
851    pub fn fd_from_pinned_path<P: AsRef<Path>>(path: P) -> Result<OwnedFd> {
852        let path_c = util::path_to_cstring(&path)?;
853        let path_ptr = path_c.as_ptr();
854
855        let fd = unsafe { libbpf_sys::bpf_obj_get(path_ptr) };
856        let fd = util::parse_ret_i32(fd).with_context(|| {
857            format!(
858                "failed to retrieve BPF object from pinned path `{}`",
859                path.as_ref().display()
860            )
861        })?;
862        let fd = unsafe { OwnedFd::from_raw_fd(fd) };
863
864        // A pinned path may represent an object of any kind, including map
865        // and link. This may cause unexpected behaviour for following functions,
866        // like bpf_*_get_info_by_fd(), which allow objects of any type.
867        let fd_type = util::object_type_from_fd(fd.as_fd())?;
868        match fd_type {
869            BpfObjectType::Program => Ok(fd),
870            other => Err(Error::with_invalid_data(format!(
871                "retrieved BPF fd is not a program fd: {other:#?}"
872            ))),
873        }
874    }
875
876    /// Returns flags that have been set for the program.
877    pub fn flags(&self) -> u32 {
878        unsafe { libbpf_sys::bpf_program__flags(self.ptr.as_ptr()) }
879    }
880
881    /// Retrieve the attach type of the program.
882    pub fn attach_type(&self) -> ProgramAttachType {
883        ProgramAttachType::from(unsafe {
884            libbpf_sys::bpf_program__expected_attach_type(self.ptr.as_ptr())
885        })
886    }
887
888    /// Return `true` if the bpf program is set to autoload, `false` otherwise.
889    pub fn autoload(&self) -> bool {
890        unsafe { libbpf_sys::bpf_program__autoload(self.ptr.as_ptr()) }
891    }
892
893    /// Return the bpf program's log level.
894    pub fn log_level(&self) -> u32 {
895        unsafe { libbpf_sys::bpf_program__log_level(self.ptr.as_ptr()) }
896    }
897
898    /// Returns the number of instructions that form the program.
899    ///
900    /// Please see note in [`OpenProgram::insn_cnt`].
901    pub fn insn_cnt(&self) -> usize {
902        unsafe { libbpf_sys::bpf_program__insn_cnt(self.ptr.as_ptr()) as usize }
903    }
904
905    /// Gives read-only access to BPF program's underlying BPF instructions.
906    ///
907    /// Please see note in [`OpenProgram::insns`].
908    pub fn insns(&self) -> &'obj [libbpf_sys::bpf_insn] {
909        let count = self.insn_cnt();
910        let ptr = unsafe { libbpf_sys::bpf_program__insns(self.ptr.as_ptr()) };
911        unsafe { slice::from_raw_parts(ptr, count) }
912    }
913}
914
915impl<'obj> ProgramMut<'obj> {
916    /// Create a [`Program`] from a [`libbpf_sys::bpf_program`]
917    pub fn new_mut(prog: &'obj mut libbpf_sys::bpf_program) -> Self {
918        Self {
919            ptr: unsafe { NonNull::new_unchecked(prog as *mut _) },
920            _phantom: PhantomData,
921        }
922    }
923
924    /// [Pin](https://facebookmicrosites.github.io/bpf/blog/2018/08/31/object-lifetime.html#bpffs)
925    /// this program to bpffs.
926    pub fn pin<P: AsRef<Path>>(&mut self, path: P) -> Result<()> {
927        let path_c = util::path_to_cstring(path)?;
928        let path_ptr = path_c.as_ptr();
929
930        let ret = unsafe { libbpf_sys::bpf_program__pin(self.ptr.as_ptr(), path_ptr) };
931        util::parse_ret(ret)
932    }
933
934    /// [Unpin](https://facebookmicrosites.github.io/bpf/blog/2018/08/31/object-lifetime.html#bpffs)
935    /// this program from bpffs
936    pub fn unpin<P: AsRef<Path>>(&mut self, path: P) -> Result<()> {
937        let path_c = util::path_to_cstring(path)?;
938        let path_ptr = path_c.as_ptr();
939
940        let ret = unsafe { libbpf_sys::bpf_program__unpin(self.ptr.as_ptr(), path_ptr) };
941        util::parse_ret(ret)
942    }
943
944    /// Auto-attach based on prog section
945    pub fn attach(&self) -> Result<Link> {
946        let ptr = unsafe { libbpf_sys::bpf_program__attach(self.ptr.as_ptr()) };
947        let ptr = validate_bpf_ret(ptr).context("failed to attach BPF program")?;
948        // SAFETY: the pointer came from libbpf and has been checked for errors.
949        let link = unsafe { Link::new(ptr) };
950        Ok(link)
951    }
952
953    /// Attach this program to a
954    /// [cgroup](https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v2.html).
955    pub fn attach_cgroup(&self, cgroup_fd: i32) -> Result<Link> {
956        let ptr = unsafe { libbpf_sys::bpf_program__attach_cgroup(self.ptr.as_ptr(), cgroup_fd) };
957        let ptr = validate_bpf_ret(ptr).context("failed to attach cgroup")?;
958        // SAFETY: the pointer came from libbpf and has been checked for errors.
959        let link = unsafe { Link::new(ptr) };
960        Ok(link)
961    }
962
963    /// Attach this program to a [perf event](https://linux.die.net/man/2/perf_event_open).
964    pub fn attach_perf_event(&self, pfd: i32) -> Result<Link> {
965        let ptr = unsafe { libbpf_sys::bpf_program__attach_perf_event(self.ptr.as_ptr(), pfd) };
966        let ptr = validate_bpf_ret(ptr).context("failed to attach perf event")?;
967        // SAFETY: the pointer came from libbpf and has been checked for errors.
968        let link = unsafe { Link::new(ptr) };
969        Ok(link)
970    }
971
972    /// Attach this program to a [perf event](https://linux.die.net/man/2/perf_event_open),
973    /// providing additional options.
974    pub fn attach_perf_event_with_opts(&self, pfd: i32, opts: PerfEventOpts) -> Result<Link> {
975        let libbpf_opts = libbpf_sys::bpf_perf_event_opts::from(opts);
976        let ptr = unsafe {
977            libbpf_sys::bpf_program__attach_perf_event_opts(self.ptr.as_ptr(), pfd, &libbpf_opts)
978        };
979        let ptr = validate_bpf_ret(ptr).context("failed to attach perf event")?;
980        // SAFETY: the pointer came from libbpf and has been checked for errors.
981        let link = unsafe { Link::new(ptr) };
982        Ok(link)
983    }
984
985    /// Attach this program to a [userspace
986    /// probe](https://www.kernel.org/doc/html/latest/trace/uprobetracer.html).
987    pub fn attach_uprobe<T: AsRef<Path>>(
988        &self,
989        retprobe: bool,
990        pid: i32,
991        binary_path: T,
992        func_offset: usize,
993    ) -> Result<Link> {
994        let path = util::path_to_cstring(binary_path)?;
995        let path_ptr = path.as_ptr();
996        let ptr = unsafe {
997            libbpf_sys::bpf_program__attach_uprobe(
998                self.ptr.as_ptr(),
999                retprobe,
1000                pid,
1001                path_ptr,
1002                func_offset as libbpf_sys::size_t,
1003            )
1004        };
1005        let ptr = validate_bpf_ret(ptr).context("failed to attach uprobe")?;
1006        // SAFETY: the pointer came from libbpf and has been checked for errors.
1007        let link = unsafe { Link::new(ptr) };
1008        Ok(link)
1009    }
1010
1011    /// Attach this program to a [userspace
1012    /// probe](https://www.kernel.org/doc/html/latest/trace/uprobetracer.html),
1013    /// providing additional options.
1014    pub fn attach_uprobe_with_opts(
1015        &self,
1016        pid: i32,
1017        binary_path: impl AsRef<Path>,
1018        func_offset: usize,
1019        opts: UprobeOpts,
1020    ) -> Result<Link> {
1021        let path = util::path_to_cstring(binary_path)?;
1022        let path_ptr = path.as_ptr();
1023        let UprobeOpts {
1024            ref_ctr_offset,
1025            cookie,
1026            retprobe,
1027            func_name,
1028            _non_exhaustive,
1029        } = opts;
1030
1031        let func_name: Option<CString> = if let Some(func_name) = func_name {
1032            Some(util::str_to_cstring(&func_name)?)
1033        } else {
1034            None
1035        };
1036        let ptr = func_name
1037            .as_ref()
1038            .map_or(ptr::null(), |func_name| func_name.as_ptr());
1039        let opts = libbpf_sys::bpf_uprobe_opts {
1040            sz: size_of::<libbpf_sys::bpf_uprobe_opts>() as _,
1041            ref_ctr_offset: ref_ctr_offset as libbpf_sys::size_t,
1042            bpf_cookie: cookie,
1043            retprobe,
1044            func_name: ptr,
1045            ..Default::default()
1046        };
1047
1048        let ptr = unsafe {
1049            libbpf_sys::bpf_program__attach_uprobe_opts(
1050                self.ptr.as_ptr(),
1051                pid,
1052                path_ptr,
1053                func_offset as libbpf_sys::size_t,
1054                &opts as *const _,
1055            )
1056        };
1057        let ptr = validate_bpf_ret(ptr).context("failed to attach uprobe")?;
1058        // SAFETY: the pointer came from libbpf and has been checked for errors.
1059        let link = unsafe { Link::new(ptr) };
1060        Ok(link)
1061    }
1062
1063    /// Attach this program to multiple
1064    /// [uprobes](https://www.kernel.org/doc/html/latest/trace/uprobetracer.html) at once.
1065    pub fn attach_uprobe_multi(
1066        &self,
1067        pid: i32,
1068        binary_path: impl AsRef<Path>,
1069        func_pattern: impl AsRef<str>,
1070        retprobe: bool,
1071        session: bool,
1072    ) -> Result<Link> {
1073        let opts = UprobeMultiOpts {
1074            syms: Vec::new(),
1075            offsets: Vec::new(),
1076            ref_ctr_offsets: Vec::new(),
1077            cookies: Vec::new(),
1078            retprobe,
1079            session,
1080            _non_exhaustive: (),
1081        };
1082
1083        self.attach_uprobe_multi_with_opts(pid, binary_path, func_pattern, opts)
1084    }
1085
1086    /// Attach this program to multiple
1087    /// [uprobes](https://www.kernel.org/doc/html/latest/trace/uprobetracer.html)
1088    /// at once, providing additional options.
1089    pub fn attach_uprobe_multi_with_opts(
1090        &self,
1091        pid: i32,
1092        binary_path: impl AsRef<Path>,
1093        func_pattern: impl AsRef<str>,
1094        opts: UprobeMultiOpts,
1095    ) -> Result<Link> {
1096        let path = util::path_to_cstring(binary_path)?;
1097        let path_ptr = path.as_ptr();
1098
1099        let UprobeMultiOpts {
1100            syms,
1101            offsets,
1102            ref_ctr_offsets,
1103            cookies,
1104            retprobe,
1105            session,
1106            _non_exhaustive,
1107        } = opts;
1108
1109        let pattern = util::str_to_cstring(func_pattern.as_ref())?;
1110        let pattern_ptr = pattern.as_ptr();
1111
1112        let syms_cstrings = syms
1113            .iter()
1114            .map(|s| util::str_to_cstring(s))
1115            .collect::<Result<Vec<_>>>()?;
1116        let syms_ptrs = syms_cstrings
1117            .iter()
1118            .map(|cs| cs.as_ptr())
1119            .collect::<Vec<_>>();
1120        let syms_ptr = if !syms_ptrs.is_empty() {
1121            syms_ptrs.as_ptr()
1122        } else {
1123            ptr::null()
1124        };
1125        let offsets_ptr = if !offsets.is_empty() {
1126            offsets.as_ptr()
1127        } else {
1128            ptr::null()
1129        };
1130        let ref_ctr_offsets_ptr = if !ref_ctr_offsets.is_empty() {
1131            ref_ctr_offsets.as_ptr()
1132        } else {
1133            ptr::null()
1134        };
1135        let cookies_ptr = if !cookies.is_empty() {
1136            cookies.as_ptr()
1137        } else {
1138            ptr::null()
1139        };
1140        let cnt = if !syms.is_empty() {
1141            syms.len()
1142        } else if !offsets.is_empty() {
1143            offsets.len()
1144        } else {
1145            0
1146        };
1147
1148        let c_opts = libbpf_sys::bpf_uprobe_multi_opts {
1149            sz: size_of::<libbpf_sys::bpf_uprobe_multi_opts>() as _,
1150            syms: syms_ptr.cast_mut(),
1151            offsets: offsets_ptr.cast(),
1152            ref_ctr_offsets: ref_ctr_offsets_ptr.cast(),
1153            cookies: cookies_ptr.cast(),
1154            cnt: cnt as libbpf_sys::size_t,
1155            retprobe,
1156            session,
1157            ..Default::default()
1158        };
1159
1160        let ptr = unsafe {
1161            libbpf_sys::bpf_program__attach_uprobe_multi(
1162                self.ptr.as_ptr(),
1163                pid,
1164                path_ptr,
1165                pattern_ptr,
1166                &c_opts as *const _,
1167            )
1168        };
1169
1170        let ptr = validate_bpf_ret(ptr).context("failed to attach uprobe multi")?;
1171        // SAFETY: the pointer came from libbpf and has been checked for errors.
1172        let link = unsafe { Link::new(ptr) };
1173        Ok(link)
1174    }
1175
1176    /// Attach this program to a [kernel
1177    /// probe](https://www.kernel.org/doc/html/latest/trace/kprobetrace.html).
1178    pub fn attach_kprobe<T: AsRef<str>>(&self, retprobe: bool, func_name: T) -> Result<Link> {
1179        let func_name = util::str_to_cstring(func_name.as_ref())?;
1180        let func_name_ptr = func_name.as_ptr();
1181        let ptr = unsafe {
1182            libbpf_sys::bpf_program__attach_kprobe(self.ptr.as_ptr(), retprobe, func_name_ptr)
1183        };
1184        let ptr = validate_bpf_ret(ptr).context("failed to attach kprobe")?;
1185        // SAFETY: the pointer came from libbpf and has been checked for errors.
1186        let link = unsafe { Link::new(ptr) };
1187        Ok(link)
1188    }
1189
1190    /// Attach this program to a [kernel
1191    /// probe](https://www.kernel.org/doc/html/latest/trace/kprobetrace.html),
1192    /// providing additional options.
1193    pub fn attach_kprobe_with_opts<T: AsRef<str>>(
1194        &self,
1195        retprobe: bool,
1196        func_name: T,
1197        opts: KprobeOpts,
1198    ) -> Result<Link> {
1199        let func_name = util::str_to_cstring(func_name.as_ref())?;
1200        let func_name_ptr = func_name.as_ptr();
1201
1202        let mut opts = libbpf_sys::bpf_kprobe_opts::from(opts);
1203        opts.retprobe = retprobe;
1204
1205        let ptr = unsafe {
1206            libbpf_sys::bpf_program__attach_kprobe_opts(
1207                self.ptr.as_ptr(),
1208                func_name_ptr,
1209                &opts as *const _,
1210            )
1211        };
1212        let ptr = validate_bpf_ret(ptr).context("failed to attach kprobe")?;
1213        // SAFETY: the pointer came from libbpf and has been checked for errors.
1214        let link = unsafe { Link::new(ptr) };
1215        Ok(link)
1216    }
1217
1218    fn check_kprobe_multi_args<T: AsRef<str>>(symbols: &[T], cookies: &[u64]) -> Result<usize> {
1219        if symbols.is_empty() {
1220            return Err(Error::with_invalid_input("Symbols list cannot be empty"));
1221        }
1222
1223        if !cookies.is_empty() && symbols.len() != cookies.len() {
1224            return Err(Error::with_invalid_input(
1225                "Symbols and cookies list must have the same size",
1226            ));
1227        }
1228
1229        Ok(symbols.len())
1230    }
1231
1232    fn attach_kprobe_multi_impl(&self, opts: libbpf_sys::bpf_kprobe_multi_opts) -> Result<Link> {
1233        let ptr = unsafe {
1234            libbpf_sys::bpf_program__attach_kprobe_multi_opts(
1235                self.ptr.as_ptr(),
1236                ptr::null(),
1237                &opts as *const _,
1238            )
1239        };
1240        let ptr = validate_bpf_ret(ptr).context("failed to attach kprobe multi")?;
1241        // SAFETY: the pointer came from libbpf and has been checked for errors.
1242        let link = unsafe { Link::new(ptr) };
1243        Ok(link)
1244    }
1245
1246    /// Attach this program to multiple [kernel
1247    /// probes](https://www.kernel.org/doc/html/latest/trace/kprobetrace.html)
1248    /// at once.
1249    pub fn attach_kprobe_multi<T: AsRef<str>>(
1250        &self,
1251        retprobe: bool,
1252        symbols: Vec<T>,
1253    ) -> Result<Link> {
1254        let cnt = Self::check_kprobe_multi_args(&symbols, &[])?;
1255
1256        let csyms = symbols
1257            .iter()
1258            .map(|s| util::str_to_cstring(s.as_ref()))
1259            .collect::<Result<Vec<_>>>()?;
1260        let mut syms = csyms.iter().map(|s| s.as_ptr()).collect::<Vec<_>>();
1261
1262        let opts = libbpf_sys::bpf_kprobe_multi_opts {
1263            sz: size_of::<libbpf_sys::bpf_kprobe_multi_opts>() as _,
1264            syms: syms.as_mut_ptr() as _,
1265            cnt: cnt as libbpf_sys::size_t,
1266            retprobe,
1267            // bpf_kprobe_multi_opts might have padding fields on some platform
1268            ..Default::default()
1269        };
1270
1271        self.attach_kprobe_multi_impl(opts)
1272    }
1273
1274    /// Attach this program to multiple [kernel
1275    /// probes](https://www.kernel.org/doc/html/latest/trace/kprobetrace.html)
1276    /// at once, providing additional options.
1277    pub fn attach_kprobe_multi_with_opts(&self, opts: KprobeMultiOpts) -> Result<Link> {
1278        let KprobeMultiOpts {
1279            symbols,
1280            mut cookies,
1281            retprobe,
1282            _non_exhaustive,
1283        } = opts;
1284
1285        let cnt = Self::check_kprobe_multi_args(&symbols, &cookies)?;
1286
1287        let csyms = symbols
1288            .iter()
1289            .map(|s| util::str_to_cstring(s.as_ref()))
1290            .collect::<Result<Vec<_>>>()?;
1291        let mut syms = csyms.iter().map(|s| s.as_ptr()).collect::<Vec<_>>();
1292
1293        let opts = libbpf_sys::bpf_kprobe_multi_opts {
1294            sz: size_of::<libbpf_sys::bpf_kprobe_multi_opts>() as _,
1295            syms: syms.as_mut_ptr() as _,
1296            cookies: if !cookies.is_empty() {
1297                cookies.as_mut_ptr() as _
1298            } else {
1299                ptr::null()
1300            },
1301            cnt: cnt as libbpf_sys::size_t,
1302            retprobe,
1303            // bpf_kprobe_multi_opts might have padding fields on some platform
1304            ..Default::default()
1305        };
1306
1307        self.attach_kprobe_multi_impl(opts)
1308    }
1309
1310    /// Attach this program to the specified syscall
1311    pub fn attach_ksyscall<T: AsRef<str>>(&self, retprobe: bool, syscall_name: T) -> Result<Link> {
1312        let opts = libbpf_sys::bpf_ksyscall_opts {
1313            sz: size_of::<libbpf_sys::bpf_ksyscall_opts>() as _,
1314            retprobe,
1315            ..Default::default()
1316        };
1317
1318        let syscall_name = util::str_to_cstring(syscall_name.as_ref())?;
1319        let syscall_name_ptr = syscall_name.as_ptr();
1320        let ptr = unsafe {
1321            libbpf_sys::bpf_program__attach_ksyscall(self.ptr.as_ptr(), syscall_name_ptr, &opts)
1322        };
1323        let ptr = validate_bpf_ret(ptr).context("failed to attach ksyscall")?;
1324        // SAFETY: the pointer came from libbpf and has been checked for errors.
1325        let link = unsafe { Link::new(ptr) };
1326        Ok(link)
1327    }
1328
1329    fn attach_tracepoint_impl(
1330        &self,
1331        tp_category: &str,
1332        tp_name: &str,
1333        tp_opts: Option<TracepointOpts>,
1334    ) -> Result<Link> {
1335        let tp_category = util::str_to_cstring(tp_category)?;
1336        let tp_category_ptr = tp_category.as_ptr();
1337        let tp_name = util::str_to_cstring(tp_name)?;
1338        let tp_name_ptr = tp_name.as_ptr();
1339
1340        let tp_opts = tp_opts.map(libbpf_sys::bpf_tracepoint_opts::from);
1341        let opts = tp_opts.as_ref().map_or(ptr::null(), |opts| opts);
1342        let ptr = unsafe {
1343            libbpf_sys::bpf_program__attach_tracepoint_opts(
1344                self.ptr.as_ptr(),
1345                tp_category_ptr,
1346                tp_name_ptr,
1347                opts as *const _,
1348            )
1349        };
1350
1351        let ptr = validate_bpf_ret(ptr).context("failed to attach tracepoint")?;
1352        // SAFETY: the pointer came from libbpf and has been checked for errors.
1353        let link = unsafe { Link::new(ptr) };
1354        Ok(link)
1355    }
1356
1357    /// Attach this program to a [kernel
1358    /// tracepoint](https://www.kernel.org/doc/html/latest/trace/tracepoints.html).
1359    pub fn attach_tracepoint(
1360        &self,
1361        tp_category: TracepointCategory,
1362        tp_name: impl AsRef<str>,
1363    ) -> Result<Link> {
1364        self.attach_tracepoint_impl(tp_category.as_ref(), tp_name.as_ref(), None)
1365    }
1366
1367    /// Attach this program to a [kernel
1368    /// tracepoint](https://www.kernel.org/doc/html/latest/trace/tracepoints.html),
1369    /// providing additional options.
1370    pub fn attach_tracepoint_with_opts(
1371        &self,
1372        tp_category: TracepointCategory,
1373        tp_name: impl AsRef<str>,
1374        tp_opts: TracepointOpts,
1375    ) -> Result<Link> {
1376        self.attach_tracepoint_impl(tp_category.as_ref(), tp_name.as_ref(), Some(tp_opts))
1377    }
1378
1379    /// Attach this program to a [raw kernel
1380    /// tracepoint](https://lwn.net/Articles/748352/).
1381    pub fn attach_raw_tracepoint<T: AsRef<str>>(&self, tp_name: T) -> Result<Link> {
1382        let tp_name = util::str_to_cstring(tp_name.as_ref())?;
1383        let tp_name_ptr = tp_name.as_ptr();
1384        let ptr = unsafe {
1385            libbpf_sys::bpf_program__attach_raw_tracepoint(self.ptr.as_ptr(), tp_name_ptr)
1386        };
1387        let ptr = validate_bpf_ret(ptr).context("failed to attach raw tracepoint")?;
1388        // SAFETY: the pointer came from libbpf and has been checked for errors.
1389        let link = unsafe { Link::new(ptr) };
1390        Ok(link)
1391    }
1392
1393    /// Attach this program to a [raw kernel
1394    /// tracepoint](https://lwn.net/Articles/748352/), providing additional
1395    /// options.
1396    pub fn attach_raw_tracepoint_with_opts<T: AsRef<str>>(
1397        &self,
1398        tp_name: T,
1399        tp_opts: RawTracepointOpts,
1400    ) -> Result<Link> {
1401        let tp_name = util::str_to_cstring(tp_name.as_ref())?;
1402        let tp_name_ptr = tp_name.as_ptr();
1403        let mut tp_opts = libbpf_sys::bpf_raw_tracepoint_opts::from(tp_opts);
1404        let ptr = unsafe {
1405            libbpf_sys::bpf_program__attach_raw_tracepoint_opts(
1406                self.ptr.as_ptr(),
1407                tp_name_ptr,
1408                &mut tp_opts as *mut _,
1409            )
1410        };
1411        let ptr = validate_bpf_ret(ptr).context("failed to attach raw tracepoint")?;
1412        // SAFETY: the pointer came from libbpf and has been checked for errors.
1413        let link = unsafe { Link::new(ptr) };
1414        Ok(link)
1415    }
1416
1417    /// Attach to an [LSM](https://en.wikipedia.org/wiki/Linux_Security_Modules) hook
1418    pub fn attach_lsm(&self) -> Result<Link> {
1419        let ptr = unsafe { libbpf_sys::bpf_program__attach_lsm(self.ptr.as_ptr()) };
1420        let ptr = validate_bpf_ret(ptr).context("failed to attach LSM")?;
1421        // SAFETY: the pointer came from libbpf and has been checked for errors.
1422        let link = unsafe { Link::new(ptr) };
1423        Ok(link)
1424    }
1425
1426    /// Attach to a [fentry/fexit kernel probe](https://lwn.net/Articles/801479/)
1427    pub fn attach_trace(&self) -> Result<Link> {
1428        let ptr = unsafe { libbpf_sys::bpf_program__attach_trace(self.ptr.as_ptr()) };
1429        let ptr = validate_bpf_ret(ptr).context("failed to attach fentry/fexit kernel probe")?;
1430        // SAFETY: the pointer came from libbpf and has been checked for errors.
1431        let link = unsafe { Link::new(ptr) };
1432        Ok(link)
1433    }
1434
1435    /// Attach a verdict/parser to a [sockmap/sockhash](https://lwn.net/Articles/731133/)
1436    pub fn attach_sockmap(&self, map_fd: i32) -> Result<()> {
1437        let err = unsafe {
1438            libbpf_sys::bpf_prog_attach(
1439                self.as_fd().as_raw_fd(),
1440                map_fd,
1441                self.attach_type() as u32,
1442                0,
1443            )
1444        };
1445        util::parse_ret(err)
1446    }
1447
1448    /// Attach this program to [XDP](https://lwn.net/Articles/825998/)
1449    pub fn attach_xdp(&self, ifindex: i32) -> Result<Link> {
1450        let ptr = unsafe { libbpf_sys::bpf_program__attach_xdp(self.ptr.as_ptr(), ifindex) };
1451        let ptr = validate_bpf_ret(ptr).context("failed to attach XDP program")?;
1452        // SAFETY: the pointer came from libbpf and has been checked for errors.
1453        let link = unsafe { Link::new(ptr) };
1454        Ok(link)
1455    }
1456
1457    /// Attach this program to [netns-based programs](https://lwn.net/Articles/819618/)
1458    pub fn attach_netns(&self, netns_fd: i32) -> Result<Link> {
1459        let ptr = unsafe { libbpf_sys::bpf_program__attach_netns(self.ptr.as_ptr(), netns_fd) };
1460        let ptr = validate_bpf_ret(ptr).context("failed to attach network namespace program")?;
1461        // SAFETY: the pointer came from libbpf and has been checked for errors.
1462        let link = unsafe { Link::new(ptr) };
1463        Ok(link)
1464    }
1465
1466    /// Attach this program to [netfilter programs](https://lwn.net/Articles/925082/)
1467    pub fn attach_netfilter_with_opts(
1468        &self,
1469        netfilter_opt: netfilter::NetfilterOpts,
1470    ) -> Result<Link> {
1471        let netfilter_opts = libbpf_sys::bpf_netfilter_opts::from(netfilter_opt);
1472
1473        let ptr = unsafe {
1474            libbpf_sys::bpf_program__attach_netfilter(
1475                self.ptr.as_ptr(),
1476                &netfilter_opts as *const _,
1477            )
1478        };
1479
1480        let ptr = validate_bpf_ret(ptr).context("failed to attach netfilter program")?;
1481        // SAFETY: the pointer came from libbpf and has been checked for errors.
1482        let link = unsafe { Link::new(ptr) };
1483        Ok(link)
1484    }
1485
1486    fn attach_usdt_impl(
1487        &self,
1488        pid: i32,
1489        binary_path: &Path,
1490        usdt_provider: &str,
1491        usdt_name: &str,
1492        usdt_opts: Option<UsdtOpts>,
1493    ) -> Result<Link> {
1494        let path = util::path_to_cstring(binary_path)?;
1495        let path_ptr = path.as_ptr();
1496        let usdt_provider = util::str_to_cstring(usdt_provider)?;
1497        let usdt_provider_ptr = usdt_provider.as_ptr();
1498        let usdt_name = util::str_to_cstring(usdt_name)?;
1499        let usdt_name_ptr = usdt_name.as_ptr();
1500        let usdt_opts = usdt_opts.map(libbpf_sys::bpf_usdt_opts::from);
1501        let usdt_opts_ptr = usdt_opts
1502            .as_ref()
1503            .map(|opts| opts as *const _)
1504            .unwrap_or_else(ptr::null);
1505
1506        let ptr = unsafe {
1507            libbpf_sys::bpf_program__attach_usdt(
1508                self.ptr.as_ptr(),
1509                pid,
1510                path_ptr,
1511                usdt_provider_ptr,
1512                usdt_name_ptr,
1513                usdt_opts_ptr,
1514            )
1515        };
1516        let ptr = validate_bpf_ret(ptr).context("failed to attach USDT")?;
1517        // SAFETY: the pointer came from libbpf and has been checked for errors.
1518        let link = unsafe { Link::new(ptr) };
1519        Ok(link)
1520    }
1521
1522    /// Attach this program to a [USDT](https://lwn.net/Articles/753601/) probe
1523    /// point. The entry point of the program must be defined with
1524    /// `SEC("usdt")`.
1525    pub fn attach_usdt(
1526        &self,
1527        pid: i32,
1528        binary_path: impl AsRef<Path>,
1529        usdt_provider: impl AsRef<str>,
1530        usdt_name: impl AsRef<str>,
1531    ) -> Result<Link> {
1532        self.attach_usdt_impl(
1533            pid,
1534            binary_path.as_ref(),
1535            usdt_provider.as_ref(),
1536            usdt_name.as_ref(),
1537            None,
1538        )
1539    }
1540
1541    /// Attach this program to a [USDT](https://lwn.net/Articles/753601/) probe
1542    /// point, providing additional options. The entry point of the program must
1543    /// be defined with `SEC("usdt")`.
1544    pub fn attach_usdt_with_opts(
1545        &self,
1546        pid: i32,
1547        binary_path: impl AsRef<Path>,
1548        usdt_provider: impl AsRef<str>,
1549        usdt_name: impl AsRef<str>,
1550        usdt_opts: UsdtOpts,
1551    ) -> Result<Link> {
1552        self.attach_usdt_impl(
1553            pid,
1554            binary_path.as_ref(),
1555            usdt_provider.as_ref(),
1556            usdt_name.as_ref(),
1557            Some(usdt_opts),
1558        )
1559    }
1560
1561    /// Attach this program to a
1562    /// [BPF Iterator](https://www.kernel.org/doc/html/latest/bpf/bpf_iterators.html).
1563    /// The entry point of the program must be defined with `SEC("iter")` or `SEC("iter.s")`.
1564    pub fn attach_iter(&self, map_fd: BorrowedFd<'_>) -> Result<Link> {
1565        let map_opts = MapIterOpts {
1566            fd: map_fd,
1567            _non_exhaustive: (),
1568        };
1569        self.attach_iter_with_opts(IterOpts::Map(map_opts))
1570    }
1571
1572    /// Attach this program to a
1573    /// [BPF Iterator](https://www.kernel.org/doc/html/latest/bpf/bpf_iterators.html),
1574    /// providing additional options.
1575    ///
1576    /// The entry point of the program must be defined with `SEC("iter")` or `SEC("iter.s")`.
1577    pub fn attach_iter_with_opts(&self, opts: IterOpts<'_>) -> Result<Link> {
1578        let mut linkinfo = libbpf_sys::bpf_iter_link_info::from(opts);
1579        let attach_opt = libbpf_sys::bpf_iter_attach_opts {
1580            link_info: &raw mut linkinfo,
1581            link_info_len: size_of::<libbpf_sys::bpf_iter_link_info>() as _,
1582            sz: size_of::<libbpf_sys::bpf_iter_attach_opts>() as _,
1583            ..Default::default()
1584        };
1585        let ptr = unsafe {
1586            libbpf_sys::bpf_program__attach_iter(
1587                self.ptr.as_ptr(),
1588                &attach_opt as *const libbpf_sys::bpf_iter_attach_opts,
1589            )
1590        };
1591
1592        let ptr = validate_bpf_ret(ptr).context("failed to attach iterator")?;
1593        // SAFETY: the pointer came from libbpf and has been checked for errors.
1594        let link = unsafe { Link::new(ptr) };
1595        Ok(link)
1596    }
1597
1598    /// Test run the program with the given input data.
1599    ///
1600    /// This function uses the
1601    /// [BPF_PROG_RUN](https://www.kernel.org/doc/html/latest/bpf/bpf_prog_run.html)
1602    /// facility.
1603    pub fn test_run<'dat>(&self, input: Input<'dat>) -> Result<Output<'dat>> {
1604        unsafe fn slice_from_array<'t, T>(items: *mut T, num_items: usize) -> Option<&'t mut [T]> {
1605            if items.is_null() {
1606                None
1607            } else {
1608                Some(unsafe { slice::from_raw_parts_mut(items, num_items) })
1609            }
1610        }
1611
1612        let Input {
1613            context_in,
1614            mut context_out,
1615            data_in,
1616            mut data_out,
1617            cpu,
1618            flags,
1619            repeat,
1620            _non_exhaustive: (),
1621        } = input;
1622
1623        let mut opts = unsafe { mem::zeroed::<libbpf_sys::bpf_test_run_opts>() };
1624        opts.sz = size_of_val(&opts) as _;
1625        opts.ctx_in = context_in
1626            .as_ref()
1627            .map(|data| data.as_ptr().cast())
1628            .unwrap_or_else(ptr::null);
1629        opts.ctx_size_in = context_in.map(|data| data.len() as _).unwrap_or(0);
1630        opts.ctx_out = context_out
1631            .as_mut()
1632            .map(|data| data.as_mut_ptr().cast())
1633            .unwrap_or_else(ptr::null_mut);
1634        opts.ctx_size_out = context_out.map(|data| data.len() as _).unwrap_or(0);
1635        opts.data_in = data_in
1636            .map(|data| data.as_ptr().cast())
1637            .unwrap_or_else(ptr::null);
1638        opts.data_size_in = data_in.map(|data| data.len() as _).unwrap_or(0);
1639        opts.data_out = data_out
1640            .as_mut()
1641            .map(|data| data.as_mut_ptr().cast())
1642            .unwrap_or_else(ptr::null_mut);
1643        opts.data_size_out = data_out.map(|data| data.len() as _).unwrap_or(0);
1644        opts.cpu = cpu;
1645        opts.flags = flags;
1646        // 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
1647        // the kernel user api uses __u32: https://elixir.bootlin.com/linux/v6.2.11/source/include/uapi/linux/bpf.h#L1430
1648        opts.repeat = repeat as i32;
1649
1650        let rc = unsafe { libbpf_sys::bpf_prog_test_run_opts(self.as_fd().as_raw_fd(), &mut opts) };
1651        let () = util::parse_ret(rc)?;
1652        let output = Output {
1653            return_value: opts.retval,
1654            context: unsafe { slice_from_array(opts.ctx_out.cast(), opts.ctx_size_out as _) },
1655            data: unsafe { slice_from_array(opts.data_out.cast(), opts.data_size_out as _) },
1656            duration: Duration::from_nanos(opts.duration.into()),
1657            _non_exhaustive: (),
1658        };
1659        Ok(output)
1660    }
1661
1662    /// Get the stdout BPF stream of the program.
1663    pub fn stdout(&self) -> impl Read + '_ {
1664        Stream::new(self.as_fd(), Stream::BPF_STDOUT)
1665    }
1666
1667    /// Get the stderr BPF stream of the program.
1668    pub fn stderr(&self) -> impl Read + '_ {
1669        Stream::new(self.as_fd(), Stream::BPF_STDERR)
1670    }
1671}
1672
1673impl<'obj> Deref for ProgramMut<'obj> {
1674    type Target = Program<'obj>;
1675
1676    fn deref(&self) -> &Self::Target {
1677        // SAFETY: `ProgramImpl` is `repr(transparent)` and so in-memory
1678        //         representation of both types is the same.
1679        unsafe { transmute::<&ProgramMut<'obj>, &Program<'obj>>(self) }
1680    }
1681}
1682
1683impl<T> AsFd for ProgramImpl<'_, T> {
1684    fn as_fd(&self) -> BorrowedFd<'_> {
1685        let fd = unsafe { libbpf_sys::bpf_program__fd(self.ptr.as_ptr()) };
1686        unsafe { BorrowedFd::borrow_raw(fd) }
1687    }
1688}
1689
1690impl<T> AsRawLibbpf for ProgramImpl<'_, T> {
1691    type LibbpfType = libbpf_sys::bpf_program;
1692
1693    /// Retrieve the underlying [`libbpf_sys::bpf_program`].
1694    fn as_libbpf_object(&self) -> NonNull<Self::LibbpfType> {
1695        self.ptr
1696    }
1697}
1698
1699#[cfg(test)]
1700mod tests {
1701    use super::*;
1702
1703    use std::mem::discriminant;
1704
1705    #[test]
1706    fn program_type() {
1707        use ProgramType::*;
1708
1709        for t in [
1710            Unspec,
1711            SocketFilter,
1712            Kprobe,
1713            SchedCls,
1714            SchedAct,
1715            Tracepoint,
1716            Xdp,
1717            PerfEvent,
1718            CgroupSkb,
1719            CgroupSock,
1720            LwtIn,
1721            LwtOut,
1722            LwtXmit,
1723            SockOps,
1724            SkSkb,
1725            CgroupDevice,
1726            SkMsg,
1727            RawTracepoint,
1728            CgroupSockAddr,
1729            LwtSeg6local,
1730            LircMode2,
1731            SkReuseport,
1732            FlowDissector,
1733            CgroupSysctl,
1734            RawTracepointWritable,
1735            CgroupSockopt,
1736            Tracing,
1737            StructOps,
1738            Ext,
1739            Lsm,
1740            SkLookup,
1741            Syscall,
1742            Unknown,
1743        ] {
1744            // check if discriminants match after a roundtrip conversion
1745            assert_eq!(discriminant(&t), discriminant(&ProgramType::from(t as u32)));
1746        }
1747    }
1748
1749    #[test]
1750    fn program_attach_type() {
1751        use ProgramAttachType::*;
1752
1753        for t in [
1754            CgroupInetIngress,
1755            CgroupInetEgress,
1756            CgroupInetSockCreate,
1757            CgroupSockOps,
1758            SkSkbStreamParser,
1759            SkSkbStreamVerdict,
1760            CgroupDevice,
1761            SkMsgVerdict,
1762            CgroupInet4Bind,
1763            CgroupInet6Bind,
1764            CgroupInet4Connect,
1765            CgroupInet6Connect,
1766            CgroupInet4PostBind,
1767            CgroupInet6PostBind,
1768            CgroupUdp4Sendmsg,
1769            CgroupUdp6Sendmsg,
1770            LircMode2,
1771            FlowDissector,
1772            CgroupSysctl,
1773            CgroupUdp4Recvmsg,
1774            CgroupUdp6Recvmsg,
1775            CgroupGetsockopt,
1776            CgroupSetsockopt,
1777            TraceRawTp,
1778            TraceFentry,
1779            TraceFexit,
1780            ModifyReturn,
1781            LsmMac,
1782            TraceIter,
1783            CgroupInet4Getpeername,
1784            CgroupInet6Getpeername,
1785            CgroupInet4Getsockname,
1786            CgroupInet6Getsockname,
1787            XdpDevmap,
1788            CgroupInetSockRelease,
1789            XdpCpumap,
1790            SkLookup,
1791            Xdp,
1792            SkSkbVerdict,
1793            SkReuseportSelect,
1794            SkReuseportSelectOrMigrate,
1795            PerfEvent,
1796            Unknown,
1797        ] {
1798            // check if discriminants match after a roundtrip conversion
1799            assert_eq!(
1800                discriminant(&t),
1801                discriminant(&ProgramAttachType::from(t as u32))
1802            );
1803        }
1804    }
1805}