diff --git a/node/Bond.cpp b/node/Bond.cpp index 2a061796d..7dbd12ef3 100644 --- a/node/Bond.cpp +++ b/node/Bond.cpp @@ -854,7 +854,7 @@ void Bond::sendPATH_NEGOTIATION_REQUEST(void* tPtr, int pathIdx) outp.append(_localUtility); if (_paths[pathIdx].p->address()) { Metrics::pkt_path_negotiation_request_out++; - outp.armor(_peer->key(), false, _peer->aesKeysIfSupported()); + outp.armor(_peer->key(), true, false, _peer->aesKeysIfSupported(), _peer->identity()); RR->node->putPacket(tPtr, _paths[pathIdx].p->localSocket(), _paths[pathIdx].p->address(), outp.data(), outp.size()); _overheadBytes += outp.size(); } @@ -895,7 +895,7 @@ void Bond::sendQOS_MEASUREMENT(void* tPtr, int pathIdx, int64_t localSocket, con // debug("sending QOS via link %s (len=%d)", pathToStr(_paths[pathIdx].p).c_str(), len); outp.append(qosData, len); if (atAddress) { - outp.armor(_peer->key(), false, _peer->aesKeysIfSupported()); + outp.armor(_peer->key(), true, false, _peer->aesKeysIfSupported(), _peer->identity()); RR->node->putPacket(tPtr, localSocket, atAddress, outp.data(), outp.size()); } else { @@ -933,7 +933,7 @@ void Bond::processBackgroundBondTasks(void* tPtr, int64_t now) if ((_monitorInterval > 0) && (((now - _paths[i].p->_lastIn) >= (_paths[i].alive ? _monitorInterval : _failoverInterval)))) { if ((_peer->remoteVersionProtocol() >= 5) && (! ((_peer->remoteVersionMajor() == 1) && (_peer->remoteVersionMinor() == 1) && (_peer->remoteVersionRevision() == 0)))) { Packet outp(_peer->address(), RR->identity.address(), Packet::VERB_ECHO); // ECHO (this is our bond's heartbeat) - outp.armor(_peer->key(), true, _peer->aesKeysIfSupported()); + outp.armor(_peer->key(), true, false, _peer->aesKeysIfSupported(), _peer->identity()); RR->node->expectReplyTo(outp.packetId()); RR->node->putPacket(tPtr, _paths[i].p->localSocket(), _paths[i].p->address(), outp.data(), outp.size()); _paths[i].p->_lastOut = now; diff --git a/node/Credential.hpp b/node/Credential.hpp index 7fb98ba79..a1321f664 100644 --- a/node/Credential.hpp +++ b/node/Credential.hpp @@ -14,17 +14,6 @@ #ifndef ZT_CREDENTIAL_HPP #define ZT_CREDENTIAL_HPP -#include -#include -#include - -#include -#include -#include -#include - -#include "Constants.hpp" - namespace ZeroTier { /** diff --git a/node/IncomingPacket.cpp b/node/IncomingPacket.cpp index 2537c0fb6..1404f8c10 100644 --- a/node/IncomingPacket.cpp +++ b/node/IncomingPacket.cpp @@ -68,7 +68,7 @@ bool IncomingPacket::tryDecode(const RuntimeEnvironment *RR,void *tPtr,int32_t f const SharedPtr peer(RR->topology->getPeer(tPtr,sourceAddress)); if (peer) { if (!_authenticated) { - if (!dearmor(peer->key(), peer->aesKeys())) { + if (!dearmor(peer->key(), peer->aesKeys(), RR->identity)) { RR->t->incomingPacketMessageAuthenticationFailure(tPtr,_path,packetId(),sourceAddress,hops(),"invalid MAC"); peer->recordIncomingInvalidPacket(_path); return true; @@ -398,13 +398,13 @@ bool IncomingPacket::_doHELLO(const RuntimeEnvironment *RR,void *tPtr,const bool uint8_t key[ZT_SYMMETRIC_KEY_SIZE]; if (RR->identity.agree(id,key)) { - if (dearmor(key, peer->aesKeysIfSupported())) { // ensure packet is authentic, otherwise drop + if (dearmor(key, peer->aesKeysIfSupported(), RR->identity)) { // ensure packet is authentic, otherwise drop RR->t->incomingPacketDroppedHELLO(tPtr,_path,pid,fromAddress,"address collision"); Packet outp(id.address(),RR->identity.address(),Packet::VERB_ERROR); outp.append((uint8_t)Packet::VERB_HELLO); outp.append((uint64_t)pid); outp.append((uint8_t)Packet::ERROR_IDENTITY_COLLISION); - outp.armor(key,true,peer->aesKeysIfSupported()); + outp.armor(key,true,false,peer->aesKeysIfSupported(),peer->identity()); Metrics::pkt_error_out++; Metrics::pkt_error_identity_collision_out++; _path->send(RR,tPtr,outp.data(),outp.size(),RR->node->now()); @@ -419,7 +419,7 @@ bool IncomingPacket::_doHELLO(const RuntimeEnvironment *RR,void *tPtr,const bool } else { // Identity is the same as the one we already have -- check packet integrity - if (!dearmor(peer->key(), peer->aesKeysIfSupported())) { + if (!dearmor(peer->key(), peer->aesKeysIfSupported(), RR->identity)) { RR->t->incomingPacketMessageAuthenticationFailure(tPtr,_path,pid,fromAddress,hops(),"invalid MAC"); return true; } @@ -444,7 +444,7 @@ bool IncomingPacket::_doHELLO(const RuntimeEnvironment *RR,void *tPtr,const bool // Check packet integrity and MAC (this is faster than locallyValidate() so do it first to filter out total crap) SharedPtr newPeer(new Peer(RR,RR->identity,id)); - if (!dearmor(newPeer->key(), newPeer->aesKeysIfSupported())) { + if (!dearmor(newPeer->key(), newPeer->aesKeysIfSupported(), RR->identity)) { RR->t->incomingPacketMessageAuthenticationFailure(tPtr,_path,pid,fromAddress,hops(),"invalid MAC"); return true; } @@ -510,38 +510,7 @@ bool IncomingPacket::_doHELLO(const RuntimeEnvironment *RR,void *tPtr,const bool outp.append((unsigned char)ZEROTIER_ONE_VERSION_MAJOR); outp.append((unsigned char)ZEROTIER_ONE_VERSION_MINOR); outp.append((uint16_t)ZEROTIER_ONE_VERSION_REVISION); - - if (protoVersion >= 5) { - _path->address().serialize(outp); - } else { - /* LEGACY COMPATIBILITY HACK: - * - * For a while now (since 1.0.3), ZeroTier has recognized changes in - * its network environment empirically by examining its external network - * address as reported by trusted peers. In versions prior to 1.1.0 - * (protocol version < 5), they did this by saving a snapshot of this - * information (in SelfAwareness.hpp) keyed by reporting device ID and - * address type. - * - * This causes problems when clustering is combined with symmetric NAT. - * Symmetric NAT remaps ports, so different endpoints in a cluster will - * report back different exterior addresses. Since the old code keys - * this by device ID and not sending physical address and compares the - * entire address including port, it constantly thinks its external - * surface is changing and resets connections when talking to a cluster. - * - * In new code we key by sending physical address and device and we also - * take the more conservative position of only interpreting changes in - * IP address (neglecting port) as a change in network topology that - * necessitates a reset. But we can make older clients work here by - * nulling out the port field. Since this info is only used for empirical - * detection of link changes, it doesn't break anything else. - */ - InetAddress tmpa(_path->address()); - tmpa.setPort(0); - tmpa.serialize(outp); - } - + _path->address().serialize(outp); const unsigned int worldUpdateSizeAt = outp.size(); outp.addSize(2); // make room for 16-bit size field if ((planetWorldId)&&(RR->topology->planetWorldTimestamp() > planetWorldTimestamp)&&(planetWorldId == RR->topology->planetWorldId())) { @@ -562,7 +531,7 @@ bool IncomingPacket::_doHELLO(const RuntimeEnvironment *RR,void *tPtr,const bool } outp.setAt(worldUpdateSizeAt,(uint16_t)(outp.size() - (worldUpdateSizeAt + 2))); - outp.armor(peer->key(),true,peer->aesKeysIfSupported()); + outp.armor(peer->key(),true,false,peer->aesKeysIfSupported(),peer->identity()); peer->recordOutgoingPacket(_path,outp.packetId(),outp.payloadLength(),outp.verb(),ZT_QOS_NO_FLOW,now); Metrics::pkt_ok_out++; _path->send(RR,tPtr,outp.data(),outp.size(),now); @@ -724,7 +693,7 @@ bool IncomingPacket::_doWHOIS(const RuntimeEnvironment *RR,void *tPtr,const Shar if (count > 0) { Metrics::pkt_ok_out++; - outp.armor(peer->key(),true,peer->aesKeysIfSupported()); + outp.armor(peer->key(),true,false,peer->aesKeysIfSupported(),RR->identity); _path->send(RR,tPtr,outp.data(),outp.size(),RR->node->now()); } @@ -952,7 +921,7 @@ bool IncomingPacket::_doEXT_FRAME(const RuntimeEnvironment *RR,void *tPtr,const outp.append((uint64_t)packetId()); outp.append((uint64_t)nwid); const int64_t now = RR->node->now(); - outp.armor(peer->key(),true,peer->aesKeysIfSupported()); + outp.armor(peer->key(),true,false,peer->aesKeysIfSupported(),peer->identity()); peer->recordOutgoingPacket(_path,outp.packetId(),outp.payloadLength(),outp.verb(),ZT_QOS_NO_FLOW,now); Metrics::pkt_ok_out++; _path->send(RR,tPtr,outp.data(),outp.size(),RR->node->now()); @@ -981,7 +950,7 @@ bool IncomingPacket::_doECHO(const RuntimeEnvironment *RR,void *tPtr,const Share if (size() > ZT_PACKET_IDX_PAYLOAD) { outp.append(reinterpret_cast(data()) + ZT_PACKET_IDX_PAYLOAD,size() - ZT_PACKET_IDX_PAYLOAD); } - outp.armor(peer->key(),true,peer->aesKeysIfSupported()); + outp.armor(peer->key(),true,false,peer->aesKeysIfSupported(),peer->identity()); peer->recordOutgoingPacket(_path,outp.packetId(),outp.payloadLength(),outp.verb(),ZT_QOS_NO_FLOW,now); Metrics::pkt_ok_out++; _path->send(RR,tPtr,outp.data(),outp.size(),RR->node->now()); @@ -1177,7 +1146,7 @@ bool IncomingPacket::_doNETWORK_CONFIG_REQUEST(const RuntimeEnvironment *RR,void outp.append(requestPacketId); outp.append((unsigned char)Packet::ERROR_UNSUPPORTED_OPERATION); outp.append(nwid); - outp.armor(peer->key(),true,peer->aesKeysIfSupported()); + outp.armor(peer->key(),true,false,peer->aesKeysIfSupported(),peer->identity()); Metrics::pkt_error_out++; Metrics::pkt_error_unsupported_op_out++; _path->send(RR,tPtr,outp.data(),outp.size(),RR->node->now()); @@ -1201,7 +1170,7 @@ bool IncomingPacket::_doNETWORK_CONFIG(const RuntimeEnvironment *RR,void *tPtr,c outp.append((uint64_t)network->id()); outp.append((uint64_t)configUpdateId); const int64_t now = RR->node->now(); - outp.armor(peer->key(),true,peer->aesKeysIfSupported()); + outp.armor(peer->key(),true,false,peer->aesKeysIfSupported(),peer->identity()); peer->recordOutgoingPacket(_path,outp.packetId(),outp.payloadLength(),outp.verb(),ZT_QOS_NO_FLOW,now); Metrics::pkt_ok_out++; _path->send(RR,tPtr,outp.data(),outp.size(),RR->node->now()); @@ -1244,7 +1213,7 @@ bool IncomingPacket::_doMULTICAST_GATHER(const RuntimeEnvironment *RR,void *tPtr outp.append((uint32_t)mg.adi()); const unsigned int gatheredLocally = RR->mc->gather(peer->address(),nwid,mg,outp,gatherLimit); if (gatheredLocally > 0) { - outp.armor(peer->key(),true,peer->aesKeysIfSupported()); + outp.armor(peer->key(),true,false,peer->aesKeysIfSupported(),peer->identity()); peer->recordOutgoingPacket(_path,outp.packetId(),outp.payloadLength(),outp.verb(),ZT_QOS_NO_FLOW,now); Metrics::pkt_ok_out++; _path->send(RR,tPtr,outp.data(),outp.size(),now); @@ -1348,7 +1317,7 @@ bool IncomingPacket::_doMULTICAST_FRAME(const RuntimeEnvironment *RR,void *tPtr, outp.append((unsigned char)0x02); // flag 0x02 = contains gather results if (RR->mc->gather(peer->address(),nwid,to,outp,gatherLimit)) { const int64_t now = RR->node->now(); - outp.armor(peer->key(),true,peer->aesKeysIfSupported()); + outp.armor(peer->key(),true,false,peer->aesKeysIfSupported(),peer->identity()); peer->recordOutgoingPacket(_path,outp.packetId(),outp.payloadLength(),outp.verb(),ZT_QOS_NO_FLOW,now); Metrics::pkt_ok_out++; _path->send(RR,tPtr,outp.data(),outp.size(),RR->node->now()); @@ -1490,7 +1459,7 @@ void IncomingPacket::_sendErrorNeedCredentials(const RuntimeEnvironment *RR,void outp.append(packetId()); outp.append((uint8_t)Packet::ERROR_NEED_MEMBERSHIP_CERTIFICATE); outp.append(nwid); - outp.armor(peer->key(),true,peer->aesKeysIfSupported()); + outp.armor(peer->key(),true,false,peer->aesKeysIfSupported(),peer->identity()); Metrics::pkt_error_out++; Metrics::pkt_error_need_membership_cert_out++; _path->send(RR,tPtr,outp.data(),outp.size(),RR->node->now()); diff --git a/node/Multicaster.cpp b/node/Multicaster.cpp index 0b49077cd..20fa17dd4 100644 --- a/node/Multicaster.cpp +++ b/node/Multicaster.cpp @@ -198,7 +198,7 @@ void Multicaster::send( if (!network->config().disableCompression()) { outp.compress(); } - outp.armor(bestMulticastReplicator->key(),true,bestMulticastReplicator->aesKeysIfSupported()); + outp.armor(bestMulticastReplicator->key(),true,false,bestMulticastReplicator->aesKeysIfSupported(),bestMulticastReplicator->identity()); Metrics::pkt_multicast_frame_out++; bestMulticastReplicatorPath->send(RR,tPtr,outp.data(),outp.size(),now); return; diff --git a/node/Packet.cpp b/node/Packet.cpp index 9c09736ed..99100c69d 100644 --- a/node/Packet.cpp +++ b/node/Packet.cpp @@ -18,6 +18,7 @@ #include #include "Packet.hpp" +#include "ECC.hpp" #if defined(ZT_USE_X64_ASM_SALSA2012) && defined(ZT_ARCH_X64) #include "../ext/x64-salsa2012-asm/salsa2012.h" @@ -1009,8 +1010,7 @@ void Packet::armor(const void *key,bool encryptPayload,bool extendedArmor,const { uint8_t *const data = reinterpret_cast(unsafeData()); - if (extendedArmor) { - } + this->setExtendedArmor(extendedArmor); if ((aesKeys) && (encryptPayload)) { setCipher(ZT_PROTO_CIPHER_SUITE__AES_GMAC_SIV); @@ -1042,19 +1042,22 @@ void Packet::armor(const void *key,bool encryptPayload,bool extendedArmor,const if (ZT_HAS_FAST_CRYPTO()) { const unsigned int payloadLen = (encryptPayload) ? (size() - ZT_PACKET_IDX_VERB) : 0; uint64_t keyStream[(ZT_PROTO_MAX_PACKET_LENGTH + 64 + 8) / 8]; + uint64_t mac[2]; + ZT_FAST_SINGLE_PASS_SALSA2012(keyStream,payloadLen + 64,(data + ZT_PACKET_IDX_IV),mangledKey); Salsa20::memxor(data + ZT_PACKET_IDX_VERB,reinterpret_cast(keyStream + 8),payloadLen); - uint64_t mac[2]; Poly1305::compute(mac,data + ZT_PACKET_IDX_VERB,size() - ZT_PACKET_IDX_VERB,keyStream); + #ifdef ZT_NO_TYPE_PUNNING memcpy(data + ZT_PACKET_IDX_MAC,mac,8); #else (*reinterpret_cast(data + ZT_PACKET_IDX_MAC)) = mac[0]; #endif } else { - Salsa20 s20(mangledKey,data + ZT_PACKET_IDX_IV); - uint64_t macKey[4]; + uint64_t mac[2]; + + Salsa20 s20(mangledKey,data + ZT_PACKET_IDX_IV); s20.crypt12(ZERO_KEY,macKey,sizeof(macKey)); uint8_t *const payload = data + ZT_PACKET_IDX_VERB; @@ -1062,17 +1065,51 @@ void Packet::armor(const void *key,bool encryptPayload,bool extendedArmor,const if (encryptPayload) { s20.crypt12(payload,payload,payloadLen); } - uint64_t mac[2]; Poly1305::compute(mac,payload,payloadLen,macKey); memcpy(data + ZT_PACKET_IDX_MAC,mac,8); } } + + if (extendedArmor) { + ECC::Pair ephemeralKeyPair = ECC::generate(); + uint8_t ephemeralSymmetric[32]; + ECC::agree(ephemeralKeyPair, identity.publicKey(), ephemeralSymmetric, 32); + + AES cipher(ephemeralSymmetric); + AES::CTR aesCtr(cipher); + aesCtr.init(data, 0, data + ZT_PACKET_IDX_EXTENDED_ARMOR_START); + aesCtr.crypt(data + ZT_PACKET_IDX_EXTENDED_ARMOR_START, size() - ZT_PACKET_IDX_EXTENDED_ARMOR_START); + + this->append(ephemeralKeyPair.pub.data, ZT_ECC_EPHEMERAL_PUBLIC_KEY_LEN); + } } bool Packet::dearmor(const void *key,const AES aesKeys[2],const Identity &identity) { uint8_t *const data = reinterpret_cast(unsafeData()); + + if (extendedArmor()) { + if (size() < ZT_ECC_EPHEMERAL_PUBLIC_KEY_LEN) { + return false; + } + uint8_t ephemeralSymmetric[32]; + ECC::Public ephemeralKey; + memcpy(ephemeralKey.data, data + (size() - ZT_ECC_EPHEMERAL_PUBLIC_KEY_LEN), ZT_ECC_EPHEMERAL_PUBLIC_KEY_LEN); + ECC::agree(identity.privateKeyPair(), ephemeralKey, ephemeralSymmetric, 32); + + AES cipher(ephemeralSymmetric); + AES::CTR aesCtr(cipher); + aesCtr.init(data, 0, data + ZT_PACKET_IDX_EXTENDED_ARMOR_START); + aesCtr.crypt(data + ZT_PACKET_IDX_EXTENDED_ARMOR_START, size() - ZT_PACKET_IDX_EXTENDED_ARMOR_START); + + this->setSize(size() - ZT_ECC_EPHEMERAL_PUBLIC_KEY_LEN); + + /* Note: both the MAC and the data were encrypted with the ephemeral key. We don't need + * a separate MAC for the ephemeral encryption because the MAC check below is obviously + * going to fail if the ephemeral key was incorrect. */ + } + const unsigned int payloadLen = size() - ZT_PACKET_IDX_VERB; unsigned char *const payload = data + ZT_PACKET_IDX_VERB; const unsigned int cs = cipher(); @@ -1080,6 +1117,7 @@ bool Packet::dearmor(const void *key,const AES aesKeys[2],const Identity &identi if (cs == ZT_PROTO_CIPHER_SUITE__AES_GMAC_SIV) { if (aesKeys) { uint64_t tag[2]; + #ifdef ZT_NO_UNALIGNED_ACCESS Utils::copy<8>(tag, data); Utils::copy<8>(tag + 1, data + ZT_PACKET_IDX_MAC); diff --git a/node/Packet.hpp b/node/Packet.hpp index da889cae4..703854385 100644 --- a/node/Packet.hpp +++ b/node/Packet.hpp @@ -1182,6 +1182,25 @@ class Packet : public Buffer } } + /** + * @return True if packet is encrypted with an extra ephemeral key + */ + inline bool extendedArmor() const { return (((unsigned char)(*this)[ZT_PACKET_IDX_FLAGS] & ZT_PROTO_FLAG_EXTENDED_ARMOR) != 0); } + + /** + * Set this packet's extended armor flag + * + * @param f Extended armor flag value + */ + inline void setExtendedArmor(bool f) + { + if (f) { + (*this)[ZT_PACKET_IDX_FLAGS] |= (char)ZT_PROTO_FLAG_EXTENDED_ARMOR; + } else { + (*this)[ZT_PACKET_IDX_FLAGS] &= (char)(~ZT_PROTO_FLAG_EXTENDED_ARMOR); + } + } + /** * @return True if compressed (result only valid if unencrypted) */ @@ -1287,6 +1306,8 @@ class Packet : public Buffer * * @param key 32-byte key * @param encryptPayload If true, encrypt packet payload, else just MAC + * @param extendedArmor Use an ephemeral key to encrypt payload (for encrypted HELLO) + * @param identity Identity of packet recipient/destination * @param aesKeys If non-NULL these are the two keys for AES-GMAC-SIV */ void armor(const void *key,bool encryptPayload,bool extendedArmor,const AES aesKeys[2],const Identity &identity); @@ -1300,6 +1321,7 @@ class Packet : public Buffer * * @param key 32-byte key * @param aesKeys If non-NULL these are the two keys for AES-GMAC-SIV + * @param identity Receiver's identity (must include secret) * @return False if packet is invalid or failed MAC authenticity check */ bool dearmor(const void *key,const AES aesKeys[2],const Identity &identity); diff --git a/node/Peer.cpp b/node/Peer.cpp index f77b4e6fd..0b0cea7dd 100644 --- a/node/Peer.cpp +++ b/node/Peer.cpp @@ -14,6 +14,7 @@ #include "../version.h" #include "Constants.hpp" #include "Peer.hpp" +#include "Identity.hpp" #include "Switch.hpp" #include "Network.hpp" #include "SelfAwareness.hpp" @@ -29,674 +30,675 @@ namespace ZeroTier { static unsigned char s_freeRandomByteCounter = 0; Peer::Peer(const RuntimeEnvironment *renv,const Identity &myIdentity,const Identity &peerIdentity) - : RR(renv) - , _lastReceive(0) - , _lastNontrivialReceive(0) - , _lastTriedMemorizedPath(0) - , _lastDirectPathPushSent(0) - , _lastDirectPathPushReceive(0) - , _lastCredentialRequestSent(0) - , _lastWhoisRequestReceived(0) - , _lastCredentialsReceived(0) - , _lastTrustEstablishedPacketReceived(0) - , _lastSentFullHello(0) - , _lastEchoCheck(0) - , _freeRandomByte((unsigned char)((uintptr_t)this >> 4) ^ ++s_freeRandomByteCounter) - , _vProto(0) - , _vMajor(0) - , _vMinor(0) - , _vRevision(0) - , _id(peerIdentity) - , _directPathPushCutoffCount(0) - , _echoRequestCutoffCount(0) - , _localMultipathSupported(false) - , _lastComputedAggregateMeanLatency(0) + : RR(renv) + , _lastReceive(0) + , _lastNontrivialReceive(0) + , _lastTriedMemorizedPath(0) + , _lastDirectPathPushSent(0) + , _lastDirectPathPushReceive(0) + , _lastCredentialRequestSent(0) + , _lastWhoisRequestReceived(0) + , _lastCredentialsReceived(0) + , _lastTrustEstablishedPacketReceived(0) + , _lastSentFullHello(0) + , _lastEchoCheck(0) + , _freeRandomByte((unsigned char)((uintptr_t)this >> 4) ^ ++s_freeRandomByteCounter) + , _vProto(0) + , _vMajor(0) + , _vMinor(0) + , _vRevision(0) + , _id(peerIdentity) + , _directPathPushCutoffCount(0) + , _echoRequestCutoffCount(0) + , _localMultipathSupported(false) + , _lastComputedAggregateMeanLatency(0) #ifndef ZT_NO_PEER_METRICS - , _peer_latency{Metrics::peer_latency.Add({{"node_id", OSUtils::nodeIDStr(peerIdentity.address().toInt())}}, std::vector{1,3,6,10,30,60,100,300,600,1000})} - , _alive_path_count{Metrics::peer_path_count.Add({{"node_id", OSUtils::nodeIDStr(peerIdentity.address().toInt())},{"status","alive"}})} - , _dead_path_count{Metrics::peer_path_count.Add({{"node_id", OSUtils::nodeIDStr(peerIdentity.address().toInt())},{"status","dead"}})} - , _incoming_packet{Metrics::peer_packets.Add({{"direction", "rx"},{"node_id", OSUtils::nodeIDStr(peerIdentity.address().toInt())}})} - , _outgoing_packet{Metrics::peer_packets.Add({{"direction", "tx"},{"node_id", OSUtils::nodeIDStr(peerIdentity.address().toInt())}})} - , _packet_errors{Metrics::peer_packet_errors.Add({{"node_id", OSUtils::nodeIDStr(peerIdentity.address().toInt())}})} + , _peer_latency{Metrics::peer_latency.Add({{"node_id", OSUtils::nodeIDStr(peerIdentity.address().toInt())}}, std::vector{1,3,6,10,30,60,100,300,600,1000})} + , _alive_path_count{Metrics::peer_path_count.Add({{"node_id", OSUtils::nodeIDStr(peerIdentity.address().toInt())},{"status","alive"}})} + , _dead_path_count{Metrics::peer_path_count.Add({{"node_id", OSUtils::nodeIDStr(peerIdentity.address().toInt())},{"status","dead"}})} + , _incoming_packet{Metrics::peer_packets.Add({{"direction", "rx"},{"node_id", OSUtils::nodeIDStr(peerIdentity.address().toInt())}})} + , _outgoing_packet{Metrics::peer_packets.Add({{"direction", "tx"},{"node_id", OSUtils::nodeIDStr(peerIdentity.address().toInt())}})} + , _packet_errors{Metrics::peer_packet_errors.Add({{"node_id", OSUtils::nodeIDStr(peerIdentity.address().toInt())}})} #endif { - if (!myIdentity.agree(peerIdentity,_key)) { - throw ZT_EXCEPTION_INVALID_ARGUMENT; - } - - uint8_t ktmp[ZT_SYMMETRIC_KEY_SIZE]; - KBKDFHMACSHA384(_key,ZT_KBKDF_LABEL_AES_GMAC_SIV_K0,0,0,ktmp); - _aesKeys[0].init(ktmp); - KBKDFHMACSHA384(_key,ZT_KBKDF_LABEL_AES_GMAC_SIV_K1,0,0,ktmp); - _aesKeys[1].init(ktmp); - Utils::burn(ktmp,ZT_SYMMETRIC_KEY_SIZE); + if (!myIdentity.agree(peerIdentity,_key)) { + throw ZT_EXCEPTION_INVALID_ARGUMENT; + } + + uint8_t ktmp[ZT_SYMMETRIC_KEY_SIZE]; + KBKDFHMACSHA384(_key,ZT_KBKDF_LABEL_AES_GMAC_SIV_K0,0,0,ktmp); + _aesKeys[0].init(ktmp); + KBKDFHMACSHA384(_key,ZT_KBKDF_LABEL_AES_GMAC_SIV_K1,0,0,ktmp); + _aesKeys[1].init(ktmp); + Utils::burn(ktmp,ZT_SYMMETRIC_KEY_SIZE); } void Peer::received( - void *tPtr, - const SharedPtr &path, - const unsigned int hops, - const uint64_t packetId, - const unsigned int payloadLength, - const Packet::Verb verb, - const uint64_t inRePacketId, - const Packet::Verb inReVerb, - const bool trustEstablished, - const uint64_t networkId, - const int32_t flowId) + void *tPtr, + const SharedPtr &path, + const unsigned int hops, + const uint64_t packetId, + const unsigned int payloadLength, + const Packet::Verb verb, + const uint64_t inRePacketId, + const Packet::Verb inReVerb, + const bool trustEstablished, + const uint64_t networkId, + const int32_t flowId) { - const int64_t now = RR->node->now(); - - _lastReceive = now; - switch (verb) { - case Packet::VERB_FRAME: - case Packet::VERB_EXT_FRAME: - case Packet::VERB_NETWORK_CONFIG_REQUEST: - case Packet::VERB_NETWORK_CONFIG: - case Packet::VERB_MULTICAST_FRAME: - _lastNontrivialReceive = now; - break; - default: - break; - } + const int64_t now = RR->node->now(); + + _lastReceive = now; + switch (verb) { + case Packet::VERB_FRAME: + case Packet::VERB_EXT_FRAME: + case Packet::VERB_NETWORK_CONFIG_REQUEST: + case Packet::VERB_NETWORK_CONFIG: + case Packet::VERB_MULTICAST_FRAME: + _lastNontrivialReceive = now; + break; + default: + break; + } #ifndef ZT_NO_PEER_METRICS - _incoming_packet++; + _incoming_packet++; #endif - recordIncomingPacket(path, packetId, payloadLength, verb, flowId, now); - - if (trustEstablished) { - _lastTrustEstablishedPacketReceived = now; - path->trustedPacketReceived(now); - } - - if (hops == 0) { - // If this is a direct packet (no hops), update existing paths or learn new ones - bool havePath = false; - { - Mutex::Lock _l(_paths_m); - for(unsigned int i=0;iaddress().ipsEqual(path->address()) && _paths[i].p->localSocket() == path->localSocket()) { - if (_paths[i].p->alive(now) && !_bond) { - havePath = true; - break; - } - } - } else { - break; - } - } - } - - if ( (!havePath) && RR->node->shouldUsePathForZeroTierTraffic(tPtr,_id.address(),path->localSocket(),path->address()) ) { - if (verb == Packet::VERB_OK) { - Mutex::Lock _l(_paths_m); - unsigned int oldestPathIdx = ZT_MAX_PEER_NETWORK_PATHS; - unsigned int oldestPathAge = 0; - unsigned int replacePath = ZT_MAX_PEER_NETWORK_PATHS; - - for(unsigned int i=0;iage(now); - if (currAge > oldestPathAge) { - oldestPathAge = currAge; - oldestPathIdx = i; - } - if (_paths[i].p->address().ipsEqual(path->address())) { - if (_paths[i].p->localSocket() == path->localSocket()) { - if (!_paths[i].p->alive(now)) { - replacePath = i; - break; - } - } - } - } else { - replacePath = i; - break; - } - } - - // If we didn't find a good candidate then resort to replacing oldest path - replacePath = (replacePath == ZT_MAX_PEER_NETWORK_PATHS) ? oldestPathIdx : replacePath; - if (replacePath != ZT_MAX_PEER_NETWORK_PATHS) { - RR->t->peerLearnedNewPath(tPtr, networkId, *this, path, packetId); - _paths[replacePath].lr = now; - _paths[replacePath].p = path; - _paths[replacePath].priority = 1; - Mutex::Lock _l(_bond_m); - if(_bond) { - _bond->nominatePathToBond(_paths[replacePath].p, now); - } - } - } else { - Mutex::Lock ltl(_lastTriedPath_m); - - bool triedTooRecently = false; - for(std::list< std::pair< Path *, int64_t > >::iterator i(_lastTriedPath.begin());i!=_lastTriedPath.end();) { - if ((now - i->second) > 1000) { - _lastTriedPath.erase(i++); - } else if (i->first == path.ptr()) { - ++i; - triedTooRecently = true; - } else { - ++i; - } - } - - if (!triedTooRecently) { - _lastTriedPath.push_back(std::pair< Path *, int64_t >(path.ptr(), now)); - attemptToContactAt(tPtr,path->localSocket(),path->address(),now,true); - path->sent(now); - RR->t->peerConfirmingUnknownPath(tPtr,networkId,*this,path,packetId,verb); - } - } - } - } - - // If we have a trust relationship periodically push a message enumerating - // all known external addresses for ourselves. If we already have a path this - // is done less frequently. - if (this->trustEstablished(now)) { - const int64_t sinceLastPush = now - _lastDirectPathPushSent; - bool lowBandwidth = RR->node->lowBandwidthModeEnabled(); - int timerScale = lowBandwidth ? 16 : 1; - if (sinceLastPush >= ((hops == 0) ? ZT_DIRECT_PATH_PUSH_INTERVAL_HAVEPATH * timerScale : ZT_DIRECT_PATH_PUSH_INTERVAL)) { - _lastDirectPathPushSent = now; - std::vector pathsToPush(RR->node->directPaths()); - std::vector ma = RR->sa->whoami(); - pathsToPush.insert(pathsToPush.end(), ma.begin(), ma.end()); - if (!pathsToPush.empty()) { - std::vector::const_iterator p(pathsToPush.begin()); - while (p != pathsToPush.end()) { - Packet *const outp = new Packet(_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) { - Metrics::pkt_push_direct_paths_out++; - outp->setAt(ZT_PACKET_IDX_PAYLOAD,(uint16_t)count); - outp->compress(); - outp->armor(_key,true,aesKeysIfSupported()); - Metrics::pkt_push_direct_paths_out++; - path->send(RR,tPtr,outp->data(),outp->size(),now); - } - delete outp; - } - } - } - } + recordIncomingPacket(path, packetId, payloadLength, verb, flowId, now); + + if (trustEstablished) { + _lastTrustEstablishedPacketReceived = now; + path->trustedPacketReceived(now); + } + + if (hops == 0) { + // If this is a direct packet (no hops), update existing paths or learn new ones + bool havePath = false; + { + Mutex::Lock _l(_paths_m); + for(unsigned int i=0;iaddress().ipsEqual(path->address()) && _paths[i].p->localSocket() == path->localSocket()) { + if (_paths[i].p->alive(now) && !_bond) { + havePath = true; + break; + } + } + } else { + break; + } + } + } + + if ( (!havePath) && RR->node->shouldUsePathForZeroTierTraffic(tPtr,_id.address(),path->localSocket(),path->address()) ) { + if (verb == Packet::VERB_OK) { + Mutex::Lock _l(_paths_m); + unsigned int oldestPathIdx = ZT_MAX_PEER_NETWORK_PATHS; + unsigned int oldestPathAge = 0; + unsigned int replacePath = ZT_MAX_PEER_NETWORK_PATHS; + + for(unsigned int i=0;iage(now); + if (currAge > oldestPathAge) { + oldestPathAge = currAge; + oldestPathIdx = i; + } + if (_paths[i].p->address().ipsEqual(path->address())) { + if (_paths[i].p->localSocket() == path->localSocket()) { + if (!_paths[i].p->alive(now)) { + replacePath = i; + break; + } + } + } + } else { + replacePath = i; + break; + } + } + + // If we didn't find a good candidate then resort to replacing oldest path + replacePath = (replacePath == ZT_MAX_PEER_NETWORK_PATHS) ? oldestPathIdx : replacePath; + if (replacePath != ZT_MAX_PEER_NETWORK_PATHS) { + RR->t->peerLearnedNewPath(tPtr, networkId, *this, path, packetId); + _paths[replacePath].lr = now; + _paths[replacePath].p = path; + _paths[replacePath].priority = 1; + Mutex::Lock _l(_bond_m); + if(_bond) { + _bond->nominatePathToBond(_paths[replacePath].p, now); + } + } + } else { + Mutex::Lock ltl(_lastTriedPath_m); + + bool triedTooRecently = false; + for(std::list< std::pair< Path *, int64_t > >::iterator i(_lastTriedPath.begin());i!=_lastTriedPath.end();) { + if ((now - i->second) > 1000) { + _lastTriedPath.erase(i++); + } else if (i->first == path.ptr()) { + ++i; + triedTooRecently = true; + } else { + ++i; + } + } + + if (!triedTooRecently) { + _lastTriedPath.push_back(std::pair< Path *, int64_t >(path.ptr(), now)); + attemptToContactAt(tPtr,path->localSocket(),path->address(),now,true); + path->sent(now); + RR->t->peerConfirmingUnknownPath(tPtr,networkId,*this,path,packetId,verb); + } + } + } + } + + // If we have a trust relationship periodically push a message enumerating + // all known external addresses for ourselves. If we already have a path this + // is done less frequently. + if (this->trustEstablished(now)) { + const int64_t sinceLastPush = now - _lastDirectPathPushSent; + bool lowBandwidth = RR->node->lowBandwidthModeEnabled(); + int timerScale = lowBandwidth ? 16 : 1; + if (sinceLastPush >= ((hops == 0) ? ZT_DIRECT_PATH_PUSH_INTERVAL_HAVEPATH * timerScale : ZT_DIRECT_PATH_PUSH_INTERVAL)) { + _lastDirectPathPushSent = now; + std::vector pathsToPush(RR->node->directPaths()); + std::vector ma = RR->sa->whoami(); + pathsToPush.insert(pathsToPush.end(), ma.begin(), ma.end()); + if (!pathsToPush.empty()) { + std::vector::const_iterator p(pathsToPush.begin()); + while (p != pathsToPush.end()) { + Packet *const outp = new Packet(_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) { + Metrics::pkt_push_direct_paths_out++; + outp->setAt(ZT_PACKET_IDX_PAYLOAD,(uint16_t)count); + outp->compress(); + outp->armor(_key,true,false,aesKeysIfSupported(),_id); + Metrics::pkt_push_direct_paths_out++; + path->send(RR,tPtr,outp->data(),outp->size(),now); + } + delete outp; + } + } + } + } } SharedPtr Peer::getAppropriatePath(int64_t now, bool includeExpired, int32_t flowId) { - Mutex::Lock _l(_paths_m); - Mutex::Lock _lb(_bond_m); - if(_bond && _bond->isReady()) { - return _bond->getAppropriatePath(now, flowId); - } - unsigned int bestPath = ZT_MAX_PEER_NETWORK_PATHS; - /** - * Send traffic across the highest quality path only. This algorithm will still - * use the old path quality metric from protocol version 9. - */ - long bestPathQuality = 2147483647; - for(unsigned int i=0;iquality(now) / _paths[i].priority; - if (q <= bestPathQuality) { - bestPathQuality = q; - bestPath = i; - } - } - } else { - break; - } - } - if (bestPath != ZT_MAX_PEER_NETWORK_PATHS) { - return _paths[bestPath].p; - } - return SharedPtr(); + Mutex::Lock _l(_paths_m); + Mutex::Lock _lb(_bond_m); + if(_bond && _bond->isReady()) { + return _bond->getAppropriatePath(now, flowId); + } + unsigned int bestPath = ZT_MAX_PEER_NETWORK_PATHS; + /** + * Send traffic across the highest quality path only. This algorithm will still + * use the old path quality metric from protocol version 9. + */ + long bestPathQuality = 2147483647; + for(unsigned int i=0;iquality(now) / _paths[i].priority; + if (q <= bestPathQuality) { + bestPathQuality = q; + bestPath = i; + } + } + } else { + break; + } + } + if (bestPath != ZT_MAX_PEER_NETWORK_PATHS) { + return _paths[bestPath].p; + } + return SharedPtr(); } void Peer::introduce(void *const tPtr,const int64_t now,const SharedPtr &other) const { - unsigned int myBestV4ByScope[ZT_INETADDRESS_MAX_SCOPE+1]; - unsigned int myBestV6ByScope[ZT_INETADDRESS_MAX_SCOPE+1]; - long myBestV4QualityByScope[ZT_INETADDRESS_MAX_SCOPE+1]; - long myBestV6QualityByScope[ZT_INETADDRESS_MAX_SCOPE+1]; - unsigned int theirBestV4ByScope[ZT_INETADDRESS_MAX_SCOPE+1]; - unsigned int theirBestV6ByScope[ZT_INETADDRESS_MAX_SCOPE+1]; - long theirBestV4QualityByScope[ZT_INETADDRESS_MAX_SCOPE+1]; - long theirBestV6QualityByScope[ZT_INETADDRESS_MAX_SCOPE+1]; - for(int i=0;i<=ZT_INETADDRESS_MAX_SCOPE;++i) { - myBestV4ByScope[i] = ZT_MAX_PEER_NETWORK_PATHS; - myBestV6ByScope[i] = ZT_MAX_PEER_NETWORK_PATHS; - myBestV4QualityByScope[i] = 2147483647; - myBestV6QualityByScope[i] = 2147483647; - theirBestV4ByScope[i] = ZT_MAX_PEER_NETWORK_PATHS; - theirBestV6ByScope[i] = ZT_MAX_PEER_NETWORK_PATHS; - theirBestV4QualityByScope[i] = 2147483647; - theirBestV6QualityByScope[i] = 2147483647; - } - - Mutex::Lock _l1(_paths_m); - - for(unsigned int i=0;iquality(now) / _paths[i].priority; - const unsigned int s = (unsigned int)_paths[i].p->ipScope(); - switch(_paths[i].p->address().ss_family) { - case AF_INET: - if (q <= myBestV4QualityByScope[s]) { - myBestV4QualityByScope[s] = q; - myBestV4ByScope[s] = i; - } - break; - case AF_INET6: - if (q <= myBestV6QualityByScope[s]) { - myBestV6QualityByScope[s] = q; - myBestV6ByScope[s] = i; - } - break; - } - } else { - break; - } - } - - Mutex::Lock _l2(other->_paths_m); - - for(unsigned int i=0;i_paths[i].p) { - const long q = other->_paths[i].p->quality(now) / other->_paths[i].priority; - const unsigned int s = (unsigned int)other->_paths[i].p->ipScope(); - switch(other->_paths[i].p->address().ss_family) { - case AF_INET: - if (q <= theirBestV4QualityByScope[s]) { - theirBestV4QualityByScope[s] = q; - theirBestV4ByScope[s] = i; - } - break; - case AF_INET6: - if (q <= theirBestV6QualityByScope[s]) { - theirBestV6QualityByScope[s] = q; - theirBestV6ByScope[s] = i; - } - break; - } - } else { - break; - } - } - - unsigned int mine = ZT_MAX_PEER_NETWORK_PATHS; - unsigned int theirs = ZT_MAX_PEER_NETWORK_PATHS; - - for(int s=ZT_INETADDRESS_MAX_SCOPE;s>=0;--s) { - if ((myBestV6ByScope[s] != ZT_MAX_PEER_NETWORK_PATHS)&&(theirBestV6ByScope[s] != ZT_MAX_PEER_NETWORK_PATHS)) { - mine = myBestV6ByScope[s]; - theirs = theirBestV6ByScope[s]; - break; - } - if ((myBestV4ByScope[s] != ZT_MAX_PEER_NETWORK_PATHS)&&(theirBestV4ByScope[s] != ZT_MAX_PEER_NETWORK_PATHS)) { - mine = myBestV4ByScope[s]; - theirs = theirBestV4ByScope[s]; - break; - } - } - - if (mine != ZT_MAX_PEER_NETWORK_PATHS) { - unsigned int alt = (unsigned int)RR->node->prng() & 1; // randomize which hint we send first for black magickal NAT-t reasons - const unsigned int completed = alt + 2; - while (alt != completed) { - if ((alt & 1) == 0) { - Packet outp(_id.address(),RR->identity.address(),Packet::VERB_RENDEZVOUS); - outp.append((uint8_t)0); - other->_id.address().appendTo(outp); - outp.append((uint16_t)other->_paths[theirs].p->address().port()); - if (other->_paths[theirs].p->address().ss_family == AF_INET6) { - outp.append((uint8_t)16); - outp.append(other->_paths[theirs].p->address().rawIpData(),16); - } else { - outp.append((uint8_t)4); - outp.append(other->_paths[theirs].p->address().rawIpData(),4); - } - outp.armor(_key,true,aesKeysIfSupported()); - Metrics::pkt_rendezvous_out++; - _paths[mine].p->send(RR,tPtr,outp.data(),outp.size(),now); - } else { - Packet outp(other->_id.address(),RR->identity.address(),Packet::VERB_RENDEZVOUS); - outp.append((uint8_t)0); - _id.address().appendTo(outp); - outp.append((uint16_t)_paths[mine].p->address().port()); - if (_paths[mine].p->address().ss_family == AF_INET6) { - outp.append((uint8_t)16); - outp.append(_paths[mine].p->address().rawIpData(),16); - } else { - outp.append((uint8_t)4); - outp.append(_paths[mine].p->address().rawIpData(),4); - } - outp.armor(other->_key,true,other->aesKeysIfSupported()); - Metrics::pkt_rendezvous_out++; - other->_paths[theirs].p->send(RR,tPtr,outp.data(),outp.size(),now); - } - ++alt; - } - } + unsigned int myBestV4ByScope[ZT_INETADDRESS_MAX_SCOPE+1]; + unsigned int myBestV6ByScope[ZT_INETADDRESS_MAX_SCOPE+1]; + long myBestV4QualityByScope[ZT_INETADDRESS_MAX_SCOPE+1]; + long myBestV6QualityByScope[ZT_INETADDRESS_MAX_SCOPE+1]; + unsigned int theirBestV4ByScope[ZT_INETADDRESS_MAX_SCOPE+1]; + unsigned int theirBestV6ByScope[ZT_INETADDRESS_MAX_SCOPE+1]; + long theirBestV4QualityByScope[ZT_INETADDRESS_MAX_SCOPE+1]; + long theirBestV6QualityByScope[ZT_INETADDRESS_MAX_SCOPE+1]; + for(int i=0;i<=ZT_INETADDRESS_MAX_SCOPE;++i) { + myBestV4ByScope[i] = ZT_MAX_PEER_NETWORK_PATHS; + myBestV6ByScope[i] = ZT_MAX_PEER_NETWORK_PATHS; + myBestV4QualityByScope[i] = 2147483647; + myBestV6QualityByScope[i] = 2147483647; + theirBestV4ByScope[i] = ZT_MAX_PEER_NETWORK_PATHS; + theirBestV6ByScope[i] = ZT_MAX_PEER_NETWORK_PATHS; + theirBestV4QualityByScope[i] = 2147483647; + theirBestV6QualityByScope[i] = 2147483647; + } + + Mutex::Lock _l1(_paths_m); + + for(unsigned int i=0;iquality(now) / _paths[i].priority; + const unsigned int s = (unsigned int)_paths[i].p->ipScope(); + switch(_paths[i].p->address().ss_family) { + case AF_INET: + if (q <= myBestV4QualityByScope[s]) { + myBestV4QualityByScope[s] = q; + myBestV4ByScope[s] = i; + } + break; + case AF_INET6: + if (q <= myBestV6QualityByScope[s]) { + myBestV6QualityByScope[s] = q; + myBestV6ByScope[s] = i; + } + break; + } + } else { + break; + } + } + + Mutex::Lock _l2(other->_paths_m); + + for(unsigned int i=0;i_paths[i].p) { + const long q = other->_paths[i].p->quality(now) / other->_paths[i].priority; + const unsigned int s = (unsigned int)other->_paths[i].p->ipScope(); + switch(other->_paths[i].p->address().ss_family) { + case AF_INET: + if (q <= theirBestV4QualityByScope[s]) { + theirBestV4QualityByScope[s] = q; + theirBestV4ByScope[s] = i; + } + break; + case AF_INET6: + if (q <= theirBestV6QualityByScope[s]) { + theirBestV6QualityByScope[s] = q; + theirBestV6ByScope[s] = i; + } + break; + } + } else { + break; + } + } + + unsigned int mine = ZT_MAX_PEER_NETWORK_PATHS; + unsigned int theirs = ZT_MAX_PEER_NETWORK_PATHS; + + for(int s=ZT_INETADDRESS_MAX_SCOPE;s>=0;--s) { + if ((myBestV6ByScope[s] != ZT_MAX_PEER_NETWORK_PATHS)&&(theirBestV6ByScope[s] != ZT_MAX_PEER_NETWORK_PATHS)) { + mine = myBestV6ByScope[s]; + theirs = theirBestV6ByScope[s]; + break; + } + if ((myBestV4ByScope[s] != ZT_MAX_PEER_NETWORK_PATHS)&&(theirBestV4ByScope[s] != ZT_MAX_PEER_NETWORK_PATHS)) { + mine = myBestV4ByScope[s]; + theirs = theirBestV4ByScope[s]; + break; + } + } + + if (mine != ZT_MAX_PEER_NETWORK_PATHS) { + unsigned int alt = (unsigned int)RR->node->prng() & 1; // randomize which hint we send first for black magickal NAT-t reasons + const unsigned int completed = alt + 2; + while (alt != completed) { + if ((alt & 1) == 0) { + Packet outp(_id.address(),RR->identity.address(),Packet::VERB_RENDEZVOUS); + outp.append((uint8_t)0); + other->_id.address().appendTo(outp); + outp.append((uint16_t)other->_paths[theirs].p->address().port()); + if (other->_paths[theirs].p->address().ss_family == AF_INET6) { + outp.append((uint8_t)16); + outp.append(other->_paths[theirs].p->address().rawIpData(),16); + } else { + outp.append((uint8_t)4); + outp.append(other->_paths[theirs].p->address().rawIpData(),4); + } + outp.armor(_key,true,false,aesKeysIfSupported(),_id); + Metrics::pkt_rendezvous_out++; + _paths[mine].p->send(RR,tPtr,outp.data(),outp.size(),now); + } else { + Packet outp(other->_id.address(),RR->identity.address(),Packet::VERB_RENDEZVOUS); + outp.append((uint8_t)0); + _id.address().appendTo(outp); + outp.append((uint16_t)_paths[mine].p->address().port()); + if (_paths[mine].p->address().ss_family == AF_INET6) { + outp.append((uint8_t)16); + outp.append(_paths[mine].p->address().rawIpData(),16); + } else { + outp.append((uint8_t)4); + outp.append(_paths[mine].p->address().rawIpData(),4); + } + outp.armor(other->_key,true,false,other->aesKeysIfSupported(),other->identity()); + Metrics::pkt_rendezvous_out++; + other->_paths[theirs].p->send(RR,tPtr,outp.data(),outp.size(),now); + } + ++alt; + } + } } void Peer::sendHELLO(void *tPtr,const int64_t localSocket,const InetAddress &atAddress,int64_t now) { - Packet outp(_id.address(),RR->identity.address(),Packet::VERB_HELLO); - - outp.append((unsigned char)ZT_PROTO_VERSION); - outp.append((unsigned char)ZEROTIER_ONE_VERSION_MAJOR); - outp.append((unsigned char)ZEROTIER_ONE_VERSION_MINOR); - outp.append((uint16_t)ZEROTIER_ONE_VERSION_REVISION); - outp.append(now); - RR->identity.serialize(outp,false); - atAddress.serialize(outp); - - outp.append((uint64_t)RR->topology->planetWorldId()); - outp.append((uint64_t)RR->topology->planetWorldTimestamp()); - - const unsigned int startCryptedPortionAt = outp.size(); - - std::vector moons(RR->topology->moons()); - std::vector moonsWanted(RR->topology->moonsWanted()); - outp.append((uint16_t)(moons.size() + moonsWanted.size())); - for(std::vector::const_iterator m(moons.begin());m!=moons.end();++m) { - outp.append((uint8_t)m->type()); - outp.append((uint64_t)m->id()); - outp.append((uint64_t)m->timestamp()); - } - for(std::vector::const_iterator m(moonsWanted.begin());m!=moonsWanted.end();++m) { - outp.append((uint8_t)World::TYPE_MOON); - outp.append(*m); - outp.append((uint64_t)0); - } - - outp.cryptField(_key,startCryptedPortionAt,outp.size() - startCryptedPortionAt); - - Metrics::pkt_hello_out++; - - if (atAddress) { - outp.armor(_key,false,nullptr); // false == don't encrypt full payload, but add MAC - RR->node->expectReplyTo(outp.packetId()); - RR->node->putPacket(tPtr,RR->node->lowBandwidthModeEnabled() ? localSocket : -1,atAddress,outp.data(),outp.size()); - } else { - RR->node->expectReplyTo(outp.packetId()); - RR->sw->send(tPtr,outp,false); // false == don't encrypt full payload, but add MAC - } + Packet outp(_id.address(),RR->identity.address(),Packet::VERB_HELLO); + + outp.append((unsigned char)ZT_PROTO_VERSION); + outp.append((unsigned char)ZEROTIER_ONE_VERSION_MAJOR); + outp.append((unsigned char)ZEROTIER_ONE_VERSION_MINOR); + outp.append((uint16_t)ZEROTIER_ONE_VERSION_REVISION); + outp.append(now); + RR->identity.serialize(outp,false); + atAddress.serialize(outp); + + outp.append((uint64_t)RR->topology->planetWorldId()); + outp.append((uint64_t)RR->topology->planetWorldTimestamp()); + + const unsigned int startCryptedPortionAt = outp.size(); + + std::vector moons(RR->topology->moons()); + std::vector moonsWanted(RR->topology->moonsWanted()); + outp.append((uint16_t)(moons.size() + moonsWanted.size())); + for(std::vector::const_iterator m(moons.begin());m!=moons.end();++m) { + outp.append((uint8_t)m->type()); + outp.append((uint64_t)m->id()); + outp.append((uint64_t)m->timestamp()); + } + for(std::vector::const_iterator m(moonsWanted.begin());m!=moonsWanted.end();++m) { + outp.append((uint8_t)World::TYPE_MOON); + outp.append(*m); + outp.append((uint64_t)0); + } + + outp.cryptField(_key,startCryptedPortionAt,outp.size() - startCryptedPortionAt); + + Metrics::pkt_hello_out++; + + if (atAddress) { + // TODO: this is where extended armor should be invoked + outp.armor(_key,false,false,nullptr,_id); + RR->node->expectReplyTo(outp.packetId()); + RR->node->putPacket(tPtr,RR->node->lowBandwidthModeEnabled() ? localSocket : -1,atAddress,outp.data(),outp.size()); + } else { + RR->node->expectReplyTo(outp.packetId()); + RR->sw->send(tPtr,outp,true); + } } void Peer::attemptToContactAt(void *tPtr,const int64_t localSocket,const InetAddress &atAddress,int64_t now,bool sendFullHello) { - if ( (!sendFullHello) && (_vProto >= 5) && (!((_vMajor == 1)&&(_vMinor == 1)&&(_vRevision == 0))) ) { - Packet outp(_id.address(),RR->identity.address(),Packet::VERB_ECHO); - outp.armor(_key,true,aesKeysIfSupported()); - Metrics::pkt_echo_out++; - RR->node->expectReplyTo(outp.packetId()); - RR->node->putPacket(tPtr,localSocket,atAddress,outp.data(),outp.size()); - } else { - sendHELLO(tPtr,localSocket,atAddress,now); - } + if ( (!sendFullHello) && (_vProto >= 5) && (!((_vMajor == 1)&&(_vMinor == 1)&&(_vRevision == 0))) ) { + Packet outp(_id.address(),RR->identity.address(),Packet::VERB_ECHO); + outp.armor(_key,true,false,aesKeysIfSupported(),_id); + Metrics::pkt_echo_out++; + RR->node->expectReplyTo(outp.packetId()); + RR->node->putPacket(tPtr,localSocket,atAddress,outp.data(),outp.size()); + } else { + sendHELLO(tPtr,localSocket,atAddress,now); + } } void Peer::tryMemorizedPath(void *tPtr,int64_t now) { - if ((now - _lastTriedMemorizedPath) >= ZT_TRY_MEMORIZED_PATH_INTERVAL) { - _lastTriedMemorizedPath = now; - InetAddress mp; - if (RR->node->externalPathLookup(tPtr,_id.address(),-1,mp)) { - attemptToContactAt(tPtr,-1,mp,now,true); - } - } + if ((now - _lastTriedMemorizedPath) >= ZT_TRY_MEMORIZED_PATH_INTERVAL) { + _lastTriedMemorizedPath = now; + InetAddress mp; + if (RR->node->externalPathLookup(tPtr,_id.address(),-1,mp)) { + attemptToContactAt(tPtr,-1,mp,now,true); + } + } } void Peer::performMultipathStateCheck(void *tPtr, int64_t now) { - Mutex::Lock _l(_bond_m); - /** - * Check for conditions required for multipath bonding and create a bond - * if allowed. - */ - int numAlivePaths = 0; - bool atLeastOneNonExpired = false; - for(unsigned int i=0;ialive(now)) { - numAlivePaths++; - } - if ((now - _paths[i].lr) < ZT_PEER_PATH_EXPIRATION) { - atLeastOneNonExpired = true; - } - } - } - if (_bond) { - if (numAlivePaths == 0 && !atLeastOneNonExpired) { - _bond = SharedPtr(); - RR->bc->destroyBond(_id.address().toInt()); - } - return; - } - _localMultipathSupported = ((numAlivePaths >= 1) && (RR->bc->inUse()) && (ZT_PROTO_VERSION > 9)); - if (_localMultipathSupported && !_bond) { - if (RR->bc) { - _bond = RR->bc->createBond(RR, this); - /** - * Allow new bond to retroactively learn all paths known to this peer - */ - if (_bond) { - for (unsigned int i=0;inominatePathToBond(_paths[i].p, now); - } - } - } - } - } + Mutex::Lock _l(_bond_m); + /** + * Check for conditions required for multipath bonding and create a bond + * if allowed. + */ + int numAlivePaths = 0; + bool atLeastOneNonExpired = false; + for(unsigned int i=0;ialive(now)) { + numAlivePaths++; + } + if ((now - _paths[i].lr) < ZT_PEER_PATH_EXPIRATION) { + atLeastOneNonExpired = true; + } + } + } + if (_bond) { + if (numAlivePaths == 0 && !atLeastOneNonExpired) { + _bond = SharedPtr(); + RR->bc->destroyBond(_id.address().toInt()); + } + return; + } + _localMultipathSupported = ((numAlivePaths >= 1) && (RR->bc->inUse()) && (ZT_PROTO_VERSION > 9)); + if (_localMultipathSupported && !_bond) { + if (RR->bc) { + _bond = RR->bc->createBond(RR, this); + /** + * Allow new bond to retroactively learn all paths known to this peer + */ + if (_bond) { + for (unsigned int i=0;inominatePathToBond(_paths[i].p, now); + } + } + } + } + } } unsigned int Peer::doPingAndKeepalive(void *tPtr,int64_t now) { - unsigned int sent = 0; - { - Mutex::Lock _l(_paths_m); - - performMultipathStateCheck(tPtr, now); - - const bool sendFullHello = ((now - _lastSentFullHello) >= ZT_PEER_PING_PERIOD); - if (sendFullHello) { - _lastSentFullHello = now; - } - - // Right now we only keep pinging links that have the maximum priority. The - // priority is used to track cluster redirections, meaning that when a cluster - // redirects us its redirect target links override all other links and we - // let those old links expire. - long maxPriority = 0; - for(unsigned int i=0;ineedsHeartbeat(now))) { - attemptToContactAt(tPtr,_paths[i].p->localSocket(),_paths[i].p->address(),now,sendFullHello); - _paths[i].p->sent(now); - sent |= (_paths[i].p->address().ss_family == AF_INET) ? 0x1 : 0x2; - } - } else { - _paths[i] = _PeerPath(); - deletionOccurred = true; - } - } - if (!_paths[i].p || deletionOccurred) { - for(unsigned int j=i;j= ZT_PEER_PING_PERIOD); + if (sendFullHello) { + _lastSentFullHello = now; + } + + // Right now we only keep pinging links that have the maximum priority. The + // priority is used to track cluster redirections, meaning that when a cluster + // redirects us its redirect target links override all other links and we + // let those old links expire. + long maxPriority = 0; + for(unsigned int i=0;ineedsHeartbeat(now))) { + attemptToContactAt(tPtr,_paths[i].p->localSocket(),_paths[i].p->address(),now,sendFullHello); + _paths[i].p->sent(now); + sent |= (_paths[i].p->address().ss_family == AF_INET) ? 0x1 : 0x2; + } + } else { + _paths[i] = _PeerPath(); + deletionOccurred = true; + } + } + if (!_paths[i].p || deletionOccurred) { + for(unsigned int j=i;jalive(now)) { - alive_path_count_tmp++; - } - else { - dead_path_count_tmp++; - } - } - } - _alive_path_count = alive_path_count_tmp; - _dead_path_count = dead_path_count_tmp; + uint16_t alive_path_count_tmp = 0, dead_path_count_tmp = 0; + for(unsigned int i=0;ialive(now)) { + alive_path_count_tmp++; + } + else { + dead_path_count_tmp++; + } + } + } + _alive_path_count = alive_path_count_tmp; + _dead_path_count = dead_path_count_tmp; #endif - } + } #ifndef ZT_NO_PEER_METRICS - _peer_latency.Observe(latency(now)); + _peer_latency.Observe(latency(now)); #endif - return sent; + return sent; } void Peer::clusterRedirect(void *tPtr,const SharedPtr &originatingPath,const InetAddress &remoteAddress,const int64_t now) { - SharedPtr np(RR->topology->getPath(originatingPath->localSocket(),remoteAddress)); - RR->t->peerRedirected(tPtr,0,*this,np); - - attemptToContactAt(tPtr,originatingPath->localSocket(),remoteAddress,now,true); - - { - Mutex::Lock _l(_paths_m); - - // New priority is higher than the priority of the originating path (if known) - long newPriority = 1; - for(unsigned int i=0;i= newPriority)&&(!_paths[i].p->address().ipsEqual2(remoteAddress))) { - if (i != j) { - _paths[j] = _paths[i]; - } - ++j; - } - } - } - if (j < ZT_MAX_PEER_NETWORK_PATHS) { - _paths[j].lr = now; - _paths[j].p = np; - _paths[j].priority = newPriority; - ++j; - while (j < ZT_MAX_PEER_NETWORK_PATHS) { - _paths[j].lr = 0; - _paths[j].p.zero(); - _paths[j].priority = 1; - ++j; - } - } - } + SharedPtr np(RR->topology->getPath(originatingPath->localSocket(),remoteAddress)); + RR->t->peerRedirected(tPtr,0,*this,np); + + attemptToContactAt(tPtr,originatingPath->localSocket(),remoteAddress,now,true); + + { + Mutex::Lock _l(_paths_m); + + // New priority is higher than the priority of the originating path (if known) + long newPriority = 1; + for(unsigned int i=0;i= newPriority)&&(!_paths[i].p->address().ipsEqual2(remoteAddress))) { + if (i != j) { + _paths[j] = _paths[i]; + } + ++j; + } + } + } + if (j < ZT_MAX_PEER_NETWORK_PATHS) { + _paths[j].lr = now; + _paths[j].p = np; + _paths[j].priority = newPriority; + ++j; + while (j < ZT_MAX_PEER_NETWORK_PATHS) { + _paths[j].lr = 0; + _paths[j].p.zero(); + _paths[j].priority = 1; + ++j; + } + } + } } void Peer::resetWithinScope(void *tPtr,InetAddress::IpScope scope,int inetAddressFamily,int64_t now) { - Mutex::Lock _l(_paths_m); - for(unsigned int i=0;iaddress().ss_family == inetAddressFamily)&&(_paths[i].p->ipScope() == scope)) { - attemptToContactAt(tPtr,_paths[i].p->localSocket(),_paths[i].p->address(),now,false); - _paths[i].p->sent(now); - _paths[i].lr = 0; // path will not be used unless it speaks again - } - } else { - break; - } - } + Mutex::Lock _l(_paths_m); + for(unsigned int i=0;iaddress().ss_family == inetAddressFamily)&&(_paths[i].p->ipScope() == scope)) { + attemptToContactAt(tPtr,_paths[i].p->localSocket(),_paths[i].p->address(),now,false); + _paths[i].p->sent(now); + _paths[i].lr = 0; // path will not be used unless it speaks again + } + } else { + break; + } + } } void Peer::recordOutgoingPacket(const SharedPtr &path, const uint64_t packetId, - uint16_t payloadLength, const Packet::Verb verb, const int32_t flowId, int64_t now) + uint16_t payloadLength, const Packet::Verb verb, const int32_t flowId, int64_t now) { #ifndef ZT_NO_PEER_METRICS - _outgoing_packet++; + _outgoing_packet++; #endif - if (_localMultipathSupported && _bond) { - _bond->recordOutgoingPacket(path, packetId, payloadLength, verb, flowId, now); - } + if (_localMultipathSupported && _bond) { + _bond->recordOutgoingPacket(path, packetId, payloadLength, verb, flowId, now); + } } void Peer::recordIncomingInvalidPacket(const SharedPtr& path) { #ifndef ZT_NO_PEER_METRICS - _packet_errors++; + _packet_errors++; #endif - if (_localMultipathSupported && _bond) { - _bond->recordIncomingInvalidPacket(path); - } + if (_localMultipathSupported && _bond) { + _bond->recordIncomingInvalidPacket(path); + } } void Peer::recordIncomingPacket(const SharedPtr &path, const uint64_t packetId, - uint16_t payloadLength, const Packet::Verb verb, const int32_t flowId, int64_t now) + uint16_t payloadLength, const Packet::Verb verb, const int32_t flowId, int64_t now) { - if (_localMultipathSupported && _bond) { - _bond->recordIncomingPacket(path, packetId, payloadLength, verb, flowId, now); - } + if (_localMultipathSupported && _bond) { + _bond->recordIncomingPacket(path, packetId, payloadLength, verb, flowId, now); + } } } // namespace ZeroTier diff --git a/node/Peer.hpp b/node/Peer.hpp index 777a1e966..8a765e51c 100644 --- a/node/Peer.hpp +++ b/node/Peer.hpp @@ -45,646 +45,646 @@ namespace ZeroTier { */ class Peer { - friend class SharedPtr; - friend class SharedPtr; - friend class Switch; - friend class Bond; + friend class SharedPtr; + friend class SharedPtr; + friend class Switch; + friend class Bond; private: - Peer() = delete; // disabled to prevent bugs -- should not be constructed uninitialized + Peer() = delete; // disabled to prevent bugs -- should not be constructed uninitialized public: - ~Peer() { - Utils::burn(_key,sizeof(_key)); - } - - /** - * Construct a new peer - * - * @param renv Runtime environment - * @param myIdentity Identity of THIS node (for key agreement) - * @param peerIdentity Identity of peer - * @throws std::runtime_error Key agreement with peer's identity failed - */ - Peer(const RuntimeEnvironment *renv,const Identity &myIdentity,const Identity &peerIdentity); - - /** - * @return This peer's ZT address (short for identity().address()) - */ - inline const Address &address() const { return _id.address(); } - - /** - * @return This peer's identity - */ - inline const Identity &identity() const { return _id; } - - /** - * Log receipt of an authenticated packet - * - * This is called by the decode pipe when a packet is proven to be authentic - * and appears to be valid. - * - * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call - * @param path Path over which packet was received - * @param hops ZeroTier (not IP) hops - * @param packetId Packet ID - * @param verb Packet verb - * @param inRePacketId Packet ID in reply to (default: none) - * @param inReVerb Verb in reply to (for OK/ERROR, default: VERB_NOP) - * @param trustEstablished If true, some form of non-trivial trust (like allowed in network) has been established - * @param networkId Network ID if this pertains to a network, or 0 otherwise - */ - void received( - void *tPtr, - const SharedPtr &path, - const unsigned int hops, - const uint64_t packetId, - const unsigned int payloadLength, - const Packet::Verb verb, - const uint64_t inRePacketId, - const Packet::Verb inReVerb, - const bool trustEstablished, - const uint64_t networkId, - const int32_t flowId); - - /** - * Check whether we have an active path to this peer via the given address - * - * @param now Current time - * @param addr Remote address - * @return True if we have an active path to this destination - */ - inline bool hasActivePathTo(int64_t now,const InetAddress &addr) const - { - Mutex::Lock _l(_paths_m); - for(unsigned int i=0;iaddress() == addr)) { - return true; - } - } else { - break; - } - } - return false; - } - - /** - * Send via best direct path - * - * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call - * @param data Packet data - * @param len Packet length - * @param now Current time - * @param force If true, send even if path is not alive - * @return True if we actually sent something - */ - inline bool sendDirect(void *tPtr,const void *data,unsigned int len,int64_t now,bool force) - { - SharedPtr bp(getAppropriatePath(now,force)); - if (bp) { - return bp->send(RR,tPtr,data,len,now); - } - return false; - } - - /** - * Record incoming packets to - * - * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call - * @param path Path over which packet was received - * @param packetId Packet ID - * @param payloadLength Length of packet data payload - * @param verb Packet verb - * @param flowId Flow ID - * @param now Current time - */ - void recordIncomingPacket(const SharedPtr &path, const uint64_t packetId, - uint16_t payloadLength, const Packet::Verb verb, const int32_t flowId, int64_t now); - - /** - * - * @param path Path over which packet is being sent - * @param packetId Packet ID - * @param payloadLength Length of packet data payload - * @param verb Packet verb - * @param flowId Flow ID - * @param now Current time - */ - void recordOutgoingPacket(const SharedPtr &path, const uint64_t packetId, - uint16_t payloadLength, const Packet::Verb verb, const int32_t flowId, int64_t now); - - /** - * Record an invalid incoming packet. This packet failed - * MAC/compression/cipher checks and will now contribute to a - * Packet Error Ratio (PER). - * - * @param path Path over which packet was received - */ - void recordIncomingInvalidPacket(const SharedPtr& path); - - /** - * Get the most appropriate direct path based on current multipath and QoS configuration - * - * @param now Current time - * @param includeExpired If true, include even expired paths - * @return Best current path or NULL if none - */ - SharedPtr getAppropriatePath(int64_t now, bool includeExpired, int32_t flowId = -1); - - /** - * Send VERB_RENDEZVOUS to this and another peer via the best common IP scope and path - */ - void introduce(void *const tPtr,const int64_t now,const SharedPtr &other) const; - - /** - * Send a HELLO to this peer at a specified physical address - * - * No statistics or sent times are updated here. - * - * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call - * @param localSocket Local source socket - * @param atAddress Destination address - * @param now Current time - */ - void sendHELLO(void *tPtr,const int64_t localSocket,const InetAddress &atAddress,int64_t now); - - /** - * Send ECHO (or HELLO for older peers) to this peer at the given address - * - * No statistics or sent times are updated here. - * - * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call - * @param localSocket Local source socket - * @param atAddress Destination address - * @param now Current time - * @param sendFullHello If true, always send a full HELLO instead of just an ECHO - */ - void attemptToContactAt(void *tPtr,const int64_t localSocket,const InetAddress &atAddress,int64_t now,bool sendFullHello); - - /** - * Try a memorized or statically defined path if any are known - * - * Under the hood this is done periodically based on ZT_TRY_MEMORIZED_PATH_INTERVAL. - * - * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call - * @param now Current time - */ - void tryMemorizedPath(void *tPtr,int64_t now); - - /** - * A check to be performed periodically which determines whether multipath communication is - * possible with this peer. This check should be performed early in the life-cycle of the peer - * as well as during the process of learning new paths. - */ - void performMultipathStateCheck(void *tPtr, int64_t now); - - /** - * Send pings or keepalives depending on configured timeouts - * - * This also cleans up some internal data structures. It's called periodically from Node. - * - * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call - * @param now Current time - * @param inetAddressFamily Keep this address family alive, or -1 for any - * @return 0 if nothing sent or bit mask: bit 0x1 if IPv4 sent, bit 0x2 if IPv6 sent (0x3 means both sent) - */ - unsigned int doPingAndKeepalive(void *tPtr,int64_t now); - - /** - * Process a cluster redirect sent by this peer - * - * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call - * @param originatingPath Path from which redirect originated - * @param remoteAddress Remote address - * @param now Current time - */ - void clusterRedirect(void *tPtr,const SharedPtr &originatingPath,const InetAddress &remoteAddress,const int64_t now); - - /** - * Reset paths within a given IP scope and address family - * - * Resetting a path involves sending an ECHO to it and then deactivating - * it until or unless it responds. This is done when we detect a change - * to our external IP or another system change that might invalidate - * many or all current paths. - * - * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call - * @param scope IP scope - * @param inetAddressFamily Family e.g. AF_INET - * @param now Current time - */ - void resetWithinScope(void *tPtr,InetAddress::IpScope scope,int inetAddressFamily,int64_t now); - - /** - * @param now Current time - * @return All known paths to this peer - */ - inline std::vector< SharedPtr > paths(const int64_t now) const - { - std::vector< SharedPtr > pp; - Mutex::Lock _l(_paths_m); - for(unsigned int i=0;i bp(getAppropriatePath(now,false)); - if (bp) { - return (unsigned int)bp->latency(); - } - return 0xffff; - } - } - - /** - * This computes a quality score for relays and root servers - * - * If we haven't heard anything from these in ZT_PEER_ACTIVITY_TIMEOUT, they - * receive the worst possible quality (max unsigned int). Otherwise the - * quality is a product of latency and the number of potential missed - * pings. This causes roots and relays to switch over a bit faster if they - * fail. - * - * @return Relay quality score computed from latency and other factors, lower is better - */ - inline unsigned int relayQuality(const int64_t now) - { - const uint64_t tsr = now - _lastReceive; - if (tsr >= ZT_PEER_ACTIVITY_TIMEOUT) { - return (~(unsigned int)0); - } - unsigned int l = latency(now); - if (!l) { - l = 0xffff; - } - return (l * (((unsigned int)tsr / (ZT_PEER_PING_PERIOD + 1000)) + 1)); - } - - /** - * @return 256-bit secret symmetric encryption key - */ - inline const unsigned char *key() const { return _key; } - - /** - * Set the currently known remote version of this peer's client - * - * @param vproto Protocol version - * @param vmaj Major version - * @param vmin Minor version - * @param vrev Revision - */ - inline void setRemoteVersion(unsigned int vproto,unsigned int vmaj,unsigned int vmin,unsigned int vrev) - { - _vProto = (uint16_t)vproto; - _vMajor = (uint16_t)vmaj; - _vMinor = (uint16_t)vmin; - _vRevision = (uint16_t)vrev; - } - - inline unsigned int remoteVersionProtocol() const { return _vProto; } - inline unsigned int remoteVersionMajor() const { return _vMajor; } - inline unsigned int remoteVersionMinor() const { return _vMinor; } - inline unsigned int remoteVersionRevision() const { return _vRevision; } - - inline bool remoteVersionKnown() const { return ((_vMajor > 0)||(_vMinor > 0)||(_vRevision > 0)); } - - /** - * @return True if peer has received a trust established packet (e.g. common network membership) in the past ZT_TRUST_EXPIRATION ms - */ - inline bool trustEstablished(const int64_t now) const { return ((now - _lastTrustEstablishedPacketReceived) < ZT_TRUST_EXPIRATION); } - - /** - * Rate limit gate for VERB_PUSH_DIRECT_PATHS - */ - inline bool rateGatePushDirectPaths(const int64_t now) - { - if ((now - _lastDirectPathPushReceive) <= ZT_PUSH_DIRECT_PATHS_CUTOFF_TIME) { - ++_directPathPushCutoffCount; - } else { - _directPathPushCutoffCount = 0; - } - _lastDirectPathPushReceive = now; - return (_directPathPushCutoffCount < ZT_PUSH_DIRECT_PATHS_CUTOFF_LIMIT); - } - - /** - * Rate limit gate for VERB_NETWORK_CREDENTIALS - */ - inline bool rateGateCredentialsReceived(const int64_t now) - { - if ((now - _lastCredentialsReceived) >= ZT_PEER_CREDENTIALS_RATE_LIMIT) { - _lastCredentialsReceived = now; - return true; - } - return false; - } - - /** - * Rate limit gate for sending of ERROR_NEED_MEMBERSHIP_CERTIFICATE - */ - inline bool rateGateRequestCredentials(const int64_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 int64_t now) - { - if ((now - _lastWhoisRequestReceived) >= ZT_PEER_WHOIS_RATE_LIMIT) { - _lastWhoisRequestReceived = now; - return true; - } - return false; - } - - /** - * See definition in Bond - */ - inline bool rateGateQoS(int64_t now, SharedPtr& path) - { - Mutex::Lock _l(_bond_m); - if(_bond) { - return _bond->rateGateQoS(now, path); - } - return false; // Default behavior. If there is no bond, we drop these - } - - /** - * See definition in Bond - */ - void receivedQoS(const SharedPtr& path, int64_t now, int count, uint64_t* rx_id, uint16_t* rx_ts) - { - Mutex::Lock _l(_bond_m); - if(_bond) { - _bond->receivedQoS(path, now, count, rx_id, rx_ts); - } - } - - /** - * See definition in Bond - */ - void processIncomingPathNegotiationRequest(uint64_t now, SharedPtr& path, int16_t remoteUtility) - { - Mutex::Lock _l(_bond_m); - if(_bond) { - _bond->processIncomingPathNegotiationRequest(now, path, remoteUtility); - } - } - - /** - * See definition in Bond - */ - inline bool rateGatePathNegotiation(int64_t now, SharedPtr& path) - { - Mutex::Lock _l(_bond_m); - if(_bond) { - return _bond->rateGatePathNegotiation(now, path); - } - return false; // Default behavior. If there is no bond, we drop these - } - - /** - * See definition in Bond - */ - bool flowHashingSupported() - { - Mutex::Lock _l(_bond_m); - if(_bond) { - return _bond->flowHashingSupported(); - } - return false; - } - - /** - * Serialize a peer for storage in local cache - * - * This does not serialize everything, just non-ephemeral information. - */ - template - inline void serializeForCache(Buffer &b) const - { - b.append((uint8_t)2); - - _id.serialize(b); - - b.append((uint16_t)_vProto); - b.append((uint16_t)_vMajor); - b.append((uint16_t)_vMinor); - b.append((uint16_t)_vRevision); - - { - Mutex::Lock _l(_paths_m); - unsigned int pc = 0; - for(unsigned int i=0;iaddress().serialize(b); - } - } - } - - template - inline static SharedPtr deserializeFromCache(int64_t now,void *tPtr,Buffer &b,const RuntimeEnvironment *renv) - { - try { - unsigned int ptr = 0; - if (b[ptr++] != 2) { - return SharedPtr(); - } - - Identity id; - ptr += id.deserialize(b,ptr); - if (!id) { - return SharedPtr(); - } - - SharedPtr p(new Peer(renv,renv->identity,id)); - - p->_vProto = b.template at(ptr); - ptr += 2; - p->_vMajor = b.template at(ptr); - ptr += 2; - p->_vMinor = b.template at(ptr); - ptr += 2; - p->_vRevision = b.template at(ptr); - ptr += 2; - - // When we deserialize from the cache we don't actually restore paths. We - // just try them and then re-learn them if they happen to still be up. - // Paths are fairly ephemeral in the real world in most cases. - const unsigned int tryPathCount = b.template at(ptr); - ptr += 2; - for(unsigned int i=0;iattemptToContactAt(tPtr,-1,inaddr,now,true); - } - } catch ( ... ) { - break; - } - } - - return p; - } catch ( ... ) { - return SharedPtr(); - } - } - - /** - * @return The bonding policy used to reach this peer - */ - SharedPtr bond() { return _bond; } - - /** - * @return The bonding policy used to reach this peer - */ - inline int8_t bondingPolicy() { - Mutex::Lock _l(_bond_m); - if (_bond) { - return _bond->policy(); - } - return ZT_BOND_POLICY_NONE; - } - - /** - * @return the number of links in this bond which are considered alive - */ - inline uint8_t getNumAliveLinks() { - Mutex::Lock _l(_paths_m); - if (_bond) { - return _bond->getNumAliveLinks(); - } - return 0; - } - - /** - * @return the number of links in this bond - */ - inline uint8_t getNumTotalLinks() { - Mutex::Lock _l(_paths_m); - if (_bond) { - return _bond->getNumTotalLinks(); - } - return 0; - } - - //inline const AES *aesKeysIfSupported() const - //{ return (const AES *)0; } - - inline const AES *aesKeysIfSupported() const - { return (_vProto >= 12) ? _aesKeys : (const AES *)0; } - - inline const AES *aesKeys() const - { return _aesKeys; } + ~Peer() { + Utils::burn(_key,sizeof(_key)); + } + + /** + * Construct a new peer + * + * @param renv Runtime environment + * @param myIdentity Identity of THIS node (for key agreement) + * @param peerIdentity Identity of peer + * @throws std::runtime_error Key agreement with peer's identity failed + */ + Peer(const RuntimeEnvironment *renv,const Identity &myIdentity,const Identity &peerIdentity); + + /** + * @return This peer's ZT address (short for identity().address()) + */ + inline const Address &address() const { return _id.address(); } + + /** + * @return This peer's identity + */ + inline const Identity &identity() const { return _id; } + + /** + * Log receipt of an authenticated packet + * + * This is called by the decode pipe when a packet is proven to be authentic + * and appears to be valid. + * + * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call + * @param path Path over which packet was received + * @param hops ZeroTier (not IP) hops + * @param packetId Packet ID + * @param verb Packet verb + * @param inRePacketId Packet ID in reply to (default: none) + * @param inReVerb Verb in reply to (for OK/ERROR, default: VERB_NOP) + * @param trustEstablished If true, some form of non-trivial trust (like allowed in network) has been established + * @param networkId Network ID if this pertains to a network, or 0 otherwise + */ + void received( + void *tPtr, + const SharedPtr &path, + const unsigned int hops, + const uint64_t packetId, + const unsigned int payloadLength, + const Packet::Verb verb, + const uint64_t inRePacketId, + const Packet::Verb inReVerb, + const bool trustEstablished, + const uint64_t networkId, + const int32_t flowId); + + /** + * Check whether we have an active path to this peer via the given address + * + * @param now Current time + * @param addr Remote address + * @return True if we have an active path to this destination + */ + inline bool hasActivePathTo(int64_t now,const InetAddress &addr) const + { + Mutex::Lock _l(_paths_m); + for(unsigned int i=0;iaddress() == addr)) { + return true; + } + } else { + break; + } + } + return false; + } + + /** + * Send via best direct path + * + * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call + * @param data Packet data + * @param len Packet length + * @param now Current time + * @param force If true, send even if path is not alive + * @return True if we actually sent something + */ + inline bool sendDirect(void *tPtr,const void *data,unsigned int len,int64_t now,bool force) + { + SharedPtr bp(getAppropriatePath(now,force)); + if (bp) { + return bp->send(RR,tPtr,data,len,now); + } + return false; + } + + /** + * Record incoming packets to + * + * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call + * @param path Path over which packet was received + * @param packetId Packet ID + * @param payloadLength Length of packet data payload + * @param verb Packet verb + * @param flowId Flow ID + * @param now Current time + */ + void recordIncomingPacket(const SharedPtr &path, const uint64_t packetId, + uint16_t payloadLength, const Packet::Verb verb, const int32_t flowId, int64_t now); + + /** + * + * @param path Path over which packet is being sent + * @param packetId Packet ID + * @param payloadLength Length of packet data payload + * @param verb Packet verb + * @param flowId Flow ID + * @param now Current time + */ + void recordOutgoingPacket(const SharedPtr &path, const uint64_t packetId, + uint16_t payloadLength, const Packet::Verb verb, const int32_t flowId, int64_t now); + + /** + * Record an invalid incoming packet. This packet failed + * MAC/compression/cipher checks and will now contribute to a + * Packet Error Ratio (PER). + * + * @param path Path over which packet was received + */ + void recordIncomingInvalidPacket(const SharedPtr& path); + + /** + * Get the most appropriate direct path based on current multipath and QoS configuration + * + * @param now Current time + * @param includeExpired If true, include even expired paths + * @return Best current path or NULL if none + */ + SharedPtr getAppropriatePath(int64_t now, bool includeExpired, int32_t flowId = -1); + + /** + * Send VERB_RENDEZVOUS to this and another peer via the best common IP scope and path + */ + void introduce(void *const tPtr,const int64_t now,const SharedPtr &other) const; + + /** + * Send a HELLO to this peer at a specified physical address + * + * No statistics or sent times are updated here. + * + * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call + * @param localSocket Local source socket + * @param atAddress Destination address + * @param now Current time + */ + void sendHELLO(void *tPtr,const int64_t localSocket,const InetAddress &atAddress,int64_t now); + + /** + * Send ECHO (or HELLO for older peers) to this peer at the given address + * + * No statistics or sent times are updated here. + * + * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call + * @param localSocket Local source socket + * @param atAddress Destination address + * @param now Current time + * @param sendFullHello If true, always send a full HELLO instead of just an ECHO + */ + void attemptToContactAt(void *tPtr,const int64_t localSocket,const InetAddress &atAddress,int64_t now,bool sendFullHello); + + /** + * Try a memorized or statically defined path if any are known + * + * Under the hood this is done periodically based on ZT_TRY_MEMORIZED_PATH_INTERVAL. + * + * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call + * @param now Current time + */ + void tryMemorizedPath(void *tPtr,int64_t now); + + /** + * A check to be performed periodically which determines whether multipath communication is + * possible with this peer. This check should be performed early in the life-cycle of the peer + * as well as during the process of learning new paths. + */ + void performMultipathStateCheck(void *tPtr, int64_t now); + + /** + * Send pings or keepalives depending on configured timeouts + * + * This also cleans up some internal data structures. It's called periodically from Node. + * + * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call + * @param now Current time + * @param inetAddressFamily Keep this address family alive, or -1 for any + * @return 0 if nothing sent or bit mask: bit 0x1 if IPv4 sent, bit 0x2 if IPv6 sent (0x3 means both sent) + */ + unsigned int doPingAndKeepalive(void *tPtr,int64_t now); + + /** + * Process a cluster redirect sent by this peer + * + * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call + * @param originatingPath Path from which redirect originated + * @param remoteAddress Remote address + * @param now Current time + */ + void clusterRedirect(void *tPtr,const SharedPtr &originatingPath,const InetAddress &remoteAddress,const int64_t now); + + /** + * Reset paths within a given IP scope and address family + * + * Resetting a path involves sending an ECHO to it and then deactivating + * it until or unless it responds. This is done when we detect a change + * to our external IP or another system change that might invalidate + * many or all current paths. + * + * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call + * @param scope IP scope + * @param inetAddressFamily Family e.g. AF_INET + * @param now Current time + */ + void resetWithinScope(void *tPtr,InetAddress::IpScope scope,int inetAddressFamily,int64_t now); + + /** + * @param now Current time + * @return All known paths to this peer + */ + inline std::vector< SharedPtr > paths(const int64_t now) const + { + std::vector< SharedPtr > pp; + Mutex::Lock _l(_paths_m); + for(unsigned int i=0;i bp(getAppropriatePath(now,false)); + if (bp) { + return (unsigned int)bp->latency(); + } + return 0xffff; + } + } + + /** + * This computes a quality score for relays and root servers + * + * If we haven't heard anything from these in ZT_PEER_ACTIVITY_TIMEOUT, they + * receive the worst possible quality (max unsigned int). Otherwise the + * quality is a product of latency and the number of potential missed + * pings. This causes roots and relays to switch over a bit faster if they + * fail. + * + * @return Relay quality score computed from latency and other factors, lower is better + */ + inline unsigned int relayQuality(const int64_t now) + { + const uint64_t tsr = now - _lastReceive; + if (tsr >= ZT_PEER_ACTIVITY_TIMEOUT) { + return (~(unsigned int)0); + } + unsigned int l = latency(now); + if (!l) { + l = 0xffff; + } + return (l * (((unsigned int)tsr / (ZT_PEER_PING_PERIOD + 1000)) + 1)); + } + + /** + * @return 256-bit secret symmetric encryption key + */ + inline const unsigned char *key() const { return _key; } + + /** + * Set the currently known remote version of this peer's client + * + * @param vproto Protocol version + * @param vmaj Major version + * @param vmin Minor version + * @param vrev Revision + */ + inline void setRemoteVersion(unsigned int vproto,unsigned int vmaj,unsigned int vmin,unsigned int vrev) + { + _vProto = (uint16_t)vproto; + _vMajor = (uint16_t)vmaj; + _vMinor = (uint16_t)vmin; + _vRevision = (uint16_t)vrev; + } + + inline unsigned int remoteVersionProtocol() const { return _vProto; } + inline unsigned int remoteVersionMajor() const { return _vMajor; } + inline unsigned int remoteVersionMinor() const { return _vMinor; } + inline unsigned int remoteVersionRevision() const { return _vRevision; } + + inline bool remoteVersionKnown() const { return ((_vMajor > 0)||(_vMinor > 0)||(_vRevision > 0)); } + + /** + * @return True if peer has received a trust established packet (e.g. common network membership) in the past ZT_TRUST_EXPIRATION ms + */ + inline bool trustEstablished(const int64_t now) const { return ((now - _lastTrustEstablishedPacketReceived) < ZT_TRUST_EXPIRATION); } + + /** + * Rate limit gate for VERB_PUSH_DIRECT_PATHS + */ + inline bool rateGatePushDirectPaths(const int64_t now) + { + if ((now - _lastDirectPathPushReceive) <= ZT_PUSH_DIRECT_PATHS_CUTOFF_TIME) { + ++_directPathPushCutoffCount; + } else { + _directPathPushCutoffCount = 0; + } + _lastDirectPathPushReceive = now; + return (_directPathPushCutoffCount < ZT_PUSH_DIRECT_PATHS_CUTOFF_LIMIT); + } + + /** + * Rate limit gate for VERB_NETWORK_CREDENTIALS + */ + inline bool rateGateCredentialsReceived(const int64_t now) + { + if ((now - _lastCredentialsReceived) >= ZT_PEER_CREDENTIALS_RATE_LIMIT) { + _lastCredentialsReceived = now; + return true; + } + return false; + } + + /** + * Rate limit gate for sending of ERROR_NEED_MEMBERSHIP_CERTIFICATE + */ + inline bool rateGateRequestCredentials(const int64_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 int64_t now) + { + if ((now - _lastWhoisRequestReceived) >= ZT_PEER_WHOIS_RATE_LIMIT) { + _lastWhoisRequestReceived = now; + return true; + } + return false; + } + + /** + * See definition in Bond + */ + inline bool rateGateQoS(int64_t now, SharedPtr& path) + { + Mutex::Lock _l(_bond_m); + if(_bond) { + return _bond->rateGateQoS(now, path); + } + return false; // Default behavior. If there is no bond, we drop these + } + + /** + * See definition in Bond + */ + void receivedQoS(const SharedPtr& path, int64_t now, int count, uint64_t* rx_id, uint16_t* rx_ts) + { + Mutex::Lock _l(_bond_m); + if(_bond) { + _bond->receivedQoS(path, now, count, rx_id, rx_ts); + } + } + + /** + * See definition in Bond + */ + void processIncomingPathNegotiationRequest(uint64_t now, SharedPtr& path, int16_t remoteUtility) + { + Mutex::Lock _l(_bond_m); + if(_bond) { + _bond->processIncomingPathNegotiationRequest(now, path, remoteUtility); + } + } + + /** + * See definition in Bond + */ + inline bool rateGatePathNegotiation(int64_t now, SharedPtr& path) + { + Mutex::Lock _l(_bond_m); + if(_bond) { + return _bond->rateGatePathNegotiation(now, path); + } + return false; // Default behavior. If there is no bond, we drop these + } + + /** + * See definition in Bond + */ + bool flowHashingSupported() + { + Mutex::Lock _l(_bond_m); + if(_bond) { + return _bond->flowHashingSupported(); + } + return false; + } + + /** + * Serialize a peer for storage in local cache + * + * This does not serialize everything, just non-ephemeral information. + */ + template + inline void serializeForCache(Buffer &b) const + { + b.append((uint8_t)2); + + _id.serialize(b); + + b.append((uint16_t)_vProto); + b.append((uint16_t)_vMajor); + b.append((uint16_t)_vMinor); + b.append((uint16_t)_vRevision); + + { + Mutex::Lock _l(_paths_m); + unsigned int pc = 0; + for(unsigned int i=0;iaddress().serialize(b); + } + } + } + + template + inline static SharedPtr deserializeFromCache(int64_t now,void *tPtr,Buffer &b,const RuntimeEnvironment *renv) + { + try { + unsigned int ptr = 0; + if (b[ptr++] != 2) { + return SharedPtr(); + } + + Identity id; + ptr += id.deserialize(b,ptr); + if (!id) { + return SharedPtr(); + } + + SharedPtr p(new Peer(renv,renv->identity,id)); + + p->_vProto = b.template at(ptr); + ptr += 2; + p->_vMajor = b.template at(ptr); + ptr += 2; + p->_vMinor = b.template at(ptr); + ptr += 2; + p->_vRevision = b.template at(ptr); + ptr += 2; + + // When we deserialize from the cache we don't actually restore paths. We + // just try them and then re-learn them if they happen to still be up. + // Paths are fairly ephemeral in the real world in most cases. + const unsigned int tryPathCount = b.template at(ptr); + ptr += 2; + for(unsigned int i=0;iattemptToContactAt(tPtr,-1,inaddr,now,true); + } + } catch ( ... ) { + break; + } + } + + return p; + } catch ( ... ) { + return SharedPtr(); + } + } + + /** + * @return The bonding policy used to reach this peer + */ + SharedPtr bond() { return _bond; } + + /** + * @return The bonding policy used to reach this peer + */ + inline int8_t bondingPolicy() { + Mutex::Lock _l(_bond_m); + if (_bond) { + return _bond->policy(); + } + return ZT_BOND_POLICY_NONE; + } + + /** + * @return the number of links in this bond which are considered alive + */ + inline uint8_t getNumAliveLinks() { + Mutex::Lock _l(_paths_m); + if (_bond) { + return _bond->getNumAliveLinks(); + } + return 0; + } + + /** + * @return the number of links in this bond + */ + inline uint8_t getNumTotalLinks() { + Mutex::Lock _l(_paths_m); + if (_bond) { + return _bond->getNumTotalLinks(); + } + return 0; + } + + //inline const AES *aesKeysIfSupported() const + //{ return (const AES *)0; } + + inline const AES *aesKeysIfSupported() const + { return (_vProto >= 12) ? _aesKeys : (const AES *)0; } + + inline const AES *aesKeys() const + { return _aesKeys; } private: - struct _PeerPath - { - _PeerPath() : lr(0),p(),priority(1) {} - int64_t lr; // time of last valid ZeroTier packet - SharedPtr p; - long priority; // >= 1, higher is better - }; + struct _PeerPath + { + _PeerPath() : lr(0),p(),priority(1) {} + int64_t lr; // time of last valid ZeroTier packet + SharedPtr p; + long priority; // >= 1, higher is better + }; - uint8_t _key[ZT_SYMMETRIC_KEY_SIZE]; - AES _aesKeys[2]; + uint8_t _key[ZT_SYMMETRIC_KEY_SIZE]; + AES _aesKeys[2]; - const RuntimeEnvironment *RR; + const RuntimeEnvironment *RR; - int64_t _lastReceive; // direct or indirect - int64_t _lastNontrivialReceive; // frames, things like netconf, etc. - int64_t _lastTriedMemorizedPath; - int64_t _lastDirectPathPushSent; - int64_t _lastDirectPathPushReceive; - int64_t _lastCredentialRequestSent; - int64_t _lastWhoisRequestReceived; - int64_t _lastCredentialsReceived; - int64_t _lastTrustEstablishedPacketReceived; - int64_t _lastSentFullHello; - int64_t _lastEchoCheck; + int64_t _lastReceive; // direct or indirect + int64_t _lastNontrivialReceive; // frames, things like netconf, etc. + int64_t _lastTriedMemorizedPath; + int64_t _lastDirectPathPushSent; + int64_t _lastDirectPathPushReceive; + int64_t _lastCredentialRequestSent; + int64_t _lastWhoisRequestReceived; + int64_t _lastCredentialsReceived; + int64_t _lastTrustEstablishedPacketReceived; + int64_t _lastSentFullHello; + int64_t _lastEchoCheck; - unsigned char _freeRandomByte; + unsigned char _freeRandomByte; - uint16_t _vProto; - uint16_t _vMajor; - uint16_t _vMinor; - uint16_t _vRevision; + uint16_t _vProto; + uint16_t _vMajor; + uint16_t _vMinor; + uint16_t _vRevision; - std::list< std::pair< Path *, int64_t > > _lastTriedPath; - Mutex _lastTriedPath_m; + std::list< std::pair< Path *, int64_t > > _lastTriedPath; + Mutex _lastTriedPath_m; - _PeerPath _paths[ZT_MAX_PEER_NETWORK_PATHS]; - Mutex _paths_m; - Mutex _bond_m; + _PeerPath _paths[ZT_MAX_PEER_NETWORK_PATHS]; + Mutex _paths_m; + Mutex _bond_m; - bool _isLeaf; + bool _isLeaf; - Identity _id; + Identity _id; - unsigned int _directPathPushCutoffCount; - unsigned int _echoRequestCutoffCount; + unsigned int _directPathPushCutoffCount; + unsigned int _echoRequestCutoffCount; - AtomicCounter __refCount; + AtomicCounter __refCount; - bool _localMultipathSupported; + bool _localMultipathSupported; - volatile bool _shouldCollectPathStatistics; + volatile bool _shouldCollectPathStatistics; - int32_t _lastComputedAggregateMeanLatency; + int32_t _lastComputedAggregateMeanLatency; - SharedPtr _bond; + SharedPtr _bond; #ifndef ZT_NO_PEER_METRICS - prometheus::Histogram &_peer_latency; - prometheus::simpleapi::gauge_metric_t _alive_path_count; - prometheus::simpleapi::gauge_metric_t _dead_path_count; - prometheus::simpleapi::counter_metric_t _incoming_packet; - prometheus::simpleapi::counter_metric_t _outgoing_packet; - prometheus::simpleapi::counter_metric_t _packet_errors; + prometheus::Histogram &_peer_latency; + prometheus::simpleapi::gauge_metric_t _alive_path_count; + prometheus::simpleapi::gauge_metric_t _dead_path_count; + prometheus::simpleapi::counter_metric_t _incoming_packet; + prometheus::simpleapi::counter_metric_t _outgoing_packet; + prometheus::simpleapi::counter_metric_t _packet_errors; #endif }; @@ -692,11 +692,11 @@ class Peer // Add a swap() for shared ptr's to peers to speed up peer sorts namespace std { - template<> - inline void swap(ZeroTier::SharedPtr &a,ZeroTier::SharedPtr &b) - { - a.swap(b); - } + template<> + inline void swap(ZeroTier::SharedPtr &a,ZeroTier::SharedPtr &b) + { + a.swap(b); + } } #endif diff --git a/node/Switch.cpp b/node/Switch.cpp index 06f3a476f..c80235110 100644 --- a/node/Switch.cpp +++ b/node/Switch.cpp @@ -1119,7 +1119,7 @@ void Switch::_sendViaSpecificPath(void *tPtr,SharedPtr peer,SharedPtrkey(),encrypt,peer->aesKeysIfSupported()); + packet.armor(peer->key(),encrypt,false,peer->aesKeysIfSupported(),peer->identity()); } RR->node->expectReplyTo(packet.packetId()); }