[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: pf and statesfull filtering on a bridge
Several people have asked about stateful filtering with pf for bridges,
and I wasn't sure myself about what's possible until I actually tried it
today :)
Assume we want to build a stealth ethernet bridge that does stateful packet
filtering using pf. First set up the bridge and verify it's working correctly:
/etc/hostname.rl0
up
/etc/hostname.rl1
up
/etc/bridgename.bridge0
add rl0 add rl1 up
Now enable pf with an empty rule set (passing all packets by default), and
observe how everything is still working.
To better understand how pf sees the packets, we add these four rules:
pass in log on rl0 inet proto icmp
pass out log on rl0 inet proto icmp
pass in log on rl1 inet proto icmp
pass out log on rl1 inet proto icmp
We send an echo-request (ping) from a host on the rl0 side of the bridge
to a host on the rl1 side, and an echo-reply comes back. We see the following
log entries:
rule 0, pass in on rl0, icmp echo-request
rule 3, pass out on rl1, icmp echo-request
rule 2, pass in on rl1, icmp echo-reply
rule 1, pass out on rl0, icmp echo-reply
As you can see, each packet goes through pf twice. It comes in on one
interface and goes out through the other.
With pf (unlike ipf), we can actually address each of these cases, both
directions on both interfaces.
If we filter statefully, there's one thing to keep in mind: the states in
the state table are sorted by a key which consists of the two address and
port pairs of the connection. The order of these two pairs is relevant.
If an outgoing packet from A to B creates state, pf will let pass outgoing
packets from A to B and incoming packets from B to A. But it will still
block outgoing packets from B to A and incoming packets from A to B. In
a non-bridging case this is perfectly clear and obvious.
But in our bridging case, the same packet goes through pf twice, once through
each interface. And the order of the key pairs is different in both cases.
There are two solutions. The first one is to create two states per
connection, one for each direction, using two 'keep state' rules.
I don't think anyone will want to do that. Especially since the second
solution is very simple and elegant:
>From pf perspective, packets go through our bridge twice. If you look at
either interface, you see exactly the same traffic, only the direction
is reversed. Hence, we can ignore one interface and do all the filtering
on the other one.
Here's an example:
# we want to filter on rl0, hence just pass anything on rl1.
pass in quick on rl1 all
pass out quick on rl1 all
# as an example, we block eveything by default, and let pass only
# icmp echo requests related replies (both directions, statefully).
block in on rl0 all
block out on rl0 all
pass in on rl0 inet proto icmp all icmp-type echoreq keep state
pass out on rl0 inet proto icmp all icmp-type echoreq keep state
This works equally well for more complex rules. The general recipe is
to pick one interface you want to filter on. Start with two rules
that let pass anything on the other interface (quick). After that,
filter only on the interface you picked.
Regards,
Daniel