At least in Arch Linux, this is the default. I think this makes bridge behavior conterintuitive because it's supposed to act like an unmanaged switch and it's now dropping packets, as most of our forward chains' default policy is drop.

Is there any reasons behind these default values?


1 Answer 1


This feature allows to augment the use of ebtables with the use iptables in bridge path to implement a stateful firewall bridge (rather than router). It has been around for a long time (2002) initially with no toggle and being part of the bridge code: if bridge and netfilter support were enabled, so was it.

Then in 2003 toggles were added. Of course for compatibility reasons, the default was set as enabled.

Then with kernel 3.18 the feature was separated to its own kernel module named br_netfilter because of issues it could cause. There were concerns about compatibility and already goals about deprecating it for nftables:

Note that this is breaking compatibility [...] However, the damage can be easily undone by modprobing br_netfilter.

On top of that, the plan is that nftables will not rely on this software layer, but integrate the connection tracking into the bridge layer to enable stateful filtering and NAT, which is was bridge netfilter users seem to require.

The kernel module has now to be loaded for the feature to work. But still, its main known customer then which would often be found in any bridge stateful firewall setup would be the iptables target physdev which auto-loads br_netfilter again for compatibility reasons.

So on a simple system, br_netfilter shouldn't be loaded and there shouldn't be any sysctl available within net.bridge: bridge-nf-call-arptables, bridge-nf-call-ip6tables, bridge-nf-call-iptables (as well as bridge-nf-filter-pppoe-tagged, bridge-nf-filter-vlan-tagged, bridge-nf-pass-vlan-input-dev) should not appear at all.

But nowadays, there's one new main customer for this kernel module: Docker. The usual way one encounters issues with this is when one runs Docker, because Docker explicitly loads br_netfilter to be able to control internal communication between containers and at the same time filters and drops forwarded packets by default. People tend thus to find it in use even when not expecting it or think this behavior is the expected behavior when it should not anymore.

As the default setting is system-wide, in addition to bridges managed by Dockers it will affect all other bridges on the system: not only bridges in the initial host namespace but also bridges in any other network namespace.

Likewise, iptables' physdev autoloads br_netfilter only when its own module (xt_physdev) is itself loaded (it used to be more often than this before this patch). Note how in the patch description this is written:

The better fix would be to change the "call-iptables" defaults to 0 and enforce explicit setting to 1, but that breaks backwards compatibility.

All this makes the answer to this question be:

It's for backwards compatibility reasons.

Additional notes.

On a system where kernel modules xt_physdev and br_netfilter are not currently loaded, a normal user just allowed to run user namespaces can do:

$ unshare -urnm
# iptables -A FORWARD -m physdev --physdev-is-bridged
# exit

and potentially have disrupted LAN connectivity for any other technology (VMs, containers ...) installed on the system that didn't use special and in-appearance-only useless rules to protect themselves from

iptables -P FORWARD DROP

The effects are described in this Netfilter documentation page explaining interactions between bridge path, routing path, ebtables and iptables: ebtables/iptables interaction on a Linux-based bridge. The part 7 is especially useful because it describe what kind of protection should be added, like:

iptables -t nat -A POSTROUTING -s -d -j ACCEPT
iptables -t nat -A POSTROUTING -s -j MASQUERADE

Above, the first rule does not make much sense, because packets between addresses in the same LAN are not forwarded at IP layer (ie routed) but are forwarded as frames at Ethernet layer (are bridged/switched) so the rule should never match, since they are not seen as routed but bridged and iptables is expected to work only at layer 3: IPv4. But when br_netfilter is loaded these kinds of "useless rules" become needed, else action intended only for routing will happen at layer 2 in bridge path (like here: NAT would be done by the bridge between two nodes in the same LAN without the first rule).

There is ongoing effort (over several years) to get rid of br_netfilter, but this requires feature parity. This was more or less achieved for IP and IPv6 (and I guess for ARP) with kernel 5.3, nftables and kernel module nf_conntrack_bridge which allow nftables in the bridge family (not in the ip, ip6 or inet families like br_netfilter triggers) to use conntrack:

This provides a replacement for the `br_netfilter' infrastructure.

Stateful filtering

The bridge family supports connection tracking since Linux kernel 5.3.

You only have to match on conntrack state information from your ruleset to enable it.

For those that are familiar with iptables: this provides a replacement for br_netfilter and the -m physdev match for iptables.

But the other features also involving encapsulation/decapsulation of VLAN or especially PPPoE protocol are still not ready.

Also since kernel 5.3 it's possible to deactivate the network namespace-wide feature and have it active only per network namespace or even per bridge. But this require for every new namespace created (including the initial network namespace is such is the need) run early inside it something like:

sysctl -w net.bridge.bridge-nf-call-arptables=0
sysctl -w net.bridge.bridge-nf-call-iptables=0
sysctl -w net.bridge.bridge-nf-call-ip6tables=0

and then for hand-picked bridges needing it, at bridge creation (ip link add ...) or later (ip link set ...):

ip link set somebridge type bridge nf_call_iptables 1

Docker with its default iptables setup can't handle this, so Docker can't run at the same time on an host with such setup, but Docker-in-Docker (actually in an other container technology like LXC rather than Docker for the same reason) would probably be fine, since the defaults in each new namespace are still backwards compatible.


You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .