Răsfoiți Sursa

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

Adam Ierymenko 10 ani în urmă
părinte
comite
6eb9289367
11 a modificat fișierele cu 162 adăugiri și 142 ștergeri
  1. 4 14
      include/ZeroTierOne.h
  2. 1 1
      node/Constants.hpp
  3. 4 6
      node/IncomingPacket.hpp
  4. 2 2
      node/Node.cpp
  5. 2 2
      node/Node.hpp
  6. 0 1
      node/Packet.cpp
  7. 1 39
      node/Packet.hpp
  8. 4 4
      node/Path.hpp
  9. 20 1
      node/Peer.hpp
  10. 114 69
      node/Switch.cpp
  11. 10 3
      node/Switch.hpp

+ 4 - 14
include/ZeroTierOne.h

@@ -151,12 +151,7 @@ enum ZT1_NodeStatusCode
 	/**
 	 * Node is online -- at least one upstream is reachable
 	 */
-	ZT1_NODE_STATUS_ONLINE = 1,
-
-	/**
-	 * Link desperation level has changed
-	 */
-	ZT1_NODE_STATUS_DESPERATION_CHANGE = 3
+	ZT1_NODE_STATUS_ONLINE = 1
 };
 
 /**
@@ -191,7 +186,7 @@ typedef struct
 	/**
 	 * Current maximum link desperation metric
 	 */
-	int desperation;
+	unsigned int desperation;
 } ZT1_NodeStatus;
 
 /**
@@ -393,11 +388,6 @@ typedef struct
 	 */
 	uint64_t bytesReceived;
 
-	/**
-	 * This path's desperation metric (higher == worse)
-	 */
-	int desperation;
-
 	/**
 	 * 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
  * 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
@@ -602,7 +592,7 @@ enum ZT1_ResultCode ZT1_Node_processWirePacket(
 	ZT1_Node *node,
 	uint64_t now,
 	const struct sockaddr_storage *remoteAddress,
-	int linkDesperation,
+	unsigned int linkDesperation,
 	const void *packetData,
 	unsigned int packetLength,
 	uint64_t *nextCallDeadline);

+ 1 - 1
node/Constants.hpp

@@ -334,7 +334,7 @@
  * 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.
  */
-#define ZT_MIN_UNITE_INTERVAL 30000
+#define ZT_MIN_UNITE_INTERVAL 60000
 
 /**
  * Delay between initial direct NAT-t packet and more aggressive techniques

+ 4 - 6
node/IncomingPacket.hpp

@@ -31,14 +31,12 @@
 #include <stdexcept>
 
 #include "Packet.hpp"
-#include "SocketManager.hpp"
 #include "InetAddress.hpp"
 #include "Utils.hpp"
 #include "SharedPtr.hpp"
 #include "AtomicCounter.hpp"
 #include "MulticastGroup.hpp"
 #include "Peer.hpp"
-#include "Socket.hpp"
 
 /*
  * The big picture:
@@ -73,17 +71,17 @@ public:
 	 * Create a new packet-in-decode
 	 *
 	 * @param b Source buffer with raw packet data
-	 * @param fromSock Socket on which packet was received
 	 * @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
 	 */
 	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) :
  		Packet(b),
  		_receiveTime(Utils::now()),
- 		_fromSock(fromSock),
  		_remoteAddress(remoteAddress),
+ 		_linkDesperation(linkDesperation),
  		__refCount()
 	{
 	}
@@ -130,8 +128,8 @@ private:
 	void _sendErrorNeedCertificate(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer,uint64_t nwid);
 
 	uint64_t _receiveTime;
-	SharedPtr<Socket> _fromSock;
 	InetAddress _remoteAddress;
+	unsigned int _linkDesperation;
 	AtomicCounter __refCount;
 };
 

+ 2 - 2
node/Node.cpp

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

+ 2 - 2
node/Node.hpp

@@ -71,7 +71,7 @@ public:
 	ZT1_ResultCode processWirePacket(
 		uint64_t now,
 		const struct sockaddr_storage *remoteAddress,
-		int linkDesperation,
+		unsigned int linkDesperation,
 		const void *packetData,
 		unsigned int packetLength,
 		uint64_t *nextCallDeadline);
