commit b8d5489de69ccb5891acb682740c179838976afb Author: Jay Larson Date: Thu May 4 21:55:18 2017 -0500 First check in - not complete! The ifup script is functional though. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..f24ad28 --- /dev/null +++ b/Makefile @@ -0,0 +1,75 @@ +# This file is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 as +# published by the Free Software Foundation here: +# (http://www.gnu.org/licenses/gpl-2.0.html) +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +DEPENDS = iproute2,perl +ARCH = +URL = +BRIEF = +DESC = +SNAPVER = + +ARCHIVE := $(PWD)/SRC/$(shell ls SRC|egrep '(bz2|gz|tar|xz)$$'|tail -1) +TYPE := $(shell file -ib $(ARCHIVE)|cut -d';' -f1|tr -d '\n') +SRCDIR := $(shell tar -tf $(ARCHIVE)|head -1|sed 's/\/.*//') +PATCHDIR := $(PWD)/SRC/patches +VERSION := $(shell echo $(SRCDIR)|egrep -o '\-[0-9].*'|sed 's/^-//')-$(SNAPVER) + +include /usr/share/snap/Makefile.snaplinux + +$(SRCDIR)/configure: $(ARCHIVE) + @if [ '$(TYPE)' == 'application/x-bzip2' ]; then \ + tar -jxf $(ARCHIVE); \ + elif [ '$(TYPE)' == 'application/x-gzip' ]; then \ + tar -zxf $(ARCHIVE); \ + elif [ '$(TYPE)' == 'application/x-tar' ]; then \ + tar -xf $(ARCHIVE); \ + elif [ '$(TYPE)' == 'application/x-xz' ]; then \ + tar -xf $(ARCHIVE); \ + else \ + echo 'Unable to determine archive type'; \ + exit 1; \ + fi + @touch $(SRCDIR)/configure + +$(SRCDIR)/config.log: $(SRCDIR)/configure + @cd $(SRCDIR) && \ + for patch in `find $(PATCHDIR) -name \*.patch|sort`; do \ + patch --verbose -Np1 -i $$patch; \ + done + @cd $(SRCDIR); \ + ./configure \ + --prefix=/usr \ + --build=x86_64-snap-linux-gnu \ + --host=x86_64-snap-linux-gnu \ + --target=x86_64-snap-linux-gnu + +$(SRCDIR)/binfile: $(SRCDIR)/config.log + @cd $(SRCDIR) && make + +$(ROOT): $(SRCDIR)/binfile + @if [ -d $(ROOT) ]; then \ + touch $(ROOT); \ + else \ + mkdir -v $(ROOT); \ + fi + + @cd $(SRCDIR) && make install DESTDIR=$(ROOT) + +test: $(ROOT) + @cd $(SRCDIR); \ + make check + +clean: + @rm -rvf $(ROOT) \ + $(SNAPINFO) \ + $(MANIFEST) \ + $(FILES) \ + $(SRCDIR) + diff --git a/SNAP/README b/SNAP/README new file mode 100644 index 0000000..cb756b1 --- /dev/null +++ b/SNAP/README @@ -0,0 +1,5 @@ +This is the directory where the manifest, snapinfo, +and files.tar.gz files will be created. It is also +where the usher file should be placed if it is +required by the package. Any other files that need +to be included could also be placed here. diff --git a/SRC/ifup b/SRC/ifup new file mode 100755 index 0000000..6adf9bb --- /dev/null +++ b/SRC/ifup @@ -0,0 +1,906 @@ +#!/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" + . " Address: $runningconf->{$iface}{'address'}\n" + . " 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 ); diff --git a/SRC/ifup.8 b/SRC/ifup.8 new file mode 100644 index 0000000..77e326a --- /dev/null +++ b/SRC/ifup.8 @@ -0,0 +1,98 @@ +.TH ifup 8 "May 2017" "iftools" "Linux System Administrator's Manual" +.SH NAME +ifup \- bring a network interface up +.br +ifdown \- take a network interface down +.br +ifreload \- reload interface configuration +.br +ifquery \- view current state of interfaces +.SH SYNOPSIS +.B ifup +\f | +.B ifdown +\f | +.B ifreload +\f | +.B ifquery +\f +.SH DESCRIPTION +The +.BR ifup " and " ifdown +commands may be used to configure (or, respectively, deconfigure) network +interfaces based on interface definitions in the file +.IR /etc/network.conf ". The" +.BR ifreload " command will perform an ifdown and ifup in one command." +.BR ifquery " may be used to display the running configuration." +.SH OPTIONS +A summary of options is included below. +.TP +.BR \-a ", " \-\-all +If given to \fBifup\fP, affect all interfaces marked \fBauto\fP. +Interfaces are brought up in the order in which they are defined +in +.IR /etc/network.conf . +If given to \fBifdown\fP, affect all defined interfaces. +Interfaces are brought down in the order in which they are +listed in the configuration files. +.TP +.BR \-h ", " \-\-help +Show summary of options. +.TP +.BR \-n ", " \-\-no\-act +Don't configure any interfaces or run any "up" or "down" commands. +.TP +.BR \-V ", " \-\-version +Show copyright and version information. +.TP +.BR \-v ", " \-\-verbose +Show commands as they are executed. +.SH EXAMPLES +.TP +.B ifup -a +Bring up all the interfaces defined with +.I auto +in +.nh +.I /etc/network.conf +.TP +.B ifup eth0 +Bring up interface +.B eth0 +.TP +.B ifdown -a +Bring down all interfaces that are currently up. +.TP +.B ifquery eth0 +Print the running configuration of eth0 +.SH NOTES +.BR ifup , +.BR ifdown , +.BR ifreload , +and +.BR ifquery +are actually the same program called by different names. +.P +The program does not configure network interfaces directly; +it runs low level utilities such as +.BR ip +to do its dirty work. +.P +When invoked a check will be performed to verify that there +are no other instances running. If another instance is +detected the program will exit with an error. +.SH FILES +.TP +.I /etc/network.conf +definitions of network interfaces +See +.BR network.conf (5) +for more information. +.TP +.I /run/network/ifstate +current state of network interfaces +.SH AUTHOR +iftools was written by Jay Larson . The functionality is loosely based on the ifupdown suite written by Anthony Towns . +.SH SEE ALSO +.BR network.conf (5), +.BR ip (8) diff --git a/SRC/network.conf.5 b/SRC/network.conf.5 new file mode 100644 index 0000000..ed0c1e6 --- /dev/null +++ b/SRC/network.conf.5 @@ -0,0 +1,89 @@ +.TH network.conf 5 "May 2017" "iftools" "File formats" +.SH NAME +/etc/network.conf \- network interface configuration for iftools +.SH DESCRIPTION +/etc/network.conf contains network interface configuration +information for the +.BR ifup (8) +and +.BR ifdown (8) +commands. +This is where you configure how your system is connected to the network. +.P +Lines starting with `#' are ignored. Note that end-of-line comments are +NOT supported, comments must be on a line of their own. +.P +The file consists of any number of "iface", "auto", and +.nh +"source\-directory" stanzas. Here is an example: +.P +.RS +.EX +auto eth0 +iface eth0 inet static + address 192.168.1.10 + netmask 255.255.255.0 + gateway 192.168.1.1 + +iface eth1 inet dhcp + +iface eth1:1 + address 192.168.1.2 + netmask 255.255.255.0 + +source-directory network.d +.EE +.RE +.P +Lines beginning with the word "auto" are used to identify the physical +interfaces to be brought up when +.B ifup +is run with the +.B \-a +option. (This option is used by the system boot scripts.) +There can be multiple "auto" stanzas. +.B ifup +brings the named interfaces up in the order listed. +.P +Lines beginning with "source-directory" are used to source multiple +files at once. All non-dotted files within the directory are sourced +in lexical order. +.P +When sourcing directories, if a path doesn't have a leading slash +it's considered relative to the directory containing the network.conf +file (/etc). In the example above, files in /etc/network.d are sourced. +.P +By default, on a freshly installed system, the network.conf file includes +a line to source files in the +.IR /etc/network.d +directory. +.P +Stanzas defining logical interfaces start with a line consisting of the +word "iface" followed by the name of the logical interface. The name +should be the name of the physical interface, a colon, and the logical +name. This is shown in the example above as eth1:1. +.P +Additional options can be given on subsequent lines in the stanza. +Options are usually indented for clarity (as in the example above) +but are not required to be. +.P +.SH OPTIONS +The following additional options are available: +.TP +.BR macaddr " XX:XX:XX:XX:XX:XX" +Set the MAC address to be used by a physical interface. This option +cannot be applied to logical interfaces. +.TP +.BR route " [NETWORK|IP] " via " [GATEWAY]" +Specify a gateway for a particular network or IP address. The network +or IP address can be specified in CIDR notation. This option is also +only available for physical devices. +.SH AUTHOR +iftools was written by Jay Larson . The functionality is +loosely based on the ifupdown suite written by Anthony Towns . +.SH "SEE ALSO" +.BR ifup (8), +.BR ip (8), +.BR ifconfig (8), +.BR run\-parts (8), +.BR resolvconf (8). diff --git a/SRC/patches/README b/SRC/patches/README new file mode 100644 index 0000000..976577a --- /dev/null +++ b/SRC/patches/README @@ -0,0 +1,3 @@ +Place any patch files here and preface each with a +number indicating the order of execution. Patch +files are expected to use a .patch extension.