From daaec84c6be11b57572ff57c97efd993385890fd Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Wed, 26 Mar 2014 15:35:15 -0700 Subject: Add TCP channel support for supernode list, make Peer pick the first path if all paths are equally dead. --- node/Peer.cpp | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) (limited to 'node/Peer.cpp') diff --git a/node/Peer.cpp b/node/Peer.cpp index b9b9b0c7..dcc8d4ea 100644 --- a/node/Peer.cpp +++ b/node/Peer.cpp @@ -91,6 +91,7 @@ void Peer::receive( } // Announce multicast LIKEs to peers to whom we have a direct link + // Lock can't be locked here or it'll recurse and deadlock. if ((now - _lastAnnouncedTo) >= ((ZT_MULTICAST_LIKE_EXPIRE / 2) - 1000)) { _lastAnnouncedTo = now; _r->sw->announceMulticastGroups(SharedPtr(this)); @@ -107,12 +108,14 @@ bool Peer::send(const RuntimeEnvironment *_r,const void *data,unsigned int len,u { Mutex::Lock _l(_lock); - if (_paths.empty()) + std::vector::iterator p(_paths.begin()); + if (p == _paths.end()) { + TRACE("send to %s failed: no paths available",_id.address().toString().c_str()); return false; - - uint64_t bestPathLastReceived = 0; - std::vector::iterator bestPath; - for(std::vector::iterator p(_paths.begin());p!=_paths.end();++p) { + } + uint64_t bestPathLastReceived = p->lastReceived(); + std::vector::iterator bestPath = p; + while (++p != _paths.end()) { uint64_t lr = p->lastReceived(); if (lr >= bestPathLastReceived) { bestPathLastReceived = lr; @@ -120,6 +123,8 @@ bool Peer::send(const RuntimeEnvironment *_r,const void *data,unsigned int len,u } } + TRACE("send to %s: using path: %s",_id.address().toString().c_str(),bestPath->toString().c_str()); + if (_r->sm->send(bestPath->address(),bestPath->tcp(),data,len)) { bestPath->sent(now); return true; @@ -147,19 +152,22 @@ bool Peer::sendPing(const RuntimeEnvironment *_r,uint64_t now,bool firstSinceRes SharedPtr self(this); Mutex::Lock _l(_lock); - bool allPingsUnanswered; + bool pingTcp; if (!firstSinceReset) { - allPingsUnanswered = true; + // Do not use TCP if one of our UDP endpoints has answered recently. + pingTcp = true; for(std::vector::iterator p(_paths.begin());p!=_paths.end();++p) { if (!p->pingUnanswered(now)) { - allPingsUnanswered = false; + pingTcp = false; break; } } - } else allPingsUnanswered = false; + } else pingTcp = false; + + TRACE("PING %s (pingTcp==%d)",_id.address().toString().c_str(),(int)pingTcp); for(std::vector::iterator p(_paths.begin());p!=_paths.end();++p) { - if ((allPingsUnanswered)||(!p->tcp())) { + if ((pingTcp)||(!p->tcp())) { if (_r->sw->sendHELLO(self,p->address(),p->tcp())) { p->sent(now); p->pinged(now); -- cgit v1.2.3 From 04169b51505bc41f70ad7b8797e8e4d5376bb9c7 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Wed, 26 Mar 2014 15:44:24 -0700 Subject: If I want it to pick the first, actually picking the first is helpful. --- node/Peer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'node/Peer.cpp') diff --git a/node/Peer.cpp b/node/Peer.cpp index dcc8d4ea..f8ff9ba6 100644 --- a/node/Peer.cpp +++ b/node/Peer.cpp @@ -117,7 +117,7 @@ bool Peer::send(const RuntimeEnvironment *_r,const void *data,unsigned int len,u std::vector::iterator bestPath = p; while (++p != _paths.end()) { uint64_t lr = p->lastReceived(); - if (lr >= bestPathLastReceived) { + if (lr > bestPathLastReceived) { bestPathLastReceived = lr; bestPath = p; } -- cgit v1.2.3 From e6b23059aca4947c8c4638c5d5e0abdba3b2b7b7 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Wed, 26 Mar 2014 16:44:58 -0700 Subject: Change the way TCP failover is invoked. --- node/Path.hpp | 13 ------------- node/Peer.cpp | 16 +++++++++++----- 2 files changed, 11 insertions(+), 18 deletions(-) (limited to 'node/Peer.cpp') diff --git a/node/Path.hpp b/node/Path.hpp index 960b08b3..09ed1f9e 100644 --- a/node/Path.hpp +++ b/node/Path.hpp @@ -87,19 +87,6 @@ public: return ((_addr)&&((_fixed)||((now - _lastReceived) < ZT_PEER_PATH_ACTIVITY_TIMEOUT))); } - /** - * @return True if it appears that a ping has gone unanswered - */ - inline bool pingUnanswered(uint64_t now) const - throw() - { - uint64_t lp = _lastPing; - uint64_t lr = _lastReceived; - if (lp) - return ((lr < lp)&&((lp - lr) > ZT_PING_UNANSWERED_AFTER)); - return false; - } - /** * @return Human-readable address and other information about this path, some computed as of current time */ diff --git a/node/Peer.cpp b/node/Peer.cpp index f8ff9ba6..0cd82909 100644 --- a/node/Peer.cpp +++ b/node/Peer.cpp @@ -150,18 +150,24 @@ bool Peer::sendPing(const RuntimeEnvironment *_r,uint64_t now,bool firstSinceRes { bool sent = false; SharedPtr self(this); + Mutex::Lock _l(_lock); + // NOTE: this will never ping a peer that has *only* TCP paths. Right + // now there's never such a thing as TCP is only for failover. + bool pingTcp; if (!firstSinceReset) { // Do not use TCP if one of our UDP endpoints has answered recently. - pingTcp = true; + uint64_t lastPing = 0; + uint64_t lastDirectReceive = 0; + for(std::vector::iterator p(_paths.begin());p!=_paths.end();++p) { - if (!p->pingUnanswered(now)) { - pingTcp = false; - break; - } + lastPing = std::max(lastPing,p->lastPing()); + lastDirectReceive = std::max(lastDirectReceive,p->lastReceived()); } + + pingTcp = ( (lastDirectReceive < lastPing) && ((lastPing - lastDirectReceive) >= ZT_PING_UNANSWERED_AFTER) ); } else pingTcp = false; TRACE("PING %s (pingTcp==%d)",_id.address().toString().c_str(),(int)pingTcp); -- cgit v1.2.3 From 2ac56fd120c3c9dd924fccc169beed2d51658ba9 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Wed, 26 Mar 2014 17:59:45 -0700 Subject: Fix TCP connection accumulation problem, still having issues with TCP tunneling. --- node/InetAddress.cpp | 32 +++++++++++++++++--------------- node/Node.cpp | 5 +++-- node/Peer.cpp | 2 ++ node/RuntimeEnvironment.hpp | 5 +++++ node/SocketManager.cpp | 10 +++++----- node/TcpSocket.cpp | 30 ++++++++++-------------------- node/Utils.cpp | 4 ++-- 7 files changed, 44 insertions(+), 44 deletions(-) (limited to 'node/Peer.cpp') diff --git a/node/InetAddress.cpp b/node/InetAddress.cpp index 349646cb..0a7dd88c 100644 --- a/node/InetAddress.cpp +++ b/node/InetAddress.cpp @@ -141,27 +141,29 @@ bool InetAddress::operator==(const InetAddress &a) const return (!memcmp(_sa.sin6.sin6_addr.s6_addr,a._sa.sin6.sin6_addr.s6_addr,sizeof(_sa.sin6.sin6_addr.s6_addr))); } return false; - } else if (!_sa.saddr.sa_family) - return (!a._sa.saddr.sa_family); - return (!memcmp(&_sa,&a._sa,sizeof(_sa))); + } else return (memcmp(&_sa,&a._sa,sizeof(_sa)) == 0); } bool InetAddress::operator<(const InetAddress &a) const throw() { - if (_sa.saddr.sa_family == AF_INET) { - if (a._sa.saddr.sa_family == AF_INET) - return ((ntohl(_sa.sin.sin_addr.s_addr < ntohl(a._sa.sin.sin_addr.s_addr)))||((_sa.sin.sin_addr.s_addr == a._sa.sin.sin_addr.s_addr)&&(ntohs(_sa.sin.sin_port) < ntohs(a._sa.sin.sin_port)))); - else if (a._sa.saddr.sa_family == AF_INET6) - return true; - } else if (_sa.saddr.sa_family == AF_INET6) { - if (a._sa.saddr.sa_family == AF_INET6) { - int cmp = memcmp(_sa.sin6.sin6_addr.s6_addr,a._sa.sin6.sin6_addr.s6_addr,16); - return ((cmp < 0)||((!cmp)&&(ntohs(_sa.sin6.sin6_port) < ntohs(a._sa.sin6.sin6_port)))); - } else if (a._sa.saddr.sa_family == AF_INET) - return false; + if (_sa.saddr.sa_family < a._sa.saddr.sa_family) + return true; + else if (_sa.saddr.sa_family == a._sa.saddr.sa_family) { + if (_sa.saddr.sa_family == AF_INET) { + unsigned long x = ntohl(_sa.sin.sin_addr.s_addr); + unsigned long y = ntohl(a._sa.sin.sin_addr.s_addr); + if (x == y) + return (ntohs(_sa.sin.sin_port) < ntohs(a._sa.sin.sin_port)); + else return (x < y); + } else if (_sa.saddr.sa_family == AF_INET6) { + int cmp = (int)memcmp(_sa.sin6.sin6_addr.s6_addr,a._sa.sin6.sin6_addr.s6_addr,16); + if (cmp == 0) + return (ntohs(_sa.sin6.sin6_port) < ntohs(a._sa.sin6.sin6_port)); + else return (cmp < 0); + } else return (memcmp(&_sa,&a._sa,sizeof(_sa)) < 0); } - return (_sa.saddr.sa_family < a._sa.saddr.sa_family); + return false; } } // namespace ZeroTier diff --git a/node/Node.cpp b/node/Node.cpp index 20a049bc..95e4bb1d 100644 --- a/node/Node.cpp +++ b/node/Node.cpp @@ -577,8 +577,9 @@ Node::ReasonForTermination Node::run() } // Ping supernodes separately for two reasons: (1) supernodes only ping each - // other, and (2) we still want to ping them first on resynchronize. - if ((resynchronize)||((now - lastSupernodePing) >= ZT_PEER_DIRECT_PING_DELAY)) { + // other, and (2) we still want to ping them first on resynchronize. Also ping + // more aggressively if nothing seems to be happening at all. + if ((resynchronize)||((now - lastSupernodePing) >= ZT_PEER_DIRECT_PING_DELAY)||((now - _r->timeOfLastPacketReceived) >= ZT_PING_UNANSWERED_AFTER)) { lastSupernodePing = now; std::vector< SharedPtr > sns(_r->topology->supernodePeers()); TRACE("pinging %d supernodes",(int)sns.size()); diff --git a/node/Peer.cpp b/node/Peer.cpp index 0cd82909..fa8ab3e8 100644 --- a/node/Peer.cpp +++ b/node/Peer.cpp @@ -69,6 +69,8 @@ void Peer::receive( Packet::Verb inReVerb, uint64_t now) { + *((const_cast(&(_r->timeOfLastPacketReceived)))) = now; + if (!hops) { // direct packet { Mutex::Lock _l(_lock); diff --git a/node/RuntimeEnvironment.hpp b/node/RuntimeEnvironment.hpp index ffa6cbdb..29693c55 100644 --- a/node/RuntimeEnvironment.hpp +++ b/node/RuntimeEnvironment.hpp @@ -65,6 +65,7 @@ public: RuntimeEnvironment() : shutdownInProgress(false), timeOfLastNetworkEnvironmentChange(0), + timeOfLastPacketReceived(0), log((Logger *)0), prng((CMWC4096 *)0), mc((Multicaster *)0), @@ -92,6 +93,10 @@ public: // Time network environment (e.g. fingerprint) last changed -- used to determine online-ness volatile uint64_t timeOfLastNetworkEnvironmentChange; + // Time last packet was received -- from anywhere. This is updated in Peer::receive() + // via an ugly const_cast<>. + volatile uint64_t timeOfLastPacketReceived; + /* * Order matters a bit here. These are constructed in this order * and then deleted in the opposite order on Node exit. The order ensures diff --git a/node/SocketManager.cpp b/node/SocketManager.cpp index 2f6eb4fb..893e17d1 100644 --- a/node/SocketManager.cpp +++ b/node/SocketManager.cpp @@ -395,17 +395,17 @@ bool SocketManager::send(const InetAddress &to,bool tcp,const void *msg,unsigned if (!ts->send(to,msg,msglen)) return false; + { + Mutex::Lock _l(_tcpSockets_m); + _tcpSockets[to] = ts; + } + _fdSetLock.lock(); FD_SET(s,&_readfds); if (connecting) FD_SET(s,&_writefds); _fdSetLock.unlock(); - { - Mutex::Lock _l(_tcpSockets_m); - _tcpSockets[to] = ts; - } - return true; } else if (to.isV4()) { if (_udpV4Socket) diff --git a/node/TcpSocket.cpp b/node/TcpSocket.cpp index a422dec6..b56775d8 100644 --- a/node/TcpSocket.cpp +++ b/node/TcpSocket.cpp @@ -164,32 +164,22 @@ bool TcpSocket::notifyAvailableForWrite(const SharedPtr &self,SocketMana if (_outptr) { int n = (int)::send(_sock,(const char *)_outbuf,_outptr,0); - if (n < 0) { + if (n <= 0) { switch(errno) { -#ifdef EBADF - case EBADF: +#ifdef EAGAIN + case EAGAIN: #endif -#ifdef EINVAL - case EINVAL: +#if defined(EWOULDBLOCK) && ( !defined(EAGAIN) || (EWOULDBLOCK != EAGAIN) ) + case EWOULDBLOCK: #endif -#ifdef ENOTSOCK - case ENOTSOCK: +#ifdef EINTR + case EINTR: #endif -#ifdef ECONNRESET - case ECONNRESET: -#endif -#ifdef EPIPE - case EPIPE: -#endif -#ifdef ENETDOWN - case ENETDOWN: -#endif - return false; - default: break; + default: + return false; } - } else if (n > 0) - memmove(_outbuf,_outbuf + (unsigned int)n,_outptr -= (unsigned int)n); + } else memmove(_outbuf,_outbuf + (unsigned int)n,_outptr -= (unsigned int)n); } if (!_outptr) diff --git a/node/Utils.cpp b/node/Utils.cpp index fe153af2..95b66337 100644 --- a/node/Utils.cpp +++ b/node/Utils.cpp @@ -207,11 +207,11 @@ void Utils::getSecureRandom(void *buf,unsigned int bytes) { int fd = ::open("/dev/urandom",O_RDONLY); if (fd < 0) { - fprintf(stderr,"FATAL ERROR: unable to open /dev/urandom%s",ZT_EOL_S); + fprintf(stderr,"FATAL ERROR: unable to open /dev/urandom (%d)"ZT_EOL_S,errno); exit(-1); } if ((int)::read(fd,randbuf,sizeof(randbuf)) != (int)sizeof(randbuf)) { - fprintf(stderr,"FATAL ERROR: unable to read from /dev/urandom%s",ZT_EOL_S); + fprintf(stderr,"FATAL ERROR: unable to read from /dev/urandom"ZT_EOL_S); exit(-1); } ::close(fd); -- cgit v1.2.3