Bunch more cleanup, improvements to NAT traversal logic, finished updating Switch.

This commit is contained in:
Adam Ierymenko 2015-04-03 16:52:53 -07:00
parent ee0f56355b
commit 6eb9289367
11 changed files with 161 additions and 141 deletions

View file

@ -151,12 +151,7 @@ enum ZT1_NodeStatusCode
/** /**
* Node is online -- at least one upstream is reachable * Node is online -- at least one upstream is reachable
*/ */
ZT1_NODE_STATUS_ONLINE = 1, ZT1_NODE_STATUS_ONLINE = 1
/**
* Link desperation level has changed
*/
ZT1_NODE_STATUS_DESPERATION_CHANGE = 3
}; };
/** /**
@ -191,7 +186,7 @@ typedef struct
/** /**
* Current maximum link desperation metric * Current maximum link desperation metric
*/ */
int desperation; unsigned int desperation;
} ZT1_NodeStatus; } ZT1_NodeStatus;
/** /**
@ -393,11 +388,6 @@ typedef struct
*/ */
uint64_t bytesReceived; uint64_t bytesReceived;
/**
* This path's desperation metric (higher == worse)
*/
int desperation;
/** /**
* Is path fixed? (i.e. not learned, static) * Is path fixed? (i.e. not learned, static)
*/ */
@ -548,7 +538,7 @@ typedef int (*ZT1_DataStorePutFunction)(ZT1_Node *,const char *,const void *,uns
* on failure. Note that success does not (of course) guarantee packet * on failure. Note that success does not (of course) guarantee packet
* delivery. It only means that the packet appears to have been sent. * delivery. It only means that the packet appears to have been sent.
*/ */
typedef int (*ZT1_WirePacketSendFunction)(ZT1_Node *,const struct sockaddr_storage *,int,const void *,unsigned int); typedef int (*ZT1_WirePacketSendFunction)(ZT1_Node *,const struct sockaddr_storage *,unsigned int,const void *,unsigned int);
/** /**
* Function to send a frame out to a virtual network port * Function to send a frame out to a virtual network port
@ -602,7 +592,7 @@ enum ZT1_ResultCode ZT1_Node_processWirePacket(
ZT1_Node *node, ZT1_Node *node,
uint64_t now, uint64_t now,
const struct sockaddr_storage *remoteAddress, const struct sockaddr_storage *remoteAddress,
int linkDesperation, unsigned int linkDesperation,
const void *packetData, const void *packetData,
unsigned int packetLength, unsigned int packetLength,
uint64_t *nextCallDeadline); uint64_t *nextCallDeadline);

View file

@ -334,7 +334,7 @@
* a RENDEZVOUS message no more than this often. This instructs the peers * a RENDEZVOUS message no more than this often. This instructs the peers
* to attempt NAT-t and gives each the other's corresponding IP:port pair. * to attempt NAT-t and gives each the other's corresponding IP:port pair.
*/ */
#define ZT_MIN_UNITE_INTERVAL 30000 #define ZT_MIN_UNITE_INTERVAL 60000
/** /**
* Delay between initial direct NAT-t packet and more aggressive techniques * Delay between initial direct NAT-t packet and more aggressive techniques

View file

@ -31,14 +31,12 @@
#include <stdexcept> #include <stdexcept>
#include "Packet.hpp" #include "Packet.hpp"
#include "SocketManager.hpp"
#include "InetAddress.hpp" #include "InetAddress.hpp"
#include "Utils.hpp" #include "Utils.hpp"
#include "SharedPtr.hpp" #include "SharedPtr.hpp"
#include "AtomicCounter.hpp" #include "AtomicCounter.hpp"
#include "MulticastGroup.hpp" #include "MulticastGroup.hpp"
#include "Peer.hpp" #include "Peer.hpp"
#include "Socket.hpp"
/* /*
* The big picture: * The big picture:
@ -73,17 +71,17 @@ public:
* Create a new packet-in-decode * Create a new packet-in-decode
* *
* @param b Source buffer with raw packet data * @param b Source buffer with raw packet data
* @param fromSock Socket on which packet was received
* @param remoteAddress Address from which packet came * @param remoteAddress Address from which packet came
* @param linkDesperation Link desperation for link over which packet was received
* @throws std::out_of_range Range error processing packet * @throws std::out_of_range Range error processing packet
*/ */
template<unsigned int C2> template<unsigned int C2>
IncomingPacket(const Buffer<C2> &b,const SharedPtr<Socket> &fromSock,const InetAddress &remoteAddress) IncomingPacket(const Buffer<C2> &b,const InetAddress &remoteAddress,unsigned int linkDesperation)
throw(std::out_of_range) : throw(std::out_of_range) :
Packet(b), Packet(b),
_receiveTime(Utils::now()), _receiveTime(Utils::now()),
_fromSock(fromSock),
_remoteAddress(remoteAddress), _remoteAddress(remoteAddress),
_linkDesperation(linkDesperation),
__refCount() __refCount()
{ {
} }
@ -130,8 +128,8 @@ private:
void _sendErrorNeedCertificate(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer,uint64_t nwid); void _sendErrorNeedCertificate(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer,uint64_t nwid);
uint64_t _receiveTime; uint64_t _receiveTime;
SharedPtr<Socket> _fromSock;
InetAddress _remoteAddress; InetAddress _remoteAddress;
unsigned int _linkDesperation;
AtomicCounter __refCount; AtomicCounter __refCount;
}; };

