IPv6 ことはじめ

第4回 ホスト名とアドレス

IIJ技術研究所
山本和彦
iij.news (November 1999 Vol.21)

IPv6 とアプリケーション

前回は IPv6 のプラグ&プレイ 機能についてお話しました。 IPv6 を喋るコンピュータは、 たとえば Ethernet につないだ 瞬間から、インターネットに接 続されたコンピュータと通信を 開始できます。

実際ユーザはアプリケーション をどうやって利用するのでしょ うか? 答えは簡単で、IPv4 の ときとなんら変わりありません。 あまり変わらないのが IPv6 の いいところだとも言えます。

たとえば、ftp を使って、IPv6 対応の ftp.kame.net に明日ア クセスするには次のようにしま す。

	% ftp ftp.kame.net
何も変わらないので拍子抜けさ れたかもしれませんね。という 訳で、ユーザはアプリケーショ ンを使う際に何も考える必要は ありません。これで今回のアプ リケーションの使い方は終りで す...

":" のジレンマ

と言ったら怒られそうなので、 もう少しホスト名と IPv6 アド レスの関係について説明したい と思います。お勧めの方法では ないので恐縮ですが、ここでは まず IPv6 アドレスを直接アプ リケーションに指定することを 考えてみましょう。例えばこん な感じになります。
	% ftp 3ffe:501:4819:2000:5254:ff:fedc:50d2
IPv6 アドレスを直接指定でき ることは、打ち込みたくないぐ らい長いことを除けば、IPv4 の時となんら変わりないように 思います。しかし、IPv6 に区 切り文字に選択された ":" が くせものなのです。

たとえば、SSH(Secure Shell) の scp を利用する場面を想像 してみて下さい。こんな感じに なります。

	% scp 3ffe:501:4819:2000:5254:ff:fedc:50d2:foo.txt .
scp では、コンピュータの識別 子(ホスト名やアドレス)とファ イル名の区切り文字に ":" を 利用します。この例から明らか なように、どこがコンピュータ の識別子でどこがファイル名な のか分からなくなります。

「scp が特殊なんでしょう」と 思う人には、rcp、X Window サー バの DIPLAY 環境変数、URL な どの例を挙げて問題の深刻さを 分かって頂くことにします。特 に URL は頻繁に使われるので、 困りものです。

IPv6 アドレスを URL に埋め込 むために、"[" と "]" で囲む 方法が現在提案されています。 たとえば、こうなります。

	http://[3ffe:501:4819:2000:5254:ff:fedc:50d2]/~kazu/
こうすれば URL の ":" と IPv6 アドレスの ":" が区別が 付くようになります。この方法 を他のアプリケーションに適用 したら問題解決ですね。めでた しめでたし...

しかし、そうは問屋が下しませ ん。scp の例にたち返ってみま しょう。"[" と "]" を使うと 次のようになります。

	% scp [3ffe:501:4819:2000:5254:ff:fedc:50d2]:foo.txt .
"[" と "]" は UNIX の一般的 なシェルでは特殊な意味を持っ ているので、ユーザの意図とは 異なって解釈されてしまいます。 結局、"'" で囲むしかなくなり ます。
	% scp '[3ffe:501:4819:2000:5254:ff:fedc:50d2]:foo.txt' .
とっても面倒ですね。結局選択 肢としては、 しかありません。僕は今年の 4 月 1 日に ":" を止めて "=" にしようという冗談の RFC を 書こうとしました。しかし、周 りの人から「洒落にならないの で止めた方がいい」となだめら れ思い留まりました。:p

IPv6 のアドレスは長く打ちに くいこと、区切り文字 ":" は いろいろなアプリケーションと 相性が悪いことなどを考えれば、 極力ホスト名を使うという案が 妥当なのかもしれません。


DNS

それではホスト名をアプリケー ションに指定したときに、何が 起こるのか考えてみましょう。 ホスト名は、DNS(Domain Name System)を使ってアドレスに変 換されます。

IPv4 の際は、A レコードを使っ てホスト名に対し IPv4 アドレ スを登録していました。以下に 例を挙げます。

	www.kame.net A 203.178.141.212
一方 IPv6 では、IPv4 よりも アドレスの長さが 4 倍になっ たということで、AAAA レコー ドを使って IPv6 アドレスを登 録します。AAAA は、quad A と 発音されることがあります。 こんな感じになります。
	www.kame.net AAAA 3ffe:501:4819:2000:5254:ff:fedc:50d2
ホスト名に複数の IPv4 アドレ スが登録されていた場合、どう いう順番でその IPv4 アドレス 並べられて返されてくるのか決 まりはありませんでした。

同様に、あるホスト名に対して IPv4 アドレスと IPv6 アドレ スが登録されていた場合、どち らのアドレスが先に返って来る のか分かりません。IPv4 アド レスが先に返って来れば、IPv4 で通信が開始されますし、逆も また真なりです。

