Friday, February 8, 2008

Jasper - Kimura Kaela

木村カエラのJasper.
なーんかこの曲は電気っぽいとおもったら卓球作曲らしい。
なるほど

Thursday, February 7, 2008

Google Analytics with MoinMoin

Google AnalyticsをMoinMoinWikiで使う方法。

設定ファイルの page_footer2 に 例の js を書けばよい。

    page_footer2 = """
<script type="text/javascript">var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
</script>
<script type="text/javascript">
var pageTracker = _gat._getTracker("UA-XXXXXX-X");
pageTracker._initData();
pageTracker._trackPageview();
</script>
"""

これで各ページの </body>タグの直前にスクリプトが挿入される。

Is UHP really required?

そもそも、プロトコルを全部UDPでやれば、初期接続がクライアントからであるかぎり、UHPなんて考えなくてもいいのではなかろうか。

再送とかのチェックがめんどうではあるけども、それはデータ転送においても同じわけで。
まぁUHPつかえないルータの場合の考慮はどうすんだという問題もあるが、そのへんは要件次第か。

Wednesday, February 6, 2008

UDP Hole Punching その2

どうやら昨日書いた手法は特許とられてるらしい。ひどい話である。
・TCPでなんらかのデータ送信の後、UDPで補足データを送信する
という請求事項にひっかかるのだとか。(特許公開番号:2007-502585)

まぁいろいろ考えたが、最初のTCPによるセッション初期化をUDPでやっちまえばいい気がしてきたので、次のように変更。

1. C UDP::UHPInitialize -- NOP
2. S UDP::UHPInitializeOK -- SESSIONID < UNIQUE ID >
3. C TCP::UHPComplete -- FLAG < 0(FAIL) | 1(SUCCESS) >[, SESSIONID < UNIQUE ID > ]
4. S TCP::UHPCompleteOK -- NOP

まぁいいんでないかなあ。初期化失敗したら知らんぷり or TCPで続ける。
あとUHPを行う場合、クライアントのUDPソースポートも固定(bind())しておかないといつのまにか通信できなくなる可能性があるので注意すべき。
あとほんとに目的のサーバから送られてきたかどうかのチェックとか。

Tuesday, February 5, 2008

UDP Hole Punching

仕事でUDPのデータストリームを扱っていて、さらにクライアントはNAT超えを考慮しなくてはならないということで、UDPのNAT超えでよく使われる手法、UDP Hole Punching (UHP) というものを実装することになった。

UDP Hole Punchingというのはルータに動的に空けられたNATテーブルの穴を逆から叩くことにより、NATの中のクライアントとUDP通信を可能にする技術であるからして。

さて、TCPのコントロールコネクションはもともとあるので、そのプロトコル上に UHP のネゴシエーションを付け加える形にした。

1. TCP C: UHPInitialize <NOP>
2. TCP S: UHPInitializeOK <SESSION ID> <SERVER UDP PORT>
3. UDP C: UHPRequest <SESSION ID>
4. UDP S: UHPRequestOK <NOP>
5. TCP C: UHPComplete <SUCCESS | FAILED>
6. TCP S: UHPCompleteOK <NOP>

3と4で UDP Hole Punching を行うわけだが、UDPなのでロストするかもしれない。なので両者とも数回再送を試みる。
最終的に、クライアントが4の UHPRequestOK を受け取る事ができればUHPは成功であり、
5 の UHPComplete で SUCCESS を返すわけである。

3もしくは4をロストしたり、そもそも UHP が不可能な NAT 環境であった場合、FAILEDを返すことにより TCP へ Fallback するなりなんなりするようにする。といった寸法である。

以上のシーケンスをRubyでテストコード書いて(仕事のコードなので公開はできないが)実行してみたのだが、うまくいかない。
NAT なしの LAN内であれば動くのでアルゴリズム的な部分は間違ってない(と、思う)。

ルータが特殊なのかと思っていたのだが、帰宅途中で罠に気がついた。

実は、テストしているサーバは複数の Network Interface をもっている。
アドレスがプライベートで NAT の中にある eth0 と、実験でつかっている(つもり)のグローバルアドレスを持つ eth1 であり、default route は eth0 の NATルータである。

そして、サーバでudp socketがbind() しているアドレスは '0.0.0.0' である。
つまり、クライアントがグローバルアドレスに sendto()し、サーバが recvfrom() により取得したした client socket address へ sendto() を返す場合、サーバの source addres はカーネルが勝手に決めているのであり、基本的に default routeが使われるわけだから、eth0のプライベートアドレスになってしまう。
要するにサーバもNAT内あるものとして動作をしていた、というわけなのである。
UHP は NAT(client)からNAT(server) では仲介サーバが居ない限り不可能である。

今回はそういったケースは考慮しないので、素直にサーバの bind address をグローバルなものにして再実験をしたところ、うまく動作した。

教訓。udp socket を '0.0.0.0' に bind()してlistenするときは複数の interface や routing がないか注意しよう。

my first post

blogger.comでブログ書くことにしました。
自前サーバでCMSのアップグレードやメンテナンスをしたり、コメントスパムと格闘したり、
テンプレートで頭を悩ますのはもううんざりなのです!

というわけでございまして。
適当に続けていこうと思います。