all lists on lists.proxmox.com
 help / color / mirror / Atom feed
* [PATCH proxmox{,-backup} 0/3] fix #6121: add 'bond-miimon' option for network interfaces
@ 2026-09-25 12:34 Christoph Heiss
  2026-09-25 12:34 ` [PATCH proxmox 1/3] fix #6121: network-api: api: add `bond-miimon` option for interfaces Christoph Heiss
                   ` (2 more replies)
  0 siblings, 3 replies; 4+ messages in thread
From: Christoph Heiss @ 2026-09-25 12:34 UTC (permalink / raw)
  To: pbs-devel

Fixes #6121 [0], for both PBS and (through proxmox-network-api) PDM.

Adds a new 'bond-miimon' option to the network interface API for
specifying the link monitoring interval.

This is needed so that (at least) in active-backup mode, the bond
correctly switches over to the backup link if the active one goes down.

[0] https://bugzilla.proxmox.com/show_bug.cgi?id=6121

proxmox:

Christoph Heiss (2):
  fix #6121: network-api: api: add `bond-miimon` option for interfaces
  fix #6121: network-api: config: add support for `bond-miimon` option

 proxmox-network-api/src/api_impl.rs      |  9 +++
 proxmox-network-api/src/api_types.rs     | 18 ++++++
 proxmox-network-api/src/config/lexer.rs  |  3 +
 proxmox-network-api/src/config/mod.rs    | 70 ++++++++++++++++++++++++
 proxmox-network-api/src/config/parser.rs | 23 ++++++++
 5 files changed, 123 insertions(+)

proxmox-backup:

Christoph Heiss (1):
  api: network: reuse interface create/update methods from network-api

 src/api2/node/network.rs | 674 ++-------------------------------------
 1 file changed, 18 insertions(+), 656 deletions(-)





^ permalink raw reply	[flat|nested] 4+ messages in thread

* [PATCH proxmox 1/3] fix #6121: network-api: api: add `bond-miimon` option for interfaces
  2026-09-25 12:34 [PATCH proxmox{,-backup} 0/3] fix #6121: add 'bond-miimon' option for network interfaces Christoph Heiss
@ 2026-09-25 12:34 ` Christoph Heiss
  2026-09-25 12:34 ` [PATCH proxmox 2/3] fix #6121: network-api: config: add support for `bond-miimon` option Christoph Heiss
  2026-09-25 12:34 ` [PATCH proxmox-backup 3/3] api: network: reuse interface create/update methods from network-api Christoph Heiss
  2 siblings, 0 replies; 4+ messages in thread
From: Christoph Heiss @ 2026-09-25 12:34 UTC (permalink / raw)
  To: pbs-devel

Partially fixes #6121 [0].

Adds a new 'bond-miimon' option to the network interface API for
specifying the link monitoring interval.

[0] https://bugzilla.proxmox.com/show_bug.cgi?id=6121

Signed-off-by: Christoph Heiss <c.heiss@proxmox.com>
---
 proxmox-network-api/src/api_impl.rs  |  9 +++++++++
 proxmox-network-api/src/api_types.rs | 18 ++++++++++++++++++
 2 files changed, 27 insertions(+)

diff --git a/proxmox-network-api/src/api_impl.rs b/proxmox-network-api/src/api_impl.rs
index 78f499c5..0a603017 100644
--- a/proxmox-network-api/src/api_impl.rs
+++ b/proxmox-network-api/src/api_impl.rs
@@ -162,6 +162,9 @@ pub fn create_interface(iface: String, config: InterfaceUpdater) -> Result<(), E
                     interface.bond_xmit_hash_policy = config.bond_xmit_hash_policy;
                 }
             }
+            if config.bond_miimon.is_some() {
+                interface.bond_miimon = config.bond_miimon;
+            }
             if let Some(slaves) = &config.slaves {
                 interface.set_bond_slave_list(slaves)?;
             }
@@ -295,6 +298,9 @@ pub fn update_interface(
                 DeletableInterfaceProperty::BondXmitHashPolicy => {
                     interface.bond_xmit_hash_policy = None
                 }
+                DeletableInterfaceProperty::BondMiimon => {
+                    interface.bond_miimon = None;
+                }
             }
         }
     }
@@ -335,6 +341,9 @@ pub fn update_interface(
             }
             interface.bond_xmit_hash_policy = update.bond_xmit_hash_policy;
         }
+        if update.bond_miimon.is_some() {
+            interface.bond_miimon = update.bond_miimon;
+        }
     }
 
     if let Some(cidr) = update.cidr {
diff --git a/proxmox-network-api/src/api_types.rs b/proxmox-network-api/src/api_types.rs
index f2467f1e..471f20e8 100644
--- a/proxmox-network-api/src/api_types.rs
+++ b/proxmox-network-api/src/api_types.rs
@@ -228,6 +228,12 @@ pub const NETWORK_INTERFACE_LIST_SCHEMA: Schema =
             type: BondXmitHashPolicy,
             optional: true,
         },
+        "bond-miimon": {
+            description: "Link monitoring interval in milliseconds for bond interfaces.",
+            type: u8,
+            optional: true,
+            default: 100,
+        },
         altnames: {
             description: "List of altnames for this interface",
             type: Array,
@@ -302,6 +308,8 @@ pub struct Interface {
     pub bond_primary: Option<String>,
     #[serde(skip_serializing_if = "Option::is_none")]
     pub bond_xmit_hash_policy: Option<BondXmitHashPolicy>,
+    #[serde(rename = "bond-miimon", skip_serializing_if = "Option::is_none")]
+    pub bond_miimon: Option<u8>,
 
     #[serde(default, skip_serializing_if = "Vec::is_empty")]
     pub altnames: Vec<String>,
@@ -333,6 +341,7 @@ impl Interface {
             bond_mode: None,
             bond_primary: None,
             bond_xmit_hash_policy: None,
+            bond_miimon: None,
             altnames: Vec::new(),
         }
     }
@@ -425,6 +434,8 @@ pub enum DeletableInterfaceProperty {
     /// Delete bond transmit hash policy
     #[serde(rename = "bond_xmit_hash_policy")]
     BondXmitHashPolicy,
+    /// Delete bond miimon (set to default 100)
+    BondMiimon,
 }
 
 #[api(
@@ -509,6 +520,12 @@ pub enum DeletableInterfaceProperty {
                 type: BondXmitHashPolicy,
                 optional: true,
             },
+            "bond-miimon": {
+                description: "Link monitoring interval in milliseconds for bond interfaces.",
+                type: u8,
+                optional: true,
+                default: 100,
+            },
             slaves: {
                 schema: NETWORK_INTERFACE_LIST_SCHEMA,
                 optional: true,
@@ -542,5 +559,6 @@ pub struct InterfaceUpdater {
     pub bond_primary: Option<String>,
     #[serde(rename = "bond_xmit_hash_policy")]
     pub bond_xmit_hash_policy: Option<BondXmitHashPolicy>,
+    pub bond_miimon: Option<u8>,
     pub slaves: Option<String>,
 }
-- 
2.55.0





^ permalink raw reply related	[flat|nested] 4+ messages in thread

* [PATCH proxmox 2/3] fix #6121: network-api: config: add support for `bond-miimon` option
  2026-09-25 12:34 [PATCH proxmox{,-backup} 0/3] fix #6121: add 'bond-miimon' option for network interfaces Christoph Heiss
  2026-09-25 12:34 ` [PATCH proxmox 1/3] fix #6121: network-api: api: add `bond-miimon` option for interfaces Christoph Heiss