ここで注意して頂きたいのは、 DNS への問い合わせに関する通 信とアプリケーションの通信と で、同じ IP バージョンが使わ れなくてもよいことです。たと えば、DNS へは IPv4 で問い合 わせ、返って来た答えが IPv6 アドレスだったので、アプリケー ションは IPv6 で通信するとい うこともありえます。

このように、「IPv6 対応」と いった場合は、コンテンツを対 応させたのか、トランスポート を対応させたのか区別しないと いけません。たとえば、 「whois データベースをIPv6 に対応させました」という台詞 だけでは、IPv6 アドレスをデー タとして登録できるようになっ たのか、whois データベースを IPv6 で検索できるようになっ たのか分からないのです。

余談になりますが、IPv6 アド レスの逆引きは 16 進数の桁の 境界で分割し逆にして、 "IP6.INT" の下に PTR レコー ドとして登録します。以下に IPv4 と一緒に IPv6 の逆引き の例を示します。登録作業が大 変なのは想像に難くないでしょ う。:-)

	$ORIGIN 141.178.203.IN-ADDR.ARPA.
	212 PTR www.kame.net.
	$ORIGIN 0.0.0.2.9.1.8.4.1.0.5.0.e.f.f.3.IP6.INT.
	2.d.0.5.c.d.e.f.f.f.0.0.4.5.2.5 PTR www.kame.net.

アプリケーションの改良

今まで出てきたアプリケーショ ンは IPv4 にも IPv6 にも対応 していると暗黙に仮定して説明 してきました。現実問題として、 これまでのアプリケーションは IPv4 しかサポートしていない ので、IPv6 にも対応させる必 要があります。

これは IPv6 プログラマの仕事 であり、みなさんが実際に作業 することはほとんどないでしょ う。しかし、技術的に面白いの で、典型的な変更例を紹介する ことします。

ここでは、クライアントの場合 を考えます。IPv4 のクライア ントは、ホスト名を IPv4 アド レスに変更するために gethostbyname() という関数を 使います。IPv4 のクライアン トのソースを見れば、以下のよ うなコードを発見できるはずで す。

	int i, s;
	struct hostent *hp;
	struct servent *sp;
	struct sockaddr_in sin;

	s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	hp = gethostbyname("www.kame.net");
	sp = getservbyname("http", "tcp");
	for (i = 0; hp->h_addr_list[i]; i++) {
	   memset(&sin, 0, sizeof(sin));
	   sin.sin_family = AF_INET;
	   sin.sin_len = sizeof(sin);
	   sin.sin_port = htons(sp->s_port);
	   memcpy(&sin.sin_addr, hp->h_addr_list[i], hp->h_length);
	   if (connect(s, &sin, sizeof(sin)) < 0)
	      continue;
	   break;
	}
まず、socket() 関数を使って ソケット開いています。ここで 注目すべきは、AF_INET つまり IPv4 であることを明示的に指 定していることです。次に、 gethostbyname() 関数を使って、 ホスト名を IPv4 アドレスに変 換してます。また、 getservbyname() 関数を読んで、 サービス名をポート番号に直し ます。

次に得られた複数の IPv4 アド レスに対し、次々に connect() を試みます。コネクションを張 れる IPv4 アドレスを発見でき た時点でこのループを終了しま す。

ループの中で注目して欲しいの は、struct sockaddr_in とい う IPv4 アドレス用の構造体で す。プログラマは明らかに IPv4 を意識してコードを書い ていることが分かります。

このアプリケーションを IPv6 に対応させるには、IPv4 に加 えて IPv6 も意識しないといけ ないのでしょうか? そうでは ありません。getaddrinfo() と いう魔法の関数を利用すると、 以下のようにすっきりしたプロ グラムになります。

	int s;
	struct addrinfo hints, *res, *res0;

	memset(&hints, 0, sizeof(hints));
	hints.ai_family = PF_UNSPEC;
	hints.ai_socktype = SOCK_STREAM;
	getaddrinfo("www.kame.net", "http", &hints, &res0);
	for (res = res0; res; res = res->ai_next) {
	   s = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
	   if (connect(s, res->ai_addr, res->ai_addrlen) < 0) {
	      close(s);
	      continue;
	   }
	   break;
	}
	freeaddrinfo(res0);
注目して頂きたいのは、 AF_INET といった IP バージョ ンを明示的に指定していないこ とです。その代わりに、 PF_UNSPEC を使っています。 PF_UNSPEC とは、どちらのバー ジョンでも OK だということを 意味しています。ループの中で ソケットを動的に開いているの で、アドレスが DNS からどう いう順番で返されても平気です。

さらに言えば、ホスト名をアド レスに変換すると同時に、サー ビス名もポート番号に変換して くれるので便利です。

サーバだともう少し込み入って きますが、基本的には同じよう な作業で済みます。データの中 にアドレスを直接埋め込むよう なお行儀の悪いアプリケーショ ンの場合は、こう簡単にはいき ません。しかし、素直なアプリ ケーションであれば、IPv6 に も対応させることはそんなに大 変ではないとお分かり頂ければ 幸いです。


Email: kazu@iijlab.net
URL: http://www.mew.org/~kazu/