Adds an enum containing most of the statements defined in the nftables-json schema [1].
[1] https://manpages.debian.org/bookworm/libnftables1/libnftables-json.5.en.html#STATEMENTS Co-authored-by: Wolfgang Bumiller <w.bumil...@proxmox.com> Signed-off-by: Stefan Hanreich <s.hanre...@proxmox.com> --- proxmox-nftables/Cargo.toml | 1 + proxmox-nftables/src/lib.rs | 2 + proxmox-nftables/src/statement.rs | 321 ++++++++++++++++++++++++++++++ proxmox-nftables/src/types.rs | 17 ++ 4 files changed, 341 insertions(+) create mode 100644 proxmox-nftables/src/statement.rs diff --git a/proxmox-nftables/Cargo.toml b/proxmox-nftables/Cargo.toml index 7e607e8..153716d 100644 --- a/proxmox-nftables/Cargo.toml +++ b/proxmox-nftables/Cargo.toml @@ -15,6 +15,7 @@ config-ext = ["dep:proxmox-ve-config"] [dependencies] log = "0.4" +anyhow = "1" serde = { version = "1", features = [ "derive" ] } serde_json = "1" diff --git a/proxmox-nftables/src/lib.rs b/proxmox-nftables/src/lib.rs index 712858b..40f6bab 100644 --- a/proxmox-nftables/src/lib.rs +++ b/proxmox-nftables/src/lib.rs @@ -1,5 +1,7 @@ pub mod expression; pub mod helper; +pub mod statement; pub mod types; pub use expression::Expression; +pub use statement::Statement; diff --git a/proxmox-nftables/src/statement.rs b/proxmox-nftables/src/statement.rs new file mode 100644 index 0000000..e569f33 --- /dev/null +++ b/proxmox-nftables/src/statement.rs @@ -0,0 +1,321 @@ +use anyhow::{bail, Error}; +use serde::{Deserialize, Serialize}; + +use crate::expression::Meta; +use crate::helper::{NfVec, Null}; +use crate::types::{RateTimescale, RateUnit, Verdict}; +use crate::Expression; + +#[derive(Clone, Debug, Deserialize, Serialize)] +#[serde(rename_all = "lowercase")] +pub enum Statement { + Match(Match), + Mangle(Mangle), + Limit(Limit), + Notrack(Null), + Reject(Reject), + Set(Set), + Log(Log), + #[serde(rename = "ct helper")] + CtHelper(String), + Vmap(Vmap), + Comment(String), + + #[serde(untagged)] + Verdict(Verdict), +} + +impl Statement { + pub const fn make_accept() -> Self { + Statement::Verdict(Verdict::Accept(Null)) + } + + pub const fn make_drop() -> Self { + Statement::Verdict(Verdict::Drop(Null)) + } + + pub const fn make_return() -> Self { + Statement::Verdict(Verdict::Return(Null)) + } + + pub const fn make_continue() -> Self { + Statement::Verdict(Verdict::Continue(Null)) + } + + pub fn jump(target: impl Into<String>) -> Self { + Statement::Verdict(Verdict::Jump { + target: target.into(), + }) + } + + pub fn goto(target: impl Into<String>) -> Self { + Statement::Verdict(Verdict::Goto { + target: target.into(), + }) + } +} + +impl From<Match> for Statement { + #[inline] + fn from(m: Match) -> Statement { + Statement::Match(m) + } +} + +impl From<Mangle> for Statement { + #[inline] + fn from(m: Mangle) -> Statement { + Statement::Mangle(m) + } +} + +impl From<Reject> for Statement { + #[inline] + fn from(m: Reject) -> Statement { + Statement::Reject(m) + } +} + +impl From<Set> for Statement { + #[inline] + fn from(m: Set) -> Statement { + Statement::Set(m) + } +} + +impl From<Vmap> for Statement { + #[inline] + fn from(m: Vmap) -> Statement { + Statement::Vmap(m) + } +} + +impl From<Log> for Statement { + #[inline] + fn from(log: Log) -> Statement { + Statement::Log(log) + } +} + +impl<T: Into<Limit>> From<T> for Statement { + #[inline] + fn from(limit: T) -> Statement { + Statement::Limit(limit.into()) + } +} + +#[derive(Clone, Debug, Deserialize, Serialize)] +#[serde(rename_all = "lowercase")] +pub enum RejectType { + #[serde(rename = "tcp reset")] + TcpRst, + IcmpX, + Icmp, + IcmpV6, +} + +#[derive(Clone, Debug, Default, Deserialize, Serialize)] +pub struct Reject { + #[serde(rename = "type", skip_serializing_if = "Option::is_none")] + ty: Option<RejectType>, + #[serde(skip_serializing_if = "Option::is_none")] + expr: Option<Expression>, +} + +#[derive(Clone, Debug, Default, Deserialize, Serialize)] +#[serde(rename_all = "kebab-case")] +pub struct Log { + #[serde(skip_serializing_if = "Option::is_none")] + prefix: Option<String>, + + #[serde(skip_serializing_if = "Option::is_none")] + group: Option<i64>, + + #[serde(skip_serializing_if = "Option::is_none")] + snaplen: Option<i64>, + + #[serde(skip_serializing_if = "Option::is_none")] + queue_threshold: Option<i64>, + + #[serde(skip_serializing_if = "Option::is_none")] + level: Option<LogLevel>, + + #[serde(default, skip_serializing_if = "Vec::is_empty")] + flags: NfVec<LogFlag>, +} + +impl Log { + pub fn new_nflog(prefix: String, group: i64) -> Self { + Self { + prefix: Some(prefix), + group: Some(group), + ..Default::default() + } + } +} + +#[derive(Clone, Debug, Deserialize, Serialize)] +#[serde(rename_all = "lowercase")] +pub enum LogLevel { + Emerg, + Alert, + Crit, + Err, + Warn, + Notice, + Info, + Debug, + Audit, +} + +impl LogLevel { + pub fn nflog_level(&self) -> u8 { + match self { + LogLevel::Emerg => 0, + LogLevel::Alert => 1, + LogLevel::Crit => 2, + LogLevel::Err => 3, + LogLevel::Warn => 4, + LogLevel::Notice => 5, + LogLevel::Info => 6, + LogLevel::Debug => 7, + LogLevel::Audit => 7, + } + } +} + +#[derive(Clone, Debug, Deserialize, Serialize)] +#[serde(rename_all = "lowercase")] +pub enum LogFlag { + #[serde(rename = "tcp sequence")] + TcpSequence, + #[serde(rename = "tcp options")] + TcpOptions, + #[serde(rename = "ip options")] + IpOptions, + + Skuid, + Ether, + All, +} + +#[derive(Clone, Debug, Deserialize, Serialize)] +#[serde(untagged)] +pub enum Limit { + Named(String), + Anonymous(AnonymousLimit), +} + +impl<T: Into<AnonymousLimit>> From<T> for Limit { + fn from(value: T) -> Self { + Limit::Anonymous(value.into()) + } +} + +#[derive(Clone, Debug, Deserialize, Serialize, Default)] +pub struct AnonymousLimit { + pub rate: i64, + + #[serde(skip_serializing_if = "Option::is_none")] + pub rate_unit: Option<RateUnit>, + + pub per: RateTimescale, + + #[serde(skip_serializing_if = "Option::is_none")] + pub burst: Option<i64>, + + #[serde(skip_serializing_if = "Option::is_none")] + pub burst_unit: Option<RateUnit>, + + #[serde(skip_serializing_if = "Option::is_none")] + pub inv: Option<bool>, +} + +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct Vmap { + key: Expression, + data: Expression, +} + +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct Match { + op: Operator, + left: Expression, + right: Expression, +} + +impl Match { + pub fn new(op: Operator, left: impl Into<Expression>, right: impl Into<Expression>) -> Self { + Self { + op, + left: left.into(), + right: right.into(), + } + } + + pub fn new_eq(left: impl Into<Expression>, right: impl Into<Expression>) -> Self { + Self::new(Operator::Eq, left, right) + } + + pub fn new_ne(left: impl Into<Expression>, right: impl Into<Expression>) -> Self { + Self::new(Operator::Ne, left, right) + } +} + +#[derive(Clone, Debug, Deserialize, Serialize)] +pub enum Operator { + #[serde(rename = "&")] + And, + #[serde(rename = "|")] + Or, + #[serde(rename = "^")] + Xor, + #[serde(rename = "<<")] + ShiftLeft, + #[serde(rename = ">>")] + ShiftRight, + #[serde(rename = "==")] + Eq, + #[serde(rename = "!=")] + Ne, + #[serde(rename = "<")] + Lt, + #[serde(rename = ">")] + Gt, + #[serde(rename = "<=")] + Le, + #[serde(rename = ">=")] + Ge, + #[serde(rename = "in")] + In, +} + +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct Mangle { + pub key: Expression, + pub value: Expression, +} + +impl Mangle { + pub fn set_mark(value: impl Into<Expression>) -> Self { + Self { + key: Meta::new("mark").into(), + value: value.into(), + } + } +} + +#[derive(Clone, Debug, Deserialize, Serialize)] +#[serde(rename_all = "lowercase")] +pub enum SetOperation { + Add, + Update, +} + +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct Set { + pub op: SetOperation, + pub elem: Expression, + pub set: String, + pub stmt: Option<NfVec<Statement>>, +} diff --git a/proxmox-nftables/src/types.rs b/proxmox-nftables/src/types.rs index 942c866..b99747b 100644 --- a/proxmox-nftables/src/types.rs +++ b/proxmox-nftables/src/types.rs @@ -30,6 +30,23 @@ impl Display for Verdict { } } +#[derive(Clone, Debug, Deserialize, Serialize)] +pub enum RateUnit { + Packets, + Bytes, +} + +#[derive(Clone, Debug, Deserialize, Serialize, Default)] +#[cfg_attr(test, derive(Eq, PartialEq))] +#[serde(rename_all = "lowercase")] +pub enum RateTimescale { + #[default] + Second, + Minute, + Hour, + Day, +} + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct ElemConfig { timeout: Option<i64>, -- 2.39.2 _______________________________________________ pve-devel mailing list pve-devel@lists.proxmox.com https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel