use core::convert::TryInto;
use core::default::Default;
use core::marker::PhantomData;
use core::{mem, ptr};
use cty::*;
use crate::bindings::*;
use crate::helpers::*;
macro_rules! define_hashmap {
($(#[$attr:meta])* $name:ident, $map_type:expr) => {
$(#[$attr])*
#[repr(transparent)]
pub struct $name<K, V> {
def: bpf_map_def,
_k: PhantomData<K>,
_v: PhantomData<V>,
}
impl<K, V> $name<K, V> {
pub const fn with_max_entries(max_entries: u32) -> Self {
Self {
def: bpf_map_def {
type_: $map_type,
key_size: mem::size_of::<K>() as u32,
value_size: mem::size_of::<V>() as u32,
max_entries,
map_flags: 0,
},
_k: PhantomData,
_v: PhantomData,
}
}
#[inline]
pub fn get(&mut self, key: &K) -> Option<&V> {
unsafe {
let value = bpf_map_lookup_elem(
&mut self.def as *mut _ as *mut c_void,
key as *const _ as *const c_void,
);
if value.is_null() {
None
} else {
Some(&*(value as *const V))
}
}
}
#[inline]
pub fn get_mut(&mut self, key: &K) -> Option<&mut V> {
unsafe {
let value = bpf_map_lookup_elem(
&mut self.def as *mut _ as *mut c_void,
key as *const _ as *const c_void,
);
if value.is_null() {
None
} else {
Some(&mut *(value as *mut V))
}
}
}
#[inline]
pub fn get_val(&mut self, key: &K) -> Option<V> {
unsafe {
let value = bpf_map_lookup_elem(
&mut self.def as *mut _ as *mut c_void,
key as *const _ as *const c_void,
);
if value.is_null() {
None
} else {
Some(ptr::read_unaligned(value as *const V))
}
}
}
#[inline]
pub fn set(&mut self, key: &K, value: &V) {
unsafe {
bpf_map_update_elem(
&mut self.def as *mut _ as *mut c_void,
key as *const _ as *const c_void,
value as *const _ as *const c_void,
BPF_ANY.into(),
);
}
}
#[inline]
pub fn delete(&mut self, key: &K) {
unsafe {
bpf_map_delete_elem(
&mut self.def as *mut _ as *mut c_void,
key as *const _ as *const c_void,
);
}
}
}
};
}
macro_rules! define_array {
($(#[$attr:meta])* $name:ident, $map_type:expr) => {
$(#[$attr])*
#[repr(transparent)]
pub struct $name<T> {
def: bpf_map_def,
_element: PhantomData<T>,
}
impl<T> $name<T> {
pub const fn with_max_entries(max_entries: u32) -> Self {
Self {
def: bpf_map_def {
type_: $map_type,
key_size: mem::size_of::<u32>() as u32,
value_size: mem::size_of::<T>() as u32,
max_entries,
map_flags: 0,
},
_element: PhantomData,
}
}
#[inline]
pub fn get(&mut self, index: u32) -> Option<&T> {
unsafe {
let value = bpf_map_lookup_elem(
&mut self.def as *mut _ as *mut c_void,
&index as *const _ as *const c_void,
);
if value.is_null() {
None
} else {
Some(&*(value as *const T))
}
}
}
#[inline]
pub fn get_mut(&mut self, index: u32) -> Option<&mut T> {
unsafe {
let value = bpf_map_lookup_elem(
&mut self.def as *mut _ as *mut c_void,
&index as *const _ as *const c_void,
);
if value.is_null() {
None
} else {
Some(&mut *(value as *mut T))
}
}
}
#[inline]
pub fn set(&mut self, index: u32, value: &T) {
unsafe {
bpf_map_update_elem(
&mut self.def as *mut _ as *mut c_void,
&index as *const _ as *const c_void,
value as *const _ as *const c_void,
BPF_ANY.into(),
);
}
}
}
};
}
define_hashmap!(
HashMap,
bpf_map_type_BPF_MAP_TYPE_HASH
);
define_hashmap!(
PerCpuHashMap,
bpf_map_type_BPF_MAP_TYPE_PERCPU_HASH
);
define_hashmap!(
LruHashMap,
bpf_map_type_BPF_MAP_TYPE_LRU_HASH
);
define_hashmap!(
LruPerCpuHashMap,
bpf_map_type_BPF_MAP_TYPE_LRU_PERCPU_HASH
);
define_array!(
Array,
bpf_map_type_BPF_MAP_TYPE_ARRAY
);
define_array!(
PerCpuArray,
bpf_map_type_BPF_MAP_TYPE_PERCPU_ARRAY
);
#[derive(Debug, Copy, Clone)]
pub struct PerfMapFlags {
index: Option<u32>,
pub(crate) xdp_size: u32,
}
impl Default for PerfMapFlags {
#[inline]
fn default() -> Self {
PerfMapFlags {
index: None,
xdp_size: 0,
}
}
}
impl PerfMapFlags {
#[inline]
pub fn new() -> Self {
Default::default()
}
#[inline]
pub fn with_xdp_size(size: u32) -> Self {
*PerfMapFlags::new().xdp_size(size)
}
#[inline]
pub fn index(&mut self, index: u32) -> &mut PerfMapFlags {
self.index = Some(index);
self
}
#[inline]
pub fn xdp_size(&mut self, size: u32) -> &mut PerfMapFlags {
self.xdp_size = size;
self
}
}
impl From<PerfMapFlags> for u64 {
#[inline]
fn from(flags: PerfMapFlags) -> u64 {
(flags.xdp_size as u64) << 32
| (flags
.index
.unwrap_or_else(|| BPF_F_CURRENT_CPU.try_into().unwrap()) as u64)
}
}
#[repr(transparent)]
pub struct PerfMap<T> {
def: bpf_map_def,
_event: PhantomData<T>,
}
impl<T> PerfMap<T> {
pub const fn with_max_entries(max_entries: u32) -> Self {
Self {
def: bpf_map_def {
type_: bpf_map_type_BPF_MAP_TYPE_PERF_EVENT_ARRAY,
key_size: mem::size_of::<u32>() as u32,
value_size: mem::size_of::<u32>() as u32,
max_entries,
map_flags: 0,
},
_event: PhantomData,
}
}
#[inline]
pub fn insert<C>(&mut self, ctx: *mut C, data: &T) {
self.insert_with_flags(ctx, data, PerfMapFlags::default())
}
#[inline]
pub fn insert_with_flags<C>(&mut self, ctx: *mut C, data: &T, flags: PerfMapFlags) {
bpf_perf_event_output(
ctx as *mut _ as *mut c_void,
&mut self.def as *mut _ as *mut c_void,
flags.into(),
data as *const _ as *const c_void,
mem::size_of::<T>() as u64,
);
}
}
const BPF_MAX_STACK_DEPTH: usize = 127;
#[repr(transparent)]
pub struct StackTrace {
def: bpf_map_def,
}
#[repr(C)]
struct BpfStackFrames {
ip: [u64; BPF_MAX_STACK_DEPTH],
}
impl StackTrace {
pub const fn with_max_entries(cap: u32) -> Self {
StackTrace {
def: bpf_map_def {
type_: bpf_map_type_BPF_MAP_TYPE_STACK_TRACE,
key_size: mem::size_of::<u32>() as u32,
value_size: mem::size_of::<BpfStackFrames>() as u32,
max_entries: cap,
map_flags: 0,
},
}
}
pub unsafe fn stack_id(&mut self, ctx: *mut pt_regs, flag: u64) -> Result<i64, i64> {
let ret = bpf_get_stackid(ctx as _, &mut self.def as *mut _ as _, flag);
if ret >= 0 {
Ok(ret)
} else {
Err(ret)
}
}
}
#[repr(transparent)]
pub struct ProgramArray {
def: bpf_map_def,
}
impl ProgramArray {
pub const fn with_max_entries(max_entries: u32) -> Self {
Self {
def: bpf_map_def {
type_: bpf_map_type_BPF_MAP_TYPE_PROG_ARRAY,
key_size: mem::size_of::<u32>() as u32,
value_size: mem::size_of::<u32>() as u32,
max_entries,
map_flags: 0,
},
}
}
pub unsafe fn tail_call<C>(&mut self, ctx: *mut C, index: u32) -> Result<(), i64> {
let ret = bpf_tail_call(ctx as *mut _, &mut self.def as *mut _ as *mut c_void, index);
if ret < 0 {
return Err(ret);
}
Ok(())
}
}
pub struct SockMap {
def: bpf_map_def,
}
impl SockMap {
pub const fn with_max_entries(max_entries: u32) -> Self {
Self {
def: bpf_map_def {
type_: bpf_map_type_BPF_MAP_TYPE_SOCKMAP,
key_size: mem::size_of::<i32>() as u32,
value_size: mem::size_of::<i32>() as u32,
max_entries,
map_flags: 0,
},
}
}
pub fn redirect(&mut self, skb: *mut __sk_buff, key: u32) -> Result<(), ()> {
let ret = unsafe {
bpf_sk_redirect_map(
skb as *mut _,
&mut self.def as *mut _ as *mut c_void,
key,
0,
) as sk_action
};
#[allow(non_upper_case_globals)]
match ret {
sk_action_SK_PASS => Ok(()),
sk_action_SK_DROP => Err(()),
_ => panic!("invalid return value of bpf_sk_redirect_map"),
}
}
pub fn redirect_ingress(&mut self, skb: *mut __sk_buff, key: u32) -> Result<(), ()> {
let ret: sk_action = unsafe {
bpf_sk_redirect_map(
skb as *mut _,
&mut self.def as *mut _ as *mut c_void,
key,
BPF_F_INGRESS.into(),
) as sk_action
};
#[allow(non_upper_case_globals)]
match ret {
sk_action_SK_PASS => Ok(()),
sk_action_SK_DROP => Err(()),
_ => panic!("invalid return value of bpf_sk_redirect_map"),
}
}
}