Fix live activation of Linux bond interfaces

ⓘ Create and configure missing bond devices with ip link, attach partner interfaces before assigning addresses, avoid legacy module auto-creation when ip is available, and add regression coverage.

Ref.: https://github.com/webmin/webmin/pull/2777
This commit is contained in:
Ilia Ross
2026-06-27 18:16:38 +02:00
parent 6135c01d57
commit 81d44f8491
3 changed files with 95 additions and 3 deletions

View File

@@ -258,7 +258,44 @@ if (&has_command("ip") && $a->{'virtual'} ne '' && !$a->{'up'}) {
return;
}
if (!&has_command("ifconfig") && &has_command("ip")) {
if (&has_command("ip") && $a->{'bond'} && $a->{'up'} && !$old) {
# Create the bond before assigning addresses to it.
my $bcmd = "ip link add ".quotemeta($a->{'name'})." type bond";
if (defined($a->{'mode'}) && $a->{'mode'} ne '') {
$bcmd .= " mode ".quotemeta(&bond_mode_name($a->{'mode'}));
}
if ($a->{'miimon'}) {
$bcmd .= " miimon ".quotemeta($a->{'miimon'});
}
if ($a->{'updelay'}) {
$bcmd .= " updelay ".quotemeta($a->{'updelay'});
}
if ($a->{'downdelay'}) {
$bcmd .= " downdelay ".quotemeta($a->{'downdelay'});
}
my $out = &backquote_logged("$bcmd 2>&1");
&error("Failed to create bond device : $out") if ($?);
foreach my $slave (grep { $_ ne '' } split(/\s+/, $a->{'partner'})) {
$bcmd = "ip link set dev ".quotemeta($slave)." down";
$out = &backquote_logged("$bcmd 2>&1");
&error("Failed to bring down bond slave : $out") if ($?);
$bcmd = "ip link set dev ".quotemeta($slave)." master ".
quotemeta($a->{'name'});
$out = &backquote_logged("$bcmd 2>&1");
&error("Failed to add bond slave : $out") if ($?);
$bcmd = "ip link set dev ".quotemeta($slave)." up";
$out = &backquote_logged("$bcmd 2>&1");
&error("Failed to bring up bond slave : $out") if ($?);
}
if ($a->{'primary'}) {
$bcmd = "ip link set dev ".quotemeta($a->{'name'}).
" type bond primary ".quotemeta($a->{'primary'});
$out = &backquote_logged("$bcmd 2>&1");
&error("Failed to set bond primary interface : $out") if ($?);
}
}
if (($a->{'bond'} || !&has_command("ifconfig")) && &has_command("ip")) {
# For a real interface, activate or de-activate the link
if ($a->{'virtual'} eq '' && $a->{'up'} && (!$old || !$old->{'up'})) {
# Bring up
@@ -646,6 +683,19 @@ local $out = &backquote_logged("$cmd 2>&1");
&error($out) if ($?);
}
# bond_mode_name(mode)
# Convert Webmin's numeric bonding mode to the name expected by ip(8).
sub bond_mode_name
{
my ($mode) = @_;
my @modes = ("balance-rr", "active-backup", "balance-xor", "broadcast",
"802.3ad", "balance-tlb", "balance-alb");
if ($mode =~ /^\d+$/ && defined($modes[$mode])) {
return $modes[$mode];
}
return $mode eq "activebackup" ? "active-backup" : $mode;
}
# Tries to unload the module
# unload_module(name)
sub unload_module

View File

@@ -377,7 +377,7 @@ else {
$err && &error("<pre>$err</pre>");
}
else {
if ($in{'bond'}) {
if ($in{'bond'} && !&has_command("ip")) {
if (($gconfig{'os_type'} eq 'debian-linux') &&
($gconfig{'os_version'} >= 5)) {}
else {&load_module($b);}
@@ -389,4 +389,3 @@ else {
"bifc", $b->{'fullname'}, $b);
}
&redirect("list_ifcs.cgi?mode=boot");

View File

@@ -617,4 +617,47 @@ is_deeply(\@commands,
[ "ip addr del 10\\.211\\.55\\.25\\/24 dev enp0s5 2>&1" ],
"Linux active virtual interface is removed when saved down");
@commands = ( );
{
no warnings 'redefine';
no warnings 'once';
local *main::has_command = sub {
return $_[0] eq "ip" ? "/sbin/ip" :
$_[0] eq "ifconfig" ? "/sbin/ifconfig" : undef;
};
local *main::active_interfaces = sub {
return ( );
};
local *main::backquote_command = sub {
return "";
};
main::activate_interface({ 'name' => 'bond0',
'fullname' => 'bond0',
'virtual' => '',
'bond' => 1,
'partner' => 'eth0 eth1',
'mode' => '1',
'primary' => 'eth0',
'miimon' => '100',
'updelay' => '200',
'downdelay' => '200',
'address' => '10.0.0.2',
'netmask' => '255.255.255.0',
'address6' => [ ],
'netmask6' => [ ],
'up' => 1 });
}
is_deeply(\@commands,
[ "ip link add bond0 type bond mode active\\-backup miimon 100 updelay 200 downdelay 200 2>&1",
"ip link set dev eth0 down 2>&1",
"ip link set dev eth0 master bond0 2>&1",
"ip link set dev eth0 up 2>&1",
"ip link set dev eth1 down 2>&1",
"ip link set dev eth1 master bond0 2>&1",
"ip link set dev eth1 up 2>&1",
"ip link set dev bond0 type bond primary eth0 2>&1",
"ip link set dev bond0 up 2>&1",
"cd / ; ip addr add 10\\.0\\.0\\.2/24 dev bond0 2>&1" ],
"Linux active bond interface is created before assigning an address");
done_testing();