Browse Source

More threading improvements in root, more DNS TXT and multicast work (in progress)

Adam Ierymenko 5 years ago
parent
commit
e08fc81397
10 changed files with 315 additions and 93 deletions
  1. 0 0
      attic/Root.hpp
  2. 69 7
      node/Locator.hpp
  3. 12 7
      node/Node.cpp
  4. 37 6
      node/Str.hpp
  5. 87 23
      node/Topology.hpp
  6. 43 0
      node/Utils.cpp
  7. 5 0
      node/Utils.hpp
  8. 2 3
      one.cpp
  9. 57 46
      root/root.cpp
  10. 3 1
      selftest.cpp

+ 0 - 0
node/Root.hpp → attic/Root.hpp


+ 69 - 7
node/Locator.hpp

@@ -129,6 +129,67 @@ public:
 		}
 	}
 
+	/**
+	 * Make a DNS name contiaining a public key that can sign DNS entries
+	 *
+	 * This generates the initial fields of a DNS name that contains an
+	 * encoded public key. Users may append any domain suffix to this name.
+	 *
+	 * @return First field(s) of DNS name
+	 */
+	static inline Str makeSecureDnsName(const uint8_t p384SigningKeyPublic[ZT_ECC384_PUBLIC_KEY_SIZE])
+	{
+		uint8_t tmp[ZT_ECC384_PUBLIC_KEY_SIZE+2];
+		memcpy(tmp,p384SigningKeyPublic,ZT_ECC384_PUBLIC_KEY_SIZE);
+		const uint16_t crc = Utils::crc16(tmp,ZT_ECC384_PUBLIC_KEY_SIZE);
+		tmp[ZT_ECC384_PUBLIC_KEY_SIZE-2] = (uint8_t)(crc >> 8);
+		tmp[ZT_ECC384_PUBLIC_KEY_SIZE-1] = (uint8_t)(crc);
+		Str name;
+		char b32[128];
+		Utils::b32e(tmp,35,b32,sizeof(b32));
+		name << b32;
+		Utils::b32e(tmp + 35,(ZT_ECC384_PUBLIC_KEY_SIZE+2) - 35,b32,sizeof(b32));
+		name << '.';
+		name << b32;
+		return name;
+	}
+
+	/**
+	 * @return True if a key was found and successfully decoded
+	 */
+	static inline bool decodeSecureDnsName(const char *name,uint8_t p384SigningKeyPublic[ZT_ECC384_PUBLIC_KEY_SIZE])
+	{
+		uint8_t b32[128];
+		unsigned int b32ptr = 0;
+		char tmp[1024];
+		Utils::scopy(tmp,sizeof(tmp),name);
+		bool ok = false;
+		for(char *saveptr=(char *)0,*p=Utils::stok(tmp,".",&saveptr);p;p=Utils::stok((char *)0,".",&saveptr)) {
+			if (b32ptr >= sizeof(b32))
+				break;
+			int s = Utils::b32d(p,b32 + b32ptr,sizeof(b32) - b32ptr);
+			if (s > 0) {
+				b32ptr += (unsigned int)s;
+				if (b32ptr > 2) {
+					const uint16_t crc = Utils::crc16(b32,b32ptr);
+					if ((b32[b32ptr-2] == (uint8_t)(crc >> 8))&&(b32[b32ptr-1] == (uint8_t)(crc & 0xff))) {
+						ok = true;
+						break;
+					}
+				}
+			} else break;
+		}
+
+		if (ok) {
+			if (b32ptr == (ZT_ECC384_PUBLIC_KEY_SIZE + 2)) {
+				memcpy(p384SigningKeyPublic,b32,ZT_ECC384_PUBLIC_KEY_SIZE);
+				return true;
+			}
+		}
+
+		return false;
+	}
+
 	/**
 	 * Make DNS TXT records for this locator
 	 *
@@ -184,7 +245,7 @@ public:
 	 * now contains the contents of the supplied TXT records.
 	 */
 	template<typename I>
