From e9375b50b0b7228f77dc8ef3d99d1021f89675ff Mon Sep 17 00:00:00 2001 From: Joseph Henry Date: Mon, 13 Dec 2021 11:54:23 -0800 Subject: [PATCH] Prevent path-amnesia --- include/ZeroTierOne.h | 2 +- node/Peer.cpp | 60 +++++++++++++++++++++++++++++++++++-------- 2 files changed, 51 insertions(+), 11 deletions(-) diff --git a/include/ZeroTierOne.h b/include/ZeroTierOne.h index 2bdea6474..ca435a71f 100644 --- a/include/ZeroTierOne.h +++ b/include/ZeroTierOne.h @@ -163,7 +163,7 @@ extern "C" { /** * Maximum number of direct network paths to a given peer */ -#define ZT_MAX_PEER_NETWORK_PATHS 16 +#define ZT_MAX_PEER_NETWORK_PATHS 64 /** * Maximum number of path configurations that can be set diff --git a/node/Peer.cpp b/node/Peer.cpp index a9dd8ce1e..ab4cb7c51 100644 --- a/node/Peer.cpp +++ b/node/Peer.cpp @@ -118,28 +118,68 @@ void Peer::received( } if ( (!havePath) && RR->node->shouldUsePathForZeroTierTraffic(tPtr,_id.address(),path->localSocket(),path->address()) ) { + + /** + * First, fill all free slots before attempting to replace a path + * - If the above fails, attempt to replace the path that has been dead the longest + * - If there are no free slots, and no dead paths (unlikely), then replace old path most similar to new path + * - If all of the above fails to yield a suitable replacement. Replace first path found to have lower `(quality / priority)` + */ + if (verb == Packet::VERB_OK) { Mutex::Lock _l(_paths_m); - unsigned int replacePath = ZT_MAX_PEER_NETWORK_PATHS; + uint64_t maxScore = 0; + uint64_t currScore; long replacePathQuality = 0; + bool foundFreeSlot = false; + for(unsigned int i=0;ialive(now)) || _paths[i].p->address().ipsEqual(path->address()) ) { - replacePath = i; - break; - } else { - const long q = _paths[i].p->quality(now) / _paths[i].priority; - if (q > replacePathQuality) { - replacePathQuality = q; - replacePath = i; + // Reward dead paths + if (!_paths[i].p->alive(now)) { + currScore = _paths[i].p->age(now) / 1000; + } + // Reward as similarity increases + if (_paths[i].p->address().ipsEqual(path->address())) { + currScore++; + if (_paths[i].p->address().port() == path->address().port()) { + currScore++; + if (_paths[i].p->localSocket() == path->localSocket()) { + currScore++; // max score (3) + } } } - } else { + // If best so far, mark for replacement + if (currScore > maxScore) { + maxScore = currScore; + replacePath = i; + } + } + else { + foundFreeSlot = true; replacePath = i; break; } } + if (!foundFreeSlot) { + if (maxScore > 3) { + // Do nothing. We found a dead path and have already marked it as a candidate + } + // If we couldn't find a replacement by matching, replacing a dead path, or taking a free slot, then replace by quality + else if (maxScore == 0) { + for(unsigned int i=0;iquality(now) / _paths[i].priority; + if (q > replacePathQuality) { + replacePathQuality = q; + replacePath = i; + } + } + } + } + } if (replacePath != ZT_MAX_PEER_NETWORK_PATHS) { RR->t->peerLearnedNewPath(tPtr, networkId, *this, path, packetId);