Browse Source

More refactoring and a bunch of AES goodness.

Adam Ierymenko 5 years ago
parent
commit
08e06f9b8f
11 changed files with 814 additions and 201 deletions
  1. 534 0
      node/AES.cpp
  2. 185 97
      node/AES.hpp
  3. 13 15
      node/Node.cpp
  4. 18 18
      node/Node.hpp
  5. 0 31
      node/Path.cpp
  6. 0 11
      node/Path.hpp
  7. 3 3
      node/Salsa20.cpp
  8. 5 5
      node/Salsa20.hpp
  9. 22 1
      node/Utils.hpp
  10. 19 16
      node/VL1.cpp
  11. 15 4
      node/VL1.hpp

File diff suppressed because it is too large
+ 534 - 0
node/AES.cpp


+ 185 - 97
node/AES.hpp

@@ -19,6 +19,7 @@
 #include "SHA512.hpp"
 
 #include <cstdint>
+#include <cstring>
 
 #if (defined(__amd64) || defined(__amd64__) || defined(__x86_64) || defined(__x86_64__) || defined(__AMD64) || defined(__AMD64__) || defined(_M_X64))
 #include <xmmintrin.h>
@@ -55,7 +56,7 @@ public:
 	}
 
 	/**
-	 * Encrypt a single AES block (ECB mode)
+	 * Encrypt a single AES block
 	 *
 	 * @param in Input block
 	 * @param out Output block (can be same as input)
@@ -71,131 +72,159 @@ public:
 		_encryptSW(in,out);
 	}
 
+	/**
+	 * Decrypt a single AES block
+	 *
+	 * @param in Input block
+	 * @param out Output block (can be same as input)
+	 */
+	ZT_ALWAYS_INLINE void decrypt(const uint8_t in[16],uint8_t out[16]) const noexcept
+	{
+#ifdef ZT_AES_AESNI
+		if (likely(Utils::CPUID.aes)) {
+			_decrypt_aesni(in,out);
+			return;
+		}
+#endif
+		_decryptSW(in,out);
+	}
+
+	/**
+	 * Streaming GMAC calculator
+	 */
+	class GMAC
+	{
+	public:
+		/**
+		 * Create a new instance of GMAC (must be initialized with init() before use)
+		 *
+		 * @param aes Keyed AES instance to use
+		 */
+		ZT_ALWAYS_INLINE GMAC(const AES &aes) : _aes(aes) {}
+
+		ZT_ALWAYS_INLINE void init(const uint8_t iv[12]) noexcept
+		{
+			_rp = 0;
+			_len = 0;
+#ifdef ZT_AES_AESNI // also implies an x64 processor
+			*reinterpret_cast<uint64_t *>(_iv) = *reinterpret_cast<const uint64_t *>(iv);
+			*reinterpret_cast<uint32_t *>(_iv + 8) = *reinterpret_cast<const uint64_t *>(iv + 8);
+			*reinterpret_cast<uint32_t *>(_iv + 12) = 0x01000000; // 00000001 in big-endian byte order
+#else
+			for(int i=0;i<12;++i)
+				_iv[i] = iv[i];
+			_iv[12] = 0;
+			_iv[13] = 0;
+			_iv[14] = 0;
+			_iv[15] = 1;
+#endif
+			_y[0] = 0;
+			_y[1] = 0;
+		}
+
+		void update(const void *data,unsigned int len) noexcept;
+
+		void finish(uint8_t tag[16]) noexcept;
+
+	private:
+		const AES &_aes;
+		unsigned int _rp;
+		unsigned int _len;
+		uint8_t _r[16]; // remainder
+		uint8_t _iv[16];
+		uint64_t _y[2];
+	};
+
+	/**
+	 * Streaming AES-CTR encrypt/decrypt
+	 */
+	class CTR
+	{
+	public:
+		ZT_ALWAYS_INLINE CTR(const AES &aes) noexcept : _aes(aes) {}
+
+		/**
+		 * Initialize this CTR instance to encrypt a new stream
+		 *
+		 * @param iv Unique initialization vector
+		 * @param output Buffer to which to store output (MUST be large enough for total bytes processed!)
+		 */
+		ZT_ALWAYS_INLINE void init(const uint8_t iv[16],void *output) noexcept
+		{
+#ifdef ZT_AES_AESNI // also implies an x64 processor
+			_ctr[0] = Utils::ntoh(*reinterpret_cast<const uint64_t *>(iv));
+			_ctr[1] = Utils::ntoh(*reinterpret_cast<const uint64_t *>(iv + 8));
+#else
+			memcpy(_ctr,iv,16);
+			_ctr[0] = Utils::ntoh(_ctr[0]);
+			_ctr[1] = Utils::ntoh(_ctr[1]);
+#endif
+			_out = reinterpret_cast<uint8_t *>(output);
+			_len = 0;
+		}
+
+		/**
+		 * Encrypt or decrypt data, writing result to the output provided to init()
+		 *
+		 * @param input Input data
+		 * @param len Length of input
+		 */
+		void crypt(const void *input,unsigned int len) noexcept;
+
+		/**
+		 * Finish any remaining bytes if total bytes processed wasn't a multiple of 16
+		 */
+		void finish() noexcept;
+
+	private:
+		const AES &_aes;
+		uint64_t _ctr[2];
+		uint8_t *_out;
+		unsigned int _len;
+	};
+
 private:
 	static const uint32_t Te0[256];
 	static const uint32_t Te1[256];
 	static const uint32_t Te2[256];
 	static const uint32_t Te3[256];
