warlocks-stave/src/ebpf.rs

67 lines
2.5 KiB
Rust

// ============================================================================
// WARLOCK'S STAVE: Linux eBPF Kernel Tracing Engine
// ============================================================================
//
// Provides kernel-level event tracing via eBPF kprobe programs.
// Requires: aya 0.11.0, clang for eBPF bytecode compilation,
// and elevated permissions (CAP_BPF or root).
#[cfg(target_os = "linux")]
use aya::{Bpf, programs::KProbe, maps::perf::AsyncPerfEventArray};
#[cfg(target_os = "linux")]
use bytes::BytesMut;
/// Kernel-level event structure populated by eBPF probe programs.
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct KernelTraceEvent {
pub pid: u32,
pub vlan_id: u16,
pub network_namespace: u32,
}
pub struct LinuxEbpfEngine;
impl LinuxEbpfEngine {
/// Spawns eBPF kprobe on sys_enter_connect to intercept outbound
/// network connections. Requires a pre-compiled eBPF object at
/// `ebpf_bin/stave_probe.o` (built by `build.sh`).
#[cfg(target_os = "linux")]
pub fn spawn_kernel_hooks() -> Result<(), Box<dyn std::error::Error>> {
let mut bpf = Bpf::load(include_bytes!("../../ebpf_bin/stave_probe.o"))?;
let program: &mut KProbe =
bpf.program_mut("sys_enter_connect").unwrap().try_into()?;
program.load()?;
program.attach("sys_connect", 0)?;
let mut perf_array =
AsyncPerfEventArray::try_from(bpf.map_mut("STAVE_EVENTS").unwrap())?;
std::thread::spawn(move || {
let runtime = tokio::runtime::Runtime::new().unwrap();
runtime.block_on(async {
let mut buffers = vec![BytesMut::with_capacity(1024); 4];
loop {
if let Ok(events) = perf_array.read_events(&mut buffers).await {
for i in 0..events.read {
let event = unsafe {
*(buffers[i].as_ptr() as *const KernelTraceEvent)
};
println!(
"[KERNEL ALERT] Intercepted Outbound Connect! \
PID: {} | NS: {}",
event.pid, event.network_namespace
);
}
}
}
});
});
Ok(())
}
/// No-op on non-Linux platforms.
#[cfg(not(target_os = "linux"))]
pub fn spawn_kernel_hooks() -> Result<(), Box<dyn std::error::Error>> {
Ok(())
}
}