@@ -113,7 +113,7 @@ public:
 	 * @param desperation Link desperation for reaching this address
 	 * @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(
 			reinterpret_cast<ZT1_Node *>(this),

+ 0 - 1
node/Packet.cpp

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

+ 1 - 39
node/Packet.hpp

@@ -717,45 +717,7 @@ public:
 		 *   <[6] multicast group MAC>
 		 *   <[4] 32-bit multicast group ADI>
 		 */
-		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
+		VERB_MULTICAST_FRAME = 14
 	};
 
 	/**

+ 4 - 4
node/Path.hpp

@@ -106,7 +106,7 @@ public:
 	 * @param t Time 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?
@@ -131,10 +131,10 @@ public:
 	 * @param now Current time
 	 * @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))
-			return std::max(_lastReceiveDesperation,(int)((_lastSend - _lastReceived) / ZT_DESPERATION_INCREMENT));
+			return std::max(_lastReceiveDesperation,(unsigned int)((_lastSend - _lastReceived) / ZT_DESPERATION_INCREMENT));
 		return _lastReceiveDesperation;
 	}
 
@@ -194,7 +194,7 @@ private:
 	uint64_t _lastSend;
 	uint64_t _lastReceived;
 	InetAddress _addr;
-	int _lastReceiveDesperation;
+	unsigned int _lastReceiveDesperation;
 	bool _fixed;
 };
 

+ 20 - 1
node/Peer.hpp

@@ -137,7 +137,7 @@ public:
 	 * @param now Current time
 	 * @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;
 		uint64_t lrMax = 0;
@@ -150,6 +150,25 @@ public:
 		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
 	 */

+ 114 - 69
node/Switch.cpp

@@ -36,16 +36,15 @@
 #include "../include/ZeroTierOne.h"
 
 #include "Constants.hpp"
+#include "RuntimeEnvironment.hpp"
 #include "Switch.hpp"
 #include "Node.hpp"
-#include "EthernetTap.hpp"
 #include "InetAddress.hpp"
 #include "Topology.hpp"
-#include "RuntimeEnvironment.hpp"
 #include "Peer.hpp"
-#include "NodeConfig.hpp"
 #include "CMWC4096.hpp"
 #include "AntiRecursion.hpp"
+#include "Packet.hpp"
 
 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
 	 * to do with their intended target audience. :P */
 	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;
 	}
 
 	// Check to make sure this protocol is allowed on this network
 	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;
 	}
 
@@ -107,7 +106,7 @@ void Switch::onLocalEthernet(const SharedPtr<Network> &network,const MAC &from,c
 	bool fromBridged = false;
 	if (from != network->mac()) {
 		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;
 		}
 		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));
 			} else if (!nconf->enableBroadcast()) {
 				// 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;
 			}
 		}
@@ -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
 		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;
 		}
 
-		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(
 			((!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);
 			}
 		} 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;
@@ -368,26 +367,32 @@ bool Switch::unite(const Address &p1,const Address &p2,bool force)
 	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());
 
