# Advanced ipfw nat functions



## terminus (Jul 12, 2009)

Hello.

I'm playing around with ipfw nat functionality. I met unexpected problems with functions like redirect_proto and redirect_addr. Also there is unclear behaviour with localy initiated connetions pass through nat.

I have testing enviroment that consists of three systems - one of them I use as a "Client", second is "NAT router" and third is "ISP gateway".

```
(Client)<--192.168.1.0/24-->(NAT)<--1.2.3.0/24-->(ISP)
```
NAT has two nic's - fxp0 in client's side, and em0 in ISP side.
clent's nic: 192.168.1.2
nat's private nic: 192.168.1.1
nat's public nic: 1.2.3.100
ISP gateway nic: 1.2.3.122

net.inet.ip.fw.one_pass=1

ipfw config:

```
add check-state
add allow ip from any to any via fxp0
add deny ip from any to 192.168.0.0/16 in recv em0
add deny ip from 192.168.0.0/16 to any in recv em0
add allow tcp from any to me 6881
add allow udp from any to me 4444
add allow icmp from me to any out keep-state
add allow tcp from me to any out keep-state
add allow udp from me to any out keep-state
nat 1 config if em0 deny_in unreg_only reset
add nat 1 all from any to any via em0
add deny ip from any to any
```


So, the problems are the following:

- I'm unable to use only nat rules without keep-state rules. If I do so, then I'm unable to initiate TCP sessions from NAT machine. In the same time looks like UDP connections initiated from NAT works just fine, and client's trafic that pass through nat also works just fine. So this is why I should catch all local trafic with keep-state rules. Why it is so? Linux iptables have abitity to nat both (clent+router) trafic at once.  Here is tcpdump of localy initiated connection from nat to ISP (routing setting are fine there, if any... Both of them use each other as default gw):

```
# tcpdump -i 2 -v -n -l ip
tcpdump: listening on em0, link-type EN10MB (Ethernet), capture size 96 bytes
14:27:29.314586 IP (tos 0x0, ttl 64, id 13378, offset 0, flags [DF], proto TCP (6), length 60) 1.2.3.100.54713 > 1.2.3.122.1234: S, cksum \
0xa1c0 (incorrect (-> 0x5599), 2816946197:2816946197(0) win 65535 <mss 1460,nop,wscale 3,sackOK,timestamp 14851438 0>
14:27:29.314817 IP (tos 0x0, ttl 64, id 0, offset 0, flags [DF], proto TCP (6), length 40) 1.2.3.122.1234 > 1.2.3.100.54713: R, cksum 0x5bb5 \
(correct), 0:0(0) ack 2816946198 win 0
^C
2 packets captured
6 packets received by filter
0 packets dropped by kernel
```

- I'm unable to perform redirect_proto or redirect_addr. In the same time redirect_port rules works just fine with single ports and also with redirection of scope of ports... Siting at ISP gateway console, I make a try to initiate redirect_proto and redirect_addr action with all of this examples of nat configuration (none of them working for me):

```
ipfw nat 1 config if em0 deny_in unreg_only reset redirect_proto tcp 192.168.1.2 1.2.3.100 1.2.3.122
ipfw nat 1 config if em0 deny_in unreg_only reset redirect_proto tcp 192.168.1.2 1.2.3.100
ipfw nat 1 config if em0 deny_in unreg_only reset redirect_proto tcp 192.168.1.2
ipfw nat 1 config redirect_proto tcp 192.168.1.2 1.2.3.100

ipfw nat 1 config if em0 deny_in unreg_only reset redirect_addr 192.168.1.2 1.2.3.100
ipfw nat 1 config if em0 deny_in unreg_only reset redirect_addr 192.168.1.2 0.0.0.0
ipfw nat 1 config if em0 redirect_addr 192.168.1.2 1.2.3.100
ipfw nat 1 config redirect_addr 192.168.1.2 1.2.3.100
ipfw nat 1 config redirect_addr 192.168.1.2 0.0.0.0
```

- What is about proxy_only rule? natd have also proxy_rule rule but ipfw nat do not. How to use proxy_only in ipfw nat?


Any clue, please?


----------



## terminus (Jul 12, 2009)

Yeah forgot to add about FreeBSD version in NAT - I'm using 7.2-STABLE FreeBSD 7.2-STABLE #0: Wed Jun 24 12:59:06 EEST 2009 i386


----------



## terminus (Jul 17, 2009)

Just for record (may be some one will have similar problems in future)



