nftnl 0.9.2

Safe abstraction for libnftnl. Provides low-level userspace access to the in-kernel nf_tables subsystem
Documentation
use crate::{MsgType, ProtoFamily};
use nftnl_sys::{self as sys, libc};
use std::{
    collections::HashSet,
    ffi::{CStr, CString, c_void},
    os::raw::c_char,
    ptr,
};

/// Abstraction of `nftnl_table`. The top level container in netfilter. A table has a protocol
/// family and contain [`Chain`]s that in turn hold the rules.
///
/// [`Chain`]: struct.Chain.html
pub struct Table {
    table: ptr::NonNull<sys::nftnl_table>,
    family: ProtoFamily,
}

// Safety: It should be safe to pass this around and *read* from it
// from multiple threads
unsafe impl Send for Table {}
unsafe impl Sync for Table {}

impl Table {
    /// Creates a new table instance with the given name and protocol family.
    pub fn new<T: AsRef<CStr>>(name: T, family: ProtoFamily) -> Table {
        let table = try_alloc!(unsafe { sys::nftnl_table_alloc() });

        unsafe {
            let table = table.as_ptr();
            sys::nftnl_table_set_u32(table, sys::NFTNL_TABLE_FAMILY as u16, family as u32);
            sys::nftnl_table_set_str(table, sys::NFTNL_TABLE_NAME as u16, name.as_ref().as_ptr());
            sys::nftnl_table_set_u32(table, sys::NFTNL_TABLE_FLAGS as u16, 0u32);
        }

        Table { table, family }
    }

    /// Returns the name of this table.
    pub fn get_name(&self) -> &CStr {
        unsafe {
            let ptr = sys::nftnl_table_get_str(self.table.as_ptr(), sys::NFTNL_TABLE_NAME as u16);
            CStr::from_ptr(ptr)
        }
    }

    /// Returns the protocol family for this table.
    pub fn get_family(&self) -> ProtoFamily {
        self.family
    }
}

unsafe impl crate::NlMsg for Table {
    unsafe fn write(&self, buf: *mut c_void, seq: u32, msg_type: MsgType) {
        let raw_msg_type = match msg_type {
            MsgType::Add => libc::NFT_MSG_NEWTABLE,
            MsgType::Del => libc::NFT_MSG_DELTABLE,
        };
        let header = unsafe {
            sys::nftnl_nlmsg_build_hdr(
                buf.cast::<c_char>(),
                raw_msg_type as u16,
                self.family as u16,
                libc::NLM_F_ACK as u16,
                seq,
            )
        };
        unsafe { sys::nftnl_table_nlmsg_build_payload(header, self.table.as_ptr()) };
    }
}

impl Drop for Table {
    fn drop(&mut self) {
        unsafe { sys::nftnl_table_free(self.table.as_ptr()) };
    }
}

/// Returns a buffer containing a netlink message which requests a list of all the netfilter
/// tables that are currently set.
pub fn get_tables_nlmsg(seq: u32) -> Vec<u8> {
    let mut buffer = vec![0; crate::nft_nlmsg_maxsize() as usize];
    let _ = unsafe {
        sys::nftnl_nlmsg_build_hdr(
            buffer.as_mut_ptr().cast::<c_char>(),
            libc::NFT_MSG_GETTABLE as u16,
            ProtoFamily::Unspec as u16,
            (libc::NLM_F_ROOT | libc::NLM_F_MATCH | libc::NLM_F_ACK) as u16,
            seq,
        )
    };
    buffer
}

/// A callback to parse the response for messages created with `get_tables_nlmsg`. This callback
/// extracts a set of applied table names.
pub fn get_tables_cb(header: &libc::nlmsghdr, tables: &mut HashSet<CString>) -> libc::c_int {
    unsafe {
        let nf_table = sys::nftnl_table_alloc();
        let err = sys::nftnl_table_nlmsg_parse(header, nf_table);
        if err < 0 {
            error!("Failed to parse nelink table message - {err}");
            sys::nftnl_table_free(nf_table);
            return err;
        }
        let table_name = CStr::from_ptr(sys::nftnl_table_get_str(
            nf_table,
            sys::NFTNL_TABLE_NAME as u16,
        ))
        .to_owned();
        tables.insert(table_name);
        sys::nftnl_table_free(nf_table);
    };
    1
}