diff --git a/aya/src/lib.rs b/aya/src/lib.rs index 8d5be4d9a..3f87e4332 100644 --- a/aya/src/lib.rs +++ b/aya/src/lib.rs @@ -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; diff --git a/aya/src/programs/mod.rs b/aya/src/programs/mod.rs index 2050f8a78..9ee02141a 100644 --- a/aya/src/programs/mod.rs +++ b/aya/src/programs/mod.rs @@ -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, }; @@ -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 { @@ -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; + + fn next(&mut self) -> Option { + 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, + } +} diff --git a/aya/src/sys/bpf.rs b/aya/src/sys/bpf.rs index 366b000fd..101823d70 100644 --- a/aya/src/sys/bpf.rs +++ b/aya/src/sys/bpf.rs @@ -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, (c_long, io::Error)> { + let mut attr = unsafe { mem::zeroed::() }; + 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( max_retries: usize, log: &mut VerifierLog,