use libc::{NF_ACCEPT, NF_DROP};
use rustables_macros::nfnetlink_struct;
use crate::error::{DecodeError, QueryError};
use crate::nlmsg::{NfNetlinkAttribute, NfNetlinkDeserializable, NfNetlinkObject};
use crate::sys::{
NFTA_CHAIN_FLAGS, NFTA_CHAIN_HOOK, NFTA_CHAIN_NAME, NFTA_CHAIN_POLICY, NFTA_CHAIN_TABLE,
NFTA_CHAIN_TYPE, NFTA_HOOK_HOOKNUM, NFTA_HOOK_PRIORITY, NFT_MSG_DELCHAIN, NFT_MSG_NEWCHAIN,
};
use crate::{Batch, ProtocolFamily, Table};
use std::fmt::Debug;
pub type ChainPriority = i32;
#[derive(Debug, Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Hash)]
#[repr(i32)]
pub enum HookClass {
PreRouting = libc::NF_INET_PRE_ROUTING,
In = libc::NF_INET_LOCAL_IN,
Forward = libc::NF_INET_FORWARD,
Out = libc::NF_INET_LOCAL_OUT,
PostRouting = libc::NF_INET_POST_ROUTING,
}
#[derive(Clone, PartialEq, Eq, Default, Debug)]
#[nfnetlink_struct(nested = true)]
pub struct Hook {
#[field(NFTA_HOOK_HOOKNUM)]
class: u32,
#[field(NFTA_HOOK_PRIORITY)]
priority: u32,
}
impl Hook {
pub fn new(class: HookClass, priority: ChainPriority) -> Self {
Hook::default()
.with_class(class as u32)
.with_priority(priority as u32)
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Hash)]
#[repr(i32)]
pub enum ChainPolicy {
Accept = NF_ACCEPT,
Drop = NF_DROP,
}
impl NfNetlinkAttribute for ChainPolicy {
fn get_size(&self) -> usize {
(*self as i32).get_size()
}
fn write_payload(&self, addr: &mut [u8]) {
(*self as i32).write_payload(addr);
}
}
impl NfNetlinkDeserializable for ChainPolicy {
fn deserialize(buf: &[u8]) -> Result<(Self, &[u8]), DecodeError> {
let (v, remaining_data) = i32::deserialize(buf)?;
Ok((
match v {
NF_ACCEPT => ChainPolicy::Accept,
NF_DROP => ChainPolicy::Accept,
_ => return Err(DecodeError::UnknownChainPolicy),
},
remaining_data,
))
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum ChainType {
Filter,
Route,
Nat,
}
impl ChainType {
fn as_str(&self) -> &'static str {
match *self {
ChainType::Filter => "filter",
ChainType::Route => "route",
ChainType::Nat => "nat",
}
}
}
impl NfNetlinkAttribute for ChainType {
fn get_size(&self) -> usize {
self.as_str().len()
}
fn write_payload(&self, addr: &mut [u8]) {
self.as_str().to_string().write_payload(addr);
}
}
impl NfNetlinkDeserializable for ChainType {
fn deserialize(buf: &[u8]) -> Result<(Self, &[u8]), DecodeError> {
let (s, remaining_data) = String::deserialize(buf)?;
Ok((
match s.as_str() {
"filter" => ChainType::Filter,
"route" => ChainType::Route,
"nat" => ChainType::Nat,
_ => return Err(DecodeError::UnknownChainType),
},
remaining_data,
))
}
}
#[nfnetlink_struct(derive_deserialize = false)]
#[derive(PartialEq, Eq, Default, Debug)]
pub struct Chain {
family: ProtocolFamily,
#[field(NFTA_CHAIN_TABLE)]
table: String,
#[field(NFTA_CHAIN_NAME)]
name: String,
#[field(NFTA_CHAIN_HOOK)]
hook: Hook,
#[field(NFTA_CHAIN_POLICY)]
policy: ChainPolicy,
#[field(NFTA_CHAIN_TYPE, name_in_functions = "type")]
chain_type: ChainType,
#[field(NFTA_CHAIN_FLAGS)]
flags: u32,
#[field(optional = true, crate::sys::NFTA_CHAIN_USERDATA)]
userdata: Vec<u8>,
}
impl Chain {
pub fn new(table: &Table) -> Chain {
let mut chain = Chain::default();
chain.family = table.get_family();
if let Some(table_name) = table.get_name() {
chain.set_table(table_name);
}
chain
}
pub fn add_to_batch(self, batch: &mut Batch) -> Self {
batch.add(&self, crate::MsgType::Add);
self
}
}
impl NfNetlinkObject for Chain {
const MSG_TYPE_ADD: u32 = NFT_MSG_NEWCHAIN;
const MSG_TYPE_DEL: u32 = NFT_MSG_DELCHAIN;
fn get_family(&self) -> ProtocolFamily {
self.family
}
fn set_family(&mut self, family: ProtocolFamily) {
self.family = family;
}
}
pub fn list_chains_for_table(table: &Table) -> Result<Vec<Chain>, QueryError> {
let mut result = Vec::new();
crate::query::list_objects_with_data(
libc::NFT_MSG_GETCHAIN as u16,
&|chain: Chain, (table, chains): &mut (&Table, &mut Vec<Chain>)| {
if chain.get_table() == table.get_name() {
chains.push(chain);
} else {
info!(
"Ignoring chain {:?} because it doesn't map the table {:?}",
chain.get_name(),
table.get_name()
);
}
Ok(())
},
None,
&mut (&table, &mut result),
)?;
Ok(result)
}