2010年4月13日火曜日

IPv4 to IPv6, IPv6 to IPv4 translator

ちょっとした必要に迫られて、IPv4とIPv6のプロトコル変換プログラムを作りました。動作は至ってシンプルで、あるひとつのIPv6アドレスを、別のユニークなIPv4アドレスに変換(また、その逆変換)を行うものです。最近流行っている、NAT64のような、複数のIPv6ノードがひとつのIPv4アドレスを共有して通信するものとは異なります。

そもそもの目的は、IPv6のみを使って構築運用されているネットワークの内部ノードを、外部に公開することでした。もちろん、IPv6を使えば、普通に外部からアクセスできるのですが、まだまだIPv6の利用が一般的ではない現在、IPv4での公開も考えなければなりません。こうした目的の場合、一般的にはIPv4とIPv6のデュアルスタック環境にするところです。しかしながら、必ずしもすべてのIPv6ノードがIPv4でアクセスできる必要はありません。運用されているIPv6ノードの中で、サーバとして公開されるものは極わずかであり、多くのノードはそのサーバのサポートをしているだけだからです。そのようなノードに、貴重なIPv4アドレスを割り当てるのはもったいないですし、またデュアルスタックにすることで運用のコストも大きくなりかねません。

そこで、公開サーバの数を収容するために必要十分なIPv4アドレスを用意して、単純に1対1で対応させてしまおう、ということになります。言ってみれば、IPv6への移行が完了するまでの、つなぎの技術ですね。

仕組み自体はNAT64とDNS64のものとほとんど変わりません。違いはトランスポート層のポート番号の変換処理が必要かどうかです。アドレスを1対1で対応させる場合、ポート番号を処理する必要がないので、変換サーバがNATのような状態を持つ必要がなくなります。状態を持たなくてよいので、負荷分散あるいは耐障害性の向上のために、簡単に多重化できるという利点があります。欠点はNAT64に比べてたくさんのIPv4アドレスが必要になることです。なにせ1対1対応ですから。

実装はtunデバイスを使っています。最近のOSなら標準でサポートしているものも多いのではないでしょうか。tunデバイスを使えば、read(2)システムコールとwrite(2)システムコールで生のIPパケットを読み書きできるので、今回のような目的にぴったりです。ユーザ空間で実装できるので、開発やデバッグも簡単になります。当然、転送速度は遅くなると思いますが、開発の簡単さ、もともとの目的がつなぎの技術であることを考えると、そう悪い選択でもないでしょう。

動作はちょっと複雑です。まず、IPv6ノードがIPv4ノードにアクセスする場合から考えてみます。IPv6ノードはIPv4アドレスを持っていないため、まずIPv4アドレスとIPv6アドレスの対応を定義します。ここでは例として、IPv6ネットワークのプレフィックスを2001:db8:0:0::/64、IPv4グローバルアドレス空間を192.0.2.0/24と仮定しましょう。以下のようなアドレスの対応を定義します。

  • 192.0.2.1 <=> 2001:db8:0:0::100

つまり、外部のIPv4ノードが2001:db8:0:0::100と通信したいと思ったときは、192.0.2.1に対して接続すればよいということになります。192.0.2.1に届いたIPv4パケットは、IPv6に変換されて2001:db8:0:0::100に転送されます。このとき、変換後のIPv6パケットの始点アドレスには、通信相手のIPv4アドレスが識別できるよう、IPv6アドレスの一部にIPv4アドレスを埋め込んでおきます。始点アドレスとして使うIPv6プレフィックスはなんでもよいのですが、仮に64::/64を使うとしましょう。外部のIPv4ノードのアドレスが202.214.86.196だった場合、変換後の始点アドレスは64::cad6:56c4となります。IPv4アドレス4バイト分が、IPv6アドレスの下4バイトに埋め込まれた形になります。

逆方向の通信には、上記の手順を逆に適用します。2001:db8:0:0::100から64::cad6:56c4に対してパケットを送信すると、変換サーバでIPv6宛先アドレスの下4バイトからIPv4宛先アドレス202.214.86.196を取り出します。また、あらかじめ定義されたIPv4とIPv6アドレスの対応表から、始点アドレスとして使うIPv4アドレス(この場合は192.0.2.1)を取り出し、IPv4パケットとして転送するのです。

コードはgithubで公開しています。興味のある方は http://github.com/keiichishima/map646 にアクセスしてみてください。forkも大歓迎です。