diff options
author | Simon THOBY <git@nightmared.fr> | 2021-10-22 22:56:45 +0200 |
---|---|---|
committer | Simon THOBY <git@nightmared.fr> | 2021-11-02 22:18:09 +0100 |
commit | 867e2df78a0e92cab93f647c3eeccabca5b869a2 (patch) | |
tree | 0f747929b6a5525ba76df37fd97364c71cec039c | |
parent | d63381b5a1cc7cba91187eff7f87eeab35d4bc86 (diff) |
Add a (memory unsafe) manner to unserialize a few other expressions
-rw-r--r-- | rustables/src/batch.rs | 1 | ||||
-rw-r--r-- | rustables/src/chain.rs | 3 | ||||
-rw-r--r-- | rustables/src/expr/cmp.rs | 49 | ||||
-rw-r--r-- | rustables/src/expr/ct.rs | 18 | ||||
-rw-r--r-- | rustables/src/expr/immediate.rs | 38 | ||||
-rw-r--r-- | rustables/src/expr/log.rs | 28 | ||||
-rw-r--r-- | rustables/src/expr/lookup.rs | 21 | ||||
-rw-r--r-- | rustables/src/expr/masquerade.rs | 8 | ||||
-rw-r--r-- | rustables/src/expr/mod.rs | 12 | ||||
-rw-r--r-- | rustables/src/expr/nat.rs | 65 |
10 files changed, 236 insertions, 7 deletions
diff --git a/rustables/src/batch.rs b/rustables/src/batch.rs index c4cd6cb..3cdd52b 100644 --- a/rustables/src/batch.rs +++ b/rustables/src/batch.rs @@ -10,6 +10,7 @@ use thiserror::Error; #[error("Error while communicating with netlink")] pub struct NetlinkError(()); +#[cfg(feature = "query")] /// Check if the kernel supports batched netlink messages to netfilter. pub fn batch_is_supported() -> std::result::Result<bool, NetlinkError> { match unsafe { sys::nftnl_batch_is_supported() } { diff --git a/rustables/src/chain.rs b/rustables/src/chain.rs index a616f90..3e28ab0 100644 --- a/rustables/src/chain.rs +++ b/rustables/src/chain.rs @@ -1,7 +1,8 @@ use crate::{MsgType, Table}; use rustables_sys::{self as sys, libc}; +#[cfg(feature = "query")] +use std::convert::TryFrom; use std::{ - convert::TryFrom, ffi::{c_void, CStr, CString}, fmt, os::raw::c_char, diff --git a/rustables/src/expr/cmp.rs b/rustables/src/expr/cmp.rs index b14aa1d..e4cfb0f 100644 --- a/rustables/src/expr/cmp.rs +++ b/rustables/src/expr/cmp.rs @@ -9,7 +9,7 @@ use std::{ }; /// Comparison operator. -#[derive(Copy, Clone, Eq, PartialEq)] +#[derive(Debug, Copy, Clone, Eq, PartialEq)] pub enum CmpOp { /// Equals. Eq, @@ -38,9 +38,23 @@ impl CmpOp { Gte => libc::NFT_CMP_GTE as u32, } } + + pub fn from_raw(val: u32) -> Option<Self> { + use self::CmpOp::*; + match val as i32 { + libc::NFT_CMP_EQ => Some(Eq), + libc::NFT_CMP_NEQ => Some(Neq), + libc::NFT_CMP_LT => Some(Lt), + libc::NFT_CMP_LTE => Some(Lte), + libc::NFT_CMP_GT => Some(Gt), + libc::NFT_CMP_GTE => Some(Gte), + _ => None, + } + } } /// Comparator expression. Allows comparing the content of the netfilter register with any value. +#[derive(Debug, PartialEq)] pub struct Cmp<T: ToSlice> { op: CmpOp, data: T, @@ -54,11 +68,42 @@ impl<T: ToSlice> Cmp<T> { } } -impl<T: ToSlice> Expression for Cmp<T> { +impl<T: ToSlice + Copy> Expression for Cmp<T> { fn get_raw_name() -> *const c_char { b"cmp\0" as *const _ as *const c_char } + fn from_expr(expr: *const sys::nftnl_expr) -> Option<Self> + where + Self: Sized, + { + unsafe { + let ref_len = std::mem::size_of::<T>() as u32; + let mut data_len = 0; + let data = sys::nftnl_expr_get( + expr, + sys::NFTNL_EXPR_CMP_DATA as u16, + &mut data_len as *mut u32, + ); + + if data.is_null() { + return None; + } else if data_len != ref_len { + debug!("Invalid size requested for deserializing a 'cmp' expression: expected {} bytes, got {}", ref_len, data_len); + return None; + } + + // Warning: this is *very* dangerous safety wise if the user supply us with + // a type that have the same size as T but a different memory layout. + // Is there a better way? And if there isn't, shouldn't we gate this behind + // an "unsafe" boundary? + let data = *(data as *const T); + + let op = CmpOp::from_raw(sys::nftnl_expr_get_u32(expr, sys::NFTNL_EXPR_CMP_OP as u16)); + op.map(|op| Cmp { op, data }) + } + } + fn to_expr(&self, _rule: &Rule) -> *mut sys::nftnl_expr { unsafe { let expr = try_alloc!(sys::nftnl_expr_alloc(Self::get_raw_name())); diff --git a/rustables/src/expr/ct.rs b/rustables/src/expr/ct.rs index 1f15858..9d58591 100644 --- a/rustables/src/expr/ct.rs +++ b/rustables/src/expr/ct.rs @@ -31,6 +31,24 @@ impl Expression for Conntrack { b"ct\0" as *const _ as *const c_char } + fn from_expr(expr: *const sys::nftnl_expr) -> Option<Self> + where + Self: Sized, + { + unsafe { + let ct_key = sys::nftnl_expr_get_u32(expr, sys::NFTNL_EXPR_CT_KEY as u16); + let ct_sreg_is_set = sys::nftnl_expr_is_set(expr, sys::NFTNL_EXPR_CT_SREG as u16); + + match ct_key as i32 { + libc::NFT_CT_STATE => Some(Conntrack::State), + libc::NFT_CT_MARK => Some(Conntrack::Mark { + set: ct_sreg_is_set, + }), + _ => None, + } + } + } + fn to_expr(&self, _rule: &Rule) -> *mut sys::nftnl_expr { unsafe { let expr = try_alloc!(sys::nftnl_expr_alloc(Self::get_raw_name())); diff --git a/rustables/src/expr/immediate.rs b/rustables/src/expr/immediate.rs index 15eb452..e8823b1 100644 --- a/rustables/src/expr/immediate.rs +++ b/rustables/src/expr/immediate.rs @@ -18,11 +18,47 @@ impl<T> Immediate<T> { } } -impl<T> Expression for Immediate<T> { +// The Copy requirement is present to allow us to dereference the newly created raw pointer in `from_expr` +impl<T: Copy> Expression for Immediate<T> { fn get_raw_name() -> *const c_char { b"immediate\0" as *const _ as *const c_char } + fn from_expr(expr: *const sys::nftnl_expr) -> Option<Self> + where + Self: Sized, + { + unsafe { + let ref_len = std::mem::size_of::<T>() as u32; + let mut data_len = 0; + let data = sys::nftnl_expr_get( + expr, + sys::NFTNL_EXPR_IMM_DATA as u16, + &mut data_len as *mut u32, + ); + + if data.is_null() { + return None; + } else if data_len != ref_len { + debug!("Invalid size requested for deserializing an 'immediate' expression: expected {} bytes, got {}", ref_len, data_len); + return None; + } + + // Warning: this is *very* dangerous safety wise if the user supply us with + // a type that have the same size as T but a different memory layout. + // Is there a better way? And if there isn't, shouldn't we gate this behind + // an "unsafe" boundary? + let data = *(data as *const T); + + let register = Register::from_raw(sys::nftnl_expr_get_u32( + expr, + sys::NFTNL_EXPR_IMM_DREG as u16, + )); + + register.map(|register| Immediate { data, register }) + } + } + fn to_expr(&self, _rule: &Rule) -> *mut sys::nftnl_expr { unsafe { let expr = try_alloc!(sys::nftnl_expr_alloc(Self::get_raw_name())); diff --git a/rustables/src/expr/log.rs b/rustables/src/expr/log.rs index 8f52686..ba1244a 100644 --- a/rustables/src/expr/log.rs +++ b/rustables/src/expr/log.rs @@ -1,10 +1,11 @@ use super::{Expression, Rule}; use rustables_sys as sys; -use std::ffi::CString; +use std::ffi::{CStr, CString}; use std::os::raw::c_char; use thiserror::Error; /// A Log expression will log all packets that match the rule. +#[derive(Debug, PartialEq)] pub struct Log { pub group: Option<LogGroup>, pub prefix: Option<LogPrefix>, @@ -15,6 +16,31 @@ impl Expression for Log { b"log\0" as *const _ as *const c_char } + fn from_expr(expr: *const sys::nftnl_expr) -> Option<Self> + where + Self: Sized, + { + unsafe { + let mut group = None; + if sys::nftnl_expr_is_set(expr, sys::NFTNL_EXPR_LOG_GROUP as u16) { + group = Some(LogGroup(sys::nftnl_expr_get_u32( + expr, + sys::NFTNL_EXPR_LOG_GROUP as u16, + ) as u16)); + } + let mut prefix = None; + if sys::nftnl_expr_is_set(expr, sys::NFTNL_EXPR_LOG_PREFIX as u16) { + let raw_prefix = sys::nftnl_expr_get_str(expr, sys::NFTNL_EXPR_LOG_PREFIX as u16); + if raw_prefix.is_null() { + trace!("Unexpected empty prefix in a 'log' expression"); + } else { + prefix = Some(LogPrefix(CStr::from_ptr(raw_prefix).to_owned())); + } + } + Some(Log { group, prefix }) + } + } + fn to_expr(&self, _rule: &Rule) -> *mut sys::nftnl_expr { unsafe { let expr = try_alloc!(sys::nftnl_expr_alloc(b"log\0" as *const _ as *const c_char)); diff --git a/rustables/src/expr/lookup.rs b/rustables/src/expr/lookup.rs index d9acbe6..b9a03d9 100644 --- a/rustables/src/expr/lookup.rs +++ b/rustables/src/expr/lookup.rs @@ -1,9 +1,10 @@ use super::{Expression, Rule}; use crate::set::Set; use rustables_sys::{self as sys, libc}; -use std::ffi::CString; +use std::ffi::{CStr, CString}; use std::os::raw::c_char; +#[derive(Debug, PartialEq)] pub struct Lookup { set_name: CString, set_id: u32, @@ -25,6 +26,24 @@ impl Expression for Lookup { b"lookup\0" as *const _ as *const c_char } + fn from_expr(expr: *const sys::nftnl_expr) -> Option<Self> + where + Self: Sized, + { + unsafe { + let set_name = sys::nftnl_expr_get_str(expr, sys::NFTNL_EXPR_LOOKUP_SET as u16); + let set_id = sys::nftnl_expr_get_u32(expr, sys::NFTNL_EXPR_LOOKUP_SET_ID as u16); + + if set_name.is_null() { + return None; + } + + let set_name = CStr::from_ptr(set_name).to_owned(); + + Some(Lookup { set_id, set_name }) + } + } + fn to_expr(&self, _rule: &Rule) -> *mut sys::nftnl_expr { unsafe { let expr = try_alloc!(sys::nftnl_expr_alloc(Self::get_raw_name())); diff --git a/rustables/src/expr/masquerade.rs b/rustables/src/expr/masquerade.rs index 31b98c0..bf4e0de 100644 --- a/rustables/src/expr/masquerade.rs +++ b/rustables/src/expr/masquerade.rs @@ -3,6 +3,7 @@ use rustables_sys as sys; use std::os::raw::c_char; /// Sets the source IP to that of the output interface. +#[derive(Debug, PartialEq)] pub struct Masquerade; impl Expression for Masquerade { @@ -10,6 +11,13 @@ impl Expression for Masquerade { b"masq\0" as *const _ as *const c_char } + fn from_expr(_expr: *const sys::nftnl_expr) -> Option<Self> + where + Self: Sized, + { + Some(Masquerade) + } + fn to_expr(&self, _rule: &Rule) -> *mut sys::nftnl_expr { try_alloc!(unsafe { sys::nftnl_expr_alloc(Self::get_raw_name()) }) } diff --git a/rustables/src/expr/mod.rs b/rustables/src/expr/mod.rs index 39ab2e0..f2a4d5e 100644 --- a/rustables/src/expr/mod.rs +++ b/rustables/src/expr/mod.rs @@ -89,6 +89,7 @@ pub trait Expression { #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] #[repr(i32)] pub enum Register { + Verdict = libc::NFT_REG_VERDICT, Reg1 = libc::NFT_REG_1, Reg2 = libc::NFT_REG_2, Reg3 = libc::NFT_REG_3, @@ -99,6 +100,17 @@ impl Register { pub fn to_raw(self) -> u32 { self as u32 } + + pub fn from_raw(val: u32) -> Option<Self> { + match val as i32 { + libc::NFT_REG_VERDICT => Some(Self::Verdict), + libc::NFT_REG_1 => Some(Self::Reg1), + libc::NFT_REG_2 => Some(Self::Reg2), + libc::NFT_REG_3 => Some(Self::Reg3), + libc::NFT_REG_4 => Some(Self::Reg4), + _ => None, + } + } } mod bitwise; diff --git a/rustables/src/expr/nat.rs b/rustables/src/expr/nat.rs index 0970134..6bd0619 100644 --- a/rustables/src/expr/nat.rs +++ b/rustables/src/expr/nat.rs @@ -1,7 +1,7 @@ use super::{Expression, Register, Rule}; use crate::ProtoFamily; use rustables_sys::{self as sys, libc}; -use std::os::raw::c_char; +use std::{convert::TryFrom, os::raw::c_char}; #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] #[repr(i32)] @@ -12,8 +12,19 @@ pub enum NatType { DNat = libc::NFT_NAT_DNAT, } +impl NatType { + fn from_raw(val: u32) -> Option<Self> { + match val as i32 { + libc::NFT_NAT_SNAT => Some(NatType::SNat), + libc::NFT_NAT_DNAT => Some(NatType::DNat), + _ => None, + } + } +} + /// A source or destination NAT statement. Modifies the source or destination address /// (and possibly port) of packets. +#[derive(Debug, PartialEq)] pub struct Nat { pub nat_type: NatType, pub family: ProtoFamily, @@ -26,6 +37,58 @@ impl Expression for Nat { b"nat\0" as *const _ as *const c_char } + fn from_expr(expr: *const sys::nftnl_expr) -> Option<Self> + where + Self: Sized, + { + unsafe { + let nat_type = NatType::from_raw(sys::nftnl_expr_get_u32( + expr, + sys::NFTNL_EXPR_NAT_TYPE as u16, + )); + let nat_type = match nat_type { + Some(x) => x, + None => return None, + }; + + let family = ProtoFamily::try_from(sys::nftnl_expr_get_u32( + expr, + sys::NFTNL_EXPR_NAT_FAMILY as u16, + ) as i32); + let family = match family { + Ok(x) => x, + Err(_) => return None, + }; + + let ip_register = Register::from_raw(sys::nftnl_expr_get_u32( + expr, + sys::NFTNL_EXPR_NAT_REG_ADDR_MIN as u16, + )); + let ip_register = match ip_register { + Some(x) => x, + None => return None, + }; + + let mut port_register = None; + if sys::nftnl_expr_is_set(expr, sys::NFTNL_EXPR_NAT_REG_PROTO_MIN as u16) { + port_register = Register::from_raw(sys::nftnl_expr_get_u32( + expr, + sys::NFTNL_EXPR_NAT_REG_PROTO_MIN as u16, + )); + if port_register.is_none() { + trace!("Invalid register in expression 'nat'"); + } + } + + Some(Nat { + ip_register, + nat_type, + family, + port_register, + }) + } + } + fn to_expr(&self, _rule: &Rule) -> *mut sys::nftnl_expr { let expr = try_alloc!(unsafe { sys::nftnl_expr_alloc(Self::get_raw_name()) }); |