#!/bin/sh -e
#
# Libvirt qemu hook to magically connect VM bridge interfaces to desired untagged Vlan
#
#  IF
#   * the VM interface if configured to be connected to a bridge,
#   * the bridge is configured to be vlan aware (vlan_filtering = 1)
#   * the VM interface name ends with _vXXXX with XXXX being a one to four
#     digit number
#  the interface will be configured as an untagged port connected to vlan XXXX.
#
#  See <man bridge> for more details about vlan aware bridges.
#
# Maximilian Wilhelm <max@sdn.clinic>
#  -- Wed, 31 Aug 2016 20:48:02 +0200

my_name="qemu-magic"

# We only care for the "started" event.
if [ "$2" != 'started' ]; then
	exit 0
fi

if ! which xmlstarlet >/dev/null 2>/dev/null; then
	logger -t "${my_name}" "ERROR: xmlstarlet not found. Dying of shame."
	echo "${my_name}: ERROR: xmlstarlet not found. Dying of shame." >&2
	exit 1
fi

# Save domain XML which was given to us on stdin, so we can poke around in it.
# Yes this is ugly. But I don't see another way storing the MTU for an device
# within the libvirt domain config when NOT using a libvirt network.
# If you happen to now a better way than specifying this within custom domain
# metadata PLEASE let me know.
export domain_xml="$(cat)"

echo "${domain_xml}" | xmlstarlet sel -t -m '//interface[@type="bridge"]' -v 'concat(target/@dev, " ", source/@bridge)'  --nl | while read iface bridge; do
	if [ ! -d "/sys/class/net/${bridge}/bridge" ]; then
		logger -t "${my_name}" "Bridge \"${bridge}\" for iface \"${iface}\" doesn't exist or isn't a bridge."
		exit 2
	fi

	#
	# Check if this kernel supports vlan-aware bridges and if ${bridge} is one
	vlan_filtering=0
	if [ -f "/sys/class/net/${bridge}/bridge/vlan_filtering" ]; then
		vlan_filtering=$(cat "/sys/class/net/${bridge}/bridge/vlan_filtering")
	fi

	# If the interface is named *_vXXXX, with XXXX being a 1-4 digit number
	# we assume that this iface should be connected to Vlan XXXX with
	# an untagged port.
	vlan_id=$(echo ${iface} | grep -o '_v[0-9]\{1,4\}$' | cut -c3-)

	# If vlan filtering is activated and we found a vlan id, kindly do the needful.
	if [ "${vlan_filtering}" -a "${vlan_id}" ]; then
		# Remove association with vlan 1 and add association with
		# vlan $vlan_id with packages being sent out untagged and
		# untagged ingress packets get tagged accordingly.
		bridge vlan del vid 1 dev "${iface}"
		bridge vlan add vid "${vlan_id}" dev "${iface}" pvid untagged
		logger -t "${my_name}" "Configured untagged pvid ${vlan_id} for ${iface} in bridge ${bridge}."

	# If vlan filtering isn't activated or supported but we found a vlan id,
	# this probably is an error!
	elif [ ! "${vlan_filtering}" -a "${vlan_id}" ]; then
		logger -t "${my_name}" -p user.error "ERROR: Should configure untagged pvid ${vlan_id} for ${iface} in bridge ${bridge}, but bridge does not support vlan filtering!"
	fi

	# We dont' care about "no vlan filtering AND no vlan id" as well as "vlan filtering AND no vlan id"

	mtu=$(echo "${domain_xml}" | xmlstarlet sel -t -m "//metadata/ffho/interface[@name='${iface}']" -v 'mtu/@size'  --nl || true)
	if [ "${mtu}" ]; then
		ip link set mtu "${mtu}" dev "${iface}"
		logger -t "${my_name}" "Setting MTU of ${iface} to ${mtu}."
	fi

	# If there is an configuration stanza in /etc/network/interfaces
	# for this interfaces, we try to get it up and running. Proceed
	# with fingers crossed.
	if grep -q "^iface\s\+${iface}" /etc/network/interfaces; then
		if ifup $iface; then
			logger -t "${my_name}" "ifup'ed ${iface}."
		else
			logger -t "${my_name}" -p user.error "ifup ${iface} FAILED."
		fi
	fi
done