+	static const uint32_t Te4[256];
+	static const uint32_t Td0[256];
+	static const uint32_t Td1[256];
+	static const uint32_t Td2[256];
+	static const uint32_t Td3[256];
+	static const uint8_t Td4[256];
 	static const uint32_t rcon[10];
 
 	void _initSW(const uint8_t key[32]) noexcept;
 	void _encryptSW(const uint8_t in[16],uint8_t out[16]) const noexcept;
+	void _decryptSW(const uint8_t in[16],uint8_t out[16]) const noexcept;
 	void _gmacSW(const uint8_t iv[12],const uint8_t *in,unsigned int len,uint8_t out[16]) const noexcept;
 
 	union {
-#ifdef ZT_AES_ARMNEON
-		// ARM NEON key and GMAC parameters
-		struct {
-			uint32x4_t k[15];
-		} neon;
-#endif
-
 #ifdef ZT_AES_AESNI
-		// AES-NI key and GMAC parameters
 		struct {
-			__m128i k[15];
+			__m128i k[28];
 			__m128i h,hh,hhh,hhhh;
 		} ni;
 #endif
 
-		// Software mode key and GMAC parameters
 		struct {
 			uint64_t h[2];
 			uint32_t ek[60];
+			uint32_t dk[60];
 		} sw;
 	} _k;
 
-#ifdef ZT_AES_ARMNEON
-	static inline void _aes_256_expAssist_armneon(uint32x4_t prev1,uint32x4_t prev2,uint32_t rcon,uint32x4_t *e1,uint32x4_t *e2) noexcept
-	{
-		uint32_t round1[4], round2[4], prv1[4], prv2[4];
-		vst1q_u32(prv1, prev1);
-		vst1q_u32(prv2, prev2);
-		round1[0] = sub_word(rot_word(prv2[3])) ^ rcon ^ prv1[0];
-		round1[1] = sub_word(rot_word(round1[0])) ^ rcon ^ prv1[1];
-		round1[2] = sub_word(rot_word(round1[1])) ^ rcon ^ prv1[2];
-		round1[3] = sub_word(rot_word(round1[2])) ^ rcon ^ prv1[3];
-		round2[0] = sub_word(rot_word(round1[3])) ^ rcon ^ prv2[0];
-		round2[1] = sub_word(rot_word(round2[0])) ^ rcon ^ prv2[1];
-		round2[2] = sub_word(rot_word(round2[1])) ^ rcon ^ prv2[2];
-		round2[3] = sub_word(rot_word(round2[2])) ^ rcon ^ prv2[3];
-		*e1 = vld1q_u3(round1);
-		*e2 = vld1q_u3(round2);
-		//uint32x4_t expansion[2] = {vld1q_u3(round1), vld1q_u3(round2)};
-		//return expansion;
-	}
-
-	inline void _init_armneon(uint8x16_t encKey) noexcept
-	{
-		uint32x4_t *schedule = _k.neon.k;
-		uint32x4_t e1,e2;
-		(*schedule)[0] = vld1q_u32(encKey);
-		(*schedule)[1] = vld1q_u32(encKey + 16);
-		_aes_256_expAssist_armneon((*schedule)[0],(*schedule)[1],0x01,&e1,&e2);
-		(*schedule)[2] = e1; (*schedule)[3] = e2;
-		_aes_256_expAssist_armneon((*schedule)[2],(*schedule)[3],0x01,&e1,&e2);
-		(*schedule)[4] = e1; (*schedule)[5] = e2;
-		_aes_256_expAssist_armneon((*schedule)[4],(*schedule)[5],0x01,&e1,&e2);
-		(*schedule)[6] = e1; (*schedule)[7] = e2;
-		_aes_256_expAssist_armneon((*schedule)[6],(*schedule)[7],0x01,&e1,&e2);
-		(*schedule)[8] = e1; (*schedule)[9] = e2;
-		_aes_256_expAssist_armneon((*schedule)[8],(*schedule)[9],0x01,&e1,&e2);
-		(*schedule)[10] = e1; (*schedule)[11] = e2;
-		_aes_256_expAssist_armneon((*schedule)[10],(*schedule)[11],0x01,&e1,&e2);
-		(*schedule)[12] = e1; (*schedule)[13] = e2;
-		_aes_256_expAssist_armneon((*schedule)[12],(*schedule)[13],0x01,&e1,&e2);
-		(*schedule)[14] = e1;
-		/*
-		doubleRound = _aes_256_expAssist_armneon((*schedule)[0], (*schedule)[1], 0x01);
-		(*schedule)[2] = doubleRound[0];
-		(*schedule)[3] = doubleRound[1];
-		doubleRound = _aes_256_expAssist_armneon((*schedule)[2], (*schedule)[3], 0x02);
-		(*schedule)[4] = doubleRound[0];
-		(*schedule)[5] = doubleRound[1];
-		doubleRound = _aes_256_expAssist_armneon((*schedule)[4], (*schedule)[5], 0x04);
-		(*schedule)[6] = doubleRound[0];
-		(*schedule)[7] = doubleRound[1];
-		doubleRound = _aes_256_expAssist_armneon((*schedule)[6], (*schedule)[7], 0x08);
-		(*schedule)[8] = doubleRound[0];
-		(*schedule)[9] = doubleRound[1];
-		doubleRound = _aes_256_expAssist_armneon((*schedule)[8], (*schedule)[9], 0x10);
-		(*schedule)[10] = doubleRound[0];
-		(*schedule)[11] = doubleRound[1];
-		doubleRound = _aes_256_expAssist_armneon((*schedule)[10], (*schedule)[11], 0x20);
-		(*schedule)[12] = doubleRound[0];
-		(*schedule)[13] = doubleRound[1];
-		doubleRound = _aes_256_expAssist_armneon((*schedule)[12], (*schedule)[13], 0x40);
-		(*schedule)[14] = doubleRound[0];
-		*/
-	}
-
-	inline void _encrypt_armneon(uint8x16_t *data) const noexcept
-	{
-		*data = veorq_u8(*data, _k.neon.k[0]);
-		*data = vaesmcq_u8(vaeseq_u8(*data, (uint8x16_t)_k.neon.k[1]));
-		*data = vaesmcq_u8(vaeseq_u8(*data, (uint8x16_t)_k.neon.k[2]));
-		*data = vaesmcq_u8(vaeseq_u8(*data, (uint8x16_t)_k.neon.k[3]));
-		*data = vaesmcq_u8(vaeseq_u8(*data, (uint8x16_t)_k.neon.k[4]));
-		*data = vaesmcq_u8(vaeseq_u8(*data, (uint8x16_t)_k.neon.k[5]));
-		*data = vaesmcq_u8(vaeseq_u8(*data, (uint8x16_t)_k.neon.k[6]));
-		*data = vaesmcq_u8(vaeseq_u8(*data, (uint8x16_t)_k.neon.k[7]));
-		*data = vaesmcq_u8(vaeseq_u8(*data, (uint8x16_t)_k.neon.k[8]));
-		*data = vaesmcq_u8(vaeseq_u8(*data, (uint8x16_t)_k.neon.k[9]));
-		*data = vaesmcq_u8(vaeseq_u8(*data, (uint8x16_t)_k.neon.k[10]));
-		*data = vaesmcq_u8(vaeseq_u8(*data, (uint8x16_t)_k.neon.k[11]));
-		*data = vaesmcq_u8(vaeseq_u8(*data, (uint8x16_t)_k.neon.k[12]));
-		*data = vaesmcq_u8(vaeseq_u8(*data, (uint8x16_t)_k.neon.k[13]));
-		*data = vaeseq_u8(*data, _k.neon.k[14]);
-	}
-#endif
 
 #ifdef ZT_AES_AESNI
