qemu-hook 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899
  1. #!/bin/sh -e
  2. #
  3. # Libvirt qemu hook to magically connect VM bridge interfaces to desired untagged VLAN and set MTU
  4. #
  5. # IF
  6. # * the VM interface if configured to be connected to a bridge,
  7. # * the bridge is configured to be vlan aware (vlan_filtering = 1)
  8. # * the VM interface name ends with _vXXXX with XXXX being a one to four
  9. # digit number
  10. # the interface will be configured as an untagged port connected to VLAN XXXX.
  11. #
  12. # See <man bridge> for more details about vlan aware bridges.
  13. #
  14. #
  15. # It's also possible to set the MTU of a VM interface to a specific value.
  16. # As the <mtu/> attribute on interfaces does sadly not work when not uing a
  17. # libvirt <network/> and any given <mtu/> value is silently discared in the
  18. # current version we are running, we work around that by using <metadata/>:
  19. #
  20. # <metadata>
  21. # <ffho:mtu xmlns:ffho="http://ffho.net/libvirt/">
  22. # <interface name="gw02_v1205">1610</interface>
  23. # </ffho:mtu>
  24. # </metadata>
  25. #
  26. # Maximilian Wilhelm <max@sdn.clinic>
  27. # -- Wed, 31 Aug 2016 20:48:02 +0200
  28. my_name="qemu-magic"
  29. # We only care for the "started" event.
  30. if [ "$2" != 'started' ]; then
  31. exit 0
  32. fi
  33. if ! which xmlstarlet >/dev/null 2>/dev/null; then
  34. logger -t "${my_name}" "ERROR: xmlstarlet not found. Dying of shame."
  35. echo "${my_name}: ERROR: xmlstarlet not found. Dying of shame." >&2
  36. exit 1
  37. fi
  38. # Save domain XML which was given to us on stdin, so we can poke around in it.
  39. export domain_xml="$(cat)"
  40. echo "${domain_xml}" | xmlstarlet sel -t -m '//interface[@type="bridge"]' -v 'concat(target/@dev, " ", source/@bridge)' --nl | while read iface bridge; do
  41. if [ ! -d "/sys/class/net/${bridge}/bridge" ]; then
  42. logger -t "${my_name}" "Bridge \"${bridge}\" for iface \"${iface}\" doesn't exist or isn't a bridge."
  43. exit 2
  44. fi
  45. #
  46. # Check if this kernel supports vlan-aware bridges and if ${bridge} is one
  47. vlan_filtering=0
  48. if [ -f "/sys/class/net/${bridge}/bridge/vlan_filtering" ]; then
  49. vlan_filtering=$(cat "/sys/class/net/${bridge}/bridge/vlan_filtering")
  50. fi
  51. # If the interface is named *_vXXXX, with XXXX being a 1-4 digit number
  52. # we assume that this iface should be connected to Vlan XXXX with
  53. # an untagged port.
  54. vlan_id=$(echo ${iface} | grep -o '_v[0-9]\{1,4\}$' | cut -c3-)
  55. # If vlan filtering is activated and we found a vlan id, kindly do the needful.
  56. if [ "${vlan_filtering}" = 1 -a "${vlan_id}" ]; then
  57. # Remove association with vlan 1 and add association with
  58. # vlan $vlan_id with packages being sent out untagged and
  59. # untagged ingress packets get tagged accordingly.
  60. bridge vlan del vid 1 dev "${iface}"
  61. bridge vlan add vid "${vlan_id}" dev "${iface}" pvid untagged
  62. logger -t "${my_name}" "Configured untagged VLAN ${vlan_id} for ${iface} in bridge ${bridge}."
  63. # If vlan filtering isn't activated or supported but we found a vlan id,
  64. # this probably is an error!
  65. elif [ "${vlan_filtering}" = 0 -a "${vlan_id}" ]; then
  66. 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!"
  67. fi
  68. # We dont' care about "no vlan filtering AND no vlan id" as well as "vlan filtering AND no vlan id"
  69. mtu=$(echo "${domain_xml}" | xmlstarlet sel -N ffho="https://ffho.net/libvirt/" -t -m "//ffho:net/interface[@name='${iface}']" -v 'mtu/@size' --nl || true)
  70. if [ "${mtu}" ]; then
  71. ip link set mtu "${mtu}" dev "${iface}"
  72. logger -t "${my_name}" "Setting MTU of ${iface} to ${mtu}."
  73. fi
  74. # If there is an configuration stanza in /etc/network/interfaces
  75. # for this interfaces, we try to get it up and running. Proceed
  76. # with fingers crossed.
  77. if grep -q "^iface\s\+${iface}" /etc/network/interfaces; then
  78. vids=$(/etc/libvirt/hooks/get-bridge-vids "${iface}")
  79. if [ "${vids}" ]; then
  80. bridge vlan del vid 1 dev "${iface}"
  81. for vid in ${vids}; do
  82. bridge vlan add vid ${vid} dev "${iface}"
  83. done
  84. logger -t "${my_name}" "Configured tagged VLANs ${vids} for ${iface} in bridge ${bridge}."
  85. fi
  86. fi
  87. done