ipfw, nat and stateful firewall: why "keep-state" on "skipto" works at all and how do this properly?

Ian Smith smithi at nimnet.asn.au
Fri Jan 30 10:50:20 UTC 2015


On Fri, 30 Jan 2015 12:05:07 +0300, Lev Serebryakov wrote:
 > On 30.01.2015 05:33, Julian Elischer wrote:
 > 
 > >> 12700 skipto 12900 ip from any to any keep-state 12800 deny ip
 > >> from any to any 12900 nat 1 ip from any to any out 12999 allow ip
 > >> from any to any
 > >> 
 > >> And rules for inbound ones are:
 > >> 
 > >> 11000 deny ip from any to not me 11500 nat 1 ip from any to any 
 > >> 11510 check-state 11600 allow tcp from any to me ssh,http setup
 > >> keep-state 11999 deny ip from any to any

 > > ok so the dynamic rule is created on the outgoing packet, and
 > > associated with skipto 12900 which sets up a NAT session.
 > > 
 > > on a later incoming packet, the rule 11500 is hit first so the
 > > packets are NAT'd back, and then their state is compared to that
 > > stored in the outgoing path, and if they match, they go to 12900 
 > > where they are not checked again becasue they are not 'out'
 > > packets. so it falls through to 12999 and is allowed in. (in its
 > > changed form). packets that are not in a known session fall through
 > > teh check-state and are dropped.
 > > 
 > > it all looks ok to me. kinda cute actually.

 >   Not cute at all for me, as 12900 needs "out" (it is already in "out"
 > group of rules!) and incoming packet is jumped to outbound section :)

So, don't do it that way.  I've also always found skipto .. keep-state 
rules confusing, so I'll reinforce wishmaster's initial comment:

 : At first, i think you should move keep-state from skipto to explicit 
 : allow rule.

Use skipto to distinguish inbound from outbound packets, on both/all of
inside and outside interfaces, then use keep-state in the appropriate 
sections to avoid confusion.

 >  Other filtration is hypothetical now, but I don't like this "skip ->
 > nat with additional "redundant" check -> allow" pattern.

Frankly the examples in the IPFW handbook section still suck, despite 
some recent good work in cleaning up that document.  I would suggest 
basing a ruleset on one of those - probably 'simple' in this case - in 
/etc/rc.firewall, adding whatever stateful rules you want there.  I 
failed in several attempts to get the newer kernel nat code used in 
'client' and 'open' rulesets into 'simple', but it's straightforward.

Even then, I extend the _structure_ of that example to split inbound 
from outbound flows as described below, rather than just nat everything.

 > > I always do what is done here and separate inwards and outwards 
 > > packets for the external interface into two different sets of
 > > rules (and another set for other interfaces).
 >   Yep, it is exactly what I do, these two groups of rules are not only
 > rules, of course.

Something else I learned from Julian a long time ago regarding natd - 
though it applies equally well to kernel nat:

  "Don't waste natd's time with packets it doesn't care about".

You need to nat all inbound packets from the outside - well, those to 
the $nat_if address anyway, if you have multiple IPs - but you only need 
to nat outbound packets on the outside interface that have earlier been 
accepted in from the inside interface from an internal address:

$fwadd skipto $NATIN ip4 from any to ${ext_ip} in recv ${ext_if}
[..]
$fwadd skipto $NATOUT ip4 from any to any out xmit $ext_if recv $int_if

[..]

$fwadd $NATIN divert natd ip4 from any to any in recv ${nat_if}
[..]
$fwadd $NATOUT divert natd ip4 from any to any out xmit ${nat_if}

Simply replacing 'divert natd' with 'nat N' of course.

If you can't follow your own ruleset a year later, it's just not clear 
enough .. speaking from bitter experience :)

cheers, Ian


More information about the freebsd-net mailing list