Skip to content

Instantly share code, notes, and snippets.

@plembo
Last active January 22, 2025 15:41
Show Gist options
  • Save plembo/f7abd2d9b6f76e7afdece02dae7e5097 to your computer and use it in GitHub Desktop.
Save plembo/f7abd2d9b6f76e7afdece02dae7e5097 to your computer and use it in GitHub Desktop.
Add br0 to Ubuntu desktop using nmcli

Add a bridge interface to Ubuntu desktop using nmcli

Had to do this for some advanced networking with KVM, and couldn't figure out how to do it using the Nework Manager gui. Did find an article later that showed how to do it with nmtui, but it's so much easier to record what you did when using the cli.

In the examples below "eth0" is the name of my physical interface. By default on Ubuntu and most distributions that will almost certainly be different, for example: "eno1", "ens1", or "enp2s0".

To see what everything looks like before starting:

$ nmcli con show

I renamed "Wired Connection 1' to the name of my physical interface:

$ sudo nmcli con mod 'Wired Connection 1' con-name eth0

So let's start out by creating the bridge itself:

$ nmcli con add ifname br0 type bridge con-name br0

Now add the physical interface as its slave:

$ nmcli con add type bridge-slave ifname eth0 master br0

Disable STP:

$ nmcli con mod br0 bridge.stp no

Now down the physical interface:

$ nmcli con down eth0

For this machine I want a static address:

$ nmcli con mod br0 ipv4.addresses 10.1.1.16/24
$ nmcli con mod br0 ipv4.gateway 10.1.1.1
$ nmcli con mod br0 ipv4.dns '10.1.1.1,8.8.8.8,8.8.4.4'

Don't forget to set your search domain:

$ nmcli con mod br0 ipv4.dns-search 'example.com'

Then tell Network Manager this will be a manual connection:

$ nmcli con mod br0 ipv4.method manual

Finally, bring up the new bridge interface:

$ nmcli con up br0

Run nmcli device show to confirm your changes, and then restart NetworkManager (sudo systemctl restart NetworkManager.service) to make sure the configuration sticks.

@b3to
Copy link

b3to commented Apr 21, 2024

hi, im trying to follow your steps to create the bridge and everything seems ok until i try to turn the br0 to up. i get this error message:
"Error: Connection activation failed: Connection 'br0' is not available on device br0 because device is strictly unmanaged" i have googled a bit of course and tried a few things but still cannot bring it up , what am i missing ? any suggestions ? thanks

PRETTY_NAME="Ubuntu 22.04.4 LTS"
NAME="Ubuntu"
VERSION_ID="22.04"
VERSION="22.04.4 LTS (Jammy Jellyfish)"
VERSION_CODENAME=jammy
ID=ubuntu
ID_LIKE=debian
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
UBUNTU_CODENAME=jammy

thanks,

@plembo
Copy link
Author

plembo commented Apr 21, 2024

Can you provide the output of ip addr, nmcli con show and nmcli con show br0 ?

@SeersantLoom
Copy link

SeersantLoom commented Apr 22, 2024

hi, im trying to follow your steps to create the bridge and everything seems ok until i try to turn the br0 to up. i get this error message: "Error: Connection activation failed: Connection 'br0' is not available on device br0 because device is strictly unmanaged" i have googled a bit of course and tried a few things but still cannot bring it up , what am i missing ? any suggestions ? thanks

Google actually knows the answer to your problem but finding it depends... Switching over to network-manager needs a bit of work before it can be used. I'll try to summarize the steps needed:

  • install network-manager ( apt install network-manager )
  • remove netplan that comes with Ubuntu by default. Do apt search netplan , then do apt purge to all those that come up as installed ( along the lines of apt purge netplan.io libnetplan0 )
  • apt autoremove to get rid of remaining orphaned stuff
  • edit /etc/NetworkManager/NetworkManager.conf file. There's [ifupdown] section, set managed=true
  • do touch /etc/NetworkManager/conf.d/10-globally-managed-devices.conf , this creates empty file and overrides settings in the same named file in /usr/lib/NetworkManager/conf.d directory
  • check if /etc/network/interfaces file is empty/comments only
  • (re)start network-manager ( systemctl start NetworkManager ), see if nmcli behaves any different
  • it may require restart (and prayer) that there's nothing else hindering nmcli now.

@plembo
Copy link
Author

plembo commented Apr 22, 2024

I haven't tried removing netplan from Desktop before (on Desktop it ships configured to give Network Manager control over all network devices, while it gives control to systemd-networkd on Server). I'd be interested to hear if removing netplan entirely cleaned up the "strictly unmanaged" error b3to is experiencing.

@nirs
Copy link

nirs commented Nov 24, 2024

