use std::net::Ipv4Addr;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(u8)]
#[non_exhaustive]
pub enum Family {
Ip = 2,
Ip6 = 10,
Inet = 1,
Arp = 3,
Bridge = 7,
Netdev = 5,
}
impl Family {
pub fn from_u8(val: u8) -> Option<Self> {
match val {
2 => Some(Self::Ip),
10 => Some(Self::Ip6),
1 => Some(Self::Inet),
3 => Some(Self::Arp),
7 => Some(Self::Bridge),
5 => Some(Self::Netdev),
_ => None,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum Hook {
Prerouting,
Input,
Forward,
Output,
Postrouting,
Ingress,
}
impl Hook {
pub fn to_u32(self) -> u32 {
match self {
Self::Prerouting => 0,
Self::Input => 1,
Self::Forward => 2,
Self::Output => 3,
Self::Postrouting => 4,
Self::Ingress => 0,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum ChainType {
Filter,
Nat,
Route,
}
impl ChainType {
pub fn as_str(&self) -> &'static str {
match self {
Self::Filter => "filter",
Self::Nat => "nat",
Self::Route => "route",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum Priority {
Raw,
Mangle,
DstNat,
Filter,
Security,
SrcNat,
Custom(i32),
}
impl Priority {
pub fn to_i32(self) -> i32 {
match self {
Self::Raw => -300,
Self::Mangle => -150,
Self::DstNat => -100,
Self::Filter => 0,
Self::Security => 50,
Self::SrcNat => 100,
Self::Custom(v) => v,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum Policy {
Accept,
Drop,
}
impl Policy {
pub fn to_u32(self) -> u32 {
match self {
Self::Accept => 1,
Self::Drop => 0,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub enum Verdict {
Accept,
Drop,
Continue,
Return,
Jump(String),
Goto(String),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct CtState(pub u32);
impl CtState {
pub const INVALID: Self = Self(1);
pub const ESTABLISHED: Self = Self(2);
pub const RELATED: Self = Self(4);
pub const NEW: Self = Self(8);
pub const UNTRACKED: Self = Self(64);
pub const fn empty() -> Self {
Self(0)
}
pub fn bits(self) -> u32 {
self.0
}
}
impl Default for CtState {
fn default() -> Self {
Self::empty()
}
}
impl std::ops::BitOr for CtState {
type Output = Self;
fn bitor(self, rhs: Self) -> Self {
Self(self.0 | rhs.0)
}
}
impl std::ops::BitOrAssign for CtState {
fn bitor_assign(&mut self, rhs: Self) {
self.0 |= rhs.0;
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum LimitUnit {
Second,
Minute,
Hour,
Day,
}
impl LimitUnit {
pub fn to_u64(self) -> u64 {
match self {
Self::Second => 1,
Self::Minute => 60,
Self::Hour => 3600,
Self::Day => 86400,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum Register {
Verdict = 0,
R0 = 8,
R1 = 9,
R2 = 10,
R3 = 11,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum CmpOp {
Eq = 0,
Neq = 1,
Lt = 2,
Lte = 3,
Gt = 4,
Gte = 5,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum PayloadBase {
LinkLayer = 0,
Network = 1,
Transport = 2,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum MetaKey {
Len = 0,
Protocol = 1,
Mark = 3,
Iif = 4,
Oif = 5,
IifName = 6,
OifName = 7,
SkUid = 10,
SkGid = 11,
NfProto = 15,
L4Proto = 16,
CGroup = 23,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum CtKey {
State = 0,
Direction = 1,
Status = 2,
Mark = 3,
Expiration = 7,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum NatType {
Snat = 0,
Dnat = 1,
}
#[derive(Debug, Clone)]
pub struct NatExpr {
pub nat_type: NatType,
pub family: Family,
pub addr: Option<Ipv4Addr>,
pub port: Option<u16>,
}
impl NatExpr {
pub fn snat(family: Family) -> Self {
Self {
nat_type: NatType::Snat,
family,
addr: None,
port: None,
}
}
pub fn dnat(family: Family) -> Self {
Self {
nat_type: NatType::Dnat,
family,
addr: None,
port: None,
}
}
pub fn addr(mut self, addr: Ipv4Addr) -> Self {
self.addr = Some(addr);
self
}
pub fn port(mut self, port: u16) -> Self {
self.port = Some(port);
self
}
}
#[derive(Debug, Clone)]
pub struct Table {
pub name: String,
pub family: Family,
pub flags: u32,
pub use_count: u32,
pub handle: u64,
}
#[derive(Debug, Clone)]
#[must_use = "builders do nothing unless used"]
pub struct Chain {
pub(crate) table: String,
pub(crate) name: String,
pub(crate) family: Family,
pub(crate) hook: Option<Hook>,
pub(crate) priority: Option<Priority>,
pub(crate) chain_type: Option<ChainType>,
pub(crate) policy: Option<Policy>,
}
impl Chain {
pub fn new(table: &str, name: &str) -> Self {
Self {
table: table.to_string(),
name: name.to_string(),
family: Family::Inet,
hook: None,
priority: None,
chain_type: None,
policy: None,
}
}
pub fn family(mut self, family: Family) -> Self {
self.family = family;
self
}
pub fn hook(mut self, hook: Hook) -> Self {
self.hook = Some(hook);
self
}
pub fn priority(mut self, priority: Priority) -> Self {
self.priority = Some(priority);
self
}
pub fn chain_type(mut self, chain_type: ChainType) -> Self {
self.chain_type = Some(chain_type);
self
}
pub fn policy(mut self, policy: Policy) -> Self {
self.policy = Some(policy);
self
}
}
#[derive(Debug, Clone)]
pub struct ChainInfo {
pub table: String,
pub name: String,
pub family: Family,
pub hook: Option<u32>,
pub priority: Option<i32>,
pub chain_type: Option<String>,
pub policy: Option<u32>,
pub handle: u64,
}
#[derive(Debug, Clone)]
#[must_use = "builders do nothing unless used"]
pub struct Rule {
pub(crate) table: String,
pub(crate) chain: String,
pub(crate) family: Family,
pub(crate) exprs: Vec<super::expr::Expr>,
pub(crate) position: Option<u64>,
}
impl Rule {
pub fn new(table: &str, chain: &str) -> Self {
Self {
table: table.to_string(),
chain: chain.to_string(),
family: Family::Inet,
exprs: Vec::new(),
position: None,
}
}
pub fn family(mut self, family: Family) -> Self {
self.family = family;
self
}
pub fn position(mut self, pos: u64) -> Self {
self.position = Some(pos);
self
}
pub fn match_tcp_dport(mut self, port: u16) -> Self {
use super::expr::Expr;
self.exprs.push(Expr::Meta {
dreg: Register::R0,
key: MetaKey::L4Proto,
});
self.exprs.push(Expr::Cmp {
sreg: Register::R0,
op: CmpOp::Eq,
data: vec![6u8], });
self.exprs.push(Expr::Payload {
dreg: Register::R0,
base: PayloadBase::Transport,
offset: 2,
len: 2,
});
self.exprs.push(Expr::Cmp {
sreg: Register::R0,
op: CmpOp::Eq,
data: port.to_be_bytes().to_vec(),
});
self
}
pub fn match_udp_dport(mut self, port: u16) -> Self {
use super::expr::Expr;
self.exprs.push(Expr::Meta {
dreg: Register::R0,
key: MetaKey::L4Proto,
});
self.exprs.push(Expr::Cmp {
sreg: Register::R0,
op: CmpOp::Eq,
data: vec![17u8], });
self.exprs.push(Expr::Payload {
dreg: Register::R0,
base: PayloadBase::Transport,
offset: 2,
len: 2,
});
self.exprs.push(Expr::Cmp {
sreg: Register::R0,
op: CmpOp::Eq,
data: port.to_be_bytes().to_vec(),
});
self
}
pub fn match_saddr_v4(mut self, addr: Ipv4Addr, prefix: u8) -> Self {
use super::expr::Expr;
self.exprs.push(Expr::Payload {
dreg: Register::R0,
base: PayloadBase::Network,
offset: 12,
len: 4,
});
if prefix == 32 {
self.exprs.push(Expr::Cmp {
sreg: Register::R0,
op: CmpOp::Eq,
data: addr.octets().to_vec(),
});
} else {
let mask = prefix_to_mask_v4(prefix);
self.exprs.push(Expr::Bitwise {
sreg: Register::R0,
dreg: Register::R0,
len: 4,
mask: mask.to_vec(),
xor: vec![0; 4],
});
let masked_addr: Vec<u8> = addr
.octets()
.iter()
.zip(mask.iter())
.map(|(a, m)| a & m)
.collect();
self.exprs.push(Expr::Cmp {
sreg: Register::R0,
op: CmpOp::Eq,
data: masked_addr,
});
}
self
}
pub fn match_daddr_v4(mut self, addr: Ipv4Addr, prefix: u8) -> Self {
use super::expr::Expr;
self.exprs.push(Expr::Payload {
dreg: Register::R0,
base: PayloadBase::Network,
offset: 16,
len: 4,
});
if prefix == 32 {
self.exprs.push(Expr::Cmp {
sreg: Register::R0,
op: CmpOp::Eq,
data: addr.octets().to_vec(),
});
} else {
let mask = prefix_to_mask_v4(prefix);
self.exprs.push(Expr::Bitwise {
sreg: Register::R0,
dreg: Register::R0,
len: 4,
mask: mask.to_vec(),
xor: vec![0; 4],
});
let masked_addr: Vec<u8> = addr
.octets()
.iter()
.zip(mask.iter())
.map(|(a, m)| a & m)
.collect();
self.exprs.push(Expr::Cmp {
sreg: Register::R0,
op: CmpOp::Eq,
data: masked_addr,
});
}
self
}
pub fn match_iif(mut self, name: &str) -> Self {
use super::expr::Expr;
self.exprs.push(Expr::Meta {
dreg: Register::R0,
key: MetaKey::IifName,
});
let mut data = name.as_bytes().to_vec();
data.push(0); data.resize(16, 0);
self.exprs.push(Expr::Cmp {
sreg: Register::R0,
op: CmpOp::Eq,
data,
});
self
}
pub fn match_oif(mut self, name: &str) -> Self {
use super::expr::Expr;
self.exprs.push(Expr::Meta {
dreg: Register::R0,
key: MetaKey::OifName,
});
let mut data = name.as_bytes().to_vec();
data.push(0);
data.resize(16, 0);
self.exprs.push(Expr::Cmp {
sreg: Register::R0,
op: CmpOp::Eq,
data,
});
self
}
pub fn match_ct_state(mut self, state: CtState) -> Self {
use super::expr::Expr;
self.exprs.push(Expr::Ct {
dreg: Register::R0,
key: CtKey::State,
});
self.exprs.push(Expr::Bitwise {
sreg: Register::R0,
dreg: Register::R0,
len: 4,
mask: state.bits().to_ne_bytes().to_vec(),
xor: vec![0; 4],
});
self.exprs.push(Expr::Cmp {
sreg: Register::R0,
op: CmpOp::Neq,
data: vec![0; 4],
});
self
}
pub fn accept(mut self) -> Self {
self.exprs.push(super::expr::Expr::Verdict(Verdict::Accept));
self
}
pub fn drop(mut self) -> Self {
self.exprs.push(super::expr::Expr::Verdict(Verdict::Drop));
self
}
pub fn jump(mut self, chain: &str) -> Self {
self.exprs
.push(super::expr::Expr::Verdict(Verdict::Jump(chain.to_string())));
self
}
pub fn goto(mut self, chain: &str) -> Self {
self.exprs
.push(super::expr::Expr::Verdict(Verdict::Goto(chain.to_string())));
self
}
pub fn counter(mut self) -> Self {
self.exprs.push(super::expr::Expr::Counter);
self
}
pub fn limit(mut self, rate: u64, unit: LimitUnit) -> Self {
self.exprs.push(super::expr::Expr::Limit {
rate,
unit,
burst: 5,
});
self
}
pub fn masquerade(mut self) -> Self {
self.exprs.push(super::expr::Expr::Masquerade);
self
}
pub fn snat(mut self, addr: Ipv4Addr, port: Option<u16>) -> Self {
use super::expr::Expr;
self.exprs.push(Expr::Immediate {
dreg: Register::R0,
data: addr.octets().to_vec(),
});
if let Some(p) = port {
self.exprs.push(Expr::Immediate {
dreg: Register::R1,
data: p.to_be_bytes().to_vec(),
});
}
self.exprs.push(Expr::Nat(NatExpr {
nat_type: NatType::Snat,
family: Family::Ip,
addr: Some(addr),
port,
}));
self
}
pub fn dnat(mut self, addr: Ipv4Addr, port: Option<u16>) -> Self {
use super::expr::Expr;
self.exprs.push(Expr::Immediate {
dreg: Register::R0,
data: addr.octets().to_vec(),
});
if let Some(p) = port {
self.exprs.push(Expr::Immediate {
dreg: Register::R1,
data: p.to_be_bytes().to_vec(),
});
}
self.exprs.push(Expr::Nat(NatExpr {
nat_type: NatType::Dnat,
family: Family::Ip,
addr: Some(addr),
port,
}));
self
}
pub fn redirect(mut self, port: Option<u16>) -> Self {
use super::expr::Expr;
if let Some(p) = port {
self.exprs.push(Expr::Immediate {
dreg: Register::R0,
data: p.to_be_bytes().to_vec(),
});
}
self.exprs.push(Expr::Redirect { port });
self
}
pub fn log(mut self, prefix: Option<&str>) -> Self {
self.exprs.push(super::expr::Expr::Log {
prefix: prefix.map(String::from),
group: None,
});
self
}
pub fn match_saddr_in_set(mut self, set: &str) -> Self {
use super::expr::Expr;
self.exprs.push(Expr::Payload {
dreg: Register::R0,
base: PayloadBase::Network,
offset: 12,
len: 4,
});
self.exprs.push(Expr::Lookup {
set: set.to_string(),
sreg: Register::R0,
});
self
}
pub fn match_daddr_in_set(mut self, set: &str) -> Self {
use super::expr::Expr;
self.exprs.push(Expr::Payload {
dreg: Register::R0,
base: PayloadBase::Network,
offset: 16,
len: 4,
});
self.exprs.push(Expr::Lookup {
set: set.to_string(),
sreg: Register::R0,
});
self
}
pub fn match_l4proto(mut self, proto: u8) -> Self {
use super::expr::Expr;
self.exprs.push(Expr::Meta {
dreg: Register::R0,
key: MetaKey::L4Proto,
});
self.exprs.push(Expr::Cmp {
sreg: Register::R0,
op: CmpOp::Eq,
data: vec![proto],
});
self
}
pub fn match_tcp_sport(mut self, port: u16) -> Self {
use super::expr::Expr;
self.exprs.push(Expr::Meta {
dreg: Register::R0,
key: MetaKey::L4Proto,
});
self.exprs.push(Expr::Cmp {
sreg: Register::R0,
op: CmpOp::Eq,
data: vec![6u8], });
self.exprs.push(Expr::Payload {
dreg: Register::R0,
base: PayloadBase::Transport,
offset: 0, len: 2,
});
self.exprs.push(Expr::Cmp {
sreg: Register::R0,
op: CmpOp::Eq,
data: port.to_be_bytes().to_vec(),
});
self
}
pub fn match_udp_sport(mut self, port: u16) -> Self {
use super::expr::Expr;
self.exprs.push(Expr::Meta {
dreg: Register::R0,
key: MetaKey::L4Proto,
});
self.exprs.push(Expr::Cmp {
sreg: Register::R0,
op: CmpOp::Eq,
data: vec![17u8], });
self.exprs.push(Expr::Payload {
dreg: Register::R0,
base: PayloadBase::Transport,
offset: 0,
len: 2,
});
self.exprs.push(Expr::Cmp {
sreg: Register::R0,
op: CmpOp::Eq,
data: port.to_be_bytes().to_vec(),
});
self
}
pub fn match_icmp_type(mut self, icmp_type: u8) -> Self {
use super::expr::Expr;
self.exprs.push(Expr::Meta {
dreg: Register::R0,
key: MetaKey::L4Proto,
});
self.exprs.push(Expr::Cmp {
sreg: Register::R0,
op: CmpOp::Eq,
data: vec![1u8], });
self.exprs.push(Expr::Payload {
dreg: Register::R0,
base: PayloadBase::Transport,
offset: 0,
len: 1,
});
self.exprs.push(Expr::Cmp {
sreg: Register::R0,
op: CmpOp::Eq,
data: vec![icmp_type],
});
self
}
pub fn match_icmpv6_type(mut self, icmp_type: u8) -> Self {
use super::expr::Expr;
self.exprs.push(Expr::Meta {
dreg: Register::R0,
key: MetaKey::L4Proto,
});
self.exprs.push(Expr::Cmp {
sreg: Register::R0,
op: CmpOp::Eq,
data: vec![58u8], });
self.exprs.push(Expr::Payload {
dreg: Register::R0,
base: PayloadBase::Transport,
offset: 0,
len: 1,
});
self.exprs.push(Expr::Cmp {
sreg: Register::R0,
op: CmpOp::Eq,
data: vec![icmp_type],
});
self
}
pub fn match_saddr_v4_not(mut self, addr: Ipv4Addr, prefix: u8) -> Self {
use super::expr::Expr;
self.exprs.push(Expr::Payload {
dreg: Register::R0,
base: PayloadBase::Network,
offset: 12,
len: 4,
});
if prefix == 32 {
self.exprs.push(Expr::Cmp {
sreg: Register::R0,
op: CmpOp::Neq,
data: addr.octets().to_vec(),
});
} else {
let mask = prefix_to_mask_v4(prefix);
self.exprs.push(Expr::Bitwise {
sreg: Register::R0,
dreg: Register::R0,
len: 4,
mask: mask.to_vec(),
xor: vec![0; 4],
});
let masked_addr: Vec<u8> = addr
.octets()
.iter()
.zip(mask.iter())
.map(|(a, m)| a & m)
.collect();
self.exprs.push(Expr::Cmp {
sreg: Register::R0,
op: CmpOp::Neq,
data: masked_addr,
});
}
self
}
pub fn match_daddr_v4_not(mut self, addr: Ipv4Addr, prefix: u8) -> Self {
use super::expr::Expr;
self.exprs.push(Expr::Payload {
dreg: Register::R0,
base: PayloadBase::Network,
offset: 16,
len: 4,
});
if prefix == 32 {
self.exprs.push(Expr::Cmp {
sreg: Register::R0,
op: CmpOp::Neq,
data: addr.octets().to_vec(),
});
} else {
let mask = prefix_to_mask_v4(prefix);
self.exprs.push(Expr::Bitwise {
sreg: Register::R0,
dreg: Register::R0,
len: 4,
mask: mask.to_vec(),
xor: vec![0; 4],
});
let masked_addr: Vec<u8> = addr
.octets()
.iter()
.zip(mask.iter())
.map(|(a, m)| a & m)
.collect();
self.exprs.push(Expr::Cmp {
sreg: Register::R0,
op: CmpOp::Neq,
data: masked_addr,
});
}
self
}
pub fn match_tcp_dport_not(mut self, port: u16) -> Self {
use super::expr::Expr;
self.exprs.push(Expr::Meta {
dreg: Register::R0,
key: MetaKey::L4Proto,
});
self.exprs.push(Expr::Cmp {
sreg: Register::R0,
op: CmpOp::Eq,
data: vec![6u8],
});
self.exprs.push(Expr::Payload {
dreg: Register::R0,
base: PayloadBase::Transport,
offset: 2,
len: 2,
});
self.exprs.push(Expr::Cmp {
sreg: Register::R0,
op: CmpOp::Neq,
data: port.to_be_bytes().to_vec(),
});
self
}
pub fn match_udp_dport_not(mut self, port: u16) -> Self {
use super::expr::Expr;
self.exprs.push(Expr::Meta {
dreg: Register::R0,
key: MetaKey::L4Proto,
});
self.exprs.push(Expr::Cmp {
sreg: Register::R0,
op: CmpOp::Eq,
data: vec![17u8],
});
self.exprs.push(Expr::Payload {
dreg: Register::R0,
base: PayloadBase::Transport,
offset: 2,
len: 2,
});
self.exprs.push(Expr::Cmp {
sreg: Register::R0,
op: CmpOp::Neq,
data: port.to_be_bytes().to_vec(),
});
self
}
pub fn match_mark(mut self, mark: u32) -> Self {
use super::expr::Expr;
self.exprs.push(Expr::Meta {
dreg: Register::R0,
key: MetaKey::Mark,
});
self.exprs.push(Expr::Cmp {
sreg: Register::R0,
op: CmpOp::Eq,
data: mark.to_ne_bytes().to_vec(),
});
self
}
pub fn reject(mut self) -> Self {
self.exprs.push(super::expr::Expr::Verdict(Verdict::Drop));
self
}
pub fn expressions(mut self, exprs: Vec<super::expr::Expr>) -> Self {
self.exprs = exprs;
self
}
}
#[derive(Debug, Clone)]
pub struct RuleInfo {
pub table: String,
pub chain: String,
pub family: Family,
pub handle: u64,
pub position: Option<u64>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum SetKeyType {
Ipv4Addr,
Ipv6Addr,
EtherAddr,
InetService,
IfIndex,
Mark,
}
impl SetKeyType {
pub fn type_id(self) -> u32 {
match self {
Self::Ipv4Addr => 7, Self::Ipv6Addr => 8, Self::EtherAddr => 9, Self::InetService => 13, Self::IfIndex => 15, Self::Mark => 12, }
}
#[allow(clippy::len_without_is_empty)]
pub fn len(self) -> u32 {
match self {
Self::Ipv4Addr => 4,
Self::Ipv6Addr => 16,
Self::EtherAddr => 8, Self::InetService => 2,
Self::IfIndex => 4,
Self::Mark => 4,
}
}
}
#[derive(Debug, Clone)]
#[must_use = "builders do nothing unless used"]
pub struct Set {
pub(crate) table: String,
pub(crate) name: String,
pub(crate) family: Family,
pub(crate) key_type: SetKeyType,
pub(crate) flags: u32,
}
impl Set {
pub fn new(table: &str, name: &str) -> Self {
Self {
table: table.to_string(),
name: name.to_string(),
family: Family::Inet,
key_type: SetKeyType::Ipv4Addr,
flags: 0,
}
}
pub fn family(mut self, family: Family) -> Self {
self.family = family;
self
}
pub fn key_type(mut self, key_type: SetKeyType) -> Self {
self.key_type = key_type;
self
}
pub fn constant(mut self) -> Self {
self.flags |= super::NFT_SET_CONSTANT;
self
}
}
#[derive(Debug, Clone)]
pub struct SetElement {
pub key: Vec<u8>,
}
impl SetElement {
pub fn new(key: Vec<u8>) -> Self {
Self { key }
}
pub fn ipv4(addr: Ipv4Addr) -> Self {
Self {
key: addr.octets().to_vec(),
}
}
pub fn ipv6(addr: std::net::Ipv6Addr) -> Self {
Self {
key: addr.octets().to_vec(),
}
}
pub fn port(port: u16) -> Self {
Self {
key: port.to_be_bytes().to_vec(),
}
}
}
#[derive(Debug, Clone)]
pub struct SetInfo {
pub table: String,
pub name: String,
pub family: Family,
pub flags: u32,
pub key_type: u32,
pub key_len: u32,
pub handle: u64,
}
fn prefix_to_mask_v4(prefix: u8) -> [u8; 4] {
if prefix == 0 {
return [0; 4];
}
let mask = !0u32 << (32 - prefix.min(32));
mask.to_be_bytes()
}
#[cfg(test)]
mod tests {
use super::*;
fn find_nat_expr(rule: &Rule) -> Option<&NatExpr> {
rule.exprs.iter().find_map(|e| match e {
super::super::expr::Expr::Nat(n) => Some(n),
_ => None,
})
}
#[test]
fn dnat_inet_table_uses_ip_family() {
let rule = Rule::new("nat", "prerouting")
.family(Family::Inet)
.dnat("10.0.0.1".parse().unwrap(), Some(8080));
let nat = find_nat_expr(&rule).expect("should have NAT expr");
assert_eq!(nat.family, Family::Ip);
assert_eq!(nat.nat_type, NatType::Dnat);
}
#[test]
fn snat_inet_table_uses_ip_family() {
let rule = Rule::new("nat", "postrouting")
.family(Family::Inet)
.snat("10.0.0.1".parse().unwrap(), None);
let nat = find_nat_expr(&rule).expect("should have NAT expr");
assert_eq!(nat.family, Family::Ip);
assert_eq!(nat.nat_type, NatType::Snat);
}
#[test]
fn dnat_ip_table_uses_ip_family() {
let rule = Rule::new("nat", "prerouting")
.family(Family::Ip)
.dnat("192.168.1.1".parse().unwrap(), Some(80));
let nat = find_nat_expr(&rule).expect("should have NAT expr");
assert_eq!(nat.family, Family::Ip);
}
#[test]
fn snat_with_port() {
let rule = Rule::new("nat", "postrouting")
.family(Family::Inet)
.snat("192.168.1.1".parse().unwrap(), Some(1024));
let nat = find_nat_expr(&rule).expect("should have NAT expr");
assert_eq!(nat.family, Family::Ip);
assert_eq!(nat.port, Some(1024));
assert_eq!(nat.addr, Some("192.168.1.1".parse().unwrap()));
}
}