#!/bin/sh

set -e
#set -x

if ! [ -r /etc/ikvswitch/ikvswitch.conf ] ; then
	echo "Could not read configuration file: exiting."
	exit 1
fi
. /etc/ikvswitch/ikvswitch.conf

# This is incremented each time a new NIC is added
MAC_END=10

forward_networking () {
	echo 1 >/proc/sys/net/ipv4/ip_forward
	echo 1 >/proc/sys/net/ipv6/conf/all/forwarding

	iptables -F FORWARD
	iptables -A FORWARD -j ACCEPT

	iptables -t nat -F POSTROUTING
	iptables -t nat -A POSTROUTING -s ${HOST_VIRTUAL_SUBNET_PREFIX}.0/19 ! -d ${HOST_VIRTUAL_SUBNET_PREFIX}.0/19 -j SNAT --to-source ${MY_IP}
	# This enables routing from host to everyone.
	# This isn't redundant, because only one via.
	# No choice unless we setup BGP in the host,
	# which isn't wanted.
	ip route add ${HOST_VIRTUAL_SUBNET_PREFIX}/19 via 192.168.96.2
	# This forwards LLDP packets through bridges.
	for i in /sys/devices/virtual/net/ik*/bridge/group_fwd_mask ; do echo 0x4000 >$i ; done
}

vm_tap_nic () {
	local NAME
	NAME=${1}
#	ip tuntap add mode tap user root ${NAME}
	ip tuntap add dev ${NAME} mode tap user root
	ifconfig ${NAME} hw ether ${MAC_PREFIX}:${MAC_END}
	MAC_END=$(printf "%X\n" $((0x${MAC_END} + 1)))
}

host_dummynet_nic () {
	local NAME
	NAME=${1}
	ip link add ${NAME} type dummy
	ifconfig ${NAME} hw ether ${MAC_PREFIX}:${MAC_END}
	MAC_END=$(printf "%X\n" $((0x${MAC_END} + 1)))
}