> I'm unable to use only nat rules without keep-state rules. If I do so, then I'm unable to initiate TCP sessions from NAT machine.


This problem hase been solved by turning off offload functions of my em0 network adapter.
before:

```
em0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> metric 0 mtu 1500
        options=9b<RXCSUM,TXCSUM,VLAN_MTU,VLAN_HWTAGGING,VLAN_HWCSUM>
```
after:

```
em0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> metric 0 mtu 1500
        options=98<VLAN_MTU,VLAN_HWTAGGING,VLAN_HWCSUM>
```
Now nat is working with localy initiated TCP/UDP traffic as expected.




> I'm unable to perform redirect_proto or redirect_addr.


I'm still unable to redirect_proto nut I got how to do redirect_addr - you should not use if or ip statements in ipfw nat rules. This is working example:


> ipfw nat 1 config same_ports redirect_addr 192.168.1.2 8.9.0.1






> What is about proxy_only rule? natd have also proxy_rule rule but ipfw nat do not. How to use proxy_only in ipfw nat?


I still have this question unaswered


----------



## MisterMinit0815 (Jul 20, 2009)

Hello everybody,

i've the same Problem as terminus, i'm using FreeBSD 7.2-RELEASE-p2 and In-Kernel NAT, the outgoing NAT works fine but the incoming Port-Redirect didn't work.

Here is my config:

Internal Host (Webserver) in DMZ:
10.10.0.5


```
${fwcmd} nat 1 config log redirect_port tcp 10.10.0.5:80 ${extnet_ip}:80
${fwcmd} nat 100 config if ${extnet_if} log reset unreg_only same_ports
${fwcmd} add 50 nat 100 all from any to any via ${extnet_if}
```

Incoming Firewall Rules:


```
${fwcmd} add 2000 pass tcp from any to 10.10.0.5 80 via ${extnet_if} keep-state
```


Can anyone tell me where the Problem is? With TCPDUMP i can see the incoming package on the external-interface, but not at the dmznet-interface of the Firewall, so there must be something wrong with the nat rules on the firewall i think.

Thanks in advance.
MisterMinit


----------



## terminus (Jul 20, 2009)

Hi. I think this problem will be solved if you will use this ipfw nat config:


```
${fwcmd} nat 100 config if ${extnet_if} log reset unreg_only same_ports redirect_port tcp 10.10.0.5:80 80
${fwcmd} add 50 nat 100 all from any to any via ${extnet_if}
```
"redirect_port" rules should be defined within nat config. There all incoming connections what will came in to you external ip:80 will be passed to 10.10.0.5:80 and vice versa.

In the same time "redirect_addr" to work should be defined in separate nat what do not have "ip" or "if" keywords:

```
ipfw nat 1 config same_ports redirect_addr 192.168.1.2 8.9.7.1
```


----------



## MisterMinit0815 (Jul 20, 2009)

Hello,

no, it doesn't work at all, now i can see int tcpdump that the packet comes and an "ack" goes back, but not more.

```
21:45:40.624958 IP 80.82.221.44.62112 > 193.238.192.117.80: S 2155569910:2155569910(0) win 65535 <mss 1440,nop,wscale 3,sackOK,timestamp 1412066391 0>
21:45:40.625007 IP 10.10.0.5.80 > 80.82.221.44.62112: R 0:0(0) ack 2155569911 win 0
```

I don't know what to do, the same Firewall Rules works fine for Years with the NATD but now when i use In-Kernel NAT it doesn't work.

Do you have any other ideas?


Best regards,
MisterMinit0815


----------



## DutchDaemon (Jul 20, 2009)

That was not really an ack, that was a tcp reset (R 0:0(0)) a.k.a. connection refused. I forget how ipfw does this, but with pf you have to create an additional 'pass' rule to make a redirect work.


----------



## MisterMinit0815 (Jul 21, 2009)

DutchDaemon you're right, the Problem is that i've a firewall rule that looks like:

```
${fwcmd} add 2000 pass tcp from any to 10.10.0.5 80 via ${extnet_if} keep-state
```

That rule is the problem, if i change that rules like:

```
${fwcmd} add 2000 pass tcp from any to 10.10.0.5 80 keep-state
```
it works fine, but now the port of the server is open for every net, i'm using 3 different networks here, and not all networks should have access to the other networks.

Why does it work if i remove the incoming device?


Thanks in advance.
MisterMinit


----------



## terminus (Jul 21, 2009)

