use std::{ cell::{Ref, RefCell, RefMut}, collections::HashMap, convert::TryFrom, }; use thiserror::Error; use crate::{ maps::{Map, MapError}, obj::{BtfRelocationError, Object, ParseError, RelocationError}, programs::{KProbe, Program, ProgramData, ProgramError, SocketFilter, TracePoint, UProbe, Xdp}, syscalls::bpf_map_update_elem_ptr, }; pub(crate) const BPF_OBJ_NAME_LEN: usize = 16; /* FIXME: these are arch dependent */ pub(crate) const PERF_EVENT_IOC_ENABLE: libc::c_ulong = 9216; pub(crate) const PERF_EVENT_IOC_DISABLE: libc::c_ulong = 9217; pub(crate) const PERF_EVENT_IOC_SET_BPF: libc::c_ulong = 1074013192; pub unsafe trait Pod: Copy + 'static {} macro_rules! unsafe_impl_pod { ($($struct_name:ident),+ $(,)?) => { $( unsafe impl Pod for $struct_name { } )+ } } unsafe_impl_pod!(i8, u8, i16, u16, i32, u32, i64, u64); #[repr(C)] #[derive(Copy, Clone, Debug)] pub(crate) struct bpf_map_def { pub(crate) map_type: u32, pub(crate) key_size: u32, pub(crate) value_size: u32, pub(crate) max_entries: u32, pub(crate) map_flags: u32, } #[derive(Debug)] pub struct Bpf { maps: HashMap<String, RefCell<Map>>, programs: HashMap<String, Program>, } impl Bpf { pub fn load(data: &[u8]) -> Result<Bpf, BpfError> { let mut obj = Object::parse(data)?; obj.relocate_btf()?; let mut maps = Vec::new(); for (_, obj) in obj.maps.drain() { let mut map = Map { obj, fd: None }; let fd = map.create()?; if !map.obj.data.is_empty() && map.obj.name != ".bss" { bpf_map_update_elem_ptr(fd, &0 as *const _, map.obj.data.as_ptr(), 0) .map_err(|(code, io_error)| MapError::UpdateElementFailed { code, io_error })?; } maps.push(map); } obj.relocate_maps(maps.as_slice())?; let programs = obj .programs .drain() .map(|(name, obj)| { let kind = obj.kind; let data = ProgramData { obj, name: name.clone(), fd: None, links: Vec::new(), }; let program = match kind { crate::obj::ProgramKind::KProbe => Program::KProbe(KProbe { data }), crate::obj::ProgramKind::UProbe => Program::UProbe(UProbe { data }), crate::obj::ProgramKind::TracePoint => Program::TracePoint(TracePoint { data }), crate::obj::ProgramKind::SocketFilter => { Program::SocketFilter(SocketFilter { data }) } crate::obj::ProgramKind::Xdp => Program::Xdp(Xdp { data }), }; (name, program) }) .collect(); Ok(Bpf { maps: maps .drain(..) .map(|map| (map.obj.name.clone(), RefCell::new(map))) .collect(), programs, }) } pub fn map<'a, 'slf: 'a, T: TryFrom<Ref<'a, Map>>>( &'slf self, name: &str, ) -> Result<Option<T>, <T as TryFrom<Ref<'a, Map>>>::Error> { self.maps .get(name) .map(|cell| T::try_from(cell.borrow())) .transpose() } pub fn map_mut<'a, 'slf: 'a, T: TryFrom<RefMut<'a, Map>>>( &'slf self, name: &str, ) -> Result<Option<T>, <T as TryFrom<RefMut<'a, Map>>>::Error> { self.maps .get(name) .map(|cell| T::try_from(cell.borrow_mut())) .transpose() } pub fn program<'a, 'slf: 'a, T: TryFrom<&'a Program>>( &'slf self, name: &str, ) -> Result<Option<T>, <T as TryFrom<&'a Program>>::Error> { self.programs.get(name).map(|p| T::try_from(p)).transpose() } pub fn program_mut<'a, 'slf: 'a, T: TryFrom<&'a mut Program>>( &'slf mut self, name: &str, ) -> Result<Option<T>, <T as TryFrom<&'a mut Program>>::Error> { self.programs .get_mut(name) .map(|p| T::try_from(p)) .transpose() } } #[derive(Debug, Error)] pub enum BpfError { #[error("error parsing BPF object: {0}")] ParseError(#[from] ParseError), #[error("error relocating BPF object: {0}")] RelocationError(#[from] RelocationError), #[error("BTF error: {error}")] BtfRelocationError { #[from] error: BtfRelocationError, }, #[error("map error: {0}")] MapError(#[from] MapError), #[error("program error: {0}")] ProgramError(#[from] ProgramError), }