#[cfg(feature = "userspace")]
use crate::{
error::{Error, LibbpfError, Result},
interface,
map_layout::{MapLayout, ReadPointer, WritePointer},
utils::*,
};
pub use libbpf_sys;
#[allow(unused)]
use std::{ffi::CString, marker::PhantomData, mem, os::raw, path::Path, ptr};
#[cfg(feature = "userspace")]
#[repr(u32)]
#[allow(non_camel_case_types)]
pub enum BpfProgType {
UNSPEC = libbpf_sys::BPF_PROG_TYPE_UNSPEC,
SOCKET_FILTER = libbpf_sys::BPF_PROG_TYPE_SOCKET_FILTER,
KPROBE = libbpf_sys::BPF_PROG_TYPE_KPROBE,
SCHED_CLS = libbpf_sys::BPF_PROG_TYPE_SCHED_CLS,
SCHED_ACT = libbpf_sys::BPF_PROG_TYPE_SCHED_ACT,
TRACEPOINT = libbpf_sys::BPF_PROG_TYPE_TRACEPOINT,
XDP = libbpf_sys::BPF_PROG_TYPE_XDP,
PERF_EVENT = libbpf_sys::BPF_PROG_TYPE_PERF_EVENT,
CGROUP_SKB = libbpf_sys::BPF_PROG_TYPE_CGROUP_SKB,
CGROUP_SOCK = libbpf_sys::BPF_PROG_TYPE_CGROUP_SOCK,
LWT_IN = libbpf_sys::BPF_PROG_TYPE_LWT_IN,
LWT_OUT = libbpf_sys::BPF_PROG_TYPE_LWT_OUT,
LWT_XMIT = libbpf_sys::BPF_PROG_TYPE_LWT_XMIT,
SOCK_OPS = libbpf_sys::BPF_PROG_TYPE_SOCK_OPS,
SK_SKB = libbpf_sys::BPF_PROG_TYPE_SK_SKB,
CGROUP_DEVICE = libbpf_sys::BPF_PROG_TYPE_CGROUP_DEVICE,
SK_MSG = libbpf_sys::BPF_PROG_TYPE_SK_MSG,
RAW_TRACEPOINT = libbpf_sys::BPF_PROG_TYPE_RAW_TRACEPOINT,
CGROUP_SOCK_ADDR = libbpf_sys::BPF_PROG_TYPE_CGROUP_SOCK_ADDR,
LWT_SEG6LOCAL = libbpf_sys::BPF_PROG_TYPE_LWT_SEG6LOCAL,
LIRC_MODE2 = libbpf_sys::BPF_PROG_TYPE_LIRC_MODE2,
SK_REUSEPORT = libbpf_sys::BPF_PROG_TYPE_SK_REUSEPORT,
FLOW_DISSECTOR = libbpf_sys::BPF_PROG_TYPE_FLOW_DISSECTOR,
CGROUP_SYSCTL = libbpf_sys::BPF_PROG_TYPE_CGROUP_SYSCTL,
RAW_TRACEPOINT_WRITABLE = libbpf_sys::BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE,
CGROUP_SOCKOPT = libbpf_sys::BPF_PROG_TYPE_CGROUP_SOCKOPT,
}
bitflags::bitflags! {
pub struct BpfUpdateElemFlags: u32 {
const ANY = libbpf_sys::BPF_ANY;
const NOEXIST = libbpf_sys::BPF_NOEXIST;
const EXIST = libbpf_sys::BPF_EXIST;
}
}
#[derive(Debug, PartialEq, Eq)]
#[repr(u32)]
#[allow(non_camel_case_types)]
pub enum BpfMapType {
UNSPEC = libbpf_sys::BPF_MAP_TYPE_UNSPEC,
HASH = libbpf_sys::BPF_MAP_TYPE_HASH,
ARRAY = libbpf_sys::BPF_MAP_TYPE_ARRAY,
PROG_ARRAY = libbpf_sys::BPF_MAP_TYPE_PROG_ARRAY,
PERF_EVENT_ARRAY = libbpf_sys::BPF_MAP_TYPE_PERF_EVENT_ARRAY,
PERCPU_HASH = libbpf_sys::BPF_MAP_TYPE_PERCPU_HASH,
PERCPU_ARRAY = libbpf_sys::BPF_MAP_TYPE_PERCPU_ARRAY,
STACK_TRACE = libbpf_sys::BPF_MAP_TYPE_STACK_TRACE,
CGROUP_ARRAY = libbpf_sys::BPF_MAP_TYPE_CGROUP_ARRAY,
LRU_HASH = libbpf_sys::BPF_MAP_TYPE_LRU_HASH,
LRU_PERCPU_HASH = libbpf_sys::BPF_MAP_TYPE_LRU_PERCPU_HASH,
LPM_TRIE = libbpf_sys::BPF_MAP_TYPE_LPM_TRIE,
ARRAY_OF_MAPS = libbpf_sys::BPF_MAP_TYPE_ARRAY_OF_MAPS,
HASH_OF_MAPS = libbpf_sys::BPF_MAP_TYPE_HASH_OF_MAPS,
DEVMAP = libbpf_sys::BPF_MAP_TYPE_DEVMAP,
SOCKMAP = libbpf_sys::BPF_MAP_TYPE_SOCKMAP,
CPUMAP = libbpf_sys::BPF_MAP_TYPE_CPUMAP,
XSKMAP = libbpf_sys::BPF_MAP_TYPE_XSKMAP,
SOCKHASH = libbpf_sys::BPF_MAP_TYPE_SOCKHASH,
CGROUP_STORAGE = libbpf_sys::BPF_MAP_TYPE_CGROUP_STORAGE,
REUSEPORT_SOCKARRAY = libbpf_sys::BPF_MAP_TYPE_REUSEPORT_SOCKARRAY,
PERCPU_CGROUP_STORAGE = libbpf_sys::BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE,
QUEUE = libbpf_sys::BPF_MAP_TYPE_QUEUE,
STACK = libbpf_sys::BPF_MAP_TYPE_STACK,
SK_STORAGE = libbpf_sys::BPF_MAP_TYPE_SK_STORAGE,
DEVMAP_HASH = libbpf_sys::BPF_MAP_TYPE_DEVMAP_HASH,
}
#[cfg(feature = "userspace")]
pub struct BpfObject {
pobj: *mut libbpf_sys::bpf_object,
}
#[cfg(feature = "userspace")]
pub struct BpfObjectIterator<'a> {
src: &'a BpfObject,
next: Option<BpfProgram>,
}
#[cfg(feature = "userspace")]
impl BpfObjectIterator<'_> {
fn new(src: &BpfObject) -> BpfObjectIterator {
let next = bpf_program__next(None, src);
BpfObjectIterator { src, next }
}
}
#[cfg(feature = "userspace")]
impl Iterator for BpfObjectIterator<'_> {
type Item = BpfProgram;
fn next(&mut self) -> Option<Self::Item> {
match &self.next {
Some(prog) => {
let mut next = bpf_program__next(Some(prog), self.src);
mem::swap(&mut next, &mut self.next);
next
}
None => None,
}
}
}
#[cfg(feature = "userspace")]
impl<'a> IntoIterator for &'a BpfObject {
type Item = BpfProgram;
type IntoIter = BpfObjectIterator<'a>;
fn into_iter(self) -> Self::IntoIter {
BpfObjectIterator::new(self)
}
}
#[cfg(feature = "userspace")]
pub trait BpfFd {
type BpfInfoType;
fn fd(&self) -> raw::c_int;
}
#[cfg(feature = "userspace")]
pub trait BpfInfo {
type BpfRawInfoType;
fn new(raw_info: Self::BpfRawInfoType) -> Self;
}
#[cfg(feature = "userspace")]
pub type BpfProgFd = BpfFdImpl<BpfProgInfo, libbpf_sys::bpf_prog_info>;
#[cfg(feature = "userspace")]
pub struct BpfFdImpl<T, U>
where
T: BpfInfo<BpfRawInfoType = U>,
{
fd: raw::c_int,
_info_type: std::marker::PhantomData<T>,
}
#[cfg(feature = "userspace")]
impl<T, U> BpfFd for BpfFdImpl<T, U>
where
T: BpfInfo<BpfRawInfoType = U>,
{
type BpfInfoType = T;
fn fd(&self) -> raw::c_int {
self.fd
}
}
#[cfg(feature = "userspace")]
pub struct BpfProgram {
pprogram: *mut libbpf_sys::bpf_program,
}
#[cfg(feature = "userspace")]
pub struct BpfProgInfo {
info: libbpf_sys::bpf_prog_info,
}
#[cfg(feature = "userspace")]
impl BpfInfo for BpfProgInfo {
type BpfRawInfoType = libbpf_sys::bpf_prog_info;
fn new(raw_info: Self::BpfRawInfoType) -> Self {
BpfProgInfo { info: raw_info }
}
}
#[cfg(feature = "userspace")]
impl BpfProgInfo {
pub fn id(&self) -> u32 {
self.info.id
}
#[named]
pub fn name(&self) -> Result<String> {
let name = &self.info.name;
c_char_pointer_to_string(name.as_ptr())
}
}
#[cfg(feature = "userspace")]
pub struct BpfMap {
pmap: *mut libbpf_sys::bpf_map,
}
#[cfg(feature = "userspace")]
pub type UnsafeBpfMapFd = BpfFdImpl<BpfMapInfo, libbpf_sys::bpf_map_info>;
#[cfg(feature = "userspace")]
pub struct BpfMapFd<Key, Value, MapLayoutTy: MapLayout> {
map_fd: UnsafeBpfMapFd,
_key_ty: std::marker::PhantomData<Key>,
_value_ty: std::marker::PhantomData<Value>,
_scalar_marker_ty: std::marker::PhantomData<MapLayoutTy>,
}
#[cfg(feature = "userspace")]
impl<K, V, L: MapLayout> BpfMapFd<K, V, L> {
pub fn new(fd: raw::c_int) -> Self {
BpfMapFd {
map_fd: UnsafeBpfMapFd {
fd,
_info_type: std::marker::PhantomData,
},
_key_ty: std::marker::PhantomData,
_value_ty: std::marker::PhantomData,
_scalar_marker_ty: std::marker::PhantomData,
}
}
}
#[cfg(feature = "userspace")]
impl<K, V, L: MapLayout> BpfFd for BpfMapFd<K, V, L> {
type BpfInfoType = BpfMapInfo;
fn fd(&self) -> raw::c_int {
self.map_fd.fd()
}
}
#[allow(unused)]
#[repr(transparent)]
pub struct BpfMapDef<T, U> {
pub(crate) map_def: libbpf_sys::bpf_map_def,
_key_ty: PhantomData<T>,
_value_ty: PhantomData<U>,
}
impl<T, U> BpfMapDef<T, U> {
pub const fn new(type_: BpfMapType, max_entries: u32) -> Self {
BpfMapDef {
map_def: libbpf_sys::bpf_map_def {
type_: type_ as u32,
key_size: mem::size_of::<T>() as u32,
value_size: mem::size_of::<U>() as u32,
max_entries,
map_flags: 0,
},
_key_ty: PhantomData,
_value_ty: PhantomData,
}
}
#[cfg(feature = "userspace")]
pub fn to_bpf_map_info(&self) -> BpfMapInfo {
let mut info: libbpf_sys::bpf_map_info = unsafe { mem::zeroed() };
info.type_ = self.map_def.type_;
info.key_size = self.map_def.key_size;
info.value_size = self.map_def.value_size;
info.max_entries = self.map_def.max_entries;
info.map_flags = self.map_def.map_flags;
BpfMapInfo { info }
}
}
#[cfg(feature = "userspace")]
pub struct BpfMapInfo {
info: libbpf_sys::bpf_map_info,
}
#[cfg(feature = "userspace")]
impl BpfInfo for BpfMapInfo {
type BpfRawInfoType = libbpf_sys::bpf_map_info;
fn new(raw_info: Self::BpfRawInfoType) -> Self {
BpfMapInfo { info: raw_info }
}
}
#[cfg(feature = "userspace")]
impl BpfMapInfo {
pub fn id(&self) -> u32 {
self.info.id
}
pub fn value_size(&self) -> u32 {
self.info.value_size
}
pub fn key_size(&self) -> u32 {
self.info.key_size
}
pub fn max_entries(&self) -> u32 {
self.info.max_entries
}
pub fn type_(&self) -> BpfMapType {
let map_type: BpfMapType = unsafe { std::mem::transmute(self.info.type_) };
map_type
}
pub fn name(&self) -> Result<String> {
let name = &self.info.name;
c_char_pointer_to_string(name.as_ptr())
}
pub fn matches_map_def<K, V>(&self, map_def: &BpfMapDef<K, V>) -> bool {
let other = map_def.to_bpf_map_info();
self.value_size() == other.value_size()
&& self.key_size() == other.key_size()
&& self.type_() == other.type_()
}
}
#[cfg(feature = "userspace")]
#[named]
pub fn bpf_obj_get_info_by_fd<T: BpfFd>(bpf_fd: &T) -> Result<T::BpfInfoType>
where
T::BpfInfoType: BpfInfo,
{
let mut info: <<T as BpfFd>::BpfInfoType as BpfInfo>::BpfRawInfoType = unsafe { mem::zeroed() };
let info_void_p = to_mut_c_void(&mut info);
let mut info_len: u32 =
mem::size_of::<<<T as BpfFd>::BpfInfoType as BpfInfo>::BpfRawInfoType>() as u32;
let err =
unsafe { libbpf_sys::bpf_obj_get_info_by_fd(bpf_fd.fd(), info_void_p, &mut info_len) };
if err != 0 {
return map_libbpf_error(function_name!(), LibbpfError::LibbpfSys(err));
}
Ok(<<T as BpfFd>::BpfInfoType as BpfInfo>::new(info))
}
#[cfg(feature = "userspace")]
#[named]
pub fn bpf_prog_load(
file_path: &Path,
bpf_prog_type: BpfProgType,
) -> Result<(BpfObject, BpfProgFd)> {
let mut pobj: *mut libbpf_sys::bpf_object = ptr::null_mut();
let mut prog_fd: raw::c_int = -1;
let file_path_s = path_to_str(file_path)?;
let file = str_to_cstring(file_path_s)?;
let err = unsafe {
libbpf_sys::bpf_prog_load(file.as_ptr(), bpf_prog_type as u32, &mut pobj, &mut prog_fd)
};
if err != 0 {
return map_libbpf_error(function_name!(), LibbpfError::LibbpfSys(err));
}
if prog_fd < 0 {
return map_libbpf_error(function_name!(), LibbpfError::InvalidFd);
}
Ok((
BpfObject { pobj },
BpfProgFd {
fd: prog_fd,
_info_type: std::marker::PhantomData,
},
))
}
#[cfg(feature = "userspace")]
pub fn bpf_map_lookup_elem<K, V, L: MapLayout>(
map_fd: &BpfMapFd<K, V, L>,
key: &K,
value: impl WritePointer<V, L>,
) -> Option<()> {
let key_void_p = to_const_c_void(key);
match unsafe { libbpf_sys::bpf_map_lookup_elem(map_fd.fd(), key_void_p, value.get_ptr_mut()) } {
0 => Some(()),
_ => None,
}
}
#[cfg(feature = "userspace")]
pub fn bpf_map_update_elem<K, V, L: MapLayout>(
map_fd: &BpfMapFd<K, V, L>,
key: &K,
value: impl ReadPointer<V, L>,
flags: BpfUpdateElemFlags,
) -> Result<()> {
let key = to_const_c_void(key);
let value = value.get_ptr();
match unsafe { libbpf_sys::bpf_map_update_elem(map_fd.fd(), key, value, flags.bits() as u64) } {
0 => Ok(()),
err => Err(Error::Libbpf(
"Map update error".to_owned(),
LibbpfError::LibbpfSys(err),
)),
}
}
#[cfg(feature = "userspace")]
#[allow(non_snake_case)]
pub fn bpf_object__find_program_by_title(
bpf_object: &BpfObject,
title: &str,
) -> Result<BpfProgram> {
let title_cs: CString = str_to_cstring(title)?;
let bpf_program: *mut libbpf_sys::bpf_program = unsafe {
libbpf_sys::bpf_object__find_program_by_title(bpf_object.pobj, title_cs.as_ptr())
};
if bpf_program.is_null() {
return Err(Error::InvalidProgName);
}
Ok(BpfProgram {
pprogram: bpf_program,
})
}
#[cfg(feature = "userspace")]
#[allow(non_snake_case)]
pub fn bpf_object__find_map_by_name(bpf_object: &BpfObject, name: &str) -> Result<BpfMap> {
let name_cs = str_to_cstring(name)?;
let bpf_map =
unsafe { libbpf_sys::bpf_object__find_map_by_name(bpf_object.pobj, name_cs.as_ptr()) };
if bpf_map.is_null() {
return Err(Error::InvalidMapName);
}
Ok(BpfMap { pmap: bpf_map })
}
#[cfg(feature = "userspace")]
#[allow(non_snake_case)]
pub fn bpf_object__find_map_fd_by_name<K, V, L: MapLayout>(
bpf_object: &BpfObject,
name: &str,
) -> Result<BpfMapFd<K, V, L>> {
let name_cs = str_to_cstring(name)?;
let bpf_map_fd =
unsafe { libbpf_sys::bpf_object__find_map_fd_by_name(bpf_object.pobj, name_cs.as_ptr()) };
if bpf_map_fd < 0 {
return Err(Error::InvalidMapName);
}
Ok(BpfMapFd::new(bpf_map_fd))
}
#[cfg(feature = "userspace")]
#[allow(non_snake_case)]
pub fn bpf_program__set_type(bpf_program: &mut BpfProgram, bpf_prog_type: BpfProgType) {
unsafe {
libbpf_sys::bpf_program__set_type(bpf_program.pprogram, bpf_prog_type as u32);
};
}
#[cfg(feature = "userspace")]
#[allow(non_snake_case)]
pub fn bpf_program__set_ifindex(bpf_program: &mut BpfProgram, interface: &interface::Interface) {
unsafe {
libbpf_sys::bpf_program__set_ifindex(bpf_program.pprogram, interface.ifindex);
};
}
#[cfg(feature = "userspace")]
#[allow(non_snake_case)]
pub fn bpf_program__next(
bpf_program: Option<&BpfProgram>,
bpf_object: &BpfObject,
) -> Option<BpfProgram> {
let pprogram = unsafe {
libbpf_sys::bpf_program__next(
bpf_program.map(|p| p.pprogram).unwrap_or(ptr::null_mut()),
bpf_object.pobj,
)
};
if pprogram.is_null() {
return None;
}
Some(BpfProgram { pprogram })
}
#[cfg(feature = "userspace")]
#[allow(non_snake_case)]
#[named]
pub fn bpf_program__fd(bpf_program: &BpfProgram) -> Result<BpfProgFd> {
let prog_fd = unsafe { libbpf_sys::bpf_program__fd(bpf_program.pprogram) };
if prog_fd < 0 {
return map_libbpf_error(function_name!(), LibbpfError::InvalidFd);
}
Ok(BpfProgFd {
fd: prog_fd,
_info_type: std::marker::PhantomData,
})
}
#[cfg(feature = "userspace")]
#[allow(non_snake_case)]
#[named]
pub fn bpf_program__title(bpf_program: &BpfProgram) -> Result<String> {
let title_c_char_p = unsafe { libbpf_sys::bpf_program__title(bpf_program.pprogram, false) };
if title_c_char_p.is_null() {
return map_libbpf_error(function_name!(), LibbpfError::InvalidTitle);
}
c_char_pointer_to_string(title_c_char_p)
}
#[cfg(feature = "userspace")]
#[allow(non_snake_case)]
#[named]
pub fn bpf_map__fd<K, V, L: MapLayout>(bpf_map: &BpfMap) -> Result<BpfMapFd<K, V, L>> {
let fd = unsafe { libbpf_sys::bpf_map__fd(bpf_map.pmap) };
if fd < 0 {
return map_libbpf_error(function_name!(), LibbpfError::InvalidFd);
}
Ok(BpfMapFd::new(fd))
}
#[derive(Debug)]
#[repr(u32)]
#[allow(non_camel_case_types)]
pub enum XdpAction {
ABORTED = libbpf_sys::XDP_ABORTED,
DROP = libbpf_sys::XDP_DROP,
PASS = libbpf_sys::XDP_PASS,
TX = libbpf_sys::XDP_TX,
REDIRECT = libbpf_sys::XDP_REDIRECT,
}
bitflags::bitflags! {
pub struct XdpFlags: u32 {
const UPDATE_IF_NOEXIST = libbpf_sys::XDP_FLAGS_UPDATE_IF_NOEXIST;
const SKB_MODE = libbpf_sys::XDP_FLAGS_SKB_MODE;
const DRV_MODE = libbpf_sys::XDP_FLAGS_DRV_MODE;
const HW_MODE = libbpf_sys::XDP_FLAGS_HW_MODE;
const MODES = libbpf_sys::XDP_FLAGS_MODES;
const MASK = libbpf_sys::XDP_FLAGS_MASK;
}
}
#[repr(transparent)]
pub struct XdpMd(libbpf_sys::xdp_md);
#[cfg(feature = "bpf")]
impl XdpMd {
#[inline(always)]
pub fn data_buffer(&self) -> Option<&[u8]> {
unsafe {
let data_buffer: *const u8 = self.0.data as usize as *const u8;
if self.0.data_end <= self.0.data {
return None;
}
let data_buffer_size = (self.0.data_end - self.0.data) as usize;
Some(std::slice::from_raw_parts(data_buffer, data_buffer_size))
}
}
#[inline(always)]
pub fn data_pointer(&self) -> (*const u8, *const u8) {
(
self.0.data as usize as *const u8,
self.0.data_end as usize as *const u8,
)
}
#[inline(always)]
pub fn data_meta(&self) -> u32 {
self.0.data_meta
}
#[inline(always)]
pub fn ingress_ifindex(&self) -> u32 {
self.0.ingress_ifindex
}
#[inline(always)]
pub fn rx_queue_index(&self) -> u32 {
self.0.rx_queue_index
}
}
#[cfg(feature = "userspace")]
#[named]
pub fn bpf_set_link_xdp_fd(
interface: &interface::Interface,
bpf_fd: Option<&BpfProgFd>,
xdp_flags: XdpFlags,
) -> Result<()> {
let fd = bpf_fd.map_or(-1, |f| f.fd);
let err =
unsafe { libbpf_sys::bpf_set_link_xdp_fd(interface.ifindex as i32, fd, xdp_flags.bits()) };
if err < 0 {
return map_libbpf_error(function_name!(), LibbpfError::LibbpfSys(err));
}
Ok(())
}
#[cfg(feature = "userspace")]
#[named]
pub fn libbpf_num_possible_cpus() -> Result<i32> {
let num_cpus = unsafe { libbpf_sys::libbpf_num_possible_cpus() };
if num_cpus < 0 {
return map_libbpf_sys_error(function_name!(), num_cpus);
}
Ok(num_cpus)
}