From 395338fe0692b4ecf8547ab5cf18dd147e07902d Mon Sep 17 00:00:00 2001 From: Christian Breunig Date: Wed, 9 Sep 2026 18:42:24 +0000 Subject: bond: T9269: keep the bond MAC when appending a member interface Commit 6ebb8cd26 ("bond: T7571: fix inconsistent MAC address behaviour") re-assigns the first member's MAC address to the bond, as newer Kernels no longer do so themselves. The flag driving it was initialized unconditionally and an already enslaved member hits "continue" without clearing it, so the step ran again on every append and handed the bond the newly added member's address: bond0 aa:c1:ab:c3:62:48 eth1 aa:c1:ab:98:48:0f eth2 aa:c1:ab:c3:62:48 Only adopt a member MAC while the bond has no member at all, which is what T7571 intended. The added smoketest covers this together with T9269 in leaf_node_changed: it commits one member, appends a second, and verifies both stay enslaved, the pre-existing member stays admin up, and the bond MAC does not move. --- smoketest/scripts/cli/test_interfaces_bonding.py | 36 ++++++++++++++++++++++++ 1 file changed, 36 insertions(+) (limited to 'smoketest/scripts/cli') diff --git a/smoketest/scripts/cli/test_interfaces_bonding.py b/smoketest/scripts/cli/test_interfaces_bonding.py index ed4e5d911..3a3b1e5cf 100755 --- a/smoketest/scripts/cli/test_interfaces_bonding.py +++ b/smoketest/scripts/cli/test_interfaces_bonding.py @@ -134,6 +134,42 @@ class BondingInterfaceTest(BasicInterfaceTest.TestCase): state = Interface(remove_member).get_admin_state() self.assertEqual('up', state) + def test_bonding_append_member(self): + # T9269: appending a member to a bond that already has one must not + # disturb the existing member. leaf_node_changed() used to report the + # already configured member as removed, because a multi node holding a + # single value renders as a plain string and only becomes a list once + # it holds several values - so the bond released it again right after + # adding the new one. The bond MAC must not move to the new member + # either, that only happens while the bond has no member at all. + if len(self._members) < 2: + self.skipTest('not enough member interfaces available') + + first, second = self._members[0], self._members[1] + + for interface in self._interfaces: + self.cli_set(self._base_path + [interface, 'member', 'interface', first]) + self.cli_commit() + + bond_macs = {} + for interface in self._interfaces: + slaves = read_file(f'/sys/class/net/{interface}/bonding/slaves').split() + self.assertEqual(slaves, [first]) + bond_macs[interface] = Interface(interface).get_mac() + + for interface in self._interfaces: + self.cli_set(self._base_path + [interface, 'member', 'interface', second]) + self.cli_commit() + + for interface in self._interfaces: + slaves = read_file(f'/sys/class/net/{interface}/bonding/slaves').split() + # the pre-existing member must still be enslaved and up + self.assertIn(first, slaves) + self.assertIn(second, slaves) + self.assertEqual(Interface(first).get_admin_state(), 'up') + # appending a member must not change the bond's own MAC address + self.assertEqual(Interface(interface).get_mac(), bond_macs[interface]) + def test_bonding_min_links(self): # configure member interfaces min_links = len(self._interfaces) -- cgit v1.2.3