912 lines
19 KiB
Perl
Executable File
912 lines
19 KiB
Perl
Executable File
#!/usr/bin/perl
|
|
|
|
use strict;
|
|
use warnings;
|
|
|
|
use IPC::Open3;
|
|
use IO::Select;
|
|
use Fcntl qw( LOCK_EX LOCK_NB );
|
|
use Data::Dumper;
|
|
|
|
open( SELF, "<$0" ) || error( int( $! ), "open(): $0: $!" );
|
|
flock( SELF, LOCK_EX|LOCK_NB ) || error( -1, "Another instance is running" );
|
|
|
|
use constant CONFDIR => '/etc';
|
|
use constant CONFIGFILE => CONFDIR . '/network.conf';
|
|
use constant NOACT => eval {
|
|
for ( my $i = 0; $i <= $#ARGV; $i++ ) {
|
|
if ( $ARGV[$i] eq '-n' || $ARGV[$i] eq '--no-act' ) {
|
|
splice( @ARGV, $i, 1 );
|
|
|
|
return( 1 );
|
|
}
|
|
}
|
|
|
|
return( 0 );
|
|
};
|
|
use constant VERBOSE => eval {
|
|
for ( my $i = 0; $i <= $#ARGV; $i++ ) {
|
|
if ( $ARGV[$i] eq '-v' || $ARGV[$i] eq '--verbose' ) {
|
|
splice( @ARGV, $i, 1 );
|
|
|
|
return( 1 );
|
|
}
|
|
}
|
|
|
|
return( 0 );
|
|
};
|
|
use constant VERSION => '0.1';
|
|
|
|
sub aliasdown {
|
|
my $alias = shift;
|
|
my $runningconf = shift;
|
|
my $aliases = $runningconf->{$alias->{'dev'}}{'aliases'};
|
|
my $result;
|
|
|
|
if ( my $running = $aliases->{$alias->{'name'}} ) {
|
|
if ( VERBOSE ) {
|
|
print "Deleting $running->{'address'}"
|
|
. " from $alias->{'dev'}"
|
|
. " (label $alias->{'dev'}:"
|
|
. "$alias->{'name'})\n";
|
|
}
|
|
|
|
$result = runcmd( "ip address delete"
|
|
. " $running->{'address'}"
|
|
. " dev $running->{'dev'} label"
|
|
. " $running->{'dev'}:$alias->{'name'}" );
|
|
|
|
if ( VERBOSE ) {
|
|
if ( $result->{'stdout'} ) {
|
|
print $result->{'stdout'};
|
|
}
|
|
if ( $result->{'stderr'} ) {
|
|
print STDERR $result->{'stderr'};
|
|
}
|
|
}
|
|
|
|
return( $result->{'stat'} );
|
|
}
|
|
}
|
|
|
|
sub aliasup {
|
|
my $alias = shift;
|
|
my $runningconf = shift;
|
|
my $dev = $alias->{'dev'};
|
|
my $name = $alias->{'name'};
|
|
my $result;
|
|
|
|
if ( my $old = $runningconf->{$dev}{'aliases'}{$name} ) {
|
|
if ( $old->{'address'} eq $alias->{'address'} &&
|
|
$old->{'dev'} eq $alias->{'dev'} &&
|
|
$old->{'name'} eq $alias->{'name'} ) {
|
|
if ( VERBOSE ) {
|
|
print STDERR "Interface $dev:$name"
|
|
. " is already up\n";
|
|
}
|
|
|
|
return( 1 );
|
|
}
|
|
|
|
if ( my $retval = aliasdown( $old, $runningconf ) ) {
|
|
return( $retval );
|
|
}
|
|
}
|
|
|
|
if ( VERBOSE ) {
|
|
print "Adding $alias->{'address'} to $alias->{'dev'}"
|
|
. " (label $alias->{'dev'}"
|
|
. ":$alias->{'name'})\n";
|
|
}
|
|
|
|
$result = runcmd( "ip address add $alias->{'address'}"
|
|
. " dev $alias->{'dev'}"
|
|
. " label $alias->{'dev'}:$alias->{'name'}" );
|
|
|
|
if ( VERBOSE ) {
|
|
if ( $result->{'stdout'} ) {
|
|
print $result->{'stdout'};
|
|
}
|
|
if ( $result->{'stderr'} ) {
|
|
print STDERR $result->{'stderr'};
|
|
}
|
|
}
|
|
|
|
return( $result->{'stat'} );
|
|
}
|
|
|
|
sub cidr2netmask {
|
|
my $cidr = shift;
|
|
my $bit = ( 2 ** ( 32 - $cidr ) ) - 1;
|
|
my $full_mask = unpack( 'N', pack( 'C4', 255,255,255,255 ) );
|
|
my $netmask = join( '.', unpack( 'C4', pack( 'N',
|
|
( $full_mask ^ $bit ) ) ) );
|
|
|
|
return( $netmask );
|
|
}
|
|
|
|
sub error {
|
|
my $status = shift;
|
|
my $errstr = shift;
|
|
my $level = 1;
|
|
my @stack = ();
|
|
|
|
chomp( $errstr );
|
|
|
|
print "\n";
|
|
|
|
print STDERR ( caller() )[1] .":\n $errstr at line "
|
|
. ( caller() )[2] . "\n";
|
|
|
|
if ( VERBOSE ) {
|
|
if ( caller( $level ) ) {
|
|
print "\n=== Stack Trace ===\n";
|
|
}
|
|
|
|
while ( my @trace = caller( $level++ ) ) {
|
|
print STDERR " $trace[1]:\n"
|
|
. " $trace[3]() called at line $trace[2]\n";
|
|
}
|
|
}
|
|
|
|
print "\n";
|
|
|
|
if ( $status ) {
|
|
exit( $status );
|
|
}
|
|
}
|
|
|
|
sub getroutes {
|
|
my $routes = {};
|
|
my $result = runcmd( 'ip route show' );
|
|
|
|
if ( $result->{'stat'} || ! $result->{'stdout'} ) {
|
|
return;
|
|
}
|
|
|
|
open( my $fh, '<', \$result->{'stdout'} );
|
|
|
|
while ( <$fh> ) {
|
|
if ( $_ =~ /^(\S+)\s+via\s+(\S+)\s+dev\s+(\S+)/ ) {
|
|
$routes->{$1}{'via'} = $2;
|
|
$routes->{$1}{'dev'} = $3;
|
|
}
|
|
}
|
|
|
|
close( $fh );
|
|
|
|
return( $routes );
|
|
}
|
|
|
|
sub ifdown {
|
|
my $iface = shift;
|
|
my $result = {};
|
|
my $stat = 0;
|
|
|
|
if ( VERBOSE ) {
|
|
print "Flushing interface $iface->{'name'}\n";
|
|
}
|
|
|
|
$result = runcmd( "ip addr flush dev $iface->{'name'}" );
|
|
|
|
if ( $result->{'stat'} ) {
|
|
$stat = $result->{'stat'};
|
|
}
|
|
|
|
if ( VERBOSE ) {
|
|
if ( $result->{'stdout'} ) {
|
|
print $result->{'stdout'};
|
|
}
|
|
if ( $result->{'stderr'} ) {
|
|
print STDERR $result->{'stderr'};
|
|
}
|
|
|
|
print "Bringing down interface $iface->{'name'}\n";
|
|
}
|
|
|
|
$result = runcmd( "ip link set $iface->{'name'} down" );
|
|
|
|
if ( $result->{'stat'} ) {
|
|
$stat = $result->{'stat'};
|
|
}
|
|
|
|
if ( VERBOSE ) {
|
|
if ( $result->{'stdout'} ) {
|
|
print $result->{'stdout'};
|
|
}
|
|
if ( $result->{'stderr'} ) {
|
|
print STDERR $result->{'stderr'};
|
|
}
|
|
}
|
|
|
|
return( $stat );
|
|
}
|
|
|
|
sub ifup {
|
|
my $iface = shift;
|
|
my $runningconf = shift;
|
|
my $routes = getroutes();
|
|
my $result;
|
|
my $stat = 0;
|
|
|
|
if ( my $old = $runningconf->{$iface->{'name'}} ) {
|
|
if ( ( $old->{'address'} && $iface->{'address'} &&
|
|
$old->{'address'} eq $iface->{'address'} ) ||
|
|
( $old->{'state'} eq 'UP' && $iface->{'inet'} eq 'dhcp' ) ) {
|
|
if ( VERBOSE ) {
|
|
print STDERR "Interface $iface->{'name'}"
|
|
. " is already up\n";
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
if ( VERBOSE ) {
|
|
print "Flushing interface $iface->{'name'}\n";
|
|
}
|
|
|
|
$result = runcmd( "ip addr flush dev $iface->{'name'}" );
|
|
|
|
if ( VERBOSE ) {
|
|
if ( $result->{'stdout'} ) {
|
|
print $result->{'stdout'};
|
|
}
|
|
if ( $result->{'stderr'} ) {
|
|
print STDERR $result->{'stderr'};
|
|
}
|
|
}
|
|
|
|
if ( $result->{'stat'} ) {
|
|
return( $result->{'stat'} );
|
|
}
|
|
}
|
|
|
|
if ( $iface->{'macaddr'} ) {
|
|
if ( VERBOSE ) {
|
|
print "Setting MAC address on interface"
|
|
. " $iface->{'name'} to $iface->{'macaddr'}\n";
|
|
}
|
|
|
|
$result = runcmd( "ip link set dev $iface->{'name'}"
|
|
. " address $iface->{'macaddr'}" );
|
|
|
|
if ( VERBOSE ) {
|
|
if ( $result->{'stdout'} ) {
|
|
print $result->{'stdout'};
|
|
}
|
|
if ( $result->{'stderr'} ) {
|
|
print STDERR $result->{'stderr'};
|
|
}
|
|
}
|
|
|
|
if ( $result->{'stat'} ) {
|
|
return( $result->{'stat'} );
|
|
}
|
|
}
|
|
|
|
if ( VERBOSE ) {
|
|
print "Bringing up interface $iface->{'name'}\n";
|
|
}
|
|
|
|
if ( $iface->{'inet'} && $iface->{'inet'} eq 'static' ) {
|
|
if ( VERBOSE ) {
|
|
print "Setting address to $iface->{'address'}"
|
|
. " on interface $iface->{'name'}\n";
|
|
}
|
|
|
|
$result = runcmd( "ip address add $iface->{'address'}"
|
|
. " dev $iface->{'name'}" );
|
|
|
|
if ( VERBOSE ) {
|
|
if ( $result->{'stdout'} ) {
|
|
print $result->{'stdout'};
|
|
}
|
|
if ( $result->{'stderr'} ) {
|
|
print STDERR $result->{'stderr'};
|
|
}
|
|
}
|
|
|
|
if ( $result->{'stat'} ) {
|
|
return( $result->{'stat'} );
|
|
}
|
|
}
|
|
elsif ( $iface->{'inet'} && $iface->{'inet'} eq 'loopback' &&
|
|
! $runningconf->{$iface->{'name'}}{'name'} ) {
|
|
if ( VERBOSE ) {
|
|
print "Setting address to $iface->{'address'}"
|
|
. " on interface $iface->{'name'}\n";
|
|
}
|
|
|
|
$result = runcmd( "ip -d address add $iface->{'address'}"
|
|
. " dev $iface->{'name'}" );
|
|
|
|
if ( VERBOSE ) {
|
|
if ( $result->{'stdout'} ) {
|
|
print $result->{'stdout'};
|
|
}
|
|
if ( $result->{'stderr'} ) {
|
|
print STDERR $result->{'stderr'};
|
|
}
|
|
}
|
|
|
|
if ( $result->{'stat'} ) {
|
|
return( $result->{'stat'} );
|
|
}
|
|
}
|
|
elsif ( $iface->{'inet'} && $iface->{'inet'} eq 'dhcp' ) {
|
|
if ( -f '/var/run/dhclient.pid' ) {
|
|
system( "dhclient -r $iface->{'name'}" );
|
|
}
|
|
|
|
if ( VERBOSE ) {
|
|
print "Starting dhclient for $iface->{'name'}\n";
|
|
}
|
|
|
|
$result = runcmd( "dhclient $iface->{'name'}" );
|
|
|
|
if ( VERBOSE ) {
|
|
if ( $result->{'stdout'} ) {
|
|
print $result->{'stdout'};
|
|
}
|
|
if ( $result->{'stderr'} ) {
|
|
print STDERR $result->{'stderr'};
|
|
}
|
|
}
|
|
|
|
if ( $result->{'stat'} ) {
|
|
return( $result->{'stat'} );
|
|
}
|
|
}
|
|
|
|
$result = runcmd( "ip link set $iface->{'name'} up" );
|
|
|
|
if ( VERBOSE ) {
|
|
if ( $result->{'stdout'} ) {
|
|
print $result->{'stdout'};
|
|
}
|
|
if ( $result->{'stderr'} ) {
|
|
print STDERR $result->{'stderr'};
|
|
}
|
|
}
|
|
|
|
if ( $result->{'stat'} ) {
|
|
return( $result->{'stat'} );
|
|
}
|
|
|
|
if ( ! $routes->{'default'} && $iface->{'gateway'} ) {
|
|
if ( VERBOSE ) {
|
|
print "Adding default gateway $iface->{'gateway'}\n";
|
|
}
|
|
|
|
$result = runcmd( "ip route add default"
|
|
. " via $iface->{'gateway'}" );
|
|
|
|
if ( VERBOSE ) {
|
|
if ( $result->{'stdout'} ) {
|
|
print $result->{'stdout'};
|
|
}
|
|
if ( $result->{'stderr'} ) {
|
|
print STDERR $result->{'stderr'};
|
|
}
|
|
}
|
|
|
|
if ( $result->{'stat'} ) {
|
|
return( $result->{'stat'} );
|
|
}
|
|
}
|
|
elsif ( $routes->{'default'}{'via'} && $iface->{'gateway'} &&
|
|
$routes->{'default'}{'via'} ne $iface->{'gateway'} &&
|
|
$routes->{'default'}{'dev'} eq $iface->{'name'} ) {
|
|
if ( VERBOSE ) {
|
|
print "Changing default gateway to"
|
|
. " $iface->{'gateway'}\n";
|
|
}
|
|
|
|
$result = runcmd( "ip route delete default via"
|
|
. " $routes->{'default'}{'via'} dev $iface->{'name'}" );
|
|
|
|
if ( VERBOSE ) {
|
|
if ( $result->{'stdout'} ) {
|
|
print $result->{'stdout'};
|
|
}
|
|
if ( $result->{'stderr'} ) {
|
|
print STDERR $result->{'stderr'};
|
|
}
|
|
}
|
|
|
|
if ( $result->{'stat'} ) {
|
|
return( $result->{'stat'} );
|
|
}
|
|
|
|
$result = runcmd( "ip route add default"
|
|
. " via $iface->{'gateway'}" );
|
|
|
|
if ( VERBOSE ) {
|
|
if ( $result->{'stdout'} ) {
|
|
print $result->{'stdout'};
|
|
}
|
|
if ( $result->{'stderr'} ) {
|
|
print STDERR $result->{'stderr'};
|
|
}
|
|
}
|
|
|
|
if ( $result->{'stat'} ) {
|
|
return( $result->{'stat'} );
|
|
}
|
|
}
|
|
|
|
if ( $iface->{'routes'} ) {
|
|
foreach my $target ( keys( %{$iface->{'routes'}} ) ) {
|
|
if ( VERBOSE ) {
|
|
print "Adding route $target via"
|
|
. " $iface->{'routes'}{$target}"
|
|
. " dev $iface->{'name'}\n";
|
|
}
|
|
|
|
system( "ip route add $target via"
|
|
. " $iface->{'routes'}{$target}"
|
|
. " dev $iface->{'name'}" );
|
|
}
|
|
}
|
|
}
|
|
|
|
sub listfiles {
|
|
my $dir = shift;
|
|
my $files = [];
|
|
my $sorted = [];
|
|
|
|
if ( ! -d $dir ) {
|
|
return;
|
|
}
|
|
|
|
opendir( DIR, $dir ) || error( int( $! ), "opendir(): $dir: $!" );
|
|
|
|
while ( readdir( DIR ) ) {
|
|
if ( ! -f "$dir/$_" || $_ =~ /^\./ ) {
|
|
next;
|
|
}
|
|
|
|
push ( @$files, $_ );
|
|
}
|
|
|
|
@$sorted = sort( @$files );
|
|
|
|
return( $sorted );
|
|
}
|
|
|
|
sub netmask2cidr {
|
|
my $netmask = shift;
|
|
my $bits = {
|
|
0 => 0,
|
|
128 => 1,
|
|
192 => 2,
|
|
224 => 3,
|
|
240 => 4,
|
|
248 => 5,
|
|
252 => 6,
|
|
254 => 7,
|
|
255 => 8
|
|
};
|
|
my $lastoct;
|
|
my $cidr = 0;
|
|
|
|
foreach ( split( /\./, $netmask ) ) {
|
|
if ( ( $lastoct && $lastoct != 255 && $_ != 0 ) ||
|
|
! defined( $bits->{$_} ) ) {
|
|
error( int( $! ), "netmask2cidr(): $netmask:"
|
|
. " Invalid netmask" );
|
|
}
|
|
|
|
$cidr += $bits->{$_};
|
|
$lastoct = $_;
|
|
}
|
|
|
|
return( $cidr );
|
|
}
|
|
|
|
sub readconf {
|
|
my $file = shift;
|
|
my $conf = shift || {};
|
|
my $iface;
|
|
|
|
open( my $config, "<$file" ) || error( int( $! ), "open(): $file: $!" );
|
|
|
|
while ( <$config> ) {
|
|
if ( $_ =~ /^\s*#/ ) {
|
|
next;
|
|
}
|
|
|
|
if ( $_ =~ /source-directory\s+(\S+)/ ) {
|
|
my $path = $1;
|
|
my $files;
|
|
|
|
if ( substr( $path, 0, 1 ) ne '/' ) {
|
|
$path = CONFDIR . "/$path";
|
|
}
|
|
|
|
$files = listfiles( $path );
|
|
|
|
foreach my $file ( @$files ) {
|
|
readconf( "$path/$file", $conf );
|
|
}
|
|
}
|
|
elsif ( $_ =~ /auto\s+(\S+)/ && $1 =~ /^(\S+):(\S+)$/ ) {
|
|
$conf->{$1}{'aliases'}{$2}{'auto'} = 1;
|
|
}
|
|
elsif ( $_ =~ /auto\s+(\S+)/ ) {
|
|
$conf->{$1}{'auto'} = 1;
|
|
}
|
|
elsif ( $_ =~ /iface\s+(\S+):(\S+)/ ) {
|
|
$iface = "$1:$2";
|
|
$conf->{$1}{'aliases'}{$2}{'dev'} = $1;
|
|
$conf->{$1}{'aliases'}{$2}{'name'} = $2;
|
|
$conf->{$1}{'aliases'}{$2}{'cnt'} =
|
|
keys( %{$conf->{$1}{'aliases'}} );
|
|
}
|
|
elsif ( $_ =~ /iface\s+(\S+)\s+(\S+)\s+(\S+)/ ) {
|
|
$iface = $1;
|
|
|
|
if ( $conf->{$iface}{'inet'} ) {
|
|
error( -1, "Multiple instances of $iface"
|
|
. " in $file" );
|
|
}
|
|
|
|
$conf->{$iface}{$2} = $3;
|
|
$conf->{$iface}{'name'} = $iface;
|
|
|
|
if ( $3 eq 'loopback' ) {
|
|
$conf->{$iface}{'address'} = '127.0.0.1/8';
|
|
}
|
|
|
|
$conf->{$iface}{'cnt'} = scalar( keys( %$conf ) );
|
|
}
|
|
elsif ( $_ =~ /route\s+(\S+)\s+via\s+(\S+)/ ) {
|
|
$conf->{$iface}{'routes'}{$1} = $2;
|
|
}
|
|
elsif ( $iface && $_ =~ /^\s*(\S+)\s+(\S+)/ ) {
|
|
my ( $attr, $value ) = ( $1, $2 );
|
|
|
|
if ( $iface =~ /^(\S+):(\S+)/ ) {
|
|
$conf->{$1}{'aliases'}{$2}{$attr} = $value;
|
|
}
|
|
else {
|
|
$conf->{$iface}{$attr} = $value;
|
|
}
|
|
}
|
|
elsif ( $iface && $_ =~ /^\s*(\S+)\s+(\S+)/ ) {
|
|
$conf->{$iface}{$1} = $2;
|
|
}
|
|
}
|
|
|
|
close( $config );
|
|
|
|
foreach my $devname ( keys( %$conf ) ) {
|
|
my $dev = $conf->{$devname};
|
|
|
|
if ( $dev->{'address'} && $dev->{'netmask'} ) {
|
|
$dev->{'address'} .= '/'
|
|
. netmask2cidr( $dev->{'netmask'} );
|
|
|
|
undef( $dev->{'netmask'} );
|
|
}
|
|
|
|
if ( ! keys( %{$dev->{'aliases'}} ) ) {
|
|
next;
|
|
}
|
|
|
|
foreach my $aliasname ( keys( %{$dev->{'aliases'}} ) ) {
|
|
my $alias = $dev->{'aliases'}{$aliasname};
|
|
|
|
if ( $alias->{'address'} && $alias->{'netmask'} ) {
|
|
$alias->{'address'} .= '/'
|
|
. netmask2cidr( $alias->{'netmask'} );
|
|
|
|
undef( $alias->{'netmask'} );
|
|
}
|
|
}
|
|
}
|
|
|
|
return( $conf );
|
|
}
|
|
|
|
sub runcmd {
|
|
if ( NOACT ) {
|
|
return( { stat => 0 } );;
|
|
}
|
|
|
|
my $cmd = shift;
|
|
my $result = {
|
|
stdout => '',
|
|
stderr => '',
|
|
stat => ''
|
|
};
|
|
my $sel = IO::Select->new();
|
|
my $pid = eval {
|
|
return( open3( \*CHLDIN, \*CHLDOUT, \*CHLDERR, $cmd ) );
|
|
} || error( int( $! ), "open3(): $cmd: $!" );
|
|
|
|
close( CHLDIN );
|
|
|
|
$sel->add( *CHLDOUT, *CHLDERR );
|
|
|
|
while ( my @fhs = $sel->can_read ) {
|
|
foreach my $fh ( @fhs ) {
|
|
if ( eof( $fh ) ) {
|
|
$sel->remove( $fh );
|
|
|
|
next;
|
|
}
|
|
|
|
if ( fileno( $fh ) == fileno( *CHLDOUT ) ) {
|
|
$result->{'stdout'} .= <$fh>;
|
|
}
|
|
elsif ( fileno( $fh ) == fileno( *CHLDERR ) ) {
|
|
$result->{'stderr'} .= <$fh>;
|
|
}
|
|
}
|
|
}
|
|
|
|
close( CHLDOUT );
|
|
close( CHLDERR );
|
|
|
|
waitpid( $pid, 0 );
|
|
$result->{'stat'} = $? >> 8;
|
|
|
|
return( $result );
|
|
}
|
|
|
|
sub runningconf {
|
|
my $cmd = 'ip -d address show';
|
|
my $sel = IO::Select->new();
|
|
my $pid = eval {
|
|
return( open3( \*CHLDIN, \*CHLDOUT, \*CHLDERR, $cmd ) );
|
|
} || error( int( $! ), "open3(): $cmd: $!" );
|
|
my $stat;
|
|
my $stdout = '';
|
|
my $stderr = '';
|
|
my $runningconf = {};
|
|
my $macaddr;
|
|
my $dev;
|
|
|
|
close( CHLDIN );
|
|
|
|
$sel->add( *CHLDOUT, *CHLDERR );
|
|
|
|
while ( my @fhs = $sel->can_read ) {
|
|
foreach my $fh ( @fhs ) {
|
|
if ( eof( $fh ) ) {
|
|
$sel->remove( $fh );
|
|
|
|
next;
|
|
}
|
|
|
|
if ( fileno( $fh ) == fileno( *CHLDOUT ) ) {
|
|
$stdout .= <$fh>;
|
|
}
|
|
elsif ( fileno( $fh ) == fileno( *CHLDERR ) ) {
|
|
$stderr .= <$fh>;
|
|
}
|
|
}
|
|
}
|
|
|
|
close( CHLDOUT );
|
|
close( CHLDERR );
|
|
|
|
waitpid( $pid, 0 );
|
|
$stat = $? >> 8;
|
|
|
|
if ( $stat ) {
|
|
error( int( $! ), "open3: $cmd: $stderr" );
|
|
}
|
|
|
|
open( my $fh, '<', \$stdout ) || error( int( $! ), "open(): $!" );
|
|
|
|
while ( <$fh> ) {
|
|
if ( $_ =~ /^\d+:\s*(\S+):/ ) {
|
|
$dev = $1;
|
|
}
|
|
|
|
if ( $_ =~ /\s+mtu\s+(\d+)/ ) {
|
|
$runningconf->{$dev}{'mtu'} = $1;
|
|
}
|
|
if ( $_ =~ /\s+qdisc\s+(\S+)/ ) {
|
|
$runningconf->{$dev}{'qdisc'} = $1;
|
|
}
|
|
if ( $_ =~ /\s+state\s+(\S+)/ ) {
|
|
$runningconf->{$dev}{'state'} = $1;
|
|
}
|
|
if ( $_ =~ /\s+group\s+(\S+)/ ) {
|
|
$runningconf->{$dev}{'group'} = $1;
|
|
}
|
|
if ( $_ =~ /\s+qlen\s+(\S+)/ ) {
|
|
$runningconf->{$dev}{'qlen'} = $1;
|
|
}
|
|
if ( $_ =~ /^\s+link\/(\S+)\s+(\S+)/ ) {
|
|
$runningconf->{$dev}{'link'} = $1;
|
|
$runningconf->{$dev}{'macaddr'} = $2;
|
|
}
|
|
if ( $_ =~ /\s+promiscuity\s+(\S+)/ ) {
|
|
$runningconf->{$dev}{'promiscuity'} = $1;
|
|
}
|
|
|
|
if ( $_ =~ /^\s+inet\s+(\S+).*\s+(\S+):(\S+)/ ) {
|
|
$runningconf->{$2}{'aliases'}{$3}{'address'} = $1;
|
|
$runningconf->{$2}{'aliases'}{$3}{'dev'} = $dev;
|
|
$runningconf->{$2}{'aliases'}{$3}{'name'} = $3;
|
|
}
|
|
elsif ( $_ =~ /^\s+inet\s+(\S+).*(\S+)$/ ) {
|
|
if ( $runningconf->{$dev}{'address'} ) {
|
|
next;
|
|
}
|
|
|
|
$runningconf->{$dev}{'address'} = $1;
|
|
}
|
|
}
|
|
|
|
close( $fh );
|
|
|
|
return( $runningconf );
|
|
}
|
|
|
|
my $conf = readconf( CONFIGFILE );
|
|
my $runningconf = runningconf();
|
|
my $self = ( split( '/', $0 ) )[-1];
|
|
my $ifaces = [];
|
|
my $all = eval {
|
|
for ( my $i = 0; $i <= $#ARGV; $i++ ) {
|
|
if ( $ARGV[$i] eq '-a' || $ARGV[$i] eq '--all' ) {
|
|
splice( @ARGV, $i, 1 );
|
|
|
|
return( 1 );
|
|
}
|
|
}
|
|
};
|
|
my $stat = 0;
|
|
#print Dumper( $conf ); exit;
|
|
|
|
if ( grep( $_ eq '-h' || $_ eq '--help', @ARGV ) ) {
|
|
print "Usage: $self [OPTIONS] [INTERFACES]\n";
|
|
|
|
if ( $self eq 'ifup' ) {
|
|
print "Bring a network interface up\n\n";
|
|
}
|
|
elsif ( $self eq 'ifdown' ) {
|
|
print "Bring a network interface down\n\n";
|
|
}
|
|
elsif ( $self eq 'ifreload' ) {
|
|
print "Reload interface configuration\n\n";
|
|
}
|
|
elsif ( $self eq 'ifquery' ) {
|
|
print "View current status of interfaces\n\n";
|
|
}
|
|
|
|
print "Options:\n"
|
|
. "\t-a, --all\t\tperform actions on all 'auto' interfaces\n"
|
|
. "\t-h, --help\t\tprint usage information\n";
|
|
|
|
if ( $self ne 'ifquery' ) {
|
|
print "\t-n, --no-act\t\tdisplay actions without taking them\n"
|
|
. "\t-v, --verbose\t\tdisplay actions as they occur\n";
|
|
}
|
|
|
|
print "\t-V, --version\t\tdisplay version information\n";
|
|
|
|
exit( 0 );
|
|
}
|
|
elsif ( grep( $_ eq '-V' || $_ eq '--version', @ARGV ) ) {
|
|
print "iftools version " . VERSION . "\n";
|
|
|
|
exit( 0 );
|
|
}
|
|
|
|
if ( ! $all ) {
|
|
foreach ( @ARGV ) {
|
|
( my $dev = $_ ) =~ s/:.*//;
|
|
|
|
if ( $runningconf->{$dev} ) {
|
|
push( @$ifaces, $_ );
|
|
}
|
|
elsif ( VERBOSE ) {
|
|
print STDERR "$_: No such device\n";
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
foreach ( sort { $conf->{$a}{'cnt'} <=> $conf->{$b}{'cnt'} }
|
|
( keys( %$conf ) ) ) {
|
|
( my $dev = $_ ) =~ s/:.*//;
|
|
|
|
if ( $self eq 'ifup' && ! $conf->{$_}{'auto'} ) {
|
|
next;
|
|
}
|
|
|
|
if ( $runningconf->{$dev} ) {
|
|
push( @$ifaces, $_ );
|
|
}
|
|
elsif ( VERBOSE ) {
|
|
print STDERR "$_: No such device\n";
|
|
|
|
next;
|
|
}
|
|
|
|
foreach my $alias ( sort {
|
|
$conf->{$dev}{'aliases'}{$a}{'cnt'} <=>
|
|
$conf->{$dev}{'aliases'}{$b}{'cnt'} }
|
|
keys( %{$conf->{$dev}{'aliases'}} ) ) {
|
|
push( @$ifaces, "$dev:$alias" );
|
|
}
|
|
}
|
|
}
|
|
|
|
foreach my $iface ( @$ifaces ) {
|
|
if ( $self eq 'ifup' ) {
|
|
if ( $iface =~ /^(\S+):(\S+)/ ) {
|
|
$stat = aliasup( $conf->{$1}{'aliases'}{$2},
|
|
$runningconf ) || $stat;
|
|
}
|
|
else {
|
|
$stat = ifup( $conf->{$iface}, $runningconf ) || $stat;
|
|
}
|
|
}
|
|
elsif ( $self eq 'ifdown' ) {
|
|
if ( $iface =~ /^(\S+):(\S+)/ ) {
|
|
$stat = aliasdown( $conf->{$1}{'aliases'}{$2},
|
|
$runningconf ) || $stat;
|
|
}
|
|
else {
|
|
$stat = ifdown( $conf->{$iface}, $runningconf ) ||
|
|
$stat;
|
|
}
|
|
}
|
|
elsif ( $self eq 'ifreload' ) {
|
|
if ( $iface =~ /^(\S+):(\S+)/ ) {
|
|
$stat = aliasdown( $conf->{$1}{'aliases'}{$2},
|
|
$runningconf ) || $stat;
|
|
$runningconf = runningconf();
|
|
$stat = aliasup( $conf->{$1}{'aliases'}{$2},
|
|
$runningconf ) || $stat;
|
|
}
|
|
else {
|
|
$stat = ifdown( $conf->{$iface}, $runningconf ) ||
|
|
$stat;
|
|
$runningconf = runningconf();
|
|
$stat = ifup( $conf->{$iface}, $runningconf ) ||
|
|
$stat;
|
|
}
|
|
}
|
|
elsif ( $self eq 'ifquery' ) {
|
|
if ( $iface =~ /^\S+:\S+/ ) {
|
|
next;
|
|
}
|
|
elsif ( ! $runningconf->{$iface} ) {
|
|
$runningconf->{$iface} = {
|
|
state => 'No such device',
|
|
address => '',
|
|
macaddr => ''
|
|
};
|
|
}
|
|
|
|
print "[$iface]\n"
|
|
. " State: $runningconf->{$iface}{'state'}\n";
|
|
|
|
if ( $runningconf->{$iface}{'address'} ) {
|
|
print " Address: $runningconf->{$iface}{'address'}\n";
|
|
}
|
|
if ( $runningconf->{$iface}{'macaddr'} ) {
|
|
print " MAC: $runningconf->{$iface}{'macaddr'}\n";
|
|
}
|
|
|
|
foreach my $name ( sort( keys(
|
|
%{$runningconf->{$iface}{'aliases'}} ) ) ) {
|
|
my $alias = $runningconf->{$iface}{'aliases'}{$name};
|
|
print "[$iface:$name]\n"
|
|
. " Address: $alias->{'address'}\n";
|
|
}
|
|
}
|
|
|
|
$runningconf = runningconf();
|
|
}
|
|
|
|
close( SELF );
|
|
exit( $stat );
|