stop_host_to_spine_networking () {
	###########################
	### ipmi_sim networking ###
	###########################
	ip link set down dev ${HOST_IPMI_BRIDGE_NAME}
	ip addr delete ${HOST_IPMI_IP}/24 dev ${HOST_IPMI_BRIDGE_NAME} || true
	brctl delif ${HOST_IPMI_BRIDGE_NAME} ${VM_TAP_NIC} || true
	brctl delbr ${HOST_IPMI_BRIDGE_NAME} || true
	ip tuntap delete mode tap ${VM_TAP_NIC} || true

	###############
	### VM TAPs ###
	###############
	echo "---> Tearing down VMs TAPs"
	for RACK in $(seq 1 3) ; do
		echo -n "rack${RACK}:"
		for U in $(seq 3 ${NUM_U}) ; do
			echo -n " u${U}"
			case ${RACK} in
			"1")	LEAF_LEFT_NUM=1; LEAF_RIGHT_NUM=2
			;;
			"2")	LEAF_LEFT_NUM=3; LEAF_RIGHT_NUM=4
			;;
			"3")	LEAF_LEFT_NUM=5; LEAF_RIGHT_NUM=6
			;;
			esac
			brctl delif ${LEAF_TO_SERVER_BRIDGE_PREFIX}${LEAF_LEFT_NUM}-${U} ${HOST_TAP_NAME_PREFIX}${RACK}-${U}-1 || true
			brctl delif ${LEAF_TO_SERVER_BRIDGE_PREFIX}${LEAF_RIGHT_NUM}-${U} ${HOST_TAP_NAME_PREFIX}${RACK}-${U}-2 || true

			ip tuntap delete mode tap ${HOST_TAP_NAME_PREFIX}${RACK}-${U}-1 || true
			ip tuntap delete mode tap ${HOST_TAP_NAME_PREFIX}${RACK}-${U}-2 || true
		done
		echo ""
	done


	##############################
	### LEAF TO VMs NETWORKING ###
	##############################
	echo "---> Tearing down leaf to VMs"
	for i in $(seq 1 6) ; do
		echo -n "-> leaf${i}"
		# Each LEAF switch has 15 NICs starting from number 3
		for j in $(seq 3 ${NUM_U}) ; do
			echo -n " u${j}"
			brctl delif ${LEAF_TO_SERVER_BRIDGE_PREFIX}${i}-${j} ${LEAF_TO_SERVERS_TAP_PREFIX}${i}-${j} || true
			ip link set down ${LEAF_TO_SERVER_BRIDGE_PREFIX}${i}-${j} || true
			brctl delbr ${LEAF_TO_SERVER_BRIDGE_PREFIX}${i}-${j} || true
			ip tuntap delete mode tap ${LEAF_TO_SERVERS_TAP_PREFIX}${i}-${j} || true
		done
		echo ""
	done

	################################
	### LEAF TO SPINE NETWORKING ###
	################################
	echo "---> Tearing down leaf to spine"
	for i in $(seq 1 6) ; do
		# Delete the connection to both bridges, going to both spines
		SPINE_BRIDGE_SUFFIX=$(( $i + 1 ))
		brctl delif ${SPINE_BRIDGE_PREFIX}1${SPINE_BRIDGE_SUFFIX} ${LEAF_TO_SPINE_TAP_PREFIX}${i}1 || true
		brctl delif ${SPINE_BRIDGE_PREFIX}2${SPINE_BRIDGE_SUFFIX} ${LEAF_TO_SPINE_TAP_PREFIX}${i}2 || true
		# Delete the TAPs for the leaf switch
		ip tuntap delete mode tap ${LEAF_TO_SPINE_TAP_PREFIX}${i}1 || true
		ip tuntap delete mode tap ${LEAF_TO_SPINE_TAP_PREFIX}${i}2 || true
	done

	###################################
	### INTERNET TO HOST NETWORKING ###
	###################################
	echo "---> Tearing down internet to host"
	brctl delif ${HOST_BRIDGE_NAME_PREFIX} ${HOST_DUMMYNET_IFNAME_PREFIX} || true
	brctl delif ${HOST_BRIDGE_NAME_PREFIX} ${HOST_VM_TAP_IFNAME_PREFIX} || true
	ip addr del ${HOST_VIRTUAL_SUBNET_NET1}.1/24 dev ${HOST_BRIDGE_NAME_PREFIX} || true
	ip link set down ${HOST_BRIDGE_NAME_PREFIX} || true
	brctl delbr ${HOST_BRIDGE_NAME_PREFIX} || true
	ip tuntap delete mode tap ${HOST_VM_TAP_IFNAME_PREFIX} || true
	ip link delete ${HOST_DUMMYNET_IFNAME_PREFIX} || true

	####################################
	### SPINE TO INTERNET NETWORKING ###
	####################################
	# Remove VM TAPs from bridges
	echo "---> Tearing down spine to inernet"
	brctl delif ${INTERNET_TO_SPINE_BRIDGE_PREFIX}1 ${INTERNET_TO_SPINE_TAP_IFNAME_PREFIX}1 || true
	brctl delif ${INTERNET_TO_SPINE_BRIDGE_PREFIX}1 ${SPINE_TO_INTERNET_TAP_PREFIX}1 || true
	brctl delif ${INTERNET_TO_SPINE_BRIDGE_PREFIX}2 ${INTERNET_TO_SPINE_TAP_IFNAME_PREFIX}2 || true
	brctl delif ${INTERNET_TO_SPINE_BRIDGE_PREFIX}2 ${SPINE_TO_INTERNET_TAP_PREFIX}2 || true
	# Down the bridges
	ip link set down ${INTERNET_TO_SPINE_BRIDGE_PREFIX}1 || true
	ip link set down ${INTERNET_TO_SPINE_BRIDGE_PREFIX}2 || true
	brctl delbr ${INTERNET_TO_SPINE_BRIDGE_PREFIX}1 || true
	brctl delbr ${INTERNET_TO_SPINE_BRIDGE_PREFIX}2 || true
	# Delete the Internet to Spine TAPs
	ip tuntap delete mode tap ${INTERNET_TO_SPINE_TAP_IFNAME_PREFIX}1 || true
	ip tuntap delete mode tap ${INTERNET_TO_SPINE_TAP_IFNAME_PREFIX}2 || true
	# Delete the Spine to Internet TAP interfaces
	ip tuntap delete mode tap ${SPINE_TO_INTERNET_TAP_PREFIX}1 || true
	ip tuntap delete mode tap ${SPINE_TO_INTERNET_TAP_PREFIX}2 || true

	#######################################
	### SPINE TO LEAF SWITCH NETWORKING ###
	#######################################
	echo "---> Tearing down spine to leaf"
	for i in $(seq 2 7) ; do
		# Add the TAP to the bridge
		brctl delif ${SPINE_BRIDGE_PREFIX}1${i} ${SPINE_VM_TAP_IFNAME_PREFIX}1${i} || true
		brctl delif ${SPINE_BRIDGE_PREFIX}2${i} ${SPINE_VM_TAP_IFNAME_PREFIX}2${i} || true
		# Bridge for that TAP
		ip link set down ${SPINE_BRIDGE_PREFIX}1${i} || true
		brctl delbr ${SPINE_BRIDGE_PREFIX}1${i} || true
		ip link set down ${SPINE_BRIDGE_PREFIX}2${i} || true
		brctl delbr ${SPINE_BRIDGE_PREFIX}2${i} || true

		# Spine to leaf IFs TAP
		ip tuntap delete mode tap ${SPINE_VM_TAP_IFNAME_PREFIX}1${i} || true
		ip tuntap delete mode tap ${SPINE_VM_TAP_IFNAME_PREFIX}2${i} || true
	done
}

