add methods to help the loaded_programs() API

Add helper methods to get useful information from the ProgramInfo
object which is returned by the `loaded_programs()` API.  Specifically
this code mirrors the bpftool program in terms of useful fields.

The methods here are not exhaustive and may expand in the future.

Add a new API macro to each aya `Program` type to allow us to fetch it's
accompanying `ProgramInfo` metadata after it's been loaded.

Add a new ProgramInfo constructor that builds a new instance using
a raw fd.

Signed-off-by: Andrew Stoycos <astoycos@redhat.com>
reviewable/pr637/r53
Andrew Stoycos 2 years ago
parent 7f98e419e6
commit d1f7228225
No known key found for this signature in database
GPG Key ID: 66735B92BB71C096

@ -4,10 +4,7 @@ use std::os::fd::{AsRawFd, IntoRawFd as _, RawFd};
use crate::{ use crate::{
generated::{bpf_attach_type::BPF_LIRC_MODE2, bpf_prog_type::BPF_PROG_TYPE_LIRC_MODE2}, generated::{bpf_attach_type::BPF_LIRC_MODE2, bpf_prog_type::BPF_PROG_TYPE_LIRC_MODE2},
programs::{load_program, query, Link, ProgramData, ProgramError, ProgramInfo}, programs::{load_program, query, Link, ProgramData, ProgramError, ProgramInfo},
sys::{ sys::{bpf_prog_attach, bpf_prog_detach, bpf_prog_get_fd_by_id, SyscallError},
bpf_prog_attach, bpf_prog_detach, bpf_prog_get_fd_by_id, bpf_prog_get_info_by_fd,
SyscallError,
},
}; };
use libc::{close, dup}; use libc::{close, dup};
@ -131,9 +128,7 @@ impl LircLink {
/// Get ProgramInfo from this link /// Get ProgramInfo from this link
pub fn info(&self) -> Result<ProgramInfo, ProgramError> { pub fn info(&self) -> Result<ProgramInfo, ProgramError> {
bpf_prog_get_info_by_fd(self.prog_fd, &mut []) ProgramInfo::new_from_fd(self.prog_fd)
.map(ProgramInfo)
.map_err(Into::into)
} }
} }

