From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from firstgate.proxmox.com (firstgate.proxmox.com [212.224.123.68]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by lists.proxmox.com (Postfix) with ESMTPS id 548A291179 for ; Wed, 3 Apr 2024 12:47:54 +0200 (CEST) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id 2E012162C6 for ; Wed, 3 Apr 2024 12:47:24 +0200 (CEST) Received: from proxmox-new.maurer-it.com (proxmox-new.maurer-it.com [94.136.29.106]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by firstgate.proxmox.com (Proxmox) with ESMTPS for ; Wed, 3 Apr 2024 12:47:23 +0200 (CEST) Received: from proxmox-new.maurer-it.com (localhost.localdomain [127.0.0.1]) by proxmox-new.maurer-it.com (Proxmox) with ESMTP id CB06344D2A for ; Wed, 3 Apr 2024 12:47:22 +0200 (CEST) Content-Type: text/plain; charset=UTF-8 Date: Wed, 03 Apr 2024 12:47:21 +0200 Message-Id: Cc: "Wolfgang Bumiller" To: "Proxmox VE development discussion" From: "Max Carrara" Content-Transfer-Encoding: quoted-printable Mime-Version: 1.0 X-Mailer: aerc 0.17.0-72-g6a84f1331f1c References: <20240402171629.536804-1-s.hanreich@proxmox.com> <20240402171629.536804-22-s.hanreich@proxmox.com> In-Reply-To: <20240402171629.536804-22-s.hanreich@proxmox.com> X-SPAM-LEVEL: Spam detection results: 0 AWL 0.028 Adjusted score from AWL reputation of From: address BAYES_00 -1.9 Bayes spam probability is 0 to 1% DMARC_MISSING 0.1 Missing DMARC policy KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_PASS -0.001 SPF: sender matches SPF record Subject: Re: [pve-devel] [PATCH proxmox-firewall 21/37] nftables: statement: add types X-BeenThere: pve-devel@lists.proxmox.com X-Mailman-Version: 2.1.29 Precedence: list List-Id: Proxmox VE development discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 03 Apr 2024 10:47:54 -0000 On Tue Apr 2, 2024 at 7:16 PM CEST, Stefan Hanreich wrote: > 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 > Signed-off-by: Stefan Hanreich > --- > 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 =3D ["dep:proxmox-ve-config"] > =20 > [dependencies] > log =3D "0.4" > +anyhow =3D "1" > =20 > serde =3D { version =3D "1", features =3D [ "derive" ] } > serde_json =3D "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; > =20 > pub use expression::Expression; > +pub use statement::Statement; > diff --git a/proxmox-nftables/src/statement.rs b/proxmox-nftables/src/sta= tement.rs > new file mode 100644 > index 0000000..e569f33 > --- /dev/null > +++ b/proxmox-nftables/src/statement.rs > @@ -0,0 +1,321 @@ > +use anyhow::{bail, Error}; Hmm, you don't use either here - you sure you didn't mean to introduce `anyhow` later? > +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 =3D "lowercase")] > +pub enum Statement { > + Match(Match), > + Mangle(Mangle), > + Limit(Limit), > + Notrack(Null), > + Reject(Reject), > + Set(Set), > + Log(Log), > + #[serde(rename =3D "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) -> Self { > + Statement::Verdict(Verdict::Jump { > + target: target.into(), > + }) > + } > + > + pub fn goto(target: impl Into) -> Self { > + Statement::Verdict(Verdict::Goto { > + target: target.into(), > + }) > + } > +} > + > +impl From for Statement { > + #[inline] > + fn from(m: Match) -> Statement { > + Statement::Match(m) > + } > +} > + > +impl From for Statement { > + #[inline] > + fn from(m: Mangle) -> Statement { > + Statement::Mangle(m) > + } > +} > + > +impl From for Statement { > + #[inline] > + fn from(m: Reject) -> Statement { > + Statement::Reject(m) > + } > +} > + > +impl From for Statement { > + #[inline] > + fn from(m: Set) -> Statement { > + Statement::Set(m) > + } > +} > + > +impl From for Statement { > + #[inline] > + fn from(m: Vmap) -> Statement { > + Statement::Vmap(m) > + } > +} > + > +impl From for Statement { > + #[inline] > + fn from(log: Log) -> Statement { > + Statement::Log(log) > + } > +} > + > +impl> From for Statement { > + #[inline] > + fn from(limit: T) -> Statement { > + Statement::Limit(limit.into()) > + } > +} > + > +#[derive(Clone, Debug, Deserialize, Serialize)] > +#[serde(rename_all =3D "lowercase")] > +pub enum RejectType { > + #[serde(rename =3D "tcp reset")] > + TcpRst, > + IcmpX, > + Icmp, > + IcmpV6, > +} > + > +#[derive(Clone, Debug, Default, Deserialize, Serialize)] > +pub struct Reject { > + #[serde(rename =3D "type", skip_serializing_if =3D "Option::is_none"= )] > + ty: Option, > + #[serde(skip_serializing_if =3D "Option::is_none")] > + expr: Option, > +} > + > +#[derive(Clone, Debug, Default, Deserialize, Serialize)] > +#[serde(rename_all =3D "kebab-case")] > +pub struct Log { > + #[serde(skip_serializing_if =3D "Option::is_none")] > + prefix: Option, > + > + #[serde(skip_serializing_if =3D "Option::is_none")] > + group: Option, > + > + #[serde(skip_serializing_if =3D "Option::is_none")] > + snaplen: Option, > + > + #[serde(skip_serializing_if =3D "Option::is_none")] > + queue_threshold: Option, > + > + #[serde(skip_serializing_if =3D "Option::is_none")] > + level: Option, > + > + #[serde(default, skip_serializing_if =3D "Vec::is_empty")] > + flags: NfVec, > +} > + > +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 =3D "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 =3D> 0, > + LogLevel::Alert =3D> 1, > + LogLevel::Crit =3D> 2, > + LogLevel::Err =3D> 3, > + LogLevel::Warn =3D> 4, > + LogLevel::Notice =3D> 5, > + LogLevel::Info =3D> 6, > + LogLevel::Debug =3D> 7, > + LogLevel::Audit =3D> 7, > + } > + } > +} > + > +#[derive(Clone, Debug, Deserialize, Serialize)] > +#[serde(rename_all =3D "lowercase")] > +pub enum LogFlag { > + #[serde(rename =3D "tcp sequence")] > + TcpSequence, > + #[serde(rename =3D "tcp options")] > + TcpOptions, > + #[serde(rename =3D "ip options")] > + IpOptions, > + > + Skuid, > + Ether, > + All, > +} > + > +#[derive(Clone, Debug, Deserialize, Serialize)] > +#[serde(untagged)] > +pub enum Limit { > + Named(String), > + Anonymous(AnonymousLimit), > +} > + > +impl> From 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 =3D "Option::is_none")] > + pub rate_unit: Option, > + > + pub per: RateTimescale, > + > + #[serde(skip_serializing_if =3D "Option::is_none")] > + pub burst: Option, > + > + #[serde(skip_serializing_if =3D "Option::is_none")] > + pub burst_unit: Option, > + > + #[serde(skip_serializing_if =3D "Option::is_none")] > + pub inv: Option, > +} > + > +#[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, right: impl In= to) -> Self { > + Self { > + op, > + left: left.into(), > + right: right.into(), > + } > + } > + > + pub fn new_eq(left: impl Into, right: impl Into) -> Self { > + Self::new(Operator::Eq, left, right) > + } > + > + pub fn new_ne(left: impl Into, right: impl Into) -> Self { > + Self::new(Operator::Ne, left, right) > + } > +} > + > +#[derive(Clone, Debug, Deserialize, Serialize)] > +pub enum Operator { > + #[serde(rename =3D "&")] > + And, > + #[serde(rename =3D "|")] > + Or, > + #[serde(rename =3D "^")] > + Xor, > + #[serde(rename =3D "<<")] > + ShiftLeft, > + #[serde(rename =3D ">>")] > + ShiftRight, > + #[serde(rename =3D "=3D=3D")] > + Eq, > + #[serde(rename =3D "!=3D")] > + Ne, > + #[serde(rename =3D "<")] > + Lt, > + #[serde(rename =3D ">")] > + Gt, > + #[serde(rename =3D "<=3D")] > + Le, > + #[serde(rename =3D ">=3D")] > + Ge, > + #[serde(rename =3D "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) -> Self { > + Self { > + key: Meta::new("mark").into(), > + value: value.into(), > + } > + } > +} > + > +#[derive(Clone, Debug, Deserialize, Serialize)] > +#[serde(rename_all =3D "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>, > +} > diff --git a/proxmox-nftables/src/types.rs b/proxmox-nftables/src/types.r= s > index 942c866..b99747b 100644 > --- a/proxmox-nftables/src/types.rs > +++ b/proxmox-nftables/src/types.rs > @@ -30,6 +30,23 @@ impl Display for Verdict { > } > } > =20 > +#[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 =3D "lowercase")] > +pub enum RateTimescale { > + #[default] > + Second, > + Minute, > + Hour, > + Day, > +} > + > #[derive(Clone, Debug, Deserialize, Serialize)] > pub struct ElemConfig { > timeout: Option,