Iptables route traffic to specific external ip to different local ip instead and return responses with external ip as source

iptables

My raspberry pi is my standard gateway, sitting between my router and the rest of my home network. When my desktop pc sends packets to a specific external IP, I want my raspberry pi to send them to another local machine instead. (Trying to emulate a game server, but the game has the server IP hardcoded) This needs to work for TCP and UDP.

I tried the following rules on my raspberry: (1337 = game port, 1.2.3.4 = game server, 192.168.0.169 = other local machine emulating server)

iptables -t nat -A PREROUTING -d 1.2.3.4/32 -p udp -m udp --dport 1337 -j DNAT --to-destination 192.168.0.169:1337
iptables -t nat -A PREROUTING -d 1.2.3.4/32 -p tcp -m tcp --dport 1337 -j DNAT --to-destination 192.168.0.169:1337

UDP works fine. Sent packets get routed to 192.168.0.169 and responses come back. However they don't come from 1.2.3.4 (the non existent game server) but from my emulated server 192.168.0.169. For UDP this doesn't really matter, however with TCP this causes the handshake to fail:

wireshark

(My desktop pc is 192.168.0.199. Wireshark view from 192.168.0.169, the emulated game server. From what I see the SYN ACK did not work, hence the retransmissions. I guess this is because the SYN ACK comes from an unexpected IP)

I tried rewriting the source ip for packets going back to 192.168.0.199 with this rule:

iptables -t nat -A POSTROUTING -s 192.168.0.169/32 -d 192.168.0.199/32 -p tcp -j SNAT --to-source 1.2.3.4

However it simply doesn't work. I am not good at iptables yet, so I probably did something wrong, but i'm not sure if rewriting the source of all packets from the game server to my desktop pc is a good idea anyways (other applications will porbably get messed up).

Bonus question: Isn't DNAT supposed to rewrite the source IP when there is a response anyway?

Best Answer

Bonus question: Isn't DNAT supposed to rewrite the source IP when there is a response anyway?

Yes it is, iptables NAT rules only operate on the first packet of a connection, later packets are handled according to the mappings established by the first rule.

Sent packets get routed to 192.168.0.169 and responses come back. However they don't come from 1.2.3.4 (the non existent game server) but from my emulated server

The problem is that the NAT can't translate packets that are never sent to it. Here is what is happening in your scenario.

  • The client crafts the initial packet to 1.2.3.4
  • The client looks in it's routing table, it doesn't find any better routes so it sends the packet to it's default gateway which is the NAT box.
  • The NAT box looks up the packet in it's connection tracking table, determines that it is a new connection, performs the NAT manipulations and establishes a connection tracking entry.
  • The NAT looks up the new destination in it's routing table and sends the packet to the server.
  • The server crafts a reply swapping source and destination as usual
  • The server looks in it's routing table, finds a match for the client's IP and sends the packet directly to the client
  • The reply arrives back at the client but since it didn't return via the NAT box it has the wrong source IP.
  • The reply is dropped.

So what can we do about it? One solution is to MASQURADE the traffic from client to server so that the server sees the traffic as coming from the NAT box. I believe the following should do it

iptables -t nat -A POSTROUTING -s 192.168.0.0/24 -d 192.168.0.169 -j MASQURADE
Related Question