use std::ffi::c_void;
use std::ffi::CStr;
use std::mem;
use std::mem::size_of;
use std::mem::size_of_val;
use std::os::unix::io::AsFd;
use std::os::unix::io::AsRawFd;
use std::os::unix::io::BorrowedFd;
use std::os::unix::io::FromRawFd;
use std::os::unix::io::OwnedFd;
use std::path::Path;
use std::ptr;
use std::ptr::NonNull;
use std::slice;
use libbpf_sys::bpf_func_id;
use strum_macros::Display;
use crate::util;
use crate::AsRawLibbpf;
use crate::Error;
use crate::Link;
use crate::Result;
#[derive(Clone, Debug, Default)]
pub struct UprobeOpts {
pub ref_ctr_offset: usize,
pub cookie: u64,
pub retprobe: bool,
pub func_name: String,
#[doc(hidden)]
pub _non_exhaustive: (),
}
#[derive(Clone, Debug, Default)]
pub struct UsdtOpts {
pub cookie: u64,
#[doc(hidden)]
pub _non_exhaustive: (),
}
impl From<UsdtOpts> for libbpf_sys::bpf_usdt_opts {
fn from(opts: UsdtOpts) -> Self {
let UsdtOpts {
cookie,
_non_exhaustive,
} = opts;
#[allow(clippy::needless_update)]
libbpf_sys::bpf_usdt_opts {
sz: size_of::<Self>() as _,
usdt_cookie: cookie,
..Default::default()
}
}
}
#[derive(Clone, Debug, Default)]
pub struct TracepointOpts {
pub cookie: u64,
#[doc(hidden)]
pub _non_exhaustive: (),
}
impl From<TracepointOpts> for libbpf_sys::bpf_tracepoint_opts {
fn from(opts: TracepointOpts) -> Self {
let TracepointOpts {
cookie,
_non_exhaustive,
} = opts;
#[allow(clippy::needless_update)]
libbpf_sys::bpf_tracepoint_opts {
sz: size_of::<Self>() as _,
bpf_cookie: cookie,
..Default::default()
}
}
}
#[derive(Debug)]
pub struct OpenProgram {
ptr: NonNull<libbpf_sys::bpf_program>,
section: String,
}
#[allow(missing_docs)]
impl OpenProgram {
pub(crate) unsafe fn new(ptr: NonNull<libbpf_sys::bpf_program>) -> Result<Self> {
let section = unsafe { libbpf_sys::bpf_program__section_name(ptr.as_ptr()) };
let section = util::c_ptr_to_string(section)?;
Ok(Self { ptr, section })
}
pub fn set_prog_type(&mut self, prog_type: ProgramType) {
unsafe {
libbpf_sys::bpf_program__set_type(self.ptr.as_ptr(), prog_type as u32);
}
}
pub fn prog_type(&self) -> ProgramType {
ProgramType::from(unsafe { libbpf_sys::bpf_program__type(self.ptr.as_ptr()) })
}
pub fn set_attach_type(&mut self, attach_type: ProgramAttachType) {
unsafe {
libbpf_sys::bpf_program__set_expected_attach_type(
self.ptr.as_ptr(),
attach_type as u32,
);
}
}
pub fn set_ifindex(&mut self, idx: u32) {
unsafe {
libbpf_sys::bpf_program__set_ifindex(self.ptr.as_ptr(), idx);
}
}
pub fn set_log_level(&mut self, log_level: u32) -> Result<()> {
let ret = unsafe { libbpf_sys::bpf_program__set_log_level(self.ptr.as_ptr(), log_level) };
util::parse_ret(ret)
}
pub fn section(&self) -> &str {
&self.section
}
pub fn name(&self) -> Result<&str> {
let name_ptr = unsafe { libbpf_sys::bpf_program__name(self.ptr.as_ptr()) };
let name_c_str = unsafe { CStr::from_ptr(name_ptr) };
name_c_str.to_str().map_err(Error::with_invalid_data)
}
pub fn set_autoload(&mut self, autoload: bool) -> Result<()> {
let ret = unsafe { libbpf_sys::bpf_program__set_autoload(self.ptr.as_ptr(), autoload) };
util::parse_ret(ret)
}
pub fn set_attach_target(
&mut self,
attach_prog_fd: i32,
attach_func_name: Option<String>,
) -> Result<()> {
let ret = if let Some(name) = attach_func_name {
let name_c = util::str_to_cstring(&name)?;
unsafe {
libbpf_sys::bpf_program__set_attach_target(
self.ptr.as_ptr(),
attach_prog_fd,
name_c.as_ptr(),
)
}
} else {
unsafe {
libbpf_sys::bpf_program__set_attach_target(
self.ptr.as_ptr(),
attach_prog_fd,
ptr::null(),
)
}
};
util::parse_ret(ret)
}
pub fn set_flags(&self, flags: u32) -> Result<()> {
let ret = unsafe { libbpf_sys::bpf_program__set_flags(self.ptr.as_ptr(), flags) };
util::parse_ret(ret)
}
pub fn insn_cnt(&self) -> usize {
unsafe { libbpf_sys::bpf_program__insn_cnt(self.ptr.as_ptr()) as usize }
}
pub fn insns(&self) -> &[libbpf_sys::bpf_insn] {
let count = self.insn_cnt();
let ptr = unsafe { libbpf_sys::bpf_program__insns(self.ptr.as_ptr()) };
unsafe { slice::from_raw_parts(ptr, count) }
}
}
impl AsRawLibbpf for OpenProgram {
type LibbpfType = libbpf_sys::bpf_program;
fn as_libbpf_object(&self) -> NonNull<Self::LibbpfType> {
self.ptr
}
}
#[non_exhaustive]
#[repr(u32)]
#[derive(Copy, Clone, Display, Debug)]
#[allow(missing_docs)]
pub enum ProgramType {
Unspec = 0,
SocketFilter,
Kprobe,
SchedCls,
SchedAct,
Tracepoint,
Xdp,
PerfEvent,
CgroupSkb,
CgroupSock,
LwtIn,
LwtOut,
LwtXmit,
SockOps,
SkSkb,
CgroupDevice,
SkMsg,
RawTracepoint,
CgroupSockAddr,
LwtSeg6local,
LircMode2,
SkReuseport,
FlowDissector,
CgroupSysctl,
RawTracepointWritable,
CgroupSockopt,
Tracing,
StructOps,
Ext,
Lsm,
SkLookup,
Syscall,
Unknown = u32::MAX,
}
impl ProgramType {
pub fn is_supported(&self) -> Result<bool> {
let ret = unsafe { libbpf_sys::libbpf_probe_bpf_prog_type(*self as u32, ptr::null()) };
match ret {
0 => Ok(false),
1 => Ok(true),
_ => Err(Error::from_raw_os_error(-ret)),
}
}
pub fn is_helper_supported(&self, helper_id: bpf_func_id) -> Result<bool> {
let ret =
unsafe { libbpf_sys::libbpf_probe_bpf_helper(*self as u32, helper_id, ptr::null()) };
match ret {
0 => Ok(false),
1 => Ok(true),
_ => Err(Error::from_raw_os_error(-ret)),
}
}
}
impl From<u32> for ProgramType {
fn from(value: u32) -> Self {
use ProgramType::*;
match value {
x if x == Unspec as u32 => Unspec,
x if x == SocketFilter as u32 => SocketFilter,
x if x == Kprobe as u32 => Kprobe,
x if x == SchedCls as u32 => SchedCls,
x if x == SchedAct as u32 => SchedAct,
x if x == Tracepoint as u32 => Tracepoint,
x if x == Xdp as u32 => Xdp,
x if x == PerfEvent as u32 => PerfEvent,
x if x == CgroupSkb as u32 => CgroupSkb,
x if x == CgroupSock as u32 => CgroupSock,
x if x == LwtIn as u32 => LwtIn,
x if x == LwtOut as u32 => LwtOut,
x if x == LwtXmit as u32 => LwtXmit,
x if x == SockOps as u32 => SockOps,
x if x == SkSkb as u32 => SkSkb,
x if x == CgroupDevice as u32 => CgroupDevice,
x if x == SkMsg as u32 => SkMsg,
x if x == RawTracepoint as u32 => RawTracepoint,
x if x == CgroupSockAddr as u32 => CgroupSockAddr,
x if x == LwtSeg6local as u32 => LwtSeg6local,
x if x == LircMode2 as u32 => LircMode2,
x if x == SkReuseport as u32 => SkReuseport,
x if x == FlowDissector as u32 => FlowDissector,
x if x == CgroupSysctl as u32 => CgroupSysctl,
x if x == RawTracepointWritable as u32 => RawTracepointWritable,
x if x == CgroupSockopt as u32 => CgroupSockopt,
x if x == Tracing as u32 => Tracing,
x if x == StructOps as u32 => StructOps,
x if x == Ext as u32 => Ext,
x if x == Lsm as u32 => Lsm,
x if x == SkLookup as u32 => SkLookup,
x if x == Syscall as u32 => Syscall,
_ => Unknown,
}
}
}
#[non_exhaustive]
#[repr(u32)]
#[derive(Clone, Display, Debug)]
#[allow(missing_docs)]
pub enum ProgramAttachType {
CgroupInetIngress,
CgroupInetEgress,
CgroupInetSockCreate,
CgroupSockOps,
SkSkbStreamParser,
SkSkbStreamVerdict,
CgroupDevice,
SkMsgVerdict,
CgroupInet4Bind,
CgroupInet6Bind,
CgroupInet4Connect,
CgroupInet6Connect,
CgroupInet4PostBind,
CgroupInet6PostBind,
CgroupUdp4Sendmsg,
CgroupUdp6Sendmsg,
LircMode2,
FlowDissector,
CgroupSysctl,
CgroupUdp4Recvmsg,
CgroupUdp6Recvmsg,
CgroupGetsockopt,
CgroupSetsockopt,
TraceRawTp,
TraceFentry,
TraceFexit,
ModifyReturn,
LsmMac,
TraceIter,
CgroupInet4Getpeername,
CgroupInet6Getpeername,
CgroupInet4Getsockname,
CgroupInet6Getsockname,
XdpDevmap,
CgroupInetSockRelease,
XdpCpumap,
SkLookup,
Xdp,
SkSkbVerdict,
SkReuseportSelect,
SkReuseportSelectOrMigrate,
PerfEvent,
Unknown = u32::MAX,
}
impl From<u32> for ProgramAttachType {
fn from(value: u32) -> Self {
use ProgramAttachType::*;
match value {
x if x == CgroupInetIngress as u32 => CgroupInetIngress,
x if x == CgroupInetEgress as u32 => CgroupInetEgress,
x if x == CgroupInetSockCreate as u32 => CgroupInetSockCreate,
x if x == CgroupSockOps as u32 => CgroupSockOps,
x if x == SkSkbStreamParser as u32 => SkSkbStreamParser,
x if x == SkSkbStreamVerdict as u32 => SkSkbStreamVerdict,
x if x == CgroupDevice as u32 => CgroupDevice,
x if x == SkMsgVerdict as u32 => SkMsgVerdict,
x if x == CgroupInet4Bind as u32 => CgroupInet4Bind,
x if x == CgroupInet6Bind as u32 => CgroupInet6Bind,
x if x == CgroupInet4Connect as u32 => CgroupInet4Connect,
x if x == CgroupInet6Connect as u32 => CgroupInet6Connect,
x if x == CgroupInet4PostBind as u32 => CgroupInet4PostBind,
x if x == CgroupInet6PostBind as u32 => CgroupInet6PostBind,
x if x == CgroupUdp4Sendmsg as u32 => CgroupUdp4Sendmsg,
x if x == CgroupUdp6Sendmsg as u32 => CgroupUdp6Sendmsg,
x if x == LircMode2 as u32 => LircMode2,
x if x == FlowDissector as u32 => FlowDissector,
x if x == CgroupSysctl as u32 => CgroupSysctl,
x if x == CgroupUdp4Recvmsg as u32 => CgroupUdp4Recvmsg,
x if x == CgroupUdp6Recvmsg as u32 => CgroupUdp6Recvmsg,
x if x == CgroupGetsockopt as u32 => CgroupGetsockopt,
x if x == CgroupSetsockopt as u32 => CgroupSetsockopt,
x if x == TraceRawTp as u32 => TraceRawTp,
x if x == TraceFentry as u32 => TraceFentry,
x if x == TraceFexit as u32 => TraceFexit,
x if x == ModifyReturn as u32 => ModifyReturn,
x if x == LsmMac as u32 => LsmMac,
x if x == TraceIter as u32 => TraceIter,
x if x == CgroupInet4Getpeername as u32 => CgroupInet4Getpeername,
x if x == CgroupInet6Getpeername as u32 => CgroupInet6Getpeername,
x if x == CgroupInet4Getsockname as u32 => CgroupInet4Getsockname,
x if x == CgroupInet6Getsockname as u32 => CgroupInet6Getsockname,
x if x == XdpDevmap as u32 => XdpDevmap,
x if x == CgroupInetSockRelease as u32 => CgroupInetSockRelease,
x if x == XdpCpumap as u32 => XdpCpumap,
x if x == SkLookup as u32 => SkLookup,
x if x == Xdp as u32 => Xdp,
x if x == SkSkbVerdict as u32 => SkSkbVerdict,
x if x == SkReuseportSelect as u32 => SkReuseportSelect,
x if x == SkReuseportSelectOrMigrate as u32 => SkReuseportSelectOrMigrate,
x if x == PerfEvent as u32 => PerfEvent,
_ => Unknown,
}
}
}
#[derive(Debug, Default)]
pub struct Input<'dat> {
pub context_in: Option<&'dat [u8]>,
pub context_out: Option<&'dat mut [u8]>,
pub data_in: Option<&'dat [u8]>,
pub data_out: Option<&'dat mut [u8]>,
pub cpu: u32,
pub flags: u32,
#[doc(hidden)]
pub _non_exhaustive: (),
}
#[derive(Debug)]
pub struct Output<'dat> {
pub return_value: u32,
pub context: Option<&'dat mut [u8]>,
pub data: Option<&'dat mut [u8]>,
#[doc(hidden)]
pub _non_exhaustive: (),
}
#[derive(Debug)]
pub struct Program {
pub(crate) ptr: NonNull<libbpf_sys::bpf_program>,
name: String,
section: String,
}
impl AsFd for Program {
fn as_fd(&self) -> BorrowedFd<'_> {
let fd = unsafe { libbpf_sys::bpf_program__fd(self.ptr.as_ptr()) };
unsafe { BorrowedFd::borrow_raw(fd) }
}
}
impl Program {
pub(crate) unsafe fn new(ptr: NonNull<libbpf_sys::bpf_program>) -> Result<Self> {
let name = unsafe { libbpf_sys::bpf_program__name(ptr.as_ptr()) };
let name = util::c_ptr_to_string(name)?;
let section = unsafe { libbpf_sys::bpf_program__section_name(ptr.as_ptr()) };
let section = util::c_ptr_to_string(section)?;
Ok(Program { ptr, name, section })
}
pub fn name(&self) -> &str {
&self.name
}
pub fn section(&self) -> &str {
&self.section
}
pub fn prog_type(&self) -> ProgramType {
ProgramType::from(unsafe { libbpf_sys::bpf_program__type(self.ptr.as_ptr()) })
}
pub fn get_fd_by_id(id: u32) -> Result<OwnedFd> {
let ret = unsafe { libbpf_sys::bpf_prog_get_fd_by_id(id) };
let fd = util::parse_ret_i32(ret)?;
Ok(unsafe { OwnedFd::from_raw_fd(fd) })
}
pub fn get_id_by_fd(fd: BorrowedFd<'_>) -> Result<u32> {
let mut prog_info = libbpf_sys::bpf_prog_info::default();
let prog_info_ptr: *mut libbpf_sys::bpf_prog_info = &mut prog_info;
let mut len = size_of::<libbpf_sys::bpf_prog_info>() as u32;
let ret = unsafe {
libbpf_sys::bpf_obj_get_info_by_fd(
fd.as_raw_fd(),
prog_info_ptr as *mut c_void,
&mut len,
)
};
util::parse_ret(ret)?;
Ok(prog_info.id)
}
pub fn flags(&self) -> u32 {
unsafe { libbpf_sys::bpf_program__flags(self.ptr.as_ptr()) }
}
pub fn attach_type(&self) -> ProgramAttachType {
ProgramAttachType::from(unsafe {
libbpf_sys::bpf_program__expected_attach_type(self.ptr.as_ptr())
})
}
pub fn autoload(&self) -> bool {
unsafe { libbpf_sys::bpf_program__autoload(self.ptr.as_ptr()) }
}
pub fn log_level(&self) -> u32 {
unsafe { libbpf_sys::bpf_program__log_level(self.ptr.as_ptr()) }
}
pub fn pin<P: AsRef<Path>>(&mut self, path: P) -> Result<()> {
let path_c = util::path_to_cstring(path)?;
let path_ptr = path_c.as_ptr();
let ret = unsafe { libbpf_sys::bpf_program__pin(self.ptr.as_ptr(), path_ptr) };
util::parse_ret(ret)
}
pub fn unpin<P: AsRef<Path>>(&mut self, path: P) -> Result<()> {
let path_c = util::path_to_cstring(path)?;
let path_ptr = path_c.as_ptr();
let ret = unsafe { libbpf_sys::bpf_program__unpin(self.ptr.as_ptr(), path_ptr) };
util::parse_ret(ret)
}
pub fn attach(&mut self) -> Result<Link> {
util::create_bpf_entity_checked(|| unsafe {
libbpf_sys::bpf_program__attach(self.ptr.as_ptr())
})
.map(|ptr| unsafe {
Link::new(ptr)
})
}
pub fn attach_cgroup(&mut self, cgroup_fd: i32) -> Result<Link> {
util::create_bpf_entity_checked(|| unsafe {
libbpf_sys::bpf_program__attach_cgroup(self.ptr.as_ptr(), cgroup_fd)
})
.map(|ptr| unsafe {
Link::new(ptr)
})
}
pub fn attach_perf_event(&mut self, pfd: i32) -> Result<Link> {
util::create_bpf_entity_checked(|| unsafe {
libbpf_sys::bpf_program__attach_perf_event(self.ptr.as_ptr(), pfd)
})
.map(|ptr| unsafe {
Link::new(ptr)
})
}
pub fn attach_uprobe<T: AsRef<Path>>(
&mut self,
retprobe: bool,
pid: i32,
binary_path: T,
func_offset: usize,
) -> Result<Link> {
let path = util::path_to_cstring(binary_path)?;
let path_ptr = path.as_ptr();
util::create_bpf_entity_checked(|| unsafe {
libbpf_sys::bpf_program__attach_uprobe(
self.ptr.as_ptr(),
retprobe,
pid,
path_ptr,
func_offset as libbpf_sys::size_t,
)
})
.map(|ptr| unsafe {
Link::new(ptr)
})
}
pub fn attach_uprobe_with_opts(
&mut self,
pid: i32,
binary_path: impl AsRef<Path>,
func_offset: usize,
opts: UprobeOpts,
) -> Result<Link> {
let path = util::path_to_cstring(binary_path)?;
let path_ptr = path.as_ptr();
let UprobeOpts {
ref_ctr_offset,
cookie,
retprobe,
func_name,
_non_exhaustive,
} = opts;
let func_name = util::str_to_cstring(&func_name)?;
let opts = libbpf_sys::bpf_uprobe_opts {
sz: size_of::<libbpf_sys::bpf_uprobe_opts>() as _,
ref_ctr_offset: ref_ctr_offset as libbpf_sys::size_t,
bpf_cookie: cookie,
retprobe,
func_name: func_name.as_ptr(),
..Default::default()
};
util::create_bpf_entity_checked(|| unsafe {
libbpf_sys::bpf_program__attach_uprobe_opts(
self.ptr.as_ptr(),
pid,
path_ptr,
func_offset as libbpf_sys::size_t,
&opts as *const _,
)
})
.map(|ptr| unsafe {
Link::new(ptr)
})
}
pub fn attach_kprobe<T: AsRef<str>>(&mut self, retprobe: bool, func_name: T) -> Result<Link> {
let func_name = util::str_to_cstring(func_name.as_ref())?;
let func_name_ptr = func_name.as_ptr();
util::create_bpf_entity_checked(|| unsafe {
libbpf_sys::bpf_program__attach_kprobe(self.ptr.as_ptr(), retprobe, func_name_ptr)
})
.map(|ptr| unsafe {
Link::new(ptr)
})
}
pub fn attach_ksyscall<T: AsRef<str>>(
&mut self,
retprobe: bool,
syscall_name: T,
) -> Result<Link> {
let opts = libbpf_sys::bpf_ksyscall_opts {
sz: size_of::<libbpf_sys::bpf_ksyscall_opts>() as _,
retprobe,
..Default::default()
};
let syscall_name = util::str_to_cstring(syscall_name.as_ref())?;
let syscall_name_ptr = syscall_name.as_ptr();
util::create_bpf_entity_checked(|| unsafe {
libbpf_sys::bpf_program__attach_ksyscall(self.ptr.as_ptr(), syscall_name_ptr, &opts)
})
.map(|ptr| unsafe {
Link::new(ptr)
})
}
fn attach_tracepoint_impl(
&mut self,
tp_category: &str,
tp_name: &str,
tp_opts: Option<TracepointOpts>,
) -> Result<Link> {
let tp_category = util::str_to_cstring(tp_category)?;
let tp_category_ptr = tp_category.as_ptr();
let tp_name = util::str_to_cstring(tp_name)?;
let tp_name_ptr = tp_name.as_ptr();
util::create_bpf_entity_checked(|| {
if let Some(tp_opts) = tp_opts {
let tp_opts = libbpf_sys::bpf_tracepoint_opts::from(tp_opts);
unsafe {
libbpf_sys::bpf_program__attach_tracepoint_opts(
self.ptr.as_ptr(),
tp_category_ptr,
tp_name_ptr,
&tp_opts as *const _,
)
}
} else {
unsafe {
libbpf_sys::bpf_program__attach_tracepoint(
self.ptr.as_ptr(),
tp_category_ptr,
tp_name_ptr,
)
}
}
})
.map(|ptr| unsafe {
Link::new(ptr)
})
}
pub fn attach_tracepoint(
&mut self,
tp_category: impl AsRef<str>,
tp_name: impl AsRef<str>,
) -> Result<Link> {
self.attach_tracepoint_impl(tp_category.as_ref(), tp_name.as_ref(), None)
}
pub fn attach_tracepoint_with_opts(
&mut self,
tp_category: impl AsRef<str>,
tp_name: impl AsRef<str>,
tp_opts: TracepointOpts,
) -> Result<Link> {
self.attach_tracepoint_impl(tp_category.as_ref(), tp_name.as_ref(), Some(tp_opts))
}
pub fn attach_raw_tracepoint<T: AsRef<str>>(&mut self, tp_name: T) -> Result<Link> {
let tp_name = util::str_to_cstring(tp_name.as_ref())?;
let tp_name_ptr = tp_name.as_ptr();
util::create_bpf_entity_checked(|| unsafe {
libbpf_sys::bpf_program__attach_raw_tracepoint(self.ptr.as_ptr(), tp_name_ptr)
})
.map(|ptr| unsafe {
Link::new(ptr)
})
}
pub fn attach_lsm(&mut self) -> Result<Link> {
util::create_bpf_entity_checked(|| unsafe {
libbpf_sys::bpf_program__attach_lsm(self.ptr.as_ptr())
})
.map(|ptr| unsafe {
Link::new(ptr)
})
}
pub fn attach_trace(&mut self) -> Result<Link> {
util::create_bpf_entity_checked(|| unsafe {
libbpf_sys::bpf_program__attach_trace(self.ptr.as_ptr())
})
.map(|ptr| unsafe {
Link::new(ptr)
})
}
pub fn attach_sockmap(&self, map_fd: i32) -> Result<()> {
let err = unsafe {
libbpf_sys::bpf_prog_attach(
self.as_fd().as_raw_fd(),
map_fd,
self.attach_type() as u32,
0,
)
};
util::parse_ret(err)
}
pub fn attach_xdp(&mut self, ifindex: i32) -> Result<Link> {
util::create_bpf_entity_checked(|| unsafe {
libbpf_sys::bpf_program__attach_xdp(self.ptr.as_ptr(), ifindex)
})
.map(|ptr| unsafe {
Link::new(ptr)
})
}
pub fn attach_netns(&mut self, netns_fd: i32) -> Result<Link> {
util::create_bpf_entity_checked(|| unsafe {
libbpf_sys::bpf_program__attach_netns(self.ptr.as_ptr(), netns_fd)
})
.map(|ptr| unsafe {
Link::new(ptr)
})
}
fn attach_usdt_impl(
&mut self,
pid: i32,
binary_path: &Path,
usdt_provider: &str,
usdt_name: &str,
usdt_opts: Option<UsdtOpts>,
) -> Result<Link> {
let path = util::path_to_cstring(binary_path)?;
let path_ptr = path.as_ptr();
let usdt_provider = util::str_to_cstring(usdt_provider)?;
let usdt_provider_ptr = usdt_provider.as_ptr();
let usdt_name = util::str_to_cstring(usdt_name)?;
let usdt_name_ptr = usdt_name.as_ptr();
let usdt_opts = usdt_opts.map(libbpf_sys::bpf_usdt_opts::from);
let usdt_opts_ptr = usdt_opts
.as_ref()
.map(|opts| opts as *const _)
.unwrap_or_else(ptr::null);
util::create_bpf_entity_checked(|| unsafe {
libbpf_sys::bpf_program__attach_usdt(
self.ptr.as_ptr(),
pid,
path_ptr,
usdt_provider_ptr,
usdt_name_ptr,
usdt_opts_ptr,
)
})
.map(|ptr| unsafe {
Link::new(ptr)
})
}
pub fn attach_usdt(
&mut self,
pid: i32,
binary_path: impl AsRef<Path>,
usdt_provider: impl AsRef<str>,
usdt_name: impl AsRef<str>,
) -> Result<Link> {
self.attach_usdt_impl(
pid,
binary_path.as_ref(),
usdt_provider.as_ref(),
usdt_name.as_ref(),
None,
)
}
pub fn attach_usdt_with_opts(
&mut self,
pid: i32,
binary_path: impl AsRef<Path>,
usdt_provider: impl AsRef<str>,
usdt_name: impl AsRef<str>,
usdt_opts: UsdtOpts,
) -> Result<Link> {
self.attach_usdt_impl(
pid,
binary_path.as_ref(),
usdt_provider.as_ref(),
usdt_name.as_ref(),
Some(usdt_opts),
)
}
pub fn attach_iter(&mut self, map_fd: BorrowedFd<'_>) -> Result<Link> {
util::create_bpf_entity_checked(|| unsafe {
let mut linkinfo = libbpf_sys::bpf_iter_link_info::default();
linkinfo.map.map_fd = map_fd.as_raw_fd() as _;
let attach_opt = libbpf_sys::bpf_iter_attach_opts {
link_info: &mut linkinfo as *mut libbpf_sys::bpf_iter_link_info,
link_info_len: size_of::<libbpf_sys::bpf_iter_link_info>() as _,
sz: size_of::<libbpf_sys::bpf_iter_attach_opts>() as _,
..Default::default()
};
libbpf_sys::bpf_program__attach_iter(
self.ptr.as_ptr(),
&attach_opt as *const libbpf_sys::bpf_iter_attach_opts,
)
})
.map(|ptr| unsafe {
Link::new(ptr)
})
}
pub fn test_run<'dat>(&mut self, input: Input<'dat>) -> Result<Output<'dat>> {
pub(crate) unsafe fn slice_from_array<'t, T>(
items: *mut T,
num_items: usize,
) -> Option<&'t mut [T]> {
if items.is_null() {
None
} else {
Some(unsafe { slice::from_raw_parts_mut(items, num_items) })
}
}
let Input {
context_in,
mut context_out,
data_in,
mut data_out,
cpu,
flags,
_non_exhaustive: (),
} = input;
let mut opts = unsafe { mem::zeroed::<libbpf_sys::bpf_test_run_opts>() };
opts.sz = size_of_val(&opts) as _;
opts.ctx_in = context_in
.map(|data| data.as_ptr().cast())
.unwrap_or_else(ptr::null);
opts.ctx_size_in = context_in.map(|data| data.len() as _).unwrap_or(0);
opts.ctx_out = context_out
.as_mut()
.map(|data| data.as_mut_ptr().cast())
.unwrap_or_else(ptr::null_mut);
opts.ctx_size_out = context_out.map(|data| data.len() as _).unwrap_or(0);
opts.data_in = data_in
.map(|data| data.as_ptr().cast())
.unwrap_or_else(ptr::null);
opts.data_size_in = data_in.map(|data| data.len() as _).unwrap_or(0);
opts.data_out = data_out
.as_mut()
.map(|data| data.as_mut_ptr().cast())
.unwrap_or_else(ptr::null_mut);
opts.data_size_out = data_out.map(|data| data.len() as _).unwrap_or(0);
opts.cpu = cpu;
opts.flags = flags;
let rc = unsafe { libbpf_sys::bpf_prog_test_run_opts(self.as_fd().as_raw_fd(), &mut opts) };
let () = util::parse_ret(rc)?;
let output = Output {
return_value: opts.retval,
context: unsafe { slice_from_array(opts.ctx_out.cast(), opts.ctx_size_out as _) },
data: unsafe { slice_from_array(opts.data_out.cast(), opts.data_size_out as _) },
_non_exhaustive: (),
};
Ok(output)
}
pub fn insn_cnt(&self) -> usize {
unsafe { libbpf_sys::bpf_program__insn_cnt(self.ptr.as_ptr()) as usize }
}
pub fn insns(&self) -> &[libbpf_sys::bpf_insn] {
let count = self.insn_cnt();
let ptr = unsafe { libbpf_sys::bpf_program__insns(self.ptr.as_ptr()) };
unsafe { slice::from_raw_parts(ptr, count) }
}
}
impl AsRawLibbpf for Program {
type LibbpfType = libbpf_sys::bpf_program;
fn as_libbpf_object(&self) -> NonNull<Self::LibbpfType> {
self.ptr
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::mem::discriminant;
#[test]
fn program_type() {
use ProgramType::*;
for t in [
Unspec,
SocketFilter,
Kprobe,
SchedCls,
SchedAct,
Tracepoint,
Xdp,
PerfEvent,
CgroupSkb,
CgroupSock,
LwtIn,
LwtOut,
LwtXmit,
SockOps,
SkSkb,
CgroupDevice,
SkMsg,
RawTracepoint,
CgroupSockAddr,
LwtSeg6local,
LircMode2,
SkReuseport,
FlowDissector,
CgroupSysctl,
RawTracepointWritable,
CgroupSockopt,
Tracing,
StructOps,
Ext,
Lsm,
SkLookup,
Syscall,
Unknown,
] {
assert_eq!(discriminant(&t), discriminant(&ProgramType::from(t as u32)));
}
}
#[test]
fn program_attach_type() {
use ProgramAttachType::*;
for t in [
CgroupInetIngress,
CgroupInetEgress,
CgroupInetSockCreate,
CgroupSockOps,
SkSkbStreamParser,
SkSkbStreamVerdict,
CgroupDevice,
SkMsgVerdict,
CgroupInet4Bind,
CgroupInet6Bind,
CgroupInet4Connect,
CgroupInet6Connect,
CgroupInet4PostBind,
CgroupInet6PostBind,
CgroupUdp4Sendmsg,
CgroupUdp6Sendmsg,
LircMode2,
FlowDissector,
CgroupSysctl,
CgroupUdp4Recvmsg,
CgroupUdp6Recvmsg,
CgroupGetsockopt,
CgroupSetsockopt,
TraceRawTp,
TraceFentry,
TraceFexit,
ModifyReturn,
LsmMac,
TraceIter,
CgroupInet4Getpeername,
CgroupInet6Getpeername,
CgroupInet4Getsockname,
CgroupInet6Getsockname,
XdpDevmap,
CgroupInetSockRelease,
XdpCpumap,
SkLookup,
Xdp,
SkSkbVerdict,
SkReuseportSelect,
SkReuseportSelectOrMigrate,
PerfEvent,
Unknown,
] {
assert_eq!(
discriminant(&t),
discriminant(&ProgramAttachType::from(t as u32))
);
}
}
}