Skip to content

Commit

Permalink
aya: Add loaded_programs() API to list all loaded programs
Browse files Browse the repository at this point in the history
This uses a Programs iterator to yield all loaded bpf programs using
bpf_prog_get_next_id.

Signed-off-by: Dave Tucker <dave@dtucker.co.uk>
  • Loading branch information
dave-tucker committed Feb 23, 2023
1 parent 56c1438 commit de4905a
Show file tree
Hide file tree
Showing 3 changed files with 93 additions and 2 deletions.
1 change: 1 addition & 0 deletions aya/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ pub mod maps;
use aya_obj as obj;
pub mod pin;
pub mod programs;
pub use programs::loaded_programs;
mod sys;
pub mod util;

Expand Down
83 changes: 81 additions & 2 deletions aya/src/programs/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,8 +109,8 @@ use crate::{
pin::PinError,
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_query, retry_with_verifier_logs,
BpfLoadProgramAttrs,
bpf_prog_get_fd_by_id, bpf_prog_get_info_by_fd, bpf_prog_get_next_id, bpf_prog_query,
retry_with_verifier_logs, BpfLoadProgramAttrs,
},
util::VerifierLog,
};
Expand Down Expand Up @@ -912,6 +912,7 @@ impl_try_from_program!(
);

/// Provides information about a loaded program, like name, id and statistics
#[derive(Debug)]
pub struct ProgramInfo(bpf_prog_info);

impl ProgramInfo {
Expand Down Expand Up @@ -971,3 +972,81 @@ impl ProgramInfo {
Ok(ProgramInfo(info))
}
}

/// ProgramsIter is an Iterator over loaded eBPF programs.
pub struct ProgramsIter {
current: u32,
error: bool,
}

impl Iterator for ProgramsIter {
type Item = Result<ProgramInfo, ProgramError>;

fn next(&mut self) -> Option<Self::Item> {
if self.error {
return None;
}
let current = self.current;

match bpf_prog_get_next_id(current) {
Ok(Some(next)) => {
self.current = next;
Some(
bpf_prog_get_fd_by_id(next)
.map_err(|io_error| ProgramError::SyscallError {
call: "bpf_prog_get_fd_by_id".to_owned(),
io_error,
})
.and_then(|fd| {
bpf_prog_get_info_by_fd(fd)
.map_err(|io_error| ProgramError::SyscallError {
call: "bpf_prog_get_info_by_fd".to_owned(),
io_error,
})
.map(ProgramInfo)
}),
)
}
Ok(None) => None,
Err((_, io_error)) => {
// If getting the next program failed, we have to yield None in our next
// iteration to avoid an infinite loop.
self.error = true;
Some(Err(ProgramError::SyscallError {
call: "bpf_prog_get_fd_by_id".to_owned(),
io_error,
}))
}
}
}
}

/// Returns an iterator over all loaded bpf programs.
///
/// This differs from [`crate::Bpf::programs`] since it will return all programs
/// listed on the host system and not only programs a specific [`crate::Bpf`] instance.
///
/// # Example
/// ```
/// # use aya::programs::loaded_programs;
///
/// for p in loaded_programs() {
/// match p {
/// Ok(program) => println!("{}", String::from_utf8_lossy(program.name())),
/// Err(e) => println!("Error iterating programs: {:?}", e),
/// }
/// }
/// ```
///
/// # Errors
///
/// Returns [`ProgramError::SyscallError`] if any of the syscalls required to either get
/// next program id, get the program fd, or the [`ProgramInfo`] fail. In cases where
/// iteration can't be performed, for example the caller does not have the necessary privileges,
/// a single item will be yielded containing the error that occurred.
pub fn loaded_programs() -> ProgramsIter {
ProgramsIter {
current: 0,
error: false,
}
}
11 changes: 11 additions & 0 deletions aya/src/sys/bpf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -859,6 +859,17 @@ pub fn sys_bpf(cmd: bpf_cmd, attr: &bpf_attr) -> SysResult {
syscall(Syscall::Bpf { cmd, attr })
}

pub(crate) fn bpf_prog_get_next_id(id: u32) -> Result<Option<u32>, (c_long, io::Error)> {
let mut attr = unsafe { mem::zeroed::<bpf_attr>() };
let u = unsafe { &mut attr.__bindgen_anon_6 };
u.__bindgen_anon_1.start_id = id;
match sys_bpf(bpf_cmd::BPF_PROG_GET_NEXT_ID, &attr) {
Ok(_) => Ok(Some(unsafe { attr.__bindgen_anon_6.next_id })),
Err((_, io_error)) if io_error.raw_os_error() == Some(ENOENT) => Ok(None),
Err(e) => Err(e),
}
}

pub(crate) fn retry_with_verifier_logs<F>(
max_retries: usize,
log: &mut VerifierLog,
Expand Down

0 comments on commit de4905a

Please sign in to comment.