前のポストで、Debian で net.ipv6.bindv6only が 1 になったために Java でのネットワーク接続ができなくなった、と書きました。
今回はそれについて、追加で分かったことと思ったことです。
この問題が残っている Java は Sun の JDK だけで、OpenJDK では bindv6only=1 でも動作するようになっているようだ、というのが1点。
しかしそれが問題のすべてではなさそうだというのがもう1点です。
OpenJDK なら OK?
ご指摘をいただいて、Java でのネットワーク接続ができなくなってしまうという問題が残っているのは Sun の JDK (sun-java6-jdk)のほうで、OpenJDK (openjdk-6-jdk) では bindv6only が1でも動作するということが分かりました。不勉強で、OpenJDK なんてまだまだ実験段階のものと思い込んでしまっていました。
以下のテストコード(TestConnect.java) で
1 | java TestConnect java.sun.com |
と実行すると、bindv6only=1 の環境では Sun JDK は SocketException でエラーになりますが、OpenJDK ではエラーにはなりません。(いずれも、bindv6only=0 のときにはエラーではありません。)
import java.net.InetAddress;
import java.net.Socket;
public class TestConnect {
public static final void main(String[] args) {
if (args.length < 1) System.exit(1);
String host = args[0];
try {
InetAddress addr = InetAddress.getByName(host);
System.out.println(addr);
Socket sock = new Socket(addr, 80);
sock.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
(でも肝心の Eclipse では OpenJDK 使用時にもやっぱり bindv6only=1 ではネットワークエラーになってしまうようです...。Debian パッケージの Eclipse ではないからかもしれませんが。)
net.ipv6.bindv6only=1 で大丈夫?
ところで、この net.ipv6.bindv6only が、なぜ 1 になってしまったのか、その経緯がよく分からなかったので調べてみました。(僕自身は IPv6 自体に関する知識があまりなく、また IPv6 でのネットワークプログラミングの経験もないので勘違いしている箇所もあると思います。詳しい方は教えていただければと思います。)
そもそも net.ipv6.bindv6only は RFC 3493 で定義されている IPV6_V6ONLY オプションのデフォルト値を設定するためのもののようです。
RFC 3493 では IPv6 用のソケット(AF_INET6 のソケット)で IPv4 のノードに接続したり、あるいは IPv4 のノードからの接続を IPv6 用ソケットを受け付けたりすることを認めています(3.7 Compatibility with IPv4 Nodes)。その上で、これを制限して IPv6 ソケットは IPv6 アドレスとのみ通信可能にするためのオプションが IPV6_V6ONLY です。
Linux では net.ipv6.bindv6only のデフォルト値は 0 (OFF) で、これによって IPV6_V6ONLY はデフォルトでは OFF であり、したがって RFC 3493 にあるように AF_INET6 のソケットは IPv4 と IPv6 の両方と通信することが可能です。そして IPv4 と IPv6 を区別して扱いたいアプリケーションは、そのアプリケーション内でソケットのオプションに IPV6_V6ONLY を指定すればそれは可能なのです。
Debian でも従来は、他の Linux システムと同様に net.ipv6.bindv6only のデフォルト値は 0 でした。
ところが2009年の10月に、Debian の netbase パッケージのメンテナである Marco d'Itri 氏によって、squeeze ではこのデフォルト値を 1 にしよう、という提案がされました(defaulting to net.ipv6.bindv6only=1 for squeeze)。
デフォルト値の変更の理由としては、他のOS(Windows や NetBSD?)に合わせようということと、プログラム内でのログや設定ファイルの処理を簡潔にできる、というものでした。また IPV6_V6ONLY オプションを指定しなくてすむ、ということもあるのでしょう。
この変更は2009年12月に実施され、その直後から多くのアプリケーションでネットワーク接続が動作しないというバグレポートが出てきました。特に Java では全てのネットワーク接続が失敗するようになってしまったため、Java を使用しているたくさんのシステムに影響しました。そして多くの人は、その原因が netbase パッケージのアップグレード時になんの警告もなくシステムに入り込んだ net.ipv6.bindv6only = 1 という指定だったことに気付くまでに、数時間から数日を費やしてしまったのです。
この選択は大失敗だったと思うのですが、しかし net.ipv6.bindv6only = 1 への変更は取り下げられませんでした。
問題があるのは net.ipv6.bindv6only = 1 のときに破綻するアプリケーションのほうだ、と主張します。
前述のように RFC 3493 は AF_INET6 のソケットが IPv4 と通信する動作を許しています(むしろ推奨している)から、本来アプリケーション側が責められるいわれはありません。
しかしその点を指摘されても(自身が参照したものであるにも関わらず) 「RFC 3493 は Informational だ (だから従う必要はない)」などと強弁します。さらに同様に RFC 3493 を引用したレポータに対して、「RFC を引用する前にネットワークの基礎を勉強しな」と言い放つ始末です。これはひどい。あんまりです。(さらには「バカなやつを話を聞く必要はない」 なんて言ってるし。)
罪のないアプリケーションにこれだけの影響を与えてしまうことを考えると、デフォルト値を変更するメリットにはいかにも説得力が感じられません。提案時に挙げられたメリットは、Debian だけでなく他のディストリビューションなど、アプリケーションが対応する全ての環境で統一されなければ意味がないものだからです。しかし現状では(Ubuntu はともかく) RedHat やその他のディストリビューションが Debian に追従する見込みはまずないのではないでしょうか。
現在では OpenJDK をはじめ、Debian の多くのアプリケーションは net.ipv6.bindv6only が 1 でも動作するように改修されました。
しかし Sun JDK への変更依頼は低いプライオリティーに留められたままです。なにしろ修正の必要な理由が Debian の net.ipv6.bindv6only のデフォルト値が 1 になったから、というだけなのですからまったく説得力がありません。なぜ 1 が正しいのかを、誰も説明できないのではないでしょうか。ナンセンスだ、と言われるのは仕方がありません。
現在は新しいデフォルト値(1)での動作に困れば自分で net.ipv6.bindv6only = 0 とセットすればそれで済みます。しかし今後 Debian で bindv6only = 1 であることに依存したアプリケーションが増えてしまえば(これがしたくて変えたんでしょう?) 自分でパラメータを変えて運用することは事実上できなくなってしまいます。バイナリ配布されるアプリケーションなどでは、Debian で動作しないものが増えてきてしまうかもしれません。
Debian がおかしな具合に独自路線を歩むのは、避けてほしいところです。
net.ipv6.bindv6only は 0 に戻すべきです。
[4月6日追記]
ひとまず Debian の BTS に登録してみました。(でも Subject では痛恨のスペルミス。)
[9月14日追記]
bindv6only のデフォルト値を 1 とする変更は、6月25日に更新された netbase 4.42 で取り下げられていたそうです。よかったー。
[...] Debian の net.ipv6.bindv6only = 1 への変更は心配 [...]