diff options
author | Adam Ierymenko <adam.ierymenko@zerotier.com> | 2018-04-25 06:39:02 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-04-25 06:39:02 -0700 |
commit | 42ec780a6f6eedef4d8b1d8218bd72fc6ed75cc0 (patch) | |
tree | 7bf86c4d92d6a0f77eced79bfc33313c62c7b6dd /node/Peer.hpp | |
parent | 18c9dc8a0649c866eff9f299f20fa5b19c502e52 (diff) | |
parent | 4608880fb06700822d01e9e5d6729fcdeb82b64b (diff) | |
download | infinitytier-42ec780a6f6eedef4d8b1d8218bd72fc6ed75cc0.tar.gz infinitytier-42ec780a6f6eedef4d8b1d8218bd72fc6ed75cc0.zip |
Merge branch 'dev' into netbsd-support
Diffstat (limited to 'node/Peer.hpp')
-rw-r--r-- | node/Peer.hpp | 634 |
1 files changed, 283 insertions, 351 deletions
diff --git a/node/Peer.hpp b/node/Peer.hpp index 445535c8..b6f3c695 100644 --- a/node/Peer.hpp +++ b/node/Peer.hpp @@ -1,6 +1,6 @@ /* * ZeroTier One - Network Virtualization Everywhere - * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/ + * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/ * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -14,6 +14,14 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * -- + * + * You can be released from the requirements of the license by purchasing + * a commercial license. Buying such a license is mandatory as soon as you + * develop commercial closed-source software that incorporates or links + * directly against ZeroTier software without disclosing the source code + * of your own application. */ #ifndef ZT_PEER_HPP @@ -31,7 +39,6 @@ #include "../include/ZeroTierOne.h" #include "RuntimeEnvironment.hpp" -#include "CertificateOfMembership.hpp" #include "Path.hpp" #include "Address.hpp" #include "Utils.hpp" @@ -42,18 +49,15 @@ #include "AtomicCounter.hpp" #include "Hashtable.hpp" #include "Mutex.hpp" -#include "NonCopyable.hpp" -// Very rough computed estimate: (8 + 256 + 80 + (16 * 64) + (128 * 256) + (128 * 16)) -// 1048576 provides tons of headroom -- overflow would just cause peer not to be persisted -#define ZT_PEER_SUGGESTED_SERIALIZATION_BUFFER_SIZE 1048576 +#define ZT_PEER_MAX_SERIALIZED_STATE_SIZE (sizeof(Peer) + 32 + (sizeof(Path) * 2)) namespace ZeroTier { /** * Peer on P2P Network (virtual layer 1) */ -class Peer : NonCopyable +class Peer { friend class SharedPtr<Peer>; @@ -74,26 +78,14 @@ public: Peer(const RuntimeEnvironment *renv,const Identity &myIdentity,const Identity &peerIdentity); /** - * @return Time peer record was last used in any way - */ - inline uint64_t lastUsed() const throw() { return _lastUsed; } - - /** - * Log a use of this peer record (done by Topology when peers are looked up) - * - * @param now New time of last use - */ - inline void use(uint64_t now) throw() { _lastUsed = now; } - - /** * @return This peer's ZT address (short for identity().address()) */ - inline const Address &address() const throw() { return _id.address(); } + inline const Address &address() const { return _id.address(); } /** * @return This peer's identity */ - inline const Identity &identity() const throw() { return _id; } + inline const Identity &identity() const { return _id; } /** * Log receipt of an authenticated packet @@ -101,154 +93,190 @@ public: * This is called by the decode pipe when a packet is proven to be authentic * and appears to be valid. * - * @param RR Runtime environment - * @param localAddr Local address - * @param remoteAddr Internet address of sender + * @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( - const InetAddress &localAddr, - const InetAddress &remoteAddr, - unsigned int hops, - uint64_t packetId, - Packet::Verb verb, - uint64_t inRePacketId = 0, - Packet::Verb inReVerb = Packet::VERB_NOP); - - /** - * Get the current best direct path to this peer + void *tPtr, + const SharedPtr<Path> &path, + const unsigned int hops, + const uint64_t packetId, + const Packet::Verb verb, + const uint64_t inRePacketId, + const Packet::Verb inReVerb, + const bool trustEstablished, + const uint64_t networkId); + + /** + * Check whether we have an active path to this peer via the given address * * @param now Current time - * @return Best path or NULL if there are no active direct paths - */ - inline Path *getBestPath(uint64_t now) { return _getBestPath(now); } - - /** - * @param now Current time * @param addr Remote address * @return True if we have an active path to this destination */ - inline bool hasActivePathTo(uint64_t now,const InetAddress &addr) const + inline bool hasActivePathTo(int64_t now,const InetAddress &addr) const { - for(unsigned int p=0;p<_numPaths;++p) { - if ((_paths[p].active(now))&&(_paths[p].address() == addr)) - return true; + Mutex::Lock _l(_paths_m); + for(unsigned int i=0;i<ZT_MAX_PEER_NETWORK_PATHS;++i) { + if (_paths[i].p) { + if (((now - _paths[i].lr) < ZT_PEER_PATH_EXPIRATION)&&(_paths[i].p->address() == addr)) + return true; + } else break; } return false; } /** - * Set all paths in the same ss_family that are not this one to cluster suboptimal - * - * Addresses in other families are not affected. + * Send via best direct path * - * @param addr Address to make exclusive + * @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 void setClusterOptimalPathForAddressFamily(const InetAddress &addr) + inline bool sendDirect(void *tPtr,const void *data,unsigned int len,int64_t now,bool force) { - for(unsigned int p=0;p<_numPaths;++p) { - if (_paths[p].address().ss_family == addr.ss_family) { - _paths[p].setClusterSuboptimal(_paths[p].address() != addr); - } - } + SharedPtr<Path> bp(getBestPath(now,force)); + if (bp) + return bp->send(RR,tPtr,data,len,now); + return false; } /** - * Send via best path + * Get the best current direct path * - * @param data Packet data - * @param len Packet length * @param now Current time - * @return Path used on success or NULL on failure + * @param includeExpired If true, include even expired paths + * @return Best current path or NULL if none */ - inline Path *send(const void *data,unsigned int len,uint64_t now) - { - Path *const bestPath = getBestPath(now); - if (bestPath) { - if (bestPath->send(RR,data,len,now)) - return bestPath; - } - return (Path *)0; - } + SharedPtr<Path> getBestPath(int64_t now,bool includeExpired) const; + + /** + * 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<Peer> &other) const; /** * Send a HELLO to this peer at a specified physical address * - * This does not update any statistics. It's used to send initial HELLOs - * for NAT traversal and path verification. + * No statistics or sent times are updated here. * - * @param localAddr Local 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 - * @param ttl Desired IP TTL (default: 0 to leave alone) */ - void sendHELLO(const InetAddress &localAddr,const InetAddress &atAddress,uint64_t now,unsigned int ttl = 0); + void sendHELLO(void *tPtr,const int64_t localSocket,const InetAddress &atAddress,int64_t now); /** - * Send pings or keepalives depending on configured timeouts + * 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 inetAddressFamily Keep this address family alive, or 0 to simply pick current best ignoring family - * @return True if at least one direct path seems alive + * @param sendFullHello If true, always send a full HELLO instead of just an ECHO */ - bool doPingAndKeepalive(uint64_t now,int inetAddressFamily); + void attemptToContactAt(void *tPtr,const int64_t localSocket,const InetAddress &atAddress,int64_t now,bool sendFullHello); /** - * Push direct paths back to self if we haven't done so in the configured timeout + * 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 localAddr Local address - * @param toAddress Remote address to send push to (usually from path) + * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call * @param now Current time - * @param force If true, push regardless of rate limit - * @param includePrivatePaths If true, include local interface address paths (should only be done to peers with a trust relationship) - * @return True if something was actually sent */ - bool pushDirectPaths(const InetAddress &localAddr,const InetAddress &toAddress,uint64_t now,bool force,bool includePrivatePaths); + void tryMemorizedPath(void *tPtr,int64_t now); /** - * @return All known direct paths to this peer (active or inactive) + * 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) */ - inline std::vector<Path> paths() const - { - std::vector<Path> pp; - for(unsigned int p=0,np=_numPaths;p<np;++p) - pp.push_back(_paths[p]); - return pp; - } + unsigned int doPingAndKeepalive(void *tPtr,int64_t now); /** - * @return Time of last receive of anything, whether direct or relayed + * 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 */ - inline uint64_t lastReceive() const throw() { return _lastReceive; } + void clusterRedirect(void *tPtr,const SharedPtr<Path> &originatingPath,const InetAddress &remoteAddress,const int64_t now); /** - * @return Time of most recent unicast frame received + * 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 */ - inline uint64_t lastUnicastFrame() const throw() { return _lastUnicastFrame; } + void resetWithinScope(void *tPtr,InetAddress::IpScope scope,int inetAddressFamily,int64_t now); /** - * @return Time of most recent multicast frame received + * @param now Current time + * @return All known paths to this peer */ - inline uint64_t lastMulticastFrame() const throw() { return _lastMulticastFrame; } + inline std::vector< SharedPtr<Path> > paths(const int64_t now) const + { + std::vector< SharedPtr<Path> > pp; + Mutex::Lock _l(_paths_m); + for(unsigned int i=0;i<ZT_MAX_PEER_NETWORK_PATHS;++i) { + if (!_paths[i].p) break; + pp.push_back(_paths[i].p); + } + return pp; + } /** - * @return Time of most recent frame of any kind (unicast or multicast) + * @return Time of last receive of anything, whether direct or relayed */ - inline uint64_t lastFrame() const throw() { return std::max(_lastUnicastFrame,_lastMulticastFrame); } + inline int64_t lastReceive() const { return _lastReceive; } + + /** + * @return True if we've heard from this peer in less than ZT_PEER_ACTIVITY_TIMEOUT + */ + inline bool isAlive(const int64_t now) const { return ((now - _lastReceive) < ZT_PEER_ACTIVITY_TIMEOUT); } /** * @return True if this peer has sent us real network traffic recently */ - inline uint64_t activelyTransferringFrames(uint64_t now) const throw() { return ((now - lastFrame()) < ZT_PEER_ACTIVITY_TIMEOUT); } + inline int64_t isActive(int64_t now) const { return ((now - _lastNontrivialReceive) < ZT_PEER_ACTIVITY_TIMEOUT); } /** - * @return Latency in milliseconds or 0 if unknown + * @return Latency in milliseconds of best path or 0xffff if unknown / no paths */ - inline unsigned int latency() const { return _latency; } + inline unsigned int latency(const int64_t now) const + { + SharedPtr<Path> bp(getBestPath(now,false)); + if (bp) + return bp->latency(); + return 0xffff; + } /** * This computes a quality score for relays and root servers @@ -261,71 +289,21 @@ public: * * @return Relay quality score computed from latency and other factors, lower is better */ - inline unsigned int relayQuality(const uint64_t now) const + inline unsigned int relayQuality(const int64_t now) const { const uint64_t tsr = now - _lastReceive; if (tsr >= ZT_PEER_ACTIVITY_TIMEOUT) return (~(unsigned int)0); - unsigned int l = _latency; + unsigned int l = latency(now); if (!l) l = 0xffff; - return (l * (((unsigned int)tsr / (ZT_PEER_DIRECT_PING_DELAY + 1000)) + 1)); + return (l * (((unsigned int)tsr / (ZT_PEER_PING_PERIOD + 1000)) + 1)); } /** - * Update latency with a new direct measurment - * - * @param l Direct latency measurment in ms - */ - inline void addDirectLatencyMeasurment(unsigned int l) - { - unsigned int ol = _latency; - if ((ol > 0)&&(ol < 10000)) - _latency = (ol + std::min(l,(unsigned int)65535)) / 2; - else _latency = std::min(l,(unsigned int)65535); - } - - /** - * @param now Current time - * @return True if this peer has at least one active direct path - */ - inline bool hasActiveDirectPath(uint64_t now) const - { - for(unsigned int p=0;p<_numPaths;++p) { - if (_paths[p].active(now)) - return true; - } - return false; - } - -#ifdef ZT_ENABLE_CLUSTER - /** - * @param now Current time - * @return True if this peer has at least one active direct path that is not cluster-suboptimal - */ - inline bool hasClusterOptimalPath(uint64_t now) const - { - for(unsigned int p=0,np=_numPaths;p<np;++p) { - if ((_paths[p].active(now))&&(!_paths[p].isClusterSuboptimal())) - return true; - } - return false; - } -#endif - - /** - * Reset paths within a given scope - * - * @param scope IP scope of paths to reset - * @param now Current time - * @return True if at least one path was forgotten - */ - bool resetWithinScope(InetAddress::IpScope scope,uint64_t now); - - /** * @return 256-bit secret symmetric encryption key */ - inline const unsigned char *key() const throw() { return _key; } + inline const unsigned char *key() const { return _key; } /** * Set the currently known remote version of this peer's client @@ -343,259 +321,213 @@ public: _vRevision = (uint16_t)vrev; } - inline unsigned int remoteVersionProtocol() const throw() { return _vProto; } - inline unsigned int remoteVersionMajor() const throw() { return _vMajor; } - inline unsigned int remoteVersionMinor() const throw() { return _vMinor; } - inline unsigned int remoteVersionRevision() const throw() { return _vRevision; } + 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 throw() { return ((_vMajor > 0)||(_vMinor > 0)||(_vRevision > 0)); } + inline bool remoteVersionKnown() const { return ((_vMajor > 0)||(_vMinor > 0)||(_vRevision > 0)); } /** - * Get most recently active path addresses for IPv4 and/or IPv6 - * - * Note that v4 and v6 are not modified if they are not found, so - * initialize these to a NULL address to be able to check. - * - * @param now Current time - * @param v4 Result parameter to receive active IPv4 address, if any - * @param v6 Result parameter to receive active IPv6 address, if any + * @return True if peer has received a trust established packet (e.g. common network membership) in the past ZT_TRUST_EXPIRATION ms */ - void getBestActiveAddresses(uint64_t now,InetAddress &v4,InetAddress &v6) const; + inline bool trustEstablished(const int64_t now) const { return ((now - _lastTrustEstablishedPacketReceived) < ZT_TRUST_EXPIRATION); } /** - * Check network COM agreement with this peer - * - * @param nwid Network ID - * @param com Another certificate of membership - * @return True if supplied COM agrees with ours, false if not or if we don't have one + * Rate limit gate for VERB_PUSH_DIRECT_PATHS */ - bool networkMembershipCertificatesAgree(uint64_t nwid,const CertificateOfMembership &com) const; + 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); + } /** - * Check the validity of the COM and add/update if valid and new - * - * @param nwid Network ID - * @param com Externally supplied COM + * Rate limit gate for VERB_NETWORK_CREDENTIALS */ - bool validateAndSetNetworkMembershipCertificate(uint64_t nwid,const CertificateOfMembership &com); + inline bool rateGateCredentialsReceived(const int64_t now) + { + if ((now - _lastCredentialsReceived) <= ZT_PEER_CREDENTIALS_CUTOFF_TIME) + ++_credentialsCutoffCount; + else _credentialsCutoffCount = 0; + _lastCredentialsReceived = now; + return (_directPathPushCutoffCount < ZT_PEER_CREDEITIALS_CUTOFF_LIMIT); + } /** - * @param nwid Network ID - * @param now Current time - * @param updateLastPushedTime If true, go ahead and update the last pushed time regardless of return value - * @return Whether or not this peer needs another COM push from us + * Rate limit gate for sending of ERROR_NEED_MEMBERSHIP_CERTIFICATE */ - bool needsOurNetworkMembershipCertificate(uint64_t nwid,uint64_t now,bool updateLastPushedTime); + inline bool rateGateRequestCredentials(const int64_t now) + { + if ((now - _lastCredentialRequestSent) >= ZT_PEER_GENERAL_RATE_LIMIT) { + _lastCredentialRequestSent = now; + return true; + } + return false; + } /** - * Perform periodic cleaning operations - * - * @param now Current time + * Rate limit gate for inbound WHOIS requests */ - void clean(uint64_t now); + inline bool rateGateInboundWhoisRequest(const int64_t now) + { + if ((now - _lastWhoisRequestReceived) >= ZT_PEER_WHOIS_RATE_LIMIT) { + _lastWhoisRequestReceived = now; + return true; + } + return false; + } /** - * Update direct path push stats and return true if we should respond - * - * This is a circuit breaker to make VERB_PUSH_DIRECT_PATHS not particularly - * useful as a DDOS amplification attack vector. Otherwise a malicious peer - * could send loads of these and cause others to bombard arbitrary IPs with - * traffic. - * - * @param now Current time - * @return True if we should respond + * Rate limit gate for inbound ECHO requests */ - inline bool shouldRespondToDirectPathPush(const uint64_t now) + inline bool rateGateEchoRequest(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); + if ((now - _lastEchoRequestReceived) >= ZT_PEER_GENERAL_RATE_LIMIT) { + _lastEchoRequestReceived = now; + return true; + } + return false; } /** - * Find a common set of addresses by which two peers can link, if any - * - * @param a Peer A - * @param b Peer B - * @param now Current time - * @return Pair: B's address (to send to A), A's address (to send to B) + * Rate gate incoming requests for network COM */ - static inline std::pair<InetAddress,InetAddress> findCommonGround(const Peer &a,const Peer &b,uint64_t now) + inline bool rateGateIncomingComRequest(const int64_t now) { - std::pair<InetAddress,InetAddress> v4,v6; - b.getBestActiveAddresses(now,v4.first,v6.first); - a.getBestActiveAddresses(now,v4.second,v6.second); - if ((v6.first)&&(v6.second)) // prefer IPv6 if both have it since NAT-t is (almost) unnecessary - return v6; - else if ((v4.first)&&(v4.second)) - return v4; - else return std::pair<InetAddress,InetAddress>(); + if ((now - _lastComRequestReceived) >= ZT_PEER_GENERAL_RATE_LIMIT) { + _lastComRequestReceived = now; + return true; + } + return false; } - template<unsigned int C> - inline void serialize(Buffer<C> &b) const + /** + * Rate gate outgoing requests for network COM + */ + inline bool rateGateOutgoingComRequest(const int64_t now) { - Mutex::Lock _l(_networkComs_m); - - const unsigned int recSizePos = b.size(); - b.addSize(4); // space for uint32_t field length + if ((now - _lastComRequestSent) >= ZT_PEER_GENERAL_RATE_LIMIT) { + _lastComRequestSent = now; + return true; + } + return false; + } - b.append((uint16_t)1); // version of serialized Peer data + /** + * Serialize a peer for storage in local cache + * + * This does not serialize everything, just non-ephemeral information. + */ + template<unsigned int C> + inline void serializeForCache(Buffer<C> &b) const + { + b.append((uint8_t)1); - _id.serialize(b,false); + _id.serialize(b); - b.append((uint64_t)_lastUsed); - b.append((uint64_t)_lastReceive); - b.append((uint64_t)_lastUnicastFrame); - b.append((uint64_t)_lastMulticastFrame); - b.append((uint64_t)_lastAnnouncedTo); - b.append((uint64_t)_lastDirectPathPushSent); - b.append((uint64_t)_lastDirectPathPushReceive); - b.append((uint64_t)_lastPathSort); b.append((uint16_t)_vProto); b.append((uint16_t)_vMajor); b.append((uint16_t)_vMinor); b.append((uint16_t)_vRevision); - b.append((uint32_t)_latency); - b.append((uint16_t)_directPathPushCutoffCount); - - b.append((uint16_t)_numPaths); - for(unsigned int i=0;i<_numPaths;++i) - _paths[i].serialize(b); - - b.append((uint32_t)_networkComs.size()); - { - uint64_t *k = (uint64_t *)0; - _NetworkCom *v = (_NetworkCom *)0; - Hashtable<uint64_t,_NetworkCom>::Iterator i(const_cast<Peer *>(this)->_networkComs); - while (i.next(k,v)) { - b.append((uint64_t)*k); - b.append((uint64_t)v->ts); - v->com.serialize(b); - } - } - b.append((uint32_t)_lastPushedComs.size()); { - uint64_t *k = (uint64_t *)0; - uint64_t *v = (uint64_t *)0; - Hashtable<uint64_t,uint64_t>::Iterator i(const_cast<Peer *>(this)->_lastPushedComs); - while (i.next(k,v)) { - b.append((uint64_t)*k); - b.append((uint64_t)*v); + Mutex::Lock _l(_paths_m); + unsigned int pc = 0; + for(unsigned int i=0;i<ZT_MAX_PEER_NETWORK_PATHS;++i) { + if (_paths[i].p) + ++pc; + else break; } + b.append((uint16_t)pc); + for(unsigned int i=0;i<pc;++i) + _paths[i].p->address().serialize(b); } - - b.template setAt<uint32_t>(recSizePos,(uint32_t)(b.size() - (recSizePos + 4))); // set size } - /** - * Create a new Peer from a serialized instance - * - * @param renv Runtime environment - * @param myIdentity This node's identity - * @param b Buffer containing serialized Peer data - * @param p Pointer to current position in buffer, will be updated in place as buffer is read (value/result) - * @return New instance of Peer or NULL if serialized data was corrupt or otherwise invalid (may also throw an exception via Buffer) - */ template<unsigned int C> - static inline SharedPtr<Peer> deserializeNew(const RuntimeEnvironment *renv,const Identity &myIdentity,const Buffer<C> &b,unsigned int &p) + inline static SharedPtr<Peer> deserializeFromCache(int64_t now,void *tPtr,Buffer<C> &b,const RuntimeEnvironment *renv) { - const unsigned int recSize = b.template at<uint32_t>(p); p += 4; - if ((p + recSize) > b.size()) - return SharedPtr<Peer>(); // size invalid - if (b.template at<uint16_t>(p) != 1) - return SharedPtr<Peer>(); // version mismatch - p += 2; - - Identity npid; - p += npid.deserialize(b,p); - if (!npid) - return SharedPtr<Peer>(); - - SharedPtr<Peer> np(new Peer(renv,myIdentity,npid)); - - np->_lastUsed = b.template at<uint64_t>(p); p += 8; - np->_lastReceive = b.template at<uint64_t>(p); p += 8; - np->_lastUnicastFrame = b.template at<uint64_t>(p); p += 8; - np->_lastMulticastFrame = b.template at<uint64_t>(p); p += 8; - np->_lastAnnouncedTo = b.template at<uint64_t>(p); p += 8; - np->_lastDirectPathPushSent = b.template at<uint64_t>(p); p += 8; - np->_lastDirectPathPushReceive = b.template at<uint64_t>(p); p += 8; - np->_lastPathSort = b.template at<uint64_t>(p); p += 8; - np->_vProto = b.template at<uint16_t>(p); p += 2; - np->_vMajor = b.template at<uint16_t>(p); p += 2; - np->_vMinor = b.template at<uint16_t>(p); p += 2; - np->_vRevision = b.template at<uint16_t>(p); p += 2; - np->_latency = b.template at<uint32_t>(p); p += 4; - np->_directPathPushCutoffCount = b.template at<uint16_t>(p); p += 2; - - const unsigned int numPaths = b.template at<uint16_t>(p); p += 2; - for(unsigned int i=0;i<numPaths;++i) { - if (i < ZT_MAX_PEER_NETWORK_PATHS) { - p += np->_paths[np->_numPaths++].deserialize(b,p); - } else { - // Skip any paths beyond max, but still read stream - Path foo; - p += foo.deserialize(b,p); + try { + unsigned int ptr = 0; + if (b[ptr++] != 1) + return SharedPtr<Peer>(); + + Identity id; + ptr += id.deserialize(b,ptr); + if (!id) + return SharedPtr<Peer>(); + + SharedPtr<Peer> p(new Peer(renv,renv->identity,id)); + + p->_vProto = b.template at<uint16_t>(ptr); ptr += 2; + p->_vMajor = b.template at<uint16_t>(ptr); ptr += 2; + p->_vMinor = b.template at<uint16_t>(ptr); ptr += 2; + p->_vRevision = b.template at<uint16_t>(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<uint16_t>(ptr); ptr += 2; + for(unsigned int i=0;i<tryPathCount;++i) { + InetAddress inaddr; + try { + ptr += inaddr.deserialize(b,ptr); + if (inaddr) + p->attemptToContactAt(tPtr,-1,inaddr,now,true); + } catch ( ... ) { + break; + } } - } - - const unsigned int numNetworkComs = b.template at<uint32_t>(p); p += 4; - for(unsigned int i=0;i<numNetworkComs;++i) { - _NetworkCom &c = np->_networkComs[b.template at<uint64_t>(p)]; p += 8; - c.ts = b.template at<uint64_t>(p); p += 8; - p += c.com.deserialize(b,p); - } - const unsigned int numLastPushed = b.template at<uint32_t>(p); p += 4; - for(unsigned int i=0;i<numLastPushed;++i) { - const uint64_t nwid = b.template at<uint64_t>(p); p += 8; - const uint64_t ts = b.template at<uint64_t>(p); p += 8; - np->_lastPushedComs.set(nwid,ts); + return p; + } catch ( ... ) { + return SharedPtr<Peer>(); } - - return np; } private: - void _doDeadPathDetection(Path &p,const uint64_t now); - Path *_getBestPath(const uint64_t now); - Path *_getBestPath(const uint64_t now,int inetAddressFamily); + struct _PeerPath + { + _PeerPath() : lr(0),p(),priority(1) {} + int64_t lr; // time of last valid ZeroTier packet + SharedPtr<Path> p; + long priority; // >= 1, higher is better + }; - unsigned char _key[ZT_PEER_SECRET_KEY_LENGTH]; // computed with key agreement, not serialized + uint8_t _key[ZT_PEER_SECRET_KEY_LENGTH]; const RuntimeEnvironment *RR; - uint64_t _lastUsed; - uint64_t _lastReceive; // direct or indirect - uint64_t _lastUnicastFrame; - uint64_t _lastMulticastFrame; - uint64_t _lastAnnouncedTo; - uint64_t _lastDirectPathPushSent; - uint64_t _lastDirectPathPushReceive; - uint64_t _lastPathSort; + + 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 _lastEchoRequestReceived; + int64_t _lastComRequestReceived; + int64_t _lastComRequestSent; + int64_t _lastCredentialsReceived; + int64_t _lastTrustEstablishedPacketReceived; + int64_t _lastSentFullHello; + uint16_t _vProto; uint16_t _vMajor; uint16_t _vMinor; uint16_t _vRevision; + + _PeerPath _paths[ZT_MAX_PEER_NETWORK_PATHS]; + Mutex _paths_m; + Identity _id; - Path _paths[ZT_MAX_PEER_NETWORK_PATHS]; - unsigned int _numPaths; - unsigned int _latency; - unsigned int _directPathPushCutoffCount; - struct _NetworkCom - { - _NetworkCom() {} - _NetworkCom(uint64_t t,const CertificateOfMembership &c) : ts(t),com(c) {} - uint64_t ts; - CertificateOfMembership com; - }; - Hashtable<uint64_t,_NetworkCom> _networkComs; - Hashtable<uint64_t,uint64_t> _lastPushedComs; - Mutex _networkComs_m; + unsigned int _directPathPushCutoffCount; + unsigned int _credentialsCutoffCount; AtomicCounter __refCount; }; |