#!/usr/bin/perl -w
#
# Copyright (c) 2012-2018 University of Utah and the Flux Group.
# 
# {{{EMULAB-LICENSE
# 
# This file is part of the Emulab network testbed software.
# 
# This file is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or (at
# your option) any later version.
# 
# This file 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 Affero General Public
# License for more details.
# 
# You should have received a copy of the GNU Affero General Public License
# along with this file.  If not, see <http://www.gnu.org/licenses/>.
# 
# }}}
#
use Getopt::Std;
use English;

#
# Set up bridging in a way that will make Xen 4 happy
#

# Default bridgename
my $XENBR = "xenbr0";
# Locations of some binaries we use, really should be in liblocsetup.pm
my $BRCTL = "/sbin/brctl";
my $IPBIN = "/bin/ip";
my $IFCONFIGBIN = "/sbin/ifconfig";
my $JAILGW   = "172.16.0.1";
my $JAILMASK = "255.240.0.0";
# For ilo. Boss takes .254
my $MNGGW    = "10.249.249.253";
my $MNGMASK  = "255.255.255.0";
my $NOIP = 0;

sub usage()
{
    print "Usage: xenbridge-setup [-b bridgename] [interface]\n";
    print  "Interface defaults to control net if not specificed\n";
    print  "Use -b option to name bridge (defaults to xenbr0)\n";
    exit(1);
}
my $optlist  = "b:N";
my %options  = ();
if (! getopts($optlist, \%options)) {
    usage();
}
if (defined($options{"b"})) {
    $XENBR = $options{"b"};
}
if (defined($options{"N"})) {
    $NOIP = 1;
}

my $interface = undef;
if (@ARGV == 1) {
    $interface = $ARGV[0];
} 
else {
    usage();
}
print "xenbridge-setup: Using interface $interface\n";

#
# Grab the IP configuration from the current control net interface
#
open(IFOUTPUT,"$IFCONFIGBIN $interface |")
    or die "xenbridge-setup: Unable to run $IFCONFIGBIN!\n";
my ($address,$netmask);
while (!eof(IFOUTPUT)) {
    my $line = <IFOUTPUT>;
    chomp $line;
    if ($line =~ /^\s+inet\s+addr:(\d+\.\d+\.\d+\.\d+).*Mask:(\d+\.\d+\.\d+\.\d+)/ ||
	$line =~ /^\s+inet (\d+\.\d+\.\d+\.\d+)\s+netmask (\d+\.\d+\.\d+\.\d+)/) {
        $address = $1;
        $netmask = $2;
    }
}
if ($NOIP) {
    # N.B. even if there was an IP, we do not propogate it
    $address = undef;
} elsif (!$address || !$netmask) {
    die "xenbridge-setup: Unable to determine IP address and mask for $interface\n";
}

if ($address) {
    print "xenbridge-setup: Using IP address $address and mask $netmask\n";
} else {
    print "xenbridge-setup: Not assigning IP address\n";
}

#
# Get the default route
#
my $defiface = "";
my $defroute;
if (!$NOIP) {
    open(ROUTEOUTPUT,"$IPBIN route list |")
	or die "xenbridge-setup: Unable to get route list!\n";
    while (!eof(ROUTEOUTPUT)) {
	my $line = <ROUTEOUTPUT>;
	chomp $line;
	if ($line =~ /^default via (\d+\.\d+\.\d+\.\d+)/) {
	    $defroute = $1;
	}
	if ($line =~ /^default via [\w\.\/]+\s+dev\s+([\w\.]+)/) {
	    $defiface = $1;
	}
    }
    if (!$defroute) {
	die "xenbridge-setup: Unable to determine default route\n";
    }
}
my $iscontrol = ($defiface eq $interface ? 1 : 0);
if ($iscontrol) {
    print "xenbridge-setup: Using default route $defroute via $defiface\n";
}

# 
# Make the bridge
#
if (system("$BRCTL addbr $XENBR")) {
    die "xenbridge-setup: Unable to create bridge $XENBR\n";
}

#
# Remove address from the interface
#
if (!$NOIP && system("$IPBIN address flush dev $interface\n")) {
    die "xenbridge-setup: Unable to remove $address from $interface\n";
}

#
# Add control net interface to the bridge
#
if (system("$BRCTL addif $XENBR $interface")) {
    die "xenbridge-setup: Unable to add $interface to bridge $XENBR\n";
}

# 
# Move IP address from the old interface to the bridge (if necessary)
# and bring up the interface.
#
if (!$NOIP) {
    if (system("$IFCONFIGBIN $XENBR inet $address netmask $netmask")) {
	die "xenbridge-setup: Unable to add address $address to bridge $XENBR\n";
    }
} else {
    if (system("$IFCONFIGBIN $XENBR up")) {
	die "xenbridge-setup: Cannot bring up bridge $XENBR\n";
    }
}

#
# Add back the default route
#
if ($iscontrol &&
    system "$IPBIN route add default via $defroute") {
    die "xenbridge-setup: Unable to add back default route $defroute\n";
}

#
# Alias the jail router to our control interface.
#
if ($iscontrol &&
    system "$IFCONFIGBIN ${XENBR}:1 inet $JAILGW netmask $JAILMASK up") {
    die "xenbridge-setup: Unable to add $JAILGW alias to $XENBR\n";
}
if ($iscontrol &&
    system "$IFCONFIGBIN ${XENBR}:2 inet $MNGGW netmask $MNGMASK up") {
    die "xenbridge-setup: Unable to add $MNGGW alias to $XENBR\n";
}

