JPunch: Java UDP Hole Punching Library

Active supporters: Artjom Lind, Ulrich Norbisrath

For F2F Computing we needed UDP hole punching support. As there were no libraries really supporting this available, we developed our own. This library here supports UDP Hole Punching between two random peers in the internet, if there is a possibility of a indirect communication between the peers (like ssh port forwarding via another server, chat connection).

It is released under the LGPL license.

We are currently working on the following paper:

JPunch - Enabling Java Based P2P through Firewalls

News

Providing Back-end For JPunch

JPunch library needs a back-end for exchange of specific info (host ip addresses, network topology info and traversal instructions). The back-end could be some existing (relayed) connection between two hosts. The simplest way to establish relayed connection between two hosts is using ssh-tunneling. Next I will give some instructions of how to do that.

figure001.JPG

Figure 1

Refer to the Figure 1, there is two hosts(A,B) in private networks that can't establish a direct connection becase of the Network Address Translators of each submnet. But using ssh-tunnels we can still make the communication possible. All we need is a realy host C which is located in public network. Host C is reachable from each private networks (A,B hosts). The goal is that hosts A and B can ssh to each other over relay host C. Let's see now in details: 1.A and B host expose their 22 port using tunnel to C

userA@A:~$ ssh -42Cvc blowfish -gR <portA>:localhost:22 <user?>@C
userB@B:~$ ssh -42Cvc blowfish -gR <portB>:localhost:22 <user?>@C

Now we have ssh service of hosts A and B exposed through ports portA and portB on host C. Unfortunately if host C restricts bindings on the public interfaces we can't ssh connect to it's bound ports. In other words if portA and portB will be bound on localhost interface of host C rather then on eth0, the next commands will definitely fail.

userA@A:~$ ssh -42Cvc blowfish -p <portB> <user?>@C
userB@B:~$ ssh -42Cvc blowfish -p <portA> <user?>@C

In order to get use of exposed ports on C host, we need to build up some more tunnels.

userA@A:~$ ssh -42Cvc blowfish -gL 10022:localhost:<portB> <user?>@C
userB@B:~$ ssh -42Cvc blowfish -gL 10022:localhost:<portA> <user?>@C

Now we have each host (A and B) in private networks forwarding the connections from the local port 10022 to the remote ports portA and portB on the C host, which are forwarded next to the 22 port of private hosts.

Now if we try to ssh connect to 1ocalhost:10022 of A host, we will actually connect to the B host.

userA@A:~$ ssh -42Cvc blowfish userB@localhost -p 10022
userB@B:~$ ssh -42Cvc blowfish userA@localhost -p 10022

That was the simple example of how to establish ssh connections between two hosts in private networks using ssh-tunnels and some public host as a relay-server. Next I will explain how to send messages from one host to another using established ssh connections. The JPunch library will use this messaging mechanism as a back-end for configurations exchange.

OpenSSH Public Key Authentication (passphrase-free login)

The ssh login generally requires the user to input the passphrase. The messaging mechanism (described below) uses ssh remote execution. It connects to the remote host with the given credentials, executes a given command, collects the result (command output) and disconnects. Each login requires user to manually input the passphrase. To simplify the process the Public Key Authentication can be used. This chapter describes how to setup passphrase login between the hosts A and B.

First we need to generate the keys (private and public) if have not yet.

userA@A:~$ mkdir ~/.ssh
userA@A:~$ cd ~/.ssh
userA@A:~$ ssh-keygen -t dsa

Second we need to copy a public to the remote host. Note, that scp also uses the ssh-tunnel to connect to host B. (The connection to userB@localhost -p 10022 will be forwarded to userB@hostB)

userA@A:~$ scp ~/.ssh/id_dsa.pub userB@localhost: -p 10022

Finally, log into the host B and add the uploaded public key id_dsa.pub to the list of authenticated keys

userA@A:~$ ssh -42Cvc blowfish userB@localhost -p 10022
...
Password: ***
...
userB@B:~$ cat ~/id_dsa.pub >> ~/.ssh/authorized_keys
userB@B:~$ rm -rf id_dsa.pub
userB@B:~$ exit

At this point a copy of your key is now stored on the remote machine as an authorized keys and any ssh connection coming from the local machine will match that key and connect with the key authentication instead of a password.

Do not forget to do the same on the second host

userB@B:~$ mkdir ~/.ssh
userB@B:~$ cd ~/.ssh
userB@B:~$ ssh-keygen -t dsa
userB@B:~$ scp ~/.ssh/id_dsa.pub userA@localhost: -p 10022
userB@B:~$ ssh -42Cvc blowfish userAlocalhost -p 10022
...
Password: ***
...
userA@A:~$ cat ~/id_dsa.pub >> ~/.ssh/authorized_keys
userA@A:~$ rm -rf id_dsa.pub
userA@A:~$ exit

Messaging mechanism

Two instances of JPunch running on different hosts A and B can send messages to each other using input/output redirection. In other words the output of JPunch (running on A host ) will be forwarded to the input of JPunch of B host and vice-versa. All we need are possibility to ssh between theese hosts and passphrase-free logins. Establishment of relayed ssh connections and public key authentication are already described above. Next we need to define pipes for redirecting input/output on each host.

The pipes can be created using following mkfifo command

userA@A:~$ mkfifo /tmp/pipe1
userA@A:~$ mkfifo /tmp/pipe2

and on B host

userB@B:~$ mkfifo /tmp/pipe1
userB@B:~$ mkfifo /tmp/pipe2

The I/O of JPunch can be redirected using linux redirection operators < (in) and > (out)

userA@A:~$jPunch/sh run.sh < /tmp/pipe2 > /tmp/pipe1

So the output will be collected into pipe1 and the input will be taken from pipe2. Now we need to transport the data between the pipe1 of the host A and the pipe2 of the host B (and vice-versa). As we have passphrase-free logins between two hosts, we can use the remote execution. The next script will always take the data from pipe1 of the host B and put it into the pipe2 of the host A.

userA@A:~$while [ true ]; do \
ssh -42Cvc blowfish userB@localhost -p 10022 "cat /tmp/pipe1" > /tmp/pipe2; \
done;

same for host B

userB@B:~$while [ true ]; do \
ssh -42Cvc blowfish userA@localhost -p 10022 "cat /tmp/pipe1" > /tmp/pipe2; \
done;

The script uses established ssh-tunnel to the B host. (ssh -42Cvc blowfish userA@localhost -p 10022). Next we add the remote execution of "cat /tmp/pipe1". That will take the data from the pipe1 of the remote host and put it into the pipe2 on the localhost (> /tmp/pipe2). The while loop will repeat the remote execution.

Now the messaging between different Jpunch instances is working properly. So the JPunch is provided with the back-end for the configuration exchange.

The described while loop should be started before the main program. It is reasonable to have two different terminals for the loop and for the JPunch program.

Host A Terminal #1

userA@A:~$while [ true ]; do \
ssh -42Cvc blowfish userB@localhost -p 10022 "cat /tmp/pipe1" > /tmp/pipe2; \
done;

Terminal #2

userA@A:~$jPunch/sh run.sh < /tmp/pipe2 > /tmp/pipe1

Host B Terminal #1

userB@B:~$while [ true ]; do \
ssh -42Cvc blowfish userA@localhost -p 10022 "cat /tmp/pipe1" > /tmp/pipe2; \
done;

Terminal #2

userB@B:~$jPunch/sh run.sh < /tmp/pipe2 > /tmp/pipe1

There are 2 attachment(s) stored for this page.

Comments

ulno.net: projects/jpunch (last edited 2009-09-24 10:55:29 by artjom85)