UDP hole punchingの実装をしてみる

ラジコンをLTE回線を使って制御できたら面白そうだなと思いいろいろ調べてる。

BeagleBone BlackL-03Dを組み合わせてL-03DにIIJmioIIJ BIC SIMを差せばインターネット経由でBeagleBone Blackを遠隔操作をする事が出来るはずです。あとはBeagleBone Blackをラジコンに載せればラジコンをインターネットを経由で遠隔操作できます。しかしこれには一つ問題があります。その問題とはIIJmioのsimを使用した場合BeagleBone Blackに割り振られるIPv4アドレスがプライベートIPアドレスとなってしまう事です。プライベートIPv4アドレスが割り振られたという事はBeagleBone Blackに割り振られたIPv4アドレスが分かったところでインターネット側からの通信はIIJmioのNATに阻まれてBeagleBone Blackにアクセスする事は通常出来ません。

この場合UDPホールパンチングというテクニックを使用するとプライベートIPアドレスが割り振られたBeagleBone Blackと直接通信する事ができます。UDPホールパンチングについては他のサイトに詳しい解説があるのでここでは省略します。そこでUDPホールパンチングを行うサーバーとクライアント(以下nodeと呼ぶ)を自分で実装しgithub.comに公開しました。

strobo/holepunch · GitHub

holepunchの解説

ビルドコマンド等についてはレポジトリ内のREADMEを参照ください。
このholepunchはlibeventを使用していますのでまずlibeventをインストールしてください。
次にPCを3台用意します。一つはUDPホールパンチングの仕組み上必要となるマッチングサーバーです。このサーバーだけはグローバルIPアドレスが必要です。残りの二つは実際に直接通信するPCとなります。この二つのPCは双方ともプライベートIPでも問題ありません。

このholepunchの実装ではstunを使用せず独自のプロトコルで通信を行います。通信相手の識別には任意で設定可能な文字列keyを使用します。この文字列keyの一致したPC同士で直接通信をする事ができます。

おおまかな通信手順
  1. nodeAがkeyをマッチングサーバーに送信
  2. マッチングサーバーは送られてきたkeyとnodeAのIPアドレスとポート番号を記録します
  3. nodeBがマッチングサーバーにnodeAと同じkeyを送信
  4. マッチングサーバーは今までに記録されたkeyと送信されたkeyが一致するかを確認し一致すれば双方に双方のnodeのIPアドレスとポート番号を通知します。一致しなければ新たなkeyとしてnodeBのIPとポート番号を記録します
  5. nodeA,B双方がマッチングサーバーから通知されたIP:ポートに対して通信を行う
実際に使用する

サーバ上で

$ ./holepunch server

nodeA上で

$ ./holepunch node example.com foo

nodeB上でも同じコマンドを実行

$ ./holepunch node example.com foo

通信が確立したら、各nodeのターミナル上で文字を入力しEnterで入力した文字列を相手に送信する事が出来ます。