Be aware of sysctl net.inet.ip.fw.one_pass value. If it is set to 1 then nat rules act as allow for traffic passing through nat. If it is set to 0 then after nating traffic be reinjected back to ipfw and you should have there some extra rules what will act as allow for this traffic.

Can you show all you ipfw rules? It would be much easier to troubleshoot you issue by looking to current ruleset. The example I gave is working (at least for me).


----------



## MisterMinit0815 (Jul 22, 2009)

Here are some of the Firewall-Rules incl. NAT:

NAT:

```
${fwcmd} nat 100 config if ${extnet_if} log reset unreg_only same_ports redirect_port udp 10.10.0.6:5060 5060 redirect_port udp \
10.10.0.6:10005-20005 10005-20005 redirect_port tcp 10.10.0.5:80 80 redirect_port tcp 10.10.0.5:21 21 redirect_port \
tcp 10.10.0.5:9500-10500 9500-10500
${fwcmd} add 50 nat 100 all from any to any via ${extnet_if}
```

Paket-Filter:

```
${fwcmd} add 2500 pass udp from any to 10.10.0.6 5060 keep-state
${fwcmd} add 2501 pass udp from any to 10.10.0.6 10005-20005 keep-state
${fwcmd} add 2502 pass udp from any to ${extnet_ip} 38974 keep-state
${fwcmd} add 2503 pass tcp from any to 10.10.0.5 80 keep-state
${fwcmd} add 2504 pass tcp from any to 10.10.0.5 21 keep-state
${fwcmd} add 2505 pass tcp from any to 10.10.0.5 9500-10500 keep-state
```

The sysctl net.inet.ip.fw.one_pass is set to "1" but the Redirect don't work if i use "via ${extnet_if}" in the firewall rules. But without the interface in the rules the ip's can be connected from "any" also from all internal Networks, and i don't want this.
The NAT Rules don't work as "allow rules", if i remove the firewall-rules 2500-2505 the traffic will be blocked. 

The next Problem is that i have a "logical string limit" for the nat rule, there must be a way to define a nat rule for every single redirect_port, can anyone help me to define that rules?



What can i do now?




MisterMinit0815


----------



## MisterMinit0815 (Jul 22, 2009)

I found a nice script where every redirect is in a seperate NAT Rule, this looks like:

```
${fwcmd} nat 1 config if ${extnet_if} log redirect_port udp 10.10.0.6:5060 ${extnet_ip}:5060
${fwcmd} add 51 nat 1 udp from any to ${extnet_ip} 5060
${fwcmd} nat 2 config if ${extnet_if} log redirect_port udp 10.10.0.6:10005-20005 ${extnet_ip}:10005-20005
${fwcmd} add 52 nat 2 udp from any to ${extnet_ip} 10005-20005
${fwcmd} nat 3 config if ${extnet_if} log redirect_port tcp 10.10.0.5:80 ${extnet_ip}:80
${fwcmd} add 53 nat 3 tcp from any to ${extnet_ip} 80
${fwcmd} nat 4 config if ${extnet_if} log redirect_port tcp 10.10.0.5:21 ${extnet_ip}:21
${fwcmd} add 54 nat 4 tcp from any to ${extnet_ip} 21
${fwcmd} nat 5 config if ${extnet_if} log redirect_port tcp 10.10.0.5:9500-10500 ${extnet_ip}:9500-10500
${fwcmd} add 55 nat 5 tcp from any to ${extnet_ip} 9500-10500

${fwcmd} nat 99 config if ${extnet_if} log reset unreg_only same_ports
${fwcmd} add 99 nat 99 all from any to any via ${extnet_if}
```


This works fine now.

But the Problem with the Firewall-Rules is still there, so if i remove the "via ${extnet_if}" from the Filewall-Rules the NAT doesn't work any more. Any good ideas?

Best regards,
MisterMinit0815


----------



## terminus (Jul 22, 2009)

Remember about "two pass" nature of ipfw. All traffic what pass through you router will go into ipfw two times - first time in IN pass (then is is receiwed from any network) and second time in OUT pass (then it goes back to any network).

And sysctl one_pass actualy have impact on ipfw nat - see ipwf man page in nat section:


> To let the packet continue after being (de)aliased, set the sysctl vari-
> able net.inet.ip.fw.one_pass to 0.



The examples you use it too complicated. I think it all can be achieved with more simple ruleset. For examle let's say that you have 3 network adapters:
fxp0 - Internet side ${extnet_if}
fxp1 - DMZ ${dmz_if}
fxp2 - Local intranet ${intranet_if}
sysctl one_pass=1

then IMHO suitable and simple ruleset will be:


