How to formulate IP forwarding rule using iptables

I have two Systems A and B. A is a TCP Client and sends a message to TCP Server on B.

------------------                --------------------------

  System A                        System B
  192.168.0.5 wlan0               192.168.0.3 wlan0
  127.0.0.1   lo                  127.0.0.1 lo
  TCP Client    <------------>    TCP Server on 127.0.0.1
------------------                ----------------------------

The TCP Client sends message to 192.168.0.3.

This should be redirected to the local interface of B as the TCP Server is running on 127.0.0.1 at Port 8000 of System B.

Therefore, I wrote the following ip table rules, however my Server at B doesn't receive any messages. Oh btw these two systems are Ubuntu linux systems.

Here is what I did on System B:

#Enable IP Forwarding for NAT
echo "1" > /proc/sys/net/ipv4/ip_forward

#Flush all iptable chains and start afresh
sudo iptables -F

#Forward incoming packets on 192.168.0.3 at wlan0 interface to 127.0.0.1
sudo iptables -t nat -A PREROUTING -p tcp -i wlan0 -d 192.168.0.3 --dport 8000 -j DNAT --to 127.0.0.1:8000

#Explicitly allow incoming connections on port 8000 
sudo iptables -A INPUT -i wlan0 -p tcp --dport 8000 -m state --state NEW,ESTABLISHED -j ACCEPT

#Explicitly allow outgoing messages from port 8000
sudo iptables -A OUTPUT -o wlan0 -p tcp --sport 8000 -m state --state ESTABLISHED -j ACCEPT

Then I start the Server on B and send a message from TCP Client on A. I can see the packets on wireshark from 192.168.0.5 on wlan0 but they never get forwarded :(

Please help.

UPDATE:

After inputs from experts here, I have made a more realistic "NAT" scenario for applying the forwarding rules but I have still issues: I have explained this in my newer post: Iptables: Forwarding packets doesn't work


Solution 1:

Here is a total non-answer answer.
as Jens Bradler said in his comment, the simplest thing to do here is to bind the service to the public IP address on port 8000, rather than NAT the connection. You can secure access to the single Server A by iptables rules like so;

 -A INPUT -s 192.168.0.5/32 -p tcp -m tcp --dport 8000 -j ACCEPT
 -A INPUT -p tcp -m tcp --dport 8000 -j REJECT

Solution 2:

Tom H's non-answer answer is better, but if you don't like it, here is a direct answer:

Sorry, my iptables skills are best when I can tinker, test, check, logs, repeat... and I don't have your system to play with, but here is my advice anyway. You probably will need to debug using the logs.

Change the direction of your redirect, as stated by Jens Bradler:

echo "1" > /proc/sys/net/ipv4/ip_forward

Prove it is right

cat /proc/sys/net/ipv4/ip_forward

Output from cat:

1

Add a rule to ACCEPT using the FORWARD table also.

sudo iptables -A FORWARD -i wlan0 -p tcp --dport 8000 -j ACCEPT

Remove the "-m state" stuff from the incoming rule (optional... unnecessary I think):

sudo iptables -A INPUT -i wlan0 -p tcp --dport 8000 -j ACCEPT

Probably anywhere that you added "ESTABLISHED", you should also have "RELATED" (Not sure but I think a return packet that starts a connection is related, but not established).

sudo iptables -A OUTPUT -o wlan0 -p tcp --sport 8000 -m state --state RELATED,ESTABLISHED -j ACCEPT

The FORWARD rule to go with the above accept, reverse of the other FORWARD.

sudo iptables -A FORWARD -o wlan0 -p tcp -s 127.0.0.1 --sport 8000 -m state --state RELATED,ESTABLISHED -j ACCEPT

Instead of using Wireshark, use -j LOG.

First check to see that there aren't already LOG rules (the default ones are better than the following... but you used flush; mine will spam your server hard):

sudo iptables --list --line-numbers -v
sudo iptables -t nat --list --line-numbers -v

If not, then add them:

sudo iptables -A INPUT -j LOG
sudo iptables -A OUTPUT -j LOG
sudo iptables -A FORWARD -j LOG
sudo iptables -t nat -A PREROUTING -j LOG

Watch the logs

tail -F /var/log/firewall

# or if that file doesn't exist:
tail -F /var/log/messages

Solution 3:

You can't ever perform -j DNAT --to 127.0.0.1.

127.0.0.1 is a super-duper-special address; it would only accept connections from 127.0.0.1.

@Tom H's answer is the way you should set up your server.

Solution 4:

System B:

sysctl -w net.ipv4.conf.wlan0.route_localnet=1
iptables -t nat -A PREROUTING -i wlan0 -p tcp --dport 8080 -j DNAT --to 127.0.0.1