diff --git a/apps/routerconsole/java/src/net/i2p/router/web/ConfigNetHandler.java b/apps/routerconsole/java/src/net/i2p/router/web/ConfigNetHandler.java
index 67c47fbb7f7a6b88cb1be0a5dee5289b27745330..827cf47f616b14f0436d74a589e88ee6d74aee49 100644
--- a/apps/routerconsole/java/src/net/i2p/router/web/ConfigNetHandler.java
+++ b/apps/routerconsole/java/src/net/i2p/router/web/ConfigNetHandler.java
@@ -7,7 +7,7 @@ import java.util.Map;
 
 import net.i2p.router.Router;
 import net.i2p.router.transport.FIFOBandwidthRefiller;
-import net.i2p.router.transport.TransportImpl;
+import net.i2p.router.transport.TransportUtil;
 import net.i2p.router.transport.TransportManager;
 import net.i2p.router.transport.udp.UDPTransport;
 import net.i2p.router.web.ConfigServiceHandler;
@@ -370,7 +370,8 @@ public class ConfigNetHandler extends FormHandler {
             addFormError(_("Invalid address") + ": " + addr);
             return false;
         }
-        boolean rv = TransportImpl.isPubliclyRoutable(iab);
+        // TODO set IPv6 arg based on configuration?
+        boolean rv = TransportUtil.isPubliclyRoutable(iab, true);
         if (!rv)
             addFormError(_("The hostname or IP {0} is not publicly routable", addr));
         return rv;
diff --git a/apps/routerconsole/java/src/net/i2p/router/web/SummaryHelper.java b/apps/routerconsole/java/src/net/i2p/router/web/SummaryHelper.java
index bff1abdd540f535ea5b6810e524665b3a305056a..7d75790aedd418577d55a28fdcd28301f5538f0b 100644
--- a/apps/routerconsole/java/src/net/i2p/router/web/SummaryHelper.java
+++ b/apps/routerconsole/java/src/net/i2p/router/web/SummaryHelper.java
@@ -24,7 +24,7 @@ import net.i2p.router.RouterContext;
 import net.i2p.router.RouterVersion;
 import net.i2p.router.TunnelPoolSettings;
 import net.i2p.router.networkdb.kademlia.FloodfillNetworkDatabaseFacade;
-import net.i2p.router.transport.ntcp.NTCPAddress;
+import net.i2p.router.transport.TransportUtil;
 import net.i2p.stat.Rate;
 import net.i2p.stat.RateStat;
 import net.i2p.util.PortMapper;
