151

I want connections coming in on ppp0 on port 8001 to be routed to 192.168.1.200 on eth0 on port 8080.

I've got these two rules

-A PREROUTING -p tcp -m tcp --dport 8001 -j DNAT --to-destination 192.168.1.200:8080 -A FORWARD -m state -p tcp -d 192.168.1.200 --dport 8080 --state NEW,ESTABLISHED,RELATED -j ACCEPT 

and it doesn't work. What am I missing?

4
  • 7
    NAT HOWTO Commented Dec 5, 2008 at 21:11
  • I'm going to go with the n-p-r tag (although this could be programming-related, though poorly phrased of course.) Commented Dec 5, 2008 at 22:12
  • 2
    How about this: I'm a programmer trying to set up an environment so I can debug my server application in eclipse being called from the innernet. Close enough? Commented Dec 5, 2008 at 22:30
  • Sure, that's what I meant by "poorly phrased"... Could you edit the question accordingly? Commented Dec 5, 2008 at 23:19

8 Answers 8

123

First of all - you should check if forwarding is allowed at all:

cat /proc/sys/net/ipv4/conf/ppp0/forwarding cat /proc/sys/net/ipv4/conf/eth0/forwarding 

If both returns 1 it's ok. If not do the following:

echo '1' | sudo tee /proc/sys/net/ipv4/conf/ppp0/forwarding echo '1' | sudo tee /proc/sys/net/ipv4/conf/eth0/forwarding 

Second thing - DNAT could be applied on nat table only. So, your rule should be extended by adding table specification as well (-t nat):

iptables -t nat -A PREROUTING -p tcp -i ppp0 --dport 8001 -j DNAT --to-destination 192.168.1.200:8080 iptables -A FORWARD -p tcp -d 192.168.1.200 --dport 8080 -m state --state NEW,ESTABLISHED,RELATED -j ACCEPT 

Both rules are applied only to TCP traffic (if you want to alter UDP as well, you need to provide similar rules but with -p udp option set).

Last, but not least is routing configuration. Type:

ip route 

and check if 192.168.1.0/24 is among returned routing entries.

9
  • 18
    I personally prefer the sysctl syntax like sysctl net.ipv4.conf.eth0.forwarding=1 Commented Sep 7, 2012 at 21:10
  • 1
    How do I remove the incorrectly entered rule? Commented Apr 13, 2014 at 2:51
  • 3
    second line: "iptables -A FORWARD -p tcp -d 192.168.1.200 --dport 8080 -m state --state NEW,ESTABLISHED,RELATED -j ACCEPT" is NOT required if you don't have firewall restrictions/security, which is the case with most of home LANs, otherwise be careful with -A, be cause it will add it AFTER restrictions/security and may not work (so check -I instead, that is adding IN FRONT of iptables rules) Commented Aug 28, 2014 at 13:34
  • 3
    @ÁronLőrincz, No. Iptables rules are volatile unless explicitly loaded at boot. Commented Jan 23, 2017 at 20:59
  • 3
    @Nickolai Leschov, enter same replacing -A with -D Commented Apr 6, 2018 at 1:27
27

The accepted solution works when the destination host and the gateway are on the same subnet (like is in your case, both are on eth0 192.168.1.0/24).

Below is a generic solution for when the gateway, source and destination are all on different subnets.

1) Enable IP forwarding:

sysctl net.ipv4.conf.eth0.forwarding=1 sysctl net.ipv6.conf.eth0.forwarding=1 

//note: if forwarding to/from localhost, also set sysctl net.ipv4.conf.eth0.route_localnet=1


2) Add 2 iptables rules to forward a specific TCP port:

To rewrite the destination IP of the packet (and back in the reply packet):

iptables -A PREROUTING -t nat -p tcp -i ppp0 --dport 8001 -j DNAT --to-destination 192.168.1.200:8080 

To rewrite the source IP of the packet to the IP of the gateway (and back in the reply packet):

iptables -A POSTROUTING -t nat -p tcp -d 192.168.1.200 --dport 8080 -j MASQUERADE 

3) If you don't have a default ACCEPT firewall rule, allow traffic to the destination:

iptables -A FORWARD -p tcp -d 192.168.1.200 --dport 8080 -m state --state NEW,ESTABLISHED,RELATED -j ACCEPT 

4) Test the new setup. If it works, make sure the changes persist across reboots:

cat <<EOF > /etc/sysctl.d/99-forwarding.conf sysctl net.ipv4.conf.eth0.forwarding=1 sysctl net.ipv6.conf.eth0.forwarding=1 EOF iptables-save > /etc/network/iptables.up.rules echo '#!/bin/sh' > /etc/network/if-pre-up.d/iptables echo "`which iptables-restore` < /etc/network/iptables.up.rules" >> /etc/network/if-pre-up.d/iptables chmod +x /etc/network/if-pre-up.d/iptables 
2
  • 4
    I guess that should be the accepted answer because the others do not consider the POSTROUTING part which solves the problem of calling wanIP:8081 from the LAN. Commented Nov 28, 2021 at 20:34
  • This worked for me, first time, after months of flailing with various inferior alternatives. Most excellent. I guess someone named "rusty" can be trusted when it comes to iptables... :-) Commented May 30, 2023 at 22:59
19

You forget postrouting source address SNAT 'ing:

