diff options
Diffstat (limited to 'node')
-rw-r--r-- | node/Constants.hpp | 26 | ||||
-rw-r--r-- | node/IncomingPacket.cpp | 5 | ||||
-rw-r--r-- | node/Peer.cpp | 2 | ||||
-rw-r--r-- | node/Peer.hpp | 31 |
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 { |