Set up a virtual routing bridge (brouter)

Peter Boy, Kevin Fenzi, Brett Weir Version F37-F44 Last review: 2026-05-13

A virtual bridge is a software application that implements a communication network between multiple devices on a server, rather than using dedicated hardware. A 'routing' bridge is a special type of bridge that forwards traffic based on the IP addresses of the devices. This is a bit of a misnomer, as bridges by definition operate based on MAC addresses. This type of bridge is used in specific environments where multiple IP addresses share the same MAC address and the receiving device must handle traffic distribution. Otherwise, the functionality is the same.

Basically there are two ways to set up a virtual bridge.

The virtual (plain) bridge

Typically, the bridge "captures" the physical interface of the server and assigns the server as a secondary device. To attach Virtual Machines, virtual interfaces are added as needed. The bridge operates at layer 2 of the OSI model and uses unique MAC addresses to determine the recipient of a data packet.

The virtual routing bridge

This bridge leaves the host’s interface untouched and instead creates an independent bridge to which VMs are attached. It uses the forwarding capability to forward incoming packets that are not destined for the host to the bridge. The destination of data packets is determined locally to the bridge based on IP addresses and routing tables.

This article deals with the latter variant.

Prerequisites

. Fully updated Fedora Server, any version of F37 or newer. F44 is preferred. . Installed virtualization support according to the Adding Virtualization Support guide. . Completed preparations for installing VMs according to the Provisioning the Server VM image guide. . Set up DNS entries for the projected VMs

Steps to configure a basic routing bridge

  1. Check the forwarding configuration

    $ cat /proc/sys/net/ipv4/ip_forward
    $ cat /proc/sys/net/ipv6/conf/default/forwarding

    In both cases, check that the command returns a value of 1. Libvirt activates IPv4 forwarding, but does not always activate IPv6 forwarding.

    $ echo 1 | sudo tee /proc/sys/net/ipv4/ip_forward
    $ echo 1 | sudo tee /proc/sys/net/ipv6/conf/all/forwarding

    To make these changes permanent, create or edit the file at /etc/sysctl.d/50-enable-forwarding.conf by using your preferred editor running under sudo:

    # local customizations
    #
    # enable forwarding for dual stack
    net.ipv4.ip_forward=1
    net.ipv6.conf.all.forwarding=1
  2. Check the existing interfaces

    $ ip a
    1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
     ...
    2: enp2s0: <BROADCAST,MULTICAST,UP,LOWER_UP> ...  state UP group default qlen 1000
        inet 148.251.152.29/32 scope global noprefixroute enp2s0
          ...
        inet6 2a01:4f8:210:512d::2/64 scope global noprefixroute
          ...
    3: virbr0: <BROADCAST,MULTICAST,UP,LOWER_UP> ...  state UP group default qlen 1000
          ...
  3. Adjust the IPv6 subnet

    As the IP address listing indicates, the external IPv6 subnet is the common full /64 network. Change the subnet in the IP configuration to trigger IPv6 forwarding. Use the original IP address from the IP address listing, but change the /64 to /128 when you modify the connection, as follows.

    $ sudo nmcli con mod enp2s0 ipv6.addresses '2a01:4f8:210:512d::2/128'
    $ sudo nmcli con up enp2s0

In the example, the connection name matches the device name. You might need to check the network connection name that the device is attached to, when you run the nmcli con commands. For example, run: nmcli con|grep nmcli enp2s0 to get the names of the network connections that the enp2s0 device is connected to.

  1. Create a routing bridge

    The (public) bridge is named vbr1s0, based on the name of the accompanying (public) interface. The IP addresses are the same, but with a different subnet range to trigger forwarding.

    In the listing of interfaces, the IPv4 address is a point-to-point connection. Therefore, the bridge uses a subnet, if any, the range that is also assigned in DNS. If the IPv4 interface is also created as a subnet, the bridge is created as a p2p connection instead.

    $ sudo nmcli con add  con-name vbr1s0  ifname vbr1s0  type bridge  stp off \
         ipv4.method manual  ipv4.addresses '148.251.152.29/27'  \
         ipv6.method manual ipv6.addresses '2a01:4f8:210:512d::2/64' ipv6.addr-gen-mode eui64

    Do not specify a zone for the connection so that the bridge is assigned to the default zone, FedoraServer, to which the Ethernet interface also belongs by default. Ensuring that the connection belongs to the default zone is very important for the firewall permissions.

    Finally, for IPv4, create the routes and the public addresses of all VMs

    $ sudo nmcli con mod vbr2s0 +ipv4.routes "148.251.152.49/32"
    $ sudo nmcli con mod vbr2s0 +ipv4.routes "148.251.152.52/32"
    $ sudo nmcli con mod vbr2s0 +ipv4.routes "148.251.152.56/32"
  2. Double check your entries, especially the IP addresses, to avoid incorrect configuration and time-consuming troubleshooting.

  3. Activate the routing bridge

    $ sudo nmcli con up vbr1s0
  4. Install a VM

    Use Cockpit or the command line

    $ sudo cp /var/lib/libvirt/boot/Fedora-Server-KVM-37-custom.qcow2 /var/lib/libvirt/images/vm-01.qcow2
    $ sudo virt-install --name vm-01 --memory 4096 --cpu host --vcpus 4 --graphics none \
    --os-variant fedora37 --import  --disk /var/lib/libvirt/images/vm-01.qcow2,format=qcow2,bus=virtio \
    --network bridge=vbr1s0,model=virtio --network bridge=virbr0,model=virtio

    Complete the First Boot Screen. Leave the network configuration as it is. It is easier to configure it after the first login.

  5. Login to the VM and configure the public interface

    $ sudo nmcli con mod 'Wired connection 1' ipv4.method manual ipv4.addresses '148.251.152.49/32' \
         ipv4.gateway '148.251.152.29'  ipv4.dns '213.133.98.98' ipv6.method 'manual' \
         ipv6.addresses '2a01:4f8:210:512d::10/64' ipv6.gateway '2a01:4f8:210:512d::2'  connection.id enp1s0
    $ sudo nmcli con up  enp1s0
  6. If it exists, adjust the internal interface.

    $ sudo nmcli con mod 'Wired connection 2'  ipv4.method auto ipv6.method disabled connection.zone 'internal' connection.id enp2s0
    $ sudo nmcli con up  enp2s0
  7. Optionally reboot to reinitialize everything

    $ sudo reboot

Test the configuration

  1. Check the forwarding configuration

    $ cat /proc/sys/net/ipv4/ip_forward
    $ cat /proc/sys/net/ipv6/conf/default/forwarding

    In both cases, check that the command returns a value of 1.

  2. Check the host configuration Ensure that SELinux is in enforcing mode and firewalld active with zone FedoraServer with both the external interface and the virtual bridge attached.

    $ getenforce
    $ sudo firewall-cmd  --list-all
    $ sudo firewall-cmd  --get-active-zones
  3. Check IPv6

    (a) ping6  external desktop → host (using ipv6 notation)
    (b) ping6  host → VM
    (c) ping6 external desktop → VM
    (d) ssh external desktop → VM
    (e) traceroute6 external desktop → host
    (f) traceroute6 external desktop → VM
  4. Check IPv4

    (a) ping  external desktop → host (using ipv4 notation)
    (b) ping  host → VM
    (c) ping external desktop → VM
    (d) ssh external desktop → VM
    (e) traceroute external desktop → host
    (f) traceroute external desktop → VM