diff --git a/node/Peer.hpp b/node/Peer.hpp index be05aa3a8..a7240cb42 100644 --- a/node/Peer.hpp +++ b/node/Peer.hpp @@ -403,26 +403,6 @@ public: 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) - */ - static inline std::pair findCommonGround(const Peer &a,const Peer &b,uint64_t now) - { - std::pair 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(); - } - private: inline uint64_t _pathScore(const unsigned int p,const uint64_t now) const { diff --git a/node/Switch.cpp b/node/Switch.cpp index 7400fd62e..a5dd57e4d 100644 --- a/node/Switch.cpp +++ b/node/Switch.cpp @@ -237,7 +237,7 @@ void Switch::onRemotePacket(const InetAddress &localAddr,const InetAddress &from uint64_t &luts = _lastUniteAttempt[_LastUniteKey(source,destination)]; if ((now - luts) >= ZT_MIN_UNITE_INTERVAL) { luts = now; - unite(source,destination); + _unite(source,destination); } } else { #ifdef ZT_ENABLE_CLUSTER @@ -590,75 +590,6 @@ void Switch::send(const Packet &packet,bool encrypt) } } -bool Switch::unite(const Address &p1,const Address &p2) -{ - if ((p1 == RR->identity.address())||(p2 == RR->identity.address())) - return false; - SharedPtr p1p = RR->topology->getPeer(p1); - if (!p1p) - return false; - SharedPtr p2p = RR->topology->getPeer(p2); - if (!p2p) - return false; - - const uint64_t now = RR->node->now(); - - std::pair cg(Peer::findCommonGround(*p1p,*p2p,now)); - if ((!(cg.first))||(cg.first.ipScope() != cg.second.ipScope())) - return false; - - TRACE("unite: %s(%s) <> %s(%s)",p1.toString().c_str(),cg.second.toString().c_str(),p2.toString().c_str(),cg.first.toString().c_str()); - - /* Tell P1 where to find P2 and vice versa, sending the packets to P1 and - * P2 in randomized order in terms of which gets sent first. This is done - * since in a few cases NAT-t can be sensitive to slight timing differences - * in terms of when the two peers initiate. Normally this is accounted for - * by the nearly-simultaneous RENDEZVOUS kickoff from the relay, but - * given that relay are hosted on cloud providers this can in some - * cases have a few ms of latency between packet departures. By randomizing - * the order we make each attempted NAT-t favor one or the other going - * first, meaning if it doesn't succeed the first time it might the second - * and so forth. */ - unsigned int alt = (unsigned int)RR->node->prng() & 1; - unsigned int completed = alt + 2; - while (alt != completed) { - if ((alt & 1) == 0) { - // Tell p1 where to find p2. - Packet outp(p1,RR->identity.address(),Packet::VERB_RENDEZVOUS); - outp.append((unsigned char)0); - p2.appendTo(outp); - outp.append((uint16_t)cg.first.port()); - if (cg.first.isV6()) { - outp.append((unsigned char)16); - outp.append(cg.first.rawIpData(),16); - } else { - outp.append((unsigned char)4); - outp.append(cg.first.rawIpData(),4); - } - outp.armor(p1p->key(),true); - p1p->sendDirect(outp.data(),outp.size(),now,true); - } else { - // Tell p2 where to find p1. - Packet outp(p2,RR->identity.address(),Packet::VERB_RENDEZVOUS); - outp.append((unsigned char)0); - p1.appendTo(outp); - outp.append((uint16_t)cg.second.port()); - if (cg.second.isV6()) { - outp.append((unsigned char)16); - outp.append(cg.second.rawIpData(),16); - } else { - outp.append((unsigned char)4); - outp.append(cg.second.rawIpData(),4); - } - outp.armor(p2p->key(),true); - p2p->sendDirect(outp.data(),outp.size(),now,true); - } - ++alt; // counts up and also flips LSB - } - - return true; -} - void Switch::requestWhois(const Address &addr) { bool inserted = false; @@ -839,4 +770,96 @@ bool Switch::_trySend(const Packet &packet,bool encrypt) return false; } +bool Switch::_unite(const Address &p1,const Address &p2) +{ + if ((p1 == RR->identity.address())||(p2 == RR->identity.address())) + return false; + + const uint64_t now = RR->node->now(); + InetAddress *p1a = (InetAddress *)0; + InetAddress *p2a = (InetAddress *)0; + InetAddress p1v4,p1v6,p2v4,p2v6,uv4,uv6; + { + const SharedPtr p1p(RR->topology->getPeer(p1)); + const SharedPtr p2p(RR->topology->getPeer(p2)); + if ((!p1p)&&(!p2p)) return false; + if (p1p) p1p->getBestActiveAddresses(now,p1v4,p1v6); + if (p2p) p2p->getBestActiveAddresses(now,p2v4,p2v6); + } + if ((p1v6)&&(p2v6)) { + p1a = &p1v6; + p2a = &p2v6; + } else if ((p1v4)&&(p2v4)) { + p1a = &p1v4; + p2a = &p2v4; + } else { + SharedPtr upstream(RR->topology->getUpstreamPeer()); + if (!upstream) + return false; + upstream->getBestActiveAddresses(now,uv4,uv6); + if ((p1v6)&&(uv6)) { + p1a = &p1v6; + p2a = &uv6; + } else if ((p1v4)&&(uv4)) { + p1a = &p1v4; + p2a = &uv4; + } else if ((p2v6)&&(uv6)) { + p1a = &p2v6; + p2a = &uv6; + } else if ((p2v4)&&(uv4)) { + p1a = &p2v4; + p2a = &uv4; + } else return false; + } + + TRACE("unite: %s(%s) <> %s(%s)",p1.toString().c_str(),p1a->toString().c_str(),p2.toString().c_str(),p2a->toString().c_str()); + + /* Tell P1 where to find P2 and vice versa, sending the packets to P1 and + * P2 in randomized order in terms of which gets sent first. This is done + * since in a few cases NAT-t can be sensitive to slight timing differences + * in terms of when the two peers initiate. Normally this is accounted for + * by the nearly-simultaneous RENDEZVOUS kickoff from the relay, but + * given that relay are hosted on cloud providers this can in some + * cases have a few ms of latency between packet departures. By randomizing + * the order we make each attempted NAT-t favor one or the other going + * first, meaning if it doesn't succeed the first time it might the second + * and so forth. */ + unsigned int alt = (unsigned int)RR->node->prng() & 1; + const unsigned int completed = alt + 2; + while (alt != completed) { + if ((alt & 1) == 0) { + // Tell p1 where to find p2. + Packet outp(p1,RR->identity.address(),Packet::VERB_RENDEZVOUS); + outp.append((unsigned char)0); + p2.appendTo(outp); + outp.append((uint16_t)p2a->port()); + if (p2a->isV6()) { + outp.append((unsigned char)16); + outp.append(p2a->rawIpData(),16); + } else { + outp.append((unsigned char)4); + outp.append(p2a->rawIpData(),4); + } + send(outp,true); + } else { + // Tell p2 where to find p1. + Packet outp(p2,RR->identity.address(),Packet::VERB_RENDEZVOUS); + outp.append((unsigned char)0); + p1.appendTo(outp); + outp.append((uint16_t)p1a->port()); + if (p1a->isV6()) { + outp.append((unsigned char)16); + outp.append(p1a->rawIpData(),16); + } else { + outp.append((unsigned char)4); + outp.append(p1a->rawIpData(),4); + } + send(outp,true); + } + ++alt; // counts up and also flips LSB + } + + return true; +} + } // namespace ZeroTier diff --git a/node/Switch.hpp b/node/Switch.hpp index 7c903ef9e..f44eef482 100644 --- a/node/Switch.hpp +++ b/node/Switch.hpp @@ -97,17 +97,6 @@ public: */ void send(const Packet &packet,bool encrypt); - /** - * Send RENDEZVOUS to two peers to permit them to directly connect - * - * This only works if both peers are known, with known working direct - * links to this peer. The best link for each peer is sent to the other. - * - * @param p1 One of two peers (order doesn't matter) - * @param p2 Second of pair - */ - bool unite(const Address &p1,const Address &p2); - /** * Request WHOIS on a given address * @@ -138,6 +127,7 @@ public: private: Address _sendWhoisRequest(const Address &addr,const Address *peersAlreadyConsulted,unsigned int numPeersAlreadyConsulted); bool _trySend(const Packet &packet,bool encrypt); + bool _unite(const Address &p1,const Address &p2); const RuntimeEnvironment *const RR; uint64_t _lastBeaconResponse;