public inbox for pve-devel@lists.proxmox.com
 help / color / mirror / Atom feed
From: Stefan Hanreich <s.hanreich@proxmox.com>
To: pve-devel@lists.proxmox.com
Cc: Stefan Hanreich <s.hanreich@proxmox.com>,
	Wolfgang Bumiller <w.bumiller@proxmox.com>
Subject: [pve-devel] [PATCH proxmox-firewall 23/37] nftables: commands: add types
Date: Tue,  2 Apr 2024 19:16:15 +0200	[thread overview]
Message-ID: <20240402171629.536804-24-s.hanreich@proxmox.com> (raw)
In-Reply-To: <20240402171629.536804-1-s.hanreich@proxmox.com>

Add rust types for most of the nftables commands as defined by
libnftables-json [1].

Different commands require different keys to be set for the same type
of object. E.g. deleting an object usually only requires a name +
name of the container (table/chain/rule). Creating an object usually
requires a few more keys, depending on the type of object created.

In order to be able to model the different objects for the different
commands, I've created specific models for a command where necessary.
Parts that are common across multiple commands (e.g. names) have been
moved to their own structs, so they can be reused.

[1] https://manpages.debian.org/bookworm/libnftables1/libnftables-json.5.en.html#COMMAND_OBJECTS

Co-authored-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
Signed-off-by: Stefan Hanreich <s.hanreich@proxmox.com>
---
 proxmox-nftables/src/command.rs | 221 ++++++++++
 proxmox-nftables/src/lib.rs     |   2 +
 proxmox-nftables/src/types.rs   | 755 +++++++++++++++++++++++++++++++-
 3 files changed, 977 insertions(+), 1 deletion(-)
 create mode 100644 proxmox-nftables/src/command.rs

