My problem is that when I print out the socket information using getsockname and getpeername, the source address is always the server's. If I connect to the server directly through a port that is not affected by the iptables rule I see the client address as expected. Why is port forwarding affecting the source address?
I'll explain, but be warned that this is going to be a long explanation.
Let's first understand what is a TCP Connection: It is a stateful connection-oriented data interchange between an 'initiator tuple'
(IP,Port) and a 'terminator tuple'
In other words, the TCP Connection is defined by a four-ple of
This means, all information transfer for that TCP Connection needs to fulfill the four-ple definition.
Now, let's assume that your server is located at 18.104.22.168, and the client at 22.214.171.124. The client opens the connection to your server's port 80. Assuming the client's ephemeral port is 34567, we have the four-ple (126.96.36.199,34567,188.8.131.52,80).
On your server, the packet gets redirected to 184.108.40.206:10080. But this results in a different four-ple of connection! E.g., (220.127.116.11,34567,18.104.22.168,10080).
What will the actual listener do? Why it would respond directly to 22.214.171.124:34567, resulting in the packet to be dropped by the client, because the client has the four-ple of (126.96.36.199,34567,188.8.131.52,80) and not (184.108.40.206,34567,220.127.116.11,10080).
So, in this case, the
REDIRECT target have to create a temporary four-ple mapping:
(18.104.22.168,34567,22.214.171.124,80) --> (126.96.36.199,x,188.8.131.52,10080)
(x is a temporary, ephemeral port to allow TCP Connection to be built).
With this temporary mapping, the server at 10080 responds back to
netfilter, and a reverse mapping happens:
(184.108.40.206,x,220.127.116.11,10080) --> (18.104.22.168,34567,22.214.171.124,80)
netfilter sends the packet which has been reverse-mapped to the Client, and all is well.
So, if you need to know the client's IP address, you can NOT use the
REDIRECT target. Rather, put your actual server in a different subnet, and use
DNAT target, with your Linux server acting as a full router. Assuming your actual server has a local LAN address of 192.168.1.51, the mapping will be:
(126.96.36.199,34567,188.8.131.52,80) --> (184.108.40.206,34567,192.168.1.51,80)
The server at 192.168.1.51 will reply to 220.127.116.11, and the router will reverse map:
(18.104.22.168,34567,192.168.1.51,80) --> (22.214.171.124,34567,126.96.36.199,80)
And all's well.