all lists on lists.proxmox.com
 help / color / mirror / Atom feed
From: Christoph Heiss <c.heiss@proxmox.com>
To: pbs-devel@lists.proxmox.com
Subject: [PATCH proxmox 2/3] fix #6121: network-api: config: add support for `bond-miimon` option
Date: Fri, 25 Sep 2026 14:34:31 +0200	[thread overview]
Message-ID: <20260925123459.424903-3-c.heiss@proxmox.com> (raw)
In-Reply-To: <20260925123459.424903-1-c.heiss@proxmox.com>

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





  parent reply	other threads:[~2026-09-25 12:35 UTC|newest]

Thread overview: 4+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
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 [this message]
2026-09-25 12:34 ` [PATCH proxmox-backup 3/3] api: network: reuse interface create/update methods from network-api Christoph Heiss

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=20260925123459.424903-3-c.heiss@proxmox.com \
    --to=c.heiss@proxmox.com \
    --cc=pbs-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 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