Jun 29, 2008

Filtering on bridge

There used to be a flaw in using ipfw on bridge interface. It's impossible to distinguish incoming packets on member interface from incoming packets on bridge itself. For example consider two rules:
add 1 allow ip from any to any in recv bridge
add 2 allow ip from any to any in recv member


First rule will never match. The logic is ok here (if you are aware of ipfw's handling of interface options). But what do you expect if you disable filtering on member interfaces and perform filtering on bridge only. You expect rule 1 to match all incoming packets on bridge. It gets extremely annoying when using stateful filtering.

First time I came across this issue several years ago. But didn't figure out how to fix it. At that time I've decided to switch to pf.

Actually ipfw is the only firewall that allow rules like
allow ip from any to any out recv if1 xmit if2
Such tricks are possible because ipfw gets input interface from mbuf of a packet. pf for example relies on pfil to provide interface.

I've added a hack into if_bridge to work around it. It contradicts traditional ipfw behaviour a little but seems to be much more useful. I think patches are useful enough and can be commited into FreeBSD:

perforce.freebsd.org/changeView.cgi?CH=143921
perforce.freebsd.org/changeView.cgi?CH=144238

Jun 22, 2008

Incompatibility and some new features

I've made some changes that break backward compatibility. But I've tried not to break anything intentionally but to do a cleanup work.

First of all most of sysctl's responsible for layer2 filtering were replaced by per interface flags.

net.link.ether.ipfw and net.link.bridge.ipfw are replaced by l2filter interface flag. So sysctl net.link.ether.ipfw=1 became ifconfig if1 l2tag.

net.link.bridge.ipfw_arp was renamed to net.link.bridge.pfil_layer2_arp

Introduced l2tag interface flag. It's purpose is to add mbuf tag containing source and destination layer2 addresses to every packet passing through interface. Note that l2tag filtering against layer2 addresses is performed in layer3.

When invoked from layer2 ipfw no longer touches layer2 headers. So they following rule won't work anymore:
ifpw allow ip from 10.1.1.1 to any src-ether 00:11:11:11:11:11 layer2

ipfw mac option was replaced by to two options: src-ether and dst-ether. ipfw still accepts mac option but translates it into src-ether and dst-ether.

Lookup tables support layer2 addresses now:
ipfw table 1 add 10.1.1.1 ether 00:11:11:11:11:11
ipfw allow ip from table(1) to any


ipfw mac-type was renamed to ether-type. Support for mac-type preserved.

Stateful filtering remains somewhat special. The problem here is that l2tag is added to a packet only in input path (when invoked from ether_demux). Such decision was intentional, mainly because it's impossible to get tag added in output path without serious layer violations or entire pfil framework and packet handling redesign. That's why a packet that has no l2tag attached, will pass against layer2 dynamic rule.

Dynamic rules (state created by the rule) do not check both source and destination layer2 address, but just the addresses specified by the rule created it. For example
ifpw allow ip from 10.1.1.1 to any src-ether 00:11:11:11:11:11 keep-state
will create dynamic rule that checks only source ethernet address of a packet, but not destination.