mirror of
https://github.com/zerotier/ZeroTierOne.git
synced 2024-12-19 04:57:53 +00:00
(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:
parent
ef87069957
commit
ab9afbc749
@ -924,13 +924,11 @@ NetworkController::ResultCode EmbeddedNetworkController::doNetworkConfigRequest(
|
||||
}
|
||||
}
|
||||
|
||||
if (_jB(network["private"],true)) {
|
||||
CertificateOfMembership com(now,credentialtmd,nwid,identity.address());
|
||||
if (com.sign(signingId)) {
|
||||
nc.com = com;
|
||||
} else {
|
||||
return NETCONF_QUERY_INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
CertificateOfMembership com(now,credentialtmd,nwid,identity.address());
|
||||
if (com.sign(signingId)) {
|
||||
nc.com = com;
|
||||
} else {
|
||||
return NETCONF_QUERY_INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
|
||||
_writeJson(memberJP,member);
|
||||
|
@ -236,6 +236,11 @@
|
||||
*/
|
||||
#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
|
||||
*
|
||||
@ -263,6 +268,11 @@
|
||||
*/
|
||||
#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
|
||||
*/
|
||||
@ -283,6 +293,11 @@
|
||||
*/
|
||||
#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
|
||||
*
|
||||
@ -326,6 +341,11 @@
|
||||
*/
|
||||
#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
|
||||
*
|
||||
|
@ -62,11 +62,8 @@ bool IncomingPacket::tryDecode(const RuntimeEnvironment *RR)
|
||||
return true;
|
||||
}
|
||||
} 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
|
||||
// special internal authentication logic. This is done for unencrypted
|
||||
// HELLOs to learn new identities, etc.
|
||||
SharedPtr<Peer> tmp;
|
||||
return _doHELLO(RR,tmp);
|
||||
// Only HELLO is allowed in the clear, but will still have a MAC
|
||||
return _doHELLO(RR,false);
|
||||
}
|
||||
|
||||
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);
|
||||
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_OK: return _doOK(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;
|
||||
}
|
||||
|
||||
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 {
|
||||
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 Address fromAddress(source());
|
||||
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()) {
|
||||
TRACE("dropped HELLO from %s(%s): identity not for sending address",fromAddress.toString().c_str(),_path->address().toString().c_str());
|
||||
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
|
||||
peer = RR->topology->getPeer(id.address());
|
||||
if (peer) {
|
||||
// We already have an identity with this address -- check for collisions
|
||||
|
||||
SharedPtr<Peer> peer(RR->topology->getPeer(id.address()));
|
||||
if (peer) {
|
||||
// We already have an identity with this address -- check for collisions
|
||||
if (!alreadyAuthenticated) {
|
||||
if (peer->identity() != id) {
|
||||
// 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
|
||||
}
|
||||
} else {
|
||||
// We don't already have an identity with this address -- validate and learn it
|
||||
} // else continue at // VALID
|
||||
} else {
|
||||
// We don't already have an identity with this address -- validate and learn it
|
||||
|
||||
// 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
|
||||
// Sanity check: this basically can't happen
|
||||
if (alreadyAuthenticated) {
|
||||
TRACE("dropped HELLO from %s(%s): somehow already authenticated with unknown peer?",id.address().toString().c_str(),_path->address().toString().c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
// 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))
|
||||
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);
|
||||
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);
|
||||
_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->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: {
|
||||
const uint64_t nwid = at<uint64_t>(ZT_PROTO_VERB_MULTICAST_GATHER__OK__IDX_NETWORK_ID);
|
||||
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));
|
||||
//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);
|
||||
@ -469,7 +471,7 @@ bool IncomingPacket::_doOK(const RuntimeEnvironment *RR,const SharedPtr<Peer> &p
|
||||
network->addCredential(com);
|
||||
}
|
||||
|
||||
if (network->gateMulticastGather(peer,verb(),packetId())) {
|
||||
if (network->gateMulticastGatherReply(peer,verb(),packetId())) {
|
||||
if ((flags & 0x02) != 0) {
|
||||
// OK(MULTICAST_FRAME) includes implicit 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)
|
||||
{
|
||||
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);
|
||||
outp.append((unsigned char)Packet::VERB_WHOIS);
|
||||
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)
|
||||
{
|
||||
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();
|
||||
Packet outp(peer->address(),RR->identity.address(),Packet::VERB_OK);
|
||||
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.armor(peer->key(),true);
|
||||
_path->send(RR,outp.data(),outp.size(),RR->node->now());
|
||||
|
||||
peer->received(_path,hops(),pid,Packet::VERB_ECHO,0,Packet::VERB_NOP,false);
|
||||
} catch ( ... ) {
|
||||
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 {
|
||||
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
|
||||
for(unsigned int ptr=ZT_PACKET_IDX_PAYLOAD;ptr<size();ptr+=18) {
|
||||
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);
|
||||
@ -721,7 +758,7 @@ bool IncomingPacket::_doNETWORK_CREDENTIALS(const RuntimeEnvironment *RR,const S
|
||||
if (network) {
|
||||
if (network->addCredential(com) == 1)
|
||||
return false; // wait for WHOIS
|
||||
}
|
||||
} else RR->mc->addCredential(com,false);
|
||||
}
|
||||
}
|
||||
++p; // skip trailing 0 after COMs if present
|
||||
@ -759,22 +796,21 @@ bool IncomingPacket::_doNETWORK_CONFIG_REQUEST(const RuntimeEnvironment *RR,cons
|
||||
{
|
||||
try {
|
||||
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 uint64_t requestPacketId = packetId();
|
||||
bool netconfOk = false;
|
||||
bool trustEstablished = false;
|
||||
|
||||
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();
|
||||
try {
|
||||
switch(RR->localNetworkController->doNetworkConfigRequest((hopCount > 0) ? InetAddress() : _path->address(),RR->identity,peer->identity(),nwid,metaData,*netconf)) {
|
||||
|
||||
case NetworkController::NETCONF_QUERY_OK: {
|
||||
netconfOk = true;
|
||||
trustEstablished = true;
|
||||
Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> *dconf = new Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY>();
|
||||
try {
|
||||
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());
|
||||
}
|
||||
|
||||
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) {
|
||||
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());
|
||||
@ -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());
|
||||
|
||||
const SharedPtr<Network> network(RR->node->network(nwid));
|
||||
|
||||
if ((flags & 0x01) != 0) {
|
||||
try {
|
||||
CertificateOfMembership com;
|
||||
com.deserialize(*this,ZT_PROTO_VERB_MULTICAST_GATHER_IDX_COM);
|
||||
if (com) {
|
||||
SharedPtr<Network> network(RR->node->network(nwid));
|
||||
if (network)
|
||||
network->addCredential(com);
|
||||
else RR->mc->addCredential(com,false);
|
||||
}
|
||||
} catch ( ... ) {
|
||||
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);
|
||||
outp.append((unsigned char)Packet::VERB_MULTICAST_GATHER);
|
||||
outp.append(packetId());
|
||||
@ -1043,7 +1081,7 @@ bool IncomingPacket::_doPUSH_DIRECT_PATHS(const RuntimeEnvironment *RR,const Sha
|
||||
const uint64_t now = RR->node->now();
|
||||
|
||||
// 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());
|
||||
peer->received(_path,hops(),packetId(),Packet::VERB_PUSH_DIRECT_PATHS,0,Packet::VERB_NOP,false);
|
||||
return true;
|
||||
|
@ -136,7 +136,7 @@ private:
|
||||
// These are called internally to handle packet contents once it has
|
||||
// been authenticated, decrypted, decompressed, and classified.
|
||||
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 _doWHOIS(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
|
||||
bool _doRENDEZVOUS(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
|
||||
|
@ -71,7 +71,7 @@ void Membership::sendCredentialsIfNeeded(const RuntimeEnvironment *RR,const uint
|
||||
}
|
||||
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) ) {
|
||||
Packet outp(peerAddress,RR->identity.address(),Packet::VERB_NETWORK_CREDENTIALS);
|
||||
if (needCom) {
|
||||
|
@ -34,8 +34,8 @@ namespace ZeroTier {
|
||||
|
||||
Multicaster::Multicaster(const RuntimeEnvironment *renv) :
|
||||
RR(renv),
|
||||
_groups(1024),
|
||||
_groups_m()
|
||||
_groups(256),
|
||||
_gatherAuth(256)
|
||||
{
|
||||
}
|
||||
|
||||
@ -244,7 +244,7 @@ void Multicaster::send(
|
||||
}
|
||||
|
||||
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);
|
||||
outp.append(nwid);
|
||||
outp.append((uint8_t)((com) ? 0x01 : 0x00));
|
||||
@ -301,42 +301,62 @@ void Multicaster::send(
|
||||
|
||||
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;
|
||||
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;
|
||||
}
|
||||
|
||||
unsigned long count = 0;
|
||||
{
|
||||
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;
|
||||
unsigned long count = 0;
|
||||
{
|
||||
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;
|
||||
}
|
||||
++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);
|
||||
} else if (s->txQueue.empty()) {
|
||||
_groups.erase(*k);
|
||||
} else {
|
||||
s->members.clear();
|
||||
{
|
||||
Mutex::Lock _l(_gatherAuth_m);
|
||||
_GatherAuthKey *k = (_GatherAuthKey *)0;
|
||||
uint64_t *ts = (uint64_t *)ts;
|
||||
Hashtable<_GatherAuthKey,uint64_t>::Iterator i(_gatherAuth);
|
||||
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)
|
||||
{
|
||||
// assumes _groups_m is locked
|
||||
|
@ -179,12 +179,52 @@ public:
|
||||
*/
|
||||
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:
|
||||
void _add(uint64_t now,uint64_t nwid,const MulticastGroup &mg,MulticastGroupStatus &gs,const Address &member);
|
||||
|
||||
const RuntimeEnvironment *RR;
|
||||
|
||||
Hashtable<Multicaster::Key,MulticastGroupStatus> _groups;
|
||||
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
|
||||
|
@ -866,31 +866,24 @@ bool Network::subscribedToMulticastGroup(const MulticastGroup &mg,bool includeBr
|
||||
return true;
|
||||
else if (includeBridgedGroups)
|
||||
return _multicastGroupsBehindMe.contains(mg);
|
||||
else return false;
|
||||
return false;
|
||||
}
|
||||
|
||||
void Network::multicastSubscribe(const MulticastGroup &mg)
|
||||
{
|
||||
{
|
||||
Mutex::Lock _l(_lock);
|
||||
if (std::binary_search(_myMulticastGroups.begin(),_myMulticastGroups.end(),mg))
|
||||
return;
|
||||
_myMulticastGroups.push_back(mg);
|
||||
std::sort(_myMulticastGroups.begin(),_myMulticastGroups.end());
|
||||
_pushStateToMembers(&mg);
|
||||
Mutex::Lock _l(_lock);
|
||||
if (!std::binary_search(_myMulticastGroups.begin(),_myMulticastGroups.end(),mg)) {
|
||||
_myMulticastGroups.insert(std::upper_bound(_myMulticastGroups.begin(),_myMulticastGroups.end(),mg),mg);
|
||||
_sendUpdatesToMembers(&mg);
|
||||
}
|
||||
}
|
||||
|
||||
void Network::multicastUnsubscribe(const MulticastGroup &mg)
|
||||
{
|
||||
Mutex::Lock _l(_lock);
|
||||
std::vector<MulticastGroup> nmg;
|
||||
for(std::vector<MulticastGroup>::const_iterator i(_myMulticastGroups.begin());i!=_myMulticastGroups.end();++i) {
|
||||
if (*i != mg)
|
||||
nmg.push_back(*i);
|
||||
}
|
||||
if (nmg.size() != _myMulticastGroups.size())
|
||||
_myMulticastGroups.swap(nmg);
|
||||
std::vector<MulticastGroup>::iterator i(std::lower_bound(_myMulticastGroups.begin(),_myMulticastGroups.end(),mg));
|
||||
if ( (i != _myMulticastGroups.end()) && (*i == mg) )
|
||||
_myMulticastGroups.erase(i);
|
||||
}
|
||||
|
||||
bool Network::applyConfiguration(const NetworkConfig &conf)
|
||||
@ -1054,30 +1047,29 @@ void Network::requestConfiguration()
|
||||
} else {
|
||||
outp.append((unsigned char)0,16);
|
||||
}
|
||||
RR->node->expectReplyTo(outp.packetId());
|
||||
|
||||
RR->node->expectReplyTo(_inboundConfigPacketId = outp.packetId());
|
||||
_inboundConfigChunks.clear();
|
||||
|
||||
outp.compress();
|
||||
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)
|
||||
{
|
||||
const uint64_t now = RR->node->now();
|
||||
Mutex::Lock _l(_lock);
|
||||
try {
|
||||
if (_config) {
|
||||
Membership &m = _membership(peer->address());
|
||||
const bool allow = m.isAllowedOnNetwork(_config);
|
||||
if (allow) {
|
||||
const uint64_t now = RR->node->now();
|
||||
m.sendCredentialsIfNeeded(RR,now,peer->address(),_config,(const Capability *)0);
|
||||
if (m.shouldLikeMulticasts(now)) {
|
||||
_announceMulticastGroupsTo(peer->address(),_allMulticastGroups());
|
||||
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);
|
||||
outp.append((uint8_t)verb);
|
||||
outp.append(packetId);
|
||||
@ -1093,7 +1085,7 @@ bool Network::gate(const SharedPtr<Peer> &peer,const Packet::Verb verb,const uin
|
||||
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()) );
|
||||
}
|
||||
@ -1180,7 +1172,22 @@ void Network::learnBridgedMulticastGroup(const MulticastGroup &mg,uint64_t now)
|
||||
const unsigned long tmp = (unsigned long)_multicastGroupsBehindMe.size();
|
||||
_multicastGroupsBehindMe.set(mg,now);
|
||||
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()
|
||||
@ -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
|
||||
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.
|
||||
const std::vector<Address> upstreams(RR->topology->upstreamAddresses());
|
||||
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);
|
||||
_config.com.serialize(outp);
|
||||
outp.append((uint8_t)0x00);
|
||||
@ -1272,12 +1279,17 @@ void Network::_pushStateToMembers(const MulticastGroup *const newMulticastGroup)
|
||||
_announceMulticastGroupsTo(*a,groups);
|
||||
}
|
||||
|
||||
// Announce to controller, which does not need our COM since it obviously
|
||||
// knows if we are a member. Of course if we already did or are going to
|
||||
// below then we can skip it here.
|
||||
// Also announce to controller, and send COM to simplify and generalize behavior even though in theory it does not need it
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure that all "network anchors" have Membership records so we will
|
||||
|
@ -260,7 +260,7 @@ public:
|
||||
/**
|
||||
* 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
|
||||
@ -276,10 +276,10 @@ public:
|
||||
/**
|
||||
* Push state to members such as multicast group memberships and latest COM (if needed)
|
||||
*/
|
||||
inline void pushStateToMembers()
|
||||
inline void sendUpdatesToMembers()
|
||||
{
|
||||
Mutex::Lock _l(_lock);
|
||||
_pushStateToMembers((const MulticastGroup *)0);
|
||||
_sendUpdatesToMembers((const MulticastGroup *)0);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -332,9 +332,7 @@ public:
|
||||
{
|
||||
Mutex::Lock _l(_lock);
|
||||
const Address *const br = _remoteBridgeRoutes.get(mac);
|
||||
if (br)
|
||||
return *br;
|
||||
return Address();
|
||||
return ((br) ? *br : Address());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -357,13 +355,7 @@ public:
|
||||
* @param com Certificate of membership
|
||||
* @return 0 == OK, 1 == waiting for WHOIS, -1 == BAD signature or credential
|
||||
*/
|
||||
inline int addCredential(const CertificateOfMembership &com)
|
||||
{
|
||||
if (com.networkId() != _id)
|
||||
return -1;
|
||||
Mutex::Lock _l(_lock);
|
||||
return _membership(com.issuedTo()).addCredential(RR,com);
|
||||
}
|
||||
int addCredential(const CertificateOfMembership &com);
|
||||
|
||||
/**
|
||||
* @param cap Capability
|
||||
@ -418,7 +410,7 @@ private:
|
||||
ZT_VirtualNetworkStatus _status() const;
|
||||
void _externalConfig(ZT_VirtualNetworkConfig *ec) const; // assumes _lock is locked
|
||||
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);
|
||||
std::vector<MulticastGroup> _allMulticastGroups() const;
|
||||
Membership &_membership(const Address &a);
|
||||
|
@ -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) {
|
||||
if (((now - n->second->lastConfigUpdate()) >= ZT_NETWORK_AUTOCONF_DELAY)||(!n->second->hasConfig()))
|
||||
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)
|
||||
|
@ -104,6 +104,7 @@ public:
|
||||
Path() :
|
||||
_lastOut(0),
|
||||
_lastIn(0),
|
||||
_lastHello(0),
|
||||
_addr(),
|
||||
_localAddress(),
|
||||
_ipScope(InetAddress::IP_SCOPE_NONE)
|
||||
@ -113,6 +114,7 @@ public:
|
||||
Path(const InetAddress &localAddress,const InetAddress &addr) :
|
||||
_lastOut(0),
|
||||
_lastIn(0),
|
||||
_lastHello(0),
|
||||
_addr(addr),
|
||||
_localAddress(localAddress),
|
||||
_ipScope(addr.ipScope())
|
||||
@ -229,9 +231,22 @@ public:
|
||||
*/
|
||||
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:
|
||||
uint64_t _lastOut;
|
||||
uint64_t _lastIn;
|
||||
uint64_t _lastHello;
|
||||
InetAddress _addr;
|
||||
InetAddress _localAddress;
|
||||
InetAddress::IpScope _ipScope; // memoize this since it's a computed value checked often
|
||||
|
160
node/Peer.cpp
160
node/Peer.cpp
@ -47,6 +47,9 @@ Peer::Peer(const RuntimeEnvironment *renv,const Identity &myIdentity,const Ident
|
||||
_lastMulticastFrame(0),
|
||||
_lastDirectPathPushSent(0),
|
||||
_lastDirectPathPushReceive(0),
|
||||
_lastCredentialRequestSent(0),
|
||||
_lastWhoisRequestReceived(0),
|
||||
_lastEchoRequestReceived(0),
|
||||
RR(renv),
|
||||
_remoteClusterOptimal4(0),
|
||||
_vProto(0),
|
||||
@ -194,7 +197,80 @@ void Peer::received(
|
||||
}
|
||||
} else if (trustEstablished) {
|
||||
// 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();
|
||||
}
|
||||
|
||||
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
|
||||
|
@ -348,7 +348,7 @@ public:
|
||||
* @param now Current time
|
||||
* @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)
|
||||
++_directPathPushCutoffCount;
|
||||
@ -357,6 +357,42 @@ public:
|
||||
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
|
||||
*
|
||||
@ -378,8 +414,6 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
bool _pushDirectPaths(const SharedPtr<Path> &path,uint64_t now);
|
||||
|
||||
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));
|
||||
@ -415,6 +449,9 @@ private:
|
||||
uint64_t _lastMulticastFrame;
|
||||
uint64_t _lastDirectPathPushSent;
|
||||
uint64_t _lastDirectPathPushReceive;
|
||||
uint64_t _lastCredentialRequestSent;
|
||||
uint64_t _lastWhoisRequestReceived;
|
||||
uint64_t _lastEchoRequestReceived;
|
||||
const RuntimeEnvironment *RR;
|
||||
uint32_t _remoteClusterOptimal4;
|
||||
uint16_t _vProto;
|
||||
|
Loading…
Reference in New Issue
Block a user