diff --git a/node/Path.hpp b/node/Path.hpp index a9e7d84df..1c3e5d3cc 100644 --- a/node/Path.hpp +++ b/node/Path.hpp @@ -123,7 +123,8 @@ public: _lastComputedThroughputDistCoeff(0.0), _lastAllocation(0) { - prepareBuffers(); + memset(_ifname, 0, 16); + memset(_addrString, 0, sizeof(_addrString)); } Path(const int64_t localSocket,const InetAddress &addr) : @@ -155,22 +156,11 @@ public: _lastComputedThroughputDistCoeff(0.0), _lastAllocation(0) { - prepareBuffers(); + memset(_ifname, 0, 16); + memset(_addrString, 0, sizeof(_addrString)); _phy->getIfName((PhySocket *)((uintptr_t)_localSocket), _ifname, 16); } - ~Path() - { - delete _throughputSamples; - delete _latencySamples; - delete _packetValiditySamples; - delete _throughputDisturbanceSamples; - _throughputSamples = NULL; - _latencySamples = NULL; - _packetValiditySamples = NULL; - _throughputDisturbanceSamples = NULL; - } - /** * Called when a packet is received from this remote path, regardless of content * @@ -216,7 +206,7 @@ public: else { _latency = l; } - _latencySamples->push(l); + _latencySamples.push(l); } /** @@ -345,7 +335,7 @@ public: _inQoSRecords[packetId] = now; _packetsReceivedSinceLastQoS++; } - _packetValiditySamples->push(true); + _packetValiditySamples.push(true); } } @@ -362,7 +352,7 @@ public: int64_t timeSinceThroughputEstimate = (now - _lastThroughputEstimation); if (timeSinceThroughputEstimate >= ZT_PATH_THROUGHPUT_MEASUREMENT_INTERVAL) { uint64_t throughput = (float)(_bytesAckedSinceLastThroughputEstimation * 8) / ((float)timeSinceThroughputEstimate / (float)1000); - _throughputSamples->push(throughput); + _throughputSamples.push(throughput); _maxLifetimeThroughput = throughput > _maxLifetimeThroughput ? throughput : _maxLifetimeThroughput; _lastThroughputEstimation = now; _bytesAckedSinceLastThroughputEstimation = 0; @@ -564,7 +554,7 @@ public: * Record an invalid incoming packet. This packet failed MAC/compression/cipher checks and will now * contribute to a Packet Error Ratio (PER). */ - inline void recordInvalidPacket() { _packetValiditySamples->push(false); } + inline void recordInvalidPacket() { _packetValiditySamples.push(false); } /** * @return A pointer to a cached copy of the address string for this Path (For debugging only) @@ -582,35 +572,43 @@ public: * * @param now Current time */ - inline void processBackgroundPathMeasurements(int64_t now) { + inline void processBackgroundPathMeasurements(const int64_t now) + { if (now - _lastPathQualityComputeTime > ZT_PATH_QUALITY_COMPUTE_INTERVAL) { Mutex::Lock _l(_statistics_m); _lastPathQualityComputeTime = now; address().toString(_addrString); - _lastComputedMeanLatency = _latencySamples->mean(); - _lastComputedPacketDelayVariance = _latencySamples->stddev(); // Similar to "jitter" (SEE: RFC 3393, RFC 4689) - _lastComputedMeanThroughput = (uint64_t)_throughputSamples->mean(); + _lastComputedMeanLatency = _latencySamples.mean(); + _lastComputedPacketDelayVariance = _latencySamples.stddev(); // Similar to "jitter" (SEE: RFC 3393, RFC 4689) + _lastComputedMeanThroughput = (uint64_t)_throughputSamples.mean(); + // If no packet validity samples, assume PER==0 - _lastComputedPacketErrorRatio = 1 - (_packetValiditySamples->count() ? _packetValiditySamples->mean() : 1); + _lastComputedPacketErrorRatio = 1 - (_packetValiditySamples.count() ? _packetValiditySamples.mean() : 1); + // Compute path stability // Normalize measurements with wildly different ranges into a reasonable range float normalized_pdv = Utils::normalize(_lastComputedPacketDelayVariance, 0, ZT_PATH_MAX_PDV, 0, 10); float normalized_la = Utils::normalize(_lastComputedMeanLatency, 0, ZT_PATH_MAX_MEAN_LATENCY, 0, 10); - float throughput_cv = _throughputSamples->mean() > 0 ? _throughputSamples->stddev() / _throughputSamples->mean() : 1; + float throughput_cv = _throughputSamples.mean() > 0 ? _throughputSamples.stddev() / _throughputSamples.mean() : 1; + // Form an exponential cutoff and apply contribution weights float pdv_contrib = exp((-1)*normalized_pdv) * ZT_PATH_CONTRIB_PDV; float latency_contrib = exp((-1)*normalized_la) * ZT_PATH_CONTRIB_LATENCY; + // Throughput Disturbance Coefficient float throughput_disturbance_contrib = exp((-1)*throughput_cv) * ZT_PATH_CONTRIB_THROUGHPUT_DISTURBANCE; - _throughputDisturbanceSamples->push(throughput_cv); - _lastComputedThroughputDistCoeff = _throughputDisturbanceSamples->mean(); + _throughputDisturbanceSamples.push(throughput_cv); + _lastComputedThroughputDistCoeff = _throughputDisturbanceSamples.mean(); + // Obey user-defined ignored contributions pdv_contrib = ZT_PATH_CONTRIB_PDV > 0.0 ? pdv_contrib : 1; latency_contrib = ZT_PATH_CONTRIB_LATENCY > 0.0 ? latency_contrib : 1; throughput_disturbance_contrib = ZT_PATH_CONTRIB_THROUGHPUT_DISTURBANCE > 0.0 ? throughput_disturbance_contrib : 1; + // Stability _lastComputedStability = pdv_contrib + latency_contrib + throughput_disturbance_contrib; _lastComputedStability *= 1 - _lastComputedPacketErrorRatio; + // Prevent QoS records from sticking around for too long std::map::iterator it = _outQoSRecords.begin(); while (it != _outQoSRecords.end()) { @@ -647,18 +645,6 @@ public: */ inline int64_t lastTrustEstablishedPacketReceived() const { return _lastTrustEstablishedPacketReceived; } - /** - * Initialize statistical buffers - */ - inline void prepareBuffers() { - _throughputSamples = new RingBuffer(ZT_PATH_QUALITY_METRIC_WIN_SZ); - _latencySamples = new RingBuffer(ZT_PATH_QUALITY_METRIC_WIN_SZ); - _packetValiditySamples = new RingBuffer(ZT_PATH_QUALITY_METRIC_WIN_SZ); - _throughputDisturbanceSamples = new RingBuffer(ZT_PATH_QUALITY_METRIC_WIN_SZ); - memset(_ifname, 0, 16); - memset(_addrString, 0, sizeof(_addrString)); - } - private: Mutex _statistics_m; @@ -672,9 +658,9 @@ private: InetAddress::IpScope _ipScope; // memoize this since it's a computed value checked often AtomicCounter __refCount; - std::map _outQoSRecords; // id:egress_time - std::map _inQoSRecords; // id:now - std::map _inACKRecords; // id:len + std::map _outQoSRecords; // id:egress_time + std::map _inQoSRecords; // id:now + std::map _inACKRecords; // id:len int64_t _lastAck; int64_t _lastThroughputEstimation; @@ -702,16 +688,14 @@ private: float _lastComputedThroughputDistCoeff; unsigned char _lastAllocation; - - // cached human-readable strings for tracing purposes char _ifname[16]; char _addrString[256]; - RingBuffer *_throughputSamples; - RingBuffer *_latencySamples; - RingBuffer *_packetValiditySamples; - RingBuffer *_throughputDisturbanceSamples; + RingBuffer _throughputSamples; + RingBuffer _latencySamples; + RingBuffer _packetValiditySamples; + RingBuffer _throughputDisturbanceSamples; }; } // namespace ZeroTier diff --git a/node/Peer.cpp b/node/Peer.cpp index ba8918bc3..2ab186c3e 100644 --- a/node/Peer.cpp +++ b/node/Peer.cpp @@ -76,7 +76,6 @@ Peer::Peer(const RuntimeEnvironment *renv,const Identity &myIdentity,const Ident Utils::getSecureRandom(&_freeRandomByte, 1); if (!myIdentity.agree(peerIdentity,_key,ZT_PEER_SECRET_KEY_LENGTH)) throw ZT_EXCEPTION_INVALID_ARGUMENT; - _pathChoiceHist = new RingBuffer(ZT_MULTIPATH_PROPORTION_WIN_SZ); } void Peer::received( @@ -471,7 +470,7 @@ SharedPtr Peer::getAppropriatePath(int64_t now, bool includeExpired) if (_paths[i].p) { if (rf < _paths[i].p->allocation()) { bestPath = i; - _pathChoiceHist->push(bestPath); // Record which path we chose + _pathChoiceHist.push(bestPath); // Record which path we chose break; } rf -= _paths[i].p->allocation(); @@ -500,7 +499,7 @@ char *Peer::interfaceListStr() float targetAllocation = 1.0 / alivePathCount; float currentAllocation = 1.0; if (alivePathCount > 1) { - currentAllocation = (float)_pathChoiceHist->countValue(i) / (float)_pathChoiceHist->count(); + currentAllocation = (float)_pathChoiceHist.countValue(i) / (float)_pathChoiceHist.count(); if (fabs(targetAllocation - currentAllocation) > ZT_PATH_IMBALANCE_THRESHOLD) { imbalanced = true; } diff --git a/node/Peer.hpp b/node/Peer.hpp index 9afae56a0..947d88618 100644 --- a/node/Peer.hpp +++ b/node/Peer.hpp @@ -60,11 +60,7 @@ private: Peer() {} // disabled to prevent bugs -- should not be constructed uninitialized public: - ~Peer() { - Utils::burn(_key,sizeof(_key)); - delete _pathChoiceHist; - _pathChoiceHist = NULL; - } + ~Peer() { Utils::burn(_key,sizeof(_key)); } /** * Construct a new peer @@ -674,7 +670,7 @@ private: AtomicCounter __refCount; - RingBuffer *_pathChoiceHist; + RingBuffer _pathChoiceHist; bool _linkIsBalanced; bool _linkIsRedundant; diff --git a/node/Poly1305.cpp b/node/Poly1305.cpp index eceb57b3f..c670e5d05 100644 --- a/node/Poly1305.cpp +++ b/node/Poly1305.cpp @@ -18,115 +18,6 @@ Public domain. namespace ZeroTier { -#if 0 - -// "Naive" implementation, which is slower... might still want this on some older -// or weird platforms if the later versions have issues. - -static inline void add(unsigned int h[17],const unsigned int c[17]) -{ - unsigned int j; - unsigned int u; - u = 0; - for (j = 0;j < 17;++j) { u += h[j] + c[j]; h[j] = u & 255; u >>= 8; } -} - -static inline void squeeze(unsigned int h[17]) -{ - unsigned int j; - unsigned int u; - u = 0; - for (j = 0;j < 16;++j) { u += h[j]; h[j] = u & 255; u >>= 8; } - u += h[16]; h[16] = u & 3; - u = 5 * (u >> 2); - for (j = 0;j < 16;++j) { u += h[j]; h[j] = u & 255; u >>= 8; } - u += h[16]; h[16] = u; -} - -static const unsigned int minusp[17] = { - 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 252 -} ; - -static inline void freeze(unsigned int h[17]) -{ - unsigned int horig[17]; - unsigned int j; - unsigned int negative; - for (j = 0;j < 17;++j) horig[j] = h[j]; - add(h,minusp); - negative = -(h[16] >> 7); - for (j = 0;j < 17;++j) h[j] ^= negative & (horig[j] ^ h[j]); -} - -static inline void mulmod(unsigned int h[17],const unsigned int r[17]) -{ - unsigned int hr[17]; - unsigned int i; - unsigned int j; - unsigned int u; - - for (i = 0;i < 17;++i) { - u = 0; - for (j = 0;j <= i;++j) u += h[j] * r[i - j]; - for (j = i + 1;j < 17;++j) u += 320 * h[j] * r[i + 17 - j]; - hr[i] = u; - } - for (i = 0;i < 17;++i) h[i] = hr[i]; - squeeze(h); -} - -static inline int crypto_onetimeauth(unsigned char *out,const unsigned char *in,unsigned long long inlen,const unsigned char *k) -{ - unsigned int j; - unsigned int r[17]; - unsigned int h[17]; - unsigned int c[17]; - - r[0] = k[0]; - r[1] = k[1]; - r[2] = k[2]; - r[3] = k[3] & 15; - r[4] = k[4] & 252; - r[5] = k[5]; - r[6] = k[6]; - r[7] = k[7] & 15; - r[8] = k[8] & 252; - r[9] = k[9]; - r[10] = k[10]; - r[11] = k[11] & 15; - r[12] = k[12] & 252; - r[13] = k[13]; - r[14] = k[14]; - r[15] = k[15] & 15; - r[16] = 0; - - for (j = 0;j < 17;++j) h[j] = 0; - - while (inlen > 0) { - for (j = 0;j < 17;++j) c[j] = 0; - for (j = 0;(j < 16) && (j < inlen);++j) c[j] = in[j]; - c[j] = 1; - in += j; inlen -= j; - add(h,c); - mulmod(h,r); - } - - freeze(h); - - for (j = 0;j < 16;++j) c[j] = k[j + 16]; - c[16] = 0; - add(h,c); - for (j = 0;j < 16;++j) out[j] = h[j]; - return 0; -} - -void Poly1305::compute(void *auth,const void *data,unsigned int len,const void *key) -{ - crypto_onetimeauth((unsigned char *)auth,(const unsigned char *)data,len,(const unsigned char *)key); -} - -#endif - namespace { typedef struct poly1305_context { @@ -215,8 +106,7 @@ static inline void U64TO8(unsigned char *p, unsigned long long v) #define U64TO8(p,v) ((*reinterpret_cast(p)) = (v)) #endif -static inline void -poly1305_init(poly1305_context *ctx, const unsigned char key[32]) { +static inline void poly1305_init(poly1305_context *ctx, const unsigned char key[32]) { poly1305_state_internal_t *st = (poly1305_state_internal_t *)ctx; unsigned long long t0,t1; @@ -241,8 +131,7 @@ poly1305_init(poly1305_context *ctx, const unsigned char key[32]) { st->final = 0; } -static inline void -poly1305_blocks(poly1305_state_internal_t *st, const unsigned char *m, size_t bytes) { +static inline void poly1305_blocks(poly1305_state_internal_t *st, const unsigned char *m, size_t bytes) { const unsigned long long hibit = (st->final) ? 0 : ((unsigned long long)1 << 40); /* 1 << 128 */ unsigned long long r0,r1,r2; unsigned long long s1,s2; @@ -293,8 +182,7 @@ poly1305_blocks(poly1305_state_internal_t *st, const unsigned char *m, size_t by st->h[2] = h2; } -static inline void -poly1305_finish(poly1305_context *ctx, unsigned char mac[16]) { +static inline void poly1305_finish(poly1305_context *ctx, unsigned char mac[16]) { poly1305_state_internal_t *st = (poly1305_state_internal_t *)ctx; unsigned long long h0,h1,h2,c; unsigned long long g0,g1,g2; @@ -582,8 +470,7 @@ poly1305_finish(poly1305_context *ctx, unsigned char mac[16]) { #endif // MSC/GCC or not -static inline void -poly1305_update(poly1305_context *ctx, const unsigned char *m, size_t bytes) { +static inline void poly1305_update(poly1305_context *ctx, const unsigned char *m, size_t bytes) { poly1305_state_internal_t *st = (poly1305_state_internal_t *)ctx; size_t i; diff --git a/node/RingBuffer.hpp b/node/RingBuffer.hpp index 0d90152ba..e9f17e1c2 100644 --- a/node/RingBuffer.hpp +++ b/node/RingBuffer.hpp @@ -47,40 +47,28 @@ namespace ZeroTier { * to reduce the complexity of code needed to interact with this type of buffer. */ -template +template class RingBuffer { private: - T * buf; - size_t size; + T buf[S]; size_t begin; size_t end; bool wrap; public: - - /** - * create a RingBuffer with space for up to size elements. - */ - explicit RingBuffer(size_t size) - : size(size), + RingBuffer() : begin(0), end(0), wrap(false) { - buf = new T[size]; - memset(buf, 0, sizeof(T) * size); - } - - ~RingBuffer() - { - delete [] buf; + memset(buf,0,sizeof(T)*S); } /** * @return A pointer to the underlying buffer */ - T* get_buf() + inline T *get_buf() { return buf + begin; } @@ -90,17 +78,17 @@ public: * @param n Number of elements to copy in * @return Number of elements we copied in */ - size_t produce(size_t n) + inline size_t produce(size_t n) { n = std::min(n, getFree()); if (n == 0) { return n; } - const size_t first_chunk = std::min(n, size - end); - end = (end + first_chunk) % size; + const size_t first_chunk = std::min(n, S - end); + end = (end + first_chunk) % S; if (first_chunk < n) { const size_t second_chunk = n - first_chunk; - end = (end + second_chunk) % size; + end = (end + second_chunk) % S; } if (begin == end) { wrap = true; @@ -112,17 +100,14 @@ public: * Fast erase, O(1). * Merely reset the buffer pointer, doesn't erase contents */ - void reset() - { - consume(count()); - } + inline void reset() { consume(count()); } /** * adjust buffer index pointer as if we copied data out * @param n Number of elements we copied from the buffer * @return Number of elements actually available from the buffer */ - size_t consume(size_t n) + inline size_t consume(size_t n) { n = std::min(n, count()); if (n == 0) { @@ -131,11 +116,11 @@ public: if (wrap) { wrap = false; } - const size_t first_chunk = std::min(n, size - begin); - begin = (begin + first_chunk) % size; + const size_t first_chunk = std::min(n, S - begin); + begin = (begin + first_chunk) % S; if (first_chunk < n) { const size_t second_chunk = n - first_chunk; - begin = (begin + second_chunk) % size; + begin = (begin + second_chunk) % S; } return n; } @@ -144,19 +129,19 @@ public: * @param data Buffer that is to be written to the ring * @param n Number of elements to write to the buffer */ - size_t write(const T * data, size_t n) + inline size_t write(const T * data, size_t n) { n = std::min(n, getFree()); if (n == 0) { return n; } - const size_t first_chunk = std::min(n, size - end); + const size_t first_chunk = std::min(n, S - end); memcpy(buf + end, data, first_chunk * sizeof(T)); - end = (end + first_chunk) % size; + end = (end + first_chunk) % S; if (first_chunk < n) { const size_t second_chunk = n - first_chunk; memcpy(buf + end, data + first_chunk, second_chunk * sizeof(T)); - end = (end + second_chunk) % size; + end = (end + second_chunk) % S; } if (begin == end) { wrap = true; @@ -169,14 +154,14 @@ public: * * @param value A single value to be placed in the buffer */ - void push(const T value) + inline void push(const T value) { - if (count() == size) { + if (count() == S) { consume(1); } - const size_t first_chunk = std::min((size_t)1, size - end); + const size_t first_chunk = std::min((size_t)1, S - end); *(buf + end) = value; - end = (end + first_chunk) % size; + end = (end + first_chunk) % S; if (begin == end) { wrap = true; } @@ -185,14 +170,14 @@ public: /** * @return The most recently pushed element on the buffer */ - T get_most_recent() { return *(buf + end); } + inline T get_most_recent() { return *(buf + end); } /** * @param dest Destination buffer * @param n Size (in terms of number of elements) of the destination buffer * @return Number of elements read from the buffer */ - size_t read(T * dest, size_t n) + inline size_t read(T *dest,size_t n) { n = std::min(n, count()); if (n == 0) { @@ -201,13 +186,13 @@ public: if (wrap) { wrap = false; } - const size_t first_chunk = std::min(n, size - begin); + const size_t first_chunk = std::min(n, S - begin); memcpy(dest, buf + begin, first_chunk * sizeof(T)); - begin = (begin + first_chunk) % size; + begin = (begin + first_chunk) % S; if (first_chunk < n) { const size_t second_chunk = n - first_chunk; memcpy(dest + first_chunk, buf + begin, second_chunk * sizeof(T)); - begin = (begin + second_chunk) % size; + begin = (begin + second_chunk) % S; } return n; } @@ -217,34 +202,34 @@ public: * * @return The number of elements in the buffer */ - size_t count() + inline size_t count() { if (end == begin) { - return wrap ? size : 0; + return wrap ? S : 0; } else if (end > begin) { return end - begin; } else { - return size + end - begin; + return S + end - begin; } } /** * @return The number of slots that are unused in the buffer */ - size_t getFree() { return size - count(); } + inline size_t getFree() { return S - count(); } /** * @return The arithmetic mean of the contents of the buffer */ - float mean() + inline float mean() { size_t iterator = begin; float subtotal = 0; size_t curr_cnt = count(); for (size_t i=0; i