diff --git a/apps/routerconsole/java/bundle-messages.sh b/apps/routerconsole/java/bundle-messages.sh
index cfc66949aa1c22bb5df8406a3aca8c44f4a8a1cd..90a8a391a6923ba826abc38104ac1646141894ce 100755
--- a/apps/routerconsole/java/bundle-messages.sh
+++ b/apps/routerconsole/java/bundle-messages.sh
@@ -36,20 +36,22 @@ fi
 # list specific files in core/ and router/ here, so we don't scan the whole tree
 ROUTERFILES="\
    ../../../core/java/src/net/i2p/data/DataHelper.java \
-   ../../../core/java/src/net/i2p/util/LogWriter.java \
-   ../../../router/java/src/net/i2p/router/tasks/CoalesceStatsEvent.java \
-   ../../../router/java/src/net/i2p/router/RouterThrottleImpl.java \
-   ../../../router/java/src/net/i2p/router/tunnel/pool/BuildHandler.java \
-   ../../../router/java/src/net/i2p/router/transport/TransportManager.java \
-   ../../../router/java/src/net/i2p/router/transport/GetBidsJob.java \
    ../../../router/java/src/net/i2p/router/Blocklist.java \
-   ../../../router/java/src/net/i2p/router/transport/ntcp/EstablishState.java \
+   ../../../router/java/src/net/i2p/router/CommSystemFacade.java \
+   ../../../router/java/src/net/i2p/router/RouterThrottleImpl.java \
    ../../../router/java/src/net/i2p/router/networkdb/reseed/Reseeder.java \
+   ../../../router/java/src/net/i2p/router/tasks/CoalesceStatsEvent.java \
    ../../../router/java/src/net/i2p/router/transport/CommSystemFacadeImpl.java \
+   ../../../router/java/src/net/i2p/router/transport/GetBidsJob.java \
+   ../../../router/java/src/net/i2p/router/transport/TransportManager.java \
+   ../../../router/java/src/net/i2p/router/transport/UPnP.java \
+   ../../../router/java/src/net/i2p/router/transport/UPnPManager.java \
+   ../../../router/java/src/net/i2p/router/transport/ntcp/EstablishState.java \
    ../../../router/java/src/net/i2p/router/transport/ntcp/NTCPTransport.java \
    ../../../router/java/src/net/i2p/router/transport/udp/UDPTransport.java \
-   ../../../router/java/src/net/i2p/router/transport/UPnP.java \
-   ../../../router/java/src/net/i2p/router/transport/UPnPManager.java"
+   ../../../router/java/src/net/i2p/router/tunnel/pool/BuildHandler.java \
+   ../../../core/java/src/net/i2p/util/LogWriter.java \
+"
 
 # add ../java/ so the refs will work in the po file
 JPATHS="../java/src ../jsp/WEB-INF ../java/strings $ROUTERFILES"
diff --git a/apps/routerconsole/java/src/net/i2p/router/web/ConfigNetHelper.java b/apps/routerconsole/java/src/net/i2p/router/web/ConfigNetHelper.java
index 77005cf9d135e0de285706d9f34dc2185bb40c28..cd2f89eba18e3e7b21c5284dfb272d09f0116a66 100644
--- a/apps/routerconsole/java/src/net/i2p/router/web/ConfigNetHelper.java
+++ b/apps/routerconsole/java/src/net/i2p/router/web/ConfigNetHelper.java
@@ -7,7 +7,7 @@ import java.util.Set;
 
 import net.i2p.data.DataHelper;
 import net.i2p.data.router.RouterAddress;
-import net.i2p.router.CommSystemFacade;
+import net.i2p.router.CommSystemFacade.Status;
 import net.i2p.router.Router;
 import net.i2p.router.transport.TransportManager;
 import net.i2p.router.transport.TransportUtil;
@@ -148,14 +148,18 @@ public class ConfigNetHelper extends HelperBase {
         return "";
     }
 
