Java and IPv6 on BSD
Preface
More and more sites use the new internet protocol IPv6. Therefore it is also necessary to enable IPv6 on servers, written in Java™ language. Although current Java ™ runtime environments claim to support IPv6 there are still errors observed. All investigations are made on FreeBSD-7.0-STABLE, but from reading the current sources I expect the same behaviour on all unix like systems. Java™ for Windows© has a different implementation.IPv6 socket model (for server sockets)
In the current transition phase we have in most cases dual tcp/ip stacks able to carry IPv4 and IPv6 packets. So we distinguish between (server) sockets bound to address family AF_INET and AF_INET6. The ability to receive IPv4 packets on sockets bound to the AF_INET6 wildcard address (INADDR_ANY) is determined by socket optionIPV6_V6ONLY
. For details see
RFC3493. The following
table summarizes the influence of the mentioned socket option.
family | address | IPV6_V6ONLY | IPv4 | IPv6 |
---|---|---|---|---|
AF_INET | INADDR_ANY | 0 | X | |
AF_INET | IPv4 | 0 | X | |
AF_INET | INADDR_ANY | 1 | X | |
AF_INET | IPv4 | 1 | X | |
AF_INET6 | INADDR_ANY | 0 | x (IPv4-mapped address) | X |
AF_INET6 | IPv6 | 0 | X | |
AF_INET6 | INADDR_ANY | 1 | X | |
AF_INET6 | IPv6 | 1 | X |
Note: The current FreeBSD versions
ship with a global setting, that
sets the default to IPV6_V6ONLY=1
, with
OpenBSD IPv6 sockets are always IPv6-only.
Using IPv6 in Java
I cite the Networking IPv6 User Guide: "Using IPv6 in Java is easy; it is transparent and automatic. Unlike in many other languages, no porting is necessary. In fact, there is no need to even recompile the source files." and further "The Java networking stack will first check whether IPv6 is supported on the underlying OS. If IPv6 is supported, it will try to use the IPv6 stack. More specifically, on dual-stack systems it will create an IPv6 socket."A further statement made in the networking guide is
"A general property of a dual-stack node is that an IPv6 socket can communicate both with an
IPv4 and IPv6 peer at the transport layer (TCP or UDP)". This is
wrong, because
the condition is missing: , if the socket is bound to the
INADDR_ANY wildcard address AND the socket option allows the use
of mapped IPv4 addresses (IPV6_V6ONLY=0
). The Java runtime
does not set the socket option, so the behaviour is operating system dependent.
java.net.preferIPv4Stack
It is possible to set the system propertyjava.net.preferIPv4Stack
to
true
. In that case only IPv4 sockets are created and no IPv6
communication is possible. So in reality it does not set any preferences, it
toggles between an exclusive IPv6 and an exclusive IPv4 stack.
Consequences
Please read again "on dual-stack systems it will create an IPv6 socket". That means that there is alyways an IPv6 socket created, independed from desired address family. Of course such a socket can not bind to an IPv4 address. Thats the reason for many users receiving ajava.net.BindException: Can't assign requested address
when
enabling IPv6 on a machine.
An IPv6 enabled java application can not bind to an IPv4 address
and only under the condition, that the socket is bound to
INADDR_ANY and IPV6_V6ONLY=0
it is able to receive IPv4 packets!
To make it worse, it is not even possible to bind to localhost
.
With the default settings the name localhost
is resolved to the IPv4
address and of course binding of that IPv4 address to an AF_INET6 socket fails.
My conclusion
It is very difficult or impossible to use the current (1.6.0) Java implementation for server services if they should listen on IPv4 and IPv6 addresses. With many BSD's default settings it is not even possible to bind to a combined IPv4/IPv6 wildcard address.The current Sun Java implementation should be changed in a way, that the socket is created with the right address family after the bind address has been determined.
To use Tomcat with IPv6 the native connector can be used. It does not show the described flaws.
Test results
The statements above can be derived from official Java documentation, but I believe not everybody reads them in detail and sees immediately where the error is to search. The same happened to me, so I made some tests first. Here you can see the results for the current JDK on FreeBSD-7.0. JDK1.4.2 and diablo-JDK1.5.0 have no working IPv6 support.
version | address | result | bound address/message | bind type | V6ONLY | preferIPv4Stack | preferIPv6Addresses |
---|---|---|---|---|---|---|---|
1.6.0_03-p4 | INADDR_ANY | OK | 0.0.0.0/0.0.0.0:10101 (Inet4Address) | 6 | true | false | false |
1.6.0_03-p4 | 127.0.0.1 | ERR | java.net.BindException: Can't assign requested address (/127.0.0.1) | - | true | false | false |
1.6.0_03-p4 | ::1 | OK | /0:0:0:0:0:0:0:1:10101 (Inet6Address) | 6 | true | false | false |
1.6.0_03-p4 | localhost | ERR | java.net.BindException: Can't assign requested address (localhost/127.0.0.1) | - | true | false | false |
1.6.0_03-p4 | INADDR_ANY | OK | ::/0:0:0:0:0:0:0:0:10101 (Inet6Address) | 6 | true | false | true |
1.6.0_03-p4 | 127.0.0.1 | ERR | java.net.BindException: Can't assign requested address (/127.0.0.1) | - | true | false | true |
1.6.0_03-p4 | ::1 | OK | /0:0:0:0:0:0:0:1:10101 (Inet6Address) | 6 | true | false | true |
1.6.0_03-p4 | localhost | OK | localhost/0:0:0:0:0:0:0:1:10101 (Inet6Address) | 6 | true | false | true |
1.6.0_03-p4 | INADDR_ANY | OK | 0.0.0.0/0.0.0.0:10101 (Inet4Address) | 4 | true | true | false |
1.6.0_03-p4 | 127.0.0.1 | OK | /127.0.0.1:10101 (Inet4Address) | 4 | true | true | false |
1.6.0_03-p4 | ::1 | ERR | java.net.SocketException: Protocol family unavailable (/0:0:0:0:0:0:0:1) | - | true | true | false |
1.6.0_03-p4 | localhost | OK | localhost/127.0.0.1:10101 (Inet4Address) | 4 | true | true | false |
1.6.0_03-p4 | INADDR_ANY | OK | 0.0.0.0/0.0.0.0:10101 (Inet4Address) | 4 | true | true | true |
1.6.0_03-p4 | 127.0.0.1 | OK | /127.0.0.1:10101 (Inet4Address) | 4 | true | true | true |
1.6.0_03-p4 | ::1 | ERR | java.net.SocketException: Protocol family unavailable (/0:0:0:0:0:0:0:1) | - | true | true | true |
1.6.0_03-p4 | localhost | OK | localhost/127.0.0.1:10101 (Inet4Address) | 4 | true | true | true |
1.6.0_03-p4 | INADDR_ANY | OK | 0.0.0.0/0.0.0.0:10101 (Inet4Address) | 46 | false | false | false |
1.6.0_03-p4 | 127.0.0.1 | OK | /127.0.0.1:10101 (Inet4Address) | 4 | false | false | false |
1.6.0_03-p4 | ::1 | OK | /0:0:0:0:0:0:0:1:10101 (Inet6Address) | 6 | false | false | false |
1.6.0_03-p4 | localhost | OK | localhost/127.0.0.1:10101 (Inet4Address) | 4 | false | false | false |
1.6.0_03-p4 | INADDR_ANY | OK | ::/0:0:0:0:0:0:0:0:10101 (Inet6Address) | 46 | false | false | true |
1.6.0_03-p4 | 127.0.0.1 | OK | /127.0.0.1:10101 (Inet4Address) | 4 | false | false | true |
1.6.0_03-p4 | ::1 | OK | /0:0:0:0:0:0:0:1:10101 (Inet6Address) | 6 | false | false | true |
1.6.0_03-p4 | localhost | OK | localhost/0:0:0:0:0:0:0:1:10101 (Inet6Address) | 6 | false | false | true |
1.6.0_03-p4 | INADDR_ANY | OK | 0.0.0.0/0.0.0.0:10101 (Inet4Address) | 4 | false | true | false |
1.6.0_03-p4 | 127.0.0.1 | OK | /127.0.0.1:10101 (Inet4Address) | 4 | false | true | false |
1.6.0_03-p4 | ::1 | ERR | java.net.SocketException: Protocol family unavailable (/0:0:0:0:0:0:0:1) | - | false | true | false |
1.6.0_03-p4 | localhost | OK | localhost/127.0.0.1:10101 (Inet4Address) | 4 | false | true | false |
1.6.0_03-p4 | INADDR_ANY | OK | 0.0.0.0/0.0.0.0:10101 (Inet4Address) | 4 | false | true | true |
1.6.0_03-p4 | 127.0.0.1 | OK | /127.0.0.1:10101 (Inet4Address) | 4 | false | true | true |
1.6.0_03-p4 | ::1 | ERR | java.net.SocketException: Protocol family unavailable (/0:0:0:0:0:0:0:1) | - | false | true | true |
1.6.0_03-p4 | localhost | OK | localhost/127.0.0.1:10101 (Inet4Address) | 4 | false | true | true |