You can safely assign the same IP subnet to two different libvirt bridges by isolating them at Layer 2. Libvirt handles this automatically because each bridge creates a distinct virtual network namespace on the host, preventing ARP conflicts and ensuring traffic never crosses between the subnets unless explicitly routed.
Understanding Network Isolation in Libvirt
When you define multiple bridges in libvirt, such as br0 and br1, the system does not merge them into a single broadcast domain. Instead, it treats each bridge as an independent switch. This architectural separation is critical for avoiding IP conflicts. If both bridges use the 192.168.100.0/24 range, a VM connected to br0 will only see MAC addresses on that specific bridge. It will never receive ARP requests from VMs on br1, even though the IP ranges are identical.
This isolation allows administrators to replicate production environments locally or test network segmentation without complex routing configurations. However, care must be taken with DHCP servers. Each bridge typically runs its own dnsmasq instance managed by libvirt. You must ensure that the DHCP ranges defined in the XML configuration do not overlap within their respective networks, although they can overlap across different bridges since the scopes are isolated.
Configuring Distinct Bridges with Identical Subnets
To set up this scenario, you need to create two separate network definitions in libvirt. We will use the command-line tool virsh to define these networks. The key is to specify unique names for the networks while assigning the same IP address pool to both.
- Create an XML file for the first bridge, named
net-isolated-a.xml. - Define the second bridge in
net-isolated-b.xml. - Load both configurations into libvirt.
- Verify that the bridges are active and isolated.
Step 1: Define the First Network
Start by creating the XML definition for the first bridge. Save the following content to net-isolated-a.xml. Note the forward mode='bridge' which attaches this virtual bridge to a physical interface or leaves it internal depending on your setup. For pure isolation testing, we often use none or a physical device.
<network>
<name>isolated-a</name>
<forward mode='bridge'/>
<bridge name='br-a' stp='on' delay='0' />
<ip address='192.168.100.1' netmask='255.255.255.0'>
<dhcp>
<range start='192.168.100.10' end='192.168.100.50' />
</dhcp>
</ip>
</network>
In this configuration, the bridge is named br-a on the host. The IP gateway is 192.168.100.1, and the DHCP server hands out addresses from .10 to .50. This defines the scope for the first isolated environment.
Step 2: Define the Second Network
Create a second XML file, net-isolated-b.xml, with nearly identical settings but a unique network name and bridge name. This ensures libvirt creates a separate Linux bridge device.
<network>
<name>isolated-b</name>
<forward mode='bridge'/>
<bridge name='br-b' stp='on' delay='0' />
<ip address='192.168.100.1' netmask='255.255.255.0'>
<dhcp>
<range start='192.168.100.10' end='192.168.100.50' />
</dhcp>
</ip>
</network>
Notice that the IP address and DHCP range are exactly the same as the first network. Because the bridge names (br-a vs br-b) are different, libvirt will create two distinct interfaces on the host. There is no conflict because the kernel routes packets based on the incoming interface.
Step 3: Load and Activate Networks
Use virsh to define and start both networks. Run the following commands sequentially:
virsh net-define net-isolated-a.xml
virsh net-start isolated-a
virsh net-define net-isolated-b.xml
virsh net-start isolated-b
You can verify the status using virsh net-list --all. Both networks should show as running. Check the host interfaces with ip addr show to confirm that both br-a and br-b have the IP 192.168.100.1 assigned. This output confirms that the host successfully manages duplicate subnets on different interfaces.
Connecting Virtual Machines
Now that the networks are established, attach your virtual machines to the appropriate bridge. When defining a VM, specify the network name in the XML or via the GUI.
<interface type='network'>
<source network='isolated-a' />
<model type='virtio' />
</interface>
For a VM in the second environment, change the source network to isolated-b. Once the VMs are booted, they will obtain IPs from the same range (e.g., 192.168.100.10). However, a ping from a VM on isolated-a to 192.168.100.10 on isolated-b will fail. This is expected behavior. The packets are dropped because there is no route between the two bridges, and no ARP response comes back from the other side.
Troubleshooting Common Issues
If you encounter connectivity issues, check the following areas. First, ensure that iptables or nftables rules on the host are not blocking forwarding between the bridges if you intend for them to communicate later. By default, libvirt sets up NAT or direct bridge rules that keep them isolated.
Second, verify that the DHCP leases are not conflicting. Since the scopes are isolated, this is rarely an issue, but if you manually assign static IPs, ensure no two VMs on the *same* bridge share an IP. Duplicate IPs on the same bridge will cause ARP flapping and connectivity loss.
Finally, if you need to route traffic between these two isolated networks, you must enable IP forwarding on the host and add static routes. This transforms the setup from isolated segments to a routed topology. Without explicit routing rules, the duplicate subnets remain perfectly safe and independent.
Frequently Asked Questions
Can I route traffic between two bridges with the same subnet?
No, standard IP routing protocols cannot handle two identical subnets directly on the same router interface stack without policy-based routing. You would need to use NAT or change one of the subnets to avoid ambiguity. Direct layer 3 routing requires unique prefixes for each interface.
Why do my VMs on different bridges get the same IP address?
This happens because each libvirt bridge runs its own independent dnsmasq DHCP server. Since the networks are isolated at layer 2, the DHCP servers do not coordinate. They independently assign the next available address from their configured range, resulting in identical IPs across different virtual switches.
How do I check if the bridges are actually isolated?
Ping an IP address on the other bridge from a VM. If the connection times out or fails with "Destination Host Unreachable," the isolation is working correctly. You can also use tcpdump -i br-a on the host to verify that traffic from VMs on br-b does not appear on br-a.
Does this setup consume more memory or CPU?
The overhead is minimal. Each bridge adds a small amount of kernel memory for the network namespace and socket buffers. The CPU impact is negligible unless you are moving high-throughput traffic through the host’s software bridge implementation. For heavy loads, consider bonding or offloading to hardware.
What happens if I delete one of the networks?
Deleting a network removes its bridge interface and DHCP configuration. VMs attached to that network will lose connectivity immediately. Their IP addresses become invalid, and they will not automatically reconnect to the remaining bridge. You must reconfigure the VMs to use a different network source.