use crate::{MsgType, Table};
use nftnl_sys::{self as sys, libc};
use std::{
ffi::{CStr, c_void},
fmt,
os::raw::c_char,
ptr,
};
pub type Priority = i32;
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
#[repr(u16)]
pub enum Hook {
PreRouting = libc::NF_INET_PRE_ROUTING as u16,
In = libc::NF_INET_LOCAL_IN as u16,
Forward = libc::NF_INET_FORWARD as u16,
Out = libc::NF_INET_LOCAL_OUT as u16,
PostRouting = libc::NF_INET_POST_ROUTING as u16,
Ingress = libc::NF_INET_INGRESS as u16,
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
#[repr(u32)]
pub enum Policy {
Accept = libc::NF_ACCEPT as u32,
Drop = libc::NF_DROP as u32,
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum ChainType {
Filter,
Route,
Nat,
}
impl ChainType {
fn as_c_str(&self) -> &'static CStr {
match *self {
ChainType::Filter => c"filter",
ChainType::Route => c"route",
ChainType::Nat => c"nat",
}
}
}
pub struct Chain<'a> {
chain: ptr::NonNull<sys::nftnl_chain>,
table: &'a Table,
}
unsafe impl Send for Chain<'_> {}
unsafe impl Sync for Chain<'_> {}
impl<'a> Chain<'a> {
pub fn new<T: AsRef<CStr>>(name: T, table: &'a Table) -> Chain<'a> {
unsafe {
let chain = try_alloc!(sys::nftnl_chain_alloc());
sys::nftnl_chain_set_u32(
chain.as_ptr(),
sys::NFTNL_CHAIN_FAMILY as u16,
table.get_family() as u32,
);
sys::nftnl_chain_set_str(
chain.as_ptr(),
sys::NFTNL_CHAIN_TABLE as u16,
table.get_name().as_ptr(),
);
sys::nftnl_chain_set_str(
chain.as_ptr(),
sys::NFTNL_CHAIN_NAME as u16,
name.as_ref().as_ptr(),
);
Chain { chain, table }
}
}
pub fn set_hook(&mut self, hook: Hook, priority: Priority) {
let chain = self.chain.as_ptr();
unsafe {
sys::nftnl_chain_set_u32(chain, sys::NFTNL_CHAIN_HOOKNUM as u16, hook as u32);
sys::nftnl_chain_set_s32(chain, sys::NFTNL_CHAIN_PRIO as u16, priority);
}
}
pub fn set_type(&mut self, chain_type: ChainType) {
unsafe {
sys::nftnl_chain_set_str(
self.chain.as_ptr(),
sys::NFTNL_CHAIN_TYPE as u16,
chain_type.as_c_str().as_ptr(),
);
}
}
pub fn set_policy(&mut self, policy: Policy) {
unsafe {
sys::nftnl_chain_set_u32(
self.chain.as_ptr(),
sys::NFTNL_CHAIN_POLICY as u16,
policy as u32,
);
}
}
pub fn set_device<T: AsRef<CStr>>(&mut self, device: T) {
unsafe {
sys::nftnl_chain_set_str(
self.chain.as_ptr(),
sys::NFTNL_CHAIN_DEV as u16,
device.as_ref().as_ptr(),
);
}
}
pub fn get_name(&self) -> &CStr {
unsafe {
let ptr = sys::nftnl_chain_get_str(self.chain.as_ptr(), sys::NFTNL_CHAIN_NAME as u16);
CStr::from_ptr(ptr)
}
}
pub fn get_table(&self) -> &Table {
self.table
}
}
impl fmt::Debug for Chain<'_> {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut buffer: [u8; 4096] = [0; 4096];
unsafe {
sys::nftnl_chain_snprintf(
buffer.as_mut_ptr().cast::<c_char>(),
buffer.len(),
self.chain.as_ptr(),
sys::NFTNL_OUTPUT_DEFAULT,
0,
);
}
let s = unsafe { CStr::from_ptr(buffer.as_ptr() as *const c_char) };
write!(fmt, "{s:?}")
}
}
unsafe impl crate::NlMsg for Chain<'_> {
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_NEWCHAIN,
MsgType::Del => libc::NFT_MSG_DELCHAIN,
};
let flags: u16 = match msg_type {
MsgType::Add => (libc::NLM_F_ACK | libc::NLM_F_CREATE) as u16,
MsgType::Del => libc::NLM_F_ACK as u16,
};
let header = unsafe {
sys::nftnl_nlmsg_build_hdr(
buf.cast::<c_char>(),
raw_msg_type as u16,
self.table.get_family() as u16,
flags,
seq,
)
};
unsafe { sys::nftnl_chain_nlmsg_build_payload(header, self.chain.as_ptr()) };
}
}
impl Drop for Chain<'_> {
fn drop(&mut self) {
unsafe { sys::nftnl_chain_free(self.chain.as_ptr()) };
}
}