View file

@ -95,7 +95,7 @@ Node::~Node()
ZT1_ResultCode Node::processWirePacket( ZT1_ResultCode Node::processWirePacket(
uint64_t now, uint64_t now,
const struct sockaddr_storage *remoteAddress, const struct sockaddr_storage *remoteAddress,
int linkDesperation, unsigned int linkDesperation,
const void *packetData, const void *packetData,
unsigned int packetLength, unsigned int packetLength,
uint64_t *nextCallDeadline) uint64_t *nextCallDeadline)
@ -207,7 +207,7 @@ enum ZT1_ResultCode ZT1_Node_processWirePacket(
ZT1_Node *node, ZT1_Node *node,
uint64_t now, uint64_t now,
const struct sockaddr_storage *remoteAddress, const struct sockaddr_storage *remoteAddress,
int linkDesperation, unsigned int linkDesperation,
const void *packetData, const void *packetData,
unsigned int packetLength, unsigned int packetLength,
uint64_t *nextCallDeadline) uint64_t *nextCallDeadline)

View file

@ -71,7 +71,7 @@ public:
ZT1_ResultCode processWirePacket( ZT1_ResultCode processWirePacket(
uint64_t now, uint64_t now,
const struct sockaddr_storage *remoteAddress, const struct sockaddr_storage *remoteAddress,
int linkDesperation, unsigned int linkDesperation,
const void *packetData, const void *packetData,
unsigned int packetLength, unsigned int packetLength,
uint64_t *nextCallDeadline); uint64_t *nextCallDeadline);
@ -113,7 +113,7 @@ public:
* @param desperation Link desperation for reaching this address * @param desperation Link desperation for reaching this address
* @return True if packet appears to have been sent * @return True if packet appears to have been sent
*/ */
inline bool putPacket(const InetAddress &addr,const void *data,unsigned int len,int desperation) inline bool putPacket(const InetAddress &addr,const void *data,unsigned int len,unsigned int desperation)
{ {
return (_wirePacketSendFunction( return (_wirePacketSendFunction(
reinterpret_cast<ZT1_Node *>(this), reinterpret_cast<ZT1_Node *>(this),

View file

@ -50,7 +50,6 @@ const char *Packet::verbString(Verb v)
case VERB_NETWORK_CONFIG_REFRESH: return "NETWORK_CONFIG_REFRESH"; case VERB_NETWORK_CONFIG_REFRESH: return "NETWORK_CONFIG_REFRESH";
case VERB_MULTICAST_GATHER: return "MULTICAST_GATHER"; case VERB_MULTICAST_GATHER: return "MULTICAST_GATHER";
case VERB_MULTICAST_FRAME: return "MULTICAST_FRAME"; case VERB_MULTICAST_FRAME: return "MULTICAST_FRAME";
case VERB_PHYSICAL_ADDRESS_CHANGED: return "PHYSICAL_ADDRESS_CHANGED";
} }
return "(unknown)"; return "(unknown)";
} }

View file

@ -717,45 +717,7 @@ public:
* <[6] multicast group MAC> * <[6] multicast group MAC>
* <[4] 32-bit multicast group ADI> * <[4] 32-bit multicast group ADI>
*/ */
VERB_MULTICAST_FRAME = 14, VERB_MULTICAST_FRAME = 14
/* Message sent to notify of a change in underlying address:
* <[1] flags>
* <[1] address type>
* <[2] 16-bit length of address>
* <[...] new address>
*
* Flags:
* 0x01 - Address was confirmed (if unset no confirmation was done)
*
* Address types:
* 0x01 - IPv4/UDP
* 0x02 - IPv6/UDP
* 0x03 - IPv4/TCP
* 0x04 - IPv6/TCP
* 0x05 - Ethernet MAC (raw framing address)
* 0x06 - Bluetooth MAC
* 0x07 - HTTP URL
* (other values are reserved)
*
* Address formats:
* IPv4: 32-bit address, 16-bit port in network byte order
* IPv6: 128-bit address, 16-bit port in network byte order
* Ethernet: 48-bit / 6-byte MAC
* Bluetooth: 48-bit / 6-byte Bluetooth MAC
* HTTP URL: ASCII string containing endpoint URL
*
* This should be sent by peers when a new remote address is detected by
* way of a new packet from a previously unknown underlying physical
* address. It may also be sent with flag 0x01 set once a new address is
* confirmed via a bi-directional transaction, indicating a higher
* confidence level.
*
* Peers can use this message to trigger reconnection semantics when
* mobility, DHCP reassignment, VM migration, or other factors lead to
* a change in physical address.
*/
VERB_PHYSICAL_ADDRESS_CHANGED = 15
}; };
/** /**

View file

@ -106,7 +106,7 @@ public:
* @param t Time of receive * @param t Time of receive
* @param d Link desperation of receive * @param d Link desperation of receive
*/ */
inline void received(uint64_t t,int d) throw() { _lastReceived = t; _lastReceiveDesperation = d; } inline void received(uint64_t t,unsigned int d) throw() { _lastReceived = t; _lastReceiveDesperation = d; }
/** /**
* @return Is this a fixed path? * @return Is this a fixed path?
@ -131,10 +131,10 @@ public:
* @param now Current time * @param now Current time
* @return Path desperation, starting at 0 * @return Path desperation, starting at 0
*/ */
inline int desperation(uint64_t now) const inline unsigned int desperation(uint64_t now) const
{ {
if ((_fixed)&&(_lastSend > _lastReceived)) if ((_fixed)&&(_lastSend > _lastReceived))
return std::max(_lastReceiveDesperation,(int)((_lastSend - _lastReceived) / ZT_DESPERATION_INCREMENT)); return std::max(_lastReceiveDesperation,(unsigned int)((_lastSend - _lastReceived) / ZT_DESPERATION_INCREMENT));
return _lastReceiveDesperation; return _lastReceiveDesperation;
} }
@ -194,7 +194,7 @@ private:
uint64_t _lastSend; uint64_t _lastSend;
uint64_t _lastReceived; uint64_t _lastReceived;
InetAddress _addr; InetAddress _addr;
int _lastReceiveDesperation; unsigned int _lastReceiveDesperation;
bool _fixed; bool _fixed;
}; };

