Search code examples
socketsnetwork-programmingudpnat

Client does not receive UDP datagram from server if the server responds from a different port


Environment:

Client - Windows desktop behind NAT, runs a UDP client application.

Server - Ubuntu server with public IP, runs a UDP server application that expects packets on port 6000.

ClientPrivate/Public - private/public IPs of the client

ServerPrivate/Public - private/public IPs of the server


Case 1:

  1. Client opens a UDP socket on ClientPrivate:777 and sends a datagram to ServerPublic:6000
  2. Server receives the datagram from ClientPublic:777 and sends a response to ClientPublic:777 from ServerPrivate:6000
  3. Client receives the response and everything is fine

WireShark output on Server: 475 88.xxx.xxx.90 -> 10.0.0.5 UDP 777 → 6000 476 10.0.0.5 -> 88.xxx.xxx.90 UDP 6000 → 777

WireShark output on Client: 152 192.168.2.85 104.40.xxx.250 UDP 777 → 6000 153 104.40.xxx.250 192.168.2.85 UDP 6000 → 777


Case 2:

  1. Client opens a UDP socket on ClientPrivate:777 and sends a datagram to ServerPublic:6000
  2. Server receives the datagram from ClientPublic:777
  3. Server opens a new socket on ServerPrivate:46666 and sends a datagram to ClientPublic:777
  4. Client does not receive the datagram

WireShark output on Server: 475 88.xxx.xxx.90 -> 10.0.0.5 UDP 777 → 6000 476 10.0.0.5 -> 88.xxx.xxx.90 UDP 46666 → 777

WireShark output on Client: 152 192.168.2.85 104.40.xxx.250 UDP 777 → 6000


Question:

Why does the Client receive a datagram in Case 1 and does not in Case 2?


Solution

  • I think this is due to the client-side NAT. When your client sends to the server public IP, the NAT router is making an entry in its internal table of connections (something like "192.168.2.85[777] => 104.40.xxx.250[6000]).

    So when a response comes back from 104.40.xxx.250[6000] the NAT will find the matching entry and return the response to the client machine. That entry is how it knows which machine in the client network to send the packet to. However, when a response comes back from 104.40.xx.250[46666], that doesn't match any entry in its table so it's dropping that packet.

    This is the way NAT works. Consider if you had another machine behind your NAT also talking to the same server, say, "192.168.2.86[777] => 104.40.xxx.250[6000]". I think you would find your NAT router actually changing the source port to something other than 777. Else how can it properly route the response back to the correct machine. In general for NAT, if the incoming packet from the router doesn't match an entry in its table (OR you've configured a port-forwarding rule) the packet will be dropped.

    Port-forwarding allows you to open up your internal machine for general access by the outside world. But lacking that, you really don't want any random IP address / port pair to be able to send a datagram to your client machine.