host_to_spine_networking () {
	###################################
	### INTERNET TO HOST NETWORKING ###
	###################################
	echo "---> Host to 'internet' VM"
	# Create dummynet on host side (ikhn1 + ikhn2)
	host_dummynet_nic ${HOST_DUMMYNET_IFNAME_PREFIX}
	# Create the 2 host bridges (ikhb1 + ikhb2)
	brctl addbr ${HOST_BRIDGE_NAME_PREFIX}
	# Give an IP to the bridges (192.168.96.1)
	ifconfig ${HOST_BRIDGE_NAME_PREFIX} ${HOST_VIRTUAL_SUBNET_NET1}.1 netmask 255.255.255.0 up
	ip link set up ${HOST_BRIDGE_NAME_PREFIX}
	# Add the dummynet IFs to the bridge
	brctl addif ${HOST_BRIDGE_NAME_PREFIX} ${HOST_DUMMYNET_IFNAME_PREFIX}
	# Create the TAP for the VM
	vm_tap_nic  ${HOST_VM_TAP_IFNAME_PREFIX}
	# Add the VM TAP to the bridges
	brctl addif ${HOST_BRIDGE_NAME_PREFIX} ${HOST_VM_TAP_IFNAME_PREFIX}

	#########################################
	### INTERNET TO SPINE HOST NETWORKING ###
	#########################################
	# Internet VM to spine TAPs interface
	echo "---> 'Internet' VM to spine 1 and 2"
	vm_tap_nic ${INTERNET_TO_SPINE_TAP_IFNAME_PREFIX}1
	vm_tap_nic ${INTERNET_TO_SPINE_TAP_IFNAME_PREFIX}2
	# Spine to Internet TAPs interface
	vm_tap_nic ${SPINE_TO_INTERNET_TAP_PREFIX}1
	vm_tap_nic ${SPINE_TO_INTERNET_TAP_PREFIX}2
	# Bridge to these TAPs
	brctl addbr ${INTERNET_TO_SPINE_BRIDGE_PREFIX}1
	brctl addbr ${INTERNET_TO_SPINE_BRIDGE_PREFIX}2
	# Add TAPs to the bridges
	brctl addif ${INTERNET_TO_SPINE_BRIDGE_PREFIX}1 ${INTERNET_TO_SPINE_TAP_IFNAME_PREFIX}1
	brctl addif ${INTERNET_TO_SPINE_BRIDGE_PREFIX}1 ${SPINE_TO_INTERNET_TAP_PREFIX}1
	brctl addif ${INTERNET_TO_SPINE_BRIDGE_PREFIX}2 ${INTERNET_TO_SPINE_TAP_IFNAME_PREFIX}2
	brctl addif ${INTERNET_TO_SPINE_BRIDGE_PREFIX}2 ${SPINE_TO_INTERNET_TAP_PREFIX}2
	# Up the bridges
	ip link set up ${INTERNET_TO_SPINE_BRIDGE_PREFIX}1
	ip link set up ${INTERNET_TO_SPINE_BRIDGE_PREFIX}2

	#######################################
	### SPINE TO LEAF SWITCH NETWORKING ###
	#######################################
	echo "---> Spine to leaf"
	for i in $(seq 2 7) ; do
		# Spine to leaf IFs TAP
		vm_tap_nic ${SPINE_VM_TAP_IFNAME_PREFIX}1${i}
		vm_tap_nic ${SPINE_VM_TAP_IFNAME_PREFIX}2${i}
		# Bridge for that TAP
		brctl addbr ${SPINE_BRIDGE_PREFIX}1${i}
		brctl addbr ${SPINE_BRIDGE_PREFIX}2${i}
		# Up the bridges, otherwise they don't route anything
		ip link set up ${SPINE_BRIDGE_PREFIX}1${i}
		ip link set up ${SPINE_BRIDGE_PREFIX}2${i}
		# Add the TAP to the bridge
		brctl addif ${SPINE_BRIDGE_PREFIX}1${i} ${SPINE_VM_TAP_IFNAME_PREFIX}1${i}
		brctl addif ${SPINE_BRIDGE_PREFIX}2${i} ${SPINE_VM_TAP_IFNAME_PREFIX}2${i}
	done

	################################
	### LEAF TO SPINE NETWORKING ###
	################################
	echo -n "---> Leaf to spine: "
	for i in $(seq 1 6) ; do
		echo -n " leaf${i}"
		# Create the TAPs for the leaf switch
		vm_tap_nic ${LEAF_TO_SPINE_TAP_PREFIX}${i}1
		vm_tap_nic ${LEAF_TO_SPINE_TAP_PREFIX}${i}2
		# Connect it to both bridges, going to both spines
		SPINE_BRIDGE_SUFFIX=$(( $i + 1 ))
		brctl addif ${SPINE_BRIDGE_PREFIX}1${SPINE_BRIDGE_SUFFIX} ${LEAF_TO_SPINE_TAP_PREFIX}${i}1
		brctl addif ${SPINE_BRIDGE_PREFIX}2${SPINE_BRIDGE_SUFFIX} ${LEAF_TO_SPINE_TAP_PREFIX}${i}2
	done
	echo ""

	##############################
	### LEAF TO VMs NETWORKING ###
	##############################
	echo "---> Leaf to VMs tap and bridges"
	for i in $(seq 1 6) ; do
		echo -n "leaf${i}:"
		# Each LEAF switch has 15 NICs starting from number 3
		for j in $(seq 3 ${NUM_U}) ; do
			echo -n " u${j}"
			vm_tap_nic ${LEAF_TO_SERVERS_TAP_PREFIX}${i}-${j}
			brctl addbr ${LEAF_TO_SERVER_BRIDGE_PREFIX}${i}-${j}
			ip link set up ${LEAF_TO_SERVER_BRIDGE_PREFIX}${i}-${j}
			brctl addif ${LEAF_TO_SERVER_BRIDGE_PREFIX}${i}-${j} ${LEAF_TO_SERVERS_TAP_PREFIX}${i}-${j}
		done
		echo ""
	done

	###############
	### VM TAPs ###
	###############
	echo "---> VM TAPs"
	for RACK in $(seq 1 3) ; do
		echo -n "rack${RACK}:"
		for U in $(seq 3 ${NUM_U}) ; do
			echo -n " u${U}"
			vm_tap_nic ${HOST_TAP_NAME_PREFIX}${RACK}-${U}-1
			vm_tap_nic ${HOST_TAP_NAME_PREFIX}${RACK}-${U}-2

			case ${RACK} in
			"1")	LEAF_LEFT_NUM=1; LEAF_RIGHT_NUM=2
			;;
			"2")	LEAF_LEFT_NUM=3; LEAF_RIGHT_NUM=4
			;;
			"3")	LEAF_LEFT_NUM=5; LEAF_RIGHT_NUM=6
			;;
			esac
			brctl addif ${LEAF_TO_SERVER_BRIDGE_PREFIX}${LEAF_LEFT_NUM}-${U} ${HOST_TAP_NAME_PREFIX}${RACK}-${U}-1
			brctl addif ${LEAF_TO_SERVER_BRIDGE_PREFIX}${LEAF_RIGHT_NUM}-${U} ${HOST_TAP_NAME_PREFIX}${RACK}-${U}-2
		done
		echo ""
	done

	###########################
	### ipmi_sim networking ###
	###########################
	brctl addbr ${HOST_IPMI_BRIDGE_NAME}
	ip addr add ${HOST_IPMI_IP}/24 dev ${HOST_IPMI_BRIDGE_NAME}
	vm_tap_nic ${VM_TAP_NIC}
	brctl addif ${HOST_IPMI_BRIDGE_NAME} ${VM_TAP_NIC}
	ip link set up dev ${HOST_IPMI_BRIDGE_NAME}
}

case "${1}" in
"start")
	host_to_spine_networking
	forward_networking
;;
"stop")
	stop_host_to_spine_networking
;;
*)
	echo "Wrong usage: exiting."
	exit 1
;;
esac

exit 0
