diff options
Diffstat (limited to 'rustables/src/expr/cmp.rs')
-rw-r--r-- | rustables/src/expr/cmp.rs | 230 |
1 files changed, 230 insertions, 0 deletions
diff --git a/rustables/src/expr/cmp.rs b/rustables/src/expr/cmp.rs new file mode 100644 index 0000000..f22a3ff --- /dev/null +++ b/rustables/src/expr/cmp.rs @@ -0,0 +1,230 @@ +use super::{Expression, Rule}; +use rustables_sys::{self as sys, libc}; +use std::{ + borrow::Cow, + ffi::{c_void, CString}, + net::{IpAddr, Ipv4Addr, Ipv6Addr}, + os::raw::c_char, + slice, +}; +use tracing::trace; + +/// Comparison operator. +#[derive(Copy, Clone, Eq, PartialEq)] +pub enum CmpOp { + /// Equals. + Eq, + /// Not equal. + Neq, + /// Less than. + Lt, + /// Less than, or equal. + Lte, + /// Greater than. + Gt, + /// Greater than, or equal. + Gte, +} + +impl CmpOp { + /// Returns the corresponding `NFT_*` constant for this comparison operation. + 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, + } + } +} + +/// Comparator expression. Allows comparing the content of the netfilter register with any value. +pub struct Cmp<T: ToSlice> { + op: CmpOp, + data: T, +} + +impl<T: ToSlice> Cmp<T> { + /// Returns a new comparison expression comparing the value loaded in the register with the + /// data in `data` using the comparison operator `op`. + pub fn new(op: CmpOp, data: T) -> Self { + Cmp { op, data } + } +} + +impl<T: ToSlice> Expression for Cmp<T> { + fn to_expr(&self, _rule: &Rule) -> *mut sys::nftnl_expr { + unsafe { + let expr = try_alloc!(sys::nftnl_expr_alloc(b"cmp\0" as *const _ as *const c_char)); + + let data = self.data.to_slice(); + trace!("Creating a cmp expr comparing with data {:?}", data); + + sys::nftnl_expr_set_u32( + expr, + sys::NFTNL_EXPR_CMP_SREG as u16, + libc::NFT_REG_1 as u32, + ); + sys::nftnl_expr_set_u32(expr, sys::NFTNL_EXPR_CMP_OP as u16, self.op.to_raw()); + sys::nftnl_expr_set( + expr, + 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) + }; +} + +/// A type that can be converted into a byte buffer. +pub trait ToSlice { + /// Returns the data this type represents. + fn to_slice(&self) -> Cow<'_, [u8]>; +} + +impl<'a> ToSlice for [u8; 0] { + fn to_slice(&self) -> Cow<'_, [u8]> { + Cow::Borrowed(&[]) + } +} + +impl<'a> ToSlice for &'a [u8] { + fn to_slice(&self) -> Cow<'_, [u8]> { + Cow::Borrowed(self) + } +} + +impl<'a> ToSlice for &'a [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::Owned(vec![*self]) + } +} + +impl ToSlice for u16 { + fn to_slice(&self) -> Cow<'_, [u8]> { + let b0 = (*self & 0x00ff) as u8; + let b1 = (*self >> 8) as u8; + Cow::Owned(vec![b0, b1]) + } +} + +impl ToSlice for u32 { + fn to_slice(&self) -> Cow<'_, [u8]> { + let b0 = *self as u8; + let b1 = (*self >> 8) as u8; + let b2 = (*self >> 16) as u8; + let b3 = (*self >> 24) as u8; + Cow::Owned(vec![b0, b1, b2, b3]) + } +} + +impl ToSlice for i32 { + fn to_slice(&self) -> Cow<'_, [u8]> { + let b0 = *self as u8; + let b1 = (*self >> 8) as u8; + let b2 = (*self >> 16) as u8; + let b3 = (*self >> 24) as u8; + Cow::Owned(vec![b0, b1, b2, b3]) + } +} + +impl<'a> ToSlice for &'a str { + fn to_slice(&self) -> Cow<'_, [u8]> { + Cow::from(self.as_bytes()) + } +} + +/// Can be used to compare the value loaded by [`Meta::IifName`] and [`Meta::OifName`]. Please +/// note that it is faster to check interface index than name. +/// +/// [`Meta::IifName`]: enum.Meta.html#variant.IifName +/// [`Meta::OifName`]: enum.Meta.html#variant.OifName +#[derive(Debug, Clone, Eq, PartialEq, Hash)] +pub enum InterfaceName { + /// Interface name must be exactly the value of the `CString`. + Exact(CString), + /// Interface name must start with the value of the `CString`. + /// + /// `InterfaceName::StartingWith("eth")` will look like `eth*` when printed and match against + /// `eth0`, `eth1`, ..., `eth99` and so on. + StartingWith(CString), +} + +impl ToSlice for InterfaceName { + fn to_slice(&self) -> Cow<'_, [u8]> { + let bytes = match *self { + InterfaceName::Exact(ref name) => name.as_bytes_with_nul(), + InterfaceName::StartingWith(ref name) => name.as_bytes(), + }; + Cow::from(bytes) + } +} + +impl<'a> ToSlice for &'a InterfaceName { + fn to_slice(&self) -> Cow<'_, [u8]> { + let bytes = match *self { + InterfaceName::Exact(ref name) => name.as_bytes_with_nul(), + InterfaceName::StartingWith(ref name) => name.as_bytes(), + }; + Cow::from(bytes) + } +} |