use alloc::vec::Vec;
use core::fmt::Debug;
use super::{BpfMapCommonOps, BpfMapMeta, BpfMapUpdateElemFlags};
use crate::{BpfError, BpfResult as Result};
type BpfQueueValue = Vec<u8>;
pub trait SpecialMap: Debug + Send + Sync + 'static {
fn push(&mut self, value: BpfQueueValue, flags: BpfMapUpdateElemFlags) -> Result<()>;
fn pop(&mut self) -> Option<BpfQueueValue>;
fn peek(&self) -> Option<&BpfQueueValue>;
fn value_size(&self) -> usize;
fn mem_usage(&self) -> Result<usize>;
}
#[derive(Debug)]
pub struct QueueMap {
max_entries: u32,
value_size: u32,
data: Vec<BpfQueueValue>,
}
impl QueueMap {
pub fn new(map_meta: &BpfMapMeta) -> Result<Self> {
if map_meta.value_size == 0 || map_meta.max_entries == 0 || map_meta.key_size != 0 {
return Err(BpfError::EINVAL);
}
let data = Vec::with_capacity(map_meta.max_entries as usize);
Ok(Self {
max_entries: map_meta.max_entries,
value_size: map_meta.value_size,
data,
})
}
}
impl SpecialMap for QueueMap {
fn push(&mut self, value: BpfQueueValue, flags: BpfMapUpdateElemFlags) -> Result<()> {
if flags != BpfMapUpdateElemFlags::empty() {
return Err(BpfError::EINVAL);
}
if self.data.len() == self.max_entries as usize {
self.data.remove(0);
}
self.data.push(value);
Ok(())
}
fn pop(&mut self) -> Option<BpfQueueValue> {
if self.data.is_empty() {
return None;
}
Some(self.data.remove(0))
}
fn peek(&self) -> Option<&BpfQueueValue> {
self.data.first()
}
fn value_size(&self) -> usize {
self.value_size as usize
}
fn mem_usage(&self) -> Result<usize> {
let mut total = 0;
for v in &self.data {
total += v.len();
}
Ok(total)
}
}
#[derive(Debug)]
pub struct StackMap(QueueMap);
impl StackMap {
pub fn new(map_meta: &BpfMapMeta) -> Result<Self> {
QueueMap::new(map_meta).map(StackMap)
}
}
impl SpecialMap for StackMap {
fn push(&mut self, value: BpfQueueValue, flags: BpfMapUpdateElemFlags) -> Result<()> {
if self.0.data.len() == self.0.max_entries as usize {
if flags.contains(BpfMapUpdateElemFlags::BPF_EXISTS) {
self.0.data.pop();
} else {
return Err(BpfError::ENOMEM);
}
}
self.0.data.push(value);
Ok(())
}
fn pop(&mut self) -> Option<BpfQueueValue> {
self.0.data.pop()
}
fn peek(&self) -> Option<&BpfQueueValue> {
self.0.data.last()
}
fn value_size(&self) -> usize {
self.0.value_size()
}
fn mem_usage(&self) -> Result<usize> {
self.0.mem_usage()
}
}
impl<T: SpecialMap> BpfMapCommonOps for T {
fn lookup_elem(&mut self, key: &[u8]) -> Result<Option<&[u8]>> {
if !key.is_empty() {
return Err(BpfError::EINVAL);
}
Ok(self.peek().map(|v| v.as_slice()))
}
fn update_elem(&mut self, key: &[u8], value: &[u8], flags: u64) -> Result<()> {
if !key.is_empty() || value.len() != self.value_size() {
return Err(BpfError::EINVAL);
}
let flag = BpfMapUpdateElemFlags::from_bits(flags).ok_or(BpfError::EINVAL)?;
if flag.contains(BpfMapUpdateElemFlags::BPF_F_LOCK)
|| (flag.contains(BpfMapUpdateElemFlags::BPF_NOEXIST)
&& flag.contains(BpfMapUpdateElemFlags::BPF_EXISTS))
{
return Err(BpfError::EINVAL);
}
self.push(value.to_vec(), flag)
}
fn lookup_and_delete_elem(&mut self, key: &[u8], value: &mut [u8]) -> Result<()> {
if !key.is_empty() || value.len() != self.value_size() {
return Err(BpfError::EINVAL);
}
if let Some(v) = self.pop() {
value.copy_from_slice(&v);
Ok(())
} else {
Err(BpfError::ENOENT)
}
}
fn push_elem(&mut self, value: &[u8], flags: u64) -> Result<()> {
self.update_elem(&[], value, flags)
}
fn pop_elem(&mut self, value: &mut [u8]) -> Result<()> {
self.lookup_and_delete_elem(&[], value)
}
fn peek_elem(&self, value: &mut [u8]) -> Result<()> {
if value.len() != self.value_size() {
return Err(BpfError::EINVAL);
}
self.peek()
.map(|v| value.copy_from_slice(v))
.ok_or(BpfError::ENOENT)
}
fn map_mem_usage(&self) -> Result<usize> {
self.mem_usage()
}
fn as_any(&self) -> &dyn core::any::Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn core::any::Any {
self
}
}
#[cfg(test)]
mod tests {
use super::QueueMap;
use crate::{
BpfError,
map::{BpfMapCommonOps, BpfMapMeta},
};
#[test]
fn test_queue_validation() {
let mut meta = BpfMapMeta::default();
meta.key_size = 0;
meta.value_size = 4;
meta.max_entries = 2;
let mut queue = QueueMap::new(&meta).unwrap();
assert_eq!(queue.update_elem(b"x", b"abcd", 0), Err(BpfError::EINVAL));
assert_eq!(queue.update_elem(&[], b"abc", 0), Err(BpfError::EINVAL));
assert_eq!(queue.update_elem(&[], b"abcd", 8), Err(BpfError::EINVAL));
assert_eq!(queue.update_elem(&[], b"abcd", 0), Ok(()));
let mut value = [0; 3];
assert_eq!(queue.peek_elem(&mut value), Err(BpfError::EINVAL));
}
}