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?

Stu
  • 2,258

8 Answers8

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.

Kousha
  • 2,050
  • 1
  • 14
  • 11
oo_olo_oo
  • 1,346
  • 1
  • 9
  • 3
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

rustyx
  • 1,979
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.

Zealotous
  • 199
  • 1
  • 2
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
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.

Nullivex
  • 101
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.

Adam Liss
  • 191
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
  • 28,400
  • 21
  • 80
  • 117
Andrew
  • 1