```
${fwcmd} add 10 deny all from any to any recv ${intranet_if} xmit ${dmz_if}
${fwcmd} add 11 deny all from any to any recv ${dmz_if} xmit ${intranet_if}

${fwcmd} nat 1 config if ${extnet_if} log deny_in reset unreg_only same_ports redirect_port udp 10.10.0.6:5060 5060 redirect_port udp 10.10.0.6:10005-20005 10005-20005 redirect_port tcp 10.10.0.5:80 80

${fwcmd} add 20 nat 1 all from any to any via ${extnet_if}

${fwcmd} add 30 allow all from any to any via ${dmz_if}
${fwcmd} add 31 allow all from any to any via ${intranet_if}
```

IMHO it should work fine - DMZ and Intranet can not communicate directly and all traffic is passed via one nat instance... 

---

And yeah - about "logical string limit". I do not have this issue (may be it is because I'm using 7.2-STABLE and there this problem recently have been fixed?). Here is working example from my system:

```
# ipfw nat 1 show config
ipfw nat 1 config if em0 log deny_in same_ports unreg_only reset redirect_port tcp 192.168.1.2:2722 2722 redirect_port udp 192.168.1.2:2734 2734 redirect_port tcp 192.168.1.2:2744 2744 redirect_port tcp 192.168.1.2:274 274 redirect_port tcp 192.168.1.2:272 272 redirect_port tcp 192.168.1.2:275 275 redirect_port tcp 192.168.1.2:279 279 redirect_port tcp 192.168.1.2:270 270 redirect_port tcp 192.168.1.2:50-60 50-60 redirect_port tcp 192.168.1.2:30 30 redirect_port udp 192.168.1.2:29 29 redirect_port udp 192.168.1.2:28 28 redirect_port tcp 192.168.1.2:27 27 redirect_port tcp 192.168.1.2:26 26 redirect_port tcp 192.168.1.2:25 25 redirect_port tcp 192.168.1.2:24 24 redirect_port tcp 192.168.1.2:23 23 redirect_port tcp 192.168.1.2:22 22
```

To solve "logical string limit" problem looks like there is nothing to do, and rules should be spilet into multile nat instances:

```
${fwcmd} add 10 deny all from any to any recv ${intranet_if} xmit ${dmz_if}
${fwcmd} add 11 deny all from any to any recv ${dmz_if} xmit ${intranet_if}

${fwcmd} nat 1 config if ${extnet_if} log deny_in redirect_port udp 10.10.0.6:5060 5060
${fwcmd} add 51 nat 1 udp from any to ${extnet_ip} 5060 via ${extnet_if}
${fwcmd} add 52 nat 1 udp from 10.10.0.6 5060 to any via ${extnet_if}

${fwcmd} nat 2 config if ${extnet_if} log deny_in redirect_port udp 10.10.0.6:10005-20005 10005-20005
${fwcmd} add 53 nat 2 udp from any to ${extnet_ip} 10005-20005 via ${extnet_if}
${fwcmd} add 54 nat 2 udp from 10.10.0.6 10005-20005 to any via ${extnet_if}

${fwcmd} nat 3 config if ${extnet_if} log deny_in redirect_port tcp 10.10.0.5:80 80
${fwcmd} add 55 nat 3 tcp from any to ${extnet_ip} 80 via ${extnet_if}
${fwcmd} add 56 nat 3 tcp from 10.10.0.5 80 to any via ${extnet_if}


${fwcmd} nat 4 config if ${extnet_if} log deny_in redirect_port tcp 10.10.0.5:21 21
${fwcmd} add 57 nat 4 tcp from any to ${extnet_ip} 21 via ${extnet_if}
${fwcmd} add 58 nat 4 tcp from 10.10.0.5 21 to any via ${extnet_if}

${fwcmd} nat 5 config if ${extnet_if} log deny_in redirect_port tcp 10.10.0.5:9500-10500 9500-10500
${fwcmd} add 59 nat 5 tcp from any to ${extnet_ip} 9500-10500 via ${extnet_if}
${fwcmd} add 60 nat 5 tcp from 10.10.0.5 9500-10500 to any via ${extnet_if}

${fwcmd} nat 100 config if ${extnet_if} log deny_in reset unreg_only same_ports
add 100 nat 100 all from any to any via ${extnet_if}

${fwcmd} add 200 allow all from any to any via ${dmz_if}
${fwcmd} add 201 allow all from any to any via ${intranet_if}
```

Can you try this config? I'm realy interested in result


----------

