From abd8ca34dc85abbcc8b344526b78b429f533578f Mon Sep 17 00:00:00 2001
From: zzz <zzz@mail.i2p>
Date: Wed, 6 May 2015 01:45:33 +0000
Subject: [PATCH] Transport: Add config to force IPv4 (only) to firewalled
 (ticket #1541) since we cannot reliably detect DS-lite (ticket #1458) Hide
 transport status on /peers unless routerconsole.advanced

---
 .../net/i2p/router/web/ConfigNetHandler.java  | 15 +++++++
 .../net/i2p/router/web/ConfigNetHelper.java   |  5 +++
 apps/routerconsole/jsp/confignet.jsp          |  4 ++
 .../i2p/router/transport/TransportImpl.java   |  8 ++++
 .../router/transport/TransportManager.java    | 14 ++++---
 .../i2p/router/transport/TransportUtil.java   |  9 +++++
 .../router/transport/ntcp/NTCPTransport.java  |  4 +-
 .../router/transport/udp/UDPTransport.java    | 39 ++++++++++++++-----
 8 files changed, 83 insertions(+), 15 deletions(-)

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 c52634b885..8465400f53 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 cd2f89eba1..3c82277943 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 6c4b6149c3..27fdfef7ef 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 b5f0870aad..68b56bf336 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 eb44660133..e5240d6a2d 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 650a9656f3..3ca7e8a2e1 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 ca6259e456..382d4d89e4 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 35f2accce1..a502eb327b 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);
-- 
GitLab