From ee5a988f1495fb1c3fa44215f8ef3287ddcb81af Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Mon, 13 Jan 2020 14:04:17 -0800 Subject: [PATCH] Wiring back up PUSH_DIRECT_PATHS and friends. --- go/native/GoGlue.cpp | 7 +- go/pkg/zerotier/identity.go | 19 ++- go/pkg/zerotier/node.go | 12 +- go/pkg/zerotier/root.go | 8 ++ node/Constants.hpp | 5 + node/IncomingPacket.cpp | 21 +-- node/Mutex.hpp | 3 +- node/Node.cpp | 4 +- node/Path.hpp | 7 + node/Peer.cpp | 255 +++++++++++++++++++++++------------- node/Peer.hpp | 62 +++------ node/Switch.cpp | 10 +- node/Topology.hpp | 17 +-- 13 files changed, 249 insertions(+), 181 deletions(-) create mode 100644 go/pkg/zerotier/root.go diff --git a/go/native/GoGlue.cpp b/go/native/GoGlue.cpp index 8e21eaafa..8eaabd9d1 100644 --- a/go/native/GoGlue.cpp +++ b/go/native/GoGlue.cpp @@ -28,14 +28,10 @@ #ifndef __WINDOWS__ #include -#include -#include #include #include #include #include -#include -#include #ifdef __BSD__ #include #endif @@ -104,7 +100,7 @@ const char *ZT_PLATFORM_DEFAULT_HOMEPATH = defaultHomePath.c_str(); /* These functions are implemented in Go in pkg/ztnode/node-callbacks.go */ extern "C" int goPathCheckFunc(void *,uint64_t,int,const void *,int); -extern "C" int goPathLookupFunc(void *,uint64_t,int,int *,uint8_t [16],int *); +extern "C" int goPathLookupFunc(void *,uint64_t,int,const ZT_Identity *,int *,uint8_t [16],int *); extern "C" void goStateObjectPutFunc(void *,int,const uint64_t [2],const void *,int); extern "C" int goStateObjectGetFunc(void *,int,const uint64_t [2],void *,unsigned int); extern "C" void goVirtualNetworkConfigFunc(void *,ZT_GoTap *,uint64_t,int,const ZT_VirtualNetworkConfig *); @@ -282,6 +278,7 @@ static int ZT_GoNode_PathLookupFunction( reinterpret_cast(uptr)->goUserPtr, ztAddress, desiredAddressFamily, + id, &family, ip, &port diff --git a/go/pkg/zerotier/identity.go b/go/pkg/zerotier/identity.go index 4b70798b8..d5099fc57 100644 --- a/go/pkg/zerotier/identity.go +++ b/go/pkg/zerotier/identity.go @@ -46,6 +46,18 @@ type Identity struct { privateKey []byte } +func newIdentityFromCIdentity(cid unsafe.Pointer) (*Identity, error) { + if uintptr(cid) == 0 { + return nil, ErrInvalidParameter + } + var idStrBuf [4096]byte + idStr := C.ZT_Identity_toString(cid,(*C.char)(unsafe.Pointer(&idStrBuf[0])),4096,1) + if uintptr(unsafe.Pointer(idStr)) == 0 { + return nil, ErrInternal + } + return NewIdentityFromString(C.GoString(idStr)) +} + // NewIdentity generates a new identity of the selected type func NewIdentity(identityType int) (*Identity, error) { cid := C.ZT_Identity_new(C.enum_ZT_Identity_Type(identityType)) @@ -53,12 +65,7 @@ func NewIdentity(identityType int) (*Identity, error) { return nil, ErrInternal } defer C.ZT_Identity_delete(cid) - var idStrBuf [4096]byte - idStr := C.ZT_Identity_toString(cid,(*C.char)(unsafe.Pointer(&idStrBuf[0])),4096,1) - if uintptr(unsafe.Pointer(idStr)) == 0 { - return nil, ErrInternal - } - return NewIdentityFromString(C.GoString(idStr)) + return newIdentityFromCIdentity(cid) } // NewIdentityFromString generates a new identity from its string representation. diff --git a/go/pkg/zerotier/node.go b/go/pkg/zerotier/node.go index 9ee8a0ab7..dd86e85c2 100644 --- a/go/pkg/zerotier/node.go +++ b/go/pkg/zerotier/node.go @@ -660,10 +660,10 @@ func (n *Node) pathCheck(ztAddress Address, af int, ip net.IP, port int) bool { return true } -func (n *Node) pathLookup(ztAddress Address) (net.IP, int) { +func (n *Node) pathLookup(id *Identity) (net.IP, int) { n.localConfigLock.RLock() defer n.localConfigLock.RUnlock() - virt := n.localConfig.Virtual[ztAddress] + virt := n.localConfig.Virtual[id.address] if len(virt.Try) > 0 { idx := rand.Int() % len(virt.Try) return virt.Try[idx].IP, virt.Try[idx].Port @@ -763,7 +763,7 @@ func goPathCheckFunc(gn unsafe.Pointer, ztAddress C.uint64_t, af C.int, ip unsaf } //export goPathLookupFunc -func goPathLookupFunc(gn unsafe.Pointer, ztAddress C.uint64_t, _ int, familyP, ipP, portP unsafe.Pointer) C.int { +func goPathLookupFunc(gn unsafe.Pointer, ztAddress C.uint64_t, desiredFamily int, identity, familyP, ipP, portP unsafe.Pointer) C.int { nodesByUserPtrLock.RLock() node := nodesByUserPtr[uintptr(gn)] nodesByUserPtrLock.RUnlock() @@ -771,7 +771,11 @@ func goPathLookupFunc(gn unsafe.Pointer, ztAddress C.uint64_t, _ int, familyP, i return 0 } - ip, port := node.pathLookup(Address(ztAddress)) + id, err := newIdentityFromCIdentity(identity) + if err != nil { + return 0 + } + ip, port := node.pathLookup(id) if len(ip) > 0 && port > 0 && port <= 65535 { ip4 := ip.To4() if len(ip4) == 4 { diff --git a/go/pkg/zerotier/root.go b/go/pkg/zerotier/root.go new file mode 100644 index 000000000..b9196b686 --- /dev/null +++ b/go/pkg/zerotier/root.go @@ -0,0 +1,8 @@ +package zerotier + +// Root is a root server with one or more permanent IPs. +type Root struct { + Identity Identity + DNSName string + PhysicalAddresses []InetAddress +} diff --git a/node/Constants.hpp b/node/Constants.hpp index 3393a5da9..51948213e 100644 --- a/node/Constants.hpp +++ b/node/Constants.hpp @@ -141,6 +141,11 @@ */ #define ZT_PEER_ACTIVITY_TIMEOUT ((ZT_PEER_PING_PERIOD * 2) + 5000) +/** + * Maximum interval between sort/prioritize of paths for a peer + */ +#define ZT_PEER_PRIORITIZE_PATHS_INTERVAL 5000 + /** * Delay between requests for updated network autoconf information * diff --git a/node/IncomingPacket.cpp b/node/IncomingPacket.cpp index 9d8acdf73..98fac0f24 100644 --- a/node/IncomingPacket.cpp +++ b/node/IncomingPacket.cpp @@ -348,6 +348,7 @@ ZT_ALWAYS_INLINE bool _doWHOIS(IncomingPacket &pkt,const RuntimeEnvironment *con ZT_ALWAYS_INLINE bool _doRENDEZVOUS(IncomingPacket &pkt,const RuntimeEnvironment *const RR,void *const tPtr,const SharedPtr &peer,const SharedPtr &path) { if (RR->topology->isRoot(peer->identity())) { + uint16_t junk = (uint16_t)Utils::random(); const Address with(pkt.field(ZT_PROTO_VERB_RENDEZVOUS_IDX_ZTADDRESS,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH); const SharedPtr rendezvousWith(RR->topology->get(with)); if (rendezvousWith) { @@ -355,9 +356,9 @@ ZT_ALWAYS_INLINE bool _doRENDEZVOUS(IncomingPacket &pkt,const RuntimeEnvironment const unsigned int addrlen = pkt[ZT_PROTO_VERB_RENDEZVOUS_IDX_ADDRLEN]; if ((port > 0)&&((addrlen == 4)||(addrlen == 16))) { InetAddress atAddr(pkt.field(ZT_PROTO_VERB_RENDEZVOUS_IDX_ADDRESS,addrlen),addrlen,port); - if (RR->node->shouldUsePathForZeroTierTraffic(tPtr,with,path->localSocket(),atAddr)) { - const uint64_t junk = Utils::random(); - RR->node->putPacket(tPtr,path->localSocket(),atAddr,&junk,4,2); // send low-TTL junk packet to 'open' local NAT(s) and stateful firewalls + if (rendezvousWith->shouldTryPath(tPtr,RR->node->now(),peer,atAddr)) { + if (atAddr.isV4()) + RR->node->putPacket(tPtr,path->localSocket(),atAddr,&junk,2,2); // IPv4 "firewall opener" rendezvousWith->sendHELLO(tPtr,path->localSocket(),atAddr,RR->node->now()); } } @@ -669,6 +670,7 @@ ZT_ALWAYS_INLINE bool _doPUSH_DIRECT_PATHS(IncomingPacket &pkt,const RuntimeEnvi unsigned int count = pkt.at(ZT_PACKET_IDX_PAYLOAD); unsigned int ptr = ZT_PACKET_IDX_PAYLOAD + 2; + uint16_t junk = (uint16_t)Utils::random(); while (count--) { /* unsigned int flags = (*this)[ptr++]; */ ++ptr; @@ -680,18 +682,17 @@ ZT_ALWAYS_INLINE bool _doPUSH_DIRECT_PATHS(IncomingPacket &pkt,const RuntimeEnvi switch(addrType) { case 4: { const InetAddress a(pkt.field(ptr,4),4,pkt.at(ptr + 4)); - if ((!peer->hasActivePathTo(now,a)) && // not already known - (RR->node->shouldUsePathForZeroTierTraffic(tPtr,peer->address(),-1,a)) ) // should use path - { - if (++countPerScope[(int)a.ipScope()][0] <= ZT_PUSH_DIRECT_PATHS_MAX_PER_SCOPE_AND_FAMILY) + if (peer->shouldTryPath(tPtr,now,peer,a)) { + if (++countPerScope[(int)a.ipScope()][0] <= ZT_PUSH_DIRECT_PATHS_MAX_PER_SCOPE_AND_FAMILY) { + RR->node->putPacket(tPtr,path->localSocket(),a,&junk,2,2); // IPv4 "firewall opener" + ++junk; peer->sendHELLO(tPtr,-1,a,now); + } } } break; case 6: { const InetAddress a(pkt.field(ptr,16),16,pkt.at(ptr + 16)); - if ((!peer->hasActivePathTo(now,a)) && // not already known - (RR->node->shouldUsePathForZeroTierTraffic(tPtr,peer->address(),-1,a)) ) // should use path - { + if (peer->shouldTryPath(tPtr,now,peer,a)) { if (++countPerScope[(int)a.ipScope()][1] <= ZT_PUSH_DIRECT_PATHS_MAX_PER_SCOPE_AND_FAMILY) peer->sendHELLO(tPtr,-1,a,now); } diff --git a/node/Mutex.hpp b/node/Mutex.hpp index 439ebe45a..2c5370391 100644 --- a/node/Mutex.hpp +++ b/node/Mutex.hpp @@ -60,13 +60,14 @@ public: ZT_ALWAYS_INLINE void lock() const { pthread_rwlock_wrlock(&((const_cast (this))->_mh)); } ZT_ALWAYS_INLINE void rlock() const { pthread_rwlock_rdlock(&((const_cast (this))->_mh)); } ZT_ALWAYS_INLINE void unlock() const { pthread_rwlock_unlock(&((const_cast (this))->_mh)); } + ZT_ALWAYS_INLINE void runlock() const { pthread_rwlock_unlock(&((const_cast (this))->_mh)); } class RLock { public: ZT_ALWAYS_INLINE RLock(RWMutex &m) : _m(&m) { m.rlock(); } ZT_ALWAYS_INLINE RLock(const RWMutex &m) : _m(const_cast(&m)) { _m->rlock(); } - ZT_ALWAYS_INLINE ~RLock() { _m->unlock(); } + ZT_ALWAYS_INLINE ~RLock() { _m->runlock(); } private: RWMutex *const _m; }; diff --git a/node/Node.cpp b/node/Node.cpp index 1ad2aeaec..ab4d00b73 100644 --- a/node/Node.cpp +++ b/node/Node.cpp @@ -174,10 +174,10 @@ struct _processBackgroundTasks_ping_eachPeer ZT_ALWAYS_INLINE bool operator()(const SharedPtr &peer,const bool isRoot) { unsigned int v4SendCount = 0,v6SendCount = 0; - peer->ping(tPtr,now,v4SendCount,v6SendCount); + peer->ping(tPtr,now,v4SendCount,v6SendCount,isRoot); if (isRoot) { - if ((now - peer->lastReceive()) <= ZT_PEER_PING_PERIOD) + if ((now - peer->lastReceive()) <= (ZT_PEER_PING_PERIOD + 5000)) online = true; if (v4SendCount == 0) { diff --git a/node/Path.hpp b/node/Path.hpp index a4ffa6e38..d1c97a4bf 100644 --- a/node/Path.hpp +++ b/node/Path.hpp @@ -92,6 +92,13 @@ public: */ bool send(const RuntimeEnvironment *RR,void *tPtr,const void *data,unsigned int len,int64_t now); + /** + * Explicitly update last sent time + * + * @param t Time of send + */ + ZT_ALWAYS_INLINE void sent(const uint64_t t) { _lastOut = t; } + /** * Called when a packet is received from this remote path, regardless of content * diff --git a/node/Peer.cpp b/node/Peer.cpp index 8ebb86011..0ada9e139 100644 --- a/node/Peer.cpp +++ b/node/Peer.cpp @@ -23,15 +23,25 @@ namespace ZeroTier { +struct _PathPriorityComparisonOperator +{ + ZT_ALWAYS_INLINE bool operator()(const SharedPtr &a,const SharedPtr &b) const + { + return ( ((a)&&(a->lastIn() > 0)) && ((!b)||(b->lastIn() <= 0)||(a->lastIn() < b->lastIn())) ); + } +}; + Peer::Peer(const RuntimeEnvironment *renv,const Identity &myIdentity,const Identity &peerIdentity) : RR(renv), _lastReceive(0), _lastWhoisRequestReceived(0), _lastEchoRequestReceived(0), _lastPushDirectPathsReceived(0), + _lastPushDirectPathsSent(0), _lastTriedStaticPath(0), + _lastPrioritizedPaths(0), _latency(0xffff), - _pathCount(0), + _alivePathCount(0), _id(peerIdentity), _vProto(0), _vMajor(0), @@ -54,85 +64,64 @@ void Peer::received( const uint64_t networkId) { const int64_t now = RR->node->now(); - _lastReceive = 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;inode->shouldUsePathForZeroTierTraffic(tPtr,_id.address(),path->localSocket(),path->address()))) { - Mutex::Lock _l(_paths_m); + if (verb == Packet::VERB_OK) { + RWMutex::Lock l(_paths_l); - // Paths are redundant if they duplicate an alive path to the same IP or - // with the same local socket and address family. - bool redundant = false; - unsigned int replacePath = ZT_MAX_PEER_NETWORK_PATHS; - for(unsigned int i=0;ialive(now)) && ( ((_paths[i]->localSocket() == path->localSocket())&&(_paths[i]->address().ss_family == path->address().ss_family)) || (_paths[i]->address().ipsEqual2(path->address())) ) ) { - redundant = true; - break; - } - // If the path is the same address and port, simply assume this is a replacement - if ( (_paths[i]->address().ipsEqual2(path->address()))) { - replacePath = i; - break; - } - } else break; - } - - // If the path isn't a duplicate of the same localSocket AND we haven't already determined a replacePath, - // then find the worst path and replace it. - if (!redundant && replacePath == ZT_MAX_PEER_NETWORK_PATHS) { - int replacePathQuality = 0; - for(unsigned int i=0;iaddress().ss_family == path->address().ss_family) && + (_paths[i]->localSocket() == path->localSocket()) && // TODO: should be localInterface when multipath is integrated + (_paths[i]->address().ipsEqual2(path->address()))) { + // If this is another path to the same place, swap it out as the + // one we just received from may replace an old one but don't + // learn it as a new path. + _paths[i] = path; + goto path_check_done; + } else { if (_paths[i]) { - const int q = _paths[i]->quality(now); - if (q > replacePathQuality) { - replacePathQuality = q; - replacePath = i; + if (_paths[i]->lastIn() > lastReceiveTimeMax) { + lastReceiveTimeMax = _paths[i]->lastIn(); + lastReceiveTimeMaxAt = i; } } else { - replacePath = i; - break; + lastReceiveTimeMax = 0x7fffffffffffffffLL; + lastReceiveTimeMaxAt = i; } } } - if (replacePath != ZT_MAX_PEER_NETWORK_PATHS) { - if (verb == Packet::VERB_OK) { - RR->t->peerLearnedNewPath(tPtr,networkId,*this,path,packetId); - _paths[replacePath] = path; - } else { - attemptToContact = true; - } + _lastPrioritizedPaths = now; + _paths[lastReceiveTimeMaxAt] = path; + _prioritizePaths(now); + RR->t->peerLearnedNewPath(tPtr,networkId,*this,path,packetId); + } else { + if (RR->node->shouldUsePathForZeroTierTraffic(tPtr,_id.address(),path->localSocket(),path->address())) { + sendHELLO(tPtr,path->localSocket(),path->address(),now); + path->sent(now); + RR->t->peerConfirmingUnknownPath(tPtr,networkId,*this,path,packetId,verb); } } - - if (attemptToContact) { - sendHELLO(tPtr,path->localSocket(),path->address(),now); - path->sent(now); - RR->t->peerConfirmingUnknownPath(tPtr,networkId,*this,path,packetId,verb); - } } - */ - // Periodically push direct paths to the peer, doing so more often if we do not - // currently have a direct path. +path_check_done: + const int64_t sinceLastPush = now - _lastPushDirectPathsSent; + if (sinceLastPush >= ((hops == 0) ? ZT_DIRECT_PATH_PUSH_INTERVAL_HAVEPATH : ZT_DIRECT_PATH_PUSH_INTERVAL)) { + _lastPushDirectPathsReceived = now; + } + /* const int64_t sinceLastPush = now - _lastDirectPathPushSent; if (sinceLastPush >= ((hops == 0) ? ZT_DIRECT_PATH_PUSH_INTERVAL_HAVEPATH : ZT_DIRECT_PATH_PUSH_INTERVAL)) { @@ -189,10 +178,23 @@ void Peer::received( */ } -bool Peer::hasActivePathTo(int64_t now,const InetAddress &addr) const +bool Peer::shouldTryPath(void *tPtr,int64_t now,const SharedPtr &suggestedBy,const InetAddress &addr) const { - // TODO - return false; + int maxHaveScope = -1; + { + RWMutex::RLock l(_paths_l); + for (unsigned int i = 0; i < _alivePathCount; ++i) { + if (_paths[i]) { + if (_paths[i]->address().ipsEqual2(addr)) + return false; + + int s = (int)_paths[i]->address().ipScope(); + if (s > maxHaveScope) + maxHaveScope = s; + } + } + } + return ( ((int)addr.ipScope() > maxHaveScope) && RR->node->shouldUsePathForZeroTierTraffic(tPtr,_id.address(),-1,addr) ); } void Peer::sendHELLO(void *tPtr,const int64_t localSocket,const InetAddress &atAddress,int64_t now) @@ -217,54 +219,125 @@ void Peer::sendHELLO(void *tPtr,const int64_t localSocket,const InetAddress &atA } } -void Peer::ping(void *tPtr,int64_t now,unsigned int &v4SendCount,unsigned int &v6SendCount) +void Peer::ping(void *tPtr,int64_t now,unsigned int &v4SendCount,unsigned int &v6SendCount,const bool pingAllAddressTypes) { - /* - Mutex::Lock _l(_paths_m); + RWMutex::RLock l(_paths_l); - unsigned int j = 0; - for(unsigned int i=0;ialive(now))) { + _lastPrioritizedPaths = now; + _prioritizePaths(now); + + if (_alivePathCount > 0) { + for (unsigned int i = 0; i < _alivePathCount; ++i) { sendHELLO(tPtr,_paths[i]->localSocket(),_paths[i]->address(),now); - _paths[i]->sent(now); + if (_paths[i]->address().isV4()) ++v4SendCount; else if (_paths[i]->address().isV6()) ++v6SendCount; - if (i != j) - _paths[j] = _paths[i]; - ++j; + if (!pingAllAddressTypes) + break; + } + } else { + SharedPtr r(RR->topology->root()); + if (r) { + SharedPtr rp(r->path(now)); + if (rp) { + sendHELLO(tPtr,rp->localSocket(),rp->address(),now); + rp->sent(now); + } } } - while(j < ZT_MAX_PEER_NETWORK_PATHS) { - _paths[j].zero(); - ++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]->ipScope() == scope)) { - sendHELLO(tPtr,_paths[i]->localSocket(),_paths[i]->address(),now); - _paths[i]->sent(now); - } - } else break; + RWMutex::RLock l(_paths_l); + for(unsigned int i=0; i < _alivePathCount; ++i) { + if ((_paths[i])&&((_paths[i]->address().ss_family == inetAddressFamily)&&(_paths[i]->address().ipScope() == scope))) { + sendHELLO(tPtr,_paths[i]->localSocket(),_paths[i]->address(),now); + _paths[i]->sent(now); + } + } +} + +void Peer::updateLatency(const unsigned int l) +{ + if ((l > 0)&&(l < 0xffff)) { + unsigned int lat = _latency; + if (lat < 0xffff) { + _latency = (l + l + lat) / 3; + } else { + _latency = l; + } + } +} + +bool Peer::sendDirect(void *tPtr,const void *data,const unsigned int len,const int64_t now) +{ + if ((now - _lastPrioritizedPaths) > ZT_PEER_PRIORITIZE_PATHS_INTERVAL) { + _lastPrioritizedPaths = now; + _paths_l.lock(); + _prioritizePaths(now); + if (_alivePathCount == 0) { + _paths_l.unlock(); + return false; + } + const bool r = _paths[0]->send(RR,tPtr,data,len,now); + _paths_l.unlock(); + return r; + } else { + _paths_l.rlock(); + if (_alivePathCount == 0) { + _paths_l.runlock(); + return false; + } + const bool r = _paths[0]->send(RR,tPtr,data,len,now); + _paths_l.runlock(); + return r; + } +} + +SharedPtr Peer::path(const int64_t now) +{ + if ((now - _lastPrioritizedPaths) > ZT_PEER_PRIORITIZE_PATHS_INTERVAL) { + _lastPrioritizedPaths = now; + RWMutex::Lock l(_paths_l); + _prioritizePaths(now); + if (_alivePathCount == 0) + return SharedPtr(); + return _paths[0]; + } else { + RWMutex::RLock l(_paths_l); + if (_alivePathCount == 0) + return SharedPtr(); + return _paths[0]; } - */ } void Peer::getAllPaths(std::vector< SharedPtr > &paths) { RWMutex::RLock l(_paths_l); paths.clear(); - paths.assign(_paths,_paths + _pathCount); + paths.assign(_paths,_paths + _alivePathCount); +} + +void Peer::_prioritizePaths(const int64_t now) +{ + // assumes _paths_l is locked for writing + std::sort(_paths,_paths + ZT_MAX_PEER_NETWORK_PATHS,_PathPriorityComparisonOperator()); + + for(int i=0;ialive(now))) { + _alivePathCount = i; + + for(;i; private: - inline Peer() {} + ZT_ALWAYS_INLINE Peer() {} public: ZT_ALWAYS_INLINE ~Peer() { Utils::burn(_key,sizeof(_key)); } @@ -92,13 +92,14 @@ public: uint64_t networkId); /** - * Check whether we have an active path to this peer via the given address + * Check whether a path to this peer should be tried if received via e.g. RENDEZVOUS OR PUSH_DIRECT_PATHS * * @param now Current time + * @param suggestingPeer Peer suggesting path (may be this peer) * @param addr Remote address * @return True if we have an active path to this destination */ - bool hasActivePathTo(int64_t now,const InetAddress &addr) const; + bool shouldTryPath(void *tPtr,int64_t now,const SharedPtr &suggestedBy,const InetAddress &addr) const; /** * Send a HELLO to this peer at a specified physical address @@ -113,16 +114,15 @@ public: void sendHELLO(void *tPtr,int64_t localSocket,const InetAddress &atAddress,int64_t now); /** - * Send pings to active paths - * - * This also cleans up some internal data structures. It's called periodically from Node. + * Send ping to this peer * * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call * @param now Current time * @param v4SendCount Number of IPv4 packets sent (result parameter) * @param v6SendCount Number of IPv6 packets sent (result parameter) + * @param pingAllAddressTypes If true, try to keep a link up for each address type/family */ - void ping(void *tPtr,int64_t now,unsigned int &v4SendCount,unsigned int &v6SendCount); + void ping(void *tPtr,int64_t now,unsigned int &v4SendCount,unsigned int &v6SendCount,bool pingAllAddressTypes); /** * Reset paths within a given IP scope and address family @@ -161,15 +161,7 @@ public: * * @param l New latency measurment (in milliseconds) */ - ZT_ALWAYS_INLINE void updateLatency(const unsigned int l) - { - if ((l > 0)&&(l < 0xffff)) { - unsigned int lat = _latency; - if (lat < 0xffff) - _latency = (l + lat) / 2; - else _latency = l; - } - } + void updateLatency(const unsigned int l); /** * @return 256-bit secret symmetric encryption key @@ -255,28 +247,12 @@ public: * @param now Current time * @return True if packet appears to have been sent, false if no path or send failed */ - ZT_ALWAYS_INLINE bool sendDirect(void *tPtr,const void *data,const unsigned int len,const int64_t now) - { - _paths_l.rlock(); - if (_pathCount == 0) { - _paths_l.unlock(); - return false; - } - const bool r = _paths[0]->send(RR,tPtr,data,len,now); - _paths_l.unlock(); - return r; - } + bool sendDirect(void *tPtr,const void *data,unsigned int len,const int64_t now); /** * @return Current best path */ - ZT_ALWAYS_INLINE SharedPtr path() - { - RWMutex::RLock l(_paths_l); - if (_pathCount == 0) - return SharedPtr(); - return _paths[0]; - } + SharedPtr path(int64_t now); /** * Get all paths @@ -286,20 +262,24 @@ public: void getAllPaths(std::vector< SharedPtr > &paths); private: + void _prioritizePaths(int64_t now); + uint8_t _key[ZT_PEER_SECRET_KEY_LENGTH]; const RuntimeEnvironment *RR; - int64_t _lastReceive; - int64_t _lastWhoisRequestReceived; - int64_t _lastEchoRequestReceived; - int64_t _lastPushDirectPathsReceived; - int64_t _lastTriedStaticPath; - unsigned int _latency; + volatile int64_t _lastReceive; + volatile int64_t _lastWhoisRequestReceived; + volatile int64_t _lastEchoRequestReceived; + volatile int64_t _lastPushDirectPathsReceived; + volatile int64_t _lastPushDirectPathsSent; + volatile int64_t _lastTriedStaticPath; + volatile int64_t _lastPrioritizedPaths; + volatile unsigned int _latency; AtomicCounter __refCount; - unsigned int _pathCount; + unsigned int _alivePathCount; SharedPtr _paths[ZT_MAX_PEER_NETWORK_PATHS]; RWMutex _paths_l; diff --git a/node/Switch.cpp b/node/Switch.cpp index 6c853e486..5c487cc94 100644 --- a/node/Switch.cpp +++ b/node/Switch.cpp @@ -59,7 +59,7 @@ void Switch::onRemotePacket(void *tPtr,const int64_t localSocket,const InetAddre fragment.incrementHops(); SharedPtr relayTo = RR->topology->get(destination); if ((!relayTo)||(!relayTo->sendDirect(tPtr,fragment.data(),fragment.size(),now))) { - relayTo = RR->topology->findRelayTo(now,destination); + relayTo = RR->topology->root(); if (relayTo) relayTo->sendDirect(tPtr,fragment.data(),fragment.size(),now); } @@ -127,7 +127,7 @@ void Switch::onRemotePacket(void *tPtr,const int64_t localSocket,const InetAddre packet.incrementHops(); SharedPtr relayTo = RR->topology->get(destination); if ((!relayTo)||(!relayTo->sendDirect(tPtr,packet.data(),packet.size(),now))) { - relayTo = RR->topology->findRelayTo(now,destination); + relayTo = RR->topology->root(); if ((relayTo)&&(relayTo->address() != source)) relayTo->sendDirect(tPtr,packet.data(),packet.size(),now); } @@ -585,7 +585,7 @@ bool Switch::_trySend(void *tPtr,Packet &packet,bool encrypt) const SharedPtr peer(RR->topology->get(destination)); if (peer) { - viaPath = peer->path(); + viaPath = peer->path(now); if (!viaPath) { if (peer->rateGateTryStaticPath(now)) { InetAddress tryAddr; @@ -599,9 +599,9 @@ bool Switch::_trySend(void *tPtr,Packet &packet,bool encrypt) } } - const SharedPtr relay(RR->topology->findRelayTo(now,destination)); + const SharedPtr relay(RR->topology->root()); if (relay) { - viaPath = relay->path(); + viaPath = relay->path(now); if (!viaPath) return false; } diff --git a/node/Topology.hpp b/node/Topology.hpp index 77f26cd44..cf30b86d0 100644 --- a/node/Topology.hpp +++ b/node/Topology.hpp @@ -111,7 +111,7 @@ public: _paths_l.rlock(); SharedPtr p(_paths[k]); - _paths_l.unlock(); + _paths_l.runlock(); if (p) return p; @@ -204,21 +204,6 @@ public: } } - /** - * Get the best relay to a given address, which may or may not be a root - * - * @param now Current time - * @param toAddr Destination address - * @return Best current relay or NULL if none - */ - ZT_ALWAYS_INLINE SharedPtr findRelayTo(const int64_t now,const Address &toAddr) - { - RWMutex::RLock l(_peers_l); - if (_rootPeers.empty()) - return SharedPtr(); - return _rootPeers[0]; - } - /** * @param allPeers vector to fill with all current peers */