diff --git a/router/java/src/net/i2p/router/transport/TransportImpl.java b/router/java/src/net/i2p/router/transport/TransportImpl.java index d243de198c68e3d5e9c98e0e4d93a33baa48fb40..37fc5fe49eb028196b8fea612b9e04ea0dc5341f 100644 --- a/router/java/src/net/i2p/router/transport/TransportImpl.java +++ b/router/java/src/net/i2p/router/transport/TransportImpl.java @@ -68,6 +68,9 @@ public abstract class TransportImpl implements Transport { private static final long UNREACHABLE_PERIOD = 5*60*1000; private static final long WAS_UNREACHABLE_PERIOD = 30*60*1000; + /** @since 0.9.44 */ + protected static final String PROP_IPV6_FIREWALLED = "i2np.lastIPv6Firewalled"; + static { long maxMemory = SystemVersion.getMaxMemory(); long min = 512; 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 b85b2d7042e379aade50e760d01e33e44e87a40d..a4e9497adefe2890d851f2c99b9eb3790d941be5 100644 --- a/router/java/src/net/i2p/router/transport/ntcp/NTCPTransport.java +++ b/router/java/src/net/i2p/router/transport/ntcp/NTCPTransport.java @@ -872,9 +872,12 @@ public class NTCPTransport extends TransportImpl { props.setProperty(RouterAddress.PROP_HOST, ia.getHostAddress()); props.setProperty(RouterAddress.PROP_PORT, Integer.toString(port)); addNTCP2Options(props); - int cost = getDefaultCost(ia instanceof Inet6Address); - myAddress = new RouterAddress(getPublishStyle(), props, cost); - replaceAddress(myAddress); + boolean ipv6 = ia instanceof Inet6Address; + if (!ipv6 || !_context.getBooleanProperty(PROP_IPV6_FIREWALLED)) { + int cost = getDefaultCost(ipv6); + myAddress = new RouterAddress(getPublishStyle(), props, cost); + replaceAddress(myAddress); + } } } else if (_enableNTCP2) { setOutboundNTCP2Address(); 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 6b18cdbfd8854b009a9c1421ed9ab048bd8d260a..d9bc105e386d122fa8123fc5bc9a47e85328236a 100644 --- a/router/java/src/net/i2p/router/transport/udp/UDPTransport.java +++ b/router/java/src/net/i2p/router/transport/udp/UDPTransport.java @@ -9,6 +9,7 @@ import java.net.UnknownHostException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.EnumSet; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; @@ -169,7 +170,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority public static final String PROP_IP_CHANGE = "i2np.lastIPChange"; public static final String PROP_LAPTOP_MODE = "i2np.laptopMode"; /** @since 0.9.43 */ - public static final String PROP_IPV6= "i2np.lastIPv6"; + public static final String PROP_IPV6 = "i2np.lastIPv6"; /** do we require introducers, regardless of our status? */ public static final String PROP_FORCE_INTRODUCERS = "i2np.udp.forceIntroducers"; @@ -235,6 +236,42 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority */ private static final String MIN_V6_PEER_TEST_VERSION = "0.9.27"; + // various state bitmaps + + private static final Set<Status> STATUS_IPV4_FW = EnumSet.of(Status.DIFFERENT, + Status.REJECT_UNSOLICITED, + Status.IPV4_FIREWALLED_IPV6_OK, + Status.IPV4_SNAT_IPV6_OK, + Status.IPV4_OK_IPV6_FIREWALLED); + + private static final Set<Status> STATUS_IPV6_FW = EnumSet.of(Status.IPV4_OK_IPV6_FIREWALLED, + Status.IPV4_UNKNOWN_IPV6_FIREWALLED, + Status.IPV4_DISABLED_IPV6_FIREWALLED); + + private static final Set<Status> STATUS_IPV6_FW_2 = EnumSet.of(Status.IPV4_OK_IPV6_FIREWALLED, + Status.IPV4_UNKNOWN_IPV6_FIREWALLED, + Status.IPV4_DISABLED_IPV6_FIREWALLED, + Status.DIFFERENT, + Status.REJECT_UNSOLICITED); + + private static final Set<Status> STATUS_IPV6_OK = EnumSet.of(Status.OK, + Status.IPV4_UNKNOWN_IPV6_OK, + Status.IPV4_FIREWALLED_IPV6_OK, + Status.IPV4_DISABLED_IPV6_OK, + Status.IPV4_SNAT_IPV6_OK); + + private static final Set<Status> STATUS_NO_RETEST = EnumSet.of(Status.OK, + Status.IPV4_OK_IPV6_UNKNOWN, + Status.IPV4_OK_IPV6_FIREWALLED, + Status.IPV4_DISABLED_IPV6_OK, + Status.IPV4_DISABLED_IPV6_UNKNOWN, + Status.IPV4_DISABLED_IPV6_FIREWALLED, + Status.DISCONNECTED); + + private static final Set<Status> STATUS_NEED_INTRO = EnumSet.of(Status.REJECT_UNSOLICITED, + Status.IPV4_FIREWALLED_IPV6_OK, + Status.IPV4_FIREWALLED_IPV6_UNKNOWN); + public UDPTransport(RouterContext ctx, DHSessionKeyBuilder.Factory dh) { super(ctx); @@ -555,17 +592,18 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority if (hasv6) continue; hasv6 = true; - // FIXME we need to check and time out after an hour of no inbound ipv6, - // change to firewalled maybe? but we don't have any test to restore - // a v6 address after it's removed. - _lastInboundIPv6 = _context.clock().now(); - if (!isIPv6Firewalled()) + if (isIPv6Firewalled() || _context.getBooleanProperty(PROP_IPV6_FIREWALLED)) { + setReachabilityStatus(Status.IPV4_UNKNOWN_IPV6_FIREWALLED, true); + } else { + _lastInboundIPv6 = _context.clock().now(); setReachabilityStatus(Status.IPV4_UNKNOWN_IPV6_OK, true); + rebuildExternalAddress(ia.getHostAddress(), newPort, false); + } } else { if (!isIPv4Firewalled()) setReachabilityStatus(Status.IPV4_OK_IPV6_UNKNOWN); + rebuildExternalAddress(ia.getHostAddress(), newPort, false); } - rebuildExternalAddress(ia.getHostAddress(), newPort, false); } } else if (newPort > 0 && !bindToAddrs.isEmpty()) { for (InetAddress ia : bindToAddrs) { @@ -595,6 +633,12 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority } public synchronized void shutdown() { + if (_haveIPv6Address) { + boolean fwOld = _context.getBooleanProperty(PROP_IPV6_FIREWALLED); + boolean fwNew = STATUS_IPV6_FW.contains(_reachabilityStatus); + if (fwOld != fwNew) + _context.router().saveConfig(PROP_IPV6_FIREWALLED, Boolean.toString(fwNew)); + } destroyAll(); for (UDPEndpoint endpoint : _endpoints) { endpoint.shutdown(); @@ -1051,11 +1095,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority _log.info("New IPv6 address received but not one of our local addresses: " + ipstr, new Exception()); return false; } - if (_reachabilityStatus == Status.IPV4_OK_IPV6_FIREWALLED || - _reachabilityStatus == Status.IPV4_UNKNOWN_IPV6_FIREWALLED || - _reachabilityStatus == Status.IPV4_DISABLED_IPV6_FIREWALLED || - _reachabilityStatus == Status.DIFFERENT || - _reachabilityStatus == Status.REJECT_UNSOLICITED) { + if (STATUS_IPV6_FW_2.contains(_reachabilityStatus)) { // If we were firewalled before, let's assume we're still firewalled. // Save the new IP and fire a test String oldIP = _context.getProperty(PROP_IPV6); @@ -1217,9 +1257,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority if (prop != null) return Boolean.parseBoolean(prop); Status status = getReachabilityStatus(); - return status != Status.REJECT_UNSOLICITED && - status != Status.IPV4_FIREWALLED_IPV6_OK && - status != Status.IPV4_FIREWALLED_IPV6_UNKNOWN; + return !STATUS_NEED_INTRO.contains(status); } /** @@ -1450,13 +1488,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority synchronized(_rebuildLock) { rebuildIfNecessary(); Status status = getReachabilityStatus(); - if (status != Status.OK && - status != Status.IPV4_OK_IPV6_UNKNOWN && - status != Status.IPV4_OK_IPV6_FIREWALLED && - status != Status.IPV4_DISABLED_IPV6_OK && - status != Status.IPV4_DISABLED_IPV6_UNKNOWN && - status != Status.IPV4_DISABLED_IPV6_FIREWALLED && - status != Status.DISCONNECTED && + if (!STATUS_NO_RETEST.contains(status) && _reachabilityStatusUnchanged < 7) { _testEvent.forceRunSoon(peer.isIPv6()); } @@ -3063,16 +3095,8 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority if (status != old) { // for the following transitions ONLY, require two in a row // to prevent thrashing - if ((old == Status.OK && (status == Status.DIFFERENT || - status == Status.REJECT_UNSOLICITED || - status == Status.IPV4_FIREWALLED_IPV6_OK || - status == Status.IPV4_SNAT_IPV6_OK || - status == Status.IPV4_OK_IPV6_FIREWALLED)) || - (status == Status.OK && (old == Status.DIFFERENT || - old == Status.REJECT_UNSOLICITED || - old == Status.IPV4_FIREWALLED_IPV6_OK || - old == Status.IPV4_SNAT_IPV6_OK || - old == Status.IPV4_OK_IPV6_FIREWALLED))) { + if ((old == Status.OK && STATUS_IPV4_FW.contains(status)) || + (status == Status.OK && STATUS_IPV4_FW.contains(old))) { if (status != _reachabilityStatusPending) { if (_log.shouldLog(Log.WARN)) _log.warn("Old status: " + old + " status pending confirmation: " + status + @@ -3103,18 +3127,10 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority // as rebuildExternalAddress() calls replaceAddress() which calls CSFI.notifyReplaceAddress() // which will start up NTCP inbound when we transition to OK. if (isIPv6) { - if (status == Status.IPV4_OK_IPV6_FIREWALLED || - status == Status.IPV4_UNKNOWN_IPV6_FIREWALLED || - status == Status.IPV4_DISABLED_IPV6_FIREWALLED) { + if (STATUS_IPV6_FW.contains(status)) { removeExternalAddress(true, true); - } else if ((old == Status.IPV4_OK_IPV6_FIREWALLED || - old == Status.IPV4_UNKNOWN_IPV6_FIREWALLED || - old == Status.IPV4_DISABLED_IPV6_FIREWALLED) && - (status == Status.OK || - status == Status.IPV4_UNKNOWN_IPV6_OK || - status == Status.IPV4_FIREWALLED_IPV6_OK || - status == Status.IPV4_DISABLED_IPV6_OK || - status == Status.IPV4_SNAT_IPV6_OK) && + } else if (STATUS_IPV6_FW.contains(old) && + STATUS_IPV6_OK.contains(status) && _lastOurIPv6 != null && !explicitAddressSpecified()){ String addr = Addresses.toString(_lastOurIPv6);