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