From: Hannes Laimer <h.laimer@proxmox.com>
To: pdm-devel@lists.proxmox.com
Subject: [pdm-devel] [PATCH proxmox 5/5] pve-api-types: regenerate
Date: Thu, 30 Oct 2025 15:33:58 +0100	[thread overview]
Message-ID: <20251030143406.193744-6-h.laimer@proxmox.com> (raw)
In-Reply-To: <20251030143406.193744-1-h.laimer@proxmox.com>
Signed-off-by: Hannes Laimer <h.laimer@proxmox.com>
---
 pve-api-types/src/generated/code.rs  |  206 +++-
 pve-api-types/src/generated/types.rs | 1364 ++++++++++++++++++++++++--
 2 files changed, 1502 insertions(+), 68 deletions(-)
diff --git a/pve-api-types/src/generated/code.rs b/pve-api-types/src/generated/code.rs
index 788e4bf5..ad6af3d1 100644
--- a/pve-api-types/src/generated/code.rs
+++ b/pve-api-types/src/generated/code.rs
@@ -60,9 +60,7 @@
 /// - /cluster/firewall/ipset/{name}
 /// - /cluster/firewall/ipset/{name}/{cidr}
 /// - /cluster/firewall/macros
-/// - /cluster/firewall/options
 /// - /cluster/firewall/refs
-/// - /cluster/firewall/rules
 /// - /cluster/firewall/rules/{pos}
 /// - /cluster/ha
 /// - /cluster/ha/groups
@@ -200,8 +198,6 @@
 /// - /nodes/{node}/execute
 /// - /nodes/{node}/firewall
 /// - /nodes/{node}/firewall/log
-/// - /nodes/{node}/firewall/options
-/// - /nodes/{node}/firewall/rules
 /// - /nodes/{node}/firewall/rules/{pos}
 /// - /nodes/{node}/hardware
 /// - /nodes/{node}/hardware/pci
@@ -220,9 +216,7 @@
 /// - /nodes/{node}/lxc/{vmid}/firewall/ipset/{name}
 /// - /nodes/{node}/lxc/{vmid}/firewall/ipset/{name}/{cidr}
 /// - /nodes/{node}/lxc/{vmid}/firewall/log
-/// - /nodes/{node}/lxc/{vmid}/firewall/options
 /// - /nodes/{node}/lxc/{vmid}/firewall/refs
-/// - /nodes/{node}/lxc/{vmid}/firewall/rules
 /// - /nodes/{node}/lxc/{vmid}/firewall/rules/{pos}
 /// - /nodes/{node}/lxc/{vmid}/interfaces
 /// - /nodes/{node}/lxc/{vmid}/move_volume
@@ -287,9 +281,7 @@
 /// - /nodes/{node}/qemu/{vmid}/firewall/ipset/{name}
 /// - /nodes/{node}/qemu/{vmid}/firewall/ipset/{name}/{cidr}
 /// - /nodes/{node}/qemu/{vmid}/firewall/log
-/// - /nodes/{node}/qemu/{vmid}/firewall/options
 /// - /nodes/{node}/qemu/{vmid}/firewall/refs
-/// - /nodes/{node}/qemu/{vmid}/firewall/rules
 /// - /nodes/{node}/qemu/{vmid}/firewall/rules/{pos}
 /// - /nodes/{node}/qemu/{vmid}/monitor
 /// - /nodes/{node}/qemu/{vmid}/move_disk
@@ -385,6 +377,11 @@ pub trait PveClient {
         Err(Error::Other("cluster_config_join not implemented"))
     }
 
