Skip to main content

libbpf_rs/
program.rs

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