summaryrefslogtreecommitdiff
path: root/node/Peer.hpp
diff options
context:
space:
mode:
Diffstat (limited to 'node/Peer.hpp')
-rw-r--r--node/Peer.hpp313
1 files changed, 177 insertions, 136 deletions
diff --git a/node/Peer.hpp b/node/Peer.hpp
index b9d85404..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
@@ -41,14 +49,15 @@
#include "AtomicCounter.hpp"
#include "Hashtable.hpp"
#include "Mutex.hpp"
-#include "NonCopyable.hpp"
+
+#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>;
@@ -71,12 +80,12 @@ public:
/**
* @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
@@ -92,6 +101,7 @@ public:
* @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,
@@ -101,17 +111,26 @@ public:
const Packet::Verb verb,
const uint64_t inRePacketId,
const Packet::Verb inReVerb,
- const bool trustEstablished);
+ 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
* @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
{
Mutex::Lock _l(_paths_m);
- return ( ((addr.ss_family == AF_INET)&&(_v4Path.p)&&(_v4Path.p->address() == addr)&&(_v4Path.p->alive(now))) || ((addr.ss_family == AF_INET6)&&(_v6Path.p)&&(_v6Path.p->address() == addr)&&(_v6Path.p->alive(now))) );
+ 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;
}
/**
@@ -124,19 +143,27 @@ public:
* @param force If true, send even if path is not alive
* @return True if we actually sent something
*/
- bool sendDirect(void *tPtr,const void *data,unsigned int len,uint64_t now,bool force);
+ inline bool sendDirect(void *tPtr,const void *data,unsigned int len,int64_t now,bool force)
+ {
+ SharedPtr<Path> bp(getBestPath(now,force));
+ if (bp)
+ return bp->send(RR,tPtr,data,len,now);
+ return false;
+ }
/**
* Get the best current direct path
*
- * This does not check Path::alive(), but does return the most recently
- * active path and does check expiration (which is a longer timeout).
- *
* @param now Current time
* @param includeExpired If true, include even expired paths
* @return Best current path or NULL if none
*/
- SharedPtr<Path> getBestPath(uint64_t now,bool includeExpired);
+ 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
@@ -144,12 +171,11 @@ public:
* 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 localAddr Local address
+ * @param localSocket Local source socket
* @param atAddress Destination address
* @param now Current time
- * @param counter Outgoing packet counter
*/
- void sendHELLO(void *tPtr,const InetAddress &localAddr,const InetAddress &atAddress,uint64_t now,unsigned int counter);
+ 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
@@ -157,13 +183,12 @@ public:
* 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 localAddr Local address
+ * @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
- * @param counter Outgoing packet counter
*/
- void attemptToContactAt(void *tPtr,const InetAddress &localAddr,const InetAddress &atAddress,uint64_t now,bool sendFullHello,unsigned int counter);
+ 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
@@ -173,106 +198,85 @@ public:
* @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,uint64_t now);
+ void tryMemorizedPath(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 True if we have at least one direct path of the given family (or any if family is -1)
+ * @return 0 if nothing sent or bit mask: bit 0x1 if IPv4 sent, bit 0x2 if IPv6 sent (0x3 means both sent)
*/
- bool doPingAndKeepalive(void *tPtr,uint64_t now,int inetAddressFamily);
+ unsigned int doPingAndKeepalive(void *tPtr,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.
+ * 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 scope IP scope
- * @param inetAddressFamily Family e.g. AF_INET
+ * @param originatingPath Path from which redirect originated
+ * @param remoteAddress Remote address
* @param now Current time
*/
- inline void resetWithinScope(void *tPtr,InetAddress::IpScope scope,int inetAddressFamily,uint64_t now)
- {
- Mutex::Lock _l(_paths_m);
- if ((inetAddressFamily == AF_INET)&&(_v4Path.lr)&&(_v4Path.p->address().ipScope() == scope)) {
- attemptToContactAt(tPtr,_v4Path.p->localAddress(),_v4Path.p->address(),now,false,_v4Path.p->nextOutgoingCounter());
- _v4Path.p->sent(now);
- _v4Path.lr = 0; // path will not be used unless it speaks again
- } else if ((inetAddressFamily == AF_INET6)&&(_v6Path.lr)&&(_v6Path.p->address().ipScope() == scope)) {
- attemptToContactAt(tPtr,_v6Path.p->localAddress(),_v6Path.p->address(),now,false,_v6Path.p->nextOutgoingCounter());
- _v6Path.p->sent(now);
- _v6Path.lr = 0; // path will not be used unless it speaks again
- }
- }
+ void clusterRedirect(void *tPtr,const SharedPtr<Path> &originatingPath,const InetAddress &remoteAddress,const int64_t now);
/**
- * Indicate that the given address was provided by a cluster as a preferred destination
+ * Reset paths within a given IP scope and address family
*
- * @param addr Address cluster prefers that we use
- */
- inline void setClusterPreferred(const InetAddress &addr)
- {
- if (addr.ss_family == AF_INET)
- _v4ClusterPreferred = addr;
- else if (addr.ss_family == AF_INET6)
- _v6ClusterPreferred = addr;
- }
-
- /**
- * Fill parameters with V4 and V6 addresses if known and alive
+ * 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
- * @param v4 Result parameter to receive active IPv4 address, if any
- * @param v6 Result parameter to receive active IPv6 address, if any
*/
- inline void getRendezvousAddresses(uint64_t now,InetAddress &v4,InetAddress &v6) const
- {
- Mutex::Lock _l(_paths_m);
- if (((now - _v4Path.lr) < ZT_PEER_PATH_EXPIRATION)&&(_v4Path.p->alive(now)))
- v4 = _v4Path.p->address();
- if (((now - _v6Path.lr) < ZT_PEER_PATH_EXPIRATION)&&(_v6Path.p->alive(now)))
- v6 = _v6Path.p->address();
- }
+ 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<Path> > paths(const uint64_t now) const
+ inline std::vector< SharedPtr<Path> > paths(const int64_t now) const
{
std::vector< SharedPtr<Path> > pp;
Mutex::Lock _l(_paths_m);
- if (((now - _v4Path.lr) < ZT_PEER_PATH_EXPIRATION)&&(_v4Path.p->alive(now)))
- pp.push_back(_v4Path.p);
- if (((now - _v6Path.lr) < ZT_PEER_PATH_EXPIRATION)&&(_v6Path.p->alive(now)))
- pp.push_back(_v6Path.p);
+ 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 last receive of anything, whether direct or relayed
*/
- inline uint64_t lastReceive() const { return _lastReceive; }
+ 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 uint64_t now) const { return ((now - _lastReceive) < 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 isActive(uint64_t now) const { return ((now - _lastNontrivialReceive) < 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
@@ -285,43 +289,18 @@ 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_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);
- }
-
-#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 hasLocalClusterOptimalPath(uint64_t now) const
- {
- Mutex::Lock _l(_paths_m);
- return ( ((_v4Path.p)&&(_v4Path.p->alive(now))&&(!_v4Path.localClusterSuboptimal)) || ((_v6Path.p)&&(_v6Path.p->alive(now))&&(!_v6Path.localClusterSuboptimal)) );
- }
-#endif
-
- /**
* @return 256-bit secret symmetric encryption key
*/
inline const unsigned char *key() const { return _key; }
@@ -352,12 +331,12 @@ public:
/**
* @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 uint64_t now) const { return ((now - _lastTrustEstablishedPacketReceived) < ZT_TRUST_EXPIRATION); }
+ 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 uint64_t now)
+ inline bool rateGatePushDirectPaths(const int64_t now)
{
if ((now - _lastDirectPathPushReceive) <= ZT_PUSH_DIRECT_PATHS_CUTOFF_TIME)
++_directPathPushCutoffCount;
@@ -369,7 +348,7 @@ public:
/**
* Rate limit gate for VERB_NETWORK_CREDENTIALS
*/
- inline bool rateGateCredentialsReceived(const uint64_t now)
+ inline bool rateGateCredentialsReceived(const int64_t now)
{
if ((now - _lastCredentialsReceived) <= ZT_PEER_CREDENTIALS_CUTOFF_TIME)
++_credentialsCutoffCount;
@@ -381,7 +360,7 @@ public:
/**
* Rate limit gate for sending of ERROR_NEED_MEMBERSHIP_CERTIFICATE
*/
- inline bool rateGateRequestCredentials(const uint64_t now)
+ inline bool rateGateRequestCredentials(const int64_t now)
{
if ((now - _lastCredentialRequestSent) >= ZT_PEER_GENERAL_RATE_LIMIT) {
_lastCredentialRequestSent = now;
@@ -393,7 +372,7 @@ public:
/**
* Rate limit gate for inbound WHOIS requests
*/
- inline bool rateGateInboundWhoisRequest(const uint64_t now)
+ inline bool rateGateInboundWhoisRequest(const int64_t now)
{
if ((now - _lastWhoisRequestReceived) >= ZT_PEER_WHOIS_RATE_LIMIT) {
_lastWhoisRequestReceived = now;
@@ -405,7 +384,7 @@ public:
/**
* Rate limit gate for inbound ECHO requests
*/
- inline bool rateGateEchoRequest(const uint64_t now)
+ inline bool rateGateEchoRequest(const int64_t now)
{
if ((now - _lastEchoRequestReceived) >= ZT_PEER_GENERAL_RATE_LIMIT) {
_lastEchoRequestReceived = now;
@@ -417,7 +396,7 @@ public:
/**
* Rate gate incoming requests for network COM
*/
- inline bool rateGateIncomingComRequest(const uint64_t now)
+ inline bool rateGateIncomingComRequest(const int64_t now)
{
if ((now - _lastComRequestReceived) >= ZT_PEER_GENERAL_RATE_LIMIT) {
_lastComRequestReceived = now;
@@ -429,7 +408,7 @@ public:
/**
* Rate gate outgoing requests for network COM
*/
- inline bool rateGateOutgoingComRequest(const uint64_t now)
+ inline bool rateGateOutgoingComRequest(const int64_t now)
{
if ((now - _lastComRequestSent) >= ZT_PEER_GENERAL_RATE_LIMIT) {
_lastComRequestSent = now;
@@ -438,53 +417,115 @@ public:
return false;
}
+ /**
+ * 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);
+
+ 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;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);
+ }
+ }
+
+ template<unsigned int C>
+ inline static SharedPtr<Peer> deserializeFromCache(int64_t now,void *tPtr,Buffer<C> &b,const RuntimeEnvironment *renv)
+ {
+ 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;
+ }
+ }
+
+ return p;
+ } catch ( ... ) {
+ return SharedPtr<Peer>();
+ }
+ }
+
private:
struct _PeerPath
{
-#ifdef ZT_ENABLE_CLUSTER
- _PeerPath() : lr(0),p(),localClusterSuboptimal(false) {}
-#else
- _PeerPath() : lr(0),p() {}
-#endif
- uint64_t lr; // time of last valid ZeroTier packet
+ _PeerPath() : lr(0),p(),priority(1) {}
+ int64_t lr; // time of last valid ZeroTier packet
SharedPtr<Path> p;
-#ifdef ZT_ENABLE_CLUSTER
- bool localClusterSuboptimal; // true if our cluster has determined that we should not be serving this peer
-#endif
+ long priority; // >= 1, higher is better
};
uint8_t _key[ZT_PEER_SECRET_KEY_LENGTH];
const RuntimeEnvironment *RR;
- uint64_t _lastReceive; // direct or indirect
- uint64_t _lastNontrivialReceive; // frames, things like netconf, etc.
- uint64_t _lastTriedMemorizedPath;
- uint64_t _lastDirectPathPushSent;
- uint64_t _lastDirectPathPushReceive;
- uint64_t _lastCredentialRequestSent;
- uint64_t _lastWhoisRequestReceived;
- uint64_t _lastEchoRequestReceived;
- uint64_t _lastComRequestReceived;
- uint64_t _lastComRequestSent;
- uint64_t _lastCredentialsReceived;
- uint64_t _lastTrustEstablishedPacketReceived;
+ 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;
- InetAddress _v4ClusterPreferred;
- InetAddress _v6ClusterPreferred;
-
- _PeerPath _v4Path; // IPv4 direct path
- _PeerPath _v6Path; // IPv6 direct path
+ _PeerPath _paths[ZT_MAX_PEER_NETWORK_PATHS];
Mutex _paths_m;
Identity _id;
- unsigned int _latency;
unsigned int _directPathPushCutoffCount;
unsigned int _credentialsCutoffCount;