1

im trying to configure a bridge network on Ubuntu 20.04. There are 4 ethernet ports in the brige. This is how the corresponding netplan config looks like:

network:
  version: 2
  ethernets:
    enp15s0:
      dhcp4: no
      dhcp6: no
    enp12s0:
      dhcp4: no
      dhcp6: no
    enp11s0:
      dhcp4: no
      dhcp6: no
    enp10s0:
      dhcp4: no
      dhcp6: no
  bridges:
    br1:
      interfaces: [enp12s0,enp11s0,enp10s0,enp15s0]
      addresses: [192.168.1.1/24]
      dhcp4: no
      dhcp6: no

To three of those ethernet ports some hardware with a fixed IPs is connected. Those are three devices with 192.168.1.50, 192.168.1.51 and 192.168.1.52. Further we have a dhcp config, which shall ensure, that when someone connects to enp15s0, he will receive an IP in the correct sub net and will be able to access the connected devices. The dhcp config is the following:

# dhcpd.conf

# option definitions common to all supported networks...
option domain-name "kira";

default-lease-time 600;
max-lease-time 7200;

# The ddns-updates-style parameter controls whether or not the server will
# attempt to do a DNS update when a lease is confirmed. We default to the
# behavior of the version 2 packages ('none', since DHCP v2 didn't
# have support for DDNS.)
ddns-update-style none;

# If this DHCP server is the official DHCP server for the local
# network, the authoritative directive should be uncommented.
#authoritative;

# Use this to send dhcp log messages to a different log file (you also
# have to hack syslog.conf to complete the redirection).
#log-facility local7;

# No service will be given on this subnet, but declaring it helps the 
# DHCP server to understand the network topology.

subnet 192.168.1.0 netmask 255.255.255.0 {
  range 192.168.1.100 192.168.1.199;
  interface br1;
}

When i attach a windows pc to the enp15s0 port, this is what i get:

  • the windows pc will get a dhcp assigned address, e.g. 192.168.1.101
  • i can ping the windows pc from the linux machine just fine
  • on the linux machine i can ping the three devices with 192.168.1.50-52
  • however on Windows i just can ping the Linux machine 192.168.1.1 but not the individual devices in this bridge subnet.
  • this is how the windows ethernet config looks like, when i plug in the pc: enter image description here

Can someone point me into the right direction, what might be missing. What i want to achieve again: plug in the windows pc and be able to ping all devices hanging in the 192.168.1.xx subnet.

Update:

i've added a default gateway to the dhcp config, now the Windows pc also receives the gateway, but the problem still exists:

subnet 192.168.1.0 netmask 255.255.255.0 {
  range 192.168.1.100 192.168.1.199;
  interface br1;
  option subnet-mask 255.255.255.0;
  option routers 192.168.1.1;
}

windows ethernet conf with gateway

Update 2

output of iptables-save

# Generated by iptables-save v1.8.4 on Fri Sep 25 17:05:52 2020
*filter
:INPUT ACCEPT [9581:2167049]
:FORWARD DROP [0:0]
:OUTPUT ACCEPT [9626:1660600]
:DOCKER - [0:0]
:DOCKER-ISOLATION-STAGE-1 - [0:0]
:DOCKER-ISOLATION-STAGE-2 - [0:0]
:DOCKER-USER - [0:0]
-A FORWARD -j DOCKER-USER
-A FORWARD -j DOCKER-ISOLATION-STAGE-1
-A FORWARD -o docker0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A FORWARD -o docker0 -j DOCKER
-A FORWARD -i docker0 ! -o docker0 -j ACCEPT
-A FORWARD -i docker0 -o docker0 -j ACCEPT
-A DOCKER-ISOLATION-STAGE-1 -i docker0 ! -o docker0 -j DOCKER-ISOLATION-STAGE-2
-A DOCKER-ISOLATION-STAGE-1 -j RETURN
-A DOCKER-ISOLATION-STAGE-2 -o docker0 -j DROP
-A DOCKER-ISOLATION-STAGE-2 -j RETURN
-A DOCKER-USER -j RETURN
COMMIT
# Completed on Fri Sep 25 17:05:52 2020
# Generated by iptables-save v1.8.4 on Fri Sep 25 17:05:52 2020
*nat
:PREROUTING ACCEPT [2:398]
:INPUT ACCEPT [2:398]
:OUTPUT ACCEPT [259:17356]
:POSTROUTING ACCEPT [259:17356]
:DOCKER - [0:0]
-A PREROUTING -m addrtype --dst-type LOCAL -j DOCKER
-A OUTPUT ! -d 127.0.0.0/8 -m addrtype --dst-type LOCAL -j DOCKER
-A POSTROUTING -s 172.17.0.0/16 ! -o docker0 -j MASQUERADE
-A DOCKER -i docker0 -j RETURN
COMMIT
# Completed on Fri Sep 25 17:05:52 2020
MechaTheo
  • 25
  • 5
  • It could be caused by having the module br_netfilter loaded. Try `rmmod br_netfilter`. If that fixes your problem I'd write a more elaborate answer (after a few questions) – A.B Sep 24 '20 at 21:14
  • Thank you very much it worked! – MechaTheo Sep 25 '20 at 11:24
  • Do you know by any chance how to make this change permanent, blacklisting didn't work for me. – MechaTheo Sep 25 '20 at 11:33
  • 1
    Now the questions. This module didn't arrive by chance. It's usually activated by *other* iptables rules, including in hidden namespaces. major culprit: Docker. Or any other similar tool. So can you tell what you are running that can affect networking in a broad meaning? You will also have to add in your answer the output of `iptables-save` so I can see how to cope with this (without removing the module). – A.B Sep 25 '20 at 12:00
  • so basically the entire application is based on Docker, so yeah docker is running. And i've added the `itables-save` output to the question. – MechaTheo Sep 25 '20 at 15:08

