1mod info;
41mod probe;
42mod utils;
43
44pub mod cgroup_device;
46pub mod cgroup_skb;
47pub mod cgroup_sock;
48pub mod cgroup_sock_addr;
49pub mod cgroup_sockopt;
50pub mod cgroup_sysctl;
51pub mod extension;
52pub mod fentry;
53pub mod fexit;
54pub mod flow_dissector;
55pub mod iter;
56pub mod kprobe;
57pub mod links;
58pub mod lirc_mode2;
59pub mod lsm;
60pub mod lsm_cgroup;
61pub mod perf_attach;
62pub mod perf_event;
63pub mod raw_trace_point;
64pub mod sk_lookup;
65pub mod sk_msg;
66pub mod sk_reuseport;
67pub mod sk_skb;
68pub mod sock_ops;
69pub mod socket_filter;
70pub mod tc;
71pub mod tp_btf;
72pub mod trace_point;
73pub mod uprobe;
74pub mod xdp;
75
76use std::{
77 borrow::Cow,
78 ffi::CString,
79 io,
80 os::fd::{AsFd, BorrowedFd},
81 path::{Path, PathBuf},
82 sync::Arc,
83};
84
85use aya_obj::{
86 VerifierLog,
87 btf::BtfError,
88 generated::{BPF_F_TEST_XDP_LIVE_FRAMES, bpf_attach_type, bpf_prog_info, bpf_prog_type},
89 programs::XdpAttachType,
90};
91use info::impl_info;
92pub use info::{LsmAttachType, ProgramInfo, ProgramType, loaded_programs};
93use libc::ENOSPC;
94use tc::SchedClassifierLink;
95use thiserror::Error;
96
97pub use crate::programs::{
99 cgroup_device::CgroupDevice,
100 cgroup_skb::{CgroupSkb, CgroupSkbAttachType},
101 cgroup_sock::{CgroupSock, CgroupSockAttachType},
102 cgroup_sock_addr::{CgroupSockAddr, CgroupSockAddrAttachType},
103 cgroup_sockopt::{CgroupSockopt, CgroupSockoptAttachType},
104 cgroup_sysctl::CgroupSysctl,
105 extension::{Extension, ExtensionError},
106 fentry::FEntry,
107 fexit::FExit,
108 flow_dissector::FlowDissector,
109 iter::Iter,
110 kprobe::{KProbe, KProbeError},
111 links::{CgroupAttachMode, Link, LinkOrder},
112 lirc_mode2::LircMode2,
113 lsm::Lsm,
114 lsm_cgroup::LsmCgroup,
115 perf_event::PerfEvent,
116 probe::ProbeKind,
117 raw_trace_point::RawTracePoint,
118 sk_lookup::SkLookup,
119 sk_msg::SkMsg,
120 sk_reuseport::{SkReuseport, SkReuseportAttachType, SkReuseportError},
121 sk_skb::{SkSkb, SkSkbKind},
122 sock_ops::SockOps,
123 socket_filter::{SocketFilter, SocketFilterError},
124 tc::{SchedClassifier, TcAttachType, TcError, TcHandle},
125 tp_btf::BtfTracePoint,
126 trace_point::{TracePoint, TracePointError},
127 uprobe::{UProbe, UProbeError},
128 xdp::{Xdp, XdpError, XdpMode},
129};
130use crate::{
131 VerifierLogLevel,
132 maps::MapError,
133 pin::PinError,
134 programs::{
135 links::{
136 FdLink, FdLinkId, LinkError, LinkInfo, Links, ProgAttachLink, ProgAttachLinkId,
137 define_link_wrapper, id_as_key, impl_try_from_fdlink, impl_try_into_fdlink,
138 },
139 perf_attach::{PerfLinkIdInner, PerfLinkInner, perf_attach, perf_attach_debugfs},
140 },
141 sys::{
142 EbpfLoadProgramAttrs, NetlinkError, ProgQueryTarget, SyscallError, bpf_btf_get_fd_by_id,
143 bpf_get_object, bpf_link_get_fd_by_id, bpf_load_program, bpf_pin_object,
144 bpf_prog_get_fd_by_id, bpf_prog_query, bpf_prog_test_run, bpf_prog_test_run_raw_tp,
145 bpf_prog_test_run_tracing, iter_link_ids, retry_with_verifier_logs,
146 },
147 util::KernelVersion,
148};
149
150#[derive(Debug, Error)]
152pub enum ProgramError {
153 #[error("the program is already loaded")]
155 AlreadyLoaded,
156
157 #[error("the program is not loaded")]
159 NotLoaded,
160
161 #[error("the program was already attached")]
163 AlreadyAttached,
164
165 #[error("the program is not attached")]
167 NotAttached,
168
169 #[error("the BPF_PROG_LOAD syscall returned {io_error}. Verifier output: {verifier_log}")]
171 LoadError {
172 #[source]
174 io_error: io::Error,
175 verifier_log: VerifierLog,
177 },
178
179 #[error(transparent)]
181 SyscallError(#[from] SyscallError),
182
183 #[error("unknown network interface {name}")]
185 UnknownInterface {
186 name: String,
188 },
189
190 #[error("unexpected program type")]
192 UnexpectedProgramType,
193
194 #[error(transparent)]
196 MapError(#[from] MapError),
197
198 #[error(transparent)]
200 KProbeError(#[from] KProbeError),
201
202 #[error(transparent)]
204 UProbeError(#[from] UProbeError),
205
206 #[error(transparent)]
208 TracePointError(#[from] TracePointError),
209
210 #[error(transparent)]
212 SocketFilterError(#[from] SocketFilterError),
213
214 #[error(transparent)]
216 SkReuseportError(#[from] SkReuseportError),
217
218 #[error(transparent)]
220 XdpError(#[from] XdpError),
221
222 #[error(transparent)]
224 TcError(#[from] TcError),
225
226 #[error(transparent)]
228 ExtensionError(#[from] ExtensionError),
229
230 #[error(transparent)]
232 Btf(#[from] BtfError),
233
234 #[error("the program name `{name}` is invalid")]
236 InvalidName {
237 name: String,
239 },
240
241 #[error(transparent)]
243 IOError(#[from] io::Error),
244
245 #[error("providing an attach cookie is not supported")]
247 AttachCookieNotSupported,
248
249 #[error(transparent)]
251 NetlinkError(#[from] NetlinkError),
252}
253
254#[derive(Debug)]
256pub struct ProgramFd(crate::MockableFd);
257
258impl ProgramFd {
259 pub fn try_clone(&self) -> io::Result<Self> {
261 let Self(inner) = self;
262 let inner = inner.try_clone()?;
263 Ok(Self(inner))
264 }
265}
266
267impl AsFd for ProgramFd {
268 fn as_fd(&self) -> BorrowedFd<'_> {
269 let Self(fd) = self;
270 fd.as_fd()
271 }
272}
273
274pub struct ProgramId(u32);
276
277impl ProgramId {
278 pub const unsafe fn new(id: u32) -> Self {
285 Self(id)
286 }
287}
288
289#[derive(Debug)]
291pub enum Program {
292 KProbe(KProbe),
294 UProbe(UProbe),
296 TracePoint(TracePoint),
298 SocketFilter(SocketFilter),
300 Xdp(Xdp),
302 SkMsg(SkMsg),
304 SkSkb(SkSkb),
306 CgroupSockAddr(CgroupSockAddr),
308 SockOps(SockOps),
310 SchedClassifier(SchedClassifier),
312 CgroupSkb(CgroupSkb),
314 CgroupSysctl(CgroupSysctl),
316 CgroupSockopt(CgroupSockopt),
318 LircMode2(LircMode2),
320 PerfEvent(PerfEvent),
322 RawTracePoint(RawTracePoint),
324 Lsm(Lsm),
326 LsmCgroup(LsmCgroup),
328 BtfTracePoint(BtfTracePoint),
330 FEntry(FEntry),
332 FExit(FExit),
334 FlowDissector(FlowDissector),
336 Extension(Extension),
338 SkLookup(SkLookup),
340 SkReuseport(SkReuseport),
342 CgroupSock(CgroupSock),
344 CgroupDevice(CgroupDevice),
346 Iter(Iter),
348}
349
350impl Program {
351 pub const fn prog_type(&self) -> ProgramType {
353 match self {
354 Self::KProbe(_) | Self::UProbe(_) => ProgramType::KProbe,
355 Self::TracePoint(_) => ProgramType::TracePoint,
356 Self::SocketFilter(_) => ProgramType::SocketFilter,
357 Self::Xdp(_) => ProgramType::Xdp,
358 Self::SkMsg(_) => ProgramType::SkMsg,
359 Self::SkSkb(_) => ProgramType::SkSkb,
360 Self::SockOps(_) => ProgramType::SockOps,
361 Self::SchedClassifier(_) => ProgramType::SchedClassifier,
362 Self::CgroupSkb(_) => ProgramType::CgroupSkb,
363 Self::CgroupSysctl(_) => ProgramType::CgroupSysctl,
364 Self::CgroupSockopt(_) => ProgramType::CgroupSockopt,
365 Self::LircMode2(_) => ProgramType::LircMode2,
366 Self::PerfEvent(_) => ProgramType::PerfEvent,
367 Self::RawTracePoint(_) => ProgramType::RawTracePoint,
368 Self::Lsm(_) => ProgramType::Lsm(LsmAttachType::Mac),
369 Self::LsmCgroup(_) => ProgramType::Lsm(LsmAttachType::Cgroup),
370 Self::BtfTracePoint(_) | Self::FEntry(_) | Self::FExit(_) | Self::Iter(_) => {
380 ProgramType::Tracing
381 }
382 Self::Extension(_) => ProgramType::Extension,
383 Self::CgroupSockAddr(_) => ProgramType::CgroupSockAddr,
384 Self::SkLookup(_) => ProgramType::SkLookup,
385 Self::SkReuseport(_) => ProgramType::SkReuseport,
386 Self::CgroupSock(_) => ProgramType::CgroupSock,
387 Self::CgroupDevice(_) => ProgramType::CgroupDevice,
388 Self::FlowDissector(_) => ProgramType::FlowDissector,
389 }
390 }
391
392 pub fn pin<P: AsRef<Path>>(&mut self, path: P) -> Result<(), PinError> {
394 match self {
395 Self::KProbe(p) => p.pin(path),
396 Self::UProbe(p) => p.pin(path),
397 Self::TracePoint(p) => p.pin(path),
398 Self::SocketFilter(p) => p.pin(path),
399 Self::Xdp(p) => p.pin(path),
400 Self::SkMsg(p) => p.pin(path),
401 Self::SkSkb(p) => p.pin(path),
402 Self::SockOps(p) => p.pin(path),
403 Self::SchedClassifier(p) => p.pin(path),
404 Self::CgroupSkb(p) => p.pin(path),
405 Self::CgroupSysctl(p) => p.pin(path),
406 Self::CgroupSockopt(p) => p.pin(path),
407 Self::LircMode2(p) => p.pin(path),
408 Self::PerfEvent(p) => p.pin(path),
409 Self::RawTracePoint(p) => p.pin(path),
410 Self::Lsm(p) => p.pin(path),
411 Self::LsmCgroup(p) => p.pin(path),
412 Self::BtfTracePoint(p) => p.pin(path),
413 Self::FEntry(p) => p.pin(path),
414 Self::FExit(p) => p.pin(path),
415 Self::FlowDissector(p) => p.pin(path),
416 Self::Extension(p) => p.pin(path),
417 Self::CgroupSockAddr(p) => p.pin(path),
418 Self::SkLookup(p) => p.pin(path),
419 Self::SkReuseport(p) => p.pin(path),
420 Self::CgroupSock(p) => p.pin(path),
421 Self::CgroupDevice(p) => p.pin(path),
422 Self::Iter(p) => p.pin(path),
423 }
424 }
425
426 pub fn unload(self) -> Result<(), ProgramError> {
428 match self {
429 Self::KProbe(mut p) => p.unload(),
430 Self::UProbe(mut p) => p.unload(),
431 Self::TracePoint(mut p) => p.unload(),
432 Self::SocketFilter(mut p) => p.unload(),
433 Self::Xdp(mut p) => p.unload(),
434 Self::SkMsg(mut p) => p.unload(),
435 Self::SkSkb(mut p) => p.unload(),
436 Self::SockOps(mut p) => p.unload(),
437 Self::SchedClassifier(mut p) => p.unload(),
438 Self::CgroupSkb(mut p) => p.unload(),
439 Self::CgroupSysctl(mut p) => p.unload(),
440 Self::CgroupSockopt(mut p) => p.unload(),
441 Self::LircMode2(mut p) => p.unload(),
442 Self::PerfEvent(mut p) => p.unload(),
443 Self::RawTracePoint(mut p) => p.unload(),
444 Self::Lsm(mut p) => p.unload(),
445 Self::LsmCgroup(mut p) => p.unload(),
446 Self::BtfTracePoint(mut p) => p.unload(),
447 Self::FEntry(mut p) => p.unload(),
448 Self::FExit(mut p) => p.unload(),
449 Self::FlowDissector(mut p) => p.unload(),
450 Self::Extension(mut p) => p.unload(),
451 Self::CgroupSockAddr(mut p) => p.unload(),
452 Self::SkLookup(mut p) => p.unload(),
453 Self::SkReuseport(mut p) => p.unload(),
454 Self::CgroupSock(mut p) => p.unload(),
455 Self::CgroupDevice(mut p) => p.unload(),
456 Self::Iter(mut p) => p.unload(),
457 }
458 }
459
460 pub fn fd(&self) -> Result<&ProgramFd, ProgramError> {
464 match self {
465 Self::KProbe(p) => p.fd(),
466 Self::UProbe(p) => p.fd(),
467 Self::TracePoint(p) => p.fd(),
468 Self::SocketFilter(p) => p.fd(),
469 Self::Xdp(p) => p.fd(),
470 Self::SkMsg(p) => p.fd(),
471 Self::SkSkb(p) => p.fd(),
472 Self::SockOps(p) => p.fd(),
473 Self::SchedClassifier(p) => p.fd(),
474 Self::CgroupSkb(p) => p.fd(),
475 Self::CgroupSysctl(p) => p.fd(),
476 Self::CgroupSockopt(p) => p.fd(),
477 Self::LircMode2(p) => p.fd(),
478 Self::PerfEvent(p) => p.fd(),
479 Self::RawTracePoint(p) => p.fd(),
480 Self::Lsm(p) => p.fd(),
481 Self::LsmCgroup(p) => p.fd(),
482 Self::BtfTracePoint(p) => p.fd(),
483 Self::FEntry(p) => p.fd(),
484 Self::FExit(p) => p.fd(),
485 Self::FlowDissector(p) => p.fd(),
486 Self::Extension(p) => p.fd(),
487 Self::CgroupSockAddr(p) => p.fd(),
488 Self::SkLookup(p) => p.fd(),
489 Self::SkReuseport(p) => p.fd(),
490 Self::CgroupSock(p) => p.fd(),
491 Self::CgroupDevice(p) => p.fd(),
492 Self::Iter(p) => p.fd(),
493 }
494 }
495
496 pub fn info(&self) -> Result<ProgramInfo, ProgramError> {
501 match self {
502 Self::KProbe(p) => p.info(),
503 Self::UProbe(p) => p.info(),
504 Self::TracePoint(p) => p.info(),
505 Self::SocketFilter(p) => p.info(),
506 Self::Xdp(p) => p.info(),
507 Self::SkMsg(p) => p.info(),
508 Self::SkSkb(p) => p.info(),
509 Self::SockOps(p) => p.info(),
510 Self::SchedClassifier(p) => p.info(),
511 Self::CgroupSkb(p) => p.info(),
512 Self::CgroupSysctl(p) => p.info(),
513 Self::CgroupSockopt(p) => p.info(),
514 Self::LircMode2(p) => p.info(),
515 Self::PerfEvent(p) => p.info(),
516 Self::RawTracePoint(p) => p.info(),
517 Self::Lsm(p) => p.info(),
518 Self::LsmCgroup(p) => p.info(),
519 Self::BtfTracePoint(p) => p.info(),
520 Self::FEntry(p) => p.info(),
521 Self::FExit(p) => p.info(),
522 Self::FlowDissector(p) => p.info(),
523 Self::Extension(p) => p.info(),
524 Self::CgroupSockAddr(p) => p.info(),
525 Self::SkLookup(p) => p.info(),
526 Self::SkReuseport(p) => p.info(),
527 Self::CgroupSock(p) => p.info(),
528 Self::CgroupDevice(p) => p.info(),
529 Self::Iter(p) => p.info(),
530 }
531 }
532}
533
534#[derive(Debug)]
535pub(crate) struct ProgramData<T: Link> {
536 pub(crate) name: Option<Cow<'static, str>>,
537 pub(crate) obj: Option<(aya_obj::Program, aya_obj::Function)>,
538 pub(crate) fd: Option<ProgramFd>,
539 pub(crate) links: Links<T>,
540 pub(crate) attach_btf_obj_fd: Option<crate::MockableFd>,
541 pub(crate) attach_btf_id: Option<u32>,
542 pub(crate) attach_prog_fd: Option<ProgramFd>,
543 pub(crate) btf_fd: Option<Arc<crate::MockableFd>>,
544 pub(crate) verifier_log_level: VerifierLogLevel,
545 pub(crate) path: Option<PathBuf>,
546 pub(crate) flags: u32,
547}
548
549impl<T: Link> ProgramData<T> {
550 pub(crate) fn new(
551 name: Option<Cow<'static, str>>,
552 obj: (aya_obj::Program, aya_obj::Function),
553 btf_fd: Option<Arc<crate::MockableFd>>,
554 verifier_log_level: VerifierLogLevel,
555 ) -> Self {
556 Self {
557 name,
558 obj: Some(obj),
559 fd: None,
560 links: Links::new(),
561 attach_btf_obj_fd: None,
562 attach_btf_id: None,
563 attach_prog_fd: None,
564 btf_fd,
565 verifier_log_level,
566 path: None,
567 flags: 0,
568 }
569 }
570
571 pub(crate) fn from_bpf_prog_info(
572 name: Option<Cow<'static, str>>,
573 fd: crate::MockableFd,
574 path: &Path,
575 info: bpf_prog_info,
576 verifier_log_level: VerifierLogLevel,
577 ) -> Result<Self, ProgramError> {
578 let attach_btf_id = (info.attach_btf_id > 0).then_some(info.attach_btf_id);
579 let attach_btf_obj_fd = (info.attach_btf_obj_id != 0)
580 .then(|| bpf_btf_get_fd_by_id(info.attach_btf_obj_id))
581 .transpose()?;
582
583 Ok(Self {
584 name,
585 obj: None,
586 fd: Some(ProgramFd(fd)),
587 links: Links::new(),
588 attach_btf_obj_fd,
589 attach_btf_id,
590 attach_prog_fd: None,
591 btf_fd: None,
592 verifier_log_level,
593 path: Some(path.to_path_buf()),
594 flags: 0,
595 })
596 }
597
598 pub(crate) fn from_pinned_path<P: AsRef<Path>>(
599 path: P,
600 verifier_log_level: VerifierLogLevel,
601 ) -> Result<Self, ProgramError> {
602 use std::os::unix::ffi::OsStrExt as _;
603
604 let path_string = CString::new(path.as_ref().as_os_str().as_bytes()).unwrap();
606 let fd = bpf_get_object(&path_string).map_err(|io_error| SyscallError {
607 call: "bpf_obj_get",
608 io_error,
609 })?;
610
611 let info = ProgramInfo::new_from_fd(fd.as_fd())?;
612 let name = info.name_as_str().map(ToOwned::to_owned).map(Into::into);
613 Self::from_bpf_prog_info(name, fd, path.as_ref(), info.0, verifier_log_level)
614 }
615}
616
617impl<T: Link> ProgramData<T> {
618 fn fd(&self) -> Result<&ProgramFd, ProgramError> {
619 self.fd.as_ref().ok_or(ProgramError::NotLoaded)
620 }
621}
622
623fn test_run<T: Link>(
624 data: &ProgramData<T>,
625 opts: TestRunOptions<'_>,
626) -> Result<TestRunResult, ProgramError> {
627 let fd = data.fd()?.as_fd();
628 bpf_prog_test_run(fd, opts).map_err(Into::into)
629}
630
631fn test_run_raw_tp<T: Link>(
632 data: &ProgramData<T>,
633 opts: RawTracePointRunOptions,
634) -> Result<RawTracePointTestRunResult, ProgramError> {
635 let fd = data.fd()?.as_fd();
636 bpf_prog_test_run_raw_tp(fd, opts).map_err(Into::into)
637}
638
639fn test_run_tracing<T: Link>(data: &ProgramData<T>) -> Result<(), ProgramError> {
640 let fd = data.fd()?.as_fd();
641 bpf_prog_test_run_tracing(fd).map_err(Into::into)
642}
643
644fn unload_program<T: Link>(data: &mut ProgramData<T>) -> Result<(), ProgramError> {
645 data.links.remove_all()?;
646 data.fd
647 .take()
648 .ok_or(ProgramError::NotLoaded)
649 .map(|ProgramFd { .. }| ())
650}
651
652fn pin_program<T: Link, P: AsRef<Path>>(data: &ProgramData<T>, path: P) -> Result<(), PinError> {
653 use std::os::unix::ffi::OsStrExt as _;
654
655 let fd = data.fd.as_ref().ok_or_else(|| PinError::NoFd {
656 name: data
657 .name
658 .as_deref()
659 .unwrap_or("<unknown program>")
660 .to_string(),
661 })?;
662 let path = path.as_ref();
663 let path_string =
664 CString::new(path.as_os_str().as_bytes()).map_err(|error| PinError::InvalidPinPath {
665 path: path.into(),
666 error,
667 })?;
668 bpf_pin_object(fd.as_fd(), &path_string).map_err(|io_error| SyscallError {
669 call: "BPF_OBJ_PIN",
670 io_error,
671 })?;
672 Ok(())
673}
674
675fn load_program_without_attach_type<T: Link>(
676 prog_type: bpf_prog_type,
677 data: &mut ProgramData<T>,
678) -> Result<(), ProgramError> {
679 load_program(prog_type, None, data)
680}
681
682fn load_program_with_attach_type<A: Into<bpf_attach_type>, T: Link>(
683 prog_type: bpf_prog_type,
684 expected_attach_type: A,
685 data: &mut ProgramData<T>,
686) -> Result<(), ProgramError> {
687 load_program(prog_type, Some(expected_attach_type.into()), data)
688}
689
690fn load_program<T: Link>(
691 prog_type: bpf_prog_type,
692 expected_attach_type: Option<bpf_attach_type>,
693 data: &mut ProgramData<T>,
694) -> Result<(), ProgramError> {
695 let ProgramData {
696 name,
697 obj,
698 fd,
699 links: _,
700 attach_btf_obj_fd,
701 attach_btf_id,
702 attach_prog_fd,
703 btf_fd,
704 verifier_log_level,
705 path: _,
706 flags,
707 } = data;
708 if fd.is_some() {
709 return Err(ProgramError::AlreadyLoaded);
710 }
711 if obj.is_none() {
712 return Err(ProgramError::AlreadyLoaded);
714 }
715 let obj = obj.as_ref().unwrap();
716 let (
717 aya_obj::Program {
718 license,
719 kernel_version,
720 ..
721 },
722 aya_obj::Function {
723 instructions,
724 func_info,
725 line_info,
726 func_info_rec_size,
727 line_info_rec_size,
728 ..
729 },
730 ) = obj;
731
732 let target_kernel_version =
733 kernel_version.unwrap_or_else(|| KernelVersion::current().map_or(0, KernelVersion::code));
734
735 let prog_name = if let Some(name) = name.as_deref() {
736 let prog_name = CString::new(name).map_err(|err @ std::ffi::NulError { .. }| {
737 let name = err.into_vec();
738 let name = unsafe { String::from_utf8_unchecked(name) };
739 ProgramError::InvalidName { name }
740 })?;
741 Some(prog_name)
742 } else {
743 None
744 };
745
746 let attr = EbpfLoadProgramAttrs {
747 name: prog_name,
748 ty: prog_type,
749 insns: instructions,
750 license,
751 kernel_version: target_kernel_version,
752 expected_attach_type,
753 prog_btf_fd: btf_fd.as_ref().map(|f| f.as_fd()),
754 attach_btf_obj_fd: attach_btf_obj_fd.as_ref().map(|fd| fd.as_fd()),
755 attach_btf_id: *attach_btf_id,
756 attach_prog_fd: attach_prog_fd.as_ref().map(|fd| fd.as_fd()),
757 func_info_rec_size: *func_info_rec_size,
758 func_info: func_info.clone(),
759 line_info_rec_size: *line_info_rec_size,
760 line_info: line_info.clone(),
761 flags: *flags,
762 };
763
764 let (ret, verifier_log) = retry_with_verifier_logs(10, |logger| {
765 bpf_load_program(&attr, logger, *verifier_log_level)
766 });
767
768 match ret {
769 Ok(prog_fd) => {
770 *fd = Some(ProgramFd(prog_fd));
771 Ok(())
772 }
773 Err(io_error) => Err(ProgramError::LoadError {
774 io_error,
775 verifier_log,
776 }),
777 }
778}
779
780pub(crate) fn query(
781 target: ProgQueryTarget<'_>,
782 attach_type: bpf_attach_type,
783 query_flags: u32,
784 attach_flags: &mut Option<u32>,
785) -> Result<(u64, Vec<u32>), ProgramError> {
786 let mut prog_ids = vec![0u32; 64];
787 let mut prog_cnt = prog_ids.len() as u32;
788 let mut revision = 0;
789
790 let mut retries = 0;
791
792 loop {
793 match bpf_prog_query(
794 &target,
795 attach_type,
796 query_flags,
797 attach_flags.as_mut(),
798 &mut prog_ids,
799 &mut prog_cnt,
800 &mut revision,
801 ) {
802 Ok(()) => {
803 prog_ids.resize(prog_cnt as usize, 0);
804 return Ok((revision, prog_ids));
805 }
806 Err(io_error) => {
807 if retries == 0 && io_error.raw_os_error() == Some(ENOSPC) {
808 prog_ids.resize(prog_cnt as usize, 0);
809 retries += 1;
810 } else {
811 return Err(SyscallError {
812 call: "bpf_prog_query",
813 io_error,
814 }
815 .into());
816 }
817 }
818 }
819 }
820}
821
822macro_rules! impl_program_unload {
823 ($($struct_name:ident),+ $(,)?) => {
824 $(
825 impl $struct_name {
826 pub fn unload(&mut self) -> Result<(), ProgramError> {
833 unload_program(&mut self.data)
834 }
835 }
836
837 impl Drop for $struct_name {
838 fn drop(&mut self) {
839 let _unused: Result<(), ProgramError> = self.unload();
840 }
841 }
842 )+
843 }
844}
845
846impl_program_unload!(
847 KProbe,
848 UProbe,
849 TracePoint,
850 SocketFilter,
851 Xdp,
852 SkMsg,
853 SkSkb,
854 SchedClassifier,
855 CgroupSkb,
856 CgroupSysctl,
857 CgroupSockopt,
858 LircMode2,
859 PerfEvent,
860 Lsm,
861 LsmCgroup,
862 RawTracePoint,
863 BtfTracePoint,
864 FEntry,
865 FExit,
866 FlowDissector,
867 Extension,
868 CgroupSockAddr,
869 SkLookup,
870 SkReuseport,
871 SockOps,
872 CgroupSock,
873 CgroupDevice,
874 Iter,
875);
876
877macro_rules! impl_fd {
878 ($($struct_name:ident),+ $(,)?) => {
879 $(
880 impl $struct_name {
881 pub fn fd(&self) -> Result<&ProgramFd, ProgramError> {
883 self.data.fd()
884 }
885 }
886 )+
887 }
888}
889
890impl_fd!(
891 KProbe,
892 UProbe,
893 TracePoint,
894 SocketFilter,
895 Xdp,
896 SkMsg,
897 SkSkb,
898 SchedClassifier,
899 CgroupSkb,
900 CgroupSysctl,
901 CgroupSockopt,
902 LircMode2,
903 PerfEvent,
904 Lsm,
905 LsmCgroup,
906 RawTracePoint,
907 BtfTracePoint,
908 FEntry,
909 FExit,
910 FlowDissector,
911 Extension,
912 CgroupSockAddr,
913 SkLookup,
914 SkReuseport,
915 SockOps,
916 CgroupSock,
917 CgroupDevice,
918 Iter,
919);
920
921#[derive(Debug)]
926pub struct TestRunAttrs {
927 pub(crate) batch_size: u32,
928 pub(crate) flags: u32,
929}
930
931impl TestRunAttrs {
932 pub const fn new() -> Self {
934 Self {
935 batch_size: 0,
936 flags: 0,
937 }
938 }
939
940 #[must_use]
945 pub const fn xdp_live_frames(mut self, batch_size: u32) -> Self {
946 self.batch_size = batch_size;
947 self.flags |= BPF_F_TEST_XDP_LIVE_FRAMES;
948 self
949 }
950}
951
952impl Default for TestRunAttrs {
953 fn default() -> Self {
954 Self::new()
955 }
956}
957
958#[derive(Debug)]
963pub struct TestRunOptions<'a> {
964 pub data_in: Option<&'a [u8]>,
966 pub data_out: Option<&'a mut [u8]>,
968 pub ctx_in: Option<&'a [u8]>,
970 pub ctx_out: Option<&'a mut [u8]>,
972 pub repeat: u32,
974 pub attrs: TestRunAttrs,
976}
977
978impl Default for TestRunOptions<'_> {
979 fn default() -> Self {
980 Self::new()
981 }
982}
983
984impl TestRunOptions<'_> {
985 pub const fn new() -> Self {
987 Self {
988 data_in: None,
989 data_out: None,
990 ctx_in: None,
991 ctx_out: None,
992 repeat: 1,
993 attrs: TestRunAttrs::new(),
994 }
995 }
996}
997
998#[derive(Debug, Default)]
1012pub struct RawTracePointRunOptions {
1013 pub args: [u64; 12],
1023 pub cpu: Option<u32>,
1028}
1029
1030impl RawTracePointRunOptions {
1031 pub const fn new() -> Self {
1033 Self {
1034 args: [0; 12],
1035 cpu: None,
1036 }
1037 }
1038}
1039
1040#[derive(Debug)]
1042pub struct TestRunResult {
1043 pub return_value: u32,
1045 pub duration: std::time::Duration,
1047 pub data_size_out: u32,
1049 pub ctx_size_out: u32,
1051}
1052
1053#[derive(Debug)]
1058pub struct RawTracePointTestRunResult {
1059 pub return_value: u32,
1061}
1062
1063pub trait TestRun {
1065 type Opts<'a>;
1072
1073 type Result;
1075
1076 fn test_run(&self, opts: Self::Opts<'_>) -> Result<Self::Result, ProgramError>;
1116}
1117
1118macro_rules! impl_program_test_run {
1119 ($($struct_name:ident), + $(,)?) => {
1120 $(
1121 impl TestRun for $struct_name {
1122 type Opts<'a> = TestRunOptions<'a>;
1123 type Result = TestRunResult;
1124
1125 fn test_run(&self, opts: Self::Opts<'_>) -> Result<Self::Result, ProgramError> {
1126 test_run(&self.data, opts)
1127 }
1128 }
1129 )+
1130 }
1131}
1132
1133impl_program_test_run!(SocketFilter, SchedClassifier, Xdp, CgroupSkb, FlowDissector);
1134
1135impl TestRun for RawTracePoint {
1136 type Opts<'a> = RawTracePointRunOptions;
1137 type Result = RawTracePointTestRunResult;
1138
1139 fn test_run(&self, opts: Self::Opts<'_>) -> Result<Self::Result, ProgramError> {
1140 test_run_raw_tp(&self.data, opts)
1141 }
1142}
1143
1144impl TestRun for FExit {
1145 type Opts<'a> = ();
1150 type Result = ();
1153
1154 fn test_run(&self, _opts: Self::Opts<'_>) -> Result<Self::Result, ProgramError> {
1155 test_run_tracing(&self.data)
1156 }
1157}
1158
1159pub trait MultiProgram {
1166 fn fd(&self) -> Result<BorrowedFd<'_>, ProgramError>;
1168}
1169
1170macro_rules! impl_multiprog_fd {
1171 ($($struct_name:ident),+ $(,)?) => {
1172 $(
1173 impl MultiProgram for $struct_name {
1174 fn fd(&self) -> Result<BorrowedFd<'_>, ProgramError> {
1175 Ok(self.fd()?.as_fd())
1176 }
1177 }
1178 )+
1179 }
1180}
1181
1182impl_multiprog_fd!(SchedClassifier);
1183
1184pub trait MultiProgLink {
1191 fn fd(&self) -> Result<BorrowedFd<'_>, LinkError>;
1193}
1194
1195macro_rules! impl_multiproglink_fd {
1196 ($($struct_name:ident),+ $(,)?) => {
1197 $(
1198 impl MultiProgLink for $struct_name {
1199 fn fd(&self) -> Result<BorrowedFd<'_>, LinkError> {
1200 let link: &FdLink = self.try_into()?;
1201 Ok(link.fd.as_fd())
1202 }
1203 }
1204 )+
1205 }
1206}
1207
1208impl_multiproglink_fd!(SchedClassifierLink);
1209
1210macro_rules! impl_program_pin{
1211 ($($struct_name:ident),+ $(,)?) => {
1212 $(
1213 impl $struct_name {
1214 pub fn pin<P: AsRef<Path>>(&mut self, path: P) -> Result<(), PinError> {
1221 self.data.path = Some(path.as_ref().to_path_buf());
1222 pin_program(&self.data, path)
1223 }
1224
1225 pub fn unpin(&mut self) -> Result<(), io::Error> {
1227 if let Some(path) = self.data.path.take() {
1228 std::fs::remove_file(path)?;
1229 }
1230 Ok(())
1231 }
1232 }
1233 )+
1234 }
1235}
1236
1237impl_program_pin!(
1238 KProbe,
1239 UProbe,
1240 TracePoint,
1241 SocketFilter,
1242 Xdp,
1243 SkMsg,
1244 SkSkb,
1245 SchedClassifier,
1246 CgroupSkb,
1247 CgroupSysctl,
1248 CgroupSockopt,
1249 LircMode2,
1250 PerfEvent,
1251 Lsm,
1252 LsmCgroup,
1253 RawTracePoint,
1254 BtfTracePoint,
1255 FEntry,
1256 FExit,
1257 FlowDissector,
1258 Extension,
1259 CgroupSockAddr,
1260 SkLookup,
1261 SkReuseport,
1262 SockOps,
1263 CgroupSock,
1264 CgroupDevice,
1265 Iter,
1266);
1267
1268macro_rules! impl_from_pin {
1269 ($($struct_name:ident),+ $(,)?) => {
1270 $(
1271 impl $struct_name {
1272 pub fn from_pin<P: AsRef<Path>>(path: P) -> Result<Self, ProgramError> {
1279 let data = ProgramData::from_pinned_path(path, VerifierLogLevel::default())?;
1280 Ok(Self { data })
1281 }
1282 }
1283 )+
1284 }
1285}
1286
1287impl_from_pin!(
1289 TracePoint,
1290 SkMsg,
1291 CgroupSysctl,
1292 LircMode2,
1293 Lsm,
1294 PerfEvent,
1295 RawTracePoint,
1296 BtfTracePoint,
1297 FEntry,
1298 FExit,
1299 FlowDissector,
1300 Extension,
1301 SkLookup,
1302 SockOps,
1303 CgroupDevice,
1304 Iter,
1305);
1306
1307macro_rules! impl_from_prog_info {
1308 (
1309 $(#[$doc:meta])*
1310 @safety
1311 [$($safety:tt)?]
1312 @rest
1313 $struct_name:ident $($var:ident : $var_ty:ty)?
1314 ) => {
1315 impl $struct_name {
1316 $(#[$doc])*
1327 pub $($safety)?
1328 fn from_program_info(
1329 info: ProgramInfo,
1330 name: Cow<'static, str>,
1331 $($var: $var_ty,)?
1332 ) -> Result<Self, ProgramError> {
1333 if info.program_type() != Self::PROGRAM_TYPE.into() {
1334 return Err(ProgramError::UnexpectedProgramType {});
1335 }
1336 let ProgramInfo(bpf_program_info) = info;
1337 let fd = info.fd()?;
1338 let fd = fd.as_fd().try_clone_to_owned()?;
1339
1340 Ok(Self {
1341 data: ProgramData::from_bpf_prog_info(
1342 Some(name),
1343 crate::MockableFd::from_fd(fd),
1344 Path::new(""),
1345 bpf_program_info,
1346 VerifierLogLevel::default(),
1347 )?,
1348 $($var,)?
1349 })
1350 }
1351 }
1352 };
1353
1354 (
1356 unsafe $struct_name:ident $($var:ident : $var_ty:ty)? $(, $($rest:tt)*)?
1357 ) => {
1358 impl_from_prog_info! {
1359 @safety [unsafe]
1368 @rest $struct_name $($var : $var_ty)?
1369 }
1370 $( impl_from_prog_info!($($rest)*); )?
1371 };
1372
1373 (
1375 $struct_name:ident $($var:ident : $var_ty:ty)? $(, $($rest:tt)*)?
1376 ) => {
1377 impl_from_prog_info! {
1378 @safety []
1379 @rest $struct_name $($var : $var_ty)?
1380 }
1381 $( impl_from_prog_info!($($rest)*); )?
1382 };
1383
1384 (
1386 $(,)?
1387 ) => {};
1388}
1389
1390impl_from_prog_info!(
1391 unsafe KProbe kind : ProbeKind,
1392 unsafe UProbe kind : ProbeKind,
1393 TracePoint,
1394 Xdp attach_type : XdpAttachType,
1395 SkMsg,
1396 SkSkb kind : SkSkbKind,
1397 SockOps,
1398 SchedClassifier,
1399 CgroupSkb attach_type : Option<CgroupSkbAttachType>,
1400 CgroupSysctl,
1401 CgroupSockopt attach_type : CgroupSockoptAttachType,
1402 LircMode2,
1403 PerfEvent,
1404 Lsm,
1405 RawTracePoint,
1406 unsafe BtfTracePoint,
1407 unsafe FEntry,
1408 unsafe FExit,
1409 Extension,
1410 SkLookup,
1411 SkReuseport attach_type : SkReuseportAttachType,
1412 CgroupDevice,
1413 Iter,
1414);
1415
1416macro_rules! impl_try_from_program {
1417 ($($ty:ident),+ $(,)?) => {
1418 $(
1419 impl<'a> TryFrom<&'a Program> for &'a $ty {
1420 type Error = ProgramError;
1421
1422 fn try_from(program: &'a Program) -> Result<&'a $ty, ProgramError> {
1423 match program {
1424 Program::$ty(p) => Ok(p),
1425 _ => Err(ProgramError::UnexpectedProgramType),
1426 }
1427 }
1428 }
1429
1430 impl<'a> TryFrom<&'a mut Program> for &'a mut $ty {
1431 type Error = ProgramError;
1432
1433 fn try_from(program: &'a mut Program) -> Result<&'a mut $ty, ProgramError> {
1434 match program {
1435 Program::$ty(p) => Ok(p),
1436 _ => Err(ProgramError::UnexpectedProgramType),
1437 }
1438 }
1439 }
1440 )+
1441 }
1442}
1443
1444impl_try_from_program!(
1445 KProbe,
1446 UProbe,
1447 TracePoint,
1448 SocketFilter,
1449 Xdp,
1450 SkMsg,
1451 SkSkb,
1452 SockOps,
1453 SchedClassifier,
1454 CgroupSkb,
1455 CgroupSysctl,
1456 CgroupSockopt,
1457 LircMode2,
1458 PerfEvent,
1459 Lsm,
1460 LsmCgroup,
1461 RawTracePoint,
1462 BtfTracePoint,
1463 FEntry,
1464 FExit,
1465 FlowDissector,
1466 Extension,
1467 CgroupSockAddr,
1468 SkLookup,
1469 SkReuseport,
1470 CgroupSock,
1471 CgroupDevice,
1472 Iter,
1473);
1474
1475impl_info!(
1476 KProbe,
1477 UProbe,
1478 TracePoint,
1479 SocketFilter,
1480 Xdp,
1481 SkMsg,
1482 SkSkb,
1483 SchedClassifier,
1484 CgroupSkb,
1485 CgroupSysctl,
1486 CgroupSockopt,
1487 LircMode2,
1488 PerfEvent,
1489 Lsm,
1490 LsmCgroup,
1491 RawTracePoint,
1492 BtfTracePoint,
1493 FEntry,
1494 FExit,
1495 FlowDissector,
1496 Extension,
1497 CgroupSockAddr,
1498 SkLookup,
1499 SkReuseport,
1500 SockOps,
1501 CgroupSock,
1502 CgroupDevice,
1503 Iter,
1504);
1505
1506pub fn loaded_links() -> impl Iterator<Item = Result<LinkInfo, LinkError>> {
1528 iter_link_ids()
1529 .map(|id| {
1530 let id = id?;
1531 bpf_link_get_fd_by_id(id)
1532 })
1533 .map(|fd| {
1534 let fd = fd?;
1535 LinkInfo::new_from_fd(fd.as_fd())
1536 })
1537}