summaryrefslogtreecommitdiff
path: root/node/Peer.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'node/Peer.cpp')
-rw-r--r--node/Peer.cpp93
1 files changed, 71 insertions, 22 deletions
diff --git a/node/Peer.cpp b/node/Peer.cpp
index 340f0c10..c75a3e46 100644
--- a/node/Peer.cpp
+++ b/node/Peer.cpp
@@ -32,7 +32,6 @@
#include "Node.hpp"
#include "Switch.hpp"
#include "Network.hpp"
-#include "AntiRecursion.hpp"
#include "SelfAwareness.hpp"
#include "Cluster.hpp"
#include "Packet.hpp"
@@ -46,8 +45,8 @@ namespace ZeroTier {
// Used to send varying values for NAT keepalive
static uint32_t _natKeepaliveBuf = 0;
-Peer::Peer(const Identity &myIdentity,const Identity &peerIdentity)
- throw(std::runtime_error) :
+Peer::Peer(const RuntimeEnvironment *renv,const Identity &myIdentity,const Identity &peerIdentity) :
+ RR(renv),
_lastUsed(0),
_lastReceive(0),
_lastUnicastFrame(0),
@@ -72,7 +71,6 @@ Peer::Peer(const Identity &myIdentity,const Identity &peerIdentity)
}
void Peer::received(
- const RuntimeEnvironment *RR,
const InetAddress &localAddr,
const InetAddress &remoteAddr,
unsigned int hops,
@@ -105,7 +103,6 @@ void Peer::received(
}
outp.append((uint16_t)redirectTo.port());
outp.armor(_key,true);
- RR->antiRec->logOutgoingZT(outp.data(),outp.size());
RR->node->putPacket(localAddr,remoteAddr,outp.data(),outp.size());
} else {
// For older peers we use RENDEZVOUS to coax them into contacting us elsewhere.
@@ -121,7 +118,6 @@ void Peer::received(
outp.append(redirectTo.rawIpData(),16);
}
outp.armor(_key,true);
- RR->antiRec->logOutgoingZT(outp.data(),outp.size());
RR->node->putPacket(localAddr,remoteAddr,outp.data(),outp.size());
}
suboptimalPath = true;
@@ -160,7 +156,7 @@ void Peer::received(
}
}
- if (!pathIsConfirmed) {
+ if ((!pathIsConfirmed)&&(RR->node->shouldUsePathForZeroTierTraffic(localAddr,remoteAddr))) {
if (verb == Packet::VERB_OK) {
Path *slot = (Path *)0;
@@ -169,7 +165,10 @@ void Peer::received(
} else {
uint64_t slotLRmin = 0xffffffffffffffffULL;
for(unsigned int p=0;p<ZT_MAX_PEER_NETWORK_PATHS;++p) {
- if (_paths[p].lastReceived() <= slotLRmin) {
+ if (!_paths[p].active(now)) {
+ slot = &(_paths[p]);
+ break;
+ } else if (_paths[p].lastReceived() <= slotLRmin) {
slotLRmin = _paths[p].lastReceived();
slot = &(_paths[p]);
}
@@ -199,7 +198,7 @@ void Peer::received(
outp.armor(_key,true);
RR->node->putPacket(localAddr,remoteAddr,outp.data(),outp.size());
} else {
- sendHELLO(RR,localAddr,remoteAddr,now);
+ sendHELLO(localAddr,remoteAddr,now);
}
}
@@ -214,7 +213,7 @@ void Peer::received(
}
}
-void Peer::sendHELLO(const RuntimeEnvironment *RR,const InetAddress &localAddr,const InetAddress &atAddress,uint64_t now,unsigned int ttl)
+void Peer::sendHELLO(const InetAddress &localAddr,const InetAddress &atAddress,uint64_t now,unsigned int ttl)
{
// _lock not required here since _id is immutable and nothing else is accessed
@@ -230,11 +229,10 @@ void Peer::sendHELLO(const RuntimeEnvironment *RR,const InetAddress &localAddr,c
outp.append((uint64_t)RR->topology->worldTimestamp());
outp.armor(_key,false); // HELLO is sent in the clear
- RR->antiRec->logOutgoingZT(outp.data(),outp.size());
RR->node->putPacket(localAddr,atAddress,outp.data(),outp.size(),ttl);
}
-bool Peer::doPingAndKeepalive(const RuntimeEnvironment *RR,uint64_t now,int inetAddressFamily)
+bool Peer::doPingAndKeepalive(uint64_t now,int inetAddressFamily)
{
Path *p = (Path *)0;
@@ -248,13 +246,14 @@ bool Peer::doPingAndKeepalive(const RuntimeEnvironment *RR,uint64_t now,int inet
if (p) {
if ((now - p->lastReceived()) >= ZT_PEER_DIRECT_PING_DELAY) {
//TRACE("PING %s(%s) after %llums/%llums send/receive inactivity",_id.address().toString().c_str(),p->address().toString().c_str(),now - p->lastSend(),now - p->lastReceived());
- sendHELLO(RR,p->localAddress(),p->address(),now);
+ sendHELLO(p->localAddress(),p->address(),now);
p->sent(now);
- } else if (((now - p->lastSend()) >= ZT_NAT_KEEPALIVE_DELAY)&&(!p->reliable())) {
+ p->pinged(now);
+ } else if ( ((now - std::max(p->lastSend(),p->lastKeepalive())) >= ZT_NAT_KEEPALIVE_DELAY) && (!p->reliable()) ) {
//TRACE("NAT keepalive %s(%s) after %llums/%llums send/receive inactivity",_id.address().toString().c_str(),p->address().toString().c_str(),now - p->lastSend(),now - p->lastReceived());
_natKeepaliveBuf += (uint32_t)((now * 0x9e3779b1) >> 1); // tumble this around to send constantly varying (meaningless) payloads
RR->node->putPacket(p->localAddress(),p->address(),&_natKeepaliveBuf,sizeof(_natKeepaliveBuf));
- p->sent(now);
+ p->sentKeepalive(now);
} else {
//TRACE("no PING or NAT keepalive: addr==%s reliable==%d %llums/%llums send/receive inactivity",p->address().toString().c_str(),(int)p->reliable(),now - p->lastSend(),now - p->lastReceived());
}
@@ -264,7 +263,7 @@ bool Peer::doPingAndKeepalive(const RuntimeEnvironment *RR,uint64_t now,int inet
return false;
}
-void Peer::pushDirectPaths(const RuntimeEnvironment *RR,Path *path,uint64_t now,bool force)
+void Peer::pushDirectPaths(Path *path,uint64_t now,bool force)
{
#ifdef ZT_ENABLE_CLUSTER
// Cluster mode disables normal PUSH_DIRECT_PATHS in favor of cluster-based peer redirection
@@ -332,7 +331,7 @@ void Peer::pushDirectPaths(const RuntimeEnvironment *RR,Path *path,uint64_t now,
}
}
-bool Peer::resetWithinScope(const RuntimeEnvironment *RR,InetAddress::IpScope scope,uint64_t now)
+bool Peer::resetWithinScope(InetAddress::IpScope scope,uint64_t now)
{
Mutex::Lock _l(_lock);
unsigned int np = _numPaths;
@@ -340,7 +339,9 @@ bool Peer::resetWithinScope(const RuntimeEnvironment *RR,InetAddress::IpScope sc
unsigned int y = 0;
while (x < np) {
if (_paths[x].address().ipScope() == scope) {
- sendHELLO(RR,_paths[x].localAddress(),_paths[x].address(),now);
+ // Resetting a path means sending a HELLO and then forgetting it. If we
+ // get OK(HELLO) then it will be re-learned.
+ sendHELLO(_paths[x].localAddress(),_paths[x].address(),now);
} else {
_paths[y++] = _paths[x];
}
@@ -383,7 +384,7 @@ bool Peer::networkMembershipCertificatesAgree(uint64_t nwid,const CertificateOfM
return false;
}
-bool Peer::validateAndSetNetworkMembershipCertificate(const RuntimeEnvironment *RR,uint64_t nwid,const CertificateOfMembership &com)
+bool Peer::validateAndSetNetworkMembershipCertificate(uint64_t nwid,const CertificateOfMembership &com)
{
// Sanity checks
if ((!com)||(com.issuedTo() != _id.address()))
@@ -448,7 +449,7 @@ bool Peer::needsOurNetworkMembershipCertificate(uint64_t nwid,uint64_t now,bool
return ((now - tmp) >= (ZT_NETWORK_AUTOCONF_DELAY / 2));
}
-void Peer::clean(const RuntimeEnvironment *RR,uint64_t now)
+void Peer::clean(uint64_t now)
{
Mutex::Lock _l(_lock);
@@ -485,6 +486,54 @@ void Peer::clean(const RuntimeEnvironment *RR,uint64_t now)
}
}
+bool Peer::_checkPath(Path &p,const uint64_t now)
+{
+ // assumes _lock is locked
+
+ if (!p.active(now))
+ return false;
+
+ /* Dead path detection: if we have sent something to this peer and have not
+ * yet received a reply, double check this path. The majority of outbound
+ * packets including Ethernet frames do generate some kind of reply either
+ * immediately or at some point in the near future. This will occasionally
+ * (every NO_ANSWER_TIMEOUT ms) check paths unnecessarily if traffic that
+ * does not generate a response is being sent such as multicast announcements
+ * or frames belonging to unidirectional UDP protocols, but the cost is very
+ * tiny and the benefit in reliability is very large. This takes care of many
+ * failure modes including crap NATs that forget links and spurious changes
+ * to physical network topology that cannot be otherwise detected.
+ *
+ * Each time we do this we increment a probation counter in the path. This
+ * counter is reset on any packet receive over this path. If it reaches the
+ * MAX_PROBATION threshold the path is considred dead. */
+
+ if (
+ (p.lastSend() > p.lastReceived()) &&
+ ((p.lastSend() - p.lastReceived()) >= ZT_PEER_DEAD_PATH_DETECTION_NO_ANSWER_TIMEOUT) &&
+ ((now - p.lastPing()) >= ZT_PEER_DEAD_PATH_DETECTION_NO_ANSWER_TIMEOUT) &&
+ (!RR->topology->amRoot())
+ ) {
+ TRACE("%s(%s) does not seem to be answering in a timely manner, checking if dead (probation == %u)",_id.address().toString().c_str(),p.address().toString().c_str(),p.probation());
+
+ if ( (_vProto >= 5) && ( !((_vMajor == 1)&&(_vMinor == 1)&&(_vRevision == 0)) ) ) {
+ // 1.1.1 and newer nodes support ECHO, which is smaller -- but 1.1.0 has a bug so use HELLO there too
+ Packet outp(_id.address(),RR->identity.address(),Packet::VERB_ECHO);
+ outp.armor(_key,true);
+ p.send(RR,outp.data(),outp.size(),now);
+ p.pinged(now);
+ } else {
+ sendHELLO(p.localAddress(),p.address(),now);
+ p.sent(now);
+ p.pinged(now);
+ }
+
+ p.increaseProbation();
+ }
+
+ return true;
+}
+
Path *Peer::_getBestPath(const uint64_t now)
{
// assumes _lock is locked
@@ -492,7 +541,7 @@ Path *Peer::_getBestPath(const uint64_t now)
uint64_t bestPathScore = 0;
for(unsigned int i=0;i<_numPaths;++i) {
const uint64_t score = _paths[i].score();
- if ((score >= bestPathScore)&&(_paths[i].active(now))) {
+ if ((score >= bestPathScore)&&(_checkPath(_paths[i],now))) {
bestPathScore = score;
bestPath = &(_paths[i]);
}
@@ -507,7 +556,7 @@ Path *Peer::_getBestPath(const uint64_t now,int inetAddressFamily)
uint64_t bestPathScore = 0;
for(unsigned int i=0;i<_numPaths;++i) {
const uint64_t score = _paths[i].score();
- if (((int)_paths[i].address().ss_family == inetAddressFamily)&&(score >= bestPathScore)&&(_paths[i].active(now))) {
+ if (((int)_paths[i].address().ss_family == inetAddressFamily)&&(score >= bestPathScore)&&(_checkPath(_paths[i],now))) {
bestPathScore = score;
bestPath = &(_paths[i]);
}