use errno::{set_errno, Errno};
use libbpf_sys as bpf;
use std::{mem::size_of, os::raw::c_void};
use crate::error::{get_errno, reset_errno};
use crate::map_batch::*;
use crate::utils;
use crate::{BatchResult, MapFlags, MapType, XDPError, XDPLoadedObject, XDPResult};
#[derive(Debug)]
pub struct KeyValue<K, V> {
pub key: K,
pub value: V,
}
#[derive(PartialEq, Eq, Debug)]
pub enum MapValue<V> {
Single(V),
Multi(Vec<V>),
}
impl<V> MapValue<V> {
pub fn into_vec(self) -> Vec<V> {
match self {
MapValue::Multi(r) => r,
MapValue::Single(r) => vec![r],
}
}
pub fn into_single(self) -> V {
match self {
MapValue::Multi(mut r) => r.swap_remove(0),
MapValue::Single(r) => r,
}
}
}
pub trait MapLike<K, V: Default> {
#[doc(hidden)]
fn get_next_key(&self, prev_key: *const c_void, key: &mut K) -> XDPResult<()> {
let rc = unsafe {
bpf::bpf_map_get_next_key(self.map_fd(), prev_key, key as *mut _ as *mut c_void)
};
crate::map_common::check_rc(rc, (), "Error getting next key")
}
#[doc(hidden)]
fn update_batching_not_supported(&self) -> bool;
#[doc(hidden)]
fn update_batch_impl(
&self,
keys: &mut Vec<K>,
values: &mut Vec<V>,
opts: &bpf::bpf_map_batch_opts,
) -> (i32, u32) {
let mut count: u32 = keys.len() as u32;
let rc = crate::map_common::update_batch(
self.map_fd(),
keys.as_mut_ptr() as *mut c_void,
values.as_mut_ptr() as *mut c_void,
&mut count,
&opts,
);
(rc, count)
}
#[doc(hidden)]
fn lookup_batch_impl(
&self,
batch_size: u32,
next_key: Option<u32>,
delete: bool,
) -> XDPResult<BatchResult<K, MapValue<V>>>;
#[doc(hidden)]
fn _items(&self) -> XDPResult<Vec<KeyValue<K, MapValue<V>>>>;
fn map_fd(&self) -> i32;
fn map_type(&self) -> MapType;
fn max_entries(&self) -> u32;
fn lookup(&self, key: &K) -> XDPResult<MapValue<V>> {
let mut value: V = Default::default();
let rc = crate::map_common::lookup_elem(
self.map_fd(),
key as *const _ as *const c_void,
&mut value as *mut _ as *mut c_void,
);
crate::map_common::check_rc(rc, MapValue::Single(value), "Error looking up elem")
}
fn update(&self, key: &K, value: &V, flags: MapFlags) -> XDPResult<()> {
crate::map_common::update_elem(
self.map_fd(),
key as *const _ as *const c_void,
value as *const _ as *const c_void,
flags as u64,
)
}
fn delete(&self, key: &K) -> XDPResult<()> {
if self.map_type().is_array() {
set_errno(Errno(22));
fail!("Delete not supported on this map type");
}
let rc =
unsafe { bpf::bpf_map_delete_elem(self.map_fd(), key as *const _ as *const c_void) };
crate::map_common::check_rc(rc, (), "Error deleting elem")
}
fn update_batch(
&self,
keys: &mut Vec<K>,
values: &mut Vec<V>,
flags: MapFlags,
) -> XDPResult<u32> {
let num_keys = keys.len();
let num_vals = values.len();
if num_keys != num_vals {
set_errno(Errno(22));
fail!(
"Num keys must match num values. Got {} keys, {} values",
num_keys,
num_vals
);
}
if self.update_batching_not_supported() {
for i in 0..num_keys {
self.update(&keys[i], &values[i], flags)?
}
return Ok(num_keys as u32);
}
let opts = bpf::bpf_map_batch_opts {
sz: 24u64,
elem_flags: flags as u64,
flags: 0u64,
};
let (rc, count) = self.update_batch_impl(keys, values, &opts);
crate::map_common::check_rc(rc, count, "Error updating batch of elements")
}
fn lookup_batch(
&self,
batch_size: u32,
next_key: Option<u32>,
) -> XDPResult<BatchResult<K, MapValue<V>>> {
if !is_batching_supported() {
set_errno(Errno(95));
fail!("Batching not supported");
}
self.lookup_batch_impl(batch_size, next_key, false)
}
fn lookup_and_delete_batch(
&self,
batch_size: u32,
next_key: Option<u32>,
) -> XDPResult<BatchResult<K, MapValue<V>>> {
if !is_batching_supported() {
set_errno(Errno(95));
fail!("Batching not supported");
}
if self.map_type().is_array() {
set_errno(Errno(22));
fail!("Delete not supported on this map type");
}
self.lookup_batch_impl(batch_size, next_key, true)
}
fn items(&self) -> XDPResult<Vec<KeyValue<K, MapValue<V>>>>;
}
pub(crate) fn check_rc<T>(rc: i32, ret: T, err_msg: &str) -> XDPResult<T> {
if rc < 0 {
fail!(err_msg);
}
Ok(ret)
}
pub(crate) fn create_map(
map_type: MapType,
key_size: u32,
value_size: u32,
max_entries: u32,
map_flags: u32,
) -> i32 {
unsafe {
bpf::bpf_create_map(
map_type as u32,
key_size as i32,
value_size as i32,
max_entries as i32,
map_flags,
)
}
}
pub(crate) fn update_elem(
fd: i32,
key: *const c_void,
val: *const c_void,
flags: u64,
) -> XDPResult<()> {
let rc = unsafe { bpf::bpf_map_update_elem(fd, key, val, flags) };
check_rc(rc, (), "Error updating elem")
}
pub(crate) fn lookup_elem(fd: i32, key: *const c_void, val: *mut c_void) -> i32 {
unsafe { bpf::bpf_map_lookup_elem(fd, key, val) }
}
pub(crate) fn update_batch(
fd: i32,
key: *mut c_void,
val: *mut c_void,
count: &mut u32,
opts: &bpf::bpf_map_batch_opts,
) -> i32 {
unsafe { bpf::bpf_map_update_batch(fd, key, val, count, opts) }
}
pub(crate) fn lookup_batch_prealloc<K, T>(
map_fd: i32,
batch_size: u32,
next_key: Option<u32>,
keys: &mut Vec<K>,
vals: &mut Vec<T>,
delete: bool,
) -> XDPResult<BatchResultInternal> {
let mut count = batch_size;
let mut nkey = 0u32;
reset_errno();
let bpf_func = if delete {
bpf::bpf_map_lookup_and_delete_batch
} else {
bpf::bpf_map_lookup_batch
};
let mut lookup = |fkey: *mut c_void| unsafe {
bpf_func(
map_fd,
fkey,
&mut nkey as *mut _ as *mut c_void,
keys.as_mut_ptr() as *mut c_void,
vals.as_mut_ptr() as *mut c_void,
&mut count,
&BATCH_OPTS,
)
};
let mut rc = match next_key {
Some(mut k) => lookup(&mut k as *mut _ as *mut c_void),
None => lookup(std::ptr::null_mut() as *mut c_void),
};
let e = get_errno();
if rc < 0 && (e == 28 || e == 2) {
rc = 0;
}
let next_key = match e {
2 => None,
_ => Some(nkey),
};
let ret = BatchResultInternal {
next_key: next_key,
num_items: count,
};
check_rc(rc, ret, "Error looking up batch of elements")
}
pub(crate) fn validate_map<K>(
xdp: &XDPLoadedObject,
map_name: &str,
) -> XDPResult<(i32, u32, u32, u32)> {
let name = utils::str_to_cstring(map_name)?;
let (map_fd, map, map_def) = unsafe {
let map_fd = bpf::bpf_object__find_map_fd_by_name(xdp.object, name.as_ptr());
let map = bpf::bpf_object__find_map_by_name(xdp.object, name.as_ptr());
let map_def = bpf::bpf_map__def(map);
(map_fd, map, map_def)
};
if map_fd < 0 || map.is_null() || map_def.is_null() {
fail!("Unable to find map with name '{}'", map_name);
}
let (ksize, vsize, mtype, max_entries) = unsafe {
(
(*map_def).key_size,
(*map_def).value_size,
(*map_def).type_,
(*map_def).max_entries,
)
};
let req_key_size = size_of::<K>() as u32;
if req_key_size != ksize {
fail!(
"Incorrect key size, XDP map has size: {}, requested key size is {}.",
ksize,
req_key_size,
);
}
Ok((map_fd, vsize, mtype, max_entries))
}