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