use super::{Expression, Rule};
use nftnl_sys::{self as sys, libc};
use std::{
borrow::Cow,
ffi::{CStr, CString, c_void},
net::{IpAddr, Ipv4Addr, Ipv6Addr},
ptr, slice,
};
#[derive(Copy, Clone, Eq, PartialEq)]
pub enum CmpOp {
Eq,
Neq,
Lt,
Lte,
Gt,
Gte,
}
impl CmpOp {
pub fn to_raw(self) -> u32 {
use self::CmpOp::*;
match self {
Eq => libc::NFT_CMP_EQ as u32,
Neq => libc::NFT_CMP_NEQ as u32,
Lt => libc::NFT_CMP_LT as u32,
Lte => libc::NFT_CMP_LTE as u32,
Gt => libc::NFT_CMP_GT as u32,
Gte => libc::NFT_CMP_GTE as u32,
}
}
}
pub struct Cmp<T: ToSlice> {
op: CmpOp,
data: T,
}
impl<T: ToSlice> Cmp<T> {
pub fn new(op: CmpOp, data: T) -> Self {
Cmp { op, data }
}
}
impl<T: ToSlice> Expression for Cmp<T> {
fn to_expr(&self, _rule: &Rule) -> ptr::NonNull<sys::nftnl_expr> {
let expr = try_alloc!(unsafe { sys::nftnl_expr_alloc(c"cmp".as_ptr()) });
let data = self.data.to_slice();
trace!("Creating a cmp expr comparing with data {data:?}");
unsafe {
sys::nftnl_expr_set_u32(
expr.as_ptr(),
sys::NFTNL_EXPR_CMP_SREG as u16,
libc::NFT_REG_1 as u32,
);
sys::nftnl_expr_set_u32(
expr.as_ptr(),
sys::NFTNL_EXPR_CMP_OP as u16,
self.op.to_raw(),
);
sys::nftnl_expr_set(
expr.as_ptr(),
sys::NFTNL_EXPR_CMP_DATA as u16,
data.as_ref() as *const _ as *const c_void,
data.len() as u32,
);
}
expr
}
}
#[macro_export(local_inner_macros)]
macro_rules! nft_expr_cmp {
(@cmp_op ==) => {
$crate::expr::CmpOp::Eq
};
(@cmp_op !=) => {
$crate::expr::CmpOp::Neq
};
(@cmp_op <) => {
$crate::expr::CmpOp::Lt
};
(@cmp_op <=) => {
$crate::expr::CmpOp::Lte
};
(@cmp_op >) => {
$crate::expr::CmpOp::Gt
};
(@cmp_op >=) => {
$crate::expr::CmpOp::Gte
};
($op:tt $data:expr) => {
$crate::expr::Cmp::new(nft_expr_cmp!(@cmp_op $op), $data)
};
}
pub trait ToSlice {
fn to_slice(&self) -> Cow<'_, [u8]>;
}
impl ToSlice for [u8; 0] {
fn to_slice(&self) -> Cow<'_, [u8]> {
Cow::Borrowed(&[])
}
}
impl ToSlice for &'_ [u8] {
fn to_slice(&self) -> Cow<'_, [u8]> {
Cow::Borrowed(self)
}
}
impl ToSlice for &'_ [u16] {
fn to_slice(&self) -> Cow<'_, [u8]> {
let ptr = self.as_ptr() as *const u8;
let len = self.len() * 2;
Cow::Borrowed(unsafe { slice::from_raw_parts(ptr, len) })
}
}
impl ToSlice for IpAddr {
fn to_slice(&self) -> Cow<'_, [u8]> {
match *self {
IpAddr::V4(ref addr) => addr.to_slice(),
IpAddr::V6(ref addr) => addr.to_slice(),
}
}
}
impl ToSlice for Ipv4Addr {
fn to_slice(&self) -> Cow<'_, [u8]> {
Cow::Owned(self.octets().to_vec())
}
}
impl ToSlice for Ipv6Addr {
fn to_slice(&self) -> Cow<'_, [u8]> {
Cow::Owned(self.octets().to_vec())
}
}
impl ToSlice for u8 {
fn to_slice(&self) -> Cow<'_, [u8]> {
Cow::Borrowed(slice::from_ref(self))
}
}
impl ToSlice for u16 {
fn to_slice(&self) -> Cow<'_, [u8]> {
Cow::Owned(self.to_ne_bytes().to_vec())
}
}
impl ToSlice for u32 {
fn to_slice(&self) -> Cow<'_, [u8]> {
Cow::Owned(self.to_ne_bytes().to_vec())
}
}
impl ToSlice for u64 {
fn to_slice(&self) -> Cow<'_, [u8]> {
Cow::Owned(self.to_ne_bytes().to_vec())
}
}
impl ToSlice for i32 {
fn to_slice(&self) -> Cow<'_, [u8]> {
Cow::Owned(self.to_ne_bytes().to_vec())
}
}
impl ToSlice for &'_ CStr {
fn to_slice(&self) -> Cow<'_, [u8]> {
Cow::from(self.to_bytes_with_nul())
}
}
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub enum InterfaceName {
Exact(CString),
StartingWith(CString),
}
impl ToSlice for InterfaceName {
fn to_slice(&self) -> Cow<'_, [u8]> {
let bytes = match self {
InterfaceName::Exact(name) => name.as_bytes_with_nul(),
InterfaceName::StartingWith(name) => name.as_bytes(),
};
Cow::from(bytes)
}
}
impl ToSlice for &'_ InterfaceName {
fn to_slice(&self) -> Cow<'_, [u8]> {
let bytes = match *self {
InterfaceName::Exact(name) => name.as_bytes_with_nul(),
InterfaceName::StartingWith(name) => name.as_bytes(),
};
Cow::from(bytes)
}
}