@ -68,9 +68,11 @@ use libc::ENOSPC;
use std::{ use std::{
ffi::CString, ffi::CString,
io, io,
num::NonZeroU32,
os::fd::{AsFd, AsRawFd, IntoRawFd as _, OwnedFd, RawFd}, os::fd::{AsFd, AsRawFd, IntoRawFd as _, OwnedFd, RawFd},
path::{Path, PathBuf}, path::{Path, PathBuf},
sync::Arc, sync::Arc,
time::{Duration, SystemTime},
}; };
use thiserror::Error; use thiserror::Error;
@ -108,6 +110,7 @@ use crate::{
maps::MapError, maps::MapError,
obj::{self, btf::BtfError, Function, VerifierLog}, obj::{self, btf::BtfError, Function, VerifierLog},
pin::PinError, pin::PinError,
programs::utils::{boot_time, get_fdinfo},
sys::{ sys::{
bpf_btf_get_fd_by_id, bpf_get_object, bpf_link_get_fd_by_id, bpf_link_get_info_by_fd, bpf_btf_get_fd_by_id, bpf_get_object, bpf_link_get_fd_by_id, bpf_link_get_info_by_fd,
bpf_load_program, bpf_pin_object, bpf_prog_get_fd_by_id, bpf_prog_get_info_by_fd, bpf_load_program, bpf_pin_object, bpf_prog_get_fd_by_id, bpf_prog_get_info_by_fd,
@ -476,14 +479,15 @@ impl<T: Link> ProgramData<T> {
) -> Result<ProgramData<T>, ProgramError> { ) -> Result<ProgramData<T>, ProgramError> {
let path_string = let path_string =
CString::new(path.as_ref().as_os_str().to_string_lossy().as_bytes()).unwrap(); CString::new(path.as_ref().as_os_str().to_string_lossy().as_bytes()).unwrap();
let fd = bpf_get_object(&path_string).map_err(|(_, io_error)| SyscallError { let fd = bpf_get_object(&path_string).map_err(|(_, io_error)| SyscallError {
call: "bpf_obj_get", call: "bpf_obj_get",
io_error, io_error,
})?; })?;
let info = bpf_prog_get_info_by_fd(fd.as_raw_fd(), &mut [])?; let info = ProgramInfo::new_from_fd(fd.as_raw_fd())?;
let name = ProgramInfo(info).name_as_str().map(|s| s.to_string()); let name = info.name_as_str().map(|s| s.to_string());
ProgramData::from_bpf_prog_info(name, fd, path.as_ref(), info, verifier_log_level) ProgramData::from_bpf_prog_info(name, fd, path.as_ref(), info.0, verifier_log_level)
} }
} }
@ -901,11 +905,65 @@ impl_try_from_program!(
CgroupDevice, CgroupDevice,
); );
/// Returns information about a loaded program with the [`ProgramInfo`] structure.
///
/// This information is populated at load time by the kernel and can be used
/// to correlate a given [`Program`] to it's corresponding [`ProgramInfo`]
/// metadata.
macro_rules! impl_program_info {
($($struct_name:ident),+ $(,)?) => {
$(
impl $struct_name {
/// Returns the file descriptor of this Program.
pub fn program_info(&self) -> Result<ProgramInfo, ProgramError> {
let fd = self.fd().ok_or(ProgramError::NotLoaded)?;
ProgramInfo::new_from_fd(fd.as_raw_fd())
}
}
)+
}
}
impl_program_info!(
KProbe,
UProbe,
TracePoint,
SocketFilter,
Xdp,
SkMsg,
SkSkb,
SchedClassifier,
CgroupSkb,
CgroupSysctl,
CgroupSockopt,
LircMode2,
PerfEvent,
Lsm,
RawTracePoint,
BtfTracePoint,
FEntry,
FExit,
Extension,
CgroupSockAddr,
SkLookup,
SockOps,
CgroupSock,
CgroupDevice,
);
/// Provides information about a loaded program, like name, id and statistics /// Provides information about a loaded program, like name, id and statistics
#[derive(Debug)] #[derive(Debug)]
pub struct ProgramInfo(bpf_prog_info); pub struct ProgramInfo(bpf_prog_info);
impl ProgramInfo { impl ProgramInfo {
fn new_from_fd(fd: RawFd) -> Result<Self, ProgramError> {
Ok(ProgramInfo(bpf_prog_get_info_by_fd(
fd.as_raw_fd(),
&mut [],
)?))
}
/// The name of the program as was provided when it was load. This is limited to 16 bytes /// The name of the program as was provided when it was load. This is limited to 16 bytes
pub fn name(&self) -> &[u8] { pub fn name(&self) -> &[u8] {
let length = self let length = self
@ -921,23 +979,88 @@ impl ProgramInfo {
unsafe { std::slice::from_raw_parts(self.0.name.as_ptr() as *const _, length) } unsafe { std::slice::from_raw_parts(self.0.name.as_ptr() as *const _, length) }
} }
/// The name of the program as a &str. If the name was not valid unicode, None is returned /// The name of the program as a &str. If the name was not valid unicode, None is returned.
pub fn name_as_str(&self) -> Option<&str> { pub fn name_as_str(&self) -> Option<&str> {
std::str::from_utf8(self.name()).ok() std::str::from_utf8(self.name()).ok()
} }
/// The program id for this program. Each program has a unique id. /// The id for this program. Each program has a unique id.
pub fn id(&self) -> u32 { pub fn id(&self) -> u32 {
self.0.id self.0.id
} }
/// Returns the fd associated with the program. /// The program tag.
///
/// The program tag is a SHA sum of the program's instructions which be used as an alternative to
/// [`Self::id()`]". A program's id can vary every time it's loaded or unloaded, but the tag
/// will remain the same.
pub fn tag(&self) -> u64 {
u64::from_be_bytes(self.0.tag)
}
/// The program type as defined by the linux kernel enum
/// [`bpf_prog_type`](https://elixir.bootlin.com/linux/v6.4.4/source/include/uapi/linux/bpf.h#L948).
pub fn program_type(&self) -> u32 {
self.0.type_
}
/// Returns true if the program is defined with a GPL-compatible license.
pub fn gpl_compatible(&self) -> bool {
self.0.gpl_compatible() != 0
}
/// The ids of the maps used by the program.
pub fn map_ids(&self) -> Result<Vec<u32>, ProgramError> {
let fd = self.fd()?;
let mut map_ids = vec![0u32; self.0.nr_map_ids as usize];
bpf_prog_get_info_by_fd(fd.as_raw_fd(), &mut map_ids)?;
Ok(map_ids)
}
/// The btf id for the program.
pub fn btf_id(&self) -> Option<NonZeroU32> {
NonZeroU32::new(self.0.btf_id)
}
/// The size in bytes of the program's translated eBPF bytecode, which is the bytecode after it has been
/// passed though the verifier where it was possibly modified by the kernel.
pub fn size_translated(&self) -> u32 {
self.0.xlated_prog_len
}
/// The size in bytes of the program's JIT-compiled machine code.
pub fn size_jitted(&self) -> u32 {
self.0.jited_prog_len
}
/// How much memory in bytes has been allocated and locked for the program.
pub fn memory_locked(&self) -> Result<u32, ProgramError> {
get_fdinfo(self.fd()?.as_fd(), "memlock")
}
/// The number of verified instructions in the program.
///
/// This may be less than the total number of instructions in the compiled
/// program due to dead code elimination in the verifier.
pub fn verified_instruction_count(&self) -> u32 {
self.0.verified_insns
}
/// The time the program was loaded.
pub fn loaded_at(&self) -> SystemTime {
boot_time() + Duration::from_nanos(self.0.load_time)
}
/// Returns a file descriptor referencing the program.
/// ///
/// The returned fd must be closed when no longer needed. /// The returned file descriptor can be closed at any time and doing so does not
pub fn fd(&self) -> Result<RawFd, ProgramError> { /// influence the life cycle of the program.
pub fn fd(&self) -> Result<OwnedFd, ProgramError> {
let Self(info) = self; let Self(info) = self;
let fd = bpf_prog_get_fd_by_id(info.id)?; let fd = bpf_prog_get_fd_by_id(info.id)?;
Ok(fd.into_raw_fd()) Ok(fd)
} }
/// Loads a program from a pinned path in bpffs. /// Loads a program from a pinned path in bpffs.

@ -1,5 +1,12 @@
//! Common functions shared between multiple eBPF program types. //! Common functions shared between multiple eBPF program types.
use std::{ffi::CStr, io, path::Path}; use std::{
ffi::CStr,
fs::File,
io::{self, BufRead, BufReader},
os::fd::{AsRawFd as _, BorrowedFd},
path::Path,
time::{Duration, SystemTime, UNIX_EPOCH},
};
use crate::{ use crate::{
programs::{FdLink, Link, ProgramData, ProgramError}, programs::{FdLink, Link, ProgramData, ProgramError},
@ -22,7 +29,7 @@ pub(crate) fn attach_raw_tracepoint<T: Link + From<FdLink>>(
program_data.links.insert(FdLink::new(pfd).into()) program_data.links.insert(FdLink::new(pfd).into())
} }
/// Find tracefs filesystem path /// Find tracefs filesystem path.
pub(crate) fn find_tracefs_path() -> Result<&'static Path, ProgramError> { pub(crate) fn find_tracefs_path() -> Result<&'static Path, ProgramError> {
lazy_static::lazy_static! { lazy_static::lazy_static! {
static ref TRACE_FS: Option<&'static Path> = { static ref TRACE_FS: Option<&'static Path> = {
@ -50,3 +57,40 @@ pub(crate) fn find_tracefs_path() -> Result<&'static Path, ProgramError> {
.as_deref() .as_deref()
.ok_or_else(|| io::Error::new(io::ErrorKind::Other, "tracefs not found").into()) .ok_or_else(|| io::Error::new(io::ErrorKind::Other, "tracefs not found").into())
} }
/// The time at which the system is booted.
pub(crate) fn boot_time() -> SystemTime {
let get_time = |clock_id| {
let mut time = unsafe { std::mem::zeroed::<libc::timespec>() };
assert_eq!(
unsafe { libc::clock_gettime(clock_id, &mut time) },
0,
"clock_gettime({}, _)",
clock_id
);
let libc::timespec { tv_sec, tv_nsec } = time;
Duration::new(tv_sec as u64, tv_nsec as u32)
};
let since_boot = get_time(libc::CLOCK_BOOTTIME);
let since_epoch = get_time(libc::CLOCK_REALTIME);
UNIX_EPOCH + since_boot - since_epoch
}
/// Get the specified information from a file descriptor's fdinfo.
pub(crate) fn get_fdinfo(fd: BorrowedFd, key: &str) -> Result<u32, ProgramError> {
let info = File::open(format!("/proc/self/fdinfo/{}", fd.as_raw_fd()))?;
let reader = BufReader::new(info);
for line in reader.lines() {
let line = line.map_err(ProgramError::IOError)?;
if !line.contains(key) {
continue;
}
let (_key, val) = line.rsplit_once('\t').unwrap();
return Ok(val.parse().unwrap());
}
Ok(0)
}

Loading…
Cancel
Save