(1) Public networks now get COMs even though they do not gate with them since they will need them to push auth for multicast stuff, (2) added a bunch of rate limit circuit breakers for anti-DOS, (3) cleanup.

This commit is contained in:
Adam Ierymenko 2016-09-09 11:36:10 -07:00
parent ef87069957
commit ab9afbc749
13 changed files with 395 additions and 229 deletions

View File

@ -924,13 +924,11 @@ NetworkController::ResultCode EmbeddedNetworkController::doNetworkConfigRequest(
} }
} }
if (_jB(network["private"],true)) { CertificateOfMembership com(now,credentialtmd,nwid,identity.address());
CertificateOfMembership com(now,credentialtmd,nwid,identity.address()); if (com.sign(signingId)) {
if (com.sign(signingId)) { nc.com = com;
nc.com = com; } else {
} else { return NETCONF_QUERY_INTERNAL_SERVER_ERROR;
return NETCONF_QUERY_INTERNAL_SERVER_ERROR;
}
} }
_writeJson(memberJP,member); _writeJson(memberJP,member);

View File

@ -236,6 +236,11 @@
*/ */
#define ZT_MULTICAST_EXPLICIT_GATHER_DELAY (ZT_MULTICAST_LIKE_EXPIRE / 10) #define ZT_MULTICAST_EXPLICIT_GATHER_DELAY (ZT_MULTICAST_LIKE_EXPIRE / 10)
/**
* Expiration for credentials presented for MULTICAST_LIKE or MULTICAST_GATHER (for non-network-members)
*/
#define ZT_MULTICAST_CREDENTIAL_EXPIRATON ZT_MULTICAST_LIKE_EXPIRE
/** /**
* Timeout for outgoing multicasts * Timeout for outgoing multicasts
* *
@ -263,6 +268,11 @@
*/ */
#define ZT_PATH_MIN_REACTIVATE_INTERVAL 2500 #define ZT_PATH_MIN_REACTIVATE_INTERVAL 2500
/**
* Do not accept HELLOs over a given path more often than this
*/
#define ZT_PATH_HELLO_RATE_LIMIT 1000
/** /**
* Delay between full-fledge pings of directly connected peers * Delay between full-fledge pings of directly connected peers
*/ */
@ -283,6 +293,11 @@
*/ */
#define ZT_PEER_ACTIVITY_TIMEOUT 500000 #define ZT_PEER_ACTIVITY_TIMEOUT 500000
/**
* General rate limit timeout for multiple packet types (HELLO, etc.)
*/
#define ZT_PEER_GENERAL_INBOUND_RATE_LIMIT 1000
/** /**
* Delay between requests for updated network autoconf information * Delay between requests for updated network autoconf information
* *
@ -326,6 +341,11 @@
*/ */
#define ZT_PUSH_DIRECT_PATHS_CUTOFF_TIME 60000 #define ZT_PUSH_DIRECT_PATHS_CUTOFF_TIME 60000
/**
* General rate limit for other kinds of rate-limited packets (HELLO, credential request, etc.) both inbound and outbound
*/
#define ZT_PEER_GENERAL_RATE_LIMIT 1000
/** /**
* Maximum number of direct path pushes within cutoff time * Maximum number of direct path pushes within cutoff time
* *

View File

@ -62,11 +62,8 @@ bool IncomingPacket::tryDecode(const RuntimeEnvironment *RR)
return true; return true;
} }
} else if ((c == ZT_PROTO_CIPHER_SUITE__C25519_POLY1305_NONE)&&(verb() == Packet::VERB_HELLO)) { } else if ((c == ZT_PROTO_CIPHER_SUITE__C25519_POLY1305_NONE)&&(verb() == Packet::VERB_HELLO)) {
// A null pointer for peer to _doHELLO() tells it to run its own // Only HELLO is allowed in the clear, but will still have a MAC
// special internal authentication logic. This is done for unencrypted return _doHELLO(RR,false);
// HELLOs to learn new identities, etc.
SharedPtr<Peer> tmp;
return _doHELLO(RR,tmp);
} }
SharedPtr<Peer> peer(RR->topology->getPeer(sourceAddress)); SharedPtr<Peer> peer(RR->topology->getPeer(sourceAddress));
@ -91,7 +88,7 @@ bool IncomingPacket::tryDecode(const RuntimeEnvironment *RR)
peer->received(_path,hops(),packetId(),v,0,Packet::VERB_NOP,false); peer->received(_path,hops(),packetId(),v,0,Packet::VERB_NOP,false);
return true; return true;
case Packet::VERB_HELLO: return _doHELLO(RR,peer); case Packet::VERB_HELLO: return _doHELLO(RR,true);
case Packet::VERB_ERROR: return _doERROR(RR,peer); case Packet::VERB_ERROR: return _doERROR(RR,peer);
case Packet::VERB_OK: return _doOK(RR,peer); case Packet::VERB_OK: return _doOK(RR,peer);
case Packet::VERB_WHOIS: return _doWHOIS(RR,peer); case Packet::VERB_WHOIS: return _doWHOIS(RR,peer);
@ -192,16 +189,16 @@ bool IncomingPacket::_doERROR(const RuntimeEnvironment *RR,const SharedPtr<Peer>
return true; return true;
} }
bool IncomingPacket::_doHELLO(const RuntimeEnvironment *RR,SharedPtr<Peer> &peer) bool IncomingPacket::_doHELLO(const RuntimeEnvironment *RR,const bool alreadyAuthenticated)
{ {
/* Note: this is the only packet ever sent in the clear, and it's also
* the only packet that we authenticate via a different path. Authentication
* occurs here and is based on the validity of the identity and the
* integrity of the packet's MAC, but it must be done after we check
* the identity since HELLO is a mechanism for learning new identities
* in the first place. */
try { try {
const uint64_t now = RR->node->now();
if (!_path->rateGateHello(now)) {
TRACE("dropped HELLO from %s(%s): rate limiting circuit breaker for HELLO on this path tripped",source().toString().c_str(),_path->address().toString().c_str());
return true;
}
const uint64_t pid = packetId(); const uint64_t pid = packetId();
const Address fromAddress(source()); const Address fromAddress(source());
const unsigned int protoVersion = (*this)[ZT_PROTO_VERB_HELLO_IDX_PROTOCOL_VERSION]; const unsigned int protoVersion = (*this)[ZT_PROTO_VERB_HELLO_IDX_PROTOCOL_VERSION];
@ -228,20 +225,19 @@ bool IncomingPacket::_doHELLO(const RuntimeEnvironment *RR,SharedPtr<Peer> &peer
} }
} }
if (protoVersion < ZT_PROTO_VERSION_MIN) {
TRACE("dropped HELLO from %s(%s): protocol version too old",id.address().toString().c_str(),_path->address().toString().c_str());
return true;
}
if (fromAddress != id.address()) { if (fromAddress != id.address()) {
TRACE("dropped HELLO from %s(%s): identity not for sending address",fromAddress.toString().c_str(),_path->address().toString().c_str()); TRACE("dropped HELLO from %s(%s): identity not for sending address",fromAddress.toString().c_str(),_path->address().toString().c_str());
return true; return true;
} }
if (protoVersion < ZT_PROTO_VERSION_MIN) {
TRACE("dropped HELLO from %s(%s): protocol version too old",id.address().toString().c_str(),_path->address().toString().c_str());
return true;
}
if (!peer) { // peer == NULL is the normal case here SharedPtr<Peer> peer(RR->topology->getPeer(id.address()));
peer = RR->topology->getPeer(id.address()); if (peer) {
if (peer) { // We already have an identity with this address -- check for collisions
// We already have an identity with this address -- check for collisions if (!alreadyAuthenticated) {
if (peer->identity() != id) { if (peer->identity() != id) {
// Identity is different from the one we already have -- address collision // Identity is different from the one we already have -- address collision
@ -273,31 +269,37 @@ bool IncomingPacket::_doHELLO(const RuntimeEnvironment *RR,SharedPtr<Peer> &peer
// Continue at // VALID // Continue at // VALID
} }
} else { } // else continue at // VALID
// We don't already have an identity with this address -- validate and learn it } else {
// We don't already have an identity with this address -- validate and learn it
// Check identity proof of work // Sanity check: this basically can't happen
if (!id.locallyValidate()) { if (alreadyAuthenticated) {
TRACE("dropped HELLO from %s(%s): identity invalid",id.address().toString().c_str(),_path->address().toString().c_str()); TRACE("dropped HELLO from %s(%s): somehow already authenticated with unknown peer?",id.address().toString().c_str(),_path->address().toString().c_str());
return true; return true;
}
// Check packet integrity and authentication
SharedPtr<Peer> newPeer(new Peer(RR,RR->identity,id));
if (!dearmor(newPeer->key())) {
TRACE("rejected HELLO from %s(%s): packet failed authentication",id.address().toString().c_str(),_path->address().toString().c_str());
return true;
}
peer = RR->topology->addPeer(newPeer);
// Continue at // VALID
} }
// VALID -- if we made it here, packet passed identity and authenticity checks! // Check identity proof of work
if (!id.locallyValidate()) {
TRACE("dropped HELLO from %s(%s): identity invalid",id.address().toString().c_str(),_path->address().toString().c_str());
return true;
}
// Check packet integrity and authentication
SharedPtr<Peer> newPeer(new Peer(RR,RR->identity,id));
if (!dearmor(newPeer->key())) {
TRACE("rejected HELLO from %s(%s): packet failed authentication",id.address().toString().c_str(),_path->address().toString().c_str());
return true;
}
peer = RR->topology->addPeer(newPeer);
// Continue at // VALID
} }
// VALID -- if we made it here, packet passed identity and authenticity checks!
if ((externalSurfaceAddress)&&(hops() == 0)) if ((externalSurfaceAddress)&&(hops() == 0))
RR->sa->iam(id.address(),_path->localAddress(),_path->address(),externalSurfaceAddress,RR->topology->isUpstream(id),RR->node->now()); RR->sa->iam(id.address(),_path->localAddress(),_path->address(),externalSurfaceAddress,RR->topology->isUpstream(id),now);
Packet outp(id.address(),RR->identity.address(),Packet::VERB_OK); Packet outp(id.address(),RR->identity.address(),Packet::VERB_OK);
outp.append((unsigned char)Packet::VERB_HELLO); outp.append((unsigned char)Packet::VERB_HELLO);
@ -349,7 +351,7 @@ bool IncomingPacket::_doHELLO(const RuntimeEnvironment *RR,SharedPtr<Peer> &peer
} }
outp.armor(peer->key(),true); outp.armor(peer->key(),true);
_path->send(RR,outp.data(),outp.size(),RR->node->now()); _path->send(RR,outp.data(),outp.size(),now);
peer->setRemoteVersion(protoVersion,vMajor,vMinor,vRevision); // important for this to go first so received() knows the version peer->setRemoteVersion(protoVersion,vMajor,vMinor,vRevision); // important for this to go first so received() knows the version
peer->received(_path,hops(),pid,Packet::VERB_HELLO,0,Packet::VERB_NOP,false); peer->received(_path,hops(),pid,Packet::VERB_HELLO,0,Packet::VERB_NOP,false);
@ -443,7 +445,7 @@ bool IncomingPacket::_doOK(const RuntimeEnvironment *RR,const SharedPtr<Peer> &p
case Packet::VERB_MULTICAST_GATHER: { case Packet::VERB_MULTICAST_GATHER: {
const uint64_t nwid = at<uint64_t>(ZT_PROTO_VERB_MULTICAST_GATHER__OK__IDX_NETWORK_ID); const uint64_t nwid = at<uint64_t>(ZT_PROTO_VERB_MULTICAST_GATHER__OK__IDX_NETWORK_ID);
SharedPtr<Network> network(RR->node->network(nwid)); SharedPtr<Network> network(RR->node->network(nwid));
if ((network)&&(network->gateMulticastGather(peer,verb(),packetId()))) { if ((network)&&(network->gateMulticastGatherReply(peer,verb(),packetId()))) {
const MulticastGroup mg(MAC(field(ZT_PROTO_VERB_MULTICAST_GATHER__OK__IDX_MAC,6),6),at<uint32_t>(ZT_PROTO_VERB_MULTICAST_GATHER__OK__IDX_ADI)); const MulticastGroup mg(MAC(field(ZT_PROTO_VERB_MULTICAST_GATHER__OK__IDX_MAC,6),6),at<uint32_t>(ZT_PROTO_VERB_MULTICAST_GATHER__OK__IDX_ADI));
//TRACE("%s(%s): OK(MULTICAST_GATHER) %.16llx/%s length %u",source().toString().c_str(),_path->address().toString().c_str(),nwid,mg.toString().c_str(),size()); //TRACE("%s(%s): OK(MULTICAST_GATHER) %.16llx/%s length %u",source().toString().c_str(),_path->address().toString().c_str(),nwid,mg.toString().c_str(),size());
const unsigned int count = at<uint16_t>(ZT_PROTO_VERB_MULTICAST_GATHER__OK__IDX_GATHER_RESULTS + 4); const unsigned int count = at<uint16_t>(ZT_PROTO_VERB_MULTICAST_GATHER__OK__IDX_GATHER_RESULTS + 4);
@ -469,7 +471,7 @@ bool IncomingPacket::_doOK(const RuntimeEnvironment *RR,const SharedPtr<Peer> &p
network->addCredential(com); network->addCredential(com);
} }
if (network->gateMulticastGather(peer,verb(),packetId())) { if (network->gateMulticastGatherReply(peer,verb(),packetId())) {
if ((flags & 0x02) != 0) { if ((flags & 0x02) != 0) {
// OK(MULTICAST_FRAME) includes implicit gather results // OK(MULTICAST_FRAME) includes implicit gather results
offset += ZT_PROTO_VERB_MULTICAST_FRAME__OK__IDX_COM_AND_GATHER_RESULTS; offset += ZT_PROTO_VERB_MULTICAST_FRAME__OK__IDX_COM_AND_GATHER_RESULTS;
@ -494,6 +496,11 @@ bool IncomingPacket::_doOK(const RuntimeEnvironment *RR,const SharedPtr<Peer> &p
bool IncomingPacket::_doWHOIS(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer) bool IncomingPacket::_doWHOIS(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer)
{ {
try { try {
if (!peer->rateGateInboundWhoisRequest(RR->node->now())) {
TRACE("dropped WHOIS from %s(%s): rate limit circuit breaker tripped",source().toString().c_str(),_path->address().toString().c_str());
return true;
}
Packet outp(peer->address(),RR->identity.address(),Packet::VERB_OK); Packet outp(peer->address(),RR->identity.address(),Packet::VERB_OK);
outp.append((unsigned char)Packet::VERB_WHOIS); outp.append((unsigned char)Packet::VERB_WHOIS);
outp.append(packetId()); outp.append(packetId());
@ -672,6 +679,11 @@ bool IncomingPacket::_doEXT_FRAME(const RuntimeEnvironment *RR,const SharedPtr<P
bool IncomingPacket::_doECHO(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer) bool IncomingPacket::_doECHO(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer)
{ {
try { try {
if (!peer->rateGateEchoRequest(RR->node->now())) {
TRACE("dropped ECHO from %s(%s): rate limit circuit breaker tripped",source().toString().c_str(),_path->address().toString().c_str());
return true;
}
const uint64_t pid = packetId(); const uint64_t pid = packetId();
Packet outp(peer->address(),RR->identity.address(),Packet::VERB_OK); Packet outp(peer->address(),RR->identity.address(),Packet::VERB_OK);
outp.append((unsigned char)Packet::VERB_ECHO); outp.append((unsigned char)Packet::VERB_ECHO);
@ -680,6 +692,7 @@ bool IncomingPacket::_doECHO(const RuntimeEnvironment *RR,const SharedPtr<Peer>
outp.append(reinterpret_cast<const unsigned char *>(data()) + ZT_PACKET_IDX_PAYLOAD,size() - ZT_PACKET_IDX_PAYLOAD); outp.append(reinterpret_cast<const unsigned char *>(data()) + ZT_PACKET_IDX_PAYLOAD,size() - ZT_PACKET_IDX_PAYLOAD);
outp.armor(peer->key(),true); outp.armor(peer->key(),true);
_path->send(RR,outp.data(),outp.size(),RR->node->now()); _path->send(RR,outp.data(),outp.size(),RR->node->now());
peer->received(_path,hops(),pid,Packet::VERB_ECHO,0,Packet::VERB_NOP,false); peer->received(_path,hops(),pid,Packet::VERB_ECHO,0,Packet::VERB_NOP,false);
} catch ( ... ) { } catch ( ... ) {
TRACE("dropped ECHO from %s(%s): unexpected exception",source().toString().c_str(),_path->address().toString().c_str()); TRACE("dropped ECHO from %s(%s): unexpected exception",source().toString().c_str(),_path->address().toString().c_str());
@ -692,11 +705,35 @@ bool IncomingPacket::_doMULTICAST_LIKE(const RuntimeEnvironment *RR,const Shared
try { try {
const uint64_t now = RR->node->now(); const uint64_t now = RR->node->now();
uint64_t authOnNetwork[256];
unsigned int authOnNetworkCount = 0;
SharedPtr<Network> network;
// Iterate through 18-byte network,MAC,ADI tuples // Iterate through 18-byte network,MAC,ADI tuples
for(unsigned int ptr=ZT_PACKET_IDX_PAYLOAD;ptr<size();ptr+=18) { for(unsigned int ptr=ZT_PACKET_IDX_PAYLOAD;ptr<size();ptr+=18) {
const uint64_t nwid = at<uint64_t>(ptr); const uint64_t nwid = at<uint64_t>(ptr);
const MulticastGroup group(MAC(field(ptr + 8,6),6),at<uint32_t>(ptr + 14));
RR->mc->add(now,nwid,group,peer->address()); bool auth = false;
for(unsigned int i=0;i<authOnNetworkCount;++i) {
if (nwid == authOnNetwork[i]) {
auth = true;
break;
}
}
if (!auth) {
if ((!network)||(network->id() != nwid))
network = RR->node->network(nwid);
if ( ((network)&&(network->gate(peer,verb(),packetId()))) || RR->mc->cacheAuthorized(peer->address(),nwid,now) ) {
auth = true;
if (authOnNetworkCount < 256) // sanity check, packets can't really be this big
authOnNetwork[authOnNetworkCount++] = nwid;
}
}
if (auth) {
const MulticastGroup group(MAC(field(ptr + 8,6),6),at<uint32_t>(ptr + 14));
RR->mc->add(now,nwid,group,peer->address());
}
} }
peer->received(_path,hops(),packetId(),Packet::VERB_MULTICAST_LIKE,0,Packet::VERB_NOP,false); peer->received(_path,hops(),packetId(),Packet::VERB_MULTICAST_LIKE,0,Packet::VERB_NOP,false);
@ -721,7 +758,7 @@ bool IncomingPacket::_doNETWORK_CREDENTIALS(const RuntimeEnvironment *RR,const S
if (network) { if (network) {
if (network->addCredential(com) == 1) if (network->addCredential(com) == 1)
return false; // wait for WHOIS return false; // wait for WHOIS
} } else RR->mc->addCredential(com,false);
} }
} }
++p; // skip trailing 0 after COMs if present ++p; // skip trailing 0 after COMs if present
@ -759,22 +796,21 @@ bool IncomingPacket::_doNETWORK_CONFIG_REQUEST(const RuntimeEnvironment *RR,cons
{ {
try { try {
const uint64_t nwid = at<uint64_t>(ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST_IDX_NETWORK_ID); const uint64_t nwid = at<uint64_t>(ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST_IDX_NETWORK_ID);
const unsigned int metaDataLength = at<uint16_t>(ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST_IDX_DICT_LEN);
const char *metaDataBytes = (const char *)field(ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST_IDX_DICT,metaDataLength);
const Dictionary<ZT_NETWORKCONFIG_METADATA_DICT_CAPACITY> metaData(metaDataBytes,metaDataLength);
const unsigned int hopCount = hops(); const unsigned int hopCount = hops();
const uint64_t requestPacketId = packetId(); const uint64_t requestPacketId = packetId();
bool netconfOk = false; bool trustEstablished = false;
if (RR->localNetworkController) { if (RR->localNetworkController) {
const unsigned int metaDataLength = at<uint16_t>(ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST_IDX_DICT_LEN);
const char *metaDataBytes = (const char *)field(ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST_IDX_DICT,metaDataLength);
const Dictionary<ZT_NETWORKCONFIG_METADATA_DICT_CAPACITY> metaData(metaDataBytes,metaDataLength);
NetworkConfig *netconf = new NetworkConfig(); NetworkConfig *netconf = new NetworkConfig();
try { try {
switch(RR->localNetworkController->doNetworkConfigRequest((hopCount > 0) ? InetAddress() : _path->address(),RR->identity,peer->identity(),nwid,metaData,*netconf)) { switch(RR->localNetworkController->doNetworkConfigRequest((hopCount > 0) ? InetAddress() : _path->address(),RR->identity,peer->identity(),nwid,metaData,*netconf)) {
case NetworkController::NETCONF_QUERY_OK: { case NetworkController::NETCONF_QUERY_OK: {
netconfOk = true; trustEstablished = true;
Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> *dconf = new Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY>(); Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> *dconf = new Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY>();
try { try {
if (netconf->toDictionary(*dconf,metaData.getUI(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_VERSION,0) < 6)) { if (netconf->toDictionary(*dconf,metaData.getUI(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_VERSION,0) < 6)) {
@ -846,7 +882,7 @@ bool IncomingPacket::_doNETWORK_CONFIG_REQUEST(const RuntimeEnvironment *RR,cons
_path->send(RR,outp.data(),outp.size(),RR->node->now()); _path->send(RR,outp.data(),outp.size(),RR->node->now());
} }
peer->received(_path,hopCount,requestPacketId,Packet::VERB_NETWORK_CONFIG_REQUEST,0,Packet::VERB_NOP,netconfOk); peer->received(_path,hopCount,requestPacketId,Packet::VERB_NETWORK_CONFIG_REQUEST,0,Packet::VERB_NOP,trustEstablished);
} catch (std::exception &exc) { } catch (std::exception &exc) {
fprintf(stderr,"WARNING: network config request failed with exception: %s" ZT_EOL_S,exc.what()); fprintf(stderr,"WARNING: network config request failed with exception: %s" ZT_EOL_S,exc.what());
TRACE("dropped NETWORK_CONFIG_REQUEST from %s(%s): %s",source().toString().c_str(),_path->address().toString().c_str(),exc.what()); TRACE("dropped NETWORK_CONFIG_REQUEST from %s(%s): %s",source().toString().c_str(),_path->address().toString().c_str(),exc.what());
@ -897,21 +933,23 @@ bool IncomingPacket::_doMULTICAST_GATHER(const RuntimeEnvironment *RR,const Shar
//TRACE("<<MC %s(%s) GATHER up to %u in %.16llx/%s",source().toString().c_str(),_path->address().toString().c_str(),gatherLimit,nwid,mg.toString().c_str()); //TRACE("<<MC %s(%s) GATHER up to %u in %.16llx/%s",source().toString().c_str(),_path->address().toString().c_str(),gatherLimit,nwid,mg.toString().c_str());
const SharedPtr<Network> network(RR->node->network(nwid));
if ((flags & 0x01) != 0) { if ((flags & 0x01) != 0) {
try { try {
CertificateOfMembership com; CertificateOfMembership com;
com.deserialize(*this,ZT_PROTO_VERB_MULTICAST_GATHER_IDX_COM); com.deserialize(*this,ZT_PROTO_VERB_MULTICAST_GATHER_IDX_COM);
if (com) { if (com) {
SharedPtr<Network> network(RR->node->network(nwid));
if (network) if (network)
network->addCredential(com); network->addCredential(com);
else RR->mc->addCredential(com,false);
} }
} catch ( ... ) { } catch ( ... ) {
TRACE("MULTICAST_GATHER from %s(%s): discarded invalid COM",peer->address().toString().c_str(),_path->address().toString().c_str()); TRACE("MULTICAST_GATHER from %s(%s): discarded invalid COM",peer->address().toString().c_str(),_path->address().toString().c_str());
} }
} }
if (gatherLimit) { if ( ( ((network)&&(network->gate(peer,verb(),packetId()))) || (RR->mc->cacheAuthorized(peer->address(),nwid,RR->node->now())) ) && (gatherLimit > 0) ) {
Packet outp(peer->address(),RR->identity.address(),Packet::VERB_OK); Packet outp(peer->address(),RR->identity.address(),Packet::VERB_OK);
outp.append((unsigned char)Packet::VERB_MULTICAST_GATHER); outp.append((unsigned char)Packet::VERB_MULTICAST_GATHER);
outp.append(packetId()); outp.append(packetId());
@ -1043,7 +1081,7 @@ bool IncomingPacket::_doPUSH_DIRECT_PATHS(const RuntimeEnvironment *RR,const Sha
const uint64_t now = RR->node->now(); const uint64_t now = RR->node->now();
// First, subject this to a rate limit // First, subject this to a rate limit
if (!peer->shouldRespondToDirectPathPush(now)) { if (!peer->rateGatePushDirectPaths(now)) {
TRACE("dropped PUSH_DIRECT_PATHS from %s(%s): circuit breaker tripped",source().toString().c_str(),_path->address().toString().c_str()); TRACE("dropped PUSH_DIRECT_PATHS from %s(%s): circuit breaker tripped",source().toString().c_str(),_path->address().toString().c_str());
peer->received(_path,hops(),packetId(),Packet::VERB_PUSH_DIRECT_PATHS,0,Packet::VERB_NOP,false); peer->received(_path,hops(),packetId(),Packet::VERB_PUSH_DIRECT_PATHS,0,Packet::VERB_NOP,false);
return true; return true;

View File

@ -136,7 +136,7 @@ private:
// These are called internally to handle packet contents once it has // These are called internally to handle packet contents once it has
// been authenticated, decrypted, decompressed, and classified. // been authenticated, decrypted, decompressed, and classified.
bool _doERROR(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer); bool _doERROR(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
bool _doHELLO(const RuntimeEnvironment *RR,SharedPtr<Peer> &peer); // can be called with NULL peer, while all others cannot bool _doHELLO(const RuntimeEnvironment *RR,const bool alreadyAuthenticated);
bool _doOK(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer); bool _doOK(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
bool _doWHOIS(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer); bool _doWHOIS(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
bool _doRENDEZVOUS(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer); bool _doRENDEZVOUS(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);

View File

@ -71,7 +71,7 @@ void Membership::sendCredentialsIfNeeded(const RuntimeEnvironment *RR,const uint
} }
capsAndTags.setAt<uint16_t>(tagCountPos,(uint16_t)appendedTags); capsAndTags.setAt<uint16_t>(tagCountPos,(uint16_t)appendedTags);
const bool needCom = ((nconf.isPrivate())&&(nconf.com)&&((now - _lastPushedCom) >= ZT_CREDENTIAL_PUSH_EVERY)); const bool needCom = ((nconf.com)&&((now - _lastPushedCom) >= ZT_CREDENTIAL_PUSH_EVERY));
if ( (needCom) || (appendedCaps) || (appendedTags) ) { if ( (needCom) || (appendedCaps) || (appendedTags) ) {
Packet outp(peerAddress,RR->identity.address(),Packet::VERB_NETWORK_CREDENTIALS); Packet outp(peerAddress,RR->identity.address(),Packet::VERB_NETWORK_CREDENTIALS);
if (needCom) { if (needCom) {

View File

@ -34,8 +34,8 @@ namespace ZeroTier {
Multicaster::Multicaster(const RuntimeEnvironment *renv) : Multicaster::Multicaster(const RuntimeEnvironment *renv) :
RR(renv), RR(renv),
_groups(1024), _groups(256),
_groups_m() _gatherAuth(256)
{ {
} }
@ -244,7 +244,7 @@ void Multicaster::send(
} }
for(unsigned int k=0;k<numExplicitGatherPeers;++k) { for(unsigned int k=0;k<numExplicitGatherPeers;++k) {
const CertificateOfMembership *com = (network) ? (((network->config())&&(network->config().isPrivate())) ? &(network->config().com) : (const CertificateOfMembership *)0) : (const CertificateOfMembership *)0; const CertificateOfMembership *com = (network) ? ((network->config().com) ? &(network->config().com) : (const CertificateOfMembership *)0) : (const CertificateOfMembership *)0;
Packet outp(explicitGatherPeers[k],RR->identity.address(),Packet::VERB_MULTICAST_GATHER); Packet outp(explicitGatherPeers[k],RR->identity.address(),Packet::VERB_MULTICAST_GATHER);
outp.append(nwid); outp.append(nwid);
outp.append((uint8_t)((com) ? 0x01 : 0x00)); outp.append((uint8_t)((com) ? 0x01 : 0x00));
@ -301,42 +301,62 @@ void Multicaster::send(
void Multicaster::clean(uint64_t now) void Multicaster::clean(uint64_t now)
{ {
Mutex::Lock _l(_groups_m); {
Mutex::Lock _l(_groups_m);
Multicaster::Key *k = (Multicaster::Key *)0;
MulticastGroupStatus *s = (MulticastGroupStatus *)0;
Hashtable<Multicaster::Key,MulticastGroupStatus>::Iterator mm(_groups);
while (mm.next(k,s)) {
for(std::list<OutboundMulticast>::iterator tx(s->txQueue.begin());tx!=s->txQueue.end();) {
if ((tx->expired(now))||(tx->atLimit()))
s->txQueue.erase(tx++);
else ++tx;
}
Multicaster::Key *k = (Multicaster::Key *)0; unsigned long count = 0;
MulticastGroupStatus *s = (MulticastGroupStatus *)0; {
Hashtable<Multicaster::Key,MulticastGroupStatus>::Iterator mm(_groups); std::vector<MulticastGroupMember>::iterator reader(s->members.begin());
while (mm.next(k,s)) { std::vector<MulticastGroupMember>::iterator writer(reader);
for(std::list<OutboundMulticast>::iterator tx(s->txQueue.begin());tx!=s->txQueue.end();) { while (reader != s->members.end()) {
if ((tx->expired(now))||(tx->atLimit())) if ((now - reader->timestamp) < ZT_MULTICAST_LIKE_EXPIRE) {
s->txQueue.erase(tx++); *writer = *reader;
else ++tx; ++writer;
} ++count;
}
unsigned long count = 0; ++reader;
{
std::vector<MulticastGroupMember>::iterator reader(s->members.begin());
std::vector<MulticastGroupMember>::iterator writer(reader);
while (reader != s->members.end()) {
if ((now - reader->timestamp) < ZT_MULTICAST_LIKE_EXPIRE) {
*writer = *reader;
++writer;
++count;
} }
++reader; }
if (count) {
s->members.resize(count);
} else if (s->txQueue.empty()) {
_groups.erase(*k);
} else {
s->members.clear();
} }
} }
}
if (count) { {
s->members.resize(count); Mutex::Lock _l(_gatherAuth_m);
} else if (s->txQueue.empty()) { _GatherAuthKey *k = (_GatherAuthKey *)0;
_groups.erase(*k); uint64_t *ts = (uint64_t *)ts;
} else { Hashtable<_GatherAuthKey,uint64_t>::Iterator i(_gatherAuth);
s->members.clear(); while (i.next(k,ts)) {
if ((now - *ts) >= ZT_MULTICAST_CREDENTIAL_EXPIRATON)
_gatherAuth.erase(*k);
} }
} }
} }
void Multicaster::addCredential(const CertificateOfMembership &com,bool alreadyValidated)
{
if ((alreadyValidated)||(com.verify(RR) == 0)) {
Mutex::Lock _l(_gatherAuth_m);
_gatherAuth[_GatherAuthKey(com.networkId(),com.issuedTo())] = RR->node->now();
}
}
void Multicaster::_add(uint64_t now,uint64_t nwid,const MulticastGroup &mg,MulticastGroupStatus &gs,const Address &member) void Multicaster::_add(uint64_t now,uint64_t nwid,const MulticastGroup &mg,MulticastGroupStatus &gs,const Address &member)
{ {
// assumes _groups_m is locked // assumes _groups_m is locked

View File

@ -179,12 +179,52 @@ public:
*/ */
void clean(uint64_t now); void clean(uint64_t now);
/**
* Add an authorization credential
*
* The Multicaster keeps its own track of when valid credentials of network
* membership are presented. This allows it to control MULTICAST_LIKE
* GATHER authorization for networks this node does not belong to.
*
* @param com Certificate of membership
* @param alreadyValidated If true, COM has already been checked and found to be valid and signed
*/
void addCredential(const CertificateOfMembership &com,bool alreadyValidated);
/**
* Check authorization for GATHER and LIKE for non-network-members
*
* @param a Address of peer
* @param nwid Network ID
* @param now Current time
* @return True if GATHER and LIKE should be allowed
*/
bool cacheAuthorized(const Address &a,const uint64_t nwid,const uint64_t now) const
{
Mutex::Lock _l(_gatherAuth_m);
const uint64_t *p = _gatherAuth.get(_GatherAuthKey(nwid,a));
return ((p)&&((now - *p) < ZT_MULTICAST_CREDENTIAL_EXPIRATON));
}
private: private:
void _add(uint64_t now,uint64_t nwid,const MulticastGroup &mg,MulticastGroupStatus &gs,const Address &member); void _add(uint64_t now,uint64_t nwid,const MulticastGroup &mg,MulticastGroupStatus &gs,const Address &member);
const RuntimeEnvironment *RR; const RuntimeEnvironment *RR;
Hashtable<Multicaster::Key,MulticastGroupStatus> _groups; Hashtable<Multicaster::Key,MulticastGroupStatus> _groups;
Mutex _groups_m; Mutex _groups_m;
struct _GatherAuthKey
{
_GatherAuthKey() : member(0),networkId(0) {}
_GatherAuthKey(const uint64_t nwid,const Address &a) : member(a.toInt()),networkId(nwid) {}
inline unsigned long hashCode() const { return (member ^ networkId); }
inline bool operator==(const _GatherAuthKey &k) const { return ((member == k.member)&&(networkId == k.networkId)); }
uint64_t member;
uint64_t networkId;
};
Hashtable< _GatherAuthKey,uint64_t > _gatherAuth;
Mutex _gatherAuth_m;
}; };
} // namespace ZeroTier } // namespace ZeroTier

View File

@ -866,31 +866,24 @@ bool Network::subscribedToMulticastGroup(const MulticastGroup &mg,bool includeBr
return true; return true;
else if (includeBridgedGroups) else if (includeBridgedGroups)
return _multicastGroupsBehindMe.contains(mg); return _multicastGroupsBehindMe.contains(mg);
else return false; return false;
} }
void Network::multicastSubscribe(const MulticastGroup &mg) void Network::multicastSubscribe(const MulticastGroup &mg)
{ {
{ Mutex::Lock _l(_lock);
Mutex::Lock _l(_lock); if (!std::binary_search(_myMulticastGroups.begin(),_myMulticastGroups.end(),mg)) {
if (std::binary_search(_myMulticastGroups.begin(),_myMulticastGroups.end(),mg)) _myMulticastGroups.insert(std::upper_bound(_myMulticastGroups.begin(),_myMulticastGroups.end(),mg),mg);
return; _sendUpdatesToMembers(&mg);
_myMulticastGroups.push_back(mg);
std::sort(_myMulticastGroups.begin(),_myMulticastGroups.end());
_pushStateToMembers(&mg);
} }
} }
void Network::multicastUnsubscribe(const MulticastGroup &mg) void Network::multicastUnsubscribe(const MulticastGroup &mg)
{ {
Mutex::Lock _l(_lock); Mutex::Lock _l(_lock);
std::vector<MulticastGroup> nmg; std::vector<MulticastGroup>::iterator i(std::lower_bound(_myMulticastGroups.begin(),_myMulticastGroups.end(),mg));
for(std::vector<MulticastGroup>::const_iterator i(_myMulticastGroups.begin());i!=_myMulticastGroups.end();++i) { if ( (i != _myMulticastGroups.end()) && (*i == mg) )
if (*i != mg) _myMulticastGroups.erase(i);
nmg.push_back(*i);
}
if (nmg.size() != _myMulticastGroups.size())
_myMulticastGroups.swap(nmg);
} }
bool Network::applyConfiguration(const NetworkConfig &conf) bool Network::applyConfiguration(const NetworkConfig &conf)
@ -1054,30 +1047,29 @@ void Network::requestConfiguration()
} else { } else {
outp.append((unsigned char)0,16); outp.append((unsigned char)0,16);
} }
RR->node->expectReplyTo(outp.packetId());
RR->node->expectReplyTo(_inboundConfigPacketId = outp.packetId());
_inboundConfigChunks.clear();
outp.compress(); outp.compress();
RR->sw->send(outp,true); RR->sw->send(outp,true);
// Expect replies with this in-re packet ID
_inboundConfigPacketId = outp.packetId();
_inboundConfigChunks.clear();
} }
bool Network::gate(const SharedPtr<Peer> &peer,const Packet::Verb verb,const uint64_t packetId) bool Network::gate(const SharedPtr<Peer> &peer,const Packet::Verb verb,const uint64_t packetId)
{ {
const uint64_t now = RR->node->now();
Mutex::Lock _l(_lock); Mutex::Lock _l(_lock);
try { try {
if (_config) { if (_config) {
Membership &m = _membership(peer->address()); Membership &m = _membership(peer->address());
const bool allow = m.isAllowedOnNetwork(_config); const bool allow = m.isAllowedOnNetwork(_config);
if (allow) { if (allow) {
const uint64_t now = RR->node->now();
m.sendCredentialsIfNeeded(RR,now,peer->address(),_config,(const Capability *)0); m.sendCredentialsIfNeeded(RR,now,peer->address(),_config,(const Capability *)0);
if (m.shouldLikeMulticasts(now)) { if (m.shouldLikeMulticasts(now)) {
_announceMulticastGroupsTo(peer->address(),_allMulticastGroups()); _announceMulticastGroupsTo(peer->address(),_allMulticastGroups());
m.likingMulticasts(now); m.likingMulticasts(now);
} }
} else if (m.recentlyAllowedOnNetwork(_config)) { } else if (m.recentlyAllowedOnNetwork(_config)&&peer->rateGateRequestCredentials(now)) {
Packet outp(peer->address(),RR->identity.address(),Packet::VERB_ERROR); Packet outp(peer->address(),RR->identity.address(),Packet::VERB_ERROR);
outp.append((uint8_t)verb); outp.append((uint8_t)verb);
outp.append(packetId); outp.append(packetId);
@ -1093,7 +1085,7 @@ bool Network::gate(const SharedPtr<Peer> &peer,const Packet::Verb verb,const uin
return false; return false;
} }
bool Network::gateMulticastGather(const SharedPtr<Peer> &peer,const Packet::Verb verb,const uint64_t packetId) bool Network::gateMulticastGatherReply(const SharedPtr<Peer> &peer,const Packet::Verb verb,const uint64_t packetId)
{ {
return ( (peer->address() == controller()) || RR->topology->isUpstream(peer->identity()) || gate(peer,verb,packetId) || _config.isAnchor(peer->address()) ); return ( (peer->address() == controller()) || RR->topology->isUpstream(peer->identity()) || gate(peer,verb,packetId) || _config.isAnchor(peer->address()) );
} }
@ -1180,7 +1172,22 @@ void Network::learnBridgedMulticastGroup(const MulticastGroup &mg,uint64_t now)
const unsigned long tmp = (unsigned long)_multicastGroupsBehindMe.size(); const unsigned long tmp = (unsigned long)_multicastGroupsBehindMe.size();
_multicastGroupsBehindMe.set(mg,now); _multicastGroupsBehindMe.set(mg,now);
if (tmp != _multicastGroupsBehindMe.size()) if (tmp != _multicastGroupsBehindMe.size())
_pushStateToMembers(&mg); _sendUpdatesToMembers(&mg);
}
int Network::addCredential(const CertificateOfMembership &com)
{
if (com.networkId() != _id)
return -1;
const Address a(com.issuedTo());
Mutex::Lock _l(_lock);
Membership &m = _membership(a);
const int result = m.addCredential(RR,com);
if (result == 0) {
m.sendCredentialsIfNeeded(RR,RR->node->now(),a,_config,(const Capability *)0);
RR->mc->addCredential(com,true);
}
return result;
} }
void Network::destroy() void Network::destroy()
@ -1245,7 +1252,7 @@ void Network::_externalConfig(ZT_VirtualNetworkConfig *ec) const
} }
} }
void Network::_pushStateToMembers(const MulticastGroup *const newMulticastGroup) void Network::_sendUpdatesToMembers(const MulticastGroup *const newMulticastGroup)
{ {
// Assumes _lock is locked // Assumes _lock is locked
const uint64_t now = RR->node->now(); const uint64_t now = RR->node->now();
@ -1263,7 +1270,7 @@ void Network::_pushStateToMembers(const MulticastGroup *const newMulticastGroup)
// them our COM so that MULTICAST_GATHER can be authenticated properly. // them our COM so that MULTICAST_GATHER can be authenticated properly.
const std::vector<Address> upstreams(RR->topology->upstreamAddresses()); const std::vector<Address> upstreams(RR->topology->upstreamAddresses());
for(std::vector<Address>::const_iterator a(upstreams.begin());a!=upstreams.end();++a) { for(std::vector<Address>::const_iterator a(upstreams.begin());a!=upstreams.end();++a) {
if ((_config.isPrivate())&&(_config.com)) { if (_config.com) {
Packet outp(*a,RR->identity.address(),Packet::VERB_NETWORK_CREDENTIALS); Packet outp(*a,RR->identity.address(),Packet::VERB_NETWORK_CREDENTIALS);
_config.com.serialize(outp); _config.com.serialize(outp);
outp.append((uint8_t)0x00); outp.append((uint8_t)0x00);
@ -1272,12 +1279,17 @@ void Network::_pushStateToMembers(const MulticastGroup *const newMulticastGroup)
_announceMulticastGroupsTo(*a,groups); _announceMulticastGroupsTo(*a,groups);
} }
// Announce to controller, which does not need our COM since it obviously // Also announce to controller, and send COM to simplify and generalize behavior even though in theory it does not need it
// knows if we are a member. Of course if we already did or are going to
// below then we can skip it here.
const Address c(controller()); const Address c(controller());
if ( (std::find(upstreams.begin(),upstreams.end(),c) == upstreams.end()) && (!_memberships.contains(c)) ) if ( (std::find(upstreams.begin(),upstreams.end(),c) == upstreams.end()) && (!_memberships.contains(c)) ) {
if (_config.com) {
Packet outp(c,RR->identity.address(),Packet::VERB_NETWORK_CREDENTIALS);
_config.com.serialize(outp);
outp.append((uint8_t)0x00);
RR->sw->send(outp,true);
}
_announceMulticastGroupsTo(c,groups); _announceMulticastGroupsTo(c,groups);
}
} }
// Make sure that all "network anchors" have Membership records so we will // Make sure that all "network anchors" have Membership records so we will

View File

@ -260,7 +260,7 @@ public:
/** /**
* Check whether this peer is allowed to provide multicast info for this network * Check whether this peer is allowed to provide multicast info for this network
*/ */
bool gateMulticastGather(const SharedPtr<Peer> &peer,const Packet::Verb verb,const uint64_t packetId); bool gateMulticastGatherReply(const SharedPtr<Peer> &peer,const Packet::Verb verb,const uint64_t packetId);
/** /**
* @param peer Peer to check * @param peer Peer to check
@ -276,10 +276,10 @@ public:
/** /**
* Push state to members such as multicast group memberships and latest COM (if needed) * Push state to members such as multicast group memberships and latest COM (if needed)
*/ */
inline void pushStateToMembers() inline void sendUpdatesToMembers()
{ {
Mutex::Lock _l(_lock); Mutex::Lock _l(_lock);
_pushStateToMembers((const MulticastGroup *)0); _sendUpdatesToMembers((const MulticastGroup *)0);
} }
/** /**
@ -332,9 +332,7 @@ public:
{ {
Mutex::Lock _l(_lock); Mutex::Lock _l(_lock);
const Address *const br = _remoteBridgeRoutes.get(mac); const Address *const br = _remoteBridgeRoutes.get(mac);
if (br) return ((br) ? *br : Address());
return *br;
return Address();
} }
/** /**
@ -357,13 +355,7 @@ public:
* @param com Certificate of membership * @param com Certificate of membership
* @return 0 == OK, 1 == waiting for WHOIS, -1 == BAD signature or credential * @return 0 == OK, 1 == waiting for WHOIS, -1 == BAD signature or credential
*/ */
inline int addCredential(const CertificateOfMembership &com) int addCredential(const CertificateOfMembership &com);
{
if (com.networkId() != _id)
return -1;
Mutex::Lock _l(_lock);
return _membership(com.issuedTo()).addCredential(RR,com);
}
/** /**
* @param cap Capability * @param cap Capability
@ -418,7 +410,7 @@ private:
ZT_VirtualNetworkStatus _status() const; ZT_VirtualNetworkStatus _status() const;
void _externalConfig(ZT_VirtualNetworkConfig *ec) const; // assumes _lock is locked void _externalConfig(ZT_VirtualNetworkConfig *ec) const; // assumes _lock is locked
bool _gate(const SharedPtr<Peer> &peer); bool _gate(const SharedPtr<Peer> &peer);
void _pushStateToMembers(const MulticastGroup *const newMulticastGroup); void _sendUpdatesToMembers(const MulticastGroup *const newMulticastGroup);
void _announceMulticastGroupsTo(const Address &peer,const std::vector<MulticastGroup> &allMulticastGroups); void _announceMulticastGroupsTo(const Address &peer,const std::vector<MulticastGroup> &allMulticastGroups);
std::vector<MulticastGroup> _allMulticastGroups() const; std::vector<MulticastGroup> _allMulticastGroups() const;
Membership &_membership(const Address &a); Membership &_membership(const Address &a);

View File

@ -266,7 +266,7 @@ ZT_ResultCode Node::processBackgroundTasks(uint64_t now,volatile uint64_t *nextB
for(std::vector< std::pair< uint64_t,SharedPtr<Network> > >::const_iterator n(_networks.begin());n!=_networks.end();++n) { for(std::vector< std::pair< uint64_t,SharedPtr<Network> > >::const_iterator n(_networks.begin());n!=_networks.end();++n) {
if (((now - n->second->lastConfigUpdate()) >= ZT_NETWORK_AUTOCONF_DELAY)||(!n->second->hasConfig())) if (((now - n->second->lastConfigUpdate()) >= ZT_NETWORK_AUTOCONF_DELAY)||(!n->second->hasConfig()))
needConfig.push_back(n->second); needConfig.push_back(n->second);
n->second->pushStateToMembers(); n->second->sendUpdatesToMembers();
} }
} }
for(std::vector< SharedPtr<Network> >::const_iterator n(needConfig.begin());n!=needConfig.end();++n) for(std::vector< SharedPtr<Network> >::const_iterator n(needConfig.begin());n!=needConfig.end();++n)

View File

@ -104,6 +104,7 @@ public:
Path() : Path() :
_lastOut(0), _lastOut(0),
_lastIn(0), _lastIn(0),
_lastHello(0),
_addr(), _addr(),
_localAddress(), _localAddress(),
_ipScope(InetAddress::IP_SCOPE_NONE) _ipScope(InetAddress::IP_SCOPE_NONE)
@ -113,6 +114,7 @@ public:
Path(const InetAddress &localAddress,const InetAddress &addr) : Path(const InetAddress &localAddress,const InetAddress &addr) :
_lastOut(0), _lastOut(0),
_lastIn(0), _lastIn(0),
_lastHello(0),
_addr(addr), _addr(addr),
_localAddress(localAddress), _localAddress(localAddress),
_ipScope(addr.ipScope()) _ipScope(addr.ipScope())
@ -229,9 +231,22 @@ public:
*/ */
inline uint64_t lastIn() const { return _lastIn; } inline uint64_t lastIn() const { return _lastIn; }
/**
* @return True if we should allow HELLO via this path
*/
inline bool rateGateHello(const uint64_t now)
{
if ((now - _lastHello) >= ZT_PATH_HELLO_RATE_LIMIT) {
_lastHello = now;
return true;
}
return false;
}
private: private:
uint64_t _lastOut; uint64_t _lastOut;
uint64_t _lastIn; uint64_t _lastIn;
uint64_t _lastHello;
InetAddress _addr; InetAddress _addr;
InetAddress _localAddress; InetAddress _localAddress;
InetAddress::IpScope _ipScope; // memoize this since it's a computed value checked often InetAddress::IpScope _ipScope; // memoize this since it's a computed value checked often

View File

@ -47,6 +47,9 @@ Peer::Peer(const RuntimeEnvironment *renv,const Identity &myIdentity,const Ident
_lastMulticastFrame(0), _lastMulticastFrame(0),
_lastDirectPathPushSent(0), _lastDirectPathPushSent(0),
_lastDirectPathPushReceive(0), _lastDirectPathPushReceive(0),
_lastCredentialRequestSent(0),
_lastWhoisRequestReceived(0),
_lastEchoRequestReceived(0),
RR(renv), RR(renv),
_remoteClusterOptimal4(0), _remoteClusterOptimal4(0),
_vProto(0), _vProto(0),
@ -194,7 +197,80 @@ void Peer::received(
} }
} else if (trustEstablished) { } else if (trustEstablished) {
// Send PUSH_DIRECT_PATHS if hops>0 (relayed) and we have a trust relationship (common network membership) // Send PUSH_DIRECT_PATHS if hops>0 (relayed) and we have a trust relationship (common network membership)
_pushDirectPaths(path,now); #ifdef ZT_ENABLE_CLUSTER
// Cluster mode disables normal PUSH_DIRECT_PATHS in favor of cluster-based peer redirection
const bool haveCluster = (RR->cluster);
#else
const bool haveCluster = false;
#endif
if ( ((now - _lastDirectPathPushSent) >= ZT_DIRECT_PATH_PUSH_INTERVAL) && (!haveCluster) ) {
_lastDirectPathPushSent = now;
std::vector<InetAddress> pathsToPush;
std::vector<InetAddress> dps(RR->node->directPaths());
for(std::vector<InetAddress>::const_iterator i(dps.begin());i!=dps.end();++i)
pathsToPush.push_back(*i);
std::vector<InetAddress> sym(RR->sa->getSymmetricNatPredictions());
for(unsigned long i=0,added=0;i<sym.size();++i) {
InetAddress tmp(sym[(unsigned long)RR->node->prng() % sym.size()]);
if (std::find(pathsToPush.begin(),pathsToPush.end(),tmp) == pathsToPush.end()) {
pathsToPush.push_back(tmp);
if (++added >= ZT_PUSH_DIRECT_PATHS_MAX_PER_SCOPE_AND_FAMILY)
break;
}
}
if (pathsToPush.size() > 0) {
#ifdef ZT_TRACE
std::string ps;
for(std::vector<InetAddress>::const_iterator p(pathsToPush.begin());p!=pathsToPush.end();++p) {
if (ps.length() > 0)
ps.push_back(',');
ps.append(p->toString());
}
TRACE("pushing %u direct paths to %s: %s",(unsigned int)pathsToPush.size(),_id.address().toString().c_str(),ps.c_str());
#endif
std::vector<InetAddress>::const_iterator p(pathsToPush.begin());
while (p != pathsToPush.end()) {
Packet outp(_id.address(),RR->identity.address(),Packet::VERB_PUSH_DIRECT_PATHS);
outp.addSize(2); // leave room for count
unsigned int count = 0;
while ((p != pathsToPush.end())&&((outp.size() + 24) < 1200)) {
uint8_t addressType = 4;
switch(p->ss_family) {
case AF_INET:
break;
case AF_INET6:
addressType = 6;
break;
default: // we currently only push IP addresses
++p;
continue;
}
outp.append((uint8_t)0); // no flags
outp.append((uint16_t)0); // no extensions
outp.append(addressType);
outp.append((uint8_t)((addressType == 4) ? 6 : 18));
outp.append(p->rawIpData(),((addressType == 4) ? 4 : 16));
outp.append((uint16_t)p->port());
++count;
++p;
}
if (count) {
outp.setAt(ZT_PACKET_IDX_PAYLOAD,(uint16_t)count);
outp.armor(_key,true);
path->send(RR,outp.data(),outp.size(),now);
}
}
}
}
} }
} }
@ -368,86 +444,4 @@ void Peer::getBestActiveAddresses(uint64_t now,InetAddress &v4,InetAddress &v6)
v6 = _paths[bestp6].path->address(); v6 = _paths[bestp6].path->address();
} }
bool Peer::_pushDirectPaths(const SharedPtr<Path> &path,uint64_t now)
{
#ifdef ZT_ENABLE_CLUSTER
// Cluster mode disables normal PUSH_DIRECT_PATHS in favor of cluster-based peer redirection
if (RR->cluster)
return false;
#endif
if ((now - _lastDirectPathPushSent) < ZT_DIRECT_PATH_PUSH_INTERVAL)
return false;
else _lastDirectPathPushSent = now;
std::vector<InetAddress> pathsToPush;
std::vector<InetAddress> dps(RR->node->directPaths());
for(std::vector<InetAddress>::const_iterator i(dps.begin());i!=dps.end();++i)
pathsToPush.push_back(*i);
std::vector<InetAddress> sym(RR->sa->getSymmetricNatPredictions());
for(unsigned long i=0,added=0;i<sym.size();++i) {
InetAddress tmp(sym[(unsigned long)RR->node->prng() % sym.size()]);
if (std::find(pathsToPush.begin(),pathsToPush.end(),tmp) == pathsToPush.end()) {
pathsToPush.push_back(tmp);
if (++added >= ZT_PUSH_DIRECT_PATHS_MAX_PER_SCOPE_AND_FAMILY)
break;
}
}
if (pathsToPush.empty())
return false;
#ifdef ZT_TRACE
{
std::string ps;
for(std::vector<InetAddress>::const_iterator p(pathsToPush.begin());p!=pathsToPush.end();++p) {
if (ps.length() > 0)
ps.push_back(',');
ps.append(p->toString());
}
TRACE("pushing %u direct paths to %s: %s",(unsigned int)pathsToPush.size(),_id.address().toString().c_str(),ps.c_str());
}
#endif
std::vector<InetAddress>::const_iterator p(pathsToPush.begin());
while (p != pathsToPush.end()) {
Packet outp(_id.address(),RR->identity.address(),Packet::VERB_PUSH_DIRECT_PATHS);
outp.addSize(2); // leave room for count
unsigned int count = 0;
while ((p != pathsToPush.end())&&((outp.size() + 24) < 1200)) {
uint8_t addressType = 4;
switch(p->ss_family) {
case AF_INET:
break;
case AF_INET6:
addressType = 6;
break;
default: // we currently only push IP addresses
++p;
continue;
}
outp.append((uint8_t)0); // no flags
outp.append((uint16_t)0); // no extensions
outp.append(addressType);
outp.append((uint8_t)((addressType == 4) ? 6 : 18));
outp.append(p->rawIpData(),((addressType == 4) ? 4 : 16));
outp.append((uint16_t)p->port());
++count;
++p;
}
if (count) {
outp.setAt(ZT_PACKET_IDX_PAYLOAD,(uint16_t)count);
outp.armor(_key,true);
path->send(RR,outp.data(),outp.size(),now);
}
}
return true;
}
} // namespace ZeroTier } // namespace ZeroTier

View File

@ -348,7 +348,7 @@ public:
* @param now Current time * @param now Current time
* @return True if we should respond * @return True if we should respond
*/ */
inline bool shouldRespondToDirectPathPush(const uint64_t now) inline bool rateGatePushDirectPaths(const uint64_t now)
{ {
if ((now - _lastDirectPathPushReceive) <= ZT_PUSH_DIRECT_PATHS_CUTOFF_TIME) if ((now - _lastDirectPathPushReceive) <= ZT_PUSH_DIRECT_PATHS_CUTOFF_TIME)
++_directPathPushCutoffCount; ++_directPathPushCutoffCount;
@ -357,6 +357,42 @@ public:
return (_directPathPushCutoffCount < ZT_PUSH_DIRECT_PATHS_CUTOFF_LIMIT); return (_directPathPushCutoffCount < ZT_PUSH_DIRECT_PATHS_CUTOFF_LIMIT);
} }
/**
* Rate limit gate for sending of ERROR_NEED_MEMBERSHIP_CERTIFICATE
*/
inline bool rateGateRequestCredentials(const uint64_t now)
{
if ((now - _lastCredentialRequestSent) >= ZT_PEER_GENERAL_RATE_LIMIT) {
_lastCredentialRequestSent = now;
return true;
}
return false;
}
/**
* Rate limit gate for inbound WHOIS requests
*/
inline bool rateGateInboundWhoisRequest(const uint64_t now)
{
if ((now - _lastWhoisRequestReceived) >= ZT_PEER_GENERAL_RATE_LIMIT) {
_lastWhoisRequestReceived = now;
return true;
}
return false;
}
/**
* Rate limit gate for inbound ECHO requests
*/
inline bool rateGateEchoRequest(const uint64_t now)
{
if ((now - _lastEchoRequestReceived) >= ZT_PEER_GENERAL_RATE_LIMIT) {
_lastEchoRequestReceived = now;
return true;
}
return false;
}
/** /**
* 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
* *
@ -378,8 +414,6 @@ public:
} }
private: private:
bool _pushDirectPaths(const SharedPtr<Path> &path,uint64_t now);
inline uint64_t _pathScore(const unsigned int p,const uint64_t now) const inline uint64_t _pathScore(const unsigned int p,const uint64_t now) const
{ {
uint64_t s = ZT_PEER_PING_PERIOD + _paths[p].lastReceive + (uint64_t)(_paths[p].path->preferenceRank() * (ZT_PEER_PING_PERIOD / ZT_PATH_MAX_PREFERENCE_RANK)); uint64_t s = ZT_PEER_PING_PERIOD + _paths[p].lastReceive + (uint64_t)(_paths[p].path->preferenceRank() * (ZT_PEER_PING_PERIOD / ZT_PATH_MAX_PREFERENCE_RANK));
@ -415,6 +449,9 @@ private:
uint64_t _lastMulticastFrame; uint64_t _lastMulticastFrame;
uint64_t _lastDirectPathPushSent; uint64_t _lastDirectPathPushSent;
uint64_t _lastDirectPathPushReceive; uint64_t _lastDirectPathPushReceive;
uint64_t _lastCredentialRequestSent;
uint64_t _lastWhoisRequestReceived;
uint64_t _lastEchoRequestReceived;
const RuntimeEnvironment *RR; const RuntimeEnvironment *RR;
uint32_t _remoteClusterOptimal4; uint32_t _remoteClusterOptimal4;
uint16_t _vProto; uint16_t _vProto;