Bridged networking with KVM

When using a bridge, the vm’s shares the physical nic of the host. This way virtual machines can receive an ip in my lan with dhcp. I prefer my vm’s to have a LAN address, instead of the default nat networking.

In addition, when running docker on the same machine, the br_netfilter enabled by Docker, will break the default nat networking used by virsh.

Prerequisites

I’m using Ubuntu 24.04 and this might work on most distro’s using systemd. Check if bridge-utils is installed, otherwise: apt install bridge-utils

Configure the bridge

Before using the bridge, i only configured the enp2s0 interface with fixed ip 192.168.0.10. When using the bridge, this ip wil bind to the bridge, and be removed from the Interface enp2s0.
Backup your netplan setting and create a new configuration in /etc/netplan/bridge.yaml. Make sure to adjust the interface names and ip according your setup.

!!! errors in netplan configuration may make you lose access: Be very careful!

 1  network:
 2  version: 2
 3  ethernets:
 4    eno1:
 5      dhcp4: true
 6      activation-mode: off # i do not use the second nic
 7    enp2s0:
 8     dhcp4: true
 9  bridges:
10    br0:
11      dhcp4: false
12      addresses:
13      - 192.168.0.10/24
14      nameservers:
15        addresses:
16        - 127.0.0.1
17        - 1.1.1.1
18      gateway4: 192.168.0.1
19      interfaces:
20        - enp2s0

Apply the configuration: netplan apply

If you still have connection, you should be able to examine the bridge.

$ brctl show br0
bridge name     bridge id               STP enabled     interfaces
br0             8000.02973c1d2848       no              enp2s0

Configure the virtual network

To let the vm’s utilise the bridge, we create a new network configuration. Adjust the variables if your bridge is named different. Since im using the default network, it probaly exist already and might need to be undifened with virsh undefine default.

You can also choose for an uniq network name and let the default as is. In that case make sure you assign the prefered network when creating a new vm.

Create the virsh network xml template with command below:

# set variables: 
BR=br0
NET_NAME=default

# create template:
echo "<network>
  <name>$NET_NAME</name>
  <forward mode=\"bridge\"/>
  <bridge name=\"$BR\"/>
</network>" > $BR.xml 

Create the network based on template made in previous step, start the network and enable auto start.

virsh net-define $BR.xml
virsh net-start $NET_NAME
virsh net-autostart $NET_NAME

Show result: virsh net-list --all

Disable netfilters for bridges.

To bypass the Docker bridge netfilter code, we disable netfilter for bridges by creating this file in /etc/sysctl.d/netfilter-bridge.conf. Activate the setting with sysctl -p /etc/sysctl.d/netfilter-bridge.conf

net.bridge.bridge-nf-call-ip6tables=0
net.bridge.bridge-nf-call-iptables=0
net.bridge.bridge-nf-call-arptables=0

Allow forwarding for bridges

This step is optional. It is only necessary if your vm’s doesnt get on dhcp ip.

If your vm’s wont get a dhcp ip, it might be that iptables are still not forwarding. Use the following command to allow traffic forwarding to the bridge:

iptables -I FORWARD -m physdev --physdev-is-bridged -j ACCEPT

Waiting for networkd service on boot

Since a new interface is added, the bridge, it might happen that systemd service systemd-networkd-wait-online.service will wait on boot for all nic' s to be connected. This will not happen and makes boot times needlessly long. To prevent create this file in /etc/systemd/system/systemd-networkd-wait-online.service.d/override.conf with the following content.

[Service]
ExecStart=
ExecStart=/usr/lib/systemd/systemd-networkd-wait-online --any

Read more

Some very clear and interesting explanation is written on this great libvirt Networking Handbook], if you like to investiage other networking options with libvirt.