@ 2026-09-25 12:34 ` Christoph Heiss
  2026-09-25 12:34 ` [PATCH proxmox-backup 3/3] api: network: reuse interface create/update methods from network-api Christoph Heiss
  2 siblings, 0 replies; 4+ messages in thread
From: Christoph Heiss @ 2026-09-25 12:34 UTC (permalink / raw)
  To: pbs-devel

Fixes #6121 [0].

Adds support for the 'bond-miimon' option to the ifupdown(2) config
writer and parser.

The option is always written out for bonds, with a default value of 100
- much like PVE does it.

This is needed so that (at least) in active-backup mode, the bond
correctly switches over to the backup link if the active one goes down.

[0] https://bugzilla.proxmox.com/show_bug.cgi?id=6121

Signed-off-by: Christoph Heiss <c.heiss@proxmox.com>
---
 proxmox-network-api/src/config/lexer.rs  |  3 +
 proxmox-network-api/src/config/mod.rs    | 70 ++++++++++++++++++++++++
 proxmox-network-api/src/config/parser.rs | 23 ++++++++
 3 files changed, 96 insertions(+)

diff --git a/proxmox-network-api/src/config/lexer.rs b/proxmox-network-api/src/config/lexer.rs
index 4729d462..2a646508 100644
--- a/proxmox-network-api/src/config/lexer.rs
+++ b/proxmox-network-api/src/config/lexer.rs
@@ -30,6 +30,7 @@ pub enum Token {
     BondMode,
     BondPrimary,
     BondXmitHashPolicy,
+    BondMiimon,
     EOF,
 }
 
@@ -62,6 +63,8 @@ static KEYWORDS: LazyLock<HashMap<&'static str, Token>> = LazyLock::new(|| {
     map.insert("bond_primary", Token::BondPrimary);
     map.insert("bond_xmit_hash_policy", Token::BondXmitHashPolicy);
     map.insert("bond-xmit-hash-policy", Token::BondXmitHashPolicy);
+    map.insert("bond-miimon", Token::BondMiimon);
+    map.insert("bond_miimon", Token::BondMiimon);
     map
 });
 
diff --git a/proxmox-network-api/src/config/mod.rs b/proxmox-network-api/src/config/mod.rs
index 09165bfb..3eff9284 100644
--- a/proxmox-network-api/src/config/mod.rs
+++ b/proxmox-network-api/src/config/mod.rs
@@ -89,6 +89,9 @@ fn write_iface_attributes(iface: &Interface, w: &mut dyn Write) -> Result<(), Er
                 }
             }
 
