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