nf_tables 0.1.0

Pure Rust crate to interact with the Linux nf_tables subsystem
Documentation
use std::borrow::Cow;
use std::ffi::CStr;

use bytes::{Buf, BufMut};
use libc::{
    NFT_MSG_DELCHAIN, NFT_MSG_DELRULE, NFT_MSG_DELTABLE, NFT_MSG_NEWCHAIN, NFT_MSG_NEWRULE,
    NFT_MSG_NEWTABLE,
};

use crate::rule::Expr;
use crate::{Command, Encode, Error, Handle, Hook, Message, Policy, ProtoFamily, read_attribute};
use crate::{constants::*, write_attribute};

/// Creates a new table.
#[derive(Clone, Debug)]
pub struct AddTable<'a> {
    /// The name of the table.
    pub name: Cow<'a, CStr>,
    /// The protocol family of the table.
    pub proto: ProtoFamily,
    pub flags: u32,
}

impl Message for AddTable<'_> {
    const ID: u16 = NFT_MSG_NEWTABLE as u16;

    fn proto(&self) -> ProtoFamily {
        self.proto
    }
}

impl Encode for AddTable<'_> {
    fn encode<B>(&self, mut buf: B)
    where
        B: BufMut,
    {
        write_attribute(&mut buf, NFTA_TABLE_NAME, self.name.to_bytes_with_nul());
        write_attribute(&mut buf, NFTA_TABLE_FLAGS, &self.flags.to_be_bytes());
    }
}

impl<'a> From<AddTable<'a>> for Command<'a> {
    #[inline]
    fn from(value: AddTable<'a>) -> Self {
        Command::AddTable(value)
    }
}

/// Deletes an existing table.
#[derive(Clone, Debug)]
pub struct DelTable<'a> {
    /// The name of the table.
    pub name: Cow<'a, CStr>,
    /// The protocol family of the table.
    pub proto: ProtoFamily,
}

impl Message for DelTable<'_> {
    const ID: u16 = NFT_MSG_DELTABLE as u16;

    fn proto(&self) -> ProtoFamily {
        self.proto
    }
}

impl Encode for DelTable<'_> {
    fn encode<B>(&self, mut buf: B)
    where
        B: BufMut,
    {
        write_attribute(&mut buf, NFTA_TABLE_NAME, self.name.to_bytes_with_nul());
    }
}

impl<'a> From<DelTable<'a>> for Command<'a> {
    #[inline]
    fn from(value: DelTable<'a>) -> Self {
        Command::DelTable(value)
    }
}

/// Creates a new chain.
#[derive(Clone, Debug)]
pub struct AddChain<'a> {
    /// The name of the table of the chain.
    pub table: Cow<'a, CStr>,
    /// The protocol family of the table of the chain.
    pub proto: ProtoFamily,
    pub hook: Option<ChainHook>,
    /// The default policy applied to all packets that don't match any rule in this chain.
    /// The name of the chain.
    pub name: Cow<'a, CStr>,
}

#[derive(Copy, Clone, Debug)]
pub struct ChainHook {
    pub hook: Hook,
    pub priority: i32,
    pub policy: Policy,
}

impl Message for AddChain<'_> {
    const ID: u16 = NFT_MSG_NEWCHAIN as u16;

    fn proto(&self) -> ProtoFamily {
        self.proto
    }

    fn read_handle<B>(mut buf: B) -> Result<Option<Handle>, Error>
    where
        B: Buf,
    {
        while buf.has_remaining() {
            let (header, mut data) = read_attribute(&mut buf)?;

            if header.ty == NFTA_CHAIN_HANDLE
                && let Ok(handle) = data.try_get_u64()
            {
                return Ok(Some(Handle::from_bits(handle)));
            }
        }

        Ok(None)
    }
}

impl Encode for AddChain<'_> {
    fn encode<B>(&self, mut buf: B)
    where
        B: BufMut,
    {
        write_attribute(&mut buf, NFTA_CHAIN_TABLE, self.table.to_bytes_with_nul());
        write_attribute(&mut buf, NFTA_CHAIN_NAME, self.name.to_bytes_with_nul());

        if let Some(hook) = self.hook {
            write_attribute(
                &mut buf,
                NFTA_CHAIN_POLICY,
                &(hook.policy as u32).to_be_bytes(),
            );

            let mut nest = Vec::new();

            write_attribute(
                &mut nest,
                NFTA_HOOK_HOOKNUM,
                &(hook.hook as u32).to_be_bytes(),
            );
            write_attribute(&mut nest, NFTA_HOOK_PRIORITY, &hook.priority.to_be_bytes());

            write_attribute(&mut buf, NFTA_CHAIN_HOOK | NLA_F_NESTED, &nest);
        }
    }
}

impl<'a> From<AddChain<'a>> for Command<'a> {
    #[inline]
    fn from(value: AddChain<'a>) -> Self {
        Command::AddChain(value)
    }
}

