Iptables: Matching packets for bridged interface

I'm building a firewall configuration tool based on iptables, and trying to get a "bump in the wire" scenario working.

Given a setup with eth0 and eth1 in a bridge br0 and a third interface eth2:

    |          |         |
   eth0       eth1      eth2
    | == br0== |         |
          |              |
          |              |
         --- linux node ---

In this scenario, lets say I want TCP port 80 traffic to be dropped if it is going to the network attached to eth0, but allow it to eth1.

I am therefore trying to reliably match packets that go out over the specific interface eth0.

If I add the following iptables rule in the filter table:

-A FORWARD -o br0 --physdev-out eth0 -j LOG

Given a packet that originates from eth1 (the other half of the bridge), then the rule matches just fine, logging:

... IN=br0 OUT=br0 PHYSIN=eth2 PHYSOUT=eth1 ...

However if the packet origniates from eth2, then the rule no longer matches.

I appears that the routing algorithm can't determine which of the bridged interfaces to choose, so the packet is sent out over both interfaces in the bridge.

If I add another more promiscuous log rule, then I get the following log output for that packet:

... IN=eth2 OUT=br0 ...

My guess is that in the first case, the routing algorithm can just choose the other interface on the bridge since that packet shouldn't go out the way it came. In the second case, it hasn't chosen a specific interface and you then get no physdev information at all!

However, if the bridge has learned the destination MAC address (as shown by brctl showmacs br0) then it can determine the correct interface, and you get physdev informatino again.

(There is also a third case: where the bridge comprises three interfaces that this seems to apply to , then it still can't establish a single interface to send the packet on just be excluding the source interface.)

So, the question is, how can I reliably match packets the go out over eth0 regardless?

Given the example I gave at the start, it is not enough to just match packets that will be routed out over multiple interfaces, one of which is eth0 (though that would be useful in other scenarios). I want to be able to treat the traffic for eth0 and eth1 differently, allowing the traffic to eth1, but not eth0.


Solution 1:

Reasons for observed behviour

The reason that iptables doesn't get the physical bridge information when the packet arrives from a non-bridged interface is that the packet has never been near the bridging mechanism, even though at this point we know we are sending it out on the bridge.

In the case where the packet did arrive over a bridge port, but it is an N>2 bridge, the problem is that the iptables PHYSDEV extention only provides for their being one value for "out", so it just doesn't bother telling us if there are two.

Solution

Use ebtables instead of iptables. The ebtables OUTPUT chain will know which physical bridged interface it is sending packets out on.

In the scenario above, where you want to filter packets that are leaving via a specific bridged interface (eth0), regardless of how it arrived into the system, add an ebtables rule along the following lines:

-A OUTPUT -o eth0 -j <target>

In a more complex scenario, where you want to filter packets arriving from a specific interface, and leaving via a bridged interface, it gets harder. Say we want to drop all traffic from eth2 (non-bridged) going to eth0 (bridged as part of br0) we need to add this rule to iptables:

-A FORWARD -i eth2 -o br0 -j MARK --set-mark 1234

This will mark any packet that comes from eth2 and is bound for the bridge. Then we add this rule to ebtables:

-A OUTPUT -physdev-out eth0 --m mark --mark 1234 -j DROP

Which will DROP any packet marked by iptables (as being from eth2) that is egressing via the specific bridge port eth0.

Acknowledgements

Thanks goes out to Pascal Hambourg over at the netfilter iptables mailing list for his help in coming up with this solution.