+	static const __m128i s_shuf;
+
 	void _init_aesni(const uint8_t key[32]) noexcept;
 
 	ZT_ALWAYS_INLINE void _encrypt_aesni(const void *const in,void *const out) const noexcept
 	{
-		__m128i tmp;
-		tmp = _mm_loadu_si128((const __m128i *)in);
+		__m128i tmp = _mm_loadu_si128((const __m128i *)in);
 		tmp = _mm_xor_si128(tmp,_k.ni.k[0]);
 		tmp = _mm_aesenc_si128(tmp,_k.ni.k[1]);
 		tmp = _mm_aesenc_si128(tmp,_k.ni.k[2]);
@@ -213,7 +242,66 @@ private:
 		_mm_storeu_si128((__m128i *)out,_mm_aesenclast_si128(tmp,_k.ni.k[14]));
 	}
 
-	void _gmac_aesni(const uint8_t iv[12],const uint8_t *in,unsigned int len,uint8_t out[16]) const noexcept;
+	ZT_ALWAYS_INLINE void _decrypt_aesni(const void *in,void *out) const noexcept
+	{
+		__m128i tmp = _mm_loadu_si128((const __m128i *)in);
+		tmp = _mm_xor_si128(tmp,_k.ni.k[14]);
+		tmp = _mm_aesdec_si128(tmp,_k.ni.k[15]);
+		tmp = _mm_aesdec_si128(tmp,_k.ni.k[16]);
+		tmp = _mm_aesdec_si128(tmp,_k.ni.k[17]);
+		tmp = _mm_aesdec_si128(tmp,_k.ni.k[18]);
+		tmp = _mm_aesdec_si128(tmp,_k.ni.k[19]);
+		tmp = _mm_aesdec_si128(tmp,_k.ni.k[20]);
+		tmp = _mm_aesdec_si128(tmp,_k.ni.k[21]);
+		tmp = _mm_aesdec_si128(tmp,_k.ni.k[22]);
+		tmp = _mm_aesdec_si128(tmp,_k.ni.k[23]);
+		tmp = _mm_aesdec_si128(tmp,_k.ni.k[24]);
+		tmp = _mm_aesdec_si128(tmp,_k.ni.k[25]);
+		tmp = _mm_aesdec_si128(tmp,_k.ni.k[26]);
+		tmp = _mm_aesdec_si128(tmp,_k.ni.k[27]);
+		_mm_storeu_si128((__m128i *)out,_mm_aesdeclast_si128(tmp,_k.ni.k[0]));
+	}
+
+	static ZT_ALWAYS_INLINE __m128i _mult_block_aesni(const __m128i shuf,const __m128i h,__m128i y) noexcept
+	{
+		y = _mm_shuffle_epi8(y,shuf);
+		__m128i t1 = _mm_clmulepi64_si128(h,y,0x00);
+		__m128i t2 = _mm_clmulepi64_si128(h,y,0x01);
+		__m128i t3 = _mm_clmulepi64_si128(h,y,0x10);
+		__m128i t4 = _mm_clmulepi64_si128(h,y,0x11);
+		t2 = _mm_xor_si128(t2,t3);
+		t3 = _mm_slli_si128(t2,8);
+		t2 = _mm_srli_si128(t2,8);
+		t1 = _mm_xor_si128(t1,t3);
+		t4 = _mm_xor_si128(t4,t2);
+		__m128i t5 = _mm_srli_epi32(t1,31);
+		t1 = _mm_slli_epi32(t1,1);
+		__m128i t6 = _mm_srli_epi32(t4,31);
+		t4 = _mm_slli_epi32(t4,1);
+		t3 = _mm_srli_si128(t5,12);
+		t6 = _mm_slli_si128(t6,4);
+		t5 = _mm_slli_si128(t5,4);
+		t1 = _mm_or_si128(t1,t5);
+		t4 = _mm_or_si128(t4,t6);
+		t4 = _mm_or_si128(t4,t3);
+		t5 = _mm_slli_epi32(t1,31);
+		t6 = _mm_slli_epi32(t1,30);
+		t3 = _mm_slli_epi32(t1,25);
+		t5 = _mm_xor_si128(t5,t6);
+		t5 = _mm_xor_si128(t5,t3);
+		t6 = _mm_srli_si128(t5,4);
+		t4 = _mm_xor_si128(t4,t6);
+		t5 = _mm_slli_si128(t5,12);
+		t1 = _mm_xor_si128(t1,t5);
+		t4 = _mm_xor_si128(t4,t1);
+		t5 = _mm_srli_epi32(t1,1);
+		t2 = _mm_srli_epi32(t1,2);
+		t3 = _mm_srli_epi32(t1,7);
+		t4 = _mm_xor_si128(t4,t2);
+		t4 = _mm_xor_si128(t4,t3);
+		t4 = _mm_xor_si128(t4,t5);
+		return _mm_shuffle_epi8(t4,shuf);
+	}
 #endif
 };
 

+ 13 - 15
node/Node.cpp

@@ -296,7 +296,7 @@ ZT_ResultCode Node::processBackgroundTasks(void *tPtr, int64_t now, volatile int
 		for(std::map<Address,int64_t>::iterator a(_peerAlarms.begin());a!=_peerAlarms.end();) {
 			if (now >= a->second) {
 				bzzt.push_back(a->first);
-				l.write(); // acquire write lock if not already in write mode
+				l.writing();
 				_peerAlarms.erase(a++);
 			} else {
 				if (a->second < earliestAlarmAt)
@@ -556,17 +556,15 @@ void Node::setInterfaceAddresses(const ZT_InterfaceAddress *addrs,unsigned int a
 	Mutex::Lock _l(_localInterfaceAddresses_m);
 	_localInterfaceAddresses.clear();
 	for(unsigned int i=0;i<addrCount;++i) {
-		if (Path::isAddressValidForPath(*(reinterpret_cast<const InetAddress *>(&addrs[i].address)))) {
-			bool dupe = false;
-			for(unsigned int j=0;j<i;++j) {
-				if (*(reinterpret_cast<const InetAddress *>(&addrs[j].address)) == *(reinterpret_cast<const InetAddress *>(&addrs[i].address))) {
-					dupe = true;
-					break;
-				}
+		bool dupe = false;
+		for(unsigned int j=0;j<i;++j) {
+			if (*(reinterpret_cast<const InetAddress *>(&addrs[j].address)) == *(reinterpret_cast<const InetAddress *>(&addrs[i].address))) {
+				dupe = true;
+				break;
 			}
-			if (!dupe)
-				_localInterfaceAddresses.push_back(addrs[i]);
 		}
+		if (!dupe)
+			_localInterfaceAddresses.push_back(addrs[i]);
 	}
 }
 
@@ -620,19 +618,18 @@ std::vector<uint8_t> Node::stateObjectGet(void *const tPtr,ZT_StateObjectType ty
 
 bool Node::shouldUsePathForZeroTierTraffic(void *tPtr,const Identity &id,const int64_t localSocket,const InetAddress &remoteAddress)
 {
-	if (Path::isAddressValidForPath(remoteAddress)) {
+	{
 		RWMutex::RLock l(_networks_m);
-		for(std::vector< SharedPtr<Network> >::iterator i(_networks.begin());i!=_networks.end();++i) {
+		for (std::vector<SharedPtr<Network> >::iterator i(_networks.begin()); i != _networks.end(); ++i) {
 			if ((*i)) {
-				for(unsigned int k=0,j=(*i)->config().staticIpCount;k<j;++k) {
+				for (unsigned int k = 0,j = (*i)->config().staticIpCount; k < j; ++k) {
 					if ((*i)->config().staticIps[k].containsAddress(remoteAddress))
 						return false;
 				}
 			}
 		}
-	} else {
-		return false;
 	}
+
 	if (_cb.pathCheckFunction) {
 		return (_cb.pathCheckFunction(
 			reinterpret_cast<ZT_Node *>(this),
@@ -643,6 +640,7 @@ bool Node::shouldUsePathForZeroTierTraffic(void *tPtr,const Identity &id,const i
 			localSocket,
 			reinterpret_cast<const struct sockaddr_storage *>(&remoteAddress)) != 0);
 	}
+
 	return true;
 }
 

+ 18 - 18
node/Node.hpp

@@ -109,7 +109,7 @@ public:
 	/**
 	 * @return Most recent time value supplied to core via API
 	 */
-	ZT_ALWAYS_INLINE int64_t now() const { return _now; }
+	ZT_ALWAYS_INLINE int64_t now() const noexcept { return _now; }
 
 	/**
 	 * Send packet to to the physical wire via callback
@@ -122,7 +122,7 @@ public:
 	 * @param ttl TTL or 0 for default/max
 	 * @return True if send appears successful
 	 */
-	ZT_ALWAYS_INLINE bool putPacket(void *tPtr,const int64_t localSocket,const InetAddress &addr,const void *data,unsigned int len,unsigned int ttl = 0)
+	ZT_ALWAYS_INLINE bool putPacket(void *tPtr,const int64_t localSocket,const InetAddress &addr,const void *data,unsigned int len,unsigned int ttl = 0) noexcept
 	{
 		return (_cb.wirePacketSendFunction(
 			reinterpret_cast<ZT_Node *>(this),
@@ -148,7 +148,7 @@ public:
 	 * @param data Ethernet frame data
 	 * @param len Ethernet frame length in bytes
 	 */
-	ZT_ALWAYS_INLINE void putFrame(void *tPtr,uint64_t nwid,void **nuptr,const MAC &source,const MAC &dest,unsigned int etherType,unsigned int vlanId,const void *data,unsigned int len)
+	ZT_ALWAYS_INLINE void putFrame(void *tPtr,uint64_t nwid,void **nuptr,const MAC &source,const MAC &dest,unsigned int etherType,unsigned int vlanId,const void *data,unsigned int len) noexcept
 	{
 		_cb.virtualNetworkFrameFunction(
 			reinterpret_cast<ZT_Node *>(this),
@@ -168,7 +168,7 @@ public:
 	 * @param nwid Network ID
 	 * @return Network associated with ID
 	 */
-	ZT_ALWAYS_INLINE SharedPtr<Network> network(uint64_t nwid) const
+	ZT_ALWAYS_INLINE SharedPtr<Network> network(uint64_t nwid) const noexcept
 	{
 		RWMutex::RLock l(_networks_m);
 		return _networks[(unsigned long)((nwid + (nwid >> 32U)) & _networksMask)];
@@ -190,7 +190,7 @@ public:
 	 * @param ev Event object
 	 * @param md Event data or NULL if none
 	 */
-	ZT_ALWAYS_INLINE void postEvent(void *tPtr,ZT_Event ev,const void *md = (const void *)0)
+	ZT_ALWAYS_INLINE void postEvent(void *tPtr,ZT_Event ev,const void *md = nullptr) noexcept
 	{
 		_cb.eventCallback(reinterpret_cast<ZT_Node *>(this),_uPtr,tPtr,ev,md);
 	}
@@ -204,7 +204,7 @@ public:
 	 * @param op Config operation or event type
 	 * @param nc Network config info
 	 */
-	ZT_ALWAYS_INLINE void configureVirtualNetworkPort(void *tPtr,uint64_t nwid,void **nuptr,ZT_VirtualNetworkConfigOperation op,const ZT_VirtualNetworkConfig *nc)
+	ZT_ALWAYS_INLINE void configureVirtualNetworkPort(void *tPtr,uint64_t nwid,void **nuptr,ZT_VirtualNetworkConfigOperation op,const ZT_VirtualNetworkConfig *nc) noexcept
 	{
 		_cb.virtualNetworkConfigFunction(reinterpret_cast<ZT_Node *>(this),_uPtr,tPtr,nwid,nuptr,op,nc);
 	}
@@ -212,7 +212,7 @@ public:
 	/**
 	 * @return True if node appears online
 	 */
-	ZT_ALWAYS_INLINE bool online() const { return _online; }
+	ZT_ALWAYS_INLINE bool online() const noexcept { return _online; }
 
 	/**
 	 * Get a state object
@@ -233,7 +233,7 @@ public:
 	 * @param data Data to store
 	 * @param len Length of data
 	 */
-	ZT_ALWAYS_INLINE void stateObjectPut(void *const tPtr,ZT_StateObjectType type,const uint64_t id[2],const void *const data,const unsigned int len)
+	ZT_ALWAYS_INLINE void stateObjectPut(void *const tPtr,ZT_StateObjectType type,const uint64_t id[2],const void *const data,const unsigned int len) noexcept
 	{
 		if (_cb.statePutFunction)
 			_cb.statePutFunction(reinterpret_cast<ZT_Node *>(this),_uPtr,tPtr,type,id,data,(int)len);
@@ -246,7 +246,7 @@ public:
 	 * @param type Object type to delete
 	 * @param id Object ID
 	 */
-	ZT_ALWAYS_INLINE void stateObjectDelete(void *const tPtr,ZT_StateObjectType type,const uint64_t id[2])
+	ZT_ALWAYS_INLINE void stateObjectDelete(void *const tPtr,ZT_StateObjectType type,const uint64_t id[2]) noexcept
 	{
 		if (_cb.statePutFunction)
 			_cb.statePutFunction(reinterpret_cast<ZT_Node *>(this),_uPtr,tPtr,type,id,(const void *)0,-1);
@@ -288,7 +288,7 @@ public:
 	/**
 	 * @return This node's identity
 	 */
-	ZT_ALWAYS_INLINE const Identity &identity() const { return _RR.identity; }
+	ZT_ALWAYS_INLINE const Identity &identity() const noexcept { return _RR.identity; }
 
 	/**
 	 * Register that we are expecting a reply to a packet ID
@@ -299,7 +299,7 @@ public:
 	 *
 	 * @param packetId Packet ID to expect reply to
 	 */
-	ZT_ALWAYS_INLINE void expectReplyTo(const uint64_t packetId)
+	ZT_ALWAYS_INLINE void expectReplyTo(const uint64_t packetId) noexcept
 	{
 		const unsigned long pid2 = (unsigned long)(packetId >> 32U);
 		const unsigned long bucket = (unsigned long)(pid2 & ZT_EXPECTING_REPLIES_BUCKET_MASK1);
@@ -316,7 +316,7 @@ public:
 	 * @param packetId Packet ID to check
 	 * @return True if we're expecting a reply
 	 */
-	ZT_ALWAYS_INLINE bool expectingReplyTo(const uint64_t packetId) const
+	ZT_ALWAYS_INLINE bool expectingReplyTo(const uint64_t packetId) const noexcept
 	{
 		const uint32_t pid2 = (uint32_t)(packetId >> 32);
 		const unsigned long bucket = (unsigned long)(pid2 & ZT_EXPECTING_REPLIES_BUCKET_MASK1);
@@ -330,7 +330,7 @@ public:
 	/**
 	 * @return True if aggressive NAT-traversal mechanisms like scanning of <1024 ports are enabled
 	 */
-	ZT_ALWAYS_INLINE bool natMustDie() const { return _natMustDie; }
+	ZT_ALWAYS_INLINE bool natMustDie() const noexcept { return _natMustDie; }
 
 	/**
 	 * Check whether we should do potentially expensive identity verification (rate limit)
@@ -339,7 +339,7 @@ public:
 	 * @param from Source address of packet
 	 * @return True if within rate limits
 	 */
-	ZT_ALWAYS_INLINE bool rateGateIdentityVerification(const int64_t now,const InetAddress &from)
+	ZT_ALWAYS_INLINE bool rateGateIdentityVerification(const int64_t now,const InetAddress &from) noexcept
 	{
 		unsigned long iph = from.rateGateHash();
 		if ((now - _lastIdentityVerification[iph]) >= ZT_IDENTITY_VALIDATION_SOURCE_RATE_LIMIT) {
@@ -413,10 +413,10 @@ private:
 	struct _LocalControllerAuth
 	{
 		uint64_t nwid,address;
-		ZT_ALWAYS_INLINE _LocalControllerAuth(const uint64_t nwid_,const Address &address_) : nwid(nwid_),address(address_.toInt()) {}
-		ZT_ALWAYS_INLINE unsigned long hashCode() const { return (unsigned long)(nwid ^ address); }
-		ZT_ALWAYS_INLINE bool operator==(const _LocalControllerAuth &a) const { return ((a.nwid == nwid)&&(a.address == address)); }
-		ZT_ALWAYS_INLINE bool operator!=(const _LocalControllerAuth &a) const { return ((a.nwid != nwid)||(a.address != address)); }
+		ZT_ALWAYS_INLINE _LocalControllerAuth(const uint64_t nwid_,const Address &address_)  noexcept: nwid(nwid_),address(address_.toInt()) {}
+		ZT_ALWAYS_INLINE unsigned long hashCode() const noexcept { return (unsigned long)(nwid ^ address); }
+		ZT_ALWAYS_INLINE bool operator==(const _LocalControllerAuth &a) const noexcept { return ((a.nwid == nwid)&&(a.address == address)); }
+		ZT_ALWAYS_INLINE bool operator!=(const _LocalControllerAuth &a) const noexcept { return ((a.nwid != nwid)||(a.address != address)); }
 	};
 	Hashtable< _LocalControllerAuth,int64_t > _localControllerAuthorizations;
 	Mutex _localControllerAuthorizations_m;

+ 0 - 31
node/Path.cpp

@@ -26,35 +26,4 @@ bool Path::send(const RuntimeEnvironment *RR,void *tPtr,const void *data,unsigne
 	return false;
 }
 
-bool Path::isAddressValidForPath(const InetAddress &a) noexcept
-{
-	if ((a.ss_family == AF_INET)||(a.ss_family == AF_INET6)) {
-		switch(a.ipScope()) {
-			/* Note: we don't do link-local at the moment. Unfortunately these
-			 * cause several issues. The first is that they usually require a
-			 * device qualifier, which we don't handle yet and can't portably
-			 * push in PUSH_DIRECT_PATHS. The second is that some OSes assign
-			 * these very ephemerally or otherwise strangely. So we'll use
-			 * private, pseudo-private, shared (e.g. carrier grade NAT), or
-			 * global IP addresses. */
-			case InetAddress::IP_SCOPE_PRIVATE:
-			case InetAddress::IP_SCOPE_PSEUDOPRIVATE:
-			case InetAddress::IP_SCOPE_SHARED:
-			case InetAddress::IP_SCOPE_GLOBAL:
-				if (a.ss_family == AF_INET6) {
-					// TEMPORARY HACK: for now, we are going to blacklist he.net IPv6
-					// tunnels due to very spotty performance and low MTU issues over
-					// these IPv6 tunnel links.
-					const uint8_t *ipd = reinterpret_cast<const uint8_t *>(reinterpret_cast<const struct sockaddr_in6 *>(&a)->sin6_addr.s6_addr);
-					if ((ipd[0] == 0x20)&&(ipd[1] == 0x01)&&(ipd[2] == 0x04)&&(ipd[3] == 0x70))
-						return false;
-				}
-				return true;
-			default:
-				return false;
-		}
-	}
-	return false;
-}
-
 } // namespace ZeroTier

+ 0 - 11
node/Path.hpp

@@ -114,17 +114,6 @@ public:
 	 */
 	ZT_ALWAYS_INLINE int64_t lastOut() const noexcept { return _lastOut; }
 
-	/**
-	 * Check whether this address is valid for a ZeroTier path
-	 *
-	 * This checks the address type and scope against address types and scopes
-	 * that we currently support for ZeroTier communication.
-	 *
-	 * @param a Address to check
-	 * @return True if address is good for ZeroTier path use
-	 */
-	static bool isAddressValidForPath(const InetAddress &a) noexcept;
-
 private:
 	int64_t _localSocket;
 	int64_t _lastIn;

+ 3 - 3
node/Salsa20.cpp

@@ -66,7 +66,7 @@ static const _s20sseconsts _S20SSECONSTANTS;
 
 namespace ZeroTier {
 
-void Salsa20::init(const void *key,const void *iv)
+void Salsa20::init(const void *key,const void *iv) noexcept
 {
 #ifdef ZT_SALSA20_SSE
 	const uint32_t *const k = (const uint32_t *)key;
@@ -108,7 +108,7 @@ void Salsa20::init(const void *key,const void *iv)
 #endif
 }
 
-void Salsa20::crypt12(const void *in,void *out,unsigned int bytes)
+void Salsa20::crypt12(const void *in,void *out,unsigned int bytes) noexcept
 {
 	uint8_t tmp[64];
 	const uint8_t *m = (const uint8_t *)in;
@@ -607,7 +607,7 @@ void Salsa20::crypt12(const void *in,void *out,unsigned int bytes)
 	}
 }
 
-void Salsa20::crypt20(const void *in,void *out,unsigned int bytes)
+void Salsa20::crypt20(const void *in,void *out,unsigned int bytes) noexcept
 {
 	uint8_t tmp[64];
 	const uint8_t *m = (const uint8_t *)in;

+ 5 - 5
node/Salsa20.hpp

@@ -37,14 +37,14 @@ namespace ZeroTier {
 class Salsa20 : public TriviallyCopyable
 {
 public:
-	ZT_ALWAYS_INLINE Salsa20() {}
+	ZT_ALWAYS_INLINE Salsa20() noexcept {}
 	ZT_ALWAYS_INLINE ~Salsa20() { Utils::burn(&_state,sizeof(_state)); }
 
 	/**
 	 * @param key 256-bit (32 byte) key
 	 * @param iv 64-bit initialization vector
 	 */
-	ZT_ALWAYS_INLINE Salsa20(const void *key,const void *iv) { init(key,iv); }
+	ZT_ALWAYS_INLINE Salsa20(const void *key,const void *iv) noexcept { init(key,iv); }
 
 	/**
 	 * Initialize cipher
@@ -52,7 +52,7 @@ public:
 	 * @param key Key bits
 	 * @param iv 64-bit initialization vector
 	 */
-	void init(const void *key,const void *iv);
+	void init(const void *key,const void *iv) noexcept;
 
 	/**
 	 * Encrypt/decrypt data using Salsa20/12
@@ -61,7 +61,7 @@ public:
 	 * @param out Output buffer
 	 * @param bytes Length of data
 	 */
-	void crypt12(const void *in,void *out,unsigned int bytes);
+	void crypt12(const void *in,void *out,unsigned int bytes) noexcept;
 
 	/**
 	 * Encrypt/decrypt data using Salsa20/20
@@ -70,7 +70,7 @@ public:
 	 * @param out Output buffer
 	 * @param bytes Length of data
 	 */
-	void crypt20(const void *in,void *out,unsigned int bytes);
+	void crypt20(const void *in,void *out,unsigned int bytes) noexcept;
 
 private:
 	union {

+ 22 - 1
node/Utils.hpp

@@ -47,7 +47,7 @@ extern CPUIDRegisters CPUID;
 #endif
 
 /**
- * 256 zero bits
+ * 256 zero bits / 32 zero bytes
  */
 extern const uint64_t ZERO256[4];
 
@@ -410,6 +410,13 @@ template<typename T>
 static ZT_ALWAYS_INLINE T ntoh(T n) noexcept { return n; }
 #endif
 
+/**
+ * Decode a big-endian value from a byte stream
+ *
+ * @tparam I Type to decode (should be unsigned e.g. uint32_t or uint64_t)
+ * @param p Byte stream, must be at least sizeof(I) in size
+ * @return Decoded integer
+ */
 template<typename I>
 static ZT_ALWAYS_INLINE I loadBigEndian(const void *const p) noexcept
 {
@@ -428,6 +435,13 @@ static ZT_ALWAYS_INLINE I loadBigEndian(const void *const p) noexcept
 #endif
 }
 
+/**
+ * Copy bits from memory into an integer type without modifying their order
+ *
+ * @tparam I Type to load
+ * @param p Byte stream, must be at least sizeof(I) in size
+ * @return Loaded raw integer
+ */
 template<typename I>
 static ZT_ALWAYS_INLINE I loadAsIsEndian(const void *const p) noexcept
 {
@@ -441,6 +455,13 @@ static ZT_ALWAYS_INLINE I loadAsIsEndian(const void *const p) noexcept
 #endif
 }
 
+/**
+ * Save an integer in big-endian format
+ *
+ * @tparam I Integer type to store (usually inferred)
+ * @param p Byte stream to write (must be at least sizeof(I))
+ * #param i Integer to write
+ */
 template<typename I>
 static ZT_ALWAYS_INLINE void storeBigEndian(void *const p,const I i) noexcept
 {

+ 19 - 16
node/VL1.cpp

@@ -75,33 +75,39 @@ void VL1::onRemotePacket(void *const tPtr,const int64_t localSocket,const InetAd
 			return;
 		}
 
-		// Discard any other runt packets that aren't probes. These are likely to be keepalives or corrupt junk.
+		// Discard any other runt packets that aren't probes. These are likely to be keepalives.
+		// No reason to bother even logging them. Note that the last receive time for the path
+		// was still updated, so tiny keepalives do keep the path alive.
 		if (len < ZT_PROTO_MIN_FRAGMENT_LENGTH)
 			return;
 
+		// A vector of slices of buffers that aspires to eventually hold an assembled packet.
+		// These are reassembled into a single contiguous buffer at the same time as decryption
+		// and authentication.
 		FCV<Buf::Slice,ZT_MAX_PACKET_FRAGMENTS> pktv;
+
+		// Destination address of packet (filled below)
 		Address destination;
 
 		if (data->b[ZT_PROTO_PACKET_FRAGMENT_INDICATOR_INDEX] == ZT_PROTO_PACKET_FRAGMENT_INDICATOR) {
 			// Fragment -----------------------------------------------------------------------------------------------------
 
-			const Protocol::FragmentHeader &fh = data->as<Protocol::FragmentHeader>();
-			destination.setTo(fh.destination);
+			const Protocol::FragmentHeader &fragmentHeader = data->as<Protocol::FragmentHeader>();
+			destination.setTo(fragmentHeader.destination);
 
 			if (destination != RR->identity.address()) {
-				// Fragment is not address to this node -----------------------------------------------------------------------
 				_relay(tPtr,path,destination,data,len);
 				return;
 			}
 
 			switch (_inputPacketAssembler.assemble(
-				fh.packetId,
+				fragmentHeader.packetId,
 				pktv,
 				data,
 				ZT_PROTO_PACKET_FRAGMENT_PAYLOAD_START_AT,
 				(unsigned int)(len - ZT_PROTO_PACKET_FRAGMENT_PAYLOAD_START_AT),
-				fh.counts & 0xfU, // fragment number
-				fh.counts >> 4U,  // total number of fragments in message is specified in each fragment
+				fragmentHeader.counts & 0xfU, // fragment number
+				fragmentHeader.counts >> 4U,  // total number of fragments in message is specified in each fragment
 				now,
 				path,
 				ZT_MAX_INCOMING_FRAGMENTS_PER_PATH)) {
@@ -120,19 +126,17 @@ void VL1::onRemotePacket(void *const tPtr,const int64_t localSocket,const InetAd
 
 			if (len < ZT_PROTO_MIN_PACKET_LENGTH)
 				return;
-			const Protocol::Header &ph = data->as<Protocol::Header>();
-			destination.setTo(ph.destination);
+			const Protocol::Header &packetHeader = data->as<Protocol::Header>();
+			destination.setTo(packetHeader.destination);
 
 			if (destination != RR->identity.address()) {
-				// Packet or packet head is not addressed to this node --------------------------------------------------------
 				_relay(tPtr,path,destination,data,len);
 				return;
 			}
 
-			if ((ph.flags & ZT_PROTO_FLAG_FRAGMENTED) != 0) {
-				// Head of fragmented packet ----------------------------------------------------------------------------------
+			if ((packetHeader.flags & ZT_PROTO_FLAG_FRAGMENTED) != 0) {
 				switch (_inputPacketAssembler.assemble(
-					ph.packetId,
+					packetHeader.packetId,
 					pktv,
 					data,
 					0,
@@ -152,10 +156,9 @@ void VL1::onRemotePacket(void *const tPtr,const int64_t localSocket,const InetAd
 						//case Defragmenter<ZT_MAX_PACKET_FRAGMENTS>::ERR_OUT_OF_MEMORY:
 						return;
 				}
-			} else {
-				// Unfragmented packet, skip defrag engine and just handle it -------------------------------------------------
+			} else { // packet isn't fragmented, so skip the Defragmenter logic completely.
 				Buf::Slice &s = pktv.push();
-				s.b = data;
+				s.b.swap(data);
 				s.s = 0;
 				s.e = len;
 			}

+ 15 - 4
node/VL1.hpp

@@ -32,7 +32,9 @@ class Peer;
 class VL2;
 
 /**
- * VL1 (virtual layer 1) packet I/O and messaging
+ * VL1 (virtual layer 1) packet I/O and messaging.
+ *
+ * This class is thread safe.
  */
 class VL1
 {
@@ -43,6 +45,12 @@ public:
 	/**
 	 * Called when a packet is received from the real network
 	 *
+	 * The packet data supplied to this method may be modified. Internal
+	 * packet handler code may also take possession of it via atomic swap
+	 * and leave the 'data' pointer NULL. The 'data' pointer and its
+	 * contents should not be used after this call. Make a copy if the
+	 * data might still be needed.
+	 *
 	 * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
 	 * @param localSocket Local I/O socket as supplied by external code
 	 * @param fromAddr Internet IP address of origin
@@ -52,10 +60,15 @@ public:
 	void onRemotePacket(void *tPtr,int64_t localSocket,const InetAddress &fromAddr,SharedPtr<Buf> &data,unsigned int len);
 
 private:
+	const RuntimeEnvironment *RR;
+
+	// Code to handle relaying of packets to other nodes.
 	void _relay(void *tPtr,const SharedPtr<Path> &path,const Address &destination,SharedPtr<Buf> &data,unsigned int len);
+
+	// Send any pending WHOIS requests.
 	void _sendPendingWhois(void *tPtr,int64_t now);
 
-	// Handlers for VL1 verbs
+	// Handlers for VL1 verbs -- for clarity's sake VL2 verbs are in the VL2 class.
 	bool _HELLO(void *tPtr,const SharedPtr<Path> &path,SharedPtr<Peer> &peer,Buf &pkt,int packetSize,bool authenticated);
 	bool _ERROR(void *tPtr,const SharedPtr<Path> &path,const SharedPtr<Peer> &peer,Buf &pkt,int packetSize);
 	bool _OK(void *tPtr,const SharedPtr<Path> &path,const SharedPtr<Peer> &peer,Buf &pkt,int packetSize);
@@ -66,8 +79,6 @@ private:
 	bool _USER_MESSAGE(void *tPtr,const SharedPtr<Path> &path,const SharedPtr<Peer> &peer,Buf &pkt,int packetSize);
 	bool _ENCAP(void *tPtr,const SharedPtr<Path> &path,const SharedPtr<Peer> &peer,Buf &pkt,int packetSize);
 
-	const RuntimeEnvironment *RR;
-
 	struct _WhoisQueueItem
 	{
 		ZT_ALWAYS_INLINE _WhoisQueueItem() : lastRetry(0),inboundPackets(),retries(0) {}

Some files were not shown because too many files changed in this diff