Tadas Vilkeliskis's Brain Dump

How to establish a P2P connection

written on Wednesday, December 8, 2010

I am working on this cool project which I don't want to disclose yet and the essential part for this project is to create a peer-to-peer connection between two computers. Creating a connection is not that difficult; however, it gets complicated when both computers are sitting behind a NAT device. The NAT device will create a private IP address space and be responsible for routing packets into and out from the private network. This means that an IP address and a port number associated with a particular service are not directly accessible from other computer networks and all inbound data packets must be routed by the NAT device. So how do you create a P2P connection when the NAT device is "blocking" access to a peer? The technique I will cover here is very elegant yet very simple --- it's called hole punching. Hole punching a technique that opens a temporary port on the NAT device which is directly mapped to a port on the internal network. There are two types of hole punching: TCP and UDP. In this post I will only cover UDP hole punching because it has better support (82% for UDP and 64% for TCP according to this source) and is easier to implement. Let me illustrate what a UDP hole punching is and then take a look at the implementation.

(the illustration was lost when i moved to posterous, but you can search google images for udp hole punching to get the idea).

As you can see there are only four steps required in order to create a P2P connection. At first, peers have to register with a broker. The broker is a computer with a public IP address; registration process is very simple --- the broker stores peer's id, its external IP address and external port number. When a peer connects to the broker in step 1 the NAT device creates a temporary mapping from the external port to an internal port, that is to say, a hole is punched though the NAT. Next, second peer wants to establish a direct connection with the first peer, but he does not now what is the port number and IP address of that peer. So, it contacts the broker and asks for peer 1 credentials. Once the credentials are received the second peer can make a direct connection to the first peer through the temporarily allocated external port on NAT 1. In theory it does not look hard at all; in practice --- it is exactly the same. I had a trouble finding some simple code examples showing how to implement hole punching, so I wrote a proof of concept code in python to test whether this technique works.

#
# Broker
#
from socket import *

HOST = ''
PORT = 9999

s = socket(AF_INET, SOCK_DGRAM)
s.bind((HOST, PORT))

peers = {}

while 1:
    data, addr = s.recvfrom(128)
    print addr, data
    peers[addr] = True
    if len(peers) > 1:
        peer1 = peers.keys()[0
        peer2 = peers.keys()[1]
        s.sendto("peer " + peer2[0] + " " + str(peer2[1]), peer1)
    else:
        s.sendto("pong", addr)
#
# Peer
#
from socket import *

BROKER_HOST = "broker.host.com"
BROKER_PORT = 9999
BROKER_ADDR = (BROKER_HOST, BROKER_PORT)

sock = socket(AF_INET, SOCK_DGRAM)
# to support two peers on the same machine
try:
    sock.bind(('', 9900))
except:
    sock.bind(('', 9901))

sock.sendto("ping", BROKER_ADDR)

while 1:
    data, addr = sock.recvfrom(128)
    new_data = data.split()
    if new_data[0] == "peer":
        addr = new_data[1]
        port = int(new_data[2])
        sock.sendto("resp test", (addr, port))
    elif new_data[0] == "resp":
        sock.sendto("what goes around comes around", addr)
    print addr, data

This is a very simple code. The broker supports only two peers and must be started on a computer that has a public IP address. A peer will punch a hole by sending a ping message to the broker. In this example peers don't request the address and the port number of another peer, but instead the broker will send this information to the second peer. If everything worked out, one peer will see a "test" message on its screen, whereas, another one will see "what goes around comes around". I was able to test this code where one peer, that would be me, was in the US and another one, my friend, was in Lithuania, and it works.

This entry was tagged networking, p2p, programming and python