123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564 |
- /*
- * Copyright (c)2013-2020 ZeroTier, Inc.
- *
- * Use of this software is governed by the Business Source License included
- * in the LICENSE.TXT file in the project's root directory.
- *
- * Change Date: 2025-01-01
- *
- * On the date above, in accordance with the Business Source License, use
- * of this software will be governed by version 2.0 of the Apache License.
- */
- /****/
- #ifndef ZT_PEER_HPP
- #define ZT_PEER_HPP
- #include "Constants.hpp"
- #include "RuntimeEnvironment.hpp"
- #include "Node.hpp"
- #include "Path.hpp"
- #include "Address.hpp"
- #include "Utils.hpp"
- #include "Identity.hpp"
- #include "InetAddress.hpp"
- #include "SharedPtr.hpp"
- #include "Mutex.hpp"
- #include "Endpoint.hpp"
- #include "Locator.hpp"
- #include "Protocol.hpp"
- #include "AES.hpp"
- #include "EphemeralKey.hpp"
- #include "SymmetricKey.hpp"
- #include "Containers.hpp"
- #define ZT_PEER_MARSHAL_SIZE_MAX ( \
- 1 + \
- ZT_ADDRESS_LENGTH + \
- ZT_SYMMETRIC_KEY_SIZE + \
- ZT_IDENTITY_MARSHAL_SIZE_MAX + \
- 1 + ZT_LOCATOR_MARSHAL_SIZE_MAX + \
- 2 + ((8 + ZT_ENDPOINT_MARSHAL_SIZE_MAX) * ZT_PEER_ENDPOINT_CACHE_SIZE) + \
- (2 * 4) + \
- 2 )
- #define ZT_PEER_DEDUP_BUFFER_SIZE 1024
- #define ZT_PEER_DEDUP_BUFFER_MASK 1023U
- namespace ZeroTier {
- class Topology;
- /**
- * Peer on P2P Network (virtual layer 1)
- */
- class Peer
- {
- friend class SharedPtr< Peer >;
- friend class Topology;
- public:
- /**
- * Create an uninitialized peer
- *
- * New peers must be initialized via either init() or unmarshal() prior to
- * use or null pointer dereference may occur.
- *
- * @param renv Runtime environment
- */
- explicit Peer(const RuntimeEnvironment *renv);
- ~Peer();
- /**
- * Initialize peer with an identity
- *
- * @param peerIdentity The peer's identity
- * @return True if initialization was succcesful
- */
- bool init(const Identity &peerIdentity);
- /**
- * @return This peer's ZT address (short for identity().address())
- */
- ZT_INLINE Address address() const noexcept
- { return m_id.address(); }
- /**
- * @return This peer's identity
- */
- ZT_INLINE const Identity &identity() const noexcept
- { return m_id; }
- /**
- * @return Current locator or NULL if no locator is known
- */
- ZT_INLINE const SharedPtr< const Locator > &locator() const noexcept
- {
- RWMutex::RLock l(m_lock);
- return m_locator;
- }
- /**
- * Set or update peer locator
- *
- * This checks the locator's timestamp against the current locator and
- * replace it if newer.
- *
- * SECURITY: note that this does NOT validate the locator's signature
- * or structural validity. This MUST be done before calling this.
- *
- * @param loc Locator update
- * @return New locator or previous if it was not replaced.
- */
- ZT_INLINE SharedPtr< const Locator > setLocator(const SharedPtr< const Locator > &loc) noexcept
- {
- RWMutex::Lock l(m_lock);
- if ((loc) && ((!m_locator) || (m_locator->timestamp() < loc->timestamp())))
- m_locator = loc;
- return m_locator;
- }
- /**
- * 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 inReVerb In-reply verb for OK or ERROR verbs
- */
- void received(
- void *tPtr,
- const SharedPtr< Path > &path,
- unsigned int hops,
- uint64_t packetId,
- unsigned int payloadLength,
- Protocol::Verb verb,
- Protocol::Verb inReVerb);
- /**
- * Log sent data
- *
- * @param now Current time
- * @param bytes Number of bytes written
- */
- ZT_INLINE void sent(const int64_t now, const unsigned int bytes) noexcept
- {
- m_lastSend = now;
- m_outMeter.log(now, bytes);
- }
- /**
- * Called when traffic destined for a different peer is sent to this one
- *
- * @param now Current time
- * @param bytes Number of bytes relayed
- */
- ZT_INLINE void relayed(const int64_t now, const unsigned int bytes) noexcept
- { m_relayedMeter.log(now, bytes); }
- /**
- * Get the current best direct path or NULL if none
- *
- * @return Current best path or NULL if there is no direct path
- */
- ZT_INLINE SharedPtr< Path > path(const int64_t now) noexcept
- {
- if (likely((now - m_lastPrioritizedPaths) < ZT_PEER_PRIORITIZE_PATHS_INTERVAL)) {
- RWMutex::RLock l(m_lock);
- if (m_alivePathCount > 0)
- return m_paths[0];
- } else {
- RWMutex::Lock l(m_lock);
- m_prioritizePaths(now);
- if (m_alivePathCount > 0)
- return m_paths[0];
- }
- return SharedPtr< Path >();
- }
- /**
- * Send data to this peer over a specific path only
- *
- * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
- * @param now Current time
- * @param data Data to send
- * @param len Length in bytes
- * @param via Path over which to send data (may or may not be an already-learned path for this peer)
- */
- ZT_INLINE void send(void *tPtr, int64_t now, const void *data, unsigned int len, const SharedPtr< Path > &via) noexcept
- {
- via->send(RR, tPtr, data, len, now);
- sent(now, len);
- }
- /**
- * Send data to this peer over the best available path
- *
- * If there is a working direct path it will be used. Otherwise the data will be
- * sent via a root server.
- *
- * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
- * @param now Current time
- * @param data Data to send
- * @param len Length in bytes
- */
- void send(void *tPtr, int64_t now, const void *data, unsigned int len) noexcept;
- /**
- * Send a HELLO to this peer at a specified physical address.
- *
- * @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
- * @return Number of bytes sent
- */
- unsigned int hello(void *tPtr, int64_t localSocket, const InetAddress &atAddress, int64_t now);
- /**
- * Ping this peer if needed and/or perform other periodic tasks.
- *
- * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
- * @param now Current time
- * @param isRoot True if this peer is a root
- */
- void pulse(void *tPtr, int64_t now, bool isRoot);
- /**
- * Attempt to contact this peer at a given endpoint.
- *
- * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
- * @param now Current time
- * @param ep Endpoint to attempt to contact
- * @param tries Number of times to try (default: 1)
- */
- void contact(void *tPtr, int64_t now, const Endpoint &ep, int tries = 1);
- /**
- * 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);
- /**
- * @return Time of last receive of anything, whether direct or relayed
- */
- ZT_INLINE int64_t lastReceive() const noexcept
- { return m_lastReceive; }
- /**
- * @return Average latency of all direct paths or -1 if no direct paths or unknown
- */
- ZT_INLINE int latency() const noexcept
- {
- int ltot = 0;
- int lcnt = 0;
- RWMutex::RLock l(m_lock);
- for (unsigned int i = 0; i < m_alivePathCount; ++i) {
- int lat = m_paths[i]->latency();
- if (lat > 0) {
- ltot += lat;
- ++lcnt;
- }
- }
- return (ltot > 0) ? (lcnt / ltot) : -1;
- }
- /**
- * @return Cipher suite that should be used to communicate with this peer
- */
- ZT_INLINE uint8_t cipher() const noexcept
- {
- //if (m_vProto >= 11)
- // return ZT_PROTO_CIPHER_SUITE__AES_GMAC_SIV;
- return ZT_PROTO_CIPHER_SUITE__POLY1305_SALSA2012;
- }
- /**
- * @return The permanent shared key for this peer computed by simple identity agreement
- */
- ZT_INLINE SharedPtr< SymmetricKey > identityKey() noexcept
- { return m_identityKey; }
- /**
- * @return AES instance for HELLO dictionary / encrypted section encryption/decryption
- */
- ZT_INLINE const AES &identityHelloDictionaryEncryptionCipher() noexcept
- { return m_helloCipher; }
- /**
- * @return Key for HMAC on HELLOs
- */
- ZT_INLINE const uint8_t *identityHelloHmacKey() noexcept
- { return m_helloMacKey; }
- /**
- * @return Raw identity key bytes
- */
- ZT_INLINE const uint8_t *rawIdentityKey() noexcept
- {
- RWMutex::RLock l(m_lock);
- return m_identityKey->secret;
- }
- /**
- * @return Current best key: either the latest ephemeral or the identity key
- */
- ZT_INLINE SharedPtr< SymmetricKey > key() noexcept
- {
- RWMutex::RLock l(m_lock);
- return m_key();
- }
- /**
- * Check whether a key is ephemeral
- *
- * This is used to check whether a packet is received with forward secrecy enabled
- * or not.
- *
- * @param k Key to check
- * @return True if this key is ephemeral, false if it's the long-lived identity key
- */
- ZT_INLINE bool isEphemeral(const SharedPtr< SymmetricKey > &k) const noexcept
- { return m_identityKey != k; }
- /**
- * 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
- */
- ZT_INLINE void setRemoteVersion(unsigned int vproto, unsigned int vmaj, unsigned int vmin, unsigned int vrev) noexcept
- {
- m_vProto = (uint16_t)vproto;
- m_vMajor = (uint16_t)vmaj;
- m_vMinor = (uint16_t)vmin;
- m_vRevision = (uint16_t)vrev;
- }
- ZT_INLINE unsigned int remoteVersionProtocol() const noexcept
- { return m_vProto; }
- ZT_INLINE unsigned int remoteVersionMajor() const noexcept
- { return m_vMajor; }
- ZT_INLINE unsigned int remoteVersionMinor() const noexcept
- { return m_vMinor; }
- ZT_INLINE unsigned int remoteVersionRevision() const noexcept
- { return m_vRevision; }
- ZT_INLINE bool remoteVersionKnown() const noexcept
- { return (m_vMajor > 0) || (m_vMinor > 0) || (m_vRevision > 0); }
- /**
- * @return True if there is at least one alive direct path
- */
- bool directlyConnected(int64_t now);
- /**
- * Get all paths
- *
- * @param paths Vector of paths with the first path being the current preferred path
- */
- void getAllPaths(Vector< SharedPtr< Path > > &paths);
- /**
- * Save the latest version of this peer to the data store
- */
- void save(void *tPtr) const;
- // NOTE: peer marshal/unmarshal only saves/restores the identity, locator, most
- // recent bootstrap address, and version information.
- static constexpr int marshalSizeMax() noexcept
- { return ZT_PEER_MARSHAL_SIZE_MAX; }
- int marshal(uint8_t data[ZT_PEER_MARSHAL_SIZE_MAX]) const noexcept;
- int unmarshal(const uint8_t *restrict data, int len) noexcept;
- /**
- * Rate limit gate for inbound WHOIS requests
- */
- ZT_INLINE bool rateGateInboundWhoisRequest(const int64_t now) noexcept
- {
- if ((now - m_lastWhoisRequestReceived) >= ZT_PEER_WHOIS_RATE_LIMIT) {
- m_lastWhoisRequestReceived = now;
- return true;
- }
- return false;
- }
- /**
- * Rate limit gate for inbound ECHO requests
- */
- ZT_INLINE bool rateGateEchoRequest(const int64_t now) noexcept
- {
- if ((now - m_lastEchoRequestReceived) >= ZT_PEER_GENERAL_RATE_LIMIT) {
- m_lastEchoRequestReceived = now;
- return true;
- }
- return false;
- }
- /**
- * Rate limit gate for inbound probes
- */
- ZT_INLINE bool rateGateProbeRequest(const int64_t now) noexcept
- {
- if ((now - m_lastProbeReceived) > ZT_PEER_PROBE_RESPONSE_RATE_LIMIT) {
- m_lastProbeReceived = now;
- return true;
- }
- return false;
- }
- /**
- * Packet deduplication filter for incoming packets
- *
- * This flags a packet ID and returns true if the same packet ID was already
- * flagged. This is done in an atomic operation if supported.
- *
- * @param packetId Packet ID to check/flag
- * @return True if this is a duplicate
- */
- ZT_INLINE bool deduplicateIncomingPacket(const uint64_t packetId) noexcept
- {
- // TODO: should take instance ID into account too, but this isn't fully wired.
- return m_dedup[Utils::hash32((uint32_t)packetId) & ZT_PEER_DEDUP_BUFFER_MASK].exchange(packetId) == packetId;
- }
- private:
- void m_prioritizePaths(int64_t now);
- unsigned int m_sendProbe(void *tPtr, int64_t localSocket, const InetAddress &atAddress, const uint16_t *ports, unsigned int numPorts, int64_t now);
- void m_deriveSecondaryIdentityKeys() noexcept;
- ZT_INLINE SharedPtr< SymmetricKey > m_key() noexcept
- {
- // assumes m_lock is locked (for read at least)
- return (m_ephemeralKeys[0]) ? m_ephemeralKeys[0] : m_identityKey;
- }
- const RuntimeEnvironment *RR;
- // Read/write mutex for non-atomic non-const fields.
- RWMutex m_lock;
- // Static identity key
- SharedPtr< SymmetricKey > m_identityKey;
- // Cipher for encrypting or decrypting the encrypted section of HELLO packets.
- AES m_helloCipher;
- // Key for HELLO HMAC-SHA384
- uint8_t m_helloMacKey[ZT_SYMMETRIC_KEY_SIZE];
- // Currently active ephemeral public key pair
- EphemeralKey m_ephemeralPair;
- int64_t m_ephemeralPairTimestamp;
- // Current and previous ephemeral key
- SharedPtr< SymmetricKey > m_ephemeralKeys[2];
- Identity m_id;
- SharedPtr< const Locator > m_locator;
- // the last time something was sent or received from this peer (direct or indirect).
- std::atomic< int64_t > m_lastReceive;
- std::atomic< int64_t > m_lastSend;
- // The last time we sent a full HELLO to this peer.
- int64_t m_lastSentHello; // only checked while locked
- // The last time a WHOIS request was received from this peer (anti-DOS / anti-flood).
- std::atomic< int64_t > m_lastWhoisRequestReceived;
- // The last time an ECHO request was received from this peer (anti-DOS / anti-flood).
- std::atomic< int64_t > m_lastEchoRequestReceived;
- // The last time we sorted paths in order of preference. (This happens pretty often.)
- std::atomic< int64_t > m_lastPrioritizedPaths;
- // The last time we got a probe from this peer.
- std::atomic< int64_t > m_lastProbeReceived;
- // Deduplication buffer
- std::atomic< uint64_t > m_dedup[ZT_PEER_DEDUP_BUFFER_SIZE];
- // Meters measuring actual bandwidth in, out, and relayed via this peer (mostly if this is a root).
- Meter<> m_inMeter;
- Meter<> m_outMeter;
- Meter<> m_relayedMeter;
- // Direct paths sorted in descending order of preference.
- SharedPtr< Path > m_paths[ZT_MAX_PEER_NETWORK_PATHS];
- // Number of paths current alive (number of non-NULL entries in _paths).
- unsigned int m_alivePathCount;
- // For SharedPtr<>
- std::atomic< int > __refCount;
- struct p_EndpointCacheItem
- {
- Endpoint target;
- int64_t lastSeen;
- ZT_INLINE bool operator<(const p_EndpointCacheItem &ci) const noexcept
- { return lastSeen < ci.lastSeen; }
- ZT_INLINE p_EndpointCacheItem() noexcept: target(), lastSeen(0)
- {}
- };
- // Endpoint cache sorted in ascending order of times seen followed by first seen time.
- p_EndpointCacheItem m_endpointCache[ZT_PEER_ENDPOINT_CACHE_SIZE];
- struct p_TryQueueItem
- {
- ZT_INLINE p_TryQueueItem() :
- target(),
- iteration(0)
- {}
- ZT_INLINE p_TryQueueItem(const Endpoint &t, int iter) :
- target(t),
- iteration(iter)
- {}
- Endpoint target;
- int iteration;
- };
- List< p_TryQueueItem > m_tryQueue;
- Map< Endpoint, int64_t > m_lastTried;
- uint16_t m_vProto;
- uint16_t m_vMajor;
- uint16_t m_vMinor;
- uint16_t m_vRevision;
- };
- } // namespace ZeroTier
- #endif
|