Understanding iptables FORWARD Chain Behavior in Linux Ethernet Bridging (br0 + eth0/tap0)


2 views

When working with Linux bridges (br0 in this case), it's crucial to understand how packets traverse different networking layers. A common misconception is that bridging operates exclusively at layer 2 (Ethernet), while in reality there's interaction with iptables at layer 3.

# Typical bridge setup commands
brctl addbr br0
brctl addif br0 eth0
brctl addif br0 tap0
ifconfig eth0 0.0.0.0 promisc up
ifconfig tap0 0.0.0.0 promisc up
ifconfig br0 10.0.1.1 netmask 255.255.255.0

Even with ebtables handling layer 2 filtering, iptables' FORWARD chain still affects bridged traffic when:

  • IP packets traverse between bridge ports
  • The bridge interface has an IP address assigned
  • Netfilter bridge-nf is enabled (default on most distros)
# Check bridge-nf kernel parameters
sysctl -a | grep bridge-nf

The minimal required rule to allow bridged traffic is:

iptables -A FORWARD -i br0 -j ACCEPT

For more granular control, consider these patterns:

# Allow established/related connections
iptables -A FORWARD -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT

# Specific protocol example (DNS)
iptables -A FORWARD -p udp --dport 53 -i br0 -j ACCEPT

If you want to completely bypass iptables for bridge traffic:

# Disable bridge-nf filtering
echo 0 > /proc/sys/net/bridge/bridge-nf-call-iptables

However, this affects all bridge interfaces and removes firewall capabilities for IP traffic crossing bridges.

Here's a complete example for a KVM/qemu bridge setup:

# Bridge creation
brctl addbr virbr0
brctl addif virbr0 eth0
ip link set dev virbr0 up

# IPTables rules
iptables -I FORWARD -i virbr0 -o virbr0 -j ACCEPT
iptables -I FORWARD -m physdev --physdev-in tap0 --physdev-out eth0 -j ACCEPT
iptables -I FORWARD -m physdev --physdev-in eth0 --physdev-out tap0 -j ACCEPT

The physdev module provides even more precise control over bridge traffic filtering.


When setting up a Linux bridge (br0) with interfaces eth0 and tap0, you'd expect layer 2 traffic to flow freely between bridge members. However, we encounter a peculiar situation where iptables FORWARD rules affect bridged traffic despite being an IP-layer firewall. Here's what's happening under the hood:

# Bridge setup commands
brctl addbr br0
brctl addif br0 eth0
brctl addif br0 tap0
ifconfig eth0 0.0.0.0 promisc up
ifconfig tap0 0.0.0.0 promisc up
ifconfig br0 10.0.1.1 netmask 255.255.255.0

The Linux bridge implementation has an interesting characteristic - when a bridge interface has an IP address assigned (like our br0 with 10.0.1.1), the kernel treats it differently:

  • For pure layer 2 bridging (no IP on br0), ebtables handles all filtering
  • With an IP address, the bridge becomes a "routing bridge" and iptables FORWARD chain participates

When packets enter a bridged interface with an IP address, they traverse both networking stacks:

# Kernel packet flow for bridged traffic with IP:
1. Physical interface (eth0/tap0) receives frame
2. Bridge checks ebtables rules
3. If bridge has IP, kernel checks iptables FORWARD chain
4. Bridge forwarding decision
5. Output interface processing

You have several approaches to handle this:

Option 1: Keep IP on bridge and allow forwarding

iptables -A FORWARD -i br0 -j ACCEPT
iptables -A FORWARD -o br0 -j ACCEPT

Option 2: Remove IP from bridge (pure layer 2)

ifconfig br0 0.0.0.0 down
ifconfig br0 up  # Bridge now operates at pure layer 2

Option 3: Use ebtables for filtering

ebtables -A FORWARD -i eth0 -o tap0 -j ACCEPT
ebtables -A FORWARD -i tap0 -o eth0 -j ACCEPT

This behavior becomes particularly noticeable in virtualization setups. For a KVM host with bridged networking:

# Typical working configuration:
brctl addbr br0
brctl addif br0 eth0
ifconfig eth0 0.0.0.0 up
ifconfig br0 192.168.1.100/24 up
iptables -A FORWARD -i br0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
iptables -A FORWARD -o br0 -j ACCEPT

If you're using the bridge purely for layer 2 connectivity (like connecting VMs), removing the IP address from br0 offers better performance as it avoids the additional iptables processing overhead. The trade-off is you lose the ability to manage the bridge interface itself via IP.