This steps almost work on Fedora 39 and 41. Typically after enabling br0, bridge-slave-{iface} is up, but br0 is not. ip a show that the iface is down and br0 does not get an ip address.

Reloading seem to fix this issue:

nmcli general reload

@Arcitec
Copy link

Arcitec commented Dec 16, 2024

Fedora 41+ Ethernet Bridge Instructions:

I wrote this after referring to Red Hat documentation and about 5 other guides, including yours, to achieve the most correct procedure.

# See the name of your ethernet interface:
# NOTE: Look for "Wired connection 1" and look at its "DEVICE" column's name.
# NOTE: "eno1" is the most common, but it may be different on your distro. Either
# way, on modern distros, ethernet device names are persistent, so it will always
# refer to the correct device after every reboot too.
nmcli con show
nmcli device status

# First create the "bridge" network device, which is just a virtual (in-kernel)
# device that can have multiple slaves to "bridge them all together", kind of
# like a virtual "networking switch".
# NOTE: Some people force "con-name br0" to make the "connection name"
# match the created device name ("br0"), but that's just confusing.
nmcli con add type bridge ifname br0

# Now add the physical interface as its slave, meaning that whenever the bridge
# wants to reach the outside world, it will use the real device for routing.
nmcli con add type bridge-slave ifname eno1 master br0

# Take down the physical auto-created ethernet connection.
# NOTE: NetworkManager auto-generated that "Wired connection 1" at startup,
# which it does whenever a device doesn't have any manual configuration.
# NOTE: Perhaps it can have a different name on your device. Refer to "con show".
nmcli con down "Wired connection 1"

# Bring up the virtual bridge device's network connection.
# NOTE: This will make it try to assign an IP to itself, which it will achieve
# via the slave-route through the normal network adapter.
nmcli con up bridge-br0

# Look at the status to see what's going on.
# NOTE: You expect to see this while it's connecting:
#  DEVICE  TYPE      STATE                                  CONNECTION
#  eno1    ethernet  connected                              bridge-slave-eno1 
#  br0     bridge    connecting (getting IP configuration)  bridge-br0
# And usually less than 30 seconds later you should see a connection:
#  br0     bridge    connected               bridge-br0
nmcli device status

# Now look at the internet devices.
# NOTE: You should see "eno1" without any IP anymore, and "br0" should have
# the exact same MAC Address and IP as the ethernet device used to have, which
# means that the ethernet device is now a slave port for the bridge which allows
# the bridge to reach your router and receive an IP via DHCP.
# NOTE: If you want a static IP, you should configure your real router to give
# yourself a static IP for your device's ethernet MAC Address.
ip a

# If you only want to see details for the bridge device, use this instead.
ip a s br0

# You can look at the properties of the bridge.
# NOTE: "-f bridge" tells it to only show the bridge properties, so remove that
# if you want to see all connection parameters for the bridge device.
# NOTE: STP is on by default, which it should be, since it protects against infinite
# loops where packets flow back and forth forever between the bridge and hardware.
nmcli -f bridge con show bridge-br0

# Note that all connection details have been saved persistently now, which means
# that this bridge configuration will be automatically applied after reboots.
ls -l /etc/NetworkManager/system-connections

# All configuration values and connection should immediately be working, but you
# can force a restart of NetworkManager if you have any issues. It might help?
sudo systemctl restart NetworkManager

# If you ever regret and want to get rid of the bridge, you should do this:
nmcli con down bridge-br0
nmcli con delete bridge-slave-eno1
nmcli con delete bridge-br0

# Check that all bridge configs are gone from your persistent config dir:
ls -l /etc/NetworkManager/system-connections

# Bring back the wired ethernet device's connection.
# NOTE: This will fail with "unknown connection" if you have rebooted after
# creating the bridge. Read the next point in that situation.
nmcli con up "Wired connection 1"

# If you have rebooted since creating the bridge, then NetworkManager hasn't
# created the auto-generated "Wired connection 1" anymore, since it saw that
# your ethernet device was part of a bridge. In that case, you must restart
# the NetworkManager service now to make it re-generate the wired connection.
sudo systemctl restart NetworkManager

# Verify that "eno1" has an IP again (only if you've deleted the bridge).
ip a

@Erotemic
Copy link

Erotemic commented Jan 4, 2025

I wrote a variant of the above script where I replace machine-specific names with environment variables in order to make it clear what needs to be changed. I also tried to write code to automagically infer what the variables should be given a default ethernet connection, but that code could be improve to be more robust:

# Show relevant tables, make sure the following code
# infers correct values
sudo nmcli con show --active

# Infer the name of the physical ethernet interface name
ETHERNET_IFACE=$(ip route | grep default | awk '{print $5}')

# Infer the ip of the router gateway
HOST_IP=$(ip route get 1.1.1.1 | grep -oP 'src \K\S+')