+    /// Get Firewall options.
+    async fn cluster_firewall_options(&self) -> Result<ClusterFirewallOptions, Error> {
+        Err(Error::Other("cluster_firewall_options not implemented"))
+    }
+
     /// Retrieve metrics of the cluster.
     async fn cluster_metrics_export(
         &self,
@@ -481,6 +478,11 @@ pub trait PveClient {
         Err(Error::Other("list_available_updates not implemented"))
     }
 
+    /// List rules.
+    async fn list_cluster_firewall_rules(&self) -> Result<Vec<ListFirewallRules>, Error> {
+        Err(Error::Other("list_cluster_firewall_rules not implemented"))
+    }
+
     /// SDN controllers index.
     async fn list_controllers(
         &self,
@@ -501,6 +503,15 @@ pub trait PveClient {
         Err(Error::Other("list_lxc not implemented"))
     }
 
+    /// List rules.
+    async fn list_lxc_firewall_rules(
+        &self,
+        node: &str,
+        vmid: u32,
+    ) -> Result<Vec<ListFirewallRules>, Error> {
+        Err(Error::Other("list_lxc_firewall_rules not implemented"))
+    }
+
     /// List available networks
     async fn list_networks(
         &self,
@@ -510,6 +521,11 @@ pub trait PveClient {
         Err(Error::Other("list_networks not implemented"))
     }
 
+    /// List rules.
+    async fn list_node_firewall_rules(&self, node: &str) -> Result<Vec<ListFirewallRules>, Error> {
+        Err(Error::Other("list_node_firewall_rules not implemented"))
+    }
+
     /// Cluster node index.
     async fn list_nodes(&self) -> Result<Vec<ClusterNodeIndexResponse>, Error> {
         Err(Error::Other("list_nodes not implemented"))
@@ -520,6 +536,15 @@ pub trait PveClient {
         Err(Error::Other("list_qemu not implemented"))
     }
 
+    /// List rules.
+    async fn list_qemu_firewall_rules(
+        &self,
+        node: &str,
+        vmid: u32,
+    ) -> Result<Vec<ListFirewallRules>, Error> {
+        Err(Error::Other("list_qemu_firewall_rules not implemented"))
+    }
+
     /// Get status for all datastores.
     async fn list_storages(
         &self,
@@ -552,6 +577,15 @@ pub trait PveClient {
         Err(Error::Other("list_zones not implemented"))
     }
 
+    /// Get VM firewall options.
+    async fn lxc_firewall_options(
+        &self,
+        node: &str,
+        vmid: u32,
+    ) -> Result<GuestFirewallOptions, Error> {
+        Err(Error::Other("lxc_firewall_options not implemented"))
+    }
+
     /// Get container configuration.
     async fn lxc_get_config(
         &self,
@@ -588,11 +622,25 @@ pub trait PveClient {
         Err(Error::Other("migrate_qemu not implemented"))
     }
 
+    /// Get host firewall options.
+    async fn node_firewall_options(&self, node: &str) -> Result<NodeFirewallOptions, Error> {
+        Err(Error::Other("node_firewall_options not implemented"))
+    }
+
     /// Read node status
     async fn node_status(&self, node: &str) -> Result<NodeStatus, Error> {
         Err(Error::Other("node_status not implemented"))
     }
 
+    /// Get VM firewall options.
+    async fn qemu_firewall_options(
+        &self,
+        node: &str,
+        vmid: u32,
+    ) -> Result<GuestFirewallOptions, Error> {
+        Err(Error::Other("qemu_firewall_options not implemented"))
+    }
+
     /// Get the virtual machine configuration with pending configuration changes
     /// applied. Set the 'current' parameter to get the current configuration
     /// instead.
@@ -678,6 +726,43 @@ pub trait PveClient {
         Err(Error::Other("sdn_apply not implemented"))
     }
 
+    /// Set Firewall options.
+    async fn set_cluster_firewall_options(
+        &self,
+        params: UpdateClusterFirewallOptions,
+    ) -> Result<(), Error> {
+        Err(Error::Other("set_cluster_firewall_options not implemented"))
+    }
+
+    /// Set Firewall options.
+    async fn set_lxc_firewall_options(
+        &self,
+        node: &str,
+        vmid: u32,
+        params: UpdateGuestFirewallOptions,
+    ) -> Result<(), Error> {
+        Err(Error::Other("set_lxc_firewall_options not implemented"))
+    }
+
+    /// Set Firewall options.
+    async fn set_node_firewall_options(
+        &self,
+        node: &str,
+        params: UpdateNodeFirewallOptions,
+    ) -> Result<(), Error> {
+        Err(Error::Other("set_node_firewall_options not implemented"))
+    }
+
+    /// Set Firewall options.
+    async fn set_qemu_firewall_options(
+        &self,
+        node: &str,
+        vmid: u32,
+        params: UpdateGuestFirewallOptions,
+    ) -> Result<(), Error> {
+        Err(Error::Other("set_qemu_firewall_options not implemented"))
+    }
+
     /// Shutdown the container. This will trigger a clean shutdown of the
     /// container, see lxc-stop(1) for details.
     async fn shutdown_lxc_async(
@@ -791,6 +876,12 @@ where
         Ok(self.0.get(url).await?.expect_json()?.data)
     }
 
+    /// Get Firewall options.
+    async fn cluster_firewall_options(&self) -> Result<ClusterFirewallOptions, Error> {
+        let url = "/api2/extjs/cluster/firewall/options";
+        Ok(self.0.get(url).await?.expect_json()?.data)
+    }
+
     /// Retrieve metrics of the cluster.
     async fn cluster_metrics_export(
         &self,
@@ -938,6 +1029,12 @@ where
         Ok(self.0.get(url).await?.expect_json()?.data)
     }
 
+    /// List rules.
+    async fn list_cluster_firewall_rules(&self) -> Result<Vec<ListFirewallRules>, Error> {
+        let url = "/api2/extjs/cluster/firewall/rules";
+        Ok(self.0.get(url).await?.expect_json()?.data)
+    }
+
     /// SDN controllers index.
     async fn list_controllers(
         &self,
@@ -965,6 +1062,16 @@ where
         Ok(self.0.get(url).await?.expect_json()?.data)
     }
 
+    /// List rules.
+    async fn list_lxc_firewall_rules(
+        &self,
+        node: &str,
+        vmid: u32,
+    ) -> Result<Vec<ListFirewallRules>, Error> {
+        let url = &format!("/api2/extjs/nodes/{node}/lxc/{vmid}/firewall/rules");
+        Ok(self.0.get(url).await?.expect_json()?.data)
+    }
+
     /// List available networks
     async fn list_networks(
         &self,
@@ -977,6 +1084,12 @@ where
         Ok(self.0.get(url).await?.expect_json()?.data)
     }
 
+    /// List rules.
+    async fn list_node_firewall_rules(&self, node: &str) -> Result<Vec<ListFirewallRules>, Error> {
+        let url = &format!("/api2/extjs/nodes/{node}/firewall/rules");
+        Ok(self.0.get(url).await?.expect_json()?.data)
+    }
+
     /// Cluster node index.
     async fn list_nodes(&self) -> Result<Vec<ClusterNodeIndexResponse>, Error> {
         let url = "/api2/extjs/nodes";
@@ -991,6 +1104,16 @@ where
         Ok(self.0.get(url).await?.expect_json()?.data)
     }
 
+    /// List rules.
+    async fn list_qemu_firewall_rules(
+        &self,
+        node: &str,
+        vmid: u32,
+    ) -> Result<Vec<ListFirewallRules>, Error> {
+        let url = &format!("/api2/extjs/nodes/{node}/qemu/{vmid}/firewall/rules");
+        Ok(self.0.get(url).await?.expect_json()?.data)
+    }
+
     /// Get status for all datastores.
     async fn list_storages(
         &self,
@@ -1039,6 +1162,16 @@ where
         Ok(self.0.get(url).await?.expect_json()?.data)
     }
 
+    /// Get VM firewall options.
+    async fn lxc_firewall_options(
+        &self,
+        node: &str,
+        vmid: u32,
+    ) -> Result<GuestFirewallOptions, Error> {
+        let url = &format!("/api2/extjs/nodes/{node}/lxc/{vmid}/firewall/options");
+        Ok(self.0.get(url).await?.expect_json()?.data)
+    }
+
     /// Get container configuration.
     async fn lxc_get_config(
         &self,
@@ -1082,12 +1215,28 @@ where
         Ok(self.0.post(url, ¶ms).await?.expect_json()?.data)
     }
 
+    /// Get host firewall options.
+    async fn node_firewall_options(&self, node: &str) -> Result<NodeFirewallOptions, Error> {
+        let url = &format!("/api2/extjs/nodes/{node}/firewall/options");
+        Ok(self.0.get(url).await?.expect_json()?.data)
+    }
+
     /// Read node status
     async fn node_status(&self, node: &str) -> Result<NodeStatus, Error> {
         let url = &format!("/api2/extjs/nodes/{node}/status");
         Ok(self.0.get(url).await?.expect_json()?.data)
     }
 
+    /// Get VM firewall options.
+    async fn qemu_firewall_options(
+        &self,
+        node: &str,
+        vmid: u32,
+    ) -> Result<GuestFirewallOptions, Error> {
+        let url = &format!("/api2/extjs/nodes/{node}/qemu/{vmid}/firewall/options");
+        Ok(self.0.get(url).await?.expect_json()?.data)
+    }
+
     /// Get the virtual machine configuration with pending configuration changes
     /// applied. Set the 'current' parameter to get the current configuration
     /// instead.
@@ -1196,6 +1345,47 @@ where
         Ok(self.0.put(url, ¶ms).await?.expect_json()?.data)
     }
 
+    /// Set Firewall options.
+    async fn set_cluster_firewall_options(
+        &self,
+        params: UpdateClusterFirewallOptions,
+    ) -> Result<(), Error> {
+        let url = "/api2/extjs/cluster/firewall/options";
+        self.0.put(url, ¶ms).await?.nodata()
+    }
+
+    /// Set Firewall options.
+    async fn set_lxc_firewall_options(
+        &self,
+        node: &str,
+        vmid: u32,
+        params: UpdateGuestFirewallOptions,
+    ) -> Result<(), Error> {
+        let url = &format!("/api2/extjs/nodes/{node}/lxc/{vmid}/firewall/options");
+        self.0.put(url, ¶ms).await?.nodata()
+    }
+
+    /// Set Firewall options.
+    async fn set_node_firewall_options(
+        &self,
+        node: &str,
+        params: UpdateNodeFirewallOptions,
+    ) -> Result<(), Error> {
+        let url = &format!("/api2/extjs/nodes/{node}/firewall/options");
+        self.0.put(url, ¶ms).await?.nodata()
+    }
+
+    /// Set Firewall options.
+    async fn set_qemu_firewall_options(
+        &self,
+        node: &str,
+        vmid: u32,
+        params: UpdateGuestFirewallOptions,
+    ) -> Result<(), Error> {
+        let url = &format!("/api2/extjs/nodes/{node}/qemu/{vmid}/firewall/options");
+        self.0.put(url, ¶ms).await?.nodata()
+    }
+
     /// Shutdown the container. This will trigger a clean shutdown of the
     /// container, see lxc-stop(1) for details.
     async fn shutdown_lxc_async(
diff --git a/pve-api-types/src/generated/types.rs b/pve-api-types/src/generated/types.rs
index b8b834be..11e2929d 100644
--- a/pve-api-types/src/generated/types.rs
+++ b/pve-api-types/src/generated/types.rs
@@ -226,6 +226,101 @@ mod cluster_resource_content {
     }
 }
 
+#[api(
+    properties: {
+        ebtables: {
+            default: true,
+            optional: true,
+        },
+        enable: {
+            default: 0,
+            minimum: 0,
+            optional: true,
+            type: Integer,
+        },
+        log_ratelimit: {
+            format: &ApiStringFormat::PropertyString(&ClusterFirewallOptionsLogRatelimit::API_SCHEMA),
+            optional: true,
+            type: String,
+        },
+        policy_forward: {
+            optional: true,
+            type: FirewallFWPolicy,
+        },
+        policy_in: {
+            optional: true,
+            type: FirewallIOPolicy,
+        },
+        policy_out: {
+            optional: true,
+            type: FirewallIOPolicy,
+        },
+    },
+)]
+/// Object.
+#[derive(Debug, serde::Deserialize, serde::Serialize)]
+pub struct ClusterFirewallOptions {
+    /// Enable ebtables rules cluster wide.
+    #[serde(deserialize_with = "proxmox_serde::perl::deserialize_bool")]
+    #[serde(default, skip_serializing_if = "Option::is_none")]
+    pub ebtables: Option<bool>,
+
+    /// Enable or disable the firewall cluster wide.
+    #[serde(deserialize_with = "proxmox_serde::perl::deserialize_u64")]
+    #[serde(default, skip_serializing_if = "Option::is_none")]
+    pub enable: Option<u64>,
+
+    /// Log ratelimiting settings
+    #[serde(default, skip_serializing_if = "Option::is_none")]
+    pub log_ratelimit: Option<String>,
+
+    #[serde(default, skip_serializing_if = "Option::is_none")]
+    pub policy_forward: Option<FirewallFWPolicy>,
+
+    #[serde(default, skip_serializing_if = "Option::is_none")]
+    pub policy_in: Option<FirewallIOPolicy>,
+
+    #[serde(default, skip_serializing_if = "Option::is_none")]
+    pub policy_out: Option<FirewallIOPolicy>,
+}
+
+#[api(
+    default_key: "enable",
+    properties: {
+        burst: {
+            default: 5,
+            minimum: 0,
+            optional: true,
+            type: Integer,
+        },
+        enable: {
+            default: true,
+        },
+        rate: {
+            default: "1/second",
+            optional: true,
+            type: String,
+        },
+    },
+)]
+/// Object.
+#[derive(Debug, serde::Deserialize, serde::Serialize)]
+pub struct ClusterFirewallOptionsLogRatelimit {
+    /// Initial burst of packages which will always get logged before the rate
+    /// is applied
+    #[serde(deserialize_with = "proxmox_serde::perl::deserialize_u64")]
+    #[serde(default, skip_serializing_if = "Option::is_none")]
+    pub burst: Option<u64>,
+
+    /// Enable or disable log rate limiting
+    #[serde(deserialize_with = "proxmox_serde::perl::deserialize_bool")]
+    pub enable: bool,
+
+    /// Frequency with which the burst bucket gets refilled
+    #[serde(default, skip_serializing_if = "Option::is_none")]
+    pub rate: Option<String>,
+}
+
 const_regex! {
 
 CLUSTER_JOIN_INFO_PREFERRED_NODE_RE = r##"^(?i:[a-z0-9](?i:[a-z0-9\-]*[a-z0-9])?)$"##;
@@ -1645,6 +1740,204 @@ pub struct CreateZone {
     pub zone: String,
 }
 
+#[api]
+/// Firewall IO policies.
+#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
+pub enum FirewallFWPolicy {
+    #[serde(rename = "ACCEPT")]
+    /// ACCEPT.
+    Accept,
+    #[serde(rename = "DROP")]
+    #[default]
+    /// DROP.
+    Drop,
+}
+serde_plain::derive_display_from_serialize!(FirewallFWPolicy);
+serde_plain::derive_fromstr_from_deserialize!(FirewallFWPolicy);
+
+#[api]
+/// Firewall IO policies.
+#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
+pub enum FirewallIOPolicy {
+    #[serde(rename = "ACCEPT")]
+    #[default]
+    /// ACCEPT.
+    Accept,
+    #[serde(rename = "DROP")]
+    /// DROP.
+    Drop,
+    #[serde(rename = "REJECT")]
+    /// REJECT.
+    Reject,
+}
+serde_plain::derive_display_from_serialize!(FirewallIOPolicy);
+serde_plain::derive_fromstr_from_deserialize!(FirewallIOPolicy);
+
+#[api]
+/// Firewall log levels.
+#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
+pub enum FirewallLogLevel {
+    #[serde(rename = "emerg")]
+    /// emerg.
+    Emerg,
+    #[serde(rename = "alert")]
+    /// alert.
+    Alert,
+    #[serde(rename = "crit")]
+    /// crit.
+    Crit,
+    #[serde(rename = "err")]
+    /// err.
+    Error,
+    #[serde(rename = "warning")]
+    /// warning.
+    Warning,
+    #[serde(rename = "notice")]
+    /// notice.
+    Notice,
+    #[serde(rename = "info")]
+    /// info.
+    Info,
+    #[serde(rename = "debug")]
+    /// debug.
+    Debug,
+    #[serde(rename = "nolog")]
+    #[default]
+    /// nolog.
+    Nolog,
+}
+serde_plain::derive_display_from_serialize!(FirewallLogLevel);
+serde_plain::derive_fromstr_from_deserialize!(FirewallLogLevel);
+
+#[api]
+/// Firewall conntrack helper.
+#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
+pub enum FwConntrackHelper {
+    #[serde(rename = "amanda")]
+    /// amanda.
+    Amanda,
+    #[serde(rename = "ftp")]
+    /// ftp.
+    Ftp,
+    #[serde(rename = "irc")]
+    /// irc.
+    Irc,
+    #[serde(rename = "netbios-ns")]
+    /// netbios-ns.
+    NetbiosNs,
+    #[serde(rename = "pptp")]
+    /// pptp.
+    Pptp,
+    #[serde(rename = "sane")]
+    /// sane.
+    Sane,
+    #[serde(rename = "sip")]
+    /// sip.
+    Sip,
+    #[serde(rename = "snmp")]
+    /// snmp.
+    Snmp,
+    #[serde(rename = "tftp")]
+    /// tftp.
+    Tftp,
+}
+serde_plain::derive_display_from_serialize!(FwConntrackHelper);
+serde_plain::derive_fromstr_from_deserialize!(FwConntrackHelper);
+
+#[api(
+    properties: {
+        dhcp: {
+            default: false,
+            optional: true,
+        },
+        enable: {
+            default: false,
+            optional: true,
+        },
+        ipfilter: {
+            default: false,
+            optional: true,
+        },
+        log_level_in: {
+            optional: true,
+            type: FirewallLogLevel,
+        },
+        log_level_out: {
+            optional: true,
+            type: FirewallLogLevel,
+        },
+        macfilter: {
+            default: true,
+            optional: true,
+        },
+        ndp: {
+            default: false,
+            optional: true,
+        },
+        policy_in: {
+            optional: true,
+            type: FirewallIOPolicy,
+        },
+        policy_out: {
+            optional: true,
+            type: FirewallIOPolicy,
+        },
+        radv: {
+            default: false,
+            optional: true,
+        },
+    },
+)]
+/// Object.
+#[derive(Debug, serde::Deserialize, serde::Serialize)]
+pub struct GuestFirewallOptions {
+    /// Enable DHCP.
+    #[serde(deserialize_with = "proxmox_serde::perl::deserialize_bool")]
+    #[serde(default, skip_serializing_if = "Option::is_none")]
+    pub dhcp: Option<bool>,
+
+    /// Enable/disable firewall rules.
+    #[serde(deserialize_with = "proxmox_serde::perl::deserialize_bool")]
+    #[serde(default, skip_serializing_if = "Option::is_none")]
+    pub enable: Option<bool>,
+
+    /// Enable default IP filters. This is equivalent to adding an empty
+    /// ipfilter-net<id> ipset for every interface. Such ipsets implicitly
+    /// contain sane default restrictions such as restricting IPv6 link local
+    /// addresses to the one derived from the interface's MAC address. For
+    /// containers the configured IP addresses will be implicitly added.
+    #[serde(deserialize_with = "proxmox_serde::perl::deserialize_bool")]
+    #[serde(default, skip_serializing_if = "Option::is_none")]
+    pub ipfilter: Option<bool>,
+
+    #[serde(default, skip_serializing_if = "Option::is_none")]
+    pub log_level_in: Option<FirewallLogLevel>,
+
+    #[serde(default, skip_serializing_if = "Option::is_none")]
+    pub log_level_out: Option<FirewallLogLevel>,
+
+    /// Enable/disable MAC address filter.
+    #[serde(deserialize_with = "proxmox_serde::perl::deserialize_bool")]
+    #[serde(default, skip_serializing_if = "Option::is_none")]
+    pub macfilter: Option<bool>,
+
+    /// Enable NDP (Neighbor Discovery Protocol).
+    #[serde(deserialize_with = "proxmox_serde::perl::deserialize_bool")]
+    #[serde(default, skip_serializing_if = "Option::is_none")]
+    pub ndp: Option<bool>,
+
+    #[serde(default, skip_serializing_if = "Option::is_none")]
+    pub policy_in: Option<FirewallIOPolicy>,
+
+    #[serde(default, skip_serializing_if = "Option::is_none")]
+    pub policy_out: Option<FirewallIOPolicy>,
+
+    /// Allow sending Router Advertisement.
+    #[serde(deserialize_with = "proxmox_serde::perl::deserialize_bool")]
+    #[serde(default, skip_serializing_if = "Option::is_none")]
+    pub radv: Option<bool>,
+}
+
 #[api]
 /// A guest's run state.
 #[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
@@ -1679,6 +1972,137 @@ pub enum ListControllersType {
 serde_plain::derive_display_from_serialize!(ListControllersType);
 serde_plain::derive_fromstr_from_deserialize!(ListControllersType);
 
+#[api(
+    properties: {
+        action: {
+            type: String,
+        },
+        comment: {
+            optional: true,
+            type: String,
+        },
+        dest: {
+            optional: true,
+            type: String,
+        },
+        dport: {
+            optional: true,
+            type: String,
+        },
+        enable: {
+            optional: true,
+            type: Integer,
+        },
+        "icmp-type": {
+            optional: true,
+            type: String,
+        },
+        iface: {
+            optional: true,
+            type: String,
+        },
+        ipversion: {
+            optional: true,
+            type: Integer,
+        },
+        log: {
+            optional: true,
+            type: FirewallLogLevel,
+        },
+        "macro": {
+            optional: true,
+            type: String,
+        },
+        pos: {
+            type: Integer,
+        },
+        proto: {
+            optional: true,
+            type: String,
+        },
+        source: {
+            optional: true,
+            type: String,
+        },
+        sport: {
+            optional: true,
+            type: String,
+        },
+        type: {
+            type: String,
+        },
+    },
+)]
+/// Object.
+#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)]
+pub struct ListFirewallRules {
+    /// Rule action ('ACCEPT', 'DROP', 'REJECT') or security group name
+    pub action: String,
+
+    /// Descriptive comment
+    #[serde(default, skip_serializing_if = "Option::is_none")]
+    pub comment: Option<String>,
+
+    /// Restrict packet destination address
+    #[serde(default, skip_serializing_if = "Option::is_none")]
+    pub dest: Option<String>,
+
+    /// Restrict TCP/UDP destination port
+    #[serde(default, skip_serializing_if = "Option::is_none")]
+    pub dport: Option<String>,
+
+    /// Flag to enable/disable a rule
+    #[serde(deserialize_with = "proxmox_serde::perl::deserialize_i64")]
+    #[serde(default, skip_serializing_if = "Option::is_none")]
+    pub enable: Option<i64>,
+
+    /// Specify icmp-type. Only valid if proto equals 'icmp' or
+    /// 'icmpv6'/'ipv6-icmp'
+    #[serde(default, skip_serializing_if = "Option::is_none")]
+    #[serde(rename = "icmp-type")]
+    pub icmp_type: Option<String>,
+
+    /// Network interface name. You have to use network configuration key names
+    /// for VMs and containers
+    #[serde(default, skip_serializing_if = "Option::is_none")]
+    pub iface: Option<String>,
+
+    /// IP version (4 or 6) - automatically determined from source/dest
+    /// addresses
+    #[serde(deserialize_with = "proxmox_serde::perl::deserialize_i64")]
+    #[serde(default, skip_serializing_if = "Option::is_none")]
+    pub ipversion: Option<i64>,
+
+    #[serde(default, skip_serializing_if = "Option::is_none")]
+    pub log: Option<FirewallLogLevel>,
+
+    /// Use predefined standard macro
+    #[serde(default, skip_serializing_if = "Option::is_none")]
+    #[serde(rename = "macro")]
+    pub r#macro: Option<String>,
+
+    /// Rule position in the ruleset
+    #[serde(deserialize_with = "proxmox_serde::perl::deserialize_i64")]
+    pub pos: i64,
+
+    /// IP protocol. You can use protocol names ('tcp'/'udp') or simple numbers,
+    /// as defined in '/etc/protocols'
+    #[serde(default, skip_serializing_if = "Option::is_none")]
+    pub proto: Option<String>,
+
+    /// Restrict packet source address
+    #[serde(default, skip_serializing_if = "Option::is_none")]
+    pub source: Option<String>,
+
+    /// Restrict TCP/UDP source port
+    #[serde(default, skip_serializing_if = "Option::is_none")]
+    pub sport: Option<String>,
+
+    /// Rule type
+    #[serde(rename = "type")]
+    pub ty: String,
+}
+
 #[api]
 /// Only list specific interface types.
 #[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
@@ -3594,29 +4018,131 @@ pub struct MigrateQemu {
     pub with_local_disks: Option<bool>,
 }
 
-const_regex! {
+const NODE_FIREWALL_OPTIONS_NF_CONNTRACK_HELPERS: Schema =
+    proxmox_schema::ArraySchema::new("list", &FwConntrackHelper::API_SCHEMA).schema();
 
-NETWORK_INTERFACE_BOND_PRIMARY_RE = r##"^[a-zA-Z][a-zA-Z0-9_]{1,20}([:\.]\d+)?$"##;
-NETWORK_INTERFACE_BRIDGE_PORTS_RE = r##"^[a-zA-Z][a-zA-Z0-9_]{1,20}([:\.]\d+)?$"##;
-NETWORK_INTERFACE_IFACE_RE = r##"^[a-zA-Z][a-zA-Z0-9_]{1,20}([:\.]\d+)?$"##;
-NETWORK_INTERFACE_OVS_BONDS_RE = r##"^[a-zA-Z][a-zA-Z0-9_]{1,20}([:\.]\d+)?$"##;
-NETWORK_INTERFACE_OVS_BRIDGE_RE = r##"^[a-zA-Z][a-zA-Z0-9_]{1,20}([:\.]\d+)?$"##;
-NETWORK_INTERFACE_OVS_PORTS_RE = r##"^[a-zA-Z][a-zA-Z0-9_]{1,20}([:\.]\d+)?$"##;
-NETWORK_INTERFACE_SLAVES_RE = r##"^[a-zA-Z][a-zA-Z0-9_]{1,20}([:\.]\d+)?$"##;
-NETWORK_INTERFACE_VLAN_RAW_DEVICE_RE = r##"^[a-zA-Z][a-zA-Z0-9_]{1,20}([:\.]\d+)?$"##;
+mod node_firewall_options_nf_conntrack_helpers {
+    use serde::{Deserialize, Deserializer, Serialize, Serializer};
 
-}
+    #[doc(hidden)]
+    pub trait Ser: Sized {
+        fn ser<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error>;
+        fn de<'de, D>(deserializer: D) -> Result<Self, D::Error>
+        where
+            D: Deserializer<'de>;
+    }
 
-#[test]
-fn test_regex_compilation_14() {
-    use regex::Regex;
-    let _: &Regex = &NETWORK_INTERFACE_BOND_PRIMARY_RE;
-    let _: &Regex = &NETWORK_INTERFACE_BRIDGE_PORTS_RE;
-    let _: &Regex = &NETWORK_INTERFACE_IFACE_RE;
-    let _: &Regex = &NETWORK_INTERFACE_OVS_BONDS_RE;
-    let _: &Regex = &NETWORK_INTERFACE_OVS_BRIDGE_RE;
-    let _: &Regex = &NETWORK_INTERFACE_OVS_PORTS_RE;
-    let _: &Regex = &NETWORK_INTERFACE_SLAVES_RE;
+    impl<T: Serialize + for<'a> Deserialize<'a>> Ser for Vec<T> {
+        fn ser<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+        where
+            S: Serializer,
+        {
+            super::stringlist::serialize(
+                &self[..],
+                serializer,
+                &super::NODE_FIREWALL_OPTIONS_NF_CONNTRACK_HELPERS,
+            )
+        }
+
+        fn de<'de, D>(deserializer: D) -> Result<Self, D::Error>
+        where
+            D: Deserializer<'de>,
+        {
+            super::stringlist::deserialize(
+                deserializer,
+                &super::NODE_FIREWALL_OPTIONS_NF_CONNTRACK_HELPERS,
+            )
+        }
+    }
+
+    impl<T: Ser> Ser for Option<T> {
+        fn ser<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+        where
+            S: Serializer,
+        {
+            match self {
+                None => serializer.serialize_none(),
+                Some(inner) => inner.ser(serializer),
+            }
+        }
+
+        fn de<'de, D>(deserializer: D) -> Result<Self, D::Error>
+        where
+            D: Deserializer<'de>,
+        {
+            use std::fmt;
+            use std::marker::PhantomData;
+
+            struct V<T: Ser>(PhantomData<T>);
+
+            impl<'de, T: Ser> serde::de::Visitor<'de> for V<T> {
+                type Value = Option<T>;
+
+                fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
+                    f.write_str("an optional string")
+                }
+
+                fn visit_none<E: serde::de::Error>(self) -> Result<Self::Value, E> {
+                    Ok(None)
+                }
+
+                fn visit_some<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
+                where
+                    D: Deserializer<'de>,
+                {
+                    T::de(deserializer).map(Some)
+                }
+
+                fn visit_str<E: serde::de::Error>(self, value: &str) -> Result<Self::Value, E> {
+                    use serde::de::IntoDeserializer;
+                    T::de(value.into_deserializer()).map(Some)
+                }
+            }
+
+            deserializer.deserialize_option(V::<T>(PhantomData))
+        }
+    }
+
+    pub fn serialize<T, S>(this: &T, serializer: S) -> Result<S::Ok, S::Error>
+    where
+        S: serde::Serializer,
+        T: Ser,
+    {
+        this.ser(serializer)
+    }
+
+    pub fn deserialize<'de, T, D>(deserializer: D) -> Result<T, D::Error>
+    where
+        D: serde::Deserializer<'de>,
+        T: Ser,
+    {
+        T::de(deserializer)
+    }
+}
+
+const_regex! {
+
+NETWORK_INTERFACE_BOND_PRIMARY_RE = r##"^[a-zA-Z][a-zA-Z0-9_]{1,20}([:\.]\d+)?$"##;
+NETWORK_INTERFACE_BRIDGE_PORTS_RE = r##"^[a-zA-Z][a-zA-Z0-9_]{1,20}([:\.]\d+)?$"##;
+NETWORK_INTERFACE_IFACE_RE = r##"^[a-zA-Z][a-zA-Z0-9_]{1,20}([:\.]\d+)?$"##;
+NETWORK_INTERFACE_OVS_BONDS_RE = r##"^[a-zA-Z][a-zA-Z0-9_]{1,20}([:\.]\d+)?$"##;
+NETWORK_INTERFACE_OVS_BRIDGE_RE = r##"^[a-zA-Z][a-zA-Z0-9_]{1,20}([:\.]\d+)?$"##;
+NETWORK_INTERFACE_OVS_PORTS_RE = r##"^[a-zA-Z][a-zA-Z0-9_]{1,20}([:\.]\d+)?$"##;
+NETWORK_INTERFACE_SLAVES_RE = r##"^[a-zA-Z][a-zA-Z0-9_]{1,20}([:\.]\d+)?$"##;
+NETWORK_INTERFACE_VLAN_RAW_DEVICE_RE = r##"^[a-zA-Z][a-zA-Z0-9_]{1,20}([:\.]\d+)?$"##;
+
+}
+
+#[test]
+fn test_regex_compilation_14() {
+    use regex::Regex;
+    let _: &Regex = &NETWORK_INTERFACE_BOND_PRIMARY_RE;
+    let _: &Regex = &NETWORK_INTERFACE_BRIDGE_PORTS_RE;
+    let _: &Regex = &NETWORK_INTERFACE_IFACE_RE;
+    let _: &Regex = &NETWORK_INTERFACE_OVS_BONDS_RE;
+    let _: &Regex = &NETWORK_INTERFACE_OVS_BRIDGE_RE;
+    let _: &Regex = &NETWORK_INTERFACE_OVS_PORTS_RE;
+    let _: &Regex = &NETWORK_INTERFACE_SLAVES_RE;
     let _: &Regex = &NETWORK_INTERFACE_VLAN_RAW_DEVICE_RE;
 }
 #[api(
@@ -4223,6 +4749,187 @@ pub enum NetworkInterfaceVlanProtocol {
 serde_plain::derive_display_from_serialize!(NetworkInterfaceVlanProtocol);
 serde_plain::derive_fromstr_from_deserialize!(NetworkInterfaceVlanProtocol);
 
+#[api(
+    properties: {
+        enable: {
+            default: true,
+            optional: true,
+        },
+        log_level_forward: {
+            optional: true,
+            type: FirewallLogLevel,
+        },
+        log_level_in: {
+            optional: true,
+            type: FirewallLogLevel,
+        },
+        log_level_out: {
+            optional: true,
+            type: FirewallLogLevel,
+        },
+        log_nf_conntrack: {
+            default: false,
+            optional: true,
+        },
+        ndp: {
+            default: false,
+            optional: true,
+        },
+        nf_conntrack_allow_invalid: {
+            default: false,
+            optional: true,
+        },
+        nf_conntrack_helpers: {
+            default: "",
+            format: &ApiStringFormat::PropertyString(&NODE_FIREWALL_OPTIONS_NF_CONNTRACK_HELPERS),
+            optional: true,
+            type: String,
+        },
+        nf_conntrack_max: {
+            default: 262144,
+            minimum: 32768,
+            optional: true,
+            type: Integer,
+        },
+        nf_conntrack_tcp_timeout_established: {
+            default: 432000,
+            minimum: 7875,
+            optional: true,
+            type: Integer,
+        },
+        nf_conntrack_tcp_timeout_syn_recv: {
+            default: 60,
+            maximum: 60,
+            minimum: 30,
+            optional: true,
+            type: Integer,
+        },
+        nftables: {
+            default: false,
+            optional: true,
+        },
+        nosmurfs: {
+            default: false,
+            optional: true,
+        },
+        protection_synflood: {
+            default: false,
+            optional: true,
+        },
+        protection_synflood_burst: {
+            default: 1000,
+            optional: true,
+            type: Integer,
+        },
+        protection_synflood_rate: {
+            default: 200,
+            optional: true,
+            type: Integer,
+        },
+        smurf_log_level: {
+            optional: true,
+            type: FirewallLogLevel,
+        },
+        tcp_flags_log_level: {
+            optional: true,
+            type: FirewallLogLevel,
+        },
+        tcpflags: {
+            default: false,
+            optional: true,
+        },
+    },
+)]
+/// Object.
+#[derive(Debug, serde::Deserialize, serde::Serialize)]
+pub struct NodeFirewallOptions {
+    /// Enable host firewall rules.
+    #[serde(deserialize_with = "proxmox_serde::perl::deserialize_bool")]
+    #[serde(default, skip_serializing_if = "Option::is_none")]
+    pub enable: Option<bool>,
+
+    #[serde(default, skip_serializing_if = "Option::is_none")]
+    pub log_level_forward: Option<FirewallLogLevel>,
+
+    #[serde(default, skip_serializing_if = "Option::is_none")]
+    pub log_level_in: Option<FirewallLogLevel>,
+
+    #[serde(default, skip_serializing_if = "Option::is_none")]
+    pub log_level_out: Option<FirewallLogLevel>,
+
+    /// Enable logging of conntrack information.
+    #[serde(deserialize_with = "proxmox_serde::perl::deserialize_bool")]
+    #[serde(default, skip_serializing_if = "Option::is_none")]
+    pub log_nf_conntrack: Option<bool>,
+
+    /// Enable NDP (Neighbor Discovery Protocol).
+    #[serde(deserialize_with = "proxmox_serde::perl::deserialize_bool")]
+    #[serde(default, skip_serializing_if = "Option::is_none")]
+    pub ndp: Option<bool>,
+
+    /// Allow invalid packets on connection tracking.
+    #[serde(deserialize_with = "proxmox_serde::perl::deserialize_bool")]
+    #[serde(default, skip_serializing_if = "Option::is_none")]
+    pub nf_conntrack_allow_invalid: Option<bool>,
+
+    /// Enable conntrack helpers for specific protocols. Supported protocols:
+    /// amanda, ftp, irc, netbios-ns, pptp, sane, sip, snmp, tftp
+    #[serde(with = "node_firewall_options_nf_conntrack_helpers")]
+    #[serde(default, skip_serializing_if = "Option::is_none")]
+    pub nf_conntrack_helpers: Option<Vec<FwConntrackHelper>>,
+
+    /// Maximum number of tracked connections.
+    #[serde(deserialize_with = "proxmox_serde::perl::deserialize_u64")]
+    #[serde(default, skip_serializing_if = "Option::is_none")]
+    pub nf_conntrack_max: Option<u64>,
+
+    /// Conntrack established timeout.
+    #[serde(deserialize_with = "proxmox_serde::perl::deserialize_u64")]
+    #[serde(default, skip_serializing_if = "Option::is_none")]
+    pub nf_conntrack_tcp_timeout_established: Option<u64>,
+
+    /// Conntrack syn recv timeout.
+    #[serde(deserialize_with = "proxmox_serde::perl::deserialize_u8")]
+    #[serde(default, skip_serializing_if = "Option::is_none")]
+    pub nf_conntrack_tcp_timeout_syn_recv: Option<u8>,
+
+    /// Enable nftables based firewall (tech preview)
+    #[serde(deserialize_with = "proxmox_serde::perl::deserialize_bool")]
+    #[serde(default, skip_serializing_if = "Option::is_none")]
+    pub nftables: Option<bool>,
+
+    /// Enable SMURFS filter.
+    #[serde(deserialize_with = "proxmox_serde::perl::deserialize_bool")]
+    #[serde(default, skip_serializing_if = "Option::is_none")]
+    pub nosmurfs: Option<bool>,
+
+    /// Enable synflood protection
+    #[serde(deserialize_with = "proxmox_serde::perl::deserialize_bool")]
+    #[serde(default, skip_serializing_if = "Option::is_none")]
+    pub protection_synflood: Option<bool>,
+
+    /// Synflood protection rate burst by ip src.
+    #[serde(deserialize_with = "proxmox_serde::perl::deserialize_i64")]
+    #[serde(default, skip_serializing_if = "Option::is_none")]
+    pub protection_synflood_burst: Option<i64>,
+
+    /// Synflood protection rate syn/sec by ip src.
+    #[serde(deserialize_with = "proxmox_serde::perl::deserialize_i64")]
+    #[serde(default, skip_serializing_if = "Option::is_none")]
+    pub protection_synflood_rate: Option<i64>,
+
+    #[serde(default, skip_serializing_if = "Option::is_none")]
+    pub smurf_log_level: Option<FirewallLogLevel>,
+
+    #[serde(default, skip_serializing_if = "Option::is_none")]
+    pub tcp_flags_log_level: Option<FirewallLogLevel>,
+
+    /// Filter illegal combinations of TCP flags.
+    #[serde(deserialize_with = "proxmox_serde::perl::deserialize_bool")]
+    #[serde(default, skip_serializing_if = "Option::is_none")]
+    pub tcpflags: Option<bool>,
+}
+
 #[api(
     additional_properties: "additional_properties",
     properties: {
@@ -11761,43 +12468,580 @@ pub struct TaskStatus {
     pub user: String,
 }
 
-const_regex! {
+const UPDATE_NODE_FIREWALL_OPTIONS_NF_CONNTRACK_HELPERS: Schema =
+    proxmox_schema::ArraySchema::new("list", &FwConntrackHelper::API_SCHEMA).schema();
 
-UPDATE_QEMU_CONFIG_AFFINITY_RE = r##"^(\s*\d+(-\d+)?\s*)(,\s*\d+(-\d+)?\s*)?$"##;
-UPDATE_QEMU_CONFIG_BOOTDISK_RE = r##"^(ide|sata|scsi|virtio|efidisk|tpmstate)\d+$"##;
-UPDATE_QEMU_CONFIG_DELETE_RE = r##"^(?i:[a-z][a-z0-9_-]+)$"##;
-UPDATE_QEMU_CONFIG_IMPORT_WORKING_STORAGE_RE = r##"^(?i:[a-z][a-z0-9\-_.]*[a-z0-9])$"##;
-UPDATE_QEMU_CONFIG_REVERT_RE = r##"^(?i:[a-z][a-z0-9_-]+)$"##;
-UPDATE_QEMU_CONFIG_SSHKEYS_RE = r##"^[-%a-zA-Z0-9_.!~*'()]*$"##;
-UPDATE_QEMU_CONFIG_TAGS_RE = r##"^(?i)[a-z0-9_][a-z0-9_\-+.]*$"##;
-UPDATE_QEMU_CONFIG_VMSTATESTORAGE_RE = r##"^(?i:[a-z][a-z0-9\-_.]*[a-z0-9])$"##;
+mod update_node_firewall_options_nf_conntrack_helpers {
+    use serde::{Deserialize, Deserializer, Serialize, Serializer};
 
-}
+    #[doc(hidden)]
+    pub trait Ser: Sized {
+        fn ser<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error>;
+        fn de<'de, D>(deserializer: D) -> Result<Self, D::Error>
+        where
+            D: Deserializer<'de>;
+    }
 
-#[test]
-fn test_regex_compilation_35() {
-    use regex::Regex;
-    let _: &Regex = &UPDATE_QEMU_CONFIG_AFFINITY_RE;
-    let _: &Regex = &UPDATE_QEMU_CONFIG_BOOTDISK_RE;
-    let _: &Regex = &UPDATE_QEMU_CONFIG_DELETE_RE;
-    let _: &Regex = &UPDATE_QEMU_CONFIG_IMPORT_WORKING_STORAGE_RE;
-    let _: &Regex = &UPDATE_QEMU_CONFIG_REVERT_RE;
-    let _: &Regex = &UPDATE_QEMU_CONFIG_SSHKEYS_RE;
-    let _: &Regex = &UPDATE_QEMU_CONFIG_TAGS_RE;
-    let _: &Regex = &UPDATE_QEMU_CONFIG_VMSTATESTORAGE_RE;
-}
-#[api(
-    properties: {
-        acpi: {
-            default: true,
-            optional: true,
-        },
-        affinity: {
-            format: &ApiStringFormat::Pattern(&UPDATE_QEMU_CONFIG_AFFINITY_RE),
-            optional: true,
-            type: String,
-        },
-        agent: {
+    impl<T: Serialize + for<'a> Deserialize<'a>> Ser for Vec<T> {
+        fn ser<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+        where
+            S: Serializer,
+        {
+            super::stringlist::serialize(
+                &self[..],
+                serializer,
+                &super::UPDATE_NODE_FIREWALL_OPTIONS_NF_CONNTRACK_HELPERS,
+            )
+        }
+
+        fn de<'de, D>(deserializer: D) -> Result<Self, D::Error>
+        where
+            D: Deserializer<'de>,
+        {
+            super::stringlist::deserialize(
+                deserializer,
+                &super::UPDATE_NODE_FIREWALL_OPTIONS_NF_CONNTRACK_HELPERS,
+            )
+        }
+    }
+
+    impl<T: Ser> Ser for Option<T> {
+        fn ser<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+        where
+            S: Serializer,
+        {
+            match self {
+                None => serializer.serialize_none(),
+                Some(inner) => inner.ser(serializer),
+            }
+        }
+
+        fn de<'de, D>(deserializer: D) -> Result<Self, D::Error>
+        where
+            D: Deserializer<'de>,
+        {
+            use std::fmt;
+            use std::marker::PhantomData;
+
+            struct V<T: Ser>(PhantomData<T>);
+
+            impl<'de, T: Ser> serde::de::Visitor<'de> for V<T> {
+                type Value = Option<T>;
+
+                fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
+                    f.write_str("an optional string")
+                }
+
+                fn visit_none<E: serde::de::Error>(self) -> Result<Self::Value, E> {
+                    Ok(None)
+                }
+
+                fn visit_some<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
+                where
+                    D: Deserializer<'de>,
+                {
+                    T::de(deserializer).map(Some)
+                }
+
+                fn visit_str<E: serde::de::Error>(self, value: &str) -> Result<Self::Value, E> {
+                    use serde::de::IntoDeserializer;
+                    T::de(value.into_deserializer()).map(Some)
+                }
+            }
+
+            deserializer.deserialize_option(V::<T>(PhantomData))
+        }
+    }
+
+    pub fn serialize<T, S>(this: &T, serializer: S) -> Result<S::Ok, S::Error>
+    where
+        S: serde::Serializer,
+        T: Ser,
+    {
+        this.ser(serializer)
+    }
+
+    pub fn deserialize<'de, T, D>(deserializer: D) -> Result<T, D::Error>
+    where
+        D: serde::Deserializer<'de>,
+        T: Ser,
+    {
+        T::de(deserializer)
+    }
+}
+
+const_regex! {
+
+UPDATE_CLUSTER_FIREWALL_OPTIONS_DELETE_RE = r##"^(?i:[a-z][a-z0-9_-]+)$"##;
+
+}
+
+#[test]
+fn test_regex_compilation_35() {
+    use regex::Regex;
+    let _: &Regex = &UPDATE_CLUSTER_FIREWALL_OPTIONS_DELETE_RE;
+}
+#[api(
+    properties: {
+        delete: {
+            items: {
+                description: "List item of type pve-configid.",
+                format: &ApiStringFormat::Pattern(&UPDATE_CLUSTER_FIREWALL_OPTIONS_DELETE_RE),
+                type: String,
+            },
+            optional: true,
+            type: Array,
+        },
+        digest: {
+            max_length: 64,
+            optional: true,
+            type: String,
+        },
+        ebtables: {
+            default: true,
+            optional: true,
+        },
+        enable: {
+            default: 0,
+            minimum: 0,
+            optional: true,
+            type: Integer,
+        },
+        log_ratelimit: {
+            format: &ApiStringFormat::PropertyString(&ClusterFirewallOptionsLogRatelimit::API_SCHEMA),
+            optional: true,
+            type: String,
+        },
+        policy_forward: {
+            optional: true,
+            type: FirewallFWPolicy,
+        },
+        policy_in: {
+            optional: true,
+            type: FirewallIOPolicy,
+        },
+        policy_out: {
+            optional: true,
+            type: FirewallIOPolicy,
+        },
+    },
+)]
+/// Object.
+#[derive(Debug, serde::Deserialize, serde::Serialize)]
+pub struct UpdateClusterFirewallOptions {
+    /// A list of settings you want to delete.
+    #[serde(default, skip_serializing_if = "Option::is_none")]
+    pub delete: Option<Vec<String>>,
+
+    /// Prevent changes if current configuration file has a different digest.
+    /// This can be used to prevent concurrent modifications.
+    #[serde(default, skip_serializing_if = "Option::is_none")]
+    pub digest: Option<String>,
+
+    /// Enable ebtables rules cluster wide.
+    #[serde(deserialize_with = "proxmox_serde::perl::deserialize_bool")]
+    #[serde(default, skip_serializing_if = "Option::is_none")]
+    pub ebtables: Option<bool>,
+
+    /// Enable or disable the firewall cluster wide.
+    #[serde(deserialize_with = "proxmox_serde::perl::deserialize_u64")]
+    #[serde(default, skip_serializing_if = "Option::is_none")]
+    pub enable: Option<u64>,
+
+    /// Log ratelimiting settings
+    #[serde(default, skip_serializing_if = "Option::is_none")]
+    pub log_ratelimit: Option<String>,
+
+    #[serde(default, skip_serializing_if = "Option::is_none")]
+    pub policy_forward: Option<FirewallFWPolicy>,
+
+    #[serde(default, skip_serializing_if = "Option::is_none")]
+    pub policy_in: Option<FirewallIOPolicy>,
+
+    #[serde(default, skip_serializing_if = "Option::is_none")]
+    pub policy_out: Option<FirewallIOPolicy>,
+}
+
+const_regex! {
+
+UPDATE_GUEST_FIREWALL_OPTIONS_DELETE_RE = r##"^(?i:[a-z][a-z0-9_-]+)$"##;
+
+}
+
+#[test]
+fn test_regex_compilation_36() {
+    use regex::Regex;
+    let _: &Regex = &UPDATE_GUEST_FIREWALL_OPTIONS_DELETE_RE;
+}
+#[api(
+    properties: {
+        delete: {
+            items: {
+                description: "List item of type pve-configid.",
+                format: &ApiStringFormat::Pattern(&UPDATE_GUEST_FIREWALL_OPTIONS_DELETE_RE),
+                type: String,
+            },
+            optional: true,
+            type: Array,
+        },
+        dhcp: {
+            default: false,
+            optional: true,
+        },
+        digest: {
+            max_length: 64,
+            optional: true,
+            type: String,
+        },
+        enable: {
+            default: false,
+            optional: true,
+        },
+        ipfilter: {
+            default: false,
+            optional: true,
+        },
+        log_level_in: {
+            optional: true,
+            type: FirewallLogLevel,
+        },
+        log_level_out: {
+            optional: true,
+            type: FirewallLogLevel,
+        },
+        macfilter: {
+            default: true,
+            optional: true,
+        },
+        ndp: {
+            default: false,
+            optional: true,
+        },
+        policy_in: {
+            optional: true,
+            type: FirewallIOPolicy,
+        },
+        policy_out: {
+            optional: true,
+            type: FirewallIOPolicy,
+        },
+        radv: {
+            default: false,
+            optional: true,
+        },
+    },
+)]
+/// Object.
+#[derive(Debug, serde::Deserialize, serde::Serialize)]
+pub struct UpdateGuestFirewallOptions {
+    /// A list of settings you want to delete.
+    #[serde(default, skip_serializing_if = "Option::is_none")]
+    pub delete: Option<Vec<String>>,
+
+    /// Enable DHCP.
+    #[serde(deserialize_with = "proxmox_serde::perl::deserialize_bool")]
+    #[serde(default, skip_serializing_if = "Option::is_none")]
+    pub dhcp: Option<bool>,
+
+    /// Prevent changes if current configuration file has a different digest.
+    /// This can be used to prevent concurrent modifications.
+    #[serde(default, skip_serializing_if = "Option::is_none")]
+    pub digest: Option<String>,
+
+    /// Enable/disable firewall rules.
+    #[serde(deserialize_with = "proxmox_serde::perl::deserialize_bool")]
+    #[serde(default, skip_serializing_if = "Option::is_none")]
+    pub enable: Option<bool>,
+
+    /// Enable default IP filters. This is equivalent to adding an empty
+    /// ipfilter-net<id> ipset for every interface. Such ipsets implicitly
+    /// contain sane default restrictions such as restricting IPv6 link local
+    /// addresses to the one derived from the interface's MAC address. For
+    /// containers the configured IP addresses will be implicitly added.
+    #[serde(deserialize_with = "proxmox_serde::perl::deserialize_bool")]
+    #[serde(default, skip_serializing_if = "Option::is_none")]
+    pub ipfilter: Option<bool>,
+
+    #[serde(default, skip_serializing_if = "Option::is_none")]
+    pub log_level_in: Option<FirewallLogLevel>,
+
+    #[serde(default, skip_serializing_if = "Option::is_none")]
+    pub log_level_out: Option<FirewallLogLevel>,
+
+    /// Enable/disable MAC address filter.
+    #[serde(deserialize_with = "proxmox_serde::perl::deserialize_bool")]
+    #[serde(default, skip_serializing_if = "Option::is_none")]
+    pub macfilter: Option<bool>,
+
+    /// Enable NDP (Neighbor Discovery Protocol).
+    #[serde(deserialize_with = "proxmox_serde::perl::deserialize_bool")]
+    #[serde(default, skip_serializing_if = "Option::is_none")]
+    pub ndp: Option<bool>,
+
+    #[serde(default, skip_serializing_if = "Option::is_none")]
+    pub policy_in: Option<FirewallIOPolicy>,
+
+    #[serde(default, skip_serializing_if = "Option::is_none")]
+    pub policy_out: Option<FirewallIOPolicy>,
+
+    /// Allow sending Router Advertisement.
+    #[serde(deserialize_with = "proxmox_serde::perl::deserialize_bool")]
+    #[serde(default, skip_serializing_if = "Option::is_none")]
+    pub radv: Option<bool>,
+}
+
+const_regex! {
+
+UPDATE_NODE_FIREWALL_OPTIONS_DELETE_RE = r##"^(?i:[a-z][a-z0-9_-]+)$"##;
+
+}
+
+#[test]
+fn test_regex_compilation_37() {
+    use regex::Regex;
+    let _: &Regex = &UPDATE_NODE_FIREWALL_OPTIONS_DELETE_RE;
+}
+#[api(
+    properties: {
+        delete: {
+            items: {
+                description: "List item of type pve-configid.",
+                format: &ApiStringFormat::Pattern(&UPDATE_NODE_FIREWALL_OPTIONS_DELETE_RE),
+                type: String,
+            },
+            optional: true,
+            type: Array,
+        },
+        digest: {
+            max_length: 64,
+            optional: true,
+            type: String,
+        },
+        enable: {
+            default: true,
+            optional: true,
+        },
+        log_level_forward: {
+            optional: true,
+            type: FirewallLogLevel,
+        },
+        log_level_in: {
+            optional: true,
+            type: FirewallLogLevel,
+        },
+        log_level_out: {
+            optional: true,
+            type: FirewallLogLevel,
+        },
+        log_nf_conntrack: {
+            default: false,
+            optional: true,
+        },
+        ndp: {
+            default: false,
+            optional: true,
+        },
+        nf_conntrack_allow_invalid: {
+            default: false,
+            optional: true,
+        },
+        nf_conntrack_helpers: {
+            default: "",
+            format: &ApiStringFormat::PropertyString(&UPDATE_NODE_FIREWALL_OPTIONS_NF_CONNTRACK_HELPERS),
+            optional: true,
+            type: String,
+        },
+        nf_conntrack_max: {
+            default: 262144,
+            minimum: 32768,
+            optional: true,
+            type: Integer,
+        },
+        nf_conntrack_tcp_timeout_established: {
+            default: 432000,
+            minimum: 7875,
+            optional: true,
+            type: Integer,
+        },
+        nf_conntrack_tcp_timeout_syn_recv: {
+            default: 60,
+            maximum: 60,
+            minimum: 30,
+            optional: true,
+            type: Integer,
+        },
+        nftables: {
+            default: false,
+            optional: true,
+        },
+        nosmurfs: {
+            default: false,
+            optional: true,
+        },
+        protection_synflood: {
+            default: false,
+            optional: true,
+        },
+        protection_synflood_burst: {
+            default: 1000,
+            optional: true,
+            type: Integer,
+        },
+        protection_synflood_rate: {
+            default: 200,
+            optional: true,
+            type: Integer,
+        },
+        smurf_log_level: {
+            optional: true,
+            type: FirewallLogLevel,
+        },
+        tcp_flags_log_level: {
+            optional: true,
+            type: FirewallLogLevel,
+        },
+        tcpflags: {
+            default: false,
+            optional: true,
+        },
+    },
+)]
+/// Object.
+#[derive(Debug, serde::Deserialize, serde::Serialize)]
+pub struct UpdateNodeFirewallOptions {
+    /// A list of settings you want to delete.
+    #[serde(default, skip_serializing_if = "Option::is_none")]
+    pub delete: Option<Vec<String>>,
+
+    /// Prevent changes if current configuration file has a different digest.
+    /// This can be used to prevent concurrent modifications.
+    #[serde(default, skip_serializing_if = "Option::is_none")]
+    pub digest: Option<String>,
+
+    /// Enable host firewall rules.
+    #[serde(deserialize_with = "proxmox_serde::perl::deserialize_bool")]
+    #[serde(default, skip_serializing_if = "Option::is_none")]
+    pub enable: Option<bool>,
+
+    #[serde(default, skip_serializing_if = "Option::is_none")]
+    pub log_level_forward: Option<FirewallLogLevel>,
+
+    #[serde(default, skip_serializing_if = "Option::is_none")]
+    pub log_level_in: Option<FirewallLogLevel>,
+
+    #[serde(default, skip_serializing_if = "Option::is_none")]
+    pub log_level_out: Option<FirewallLogLevel>,
+
+    /// Enable logging of conntrack information.
+    #[serde(deserialize_with = "proxmox_serde::perl::deserialize_bool")]
+    #[serde(default, skip_serializing_if = "Option::is_none")]
+    pub log_nf_conntrack: Option<bool>,
+
+    /// Enable NDP (Neighbor Discovery Protocol).
+    #[serde(deserialize_with = "proxmox_serde::perl::deserialize_bool")]
+    #[serde(default, skip_serializing_if = "Option::is_none")]
+    pub ndp: Option<bool>,
+
+    /// Allow invalid packets on connection tracking.
+    #[serde(deserialize_with = "proxmox_serde::perl::deserialize_bool")]
+    #[serde(default, skip_serializing_if = "Option::is_none")]
+    pub nf_conntrack_allow_invalid: Option<bool>,
+
+    /// Enable conntrack helpers for specific protocols. Supported protocols:
+    /// amanda, ftp, irc, netbios-ns, pptp, sane, sip, snmp, tftp
+    #[serde(with = "update_node_firewall_options_nf_conntrack_helpers")]
+    #[serde(default, skip_serializing_if = "Option::is_none")]
+    pub nf_conntrack_helpers: Option<Vec<FwConntrackHelper>>,
+
+    /// Maximum number of tracked connections.
+    #[serde(deserialize_with = "proxmox_serde::perl::deserialize_u64")]
+    #[serde(default, skip_serializing_if = "Option::is_none")]
+    pub nf_conntrack_max: Option<u64>,
+
+    /// Conntrack established timeout.
+    #[serde(deserialize_with = "proxmox_serde::perl::deserialize_u64")]
+    #[serde(default, skip_serializing_if = "Option::is_none")]
+    pub nf_conntrack_tcp_timeout_established: Option<u64>,
+
+    /// Conntrack syn recv timeout.
+    #[serde(deserialize_with = "proxmox_serde::perl::deserialize_u8")]
+    #[serde(default, skip_serializing_if = "Option::is_none")]
+    pub nf_conntrack_tcp_timeout_syn_recv: Option<u8>,
+
+    /// Enable nftables based firewall (tech preview)
+    #[serde(deserialize_with = "proxmox_serde::perl::deserialize_bool")]
+    #[serde(default, skip_serializing_if = "Option::is_none")]
+    pub nftables: Option<bool>,
+
+    /// Enable SMURFS filter.
+    #[serde(deserialize_with = "proxmox_serde::perl::deserialize_bool")]
+    #[serde(default, skip_serializing_if = "Option::is_none")]
+    pub nosmurfs: Option<bool>,
+
+    /// Enable synflood protection
+    #[serde(deserialize_with = "proxmox_serde::perl::deserialize_bool")]
+    #[serde(default, skip_serializing_if = "Option::is_none")]
+    pub protection_synflood: Option<bool>,
+
+    /// Synflood protection rate burst by ip src.
+    #[serde(deserialize_with = "proxmox_serde::perl::deserialize_i64")]
+    #[serde(default, skip_serializing_if = "Option::is_none")]
+    pub protection_synflood_burst: Option<i64>,
+
+    /// Synflood protection rate syn/sec by ip src.
+    #[serde(deserialize_with = "proxmox_serde::perl::deserialize_i64")]
+    #[serde(default, skip_serializing_if = "Option::is_none")]
+    pub protection_synflood_rate: Option<i64>,
+
+    #[serde(default, skip_serializing_if = "Option::is_none")]
+    pub smurf_log_level: Option<FirewallLogLevel>,
+
+    #[serde(default, skip_serializing_if = "Option::is_none")]
+    pub tcp_flags_log_level: Option<FirewallLogLevel>,
+
+    /// Filter illegal combinations of TCP flags.
+    #[serde(deserialize_with = "proxmox_serde::perl::deserialize_bool")]
+    #[serde(default, skip_serializing_if = "Option::is_none")]
+    pub tcpflags: Option<bool>,
+}
+
+const_regex! {
+
+UPDATE_QEMU_CONFIG_AFFINITY_RE = r##"^(\s*\d+(-\d+)?\s*)(,\s*\d+(-\d+)?\s*)?$"##;
+UPDATE_QEMU_CONFIG_BOOTDISK_RE = r##"^(ide|sata|scsi|virtio|efidisk|tpmstate)\d+$"##;
+UPDATE_QEMU_CONFIG_DELETE_RE = r##"^(?i:[a-z][a-z0-9_-]+)$"##;
+UPDATE_QEMU_CONFIG_IMPORT_WORKING_STORAGE_RE = r##"^(?i:[a-z][a-z0-9\-_.]*[a-z0-9])$"##;
+UPDATE_QEMU_CONFIG_REVERT_RE = r##"^(?i:[a-z][a-z0-9_-]+)$"##;
+UPDATE_QEMU_CONFIG_SSHKEYS_RE = r##"^[-%a-zA-Z0-9_.!~*'()]*$"##;
+UPDATE_QEMU_CONFIG_TAGS_RE = r##"^(?i)[a-z0-9_][a-z0-9_\-+.]*$"##;
+UPDATE_QEMU_CONFIG_VMSTATESTORAGE_RE = r##"^(?i:[a-z][a-z0-9\-_.]*[a-z0-9])$"##;
+
+}
+
+#[test]
+fn test_regex_compilation_38() {
+    use regex::Regex;
+    let _: &Regex = &UPDATE_QEMU_CONFIG_AFFINITY_RE;
+    let _: &Regex = &UPDATE_QEMU_CONFIG_BOOTDISK_RE;
+    let _: &Regex = &UPDATE_QEMU_CONFIG_DELETE_RE;
+    let _: &Regex = &UPDATE_QEMU_CONFIG_IMPORT_WORKING_STORAGE_RE;
+    let _: &Regex = &UPDATE_QEMU_CONFIG_REVERT_RE;
+    let _: &Regex = &UPDATE_QEMU_CONFIG_SSHKEYS_RE;
+    let _: &Regex = &UPDATE_QEMU_CONFIG_TAGS_RE;
+    let _: &Regex = &UPDATE_QEMU_CONFIG_VMSTATESTORAGE_RE;
+}
+#[api(
+    properties: {
+        acpi: {
+            default: true,
+            optional: true,
+        },
+        affinity: {
+            format: &ApiStringFormat::Pattern(&UPDATE_QEMU_CONFIG_AFFINITY_RE),
+            optional: true,
+            type: String,
+        },
+        agent: {
             format: &ApiStringFormat::PropertyString(&QemuConfigAgent::API_SCHEMA),
             optional: true,
             type: String,
@@ -12684,7 +13928,7 @@ UPDATE_QEMU_CONFIG_EFIDISK0_SIZE_RE = r##"^(\d+(\.\d+)?)([KMGT])?$"##;
 }
 
 #[test]
-fn test_regex_compilation_36() {
+fn test_regex_compilation_39() {
     use regex::Regex;
     let _: &Regex = &UPDATE_QEMU_CONFIG_EFIDISK0_SIZE_RE;
 }
@@ -12761,7 +14005,7 @@ UPDATE_QEMU_CONFIG_IDE_SIZE_RE = r##"^(\d+(\.\d+)?)([KMGT])?$"##;
 }
 
 #[test]
-fn test_regex_compilation_37() {
+fn test_regex_compilation_40() {
     use regex::Regex;
     let _: &Regex = &UPDATE_QEMU_CONFIG_IDE_MODEL_RE;
     let _: &Regex = &UPDATE_QEMU_CONFIG_IDE_SERIAL_RE;
@@ -13117,7 +14361,7 @@ UPDATE_QEMU_CONFIG_SATA_SIZE_RE = r##"^(\d+(\.\d+)?)([KMGT])?$"##;
 }
 
 #[test]
-fn test_regex_compilation_38() {
+fn test_regex_compilation_41() {
     use regex::Regex;
     let _: &Regex = &UPDATE_QEMU_CONFIG_SATA_SERIAL_RE;
     let _: &Regex = &UPDATE_QEMU_CONFIG_SATA_SIZE_RE;
@@ -13462,7 +14706,7 @@ UPDATE_QEMU_CONFIG_SCSI_SIZE_RE = r##"^(\d+(\.\d+)?)([KMGT])?$"##;
 }
 
 #[test]
-fn test_regex_compilation_39() {
+fn test_regex_compilation_42() {
     use regex::Regex;
     let _: &Regex = &UPDATE_QEMU_CONFIG_SCSI_SERIAL_RE;
     let _: &Regex = &UPDATE_QEMU_CONFIG_SCSI_SIZE_RE;
@@ -13862,7 +15106,7 @@ UPDATE_QEMU_CONFIG_TPMSTATE0_SIZE_RE = r##"^(\d+(\.\d+)?)([KMGT])?$"##;
 }
 
 #[test]
-fn test_regex_compilation_40() {
+fn test_regex_compilation_43() {
     use regex::Regex;
     let _: &Regex = &UPDATE_QEMU_CONFIG_TPMSTATE0_SIZE_RE;
 }
@@ -13918,7 +15162,7 @@ UPDATE_QEMU_CONFIG_VIRTIO_SIZE_RE = r##"^(\d+(\.\d+)?)([KMGT])?$"##;
 }
 
 #[test]
-fn test_regex_compilation_41() {
+fn test_regex_compilation_44() {
     use regex::Regex;
     let _: &Regex = &UPDATE_QEMU_CONFIG_VIRTIO_SERIAL_RE;
     let _: &Regex = &UPDATE_QEMU_CONFIG_VIRTIO_SIZE_RE;
-- 
2.47.3
_______________________________________________
pdm-devel mailing list
pdm-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pdm-devel
next prev parent reply	other threads:[~2025-10-30 14:34 UTC|newest]
Thread overview: 14+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-10-30 14:33 [pdm-devel] [PATCH proxmox{, -yew-comp, -datacenter-manager} 00/13] add basic integration of PVE firewall Hannes Laimer
2025-10-30 14:33 ` [pdm-devel] [PATCH proxmox 1/5] pve-api-types: update pve-api.json Hannes Laimer
2025-10-30 14:33 ` [pdm-devel] [PATCH proxmox 2/5] pve-api-types: add get/update firewall options endpoints Hannes Laimer
2025-10-30 14:33 ` [pdm-devel] [PATCH proxmox 3/5] pve-api-types: schema2rust: handle `macro` keyword like we do `type` Hannes Laimer
2025-10-30 14:33 ` [pdm-devel] [PATCH proxmox 4/5] pve-api-types: add list firewall rules endpoints Hannes Laimer
2025-10-30 14:33 ` Hannes Laimer [this message]
2025-10-30 14:33 ` [pdm-devel] [PATCH proxmox-yew-comp 1/4] form: add helpers for extractig data out of schemas Hannes Laimer
2025-10-30 14:34 ` [pdm-devel] [PATCH proxmox-yew-comp 2/4] firewall: add FirewallContext Hannes Laimer
2025-10-30 14:34 ` [pdm-devel] [PATCH proxmox-yew-comp 3/4] firewall: add options edit form Hannes Laimer
2025-10-30 14:34 ` [pdm-devel] [PATCH proxmox-yew-comp 4/4] firewall: add rules table Hannes Laimer
2025-10-30 14:34 ` [pdm-devel] [PATCH proxmox-datacenter-manager 1/4] pdm-api-types: add firewall status types Hannes Laimer
2025-10-30 14:34 ` [pdm-devel] [PATCH proxmox-datacenter-manager 2/4] api: firewall: add option, rules and status endpoints Hannes Laimer
2025-10-30 14:34 ` [pdm-devel] [PATCH proxmox-datacenter-manager 3/4] pdm-client: add api methods for firewall options, " Hannes Laimer
2025-10-30 14:34 ` [pdm-devel] [PATCH proxmox-datacenter-manager 4/4] ui: add firewall status tree Hannes Laimer
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=20251030143406.193744-6-h.laimer@proxmox.com \
    --to=h.laimer@proxmox.com \
    --cc=pdm-devel@lists.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