Modify unite() to deal with a second layer of upstreams.

This commit is contained in:
Adam Ierymenko 2016-11-17 16:59:04 -08:00
parent 1615ef1114
commit 39333c9e8e
3 changed files with 94 additions and 101 deletions

View File

@ -403,26 +403,6 @@ public:
return false; 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<InetAddress,InetAddress> findCommonGround(const Peer &a,const Peer &b,uint64_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>();
}
private: private:
inline uint64_t _pathScore(const unsigned int p,const uint64_t now) const inline uint64_t _pathScore(const unsigned int p,const uint64_t now) const
{ {

View File

@ -237,7 +237,7 @@ void Switch::onRemotePacket(const InetAddress &localAddr,const InetAddress &from
uint64_t &luts = _lastUniteAttempt[_LastUniteKey(source,destination)]; uint64_t &luts = _lastUniteAttempt[_LastUniteKey(source,destination)];
if ((now - luts) >= ZT_MIN_UNITE_INTERVAL) { if ((now - luts) >= ZT_MIN_UNITE_INTERVAL) {
luts = now; luts = now;
unite(source,destination); _unite(source,destination);
} }
} else { } else {
#ifdef ZT_ENABLE_CLUSTER #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<Peer> p1p = RR->topology->getPeer(p1);
if (!p1p)
return false;
SharedPtr<Peer> p2p = RR->topology->getPeer(p2);
if (!p2p)
return false;
const uint64_t now = RR->node->now();
std::pair<InetAddress,InetAddress> 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) void Switch::requestWhois(const Address &addr)
{ {
bool inserted = false; bool inserted = false;
@ -839,4 +770,96 @@ bool Switch::_trySend(const Packet &packet,bool encrypt)
return false; 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<Peer> p1p(RR->topology->getPeer(p1));
const SharedPtr<Peer> 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<Peer> 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 } // namespace ZeroTier

View File

@ -97,17 +97,6 @@ public:
*/ */
void send(const Packet &packet,bool encrypt); 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 * Request WHOIS on a given address
* *
@ -138,6 +127,7 @@ public:
private: private:
Address _sendWhoisRequest(const Address &addr,const Address *peersAlreadyConsulted,unsigned int numPeersAlreadyConsulted); Address _sendWhoisRequest(const Address &addr,const Address *peersAlreadyConsulted,unsigned int numPeersAlreadyConsulted);
bool _trySend(const Packet &packet,bool encrypt); bool _trySend(const Packet &packet,bool encrypt);
bool _unite(const Address &p1,const Address &p2);
const RuntimeEnvironment *const RR; const RuntimeEnvironment *const RR;
uint64_t _lastBeaconResponse; uint64_t _lastBeaconResponse;