Linux Bridge

Suppose we have this setup here with

Pasted image 20260104220931.png

By default each physical interface will be assigned a bridge in the PC. So if i do

root@UbuntuDockerGuest-2:~# ifconfig
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet6 fe80::42:a1ff:fe7e:8800  prefixlen 64  scopeid 0x20<link>
        ether 02:42:a1:7e:88:00  txqueuelen 1000  (Ethernet)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

eth1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet6 fe80::42:a1ff:fe7e:8801  prefixlen 64  scopeid 0x20<link>
        ether 02:42:a1:7e:88:01  txqueuelen 1000  (Ethernet)
        RX packets 7  bytes 566 (566.0 B)
        RX errors 0  dropped 1  overruns 0  frame 0
        TX packets 8  bytes 656 (656.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10<host>
        loop  txqueuelen 1000  (Local Loopback)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

It should show 2 physical bridges.

Now we can create a new bridges using

ip link add name br0 type bridge
ip link set br0 up
root@UbuntuDockerGuest-2:~# ifconfig
br0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet6 fe80::94f4:ddff:fe02:ea23  prefixlen 64  scopeid 0x20<link>
        ether 96:f4:dd:02:ea:23  txqueuelen 1000  (Ethernet)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 8  bytes 704 (704.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet6 fe80::42:a1ff:fe7e:8800  prefixlen 64  scopeid 0x20<link>
        ether 02:42:a1:7e:88:00  txqueuelen 1000  (Ethernet)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

eth1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet6 fe80::42:a1ff:fe7e:8801  prefixlen 64  scopeid 0x20<link>
        ether 02:42:a1:7e:88:01  txqueuelen 1000  (Ethernet)
        RX packets 10  bytes 776 (776.0 B)
        RX errors 0  dropped 1  overruns 0  frame 0
        TX packets 11  bytes 866 (866.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10<host>
        loop  txqueuelen 1000  (Local Loopback)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

From this bridge here, we can assign some ip to it

ip addr add 10.0.0.2/24 dev br0
root@UbuntuDockerGuest-2:~# ifconfig
br0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 10.0.0.2  netmask 255.255.255.0  broadcast 0.0.0.0
        inet6 fe80::94f4:ddff:fe02:ea23  prefixlen 64  scopeid 0x20<link>
        ether 96:f4:dd:02:ea:23  txqueuelen 1000  (Ethernet)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 13  bytes 1054 (1.0 KB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet6 fe80::42:a1ff:fe7e:8800  prefixlen 64  scopeid 0x20<link>
        ether 02:42:a1:7e:88:00  txqueuelen 1000  (Ethernet)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

eth1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet6 fe80::42:a1ff:fe7e:8801  prefixlen 64  scopeid 0x20<link>
        ether 02:42:a1:7e:88:01  txqueuelen 1000  (Ethernet)
        RX packets 11  bytes 846 (846.0 B)
        RX errors 0  dropped 1  overruns 0  frame 0
        TX packets 12  bytes 936 (936.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10<host>
        loop  txqueuelen 1000  (Local Loopback)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

I'll do the same thing for UbuntuDockerGuest-1

root@UbuntuDockerGuest-1:~# ifconfig
br0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 10.0.0.3  netmask 255.255.255.0  broadcast 0.0.0.0
        inet6 fe80::649a:7bff:fe5b:dc7c  prefixlen 64  scopeid 0x20<link>
        ether 66:9a:7b:5b:dc:7c  txqueuelen 1000  (Ethernet)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 10  bytes 844 (844.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet6 fe80::42:68ff:fecb:ac00  prefixlen 64  scopeid 0x20<link>
        ether 02:42:68:cb:ac:00  txqueuelen 1000  (Ethernet)
        RX packets 12  bytes 936 (936.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 13  bytes 1006 (1.0 KB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

eth1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet6 fe80::42:68ff:fecb:ac01  prefixlen 64  scopeid 0x20<link>
        ether 02:42:68:cb:ac:01  txqueuelen 1000  (Ethernet)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10<host>
        loop  txqueuelen 1000  (Local Loopback)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

However, if we do a ping from PC1 to PC2 it will go dead-end. Since this br0 does not map to any physical interface

root@UbuntuDockerGuest-1:~# ping 10.0.0.2
PING 10.0.0.2 (10.0.0.2) 56(84) bytes of data.
^C
--- 10.0.0.2 ping statistics ---
2 packets transmitted, 0 received, 100% packet loss, time 1043ms

We now need to bind br0 to a physical interface

root@UbuntuDockerGuest-1:~# ip link set eth0 master br0
root@UbuntuDockerGuest-2:~# ip link set eth1 master br0

This is because currently, Guest-1 is connecting using eth0 and Guest-2 is connecting using eth1. We confirm again:

root@UbuntuDockerGuest-1:~# ip link show master br0
21: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel master br0 state UNKNOWN mode DEFAULT group default qlen 1000
    link/ether 02:42:68:cb:ac:00 brd ff:ff:ff:ff:ff:ff

Now our ping would work

root@UbuntuDockerGuest-1:~# ping 10.0.0.2
PING 10.0.0.2 (10.0.0.2) 56(84) bytes of data.
64 bytes from 10.0.0.2: icmp_seq=1 ttl=64 time=0.626 ms
64 bytes from 10.0.0.2: icmp_seq=2 ttl=64 time=0.456 ms
root@UbuntuDockerGuest-2:~# ping 10.0.0.3
PING 10.0.0.3 (10.0.0.3) 56(84) bytes of data.
64 bytes from 10.0.0.3: icmp_seq=1 ttl=64 time=0.299 ms
64 bytes from 10.0.0.3: icmp_seq=2 ttl=64 time=0.521 ms
^C
--- 10.0.0.3 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1050ms
rtt min/avg/max/mdev = 0.299/0.410/0.521/0.111 ms