summaryrefslogtreecommitdiff
path: root/node
diff options
context:
space:
mode:
authorAdam Ierymenko <adam.ierymenko@gmail.com>2014-04-09 17:08:35 -0700
committerAdam Ierymenko <adam.ierymenko@gmail.com>2014-04-09 17:08:35 -0700
commit8fb442d81ae54f1c636c1a8c25528fb17ac04ed6 (patch)
tree84a796331ce0634fa7e65da15a7d3681e81377e9 /node
parent73153b89b4270a6cc99aea47fb4019d660116e84 (diff)
downloadinfinitytier-8fb442d81ae54f1c636c1a8c25528fb17ac04ed6.tar.gz
infinitytier-8fb442d81ae54f1c636c1a8c25528fb17ac04ed6.zip
Yet more cleanup to TCP logic, this time adding a master switch and adding UDP preference in send().
Diffstat (limited to 'node')
-rw-r--r--node/Constants.hpp2
-rw-r--r--node/Node.cpp22
-rw-r--r--node/Peer.cpp102
-rw-r--r--node/Peer.hpp20
-rw-r--r--node/RuntimeEnvironment.hpp4
-rw-r--r--node/Topology.hpp36
6 files changed, 110 insertions, 76 deletions
diff --git a/node/Constants.hpp b/node/Constants.hpp
index 524a8355..20da46c1 100644
--- a/node/Constants.hpp
+++ b/node/Constants.hpp
@@ -313,7 +313,7 @@ error_no_byte_order_defined;
#define ZT_PEER_PATH_ACTIVITY_TIMEOUT ((ZT_PEER_DIRECT_PING_DELAY * 2) + ZT_PING_CHECK_DELAY)
/**
- * Close TCP tunnels if unused for this long
+ * Close TCP tunnels if unused for this long (used in SocketManager)
*/
#define ZT_TCP_TUNNEL_ACTIVITY_TIMEOUT ZT_PEER_PATH_ACTIVITY_TIMEOUT
diff --git a/node/Node.cpp b/node/Node.cpp
index 2ae61136..26c1d90b 100644
--- a/node/Node.cpp
+++ b/node/Node.cpp
@@ -591,13 +591,29 @@ Node::ReasonForTermination Node::run()
LOG("resynchronize forced by user, syncing with network");
}
- if (resynchronize)
+ if (resynchronize) {
+ _r->tcpTunnelingEnabled = false; // turn off TCP tunneling master switch at first
_r->timeOfLastResynchronize = now;
+ }
- /* Ping supernodes separately, and do so more aggressively if we haven't
- * heard anything from anyone since our last resynchronize / startup. */
+ /* Supernodes are pinged separately and more aggressively. The
+ * ZT_STARTUP_AGGRO parameter sets a limit on how rapidly they are
+ * tried, while PingSupernodesThatNeedPing contains the logic for
+ * determining if they need PING. */
if ((now - lastSupernodePingCheck) >= ZT_STARTUP_AGGRO) {
lastSupernodePingCheck = now;
+
+ uint64_t lastReceiveFromAnySupernode = 0; // function object result paramter
+ _r->topology->eachSupernodePeer(Topology::FindMostRecentDirectReceiveTimestamp(lastReceiveFromAnySupernode));
+
+ // Turn on TCP tunneling master switch if we haven't heard anything since before
+ // the last resynchronize and we've been trying long enough.
+ uint64_t tlr = _r->timeOfLastResynchronize;
+ if ((lastReceiveFromAnySupernode < tlr)&&((now - tlr) >= ZT_TCP_TUNNEL_FAILOVER_TIMEOUT)) {
+ TRACE("network still unreachable after %u ms, TCP TUNNELING ENABLED",(unsigned int)ZT_TCP_TUNNEL_FAILOVER_TIMEOUT);
+ _r->tcpTunnelingEnabled = true;
+ }
+
_r->topology->eachSupernodePeer(Topology::PingSupernodesThatNeedPing(_r,now));
}
diff --git a/node/Peer.cpp b/node/Peer.cpp
index 502a0002..24e3c869 100644
--- a/node/Peer.cpp
+++ b/node/Peer.cpp
@@ -120,35 +120,44 @@ void Peer::receive(
bool Peer::send(const RuntimeEnvironment *_r,const void *data,unsigned int len,uint64_t now)
{
Mutex::Lock _l(_lock);
- bool useTcpOut = _isTcpFailoverTime(_r,now);
- std::vector<Path>::iterator p(_paths.begin());
- if (!useTcpOut) {
- // If we don't want to initiate TCP, seek past TCP paths if they are at the front
- // to find the first UDP path as our default.
- while ((p != _paths.end())&&(p->type() == Path::PATH_TYPE_TCP_OUT))
- ++p;
- }
- if (p == _paths.end())
- return false;
-
- // Treat first path as default and look for a better one based on time of
- // last packet received.
- std::vector<Path>::iterator bestPath = p;
- uint64_t bestPathLastReceived = p->lastReceived();
- while (++p != _paths.end()) {
+ Path *bestNormalPath = (Path *)0;
+ Path *bestTcpOutPath = (Path *)0;
+ uint64_t bestNormalPathLastReceived = 0;
+ uint64_t bestTcpOutPathLastReceived = 0;
+ for(std::vector<Path>::iterator p(_paths.begin());p!=_paths.end();++p) {
uint64_t lr = p->lastReceived();
- if ( (lr > bestPathLastReceived) && ((useTcpOut)||(p->type() != Path::PATH_TYPE_TCP_OUT)) ) {
- bestPathLastReceived = lr;
- bestPath = p;
+ if (p->type() == Path::PATH_TYPE_TCP_OUT) { // TCP_OUT paths initiate TCP connections
+ if (lr >= bestTcpOutPathLastReceived) {
+ bestTcpOutPathLastReceived = lr;
+ bestTcpOutPath = &(*p);
+ }
+ } else { // paths other than TCP_OUT are considered "normal"
+ if (lr >= bestNormalPathLastReceived) {
+ bestNormalPathLastReceived = lr;
+ bestNormalPath = &(*p);
+ }
}
}
+ Path *bestPath = (Path *)0;
+ if (!_r->tcpTunnelingEnabled) { // TCP tunneling master switch is off, use normal path
+ bestPath = bestNormalPath;
+ } else if (bestNormalPath) { // we have a normal path, so use if it looks active
+ if ((bestNormalPathLastReceived > _r->timeOfLastResynchronize)&&((now - bestNormalPathLastReceived) < ZT_PEER_PATH_ACTIVITY_TIMEOUT))
+ bestPath = bestNormalPath;
+ else bestPath = bestTcpOutPath;
+ } else { // no normal path available
+ bestPath = bestTcpOutPath;
+ }
+
+ if (!bestPath)
+ return false;
+
if (_r->sm->send(bestPath->address(),bestPath->tcp(),bestPath->type() == Path::PATH_TYPE_TCP_OUT,data,len)) {
bestPath->sent(now);
return true;
}
-
return false;
}
@@ -170,7 +179,18 @@ bool Peer::sendPing(const RuntimeEnvironment *_r,uint64_t now)
bool sent = false;
SharedPtr<Peer> self(this);
Mutex::Lock _l(_lock);
- bool useTcpOut = _isTcpFailoverTime(_r,now);
+
+ uint64_t lastUdpPingSent = 0;
+ uint64_t lastUdpReceive = 0;
+ bool haveUdp = false;
+ for(std::vector<Path>::const_iterator p(_paths.begin());p!=_paths.end();++p) {
+ if (p->type() == Path::PATH_TYPE_UDP) {
+ lastUdpPingSent = std::max(lastUdpPingSent,p->lastPing());
+ lastUdpReceive = std::max(lastUdpReceive,p->lastReceived());
+ haveUdp = true;
+ }
+ }
+ bool useTcpOut = ( (!haveUdp) || ( (_r->tcpTunnelingEnabled) && (lastUdpPingSent > lastUdpReceive) && ((now - lastUdpReceive) >= ZT_TCP_TUNNEL_FAILOVER_TIMEOUT) ) );
TRACE("PING %s (useTcpOut==%d)",_id.address().toString().c_str(),(int)useTcpOut);
@@ -199,46 +219,6 @@ void Peer::clean(uint64_t now)
_paths.resize(o);
}
-bool Peer::_isTcpFailoverTime(const RuntimeEnvironment *_r,uint64_t now) const
- throw()
-{
- // assumes _lock is locked
- uint64_t lastResync = _r->timeOfLastResynchronize;
- if ((now - lastResync) >= ZT_TCP_TUNNEL_FAILOVER_TIMEOUT) {
- if ((now - _r->timeOfLastPacketReceived) >= ZT_TCP_TUNNEL_FAILOVER_TIMEOUT)
- return true;
-
- uint64_t lastUdpPingSent = 0;
- uint64_t lastUdpReceive = 0;
- bool haveUdp = false;
-
- for(std::vector<Path>::const_iterator p(_paths.begin());p!=_paths.end();++p) {
- if (p->type() == Path::PATH_TYPE_UDP) {
- lastUdpPingSent = std::max(lastUdpPingSent,p->lastPing());
- lastUdpReceive = std::max(lastUdpReceive,p->lastReceived());
- haveUdp = true;
- }
- }
-
- return ( (!haveUdp) || ( (lastUdpPingSent > lastResync) && ((now - lastUdpReceive) >= ZT_TCP_TUNNEL_FAILOVER_TIMEOUT) ) );
- }
- return false;
-}
-
-bool Peer::pingUnanswered(const RuntimeEnvironment *_r,uint64_t now)
-{
- uint64_t lp = 0;
- uint64_t lr = 0;
- {
- Mutex::Lock _l(_lock);
- for(std::vector<Path>::const_iterator p(_paths.begin());p!=_paths.end();++p) {
- lp = std::max(p->lastPing(),lp);
- lr = std::max(p->lastReceived(),lr);
- }
- }
- return ( (lp > _r->timeOfLastResynchronize) && ((lr < lp)&&((lp - lr) >= ZT_PING_UNANSWERED_AFTER)) );
-}
-
void Peer::getBestActiveUdpPathAddresses(uint64_t now,InetAddress &v4,InetAddress &v6) const
{
uint64_t bestV4 = 0,bestV6 = 0;
diff --git a/node/Peer.hpp b/node/Peer.hpp
index 40261a29..9baafb62 100644
--- a/node/Peer.hpp
+++ b/node/Peer.hpp
@@ -218,11 +218,20 @@ public:
}
/**
- * @param _r Runtime environment
- * @param now Current time
- * @return True if the last ping is unanswered
+ * Get max timestamp of last ping and max timestamp of last receive in a single pass
+ *
+ * @param lp Last ping result parameter (init to 0 before calling)
+ * @param lr Last receive result parameter (init to 0 before calling)
*/
- bool pingUnanswered(const RuntimeEnvironment *_r,uint64_t now);
+ inline void lastPingAndDirectReceive(uint64_t &lp,uint64_t &lr)
+ throw()
+ {
+ Mutex::Lock _l(_lock);
+ for(std::vector<Path>::const_iterator p(_paths.begin());p!=_paths.end();++p) {
+ lp = std::max(lp,p->lastPing());
+ lr = std::max(lr,p->lastReceived());
+ }
+ }
/**
* @return Time of most recent unicast frame received
@@ -449,9 +458,6 @@ public:
}
private:
- bool _isTcpFailoverTime(const RuntimeEnvironment *_r,uint64_t now) const
- throw();
-
unsigned char _key[ZT_PEER_SECRET_KEY_LENGTH];
Identity _id;
diff --git a/node/RuntimeEnvironment.hpp b/node/RuntimeEnvironment.hpp
index 228679a1..e8518b97 100644
--- a/node/RuntimeEnvironment.hpp
+++ b/node/RuntimeEnvironment.hpp
@@ -64,6 +64,7 @@ class RuntimeEnvironment
public:
RuntimeEnvironment() :
shutdownInProgress(false),
+ tcpTunnelingEnabled(false),
timeOfLastResynchronize(0),
timeOfLastPacketReceived(0),
log((Logger *)0),
@@ -90,6 +91,9 @@ public:
// Indicates that we are shutting down -- this is hacky, want to factor out
volatile bool shutdownInProgress;
+ // Are we in outgoing TCP failover mode?
+ volatile bool tcpTunnelingEnabled;
+
// Time network environment (e.g. fingerprint) last changed -- used to determine online-ness
volatile uint64_t timeOfLastResynchronize;
diff --git a/node/Topology.hpp b/node/Topology.hpp
index 024b5ca8..74db8b50 100644
--- a/node/Topology.hpp
+++ b/node/Topology.hpp
@@ -216,7 +216,13 @@ public:
};
/**
- * Pings all peers that need a ping sent, excluding supernodes (which are pinged separately)
+ * Pings all peers that need a ping sent, excluding supernodes
+ *
+ * Ordinary peers are pinged if we haven't heard from them recently. Receive
+ * time rather than send time as OK is returned on success and we want to
+ * keep trying if a packet is lost. Ordinary peers are subject to a frame
+ * inactivity timeout. We give up if we haven't actually transferred any
+ * data to them recently, and eventually Topology purges them from memory.
*/
class PingPeersThatNeedPing
{
@@ -235,7 +241,7 @@ public:
* than time of last send in order to only count full round trips. */
if ( (!_supernodeAddresses.count(p->address())) &&
((_now - p->lastFrame()) < ZT_PEER_PATH_ACTIVITY_TIMEOUT) &&
- ((_now - p->lastDirectReceive()) > ZT_PEER_DIRECT_PING_DELAY) ) {
+ ((_now - p->lastDirectReceive()) >= ZT_PEER_DIRECT_PING_DELAY) ) {
p->sendPing(_r,_now);
}
}
@@ -247,7 +253,13 @@ public:
};
/**
- * Ping peers that need ping according to supernode rules (slightly more aggressive)
+ * Ping peers that need ping according to supernode rules
+ *
+ * Supernodes ping aggressively if a ping is unanswered and they are not
+ * subject to the activity timeout. In other words: we assume they are
+ * always there and always try to reach them.
+ *
+ * The ultimate rate limit for this is controlled up in the Node main loop.
*/
class PingSupernodesThatNeedPing
{
@@ -261,7 +273,11 @@ public:
/* For supernodes we always ping even if no frames have been seen, and
* we ping aggressively if pings are unanswered. The limit to this
* frequency is set in the main loop to no more than ZT_STARTUP_AGGRO. */
- if ( (p->pingUnanswered(_r,_now)) || ((_now - p->lastDirectReceive()) > ZT_PEER_DIRECT_PING_DELAY) || (p->lastDirectReceive() < _r->timeOfLastResynchronize) )
+
+ uint64_t lp = 0;
+ uint64_t lr = 0;
+ p->lastPingAndDirectReceive(lp,lr);
+ if ( (lr < _r->timeOfLastResynchronize) || ((lr < lp)&&((lp - lr) >= ZT_PING_UNANSWERED_AFTER)) || ((_now - lr) >= ZT_PEER_DIRECT_PING_DELAY) )
p->sendPing(_r,_now);
}
@@ -271,6 +287,18 @@ public:
};
/**
+ * Computes most recent timestamp of direct packet receive over a list of peers
+ */
+ class FindMostRecentDirectReceiveTimestamp
+ {
+ public:
+ FindMostRecentDirectReceiveTimestamp(uint64_t &ts) throw() : _ts(ts) {}
+ inline void operator()(Topology &t,const SharedPtr<Peer> &p) throw() { _ts = std::max(p->lastDirectReceive(),_ts); }
+ private:
+ uint64_t &_ts;
+ };
+
+ /**
* Function object to forget direct links to active peers and then ping them indirectly
*/
class ResetActivePeers