diff options
| -rw-r--r-- | node/Constants.hpp | 5 | ||||
| -rw-r--r-- | node/Demarc.hpp | 3 | ||||
| -rw-r--r-- | node/PacketDecoder.cpp | 88 | ||||
| -rw-r--r-- | node/Peer.cpp | 53 | ||||
| -rw-r--r-- | node/Peer.hpp | 192 | ||||
| -rw-r--r-- | node/Switch.cpp | 18 | 
6 files changed, 211 insertions, 148 deletions
| diff --git a/node/Constants.hpp b/node/Constants.hpp index 21c8a0ec..85af3b28 100644 --- a/node/Constants.hpp +++ b/node/Constants.hpp @@ -303,6 +303,11 @@ error_no_byte_order_defined;  #define ZT_PEER_LINK_ACTIVITY_TIMEOUT ((ZT_PEER_DIRECT_PING_DELAY * 2) + 1000)  /** + * Number of outgoing verb/packetId pairs to keep for sends expecting responses + */ +#define ZT_PEER_REQUEST_HISTORY_LENGTH 8 + +/**   * IP hops (a.k.a. TTL) to set for firewall opener packets   *   * 2 should permit traversal of double-NAT configurations, such as from inside diff --git a/node/Demarc.hpp b/node/Demarc.hpp index 0bbdef44..78a05a34 100644 --- a/node/Demarc.hpp +++ b/node/Demarc.hpp @@ -60,6 +60,9 @@ class Demarc  public:  	/**  	 * Local demarcation port +	 * +	 * NULL_PORT is zero so this can be used in if(port) to check for +	 * a valid/known port.  	 */  	typedef uint64_t Port; diff --git a/node/PacketDecoder.cpp b/node/PacketDecoder.cpp index e57b175c..1c6d09ca 100644 --- a/node/PacketDecoder.cpp +++ b/node/PacketDecoder.cpp @@ -77,16 +77,9 @@ bool PacketDecoder::tryDecode(const RuntimeEnvironment *_r)  			return true;  		} -		Packet::Verb v = verb(); - -		// Once a packet is determined to be basically valid, it can be used -		// to passively learn a new network path to the sending peer. It -		// also results in statistics updates. -		peer->onReceive(_r,_localPort,_remoteAddress,hops(),v,Utils::now()); - -		switch(v) { +		switch(verb()) {  			case Packet::VERB_NOP: -				TRACE("NOP from %s(%s)",source().toString().c_str(),_remoteAddress.toString().c_str()); +				peer->onReceive(_r,_localPort,_remoteAddress,hops(),packetId(),Packet::VERB_NOP,0,Packet::VERB_NOP,Utils::now());  				return true;  			case Packet::VERB_HELLO:  				return _doHELLO(_r); // legal, but why? :) @@ -130,7 +123,9 @@ bool PacketDecoder::_doERROR(const RuntimeEnvironment *_r,const SharedPtr<Peer>  {  	try {  		Packet::Verb inReVerb = (Packet::Verb)(*this)[ZT_PROTO_VERB_ERROR_IDX_IN_RE_VERB]; +		uint64_t inRePacketId = at<uint64_t>(ZT_PROTO_VERB_ERROR_IDX_IN_RE_PACKET_ID);  		Packet::ErrorCode errorCode = (Packet::ErrorCode)(*this)[ZT_PROTO_VERB_ERROR_IDX_ERROR_CODE]; +  		TRACE("ERROR %s from %s(%s) in-re %s",Packet::errorString(errorCode),source().toString().c_str(),_remoteAddress.toString().c_str(),Packet::verbString(inReVerb));  		switch(errorCode) { @@ -161,6 +156,8 @@ bool PacketDecoder::_doERROR(const RuntimeEnvironment *_r,const SharedPtr<Peer>  			default:  				break;  		} + +		peer->onReceive(_r,_localPort,_remoteAddress,hops(),packetId(),Packet::VERB_ERROR,inRePacketId,inReVerb,Utils::now());  	} catch (std::exception &ex) {  		TRACE("dropped ERROR from %s(%s): unexpected exception: %s",source().toString().c_str(),_remoteAddress.toString().c_str(),ex.what());  	} catch ( ... ) { @@ -183,7 +180,6 @@ bool PacketDecoder::_doHELLO(const RuntimeEnvironment *_r)  			TRACE("dropped HELLO from %s(%s): protocol version mismatch (%u, expected %u)",source().toString().c_str(),_remoteAddress.toString().c_str(),protoVersion,(unsigned int)ZT_PROTO_VERSION);  			return true;  		} -  		if (!id.locallyValidate()) {  			TRACE("dropped HELLO from %s(%s): identity invalid",source().toString().c_str(),_remoteAddress.toString().c_str());  			return true; @@ -225,14 +221,14 @@ bool PacketDecoder::_doHELLO(const RuntimeEnvironment *_r)  					_r->demarc->send(_localPort,_remoteAddress,outp.data(),outp.size(),-1);  				}  				return true; -			} +			} // else continue since identity is already known and matches  			// Learn a new peer if it's new. This also adds it to the identity  			// cache if that's enabled.  			peer = _r->topology->addPeer(SharedPtr<Peer>(new Peer(_r->identity,id)));  		} -		peer->onReceive(_r,_localPort,_remoteAddress,hops(),Packet::VERB_HELLO,Utils::now()); +		peer->onReceive(_r,_localPort,_remoteAddress,hops(),packetId(),Packet::VERB_HELLO,0,Packet::VERB_NOP,Utils::now());  		peer->setRemoteVersion(vMajor,vMinor,vRevision);  		Packet outp(source(),_r->identity.address(),Packet::VERB_OK); @@ -257,16 +253,17 @@ bool PacketDecoder::_doOK(const RuntimeEnvironment *_r,const SharedPtr<Peer> &pe  {  	try {  		Packet::Verb inReVerb = (Packet::Verb)(*this)[ZT_PROTO_VERB_OK_IDX_IN_RE_VERB]; +		uint64_t inRePacketId = at<uint64_t>(ZT_PROTO_VERB_OK_IDX_IN_RE_PACKET_ID); +  		//TRACE("%s(%s): OK(%s)",source().toString().c_str(),_remoteAddress.toString().c_str(),Packet::verbString(inReVerb)); +  		switch(inReVerb) {  			case Packet::VERB_HELLO: { -				// OK from HELLO permits computation of latency. -				unsigned int latency = std::min((unsigned int)(Utils::now() - at<uint64_t>(ZT_PROTO_VERB_HELLO__OK__IDX_TIMESTAMP)),(unsigned int)0xffff); +				//unsigned int latency = std::min((unsigned int)(Utils::now() - at<uint64_t>(ZT_PROTO_VERB_HELLO__OK__IDX_TIMESTAMP)),(unsigned int)0xffff);  				unsigned int vMajor = (*this)[ZT_PROTO_VERB_HELLO__OK__IDX_MAJOR_VERSION];  				unsigned int vMinor = (*this)[ZT_PROTO_VERB_HELLO__OK__IDX_MINOR_VERSION];  				unsigned int vRevision = at<uint16_t>(ZT_PROTO_VERB_HELLO__OK__IDX_REVISION); -				TRACE("%s(%s): OK(HELLO), latency: %u, version %u.%u.%u",source().toString().c_str(),_remoteAddress.toString().c_str(),latency,vMajor,vMinor,vRevision); -				peer->setLatency(_remoteAddress,latency); +				TRACE("%s(%s): OK(HELLO), version %u.%u.%u",source().toString().c_str(),_remoteAddress.toString().c_str(),vMajor,vMinor,vRevision);  				peer->setRemoteVersion(vMajor,vMinor,vRevision);  			}	break;  			case Packet::VERB_WHOIS: { @@ -292,8 +289,11 @@ bool PacketDecoder::_doOK(const RuntimeEnvironment *_r,const SharedPtr<Peer> &pe  					}  				}  			}	break; -			default: break; +			default: +				break;  		} + +		peer->onReceive(_r,_localPort,_remoteAddress,hops(),packetId(),Packet::VERB_OK,inRePacketId,inReVerb,Utils::now());  	} catch (std::exception &ex) {  		TRACE("dropped OK from %s(%s): unexpected exception: %s",source().toString().c_str(),_remoteAddress.toString().c_str(),ex.what());  	} catch ( ... ) { @@ -327,6 +327,7 @@ bool PacketDecoder::_doWHOIS(const RuntimeEnvironment *_r,const SharedPtr<Peer>  	} else {  		TRACE("dropped WHOIS from %s(%s): missing or invalid address",source().toString().c_str(),_remoteAddress.toString().c_str());  	} +	peer->onReceive(_r,_localPort,_remoteAddress,hops(),packetId(),Packet::VERB_WHOIS,0,Packet::VERB_NOP,Utils::now());  	return true;  } @@ -355,6 +356,7 @@ bool PacketDecoder::_doRENDEZVOUS(const RuntimeEnvironment *_r,const SharedPtr<P  				if ((port > 0)&&((addrlen == 4)||(addrlen == 16))) {  					InetAddress atAddr(field(ZT_PROTO_VERB_RENDEZVOUS_IDX_ADDRESS,addrlen),addrlen,port);  					TRACE("RENDEZVOUS from %s says %s might be at %s, starting NAT-t",source().toString().c_str(),with.toString().c_str(),atAddr.toString().c_str()); +					peer->onReceive(_r,_localPort,_remoteAddress,hops(),packetId(),Packet::VERB_RENDEZVOUS,0,Packet::VERB_NOP,Utils::now());  					_r->sw->contact(withPeer,atAddr);  				} else {  					TRACE("dropped corrupt RENDEZVOUS from %s(%s) (bad address or port)",source().toString().c_str(),_remoteAddress.toString().c_str()); @@ -380,18 +382,22 @@ bool PacketDecoder::_doFRAME(const RuntimeEnvironment *_r,const SharedPtr<Peer>  		if (network) {  			if (network->isAllowed(source())) {  				unsigned int etherType = at<uint16_t>(ZT_PROTO_VERB_FRAME_IDX_ETHERTYPE); -				if (network->config()->permitsEtherType(etherType)) { -					network->tap().put(source().toMAC(),network->tap().mac(),etherType,data() + ZT_PROTO_VERB_FRAME_IDX_PAYLOAD,size() - ZT_PROTO_VERB_FRAME_IDX_PAYLOAD); -				} else if (size() > ZT_PROTO_VERB_FRAME_IDX_PAYLOAD) { -					TRACE("dropped FRAME from %s: ethernet type %u not allowed on network %.16llx",source().toString().c_str(),etherType,(unsigned long long)network->id()); -				} +				if (size() > ZT_PROTO_VERB_FRAME_IDX_PAYLOAD) { +					if (network->config()->permitsEtherType(etherType)) { +						network->tap().put(source().toMAC(),network->tap().mac(),etherType,data() + ZT_PROTO_VERB_FRAME_IDX_PAYLOAD,size() - ZT_PROTO_VERB_FRAME_IDX_PAYLOAD); +					} else { +						TRACE("dropped FRAME from %s: ethernet type %u not allowed on network %.16llx",source().toString().c_str(),etherType,(unsigned long long)network->id()); +						return true; +					} +				} else return true; // ignore empty frames  				// Source moves "closer" to us in multicast propagation priority when  				// we receive unicast frames from it. This is called "implicit social  				// ordering" in other docs.  				_r->mc->bringCloser(network->id(),source()); +				peer->onReceive(_r,_localPort,_remoteAddress,hops(),packetId(),Packet::VERB_FRAME,0,Packet::VERB_NOP,Utils::now());  			} else { -				TRACE("dropped FRAME from %s(%s): not a member of closed network %llu",source().toString().c_str(),_remoteAddress.toString().c_str(),network->id()); +				TRACE("dropped FRAME from %s(%s): sender not a member of closed network %.16llx",source().toString().c_str(),_remoteAddress.toString().c_str(),network->id());  				Packet outp(source(),_r->identity.address(),Packet::VERB_ERROR);  				outp.append((unsigned char)Packet::VERB_FRAME); @@ -400,9 +406,12 @@ bool PacketDecoder::_doFRAME(const RuntimeEnvironment *_r,const SharedPtr<Peer>  				outp.append(network->id());  				outp.armor(peer->key(),true);  				_r->demarc->send(_localPort,_remoteAddress,outp.data(),outp.size(),-1); + +				return true;  			}  		} else { -			TRACE("dropped FRAME from %s(%s): network %llu unknown",source().toString().c_str(),_remoteAddress.toString().c_str(),at<uint64_t>(ZT_PROTO_VERB_FRAME_IDX_NETWORK_ID)); +			TRACE("dropped FRAME from %s(%s): we are not connected to network %.16llx",source().toString().c_str(),_remoteAddress.toString().c_str(),at<uint64_t>(ZT_PROTO_VERB_FRAME_IDX_NETWORK_ID)); +			return true;  		}  	} catch (std::exception &ex) {  		TRACE("dropped FRAME from %s(%s): unexpected exception: %s",source().toString().c_str(),_remoteAddress.toString().c_str(),ex.what()); @@ -532,6 +541,9 @@ bool PacketDecoder::_doMULTICAST_FRAME(const RuntimeEnvironment *_r,const Shared  		_r->demarc->send(Demarc::ANY_PORT,ZT_DEFAULTS.multicastTraceWatcher,mct,strlen(mct),-1);  #endif +		// At this point the frame is basically valid, so we can call it a receive +		peer->onReceive(_r,_localPort,_remoteAddress,hops(),packetId(),Packet::VERB_MULTICAST_FRAME,0,Packet::VERB_NOP,Utils::now()); +  		// This gets updated later in most cases but start with the global limit.  		unsigned int maxDepth = ZT_MULTICAST_GLOBAL_MAX_DEPTH; @@ -780,6 +792,8 @@ bool PacketDecoder::_doMULTICAST_LIKE(const RuntimeEnvironment *_r,const SharedP  					network->pushMembershipCertificate(peer->address(),false,now);  			}  		} + +		peer->onReceive(_r,_localPort,_remoteAddress,hops(),packetId(),Packet::VERB_MULTICAST_LIKE,0,Packet::VERB_NOP,now);  	} catch (std::exception &ex) {  		TRACE("dropped MULTICAST_LIKE from %s(%s): unexpected exception: %s",source().toString().c_str(),_remoteAddress.toString().c_str(),ex.what());  	} catch ( ... ) { @@ -792,44 +806,30 @@ bool PacketDecoder::_doNETWORK_MEMBERSHIP_CERTIFICATE(const RuntimeEnvironment *  {  	try {  		CertificateOfMembership com; +  		unsigned int ptr = ZT_PACKET_IDX_PAYLOAD;  		while (ptr < size()) {  			ptr += com.deserialize(*this,ptr); -			if (!com.hasRequiredFields()) { -				TRACE("dropped NETWORK_MEMBERSHIP_CERTIFICATE from %s(%s): invalid cert: at least one required field is missing",source().toString().c_str(),_remoteAddress.toString().c_str()); -				return true; -			} else if (com.signedBy()) { +			if ((com.hasRequiredFields())&&(com.signedBy())) {  				SharedPtr<Peer> signer(_r->topology->getPeer(com.signedBy()));  				if (signer) {  					if (com.verify(signer->identity())) {  						uint64_t nwid = com.networkId();  						SharedPtr<Network> network(_r->nc->network(nwid));  						if (network) { -							if (network->controller() == signer) { +							if (network->controller() == signer)  								network->addMembershipCertificate(com); -								return true; -							} else { -								TRACE("dropped NETWORK_MEMBERSHIP_CERTIFICATE from %s(%s): signer %s is not the controller for network %.16llx",source().toString().c_str(),_remoteAddress.toString().c_str(),signer->address().toString().c_str(),(unsigned long long)nwid); -								return true; -							} -						} else { -							TRACE("dropped NETWORK_MEMBERSHIP_CERTIFICATE from %s(%s): not a member of network %.16llx",source().toString().c_str(),_remoteAddress.toString().c_str(),(unsigned long long)nwid); -							return true;  						} -					} else { -						TRACE("dropped NETWORK_MEMBERSHIP_CERTIFICATE from %s(%s): failed signature verification for signer %s",source().toString().c_str(),_remoteAddress.toString().c_str(),signer->address().toString().c_str()); -						return true;  					}  				} else {  					_r->sw->requestWhois(com.signedBy());  					_step = DECODE_WAITING_FOR_NETWORK_MEMBERSHIP_CERTIFICATE_SIGNER_LOOKUP;  					return false;  				} -			} else { -				TRACE("dropped NETWORK_MEMBERSHIP_CERTIFICATE from %s(%s): invalid cert: no signature",source().toString().c_str(),_remoteAddress.toString().c_str()); -				return true;  			}  		} + +		peer->onReceive(_r,_localPort,_remoteAddress,hops(),packetId(),Packet::VERB_NETWORK_MEMBERSHIP_CERTIFICATE,0,Packet::VERB_NOP,Utils::now());  	} catch (std::exception &ex) {  		TRACE("dropped NETWORK_MEMBERSHIP_CERTIFICATE from %s(%s): unexpected exception: %s",source().toString().c_str(),_remoteAddress.toString().c_str(),ex.what());  	} catch ( ... ) { @@ -872,6 +872,7 @@ bool PacketDecoder::_doNETWORK_CONFIG_REQUEST(const RuntimeEnvironment *_r,const  #ifndef __WINDOWS__  		}  #endif // !__WINDOWS__ +		peer->onReceive(_r,_localPort,_remoteAddress,hops(),packetId(),Packet::VERB_NETWORK_CONFIG_REQUEST,0,Packet::VERB_NOP,Utils::now());  	} catch (std::exception &exc) {  		TRACE("dropped NETWORK_CONFIG_REQUEST from %s(%s): unexpected exception: %s",source().toString().c_str(),_remoteAddress.toString().c_str(),exc.what());  	} catch ( ... ) { @@ -892,6 +893,7 @@ bool PacketDecoder::_doNETWORK_CONFIG_REFRESH(const RuntimeEnvironment *_r,const  				nw->requestConfiguration();  			}  		} +		peer->onReceive(_r,_localPort,_remoteAddress,hops(),packetId(),Packet::VERB_NETWORK_CONFIG_REFRESH,0,Packet::VERB_NOP,Utils::now());  	} catch (std::exception &exc) {  		TRACE("dropped NETWORK_CONFIG_REFRESH from %s(%s): unexpected exception: %s",source().toString().c_str(),_remoteAddress.toString().c_str(),exc.what());  	} catch ( ... ) { diff --git a/node/Peer.cpp b/node/Peer.cpp index d30a1d31..8c2c9f9b 100644 --- a/node/Peer.cpp +++ b/node/Peer.cpp @@ -28,6 +28,8 @@  #include "Peer.hpp"  #include "Switch.hpp" +#include <algorithm> +  namespace ZeroTier {  Peer::Peer() : @@ -38,9 +40,11 @@ Peer::Peer() :  	_lastUnicastFrame(0),  	_lastMulticastFrame(0),  	_lastAnnouncedTo(0), +	_latency(0),  	_vMajor(0),  	_vMinor(0), -	_vRevision(0) +	_vRevision(0), +	_requestHistoryPtr(0)  {  } @@ -61,19 +65,44 @@ Peer::Peer(const Identity &myIdentity,const Identity &peerIdentity)  		throw std::runtime_error("new peer identity key agreement failed");  } -void Peer::onReceive(const RuntimeEnvironment *_r,Demarc::Port localPort,const InetAddress &remoteAddr,unsigned int hops,Packet::Verb verb,uint64_t now) +void Peer::onReceive( +	const RuntimeEnvironment *_r, +	Demarc::Port localPort, +	const InetAddress &remoteAddr, +	unsigned int hops, +	uint64_t packetId, +	Packet::Verb verb, +	uint64_t inRePacketId, +	Packet::Verb inReVerb, +	uint64_t now)  {  	if (!hops) { // direct packet -		WanPath *wp = (remoteAddr.isV4() ? &_ipv4p : &_ipv6p); -		wp->lastReceive = now; -		wp->localPort = localPort; -		if (!wp->fixed) -			wp->addr = remoteAddr; - +		// Announce multicast LIKEs to peers to whom we have a direct link  		if ((now - _lastAnnouncedTo) >= ((ZT_MULTICAST_LIKE_EXPIRE / 2) - 1000)) {  			_lastAnnouncedTo = now;  			_r->sw->announceMulticastGroups(SharedPtr<Peer>(this));  		} + +		// Do things like learn latency or endpoints on OK or ERROR replies +		if (inReVerb != Packet::VERB_NOP) { +			for(unsigned int p=0;p<ZT_PEER_REQUEST_HISTORY_LENGTH;++p) { +				if ((_requestHistory[p].packetId == inRePacketId)&&(_requestHistory[p].verb == inReVerb)) { +					_latency = std::min((unsigned int)(now - _requestHistory[p].timestamp),(unsigned int)0xffff); + +					// Only learn paths on replies to packets we have sent, otherwise paths +					// this introduces both an asymmetry problem in NAT-t and a potential +					// reply DOS attack. +					WanPath *const wp = (remoteAddr.isV4() ? &_ipv4p : &_ipv6p); +					wp->lastReceive = now; +					wp->localPort = ((localPort) ? localPort : Demarc::ANY_PORT); +					if (!wp->fixed) +						wp->addr = remoteAddr; + +					_requestHistory[p].packetId = 0; +					break; +				} +			} +		}  	}  	if (verb == Packet::VERB_FRAME) { @@ -83,23 +112,23 @@ void Peer::onReceive(const RuntimeEnvironment *_r,Demarc::Port localPort,const I  	}  } -bool Peer::send(const RuntimeEnvironment *_r,const void *data,unsigned int len,uint64_t now) +Demarc::Port Peer::send(const RuntimeEnvironment *_r,const void *data,unsigned int len,uint64_t now)  {  	if ((_ipv6p.isActive(now))||((!(_ipv4p.addr))&&(_ipv6p.addr))) {  		if (_r->demarc->send(_ipv6p.localPort,_ipv6p.addr,data,len,-1)) {  			_ipv6p.lastSend = now; -			return true; +			return _ipv6p.localPort;  		}  	}  	if (_ipv4p.addr) {  		if (_r->demarc->send(_ipv4p.localPort,_ipv4p.addr,data,len,-1)) {  			_ipv4p.lastSend = now; -			return true; +			return _ipv4p.localPort;  		}  	} -	return false; +	return Demarc::NULL_PORT;  }  bool Peer::sendFirewallOpener(const RuntimeEnvironment *_r,uint64_t now) diff --git a/node/Peer.hpp b/node/Peer.hpp index de5df08f..5766a27b 100644 --- a/node/Peer.hpp +++ b/node/Peer.hpp @@ -48,6 +48,9 @@  #include "NonCopyable.hpp"  #include "Mutex.hpp" +// Increment if serialization has changed +#define ZT_PEER_SERIALIZATION_VERSION 5 +  namespace ZeroTier {  /** @@ -104,28 +107,37 @@ public:  	/**  	 * Must be called on authenticated packet receive from this peer  	 *  -	 * This must be called only after a packet has passed authentication -	 * checking. Packets that fail are silently discarded. -	 *  	 * @param _r Runtime environment  	 * @param localPort Local port on which packet was received  	 * @param remoteAddr Internet address of sender  	 * @param hops ZeroTier (not IP) hops +	 * @param packetId Packet ID  	 * @param verb Packet verb +	 * @param inRePacketId Packet ID in reply to (for OK/ERROR, 0 otherwise) +	 * @param inReVerb Verb in reply to (for OK/ERROR, VERB_NOP otherwise)  	 * @param now Current time  	 */ -	void onReceive(const RuntimeEnvironment *_r,Demarc::Port localPort,const InetAddress &remoteAddr,unsigned int hops,Packet::Verb verb,uint64_t now); +	void onReceive( +		const RuntimeEnvironment *_r, +		Demarc::Port localPort, +		const InetAddress &remoteAddr, +		unsigned int hops, +		uint64_t packetId, +		Packet::Verb verb, +		uint64_t inRePacketId, +		Packet::Verb inReVerb, +		uint64_t now);  	/** -	 * Send a packet to this peer +	 * Send a UDP packet to this peer  	 *   	 * @param _r Runtime environment  	 * @param data Data to send  	 * @param len Length of packet  	 * @param now Current time -	 * @return True if packet appears to have been sent, false on local failure +	 * @return NULL_PORT or port packet was sent from  	 */ -	bool send(const RuntimeEnvironment *_r,const void *data,unsigned int len,uint64_t now); +	Demarc::Port send(const RuntimeEnvironment *_r,const void *data,unsigned int len,uint64_t now);  	/**  	 * Send firewall opener to active link @@ -226,68 +238,29 @@ public:  	/**  	 * @return Lowest of measured latencies of all paths or 0 if unknown  	 */ -	inline unsigned int latency() const -		throw() -	{ -		if (_ipv4p.latency) { -			if (_ipv6p.latency) -				return std::min(_ipv4p.latency,_ipv6p.latency); -			else return _ipv4p.latency; -		} else if (_ipv6p.latency) -			return _ipv6p.latency; -		return 0; -	} - -	/** -	 * @param addr Remote address -	 * @param latency Latency measurment -	 */ -	inline void setLatency(const InetAddress &addr,unsigned int latency) -	{ -		if (addr == _ipv4p.addr) { -			_ipv4p.latency = latency; -		} else if (addr == _ipv6p.addr) { -			_ipv6p.latency = latency; -		} -	} +	inline unsigned int latency() const throw() { return _latency; }  	/**  	 * @return True if this peer has at least one direct IP address path  	 */ -	inline bool hasDirectPath() const -		throw() -	{ -		return ((_ipv4p.addr)||(_ipv6p.addr)); -	} +	inline bool hasDirectPath() const throw() { return ((_ipv4p.addr)||(_ipv6p.addr)); }  	/**  	 * @return True if this peer has at least one direct IP address path that looks active  	 *  	 * @param now Current time  	 */ -	inline bool hasActiveDirectPath(uint64_t now) const -		throw() -	{ -		return ((_ipv4p.isActive(now))||(_ipv6p.isActive(now))); -	} +	inline bool hasActiveDirectPath(uint64_t now) const throw() { return ((_ipv4p.isActive(now))||(_ipv6p.isActive(now))); }  	/**  	 * @return IPv4 direct address or null InetAddress if none  	 */ -	inline InetAddress ipv4Path() const -		throw() -	{ -		return _ipv4p.addr; -	} +	inline InetAddress ipv4Path() const throw() { return _ipv4p.addr; }  	/**  	 * @return IPv6 direct address or null InetAddress if none  	 */ -	inline InetAddress ipv6Path() const -		throw() -	{ -		return _ipv4p.addr; -	} +	inline InetAddress ipv6Path() const throw() { return _ipv4p.addr; }  	/**  	 * @return IPv4 direct address or null InetAddress if none @@ -312,13 +285,9 @@ public:  	}  	/** -	 * @return 256-bit encryption key +	 * @return 256-bit secret symmetric encryption key  	 */ -	inline const unsigned char *key() const -		throw() -	{ -		return _key; -	} +	inline const unsigned char *key() const throw() { return _key; }  	/**  	 * Set the remote version of the peer (not persisted) @@ -347,10 +316,55 @@ public:  		return std::string("?");  	} +	/** +	 * Called when certain packet types are sent that expect OK responses +	 * +	 * @param packetId ID of sent packet +	 * @param verb Verb of sent packet +	 * @param sentFromLocalPort Outgoing local port +	 * @param now Current time +	 */ +	inline void expectResponseTo(uint64_t packetId,Packet::Verb verb,Demarc::Port sentFromLocalPort,uint64_t now) +		throw() +	{ +		unsigned int p = _requestHistoryPtr++ % ZT_PEER_REQUEST_HISTORY_LENGTH; +		_requestHistory[p].timestamp = now; +		_requestHistory[p].packetId = packetId; +		_requestHistory[p].localPort = sentFromLocalPort; +		_requestHistory[p].verb = verb; +	} + +	/** +	 * @return True if this Peer is initialized with something +	 */ +	inline operator bool() const throw() { return (_id); } + +	/** +	 * 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) +		throw() +	{ +		if ((a._ipv6p.isActive(now))&&(b._ipv6p.isActive(now))) +			return std::pair<InetAddress,InetAddress>(b._ipv6p.addr,a._ipv6p.addr); +		else if ((a._ipv4p.isActive(now))&&(b._ipv4p.isActive(now))) +			return std::pair<InetAddress,InetAddress>(b._ipv4p.addr,a._ipv4p.addr); +		else if ((a._ipv6p.addr)&&(b._ipv6p.addr)) +			return std::pair<InetAddress,InetAddress>(b._ipv6p.addr,a._ipv6p.addr); +		else if ((a._ipv4p.addr)&&(b._ipv4p.addr)) +			return std::pair<InetAddress,InetAddress>(b._ipv4p.addr,a._ipv4p.addr); +		return std::pair<InetAddress,InetAddress>(); +	} +  	template<unsigned int C>  	inline void serialize(Buffer<C> &b)  	{ -		b.append((unsigned char)4); // version +		b.append((unsigned char)ZT_PEER_SERIALIZATION_VERSION);  		b.append(_key,sizeof(_key));  		_id.serialize(b,false);  		_ipv4p.serialize(b); @@ -362,14 +376,14 @@ public:  		b.append((uint16_t)_vMajor);  		b.append((uint16_t)_vMinor);  		b.append((uint16_t)_vRevision); +		b.append((uint16_t)_latency);  	} -  	template<unsigned int C>  	inline unsigned int deserialize(const Buffer<C> &b,unsigned int startAt = 0)  	{  		unsigned int p = startAt; -		if (b[p++] != 4) +		if (b[p++] != ZT_PEER_SERIALIZATION_VERSION)  			throw std::invalid_argument("Peer: deserialize(): version mismatch");  		memcpy(_key,b.field(p,sizeof(_key)),sizeof(_key)); p += sizeof(_key); @@ -383,38 +397,14 @@ public:  		_vMajor = b.template at<uint16_t>(p); p += sizeof(uint16_t);  		_vMinor = b.template at<uint16_t>(p); p += sizeof(uint16_t);  		_vRevision = b.template at<uint16_t>(p); p += sizeof(uint16_t); +		_latency = b.template at<uint16_t>(p); p += sizeof(uint16_t);  		return (p - startAt);  	} - -	/** -	 * @return True if this Peer is initialized with something -	 */ -	inline operator bool() const throw() { return (_id); } - +private:  	/** -	 * 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 +	 * A direct IP path to a peer  	 */ -	static inline std::pair<InetAddress,InetAddress> findCommonGround(const Peer &a,const Peer &b,uint64_t now) -		throw() -	{ -		if ((a._ipv6p.isActive(now))&&(b._ipv6p.isActive(now))) -			return std::pair<InetAddress,InetAddress>(b._ipv6p.addr,a._ipv6p.addr); -		else if ((a._ipv4p.isActive(now))&&(b._ipv4p.isActive(now))) -			return std::pair<InetAddress,InetAddress>(b._ipv4p.addr,a._ipv4p.addr); -		else if ((a._ipv6p.addr)&&(b._ipv6p.addr)) -			return std::pair<InetAddress,InetAddress>(b._ipv6p.addr,a._ipv6p.addr); -		else if ((a._ipv4p.addr)&&(b._ipv4p.addr)) -			return std::pair<InetAddress,InetAddress>(b._ipv4p.addr,a._ipv4p.addr); -		return std::pair<InetAddress,InetAddress>(); -	} - -private:  	class WanPath  	{  	public: @@ -423,7 +413,6 @@ private:  			lastReceive(0),  			lastFirewallOpener(0),  			localPort(Demarc::ANY_PORT), -			latency(0),  			addr(),  			fixed(false)  		{ @@ -443,7 +432,6 @@ private:  			b.append(lastReceive);  			b.append(lastFirewallOpener);  			b.append(Demarc::portToInt(localPort)); -			b.append((uint16_t)latency);  			b.append((unsigned char)addr.type());  			switch(addr.type()) { @@ -472,7 +460,6 @@ private:  			lastReceive = b.template at<uint64_t>(p); p += sizeof(uint64_t);  			lastFirewallOpener = b.template at<uint64_t>(p); p += sizeof(uint64_t);  			localPort = Demarc::intToPort(b.template at<uint64_t>(p)); p += sizeof(uint64_t); -			latency = b.template at<uint16_t>(p); p += sizeof(uint16_t);  			switch ((InetAddress::AddressType)b[p++]) {  				case InetAddress::TYPE_NULL: @@ -497,11 +484,29 @@ private:  		uint64_t lastReceive;  		uint64_t lastFirewallOpener;  		Demarc::Port localPort; // ANY_PORT if not defined (size: uint64_t) -		unsigned int latency; // 0 if never determined  		InetAddress addr; // null InetAddress if path is undefined  		bool fixed; // do not learn address from received packets  	}; +	/** +	 * A history of a packet sent to a peer expecing a response (e.g. HELLO) +	 */ +	class RequestHistoryItem +	{ +	public: +		RequestHistoryItem() : +			timestamp(0), +			packetId(0), +			verb(Packet::VERB_NOP) +		{ +		} + +		uint64_t timestamp; +		uint64_t packetId; +		Demarc::Port localPort; +		Packet::Verb verb; +	}; +  	unsigned char _key[ZT_PEER_SECRET_KEY_LENGTH];  	Identity _id; @@ -512,8 +517,13 @@ private:  	uint64_t _lastUnicastFrame;  	uint64_t _lastMulticastFrame;  	uint64_t _lastAnnouncedTo; +	unsigned int _latency; // milliseconds, 0 if not known  	unsigned int _vMajor,_vMinor,_vRevision; +	// not persisted +	RequestHistoryItem _requestHistory[ZT_PEER_REQUEST_HISTORY_LENGTH]; +	volatile unsigned int _requestHistoryPtr; +  	AtomicCounter __refCount;  }; diff --git a/node/Switch.cpp b/node/Switch.cpp index f0641e74..a46746e7 100644 --- a/node/Switch.cpp +++ b/node/Switch.cpp @@ -219,7 +219,11 @@ bool Switch::sendHELLO(const SharedPtr<Peer> &dest,Demarc::Port localPort,const  	outp.append(now);  	_r->identity.serialize(outp,false);  	outp.armor(dest->key(),false); -	return _r->demarc->send(localPort,remoteAddr,outp.data(),outp.size(),-1); + +	if (_r->demarc->send(localPort,remoteAddr,outp.data(),outp.size(),-1)) { +		dest->expectResponseTo(outp.packetId(),Packet::VERB_HELLO,localPort,now); +		return true; +	} else return false;  }  bool Switch::unite(const Address &p1,const Address &p2,bool force) @@ -696,7 +700,8 @@ bool Switch::_trySend(const Packet &packet,bool encrypt)  		tmp.armor(peer->key(),encrypt); -		if (via->send(_r,tmp.data(),chunkSize,now)) { +		Demarc::Port localPort; +		if ((localPort = via->send(_r,tmp.data(),chunkSize,now))) {  			if (chunkSize < tmp.size()) {  				// Too big for one bite, fragment the rest  				unsigned int fragStart = chunkSize; @@ -716,6 +721,15 @@ bool Switch::_trySend(const Packet &packet,bool encrypt)  					remaining -= chunkSize;  				}  			} + +			switch(packet.verb()) { +				case Packet::VERB_HELLO: +					peer->expectResponseTo(packet.packetId(),Packet::VERB_HELLO,localPort,now); +					break; +				default: +					break; +			} +  			return true;  		}  		return false; | 
