138

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?

Dessa Simpson
  • 491
  • 7
  • 25
Stu
  • 2,118
  • 2
  • 15
  • 21

8 Answers8

112

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.

Kousha
  • 1,920
  • 1
  • 13
  • 10
oo_olo_oo
  • 1,236
  • 1
  • 9
  • 3
  • 17
    I personally prefer the `sysctl` syntax like `sysctl net.ipv4.conf.eth0.forwarding=1` – Doud Sep 07 '12 at 21:10
  • 1
    How do I remove the incorrectly entered rule? – Nickolai Leschov Apr 13 '14 at 02: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) – THESorcerer Aug 28 '14 at 13:34
  • Will these rules be persisted if I reboot the computer? – Aron Lorincz Dec 02 '15 at 16:50
  • 2
    @ÁronLőrincz, No. Iptables rules are volatile unless explicitly loaded at boot. – sherrellbc Jan 23 '17 at 20:59
  • 2
    @Nickolai Leschov, enter same replacing `-A` with `-D` – Alexei Martianov Apr 06 '18 at 01:27
18

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.

Yves Martin
  • 879
  • 3
  • 7
  • 21
Zealotous
  • 189
  • 1
  • 2
  • You got it backwards on the `POSTROUNTING` step. At this point the conversation is still about `--destination` rather than `--source`. – sherrellbc Jan 23 '17 at 21:04
16

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
Robert Gamble
  • 293
  • 1
  • 8
  • 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. –  Dec 05 '08 at 22:21
  • Okay, the syntax looked bad in the original. Did you try -i ppp0 in the rules? What is the problem exactly? – Robert Gamble Dec 05 '08 at 23:17
9

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 

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
rustyx
  • 1,506
  • 3
  • 19
  • 28
  • 2
    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. – Mayki Nayki Nov 28 '21 at 20:34
9

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.

Nullivex
  • 91
  • 1
  • 1
3

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
    
1

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.

Adam Liss
  • 181
  • 3
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 :)

Jenny D
  • 27,358
  • 21
  • 74
  • 110
Andrew
  • 1