Skip to content

Commit ce9330e

Browse files
authored
Prevent path-learning loops (zerotier#1914)
* Prevent path-learning loops * Only allow new overwrite if not bonded
1 parent b2a981f commit ce9330e

File tree

1 file changed

+40
-49
lines changed

1 file changed

+40
-49
lines changed

node/Peer.cpp

Lines changed: 40 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -109,76 +109,51 @@ void Peer::received(
109109
havePath = true;
110110
break;
111111
}
112+
// If same address on same interface then don't learn unless existing path isn't alive (prevents learning loop)
113+
if (_paths[i].p->address().ipsEqual(path->address()) && _paths[i].p->localSocket() == path->localSocket()) {
114+
if (_paths[i].p->alive(now) && !_bond) {
115+
havePath = true;
116+
break;
117+
}
118+
}
112119
} else {
113120
break;
114121
}
115122
}
116123
}
117124

118125
if ( (!havePath) && RR->node->shouldUsePathForZeroTierTraffic(tPtr,_id.address(),path->localSocket(),path->address()) ) {
119-
120-
/**
121-
* First, fill all free slots before attempting to replace a path
122-
* - If the above fails, attempt to replace the path that has been dead the longest
123-
* - If there are no free slots, and no dead paths (unlikely), then replace old path most similar to new path
124-
* - If all of the above fails to yield a suitable replacement. Replace first path found to have lower `(quality / priority)`
125-
*/
126-
127126
if (verb == Packet::VERB_OK) {
128127
Mutex::Lock _l(_paths_m);
128+
unsigned int oldestPathIdx = ZT_MAX_PEER_NETWORK_PATHS;
129+
unsigned int oldestPathAge = 0;
129130
unsigned int replacePath = ZT_MAX_PEER_NETWORK_PATHS;
130-
uint64_t maxScore = 0;
131-
uint64_t currScore;
132-
long replacePathQuality = 0;
133-
bool foundFreeSlot = false;
134131

135132
for(unsigned int i=0;i<ZT_MAX_PEER_NETWORK_PATHS;++i) {
136-
currScore = 0;
137133
if (_paths[i].p) {
138-
// Reward dead paths
139-
if (!_paths[i].p->alive(now)) {
140-
currScore = _paths[i].p->age(now) / 1000;
134+
// Keep track of oldest path as a last resort option
135+
unsigned int currAge = _paths[i].p->age(now);
136+
if (currAge > oldestPathAge) {
137+
oldestPathAge = currAge;
138+
oldestPathIdx = i;
141139
}
142-
// Reward as similarity increases
143140
if (_paths[i].p->address().ipsEqual(path->address())) {
144-
currScore++;
145-
if (_paths[i].p->address().port() == path->address().port()) {
146-
currScore++;
147-
if (_paths[i].p->localSocket() == path->localSocket()) {
148-
currScore++; // max score (3)
141+
if (_paths[i].p->localSocket() == path->localSocket()) {
142+
if (!_paths[i].p->alive(now)) {
143+
replacePath = i;
144+
break;
149145
}
150146
}
151147
}
152-
// If best so far, mark for replacement
153-
if (currScore > maxScore) {
154-
maxScore = currScore;
155-
replacePath = i;
156-
}
157148
}
158149
else {
159-
foundFreeSlot = true;
160150
replacePath = i;
161151
break;
162152
}
163153
}
164-
if (!foundFreeSlot) {
165-
if (maxScore > 3) {
166-
// Do nothing. We found a dead path and have already marked it as a candidate
167-
}
168-
// If we couldn't find a replacement by matching, replacing a dead path, or taking a free slot, then replace by quality
169-
else if (maxScore == 0) {
170-
for(unsigned int i=0;i<ZT_MAX_PEER_NETWORK_PATHS;++i) {
171-
if (_paths[i].p) {
172-
const long q = _paths[i].p->quality(now) / _paths[i].priority;
173-
if (q > replacePathQuality) {
174-
replacePathQuality = q;
175-
replacePath = i;
176-
}
177-
}
178-
}
179-
}
180-
}
181154

155+
// If we didn't find a good candidate then resort to replacing oldest path
156+
replacePath = (replacePath == ZT_MAX_PEER_NETWORK_PATHS) ? oldestPathIdx : replacePath;
182157
if (replacePath != ZT_MAX_PEER_NETWORK_PATHS) {
183158
RR->t->peerLearnedNewPath(tPtr, networkId, *this, path, packetId);
184159
_paths[replacePath].lr = now;
@@ -540,11 +515,15 @@ unsigned int Peer::doPingAndKeepalive(void *tPtr,int64_t now)
540515
// let those old links expire.
541516
long maxPriority = 0;
542517
for(unsigned int i=0;i<ZT_MAX_PEER_NETWORK_PATHS;++i) {
543-
if (_paths[i].p)
518+
if (_paths[i].p) {
544519
maxPriority = std::max(_paths[i].priority,maxPriority);
545-
else break;
520+
}
521+
else {
522+
break;
523+
}
546524
}
547525

526+
bool deletionOccurred = false;
548527
for(unsigned int i=0;i<ZT_MAX_PEER_NETWORK_PATHS;++i) {
549528
if (_paths[i].p) {
550529
// Clean expired and reduced priority paths
@@ -554,10 +533,22 @@ unsigned int Peer::doPingAndKeepalive(void *tPtr,int64_t now)
554533
_paths[i].p->sent(now);
555534
sent |= (_paths[i].p->address().ss_family == AF_INET) ? 0x1 : 0x2;
556535
}
557-
} else {
536+
}
537+
else {
558538
_paths[i] = _PeerPath();
539+
deletionOccurred = true;
559540
}
560-
} else break;
541+
}
542+
if (!_paths[i].p || deletionOccurred) {
543+
for(unsigned int j=i;j<ZT_MAX_PEER_NETWORK_PATHS;++j) {
544+
if (_paths[j].p && i != j) {
545+
_paths[i] = _paths[j];
546+
_paths[j] = _PeerPath();
547+
break;
548+
}
549+
}
550+
deletionOccurred = false;
551+
}
561552
}
562553
return sent;
563554
}

0 commit comments

Comments
 (0)