+    /**
+     *  This isn't updated for the new statuses, but it's commented out in the jsp.
+     *  @deprecated unused, to be fixed if needed
+     */
     public String getRequireIntroductionsChecked() {
-        short status = _context.commSystem().getReachabilityStatus();
+        Status status = _context.commSystem().getStatus();
         switch (status) {
-            case CommSystemFacade.STATUS_OK:
-            case CommSystemFacade.STATUS_UNKNOWN:
+            case OK:
+            case UNKNOWN:
                 return getChecked(UDPTransport.PROP_FORCE_INTRODUCERS);
-            case CommSystemFacade.STATUS_DIFFERENT:
-            case CommSystemFacade.STATUS_REJECT_UNSOLICITED:
+            case DIFFERENT:
+            case REJECT_UNSOLICITED:
             default:
                 return CHECKED;
         }
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 e97dd34806b64349fffe0f5945ddbabdf5de0534..508cf00e01daf708ce9a38b68e032c5053f3891c 100644
--- a/apps/routerconsole/java/src/net/i2p/router/web/SummaryHelper.java
+++ b/apps/routerconsole/java/src/net/i2p/router/web/SummaryHelper.java
@@ -17,7 +17,7 @@ import net.i2p.data.Hash;
 import net.i2p.data.LeaseSet;
 import net.i2p.data.router.RouterAddress;
 import net.i2p.data.router.RouterInfo;
-import net.i2p.router.CommSystemFacade;
+import net.i2p.router.CommSystemFacade.Status;
 import net.i2p.router.Router;
 import net.i2p.router.RouterContext;
 import net.i2p.router.RouterVersion;
@@ -154,34 +154,48 @@ public class SummaryHelper extends HelperBase {
         if (routerInfo == null)
             return _("Testing");
 
-        int status = _context.commSystem().getReachabilityStatus();
+        Status status = _context.commSystem().getStatus();
         switch (status) {
-            case CommSystemFacade.STATUS_OK:
+            case OK:
+            case IPV4_OK_IPV6_UNKNOWN:
+            case IPV4_OK_IPV6_FIREWALLED:
+            case IPV4_UNKNOWN_IPV6_OK:
+            case IPV4_FIREWALLED_IPV6_OK:
+            case IPV4_DISABLED_IPV6_OK:
                 RouterAddress ra = routerInfo.getTargetAddress("NTCP");
                 if (ra == null)
-                    return _("OK");
+                    return _(status.toStatusString());
                 byte[] ip = ra.getIP();
                 if (ip == null)
                     return _("ERR-Unresolved TCP Address");
                 // TODO set IPv6 arg based on configuration?
                 if (TransportUtil.isPubliclyRoutable(ip, true))
-                    return _("OK");
+                    return _(status.toStatusString());
                 return _("ERR-Private TCP Address");
-            case CommSystemFacade.STATUS_DIFFERENT:
+
+            case DIFFERENT:
                 return _("ERR-SymmetricNAT");
-            case CommSystemFacade.STATUS_REJECT_UNSOLICITED:
+
+            case REJECT_UNSOLICITED:
+            case IPV4_FIREWALLED_IPV6_UNKNOWN:
+            case IPV4_DISABLED_IPV6_FIREWALLED:
                 if (routerInfo.getTargetAddress("NTCP") != null)
                     return _("WARN-Firewalled with Inbound TCP Enabled");
                 if (((FloodfillNetworkDatabaseFacade)_context.netDb()).floodfillEnabled())
                     return _("WARN-Firewalled and Floodfill");
                 //if (_context.router().getRouterInfo().getCapabilities().indexOf('O') >= 0)
                 //    return _("WARN-Firewalled and Fast");
-                return _("Firewalled");
-            case CommSystemFacade.STATUS_DISCONNECTED:
+                return _(status.toStatusString());
+
+            case DISCONNECTED:
                 return _("Disconnected - check network cable");
-            case CommSystemFacade.STATUS_HOSED:
+
+            case HOSED:
                 return _("ERR-UDP Port In Use - Set i2np.udp.internalPort=xxxx in advanced config and restart");
-            case CommSystemFacade.STATUS_UNKNOWN: // fallthrough
+
+            case UNKNOWN:
+            case IPV4_UNKNOWN_IPV6_FIREWALLED:
+            case IPV4_DISABLED_IPV6_UNKNOWN:
             default:
                 ra = routerInfo.getTargetAddress("SSU");
                 if (ra == null && _context.router().getUptime() > 5*60*1000) {
@@ -193,7 +207,7 @@ public class SummaryHelper extends HelperBase {
                     else
                         return _("WARN-Firewalled with UDP Disabled");
                 }
-                return _("Testing");
+                return _(status.toStatusString());
         }
     }
     
diff --git a/router/java/src/net/i2p/router/Router.java b/router/java/src/net/i2p/router/Router.java
index 8b56baba08b4b1373f5db9bd86ccaaf7d1e9e8b4..1462c6ac67a093f1fd0564c268f03209afbec9b8 100644
--- a/router/java/src/net/i2p/router/Router.java
+++ b/router/java/src/net/i2p/router/Router.java
@@ -922,17 +922,26 @@ public class Router implements RouterClock.ClockShiftListener {
         }
         switch (_context.commSystem().getStatus()) {
             case OK:
+            case IPV4_OK_IPV6_UNKNOWN:
+            case IPV4_OK_IPV6_FIREWALLED:
+            case IPV4_FIREWALLED_IPV6_OK:
+            case IPV4_DISABLED_IPV6_OK:
+            case IPV4_UNKNOWN_IPV6_OK:
                 ri.addCapability(CAPABILITY_REACHABLE);
                 break;
 
             case DIFFERENT:
             case REJECT_UNSOLICITED:
+            case IPV4_DISABLED_IPV6_FIREWALLED:
                 ri.addCapability(CAPABILITY_UNREACHABLE);
                 break;
 
             case DISCONNECTED:
             case HOSED:
             case UNKNOWN:
+            case IPV4_UNKNOWN_IPV6_FIREWALLED:
+            case IPV4_DISABLED_IPV6_UNKNOWN:
+            case IPV4_FIREWALLED_IPV6_UNKNOWN:
             default:
                 // no explicit capability
                 break;
diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/ExpireRoutersJob.java b/router/java/src/net/i2p/router/networkdb/kademlia/ExpireRoutersJob.java
index b52df8e1790906561eaf82e48370d39a76b62e56..ac42d7de20506d68f32d894bdb082778a61f3c6c 100644
--- a/router/java/src/net/i2p/router/networkdb/kademlia/ExpireRoutersJob.java
+++ b/router/java/src/net/i2p/router/networkdb/kademlia/ExpireRoutersJob.java
@@ -13,7 +13,7 @@ import java.util.Set;
 import net.i2p.data.DatabaseEntry;
 import net.i2p.data.Hash;
 import net.i2p.data.router.RouterInfo;
-import net.i2p.router.CommSystemFacade;
+import net.i2p.router.CommSystemFacade.Status;
 import net.i2p.router.JobImpl;
 import net.i2p.router.RouterContext;
 import net.i2p.util.Log;
@@ -43,7 +43,7 @@ class ExpireRoutersJob extends JobImpl {
     public String getName() { return "Expire Routers Job"; }
 
     public void runJob() {
-        if (getContext().commSystem().getReachabilityStatus() != CommSystemFacade.STATUS_DISCONNECTED) {
+        if (getContext().commSystem().getStatus() != Status.DISCONNECTED) {
             int removed = expireKeys();
             if (_log.shouldLog(Log.INFO))
                 _log.info("Routers expired: " + removed);
diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/IterativeSearchJob.java b/router/java/src/net/i2p/router/networkdb/kademlia/IterativeSearchJob.java
index a6ea7d2ac762d3de8e4ca3c1b4c9652e657d188c..55464735e2c84ea4cf0668ccd00aba8c9648bb10 100644
--- a/router/java/src/net/i2p/router/networkdb/kademlia/IterativeSearchJob.java
+++ b/router/java/src/net/i2p/router/networkdb/kademlia/IterativeSearchJob.java
@@ -18,7 +18,7 @@ import net.i2p.data.i2np.I2NPMessage;
 import net.i2p.data.router.RouterInfo;
 import net.i2p.kademlia.KBucketSet;
 import net.i2p.kademlia.XORComparator;
-import net.i2p.router.CommSystemFacade;
+import net.i2p.router.CommSystemFacade.Status;
 import net.i2p.router.Job;
 import net.i2p.router.MessageSelector;
 import net.i2p.router.OutNetMessage;
@@ -509,7 +509,7 @@ class IterativeSearchJob extends FloodSearchJob {
             _dead = true;
         }
         _facade.complete(_key);
-        if (getContext().commSystem().getReachabilityStatus() != CommSystemFacade.STATUS_DISCONNECTED)
+        if (getContext().commSystem().getStatus() != Status.DISCONNECTED)
             _facade.lookupFailed(_key);
         getContext().messageRegistry().unregisterPending(_out);
         int tries;
diff --git a/router/java/src/net/i2p/router/tasks/RouterWatchdog.java b/router/java/src/net/i2p/router/tasks/RouterWatchdog.java
index b55ad6497755f531422d7edc897e81e753d6e0f7..cde59db125bbf18ade57588cecfa37b341183399 100644
--- a/router/java/src/net/i2p/router/tasks/RouterWatchdog.java
+++ b/router/java/src/net/i2p/router/tasks/RouterWatchdog.java
@@ -2,7 +2,7 @@ package net.i2p.router.tasks;
 
 import net.i2p.data.DataHelper;
 import net.i2p.router.Job;
-import net.i2p.router.CommSystemFacade;
+import net.i2p.router.CommSystemFacade.Status;
 import net.i2p.router.Router;
 import net.i2p.router.RouterContext;
 import net.i2p.router.util.EventLog;
@@ -133,7 +133,7 @@ public class RouterWatchdog implements Runnable {
         boolean ok = verifyJobQueueLiveliness();
         // If we aren't connected to the network that's why there's nobody to talk to
         long netErrors = 0;
-        if (_context.commSystem().getReachabilityStatus() == CommSystemFacade.STATUS_DISCONNECTED) {
+        if (_context.commSystem().getStatus() == Status.DISCONNECTED) {
             netErrors = 10;
         } else {
             RateStat rs = _context.statManager().getRate("udp.sendException");
diff --git a/router/java/src/net/i2p/router/transport/TransportManager.java b/router/java/src/net/i2p/router/transport/TransportManager.java
index 1afb025619c3e4c8e7384b848651f4db7187425d..ccfe50f654646454557693f7554b8ab8d47691a1 100644
--- a/router/java/src/net/i2p/router/transport/TransportManager.java
+++ b/router/java/src/net/i2p/router/transport/TransportManager.java
@@ -650,6 +650,11 @@ public class TransportManager implements TransportEventListener {
      *  will take many seconds if it has vanished.
      */
     public void renderStatusHTML(Writer out, String urlBase, int sortFlags) throws IOException {
+        out.write("<p>");
+        out.write(_("Status"));
+        out.write(": ");
+        out.write(_(getReachabilityStatus().toStatusString()));
+        out.write("</p>");
         TreeMap<String, Transport> transports = new TreeMap<String, Transport>();
         for (Transport t : _transports.values()) {
             transports.put(t.getStyle(), t);
diff --git a/router/java/src/net/i2p/router/transport/ntcp/EventPumper.java b/router/java/src/net/i2p/router/transport/ntcp/EventPumper.java
index f1a37a54d4a9e33ec3fe8476bd2743f62a99cc86..053d4a3730dd4ebbd72ee55d93536ca1c2cfe864 100644
--- a/router/java/src/net/i2p/router/transport/ntcp/EventPumper.java
+++ b/router/java/src/net/i2p/router/transport/ntcp/EventPumper.java
@@ -2,6 +2,7 @@ package net.i2p.router.transport.ntcp;
 
 import java.io.IOException;
 import java.net.InetAddress;
+import java.net.Inet6Address;
 import java.net.InetSocketAddress;
 import java.nio.ByteBuffer;
 import java.nio.channels.CancelledKeyException;
@@ -24,7 +25,7 @@ import net.i2p.I2PAppContext;
 import net.i2p.data.ByteArray;
 import net.i2p.data.router.RouterAddress;
 import net.i2p.data.router.RouterIdentity;
-import net.i2p.router.CommSystemFacade;
+import net.i2p.router.CommSystemFacade.Status;
 import net.i2p.router.RouterContext;
 import net.i2p.router.transport.FIFOBandwidthLimiter;
 import net.i2p.util.Addresses;
@@ -546,7 +547,7 @@ class EventPumper implements Runnable {
             }
 
             // BUGFIX for firewalls. --Sponge
-            if (_context.commSystem().getReachabilityStatus() != CommSystemFacade.STATUS_OK)
+            if (shouldSetKeepAlive(chan))
                 chan.socket().setKeepAlive(true);
 
             SelectionKey ckey = chan.register(_selector, SelectionKey.OP_READ);
@@ -567,7 +568,7 @@ class EventPumper implements Runnable {
                 _log.debug("processing connect for " + con + ": connected? " + connected);
             if (connected) {
                 // BUGFIX for firewalls. --Sponge
-                if (_context.commSystem().getReachabilityStatus() != CommSystemFacade.STATUS_OK)
+                if (shouldSetKeepAlive(chan))
                     chan.socket().setKeepAlive(true);
                 con.setKey(key);
                 con.outboundConnected();
@@ -590,7 +591,21 @@ class EventPumper implements Runnable {
                 _log.warn("error connecting on " + con, ncpe);
         }
     }
-    
+
+    /**
+     *  @since 0.9.20
+     */
+    private boolean shouldSetKeepAlive(SocketChannel chan) {
+        if (chan.socket().getInetAddress() instanceof Inet6Address)
+            return false;
+        Status status = _context.commSystem().getStatus();
+        if (status == Status.OK ||
+            status == Status.IPV4_OK_IPV6_UNKNOWN ||
+            status == Status.IPV4_OK_IPV6_FIREWALLED)
+            return false;
+        return true;
+    }
+
     /**
      *  OP_READ will always be set before this is called.
      *  This method will disable the interest if no more reads remain because of inbound bandwidth throttling.
diff --git a/router/java/src/net/i2p/router/tunnel/pool/BuildExecutor.java b/router/java/src/net/i2p/router/tunnel/pool/BuildExecutor.java
index 5a55b11b4ab0b16dc6529a06371b81d0c078b429..006745e130d439a624a2e1b7a7d51f066e3447c1 100644
--- a/router/java/src/net/i2p/router/tunnel/pool/BuildExecutor.java
+++ b/router/java/src/net/i2p/router/tunnel/pool/BuildExecutor.java
@@ -11,7 +11,7 @@ import java.util.concurrent.ConcurrentHashMap;
 import net.i2p.data.Hash;
 import net.i2p.data.i2np.I2NPMessage;
 import net.i2p.data.router.RouterInfo;
-import net.i2p.router.CommSystemFacade;
+import net.i2p.router.CommSystemFacade.Status;
 import net.i2p.router.RouterContext;
 import net.i2p.router.TunnelManagerFacade;
 import net.i2p.stat.Rate;
@@ -106,7 +106,7 @@ class BuildExecutor implements Runnable {
     }
 
     private int allowed() {
-        if (_context.commSystem().getReachabilityStatus() == CommSystemFacade.STATUS_DISCONNECTED)
+        if (_context.commSystem().getStatus() == Status.DISCONNECTED)
             return 0;
         int maxKBps = _context.bandwidthLimiter().getOutboundKBytesPerSecond();
         int allowed = maxKBps / 6; // Max. 1 concurrent build per 6 KB/s outbound