How to get original destination port of redirected UDP message? How to get original destination port of redirected UDP message? linux linux

How to get original destination port of redirected UDP message?


Depends on the redirection mechanism. If you are using REDIRECT (which is NAT under the hood), then you need to use SO_ORIGINAL_DST, or libnetfilter_conntrack to query the original destination of the connection before NAT was applied. However since you can serve several connections with the same listener socket, this lookup has to be done for every packet.

You can experiment with libnetfilter_conntrack and the services it provides using the conntrack command line tool.

An alterntive is to use TPROXY for the redirection, which was meant to be used in cases like this. There you can get the original destination of the packet using an ancillirary message using recvmsg(). The key to look for is the IP_RECVORIGDST setsockopt.

More information on TPROXY can be found in the kernel Documentation directory, in a file called tproxy.txt. It is a bit difficult to use, but works more reliably as it is implemented by the stack, rather than the packet filtering subsystem.

Edited: to add how to query UDP destination addresses with TProxy.

  1. open an UDP socket, bind it to 0.0.0.0 or to a more specific IP
  2. you enable IP_RECVORIGDST via setsockopt(fd, SOL_IP, IP_RECVORIGDSTADDR, ...)
  3. you use recvmsg() instead of recvfrom()/recv() to receive frames
  4. recvmsg() will return the packet and a series of ancillary messages,
  5. iterate the ancillary messages, and locate the CMSG block with level SOL_IP, index IP_ORIGDSTADDR
  6. this CMSG block will contain a struct sockaddr_in with both the IP and the port information.

Edited: SO_ORIGINAL_DST vs. udp

SO_ORIGINAL_DST should work with udp sockets, however the kernel doesn't let you specify the connection endpoints, it'll use the socket that you call SO_ORIGINAL_DST on to get this address information.

This means that it'll only work, if the UDP socket is properly bound (to the redirected-to address/port) and connected (to the client in question). Your listener socket is probably bound to 0.0.0.0 and is servicing not just a single, but mulitple clients.

However, you don't need to use your actual listener socket to query the destination address. Since since UDP doesn't transmit datagrams on connection establishment, you can create a new UDP socket, bind it to the redirect address and connect it to the client (whose address you know since it sent the first packet to your listener anyway). Then you could use this socket to run SO_ORIGINAL_DST on, however there are culprits:

  1. once you open such a socket, the kernel will prefer that if the client would send additional packets after the first one, not your listener socket
  2. this is inherently racy, since by the time your application has a chance to call SO_ORIGINAL_DST, the conntrack entry may have timed out.
  3. it is slow and a lot of overhead

The TProxy based method is clearly better.