Modify

Opened 8 years ago

Last modified 5 years ago

#1025 investigate enhancement

Port forwarding does not work from the internal network

Reported by: matthijs Owned by:
Priority: normal Milestone:
Component: fon-network Version: 2.3.6.1 (Gari jr.)
Severity: normal
Cc: jon@… Hardware: both

Description

Due to a limitation in how DNAT forwarding works, a port forward set up in the Settings -> Firewall -> Port forwarding section does not work from the internal LAN.

This is a well-known problem for port forwarding, which I'll illustrate with an example. Say way forward port 1000 to a server at 192.168.1.100. Suppose the public address of our Fonera is 203.0.113.1.

When a client connects from the internet to 203.0.113.1:1000, the Fonera receives the request packet on its WAN port, changes the destination address into 192.168.1.100:1000 and sends it to the server on the LAN network. The server processes the request and sends a reply back to the internet client. This reply has to pass the Fonera on its way to the internet, where the Fonera can change the source address this time, from the 192.168.1.100 set by the server to 203.0.113.1 again (which is what the internet client expects).

Now, when a client from the LAN, say 192.168.1.200, connects to 203.0.113.1, things get different. The packet gets passed to the Fonera (on its LAN port), since the client thinks it should go onto the internet. The Fonera then changes the destination address into 192.168.1.100 and sends the request off to the server. The source address of the request packet is unmodified, so it's still 192.168.1.200. This means the reply packet gets sent to 192.168.1.200 as well. The server can now send this reply directly to the client, bypassing the Fonera, since it knows the client is on the same network. However, this also means that the Fonera does not get the chance to "undo" the address translation on the reply packet. The client now gets a reply from the server, 192.168.1.100, but it has sent a request to the WAN ip, 203.0.113.1, so it discards the packet.

Hence, using just a DNAT rule for port forwarding doesn't work for internal requests. There is a solution for this: When a request comes from the internal network, do SNAT in addition to DNAT. This also rewrites the source address of a packet, making it look like it comes from the Fonera. This causes the internal server to send replies back to the Fonera, instead of directly to the client.

Care should be taken to only apply the SNAT to packets that need it (i.e., come from the internal network). Using SNAT in this way screws up request logging on the server, since all requests seem to come from the Fonera, the server never learns the real source of the request.

As an additional not: The current (2.3.6.1) firmware is already selectively applying the DNAT forwarding to packets coming from the WAN port. This means that forwarding from the LAN port just doesn't happen and the above explanation is not completely applicable.

Furthermore, I've looked around a bit for implementing this, but there's a few problems:

  • In the fw_redirect function that adds the forwarding rules, we only know the "src" interface, not the "dst" interface (which is the one that needs special treatment).
  • It requires changing the uci_firewall code quite a bit (and also the meaning of 'redirect' entries in the firewall uci config. This might cause problems on the long run, when upgrading to a newer OpenWRT version.

Even with the above problems, it seems it might be useful to implement this anyway. The main advantage is that people can actually test their port forwards from their internal network. Currently a lot of people think their port forwards don't work because they (only) test from their local network...

Attachments (0)

Change History (16)

comment:1 Changed 8 years ago by matthijs

An additional problem is that the SNAT rule required needs to have the IP address specified. Just using the "lan" ip address will probably work, though.

comment:2 Changed 8 years ago by matthijs

And some experimentation shows this is harder than it seems: The SNAT rule must be in the POSTROUTING chain, but you can't using -i (to select the incoming interface) in POSTROUTING, so doing SNAT on packets coming from the lan only doesn't work. This could be worked around by matching on the source address, but it's hard to do that generically and reliably, I'm afraid.

Blogic also suggested that doing both SNAT and DNAT is prone to failure and security problems, so for now, I'll leave this ticket. Perhaps we'll revisit this later on, until then, users should just read the documentation :-)

comment:3 Changed 8 years ago by koter84@…

i would also love this function..

could this be implemented with an extra firewall rule perhaps? i wouldn't mind making 2 rules, one for internal and one for external use..

comment:4 Changed 8 years ago by matthijs

Sorry, I don't really see how to do this reliably, even with custom iptables rules...

comment:5 Changed 8 years ago by koter84@…

i found this script for openwrt on their forums, maybe you (or Blogic) can understand that and use it for the fonera?

OpenWRT NAT Loopback

comment:6 Changed 8 years ago by matthijs

Looking at the script, it uses SNAT+DNAT and uses source address matching against the LAN network IP address in the POSTROUTING chain. So apparently that works, but it's probably hard to make it work in all cases still. If I have some spare time, I might try whipping up a patch for people wanting to make this work manually, but I doubt we will support this in the official firmware...

comment:7 Changed 7 years ago by guyomel@…

So is there a workaround for this bug ?