+	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
 	{
 		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)
 {
-	//TRACE("requesting WHOIS for %s",addr.toString().c_str());
 	bool inserted = false;
 	{
 		Mutex::Lock _l(_outstandingWhoisRequests_m);
@@ -436,38 +441,84 @@ void Switch::doAnythingWaitingForPeer(const SharedPtr<Peer> &peer)
 unsigned long Switch::doTimerTasks()
 {
 	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);
 		for(std::list<ContactQueueEntry>::iterator qi(_contactQueue.begin());qi!=_contactQueue.end();) {
 			if (now >= qi->fireAtTime) {
-				if (!qi->peer->hasActiveDirectPath(now)) {
-					TRACE("deploying aggressive NAT-t against %s(%s)",qi->peer->address().toString().c_str(),qi->inaddr.toString().c_str());
-
-					/* Shotgun approach -- literally -- against symmetric NATs. Most of these
-					 * either increment or decrement ports so this gets a good number. Also try
-					 * the original port one more time for good measure, since sometimes it
-					 * fails first time around. */
-					int p = (int)qi->inaddr.port() - 2;
-					for(int k=0;k<6;++k) {
-						if ((p > 0)&&(p <= 0xffff)) {
-							qi->inaddr.setPort((unsigned int)p);
-							sendHELLO(qi->peer,qi->inaddr);
-						}
-						++p;
+				if (qi->peer->hasActiveDirectPath(now)) {
+					// We've successfully NAT-t'd, so cancel attempt
+					_contactQueue.erase(qi++);
+					continue;
+				} else {
+					// Nope, nothing yet. Time to kill some kittens.
+
+					Packet outp(qi->peer->address(),RR->identity.address(),Packet::VERB_NOP);
+					outp.armor(qi->peer->key(),false);
+
+					switch(qi->strategyIteration) {
+						case 0:
+							// First strategy: rifle method: direct packet to known port
+							++qi->strategyIteration;
+							RR->node->putPacket(qi->inaddr,outp.data(),outp.size(),qi->currentDesperation);
+							break;
+						case 1: {
+							// 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 {
 				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);
 		for(std::map< Address,WhoisRequest >::iterator i(_outstandingWhoisRequests.begin());i!=_outstandingWhoisRequests.end();) {
 			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);
 					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;
 		}
 	}
 
-	{
+	{	// Time out TX queue packets that never got WHOIS lookups or other info.
 		Mutex::Lock _l(_txQueue_m);
 		for(std::multimap< Address,TXQueueEntry >::iterator i(_txQueue.begin());i!=_txQueue.end();) {
 			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);
 		for(std::list< SharedPtr<IncomingPacket> >::iterator i(_rxQueue.begin());i!=_rxQueue.end();) {
 			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);
 		for(std::map< uint64_t,DefragQueueEntry >::iterator i(_defragQueue.begin());i!=_defragQueue.end();) {
 			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.
 			// It wouldn't hurt anything, just redundant and unnecessary.
 			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
 				relayTo = RR->topology->getBestSupernode();
 				if (relayTo)
-					relayTo->send(RR,fragment.data(),fragment.size(),Utils::now());
+					relayTo->send(RR,fragment.data(),fragment.size(),RR->node->now());
 			}
 		} else {
 			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)
 {
-	SharedPtr<IncomingPacket> packet(new IncomingPacket(data,fromSock,fromAddr));
+	SharedPtr<IncomingPacket> packet(new IncomingPacket(data,fromAddr,linkDesperation));
 
 	Address source(packet->source());
 	Address destination(packet->destination());
@@ -626,18 +679,13 @@ void Switch::_handleRemotePacketHead(const InetAddress &fromAddr,int linkDespera
 			packet->incrementHops();
 
 			SharedPtr<Peer> relayTo = RR->topology->getPeer(destination);
-			Path::Type relayedVia;
-			if ((relayTo)&&((relayedVia = relayTo->send(RR,packet->data(),packet->size(),Utils::now())) != Path::PATH_TYPE_NULL)) {
-				/* 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);
+			if ((relayTo)&&((relayTo->send(RR,packet->data(),packet->size(),RR->node->now())))) {
+				unite(source,destination,false);
 			} else {
 				// Don't know peer or no direct path -- so relay via supernode
 				relayTo = RR->topology->getBestSupernode(&source,1,true);
 				if (relayTo)
-					relayTo->send(RR,packet->data(),packet->size(),Utils::now());
+					relayTo->send(RR,packet->data(),packet->size(),RR->node->now());
 			}
 		} else {
 			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;
 	SharedPtr<Peer> peer(RR->topology->getPeer(beaconAddr));
 	if (peer) {
-		uint64_t now = Utils::now();
-		if (peer->haveUdpPath(fromAddr)) {
-			if ((now - peer->lastDirectReceive()) >= ZT_PEER_DIRECT_PING_DELAY)
-				peer->sendPing(RR,now);
-		} else {
-			if ((now - _lastBeacon) < ZT_MIN_BEACON_RESPONSE_INTERVAL)
-				return;
+		const uint64_t now = RR->node->now();
+		if ((now - _lastBeacon) >= ZT_MIN_BEACON_RESPONSE_INTERVAL) {
 			_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);
 		addr.appendTo(outp);
 		outp.armor(supernode->key(),true);
-		uint64_t now = Utils::now();
-		if (supernode->send(RR,outp.data(),outp.size(),now) != Path::PATH_TYPE_NULL)
+		if (supernode->send(RR,outp.data(),outp.size(),RR->node->now()))
 			return supernode->address();
 	}
 	return Address();
@@ -725,14 +769,15 @@ bool Switch::_trySend(const Packet &packet,bool encrypt)
 	SharedPtr<Peer> peer(RR->topology->getPeer(packet.destination()));
 
 	if (peer) {
-		uint64_t now = Utils::now();
+		const uint64_t now = RR->node->now();
 
 		SharedPtr<Peer> via;
-		if (peer->hasActiveDirectPath(now)) {
+		Path *viaPath;
+		if ((viaPath = peer->getBestPath(now))) {
 			via = peer;
 		} else {
 			via = RR->topology->getBestSupernode();
-			if (!via)
+			if (!(via)||(!(viaPath = via->getBestPath(now))))
 				return false;
 		}
 
@@ -743,7 +788,7 @@ bool Switch::_trySend(const Packet &packet,bool 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()) {
 				// Too big for one bite, fragment the rest
 				unsigned int fragStart = chunkSize;
@@ -756,7 +801,7 @@ bool Switch::_trySend(const Packet &packet,bool encrypt)
 				for(unsigned int fno=1;fno<totalFragments;++fno) {
 					chunkSize = std::min(remaining,(unsigned int)(ZT_UDP_DEFAULT_PAYLOAD_MTU - ZT_PROTO_MIN_FRAGMENT_LENGTH));
 					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;
 					remaining -= chunkSize;
 				}

+ 10 - 3
node/Switch.hpp

@@ -134,8 +134,9 @@ public:
 	 *
 	 * @param peer Peer to contact
 	 * @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
@@ -241,14 +242,20 @@ private:
 	struct 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),
 			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;
 		uint64_t fireAtTime;
 		InetAddress inaddr;
+		unsigned int maxDesperation;
+		unsigned int currentDesperation;
+		unsigned int strategyIteration;
 	};
 	std::list<ContactQueueEntry> _contactQueue;
 	Mutex _contactQueue_m;