From aa9009cd22fb75412785f6fa585bebb6571456a5 Mon Sep 17 00:00:00 2001 From: Andrew Stoycos Date: Mon, 24 Jul 2023 12:16:23 -0400 Subject: [PATCH] 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. Signed-off-by: Andrew Stoycos --- aya/src/programs/mod.rs | 89 +++++++++++++++++++++++++++++++++------ aya/src/programs/utils.rs | 48 ++++++++++++++++++++- 2 files changed, 123 insertions(+), 14 deletions(-) diff --git a/aya/src/programs/mod.rs b/aya/src/programs/mod.rs index 5e4d1f4a..713cfe96 100644 --- a/aya/src/programs/mod.rs +++ b/aya/src/programs/mod.rs @@ -69,9 +69,11 @@ use libc::ENOSPC; use std::{ ffi::CString, io, - os::fd::{AsFd, AsRawFd, IntoRawFd as _, OwnedFd, RawFd}, + num::NonZeroU32, + os::fd::{AsFd, AsRawFd, OwnedFd, RawFd}, path::{Path, PathBuf}, sync::Arc, + time::{Duration, SystemTime}, }; use thiserror::Error; @@ -109,6 +111,7 @@ use crate::{ maps::MapError, obj::{self, btf::BtfError, Function, VerifierLog}, pin::PinError, + programs::utils::{boot_time, get_fdinfo}, sys::{ bpf_btf_get_fd_by_id, bpf_get_object, bpf_load_program, bpf_pin_object, bpf_prog_get_fd_by_id, bpf_prog_get_info_by_fd, bpf_prog_get_next_id, bpf_prog_query, @@ -938,26 +941,88 @@ impl ProgramInfo { 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> { 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 { self.0.id } - /// Returns the fd associated with the program. - /// - /// The returned fd must be closed when no longer needed. - pub fn fd(&self) -> Result { - let fd = - bpf_prog_get_fd_by_id(self.0.id).map_err(|io_error| ProgramError::SyscallError { - call: "bpf_prog_get_fd_by_id", + /// 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 + } + + /// Returns the ids of the maps maps used by the program. + pub fn map_ids(&self) -> Result, ProgramError> { + let fd = self.fd()?; + let map_ids = vec![0u32; self.0.nr_map_ids as usize]; + + bpf_prog_get_info_by_fd(fd.as_raw_fd(), &map_ids).map_err(|io_error| { + ProgramError::SyscallError { + call: "bpf_prog_get_info_by_fd", io_error, - })?; - Ok(fd.into_raw_fd()) + } + })?; + + Ok(map_ids) + } + + /// The btf id for the program. + pub fn btf_id(&self) -> Option { + NonZeroU32::new(self.0.btf_id) + } + + /// The size in bytes of the program's translated eBPF bytecode. + 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_jited(&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 { + 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 the fd associated with the program. + pub fn fd(&self) -> Result { + bpf_prog_get_fd_by_id(self.0.id).map_err(|io_error| ProgramError::SyscallError { + call: "bpf_prog_get_fd_by_id", + io_error, + }) } /// Loads a program from a pinned path in bpffs. diff --git a/aya/src/programs/utils.rs b/aya/src/programs/utils.rs index 69fc616e..eda41171 100644 --- a/aya/src/programs/utils.rs +++ b/aya/src/programs/utils.rs @@ -1,5 +1,12 @@ //! Common functions shared between multiple eBPF program types. -use std::{ffi::CStr, io, os::fd::RawFd, path::Path}; +use std::{ + ffi::CStr, + fs::File, + io::{self, BufRead, BufReader}, + os::fd::{AsRawFd as _, BorrowedFd, RawFd}, + path::Path, + time::{Duration, SystemTime, UNIX_EPOCH}, +}; use crate::{ programs::{FdLink, Link, ProgramData, ProgramError}, @@ -23,7 +30,7 @@ pub(crate) fn attach_raw_tracepoint>( 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> { lazy_static::lazy_static! { static ref TRACE_FS: Option<&'static Path> = { @@ -51,3 +58,40 @@ pub(crate) fn find_tracefs_path() -> Result<&'static Path, ProgramError> { .as_deref() .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::() }; + 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 { + 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) +}