Skip to content

Network Address Translation

Michiel De Backker edited this page Apr 11, 2019 · 8 revisions

NAT types

An old version of the stun spec (RFC3489) defined the following NAT variations:

Full Cone: A full cone NAT is one where all requests from the same internal IP address and port are mapped to the same external IP address and port. Furthermore, any external host can send a packet to the internal host, by sending a packet to the mapped external address.

Restricted Cone: A restricted cone NAT is one where all requests from the same internal IP address and port are mapped to the same external IP address and port. Unlike a full cone NAT, an external host (with IP address X) can send a packet to the internal host only if the internal host had previously sent a packet to IP address X.

Port Restricted Cone: A port restricted cone NAT is like a restricted cone NAT, but the restriction includes port numbers. Specifically, an external host can send a packet, with source IP address X and source port P, to the internal host only if the internal host had previously sent a packet to IP address X and port P.

Symmetric: A symmetric NAT is one where all requests from the same internal IP address and port, to a specific destination IP address and port, are mapped to the same external IP address and port. If the same host sends a packet with the same source address and port, but to a different destination, a different mapping is used. Furthermore, only the external host that receives a packet can send a UDP packet back to the internal host.

Additional research can be found in RFC5780

ccding/go-stun may be helpful to figure out your nat type(s).

NAT considerations

NAT bindings should not be considered to be deterministic:

  • If an external port is already mapped to another host the NAT has to assign you another port.
  • Some routers implement randomized port mapping.

Hole punching

Hole punching is an easy way to circumvent a NAT. Peers try to establish a direct connection by learning their respective public addresses using a 3rd party server. The 3rd party server is also used to keep the NAT binding alive.

The hole punching examples below can be used to get through the Full Cone, Restricted Cone and Port Restricted Cone NAT types. Symmetric NATs require more complex processes. However, this NAT type is more rare and can still be circumvented using a TURN server to relay traffic.

UDP hole punching

First establisch a local connection by listening using net.ListenUDP. Next use WriteTo and ReadFrom to send and receive packets to and from the desired remote addresses.

TCP hole punching

TCP hole punching is a little more involved. In order to keep the NAT binding alive the same local TCP port should be used. This can be done by setting the SO_REUSEADDR and SO_REUSEPORT socket options on most operating systems. Sadly this isn't well supported until go1.11 as discussed in golang/go#9661. Starting from go1.11 the flag can be set using the Dialer.Control function.

Example (dial.go):

var d net.Dialer
d.Control = controlOnConnSetup

func controlOnConnSetup(network string, address string, c syscall.RawConn) error {
	return nil
}

For Unix (dial_unix.go):

// +build darwin dragonfly freebsd linux netbsd openbsd solaris
func controlOnConnSetup(c syscall.RawConn, addr Addr) error {
	var operr error
	fn := func(s uintptr) {
		operr = syscall.SetsockoptInt(int(s), syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1)
		if operr != nil {
			return
		}
		operr = syscall.SetsockoptInt(int(s), syscall.SOL_SOCKET, syscall.SO_REUSEPORT, 1)
		if operr != nil {
			return
		}
	}
	if err := c.Control(fn); err != nil {
		return err
	}
	if operr != nil {
		return operr
	}
	return nil
}

For Windows (dial_windows.go):

func controlOnConnSetup(network string, address string, c syscall.RawConn) error {
	var operr error
	fn := func(s uintptr) {
		handle := syscall.Handle(fd)
		operr = syscall.SetsockoptInt(handle, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1)
		if operr != nil {
			return
		}
	}
	if err := c.Control(fn); err != nil {
		return err
	}
	if operr != nil {
		return operr
	}
	return nil
}

Candidate types and combinations of NAT types

Following chart summarises which types of candidates contribute to a successful NAT traversal in each combination of local and remote NAT types.

Open F.Cone R.Cone PR.Cone Symmetric
Open host srflx or prflx srflx or prflx srflx or prflx prflx
F.Cone srflx or prflx srflx srflx srflx srflx and prflx
R.Cone srflx or prflx srflx srflx srflx srflx and prflx
PR.Cone srflx or prflx srflx srflx srflx relay
Symmetric prflx srflx and prflx srflx and prflx relay relay
  • Open: Open to the Internet
  • F.Cone: Full-cone NAT
  • R.Cone: Restricted-cone NAT
  • PR.Cone: Port restricted-cone NAT
  • Symmetric: Symmetric NAT
  • host: Local (host) address candidate
  • srflx: Server reflexive candidate (a candidate derived from STUN)
  • prflx: Peer reflexive candidate
  • relay: Relay NAT (a candidate derived from TURN)