ipfw nat inbound keep-state with net.inet.ip.fw.one_pass=0
smithi at nimnet.asn.au
Sat Jun 25 07:56:12 UTC 2011
On Thu, 23 Jun 2011, umage wrote:
> Some points:
> 1) I did use the handbook as reference, and my ruleset mimics the layout used
Excuse the late response, I've been away. The best reference, apart
from ipfw(8), is /etc/rc.firewall. 'Nuff said.
> 2) Handbook uses divert natd, which I used until I switched to the kernel nat
Assuming that was working, is changing to ipfw nat the only difference?
Or is that when you added fwd to the mix? Is 192.168.0.55 another box
on the LAN, or an IP alias on this box? What says 'netstat -finet -rn'?
Is this on FreeBSD 8.x?
> 3) I did not find any concrete examples of ipfw nat rule usage, so I'm using
> them the old natd way.
Apart from the 'NAT, REDIRECT AND LSNAT' section in ipfw(8), natd(8) is
still useful as fuller reference, given a few less, renamed parameters.
As mentioned in that section, libalias(3) gives detail of all functions.
> I did some more experiments, and noticed that for example, an inbound
> connection can still communicate both ways after the initial state table rule
> expires (20 seconds for some reason).
ipfw(8) 'SYSCTL VARIABLES' covers timeouts (sysctl net.inet.ip.fw.dyn_*)
20 seconds suggests a SYN timeout, so a TCP connection - but see below.
Perhaps that 'works' because you're not denying established connections
and using only 'setup' on keep-state rules, again assuming TCP protocol?
> If they communicate while the state
> entry is alive, the timeout resets, but it seems like it doesn't matter at
> all. This leads me to believe that 'ipfw nat' keeps an internal state table,
> which cannot be viewed, but is checked when doing check-state. Or
> something... which I have no way of knowing.
NAT aliasing tables are entirely distinct from ipfw dynamic rule state
tables. Try adding 'log' (and maybe same_ports) to ipfw nat parameters
at least while debugging connections. That log, 'ipfw -ted show' and
a tcpdump on each interface should show exactly what's going on.
'ipfw nat 1 show config'.
> Here's a pruned version of the ruleset I used. Rule 600 is the one that adds
> that remote <--> local state table entry that messes everything up. If I omit
> keep-state on it, then traffic from the local side will be the one creating
> the states when replying, with a 5-second timeout.
sysctl net.inet.ip.fw.dyn_udp_lifetime is 5 seconds by default. So now
we're talking UDP? Please be more specific, or best, cut&paste results.
> $fw add 100 allow all from any to any via $lan_if
This passes all packets coming in from the LAN, bound for anywhere - ie
this box OR the outside - but before/without performing NAT - as well as
passing packets being transmitted to the LAN, whether locally generated
or routed after having been NAT'd on inbound pass. Not what you wanted.
You mentioned packets mistakenly reaching the outside with 192.168.*
source addresses, that'll be this rule. Try specifying 'in recv $if'
and 'out xmit $if' avoiding 'via' when it's ambiguous, especially on
outbound packets where 'via $if' is also true when they've come _in_ on
that specified interface. You need to do outbound NAT first anyway.
> $fw nat 1 config if $wan_if redirect_port 192.168.0.55:12345 12345
> $fw add 200 nat 1 ip4 from any to any in via $wan_if
Ok, you're doing inbound NAT before checking state, however you've not
specified protocol (tcp or udp) with redirect_port. I can't find any
example in ipfw(8), natd(8) or libalias(3) where proto is optional, but
I haven't read the code or tried this myself. We can't tell from this
(or rule 600) whether your port '12345' is TCP or UDP.
> $fw add 300 check-state
At this point any packet, in or out, matching dynamic state tables will
execute the action of the matching keep-state rule. For packets going
out to the WAN the action is a skipto, so all ip4 packets matching that
flow will execute the 'skipto 800', where you NAT the outbound packets,
and allow the corresponding return packets.
> $fw add 400 skipto 800 ip4 from any to any out via $wan_if keep-state
Again, 'out via $wan_if' is ambiguous, and includes packets _received_
on $wan_if and now being transmitted to the inside, again before NAT.
Specify 'out xmit' if you only want to apply this to packets being sent
out to $wan_if, as I think you do; these are the only ones you want to
perform NAT on anyway.
> $fw add 500 allow all from any to any out keep-state
Ok, only inbound packets get to here, and they've already been NAT'd ..
> $fw add 600 allow all from any to any dst-port 12345 in keep-state
> $fw add 700 deny all from any to any in
While 'all | ip' will work for tcp or udp packets, better to specify the
Ok, not only outbound packets get here, but also the return packets
coming in with matching state, from the skipto.
> $fw add 800 nat 1 ip4 from any to any out
> $fw add 900 allow all from any to any
Bottom line is you need to do NAT on packets outbound to $wan_if before
encountering the first check-state or keep-state rule, as you have for
packets inbound from the WAN. Then your dynamic rules will be matching
packets with addressing between inside IPs and external hosts.
I strongly suggest following the flow pattern of the 'simple' ruleset in
/etc/rc.firewall especially regarding placement of NAT rules, never mind
whether using natd or ipfw nat, rather than the poor Handbook examples.
Depending on what protocol port 12345 is, you may be better off using
static rather than dynamic rules to handle it .. another area where that
Handbook section is inappropriately deprecatory; rc.firewall uses both.
(but if not, try posting afresh but with more detail to freebsd-ipfw@)
More information about the freebsd-questions