Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using IPv6 in a real-world Java program

IPv6 usage is slowly starting nowadays, so I'm currently in the process of fixing and updating all applications to be prepared for IPv6.

One of the applications is the Java editor JOSM (http://josm.openstreetmap.de/). Java does not really use IPv6 in the default configuration even if the OS uses IPv6.

According to http://docs.oracle.com/javase/1.5.0/docs/guide/net/ipv6_guide/#using I set java.net.preferIPv6Addresses to true to let it use IPv6. Result have been user bug reports about broken internet connection.

It seems Java only switches to use IPv6 address instead of IPv4, but does nothing else. All C/C++ based software I maintain has been changed to check and try all available IP addresses, so broken IPv6 (or IPv4) addresses are skipped as long as one of the addresses works. For me it looks like Java only tries once, which does not work in real world.

Also usually the OS prefers IPv4 over IPv6, when IPv6 is tunneled. It seems like Java does ignore this settings as well.

So my question is: Are there any good ways to get a Java application to use IPV6 by default when available without breaking the application for IPv4 users.

User-bug reports: http://josm.openstreetmap.de/ticket/8562, http://josm.openstreetmap.de/ticket/8627.

like image 814
Dirk Stöcker Avatar asked Apr 23 '13 08:04

Dirk Stöcker


1 Answers

It seems that topic is interesting for others as well, so I describe my current solution.

  • The software does an detection whether IPv6 works or not and remembers the state -> This is done by doing a TCP connect to a known IPv6 address (Ping of isReachable() is not reliable, see this bug report: https://josm.openstreetmap.de/ticket/11452).
  • Based on the remembered state the software starts with "java.net.preferIPv6Addresses" set to "true".
  • This means for a switch from IPv4 to a IPv6 network it will use IPv4 until next restart which is ok.
  • For a switch from an IPv6 enabled to an IPv4 only network it will not work at all which is solved by restarting the software.
  • In case of doubt we assume IPv6 does not work.
  • It is not possible to change "java.net.preferIPv6Addresses" after doing the detection, as that values seems to be read only before the first network connection. If there is a way to reset that state during runtime I'd like to know about it.

This solution seems to work, we have about 4% IPv6 connections in our logs ATM, but is is not really a satisfying solution.

/**
 * Check if IPv6 can be safely enabled and do so. Because this cannot be done after network activation,
 * disabling or enabling IPV6 may only be done with next start.
 */
private static void checkIPv6() {
  if ("auto".equals(Main.pref.get("prefer.ipv6", "auto"))) {
    new Thread(new Runnable() { /* this may take some time (DNS, Connect) */
      public void run() {
        boolean hasv6 = false;
        boolean wasv6 = Main.pref.getBoolean("validated.ipv6", false);
        try {
          /* Use the check result from last run of the software, as after the test, value
             changes have no effect anymore */
          if (wasv6) {
            Utils.updateSystemProperty("java.net.preferIPv6Addresses", "true");
          }
          for (InetAddress a : InetAddress.getAllByName("josm.openstreetmap.de")) {
            if (a instanceof Inet6Address) {
              if (a.isReachable(1000)) {
                /* be sure it REALLY works */
                Socket s = new Socket();
                s.connect(new InetSocketAddress(a, 80), 1000);
                s.close();
                Utils.updateSystemProperty("java.net.preferIPv6Addresses", "true");
                if (!wasv6) {
                  Main.info(tr("Detected useable IPv6 network, prefering IPv6 over IPv4 after next restart."));
                } else {
                  Main.info(tr("Detected useable IPv6 network, prefering IPv6 over IPv4."));
                }
                hasv6 = true;
              }
              break; /* we're done */
            }
          }
        } catch (IOException | SecurityException e) {
          if (Main.isDebugEnabled()) {
            Main.debug("Exception while checking IPv6 connectivity: "+e);
          }
        }
        if (wasv6 && !hasv6) {
          Main.info(tr("Detected no useable IPv6 network, prefering IPv4 over IPv6 after next restart."));
          Main.pref.put("validated.ipv6", hasv6); // be sure it is stored before the restart!
          new RestartAction().actionPerformed(null);
        }
        Main.pref.put("validated.ipv6", hasv6);
      }
    }, "IPv6-checker").start();
  }
}
like image 127
Dirk Stöcker Avatar answered Oct 10 '22 15:10

Dirk Stöcker