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, 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 Unknown = u32::MAX,
505}
506
507impl ProgramType {
508 pub fn is_supported(&self) -> Result<bool> {
513 let ret = unsafe { libbpf_sys::libbpf_probe_bpf_prog_type(*self as u32, ptr::null()) };
514 match ret {
515 0 => Ok(false),
516 1 => Ok(true),
517 _ => Err(Error::from_raw_os_error(-ret)),
518 }
519 }
520
521 pub fn is_helper_supported(&self, helper_id: bpf_func_id) -> Result<bool> {
527 let ret =
528 unsafe { libbpf_sys::libbpf_probe_bpf_helper(*self as u32, helper_id, ptr::null()) };
529 match ret {
530 0 => Ok(false),
531 1 => Ok(true),
532 _ => Err(Error::from_raw_os_error(-ret)),
533 }
534 }
535}
536
537impl From<u32> for ProgramType {
538 fn from(value: u32) -> Self {
539 use ProgramType::*;
540
541 match value {
542 x if x == Unspec as u32 => Unspec,
543 x if x == SocketFilter as u32 => SocketFilter,
544 x if x == Kprobe as u32 => Kprobe,
545 x if x == SchedCls as u32 => SchedCls,
546 x if x == SchedAct as u32 => SchedAct,
547 x if x == Tracepoint as u32 => Tracepoint,
548 x if x == Xdp as u32 => Xdp,
549 x if x == PerfEvent as u32 => PerfEvent,
550 x if x == CgroupSkb as u32 => CgroupSkb,
551 x if x == CgroupSock as u32 => CgroupSock,
552 x if x == LwtIn as u32 => LwtIn,
553 x if x == LwtOut as u32 => LwtOut,
554 x if x == LwtXmit as u32 => LwtXmit,
555 x if x == SockOps as u32 => SockOps,
556 x if x == SkSkb as u32 => SkSkb,
557 x if x == CgroupDevice as u32 => CgroupDevice,
558 x if x == SkMsg as u32 => SkMsg,
559 x if x == RawTracepoint as u32 => RawTracepoint,
560 x if x == CgroupSockAddr as u32 => CgroupSockAddr,
561 x if x == LwtSeg6local as u32 => LwtSeg6local,
562 x if x == LircMode2 as u32 => LircMode2,
563 x if x == SkReuseport as u32 => SkReuseport,
564 x if x == FlowDissector as u32 => FlowDissector,
565 x if x == CgroupSysctl as u32 => CgroupSysctl,
566 x if x == RawTracepointWritable as u32 => RawTracepointWritable,
567 x if x == CgroupSockopt as u32 => CgroupSockopt,
568 x if x == Tracing as u32 => Tracing,
569 x if x == StructOps as u32 => StructOps,
570 x if x == Ext as u32 => Ext,
571 x if x == Lsm as u32 => Lsm,
572 x if x == SkLookup as u32 => SkLookup,
573 x if x == Syscall as u32 => Syscall,
574 _ => Unknown,
575 }
576 }
577}
578
579#[non_exhaustive]
581#[repr(u32)]
582#[derive(Clone, Debug)]
583#[expect(missing_docs)]
585pub enum ProgramAttachType {
586 CgroupInetIngress = libbpf_sys::BPF_CGROUP_INET_INGRESS,
587 CgroupInetEgress = libbpf_sys::BPF_CGROUP_INET_EGRESS,
588 CgroupInetSockCreate = libbpf_sys::BPF_CGROUP_INET_SOCK_CREATE,
589 CgroupSockOps = libbpf_sys::BPF_CGROUP_SOCK_OPS,
590 SkSkbStreamParser = libbpf_sys::BPF_SK_SKB_STREAM_PARSER,
591 SkSkbStreamVerdict = libbpf_sys::BPF_SK_SKB_STREAM_VERDICT,
592 CgroupDevice = libbpf_sys::BPF_CGROUP_DEVICE,
593 SkMsgVerdict = libbpf_sys::BPF_SK_MSG_VERDICT,
594 CgroupInet4Bind = libbpf_sys::BPF_CGROUP_INET4_BIND,
595 CgroupInet6Bind = libbpf_sys::BPF_CGROUP_INET6_BIND,
596 CgroupInet4Connect = libbpf_sys::BPF_CGROUP_INET4_CONNECT,
597 CgroupInet6Connect = libbpf_sys::BPF_CGROUP_INET6_CONNECT,
598 CgroupInet4PostBind = libbpf_sys::BPF_CGROUP_INET4_POST_BIND,
599 CgroupInet6PostBind = libbpf_sys::BPF_CGROUP_INET6_POST_BIND,
600 CgroupUdp4Sendmsg = libbpf_sys::BPF_CGROUP_UDP4_SENDMSG,
601 CgroupUdp6Sendmsg = libbpf_sys::BPF_CGROUP_UDP6_SENDMSG,
602 LircMode2 = libbpf_sys::BPF_LIRC_MODE2,
603 FlowDissector = libbpf_sys::BPF_FLOW_DISSECTOR,
604 CgroupSysctl = libbpf_sys::BPF_CGROUP_SYSCTL,
605 CgroupUdp4Recvmsg = libbpf_sys::BPF_CGROUP_UDP4_RECVMSG,
606 CgroupUdp6Recvmsg = libbpf_sys::BPF_CGROUP_UDP6_RECVMSG,
607 CgroupGetsockopt = libbpf_sys::BPF_CGROUP_GETSOCKOPT,
608 CgroupSetsockopt = libbpf_sys::BPF_CGROUP_SETSOCKOPT,
609 TraceRawTp = libbpf_sys::BPF_TRACE_RAW_TP,
610 TraceFentry = libbpf_sys::BPF_TRACE_FENTRY,
611 TraceFexit = libbpf_sys::BPF_TRACE_FEXIT,
612 ModifyReturn = libbpf_sys::BPF_MODIFY_RETURN,
613 LsmMac = libbpf_sys::BPF_LSM_MAC,
614 TraceIter = libbpf_sys::BPF_TRACE_ITER,
615 CgroupInet4Getpeername = libbpf_sys::BPF_CGROUP_INET4_GETPEERNAME,
616 CgroupInet6Getpeername = libbpf_sys::BPF_CGROUP_INET6_GETPEERNAME,
617 CgroupInet4Getsockname = libbpf_sys::BPF_CGROUP_INET4_GETSOCKNAME,
618 CgroupInet6Getsockname = libbpf_sys::BPF_CGROUP_INET6_GETSOCKNAME,
619 XdpDevmap = libbpf_sys::BPF_XDP_DEVMAP,
620 CgroupInetSockRelease = libbpf_sys::BPF_CGROUP_INET_SOCK_RELEASE,
621 XdpCpumap = libbpf_sys::BPF_XDP_CPUMAP,
622 SkLookup = libbpf_sys::BPF_SK_LOOKUP,
623 Xdp = libbpf_sys::BPF_XDP,
624 SkSkbVerdict = libbpf_sys::BPF_SK_SKB_VERDICT,
625 SkReuseportSelect = libbpf_sys::BPF_SK_REUSEPORT_SELECT,
626 SkReuseportSelectOrMigrate = libbpf_sys::BPF_SK_REUSEPORT_SELECT_OR_MIGRATE,
627 PerfEvent = libbpf_sys::BPF_PERF_EVENT,
628 KprobeMulti = libbpf_sys::BPF_TRACE_KPROBE_MULTI,
629 NetkitPeer = libbpf_sys::BPF_NETKIT_PEER,
630 TraceUprobeMulti = libbpf_sys::BPF_TRACE_UPROBE_MULTI,
631 LsmCgroup = libbpf_sys::BPF_LSM_CGROUP,
632 TraceKprobeSession = libbpf_sys::BPF_TRACE_KPROBE_SESSION,
633 TcxIngress = libbpf_sys::BPF_TCX_INGRESS,
634 TcxEgress = libbpf_sys::BPF_TCX_EGRESS,
635 Netfilter = libbpf_sys::BPF_NETFILTER,
636 CgroupUnixGetsockname = libbpf_sys::BPF_CGROUP_UNIX_GETSOCKNAME,
637 CgroupUnixSendmsg = libbpf_sys::BPF_CGROUP_UNIX_SENDMSG,
638 NetkitPrimary = libbpf_sys::BPF_NETKIT_PRIMARY,
639 CgroupUnixRecvmsg = libbpf_sys::BPF_CGROUP_UNIX_RECVMSG,
640 CgroupUnixConnect = libbpf_sys::BPF_CGROUP_UNIX_CONNECT,
641 CgroupUnixGetpeername = libbpf_sys::BPF_CGROUP_UNIX_GETPEERNAME,
642 StructOps = libbpf_sys::BPF_STRUCT_OPS,
643 Unknown = u32::MAX,
645}
646
647impl From<u32> for ProgramAttachType {
648 fn from(value: u32) -> Self {
649 use ProgramAttachType::*;
650
651 match value {
652 x if x == CgroupInetIngress as u32 => CgroupInetIngress,
653 x if x == CgroupInetEgress as u32 => CgroupInetEgress,
654 x if x == CgroupInetSockCreate as u32 => CgroupInetSockCreate,
655 x if x == CgroupSockOps as u32 => CgroupSockOps,
656 x if x == SkSkbStreamParser as u32 => SkSkbStreamParser,
657 x if x == SkSkbStreamVerdict as u32 => SkSkbStreamVerdict,
658 x if x == CgroupDevice as u32 => CgroupDevice,
659 x if x == SkMsgVerdict as u32 => SkMsgVerdict,
660 x if x == CgroupInet4Bind as u32 => CgroupInet4Bind,
661 x if x == CgroupInet6Bind as u32 => CgroupInet6Bind,
662 x if x == CgroupInet4Connect as u32 => CgroupInet4Connect,
663 x if x == CgroupInet6Connect as u32 => CgroupInet6Connect,
664 x if x == CgroupInet4PostBind as u32 => CgroupInet4PostBind,
665 x if x == CgroupInet6PostBind as u32 => CgroupInet6PostBind,
666 x if x == CgroupUdp4Sendmsg as u32 => CgroupUdp4Sendmsg,
667 x if x == CgroupUdp6Sendmsg as u32 => CgroupUdp6Sendmsg,
668 x if x == LircMode2 as u32 => LircMode2,
669 x if x == FlowDissector as u32 => FlowDissector,
670 x if x == CgroupSysctl as u32 => CgroupSysctl,
671 x if x == CgroupUdp4Recvmsg as u32 => CgroupUdp4Recvmsg,
672 x if x == CgroupUdp6Recvmsg as u32 => CgroupUdp6Recvmsg,
673 x if x == CgroupGetsockopt as u32 => CgroupGetsockopt,
674 x if x == CgroupSetsockopt as u32 => CgroupSetsockopt,
675 x if x == TraceRawTp as u32 => TraceRawTp,
676 x if x == TraceFentry as u32 => TraceFentry,
677 x if x == TraceFexit as u32 => TraceFexit,
678 x if x == ModifyReturn as u32 => ModifyReturn,
679 x if x == LsmMac as u32 => LsmMac,
680 x if x == TraceIter as u32 => TraceIter,
681 x if x == CgroupInet4Getpeername as u32 => CgroupInet4Getpeername,
682 x if x == CgroupInet6Getpeername as u32 => CgroupInet6Getpeername,
683 x if x == CgroupInet4Getsockname as u32 => CgroupInet4Getsockname,
684 x if x == CgroupInet6Getsockname as u32 => CgroupInet6Getsockname,
685 x if x == XdpDevmap as u32 => XdpDevmap,
686 x if x == CgroupInetSockRelease as u32 => CgroupInetSockRelease,
687 x if x == XdpCpumap as u32 => XdpCpumap,
688 x if x == SkLookup as u32 => SkLookup,
689 x if x == Xdp as u32 => Xdp,
690 x if x == SkSkbVerdict as u32 => SkSkbVerdict,
691 x if x == SkReuseportSelect as u32 => SkReuseportSelect,
692 x if x == SkReuseportSelectOrMigrate as u32 => SkReuseportSelectOrMigrate,
693 x if x == PerfEvent as u32 => PerfEvent,
694 x if x == KprobeMulti as u32 => KprobeMulti,
695 x if x == NetkitPeer as u32 => NetkitPeer,
696 x if x == TraceUprobeMulti as u32 => TraceUprobeMulti,
697 x if x == LsmCgroup as u32 => LsmCgroup,
698 x if x == TraceKprobeSession as u32 => TraceKprobeSession,
699 x if x == TcxIngress as u32 => TcxIngress,
700 x if x == TcxEgress as u32 => TcxEgress,
701 x if x == Netfilter as u32 => Netfilter,
702 x if x == CgroupUnixGetsockname as u32 => CgroupUnixGetsockname,
703 x if x == CgroupUnixSendmsg as u32 => CgroupUnixSendmsg,
704 x if x == NetkitPrimary as u32 => NetkitPrimary,
705 x if x == CgroupUnixRecvmsg as u32 => CgroupUnixRecvmsg,
706 x if x == CgroupUnixConnect as u32 => CgroupUnixConnect,
707 x if x == CgroupUnixGetpeername as u32 => CgroupUnixGetpeername,
708 x if x == StructOps as u32 => StructOps,
709 _ => Unknown,
710 }
711 }
712}
713
714#[derive(Debug, Default)]
719pub struct Input<'dat> {
720 pub context_in: Option<&'dat mut [u8]>,
724 pub context_out: Option<&'dat mut [u8]>,
726 pub data_in: Option<&'dat [u8]>,
728 pub data_out: Option<&'dat mut [u8]>,
730 pub cpu: u32,
732 pub flags: u32,
734 pub repeat: u32,
737 #[doc(hidden)]
739 pub _non_exhaustive: (),
740}
741
742#[derive(Debug)]
747pub struct Output<'dat> {
748 pub return_value: u32,
750 pub context: Option<&'dat mut [u8]>,
752 pub data: Option<&'dat mut [u8]>,
754 pub duration: Duration,
756 #[doc(hidden)]
758 pub _non_exhaustive: (),
759}
760
761pub type Program<'obj> = ProgramImpl<'obj>;
763pub type ProgramMut<'obj> = ProgramImpl<'obj, Mut>;
765
766#[derive(Debug)]
774#[repr(transparent)]
775pub struct ProgramImpl<'obj, T = ()> {
776 pub(crate) ptr: NonNull<libbpf_sys::bpf_program>,
777 _phantom: PhantomData<&'obj T>,
778}
779
780impl<'obj> Program<'obj> {
781 pub fn new(prog: &'obj libbpf_sys::bpf_program) -> Self {
783 Self {
786 ptr: unsafe { NonNull::new_unchecked(prog as *const _ as *mut _) },
787 _phantom: PhantomData,
788 }
789 }
790
791 pub fn name(&self) -> &'obj OsStr {
793 let name_ptr = unsafe { libbpf_sys::bpf_program__name(self.ptr.as_ptr()) };
794 let name_c_str = unsafe { CStr::from_ptr(name_ptr) };
795 OsStr::from_bytes(name_c_str.to_bytes())
797 }
798
799 pub fn section(&self) -> &'obj OsStr {
801 let p = unsafe { libbpf_sys::bpf_program__section_name(self.ptr.as_ptr()) };
803 let section_c_str = unsafe { CStr::from_ptr(p) };
806 let section = OsStr::from_bytes(section_c_str.to_bytes());
807 section
808 }
809
810 pub fn prog_type(&self) -> ProgramType {
812 ProgramType::from(unsafe { libbpf_sys::bpf_program__type(self.ptr.as_ptr()) })
813 }
814
815 #[deprecated = "renamed to Program::fd_from_id"]
816 #[expect(missing_docs)]
817 #[inline]
818 pub fn get_fd_by_id(id: u32) -> Result<OwnedFd> {
819 Self::fd_from_id(id)
820 }
821
822 pub fn fd_from_id(id: u32) -> Result<OwnedFd> {
824 let ret = unsafe { libbpf_sys::bpf_prog_get_fd_by_id(id) };
825 let fd = util::parse_ret_i32(ret)?;
826 Ok(unsafe { OwnedFd::from_raw_fd(fd) })
830 }
831
832 pub fn id_from_fd(fd: BorrowedFd<'_>) -> Result<u32> {
834 let mut prog_info = libbpf_sys::bpf_prog_info::default();
835 let prog_info_ptr: *mut libbpf_sys::bpf_prog_info = &mut prog_info;
836 let mut len = size_of::<libbpf_sys::bpf_prog_info>() as u32;
837 let ret = unsafe {
838 libbpf_sys::bpf_obj_get_info_by_fd(
839 fd.as_raw_fd(),
840 prog_info_ptr as *mut c_void,
841 &mut len,
842 )
843 };
844 util::parse_ret(ret)?;
845 Ok(prog_info.id)
846 }
847
848 pub fn fd_from_pinned_path<P: AsRef<Path>>(path: P) -> Result<OwnedFd> {
852 let path_c = util::path_to_cstring(&path)?;
853 let path_ptr = path_c.as_ptr();
854
855 let fd = unsafe { libbpf_sys::bpf_obj_get(path_ptr) };
856 let fd = util::parse_ret_i32(fd).with_context(|| {
857 format!(
858 "failed to retrieve BPF object from pinned path `{}`",
859 path.as_ref().display()
860 )
861 })?;
862 let fd = unsafe { OwnedFd::from_raw_fd(fd) };
863
864 let fd_type = util::object_type_from_fd(fd.as_fd())?;
868 match fd_type {
869 BpfObjectType::Program => Ok(fd),
870 other => Err(Error::with_invalid_data(format!(
871 "retrieved BPF fd is not a program fd: {other:#?}"
872 ))),
873 }
874 }
875
876 pub fn flags(&self) -> u32 {
878 unsafe { libbpf_sys::bpf_program__flags(self.ptr.as_ptr()) }
879 }
880
881 pub fn attach_type(&self) -> ProgramAttachType {
883 ProgramAttachType::from(unsafe {
884 libbpf_sys::bpf_program__expected_attach_type(self.ptr.as_ptr())
885 })
886 }
887
888 pub fn autoload(&self) -> bool {
890 unsafe { libbpf_sys::bpf_program__autoload(self.ptr.as_ptr()) }
891 }
892
893 pub fn log_level(&self) -> u32 {
895 unsafe { libbpf_sys::bpf_program__log_level(self.ptr.as_ptr()) }
896 }
897
898 pub fn insn_cnt(&self) -> usize {
902 unsafe { libbpf_sys::bpf_program__insn_cnt(self.ptr.as_ptr()) as usize }
903 }
904
905 pub fn insns(&self) -> &'obj [libbpf_sys::bpf_insn] {
909 let count = self.insn_cnt();
910 let ptr = unsafe { libbpf_sys::bpf_program__insns(self.ptr.as_ptr()) };
911 unsafe { slice::from_raw_parts(ptr, count) }
912 }
913}
914
915impl<'obj> ProgramMut<'obj> {
916 pub fn new_mut(prog: &'obj mut libbpf_sys::bpf_program) -> Self {
918 Self {
919 ptr: unsafe { NonNull::new_unchecked(prog as *mut _) },
920 _phantom: PhantomData,
921 }
922 }
923
924 pub fn pin<P: AsRef<Path>>(&mut self, path: P) -> Result<()> {
927 let path_c = util::path_to_cstring(path)?;
928 let path_ptr = path_c.as_ptr();
929
930 let ret = unsafe { libbpf_sys::bpf_program__pin(self.ptr.as_ptr(), path_ptr) };
931 util::parse_ret(ret)
932 }
933
934 pub fn unpin<P: AsRef<Path>>(&mut self, path: P) -> Result<()> {
937 let path_c = util::path_to_cstring(path)?;
938 let path_ptr = path_c.as_ptr();
939
940 let ret = unsafe { libbpf_sys::bpf_program__unpin(self.ptr.as_ptr(), path_ptr) };
941 util::parse_ret(ret)
942 }
943
944 pub fn attach(&self) -> Result<Link> {
946 let ptr = unsafe { libbpf_sys::bpf_program__attach(self.ptr.as_ptr()) };
947 let ptr = validate_bpf_ret(ptr).context("failed to attach BPF program")?;
948 let link = unsafe { Link::new(ptr) };
950 Ok(link)
951 }
952
953 pub fn attach_cgroup(&self, cgroup_fd: i32) -> Result<Link> {
956 let ptr = unsafe { libbpf_sys::bpf_program__attach_cgroup(self.ptr.as_ptr(), cgroup_fd) };
957 let ptr = validate_bpf_ret(ptr).context("failed to attach cgroup")?;
958 let link = unsafe { Link::new(ptr) };
960 Ok(link)
961 }
962
963 pub fn attach_perf_event(&self, pfd: i32) -> Result<Link> {
965 let ptr = unsafe { libbpf_sys::bpf_program__attach_perf_event(self.ptr.as_ptr(), pfd) };
966 let ptr = validate_bpf_ret(ptr).context("failed to attach perf event")?;
967 let link = unsafe { Link::new(ptr) };
969 Ok(link)
970 }
971
972 pub fn attach_perf_event_with_opts(&self, pfd: i32, opts: PerfEventOpts) -> Result<Link> {
975 let libbpf_opts = libbpf_sys::bpf_perf_event_opts::from(opts);
976 let ptr = unsafe {
977 libbpf_sys::bpf_program__attach_perf_event_opts(self.ptr.as_ptr(), pfd, &libbpf_opts)
978 };
979 let ptr = validate_bpf_ret(ptr).context("failed to attach perf event")?;
980 let link = unsafe { Link::new(ptr) };
982 Ok(link)
983 }
984
985 pub fn attach_uprobe<T: AsRef<Path>>(
988 &self,
989 retprobe: bool,
990 pid: i32,
991 binary_path: T,
992 func_offset: usize,
993 ) -> Result<Link> {
994 let path = util::path_to_cstring(binary_path)?;
995 let path_ptr = path.as_ptr();
996 let ptr = unsafe {
997 libbpf_sys::bpf_program__attach_uprobe(
998 self.ptr.as_ptr(),
999 retprobe,
1000 pid,
1001 path_ptr,
1002 func_offset as libbpf_sys::size_t,
1003 )
1004 };
1005 let ptr = validate_bpf_ret(ptr).context("failed to attach uprobe")?;
1006 let link = unsafe { Link::new(ptr) };
1008 Ok(link)
1009 }
1010
1011 pub fn attach_uprobe_with_opts(
1015 &self,
1016 pid: i32,
1017 binary_path: impl AsRef<Path>,
1018 func_offset: usize,
1019 opts: UprobeOpts,
1020 ) -> Result<Link> {
1021 let path = util::path_to_cstring(binary_path)?;
1022 let path_ptr = path.as_ptr();
1023 let UprobeOpts {
1024 ref_ctr_offset,
1025 cookie,
1026 retprobe,
1027 func_name,
1028 _non_exhaustive,
1029 } = opts;
1030
1031 let func_name: Option<CString> = if let Some(func_name) = func_name {
1032 Some(util::str_to_cstring(&func_name)?)
1033 } else {
1034 None
1035 };
1036 let ptr = func_name
1037 .as_ref()
1038 .map_or(ptr::null(), |func_name| func_name.as_ptr());
1039 let opts = libbpf_sys::bpf_uprobe_opts {
1040 sz: size_of::<libbpf_sys::bpf_uprobe_opts>() as _,
1041 ref_ctr_offset: ref_ctr_offset as libbpf_sys::size_t,
1042 bpf_cookie: cookie,
1043 retprobe,
1044 func_name: ptr,
1045 ..Default::default()
1046 };
1047
1048 let ptr = unsafe {
1049 libbpf_sys::bpf_program__attach_uprobe_opts(
1050 self.ptr.as_ptr(),
1051 pid,
1052 path_ptr,
1053 func_offset as libbpf_sys::size_t,
1054 &opts as *const _,
1055 )
1056 };
1057 let ptr = validate_bpf_ret(ptr).context("failed to attach uprobe")?;
1058 let link = unsafe { Link::new(ptr) };
1060 Ok(link)
1061 }
1062
1063 pub fn attach_uprobe_multi(
1066 &self,
1067 pid: i32,
1068 binary_path: impl AsRef<Path>,
1069 func_pattern: impl AsRef<str>,
1070 retprobe: bool,
1071 session: bool,
1072 ) -> Result<Link> {
1073 let opts = UprobeMultiOpts {
1074 syms: Vec::new(),
1075 offsets: Vec::new(),
1076 ref_ctr_offsets: Vec::new(),
1077 cookies: Vec::new(),
1078 retprobe,
1079 session,
1080 _non_exhaustive: (),
1081 };
1082
1083 self.attach_uprobe_multi_with_opts(pid, binary_path, func_pattern, opts)
1084 }
1085
1086 pub fn attach_uprobe_multi_with_opts(
1090 &self,
1091 pid: i32,
1092 binary_path: impl AsRef<Path>,
1093 func_pattern: impl AsRef<str>,
1094 opts: UprobeMultiOpts,
1095 ) -> Result<Link> {
1096 let path = util::path_to_cstring(binary_path)?;
1097 let path_ptr = path.as_ptr();
1098
1099 let UprobeMultiOpts {
1100 syms,
1101 offsets,
1102 ref_ctr_offsets,
1103 cookies,
1104 retprobe,
1105 session,
1106 _non_exhaustive,
1107 } = opts;
1108
1109 let pattern = util::str_to_cstring(func_pattern.as_ref())?;
1110 let pattern_ptr = pattern.as_ptr();
1111
1112 let syms_cstrings = syms
1113 .iter()
1114 .map(|s| util::str_to_cstring(s))
1115 .collect::<Result<Vec<_>>>()?;
1116 let syms_ptrs = syms_cstrings
1117 .iter()
1118 .map(|cs| cs.as_ptr())
1119 .collect::<Vec<_>>();
1120 let syms_ptr = if !syms_ptrs.is_empty() {
1121 syms_ptrs.as_ptr()
1122 } else {
1123 ptr::null()
1124 };
1125 let offsets_ptr = if !offsets.is_empty() {
1126 offsets.as_ptr()
1127 } else {
1128 ptr::null()
1129 };
1130 let ref_ctr_offsets_ptr = if !ref_ctr_offsets.is_empty() {
1131 ref_ctr_offsets.as_ptr()
1132 } else {
1133 ptr::null()
1134 };
1135 let cookies_ptr = if !cookies.is_empty() {
1136 cookies.as_ptr()
1137 } else {
1138 ptr::null()
1139 };
1140 let cnt = if !syms.is_empty() {
1141 syms.len()
1142 } else if !offsets.is_empty() {
1143 offsets.len()
1144 } else {
1145 0
1146 };
1147
1148 let c_opts = libbpf_sys::bpf_uprobe_multi_opts {
1149 sz: size_of::<libbpf_sys::bpf_uprobe_multi_opts>() as _,
1150 syms: syms_ptr.cast_mut(),
1151 offsets: offsets_ptr.cast(),
1152 ref_ctr_offsets: ref_ctr_offsets_ptr.cast(),
1153 cookies: cookies_ptr.cast(),
1154 cnt: cnt as libbpf_sys::size_t,
1155 retprobe,
1156 session,
1157 ..Default::default()
1158 };
1159
1160 let ptr = unsafe {
1161 libbpf_sys::bpf_program__attach_uprobe_multi(
1162 self.ptr.as_ptr(),
1163 pid,
1164 path_ptr,
1165 pattern_ptr,
1166 &c_opts as *const _,
1167 )
1168 };
1169
1170 let ptr = validate_bpf_ret(ptr).context("failed to attach uprobe multi")?;
1171 let link = unsafe { Link::new(ptr) };
1173 Ok(link)
1174 }
1175
1176 pub fn attach_kprobe<T: AsRef<str>>(&self, retprobe: bool, func_name: T) -> Result<Link> {
1179 let func_name = util::str_to_cstring(func_name.as_ref())?;
1180 let func_name_ptr = func_name.as_ptr();
1181 let ptr = unsafe {
1182 libbpf_sys::bpf_program__attach_kprobe(self.ptr.as_ptr(), retprobe, func_name_ptr)
1183 };
1184 let ptr = validate_bpf_ret(ptr).context("failed to attach kprobe")?;
1185 let link = unsafe { Link::new(ptr) };
1187 Ok(link)
1188 }
1189
1190 pub fn attach_kprobe_with_opts<T: AsRef<str>>(
1194 &self,
1195 retprobe: bool,
1196 func_name: T,
1197 opts: KprobeOpts,
1198 ) -> Result<Link> {
1199 let func_name = util::str_to_cstring(func_name.as_ref())?;
1200 let func_name_ptr = func_name.as_ptr();
1201
1202 let mut opts = libbpf_sys::bpf_kprobe_opts::from(opts);
1203 opts.retprobe = retprobe;
1204
1205 let ptr = unsafe {
1206 libbpf_sys::bpf_program__attach_kprobe_opts(
1207 self.ptr.as_ptr(),
1208 func_name_ptr,
1209 &opts as *const _,
1210 )
1211 };
1212 let ptr = validate_bpf_ret(ptr).context("failed to attach kprobe")?;
1213 let link = unsafe { Link::new(ptr) };
1215 Ok(link)
1216 }
1217
1218 fn check_kprobe_multi_args<T: AsRef<str>>(symbols: &[T], cookies: &[u64]) -> Result<usize> {
1219 if symbols.is_empty() {
1220 return Err(Error::with_invalid_input("Symbols list cannot be empty"));
1221 }
1222
1223 if !cookies.is_empty() && symbols.len() != cookies.len() {
1224 return Err(Error::with_invalid_input(
1225 "Symbols and cookies list must have the same size",
1226 ));
1227 }
1228
1229 Ok(symbols.len())
1230 }
1231
1232 fn attach_kprobe_multi_impl(&self, opts: libbpf_sys::bpf_kprobe_multi_opts) -> Result<Link> {
1233 let ptr = unsafe {
1234 libbpf_sys::bpf_program__attach_kprobe_multi_opts(
1235 self.ptr.as_ptr(),
1236 ptr::null(),
1237 &opts as *const _,
1238 )
1239 };
1240 let ptr = validate_bpf_ret(ptr).context("failed to attach kprobe multi")?;
1241 let link = unsafe { Link::new(ptr) };
1243 Ok(link)
1244 }
1245
1246 pub fn attach_kprobe_multi<T: AsRef<str>>(
1250 &self,
1251 retprobe: bool,
1252 symbols: Vec<T>,
1253 ) -> Result<Link> {
1254 let cnt = Self::check_kprobe_multi_args(&symbols, &[])?;
1255
1256 let csyms = symbols
1257 .iter()
1258 .map(|s| util::str_to_cstring(s.as_ref()))
1259 .collect::<Result<Vec<_>>>()?;
1260 let mut syms = csyms.iter().map(|s| s.as_ptr()).collect::<Vec<_>>();
1261
1262 let opts = libbpf_sys::bpf_kprobe_multi_opts {
1263 sz: size_of::<libbpf_sys::bpf_kprobe_multi_opts>() as _,
1264 syms: syms.as_mut_ptr() as _,
1265 cnt: cnt as libbpf_sys::size_t,
1266 retprobe,
1267 ..Default::default()
1269 };
1270
1271 self.attach_kprobe_multi_impl(opts)
1272 }
1273
1274 pub fn attach_kprobe_multi_with_opts(&self, opts: KprobeMultiOpts) -> Result<Link> {
1278 let KprobeMultiOpts {
1279 symbols,
1280 mut cookies,
1281 retprobe,
1282 _non_exhaustive,
1283 } = opts;
1284
1285 let cnt = Self::check_kprobe_multi_args(&symbols, &cookies)?;
1286
1287 let csyms = symbols
1288 .iter()
1289 .map(|s| util::str_to_cstring(s.as_ref()))
1290 .collect::<Result<Vec<_>>>()?;
1291 let mut syms = csyms.iter().map(|s| s.as_ptr()).collect::<Vec<_>>();
1292
1293 let opts = libbpf_sys::bpf_kprobe_multi_opts {
1294 sz: size_of::<libbpf_sys::bpf_kprobe_multi_opts>() as _,
1295 syms: syms.as_mut_ptr() as _,
1296 cookies: if !cookies.is_empty() {
1297 cookies.as_mut_ptr() as _
1298 } else {
1299 ptr::null()
1300 },
1301 cnt: cnt as libbpf_sys::size_t,
1302 retprobe,
1303 ..Default::default()
1305 };
1306
1307 self.attach_kprobe_multi_impl(opts)
1308 }
1309
1310 pub fn attach_ksyscall<T: AsRef<str>>(&self, retprobe: bool, syscall_name: T) -> Result<Link> {
1312 let opts = libbpf_sys::bpf_ksyscall_opts {
1313 sz: size_of::<libbpf_sys::bpf_ksyscall_opts>() as _,
1314 retprobe,
1315 ..Default::default()
1316 };
1317
1318 let syscall_name = util::str_to_cstring(syscall_name.as_ref())?;
1319 let syscall_name_ptr = syscall_name.as_ptr();
1320 let ptr = unsafe {
1321 libbpf_sys::bpf_program__attach_ksyscall(self.ptr.as_ptr(), syscall_name_ptr, &opts)
1322 };
1323 let ptr = validate_bpf_ret(ptr).context("failed to attach ksyscall")?;
1324 let link = unsafe { Link::new(ptr) };
1326 Ok(link)
1327 }
1328
1329 fn attach_tracepoint_impl(
1330 &self,
1331 tp_category: &str,
1332 tp_name: &str,
1333 tp_opts: Option<TracepointOpts>,
1334 ) -> Result<Link> {
1335 let tp_category = util::str_to_cstring(tp_category)?;
1336 let tp_category_ptr = tp_category.as_ptr();
1337 let tp_name = util::str_to_cstring(tp_name)?;
1338 let tp_name_ptr = tp_name.as_ptr();
1339
1340 let tp_opts = tp_opts.map(libbpf_sys::bpf_tracepoint_opts::from);
1341 let opts = tp_opts.as_ref().map_or(ptr::null(), |opts| opts);
1342 let ptr = unsafe {
1343 libbpf_sys::bpf_program__attach_tracepoint_opts(
1344 self.ptr.as_ptr(),
1345 tp_category_ptr,
1346 tp_name_ptr,
1347 opts as *const _,
1348 )
1349 };
1350
1351 let ptr = validate_bpf_ret(ptr).context("failed to attach tracepoint")?;
1352 let link = unsafe { Link::new(ptr) };
1354 Ok(link)
1355 }
1356
1357 pub fn attach_tracepoint(
1360 &self,
1361 tp_category: TracepointCategory,
1362 tp_name: impl AsRef<str>,
1363 ) -> Result<Link> {
1364 self.attach_tracepoint_impl(tp_category.as_ref(), tp_name.as_ref(), None)
1365 }
1366
1367 pub fn attach_tracepoint_with_opts(
1371 &self,
1372 tp_category: TracepointCategory,
1373 tp_name: impl AsRef<str>,
1374 tp_opts: TracepointOpts,
1375 ) -> Result<Link> {
1376 self.attach_tracepoint_impl(tp_category.as_ref(), tp_name.as_ref(), Some(tp_opts))
1377 }
1378
1379 pub fn attach_raw_tracepoint<T: AsRef<str>>(&self, tp_name: T) -> Result<Link> {
1382 let tp_name = util::str_to_cstring(tp_name.as_ref())?;
1383 let tp_name_ptr = tp_name.as_ptr();
1384 let ptr = unsafe {
1385 libbpf_sys::bpf_program__attach_raw_tracepoint(self.ptr.as_ptr(), tp_name_ptr)
1386 };
1387 let ptr = validate_bpf_ret(ptr).context("failed to attach raw tracepoint")?;
1388 let link = unsafe { Link::new(ptr) };
1390 Ok(link)
1391 }
1392
1393 pub fn attach_raw_tracepoint_with_opts<T: AsRef<str>>(
1397 &self,
1398 tp_name: T,
1399 tp_opts: RawTracepointOpts,
1400 ) -> Result<Link> {
1401 let tp_name = util::str_to_cstring(tp_name.as_ref())?;
1402 let tp_name_ptr = tp_name.as_ptr();
1403 let mut tp_opts = libbpf_sys::bpf_raw_tracepoint_opts::from(tp_opts);
1404 let ptr = unsafe {
1405 libbpf_sys::bpf_program__attach_raw_tracepoint_opts(
1406 self.ptr.as_ptr(),
1407 tp_name_ptr,
1408 &mut tp_opts as *mut _,
1409 )
1410 };
1411 let ptr = validate_bpf_ret(ptr).context("failed to attach raw tracepoint")?;
1412 let link = unsafe { Link::new(ptr) };
1414 Ok(link)
1415 }
1416
1417 pub fn attach_lsm(&self) -> Result<Link> {
1419 let ptr = unsafe { libbpf_sys::bpf_program__attach_lsm(self.ptr.as_ptr()) };
1420 let ptr = validate_bpf_ret(ptr).context("failed to attach LSM")?;
1421 let link = unsafe { Link::new(ptr) };
1423 Ok(link)
1424 }
1425
1426 pub fn attach_trace(&self) -> Result<Link> {
1428 let ptr = unsafe { libbpf_sys::bpf_program__attach_trace(self.ptr.as_ptr()) };
1429 let ptr = validate_bpf_ret(ptr).context("failed to attach fentry/fexit kernel probe")?;
1430 let link = unsafe { Link::new(ptr) };
1432 Ok(link)
1433 }
1434
1435 pub fn attach_sockmap(&self, map_fd: i32) -> Result<()> {
1437 let err = unsafe {
1438 libbpf_sys::bpf_prog_attach(
1439 self.as_fd().as_raw_fd(),
1440 map_fd,
1441 self.attach_type() as u32,
1442 0,
1443 )
1444 };
1445 util::parse_ret(err)
1446 }
1447
1448 pub fn attach_xdp(&self, ifindex: i32) -> Result<Link> {
1450 let ptr = unsafe { libbpf_sys::bpf_program__attach_xdp(self.ptr.as_ptr(), ifindex) };
1451 let ptr = validate_bpf_ret(ptr).context("failed to attach XDP program")?;
1452 let link = unsafe { Link::new(ptr) };
1454 Ok(link)
1455 }
1456
1457 pub fn attach_netns(&self, netns_fd: i32) -> Result<Link> {
1459 let ptr = unsafe { libbpf_sys::bpf_program__attach_netns(self.ptr.as_ptr(), netns_fd) };
1460 let ptr = validate_bpf_ret(ptr).context("failed to attach network namespace program")?;
1461 let link = unsafe { Link::new(ptr) };
1463 Ok(link)
1464 }
1465
1466 pub fn attach_netfilter_with_opts(
1468 &self,
1469 netfilter_opt: netfilter::NetfilterOpts,
1470 ) -> Result<Link> {
1471 let netfilter_opts = libbpf_sys::bpf_netfilter_opts::from(netfilter_opt);
1472
1473 let ptr = unsafe {
1474 libbpf_sys::bpf_program__attach_netfilter(
1475 self.ptr.as_ptr(),
1476 &netfilter_opts as *const _,
1477 )
1478 };
1479
1480 let ptr = validate_bpf_ret(ptr).context("failed to attach netfilter program")?;
1481 let link = unsafe { Link::new(ptr) };
1483 Ok(link)
1484 }
1485
1486 fn attach_usdt_impl(
1487 &self,
1488 pid: i32,
1489 binary_path: &Path,
1490 usdt_provider: &str,
1491 usdt_name: &str,
1492 usdt_opts: Option<UsdtOpts>,
1493 ) -> Result<Link> {
1494 let path = util::path_to_cstring(binary_path)?;
1495 let path_ptr = path.as_ptr();
1496 let usdt_provider = util::str_to_cstring(usdt_provider)?;
1497 let usdt_provider_ptr = usdt_provider.as_ptr();
1498 let usdt_name = util::str_to_cstring(usdt_name)?;
1499 let usdt_name_ptr = usdt_name.as_ptr();
1500 let usdt_opts = usdt_opts.map(libbpf_sys::bpf_usdt_opts::from);
1501 let usdt_opts_ptr = usdt_opts
1502 .as_ref()
1503 .map(|opts| opts as *const _)
1504 .unwrap_or_else(ptr::null);
1505
1506 let ptr = unsafe {
1507 libbpf_sys::bpf_program__attach_usdt(
1508 self.ptr.as_ptr(),
1509 pid,
1510 path_ptr,
1511 usdt_provider_ptr,
1512 usdt_name_ptr,
1513 usdt_opts_ptr,
1514 )
1515 };
1516 let ptr = validate_bpf_ret(ptr).context("failed to attach USDT")?;
1517 let link = unsafe { Link::new(ptr) };
1519 Ok(link)
1520 }
1521
1522 pub fn attach_usdt(
1526 &self,
1527 pid: i32,
1528 binary_path: impl AsRef<Path>,
1529 usdt_provider: impl AsRef<str>,
1530 usdt_name: impl AsRef<str>,
1531 ) -> Result<Link> {
1532 self.attach_usdt_impl(
1533 pid,
1534 binary_path.as_ref(),
1535 usdt_provider.as_ref(),
1536 usdt_name.as_ref(),
1537 None,
1538 )
1539 }
1540
1541 pub fn attach_usdt_with_opts(
1545 &self,
1546 pid: i32,
1547 binary_path: impl AsRef<Path>,
1548 usdt_provider: impl AsRef<str>,
1549 usdt_name: impl AsRef<str>,
1550 usdt_opts: UsdtOpts,
1551 ) -> Result<Link> {
1552 self.attach_usdt_impl(
1553 pid,
1554 binary_path.as_ref(),
1555 usdt_provider.as_ref(),
1556 usdt_name.as_ref(),
1557 Some(usdt_opts),
1558 )
1559 }
1560
1561 pub fn attach_iter(&self, map_fd: BorrowedFd<'_>) -> Result<Link> {
1565 let map_opts = MapIterOpts {
1566 fd: map_fd,
1567 _non_exhaustive: (),
1568 };
1569 self.attach_iter_with_opts(IterOpts::Map(map_opts))
1570 }
1571
1572 pub fn attach_iter_with_opts(&self, opts: IterOpts<'_>) -> Result<Link> {
1578 let mut linkinfo = libbpf_sys::bpf_iter_link_info::from(opts);
1579 let attach_opt = libbpf_sys::bpf_iter_attach_opts {
1580 link_info: &raw mut linkinfo,
1581 link_info_len: size_of::<libbpf_sys::bpf_iter_link_info>() as _,
1582 sz: size_of::<libbpf_sys::bpf_iter_attach_opts>() as _,
1583 ..Default::default()
1584 };
1585 let ptr = unsafe {
1586 libbpf_sys::bpf_program__attach_iter(
1587 self.ptr.as_ptr(),
1588 &attach_opt as *const libbpf_sys::bpf_iter_attach_opts,
1589 )
1590 };
1591
1592 let ptr = validate_bpf_ret(ptr).context("failed to attach iterator")?;
1593 let link = unsafe { Link::new(ptr) };
1595 Ok(link)
1596 }
1597
1598 pub fn test_run<'dat>(&self, input: Input<'dat>) -> Result<Output<'dat>> {
1604 unsafe fn slice_from_array<'t, T>(items: *mut T, num_items: usize) -> Option<&'t mut [T]> {
1605 if items.is_null() {
1606 None
1607 } else {
1608 Some(unsafe { slice::from_raw_parts_mut(items, num_items) })
1609 }
1610 }
1611
1612 let Input {
1613 context_in,
1614 mut context_out,
1615 data_in,
1616 mut data_out,
1617 cpu,
1618 flags,
1619 repeat,
1620 _non_exhaustive: (),
1621 } = input;
1622
1623 let mut opts = unsafe { mem::zeroed::<libbpf_sys::bpf_test_run_opts>() };
1624 opts.sz = size_of_val(&opts) as _;
1625 opts.ctx_in = context_in
1626 .as_ref()
1627 .map(|data| data.as_ptr().cast())
1628 .unwrap_or_else(ptr::null);
1629 opts.ctx_size_in = context_in.map(|data| data.len() as _).unwrap_or(0);
1630 opts.ctx_out = context_out
1631 .as_mut()
1632 .map(|data| data.as_mut_ptr().cast())
1633 .unwrap_or_else(ptr::null_mut);
1634 opts.ctx_size_out = context_out.map(|data| data.len() as _).unwrap_or(0);
1635 opts.data_in = data_in
1636 .map(|data| data.as_ptr().cast())
1637 .unwrap_or_else(ptr::null);
1638 opts.data_size_in = data_in.map(|data| data.len() as _).unwrap_or(0);
1639 opts.data_out = data_out
1640 .as_mut()
1641 .map(|data| data.as_mut_ptr().cast())
1642 .unwrap_or_else(ptr::null_mut);
1643 opts.data_size_out = data_out.map(|data| data.len() as _).unwrap_or(0);
1644 opts.cpu = cpu;
1645 opts.flags = flags;
1646 opts.repeat = repeat as i32;
1649
1650 let rc = unsafe { libbpf_sys::bpf_prog_test_run_opts(self.as_fd().as_raw_fd(), &mut opts) };
1651 let () = util::parse_ret(rc)?;
1652 let output = Output {
1653 return_value: opts.retval,
1654 context: unsafe { slice_from_array(opts.ctx_out.cast(), opts.ctx_size_out as _) },
1655 data: unsafe { slice_from_array(opts.data_out.cast(), opts.data_size_out as _) },
1656 duration: Duration::from_nanos(opts.duration.into()),
1657 _non_exhaustive: (),
1658 };
1659 Ok(output)
1660 }
1661
1662 pub fn stdout(&self) -> impl Read + '_ {
1664 Stream::new(self.as_fd(), Stream::BPF_STDOUT)
1665 }
1666
1667 pub fn stderr(&self) -> impl Read + '_ {
1669 Stream::new(self.as_fd(), Stream::BPF_STDERR)
1670 }
1671}
1672
1673impl<'obj> Deref for ProgramMut<'obj> {
1674 type Target = Program<'obj>;
1675
1676 fn deref(&self) -> &Self::Target {
1677 unsafe { transmute::<&ProgramMut<'obj>, &Program<'obj>>(self) }
1680 }
1681}
1682
1683impl<T> AsFd for ProgramImpl<'_, T> {
1684 fn as_fd(&self) -> BorrowedFd<'_> {
1685 let fd = unsafe { libbpf_sys::bpf_program__fd(self.ptr.as_ptr()) };
1686 unsafe { BorrowedFd::borrow_raw(fd) }
1687 }
1688}
1689
1690impl<T> AsRawLibbpf for ProgramImpl<'_, T> {
1691 type LibbpfType = libbpf_sys::bpf_program;
1692
1693 fn as_libbpf_object(&self) -> NonNull<Self::LibbpfType> {
1695 self.ptr
1696 }
1697}
1698
1699#[cfg(test)]
1700mod tests {
1701 use super::*;
1702
1703 use std::mem::discriminant;
1704
1705 #[test]
1706 fn program_type() {
1707 use ProgramType::*;
1708
1709 for t in [
1710 Unspec,
1711 SocketFilter,
1712 Kprobe,
1713 SchedCls,
1714 SchedAct,
1715 Tracepoint,
1716 Xdp,
1717 PerfEvent,
1718 CgroupSkb,
1719 CgroupSock,
1720 LwtIn,
1721 LwtOut,
1722 LwtXmit,
1723 SockOps,
1724 SkSkb,
1725 CgroupDevice,
1726 SkMsg,
1727 RawTracepoint,
1728 CgroupSockAddr,
1729 LwtSeg6local,
1730 LircMode2,
1731 SkReuseport,
1732 FlowDissector,
1733 CgroupSysctl,
1734 RawTracepointWritable,
1735 CgroupSockopt,
1736 Tracing,
1737 StructOps,
1738 Ext,
1739 Lsm,
1740 SkLookup,
1741 Syscall,
1742 Unknown,
1743 ] {
1744 assert_eq!(discriminant(&t), discriminant(&ProgramType::from(t as u32)));
1746 }
1747 }
1748
1749 #[test]
1750 fn program_attach_type() {
1751 use ProgramAttachType::*;
1752
1753 for t in [
1754 CgroupInetIngress,
1755 CgroupInetEgress,
1756 CgroupInetSockCreate,
1757 CgroupSockOps,
1758 SkSkbStreamParser,
1759 SkSkbStreamVerdict,
1760 CgroupDevice,
1761 SkMsgVerdict,
1762 CgroupInet4Bind,
1763 CgroupInet6Bind,
1764 CgroupInet4Connect,
1765 CgroupInet6Connect,
1766 CgroupInet4PostBind,
1767 CgroupInet6PostBind,
1768 CgroupUdp4Sendmsg,
1769 CgroupUdp6Sendmsg,
1770 LircMode2,
1771 FlowDissector,
1772 CgroupSysctl,
1773 CgroupUdp4Recvmsg,
1774 CgroupUdp6Recvmsg,
1775 CgroupGetsockopt,
1776 CgroupSetsockopt,
1777 TraceRawTp,
1778 TraceFentry,
1779 TraceFexit,
1780 ModifyReturn,
1781 LsmMac,
1782 TraceIter,
1783 CgroupInet4Getpeername,
1784 CgroupInet6Getpeername,
1785 CgroupInet4Getsockname,
1786 CgroupInet6Getsockname,
1787 XdpDevmap,
1788 CgroupInetSockRelease,
1789 XdpCpumap,
1790 SkLookup,
1791 Xdp,
1792 SkSkbVerdict,
1793 SkReuseportSelect,
1794 SkReuseportSelectOrMigrate,
1795 PerfEvent,
1796 Unknown,
1797 ] {
1798 assert_eq!(
1800 discriminant(&t),
1801 discriminant(&ProgramAttachType::from(t as u32))
1802 );
1803 }
1804 }
1805}