forked from I2P_Developers/i2p.i2p
SSU: NAT fixes part 3
Eliminate Symmetric NAT errors for "full cone" NATs with different external port Separate isPortFixed() states for IPv4 and IPv6 Allow port changes when state is unknown Require two tests to transition from unknown IPv4 state Save external port config on change Don't reset external port config at startup Add port change event to event log
This commit is contained in:
@@ -282,6 +282,20 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
|
|||||||
|
|
||||||
// various state bitmaps
|
// various state bitmaps
|
||||||
|
|
||||||
|
private static final Set<Status> STATUS_IPV4_UNK = EnumSet.of(Status.UNKNOWN,
|
||||||
|
Status.DISCONNECTED,
|
||||||
|
Status.HOSED,
|
||||||
|
Status.IPV4_UNKNOWN_IPV6_OK,
|
||||||
|
Status.IPV4_UNKNOWN_IPV6_FIREWALLED);
|
||||||
|
|
||||||
|
private static final Set<Status> STATUS_IPV6_UNK = EnumSet.of(Status.UNKNOWN,
|
||||||
|
Status.DISCONNECTED,
|
||||||
|
Status.HOSED,
|
||||||
|
Status.IPV4_OK_IPV6_UNKNOWN,
|
||||||
|
Status.IPV4_FIREWALLED_IPV6_UNKNOWN,
|
||||||
|
Status.IPV4_SNAT_IPV6_UNKNOWN,
|
||||||
|
Status.IPV4_DISABLED_IPV6_UNKNOWN);
|
||||||
|
|
||||||
private static final Set<Status> STATUS_IPV4_FW = EnumSet.of(Status.DIFFERENT,
|
private static final Set<Status> STATUS_IPV4_FW = EnumSet.of(Status.DIFFERENT,
|
||||||
Status.REJECT_UNSOLICITED,
|
Status.REJECT_UNSOLICITED,
|
||||||
Status.IPV4_FIREWALLED_IPV6_OK,
|
Status.IPV4_FIREWALLED_IPV6_OK,
|
||||||
@@ -323,10 +337,6 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
|
|||||||
Status.IPV4_DISABLED_IPV6_FIREWALLED,
|
Status.IPV4_DISABLED_IPV6_FIREWALLED,
|
||||||
Status.DISCONNECTED);
|
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);
|
|
||||||
|
|
||||||
private static final Set<Status> STATUS_OK = EnumSet.of(Status.OK,
|
private static final Set<Status> STATUS_OK = EnumSet.of(Status.OK,
|
||||||
Status.IPV4_DISABLED_IPV6_OK);
|
Status.IPV4_DISABLED_IPV6_OK);
|
||||||
|
|
||||||
@@ -541,8 +551,9 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
|
|||||||
if (port <= 0) {
|
if (port <= 0) {
|
||||||
port = TransportUtil.selectRandomPort(_context, STYLE);
|
port = TransportUtil.selectRandomPort(_context, STYLE);
|
||||||
Map<String, String> changes = new HashMap<String, String>(2);
|
Map<String, String> changes = new HashMap<String, String>(2);
|
||||||
changes.put(PROP_INTERNAL_PORT, Integer.toString(port));
|
String sport = Integer.toString(port);
|
||||||
changes.put(PROP_EXTERNAL_PORT, Integer.toString(port));
|
changes.put(PROP_INTERNAL_PORT, sport);
|
||||||
|
changes.put(PROP_EXTERNAL_PORT, sport);
|
||||||
_context.router().saveConfig(changes, null);
|
_context.router().saveConfig(changes, null);
|
||||||
_log.logAlways(Log.INFO, "UDP selected random port " + port);
|
_log.logAlways(Log.INFO, "UDP selected random port " + port);
|
||||||
}
|
}
|
||||||
@@ -758,12 +769,14 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (newPort > 0 &&
|
if (newPort > 0 &&
|
||||||
(newPort != port || newPort != oldIPort || newPort != oldEPort)) {
|
(newPort != port || newPort != oldIPort)) {
|
||||||
// attempt to use it as our external port - this will be overridden by
|
// attempt to use it as our external port - this will be overridden by
|
||||||
// externalAddressReceived(...)
|
// externalAddressReceived(...)
|
||||||
Map<String, String> changes = new HashMap<String, String>();
|
Map<String, String> changes = new HashMap<String, String>();
|
||||||
changes.put(PROP_INTERNAL_PORT, Integer.toString(newPort));
|
String sport = Integer.toString(newPort);
|
||||||
changes.put(PROP_EXTERNAL_PORT, Integer.toString(newPort));
|
changes.put(PROP_INTERNAL_PORT, sport);
|
||||||
|
if (oldEPort <= 0)
|
||||||
|
changes.put(PROP_EXTERNAL_PORT, sport);
|
||||||
_context.router().saveConfig(changes, null);
|
_context.router().saveConfig(changes, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1426,7 +1439,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
|
|||||||
if (!isIPv6) {
|
if (!isIPv6) {
|
||||||
if (from.equals(_lastFromv4) || !eq(_lastOurIPv4, _lastOurPortv4, ourIP, ourPort)) {
|
if (from.equals(_lastFromv4) || !eq(_lastOurIPv4, _lastOurPortv4, ourIP, ourPort)) {
|
||||||
if (_log.shouldLog(Log.INFO))
|
if (_log.shouldLog(Log.INFO))
|
||||||
_log.info("The router " + from + " told us we have a new IP - "
|
_log.info("The router " + from + " told us we have a new IP/port - "
|
||||||
+ Addresses.toString(ourIP, ourPort) + ". Wait until somebody else tells us the same thing.");
|
+ Addresses.toString(ourIP, ourPort) + ". Wait until somebody else tells us the same thing.");
|
||||||
} else {
|
} else {
|
||||||
changeIt = true;
|
changeIt = true;
|
||||||
@@ -1438,7 +1451,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
|
|||||||
} else {
|
} else {
|
||||||
if (from.equals(_lastFromv6) || !eq(_lastOurIPv6, _lastOurPortv6, ourIP, ourPort)) {
|
if (from.equals(_lastFromv6) || !eq(_lastOurIPv6, _lastOurPortv6, ourIP, ourPort)) {
|
||||||
if (_log.shouldLog(Log.INFO))
|
if (_log.shouldLog(Log.INFO))
|
||||||
_log.info("The router " + from + " told us we have a new IP - "
|
_log.info("The router " + from + " told us we have a new IP/port - "
|
||||||
+ Addresses.toString(ourIP, ourPort) + ". Wait until somebody else tells us the same thing.");
|
+ Addresses.toString(ourIP, ourPort) + ". Wait until somebody else tells us the same thing.");
|
||||||
} else {
|
} else {
|
||||||
changeIt = true;
|
changeIt = true;
|
||||||
@@ -1470,12 +1483,12 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
|
|||||||
* @return true if updated
|
* @return true if updated
|
||||||
*/
|
*/
|
||||||
private boolean changeAddress(byte ourIP[], int ourPort) {
|
private boolean changeAddress(byte ourIP[], int ourPort) {
|
||||||
// this defaults to true when we are firewalled and false otherwise.
|
|
||||||
boolean fixedPort = getIsPortFixed();
|
|
||||||
boolean updated = false;
|
boolean updated = false;
|
||||||
boolean fireTest = false;
|
boolean fireTest = false;
|
||||||
|
|
||||||
boolean isIPv6 = ourIP.length == 16;
|
boolean isIPv6 = ourIP.length == 16;
|
||||||
|
// this defaults to true when we are firewalled or unknown and false otherwise.
|
||||||
|
boolean fixedPort = getIsPortFixed(isIPv6);
|
||||||
|
|
||||||
synchronized (_rebuildLock) {
|
synchronized (_rebuildLock) {
|
||||||
RouterAddress current = getCurrentExternalAddress(isIPv6);
|
RouterAddress current = getCurrentExternalAddress(isIPv6);
|
||||||
@@ -1539,8 +1552,18 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
|
|||||||
//if ( (_reachabilityStatus != CommSystemFacade.STATUS_OK) ||
|
//if ( (_reachabilityStatus != CommSystemFacade.STATUS_OK) ||
|
||||||
// (_externalListenHost == null) || (_externalListenPort <= 0) ||
|
// (_externalListenHost == null) || (_externalListenPort <= 0) ||
|
||||||
// (_context.clock().now() - _reachabilityStatusLastUpdated > 2*TEST_FREQUENCY) ) {
|
// (_context.clock().now() - _reachabilityStatusLastUpdated > 2*TEST_FREQUENCY) ) {
|
||||||
// they told us something different and our tests are either old or failing
|
|
||||||
|
// they told us something different and our tests are either old or failing
|
||||||
if (rebuild) {
|
if (rebuild) {
|
||||||
|
if (externalListenPort > 0 && ourPort > 0 &&
|
||||||
|
externalListenPort != ourPort &&
|
||||||
|
_context.getProperty(PROP_EXTERNAL_PORT, 0) != ourPort) {
|
||||||
|
// save the external port setting only
|
||||||
|
_context.router().saveConfig(PROP_EXTERNAL_PORT, Integer.toString(ourPort));
|
||||||
|
_context.router().eventLog().addEvent(EventLog.CHANGE_PORT, "IPv" +
|
||||||
|
(isIPv6 ? '6' : '4') +
|
||||||
|
" port " + ourPort);
|
||||||
|
}
|
||||||
if (_enableSSU2) {
|
if (_enableSSU2) {
|
||||||
// flush SSU2 tokens
|
// flush SSU2 tokens
|
||||||
if (ourPort != externalListenPort) {
|
if (ourPort != externalListenPort) {
|
||||||
@@ -1668,13 +1691,23 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
|
|||||||
* our firewall is changing our port), unless overridden by the property.
|
* our firewall is changing our port), unless overridden by the property.
|
||||||
* We must have an accurate external port when firewalled, or else
|
* We must have an accurate external port when firewalled, or else
|
||||||
* our signature of the SessionCreated packet will be invalid.
|
* our signature of the SessionCreated packet will be invalid.
|
||||||
|
*
|
||||||
|
* As of 0.9.58, returns false if status is UNKNOWN
|
||||||
*/
|
*/
|
||||||
private boolean getIsPortFixed() {
|
private boolean getIsPortFixed(boolean isIPv6) {
|
||||||
String prop = _context.getProperty(PROP_FIXED_PORT);
|
String prop = _context.getProperty(PROP_FIXED_PORT);
|
||||||
if (prop != null)
|
if (prop != null)
|
||||||
return Boolean.parseBoolean(prop);
|
return Boolean.parseBoolean(prop);
|
||||||
Status status = getReachabilityStatus();
|
Status status = getReachabilityStatus();
|
||||||
return !STATUS_NEED_INTRO.contains(status);
|
if (isIPv6) {
|
||||||
|
if (STATUS_IPV6_UNK.contains(status))
|
||||||
|
return false;
|
||||||
|
return !STATUS_IPV6_FW.contains(status);
|
||||||
|
} else {
|
||||||
|
if (STATUS_IPV4_UNK.contains(status))
|
||||||
|
return false;
|
||||||
|
return !STATUS_IPV4_FW.contains(status);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -4002,7 +4035,8 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
|
|||||||
// to prevent thrashing
|
// to prevent thrashing
|
||||||
if ((STATUS_OK.contains(old) && STATUS_FW.contains(status)) ||
|
if ((STATUS_OK.contains(old) && STATUS_FW.contains(status)) ||
|
||||||
(STATUS_OK.contains(status) && STATUS_FW.contains(old)) ||
|
(STATUS_OK.contains(status) && STATUS_FW.contains(old)) ||
|
||||||
(STATUS_FW.contains(status) && STATUS_FW.contains(old))) {
|
(STATUS_FW.contains(status) && STATUS_FW.contains(old)) ||
|
||||||
|
(!isIPv6 && STATUS_IPV4_UNK.contains(old) && !STATUS_IPV4_UNK.contains(status))) {
|
||||||
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 +
|
||||||
|
Reference in New Issue
Block a user