# Infer the current IP of the ethernet interface
ROUTER_GATEWAY_IP=$(ip route get 1.1.1.1 | grep -oP 'via \K\S+')

# Infer the nmcli connection name of the ethernet interface
CONN_NAME=$(ETHERNET_IFACE=$ETHERNET_IFACE python -c "if 1:
    # Parse the output of nmcli con show into a table
    import subprocess, re, os
    bs, nl = chr(92), chr(10)
    data = subprocess.check_output('sudo nmcli con show', shell=True).decode('utf8')
    lines = data.rstrip().split(nl)
    header = lines[0]
    positions = [m.start() for m in re.finditer(bs + 'S+', header)] + [len(header)]
    table = [{
        header[idx1:idx2].strip(): line[idx1:idx2].strip()
        for idx1, idx2 in zip(positions[:-1], positions[1:])
    } for line in lines[1:]]
    ethernet_device = os.environ.get('ETHERNET_IFACE')
    found = None
    for row in table:
        if row['DEVICE'] == ethernet_device:
            found = row['NAME']
    if found is not None:
        print(found)
    else:
        print(f'ERROR: NOT FOUND: {ethernet_device!r}')
        import pandas as pd
        print(pd.DataFrame(table).to_string())
")
echo "CONN_NAME = $CONN_NAME"

# Options for making the bridge
BRIDGE_CONN_NAME=br0
BRIDGE_IFNAME=br0
MAIN_DNS=$ROUTER_GATEWAY_IP
BACKUP_DNS1=8.8.8.8
BACKUP_DNS2=8.8.8.4

echo "
Variables:
    ETHERNET_IFACE    = $ETHERNET_IFACE
    HOST_IP           = $HOST_IP
    ROUTER_GATEWAY_IP = $ROUTER_GATEWAY_IP
    CONN_NAME         = $CONN_NAME

    BRIDGE_CONN_NAME=$BRIDGE_CONN_NAME
    BRIDGE_IFNAME=$BRIDGE_IFNAME

    MAIN_DNS=$MAIN_DNS
    BACKUP_DNS1=$BACKUP_DNS1
    BACKUP_DNS2=$BACKUP_DNS2
"

# Create the bridge
nmcli con add ifname "$BRIDGE_IFNAME" type bridge con-name "$BRIDGE_CONN_NAME"
# Not sure if the argument should be an ifname or conn name after master
nmcli con add type bridge-slave ifname "$ETHERNET_IFACE" master "$BRIDGE_IFNAME"

# Disable STP (Spanning Tree Protocol)
nmcli con mod "$BRIDGE_CONN_NAME" bridge.stp no

# Down the physical interface
nmcli con down "$CONN_NAME"

# Setup static IP addresses
nmcli con mod "$BRIDGE_CONN_NAME" ipv4.addresses "$HOST_IP/24"
nmcli con mod "$BRIDGE_CONN_NAME" ipv4.gateway "$ROUTER_GATEWAY_IP"
nmcli con mod "$BRIDGE_CONN_NAME" ipv4.dns "$MAIN_DNS,$BACKUP_DNS1,$BACKUP_DNS2"

# Set the connection to be manual (might want to use DHCP instead?)
nmcli con mod "$BRIDGE_CONN_NAME" ipv4.method manual

# set your search domain (not sure what the right approach is here)
nmcli con mod "$BRIDGE_CONN_NAME" ipv4.dns-search 'example.com'
nmcli con mod "$BRIDGE_CONN_NAME" ipv4.dns-search 'lan'
nmcli con mod "$BRIDGE_CONN_NAME" ipv4.dns-search ''

# Prevent the ethernet iface from automatically creating a new connection
# otherwise, it will reset if the bridge goes down
nmcli con mod "$ETHERNET_IFACE" connection.autoconnect no

# Bring up the new bridge interaface
nmcli con up "$BRIDGE_CONN_NAME"

# Show changes
nmcli device show

# Restart the service
sudo systemctl restart NetworkManager.service

I will edit the above code if anyone points out corrections or how to make things better.

@hlusa
Copy link

hlusa commented Jan 22, 2025

Hi All,
Thanks for this.
My concern is this; I have read many tutorials, they only talk about the virt-manager bridge mode when the host machine is connected to the physical network via Ethernet. In my case, my PC is permanently connected via Wi-Fi. Would there be a possibility to connect the VMs in bridge mode with virt-manager.

Thanks in advance!

@nirs
Copy link

nirs commented Jan 22, 2025

In my case, my PC is permanently connected via Wi-Fi. Would there be a possibility to connect the VMs in bridge mode with virt-manager.

You can try to add the wifi interface instead of wired connection.

nmcli con add type bridge-slave ifname {your-wifi-interface} master br0

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment