mod builder;
pub use builder::RuleBuilder;
use std::ffi::CString;
use bytes::BufMut;
use libc::{
NF_ACCEPT, NF_DROP, NF_QUEUE, NFT_BREAK, NFT_CMP_EQ, NFT_CMP_GT, NFT_CMP_GTE, NFT_CMP_LT,
NFT_CMP_LTE, NFT_CMP_NEQ, NFT_CONTINUE, NFT_GOTO, NFT_JUMP, NFT_META_BRI_IIFNAME,
NFT_META_BRI_OIFNAME, NFT_META_CGROUP, NFT_META_CPU, NFT_META_IIF, NFT_META_IIFGROUP,
NFT_META_IIFNAME, NFT_META_IIFTYPE, NFT_META_L4PROTO, NFT_META_LEN, NFT_META_MARK,
NFT_META_NFPROTO, NFT_META_OIF, NFT_META_OIFGROUP, NFT_META_OIFNAME, NFT_META_OIFTYPE,
NFT_META_PKTTYPE, NFT_META_PRANDOM, NFT_META_PRIORITY, NFT_META_PROTOCOL, NFT_META_RTCLASSID,
NFT_META_SECMARK, NFT_META_SKGID, NFT_META_SKUID, NFT_PAYLOAD_LL_HEADER,
NFT_PAYLOAD_NETWORK_HEADER, NFT_PAYLOAD_TRANSPORT_HEADER, NFT_REG_1, NFT_REG_2, NFT_REG_3,
NFT_REG_4, NFT_REG_VERDICT, NFT_RETURN,
};
use crate::constants::*;
use crate::{Encode, write_attribute};
#[derive(Clone, Debug)]
#[cfg_attr(test, derive(PartialEq))]
#[non_exhaustive]
pub enum Expr {
Meta(Meta),
Cmp(Cmp),
Bitwise(Bitwise),
Payload(Payload),
Verdict(Verdict),
}
impl Encode for Expr {
fn encode<B>(&self, buf: B)
where
B: BufMut,
{
match self {
Self::Meta(expr) => expr.encode(buf),
Self::Cmp(expr) => expr.encode(buf),
Self::Bitwise(expr) => expr.encode(buf),
Self::Payload(expr) => expr.encode(buf),
Self::Verdict(expr) => expr.encode(buf),
}
}
}
#[derive(Clone, Debug)]
#[cfg_attr(test, derive(PartialEq))]
pub struct Meta {
pub key: MetaProperty,
pub register: Register,
pub src_register: bool,
}
impl Encode for Meta {
fn encode<B>(&self, mut buf: B)
where
B: BufMut,
{
let mut expr = Vec::new();
write_attribute(&mut expr, NFTA_META_KEY, &(self.key as u32).to_be_bytes());
if self.src_register {
write_attribute(
&mut expr,
NFTA_META_SREG,
&(self.register as u32).to_be_bytes(),
);
} else {
write_attribute(
&mut expr,
NFTA_META_DREG,
&(self.register as u32).to_be_bytes(),
);
}
write_attribute(&mut buf, NFTA_EXPR_NAME, c"meta".to_bytes_with_nul());
write_attribute(&mut buf, NFTA_EXPR_DATA | NLA_F_NESTED, &expr);
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
#[repr(i32)]
#[non_exhaustive]
pub enum MetaProperty {
Len = NFT_META_LEN,
Protocol = NFT_META_PROTOCOL,
Priority = NFT_META_PRIORITY,
Mask = NFT_META_MARK,
Iif = NFT_META_IIF,
Oif = NFT_META_OIF,
IIfName = NFT_META_IIFNAME,
OifName = NFT_META_OIFNAME,
IIfType = NFT_META_IIFTYPE,
OifType = NFT_META_OIFTYPE,
SkUid = NFT_META_SKUID,
SkGid = NFT_META_SKGID,
RtClassId = NFT_META_RTCLASSID,
SecMark = NFT_META_SECMARK,
NfProto = NFT_META_NFPROTO,
L4Proto = NFT_META_L4PROTO,
BriIfName = NFT_META_BRI_IIFNAME,
BrOifName = NFT_META_BRI_OIFNAME,
PktType = NFT_META_PKTTYPE,
Cpu = NFT_META_CPU,
IIfGroup = NFT_META_IIFGROUP,
OifGroup = NFT_META_OIFGROUP,
Cgroup = NFT_META_CGROUP,
PRandom = NFT_META_PRANDOM,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
#[repr(i32)]
#[non_exhaustive]
pub enum Register {
Reg1 = NFT_REG_1,
Reg2 = NFT_REG_2,
Reg3 = NFT_REG_3,
Reg4 = NFT_REG_4,
}
#[derive(Clone, Debug)]
#[cfg_attr(test, derive(PartialEq))]
pub struct Cmp {
pub op: CmpOp,
pub register: Register,
pub data: Vec<u8>,
}
impl Encode for Cmp {
fn encode<B>(&self, mut buf: B)
where
B: BufMut,
{
let mut data = Vec::new();
write_attribute(&mut data, NFTA_DATA_VALUE, &self.data);
let mut expr = Vec::new();
write_attribute(
&mut expr,
NFTA_CMP_SREG,
&(self.register as u32).to_be_bytes(),
);
write_attribute(&mut expr, NFTA_CMP_OP, &(self.op as u32).to_be_bytes());
write_attribute(&mut expr, NFTA_CMP_DATA | NLA_F_NESTED, &data);
write_attribute(&mut buf, NFTA_EXPR_NAME, c"cmp".to_bytes_with_nul());
write_attribute(&mut buf, NFTA_EXPR_DATA | NLA_F_NESTED, &expr);
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
#[repr(i32)]
#[non_exhaustive]
pub enum CmpOp {
Equal = NFT_CMP_EQ,
NotEqual = NFT_CMP_NEQ,
LessThan = NFT_CMP_LT,
LessThanOrEqual = NFT_CMP_LTE,
GreaterThan = NFT_CMP_GT,
GreaterThanOrEqual = NFT_CMP_GTE,
}
#[derive(Clone, Debug)]
#[cfg_attr(test, derive(PartialEq))]
#[non_exhaustive]
pub enum Verdict {
Accept,
Drop,
Queue,
Continue,
Break,
Jump(CString),
Goto(CString),
Return,
}
impl Encode for Verdict {
fn encode<B>(&self, mut buf: B)
where
B: BufMut,
{
let mut verdict_data = Vec::new();
let code: i32 = match self {
Self::Accept => NF_ACCEPT,
Self::Drop => NF_DROP,
Self::Queue => NF_QUEUE,
Self::Continue => NFT_CONTINUE,
Self::Break => NFT_BREAK,
Self::Jump(_) => NFT_JUMP,
Self::Goto(_) => NFT_GOTO,
Self::Return => NFT_RETURN,
};
write_attribute(&mut verdict_data, NFTA_VERDICT_CODE, &code.to_be_bytes());
if let Verdict::Jump(chain) | Verdict::Goto(chain) = self {
write_attribute(
&mut verdict_data,
NFTA_VERDICT_CHAIN,
chain.to_bytes_with_nul(),
);
}
let mut verdict = Vec::new();
write_attribute(
&mut verdict,
NFTA_DATA_VERDICT | NLA_F_NESTED,
&verdict_data,
);
let mut imm = Vec::new();
write_attribute(
&mut imm,
NFTA_IMMEDIATE_DREG,
&NFT_REG_VERDICT.to_be_bytes(),
);
write_attribute(&mut imm, NFTA_IMMEDIATE_DATA | NLA_F_NESTED, &verdict);
write_attribute(&mut buf, NFTA_EXPR_NAME, c"immediate".to_bytes_with_nul());
write_attribute(&mut buf, NFTA_EXPR_DATA | NLA_F_NESTED, &imm);
}
}
#[derive(Clone, Debug)]
#[cfg_attr(test, derive(PartialEq))]
pub struct Bitwise {
pub src_register: Register,
pub dst_register: Register,
pub len: u32,
pub mask: Vec<u8>,
pub xor: Vec<u8>,
}
impl Encode for Bitwise {
fn encode<B>(&self, mut buf: B)
where
B: BufMut,
{
let mut mask = Vec::new();
write_attribute(&mut mask, NFTA_DATA_VALUE, &self.mask);
let mut xor = Vec::new();
write_attribute(&mut xor, NFTA_DATA_VALUE, &self.xor);
let mut expr = Vec::new();
write_attribute(
&mut expr,
NFTA_BITWISE_SREG,
&(self.src_register as u32).to_be_bytes(),
);
write_attribute(
&mut expr,
NFTA_BITWISE_DREG,
&(self.dst_register as u32).to_be_bytes(),
);
write_attribute(&mut expr, NFTA_BITWISE_LEN, &self.len.to_be_bytes());
write_attribute(&mut expr, NFTA_BITWISE_MASK | NLA_F_NESTED, &mask);
write_attribute(&mut expr, NFTA_BITWISE_XOR | NLA_F_NESTED, &xor);
write_attribute(&mut buf, NFTA_EXPR_NAME, c"bitwise".to_bytes_with_nul());
write_attribute(&mut buf, NFTA_EXPR_DATA, &expr);
}
}
#[derive(Clone, Debug)]
#[cfg_attr(test, derive(PartialEq))]
pub struct Payload {
pub op: PayloadOp,
pub base: PayloadBase,
pub offset: u32,
pub len: u32,
}
impl Encode for Payload {
fn encode<B>(&self, mut buf: B)
where
B: BufMut,
{
let mut expr = Vec::new();
match self.op {
PayloadOp::Load(reg) => {
write_attribute(&mut expr, NFTA_PAYLOAD_DREG, &(reg as u32).to_be_bytes());
}
PayloadOp::Write(reg) => {
write_attribute(&mut expr, NFTA_PAYLOAD_SREG, &(reg as u32).to_be_bytes());
}
}
write_attribute(
&mut expr,
NFTA_PAYLOAD_BASE,
&(self.base as u32).to_be_bytes(),
);
write_attribute(&mut expr, NFTA_PAYLOAD_OFFSET, &self.offset.to_be_bytes());
write_attribute(&mut expr, NFTA_PAYLOAD_LEN, &self.len.to_be_bytes());
write_attribute(&mut buf, NFTA_EXPR_NAME, c"payload".to_bytes_with_nul());
write_attribute(&mut buf, NFTA_EXPR_DATA | NLA_F_NESTED, &expr);
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub enum PayloadOp {
Load(Register),
Write(Register),
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
#[repr(i32)]
#[non_exhaustive]
pub enum PayloadBase {
LinkLayer = NFT_PAYLOAD_LL_HEADER,
Network = NFT_PAYLOAD_NETWORK_HEADER,
Transport = NFT_PAYLOAD_TRANSPORT_HEADER,
}