Below I list a launch script which creates the MacVATP tun file as root and
then drops privileges to the qemu
user. This user thus needs access to the
image file passed as first parameter. This is /var/vms/chimera.img
in the
example systemd unit. Also this launch script makes QEMU create unix sockets
for the monitor and console streams. This way socat
can be used to access
e.g. the guest console with the following command:
socat STDIO,cfmakeraw UNIX:/var/vms/chimera.img.console.sock
The launch script is saved in /usr/local/bin/launch_vm
:
#!/usr/bin/env zsh
# Note currently the image is assumed to be of raw format
#
# Useful options to add:
# -cdrom foo.iso \
# -nic user,model=virtio-net-pci,hostfwd=tcp::2222-:22 \
if [ "$#" -ne 3 ]; then
echo "Usage: $0 <path_to_image> <mac_address> <lower_netdev>"
exit 1
fi
img="$1"
mac_address="$2"
lower_netdev="$3"
tap_name="${img:t:r}_macvtap"
user="qemu"
img_format="raw"
ovmf_vars="${img}_OVMF_VARS.fd"
ip link add link "${lower_netdev}" name "${tap_name}" address "${mac_address}" type macvtap mode bridge
ip link set "${tap_name}" up
tap_index=$(< /sys/class/net/$tap_name/ifindex)
tap_dev=/dev/tap"$tap_index"
tap_mac=$(< /sys/class/net/$tap_name/address)
exec {tap_fd}<>"$tap_dev"
if [ ! -f "${ovmf_vars}" ]; then
cp /usr/share/edk2/x64/OVMF_VARS.4m.fd "${ovmf_vars}"
chown "${user}:${user}" "${ovmf_vars}"
fi
echo "Tap Dev: ${tap_dev}"
echo "Mac Address: ${tap_mac}"
echo "Image: ${img}"
echo "OVMF Vars: ${ovmf_vars}"
echo "lower netdev: ${lower_netdev}"
# Drop privileges before starting QEMU
EUID=0 USERNAME="${user}"
qemu-system-x86_64 -machine q35 \
-boot menu=on \
-drive if=pflash,format=raw,readonly=on,file=/usr/share/edk2/x64/OVMF_CODE.4m.fd \
-drive if=pflash,format=raw,file="${ovmf_vars}" \
-bios /usr/share/edk2/x64/OVMF_CODE.4m.fd \
-boot menu=on \
-enable-kvm -smp 4 -m 4G \
-drive file="${img}",format="${img_format}",if=virtio \
-netdev tap,fd="${tap_fd}",id=hostnet0,vhost=on -device virtio-net-pci,netdev=hostnet0,id=net0,mac=$tap_mac \
-device virtio-serial \
-display none \
-monitor unix:"${img}."monitor.sock,server,nowait \
-serial unix:"${img}."console.sock,server,nowait \
Note: In the future I'd love to use systemd-networkd
's MacVTAP support.
A basic version of that can be found in an earlier revision but this requires
more configuration files. Sadly I haven't found a way to get the MacVTAP fd
directly from the systemd unit file e.g. via OpenFile=
because the exact
/dev/tapXY
to be opened is dynamic. In the above launch script this
information is gathered from sysfs but I haven't found a way to do this
directly in a unit file.
A matching systemd unit file to start a VM using the above script with the
example /var/vms/chimera.img
disk image is listed below. This uses the lower
netdev's systemd-networkd
device as After=
and Wants=
to ensure ordering.
See later for the minimal configuration.
The MacVTAP is deleted on stopping the VM using ExecStopPost=
.
Saved in /etc/systemd/system/chimera_vm.service
[Unit]
Description=Chimera Linux VM
After=sys-subsystem-net-devices-enp2s0.device
Wants=sys-subsystem-net-devices-enp2s0.device
[Service]
Type=simple
ExecStart=/usr/local/bin/launch_vm /var/vms/chimera.img d2:f3:32:c9:83:93 enp2s0
ExecStopPost=ip link del chimera_macvtap
[Install]
WantedBy=multi-user.target
In my network configuration the enp2s0
interface is a secondary
port on my NAS and not used for host networking. This removes the
need for the switch to support hairpin mode for the host to talk
to the guests.
Saved in /etc/systemd/network/30-virttap-lower.network
[Match]
Name=enp2s0
[Network]
DHCP=no