diff --git a/proxmox-nftables/src/command.rs b/proxmox-nftables/src/command.rs
new file mode 100644
index 0000000..59163bc
--- /dev/null
+++ b/proxmox-nftables/src/command.rs
@@ -0,0 +1,221 @@
+use std::ops::{Deref, DerefMut};
+
+use crate::helper::Null;
+use crate::types::*;
+use serde::{Deserialize, Serialize};
+
+#[derive(Clone, Debug, Default, Deserialize, Serialize)]
+pub struct Commands {
+    nftables: Vec<Command>,
+}
+
+impl Commands {
+    pub fn new(commands: Vec<Command>) -> Self {
+        Self { nftables: commands }
+    }
+}
+
+impl Deref for Commands {
+    type Target = Vec<Command>;
+
+    fn deref(&self) -> &Self::Target {
+        &self.nftables
+    }
+}
+
+impl DerefMut for Commands {
+    fn deref_mut(&mut self) -> &mut Self::Target {
+        &mut self.nftables
+    }
+}
+
+#[derive(Clone, Debug, Deserialize, Serialize)]
+#[serde(rename_all = "lowercase")]
+pub enum Command {
+    Add(Add),
+    Create(Add),
+    Delete(Delete),
+    Flush(Flush),
+    List(List),
+    // Insert(super::Rule),
+    // Rename(RenameChain),
+    // Replace(super::Rule),
+}
+
+#[derive(Clone, Debug, Deserialize, Serialize)]
+#[serde(rename_all = "lowercase")]
+pub enum List {
+    Chains(Null),
+}
+
+impl List {
+    #[inline]
+    pub fn chains() -> Command {
+        Command::List(List::Chains(Null))
+    }
+}
+
+#[derive(Clone, Debug, Deserialize, Serialize)]
+#[serde(rename_all = "lowercase")]
+pub enum Add {
+    Table(AddTable),
+    Chain(AddChain),
+    Rule(AddRule),
+    Set(AddSet),
+    Map(AddMap),
+    Limit(AddLimit),
+    Element(AddElement),
+    #[serde(rename = "ct helper")]
+    CtHelper(AddCtHelper),
+}
+
+impl Add {
+    #[inline]
+    pub fn table(table: impl Into<AddTable>) -> Command {
+        Command::Add(Add::Table(table.into()))
+    }
+
+    #[inline]
+    pub fn chain(chain: impl Into<AddChain>) -> Command {
+        Command::Add(Add::Chain(chain.into()))
+    }
+
+    #[inline]
+    pub fn rule(rule: impl Into<AddRule>) -> Command {
+        Command::Add(Add::Rule(rule.into()))
+    }
+
+    #[inline]
+    pub fn set(set: impl Into<AddSet>) -> Command {
+        Command::Add(Add::Set(set.into()))
+    }
+
+    #[inline]
+    pub fn map(map: impl Into<AddMap>) -> Command {
+        Command::Add(Add::Map(map.into()))
+    }
+
+    #[inline]
+    pub fn limit(limit: impl Into<AddLimit>) -> Command {
+        Command::Add(Add::Limit(limit.into()))
+    }
+
+    #[inline]
+    pub fn element(element: impl Into<AddElement>) -> Command {
+        Command::Add(Add::Element(element.into()))
+    }
+
+    #[inline]
+    pub fn ct_helper(ct_helper: impl Into<AddCtHelper>) -> Command {
+        Command::Add(Add::CtHelper(ct_helper.into()))
+    }
+}
+
+#[derive(Clone, Debug, Deserialize, Serialize)]
+#[serde(rename_all = "lowercase")]
+pub enum Flush {
+    Table(TableName),
+    Chain(ChainName),
+    Set(SetName),
+    Map(SetName),
+    Ruleset(Null),
+}
+
+impl Flush {
+    #[inline]
+    pub fn table(table: impl Into<TableName>) -> Command {
+        Command::Flush(Flush::Table(table.into()))
+    }
+
+    #[inline]
+    pub fn chain(chain: impl Into<ChainName>) -> Command {
+        Command::Flush(Flush::Chain(chain.into()))
+    }
+
+    #[inline]
+    pub fn set(set: impl Into<SetName>) -> Command {
+        Command::Flush(Flush::Set(set.into()))
+    }
+
+    #[inline]
+    pub fn map(map: impl Into<SetName>) -> Command {
+        Command::Flush(Flush::Map(map.into()))
+    }
+
+    #[inline]
+    pub fn ruleset() -> Command {
+        Command::Flush(Flush::Ruleset(Null))
+    }
+}
+
+impl From<TableName> for Flush {
+    #[inline]
+    fn from(value: TableName) -> Self {
+        Flush::Table(value)
+    }
+}
+
+impl From<ChainName> for Flush {
+    #[inline]
+    fn from(value: ChainName) -> Self {
+        Flush::Chain(value)
+    }
+}
+
+#[derive(Clone, Debug, Deserialize, Serialize)]
+#[serde(rename_all = "lowercase")]
+pub enum Delete {
+    Table(TableName),
+    Chain(ChainName),
+}
+
+impl Delete {
+    #[inline]
+    pub fn table(table: impl Into<TableName>) -> Command {
+        Command::Delete(Delete::Table(table.into()))
+    }
+
+    #[inline]
+    pub fn chain(chain: impl Into<ChainName>) -> Command {
+        Command::Delete(Delete::Chain(chain.into()))
+    }
+}
+
+impl From<TableName> for Delete {
+    #[inline]
+    fn from(value: TableName) -> Self {
+        Delete::Table(value)
+    }
+}
+
+impl From<ChainName> for Delete {
+    #[inline]
+    fn from(value: ChainName) -> Self {
+        Delete::Chain(value)
+    }
+}
+
+#[derive(Clone, Debug, Deserialize, Serialize)]
+#[serde(rename_all = "lowercase")]
+pub enum ListOutput {
+    Metainfo(serde_json::Value),
+    // Table(super::AddTable),
+    Chain(ListChain),
+    // Rule(super::Rule),
+    // Set(super::Set),
+    // Map(super::Map),
+    // Element(super::SetElement),
+}
+
+#[derive(Clone, Debug, Deserialize, Serialize)]
+pub struct CommandOutput {
+    pub nftables: Vec<ListOutput>,
+}
+
+impl Deref for CommandOutput {
+    type Target = Vec<ListOutput>;
+
+    fn deref(&self) -> &Self::Target {
+        &self.nftables
+    }
+}
diff --git a/proxmox-nftables/src/lib.rs b/proxmox-nftables/src/lib.rs
index 40f6bab..60ddb3f 100644
--- a/proxmox-nftables/src/lib.rs
+++ b/proxmox-nftables/src/lib.rs
@@ -1,7 +1,9 @@
+pub mod command;
 pub mod expression;
 pub mod helper;
 pub mod statement;
 pub mod types;
 
+pub use command::Command;
 pub use expression::Expression;
 pub use statement::Statement;
diff --git a/proxmox-nftables/src/types.rs b/proxmox-nftables/src/types.rs
index b99747b..f9dc9b6 100644
--- a/proxmox-nftables/src/types.rs
+++ b/proxmox-nftables/src/types.rs
@@ -1,8 +1,90 @@
 use std::fmt::Display;
+use std::ops::{Deref, DerefMut};
+
+use crate::expression::IpFamily;
+use crate::helper::{NfVec, Null};
+use crate::{Expression, Statement};
 
 use serde::{Deserialize, Serialize};
 
