-
Notifications
You must be signed in to change notification settings - Fork 211
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Reduce pings and dns lookups #993
Changes from all commits
472c0af
96b9241
ced3b16
1cf9ed5
8957221
3bdf89a
61e74fa
ae05c2c
6220388
825bc26
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,6 +3,12 @@ | |
* http://www.gnu.org/ for further details of the GPL. */ | ||
package freenet.node; | ||
|
||
import java.util.ArrayDeque; | ||
import java.util.Arrays; | ||
import java.util.Deque; | ||
import java.util.HashSet; | ||
import java.util.Set; | ||
|
||
import freenet.support.LogThresholdCallback; | ||
import freenet.support.Logger; | ||
import freenet.support.Logger.LogLevel; | ||
|
@@ -16,6 +22,8 @@ public class DNSRequester implements Runnable { | |
|
||
final Node node; | ||
private long lastLogTime; | ||
private final Set<Double> recentNodeIdentitySet = new HashSet<>(); | ||
private final Deque<Double> recentNodeIdentityQueue = new ArrayDeque<>(); | ||
// Only set when doing simulations. | ||
static boolean DISABLE = false; | ||
|
||
|
@@ -54,25 +62,53 @@ public void run() { | |
} | ||
|
||
private void realRun() { | ||
PeerNode[] nodes = node.getPeers().myPeers(); | ||
long now = System.currentTimeMillis(); | ||
if((now - lastLogTime) > 1000) { | ||
if(logMINOR) | ||
Logger.minor(this, "Processing DNS Requests (log rate-limited)"); | ||
// run DNS requests for not recently checked, unconnected | ||
// nodes to avoid the coupon collector's problem | ||
PeerNode[] nodesToCheck = Arrays.stream(node.getPeers().myPeers()) | ||
.filter(peerNode -> !peerNode.isConnected()) | ||
// identify recent nodes by location, because the exact location cannot be used twice | ||
// (that already triggers the simplest pitch black attack defenses) | ||
// Double may not be comparable in general (floating point), | ||
// but just checking for equality with itself is safe | ||
.filter(peerNode -> !recentNodeIdentitySet.contains(peerNode.getLocation())) | ||
.toArray(PeerNode[]::new); | ||
|
||
if(logMINOR) { | ||
long now = System.currentTimeMillis(); | ||
if((now - lastLogTime) > 100) { | ||
Logger.minor(this, "Processing DNS Requests (log rate-limited)"); | ||
} | ||
lastLogTime = now; | ||
} | ||
for(PeerNode pn: nodes) { | ||
//Logger.minor(this, "Node: "+pn); | ||
if(!pn.isConnected()) { | ||
// Not connected | ||
// Try new DNS lookup | ||
//Logger.minor(this, "Doing lookup on "+pn+" of "+nodes.length); | ||
pn.maybeUpdateHandshakeIPs(false); | ||
|
||
int unconnectedNodesLength = nodesToCheck.length; | ||
if (unconnectedNodesLength == 0) { | ||
return; // nothing to do | ||
} | ||
// check a randomly chosen node that has not been checked | ||
// recently to avoid sending bursts of DNS requests | ||
PeerNode pn = nodesToCheck[node.getFastWeakRandom().nextInt(unconnectedNodesLength)]; | ||
if (unconnectedNodesLength < 5) { | ||
// no need for optimizations: just clear all state | ||
recentNodeIdentitySet.clear(); | ||
recentNodeIdentityQueue.clear(); | ||
} else { | ||
// do not request this node again, | ||
// until at least 81% of the other unconnected nodes have been checked | ||
recentNodeIdentitySet.add(pn.getLocation()); | ||
recentNodeIdentityQueue.offerFirst(pn.getLocation()); | ||
while (recentNodeIdentityQueue.size() > (0.81 * unconnectedNodesLength)) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The comment says 80% but the implementation says 81%. I don't have a strong opinion on the actual percentage used, but please do align the two. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I aligned the comment with the code — 0.81 is just to ensure that 5 nodes with 4 already checked is not an edge case (exactly matching the boundary). |
||
recentNodeIdentitySet.remove(recentNodeIdentityQueue.removeLast()); | ||
} | ||
} | ||
|
||
// Try new DNS lookup | ||
pn.maybeUpdateHandshakeIPs(false); | ||
|
||
int nextMaxWaitTime = 1000 + node.getFastWeakRandom().nextInt(60000); | ||
try { | ||
synchronized(this) { | ||
wait(10000); // sleep 10s ... | ||
wait(nextMaxWaitTime); // sleep 1-61s ... | ||
} | ||
} catch (InterruptedException e) { | ||
// Ignore, just wake up. Just sleeping to not busy wait anyway | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,7 +14,6 @@ | |
import java.util.ArrayList; | ||
import java.util.Arrays; | ||
import java.util.Collections; | ||
import java.util.Comparator; | ||
import java.util.EnumMap; | ||
import java.util.GregorianCalendar; | ||
import java.util.HashSet; | ||
|
@@ -142,7 +141,7 @@ public abstract class PeerNode implements USKRetrieverCallback, BasePeerNode, Pe | |
|
||
protected long jfkContextLifetime = 0; | ||
/** My low-level address for SocketManager purposes */ | ||
private Peer detectedPeer; | ||
private Peer detectedPeer = null; | ||
/** My OutgoingPacketMangler i.e. the object which encrypts packets sent to this node */ | ||
private final OutgoingPacketMangler outgoingMangler; | ||
/** Advertised addresses */ | ||
|
@@ -456,7 +455,7 @@ public PeerNode(SimpleFieldSet fs, Node node2, NodeCrypto crypto, boolean fromLo | |
|
||
testnetEnabled = fs.getBoolean("testnet", false); | ||
if(testnetEnabled) { | ||
String err = "Ignoring incompatible testnet node " + detectedPeer; | ||
String err = "Ignoring incompatible testnet node " + fs.toOrderedString(); | ||
Logger.error(this, err); | ||
throw new PeerParseException(err); | ||
} | ||
|
@@ -543,8 +542,7 @@ public PeerNode(SimpleFieldSet fs, Node node2, NodeCrypto crypto, boolean fromLo | |
"\nNode: " + HexUtil.bytesToHex(nodeKey) + | ||
"\nNode hash: " + HexUtil.bytesToHex(nodeKeyHash) + | ||
"\nThis: " + HexUtil.bytesToHex(identityHash) + | ||
"\nThis hash: " + HexUtil.bytesToHex(identityHashHash) + | ||
"\nFor: " + getPeer()); | ||
"\nThis hash: " + HexUtil.bytesToHex(identityHashHash)); | ||
|
||
try { | ||
incomingSetupCipher = new Rijndael(256, 256); | ||
|
@@ -593,12 +591,6 @@ public PeerNode(SimpleFieldSet fs, Node node2, NodeCrypto crypto, boolean fromLo | |
} | ||
if(nominalPeer.isEmpty()) { | ||
Logger.normal(this, "No IP addresses found for identity '" + identityAsBase64String + "', possibly at location '" + location + ": " + userToString()); | ||
detectedPeer = null; | ||
} else { | ||
nominalPeer.sort(Peer.PEER_COMPARATOR); | ||
// TODO this throws away all valid addresses but the first, without checking whether they can connect. Need to try a later one if connection fails. | ||
// sort hostName first. | ||
detectedPeer = nominalPeer.get(0); | ||
} | ||
updateShortToString(); | ||
|
||
|
@@ -692,7 +684,6 @@ public PeerNode(SimpleFieldSet fs, Node node2, NodeCrypto crypto, boolean fromLo | |
} | ||
// populate handshakeIPs so handshakes can start ASAP | ||
lastAttemptedHandshakeIPUpdateTime = 0; | ||
maybeUpdateHandshakeIPs(true); | ||
|
||
listeningHandshakeBurstCount = 0; | ||
listeningHandshakeBurstSize = Node.MIN_BURSTING_HANDSHAKE_BURST_SIZE | ||
|
@@ -788,9 +779,18 @@ private boolean parseARK(SimpleFieldSet fs, boolean onStartup, boolean forDiffNo | |
*/ | ||
@Override | ||
public synchronized Peer getPeer() { | ||
if (detectedPeer == null && !nominalPeer.isEmpty()) { | ||
sortNominalPeer(); | ||
detectedPeer = nominalPeer.get(0); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. fixed in 1cf9ed5 |
||
updateShortToString(); | ||
} | ||
return detectedPeer; | ||
} | ||
|
||
private void sortNominalPeer() { | ||
nominalPeer.sort(Peer.PEER_COMPARATOR); | ||
} | ||
|
||
/** | ||
* Returns an array with the advertised addresses and the detected one | ||
*/ | ||
|
@@ -863,7 +863,7 @@ public void maybeUpdateHandshakeIPs(boolean ignoreHostnames) { | |
long now = System.currentTimeMillis(); | ||
Peer localDetectedPeer = null; | ||
synchronized(this) { | ||
localDetectedPeer = detectedPeer; | ||
localDetectedPeer = getPeer(); | ||
if((now - lastAttemptedHandshakeIPUpdateTime) < MINUTES.toMillis(5)) { | ||
//Logger.minor(this, "Looked up recently (localDetectedPeer = "+localDetectedPeer + " : "+((localDetectedPeer == null) ? "" : localDetectedPeer.getAddress(false).toString())); | ||
return; | ||
|
@@ -1187,7 +1187,7 @@ public void startRekeying() { | |
sendHandshakeTime = now; // Immediately | ||
ctx = null; | ||
} | ||
Logger.normal(this, "We are asking for the key to be renewed (" + this.detectedPeer + ')'); | ||
Logger.normal(this, "We are asking for the key to be renewed (" + this.getPeer() + ')'); | ||
} | ||
|
||
/** | ||
|
@@ -1414,7 +1414,12 @@ public boolean shouldSendHandshake() { | |
boolean tempShouldSendHandshake = false; | ||
synchronized(this) { | ||
if(disconnecting) return false; | ||
tempShouldSendHandshake = ((now > sendHandshakeTime) && (handshakeIPs != null) && (isRekeying || !isConnected())); | ||
if (now > sendHandshakeTime) { | ||
maybeUpdateHandshakeIPs(true); | ||
tempShouldSendHandshake = ((now > sendHandshakeTime) && (getHandshakeIPs() != null) && ( | ||
isRekeying | ||
|| !isConnected())); | ||
} | ||
} | ||
if(logMINOR) Logger.minor(this, "shouldSendHandshake(): initial = "+tempShouldSendHandshake); | ||
if(tempShouldSendHandshake && (hasLiveHandshake(now))) | ||
|
@@ -1807,7 +1812,7 @@ private void setDetectedPeer(Peer newPeer) { | |
return; | ||
} | ||
synchronized(this) { | ||
Peer oldPeer = detectedPeer; | ||
Peer oldPeer = getPeer(); | ||
if((newPeer != null) && ((oldPeer == null) || !oldPeer.equals(newPeer))) { | ||
this.detectedPeer = newPeer; | ||
updateShortToString(); | ||
|
@@ -2256,7 +2261,7 @@ protected void sendInitialMessages() { | |
loadSender(true).setSendASAP(); | ||
loadSender(false).setSendASAP(); | ||
Message locMsg = DMT.createFNPLocChangeNotificationNew(node.getLocationManager().getLocation(), node.getPeers().getPeerLocationDoubles(true)); | ||
Message ipMsg = DMT.createFNPDetectedIPAddress(detectedPeer); | ||
Message ipMsg = DMT.createFNPDetectedIPAddress(getPeer()); | ||
Message timeMsg = DMT.createFNPTime(System.currentTimeMillis()); | ||
Message dRoutingMsg = DMT.createRoutingStatus(!disableRoutingHasBeenSetLocally); | ||
Message uptimeMsg = DMT.createFNPUptime((byte)(int)(100*node.getUptimeEstimator().getUptime())); | ||
|
@@ -2276,7 +2281,7 @@ protected void sendInitialMessages() { | |
} | ||
|
||
private void sendIPAddressMessage() { | ||
Message ipMsg = DMT.createFNPDetectedIPAddress(detectedPeer); | ||
Message ipMsg = DMT.createFNPDetectedIPAddress(getPeer()); | ||
try { | ||
sendAsync(ipMsg, null, node.getNodeStats().changedIPCtr); | ||
} catch(NotConnectedException e) { | ||
|
@@ -2444,7 +2449,7 @@ protected synchronized boolean innerProcessNewNoderef(SimpleFieldSet fs, boolean | |
// Anything may be omitted for a differential node reference | ||
boolean changedAnything = false; | ||
if(!forDiffNodeRef && (false != fs.getBoolean("testnet", false))) { | ||
String err = "Preventing connection to node " + detectedPeer +" - testnet is enabled!"; | ||
String err = "Preventing connection to node " + getPeer() +" - testnet is enabled!"; | ||
Logger.error(this, err); | ||
throw new FSParseException(err); | ||
} | ||
|
@@ -2554,8 +2559,9 @@ else if(logMINOR) | |
nominalPeer.add(p); | ||
} | ||
} | ||
sortNominalPeer(); | ||
// XXX should we trigger changedAnything on *any* change, or on just *addition* of new addresses | ||
if(!Arrays.equals(oldPeers, nominalPeer.toArray(new Peer[nominalPeer.size()]))) { | ||
if(!Arrays.equals(oldPeers, nominalPeer.toArray(new Peer[0]))) { | ||
changedAnything = true; | ||
if(logMINOR) Logger.minor(this, "Got new physical.udp for "+this+" : "+Arrays.toString(nominalPeer.toArray())); | ||
// Look up the DNS names if any ASAP | ||
|
@@ -2694,8 +2700,8 @@ public synchronized SimpleFieldSet exportDiskFieldSet() { | |
*/ | ||
public synchronized SimpleFieldSet exportMetadataFieldSet(long now) { | ||
SimpleFieldSet fs = new SimpleFieldSet(true); | ||
if(detectedPeer != null) | ||
fs.putSingle("detected.udp", detectedPeer.toStringPrefNumeric()); | ||
if(getPeer() != null) | ||
fs.putSingle("detected.udp", getPeer().toStringPrefNumeric()); | ||
if(lastReceivedPacketTime() > 0) | ||
fs.put("timeLastReceivedPacket", timeLastReceivedPacket); | ||
if(lastReceivedAckTime() > 0) | ||
|
@@ -3956,7 +3962,7 @@ public WeakReference<PeerNode> getWeakRef() { | |
public Peer getHandshakeIP() { | ||
Peer[] localHandshakeIPs; | ||
if(!shouldSendHandshake()) { | ||
if(logMINOR) Logger.minor(this, "Not sending handshake to "+getPeer()+" because pn.shouldSendHandshake() returned false"); | ||
if(logMINOR) Logger.minor(this, "Not sending handshake to "+detectedPeer+" because pn.shouldSendHandshake() returned false"); | ||
return null; | ||
} | ||
long firstTime = System.currentTimeMillis(); | ||
|
@@ -5427,7 +5433,7 @@ public Random paddingGen() { | |
} | ||
|
||
public synchronized boolean matchesPeerAndPort(Peer peer) { | ||
if(detectedPeer != null && detectedPeer.laxEquals(peer)) return true; | ||
if(getPeer() != null && getPeer().laxEquals(peer)) return true; | ||
if(nominalPeer != null) { // FIXME condition necessary??? | ||
for(Peer p : nominalPeer) { | ||
if(p != null && p.laxEquals(peer)) return true; | ||
|
@@ -5440,8 +5446,8 @@ public synchronized boolean matchesPeerAndPort(Peer peer) { | |
* @param strict If true, only match if the IP is actually in use. If false, | ||
* also match from nominal IP addresses and domain names etc. */ | ||
public synchronized boolean matchesIP(FreenetInetAddress addr, boolean strict) { | ||
if(detectedPeer != null) { | ||
FreenetInetAddress a = detectedPeer.getFreenetAddress(); | ||
if(getPeer() != null) { | ||
FreenetInetAddress a = getPeer().getFreenetAddress(); | ||
if(a != null) { | ||
if(strict ? a.equals(addr) : a.laxEquals(addr)) | ||
return true; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will throw
ArrayIndexOutOfBoundsException
ifunconnectedNodesLength
is zero. Can be solved by an early return.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
early return sounds like the best fix — implemented. Thanks!