Add a circuit breaker for VERB_PUSH_DIRECT_PATHS.

This commit is contained in:
Adam Ierymenko 2015-10-27 18:18:26 -07:00
parent 88b100e5d0
commit cdc99bfee1
4 changed files with 56 additions and 8 deletions

View File

@ -319,11 +319,6 @@
*/ */
#define ZT_MIN_PATH_CONFIRMATION_INTERVAL 1000 #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? * How long (max) to remember network certificates of membership?
* *
@ -347,10 +342,29 @@
*/ */
#define ZT_MAX_BRIDGE_SPAM 16 #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) * 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 * A test pseudo-network-ID that can be joined

View File

@ -901,6 +901,11 @@ bool IncomingPacket::_doPUSH_DIRECT_PATHS(const RuntimeEnvironment *RR,const Sha
{ {
try { try {
const uint64_t now = RR->node->now(); 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); const Path *currentBest = peer->getBestPath(now);
unsigned int count = at<uint16_t>(ZT_PACKET_IDX_PAYLOAD); unsigned int count = at<uint16_t>(ZT_PACKET_IDX_PAYLOAD);

View File

@ -55,6 +55,7 @@ Peer::Peer(const Identity &myIdentity,const Identity &peerIdentity)
_lastAnnouncedTo(0), _lastAnnouncedTo(0),
_lastPathConfirmationSent(0), _lastPathConfirmationSent(0),
_lastDirectPathPushSent(0), _lastDirectPathPushSent(0),
_lastDirectPathPushReceive(0),
_lastPathSort(0), _lastPathSort(0),
_vProto(0), _vProto(0),
_vMajor(0), _vMajor(0),
@ -63,6 +64,7 @@ Peer::Peer(const Identity &myIdentity,const Identity &peerIdentity)
_id(peerIdentity), _id(peerIdentity),
_numPaths(0), _numPaths(0),
_latency(0), _latency(0),
_directPathPushCutoffCount(0),
_networkComs(4), _networkComs(4),
_lastPushedComs(4) _lastPushedComs(4)
{ {

View File

@ -431,6 +431,27 @@ public:
_numPaths = y; _numPaths = y;
} }
/**
* 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 * Find a common set of addresses by which two peers can link, if any
* *
@ -459,7 +480,7 @@ public:
const unsigned int recSizePos = b.size(); const unsigned int recSizePos = b.size();
b.addSize(4); // space for uint32_t field length 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); _id.serialize(b,false);
@ -470,12 +491,14 @@ public:
b.append((uint64_t)_lastAnnouncedTo); b.append((uint64_t)_lastAnnouncedTo);
b.append((uint64_t)_lastPathConfirmationSent); b.append((uint64_t)_lastPathConfirmationSent);
b.append((uint64_t)_lastDirectPathPushSent); b.append((uint64_t)_lastDirectPathPushSent);
b.append((uint64_t)_lastDirectPathPushReceive);
b.append((uint64_t)_lastPathSort); b.append((uint64_t)_lastPathSort);
b.append((uint16_t)_vProto); b.append((uint16_t)_vProto);
b.append((uint16_t)_vMajor); b.append((uint16_t)_vMajor);
b.append((uint16_t)_vMinor); b.append((uint16_t)_vMinor);
b.append((uint16_t)_vRevision); b.append((uint16_t)_vRevision);
b.append((uint32_t)_latency); b.append((uint32_t)_latency);
b.append((uint16_t)_directPathPushCutoffCount);
b.append((uint16_t)_numPaths); b.append((uint16_t)_numPaths);
for(unsigned int i=0;i<_numPaths;++i) 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; const unsigned int recSize = b.template at<uint32_t>(p); p += 4;
if ((p + recSize) > b.size()) if ((p + recSize) > b.size())
return SharedPtr<Peer>(); // size invalid 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 return SharedPtr<Peer>(); // version mismatch
p += 2; p += 2;
@ -539,12 +562,14 @@ public:
np->_lastAnnouncedTo = b.template at<uint64_t>(p); p += 8; np->_lastAnnouncedTo = b.template at<uint64_t>(p); p += 8;
np->_lastPathConfirmationSent = 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->_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->_lastPathSort = b.template at<uint64_t>(p); p += 8;
np->_vProto = b.template at<uint16_t>(p); p += 2; np->_vProto = b.template at<uint16_t>(p); p += 2;
np->_vMajor = 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->_vMinor = b.template at<uint16_t>(p); p += 2;
np->_vRevision = 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->_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; const unsigned int numPaths = b.template at<uint16_t>(p); p += 2;
for(unsigned int i=0;i<numPaths;++i) { for(unsigned int i=0;i<numPaths;++i) {
@ -588,6 +613,7 @@ private:
uint64_t _lastAnnouncedTo; uint64_t _lastAnnouncedTo;
uint64_t _lastPathConfirmationSent; uint64_t _lastPathConfirmationSent;
uint64_t _lastDirectPathPushSent; uint64_t _lastDirectPathPushSent;
uint64_t _lastDirectPathPushReceive;
uint64_t _lastPathSort; uint64_t _lastPathSort;
uint16_t _vProto; uint16_t _vProto;
uint16_t _vMajor; uint16_t _vMajor;
@ -597,6 +623,7 @@ private:
Path _paths[ZT_MAX_PEER_NETWORK_PATHS]; Path _paths[ZT_MAX_PEER_NETWORK_PATHS];
unsigned int _numPaths; unsigned int _numPaths;
unsigned int _latency; unsigned int _latency;
unsigned int _directPathPushCutoffCount;
struct _NetworkCom struct _NetworkCom
{ {