@@ -159,7 +159,8 @@ public class SummaryHelper extends HelperBase {
         switch (status) {
             case CommSystemFacade.STATUS_OK:
                 RouterAddress ra = routerInfo.getTargetAddress("NTCP");
-                if (ra == null || (new NTCPAddress(ra)).isPubliclyRoutable())
+                // TODO set IPv6 arg based on configuration?
+                if (ra == null || TransportUtil.isPubliclyRoutable(ra.getIP(), true))
                     return _("OK");
                 return _("ERR-Private TCP Address");
             case CommSystemFacade.STATUS_DIFFERENT:
diff --git a/router/java/src/net/i2p/router/RouterVersion.java b/router/java/src/net/i2p/router/RouterVersion.java
index f5c92645490ac230cc1d9da5a1333a329d7b0385..b15b3200517d4a7f3ff248a1d995510afca6fb69 100644
--- a/router/java/src/net/i2p/router/RouterVersion.java
+++ b/router/java/src/net/i2p/router/RouterVersion.java
@@ -21,7 +21,7 @@ public class RouterVersion {
     public final static long BUILD = 20;
 
     /** for example "-test" */
-    public final static String EXTRA = "";
+    public final static String EXTRA = "-ipv6";
     public final static String FULL_VERSION = VERSION + "-" + BUILD + EXTRA;
     public static void main(String args[]) {
         System.out.println("I2P Router version: " + FULL_VERSION);
diff --git a/router/java/src/net/i2p/router/transport/TransportImpl.java b/router/java/src/net/i2p/router/transport/TransportImpl.java
index 52d1f201683533e82c313780d6812cafc4398df1..81a498bced2c0d612800f2af4bc7c7c6852b9d28 100644
--- a/router/java/src/net/i2p/router/transport/TransportImpl.java
+++ b/router/java/src/net/i2p/router/transport/TransportImpl.java
@@ -668,26 +668,18 @@ public abstract class TransportImpl implements Transport {
         }
     }
 
-    /** @param addr non-null */
-    public static boolean isPubliclyRoutable(byte addr[]) {
-        if (addr.length == 4) {
-            int a0 = addr[0] & 0xFF;
-            if (a0 == 127) return false;
-            if (a0 == 10) return false;
-            int a1 = addr[1] & 0xFF;
-            if (a0 == 172 && a1 >= 16 && a1 <= 31) return false;
-            if (a0 == 192 && a1 == 168) return false;
-            if (a0 >= 224) return false; // no multicast
-            if (a0 == 0) return false;
-            if (a0 == 169 && a1 == 254) return false;
-            // 5/8 allocated to RIPE (30 November 2010)
-            //if ((addr[0]&0xFF) == 5) return false;  // Hamachi
-            return true; // or at least possible to be true
-        } else if (addr.length == 16) {
-            return false;
-        } else {
-            // ipv?
-            return false;
-        }
+    /**
+     *  @since IPv6
+     */
+    protected TransportUtil.IPv6Config getIPv6Config() {
+        return TransportUtil.getIPv6Config(_context, getStyle());
+    }
+
+    /**
+     *  @param addr non-null
+     */
+    protected boolean isPubliclyRoutable(byte addr[]) {
+        return TransportUtil.isPubliclyRoutable(addr,
+                                                getIPv6Config() != TransportUtil.IPv6Config.IPV6_DISABLED);
     }
 }
diff --git a/router/java/src/net/i2p/router/transport/TransportUtil.java b/router/java/src/net/i2p/router/transport/TransportUtil.java
index f581b1944344beb0faae3721d3b3b7c3385ad494..cc708d1142ff204a542e7c593b7e2dec6aea5368 100644
--- a/router/java/src/net/i2p/router/transport/TransportUtil.java
+++ b/router/java/src/net/i2p/router/transport/TransportUtil.java
@@ -8,6 +8,8 @@ package net.i2p.router.transport;
  *
  */
 
+import java.net.InetAddress;
+import java.net.UnknownHostException;
 import java.util.HashMap;
 import java.util.Map;
 
@@ -75,4 +77,38 @@ public abstract class TransportUtil {
             return c;
         return IPv6Config.IPV6_DISABLED;
     }
+
+    /**
+     *  @param addr non-null
+     *  @since IPv6 moved from TransportImpl
+     */
+    public static boolean isPubliclyRoutable(byte addr[], boolean allowIPv6) {
+        if (addr.length == 4) {
+            int a0 = addr[0] & 0xFF;
+            if (a0 == 127) return false;
+            if (a0 == 10) return false;
+            int a1 = addr[1] & 0xFF;
+            if (a0 == 172 && a1 >= 16 && a1 <= 31) return false;
+            if (a0 == 192 && a1 == 168) return false;
+            if (a0 >= 224) return false; // no multicast
+            if (a0 == 0) return false;
+            if (a0 == 169 && a1 == 254) return false;
+            // 5/8 allocated to RIPE (30 November 2010)
+            //if ((addr[0]&0xFF) == 5) return false;  // Hamachi
+            return true; // or at least possible to be true
+        } else if (addr.length == 16) {
+            if (allowIPv6) {
+                try {
+                    InetAddress ia = InetAddress.getByAddress(addr);
+                    return
+                        (!ia.isLinkLocalAddress()) &&
+                        (!ia.isMulticastAddress()) &&
+                        (!ia.isAnyLocalAddress()) &&
+                        (!ia.isLoopbackAddress()) &&
+                        (!ia.isSiteLocalAddress());
+                } catch (UnknownHostException uhe) {}
+            }
+        }
+        return false;
+    }
 }
diff --git a/router/java/src/net/i2p/router/transport/UPnP.java b/router/java/src/net/i2p/router/transport/UPnP.java
index cee870c96ec43c4b519bad569bcf35ee5fae1aae..e8d9dd9a74f759adb12c3a217a5415792eaeec9f 100644
--- a/router/java/src/net/i2p/router/transport/UPnP.java
+++ b/router/java/src/net/i2p/router/transport/UPnP.java
@@ -146,7 +146,7 @@ class UPnP extends ControlPoint implements DeviceChangeListener, EventListener {
 			InetAddress detectedIP = InetAddress.getByName(natAddress);
 
 			short status = DetectedIP.NOT_SUPPORTED;
-			thinksWeAreDoubleNatted = !TransportImpl.isPubliclyRoutable(detectedIP.getAddress());
+			thinksWeAreDoubleNatted = !TransportUtil.isPubliclyRoutable(detectedIP.getAddress(), false);
 			// If we have forwarded a port AND we don't have a private address
 			if (_log.shouldLog(Log.WARN))
 				_log.warn("NATAddress: \"" + natAddress + "\" detectedIP: " + detectedIP + " double? " + thinksWeAreDoubleNatted);
diff --git a/router/java/src/net/i2p/router/transport/UPnPManager.java b/router/java/src/net/i2p/router/transport/UPnPManager.java
index fb368b37b8cf94efadc6f814021114469b655493..10355f64b51ca2bef4ccc304596e814afe7f03b8 100644
--- a/router/java/src/net/i2p/router/transport/UPnPManager.java
+++ b/router/java/src/net/i2p/router/transport/UPnPManager.java
@@ -156,7 +156,7 @@ class UPnPManager {
             if (ips != null) {
                 for (DetectedIP ip : ips) {
                     // store the first public one and tell the transport manager if it changed
-                    if (TransportImpl.isPubliclyRoutable(ip.publicAddress.getAddress())) {
+                    if (TransportUtil.isPubliclyRoutable(ip.publicAddress.getAddress(), false)) {
                         if (_log.shouldLog(Log.DEBUG))
                             _log.debug("External address: " + ip.publicAddress + " type: " + ip.natType);
                         if (!ip.publicAddress.equals(_detectedAddress)) {
diff --git a/router/java/src/net/i2p/router/transport/ntcp/NTCPAddress.java b/router/java/src/net/i2p/router/transport/ntcp/NTCPAddress.java
index b37d23cc11f7aea5ec0401e4029ce2f0e7360613..43d051af476344943406d5ff27dd7a6177917251 100644
--- a/router/java/src/net/i2p/router/transport/ntcp/NTCPAddress.java
+++ b/router/java/src/net/i2p/router/transport/ntcp/NTCPAddress.java
@@ -88,16 +88,6 @@ public class NTCPAddress {
     public int getPort() { return _port; }
     //public void setPort(int port) { _port = port; }
     
-    public boolean isPubliclyRoutable() {
-        return isPubliclyRoutable(_host);
-    }
-
-    public static boolean isPubliclyRoutable(String host) {
-        if (host == null) return false;
-        byte quad[] = Addresses.getIP(host);
-        return TransportImpl.isPubliclyRoutable(quad);
-    }
-    
     @Override
     public String toString() { return _host + ":" + _port; }