1 Answers1

4

TL;DR

Add an exception rule in the DOCKER-USER chain used for this purpose by Docker (called from filter/FORWARD), to avoid bridged Ethernet frames traversing iptables being dropped by the filter/FORWARD's DROP policy set by recent versions of Docker. Without module assistance, iptables sees one interface: the bridge interface br1, not its bridge ports.

iptables -A DOCKER-USER -i br1 -o br1 -j ACCEPT

You'll have to figure out how and when to add this rule. Eg: is Docker emptying this chain or leaving it as found? if not emptied, any order is fine so earliest at boot is probably the best (preceding it with iptables -N DOCKER-USER); if it's emptied, it should be done after starting Docker.


Note that features described below hopefully won't be needed or stay useful for much more time because nftables can provide (most of) them in a cleaner way. But a slight part don't have yet equivalent through nftables (like VLAN and PPPoE decapsulation/encapsulation).

Also note that br_netfilter affects iptables-nft or nftables' ip family in the same way it affects iptables-legacy. It also affects IPv6 likewise.

The br_netfilter kernel module

When Linux is configured as a bridge, to do filtering on this bridge, ebtables should be used. But ebtables is more limited in features than iptables when dealing with IPv4 traffic. In particular, to implement a (transparent) stateful bridging firewall, Netfilter's conntrack has to be available, but ebtables can't use it. So what was devised is a mechanism to temporarily turn Layer 2 Ethernet frames containing an IPv4 packet into Layer 3 IPv4 packets, pass them through conntrack and iptables which can make use of conntrack, then convert them back to Ethernet frames: that's the role of the kernel module br_netfilter when it's loaded and bridge-nf-call-iptables is set (which is the default). This makes bridged frames traverse iptables, and might even, if they end up being destined for the host, pass a second time through iptables as routed packets, as described in this documentation.

This module can help likewise for other protocols (IPv6 and ip6tables, ARP, or VLAN and PPPoE) filtered at the bridge level. Other uses of this module is for iptables' physdev match module (to allow also matching per bridge port rather than only per bridge). This match module, whenever used in any network namespace, triggers automatically the loading of br_netfilter, which has a system-wide effect on all network namespaces (since kernel 5.3 other options are possible but they are not compatible with using Docker).

Of course when one doesn't need this module or doesn't even know about its existence, it can become the source of many unexpected effects, or even wrong assumptions (eg: taking for granted that iptables is used to enable bridge forwarding, while it's the exception).

Docker loads the br_netfilter kernel module for proper isolation of containers.

The Link Layer (blue color) part of the Packet flow in Netfilter and General Networking schematic below describes what happens for an ethernet frame containing an IPv4 packet when br_netfilter is in effect:

Packet flow in Netfilter and General Networking

Once loaded all frames enter iptable's filter/FORWARD chain from the bridge path (green box within the Link Layer blue field), and hit its default DROP policy: nothing gets bridged, only communication with the bridge works since then iptables doesn't get called.

Working around unwanted effects

Instead of preventing the loading of this module or disabling its effects on IPv4 (which are probably needed for some feature in use) better cope with its effects, as described in the previous link. With Docker having a dedicated chain DOCKER-USER it won't use to insert custom rules, This is enough:

iptables -A DOCKER-USER -i br1 -o br1 -j ACCEPT

or if there's no foreign IP address:

iptables -A DOCKER-USER -s 192.168.1.0/24 -o 192.168.1.0/24 -j ACCEPT

The frame also traverses other tables like the nat table, but Docker rules in place won't affect such frames (and apparently it doesn't provide a DOCKER-USER chain here either). Still, be careful when adding your own rules, to prevent them from having effect on bridged packets, or your bridge could for example nat anything traversing it, making targets in br1's LAN seeing everything with a source IP of 192.168.1.1.

Without involving marks, with the interface limitations in place, this is a possible (and imperfect) method to avoid any further badly made rule to affect the bridge path (again, those are not needed at all currently, so I didn't mention them in the TL;DR part):

iptables -t nat -I PREROUTING -i br1 -d 192.168.1.0/24 -j ACCEPT
iptables -t nat -I POSTROUTING -s 192.168.1.0/24 -o br1 -j ACCEPT

Just don't use any "bad" rule. An example of bad rule would be:

iptables -t nat -I POSTROUTING -o br1 -j MASQUERADE

which would nat all bridged frames's source IPv4 addresses with no routing involved.

A.B
  • 5,338
  • 1
  • 17
  • 20