Introduction
When building a service that needs to communicate in a peer-to-peer fashion across the open Internet each node would, ideally, communicate using a unique public IP address and port number that would be known to all the other nodes.
However, in most contemporary office or home networks, there are a number of computers that live behind a router; i.e. the whole netwok of computers share a single IP address. A technology that is used to share a single IP address amongst a network of computers is Network Address Translation (NAT).
The simplest form of NAT is called a full-cone NAT. This is usually configured using port forwarding rules in the router. For example, a router could be configured such that any HTTP (port 80) traffic is sent to a specific computer behind the router running a web server on port 8080.
However, these static rules would require administrator configuration in the router and so most router devices implement some kind of restricted cone NAT. The way this works is that when a computer on the local network makes a request to a remote computer, a dynamic mapping is made in the router. e.g. Let’s say the local node makes a request from 192.168.10.10:9000
to www.google.com:80
, and that the public IP address of the router is 50.50.50.50
. A mapping is made in the router from 192.168.10.10:9000
to 50.50.50.50:10000
(note the port number can change but doesn’t have to). Now when a response from www.google.com
comes in to 50.50.50.50:10000
the router knows to send that response to 192.168.10.10:9000
. These mappings have a short time-to-live (TTL) that is refreshed by communication in either direction. There are two kinds of restricted cone NAT, the first one will forward any request from www.google.com
to 50.50.50.50:10000
regardless of the port it comes from and is called a restricted cone NAT. The second one, will only forward requests from www.google.com:80
to 50.50.50.50:10000
and is called a port restricted cone NAT.
The other feature of cone NATs is that the mapping of public endpoint to local endpoint is fixed for a specific local endpoint. i.e. if a mapping is made from 192.168.10.10:9000
to 50.50.50.50:10000
for the use of traffic from www.google.com
, then if 192.168.10.10:9000
is used to communicate with some other server, then the same public endpoint, 50.50.50.50:10000
, will also be used for that communication. This contrasts with a symmetric NAT
which will establish a separate public endpoint for each connection.
Hole Punching
This dynamic mapping feature of NATs can be used to create a hole in the routers firewall that is useful in peer-to-peer communications.
For example, let’s say we created a server on the Telestream internal network that responded on port 9000. We would like a client on the Internet to be able to connect to our server and we know Telestream’s public IP address. However, if our client sends a message to port 9000 on Telestream’s public IP address, the router would reject that message because
- It has a firewall that doesn’t allow through arbitrary traffic in port 9000.
- It doesn’t know which computer on the local network would like to receive that message.
This can be fixed if, first, we send a message from our server to our client from port 9000 (even if this message is not processed by the server). This will establish a dynamic mapping entry in our router. Now when our client tries to connect back to Telestream’s public IP address (and configured port), the router will assume it is part of the communication initiated by the server and will route the message to the server.
STUN
In order to be able to punch a hole in our router / firewall to enable peer-to-peer communication in our application, our application would need to know
- What type of NAT it is behind.
- What the public IP address / port is for the application.
Session Traversal of UDP Through NAT (STUN) is a specification that defines how this can be achieved in a standardized way. There are numerous public STUN servers that can be used by an application to achieve this goal. They work by receiving a STUN protocol message from an application and responding with the public IP address and port that the message was receieved from. By sending and receiving a few of these messages from various IP address / port combinations, the type / configuration of the NAT can be determined.
The STUN protocol can easily be examined using an open source C# project, https://github.com/gkocot/STUN.git.
dotnet build
dotnet run --project ./TestApp/TestApp.csproj stun.schlund.de:3478
PublicEndPoint: 23.122.225.224:17777
LocalEndPoint: 0.0.0.0:17777
NAT Type: PortRestricted
In this example, the STUN message is sent from the local IP address (behind a NAT) of 192.168.1.155:17777 to the STUN server at 212.227.67.34. The response from the STUN server indicates that the public IP address for the message is 23.122.225.224:17777. This matches up with the console output seen from the test application. In addition. the test application is also able to determine that the NAT Type is a port restricted cone.
This information could now be used by exchanged with a peer application on the public internet to allow peer-to-peer UDP communications to be established.