use crate::{MsgType, Table};
use crate::sys::{self as sys, libc};
#[cfg(feature = "query")]
use std::convert::TryFrom;
use std::{
ffi::{c_void, CStr, CString},
fmt,
os::raw::c_char,
rc::Rc,
};
pub type Priority = i32;
/// The netfilter event hooks a chain can register for.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
#[repr(u16)]
pub enum Hook {
/// Hook into the pre-routing stage of netfilter. Corresponds to `NF_INET_PRE_ROUTING`.
PreRouting = libc::NF_INET_PRE_ROUTING as u16,
/// Hook into the input stage of netfilter. Corresponds to `NF_INET_LOCAL_IN`.
In = libc::NF_INET_LOCAL_IN as u16,
/// Hook into the forward stage of netfilter. Corresponds to `NF_INET_FORWARD`.
Forward = libc::NF_INET_FORWARD as u16,
/// Hook into the output stage of netfilter. Corresponds to `NF_INET_LOCAL_OUT`.
Out = libc::NF_INET_LOCAL_OUT as u16,
/// Hook into the post-routing stage of netfilter. Corresponds to `NF_INET_POST_ROUTING`.
PostRouting = libc::NF_INET_POST_ROUTING as u16,
}
/// A chain policy. Decides what to do with a packet that was processed by the chain but did not
/// match any rules.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
#[repr(u32)]
pub enum Policy {
/// Accept the packet.
Accept = libc::NF_ACCEPT as u32,
/// Drop the packet.
Drop = libc::NF_DROP as u32,
}
/// Base chain type.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum ChainType {
/// Used to filter packets.
/// Supported protocols: ip, ip6, inet, arp, and bridge tables.
Filter,
/// Used to reroute packets if IP headers or packet marks are modified.
/// Supported protocols: ip, and ip6 tables.
Route,
/// Used to perform NAT.
/// Supported protocols: ip, and ip6 tables.
Nat,
}
impl ChainType {
fn as_c_str(&self) -> &'static [u8] {
match *self {
ChainType::Filter => b"filter\0",
ChainType::Route => b"route\0",
ChainType::Nat => b"nat\0",
}
}
}
/// Abstraction of a `nftnl_chain`. Chains reside inside [`Table`]s and they hold [`Rule`]s.
///
/// There are two types of chains, "base chain" and "regular chain". See [`set_hook`] for more
/// details.
///
/// [`Table`]: struct.Table.html
/// [`Rule`]: struct.Rule.html
/// [`set_hook`]: #method.set_hook
pub struct Chain {
pub(crate) chain: *mut sys::nftnl_chain,
pub(crate) table: Rc
,
}
impl Chain {
/// Creates a new chain instance inside the given [`Table`] and with the given name.
///
/// [`Table`]: struct.Table.html
pub fn new>(name: &T, table: Rc) -> Chain {
unsafe {
let chain = try_alloc!(sys::nftnl_chain_alloc());
sys::nftnl_chain_set_u32(
chain,
sys::NFTNL_CHAIN_FAMILY as u16,
table.get_family() as u32,
);
sys::nftnl_chain_set_str(
chain,
sys::NFTNL_CHAIN_TABLE as u16,
table.get_name().as_ptr(),
);
sys::nftnl_chain_set_str(chain, sys::NFTNL_CHAIN_NAME as u16, name.as_ref().as_ptr());
Chain { chain, table }
}
}
pub unsafe fn from_raw(chain: *mut sys::nftnl_chain, table: Rc) -> Self {
Chain { chain, table }
}
/// Sets the hook and priority for this chain. Without calling this method the chain will
/// become a "regular chain" without any hook and will thus not receive any traffic unless
/// some rule forward packets to it via goto or jump verdicts.
///
/// By calling `set_hook` with a hook the chain that is created will be registered with that
/// hook and is thus a "base chain". A "base chain" is an entry point for packets from the
/// networking stack.
pub fn set_hook(&mut self, hook: Hook, priority: Priority) {
unsafe {
sys::nftnl_chain_set_u32(self.chain, sys::NFTNL_CHAIN_HOOKNUM as u16, hook as u32);
sys::nftnl_chain_set_s32(self.chain, sys::NFTNL_CHAIN_PRIO as u16, priority);
}
}
/// Set the type of a base chain. This only applies if the chain has been registered
/// with a hook by calling `set_hook`.
pub fn set_type(&mut self, chain_type: ChainType) {
unsafe {
sys::nftnl_chain_set_str(
self.chain,
sys::NFTNL_CHAIN_TYPE as u16,
chain_type.as_c_str().as_ptr() as *const c_char,
);
}
}
/// Sets the default policy for this chain. That means what action netfilter will apply to
/// packets processed by this chain, but that did not match any rules in it.
pub fn set_policy(&mut self, policy: Policy) {
unsafe {
sys::nftnl_chain_set_u32(self.chain, sys::NFTNL_CHAIN_POLICY as u16, policy as u32);
}
}
/// Returns the userdata of this chain.
pub fn get_userdata(&self) -> Option<&CStr> {
unsafe {
let ptr = sys::nftnl_chain_get_str(self.chain, sys::NFTNL_CHAIN_USERDATA as u16);
if ptr == std::ptr::null() {
return None;
}
Some(CStr::from_ptr(ptr))
}
}
/// Updates the userdata of this chain.
pub fn set_userdata(&self, data: &CStr) {
unsafe {
sys::nftnl_chain_set_str(self.chain, sys::NFTNL_CHAIN_USERDATA as u16, data.as_ptr());
}
}
/// Returns the name of this chain.
pub fn get_name(&self) -> &CStr {
unsafe {
let ptr = sys::nftnl_chain_get_str(self.chain, sys::NFTNL_CHAIN_NAME as u16);
if ptr.is_null() {
panic!("Impossible situation: retrieving the name of a chain failed")
} else {
CStr::from_ptr(ptr)
}
}
}
/// Returns a textual description of the chain.
pub fn get_str(&self) -> CString {
let mut descr_buf = vec![0i8; 4096];
unsafe {
sys::nftnl_chain_snprintf(
descr_buf.as_mut_ptr() as *mut c_char,
(descr_buf.len() - 1) as u64,
self.chain,
sys::NFTNL_OUTPUT_DEFAULT,
0,
);
CStr::from_ptr(descr_buf.as_ptr() as *mut c_char).to_owned()
}
}
/// Returns a reference to the [`Table`] this chain belongs to.
///
/// [`Table`]: struct.Table.html
pub fn get_table(&self) -> Rc {
self.table.clone()
}
#[cfg(feature = "unsafe-raw-handles")]
/// Returns the raw handle.
pub fn as_ptr(&self) -> *const sys::nftnl_chain {
self.chain as *const sys::nftnl_chain
}
#[cfg(feature = "unsafe-raw-handles")]
/// Returns a mutable version of the raw handle.
pub fn as_mut_ptr(&mut self) -> *mut sys::nftnl_chain {
self.chain
}
}
impl fmt::Debug for Chain {
/// Returns a string representation of the chain.
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(fmt, "{:?}", self.get_str())
}
}
impl PartialEq for Chain {
fn eq(&self, other: &Self) -> bool {
self.get_table() == other.get_table() && self.get_name() == other.get_name()
}
}
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,
} | libc::NLM_F_ACK as u16;
let header = sys::nftnl_nlmsg_build_hdr(
buf as *mut c_char,
raw_msg_type as u16,
self.table.get_family() as u16,
flags,
seq,
);
sys::nftnl_chain_nlmsg_build_payload(header, self.chain);
}
}
impl Drop for Chain {
fn drop(&mut self) {
unsafe { sys::nftnl_chain_free(self.chain) };
}
}
#[cfg(feature = "query")]
pub fn get_chains_cb<'a>(
header: &libc::nlmsghdr,
(table, chains): &mut (&Rc, &mut Vec),
) -> libc::c_int {
unsafe {
let chain = sys::nftnl_chain_alloc();
if chain == std::ptr::null_mut() {
return mnl::mnl_sys::MNL_CB_ERROR;
}
let err = sys::nftnl_chain_nlmsg_parse(header, chain);
if err < 0 {
error!("Failed to parse nelink chain message - {}", err);
sys::nftnl_chain_free(chain);
return err;
}
let table_name = CStr::from_ptr(sys::nftnl_chain_get_str(
chain,
sys::NFTNL_CHAIN_TABLE as u16,
));
let family = sys::nftnl_chain_get_u32(chain, sys::NFTNL_CHAIN_FAMILY as u16);
let family = match crate::ProtoFamily::try_from(family as i32) {
Ok(family) => family,
Err(crate::InvalidProtocolFamily) => {
error!("The netlink table didn't have a valid protocol family !?");
sys::nftnl_chain_free(chain);
return mnl::mnl_sys::MNL_CB_ERROR;
}
};
if table_name != table.get_name() {
sys::nftnl_chain_free(chain);
return mnl::mnl_sys::MNL_CB_OK;
}
if family != crate::ProtoFamily::Unspec && family != table.get_family() {
sys::nftnl_chain_free(chain);
return mnl::mnl_sys::MNL_CB_OK;
}
chains.push(Chain::from_raw(chain, table.clone()));
}
mnl::mnl_sys::MNL_CB_OK
}
#[cfg(feature = "query")]
pub fn list_chains_for_table(table: Rc) -> Result, crate::query::Error> {
crate::query::list_objects_with_data(libc::NFT_MSG_GETCHAIN as u16, get_chains_cb, &table, None)
}