What a bridge is
A bridge behaves like an L2 switch: it accepts [[ethernet-frame|Ethernet frames]] on one port, reads the dst-MAC, looks it up in the FDB (forwarding DB), and sends the frame to the right port. If the MAC is unknown, it floods to all ports except the source.
Unlike routing (L3), a bridge does not rewrite addresses or inspect IP headers. This is transparent L2 forwarding.
Why you need it in Linux
- Docker - the
bridgenetwork driver createsdocker0and connects containers through [[veth-pair|veth pairs]] - KVM/libvirt - VMs have tap interfaces attached to a bridge
- VLAN-aware bridge - replaces a full managed switch in software-defined data centers
- Container labs (containerlab, mininet) - isolated topologies
- Network namespaces - connecting netns to each other is done through bridge + veth
Creating a bridge
# Create
ip link add br0 type bridge
ip link set br0 up
# Add interfaces
ip link set eth0 master br0
ip link set eth1 master br0
# IP on the bridge itself (for management)
ip addr add 192.168.1.1/24 dev br0
After this, eth0 and eth1 become slave ports: their own IP addresses
lose meaning, and everything works through br0.
FDB: forwarding database
The bridge learns MAC addresses the same way a physical switch does:
$ bridge fdb show
aa:bb:cc:11:22:33 dev eth0 master br0
aa:bb:cc:44:55:66 dev eth1 master br0
33:33:00:00:00:01 dev br0 self permanent
Entries fall into three kinds:
- dynamic - learned from passing traffic, expire after 300 s (
bridge_fdb_aging) - static - added manually or by an application, do not expire
- permanent - system entries (multicast, local MAC)
VLAN-aware bridge
The classic bridge is a single [[broadcast-domain|broadcast domain]]. A bridge
with vlan_filtering=1 behaves like a managed switch with
[[vlan-and-trunk|VLANs]]:
# Enable VLAN filtering
ip link set br0 type bridge vlan_filtering 1
# eth0 - access port in VLAN 10
bridge vlan add dev eth0 vid 10 pvid untagged
bridge vlan del dev eth0 vid 1
# eth1 - trunk with VLAN 10 and 20
bridge vlan add dev eth1 vid 10 tagged
bridge vlan add dev eth1 vid 20 tagged
# Verify
bridge vlan show
One bridge handles many VLANs and ports of different types.
STP: Spanning Tree Protocol
If a bridge forms a loop (two bridges connected in both directions), broadcasts circulate forever and the network collapses. STP (802.1D) detects loops and blocks one of the ports:
ip link set br0 type bridge stp_state 1
By default, Linux has STP disabled. It is not needed in Docker or KVM where the topology is fixed. At the boundary with a physical network, enable it.
Multicast and IGMP snooping
A bridge without snooping floods multicast to all ports (like broadcast). With IGMP snooping it tracks group subscriptions and sends only to interested ports:
ip link set br0 type bridge mcast_snooping 1
This matters when the bridge has many ports and heavy multicast traffic (video streaming, Avahi, Bonjour).
Docker bridge: under the hood
What Docker does on docker run:
- Creates a pair
veth0a(in the host netns) andveth0b(in the container netns) veth0bis renamed toeth0inside the containerveth0ais attached to thedocker0bridge- The container gets an IP from the
docker0subnet - An iptables NAT rule on the host handles outbound traffic
This gives containers L2 connectivity to each other (through docker0) and
L3 connectivity to the outside (through NAT).
OVS: the alternative
Open vSwitch is a more advanced replacement for the Linux bridge: OpenFlow,
per-port QoS, finer VLAN control. It is installed separately
(apt install openvswitch-switch). For straightforward tasks the vanilla bridge
is cheaper and simpler.
When something goes wrong
- Containers cannot ping each other - check that both interfaces have
master br0and thatbridge fdbshows their MACs - Slow performance - confirm that the MTU is the same on the bridge and all slave interfaces
- Multicast is slow - enable
mcast_snooping - Network died after connecting two bridges - you have a loop; enable STP
- VLAN does not work - you forgot
vlan_filtering=1on the bridge, or the PVID does not match on both ports net.bridge.bridge-nf-call-iptables=1loads the CPU - the bridge is proxying traffic into iptables; this is often disabled for container networks