use netlink_packet_core::{
emit_u32_be, emit_u64_be, parse_string, parse_u32_be, parse_u64_be,
DecodeError, DefaultNla, Emitable as _, ErrorContext as _, Nla, NlaBuffer,
NlasIterator, Parseable, NLA_F_NESTED,
};
use crate::nftables::attributes::{
list::VecListAttribute, ExpressionAttribute, ListAttribute,
};
const NFTA_RULE_UNSPEC: u16 = 0;
const NFTA_RULE_TABLE: u16 = 1;
const NFTA_RULE_CHAIN: u16 = 2;
const NFTA_RULE_HANDLE: u16 = 3;
const NFTA_RULE_EXPRESSIONS: u16 = 4;
const NFTA_RULE_COMPAT: u16 = 5;
const NFTA_RULE_POSITION: u16 = 6;
const NFTA_RULE_USERDATA: u16 = 7;
const NFTA_RULE_ID: u16 = 9;
const NFTA_RULE_POSITION_ID: u16 = 10;
const NFTA_RULE_CHAIN_ID: u16 = 11;
#[derive(Debug, PartialEq, Eq, Clone)]
#[non_exhaustive]
pub enum RuleAttribute {
Unspecified,
Table(String),
Chain(String),
Handle(u64),
Expressions(Vec<ListAttribute<ExpressionAttribute>>),
Compat(Vec<DefaultNla>),
Position(u64),
UserData(Vec<u8>),
Id(u32),
PositionId(u32),
ChainId(u32),
Other(DefaultNla),
}
impl Nla for RuleAttribute {
fn value_len(&self) -> usize {
match self {
Self::Unspecified => 0,
Self::Table(string) | Self::Chain(string) => string.len() + 1,
Self::Handle(_) | Self::Position(_) => 8,
Self::Expressions(attrs) => attrs.as_slice().buffer_len(),
Self::Compat(attrs) => attrs.as_slice().buffer_len(),
Self::UserData(bytes) => bytes.len(),
Self::Id(_) | Self::PositionId(_) | Self::ChainId(_) => 4,
Self::Other(attr) => attr.value_len(),
}
}
fn kind(&self) -> u16 {
match self {
Self::Unspecified => NFTA_RULE_UNSPEC,
Self::Table(_) => NFTA_RULE_TABLE,
Self::Chain(_) => NFTA_RULE_CHAIN,
Self::Handle(_) => NFTA_RULE_HANDLE,
Self::Expressions(_) => NFTA_RULE_EXPRESSIONS | NLA_F_NESTED,
Self::Compat(_) => NFTA_RULE_COMPAT | NLA_F_NESTED,
Self::Position(_) => NFTA_RULE_POSITION,
Self::UserData(_) => NFTA_RULE_USERDATA,
Self::Id(_) => NFTA_RULE_ID,
Self::PositionId(_) => NFTA_RULE_POSITION_ID,
Self::ChainId(_) => NFTA_RULE_CHAIN_ID,
Self::Other(attr) => attr.kind(),
}
}
fn emit_value(&self, buffer: &mut [u8]) {
match self {
Self::Unspecified => {}
Self::Table(string) | Self::Chain(string) => {
buffer[..string.len()].copy_from_slice(string.as_bytes());
buffer[string.len()] = 0;
}
Self::Handle(value) | Self::Position(value) => {
emit_u64_be(buffer, *value).unwrap()
}
Self::Expressions(attrs) => attrs.as_slice().emit(buffer),
Self::Compat(attrs) => attrs.as_slice().emit(buffer),
Self::UserData(bytes) => {
buffer[..bytes.len()].copy_from_slice(bytes.as_slice())
}
Self::Id(value)
| Self::PositionId(value)
| Self::ChainId(value) => emit_u32_be(buffer, *value).unwrap(),
Self::Other(attr) => attr.emit_value(buffer),
}
}
fn is_nested(&self) -> bool {
matches!(self, Self::Expressions(_) | Self::Compat(_))
|| (self.kind() & NLA_F_NESTED) != 0
}
}
impl<'a, T: AsRef<[u8]> + ?Sized> Parseable<NlaBuffer<&'a T>>
for RuleAttribute
{
fn parse(buf: &NlaBuffer<&'a T>) -> Result<Self, DecodeError> {
let payload = buf.value();
Ok(match buf.kind() {
NFTA_RULE_UNSPEC => Self::Unspecified,
NFTA_RULE_TABLE => Self::Table(
parse_string(payload)
.context("invalid NFTA_RULE_TABLE value")?,
),
NFTA_RULE_CHAIN => Self::Chain(
parse_string(payload)
.context("invalid NFTA_RULE_CHAIN value")?,
),
NFTA_RULE_HANDLE => Self::Handle(
parse_u64_be(payload)
.context("invalid NFTA_RULE_HANDLE value")?,
),
NFTA_RULE_EXPRESSIONS => Self::Expressions(
VecListAttribute::parse(payload)
.context("invalid NFTA_RULE_EXPRESSIONS value")?
.0,
),
NFTA_RULE_COMPAT => {
let mut nlas = vec![];
for nla in NlasIterator::new(payload) {
let nla = nla.context(format!(
"invalid NFTA_RULE_COMPAT payload {:?}",
payload
))?;
nlas.push(DefaultNla::parse(&nla)?);
}
Self::Compat(nlas)
}
NFTA_RULE_POSITION => Self::Position(
parse_u64_be(payload)
.context("invalid NFTA_RULE_POSITION value")?,
),
NFTA_RULE_USERDATA => Self::UserData(payload.to_vec()),
NFTA_RULE_ID => Self::Id(
parse_u32_be(payload).context("invalid NFTA_RULE_ID value")?,
),
NFTA_RULE_POSITION_ID => Self::PositionId(
parse_u32_be(payload)
.context("invalid NFTA_RULE_POSITION_ID value")?,
),
NFTA_RULE_CHAIN_ID => Self::ChainId(
parse_u32_be(payload)
.context("invalid NFTA_RULE_CHAIN_ID value")?,
),
_ => Self::Other(DefaultNla::parse(buf)?),
})
}
}