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::ffi::OsString;
10use std::fs::remove_file;
11use std::io::Read;
12use std::marker::PhantomData;
13use std::mem;
14use std::mem::size_of;
15use std::mem::size_of_val;
16use std::mem::transmute;
17use std::ops::Deref;
18use std::os::unix::ffi::OsStrExt as _;
19use std::os::unix::io::AsFd;
20use std::os::unix::io::AsRawFd;
21use std::os::unix::io::BorrowedFd;
22use std::os::unix::io::FromRawFd;
23use std::os::unix::io::OwnedFd;
24use std::path::Path;
25use std::ptr;
26use std::ptr::NonNull;
27use std::slice;
28use std::time::Duration;
29
30use libbpf_sys::bpf_func_id;
31
32use crate::netfilter;
33use crate::streams::Stream;
34use crate::util;
35use crate::util::validate_bpf_ret;
36use crate::util::BpfObjectType;
37use crate::AsRawLibbpf;
38use crate::Error;
39use crate::ErrorExt as _;
40use crate::Link;
41use crate::Map;
42use crate::Mut;
43use crate::RawTracepointOpts;
44use crate::Result;
45use crate::TracepointCategory;
46use crate::TracepointOpts;
47
48#[derive(Clone, Debug, Default)]
50pub struct UprobeOpts {
51 pub ref_ctr_offset: usize,
53 pub cookie: u64,
55 pub retprobe: bool,
57 pub func_name: Option<String>,
69 #[doc(hidden)]
70 pub _non_exhaustive: (),
71}
72
73#[derive(Clone, Debug, Default)]
75pub struct UprobeMultiOpts {
76 pub syms: Vec<String>,
78 pub offsets: Vec<usize>,
80 pub ref_ctr_offsets: Vec<usize>,
82 pub cookies: Vec<u64>,
84 pub retprobe: bool,
86 pub session: bool,
88 #[doc(hidden)]
89 pub _non_exhaustive: (),
90}
91
92#[derive(Clone, Debug, Default)]
94pub struct UsdtOpts {
95 pub cookie: u64,
97 #[doc(hidden)]
98 pub _non_exhaustive: (),
99}
100
101impl From<UsdtOpts> for libbpf_sys::bpf_usdt_opts {
102 fn from(opts: UsdtOpts) -> Self {
103 let UsdtOpts {
104 cookie,
105 _non_exhaustive,
106 } = opts;
107 #[allow(clippy::needless_update)]
108 Self {
109 sz: size_of::<Self>() as _,
110 usdt_cookie: cookie,
111 ..Default::default()
113 }
114 }
115}
116
117#[derive(Clone, Debug, Default)]
119pub struct KprobeOpts {
120 pub cookie: u64,
122 #[doc(hidden)]
123 pub _non_exhaustive: (),
124}
125
126impl From<KprobeOpts> for libbpf_sys::bpf_kprobe_opts {
127 fn from(opts: KprobeOpts) -> Self {
128 let KprobeOpts {
129 cookie,
130 _non_exhaustive,
131 } = opts;
132
133 #[allow(clippy::needless_update)]
134 Self {
135 sz: size_of::<Self>() as _,
136 bpf_cookie: cookie,
137 ..Default::default()
139 }
140 }
141}
142
143#[derive(Clone, Debug, Default)]
145pub struct KprobeMultiOpts {
146 pub symbols: Vec<String>,
148 pub cookies: Vec<u64>,
150 pub retprobe: bool,
152 #[doc(hidden)]
153 pub _non_exhaustive: (),
154}
155
156#[derive(Clone, Debug, Default)]
158pub struct PerfEventOpts {
159 pub cookie: u64,
161 pub force_ioctl_attach: bool,
163 #[doc(hidden)]
164 pub _non_exhaustive: (),
165}
166
167impl From<PerfEventOpts> for libbpf_sys::bpf_perf_event_opts {
168 fn from(opts: PerfEventOpts) -> Self {
169 let PerfEventOpts {
170 cookie,
171 force_ioctl_attach,
172 _non_exhaustive,
173 } = opts;
174
175 #[allow(clippy::needless_update)]
176 Self {
177 sz: size_of::<Self>() as _,
178 bpf_cookie: cookie,
179 force_ioctl_attach,
180 ..Default::default()
182 }
183 }
184}
185
186
187#[derive(Clone, Debug)]
189pub struct MapIterOpts<'fd> {
190 pub fd: BorrowedFd<'fd>,
192 #[doc(hidden)]
193 pub _non_exhaustive: (),
194}
195
196impl<'fd> MapIterOpts<'fd> {
197 pub fn from_fd(fd: BorrowedFd<'fd>) -> Self {
199 Self {
200 fd,
201 _non_exhaustive: (),
202 }
203 }
204}
205
206
207#[non_exhaustive]
209#[repr(u32)]
210#[derive(Clone, Debug, Default)]
211pub enum CgroupIterOrder {
212 #[default]
214 Default = libbpf_sys::BPF_CGROUP_ITER_ORDER_UNSPEC,
215 SelfOnly = libbpf_sys::BPF_CGROUP_ITER_SELF_ONLY,
217 DescendantsPre = libbpf_sys::BPF_CGROUP_ITER_DESCENDANTS_PRE,
219 DescendantsPost = libbpf_sys::BPF_CGROUP_ITER_DESCENDANTS_POST,
221 AncestorsUp = libbpf_sys::BPF_CGROUP_ITER_ANCESTORS_UP,
223}
224
225#[derive(Clone, Debug)]
227pub struct CgroupIterOpts<'fd> {
228 pub fd: BorrowedFd<'fd>,
230 pub order: CgroupIterOrder,
232 #[doc(hidden)]
233 pub _non_exhaustive: (),
234}
235
236impl<'fd> CgroupIterOpts<'fd> {
237 pub fn from_fd(fd: BorrowedFd<'fd>) -> Self {
239 Self {
240 fd,
241 order: CgroupIterOrder::default(),
242 _non_exhaustive: (),
243 }
244 }
245}
246
247
248#[non_exhaustive]
250#[derive(Clone, Debug)]
251pub enum IterOpts<'fd> {
252 None,
254 Map(MapIterOpts<'fd>),
256 Cgroup(CgroupIterOpts<'fd>),
258}
259
260
261pub type OpenProgram<'obj> = OpenProgramImpl<'obj>;
263pub type OpenProgramMut<'obj> = OpenProgramImpl<'obj, Mut>;
265
266
267#[derive(Debug)]
271#[repr(transparent)]
272pub struct OpenProgramImpl<'obj, T = ()> {
273 ptr: NonNull<libbpf_sys::bpf_program>,
274 _phantom: PhantomData<&'obj T>,
275}
276
277impl<'obj> OpenProgram<'obj> {
278 pub fn new(prog: &'obj libbpf_sys::bpf_program) -> Self {
280 Self {
283 ptr: unsafe { NonNull::new_unchecked(prog as *const _ as *mut _) },
284 _phantom: PhantomData,
285 }
286 }
287
288 pub fn prog_type(&self) -> ProgramType {
290 ProgramType::from(unsafe { libbpf_sys::bpf_program__type(self.ptr.as_ptr()) })
291 }
292
293 pub fn name(&self) -> &'obj OsStr {
295 let name_ptr = unsafe { libbpf_sys::bpf_program__name(self.ptr.as_ptr()) };
296 let name_c_str = unsafe { CStr::from_ptr(name_ptr) };
297 OsStr::from_bytes(name_c_str.to_bytes())
299 }
300
301 pub fn section(&self) -> &'obj OsStr {
303 let p = unsafe { libbpf_sys::bpf_program__section_name(self.ptr.as_ptr()) };
305 let section_c_str = unsafe { CStr::from_ptr(p) };
308 let section = OsStr::from_bytes(section_c_str.to_bytes());
309 section
310 }
311
312 pub fn insn_cnt(&self) -> usize {
318 unsafe { libbpf_sys::bpf_program__insn_cnt(self.ptr.as_ptr()) as usize }
319 }
320
321 pub fn insns(&self) -> &'obj [libbpf_sys::bpf_insn] {
331 let count = self.insn_cnt();
332 let ptr = unsafe { libbpf_sys::bpf_program__insns(self.ptr.as_ptr()) };
333 unsafe { slice::from_raw_parts(ptr, count) }
334 }
335
336 pub fn autoload(&self) -> bool {
338 unsafe { libbpf_sys::bpf_program__autoload(self.ptr.as_ptr()) }
339 }
340}
341
342impl<'obj> OpenProgramMut<'obj> {
343 pub fn new_mut(prog: &'obj mut libbpf_sys::bpf_program) -> Self {
345 Self {
346 ptr: unsafe { NonNull::new_unchecked(prog as *mut _) },
347 _phantom: PhantomData,
348 }
349 }
350
351 pub fn set_prog_type(&mut self, prog_type: ProgramType) {
353 let rc = unsafe { libbpf_sys::bpf_program__set_type(self.ptr.as_ptr(), prog_type as u32) };
354 debug_assert!(util::parse_ret(rc).is_ok(), "{rc}");
355 }
356
357 pub fn set_attach_type(&mut self, attach_type: ProgramAttachType) {
359 let rc = unsafe {
360 libbpf_sys::bpf_program__set_expected_attach_type(self.ptr.as_ptr(), attach_type as u32)
361 };
362 debug_assert!(util::parse_ret(rc).is_ok(), "{rc}");
363 }
364
365 pub fn set_ifindex(&mut self, idx: u32) {
369 unsafe { libbpf_sys::bpf_program__set_ifindex(self.ptr.as_ptr(), idx) }
370 }
371
372 pub fn set_log_level(&mut self, log_level: u32) {
381 let rc = unsafe { libbpf_sys::bpf_program__set_log_level(self.ptr.as_ptr(), log_level) };
382 debug_assert!(util::parse_ret(rc).is_ok(), "{rc}");
383 }
384
385 pub fn set_autoload(&mut self, autoload: bool) {
388 let rc = unsafe { libbpf_sys::bpf_program__set_autoload(self.ptr.as_ptr(), autoload) };
389 debug_assert!(util::parse_ret(rc).is_ok(), "{rc}");
390 }
391
392 pub fn set_autoattach(&mut self, autoattach: bool) {
395 unsafe { libbpf_sys::bpf_program__set_autoattach(self.ptr.as_ptr(), autoattach) };
396 }
397
398 #[expect(missing_docs)]
399 pub fn set_attach_target(
400 &mut self,
401 attach_prog_fd: i32,
402 attach_func_name: Option<String>,
403 ) -> Result<()> {
404 let name_c = if let Some(name) = attach_func_name {
405 Some(util::str_to_cstring(&name)?)
406 } else {
407 None
408 };
409 let name_ptr = name_c.as_ref().map_or(ptr::null(), |name| name.as_ptr());
410 let ret = unsafe {
411 libbpf_sys::bpf_program__set_attach_target(self.ptr.as_ptr(), attach_prog_fd, name_ptr)
412 };
413 util::parse_ret(ret)
414 }
415
416 pub fn set_flags(&mut self, flags: u32) {
418 let rc = unsafe { libbpf_sys::bpf_program__set_flags(self.ptr.as_ptr(), flags) };
419 debug_assert!(util::parse_ret(rc).is_ok(), "{rc}");
420 }
421}
422
423impl<'obj> Deref for OpenProgramMut<'obj> {
424 type Target = OpenProgram<'obj>;
425
426 fn deref(&self) -> &Self::Target {
427 unsafe { transmute::<&OpenProgramMut<'obj>, &OpenProgram<'obj>>(self) }
430 }
431}
432
433impl<T> AsRawLibbpf for OpenProgramImpl<'_, T> {
434 type LibbpfType = libbpf_sys::bpf_program;
435
436 fn as_libbpf_object(&self) -> NonNull<Self::LibbpfType> {
438 self.ptr
439 }
440}
441
442#[non_exhaustive]
444#[repr(u32)]
445#[derive(Copy, Clone, PartialEq, Eq, Debug)]
446#[expect(missing_docs)]
448pub enum ProgramType {
449 Unspec = 0,
450 SocketFilter = libbpf_sys::BPF_PROG_TYPE_SOCKET_FILTER,
451 Kprobe = libbpf_sys::BPF_PROG_TYPE_KPROBE,
452 SchedCls = libbpf_sys::BPF_PROG_TYPE_SCHED_CLS,
453 SchedAct = libbpf_sys::BPF_PROG_TYPE_SCHED_ACT,
454 Tracepoint = libbpf_sys::BPF_PROG_TYPE_TRACEPOINT,
455 Xdp = libbpf_sys::BPF_PROG_TYPE_XDP,
456 PerfEvent = libbpf_sys::BPF_PROG_TYPE_PERF_EVENT,
457 CgroupSkb = libbpf_sys::BPF_PROG_TYPE_CGROUP_SKB,
458 CgroupSock = libbpf_sys::BPF_PROG_TYPE_CGROUP_SOCK,
459 LwtIn = libbpf_sys::BPF_PROG_TYPE_LWT_IN,
460 LwtOut = libbpf_sys::BPF_PROG_TYPE_LWT_OUT,
461 LwtXmit = libbpf_sys::BPF_PROG_TYPE_LWT_XMIT,
462 SockOps = libbpf_sys::BPF_PROG_TYPE_SOCK_OPS,
463 SkSkb = libbpf_sys::BPF_PROG_TYPE_SK_SKB,
464 CgroupDevice = libbpf_sys::BPF_PROG_TYPE_CGROUP_DEVICE,
465 SkMsg = libbpf_sys::BPF_PROG_TYPE_SK_MSG,
466 RawTracepoint = libbpf_sys::BPF_PROG_TYPE_RAW_TRACEPOINT,
467 CgroupSockAddr = libbpf_sys::BPF_PROG_TYPE_CGROUP_SOCK_ADDR,
468 LwtSeg6local = libbpf_sys::BPF_PROG_TYPE_LWT_SEG6LOCAL,
469 LircMode2 = libbpf_sys::BPF_PROG_TYPE_LIRC_MODE2,
470 SkReuseport = libbpf_sys::BPF_PROG_TYPE_SK_REUSEPORT,
471 FlowDissector = libbpf_sys::BPF_PROG_TYPE_FLOW_DISSECTOR,
472 CgroupSysctl = libbpf_sys::BPF_PROG_TYPE_CGROUP_SYSCTL,
473 RawTracepointWritable = libbpf_sys::BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE,
474 CgroupSockopt = libbpf_sys::BPF_PROG_TYPE_CGROUP_SOCKOPT,
475 Tracing = libbpf_sys::BPF_PROG_TYPE_TRACING,
476 StructOps = libbpf_sys::BPF_PROG_TYPE_STRUCT_OPS,
477 Ext = libbpf_sys::BPF_PROG_TYPE_EXT,
478 Lsm = libbpf_sys::BPF_PROG_TYPE_LSM,
479 SkLookup = libbpf_sys::BPF_PROG_TYPE_SK_LOOKUP,
480 Syscall = libbpf_sys::BPF_PROG_TYPE_SYSCALL,
481 Netfilter = libbpf_sys::BPF_PROG_TYPE_NETFILTER,
482 Unknown = u32::MAX,
484}
485
486impl ProgramType {
487 pub fn is_supported(&self) -> Result<bool> {
492 let ret = unsafe { libbpf_sys::libbpf_probe_bpf_prog_type(*self as u32, ptr::null()) };
493 match ret {
494 0 => Ok(false),
495 1 => Ok(true),
496 _ => Err(Error::from_raw_os_error(-ret)),
497 }
498 }
499
500 pub fn is_helper_supported(&self, helper_id: bpf_func_id) -> Result<bool> {
506 let ret =
507 unsafe { libbpf_sys::libbpf_probe_bpf_helper(*self as u32, helper_id, ptr::null()) };
508 match ret {
509 0 => Ok(false),
510 1 => Ok(true),
511 _ => Err(Error::from_raw_os_error(-ret)),
512 }
513 }
514}
515
516impl From<u32> for ProgramType {
517 fn from(value: u32) -> Self {
518 use ProgramType::*;
519
520 match value {
521 x if x == Unspec as u32 => Unspec,
522 x if x == SocketFilter as u32 => SocketFilter,
523 x if x == Kprobe as u32 => Kprobe,
524 x if x == SchedCls as u32 => SchedCls,
525 x if x == SchedAct as u32 => SchedAct,
526 x if x == Tracepoint as u32 => Tracepoint,
527 x if x == Xdp as u32 => Xdp,
528 x if x == PerfEvent as u32 => PerfEvent,
529 x if x == CgroupSkb as u32 => CgroupSkb,
530 x if x == CgroupSock as u32 => CgroupSock,
531 x if x == LwtIn as u32 => LwtIn,
532 x if x == LwtOut as u32 => LwtOut,
533 x if x == LwtXmit as u32 => LwtXmit,
534 x if x == SockOps as u32 => SockOps,
535 x if x == SkSkb as u32 => SkSkb,
536 x if x == CgroupDevice as u32 => CgroupDevice,
537 x if x == SkMsg as u32 => SkMsg,
538 x if x == RawTracepoint as u32 => RawTracepoint,
539 x if x == CgroupSockAddr as u32 => CgroupSockAddr,
540 x if x == LwtSeg6local as u32 => LwtSeg6local,
541 x if x == LircMode2 as u32 => LircMode2,
542 x if x == SkReuseport as u32 => SkReuseport,
543 x if x == FlowDissector as u32 => FlowDissector,
544 x if x == CgroupSysctl as u32 => CgroupSysctl,
545 x if x == RawTracepointWritable as u32 => RawTracepointWritable,
546 x if x == CgroupSockopt as u32 => CgroupSockopt,
547 x if x == Tracing as u32 => Tracing,
548 x if x == StructOps as u32 => StructOps,
549 x if x == Ext as u32 => Ext,
550 x if x == Lsm as u32 => Lsm,
551 x if x == SkLookup as u32 => SkLookup,
552 x if x == Syscall as u32 => Syscall,
553 x if x == Netfilter as u32 => Netfilter,
554 _ => Unknown,
555 }
556 }
557}
558
559#[non_exhaustive]
561#[repr(u32)]
562#[derive(Clone, Debug)]
563#[expect(missing_docs)]
565pub enum ProgramAttachType {
566 CgroupInetIngress = libbpf_sys::BPF_CGROUP_INET_INGRESS,
567 CgroupInetEgress = libbpf_sys::BPF_CGROUP_INET_EGRESS,
568 CgroupInetSockCreate = libbpf_sys::BPF_CGROUP_INET_SOCK_CREATE,
569 CgroupSockOps = libbpf_sys::BPF_CGROUP_SOCK_OPS,
570 SkSkbStreamParser = libbpf_sys::BPF_SK_SKB_STREAM_PARSER,
571 SkSkbStreamVerdict = libbpf_sys::BPF_SK_SKB_STREAM_VERDICT,
572 CgroupDevice = libbpf_sys::BPF_CGROUP_DEVICE,
573 SkMsgVerdict = libbpf_sys::BPF_SK_MSG_VERDICT,
574 CgroupInet4Bind = libbpf_sys::BPF_CGROUP_INET4_BIND,
575 CgroupInet6Bind = libbpf_sys::BPF_CGROUP_INET6_BIND,
576 CgroupInet4Connect = libbpf_sys::BPF_CGROUP_INET4_CONNECT,
577 CgroupInet6Connect = libbpf_sys::BPF_CGROUP_INET6_CONNECT,
578 CgroupInet4PostBind = libbpf_sys::BPF_CGROUP_INET4_POST_BIND,
579 CgroupInet6PostBind = libbpf_sys::BPF_CGROUP_INET6_POST_BIND,
580 CgroupUdp4Sendmsg = libbpf_sys::BPF_CGROUP_UDP4_SENDMSG,
581 CgroupUdp6Sendmsg = libbpf_sys::BPF_CGROUP_UDP6_SENDMSG,
582 LircMode2 = libbpf_sys::BPF_LIRC_MODE2,
583 FlowDissector = libbpf_sys::BPF_FLOW_DISSECTOR,
584 CgroupSysctl = libbpf_sys::BPF_CGROUP_SYSCTL,
585 CgroupUdp4Recvmsg = libbpf_sys::BPF_CGROUP_UDP4_RECVMSG,
586 CgroupUdp6Recvmsg = libbpf_sys::BPF_CGROUP_UDP6_RECVMSG,
587 CgroupGetsockopt = libbpf_sys::BPF_CGROUP_GETSOCKOPT,
588 CgroupSetsockopt = libbpf_sys::BPF_CGROUP_SETSOCKOPT,
589 TraceRawTp = libbpf_sys::BPF_TRACE_RAW_TP,
590 TraceFentry = libbpf_sys::BPF_TRACE_FENTRY,
591 TraceFexit = libbpf_sys::BPF_TRACE_FEXIT,
592 ModifyReturn = libbpf_sys::BPF_MODIFY_RETURN,
593 LsmMac = libbpf_sys::BPF_LSM_MAC,
594 TraceIter = libbpf_sys::BPF_TRACE_ITER,
595 CgroupInet4Getpeername = libbpf_sys::BPF_CGROUP_INET4_GETPEERNAME,
596 CgroupInet6Getpeername = libbpf_sys::BPF_CGROUP_INET6_GETPEERNAME,
597 CgroupInet4Getsockname = libbpf_sys::BPF_CGROUP_INET4_GETSOCKNAME,
598 CgroupInet6Getsockname = libbpf_sys::BPF_CGROUP_INET6_GETSOCKNAME,
599 XdpDevmap = libbpf_sys::BPF_XDP_DEVMAP,
600 CgroupInetSockRelease = libbpf_sys::BPF_CGROUP_INET_SOCK_RELEASE,
601 XdpCpumap = libbpf_sys::BPF_XDP_CPUMAP,
602 SkLookup = libbpf_sys::BPF_SK_LOOKUP,
603 Xdp = libbpf_sys::BPF_XDP,
604 SkSkbVerdict = libbpf_sys::BPF_SK_SKB_VERDICT,
605 SkReuseportSelect = libbpf_sys::BPF_SK_REUSEPORT_SELECT,
606 SkReuseportSelectOrMigrate = libbpf_sys::BPF_SK_REUSEPORT_SELECT_OR_MIGRATE,
607 PerfEvent = libbpf_sys::BPF_PERF_EVENT,
608 KprobeMulti = libbpf_sys::BPF_TRACE_KPROBE_MULTI,
609 NetkitPeer = libbpf_sys::BPF_NETKIT_PEER,
610 TraceUprobeMulti = libbpf_sys::BPF_TRACE_UPROBE_MULTI,
611 LsmCgroup = libbpf_sys::BPF_LSM_CGROUP,
612 TraceKprobeSession = libbpf_sys::BPF_TRACE_KPROBE_SESSION,
613 TcxIngress = libbpf_sys::BPF_TCX_INGRESS,
614 TcxEgress = libbpf_sys::BPF_TCX_EGRESS,
615 Netfilter = libbpf_sys::BPF_NETFILTER,
616 CgroupUnixGetsockname = libbpf_sys::BPF_CGROUP_UNIX_GETSOCKNAME,
617 CgroupUnixSendmsg = libbpf_sys::BPF_CGROUP_UNIX_SENDMSG,
618 NetkitPrimary = libbpf_sys::BPF_NETKIT_PRIMARY,
619 CgroupUnixRecvmsg = libbpf_sys::BPF_CGROUP_UNIX_RECVMSG,
620 CgroupUnixConnect = libbpf_sys::BPF_CGROUP_UNIX_CONNECT,
621 CgroupUnixGetpeername = libbpf_sys::BPF_CGROUP_UNIX_GETPEERNAME,
622 StructOps = libbpf_sys::BPF_STRUCT_OPS,
623 Unknown = u32::MAX,
625}
626
627impl From<u32> for ProgramAttachType {
628 fn from(value: u32) -> Self {
629 use ProgramAttachType::*;
630
631 match value {
632 x if x == CgroupInetIngress as u32 => CgroupInetIngress,
633 x if x == CgroupInetEgress as u32 => CgroupInetEgress,
634 x if x == CgroupInetSockCreate as u32 => CgroupInetSockCreate,
635 x if x == CgroupSockOps as u32 => CgroupSockOps,
636 x if x == SkSkbStreamParser as u32 => SkSkbStreamParser,
637 x if x == SkSkbStreamVerdict as u32 => SkSkbStreamVerdict,
638 x if x == CgroupDevice as u32 => CgroupDevice,
639 x if x == SkMsgVerdict as u32 => SkMsgVerdict,
640 x if x == CgroupInet4Bind as u32 => CgroupInet4Bind,
641 x if x == CgroupInet6Bind as u32 => CgroupInet6Bind,
642 x if x == CgroupInet4Connect as u32 => CgroupInet4Connect,
643 x if x == CgroupInet6Connect as u32 => CgroupInet6Connect,
644 x if x == CgroupInet4PostBind as u32 => CgroupInet4PostBind,
645 x if x == CgroupInet6PostBind as u32 => CgroupInet6PostBind,
646 x if x == CgroupUdp4Sendmsg as u32 => CgroupUdp4Sendmsg,
647 x if x == CgroupUdp6Sendmsg as u32 => CgroupUdp6Sendmsg,
648 x if x == LircMode2 as u32 => LircMode2,
649 x if x == FlowDissector as u32 => FlowDissector,
650 x if x == CgroupSysctl as u32 => CgroupSysctl,
651 x if x == CgroupUdp4Recvmsg as u32 => CgroupUdp4Recvmsg,
652 x if x == CgroupUdp6Recvmsg as u32 => CgroupUdp6Recvmsg,
653 x if x == CgroupGetsockopt as u32 => CgroupGetsockopt,
654 x if x == CgroupSetsockopt as u32 => CgroupSetsockopt,
655 x if x == TraceRawTp as u32 => TraceRawTp,
656 x if x == TraceFentry as u32 => TraceFentry,
657 x if x == TraceFexit as u32 => TraceFexit,
658 x if x == ModifyReturn as u32 => ModifyReturn,
659 x if x == LsmMac as u32 => LsmMac,
660 x if x == TraceIter as u32 => TraceIter,
661 x if x == CgroupInet4Getpeername as u32 => CgroupInet4Getpeername,
662 x if x == CgroupInet6Getpeername as u32 => CgroupInet6Getpeername,
663 x if x == CgroupInet4Getsockname as u32 => CgroupInet4Getsockname,
664 x if x == CgroupInet6Getsockname as u32 => CgroupInet6Getsockname,
665 x if x == XdpDevmap as u32 => XdpDevmap,
666 x if x == CgroupInetSockRelease as u32 => CgroupInetSockRelease,
667 x if x == XdpCpumap as u32 => XdpCpumap,
668 x if x == SkLookup as u32 => SkLookup,
669 x if x == Xdp as u32 => Xdp,
670 x if x == SkSkbVerdict as u32 => SkSkbVerdict,
671 x if x == SkReuseportSelect as u32 => SkReuseportSelect,
672 x if x == SkReuseportSelectOrMigrate as u32 => SkReuseportSelectOrMigrate,
673 x if x == PerfEvent as u32 => PerfEvent,
674 x if x == KprobeMulti as u32 => KprobeMulti,
675 x if x == NetkitPeer as u32 => NetkitPeer,
676 x if x == TraceUprobeMulti as u32 => TraceUprobeMulti,
677 x if x == LsmCgroup as u32 => LsmCgroup,
678 x if x == TraceKprobeSession as u32 => TraceKprobeSession,
679 x if x == TcxIngress as u32 => TcxIngress,
680 x if x == TcxEgress as u32 => TcxEgress,
681 x if x == Netfilter as u32 => Netfilter,
682 x if x == CgroupUnixGetsockname as u32 => CgroupUnixGetsockname,
683 x if x == CgroupUnixSendmsg as u32 => CgroupUnixSendmsg,
684 x if x == NetkitPrimary as u32 => NetkitPrimary,
685 x if x == CgroupUnixRecvmsg as u32 => CgroupUnixRecvmsg,
686 x if x == CgroupUnixConnect as u32 => CgroupUnixConnect,
687 x if x == CgroupUnixGetpeername as u32 => CgroupUnixGetpeername,
688 x if x == StructOps as u32 => StructOps,
689 _ => Unknown,
690 }
691 }
692}
693
694#[derive(Debug, Default)]
699pub struct Input<'dat> {
700 pub context_in: Option<&'dat mut [u8]>,
704 pub context_out: Option<&'dat mut [u8]>,
706 pub data_in: Option<&'dat [u8]>,
708 pub data_out: Option<&'dat mut [u8]>,
710 pub cpu: u32,
712 pub flags: u32,
714 pub repeat: u32,
717 #[doc(hidden)]
719 pub _non_exhaustive: (),
720}
721
722#[derive(Debug)]
727pub struct Output<'dat> {
728 pub return_value: u32,
730 pub context: Option<&'dat mut [u8]>,
732 pub data: Option<&'dat mut [u8]>,
734 pub duration: Duration,
736 #[doc(hidden)]
738 pub _non_exhaustive: (),
739}
740
741pub type Program<'obj> = ProgramImpl<'obj>;
743pub type ProgramMut<'obj> = ProgramImpl<'obj, Mut>;
745
746#[derive(Debug)]
754#[repr(transparent)]
755pub struct ProgramImpl<'obj, T = ()> {
756 pub(crate) ptr: NonNull<libbpf_sys::bpf_program>,
757 _phantom: PhantomData<&'obj T>,
758}
759
760impl<'obj> Program<'obj> {
761 pub fn new(prog: &'obj libbpf_sys::bpf_program) -> Self {
763 Self {
766 ptr: unsafe { NonNull::new_unchecked(prog as *const _ as *mut _) },
767 _phantom: PhantomData,
768 }
769 }
770
771 pub fn name(&self) -> &'obj OsStr {
773 let name_ptr = unsafe { libbpf_sys::bpf_program__name(self.ptr.as_ptr()) };
774 let name_c_str = unsafe { CStr::from_ptr(name_ptr) };
775 OsStr::from_bytes(name_c_str.to_bytes())
777 }
778
779 pub fn section(&self) -> &'obj OsStr {
781 let p = unsafe { libbpf_sys::bpf_program__section_name(self.ptr.as_ptr()) };
783 let section_c_str = unsafe { CStr::from_ptr(p) };
786 let section = OsStr::from_bytes(section_c_str.to_bytes());
787 section
788 }
789
790 pub fn prog_type(&self) -> ProgramType {
792 ProgramType::from(unsafe { libbpf_sys::bpf_program__type(self.ptr.as_ptr()) })
793 }
794
795 #[deprecated = "renamed to Program::fd_from_id"]
796 #[expect(missing_docs)]
797 #[inline]
798 pub fn get_fd_by_id(id: u32) -> Result<OwnedFd> {
799 Self::fd_from_id(id)
800 }
801
802 pub fn fd_from_id(id: u32) -> Result<OwnedFd> {
804 let ret = unsafe { libbpf_sys::bpf_prog_get_fd_by_id(id) };
805 let fd = util::parse_ret_i32(ret)?;
806 Ok(unsafe { OwnedFd::from_raw_fd(fd) })
810 }
811
812 pub fn id_from_fd(fd: BorrowedFd<'_>) -> Result<u32> {
814 let mut prog_info = libbpf_sys::bpf_prog_info::default();
815 let prog_info_ptr: *mut libbpf_sys::bpf_prog_info = &mut prog_info;
816 let mut len = size_of::<libbpf_sys::bpf_prog_info>() as u32;
817 let ret = unsafe {
818 libbpf_sys::bpf_obj_get_info_by_fd(
819 fd.as_raw_fd(),
820 prog_info_ptr.cast::<c_void>(),
821 &mut len,
822 )
823 };
824 util::parse_ret(ret)?;
825 Ok(prog_info.id)
826 }
827
828 pub fn fd_from_pinned_path<P: AsRef<Path>>(path: P) -> Result<OwnedFd> {
832 let path_c = util::path_to_cstring(&path)?;
833 let path_ptr = path_c.as_ptr();
834
835 let fd = unsafe { libbpf_sys::bpf_obj_get(path_ptr) };
836 let fd = util::parse_ret_i32(fd).with_context(|| {
837 format!(
838 "failed to retrieve BPF object from pinned path `{}`",
839 path.as_ref().display()
840 )
841 })?;
842 let fd = unsafe { OwnedFd::from_raw_fd(fd) };
843
844 let fd_type = util::object_type_from_fd(fd.as_fd())?;
848 match fd_type {
849 BpfObjectType::Program => Ok(fd),
850 other => Err(Error::with_invalid_data(format!(
851 "retrieved BPF fd is not a program fd: {other:#?}"
852 ))),
853 }
854 }
855
856 pub fn flags(&self) -> u32 {
858 unsafe { libbpf_sys::bpf_program__flags(self.ptr.as_ptr()) }
859 }
860
861 pub fn attach_type(&self) -> ProgramAttachType {
863 ProgramAttachType::from(unsafe {
864 libbpf_sys::bpf_program__expected_attach_type(self.ptr.as_ptr())
865 })
866 }
867
868 pub fn autoload(&self) -> bool {
870 unsafe { libbpf_sys::bpf_program__autoload(self.ptr.as_ptr()) }
871 }
872
873 pub fn log_level(&self) -> u32 {
875 unsafe { libbpf_sys::bpf_program__log_level(self.ptr.as_ptr()) }
876 }
877
878 pub fn insn_cnt(&self) -> usize {
882 unsafe { libbpf_sys::bpf_program__insn_cnt(self.ptr.as_ptr()) as usize }
883 }
884
885 pub fn insns(&self) -> &'obj [libbpf_sys::bpf_insn] {
889 let count = self.insn_cnt();
890 let ptr = unsafe { libbpf_sys::bpf_program__insns(self.ptr.as_ptr()) };
891 unsafe { slice::from_raw_parts(ptr, count) }
892 }
893}
894
895impl<'obj> ProgramMut<'obj> {
896 pub fn new_mut(prog: &'obj mut libbpf_sys::bpf_program) -> Self {
898 Self {
899 ptr: unsafe { NonNull::new_unchecked(prog as *mut _) },
900 _phantom: PhantomData,
901 }
902 }
903
904 pub fn pin<P: AsRef<Path>>(&mut self, path: P) -> Result<()> {
907 let path_c = util::path_to_cstring(path)?;
908 let path_ptr = path_c.as_ptr();
909
910 let ret = unsafe { libbpf_sys::bpf_program__pin(self.ptr.as_ptr(), path_ptr) };
911 util::parse_ret(ret)
912 }
913
914 pub fn unpin<P: AsRef<Path>>(&mut self, path: P) -> Result<()> {
917 let path_c = util::path_to_cstring(path)?;
918 let path_ptr = path_c.as_ptr();
919
920 let ret = unsafe { libbpf_sys::bpf_program__unpin(self.ptr.as_ptr(), path_ptr) };
921 util::parse_ret(ret)
922 }
923
924 pub fn attach(&self) -> Result<Link> {
926 let ptr = unsafe { libbpf_sys::bpf_program__attach(self.ptr.as_ptr()) };
927 let ptr = validate_bpf_ret(ptr).context("failed to attach BPF program")?;
928 let link = unsafe { Link::new(ptr) };
930 Ok(link)
931 }
932
933 pub fn attach_cgroup(&self, cgroup_fd: i32) -> Result<Link> {
936 let ptr = unsafe { libbpf_sys::bpf_program__attach_cgroup(self.ptr.as_ptr(), cgroup_fd) };
937 let ptr = validate_bpf_ret(ptr).context("failed to attach cgroup")?;
938 let link = unsafe { Link::new(ptr) };
940 Ok(link)
941 }
942
943 pub fn attach_perf_event(&self, pfd: i32) -> Result<Link> {
945 let ptr = unsafe { libbpf_sys::bpf_program__attach_perf_event(self.ptr.as_ptr(), pfd) };
946 let ptr = validate_bpf_ret(ptr).context("failed to attach perf event")?;
947 let link = unsafe { Link::new(ptr) };
949 Ok(link)
950 }
951
952 pub fn attach_perf_event_with_opts(&self, pfd: i32, opts: PerfEventOpts) -> Result<Link> {
955 let libbpf_opts = libbpf_sys::bpf_perf_event_opts::from(opts);
956 let ptr = unsafe {
957 libbpf_sys::bpf_program__attach_perf_event_opts(self.ptr.as_ptr(), pfd, &libbpf_opts)
958 };
959 let ptr = validate_bpf_ret(ptr).context("failed to attach perf event")?;
960 let link = unsafe { Link::new(ptr) };
962 Ok(link)
963 }
964
965 pub fn attach_uprobe<T: AsRef<Path>>(
968 &self,
969 retprobe: bool,
970 pid: i32,
971 binary_path: T,
972 func_offset: usize,
973 ) -> Result<Link> {
974 let path = util::path_to_cstring(binary_path)?;
975 let path_ptr = path.as_ptr();
976 let ptr = unsafe {
977 libbpf_sys::bpf_program__attach_uprobe(
978 self.ptr.as_ptr(),
979 retprobe,
980 pid,
981 path_ptr,
982 func_offset as libbpf_sys::size_t,
983 )
984 };
985 let ptr = validate_bpf_ret(ptr).context("failed to attach uprobe")?;
986 let link = unsafe { Link::new(ptr) };
988 Ok(link)
989 }
990
991 pub fn attach_uprobe_with_opts(
995 &self,
996 pid: i32,
997 binary_path: impl AsRef<Path>,
998 func_offset: usize,
999 opts: UprobeOpts,
1000 ) -> Result<Link> {
1001 let path = util::path_to_cstring(binary_path)?;
1002 let path_ptr = path.as_ptr();
1003 let UprobeOpts {
1004 ref_ctr_offset,
1005 cookie,
1006 retprobe,
1007 func_name,
1008 _non_exhaustive,
1009 } = opts;
1010
1011 let func_name: Option<CString> = if let Some(func_name) = func_name {
1012 Some(util::str_to_cstring(&func_name)?)
1013 } else {
1014 None
1015 };
1016 let ptr = func_name
1017 .as_ref()
1018 .map_or(ptr::null(), |func_name| func_name.as_ptr());
1019 let opts = libbpf_sys::bpf_uprobe_opts {
1020 sz: size_of::<libbpf_sys::bpf_uprobe_opts>() as _,
1021 ref_ctr_offset: ref_ctr_offset as libbpf_sys::size_t,
1022 bpf_cookie: cookie,
1023 retprobe,
1024 func_name: ptr,
1025 ..Default::default()
1026 };
1027
1028 let ptr = unsafe {
1029 libbpf_sys::bpf_program__attach_uprobe_opts(
1030 self.ptr.as_ptr(),
1031 pid,
1032 path_ptr,
1033 func_offset as libbpf_sys::size_t,
1034 &opts as *const _,
1035 )
1036 };
1037 let ptr = validate_bpf_ret(ptr).context("failed to attach uprobe")?;
1038 let link = unsafe { Link::new(ptr) };
1040 Ok(link)
1041 }
1042
1043 pub fn attach_uprobe_multi(
1046 &self,
1047 pid: i32,
1048 binary_path: impl AsRef<Path>,
1049 func_pattern: impl AsRef<str>,
1050 retprobe: bool,
1051 session: bool,
1052 ) -> Result<Link> {
1053 let opts = UprobeMultiOpts {
1054 syms: Vec::new(),
1055 offsets: Vec::new(),
1056 ref_ctr_offsets: Vec::new(),
1057 cookies: Vec::new(),
1058 retprobe,
1059 session,
1060 _non_exhaustive: (),
1061 };
1062
1063 self.attach_uprobe_multi_with_opts(pid, binary_path, func_pattern, opts)
1064 }
1065
1066 pub fn attach_uprobe_multi_with_opts(
1070 &self,
1071 pid: i32,
1072 binary_path: impl AsRef<Path>,
1073 func_pattern: impl AsRef<str>,
1074 opts: UprobeMultiOpts,
1075 ) -> Result<Link> {
1076 let path = util::path_to_cstring(binary_path)?;
1077 let path_ptr = path.as_ptr();
1078
1079 let UprobeMultiOpts {
1080 syms,
1081 offsets,
1082 ref_ctr_offsets,
1083 cookies,
1084 retprobe,
1085 session,
1086 _non_exhaustive,
1087 } = opts;
1088
1089 let pattern = util::str_to_cstring(func_pattern.as_ref())?;
1090 let pattern_ptr = if pattern.is_empty() {
1092 ptr::null()
1093 } else {
1094 pattern.as_ptr()
1095 };
1096
1097 let syms_cstrings = syms
1098 .iter()
1099 .map(|s| util::str_to_cstring(s))
1100 .collect::<Result<Vec<_>>>()?;
1101 let syms_ptrs = syms_cstrings
1102 .iter()
1103 .map(|cs| cs.as_ptr())
1104 .collect::<Vec<_>>();
1105 let syms_ptr = if !syms_ptrs.is_empty() {
1106 syms_ptrs.as_ptr()
1107 } else {
1108 ptr::null()
1109 };
1110 let offsets_ptr = if !offsets.is_empty() {
1111 offsets.as_ptr()
1112 } else {
1113 ptr::null()
1114 };
1115 let ref_ctr_offsets_ptr = if !ref_ctr_offsets.is_empty() {
1116 ref_ctr_offsets.as_ptr()
1117 } else {
1118 ptr::null()
1119 };
1120 let cookies_ptr = if !cookies.is_empty() {
1121 cookies.as_ptr()
1122 } else {
1123 ptr::null()
1124 };
1125 let cnt = if !syms.is_empty() {
1126 syms.len()
1127 } else if !offsets.is_empty() {
1128 offsets.len()
1129 } else {
1130 0
1131 };
1132
1133 let c_opts = libbpf_sys::bpf_uprobe_multi_opts {
1134 sz: size_of::<libbpf_sys::bpf_uprobe_multi_opts>() as _,
1135 syms: syms_ptr.cast_mut(),
1136 offsets: offsets_ptr.cast(),
1137 ref_ctr_offsets: ref_ctr_offsets_ptr.cast(),
1138 cookies: cookies_ptr.cast(),
1139 cnt: cnt as libbpf_sys::size_t,
1140 retprobe,
1141 session,
1142 ..Default::default()
1143 };
1144
1145 let ptr = unsafe {
1146 libbpf_sys::bpf_program__attach_uprobe_multi(
1147 self.ptr.as_ptr(),
1148 pid,
1149 path_ptr,
1150 pattern_ptr,
1151 &c_opts as *const _,
1152 )
1153 };
1154
1155 let ptr = validate_bpf_ret(ptr).context("failed to attach uprobe multi")?;
1156 let link = unsafe { Link::new(ptr) };
1158 Ok(link)
1159 }
1160
1161 pub fn attach_kprobe<T: AsRef<str>>(&self, retprobe: bool, func_name: T) -> Result<Link> {
1164 let func_name = util::str_to_cstring(func_name.as_ref())?;
1165 let func_name_ptr = func_name.as_ptr();
1166 let ptr = unsafe {
1167 libbpf_sys::bpf_program__attach_kprobe(self.ptr.as_ptr(), retprobe, func_name_ptr)
1168 };
1169 let ptr = validate_bpf_ret(ptr).context("failed to attach kprobe")?;
1170 let link = unsafe { Link::new(ptr) };
1172 Ok(link)
1173 }
1174
1175 pub fn attach_kprobe_with_opts<T: AsRef<str>>(
1179 &self,
1180 retprobe: bool,
1181 func_name: T,
1182 opts: KprobeOpts,
1183 ) -> Result<Link> {
1184 let func_name = util::str_to_cstring(func_name.as_ref())?;
1185 let func_name_ptr = func_name.as_ptr();
1186
1187 let mut opts = libbpf_sys::bpf_kprobe_opts::from(opts);
1188 opts.retprobe = retprobe;
1189
1190 let ptr = unsafe {
1191 libbpf_sys::bpf_program__attach_kprobe_opts(
1192 self.ptr.as_ptr(),
1193 func_name_ptr,
1194 &opts as *const _,
1195 )
1196 };
1197 let ptr = validate_bpf_ret(ptr).context("failed to attach kprobe")?;
1198 let link = unsafe { Link::new(ptr) };
1200 Ok(link)
1201 }
1202
1203 fn check_kprobe_multi_args<T: AsRef<str>>(symbols: &[T], cookies: &[u64]) -> Result<usize> {
1204 if symbols.is_empty() {
1205 return Err(Error::with_invalid_input("Symbols list cannot be empty"));
1206 }
1207
1208 if !cookies.is_empty() && symbols.len() != cookies.len() {
1209 return Err(Error::with_invalid_input(
1210 "Symbols and cookies list must have the same size",
1211 ));
1212 }
1213
1214 Ok(symbols.len())
1215 }
1216
1217 fn attach_kprobe_multi_impl(&self, opts: libbpf_sys::bpf_kprobe_multi_opts) -> Result<Link> {
1218 let ptr = unsafe {
1219 libbpf_sys::bpf_program__attach_kprobe_multi_opts(
1220 self.ptr.as_ptr(),
1221 ptr::null(),
1222 &opts as *const _,
1223 )
1224 };
1225 let ptr = validate_bpf_ret(ptr).context("failed to attach kprobe multi")?;
1226 let link = unsafe { Link::new(ptr) };
1228 Ok(link)
1229 }
1230
1231 pub fn attach_kprobe_multi<T: AsRef<str>>(
1235 &self,
1236 retprobe: bool,
1237 symbols: Vec<T>,
1238 ) -> Result<Link> {
1239 let cnt = Self::check_kprobe_multi_args(&symbols, &[])?;
1240
1241 let csyms = symbols
1242 .iter()
1243 .map(|s| util::str_to_cstring(s.as_ref()))
1244 .collect::<Result<Vec<_>>>()?;
1245 let mut syms = csyms.iter().map(|s| s.as_ptr()).collect::<Vec<_>>();
1246
1247 let opts = libbpf_sys::bpf_kprobe_multi_opts {
1248 sz: size_of::<libbpf_sys::bpf_kprobe_multi_opts>() as _,
1249 syms: syms.as_mut_ptr().cast(),
1250 cnt: cnt as libbpf_sys::size_t,
1251 retprobe,
1252 ..Default::default()
1254 };
1255
1256 self.attach_kprobe_multi_impl(opts)
1257 }
1258
1259 pub fn attach_kprobe_multi_with_opts(&self, opts: KprobeMultiOpts) -> Result<Link> {
1263 let KprobeMultiOpts {
1264 symbols,
1265 mut cookies,
1266 retprobe,
1267 _non_exhaustive,
1268 } = opts;
1269
1270 let cnt = Self::check_kprobe_multi_args(&symbols, &cookies)?;
1271
1272 let csyms = symbols
1273 .iter()
1274 .map(|s| util::str_to_cstring(s.as_ref()))
1275 .collect::<Result<Vec<_>>>()?;
1276 let mut syms = csyms.iter().map(|s| s.as_ptr()).collect::<Vec<_>>();
1277
1278 let opts = libbpf_sys::bpf_kprobe_multi_opts {
1279 sz: size_of::<libbpf_sys::bpf_kprobe_multi_opts>() as _,
1280 syms: syms.as_mut_ptr().cast(),
1281 cookies: if !cookies.is_empty() {
1282 cookies.as_mut_ptr().cast()
1283 } else {
1284 ptr::null()
1285 },
1286 cnt: cnt as libbpf_sys::size_t,
1287 retprobe,
1288 ..Default::default()
1290 };
1291
1292 self.attach_kprobe_multi_impl(opts)
1293 }
1294
1295 pub fn attach_ksyscall<T: AsRef<str>>(&self, retprobe: bool, syscall_name: T) -> Result<Link> {
1297 let opts = libbpf_sys::bpf_ksyscall_opts {
1298 sz: size_of::<libbpf_sys::bpf_ksyscall_opts>() as _,
1299 retprobe,
1300 ..Default::default()
1301 };
1302
1303 let syscall_name = util::str_to_cstring(syscall_name.as_ref())?;
1304 let syscall_name_ptr = syscall_name.as_ptr();
1305 let ptr = unsafe {
1306 libbpf_sys::bpf_program__attach_ksyscall(self.ptr.as_ptr(), syscall_name_ptr, &opts)
1307 };
1308 let ptr = validate_bpf_ret(ptr).context("failed to attach ksyscall")?;
1309 let link = unsafe { Link::new(ptr) };
1311 Ok(link)
1312 }
1313
1314 fn attach_tracepoint_impl(
1315 &self,
1316 tp_category: &str,
1317 tp_name: &str,
1318 tp_opts: Option<TracepointOpts>,
1319 ) -> Result<Link> {
1320 let tp_category = util::str_to_cstring(tp_category)?;
1321 let tp_category_ptr = tp_category.as_ptr();
1322 let tp_name = util::str_to_cstring(tp_name)?;
1323 let tp_name_ptr = tp_name.as_ptr();
1324
1325 let tp_opts = tp_opts.map(libbpf_sys::bpf_tracepoint_opts::from);
1326 let opts = tp_opts.as_ref().map_or(ptr::null(), |opts| opts);
1327 let ptr = unsafe {
1328 libbpf_sys::bpf_program__attach_tracepoint_opts(
1329 self.ptr.as_ptr(),
1330 tp_category_ptr,
1331 tp_name_ptr,
1332 opts.cast(),
1333 )
1334 };
1335
1336 let ptr = validate_bpf_ret(ptr).context("failed to attach tracepoint")?;
1337 let link = unsafe { Link::new(ptr) };
1339 Ok(link)
1340 }
1341
1342 pub fn attach_tracepoint(
1345 &self,
1346 tp_category: TracepointCategory,
1347 tp_name: impl AsRef<str>,
1348 ) -> Result<Link> {
1349 self.attach_tracepoint_impl(tp_category.as_ref(), tp_name.as_ref(), None)
1350 }
1351
1352 pub fn attach_tracepoint_with_opts(
1356 &self,
1357 tp_category: TracepointCategory,
1358 tp_name: impl AsRef<str>,
1359 tp_opts: TracepointOpts,
1360 ) -> Result<Link> {
1361 self.attach_tracepoint_impl(tp_category.as_ref(), tp_name.as_ref(), Some(tp_opts))
1362 }
1363
1364 pub fn attach_raw_tracepoint<T: AsRef<str>>(&self, tp_name: T) -> Result<Link> {
1367 let tp_name = util::str_to_cstring(tp_name.as_ref())?;
1368 let tp_name_ptr = tp_name.as_ptr();
1369 let ptr = unsafe {
1370 libbpf_sys::bpf_program__attach_raw_tracepoint(self.ptr.as_ptr(), tp_name_ptr)
1371 };
1372 let ptr = validate_bpf_ret(ptr).context("failed to attach raw tracepoint")?;
1373 let link = unsafe { Link::new(ptr) };
1375 Ok(link)
1376 }
1377
1378 pub fn attach_raw_tracepoint_with_opts<T: AsRef<str>>(
1382 &self,
1383 tp_name: T,
1384 tp_opts: RawTracepointOpts,
1385 ) -> Result<Link> {
1386 let tp_name = util::str_to_cstring(tp_name.as_ref())?;
1387 let tp_name_ptr = tp_name.as_ptr();
1388 let mut tp_opts = libbpf_sys::bpf_raw_tracepoint_opts::from(tp_opts);
1389 let ptr = unsafe {
1390 libbpf_sys::bpf_program__attach_raw_tracepoint_opts(
1391 self.ptr.as_ptr(),
1392 tp_name_ptr,
1393 &mut tp_opts as *mut _,
1394 )
1395 };
1396 let ptr = validate_bpf_ret(ptr).context("failed to attach raw tracepoint")?;
1397 let link = unsafe { Link::new(ptr) };
1399 Ok(link)
1400 }
1401
1402 pub fn attach_lsm(&self) -> Result<Link> {
1404 let ptr = unsafe { libbpf_sys::bpf_program__attach_lsm(self.ptr.as_ptr()) };
1405 let ptr = validate_bpf_ret(ptr).context("failed to attach LSM")?;
1406 let link = unsafe { Link::new(ptr) };
1408 Ok(link)
1409 }
1410
1411 pub fn attach_trace(&self) -> Result<Link> {
1413 let ptr = unsafe { libbpf_sys::bpf_program__attach_trace(self.ptr.as_ptr()) };
1414 let ptr = validate_bpf_ret(ptr).context("failed to attach fentry/fexit kernel probe")?;
1415 let link = unsafe { Link::new(ptr) };
1417 Ok(link)
1418 }
1419
1420 pub fn attach_sockmap(&self, map_fd: i32) -> Result<()> {
1422 let err = unsafe {
1423 libbpf_sys::bpf_prog_attach(
1424 self.as_fd().as_raw_fd(),
1425 map_fd,
1426 self.attach_type() as u32,
1427 0,
1428 )
1429 };
1430 util::parse_ret(err)
1431 }
1432
1433 pub fn attach_xdp(&self, ifindex: i32) -> Result<Link> {
1435 let ptr = unsafe { libbpf_sys::bpf_program__attach_xdp(self.ptr.as_ptr(), ifindex) };
1436 let ptr = validate_bpf_ret(ptr).context("failed to attach XDP program")?;
1437 let link = unsafe { Link::new(ptr) };
1439 Ok(link)
1440 }
1441
1442 pub fn attach_netns(&self, netns_fd: i32) -> Result<Link> {
1444 let ptr = unsafe { libbpf_sys::bpf_program__attach_netns(self.ptr.as_ptr(), netns_fd) };
1445 let ptr = validate_bpf_ret(ptr).context("failed to attach network namespace program")?;
1446 let link = unsafe { Link::new(ptr) };
1448 Ok(link)
1449 }
1450
1451 pub fn attach_netfilter_with_opts(
1453 &self,
1454 netfilter_opt: netfilter::NetfilterOpts,
1455 ) -> Result<Link> {
1456 let netfilter_opts = libbpf_sys::bpf_netfilter_opts::from(netfilter_opt);
1457
1458 let ptr = unsafe {
1459 libbpf_sys::bpf_program__attach_netfilter(
1460 self.ptr.as_ptr(),
1461 &netfilter_opts as *const _,
1462 )
1463 };
1464
1465 let ptr = validate_bpf_ret(ptr).context("failed to attach netfilter program")?;
1466 let link = unsafe { Link::new(ptr) };
1468 Ok(link)
1469 }
1470
1471 fn attach_usdt_impl(
1472 &self,
1473 pid: i32,
1474 binary_path: &Path,
1475 usdt_provider: &str,
1476 usdt_name: &str,
1477 usdt_opts: Option<UsdtOpts>,
1478 ) -> Result<Link> {
1479 let path = util::path_to_cstring(binary_path)?;
1480 let path_ptr = path.as_ptr();
1481 let usdt_provider = util::str_to_cstring(usdt_provider)?;
1482 let usdt_provider_ptr = usdt_provider.as_ptr();
1483 let usdt_name = util::str_to_cstring(usdt_name)?;
1484 let usdt_name_ptr = usdt_name.as_ptr();
1485 let usdt_opts = usdt_opts.map(libbpf_sys::bpf_usdt_opts::from);
1486 let usdt_opts_ptr = usdt_opts
1487 .as_ref()
1488 .map(|opts| opts as *const _)
1489 .unwrap_or_else(ptr::null);
1490
1491 let ptr = unsafe {
1492 libbpf_sys::bpf_program__attach_usdt(
1493 self.ptr.as_ptr(),
1494 pid,
1495 path_ptr,
1496 usdt_provider_ptr,
1497 usdt_name_ptr,
1498 usdt_opts_ptr,
1499 )
1500 };
1501 let ptr = validate_bpf_ret(ptr).context("failed to attach USDT")?;
1502 let link = unsafe { Link::new(ptr) };
1504 Ok(link)
1505 }
1506
1507 pub fn attach_usdt(
1511 &self,
1512 pid: i32,
1513 binary_path: impl AsRef<Path>,
1514 usdt_provider: impl AsRef<str>,
1515 usdt_name: impl AsRef<str>,
1516 ) -> Result<Link> {
1517 self.attach_usdt_impl(
1518 pid,
1519 binary_path.as_ref(),
1520 usdt_provider.as_ref(),
1521 usdt_name.as_ref(),
1522 None,
1523 )
1524 }
1525
1526 pub fn attach_usdt_with_opts(
1530 &self,
1531 pid: i32,
1532 binary_path: impl AsRef<Path>,
1533 usdt_provider: impl AsRef<str>,
1534 usdt_name: impl AsRef<str>,
1535 usdt_opts: UsdtOpts,
1536 ) -> Result<Link> {
1537 self.attach_usdt_impl(
1538 pid,
1539 binary_path.as_ref(),
1540 usdt_provider.as_ref(),
1541 usdt_name.as_ref(),
1542 Some(usdt_opts),
1543 )
1544 }
1545
1546 pub fn attach_iter(&self, map_fd: BorrowedFd<'_>) -> Result<Link> {
1550 let map_opts = MapIterOpts {
1551 fd: map_fd,
1552 _non_exhaustive: (),
1553 };
1554 self.attach_iter_with_opts(IterOpts::Map(map_opts))
1555 }
1556
1557 pub fn attach_iter_with_opts(&self, opts: IterOpts<'_>) -> Result<Link> {
1563 let mut linkinfo = match opts {
1564 IterOpts::None => None,
1565 IterOpts::Map(map_opts) => {
1566 let MapIterOpts {
1567 fd,
1568 _non_exhaustive: (),
1569 } = map_opts;
1570
1571 let mut linkinfo = libbpf_sys::bpf_iter_link_info::default();
1572 linkinfo.map.map_fd = fd.as_raw_fd() as _;
1573 Some(linkinfo)
1574 }
1575 IterOpts::Cgroup(cgroup_opts) => {
1576 let CgroupIterOpts {
1577 fd,
1578 order,
1579 _non_exhaustive: (),
1580 } = cgroup_opts;
1581
1582 let mut linkinfo = libbpf_sys::bpf_iter_link_info::default();
1583 linkinfo.cgroup.order = order as libbpf_sys::bpf_cgroup_iter_order;
1584 linkinfo.cgroup.cgroup_fd = fd.as_raw_fd() as _;
1585 Some(linkinfo)
1586 }
1587 };
1588 let (linkinfo_ptr, linkinfo_len) = match &mut linkinfo {
1589 Some(info) => (
1590 info as *mut _,
1591 size_of::<libbpf_sys::bpf_iter_link_info>() as _,
1592 ),
1593 None => (ptr::null_mut(), 0),
1594 };
1595
1596 let attach_opt = libbpf_sys::bpf_iter_attach_opts {
1597 link_info: linkinfo_ptr,
1598 link_info_len: linkinfo_len,
1599 sz: size_of::<libbpf_sys::bpf_iter_attach_opts>() as _,
1600 ..Default::default()
1601 };
1602 let ptr = unsafe {
1603 libbpf_sys::bpf_program__attach_iter(
1604 self.ptr.as_ptr(),
1605 &attach_opt as *const libbpf_sys::bpf_iter_attach_opts,
1606 )
1607 };
1608
1609 let ptr = validate_bpf_ret(ptr).context("failed to attach iterator")?;
1610 let link = unsafe { Link::new(ptr) };
1612 Ok(link)
1613 }
1614
1615 pub fn assoc_struct_ops(&self, map: &Map<'_>) -> Result<()> {
1624 let ret = unsafe {
1625 libbpf_sys::bpf_program__assoc_struct_ops(
1626 self.ptr.as_ptr(),
1627 map.as_libbpf_object().as_ptr(),
1628 ptr::null_mut(),
1629 )
1630 };
1631 util::parse_ret(ret).context("failed to associate program with struct_ops map")
1632 }
1633
1634 pub fn test_run<'dat>(&self, input: Input<'dat>) -> Result<Output<'dat>> {
1640 unsafe fn slice_from_array<'t, T>(items: *mut T, num_items: usize) -> Option<&'t mut [T]> {
1641 if items.is_null() {
1642 None
1643 } else {
1644 Some(unsafe { slice::from_raw_parts_mut(items, num_items) })
1645 }
1646 }
1647
1648 let Input {
1649 context_in,
1650 mut context_out,
1651 data_in,
1652 mut data_out,
1653 cpu,
1654 flags,
1655 repeat,
1656 _non_exhaustive: (),
1657 } = input;
1658
1659 let mut opts = unsafe { mem::zeroed::<libbpf_sys::bpf_test_run_opts>() };
1660 opts.sz = size_of_val(&opts) as _;
1661 opts.ctx_in = context_in
1662 .as_ref()
1663 .map(|data| data.as_ptr().cast())
1664 .unwrap_or_else(ptr::null);
1665 opts.ctx_size_in = context_in.map(|data| data.len() as _).unwrap_or(0);
1666 opts.ctx_out = context_out
1667 .as_mut()
1668 .map(|data| data.as_mut_ptr().cast())
1669 .unwrap_or_else(ptr::null_mut);
1670 opts.ctx_size_out = context_out.map(|data| data.len() as _).unwrap_or(0);
1671 opts.data_in = data_in
1672 .map(|data| data.as_ptr().cast())
1673 .unwrap_or_else(ptr::null);
1674 opts.data_size_in = data_in.map(|data| data.len() as _).unwrap_or(0);
1675 opts.data_out = data_out
1676 .as_mut()
1677 .map(|data| data.as_mut_ptr().cast())
1678 .unwrap_or_else(ptr::null_mut);
1679 opts.data_size_out = data_out.map(|data| data.len() as _).unwrap_or(0);
1680 opts.cpu = cpu;
1681 opts.flags = flags;
1682 opts.repeat = repeat as i32;
1685
1686 let rc = unsafe { libbpf_sys::bpf_prog_test_run_opts(self.as_fd().as_raw_fd(), &mut opts) };
1687 let () = util::parse_ret(rc)?;
1688 let output = Output {
1689 return_value: opts.retval,
1690 context: unsafe { slice_from_array(opts.ctx_out.cast(), opts.ctx_size_out as _) },
1691 data: unsafe { slice_from_array(opts.data_out.cast(), opts.data_size_out as _) },
1692 duration: Duration::from_nanos(opts.duration.into()),
1693 _non_exhaustive: (),
1694 };
1695 Ok(output)
1696 }
1697
1698 pub fn stdout(&self) -> impl Read + '_ {
1700 Stream::new(self.as_fd(), Stream::BPF_STDOUT)
1701 }
1702
1703 pub fn stderr(&self) -> impl Read + '_ {
1705 Stream::new(self.as_fd(), Stream::BPF_STDERR)
1706 }
1707}
1708
1709impl<'obj> Deref for ProgramMut<'obj> {
1710 type Target = Program<'obj>;
1711
1712 fn deref(&self) -> &Self::Target {
1713 unsafe { transmute::<&ProgramMut<'obj>, &Program<'obj>>(self) }
1716 }
1717}
1718
1719impl<T> AsFd for ProgramImpl<'_, T> {
1720 fn as_fd(&self) -> BorrowedFd<'_> {
1721 let fd = unsafe { libbpf_sys::bpf_program__fd(self.ptr.as_ptr()) };
1722 unsafe { BorrowedFd::borrow_raw(fd) }
1723 }
1724}
1725
1726impl<T> AsRawLibbpf for ProgramImpl<'_, T> {
1727 type LibbpfType = libbpf_sys::bpf_program;
1728
1729 fn as_libbpf_object(&self) -> NonNull<Self::LibbpfType> {
1731 self.ptr
1732 }
1733}
1734
1735#[derive(Debug)]
1740pub struct ProgramHandle {
1741 fd: OwnedFd,
1742 name: OsString,
1743 ty: ProgramType,
1744 tag: [u8; 8],
1745 id: u32,
1746}
1747
1748impl ProgramHandle {
1749 fn from_fd(fd: OwnedFd) -> Result<Self> {
1750 let mut info = libbpf_sys::bpf_prog_info::default();
1751 let mut len = size_of::<libbpf_sys::bpf_prog_info>() as u32;
1752 let ret = unsafe {
1753 libbpf_sys::bpf_obj_get_info_by_fd(
1754 fd.as_raw_fd(),
1755 (&mut info as *mut libbpf_sys::bpf_prog_info).cast::<c_void>(),
1756 &mut len,
1757 )
1758 };
1759 util::parse_ret(ret)?;
1760
1761 let name_cstr = util::c_char_slice_to_cstr(&info.name)
1762 .ok_or_else(|| Error::with_invalid_data("program name not NUL-terminated"))?;
1763 let name = OsStr::from_bytes(name_cstr.to_bytes()).to_os_string();
1764
1765 Ok(Self {
1766 fd,
1767 name,
1768 ty: ProgramType::from(info.type_),
1769 tag: info.tag,
1770 id: info.id,
1771 })
1772 }
1773
1774 pub fn from_prog_id(id: u32) -> Result<Self> {
1776 Self::from_fd(Program::fd_from_id(id)?)
1777 }
1778
1779 pub fn from_pinned_path<P: AsRef<Path>>(path: P) -> Result<Self> {
1781 let fd = Program::fd_from_pinned_path(path)?;
1782 Self::from_fd(fd)
1783 }
1784
1785 #[inline]
1787 pub fn name(&self) -> &OsStr {
1788 &self.name
1789 }
1790
1791 #[inline]
1793 pub fn prog_type(&self) -> ProgramType {
1794 self.ty
1795 }
1796
1797 #[inline]
1799 pub fn tag(&self) -> [u8; 8] {
1800 self.tag
1801 }
1802
1803 #[inline]
1805 pub fn id(&self) -> u32 {
1806 self.id
1807 }
1808
1809 pub fn pin<P: AsRef<Path>>(&self, path: P) -> Result<()> {
1812 let path_c = util::path_to_cstring(path)?;
1813 let ret = unsafe { libbpf_sys::bpf_obj_pin(self.fd.as_raw_fd(), path_c.as_ptr()) };
1814 util::parse_ret(ret)
1815 }
1816
1817 pub fn unpin<P: AsRef<Path>>(&self, path: P) -> Result<()> {
1820 remove_file(path).context("failed to remove pinned program")
1821 }
1822}
1823
1824impl AsFd for ProgramHandle {
1825 #[inline]
1826 fn as_fd(&self) -> BorrowedFd<'_> {
1827 self.fd.as_fd()
1828 }
1829}
1830
1831impl<'obj, T> TryFrom<&ProgramImpl<'obj, T>> for ProgramHandle
1832where
1833 ProgramImpl<'obj, T>: Deref<Target = Program<'obj>>,
1834{
1835 type Error = Error;
1836
1837 fn try_from(prog: &ProgramImpl<'obj, T>) -> Result<Self> {
1838 let fd = prog
1839 .as_fd()
1840 .try_clone_to_owned()
1841 .context("failed to duplicate program file descriptor")?;
1842 Ok(Self {
1843 name: prog.name().to_os_string(),
1844 ..Self::from_fd(fd)?
1845 })
1846 }
1847}
1848
1849impl TryFrom<&Self> for ProgramHandle {
1850 type Error = Error;
1851
1852 fn try_from(other: &Self) -> Result<Self> {
1853 Ok(Self {
1854 fd: other
1855 .as_fd()
1856 .try_clone_to_owned()
1857 .context("failed to duplicate program file descriptor")?,
1858 name: other.name.clone(),
1859 ty: other.ty,
1860 tag: other.tag,
1861 id: other.id,
1862 })
1863 }
1864}
1865
1866#[cfg(test)]
1867mod tests {
1868 use super::*;
1869
1870 use std::mem::discriminant;
1871
1872 #[test]
1873 fn program_type() {
1874 use ProgramType::*;
1875
1876 for t in [
1877 Unspec,
1878 SocketFilter,
1879 Kprobe,
1880 SchedCls,
1881 SchedAct,
1882 Tracepoint,
1883 Xdp,
1884 PerfEvent,
1885 CgroupSkb,
1886 CgroupSock,
1887 LwtIn,
1888 LwtOut,
1889 LwtXmit,
1890 SockOps,
1891 SkSkb,
1892 CgroupDevice,
1893 SkMsg,
1894 RawTracepoint,
1895 CgroupSockAddr,
1896 LwtSeg6local,
1897 LircMode2,
1898 SkReuseport,
1899 FlowDissector,
1900 CgroupSysctl,
1901 RawTracepointWritable,
1902 CgroupSockopt,
1903 Tracing,
1904 StructOps,
1905 Ext,
1906 Lsm,
1907 SkLookup,
1908 Syscall,
1909 Netfilter,
1910 Unknown,
1911 ] {
1912 assert_eq!(discriminant(&t), discriminant(&ProgramType::from(t as u32)));
1914 }
1915 }
1916
1917 #[test]
1918 fn program_attach_type() {
1919 use ProgramAttachType::*;
1920
1921 for t in [
1922 CgroupInetIngress,
1923 CgroupInetEgress,
1924 CgroupInetSockCreate,
1925 CgroupSockOps,
1926 SkSkbStreamParser,
1927 SkSkbStreamVerdict,
1928 CgroupDevice,
1929 SkMsgVerdict,
1930 CgroupInet4Bind,
1931 CgroupInet6Bind,
1932 CgroupInet4Connect,
1933 CgroupInet6Connect,
1934 CgroupInet4PostBind,
1935 CgroupInet6PostBind,
1936 CgroupUdp4Sendmsg,
1937 CgroupUdp6Sendmsg,
1938 LircMode2,
1939 FlowDissector,
1940 CgroupSysctl,
1941 CgroupUdp4Recvmsg,
1942 CgroupUdp6Recvmsg,
1943 CgroupGetsockopt,
1944 CgroupSetsockopt,
1945 TraceRawTp,
1946 TraceFentry,
1947 TraceFexit,
1948 ModifyReturn,
1949 LsmMac,
1950 TraceIter,
1951 CgroupInet4Getpeername,
1952 CgroupInet6Getpeername,
1953 CgroupInet4Getsockname,
1954 CgroupInet6Getsockname,
1955 XdpDevmap,
1956 CgroupInetSockRelease,
1957 XdpCpumap,
1958 SkLookup,
1959 Xdp,
1960 SkSkbVerdict,
1961 SkReuseportSelect,
1962 SkReuseportSelectOrMigrate,
1963 PerfEvent,
1964 Unknown,
1965 ] {
1966 assert_eq!(
1968 discriminant(&t),
1969 discriminant(&ProgramAttachType::from(t as u32))
1970 );
1971 }
1972 }
1973}