View file

@ -137,7 +137,7 @@ public:
* @param now Current time * @param now Current time
* @return Best path or NULL if there are no active (or fixed) direct paths * @return Best path or NULL if there are no active (or fixed) direct paths
*/ */
Path *getBestPath(uint64_t now) inline Path *getBestPath(uint64_t now)
{ {
Path *bestPath = (Path *)0; Path *bestPath = (Path *)0;
uint64_t lrMax = 0; uint64_t lrMax = 0;
@ -150,6 +150,25 @@ public:
return bestPath; return bestPath;
} }
/**
* Send via best path
*
* @param RR Runtime environment
* @param data Packet data
* @param len Packet length
* @param now Current time
* @return Path used on success or NULL on failure
*/
inline Path *send(const RuntimeEnvironment *RR,const void *data,unsigned int len,uint64_t now)
{
Path *bestPath = getBestPath(now);
if (bestPath) {
if (bestPath->send(RR,data,len,now))
return bestPath;
}
return (Path *)0;
}
/** /**
* @return All known direct paths to this peer * @return All known direct paths to this peer
*/ */

View file

@ -36,16 +36,15 @@
#include "../include/ZeroTierOne.h" #include "../include/ZeroTierOne.h"
#include "Constants.hpp" #include "Constants.hpp"
#include "RuntimeEnvironment.hpp"
#include "Switch.hpp" #include "Switch.hpp"
#include "Node.hpp" #include "Node.hpp"
#include "EthernetTap.hpp"
#include "InetAddress.hpp" #include "InetAddress.hpp"
#include "Topology.hpp" #include "Topology.hpp"
#include "RuntimeEnvironment.hpp"
#include "Peer.hpp" #include "Peer.hpp"
#include "NodeConfig.hpp"
#include "CMWC4096.hpp" #include "CMWC4096.hpp"
#include "AntiRecursion.hpp" #include "AntiRecursion.hpp"
#include "Packet.hpp"
namespace ZeroTier { namespace ZeroTier {
@ -93,13 +92,13 @@ void Switch::onLocalEthernet(const SharedPtr<Network> &network,const MAC &from,c
* still happen because Windows likes to send broadcasts over interfaces that have little * still happen because Windows likes to send broadcasts over interfaces that have little
* to do with their intended target audience. :P */ * to do with their intended target audience. :P */
if (!RR->antiRec->checkEthernetFrame(data.data(),data.size())) { if (!RR->antiRec->checkEthernetFrame(data.data(),data.size())) {
TRACE("%s: rejected recursively addressed ZeroTier packet by tail match (type %s, length: %u)",network->tapDeviceName().c_str(),etherTypeName(etherType),data.size()); TRACE("%.16llx: rejected recursively addressed ZeroTier packet by tail match (type %s, length: %u)",network->id(),etherTypeName(etherType),data.size());
return; return;
} }
// Check to make sure this protocol is allowed on this network // Check to make sure this protocol is allowed on this network
if (!nconf->permitsEtherType(etherType)) { if (!nconf->permitsEtherType(etherType)) {
TRACE("%s: ignored tap: %s -> %s: ethertype %s not allowed on network %.16llx",network->tapDeviceName().c_str(),from.toString().c_str(),to.toString().c_str(),etherTypeName(etherType),(unsigned long long)network->id()); TRACE("%.16llx: ignored tap: %s -> %s: ethertype %s not allowed on network %.16llx",network->id(),from.toString().c_str(),to.toString().c_str(),etherTypeName(etherType),(unsigned long long)network->id());
return; return;
} }
@ -107,7 +106,7 @@ void Switch::onLocalEthernet(const SharedPtr<Network> &network,const MAC &from,c
bool fromBridged = false; bool fromBridged = false;
if (from != network->mac()) { if (from != network->mac()) {
if (!network->permitsBridging(RR->identity.address())) { if (!network->permitsBridging(RR->identity.address())) {
LOG("%s: %s -> %s %s not forwarded, bridging disabled on %.16llx or this peer not a bridge",network->tapDeviceName().c_str(),from.toString().c_str(),to.toString().c_str(),etherTypeName(etherType),network->id()); LOG("%.16llx: %s -> %s %s not forwarded, bridging disabled or this peer not a bridge",network->id(),from.toString().c_str(),to.toString().c_str(),etherTypeName(etherType));
return; return;
} }
fromBridged = true; fromBridged = true;
@ -126,7 +125,7 @@ void Switch::onLocalEthernet(const SharedPtr<Network> &network,const MAC &from,c
mg = MulticastGroup::deriveMulticastGroupForAddressResolution(InetAddress(data.field(24,4),4,0)); mg = MulticastGroup::deriveMulticastGroupForAddressResolution(InetAddress(data.field(24,4),4,0));
} else if (!nconf->enableBroadcast()) { } else if (!nconf->enableBroadcast()) {
// Don't transmit broadcasts if this network doesn't want them // Don't transmit broadcasts if this network doesn't want them
TRACE("%s: dropped broadcast since ff:ff:ff:ff:ff:ff is not enabled on network %.16llx",network->tapDeviceName().c_str(),network->id()); TRACE("%.16llx: dropped broadcast since ff:ff:ff:ff:ff:ff is not enabled",network->id());
return; return;
} }
} }
@ -140,11 +139,11 @@ void Switch::onLocalEthernet(const SharedPtr<Network> &network,const MAC &from,c
// Check multicast/broadcast bandwidth quotas and reject if quota exceeded // Check multicast/broadcast bandwidth quotas and reject if quota exceeded
if (!network->updateAndCheckMulticastBalance(mg,data.size())) { if (!network->updateAndCheckMulticastBalance(mg,data.size())) {
TRACE("%s: didn't multicast %d bytes, quota exceeded for multicast group %s",network->tapDeviceName().c_str(),(int)data.size(),mg.toString().c_str()); TRACE("%.16llx: didn't multicast %d bytes, quota exceeded for multicast group %s",network->id(),(int)data.size(),mg.toString().c_str());
return; return;
} }
TRACE("%s: MULTICAST %s -> %s %s %d",network->tapDeviceName().c_str(),from.toString().c_str(),mg.toString().c_str(),etherTypeName(etherType),(int)data.size()); TRACE("%.16llx: MULTICAST %s -> %s %s %d",network->id(),from.toString().c_str(),mg.toString().c_str(),etherTypeName(etherType),(int)data.size());
RR->mc->send( RR->mc->send(
((!nconf->isPublic())&&(nconf->com())) ? &(nconf->com()) : (const CertificateOfMembership *)0, ((!nconf->isPublic())&&(nconf->com())) ? &(nconf->com()) : (const CertificateOfMembership *)0,
@ -195,7 +194,7 @@ void Switch::onLocalEthernet(const SharedPtr<Network> &network,const MAC &from,c
send(outp,true); send(outp,true);
} }
} else { } else {
TRACE("%s: UNICAST: %s -> %s %s dropped, destination not a member of closed network %.16llx",network->tapDeviceName().c_str(),from.toString().c_str(),to.toString().c_str(),etherTypeName(etherType),network->id()); TRACE("%.16llx: UNICAST: %s -> %s %s dropped, destination not a member of private network",network->id(),from.toString().c_str(),to.toString().c_str(),etherTypeName(etherType));
} }
return; return;
@ -368,26 +367,32 @@ bool Switch::unite(const Address &p1,const Address &p2,bool force)
return true; return true;
} }
void Switch::contact(const SharedPtr<Peer> &peer,const InetAddress &atAddr) void Switch::contact(const SharedPtr<Peer> &peer,const InetAddress &atAddr,unsigned int maxDesperation)
{ {
// Send simple packet directly to indicated address -- works for most NATs
sendHELLO(peer,atAddr);
TRACE("sending NAT-t message to %s(%s)",peer->address().toString().c_str(),atAddr.toString().c_str()); TRACE("sending NAT-t message to %s(%s)",peer->address().toString().c_str(),atAddr.toString().c_str());
uint64_t now = RR->node->now();
Packet outp(peer->address(),RR->identity.address(),Packet::VERB_NOP);
outp.armor(peer->key(),false);
/* Note that we don't log this as a "sent" packet or send it via the peer's
* normal send() path. That's because this is a trial packet to an
* unconfirmed address.
*
* First attempt is always at desperation zero. Then we escalate to max
* before escalating through other NAT-t strategies. */
RR->node->putPacket(atAddr,outp.data(),outp.size(),0);
// If we have not punched through after this timeout, open refreshing can of whupass // If we have not punched through after this timeout, open refreshing can of whupass
{ {
Mutex::Lock _l(_contactQueue_m); Mutex::Lock _l(_contactQueue_m);
_contactQueue.push_back(ContactQueueEntry(peer,Utils::now() + ZT_NAT_T_TACTICAL_ESCALATION_DELAY,atAddr)); _contactQueue.push_back(ContactQueueEntry(peer,now + ZT_NAT_T_TACTICAL_ESCALATION_DELAY,atAddr,maxDesperation));
} }
// Kick main loop out of wait so that it can pick up this
// change to our scheduled timer tasks.
RR->sm->whack();
} }
void Switch::requestWhois(const Address &addr) void Switch::requestWhois(const Address &addr)
{ {
//TRACE("requesting WHOIS for %s",addr.toString().c_str());
bool inserted = false; bool inserted = false;
{ {
Mutex::Lock _l(_outstandingWhoisRequests_m); Mutex::Lock _l(_outstandingWhoisRequests_m);
@ -436,38 +441,84 @@ void Switch::doAnythingWaitingForPeer(const SharedPtr<Peer> &peer)
unsigned long Switch::doTimerTasks() unsigned long Switch::doTimerTasks()
{ {
unsigned long nextDelay = ~((unsigned long)0); // big number, caller will cap return value unsigned long nextDelay = ~((unsigned long)0); // big number, caller will cap return value
uint64_t now = Utils::now(); const uint64_t now = RR->node->now();
{ { // Aggressive NAT traversal time!
Mutex::Lock _l(_contactQueue_m); Mutex::Lock _l(_contactQueue_m);
for(std::list<ContactQueueEntry>::iterator qi(_contactQueue.begin());qi!=_contactQueue.end();) { for(std::list<ContactQueueEntry>::iterator qi(_contactQueue.begin());qi!=_contactQueue.end();) {
if (now >= qi->fireAtTime) { if (now >= qi->fireAtTime) {
if (!qi->peer->hasActiveDirectPath(now)) { if (qi->peer->hasActiveDirectPath(now)) {
TRACE("deploying aggressive NAT-t against %s(%s)",qi->peer->address().toString().c_str(),qi->inaddr.toString().c_str()); // We've successfully NAT-t'd, so cancel attempt
_contactQueue.erase(qi++);
continue;
} else {
// Nope, nothing yet. Time to kill some kittens.
/* Shotgun approach -- literally -- against symmetric NATs. Most of these Packet outp(qi->peer->address(),RR->identity.address(),Packet::VERB_NOP);
* either increment or decrement ports so this gets a good number. Also try outp.armor(qi->peer->key(),false);
* the original port one more time for good measure, since sometimes it
* fails first time around. */ switch(qi->strategyIteration) {
int p = (int)qi->inaddr.port() - 2; case 0:
for(int k=0;k<6;++k) { // First strategy: rifle method: direct packet to known port
if ((p > 0)&&(p <= 0xffff)) { ++qi->strategyIteration;
qi->inaddr.setPort((unsigned int)p); RR->node->putPacket(qi->inaddr,outp.data(),outp.size(),qi->currentDesperation);
sendHELLO(qi->peer,qi->inaddr); break;
} case 1: {
++p; // Second strategy: shotgun method up: try a few ports above
++qi->strategyIteration;
int p = (int)qi->inaddr.port();
for(int i=0;i<6;++i) {
if (++p > 0xffff)
break;
InetAddress tmpaddr(qi->inaddr);
tmpaddr.setPort((unsigned int)p);
RR->node->putPacket(tmpaddr,outp.data(),outp.size(),qi->currentDesperation);
}
} break;
case 2: {
// Third strategy: shotgun method down: try a few ports below
++qi->strategyIteration;
int p = (int)qi->inaddr.port();
for(int i=0;i<3;++i) {
if (--p < 1024)
break;
InetAddress tmpaddr(qi->inaddr);
tmpaddr.setPort((unsigned int)p);
RR->node->putPacket(tmpaddr,outp.data(),outp.size(),qi->currentDesperation);
}
} break;
case 3:
// Fourth strategy: sawed-off shotgun: try random non-privileged ports
for(int i=0;i<16;++i) {
InetAddress tmpaddr(qi->inaddr);
tmpaddr.setPort((unsigned int)(1024 + (RR->prng->next32() % (65536 - 1024))));
RR->node->putPacket(tmpaddr,outp.data(),outp.size(),qi->currentDesperation);
}
// Escalate link desperation after all strategies attempted
++qi->currentDesperation;
if (qi->currentDesperation > qi->maxDesperation) {
// We've tried all strategies at all levels of desperation, give up.
_contactQueue.erase(qi++);
continue;
} else {
// Otherwise restart at new link desperation level (e.g. try a tougher transport)
qi->strategyIteration = 0;
}
break;
} }
}
_contactQueue.erase(qi++); qi->fireAtTime = now + ZT_NAT_T_TACTICAL_ESCALATION_DELAY;
nextDelay = std::min(nextDelay,(unsigned long)ZT_NAT_T_TACTICAL_ESCALATION_DELAY);
}
} else { } else {
nextDelay = std::min(nextDelay,(unsigned long)(qi->fireAtTime - now)); nextDelay = std::min(nextDelay,(unsigned long)(qi->fireAtTime - now));
++qi;
} }
++qi; // if qi was erased, loop will have continued before here
} }
} }
{ { // Retry outstanding WHOIS requests
Mutex::Lock _l(_outstandingWhoisRequests_m); Mutex::Lock _l(_outstandingWhoisRequests_m);
for(std::map< Address,WhoisRequest >::iterator i(_outstandingWhoisRequests.begin());i!=_outstandingWhoisRequests.end();) { for(std::map< Address,WhoisRequest >::iterator i(_outstandingWhoisRequests.begin());i!=_outstandingWhoisRequests.end();) {
unsigned long since = (unsigned long)(now - i->second.lastSent); unsigned long since = (unsigned long)(now - i->second.lastSent);
@ -483,12 +534,14 @@ unsigned long Switch::doTimerTasks()
TRACE("WHOIS %s (retry %u)",i->first.toString().c_str(),i->second.retries); TRACE("WHOIS %s (retry %u)",i->first.toString().c_str(),i->second.retries);
nextDelay = std::min(nextDelay,(unsigned long)ZT_WHOIS_RETRY_DELAY); nextDelay = std::min(nextDelay,(unsigned long)ZT_WHOIS_RETRY_DELAY);
} }
} else nextDelay = std::min(nextDelay,ZT_WHOIS_RETRY_DELAY - since); } else {
nextDelay = std::min(nextDelay,ZT_WHOIS_RETRY_DELAY - since);
}
++i; ++i;
} }
} }
{ { // Time out TX queue packets that never got WHOIS lookups or other info.
Mutex::Lock _l(_txQueue_m); Mutex::Lock _l(_txQueue_m);
for(std::multimap< Address,TXQueueEntry >::iterator i(_txQueue.begin());i!=_txQueue.end();) { for(std::multimap< Address,TXQueueEntry >::iterator i(_txQueue.begin());i!=_txQueue.end();) {
if (_trySend(i->second.packet,i->second.encrypt)) if (_trySend(i->second.packet,i->second.encrypt))
@ -500,7 +553,7 @@ unsigned long Switch::doTimerTasks()
} }
} }
{ { // Time out RX queue packets that never got WHOIS lookups or other info.
Mutex::Lock _l(_rxQueue_m); Mutex::Lock _l(_rxQueue_m);
for(std::list< SharedPtr<IncomingPacket> >::iterator i(_rxQueue.begin());i!=_rxQueue.end();) { for(std::list< SharedPtr<IncomingPacket> >::iterator i(_rxQueue.begin());i!=_rxQueue.end();) {
if ((now - (*i)->receiveTime()) > ZT_RECEIVE_QUEUE_TIMEOUT) { if ((now - (*i)->receiveTime()) > ZT_RECEIVE_QUEUE_TIMEOUT) {
@ -510,7 +563,7 @@ unsigned long Switch::doTimerTasks()
} }
} }
{ { // Time out packets that didn't get all their fragments.
Mutex::Lock _l(_defragQueue_m); Mutex::Lock _l(_defragQueue_m);
for(std::map< uint64_t,DefragQueueEntry >::iterator i(_defragQueue.begin());i!=_defragQueue.end();) { for(std::map< uint64_t,DefragQueueEntry >::iterator i(_defragQueue.begin());i!=_defragQueue.end();) {
if ((now - i->second.creationTime) > ZT_FRAGMENTED_PACKET_RECEIVE_TIMEOUT) { if ((now - i->second.creationTime) > ZT_FRAGMENTED_PACKET_RECEIVE_TIMEOUT) {
@ -552,11 +605,11 @@ void Switch::_handleRemotePacketFragment(const InetAddress &fromAddr,int linkDes
// Note: we don't bother initiating NAT-t for fragments, since heads will set that off. // Note: we don't bother initiating NAT-t for fragments, since heads will set that off.
// It wouldn't hurt anything, just redundant and unnecessary. // It wouldn't hurt anything, just redundant and unnecessary.
SharedPtr<Peer> relayTo = RR->topology->getPeer(destination); SharedPtr<Peer> relayTo = RR->topology->getPeer(destination);
if ((!relayTo)||(relayTo->send(RR,fragment.data(),fragment.size(),Utils::now()) == Path::PATH_TYPE_NULL)) { if ((!relayTo)||(!relayTo->send(RR,fragment.data(),fragment.size(),RR->node->now()))) {
// Don't know peer or no direct path -- so relay via supernode // Don't know peer or no direct path -- so relay via supernode
relayTo = RR->topology->getBestSupernode(); relayTo = RR->topology->getBestSupernode();
if (relayTo) if (relayTo)
relayTo->send(RR,fragment.data(),fragment.size(),Utils::now()); relayTo->send(RR,fragment.data(),fragment.size(),RR->node->now());
} }
} else { } else {
TRACE("dropped relay [fragment](%s) -> %s, max hops exceeded",fromAddr.toString().c_str(),destination.toString().c_str()); TRACE("dropped relay [fragment](%s) -> %s, max hops exceeded",fromAddr.toString().c_str(),destination.toString().c_str());
@ -613,7 +666,7 @@ void Switch::_handleRemotePacketFragment(const InetAddress &fromAddr,int linkDes
void Switch::_handleRemotePacketHead(const InetAddress &fromAddr,int linkDesperation,const Buffer<4096> &data) void Switch::_handleRemotePacketHead(const InetAddress &fromAddr,int linkDesperation,const Buffer<4096> &data)
{ {
SharedPtr<IncomingPacket> packet(new IncomingPacket(data,fromSock,fromAddr)); SharedPtr<IncomingPacket> packet(new IncomingPacket(data,fromAddr,linkDesperation));
Address source(packet->source()); Address source(packet->source());
Address destination(packet->destination()); Address destination(packet->destination());
@ -626,18 +679,13 @@ void Switch::_handleRemotePacketHead(const InetAddress &fromAddr,int linkDespera
packet->incrementHops(); packet->incrementHops();
SharedPtr<Peer> relayTo = RR->topology->getPeer(destination); SharedPtr<Peer> relayTo = RR->topology->getPeer(destination);
Path::Type relayedVia; if ((relayTo)&&((relayTo->send(RR,packet->data(),packet->size(),RR->node->now())))) {
if ((relayTo)&&((relayedVia = relayTo->send(RR,packet->data(),packet->size(),Utils::now())) != Path::PATH_TYPE_NULL)) { unite(source,destination,false);
/* If both paths are UDP, attempt to invoke UDP NAT-t between peers
* by sending VERB_RENDEZVOUS. Do not do this for TCP due to GitHub
* issue #63. */
if ((fromSock->udp())&&(relayedVia == Path::PATH_TYPE_UDP))
unite(source,destination,false);
} else { } else {
// Don't know peer or no direct path -- so relay via supernode // Don't know peer or no direct path -- so relay via supernode
relayTo = RR->topology->getBestSupernode(&source,1,true); relayTo = RR->topology->getBestSupernode(&source,1,true);
if (relayTo) if (relayTo)
relayTo->send(RR,packet->data(),packet->size(),Utils::now()); relayTo->send(RR,packet->data(),packet->size(),RR->node->now());
} }
} else { } else {
TRACE("dropped relay %s(%s) -> %s, max hops exceeded",packet->source().toString().c_str(),fromAddr.toString().c_str(),destination.toString().c_str()); TRACE("dropped relay %s(%s) -> %s, max hops exceeded",packet->source().toString().c_str(),fromAddr.toString().c_str(),destination.toString().c_str());
@ -693,15 +741,12 @@ void Switch::_handleBeacon(const InetAddress &fromAddr,int linkDesperation,const
return; return;
SharedPtr<Peer> peer(RR->topology->getPeer(beaconAddr)); SharedPtr<Peer> peer(RR->topology->getPeer(beaconAddr));
if (peer) { if (peer) {
uint64_t now = Utils::now(); const uint64_t now = RR->node->now();
if (peer->haveUdpPath(fromAddr)) { if ((now - _lastBeacon) >= ZT_MIN_BEACON_RESPONSE_INTERVAL) {
if ((now - peer->lastDirectReceive()) >= ZT_PEER_DIRECT_PING_DELAY)
peer->sendPing(RR,now);
} else {
if ((now - _lastBeacon) < ZT_MIN_BEACON_RESPONSE_INTERVAL)
return;
_lastBeacon = now; _lastBeacon = now;
sendHELLO(peer,fromAddr); Packet outp(peer->address(),RR->identity.address(),Packet::VERB_NOP);
outp.armor(peer->key(),false);
RR->node->putPacket(fromAddr,outp.data(),outp.size(),linkDesperation);
} }
} }
} }
@ -713,8 +758,7 @@ Address Switch::_sendWhoisRequest(const Address &addr,const Address *peersAlread
Packet outp(supernode->address(),RR->identity.address(),Packet::VERB_WHOIS); Packet outp(supernode->address(),RR->identity.address(),Packet::VERB_WHOIS);
addr.appendTo(outp); addr.appendTo(outp);
outp.armor(supernode->key(),true); outp.armor(supernode->key(),true);
uint64_t now = Utils::now(); if (supernode->send(RR,outp.data(),outp.size(),RR->node->now()))
if (supernode->send(RR,outp.data(),outp.size(),now) != Path::PATH_TYPE_NULL)
return supernode->address(); return supernode->address();
} }
return Address(); return Address();
@ -725,14 +769,15 @@ bool Switch::_trySend(const Packet &packet,bool encrypt)
SharedPtr<Peer> peer(RR->topology->getPeer(packet.destination())); SharedPtr<Peer> peer(RR->topology->getPeer(packet.destination()));
if (peer) { if (peer) {
uint64_t now = Utils::now(); const uint64_t now = RR->node->now();
SharedPtr<Peer> via; SharedPtr<Peer> via;
if (peer->hasActiveDirectPath(now)) { Path *viaPath;
if ((viaPath = peer->getBestPath(now))) {
via = peer; via = peer;
} else { } else {
via = RR->topology->getBestSupernode(); via = RR->topology->getBestSupernode();
if (!via) if (!(via)||(!(viaPath = via->getBestPath(now))))
return false; return false;
} }
@ -743,7 +788,7 @@ bool Switch::_trySend(const Packet &packet,bool encrypt)
tmp.armor(peer->key(),encrypt); tmp.armor(peer->key(),encrypt);
if (via->send(RR,tmp.data(),chunkSize,now) != Path::PATH_TYPE_NULL) { if (viaPath->send(RR,tmp.data(),chunkSize,now)) {
if (chunkSize < tmp.size()) { if (chunkSize < tmp.size()) {
// Too big for one bite, fragment the rest // Too big for one bite, fragment the rest
unsigned int fragStart = chunkSize; unsigned int fragStart = chunkSize;
@ -756,7 +801,7 @@ bool Switch::_trySend(const Packet &packet,bool encrypt)
for(unsigned int fno=1;fno<totalFragments;++fno) { for(unsigned int fno=1;fno<totalFragments;++fno) {
chunkSize = std::min(remaining,(unsigned int)(ZT_UDP_DEFAULT_PAYLOAD_MTU - ZT_PROTO_MIN_FRAGMENT_LENGTH)); chunkSize = std::min(remaining,(unsigned int)(ZT_UDP_DEFAULT_PAYLOAD_MTU - ZT_PROTO_MIN_FRAGMENT_LENGTH));
Packet::Fragment frag(tmp,fragStart,chunkSize,fno,totalFragments); Packet::Fragment frag(tmp,fragStart,chunkSize,fno,totalFragments);
via->send(RR,frag.data(),frag.size(),now); viaPath->send(RR,frag.data(),frag.size(),now);
fragStart += chunkSize; fragStart += chunkSize;
remaining -= chunkSize; remaining -= chunkSize;
} }

View file

@ -134,8 +134,9 @@ public:
* *
* @param peer Peer to contact * @param peer Peer to contact
* @param atAddr Address of peer * @param atAddr Address of peer
* @param linkDesperation Attempt up to given max desperation
*/ */
void contact(const SharedPtr<Peer> &peer,const InetAddress &atAddr); void contact(const SharedPtr<Peer> &peer,const InetAddress &atAddr,unsigned int maxDesperation);
/** /**
* Request WHOIS on a given address * Request WHOIS on a given address
@ -241,14 +242,20 @@ private:
struct ContactQueueEntry struct ContactQueueEntry
{ {
ContactQueueEntry() {} ContactQueueEntry() {}
ContactQueueEntry(const SharedPtr<Peer> &p,uint64_t ft,const InetAddress &a) : ContactQueueEntry(const SharedPtr<Peer> &p,uint64_t ft,const InetAddress &a,unsigned int md) :
peer(p), peer(p),
fireAtTime(ft), fireAtTime(ft),
inaddr(a) {} inaddr(a),
maxDesperation(md),
currentDesperation(0),
strategyIteration(1) {} // start with 2nd strategy, zero desperation, since we've already tried 0/0
SharedPtr<Peer> peer; SharedPtr<Peer> peer;
uint64_t fireAtTime; uint64_t fireAtTime;
InetAddress inaddr; InetAddress inaddr;
unsigned int maxDesperation;
unsigned int currentDesperation;
unsigned int strategyIteration;
}; };
std::list<ContactQueueEntry> _contactQueue; std::list<ContactQueueEntry> _contactQueue;
Mutex _contactQueue_m; Mutex _contactQueue_m;