aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimon THOBY <git@nightmared.fr>2021-10-22 22:56:45 +0200
committerSimon THOBY <git@nightmared.fr>2021-11-02 22:18:09 +0100
commit867e2df78a0e92cab93f647c3eeccabca5b869a2 (patch)
tree0f747929b6a5525ba76df37fd97364c71cec039c
parentd63381b5a1cc7cba91187eff7f87eeab35d4bc86 (diff)
Add a (memory unsafe) manner to unserialize a few other expressions
-rw-r--r--rustables/src/batch.rs1
-rw-r--r--rustables/src/chain.rs3
-rw-r--r--rustables/src/expr/cmp.rs49
-rw-r--r--rustables/src/expr/ct.rs18
-rw-r--r--rustables/src/expr/immediate.rs38
-rw-r--r--rustables/src/expr/log.rs28
-rw-r--r--rustables/src/expr/lookup.rs21
-rw-r--r--rustables/src/expr/masquerade.rs8
-rw-r--r--rustables/src/expr/mod.rs12
-rw-r--r--rustables/src/expr/nat.rs65
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()) });