Transport: Detect IPv6 address changes

Use same IPv6 address as previous if still valid
Log tweaks
This commit is contained in:
zzz
2019-09-07 13:29:37 +00:00
parent e03a94647c
commit bb88555a63
5 changed files with 107 additions and 45 deletions

View File

@ -18,7 +18,7 @@ public class RouterVersion {
/** deprecated */
public final static String ID = "Monotone";
public final static String VERSION = CoreVersion.VERSION;
public final static long BUILD = 3;
public final static long BUILD = 4;
/** for example "-test" */
public final static String EXTRA = "";

View File

@ -262,7 +262,8 @@ public abstract class TransportImpl implements Transport {
// Bail out now as it will NPE in a dozen places below.
return;
}
boolean log = false;
//boolean log = false;
final boolean debug = _log.shouldDebug();
if (sendSuccessful)
msg.timestamp("afterSend(successful)");
else
@ -272,7 +273,7 @@ public abstract class TransportImpl implements Transport {
msg.transportFailed(getStyle());
if (msToSend > 1500) {
if (_log.shouldDebug())
if (debug)
_log.debug(getStyle() + " afterSend slow: " + (sendSuccessful ? "success " : "FAIL ")
+ msg.getMessageSize() + " byte "
+ msg.getMessageType() + ' ' + msg.getMessageId() + " to "
@ -292,7 +293,7 @@ public abstract class TransportImpl implements Transport {
+ msg.getMessageType() + " " + msg.getMessageId() + " from " + _context.routerHash().toBase64().substring(0,6)
+ " to " + msg.getTarget().getIdentity().calculateHash().toBase64().substring(0,6) + ": " + msg.toString());
} else {
if (_log.shouldDebug())
if (debug)
_log.debug(getStyle() + " afterSend: " + (sendSuccessful ? "success " : "FAIL ")
+ msg.getMessageSize() + " byte "
+ msg.getMessageType() + " " + msg.getMessageId() + " from " + _context.routerHash().toBase64().substring(0,6)
@ -300,16 +301,16 @@ public abstract class TransportImpl implements Transport {
}
if (sendSuccessful) {
if (_log.shouldLog(Log.DEBUG))
if (debug)
_log.debug(getStyle() + " Sent " + msg.getMessageType() + " successfully to "
+ msg.getTarget().getIdentity().getHash().toBase64());
Job j = msg.getOnSendJob();
if (j != null)
_context.jobQueue().addJob(j);
log = true;
//log = true;
msg.discardData();
} else {
if (_log.shouldDebug())
if (debug)
_log.debug(getStyle() + " Failed to send " + msg.getMessageType()
+ " to " + msg.getTarget().getIdentity().getHash().toBase64()
+ " (details: " + msg + ')');
@ -323,8 +324,8 @@ public abstract class TransportImpl implements Transport {
_context.outNetMessagePool().add(msg);
// don't discard the data yet!
} else {
if (_log.shouldLog(Log.INFO))
_log.info("No more time left (" + new Date(msg.getExpiration())
if (debug)
_log.debug("No more time left (" + new Date(msg.getExpiration())
+ ", expiring without sending successfully the "
+ msg.getMessageType());
if (msg.getOnFailedSendJob() != null)
@ -333,13 +334,13 @@ public abstract class TransportImpl implements Transport {
if (selector != null) {
_context.messageRegistry().unregisterPending(msg);
}
log = true;
//log = true;
msg.discardData();
}
} else {
MessageSelector selector = msg.getReplySelector();
if (_log.shouldLog(Log.INFO))
_log.info("Failed and no requeue allowed for a "
if (debug)
_log.debug("Failed and no requeue allowed for a "
+ msg.getMessageSize() + " byte "
+ msg.getMessageType() + " message with selector " + selector, new Exception("fail cause"));
if (msg.getOnFailedSendJob() != null)
@ -348,28 +349,28 @@ public abstract class TransportImpl implements Transport {
_context.jobQueue().addJob(msg.getOnFailedReplyJob());
if (selector != null)
_context.messageRegistry().unregisterPending(msg);
log = true;
//log = true;
msg.discardData();
}
}
/****
if (log) {
/*
String type = msg.getMessageType();
// the udp transport logs some further details
_context.messageHistory().sendMessage(type, msg.getMessageId(),
msg.getExpiration(),
msg.getTarget().getIdentity().getHash(),
sendSuccessful);
*/
}
****/
long now = _context.clock().now();
long sendTime = now - msg.getSendBegin();
long allTime = now - msg.getCreated();
if (allTime > 5*1000) {
if (_log.shouldLog(Log.INFO))
_log.info("Took too long from preparation to afterSend(ok? " + sendSuccessful
if (debug)
_log.debug("Took too long from preparation to afterSend(ok? " + sendSuccessful
+ "): " + allTime + "ms/" + sendTime + "ms after failing on: "
+ msg.getFailedTransports()
+ (sendSuccessful ? (" and succeeding on " + getStyle()) : ""));

View File

@ -243,6 +243,8 @@ public class TransportManager implements TransportEventListener {
return;
// non-local (unless test mode), don't include loopback, include IPv6
Set<String> ipset = Addresses.getAddresses(_context.getBooleanProperty("i2np.allowLocal"), false, true);
String lastv4 = _context.getProperty(UDPTransport.PROP_IP);
String lastv6 = _context.getProperty(UDPTransport.PROP_IPV6);
//
// Avoid IPv6 temporary addresses if we have a non-temporary one
//
@ -260,15 +262,20 @@ public class TransportManager implements TransportEventListener {
// _log.warn("Not binding to deprecated temporary address " + bt);
// continue;
//}
if (Addresses.isTemporary(v6addr)) {
if (Addresses.isTemporary(v6addr) && !ips.equals(lastv6)) {
// Save temporary addresses
// we only use these if we don't have a non-temporary adress
// we only use these if we don't have a non-temporary address,
// unless it's the last IP we used
tempV6Addresses.add(v6addr);
continue;
}
hasNonTempV6Address = true;
}
addresses.add(addr);
// put previously used addresses at the front of the list
if (ips.equals(lastv4) || ips.equals(lastv6))
addresses.add(0, addr);
else
addresses.add(addr);
} catch (UnknownHostException e) {
_log.error("UDP failed to bind to local address", e);
}
@ -286,8 +293,20 @@ public class TransportManager implements TransportEventListener {
}
}
for (Transport t : ts) {
// the transports really don't like being called with more than one of each
boolean hasv4 = false;
boolean hasv6 = false;
for (InetAddress ia : addresses) {
byte[] ip = ia.getAddress();
if (ip.length == 4) {
if (hasv4)
continue;
hasv4 = true;
} else {
if (hasv6)
continue;
hasv6 = true;
}
t.externalAddressReceived(SOURCE_INTERFACE, ip, 0);
}
}

View File

@ -119,9 +119,9 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
private volatile long _expireTimeout;
/** last report from a peer of our IP */
private Hash _lastFrom;
private byte[] _lastOurIP;
private int _lastOurPort;
private Hash _lastFromv4, _lastFromv6;
private byte[] _lastOurIPv4, _lastOurIPv6;
private int _lastOurPortv4, _lastOurPortv6;
/** since we don't publish our IP/port if introduced anymore, we need
to store it somewhere. */
private RouterAddress _currentOurV4Address;
@ -168,6 +168,8 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
public static final String PROP_IP= "i2np.lastIP";
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";
/** do we require introducers, regardless of our status? */
public static final String PROP_FORCE_INTRODUCERS = "i2np.udp.forceIntroducers";
@ -543,13 +545,16 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
// REA param is false;
// TransportManager.startListening() calls router.rebuildRouterInfo()
if (newPort > 0 && bindToAddrs.isEmpty()) {
boolean hasv6 = false;
for (InetAddress ia : getSavedLocalAddresses()) {
// Discovered or configured addresses are presumed good at the start.
// when externalAddressReceived() was called with SOURCE_INTERFACE,
// isAlive() was false, so setReachabilityStatus() was not called
// TODO should we set both to unknown and wait for an inbound v6 conn,
// since there's no v6 testing?
if (ia.getAddress().length == 16) {
// only call REA for one v6 address
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.
@ -873,8 +878,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
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?
// TODO this will set non-firewalled every time our IPv6 address changes
if (!isIPv6Firewalled())
setReachabilityStatus(Status.IPV4_UNKNOWN_IPV6_OK, true);
}
@ -914,21 +918,22 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
* - This gets harder if and when we publish multiple addresses, or IPv6
*
* @param from Hash of inbound destination
* @param ourIP publicly routable IPv4 only
* @param ourIP publicly routable IPv4 or IPv6 only, non-null
* @param ourPort &gt;= 1024
*/
void externalAddressReceived(Hash from, byte ourIP[], int ourPort) {
boolean isValid = isValid(ourIP) &&
TransportUtil.isValidPort(ourPort);
boolean explicitSpecified = explicitAddressSpecified();
boolean inboundRecent = _lastInboundReceivedOn + ALLOW_IP_CHANGE_INTERVAL > System.currentTimeMillis();
boolean inboundRecent;
if (ourIP.length == 4)
inboundRecent = _lastInboundReceivedOn + ALLOW_IP_CHANGE_INTERVAL > System.currentTimeMillis();
else
inboundRecent = _lastInboundIPv6 + ALLOW_IP_CHANGE_INTERVAL > _context.clock().now();
if (_log.shouldLog(Log.INFO))
_log.info("External address received: " + Addresses.toString(ourIP, ourPort) + " from "
+ from + ", isValid? " + isValid + ", explicitSpecified? " + explicitSpecified
+ ", receivedInboundRecent? " + inboundRecent + " status " + _reachabilityStatus);
if (ourIP.length != 4) {
return;
}
if (explicitSpecified)
return;
@ -966,23 +971,35 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
// New IP
boolean changeIt = false;
synchronized(this) {
if (from.equals(_lastFrom) || !eq(_lastOurIP, _lastOurPort, ourIP, ourPort)) {
_lastFrom = from;
_lastOurIP = ourIP;
_lastOurPort = ourPort;
if (_log.shouldLog(Log.INFO))
_log.info("The router " + from + " told us we have a new IP - "
+ Addresses.toString(ourIP, ourPort) + ". Wait until somebody else tells us the same thing.");
if (ourIP.length == 4) {
if (from.equals(_lastFromv4) || !eq(_lastOurIPv4, _lastOurPortv4, ourIP, ourPort)) {
if (_log.shouldLog(Log.INFO))
_log.info("The router " + from + " told us we have a new IP - "
+ Addresses.toString(ourIP, ourPort) + ". Wait until somebody else tells us the same thing.");
} else {
changeIt = true;
}
_lastFromv4 = from;
_lastOurIPv4 = ourIP;
_lastOurPortv4 = ourPort;
} else if (ourIP.length == 16) {
if (from.equals(_lastFromv6) || !eq(_lastOurIPv6, _lastOurPortv6, ourIP, ourPort)) {
if (_log.shouldLog(Log.INFO))
_log.info("The router " + from + " told us we have a new IP - "
+ Addresses.toString(ourIP, ourPort) + ". Wait until somebody else tells us the same thing.");
} else {
changeIt = true;
}
_lastFromv6 = from;
_lastOurIPv6 = ourIP;
_lastOurPortv6 = ourPort;
} else {
_lastFrom = from;
_lastOurIP = ourIP;
_lastOurPort = ourPort;
changeIt = true;
return;
}
}
if (changeIt) {
if (_log.shouldLog(Log.INFO))
_log.info(from + " and " + _lastFrom + " agree we have the IP "
_log.info(from + " and another peer agree we have the IP "
+ Addresses.toString(ourIP, ourPort) + ". Changing address.");
changeAddress(ourIP, ourPort);
}
@ -997,6 +1014,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
* @param ourIP MUST have been previously validated with isValid()
* IPv4 or IPv6 OK
* @param ourPort &gt;= 1024 or 0 for no change
* @return true if updated
*/
private boolean changeAddress(byte ourIP[], int ourPort) {
// this defaults to true when we are firewalled and false otherwise.
@ -1022,6 +1040,17 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
if (ourPort > 0 &&
!eq(externalListenHost, externalListenPort, ourIP, ourPort)) {
if (isIPv6) {
// For IPv6, we only accept changes if this is one of our local addresses
Set<String> ipset = Addresses.getAddresses(false, true);
String ipstr = Addresses.toString(ourIP);
if (!ipset.contains(ipstr)) {
if (_log.shouldInfo())
_log.info("New IPv6 address received but not one of our local addresses: " + ipstr, new Exception());
return false;
}
}
// This prevents us from changing our IP when we are not firewalled
//if ( (_reachabilityStatus != CommSystemFacade.STATUS_OK) ||
// (_externalListenHost == null) || (_externalListenPort <= 0) ||
@ -1099,6 +1128,15 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
} else if (ourIP.length == 4 && !fixedPort) {
// save PROP_EXTERNAL_PORT
_context.router().saveConfig(changes, null);
} else if (ourIP.length == 16) {
oldIP = _context.getProperty(PROP_IPV6);
if (!newIP.equals(oldIP)) {
changes.put(PROP_IPV6, newIP);
_context.router().saveConfig(changes, null);
if (oldIP != null) {
_context.router().eventLog().addEvent(EventLog.CHANGE_IP, newIP);
}
}
}
// deadlock thru here ticket #1699
// this causes duplicate publish, REA() call above calls rebuildRouterInfo
@ -2208,7 +2246,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
private RouterAddress locked_rebuildExternalAddress(String host, int port, boolean allowRebuildRouterInfo) {
if (_log.shouldDebug())
_log.debug("REA4 " + host + ':' + port);
_log.debug("REA4 " + host + ' ' + port, new Exception());
if (_context.router().isHidden())
return null;