Transport: Save IPv6 firewalled state across restarts (ticket #2175)

Use EnumSets in UDPTransport
This commit is contained in:
zzz
2019-11-16 17:12:40 +00:00
parent 79334afcbc
commit 03f4624f91
3 changed files with 68 additions and 46 deletions

View File

@ -68,6 +68,9 @@ public abstract class TransportImpl implements Transport {
private static final long UNREACHABLE_PERIOD = 5*60*1000; private static final long UNREACHABLE_PERIOD = 5*60*1000;
private static final long WAS_UNREACHABLE_PERIOD = 30*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 { static {
long maxMemory = SystemVersion.getMaxMemory(); long maxMemory = SystemVersion.getMaxMemory();
long min = 512; long min = 512;

View File

@ -872,9 +872,12 @@ public class NTCPTransport extends TransportImpl {
props.setProperty(RouterAddress.PROP_HOST, ia.getHostAddress()); props.setProperty(RouterAddress.PROP_HOST, ia.getHostAddress());
props.setProperty(RouterAddress.PROP_PORT, Integer.toString(port)); props.setProperty(RouterAddress.PROP_PORT, Integer.toString(port));
addNTCP2Options(props); addNTCP2Options(props);
int cost = getDefaultCost(ia instanceof Inet6Address); boolean ipv6 = ia instanceof Inet6Address;
myAddress = new RouterAddress(getPublishStyle(), props, cost); if (!ipv6 || !_context.getBooleanProperty(PROP_IPV6_FIREWALLED)) {
replaceAddress(myAddress); int cost = getDefaultCost(ipv6);
myAddress = new RouterAddress(getPublishStyle(), props, cost);
replaceAddress(myAddress);
}
} }
} else if (_enableNTCP2) { } else if (_enableNTCP2) {
setOutboundNTCP2Address(); setOutboundNTCP2Address();

View File

@ -9,6 +9,7 @@ import java.net.UnknownHostException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.EnumSet;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.Iterator; 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_IP_CHANGE = "i2np.lastIPChange";
public static final String PROP_LAPTOP_MODE = "i2np.laptopMode"; public static final String PROP_LAPTOP_MODE = "i2np.laptopMode";
/** @since 0.9.43 */ /** @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? */ /** do we require introducers, regardless of our status? */
public static final String PROP_FORCE_INTRODUCERS = "i2np.udp.forceIntroducers"; 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"; 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) { public UDPTransport(RouterContext ctx, DHSessionKeyBuilder.Factory dh) {
super(ctx); super(ctx);
@ -555,17 +592,18 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
if (hasv6) if (hasv6)
continue; continue;
hasv6 = true; hasv6 = true;
// FIXME we need to check and time out after an hour of no inbound ipv6, if (isIPv6Firewalled() || _context.getBooleanProperty(PROP_IPV6_FIREWALLED)) {
// change to firewalled maybe? but we don't have any test to restore setReachabilityStatus(Status.IPV4_UNKNOWN_IPV6_FIREWALLED, true);
// a v6 address after it's removed. } else {
_lastInboundIPv6 = _context.clock().now(); _lastInboundIPv6 = _context.clock().now();
if (!isIPv6Firewalled())
setReachabilityStatus(Status.IPV4_UNKNOWN_IPV6_OK, true); setReachabilityStatus(Status.IPV4_UNKNOWN_IPV6_OK, true);
rebuildExternalAddress(ia.getHostAddress(), newPort, false);
}
} else { } else {
if (!isIPv4Firewalled()) if (!isIPv4Firewalled())
setReachabilityStatus(Status.IPV4_OK_IPV6_UNKNOWN); setReachabilityStatus(Status.IPV4_OK_IPV6_UNKNOWN);
rebuildExternalAddress(ia.getHostAddress(), newPort, false);
} }
rebuildExternalAddress(ia.getHostAddress(), newPort, false);
} }
} else if (newPort > 0 && !bindToAddrs.isEmpty()) { } else if (newPort > 0 && !bindToAddrs.isEmpty()) {
for (InetAddress ia : bindToAddrs) { for (InetAddress ia : bindToAddrs) {
@ -595,6 +633,12 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
} }
public synchronized void shutdown() { 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(); destroyAll();
for (UDPEndpoint endpoint : _endpoints) { for (UDPEndpoint endpoint : _endpoints) {
endpoint.shutdown(); 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()); _log.info("New IPv6 address received but not one of our local addresses: " + ipstr, new Exception());
return false; return false;
} }
if (_reachabilityStatus == Status.IPV4_OK_IPV6_FIREWALLED || if (STATUS_IPV6_FW_2.contains(_reachabilityStatus)) {
_reachabilityStatus == Status.IPV4_UNKNOWN_IPV6_FIREWALLED ||
_reachabilityStatus == Status.IPV4_DISABLED_IPV6_FIREWALLED ||
_reachabilityStatus == Status.DIFFERENT ||
_reachabilityStatus == Status.REJECT_UNSOLICITED) {
// If we were firewalled before, let's assume we're still firewalled. // If we were firewalled before, let's assume we're still firewalled.
// Save the new IP and fire a test // Save the new IP and fire a test
String oldIP = _context.getProperty(PROP_IPV6); String oldIP = _context.getProperty(PROP_IPV6);
@ -1217,9 +1257,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
if (prop != null) if (prop != null)
return Boolean.parseBoolean(prop); return Boolean.parseBoolean(prop);
Status status = getReachabilityStatus(); Status status = getReachabilityStatus();
return status != Status.REJECT_UNSOLICITED && return !STATUS_NEED_INTRO.contains(status);
status != Status.IPV4_FIREWALLED_IPV6_OK &&
status != Status.IPV4_FIREWALLED_IPV6_UNKNOWN;
} }
/** /**
@ -1450,13 +1488,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
synchronized(_rebuildLock) { synchronized(_rebuildLock) {
rebuildIfNecessary(); rebuildIfNecessary();
Status status = getReachabilityStatus(); Status status = getReachabilityStatus();
if (status != Status.OK && if (!STATUS_NO_RETEST.contains(status) &&
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 &&
_reachabilityStatusUnchanged < 7) { _reachabilityStatusUnchanged < 7) {
_testEvent.forceRunSoon(peer.isIPv6()); _testEvent.forceRunSoon(peer.isIPv6());
} }
@ -3063,16 +3095,8 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
if (status != old) { if (status != old) {
// for the following transitions ONLY, require two in a row // for the following transitions ONLY, require two in a row
// to prevent thrashing // to prevent thrashing
if ((old == Status.OK && (status == Status.DIFFERENT || if ((old == Status.OK && STATUS_IPV4_FW.contains(status)) ||
status == Status.REJECT_UNSOLICITED || (status == Status.OK && STATUS_IPV4_FW.contains(old))) {
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 (status != _reachabilityStatusPending) { if (status != _reachabilityStatusPending) {
if (_log.shouldLog(Log.WARN)) if (_log.shouldLog(Log.WARN))
_log.warn("Old status: " + old + " status pending confirmation: " + status + _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() // as rebuildExternalAddress() calls replaceAddress() which calls CSFI.notifyReplaceAddress()
// which will start up NTCP inbound when we transition to OK. // which will start up NTCP inbound when we transition to OK.
if (isIPv6) { if (isIPv6) {
if (status == Status.IPV4_OK_IPV6_FIREWALLED || if (STATUS_IPV6_FW.contains(status)) {
status == Status.IPV4_UNKNOWN_IPV6_FIREWALLED ||
status == Status.IPV4_DISABLED_IPV6_FIREWALLED) {
removeExternalAddress(true, true); removeExternalAddress(true, true);
} else if ((old == Status.IPV4_OK_IPV6_FIREWALLED || } else if (STATUS_IPV6_FW.contains(old) &&
old == Status.IPV4_UNKNOWN_IPV6_FIREWALLED || STATUS_IPV6_OK.contains(status) &&
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) &&
_lastOurIPv6 != null && _lastOurIPv6 != null &&
!explicitAddressSpecified()){ !explicitAddressSpecified()){
String addr = Addresses.toString(_lastOurIPv6); String addr = Addresses.toString(_lastOurIPv6);