summaryrefslogtreecommitdiff
path: root/node
diff options
context:
space:
mode:
Diffstat (limited to 'node')
-rw-r--r--node/Constants.hpp26
-rw-r--r--node/IncomingPacket.cpp5
-rw-r--r--node/Peer.cpp2
-rw-r--r--node/Peer.hpp31
4 files changed, 56 insertions, 8 deletions
diff --git a/node/Constants.hpp b/node/Constants.hpp
index 4b06db44..53cc64c7 100644
--- a/node/Constants.hpp
+++ b/node/Constants.hpp
@@ -320,11 +320,6 @@
#define ZT_MIN_PATH_CONFIRMATION_INTERVAL 1000
/**
- * Interval between direct path pushes in milliseconds
- */
-#define ZT_DIRECT_PATH_PUSH_INTERVAL 120000
-
-/**
* How long (max) to remember network certificates of membership?
*
* This only applies to networks we don't belong to.
@@ -348,9 +343,28 @@
#define ZT_MAX_BRIDGE_SPAM 16
/**
+ * Interval between direct path pushes in milliseconds
+ */
+#define ZT_DIRECT_PATH_PUSH_INTERVAL 120000
+
+/**
* Maximum number of endpoints to contact per address type (to limit pushes like GitHub issue #235)
*/
-#define ZT_PUSH_DIRECT_PATHS_MAX_ENDPOINTS_PER_TYPE 4
+#define ZT_PUSH_DIRECT_PATHS_MAX_ENDPOINTS_PER_TYPE 5
+
+/**
+ * Time horizon for push direct paths cutoff
+ */
+#define ZT_PUSH_DIRECT_PATHS_CUTOFF_TIME 60000
+
+/**
+ * Maximum number of direct path pushes within cutoff time
+ *
+ * This limits response to PUSH_DIRECT_PATHS to CUTOFF_LIMIT responses
+ * per CUTOFF_TIME milliseconds per peer to prevent this from being
+ * useful for DOS amplification attacks.
+ */
+#define ZT_PUSH_DIRECT_PATHS_CUTOFF_LIMIT 5
/**
* A test pseudo-network-ID that can be joined
diff --git a/node/IncomingPacket.cpp b/node/IncomingPacket.cpp
index b1a9af3b..e985b34c 100644
--- a/node/IncomingPacket.cpp
+++ b/node/IncomingPacket.cpp
@@ -901,6 +901,11 @@ bool IncomingPacket::_doPUSH_DIRECT_PATHS(const RuntimeEnvironment *RR,const Sha
{
try {
const uint64_t now = RR->node->now();
+ if (!peer->shouldRespondToDirectPathPush(now)) {
+ TRACE("dropped PUSH_DIRECT_PATHS from %s(%s): circuit breaker tripped",source().toString().c_str(),_remoteAddress.toString().c_str());
+ return true;
+ }
+
const Path *currentBest = peer->getBestPath(now);
unsigned int count = at<uint16_t>(ZT_PACKET_IDX_PAYLOAD);
diff --git a/node/Peer.cpp b/node/Peer.cpp
index 99eb32c7..976c7c44 100644
--- a/node/Peer.cpp
+++ b/node/Peer.cpp
@@ -55,6 +55,7 @@ Peer::Peer(const Identity &myIdentity,const Identity &peerIdentity)
_lastAnnouncedTo(0),
_lastPathConfirmationSent(0),
_lastDirectPathPushSent(0),
+ _lastDirectPathPushReceive(0),
_lastPathSort(0),
_vProto(0),
_vMajor(0),
@@ -63,6 +64,7 @@ Peer::Peer(const Identity &myIdentity,const Identity &peerIdentity)
_id(peerIdentity),
_numPaths(0),
_latency(0),
+ _directPathPushCutoffCount(0),
_networkComs(4),
_lastPushedComs(4)
{
diff --git a/node/Peer.hpp b/node/Peer.hpp
index 69343f20..d9ef5fcb 100644
--- a/node/Peer.hpp
+++ b/node/Peer.hpp
@@ -432,6 +432,27 @@ public:
}
/**
+ * Update direct path push stats and return true if we should respond
+ *
+ * This is a circuit breaker to make VERB_PUSH_DIRECT_PATHS not particularly
+ * useful as a DDOS amplification attack vector. Otherwise a malicious peer
+ * could send loads of these and cause others to bombard arbitrary IPs with
+ * traffic.
+ *
+ * @param now Current time
+ * @return True if we should respond
+ */
+ inline bool shouldRespondToDirectPathPush(const uint64_t now)
+ {
+ Mutex::Lock _l(_lock);
+ if ((now - _lastDirectPathPushReceive) <= ZT_PUSH_DIRECT_PATHS_CUTOFF_TIME)
+ ++_directPathPushCutoffCount;
+ else _directPathPushCutoffCount = 0;
+ _lastDirectPathPushReceive = now;
+ return (_directPathPushCutoffCount >= ZT_PUSH_DIRECT_PATHS_CUTOFF_LIMIT);
+ }
+
+ /**
* Find a common set of addresses by which two peers can link, if any
*
* @param a Peer A
@@ -459,7 +480,7 @@ public:
const unsigned int recSizePos = b.size();
b.addSize(4); // space for uint32_t field length
- b.append((uint16_t)0); // version of serialized Peer data
+ b.append((uint16_t)1); // version of serialized Peer data
_id.serialize(b,false);
@@ -470,12 +491,14 @@ public:
b.append((uint64_t)_lastAnnouncedTo);
b.append((uint64_t)_lastPathConfirmationSent);
b.append((uint64_t)_lastDirectPathPushSent);
+ b.append((uint64_t)_lastDirectPathPushReceive);
b.append((uint64_t)_lastPathSort);
b.append((uint16_t)_vProto);
b.append((uint16_t)_vMajor);
b.append((uint16_t)_vMinor);
b.append((uint16_t)_vRevision);
b.append((uint32_t)_latency);
+ b.append((uint16_t)_directPathPushCutoffCount);
b.append((uint16_t)_numPaths);
for(unsigned int i=0;i<_numPaths;++i)
@@ -521,7 +544,7 @@ public:
const unsigned int recSize = b.template at<uint32_t>(p); p += 4;
if ((p + recSize) > b.size())
return SharedPtr<Peer>(); // size invalid
- if (b.template at<uint16_t>(p) != 0)
+ if (b.template at<uint16_t>(p) != 1)
return SharedPtr<Peer>(); // version mismatch
p += 2;
@@ -539,12 +562,14 @@ public:
np->_lastAnnouncedTo = b.template at<uint64_t>(p); p += 8;
np->_lastPathConfirmationSent = b.template at<uint64_t>(p); p += 8;
np->_lastDirectPathPushSent = b.template at<uint64_t>(p); p += 8;
+ np->_lastDirectPathPushReceive = b.template at<uint64_t>(p); p += 8;
np->_lastPathSort = b.template at<uint64_t>(p); p += 8;
np->_vProto = b.template at<uint16_t>(p); p += 2;
np->_vMajor = b.template at<uint16_t>(p); p += 2;
np->_vMinor = b.template at<uint16_t>(p); p += 2;
np->_vRevision = b.template at<uint16_t>(p); p += 2;
np->_latency = b.template at<uint32_t>(p); p += 4;
+ np->_directPathPushCutoffCount = b.template at<uint16_t>(p); p += 2;
const unsigned int numPaths = b.template at<uint16_t>(p); p += 2;
for(unsigned int i=0;i<numPaths;++i) {
@@ -588,6 +613,7 @@ private:
uint64_t _lastAnnouncedTo;
uint64_t _lastPathConfirmationSent;
uint64_t _lastDirectPathPushSent;
+ uint64_t _lastDirectPathPushReceive;
uint64_t _lastPathSort;
uint16_t _vProto;
uint16_t _vMajor;
@@ -597,6 +623,7 @@ private:
Path _paths[ZT_MAX_PEER_NETWORK_PATHS];
unsigned int _numPaths;
unsigned int _latency;
+ unsigned int _directPathPushCutoffCount;
struct _NetworkCom
{