diff --git a/node/AES.cpp b/node/AES.cpp index 7313bfaa4..672265209 100644 --- a/node/AES.cpp +++ b/node/AES.cpp @@ -535,7 +535,7 @@ void AES::CTR::crypt(const void *const input,unsigned int len) noexcept out += totalLen; _len = (totalLen + len); - if (likely((c1 + len) > c1)) { // it's incredibly likely that we can ignore carry in counter increment + if (likely((c1 + len) > c1)) { // if this is true we can just increment c1 and ignore c0 while (len >= 64) { __m128i d0 = _mm_set_epi64x((long long)Utils::hton(c1),(long long)c0); __m128i d1 = _mm_set_epi64x((long long)Utils::hton(c1 + 1ULL),(long long)c0); @@ -663,7 +663,7 @@ void AES::CTR::crypt(const void *const input,unsigned int len) noexcept out += 16; } while (len >= 16); } - } else { + } else { // in the unlikely case c1 is near uint64_max, we must add with carry while (len >= 64) { __m128i d0 = _mm_set_epi64x((long long)Utils::hton(c1++),(long long)c0); if (unlikely(c1 == 0ULL)) c0 = Utils::hton(Utils::ntoh(c0) + 1ULL); diff --git a/node/Defragmenter.hpp b/node/Defragmenter.hpp index a186e56f2..39efe3358 100644 --- a/node/Defragmenter.hpp +++ b/node/Defragmenter.hpp @@ -43,7 +43,7 @@ namespace ZeroTier { * @tparam GCS Garbage collection target size for the incoming message queue * @tparam GCT Garbage collection trigger threshold, usually 2X GCS */ -template +template class Defragmenter { public: @@ -248,7 +248,7 @@ public: s.e = fragmentDataIndex + fragmentDataSize; // If we now have all fragments then assemble them. - if ((e->message.size() >= e->totalFragmentsExpected)&&(e->totalFragmentsExpected > 0)) { + if ((++e->fragmentsReceived >= e->totalFragmentsExpected)&&(e->totalFragmentsExpected > 0)) { // This message is done so de-register it with its path if one is associated. if (e->via) { e->via->_inboundFragmentedMessages_l.lock(); @@ -277,19 +277,31 @@ public: _messages.clear(); } + /** + * @return Number of entries currently in message defragmentation cache + */ + ZT_ALWAYS_INLINE unsigned int cacheSize() noexcept + { + RWMutex::RLock ml(_messages_l); + return _messages.size(); + } + private: struct _E { - ZT_ALWAYS_INLINE _E() : id(0),lastUsed(0),totalFragmentsExpected(0),via(),message(),lock() {} + ZT_ALWAYS_INLINE _E() : id(0),lastUsed(0),totalFragmentsExpected(0),fragmentsReceived(0),via(),message(),lock() {} ZT_ALWAYS_INLINE ~_E() { - via->_inboundFragmentedMessages_l.lock(); - via->_inboundFragmentedMessages.erase(id); - via->_inboundFragmentedMessages_l.unlock(); + if (via) { + via->_inboundFragmentedMessages_l.lock(); + via->_inboundFragmentedMessages.erase(id); + via->_inboundFragmentedMessages_l.unlock(); + } } uint64_t id; volatile int64_t lastUsed; unsigned int totalFragmentsExpected; + unsigned int fragmentsReceived; SharedPtr via; FCV< Buf::Slice,MF > message; Mutex lock; diff --git a/node/Protocol.cpp b/node/Protocol.cpp index 78d3af2f3..8d6a33988 100644 --- a/node/Protocol.cpp +++ b/node/Protocol.cpp @@ -16,6 +16,13 @@ #include "Utils.hpp" #include +#include + +#ifdef __WINDOWS__ +#include +#else +#include +#endif namespace ZeroTier { namespace Protocol { @@ -30,11 +37,7 @@ uint64_t createProbe(const Identity &sender,const Identity &recipient,const uint return hash[0]; } -uint64_t getPacketId() noexcept -{ - static std::atomic s_packetIdCtr(Utils::getSecureRandomU64()); - return ++s_packetIdCtr; -} +std::atomic _s_packetIdCtr((uint64_t)time(nullptr) << 32U); void armor(Buf &pkt,int packetSize,const uint8_t key[ZT_PEER_SECRET_KEY_LENGTH],uint8_t cipherSuite) noexcept { diff --git a/node/Protocol.hpp b/node/Protocol.hpp index f4c257674..0eeda037e 100644 --- a/node/Protocol.hpp +++ b/node/Protocol.hpp @@ -1048,12 +1048,15 @@ static ZT_ALWAYS_INLINE void salsa2012DeriveKey(const uint8_t *const in,uint8_t */ uint64_t createProbe(const Identity &sender,const Identity &recipient,const uint8_t key[ZT_PEER_SECRET_KEY_LENGTH]) noexcept; +// Do not use directly +extern std::atomic _s_packetIdCtr; + /** - * Get a sequential non-repeating packet ID for the next packet (thread-safe) + * Get a packet ID (and nonce) for a new packet * - * @return Next packet ID / cryptographic nonce + * @return Next packet ID */ -uint64_t getPacketId() noexcept; +static ZT_ALWAYS_INLINE uint64_t getPacketId() noexcept { return ++_s_packetIdCtr; } /** * Encrypt and compute packet MAC diff --git a/node/Tests.cpp b/node/Tests.cpp index 8e9e9dd1e..eac2aabed 100644 --- a/node/Tests.cpp +++ b/node/Tests.cpp @@ -465,6 +465,96 @@ extern "C" const char *ZTT_general() } ZT_T_PRINTF("OK" ZT_EOL_S); } + + { + // This doesn't check behavior when fragments are invalid or input is totally insane. + // That's done during fuzzing. + ZT_T_PRINTF("[general] Testing Defragmenter... "); + Defragmenter<> defrag; + + const SharedPtr nullvia; + uint64_t messageId = 0; + int64_t ts = now(); + for(int k=0;k<50000;++k) { + ++messageId; + FCV message; + FCV ref; + + int frags = 1 + (int)(Utils::random() % 16); + int skip = ((k & 3) == 1) ? -1 : (int)(Utils::random() % frags); + bool complete = false; + message.resize(frags); + ref.resize(frags); + + for (int f=0;funsafeData[i] = (uint8_t)f; + } + } + + for (int f=0;f::OK: + break; + case Defragmenter<>::COMPLETE: + complete = true; + break; + case Defragmenter<>::ERR_DUPLICATE_FRAGMENT: + break; + case Defragmenter<>::ERR_INVALID_FRAGMENT: + ZT_T_PRINTF("FAILED (invalid fragment)" ZT_EOL_S); + return "Defragmenter test failed: invalid fragment"; + case Defragmenter<>::ERR_TOO_MANY_FRAGMENTS_FOR_PATH: + break; + case Defragmenter<>::ERR_OUT_OF_MEMORY: + ZT_T_PRINTF("FAILED (out of memory)" ZT_EOL_S); + return "Defragmenter test failed: out of memory"; + } + } + } + + if (skip == -1) { + if (complete) { + for(int f=0;funsafeData[i] != (uint8_t)f) { + ZT_T_PRINTF("FAILED (fragment %d data invalid (raw index %u: %d != %d))" ZT_EOL_S,f,i,(int)message[f].b->unsafeData[i],f); + return "Defragmenter test failed: fragment data invalid"; + } + } + } + } else { + ZT_T_PRINTF("FAILED (message incomplete after all fragments)" ZT_EOL_S); + return "Defragmenter test failed: message incomplete after all fragments"; + } + } else { + if (complete) { + ZT_T_PRINTF("FAILED (message completed without all fragments)" ZT_EOL_S); + return "Defragmenter test failed: message completed without all fragments"; + } + } + } + + Buf::freePool(); + ZT_T_PRINTF("OK (cache remaining: %u)" ZT_EOL_S,defrag.cacheSize()); + } } catch (std::exception &e) { ZT_T_PRINTF(ZT_EOL_S "[general] Unexpected exception: %s" ZT_EOL_S,e.what()); return e.what(); @@ -702,35 +792,6 @@ extern "C" const char *ZTT_crypto() return nullptr; } -extern "C" const char *ZTT_defragmenter() -{ -#if 0 - Defragmenter<11> defrag; - -/* - ZT_ALWAYS_INLINE ResultCode assemble( - const uint64_t messageId, - FCV< Buf::Slice,MF > &message, - SharedPtr &fragment, - const unsigned int fragmentDataIndex, - const unsigned int fragmentDataSize, - const unsigned int fragmentNo, - const unsigned int totalFragmentsExpected, - const int64_t now, - const SharedPtr< Path > &via, - const unsigned int maxIncomingFragmentsPerPath) - { -*/ - - uint64_t messageId = 1; - FCV< Buf::Slice,11 > message; - for(int kk=0;kk<16;++kk) { - } - -#endif - return nullptr; -} - extern "C" const char *ZTT_benchmarkCrypto() { try { diff --git a/node/Tests.h b/node/Tests.h index 9f8a6f500..f30c17b04 100644 --- a/node/Tests.h +++ b/node/Tests.h @@ -56,7 +56,6 @@ extern "C" { const char *ZTT_general(); const char *ZTT_crypto(); -const char *ZTT_defragmenter(); // Benchmarks ---------------------------------------------------------------------------------------------------------