With IPv6 deployment I’d like to put my money where my mouth is so after the IETF IPv6 only network experiment I wanted to make sure that my jabber server runs both IPv6 and IPv4. I ran into a bunch of problems:
OpenFire is documented to run IPv6. It draws its capabilities from the java implementation it runs on.
The default Java virtual machine that comes with the FreeBSD ports is diablo-jdk15. That port does not come with IPv6 enabled. You can test that by using the ListNets program that is available from the Java Tutorial site. Copy and paste the code on that page into a file called ListNets.java and test
$ javac ListNets.java
$ java -version
java version "1.5.0"
Java(TM) 2 Runtime Environment, Standard Edition (build diablo-1.5.0-b01)
Java HotSpot(TM) Client VM (build diablo-1.5.0_07-b01, mixed mode)
$ java ListNets
Display name: lo0
Display name: rl0
I double checked if there is a compile time configuration option to turn on IPv6 for diablo, there is none. Off to install the jdk1.5 port. Here you have to start with a "make config" in order to enable IPv6 support and compilation will take an hour or so. Once installed the test program will show all interfaces:
$ /usr/local/jdk1.5.0/bin/java -version
java version "1.5.0_14-p8"
Java(TM) 2 Runtime Environment, Standard Edition (build )
Java HotSpot(TM) Client VM (build 1.5.0_14-p8-olaf_28_apr_2008_16_18, mixed mode)
$ /usr/local/jdk1.5.0/bin/java ListNets
Display name: lo0
Display name: rl0
Starting openfire manually, after setting JAVA_HOME to /usr/local/jdk1.5.0/ one can validate that the program actually binds to the tcp6 sockets:
openfire java 96677 11 stream (not connected) openfire java 96677 13 tcp6 *:7777 *:*
openfire java 96677 16 tcp6 *:5269 *:*
openfire java 96677 17 tcp6 *:5229 *:*
openfire java 96677 22 tcp6 *:9090 *:*
openfire java 96677 25 tcp6 *:9091 *:*
openfire java 96677 30 tcp6 *:5222 *:*
openfire java 96677 33 tcp6 *:5223 *:*
Testing the IPv6 connection towards the openfire management interface using "telnet ::1 9090" demonstrates that the IPv6 connection works. However a "telnet 127.0.0.1 9090" fails. So we only have IPv6 and no IPv4 connectivity.
So, why is this?
FreeBSD (and open and net BSD) turn off IPv4 binding to IPv6 sockets by default. This behavior is controlled using the "net.inet6.ip6.v6only" kernel option. One workaround to solve this problem is to set net.inet6.ip6.v6only=0. However this could lead to cause possible security problems. The security problems in Itojun’s draft are the only security issues I am aware off and they can be mitigated by filtering on ::ffff:0:0/96 network traffic e.g. at ones network perimeter. That traffic should not be on the network in the first place (see e.g. informationa RFC number RFC5156 section 2.2).
With net.inet6.ip6.v6only=1 it is impossible to use AF_INET6 to bind to both IPv6 and IPv4 addresses.
As an alternative I have tried to bind to interfaces explicitly in the openfire.xml configuration but that fails too as it seems that openfire only accepts one instance of the network.interface configuration option. I would argue that on multihomed machines one may want to bind to a subset of the available addresses instead of binding to the wildcard and that allowing for address family agnostic specification of one or more interfaces is the best solution.
Starting two instances of openfire, one on IPv6 and one on IPv4, by specifically binding to the v6 and v4 interfaces is no solution either because the IPv4 server would not know of the presence of clients registered on the IPv6 server.
In order to get a working dual stack openfire running on FreeBSD do the following.
- add the following lines to your /etc/rc.conf:
# Allow IPv4-mapped addresses ipv6_ipv4mapping="YES"
- Make sure your java distribution supports IPv6.
- /usr/ports/java/diablo-jdk15 does not support IPv6
- /usr/ports/java/jdk15 and /usr/ports/java/jdk16 do support IPv6, but you have to rune make config and specifically set IPV6 support before building!
- Edit your /usr/local/etc/rc.d/openfire to allow for a different java vm to be used:
— /usr/local/etc/rc.d/openfire.bak 2008-05-02 10:22:16.000000000 +0200
+++ /usr/local/etc/rc.d/openfire 2008-05-02 10:23:00.000000000 +0200
@@ -20,6 +20,9 @@
# Set it to java home directory.
# openfire_javargs (args): Set to -Xmx256M by default.
# See java -h for available arguments.
+# openfire_java_home (path): Set to /usr/local by default.
+# Sets JAVA_HOME before calling java
+# See javavm(1)
@@ -34,6 +37,9 @@
Hopefully the openfire ports maintainer will apply this patch, or offer a similar solution in a forthcoming release.
- Set openfire_java_home in your /etc/rc.conf:
# Start openfire, make sure to use an IPv6 enabled java engine
openfire_java_home="/usr/local/jdk1.6.0/" # or your local variety
- Verify that both IPv4 and IPv6 are being used. Allow openfire a few seconds to bind to the various interfaces en then use sockstat to verify if tcp4 and tcp6 are in use:
# sockstat | grep openfire
openfire java 12742 27 stream (not connected)
openfire java 12742 29 udp46 *:10020 *:*
openfire java 12742 30 tcp46 *:7777 *:*
openfire java 12742 31 tcp6 ::1:60235 ::1:60234
openfire java 12742 33 tcp46 *:5229 *:*
openfire java 12742 35 tcp46 *:5269 *:*
openfire java 12742 45 tcp46 *:9990 *:*
openfire java 12742 49 tcp46 *:9991 *:*
- Don’t forget to block ::ffff:0:0/96 traffic on your network
In the DNS I pointed the SRV records to a host with both an IPv4 and IPv6 address, something like:
_xmpp-client._tcp.jabber.secret-wg.org. 300 IN SRV 0 0 5222 jabber.secret-wg.org
jabber.secret-wg.org. 3600 IN A 220.127.116.11
3600 AAAA 2001:7b8:206:1:0:1234:be21:e31e
It turns out that with the clients I tried (iChat and Adium) I was not able to connect to the server while being connected to an IPv6 only network. When specifically connecting to the server by entering jabber-6.secret-wg.org (with AAAA RRs and without A RRs) things work like a charm.
Note also that you have to generate a certificate with a number of alt names: jabber.secret-wg.org *.jabber.secret-wg.org, jabber-6.secret-wg.org, and *.jabber-6.secret-wg.org. Create an openssl configuration file with information similar to the following, create a certificate request and get it signed with for instance cacert.
commonName = Common Name (eg, YOUR name)
commonName_default = jabber.secret-wg.org
0.subjectAltName = Subject altname
0.subjectAltName_default = DNS:*.jabber.secret-wg.org
1.subjectAltName = Subject altname
1.subjectAltName_default = DNS:jabber-6.secret-wg.org
2.subjectAltName = Subject altname
2.subjectAltName_default = DNS:*.jabber-6.secret-wg.org
[Edited to clarify a few points on April 30]
[Edited to add the Howto section on May 2]
[Edited to add the Some Tweaks section on May 7]