-use crate::helper::Null;
+
+#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
+pub struct Handle(i32);
+
+#[derive(Clone, Copy, Debug, Eq, PartialEq, Deserialize, Serialize)]
+#[serde(rename_all = "lowercase")]
+pub enum TableFamily {
+    Ip,
+    Ip6,
+    Inet,
+    Arp,
+    Bridge,
+    Netdev,
+}
+serde_plain::derive_display_from_serialize!(TableFamily);
+
+impl TableFamily {
+    pub fn ip_families(&self) -> Vec<IpFamily> {
+        match self {
+            TableFamily::Ip => vec![IpFamily::Ip],
+            TableFamily::Ip6 => vec![IpFamily::Ip6],
+            _ => vec![IpFamily::Ip, IpFamily::Ip6],
+        }
+    }
+}
+
+#[derive(Clone, Copy, Debug, Eq, PartialEq, Deserialize, Serialize)]
+#[serde(rename_all = "snake_case")]
+pub enum ElementType {
+    Ifname,
+    Ipv4Addr,
+    Ipv6Addr,
+}
+serde_plain::derive_display_from_serialize!(ElementType);
+
+#[derive(Clone, Copy, Debug, Eq, PartialEq, Deserialize, Serialize)]
+#[serde(rename_all = "lowercase")]
+pub enum ChainType {
+    Filter,
+    Nat,
+    Route,
+}
+serde_plain::derive_display_from_serialize!(ChainType);
+
+#[derive(Clone, Copy, Debug, Eq, PartialEq, Deserialize, Serialize)]
+#[serde(rename_all = "lowercase")]
+pub enum SetPolicy {
+    Performance,
+    Memory,
+}
+serde_plain::derive_display_from_serialize!(SetPolicy);
+
+#[derive(Clone, Copy, Debug, Eq, PartialEq, Deserialize, Serialize)]
+#[serde(rename_all = "lowercase")]
+pub enum SetFlag {
+    Constant,
+    Interval,
+    Timeout,
+}
+serde_plain::derive_display_from_serialize!(SetFlag);
+
+#[derive(Clone, Copy, Debug, Eq, PartialEq, Deserialize, Serialize)]
+#[serde(rename_all = "snake_case")]
+pub enum OutputType {
+    Verdict,
+    Type(ElementType),
+}
+
+#[derive(Clone, Copy, Debug, Eq, PartialEq, Deserialize, Serialize)]
+#[serde(rename_all = "lowercase")]
+pub enum Hook {
+    Prerouting,
+    Input,
+    Forward,
+    Output,
+    Postrouting,
+}
+serde_plain::derive_display_from_serialize!(Hook);
 
 #[derive(Clone, Debug, Deserialize, Serialize)]
 #[serde(rename_all = "snake_case")]
@@ -30,6 +112,32 @@ impl Display for Verdict {
     }
 }
 
