diff options
-rwxr-xr-x | scripts/vyatta-bonding.pl | 123 |
1 files changed, 106 insertions, 17 deletions
diff --git a/scripts/vyatta-bonding.pl b/scripts/vyatta-bonding.pl index a0bdbd6e..66417f9f 100755 --- a/scripts/vyatta-bonding.pl +++ b/scripts/vyatta-bonding.pl @@ -46,45 +46,134 @@ my %modes = ( ); sub set_mode { - my ($intf, $mode) = @_; + my ( $intf, $mode ) = @_; my $val = $modes{$mode}; die "Unknown bonding mode $mode\n" unless defined($val); open my $fm, '>', "/sys/class/net/$intf/bonding/mode" - or die "Error: $intf is not a bonding device:$!\n"; + or die "Error: $intf is not a bonding device:$!\n"; print {$fm} $val, "\n"; close $fm - or die "Error: $intf can not set mode $val:$!\n"; + or die "Error: $intf can not set mode $val:$!\n"; } +sub get_slaves { + my $intf = shift; + + open my $f, '<', "/sys/class/net/$intf/bonding/slaves" + or die "$intf is not a bonding interface"; + my $slaves = <$f>; + close $f; + return unless $slaves; + + chomp $slaves; + return split( ' ', $slaves ); +} + +sub add_slave { + my ( $intf, $slave ) = @_; + + open my $f, '>', "/sys/class/net/$intf/bonding/slaves" + or die "$intf is not a bonding interface"; + + print {$f} "+$slave\n"; + close $f; +} + +sub remove_slave { + my ( $intf, $slave ) = @_; + + open my $f, '>', "/sys/class/net/$intf/bonding/slaves" + or die "$intf is not a bonding interface"; + + print {$f} "-$slave\n"; + close $f; +} + +# Go dumpster diving to figure out which ethernet interface (if any) +# gave it's address to be used by all bonding devices. +sub primary_slave { + my ( $intf, $bond_addr ) = @_; + + open my $p, '<', "/proc/net/bonding/$intf" + or die "Can't open /proc/net/bonding/$intf : $!"; + + my ( $dev, $match ); + while ( my $line = <$p> ) { + chomp $line; + if ( $line =~ m/^Slave Interface: (.*)$/ ) { + $dev = $1; + } + elsif ( $line =~ m/^Permanent HW addr: (.*)$/ ) { + if ( $1 eq $bond_addr ) { + $match = $dev; + last; + } + } + } + close $p; + + return $match; +} + +sub if_down { + my $intf = shift; + system "ip link set dev $intf down" + and die "Could not set $intf up ($!)\n"; +} + +sub if_up { + my $intf = shift; + system "ip link set dev $intf up" + and die "Could not set $intf up ($!)\n"; +} sub change_mode { - my ($intf, $mode) = @_; + my ( $intf, $mode ) = @_; my $interface = new Vyatta::Interface($intf); - die "$intf is not a valid interface" unless $interface; - if ($interface->up()) { - system "sudo ip link set $intf down" - and die "Could not set $intf down ($!)\n"; + my $primary = primary_slave( $intf, $interface->hw_address() ); + + my @slaves = get_slaves($intf); + foreach my $slave (@slaves) { + if_down($slave); + remove_slave( $intf, $slave ) unless ( $primary && $slave eq $primary ); + } + if ($primary) { + if_down($primary); + remove_slave( $intf, $primary ); + } - set_mode($intf, $mode); + my $bond_up = $interface->up(); + if_down($intf) if $bond_up; + set_mode( $intf, $mode ); + if_up($intf) if $bond_up; - system "sudo ip link set $intf up" - and die "Could not set $intf up ($!)\n"; - } else { - set_mode($intf, $mode); + foreach my $slave ( @slaves ) { + add_slave( $intf, $slave ); } } sub usage { - print "Usage: $0 --set-mode=s{2}\n"; + print "Usage: $0 --dev=bondX --mode={mode}\n"; + print " $0 --dev=bondX --add-port=ethX\n"; + print " $0 --dev=bondX --remove-port=ethX\n"; + print print "modes := ", join( ',', sort( keys %modes ) ), "\n"; + exit 1; } -my @mode_change; +my ( $dev, $mode, $add_port, $rem_port ); GetOptions( - 'set-mode=s{2}' => \@mode_change, + 'dev=s' => \$dev, + 'mode=s' => \$mode, + 'add=s' => \$add_port, + 'remove=s' => \$rem_port, ) or usage(); -change_mode( @mode_change ) if @mode_change; +die "$0: device not specified\n" unless $dev; + +change_mode( $dev, $mode ) if $mode; +add_slave( $dev, $add_port ) if $add_port; +remove_slave( $dev, $rem_port ) if $rem_port; |