[pve-devel] [PATCH proxmox-firewall 23/37] nftables: commands: add types
Stefan Hanreich
s.hanreich at proxmox.com
Tue Apr 2 19:16:15 CEST 2024
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 at proxmox.com>
Signed-off-by: Stefan Hanreich <s.hanreich at 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
More information about the pve-devel
mailing list