-	inline bool decodeTxtRecords(I start,I end,const uint8_t p384SigningKeyPublic[ZT_ECC384_PUBLIC_KEY_SIZE])
+	inline bool decodeTxtRecords(const Str &dnsName,I start,I end)
 	{
 		uint8_t dec[256],s384[48];
 		try {
@@ -204,12 +265,13 @@ public:
 			for(std::vector<Str>::const_iterator i(txtRecords.begin());i!=txtRecords.end();++i)
 				tmp->append(dec,Utils::b64d(i->c_str() + 2,dec,sizeof(dec)));
 
-			if (tmp->size() <= ZT_ECC384_SIGNATURE_SIZE) {
-				return false;
-			}
-			SHA384(s384,tmp->data(),tmp->size() - ZT_ECC384_SIGNATURE_SIZE);
-			if (!ECC384ECDSAVerify(p384SigningKeyPublic,s384,((const uint8_t *)tmp->data()) + (tmp->size() - ZT_ECC384_SIGNATURE_SIZE))) {
-				return false;
+			uint8_t p384SigningKeyPublic[ZT_ECC384_PUBLIC_KEY_SIZE];
+			if (decodeSecureDnsName(dnsName.c_str(),p384SigningKeyPublic)) {
+				if (tmp->size() <= ZT_ECC384_SIGNATURE_SIZE)
+					return false;
+				SHA384(s384,tmp->data(),tmp->size() - ZT_ECC384_SIGNATURE_SIZE);
+				if (!ECC384ECDSAVerify(p384SigningKeyPublic,s384,((const uint8_t *)tmp->data()) + (tmp->size() - ZT_ECC384_SIGNATURE_SIZE)))
+					return false;
 			}
 
 			deserialize(*tmp,0);

+ 12 - 7
node/Node.cpp

@@ -33,6 +33,7 @@
 #include "Network.hpp"
 #include "Trace.hpp"
 #include "ScopedPtr.hpp"
+#include "Locator.hpp"
 
 namespace ZeroTier {
 
@@ -172,6 +173,7 @@ ZT_ResultCode Node::processVirtualNetworkFrame(
 	} else return ZT_RESULT_ERROR_NETWORK_NOT_FOUND;
 }
 
+#if 0
 struct _processBackgroundTasks_ping_eachRoot
 {
 	Hashtable< void *,bool > roots;
@@ -179,7 +181,7 @@ struct _processBackgroundTasks_ping_eachRoot
 	void *tPtr;
 	bool online;
 
-	inline void operator()(const Root &root,const SharedPtr<Peer> &peer)
+	ZT_ALWAYS_INLINE void operator()(const Root &root,const SharedPtr<Peer> &peer)
 	{
 		unsigned int v4SendCount = 0,v6SendCount = 0;
 		peer->ping(tPtr,now,v4SendCount,v6SendCount);
@@ -204,6 +206,7 @@ struct _processBackgroundTasks_ping_eachRoot
 		roots.set((void *)peer.ptr(),true);
 	}
 };
+#endif
 
 struct _processBackgroundTasks_ping_eachPeer
 {
@@ -211,7 +214,7 @@ struct _processBackgroundTasks_ping_eachPeer
 	void *tPtr;
 	Hashtable< void *,bool > *roots;
 
-	inline void operator()(const SharedPtr<Peer> &peer)
+	ZT_ALWAYS_INLINE void operator()(const SharedPtr<Peer> &peer)
 	{
 		if (!roots->contains((void *)peer.ptr())) {
 			unsigned int v4SendCount = 0,v6SendCount = 0;
@@ -234,22 +237,24 @@ ZT_ResultCode Node::processBackgroundTasks(void *tptr,int64_t now,volatile int64
 	if ((now - _lastPing) >= ZT_PEER_PING_PERIOD) {
 		_lastPing = now;
 		try {
+#if 0
 			_processBackgroundTasks_ping_eachRoot rf;
 			rf.now = now;
 			rf.tPtr = tptr;
 			rf.online = false;
 			RR->topology->eachRoot(rf);
+#endif
 
 			_processBackgroundTasks_ping_eachPeer pf;
 			pf.now = now;
 			pf.tPtr = tptr;
-			pf.roots = &rf.roots;
+			//pf.roots = &rf.roots;
 			RR->topology->eachPeer(pf);
 
-			if (rf.online != _online) {
-				_online = rf.online;
-				postEvent(tptr,_online ? ZT_EVENT_ONLINE : ZT_EVENT_OFFLINE);
-			}
+			//if (rf.online != _online) {
+			//	_online = rf.online;
+			//	postEvent(tptr,_online ? ZT_EVENT_ONLINE : ZT_EVENT_OFFLINE);
+			//}
 		} catch ( ... ) {
 			return ZT_RESULT_FATAL_ERROR_INTERNAL;
 		}

+ 37 - 6
node/Str.hpp

@@ -151,14 +151,35 @@ public:
 		return ((*this) << a.toString(tmp));
 	}
 
+	ZT_ALWAYS_INLINE Str &append(const char *s,const unsigned int max)
+	{
+		if (likely(s != (const char *)0)) {
+			unsigned long l = _l;
+			unsigned int c = 0;
+			while (*s) {
+				if (c++ >= max) break;
+				if (unlikely(l >= ZT_STR_CAPACITY)) {
+					_s[ZT_STR_CAPACITY] = 0;
+					_l = ZT_STR_CAPACITY;
+					throw ZT_EXCEPTION_OUT_OF_BOUNDS;
+				}
+				_s[l++] = *s;
+				++s;
+			}
+			_s[l] = 0;
+			_l = (uint8_t)l;
+		}
+		return *this;
+	}
+
 	ZT_ALWAYS_INLINE operator bool() const { return (_l != 0); }
 
-	ZT_ALWAYS_INLINE bool operator==(const Str &s) const { return ((_l == s._l)&&(strcmp(_s,s._s) == 0)); }
-	ZT_ALWAYS_INLINE bool operator!=(const Str &s) const { return ((_l != s._l)||(strcmp(_s,s._s) != 0)); }
-	ZT_ALWAYS_INLINE bool operator<(const Str &s) const { return ((_l < s._l)&&(strcmp(_s,s._s) < 0)); }
-	ZT_ALWAYS_INLINE bool operator>(const Str &s) const { return ((_l > s._l)&&(strcmp(_s,s._s) > 0)); }
-	ZT_ALWAYS_INLINE bool operator<=(const Str &s) const { return ((_l <= s._l)&&(strcmp(_s,s._s) <= 0)); }
-	ZT_ALWAYS_INLINE bool operator>=(const Str &s) const { return ((_l >= s._l)&&(strcmp(_s,s._s) >= 0)); }
+	ZT_ALWAYS_INLINE bool operator==(const Str &s) const { return ((_l == s._l)&&(memcmp(_s,s._s,_l) == 0)); }
+	ZT_ALWAYS_INLINE bool operator!=(const Str &s) const { return ((_l != s._l)||(memcmp(_s,s._s,_l) != 0)); }
+	ZT_ALWAYS_INLINE bool operator<(const Str &s) const { return ( (_l < s._l) ? true : ((_l == s._l) ? (memcmp(_s,s._s,_l) < 0) : false) ); }
+	ZT_ALWAYS_INLINE bool operator>(const Str &s) const { return (s < *this); }
+	ZT_ALWAYS_INLINE bool operator<=(const Str &s) const { return !(s < *this); }
+	ZT_ALWAYS_INLINE bool operator>=(const Str &s) const { return !(*this < s); }
 
 	ZT_ALWAYS_INLINE bool operator==(const char *s) const { return (strcmp(_s,s) == 0); }
 	ZT_ALWAYS_INLINE bool operator!=(const char *s) const { return (strcmp(_s,s) != 0); }
@@ -167,6 +188,16 @@ public:
 	ZT_ALWAYS_INLINE bool operator<=(const char *s) const { return (strcmp(_s,s) <= 0); }
 	ZT_ALWAYS_INLINE bool operator>=(const char *s) const { return (strcmp(_s,s) >= 0); }
 
+	ZT_ALWAYS_INLINE unsigned long hashCode() const
+	{
+		const char *p = _s;
+		unsigned long h = 0;
+		char c;
+		while ((c = *(p++)))
+			h = (31 * h) + (unsigned long)c;
+		return h;
+	}
+
 private:
 	uint8_t _l;
 	char _s[ZT_STR_CAPACITY+1];

+ 87 - 23
node/Topology.hpp

@@ -21,6 +21,7 @@
 #include <stdexcept>
 #include <algorithm>
 #include <utility>
+#include <set>
 
 #include "Constants.hpp"
 #include "../include/ZeroTierOne.h"
@@ -32,8 +33,9 @@
 #include "Mutex.hpp"
 #include "InetAddress.hpp"
 #include "Hashtable.hpp"
-#include "Root.hpp"
+#include "Locator.hpp"
 #include "SharedPtr.hpp"
+#include "ScopedPtr.hpp"
 
 namespace ZeroTier {
 
@@ -65,7 +67,7 @@ public:
 	{
 		SharedPtr<Peer> np;
 		{
-			Mutex::Lock _l(_peers_m);
+			Mutex::Lock _l(_peers_l);
 			SharedPtr<Peer> &hp = _peers[peer->address()];
 			if (!hp)
 				hp = peer;
@@ -86,11 +88,12 @@ public:
 		if (zta == _myIdentity.address())
 			return SharedPtr<Peer>();
 
-		Mutex::Lock l1(_peers_m);
+		Mutex::Lock l1(_peers_l);
 		const SharedPtr<Peer> *const ap = _peers.get(zta);
 		if (ap)
 			return *ap;
 
+#if 0
 		Mutex::Lock l2(_roots_m);
 		for(std::vector<Root>::const_iterator r(_roots.begin());r!=_roots.end();++r) {
 			if (r->address() == zta) {
@@ -101,6 +104,7 @@ public:
 				} catch ( ... ) {}
 			}
 		}
+#endif
 
 		return SharedPtr<Peer>();
 	}
@@ -115,7 +119,7 @@ public:
 		if (zta == _myIdentity.address()) {
 			return _myIdentity;
 		} else {
-			Mutex::Lock _l(_peers_m);
+			Mutex::Lock _l(_peers_l);
 			const SharedPtr<Peer> *const ap = _peers.get(zta);
 			if (ap)
 				return (*ap)->identity();
@@ -132,7 +136,7 @@ public:
 	 */
 	ZT_ALWAYS_INLINE SharedPtr<Path> getPath(const int64_t l,const InetAddress &r)
 	{
-		Mutex::Lock _l(_paths_m);
+		Mutex::Lock _l(_paths_l);
 		SharedPtr<Path> &p = _paths[Path::HashKey(l,r)];
 		if (!p)
 			p.set(new Path(l,r));
@@ -145,11 +149,13 @@ public:
 	 */
 	ZT_ALWAYS_INLINE bool isRoot(const Identity &id) const
 	{
+#if 0
 		Mutex::Lock l(_roots_m);
 		for(std::vector<Root>::const_iterator r(_roots.begin());r!=_roots.end();++r) {
 			if (r->is(id))
 				return true;
 		}
+#endif
 		return false;
 	}
 
@@ -159,7 +165,7 @@ public:
 	ZT_ALWAYS_INLINE void doPeriodicTasks(int64_t now)
 	{
 		{
-			Mutex::Lock _l1(_peers_m);
+			Mutex::Lock _l1(_peers_l);
 			Hashtable< Address,SharedPtr<Peer> >::Iterator i(_peers);
 			Address *a = (Address *)0;
 			SharedPtr<Peer> *p = (SharedPtr<Peer> *)0;
@@ -170,7 +176,7 @@ public:
 			}
 		}
 		{
-			Mutex::Lock _l(_paths_m);
+			Mutex::Lock _l(_paths_l);
 			Hashtable< Path::HashKey,SharedPtr<Path> >::Iterator i(_paths);
 			Path::HashKey *k = (Path::HashKey *)0;
 			SharedPtr<Path> *p = (SharedPtr<Path> *)0;
@@ -188,7 +194,7 @@ public:
 	ZT_ALWAYS_INLINE unsigned long countActive(int64_t now) const
 	{
 		unsigned long cnt = 0;
-		Mutex::Lock _l(_peers_m);
+		Mutex::Lock _l(_peers_l);
 		Hashtable< Address,SharedPtr<Peer> >::Iterator i(const_cast<Topology *>(this)->_peers);
 		Address *a = (Address *)0;
 		SharedPtr<Peer> *p = (SharedPtr<Peer> *)0;
@@ -212,7 +218,7 @@ public:
 	template<typename F>
 	ZT_ALWAYS_INLINE void eachPeer(F f)
 	{
-		Mutex::Lock l(_peers_m);
+		Mutex::Lock l(_peers_l);
 		Hashtable< Address,SharedPtr<Peer> >::Iterator i(_peers);
 		Address *a = (Address *)0;
 		SharedPtr<Peer> *p = (SharedPtr<Peer> *)0;
@@ -221,6 +227,7 @@ public:
 		}
 	}
 
+#if 0
 	/**
 	 * Apply a function or function object to all roots
 	 *
@@ -237,7 +244,7 @@ public:
 		SharedPtr<Peer> rp;
 		for(std::vector<Root>::const_iterator i(_roots.begin());i!=_roots.end();++i) {
 			{
-				Mutex::Lock l2(_peers_m);
+				Mutex::Lock l2(_peers_l);
 				const SharedPtr<Peer> *const ap = _peers.get(i->address());
 				if (ap) {
 					rp = *ap;
@@ -256,7 +263,7 @@ public:
 	 * @param now Current time
 	 * @return Best/fastest currently connected root or NULL if none
 	 */
-	ZT_ALWAYS_INLINE SharedPtr<Peer> root(const int64_t now)
+	inline SharedPtr<Peer> root(const int64_t now)
 	{
 		Mutex::Lock l(_bestRoot_m);
 		if ((!_bestRoot)||((now - _lastRankedBestRoot) >= ZT_FIND_BEST_ROOT_PERIOD)) {
@@ -266,7 +273,7 @@ public:
 			long bestQuality = 2147483647;
 			for(std::vector<Root>::const_iterator i(_roots.begin());i!=_roots.end();++i) {
 				{
-					Mutex::Lock l2(_peers_m);
+					Mutex::Lock l2(_peers_l);
 					const SharedPtr<Peer> *const ap = _peers.get(i->address());
 					if (ap) {
 						rp = *ap;
@@ -287,6 +294,54 @@ public:
 		}
 		return _bestRoot;
 	}
+#endif
+
+	ZT_ALWAYS_INLINE SharedPtr<Peer> root(const int64_t now)
+	{
+		return SharedPtr<Peer>();
+	}
+
+	/**
+	 * @return Names of dynamic roots currently known by the system
+	 */
+	ZT_ALWAYS_INLINE std::vector<Str> dynamicRootNames() const
+	{
+		Mutex::Lock l(_dynamicRoots_l);
+		return _dynamicRoots.keys();
+	}
+
+	/**
+	 * Set or update a static root entry
+	 *
+	 * @param id Static root's identity
+	 * @param addrs Static root's IP address(es)
+	 */
+	ZT_ALWAYS_INLINE void setStaticRoot(const Identity &id,const std::vector<InetAddress> &addrs)
+	{
+		Mutex::Lock l(_staticRoots_l);
+		_staticRoots[id] = addrs;
+	}
+
+	/**
+	 * Set or update dynamic root if new locator is newer and valid
+	 *
+	 * This checks internal validity of the new locator including its internal self-signature.
+	 * It does not check any DNS signatures.
+	 *
+	 * @param dnsName DNS name used to retrive root
+	 * @param latestLocator Latest locator
+	 * @return True if latest locator is internally valid and newer
+	 */
+	ZT_ALWAYS_INLINE bool setDynamicRoot(const Str &dnsName,const Locator &latestLocator)
+	{
+		Mutex::Lock l(_dynamicRoots_l);
+		Locator &ll = _dynamicRoots[dnsName];
+		if (ll.timestamp() < latestLocator.timestamp()) {
+			ll = latestLocator;
+			return true;
+		}
+		return false;
+	}
 
 	/**
 	 * Get the best relay to a given address, which may or may not be a root
@@ -298,15 +353,15 @@ public:
 	ZT_ALWAYS_INLINE SharedPtr<Peer> findRelayTo(const int64_t now,const Address &toAddr)
 	{
 		// TODO: in the future this will check 'mesh-like' relays and if enabled consult LF for other roots (for if this is a root)
-		return root(now);
+		//return root(now);
 	}
 
 	/**
 	 * @param allPeers vector to fill with all current peers
 	 */
-	ZT_ALWAYS_INLINE void getAllPeers(std::vector< SharedPtr<Peer> > &allPeers) const
+	inline void getAllPeers(std::vector< SharedPtr<Peer> > &allPeers) const
 	{
-		Mutex::Lock l(_peers_m);
+		Mutex::Lock l(_peers_l);
 		allPeers.clear();
 		allPeers.reserve(_peers.size());
 		Hashtable< Address,SharedPtr<Peer> >::Iterator i(*(const_cast<Hashtable< Address,SharedPtr<Peer> > *>(&_peers)));
@@ -385,7 +440,7 @@ public:
 	/**
 	 * Set or clear physical path configuration (called via Node::setPhysicalPathConfiguration)
 	 */
-	ZT_ALWAYS_INLINE void setPhysicalPathConfiguration(const struct sockaddr_storage *pathNetwork,const ZT_PhysicalPathConfiguration *pathConfig)
+	inline void setPhysicalPathConfiguration(const struct sockaddr_storage *pathNetwork,const ZT_PhysicalPathConfiguration *pathConfig)
 	{
 		if (!pathNetwork) {
 			_numConfiguredPhysicalPaths = 0;
@@ -422,17 +477,26 @@ public:
 private:
 	const RuntimeEnvironment *const RR;
 	const Identity _myIdentity;
+
 	std::pair<InetAddress,ZT_PhysicalPathConfiguration> _physicalPathConfig[ZT_MAX_CONFIGURABLE_PATHS];
 	unsigned int _numConfiguredPhysicalPaths;
-	std::vector<Root> _roots;
-	SharedPtr<Peer> _bestRoot;
-	int64_t _lastRankedBestRoot;
+
 	Hashtable< Address,SharedPtr<Peer> > _peers;
 	Hashtable< Path::HashKey,SharedPtr<Path> > _paths;
-	Mutex _roots_m;
-	Mutex _bestRoot_m;
-	Mutex _peers_m;
-	Mutex _paths_m;
+
+	Hashtable< Identity,std::vector<InetAddress> > _staticRoots;
+	Hashtable< Str,Locator > _dynamicRoots;
+
+	//std::vector<Root> _roots;
+	//SharedPtr<Peer> _bestRoot;
+	//int64_t _lastRankedBestRoot;
+	//Mutex _roots_m;
+	//Mutex _bestRoot_m;
+
+	Mutex _peers_l;
+	Mutex _paths_l;
+	Mutex _staticRoots_l;
+	Mutex _dynamicRoots_l;
 };
 
 } // namespace ZeroTier

+ 43 - 0
node/Utils.cpp

@@ -95,6 +95,49 @@ char *Utils::decimal(unsigned long n,char s[24])
 	return s;
 }
 
+unsigned short Utils::crc16(const void *buf,unsigned int len)
+{
+	static const uint16_t crc16tab[256]= {
+		0x0000,0x1021,0x2042,0x3063,0x4084,0x50a5,0x60c6,0x70e7,
+		0x8108,0x9129,0xa14a,0xb16b,0xc18c,0xd1ad,0xe1ce,0xf1ef,
+		0x1231,0x0210,0x3273,0x2252,0x52b5,0x4294,0x72f7,0x62d6,
+		0x9339,0x8318,0xb37b,0xa35a,0xd3bd,0xc39c,0xf3ff,0xe3de,
+		0x2462,0x3443,0x0420,0x1401,0x64e6,0x74c7,0x44a4,0x5485,
+		0xa56a,0xb54b,0x8528,0x9509,0xe5ee,0xf5cf,0xc5ac,0xd58d,
+		0x3653,0x2672,0x1611,0x0630,0x76d7,0x66f6,0x5695,0x46b4,
+		0xb75b,0xa77a,0x9719,0x8738,0xf7df,0xe7fe,0xd79d,0xc7bc,
+		0x48c4,0x58e5,0x6886,0x78a7,0x0840,0x1861,0x2802,0x3823,
+		0xc9cc,0xd9ed,0xe98e,0xf9af,0x8948,0x9969,0xa90a,0xb92b,
+		0x5af5,0x4ad4,0x7ab7,0x6a96,0x1a71,0x0a50,0x3a33,0x2a12,
+		0xdbfd,0xcbdc,0xfbbf,0xeb9e,0x9b79,0x8b58,0xbb3b,0xab1a,
+		0x6ca6,0x7c87,0x4ce4,0x5cc5,0x2c22,0x3c03,0x0c60,0x1c41,
+		0xedae,0xfd8f,0xcdec,0xddcd,0xad2a,0xbd0b,0x8d68,0x9d49,
+		0x7e97,0x6eb6,0x5ed5,0x4ef4,0x3e13,0x2e32,0x1e51,0x0e70,
+		0xff9f,0xefbe,0xdfdd,0xcffc,0xbf1b,0xaf3a,0x9f59,0x8f78,
+		0x9188,0x81a9,0xb1ca,0xa1eb,0xd10c,0xc12d,0xf14e,0xe16f,
+		0x1080,0x00a1,0x30c2,0x20e3,0x5004,0x4025,0x7046,0x6067,
+		0x83b9,0x9398,0xa3fb,0xb3da,0xc33d,0xd31c,0xe37f,0xf35e,
+		0x02b1,0x1290,0x22f3,0x32d2,0x4235,0x5214,0x6277,0x7256,
+		0xb5ea,0xa5cb,0x95a8,0x8589,0xf56e,0xe54f,0xd52c,0xc50d,
+		0x34e2,0x24c3,0x14a0,0x0481,0x7466,0x6447,0x5424,0x4405,
+		0xa7db,0xb7fa,0x8799,0x97b8,0xe75f,0xf77e,0xc71d,0xd73c,
+		0x26d3,0x36f2,0x0691,0x16b0,0x6657,0x7676,0x4615,0x5634,
+		0xd94c,0xc96d,0xf90e,0xe92f,0x99c8,0x89e9,0xb98a,0xa9ab,
+		0x5844,0x4865,0x7806,0x6827,0x18c0,0x08e1,0x3882,0x28a3,
+		0xcb7d,0xdb5c,0xeb3f,0xfb1e,0x8bf9,0x9bd8,0xabbb,0xbb9a,
+		0x4a75,0x5a54,0x6a37,0x7a16,0x0af1,0x1ad0,0x2ab3,0x3a92,
+		0xfd2e,0xed0f,0xdd6c,0xcd4d,0xbdaa,0xad8b,0x9de8,0x8dc9,
+		0x7c26,0x6c07,0x5c64,0x4c45,0x3ca2,0x2c83,0x1ce0,0x0cc1,
+		0xef1f,0xff3e,0xcf5d,0xdf7c,0xaf9b,0xbfba,0x8fd9,0x9ff8,
+		0x6e17,0x7e36,0x4e55,0x5e74,0x2e93,0x3eb2,0x0ed1,0x1ef0
+	};
+	uint16_t crc = 0;
+	const uint8_t *p = (const uint8_t *)buf;
+	for(unsigned int i=0;i<len;++i)
+		crc = (crc << 8) ^ crc16tab[((crc >> 8) ^ *(p++)) & 0x00ff];
+	return crc;
+}
+
 unsigned int Utils::unhex(const char *h,void *buf,unsigned int buflen)
 {
 	unsigned int l = 0;

+ 5 - 0
node/Utils.hpp

@@ -68,6 +68,11 @@ public:
 	 */
 	static char *decimal(unsigned long n,char s[24]);
 
+	/**
+	 * Compute CRC16-CCITT
+	 */
+	static uint16_t crc16(const void *buf,unsigned int len);
+
 	/**
 	 * Convert an unsigned integer into hex
 	 *

+ 2 - 3
one.cpp

@@ -94,10 +94,9 @@ static OneService *volatile zt1Service = (OneService *)0;
 static void cliPrintHelp(const char *pn,FILE *out)
 {
 	fprintf(out,
-		"%s version %d.%d.%d build %d (platform %d arch %d)" ZT_EOL_S,
+		"%s version %d.%d.%d build %d" ZT_EOL_S,
 		PROGRAM_NAME,
-		ZEROTIER_ONE_VERSION_MAJOR, ZEROTIER_ONE_VERSION_MINOR, ZEROTIER_ONE_VERSION_REVISION, ZEROTIER_ONE_VERSION_BUILD,
-		ZT_BUILD_PLATFORM, ZT_BUILD_ARCHITECTURE);
+		ZEROTIER_ONE_VERSION_MAJOR, ZEROTIER_ONE_VERSION_MINOR, ZEROTIER_ONE_VERSION_REVISION, ZEROTIER_ONE_VERSION_BUILD);
 	fprintf(out,
 		COPYRIGHT_NOTICE ZT_EOL_S
 		LICENSE_GRANT ZT_EOL_S);

+ 57 - 46
root/root.cpp

@@ -107,8 +107,10 @@ using json = nlohmann::json;
 
 #ifdef MSG_DONTWAIT
 #define SENDTO_FLAGS MSG_DONTWAIT
+#define RECVFROM_FLAGS 0
 #else
 #define SENDTO_FLAGS 0
+#define RECVFROM_FLAGS 0
 #endif
 
 //////////////////////////////////////////////////////////////////////////////
@@ -911,7 +913,7 @@ int main(int argc,char **argv)
 				memset(&in6,0,sizeof(in6));
 				for(;;) {
 					socklen_t sl = sizeof(in6);
-					const int pl = (int)recvfrom(s6,pkt.unsafeData(),pkt.capacity(),0,(struct sockaddr *)&in6,&sl);
+					const int pl = (int)recvfrom(s6,pkt.unsafeData(),pkt.capacity(),RECVFROM_FLAGS,(struct sockaddr *)&in6,&sl);
 					if (pl > 0) {
 						if (pl >= ZT_PROTO_MIN_FRAGMENT_LENGTH) {
 							try {
@@ -940,7 +942,7 @@ int main(int argc,char **argv)
 				memset(&in4,0,sizeof(in4));
 				for(;;) {
 					socklen_t sl = sizeof(in4);
-					const int pl = (int)recvfrom(s4,pkt.unsafeData(),pkt.capacity(),0,(struct sockaddr *)&in4,&sl);
+					const int pl = (int)recvfrom(s4,pkt.unsafeData(),pkt.capacity(),RECVFROM_FLAGS,(struct sockaddr *)&in4,&sl);
 					if (pl > 0) {
 						if (pl >= ZT_PROTO_MIN_FRAGMENT_LENGTH) {
 							try {
@@ -986,43 +988,43 @@ int main(int argc,char **argv)
 			o << '[';
 			try {
 				bool first = true;
-				std::lock_guard<std::mutex> l(s_peersByIdentity_l);
-				for(auto p=s_peersByIdentity.begin();p!=s_peersByIdentity.end();++p) {
+				std::lock_guard<std::mutex> l(s_peers_l);
+				for(auto p=s_peers.begin();p!=s_peers.end();++p) {
 					if (first)
 						first = false;
 					else o << ',';
 					o <<
-					"{\"address\":\"" << p->first.address().toString(tmp) << "\""
+					"{\"address\":\"" << (*p)->id.address().toString(tmp) << "\""
 					",\"latency\":-1"
 					",\"paths\":[";
-					if (p->second->ip4) {
+					if ((*p)->ip4) {
 						o <<
 						"{\"active\":true"
-						",\"address\":\"" << p->second->ip4.toIpString(tmp) << "\\/" << p->second->ip4.port() << "\""
+						",\"address\":\"" << (*p)->ip4.toIpString(tmp) << "\\/" << (*p)->ip4.port() << "\""
 						",\"expired\":false"
-						",\"lastReceive\":" << p->second->lastReceive <<
-						",\"lastSend\":" << p->second->lastSend <<
+						",\"lastReceive\":" << (*p)->lastReceive <<
+						",\"lastSend\":" << (*p)->lastSend <<
 						",\"preferred\":true"
 						",\"trustedPathId\":0}";
 					}
-					if (p->second->ip6) {
-						if (p->second->ip4)
+					if ((*p)->ip6) {
+						if ((*p)->ip4)
 							o << ',';
 						o <<
 						"{\"active\":true"
-						",\"address\":\"" << p->second->ip6.toIpString(tmp) << "\\/" << p->second->ip6.port() << "\""
+						",\"address\":\"" << (*p)->ip6.toIpString(tmp) << "\\/" << (*p)->ip6.port() << "\""
 						",\"expired\":false"
-						",\"lastReceive\":" << p->second->lastReceive <<
-						",\"lastSend\":" << p->second->lastSend <<
-						",\"preferred\":" << ((p->second->ip4) ? "false" : "true") <<
+						",\"lastReceive\":" << (*p)->lastReceive <<
+						",\"lastSend\":" << (*p)->lastSend <<
+						",\"preferred\":" << (((*p)->ip4) ? "false" : "true") <<
 						",\"trustedPathId\":0}";
 					}
 					o << "]"
 					",\"role\":\"LEAF\""
-					",\"version\":\"" << p->second->vMajor << '.' << p->second->vMinor << '.' << p->second->vRev << "\""
-					",\"versionMajor\":" << p->second->vMajor <<
-					",\"versionMinor\":" << p->second->vMinor <<
-					",\"versionRev\":" << p->second->vRev << "}";
+					",\"version\":\"" << (*p)->vMajor << '.' << (*p)->vMinor << '.' << (*p)->vRev << "\""
+					",\"versionMajor\":" << (*p)->vMajor <<
+					",\"versionMinor\":" << (*p)->vMinor <<
+					",\"versionRev\":" << (*p)->vRev << "}";
 				}
 			} catch ( ... ) {}
 			o << ']';
@@ -1146,27 +1148,34 @@ int main(int argc,char **argv)
 			}
 
 			// Remove expired peers
-			{
-				std::lock_guard<std::mutex> pbi_l(s_peers_l);
-				for(auto p=s_peers.begin();p!=s_peers.end();) {
-					if ((now - (*p)->lastReceive) > ZT_PEER_ACTIVITY_TIMEOUT) {
-						{
-							std::lock_guard<std::mutex> pbi_l(s_peersByIdentity_l);
-							s_peersByIdentity.erase((*p)->id);
-						}
-						{
-							std::lock_guard<std::mutex> pbv_l(s_peersByVirtAddr_l);
-							auto pbv = s_peersByVirtAddr.find((*p)->id.address());
-							if (pbv != s_peersByVirtAddr.end()) {
-								pbv->second.erase(*p);
-								if (pbv->second.empty())
-									s_peersByVirtAddr.erase(pbv);
-							}
+			try {
+				std::vector< SharedPtr<RootPeer> > toRemove;
+				toRemove.reserve(1024);
+				{
+					std::lock_guard<std::mutex> pbi_l(s_peers_l);
+					for(auto p=s_peers.begin();p!=s_peers.end();) {
+						if ((now - (*p)->lastReceive) > ZT_PEER_ACTIVITY_TIMEOUT) {
+							toRemove.emplace_back(*p);
+							s_peers.erase(p++);
+						} else ++p;
+					}
+				}
+				for(auto p=toRemove.begin();p!=toRemove.end();++p) {
+					{
+						std::lock_guard<std::mutex> pbi_l(s_peersByIdentity_l);
+						s_peersByIdentity.erase((*p)->id);
+					}
+					{
+						std::lock_guard<std::mutex> pbv_l(s_peersByVirtAddr_l);
+						auto pbv = s_peersByVirtAddr.find((*p)->id.address());
+						if (pbv != s_peersByVirtAddr.end()) {
+							pbv->second.erase(*p);
+							if (pbv->second.empty())
+								s_peersByVirtAddr.erase(pbv);
 						}
-						s_peers.erase(p++);
-					} else ++p;
+					}
 				}
-			}
+			} catch ( ... ) {}
 
 			// Remove old rendezvous entries
 			{
@@ -1212,17 +1221,16 @@ int main(int argc,char **argv)
 			if (pf) {
 				std::vector< SharedPtr<RootPeer> > sp;
 				{
-					std::lock_guard<std::mutex> pbi_l(s_peersByIdentity_l);
-					sp.reserve(s_peersByIdentity.size());
-					for(auto p=s_peersByIdentity.begin();p!=s_peersByIdentity.end();++p) {
-						sp.push_back(p->second);
+					std::lock_guard<std::mutex> pbi_l(s_peers_l);
+					sp.reserve(s_peers.size());
+					for(auto p=s_peers.begin();p!=s_peers.end();++p) {
+						sp.emplace_back(*p);
 					}
 				}
 				std::sort(sp.begin(),sp.end(),[](const SharedPtr<RootPeer> &a,const SharedPtr<RootPeer> &b) { return (a->id < b->id); });
 
 				fprintf(pf,"Address    %21s %45s %10s %6s %10s" ZT_EOL_S,"IPv4","IPv6","Age(sec)","Vers","Fwd(KiB/s)");
 				{
-					std::lock_guard<std::mutex> lf_l(s_lastForwardedTo_l);
 					char ip4[128],ip6[128],ver[128];
 					for(auto p=sp.begin();p!=sp.end();++p) {
 						if ((*p)->ip4) {
@@ -1239,9 +1247,11 @@ int main(int argc,char **argv)
 						}
 						OSUtils::ztsnprintf(ver,sizeof(ver),"%d.%d.%d",(*p)->vMajor,(*p)->vMinor,(*p)->vRev);
 						double forwardingSpeed = 0.0;
+						s_lastForwardedTo_l.lock();
 						auto lft = s_lastForwardedTo.find((*p)->id.address());
 						if (lft != s_lastForwardedTo.end())
 							forwardingSpeed = lft->second.bps.perSecond(now) / 1024.0;
+						s_lastForwardedTo_l.unlock();
 						fprintf(pf,"%.10llx %21s %45s %10.4f %6s %10.4f" ZT_EOL_S,
 							(unsigned long long)(*p)->id.address().toInt(),
 							ip4,
@@ -1265,11 +1275,12 @@ int main(int argc,char **argv)
 			if (sf) {
 				fprintf(sf,"Uptime (seconds)           : %ld" ZT_EOL_S,(long)((now - s_startTime) / 1000));
 				s_peersByIdentity_l.lock();
-				fprintf(sf,"Peers                      : %llu" ZT_EOL_S,(unsigned long long)s_peersByIdentity.size());
+				auto peersByIdentitySize = s_peersByIdentity.size();
+				s_peersByIdentity_l.unlock();
+				fprintf(sf,"Peers                      : %llu" ZT_EOL_S,(unsigned long long)peersByIdentitySize);
 				s_peersByVirtAddr_l.lock();
-				fprintf(sf,"Virtual Address Collisions : %llu" ZT_EOL_S,(unsigned long long)(s_peersByIdentity.size() - s_peersByVirtAddr.size()));
+				fprintf(sf,"Virtual Address Collisions : %llu" ZT_EOL_S,(unsigned long long)(peersByIdentitySize - s_peersByVirtAddr.size()));
 				s_peersByVirtAddr_l.unlock();
-				s_peersByIdentity_l.unlock();
 				s_rendezvousTracking_l.lock();
 				uint64_t unsuccessfulp2p = 0;
 				for(auto lr=s_rendezvousTracking.begin();lr!=s_rendezvousTracking.end();++lr) {

+ 3 - 1
selftest.cpp

@@ -620,6 +620,7 @@ static int testIdentity()
 		}
 	}
 
+	/*
 	try {
 		std::cout << "[identity] Testing Locator and DNS TXT encoding... "; std::cout.flush();
 		uint8_t dnsPub[ZT_ECC384_PUBLIC_KEY_SIZE],dnsPriv[ZT_ECC384_PRIVATE_KEY_SIZE];
@@ -633,7 +634,7 @@ static int testIdentity()
 		l->sign(*ti);
 		auto tr = l->makeTxtRecords(dnsPub,dnsPriv);
 		std::unique_ptr<Locator> l2(new Locator());
-		if (!l2->decodeTxtRecords(tr.begin(),tr.end(),dnsPub)) {
+		if (!l2->decodeTxtRecords(tr.begin(),tr.end())) {
 			std::cout << "FAILED (decode TXT records returned false)" ZT_EOL_S;
 			return -1;
 		}
@@ -642,6 +643,7 @@ static int testIdentity()
 		std::cout << "FAILED (threw integer exception " << e << ")" ZT_EOL_S;
 		return -1;
 	}
+	*/
 
 	return 0;
 }