+            let miimon = iface.bond_miimon.unwrap_or(100);
+            writeln!(w, "\tbond-miimon {miimon}")?;
+
             let slaves = iface.slaves.as_ref().unwrap_or(&EMPTY_LIST);
             if slaves.is_empty() {
                 writeln!(w, "\tbond-slaves none")?;
@@ -791,4 +794,71 @@ iface individual_name inet manual
         assert_eq!(parse_vlan_raw_device_from_name("vmbr0"), None);
         assert_eq!(parse_vlan_raw_device_from_name("vmbr0.200"), Some("vmbr0"));
     }
+
+    #[test]
+    fn test_write_network_config_bond_default_miimon() {
+        let mut bond = Interface::new("bond0".to_owned());
+        bond.interface_type = NetworkInterfaceType::Bond;
+        bond.method = Some(NetworkConfigMethod::Manual);
+        bond.slaves = Some(vec![String::from("eth0"), String::from("eth1")]);
+        bond.bond_mode = Some(LinuxBondMode::ActiveBackup);
+
+        let mut eth0 = Interface::new("eth0".to_owned());
+        eth0.interface_type = NetworkInterfaceType::Eth;
+
+        let mut eth1 = Interface::new("eth1".to_owned());
+        eth1.interface_type = NetworkInterfaceType::Eth;
+
+        let nw_config = NetworkConfig {
+            interfaces: BTreeMap::from([
+                ("eth0".into(), eth0),
+                ("eth1".into(), eth1),
+                ("bond0".into(), bond),
+            ]),
+            order: vec![NetworkOrderEntry::Iface("bond0".to_owned())],
+        };
+        assert_eq!(
+            String::try_from(nw_config).unwrap().trim(),
+            r#"
+iface bond0 inet manual
+	bond-mode active-backup
+	bond-miimon 100
+	bond-slaves eth0 eth1"#
+                .trim()
+        );
+    }
+
+    #[test]
+    fn test_write_network_config_bond_explicit_miimon() {
+        let mut bond = Interface::new("bond0".to_owned());
+        bond.interface_type = NetworkInterfaceType::Bond;
+        bond.method = Some(NetworkConfigMethod::Manual);
+        bond.slaves = Some(vec![String::from("eth0"), String::from("eth1")]);
+        bond.bond_mode = Some(LinuxBondMode::ActiveBackup);
+        bond.bond_miimon = Some(250);
+
+        let mut eth0 = Interface::new("eth0".to_owned());
+        eth0.interface_type = NetworkInterfaceType::Eth;
+
+        let mut eth1 = Interface::new("eth1".to_owned());
+        eth1.interface_type = NetworkInterfaceType::Eth;
+
+        let nw_config = NetworkConfig {
+            interfaces: BTreeMap::from([
+                ("eth0".into(), eth0),
+                ("eth1".into(), eth1),
+                ("bond0".into(), bond),
+            ]),
+            order: vec![NetworkOrderEntry::Iface("bond0".to_owned())],
+        };
+        assert_eq!(
+            String::try_from(nw_config).unwrap().trim(),
+            r#"
+iface bond0 inet manual
+	bond-mode active-backup
+	bond-miimon 250
+	bond-slaves eth0 eth1"#
+                .trim()
+        );
+    }
 }
diff --git a/proxmox-network-api/src/config/parser.rs b/proxmox-network-api/src/config/parser.rs
index 71c9ec0f..2b789f64 100644
--- a/proxmox-network-api/src/config/parser.rs
+++ b/proxmox-network-api/src/config/parser.rs
@@ -378,6 +378,12 @@ impl<R: BufRead> NetworkParser<R> {
                     interface.bond_xmit_hash_policy = Some(policy);
                     self.eat(Token::Newline)?;
                 }
+                Token::BondMiimon => {
+                    self.eat(Token::BondMiimon)?;
+                    let miimon = self.next_text()?.parse()?;
+                    interface.bond_miimon = Some(miimon);
+                    self.eat(Token::Newline)?;
+                }
                 Token::VlanId => {
                     self.eat(Token::VlanId)?;
                     let vlan_id = self.next_text()?.parse()?;
@@ -918,4 +924,21 @@ iface individual_name inet static
         assert_eq!(iface.method, Some(NetworkConfigMethod::Static));
         assert_eq!(iface.cidr, Some(String::from("10.0.0.100/16")));
     }
+
+    #[test]
+    fn test_network_config_parser_bond_miimon() {
+        let input = r#"
+iface bond0 inet manual
+	bond-slaves eth0 eth1
+	bond-mode active-backup
+	bond-miimon 200
+"#;
+
+        let mut parser = NetworkParser::new(input.as_bytes());
+        let config = parser.parse_interfaces(None).unwrap();
+
+        let iface = config.interfaces.get("bond0").unwrap();
+        assert_eq!(iface.interface_type, NetworkInterfaceType::Bond);
+        assert_eq!(iface.bond_miimon, Some(200));
+    }
 }
-- 
2.55.0





^ permalink raw reply related	[flat|nested] 4+ messages in thread

* [PATCH proxmox-backup 3/3] api: network: reuse interface create/update methods from network-api
  2026-09-25 12:34 [PATCH proxmox{,-backup} 0/3] fix #6121: add 'bond-miimon' option for network interfaces Christoph Heiss
  2026-09-25 12:34 ` [PATCH proxmox 1/3] fix #6121: network-api: api: add `bond-miimon` option for interfaces Christoph Heiss
  2026-09-25 12:34 ` [PATCH proxmox 2/3] fix #6121: network-api: config: add support for `bond-miimon` option Christoph Heiss