+#[derive(Clone, Debug, Deserialize, Serialize)]
+#[serde(rename_all = "snake_case")]
+pub enum ChainPolicy {
+    Accept,
+    Drop,
+}
+
+#[derive(Clone, Debug, Deserialize, Serialize)]
+#[serde(rename_all = "lowercase")]
+pub enum PriorityKeyword {
+    Raw,
+    Mangle,
+    DstNat,
+    Filter,
+    Security,
+    SrcNat,
+    Out,
+}
+
+#[derive(Clone, Debug, Deserialize, Serialize)]
+#[serde(untagged)]
+pub enum Priority {
+    Keyword(PriorityKeyword),
+    Number(i64),
+}
+
 #[derive(Clone, Debug, Deserialize, Serialize)]
 pub enum RateUnit {
     Packets,
@@ -47,6 +155,529 @@ pub enum RateTimescale {
     Day,
 }
 
+#[derive(Clone, Debug, Deserialize, Serialize)]
+pub struct TableName {
+    family: TableFamily,
+    name: String,
+}
+
+impl TableName {
+    pub fn new(family: TableFamily, name: impl Into<String>) -> Self {
+        Self {
+            family,
+            name: name.into(),
+        }
+    }
+
+    pub fn family(&self) -> &TableFamily {
+        &self.family
+    }
+
+    pub fn name(&self) -> &str {
+        &self.name
+    }
+}
+
+#[derive(Clone, Debug, Deserialize, Serialize)]
+pub struct TablePart {
+    family: TableFamily,
+    table: String,
+}
+
+impl TablePart {
+    pub fn new(family: TableFamily, name: impl Into<String>) -> Self {
+        Self {
+            family,
+            table: name.into(),
+        }
+    }
+
+    pub fn family(&self) -> &TableFamily {
+        &self.family
+    }
+
+    pub fn table(&self) -> &str {
+        &self.table
+    }
+}
+
+impl From<TablePart> for TableName {
+    fn from(t: TablePart) -> Self {
+        Self {
+            family: t.family,
+            name: t.table,
+        }
+    }
+}
+
+impl From<TableName> for TablePart {
+    fn from(t: TableName) -> Self {
+        Self {
+            family: t.family,
+            table: t.name,
+        }
+    }
+}
+
+#[derive(Clone, Debug, Deserialize, Serialize)]
+pub struct ChainName {
+    #[serde(flatten)]
+    table: TablePart,
+    name: String,
+}
+
+impl From<AddChain> for ChainName {
+    fn from(value: AddChain) -> Self {
+        Self {
+            table: value.table,
+            name: value.name,
+        }
+    }
+}
+
+impl From<ListChain> for ChainName {
+    fn from(value: ListChain) -> Self {
+        Self {
+            table: value.table,
+            name: value.name,
+        }
+    }
+}
+
+#[derive(Clone, Debug, Deserialize, Serialize)]
+pub struct ChainPart {
+    #[serde(flatten)]
+    table: TablePart,
+    chain: String,
+}
+
+impl ChainPart {
+    pub fn new(table: TablePart, chain: impl Into<String>) -> Self {
+        Self {
+            table,
+            chain: chain.into(),
+        }
+    }
+
+    pub fn table(&self) -> &TablePart {
+        &self.table
+    }
+
+    pub fn name(&self) -> &str {
+        &self.chain
+    }
+}
+
+impl From<ChainName> for ChainPart {
+    fn from(c: ChainName) -> Self {
+        Self {
+            table: c.table,
+            chain: c.name,
+        }
+    }
+}
+
+impl From<ChainPart> for ChainName {
+    fn from(c: ChainPart) -> Self {
+        Self {
+            table: c.table,
+            name: c.chain,
+        }
+    }
+}
+
+#[derive(Clone, Debug, Deserialize, Serialize)]
+pub struct AddTable {
+    family: TableFamily,
+    name: String,
+
+    #[serde(skip_serializing_if = "Option::is_none")]
+    handle: Option<Handle>,
+}
+
+impl AddTable {
+    pub fn new(family: TableFamily, name: impl Into<String>) -> Self {
+        Self {
+            family,
+            name: name.into(),
+            handle: None,
+        }
+    }
+}
+
+#[derive(Clone, Debug, Deserialize, Serialize)]
+pub struct BaseChainConfig {
+    #[serde(rename = "type")]
+    ty: ChainType,
+    hook: Hook,
+    prio: Expression,
+    policy: ChainPolicy,
+
+    /// netdev family only
+    #[serde(skip_serializing_if = "Option::is_none")]
+    dev: Option<String>,
+}
+
+impl BaseChainConfig {
+    pub fn new(
+        ty: ChainType,
+        hook: Hook,
+        prio: impl Into<Expression>,
+        policy: ChainPolicy,
+    ) -> Self {
+        Self {
+            ty,
+            hook,
+            prio: prio.into(),
+            policy,
+            dev: None,
+        }
+    }
+}
+
+#[derive(Clone, Debug, Deserialize, Serialize)]
+pub struct AddChain {
+    #[serde(flatten)]
+    table: TablePart,
+    name: String,
+
+    #[serde(flatten, skip_serializing_if = "Option::is_none")]
+    config: Option<BaseChainConfig>,
+}
+
+impl AddChain {
+    pub fn new(table: TablePart, name: impl Into<String>) -> Self {
+        Self {
+            table,
+            name: name.into(),
+            config: None,
+        }
+    }
+
+    pub fn new_base_chain(
+        table: TablePart,
+        name: impl Into<String>,
+        config: BaseChainConfig,
+    ) -> Self {
+        Self {
+            table,
+            name: name.into(),
+            config: Some(config),
+        }
+    }
+}
+
+impl From<ChainPart> for AddChain {
+    #[inline]
+    fn from(part: ChainPart) -> Self {
+        Self::new(part.table, part.chain)
+    }
+}
+
+#[derive(Clone, Debug, Deserialize, Serialize)]
+pub struct AddRule {
+    #[serde(flatten)]
+    chain: ChainPart,
+
+    #[serde(skip_serializing_if = "Option::is_none")]
+    handle: Option<Handle>,
+
+    #[serde(skip_serializing_if = "Option::is_none")]
+    index: Option<u64>,
+
+    #[serde(skip_serializing_if = "Option::is_none")]
+    comment: Option<String>,
+
+    expr: Vec<Statement>,
+}
+
+impl Deref for AddRule {
+    type Target = Vec<Statement>;
+
+    fn deref(&self) -> &Self::Target {
+        &self.expr
+    }
+}
+
+impl DerefMut for AddRule {
+    fn deref_mut(&mut self) -> &mut Self::Target {
+        &mut self.expr
+    }
+}
+
+impl AddRule {
+    pub fn from_statement(chain: ChainPart, expression: impl Into<Statement>) -> Self {
+        Self {
+            chain,
+            expr: vec![expression.into()],
+            handle: None,
+            index: None,
+            comment: None,
+        }
+    }
+
+    pub fn from_statements<I: IntoIterator<Item = Statement>>(
+        chain: ChainPart,
+        expression: I,
+    ) -> Self {
+        Self {
+            chain,
+            expr: expression.into_iter().collect(),
+            handle: None,
+            index: None,
+            comment: None,
+        }
+    }
+
+    pub fn new(chain: ChainPart) -> Self {
+        Self {
+            chain,
+            expr: Vec::new(),
+            handle: None,
+            index: None,
+            comment: None,
+        }
+    }
+
+    pub fn with_comment(mut self, comment: impl Into<String>) -> Self {
+        self.comment = Some(comment.into());
+        self
+    }
+}
+
+#[derive(Clone, Debug, Deserialize, Serialize)]
+#[serde(rename_all = "kebab-case")]
+pub struct SetConfig {
+    #[serde(flatten)]
+    name: SetName,
+
+    #[serde(rename = "type", default, skip_serializing_if = "Vec::is_empty")]
+    ty: NfVec<ElementType>,
+
+    #[serde(skip_serializing_if = "Option::is_none")]
+    policy: Option<SetPolicy>,
+
+    #[serde(skip_serializing_if = "Vec::is_empty", default)]
+    flags: Vec<SetFlag>,
+
+    #[serde(skip_serializing_if = "Option::is_none")]
+    timeout: Option<i64>,
+
+    #[serde(skip_serializing_if = "Option::is_none")]
+    gc_interval: Option<i64>,
+
+    #[serde(skip_serializing_if = "Option::is_none")]
+    size: Option<i64>,
+}
+
+impl SetConfig {
+    pub fn new(name: impl Into<SetName>, ty: impl Into<NfVec<ElementType>>) -> Self {
+        Self {
+            name: name.into(),
+            ty: ty.into(),
+            flags: Vec::new(),
+            policy: None,
+            timeout: None,
+            gc_interval: None,
+            size: None,
+        }
+    }
+
+    pub fn name(&self) -> &SetName {
+        &self.name
+    }
+
+    pub fn with_flag(mut self, flag: SetFlag) -> Self {
+        self.flags.push(flag);
+        self
+    }
+}
+
+#[derive(Clone, Debug, Deserialize, Serialize)]
+#[serde(rename_all = "kebab-case")]
+pub struct AddMap {
+    #[serde(flatten)]
+    config: SetConfig,
+
+    map: OutputType,
+
+    #[serde(default, skip_serializing_if = "Vec::is_empty")]
+    elem: NfVec<MapElem>,
+}
+
+impl AddMap {
+    pub fn new(config: SetConfig, output_type: OutputType) -> Self {
+        Self {
+            config,
+            map: output_type,
+            elem: NfVec::new(),
+        }
+    }
+}
+
+impl Deref for AddMap {
+    type Target = Vec<MapElem>;
+
+    fn deref(&self) -> &Self::Target {
+        &self.elem
+    }
+}
+
+impl DerefMut for AddMap {
+    fn deref_mut(&mut self) -> &mut Self::Target {
+        &mut self.elem
+    }
+}
+
+#[derive(Clone, Debug, Deserialize, Serialize)]
+pub struct AddSet {
+    #[serde(flatten)]
+    config: SetConfig,
+
+    #[serde(default, skip_serializing_if = "Vec::is_empty")]
+    elem: NfVec<SetElem>,
+}
+
+impl From<SetConfig> for AddSet {
+    fn from(value: SetConfig) -> Self {
+        Self {
+            config: value,
+            elem: NfVec::new(),
+        }
+    }
+}
+
+impl Deref for AddSet {
+    type Target = Vec<SetElem>;
+
+    fn deref(&self) -> &Self::Target {
+        &self.elem
+    }
+}
+
+impl DerefMut for AddSet {
+    fn deref_mut(&mut self) -> &mut Self::Target {
+        &mut self.elem
+    }
+}
+
+impl AddSet {
+    pub fn new(config: impl Into<SetConfig>, elements: impl IntoIterator<Item = SetElem>) -> Self {
+        Self {
+            config: config.into(),
+            elem: NfVec::from(elements.into_iter().collect::<Vec<_>>()),
+        }
+    }
+}
+
+#[derive(Clone, Debug, Deserialize, Serialize)]
+pub struct SetName {
+    #[serde(flatten)]
+    table: TablePart,
+    name: String,
+}
+
+impl SetName {
+    pub fn new(table: TablePart, name: impl Into<String>) -> Self {
+        Self {
+            table,
+            name: name.into(),
+        }
+    }
+}
+
+#[derive(Clone, Debug, Deserialize, Serialize)]
+pub struct SetElem(Expression);
+
+impl From<Expression> for SetElem {
+    #[inline]
+    fn from(value: Expression) -> Self {
+        Self(value)
+    }
+}
+
+#[derive(Clone, Debug, Deserialize, Serialize)]
+#[serde(untagged)]
+pub enum MapValue {
+    Expression(Expression),
+    Verdict(Verdict),
+    // Concat
+}
+
+impl From<Verdict> for MapValue {
+    #[inline]
+    fn from(value: Verdict) -> Self {
+        Self::Verdict(value)
+    }
+}
+
+impl From<Expression> for MapValue {
+    #[inline]
+    fn from(value: Expression) -> Self {
+        Self::Expression(value)
+    }
+}
+
+#[derive(Clone, Debug, Deserialize, Serialize)]
+pub struct MapElem((Expression, MapValue));
+
+impl MapElem {
+    pub fn new(key: Expression, value: impl Into<MapValue>) -> Self {
+        Self((key, value.into()))
+    }
+}
+
+#[derive(Clone, Debug, Deserialize, Serialize)]
+pub struct AddSetElement {
+    #[serde(flatten)]
+    set: SetName,
+    #[serde(default, skip_serializing_if = "Vec::is_empty")]
+    elem: Vec<SetElement>,
+}
+
+#[derive(Clone, Debug, Deserialize, Serialize)]
+pub struct AddMapElement {
+    #[serde(flatten)]
+    map: SetName,
+    #[serde(default, skip_serializing_if = "Vec::is_empty")]
+    elem: Vec<MapElement>,
+}
+
+#[derive(Clone, Debug, Deserialize, Serialize)]
+#[serde(untagged)]
+pub enum AddElement {
+    Set(AddSetElement),
+    Map(AddMapElement),
+}
+
+impl AddElement {
+    pub fn map_from_expressions(
+        map: SetName,
+        elem: impl IntoIterator<Item = (Expression, MapValue)>,
+    ) -> Self {
+        Self::Map(AddMapElement {
+            map,
+            elem: Vec::from_iter(
+                elem.into_iter()
+                    .map(|(key, value)| MapElem::new(key, value).into()),
+            ),
+        })
+    }
+
+    pub fn set_from_expressions(set: SetName, elem: impl IntoIterator<Item = Expression>) -> Self {
+        Self::Set(AddSetElement {
+            set,
+            elem: Vec::from_iter(elem.into_iter().map(SetElement::from)),
+        })
+    }
+}
+
+impl From<AddSetElement> for AddElement {
+    fn from(value: AddSetElement) -> Self {
+        AddElement::Set(value)
+    }
+}
+
 #[derive(Clone, Debug, Deserialize, Serialize)]
 pub struct ElemConfig {
     timeout: Option<i64>,
@@ -68,3 +699,125 @@ impl ElemConfig {
     }
 }
 
+#[derive(Clone, Debug, Deserialize, Serialize)]
+pub struct SetElemObject {
+    #[serde(flatten)]
+    config: ElemConfig,
+    elem: SetElem,
+}
+
+#[derive(Clone, Debug, Deserialize, Serialize)]
+pub struct MapElemObject {
+    #[serde(flatten)]
+    config: ElemConfig,
+    elem: MapElem,
+}
+
+#[derive(Clone, Debug, Deserialize, Serialize)]
+pub enum MapElement {
+    #[serde(rename = "elem")]
+    Object(MapElemObject),
+    #[serde(untagged)]
+    Value(MapElem),
+}
+
+impl From<MapElem> for MapElement {
+    fn from(value: MapElem) -> Self {
+        Self::Value(value)
+    }
+}
+
+#[derive(Clone, Debug, Deserialize, Serialize)]
+pub enum SetElement {
+    #[serde(rename = "elem")]
+    Object(SetElemObject),
+    #[serde(untagged)]
+    Value(SetElem),
+}
+
+impl From<Expression> for SetElement {
+    #[inline]
+    fn from(value: Expression) -> Self {
+        Self::Value(SetElem::from(value))
+    }
+}
+
+#[derive(Clone, Debug, Deserialize, Serialize)]
+#[serde(rename_all = "lowercase")]
+pub struct AddLimit {
+    #[serde(flatten)]
+    table: TablePart,
+
+    name: String,
+
+    rate: i64,
+
+    #[serde(skip_serializing_if = "Option::is_none")]
+    unit: Option<RateUnit>,
+
+    #[serde(skip_serializing_if = "Option::is_none")]
+    per: Option<RateTimescale>,
+
+    #[serde(skip_serializing_if = "Option::is_none")]
+    burst: Option<i64>,
+
+    #[serde(skip_serializing_if = "Option::is_none")]
+    inv: Option<bool>,
+}
+
+impl AddLimit {
+    pub fn new(table: TablePart, name: String, rate: i64) -> Self {
+        Self {
+            table,
+            name,
+            rate,
+            unit: None,
+            per: None,
+            burst: None,
+            inv: None,
+        }
+    }
+}
+
+#[derive(Clone, Debug, Deserialize, Serialize)]
+#[serde(rename_all = "lowercase")]
+pub enum L3Protocol {
+    Ip,
+    Ip6,
+}
+
+#[derive(Clone, Debug, Deserialize, Serialize)]
+#[serde(rename_all = "lowercase")]
+pub enum CtHelperProtocol {
+    TCP,
+    UDP,
+}
+
+#[derive(Clone, Debug, Deserialize, Serialize)]
+#[serde(rename = "ct helper")]
+pub struct AddCtHelper {
+    #[serde(flatten)]
+    pub table: TablePart,
+    pub name: String,
+    #[serde(rename = "type")]
+    pub ty: String,
+    pub protocol: CtHelperProtocol,
+    pub l3proto: Option<L3Protocol>,
+}
+
+#[derive(Clone, Debug, Deserialize, Serialize)]
+pub struct ListChain {
+    #[serde(flatten)]
+    table: TablePart,
+    name: String,
+    handle: i64,
+
+    #[serde(flatten)]
+    config: Option<BaseChainConfig>,
+}
+
+impl ListChain {
+    pub fn name(&self) -> &str {
+        &self.name
+    }
+}
-- 
2.39.2




  parent reply	other threads:[~2024-04-02 17:17 UTC|newest]

Thread overview: 67+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2024-04-02 17:15 [pve-devel] [RFC container/firewall/manager/proxmox-firewall/qemu-server 00/37] proxmox firewall nftables implementation Stefan Hanreich
2024-04-02 17:15 ` [pve-devel] [PATCH proxmox-firewall 01/37] config: add proxmox-ve-config crate Stefan Hanreich
2024-04-02 17:15 ` [pve-devel] [PATCH proxmox-firewall 02/37] config: firewall: add types for ip addresses Stefan Hanreich
2024-04-03 10:46   ` Max Carrara
2024-04-09  8:26     ` Stefan Hanreich
2024-04-02 17:15 ` [pve-devel] [PATCH proxmox-firewall 03/37] config: firewall: add types for ports Stefan Hanreich
2024-04-02 17:15 ` [pve-devel] [PATCH proxmox-firewall 04/37] config: firewall: add types for log level and rate limit Stefan Hanreich
2024-04-02 17:15 ` [pve-devel] [PATCH proxmox-firewall 05/37] config: firewall: add types for aliases Stefan Hanreich
2024-04-02 17:15 ` [pve-devel] [PATCH proxmox-firewall 06/37] config: host: add helpers for host network configuration Stefan Hanreich
2024-04-03 10:46   ` Max Carrara
2024-04-09  8:32     ` Stefan Hanreich
2024-04-09 14:20   ` Lukas Wagner
2024-04-02 17:15 ` [pve-devel] [PATCH proxmox-firewall 07/37] config: guest: add helpers for parsing guest network config Stefan Hanreich
2024-04-02 17:16 ` [pve-devel] [PATCH proxmox-firewall 08/37] config: firewall: add types for ipsets Stefan Hanreich
2024-04-02 17:16 ` [pve-devel] [PATCH proxmox-firewall 09/37] config: firewall: add types for rules Stefan Hanreich
2024-04-03 10:46   ` Max Carrara
2024-04-09  8:36     ` Stefan Hanreich
2024-04-09 14:55     ` Lukas Wagner
2024-04-02 17:16 ` [pve-devel] [PATCH proxmox-firewall 10/37] config: firewall: add types for security groups Stefan Hanreich
2024-04-02 17:16 ` [pve-devel] [PATCH proxmox-firewall 11/37] config: firewall: add generic parser for firewall configs Stefan Hanreich
2024-04-03 10:47   ` Max Carrara
2024-04-09  8:38     ` Stefan Hanreich
2024-04-02 17:16 ` [pve-devel] [PATCH proxmox-firewall 12/37] config: firewall: add cluster-specific config + option types Stefan Hanreich
2024-04-02 17:16 ` [pve-devel] [PATCH proxmox-firewall 13/37] config: firewall: add host specific " Stefan Hanreich
2024-04-03 10:47   ` Max Carrara
2024-04-09  8:55     ` Stefan Hanreich
2024-04-02 17:16 ` [pve-devel] [PATCH proxmox-firewall 14/37] config: firewall: add guest-specific " Stefan Hanreich
2024-04-02 17:16 ` [pve-devel] [PATCH proxmox-firewall 15/37] config: firewall: add firewall macros Stefan Hanreich
2024-04-02 17:16 ` [pve-devel] [PATCH proxmox-firewall 16/37] config: firewall: add conntrack helper types Stefan Hanreich
2024-04-02 17:16 ` [pve-devel] [PATCH proxmox-firewall 17/37] nftables: add crate for libnftables bindings Stefan Hanreich
2024-04-02 17:16 ` [pve-devel] [PATCH proxmox-firewall 18/37] nftables: add helpers Stefan Hanreich
2024-04-02 17:16 ` [pve-devel] [PATCH proxmox-firewall 19/37] nftables: expression: add types Stefan Hanreich
2024-04-02 17:16 ` [pve-devel] [PATCH proxmox-firewall 20/37] nftables: expression: implement conversion traits for firewall config Stefan Hanreich
2024-04-02 17:16 ` [pve-devel] [PATCH proxmox-firewall 21/37] nftables: statement: add types Stefan Hanreich
2024-04-03 10:47   ` Max Carrara
2024-04-09  8:58     ` Stefan Hanreich
2024-04-02 17:16 ` [pve-devel] [PATCH proxmox-firewall 22/37] nftables: statement: add conversion traits for config types Stefan Hanreich
2024-04-02 17:16 ` Stefan Hanreich [this message]
2024-04-02 17:16 ` [pve-devel] [PATCH proxmox-firewall 24/37] nftables: types: add conversion traits Stefan Hanreich
2024-04-02 17:16 ` [pve-devel] [PATCH proxmox-firewall 25/37] nftables: add libnftables bindings Stefan Hanreich
2024-04-02 17:16 ` [pve-devel] [PATCH proxmox-firewall 26/37] firewall: add firewall crate Stefan Hanreich
2024-04-02 17:16 ` [pve-devel] [PATCH proxmox-firewall 27/37] firewall: add base ruleset Stefan Hanreich
2024-04-02 17:16 ` [pve-devel] [PATCH proxmox-firewall 28/37] firewall: add config loader Stefan Hanreich
2024-04-02 17:16 ` [pve-devel] [PATCH proxmox-firewall 29/37] firewall: add rule generation logic Stefan Hanreich
2024-04-02 17:16 ` [pve-devel] [PATCH proxmox-firewall 30/37] firewall: add object " Stefan Hanreich
2024-04-02 17:16 ` [pve-devel] [PATCH proxmox-firewall 31/37] firewall: add ruleset " Stefan Hanreich
2024-04-02 17:16 ` [pve-devel] [PATCH proxmox-firewall 32/37] firewall: add proxmox-firewall binary Stefan Hanreich
2024-04-02 17:16 ` [pve-devel] [PATCH proxmox-firewall 33/37] firewall: add files for debian packaging Stefan Hanreich
2024-04-03 13:14   ` Fabian Grünbichler
2024-04-09  8:56     ` Stefan Hanreich
2024-04-02 17:16 ` [pve-devel] [PATCH qemu-server 34/37] firewall: add handling for new nft firewall Stefan Hanreich
2024-04-02 17:16 ` [pve-devel] [PATCH pve-container 35/37] " Stefan Hanreich
2024-04-02 17:16 ` [pve-devel] [PATCH pve-firewall 36/37] add configuration option for new nftables firewall Stefan Hanreich
2024-04-02 17:16 ` [pve-devel] [PATCH pve-manager 37/37] firewall: expose " Stefan Hanreich
2024-04-02 20:47 ` [pve-devel] [RFC container/firewall/manager/proxmox-firewall/qemu-server 00/37] proxmox firewall nftables implementation Laurent GUERBY
2024-04-03  7:33   ` Stefan Hanreich
     [not found] ` <mailman.54.1712122640.450.pve-devel@lists.proxmox.com>
2024-04-03  7:52   ` Stefan Hanreich
2024-04-03 12:26   ` Stefan Hanreich
     [not found] ` <mailman.56.1712124362.450.pve-devel@lists.proxmox.com>
2024-04-03  8:15   ` Stefan Hanreich
     [not found]     ` <mailman.77.1712145853.450.pve-devel@lists.proxmox.com>
2024-04-03 12:25       ` Stefan Hanreich
     [not found]         ` <mailman.78.1712149473.450.pve-devel@lists.proxmox.com>
2024-04-03 13:08           ` Stefan Hanreich
2024-04-03 10:46 ` Max Carrara
2024-04-09  9:21   ` Stefan Hanreich
2024-04-10 10:25 ` Lukas Wagner
2024-04-11  5:21   ` Stefan Hanreich
2024-04-11  7:34     ` Thomas Lamprecht
2024-04-11  7:55       ` Stefan Hanreich

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20240402171629.536804-24-s.hanreich@proxmox.com \
    --to=s.hanreich@proxmox.com \
    --cc=pve-devel@lists.proxmox.com \
    --cc=w.bumiller@proxmox.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal