I’ve not really used qemu much before but I found myself needing it last week for a particular project and discovered - to my shock and amazement - that the documentation (official and 3rd party) surrounding it is almost universally terrible. A mixture of overly-complex, out of date, incredibly niche, and just straight up poorly-written.
I’ll be honest, I wasn’t after much, I had a single qemu VM and I wanted it to be routable on my LAN. For various reasons, PCI pass-through wasn’t an option (not that the docs there are any better) but I did have a NIC that wasn’t being used for anything else that I was hoping could be put to use here.
As Simple As Possible
It took an afternoon of screwing around but I got everything I wanted working and ultimately it was extremely simple - far simpler than all the man pages and blogs I read would suggest.
First some prep, edit
/etc/qemu/bridge.conf and add the line
virtbr0 is an arbitrary name for the virtual bridge you’re going to use. Call it whatever you want.
chmod +s /usr/lib/qemu/qemu-bridge-helper. This allows a non-admin user to make use of the qemu bridge so we can run our VM unprivileged.
You’ll also want to disable DHCP on the NIC you’re going to use here - or unassign the IP if it’s static - because you’ll be using the bridge IP instead. You don’t have to, but it’s a waste of an address if you don’t.
Now the network bridge. First install
bridge-utils (Debian/Ubuntu), and then:
sudo brctl addbr virtbr0 sudo brctl addif virtbr0 enp3s0 sudo ip addr add 192.168.0.20/24 dev virtbr0 sudo ip link set virtbr0 up
In short: create a bridge called
virtbr0, add your spare NIC to it (
enp3s0 in my case), give the bridge an IP address on your LAN, then bring up the bridge interface.
Next, configure iptables/nftables to forward traffic across the bridge network:
sudo iptables -I FORWARD -m physdev --physdev-is-bridged -j ACCEPT
And that’s pretty much it. Configure your VM with
-net nic,model=virtio,macaddr=52:54:00:00:00:01 -net bridge,br=virtbr0 and away you go.
The MAC here is arbitrary,
52:54:00 is the qemu prefix and the rest can be whatever as long as it doesn’t conflict with an existing virtual NIC on your network. The
br should obviously match whatever you’ve called your network bridge.
Persistence Is The Key
This is great but a host reboot will wipe it all out again. To persist the config with systemd, you can create a service in
/lib/systemd/system/, I called mine
qemu-startup.service, and do something like:
[Unit] Description=Setup qemu network bridging After=network-online.target [Service] Type=oneshot Restart=on-failure ExecStart=brctl addbr virtbr0 ExecStart=brctl addif virtbr0 enp3s0 ExecStart=ip addr add 192.168.0.20/24 dev virtbr0 ExecStart=ip link set virtbr0 up ExecStart=iptables -I FORWARD -m physdev --physdev-is-bridged -j ACCEPT [Install] WantedBy=multi-user.target
You can add whichever services and targets you like to the
After statement to control when it tries to do this, in case you’ve got other things you want to start first. If you’re also auto-starting your VMs via systemd just add
qemu-startup.service to its
After statement so it doesn’t try and start before your network config is in place.
Simple (I hope).