@ 2026-09-25 12:34 ` Christoph Heiss
  2 siblings, 0 replies; 4+ messages in thread
From: Christoph Heiss @ 2026-09-25 12:34 UTC (permalink / raw)
  To: pbs-devel

They are nearly identical in parameters and implementation, only that the
network-api crate uses a struct instead of a long list of single
parameters.

The only real difference are bit more strict checks, namely:
- the 'auto' method is now rejected for IPv4, which is IPv6 only
- enforces that the method is 'static' when explicitly setting CIDR/GW

Signed-off-by: Christoph Heiss <c.heiss@proxmox.com>
---
In combination w/ the earlier patches in the series; also fixes #6121
[0].

[0] https://bugzilla.proxmox.com/show_bug.cgi?id=6121

 src/api2/node/network.rs | 674 ++-------------------------------------
 1 file changed, 18 insertions(+), 656 deletions(-)

diff --git a/src/api2/node/network.rs b/src/api2/node/network.rs
index 07ef4bb64..591b4b6e8 100644
--- a/src/api2/node/network.rs
+++ b/src/api2/node/network.rs
@@ -1,5 +1,4 @@
-use anyhow::{Error, bail};
-use serde::{Deserialize, Serialize};
+use anyhow::Error;
 use serde_json::{Value, to_value};
 
 use proxmox_router::{ApiMethod, Permission, Router, RpcEnvironment};
@@ -9,86 +8,14 @@ use pbs_api_types::{
     Authid, NODE_SCHEMA, PRIV_SYS_AUDIT, PRIV_SYS_MODIFY, PROXMOX_CONFIG_DIGEST_SCHEMA,
 };
 
+use proxmox_config_digest::ConfigDigest;
 use proxmox_network_api::{
-    self as network, BondXmitHashPolicy, CIDR_V4_SCHEMA, CIDR_V6_SCHEMA, IP_V4_SCHEMA,
-    IP_V6_SCHEMA, Interface, LinuxBondMode, NETWORK_INTERFACE_ARRAY_SCHEMA,
-    NETWORK_INTERFACE_LIST_SCHEMA, NETWORK_INTERFACE_NAME_SCHEMA, NetworkConfig,
-    NetworkConfigMethod, NetworkInterfaceType, parse_vlan_id_from_name,
-    parse_vlan_raw_device_from_name,
+    self as network, DeletableInterfaceProperty, Interface, InterfaceUpdater,
+    NETWORK_INTERFACE_NAME_SCHEMA,
 };
 
 use proxmox_rest_server::WorkerTask;
 
-fn split_interface_list(list: &str) -> Result<Vec<String>, Error> {
-    let value = NETWORK_INTERFACE_ARRAY_SCHEMA.parse_property_string(list)?;
-    Ok(value
-        .as_array()
-        .unwrap()
-        .iter()
-        .map(|v| v.as_str().unwrap().to_string())
-        .collect())
-}
-
-fn check_duplicate_gateway_v4(config: &NetworkConfig, iface: &str) -> Result<(), Error> {
-    let current_gateway_v4 = config
-        .interfaces
-        .iter()
-        .find(|(_, interface)| interface.gateway.is_some())
-        .map(|(name, _)| name.to_string());
-
-    if let Some(current_gateway_v4) = current_gateway_v4 {
-        if current_gateway_v4 != iface {
-            bail!(
-                "Default IPv4 gateway already exists on interface '{}'",
-                current_gateway_v4
-            );
-        }
-    }
-    Ok(())
-}
-
-fn check_duplicate_gateway_v6(config: &NetworkConfig, iface: &str) -> Result<(), Error> {
-    let current_gateway_v6 = config
-        .interfaces
-        .iter()
-        .find(|(_, interface)| interface.gateway6.is_some())
-        .map(|(name, _)| name.to_string());
-
-    if let Some(current_gateway_v6) = current_gateway_v6 {
-        if current_gateway_v6 != iface {
-            bail!(
-                "Default IPv6 gateway already exists on interface '{}'",
-                current_gateway_v6
-            );
-        }
-    }
-    Ok(())
-}
-
-fn set_bridge_ports(iface: &mut Interface, ports: Vec<String>) -> Result<(), Error> {
-    if iface.interface_type != NetworkInterfaceType::Bridge {
-        bail!(
-            "interface '{}' is no bridge (type is {:?})",
-            iface.name,
-            iface.interface_type
-        );
-    }
-    iface.bridge_ports = Some(ports);
-    Ok(())
-}
-
-fn set_bond_slaves(iface: &mut Interface, slaves: Vec<String>) -> Result<(), Error> {
-    if iface.interface_type != NetworkInterfaceType::Bond {
-        bail!(
-            "interface '{}' is no bond (type is {:?})",
-            iface.name,
-            iface.interface_type
-        );
-    }
-    iface.slaves = Some(slaves);
-    Ok(())
-}
-
 #[api(
     input: {
         properties: {
@@ -175,89 +102,9 @@ pub fn read_interface(iface: String) -> Result<Value, Error> {
             iface: {
                 schema: NETWORK_INTERFACE_NAME_SCHEMA,
             },
-            "type": {
-                type: NetworkInterfaceType,
-                optional: true,
-            },
-            autostart: {
-                description: "Autostart interface.",
-                type: bool,
-                optional: true,
-            },
-            method: {
-                type: NetworkConfigMethod,
-                optional: true,
-            },
-            method6: {
-                type: NetworkConfigMethod,
-                optional: true,
-            },
-            comments: {
-                description: "Comments (inet, may span multiple lines)",
-                type: String,
-                optional: true,
-            },
-            comments6: {
-                description: "Comments (inet5, may span multiple lines)",
-                type: String,
-                optional: true,
-            },
-            cidr: {
-                schema: CIDR_V4_SCHEMA,
-                optional: true,
-            },
-            cidr6: {
-                schema: CIDR_V6_SCHEMA,
-                optional: true,
-            },
-            gateway: {
-                schema: IP_V4_SCHEMA,
-                optional: true,
-            },
-            gateway6: {
-                schema: IP_V6_SCHEMA,
-                optional: true,
-            },
-            mtu: {
-                description: "Maximum Transmission Unit.",
-                optional: true,
-                minimum: 46,
-                maximum: 65535,
-                default: 1500,
-            },
-            bridge_ports: {
-                schema: NETWORK_INTERFACE_LIST_SCHEMA,
-                optional: true,
-            },
-            bridge_vlan_aware: {
-                description: "Enable bridge vlan support.",
-                type: bool,
-                optional: true,
-            },
-            "vlan-id": {
-                description: "VLAN ID.",
-                type: u16,
-                optional: true,
-            },
-            "vlan-raw-device": {
-                schema: NETWORK_INTERFACE_NAME_SCHEMA,
-                optional: true,
-            },
-            bond_mode: {
-                type: LinuxBondMode,
-                optional: true,
-            },
-            "bond-primary": {
-                schema: NETWORK_INTERFACE_NAME_SCHEMA,
-                optional: true,
-            },
-            bond_xmit_hash_policy: {
-                type: BondXmitHashPolicy,
-                optional: true,
-            },
-            slaves: {
-                schema: NETWORK_INTERFACE_LIST_SCHEMA,
-                optional: true,
+            config: {
+                type: InterfaceUpdater,
+                flatten: true,
             },
         },
     },
@@ -266,210 +113,8 @@ pub fn read_interface(iface: String) -> Result<Value, Error> {
     },
 )]
 /// Create network interface configuration.
-#[allow(clippy::too_many_arguments)]
-pub fn create_interface(
-    iface: String,
-    autostart: Option<bool>,
-    method: Option<NetworkConfigMethod>,
-    method6: Option<NetworkConfigMethod>,
-    comments: Option<String>,
-    comments6: Option<String>,
-    cidr: Option<String>,
-    gateway: Option<String>,
-    cidr6: Option<String>,
-    gateway6: Option<String>,
-    mtu: Option<u64>,
-    bridge_ports: Option<String>,
-    bridge_vlan_aware: Option<bool>,
-    vlan_id: Option<u16>,
-    vlan_raw_device: Option<String>,
-    bond_mode: Option<LinuxBondMode>,
-    bond_primary: Option<String>,
-    bond_xmit_hash_policy: Option<BondXmitHashPolicy>,
-    slaves: Option<String>,
-    param: Value,
-) -> Result<(), Error> {
-    let interface_type = pbs_tools::json::required_string_param(&param, "type")?;
-    let interface_type: NetworkInterfaceType = serde_json::from_value(interface_type.into())?;
-
-    let _lock = network::lock_config()?;
-
-    let (mut config, _digest) = network::config()?;
-
-    if config.interfaces.contains_key(&iface) {
-        bail!("interface '{}' already exists", iface);
-    }
-
-    let mut interface = Interface::new(iface.clone());
-    interface.interface_type = interface_type;
-
-    if let Some(autostart) = autostart {
-        interface.autostart = autostart;
-    }
-    if method.is_some() {
-        interface.method = method;
-    }
-    if method6.is_some() {
-        interface.method6 = method6;
-    }
-    if mtu.is_some() {
-        interface.mtu = mtu;
-    }
-    if comments.is_some() {
-        interface.comments = comments;
-    }
-    if comments6.is_some() {
-        interface.comments6 = comments6;
-    }
-
-    if let Some(cidr) = cidr {
-        let (_, _, is_v6) = network::parse_cidr(&cidr)?;
-        if is_v6 {
-            bail!("invalid address type (expected IPv4, got IPv6)");
-        }
-        interface.cidr = Some(cidr);
-    }
-
-    if let Some(cidr6) = cidr6 {
-        let (_, _, is_v6) = network::parse_cidr(&cidr6)?;
-        if !is_v6 {
-            bail!("invalid address type (expected IPv6, got IPv4)");
-        }
-        interface.cidr6 = Some(cidr6);
-    }
-
-    if let Some(gateway) = gateway {
-        let is_v6 = gateway.contains(':');
-        if is_v6 {
-            bail!("invalid address type (expected IPv4, got IPv6)");
-        }
-        check_duplicate_gateway_v4(&config, &iface)?;
-        interface.gateway = Some(gateway);
-    }
-
-    if let Some(gateway6) = gateway6 {
-        let is_v6 = gateway6.contains(':');
-        if !is_v6 {
-            bail!("invalid address type (expected IPv6, got IPv4)");
-        }
-        check_duplicate_gateway_v6(&config, &iface)?;
-        interface.gateway6 = Some(gateway6);
-    }
-
-    match interface_type {
-        NetworkInterfaceType::Bridge => {
-            if let Some(ports) = bridge_ports {
-                let ports = split_interface_list(&ports)?;
-                set_bridge_ports(&mut interface, ports)?;
-            }
-            if bridge_vlan_aware.is_some() {
-                interface.bridge_vlan_aware = bridge_vlan_aware;
-            }
-        }
-        NetworkInterfaceType::Bond => {
-            if let Some(mode) = bond_mode {
-                interface.bond_mode = bond_mode;
-                if bond_primary.is_some() {
-                    if mode != LinuxBondMode::ActiveBackup {
-                        bail!("bond-primary is only valid with Active/Backup mode");
-                    }
-                    interface.bond_primary = bond_primary;
-                }
-                if bond_xmit_hash_policy.is_some() {
-                    if mode != LinuxBondMode::Ieee802_3ad && mode != LinuxBondMode::BalanceXor {
-                        bail!(
-                            "bond_xmit_hash_policy is only valid with LACP(802.3ad) or balance-xor mode"
-                        );
-                    }
-                    interface.bond_xmit_hash_policy = bond_xmit_hash_policy;
-                }
-            }
-            if let Some(slaves) = slaves {
-                let slaves = split_interface_list(&slaves)?;
-                set_bond_slaves(&mut interface, slaves)?;
-            }
-        }
-        NetworkInterfaceType::Vlan => {
-            if vlan_id.is_none() && parse_vlan_id_from_name(&iface).is_none() {
-                bail!("vlan-id must be set");
-            }
-            interface.vlan_id = vlan_id;
-
-            if let Some(dev) = vlan_raw_device
-                .as_deref()
-                .or_else(|| parse_vlan_raw_device_from_name(&iface))
-            {
-                if !config.interfaces.contains_key(dev) {
-                    bail!("vlan-raw-device {dev} does not exist");
-                }
-            } else {
-                bail!("vlan-raw-device must be set");
-            }
-            interface.vlan_raw_device = vlan_raw_device;
-        }
-        _ => bail!(
-            "creating network interface type '{:?}' is not supported",
-            interface_type
-        ),
-    }
-
-    if interface.cidr.is_some() || interface.gateway.is_some() {
-        interface.method = Some(NetworkConfigMethod::Static);
-    } else if interface.method.is_none() {
-        interface.method = Some(NetworkConfigMethod::Manual);
-    }
-
-    if interface.cidr6.is_some() || interface.gateway6.is_some() {
-        interface.method6 = Some(NetworkConfigMethod::Static);
-    } else if interface.method6.is_none() {
-        interface.method6 = Some(NetworkConfigMethod::Manual);
-    }
-
-    config.interfaces.insert(iface, interface);
-
-    network::save_config(&config)?;
-
-    Ok(())
-}
-
-#[api()]
-#[derive(Serialize, Deserialize)]
-#[serde(rename_all = "kebab-case")]
-/// Deletable property name
-pub enum DeletableProperty {
-    /// Delete the IPv4 address property.
-    Cidr,
-    /// Delete the IPv6 address property.
-    Cidr6,
-    /// Delete the IPv4 gateway property.
-    Gateway,
-    /// Delete the IPv6 gateway property.
-    Gateway6,
-    /// Delete the whole IPv4 configuration entry.
-    Method,
-    /// Delete the whole IPv6 configuration entry.
-    Method6,
-    /// Delete IPv4 comments
-    Comments,
-    /// Delete IPv6 comments
-    Comments6,
-    /// Delete mtu.
-    Mtu,
-    /// Delete autostart flag
-    Autostart,
-    /// Delete bridge ports (set to 'none')
-    #[serde(rename = "bridge_ports")]
-    BridgePorts,
-    /// Delete bridge-vlan-aware flag
-    #[serde(rename = "bridge_vlan_aware")]
-    BridgeVlanAware,
-    /// Delete bond-slaves (set to 'none')
-    Slaves,
-    /// Delete bond-primary
-    BondPrimary,
-    /// Delete bond transmit hash policy
-    #[serde(rename = "bond_xmit_hash_policy")]
-    BondXmitHashPolicy,
+pub fn create_interface(iface: String, config: InterfaceUpdater) -> Result<(), Error> {
+    proxmox_network_api::create_interface(iface, config)
 }
 
 #[api(
@@ -482,101 +127,21 @@ pub enum DeletableProperty {
             iface: {
                 schema: NETWORK_INTERFACE_NAME_SCHEMA,
             },
-            "type": {
-                type: NetworkInterfaceType,
-                optional: true,
-            },
-            autostart: {
-                description: "Autostart interface.",
-                type: bool,
-                optional: true,
-            },
-            method: {
-                type: NetworkConfigMethod,
-                optional: true,
-            },
-            method6: {
-                type: NetworkConfigMethod,
-                optional: true,
-            },
-            comments: {
-                description: "Comments (inet, may span multiple lines)",
-                type: String,
-                optional: true,
-            },
-            comments6: {
-                description: "Comments (inet5, may span multiple lines)",
-                type: String,
-                optional: true,
-            },
-            cidr: {
-                schema: CIDR_V4_SCHEMA,
-                optional: true,
-            },
-            cidr6: {
-                schema: CIDR_V6_SCHEMA,
-                optional: true,
-            },
-            gateway: {
-                schema: IP_V4_SCHEMA,
-                optional: true,
-            },
-            gateway6: {
-                schema: IP_V6_SCHEMA,
-                optional: true,
-            },
-            mtu: {
-                description: "Maximum Transmission Unit.",
-                optional: true,
-                minimum: 46,
-                maximum: 65535,
-                default: 1500,
-            },
-            bridge_ports: {
-                schema: NETWORK_INTERFACE_LIST_SCHEMA,
-                optional: true,
-            },
-            bridge_vlan_aware: {
-                description: "Enable bridge vlan support.",
-                type: bool,
-                optional: true,
-            },
-            "vlan-id": {
-                description: "VLAN ID.",
-                type: u16,
-                optional: true,
-            },
-            "vlan-raw-device": {
-                schema: NETWORK_INTERFACE_NAME_SCHEMA,
-                optional: true,
-            },
-            bond_mode: {
-                type: LinuxBondMode,
-                optional: true,
-            },
-            "bond-primary": {
-                schema: NETWORK_INTERFACE_NAME_SCHEMA,
-                optional: true,
-            },
-            bond_xmit_hash_policy: {
-                type: BondXmitHashPolicy,
-                optional: true,
-            },
-            slaves: {
-                schema: NETWORK_INTERFACE_LIST_SCHEMA,
-                optional: true,
+            update: {
+                type: InterfaceUpdater,
+                flatten: true,
             },
             delete: {
                 description: "List of properties to delete.",
                 type: Array,
                 optional: true,
                 items: {
-                    type: DeletableProperty,
+                    type: DeletableInterfaceProperty,
                 }
             },
             digest: {
+                type: ConfigDigest,
                 optional: true,
-                schema: PROXMOX_CONFIG_DIGEST_SCHEMA,
             },
         },
     },
@@ -585,216 +150,13 @@ pub enum DeletableProperty {
     },
 )]
 /// Update network interface config.
-#[allow(clippy::too_many_arguments)]
 pub fn update_interface(
     iface: String,
-    autostart: Option<bool>,
-    method: Option<NetworkConfigMethod>,
-    method6: Option<NetworkConfigMethod>,
-    comments: Option<String>,
-    comments6: Option<String>,
-    cidr: Option<String>,
-    gateway: Option<String>,
-    cidr6: Option<String>,
-    gateway6: Option<String>,
-    mtu: Option<u64>,
-    bridge_ports: Option<String>,
-    bridge_vlan_aware: Option<bool>,
-    vlan_id: Option<u16>,
-    vlan_raw_device: Option<String>,
-    bond_mode: Option<LinuxBondMode>,
-    bond_primary: Option<String>,
-    bond_xmit_hash_policy: Option<BondXmitHashPolicy>,
-    slaves: Option<String>,
-    delete: Option<Vec<DeletableProperty>>,
-    digest: Option<String>,
-    param: Value,
+    update: InterfaceUpdater,
+    delete: Option<Vec<DeletableInterfaceProperty>>,
+    digest: Option<ConfigDigest>,
 ) -> Result<(), Error> {
-    let _lock = network::lock_config()?;
-
-    let (mut config, expected_digest) = network::config()?;
-
-    pbs_config::detect_modified_configuration_file(digest, &expected_digest)?;
-
-    if gateway.is_some() {
-        check_duplicate_gateway_v4(&config, &iface)?;
-    }
-    if gateway6.is_some() {
-        check_duplicate_gateway_v6(&config, &iface)?;
-    }
-
-    if let Some(dev) = vlan_raw_device
-        .as_deref()
-        .or_else(|| parse_vlan_raw_device_from_name(&iface))
-    {
-        if !config.interfaces.contains_key(dev) {
-            bail!("vlan-raw-device {dev} does not exist");
-        }
-    }
-
-    let interface = config.lookup_mut(&iface)?;
-
-    if let Some(interface_type) = param.get("type") {
-        let interface_type = NetworkInterfaceType::deserialize(interface_type)?;
-        if interface_type != interface.interface_type {
-            bail!(
-                "got unexpected interface type ({:?} != {:?})",
-                interface_type,
-                interface.interface_type
-            );
-        }
-    }
-
-    if let Some(delete) = delete {
-        for delete_prop in delete {
-            match delete_prop {
-                DeletableProperty::Cidr => {
-                    interface.cidr = None;
-                }
-                DeletableProperty::Cidr6 => {
-                    interface.cidr6 = None;
-                }
-                DeletableProperty::Gateway => {
-                    interface.gateway = None;
-                }
-                DeletableProperty::Gateway6 => {
-                    interface.gateway6 = None;
-                }
-                DeletableProperty::Method => {
-                    interface.method = None;
-                }
-                DeletableProperty::Method6 => {
-                    interface.method6 = None;
-                }
-                DeletableProperty::Comments => {
-                    interface.comments = None;
-                }
-                DeletableProperty::Comments6 => {
-                    interface.comments6 = None;
-                }
-                DeletableProperty::Mtu => {
-                    interface.mtu = None;
-                }
-                DeletableProperty::Autostart => {
-                    interface.autostart = false;
-                }
-                DeletableProperty::BridgePorts => {
-                    set_bridge_ports(interface, Vec::new())?;
-                }
-                DeletableProperty::BridgeVlanAware => {
-                    interface.bridge_vlan_aware = None;
-                }
-                DeletableProperty::Slaves => {
-                    set_bond_slaves(interface, Vec::new())?;
-                }
-                DeletableProperty::BondPrimary => {
-                    interface.bond_primary = None;
-                }
-                DeletableProperty::BondXmitHashPolicy => interface.bond_xmit_hash_policy = None,
-            }
-        }
-    }
-
-    if let Some(autostart) = autostart {
-        interface.autostart = autostart;
-    }
-    if method.is_some() {
-        interface.method = method;
-    }
-    if method6.is_some() {
-        interface.method6 = method6;
-    }
-    if mtu.is_some() {
-        interface.mtu = mtu;
-    }
-    if let Some(ports) = bridge_ports {
-        let ports = split_interface_list(&ports)?;
-        set_bridge_ports(interface, ports)?;
-    }
-    if bridge_vlan_aware.is_some() {
-        interface.bridge_vlan_aware = bridge_vlan_aware;
-    }
-    if let Some(slaves) = slaves {
-        let slaves = split_interface_list(&slaves)?;
-        set_bond_slaves(interface, slaves)?;
-    }
-    if let Some(mode) = bond_mode {
-        interface.bond_mode = bond_mode;
-        if bond_primary.is_some() {
-            if mode != LinuxBondMode::ActiveBackup {
-                bail!("bond-primary is only valid with Active/Backup mode");
-            }
-            interface.bond_primary = bond_primary;
-        }
-        if bond_xmit_hash_policy.is_some() {
-            if mode != LinuxBondMode::Ieee802_3ad && mode != LinuxBondMode::BalanceXor {
-                bail!("bond_xmit_hash_policy is only valid with LACP(802.3ad) or balance-xor mode");
-            }
-            interface.bond_xmit_hash_policy = bond_xmit_hash_policy;
-        }
-    }
-
-    if let Some(cidr) = cidr {
-        let (_, _, is_v6) = network::parse_cidr(&cidr)?;
-        if is_v6 {
-            bail!("invalid address type (expected IPv4, got IPv6)");
-        }
-        interface.cidr = Some(cidr);
-    }
-
-    if let Some(cidr6) = cidr6 {
-        let (_, _, is_v6) = network::parse_cidr(&cidr6)?;
-        if !is_v6 {
-            bail!("invalid address type (expected IPv6, got IPv4)");
-        }
-        interface.cidr6 = Some(cidr6);
-    }
-
-    if let Some(gateway) = gateway {
-        let is_v6 = gateway.contains(':');
-        if is_v6 {
-            bail!("invalid address type (expected IPv4, got IPv6)");
-        }
-        interface.gateway = Some(gateway);
-    }
-
-    if let Some(gateway6) = gateway6 {
-        let is_v6 = gateway6.contains(':');
-        if !is_v6 {
-            bail!("invalid address type (expected IPv6, got IPv4)");
-        }
-        interface.gateway6 = Some(gateway6);
-    }
-
-    if comments.is_some() {
-        interface.comments = comments;
-    }
-    if comments6.is_some() {
-        interface.comments6 = comments6;
-    }
-
-    if interface.cidr.is_some() || interface.gateway.is_some() {
-        interface.method = Some(NetworkConfigMethod::Static);
-    } else {
-        interface.method = Some(NetworkConfigMethod::Manual);
-    }
-
-    if interface.cidr6.is_some() || interface.gateway6.is_some() {
-        interface.method6 = Some(NetworkConfigMethod::Static);
-    } else if interface.method6.is_none() {
-        interface.method6 = Some(NetworkConfigMethod::Manual);
-    }
-
-    if vlan_id.is_some() {
-        interface.vlan_id = vlan_id;
-    }
-    if vlan_raw_device.is_some() {
-        interface.vlan_raw_device = vlan_raw_device;
-    }
-
-    network::save_config(&config)?;
-
-    Ok(())
+    proxmox_network_api::update_interface(iface, update, delete, digest)
 }
 
 #[api(
-- 
2.55.0





^ permalink raw reply related	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2026-09-25 12:35 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-25 12:34 [PATCH proxmox{,-backup} 0/3] fix #6121: add 'bond-miimon' option for network interfaces Christoph Heiss
2026-09-25 12:34 ` [PATCH proxmox 1/3] fix #6121: network-api: api: add `bond-miimon` option for interfaces Christoph Heiss
2026-09-25 12:34 ` [PATCH proxmox 2/3] fix #6121: network-api: config: add support for `bond-miimon` option Christoph Heiss
2026-09-25 12:34 ` [PATCH proxmox-backup 3/3] api: network: reuse interface create/update methods from network-api Christoph Heiss

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal