#!/bin/bash
#============================================================================
# Copyright (c) 2007 Luciano Rocha <luciano@eurotux.com>

# /etc/xen/scripts/vlan-bridge-common.sh

# Common functions for adding an interface to a bridge, supporting
# vlans and the creation of both vlans and bridges as needed.

# warning: If the real network interface is a bond device, adding
# a new vlan may cause the current active slave interface to become
# disabled. Try stopping haldaemon and/or avahi-daemon.

# This library is free software; you can redistribute it and/or
# modify it under the terms of version 2.1 of the GNU Lesser General Public
# License as published by the Free Software Foundation.
#
# This library 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
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

#============================================================================

PATH=/usr/bin:/bin:/usr/sbin:/sbin:$PATH

ifdown()
{
	ip link set dev $1 down
	ip addr flush dev $1 &> /dev/null
	ip route flush dev $1 &> /dev/null
}

ifup()
{
	ip link set dev $1 up
	ip addr flush dev $1 &> /dev/null
	ip route flush dev $1 &> /dev/null
}

exists()
{
	ip addr show dev $1 >/dev/null 2>&1
}

getnet()
{
	netconf_ip="$(ip addr show dev $1 | awk -v ORS="" '
		$1 == "inet" || $1 == "inet6" {
			print "ip addr add " $2;
			if ($3 == "brd") print " brd " $4;
			print " dev $dev;";
		}')"
	netconf_route="$(ip route show dev $1 | awk -v ORS="" -v IGNORECASE=1 '
		!/proto[[:blank:]]+kernel/ {
			if ($1 == "default") {
				print "ip route replace default via ";
				print $3;
				print " dev $dev;";
			} else {
				print "ip route add " $1;
				if ($2 == "via") print " via " $3;
				print " dev $dev;";
			}
		}')"
}

setnet()
{
	ifup $1
	eval "dev=$1; $netconf_ip :; $netconf_route :" 1>&2
}

bradd()
{
	local br="$1"

	shift

	if ! exists $br; then
		brctl addbr $br
		brctl setfd $br 0
		brctl stp $br off
		ifup $br
	fi

	getnet $1

	for dev; do
		ifup $dev
		brctl addif $br $dev
	done

	setnet $br
}

brif()
{
	local br="$1"
	shift

	brctl show | awk -v br=$br '
		BEGIN { inbr=0 }
		/^[^ \t]/ { inbr=0 }
		$1 == br { if ($4) { print $4 } inbr=1 }
		/^[ \t]/ && inbr==1 { print $1 }
	'
}

brdel()
{
	local br="$1"

	shift

	exists $br || return 0

	for dev; do
		brctl delif $br $dev
		ifdown $dev
	done

	[ $(brif $br | wc -l) -eq 0 ] || return 0

	getnet $br

	ifdown $br
	brctl delbr $br

	setnet $1
}

vlandel()
{
	local vid="$1" dev="$2"

	lockf=/var/tmp/.vlan.$vid.lock
	lockfile -2 -r 100 -l 40 $lockf || exit 1
	trap "rm -f $lockf" EXIT

	br=br$vid
	brdel $br $dev

	if [ $vid -gt 0 ]; then
		vlan=vlan$vid
	else
		vlan=$phy
	fi

	if [ $(brif $br | grep -vFx $vlan | wc -l) -eq 0 ]; then
		brdel $br $vlan
	fi
}

vlanadd()
{
	local vid="$1"
	shift

	lockf=/var/tmp/.vlan.$vid.lock
	lockfile -2 -r 100 -l 40 $lockf || exit 1
	trap "rm -f $lockf" EXIT

	if ! [ -d /proc/net/vlan ]; then
		modprobe -q 8021q
		vconfig set_name_type VLAN_PLUS_VID_NO_PAD
	fi

	br=br$vid
	if [ $vid -gt 0 ]; then
		vlan=vlan$vid

		if ! [ -f /proc/net/vlan/$vlan ]; then
			vconfig add $phy $vid
		fi
		bradd $br $vlan "$@"
	else
		bradd $br $phy "$@"
	fi
}

if exists bond0; then
	phy=bond0
else
	phy=eth0
fi
