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