It's a major issue for me since I host my server at home. Without this feature, I cannot access to my mail, blog, etc.. from LAN. And I can not either set up my phone and laptops directly with private IP. :-(

comment:8 Changed 7 years ago by giuseppeg88@…

From the forum (by guyome):

So, I found a workaround based on this

You modify /etc/firewall.user as following

# get the WANIP
WANIP=`ifconfig YOURINTERFACE | awk '/inet addr/ {split ($2,A,":"); print A[2]}'`

# my lan ip
LAN=IP.OF.YOU.NETWORK/MASK

nat_loopback() {
        iptables -t nat -I zone_lan_prerouting -d $WANIP -p $3 --dport $1 -j DNAT --t
        iptables -I zone_lan_forward -p $3 --dport $1 -d $2 -j ACCEPT
        iptables -t nat -A postrouting_rule -s $LAN -p $3 --dport $1 -d $2 -j SNAT  --to-source $WANIP
}

nat_loopback YOURPORT IP.OF.YOUR.SERVER TCPORUDP

Then a little

/etc/init.d/firewall reload

And you get it !

comment:9 Changed 6 years ago by matthijs

  • Cc jon@… added
  • Status changed from new to investigate

#444 is a duplicate.

comment:10 Changed 5 years ago by emile@…

In the current version (2.3.7.1) I can't get the fix to work. This is my /etc/firewall.user

WANIP=`ifconfig eth0 | awk '/inet addr/ {split ($2,A,":"); print A[2]}'`

# my lan ip
LAN=10.0.0.0/24

nat_loopback() {
        iptables -t nat -I zone_lan_prerouting -d $WANIP -p $3 --dport $1 -j DNAT --t
                iptables -I zone_lan_forward -p $3 --dport $1 -d $2 -j ACCEPT
                        iptables -t nat -A postrouting_rule -s $LAN -p $3 --dport $1 -d $2 -j SNAT
                        }

nat_loopback 9000 10.0.0.138 tcp

When reloading I'm told:

 /etc/firewall.user
Bad argument `tcp'
Try `iptables -h' or 'iptables --help' for more information.
iptables v1.4.0: Unknown arg `--to-source'
Try `iptables -h' or 'iptables --help' for more information.

Anybody any thought on this?

comment:11 Changed 5 years ago by matthijs

Could you try running:

root@Fonera:~# sh -x /etc/firewall.user

That should print the exact commands executed and might provide some more insight in what's going wrong.

comment:12 Changed 5 years ago by anonymous

this is the output of sh - x /etc/firewall.user

+ ifconfig eth0
+ awk /inet addr/ {split ($2,A,":"); print A[2]}
+ WANIP=
+ LAN=10.0.0.0/24
+ nat_loopback 9000 10.0.0.138 tcp
+ iptables -t nat -I zone_lan_prerouting -d -p tcp --dport 9000 -j DNAT --t
Bad argument `tcp'
Try `iptables -h' or 'iptables --help' for more information.
+ iptables -I zone_lan_forward -p tcp --dport 9000 -d 10.0.0.138 -j ACCEPT
+ iptables -t nat -A postrouting_rule -s 10.0.0.0/24 -p tcp --dport 9000 -d 10.0.0.138 -j SNAT --to-source
iptables v1.4.0: Unknown arg `--to-source'
Try `iptables -h' or 'iptables --help' for more information.

I hope this helps.

comment:13 Changed 5 years ago by anonymous

Fixed my own problem. WRONG: ifconfig eth0 RIGHT: ifconfig eth0.2

also I was missing:

  --to-source $WANIP

in line:

iptables -t nat -A postrouting_rule -s $LAN -p $3 --dport $1 -d $2 -j SNAT  --to-source $WANIP

(copy/paste error)

comment:14 Changed 5 years ago by matthijs

Good to hear it's fixed :-)

comment:15 Changed 5 years ago by emile@…

Well, almost... I had to modify first line of nat_looback like so:

nat_loopback() {
        iptables -t nat -I zone_lan_prerouting -d $WANIP -p $3 --dport $1 -j DNAT --to $2
        iptables -I zone_lan_forward -p $3 --dport $1 -d $2 -j ACCEPT
        iptables -t nat -A postrouting_rule -s $LAN -p $3 --dport $1 -d $2 -j SNAT --to-source $WANIP
        }

And now it works (hope I didn't create a major security hole in doing so)

comment:16 Changed 5 years ago by matthijs

Right, it looks like that was a copy-paste error in the original posting of that script :-) The first line has --t, which does not make sense as an iptables option, so that probably had to be {{{--to $2}} (which makes sense to me),

Add Comment

Modify Ticket

Action
as investigate The ticket will remain with no owner.
Author


E-mail address and user name can be saved in the Preferences.

 
Note: See TracTickets for help on using tickets.