Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 02 Oct 2002 12:06:23 -0300
From:      "Daniel C. Sobral" <dcs@tcoip.com.br>
To:        Georg Graf <georg-ipfw@graf.priv.at>
Cc:        freebsd-ipfw@FreeBSD.ORG
Subject:   Re: Natd plus statefull connections impossible?
Message-ID:  <3D9B0B6F.5020304@tcoip.com.br>
References:  <20021002115143.GA54827@graf.priv.at>

next in thread | previous in thread | raw e-mail | index | archive | help
Georg Graf wrote:
 >
[diet quote: I want to use nat and stateful firewall rules]
> 
> Questions:
> 
> a) is there a way to do the thing I want at all with ipfw?
> b) if no, is there a proof?
> c) Did I miss something obvious? (Maybe the intelligent use of skipto?)
> d) Did I miss something not so obvious?

For a long time, I also thought it was not possible. But, while working 
on another firewall, and trying to understand how NAT interacted with 
firewall rules (they were separated), it came to me that all rules 
applied to the real addresses, never their translation. I then set out 
to reproduce this in ipfw. Here is how.

Requirements:

1) If the packet is outgoing (ie, will be natted on it's way out), you 
want the NAT to be the last thing done.

2) If the packet is incoming (ie, will be "un-natted" on it's way in), 
you want the NAT to be the first thing done.

Now, the second requirement is easily accomplished. Just put the nat 
rules at the beginning:

ipfw add divert natd ip from any to ${NAT_ADDRESSES} in via ${EXT_IF}

There. If a packet is arriving on the external interface to a natted 
address, unnat it and be done with it.

Now, the reverse is much more difficult, because the first "accept" rule 
will end processing. The secret, alas, is *also* adding it to the beginning.

First, the rule, then the explanation:

ipfw add divert natd ip from ${NATABLE_ADDRESSES} to any out via ${EXT_IF}
ipfw add allow ip from ${NAT_ADDRESSES} to any out via ${EXT_IF}

Well, what it does is easy to see. Nat anything outgoing that needs to 
be natted, and then allow it to go out. More difficult to explain is why 
this is ok (and when it isn't).

Thing is, a packet goes through ipfw rules two times. First, when 
entering through and interface, and second when leaving through another 
(or the same, as the case may be).

Remember that I proposed to have rules apply only to the _unnatted_ 
addresses, right? So, when the packet arrives on the firewall, it 
by-passes the first rule because it's "in", not "out". The second rule 
is by-passed for the very same reason.

Thus, the packet has to be allowed (or denied) by the normal firewall rules.

The second time around, though, the packet will be blissfully allowed to 
go, the filtering having been done on the first time.

Now, what blind-spots may this introduce? First, you have to be sure you 
don't have any rules that have to be done on the packet's way out. Mind 
you, you still can have such rules for packets going to your internal 
networks, or packets going out that do not get natted.

Second, one must never enable any feature that makes ipfw process the 
rules only once. IIRC, there are ways to do that.

Third, and most importantly, this rule won't block any outgoing traffic 
from the firewall itself, if the nat address is used by the firewall 
itself (as is very common).

I have thought of two solutions for this third problem. First, and most 
simple, but which goes agains the idea of having the NAT first thing, is 
having the firewall own rules as first thing:

# Firewall rules

ipfw check-state
ipfw add allow tcp from me to any ssh setup keep-state out via ${EXT_IF}
ipfw add allow udp from me to any 53 keep-state out via ${EXT_IF}
ipfw add allow icmp from me to any out via ${EXT_IF}
ipfw add deny ip from me to any out via ${EXT_IF}

# Nat rules

...

# Main rules

ipfw check-state

So, what that does is allowing the firewall to do ssh, dns requests and 
pings (well, more than pings, but let's not go there), denying anything 
else, and only then doing the NAT, which will let *other* kinds of 
traffic to go out using the firewall ip. The two check-states ought to 
do the right thing. If they don't, then the deny in the first set must 
be restricted to outgoing setup TCP packets only, and you'll  have to 
live with that.

The second alternative is placing the line that allows all outgoing 
traffic out at the *end* of the rules, before the denies. This won't 
work if you have denies alternated with allows.

I can think of a few other alternatives, but these are the best, IMHO.

So. There you have. The stateful rules will work, because they will 
ALWAYS refer to the real address, not the translated one, no matter if 
the packet is incoming or outgoing. And it *will* be stateful, because 
the packets will have been allowed by the stateful rules when entering 
the firewall, or they won't get the *chance* to go out. :-)

If anyone is willing to create an rc.firewall profile with stateful 
rules *and* NAT, preferably based on one of the others, I'll see if I 
can get it committed.

-- 
Daniel C. Sobral                   (8-DCS)
Gerencia de Operacoes
Divisao de Comunicacao de Dados
Coordenacao de Seguranca
TCO
Fones: 55-61-313-7654/Cel: 55-61-9618-0904
E-mail: Daniel.Capo@tco.net.br
         Daniel.Sobral@tcoip.com.br
         dcs@tcoip.com.br

Outros:
	dcs@newsguy.com
	dcs@freebsd.org
	capo@notorious.bsdconspiracy.net

Mundus vult decipi decipiatur ergo.
		-- Xaviera Hollander

[The world wants to be cheated, so cheat.]


To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-ipfw" in the body of the message




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?3D9B0B6F.5020304>