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 c52634b8856470be7b00b570184d6a6b2074e708..8465400f5373d9fcd2e21c88e894dbfb5eca25c6 100644
--- a/apps/routerconsole/java/src/net/i2p/router/web/ConfigNetHandler.java
+++ b/apps/routerconsole/java/src/net/i2p/router/web/ConfigNetHandler.java
@@ -50,6 +50,7 @@ public class ConfigNetHandler extends FormHandler {
     private boolean _ratesOnly;
     private boolean _udpDisabled;
     private String _ipv6Mode;
+    private boolean _ipv4Firewalled;
     private final Map<String, String> changes = new HashMap<String, String>();
     private static final String PROP_HIDDEN = Router.PROP_HIDDEN_HIDDEN; // see Router for other choice
     
@@ -79,8 +80,12 @@ public class ConfigNetHandler extends FormHandler {
     public void setNtcpAutoPort(String mode) {
         _ntcpAutoPort = mode.equals("2");
     }
+
     public void setUpnp(String moo) { _upnp = true; }
     public void setLaptop(String moo) { _laptop = true; }
+
+    /** @since 0.9.20 */
+    public void setIPv4Firewalled(String moo) { _ipv4Firewalled = true; }
     
     public void setHostname(String hostname) { 
         _hostname = (hostname != null ? hostname.trim() : null); 
@@ -348,6 +353,16 @@ public class ConfigNetHandler extends FormHandler {
             }
             changes.put(UDPTransport.PROP_LAPTOP_MODE, "" + _laptop);
 
+            if (Boolean.parseBoolean(_context.getProperty(TransportUtil.PROP_IPV4_FIREWALLED)) !=
+                _ipv4Firewalled) {
+                if (_ipv4Firewalled)
+                    addFormNotice(_("Disabling inbound IPv4"));
+                else
+                    addFormNotice(_("Enabling inbound IPv4"));
+                restartRequired = true;
+            }
+            changes.put(TransportUtil.PROP_IPV4_FIREWALLED, "" + _ipv4Firewalled);
+
             if (_context.getBooleanPropertyDefaultTrue(TransportManager.PROP_ENABLE_UDP) !=
                 !_udpDisabled) {
                 if (_udpDisabled)
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 cd2f89eba18e3e7b21c5284dfb272d09f0116a66..3c82277943ccdbe507867a4e587d9b44f2915ada 100644
--- a/apps/routerconsole/java/src/net/i2p/router/web/ConfigNetHelper.java
+++ b/apps/routerconsole/java/src/net/i2p/router/web/ConfigNetHelper.java
@@ -93,6 +93,11 @@ public class ConfigNetHelper extends HelperBase {
         return getChecked(UDPTransport.PROP_LAPTOP_MODE);
     }
 
+    /** @since 0.9.20 */
+    public String getIPv4FirewalledChecked() {
+        return getChecked(TransportUtil.PROP_IPV4_FIREWALLED);
+    }
+
     public String getTcpAutoPortChecked(int mode) {
         String port = _context.getProperty(PROP_I2NP_NTCP_PORT); 
         boolean specified = port != null && port.length() > 0;
diff --git a/apps/routerconsole/jsp/confignet.jsp b/apps/routerconsole/jsp/confignet.jsp
index 6c4b6149c3832eae747b302fc08ceef92dc8f83a..27fdfef7ef34694ab1fc3e64c3eac8e2dc93848d 100644
--- a/apps/routerconsole/jsp/confignet.jsp
+++ b/apps/routerconsole/jsp/confignet.jsp
@@ -51,6 +51,10 @@
     <%=intl._("Laptop mode - Change router identity and UDP port when IP changes for enhanced anonymity")%>
     (<i><%=intl._("Experimental")%></i>)
  </p><p>
+ <%=intl._("IPv4 Configuration")%>:<br>
+    <input type="checkbox" class="optbox" name="IPv4Firewalled" value="true" <jsp:getProperty name="nethelper" property="IPv4FirewalledChecked" /> >
+    <%=intl._("Disable inbound (Firewalled by Carrier-grade NAT or DS-Lite)")%>
+ </p><p>
  <%=intl._("IPv6 Configuration")%>:<br>
     <input type="radio" class="optbox" name="ipv6" value="false" <%=nethelper.getIPv6Checked("false") %> >
     <%=intl._("Disable IPv6")%><br>
diff --git a/router/java/src/net/i2p/router/transport/TransportImpl.java b/router/java/src/net/i2p/router/transport/TransportImpl.java
index b5f0870aadefab5d75e4e1b0580780c33ff5b582..68b56bf33604ef706ee2c13d2dea0cbfb4608841 100644
--- a/router/java/src/net/i2p/router/transport/TransportImpl.java
+++ b/router/java/src/net/i2p/router/transport/TransportImpl.java
@@ -813,6 +813,14 @@ public abstract class TransportImpl implements Transport {
      */
     public void recheckReachability() {}
 
+    /**
+     *  @param transportStyle ignored
+     *  @since 0.9.20
+     */
+    protected boolean isIPv4Firewalled() {
+        return TransportUtil.isIPv4Firewalled(_context, getStyle());
+    }
+
     public boolean isBacklogged(Hash dest) { return false; }
     public boolean isEstablished(Hash dest) { return false; }
 
diff --git a/router/java/src/net/i2p/router/transport/TransportManager.java b/router/java/src/net/i2p/router/transport/TransportManager.java
index eb44660133b224e0deb14b07eb7f2e23143190a0..e5240d6a2d1b0d06c3bee482aefa16c46314d596 100644
--- a/router/java/src/net/i2p/router/transport/TransportManager.java
+++ b/router/java/src/net/i2p/router/transport/TransportManager.java
@@ -60,6 +60,8 @@ public class TransportManager implements TransportEventListener {
     public final static String PROP_ENABLE_NTCP = "i2np.ntcp.enable";
     /** default true */
     public final static String PROP_ENABLE_UPNP = "i2np.upnp.enable";
+
+    private static final String PROP_ADVANCED = "routerconsole.advanced";
     
     /** not forever, since they may update */
     private static final long SIGTYPE_BANLIST_DURATION = 36*60*60*1000L;
@@ -667,11 +669,13 @@ 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>");
+        if (_context.getBooleanProperty(PROP_ADVANCED)) {
+            out.write("<p><b>");
+            out.write(_("Status"));
+            out.write(": ");
+            out.write(_(getReachabilityStatus().toStatusString()));
+            out.write("</b></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/TransportUtil.java b/router/java/src/net/i2p/router/transport/TransportUtil.java
index 650a9656f35b1764ce1d1e3f24ae6ae3af9a63d4..3ca7e8a2e1a6789909683a281c3a16a8cc83cbaa 100644
--- a/router/java/src/net/i2p/router/transport/TransportUtil.java
+++ b/router/java/src/net/i2p/router/transport/TransportUtil.java
@@ -23,6 +23,7 @@ public abstract class TransportUtil {
 
     public static final String NTCP_IPV6_CONFIG = "i2np.ntcp.ipv6";
     public static final String SSU_IPV6_CONFIG = "i2np.udp.ipv6";
+    public static final String PROP_IPV4_FIREWALLED = "i2np.ipv4.firewalled";
 
     public enum IPv6Config {
         /** IPv6 disabled */
@@ -83,6 +84,14 @@ public abstract class TransportUtil {
         return DEFAULT_IPV6_CONFIG;
     }
 
+    /**
+     *  @param transportStyle ignored
+     *  @since 0.9.20
+     */
+    public static boolean isIPv4Firewalled(RouterContext ctx, String transportStyle) {
+        return ctx.getBooleanProperty(PROP_IPV4_FIREWALLED);
+    }
+
     /**
      *  Addresses without a host (i.e. w/introducers)
      *  are assumed to be IPv4
diff --git a/router/java/src/net/i2p/router/transport/ntcp/NTCPTransport.java b/router/java/src/net/i2p/router/transport/ntcp/NTCPTransport.java
index ca6259e456578529bd88d01a526b3196530842e0..382d4d89e45b84ccd8a6ccc7b83218bb8dc4c374 100644
--- a/router/java/src/net/i2p/router/transport/ntcp/NTCPTransport.java
+++ b/router/java/src/net/i2p/router/transport/ntcp/NTCPTransport.java
@@ -1344,7 +1344,9 @@ public class NTCPTransport extends TransportImpl {
         buf.append("<h3 id=\"ntcpcon\">").append(_("NTCP connections")).append(": ").append(peers.size());
         buf.append(". ").append(_("Limit")).append(": ").append(getMaxConnections());
         buf.append(". ").append(_("Timeout")).append(": ").append(DataHelper.formatDuration2(_pumper.getIdleTimeout()));
-        buf.append(". ").append(_("Status")).append(": ").append(_(getReachabilityStatus().toStatusString()));
+        if (_context.getBooleanProperty(PROP_ADVANCED)) {
+            buf.append(". ").append(_("Status")).append(": ").append(_(getReachabilityStatus().toStatusString()));
+        }
         buf.append(".</h3>\n" +
                    "<table>\n" +
                    "<tr><th><a href=\"#def.peer\">").append(_("Peer")).append("</a></th>" +
diff --git a/router/java/src/net/i2p/router/transport/udp/UDPTransport.java b/router/java/src/net/i2p/router/transport/udp/UDPTransport.java
index 35f2accce1c8c773c067fed548d4c37781e2c7f4..a502eb327b785d9cb74c207d6b4fc7b123dc3dfe 100644
--- a/router/java/src/net/i2p/router/transport/udp/UDPTransport.java
+++ b/router/java/src/net/i2p/router/transport/udp/UDPTransport.java
@@ -167,6 +167,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
     public static final String PROP_BIND_INTERFACE = "i2np.udp.bindInterface";
     /** override the "large" (max) MTU, default is PeerState.LARGE_MTU */
     private static final String PROP_DEFAULT_MTU = "i2np.udp.mtu";
+    private static final String PROP_ADVANCED = "routerconsole.advanced";
         
     private static final String CAP_TESTING = "" + UDPAddress.CAPACITY_TESTING;
     private static final String CAP_TESTING_INTRO = "" + UDPAddress.CAPACITY_TESTING + UDPAddress.CAPACITY_INTRODUCER;
@@ -457,6 +458,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
         //if (SHOULD_FLOOD_PEERS)
         //    _flooder.startup();
         _expireEvent.setIsAlive(true);
+        _reachabilityStatus = Status.UNKNOWN;
         _testEvent.setIsAlive(true); // this queues it for 3-6 minutes in the future...
         _testEvent.reschedule(10*1000); // lets requeue it for Real Soon
 
@@ -477,7 +479,8 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
                     _lastInboundIPv6 = _context.clock().now();
                     setReachabilityStatus(Status.IPV4_UNKNOWN_IPV6_OK);
                 } else {
-                    setReachabilityStatus(Status.IPV4_OK_IPV6_UNKNOWN);
+                    if (!isIPv4Firewalled())
+                        setReachabilityStatus(Status.IPV4_OK_IPV6_UNKNOWN);
                 }
                 rebuildExternalAddress(ia.getHostAddress(), newPort, false);
             }
@@ -487,11 +490,18 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
                     _lastInboundIPv6 = _context.clock().now();
                     setReachabilityStatus(Status.IPV4_UNKNOWN_IPV6_OK);
                 } else {
-                    setReachabilityStatus(Status.IPV4_OK_IPV6_UNKNOWN);
+                    if (!isIPv4Firewalled())
+                        setReachabilityStatus(Status.IPV4_OK_IPV6_UNKNOWN);
                 }
                 rebuildExternalAddress(ia.getHostAddress(), newPort, false);
             }
         }
+        if (isIPv4Firewalled()) {
+            if (_lastInboundIPv6 > 0)
+                setReachabilityStatus(Status.IPV4_FIREWALLED_IPV6_UNKNOWN);
+            else
+                setReachabilityStatus(Status.REJECT_UNSOLICITED);
+        }
         rebuildExternalAddress(false);
     }
     
@@ -763,12 +773,14 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
         // Assume if we have an interface with a public IP that we aren't firewalled.
         // If this is wrong, the peer test will figure it out and change the status.
         if (changed && source == SOURCE_INTERFACE) {
-            if (ip.length == 4)
-                setReachabilityStatus(Status.IPV4_OK_IPV6_UNKNOWN);
-            else if (ip.length == 16)
+            if (ip.length == 4) {
+                if (!isIPv4Firewalled())
+                    setReachabilityStatus(Status.IPV4_OK_IPV6_UNKNOWN);
+            } else if (ip.length == 16) {
                 // TODO should we set both to unknown and wait for an inbound v6 conn,
                 // since there's no v6 testing?
                 setReachabilityStatus(Status.IPV4_UNKNOWN_IPV6_OK);
+            }
         }
     }
 
@@ -786,8 +798,10 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
             else
                 _log.warn("UPnP has failed to open the SSU port: " + port + " reason: " + reason);
         }
-        if (success && ip != null && getExternalIP() != null)
-            setReachabilityStatus(Status.IPV4_OK_IPV6_UNKNOWN);
+        if (success && ip != null && getExternalIP() != null) {
+            if (!isIPv4Firewalled())
+                setReachabilityStatus(Status.IPV4_OK_IPV6_UNKNOWN);
+        }
     }
 
     /**
@@ -2434,7 +2448,9 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
         buf.append("<h3 id=\"udpcon\">").append(_("UDP connections")).append(": ").append(peers.size());
         buf.append(". ").append(_("Limit")).append(": ").append(getMaxConnections());
         buf.append(". ").append(_("Timeout")).append(": ").append(DataHelper.formatDuration2(_expireTimeout));
-        buf.append(". ").append(_("Status")).append(": ").append(_(_reachabilityStatus.toStatusString()));
+        if (_context.getBooleanProperty(PROP_ADVANCED)) {
+            buf.append(". ").append(_("Status")).append(": ").append(_(_reachabilityStatus.toStatusString()));
+        }
         buf.append(".</h3>\n");
         buf.append("<table>\n");
         buf.append("<tr><th class=\"smallhead\" nowrap><a href=\"#def.peer\">").append(_("Peer")).append("</a><br>");
@@ -3003,7 +3019,8 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
     }
     
     private boolean shouldTest() {
-        return ! _context.router().isHidden();
+        return ! (_context.router().isHidden() ||
+                  isIPv4Firewalled());
         //String val = _context.getProperty(PROP_SHOULD_TEST);
         //return ( (val != null) && ("true".equals(val)) );
     }
@@ -3053,6 +3070,8 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
          *  @since 0.9.13
          */
         public synchronized void forceRunSoon() {
+            if (isIPv4Firewalled())
+                return;
             _forceRun = true;
             reschedule(MIN_TEST_FREQUENCY);
         }
@@ -3063,6 +3082,8 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
          *  @since 0.9.13
          */
         public synchronized void forceRunImmediately() {
+            if (isIPv4Firewalled())
+                return;
             _lastTested.set(0);
             _forceRun = true;
             reschedule(5*1000);