145

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
  • 539
  • 7
  • 27
Stu
  • 2,198
  • 2
  • 16
  • 23

8 Answers8

118

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
  • 2,040
  • 1
  • 14
  • 10
oo_olo_oo
  • 1,296
  • 1
  • 9
  • 3
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
  • 8
  • 21
Zealotous
  • 189
  • 1
  • 2
16

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,676
  • 3
  • 21
  • 30
  • 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. – Mike Nov 28 '21 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... :-) – Steve Summit May 30 '23 at 22:59
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

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,780
  • 21
  • 75
  • 114
Andrew
  • 1