#!/bin/sh -e # # Libvirt qemu hook to magically connect VM bridge interfaces to desired untagged VLAN and set MTU # # 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 for more details about vlan aware bridges. # # # It's also possible to set the MTU of a VM interface to a specific value. # As the attribute on interfaces does sadly not work when not uing a # libvirt and any given value is silently discared in the # current version we are running, we work around that by using : # # # # 1610 # # # # Maximilian Wilhelm # -- 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. 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}" = 1 -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 VLAN ${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}" = 0 -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 -N ffho="https://ffho.net/libvirt/" -t -m "//ffho:net/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 vids=$(/etc/libvirt/hooks/get-bridge-vids "${iface}") if [ "${vids}" ]; then bridge vlan del vid 1 dev "${iface}" for vid in ${vids}; do bridge vlan add vid ${vid} dev "${iface}" done logger -t "${my_name}" "Configured tagged VLANs ${vids} for ${iface} in bridge ${bridge}." fi fi done