use std::mem;
use aa_ebpf_common::{
exec::{ExecEvent, ProcessExitEvent},
file::FileIoEventRaw,
tls::TlsCaptureEvent,
};
use aya::{
maps::{MapData, RingBuf},
Ebpf,
};
use tokio::io::unix::AsyncFd;
use crate::error::EbpfError;
#[derive(Debug)]
pub enum EbpfEvent {
Tls(Box<TlsCaptureEvent>),
File(Box<FileIoEventRaw>),
Exec(Box<ExecEvent>),
Exit(Box<ProcessExitEvent>),
}
pub struct RingBufReader {
_bpf: Ebpf,
async_fd: AsyncFd<RingBuf<MapData>>,
}
impl RingBufReader {
pub fn new(mut bpf: Ebpf) -> Result<Self, EbpfError> {
let map = bpf
.take_map("EVENTS")
.ok_or_else(|| EbpfError::MapNotFound { name: "EVENTS".into() })?;
let ring_buf = RingBuf::try_from(map)?;
let async_fd = AsyncFd::new(ring_buf)?;
Ok(Self { _bpf: bpf, async_fd })
}
pub async fn next(&mut self) -> Result<Option<EbpfEvent>, EbpfError> {
loop {
let mut guard = self.async_fd.readable_mut().await?;
let rb = guard.get_inner_mut();
let raw: Option<Vec<u8>> = rb.next().map(|item| item.to_vec());
guard.clear_ready();
if let Some(bytes) = raw {
return Ok(Some(parse_event(&bytes)?));
}
}
}
}
fn parse_event(bytes: &[u8]) -> Result<EbpfEvent, EbpfError> {
match bytes.len() {
n if n == mem::size_of::<TlsCaptureEvent>() => Ok(EbpfEvent::Tls(Box::new(bytes_to::<TlsCaptureEvent>(bytes)))),
n if n == mem::size_of::<FileIoEventRaw>() => Ok(EbpfEvent::File(Box::new(bytes_to::<FileIoEventRaw>(bytes)))),
n if n == mem::size_of::<ExecEvent>() => Ok(EbpfEvent::Exec(Box::new(bytes_to::<ExecEvent>(bytes)))),
n if n == mem::size_of::<ProcessExitEvent>() => {
Ok(EbpfEvent::Exit(Box::new(bytes_to::<ProcessExitEvent>(bytes))))
}
got => Err(EbpfError::EventSize {
expected: mem::size_of::<TlsCaptureEvent>(),
got,
}),
}
}
fn bytes_to<T: Copy>(bytes: &[u8]) -> T {
assert_eq!(bytes.len(), mem::size_of::<T>());
let mut value = mem::MaybeUninit::<T>::uninit();
unsafe {
std::ptr::copy_nonoverlapping(bytes.as_ptr(), value.as_mut_ptr().cast::<u8>(), bytes.len());
value.assume_init()
}
}