/// Deletes an existing chain.
#[derive(Clone, Debug)]
pub struct DelChain<'a> {
    /// The table in which the chain is located.
    pub table: Cow<'a, CStr>,
    /// The protocol family of the table.
    pub proto: ProtoFamily,
    /// The chain that should be deleted.
    pub chain: ChainRef<'a>,
}

impl Message for DelChain<'_> {
    const ID: u16 = NFT_MSG_DELCHAIN as u16;

    fn proto(&self) -> ProtoFamily {
        self.proto
    }
}

impl Encode for DelChain<'_> {
    fn encode<B>(&self, mut buf: B)
    where
        B: BufMut,
    {
        write_attribute(&mut buf, NFTA_CHAIN_TABLE, self.table.to_bytes_with_nul());

        match &self.chain {
            ChainRef::Name(name) => {
                write_attribute(&mut buf, NFTA_CHAIN_NAME, name.to_bytes_with_nul());
            }
            ChainRef::Handle(handle) => {
                write_attribute(&mut buf, NFTA_CHAIN_HANDLE, &handle.to_bits().to_be_bytes());
            }
        }
    }
}

impl<'a> From<DelChain<'a>> for Command<'a> {
    #[inline]
    fn from(value: DelChain<'a>) -> Self {
        Command::DelChain(value)
    }
}

/// A reference to a chain.
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum ChainRef<'a> {
    /// A reference to a chain by name.
    Name(Cow<'a, CStr>),
    /// A reference to a chain by handle.
    Handle(Handle),
}

/// Adds a new rule to a chain.
#[derive(Clone, Debug)]
pub struct AddRule<'a> {
    /// The table in which the chain resides.
    pub table: Cow<'a, CStr>,
    /// The protocol family of the table.
    pub proto: ProtoFamily,
    /// The name of the chain.
    pub chain: Cow<'a, CStr>,
    /// The position in the chain where the new rule will be placed.
    ///
    /// If `None` the rule will be added at the end.
    pub position: Option<u64>,
    /// The expressions defining the rule.
    pub exprs: Vec<Expr>,
}

impl Message for AddRule<'_> {
    const ID: u16 = NFT_MSG_NEWRULE as u16;

    fn proto(&self) -> ProtoFamily {
        self.proto
    }

    fn read_handle<B>(mut buf: B) -> Result<Option<Handle>, Error>
    where
        B: Buf,
    {
        while buf.has_remaining() {
            let (header, mut data) = read_attribute(&mut buf)?;

            if header.ty == NFTA_RULE_HANDLE
                && let Ok(handle) = data.try_get_u64()
            {
                return Ok(Some(Handle::from_bits(handle)));
            }
        }

        Ok(None)
    }
}

impl Encode for AddRule<'_> {
    fn encode<B>(&self, mut buf: B)
    where
        B: BufMut,
    {
        write_attribute(&mut buf, NFTA_RULE_TABLE, self.table.to_bytes_with_nul());
        write_attribute(&mut buf, NFTA_RULE_CHAIN, self.chain.to_bytes_with_nul());

        if let Some(position) = self.position {
            write_attribute(&mut buf, NFTA_RULE_POSITION, &position.to_be_bytes());
        }

        let mut exprs = Vec::new();
        for expr in &self.exprs {
            let mut data = Vec::new();
            expr.encode(&mut data);

            write_attribute(&mut exprs, NFTA_LIST_ELEM | NLA_F_NESTED, &data);
        }

        if !exprs.is_empty() {
            write_attribute(&mut buf, NFTA_RULE_EXPRESSIONS | NLA_F_NESTED, &exprs);
        }
    }
}

impl<'a> From<AddRule<'a>> for Command<'a> {
    #[inline]
    fn from(value: AddRule<'a>) -> Self {
        Command::AddRule(value)
    }
}

/// Deletes an existing rule.
#[derive(Clone, Debug)]
pub struct DelRule<'a> {
    /// The table where the chain containing the rule is placed.
    pub table: Cow<'a, CStr>,
    /// The protocol family of the table.
    pub proto: ProtoFamily,
    /// The chain where the rule is placed.
    pub chain: Cow<'a, CStr>,
    /// The handle of the rule.
    pub handle: Handle,
}

impl Message for DelRule<'_> {
    const ID: u16 = NFT_MSG_DELRULE as u16;

    fn proto(&self) -> ProtoFamily {
        self.proto
    }
}

impl Encode for DelRule<'_> {
    fn encode<B>(&self, mut buf: B)
    where
        B: BufMut,
    {
        write_attribute(&mut buf, NFTA_RULE_TABLE, self.table.to_bytes_with_nul());
        write_attribute(&mut buf, NFTA_RULE_CHAIN, self.chain.to_bytes_with_nul());
        write_attribute(
            &mut buf,
            NFTA_RULE_HANDLE,
            &self.handle.to_bits().to_be_bytes(),
        );
    }
}

impl<'a> From<DelRule<'a>> for Command<'a> {
    #[inline]
    fn from(value: DelRule<'a>) -> Self {
        Command::DelRule(value)
    }
}