use super::connection::{parse_chain, parse_flowtable, parse_rule, parse_table};
use super::types::{ChainInfo, Family, Flowtable, RuleInfo, Table};
use super::{
NFGENMSG_HDRLEN, NFNL_SUBSYS_NFTABLES, NFT_MSG_DELCHAIN, NFT_MSG_DELFLOWTABLE,
NFT_MSG_DELRULE, NFT_MSG_DELTABLE, NFT_MSG_NEWCHAIN, NFT_MSG_NEWFLOWTABLE, NFT_MSG_NEWRULE,
NFT_MSG_NEWTABLE,
};
pub const NFNLGRP_NFTABLES: u32 = 7;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum NftablesGroup {
All,
}
impl NftablesGroup {
pub fn to_kernel_group(self) -> u32 {
match self {
Self::All => NFNLGRP_NFTABLES,
}
}
}
#[derive(Debug, Clone)]
#[non_exhaustive]
pub enum NftablesEvent {
NewTable(Table),
DelTable(Table),
NewChain(ChainInfo),
DelChain(ChainInfo),
NewRule(RuleInfo),
DelRule(RuleInfo),
NewFlowtable(Flowtable),
DelFlowtable(Flowtable),
}
pub(crate) fn parse_nftables_event(msg_type: u16, body: &[u8]) -> Option<NftablesEvent> {
if (msg_type >> 8) != NFNL_SUBSYS_NFTABLES {
return None;
}
if body.len() < NFGENMSG_HDRLEN {
return None;
}
let family = Family::from_u8(body[0]).unwrap_or(Family::Inet);
let attrs = &body[NFGENMSG_HDRLEN..];
match (msg_type & 0xFF) as u8 {
NFT_MSG_NEWTABLE => parse_table(attrs, family).map(NftablesEvent::NewTable),
NFT_MSG_DELTABLE => parse_table(attrs, family).map(NftablesEvent::DelTable),
NFT_MSG_NEWCHAIN => parse_chain(attrs, family).map(NftablesEvent::NewChain),
NFT_MSG_DELCHAIN => parse_chain(attrs, family).map(NftablesEvent::DelChain),
NFT_MSG_NEWRULE => parse_rule(attrs, family).map(NftablesEvent::NewRule),
NFT_MSG_DELRULE => parse_rule(attrs, family).map(NftablesEvent::DelRule),
NFT_MSG_NEWFLOWTABLE => parse_flowtable(attrs, family).map(NftablesEvent::NewFlowtable),
NFT_MSG_DELFLOWTABLE => parse_flowtable(attrs, family).map(NftablesEvent::DelFlowtable),
_ => None,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn group_maps_to_kernel_id() {
assert_eq!(NftablesGroup::All.to_kernel_group(), 7);
}
#[test]
fn parse_rejects_wrong_subsystem() {
let msg_type = (1u16 << 8) | NFT_MSG_NEWTABLE as u16;
let body = vec![0u8; NFGENMSG_HDRLEN];
assert!(parse_nftables_event(msg_type, &body).is_none());
}
#[test]
fn parse_rejects_truncated_body() {
let msg_type = (NFNL_SUBSYS_NFTABLES << 8) | NFT_MSG_NEWTABLE as u16;
let body: Vec<u8> = vec![0; 2]; assert!(parse_nftables_event(msg_type, &body).is_none());
}
#[test]
fn parse_unknown_msg_type_returns_none() {
let msg_type = (NFNL_SUBSYS_NFTABLES << 8) | 9u16;
let body = vec![0u8; NFGENMSG_HDRLEN];
assert!(parse_nftables_event(msg_type, &body).is_none());
}
}