sysctl net.ipv4.ip_forward=1 yours_wan_ip=101.23.3.1 -A PREROUTING -p tcp -m tcp -d $yours_wan_ip --dport 8001 -j DNAT --to-destination 192.168.1.200:8080 -A FORWARD -m state -p tcp -d 192.168.1.200 --dport 8080 --state NEW,ESTABLISHED,RELATED -j ACCEPT -A POSTROUTING -t nat -p tcp -m tcp -s 192.168.1.200 --sport 8080 -j SNAT --to-source $yours_wan_ip 

And don't forget to set your linux firewall as default gateway on computer with 192.168.1.200 address.

2
  • 1
    You got it backwards on the POSTROUNTING step. At this point the conversation is still about --destination rather than --source. Commented Jan 23, 2017 at 21:04
  • embeded system -/bin/sh: sysctl: not found Commented Dec 20, 2022 at 12:12
18

I think what you want is:

iptables -A FORWARD -m state -p tcp -d 192.168.1.200 --dport 8080 --state NEW,ESTABLISHED,RELATED -j ACCEPT iptables -t nat -A PREROUTING -p tcp --dport 8001 -j DNAT --to-destination 192.168.1.200:8080 
2
  • 3
    ummm... that IS what I have already. I use iptables-restore to load it so each is in its own section, but that's what I wrote above. Commented Dec 5, 2008 at 22:21
  • Okay, the syntax looked bad in the original. Did you try -i ppp0 in the rules? What is the problem exactly? Commented Dec 5, 2008 at 23:17
10

I have created the following bash script for doing this on my linux router. It automatically infers the WAN IP and confirms your selections before proceeding.

#!/bin/bash # decide which action to use action="add" if [[ "-r" == "$1" ]]; then action="remove" shift fi # break out components dest_addr_lan="$1" dest_port_wan="$2" dest_port_lan="$3" # figure out our WAN ip wan_addr=`curl -4 -s icanhazip.com` # auto fill our dest lan port if we need to if [ -z $dest_port_lan ]; then dest_port_lan="$dest_port_wan" fi # print info for review echo "Destination LAN Address: $dest_addr_lan" echo "Destination Port WAN: $dest_port_wan" echo "Destination Port LAN: $dest_port_lan" echo "WAN Address: $wan_addr" # confirm with user read -p "Does everything look correct? " -n 1 -r echo # (optional) move to a new line if [[ $REPLY =~ ^[Yy]$ ]]; then if [[ "remove" == "$action" ]]; then iptables -t nat -D PREROUTING -p tcp -m tcp -d $wan_addr --dport $dest_port_wan -j DNAT --to-destination $dest_addr_lan:$dest_port_lan iptables -D FORWARD -m state -p tcp -d $dest_addr_lan --dport $dest_port_lan --state NEW,ESTABLISHED,RELATED -j ACCEPT iptables -t nat -D POSTROUTING -p tcp -m tcp -s $dest_addr_lan --sport $dest_port_lan -j SNAT --to-source $wan_addr echo "Forwarding rule removed" else iptables -t nat -A PREROUTING -p tcp -m tcp -d $wan_addr --dport $dest_port_wan -j DNAT --to-destination $dest_addr_lan:$dest_port_lan iptables -A FORWARD -m state -p tcp -d $dest_addr_lan --dport $dest_port_lan --state NEW,ESTABLISHED,RELATED -j ACCEPT iptables -t nat -A POSTROUTING -p tcp -m tcp -s $dest_addr_lan --sport $dest_port_lan -j SNAT --to-source $wan_addr echo "Forwarding rule added" fi else echo "Info not confirmed, exiting..." fi 

The use of the script is simple just copy and paste it to a file and then.

# chmod +x port_forward.sh # ./port_forward.sh 192.168.1.100 3000 ... confirm details ... press y # Forwarding rule added 

To remove the same rule

# ./port_forward.sh -r 192.168.1.100 3000 ... confirm details ... press y # Forwarding rule removed 

I thought this might save someone time on their respective router.

4

I had the task to make MACHINE_A into thinking that the service is running physically on MACHINE_B, but transparently re-route all requests to MACHINE_C.

The trick was to use MASQUERADE.

sysctl net.ipv4.ip_forward=1 iptables -t nat -A PREROUTING -p tcp -d MACHINE_B --dport 443 -j DNAT --to-destination MACHINE_C iptables -t nat -A POSTROUTING -s MACHINE_A -o INTERFACE_NAME -j MASQUERADE 

Please note that you might want to tweak the commands:

  1. To allow packet forwardning on a specific interface only. For example:

    sysctl net.ipv4.conf.eth0.forwarding=1 
  2. To allow not only MACHINE_A, but also all others to use port forwarding, remove:

    -s MACHINE_A 
2

Try

echo "1" > /proc/sys/net/ipv4/conf/ppp0/forwarding echo "1" > /proc/sys/net/ipv4/conf/eth0/forwarding 

These files tell the kernel it's allowed to forward packets between the interfaces.

0

This command doesn't work for me:

-A POSTROUTING -t nat -p tcp -m tcp -s 192.168.1.200 --sport 8080 -j SNAT --to-source $yours_wan_ip 

I have 2 LAN interfaces and FORWARD work when I'll written:

iptables -t nat -A POSTROUTING -o $LAN_IF -p tcp -m tcp --dport $FW_PORT -j SNAT --to-source $LAN_IP 
  • LAN_IF - LAN interface (eg. eth1, br0...)
  • FW_PORD - forwarded port (on detination host)
  • LAN_IP - IP address on LAN interface (on the router)

PREROUTING and FORWARD are necessary too, of course :)

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.