1#![allow(rustdoc::private_intra_doc_links)]
4
5use std::ffi::c_void;
6use std::ffi::CStr;
7use std::ffi::CString;
8use std::ffi::OsStr;
9use std::io::Read;
10use std::marker::PhantomData;
11use std::mem;
12use std::mem::size_of;
13use std::mem::size_of_val;
14use std::mem::transmute;
15use std::ops::Deref;
16use std::os::unix::ffi::OsStrExt as _;
17use std::os::unix::io::AsFd;
18use std::os::unix::io::AsRawFd;
19use std::os::unix::io::BorrowedFd;
20use std::os::unix::io::FromRawFd;
21use std::os::unix::io::OwnedFd;
22use std::path::Path;
23use std::ptr;
24use std::ptr::NonNull;
25use std::slice;
26use std::time::Duration;
27
28use libbpf_sys::bpf_func_id;
29
30use crate::netfilter;
31use crate::streams::Stream;
32use crate::util;
33use crate::util::validate_bpf_ret;
34use crate::util::BpfObjectType;
35use crate::AsRawLibbpf;
36use crate::Error;
37use crate::ErrorExt as _;
38use crate::Link;
39use crate::Mut;
40use crate::RawTracepointOpts;
41use crate::Result;
42use crate::TracepointCategory;
43use crate::TracepointOpts;
44
45#[derive(Clone, Debug, Default)]
47pub struct UprobeOpts {
48 pub ref_ctr_offset: usize,
50 pub cookie: u64,
52 pub retprobe: bool,
54 pub func_name: Option<String>,
66 #[doc(hidden)]
67 pub _non_exhaustive: (),
68}
69
70#[derive(Clone, Debug, Default)]
72pub struct UprobeMultiOpts {
73 pub syms: Vec<String>,
75 pub offsets: Vec<usize>,
77 pub ref_ctr_offsets: Vec<usize>,
79 pub cookies: Vec<u64>,
81 pub retprobe: bool,
83 pub session: bool,
85 #[doc(hidden)]
86 pub _non_exhaustive: (),
87}
88
89#[derive(Clone, Debug, Default)]
91pub struct UsdtOpts {
92 pub cookie: u64,
94 #[doc(hidden)]
95 pub _non_exhaustive: (),
96}
97
98impl From<UsdtOpts> for libbpf_sys::bpf_usdt_opts {
99 fn from(opts: UsdtOpts) -> Self {
100 let UsdtOpts {
101 cookie,
102 _non_exhaustive,
103 } = opts;
104 #[allow(clippy::needless_update)]
105 Self {
106 sz: size_of::<Self>() as _,
107 usdt_cookie: cookie,
108 ..Default::default()
110 }
111 }
112}
113
114#[derive(Clone, Debug, Default)]
116pub struct KprobeOpts {
117 pub cookie: u64,
119 #[doc(hidden)]
120 pub _non_exhaustive: (),
121}
122
123impl From<KprobeOpts> for libbpf_sys::bpf_kprobe_opts {
124 fn from(opts: KprobeOpts) -> Self {
125 let KprobeOpts {
126 cookie,
127 _non_exhaustive,
128 } = opts;
129
130 #[allow(clippy::needless_update)]
131 Self {
132 sz: size_of::<Self>() as _,
133 bpf_cookie: cookie,
134 ..Default::default()
136 }
137 }
138}
139
140#[derive(Clone, Debug, Default)]
142pub struct KprobeMultiOpts {
143 pub symbols: Vec<String>,
145 pub cookies: Vec<u64>,
147 pub retprobe: bool,
149 #[doc(hidden)]
150 pub _non_exhaustive: (),
151}
152
153#[derive(Clone, Debug, Default)]
155pub struct PerfEventOpts {
156 pub cookie: u64,
158 pub force_ioctl_attach: bool,
160 #[doc(hidden)]
161 pub _non_exhaustive: (),
162}
163
164impl From<PerfEventOpts> for libbpf_sys::bpf_perf_event_opts {
165 fn from(opts: PerfEventOpts) -> Self {
166 let PerfEventOpts {
167 cookie,
168 force_ioctl_attach,
169 _non_exhaustive,
170 } = opts;
171
172 #[allow(clippy::needless_update)]
173 Self {
174 sz: size_of::<Self>() as _,
175 bpf_cookie: cookie,
176 force_ioctl_attach,
177 ..Default::default()
179 }
180 }
181}
182
183
184#[derive(Clone, Debug)]
186pub struct MapIterOpts<'fd> {
187 pub fd: BorrowedFd<'fd>,
189 #[doc(hidden)]
190 pub _non_exhaustive: (),
191}
192
193impl<'fd> MapIterOpts<'fd> {
194 pub fn from_fd(fd: BorrowedFd<'fd>) -> Self {
196 Self {
197 fd,
198 _non_exhaustive: (),
199 }
200 }
201}
202
203
204#[non_exhaustive]
206#[repr(u32)]
207#[derive(Clone, Debug, Default)]
208pub enum CgroupIterOrder {
209 #[default]
211 Default = libbpf_sys::BPF_CGROUP_ITER_ORDER_UNSPEC,
212 SelfOnly = libbpf_sys::BPF_CGROUP_ITER_SELF_ONLY,
214 DescendantsPre = libbpf_sys::BPF_CGROUP_ITER_DESCENDANTS_PRE,
216 DescendantsPost = libbpf_sys::BPF_CGROUP_ITER_DESCENDANTS_POST,
218 AncestorsUp = libbpf_sys::BPF_CGROUP_ITER_ANCESTORS_UP,
220}
221
222#[derive(Clone, Debug)]
224pub struct CgroupIterOpts<'fd> {
225 pub fd: BorrowedFd<'fd>,
227 pub order: CgroupIterOrder,
229 #[doc(hidden)]
230 pub _non_exhaustive: (),
231}
232
233impl<'fd> CgroupIterOpts<'fd> {
234 pub fn from_fd(fd: BorrowedFd<'fd>) -> Self {
236 Self {
237 fd,
238 order: CgroupIterOrder::default(),
239 _non_exhaustive: (),
240 }
241 }
242}
243
244
245#[non_exhaustive]
247#[derive(Clone, Debug)]
248pub enum IterOpts<'fd> {
249 Map(MapIterOpts<'fd>),
251 Cgroup(CgroupIterOpts<'fd>),
253}
254
255impl From<IterOpts<'_>> for libbpf_sys::bpf_iter_link_info {
256 fn from(opts: IterOpts) -> Self {
257 let mut linkinfo = Self::default();
258 match opts {
259 IterOpts::Map(map_opts) => {
260 let MapIterOpts {
261 fd,
262 _non_exhaustive: (),
263 } = map_opts;
264
265 linkinfo.map.map_fd = fd.as_raw_fd() as _;
266 }
267 IterOpts::Cgroup(cgroup_opts) => {
268 let CgroupIterOpts {
269 fd,
270 order,
271 _non_exhaustive: (),
272 } = cgroup_opts;
273
274 linkinfo.cgroup.cgroup_fd = fd.as_raw_fd() as _;
275 linkinfo.cgroup.order = order as libbpf_sys::bpf_cgroup_iter_order;
276 }
277 };
278 linkinfo
279 }
280}
281
282
283pub type OpenProgram<'obj> = OpenProgramImpl<'obj>;
285pub type OpenProgramMut<'obj> = OpenProgramImpl<'obj, Mut>;
287
288
289#[derive(Debug)]
293#[repr(transparent)]
294pub struct OpenProgramImpl<'obj, T = ()> {
295 ptr: NonNull<libbpf_sys::bpf_program>,
296 _phantom: PhantomData<&'obj T>,
297}
298
299impl<'obj> OpenProgram<'obj> {
300 pub fn new(prog: &'obj libbpf_sys::bpf_program) -> Self {
302 Self {
305 ptr: unsafe { NonNull::new_unchecked(prog as *const _ as *mut _) },
306 _phantom: PhantomData,
307 }
308 }
309
310 pub fn prog_type(&self) -> ProgramType {
312 ProgramType::from(unsafe { libbpf_sys::bpf_program__type(self.ptr.as_ptr()) })
313 }
314
315 pub fn name(&self) -> &'obj OsStr {
317 let name_ptr = unsafe { libbpf_sys::bpf_program__name(self.ptr.as_ptr()) };
318 let name_c_str = unsafe { CStr::from_ptr(name_ptr) };
319 OsStr::from_bytes(name_c_str.to_bytes())
321 }
322
323 pub fn section(&self) -> &'obj OsStr {
325 let p = unsafe { libbpf_sys::bpf_program__section_name(self.ptr.as_ptr()) };
327 let section_c_str = unsafe { CStr::from_ptr(p) };
330 let section = OsStr::from_bytes(section_c_str.to_bytes());
331 section
332 }
333
334 pub fn insn_cnt(&self) -> usize {
340 unsafe { libbpf_sys::bpf_program__insn_cnt(self.ptr.as_ptr()) as usize }
341 }
342
343 pub fn insns(&self) -> &'obj [libbpf_sys::bpf_insn] {
353 let count = self.insn_cnt();
354 let ptr = unsafe { libbpf_sys::bpf_program__insns(self.ptr.as_ptr()) };
355 unsafe { slice::from_raw_parts(ptr, count) }
356 }
357
358 pub fn autoload(&self) -> bool {
360 unsafe { libbpf_sys::bpf_program__autoload(self.ptr.as_ptr()) }
361 }
362}
363
364impl<'obj> OpenProgramMut<'obj> {
365 pub fn new_mut(prog: &'obj mut libbpf_sys::bpf_program) -> Self {
367 Self {
368 ptr: unsafe { NonNull::new_unchecked(prog as *mut _) },
369 _phantom: PhantomData,
370 }
371 }
372
373 pub fn set_prog_type(&mut self, prog_type: ProgramType) {
375 let rc = unsafe { libbpf_sys::bpf_program__set_type(self.ptr.as_ptr(), prog_type as u32) };
376 debug_assert!(util::parse_ret(rc).is_ok(), "{rc}");
377 }
378
379 pub fn set_attach_type(&mut self, attach_type: ProgramAttachType) {
381 let rc = unsafe {
382 libbpf_sys::bpf_program__set_expected_attach_type(self.ptr.as_ptr(), attach_type as u32)
383 };
384 debug_assert!(util::parse_ret(rc).is_ok(), "{rc}");
385 }
386
387 pub fn set_ifindex(&mut self, idx: u32) {
391 unsafe { libbpf_sys::bpf_program__set_ifindex(self.ptr.as_ptr(), idx) }
392 }
393
394 pub fn set_log_level(&mut self, log_level: u32) {
403 let rc = unsafe { libbpf_sys::bpf_program__set_log_level(self.ptr.as_ptr(), log_level) };
404 debug_assert!(util::parse_ret(rc).is_ok(), "{rc}");
405 }
406
407 pub fn set_autoload(&mut self, autoload: bool) {
410 let rc = unsafe { libbpf_sys::bpf_program__set_autoload(self.ptr.as_ptr(), autoload) };
411 debug_assert!(util::parse_ret(rc).is_ok(), "{rc}");
412 }
413
414 pub fn set_autoattach(&mut self, autoattach: bool) {
417 unsafe { libbpf_sys::bpf_program__set_autoattach(self.ptr.as_ptr(), autoattach) };
418 }
419
420 #[expect(missing_docs)]
421 pub fn set_attach_target(
422 &mut self,
423 attach_prog_fd: i32,
424 attach_func_name: Option<String>,
425 ) -> Result<()> {
426 let name_c = if let Some(name) = attach_func_name {
427 Some(util::str_to_cstring(&name)?)
428 } else {
429 None
430 };
431 let name_ptr = name_c.as_ref().map_or(ptr::null(), |name| name.as_ptr());
432 let ret = unsafe {
433 libbpf_sys::bpf_program__set_attach_target(self.ptr.as_ptr(), attach_prog_fd, name_ptr)
434 };
435 util::parse_ret(ret)
436 }
437
438 pub fn set_flags(&mut self, flags: u32) {
440 let rc = unsafe { libbpf_sys::bpf_program__set_flags(self.ptr.as_ptr(), flags) };
441 debug_assert!(util::parse_ret(rc).is_ok(), "{rc}");
442 }
443}
444
445impl<'obj> Deref for OpenProgramMut<'obj> {
446 type Target = OpenProgram<'obj>;
447
448 fn deref(&self) -> &Self::Target {
449 unsafe { transmute::<&OpenProgramMut<'obj>, &OpenProgram<'obj>>(self) }
452 }
453}
454
455impl<T> AsRawLibbpf for OpenProgramImpl<'_, T> {
456 type LibbpfType = libbpf_sys::bpf_program;
457
458 fn as_libbpf_object(&self) -> NonNull<Self::LibbpfType> {
460 self.ptr
461 }
462}
463
464#[non_exhaustive]
466#[repr(u32)]
467#[derive(Copy, Clone, PartialEq, Eq, Debug)]
468#[expect(missing_docs)]
470pub enum ProgramType {
471 Unspec = 0,
472 SocketFilter = libbpf_sys::BPF_PROG_TYPE_SOCKET_FILTER,
473 Kprobe = libbpf_sys::BPF_PROG_TYPE_KPROBE,
474 SchedCls = libbpf_sys::BPF_PROG_TYPE_SCHED_CLS,
475 SchedAct = libbpf_sys::BPF_PROG_TYPE_SCHED_ACT,
476 Tracepoint = libbpf_sys::BPF_PROG_TYPE_TRACEPOINT,
477 Xdp = libbpf_sys::BPF_PROG_TYPE_XDP,
478 PerfEvent = libbpf_sys::BPF_PROG_TYPE_PERF_EVENT,
479 CgroupSkb = libbpf_sys::BPF_PROG_TYPE_CGROUP_SKB,
480 CgroupSock = libbpf_sys::BPF_PROG_TYPE_CGROUP_SOCK,
481 LwtIn = libbpf_sys::BPF_PROG_TYPE_LWT_IN,
482 LwtOut = libbpf_sys::BPF_PROG_TYPE_LWT_OUT,
483 LwtXmit = libbpf_sys::BPF_PROG_TYPE_LWT_XMIT,
484 SockOps = libbpf_sys::BPF_PROG_TYPE_SOCK_OPS,
485 SkSkb = libbpf_sys::BPF_PROG_TYPE_SK_SKB,
486 CgroupDevice = libbpf_sys::BPF_PROG_TYPE_CGROUP_DEVICE,
487 SkMsg = libbpf_sys::BPF_PROG_TYPE_SK_MSG,
488 RawTracepoint = libbpf_sys::BPF_PROG_TYPE_RAW_TRACEPOINT,
489 CgroupSockAddr = libbpf_sys::BPF_PROG_TYPE_CGROUP_SOCK_ADDR,
490 LwtSeg6local = libbpf_sys::BPF_PROG_TYPE_LWT_SEG6LOCAL,
491 LircMode2 = libbpf_sys::BPF_PROG_TYPE_LIRC_MODE2,
492 SkReuseport = libbpf_sys::BPF_PROG_TYPE_SK_REUSEPORT,
493 FlowDissector = libbpf_sys::BPF_PROG_TYPE_FLOW_DISSECTOR,
494 CgroupSysctl = libbpf_sys::BPF_PROG_TYPE_CGROUP_SYSCTL,
495 RawTracepointWritable = libbpf_sys::BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE,
496 CgroupSockopt = libbpf_sys::BPF_PROG_TYPE_CGROUP_SOCKOPT,
497 Tracing = libbpf_sys::BPF_PROG_TYPE_TRACING,
498 StructOps = libbpf_sys::BPF_PROG_TYPE_STRUCT_OPS,
499 Ext = libbpf_sys::BPF_PROG_TYPE_EXT,
500 Lsm = libbpf_sys::BPF_PROG_TYPE_LSM,
501 SkLookup = libbpf_sys::BPF_PROG_TYPE_SK_LOOKUP,
502 Syscall = libbpf_sys::BPF_PROG_TYPE_SYSCALL,
503 Netfilter = libbpf_sys::BPF_PROG_TYPE_NETFILTER,
504 Unknown = u32::MAX,
506}
507
508impl ProgramType {
509 pub fn is_supported(&self) -> Result<bool> {
514 let ret = unsafe { libbpf_sys::libbpf_probe_bpf_prog_type(*self as u32, ptr::null()) };
515 match ret {
516 0 => Ok(false),
517 1 => Ok(true),
518 _ => Err(Error::from_raw_os_error(-ret)),
519 }
520 }
521
522 pub fn is_helper_supported(&self, helper_id: bpf_func_id) -> Result<bool> {
528 let ret =
529 unsafe { libbpf_sys::libbpf_probe_bpf_helper(*self as u32, helper_id, ptr::null()) };
530 match ret {
531 0 => Ok(false),
532 1 => Ok(true),
533 _ => Err(Error::from_raw_os_error(-ret)),
534 }
535 }
536}
537
538impl From<u32> for ProgramType {
539 fn from(value: u32) -> Self {
540 use ProgramType::*;
541
542 match value {
543 x if x == Unspec as u32 => Unspec,
544 x if x == SocketFilter as u32 => SocketFilter,
545 x if x == Kprobe as u32 => Kprobe,
546 x if x == SchedCls as u32 => SchedCls,
547 x if x == SchedAct as u32 => SchedAct,
548 x if x == Tracepoint as u32 => Tracepoint,
549 x if x == Xdp as u32 => Xdp,
550 x if x == PerfEvent as u32 => PerfEvent,
551 x if x == CgroupSkb as u32 => CgroupSkb,
552 x if x == CgroupSock as u32 => CgroupSock,
553 x if x == LwtIn as u32 => LwtIn,
554 x if x == LwtOut as u32 => LwtOut,
555 x if x == LwtXmit as u32 => LwtXmit,
556 x if x == SockOps as u32 => SockOps,
557 x if x == SkSkb as u32 => SkSkb,
558 x if x == CgroupDevice as u32 => CgroupDevice,
559 x if x == SkMsg as u32 => SkMsg,
560 x if x == RawTracepoint as u32 => RawTracepoint,
561 x if x == CgroupSockAddr as u32 => CgroupSockAddr,
562 x if x == LwtSeg6local as u32 => LwtSeg6local,
563 x if x == LircMode2 as u32 => LircMode2,
564 x if x == SkReuseport as u32 => SkReuseport,
565 x if x == FlowDissector as u32 => FlowDissector,
566 x if x == CgroupSysctl as u32 => CgroupSysctl,
567 x if x == RawTracepointWritable as u32 => RawTracepointWritable,
568 x if x == CgroupSockopt as u32 => CgroupSockopt,
569 x if x == Tracing as u32 => Tracing,
570 x if x == StructOps as u32 => StructOps,
571 x if x == Ext as u32 => Ext,
572 x if x == Lsm as u32 => Lsm,
573 x if x == SkLookup as u32 => SkLookup,
574 x if x == Syscall as u32 => Syscall,
575 x if x == Netfilter as u32 => Netfilter,
576 _ => Unknown,
577 }
578 }
579}
580
581#[non_exhaustive]
583#[repr(u32)]
584#[derive(Clone, Debug)]
585#[expect(missing_docs)]
587pub enum ProgramAttachType {
588 CgroupInetIngress = libbpf_sys::BPF_CGROUP_INET_INGRESS,
589 CgroupInetEgress = libbpf_sys::BPF_CGROUP_INET_EGRESS,
590 CgroupInetSockCreate = libbpf_sys::BPF_CGROUP_INET_SOCK_CREATE,
591 CgroupSockOps = libbpf_sys::BPF_CGROUP_SOCK_OPS,
592 SkSkbStreamParser = libbpf_sys::BPF_SK_SKB_STREAM_PARSER,
593 SkSkbStreamVerdict = libbpf_sys::BPF_SK_SKB_STREAM_VERDICT,
594 CgroupDevice = libbpf_sys::BPF_CGROUP_DEVICE,
595 SkMsgVerdict = libbpf_sys::BPF_SK_MSG_VERDICT,
596 CgroupInet4Bind = libbpf_sys::BPF_CGROUP_INET4_BIND,
597 CgroupInet6Bind = libbpf_sys::BPF_CGROUP_INET6_BIND,
598 CgroupInet4Connect = libbpf_sys::BPF_CGROUP_INET4_CONNECT,
599 CgroupInet6Connect = libbpf_sys::BPF_CGROUP_INET6_CONNECT,
600 CgroupInet4PostBind = libbpf_sys::BPF_CGROUP_INET4_POST_BIND,
601 CgroupInet6PostBind = libbpf_sys::BPF_CGROUP_INET6_POST_BIND,
602 CgroupUdp4Sendmsg = libbpf_sys::BPF_CGROUP_UDP4_SENDMSG,
603 CgroupUdp6Sendmsg = libbpf_sys::BPF_CGROUP_UDP6_SENDMSG,
604 LircMode2 = libbpf_sys::BPF_LIRC_MODE2,
605 FlowDissector = libbpf_sys::BPF_FLOW_DISSECTOR,
606 CgroupSysctl = libbpf_sys::BPF_CGROUP_SYSCTL,
607 CgroupUdp4Recvmsg = libbpf_sys::BPF_CGROUP_UDP4_RECVMSG,
608 CgroupUdp6Recvmsg = libbpf_sys::BPF_CGROUP_UDP6_RECVMSG,
609 CgroupGetsockopt = libbpf_sys::BPF_CGROUP_GETSOCKOPT,
610 CgroupSetsockopt = libbpf_sys::BPF_CGROUP_SETSOCKOPT,
611 TraceRawTp = libbpf_sys::BPF_TRACE_RAW_TP,
612 TraceFentry = libbpf_sys::BPF_TRACE_FENTRY,
613 TraceFexit = libbpf_sys::BPF_TRACE_FEXIT,
614 ModifyReturn = libbpf_sys::BPF_MODIFY_RETURN,
615 LsmMac = libbpf_sys::BPF_LSM_MAC,
616 TraceIter = libbpf_sys::BPF_TRACE_ITER,
617 CgroupInet4Getpeername = libbpf_sys::BPF_CGROUP_INET4_GETPEERNAME,
618 CgroupInet6Getpeername = libbpf_sys::BPF_CGROUP_INET6_GETPEERNAME,
619 CgroupInet4Getsockname = libbpf_sys::BPF_CGROUP_INET4_GETSOCKNAME,
620 CgroupInet6Getsockname = libbpf_sys::BPF_CGROUP_INET6_GETSOCKNAME,
621 XdpDevmap = libbpf_sys::BPF_XDP_DEVMAP,
622 CgroupInetSockRelease = libbpf_sys::BPF_CGROUP_INET_SOCK_RELEASE,
623 XdpCpumap = libbpf_sys::BPF_XDP_CPUMAP,
624 SkLookup = libbpf_sys::BPF_SK_LOOKUP,
625 Xdp = libbpf_sys::BPF_XDP,
626 SkSkbVerdict = libbpf_sys::BPF_SK_SKB_VERDICT,
627 SkReuseportSelect = libbpf_sys::BPF_SK_REUSEPORT_SELECT,
628 SkReuseportSelectOrMigrate = libbpf_sys::BPF_SK_REUSEPORT_SELECT_OR_MIGRATE,
629 PerfEvent = libbpf_sys::BPF_PERF_EVENT,
630 KprobeMulti = libbpf_sys::BPF_TRACE_KPROBE_MULTI,
631 NetkitPeer = libbpf_sys::BPF_NETKIT_PEER,
632 TraceUprobeMulti = libbpf_sys::BPF_TRACE_UPROBE_MULTI,
633 LsmCgroup = libbpf_sys::BPF_LSM_CGROUP,
634 TraceKprobeSession = libbpf_sys::BPF_TRACE_KPROBE_SESSION,
635 TcxIngress = libbpf_sys::BPF_TCX_INGRESS,
636 TcxEgress = libbpf_sys::BPF_TCX_EGRESS,
637 Netfilter = libbpf_sys::BPF_NETFILTER,
638 CgroupUnixGetsockname = libbpf_sys::BPF_CGROUP_UNIX_GETSOCKNAME,
639 CgroupUnixSendmsg = libbpf_sys::BPF_CGROUP_UNIX_SENDMSG,
640 NetkitPrimary = libbpf_sys::BPF_NETKIT_PRIMARY,
641 CgroupUnixRecvmsg = libbpf_sys::BPF_CGROUP_UNIX_RECVMSG,
642 CgroupUnixConnect = libbpf_sys::BPF_CGROUP_UNIX_CONNECT,
643 CgroupUnixGetpeername = libbpf_sys::BPF_CGROUP_UNIX_GETPEERNAME,
644 StructOps = libbpf_sys::BPF_STRUCT_OPS,
645 Unknown = u32::MAX,
647}
648
649impl From<u32> for ProgramAttachType {
650 fn from(value: u32) -> Self {
651 use ProgramAttachType::*;
652
653 match value {
654 x if x == CgroupInetIngress as u32 => CgroupInetIngress,
655 x if x == CgroupInetEgress as u32 => CgroupInetEgress,
656 x if x == CgroupInetSockCreate as u32 => CgroupInetSockCreate,
657 x if x == CgroupSockOps as u32 => CgroupSockOps,
658 x if x == SkSkbStreamParser as u32 => SkSkbStreamParser,
659 x if x == SkSkbStreamVerdict as u32 => SkSkbStreamVerdict,
660 x if x == CgroupDevice as u32 => CgroupDevice,
661 x if x == SkMsgVerdict as u32 => SkMsgVerdict,
662 x if x == CgroupInet4Bind as u32 => CgroupInet4Bind,
663 x if x == CgroupInet6Bind as u32 => CgroupInet6Bind,
664 x if x == CgroupInet4Connect as u32 => CgroupInet4Connect,
665 x if x == CgroupInet6Connect as u32 => CgroupInet6Connect,
666 x if x == CgroupInet4PostBind as u32 => CgroupInet4PostBind,
667 x if x == CgroupInet6PostBind as u32 => CgroupInet6PostBind,
668 x if x == CgroupUdp4Sendmsg as u32 => CgroupUdp4Sendmsg,
669 x if x == CgroupUdp6Sendmsg as u32 => CgroupUdp6Sendmsg,
670 x if x == LircMode2 as u32 => LircMode2,
671 x if x == FlowDissector as u32 => FlowDissector,
672 x if x == CgroupSysctl as u32 => CgroupSysctl,
673 x if x == CgroupUdp4Recvmsg as u32 => CgroupUdp4Recvmsg,
674 x if x == CgroupUdp6Recvmsg as u32 => CgroupUdp6Recvmsg,
675 x if x == CgroupGetsockopt as u32 => CgroupGetsockopt,
676 x if x == CgroupSetsockopt as u32 => CgroupSetsockopt,
677 x if x == TraceRawTp as u32 => TraceRawTp,
678 x if x == TraceFentry as u32 => TraceFentry,
679 x if x == TraceFexit as u32 => TraceFexit,
680 x if x == ModifyReturn as u32 => ModifyReturn,
681 x if x == LsmMac as u32 => LsmMac,
682 x if x == TraceIter as u32 => TraceIter,
683 x if x == CgroupInet4Getpeername as u32 => CgroupInet4Getpeername,
684 x if x == CgroupInet6Getpeername as u32 => CgroupInet6Getpeername,
685 x if x == CgroupInet4Getsockname as u32 => CgroupInet4Getsockname,
686 x if x == CgroupInet6Getsockname as u32 => CgroupInet6Getsockname,
687 x if x == XdpDevmap as u32 => XdpDevmap,
688 x if x == CgroupInetSockRelease as u32 => CgroupInetSockRelease,
689 x if x == XdpCpumap as u32 => XdpCpumap,
690 x if x == SkLookup as u32 => SkLookup,
691 x if x == Xdp as u32 => Xdp,
692 x if x == SkSkbVerdict as u32 => SkSkbVerdict,
693 x if x == SkReuseportSelect as u32 => SkReuseportSelect,
694 x if x == SkReuseportSelectOrMigrate as u32 => SkReuseportSelectOrMigrate,
695 x if x == PerfEvent as u32 => PerfEvent,
696 x if x == KprobeMulti as u32 => KprobeMulti,
697 x if x == NetkitPeer as u32 => NetkitPeer,
698 x if x == TraceUprobeMulti as u32 => TraceUprobeMulti,
699 x if x == LsmCgroup as u32 => LsmCgroup,
700 x if x == TraceKprobeSession as u32 => TraceKprobeSession,
701 x if x == TcxIngress as u32 => TcxIngress,
702 x if x == TcxEgress as u32 => TcxEgress,
703 x if x == Netfilter as u32 => Netfilter,
704 x if x == CgroupUnixGetsockname as u32 => CgroupUnixGetsockname,
705 x if x == CgroupUnixSendmsg as u32 => CgroupUnixSendmsg,
706 x if x == NetkitPrimary as u32 => NetkitPrimary,
707 x if x == CgroupUnixRecvmsg as u32 => CgroupUnixRecvmsg,
708 x if x == CgroupUnixConnect as u32 => CgroupUnixConnect,
709 x if x == CgroupUnixGetpeername as u32 => CgroupUnixGetpeername,
710 x if x == StructOps as u32 => StructOps,
711 _ => Unknown,
712 }
713 }
714}
715
716#[derive(Debug, Default)]
721pub struct Input<'dat> {
722 pub context_in: Option<&'dat mut [u8]>,
726 pub context_out: Option<&'dat mut [u8]>,
728 pub data_in: Option<&'dat [u8]>,
730 pub data_out: Option<&'dat mut [u8]>,
732 pub cpu: u32,
734 pub flags: u32,
736 pub repeat: u32,
739 #[doc(hidden)]
741 pub _non_exhaustive: (),
742}
743
744#[derive(Debug)]
749pub struct Output<'dat> {
750 pub return_value: u32,
752 pub context: Option<&'dat mut [u8]>,
754 pub data: Option<&'dat mut [u8]>,
756 pub duration: Duration,
758 #[doc(hidden)]
760 pub _non_exhaustive: (),
761}
762
763pub type Program<'obj> = ProgramImpl<'obj>;
765pub type ProgramMut<'obj> = ProgramImpl<'obj, Mut>;
767
768#[derive(Debug)]
776#[repr(transparent)]
777pub struct ProgramImpl<'obj, T = ()> {
778 pub(crate) ptr: NonNull<libbpf_sys::bpf_program>,
779 _phantom: PhantomData<&'obj T>,
780}
781
782impl<'obj> Program<'obj> {
783 pub fn new(prog: &'obj libbpf_sys::bpf_program) -> Self {
785 Self {
788 ptr: unsafe { NonNull::new_unchecked(prog as *const _ as *mut _) },
789 _phantom: PhantomData,
790 }
791 }
792
793 pub fn name(&self) -> &'obj OsStr {
795 let name_ptr = unsafe { libbpf_sys::bpf_program__name(self.ptr.as_ptr()) };
796 let name_c_str = unsafe { CStr::from_ptr(name_ptr) };
797 OsStr::from_bytes(name_c_str.to_bytes())
799 }
800
801 pub fn section(&self) -> &'obj OsStr {
803 let p = unsafe { libbpf_sys::bpf_program__section_name(self.ptr.as_ptr()) };
805 let section_c_str = unsafe { CStr::from_ptr(p) };
808 let section = OsStr::from_bytes(section_c_str.to_bytes());
809 section
810 }
811
812 pub fn prog_type(&self) -> ProgramType {
814 ProgramType::from(unsafe { libbpf_sys::bpf_program__type(self.ptr.as_ptr()) })
815 }
816
817 #[deprecated = "renamed to Program::fd_from_id"]
818 #[expect(missing_docs)]
819 #[inline]
820 pub fn get_fd_by_id(id: u32) -> Result<OwnedFd> {
821 Self::fd_from_id(id)
822 }
823
824 pub fn fd_from_id(id: u32) -> Result<OwnedFd> {
826 let ret = unsafe { libbpf_sys::bpf_prog_get_fd_by_id(id) };
827 let fd = util::parse_ret_i32(ret)?;
828 Ok(unsafe { OwnedFd::from_raw_fd(fd) })
832 }
833
834 pub fn id_from_fd(fd: BorrowedFd<'_>) -> Result<u32> {
836 let mut prog_info = libbpf_sys::bpf_prog_info::default();
837 let prog_info_ptr: *mut libbpf_sys::bpf_prog_info = &mut prog_info;
838 let mut len = size_of::<libbpf_sys::bpf_prog_info>() as u32;
839 let ret = unsafe {
840 libbpf_sys::bpf_obj_get_info_by_fd(
841 fd.as_raw_fd(),
842 prog_info_ptr as *mut c_void,
843 &mut len,
844 )
845 };
846 util::parse_ret(ret)?;
847 Ok(prog_info.id)
848 }
849
850 pub fn fd_from_pinned_path<P: AsRef<Path>>(path: P) -> Result<OwnedFd> {
854 let path_c = util::path_to_cstring(&path)?;
855 let path_ptr = path_c.as_ptr();
856
857 let fd = unsafe { libbpf_sys::bpf_obj_get(path_ptr) };
858 let fd = util::parse_ret_i32(fd).with_context(|| {
859 format!(
860 "failed to retrieve BPF object from pinned path `{}`",
861 path.as_ref().display()
862 )
863 })?;
864 let fd = unsafe { OwnedFd::from_raw_fd(fd) };
865
866 let fd_type = util::object_type_from_fd(fd.as_fd())?;
870 match fd_type {
871 BpfObjectType::Program => Ok(fd),
872 other => Err(Error::with_invalid_data(format!(
873 "retrieved BPF fd is not a program fd: {other:#?}"
874 ))),
875 }
876 }
877
878 pub fn flags(&self) -> u32 {
880 unsafe { libbpf_sys::bpf_program__flags(self.ptr.as_ptr()) }
881 }
882
883 pub fn attach_type(&self) -> ProgramAttachType {
885 ProgramAttachType::from(unsafe {
886 libbpf_sys::bpf_program__expected_attach_type(self.ptr.as_ptr())
887 })
888 }
889
890 pub fn autoload(&self) -> bool {
892 unsafe { libbpf_sys::bpf_program__autoload(self.ptr.as_ptr()) }
893 }
894
895 pub fn log_level(&self) -> u32 {
897 unsafe { libbpf_sys::bpf_program__log_level(self.ptr.as_ptr()) }
898 }
899
900 pub fn insn_cnt(&self) -> usize {
904 unsafe { libbpf_sys::bpf_program__insn_cnt(self.ptr.as_ptr()) as usize }
905 }
906
907 pub fn insns(&self) -> &'obj [libbpf_sys::bpf_insn] {
911 let count = self.insn_cnt();
912 let ptr = unsafe { libbpf_sys::bpf_program__insns(self.ptr.as_ptr()) };
913 unsafe { slice::from_raw_parts(ptr, count) }
914 }
915}
916
917impl<'obj> ProgramMut<'obj> {
918 pub fn new_mut(prog: &'obj mut libbpf_sys::bpf_program) -> Self {
920 Self {
921 ptr: unsafe { NonNull::new_unchecked(prog as *mut _) },
922 _phantom: PhantomData,
923 }
924 }
925
926 pub fn pin<P: AsRef<Path>>(&mut self, path: P) -> Result<()> {
929 let path_c = util::path_to_cstring(path)?;
930 let path_ptr = path_c.as_ptr();
931
932 let ret = unsafe { libbpf_sys::bpf_program__pin(self.ptr.as_ptr(), path_ptr) };
933 util::parse_ret(ret)
934 }
935
936 pub fn unpin<P: AsRef<Path>>(&mut self, path: P) -> Result<()> {
939 let path_c = util::path_to_cstring(path)?;
940 let path_ptr = path_c.as_ptr();
941
942 let ret = unsafe { libbpf_sys::bpf_program__unpin(self.ptr.as_ptr(), path_ptr) };
943 util::parse_ret(ret)
944 }
945
946 pub fn attach(&self) -> Result<Link> {
948 let ptr = unsafe { libbpf_sys::bpf_program__attach(self.ptr.as_ptr()) };
949 let ptr = validate_bpf_ret(ptr).context("failed to attach BPF program")?;
950 let link = unsafe { Link::new(ptr) };
952 Ok(link)
953 }
954
955 pub fn attach_cgroup(&self, cgroup_fd: i32) -> Result<Link> {
958 let ptr = unsafe { libbpf_sys::bpf_program__attach_cgroup(self.ptr.as_ptr(), cgroup_fd) };
959 let ptr = validate_bpf_ret(ptr).context("failed to attach cgroup")?;
960 let link = unsafe { Link::new(ptr) };
962 Ok(link)
963 }
964
965 pub fn attach_perf_event(&self, pfd: i32) -> Result<Link> {
967 let ptr = unsafe { libbpf_sys::bpf_program__attach_perf_event(self.ptr.as_ptr(), pfd) };
968 let ptr = validate_bpf_ret(ptr).context("failed to attach perf event")?;
969 let link = unsafe { Link::new(ptr) };
971 Ok(link)
972 }
973
974 pub fn attach_perf_event_with_opts(&self, pfd: i32, opts: PerfEventOpts) -> Result<Link> {
977 let libbpf_opts = libbpf_sys::bpf_perf_event_opts::from(opts);
978 let ptr = unsafe {
979 libbpf_sys::bpf_program__attach_perf_event_opts(self.ptr.as_ptr(), pfd, &libbpf_opts)
980 };
981 let ptr = validate_bpf_ret(ptr).context("failed to attach perf event")?;
982 let link = unsafe { Link::new(ptr) };
984 Ok(link)
985 }
986
987 pub fn attach_uprobe<T: AsRef<Path>>(
990 &self,
991 retprobe: bool,
992 pid: i32,
993 binary_path: T,
994 func_offset: usize,
995 ) -> Result<Link> {
996 let path = util::path_to_cstring(binary_path)?;
997 let path_ptr = path.as_ptr();
998 let ptr = unsafe {
999 libbpf_sys::bpf_program__attach_uprobe(
1000 self.ptr.as_ptr(),
1001 retprobe,
1002 pid,
1003 path_ptr,
1004 func_offset as libbpf_sys::size_t,
1005 )
1006 };
1007 let ptr = validate_bpf_ret(ptr).context("failed to attach uprobe")?;
1008 let link = unsafe { Link::new(ptr) };
1010 Ok(link)
1011 }
1012
1013 pub fn attach_uprobe_with_opts(
1017 &self,
1018 pid: i32,
1019 binary_path: impl AsRef<Path>,
1020 func_offset: usize,
1021 opts: UprobeOpts,
1022 ) -> Result<Link> {
1023 let path = util::path_to_cstring(binary_path)?;
1024 let path_ptr = path.as_ptr();
1025 let UprobeOpts {
1026 ref_ctr_offset,
1027 cookie,
1028 retprobe,
1029 func_name,
1030 _non_exhaustive,
1031 } = opts;
1032
1033 let func_name: Option<CString> = if let Some(func_name) = func_name {
1034 Some(util::str_to_cstring(&func_name)?)
1035 } else {
1036 None
1037 };
1038 let ptr = func_name
1039 .as_ref()
1040 .map_or(ptr::null(), |func_name| func_name.as_ptr());
1041 let opts = libbpf_sys::bpf_uprobe_opts {
1042 sz: size_of::<libbpf_sys::bpf_uprobe_opts>() as _,
1043 ref_ctr_offset: ref_ctr_offset as libbpf_sys::size_t,
1044 bpf_cookie: cookie,
1045 retprobe,
1046 func_name: ptr,
1047 ..Default::default()
1048 };
1049
1050 let ptr = unsafe {
1051 libbpf_sys::bpf_program__attach_uprobe_opts(
1052 self.ptr.as_ptr(),
1053 pid,
1054 path_ptr,
1055 func_offset as libbpf_sys::size_t,
1056 &opts as *const _,
1057 )
1058 };
1059 let ptr = validate_bpf_ret(ptr).context("failed to attach uprobe")?;
1060 let link = unsafe { Link::new(ptr) };
1062 Ok(link)
1063 }
1064
1065 pub fn attach_uprobe_multi(
1068 &self,
1069 pid: i32,
1070 binary_path: impl AsRef<Path>,
1071 func_pattern: impl AsRef<str>,
1072 retprobe: bool,
1073 session: bool,
1074 ) -> Result<Link> {
1075 let opts = UprobeMultiOpts {
1076 syms: Vec::new(),
1077 offsets: Vec::new(),
1078 ref_ctr_offsets: Vec::new(),
1079 cookies: Vec::new(),
1080 retprobe,
1081 session,
1082 _non_exhaustive: (),
1083 };
1084
1085 self.attach_uprobe_multi_with_opts(pid, binary_path, func_pattern, opts)
1086 }
1087
1088 pub fn attach_uprobe_multi_with_opts(
1092 &self,
1093 pid: i32,
1094 binary_path: impl AsRef<Path>,
1095 func_pattern: impl AsRef<str>,
1096 opts: UprobeMultiOpts,
1097 ) -> Result<Link> {
1098 let path = util::path_to_cstring(binary_path)?;
1099 let path_ptr = path.as_ptr();
1100
1101 let UprobeMultiOpts {
1102 syms,
1103 offsets,
1104 ref_ctr_offsets,
1105 cookies,
1106 retprobe,
1107 session,
1108 _non_exhaustive,
1109 } = opts;
1110
1111 let pattern = util::str_to_cstring(func_pattern.as_ref())?;
1112 let pattern_ptr = if pattern.is_empty() {
1114 ptr::null()
1115 } else {
1116 pattern.as_ptr()
1117 };
1118
1119 let syms_cstrings = syms
1120 .iter()
1121 .map(|s| util::str_to_cstring(s))
1122 .collect::<Result<Vec<_>>>()?;
1123 let syms_ptrs = syms_cstrings
1124 .iter()
1125 .map(|cs| cs.as_ptr())
1126 .collect::<Vec<_>>();
1127 let syms_ptr = if !syms_ptrs.is_empty() {
1128 syms_ptrs.as_ptr()
1129 } else {
1130 ptr::null()
1131 };
1132 let offsets_ptr = if !offsets.is_empty() {
1133 offsets.as_ptr()
1134 } else {
1135 ptr::null()
1136 };
1137 let ref_ctr_offsets_ptr = if !ref_ctr_offsets.is_empty() {
1138 ref_ctr_offsets.as_ptr()
1139 } else {
1140 ptr::null()
1141 };
1142 let cookies_ptr = if !cookies.is_empty() {
1143 cookies.as_ptr()
1144 } else {
1145 ptr::null()
1146 };
1147 let cnt = if !syms.is_empty() {
1148 syms.len()
1149 } else if !offsets.is_empty() {
1150 offsets.len()
1151 } else {
1152 0
1153 };
1154
1155 let c_opts = libbpf_sys::bpf_uprobe_multi_opts {
1156 sz: size_of::<libbpf_sys::bpf_uprobe_multi_opts>() as _,
1157 syms: syms_ptr.cast_mut(),
1158 offsets: offsets_ptr.cast(),
1159 ref_ctr_offsets: ref_ctr_offsets_ptr.cast(),
1160 cookies: cookies_ptr.cast(),
1161 cnt: cnt as libbpf_sys::size_t,
1162 retprobe,
1163 session,
1164 ..Default::default()
1165 };
1166
1167 let ptr = unsafe {
1168 libbpf_sys::bpf_program__attach_uprobe_multi(
1169 self.ptr.as_ptr(),
1170 pid,
1171 path_ptr,
1172 pattern_ptr,
1173 &c_opts as *const _,
1174 )
1175 };
1176
1177 let ptr = validate_bpf_ret(ptr).context("failed to attach uprobe multi")?;
1178 let link = unsafe { Link::new(ptr) };
1180 Ok(link)
1181 }
1182
1183 pub fn attach_kprobe<T: AsRef<str>>(&self, retprobe: bool, func_name: T) -> Result<Link> {
1186 let func_name = util::str_to_cstring(func_name.as_ref())?;
1187 let func_name_ptr = func_name.as_ptr();
1188 let ptr = unsafe {
1189 libbpf_sys::bpf_program__attach_kprobe(self.ptr.as_ptr(), retprobe, func_name_ptr)
1190 };
1191 let ptr = validate_bpf_ret(ptr).context("failed to attach kprobe")?;
1192 let link = unsafe { Link::new(ptr) };
1194 Ok(link)
1195 }
1196
1197 pub fn attach_kprobe_with_opts<T: AsRef<str>>(
1201 &self,
1202 retprobe: bool,
1203 func_name: T,
1204 opts: KprobeOpts,
1205 ) -> Result<Link> {
1206 let func_name = util::str_to_cstring(func_name.as_ref())?;
1207 let func_name_ptr = func_name.as_ptr();
1208
1209 let mut opts = libbpf_sys::bpf_kprobe_opts::from(opts);
1210 opts.retprobe = retprobe;
1211
1212 let ptr = unsafe {
1213 libbpf_sys::bpf_program__attach_kprobe_opts(
1214 self.ptr.as_ptr(),
1215 func_name_ptr,
1216 &opts as *const _,
1217 )
1218 };
1219 let ptr = validate_bpf_ret(ptr).context("failed to attach kprobe")?;
1220 let link = unsafe { Link::new(ptr) };
1222 Ok(link)
1223 }
1224
1225 fn check_kprobe_multi_args<T: AsRef<str>>(symbols: &[T], cookies: &[u64]) -> Result<usize> {
1226 if symbols.is_empty() {
1227 return Err(Error::with_invalid_input("Symbols list cannot be empty"));
1228 }
1229
1230 if !cookies.is_empty() && symbols.len() != cookies.len() {
1231 return Err(Error::with_invalid_input(
1232 "Symbols and cookies list must have the same size",
1233 ));
1234 }
1235
1236 Ok(symbols.len())
1237 }
1238
1239 fn attach_kprobe_multi_impl(&self, opts: libbpf_sys::bpf_kprobe_multi_opts) -> Result<Link> {
1240 let ptr = unsafe {
1241 libbpf_sys::bpf_program__attach_kprobe_multi_opts(
1242 self.ptr.as_ptr(),
1243 ptr::null(),
1244 &opts as *const _,
1245 )
1246 };
1247 let ptr = validate_bpf_ret(ptr).context("failed to attach kprobe multi")?;
1248 let link = unsafe { Link::new(ptr) };
1250 Ok(link)
1251 }
1252
1253 pub fn attach_kprobe_multi<T: AsRef<str>>(
1257 &self,
1258 retprobe: bool,
1259 symbols: Vec<T>,
1260 ) -> Result<Link> {
1261 let cnt = Self::check_kprobe_multi_args(&symbols, &[])?;
1262
1263 let csyms = symbols
1264 .iter()
1265 .map(|s| util::str_to_cstring(s.as_ref()))
1266 .collect::<Result<Vec<_>>>()?;
1267 let mut syms = csyms.iter().map(|s| s.as_ptr()).collect::<Vec<_>>();
1268
1269 let opts = libbpf_sys::bpf_kprobe_multi_opts {
1270 sz: size_of::<libbpf_sys::bpf_kprobe_multi_opts>() as _,
1271 syms: syms.as_mut_ptr() as _,
1272 cnt: cnt as libbpf_sys::size_t,
1273 retprobe,
1274 ..Default::default()
1276 };
1277
1278 self.attach_kprobe_multi_impl(opts)
1279 }
1280
1281 pub fn attach_kprobe_multi_with_opts(&self, opts: KprobeMultiOpts) -> Result<Link> {
1285 let KprobeMultiOpts {
1286 symbols,
1287 mut cookies,
1288 retprobe,
1289 _non_exhaustive,
1290 } = opts;
1291
1292 let cnt = Self::check_kprobe_multi_args(&symbols, &cookies)?;
1293
1294 let csyms = symbols
1295 .iter()
1296 .map(|s| util::str_to_cstring(s.as_ref()))
1297 .collect::<Result<Vec<_>>>()?;
1298 let mut syms = csyms.iter().map(|s| s.as_ptr()).collect::<Vec<_>>();
1299
1300 let opts = libbpf_sys::bpf_kprobe_multi_opts {
1301 sz: size_of::<libbpf_sys::bpf_kprobe_multi_opts>() as _,
1302 syms: syms.as_mut_ptr() as _,
1303 cookies: if !cookies.is_empty() {
1304 cookies.as_mut_ptr() as _
1305 } else {
1306 ptr::null()
1307 },
1308 cnt: cnt as libbpf_sys::size_t,
1309 retprobe,
1310 ..Default::default()
1312 };
1313
1314 self.attach_kprobe_multi_impl(opts)
1315 }
1316
1317 pub fn attach_ksyscall<T: AsRef<str>>(&self, retprobe: bool, syscall_name: T) -> Result<Link> {
1319 let opts = libbpf_sys::bpf_ksyscall_opts {
1320 sz: size_of::<libbpf_sys::bpf_ksyscall_opts>() as _,
1321 retprobe,
1322 ..Default::default()
1323 };
1324
1325 let syscall_name = util::str_to_cstring(syscall_name.as_ref())?;
1326 let syscall_name_ptr = syscall_name.as_ptr();
1327 let ptr = unsafe {
1328 libbpf_sys::bpf_program__attach_ksyscall(self.ptr.as_ptr(), syscall_name_ptr, &opts)
1329 };
1330 let ptr = validate_bpf_ret(ptr).context("failed to attach ksyscall")?;
1331 let link = unsafe { Link::new(ptr) };
1333 Ok(link)
1334 }
1335
1336 fn attach_tracepoint_impl(
1337 &self,
1338 tp_category: &str,
1339 tp_name: &str,
1340 tp_opts: Option<TracepointOpts>,
1341 ) -> Result<Link> {
1342 let tp_category = util::str_to_cstring(tp_category)?;
1343 let tp_category_ptr = tp_category.as_ptr();
1344 let tp_name = util::str_to_cstring(tp_name)?;
1345 let tp_name_ptr = tp_name.as_ptr();
1346
1347 let tp_opts = tp_opts.map(libbpf_sys::bpf_tracepoint_opts::from);
1348 let opts = tp_opts.as_ref().map_or(ptr::null(), |opts| opts);
1349 let ptr = unsafe {
1350 libbpf_sys::bpf_program__attach_tracepoint_opts(
1351 self.ptr.as_ptr(),
1352 tp_category_ptr,
1353 tp_name_ptr,
1354 opts as *const _,
1355 )
1356 };
1357
1358 let ptr = validate_bpf_ret(ptr).context("failed to attach tracepoint")?;
1359 let link = unsafe { Link::new(ptr) };
1361 Ok(link)
1362 }
1363
1364 pub fn attach_tracepoint(
1367 &self,
1368 tp_category: TracepointCategory,
1369 tp_name: impl AsRef<str>,
1370 ) -> Result<Link> {
1371 self.attach_tracepoint_impl(tp_category.as_ref(), tp_name.as_ref(), None)
1372 }
1373
1374 pub fn attach_tracepoint_with_opts(
1378 &self,
1379 tp_category: TracepointCategory,
1380 tp_name: impl AsRef<str>,
1381 tp_opts: TracepointOpts,
1382 ) -> Result<Link> {
1383 self.attach_tracepoint_impl(tp_category.as_ref(), tp_name.as_ref(), Some(tp_opts))
1384 }
1385
1386 pub fn attach_raw_tracepoint<T: AsRef<str>>(&self, tp_name: T) -> Result<Link> {
1389 let tp_name = util::str_to_cstring(tp_name.as_ref())?;
1390 let tp_name_ptr = tp_name.as_ptr();
1391 let ptr = unsafe {
1392 libbpf_sys::bpf_program__attach_raw_tracepoint(self.ptr.as_ptr(), tp_name_ptr)
1393 };
1394 let ptr = validate_bpf_ret(ptr).context("failed to attach raw tracepoint")?;
1395 let link = unsafe { Link::new(ptr) };
1397 Ok(link)
1398 }
1399
1400 pub fn attach_raw_tracepoint_with_opts<T: AsRef<str>>(
1404 &self,
1405 tp_name: T,
1406 tp_opts: RawTracepointOpts,
1407 ) -> Result<Link> {
1408 let tp_name = util::str_to_cstring(tp_name.as_ref())?;
1409 let tp_name_ptr = tp_name.as_ptr();
1410 let mut tp_opts = libbpf_sys::bpf_raw_tracepoint_opts::from(tp_opts);
1411 let ptr = unsafe {
1412 libbpf_sys::bpf_program__attach_raw_tracepoint_opts(
1413 self.ptr.as_ptr(),
1414 tp_name_ptr,
1415 &mut tp_opts as *mut _,
1416 )
1417 };
1418 let ptr = validate_bpf_ret(ptr).context("failed to attach raw tracepoint")?;
1419 let link = unsafe { Link::new(ptr) };
1421 Ok(link)
1422 }
1423
1424 pub fn attach_lsm(&self) -> Result<Link> {
1426 let ptr = unsafe { libbpf_sys::bpf_program__attach_lsm(self.ptr.as_ptr()) };
1427 let ptr = validate_bpf_ret(ptr).context("failed to attach LSM")?;
1428 let link = unsafe { Link::new(ptr) };
1430 Ok(link)
1431 }
1432
1433 pub fn attach_trace(&self) -> Result<Link> {
1435 let ptr = unsafe { libbpf_sys::bpf_program__attach_trace(self.ptr.as_ptr()) };
1436 let ptr = validate_bpf_ret(ptr).context("failed to attach fentry/fexit kernel probe")?;
1437 let link = unsafe { Link::new(ptr) };
1439 Ok(link)
1440 }
1441
1442 pub fn attach_sockmap(&self, map_fd: i32) -> Result<()> {
1444 let err = unsafe {
1445 libbpf_sys::bpf_prog_attach(
1446 self.as_fd().as_raw_fd(),
1447 map_fd,
1448 self.attach_type() as u32,
1449 0,
1450 )
1451 };
1452 util::parse_ret(err)
1453 }
1454
1455 pub fn attach_xdp(&self, ifindex: i32) -> Result<Link> {
1457 let ptr = unsafe { libbpf_sys::bpf_program__attach_xdp(self.ptr.as_ptr(), ifindex) };
1458 let ptr = validate_bpf_ret(ptr).context("failed to attach XDP program")?;
1459 let link = unsafe { Link::new(ptr) };
1461 Ok(link)
1462 }
1463
1464 pub fn attach_netns(&self, netns_fd: i32) -> Result<Link> {
1466 let ptr = unsafe { libbpf_sys::bpf_program__attach_netns(self.ptr.as_ptr(), netns_fd) };
1467 let ptr = validate_bpf_ret(ptr).context("failed to attach network namespace program")?;
1468 let link = unsafe { Link::new(ptr) };
1470 Ok(link)
1471 }
1472
1473 pub fn attach_netfilter_with_opts(
1475 &self,
1476 netfilter_opt: netfilter::NetfilterOpts,
1477 ) -> Result<Link> {
1478 let netfilter_opts = libbpf_sys::bpf_netfilter_opts::from(netfilter_opt);
1479
1480 let ptr = unsafe {
1481 libbpf_sys::bpf_program__attach_netfilter(
1482 self.ptr.as_ptr(),
1483 &netfilter_opts as *const _,
1484 )
1485 };
1486
1487 let ptr = validate_bpf_ret(ptr).context("failed to attach netfilter program")?;
1488 let link = unsafe { Link::new(ptr) };
1490 Ok(link)
1491 }
1492
1493 fn attach_usdt_impl(
1494 &self,
1495 pid: i32,
1496 binary_path: &Path,
1497 usdt_provider: &str,
1498 usdt_name: &str,
1499 usdt_opts: Option<UsdtOpts>,
1500 ) -> Result<Link> {
1501 let path = util::path_to_cstring(binary_path)?;
1502 let path_ptr = path.as_ptr();
1503 let usdt_provider = util::str_to_cstring(usdt_provider)?;
1504 let usdt_provider_ptr = usdt_provider.as_ptr();
1505 let usdt_name = util::str_to_cstring(usdt_name)?;
1506 let usdt_name_ptr = usdt_name.as_ptr();
1507 let usdt_opts = usdt_opts.map(libbpf_sys::bpf_usdt_opts::from);
1508 let usdt_opts_ptr = usdt_opts
1509 .as_ref()
1510 .map(|opts| opts as *const _)
1511 .unwrap_or_else(ptr::null);
1512
1513 let ptr = unsafe {
1514 libbpf_sys::bpf_program__attach_usdt(
1515 self.ptr.as_ptr(),
1516 pid,
1517 path_ptr,
1518 usdt_provider_ptr,
1519 usdt_name_ptr,
1520 usdt_opts_ptr,
1521 )
1522 };
1523 let ptr = validate_bpf_ret(ptr).context("failed to attach USDT")?;
1524 let link = unsafe { Link::new(ptr) };
1526 Ok(link)
1527 }
1528
1529 pub fn attach_usdt(
1533 &self,
1534 pid: i32,
1535 binary_path: impl AsRef<Path>,
1536 usdt_provider: impl AsRef<str>,
1537 usdt_name: impl AsRef<str>,
1538 ) -> Result<Link> {
1539 self.attach_usdt_impl(
1540 pid,
1541 binary_path.as_ref(),
1542 usdt_provider.as_ref(),
1543 usdt_name.as_ref(),
1544 None,
1545 )
1546 }
1547
1548 pub fn attach_usdt_with_opts(
1552 &self,
1553 pid: i32,
1554 binary_path: impl AsRef<Path>,
1555 usdt_provider: impl AsRef<str>,
1556 usdt_name: impl AsRef<str>,
1557 usdt_opts: UsdtOpts,
1558 ) -> Result<Link> {
1559 self.attach_usdt_impl(
1560 pid,
1561 binary_path.as_ref(),
1562 usdt_provider.as_ref(),
1563 usdt_name.as_ref(),
1564 Some(usdt_opts),
1565 )
1566 }
1567
1568 pub fn attach_iter(&self, map_fd: BorrowedFd<'_>) -> Result<Link> {
1572 let map_opts = MapIterOpts {
1573 fd: map_fd,
1574 _non_exhaustive: (),
1575 };
1576 self.attach_iter_with_opts(IterOpts::Map(map_opts))
1577 }
1578
1579 pub fn attach_iter_with_opts(&self, opts: IterOpts<'_>) -> Result<Link> {
1585 let mut linkinfo = libbpf_sys::bpf_iter_link_info::from(opts);
1586 let attach_opt = libbpf_sys::bpf_iter_attach_opts {
1587 link_info: &raw mut linkinfo,
1588 link_info_len: size_of::<libbpf_sys::bpf_iter_link_info>() as _,
1589 sz: size_of::<libbpf_sys::bpf_iter_attach_opts>() as _,
1590 ..Default::default()
1591 };
1592 let ptr = unsafe {
1593 libbpf_sys::bpf_program__attach_iter(
1594 self.ptr.as_ptr(),
1595 &attach_opt as *const libbpf_sys::bpf_iter_attach_opts,
1596 )
1597 };
1598
1599 let ptr = validate_bpf_ret(ptr).context("failed to attach iterator")?;
1600 let link = unsafe { Link::new(ptr) };
1602 Ok(link)
1603 }
1604
1605 pub fn test_run<'dat>(&self, input: Input<'dat>) -> Result<Output<'dat>> {
1611 unsafe fn slice_from_array<'t, T>(items: *mut T, num_items: usize) -> Option<&'t mut [T]> {
1612 if items.is_null() {
1613 None
1614 } else {
1615 Some(unsafe { slice::from_raw_parts_mut(items, num_items) })
1616 }
1617 }
1618
1619 let Input {
1620 context_in,
1621 mut context_out,
1622 data_in,
1623 mut data_out,
1624 cpu,
1625 flags,
1626 repeat,
1627 _non_exhaustive: (),
1628 } = input;
1629
1630 let mut opts = unsafe { mem::zeroed::<libbpf_sys::bpf_test_run_opts>() };
1631 opts.sz = size_of_val(&opts) as _;
1632 opts.ctx_in = context_in
1633 .as_ref()
1634 .map(|data| data.as_ptr().cast())
1635 .unwrap_or_else(ptr::null);
1636 opts.ctx_size_in = context_in.map(|data| data.len() as _).unwrap_or(0);
1637 opts.ctx_out = context_out
1638 .as_mut()
1639 .map(|data| data.as_mut_ptr().cast())
1640 .unwrap_or_else(ptr::null_mut);
1641 opts.ctx_size_out = context_out.map(|data| data.len() as _).unwrap_or(0);
1642 opts.data_in = data_in
1643 .map(|data| data.as_ptr().cast())
1644 .unwrap_or_else(ptr::null);
1645 opts.data_size_in = data_in.map(|data| data.len() as _).unwrap_or(0);
1646 opts.data_out = data_out
1647 .as_mut()
1648 .map(|data| data.as_mut_ptr().cast())
1649 .unwrap_or_else(ptr::null_mut);
1650 opts.data_size_out = data_out.map(|data| data.len() as _).unwrap_or(0);
1651 opts.cpu = cpu;
1652 opts.flags = flags;
1653 opts.repeat = repeat as i32;
1656
1657 let rc = unsafe { libbpf_sys::bpf_prog_test_run_opts(self.as_fd().as_raw_fd(), &mut opts) };
1658 let () = util::parse_ret(rc)?;
1659 let output = Output {
1660 return_value: opts.retval,
1661 context: unsafe { slice_from_array(opts.ctx_out.cast(), opts.ctx_size_out as _) },
1662 data: unsafe { slice_from_array(opts.data_out.cast(), opts.data_size_out as _) },
1663 duration: Duration::from_nanos(opts.duration.into()),
1664 _non_exhaustive: (),
1665 };
1666 Ok(output)
1667 }
1668
1669 pub fn stdout(&self) -> impl Read + '_ {
1671 Stream::new(self.as_fd(), Stream::BPF_STDOUT)
1672 }
1673
1674 pub fn stderr(&self) -> impl Read + '_ {
1676 Stream::new(self.as_fd(), Stream::BPF_STDERR)
1677 }
1678}
1679
1680impl<'obj> Deref for ProgramMut<'obj> {
1681 type Target = Program<'obj>;
1682
1683 fn deref(&self) -> &Self::Target {
1684 unsafe { transmute::<&ProgramMut<'obj>, &Program<'obj>>(self) }
1687 }
1688}
1689
1690impl<T> AsFd for ProgramImpl<'_, T> {
1691 fn as_fd(&self) -> BorrowedFd<'_> {
1692 let fd = unsafe { libbpf_sys::bpf_program__fd(self.ptr.as_ptr()) };
1693 unsafe { BorrowedFd::borrow_raw(fd) }
1694 }
1695}
1696
1697impl<T> AsRawLibbpf for ProgramImpl<'_, T> {
1698 type LibbpfType = libbpf_sys::bpf_program;
1699
1700 fn as_libbpf_object(&self) -> NonNull<Self::LibbpfType> {
1702 self.ptr
1703 }
1704}
1705
1706#[cfg(test)]
1707mod tests {
1708 use super::*;
1709
1710 use std::mem::discriminant;
1711
1712 #[test]
1713 fn program_type() {
1714 use ProgramType::*;
1715
1716 for t in [
1717 Unspec,
1718 SocketFilter,
1719 Kprobe,
1720 SchedCls,
1721 SchedAct,
1722 Tracepoint,
1723 Xdp,
1724 PerfEvent,
1725 CgroupSkb,
1726 CgroupSock,
1727 LwtIn,
1728 LwtOut,
1729 LwtXmit,
1730 SockOps,
1731 SkSkb,
1732 CgroupDevice,
1733 SkMsg,
1734 RawTracepoint,
1735 CgroupSockAddr,
1736 LwtSeg6local,
1737 LircMode2,
1738 SkReuseport,
1739 FlowDissector,
1740 CgroupSysctl,
1741 RawTracepointWritable,
1742 CgroupSockopt,
1743 Tracing,
1744 StructOps,
1745 Ext,
1746 Lsm,
1747 SkLookup,
1748 Syscall,
1749 Netfilter,
1750 Unknown,
1751 ] {
1752 assert_eq!(discriminant(&t), discriminant(&ProgramType::from(t as u32)));
1754 }
1755 }
1756
1757 #[test]
1758 fn program_attach_type() {
1759 use ProgramAttachType::*;
1760
1761 for t in [
1762 CgroupInetIngress,
1763 CgroupInetEgress,
1764 CgroupInetSockCreate,
1765 CgroupSockOps,
1766 SkSkbStreamParser,
1767 SkSkbStreamVerdict,
1768 CgroupDevice,
1769 SkMsgVerdict,
1770 CgroupInet4Bind,
1771 CgroupInet6Bind,
1772 CgroupInet4Connect,
1773 CgroupInet6Connect,
1774 CgroupInet4PostBind,
1775 CgroupInet6PostBind,
1776 CgroupUdp4Sendmsg,
1777 CgroupUdp6Sendmsg,
1778 LircMode2,
1779 FlowDissector,
1780 CgroupSysctl,
1781 CgroupUdp4Recvmsg,
1782 CgroupUdp6Recvmsg,
1783 CgroupGetsockopt,
1784 CgroupSetsockopt,
1785 TraceRawTp,
1786 TraceFentry,
1787 TraceFexit,
1788 ModifyReturn,
1789 LsmMac,
1790 TraceIter,
1791 CgroupInet4Getpeername,
1792 CgroupInet6Getpeername,
1793 CgroupInet4Getsockname,
1794 CgroupInet6Getsockname,
1795 XdpDevmap,
1796 CgroupInetSockRelease,
1797 XdpCpumap,
1798 SkLookup,
1799 Xdp,
1800 SkSkbVerdict,
1801 SkReuseportSelect,
1802 SkReuseportSelectOrMigrate,
1803 PerfEvent,
1804 Unknown,
1805 ] {
1806 assert_eq!(
1808 discriminant(&t),
1809 discriminant(&ProgramAttachType::from(t as u32))
1810 );
1811 }
1812 }
1813}