summaryrefslogtreecommitdiff
path: root/node
diff options
context:
space:
mode:
Diffstat (limited to 'node')
-rw-r--r--node/Address.hpp102
-rw-r--r--node/Array.hpp107
-rw-r--r--node/AtomicCounter.hpp69
-rw-r--r--node/BinarySemaphore.hpp97
-rw-r--r--node/Buffer.hpp207
-rw-r--r--node/C25519.cpp2416
-rw-r--r--node/C25519.hpp73
-rw-r--r--node/Capability.cpp74
-rw-r--r--node/Capability.hpp493
-rw-r--r--node/CertificateOfMembership.cpp59
-rw-r--r--node/CertificateOfMembership.hpp170
-rw-r--r--node/CertificateOfOwnership.cpp72
-rw-r--r--node/CertificateOfOwnership.hpp247
-rw-r--r--node/Cluster.cpp913
-rw-r--r--node/Cluster.hpp406
-rw-r--r--node/Constants.hpp225
-rw-r--r--node/Credential.hpp65
-rw-r--r--node/DeferredPackets.cpp100
-rw-r--r--node/DeferredPackets.hpp85
-rw-r--r--node/Dictionary.hpp157
-rw-r--r--node/Hashtable.hpp58
-rw-r--r--node/Identity.cpp87
-rw-r--r--node/Identity.hpp110
-rw-r--r--node/IncomingPacket.cpp1965
-rw-r--r--node/IncomingPacket.hpp125
-rw-r--r--node/InetAddress.cpp233
-rw-r--r--node/InetAddress.hpp247
-rw-r--r--node/MAC.hpp118
-rw-r--r--node/Membership.cpp237
-rw-r--r--node/Membership.hpp285
-rw-r--r--node/MulticastGroup.hpp73
-rw-r--r--node/Multicaster.cpp267
-rw-r--r--node/Multicaster.hpp145
-rw-r--r--node/Mutex.hpp128
-rw-r--r--node/Network.cpp1488
-rw-r--r--node/Network.hpp344
-rw-r--r--node/NetworkConfig.cpp562
-rw-r--r--node/NetworkConfig.hpp447
-rw-r--r--node/NetworkController.hpp90
-rw-r--r--node/Node.cpp970
-rw-r--r--node/Node.hpp297
-rw-r--r--node/NonCopyable.hpp38
-rw-r--r--node/OutboundMulticast.cpp107
-rw-r--r--node/OutboundMulticast.hpp56
-rw-r--r--node/Packet.cpp1042
-rw-r--r--node/Packet.hpp575
-rw-r--r--node/Path.cpp16
-rw-r--r--node/Path.hpp336
-rw-r--r--node/Peer.cpp841
-rw-r--r--node/Peer.hpp634
-rw-r--r--node/Poly1305.cpp22
-rw-r--r--node/Poly1305.hpp13
-rw-r--r--node/README.md2
-rw-r--r--node/Revocation.cpp55
-rw-r--r--node/Revocation.hpp197
-rw-r--r--node/RuntimeEnvironment.hpp58
-rw-r--r--node/SHA512.cpp593
-rw-r--r--node/SHA512.hpp10
-rw-r--r--node/Salsa20.cpp61
-rw-r--r--node/Salsa20.hpp128
-rw-r--r--node/SelfAwareness.cpp163
-rw-r--r--node/SelfAwareness.hpp28
-rw-r--r--node/SharedPtr.hpp82
-rw-r--r--node/Switch.cpp716
-rw-r--r--node/Switch.hpp145
-rw-r--r--node/Tag.cpp55
-rw-r--r--node/Tag.hpp210
-rw-r--r--node/Topology.cpp562
-rw-r--r--node/Topology.hpp348
-rw-r--r--node/Trace.cpp540
-rw-r--r--node/Trace.hpp176
-rw-r--r--node/Utils.cpp237
-rw-r--r--node/Utils.hpp410
-rw-r--r--node/World.hpp162
74 files changed, 12733 insertions, 10298 deletions
diff --git a/node/Address.hpp b/node/Address.hpp
index 9bf5605a..41977af2 100644
--- a/node/Address.hpp
+++ b/node/Address.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_ADDRESS_HPP
@@ -38,57 +46,26 @@ namespace ZeroTier {
class Address
{
public:
- Address()
- throw() :
- _a(0)
- {
- }
-
- Address(const Address &a)
- throw() :
- _a(a._a)
- {
- }
-
- Address(uint64_t a)
- throw() :
- _a(a & 0xffffffffffULL)
- {
- }
-
- Address(const char *s)
- throw()
- {
- unsigned char foo[ZT_ADDRESS_LENGTH];
- setTo(foo,Utils::unhex(s,foo,ZT_ADDRESS_LENGTH));
- }
-
- Address(const std::string &s)
- throw()
- {
- unsigned char foo[ZT_ADDRESS_LENGTH];
- setTo(foo,Utils::unhex(s.c_str(),foo,ZT_ADDRESS_LENGTH));
- }
+ Address() : _a(0) {}
+ Address(const Address &a) : _a(a._a) {}
+ Address(uint64_t a) : _a(a & 0xffffffffffULL) {}
/**
* @param bits Raw address -- 5 bytes, big-endian byte order
* @param len Length of array
*/
Address(const void *bits,unsigned int len)
- throw()
{
setTo(bits,len);
}
inline Address &operator=(const Address &a)
- throw()
{
_a = a._a;
return *this;
}
inline Address &operator=(const uint64_t a)
- throw()
{
_a = (a & 0xffffffffffULL);
return *this;
@@ -99,7 +76,6 @@ public:
* @param len Length of array
*/
inline void setTo(const void *bits,unsigned int len)
- throw()
{
if (len < ZT_ADDRESS_LENGTH) {
_a = 0;
@@ -119,7 +95,6 @@ public:
* @param len Length of array
*/
inline void copyTo(void *bits,unsigned int len) const
- throw()
{
if (len < ZT_ADDRESS_LENGTH)
return;
@@ -138,7 +113,6 @@ public:
*/
template<unsigned int C>
inline void appendTo(Buffer<C> &b) const
- throw(std::out_of_range)
{
unsigned char *p = (unsigned char *)b.appendField(ZT_ADDRESS_LENGTH);
*(p++) = (unsigned char)((_a >> 32) & 0xff);
@@ -152,7 +126,6 @@ public:
* @return Integer containing address (0 to 2^40)
*/
inline uint64_t toInt() const
- throw()
{
return _a;
}
@@ -161,7 +134,6 @@ public:
* @return Hash code for use with Hashtable
*/
inline unsigned long hashCode() const
- throw()
{
return (unsigned long)_a;
}
@@ -169,31 +141,20 @@ public:
/**
* @return Hexadecimal string
*/
- inline std::string toString() const
- {
- char buf[16];
- Utils::snprintf(buf,sizeof(buf),"%.10llx",(unsigned long long)_a);
- return std::string(buf);
- };
-
- /**
- * @param buf Buffer to fill
- * @param len Length of buffer
- */
- inline void toString(char *buf,unsigned int len) const
+ inline char *toString(char buf[11]) const
{
- Utils::snprintf(buf,len,"%.10llx",(unsigned long long)_a);
+ return Utils::hex10(_a,buf);
}
/**
* @return True if this address is not zero
*/
- inline operator bool() const throw() { return (_a != 0); }
+ inline operator bool() const { return (_a != 0); }
/**
* Set to null/zero
*/
- inline void zero() throw() { _a = 0; }
+ inline void zero() { _a = 0; }
/**
* Check if this address is reserved
@@ -205,7 +166,6 @@ public:
* @return True if address is reserved and may not be used
*/
inline bool isReserved() const
- throw()
{
return ((!_a)||((_a >> 32) == ZT_ADDRESS_RESERVED_PREFIX));
}
@@ -214,21 +174,21 @@ public:
* @param i Value from 0 to 4 (inclusive)
* @return Byte at said position (address interpreted in big-endian order)
*/
- inline unsigned char operator[](unsigned int i) const throw() { return (unsigned char)((_a >> (32 - (i * 8))) & 0xff); }
-
- inline bool operator==(const uint64_t &a) const throw() { return (_a == (a & 0xffffffffffULL)); }
- inline bool operator!=(const uint64_t &a) const throw() { return (_a != (a & 0xffffffffffULL)); }
- inline bool operator>(const uint64_t &a) const throw() { return (_a > (a & 0xffffffffffULL)); }
- inline bool operator<(const uint64_t &a) const throw() { return (_a < (a & 0xffffffffffULL)); }
- inline bool operator>=(const uint64_t &a) const throw() { return (_a >= (a & 0xffffffffffULL)); }
- inline bool operator<=(const uint64_t &a) const throw() { return (_a <= (a & 0xffffffffffULL)); }
-
- inline bool operator==(const Address &a) const throw() { return (_a == a._a); }
- inline bool operator!=(const Address &a) const throw() { return (_a != a._a); }
- inline bool operator>(const Address &a) const throw() { return (_a > a._a); }
- inline bool operator<(const Address &a) const throw() { return (_a < a._a); }
- inline bool operator>=(const Address &a) const throw() { return (_a >= a._a); }
- inline bool operator<=(const Address &a) const throw() { return (_a <= a._a); }
+ inline unsigned char operator[](unsigned int i) const { return (unsigned char)((_a >> (32 - (i * 8))) & 0xff); }
+
+ inline bool operator==(const uint64_t &a) const { return (_a == (a & 0xffffffffffULL)); }
+ inline bool operator!=(const uint64_t &a) const { return (_a != (a & 0xffffffffffULL)); }
+ inline bool operator>(const uint64_t &a) const { return (_a > (a & 0xffffffffffULL)); }
+ inline bool operator<(const uint64_t &a) const { return (_a < (a & 0xffffffffffULL)); }
+ inline bool operator>=(const uint64_t &a) const { return (_a >= (a & 0xffffffffffULL)); }
+ inline bool operator<=(const uint64_t &a) const { return (_a <= (a & 0xffffffffffULL)); }
+
+ inline bool operator==(const Address &a) const { return (_a == a._a); }
+ inline bool operator!=(const Address &a) const { return (_a != a._a); }
+ inline bool operator>(const Address &a) const { return (_a > a._a); }
+ inline bool operator<(const Address &a) const { return (_a < a._a); }
+ inline bool operator>=(const Address &a) const { return (_a >= a._a); }
+ inline bool operator<=(const Address &a) const { return (_a <= a._a); }
private:
uint64_t _a;
diff --git a/node/Array.hpp b/node/Array.hpp
deleted file mode 100644
index 19b29eb3..00000000
--- a/node/Array.hpp
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef ZT_ARRAY_HPP
-#define ZT_ARRAY_HPP
-
-#include <string>
-#include <algorithm>
-
-namespace ZeroTier {
-
-/**
- * Static array -- a simple thing that's belonged in STL since the time of the dinosaurs
- */
-template<typename T,std::size_t S>
-class Array
-{
-public:
- Array() throw() {}
-
- Array(const Array &a)
- {
- for(std::size_t i=0;i<S;++i)
- data[i] = a.data[i];
- }
-
- Array(const T *ptr)
- {
- for(std::size_t i=0;i<S;++i)
- data[i] = ptr[i];
- }
-
- inline Array &operator=(const Array &a)
- {
- for(std::size_t i=0;i<S;++i)
- data[i] = a.data[i];
- return *this;
- }
-
- typedef T value_type;
- typedef T* pointer;
- typedef const T* const_pointer;
- typedef T& reference;
- typedef const T& const_reference;
- typedef T* iterator;
- typedef const T* const_iterator;
- typedef std::size_t size_type;
- typedef std::ptrdiff_t difference_type;
- typedef std::reverse_iterator<iterator> reverse_iterator;
- typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
-
- inline iterator begin() throw() { return data; }
- inline iterator end() throw() { return &(data[S]); }
- inline const_iterator begin() const throw() { return data; }
- inline const_iterator end() const throw() { return &(data[S]); }
-
- inline reverse_iterator rbegin() throw() { return reverse_iterator(begin()); }
- inline reverse_iterator rend() throw() { return reverse_iterator(end()); }
- inline const_reverse_iterator rbegin() const throw() { return const_reverse_iterator(begin()); }
- inline const_reverse_iterator rend() const throw() { return const_reverse_iterator(end()); }
-
- inline std::size_t size() const throw() { return S; }
- inline std::size_t max_size() const throw() { return S; }
-
- inline reference operator[](const std::size_t n) throw() { return data[n]; }
- inline const_reference operator[](const std::size_t n) const throw() { return data[n]; }
-
- inline reference front() throw() { return data[0]; }
- inline const_reference front() const throw() { return data[0]; }
- inline reference back() throw() { return data[S-1]; }
- inline const_reference back() const throw() { return data[S-1]; }
-
- inline bool operator==(const Array &k) const throw()
- {
- for(unsigned long i=0;i<S;++i) {
- if (data[i] != k.data[i])
- return false;
- }
- return true;
- }
- inline bool operator<(const Array &k) const throw() { return std::lexicographical_compare(begin(),end(),k.begin(),k.end()); }
- inline bool operator!=(const Array &k) const throw() { return !(*this == k); }
- inline bool operator>(const Array &k) const throw() { return (k < *this); }
- inline bool operator<=(const Array &k) const throw() { return !(k < *this); }
- inline bool operator>=(const Array &k) const throw() { return !(*this < k); }
-
- T data[S];
-};
-
-} // namespace ZeroTier
-
-#endif
diff --git a/node/AtomicCounter.hpp b/node/AtomicCounter.hpp
index b4993771..a42a18d4 100644
--- a/node/AtomicCounter.hpp
+++ b/node/AtomicCounter.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,17 +14,22 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_ATOMICCOUNTER_HPP
#define ZT_ATOMICCOUNTER_HPP
#include "Constants.hpp"
-#include "Mutex.hpp"
-#include "NonCopyable.hpp"
-#ifdef __WINDOWS__
-// <atomic> will replace this whole class eventually once it's ubiquitous
+#ifndef __GNUC__
#include <atomic>
#endif
@@ -33,78 +38,46 @@ namespace ZeroTier {
/**
* Simple atomic counter supporting increment and decrement
*/
-class AtomicCounter : NonCopyable
+class AtomicCounter
{
public:
- /**
- * Initialize counter at zero
- */
- AtomicCounter()
- throw()
- {
- _v = 0;
- }
+ AtomicCounter() { _v = 0; }
- inline operator int() const
- throw()
+ inline int load() const
{
#ifdef __GNUC__
- return __sync_or_and_fetch(const_cast <volatile int *>(&_v),0);
+ return __sync_or_and_fetch(const_cast<int *>(&_v),0);
#else
-#ifdef __WINDOWS__
- return (int)_v;
-#else
- _l.lock();
- int v = _v;
- _l.unlock();
- return v;
-#endif
+ return _v.load();
#endif
}
inline int operator++()
- throw()
{
#ifdef __GNUC__
return __sync_add_and_fetch(&_v,1);
#else
-#ifdef __WINDOWS__
return ++_v;
-#else
- _l.lock();
- int v = ++_v;
- _l.unlock();
- return v;
-#endif
#endif
}
inline int operator--()
- throw()
{
#ifdef __GNUC__
return __sync_sub_and_fetch(&_v,1);
#else
-#ifdef __WINDOWS__
return --_v;
-#else
- _l.lock();
- int v = --_v;
- _l.unlock();
- return v;
-#endif
#endif
}
private:
-#ifdef __WINDOWS__
- std::atomic_int _v;
-#else
+ AtomicCounter(const AtomicCounter &) {}
+ const AtomicCounter &operator=(const AtomicCounter &) { return *this; }
+
+#ifdef __GNUC__
int _v;
-#ifndef __GNUC__
-#warning Neither __WINDOWS__ nor __GNUC__ so AtomicCounter using Mutex
- Mutex _l;
-#endif
+#else
+ std::atomic_int _v;
#endif
};
diff --git a/node/BinarySemaphore.hpp b/node/BinarySemaphore.hpp
deleted file mode 100644
index 315d2b00..00000000
--- a/node/BinarySemaphore.hpp
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef ZT_BINARYSEMAPHORE_HPP
-#define ZT_BINARYSEMAPHORE_HPP
-
-#include <stdio.h>
-#include <stdint.h>
-#include <stdlib.h>
-
-#include "Constants.hpp"
-#include "NonCopyable.hpp"
-
-#ifdef __WINDOWS__
-
-#include <Windows.h>
-
-namespace ZeroTier {
-
-class BinarySemaphore : NonCopyable
-{
-public:
- BinarySemaphore() throw() { _sem = CreateSemaphore(NULL,0,1,NULL); }
- ~BinarySemaphore() { CloseHandle(_sem); }
- inline void wait() { WaitForSingleObject(_sem,INFINITE); }
- inline void post() { ReleaseSemaphore(_sem,1,NULL); }
-private:
- HANDLE _sem;
-};
-
-} // namespace ZeroTier
-
-#else // !__WINDOWS__
-
-#include <pthread.h>
-
-namespace ZeroTier {
-
-class BinarySemaphore : NonCopyable
-{
-public:
- BinarySemaphore()
- {
- pthread_mutex_init(&_mh,(const pthread_mutexattr_t *)0);
- pthread_cond_init(&_cond,(const pthread_condattr_t *)0);
- _f = false;
- }
-
- ~BinarySemaphore()
- {
- pthread_cond_destroy(&_cond);
- pthread_mutex_destroy(&_mh);
- }
-
- inline void wait()
- {
- pthread_mutex_lock(const_cast <pthread_mutex_t *>(&_mh));
- while (!_f)
- pthread_cond_wait(const_cast <pthread_cond_t *>(&_cond),const_cast <pthread_mutex_t *>(&_mh));
- _f = false;
- pthread_mutex_unlock(const_cast <pthread_mutex_t *>(&_mh));
- }
-
- inline void post()
- {
- pthread_mutex_lock(const_cast <pthread_mutex_t *>(&_mh));
- _f = true;
- pthread_mutex_unlock(const_cast <pthread_mutex_t *>(&_mh));
- pthread_cond_signal(const_cast <pthread_cond_t *>(&_cond));
- }
-
-private:
- pthread_cond_t _cond;
- pthread_mutex_t _mh;
- volatile bool _f;
-};
-
-} // namespace ZeroTier
-
-#endif // !__WINDOWS__
-
-#endif
diff --git a/node/Buffer.hpp b/node/Buffer.hpp
index 0b171592..bbf4ee37 100644
--- a/node/Buffer.hpp
+++ b/node/Buffer.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_BUFFER_HPP
@@ -61,11 +69,11 @@ public:
// STL container idioms
typedef unsigned char value_type;
typedef unsigned char * pointer;
- typedef const unsigned char * const_pointer;
- typedef unsigned char & reference;
- typedef const unsigned char & const_reference;
- typedef unsigned char * iterator;
- typedef const unsigned char * const_iterator;
+ typedef const char * const_pointer;
+ typedef char & reference;
+ typedef const char & const_reference;
+ typedef char * iterator;
+ typedef const char * const_iterator;
typedef unsigned int size_type;
typedef int difference_type;
typedef std::reverse_iterator<iterator> reverse_iterator;
@@ -79,78 +87,61 @@ public:
inline const_reverse_iterator rbegin() const { return const_reverse_iterator(begin()); }
inline const_reverse_iterator rend() const { return const_reverse_iterator(end()); }
- Buffer()
- throw() :
+ Buffer() :
_l(0)
{
}
Buffer(unsigned int l)
- throw(std::out_of_range)
{
if (l > C)
- throw std::out_of_range("Buffer: construct with size larger than capacity");
+ throw ZT_EXCEPTION_OUT_OF_BOUNDS;
_l = l;
}
template<unsigned int C2>
Buffer(const Buffer<C2> &b)
- throw(std::out_of_range)
{
*this = b;
}
Buffer(const void *b,unsigned int l)
- throw(std::out_of_range)
{
copyFrom(b,l);
}
- Buffer(const std::string &s)
- throw(std::out_of_range)
- {
- copyFrom(s.data(),s.length());
- }
-
template<unsigned int C2>
inline Buffer &operator=(const Buffer<C2> &b)
- throw(std::out_of_range)
- {
- if (b._l > C)
- throw std::out_of_range("Buffer: assignment from buffer larger than capacity");
- memcpy(_b,b._b,_l = b._l);
- return *this;
- }
-
- inline Buffer &operator=(const std::string &s)
- throw(std::out_of_range)
{
- copyFrom(s.data(),s.length());
+ if (unlikely(b._l > C))
+ throw ZT_EXCEPTION_OUT_OF_BOUNDS;
+ if (C2 == C) {
+ ZT_FAST_MEMCPY(this,&b,sizeof(Buffer<C>));
+ } else {
+ ZT_FAST_MEMCPY(_b,b._b,_l = b._l);
+ }
return *this;
}
inline void copyFrom(const void *b,unsigned int l)
- throw(std::out_of_range)
{
- if (l > C)
- throw std::out_of_range("Buffer: set from C array larger than capacity");
+ if (unlikely(l > C))
+ throw ZT_EXCEPTION_OUT_OF_BOUNDS;
+ ZT_FAST_MEMCPY(_b,b,l);
_l = l;
- memcpy(_b,b,l);
}
unsigned char operator[](const unsigned int i) const
- throw(std::out_of_range)
{
- if (i >= _l)
- throw std::out_of_range("Buffer: [] beyond end of data");
+ if (unlikely(i >= _l))
+ throw ZT_EXCEPTION_OUT_OF_BOUNDS;
return (unsigned char)_b[i];
}
unsigned char &operator[](const unsigned int i)
- throw(std::out_of_range)
{
- if (i >= _l)
- throw std::out_of_range("Buffer: [] beyond end of data");
+ if (unlikely(i >= _l))
+ throw ZT_EXCEPTION_OUT_OF_BOUNDS;
return ((unsigned char *)_b)[i];
}
@@ -168,17 +159,15 @@ public:
* @throws std::out_of_range Field extends beyond data size
*/
unsigned char *field(unsigned int i,unsigned int l)
- throw(std::out_of_range)
{
- if ((i + l) > _l)
- throw std::out_of_range("Buffer: field() beyond end of data");
+ if (unlikely((i + l) > _l))
+ throw ZT_EXCEPTION_OUT_OF_BOUNDS;
return (unsigned char *)(_b + i);
}
const unsigned char *field(unsigned int i,unsigned int l) const
- throw(std::out_of_range)
{
- if ((i + l) > _l)
- throw std::out_of_range("Buffer: field() beyond end of data");
+ if (unlikely((i + l) > _l))
+ throw ZT_EXCEPTION_OUT_OF_BOUNDS;
return (const unsigned char *)(_b + i);
}
@@ -191,10 +180,9 @@ public:
*/
template<typename T>
inline void setAt(unsigned int i,const T v)
- throw(std::out_of_range)
{
- if ((i + sizeof(T)) > _l)
- throw std::out_of_range("Buffer: setAt() beyond end of data");
+ if (unlikely((i + sizeof(T)) > _l))
+ throw ZT_EXCEPTION_OUT_OF_BOUNDS;
#ifdef ZT_NO_TYPE_PUNNING
uint8_t *p = reinterpret_cast<uint8_t *>(_b + i);
for(unsigned int x=1;x<=sizeof(T);++x)
@@ -214,10 +202,9 @@ public:
*/
template<typename T>
inline T at(unsigned int i) const
- throw(std::out_of_range)
{
- if ((i + sizeof(T)) > _l)
- throw std::out_of_range("Buffer: at() beyond end of data");
+ if (unlikely((i + sizeof(T)) > _l))
+ throw ZT_EXCEPTION_OUT_OF_BOUNDS;
#ifdef ZT_NO_TYPE_PUNNING
T v = 0;
const uint8_t *p = reinterpret_cast<const uint8_t *>(_b + i);
@@ -241,10 +228,9 @@ public:
*/
template<typename T>
inline void append(const T v)
- throw(std::out_of_range)
{
- if ((_l + sizeof(T)) > C)
- throw std::out_of_range("Buffer: append beyond capacity");
+ if (unlikely((_l + sizeof(T)) > C))
+ throw ZT_EXCEPTION_OUT_OF_BOUNDS;
#ifdef ZT_NO_TYPE_PUNNING
uint8_t *p = reinterpret_cast<uint8_t *>(_b + _l);
for(unsigned int x=1;x<=sizeof(T);++x)
@@ -264,40 +250,39 @@ public:
* @throws std::out_of_range Attempt to append beyond capacity
*/
inline void append(unsigned char c,unsigned int n)
- throw(std::out_of_range)
{
- if ((_l + n) > C)
- throw std::out_of_range("Buffer: append beyond capacity");
+ if (unlikely((_l + n) > C))
+ throw ZT_EXCEPTION_OUT_OF_BOUNDS;
for(unsigned int i=0;i<n;++i)
_b[_l++] = (char)c;
}
/**
- * Append a C-array of bytes
+ * Append secure random bytes
*
- * @param b Data
- * @param l Length
- * @throws std::out_of_range Attempt to append beyond capacity
+ * @param n Number of random bytes to append
*/
- inline void append(const void *b,unsigned int l)
- throw(std::out_of_range)
+ inline void appendRandom(unsigned int n)
{
- if ((_l + l) > C)
- throw std::out_of_range("Buffer: append beyond capacity");
- memcpy(_b + _l,b,l);
- _l += l;
+ if (unlikely((_l + n) > C))
+ throw ZT_EXCEPTION_OUT_OF_BOUNDS;
+ Utils::getSecureRandom(_b + _l,n);
+ _l += n;
}
/**
- * Append a string
+ * Append a C-array of bytes
*
- * @param s String to append
+ * @param b Data
+ * @param l Length
* @throws std::out_of_range Attempt to append beyond capacity
*/
- inline void append(const std::string &s)
- throw(std::out_of_range)
+ inline void append(const void *b,unsigned int l)
{
- append(s.data(),(unsigned int)s.length());
+ if (unlikely((_l + l) > C))
+ throw ZT_EXCEPTION_OUT_OF_BOUNDS;
+ ZT_FAST_MEMCPY(_b + _l,b,l);
+ _l += l;
}
/**
@@ -307,11 +292,10 @@ public:
* @throws std::out_of_range Attempt to append beyond capacity
*/
inline void appendCString(const char *s)
- throw(std::out_of_range)
{
for(;;) {
- if (_l >= C)
- throw std::out_of_range("Buffer: append beyond capacity");
+ if (unlikely(_l >= C))
+ throw ZT_EXCEPTION_OUT_OF_BOUNDS;
if (!(_b[_l++] = *(s++)))
break;
}
@@ -326,7 +310,6 @@ public:
*/
template<unsigned int C2>
inline void append(const Buffer<C2> &b)
- throw(std::out_of_range)
{
append(b._b,b._l);
}
@@ -342,10 +325,9 @@ public:
* @return Pointer to beginning of appended field of length 'l'
*/
inline char *appendField(unsigned int l)
- throw(std::out_of_range)
{
- if ((_l + l) > C)
- throw std::out_of_range("Buffer: append beyond capacity");
+ if (unlikely((_l + l) > C))
+ throw ZT_EXCEPTION_OUT_OF_BOUNDS;
char *r = _b + _l;
_l += l;
return r;
@@ -360,10 +342,9 @@ public:
* @throws std::out_of_range Capacity exceeded
*/
inline void addSize(unsigned int i)
- throw(std::out_of_range)
{
- if ((i + _l) > C)
- throw std::out_of_range("Buffer: setSize to larger than capacity");
+ if (unlikely((i + _l) > C))
+ throw ZT_EXCEPTION_OUT_OF_BOUNDS;
_l += i;
}
@@ -376,10 +357,9 @@ public:
* @throws std::out_of_range Size larger than capacity
*/
inline void setSize(const unsigned int i)
- throw(std::out_of_range)
{
- if (i > C)
- throw std::out_of_range("Buffer: setSize to larger than capacity");
+ if (unlikely(i > C))
+ throw ZT_EXCEPTION_OUT_OF_BOUNDS;
_l = i;
}
@@ -387,15 +367,14 @@ public:
* Move everything after 'at' to the buffer's front and truncate
*
* @param at Truncate before this position
- * @throw std::out_of_range Position is beyond size of buffer
+ * @throws std::out_of_range Position is beyond size of buffer
*/
inline void behead(const unsigned int at)
- throw(std::out_of_range)
{
if (!at)
return;
- if (at > _l)
- throw std::out_of_range("Buffer: behead() beyond capacity");
+ if (unlikely(at > _l))
+ throw ZT_EXCEPTION_OUT_OF_BOUNDS;
::memmove(_b,_b + at,_l -= at);
}
@@ -404,14 +383,13 @@ public:
*
* @param start Starting position
* @param length Length of block to erase
- * @throw std::out_of_range Position plus length is beyond size of buffer
+ * @throws std::out_of_range Position plus length is beyond size of buffer
*/
inline void erase(const unsigned int at,const unsigned int length)
- throw(std::out_of_range)
{
const unsigned int endr = at + length;
- if (endr > _l)
- throw std::out_of_range("Buffer: erase() range beyond end of buffer");
+ if (unlikely(endr > _l))
+ throw ZT_EXCEPTION_OUT_OF_BOUNDS;
::memmove(_b + at,_b + endr,_l - endr);
_l -= length;
}
@@ -419,94 +397,77 @@ public:
/**
* Set buffer data length to zero
*/
- inline void clear()
- throw()
- {
- _l = 0;
- }
+ inline void clear() { _l = 0; }
/**
* Zero buffer up to size()
*/
- inline void zero()
- throw()
- {
- memset(_b,0,_l);
- }
+ inline void zero() { memset(_b,0,_l); }
/**
* Zero unused capacity area
*/
- inline void zeroUnused()
- throw()
- {
- memset(_b + _l,0,C - _l);
- }
+ inline void zeroUnused() { memset(_b + _l,0,C - _l); }
/**
* Unconditionally and securely zero buffer's underlying memory
*/
- inline void burn()
- throw()
- {
- Utils::burn(_b,sizeof(_b));
- }
+ inline void burn() { Utils::burn(_b,sizeof(_b)); }
/**
* @return Constant pointer to data in buffer
*/
- inline const void *data() const throw() { return _b; }
+ inline const void *data() const { return _b; }
+
+ /**
+ * @return Non-constant pointer to data in buffer
+ */
+ inline void *unsafeData() { return _b; }
/**
* @return Size of data in buffer
*/
- inline unsigned int size() const throw() { return _l; }
+ inline unsigned int size() const { return _l; }
/**
* @return Capacity of buffer
*/
- inline unsigned int capacity() const throw() { return C; }
+ inline unsigned int capacity() const { return C; }
template<unsigned int C2>
inline bool operator==(const Buffer<C2> &b) const
- throw()
{
return ((_l == b._l)&&(!memcmp(_b,b._b,_l)));
}
template<unsigned int C2>
inline bool operator!=(const Buffer<C2> &b) const
- throw()
{
return ((_l != b._l)||(memcmp(_b,b._b,_l)));
}
template<unsigned int C2>
inline bool operator<(const Buffer<C2> &b) const
- throw()
{
return (memcmp(_b,b._b,std::min(_l,b._l)) < 0);
}
template<unsigned int C2>
inline bool operator>(const Buffer<C2> &b) const
- throw()
{
return (b < *this);
}
template<unsigned int C2>
inline bool operator<=(const Buffer<C2> &b) const
- throw()
{
return !(b < *this);
}
template<unsigned int C2>
inline bool operator>=(const Buffer<C2> &b) const
- throw()
{
return !(*this < b);
}
private:
- unsigned int _l;
char ZT_VAR_MAY_ALIAS _b[C];
+ unsigned int _l;
};
} // namespace ZeroTier
diff --git a/node/C25519.cpp b/node/C25519.cpp
index e9ffecc1..77084bd8 100644
--- a/node/C25519.cpp
+++ b/node/C25519.cpp
@@ -1,5 +1,3 @@
-// Code taken from NaCl by D. J. Bernstein and others
-
/*
Matthew Dempsky
Public domain.
@@ -7,7 +5,7 @@ Derived from public domain code by D. J. Bernstein.
*/
// Modified very slightly for ZeroTier One by Adam Ierymenko
-// (no functional changes)
+// This code remains in the public domain.
#include <stdint.h>
#include <stdlib.h>
@@ -22,7 +20,7 @@ Derived from public domain code by D. J. Bernstein.
#pragma warning(disable: 4146)
#endif
-namespace ZeroTier {
+namespace {
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
@@ -33,990 +31,853 @@ namespace ZeroTier {
#define crypto_uint64 uint64_t
#define crypto_hash_sha512_BYTES 64
-static inline void add(unsigned int out[32],const unsigned int a[32],const unsigned int b[32])
+void add(unsigned int out[32],const unsigned int a[32],const unsigned int b[32])
{
- unsigned int j;
- unsigned int u;
- u = 0;
- for (j = 0;j < 31;++j) { u += a[j] + b[j]; out[j] = u & 255; u >>= 8; }
- u += a[31] + b[31]; out[31] = u;
+ unsigned int j;
+ unsigned int u;
+ u = 0;
+ for (j = 0;j < 31;++j) { u += a[j] + b[j]; out[j] = u & 255; u >>= 8; }
+ u += a[31] + b[31]; out[31] = u;
}
-static inline void sub(unsigned int out[32],const unsigned int a[32],const unsigned int b[32])
+void sub(unsigned int out[32],const unsigned int a[32],const unsigned int b[32])
{
- unsigned int j;
- unsigned int u;
- u = 218;
- for (j = 0;j < 31;++j) {
- u += a[j] + 65280 - b[j];
- out[j] = u & 255;
- u >>= 8;
- }
- u += a[31] - b[31];
- out[31] = u;
+ unsigned int j;
+ unsigned int u;
+ u = 218;
+ for (j = 0;j < 31;++j) {
+ u += a[j] + 65280 - b[j];
+ out[j] = u & 255;
+ u >>= 8;
+ }
+ u += a[31] - b[31];
+ out[31] = u;
}
-static inline void squeeze(unsigned int a[32])
+void squeeze(unsigned int a[32])
{
- unsigned int j;
- unsigned int u;
- u = 0;
- for (j = 0;j < 31;++j) { u += a[j]; a[j] = u & 255; u >>= 8; }
- u += a[31]; a[31] = u & 127;
- u = 19 * (u >> 7);
- for (j = 0;j < 31;++j) { u += a[j]; a[j] = u & 255; u >>= 8; }
- u += a[31]; a[31] = u;
+ unsigned int j;
+ unsigned int u;
+ u = 0;
+ for (j = 0;j < 31;++j) { u += a[j]; a[j] = u & 255; u >>= 8; }
+ u += a[31]; a[31] = u & 127;
+ u = 19 * (u >> 7);
+ for (j = 0;j < 31;++j) { u += a[j]; a[j] = u & 255; u >>= 8; }
+ u += a[31]; a[31] = u;
}
static const unsigned int minusp[32] = {
19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128
} ;
-static inline void freeze(unsigned int a[32])
+void freeze(unsigned int a[32])
{
- unsigned int aorig[32];
- unsigned int j;
- unsigned int negative;
+ unsigned int aorig[32];
+ unsigned int j;
+ unsigned int negative;
- for (j = 0;j < 32;++j) aorig[j] = a[j];
- add(a,a,minusp);
- negative = -((a[31] >> 7) & 1);
- for (j = 0;j < 32;++j) a[j] ^= negative & (aorig[j] ^ a[j]);
+ for (j = 0;j < 32;++j) aorig[j] = a[j];
+ add(a,a,minusp);
+ negative = -((a[31] >> 7) & 1);
+ for (j = 0;j < 32;++j) a[j] ^= negative & (aorig[j] ^ a[j]);
}
-static inline void mult(unsigned int out[32],const unsigned int a[32],const unsigned int b[32])
+void mult(unsigned int out[32],const unsigned int a[32],const unsigned int b[32])
{
- unsigned int i;
- unsigned int j;
- unsigned int u;
+ unsigned int i;
+ unsigned int j;
+ unsigned int u;
- for (i = 0;i < 32;++i) {
- u = 0;
- for (j = 0;j <= i;++j) u += a[j] * b[i - j];
- for (j = i + 1;j < 32;++j) u += 38 * a[j] * b[i + 32 - j];
- out[i] = u;
- }
- squeeze(out);
+ for (i = 0;i < 32;++i) {
+ u = 0;
+ for (j = 0;j <= i;++j) u += a[j] * b[i - j];
+ for (j = i + 1;j < 32;++j) u += 38 * a[j] * b[i + 32 - j];
+ out[i] = u;
+ }
+ squeeze(out);
}
-static inline void mult121665(unsigned int out[32],const unsigned int a[32])
+void mult121665(unsigned int out[32],const unsigned int a[32])
{
- unsigned int j;
- unsigned int u;
+ unsigned int j;
+ unsigned int u;
- u = 0;
- for (j = 0;j < 31;++j) { u += 121665 * a[j]; out[j] = u & 255; u >>= 8; }
- u += 121665 * a[31]; out[31] = u & 127;
- u = 19 * (u >> 7);
- for (j = 0;j < 31;++j) { u += out[j]; out[j] = u & 255; u >>= 8; }
- u += out[j]; out[j] = u;
+ u = 0;
+ for (j = 0;j < 31;++j) { u += 121665 * a[j]; out[j] = u & 255; u >>= 8; }
+ u += 121665 * a[31]; out[31] = u & 127;
+ u = 19 * (u >> 7);
+ for (j = 0;j < 31;++j) { u += out[j]; out[j] = u & 255; u >>= 8; }
+ u += out[j]; out[j] = u;
}
-static inline void square(unsigned int out[32],const unsigned int a[32])
+void square(unsigned int out[32],const unsigned int a[32])
{
- unsigned int i;
- unsigned int j;
- unsigned int u;
+ unsigned int i;
+ unsigned int j;
+ unsigned int u;
- for (i = 0;i < 32;++i) {
- u = 0;
- for (j = 0;j < i - j;++j) u += a[j] * a[i - j];
- for (j = i + 1;j < i + 32 - j;++j) u += 38 * a[j] * a[i + 32 - j];
- u *= 2;
- if ((i & 1) == 0) {
- u += a[i / 2] * a[i / 2];
- u += 38 * a[i / 2 + 16] * a[i / 2 + 16];
- }
- out[i] = u;
- }
- squeeze(out);
+ for (i = 0;i < 32;++i) {
+ u = 0;
+ for (j = 0;j < i - j;++j) u += a[j] * a[i - j];
+ for (j = i + 1;j < i + 32 - j;++j) u += 38 * a[j] * a[i + 32 - j];
+ u *= 2;
+ if ((i & 1) == 0) {
+ u += a[i / 2] * a[i / 2];
+ u += 38 * a[i / 2 + 16] * a[i / 2 + 16];
+ }
+ out[i] = u;
+ }
+ squeeze(out);
}
-static inline void select(unsigned int p[64],unsigned int q[64],const unsigned int r[64],const unsigned int s[64],unsigned int b)
+void select(unsigned int p[64],unsigned int q[64],const unsigned int r[64],const unsigned int s[64],unsigned int b)
{
- unsigned int j;
- unsigned int t;
- unsigned int bminus1;
+ unsigned int j;
+ unsigned int t;
+ unsigned int bminus1;
- bminus1 = b - 1;
- for (j = 0;j < 64;++j) {
- t = bminus1 & (r[j] ^ s[j]);
- p[j] = s[j] ^ t;
- q[j] = r[j] ^ t;
- }
+ bminus1 = b - 1;
+ for (j = 0;j < 64;++j) {
+ t = bminus1 & (r[j] ^ s[j]);
+ p[j] = s[j] ^ t;
+ q[j] = r[j] ^ t;
+ }
}
static void mainloop(unsigned int work[64],const unsigned char e[32])
{
- unsigned int xzm1[64];
- unsigned int xzm[64];
- unsigned int xzmb[64];
- unsigned int xzm1b[64];
- unsigned int xznb[64];
- unsigned int xzn1b[64];
- unsigned int a0[64];
- unsigned int a1[64];
- unsigned int b0[64];
- unsigned int b1[64];
- unsigned int c1[64];
- unsigned int r[32];
- unsigned int s[32];
- unsigned int t[32];
- unsigned int u[32];
- //unsigned int i;
- unsigned int j;
- unsigned int b;
- int pos;
-
- for (j = 0;j < 32;++j) xzm1[j] = work[j];
- xzm1[32] = 1;
- for (j = 33;j < 64;++j) xzm1[j] = 0;
-
- xzm[0] = 1;
- for (j = 1;j < 64;++j) xzm[j] = 0;
-
- for (pos = 254;pos >= 0;--pos) {
- b = e[pos / 8] >> (pos & 7);
- b &= 1;
- select(xzmb,xzm1b,xzm,xzm1,b);
- add(a0,xzmb,xzmb + 32);
- sub(a0 + 32,xzmb,xzmb + 32);
- add(a1,xzm1b,xzm1b + 32);
- sub(a1 + 32,xzm1b,xzm1b + 32);
- square(b0,a0);
- square(b0 + 32,a0 + 32);
- mult(b1,a1,a0 + 32);
- mult(b1 + 32,a1 + 32,a0);
- add(c1,b1,b1 + 32);
- sub(c1 + 32,b1,b1 + 32);
- square(r,c1 + 32);
- sub(s,b0,b0 + 32);
- mult121665(t,s);
- add(u,t,b0);
- mult(xznb,b0,b0 + 32);
- mult(xznb + 32,s,u);
- square(xzn1b,c1);
- mult(xzn1b + 32,r,work);
- select(xzm,xzm1,xznb,xzn1b,b);
- }
-
- for (j = 0;j < 64;++j) work[j] = xzm[j];
+ unsigned int xzm1[64];
+ unsigned int xzm[64];
+ unsigned int xzmb[64];
+ unsigned int xzm1b[64];
+ unsigned int xznb[64];
+ unsigned int xzn1b[64];
+ unsigned int a0[64];
+ unsigned int a1[64];
+ unsigned int b0[64];
+ unsigned int b1[64];
+ unsigned int c1[64];
+ unsigned int r[32];
+ unsigned int s[32];
+ unsigned int t[32];
+ unsigned int u[32];
+ //unsigned int i;
+ unsigned int j;
+ unsigned int b;
+ int pos;
+
+ for (j = 0;j < 32;++j) xzm1[j] = work[j];
+ xzm1[32] = 1;
+ for (j = 33;j < 64;++j) xzm1[j] = 0;
+
+ xzm[0] = 1;
+ for (j = 1;j < 64;++j) xzm[j] = 0;
+
+ for (pos = 254;pos >= 0;--pos) {
+ b = e[pos / 8] >> (pos & 7);
+ b &= 1;
+ select(xzmb,xzm1b,xzm,xzm1,b);
+ add(a0,xzmb,xzmb + 32);
+ sub(a0 + 32,xzmb,xzmb + 32);
+ add(a1,xzm1b,xzm1b + 32);
+ sub(a1 + 32,xzm1b,xzm1b + 32);
+ square(b0,a0);
+ square(b0 + 32,a0 + 32);
+ mult(b1,a1,a0 + 32);
+ mult(b1 + 32,a1 + 32,a0);
+ add(c1,b1,b1 + 32);
+ sub(c1 + 32,b1,b1 + 32);
+ square(r,c1 + 32);
+ sub(s,b0,b0 + 32);
+ mult121665(t,s);
+ add(u,t,b0);
+ mult(xznb,b0,b0 + 32);
+ mult(xznb + 32,s,u);
+ square(xzn1b,c1);
+ mult(xzn1b + 32,r,work);
+ select(xzm,xzm1,xznb,xzn1b,b);
+ }
+
+ for (j = 0;j < 64;++j) work[j] = xzm[j];
}
static void recip(unsigned int out[32],const unsigned int z[32])
{
- unsigned int z2[32];
- unsigned int z9[32];
- unsigned int z11[32];
- unsigned int z2_5_0[32];
- unsigned int z2_10_0[32];
- unsigned int z2_20_0[32];
- unsigned int z2_50_0[32];
- unsigned int z2_100_0[32];
- unsigned int t0[32];
- unsigned int t1[32];
- int i;
-
- /* 2 */ square(z2,z);
- /* 4 */ square(t1,z2);
- /* 8 */ square(t0,t1);
- /* 9 */ mult(z9,t0,z);
- /* 11 */ mult(z11,z9,z2);
- /* 22 */ square(t0,z11);
- /* 2^5 - 2^0 = 31 */ mult(z2_5_0,t0,z9);
-
- /* 2^6 - 2^1 */ square(t0,z2_5_0);
- /* 2^7 - 2^2 */ square(t1,t0);
- /* 2^8 - 2^3 */ square(t0,t1);
- /* 2^9 - 2^4 */ square(t1,t0);
- /* 2^10 - 2^5 */ square(t0,t1);
- /* 2^10 - 2^0 */ mult(z2_10_0,t0,z2_5_0);
-
- /* 2^11 - 2^1 */ square(t0,z2_10_0);
- /* 2^12 - 2^2 */ square(t1,t0);
- /* 2^20 - 2^10 */ for (i = 2;i < 10;i += 2) { square(t0,t1); square(t1,t0); }
- /* 2^20 - 2^0 */ mult(z2_20_0,t1,z2_10_0);
-
- /* 2^21 - 2^1 */ square(t0,z2_20_0);
- /* 2^22 - 2^2 */ square(t1,t0);
- /* 2^40 - 2^20 */ for (i = 2;i < 20;i += 2) { square(t0,t1); square(t1,t0); }
- /* 2^40 - 2^0 */ mult(t0,t1,z2_20_0);
-
- /* 2^41 - 2^1 */ square(t1,t0);
- /* 2^42 - 2^2 */ square(t0,t1);
- /* 2^50 - 2^10 */ for (i = 2;i < 10;i += 2) { square(t1,t0); square(t0,t1); }
- /* 2^50 - 2^0 */ mult(z2_50_0,t0,z2_10_0);
-
- /* 2^51 - 2^1 */ square(t0,z2_50_0);
- /* 2^52 - 2^2 */ square(t1,t0);
- /* 2^100 - 2^50 */ for (i = 2;i < 50;i += 2) { square(t0,t1); square(t1,t0); }
- /* 2^100 - 2^0 */ mult(z2_100_0,t1,z2_50_0);
-
- /* 2^101 - 2^1 */ square(t1,z2_100_0);
- /* 2^102 - 2^2 */ square(t0,t1);
- /* 2^200 - 2^100 */ for (i = 2;i < 100;i += 2) { square(t1,t0); square(t0,t1); }
- /* 2^200 - 2^0 */ mult(t1,t0,z2_100_0);
-
- /* 2^201 - 2^1 */ square(t0,t1);
- /* 2^202 - 2^2 */ square(t1,t0);
- /* 2^250 - 2^50 */ for (i = 2;i < 50;i += 2) { square(t0,t1); square(t1,t0); }
- /* 2^250 - 2^0 */ mult(t0,t1,z2_50_0);
-
- /* 2^251 - 2^1 */ square(t1,t0);
- /* 2^252 - 2^2 */ square(t0,t1);
- /* 2^253 - 2^3 */ square(t1,t0);
- /* 2^254 - 2^4 */ square(t0,t1);
- /* 2^255 - 2^5 */ square(t1,t0);
- /* 2^255 - 21 */ mult(out,t1,z11);
-}
-
-static inline int crypto_scalarmult(unsigned char *q,
- const unsigned char *n,
- const unsigned char *p)
-{
- unsigned int work[96];
- unsigned char e[32];
- unsigned int i;
- for (i = 0;i < 32;++i) e[i] = n[i];
- e[0] &= 248;
- e[31] &= 127;
- e[31] |= 64;
- for (i = 0;i < 32;++i) work[i] = p[i];
- mainloop(work,e);
- recip(work + 32,work + 32);
- mult(work + 64,work,work + 32);
- freeze(work + 64);
- for (i = 0;i < 32;++i) q[i] = work[64 + i];
- return 0;
+ unsigned int z2[32];
+ unsigned int z9[32];
+ unsigned int z11[32];
+ unsigned int z2_5_0[32];
+ unsigned int z2_10_0[32];
+ unsigned int z2_20_0[32];
+ unsigned int z2_50_0[32];
+ unsigned int z2_100_0[32];
+ unsigned int t0[32];
+ unsigned int t1[32];
+ int i;
+
+ /* 2 */ square(z2,z);
+ /* 4 */ square(t1,z2);
+ /* 8 */ square(t0,t1);
+ /* 9 */ mult(z9,t0,z);
+ /* 11 */ mult(z11,z9,z2);
+ /* 22 */ square(t0,z11);
+ /* 2^5 - 2^0 = 31 */ mult(z2_5_0,t0,z9);
+
+ /* 2^6 - 2^1 */ square(t0,z2_5_0);
+ /* 2^7 - 2^2 */ square(t1,t0);
+ /* 2^8 - 2^3 */ square(t0,t1);
+ /* 2^9 - 2^4 */ square(t1,t0);
+ /* 2^10 - 2^5 */ square(t0,t1);
+ /* 2^10 - 2^0 */ mult(z2_10_0,t0,z2_5_0);
+
+ /* 2^11 - 2^1 */ square(t0,z2_10_0);
+ /* 2^12 - 2^2 */ square(t1,t0);
+ /* 2^20 - 2^10 */ for (i = 2;i < 10;i += 2) { square(t0,t1); square(t1,t0); }
+ /* 2^20 - 2^0 */ mult(z2_20_0,t1,z2_10_0);
+
+ /* 2^21 - 2^1 */ square(t0,z2_20_0);
+ /* 2^22 - 2^2 */ square(t1,t0);
+ /* 2^40 - 2^20 */ for (i = 2;i < 20;i += 2) { square(t0,t1); square(t1,t0); }
+ /* 2^40 - 2^0 */ mult(t0,t1,z2_20_0);
+
+ /* 2^41 - 2^1 */ square(t1,t0);
+ /* 2^42 - 2^2 */ square(t0,t1);
+ /* 2^50 - 2^10 */ for (i = 2;i < 10;i += 2) { square(t1,t0); square(t0,t1); }
+ /* 2^50 - 2^0 */ mult(z2_50_0,t0,z2_10_0);
+
+ /* 2^51 - 2^1 */ square(t0,z2_50_0);
+ /* 2^52 - 2^2 */ square(t1,t0);
+ /* 2^100 - 2^50 */ for (i = 2;i < 50;i += 2) { square(t0,t1); square(t1,t0); }
+ /* 2^100 - 2^0 */ mult(z2_100_0,t1,z2_50_0);
+
+ /* 2^101 - 2^1 */ square(t1,z2_100_0);
+ /* 2^102 - 2^2 */ square(t0,t1);
+ /* 2^200 - 2^100 */ for (i = 2;i < 100;i += 2) { square(t1,t0); square(t0,t1); }
+ /* 2^200 - 2^0 */ mult(t1,t0,z2_100_0);
+
+ /* 2^201 - 2^1 */ square(t0,t1);
+ /* 2^202 - 2^2 */ square(t1,t0);
+ /* 2^250 - 2^50 */ for (i = 2;i < 50;i += 2) { square(t0,t1); square(t1,t0); }
+ /* 2^250 - 2^0 */ mult(t0,t1,z2_50_0);
+
+ /* 2^251 - 2^1 */ square(t1,t0);
+ /* 2^252 - 2^2 */ square(t0,t1);
+ /* 2^253 - 2^3 */ square(t1,t0);
+ /* 2^254 - 2^4 */ square(t0,t1);
+ /* 2^255 - 2^5 */ square(t1,t0);
+ /* 2^255 - 21 */ mult(out,t1,z11);
+}
+
+int crypto_scalarmult(unsigned char *q,const unsigned char *n,const unsigned char *p)
+{
+ unsigned int work[96];
+ unsigned char e[32];
+ unsigned int i;
+ for (i = 0;i < 32;++i) e[i] = n[i];
+ e[0] &= 248;
+ e[31] &= 127;
+ e[31] |= 64;
+ for (i = 0;i < 32;++i) work[i] = p[i];
+ mainloop(work,e);
+ recip(work + 32,work + 32);
+ mult(work + 64,work,work + 32);
+ freeze(work + 64);
+ for (i = 0;i < 32;++i) q[i] = work[64 + i];
+ return 0;
}
static const unsigned char base[32] = {9};
-
-static inline int crypto_scalarmult_base(unsigned char *q,
- const unsigned char *n)
+int crypto_scalarmult_base(unsigned char *q,const unsigned char *n)
{
- return crypto_scalarmult(q,n,base);
+ return crypto_scalarmult(q,n,base);
}
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
-// This is the Ed25519 stuff from SUPERCOP:
-// http://bench.cr.yp.to/supercop.html
+// Ed25519 ref from: http://bench.cr.yp.to/supercop.html
-// Also public domain, newer version than the Ed25519 found in NaCl
-
-typedef struct
+typedef struct
{
- crypto_uint32 v[32];
+ crypto_uint32 v[32];
}
fe25519;
+typedef struct
+{
+ crypto_uint32 v[32];
+}
+sc25519;
+
+typedef struct
+{
+ crypto_uint32 v[16];
+}
+shortsc25519;
+
+typedef struct
+{
+ fe25519 x;
+ fe25519 y;
+ fe25519 z;
+ fe25519 t;
+} ge25519;
+
+#define ge25519_p3 ge25519
+
+typedef struct
+{
+ fe25519 x;
+ fe25519 z;
+ fe25519 y;
+ fe25519 t;
+} ge25519_p1p1;
+
+typedef struct
+{
+ fe25519 x;
+ fe25519 y;
+ fe25519 z;
+} ge25519_p2;
+
+typedef struct
+{
+ fe25519 x;
+ fe25519 y;
+} ge25519_aff;
+
static void fe25519_sub(fe25519 *r, const fe25519 *x, const fe25519 *y);
-static inline crypto_uint32 equal(crypto_uint32 a,crypto_uint32 b) /* 16-bit inputs */
+crypto_uint32 equal(crypto_uint32 a,crypto_uint32 b) /* 16-bit inputs */
{
- crypto_uint32 x = a ^ b; /* 0: yes; 1..65535: no */
- x -= 1; /* 4294967295: yes; 0..65534: no */
- x >>= 31; /* 1: yes; 0: no */
- return x;
+ crypto_uint32 x = a ^ b; /* 0: yes; 1..65535: no */
+ x -= 1; /* 4294967295: yes; 0..65534: no */
+ x >>= 31; /* 1: yes; 0: no */
+ return x;
}
-static inline crypto_uint32 ge(crypto_uint32 a,crypto_uint32 b) /* 16-bit inputs */
+crypto_uint32 ge(crypto_uint32 a,crypto_uint32 b) /* 16-bit inputs */
{
- unsigned int x = a;
- x -= (unsigned int) b; /* 0..65535: yes; 4294901761..4294967295: no */
- x >>= 31; /* 0: yes; 1: no */
- x ^= 1; /* 1: yes; 0: no */
- return x;
+ unsigned int x = a;
+ x -= (unsigned int) b; /* 0..65535: yes; 4294901761..4294967295: no */
+ x >>= 31; /* 0: yes; 1: no */
+ x ^= 1; /* 1: yes; 0: no */
+ return x;
}
-static inline crypto_uint32 times19(crypto_uint32 a)
+crypto_uint32 times19(crypto_uint32 a)
{
- return (a << 4) + (a << 1) + a;
+ return (a << 4) + (a << 1) + a;
}
-static inline crypto_uint32 times38(crypto_uint32 a)
+crypto_uint32 times38(crypto_uint32 a)
{
- return (a << 5) + (a << 2) + (a << 1);
+ return (a << 5) + (a << 2) + (a << 1);
}
-static inline void reduce_add_sub(fe25519 *r)
+void reduce_add_sub(fe25519 *r)
{
- crypto_uint32 t;
- int i,rep;
+ crypto_uint32 t;
+ int i,rep;
- for(rep=0;rep<4;rep++)
- {
- t = r->v[31] >> 7;
- r->v[31] &= 127;
- t = times19(t);
- r->v[0] += t;
- for(i=0;i<31;i++)
- {
- t = r->v[i] >> 8;
- r->v[i+1] += t;
- r->v[i] &= 255;
- }
- }
+ for(rep=0;rep<4;rep++)
+ {
+ t = r->v[31] >> 7;
+ r->v[31] &= 127;
+ t = times19(t);
+ r->v[0] += t;
+ for(i=0;i<31;i++)
+ {
+ t = r->v[i] >> 8;
+ r->v[i+1] += t;
+ r->v[i] &= 255;
+ }
+ }
}
-static inline void reduce_mul(fe25519 *r)
+void reduce_mul(fe25519 *r)
{
- crypto_uint32 t;
- int i,rep;
+ crypto_uint32 t;
+ int i,rep;
- for(rep=0;rep<2;rep++)
- {
- t = r->v[31] >> 7;
- r->v[31] &= 127;
- t = times19(t);
- r->v[0] += t;
- for(i=0;i<31;i++)
- {
- t = r->v[i] >> 8;
- r->v[i+1] += t;
- r->v[i] &= 255;
- }
- }
+ for(rep=0;rep<2;rep++)
+ {
+ t = r->v[31] >> 7;
+ r->v[31] &= 127;
+ t = times19(t);
+ r->v[0] += t;
+ for(i=0;i<31;i++)
+ {
+ t = r->v[i] >> 8;
+ r->v[i+1] += t;
+ r->v[i] &= 255;
+ }
+ }
}
/* reduction modulo 2^255-19 */
-static inline void fe25519_freeze(fe25519 *r)
+void fe25519_freeze(fe25519 *r)
{
- int i;
- crypto_uint32 m = equal(r->v[31],127);
- for(i=30;i>0;i--)
- m &= equal(r->v[i],255);
- m &= ge(r->v[0],237);
+ int i;
+ crypto_uint32 m = equal(r->v[31],127);
+ for(i=30;i>0;i--)
+ m &= equal(r->v[i],255);
+ m &= ge(r->v[0],237);
- m = -m;
+ m = -m;
- r->v[31] -= m&127;
- for(i=30;i>0;i--)
- r->v[i] -= m&255;
- r->v[0] -= m&237;
+ r->v[31] -= m&127;
+ for(i=30;i>0;i--)
+ r->v[i] -= m&255;
+ r->v[0] -= m&237;
}
-static inline void fe25519_unpack(fe25519 *r, const unsigned char x[32])
+void fe25519_unpack(fe25519 *r, const unsigned char x[32])
{
- int i;
- for(i=0;i<32;i++) r->v[i] = x[i];
- r->v[31] &= 127;
+ int i;
+ for(i=0;i<32;i++) r->v[i] = x[i];
+ r->v[31] &= 127;
}
/* Assumes input x being reduced below 2^255 */
-static inline void fe25519_pack(unsigned char r[32], const fe25519 *x)
+void fe25519_pack(unsigned char r[32], const fe25519 *x)
{
- int i;
- fe25519 y = *x;
- fe25519_freeze(&y);
- for(i=0;i<32;i++)
- r[i] = y.v[i];
+ int i;
+ fe25519 y = *x;
+ fe25519_freeze(&y);
+ for(i=0;i<32;i++)
+ r[i] = y.v[i];
}
-#if 0
-static int fe25519_iszero(const fe25519 *x)
-{
- int i;
- int r;
- fe25519 t = *x;
- fe25519_freeze(&t);
- r = equal(t.v[0],0);
- for(i=1;i<32;i++)
- r &= equal(t.v[i],0);
- return r;
-}
-#endif
-
-static inline int fe25519_iseq_vartime(const fe25519 *x, const fe25519 *y)
+int fe25519_iseq_vartime(const fe25519 *x, const fe25519 *y)
{
- int i;
- fe25519 t1 = *x;
- fe25519 t2 = *y;
- fe25519_freeze(&t1);
- fe25519_freeze(&t2);
- for(i=0;i<32;i++)
- if(t1.v[i] != t2.v[i]) return 0;
- return 1;
+ int i;
+ fe25519 t1 = *x;
+ fe25519 t2 = *y;
+ fe25519_freeze(&t1);
+ fe25519_freeze(&t2);
+ for(i=0;i<32;i++)
+ if(t1.v[i] != t2.v[i]) return 0;
+ return 1;
}
-static inline void fe25519_cmov(fe25519 *r, const fe25519 *x, unsigned char b)
+void fe25519_cmov(fe25519 *r, const fe25519 *x, unsigned char b)
{
- int i;
- crypto_uint32 mask = b;
- mask = -mask;
- for(i=0;i<32;i++) r->v[i] ^= mask & (x->v[i] ^ r->v[i]);
+ int i;
+ crypto_uint32 mask = b;
+ mask = -mask;
+ for(i=0;i<32;i++) r->v[i] ^= mask & (x->v[i] ^ r->v[i]);
}
-static inline unsigned char fe25519_getparity(const fe25519 *x)
+unsigned char fe25519_getparity(const fe25519 *x)
{
- fe25519 t = *x;
- fe25519_freeze(&t);
- return t.v[0] & 1;
+ fe25519 t = *x;
+ fe25519_freeze(&t);
+ return t.v[0] & 1;
}
-static inline void fe25519_setone(fe25519 *r)
+void fe25519_setone(fe25519 *r)
{
- int i;
- r->v[0] = 1;
- for(i=1;i<32;i++) r->v[i]=0;
+ int i;
+ r->v[0] = 1;
+ for(i=1;i<32;i++) r->v[i]=0;
}
-static inline void fe25519_setzero(fe25519 *r)
+void fe25519_setzero(fe25519 *r)
{
- int i;
- for(i=0;i<32;i++) r->v[i]=0;
+ int i;
+ for(i=0;i<32;i++) r->v[i]=0;
}
-static inline void fe25519_neg(fe25519 *r, const fe25519 *x)
+void fe25519_neg(fe25519 *r, const fe25519 *x)
{
- fe25519 t;
- int i;
- for(i=0;i<32;i++) t.v[i]=x->v[i];
- fe25519_setzero(r);
- fe25519_sub(r, r, &t);
+ fe25519 t;
+ int i;
+ for(i=0;i<32;i++) t.v[i]=x->v[i];
+ fe25519_setzero(r);
+ fe25519_sub(r, r, &t);
}
-static inline void fe25519_add(fe25519 *r, const fe25519 *x, const fe25519 *y)
+void fe25519_add(fe25519 *r, const fe25519 *x, const fe25519 *y)
{
- int i;
- for(i=0;i<32;i++) r->v[i] = x->v[i] + y->v[i];
- reduce_add_sub(r);
+ int i;
+ for(i=0;i<32;i++) r->v[i] = x->v[i] + y->v[i];
+ reduce_add_sub(r);
}
-static inline void fe25519_sub(fe25519 *r, const fe25519 *x, const fe25519 *y)
+void fe25519_sub(fe25519 *r, const fe25519 *x, const fe25519 *y)
{
- int i;
- crypto_uint32 t[32];
- t[0] = x->v[0] + 0x1da;
- t[31] = x->v[31] + 0xfe;
- for(i=1;i<31;i++) t[i] = x->v[i] + 0x1fe;
- for(i=0;i<32;i++) r->v[i] = t[i] - y->v[i];
- reduce_add_sub(r);
+ int i;
+ crypto_uint32 t[32];
+ t[0] = x->v[0] + 0x1da;
+ t[31] = x->v[31] + 0xfe;
+ for(i=1;i<31;i++) t[i] = x->v[i] + 0x1fe;
+ for(i=0;i<32;i++) r->v[i] = t[i] - y->v[i];
+ reduce_add_sub(r);
}
-static inline void fe25519_mul(fe25519 *r, const fe25519 *x, const fe25519 *y)
+void fe25519_mul(fe25519 *r, const fe25519 *x, const fe25519 *y)
{
- int i,j;
- crypto_uint32 t[63];
- for(i=0;i<63;i++)t[i] = 0;
+ int i,j;
+ crypto_uint32 t[63];
+ for(i=0;i<63;i++)t[i] = 0;
- for(i=0;i<32;i++)
- for(j=0;j<32;j++)
- t[i+j] += x->v[i] * y->v[j];
+ for(i=0;i<32;i++)
+ for(j=0;j<32;j++)
+ t[i+j] += x->v[i] * y->v[j];
- for(i=32;i<63;i++)
- r->v[i-32] = t[i-32] + times38(t[i]);
- r->v[31] = t[31]; /* result now in r[0]...r[31] */
+ for(i=32;i<63;i++)
+ r->v[i-32] = t[i-32] + times38(t[i]);
+ r->v[31] = t[31]; /* result now in r[0]...r[31] */
- reduce_mul(r);
+ reduce_mul(r);
}
-static inline void fe25519_square(fe25519 *r, const fe25519 *x)
+void fe25519_square(fe25519 *r, const fe25519 *x)
{
- fe25519_mul(r, x, x);
+ fe25519_mul(r, x, x);
}
-static void fe25519_invert(fe25519 *r, const fe25519 *x)
+void fe25519_invert(fe25519 *r, const fe25519 *x)
{
- fe25519 z2;
- fe25519 z9;
- fe25519 z11;
- fe25519 z2_5_0;
- fe25519 z2_10_0;
- fe25519 z2_20_0;
- fe25519 z2_50_0;
- fe25519 z2_100_0;
- fe25519 t0;
- fe25519 t1;
- int i;
-
- /* 2 */ fe25519_square(&z2,x);
- /* 4 */ fe25519_square(&t1,&z2);
- /* 8 */ fe25519_square(&t0,&t1);
- /* 9 */ fe25519_mul(&z9,&t0,x);
- /* 11 */ fe25519_mul(&z11,&z9,&z2);
- /* 22 */ fe25519_square(&t0,&z11);
- /* 2^5 - 2^0 = 31 */ fe25519_mul(&z2_5_0,&t0,&z9);
+ fe25519 z2;
+ fe25519 z9;
+ fe25519 z11;
+ fe25519 z2_5_0;
+ fe25519 z2_10_0;
+ fe25519 z2_20_0;
+ fe25519 z2_50_0;
+ fe25519 z2_100_0;
+ fe25519 t0;
+ fe25519 t1;
+ int i;
- /* 2^6 - 2^1 */ fe25519_square(&t0,&z2_5_0);
- /* 2^7 - 2^2 */ fe25519_square(&t1,&t0);
- /* 2^8 - 2^3 */ fe25519_square(&t0,&t1);
- /* 2^9 - 2^4 */ fe25519_square(&t1,&t0);
- /* 2^10 - 2^5 */ fe25519_square(&t0,&t1);
- /* 2^10 - 2^0 */ fe25519_mul(&z2_10_0,&t0,&z2_5_0);
+ /* 2 */ fe25519_square(&z2,x);
+ /* 4 */ fe25519_square(&t1,&z2);
+ /* 8 */ fe25519_square(&t0,&t1);
+ /* 9 */ fe25519_mul(&z9,&t0,x);
+ /* 11 */ fe25519_mul(&z11,&z9,&z2);
+ /* 22 */ fe25519_square(&t0,&z11);
+ /* 2^5 - 2^0 = 31 */ fe25519_mul(&z2_5_0,&t0,&z9);
- /* 2^11 - 2^1 */ fe25519_square(&t0,&z2_10_0);
- /* 2^12 - 2^2 */ fe25519_square(&t1,&t0);
- /* 2^20 - 2^10 */ for (i = 2;i < 10;i += 2) { fe25519_square(&t0,&t1); fe25519_square(&t1,&t0); }
- /* 2^20 - 2^0 */ fe25519_mul(&z2_20_0,&t1,&z2_10_0);
+ /* 2^6 - 2^1 */ fe25519_square(&t0,&z2_5_0);
+ /* 2^7 - 2^2 */ fe25519_square(&t1,&t0);
+ /* 2^8 - 2^3 */ fe25519_square(&t0,&t1);
+ /* 2^9 - 2^4 */ fe25519_square(&t1,&t0);
+ /* 2^10 - 2^5 */ fe25519_square(&t0,&t1);
+ /* 2^10 - 2^0 */ fe25519_mul(&z2_10_0,&t0,&z2_5_0);
- /* 2^21 - 2^1 */ fe25519_square(&t0,&z2_20_0);
- /* 2^22 - 2^2 */ fe25519_square(&t1,&t0);
- /* 2^40 - 2^20 */ for (i = 2;i < 20;i += 2) { fe25519_square(&t0,&t1); fe25519_square(&t1,&t0); }
- /* 2^40 - 2^0 */ fe25519_mul(&t0,&t1,&z2_20_0);
+ /* 2^11 - 2^1 */ fe25519_square(&t0,&z2_10_0);
+ /* 2^12 - 2^2 */ fe25519_square(&t1,&t0);
+ /* 2^20 - 2^10 */ for (i = 2;i < 10;i += 2) { fe25519_square(&t0,&t1); fe25519_square(&t1,&t0); }
+ /* 2^20 - 2^0 */ fe25519_mul(&z2_20_0,&t1,&z2_10_0);
- /* 2^41 - 2^1 */ fe25519_square(&t1,&t0);
- /* 2^42 - 2^2 */ fe25519_square(&t0,&t1);
- /* 2^50 - 2^10 */ for (i = 2;i < 10;i += 2) { fe25519_square(&t1,&t0); fe25519_square(&t0,&t1); }
- /* 2^50 - 2^0 */ fe25519_mul(&z2_50_0,&t0,&z2_10_0);
+ /* 2^21 - 2^1 */ fe25519_square(&t0,&z2_20_0);
+ /* 2^22 - 2^2 */ fe25519_square(&t1,&t0);
+ /* 2^40 - 2^20 */ for (i = 2;i < 20;i += 2) { fe25519_square(&t0,&t1); fe25519_square(&t1,&t0); }
+ /* 2^40 - 2^0 */ fe25519_mul(&t0,&t1,&z2_20_0);
- /* 2^51 - 2^1 */ fe25519_square(&t0,&z2_50_0);
- /* 2^52 - 2^2 */ fe25519_square(&t1,&t0);
- /* 2^100 - 2^50 */ for (i = 2;i < 50;i += 2) { fe25519_square(&t0,&t1); fe25519_square(&t1,&t0); }
- /* 2^100 - 2^0 */ fe25519_mul(&z2_100_0,&t1,&z2_50_0);
+ /* 2^41 - 2^1 */ fe25519_square(&t1,&t0);
+ /* 2^42 - 2^2 */ fe25519_square(&t0,&t1);
+ /* 2^50 - 2^10 */ for (i = 2;i < 10;i += 2) { fe25519_square(&t1,&t0); fe25519_square(&t0,&t1); }
+ /* 2^50 - 2^0 */ fe25519_mul(&z2_50_0,&t0,&z2_10_0);
- /* 2^101 - 2^1 */ fe25519_square(&t1,&z2_100_0);
- /* 2^102 - 2^2 */ fe25519_square(&t0,&t1);
- /* 2^200 - 2^100 */ for (i = 2;i < 100;i += 2) { fe25519_square(&t1,&t0); fe25519_square(&t0,&t1); }
- /* 2^200 - 2^0 */ fe25519_mul(&t1,&t0,&z2_100_0);
-
- /* 2^201 - 2^1 */ fe25519_square(&t0,&t1);
- /* 2^202 - 2^2 */ fe25519_square(&t1,&t0);
- /* 2^250 - 2^50 */ for (i = 2;i < 50;i += 2) { fe25519_square(&t0,&t1); fe25519_square(&t1,&t0); }
- /* 2^250 - 2^0 */ fe25519_mul(&t0,&t1,&z2_50_0);
+ /* 2^51 - 2^1 */ fe25519_square(&t0,&z2_50_0);
+ /* 2^52 - 2^2 */ fe25519_square(&t1,&t0);
+ /* 2^100 - 2^50 */ for (i = 2;i < 50;i += 2) { fe25519_square(&t0,&t1); fe25519_square(&t1,&t0); }
+ /* 2^100 - 2^0 */ fe25519_mul(&z2_100_0,&t1,&z2_50_0);
- /* 2^251 - 2^1 */ fe25519_square(&t1,&t0);
- /* 2^252 - 2^2 */ fe25519_square(&t0,&t1);
- /* 2^253 - 2^3 */ fe25519_square(&t1,&t0);
- /* 2^254 - 2^4 */ fe25519_square(&t0,&t1);
- /* 2^255 - 2^5 */ fe25519_square(&t1,&t0);
- /* 2^255 - 21 */ fe25519_mul(r,&t1,&z11);
-}
-
-static void fe25519_pow2523(fe25519 *r, const fe25519 *x)
-{
- fe25519 z2;
- fe25519 z9;
- fe25519 z11;
- fe25519 z2_5_0;
- fe25519 z2_10_0;
- fe25519 z2_20_0;
- fe25519 z2_50_0;
- fe25519 z2_100_0;
- fe25519 t;
- int i;
-
- /* 2 */ fe25519_square(&z2,x);
- /* 4 */ fe25519_square(&t,&z2);
- /* 8 */ fe25519_square(&t,&t);
- /* 9 */ fe25519_mul(&z9,&t,x);
- /* 11 */ fe25519_mul(&z11,&z9,&z2);
- /* 22 */ fe25519_square(&t,&z11);
- /* 2^5 - 2^0 = 31 */ fe25519_mul(&z2_5_0,&t,&z9);
-
- /* 2^6 - 2^1 */ fe25519_square(&t,&z2_5_0);
- /* 2^10 - 2^5 */ for (i = 1;i < 5;i++) { fe25519_square(&t,&t); }
- /* 2^10 - 2^0 */ fe25519_mul(&z2_10_0,&t,&z2_5_0);
-
- /* 2^11 - 2^1 */ fe25519_square(&t,&z2_10_0);
- /* 2^20 - 2^10 */ for (i = 1;i < 10;i++) { fe25519_square(&t,&t); }
- /* 2^20 - 2^0 */ fe25519_mul(&z2_20_0,&t,&z2_10_0);
-
- /* 2^21 - 2^1 */ fe25519_square(&t,&z2_20_0);
- /* 2^40 - 2^20 */ for (i = 1;i < 20;i++) { fe25519_square(&t,&t); }
- /* 2^40 - 2^0 */ fe25519_mul(&t,&t,&z2_20_0);
-
- /* 2^41 - 2^1 */ fe25519_square(&t,&t);
- /* 2^50 - 2^10 */ for (i = 1;i < 10;i++) { fe25519_square(&t,&t); }
- /* 2^50 - 2^0 */ fe25519_mul(&z2_50_0,&t,&z2_10_0);
-
- /* 2^51 - 2^1 */ fe25519_square(&t,&z2_50_0);
- /* 2^100 - 2^50 */ for (i = 1;i < 50;i++) { fe25519_square(&t,&t); }
- /* 2^100 - 2^0 */ fe25519_mul(&z2_100_0,&t,&z2_50_0);
-
- /* 2^101 - 2^1 */ fe25519_square(&t,&z2_100_0);
- /* 2^200 - 2^100 */ for (i = 1;i < 100;i++) { fe25519_square(&t,&t); }
- /* 2^200 - 2^0 */ fe25519_mul(&t,&t,&z2_100_0);
-
- /* 2^201 - 2^1 */ fe25519_square(&t,&t);
- /* 2^250 - 2^50 */ for (i = 1;i < 50;i++) { fe25519_square(&t,&t); }
- /* 2^250 - 2^0 */ fe25519_mul(&t,&t,&z2_50_0);
-
- /* 2^251 - 2^1 */ fe25519_square(&t,&t);
- /* 2^252 - 2^2 */ fe25519_square(&t,&t);
- /* 2^252 - 3 */ fe25519_mul(r,&t,x);
-}
-
-typedef struct
-{
- crypto_uint32 v[32];
-}
-sc25519;
-
-typedef struct
-{
- crypto_uint32 v[16];
-}
-shortsc25519;
+ /* 2^101 - 2^1 */ fe25519_square(&t1,&z2_100_0);
+ /* 2^102 - 2^2 */ fe25519_square(&t0,&t1);
+ /* 2^200 - 2^100 */ for (i = 2;i < 100;i += 2) { fe25519_square(&t1,&t0); fe25519_square(&t0,&t1); }
+ /* 2^200 - 2^0 */ fe25519_mul(&t1,&t0,&z2_100_0);
-static const crypto_uint32 m[32] = {0xED, 0xD3, 0xF5, 0x5C, 0x1A, 0x63, 0x12, 0x58, 0xD6, 0x9C, 0xF7, 0xA2, 0xDE, 0xF9, 0xDE, 0x14,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10};
+ /* 2^201 - 2^1 */ fe25519_square(&t0,&t1);
+ /* 2^202 - 2^2 */ fe25519_square(&t1,&t0);
+ /* 2^250 - 2^50 */ for (i = 2;i < 50;i += 2) { fe25519_square(&t0,&t1); fe25519_square(&t1,&t0); }
+ /* 2^250 - 2^0 */ fe25519_mul(&t0,&t1,&z2_50_0);
-static const crypto_uint32 mu[33] = {0x1B, 0x13, 0x2C, 0x0A, 0xA3, 0xE5, 0x9C, 0xED, 0xA7, 0x29, 0x63, 0x08, 0x5D, 0x21, 0x06, 0x21,
- 0xEB, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F};
+ /* 2^251 - 2^1 */ fe25519_square(&t1,&t0);
+ /* 2^252 - 2^2 */ fe25519_square(&t0,&t1);
+ /* 2^253 - 2^3 */ fe25519_square(&t1,&t0);
+ /* 2^254 - 2^4 */ fe25519_square(&t0,&t1);
+ /* 2^255 - 2^5 */ fe25519_square(&t1,&t0);
+ /* 2^255 - 21 */ fe25519_mul(r,&t1,&z11);
+}
-static inline crypto_uint32 lt(crypto_uint32 a,crypto_uint32 b) /* 16-bit inputs */
+void fe25519_pow2523(fe25519 *r, const fe25519 *x)
+{
+ fe25519 z2;
+ fe25519 z9;
+ fe25519 z11;
+ fe25519 z2_5_0;
+ fe25519 z2_10_0;
+ fe25519 z2_20_0;
+ fe25519 z2_50_0;
+ fe25519 z2_100_0;
+ fe25519 t;
+ int i;
+
+ /* 2 */ fe25519_square(&z2,x);
+ /* 4 */ fe25519_square(&t,&z2);
+ /* 8 */ fe25519_square(&t,&t);
+ /* 9 */ fe25519_mul(&z9,&t,x);
+ /* 11 */ fe25519_mul(&z11,&z9,&z2);
+ /* 22 */ fe25519_square(&t,&z11);
+ /* 2^5 - 2^0 = 31 */ fe25519_mul(&z2_5_0,&t,&z9);
+
+ /* 2^6 - 2^1 */ fe25519_square(&t,&z2_5_0);
+ /* 2^10 - 2^5 */ for (i = 1;i < 5;i++) { fe25519_square(&t,&t); }
+ /* 2^10 - 2^0 */ fe25519_mul(&z2_10_0,&t,&z2_5_0);
+
+ /* 2^11 - 2^1 */ fe25519_square(&t,&z2_10_0);
+ /* 2^20 - 2^10 */ for (i = 1;i < 10;i++) { fe25519_square(&t,&t); }
+ /* 2^20 - 2^0 */ fe25519_mul(&z2_20_0,&t,&z2_10_0);
+
+ /* 2^21 - 2^1 */ fe25519_square(&t,&z2_20_0);
+ /* 2^40 - 2^20 */ for (i = 1;i < 20;i++) { fe25519_square(&t,&t); }
+ /* 2^40 - 2^0 */ fe25519_mul(&t,&t,&z2_20_0);
+
+ /* 2^41 - 2^1 */ fe25519_square(&t,&t);
+ /* 2^50 - 2^10 */ for (i = 1;i < 10;i++) { fe25519_square(&t,&t); }
+ /* 2^50 - 2^0 */ fe25519_mul(&z2_50_0,&t,&z2_10_0);
+
+ /* 2^51 - 2^1 */ fe25519_square(&t,&z2_50_0);
+ /* 2^100 - 2^50 */ for (i = 1;i < 50;i++) { fe25519_square(&t,&t); }
+ /* 2^100 - 2^0 */ fe25519_mul(&z2_100_0,&t,&z2_50_0);
+
+ /* 2^101 - 2^1 */ fe25519_square(&t,&z2_100_0);
+ /* 2^200 - 2^100 */ for (i = 1;i < 100;i++) { fe25519_square(&t,&t); }
+ /* 2^200 - 2^0 */ fe25519_mul(&t,&t,&z2_100_0);
+
+ /* 2^201 - 2^1 */ fe25519_square(&t,&t);
+ /* 2^250 - 2^50 */ for (i = 1;i < 50;i++) { fe25519_square(&t,&t); }
+ /* 2^250 - 2^0 */ fe25519_mul(&t,&t,&z2_50_0);
+
+ /* 2^251 - 2^1 */ fe25519_square(&t,&t);
+ /* 2^252 - 2^2 */ fe25519_square(&t,&t);
+ /* 2^252 - 3 */ fe25519_mul(r,&t,x);
+}
+
+static const crypto_uint32 m[32] = {0xED, 0xD3, 0xF5, 0x5C, 0x1A, 0x63, 0x12, 0x58, 0xD6, 0x9C, 0xF7, 0xA2, 0xDE, 0xF9, 0xDE, 0x14,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10};
+
+static const crypto_uint32 mu[33] = {0x1B, 0x13, 0x2C, 0x0A, 0xA3, 0xE5, 0x9C, 0xED, 0xA7, 0x29, 0x63, 0x08, 0x5D, 0x21, 0x06, 0x21,
+ 0xEB, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F};
+
+crypto_uint32 lt(crypto_uint32 a,crypto_uint32 b) /* 16-bit inputs */
{
- unsigned int x = a;
- x -= (unsigned int) b; /* 0..65535: no; 4294901761..4294967295: yes */
- x >>= 31; /* 0: no; 1: yes */
- return x;
+ unsigned int x = a;
+ x -= (unsigned int) b; /* 0..65535: no; 4294901761..4294967295: yes */
+ x >>= 31; /* 0: no; 1: yes */
+ return x;
}
/* Reduce coefficients of r before calling reduce_add_sub */
-static inline void reduce_add_sub(sc25519 *r)
-{
- crypto_uint32 pb = 0;
- crypto_uint32 b;
- crypto_uint32 mask;
- int i;
- unsigned char t[32];
-
- for(i=0;i<32;i++)
- {
- pb += m[i];
- b = lt(r->v[i],pb);
- t[i] = r->v[i]-pb+(b<<8);
- pb = b;
- }
- mask = b - 1;
- for(i=0;i<32;i++)
- r->v[i] ^= mask & (r->v[i] ^ t[i]);
+void reduce_add_sub(sc25519 *r)
+{
+ crypto_uint32 pb = 0;
+ crypto_uint32 b;
+ crypto_uint32 mask;
+ int i;
+ unsigned char t[32];
+
+ for(i=0;i<32;i++)
+ {
+ pb += m[i];
+ b = lt(r->v[i],pb);
+ t[i] = r->v[i]-pb+(b<<8);
+ pb = b;
+ }
+ mask = b - 1;
+ for(i=0;i<32;i++)
+ r->v[i] ^= mask & (r->v[i] ^ t[i]);
}
/* Reduce coefficients of x before calling barrett_reduce */
-static inline void barrett_reduce(sc25519 *r, const crypto_uint32 x[64])
-{
- /* See HAC, Alg. 14.42 */
- int i,j;
- crypto_uint32 q2[66];
- crypto_uint32 *q3 = q2 + 33;
- crypto_uint32 r1[33];
- crypto_uint32 r2[33];
- crypto_uint32 carry;
- crypto_uint32 pb = 0;
- crypto_uint32 b;
-
- for (i = 0;i < 66;++i) q2[i] = 0;
- for (i = 0;i < 33;++i) r2[i] = 0;
-
- for(i=0;i<33;i++)
- for(j=0;j<33;j++)
- if(i+j >= 31) q2[i+j] += mu[i]*x[j+31];
- carry = q2[31] >> 8;
- q2[32] += carry;
- carry = q2[32] >> 8;
- q2[33] += carry;
-
- for(i=0;i<33;i++)r1[i] = x[i];
- for(i=0;i<32;i++)
- for(j=0;j<33;j++)
- if(i+j < 33) r2[i+j] += m[i]*q3[j];
-
- for(i=0;i<32;i++)
- {
- carry = r2[i] >> 8;
- r2[i+1] += carry;
- r2[i] &= 0xff;
- }
-
- for(i=0;i<32;i++)
- {
- pb += r2[i];
- b = lt(r1[i],pb);
- r->v[i] = r1[i]-pb+(b<<8);
- pb = b;
- }
-
- /* XXX: Can it really happen that r<0?, See HAC, Alg 14.42, Step 3
- * If so: Handle it here!
- */
-
- reduce_add_sub(r);
- reduce_add_sub(r);
-}
-
-static inline void sc25519_from32bytes(sc25519 *r, const unsigned char x[32])
-{
- int i;
- crypto_uint32 t[64];
- for(i=0;i<32;i++) t[i] = x[i];
- for(i=32;i<64;++i) t[i] = 0;
- barrett_reduce(r, t);
-}
-
-#if 0
-static void shortsc25519_from16bytes(shortsc25519 *r, const unsigned char x[16])
-{
- int i;
- for(i=0;i<16;i++) r->v[i] = x[i];
-}
-#endif
-
-static inline void sc25519_from64bytes(sc25519 *r, const unsigned char x[64])
-{
- int i;
- crypto_uint32 t[64];
- for(i=0;i<64;i++) t[i] = x[i];
- barrett_reduce(r, t);
-}
+void barrett_reduce(sc25519 *r, const crypto_uint32 x[64])
+{
+ /* See HAC, Alg. 14.42 */
+ int i,j;
+ crypto_uint32 q2[66];
+ crypto_uint32 *q3 = q2 + 33;
+ crypto_uint32 r1[33];
+ crypto_uint32 r2[33];
+ crypto_uint32 carry;
+ crypto_uint32 pb = 0;
+ crypto_uint32 b;
+
+ for (i = 0;i < 66;++i) q2[i] = 0;
+ for (i = 0;i < 33;++i) r2[i] = 0;
+
+ for(i=0;i<33;i++)
+ for(j=0;j<33;j++)
+ if(i+j >= 31) q2[i+j] += mu[i]*x[j+31];
+ carry = q2[31] >> 8;
+ q2[32] += carry;
+ carry = q2[32] >> 8;
+ q2[33] += carry;
+
+ for(i=0;i<33;i++)r1[i] = x[i];
+ for(i=0;i<32;i++)
+ for(j=0;j<33;j++)
+ if(i+j < 33) r2[i+j] += m[i]*q3[j];
+
+ for(i=0;i<32;i++)
+ {
+ carry = r2[i] >> 8;
+ r2[i+1] += carry;
+ r2[i] &= 0xff;
+ }
-#if 0
-static void sc25519_from_shortsc(sc25519 *r, const shortsc25519 *x)
-{
- int i;
- for(i=0;i<16;i++)
- r->v[i] = x->v[i];
- for(i=0;i<16;i++)
- r->v[16+i] = 0;
-}
-#endif
+ for(i=0;i<32;i++)
+ {
+ pb += r2[i];
+ b = lt(r1[i],pb);
+ r->v[i] = r1[i]-pb+(b<<8);
+ pb = b;
+ }
-static inline void sc25519_to32bytes(unsigned char r[32], const sc25519 *x)
-{
- int i;
- for(i=0;i<32;i++) r[i] = x->v[i];
-}
+ /* XXX: Can it really happen that r<0?, See HAC, Alg 14.42, Step 3
+ * If so: Handle it here!
+ */
-#if 0
-static int sc25519_iszero_vartime(const sc25519 *x)
-{
- int i;
- for(i=0;i<32;i++)
- if(x->v[i] != 0) return 0;
- return 1;
+ reduce_add_sub(r);
+ reduce_add_sub(r);
}
-#endif
-#if 0
-static int sc25519_isshort_vartime(const sc25519 *x)
+void sc25519_from32bytes(sc25519 *r, const unsigned char x[32])
{
- int i;
- for(i=31;i>15;i--)
- if(x->v[i] != 0) return 0;
- return 1;
+ int i;
+ crypto_uint32 t[64];
+ for(i=0;i<32;i++) t[i] = x[i];
+ for(i=32;i<64;++i) t[i] = 0;
+ barrett_reduce(r, t);
}
-#endif
-#if 0
-static int sc25519_lt_vartime(const sc25519 *x, const sc25519 *y)
+void sc25519_from64bytes(sc25519 *r, const unsigned char x[64])
{
- int i;
- for(i=31;i>=0;i--)
- {
- if(x->v[i] < y->v[i]) return 1;
- if(x->v[i] > y->v[i]) return 0;
- }
- return 0;
+ int i;
+ crypto_uint32 t[64];
+ for(i=0;i<64;i++) t[i] = x[i];
+ barrett_reduce(r, t);
}
-#endif
-static inline void sc25519_add(sc25519 *r, const sc25519 *x, const sc25519 *y)
+void sc25519_to32bytes(unsigned char r[32], const sc25519 *x)
{
- int i, carry;
- for(i=0;i<32;i++) r->v[i] = x->v[i] + y->v[i];
- for(i=0;i<31;i++)
- {
- carry = r->v[i] >> 8;
- r->v[i+1] += carry;
- r->v[i] &= 0xff;
- }
- reduce_add_sub(r);
+ int i;
+ for(i=0;i<32;i++) r[i] = x->v[i];
}
-#if 0
-static void sc25519_sub_nored(sc25519 *r, const sc25519 *x, const sc25519 *y)
+void sc25519_add(sc25519 *r, const sc25519 *x, const sc25519 *y)
{
- crypto_uint32 b = 0;
- crypto_uint32 t;
- int i;
- for(i=0;i<32;i++)
- {
- t = x->v[i] - y->v[i] - b;
- r->v[i] = t & 255;
- b = (t >> 8) & 1;
- }
+ int i, carry;
+ for(i=0;i<32;i++) r->v[i] = x->v[i] + y->v[i];
+ for(i=0;i<31;i++)
+ {
+ carry = r->v[i] >> 8;
+ r->v[i+1] += carry;
+ r->v[i] &= 0xff;
+ }
+ reduce_add_sub(r);
}
-#endif
-static inline void sc25519_mul(sc25519 *r, const sc25519 *x, const sc25519 *y)
+void sc25519_mul(sc25519 *r, const sc25519 *x, const sc25519 *y)
{
- int i,j,carry;
- crypto_uint32 t[64];
- for(i=0;i<64;i++)t[i] = 0;
+ int i,j,carry;
+ crypto_uint32 t[64];
+ for(i=0;i<64;i++)t[i] = 0;
- for(i=0;i<32;i++)
- for(j=0;j<32;j++)
- t[i+j] += x->v[i] * y->v[j];
+ for(i=0;i<32;i++)
+ for(j=0;j<32;j++)
+ t[i+j] += x->v[i] * y->v[j];
- /* Reduce coefficients */
- for(i=0;i<63;i++)
- {
- carry = t[i] >> 8;
- t[i+1] += carry;
- t[i] &= 0xff;
- }
-
- barrett_reduce(r, t);
-}
-
-#if 0
-static void sc25519_mul_shortsc(sc25519 *r, const sc25519 *x, const shortsc25519 *y)
-{
- sc25519 t;
- sc25519_from_shortsc(&t, y);
- sc25519_mul(r, x, &t);
-}
-#endif
+ /* Reduce coefficients */
+ for(i=0;i<63;i++)
+ {
+ carry = t[i] >> 8;
+ t[i+1] += carry;
+ t[i] &= 0xff;
+ }
-static inline void sc25519_window3(signed char r[85], const sc25519 *s)
-{
- char carry;
- int i;
- for(i=0;i<10;i++)
- {
- r[8*i+0] = s->v[3*i+0] & 7;
- r[8*i+1] = (s->v[3*i+0] >> 3) & 7;
- r[8*i+2] = (s->v[3*i+0] >> 6) & 7;
- r[8*i+2] ^= (s->v[3*i+1] << 2) & 7;
- r[8*i+3] = (s->v[3*i+1] >> 1) & 7;
- r[8*i+4] = (s->v[3*i+1] >> 4) & 7;
- r[8*i+5] = (s->v[3*i+1] >> 7) & 7;
- r[8*i+5] ^= (s->v[3*i+2] << 1) & 7;
- r[8*i+6] = (s->v[3*i+2] >> 2) & 7;
- r[8*i+7] = (s->v[3*i+2] >> 5) & 7;
- }
- r[8*i+0] = s->v[3*i+0] & 7;
- r[8*i+1] = (s->v[3*i+0] >> 3) & 7;
- r[8*i+2] = (s->v[3*i+0] >> 6) & 7;
- r[8*i+2] ^= (s->v[3*i+1] << 2) & 7;
- r[8*i+3] = (s->v[3*i+1] >> 1) & 7;
- r[8*i+4] = (s->v[3*i+1] >> 4) & 7;
-
- /* Making it signed */
- carry = 0;
- for(i=0;i<84;i++)
- {
- r[i] += carry;
- r[i+1] += r[i] >> 3;
- r[i] &= 7;
- carry = r[i] >> 2;
- r[i] -= carry<<3;
- }
- r[84] += carry;
-}
-
-#if 0
-static void sc25519_window5(signed char r[51], const sc25519 *s)
-{
- char carry;
- int i;
- for(i=0;i<6;i++)
- {
- r[8*i+0] = s->v[5*i+0] & 31;
- r[8*i+1] = (s->v[5*i+0] >> 5) & 31;
- r[8*i+1] ^= (s->v[5*i+1] << 3) & 31;
- r[8*i+2] = (s->v[5*i+1] >> 2) & 31;
- r[8*i+3] = (s->v[5*i+1] >> 7) & 31;
- r[8*i+3] ^= (s->v[5*i+2] << 1) & 31;
- r[8*i+4] = (s->v[5*i+2] >> 4) & 31;
- r[8*i+4] ^= (s->v[5*i+3] << 4) & 31;
- r[8*i+5] = (s->v[5*i+3] >> 1) & 31;
- r[8*i+6] = (s->v[5*i+3] >> 6) & 31;
- r[8*i+6] ^= (s->v[5*i+4] << 2) & 31;
- r[8*i+7] = (s->v[5*i+4] >> 3) & 31;
- }
- r[8*i+0] = s->v[5*i+0] & 31;
- r[8*i+1] = (s->v[5*i+0] >> 5) & 31;
- r[8*i+1] ^= (s->v[5*i+1] << 3) & 31;
- r[8*i+2] = (s->v[5*i+1] >> 2) & 31;
-
- /* Making it signed */
- carry = 0;
- for(i=0;i<50;i++)
- {
- r[i] += carry;
- r[i+1] += r[i] >> 5;
- r[i] &= 31;
- carry = r[i] >> 4;
- r[i] -= carry<<5;
- }
- r[50] += carry;
+ barrett_reduce(r, t);
+}
+
+void sc25519_window3(signed char r[85], const sc25519 *s)
+{
+ char carry;
+ int i;
+ for(i=0;i<10;i++)
+ {
+ r[8*i+0] = s->v[3*i+0] & 7;
+ r[8*i+1] = (s->v[3*i+0] >> 3) & 7;
+ r[8*i+2] = (s->v[3*i+0] >> 6) & 7;
+ r[8*i+2] ^= (s->v[3*i+1] << 2) & 7;
+ r[8*i+3] = (s->v[3*i+1] >> 1) & 7;
+ r[8*i+4] = (s->v[3*i+1] >> 4) & 7;
+ r[8*i+5] = (s->v[3*i+1] >> 7) & 7;
+ r[8*i+5] ^= (s->v[3*i+2] << 1) & 7;
+ r[8*i+6] = (s->v[3*i+2] >> 2) & 7;
+ r[8*i+7] = (s->v[3*i+2] >> 5) & 7;
+ }
+ r[8*i+0] = s->v[3*i+0] & 7;
+ r[8*i+1] = (s->v[3*i+0] >> 3) & 7;
+ r[8*i+2] = (s->v[3*i+0] >> 6) & 7;
+ r[8*i+2] ^= (s->v[3*i+1] << 2) & 7;
+ r[8*i+3] = (s->v[3*i+1] >> 1) & 7;
+ r[8*i+4] = (s->v[3*i+1] >> 4) & 7;
+
+ /* Making it signed */
+ carry = 0;
+ for(i=0;i<84;i++)
+ {
+ r[i] += carry;
+ r[i+1] += r[i] >> 3;
+ r[i] &= 7;
+ carry = r[i] >> 2;
+ r[i] -= carry<<3;
+ }
+ r[84] += carry;
}
-#endif
-static inline void sc25519_2interleave2(unsigned char r[127], const sc25519 *s1, const sc25519 *s2)
+void sc25519_2interleave2(unsigned char r[127], const sc25519 *s1, const sc25519 *s2)
{
- int i;
- for(i=0;i<31;i++)
- {
- r[4*i] = ( s1->v[i] & 3) ^ (( s2->v[i] & 3) << 2);
- r[4*i+1] = ((s1->v[i] >> 2) & 3) ^ (((s2->v[i] >> 2) & 3) << 2);
- r[4*i+2] = ((s1->v[i] >> 4) & 3) ^ (((s2->v[i] >> 4) & 3) << 2);
- r[4*i+3] = ((s1->v[i] >> 6) & 3) ^ (((s2->v[i] >> 6) & 3) << 2);
- }
- r[124] = ( s1->v[31] & 3) ^ (( s2->v[31] & 3) << 2);
- r[125] = ((s1->v[31] >> 2) & 3) ^ (((s2->v[31] >> 2) & 3) << 2);
- r[126] = ((s1->v[31] >> 4) & 3) ^ (((s2->v[31] >> 4) & 3) << 2);
+ int i;
+ for(i=0;i<31;i++)
+ {
+ r[4*i] = ( s1->v[i] & 3) ^ (( s2->v[i] & 3) << 2);
+ r[4*i+1] = ((s1->v[i] >> 2) & 3) ^ (((s2->v[i] >> 2) & 3) << 2);
+ r[4*i+2] = ((s1->v[i] >> 4) & 3) ^ (((s2->v[i] >> 4) & 3) << 2);
+ r[4*i+3] = ((s1->v[i] >> 6) & 3) ^ (((s2->v[i] >> 6) & 3) << 2);
+ }
+ r[124] = ( s1->v[31] & 3) ^ (( s2->v[31] & 3) << 2);
+ r[125] = ((s1->v[31] >> 2) & 3) ^ (((s2->v[31] >> 2) & 3) << 2);
+ r[126] = ((s1->v[31] >> 4) & 3) ^ (((s2->v[31] >> 4) & 3) << 2);
}
-typedef struct
-{
- fe25519 x;
- fe25519 y;
- fe25519 z;
- fe25519 t;
-} ge25519;
-
/* d */
-static const fe25519 ge25519_ecd = {{0xA3, 0x78, 0x59, 0x13, 0xCA, 0x4D, 0xEB, 0x75, 0xAB, 0xD8, 0x41, 0x41, 0x4D, 0x0A, 0x70, 0x00,
- 0x98, 0xE8, 0x79, 0x77, 0x79, 0x40, 0xC7, 0x8C, 0x73, 0xFE, 0x6F, 0x2B, 0xEE, 0x6C, 0x03, 0x52}};
+static const fe25519 ge25519_ecd = {{0xA3, 0x78, 0x59, 0x13, 0xCA, 0x4D, 0xEB, 0x75, 0xAB, 0xD8, 0x41, 0x41, 0x4D, 0x0A, 0x70, 0x00,
+ 0x98, 0xE8, 0x79, 0x77, 0x79, 0x40, 0xC7, 0x8C, 0x73, 0xFE, 0x6F, 0x2B, 0xEE, 0x6C, 0x03, 0x52}};
/* 2*d */
-static const fe25519 ge25519_ec2d = {{0x59, 0xF1, 0xB2, 0x26, 0x94, 0x9B, 0xD6, 0xEB, 0x56, 0xB1, 0x83, 0x82, 0x9A, 0x14, 0xE0, 0x00,
- 0x30, 0xD1, 0xF3, 0xEE, 0xF2, 0x80, 0x8E, 0x19, 0xE7, 0xFC, 0xDF, 0x56, 0xDC, 0xD9, 0x06, 0x24}};
+static const fe25519 ge25519_ec2d = {{0x59, 0xF1, 0xB2, 0x26, 0x94, 0x9B, 0xD6, 0xEB, 0x56, 0xB1, 0x83, 0x82, 0x9A, 0x14, 0xE0, 0x00,
+ 0x30, 0xD1, 0xF3, 0xEE, 0xF2, 0x80, 0x8E, 0x19, 0xE7, 0xFC, 0xDF, 0x56, 0xDC, 0xD9, 0x06, 0x24}};
/* sqrt(-1) */
-static const fe25519 ge25519_sqrtm1 = {{0xB0, 0xA0, 0x0E, 0x4A, 0x27, 0x1B, 0xEE, 0xC4, 0x78, 0xE4, 0x2F, 0xAD, 0x06, 0x18, 0x43, 0x2F,
- 0xA7, 0xD7, 0xFB, 0x3D, 0x99, 0x00, 0x4D, 0x2B, 0x0B, 0xDF, 0xC1, 0x4F, 0x80, 0x24, 0x83, 0x2B}};
-
-#define ge25519_p3 ge25519
-
-typedef struct
-{
- fe25519 x;
- fe25519 z;
- fe25519 y;
- fe25519 t;
-} ge25519_p1p1;
-
-typedef struct
-{
- fe25519 x;
- fe25519 y;
- fe25519 z;
-} ge25519_p2;
-
-typedef struct
-{
- fe25519 x;
- fe25519 y;
-} ge25519_aff;
-
+static const fe25519 ge25519_sqrtm1 = {{0xB0, 0xA0, 0x0E, 0x4A, 0x27, 0x1B, 0xEE, 0xC4, 0x78, 0xE4, 0x2F, 0xAD, 0x06, 0x18, 0x43, 0x2F,
+ 0xA7, 0xD7, 0xFB, 0x3D, 0x99, 0x00, 0x4D, 0x2B, 0x0B, 0xDF, 0xC1, 0x4F, 0x80, 0x24, 0x83, 0x2B}};
/* Packed coordinates of the base point */
-static const ge25519 ge25519_base = {{{0x1A, 0xD5, 0x25, 0x8F, 0x60, 0x2D, 0x56, 0xC9, 0xB2, 0xA7, 0x25, 0x95, 0x60, 0xC7, 0x2C, 0x69,
- 0x5C, 0xDC, 0xD6, 0xFD, 0x31, 0xE2, 0xA4, 0xC0, 0xFE, 0x53, 0x6E, 0xCD, 0xD3, 0x36, 0x69, 0x21}},
- {{0x58, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66,
- 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66}},
- {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
- {{0xA3, 0xDD, 0xB7, 0xA5, 0xB3, 0x8A, 0xDE, 0x6D, 0xF5, 0x52, 0x51, 0x77, 0x80, 0x9F, 0xF0, 0x20,
- 0x7D, 0xE3, 0xAB, 0x64, 0x8E, 0x4E, 0xEA, 0x66, 0x65, 0x76, 0x8B, 0xD7, 0x0F, 0x5F, 0x87, 0x67}}};
+static const ge25519 ge25519_base = {{{0x1A, 0xD5, 0x25, 0x8F, 0x60, 0x2D, 0x56, 0xC9, 0xB2, 0xA7, 0x25, 0x95, 0x60, 0xC7, 0x2C, 0x69,
+ 0x5C, 0xDC, 0xD6, 0xFD, 0x31, 0xE2, 0xA4, 0xC0, 0xFE, 0x53, 0x6E, 0xCD, 0xD3, 0x36, 0x69, 0x21}},
+ {{0x58, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66,
+ 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66}},
+ {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+ {{0xA3, 0xDD, 0xB7, 0xA5, 0xB3, 0x8A, 0xDE, 0x6D, 0xF5, 0x52, 0x51, 0x77, 0x80, 0x9F, 0xF0, 0x20,
+ 0x7D, 0xE3, 0xAB, 0x64, 0x8E, 0x4E, 0xEA, 0x66, 0x65, 0x76, 0x8B, 0xD7, 0x0F, 0x5F, 0x87, 0x67}}};
/* Multiples of the base point in affine representation */
static const ge25519_aff ge25519_base_multiples_affine[425] = {
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x1a, 0xd5, 0x25, 0x8f, 0x60, 0x2d, 0x56, 0xc9, 0xb2, 0xa7, 0x25, 0x95, 0x60, 0xc7, 0x2c, 0x69, 0x5c, 0xdc, 0xd6, 0xfd, 0x31, 0xe2, 0xa4, 0xc0, 0xfe, 0x53, 0x6e, 0xcd, 0xd3, 0x36, 0x69, 0x21}} ,
{{0x58, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66}}},
@@ -1026,7 +887,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0xd4, 0xb4, 0xf5, 0x78, 0x48, 0x68, 0xc3, 0x02, 0x04, 0x03, 0x24, 0x67, 0x17, 0xec, 0x16, 0x9f, 0xf7, 0x9e, 0x26, 0x60, 0x8e, 0xa1, 0x26, 0xa1, 0xab, 0x69, 0xee, 0x77, 0xd1, 0xb1, 0x67, 0x12}}},
{{{0x70, 0xf8, 0xc9, 0xc4, 0x57, 0xa6, 0x3a, 0x49, 0x47, 0x15, 0xce, 0x93, 0xc1, 0x9e, 0x73, 0x1a, 0xf9, 0x20, 0x35, 0x7a, 0xb8, 0xd4, 0x25, 0x83, 0x46, 0xf1, 0xcf, 0x56, 0xdb, 0xa8, 0x3d, 0x20}} ,
{{0x2f, 0x11, 0x32, 0xca, 0x61, 0xab, 0x38, 0xdf, 0xf0, 0x0f, 0x2f, 0xea, 0x32, 0x28, 0xf2, 0x4c, 0x6c, 0x71, 0xd5, 0x80, 0x85, 0xb8, 0x0e, 0x47, 0xe1, 0x95, 0x15, 0xcb, 0x27, 0xe8, 0xd0, 0x47}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0xc8, 0x84, 0xa5, 0x08, 0xbc, 0xfd, 0x87, 0x3b, 0x99, 0x8b, 0x69, 0x80, 0x7b, 0xc6, 0x3a, 0xeb, 0x93, 0xcf, 0x4e, 0xf8, 0x5c, 0x2d, 0x86, 0x42, 0xb6, 0x71, 0xd7, 0x97, 0x5f, 0xe1, 0x42, 0x67}} ,
{{0xb4, 0xb9, 0x37, 0xfc, 0xa9, 0x5b, 0x2f, 0x1e, 0x93, 0xe4, 0x1e, 0x62, 0xfc, 0x3c, 0x78, 0x81, 0x8f, 0xf3, 0x8a, 0x66, 0x09, 0x6f, 0xad, 0x6e, 0x79, 0x73, 0xe5, 0xc9, 0x00, 0x06, 0xd3, 0x21}}},
@@ -1036,7 +897,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0xf1, 0x79, 0x7b, 0xed, 0x4f, 0x44, 0xb2, 0xe7, 0x08, 0x0d, 0xc2, 0x08, 0x12, 0xd2, 0x9f, 0xdf, 0xcd, 0x93, 0x20, 0x8a, 0xcf, 0x33, 0xca, 0x6d, 0x89, 0xb9, 0x77, 0xc8, 0x93, 0x1b, 0x4e, 0x60}}},
{{{0x26, 0x4f, 0x7e, 0x97, 0xf6, 0x40, 0xdd, 0x4f, 0xfc, 0x52, 0x78, 0xf9, 0x90, 0x31, 0x03, 0xe6, 0x7d, 0x56, 0x39, 0x0b, 0x1d, 0x56, 0x82, 0x85, 0xf9, 0x1a, 0x42, 0x17, 0x69, 0x6c, 0xcf, 0x39}} ,
{{0x69, 0xd2, 0x06, 0x3a, 0x4f, 0x39, 0x2d, 0xf9, 0x38, 0x40, 0x8c, 0x4c, 0xe7, 0x05, 0x12, 0xb4, 0x78, 0x8b, 0xf8, 0xc0, 0xec, 0x93, 0xde, 0x7a, 0x6b, 0xce, 0x2c, 0xe1, 0x0e, 0xa9, 0x34, 0x44}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x0b, 0xa4, 0x3c, 0xb0, 0x0f, 0x7a, 0x51, 0xf1, 0x78, 0xd6, 0xd9, 0x6a, 0xfd, 0x46, 0xe8, 0xb8, 0xa8, 0x79, 0x1d, 0x87, 0xf9, 0x90, 0xf2, 0x9c, 0x13, 0x29, 0xf8, 0x0b, 0x20, 0x64, 0xfa, 0x05}} ,
{{0x26, 0x09, 0xda, 0x17, 0xaf, 0x95, 0xd6, 0xfb, 0x6a, 0x19, 0x0d, 0x6e, 0x5e, 0x12, 0xf1, 0x99, 0x4c, 0xaa, 0xa8, 0x6f, 0x79, 0x86, 0xf4, 0x72, 0x28, 0x00, 0x26, 0xf9, 0xea, 0x9e, 0x19, 0x3d}}},
@@ -1046,7 +907,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x1c, 0x59, 0x1c, 0x65, 0x5d, 0x34, 0xa4, 0x09, 0xcd, 0x13, 0x9c, 0x70, 0x7d, 0xb1, 0x2a, 0xc5, 0x88, 0xaf, 0x0b, 0x60, 0xc7, 0x9f, 0x34, 0x8d, 0xd6, 0xb7, 0x7f, 0xea, 0x78, 0x65, 0x8d, 0x77}}},
{{{0x56, 0xa5, 0xc2, 0x0c, 0xdd, 0xbc, 0xb8, 0x20, 0x6d, 0x57, 0x61, 0xb5, 0xfb, 0x78, 0xb5, 0xd4, 0x49, 0x54, 0x90, 0x26, 0xc1, 0xcb, 0xe9, 0xe6, 0xbf, 0xec, 0x1d, 0x4e, 0xed, 0x07, 0x7e, 0x5e}} ,
{{0xc7, 0xf6, 0x6c, 0x56, 0x31, 0x20, 0x14, 0x0e, 0xa8, 0xd9, 0x27, 0xc1, 0x9a, 0x3d, 0x1b, 0x7d, 0x0e, 0x26, 0xd3, 0x81, 0xaa, 0xeb, 0xf5, 0x6b, 0x79, 0x02, 0xf1, 0x51, 0x5c, 0x75, 0x55, 0x0f}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x0a, 0x34, 0xcd, 0x82, 0x3c, 0x33, 0x09, 0x54, 0xd2, 0x61, 0x39, 0x30, 0x9b, 0xfd, 0xef, 0x21, 0x26, 0xd4, 0x70, 0xfa, 0xee, 0xf9, 0x31, 0x33, 0x73, 0x84, 0xd0, 0xb3, 0x81, 0xbf, 0xec, 0x2e}} ,
{{0xe8, 0x93, 0x8b, 0x00, 0x64, 0xf7, 0x9c, 0xb8, 0x74, 0xe0, 0xe6, 0x49, 0x48, 0x4d, 0x4d, 0x48, 0xb6, 0x19, 0xa1, 0x40, 0xb7, 0xd9, 0x32, 0x41, 0x7c, 0x82, 0x37, 0xa1, 0x2d, 0xdc, 0xd2, 0x54}}},
@@ -1056,7 +917,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0xbc, 0x58, 0xe6, 0xc0, 0x95, 0x2a, 0x2a, 0x81, 0x9a, 0x7a, 0xf3, 0xd2, 0x06, 0xbe, 0x48, 0xbc, 0x0c, 0xc5, 0x46, 0xe0, 0x6a, 0xd4, 0xac, 0x0f, 0xd9, 0xcc, 0x82, 0x34, 0x2c, 0xaf, 0xdb, 0x1f}}},
{{{0xf7, 0x17, 0x13, 0xbd, 0xfb, 0xbc, 0xd2, 0xec, 0x45, 0xb3, 0x15, 0x31, 0xe9, 0xaf, 0x82, 0x84, 0x3d, 0x28, 0xc6, 0xfc, 0x11, 0xf5, 0x41, 0xb5, 0x8b, 0xd3, 0x12, 0x76, 0x52, 0xe7, 0x1a, 0x3c}} ,
{{0x4e, 0x36, 0x11, 0x07, 0xa2, 0x15, 0x20, 0x51, 0xc4, 0x2a, 0xc3, 0x62, 0x8b, 0x5e, 0x7f, 0xa6, 0x0f, 0xf9, 0x45, 0x85, 0x6c, 0x11, 0x86, 0xb7, 0x7e, 0xe5, 0xd7, 0xf9, 0xc3, 0x91, 0x1c, 0x05}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0xea, 0xd6, 0xde, 0x29, 0x3a, 0x00, 0xb9, 0x02, 0x59, 0xcb, 0x26, 0xc4, 0xba, 0x99, 0xb1, 0x97, 0x2f, 0x8e, 0x00, 0x92, 0x26, 0x4f, 0x52, 0xeb, 0x47, 0x1b, 0x89, 0x8b, 0x24, 0xc0, 0x13, 0x7d}} ,
{{0xd5, 0x20, 0x5b, 0x80, 0xa6, 0x80, 0x20, 0x95, 0xc3, 0xe9, 0x9f, 0x8e, 0x87, 0x9e, 0x1e, 0x9e, 0x7a, 0xc7, 0xcc, 0x75, 0x6c, 0xa5, 0xf1, 0x91, 0x1a, 0xa8, 0x01, 0x2c, 0xab, 0x76, 0xa9, 0x59}}},
@@ -1066,7 +927,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x22, 0xf5, 0x5f, 0x4d, 0x15, 0xef, 0xfc, 0x4e, 0x57, 0x03, 0x36, 0x89, 0xf0, 0xeb, 0x5b, 0x91, 0xd6, 0xe2, 0xca, 0x01, 0xa5, 0xee, 0x52, 0xec, 0xa0, 0x3c, 0x8f, 0x33, 0x90, 0x5a, 0x94, 0x72}}},
{{{0x8a, 0x4b, 0xe7, 0x38, 0xbc, 0xda, 0xc2, 0xb0, 0x85, 0xe1, 0x4a, 0xfe, 0x2d, 0x44, 0x84, 0xcb, 0x20, 0x6b, 0x2d, 0xbf, 0x11, 0x9c, 0xd7, 0xbe, 0xd3, 0x3e, 0x5f, 0xbf, 0x68, 0xbc, 0xa8, 0x07}} ,
{{0x01, 0x89, 0x28, 0x22, 0x6a, 0x78, 0xaa, 0x29, 0x03, 0xc8, 0x74, 0x95, 0x03, 0x3e, 0xdc, 0xbd, 0x07, 0x13, 0xa8, 0xa2, 0x20, 0x2d, 0xb3, 0x18, 0x70, 0x42, 0xfd, 0x7a, 0xc4, 0xd7, 0x49, 0x72}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x02, 0xff, 0x32, 0x2b, 0x5c, 0x93, 0x54, 0x32, 0xe8, 0x57, 0x54, 0x1a, 0x8b, 0x33, 0x60, 0x65, 0xd3, 0x67, 0xa4, 0xc1, 0x26, 0xc4, 0xa4, 0x34, 0x1f, 0x9b, 0xa7, 0xa9, 0xf4, 0xd9, 0x4f, 0x5b}} ,
{{0x46, 0x8d, 0xb0, 0x33, 0x54, 0x26, 0x5b, 0x68, 0xdf, 0xbb, 0xc5, 0xec, 0xc2, 0xf9, 0x3c, 0x5a, 0x37, 0xc1, 0x8e, 0x27, 0x47, 0xaa, 0x49, 0x5a, 0xf8, 0xfb, 0x68, 0x04, 0x23, 0xd1, 0xeb, 0x40}}},
@@ -1076,7 +937,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x60, 0xd6, 0xdd, 0x78, 0xe6, 0xd4, 0x22, 0x42, 0x1f, 0x00, 0xf9, 0xb1, 0x6a, 0x63, 0xe2, 0x92, 0x59, 0xd1, 0x1a, 0xb7, 0x00, 0x54, 0x29, 0xc9, 0xc1, 0xf6, 0x6f, 0x7a, 0xc5, 0x3c, 0x5f, 0x65}}},
{{{0x27, 0x4f, 0xd0, 0x72, 0xb1, 0x11, 0x14, 0x27, 0x15, 0x94, 0x48, 0x81, 0x7e, 0x74, 0xd8, 0x32, 0xd5, 0xd1, 0x11, 0x28, 0x60, 0x63, 0x36, 0x32, 0x37, 0xb5, 0x13, 0x1c, 0xa0, 0x37, 0xe3, 0x74}} ,
{{0xf1, 0x25, 0x4e, 0x11, 0x96, 0x67, 0xe6, 0x1c, 0xc2, 0xb2, 0x53, 0xe2, 0xda, 0x85, 0xee, 0xb2, 0x9f, 0x59, 0xf3, 0xba, 0xbd, 0xfa, 0xcf, 0x6e, 0xf9, 0xda, 0xa4, 0xb3, 0x02, 0x8f, 0x64, 0x08}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x34, 0x94, 0xf2, 0x64, 0x54, 0x47, 0x37, 0x07, 0x40, 0x8a, 0x20, 0xba, 0x4a, 0x55, 0xd7, 0x3f, 0x47, 0xba, 0x25, 0x23, 0x14, 0xb0, 0x2c, 0xe8, 0x55, 0xa8, 0xa6, 0xef, 0x51, 0xbd, 0x6f, 0x6a}} ,
{{0x71, 0xd6, 0x16, 0x76, 0xb2, 0x06, 0xea, 0x79, 0xf5, 0xc4, 0xc3, 0x52, 0x7e, 0x61, 0xd1, 0xe1, 0xad, 0x70, 0x78, 0x1d, 0x16, 0x11, 0xf8, 0x7c, 0x2b, 0xfc, 0x55, 0x9f, 0x52, 0xf8, 0xf5, 0x16}}},
@@ -1086,7 +947,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x02, 0xcc, 0x22, 0x74, 0x4d, 0x19, 0x07, 0xc0, 0xda, 0xb5, 0x76, 0x51, 0x2a, 0xaa, 0xa6, 0x0a, 0x5f, 0x26, 0xd4, 0xbc, 0xaf, 0x48, 0x88, 0x7f, 0x02, 0xbc, 0xf2, 0xe1, 0xcf, 0xe9, 0xdd, 0x15}}},
{{{0xed, 0xb5, 0x9a, 0x8c, 0x9a, 0xdd, 0x27, 0xf4, 0x7f, 0x47, 0xd9, 0x52, 0xa7, 0xcd, 0x65, 0xa5, 0x31, 0x22, 0xed, 0xa6, 0x63, 0x5b, 0x80, 0x4a, 0xad, 0x4d, 0xed, 0xbf, 0xee, 0x49, 0xb3, 0x06}} ,
{{0xf8, 0x64, 0x8b, 0x60, 0x90, 0xe9, 0xde, 0x44, 0x77, 0xb9, 0x07, 0x36, 0x32, 0xc2, 0x50, 0xf5, 0x65, 0xdf, 0x48, 0x4c, 0x37, 0xaa, 0x68, 0xab, 0x9a, 0x1f, 0x3e, 0xff, 0x89, 0x92, 0xa0, 0x07}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x7d, 0x4f, 0x9c, 0x19, 0xc0, 0x4a, 0x31, 0xec, 0xf9, 0xaa, 0xeb, 0xb2, 0x16, 0x9c, 0xa3, 0x66, 0x5f, 0xd1, 0xd4, 0xed, 0xb8, 0x92, 0x1c, 0xab, 0xda, 0xea, 0xd9, 0x57, 0xdf, 0x4c, 0x2a, 0x48}} ,
{{0x4b, 0xb0, 0x4e, 0x6e, 0x11, 0x3b, 0x51, 0xbd, 0x6a, 0xfd, 0xe4, 0x25, 0xa5, 0x5f, 0x11, 0x3f, 0x98, 0x92, 0x51, 0x14, 0xc6, 0x5f, 0x3c, 0x0b, 0xa8, 0xf7, 0xc2, 0x81, 0x43, 0xde, 0x91, 0x73}}},
@@ -1096,7 +957,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x2e, 0x69, 0x98, 0xfd, 0xc6, 0xbd, 0xcc, 0xca, 0xdf, 0x9a, 0x44, 0x7e, 0x9d, 0xca, 0x89, 0x6d, 0xbf, 0x27, 0xc2, 0xf8, 0xcd, 0x46, 0x00, 0x2b, 0xb5, 0x58, 0x4e, 0xb7, 0x89, 0x09, 0xe9, 0x2d}}},
{{{0x54, 0xbe, 0x75, 0xcb, 0x05, 0xb0, 0x54, 0xb7, 0xe7, 0x26, 0x86, 0x4a, 0xfc, 0x19, 0xcf, 0x27, 0x46, 0xd4, 0x22, 0x96, 0x5a, 0x11, 0xe8, 0xd5, 0x1b, 0xed, 0x71, 0xc5, 0x5d, 0xc8, 0xaf, 0x45}} ,
{{0x40, 0x7b, 0x77, 0x57, 0x49, 0x9e, 0x80, 0x39, 0x23, 0xee, 0x81, 0x0b, 0x22, 0xcf, 0xdb, 0x7a, 0x2f, 0x14, 0xb8, 0x57, 0x8f, 0xa1, 0x39, 0x1e, 0x77, 0xfc, 0x0b, 0xa6, 0xbf, 0x8a, 0x0c, 0x6c}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x77, 0x3a, 0xd4, 0xd8, 0x27, 0xcf, 0xe8, 0xa1, 0x72, 0x9d, 0xca, 0xdd, 0x0d, 0x96, 0xda, 0x79, 0xed, 0x56, 0x42, 0x15, 0x60, 0xc7, 0x1c, 0x6b, 0x26, 0x30, 0xf6, 0x6a, 0x95, 0x67, 0xf3, 0x0a}} ,
{{0xc5, 0x08, 0xa4, 0x2b, 0x2f, 0xbd, 0x31, 0x81, 0x2a, 0xa6, 0xb6, 0xe4, 0x00, 0x91, 0xda, 0x3d, 0xb2, 0xb0, 0x96, 0xce, 0x8a, 0xd2, 0x8d, 0x70, 0xb3, 0xd3, 0x34, 0x01, 0x90, 0x8d, 0x10, 0x21}}},
@@ -1106,7 +967,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x61, 0x36, 0xd6, 0x08, 0x90, 0xbf, 0xa7, 0x7a, 0x97, 0x6c, 0x0f, 0x84, 0xd5, 0x33, 0x2d, 0x37, 0xc9, 0x6a, 0x80, 0x90, 0x3d, 0x0a, 0xa2, 0xaa, 0xe1, 0xb8, 0x84, 0xba, 0x61, 0x36, 0xdd, 0x69}}},
{{{0x6b, 0xdb, 0x5b, 0x9c, 0xc6, 0x92, 0xbc, 0x23, 0xaf, 0xc5, 0xb8, 0x75, 0xf8, 0x42, 0xfa, 0xd6, 0xb6, 0x84, 0x94, 0x63, 0x98, 0x93, 0x48, 0x78, 0x38, 0xcd, 0xbb, 0x18, 0x34, 0xc3, 0xdb, 0x67}} ,
{{0x96, 0xf3, 0x3a, 0x09, 0x56, 0xb0, 0x6f, 0x7c, 0x51, 0x1e, 0x1b, 0x39, 0x48, 0xea, 0xc9, 0x0c, 0x25, 0xa2, 0x7a, 0xca, 0xe7, 0x92, 0xfc, 0x59, 0x30, 0xa3, 0x89, 0x85, 0xdf, 0x6f, 0x43, 0x38}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x79, 0x84, 0x44, 0x19, 0xbd, 0xe9, 0x54, 0xc4, 0xc0, 0x6e, 0x2a, 0xa8, 0xa8, 0x9b, 0x43, 0xd5, 0x71, 0x22, 0x5f, 0xdc, 0x01, 0xfa, 0xdf, 0xb3, 0xb8, 0x47, 0x4b, 0x0a, 0xa5, 0x44, 0xea, 0x29}} ,
{{0x05, 0x90, 0x50, 0xaf, 0x63, 0x5f, 0x9d, 0x9e, 0xe1, 0x9d, 0x38, 0x97, 0x1f, 0x6c, 0xac, 0x30, 0x46, 0xb2, 0x6a, 0x19, 0xd1, 0x4b, 0xdb, 0xbb, 0x8c, 0xda, 0x2e, 0xab, 0xc8, 0x5a, 0x77, 0x6c}}},
@@ -1116,7 +977,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x5d, 0x04, 0xb3, 0x40, 0x28, 0x95, 0x2d, 0x30, 0x83, 0xec, 0x5e, 0xe4, 0xff, 0x75, 0xfe, 0x79, 0x26, 0x9d, 0x1d, 0x36, 0xcd, 0x0a, 0x15, 0xd2, 0x24, 0x14, 0x77, 0x71, 0xd7, 0x8a, 0x1b, 0x04}}},
{{{0x5d, 0x93, 0xc9, 0xbe, 0xaa, 0x90, 0xcd, 0x9b, 0xfb, 0x73, 0x7e, 0xb0, 0x64, 0x98, 0x57, 0x44, 0x42, 0x41, 0xb1, 0xaf, 0xea, 0xc1, 0xc3, 0x22, 0xff, 0x60, 0x46, 0xcb, 0x61, 0x81, 0x70, 0x61}} ,
{{0x0d, 0x82, 0xb9, 0xfe, 0x21, 0xcd, 0xc4, 0xf5, 0x98, 0x0c, 0x4e, 0x72, 0xee, 0x87, 0x49, 0xf8, 0xa1, 0x95, 0xdf, 0x8f, 0x2d, 0xbd, 0x21, 0x06, 0x7c, 0x15, 0xe8, 0x12, 0x6d, 0x93, 0xd6, 0x38}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x91, 0xf7, 0x51, 0xd9, 0xef, 0x7d, 0x42, 0x01, 0x13, 0xe9, 0xb8, 0x7f, 0xa6, 0x49, 0x17, 0x64, 0x21, 0x80, 0x83, 0x2c, 0x63, 0x4c, 0x60, 0x09, 0x59, 0x91, 0x92, 0x77, 0x39, 0x51, 0xf4, 0x48}} ,
{{0x60, 0xd5, 0x22, 0x83, 0x08, 0x2f, 0xff, 0x99, 0x3e, 0x69, 0x6d, 0x88, 0xda, 0xe7, 0x5b, 0x52, 0x26, 0x31, 0x2a, 0xe5, 0x89, 0xde, 0x68, 0x90, 0xb6, 0x22, 0x5a, 0xbd, 0xd3, 0x85, 0x53, 0x31}}},
@@ -1126,7 +987,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0xba, 0x38, 0x02, 0xf7, 0x81, 0x43, 0x83, 0xa3, 0x20, 0x4f, 0x01, 0x3b, 0x8a, 0x04, 0x38, 0x31, 0xc6, 0x0f, 0xc8, 0xdf, 0xd7, 0xfa, 0x2f, 0x88, 0x3f, 0xfc, 0x0c, 0x76, 0xc4, 0xa6, 0x45, 0x72}}},
{{{0xbb, 0x0c, 0xbc, 0x6a, 0xa4, 0x97, 0x17, 0x93, 0x2d, 0x6f, 0xde, 0x72, 0x10, 0x1c, 0x08, 0x2c, 0x0f, 0x80, 0x32, 0x68, 0x27, 0xd4, 0xab, 0xdd, 0xc5, 0x58, 0x61, 0x13, 0x6d, 0x11, 0x1e, 0x4d}} ,
{{0x1a, 0xb9, 0xc9, 0x10, 0xfb, 0x1e, 0x4e, 0xf4, 0x84, 0x4b, 0x8a, 0x5e, 0x7b, 0x4b, 0xe8, 0x43, 0x8c, 0x8f, 0x00, 0xb5, 0x54, 0x13, 0xc5, 0x5c, 0xb6, 0x35, 0x4e, 0x9d, 0xe4, 0x5b, 0x41, 0x6d}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x15, 0x7d, 0x12, 0x48, 0x82, 0x14, 0x42, 0xcd, 0x32, 0xd4, 0x4b, 0xc1, 0x72, 0x61, 0x2a, 0x8c, 0xec, 0xe2, 0xf8, 0x24, 0x45, 0x94, 0xe3, 0xbe, 0xdd, 0x67, 0xa8, 0x77, 0x5a, 0xae, 0x5b, 0x4b}} ,
{{0xcb, 0x77, 0x9a, 0x20, 0xde, 0xb8, 0x23, 0xd9, 0xa0, 0x0f, 0x8c, 0x7b, 0xa5, 0xcb, 0xae, 0xb6, 0xec, 0x42, 0x67, 0x0e, 0x58, 0xa4, 0x75, 0x98, 0x21, 0x71, 0x84, 0xb3, 0xe0, 0x76, 0x94, 0x73}}},
@@ -1136,7 +997,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x32, 0xfb, 0x86, 0xec, 0x33, 0x6b, 0x2e, 0x51, 0x2b, 0xc8, 0xfa, 0x6c, 0x70, 0x47, 0x7e, 0xce, 0x05, 0x0c, 0x71, 0xf3, 0xb4, 0x56, 0xa6, 0xdc, 0xcc, 0x78, 0x07, 0x75, 0xd0, 0xdd, 0xb2, 0x6a}}},
{{{0xc6, 0xef, 0xb9, 0xc0, 0x2b, 0x22, 0x08, 0x1e, 0x71, 0x70, 0xb3, 0x35, 0x9c, 0x7a, 0x01, 0x92, 0x44, 0x9a, 0xf6, 0xb0, 0x58, 0x95, 0xc1, 0x9b, 0x02, 0xed, 0x2d, 0x7c, 0x34, 0x29, 0x49, 0x44}} ,
{{0x45, 0x62, 0x1d, 0x2e, 0xff, 0x2a, 0x1c, 0x21, 0xa4, 0x25, 0x7b, 0x0d, 0x8c, 0x15, 0x39, 0xfc, 0x8f, 0x7c, 0xa5, 0x7d, 0x1e, 0x25, 0xa3, 0x45, 0xd6, 0xab, 0xbd, 0xcb, 0xc5, 0x5e, 0x78, 0x77}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0xd0, 0xd3, 0x42, 0xed, 0x1d, 0x00, 0x3c, 0x15, 0x2c, 0x9c, 0x77, 0x81, 0xd2, 0x73, 0xd1, 0x06, 0xd5, 0xc4, 0x7f, 0x94, 0xbb, 0x92, 0x2d, 0x2c, 0x4b, 0x45, 0x4b, 0xe9, 0x2a, 0x89, 0x6b, 0x2b}} ,
{{0xd2, 0x0c, 0x88, 0xc5, 0x48, 0x4d, 0xea, 0x0d, 0x4a, 0xc9, 0x52, 0x6a, 0x61, 0x79, 0xe9, 0x76, 0xf3, 0x85, 0x52, 0x5c, 0x1b, 0x2c, 0xe1, 0xd6, 0xc4, 0x0f, 0x18, 0x0e, 0x4e, 0xf6, 0x1c, 0x7f}}},
@@ -1146,7 +1007,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x4c, 0xe6, 0xb0, 0xc1, 0xa5, 0x2a, 0x82, 0x09, 0x08, 0xad, 0x79, 0x9c, 0x56, 0xf6, 0xf9, 0xc1, 0xd7, 0x7c, 0x39, 0x7f, 0x93, 0xca, 0x11, 0x55, 0xbf, 0x07, 0x1b, 0x82, 0x29, 0x69, 0x95, 0x5c}}},
{{{0x87, 0xee, 0xa6, 0x56, 0x9e, 0xc2, 0x9a, 0x56, 0x24, 0x42, 0x85, 0x4d, 0x98, 0x31, 0x1e, 0x60, 0x4d, 0x87, 0x85, 0x04, 0xae, 0x46, 0x12, 0xf9, 0x8e, 0x7f, 0xe4, 0x7f, 0xf6, 0x1c, 0x37, 0x01}} ,
{{0x73, 0x4c, 0xb6, 0xc5, 0xc4, 0xe9, 0x6c, 0x85, 0x48, 0x4a, 0x5a, 0xac, 0xd9, 0x1f, 0x43, 0xf8, 0x62, 0x5b, 0xee, 0x98, 0x2a, 0x33, 0x8e, 0x79, 0xce, 0x61, 0x06, 0x35, 0xd8, 0xd7, 0xca, 0x71}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x72, 0xd3, 0xae, 0xa6, 0xca, 0x8f, 0xcd, 0xcc, 0x78, 0x8e, 0x19, 0x4d, 0xa7, 0xd2, 0x27, 0xe9, 0xa4, 0x3c, 0x16, 0x5b, 0x84, 0x80, 0xf9, 0xd0, 0xcc, 0x6a, 0x1e, 0xca, 0x1e, 0x67, 0xbd, 0x63}} ,
{{0x7b, 0x6e, 0x2a, 0xd2, 0x87, 0x48, 0xff, 0xa1, 0xca, 0xe9, 0x15, 0x85, 0xdc, 0xdb, 0x2c, 0x39, 0x12, 0x91, 0xa9, 0x20, 0xaa, 0x4f, 0x29, 0xf4, 0x15, 0x7a, 0xd2, 0xf5, 0x32, 0xcc, 0x60, 0x04}}},
@@ -1156,7 +1017,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0xbb, 0x40, 0x5e, 0xbc, 0x16, 0x92, 0x05, 0xc4, 0xc0, 0x4e, 0x72, 0x90, 0x0e, 0xab, 0xcf, 0x8a, 0xed, 0xef, 0xb9, 0x2d, 0x3b, 0xf8, 0x43, 0x5b, 0xba, 0x2d, 0xeb, 0x2f, 0x52, 0xd2, 0xd1, 0x5a}}},
{{{0x40, 0xb4, 0xab, 0xe6, 0xad, 0x9f, 0x46, 0x69, 0x4a, 0xb3, 0x8e, 0xaa, 0xea, 0x9c, 0x8a, 0x20, 0x16, 0x5d, 0x8c, 0x13, 0xbd, 0xf6, 0x1d, 0xc5, 0x24, 0xbd, 0x90, 0x2a, 0x1c, 0xc7, 0x13, 0x3b}} ,
{{0x54, 0xdc, 0x16, 0x0d, 0x18, 0xbe, 0x35, 0x64, 0x61, 0x52, 0x02, 0x80, 0xaf, 0x05, 0xf7, 0xa6, 0x42, 0xd3, 0x8f, 0x2e, 0x79, 0x26, 0xa8, 0xbb, 0xb2, 0x17, 0x48, 0xb2, 0x7a, 0x0a, 0x89, 0x14}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x20, 0xa8, 0x88, 0xe3, 0x91, 0xc0, 0x6e, 0xbb, 0x8a, 0x27, 0x82, 0x51, 0x83, 0xb2, 0x28, 0xa9, 0x83, 0xeb, 0xa6, 0xa9, 0x4d, 0x17, 0x59, 0x22, 0x54, 0x00, 0x50, 0x45, 0xcb, 0x48, 0x4b, 0x18}} ,
{{0x33, 0x7c, 0xe7, 0x26, 0xba, 0x4d, 0x32, 0xfe, 0x53, 0xf4, 0xfa, 0x83, 0xe3, 0xa5, 0x79, 0x66, 0x73, 0xef, 0x80, 0x23, 0x68, 0xc2, 0x60, 0xdd, 0xa9, 0x33, 0xdc, 0x03, 0x7a, 0xe0, 0xe0, 0x3e}}},
@@ -1166,7 +1027,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x6e, 0x44, 0xa3, 0xf9, 0x64, 0xaf, 0xe7, 0x6d, 0x7d, 0xdf, 0x1e, 0xac, 0x04, 0xea, 0x3b, 0x5f, 0x9b, 0xe8, 0x24, 0x9d, 0x0e, 0xe5, 0x2e, 0x3e, 0xdf, 0xa9, 0xf7, 0xd4, 0x50, 0x71, 0xf0, 0x78}}},
{{{0x3e, 0xa8, 0x38, 0xc2, 0x57, 0x56, 0x42, 0x9a, 0xb1, 0xe2, 0xf8, 0x45, 0xaa, 0x11, 0x48, 0x5f, 0x17, 0xc4, 0x54, 0x27, 0xdc, 0x5d, 0xaa, 0xdd, 0x41, 0xbc, 0xdf, 0x81, 0xb9, 0x53, 0xee, 0x52}} ,
{{0xc3, 0xf1, 0xa7, 0x6d, 0xb3, 0x5f, 0x92, 0x6f, 0xcc, 0x91, 0xb8, 0x95, 0x05, 0xdf, 0x3c, 0x64, 0x57, 0x39, 0x61, 0x51, 0xad, 0x8c, 0x38, 0x7b, 0xc8, 0xde, 0x00, 0x34, 0xbe, 0xa1, 0xb0, 0x7e}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x25, 0x24, 0x1d, 0x8a, 0x67, 0x20, 0xee, 0x42, 0xeb, 0x38, 0xed, 0x0b, 0x8b, 0xcd, 0x46, 0x9d, 0x5e, 0x6b, 0x1e, 0x24, 0x9d, 0x12, 0x05, 0x1a, 0xcc, 0x05, 0x4e, 0x92, 0x38, 0xe1, 0x1f, 0x50}} ,
{{0x4e, 0xee, 0x1c, 0x91, 0xe6, 0x11, 0xbd, 0x8e, 0x55, 0x1a, 0x18, 0x75, 0x66, 0xaf, 0x4d, 0x7b, 0x0f, 0xae, 0x6d, 0x85, 0xca, 0x82, 0x58, 0x21, 0x9c, 0x18, 0xe0, 0xed, 0xec, 0x22, 0x80, 0x2f}}},
@@ -1176,7 +1037,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0xa1, 0x0c, 0xf3, 0x08, 0x43, 0xd9, 0x24, 0x1e, 0x83, 0xa7, 0xdf, 0x91, 0xca, 0xbd, 0x69, 0x47, 0x8d, 0x1b, 0xe2, 0xb9, 0x4e, 0xb5, 0xe1, 0x76, 0xb3, 0x1c, 0x93, 0x03, 0xce, 0x5f, 0xb3, 0x5a}}},
{{{0x1d, 0xda, 0xe4, 0x61, 0x03, 0x50, 0xa9, 0x8b, 0x68, 0x18, 0xef, 0xb2, 0x1c, 0x84, 0x3b, 0xa2, 0x44, 0x95, 0xa3, 0x04, 0x3b, 0xd6, 0x99, 0x00, 0xaf, 0x76, 0x42, 0x67, 0x02, 0x7d, 0x85, 0x56}} ,
{{0xce, 0x72, 0x0e, 0x29, 0x84, 0xb2, 0x7d, 0xd2, 0x45, 0xbe, 0x57, 0x06, 0xed, 0x7f, 0xcf, 0xed, 0xcd, 0xef, 0x19, 0xd6, 0xbc, 0x15, 0x79, 0x64, 0xd2, 0x18, 0xe3, 0x20, 0x67, 0x3a, 0x54, 0x0b}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x52, 0xfd, 0x04, 0xc5, 0xfb, 0x99, 0xe7, 0xe8, 0xfb, 0x8c, 0xe1, 0x42, 0x03, 0xef, 0x9d, 0xd9, 0x9e, 0x4d, 0xf7, 0x80, 0xcf, 0x2e, 0xcc, 0x9b, 0x45, 0xc9, 0x7b, 0x7a, 0xbc, 0x37, 0xa8, 0x52}} ,
{{0x96, 0x11, 0x41, 0x8a, 0x47, 0x91, 0xfe, 0xb6, 0xda, 0x7a, 0x54, 0x63, 0xd1, 0x14, 0x35, 0x05, 0x86, 0x8c, 0xa9, 0x36, 0x3f, 0xf2, 0x85, 0x54, 0x4e, 0x92, 0xd8, 0x85, 0x01, 0x46, 0xd6, 0x50}}},
@@ -1186,7 +1047,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x93, 0x1c, 0x06, 0x2d, 0x66, 0x65, 0x02, 0xa4, 0x97, 0x18, 0xfd, 0x00, 0xe7, 0xab, 0x03, 0xec, 0xce, 0xc1, 0xbf, 0x37, 0xf8, 0x13, 0x53, 0xa5, 0xe5, 0x0c, 0x3a, 0xa8, 0x55, 0xb9, 0xff, 0x68}}},
{{{0xe4, 0xe6, 0x6d, 0x30, 0x7d, 0x30, 0x35, 0xc2, 0x78, 0x87, 0xf9, 0xfc, 0x6b, 0x5a, 0xc3, 0xb7, 0x65, 0xd8, 0x2e, 0xc7, 0xa5, 0x0c, 0xc6, 0xdc, 0x12, 0xaa, 0xd6, 0x4f, 0xc5, 0x38, 0xbc, 0x0e}} ,
{{0xe2, 0x3c, 0x76, 0x86, 0x38, 0xf2, 0x7b, 0x2c, 0x16, 0x78, 0x8d, 0xf5, 0xa4, 0x15, 0xda, 0xdb, 0x26, 0x85, 0xa0, 0x56, 0xdd, 0x1d, 0xe3, 0xb3, 0xfd, 0x40, 0xef, 0xf2, 0xd9, 0xa1, 0xb3, 0x04}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0xdb, 0x49, 0x0e, 0xe6, 0x58, 0x10, 0x7a, 0x52, 0xda, 0xb5, 0x7d, 0x37, 0x6a, 0x3e, 0xa1, 0x78, 0xce, 0xc7, 0x1c, 0x24, 0x23, 0xdb, 0x7d, 0xfb, 0x8c, 0x8d, 0xdc, 0x30, 0x67, 0x69, 0x75, 0x3b}} ,
{{0xa9, 0xea, 0x6d, 0x16, 0x16, 0x60, 0xf4, 0x60, 0x87, 0x19, 0x44, 0x8c, 0x4a, 0x8b, 0x3e, 0xfb, 0x16, 0x00, 0x00, 0x54, 0xa6, 0x9e, 0x9f, 0xef, 0xcf, 0xd9, 0xd2, 0x4c, 0x74, 0x31, 0xd0, 0x34}}},
@@ -1196,7 +1057,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0xb0, 0x8b, 0xaa, 0x1e, 0xec, 0xc7, 0xa5, 0x8f, 0x1f, 0x92, 0x04, 0xc6, 0x05, 0xf6, 0xdf, 0xa1, 0xcc, 0x1f, 0x81, 0xf5, 0x0e, 0x9c, 0x57, 0xdc, 0xe3, 0xbb, 0x06, 0x87, 0x1e, 0xfe, 0x23, 0x6c}}},
{{{0xd8, 0x2b, 0x5b, 0x16, 0xea, 0x20, 0xf1, 0xd3, 0x68, 0x8f, 0xae, 0x5b, 0xd0, 0xa9, 0x1a, 0x19, 0xa8, 0x36, 0xfb, 0x2b, 0x57, 0x88, 0x7d, 0x90, 0xd5, 0xa6, 0xf3, 0xdc, 0x38, 0x89, 0x4e, 0x1f}} ,
{{0xcc, 0x19, 0xda, 0x9b, 0x3b, 0x43, 0x48, 0x21, 0x2e, 0x23, 0x4d, 0x3d, 0xae, 0xf8, 0x8c, 0xfc, 0xdd, 0xa6, 0x74, 0x37, 0x65, 0xca, 0xee, 0x1a, 0x19, 0x8e, 0x9f, 0x64, 0x6f, 0x0c, 0x8b, 0x5a}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x25, 0xb9, 0xc2, 0xf0, 0x72, 0xb8, 0x15, 0x16, 0xcc, 0x8d, 0x3c, 0x6f, 0x25, 0xed, 0xf4, 0x46, 0x2e, 0x0c, 0x60, 0x0f, 0xe2, 0x84, 0x34, 0x55, 0x89, 0x59, 0x34, 0x1b, 0xf5, 0x8d, 0xfe, 0x08}} ,
{{0xf8, 0xab, 0x93, 0xbc, 0x44, 0xba, 0x1b, 0x75, 0x4b, 0x49, 0x6f, 0xd0, 0x54, 0x2e, 0x63, 0xba, 0xb5, 0xea, 0xed, 0x32, 0x14, 0xc9, 0x94, 0xd8, 0xc5, 0xce, 0xf4, 0x10, 0x68, 0xe0, 0x38, 0x27}}},
@@ -1206,7 +1067,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x6c, 0xba, 0x14, 0xc5, 0xea, 0x12, 0x9e, 0x2e, 0x82, 0x63, 0xce, 0x9b, 0x4a, 0xe7, 0x1d, 0xec, 0xf1, 0x2e, 0x51, 0x1c, 0xf4, 0xd0, 0x69, 0x15, 0x42, 0x9d, 0xa3, 0x3f, 0x0e, 0xbf, 0xe9, 0x5c}}},
{{{0xe4, 0x0d, 0xf4, 0xbd, 0xee, 0x31, 0x10, 0xed, 0xcb, 0x12, 0x86, 0xad, 0xd4, 0x2f, 0x90, 0x37, 0x32, 0xc3, 0x0b, 0x73, 0xec, 0x97, 0x85, 0xa4, 0x01, 0x1c, 0x76, 0x35, 0xfe, 0x75, 0xdd, 0x71}} ,
{{0x11, 0xa4, 0x88, 0x9f, 0x3e, 0x53, 0x69, 0x3b, 0x1b, 0xe0, 0xf7, 0xba, 0x9b, 0xad, 0x4e, 0x81, 0x5f, 0xb5, 0x5c, 0xae, 0xbe, 0x67, 0x86, 0x37, 0x34, 0x8e, 0x07, 0x32, 0x45, 0x4a, 0x67, 0x39}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x90, 0x70, 0x58, 0x20, 0x03, 0x1e, 0x67, 0xb2, 0xc8, 0x9b, 0x58, 0xc5, 0xb1, 0xeb, 0x2d, 0x4a, 0xde, 0x82, 0x8c, 0xf2, 0xd2, 0x14, 0xb8, 0x70, 0x61, 0x4e, 0x73, 0xd6, 0x0b, 0x6b, 0x0d, 0x30}} ,
{{0x81, 0xfc, 0x55, 0x5c, 0xbf, 0xa7, 0xc4, 0xbd, 0xe2, 0xf0, 0x4b, 0x8f, 0xe9, 0x7d, 0x99, 0xfa, 0xd3, 0xab, 0xbc, 0xc7, 0x83, 0x2b, 0x04, 0x7f, 0x0c, 0x19, 0x43, 0x03, 0x3d, 0x07, 0xca, 0x40}}},
@@ -1216,7 +1077,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x17, 0x62, 0x6b, 0x14, 0xa1, 0x7c, 0xd0, 0x79, 0x6e, 0xd8, 0x8a, 0xa5, 0x6d, 0x8c, 0x93, 0xd2, 0x3f, 0xec, 0x44, 0x8d, 0x6e, 0x91, 0x01, 0x8c, 0x8f, 0xee, 0x01, 0x8f, 0xc0, 0xb4, 0x85, 0x0e}}},
{{{0x02, 0x3a, 0x70, 0x41, 0xe4, 0x11, 0x57, 0x23, 0xac, 0xe6, 0xfc, 0x54, 0x7e, 0xcd, 0xd7, 0x22, 0xcb, 0x76, 0x9f, 0x20, 0xce, 0xa0, 0x73, 0x76, 0x51, 0x3b, 0xa4, 0xf8, 0xe3, 0x62, 0x12, 0x6c}} ,
{{0x7f, 0x00, 0x9c, 0x26, 0x0d, 0x6f, 0x48, 0x7f, 0x3a, 0x01, 0xed, 0xc5, 0x96, 0xb0, 0x1f, 0x4f, 0xa8, 0x02, 0x62, 0x27, 0x8a, 0x50, 0x8d, 0x9a, 0x8b, 0x52, 0x0f, 0x1e, 0xcf, 0x41, 0x38, 0x19}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0xf5, 0x6c, 0xd4, 0x2f, 0x0f, 0x69, 0x0f, 0x87, 0x3f, 0x61, 0x65, 0x1e, 0x35, 0x34, 0x85, 0xba, 0x02, 0x30, 0xac, 0x25, 0x3d, 0xe2, 0x62, 0xf1, 0xcc, 0xe9, 0x1b, 0xc2, 0xef, 0x6a, 0x42, 0x57}} ,
{{0x34, 0x1f, 0x2e, 0xac, 0xd1, 0xc7, 0x04, 0x52, 0x32, 0x66, 0xb2, 0x33, 0x73, 0x21, 0x34, 0x54, 0xf7, 0x71, 0xed, 0x06, 0xb0, 0xff, 0xa6, 0x59, 0x6f, 0x8a, 0x4e, 0xfb, 0x02, 0xb0, 0x45, 0x6b}}},
@@ -1226,7 +1087,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0xdd, 0xb5, 0x7d, 0xab, 0x5a, 0x21, 0x41, 0x53, 0xbb, 0x17, 0x79, 0x0d, 0xd1, 0xa8, 0x0c, 0x0c, 0x20, 0x88, 0x09, 0xe9, 0x84, 0xe8, 0x25, 0x11, 0x67, 0x7a, 0x8b, 0x1a, 0xe4, 0x5d, 0xe1, 0x5d}}},
{{{0x37, 0xea, 0xfe, 0x65, 0x3b, 0x25, 0xe8, 0xe1, 0xc2, 0xc5, 0x02, 0xa4, 0xbe, 0x98, 0x0a, 0x2b, 0x61, 0xc1, 0x9b, 0xe2, 0xd5, 0x92, 0xe6, 0x9e, 0x7d, 0x1f, 0xca, 0x43, 0x88, 0x8b, 0x2c, 0x59}} ,
{{0xe0, 0xb5, 0x00, 0x1d, 0x2a, 0x6f, 0xaf, 0x79, 0x86, 0x2f, 0xa6, 0x5a, 0x93, 0xd1, 0xfe, 0xae, 0x3a, 0xee, 0xdb, 0x7c, 0x61, 0xbe, 0x7c, 0x01, 0xf9, 0xfe, 0x52, 0xdc, 0xd8, 0x52, 0xa3, 0x42}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x22, 0xaf, 0x13, 0x37, 0xbd, 0x37, 0x71, 0xac, 0x04, 0x46, 0x63, 0xac, 0xa4, 0x77, 0xed, 0x25, 0x38, 0xe0, 0x15, 0xa8, 0x64, 0x00, 0x0d, 0xce, 0x51, 0x01, 0xa9, 0xbc, 0x0f, 0x03, 0x1c, 0x04}} ,
{{0x89, 0xf9, 0x80, 0x07, 0xcf, 0x3f, 0xb3, 0xe9, 0xe7, 0x45, 0x44, 0x3d, 0x2a, 0x7c, 0xe9, 0xe4, 0x16, 0x5c, 0x5e, 0x65, 0x1c, 0xc7, 0x7d, 0xc6, 0x7a, 0xfb, 0x43, 0xee, 0x25, 0x76, 0x46, 0x72}}},
@@ -1236,7 +1097,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x0d, 0xac, 0x66, 0x6d, 0xc3, 0x8b, 0xba, 0x16, 0xb5, 0xe2, 0xa0, 0x0d, 0x0c, 0xbd, 0xa4, 0x8e, 0x18, 0x6c, 0xf2, 0xdc, 0xf9, 0xdc, 0x4a, 0x86, 0x25, 0x95, 0x14, 0xcb, 0xd8, 0x1a, 0x04, 0x0f}}},
{{{0x97, 0xa5, 0xdb, 0x8b, 0x2d, 0xaa, 0x42, 0x11, 0x09, 0xf2, 0x93, 0xbb, 0xd9, 0x06, 0x84, 0x4e, 0x11, 0xa8, 0xa0, 0x25, 0x2b, 0xa6, 0x5f, 0xae, 0xc4, 0xb4, 0x4c, 0xc8, 0xab, 0xc7, 0x3b, 0x02}} ,
{{0xee, 0xc9, 0x29, 0x0f, 0xdf, 0x11, 0x85, 0xed, 0xce, 0x0d, 0x62, 0x2c, 0x8f, 0x4b, 0xf9, 0x04, 0xe9, 0x06, 0x72, 0x1d, 0x37, 0x20, 0x50, 0xc9, 0x14, 0xeb, 0xec, 0x39, 0xa7, 0x97, 0x2b, 0x4d}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x69, 0xd1, 0x39, 0xbd, 0xfb, 0x33, 0xbe, 0xc4, 0xf0, 0x5c, 0xef, 0xf0, 0x56, 0x68, 0xfc, 0x97, 0x47, 0xc8, 0x72, 0xb6, 0x53, 0xa4, 0x0a, 0x98, 0xa5, 0xb4, 0x37, 0x71, 0xcf, 0x66, 0x50, 0x6d}} ,
{{0x17, 0xa4, 0x19, 0x52, 0x11, 0x47, 0xb3, 0x5c, 0x5b, 0xa9, 0x2e, 0x22, 0xb4, 0x00, 0x52, 0xf9, 0x57, 0x18, 0xb8, 0xbe, 0x5a, 0xe3, 0xab, 0x83, 0xc8, 0x87, 0x0a, 0x2a, 0xd8, 0x8c, 0xbb, 0x54}}},
@@ -1246,7 +1107,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x20, 0xd6, 0xd8, 0x91, 0x54, 0xa7, 0xf3, 0x20, 0x4b, 0x34, 0x06, 0xfa, 0x30, 0xc8, 0x6f, 0x14, 0x10, 0x65, 0x74, 0x13, 0x4e, 0xf0, 0x69, 0x26, 0xce, 0xcf, 0x90, 0xf4, 0xd0, 0xc5, 0xc8, 0x64}}},
{{{0x26, 0xa2, 0x50, 0x02, 0x24, 0x72, 0xf1, 0xf0, 0x4e, 0x2d, 0x93, 0xd5, 0x08, 0xe7, 0xae, 0x38, 0xf7, 0x18, 0xa5, 0x32, 0x34, 0xc2, 0xf0, 0xa6, 0xec, 0xb9, 0x61, 0x7b, 0x64, 0x99, 0xac, 0x71}} ,
{{0x25, 0xcf, 0x74, 0x55, 0x1b, 0xaa, 0xa9, 0x38, 0x41, 0x40, 0xd5, 0x95, 0x95, 0xab, 0x1c, 0x5e, 0xbc, 0x41, 0x7e, 0x14, 0x30, 0xbe, 0x13, 0x89, 0xf4, 0xe5, 0xeb, 0x28, 0xc0, 0xc2, 0x96, 0x3a}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x2b, 0x77, 0x45, 0xec, 0x67, 0x76, 0x32, 0x4c, 0xb9, 0xdf, 0x25, 0x32, 0x6b, 0xcb, 0xe7, 0x14, 0x61, 0x43, 0xee, 0xba, 0x9b, 0x71, 0xef, 0xd2, 0x48, 0x65, 0xbb, 0x1b, 0x8a, 0x13, 0x1b, 0x22}} ,
{{0x84, 0xad, 0x0c, 0x18, 0x38, 0x5a, 0xba, 0xd0, 0x98, 0x59, 0xbf, 0x37, 0xb0, 0x4f, 0x97, 0x60, 0x20, 0xb3, 0x9b, 0x97, 0xf6, 0x08, 0x6c, 0xa4, 0xff, 0xfb, 0xb7, 0xfa, 0x95, 0xb2, 0x51, 0x79}}},
@@ -1256,7 +1117,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0xa9, 0x85, 0xf4, 0xe7, 0xb0, 0x15, 0x33, 0x84, 0x1b, 0x14, 0x1a, 0x02, 0xd9, 0x3b, 0xad, 0x0f, 0x43, 0x6c, 0xea, 0x3e, 0x0f, 0x7e, 0xda, 0xdd, 0x6b, 0x4c, 0x7f, 0x6e, 0xd4, 0x6b, 0xbf, 0x0f}}},
{{{0x47, 0x9f, 0x7c, 0x56, 0x7c, 0x43, 0x91, 0x1c, 0xbb, 0x4e, 0x72, 0x3e, 0x64, 0xab, 0xa0, 0xa0, 0xdf, 0xb4, 0xd8, 0x87, 0x3a, 0xbd, 0xa8, 0x48, 0xc9, 0xb8, 0xef, 0x2e, 0xad, 0x6f, 0x84, 0x4f}} ,
{{0x2d, 0x2d, 0xf0, 0x1b, 0x7e, 0x2a, 0x6c, 0xf8, 0xa9, 0x6a, 0xe1, 0xf0, 0x99, 0xa1, 0x67, 0x9a, 0xd4, 0x13, 0xca, 0xca, 0xba, 0x27, 0x92, 0xaa, 0xa1, 0x5d, 0x50, 0xde, 0xcc, 0x40, 0x26, 0x0a}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x9f, 0x3e, 0xf2, 0xb2, 0x90, 0xce, 0xdb, 0x64, 0x3e, 0x03, 0xdd, 0x37, 0x36, 0x54, 0x70, 0x76, 0x24, 0xb5, 0x69, 0x03, 0xfc, 0xa0, 0x2b, 0x74, 0xb2, 0x05, 0x0e, 0xcc, 0xd8, 0x1f, 0x6a, 0x1f}} ,
{{0x19, 0x5e, 0x60, 0x69, 0x58, 0x86, 0xa0, 0x31, 0xbd, 0x32, 0xe9, 0x2c, 0x5c, 0xd2, 0x85, 0xba, 0x40, 0x64, 0xa8, 0x74, 0xf8, 0x0e, 0x1c, 0xb3, 0xa9, 0x69, 0xe8, 0x1e, 0x40, 0x64, 0x99, 0x77}}},
@@ -1266,7 +1127,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0xca, 0x59, 0x74, 0x1a, 0x11, 0xef, 0x6d, 0xf7, 0x39, 0x5c, 0x3b, 0x1f, 0xfa, 0xe3, 0x40, 0x41, 0x23, 0x9e, 0xf6, 0xd1, 0x21, 0xa2, 0xbf, 0xad, 0x65, 0x42, 0x6b, 0x59, 0x8a, 0xe8, 0xc5, 0x7f}}},
{{{0x64, 0x05, 0x7a, 0x84, 0x4a, 0x13, 0xc3, 0xf6, 0xb0, 0x6e, 0x9a, 0x6b, 0x53, 0x6b, 0x32, 0xda, 0xd9, 0x74, 0x75, 0xc4, 0xba, 0x64, 0x3d, 0x3b, 0x08, 0xdd, 0x10, 0x46, 0xef, 0xc7, 0x90, 0x1f}} ,
{{0x7b, 0x2f, 0x3a, 0xce, 0xc8, 0xa1, 0x79, 0x3c, 0x30, 0x12, 0x44, 0x28, 0xf6, 0xbc, 0xff, 0xfd, 0xf4, 0xc0, 0x97, 0xb0, 0xcc, 0xc3, 0x13, 0x7a, 0xb9, 0x9a, 0x16, 0xe4, 0xcb, 0x4c, 0x34, 0x63}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x07, 0x4e, 0xd3, 0x2d, 0x09, 0x33, 0x0e, 0xd2, 0x0d, 0xbe, 0x3e, 0xe7, 0xe4, 0xaa, 0xb7, 0x00, 0x8b, 0xe8, 0xad, 0xaa, 0x7a, 0x8d, 0x34, 0x28, 0xa9, 0x81, 0x94, 0xc5, 0xe7, 0x42, 0xac, 0x47}} ,
{{0x24, 0x89, 0x7a, 0x8f, 0xb5, 0x9b, 0xf0, 0xc2, 0x03, 0x64, 0xd0, 0x1e, 0xf5, 0xa4, 0xb2, 0xf3, 0x74, 0xe9, 0x1a, 0x16, 0xfd, 0xcb, 0x15, 0xea, 0xeb, 0x10, 0x6c, 0x35, 0xd1, 0xc1, 0xa6, 0x28}}},
@@ -1276,7 +1137,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x19, 0xa6, 0x5a, 0x65, 0x93, 0xc3, 0xb5, 0x31, 0x22, 0x4f, 0xf3, 0xf6, 0x0f, 0xeb, 0x28, 0xc3, 0x7c, 0xeb, 0xce, 0x86, 0xec, 0x67, 0x76, 0x6e, 0x35, 0x45, 0x7b, 0xd8, 0x6b, 0x92, 0x01, 0x65}}},
{{{0x3d, 0xd5, 0x9a, 0x64, 0x73, 0x36, 0xb1, 0xd6, 0x86, 0x98, 0x42, 0x3f, 0x8a, 0xf1, 0xc7, 0xf5, 0x42, 0xa8, 0x9c, 0x52, 0xa8, 0xdc, 0xf9, 0x24, 0x3f, 0x4a, 0xa1, 0xa4, 0x5b, 0xe8, 0x62, 0x1a}} ,
{{0xc5, 0xbd, 0xc8, 0x14, 0xd5, 0x0d, 0xeb, 0xe1, 0xa5, 0xe6, 0x83, 0x11, 0x09, 0x00, 0x1d, 0x55, 0x83, 0x51, 0x7e, 0x75, 0x00, 0x81, 0xb9, 0xcb, 0xd8, 0xc5, 0xe5, 0xa1, 0xd9, 0x17, 0x6d, 0x1f}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0xea, 0xf9, 0xe4, 0xe9, 0xe1, 0x52, 0x3f, 0x51, 0x19, 0x0d, 0xdd, 0xd9, 0x9d, 0x93, 0x31, 0x87, 0x23, 0x09, 0xd5, 0x83, 0xeb, 0x92, 0x09, 0x76, 0x6e, 0xe3, 0xf8, 0xc0, 0xa2, 0x66, 0xb5, 0x36}} ,
{{0x3a, 0xbb, 0x39, 0xed, 0x32, 0x02, 0xe7, 0x43, 0x7a, 0x38, 0x14, 0x84, 0xe3, 0x44, 0xd2, 0x5e, 0x94, 0xdd, 0x78, 0x89, 0x55, 0x4c, 0x73, 0x9e, 0xe1, 0xe4, 0x3e, 0x43, 0xd0, 0x4a, 0xde, 0x1b}}},
@@ -1286,7 +1147,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x46, 0xda, 0x9e, 0x51, 0x3a, 0xe6, 0xd1, 0xa6, 0xbb, 0x4d, 0x7b, 0x08, 0xbe, 0x8c, 0xd5, 0xf3, 0x3f, 0xfd, 0xf7, 0x44, 0x80, 0x2d, 0x53, 0x4b, 0xd0, 0x87, 0x68, 0xc1, 0xb5, 0xd8, 0xf7, 0x07}}},
{{{0xf4, 0x10, 0x46, 0xbe, 0xb7, 0xd2, 0xd1, 0xce, 0x5e, 0x76, 0xa2, 0xd7, 0x03, 0xdc, 0xe4, 0x81, 0x5a, 0xf6, 0x3c, 0xde, 0xae, 0x7a, 0x9d, 0x21, 0x34, 0xa5, 0xf6, 0xa9, 0x73, 0xe2, 0x8d, 0x60}} ,
{{0xfa, 0x44, 0x71, 0xf6, 0x41, 0xd8, 0xc6, 0x58, 0x13, 0x37, 0xeb, 0x84, 0x0f, 0x96, 0xc7, 0xdc, 0xc8, 0xa9, 0x7a, 0x83, 0xb2, 0x2f, 0x31, 0xb1, 0x1a, 0xd8, 0x98, 0x3f, 0x11, 0xd0, 0x31, 0x3b}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x81, 0xd5, 0x34, 0x16, 0x01, 0xa3, 0x93, 0xea, 0x52, 0x94, 0xec, 0x93, 0xb7, 0x81, 0x11, 0x2d, 0x58, 0xf9, 0xb5, 0x0a, 0xaa, 0x4f, 0xf6, 0x2e, 0x3f, 0x36, 0xbf, 0x33, 0x5a, 0xe7, 0xd1, 0x08}} ,
{{0x1a, 0xcf, 0x42, 0xae, 0xcc, 0xb5, 0x77, 0x39, 0xc4, 0x5b, 0x5b, 0xd0, 0x26, 0x59, 0x27, 0xd0, 0x55, 0x71, 0x12, 0x9d, 0x88, 0x3d, 0x9c, 0xea, 0x41, 0x6a, 0xf0, 0x50, 0x93, 0x93, 0xdd, 0x47}}},
@@ -1296,7 +1157,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x13, 0xa8, 0x82, 0x3e, 0xe9, 0x13, 0xad, 0xeb, 0x01, 0xca, 0xcf, 0xda, 0xcd, 0xf7, 0x6c, 0xc7, 0x7a, 0xdc, 0x1e, 0x6e, 0xc8, 0x4e, 0x55, 0x62, 0x80, 0xea, 0x78, 0x0c, 0x86, 0xb9, 0x40, 0x51}}},
{{{0x27, 0xae, 0xd3, 0x0d, 0x4c, 0x8f, 0x34, 0xea, 0x7d, 0x3c, 0xe5, 0x8a, 0xcf, 0x5b, 0x92, 0xd8, 0x30, 0x16, 0xb4, 0xa3, 0x75, 0xff, 0xeb, 0x27, 0xc8, 0x5c, 0x6c, 0xc2, 0xee, 0x6c, 0x21, 0x0b}} ,
{{0xc3, 0xba, 0x12, 0x53, 0x2a, 0xaa, 0x77, 0xad, 0x19, 0x78, 0x55, 0x8a, 0x2e, 0x60, 0x87, 0xc2, 0x6e, 0x91, 0x38, 0x91, 0x3f, 0x7a, 0xc5, 0x24, 0x8f, 0x51, 0xc5, 0xde, 0xb0, 0x53, 0x30, 0x56}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x02, 0xfe, 0x54, 0x12, 0x18, 0xca, 0x7d, 0xa5, 0x68, 0x43, 0xa3, 0x6d, 0x14, 0x2a, 0x6a, 0xa5, 0x8e, 0x32, 0xe7, 0x63, 0x4f, 0xe3, 0xc6, 0x44, 0x3e, 0xab, 0x63, 0xca, 0x17, 0x86, 0x74, 0x3f}} ,
{{0x1e, 0x64, 0xc1, 0x7d, 0x52, 0xdc, 0x13, 0x5a, 0xa1, 0x9c, 0x4e, 0xee, 0x99, 0x28, 0xbb, 0x4c, 0xee, 0xac, 0xa9, 0x1b, 0x89, 0xa2, 0x38, 0x39, 0x7b, 0xc4, 0x0f, 0x42, 0xe6, 0x89, 0xed, 0x0f}}},
@@ -1306,7 +1167,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0xe7, 0x29, 0xf9, 0x37, 0xd9, 0x46, 0x5a, 0xcd, 0x70, 0xfe, 0x4d, 0x5b, 0xbf, 0xa5, 0xcf, 0x91, 0xf4, 0xef, 0xee, 0x8a, 0x29, 0xd0, 0xe7, 0xc4, 0x25, 0x92, 0x8a, 0xff, 0x36, 0xfc, 0xe4, 0x49}}},
{{{0xbd, 0x00, 0xb9, 0x04, 0x7d, 0x35, 0xfc, 0xeb, 0xd0, 0x0b, 0x05, 0x32, 0x52, 0x7a, 0x89, 0x24, 0x75, 0x50, 0xe1, 0x63, 0x02, 0x82, 0x8e, 0xe7, 0x85, 0x0c, 0xf2, 0x56, 0x44, 0x37, 0x83, 0x25}} ,
{{0x8f, 0xa1, 0xce, 0xcb, 0x60, 0xda, 0x12, 0x02, 0x1e, 0x29, 0x39, 0x2a, 0x03, 0xb7, 0xeb, 0x77, 0x40, 0xea, 0xc9, 0x2b, 0x2c, 0xd5, 0x7d, 0x7e, 0x2c, 0xc7, 0x5a, 0xfd, 0xff, 0xc4, 0xd1, 0x62}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x1d, 0x88, 0x98, 0x5b, 0x4e, 0xfc, 0x41, 0x24, 0x05, 0xe6, 0x50, 0x2b, 0xae, 0x96, 0x51, 0xd9, 0x6b, 0x72, 0xb2, 0x33, 0x42, 0x98, 0x68, 0xbb, 0x10, 0x5a, 0x7a, 0x8c, 0x9d, 0x07, 0xb4, 0x05}} ,
{{0x2f, 0x61, 0x9f, 0xd7, 0xa8, 0x3f, 0x83, 0x8c, 0x10, 0x69, 0x90, 0xe6, 0xcf, 0xd2, 0x63, 0xa3, 0xe4, 0x54, 0x7e, 0xe5, 0x69, 0x13, 0x1c, 0x90, 0x57, 0xaa, 0xe9, 0x53, 0x22, 0x43, 0x29, 0x23}}},
@@ -1316,7 +1177,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0xf5, 0xff, 0xcc, 0xf0, 0xb4, 0x12, 0x03, 0x5f, 0xc9, 0x84, 0xcb, 0x1d, 0x17, 0xe0, 0xbc, 0xcc, 0x03, 0x62, 0xa9, 0x8b, 0x94, 0xa6, 0xaa, 0x18, 0xcb, 0x27, 0x8d, 0x49, 0xa6, 0x17, 0x15, 0x07}}},
{{{0xd9, 0xb6, 0xd4, 0x9d, 0xd4, 0x6a, 0xaf, 0x70, 0x07, 0x2c, 0x10, 0x9e, 0xbd, 0x11, 0xad, 0xe4, 0x26, 0x33, 0x70, 0x92, 0x78, 0x1c, 0x74, 0x9f, 0x75, 0x60, 0x56, 0xf4, 0x39, 0xa8, 0xa8, 0x62}} ,
{{0x3b, 0xbf, 0x55, 0x35, 0x61, 0x8b, 0x44, 0x97, 0xe8, 0x3a, 0x55, 0xc1, 0xc8, 0x3b, 0xfd, 0x95, 0x29, 0x11, 0x60, 0x96, 0x1e, 0xcb, 0x11, 0x9d, 0xc2, 0x03, 0x8a, 0x1b, 0xc6, 0xd6, 0x45, 0x3d}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x7e, 0x0e, 0x50, 0xb2, 0xcc, 0x0d, 0x6b, 0xa6, 0x71, 0x5b, 0x42, 0xed, 0xbd, 0xaf, 0xac, 0xf0, 0xfc, 0x12, 0xa2, 0x3f, 0x4e, 0xda, 0xe8, 0x11, 0xf3, 0x23, 0xe1, 0x04, 0x62, 0x03, 0x1c, 0x4e}} ,
{{0xc8, 0xb1, 0x1b, 0x6f, 0x73, 0x61, 0x3d, 0x27, 0x0d, 0x7d, 0x7a, 0x25, 0x5f, 0x73, 0x0e, 0x2f, 0x93, 0xf6, 0x24, 0xd8, 0x4f, 0x90, 0xac, 0xa2, 0x62, 0x0a, 0xf0, 0x61, 0xd9, 0x08, 0x59, 0x6a}}},
@@ -1326,7 +1187,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x8d, 0xa6, 0xd5, 0xac, 0x9d, 0xcc, 0x68, 0x75, 0x7f, 0xc3, 0x4d, 0x4b, 0xdd, 0x6c, 0xbb, 0x11, 0x5a, 0x60, 0xe5, 0xbd, 0x7d, 0x27, 0x8b, 0xda, 0xb4, 0x95, 0xf6, 0x03, 0x27, 0xa4, 0x92, 0x3f}}},
{{{0x22, 0xd6, 0xb5, 0x17, 0x84, 0xbf, 0x12, 0xcc, 0x23, 0x14, 0x4a, 0xdf, 0x14, 0x31, 0xbc, 0xa1, 0xac, 0x6e, 0xab, 0xfa, 0x57, 0x11, 0x53, 0xb3, 0x27, 0xe6, 0xf9, 0x47, 0x33, 0x44, 0x34, 0x1e}} ,
{{0x79, 0xfc, 0xa6, 0xb4, 0x0b, 0x35, 0x20, 0xc9, 0x4d, 0x22, 0x84, 0xc4, 0xa9, 0x20, 0xec, 0x89, 0x94, 0xba, 0x66, 0x56, 0x48, 0xb9, 0x87, 0x7f, 0xca, 0x1e, 0x06, 0xed, 0xa5, 0x55, 0x59, 0x29}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x56, 0xe1, 0xf5, 0xf1, 0xd5, 0xab, 0xa8, 0x2b, 0xae, 0x89, 0xf3, 0xcf, 0x56, 0x9f, 0xf2, 0x4b, 0x31, 0xbc, 0x18, 0xa9, 0x06, 0x5b, 0xbe, 0xb4, 0x61, 0xf8, 0xb2, 0x06, 0x9c, 0x81, 0xab, 0x4c}} ,
{{0x1f, 0x68, 0x76, 0x01, 0x16, 0x38, 0x2b, 0x0f, 0x77, 0x97, 0x92, 0x67, 0x4e, 0x86, 0x6a, 0x8b, 0xe5, 0xe8, 0x0c, 0xf7, 0x36, 0x39, 0xb5, 0x33, 0xe6, 0xcf, 0x5e, 0xbd, 0x18, 0xfb, 0x10, 0x1f}}},
@@ -1336,7 +1197,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0xf5, 0x1a, 0x61, 0xf7, 0x37, 0x9d, 0x00, 0xf4, 0xf2, 0x69, 0x6f, 0x4b, 0x01, 0x85, 0x19, 0x45, 0x4d, 0x7f, 0x02, 0x7c, 0x6a, 0x05, 0x47, 0x6c, 0x1f, 0x81, 0x20, 0xd4, 0xe8, 0x50, 0x27, 0x72}}},
{{{0x2c, 0x3a, 0xe5, 0xad, 0xf4, 0xdd, 0x2d, 0xf7, 0x5c, 0x44, 0xb5, 0x5b, 0x21, 0xa3, 0x89, 0x5f, 0x96, 0x45, 0xca, 0x4d, 0xa4, 0x21, 0x99, 0x70, 0xda, 0xc4, 0xc4, 0xa0, 0xe5, 0xf4, 0xec, 0x0a}} ,
{{0x07, 0x68, 0x21, 0x65, 0xe9, 0x08, 0xa0, 0x0b, 0x6a, 0x4a, 0xba, 0xb5, 0x80, 0xaf, 0xd0, 0x1b, 0xc5, 0xf5, 0x4b, 0x73, 0x50, 0x60, 0x2d, 0x71, 0x69, 0x61, 0x0e, 0xc0, 0x20, 0x40, 0x30, 0x19}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0xd0, 0x75, 0x57, 0x3b, 0xeb, 0x5c, 0x14, 0x56, 0x50, 0xc9, 0x4f, 0xb8, 0xb8, 0x1e, 0xa3, 0xf4, 0xab, 0xf5, 0xa9, 0x20, 0x15, 0x94, 0x82, 0xda, 0x96, 0x1c, 0x9b, 0x59, 0x8c, 0xff, 0xf4, 0x51}} ,
{{0xc1, 0x3a, 0x86, 0xd7, 0xb0, 0x06, 0x84, 0x7f, 0x1b, 0xbd, 0xd4, 0x07, 0x78, 0x80, 0x2e, 0xb1, 0xb4, 0xee, 0x52, 0x38, 0xee, 0x9a, 0xf9, 0xf6, 0xf3, 0x41, 0x6e, 0xd4, 0x88, 0x95, 0xac, 0x35}}},
@@ -1346,7 +1207,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x34, 0xe9, 0x59, 0x93, 0x9d, 0x26, 0x80, 0x54, 0xf2, 0xcc, 0x3c, 0xc2, 0x25, 0x85, 0xe3, 0x6a, 0xc1, 0x62, 0x04, 0xa7, 0x08, 0x32, 0x6d, 0xa1, 0x39, 0x84, 0x8a, 0x3b, 0x87, 0x5f, 0x11, 0x13}}},
{{{0xda, 0x03, 0x34, 0x66, 0xc4, 0x0c, 0x73, 0x6e, 0xbc, 0x24, 0xb5, 0xf9, 0x70, 0x81, 0x52, 0xe9, 0xf4, 0x7c, 0x23, 0xdd, 0x9f, 0xb8, 0x46, 0xef, 0x1d, 0x22, 0x55, 0x7d, 0x71, 0xc4, 0x42, 0x33}} ,
{{0xc5, 0x37, 0x69, 0x5b, 0xa8, 0xc6, 0x9d, 0xa4, 0xfc, 0x61, 0x6e, 0x68, 0x46, 0xea, 0xd7, 0x1c, 0x67, 0xd2, 0x7d, 0xfa, 0xf1, 0xcc, 0x54, 0x8d, 0x36, 0x35, 0xc9, 0x00, 0xdf, 0x6c, 0x67, 0x50}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x9a, 0x4d, 0x42, 0x29, 0x5d, 0xa4, 0x6b, 0x6f, 0xa8, 0x8a, 0x4d, 0x91, 0x7b, 0xd2, 0xdf, 0x36, 0xef, 0x01, 0x22, 0xc5, 0xcc, 0x8d, 0xeb, 0x58, 0x3d, 0xb3, 0x50, 0xfc, 0x8b, 0x97, 0x96, 0x33}} ,
{{0x93, 0x33, 0x07, 0xc8, 0x4a, 0xca, 0xd0, 0xb1, 0xab, 0xbd, 0xdd, 0xa7, 0x7c, 0xac, 0x3e, 0x45, 0xcb, 0xcc, 0x07, 0x91, 0xbf, 0x35, 0x9d, 0xcb, 0x7d, 0x12, 0x3c, 0x11, 0x59, 0x13, 0xcf, 0x5c}}},
@@ -1356,7 +1217,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x8b, 0xa4, 0xd6, 0x50, 0xb4, 0xaa, 0x5d, 0x64, 0x64, 0x76, 0x2e, 0xa1, 0xa6, 0xb3, 0xb8, 0x7c, 0x7a, 0x56, 0xf5, 0x5c, 0x4e, 0x84, 0x5c, 0xfb, 0xdd, 0xca, 0x48, 0x8b, 0x48, 0xb9, 0xba, 0x34}}},
{{{0xc5, 0xe3, 0xe8, 0xae, 0x17, 0x27, 0xe3, 0x64, 0x60, 0x71, 0x47, 0x29, 0x02, 0x0f, 0x92, 0x5d, 0x10, 0x93, 0xc8, 0x0e, 0xa1, 0xed, 0xba, 0xa9, 0x96, 0x1c, 0xc5, 0x76, 0x30, 0xcd, 0xf9, 0x30}} ,
{{0x95, 0xb0, 0xbd, 0x8c, 0xbc, 0xa7, 0x4f, 0x7e, 0xfd, 0x4e, 0x3a, 0xbf, 0x5f, 0x04, 0x79, 0x80, 0x2b, 0x5a, 0x9f, 0x4f, 0x68, 0x21, 0x19, 0x71, 0xc6, 0x20, 0x01, 0x42, 0xaa, 0xdf, 0xae, 0x2c}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x90, 0x6e, 0x7e, 0x4b, 0x71, 0x93, 0xc0, 0x72, 0xed, 0xeb, 0x71, 0x24, 0x97, 0x26, 0x9c, 0xfe, 0xcb, 0x3e, 0x59, 0x19, 0xa8, 0x0f, 0x75, 0x7d, 0xbe, 0x18, 0xe6, 0x96, 0x1e, 0x95, 0x70, 0x60}} ,
{{0x89, 0x66, 0x3e, 0x1d, 0x4c, 0x5f, 0xfe, 0xc0, 0x04, 0x43, 0xd6, 0x44, 0x19, 0xb5, 0xad, 0xc7, 0x22, 0xdc, 0x71, 0x28, 0x64, 0xde, 0x41, 0x38, 0x27, 0x8f, 0x2c, 0x6b, 0x08, 0xb8, 0xb8, 0x7b}}},
@@ -1366,7 +1227,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x42, 0xa3, 0xf1, 0xc5, 0xb4, 0x0f, 0xd8, 0xc8, 0x8d, 0x15, 0x31, 0xbd, 0xf8, 0x07, 0x8b, 0xcd, 0x08, 0x8a, 0xfb, 0x18, 0x07, 0xfe, 0x8e, 0x52, 0x86, 0xef, 0xbe, 0xec, 0x49, 0x52, 0x99, 0x08}}},
{{{0x0f, 0xa9, 0xd5, 0x01, 0xaa, 0x48, 0x4f, 0x28, 0x66, 0x32, 0x1a, 0xba, 0x7c, 0xea, 0x11, 0x80, 0x17, 0x18, 0x9b, 0x56, 0x88, 0x25, 0x06, 0x69, 0x12, 0x2c, 0xea, 0x56, 0x69, 0x41, 0x24, 0x19}} ,
{{0xde, 0x21, 0xf0, 0xda, 0x8a, 0xfb, 0xb1, 0xb8, 0xcd, 0xc8, 0x6a, 0x82, 0x19, 0x73, 0xdb, 0xc7, 0xcf, 0x88, 0xeb, 0x96, 0xee, 0x6f, 0xfb, 0x06, 0xd2, 0xcd, 0x7d, 0x7b, 0x12, 0x28, 0x8e, 0x0c}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x93, 0x44, 0x97, 0xce, 0x28, 0xff, 0x3a, 0x40, 0xc4, 0xf5, 0xf6, 0x9b, 0xf4, 0x6b, 0x07, 0x84, 0xfb, 0x98, 0xd8, 0xec, 0x8c, 0x03, 0x57, 0xec, 0x49, 0xed, 0x63, 0xb6, 0xaa, 0xff, 0x98, 0x28}} ,
{{0x3d, 0x16, 0x35, 0xf3, 0x46, 0xbc, 0xb3, 0xf4, 0xc6, 0xb6, 0x4f, 0xfa, 0xf4, 0xa0, 0x13, 0xe6, 0x57, 0x45, 0x93, 0xb9, 0xbc, 0xd6, 0x59, 0xe7, 0x77, 0x94, 0x6c, 0xab, 0x96, 0x3b, 0x4f, 0x09}}},
@@ -1376,7 +1237,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0xcf, 0xe8, 0x0e, 0x6b, 0x96, 0x7d, 0xed, 0x27, 0xd1, 0x3c, 0xa9, 0xd9, 0x50, 0xa9, 0x98, 0x84, 0x5e, 0x86, 0xef, 0xd6, 0xf0, 0xf8, 0x0e, 0x89, 0x05, 0x2f, 0xd9, 0x5f, 0x15, 0x5f, 0x73, 0x79}}},
{{{0xc8, 0x5c, 0x16, 0xfe, 0xed, 0x9f, 0x26, 0x56, 0xf6, 0x4b, 0x9f, 0xa7, 0x0a, 0x85, 0xfe, 0xa5, 0x8c, 0x87, 0xdd, 0x98, 0xce, 0x4e, 0xc3, 0x58, 0x55, 0xb2, 0x7b, 0x3d, 0xd8, 0x6b, 0xb5, 0x4c}} ,
{{0x65, 0x38, 0xa0, 0x15, 0xfa, 0xa7, 0xb4, 0x8f, 0xeb, 0xc4, 0x86, 0x9b, 0x30, 0xa5, 0x5e, 0x4d, 0xea, 0x8a, 0x9a, 0x9f, 0x1a, 0xd8, 0x5b, 0x53, 0x14, 0x19, 0x25, 0x63, 0xb4, 0x6f, 0x1f, 0x5d}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0xac, 0x8f, 0xbc, 0x1e, 0x7d, 0x8b, 0x5a, 0x0b, 0x8d, 0xaf, 0x76, 0x2e, 0x71, 0xe3, 0x3b, 0x6f, 0x53, 0x2f, 0x3e, 0x90, 0x95, 0xd4, 0x35, 0x14, 0x4f, 0x8c, 0x3c, 0xce, 0x57, 0x1c, 0x76, 0x49}} ,
{{0xa8, 0x50, 0xe1, 0x61, 0x6b, 0x57, 0x35, 0xeb, 0x44, 0x0b, 0x0c, 0x6e, 0xf9, 0x25, 0x80, 0x74, 0xf2, 0x8f, 0x6f, 0x7a, 0x3e, 0x7f, 0x2d, 0xf3, 0x4e, 0x09, 0x65, 0x10, 0x5e, 0x03, 0x25, 0x32}}},
@@ -1386,7 +1247,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x97, 0x67, 0x9e, 0xeb, 0x6a, 0xf9, 0x6e, 0xd6, 0x73, 0xe8, 0x6b, 0x29, 0xec, 0x63, 0x82, 0x00, 0xa8, 0x99, 0x1c, 0x1d, 0x30, 0xc8, 0x90, 0x52, 0x90, 0xb6, 0x6a, 0x80, 0x4e, 0xff, 0x4b, 0x51}}},
{{{0x0f, 0x7d, 0x63, 0x8c, 0x6e, 0x5c, 0xde, 0x30, 0xdf, 0x65, 0xfa, 0x2e, 0xb0, 0xa3, 0x25, 0x05, 0x54, 0xbd, 0x25, 0xba, 0x06, 0xae, 0xdf, 0x8b, 0xd9, 0x1b, 0xea, 0x38, 0xb3, 0x05, 0x16, 0x09}} ,
{{0xc7, 0x8c, 0xbf, 0x64, 0x28, 0xad, 0xf8, 0xa5, 0x5a, 0x6f, 0xc9, 0xba, 0xd5, 0x7f, 0xd5, 0xd6, 0xbd, 0x66, 0x2f, 0x3d, 0xaa, 0x54, 0xf6, 0xba, 0x32, 0x22, 0x9a, 0x1e, 0x52, 0x05, 0xf4, 0x1d}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0xaa, 0x1f, 0xbb, 0xeb, 0xfe, 0xe4, 0x87, 0xfc, 0xb1, 0x2c, 0xb7, 0x88, 0xf4, 0xc6, 0xb9, 0xf5, 0x24, 0x46, 0xf2, 0xa5, 0x9f, 0x8f, 0x8a, 0x93, 0x70, 0x69, 0xd4, 0x56, 0xec, 0xfd, 0x06, 0x46}} ,
{{0x4e, 0x66, 0xcf, 0x4e, 0x34, 0xce, 0x0c, 0xd9, 0xa6, 0x50, 0xd6, 0x5e, 0x95, 0xaf, 0xe9, 0x58, 0xfa, 0xee, 0x9b, 0xb8, 0xa5, 0x0f, 0x35, 0xe0, 0x43, 0x82, 0x6d, 0x65, 0xe6, 0xd9, 0x00, 0x0f}}},
@@ -1396,7 +1257,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x99, 0xeb, 0x7c, 0x31, 0x73, 0x64, 0x67, 0x7f, 0x0c, 0x66, 0xaa, 0x8c, 0x69, 0x91, 0xe2, 0x26, 0xd3, 0x23, 0xe2, 0x76, 0x5d, 0x32, 0x52, 0xdf, 0x5d, 0xc5, 0x8f, 0xb7, 0x7c, 0x84, 0xb3, 0x70}}},
{{{0xeb, 0x01, 0xc7, 0x36, 0x97, 0x4e, 0xb6, 0xab, 0x5f, 0x0d, 0x2c, 0xba, 0x67, 0x64, 0x55, 0xde, 0xbc, 0xff, 0xa6, 0xec, 0x04, 0xd3, 0x8d, 0x39, 0x56, 0x5e, 0xee, 0xf8, 0xe4, 0x2e, 0x33, 0x62}} ,
{{0x65, 0xef, 0xb8, 0x9f, 0xc8, 0x4b, 0xa7, 0xfd, 0x21, 0x49, 0x9b, 0x92, 0x35, 0x82, 0xd6, 0x0a, 0x9b, 0xf2, 0x79, 0xf1, 0x47, 0x2f, 0x6a, 0x7e, 0x9f, 0xcf, 0x18, 0x02, 0x3c, 0xfb, 0x1b, 0x3e}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x2f, 0x8b, 0xc8, 0x40, 0x51, 0xd1, 0xac, 0x1a, 0x0b, 0xe4, 0xa9, 0xa2, 0x42, 0x21, 0x19, 0x2f, 0x7b, 0x97, 0xbf, 0xf7, 0x57, 0x6d, 0x3f, 0x3d, 0x4f, 0x0f, 0xe2, 0xb2, 0x81, 0x00, 0x9e, 0x7b}} ,
{{0x8c, 0x85, 0x2b, 0xc4, 0xfc, 0xf1, 0xab, 0xe8, 0x79, 0x22, 0xc4, 0x84, 0x17, 0x3a, 0xfa, 0x86, 0xa6, 0x7d, 0xf9, 0xf3, 0x6f, 0x03, 0x57, 0x20, 0x4d, 0x79, 0xf9, 0x6e, 0x71, 0x54, 0x38, 0x09}}},
@@ -1406,7 +1267,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x57, 0x82, 0x1c, 0xab, 0xbf, 0x62, 0x70, 0xe8, 0xc4, 0xcf, 0xf0, 0x28, 0x6e, 0x16, 0x3c, 0x08, 0x78, 0x89, 0x85, 0x46, 0x0f, 0xf6, 0x7f, 0xcf, 0xcb, 0x7e, 0xb8, 0x25, 0xe9, 0x5a, 0xfa, 0x03}}},
{{{0xfb, 0x95, 0x92, 0x63, 0x50, 0xfc, 0x62, 0xf0, 0xa4, 0x5e, 0x8c, 0x18, 0xc2, 0x17, 0x24, 0xb7, 0x78, 0xc2, 0xa9, 0xe7, 0x6a, 0x32, 0xd6, 0x29, 0x85, 0xaf, 0xcb, 0x8d, 0x91, 0x13, 0xda, 0x6b}} ,
{{0x36, 0x0a, 0xc2, 0xb6, 0x4b, 0xa5, 0x5d, 0x07, 0x17, 0x41, 0x31, 0x5f, 0x62, 0x46, 0xf8, 0x92, 0xf9, 0x66, 0x48, 0x73, 0xa6, 0x97, 0x0d, 0x7d, 0x88, 0xee, 0x62, 0xb1, 0x03, 0xa8, 0x3f, 0x2c}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x4a, 0xb1, 0x70, 0x8a, 0xa9, 0xe8, 0x63, 0x79, 0x00, 0xe2, 0x25, 0x16, 0xca, 0x4b, 0x0f, 0xa4, 0x66, 0xad, 0x19, 0x9f, 0x88, 0x67, 0x0c, 0x8b, 0xc2, 0x4a, 0x5b, 0x2b, 0x6d, 0x95, 0xaf, 0x19}} ,
{{0x8b, 0x9d, 0xb6, 0xcc, 0x60, 0xb4, 0x72, 0x4f, 0x17, 0x69, 0x5a, 0x4a, 0x68, 0x34, 0xab, 0xa1, 0x45, 0x32, 0x3c, 0x83, 0x87, 0x72, 0x30, 0x54, 0x77, 0x68, 0xae, 0xfb, 0xb5, 0x8b, 0x22, 0x5e}}},
@@ -1416,7 +1277,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x32, 0x65, 0x03, 0xe5, 0x89, 0xa6, 0x6e, 0xb3, 0x5b, 0x8e, 0xca, 0xeb, 0xfe, 0x22, 0x56, 0x8b, 0x5d, 0x14, 0x4b, 0x4d, 0xf9, 0xbe, 0xb5, 0xf5, 0xe6, 0x5c, 0x7b, 0x8b, 0xf4, 0x13, 0x11, 0x34}}},
{{{0x07, 0xc6, 0x22, 0x15, 0xe2, 0x9c, 0x60, 0xa2, 0x19, 0xd9, 0x27, 0xae, 0x37, 0x4e, 0xa6, 0xc9, 0x80, 0xa6, 0x91, 0x8f, 0x12, 0x49, 0xe5, 0x00, 0x18, 0x47, 0xd1, 0xd7, 0x28, 0x22, 0x63, 0x39}} ,
{{0xe8, 0xe2, 0x00, 0x7e, 0xf2, 0x9e, 0x1e, 0x99, 0x39, 0x95, 0x04, 0xbd, 0x1e, 0x67, 0x7b, 0xb2, 0x26, 0xac, 0xe6, 0xaa, 0xe2, 0x46, 0xd5, 0xe4, 0xe8, 0x86, 0xbd, 0xab, 0x7c, 0x55, 0x59, 0x6f}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x24, 0x64, 0x6e, 0x9b, 0x35, 0x71, 0x78, 0xce, 0x33, 0x03, 0x21, 0x33, 0x36, 0xf1, 0x73, 0x9b, 0xb9, 0x15, 0x8b, 0x2c, 0x69, 0xcf, 0x4d, 0xed, 0x4f, 0x4d, 0x57, 0x14, 0x13, 0x82, 0xa4, 0x4d}} ,
{{0x65, 0x6e, 0x0a, 0xa4, 0x59, 0x07, 0x17, 0xf2, 0x6b, 0x4a, 0x1f, 0x6e, 0xf6, 0xb5, 0xbc, 0x62, 0xe4, 0xb6, 0xda, 0xa2, 0x93, 0xbc, 0x29, 0x05, 0xd2, 0xd2, 0x73, 0x46, 0x03, 0x16, 0x40, 0x31}}},
@@ -1426,7 +1287,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x86, 0x17, 0x90, 0xe7, 0xc9, 0x1f, 0x10, 0xa5, 0x6a, 0x2d, 0x39, 0xd0, 0x3b, 0xc4, 0xa6, 0xe9, 0x59, 0x13, 0xda, 0x1a, 0xe6, 0xa0, 0xb9, 0x3c, 0x50, 0xb8, 0x40, 0x7c, 0x15, 0x36, 0x5a, 0x42}}},
{{{0xb4, 0x0b, 0x32, 0xab, 0xdc, 0x04, 0x51, 0x55, 0x21, 0x1e, 0x0b, 0x75, 0x99, 0x89, 0x73, 0x35, 0x3a, 0x91, 0x2b, 0xfe, 0xe7, 0x49, 0xea, 0x76, 0xc1, 0xf9, 0x46, 0xb9, 0x53, 0x02, 0x23, 0x04}} ,
{{0xfc, 0x5a, 0x1e, 0x1d, 0x74, 0x58, 0x95, 0xa6, 0x8f, 0x7b, 0x97, 0x3e, 0x17, 0x3b, 0x79, 0x2d, 0xa6, 0x57, 0xef, 0x45, 0x02, 0x0b, 0x4d, 0x6e, 0x9e, 0x93, 0x8d, 0x2f, 0xd9, 0x9d, 0xdb, 0x04}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0xc0, 0xd7, 0x56, 0x97, 0x58, 0x91, 0xde, 0x09, 0x4f, 0x9f, 0xbe, 0x63, 0xb0, 0x83, 0x86, 0x43, 0x5d, 0xbc, 0xe0, 0xf3, 0xc0, 0x75, 0xbf, 0x8b, 0x8e, 0xaa, 0xf7, 0x8b, 0x64, 0x6e, 0xb0, 0x63}} ,
{{0x16, 0xae, 0x8b, 0xe0, 0x9b, 0x24, 0x68, 0x5c, 0x44, 0xc2, 0xd0, 0x08, 0xb7, 0x7b, 0x62, 0xfd, 0x7f, 0xd8, 0xd4, 0xb7, 0x50, 0xfd, 0x2c, 0x1b, 0xbf, 0x41, 0x95, 0xd9, 0x8e, 0xd8, 0x17, 0x1b}}},
@@ -1436,7 +1297,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x02, 0xfb, 0x54, 0xb8, 0x05, 0xf3, 0x81, 0x52, 0x69, 0x34, 0x46, 0x9d, 0x86, 0x76, 0x8f, 0xd7, 0xf8, 0x6a, 0x66, 0xff, 0xe6, 0xa7, 0x90, 0xf7, 0x5e, 0xcd, 0x6a, 0x9b, 0x55, 0xfc, 0x9d, 0x48}}},
{{{0xbd, 0xaa, 0x13, 0xe6, 0xcd, 0x45, 0x4a, 0xa4, 0x59, 0x0a, 0x64, 0xb1, 0x98, 0xd6, 0x34, 0x13, 0x04, 0xe6, 0x97, 0x94, 0x06, 0xcb, 0xd4, 0x4e, 0xbb, 0x96, 0xcd, 0xd1, 0x57, 0xd1, 0xe3, 0x06}} ,
{{0x7a, 0x6c, 0x45, 0x27, 0xc4, 0x93, 0x7f, 0x7d, 0x7c, 0x62, 0x50, 0x38, 0x3a, 0x6b, 0xb5, 0x88, 0xc6, 0xd9, 0xf1, 0x78, 0x19, 0xb9, 0x39, 0x93, 0x3d, 0xc9, 0xe0, 0x9c, 0x3c, 0xce, 0xf5, 0x72}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x24, 0xea, 0x23, 0x7d, 0x56, 0x2c, 0xe2, 0x59, 0x0e, 0x85, 0x60, 0x04, 0x88, 0x5a, 0x74, 0x1e, 0x4b, 0xef, 0x13, 0xda, 0x4c, 0xff, 0x83, 0x45, 0x85, 0x3f, 0x08, 0x95, 0x2c, 0x20, 0x13, 0x1f}} ,
{{0x48, 0x5f, 0x27, 0x90, 0x5c, 0x02, 0x42, 0xad, 0x78, 0x47, 0x5c, 0xb5, 0x7e, 0x08, 0x85, 0x00, 0xfa, 0x7f, 0xfd, 0xfd, 0xe7, 0x09, 0x11, 0xf2, 0x7e, 0x1b, 0x38, 0x6c, 0x35, 0x6d, 0x33, 0x66}}},
@@ -1446,7 +1307,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x4e, 0x3b, 0x69, 0xc8, 0x43, 0x75, 0x51, 0x6c, 0x79, 0x56, 0xe4, 0xcb, 0xf7, 0xa6, 0x51, 0xc2, 0x2c, 0x42, 0x0b, 0xd4, 0x82, 0x20, 0x1c, 0x01, 0x08, 0x66, 0xd7, 0xbf, 0x04, 0x56, 0xfc, 0x02}}},
{{{0x24, 0xe8, 0xb7, 0x60, 0xae, 0x47, 0x80, 0xfc, 0xe5, 0x23, 0xe7, 0xc2, 0xc9, 0x85, 0xe6, 0x98, 0xa0, 0x29, 0x4e, 0xe1, 0x84, 0x39, 0x2d, 0x95, 0x2c, 0xf3, 0x45, 0x3c, 0xff, 0xaf, 0x27, 0x4c}} ,
{{0x6b, 0xa6, 0xf5, 0x4b, 0x11, 0xbd, 0xba, 0x5b, 0x9e, 0xc4, 0xa4, 0x51, 0x1e, 0xbe, 0xd0, 0x90, 0x3a, 0x9c, 0xc2, 0x26, 0xb6, 0x1e, 0xf1, 0x95, 0x7d, 0xc8, 0x6d, 0x52, 0xe6, 0x99, 0x2c, 0x5f}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x85, 0xe0, 0x24, 0x32, 0xb4, 0xd1, 0xef, 0xfc, 0x69, 0xa2, 0xbf, 0x8f, 0x72, 0x2c, 0x95, 0xf6, 0xe4, 0x6e, 0x7d, 0x90, 0xf7, 0x57, 0x81, 0xa0, 0xf7, 0xda, 0xef, 0x33, 0x07, 0xe3, 0x6b, 0x78}} ,
{{0x36, 0x27, 0x3e, 0xc6, 0x12, 0x07, 0xab, 0x4e, 0xbe, 0x69, 0x9d, 0xb3, 0xbe, 0x08, 0x7c, 0x2a, 0x47, 0x08, 0xfd, 0xd4, 0xcd, 0x0e, 0x27, 0x34, 0x5b, 0x98, 0x34, 0x2f, 0x77, 0x5f, 0x3a, 0x65}}},
@@ -1456,7 +1317,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x77, 0x5b, 0xe2, 0x37, 0xc7, 0xe1, 0x7c, 0x13, 0x8c, 0x9f, 0x7b, 0x7b, 0x2a, 0xce, 0x42, 0xa3, 0xb9, 0x2a, 0x99, 0xa8, 0xc0, 0xd8, 0x3c, 0x86, 0xb0, 0xfb, 0xe9, 0x76, 0x77, 0xf7, 0xf5, 0x56}}},
{{{0xdf, 0xb3, 0x46, 0x11, 0x6e, 0x13, 0xb7, 0x28, 0x4e, 0x56, 0xdd, 0xf1, 0xac, 0xad, 0x58, 0xc3, 0xf8, 0x88, 0x94, 0x5e, 0x06, 0x98, 0xa1, 0xe4, 0x6a, 0xfb, 0x0a, 0x49, 0x5d, 0x8a, 0xfe, 0x77}} ,
{{0x46, 0x02, 0xf5, 0xa5, 0xaf, 0xc5, 0x75, 0x6d, 0xba, 0x45, 0x35, 0x0a, 0xfe, 0xc9, 0xac, 0x22, 0x91, 0x8d, 0x21, 0x95, 0x33, 0x03, 0xc0, 0x8a, 0x16, 0xf3, 0x39, 0xe0, 0x01, 0x0f, 0x53, 0x3c}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x34, 0x75, 0x37, 0x1f, 0x34, 0x4e, 0xa9, 0x1d, 0x68, 0x67, 0xf8, 0x49, 0x98, 0x96, 0xfc, 0x4c, 0x65, 0x97, 0xf7, 0x02, 0x4a, 0x52, 0x6c, 0x01, 0xbd, 0x48, 0xbb, 0x1b, 0xed, 0xa4, 0xe2, 0x53}} ,
{{0x59, 0xd5, 0x9b, 0x5a, 0xa2, 0x90, 0xd3, 0xb8, 0x37, 0x4c, 0x55, 0x82, 0x28, 0x08, 0x0f, 0x7f, 0xaa, 0x81, 0x65, 0xe0, 0x0c, 0x52, 0xc9, 0xa3, 0x32, 0x27, 0x64, 0xda, 0xfd, 0x34, 0x23, 0x5a}}},
@@ -1466,7 +1327,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x1d, 0x29, 0x7a, 0xa1, 0xec, 0x8e, 0xb5, 0xad, 0xea, 0x02, 0x68, 0x60, 0x74, 0x29, 0x1c, 0xa5, 0xcf, 0xc8, 0x3b, 0x7d, 0x8b, 0x2b, 0x7c, 0xad, 0xa4, 0x40, 0x17, 0x51, 0x59, 0x7c, 0x2e, 0x5d}}},
{{{0x0a, 0x6c, 0x4f, 0xbc, 0x3e, 0x32, 0xe7, 0x4a, 0x1a, 0x13, 0xc1, 0x49, 0x38, 0xbf, 0xf7, 0xc2, 0xd3, 0x8f, 0x6b, 0xad, 0x52, 0xf7, 0xcf, 0xbc, 0x27, 0xcb, 0x40, 0x67, 0x76, 0xcd, 0x6d, 0x56}} ,
{{0xe5, 0xb0, 0x27, 0xad, 0xbe, 0x9b, 0xf2, 0xb5, 0x63, 0xde, 0x3a, 0x23, 0x95, 0xb7, 0x0a, 0x7e, 0xf3, 0x9e, 0x45, 0x6f, 0x19, 0x39, 0x75, 0x8f, 0x39, 0x3d, 0x0f, 0xc0, 0x9f, 0xf1, 0xe9, 0x51}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x88, 0xaa, 0x14, 0x24, 0x86, 0x94, 0x11, 0x12, 0x3e, 0x1a, 0xb5, 0xcc, 0xbb, 0xe0, 0x9c, 0xd5, 0x9c, 0x6d, 0xba, 0x58, 0x72, 0x8d, 0xfb, 0x22, 0x7b, 0x9f, 0x7c, 0x94, 0x30, 0xb3, 0x51, 0x21}} ,
{{0xf6, 0x74, 0x3d, 0xf2, 0xaf, 0xd0, 0x1e, 0x03, 0x7c, 0x23, 0x6b, 0xc9, 0xfc, 0x25, 0x70, 0x90, 0xdc, 0x9a, 0xa4, 0xfb, 0x49, 0xfc, 0x3d, 0x0a, 0x35, 0x38, 0x6f, 0xe4, 0x7e, 0x50, 0x01, 0x2a}}},
@@ -1476,7 +1337,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x84, 0x7c, 0xc2, 0xa6, 0x91, 0x23, 0xce, 0xbd, 0xdc, 0xf9, 0xce, 0xd5, 0x75, 0x30, 0x22, 0xe6, 0xf9, 0x43, 0x62, 0x0d, 0xf7, 0x75, 0x9d, 0x7f, 0x8c, 0xff, 0x7d, 0xe4, 0x72, 0xac, 0x9f, 0x1c}}},
{{{0x88, 0xc1, 0x99, 0xd0, 0x3c, 0x1c, 0x5d, 0xb4, 0xef, 0x13, 0x0f, 0x90, 0xb9, 0x36, 0x2f, 0x95, 0x95, 0xc6, 0xdc, 0xde, 0x0a, 0x51, 0xe2, 0x8d, 0xf3, 0xbc, 0x51, 0xec, 0xdf, 0xb1, 0xa2, 0x5f}} ,
{{0x2e, 0x68, 0xa1, 0x23, 0x7d, 0x9b, 0x40, 0x69, 0x85, 0x7b, 0x42, 0xbf, 0x90, 0x4b, 0xd6, 0x40, 0x2f, 0xd7, 0x52, 0x52, 0xb2, 0x21, 0xde, 0x64, 0xbd, 0x88, 0xc3, 0x6d, 0xa5, 0xfa, 0x81, 0x3f}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0xfb, 0xfd, 0x47, 0x7b, 0x8a, 0x66, 0x9e, 0x79, 0x2e, 0x64, 0x82, 0xef, 0xf7, 0x21, 0xec, 0xf6, 0xd8, 0x86, 0x09, 0x31, 0x7c, 0xdd, 0x03, 0x6a, 0x58, 0xa0, 0x77, 0xb7, 0x9b, 0x8c, 0x87, 0x1f}} ,
{{0x55, 0x47, 0xe4, 0xa8, 0x3d, 0x55, 0x21, 0x34, 0xab, 0x1d, 0xae, 0xe0, 0xf4, 0xea, 0xdb, 0xc5, 0xb9, 0x58, 0xbf, 0xc4, 0x2a, 0x89, 0x31, 0x1a, 0xf4, 0x2d, 0xe1, 0xca, 0x37, 0x99, 0x47, 0x59}}},
@@ -1486,7 +1347,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x29, 0xac, 0xff, 0x27, 0xe0, 0x59, 0xbe, 0x39, 0x9c, 0x0d, 0x83, 0xd7, 0x10, 0x0b, 0x15, 0xb7, 0xe1, 0xc2, 0x2c, 0x30, 0x73, 0x80, 0x3a, 0x7d, 0x5d, 0xab, 0x58, 0x6b, 0xc1, 0xf0, 0xf4, 0x22}}},
{{{0xfe, 0x7f, 0xfb, 0x35, 0x7d, 0xc6, 0x01, 0x23, 0x28, 0xc4, 0x02, 0xac, 0x1f, 0x42, 0xb4, 0x9d, 0xfc, 0x00, 0x94, 0xa5, 0xee, 0xca, 0xda, 0x97, 0x09, 0x41, 0x77, 0x87, 0x5d, 0x7b, 0x87, 0x78}} ,
{{0xf5, 0xfb, 0x90, 0x2d, 0x81, 0x19, 0x9e, 0x2f, 0x6d, 0x85, 0x88, 0x8c, 0x40, 0x5c, 0x77, 0x41, 0x4d, 0x01, 0x19, 0x76, 0x60, 0xe8, 0x4c, 0x48, 0xe4, 0x33, 0x83, 0x32, 0x6c, 0xb4, 0x41, 0x03}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0xff, 0x10, 0xc2, 0x09, 0x4f, 0x6e, 0xf4, 0xd2, 0xdf, 0x7e, 0xca, 0x7b, 0x1c, 0x1d, 0xba, 0xa3, 0xb6, 0xda, 0x67, 0x33, 0xd4, 0x87, 0x36, 0x4b, 0x11, 0x20, 0x05, 0xa6, 0x29, 0xc1, 0x87, 0x17}} ,
{{0xf6, 0x96, 0xca, 0x2f, 0xda, 0x38, 0xa7, 0x1b, 0xfc, 0xca, 0x7d, 0xfe, 0x08, 0x89, 0xe2, 0x47, 0x2b, 0x6a, 0x5d, 0x4b, 0xfa, 0xa1, 0xb4, 0xde, 0xb6, 0xc2, 0x31, 0x51, 0xf5, 0xe0, 0xa4, 0x0b}}},
@@ -1496,7 +1357,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x0c, 0x0e, 0xf3, 0xba, 0xf0, 0xe5, 0xba, 0xb2, 0x57, 0x77, 0xc6, 0x20, 0x9b, 0x89, 0x24, 0xbe, 0xf2, 0x9c, 0x8a, 0xba, 0x69, 0xc1, 0xf1, 0xb0, 0x4f, 0x2a, 0x05, 0x9a, 0xee, 0x10, 0x7e, 0x36}}},
{{{0x3f, 0x26, 0xe9, 0x40, 0xe9, 0x03, 0xad, 0x06, 0x69, 0x91, 0xe0, 0xd1, 0x89, 0x60, 0x84, 0x79, 0xde, 0x27, 0x6d, 0xe6, 0x76, 0xbd, 0xea, 0xe6, 0xae, 0x48, 0xc3, 0x67, 0xc0, 0x57, 0xcd, 0x2f}} ,
{{0x7f, 0xc1, 0xdc, 0xb9, 0xc7, 0xbc, 0x86, 0x3d, 0x55, 0x4b, 0x28, 0x7a, 0xfb, 0x4d, 0xc7, 0xf8, 0xbc, 0x67, 0x2a, 0x60, 0x4d, 0x8f, 0x07, 0x0b, 0x1a, 0x17, 0xbf, 0xfa, 0xac, 0xa7, 0x3d, 0x1a}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x91, 0x3f, 0xed, 0x5e, 0x18, 0x78, 0x3f, 0x23, 0x2c, 0x0d, 0x8c, 0x44, 0x00, 0xe8, 0xfb, 0xe9, 0x8e, 0xd6, 0xd1, 0x36, 0x58, 0x57, 0x9e, 0xae, 0x4b, 0x5c, 0x0b, 0x07, 0xbc, 0x6b, 0x55, 0x2b}} ,
{{0x6f, 0x4d, 0x17, 0xd7, 0xe1, 0x84, 0xd9, 0x78, 0xb1, 0x90, 0xfd, 0x2e, 0xb3, 0xb5, 0x19, 0x3f, 0x1b, 0xfa, 0xc0, 0x68, 0xb3, 0xdd, 0x00, 0x2e, 0x89, 0xbd, 0x7e, 0x80, 0x32, 0x13, 0xa0, 0x7b}}},
@@ -1506,7 +1367,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x01, 0xb8, 0xd6, 0x16, 0x67, 0xa0, 0x15, 0xb9, 0xe1, 0x58, 0xa4, 0xa7, 0x31, 0x37, 0x77, 0x2f, 0x8b, 0x12, 0x9f, 0xf4, 0x3f, 0xc7, 0x36, 0x66, 0xd2, 0xa8, 0x56, 0xf7, 0x7f, 0x74, 0xc6, 0x41}}},
{{{0x5d, 0xf8, 0xb4, 0xa8, 0x30, 0xdd, 0xcc, 0x38, 0xa5, 0xd3, 0xca, 0xd8, 0xd1, 0xf8, 0xb2, 0x31, 0x91, 0xd4, 0x72, 0x05, 0x57, 0x4a, 0x3b, 0x82, 0x4a, 0xc6, 0x68, 0x20, 0xe2, 0x18, 0x41, 0x61}} ,
{{0x19, 0xd4, 0x8d, 0x47, 0x29, 0x12, 0x65, 0xb0, 0x11, 0x78, 0x47, 0xb5, 0xcb, 0xa3, 0xa5, 0xfa, 0x05, 0x85, 0x54, 0xa9, 0x33, 0x97, 0x8d, 0x2b, 0xc2, 0xfe, 0x99, 0x35, 0x28, 0xe5, 0xeb, 0x63}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0xb1, 0x3f, 0x3f, 0xef, 0xd8, 0xf4, 0xfc, 0xb3, 0xa0, 0x60, 0x50, 0x06, 0x2b, 0x29, 0x52, 0x70, 0x15, 0x0b, 0x24, 0x24, 0xf8, 0x5f, 0x79, 0x18, 0xcc, 0xff, 0x89, 0x99, 0x84, 0xa1, 0xae, 0x13}} ,
{{0x44, 0x1f, 0xb8, 0xc2, 0x01, 0xc1, 0x30, 0x19, 0x55, 0x05, 0x60, 0x10, 0xa4, 0x6c, 0x2d, 0x67, 0x70, 0xe5, 0x25, 0x1b, 0xf2, 0xbf, 0xdd, 0xfb, 0x70, 0x2b, 0xa1, 0x8c, 0x9c, 0x94, 0x84, 0x08}}},
@@ -1516,7 +1377,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0xdc, 0xff, 0x15, 0x61, 0x2f, 0x4a, 0x2f, 0x62, 0xf2, 0x04, 0x2f, 0xb5, 0x0c, 0xb7, 0x1e, 0x3f, 0x74, 0x1a, 0x0f, 0xd7, 0xea, 0xcd, 0xd9, 0x7d, 0xf6, 0x12, 0x0e, 0x2f, 0xdb, 0x5a, 0x3b, 0x16}}},
{{{0x1b, 0x37, 0x47, 0xe3, 0xf5, 0x9e, 0xea, 0x2c, 0x2a, 0xe7, 0x82, 0x36, 0xf4, 0x1f, 0x81, 0x47, 0x92, 0x4b, 0x69, 0x0e, 0x11, 0x8c, 0x5d, 0x53, 0x5b, 0x81, 0x27, 0x08, 0xbc, 0xa0, 0xae, 0x25}} ,
{{0x69, 0x32, 0xa1, 0x05, 0x11, 0x42, 0x00, 0xd2, 0x59, 0xac, 0x4d, 0x62, 0x8b, 0x13, 0xe2, 0x50, 0x5d, 0xa0, 0x9d, 0x9b, 0xfd, 0xbb, 0x12, 0x41, 0x75, 0x41, 0x9e, 0xcc, 0xdc, 0xc7, 0xdc, 0x5d}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0xd9, 0xe3, 0x38, 0x06, 0x46, 0x70, 0x82, 0x5e, 0x28, 0x49, 0x79, 0xff, 0x25, 0xd2, 0x4e, 0x29, 0x8d, 0x06, 0xb0, 0x23, 0xae, 0x9b, 0x66, 0xe4, 0x7d, 0xc0, 0x70, 0x91, 0xa3, 0xfc, 0xec, 0x4e}} ,
{{0x62, 0x12, 0x37, 0x6a, 0x30, 0xf6, 0x1e, 0xfb, 0x14, 0x5c, 0x0d, 0x0e, 0xb7, 0x81, 0x6a, 0xe7, 0x08, 0x05, 0xac, 0xaa, 0x38, 0x46, 0xe2, 0x73, 0xea, 0x4b, 0x07, 0x81, 0x43, 0x7c, 0x9e, 0x5e}}},
@@ -1526,7 +1387,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0xe5, 0x44, 0xec, 0x06, 0x9d, 0x90, 0x79, 0x9f, 0xd3, 0xe0, 0x79, 0xaf, 0x8f, 0x10, 0xfd, 0xdd, 0x04, 0xae, 0x27, 0x97, 0x46, 0x33, 0x79, 0xea, 0xb8, 0x4e, 0xca, 0x5a, 0x59, 0x57, 0xe1, 0x0e}}},
{{{0x1a, 0xda, 0xf3, 0xa5, 0x41, 0x43, 0x28, 0xfc, 0x7e, 0xe7, 0x71, 0xea, 0xc6, 0x3b, 0x59, 0xcc, 0x2e, 0xd3, 0x40, 0xec, 0xb3, 0x13, 0x6f, 0x44, 0xcd, 0x13, 0xb2, 0x37, 0xf2, 0x6e, 0xd9, 0x1c}} ,
{{0xe3, 0xdb, 0x60, 0xcd, 0x5c, 0x4a, 0x18, 0x0f, 0xef, 0x73, 0x36, 0x71, 0x8c, 0xf6, 0x11, 0xb4, 0xd8, 0xce, 0x17, 0x5e, 0x4f, 0x26, 0x77, 0x97, 0x5f, 0xcb, 0xef, 0x91, 0xeb, 0x6a, 0x62, 0x7a}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x18, 0x4a, 0xa2, 0x97, 0x08, 0x81, 0x2d, 0x83, 0xc4, 0xcc, 0xf0, 0x83, 0x7e, 0xec, 0x0d, 0x95, 0x4c, 0x5b, 0xfb, 0xfa, 0x98, 0x80, 0x4a, 0x66, 0x56, 0x0c, 0x51, 0xb3, 0xf2, 0x04, 0x5d, 0x27}} ,
{{0x3b, 0xb9, 0xb8, 0x06, 0x5a, 0x2e, 0xfe, 0xc3, 0x82, 0x37, 0x9c, 0xa3, 0x11, 0x1f, 0x9c, 0xa6, 0xda, 0x63, 0x48, 0x9b, 0xad, 0xde, 0x2d, 0xa6, 0xbc, 0x6e, 0x32, 0xda, 0x27, 0x65, 0xdd, 0x57}}},
@@ -1536,7 +1397,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0xa4, 0x4b, 0x62, 0x4c, 0xe6, 0xfd, 0x72, 0x07, 0xf2, 0x81, 0xfc, 0xf2, 0xbd, 0x12, 0x7c, 0x68, 0x76, 0x2a, 0xba, 0xf5, 0x65, 0xb1, 0x1f, 0x17, 0x0a, 0x38, 0xb0, 0xbf, 0xc0, 0xf8, 0xf4, 0x2a}}},
{{{0x55, 0x60, 0x55, 0x5b, 0xe4, 0x1d, 0x71, 0x4c, 0x9d, 0x5b, 0x9f, 0x70, 0xa6, 0x85, 0x9a, 0x2c, 0xa0, 0xe2, 0x32, 0x48, 0xce, 0x9e, 0x2a, 0xa5, 0x07, 0x3b, 0xc7, 0x6c, 0x86, 0x77, 0xde, 0x3c}} ,
{{0xf7, 0x18, 0x7a, 0x96, 0x7e, 0x43, 0x57, 0xa9, 0x55, 0xfc, 0x4e, 0xb6, 0x72, 0x00, 0xf2, 0xe4, 0xd7, 0x52, 0xd3, 0xd3, 0xb6, 0x85, 0xf6, 0x71, 0xc7, 0x44, 0x3f, 0x7f, 0xd7, 0xb3, 0xf2, 0x79}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x46, 0xca, 0xa7, 0x55, 0x7b, 0x79, 0xf3, 0xca, 0x5a, 0x65, 0xf6, 0xed, 0x50, 0x14, 0x7b, 0xe4, 0xc4, 0x2a, 0x65, 0x9e, 0xe2, 0xf9, 0xca, 0xa7, 0x22, 0x26, 0x53, 0xcb, 0x21, 0x5b, 0xa7, 0x31}} ,
{{0x90, 0xd7, 0xc5, 0x26, 0x08, 0xbd, 0xb0, 0x53, 0x63, 0x58, 0xc3, 0x31, 0x5e, 0x75, 0x46, 0x15, 0x91, 0xa6, 0xf8, 0x2f, 0x1a, 0x08, 0x65, 0x88, 0x2f, 0x98, 0x04, 0xf1, 0x7c, 0x6e, 0x00, 0x77}}},
@@ -1546,7 +1407,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x68, 0x0e, 0x6f, 0x97, 0xba, 0x70, 0xbb, 0xa3, 0x0e, 0xe5, 0x0b, 0x12, 0xf4, 0xa2, 0xdc, 0x47, 0xf8, 0xe6, 0xd0, 0x23, 0x6c, 0x33, 0xa8, 0x99, 0x46, 0x6e, 0x0f, 0x44, 0xba, 0x76, 0x48, 0x0f}}},
{{{0xa3, 0x2a, 0x61, 0x37, 0xe2, 0x59, 0x12, 0x0e, 0x27, 0xba, 0x64, 0x43, 0xae, 0xc0, 0x42, 0x69, 0x79, 0xa4, 0x1e, 0x29, 0x8b, 0x15, 0xeb, 0xf8, 0xaf, 0xd4, 0xa2, 0x68, 0x33, 0xb5, 0x7a, 0x24}} ,
{{0x2c, 0x19, 0x33, 0xdd, 0x1b, 0xab, 0xec, 0x01, 0xb0, 0x23, 0xf8, 0x42, 0x2b, 0x06, 0x88, 0xea, 0x3d, 0x2d, 0x00, 0x2a, 0x78, 0x45, 0x4d, 0x38, 0xed, 0x2e, 0x2e, 0x44, 0x49, 0xed, 0xcb, 0x33}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0xa0, 0x68, 0xe8, 0x41, 0x8f, 0x91, 0xf8, 0x11, 0x13, 0x90, 0x2e, 0xa7, 0xab, 0x30, 0xef, 0xad, 0xa0, 0x61, 0x00, 0x88, 0xef, 0xdb, 0xce, 0x5b, 0x5c, 0xbb, 0x62, 0xc8, 0x56, 0xf9, 0x00, 0x73}} ,
{{0x3f, 0x60, 0xc1, 0x82, 0x2d, 0xa3, 0x28, 0x58, 0x24, 0x9e, 0x9f, 0xe3, 0x70, 0xcc, 0x09, 0x4e, 0x1a, 0x3f, 0x11, 0x11, 0x15, 0x07, 0x3c, 0xa4, 0x41, 0xe0, 0x65, 0xa3, 0x0a, 0x41, 0x6d, 0x11}}},
@@ -1556,7 +1417,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x32, 0xb4, 0x18, 0x47, 0x79, 0xcb, 0xd4, 0x5a, 0x07, 0x14, 0x0f, 0xa0, 0xd5, 0xac, 0xd0, 0x41, 0x40, 0xab, 0x61, 0x23, 0xe5, 0x2a, 0x2a, 0x6f, 0xf7, 0xa8, 0xd4, 0x76, 0xef, 0xe7, 0x45, 0x6c}}},
{{{0xa1, 0x5e, 0x60, 0x4f, 0xfb, 0xe1, 0x70, 0x6a, 0x1f, 0x55, 0x4f, 0x09, 0xb4, 0x95, 0x33, 0x36, 0xc6, 0x81, 0x01, 0x18, 0x06, 0x25, 0x27, 0xa4, 0xb4, 0x24, 0xa4, 0x86, 0x03, 0x4c, 0xac, 0x02}} ,
{{0x77, 0x38, 0xde, 0xd7, 0x60, 0x48, 0x07, 0xf0, 0x74, 0xa8, 0xff, 0x54, 0xe5, 0x30, 0x43, 0xff, 0x77, 0xfb, 0x21, 0x07, 0xff, 0xb2, 0x07, 0x6b, 0xe4, 0xe5, 0x30, 0xfc, 0x19, 0x6c, 0xa3, 0x01}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x13, 0xc5, 0x2c, 0xac, 0xd3, 0x83, 0x82, 0x7c, 0x29, 0xf7, 0x05, 0xa5, 0x00, 0xb6, 0x1f, 0x86, 0x55, 0xf4, 0xd6, 0x2f, 0x0c, 0x99, 0xd0, 0x65, 0x9b, 0x6b, 0x46, 0x0d, 0x43, 0xf8, 0x16, 0x28}} ,
{{0x1e, 0x7f, 0xb4, 0x74, 0x7e, 0xb1, 0x89, 0x4f, 0x18, 0x5a, 0xab, 0x64, 0x06, 0xdf, 0x45, 0x87, 0xe0, 0x6a, 0xc6, 0xf0, 0x0e, 0xc9, 0x24, 0x35, 0x38, 0xea, 0x30, 0x54, 0xb4, 0xc4, 0x52, 0x54}}},
@@ -1566,7 +1427,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x32, 0xad, 0xae, 0x85, 0x58, 0x43, 0xb8, 0xb1, 0xe6, 0x3e, 0x00, 0x9c, 0x78, 0x88, 0x56, 0xdb, 0x9c, 0xfc, 0x79, 0xf6, 0xf9, 0x41, 0x5f, 0xb7, 0xbc, 0x11, 0xf9, 0x20, 0x36, 0x1c, 0x53, 0x2b}}},
{{{0x5a, 0x20, 0x5b, 0xa1, 0xa5, 0x44, 0x91, 0x24, 0x02, 0x63, 0x12, 0x64, 0xb8, 0x55, 0xf6, 0xde, 0x2c, 0xdb, 0x47, 0xb8, 0xc6, 0x0a, 0xc3, 0x00, 0x78, 0x93, 0xd8, 0xf5, 0xf5, 0x18, 0x28, 0x0a}} ,
{{0xd6, 0x1b, 0x9a, 0x6c, 0xe5, 0x46, 0xea, 0x70, 0x96, 0x8d, 0x4e, 0x2a, 0x52, 0x21, 0x26, 0x4b, 0xb1, 0xbb, 0x0f, 0x7c, 0xa9, 0x9b, 0x04, 0xbb, 0x51, 0x08, 0xf1, 0x9a, 0xa4, 0x76, 0x7c, 0x18}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0xfa, 0x94, 0xf7, 0x40, 0xd0, 0xd7, 0xeb, 0xa9, 0x82, 0x36, 0xd5, 0x15, 0xb9, 0x33, 0x7a, 0xbf, 0x8a, 0xf2, 0x63, 0xaa, 0x37, 0xf5, 0x59, 0xac, 0xbd, 0xbb, 0x32, 0x36, 0xbe, 0x73, 0x99, 0x38}} ,
{{0x2c, 0xb3, 0xda, 0x7a, 0xd8, 0x3d, 0x99, 0xca, 0xd2, 0xf4, 0xda, 0x99, 0x8e, 0x4f, 0x98, 0xb7, 0xf4, 0xae, 0x3e, 0x9f, 0x8e, 0x35, 0x60, 0xa4, 0x33, 0x75, 0xa4, 0x04, 0x93, 0xb1, 0x6b, 0x4d}}},
@@ -1576,7 +1437,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x53, 0xcf, 0xc2, 0xa1, 0xad, 0x6a, 0xf3, 0xcd, 0x8f, 0xc9, 0xde, 0x1c, 0xf8, 0x6c, 0x8f, 0xf8, 0x76, 0x42, 0xe7, 0xfe, 0xb2, 0x72, 0x21, 0x0a, 0x66, 0x74, 0x8f, 0xb7, 0xeb, 0xe4, 0x6f, 0x01}}},
{{{0x22, 0x8c, 0x6b, 0xbe, 0xfc, 0x4d, 0x70, 0x62, 0x6e, 0x52, 0x77, 0x99, 0x88, 0x7e, 0x7b, 0x57, 0x7a, 0x0d, 0xfe, 0xdc, 0x72, 0x92, 0xf1, 0x68, 0x1d, 0x97, 0xd7, 0x7c, 0x8d, 0x53, 0x10, 0x37}} ,
{{0x53, 0x88, 0x77, 0x02, 0xca, 0x27, 0xa8, 0xe5, 0x45, 0xe2, 0xa8, 0x48, 0x2a, 0xab, 0x18, 0xca, 0xea, 0x2d, 0x2a, 0x54, 0x17, 0x37, 0x32, 0x09, 0xdc, 0xe0, 0x4a, 0xb7, 0x7d, 0x82, 0x10, 0x7d}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x8a, 0x64, 0x1e, 0x14, 0x0a, 0x57, 0xd4, 0xda, 0x5c, 0x96, 0x9b, 0x01, 0x4c, 0x67, 0xbf, 0x8b, 0x30, 0xfe, 0x08, 0xdb, 0x0d, 0xd5, 0xa8, 0xd7, 0x09, 0x11, 0x85, 0xa2, 0xd3, 0x45, 0xfb, 0x7e}} ,
{{0xda, 0x8c, 0xc2, 0xd0, 0xac, 0x18, 0xe8, 0x52, 0x36, 0xd4, 0x21, 0xa3, 0xdd, 0x57, 0x22, 0x79, 0xb7, 0xf8, 0x71, 0x9d, 0xc6, 0x91, 0x70, 0x86, 0x56, 0xbf, 0xa1, 0x11, 0x8b, 0x19, 0xe1, 0x0f}}},
@@ -1586,7 +1447,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x62, 0x4f, 0xd0, 0x75, 0x77, 0xba, 0x76, 0x77, 0xd7, 0xb8, 0xd8, 0x92, 0x6f, 0x98, 0x34, 0x3d, 0xd6, 0x4e, 0x1c, 0x0f, 0xf0, 0x8f, 0x2e, 0xf1, 0xb3, 0xbd, 0xb1, 0xb9, 0xec, 0x99, 0xb4, 0x07}}},
{{{0x60, 0x57, 0x2e, 0x9a, 0x72, 0x1d, 0x6b, 0x6e, 0x58, 0x33, 0x24, 0x8c, 0x48, 0x39, 0x46, 0x8e, 0x89, 0x6a, 0x88, 0x51, 0x23, 0x62, 0xb5, 0x32, 0x09, 0x36, 0xe3, 0x57, 0xf5, 0x98, 0xde, 0x6f}} ,
{{0x8b, 0x2c, 0x00, 0x48, 0x4a, 0xf9, 0x5b, 0x87, 0x69, 0x52, 0xe5, 0x5b, 0xd1, 0xb1, 0xe5, 0x25, 0x25, 0xe0, 0x9c, 0xc2, 0x13, 0x44, 0xe8, 0xb9, 0x0a, 0x70, 0xad, 0xbd, 0x0f, 0x51, 0x94, 0x69}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0xa2, 0xdc, 0xab, 0xa9, 0x25, 0x2d, 0xac, 0x5f, 0x03, 0x33, 0x08, 0xe7, 0x7e, 0xfe, 0x95, 0x36, 0x3c, 0x5b, 0x3a, 0xd3, 0x05, 0x82, 0x1c, 0x95, 0x2d, 0xd8, 0x77, 0x7e, 0x02, 0xd9, 0x5b, 0x70}} ,
{{0xc2, 0xfe, 0x1b, 0x0c, 0x67, 0xcd, 0xd6, 0xe0, 0x51, 0x8e, 0x2c, 0xe0, 0x79, 0x88, 0xf0, 0xcf, 0x41, 0x4a, 0xad, 0x23, 0xd4, 0x46, 0xca, 0x94, 0xa1, 0xc3, 0xeb, 0x28, 0x06, 0xfa, 0x17, 0x14}}},
@@ -1596,7 +1457,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x64, 0xe9, 0x28, 0xc7, 0xa4, 0xcf, 0x2a, 0xf9, 0x90, 0x64, 0x72, 0x2c, 0x8b, 0xeb, 0xec, 0xa0, 0xf2, 0x7d, 0x35, 0xb5, 0x90, 0x4d, 0x7f, 0x5b, 0x4a, 0x49, 0xe4, 0xb8, 0x3b, 0xc8, 0xa1, 0x2f}}},
{{{0x8b, 0xc5, 0xcc, 0x3d, 0x69, 0xa6, 0xa1, 0x18, 0x44, 0xbc, 0x4d, 0x77, 0x37, 0xc7, 0x86, 0xec, 0x0c, 0xc9, 0xd6, 0x44, 0xa9, 0x23, 0x27, 0xb9, 0x03, 0x34, 0xa7, 0x0a, 0xd5, 0xc7, 0x34, 0x37}} ,
{{0xf9, 0x7e, 0x3e, 0x66, 0xee, 0xf9, 0x99, 0x28, 0xff, 0xad, 0x11, 0xd8, 0xe2, 0x66, 0xc5, 0xcd, 0x0f, 0x0d, 0x0b, 0x6a, 0xfc, 0x7c, 0x24, 0xa8, 0x4f, 0xa8, 0x5e, 0x80, 0x45, 0x8b, 0x6c, 0x41}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0xef, 0x1e, 0xec, 0xf7, 0x8d, 0x77, 0xf2, 0xea, 0xdb, 0x60, 0x03, 0x21, 0xc0, 0xff, 0x5e, 0x67, 0xc3, 0x71, 0x0b, 0x21, 0xb4, 0x41, 0xa0, 0x68, 0x38, 0xc6, 0x01, 0xa3, 0xd3, 0x51, 0x3c, 0x3c}} ,
{{0x92, 0xf8, 0xd6, 0x4b, 0xef, 0x42, 0x13, 0xb2, 0x4a, 0xc4, 0x2e, 0x72, 0x3f, 0xc9, 0x11, 0xbd, 0x74, 0x02, 0x0e, 0xf5, 0x13, 0x9d, 0x83, 0x1a, 0x1b, 0xd5, 0x54, 0xde, 0xc4, 0x1e, 0x16, 0x6c}}},
@@ -1606,7 +1467,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x7a, 0x8d, 0xa1, 0x5d, 0x70, 0x5d, 0x51, 0x27, 0xee, 0x30, 0x65, 0x56, 0x95, 0x46, 0xde, 0xbd, 0x03, 0x75, 0xb4, 0x57, 0x59, 0x89, 0xeb, 0x02, 0x9e, 0xcc, 0x89, 0x19, 0xa7, 0xcb, 0x17, 0x67}}},
{{{0x6a, 0xeb, 0xfc, 0x9a, 0x9a, 0x10, 0xce, 0xdb, 0x3a, 0x1c, 0x3c, 0x6a, 0x9d, 0xea, 0x46, 0xbc, 0x45, 0x49, 0xac, 0xe3, 0x41, 0x12, 0x7c, 0xf0, 0xf7, 0x4f, 0xf9, 0xf7, 0xff, 0x2c, 0x89, 0x04}} ,
{{0x30, 0x31, 0x54, 0x1a, 0x46, 0xca, 0xe6, 0xc6, 0xcb, 0xe2, 0xc3, 0xc1, 0x8b, 0x75, 0x81, 0xbe, 0xee, 0xf8, 0xa3, 0x11, 0x1c, 0x25, 0xa3, 0xa7, 0x35, 0x51, 0x55, 0xe2, 0x25, 0xaa, 0xe2, 0x3a}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0xb4, 0x48, 0x10, 0x9f, 0x8a, 0x09, 0x76, 0xfa, 0xf0, 0x7a, 0xb0, 0x70, 0xf7, 0x83, 0x80, 0x52, 0x84, 0x2b, 0x26, 0xa2, 0xc4, 0x5d, 0x4f, 0xba, 0xb1, 0xc8, 0x40, 0x0d, 0x78, 0x97, 0xc4, 0x60}} ,
{{0xd4, 0xb1, 0x6c, 0x08, 0xc7, 0x40, 0x38, 0x73, 0x5f, 0x0b, 0xf3, 0x76, 0x5d, 0xb2, 0xa5, 0x2f, 0x57, 0x57, 0x07, 0xed, 0x08, 0xa2, 0x6c, 0x4f, 0x08, 0x02, 0xb5, 0x0e, 0xee, 0x44, 0xfa, 0x22}}},
@@ -1616,7 +1477,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0xff, 0x76, 0x03, 0xc5, 0x4b, 0x89, 0x99, 0x70, 0x00, 0x59, 0x70, 0x9c, 0xd5, 0xd9, 0x11, 0x89, 0x5a, 0x46, 0xfe, 0xef, 0xdc, 0xd9, 0x55, 0x2b, 0x45, 0xa7, 0xb0, 0x2d, 0xfb, 0x24, 0xc2, 0x29}}},
{{{0x38, 0x06, 0xf8, 0x0b, 0xac, 0x82, 0xc4, 0x97, 0x2b, 0x90, 0xe0, 0xf7, 0xa8, 0xab, 0x6c, 0x08, 0x80, 0x66, 0x90, 0x46, 0xf7, 0x26, 0x2d, 0xf8, 0xf1, 0xc4, 0x6b, 0x4a, 0x82, 0x98, 0x8e, 0x37}} ,
{{0x8e, 0xb4, 0xee, 0xb8, 0xd4, 0x3f, 0xb2, 0x1b, 0xe0, 0x0a, 0x3d, 0x75, 0x34, 0x28, 0xa2, 0x8e, 0xc4, 0x92, 0x7b, 0xfe, 0x60, 0x6e, 0x6d, 0xb8, 0x31, 0x1d, 0x62, 0x0d, 0x78, 0x14, 0x42, 0x11}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x5e, 0xa8, 0xd8, 0x04, 0x9b, 0x73, 0xc9, 0xc9, 0xdc, 0x0d, 0x73, 0xbf, 0x0a, 0x0a, 0x73, 0xff, 0x18, 0x1f, 0x9c, 0x51, 0xaa, 0xc6, 0xf1, 0x83, 0x25, 0xfd, 0xab, 0xa3, 0x11, 0xd3, 0x01, 0x24}} ,
{{0x4d, 0xe3, 0x7e, 0x38, 0x62, 0x5e, 0x64, 0xbb, 0x2b, 0x53, 0xb5, 0x03, 0x68, 0xc4, 0xf2, 0x2b, 0x5a, 0x03, 0x32, 0x99, 0x4a, 0x41, 0x9a, 0xe1, 0x1a, 0xae, 0x8c, 0x48, 0xf3, 0x24, 0x32, 0x65}}},
@@ -1626,7 +1487,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x7c, 0x31, 0x33, 0xcc, 0xe4, 0xcf, 0x6c, 0xff, 0x80, 0x47, 0x77, 0xd1, 0xd8, 0xe9, 0x69, 0x97, 0x98, 0x7f, 0x20, 0x57, 0x1d, 0x1d, 0x4f, 0x08, 0x27, 0xc8, 0x35, 0x57, 0x40, 0xc6, 0x21, 0x0c}}},
{{{0xd2, 0x8e, 0x9b, 0xfa, 0x42, 0x8e, 0xdf, 0x8f, 0xc7, 0x86, 0xf9, 0xa4, 0xca, 0x70, 0x00, 0x9d, 0x21, 0xbf, 0xec, 0x57, 0x62, 0x30, 0x58, 0x8c, 0x0d, 0x35, 0xdb, 0x5d, 0x8b, 0x6a, 0xa0, 0x5a}} ,
{{0xc1, 0x58, 0x7c, 0x0d, 0x20, 0xdd, 0x11, 0x26, 0x5f, 0x89, 0x3b, 0x97, 0x58, 0xf8, 0x8b, 0xe3, 0xdf, 0x32, 0xe2, 0xfc, 0xd8, 0x67, 0xf2, 0xa5, 0x37, 0x1e, 0x6d, 0xec, 0x7c, 0x27, 0x20, 0x79}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0xd0, 0xe9, 0xc0, 0xfa, 0x95, 0x45, 0x23, 0x96, 0xf1, 0x2c, 0x79, 0x25, 0x14, 0xce, 0x40, 0x14, 0x44, 0x2c, 0x36, 0x50, 0xd9, 0x63, 0x56, 0xb7, 0x56, 0x3b, 0x9e, 0xa7, 0xef, 0x89, 0xbb, 0x0e}} ,
{{0xce, 0x7f, 0xdc, 0x0a, 0xcc, 0x82, 0x1c, 0x0a, 0x78, 0x71, 0xe8, 0x74, 0x8d, 0x01, 0x30, 0x0f, 0xa7, 0x11, 0x4c, 0xdf, 0x38, 0xd7, 0xa7, 0x0d, 0xf8, 0x48, 0x52, 0x00, 0x80, 0x7b, 0x5f, 0x0e}}},
@@ -1636,7 +1497,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x50, 0x5b, 0xff, 0xa9, 0xb2, 0xf1, 0xf1, 0x78, 0xcf, 0x14, 0xa4, 0xa9, 0xfc, 0x09, 0x46, 0x94, 0x54, 0x65, 0x0d, 0x9c, 0x5f, 0x72, 0x21, 0xe2, 0x97, 0xa5, 0x2d, 0x81, 0xce, 0x4a, 0x5f, 0x79}}},
{{{0x3d, 0x5f, 0x5c, 0xd2, 0xbc, 0x7d, 0x77, 0x0e, 0x2a, 0x6d, 0x22, 0x45, 0x84, 0x06, 0xc4, 0xdd, 0xc6, 0xa6, 0xc6, 0xd7, 0x49, 0xad, 0x6d, 0x87, 0x91, 0x0e, 0x3a, 0x67, 0x1d, 0x2c, 0x1d, 0x56}} ,
{{0xfe, 0x7a, 0x74, 0xcf, 0xd4, 0xd2, 0xe5, 0x19, 0xde, 0xd0, 0xdb, 0x70, 0x23, 0x69, 0xe6, 0x6d, 0xec, 0xec, 0xcc, 0x09, 0x33, 0x6a, 0x77, 0xdc, 0x6b, 0x22, 0x76, 0x5d, 0x92, 0x09, 0xac, 0x2d}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x23, 0x15, 0x17, 0xeb, 0xd3, 0xdb, 0x12, 0x5e, 0x01, 0xf0, 0x91, 0xab, 0x2c, 0x41, 0xce, 0xac, 0xed, 0x1b, 0x4b, 0x2d, 0xbc, 0xdb, 0x17, 0x66, 0x89, 0x46, 0xad, 0x4b, 0x1e, 0x6f, 0x0b, 0x14}} ,
{{0x11, 0xce, 0xbf, 0xb6, 0x77, 0x2d, 0x48, 0x22, 0x18, 0x4f, 0xa3, 0x5d, 0x4a, 0xb0, 0x70, 0x12, 0x3e, 0x54, 0xd7, 0xd8, 0x0e, 0x2b, 0x27, 0xdc, 0x53, 0xff, 0xca, 0x8c, 0x59, 0xb3, 0x4e, 0x44}}},
@@ -1646,7 +1507,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x0d, 0x16, 0xac, 0x3c, 0x4a, 0x58, 0x86, 0x3a, 0x46, 0x7f, 0x6c, 0xa3, 0x52, 0x6e, 0x37, 0xe4, 0x96, 0x9c, 0xe9, 0x5c, 0x66, 0x41, 0x67, 0xe4, 0xfb, 0x79, 0x0c, 0x05, 0xf6, 0x64, 0xd5, 0x7c}}},
{{{0x28, 0xc1, 0xe1, 0x54, 0x73, 0xf2, 0xbf, 0x76, 0x74, 0x19, 0x19, 0x1b, 0xe4, 0xb9, 0xa8, 0x46, 0x65, 0x73, 0xf3, 0x77, 0x9b, 0x29, 0x74, 0x5b, 0xc6, 0x89, 0x6c, 0x2c, 0x7c, 0xf8, 0xb3, 0x0f}} ,
{{0xf7, 0xd5, 0xe9, 0x74, 0x5d, 0xb8, 0x25, 0x16, 0xb5, 0x30, 0xbc, 0x84, 0xc5, 0xf0, 0xad, 0xca, 0x12, 0x28, 0xbc, 0x9d, 0xd4, 0xfa, 0x82, 0xe6, 0xe3, 0xbf, 0xa2, 0x15, 0x2c, 0xd4, 0x34, 0x10}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x61, 0xb1, 0x46, 0xba, 0x0e, 0x31, 0xa5, 0x67, 0x6c, 0x7f, 0xd6, 0xd9, 0x27, 0x85, 0x0f, 0x79, 0x14, 0xc8, 0x6c, 0x2f, 0x5f, 0x5b, 0x9c, 0x35, 0x3d, 0x38, 0x86, 0x77, 0x65, 0x55, 0x6a, 0x7b}} ,
{{0xd3, 0xb0, 0x3a, 0x66, 0x60, 0x1b, 0x43, 0xf1, 0x26, 0x58, 0x99, 0x09, 0x8f, 0x2d, 0xa3, 0x14, 0x71, 0x85, 0xdb, 0xed, 0xf6, 0x26, 0xd5, 0x61, 0x9a, 0x73, 0xac, 0x0e, 0xea, 0xac, 0xb7, 0x0c}}},
@@ -1656,7 +1517,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0xe8, 0xc1, 0xa9, 0xc2, 0x7b, 0x59, 0x21, 0x33, 0xe2, 0x43, 0x73, 0x2b, 0xac, 0x2d, 0xc1, 0x89, 0x3b, 0x15, 0xe2, 0xd5, 0xc0, 0x97, 0x8a, 0xfd, 0x6f, 0x36, 0x33, 0xb7, 0xb9, 0xc3, 0x88, 0x09}}},
{{{0xd0, 0xb6, 0x56, 0x30, 0x5c, 0xae, 0xb3, 0x75, 0x44, 0xa4, 0x83, 0x51, 0x6e, 0x01, 0x65, 0xef, 0x45, 0x76, 0xe6, 0xf5, 0xa2, 0x0d, 0xd4, 0x16, 0x3b, 0x58, 0x2f, 0xf2, 0x2f, 0x36, 0x18, 0x3f}} ,
{{0xfd, 0x2f, 0xe0, 0x9b, 0x1e, 0x8c, 0xc5, 0x18, 0xa9, 0xca, 0xd4, 0x2b, 0x35, 0xb6, 0x95, 0x0a, 0x9f, 0x7e, 0xfb, 0xc4, 0xef, 0x88, 0x7b, 0x23, 0x43, 0xec, 0x2f, 0x0d, 0x0f, 0x7a, 0xfc, 0x5c}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x8d, 0xd2, 0xda, 0xc7, 0x44, 0xd6, 0x7a, 0xdb, 0x26, 0x7d, 0x1d, 0xb8, 0xe1, 0xde, 0x9d, 0x7a, 0x7d, 0x17, 0x7e, 0x1c, 0x37, 0x04, 0x8d, 0x2d, 0x7c, 0x5e, 0x18, 0x38, 0x1e, 0xaf, 0xc7, 0x1b}} ,
{{0x33, 0x48, 0x31, 0x00, 0x59, 0xf6, 0xf2, 0xca, 0x0f, 0x27, 0x1b, 0x63, 0x12, 0x7e, 0x02, 0x1d, 0x49, 0xc0, 0x5d, 0x79, 0x87, 0xef, 0x5e, 0x7a, 0x2f, 0x1f, 0x66, 0x55, 0xd8, 0x09, 0xd9, 0x61}}},
@@ -1666,7 +1527,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0xa1, 0x4c, 0x70, 0x4b, 0x0e, 0xa0, 0x83, 0x70, 0x13, 0xa4, 0xaf, 0xb8, 0x38, 0x19, 0x22, 0x65, 0x09, 0xb4, 0x02, 0x4f, 0x06, 0xf8, 0x17, 0xce, 0x46, 0x45, 0xda, 0x50, 0x7c, 0x8a, 0xd1, 0x4e}}},
{{{0xf7, 0xd4, 0x16, 0x6c, 0x4e, 0x95, 0x9d, 0x5d, 0x0f, 0x91, 0x2b, 0x52, 0xfe, 0x5c, 0x34, 0xe5, 0x30, 0xe6, 0xa4, 0x3b, 0xf3, 0xf3, 0x34, 0x08, 0xa9, 0x4a, 0xa0, 0xb5, 0x6e, 0xb3, 0x09, 0x0a}} ,
{{0x26, 0xd9, 0x5e, 0xa3, 0x0f, 0xeb, 0xa2, 0xf3, 0x20, 0x3b, 0x37, 0xd4, 0xe4, 0x9e, 0xce, 0x06, 0x3d, 0x53, 0xed, 0xae, 0x2b, 0xeb, 0xb6, 0x24, 0x0a, 0x11, 0xa3, 0x0f, 0xd6, 0x7f, 0xa4, 0x3a}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0xdb, 0x9f, 0x2c, 0xfc, 0xd6, 0xb2, 0x1e, 0x2e, 0x52, 0x7a, 0x06, 0x87, 0x2d, 0x86, 0x72, 0x2b, 0x6d, 0x90, 0x77, 0x46, 0x43, 0xb5, 0x7a, 0xf8, 0x60, 0x7d, 0x91, 0x60, 0x5b, 0x9d, 0x9e, 0x07}} ,
{{0x97, 0x87, 0xc7, 0x04, 0x1c, 0x38, 0x01, 0x39, 0x58, 0xc7, 0x85, 0xa3, 0xfc, 0x64, 0x00, 0x64, 0x25, 0xa2, 0xbf, 0x50, 0x94, 0xca, 0x26, 0x31, 0x45, 0x0a, 0x24, 0xd2, 0x51, 0x29, 0x51, 0x16}}},
@@ -1676,7 +1537,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x95, 0x21, 0x01, 0xce, 0x95, 0x5b, 0x0e, 0x57, 0xc7, 0xb9, 0x62, 0xb5, 0x28, 0xca, 0x11, 0xec, 0xb4, 0x46, 0x06, 0x73, 0x26, 0xff, 0xfb, 0x66, 0x7d, 0xee, 0x5f, 0xb2, 0x56, 0xfd, 0x2a, 0x08}}},
{{{0x92, 0x67, 0x77, 0x56, 0xa1, 0xff, 0xc4, 0xc5, 0x95, 0xf0, 0xe3, 0x3a, 0x0a, 0xca, 0x94, 0x4d, 0x9e, 0x7e, 0x3d, 0xb9, 0x6e, 0xb6, 0xb0, 0xce, 0xa4, 0x30, 0x89, 0x99, 0xe9, 0xad, 0x11, 0x59}} ,
{{0xf6, 0x48, 0x95, 0xa1, 0x6f, 0x5f, 0xb7, 0xa5, 0xbb, 0x30, 0x00, 0x1c, 0xd2, 0x8a, 0xd6, 0x25, 0x26, 0x1b, 0xb2, 0x0d, 0x37, 0x6a, 0x05, 0xf4, 0x9d, 0x3e, 0x17, 0x2a, 0x43, 0xd2, 0x3a, 0x06}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x32, 0x99, 0x93, 0xd1, 0x9a, 0x72, 0xf3, 0xa9, 0x16, 0xbd, 0xb4, 0x4c, 0xdd, 0xf9, 0xd4, 0xb2, 0x64, 0x9a, 0xd3, 0x05, 0xe4, 0xa3, 0x73, 0x1c, 0xcb, 0x7e, 0x57, 0x67, 0xff, 0x04, 0xb3, 0x10}} ,
{{0xb9, 0x4b, 0xa4, 0xad, 0xd0, 0x6d, 0x61, 0x23, 0xb4, 0xaf, 0x34, 0xa9, 0xaa, 0x65, 0xec, 0xd9, 0x69, 0xe3, 0x85, 0xcd, 0xcc, 0xe7, 0xb0, 0x9b, 0x41, 0xc1, 0x1c, 0xf9, 0xa0, 0xfa, 0xb7, 0x13}}},
@@ -1686,7 +1547,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0xfd, 0x4e, 0x2a, 0xc1, 0x3a, 0xca, 0x8f, 0x00, 0xd8, 0xec, 0x74, 0x67, 0xef, 0x61, 0xe0, 0x28, 0xd0, 0x96, 0xf4, 0x48, 0xde, 0x81, 0xe3, 0xef, 0xdc, 0xaa, 0x7d, 0xf3, 0xb6, 0x55, 0xa6, 0x65}}},
{{{0xeb, 0xcb, 0xc5, 0x70, 0x91, 0x31, 0x10, 0x93, 0x0d, 0xc8, 0xd0, 0xef, 0x62, 0xe8, 0x6f, 0x82, 0xe3, 0x69, 0x3d, 0x91, 0x7f, 0x31, 0xe1, 0x26, 0x35, 0x3c, 0x4a, 0x2f, 0xab, 0xc4, 0x9a, 0x5e}} ,
{{0xab, 0x1b, 0xb5, 0xe5, 0x2b, 0xc3, 0x0e, 0x29, 0xb0, 0xd0, 0x73, 0xe6, 0x4f, 0x64, 0xf2, 0xbc, 0xe4, 0xe4, 0xe1, 0x9a, 0x52, 0x33, 0x2f, 0xbd, 0xcc, 0x03, 0xee, 0x8a, 0xfa, 0x00, 0x5f, 0x50}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0xf6, 0xdb, 0x0d, 0x22, 0x3d, 0xb5, 0x14, 0x75, 0x31, 0xf0, 0x81, 0xe2, 0xb9, 0x37, 0xa2, 0xa9, 0x84, 0x11, 0x9a, 0x07, 0xb5, 0x53, 0x89, 0x78, 0xa9, 0x30, 0x27, 0xa1, 0xf1, 0x4e, 0x5c, 0x2e}} ,
{{0x8b, 0x00, 0x54, 0xfb, 0x4d, 0xdc, 0xcb, 0x17, 0x35, 0x40, 0xff, 0xb7, 0x8c, 0xfe, 0x4a, 0xe4, 0x4e, 0x99, 0x4e, 0xa8, 0x74, 0x54, 0x5d, 0x5c, 0x96, 0xa3, 0x12, 0x55, 0x36, 0x31, 0x17, 0x5c}}},
@@ -1696,7 +1557,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x8c, 0x44, 0x09, 0x28, 0xd5, 0x23, 0xc9, 0x8f, 0xf3, 0x84, 0x45, 0xc6, 0x9a, 0x5e, 0xff, 0xd2, 0xc7, 0x57, 0x93, 0xa3, 0xc1, 0x69, 0xdd, 0x62, 0x0f, 0xda, 0x5c, 0x30, 0x59, 0x5d, 0xe9, 0x4c}}},
{{{0x92, 0x7e, 0x50, 0x27, 0x72, 0xd7, 0x0c, 0xd6, 0x69, 0x96, 0x81, 0x35, 0x84, 0x94, 0x35, 0x8b, 0x6c, 0xaa, 0x62, 0x86, 0x6e, 0x1c, 0x15, 0xf3, 0x6c, 0xb3, 0xff, 0x65, 0x1b, 0xa2, 0x9b, 0x59}} ,
{{0xe2, 0xa9, 0x65, 0x88, 0xc4, 0x50, 0xfa, 0xbb, 0x3b, 0x6e, 0x5f, 0x44, 0x01, 0xca, 0x97, 0xd4, 0xdd, 0xf6, 0xcd, 0x3f, 0x3f, 0xe5, 0x97, 0x67, 0x2b, 0x8c, 0x66, 0x0f, 0x35, 0x9b, 0xf5, 0x07}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0xf1, 0x59, 0x27, 0xd8, 0xdb, 0x5a, 0x11, 0x5e, 0x82, 0xf3, 0x38, 0xff, 0x1c, 0xed, 0xfe, 0x3f, 0x64, 0x54, 0x3f, 0x7f, 0xd1, 0x81, 0xed, 0xef, 0x65, 0xc5, 0xcb, 0xfd, 0xe1, 0x80, 0xcd, 0x11}} ,
{{0xe0, 0xdb, 0x22, 0x28, 0xe6, 0xff, 0x61, 0x9d, 0x41, 0x14, 0x2d, 0x3b, 0x26, 0x22, 0xdf, 0xf1, 0x34, 0x81, 0xe9, 0x45, 0xee, 0x0f, 0x98, 0x8b, 0xa6, 0x3f, 0xef, 0xf7, 0x43, 0x19, 0xf1, 0x43}}},
@@ -1706,7 +1567,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x60, 0xbc, 0x45, 0x1f, 0x23, 0xa2, 0x3b, 0x70, 0x76, 0xe6, 0x97, 0x99, 0x4f, 0x77, 0x54, 0x67, 0x30, 0x9a, 0xe7, 0x66, 0xd6, 0xcd, 0x2e, 0x51, 0x24, 0x2c, 0x42, 0x4a, 0x11, 0xfe, 0x6f, 0x7e}}},
{{{0x87, 0xc0, 0xb1, 0xf0, 0xa3, 0x6f, 0x0c, 0x93, 0xa9, 0x0a, 0x72, 0xef, 0x5c, 0xbe, 0x65, 0x35, 0xa7, 0x6a, 0x4e, 0x2c, 0xbf, 0x21, 0x23, 0xe8, 0x2f, 0x97, 0xc7, 0x3e, 0xc8, 0x17, 0xac, 0x1e}} ,
{{0x7b, 0xef, 0x21, 0xe5, 0x40, 0xcc, 0x1e, 0xdc, 0xd6, 0xbd, 0x97, 0x7a, 0x7c, 0x75, 0x86, 0x7a, 0x25, 0x5a, 0x6e, 0x7c, 0xe5, 0x51, 0x3c, 0x1b, 0x5b, 0x82, 0x9a, 0x07, 0x60, 0xa1, 0x19, 0x04}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x96, 0x88, 0xa6, 0xab, 0x8f, 0xe3, 0x3a, 0x49, 0xf8, 0xfe, 0x34, 0xe7, 0x6a, 0xb2, 0xfe, 0x40, 0x26, 0x74, 0x57, 0x4c, 0xf6, 0xd4, 0x99, 0xce, 0x5d, 0x7b, 0x2f, 0x67, 0xd6, 0x5a, 0xe4, 0x4e}} ,
{{0x5c, 0x82, 0xb3, 0xbd, 0x55, 0x25, 0xf6, 0x6a, 0x93, 0xa4, 0x02, 0xc6, 0x7d, 0x5c, 0xb1, 0x2b, 0x5b, 0xff, 0xfb, 0x56, 0xf8, 0x01, 0x41, 0x90, 0xc6, 0xb6, 0xac, 0x4f, 0xfe, 0xa7, 0x41, 0x70}}},
@@ -1716,7 +1577,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x17, 0x0f, 0x86, 0x52, 0xd7, 0x9d, 0xc3, 0x44, 0x51, 0x76, 0x32, 0x65, 0xb4, 0x37, 0x81, 0x99, 0x46, 0x37, 0x62, 0xed, 0xcf, 0x64, 0x9d, 0x72, 0x40, 0x7a, 0x4c, 0x0b, 0x76, 0x2a, 0xfb, 0x56}}},
{{{0x33, 0xa7, 0x90, 0x7c, 0xc3, 0x6f, 0x17, 0xa5, 0xa0, 0x67, 0x72, 0x17, 0xea, 0x7e, 0x63, 0x14, 0x83, 0xde, 0xc1, 0x71, 0x2d, 0x41, 0x32, 0x7a, 0xf3, 0xd1, 0x2b, 0xd8, 0x2a, 0xa6, 0x46, 0x36}} ,
{{0xac, 0xcc, 0x6b, 0x7c, 0xf9, 0xb8, 0x8b, 0x08, 0x5c, 0xd0, 0x7d, 0x8f, 0x73, 0xea, 0x20, 0xda, 0x86, 0xca, 0x00, 0xc7, 0xad, 0x73, 0x4d, 0xe9, 0xe8, 0xa9, 0xda, 0x1f, 0x03, 0x06, 0xdd, 0x24}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x9c, 0xb2, 0x61, 0x0a, 0x98, 0x2a, 0xa5, 0xd7, 0xee, 0xa9, 0xac, 0x65, 0xcb, 0x0a, 0x1e, 0xe2, 0xbe, 0xdc, 0x85, 0x59, 0x0f, 0x9c, 0xa6, 0x57, 0x34, 0xa5, 0x87, 0xeb, 0x7b, 0x1e, 0x0c, 0x3c}} ,
{{0x2f, 0xbd, 0x84, 0x63, 0x0d, 0xb5, 0xa0, 0xf0, 0x4b, 0x9e, 0x93, 0xc6, 0x34, 0x9a, 0x34, 0xff, 0x73, 0x19, 0x2f, 0x6e, 0x54, 0x45, 0x2c, 0x92, 0x31, 0x76, 0x34, 0xf1, 0xb2, 0x26, 0xe8, 0x74}}},
@@ -1726,7 +1587,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x87, 0xc8, 0xa0, 0x6e, 0xe1, 0xb0, 0xad, 0x6a, 0x4a, 0x34, 0x71, 0xed, 0x7c, 0xd6, 0x44, 0x03, 0x65, 0x4a, 0x5c, 0x5c, 0x04, 0xf5, 0x24, 0x3f, 0xb0, 0x16, 0x5e, 0x8c, 0xb2, 0xd2, 0xc5, 0x20}}},
{{{0x98, 0x83, 0xc2, 0x37, 0xa0, 0x41, 0xa8, 0x48, 0x5c, 0x5f, 0xbf, 0xc8, 0xfa, 0x24, 0xe0, 0x59, 0x2c, 0xbd, 0xf6, 0x81, 0x7e, 0x88, 0xe6, 0xca, 0x04, 0xd8, 0x5d, 0x60, 0xbb, 0x74, 0xa7, 0x0b}} ,
{{0x21, 0x13, 0x91, 0xbf, 0x77, 0x7a, 0x33, 0xbc, 0xe9, 0x07, 0x39, 0x0a, 0xdd, 0x7d, 0x06, 0x10, 0x9a, 0xee, 0x47, 0x73, 0x1b, 0x15, 0x5a, 0xfb, 0xcd, 0x4d, 0xd0, 0xd2, 0x3a, 0x01, 0xba, 0x54}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x48, 0xd5, 0x39, 0x4a, 0x0b, 0x20, 0x6a, 0x43, 0xa0, 0x07, 0x82, 0x5e, 0x49, 0x7c, 0xc9, 0x47, 0xf1, 0x7c, 0x37, 0xb9, 0x23, 0xef, 0x6b, 0x46, 0x45, 0x8c, 0x45, 0x76, 0xdf, 0x14, 0x6b, 0x6e}} ,
{{0x42, 0xc9, 0xca, 0x29, 0x4c, 0x76, 0x37, 0xda, 0x8a, 0x2d, 0x7c, 0x3a, 0x58, 0xf2, 0x03, 0xb4, 0xb5, 0xb9, 0x1a, 0x13, 0x2d, 0xde, 0x5f, 0x6b, 0x9d, 0xba, 0x52, 0xc9, 0x5d, 0xb3, 0xf3, 0x30}}},
@@ -1736,7 +1597,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0xe4, 0xe7, 0x43, 0x4b, 0xa0, 0x3f, 0x2b, 0x06, 0xba, 0x17, 0xae, 0x3d, 0xe6, 0xce, 0xbd, 0xb8, 0xed, 0x74, 0x11, 0x35, 0xec, 0x96, 0xfe, 0x31, 0xe3, 0x0e, 0x7a, 0x4e, 0xc9, 0x1d, 0xcb, 0x20}}},
{{{0xe0, 0x67, 0xe9, 0x7b, 0xdb, 0x96, 0x5c, 0xb0, 0x32, 0xd0, 0x59, 0x31, 0x90, 0xdc, 0x92, 0x97, 0xac, 0x09, 0x38, 0x31, 0x0f, 0x7e, 0xd6, 0x5d, 0xd0, 0x06, 0xb6, 0x1f, 0xea, 0xf0, 0x5b, 0x07}} ,
{{0x81, 0x9f, 0xc7, 0xde, 0x6b, 0x41, 0x22, 0x35, 0x14, 0x67, 0x77, 0x3e, 0x90, 0x81, 0xb0, 0xd9, 0x85, 0x4c, 0xca, 0x9b, 0x3f, 0x04, 0x59, 0xd6, 0xaa, 0x17, 0xc3, 0x88, 0x34, 0x37, 0xba, 0x43}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x4c, 0xb6, 0x69, 0xc8, 0x81, 0x95, 0x94, 0x33, 0x92, 0x34, 0xe9, 0x3c, 0x84, 0x0d, 0x3d, 0x5a, 0x37, 0x9c, 0x22, 0xa0, 0xaa, 0x65, 0xce, 0xb4, 0xc2, 0x2d, 0x66, 0x67, 0x02, 0xff, 0x74, 0x10}} ,
{{0x22, 0xb0, 0xd5, 0xe6, 0xc7, 0xef, 0xb1, 0xa7, 0x13, 0xda, 0x60, 0xb4, 0x80, 0xc1, 0x42, 0x7d, 0x10, 0x70, 0x97, 0x04, 0x4d, 0xda, 0x23, 0x89, 0xc2, 0x0e, 0x68, 0xcb, 0xde, 0xe0, 0x9b, 0x29}}},
@@ -1746,7 +1607,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0xa0, 0x20, 0x99, 0x69, 0x0a, 0xae, 0xa3, 0x70, 0x4e, 0x64, 0x80, 0xb7, 0x85, 0x9c, 0x87, 0x54, 0x43, 0x43, 0x55, 0x80, 0x6d, 0x8d, 0x7c, 0xa9, 0x64, 0xca, 0x6c, 0x2e, 0x21, 0xd8, 0xc8, 0x6c}}},
{{{0x91, 0x4a, 0x07, 0xad, 0x08, 0x75, 0xc1, 0x4f, 0xa4, 0xb2, 0xc3, 0x6f, 0x46, 0x3e, 0xb1, 0xce, 0x52, 0xab, 0x67, 0x09, 0x54, 0x48, 0x6b, 0x6c, 0xd7, 0x1d, 0x71, 0x76, 0xcb, 0xff, 0xdd, 0x31}} ,
{{0x36, 0x88, 0xfa, 0xfd, 0xf0, 0x36, 0x6f, 0x07, 0x74, 0x88, 0x50, 0xd0, 0x95, 0x38, 0x4a, 0x48, 0x2e, 0x07, 0x64, 0x97, 0x11, 0x76, 0x01, 0x1a, 0x27, 0x4d, 0x8e, 0x25, 0x9a, 0x9b, 0x1c, 0x22}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0xbe, 0x57, 0xbd, 0x0e, 0x0f, 0xac, 0x5e, 0x76, 0xa3, 0x71, 0xad, 0x2b, 0x10, 0x45, 0x02, 0xec, 0x59, 0xd5, 0x5d, 0xa9, 0x44, 0xcc, 0x25, 0x4c, 0xb3, 0x3c, 0x5b, 0x69, 0x07, 0x55, 0x26, 0x6b}} ,
{{0x30, 0x6b, 0xd4, 0xa7, 0x51, 0x29, 0xe3, 0xf9, 0x7a, 0x75, 0x2a, 0x82, 0x2f, 0xd6, 0x1d, 0x99, 0x2b, 0x80, 0xd5, 0x67, 0x1e, 0x15, 0x9d, 0xca, 0xfd, 0xeb, 0xac, 0x97, 0x35, 0x09, 0x7f, 0x3f}}},
@@ -1756,7 +1617,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0xad, 0xa0, 0x41, 0xec, 0xc8, 0x4d, 0xb9, 0xd2, 0x6e, 0x96, 0x4e, 0x5b, 0xc5, 0xc2, 0xa0, 0x1b, 0xcf, 0x0c, 0xbf, 0x17, 0x66, 0x57, 0xc1, 0x17, 0x90, 0x45, 0x71, 0xc2, 0xe1, 0x24, 0xeb, 0x27}}},
{{{0x2c, 0xb9, 0x42, 0xa4, 0xaf, 0x3b, 0x42, 0x0e, 0xc2, 0x0f, 0xf2, 0xea, 0x83, 0xaf, 0x9a, 0x13, 0x17, 0xb0, 0xbd, 0x89, 0x17, 0xe3, 0x72, 0xcb, 0x0e, 0x76, 0x7e, 0x41, 0x63, 0x04, 0x88, 0x71}} ,
{{0x75, 0x78, 0x38, 0x86, 0x57, 0xdd, 0x9f, 0xee, 0x54, 0x70, 0x65, 0xbf, 0xf1, 0x2c, 0xe0, 0x39, 0x0d, 0xe3, 0x89, 0xfd, 0x8e, 0x93, 0x4f, 0x43, 0xdc, 0xd5, 0x5b, 0xde, 0xf9, 0x98, 0xe5, 0x7b}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0xe7, 0x3b, 0x65, 0x11, 0xdf, 0xb2, 0xf2, 0x63, 0x94, 0x12, 0x6f, 0x5c, 0x9e, 0x77, 0xc1, 0xb6, 0xd8, 0xab, 0x58, 0x7a, 0x1d, 0x95, 0x73, 0xdd, 0xe7, 0xe3, 0x6f, 0xf2, 0x03, 0x1d, 0xdb, 0x76}} ,
{{0xae, 0x06, 0x4e, 0x2c, 0x52, 0x1b, 0xbc, 0x5a, 0x5a, 0xa5, 0xbe, 0x27, 0xbd, 0xeb, 0xe1, 0x14, 0x17, 0x68, 0x26, 0x07, 0x03, 0xd1, 0x18, 0x0b, 0xdf, 0xf1, 0x06, 0x5c, 0xa6, 0x1b, 0xb9, 0x24}}},
@@ -1766,7 +1627,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x8d, 0xbd, 0xa3, 0xc4, 0x06, 0x9b, 0x1f, 0x90, 0x58, 0x60, 0x74, 0xb2, 0x00, 0x3b, 0x3c, 0xd2, 0xda, 0x82, 0xbb, 0x10, 0x90, 0x69, 0x92, 0xa9, 0xb4, 0x30, 0x81, 0xe3, 0x7c, 0xa8, 0x89, 0x45}}},
{{{0x3f, 0xdc, 0x05, 0xcb, 0x41, 0x3c, 0xc8, 0x23, 0x04, 0x2c, 0x38, 0x99, 0xe3, 0x68, 0x55, 0xf9, 0xd3, 0x32, 0xc7, 0xbf, 0xfa, 0xd4, 0x1b, 0x5d, 0xde, 0xdc, 0x10, 0x42, 0xc0, 0x42, 0xd9, 0x75}} ,
{{0x2d, 0xab, 0x35, 0x4e, 0x87, 0xc4, 0x65, 0x97, 0x67, 0x24, 0xa4, 0x47, 0xad, 0x3f, 0x8e, 0xf3, 0xcb, 0x31, 0x17, 0x77, 0xc5, 0xe2, 0xd7, 0x8f, 0x3c, 0xc1, 0xcd, 0x56, 0x48, 0xc1, 0x6c, 0x69}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x14, 0xae, 0x5f, 0x88, 0x7b, 0xa5, 0x90, 0xdf, 0x10, 0xb2, 0x8b, 0x5e, 0x24, 0x17, 0xc3, 0xa3, 0xd4, 0x0f, 0x92, 0x61, 0x1a, 0x19, 0x5a, 0xad, 0x76, 0xbd, 0xd8, 0x1c, 0xdd, 0xe0, 0x12, 0x6d}} ,
{{0x8e, 0xbd, 0x70, 0x8f, 0x02, 0xa3, 0x24, 0x4d, 0x5a, 0x67, 0xc4, 0xda, 0xf7, 0x20, 0x0f, 0x81, 0x5b, 0x7a, 0x05, 0x24, 0x67, 0x83, 0x0b, 0x2a, 0x80, 0xe7, 0xfd, 0x74, 0x4b, 0x9e, 0x5c, 0x0d}}},
@@ -1776,7 +1637,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x97, 0x29, 0x6c, 0xc4, 0x42, 0x0b, 0xdd, 0xc0, 0x29, 0x5c, 0x9b, 0x34, 0x97, 0xd0, 0xc7, 0x79, 0x80, 0x63, 0x74, 0xe4, 0x8e, 0x37, 0xb0, 0x2b, 0x7c, 0xe8, 0x68, 0x6c, 0xc3, 0x82, 0x97, 0x57}}},
{{{0x22, 0xbe, 0x83, 0xb6, 0x4b, 0x80, 0x6b, 0x43, 0x24, 0x5e, 0xef, 0x99, 0x9b, 0xa8, 0xfc, 0x25, 0x8d, 0x3b, 0x03, 0x94, 0x2b, 0x3e, 0xe7, 0x95, 0x76, 0x9b, 0xcc, 0x15, 0xdb, 0x32, 0xe6, 0x66}} ,
{{0x84, 0xf0, 0x4a, 0x13, 0xa6, 0xd6, 0xfa, 0x93, 0x46, 0x07, 0xf6, 0x7e, 0x5c, 0x6d, 0x5e, 0xf6, 0xa6, 0xe7, 0x48, 0xf0, 0x06, 0xea, 0xff, 0x90, 0xc1, 0xcc, 0x4c, 0x19, 0x9c, 0x3c, 0x4e, 0x53}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x2a, 0x50, 0xe3, 0x07, 0x15, 0x59, 0xf2, 0x8b, 0x81, 0xf2, 0xf3, 0xd3, 0x6c, 0x99, 0x8c, 0x70, 0x67, 0xec, 0xcc, 0xee, 0x9e, 0x59, 0x45, 0x59, 0x7d, 0x47, 0x75, 0x69, 0xf5, 0x24, 0x93, 0x5d}} ,
{{0x6a, 0x4f, 0x1b, 0xbe, 0x6b, 0x30, 0xcf, 0x75, 0x46, 0xe3, 0x7b, 0x9d, 0xfc, 0xcd, 0xd8, 0x5c, 0x1f, 0xb4, 0xc8, 0xe2, 0x24, 0xec, 0x1a, 0x28, 0x05, 0x32, 0x57, 0xfd, 0x3c, 0x5a, 0x98, 0x10}}},
@@ -1786,7 +1647,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x9b, 0x35, 0xe2, 0xa9, 0x3d, 0x32, 0x1e, 0xbb, 0x16, 0x28, 0x70, 0xe9, 0x45, 0x2f, 0x8f, 0x70, 0x7f, 0x08, 0x7e, 0x53, 0xc4, 0x7a, 0xbf, 0xf7, 0xe1, 0xa4, 0x6a, 0xd8, 0xac, 0x64, 0x1b, 0x11}}},
{{{0xb2, 0xeb, 0x47, 0x46, 0x18, 0x3e, 0x1f, 0x99, 0x0c, 0xcc, 0xf1, 0x2c, 0xe0, 0xe7, 0x8f, 0xe0, 0x01, 0x7e, 0x65, 0xb8, 0x0c, 0xd0, 0xfb, 0xc8, 0xb9, 0x90, 0x98, 0x33, 0x61, 0x3b, 0xd8, 0x27}} ,
{{0xa0, 0xbe, 0x72, 0x3a, 0x50, 0x4b, 0x74, 0xab, 0x01, 0xc8, 0x93, 0xc5, 0xe4, 0xc7, 0x08, 0x6c, 0xb4, 0xca, 0xee, 0xeb, 0x8e, 0xd7, 0x4e, 0x26, 0xc6, 0x1d, 0xe2, 0x71, 0xaf, 0x89, 0xa0, 0x2a}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x98, 0x0b, 0xe4, 0xde, 0xdb, 0xa8, 0xfa, 0x82, 0x74, 0x06, 0x52, 0x6d, 0x08, 0x52, 0x8a, 0xff, 0x62, 0xc5, 0x6a, 0x44, 0x0f, 0x51, 0x8c, 0x1f, 0x6e, 0xb6, 0xc6, 0x2c, 0x81, 0xd3, 0x76, 0x46}} ,
{{0xf4, 0x29, 0x74, 0x2e, 0x80, 0xa7, 0x1a, 0x8f, 0xf6, 0xbd, 0xd6, 0x8e, 0xbf, 0xc1, 0x95, 0x2a, 0xeb, 0xa0, 0x7f, 0x45, 0xa0, 0x50, 0x14, 0x05, 0xb1, 0x57, 0x4c, 0x74, 0xb7, 0xe2, 0x89, 0x7d}}},
@@ -1796,7 +1657,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x29, 0xd8, 0xf2, 0xaa, 0xe9, 0x0e, 0xf7, 0x2e, 0x5f, 0x9d, 0x8a, 0x5b, 0x09, 0xed, 0xc9, 0x24, 0x22, 0xf4, 0x0f, 0x25, 0x8f, 0x1c, 0x84, 0x6e, 0x34, 0x14, 0x6c, 0xea, 0xb3, 0x86, 0x5d, 0x04}}},
{{{0x07, 0x98, 0x61, 0xe8, 0x6a, 0xd2, 0x81, 0x49, 0x25, 0xd5, 0x5b, 0x18, 0xc7, 0x35, 0x52, 0x51, 0xa4, 0x46, 0xad, 0x18, 0x0d, 0xc9, 0x5f, 0x18, 0x91, 0x3b, 0xb4, 0xc0, 0x60, 0x59, 0x8d, 0x66}} ,
{{0x03, 0x1b, 0x79, 0x53, 0x6e, 0x24, 0xae, 0x57, 0xd9, 0x58, 0x09, 0x85, 0x48, 0xa2, 0xd3, 0xb5, 0xe2, 0x4d, 0x11, 0x82, 0xe6, 0x86, 0x3c, 0xe9, 0xb1, 0x00, 0x19, 0xc2, 0x57, 0xf7, 0x66, 0x7a}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x0f, 0xe3, 0x89, 0x03, 0xd7, 0x22, 0x95, 0x9f, 0xca, 0xb4, 0x8d, 0x9e, 0x6d, 0x97, 0xff, 0x8d, 0x21, 0x59, 0x07, 0xef, 0x03, 0x2d, 0x5e, 0xf8, 0x44, 0x46, 0xe7, 0x85, 0x80, 0xc5, 0x89, 0x50}} ,
{{0x8b, 0xd8, 0x53, 0x86, 0x24, 0x86, 0x29, 0x52, 0x01, 0xfa, 0x20, 0xc3, 0x4e, 0x95, 0xcb, 0xad, 0x7b, 0x34, 0x94, 0x30, 0xb7, 0x7a, 0xfa, 0x96, 0x41, 0x60, 0x2b, 0xcb, 0x59, 0xb9, 0xca, 0x50}}},
@@ -1806,7 +1667,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x19, 0xf9, 0x35, 0xaa, 0x59, 0x1a, 0x0c, 0x5c, 0x06, 0xfc, 0x6a, 0x0b, 0x97, 0x53, 0x36, 0xfc, 0x2a, 0xa5, 0x5a, 0x9b, 0x30, 0xef, 0x23, 0xaf, 0x39, 0x5d, 0x9a, 0x6b, 0x75, 0x57, 0x48, 0x0b}}},
{{{0x26, 0xdc, 0x76, 0x3b, 0xfc, 0xf9, 0x9c, 0x3f, 0x89, 0x0b, 0x62, 0x53, 0xaf, 0x83, 0x01, 0x2e, 0xbc, 0x6a, 0xc6, 0x03, 0x0d, 0x75, 0x2a, 0x0d, 0xe6, 0x94, 0x54, 0xcf, 0xb3, 0xe5, 0x96, 0x25}} ,
{{0xfe, 0x82, 0xb1, 0x74, 0x31, 0x8a, 0xa7, 0x6f, 0x56, 0xbd, 0x8d, 0xf4, 0xe0, 0x94, 0x51, 0x59, 0xde, 0x2c, 0x5a, 0xf4, 0x84, 0x6b, 0x4a, 0x88, 0x93, 0xc0, 0x0c, 0x9a, 0xac, 0xa7, 0xa0, 0x68}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x25, 0x0d, 0xd6, 0xc7, 0x23, 0x47, 0x10, 0xad, 0xc7, 0x08, 0x5c, 0x87, 0x87, 0x93, 0x98, 0x18, 0xb8, 0xd3, 0x9c, 0xac, 0x5a, 0x3d, 0xc5, 0x75, 0xf8, 0x49, 0x32, 0x14, 0xcc, 0x51, 0x96, 0x24}} ,
{{0x65, 0x9c, 0x5d, 0xf0, 0x37, 0x04, 0xf0, 0x34, 0x69, 0x2a, 0xf0, 0xa5, 0x64, 0xca, 0xde, 0x2b, 0x5b, 0x15, 0x10, 0xd2, 0xab, 0x06, 0xdd, 0xc4, 0xb0, 0xb6, 0x5b, 0xc1, 0x17, 0xdf, 0x8f, 0x02}}},
@@ -1816,7 +1677,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x00, 0xa9, 0x4c, 0xb2, 0x12, 0xf8, 0x32, 0xa8, 0x7a, 0x00, 0x4b, 0x49, 0x32, 0xba, 0x1f, 0x5d, 0x44, 0x8e, 0x44, 0x7a, 0xdc, 0x11, 0xfb, 0x39, 0x08, 0x57, 0x87, 0xa5, 0x12, 0x42, 0x93, 0x0e}}},
{{{0x17, 0xb4, 0xae, 0x72, 0x59, 0xd0, 0xaa, 0xa8, 0x16, 0x8b, 0x63, 0x11, 0xb3, 0x43, 0x04, 0xda, 0x0c, 0xa8, 0xb7, 0x68, 0xdd, 0x4e, 0x54, 0xe7, 0xaf, 0x5d, 0x5d, 0x05, 0x76, 0x36, 0xec, 0x0d}} ,
{{0x6d, 0x7c, 0x82, 0x32, 0x38, 0x55, 0x57, 0x74, 0x5b, 0x7d, 0xc3, 0xc4, 0xfb, 0x06, 0x29, 0xf0, 0x13, 0x55, 0x54, 0xc6, 0xa7, 0xdc, 0x4c, 0x9f, 0x98, 0x49, 0x20, 0xa8, 0xc3, 0x8d, 0xfa, 0x48}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x87, 0x47, 0x9d, 0xe9, 0x25, 0xd5, 0xe3, 0x47, 0x78, 0xdf, 0x85, 0xa7, 0x85, 0x5e, 0x7a, 0x4c, 0x5f, 0x79, 0x1a, 0xf3, 0xa2, 0xb2, 0x28, 0xa0, 0x9c, 0xdd, 0x30, 0x40, 0xd4, 0x38, 0xbd, 0x28}} ,
{{0xfc, 0xbb, 0xd5, 0x78, 0x6d, 0x1d, 0xd4, 0x99, 0xb4, 0xaa, 0x44, 0x44, 0x7a, 0x1b, 0xd8, 0xfe, 0xb4, 0x99, 0xb9, 0xcc, 0xe7, 0xc4, 0xd3, 0x3a, 0x73, 0x83, 0x41, 0x5c, 0x40, 0xd7, 0x2d, 0x55}}},
@@ -1826,7 +1687,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0xc0, 0x61, 0x48, 0x48, 0x17, 0xf4, 0x9e, 0x18, 0x51, 0x2d, 0xea, 0x2f, 0xf2, 0xf2, 0xe0, 0xa3, 0x14, 0xb7, 0x8b, 0x3a, 0x30, 0xf5, 0x81, 0xc1, 0x5d, 0x71, 0x39, 0x62, 0x55, 0x1f, 0x60, 0x5a}}},
{{{0xe5, 0x89, 0x8a, 0x76, 0x6c, 0xdb, 0x4d, 0x0a, 0x5b, 0x72, 0x9d, 0x59, 0x6e, 0x63, 0x63, 0x18, 0x7c, 0xe3, 0xfa, 0xe2, 0xdb, 0xa1, 0x8d, 0xf4, 0xa5, 0xd7, 0x16, 0xb2, 0xd0, 0xb3, 0x3f, 0x39}} ,
{{0xce, 0x60, 0x09, 0x6c, 0xf5, 0x76, 0x17, 0x24, 0x80, 0x3a, 0x96, 0xc7, 0x94, 0x2e, 0xf7, 0x6b, 0xef, 0xb5, 0x05, 0x96, 0xef, 0xd3, 0x7b, 0x51, 0xda, 0x05, 0x44, 0x67, 0xbc, 0x07, 0x21, 0x4e}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0xe9, 0x73, 0x6f, 0x21, 0xb9, 0xde, 0x22, 0x7d, 0xeb, 0x97, 0x31, 0x10, 0xa3, 0xea, 0xe1, 0xc6, 0x37, 0xeb, 0x8f, 0x43, 0x58, 0xde, 0x41, 0x64, 0x0e, 0x3e, 0x07, 0x99, 0x3d, 0xf1, 0xdf, 0x1e}} ,
{{0xf8, 0xad, 0x43, 0xc2, 0x17, 0x06, 0xe2, 0xe4, 0xa9, 0x86, 0xcd, 0x18, 0xd7, 0x78, 0xc8, 0x74, 0x66, 0xd2, 0x09, 0x18, 0xa5, 0xf1, 0xca, 0xa6, 0x62, 0x92, 0xc1, 0xcb, 0x00, 0xeb, 0x42, 0x2e}}},
@@ -1836,7 +1697,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0xa3, 0x64, 0x17, 0x9b, 0x8b, 0xc8, 0x3a, 0x61, 0xe6, 0x9d, 0xc6, 0xed, 0x7b, 0x03, 0x52, 0x26, 0x9d, 0x3a, 0xb3, 0x13, 0xcc, 0x8a, 0xfd, 0x2c, 0x1a, 0x1d, 0xed, 0x13, 0xd0, 0x55, 0x57, 0x0e}}},
{{{0x1a, 0xea, 0xbf, 0xfd, 0x4a, 0x3c, 0x8e, 0xec, 0x29, 0x7e, 0x77, 0x77, 0x12, 0x99, 0xd7, 0x84, 0xf9, 0x55, 0x7f, 0xf1, 0x8b, 0xb4, 0xd2, 0x95, 0xa3, 0x8d, 0xf0, 0x8a, 0xa7, 0xeb, 0x82, 0x4b}} ,
{{0x2c, 0x28, 0xf4, 0x3a, 0xf6, 0xde, 0x0a, 0xe0, 0x41, 0x44, 0x23, 0xf8, 0x3f, 0x03, 0x64, 0x9f, 0xc3, 0x55, 0x4c, 0xc6, 0xc1, 0x94, 0x1c, 0x24, 0x5d, 0x5f, 0x92, 0x45, 0x96, 0x57, 0x37, 0x14}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0xc1, 0xcd, 0x90, 0x66, 0xb9, 0x76, 0xa0, 0x5b, 0xa5, 0x85, 0x75, 0x23, 0xf9, 0x89, 0xa5, 0x82, 0xb2, 0x6f, 0xb1, 0xeb, 0xc4, 0x69, 0x6f, 0x18, 0x5a, 0xed, 0x94, 0x3d, 0x9d, 0xd9, 0x2c, 0x1a}} ,
{{0x35, 0xb0, 0xe6, 0x73, 0x06, 0xb7, 0x37, 0xe0, 0xf8, 0xb0, 0x22, 0xe8, 0xd2, 0xed, 0x0b, 0xef, 0xe6, 0xc6, 0x5a, 0x99, 0x9e, 0x1a, 0x9f, 0x04, 0x97, 0xe4, 0x4d, 0x0b, 0xbe, 0xba, 0x44, 0x40}}},
@@ -1846,7 +1707,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0xa7, 0xbb, 0xf6, 0x8e, 0xad, 0xdd, 0xf7, 0x90, 0xdd, 0x5f, 0x93, 0x89, 0xae, 0x04, 0x37, 0xe6, 0x9a, 0xb7, 0xe8, 0xc0, 0xdf, 0x16, 0x2a, 0xbf, 0xc4, 0x3a, 0x3c, 0x41, 0xd5, 0x89, 0x72, 0x5a}}},
{{{0x1f, 0x96, 0xff, 0x34, 0x2c, 0x13, 0x21, 0xcb, 0x0a, 0x89, 0x85, 0xbe, 0xb3, 0x70, 0x9e, 0x1e, 0xde, 0x97, 0xaf, 0x96, 0x30, 0xf7, 0x48, 0x89, 0x40, 0x8d, 0x07, 0xf1, 0x25, 0xf0, 0x30, 0x58}} ,
{{0x1e, 0xd4, 0x93, 0x57, 0xe2, 0x17, 0xe7, 0x9d, 0xab, 0x3c, 0x55, 0x03, 0x82, 0x2f, 0x2b, 0xdb, 0x56, 0x1e, 0x30, 0x2e, 0x24, 0x47, 0x6e, 0xe6, 0xff, 0x33, 0x24, 0x2c, 0x75, 0x51, 0xd4, 0x67}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x2b, 0x06, 0xd9, 0xa1, 0x5d, 0xe1, 0xf4, 0xd1, 0x1e, 0x3c, 0x9a, 0xc6, 0x29, 0x2b, 0x13, 0x13, 0x78, 0xc0, 0xd8, 0x16, 0x17, 0x2d, 0x9e, 0xa9, 0xc9, 0x79, 0x57, 0xab, 0x24, 0x91, 0x92, 0x19}} ,
{{0x69, 0xfb, 0xa1, 0x9c, 0xa6, 0x75, 0x49, 0x7d, 0x60, 0x73, 0x40, 0x42, 0xc4, 0x13, 0x0a, 0x95, 0x79, 0x1e, 0x04, 0x83, 0x94, 0x99, 0x9b, 0x1e, 0x0c, 0xe8, 0x1f, 0x54, 0xef, 0xcb, 0xc0, 0x52}}},
@@ -1856,7 +1717,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x9d, 0x45, 0xd6, 0x80, 0xe6, 0x45, 0xaa, 0xf4, 0x15, 0xaa, 0x5c, 0x34, 0x87, 0x99, 0xa2, 0x8c, 0x26, 0x84, 0x62, 0x7d, 0xb6, 0x29, 0xc0, 0x52, 0xea, 0xf5, 0x81, 0x18, 0x0f, 0x35, 0xa9, 0x0e}}},
{{{0xe7, 0x20, 0x72, 0x7c, 0x6d, 0x94, 0x5f, 0x52, 0x44, 0x54, 0xe3, 0xf1, 0xb2, 0xb0, 0x36, 0x46, 0x0f, 0xae, 0x92, 0xe8, 0x70, 0x9d, 0x6e, 0x79, 0xb1, 0xad, 0x37, 0xa9, 0x5f, 0xc0, 0xde, 0x03}} ,
{{0x15, 0x55, 0x37, 0xc6, 0x1c, 0x27, 0x1c, 0x6d, 0x14, 0x4f, 0xca, 0xa4, 0xc4, 0x88, 0x25, 0x46, 0x39, 0xfc, 0x5a, 0xe5, 0xfe, 0x29, 0x11, 0x69, 0xf5, 0x72, 0x84, 0x4d, 0x78, 0x9f, 0x94, 0x15}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0xec, 0xd3, 0xff, 0x57, 0x0b, 0xb0, 0xb2, 0xdc, 0xf8, 0x4f, 0xe2, 0x12, 0xd5, 0x36, 0xbe, 0x6b, 0x09, 0x43, 0x6d, 0xa3, 0x4d, 0x90, 0x2d, 0xb8, 0x74, 0xe8, 0x71, 0x45, 0x19, 0x8b, 0x0c, 0x6a}} ,
{{0xb8, 0x42, 0x1c, 0x03, 0xad, 0x2c, 0x03, 0x8e, 0xac, 0xd7, 0x98, 0x29, 0x13, 0xc6, 0x02, 0x29, 0xb5, 0xd4, 0xe7, 0xcf, 0xcc, 0x8b, 0x83, 0xec, 0x35, 0xc7, 0x9c, 0x74, 0xb7, 0xad, 0x85, 0x5f}}},
@@ -1868,407 +1729,279 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x69, 0x3e, 0x47, 0x97, 0x2c, 0xaf, 0x52, 0x7c, 0x78, 0x83, 0xad, 0x1b, 0x39, 0x82, 0x2f, 0x02, 0x6f, 0x47, 0xdb, 0x2a, 0xb0, 0xe1, 0x91, 0x99, 0x55, 0xb8, 0x99, 0x3a, 0xa0, 0x44, 0x11, 0x51}}}
};
-static inline void p1p1_to_p2(ge25519_p2 *r, const ge25519_p1p1 *p)
+void p1p1_to_p2(ge25519_p2 *r, const ge25519_p1p1 *p)
{
- fe25519_mul(&r->x, &p->x, &p->t);
- fe25519_mul(&r->y, &p->y, &p->z);
- fe25519_mul(&r->z, &p->z, &p->t);
+ fe25519_mul(&r->x, &p->x, &p->t);
+ fe25519_mul(&r->y, &p->y, &p->z);
+ fe25519_mul(&r->z, &p->z, &p->t);
}
-static inline void p1p1_to_p2_2(ge25519_p3 *r, const ge25519_p1p1 *p)
+void p1p1_to_p2_2(ge25519_p3 *r, const ge25519_p1p1 *p)
{
- fe25519_mul(&r->x, &p->x, &p->t);
- fe25519_mul(&r->y, &p->y, &p->z);
- fe25519_mul(&r->z, &p->z, &p->t);
+ fe25519_mul(&r->x, &p->x, &p->t);
+ fe25519_mul(&r->y, &p->y, &p->z);
+ fe25519_mul(&r->z, &p->z, &p->t);
}
-static inline void p1p1_to_p3(ge25519_p3 *r, const ge25519_p1p1 *p)
+void p1p1_to_p3(ge25519_p3 *r, const ge25519_p1p1 *p)
{
- p1p1_to_p2_2(r, p);
- fe25519_mul(&r->t, &p->x, &p->y);
+ p1p1_to_p2_2(r, p);
+ fe25519_mul(&r->t, &p->x, &p->y);
}
-static void ge25519_mixadd2(ge25519_p3 *r, const ge25519_aff *q)
+void ge25519_mixadd2(ge25519_p3 *r, const ge25519_aff *q)
{
- fe25519 a,b,t1,t2,c,d,e,f,g,h,qt;
- fe25519_mul(&qt, &q->x, &q->y);
- fe25519_sub(&a, &r->y, &r->x); /* A = (Y1-X1)*(Y2-X2) */
- fe25519_add(&b, &r->y, &r->x); /* B = (Y1+X1)*(Y2+X2) */
- fe25519_sub(&t1, &q->y, &q->x);
- fe25519_add(&t2, &q->y, &q->x);
- fe25519_mul(&a, &a, &t1);
- fe25519_mul(&b, &b, &t2);
- fe25519_sub(&e, &b, &a); /* E = B-A */
- fe25519_add(&h, &b, &a); /* H = B+A */
- fe25519_mul(&c, &r->t, &qt); /* C = T1*k*T2 */
- fe25519_mul(&c, &c, &ge25519_ec2d);
- fe25519_add(&d, &r->z, &r->z); /* D = Z1*2 */
- fe25519_sub(&f, &d, &c); /* F = D-C */
- fe25519_add(&g, &d, &c); /* G = D+C */
- fe25519_mul(&r->x, &e, &f);
- fe25519_mul(&r->y, &h, &g);
- fe25519_mul(&r->z, &g, &f);
- fe25519_mul(&r->t, &e, &h);
+ fe25519 a,b,t1,t2,c,d,e,f,g,h,qt;
+ fe25519_mul(&qt, &q->x, &q->y);
+ fe25519_sub(&a, &r->y, &r->x); /* A = (Y1-X1)*(Y2-X2) */
+ fe25519_add(&b, &r->y, &r->x); /* B = (Y1+X1)*(Y2+X2) */
+ fe25519_sub(&t1, &q->y, &q->x);
+ fe25519_add(&t2, &q->y, &q->x);
+ fe25519_mul(&a, &a, &t1);
+ fe25519_mul(&b, &b, &t2);
+ fe25519_sub(&e, &b, &a); /* E = B-A */
+ fe25519_add(&h, &b, &a); /* H = B+A */
+ fe25519_mul(&c, &r->t, &qt); /* C = T1*k*T2 */
+ fe25519_mul(&c, &c, &ge25519_ec2d);
+ fe25519_add(&d, &r->z, &r->z); /* D = Z1*2 */
+ fe25519_sub(&f, &d, &c); /* F = D-C */
+ fe25519_add(&g, &d, &c); /* G = D+C */
+ fe25519_mul(&r->x, &e, &f);
+ fe25519_mul(&r->y, &h, &g);
+ fe25519_mul(&r->z, &g, &f);
+ fe25519_mul(&r->t, &e, &h);
}
-static void add_p1p1(ge25519_p1p1 *r, const ge25519_p3 *p, const ge25519_p3 *q)
+void add_p1p1(ge25519_p1p1 *r, const ge25519_p3 *p, const ge25519_p3 *q)
{
- fe25519 a, b, c, d, t;
-
- fe25519_sub(&a, &p->y, &p->x); /* A = (Y1-X1)*(Y2-X2) */
- fe25519_sub(&t, &q->y, &q->x);
- fe25519_mul(&a, &a, &t);
- fe25519_add(&b, &p->x, &p->y); /* B = (Y1+X1)*(Y2+X2) */
- fe25519_add(&t, &q->x, &q->y);
- fe25519_mul(&b, &b, &t);
- fe25519_mul(&c, &p->t, &q->t); /* C = T1*k*T2 */
- fe25519_mul(&c, &c, &ge25519_ec2d);
- fe25519_mul(&d, &p->z, &q->z); /* D = Z1*2*Z2 */
- fe25519_add(&d, &d, &d);
- fe25519_sub(&r->x, &b, &a); /* E = B-A */
- fe25519_sub(&r->t, &d, &c); /* F = D-C */
- fe25519_add(&r->z, &d, &c); /* G = D+C */
- fe25519_add(&r->y, &b, &a); /* H = B+A */
+ fe25519 a, b, c, d, t;
+
+ fe25519_sub(&a, &p->y, &p->x); /* A = (Y1-X1)*(Y2-X2) */
+ fe25519_sub(&t, &q->y, &q->x);
+ fe25519_mul(&a, &a, &t);
+ fe25519_add(&b, &p->x, &p->y); /* B = (Y1+X1)*(Y2+X2) */
+ fe25519_add(&t, &q->x, &q->y);
+ fe25519_mul(&b, &b, &t);
+ fe25519_mul(&c, &p->t, &q->t); /* C = T1*k*T2 */
+ fe25519_mul(&c, &c, &ge25519_ec2d);
+ fe25519_mul(&d, &p->z, &q->z); /* D = Z1*2*Z2 */
+ fe25519_add(&d, &d, &d);
+ fe25519_sub(&r->x, &b, &a); /* E = B-A */
+ fe25519_sub(&r->t, &d, &c); /* F = D-C */
+ fe25519_add(&r->z, &d, &c); /* G = D+C */
+ fe25519_add(&r->y, &b, &a); /* H = B+A */
}
/* See http://www.hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html#doubling-dbl-2008-hwcd */
-static void dbl_p1p1(ge25519_p1p1 *r, const ge25519_p2 *p)
+void dbl_p1p1(ge25519_p1p1 *r, const ge25519_p2 *p)
{
- fe25519 a,b,c,d;
- fe25519_square(&a, &p->x);
- fe25519_square(&b, &p->y);
- fe25519_square(&c, &p->z);
- fe25519_add(&c, &c, &c);
- fe25519_neg(&d, &a);
+ fe25519 a,b,c,d;
+ fe25519_square(&a, &p->x);
+ fe25519_square(&b, &p->y);
+ fe25519_square(&c, &p->z);
+ fe25519_add(&c, &c, &c);
+ fe25519_neg(&d, &a);
- fe25519_add(&r->x, &p->x, &p->y);
- fe25519_square(&r->x, &r->x);
- fe25519_sub(&r->x, &r->x, &a);
- fe25519_sub(&r->x, &r->x, &b);
- fe25519_add(&r->z, &d, &b);
- fe25519_sub(&r->t, &r->z, &c);
- fe25519_sub(&r->y, &d, &b);
+ fe25519_add(&r->x, &p->x, &p->y);
+ fe25519_square(&r->x, &r->x);
+ fe25519_sub(&r->x, &r->x, &a);
+ fe25519_sub(&r->x, &r->x, &b);
+ fe25519_add(&r->z, &d, &b);
+ fe25519_sub(&r->t, &r->z, &c);
+ fe25519_sub(&r->y, &d, &b);
}
/* Constant-time version of: if(b) r = p */
-static inline void cmov_aff(ge25519_aff *r, const ge25519_aff *p, unsigned char b)
+void cmov_aff(ge25519_aff *r, const ge25519_aff *p, unsigned char b)
{
- fe25519_cmov(&r->x, &p->x, b);
- fe25519_cmov(&r->y, &p->y, b);
+ fe25519_cmov(&r->x, &p->x, b);
+ fe25519_cmov(&r->y, &p->y, b);
}
-static inline unsigned char equal(signed char b,signed char c)
+unsigned char equal(signed char b,signed char c)
{
- unsigned char ub = b;
- unsigned char uc = c;
- unsigned char x = ub ^ uc; /* 0: yes; 1..255: no */
- crypto_uint32 y = x; /* 0: yes; 1..255: no */
- y -= 1; /* 4294967295: yes; 0..254: no */
- y >>= 31; /* 1: yes; 0: no */
- return (unsigned char)y;
+ unsigned char ub = b;
+ unsigned char uc = c;
+ unsigned char x = ub ^ uc; /* 0: yes; 1..255: no */
+ crypto_uint32 y = x; /* 0: yes; 1..255: no */
+ y -= 1; /* 4294967295: yes; 0..254: no */
+ y >>= 31; /* 1: yes; 0: no */
+ return (unsigned char)y;
}
-static inline unsigned char negative(signed char b)
+unsigned char negative(signed char b)
{
- unsigned long long x = b; /* 18446744073709551361..18446744073709551615: yes; 0..255: no */
- x >>= 63; /* 1: yes; 0: no */
- return (unsigned char)x;
+ unsigned long long x = b; /* 18446744073709551361..18446744073709551615: yes; 0..255: no */
+ x >>= 63; /* 1: yes; 0: no */
+ return (unsigned char)x;
}
-static inline void choose_t(ge25519_aff *t, unsigned long long pos, signed char b)
+void choose_t(ge25519_aff *t, unsigned long long pos, signed char b)
{
- /* constant time */
- fe25519 v;
- *t = ge25519_base_multiples_affine[5*pos+0];
- cmov_aff(t, &ge25519_base_multiples_affine[5*pos+1],equal(b,1) | equal(b,-1));
- cmov_aff(t, &ge25519_base_multiples_affine[5*pos+2],equal(b,2) | equal(b,-2));
- cmov_aff(t, &ge25519_base_multiples_affine[5*pos+3],equal(b,3) | equal(b,-3));
- cmov_aff(t, &ge25519_base_multiples_affine[5*pos+4],equal(b,-4));
- fe25519_neg(&v, &t->x);
- fe25519_cmov(&t->x, &v, negative(b));
+ /* constant time */
+ fe25519 v;
+ *t = ge25519_base_multiples_affine[5*pos+0];
+ cmov_aff(t, &ge25519_base_multiples_affine[5*pos+1],equal(b,1) | equal(b,-1));
+ cmov_aff(t, &ge25519_base_multiples_affine[5*pos+2],equal(b,2) | equal(b,-2));
+ cmov_aff(t, &ge25519_base_multiples_affine[5*pos+3],equal(b,3) | equal(b,-3));
+ cmov_aff(t, &ge25519_base_multiples_affine[5*pos+4],equal(b,-4));
+ fe25519_neg(&v, &t->x);
+ fe25519_cmov(&t->x, &v, negative(b));
}
-static inline void setneutral(ge25519 *r)
+void setneutral(ge25519 *r)
{
- fe25519_setzero(&r->x);
- fe25519_setone(&r->y);
- fe25519_setone(&r->z);
- fe25519_setzero(&r->t);
+ fe25519_setzero(&r->x);
+ fe25519_setone(&r->y);
+ fe25519_setone(&r->z);
+ fe25519_setzero(&r->t);
}
/* return 0 on success, -1 otherwise */
-static int ge25519_unpackneg_vartime(ge25519_p3 *r, const unsigned char p[32])
-{
- unsigned char par;
- fe25519 t, chk, num, den, den2, den4, den6;
- fe25519_setone(&r->z);
- par = p[31] >> 7;
- fe25519_unpack(&r->y, p);
- fe25519_square(&num, &r->y); /* x = y^2 */
- fe25519_mul(&den, &num, &ge25519_ecd); /* den = dy^2 */
- fe25519_sub(&num, &num, &r->z); /* x = y^2-1 */
- fe25519_add(&den, &r->z, &den); /* den = dy^2+1 */
-
- /* Computation of sqrt(num/den) */
- /* 1.: computation of num^((p-5)/8)*den^((7p-35)/8) = (num*den^7)^((p-5)/8) */
- fe25519_square(&den2, &den);
- fe25519_square(&den4, &den2);
- fe25519_mul(&den6, &den4, &den2);
- fe25519_mul(&t, &den6, &num);
- fe25519_mul(&t, &t, &den);
-
- fe25519_pow2523(&t, &t);
- /* 2. computation of r->x = t * num * den^3 */
- fe25519_mul(&t, &t, &num);
- fe25519_mul(&t, &t, &den);
- fe25519_mul(&t, &t, &den);
- fe25519_mul(&r->x, &t, &den);
-
- /* 3. Check whether sqrt computation gave correct result, multiply by sqrt(-1) if not: */
- fe25519_square(&chk, &r->x);
- fe25519_mul(&chk, &chk, &den);
- if (!fe25519_iseq_vartime(&chk, &num))
- fe25519_mul(&r->x, &r->x, &ge25519_sqrtm1);
-
- /* 4. Now we have one of the two square roots, except if input was not a square */
- fe25519_square(&chk, &r->x);
- fe25519_mul(&chk, &chk, &den);
- if (!fe25519_iseq_vartime(&chk, &num))
- return -1;
-
- /* 5. Choose the desired square root according to parity: */
- if(fe25519_getparity(&r->x) != (1-par))
- fe25519_neg(&r->x, &r->x);
-
- fe25519_mul(&r->t, &r->x, &r->y);
- return 0;
-}
-
-static inline void ge25519_pack(unsigned char r[32], const ge25519_p3 *p)
-{
- fe25519 tx, ty, zi;
- fe25519_invert(&zi, &p->z);
- fe25519_mul(&tx, &p->x, &zi);
- fe25519_mul(&ty, &p->y, &zi);
- fe25519_pack(r, &ty);
- r[31] ^= fe25519_getparity(&tx) << 7;
-}
-
-#if 0
-static int ge25519_isneutral_vartime(const ge25519_p3 *p)
-{
- int ret = 1;
- if(!fe25519_iszero(&p->x)) ret = 0;
- if(!fe25519_iseq_vartime(&p->y, &p->z)) ret = 0;
- return ret;
+int ge25519_unpackneg_vartime(ge25519_p3 *r, const unsigned char p[32])
+{
+ unsigned char par;
+ fe25519 t, chk, num, den, den2, den4, den6;
+ fe25519_setone(&r->z);
+ par = p[31] >> 7;
+ fe25519_unpack(&r->y, p);
+ fe25519_square(&num, &r->y); /* x = y^2 */
+ fe25519_mul(&den, &num, &ge25519_ecd); /* den = dy^2 */
+ fe25519_sub(&num, &num, &r->z); /* x = y^2-1 */
+ fe25519_add(&den, &r->z, &den); /* den = dy^2+1 */
+
+ /* Computation of sqrt(num/den) */
+ /* 1.: computation of num^((p-5)/8)*den^((7p-35)/8) = (num*den^7)^((p-5)/8) */
+ fe25519_square(&den2, &den);
+ fe25519_square(&den4, &den2);
+ fe25519_mul(&den6, &den4, &den2);
+ fe25519_mul(&t, &den6, &num);
+ fe25519_mul(&t, &t, &den);
+
+ fe25519_pow2523(&t, &t);
+ /* 2. computation of r->x = t * num * den^3 */
+ fe25519_mul(&t, &t, &num);
+ fe25519_mul(&t, &t, &den);
+ fe25519_mul(&t, &t, &den);
+ fe25519_mul(&r->x, &t, &den);
+
+ /* 3. Check whether sqrt computation gave correct result, multiply by sqrt(-1) if not: */
+ fe25519_square(&chk, &r->x);
+ fe25519_mul(&chk, &chk, &den);
+ if (!fe25519_iseq_vartime(&chk, &num))
+ fe25519_mul(&r->x, &r->x, &ge25519_sqrtm1);
+
+ /* 4. Now we have one of the two square roots, except if input was not a square */
+ fe25519_square(&chk, &r->x);
+ fe25519_mul(&chk, &chk, &den);
+ if (!fe25519_iseq_vartime(&chk, &num))
+ return -1;
+
+ /* 5. Choose the desired square root according to parity: */
+ if(fe25519_getparity(&r->x) != (1-par))
+ fe25519_neg(&r->x, &r->x);
+
+ fe25519_mul(&r->t, &r->x, &r->y);
+ return 0;
+}
+
+void ge25519_pack(unsigned char r[32], const ge25519_p3 *p)
+{
+ fe25519 tx, ty, zi;
+ fe25519_invert(&zi, &p->z);
+ fe25519_mul(&tx, &p->x, &zi);
+ fe25519_mul(&ty, &p->y, &zi);
+ fe25519_pack(r, &ty);
+ r[31] ^= fe25519_getparity(&tx) << 7;
}
-#endif
/* computes [s1]p1 + [s2]p2 */
-static void ge25519_double_scalarmult_vartime(ge25519_p3 *r, const ge25519_p3 *p1, const sc25519 *s1, const ge25519_p3 *p2, const sc25519 *s2)
-{
- ge25519_p1p1 tp1p1;
- ge25519_p3 pre[16];
- char *pre5 = (char *)(&(pre[5])); // eliminate type punning warning
- unsigned char b[127];
- int i;
-
- /* precomputation s2 s1 */
- setneutral(pre); /* 00 00 */
- pre[1] = *p1; /* 00 01 */
- dbl_p1p1(&tp1p1,(ge25519_p2 *)p1); p1p1_to_p3( &pre[2], &tp1p1); /* 00 10 */
- add_p1p1(&tp1p1,&pre[1], &pre[2]); p1p1_to_p3( &pre[3], &tp1p1); /* 00 11 */
- pre[4] = *p2; /* 01 00 */
- add_p1p1(&tp1p1,&pre[1], &pre[4]); p1p1_to_p3( &pre[5], &tp1p1); /* 01 01 */
- add_p1p1(&tp1p1,&pre[2], &pre[4]); p1p1_to_p3( &pre[6], &tp1p1); /* 01 10 */
- add_p1p1(&tp1p1,&pre[3], &pre[4]); p1p1_to_p3( &pre[7], &tp1p1); /* 01 11 */
- dbl_p1p1(&tp1p1,(ge25519_p2 *)p2); p1p1_to_p3( &pre[8], &tp1p1); /* 10 00 */
- add_p1p1(&tp1p1,&pre[1], &pre[8]); p1p1_to_p3( &pre[9], &tp1p1); /* 10 01 */
- dbl_p1p1(&tp1p1,(ge25519_p2 *)pre5); p1p1_to_p3(&pre[10], &tp1p1); /* 10 10 */
- add_p1p1(&tp1p1,&pre[3], &pre[8]); p1p1_to_p3(&pre[11], &tp1p1); /* 10 11 */
- add_p1p1(&tp1p1,&pre[4], &pre[8]); p1p1_to_p3(&pre[12], &tp1p1); /* 11 00 */
- add_p1p1(&tp1p1,&pre[1],&pre[12]); p1p1_to_p3(&pre[13], &tp1p1); /* 11 01 */
- add_p1p1(&tp1p1,&pre[2],&pre[12]); p1p1_to_p3(&pre[14], &tp1p1); /* 11 10 */
- add_p1p1(&tp1p1,&pre[3],&pre[12]); p1p1_to_p3(&pre[15], &tp1p1); /* 11 11 */
-
- sc25519_2interleave2(b,s1,s2);
-
- /* scalar multiplication */
- *r = pre[b[126]];
- for(i=125;i>=0;i--)
- {
- dbl_p1p1(&tp1p1, (ge25519_p2 *)r);
- p1p1_to_p2((ge25519_p2 *) r, &tp1p1);
- dbl_p1p1(&tp1p1, (ge25519_p2 *)r);
- if(b[i]!=0)
- {
- p1p1_to_p3(r, &tp1p1);
- add_p1p1(&tp1p1, r, &pre[b[i]]);
- }
- if(i != 0) p1p1_to_p2((ge25519_p2 *)r, &tp1p1);
- else p1p1_to_p3(r, &tp1p1);
- }
-}
-
-static inline void ge25519_scalarmult_base(ge25519_p3 *r, const sc25519 *s)
-{
- signed char b[85];
- int i;
- ge25519_aff t;
- sc25519_window3(b,s);
-
- choose_t((ge25519_aff *)r, 0, b[0]);
- fe25519_setone(&r->z);
- fe25519_mul(&r->t, &r->x, &r->y);
- for(i=1;i<85;i++)
- {
- choose_t(&t, (unsigned long long) i, b[i]);
- ge25519_mixadd2(r, &t);
- }
-}
-
-static inline void get_hram(unsigned char *hram, const unsigned char *sm, const unsigned char *pk, unsigned char *playground, unsigned long long smlen)
-{
- unsigned long long i;
-
- for (i = 0;i < 32;++i) playground[i] = sm[i];
- for (i = 32;i < 64;++i) playground[i] = pk[i-32];
- for (i = 64;i < smlen;++i) playground[i] = sm[i];
-
- //crypto_hash_sha512(hram,playground,smlen);
- SHA512::hash(hram,playground,(unsigned int)smlen);
-}
-
-// This is the original sign and verify code -- the versions in sign() and
-// verify() below the fold are slightly modified in terms of how they behave
-// in relation to the message, but the algorithms are the same.
-
-#if 0
-int crypto_sign_keypair(
- unsigned char *pk,
- unsigned char *sk
- )
-{
- sc25519 scsk;
- ge25519 gepk;
- unsigned char extsk[64];
- int i;
-
- randombytes(sk, 32);
- crypto_hash_sha512(extsk, sk, 32);
- extsk[0] &= 248;
- extsk[31] &= 127;
- extsk[31] |= 64;
-
- sc25519_from32bytes(&scsk,extsk);
-
- ge25519_scalarmult_base(&gepk, &scsk);
- ge25519_pack(pk, &gepk);
- for(i=0;i<32;i++)
- sk[32 + i] = pk[i];
- return 0;
-}
-
-static int crypto_sign(
- unsigned char *sm,unsigned long long *smlen,
- const unsigned char *m,unsigned long long mlen,
- const unsigned char *sk
- )
-{
- sc25519 sck, scs, scsk;
- ge25519 ger;
- unsigned char r[32];
- unsigned char s[32];
- unsigned char extsk[64];
- unsigned long long i;
- unsigned char hmg[crypto_hash_sha512_BYTES];
- unsigned char hram[crypto_hash_sha512_BYTES];
-
- crypto_hash_sha512(extsk, sk, 32);
- extsk[0] &= 248;
- extsk[31] &= 127;
- extsk[31] |= 64;
-
- *smlen = mlen+64;
- for(i=0;i<mlen;i++)
- sm[64 + i] = m[i];
- for(i=0;i<32;i++)
- sm[32 + i] = extsk[32+i];
-
- crypto_hash_sha512(hmg, sm+32, mlen+32); /* Generate k as h(extsk[32],...,extsk[63],m) */
-
- /* Computation of R */
- sc25519_from64bytes(&sck, hmg);
- ge25519_scalarmult_base(&ger, &sck);
- ge25519_pack(r, &ger);
-
- /* Computation of s */
- for(i=0;i<32;i++)
- sm[i] = r[i];
+void ge25519_double_scalarmult_vartime(ge25519_p3 *r, const ge25519_p3 *p1, const sc25519 *s1, const ge25519_p3 *p2, const sc25519 *s2)
+{
+ ge25519_p1p1 tp1p1;
+ ge25519_p3 pre[16];
+ char *pre5 = (char *)(&(pre[5])); // eliminate type punning warning
+ unsigned char b[127];
+ int i;
+
+ /* precomputation s2 s1 */
+ setneutral(pre); /* 00 00 */
+ pre[1] = *p1; /* 00 01 */
+ dbl_p1p1(&tp1p1,(ge25519_p2 *)p1); p1p1_to_p3( &pre[2], &tp1p1); /* 00 10 */
+ add_p1p1(&tp1p1,&pre[1], &pre[2]); p1p1_to_p3( &pre[3], &tp1p1); /* 00 11 */
+ pre[4] = *p2; /* 01 00 */
+ add_p1p1(&tp1p1,&pre[1], &pre[4]); p1p1_to_p3( &pre[5], &tp1p1); /* 01 01 */
+ add_p1p1(&tp1p1,&pre[2], &pre[4]); p1p1_to_p3( &pre[6], &tp1p1); /* 01 10 */
+ add_p1p1(&tp1p1,&pre[3], &pre[4]); p1p1_to_p3( &pre[7], &tp1p1); /* 01 11 */
+ dbl_p1p1(&tp1p1,(ge25519_p2 *)p2); p1p1_to_p3( &pre[8], &tp1p1); /* 10 00 */
+ add_p1p1(&tp1p1,&pre[1], &pre[8]); p1p1_to_p3( &pre[9], &tp1p1); /* 10 01 */
+ dbl_p1p1(&tp1p1,(ge25519_p2 *)pre5); p1p1_to_p3(&pre[10], &tp1p1); /* 10 10 */
+ add_p1p1(&tp1p1,&pre[3], &pre[8]); p1p1_to_p3(&pre[11], &tp1p1); /* 10 11 */
+ add_p1p1(&tp1p1,&pre[4], &pre[8]); p1p1_to_p3(&pre[12], &tp1p1); /* 11 00 */
+ add_p1p1(&tp1p1,&pre[1],&pre[12]); p1p1_to_p3(&pre[13], &tp1p1); /* 11 01 */
+ add_p1p1(&tp1p1,&pre[2],&pre[12]); p1p1_to_p3(&pre[14], &tp1p1); /* 11 10 */
+ add_p1p1(&tp1p1,&pre[3],&pre[12]); p1p1_to_p3(&pre[15], &tp1p1); /* 11 11 */
+
+ sc25519_2interleave2(b,s1,s2);
+
+ /* scalar multiplication */
+ *r = pre[b[126]];
+ for(i=125;i>=0;i--)
+ {
+ dbl_p1p1(&tp1p1, (ge25519_p2 *)r);
+ p1p1_to_p2((ge25519_p2 *) r, &tp1p1);
+ dbl_p1p1(&tp1p1, (ge25519_p2 *)r);
+ if(b[i]!=0)
+ {
+ p1p1_to_p3(r, &tp1p1);
+ add_p1p1(&tp1p1, r, &pre[b[i]]);
+ }
+ if(i != 0) p1p1_to_p2((ge25519_p2 *)r, &tp1p1);
+ else p1p1_to_p3(r, &tp1p1);
+ }
+}
- get_hram(hram, sm, sk+32, sm, mlen+64);
+void ge25519_scalarmult_base(ge25519_p3 *r, const sc25519 *s)
+{
+ signed char b[85];
+ int i;
+ ge25519_aff t;
+ sc25519_window3(b,s);
+
+ choose_t((ge25519_aff *)r, 0, b[0]);
+ fe25519_setone(&r->z);
+ fe25519_mul(&r->t, &r->x, &r->y);
+ for(i=1;i<85;i++)
+ {
+ choose_t(&t, (unsigned long long) i, b[i]);
+ ge25519_mixadd2(r, &t);
+ }
+}
- sc25519_from64bytes(&scs, hram);
- sc25519_from32bytes(&scsk, extsk);
- sc25519_mul(&scs, &scs, &scsk);
-
- sc25519_add(&scs, &scs, &sck);
-
- sc25519_to32bytes(s,&scs); /* cat s */
- for(i=0;i<32;i++)
- sm[32 + i] = s[i];
-
- return 0;
-}
-
-static int crypto_sign_open(
- unsigned char *m,unsigned long long *mlen,
- const unsigned char *sm,unsigned long long smlen,
- const unsigned char *pk
- )
+void get_hram(unsigned char *hram, const unsigned char *sm, const unsigned char *pk, unsigned char *playground, unsigned long long smlen)
{
- int i, ret;
- unsigned char t2[32];
- ge25519 get1, get2;
- sc25519 schram, scs;
- unsigned char hram[crypto_hash_sha512_BYTES];
-
- *mlen = (unsigned long long) -1;
- if (smlen < 64) return -1;
-
- if (ge25519_unpackneg_vartime(&get1, pk)) return -1;
-
- get_hram(hram,sm,pk,m,smlen);
-
- sc25519_from64bytes(&schram, hram);
-
- sc25519_from32bytes(&scs, sm+32);
-
- ge25519_double_scalarmult_vartime(&get2, &get1, &schram, &ge25519_base, &scs);
- ge25519_pack(t2, &get2);
-
- ret = crypto_verify_32(sm, t2);
-
- if (!ret)
- {
- for(i=0;i<smlen-64;i++)
- m[i] = sm[i + 64];
- *mlen = smlen-64;
- }
- else
- {
- for(i=0;i<smlen-64;i++)
- m[i] = 0;
- }
- return ret;
-}
-#endif // 0
+ unsigned long long i;
+
+ for (i = 0;i < 32;++i) playground[i] = sm[i];
+ for (i = 32;i < 64;++i) playground[i] = pk[i-32];
+ for (i = 64;i < smlen;++i) playground[i] = sm[i];
+
+ //crypto_hash_sha512(hram,playground,smlen);
+ ZeroTier::SHA512::hash(hram,playground,(unsigned int)smlen);
+}
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
+} // anonymous namespace
+
+#ifdef ZT_USE_FAST_X64_ED25519
+extern "C" void ed25519_amd64_asm_sign(const unsigned char *sk,const unsigned char *pk,const unsigned char *m,const unsigned int mlen,unsigned char *sig);
+#endif
+
+namespace ZeroTier {
+
void C25519::agree(const C25519::Private &mine,const C25519::Public &their,void *keybuf,unsigned int keylen)
- throw()
{
unsigned char rawkey[32];
unsigned char digest[64];
@@ -2285,114 +2018,113 @@ void C25519::agree(const C25519::Private &mine,const C25519::Public &their,void
}
void C25519::sign(const C25519::Private &myPrivate,const C25519::Public &myPublic,const void *msg,unsigned int len,void *signature)
- throw()
-{
- sc25519 sck, scs, scsk;
- ge25519 ger;
- unsigned char r[32];
- unsigned char s[32];
- unsigned char extsk[64];
- unsigned char hmg[crypto_hash_sha512_BYTES];
- unsigned char hram[crypto_hash_sha512_BYTES];
- unsigned char *sig = (unsigned char *)signature;
- unsigned char digest[64]; // we sign the first 32 bytes of SHA-512(msg)
-
- SHA512::hash(digest,msg,len);
-
- SHA512::hash(extsk,myPrivate.data + 32,32);
- extsk[0] &= 248;
- extsk[31] &= 127;
- extsk[31] |= 64;
-
- for(unsigned int i=0;i<32;i++)
- sig[32 + i] = extsk[32 + i];
- for(unsigned int i=0;i<32;i++)
- sig[64 + i] = digest[i];
-
- SHA512::hash(hmg,sig + 32,64);
- //crypto_hash_sha512(hmg, sm+32, mlen+32); /* Generate k as h(extsk[32],...,extsk[63],m) */
-
- /* Computation of R */
- sc25519_from64bytes(&sck, hmg);
- ge25519_scalarmult_base(&ger, &sck);
- ge25519_pack(r, &ger);
-
- /* Computation of s */
- for(unsigned int i=0;i<32;i++)
- sig[i] = r[i];
-
- get_hram(hram,sig,myPublic.data + 32,sig,96);
-
- sc25519_from64bytes(&scs, hram);
- sc25519_from32bytes(&scsk, extsk);
- sc25519_mul(&scs, &scs, &scsk);
-
- sc25519_add(&scs, &scs, &sck);
-
- sc25519_to32bytes(s,&scs); /* cat s */
- for(unsigned int i=0;i<32;i++)
- sig[32 + i] = s[i];
+{
+#ifdef ZT_USE_FAST_X64_ED25519
+ ed25519_amd64_asm_sign(myPrivate.data + 32,myPublic.data + 32,(const unsigned char *)msg,len,(unsigned char *)signature);
+#else
+ sc25519 sck, scs, scsk;
+ ge25519 ger;
+ unsigned char r[32];
+ unsigned char s[32];
+ unsigned char extsk[64];
+ unsigned char hmg[crypto_hash_sha512_BYTES];
+ unsigned char hram[crypto_hash_sha512_BYTES];
+ unsigned char *sig = (unsigned char *)signature;
+ unsigned char digest[64]; // we sign the first 32 bytes of SHA-512(msg)
+
+ SHA512::hash(digest,msg,len);
+
+ SHA512::hash(extsk,myPrivate.data + 32,32);
+ extsk[0] &= 248;
+ extsk[31] &= 127;
+ extsk[31] |= 64;
+
+ for(unsigned int i=0;i<32;i++)
+ sig[32 + i] = extsk[32 + i];
+ for(unsigned int i=0;i<32;i++)
+ sig[64 + i] = digest[i];
+
+ SHA512::hash(hmg,sig + 32,64);
+
+ /* Computation of R */
+ sc25519_from64bytes(&sck, hmg);
+ ge25519_scalarmult_base(&ger, &sck);
+ ge25519_pack(r, &ger);
+
+ /* Computation of s */
+ for(unsigned int i=0;i<32;i++)
+ sig[i] = r[i];
+
+ get_hram(hram,sig,myPublic.data + 32,sig,96);
+
+ sc25519_from64bytes(&scs, hram);
+ sc25519_from32bytes(&scsk, extsk);
+ sc25519_mul(&scs, &scs, &scsk);
+
+ sc25519_add(&scs, &scs, &sck);
+
+ sc25519_to32bytes(s,&scs); /* cat s */
+ for(unsigned int i=0;i<32;i++)
+ sig[32 + i] = s[i];
+#endif
}
bool C25519::verify(const C25519::Public &their,const void *msg,unsigned int len,const void *signature)
- throw()
{
- unsigned char t2[32];
- ge25519 get1, get2;
- sc25519 schram, scs;
- unsigned char hram[crypto_hash_sha512_BYTES];
- unsigned char m[96];
- unsigned char digest[64]; // we sign the first 32 bytes of SHA-512(msg)
- const unsigned char *sig = (const unsigned char *)signature;
+ unsigned char t2[32];
+ ge25519 get1, get2;
+ sc25519 schram, scs;
+ unsigned char hram[crypto_hash_sha512_BYTES];
+ unsigned char m[96];
+ unsigned char digest[64]; // we sign the first 32 bytes of SHA-512(msg)
+ const unsigned char *sig = (const unsigned char *)signature;
- // First check the message's integrity
- SHA512::hash(digest,msg,len);
- if (!Utils::secureEq(sig + 64,digest,32))
- return false;
+ // First check the message's integrity
+ SHA512::hash(digest,msg,len);
+ if (!Utils::secureEq(sig + 64,digest,32))
+ return false;
- if (ge25519_unpackneg_vartime(&get1,their.data + 32))
- return false;
+ if (ge25519_unpackneg_vartime(&get1,their.data + 32))
+ return false;
- get_hram(hram,sig,their.data + 32,m,96);
+ get_hram(hram,sig,their.data + 32,m,96);
- sc25519_from64bytes(&schram, hram);
+ sc25519_from64bytes(&schram, hram);
- sc25519_from32bytes(&scs, sig+32);
+ sc25519_from32bytes(&scs, sig+32);
- ge25519_double_scalarmult_vartime(&get2, &get1, &schram, &ge25519_base, &scs);
- ge25519_pack(t2, &get2);
+ ge25519_double_scalarmult_vartime(&get2, &get1, &schram, &ge25519_base, &scs);
+ ge25519_pack(t2, &get2);
- return Utils::secureEq(sig,t2,32);
+ return Utils::secureEq(sig,t2,32);
}
void C25519::_calcPubDH(C25519::Pair &kp)
- throw()
{
- // First 32 bytes of pub and priv are the keys for ECDH key
- // agreement. This generates the public portion from the private.
- crypto_scalarmult_base(kp.pub.data,kp.priv.data);
+ // First 32 bytes of pub and priv are the keys for ECDH key
+ // agreement. This generates the public portion from the private.
+ crypto_scalarmult_base(kp.pub.data,kp.priv.data);
}
void C25519::_calcPubED(C25519::Pair &kp)
- throw()
-{
- unsigned char extsk[64];
- sc25519 scsk;
- ge25519 gepk;
-
- // Second 32 bytes of pub and priv are the keys for ed25519
- // signing and verification.
- SHA512::hash(extsk,kp.priv.data + 32,32);
- extsk[0] &= 248;
- extsk[31] &= 127;
- extsk[31] |= 64;
- sc25519_from32bytes(&scsk,extsk);
- ge25519_scalarmult_base(&gepk,&scsk);
- ge25519_pack(kp.pub.data + 32,&gepk);
- // In NaCl, the public key is crammed into the next 32 bytes
- // of the private key for signing since both keys are required
- // to sign. In this version we just get it from kp.pub, so we
- // leave that out of private.
+{
+ unsigned char extsk[64];
+ sc25519 scsk;
+ ge25519 gepk;
+
+ // Second 32 bytes of pub and priv are the keys for ed25519
+ // signing and verification.
+ SHA512::hash(extsk,kp.priv.data + 32,32);
+ extsk[0] &= 248;
+ extsk[31] &= 127;
+ extsk[31] |= 64;
+ sc25519_from32bytes(&scsk,extsk);
+ ge25519_scalarmult_base(&gepk,&scsk);
+ ge25519_pack(kp.pub.data + 32,&gepk);
+ // In NaCl, the public key is crammed into the next 32 bytes
+ // of the private key for signing since both keys are required
+ // to sign. In this version we just get it from kp.pub, so we
+ // leave that out of private.
}
} // namespace ZeroTier
diff --git a/node/C25519.hpp b/node/C25519.hpp
index b19d9693..2e9184a0 100644
--- a/node/C25519.hpp
+++ b/node/C25519.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,12 +14,19 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_C25519_HPP
#define ZT_C25519_HPP
-#include "Array.hpp"
#include "Utils.hpp"
namespace ZeroTier {
@@ -34,37 +41,18 @@ namespace ZeroTier {
class C25519
{
public:
- /**
- * Public key (both crypto and signing)
- */
- typedef Array<unsigned char,ZT_C25519_PUBLIC_KEY_LEN> Public; // crypto key, signing key (both 32 bytes)
-
- /**
- * Private key (both crypto and signing)
- */
- typedef Array<unsigned char,ZT_C25519_PRIVATE_KEY_LEN> Private; // crypto key, signing key (both 32 bytes)
-
- /**
- * Message signature
- */
- typedef Array<unsigned char,ZT_C25519_SIGNATURE_LEN> Signature;
-
- /**
- * Public/private key pair
- */
- typedef struct {
- Public pub;
- Private priv;
- } Pair;
+ struct Public { uint8_t data[ZT_C25519_PUBLIC_KEY_LEN]; };
+ struct Private { uint8_t data[ZT_C25519_PRIVATE_KEY_LEN]; };
+ struct Signature { uint8_t data[ZT_C25519_SIGNATURE_LEN]; };
+ struct Pair { Public pub; Private priv; };
/**
* Generate a C25519 elliptic curve key pair
*/
static inline Pair generate()
- throw()
{
Pair kp;
- Utils::getSecureRandom(kp.priv.data,(unsigned int)kp.priv.size());
+ Utils::getSecureRandom(kp.priv.data,ZT_C25519_PRIVATE_KEY_LEN);
_calcPubDH(kp);
_calcPubED(kp);
return kp;
@@ -85,11 +73,10 @@ public:
*/
template<typename F>
static inline Pair generateSatisfying(F cond)
- throw()
{
Pair kp;
void *const priv = (void *)kp.priv.data;
- Utils::getSecureRandom(priv,(unsigned int)kp.priv.size());
+ Utils::getSecureRandom(priv,ZT_C25519_PRIVATE_KEY_LEN);
_calcPubED(kp); // do Ed25519 key -- bytes 32-63 of pub and priv
do {
++(((uint64_t *)priv)[1]);
@@ -110,13 +97,8 @@ public:
* @param keybuf Buffer to fill
* @param keylen Number of key bytes to generate
*/
- static void agree(const Private &mine,const Public &their,void *keybuf,unsigned int keylen)
- throw();
- static inline void agree(const Pair &mine,const Public &their,void *keybuf,unsigned int keylen)
- throw()
- {
- agree(mine.priv,their,keybuf,keylen);
- }
+ static void agree(const Private &mine,const Public &their,void *keybuf,unsigned int keylen);
+ static inline void agree(const Pair &mine,const Public &their,void *keybuf,unsigned int keylen) { agree(mine.priv,their,keybuf,keylen); }
/**
* Sign a message with a sender's key pair
@@ -137,13 +119,8 @@ public:
* @param len Length of message in bytes
* @param signature Buffer to fill with signature -- MUST be 96 bytes in length
*/
- static void sign(const Private &myPrivate,const Public &myPublic,const void *msg,unsigned int len,void *signature)
- throw();
- static inline void sign(const Pair &mine,const void *msg,unsigned int len,void *signature)
- throw()
- {
- sign(mine.priv,mine.pub,msg,len,signature);
- }
+ static void sign(const Private &myPrivate,const Public &myPublic,const void *msg,unsigned int len,void *signature);
+ static inline void sign(const Pair &mine,const void *msg,unsigned int len,void *signature) { sign(mine.priv,mine.pub,msg,len,signature); }
/**
* Sign a message with a sender's key pair
@@ -155,14 +132,12 @@ public:
* @return Signature
*/
static inline Signature sign(const Private &myPrivate,const Public &myPublic,const void *msg,unsigned int len)
- throw()
{
Signature sig;
sign(myPrivate,myPublic,msg,len,sig.data);
return sig;
}
static inline Signature sign(const Pair &mine,const void *msg,unsigned int len)
- throw()
{
Signature sig;
sign(mine.priv,mine.pub,msg,len,sig.data);
@@ -178,8 +153,7 @@ public:
* @param signature 96-byte signature
* @return True if signature is valid and the message is authentic and unmodified
*/
- static bool verify(const Public &their,const void *msg,unsigned int len,const void *signature)
- throw();
+ static bool verify(const Public &their,const void *msg,unsigned int len,const void *signature);
/**
* Verify a message's signature
@@ -191,7 +165,6 @@ public:
* @return True if signature is valid and the message is authentic and unmodified
*/
static inline bool verify(const Public &their,const void *msg,unsigned int len,const Signature &signature)
- throw()
{
return verify(their,msg,len,signature.data);
}
@@ -199,13 +172,11 @@ public:
private:
// derive first 32 bytes of kp.pub from first 32 bytes of kp.priv
// this is the ECDH key
- static void _calcPubDH(Pair &kp)
- throw();
+ static void _calcPubDH(Pair &kp);
// derive 2nd 32 bytes of kp.pub from 2nd 32 bytes of kp.priv
// this is the Ed25519 sign/verify key
- static void _calcPubED(Pair &kp)
- throw();
+ static void _calcPubED(Pair &kp);
};
} // namespace ZeroTier
diff --git a/node/Capability.cpp b/node/Capability.cpp
new file mode 100644
index 00000000..fb52be8a
--- /dev/null
+++ b/node/Capability.cpp
@@ -0,0 +1,74 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
+ */
+
+#include "Capability.hpp"
+#include "RuntimeEnvironment.hpp"
+#include "Identity.hpp"
+#include "Topology.hpp"
+#include "Switch.hpp"
+#include "Network.hpp"
+#include "Node.hpp"
+
+namespace ZeroTier {
+
+int Capability::verify(const RuntimeEnvironment *RR,void *tPtr) const
+{
+ try {
+ // There must be at least one entry, and sanity check for bad chain max length
+ if ((_maxCustodyChainLength < 1)||(_maxCustodyChainLength > ZT_MAX_CAPABILITY_CUSTODY_CHAIN_LENGTH))
+ return -1;
+
+ // Validate all entries in chain of custody
+ Buffer<(sizeof(Capability) * 2)> tmp;
+ this->serialize(tmp,true);
+ for(unsigned int c=0;c<_maxCustodyChainLength;++c) {
+ if (c == 0) {
+ if ((!_custody[c].to)||(!_custody[c].from)||(_custody[c].from != Network::controllerFor(_nwid)))
+ return -1; // the first entry must be present and from the network's controller
+ } else {
+ if (!_custody[c].to)
+ return 0; // all previous entries were valid, so we are valid
+ else if ((!_custody[c].from)||(_custody[c].from != _custody[c-1].to))
+ return -1; // otherwise if we have another entry it must be from the previous holder in the chain
+ }
+
+ const Identity id(RR->topology->getIdentity(tPtr,_custody[c].from));
+ if (id) {
+ if (!id.verify(tmp.data(),tmp.size(),_custody[c].signature))
+ return -1;
+ } else {
+ RR->sw->requestWhois(tPtr,RR->node->now(),_custody[c].from);
+ return 1;
+ }
+ }
+
+ // We reached max custody chain length and everything was valid
+ return 0;
+ } catch ( ... ) {}
+ return -1;
+}
+
+} // namespace ZeroTier
diff --git a/node/Capability.hpp b/node/Capability.hpp
new file mode 100644
index 00000000..91a46566
--- /dev/null
+++ b/node/Capability.hpp
@@ -0,0 +1,493 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
+ */
+
+#ifndef ZT_CAPABILITY_HPP
+#define ZT_CAPABILITY_HPP
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "Constants.hpp"
+#include "Credential.hpp"
+#include "Address.hpp"
+#include "C25519.hpp"
+#include "Utils.hpp"
+#include "Buffer.hpp"
+#include "Identity.hpp"
+#include "../include/ZeroTierOne.h"
+
+namespace ZeroTier {
+
+class RuntimeEnvironment;
+
+/**
+ * A set of grouped and signed network flow rules
+ *
+ * On the sending side the sender does the following for each packet:
+ *
+ * (1) Evaluates its capabilities in ascending order of ID to determine
+ * which capability allows it to transmit this packet.
+ * (2) If it has not done so lately, it then sends this capability to the
+ * receving peer ("presents" it).
+ * (3) The sender then sends the packet.
+ *
+ * On the receiving side the receiver evaluates the capabilities presented
+ * by the sender. If any valid un-expired capability allows this packet it
+ * is accepted.
+ *
+ * Note that this is after evaluation of network scope rules and only if
+ * network scope rules do not deliver an explicit match.
+ *
+ * Capabilities support a chain of custody. This is currently unused but
+ * in the future would allow the publication of capabilities that can be
+ * handed off between nodes. Limited transferrability of capabilities is
+ * a feature of true capability based security.
+ */
+class Capability : public Credential
+{
+public:
+ static inline Credential::Type credentialType() { return Credential::CREDENTIAL_TYPE_CAPABILITY; }
+
+ Capability()
+ {
+ memset(this,0,sizeof(Capability));
+ }
+
+ /**
+ * @param id Capability ID
+ * @param nwid Network ID
+ * @param ts Timestamp (at controller)
+ * @param mccl Maximum custody chain length (1 to create non-transferrable capability)
+ * @param rules Network flow rules for this capability
+ * @param ruleCount Number of flow rules
+ */
+ Capability(uint32_t id,uint64_t nwid,int64_t ts,unsigned int mccl,const ZT_VirtualNetworkRule *rules,unsigned int ruleCount)
+ {
+ memset(this,0,sizeof(Capability));
+ _nwid = nwid;
+ _ts = ts;
+ _id = id;
+ _maxCustodyChainLength = (mccl > 0) ? ((mccl < ZT_MAX_CAPABILITY_CUSTODY_CHAIN_LENGTH) ? mccl : (unsigned int)ZT_MAX_CAPABILITY_CUSTODY_CHAIN_LENGTH) : 1;
+ _ruleCount = (ruleCount < ZT_MAX_CAPABILITY_RULES) ? ruleCount : ZT_MAX_CAPABILITY_RULES;
+ if (_ruleCount)
+ ZT_FAST_MEMCPY(_rules,rules,sizeof(ZT_VirtualNetworkRule) * _ruleCount);
+ }
+
+ /**
+ * @return Rules -- see ruleCount() for size of array
+ */
+ inline const ZT_VirtualNetworkRule *rules() const { return _rules; }
+
+ /**
+ * @return Number of rules in rules()
+ */
+ inline unsigned int ruleCount() const { return _ruleCount; }
+
+ /**
+ * @return ID and evaluation order of this capability in network
+ */
+ inline uint32_t id() const { return _id; }
+
+ /**
+ * @return Network ID for which this capability was issued
+ */
+ inline uint64_t networkId() const { return _nwid; }
+
+ /**
+ * @return Timestamp
+ */
+ inline int64_t timestamp() const { return _ts; }
+
+ /**
+ * @return Last 'to' address in chain of custody
+ */
+ inline Address issuedTo() const
+ {
+ Address i2;
+ for(unsigned int i=0;i<ZT_MAX_CAPABILITY_CUSTODY_CHAIN_LENGTH;++i) {
+ if (!_custody[i].to)
+ return i2;
+ else i2 = _custody[i].to;
+ }
+ return i2;
+ }
+
+ /**
+ * Sign this capability and add signature to its chain of custody
+ *
+ * If this returns false, this object should be considered to be
+ * in an undefined state and should be discarded. False can be returned
+ * if there is no more room for signatures (max chain length reached)
+ * or if the 'from' identity does not include a secret key to allow
+ * it to sign anything.
+ *
+ * @param from Signing identity (must have secret)
+ * @param to Recipient of this signature
+ * @return True if signature successful and chain of custody appended
+ */
+ inline bool sign(const Identity &from,const Address &to)
+ {
+ try {
+ for(unsigned int i=0;((i<_maxCustodyChainLength)&&(i<ZT_MAX_CAPABILITY_CUSTODY_CHAIN_LENGTH));++i) {
+ if (!(_custody[i].to)) {
+ Buffer<(sizeof(Capability) * 2)> tmp;
+ this->serialize(tmp,true);
+ _custody[i].to = to;
+ _custody[i].from = from.address();
+ _custody[i].signature = from.sign(tmp.data(),tmp.size());
+ return true;
+ }
+ }
+ } catch ( ... ) {}
+ return false;
+ }
+
+ /**
+ * Verify this capability's chain of custody and signatures
+ *
+ * @param RR Runtime environment to provide for peer lookup, etc.
+ * @return 0 == OK, 1 == waiting for WHOIS, -1 == BAD signature or chain
+ */
+ int verify(const RuntimeEnvironment *RR,void *tPtr) const;
+
+ template<unsigned int C>
+ static inline void serializeRules(Buffer<C> &b,const ZT_VirtualNetworkRule *rules,unsigned int ruleCount)
+ {
+ for(unsigned int i=0;i<ruleCount;++i) {
+ // Each rule consists of its 8-bit type followed by the size of that type's
+ // field followed by field data. The inclusion of the size will allow non-supported
+ // rules to be ignored but still parsed.
+ b.append((uint8_t)rules[i].t);
+ switch((ZT_VirtualNetworkRuleType)(rules[i].t & 0x3f)) {
+ default:
+ b.append((uint8_t)0);
+ break;
+ case ZT_NETWORK_RULE_ACTION_TEE:
+ case ZT_NETWORK_RULE_ACTION_WATCH:
+ case ZT_NETWORK_RULE_ACTION_REDIRECT:
+ b.append((uint8_t)14);
+ b.append((uint64_t)rules[i].v.fwd.address);
+ b.append((uint32_t)rules[i].v.fwd.flags);
+ b.append((uint16_t)rules[i].v.fwd.length); // unused for redirect
+ break;
+ case ZT_NETWORK_RULE_MATCH_SOURCE_ZEROTIER_ADDRESS:
+ case ZT_NETWORK_RULE_MATCH_DEST_ZEROTIER_ADDRESS:
+ b.append((uint8_t)5);
+ Address(rules[i].v.zt).appendTo(b);
+ break;
+ case ZT_NETWORK_RULE_MATCH_VLAN_ID:
+ b.append((uint8_t)2);
+ b.append((uint16_t)rules[i].v.vlanId);
+ break;
+ case ZT_NETWORK_RULE_MATCH_VLAN_PCP:
+ b.append((uint8_t)1);
+ b.append((uint8_t)rules[i].v.vlanPcp);
+ break;
+ case ZT_NETWORK_RULE_MATCH_VLAN_DEI:
+ b.append((uint8_t)1);
+ b.append((uint8_t)rules[i].v.vlanDei);
+ break;
+ case ZT_NETWORK_RULE_MATCH_MAC_SOURCE:
+ case ZT_NETWORK_RULE_MATCH_MAC_DEST:
+ b.append((uint8_t)6);
+ b.append(rules[i].v.mac,6);
+ break;
+ case ZT_NETWORK_RULE_MATCH_IPV4_SOURCE:
+ case ZT_NETWORK_RULE_MATCH_IPV4_DEST:
+ b.append((uint8_t)5);
+ b.append(&(rules[i].v.ipv4.ip),4);
+ b.append((uint8_t)rules[i].v.ipv4.mask);
+ break;
+ case ZT_NETWORK_RULE_MATCH_IPV6_SOURCE:
+ case ZT_NETWORK_RULE_MATCH_IPV6_DEST:
+ b.append((uint8_t)17);
+ b.append(rules[i].v.ipv6.ip,16);
+ b.append((uint8_t)rules[i].v.ipv6.mask);
+ break;
+ case ZT_NETWORK_RULE_MATCH_IP_TOS:
+ b.append((uint8_t)3);
+ b.append((uint8_t)rules[i].v.ipTos.mask);
+ b.append((uint8_t)rules[i].v.ipTos.value[0]);
+ b.append((uint8_t)rules[i].v.ipTos.value[1]);
+ break;
+ case ZT_NETWORK_RULE_MATCH_IP_PROTOCOL:
+ b.append((uint8_t)1);
+ b.append((uint8_t)rules[i].v.ipProtocol);
+ break;
+ case ZT_NETWORK_RULE_MATCH_ETHERTYPE:
+ b.append((uint8_t)2);
+ b.append((uint16_t)rules[i].v.etherType);
+ break;
+ case ZT_NETWORK_RULE_MATCH_ICMP:
+ b.append((uint8_t)3);
+ b.append((uint8_t)rules[i].v.icmp.type);
+ b.append((uint8_t)rules[i].v.icmp.code);
+ b.append((uint8_t)rules[i].v.icmp.flags);
+ break;
+ case ZT_NETWORK_RULE_MATCH_IP_SOURCE_PORT_RANGE:
+ case ZT_NETWORK_RULE_MATCH_IP_DEST_PORT_RANGE:
+ b.append((uint8_t)4);
+ b.append((uint16_t)rules[i].v.port[0]);
+ b.append((uint16_t)rules[i].v.port[1]);
+ break;
+ case ZT_NETWORK_RULE_MATCH_CHARACTERISTICS:
+ b.append((uint8_t)8);
+ b.append((uint64_t)rules[i].v.characteristics);
+ break;
+ case ZT_NETWORK_RULE_MATCH_FRAME_SIZE_RANGE:
+ b.append((uint8_t)4);
+ b.append((uint16_t)rules[i].v.frameSize[0]);
+ b.append((uint16_t)rules[i].v.frameSize[1]);
+ break;
+ case ZT_NETWORK_RULE_MATCH_RANDOM:
+ b.append((uint8_t)4);
+ b.append((uint32_t)rules[i].v.randomProbability);
+ break;
+ case ZT_NETWORK_RULE_MATCH_TAGS_DIFFERENCE:
+ case ZT_NETWORK_RULE_MATCH_TAGS_BITWISE_AND:
+ case ZT_NETWORK_RULE_MATCH_TAGS_BITWISE_OR:
+ case ZT_NETWORK_RULE_MATCH_TAGS_BITWISE_XOR:
+ case ZT_NETWORK_RULE_MATCH_TAGS_EQUAL:
+ case ZT_NETWORK_RULE_MATCH_TAG_SENDER:
+ case ZT_NETWORK_RULE_MATCH_TAG_RECEIVER:
+ b.append((uint8_t)8);
+ b.append((uint32_t)rules[i].v.tag.id);
+ b.append((uint32_t)rules[i].v.tag.value);
+ break;
+ case ZT_NETWORK_RULE_MATCH_INTEGER_RANGE:
+ b.append((uint8_t)19);
+ b.append((uint64_t)rules[i].v.intRange.start);
+ b.append((uint64_t)(rules[i].v.intRange.start + (uint64_t)rules[i].v.intRange.end)); // more future-proof
+ b.append((uint16_t)rules[i].v.intRange.idx);
+ b.append((uint8_t)rules[i].v.intRange.format);
+ break;
+ }
+ }
+ }
+
+ template<unsigned int C>
+ static inline void deserializeRules(const Buffer<C> &b,unsigned int &p,ZT_VirtualNetworkRule *rules,unsigned int &ruleCount,const unsigned int maxRuleCount)
+ {
+ while ((ruleCount < maxRuleCount)&&(p < b.size())) {
+ rules[ruleCount].t = (uint8_t)b[p++];
+ const unsigned int fieldLen = (unsigned int)b[p++];
+ switch((ZT_VirtualNetworkRuleType)(rules[ruleCount].t & 0x3f)) {
+ default:
+ break;
+ case ZT_NETWORK_RULE_ACTION_TEE:
+ case ZT_NETWORK_RULE_ACTION_WATCH:
+ case ZT_NETWORK_RULE_ACTION_REDIRECT:
+ rules[ruleCount].v.fwd.address = b.template at<uint64_t>(p);
+ rules[ruleCount].v.fwd.flags = b.template at<uint32_t>(p + 8);
+ rules[ruleCount].v.fwd.length = b.template at<uint16_t>(p + 12);
+ break;
+ case ZT_NETWORK_RULE_MATCH_SOURCE_ZEROTIER_ADDRESS:
+ case ZT_NETWORK_RULE_MATCH_DEST_ZEROTIER_ADDRESS:
+ rules[ruleCount].v.zt = Address(b.field(p,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH).toInt();
+ break;
+ case ZT_NETWORK_RULE_MATCH_VLAN_ID:
+ rules[ruleCount].v.vlanId = b.template at<uint16_t>(p);
+ break;
+ case ZT_NETWORK_RULE_MATCH_VLAN_PCP:
+ rules[ruleCount].v.vlanPcp = (uint8_t)b[p];
+ break;
+ case ZT_NETWORK_RULE_MATCH_VLAN_DEI:
+ rules[ruleCount].v.vlanDei = (uint8_t)b[p];
+ break;
+ case ZT_NETWORK_RULE_MATCH_MAC_SOURCE:
+ case ZT_NETWORK_RULE_MATCH_MAC_DEST:
+ ZT_FAST_MEMCPY(rules[ruleCount].v.mac,b.field(p,6),6);
+ break;
+ case ZT_NETWORK_RULE_MATCH_IPV4_SOURCE:
+ case ZT_NETWORK_RULE_MATCH_IPV4_DEST:
+ ZT_FAST_MEMCPY(&(rules[ruleCount].v.ipv4.ip),b.field(p,4),4);
+ rules[ruleCount].v.ipv4.mask = (uint8_t)b[p + 4];
+ break;
+ case ZT_NETWORK_RULE_MATCH_IPV6_SOURCE:
+ case ZT_NETWORK_RULE_MATCH_IPV6_DEST:
+ ZT_FAST_MEMCPY(rules[ruleCount].v.ipv6.ip,b.field(p,16),16);
+ rules[ruleCount].v.ipv6.mask = (uint8_t)b[p + 16];
+ break;
+ case ZT_NETWORK_RULE_MATCH_IP_TOS:
+ rules[ruleCount].v.ipTos.mask = (uint8_t)b[p];
+ rules[ruleCount].v.ipTos.value[0] = (uint8_t)b[p+1];
+ rules[ruleCount].v.ipTos.value[1] = (uint8_t)b[p+2];
+ break;
+ case ZT_NETWORK_RULE_MATCH_IP_PROTOCOL:
+ rules[ruleCount].v.ipProtocol = (uint8_t)b[p];
+ break;
+ case ZT_NETWORK_RULE_MATCH_ETHERTYPE:
+ rules[ruleCount].v.etherType = b.template at<uint16_t>(p);
+ break;
+ case ZT_NETWORK_RULE_MATCH_ICMP:
+ rules[ruleCount].v.icmp.type = (uint8_t)b[p];
+ rules[ruleCount].v.icmp.code = (uint8_t)b[p+1];
+ rules[ruleCount].v.icmp.flags = (uint8_t)b[p+2];
+ break;
+ case ZT_NETWORK_RULE_MATCH_IP_SOURCE_PORT_RANGE:
+ case ZT_NETWORK_RULE_MATCH_IP_DEST_PORT_RANGE:
+ rules[ruleCount].v.port[0] = b.template at<uint16_t>(p);
+ rules[ruleCount].v.port[1] = b.template at<uint16_t>(p + 2);
+ break;
+ case ZT_NETWORK_RULE_MATCH_CHARACTERISTICS:
+ rules[ruleCount].v.characteristics = b.template at<uint64_t>(p);
+ break;
+ case ZT_NETWORK_RULE_MATCH_FRAME_SIZE_RANGE:
+ rules[ruleCount].v.frameSize[0] = b.template at<uint16_t>(p);
+ rules[ruleCount].v.frameSize[1] = b.template at<uint16_t>(p + 2);
+ break;
+ case ZT_NETWORK_RULE_MATCH_RANDOM:
+ rules[ruleCount].v.randomProbability = b.template at<uint32_t>(p);
+ break;
+ case ZT_NETWORK_RULE_MATCH_TAGS_DIFFERENCE:
+ case ZT_NETWORK_RULE_MATCH_TAGS_BITWISE_AND:
+ case ZT_NETWORK_RULE_MATCH_TAGS_BITWISE_OR:
+ case ZT_NETWORK_RULE_MATCH_TAGS_BITWISE_XOR:
+ case ZT_NETWORK_RULE_MATCH_TAGS_EQUAL:
+ case ZT_NETWORK_RULE_MATCH_TAG_SENDER:
+ case ZT_NETWORK_RULE_MATCH_TAG_RECEIVER:
+ rules[ruleCount].v.tag.id = b.template at<uint32_t>(p);
+ rules[ruleCount].v.tag.value = b.template at<uint32_t>(p + 4);
+ break;
+ case ZT_NETWORK_RULE_MATCH_INTEGER_RANGE:
+ rules[ruleCount].v.intRange.start = b.template at<uint64_t>(p);
+ rules[ruleCount].v.intRange.end = (uint32_t)(b.template at<uint64_t>(p + 8) - rules[ruleCount].v.intRange.start);
+ rules[ruleCount].v.intRange.idx = b.template at<uint16_t>(p + 16);
+ rules[ruleCount].v.intRange.format = (uint8_t)b[p + 18];
+ break;
+ }
+ p += fieldLen;
+ ++ruleCount;
+ }
+ }
+
+ template<unsigned int C>
+ inline void serialize(Buffer<C> &b,const bool forSign = false) const
+ {
+ if (forSign) b.append((uint64_t)0x7f7f7f7f7f7f7f7fULL);
+
+ // These are the same between Tag and Capability
+ b.append(_nwid);
+ b.append(_ts);
+ b.append(_id);
+
+ b.append((uint16_t)_ruleCount);
+ serializeRules(b,_rules,_ruleCount);
+ b.append((uint8_t)_maxCustodyChainLength);
+
+ if (!forSign) {
+ for(unsigned int i=0;;++i) {
+ if ((i < _maxCustodyChainLength)&&(i < ZT_MAX_CAPABILITY_CUSTODY_CHAIN_LENGTH)&&(_custody[i].to)) {
+ _custody[i].to.appendTo(b);
+ _custody[i].from.appendTo(b);
+ b.append((uint8_t)1); // 1 == Ed25519 signature
+ b.append((uint16_t)ZT_C25519_SIGNATURE_LEN); // length of signature
+ b.append(_custody[i].signature.data,ZT_C25519_SIGNATURE_LEN);
+ } else {
+ b.append((unsigned char)0,ZT_ADDRESS_LENGTH); // zero 'to' terminates chain
+ break;
+ }
+ }
+ }
+
+ // This is the size of any additional fields, currently 0.
+ b.append((uint16_t)0);
+
+ if (forSign) b.append((uint64_t)0x7f7f7f7f7f7f7f7fULL);
+ }
+
+ template<unsigned int C>
+ inline unsigned int deserialize(const Buffer<C> &b,unsigned int startAt = 0)
+ {
+ memset(this,0,sizeof(Capability));
+
+ unsigned int p = startAt;
+
+ _nwid = b.template at<uint64_t>(p); p += 8;
+ _ts = b.template at<uint64_t>(p); p += 8;
+ _id = b.template at<uint32_t>(p); p += 4;
+
+ const unsigned int rc = b.template at<uint16_t>(p); p += 2;
+ if (rc > ZT_MAX_CAPABILITY_RULES)
+ throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_OVERFLOW;
+ deserializeRules(b,p,_rules,_ruleCount,rc);
+
+ _maxCustodyChainLength = (unsigned int)b[p++];
+ if ((_maxCustodyChainLength < 1)||(_maxCustodyChainLength > ZT_MAX_CAPABILITY_CUSTODY_CHAIN_LENGTH))
+ throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_OVERFLOW;
+
+ for(unsigned int i=0;;++i) {
+ const Address to(b.field(p,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH); p += ZT_ADDRESS_LENGTH;
+ if (!to)
+ break;
+ if ((i >= _maxCustodyChainLength)||(i >= ZT_MAX_CAPABILITY_CUSTODY_CHAIN_LENGTH))
+ throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_OVERFLOW;
+ _custody[i].to = to;
+ _custody[i].from.setTo(b.field(p,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH); p += ZT_ADDRESS_LENGTH;
+ if (b[p++] == 1) {
+ if (b.template at<uint16_t>(p) != ZT_C25519_SIGNATURE_LEN)
+ throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_INVALID_CRYPTOGRAPHIC_TOKEN;
+ p += 2;
+ ZT_FAST_MEMCPY(_custody[i].signature.data,b.field(p,ZT_C25519_SIGNATURE_LEN),ZT_C25519_SIGNATURE_LEN); p += ZT_C25519_SIGNATURE_LEN;
+ } else {
+ p += 2 + b.template at<uint16_t>(p);
+ }
+ }
+
+ p += 2 + b.template at<uint16_t>(p);
+ if (p > b.size())
+ throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_OVERFLOW;
+
+ return (p - startAt);
+ }
+
+ // Provides natural sort order by ID
+ inline bool operator<(const Capability &c) const { return (_id < c._id); }
+
+ inline bool operator==(const Capability &c) const { return (memcmp(this,&c,sizeof(Capability)) == 0); }
+ inline bool operator!=(const Capability &c) const { return (memcmp(this,&c,sizeof(Capability)) != 0); }
+
+private:
+ uint64_t _nwid;
+ int64_t _ts;
+ uint32_t _id;
+
+ unsigned int _maxCustodyChainLength;
+
+ unsigned int _ruleCount;
+ ZT_VirtualNetworkRule _rules[ZT_MAX_CAPABILITY_RULES];
+
+ struct {
+ Address to;
+ Address from;
+ C25519::Signature signature;
+ } _custody[ZT_MAX_CAPABILITY_CUSTODY_CHAIN_LENGTH];
+};
+
+} // namespace ZeroTier
+
+#endif
diff --git a/node/CertificateOfMembership.cpp b/node/CertificateOfMembership.cpp
index 55537fd9..8184ce99 100644
--- a/node/CertificateOfMembership.cpp
+++ b/node/CertificateOfMembership.cpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,9 +14,22 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#include "CertificateOfMembership.hpp"
+#include "RuntimeEnvironment.hpp"
+#include "Topology.hpp"
+#include "Switch.hpp"
+#include "Network.hpp"
+#include "Node.hpp"
namespace ZeroTier {
@@ -45,6 +58,7 @@ void CertificateOfMembership::setQualifier(uint64_t id,uint64_t value,uint64_t m
std::string CertificateOfMembership::toString() const
{
+ char tmp[ZT_NETWORK_COM_MAX_QUALIFIERS * 32];
std::string s;
s.append("1:"); // COM_UINT64_ED25519
@@ -57,7 +71,7 @@ std::string CertificateOfMembership::toString() const
buf[ptr++] = Utils::hton(_qualifiers[i].value);
buf[ptr++] = Utils::hton(_qualifiers[i].maxDelta);
}
- s.append(Utils::hex(buf,ptr * sizeof(uint64_t)));
+ s.append(Utils::hex(buf,ptr * sizeof(uint64_t),tmp));
delete [] buf;
} catch ( ... ) {
delete [] buf;
@@ -66,11 +80,11 @@ std::string CertificateOfMembership::toString() const
s.push_back(':');
- s.append(_signedBy.toString());
+ s.append(_signedBy.toString(tmp));
if (_signedBy) {
s.push_back(':');
- s.append(Utils::hex(_signature.data,(unsigned int)_signature.size()));
+ s.append(Utils::hex(_signature.data,ZT_C25519_SIGNATURE_LEN,tmp));
}
return s;
@@ -80,7 +94,7 @@ void CertificateOfMembership::fromString(const char *s)
{
_qualifierCount = 0;
_signedBy.zero();
- memset(_signature.data,0,_signature.size());
+ memset(_signature.data,0,ZT_C25519_SIGNATURE_LEN);
if (!*s)
return;
@@ -131,7 +145,7 @@ void CertificateOfMembership::fromString(const char *s)
colonAt = 0;
while ((s[colonAt])&&(s[colonAt] != ':')) ++colonAt;
if (colonAt) {
- if (Utils::unhex(s,colonAt,_signature.data,(unsigned int)_signature.size()) != _signature.size())
+ if (Utils::unhex(s,colonAt,_signature.data,ZT_C25519_SIGNATURE_LEN) != ZT_C25519_SIGNATURE_LEN)
_signedBy.zero();
} else {
_signedBy.zero();
@@ -152,6 +166,9 @@ bool CertificateOfMembership::agreesWith(const CertificateOfMembership &other) c
unsigned int myidx = 0;
unsigned int otheridx = 0;
+ if ((_qualifierCount == 0)||(other._qualifierCount == 0))
+ return false;
+
while (myidx < _qualifierCount) {
// Fail if we're at the end of other, since this means the field is
// missing.
@@ -182,7 +199,7 @@ bool CertificateOfMembership::agreesWith(const CertificateOfMembership &other) c
bool CertificateOfMembership::sign(const Identity &with)
{
- uint64_t *const buf = new uint64_t[_qualifierCount * 3];
+ uint64_t buf[ZT_NETWORK_COM_MAX_QUALIFIERS * 3];
unsigned int ptr = 0;
for(unsigned int i=0;i<_qualifierCount;++i) {
buf[ptr++] = Utils::hton(_qualifiers[i].id);
@@ -193,38 +210,32 @@ bool CertificateOfMembership::sign(const Identity &with)
try {
_signature = with.sign(buf,ptr * sizeof(uint64_t));
_signedBy = with.address();
- delete [] buf;
return true;
} catch ( ... ) {
_signedBy.zero();
- delete [] buf;
return false;
}
}
-bool CertificateOfMembership::verify(const Identity &id) const
+int CertificateOfMembership::verify(const RuntimeEnvironment *RR,void *tPtr) const
{
- if (!_signedBy)
- return false;
- if (id.address() != _signedBy)
- return false;
+ if ((!_signedBy)||(_signedBy != Network::controllerFor(networkId()))||(_qualifierCount > ZT_NETWORK_COM_MAX_QUALIFIERS))
+ return -1;
- uint64_t *const buf = new uint64_t[_qualifierCount * 3];
+ const Identity id(RR->topology->getIdentity(tPtr,_signedBy));
+ if (!id) {
+ RR->sw->requestWhois(tPtr,RR->node->now(),_signedBy);
+ return 1;
+ }
+
+ uint64_t buf[ZT_NETWORK_COM_MAX_QUALIFIERS * 3];
unsigned int ptr = 0;
for(unsigned int i=0;i<_qualifierCount;++i) {
buf[ptr++] = Utils::hton(_qualifiers[i].id);
buf[ptr++] = Utils::hton(_qualifiers[i].value);
buf[ptr++] = Utils::hton(_qualifiers[i].maxDelta);
}
-
- bool valid = false;
- try {
- valid = id.verify(buf,ptr * sizeof(uint64_t),_signature);
- delete [] buf;
- } catch ( ... ) {
- delete [] buf;
- }
- return valid;
+ return (id.verify(buf,ptr * sizeof(uint64_t),_signature) ? 0 : -1);
}
} // namespace ZeroTier
diff --git a/node/CertificateOfMembership.hpp b/node/CertificateOfMembership.hpp
index 0342bc33..b5a90007 100644
--- a/node/CertificateOfMembership.hpp
+++ b/node/CertificateOfMembership.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_CERTIFICATEOFMEMBERSHIP_HPP
@@ -27,6 +35,7 @@
#include <algorithm>
#include "Constants.hpp"
+#include "Credential.hpp"
#include "Buffer.hpp"
#include "Address.hpp"
#include "C25519.hpp"
@@ -34,22 +43,14 @@
#include "Utils.hpp"
/**
- * Default window of time for certificate agreement
- *
- * Right now we use time for 'revision' so this is the maximum time divergence
- * between two certs for them to agree. It comes out to five minutes, which
- * gives a lot of margin for error if the controller hiccups or its clock
- * drifts but causes de-authorized peers to fall off fast enough.
- */
-#define ZT_NETWORK_COM_DEFAULT_REVISION_MAX_DELTA (ZT_NETWORK_AUTOCONF_DELAY * 5)
-
-/**
- * Maximum number of qualifiers in a COM
+ * Maximum number of qualifiers allowed in a COM (absolute max: 65535)
*/
-#define ZT_NETWORK_COM_MAX_QUALIFIERS 16
+#define ZT_NETWORK_COM_MAX_QUALIFIERS 8
namespace ZeroTier {
+class RuntimeEnvironment;
+
/**
* Certificate of network membership
*
@@ -76,25 +77,16 @@ namespace ZeroTier {
* This is a memcpy()'able structure and is safe (in a crash sense) to modify
* without locks.
*/
-class CertificateOfMembership
+class CertificateOfMembership : public Credential
{
public:
- /**
- * Certificate type codes, used in serialization
- *
- * Only one so far, and only one hopefully there shall be for quite some
- * time.
- */
- enum Type
- {
- COM_UINT64_ED25519 = 1 // tuples of unsigned 64's signed with Ed25519
- };
+ static inline Credential::Type credentialType() { return Credential::CREDENTIAL_TYPE_COM; }
/**
* Reserved qualifier IDs
*
- * IDs below 65536 should be considered reserved for future global
- * assignment here.
+ * IDs below 1024 are reserved for use as standard IDs. Others are available
+ * for user-defined use.
*
* Addition of new required fields requires that code in hasRequiredFields
* be updated as well.
@@ -102,56 +94,47 @@ public:
enum ReservedId
{
/**
- * Revision number of certificate
- *
- * Certificates may differ in revision number by a designated max
- * delta. Differences wider than this cause certificates not to agree.
+ * Timestamp of certificate
*/
- COM_RESERVED_ID_REVISION = 0,
+ COM_RESERVED_ID_TIMESTAMP = 0,
/**
* Network ID for which certificate was issued
- *
- * maxDelta here is zero, since this must match.
*/
COM_RESERVED_ID_NETWORK_ID = 1,
/**
* ZeroTier address to whom certificate was issued
- *
- * maxDelta will be 0xffffffffffffffff here since it's permitted to differ
- * from peers obviously.
*/
COM_RESERVED_ID_ISSUED_TO = 2
};
/**
- * Create an empty certificate
+ * Create an empty certificate of membership
*/
- CertificateOfMembership() :
- _qualifierCount(0)
+ CertificateOfMembership()
{
- memset(_signature.data,0,_signature.size());
+ memset(this,0,sizeof(CertificateOfMembership));
}
CertificateOfMembership(const CertificateOfMembership &c)
{
- memcpy(this,&c,sizeof(CertificateOfMembership));
+ ZT_FAST_MEMCPY(this,&c,sizeof(CertificateOfMembership));
}
/**
* Create from required fields common to all networks
*
- * @param revision Revision number of certificate
+ * @param timestamp Timestamp of certificate
* @param timestampMaxDelta Maximum variation between timestamps on this net
* @param nwid Network ID
* @param issuedTo Certificate recipient
*/
- CertificateOfMembership(uint64_t revision,uint64_t revisionMaxDelta,uint64_t nwid,const Address &issuedTo)
+ CertificateOfMembership(uint64_t timestamp,uint64_t timestampMaxDelta,uint64_t nwid,const Address &issuedTo)
{
- _qualifiers[0].id = COM_RESERVED_ID_REVISION;
- _qualifiers[0].value = revision;
- _qualifiers[0].maxDelta = revisionMaxDelta;
+ _qualifiers[0].id = COM_RESERVED_ID_TIMESTAMP;
+ _qualifiers[0].value = timestamp;
+ _qualifiers[0].maxDelta = timestampMaxDelta;
_qualifiers[1].id = COM_RESERVED_ID_NETWORK_ID;
_qualifiers[1].value = nwid;
_qualifiers[1].maxDelta = 0;
@@ -159,31 +142,15 @@ public:
_qualifiers[2].value = issuedTo.toInt();
_qualifiers[2].maxDelta = 0xffffffffffffffffULL;
_qualifierCount = 3;
- memset(_signature.data,0,_signature.size());
+ memset(_signature.data,0,ZT_C25519_SIGNATURE_LEN);
}
inline CertificateOfMembership &operator=(const CertificateOfMembership &c)
{
- memcpy(this,&c,sizeof(CertificateOfMembership));
+ ZT_FAST_MEMCPY(this,&c,sizeof(CertificateOfMembership));
return *this;
}
-#ifdef ZT_SUPPORT_OLD_STYLE_NETCONF
- /**
- * Create from string-serialized data
- *
- * @param s String-serialized COM
- */
- CertificateOfMembership(const char *s) { fromString(s); }
-
- /**
- * Create from string-serialized data
- *
- * @param s String-serialized COM
- */
- CertificateOfMembership(const std::string &s) { fromString(s.c_str()); }
-#endif // ZT_SUPPORT_OLD_STYLE_NETCONF
-
/**
* Create from binary-serialized COM in buffer
*
@@ -199,48 +166,23 @@ public:
/**
* @return True if there's something here
*/
- inline operator bool() const throw() { return (_qualifierCount != 0); }
+ inline operator bool() const { return (_qualifierCount != 0); }
/**
- * Check for presence of all required fields common to all networks
- *
- * @return True if all required fields are present
+ * @return Credential ID, always 0 for COMs
*/
- inline bool hasRequiredFields() const
- {
- if (_qualifierCount < 3)
- return false;
- if (_qualifiers[0].id != COM_RESERVED_ID_REVISION)
- return false;
- if (_qualifiers[1].id != COM_RESERVED_ID_NETWORK_ID)
- return false;
- if (_qualifiers[2].id != COM_RESERVED_ID_ISSUED_TO)
- return false;
- return true;
- }
+ inline uint32_t id() const { return 0; }
/**
- * @return Maximum delta for mandatory revision field or 0 if field missing
+ * @return Timestamp for this cert and maximum delta for timestamp
*/
- inline uint64_t revisionMaxDelta() const
+ inline int64_t timestamp() const
{
for(unsigned int i=0;i<_qualifierCount;++i) {
- if (_qualifiers[i].id == COM_RESERVED_ID_REVISION)
- return _qualifiers[i].maxDelta;
- }
- return 0ULL;
- }
-
- /**
- * @return Revision number for this cert
- */
- inline uint64_t revision() const
- {
- for(unsigned int i=0;i<_qualifierCount;++i) {
- if (_qualifiers[i].id == COM_RESERVED_ID_REVISION)
+ if (_qualifiers[i].id == COM_RESERVED_ID_TIMESTAMP)
return _qualifiers[i].value;
}
- return 0ULL;
+ return 0;
}
/**
@@ -321,27 +263,28 @@ public:
bool sign(const Identity &with);
/**
- * Verify certificate against an identity
+ * Verify this COM and its signature
*
- * @param id Identity to verify against
- * @return True if certificate is signed by this identity and verification was successful
+ * @param RR Runtime environment for looking up peers
+ * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
+ * @return 0 == OK, 1 == waiting for WHOIS, -1 == BAD signature or credential
*/
- bool verify(const Identity &id) const;
+ int verify(const RuntimeEnvironment *RR,void *tPtr) const;
/**
* @return True if signed
*/
- inline bool isSigned() const throw() { return (_signedBy); }
+ inline bool isSigned() const { return (_signedBy); }
/**
* @return Address that signed this certificate or null address if none
*/
- inline const Address &signedBy() const throw() { return _signedBy; }
+ inline const Address &signedBy() const { return _signedBy; }
template<unsigned int C>
inline void serialize(Buffer<C> &b) const
{
- b.append((unsigned char)COM_UINT64_ED25519);
+ b.append((uint8_t)1);
b.append((uint16_t)_qualifierCount);
for(unsigned int i=0;i<_qualifierCount;++i) {
b.append(_qualifiers[i].id);
@@ -350,7 +293,7 @@ public:
}
_signedBy.appendTo(b);
if (_signedBy)
- b.append(_signature.data,(unsigned int)_signature.size());
+ b.append(_signature.data,ZT_C25519_SIGNATURE_LEN);
}
template<unsigned int C>
@@ -361,15 +304,15 @@ public:
_qualifierCount = 0;
_signedBy.zero();
- if (b[p++] != COM_UINT64_ED25519)
- throw std::invalid_argument("invalid type");
+ if (b[p++] != 1)
+ throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_INVALID_TYPE;
unsigned int numq = b.template at<uint16_t>(p); p += sizeof(uint16_t);
uint64_t lastId = 0;
for(unsigned int i=0;i<numq;++i) {
const uint64_t qid = b.template at<uint64_t>(p);
if (qid < lastId)
- throw std::invalid_argument("qualifiers not sorted");
+ throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_BAD_ENCODING;
else lastId = qid;
if (_qualifierCount < ZT_NETWORK_COM_MAX_QUALIFIERS) {
_qualifiers[_qualifierCount].id = qid;
@@ -378,7 +321,7 @@ public:
p += 24;
++_qualifierCount;
} else {
- throw std::invalid_argument("too many qualifiers");
+ throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_OVERFLOW;
}
}
@@ -386,15 +329,14 @@ public:
p += ZT_ADDRESS_LENGTH;
if (_signedBy) {
- memcpy(_signature.data,b.field(p,(unsigned int)_signature.size()),_signature.size());
- p += (unsigned int)_signature.size();
+ ZT_FAST_MEMCPY(_signature.data,b.field(p,ZT_C25519_SIGNATURE_LEN),ZT_C25519_SIGNATURE_LEN);
+ p += ZT_C25519_SIGNATURE_LEN;
}
return (p - startAt);
}
inline bool operator==(const CertificateOfMembership &c) const
- throw()
{
if (_signedBy != c._signedBy)
return false;
@@ -406,9 +348,9 @@ public:
if ((a.id != b.id)||(a.value != b.value)||(a.maxDelta != b.maxDelta))
return false;
}
- return (_signature == c._signature);
+ return (memcmp(_signature.data,c._signature.data,ZT_C25519_SIGNATURE_LEN) == 0);
}
- inline bool operator!=(const CertificateOfMembership &c) const throw() { return (!(*this == c)); }
+ inline bool operator!=(const CertificateOfMembership &c) const { return (!(*this == c)); }
private:
struct _Qualifier
@@ -417,7 +359,7 @@ private:
uint64_t id;
uint64_t value;
uint64_t maxDelta;
- inline bool operator<(const _Qualifier &q) const throw() { return (id < q.id); } // sort order
+ inline bool operator<(const _Qualifier &q) const { return (id < q.id); } // sort order
};
Address _signedBy;
diff --git a/node/CertificateOfOwnership.cpp b/node/CertificateOfOwnership.cpp
new file mode 100644
index 00000000..8ee67865
--- /dev/null
+++ b/node/CertificateOfOwnership.cpp
@@ -0,0 +1,72 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
+ */
+
+#include "CertificateOfOwnership.hpp"
+#include "RuntimeEnvironment.hpp"
+#include "Identity.hpp"
+#include "Topology.hpp"
+#include "Switch.hpp"
+#include "Network.hpp"
+#include "Node.hpp"
+
+namespace ZeroTier {
+
+int CertificateOfOwnership::verify(const RuntimeEnvironment *RR,void *tPtr) const
+{
+ if ((!_signedBy)||(_signedBy != Network::controllerFor(_networkId)))
+ return -1;
+ const Identity id(RR->topology->getIdentity(tPtr,_signedBy));
+ if (!id) {
+ RR->sw->requestWhois(tPtr,RR->node->now(),_signedBy);
+ return 1;
+ }
+ try {
+ Buffer<(sizeof(CertificateOfOwnership) + 64)> tmp;
+ this->serialize(tmp,true);
+ return (id.verify(tmp.data(),tmp.size(),_signature) ? 0 : -1);
+ } catch ( ... ) {
+ return -1;
+ }
+}
+
+bool CertificateOfOwnership::_owns(const CertificateOfOwnership::Thing &t,const void *v,unsigned int l) const
+{
+ for(unsigned int i=0,j=_thingCount;i<j;++i) {
+ if (_thingTypes[i] == (uint8_t)t) {
+ unsigned int k = 0;
+ while (k < l) {
+ if (reinterpret_cast<const uint8_t *>(v)[k] != _thingValues[i][k])
+ break;
+ ++k;
+ }
+ if (k == l)
+ return true;
+ }
+ }
+ return false;
+}
+
+} // namespace ZeroTier
diff --git a/node/CertificateOfOwnership.hpp b/node/CertificateOfOwnership.hpp
new file mode 100644
index 00000000..278ae863
--- /dev/null
+++ b/node/CertificateOfOwnership.hpp
@@ -0,0 +1,247 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
+ */
+
+#ifndef ZT_CERTIFICATEOFOWNERSHIP_HPP
+#define ZT_CERTIFICATEOFOWNERSHIP_HPP
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "Constants.hpp"
+#include "Credential.hpp"
+#include "C25519.hpp"
+#include "Address.hpp"
+#include "Identity.hpp"
+#include "Buffer.hpp"
+#include "InetAddress.hpp"
+#include "MAC.hpp"
+
+// Max things per CertificateOfOwnership
+#define ZT_CERTIFICATEOFOWNERSHIP_MAX_THINGS 16
+
+// Maximum size of a thing's value field in bytes
+#define ZT_CERTIFICATEOFOWNERSHIP_MAX_THING_VALUE_SIZE 16
+
+namespace ZeroTier {
+
+class RuntimeEnvironment;
+
+/**
+ * Certificate indicating ownership of a network identifier
+ */
+class CertificateOfOwnership : public Credential
+{
+public:
+ static inline Credential::Type credentialType() { return Credential::CREDENTIAL_TYPE_COO; }
+
+ enum Thing
+ {
+ THING_NULL = 0,
+ THING_MAC_ADDRESS = 1,
+ THING_IPV4_ADDRESS = 2,
+ THING_IPV6_ADDRESS = 3
+ };
+
+ CertificateOfOwnership()
+ {
+ memset(this,0,sizeof(CertificateOfOwnership));
+ }
+
+ CertificateOfOwnership(const uint64_t nwid,const int64_t ts,const Address &issuedTo,const uint32_t id) :
+ _networkId(nwid),
+ _ts(ts),
+ _flags(0),
+ _id(id),
+ _thingCount(0),
+ _issuedTo(issuedTo)
+ {
+ memset(_thingTypes,0,sizeof(_thingTypes));
+ memset(_thingValues,0,sizeof(_thingValues));
+ }
+
+ inline uint64_t networkId() const { return _networkId; }
+ inline int64_t timestamp() const { return _ts; }
+ inline uint32_t id() const { return _id; }
+ inline unsigned int thingCount() const { return (unsigned int)_thingCount; }
+
+ inline Thing thingType(const unsigned int i) const { return (Thing)_thingTypes[i]; }
+ inline const uint8_t *thingValue(const unsigned int i) const { return _thingValues[i]; }
+
+ inline const Address &issuedTo() const { return _issuedTo; }
+
+ inline bool owns(const InetAddress &ip) const
+ {
+ if (ip.ss_family == AF_INET)
+ return this->_owns(THING_IPV4_ADDRESS,&(reinterpret_cast<const struct sockaddr_in *>(&ip)->sin_addr.s_addr),4);
+ if (ip.ss_family == AF_INET6)
+ return this->_owns(THING_IPV6_ADDRESS,reinterpret_cast<const struct sockaddr_in6 *>(&ip)->sin6_addr.s6_addr,16);
+ return false;
+ }
+
+ inline bool owns(const MAC &mac) const
+ {
+ uint8_t tmp[6];
+ mac.copyTo(tmp,6);
+ return this->_owns(THING_MAC_ADDRESS,tmp,6);
+ }
+
+ inline void addThing(const InetAddress &ip)
+ {
+ if (_thingCount >= ZT_CERTIFICATEOFOWNERSHIP_MAX_THINGS) return;
+ if (ip.ss_family == AF_INET) {
+ _thingTypes[_thingCount] = THING_IPV4_ADDRESS;
+ ZT_FAST_MEMCPY(_thingValues[_thingCount],&(reinterpret_cast<const struct sockaddr_in *>(&ip)->sin_addr.s_addr),4);
+ ++_thingCount;
+ } else if (ip.ss_family == AF_INET6) {
+ _thingTypes[_thingCount] = THING_IPV6_ADDRESS;
+ ZT_FAST_MEMCPY(_thingValues[_thingCount],reinterpret_cast<const struct sockaddr_in6 *>(&ip)->sin6_addr.s6_addr,16);
+ ++_thingCount;
+ }
+ }
+
+ inline void addThing(const MAC &mac)
+ {
+ if (_thingCount >= ZT_CERTIFICATEOFOWNERSHIP_MAX_THINGS) return;
+ _thingTypes[_thingCount] = THING_MAC_ADDRESS;
+ mac.copyTo(_thingValues[_thingCount],6);
+ ++_thingCount;
+ }
+
+ /**
+ * @param signer Signing identity, must have private key
+ * @return True if signature was successful
+ */
+ inline bool sign(const Identity &signer)
+ {
+ if (signer.hasPrivate()) {
+ Buffer<sizeof(CertificateOfOwnership) + 64> tmp;
+ _signedBy = signer.address();
+ this->serialize(tmp,true);
+ _signature = signer.sign(tmp.data(),tmp.size());
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * @param RR Runtime environment to allow identity lookup for signedBy
+ * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
+ * @return 0 == OK, 1 == waiting for WHOIS, -1 == BAD signature
+ */
+ int verify(const RuntimeEnvironment *RR,void *tPtr) const;
+
+ template<unsigned int C>
+ inline void serialize(Buffer<C> &b,const bool forSign = false) const
+ {
+ if (forSign) b.append((uint64_t)0x7f7f7f7f7f7f7f7fULL);
+
+ b.append(_networkId);
+ b.append(_ts);
+ b.append(_flags);
+ b.append(_id);
+ b.append((uint16_t)_thingCount);
+ for(unsigned int i=0,j=_thingCount;i<j;++i) {
+ b.append((uint8_t)_thingTypes[i]);
+ b.append(_thingValues[i],ZT_CERTIFICATEOFOWNERSHIP_MAX_THING_VALUE_SIZE);
+ }
+
+ _issuedTo.appendTo(b);
+ _signedBy.appendTo(b);
+ if (!forSign) {
+ b.append((uint8_t)1); // 1 == Ed25519
+ b.append((uint16_t)ZT_C25519_SIGNATURE_LEN); // length of signature
+ b.append(_signature.data,ZT_C25519_SIGNATURE_LEN);
+ }
+
+ b.append((uint16_t)0); // length of additional fields, currently 0
+
+ if (forSign) b.append((uint64_t)0x7f7f7f7f7f7f7f7fULL);
+ }
+
+ template<unsigned int C>
+ inline unsigned int deserialize(const Buffer<C> &b,unsigned int startAt = 0)
+ {
+ unsigned int p = startAt;
+
+ memset(this,0,sizeof(CertificateOfOwnership));
+
+ _networkId = b.template at<uint64_t>(p); p += 8;
+ _ts = b.template at<uint64_t>(p); p += 8;
+ _flags = b.template at<uint64_t>(p); p += 8;
+ _id = b.template at<uint32_t>(p); p += 4;
+ _thingCount = b.template at<uint16_t>(p); p += 2;
+ for(unsigned int i=0,j=_thingCount;i<j;++i) {
+ if (i < ZT_CERTIFICATEOFOWNERSHIP_MAX_THINGS) {
+ _thingTypes[i] = (uint8_t)b[p++];
+ ZT_FAST_MEMCPY(_thingValues[i],b.field(p,ZT_CERTIFICATEOFOWNERSHIP_MAX_THING_VALUE_SIZE),ZT_CERTIFICATEOFOWNERSHIP_MAX_THING_VALUE_SIZE);
+ p += ZT_CERTIFICATEOFOWNERSHIP_MAX_THING_VALUE_SIZE;
+ }
+ }
+
+ _issuedTo.setTo(b.field(p,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH); p += ZT_ADDRESS_LENGTH;
+ _signedBy.setTo(b.field(p,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH); p += ZT_ADDRESS_LENGTH;
+ if (b[p++] == 1) {
+ if (b.template at<uint16_t>(p) != ZT_C25519_SIGNATURE_LEN)
+ throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_INVALID_CRYPTOGRAPHIC_TOKEN;
+ p += 2;
+ ZT_FAST_MEMCPY(_signature.data,b.field(p,ZT_C25519_SIGNATURE_LEN),ZT_C25519_SIGNATURE_LEN); p += ZT_C25519_SIGNATURE_LEN;
+ } else {
+ p += 2 + b.template at<uint16_t>(p);
+ }
+
+ p += 2 + b.template at<uint16_t>(p);
+ if (p > b.size())
+ throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_OVERFLOW;
+
+ return (p - startAt);
+ }
+
+ // Provides natural sort order by ID
+ inline bool operator<(const CertificateOfOwnership &coo) const { return (_id < coo._id); }
+
+ inline bool operator==(const CertificateOfOwnership &coo) const { return (memcmp(this,&coo,sizeof(CertificateOfOwnership)) == 0); }
+ inline bool operator!=(const CertificateOfOwnership &coo) const { return (memcmp(this,&coo,sizeof(CertificateOfOwnership)) != 0); }
+
+private:
+ bool _owns(const Thing &t,const void *v,unsigned int l) const;
+
+ uint64_t _networkId;
+ int64_t _ts;
+ uint64_t _flags;
+ uint32_t _id;
+ uint16_t _thingCount;
+ uint8_t _thingTypes[ZT_CERTIFICATEOFOWNERSHIP_MAX_THINGS];
+ uint8_t _thingValues[ZT_CERTIFICATEOFOWNERSHIP_MAX_THINGS][ZT_CERTIFICATEOFOWNERSHIP_MAX_THING_VALUE_SIZE];
+ Address _issuedTo;
+ Address _signedBy;
+ C25519::Signature _signature;
+};
+
+} // namespace ZeroTier
+
+#endif
diff --git a/node/Cluster.cpp b/node/Cluster.cpp
deleted file mode 100644
index f590ad1c..00000000
--- a/node/Cluster.cpp
+++ /dev/null
@@ -1,913 +0,0 @@
-/*
- * ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifdef ZT_ENABLE_CLUSTER
-
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <math.h>
-
-#include <map>
-#include <algorithm>
-#include <set>
-#include <utility>
-#include <list>
-#include <stdexcept>
-
-#include "../version.h"
-
-#include "Cluster.hpp"
-#include "RuntimeEnvironment.hpp"
-#include "MulticastGroup.hpp"
-#include "CertificateOfMembership.hpp"
-#include "Salsa20.hpp"
-#include "Poly1305.hpp"
-#include "Identity.hpp"
-#include "Topology.hpp"
-#include "Packet.hpp"
-#include "Switch.hpp"
-#include "Node.hpp"
-#include "Array.hpp"
-
-namespace ZeroTier {
-
-static inline double _dist3d(int x1,int y1,int z1,int x2,int y2,int z2)
- throw()
-{
- double dx = ((double)x2 - (double)x1);
- double dy = ((double)y2 - (double)y1);
- double dz = ((double)z2 - (double)z1);
- return sqrt((dx * dx) + (dy * dy) + (dz * dz));
-}
-
-// An entry in _ClusterSendQueue
-struct _ClusterSendQueueEntry
-{
- uint64_t timestamp;
- Address fromPeerAddress;
- Address toPeerAddress;
- // if we ever support larger transport MTUs this must be increased
- unsigned char data[ZT_CLUSTER_SEND_QUEUE_DATA_MAX];
- unsigned int len;
- bool unite;
-};
-
-// A multi-index map with entry memory pooling -- this allows our queue to
-// be O(log(N)) and is complex enough that it makes the code a lot cleaner
-// to break it out from Cluster.
-class _ClusterSendQueue
-{
-public:
- _ClusterSendQueue() :
- _poolCount(0) {}
- ~_ClusterSendQueue() {} // memory is automatically freed when _chunks is destroyed
-
- inline void enqueue(uint64_t now,const Address &from,const Address &to,const void *data,unsigned int len,bool unite)
- {
- if (len > ZT_CLUSTER_SEND_QUEUE_DATA_MAX)
- return;
-
- Mutex::Lock _l(_lock);
-
- // Delete oldest queue entry for this sender if this enqueue() would take them over the per-sender limit
- {
- std::set< std::pair<Address,_ClusterSendQueueEntry *> >::iterator qi(_bySrc.lower_bound(std::pair<Address,_ClusterSendQueueEntry *>(from,(_ClusterSendQueueEntry *)0)));
- std::set< std::pair<Address,_ClusterSendQueueEntry *> >::iterator oldest(qi);
- unsigned long countForSender = 0;
- while ((qi != _bySrc.end())&&(qi->first == from)) {
- if (qi->second->timestamp < oldest->second->timestamp)
- oldest = qi;
- ++countForSender;
- ++qi;
- }
- if (countForSender >= ZT_CLUSTER_MAX_QUEUE_PER_SENDER) {
- _byDest.erase(std::pair<Address,_ClusterSendQueueEntry *>(oldest->second->toPeerAddress,oldest->second));
- _pool[_poolCount++] = oldest->second;
- _bySrc.erase(oldest);
- }
- }
-
- _ClusterSendQueueEntry *e;
- if (_poolCount > 0) {
- e = _pool[--_poolCount];
- } else {
- if (_chunks.size() >= ZT_CLUSTER_MAX_QUEUE_CHUNKS)
- return; // queue is totally full!
- _chunks.push_back(Array<_ClusterSendQueueEntry,ZT_CLUSTER_QUEUE_CHUNK_SIZE>());
- e = &(_chunks.back().data[0]);
- for(unsigned int i=1;i<ZT_CLUSTER_QUEUE_CHUNK_SIZE;++i)
- _pool[_poolCount++] = &(_chunks.back().data[i]);
- }
-
- e->timestamp = now;
- e->fromPeerAddress = from;
- e->toPeerAddress = to;
- memcpy(e->data,data,len);
- e->len = len;
- e->unite = unite;
-
- _bySrc.insert(std::pair<Address,_ClusterSendQueueEntry *>(from,e));
- _byDest.insert(std::pair<Address,_ClusterSendQueueEntry *>(to,e));
- }
-
- inline void expire(uint64_t now)
- {
- Mutex::Lock _l(_lock);
- for(std::set< std::pair<Address,_ClusterSendQueueEntry *> >::iterator qi(_bySrc.begin());qi!=_bySrc.end();) {
- if ((now - qi->second->timestamp) > ZT_CLUSTER_QUEUE_EXPIRATION) {
- _byDest.erase(std::pair<Address,_ClusterSendQueueEntry *>(qi->second->toPeerAddress,qi->second));
- _pool[_poolCount++] = qi->second;
- _bySrc.erase(qi++);
- } else ++qi;
- }
- }
-
- /**
- * Get and dequeue entries for a given destination address
- *
- * After use these entries must be returned with returnToPool()!
- *
- * @param dest Destination address
- * @param results Array to fill with results
- * @param maxResults Size of results[] in pointers
- * @return Number of actual results returned
- */
- inline unsigned int getByDest(const Address &dest,_ClusterSendQueueEntry **results,unsigned int maxResults)
- {
- unsigned int count = 0;
- Mutex::Lock _l(_lock);
- std::set< std::pair<Address,_ClusterSendQueueEntry *> >::iterator qi(_byDest.lower_bound(std::pair<Address,_ClusterSendQueueEntry *>(dest,(_ClusterSendQueueEntry *)0)));
- while ((qi != _byDest.end())&&(qi->first == dest)) {
- _bySrc.erase(std::pair<Address,_ClusterSendQueueEntry *>(qi->second->fromPeerAddress,qi->second));
- results[count++] = qi->second;
- if (count == maxResults)
- break;
- _byDest.erase(qi++);
- }
- return count;
- }
-
- /**
- * Return entries to pool after use
- *
- * @param entries Array of entries
- * @param count Number of entries
- */
- inline void returnToPool(_ClusterSendQueueEntry **entries,unsigned int count)
- {
- Mutex::Lock _l(_lock);
- for(unsigned int i=0;i<count;++i)
- _pool[_poolCount++] = entries[i];
- }
-
-private:
- std::list< Array<_ClusterSendQueueEntry,ZT_CLUSTER_QUEUE_CHUNK_SIZE> > _chunks;
- _ClusterSendQueueEntry *_pool[ZT_CLUSTER_QUEUE_CHUNK_SIZE * ZT_CLUSTER_MAX_QUEUE_CHUNKS];
- unsigned long _poolCount;
- std::set< std::pair<Address,_ClusterSendQueueEntry *> > _bySrc;
- std::set< std::pair<Address,_ClusterSendQueueEntry *> > _byDest;
- Mutex _lock;
-};
-
-Cluster::Cluster(
- const RuntimeEnvironment *renv,
- uint16_t id,
- const std::vector<InetAddress> &zeroTierPhysicalEndpoints,
- int32_t x,
- int32_t y,
- int32_t z,
- void (*sendFunction)(void *,unsigned int,const void *,unsigned int),
- void *sendFunctionArg,
- int (*addressToLocationFunction)(void *,const struct sockaddr_storage *,int *,int *,int *),
- void *addressToLocationFunctionArg) :
- RR(renv),
- _sendQueue(new _ClusterSendQueue()),
- _sendFunction(sendFunction),
- _sendFunctionArg(sendFunctionArg),
- _addressToLocationFunction(addressToLocationFunction),
- _addressToLocationFunctionArg(addressToLocationFunctionArg),
- _x(x),
- _y(y),
- _z(z),
- _id(id),
- _zeroTierPhysicalEndpoints(zeroTierPhysicalEndpoints),
- _members(new _Member[ZT_CLUSTER_MAX_MEMBERS]),
- _lastFlushed(0),
- _lastCleanedRemotePeers(0),
- _lastCleanedQueue(0)
-{
- uint16_t stmp[ZT_SHA512_DIGEST_LEN / sizeof(uint16_t)];
-
- // Generate master secret by hashing the secret from our Identity key pair
- RR->identity.sha512PrivateKey(_masterSecret);
-
- // Generate our inbound message key, which is the master secret XORed with our ID and hashed twice
- memcpy(stmp,_masterSecret,sizeof(stmp));
- stmp[0] ^= Utils::hton(id);
- SHA512::hash(stmp,stmp,sizeof(stmp));
- SHA512::hash(stmp,stmp,sizeof(stmp));
- memcpy(_key,stmp,sizeof(_key));
- Utils::burn(stmp,sizeof(stmp));
-}
-
-Cluster::~Cluster()
-{
- Utils::burn(_masterSecret,sizeof(_masterSecret));
- Utils::burn(_key,sizeof(_key));
- delete [] _members;
- delete _sendQueue;
-}
-
-void Cluster::handleIncomingStateMessage(const void *msg,unsigned int len)
-{
- Buffer<ZT_CLUSTER_MAX_MESSAGE_LENGTH> dmsg;
- {
- // FORMAT: <[16] iv><[8] MAC><... data>
- if ((len < 24)||(len > ZT_CLUSTER_MAX_MESSAGE_LENGTH))
- return;
-
- // 16-byte IV: first 8 bytes XORed with key, last 8 bytes used as Salsa20 64-bit IV
- char keytmp[32];
- memcpy(keytmp,_key,32);
- for(int i=0;i<8;++i)
- keytmp[i] ^= reinterpret_cast<const char *>(msg)[i];
- Salsa20 s20(keytmp,256,reinterpret_cast<const char *>(msg) + 8);
- Utils::burn(keytmp,sizeof(keytmp));
-
- // One-time-use Poly1305 key from first 32 bytes of Salsa20 keystream (as per DJB/NaCl "standard")
- char polykey[ZT_POLY1305_KEY_LEN];
- memset(polykey,0,sizeof(polykey));
- s20.encrypt12(polykey,polykey,sizeof(polykey));
-
- // Compute 16-byte MAC
- char mac[ZT_POLY1305_MAC_LEN];
- Poly1305::compute(mac,reinterpret_cast<const char *>(msg) + 24,len - 24,polykey);
-
- // Check first 8 bytes of MAC against 64-bit MAC in stream
- if (!Utils::secureEq(mac,reinterpret_cast<const char *>(msg) + 16,8))
- return;
-
- // Decrypt!
- dmsg.setSize(len - 24);
- s20.decrypt12(reinterpret_cast<const char *>(msg) + 24,const_cast<void *>(dmsg.data()),dmsg.size());
- }
-
- if (dmsg.size() < 4)
- return;
- const uint16_t fromMemberId = dmsg.at<uint16_t>(0);
- unsigned int ptr = 2;
- if (fromMemberId == _id) // sanity check: we don't talk to ourselves
- return;
- const uint16_t toMemberId = dmsg.at<uint16_t>(ptr);
- ptr += 2;
- if (toMemberId != _id) // sanity check: message not for us?
- return;
-
- { // make sure sender is actually considered a member
- Mutex::Lock _l3(_memberIds_m);
- if (std::find(_memberIds.begin(),_memberIds.end(),fromMemberId) == _memberIds.end())
- return;
- }
-
- try {
- while (ptr < dmsg.size()) {
- const unsigned int mlen = dmsg.at<uint16_t>(ptr); ptr += 2;
- const unsigned int nextPtr = ptr + mlen;
- if (nextPtr > dmsg.size())
- break;
-
- int mtype = -1;
- try {
- switch((StateMessageType)(mtype = (int)dmsg[ptr++])) {
- default:
- break;
-
- case CLUSTER_MESSAGE_ALIVE: {
- _Member &m = _members[fromMemberId];
- Mutex::Lock mlck(m.lock);
- ptr += 7; // skip version stuff, not used yet
- m.x = dmsg.at<int32_t>(ptr); ptr += 4;
- m.y = dmsg.at<int32_t>(ptr); ptr += 4;
- m.z = dmsg.at<int32_t>(ptr); ptr += 4;
- ptr += 8; // skip local clock, not used
- m.load = dmsg.at<uint64_t>(ptr); ptr += 8;
- m.peers = dmsg.at<uint64_t>(ptr); ptr += 8;
- ptr += 8; // skip flags, unused
-#ifdef ZT_TRACE
- std::string addrs;
-#endif
- unsigned int physicalAddressCount = dmsg[ptr++];
- m.zeroTierPhysicalEndpoints.clear();
- for(unsigned int i=0;i<physicalAddressCount;++i) {
- m.zeroTierPhysicalEndpoints.push_back(InetAddress());
- ptr += m.zeroTierPhysicalEndpoints.back().deserialize(dmsg,ptr);
- if (!(m.zeroTierPhysicalEndpoints.back())) {
- m.zeroTierPhysicalEndpoints.pop_back();
- }
-#ifdef ZT_TRACE
- else {
- if (addrs.length() > 0)
- addrs.push_back(',');
- addrs.append(m.zeroTierPhysicalEndpoints.back().toString());
- }
-#endif
- }
-#ifdef ZT_TRACE
- if ((RR->node->now() - m.lastReceivedAliveAnnouncement) >= ZT_CLUSTER_TIMEOUT) {
- TRACE("[%u] I'm alive! peers close to %d,%d,%d can be redirected to: %s",(unsigned int)fromMemberId,m.x,m.y,m.z,addrs.c_str());
- }
-#endif
- m.lastReceivedAliveAnnouncement = RR->node->now();
- } break;
-
- case CLUSTER_MESSAGE_HAVE_PEER: {
- Identity id;
- ptr += id.deserialize(dmsg,ptr);
- if (id) {
- RR->topology->saveIdentity(id);
-
- {
- Mutex::Lock _l(_remotePeers_m);
- _remotePeers[std::pair<Address,unsigned int>(id.address(),(unsigned int)fromMemberId)] = RR->node->now();
- }
-
- _ClusterSendQueueEntry *q[16384]; // 16384 is "tons"
- unsigned int qc = _sendQueue->getByDest(id.address(),q,16384);
- for(unsigned int i=0;i<qc;++i)
- this->sendViaCluster(q[i]->fromPeerAddress,q[i]->toPeerAddress,q[i]->data,q[i]->len,q[i]->unite);
- _sendQueue->returnToPool(q,qc);
-
- TRACE("[%u] has %s (retried %u queued sends)",(unsigned int)fromMemberId,id.address().toString().c_str(),qc);
- }
- } break;
-
- case CLUSTER_MESSAGE_WANT_PEER: {
- const Address zeroTierAddress(dmsg.field(ptr,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH); ptr += ZT_ADDRESS_LENGTH;
- SharedPtr<Peer> peer(RR->topology->getPeerNoCache(zeroTierAddress));
- if ( (peer) && (peer->hasClusterOptimalPath(RR->node->now())) ) {
- Buffer<1024> buf;
- peer->identity().serialize(buf);
- Mutex::Lock _l2(_members[fromMemberId].lock);
- _send(fromMemberId,CLUSTER_MESSAGE_HAVE_PEER,buf.data(),buf.size());
- }
- } break;
-
- case CLUSTER_MESSAGE_REMOTE_PACKET: {
- const unsigned int plen = dmsg.at<uint16_t>(ptr); ptr += 2;
- if (plen) {
- Packet remotep(dmsg.field(ptr,plen),plen); ptr += plen;
- //TRACE("remote %s from %s via %u (%u bytes)",Packet::verbString(remotep.verb()),remotep.source().toString().c_str(),fromMemberId,plen);
- switch(remotep.verb()) {
- case Packet::VERB_WHOIS: _doREMOTE_WHOIS(fromMemberId,remotep); break;
- case Packet::VERB_MULTICAST_GATHER: _doREMOTE_MULTICAST_GATHER(fromMemberId,remotep); break;
- default: break; // ignore things we don't care about across cluster
- }
- }
- } break;
-
- case CLUSTER_MESSAGE_PROXY_UNITE: {
- const Address localPeerAddress(dmsg.field(ptr,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH); ptr += ZT_ADDRESS_LENGTH;
- const Address remotePeerAddress(dmsg.field(ptr,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH); ptr += ZT_ADDRESS_LENGTH;
- const unsigned int numRemotePeerPaths = dmsg[ptr++];
- InetAddress remotePeerPaths[256]; // size is 8-bit, so 256 is max
- for(unsigned int i=0;i<numRemotePeerPaths;++i)
- ptr += remotePeerPaths[i].deserialize(dmsg,ptr);
-
- TRACE("[%u] requested that we unite local %s with remote %s",(unsigned int)fromMemberId,localPeerAddress.toString().c_str(),remotePeerAddress.toString().c_str());
-
- const uint64_t now = RR->node->now();
- SharedPtr<Peer> localPeer(RR->topology->getPeerNoCache(localPeerAddress));
- if ((localPeer)&&(numRemotePeerPaths > 0)) {
- InetAddress bestLocalV4,bestLocalV6;
- localPeer->getBestActiveAddresses(now,bestLocalV4,bestLocalV6);
-
- InetAddress bestRemoteV4,bestRemoteV6;
- for(unsigned int i=0;i<numRemotePeerPaths;++i) {
- if ((bestRemoteV4)&&(bestRemoteV6))
- break;
- switch(remotePeerPaths[i].ss_family) {
- case AF_INET:
- if (!bestRemoteV4)
- bestRemoteV4 = remotePeerPaths[i];
- break;
- case AF_INET6:
- if (!bestRemoteV6)
- bestRemoteV6 = remotePeerPaths[i];
- break;
- }
- }
-
- Packet rendezvousForLocal(localPeerAddress,RR->identity.address(),Packet::VERB_RENDEZVOUS);
- rendezvousForLocal.append((uint8_t)0);
- remotePeerAddress.appendTo(rendezvousForLocal);
-
- Buffer<2048> rendezvousForRemote;
- remotePeerAddress.appendTo(rendezvousForRemote);
- rendezvousForRemote.append((uint8_t)Packet::VERB_RENDEZVOUS);
- rendezvousForRemote.addSize(2); // space for actual packet payload length
- rendezvousForRemote.append((uint8_t)0); // flags == 0
- localPeerAddress.appendTo(rendezvousForRemote);
-
- bool haveMatch = false;
- if ((bestLocalV6)&&(bestRemoteV6)) {
- haveMatch = true;
-
- rendezvousForLocal.append((uint16_t)bestRemoteV6.port());
- rendezvousForLocal.append((uint8_t)16);
- rendezvousForLocal.append(bestRemoteV6.rawIpData(),16);
-
- rendezvousForRemote.append((uint16_t)bestLocalV6.port());
- rendezvousForRemote.append((uint8_t)16);
- rendezvousForRemote.append(bestLocalV6.rawIpData(),16);
- rendezvousForRemote.setAt<uint16_t>(ZT_ADDRESS_LENGTH + 1,(uint16_t)(9 + 16));
- } else if ((bestLocalV4)&&(bestRemoteV4)) {
- haveMatch = true;
-
- rendezvousForLocal.append((uint16_t)bestRemoteV4.port());
- rendezvousForLocal.append((uint8_t)4);
- rendezvousForLocal.append(bestRemoteV4.rawIpData(),4);
-
- rendezvousForRemote.append((uint16_t)bestLocalV4.port());
- rendezvousForRemote.append((uint8_t)4);
- rendezvousForRemote.append(bestLocalV4.rawIpData(),4);
- rendezvousForRemote.setAt<uint16_t>(ZT_ADDRESS_LENGTH + 1,(uint16_t)(9 + 4));
- }
-
- if (haveMatch) {
- {
- Mutex::Lock _l2(_members[fromMemberId].lock);
- _send(fromMemberId,CLUSTER_MESSAGE_PROXY_SEND,rendezvousForRemote.data(),rendezvousForRemote.size());
- }
- RR->sw->send(rendezvousForLocal,true,0);
- }
- }
- } break;
-
- case CLUSTER_MESSAGE_PROXY_SEND: {
- const Address rcpt(dmsg.field(ptr,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH); ptr += ZT_ADDRESS_LENGTH;
- const Packet::Verb verb = (Packet::Verb)dmsg[ptr++];
- const unsigned int len = dmsg.at<uint16_t>(ptr); ptr += 2;
- Packet outp(rcpt,RR->identity.address(),verb);
- outp.append(dmsg.field(ptr,len),len); ptr += len;
- RR->sw->send(outp,true,0);
- //TRACE("[%u] proxy send %s to %s length %u",(unsigned int)fromMemberId,Packet::verbString(verb),rcpt.toString().c_str(),len);
- } break;
- }
- } catch ( ... ) {
- TRACE("invalid message of size %u type %d (inner decode), discarding",mlen,mtype);
- // drop invalids
- }
-
- ptr = nextPtr;
- }
- } catch ( ... ) {
- TRACE("invalid message (outer loop), discarding");
- // drop invalids
- }
-}
-
-void Cluster::broadcastHavePeer(const Identity &id)
-{
- Buffer<1024> buf;
- id.serialize(buf);
- Mutex::Lock _l(_memberIds_m);
- for(std::vector<uint16_t>::const_iterator mid(_memberIds.begin());mid!=_memberIds.end();++mid) {
- Mutex::Lock _l2(_members[*mid].lock);
- _send(*mid,CLUSTER_MESSAGE_HAVE_PEER,buf.data(),buf.size());
- }
-}
-
-void Cluster::sendViaCluster(const Address &fromPeerAddress,const Address &toPeerAddress,const void *data,unsigned int len,bool unite)
-{
- if (len > ZT_PROTO_MAX_PACKET_LENGTH) // sanity check
- return;
-
- const uint64_t now = RR->node->now();
-
- uint64_t mostRecentTs = 0;
- unsigned int mostRecentMemberId = 0xffffffff;
- {
- Mutex::Lock _l2(_remotePeers_m);
- std::map< std::pair<Address,unsigned int>,uint64_t >::const_iterator rpe(_remotePeers.lower_bound(std::pair<Address,unsigned int>(toPeerAddress,0)));
- for(;;) {
- if ((rpe == _remotePeers.end())||(rpe->first.first != toPeerAddress))
- break;
- else if (rpe->second > mostRecentTs) {
- mostRecentTs = rpe->second;
- mostRecentMemberId = rpe->first.second;
- }
- ++rpe;
- }
- }
-
- const uint64_t age = now - mostRecentTs;
- if (age >= (ZT_PEER_ACTIVITY_TIMEOUT / 3)) {
- const bool enqueueAndWait = ((age >= ZT_PEER_ACTIVITY_TIMEOUT)||(mostRecentMemberId > 0xffff));
-
- // Poll everyone with WANT_PEER if the age of our most recent entry is
- // approaching expiration (or has expired, or does not exist).
- char tmp[ZT_ADDRESS_LENGTH];
- toPeerAddress.copyTo(tmp,ZT_ADDRESS_LENGTH);
- {
- Mutex::Lock _l(_memberIds_m);
- for(std::vector<uint16_t>::const_iterator mid(_memberIds.begin());mid!=_memberIds.end();++mid) {
- Mutex::Lock _l2(_members[*mid].lock);
- _send(*mid,CLUSTER_MESSAGE_WANT_PEER,tmp,ZT_ADDRESS_LENGTH);
- }
- }
-
- // If there isn't a good place to send via, then enqueue this for retrying
- // later and return after having broadcasted a WANT_PEER.
- if (enqueueAndWait) {
- TRACE("sendViaCluster %s -> %s enqueueing to wait for HAVE_PEER",fromPeerAddress.toString().c_str(),toPeerAddress.toString().c_str());
- _sendQueue->enqueue(now,fromPeerAddress,toPeerAddress,data,len,unite);
- return;
- }
- }
-
- Buffer<1024> buf;
- if (unite) {
- InetAddress v4,v6;
- if (fromPeerAddress) {
- SharedPtr<Peer> fromPeer(RR->topology->getPeerNoCache(fromPeerAddress));
- if (fromPeer)
- fromPeer->getBestActiveAddresses(now,v4,v6);
- }
- uint8_t addrCount = 0;
- if (v4)
- ++addrCount;
- if (v6)
- ++addrCount;
- if (addrCount) {
- toPeerAddress.appendTo(buf);
- fromPeerAddress.appendTo(buf);
- buf.append(addrCount);
- if (v4)
- v4.serialize(buf);
- if (v6)
- v6.serialize(buf);
- }
- }
-
- {
- Mutex::Lock _l2(_members[mostRecentMemberId].lock);
- if (buf.size() > 0)
- _send(mostRecentMemberId,CLUSTER_MESSAGE_PROXY_UNITE,buf.data(),buf.size());
-
- for(std::vector<InetAddress>::const_iterator i1(_zeroTierPhysicalEndpoints.begin());i1!=_zeroTierPhysicalEndpoints.end();++i1) {
- for(std::vector<InetAddress>::const_iterator i2(_members[mostRecentMemberId].zeroTierPhysicalEndpoints.begin());i2!=_members[mostRecentMemberId].zeroTierPhysicalEndpoints.end();++i2) {
- if (i1->ss_family == i2->ss_family) {
- TRACE("sendViaCluster relaying %u bytes from %s to %s by way of %u (%s->%s)",len,fromPeerAddress.toString().c_str(),toPeerAddress.toString().c_str(),(unsigned int)mostRecentMemberId,i1->toString().c_str(),i2->toString().c_str());
- RR->node->putPacket(*i1,*i2,data,len);
- return;
- }
- }
- }
-
- TRACE("sendViaCluster relaying %u bytes from %s to %s by way of %u failed: no common endpoints with the same address family!",len,fromPeerAddress.toString().c_str(),toPeerAddress.toString().c_str(),(unsigned int)mostRecentMemberId);
- return;
- }
-}
-
-void Cluster::sendDistributedQuery(const Packet &pkt)
-{
- Buffer<4096> buf;
- buf.append((uint16_t)pkt.size());
- buf.append(pkt.data(),pkt.size());
- Mutex::Lock _l(_memberIds_m);
- for(std::vector<uint16_t>::const_iterator mid(_memberIds.begin());mid!=_memberIds.end();++mid) {
- Mutex::Lock _l2(_members[*mid].lock);
- _send(*mid,CLUSTER_MESSAGE_REMOTE_PACKET,buf.data(),buf.size());
- }
-}
-
-void Cluster::doPeriodicTasks()
-{
- const uint64_t now = RR->node->now();
-
- if ((now - _lastFlushed) >= ZT_CLUSTER_FLUSH_PERIOD) {
- _lastFlushed = now;
-
- Mutex::Lock _l(_memberIds_m);
- for(std::vector<uint16_t>::const_iterator mid(_memberIds.begin());mid!=_memberIds.end();++mid) {
- Mutex::Lock _l2(_members[*mid].lock);
-
- if ((now - _members[*mid].lastAnnouncedAliveTo) >= ((ZT_CLUSTER_TIMEOUT / 2) - 1000)) {
- _members[*mid].lastAnnouncedAliveTo = now;
-
- Buffer<2048> alive;
- alive.append((uint16_t)ZEROTIER_ONE_VERSION_MAJOR);
- alive.append((uint16_t)ZEROTIER_ONE_VERSION_MINOR);
- alive.append((uint16_t)ZEROTIER_ONE_VERSION_REVISION);
- alive.append((uint8_t)ZT_PROTO_VERSION);
- if (_addressToLocationFunction) {
- alive.append((int32_t)_x);
- alive.append((int32_t)_y);
- alive.append((int32_t)_z);
- } else {
- alive.append((int32_t)0);
- alive.append((int32_t)0);
- alive.append((int32_t)0);
- }
- alive.append((uint64_t)now);
- alive.append((uint64_t)0); // TODO: compute and send load average
- alive.append((uint64_t)RR->topology->countActive(now));
- alive.append((uint64_t)0); // unused/reserved flags
- alive.append((uint8_t)_zeroTierPhysicalEndpoints.size());
- for(std::vector<InetAddress>::const_iterator pe(_zeroTierPhysicalEndpoints.begin());pe!=_zeroTierPhysicalEndpoints.end();++pe)
- pe->serialize(alive);
- _send(*mid,CLUSTER_MESSAGE_ALIVE,alive.data(),alive.size());
- }
-
- _flush(*mid);
- }
- }
-
- if ((now - _lastCleanedRemotePeers) >= (ZT_PEER_ACTIVITY_TIMEOUT * 2)) {
- _lastCleanedRemotePeers = now;
-
- Mutex::Lock _l(_remotePeers_m);
- for(std::map< std::pair<Address,unsigned int>,uint64_t >::iterator rp(_remotePeers.begin());rp!=_remotePeers.end();) {
- if ((now - rp->second) >= ZT_PEER_ACTIVITY_TIMEOUT)
- _remotePeers.erase(rp++);
- else ++rp;
- }
- }
-
- if ((now - _lastCleanedQueue) >= ZT_CLUSTER_QUEUE_EXPIRATION) {
- _lastCleanedQueue = now;
- _sendQueue->expire(now);
- }
-}
-
-void Cluster::addMember(uint16_t memberId)
-{
- if ((memberId >= ZT_CLUSTER_MAX_MEMBERS)||(memberId == _id))
- return;
-
- Mutex::Lock _l2(_members[memberId].lock);
-
- {
- Mutex::Lock _l(_memberIds_m);
- if (std::find(_memberIds.begin(),_memberIds.end(),memberId) != _memberIds.end())
- return;
- _memberIds.push_back(memberId);
- std::sort(_memberIds.begin(),_memberIds.end());
- }
-
- _members[memberId].clear();
-
- // Generate this member's message key from the master and its ID
- uint16_t stmp[ZT_SHA512_DIGEST_LEN / sizeof(uint16_t)];
- memcpy(stmp,_masterSecret,sizeof(stmp));
- stmp[0] ^= Utils::hton(memberId);
- SHA512::hash(stmp,stmp,sizeof(stmp));
- SHA512::hash(stmp,stmp,sizeof(stmp));
- memcpy(_members[memberId].key,stmp,sizeof(_members[memberId].key));
- Utils::burn(stmp,sizeof(stmp));
-
- // Prepare q
- _members[memberId].q.clear();
- char iv[16];
- Utils::getSecureRandom(iv,16);
- _members[memberId].q.append(iv,16);
- _members[memberId].q.addSize(8); // room for MAC
- _members[memberId].q.append((uint16_t)_id);
- _members[memberId].q.append((uint16_t)memberId);
-}
-
-void Cluster::removeMember(uint16_t memberId)
-{
- Mutex::Lock _l(_memberIds_m);
- std::vector<uint16_t> newMemberIds;
- for(std::vector<uint16_t>::const_iterator mid(_memberIds.begin());mid!=_memberIds.end();++mid) {
- if (*mid != memberId)
- newMemberIds.push_back(*mid);
- }
- _memberIds = newMemberIds;
-}
-
-bool Cluster::findBetterEndpoint(InetAddress &redirectTo,const Address &peerAddress,const InetAddress &peerPhysicalAddress,bool offload)
-{
- if (_addressToLocationFunction) {
- // Pick based on location if it can be determined
- int px = 0,py = 0,pz = 0;
- if (_addressToLocationFunction(_addressToLocationFunctionArg,reinterpret_cast<const struct sockaddr_storage *>(&peerPhysicalAddress),&px,&py,&pz) == 0) {
- TRACE("no geolocation data for %s",peerPhysicalAddress.toIpString().c_str());
- return false;
- }
-
- // Find member closest to this peer
- const uint64_t now = RR->node->now();
- std::vector<InetAddress> best;
- const double currentDistance = _dist3d(_x,_y,_z,px,py,pz);
- double bestDistance = (offload ? 2147483648.0 : currentDistance);
- unsigned int bestMember = _id;
- {
- Mutex::Lock _l(_memberIds_m);
- for(std::vector<uint16_t>::const_iterator mid(_memberIds.begin());mid!=_memberIds.end();++mid) {
- _Member &m = _members[*mid];
- Mutex::Lock _ml(m.lock);
-
- // Consider member if it's alive and has sent us a location and one or more physical endpoints to send peers to
- if ( ((now - m.lastReceivedAliveAnnouncement) < ZT_CLUSTER_TIMEOUT) && ((m.x != 0)||(m.y != 0)||(m.z != 0)) && (m.zeroTierPhysicalEndpoints.size() > 0) ) {
- const double mdist = _dist3d(m.x,m.y,m.z,px,py,pz);
- if (mdist < bestDistance) {
- bestDistance = mdist;
- bestMember = *mid;
- best = m.zeroTierPhysicalEndpoints;
- }
- }
- }
- }
-
- // Redirect to a closer member if it has a ZeroTier endpoint address in the same ss_family
- for(std::vector<InetAddress>::const_iterator a(best.begin());a!=best.end();++a) {
- if (a->ss_family == peerPhysicalAddress.ss_family) {
- TRACE("%s at [%d,%d,%d] is %f from us but %f from %u, can redirect to %s",peerAddress.toString().c_str(),px,py,pz,currentDistance,bestDistance,bestMember,a->toString().c_str());
- redirectTo = *a;
- return true;
- }
- }
- TRACE("%s at [%d,%d,%d] is %f from us, no better endpoints found",peerAddress.toString().c_str(),px,py,pz,currentDistance);
- return false;
- } else {
- // TODO: pick based on load if no location info?
- return false;
- }
-}
-
-void Cluster::status(ZT_ClusterStatus &status) const
-{
- const uint64_t now = RR->node->now();
- memset(&status,0,sizeof(ZT_ClusterStatus));
-
- status.myId = _id;
-
- {
- ZT_ClusterMemberStatus *const s = &(status.members[status.clusterSize++]);
- s->id = _id;
- s->alive = 1;
- s->x = _x;
- s->y = _y;
- s->z = _z;
- s->load = 0; // TODO
- s->peers = RR->topology->countActive(now);
- for(std::vector<InetAddress>::const_iterator ep(_zeroTierPhysicalEndpoints.begin());ep!=_zeroTierPhysicalEndpoints.end();++ep) {
- if (s->numZeroTierPhysicalEndpoints >= ZT_CLUSTER_MAX_ZT_PHYSICAL_ADDRESSES) // sanity check
- break;
- memcpy(&(s->zeroTierPhysicalEndpoints[s->numZeroTierPhysicalEndpoints++]),&(*ep),sizeof(struct sockaddr_storage));
- }
- }
-
- {
- Mutex::Lock _l1(_memberIds_m);
- for(std::vector<uint16_t>::const_iterator mid(_memberIds.begin());mid!=_memberIds.end();++mid) {
- if (status.clusterSize >= ZT_CLUSTER_MAX_MEMBERS) // sanity check
- break;
-
- _Member &m = _members[*mid];
- Mutex::Lock ml(m.lock);
-
- ZT_ClusterMemberStatus *const s = &(status.members[status.clusterSize++]);
- s->id = *mid;
- s->msSinceLastHeartbeat = (unsigned int)std::min((uint64_t)(~((unsigned int)0)),(now - m.lastReceivedAliveAnnouncement));
- s->alive = (s->msSinceLastHeartbeat < ZT_CLUSTER_TIMEOUT) ? 1 : 0;
- s->x = m.x;
- s->y = m.y;
- s->z = m.z;
- s->load = m.load;
- s->peers = m.peers;
- for(std::vector<InetAddress>::const_iterator ep(m.zeroTierPhysicalEndpoints.begin());ep!=m.zeroTierPhysicalEndpoints.end();++ep) {
- if (s->numZeroTierPhysicalEndpoints >= ZT_CLUSTER_MAX_ZT_PHYSICAL_ADDRESSES) // sanity check
- break;
- memcpy(&(s->zeroTierPhysicalEndpoints[s->numZeroTierPhysicalEndpoints++]),&(*ep),sizeof(struct sockaddr_storage));
- }
- }
- }
-}
-
-void Cluster::_send(uint16_t memberId,StateMessageType type,const void *msg,unsigned int len)
-{
- if ((len + 3) > (ZT_CLUSTER_MAX_MESSAGE_LENGTH - (24 + 2 + 2))) // sanity check
- return;
- _Member &m = _members[memberId];
- // assumes m.lock is locked!
- if ((m.q.size() + len + 3) > ZT_CLUSTER_MAX_MESSAGE_LENGTH)
- _flush(memberId);
- m.q.append((uint16_t)(len + 1));
- m.q.append((uint8_t)type);
- m.q.append(msg,len);
-}
-
-void Cluster::_flush(uint16_t memberId)
-{
- _Member &m = _members[memberId];
- // assumes m.lock is locked!
- if (m.q.size() > (24 + 2 + 2)) { // 16-byte IV + 8-byte MAC + 2 byte from-member-ID + 2 byte to-member-ID
- // Create key from member's key and IV
- char keytmp[32];
- memcpy(keytmp,m.key,32);
- for(int i=0;i<8;++i)
- keytmp[i] ^= m.q[i];
- Salsa20 s20(keytmp,256,m.q.field(8,8));
- Utils::burn(keytmp,sizeof(keytmp));
-
- // One-time-use Poly1305 key from first 32 bytes of Salsa20 keystream (as per DJB/NaCl "standard")
- char polykey[ZT_POLY1305_KEY_LEN];
- memset(polykey,0,sizeof(polykey));
- s20.encrypt12(polykey,polykey,sizeof(polykey));
-
- // Encrypt m.q in place
- s20.encrypt12(reinterpret_cast<const char *>(m.q.data()) + 24,const_cast<char *>(reinterpret_cast<const char *>(m.q.data())) + 24,m.q.size() - 24);
-
- // Add MAC for authentication (encrypt-then-MAC)
- char mac[ZT_POLY1305_MAC_LEN];
- Poly1305::compute(mac,reinterpret_cast<const char *>(m.q.data()) + 24,m.q.size() - 24,polykey);
- memcpy(m.q.field(16,8),mac,8);
-
- // Send!
- _sendFunction(_sendFunctionArg,memberId,m.q.data(),m.q.size());
-
- // Prepare for more
- m.q.clear();
- char iv[16];
- Utils::getSecureRandom(iv,16);
- m.q.append(iv,16);
- m.q.addSize(8); // room for MAC
- m.q.append((uint16_t)_id); // from member ID
- m.q.append((uint16_t)memberId); // to member ID
- }
-}
-
-void Cluster::_doREMOTE_WHOIS(uint64_t fromMemberId,const Packet &remotep)
-{
- if (remotep.payloadLength() >= ZT_ADDRESS_LENGTH) {
- Identity queried(RR->topology->getIdentity(Address(remotep.payload(),ZT_ADDRESS_LENGTH)));
- if (queried) {
- Buffer<1024> routp;
- remotep.source().appendTo(routp);
- routp.append((uint8_t)Packet::VERB_OK);
- routp.addSize(2); // space for length
- routp.append((uint8_t)Packet::VERB_WHOIS);
- routp.append(remotep.packetId());
- queried.serialize(routp);
- routp.setAt<uint16_t>(ZT_ADDRESS_LENGTH + 1,(uint16_t)(routp.size() - ZT_ADDRESS_LENGTH - 3));
-
- TRACE("responding to remote WHOIS from %s @ %u with identity of %s",remotep.source().toString().c_str(),(unsigned int)fromMemberId,queried.address().toString().c_str());
- Mutex::Lock _l2(_members[fromMemberId].lock);
- _send(fromMemberId,CLUSTER_MESSAGE_PROXY_SEND,routp.data(),routp.size());
- }
- }
-}
-
-void Cluster::_doREMOTE_MULTICAST_GATHER(uint64_t fromMemberId,const Packet &remotep)
-{
- const uint64_t nwid = remotep.at<uint64_t>(ZT_PROTO_VERB_MULTICAST_GATHER_IDX_NETWORK_ID);
- const MulticastGroup mg(MAC(remotep.field(ZT_PROTO_VERB_MULTICAST_GATHER_IDX_MAC,6),6),remotep.at<uint32_t>(ZT_PROTO_VERB_MULTICAST_GATHER_IDX_ADI));
- unsigned int gatherLimit = remotep.at<uint32_t>(ZT_PROTO_VERB_MULTICAST_GATHER_IDX_GATHER_LIMIT);
- const Address remotePeerAddress(remotep.source());
-
- if (gatherLimit) {
- Buffer<ZT_PROTO_MAX_PACKET_LENGTH> routp;
- remotePeerAddress.appendTo(routp);
- routp.append((uint8_t)Packet::VERB_OK);
- routp.addSize(2); // space for length
- routp.append((uint8_t)Packet::VERB_MULTICAST_GATHER);
- routp.append(remotep.packetId());
- routp.append(nwid);
- mg.mac().appendTo(routp);
- routp.append((uint32_t)mg.adi());
-
- if (gatherLimit > ((ZT_CLUSTER_MAX_MESSAGE_LENGTH - 80) / 5))
- gatherLimit = ((ZT_CLUSTER_MAX_MESSAGE_LENGTH - 80) / 5);
- if (RR->mc->gather(remotePeerAddress,nwid,mg,routp,gatherLimit)) {
- routp.setAt<uint16_t>(ZT_ADDRESS_LENGTH + 1,(uint16_t)(routp.size() - ZT_ADDRESS_LENGTH - 3));
-
- TRACE("responding to remote MULTICAST_GATHER from %s @ %u with %u bytes",remotePeerAddress.toString().c_str(),(unsigned int)fromMemberId,routp.size());
- Mutex::Lock _l2(_members[fromMemberId].lock);
- _send(fromMemberId,CLUSTER_MESSAGE_PROXY_SEND,routp.data(),routp.size());
- }
- }
-}
-
-} // namespace ZeroTier
-
-#endif // ZT_ENABLE_CLUSTER
diff --git a/node/Cluster.hpp b/node/Cluster.hpp
deleted file mode 100644
index dafbf425..00000000
--- a/node/Cluster.hpp
+++ /dev/null
@@ -1,406 +0,0 @@
-/*
- * ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef ZT_CLUSTER_HPP
-#define ZT_CLUSTER_HPP
-
-#ifdef ZT_ENABLE_CLUSTER
-
-#include <map>
-
-#include "Constants.hpp"
-#include "../include/ZeroTierOne.h"
-#include "Address.hpp"
-#include "InetAddress.hpp"
-#include "SHA512.hpp"
-#include "Utils.hpp"
-#include "Buffer.hpp"
-#include "Mutex.hpp"
-#include "SharedPtr.hpp"
-#include "Hashtable.hpp"
-#include "Packet.hpp"
-#include "SharedPtr.hpp"
-
-/**
- * Timeout for cluster members being considered "alive"
- *
- * A cluster member is considered dead and will no longer have peers
- * redirected to it if we have not heard a heartbeat in this long.
- */
-#define ZT_CLUSTER_TIMEOUT 5000
-
-/**
- * Desired period between doPeriodicTasks() in milliseconds
- */
-#define ZT_CLUSTER_PERIODIC_TASK_PERIOD 20
-
-/**
- * How often to flush outgoing message queues (maximum interval)
- */
-#define ZT_CLUSTER_FLUSH_PERIOD ZT_CLUSTER_PERIODIC_TASK_PERIOD
-
-/**
- * Maximum number of queued outgoing packets per sender address
- */
-#define ZT_CLUSTER_MAX_QUEUE_PER_SENDER 16
-
-/**
- * Expiration time for send queue entries
- */
-#define ZT_CLUSTER_QUEUE_EXPIRATION 3000
-
-/**
- * Chunk size for allocating queue entries
- *
- * Queue entries are allocated in chunks of this many and are added to a pool.
- * ZT_CLUSTER_MAX_QUEUE_GLOBAL must be evenly divisible by this.
- */
-#define ZT_CLUSTER_QUEUE_CHUNK_SIZE 32
-
-/**
- * Maximum number of chunks to ever allocate
- *
- * This is a global sanity limit to prevent resource exhaustion attacks. It
- * works out to about 600mb of RAM. You'll never see this on a normal edge
- * node. We're unlikely to see this on a root server unless someone is DOSing
- * us. In that case cluster relaying will be affected but other functions
- * should continue to operate normally.
- */
-#define ZT_CLUSTER_MAX_QUEUE_CHUNKS 8194
-
-/**
- * Max data per queue entry
- */
-#define ZT_CLUSTER_SEND_QUEUE_DATA_MAX 1500
-
-namespace ZeroTier {
-
-class RuntimeEnvironment;
-class MulticastGroup;
-class Peer;
-class Identity;
-
-// Internal class implemented inside Cluster.cpp
-class _ClusterSendQueue;
-
-/**
- * Multi-homing cluster state replication and packet relaying
- *
- * Multi-homing means more than one node sharing the same ZeroTier identity.
- * There is nothing in the protocol to prevent this, but to make it work well
- * requires the devices sharing an identity to cooperate and share some
- * information.
- *
- * There are three use cases we want to fulfill:
- *
- * (1) Multi-homing of root servers with handoff for efficient routing,
- * HA, and load balancing across many commodity nodes.
- * (2) Multi-homing of network controllers for the same reason.
- * (3) Multi-homing of nodes on virtual networks, such as domain servers
- * and other important endpoints.
- *
- * These use cases are in order of escalating difficulty. The initial
- * version of Cluster is aimed at satisfying the first, though you are
- * free to try #2 and #3.
- */
-class Cluster
-{
-public:
- /**
- * State message types
- */
- enum StateMessageType
- {
- CLUSTER_MESSAGE_NOP = 0,
-
- /**
- * This cluster member is alive:
- * <[2] version minor>
- * <[2] version major>
- * <[2] version revision>
- * <[1] protocol version>
- * <[4] X location (signed 32-bit)>
- * <[4] Y location (signed 32-bit)>
- * <[4] Z location (signed 32-bit)>
- * <[8] local clock at this member>
- * <[8] load average>
- * <[8] number of peers>
- * <[8] flags (currently unused, must be zero)>
- * <[1] number of preferred ZeroTier endpoints>
- * <[...] InetAddress(es) of preferred ZeroTier endpoint(s)>
- *
- * Cluster members constantly broadcast an alive heartbeat and will only
- * receive peer redirects if they've done so within the timeout.
- */
- CLUSTER_MESSAGE_ALIVE = 1,
-
- /**
- * Cluster member has this peer:
- * <[...] serialized identity of peer>
- *
- * This is typically sent in response to WANT_PEER but can also be pushed
- * to prepopulate if this makes sense.
- */
- CLUSTER_MESSAGE_HAVE_PEER = 2,
-
- /**
- * Cluster member wants this peer:
- * <[5] ZeroTier address of peer>
- *
- * Members that have a direct link to this peer will respond with
- * HAVE_PEER.
- */
- CLUSTER_MESSAGE_WANT_PEER = 3,
-
- /**
- * A remote packet that we should also possibly respond to:
- * <[2] 16-bit length of remote packet>
- * <[...] remote packet payload>
- *
- * Cluster members may relay requests by relaying the request packet.
- * These may include requests such as WHOIS and MULTICAST_GATHER. The
- * packet must be already decrypted, decompressed, and authenticated.
- *
- * This can only be used for small request packets as per the cluster
- * message size limit, but since these are the only ones in question
- * this is fine.
- *
- * If a response is generated it is sent via PROXY_SEND.
- */
- CLUSTER_MESSAGE_REMOTE_PACKET = 4,
-
- /**
- * Request that VERB_RENDEZVOUS be sent to a peer that we have:
- * <[5] ZeroTier address of peer on recipient's side>
- * <[5] ZeroTier address of peer on sender's side>
- * <[1] 8-bit number of sender's peer's active path addresses>
- * <[...] series of serialized InetAddresses of sender's peer's paths>
- *
- * This requests that we perform NAT-t introduction between a peer that
- * we have and one on the sender's side. The sender furnishes contact
- * info for its peer, and we send VERB_RENDEZVOUS to both sides: to ours
- * directly and with PROXY_SEND to theirs.
- */
- CLUSTER_MESSAGE_PROXY_UNITE = 5,
-
- /**
- * Request that a cluster member send a packet to a locally-known peer:
- * <[5] ZeroTier address of recipient>
- * <[1] packet verb>
- * <[2] length of packet payload>
- * <[...] packet payload>
- *
- * This differs from RELAY in that it requests the receiving cluster
- * member to actually compose a ZeroTier Packet from itself to the
- * provided recipient. RELAY simply says "please forward this blob."
- * RELAY is used to implement peer-to-peer relaying with RENDEZVOUS,
- * while PROXY_SEND is used to implement proxy sending (which right
- * now is only used to send RENDEZVOUS).
- */
- CLUSTER_MESSAGE_PROXY_SEND = 6,
-
- /**
- * Replicate a network config for a network we belong to:
- * <[8] 64-bit network ID>
- * <[2] 16-bit length of network config>
- * <[...] serialized network config>
- *
- * This is used by clusters to avoid every member having to query
- * for the same netconf for networks all members belong to.
- *
- * TODO: not implemented yet!
- */
- CLUSTER_MESSAGE_NETWORK_CONFIG = 7
- };
-
- /**
- * Construct a new cluster
- */
- Cluster(
- const RuntimeEnvironment *renv,
- uint16_t id,
- const std::vector<InetAddress> &zeroTierPhysicalEndpoints,
- int32_t x,
- int32_t y,
- int32_t z,
- void (*sendFunction)(void *,unsigned int,const void *,unsigned int),
- void *sendFunctionArg,
- int (*addressToLocationFunction)(void *,const struct sockaddr_storage *,int *,int *,int *),
- void *addressToLocationFunctionArg);
-
- ~Cluster();
-
- /**
- * @return This cluster member's ID
- */
- inline uint16_t id() const throw() { return _id; }
-
- /**
- * Handle an incoming intra-cluster message
- *
- * @param data Message data
- * @param len Message length (max: ZT_CLUSTER_MAX_MESSAGE_LENGTH)
- */
- void handleIncomingStateMessage(const void *msg,unsigned int len);
-
- /**
- * Broadcast that we have a given peer
- *
- * This should be done when new peers are first contacted.
- *
- * @param id Identity of peer
- */
- void broadcastHavePeer(const Identity &id);
-
- /**
- * Send this packet via another node in this cluster if another node has this peer
- *
- * This is used in the outgoing packet and relaying logic in Switch to
- * relay packets to other cluster members. It isn't PROXY_SEND-- that is
- * used internally in Cluster to send responses to peer queries.
- *
- * @param fromPeerAddress Source peer address (if known, should be NULL for fragments)
- * @param toPeerAddress Destination peer address
- * @param data Packet or packet fragment data
- * @param len Length of packet or fragment
- * @param unite If true, also request proxy unite across cluster
- */
- void sendViaCluster(const Address &fromPeerAddress,const Address &toPeerAddress,const void *data,unsigned int len,bool unite);
-
- /**
- * Send a distributed query to other cluster members
- *
- * Some queries such as WHOIS or MULTICAST_GATHER need a response from other
- * cluster members. Replies (if any) will be sent back to the peer via
- * PROXY_SEND across the cluster.
- *
- * @param pkt Packet to distribute
- */
- void sendDistributedQuery(const Packet &pkt);
-
- /**
- * Call every ~ZT_CLUSTER_PERIODIC_TASK_PERIOD milliseconds.
- */
- void doPeriodicTasks();
-
- /**
- * Add a member ID to this cluster
- *
- * @param memberId Member ID
- */
- void addMember(uint16_t memberId);
-
- /**
- * Remove a member ID from this cluster
- *
- * @param memberId Member ID to remove
- */
- void removeMember(uint16_t memberId);
-
- /**
- * Find a better cluster endpoint for this peer (if any)
- *
- * @param redirectTo InetAddress to be set to a better endpoint (if there is one)
- * @param peerAddress Address of peer to (possibly) redirect
- * @param peerPhysicalAddress Physical address of peer's current best path (where packet was most recently received or getBestPath()->address())
- * @param offload Always redirect if possible -- can be used to offload peers during shutdown
- * @return True if redirectTo was set to a new address, false if redirectTo was not modified
- */
- bool findBetterEndpoint(InetAddress &redirectTo,const Address &peerAddress,const InetAddress &peerPhysicalAddress,bool offload);
-
- /**
- * Fill out ZT_ClusterStatus structure (from core API)
- *
- * @param status Reference to structure to hold result (anything there is replaced)
- */
- void status(ZT_ClusterStatus &status) const;
-
-private:
- void _send(uint16_t memberId,StateMessageType type,const void *msg,unsigned int len);
- void _flush(uint16_t memberId);
-
- void _doREMOTE_WHOIS(uint64_t fromMemberId,const Packet &remotep);
- void _doREMOTE_MULTICAST_GATHER(uint64_t fromMemberId,const Packet &remotep);
-
- // These are initialized in the constructor and remain immutable ------------
- uint16_t _masterSecret[ZT_SHA512_DIGEST_LEN / sizeof(uint16_t)];
- unsigned char _key[ZT_PEER_SECRET_KEY_LENGTH];
- const RuntimeEnvironment *RR;
- _ClusterSendQueue *const _sendQueue;
- void (*_sendFunction)(void *,unsigned int,const void *,unsigned int);
- void *_sendFunctionArg;
- int (*_addressToLocationFunction)(void *,const struct sockaddr_storage *,int *,int *,int *);
- void *_addressToLocationFunctionArg;
- const int32_t _x;
- const int32_t _y;
- const int32_t _z;
- const uint16_t _id;
- const std::vector<InetAddress> _zeroTierPhysicalEndpoints;
- // end immutable fields -----------------------------------------------------
-
- struct _Member
- {
- unsigned char key[ZT_PEER_SECRET_KEY_LENGTH];
-
- uint64_t lastReceivedAliveAnnouncement;
- uint64_t lastAnnouncedAliveTo;
-
- uint64_t load;
- uint64_t peers;
- int32_t x,y,z;
-
- std::vector<InetAddress> zeroTierPhysicalEndpoints;
-
- Buffer<ZT_CLUSTER_MAX_MESSAGE_LENGTH> q;
-
- Mutex lock;
-
- inline void clear()
- {
- lastReceivedAliveAnnouncement = 0;
- lastAnnouncedAliveTo = 0;
- load = 0;
- peers = 0;
- x = 0;
- y = 0;
- z = 0;
- zeroTierPhysicalEndpoints.clear();
- q.clear();
- }
-
- _Member() { this->clear(); }
- ~_Member() { Utils::burn(key,sizeof(key)); }
- };
- _Member *const _members;
-
- std::vector<uint16_t> _memberIds;
- Mutex _memberIds_m;
-
- std::map< std::pair<Address,unsigned int>,uint64_t > _remotePeers; // we need ordered behavior and lower_bound here
- Mutex _remotePeers_m;
-
- uint64_t _lastFlushed;
- uint64_t _lastCleanedRemotePeers;
- uint64_t _lastCleanedQueue;
-};
-
-} // namespace ZeroTier
-
-#endif // ZT_ENABLE_CLUSTER
-
-#endif
diff --git a/node/Constants.hpp b/node/Constants.hpp
index b16cb9b5..e2a35dce 100644
--- a/node/Constants.hpp
+++ b/node/Constants.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_CONSTANTS_HPP
@@ -52,6 +60,8 @@
#endif
#ifdef __APPLE__
+#define likely(x) __builtin_expect((x),1)
+#define unlikely(x) __builtin_expect((x),0)
#include <TargetConditionals.h>
#ifndef __UNIX_LIKE__
#define __UNIX_LIKE__
@@ -128,6 +138,28 @@
#define RTF_MULTICAST 0x20000000
#endif
+#if (defined(__GNUC__) && (__GNUC__ >= 3)) || (defined(__INTEL_COMPILER) && (__INTEL_COMPILER >= 800)) || defined(__clang__)
+#ifndef likely
+#define likely(x) __builtin_expect((x),1)
+#endif
+#ifndef unlikely
+#define unlikely(x) __builtin_expect((x),0)
+#endif
+#else
+#ifndef likely
+#define likely(x) (x)
+#endif
+#ifndef unlikely
+#define unlikely(x) (x)
+#endif
+#endif
+
+#ifdef __WINDOWS__
+#define ZT_PACKED_STRUCT(D) __pragma(pack(push,1)) D __pragma(pack(pop))
+#else
+#define ZT_PACKED_STRUCT(D) D __attribute__((packed))
+#endif
+
/**
* Length of a ZeroTier address in bytes
*/
@@ -144,26 +176,14 @@
#define ZT_ADDRESS_RESERVED_PREFIX 0xff
/**
- * Default payload MTU for UDP packets
- *
- * In the future we might support UDP path MTU discovery, but for now we
- * set a maximum that is equal to 1500 minus 8 (for PPPoE overhead, common
- * in some markets) minus 48 (IPv6 UDP overhead).
- */
-#define ZT_UDP_DEFAULT_PAYLOAD_MTU 1444
-
-/**
* Default MTU used for Ethernet tap device
*/
-#define ZT_IF_MTU ZT_MAX_MTU
+#define ZT_DEFAULT_MTU 2800
/**
- * Maximum number of packet fragments we'll support
- *
- * The actual spec allows 16, but this is the most we'll support right
- * now. Packets with more than this many fragments are dropped.
+ * Maximum number of packet fragments we'll support (protocol max: 16)
*/
-#define ZT_MAX_PACKET_FRAGMENTS 4
+#define ZT_MAX_PACKET_FRAGMENTS 7
/**
* Size of RX queue
@@ -174,49 +194,39 @@
#define ZT_RX_QUEUE_SIZE 64
/**
- * RX queue entries older than this do not "exist"
- */
-#define ZT_RX_QUEUE_EXPIRE 4000
-
-/**
* Length of secret key in bytes -- 256-bit -- do not change
*/
#define ZT_PEER_SECRET_KEY_LENGTH 32
/**
- * How often Topology::clean() and Network::clean() and similar are called, in ms
- */
-#define ZT_HOUSEKEEPING_PERIOD 120000
-
-/**
- * Overriding granularity for timer tasks to prevent CPU-intensive thrashing on every packet
+ * Minimum delay between timer task checks to prevent thrashing
*/
#define ZT_CORE_TIMER_TASK_GRANULARITY 500
/**
- * How long to remember peer records in RAM if they haven't been used
+ * How often Topology::clean() and Network::clean() and similar are called, in ms
*/
-#define ZT_PEER_IN_MEMORY_EXPIRATION 600000
+#define ZT_HOUSEKEEPING_PERIOD 60000
/**
* Delay between WHOIS retries in ms
*/
-#define ZT_WHOIS_RETRY_DELAY 1000
+#define ZT_WHOIS_RETRY_DELAY 500
/**
- * Maximum identity WHOIS retries (each attempt tries consulting a different peer)
+ * Transmit queue entry timeout
*/
-#define ZT_MAX_WHOIS_RETRIES 3
+#define ZT_TRANSMIT_QUEUE_TIMEOUT 5000
/**
- * Transmit queue entry timeout
+ * Receive queue entry timeout
*/
-#define ZT_TRANSMIT_QUEUE_TIMEOUT (ZT_WHOIS_RETRY_DELAY * (ZT_MAX_WHOIS_RETRIES + 1))
+#define ZT_RECEIVE_QUEUE_TIMEOUT 5000
/**
- * Receive queue entry timeout
+ * Maximum latency to allow for OK(HELLO) before packet is discarded
*/
-#define ZT_RECEIVE_QUEUE_TIMEOUT (ZT_WHOIS_RETRY_DELAY * (ZT_MAX_WHOIS_RETRIES + 1))
+#define ZT_HELLO_MAX_ALLOWABLE_LATENCY 120000
/**
* Maximum number of ZT hops allowed (this is not IP hops/TTL)
@@ -231,11 +241,21 @@
#define ZT_MULTICAST_LIKE_EXPIRE 600000
/**
+ * Period for multicast LIKE announcements
+ */
+#define ZT_MULTICAST_ANNOUNCE_PERIOD 120000
+
+/**
* Delay between explicit MULTICAST_GATHER requests for a given multicast channel
*/
#define ZT_MULTICAST_EXPLICIT_GATHER_DELAY (ZT_MULTICAST_LIKE_EXPIRE / 10)
/**
+ * Expiration for credentials presented for MULTICAST_LIKE or MULTICAST_GATHER (for non-network-members)
+ */
+#define ZT_MULTICAST_CREDENTIAL_EXPIRATON ZT_MULTICAST_LIKE_EXPIRE
+
+/**
* Timeout for outgoing multicasts
*
* This is how long we wait for explicit or implicit gather results.
@@ -243,50 +263,49 @@
#define ZT_MULTICAST_TRANSMIT_TIMEOUT 5000
/**
- * Default maximum number of peers to address with a single multicast (if unspecified in network config)
+ * Delay between checks of peer pings, etc., and also related housekeeping tasks
*/
-#define ZT_MULTICAST_DEFAULT_LIMIT 32
+#define ZT_PING_CHECK_INVERVAL 5000
/**
- * How frequently to send a zero-byte UDP keepalive packet
- *
- * There are NATs with timeouts as short as 20 seconds, so this turns out
- * to be needed.
+ * How frequently to send heartbeats over in-use paths
*/
-#define ZT_NAT_KEEPALIVE_DELAY 19000
+#define ZT_PATH_HEARTBEAT_PERIOD 14000
/**
- * Delay between scans of the topology active peer DB for peers that need ping
- *
- * This is also how often pings will be retried to upstream peers (relays, roots)
- * constantly until something is heard.
+ * Do not accept HELLOs over a given path more often than this
*/
-#define ZT_PING_CHECK_INVERVAL 9500
+#define ZT_PATH_HELLO_RATE_LIMIT 1000
/**
- * Delay between ordinary case pings of direct links
+ * Delay between full-fledge pings of directly connected peers
*/
-#define ZT_PEER_DIRECT_PING_DELAY 60000
+#define ZT_PEER_PING_PERIOD 60000
/**
- * Timeout for overall peer activity (measured from last receive)
+ * Paths are considered expired if they have not sent us a real packet in this long
*/
-#define ZT_PEER_ACTIVITY_TIMEOUT 500000
+#define ZT_PEER_PATH_EXPIRATION ((ZT_PEER_PING_PERIOD * 4) + 3000)
/**
- * Timeout for path activity
+ * How often to retry expired paths that we're still remembering
+ */
+#define ZT_PEER_EXPIRED_PATH_TRIAL_PERIOD (ZT_PEER_PING_PERIOD * 10)
+
+/**
+ * Timeout for overall peer activity (measured from last receive)
*/
-#define ZT_PATH_ACTIVITY_TIMEOUT ZT_PEER_ACTIVITY_TIMEOUT
+#define ZT_PEER_ACTIVITY_TIMEOUT 500000
/**
- * No answer timeout to trigger dead path detection
+ * General rate limit timeout for multiple packet types (HELLO, etc.)
*/
-#define ZT_PEER_DEAD_PATH_DETECTION_NO_ANSWER_TIMEOUT 2000
+#define ZT_PEER_GENERAL_INBOUND_RATE_LIMIT 500
/**
- * Probation threshold after which a path becomes dead
+ * General limit for max RTT for requests over the network
*/
-#define ZT_PEER_DEAD_PATH_DETECTION_MAX_PROBATION 3
+#define ZT_GENERAL_RTT_LIMIT 5000
/**
* Delay between requests for updated network autoconf information
@@ -306,19 +325,9 @@
#define ZT_MIN_UNITE_INTERVAL 30000
/**
- * Delay between initial direct NAT-t packet and more aggressive techniques
- *
- * This may also be a delay before sending the first packet if we determine
- * that we should wait for the remote to initiate rendezvous first.
+ * How often should peers try memorized or statically defined paths?
*/
-#define ZT_NAT_T_TACTICAL_ESCALATION_DELAY 1000
-
-/**
- * How long (max) to remember network certificates of membership?
- *
- * This only applies to networks we don't belong to.
- */
-#define ZT_PEER_NETWORK_COM_EXPIRATION 3600000
+#define ZT_TRY_MEMORIZED_PATH_INTERVAL 30000
/**
* Sanity limit on maximum bridge routes
@@ -334,7 +343,7 @@
/**
* If there is no known route, spam to up to this many active bridges
*/
-#define ZT_MAX_BRIDGE_SPAM 16
+#define ZT_MAX_BRIDGE_SPAM 32
/**
* Interval between direct path pushes in milliseconds
@@ -344,7 +353,7 @@
/**
* Time horizon for push direct paths cutoff
*/
-#define ZT_PUSH_DIRECT_PATHS_CUTOFF_TIME 60000
+#define ZT_PUSH_DIRECT_PATHS_CUTOFF_TIME 30000
/**
* Maximum number of direct path pushes within cutoff time
@@ -353,30 +362,62 @@
* per CUTOFF_TIME milliseconds per peer to prevent this from being
* useful for DOS amplification attacks.
*/
-#define ZT_PUSH_DIRECT_PATHS_CUTOFF_LIMIT 5
+#define ZT_PUSH_DIRECT_PATHS_CUTOFF_LIMIT 8
/**
* Maximum number of paths per IP scope (e.g. global, link-local) and family (e.g. v4/v6)
*/
-#define ZT_PUSH_DIRECT_PATHS_MAX_PER_SCOPE_AND_FAMILY 4
+#define ZT_PUSH_DIRECT_PATHS_MAX_PER_SCOPE_AND_FAMILY 8
/**
- * Enable support for old Dictionary based network configs
+ * Time horizon for VERB_NETWORK_CREDENTIALS cutoff
*/
-#define ZT_SUPPORT_OLD_STYLE_NETCONF 1
+#define ZT_PEER_CREDENTIALS_CUTOFF_TIME 60000
/**
- * A test pseudo-network-ID that can be joined
- *
- * Joining this network ID will result in a network with no IP addressing
- * and default parameters. No network configuration master will be consulted
- * and instead a static config will be used. This is used in built-in testnet
- * scenarios and can also be used for external testing.
+ * Maximum number of VERB_NETWORK_CREDENTIALS within cutoff time
+ */
+#define ZT_PEER_CREDEITIALS_CUTOFF_LIMIT 15
+
+/**
+ * WHOIS rate limit (we allow these to be pretty fast)
+ */
+#define ZT_PEER_WHOIS_RATE_LIMIT 100
+
+/**
+ * General rate limit for other kinds of rate-limited packets (HELLO, credential request, etc.) both inbound and outbound
+ */
+#define ZT_PEER_GENERAL_RATE_LIMIT 1000
+
+/**
+ * Don't do expensive identity validation more often than this
*
- * This is an impossible real network ID since 0xff is a reserved address
- * prefix.
+ * IPv4 and IPv6 address prefixes are hashed down to 14-bit (0-16383) integers
+ * using the first 24 bits for IPv4 or the first 48 bits for IPv6. These are
+ * then rate limited to one identity validation per this often milliseconds.
+ */
+#if (defined(__amd64) || defined(__amd64__) || defined(__x86_64) || defined(__x86_64__) || defined(__AMD64) || defined(__AMD64__) || defined(_M_X64) || defined(_M_AMD64))
+// AMD64 machines can do anywhere from one every 50ms to one every 10ms. This provides plenty of margin.
+#define ZT_IDENTITY_VALIDATION_SOURCE_RATE_LIMIT 2000
+#else
+#if (defined(__i386__) || defined(__i486__) || defined(__i586__) || defined(__i686__) || defined(_M_IX86) || defined(_X86_) || defined(__I86__))
+// 32-bit Intel machines usually average about one every 100ms
+#define ZT_IDENTITY_VALIDATION_SOURCE_RATE_LIMIT 5000
+#else
+// This provides a safe margin for ARM, MIPS, etc. that usually average one every 250-400ms
+#define ZT_IDENTITY_VALIDATION_SOURCE_RATE_LIMIT 10000
+#endif
+#endif
+
+/**
+ * How long is a path or peer considered to have a trust relationship with us (for e.g. relay policy) since last trusted established packet?
*/
-#define ZT_TEST_NETWORK_ID 0xffffffffffffffffULL
+#define ZT_TRUST_EXPIRATION 600000
+
+/**
+ * Enable support for older network configurations from older (pre-1.1.6) controllers
+ */
+#define ZT_SUPPORT_OLD_STYLE_NETCONF 1
/**
* Desired buffer size for UDP sockets (used in service and osdep but defined here)
@@ -387,6 +428,11 @@
#define ZT_UDP_DESIRED_BUF_SIZE 131072
#endif
+/**
+ * Desired / recommended min stack size for threads (used on some platforms to reset thread stack size)
+ */
+#define ZT_THREAD_MIN_STACK_SIZE 1048576
+
/* Ethernet frame types that might be relevant to us */
#define ZT_ETHERTYPE_IPV4 0x0800
#define ZT_ETHERTYPE_ARP 0x0806
@@ -397,4 +443,13 @@
#define ZT_ETHERTYPE_IPX_B 0x8138
#define ZT_ETHERTYPE_IPV6 0x86dd
+#define ZT_EXCEPTION_OUT_OF_BOUNDS 100
+#define ZT_EXCEPTION_OUT_OF_MEMORY 101
+#define ZT_EXCEPTION_PRIVATE_KEY_REQUIRED 102
+#define ZT_EXCEPTION_INVALID_ARGUMENT 103
+#define ZT_EXCEPTION_INVALID_SERIALIZED_DATA_INVALID_TYPE 200
+#define ZT_EXCEPTION_INVALID_SERIALIZED_DATA_OVERFLOW 201
+#define ZT_EXCEPTION_INVALID_SERIALIZED_DATA_INVALID_CRYPTOGRAPHIC_TOKEN 202
+#define ZT_EXCEPTION_INVALID_SERIALIZED_DATA_BAD_ENCODING 203
+
#endif
diff --git a/node/Credential.hpp b/node/Credential.hpp
new file mode 100644
index 00000000..34e94162
--- /dev/null
+++ b/node/Credential.hpp
@@ -0,0 +1,65 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
+ */
+
+#ifndef ZT_CREDENTIAL_HPP
+#define ZT_CREDENTIAL_HPP
+
+#include <string>
+#include <memory>
+#include <stdexcept>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+
+#include "Constants.hpp"
+
+namespace ZeroTier {
+
+/**
+ * Base class for credentials
+ */
+class Credential
+{
+public:
+ /**
+ * Do not change type code IDs -- these are used in Revocation objects and elsewhere
+ */
+ enum Type
+ {
+ CREDENTIAL_TYPE_NULL = 0,
+ CREDENTIAL_TYPE_COM = 1, // CertificateOfMembership
+ CREDENTIAL_TYPE_CAPABILITY = 2,
+ CREDENTIAL_TYPE_TAG = 3,
+ CREDENTIAL_TYPE_COO = 4, // CertificateOfOwnership
+ CREDENTIAL_TYPE_REVOCATION = 6
+ };
+};
+
+} // namespace ZeroTier
+
+#endif
diff --git a/node/DeferredPackets.cpp b/node/DeferredPackets.cpp
deleted file mode 100644
index 192b4078..00000000
--- a/node/DeferredPackets.cpp
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include "Constants.hpp"
-#include "DeferredPackets.hpp"
-#include "IncomingPacket.hpp"
-#include "RuntimeEnvironment.hpp"
-#include "Node.hpp"
-
-namespace ZeroTier {
-
-DeferredPackets::DeferredPackets(const RuntimeEnvironment *renv) :
- RR(renv),
- _waiting(0),
- _die(false)
-{
-}
-
-DeferredPackets::~DeferredPackets()
-{
- _q_m.lock();
- _die = true;
- _q_m.unlock();
-
- for(;;) {
- _q_s.post();
-
- _q_m.lock();
- if (_waiting <= 0) {
- _q_m.unlock();
- break;
- } else {
- _q_m.unlock();
- }
- }
-}
-
-bool DeferredPackets::enqueue(IncomingPacket *pkt)
-{
- {
- Mutex::Lock _l(_q_m);
- if (_q.size() >= ZT_DEFFEREDPACKETS_MAX)
- return false;
- _q.push_back(*pkt);
- }
- _q_s.post();
- return true;
-}
-
-int DeferredPackets::process()
-{
- std::list<IncomingPacket> pkt;
-
- _q_m.lock();
-
- if (_die) {
- _q_m.unlock();
- return -1;
- }
-
- while (_q.empty()) {
- ++_waiting;
- _q_m.unlock();
- _q_s.wait();
- _q_m.lock();
- --_waiting;
- if (_die) {
- _q_m.unlock();
- return -1;
- }
- }
-
- // Move item from _q list to a dummy list here to avoid copying packet
- pkt.splice(pkt.end(),_q,_q.begin());
-
- _q_m.unlock();
-
- try {
- pkt.front().tryDecode(RR,true);
- } catch ( ... ) {} // drop invalids
-
- return 1;
-}
-
-} // namespace ZeroTier
diff --git a/node/DeferredPackets.hpp b/node/DeferredPackets.hpp
deleted file mode 100644
index a9855396..00000000
--- a/node/DeferredPackets.hpp
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef ZT_DEFERREDPACKETS_HPP
-#define ZT_DEFERREDPACKETS_HPP
-
-#include <list>
-
-#include "Constants.hpp"
-#include "SharedPtr.hpp"
-#include "Mutex.hpp"
-#include "DeferredPackets.hpp"
-#include "BinarySemaphore.hpp"
-
-/**
- * Maximum number of deferred packets
- */
-#define ZT_DEFFEREDPACKETS_MAX 256
-
-namespace ZeroTier {
-
-class IncomingPacket;
-class RuntimeEnvironment;
-
-/**
- * Deferred packets
- *
- * IncomingPacket can defer its decoding this way by enqueueing itself here.
- * When this is done, deferredDecode() is called later. This is done for
- * operations that may be expensive to allow them to potentially be handled
- * in the background or rate limited to maintain quality of service for more
- * routine operations.
- */
-class DeferredPackets
-{
-public:
- DeferredPackets(const RuntimeEnvironment *renv);
- ~DeferredPackets();
-
- /**
- * Enqueue a packet
- *
- * @param pkt Packet to process later (possibly in the background)
- * @return False if queue is full
- */
- bool enqueue(IncomingPacket *pkt);
-
- /**
- * Wait for and then process a deferred packet
- *
- * If we are shutting down (in destructor), this returns -1 and should
- * not be called again. Otherwise it returns the number of packets
- * processed.
- *
- * @return Number processed or -1 if shutting down
- */
- int process();
-
-private:
- std::list<IncomingPacket> _q;
- const RuntimeEnvironment *const RR;
- volatile int _waiting;
- volatile bool _die;
- Mutex _q_m;
- BinarySemaphore _q_s;
-};
-
-} // namespace ZeroTier
-
-#endif
diff --git a/node/Dictionary.hpp b/node/Dictionary.hpp
index 59fc4bbf..f89b6ffc 100644
--- a/node/Dictionary.hpp
+++ b/node/Dictionary.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_DICTIONARY_HPP
@@ -54,35 +62,29 @@ template<unsigned int C>
class Dictionary
{
public:
- Dictionary()
- {
- _d[0] = (char)0;
- }
-
- Dictionary(const char *s)
- {
- Utils::scopy(_d,sizeof(_d),s);
- }
-
+ Dictionary() { memset(_d,0,sizeof(_d)); }
+ Dictionary(const char *s) { this->load(s); }
Dictionary(const char *s,unsigned int len)
{
- if (len > (C-1))
- len = C-1;
- memcpy(_d,s,len);
- _d[len] = (char)0;
- }
-
- Dictionary(const Dictionary &d)
- {
- Utils::scopy(_d,sizeof(_d),d._d);
+ for(unsigned int i=0;i<C;++i) {
+ if ((s)&&(i < len)) {
+ if (!(_d[i] = *s))
+ s = (const char *)0;
+ else ++s;
+ } else _d[i] = (char)0;
+ }
+ _d[C - 1] = (char)0;
}
+ Dictionary(const Dictionary &d) { memcpy(_d,d._d,C); }
inline Dictionary &operator=(const Dictionary &d)
{
- Utils::scopy(_d,sizeof(_d),d._d);
+ memcpy(_d,d._d,C);
return *this;
}
+ inline operator bool() const { return (_d[0] != 0); }
+
/**
* Load a dictionary from a C-string
*
@@ -91,7 +93,15 @@ public:
*/
inline bool load(const char *s)
{
- return Utils::scopy(_d,sizeof(_d),s);
+ for(unsigned int i=0;i<C;++i) {
+ if (s) {
+ if (!(_d[i] = *s))
+ s = (const char *)0;
+ else ++s;
+ } else _d[i] = (char)0;
+ }
+ _d[C - 1] = (char)0;
+ return (!s);
}
/**
@@ -99,7 +109,7 @@ public:
*/
inline void clear()
{
- _d[0] = (char)0;
+ memset(_d,0,sizeof(_d));
}
/**
@@ -163,12 +173,12 @@ public:
j = 0;
esc = false;
++p;
- while ((*p != 0)&&(*p != '\r')&&(*p != '\n')) {
+ while ((*p != 0)&&(*p != 13)&&(*p != 10)) {
if (esc) {
esc = false;
switch(*p) {
- case 'r': dest[j++] = '\r'; break;
- case 'n': dest[j++] = '\n'; break;
+ case 'r': dest[j++] = 13; break;
+ case 'n': dest[j++] = 10; break;
case '0': dest[j++] = (char)0; break;
case 'e': dest[j++] = '='; break;
default: dest[j++] = *p; break;
@@ -194,7 +204,7 @@ public:
dest[j] = (char)0;
return j;
} else {
- while ((*p)&&(*p != '\r')&&(*p != '\n')) {
+ while ((*p)&&(*p != 13)&&(*p != 10)) {
if (++p == eof) {
dest[0] = (char)0;
return -1;
@@ -266,6 +276,21 @@ public:
}
/**
+ * Get an unsigned int64 stored as hex in the dictionary
+ *
+ * @param key Key to look up
+ * @param dfl Default value or 0 if unspecified
+ * @return Decoded hex UInt value or 'dfl' if not found
+ */
+ inline int64_t getI(const char *key,int64_t dfl = 0) const
+ {
+ char tmp[128];
+ if (this->get(key,tmp,sizeof(tmp)) >= 1)
+ return Utils::hexStrTo64(tmp);
+ return dfl;
+ }
+
+ /**
* Add a new key=value pair
*
* If the key is already present this will append another, but the first
@@ -286,7 +311,7 @@ public:
unsigned int j = i;
if (j > 0) {
- _d[j++] = '\n';
+ _d[j++] = (char)10;
if (j == C) {
_d[i] = (char)0;
return false;
@@ -313,8 +338,8 @@ public:
while ( ((vlen < 0)&&(*p)) || (k < vlen) ) {
switch(*p) {
case 0:
- case '\r':
- case '\n':
+ case 13:
+ case 10:
case '\\':
case '=':
_d[j++] = '\\';
@@ -324,8 +349,8 @@ public:
}
switch(*p) {
case 0: _d[j++] = '0'; break;
- case '\r': _d[j++] = 'r'; break;
- case '\n': _d[j++] = 'n'; break;
+ case 13: _d[j++] = 'r'; break;
+ case 10: _d[j++] = 'n'; break;
case '\\': _d[j++] = '\\'; break;
case '=': _d[j++] = 'e'; break;
}
@@ -368,8 +393,21 @@ public:
inline bool add(const char *key,uint64_t value)
{
char tmp[32];
- Utils::snprintf(tmp,sizeof(tmp),"%llx",(unsigned long long)value);
- return this->add(key,tmp,-1);
+ return this->add(key,Utils::hex(value,tmp),-1);
+ }
+
+ /**
+ * Add a 64-bit integer (unsigned) as a hex value
+ */
+ inline bool add(const char *key,int64_t value)
+ {
+ char tmp[32];
+ if (value >= 0) {
+ return this->add(key,Utils::hex((uint64_t)value,tmp),-1);
+ } else {
+ tmp[0] = '-';
+ return this->add(key,Utils::hex((uint64_t)(value * -1),tmp+1),-1);
+ }
}
/**
@@ -378,8 +416,7 @@ public:
inline bool add(const char *key,const Address &a)
{
char tmp[32];
- Utils::snprintf(tmp,sizeof(tmp),"%.10llx",(unsigned long long)a.toInt());
- return this->add(key,tmp,-1);
+ return this->add(key,Utils::hex(a.toInt(),tmp),-1);
}
/**
@@ -404,55 +441,13 @@ public:
}
/**
- * Erase a key from this dictionary
- *
- * Use this before add() to ensure that a key is replaced if it might
- * already be present.
- *
- * @param key Key to erase
- * @return True if key was found and erased
- */
- inline bool erase(const char *key)
- {
- char d2[C];
- char *saveptr = (char *)0;
- unsigned int d2ptr = 0;
- bool found = false;
- for(char *f=Utils::stok(_d,"\r\n",&saveptr);(f);f=Utils::stok((char *)0,"\r\n",&saveptr)) {
- if (*f) {
- const char *p = f;
- const char *k = key;
- while ((*k)&&(*p)) {
- if (*k != *p)
- break;
- ++k;
- ++p;
- }
- if (*k) {
- p = f;
- while (*p)
- d2[d2ptr++] = *(p++);
- d2[d2ptr++] = '\n';
- } else {
- found = true;
- }
- }
- }
- d2[d2ptr++] = (char)0;
- memcpy(_d,d2,d2ptr);
- return found;
- }
-
- /**
- * @return Dictionary data as a 0-terminated C-string
- */
- inline const char *data() const { return _d; }
-
- /**
* @return Value of C template parameter
*/
inline unsigned int capacity() const { return C; }
+ inline const char *data() const { return _d; }
+ inline char *unsafeData() { return _d; }
+
private:
char _d[C];
};
diff --git a/node/Hashtable.hpp b/node/Hashtable.hpp
index f06b2230..777e88dc 100644
--- a/node/Hashtable.hpp
+++ b/node/Hashtable.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,11 +14,21 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_HASHTABLE_HPP
#define ZT_HASHTABLE_HPP
+#include "Constants.hpp"
+
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
@@ -32,11 +42,6 @@ namespace ZeroTier {
/**
* A minimal hash table implementation for the ZeroTier core
- *
- * This is not a drop-in replacement for STL containers, and has several
- * limitations. Keys can be uint64_t or an object, and if the latter they
- * must implement a method called hashCode() that returns an unsigned long
- * value that is evenly distributed.
*/
template<typename K,typename V>
class Hashtable
@@ -100,18 +105,18 @@ public:
Hashtable *_ht;
_Bucket *_b;
};
- friend class Hashtable::Iterator;
+ //friend class Hashtable<K,V>::Iterator;
/**
- * @param bc Initial capacity in buckets (default: 128, must be nonzero)
+ * @param bc Initial capacity in buckets (default: 64, must be nonzero)
*/
- Hashtable(unsigned long bc = 128) :
+ Hashtable(unsigned long bc = 64) :
_t(reinterpret_cast<_Bucket **>(::malloc(sizeof(_Bucket *) * bc))),
_bc(bc),
_s(0)
{
if (!_t)
- throw std::bad_alloc();
+ throw ZT_EXCEPTION_OUT_OF_MEMORY;
for(unsigned long i=0;i<bc;++i)
_t[i] = (_Bucket *)0;
}
@@ -122,7 +127,7 @@ public:
_s(ht._s)
{
if (!_t)
- throw std::bad_alloc();
+ throw ZT_EXCEPTION_OUT_OF_MEMORY;
for(unsigned long i=0;i<_bc;++i)
_t[i] = (_Bucket *)0;
for(unsigned long i=0;i<_bc;++i) {
@@ -251,6 +256,24 @@ public:
inline const V *get(const K &k) const { return const_cast<Hashtable *>(this)->get(k); }
/**
+ * @param k Key
+ * @param v Value to fill with result
+ * @return True if value was found and set (if false, v is not modified)
+ */
+ inline bool get(const K &k,V &v) const
+ {
+ _Bucket *b = _t[_hc(k) % _bc];
+ while (b) {
+ if (b->k == k) {
+ v = b->v;
+ return true;
+ }
+ b = b->next;
+ }
+ return false;
+ }
+
+ /**
* @param k Key to check
* @return True if key is present
*/
@@ -351,27 +374,22 @@ public:
/**
* @return Number of entries
*/
- inline unsigned long size() const throw() { return _s; }
+ inline unsigned long size() const { return _s; }
/**
* @return True if table is empty
*/
- inline bool empty() const throw() { return (_s == 0); }
+ inline bool empty() const { return (_s == 0); }
private:
template<typename O>
static inline unsigned long _hc(const O &obj)
{
- return obj.hashCode();
+ return (unsigned long)obj.hashCode();
}
static inline unsigned long _hc(const uint64_t i)
{
- /* NOTE: this assumes that 'i' is evenly distributed, which is the case for
- * packet IDs and network IDs -- the two use cases in ZT for uint64_t keys.
- * These values are also greater than 0xffffffff so they'll map onto a full
- * bucket count just fine no matter what happens. Normally you'd want to
- * hash an integer key index in a hash table. */
- return (unsigned long)i;
+ return (unsigned long)(i ^ (i >> 32)); // good for network IDs and addresses
}
static inline unsigned long _hc(const uint32_t i)
{
diff --git a/node/Identity.cpp b/node/Identity.cpp
index 6f89a1ee..03f27083 100644
--- a/node/Identity.cpp
+++ b/node/Identity.cpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#include <stdio.h>
@@ -45,8 +53,8 @@ static inline void _computeMemoryHardHash(const void *publicKey,unsigned int pub
// ordinary Salsa20 is randomly seekable. This is good for a cipher
// but is not what we want for sequential memory-harndess.
memset(genmem,0,ZT_IDENTITY_GEN_MEMORY);
- Salsa20 s20(digest,256,(char *)digest + 32);
- s20.encrypt20((char *)genmem,(char *)genmem,64);
+ Salsa20 s20(digest,(char *)digest + 32);
+ s20.crypt20((char *)genmem,(char *)genmem,64);
for(unsigned long i=64;i<ZT_IDENTITY_GEN_MEMORY;i+=64) {
unsigned long k = i - 64;
*((uint64_t *)((char *)genmem + i)) = *((uint64_t *)((char *)genmem + k));
@@ -57,7 +65,7 @@ static inline void _computeMemoryHardHash(const void *publicKey,unsigned int pub
*((uint64_t *)((char *)genmem + i + 40)) = *((uint64_t *)((char *)genmem + k + 40));
*((uint64_t *)((char *)genmem + i + 48)) = *((uint64_t *)((char *)genmem + k + 48));
*((uint64_t *)((char *)genmem + i + 56)) = *((uint64_t *)((char *)genmem + k + 56));
- s20.encrypt20((char *)genmem + i,(char *)genmem + i,64);
+ s20.crypt20((char *)genmem + i,(char *)genmem + i,64);
}
// Render final digest using genmem as a lookup table
@@ -67,7 +75,7 @@ static inline void _computeMemoryHardHash(const void *publicKey,unsigned int pub
uint64_t tmp = ((uint64_t *)genmem)[idx2];
((uint64_t *)genmem)[idx2] = ((uint64_t *)digest)[idx1];
((uint64_t *)digest)[idx1] = tmp;
- s20.encrypt20(digest,digest,64);
+ s20.crypt20(digest,digest,64);
}
}
@@ -75,12 +83,11 @@ static inline void _computeMemoryHardHash(const void *publicKey,unsigned int pub
// threshold value.
struct _Identity_generate_cond
{
- _Identity_generate_cond() throw() {}
- _Identity_generate_cond(unsigned char *sb,char *gm) throw() : digest(sb),genmem(gm) {}
+ _Identity_generate_cond() {}
+ _Identity_generate_cond(unsigned char *sb,char *gm) : digest(sb),genmem(gm) {}
inline bool operator()(const C25519::Pair &kp) const
- throw()
{
- _computeMemoryHardHash(kp.pub.data,(unsigned int)kp.pub.size(),digest,genmem);
+ _computeMemoryHardHash(kp.pub.data,ZT_C25519_PUBLIC_KEY_LEN,digest,genmem);
return (digest[0] < ZT_IDENTITY_GEN_HASHCASH_FIRST_BYTE_LESS_THAN);
}
unsigned char *digest;
@@ -113,7 +120,7 @@ bool Identity::locallyValidate() const
unsigned char digest[64];
char *genmem = new char[ZT_IDENTITY_GEN_MEMORY];
- _computeMemoryHardHash(_publicKey.data,(unsigned int)_publicKey.size(),digest,genmem);
+ _computeMemoryHardHash(_publicKey.data,ZT_C25519_PUBLIC_KEY_LEN,digest,genmem);
delete [] genmem;
unsigned char addrb[5];
@@ -128,61 +135,79 @@ bool Identity::locallyValidate() const
(digest[63] == addrb[4]));
}
-std::string Identity::toString(bool includePrivate) const
+char *Identity::toString(bool includePrivate,char buf[ZT_IDENTITY_STRING_BUFFER_LENGTH]) const
{
- std::string r;
-
- r.append(_address.toString());
- r.append(":0:"); // 0 == IDENTITY_TYPE_C25519
- r.append(Utils::hex(_publicKey.data,(unsigned int)_publicKey.size()));
+ char *p = buf;
+ Utils::hex10(_address.toInt(),p);
+ p += 10;
+ *(p++) = ':';
+ *(p++) = '0';
+ *(p++) = ':';
+ Utils::hex(_publicKey.data,ZT_C25519_PUBLIC_KEY_LEN,p);
+ p += ZT_C25519_PUBLIC_KEY_LEN * 2;
if ((_privateKey)&&(includePrivate)) {
- r.push_back(':');
- r.append(Utils::hex(_privateKey->data,(unsigned int)_privateKey->size()));
+ *(p++) = ':';
+ Utils::hex(_privateKey->data,ZT_C25519_PRIVATE_KEY_LEN,p);
+ p += ZT_C25519_PRIVATE_KEY_LEN * 2;
}
-
- return r;
+ *p = (char)0;
+ return buf;
}
bool Identity::fromString(const char *str)
{
- if (!str)
+ if (!str) {
+ _address.zero();
return false;
-
- char *saveptr = (char *)0;
- char tmp[1024];
- if (!Utils::scopy(tmp,sizeof(tmp),str))
+ }
+ char tmp[ZT_IDENTITY_STRING_BUFFER_LENGTH];
+ if (!Utils::scopy(tmp,sizeof(tmp),str)) {
+ _address.zero();
return false;
+ }
delete _privateKey;
_privateKey = (C25519::Private *)0;
int fno = 0;
+ char *saveptr = (char *)0;
for(char *f=Utils::stok(tmp,":",&saveptr);(f);f=Utils::stok((char *)0,":",&saveptr)) {
switch(fno++) {
case 0:
- _address = Address(f);
- if (_address.isReserved())
+ _address = Address(Utils::hexStrToU64(f));
+ if (_address.isReserved()) {
+ _address.zero();
return false;
+ }
break;
case 1:
- if ((f[0] != '0')||(f[1]))
+ if ((f[0] != '0')||(f[1])) {
+ _address.zero();
return false;
+ }
break;
case 2:
- if (Utils::unhex(f,_publicKey.data,(unsigned int)_publicKey.size()) != _publicKey.size())
+ if (Utils::unhex(f,_publicKey.data,ZT_C25519_PUBLIC_KEY_LEN) != ZT_C25519_PUBLIC_KEY_LEN) {
+ _address.zero();
return false;
+ }
break;
case 3:
_privateKey = new C25519::Private();
- if (Utils::unhex(f,_privateKey->data,(unsigned int)_privateKey->size()) != _privateKey->size())
+ if (Utils::unhex(f,_privateKey->data,ZT_C25519_PRIVATE_KEY_LEN) != ZT_C25519_PRIVATE_KEY_LEN) {
+ _address.zero();
return false;
+ }
break;
default:
+ _address.zero();
return false;
}
}
- if (fno < 3)
+ if (fno < 3) {
+ _address.zero();
return false;
+ }
return true;
}
diff --git a/node/Identity.hpp b/node/Identity.hpp
index e19c4980..74716aa0 100644
--- a/node/Identity.hpp
+++ b/node/Identity.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_IDENTITY_HPP
@@ -21,16 +29,16 @@
#include <stdio.h>
#include <stdlib.h>
-#include <string>
#include "Constants.hpp"
-#include "Array.hpp"
#include "Utils.hpp"
#include "Address.hpp"
#include "C25519.hpp"
#include "Buffer.hpp"
#include "SHA512.hpp"
+#define ZT_IDENTITY_STRING_BUFFER_LENGTH 384
+
namespace ZeroTier {
/**
@@ -46,14 +54,6 @@ namespace ZeroTier {
class Identity
{
public:
- /**
- * Identity types
- */
- enum Type
- {
- IDENTITY_TYPE_C25519 = 0
- };
-
Identity() :
_privateKey((C25519::Private *)0)
{
@@ -66,20 +66,11 @@ public:
{
}
- Identity(const char *str)
- throw(std::invalid_argument) :
+ Identity(const char *str) :
_privateKey((C25519::Private *)0)
{
if (!fromString(str))
- throw std::invalid_argument(std::string("invalid string-serialized identity: ") + str);
- }
-
- Identity(const std::string &str)
- throw(std::invalid_argument) :
- _privateKey((C25519::Private *)0)
- {
- if (!fromString(str))
- throw std::invalid_argument(std::string("invalid string-serialized identity: ") + str);
+ throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_INVALID_TYPE;
}
template<unsigned int C>
@@ -91,7 +82,10 @@ public:
~Identity()
{
- delete _privateKey;
+ if (_privateKey) {
+ Utils::burn(_privateKey,sizeof(C25519::Private));
+ delete _privateKey;
+ }
}
inline Identity &operator=(const Identity &id)
@@ -126,7 +120,7 @@ public:
/**
* @return True if this identity contains a private key
*/
- inline bool hasPrivate() const throw() { return (_privateKey != (C25519::Private *)0); }
+ inline bool hasPrivate() const { return (_privateKey != (C25519::Private *)0); }
/**
* Compute the SHA512 hash of our private key (if we have one)
@@ -150,11 +144,10 @@ public:
* @param len Length of data
*/
inline C25519::Signature sign(const void *data,unsigned int len) const
- throw(std::runtime_error)
{
if (_privateKey)
return C25519::sign(*_privateKey,_publicKey,data,len);
- throw std::runtime_error("sign() requires a private key");
+ throw ZT_EXCEPTION_PRIVATE_KEY_REQUIRED;
}
/**
@@ -206,14 +199,9 @@ public:
}
/**
- * @return Identity type
- */
- inline Type type() const throw() { return IDENTITY_TYPE_C25519; }
-
- /**
* @return This identity's address
*/
- inline const Address &address() const throw() { return _address; }
+ inline const Address &address() const { return _address; }
/**
* Serialize this identity (binary)
@@ -226,11 +214,11 @@ public:
inline void serialize(Buffer<C> &b,bool includePrivate = false) const
{
_address.appendTo(b);
- b.append((unsigned char)IDENTITY_TYPE_C25519);
- b.append(_publicKey.data,(unsigned int)_publicKey.size());
+ b.append((uint8_t)0); // C25519/Ed25519 identity type
+ b.append(_publicKey.data,ZT_C25519_PUBLIC_KEY_LEN);
if ((_privateKey)&&(includePrivate)) {
- b.append((unsigned char)_privateKey->size());
- b.append(_privateKey->data,(unsigned int)_privateKey->size());
+ b.append((unsigned char)ZT_C25519_PRIVATE_KEY_LEN);
+ b.append(_privateKey->data,ZT_C25519_PRIVATE_KEY_LEN);
} else b.append((unsigned char)0);
}
@@ -257,18 +245,18 @@ public:
_address.setTo(b.field(p,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH);
p += ZT_ADDRESS_LENGTH;
- if (b[p++] != IDENTITY_TYPE_C25519)
- throw std::invalid_argument("unsupported identity type");
+ if (b[p++] != 0)
+ throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_INVALID_TYPE;
- memcpy(_publicKey.data,b.field(p,(unsigned int)_publicKey.size()),(unsigned int)_publicKey.size());
- p += (unsigned int)_publicKey.size();
+ ZT_FAST_MEMCPY(_publicKey.data,b.field(p,ZT_C25519_PUBLIC_KEY_LEN),ZT_C25519_PUBLIC_KEY_LEN);
+ p += ZT_C25519_PUBLIC_KEY_LEN;
unsigned int privateKeyLength = (unsigned int)b[p++];
if (privateKeyLength) {
if (privateKeyLength != ZT_C25519_PRIVATE_KEY_LEN)
- throw std::invalid_argument("invalid private key");
+ throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_INVALID_CRYPTOGRAPHIC_TOKEN;
_privateKey = new C25519::Private();
- memcpy(_privateKey->data,b.field(p,ZT_C25519_PRIVATE_KEY_LEN),ZT_C25519_PRIVATE_KEY_LEN);
+ ZT_FAST_MEMCPY(_privateKey->data,b.field(p,ZT_C25519_PRIVATE_KEY_LEN),ZT_C25519_PRIVATE_KEY_LEN);
p += ZT_C25519_PRIVATE_KEY_LEN;
}
@@ -279,9 +267,10 @@ public:
* Serialize to a more human-friendly string
*
* @param includePrivate If true, include private key (if it exists)
+ * @param buf Buffer to store string
* @return ASCII string representation of identity
*/
- std::string toString(bool includePrivate) const;
+ char *toString(bool includePrivate,char buf[ZT_IDENTITY_STRING_BUFFER_LENGTH]) const;
/**
* Deserialize a human-friendly string
@@ -293,19 +282,36 @@ public:
* @return True if deserialization appears successful
*/
bool fromString(const char *str);
- inline bool fromString(const std::string &str) { return fromString(str.c_str()); }
+
+ /**
+ * @return C25519 public key
+ */
+ inline const C25519::Public &publicKey() const { return _publicKey; }
+
+ /**
+ * @return C25519 key pair (only returns valid pair if private key is present in this Identity object)
+ */
+ inline const C25519::Pair privateKeyPair() const
+ {
+ C25519::Pair pair;
+ pair.pub = _publicKey;
+ if (_privateKey)
+ pair.priv = *_privateKey;
+ else memset(pair.priv.data,0,ZT_C25519_PRIVATE_KEY_LEN);
+ return pair;
+ }
/**
* @return True if this identity contains something
*/
- inline operator bool() const throw() { return (_address); }
-
- inline bool operator==(const Identity &id) const throw() { return ((_address == id._address)&&(_publicKey == id._publicKey)); }
- inline bool operator<(const Identity &id) const throw() { return ((_address < id._address)||((_address == id._address)&&(_publicKey < id._publicKey))); }
- inline bool operator!=(const Identity &id) const throw() { return !(*this == id); }
- inline bool operator>(const Identity &id) const throw() { return (id < *this); }
- inline bool operator<=(const Identity &id) const throw() { return !(id < *this); }
- inline bool operator>=(const Identity &id) const throw() { return !(*this < id); }
+ inline operator bool() const { return (_address); }
+
+ inline bool operator==(const Identity &id) const { return ((_address == id._address)&&(memcmp(_publicKey.data,id._publicKey.data,ZT_C25519_PUBLIC_KEY_LEN) == 0)); }
+ inline bool operator<(const Identity &id) const { return ((_address < id._address)||((_address == id._address)&&(memcmp(_publicKey.data,id._publicKey.data,ZT_C25519_PUBLIC_KEY_LEN) < 0))); }
+ inline bool operator!=(const Identity &id) const { return !(*this == id); }
+ inline bool operator>(const Identity &id) const { return (id < *this); }
+ inline bool operator<=(const Identity &id) const { return !(id < *this); }
+ inline bool operator>=(const Identity &id) const { return !(*this < id); }
private:
Address _address;
diff --git a/node/IncomingPacket.cpp b/node/IncomingPacket.cpp
index 37af8425..ff4fc94b 100644
--- a/node/IncomingPacket.cpp
+++ b/node/IncomingPacket.cpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#include <stdio.h>
@@ -34,13 +42,16 @@
#include "Salsa20.hpp"
#include "SHA512.hpp"
#include "World.hpp"
-#include "Cluster.hpp"
#include "Node.hpp"
-#include "DeferredPackets.hpp"
+#include "CertificateOfMembership.hpp"
+#include "Capability.hpp"
+#include "Tag.hpp"
+#include "Revocation.hpp"
+#include "Trace.hpp"
namespace ZeroTier {
-bool IncomingPacket::tryDecode(const RuntimeEnvironment *RR,bool deferred)
+bool IncomingPacket::tryDecode(const RuntimeEnvironment *RR,void *tPtr)
{
const Address sourceAddress(source());
@@ -52,410 +63,435 @@ bool IncomingPacket::tryDecode(const RuntimeEnvironment *RR,bool deferred)
// If this is marked as a packet via a trusted path, check source address and path ID.
// Obviously if no trusted paths are configured this always returns false and such
// packets are dropped on the floor.
- if (RR->topology->shouldInboundPathBeTrusted(_remoteAddress,trustedPathId())) {
+ const uint64_t tpid = trustedPathId();
+ if (RR->topology->shouldInboundPathBeTrusted(_path->address(),tpid)) {
trusted = true;
- TRACE("TRUSTED PATH packet approved from %s(%s), trusted path ID %llx",sourceAddress.toString().c_str(),_remoteAddress.toString().c_str(),trustedPathId());
} else {
- TRACE("dropped packet from %s(%s), cipher set to trusted path mode but path %llx@%s is not trusted!",sourceAddress.toString().c_str(),_remoteAddress.toString().c_str(),trustedPathId(),_remoteAddress.toString().c_str());
+ RR->t->incomingPacketMessageAuthenticationFailure(tPtr,_path,packetId(),sourceAddress,hops(),"path not trusted");
return true;
}
} else if ((c == ZT_PROTO_CIPHER_SUITE__C25519_POLY1305_NONE)&&(verb() == Packet::VERB_HELLO)) {
- // Unencrypted HELLOs require some potentially expensive verification, so
- // do this in the background if background processing is enabled.
- if ((RR->dpEnabled > 0)&&(!deferred)) {
- RR->dp->enqueue(this);
- return true; // 'handled' via deferring to background thread(s)
- } else {
- // A null pointer for peer to _doHELLO() tells it to run its own
- // special internal authentication logic. This is done for unencrypted
- // HELLOs to learn new identities, etc.
- SharedPtr<Peer> tmp;
- return _doHELLO(RR,tmp);
- }
+ // Only HELLO is allowed in the clear, but will still have a MAC
+ return _doHELLO(RR,tPtr,false);
}
- SharedPtr<Peer> peer(RR->topology->getPeer(sourceAddress));
+ const SharedPtr<Peer> peer(RR->topology->getPeer(tPtr,sourceAddress));
if (peer) {
if (!trusted) {
if (!dearmor(peer->key())) {
- TRACE("dropped packet from %s(%s), MAC authentication failed (size: %u)",sourceAddress.toString().c_str(),_remoteAddress.toString().c_str(),size());
+ RR->t->incomingPacketMessageAuthenticationFailure(tPtr,_path,packetId(),sourceAddress,hops(),"invalid MAC");
return true;
}
}
if (!uncompress()) {
- TRACE("dropped packet from %s(%s), compressed data invalid",sourceAddress.toString().c_str(),_remoteAddress.toString().c_str());
+ RR->t->incomingPacketInvalid(tPtr,_path,packetId(),sourceAddress,hops(),Packet::VERB_NOP,"LZ4 decompression failed");
return true;
}
const Packet::Verb v = verb();
- //TRACE("<< %s from %s(%s)",Packet::verbString(v),sourceAddress.toString().c_str(),_remoteAddress.toString().c_str());
switch(v) {
//case Packet::VERB_NOP:
default: // ignore unknown verbs, but if they pass auth check they are "received"
- peer->received(_localAddress,_remoteAddress,hops(),packetId(),v,0,Packet::VERB_NOP);
+ peer->received(tPtr,_path,hops(),packetId(),v,0,Packet::VERB_NOP,false,0);
return true;
-
- case Packet::VERB_HELLO: return _doHELLO(RR,peer);
- case Packet::VERB_ERROR: return _doERROR(RR,peer);
- case Packet::VERB_OK: return _doOK(RR,peer);
- case Packet::VERB_WHOIS: return _doWHOIS(RR,peer);
- case Packet::VERB_RENDEZVOUS: return _doRENDEZVOUS(RR,peer);
- case Packet::VERB_FRAME: return _doFRAME(RR,peer);
- case Packet::VERB_EXT_FRAME: return _doEXT_FRAME(RR,peer);
- case Packet::VERB_ECHO: return _doECHO(RR,peer);
- case Packet::VERB_MULTICAST_LIKE: return _doMULTICAST_LIKE(RR,peer);
- case Packet::VERB_NETWORK_MEMBERSHIP_CERTIFICATE: return _doNETWORK_MEMBERSHIP_CERTIFICATE(RR,peer);
- case Packet::VERB_NETWORK_CONFIG_REQUEST: return _doNETWORK_CONFIG_REQUEST(RR,peer);
- case Packet::VERB_NETWORK_CONFIG_REFRESH: return _doNETWORK_CONFIG_REFRESH(RR,peer);
- case Packet::VERB_MULTICAST_GATHER: return _doMULTICAST_GATHER(RR,peer);
- case Packet::VERB_MULTICAST_FRAME: return _doMULTICAST_FRAME(RR,peer);
- case Packet::VERB_PUSH_DIRECT_PATHS: return _doPUSH_DIRECT_PATHS(RR,peer);
- case Packet::VERB_CIRCUIT_TEST: return _doCIRCUIT_TEST(RR,peer);
- case Packet::VERB_CIRCUIT_TEST_REPORT: return _doCIRCUIT_TEST_REPORT(RR,peer);
- case Packet::VERB_REQUEST_PROOF_OF_WORK: return _doREQUEST_PROOF_OF_WORK(RR,peer);
+ case Packet::VERB_HELLO: return _doHELLO(RR,tPtr,true);
+ case Packet::VERB_ERROR: return _doERROR(RR,tPtr,peer);
+ case Packet::VERB_OK: return _doOK(RR,tPtr,peer);
+ case Packet::VERB_WHOIS: return _doWHOIS(RR,tPtr,peer);
+ case Packet::VERB_RENDEZVOUS: return _doRENDEZVOUS(RR,tPtr,peer);
+ case Packet::VERB_FRAME: return _doFRAME(RR,tPtr,peer);
+ case Packet::VERB_EXT_FRAME: return _doEXT_FRAME(RR,tPtr,peer);
+ case Packet::VERB_ECHO: return _doECHO(RR,tPtr,peer);
+ case Packet::VERB_MULTICAST_LIKE: return _doMULTICAST_LIKE(RR,tPtr,peer);
+ case Packet::VERB_NETWORK_CREDENTIALS: return _doNETWORK_CREDENTIALS(RR,tPtr,peer);
+ case Packet::VERB_NETWORK_CONFIG_REQUEST: return _doNETWORK_CONFIG_REQUEST(RR,tPtr,peer);
+ case Packet::VERB_NETWORK_CONFIG: return _doNETWORK_CONFIG(RR,tPtr,peer);
+ case Packet::VERB_MULTICAST_GATHER: return _doMULTICAST_GATHER(RR,tPtr,peer);
+ case Packet::VERB_MULTICAST_FRAME: return _doMULTICAST_FRAME(RR,tPtr,peer);
+ case Packet::VERB_PUSH_DIRECT_PATHS: return _doPUSH_DIRECT_PATHS(RR,tPtr,peer);
+ case Packet::VERB_USER_MESSAGE: return _doUSER_MESSAGE(RR,tPtr,peer);
+ case Packet::VERB_REMOTE_TRACE: return _doREMOTE_TRACE(RR,tPtr,peer);
}
} else {
- RR->sw->requestWhois(sourceAddress);
+ RR->sw->requestWhois(tPtr,RR->node->now(),sourceAddress);
return false;
}
} catch ( ... ) {
- // Exceptions are more informatively caught in _do...() handlers but
- // this outer try/catch will catch anything else odd.
- TRACE("dropped ??? from %s(%s): unexpected exception in tryDecode()",sourceAddress.toString().c_str(),_remoteAddress.toString().c_str());
+ RR->t->incomingPacketInvalid(tPtr,_path,packetId(),sourceAddress,hops(),verb(),"unexpected exception in tryDecode()");
return true;
}
}
-bool IncomingPacket::_doERROR(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer)
+bool IncomingPacket::_doERROR(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer)
{
- try {
- const Packet::Verb inReVerb = (Packet::Verb)(*this)[ZT_PROTO_VERB_ERROR_IDX_IN_RE_VERB];
- const uint64_t inRePacketId = at<uint64_t>(ZT_PROTO_VERB_ERROR_IDX_IN_RE_PACKET_ID);
- const 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),peer->address().toString().c_str(),_remoteAddress.toString().c_str(),Packet::verbString(inReVerb));
-
- switch(errorCode) {
-
- case Packet::ERROR_OBJ_NOT_FOUND:
- if (inReVerb == Packet::VERB_NETWORK_CONFIG_REQUEST) {
- SharedPtr<Network> network(RR->node->network(at<uint64_t>(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD)));
- if ((network)&&(network->controller() == peer->address()))
- network->setNotFound();
- }
- break;
-
- case Packet::ERROR_UNSUPPORTED_OPERATION:
- if (inReVerb == Packet::VERB_NETWORK_CONFIG_REQUEST) {
- SharedPtr<Network> network(RR->node->network(at<uint64_t>(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD)));
- if ((network)&&(network->controller() == peer->address()))
- network->setNotFound();
- }
- break;
-
- case Packet::ERROR_IDENTITY_COLLISION:
- if (RR->topology->isRoot(peer->identity()))
- RR->node->postEvent(ZT_EVENT_FATAL_ERROR_IDENTITY_COLLISION);
- break;
-
- case Packet::ERROR_NEED_MEMBERSHIP_CERTIFICATE: {
- /* Note: certificates are public so it's safe to push them to anyone
- * who asks. We won't communicate unless we also get a certificate
- * from the remote that agrees. */
- SharedPtr<Network> network(RR->node->network(at<uint64_t>(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD)));
- if ((network)&&(network->hasConfig())&&(network->config().com)) {
- Packet outp(peer->address(),RR->identity.address(),Packet::VERB_NETWORK_MEMBERSHIP_CERTIFICATE);
- network->config().com.serialize(outp);
- outp.armor(peer->key(),true);
- RR->node->putPacket(_localAddress,_remoteAddress,outp.data(),outp.size());
- }
- } break;
-
- case Packet::ERROR_NETWORK_ACCESS_DENIED_: {
- SharedPtr<Network> network(RR->node->network(at<uint64_t>(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD)));
+ const Packet::Verb inReVerb = (Packet::Verb)(*this)[ZT_PROTO_VERB_ERROR_IDX_IN_RE_VERB];
+ const uint64_t inRePacketId = at<uint64_t>(ZT_PROTO_VERB_ERROR_IDX_IN_RE_PACKET_ID);
+ const Packet::ErrorCode errorCode = (Packet::ErrorCode)(*this)[ZT_PROTO_VERB_ERROR_IDX_ERROR_CODE];
+ uint64_t networkId = 0;
+
+ /* Security note: we do not gate doERROR() with expectingReplyTo() to
+ * avoid having to log every outgoing packet ID. Instead we put the
+ * logic to determine whether we should consider an ERROR in each
+ * error handler. In most cases these are only trusted in specific
+ * circumstances. */
+
+ switch(errorCode) {
+
+ case Packet::ERROR_OBJ_NOT_FOUND:
+ // Object not found, currently only meaningful from network controllers.
+ if (inReVerb == Packet::VERB_NETWORK_CONFIG_REQUEST) {
+ const SharedPtr<Network> network(RR->node->network(at<uint64_t>(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD)));
if ((network)&&(network->controller() == peer->address()))
- network->setAccessDenied();
- } break;
+ network->setNotFound();
+ }
+ break;
+
+ case Packet::ERROR_UNSUPPORTED_OPERATION:
+ // This can be sent in response to any operation, though right now we only
+ // consider it meaningful from network controllers. This would indicate
+ // that the queried node does not support acting as a controller.
+ if (inReVerb == Packet::VERB_NETWORK_CONFIG_REQUEST) {
+ const SharedPtr<Network> network(RR->node->network(at<uint64_t>(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD)));
+ if ((network)&&(network->controller() == peer->address()))
+ network->setNotFound();
+ }
+ break;
+
+ case Packet::ERROR_IDENTITY_COLLISION:
+ // FIXME: for federation this will need a payload with a signature or something.
+ if (RR->topology->isUpstream(peer->identity()))
+ RR->node->postEvent(tPtr,ZT_EVENT_FATAL_ERROR_IDENTITY_COLLISION);
+ break;
+
+ case Packet::ERROR_NEED_MEMBERSHIP_CERTIFICATE: {
+ // Peers can send this in response to frames if they do not have a recent enough COM from us
+ networkId = at<uint64_t>(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD);
+ const SharedPtr<Network> network(RR->node->network(networkId));
+ const int64_t now = RR->node->now();
+ if ( (network) && (network->config().com) && (peer->rateGateIncomingComRequest(now)) )
+ network->pushCredentialsNow(tPtr,peer->address(),now);
+ } break;
+
+ case Packet::ERROR_NETWORK_ACCESS_DENIED_: {
+ // Network controller: network access denied.
+ const SharedPtr<Network> network(RR->node->network(at<uint64_t>(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD)));
+ if ((network)&&(network->controller() == peer->address()))
+ network->setAccessDenied();
+ } break;
+
+ case Packet::ERROR_UNWANTED_MULTICAST: {
+ // Members of networks can use this error to indicate that they no longer
+ // want to receive multicasts on a given channel.
+ networkId = at<uint64_t>(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD);
+ const SharedPtr<Network> network(RR->node->network(networkId));
+ if ((network)&&(network->gate(tPtr,peer))) {
+ const MulticastGroup mg(MAC(field(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD + 8,6),6),at<uint32_t>(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD + 14));
+ RR->mc->remove(network->id(),mg,peer->address());
+ }
+ } break;
- case Packet::ERROR_UNWANTED_MULTICAST: {
- uint64_t nwid = at<uint64_t>(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD);
- MulticastGroup mg(MAC(field(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD + 8,6),6),at<uint32_t>(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD + 14));
- TRACE("%.16llx: peer %s unsubscrubed from multicast group %s",nwid,peer->address().toString().c_str(),mg.toString().c_str());
- RR->mc->remove(nwid,mg,peer->address());
- } break;
+ default: break;
+ }
- default: break;
- }
+ peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_ERROR,inRePacketId,inReVerb,false,networkId);
- peer->received(_localAddress,_remoteAddress,hops(),packetId(),Packet::VERB_ERROR,inRePacketId,inReVerb);
- } catch ( ... ) {
- TRACE("dropped ERROR from %s(%s): unexpected exception",peer->address().toString().c_str(),_remoteAddress.toString().c_str());
- }
return true;
}
-bool IncomingPacket::_doHELLO(const RuntimeEnvironment *RR,SharedPtr<Peer> &peer)
+bool IncomingPacket::_doHELLO(const RuntimeEnvironment *RR,void *tPtr,const bool alreadyAuthenticated)
{
- /* Note: this is the only packet ever sent in the clear, and it's also
- * the only packet that we authenticate via a different path. Authentication
- * occurs here and is based on the validity of the identity and the
- * integrity of the packet's MAC, but it must be done after we check
- * the identity since HELLO is a mechanism for learning new identities
- * in the first place. */
+ const int64_t now = RR->node->now();
+
+ const uint64_t pid = packetId();
+ const Address fromAddress(source());
+ const unsigned int protoVersion = (*this)[ZT_PROTO_VERB_HELLO_IDX_PROTOCOL_VERSION];
+ const unsigned int vMajor = (*this)[ZT_PROTO_VERB_HELLO_IDX_MAJOR_VERSION];
+ const unsigned int vMinor = (*this)[ZT_PROTO_VERB_HELLO_IDX_MINOR_VERSION];
+ const unsigned int vRevision = at<uint16_t>(ZT_PROTO_VERB_HELLO_IDX_REVISION);
+ const int64_t timestamp = at<int64_t>(ZT_PROTO_VERB_HELLO_IDX_TIMESTAMP);
+ Identity id;
+ unsigned int ptr = ZT_PROTO_VERB_HELLO_IDX_IDENTITY + id.deserialize(*this,ZT_PROTO_VERB_HELLO_IDX_IDENTITY);
+
+ if (protoVersion < ZT_PROTO_VERSION_MIN) {
+ RR->t->incomingPacketDroppedHELLO(tPtr,_path,pid,fromAddress,"protocol version too old");
+ return true;
+ }
+ if (fromAddress != id.address()) {
+ RR->t->incomingPacketDroppedHELLO(tPtr,_path,pid,fromAddress,"identity/address mismatch");
+ return true;
+ }
- try {
- const uint64_t pid = packetId();
- const Address fromAddress(source());
- const unsigned int protoVersion = (*this)[ZT_PROTO_VERB_HELLO_IDX_PROTOCOL_VERSION];
- const unsigned int vMajor = (*this)[ZT_PROTO_VERB_HELLO_IDX_MAJOR_VERSION];
- const unsigned int vMinor = (*this)[ZT_PROTO_VERB_HELLO_IDX_MINOR_VERSION];
- const unsigned int vRevision = at<uint16_t>(ZT_PROTO_VERB_HELLO_IDX_REVISION);
- const uint64_t timestamp = at<uint64_t>(ZT_PROTO_VERB_HELLO_IDX_TIMESTAMP);
-
- Identity id;
- InetAddress externalSurfaceAddress;
- uint64_t worldId = ZT_WORLD_ID_NULL;
- uint64_t worldTimestamp = 0;
- {
- unsigned int ptr = ZT_PROTO_VERB_HELLO_IDX_IDENTITY + id.deserialize(*this,ZT_PROTO_VERB_HELLO_IDX_IDENTITY);
- if (ptr < size()) // ZeroTier One < 1.0.3 did not include physical destination address info
- ptr += externalSurfaceAddress.deserialize(*this,ptr);
- if ((ptr + 16) <= size()) { // older versions also did not include World IDs or timestamps
- worldId = at<uint64_t>(ptr); ptr += 8;
- worldTimestamp = at<uint64_t>(ptr);
- }
- }
+ SharedPtr<Peer> peer(RR->topology->getPeer(tPtr,id.address()));
+ if (peer) {
+ // We already have an identity with this address -- check for collisions
+ if (!alreadyAuthenticated) {
+ if (peer->identity() != id) {
+ // Identity is different from the one we already have -- address collision
- if (protoVersion < ZT_PROTO_VERSION_MIN) {
- TRACE("dropped HELLO from %s(%s): protocol version too old",id.address().toString().c_str(),_remoteAddress.toString().c_str());
- return true;
- }
- if (fromAddress != id.address()) {
- TRACE("dropped HELLO from %s(%s): identity not for sending address",fromAddress.toString().c_str(),_remoteAddress.toString().c_str());
- return true;
- }
+ // Check rate limits
+ if (!RR->node->rateGateIdentityVerification(now,_path->address()))
+ return true;
- if (!peer) { // peer == NULL is the normal case here
- peer = RR->topology->getPeer(id.address());
- if (peer) {
- // We already have an identity with this address -- check for collisions
-
- if (peer->identity() != id) {
- // Identity is different from the one we already have -- address collision
-
- unsigned char key[ZT_PEER_SECRET_KEY_LENGTH];
- if (RR->identity.agree(id,key,ZT_PEER_SECRET_KEY_LENGTH)) {
- if (dearmor(key)) { // ensure packet is authentic, otherwise drop
- TRACE("rejected HELLO from %s(%s): address already claimed",id.address().toString().c_str(),_remoteAddress.toString().c_str());
- Packet outp(id.address(),RR->identity.address(),Packet::VERB_ERROR);
- outp.append((unsigned char)Packet::VERB_HELLO);
- outp.append((uint64_t)pid);
- outp.append((unsigned char)Packet::ERROR_IDENTITY_COLLISION);
- outp.armor(key,true);
- RR->node->putPacket(_localAddress,_remoteAddress,outp.data(),outp.size());
- } else {
- TRACE("rejected HELLO from %s(%s): packet failed authentication",id.address().toString().c_str(),_remoteAddress.toString().c_str());
- }
+ uint8_t key[ZT_PEER_SECRET_KEY_LENGTH];
+ if (RR->identity.agree(id,key,ZT_PEER_SECRET_KEY_LENGTH)) {
+ if (dearmor(key)) { // ensure packet is authentic, otherwise drop
+ RR->t->incomingPacketDroppedHELLO(tPtr,_path,pid,fromAddress,"address collision");
+ Packet outp(id.address(),RR->identity.address(),Packet::VERB_ERROR);
+ outp.append((uint8_t)Packet::VERB_HELLO);
+ outp.append((uint64_t)pid);
+ outp.append((uint8_t)Packet::ERROR_IDENTITY_COLLISION);
+ outp.armor(key,true);
+ _path->send(RR,tPtr,outp.data(),outp.size(),RR->node->now());
} else {
- TRACE("rejected HELLO from %s(%s): key agreement failed",id.address().toString().c_str(),_remoteAddress.toString().c_str());
+ RR->t->incomingPacketMessageAuthenticationFailure(tPtr,_path,pid,fromAddress,hops(),"invalid MAC");
}
-
- return true;
} else {
- // Identity is the same as the one we already have -- check packet integrity
-
- if (!dearmor(peer->key())) {
- TRACE("rejected HELLO from %s(%s): packet failed authentication",id.address().toString().c_str(),_remoteAddress.toString().c_str());
- return true;
- }
-
- // Continue at // VALID
+ RR->t->incomingPacketMessageAuthenticationFailure(tPtr,_path,pid,fromAddress,hops(),"invalid identity");
}
- } else {
- // We don't already have an identity with this address -- validate and learn it
- // Check identity proof of work
- if (!id.locallyValidate()) {
- TRACE("dropped HELLO from %s(%s): identity invalid",id.address().toString().c_str(),_remoteAddress.toString().c_str());
- return true;
- }
+ return true;
+ } else {
+ // Identity is the same as the one we already have -- check packet integrity
- // Check packet integrity and authentication
- SharedPtr<Peer> newPeer(new Peer(RR,RR->identity,id));
- if (!dearmor(newPeer->key())) {
- TRACE("rejected HELLO from %s(%s): packet failed authentication",id.address().toString().c_str(),_remoteAddress.toString().c_str());
+ if (!dearmor(peer->key())) {
+ RR->t->incomingPacketMessageAuthenticationFailure(tPtr,_path,pid,fromAddress,hops(),"invalid MAC");
return true;
}
- peer = RR->topology->addPeer(newPeer);
// Continue at // VALID
}
+ } // else if alreadyAuthenticated then continue at // VALID
+ } else {
+ // We don't already have an identity with this address -- validate and learn it
- // VALID -- if we made it here, packet passed identity and authenticity checks!
+ // Sanity check: this basically can't happen
+ if (alreadyAuthenticated) {
+ RR->t->incomingPacketDroppedHELLO(tPtr,_path,pid,fromAddress,"illegal alreadyAuthenticated state");
+ return true;
}
- if (externalSurfaceAddress)
- RR->sa->iam(id.address(),_localAddress,_remoteAddress,externalSurfaceAddress,RR->topology->isRoot(id),RR->node->now());
-
- Packet outp(id.address(),RR->identity.address(),Packet::VERB_OK);
- outp.append((unsigned char)Packet::VERB_HELLO);
- outp.append((uint64_t)pid);
- outp.append((uint64_t)timestamp);
- outp.append((unsigned char)ZT_PROTO_VERSION);
- outp.append((unsigned char)ZEROTIER_ONE_VERSION_MAJOR);
- outp.append((unsigned char)ZEROTIER_ONE_VERSION_MINOR);
- outp.append((uint16_t)ZEROTIER_ONE_VERSION_REVISION);
- if (protoVersion >= 5) {
- _remoteAddress.serialize(outp);
- } else {
- /* LEGACY COMPATIBILITY HACK:
- *
- * For a while now (since 1.0.3), ZeroTier has recognized changes in
- * its network environment empirically by examining its external network
- * address as reported by trusted peers. In versions prior to 1.1.0
- * (protocol version < 5), they did this by saving a snapshot of this
- * information (in SelfAwareness.hpp) keyed by reporting device ID and
- * address type.
- *
- * This causes problems when clustering is combined with symmetric NAT.
- * Symmetric NAT remaps ports, so different endpoints in a cluster will
- * report back different exterior addresses. Since the old code keys
- * this by device ID and not sending physical address and compares the
- * entire address including port, it constantly thinks its external
- * surface is changing and resets connections when talking to a cluster.
- *
- * In new code we key by sending physical address and device and we also
- * take the more conservative position of only interpreting changes in
- * IP address (neglecting port) as a change in network topology that
- * necessitates a reset. But we can make older clients work here by
- * nulling out the port field. Since this info is only used for empirical
- * detection of link changes, it doesn't break anything else.
- */
- InetAddress tmpa(_remoteAddress);
- tmpa.setPort(0);
- tmpa.serialize(outp);
+ // Check rate limits
+ if (!RR->node->rateGateIdentityVerification(now,_path->address())) {
+ RR->t->incomingPacketDroppedHELLO(tPtr,_path,pid,fromAddress,"rate limit exceeded");
+ return true;
}
- if ((worldId != ZT_WORLD_ID_NULL)&&(RR->topology->worldTimestamp() > worldTimestamp)&&(worldId == RR->topology->worldId())) {
- World w(RR->topology->world());
- const unsigned int sizeAt = outp.size();
- outp.addSize(2); // make room for 16-bit size field
- w.serialize(outp,false);
- outp.setAt<uint16_t>(sizeAt,(uint16_t)(outp.size() - (sizeAt + 2)));
- } else {
- outp.append((uint16_t)0); // no world update needed
+ // Check packet integrity and MAC (this is faster than locallyValidate() so do it first to filter out total crap)
+ SharedPtr<Peer> newPeer(new Peer(RR,RR->identity,id));
+ if (!dearmor(newPeer->key())) {
+ RR->t->incomingPacketMessageAuthenticationFailure(tPtr,_path,pid,fromAddress,hops(),"invalid MAC");
+ return true;
}
- outp.armor(peer->key(),true);
- RR->node->putPacket(_localAddress,_remoteAddress,outp.data(),outp.size());
+ // Check that identity's address is valid as per the derivation function
+ if (!id.locallyValidate()) {
+ RR->t->incomingPacketDroppedHELLO(tPtr,_path,pid,fromAddress,"invalid identity");
+ return true;
+ }
- peer->setRemoteVersion(protoVersion,vMajor,vMinor,vRevision); // important for this to go first so received() knows the version
- peer->received(_localAddress,_remoteAddress,hops(),pid,Packet::VERB_HELLO,0,Packet::VERB_NOP);
- } catch ( ... ) {
- TRACE("dropped HELLO from %s(%s): unexpected exception",source().toString().c_str(),_remoteAddress.toString().c_str());
+ peer = RR->topology->addPeer(tPtr,newPeer);
+
+ // Continue at // VALID
+ }
+
+ // VALID -- if we made it here, packet passed identity and authenticity checks!
+
+ // Get external surface address if present (was not in old versions)
+ InetAddress externalSurfaceAddress;
+ if (ptr < size()) {
+ ptr += externalSurfaceAddress.deserialize(*this,ptr);
+ if ((externalSurfaceAddress)&&(hops() == 0))
+ RR->sa->iam(tPtr,id.address(),_path->localSocket(),_path->address(),externalSurfaceAddress,RR->topology->isUpstream(id),now);
+ }
+
+ // Get primary planet world ID and world timestamp if present
+ uint64_t planetWorldId = 0;
+ uint64_t planetWorldTimestamp = 0;
+ if ((ptr + 16) <= size()) {
+ planetWorldId = at<uint64_t>(ptr); ptr += 8;
+ planetWorldTimestamp = at<uint64_t>(ptr); ptr += 8;
}
+
+ std::vector< std::pair<uint64_t,uint64_t> > moonIdsAndTimestamps;
+ if (ptr < size()) {
+ // Remainder of packet, if present, is encrypted
+ cryptField(peer->key(),ptr,size() - ptr);
+
+ // Get moon IDs and timestamps if present
+ if ((ptr + 2) <= size()) {
+ const unsigned int numMoons = at<uint16_t>(ptr); ptr += 2;
+ for(unsigned int i=0;i<numMoons;++i) {
+ if ((World::Type)(*this)[ptr++] == World::TYPE_MOON)
+ moonIdsAndTimestamps.push_back(std::pair<uint64_t,uint64_t>(at<uint64_t>(ptr),at<uint64_t>(ptr + 8)));
+ ptr += 16;
+ }
+ }
+ }
+
+ // Send OK(HELLO) with an echo of the packet's timestamp and some of the same
+ // information about us: version, sent-to address, etc.
+
+ Packet outp(id.address(),RR->identity.address(),Packet::VERB_OK);
+ outp.append((unsigned char)Packet::VERB_HELLO);
+ outp.append((uint64_t)pid);
+ outp.append((uint64_t)timestamp);
+ outp.append((unsigned char)ZT_PROTO_VERSION);
+ outp.append((unsigned char)ZEROTIER_ONE_VERSION_MAJOR);
+ outp.append((unsigned char)ZEROTIER_ONE_VERSION_MINOR);
+ outp.append((uint16_t)ZEROTIER_ONE_VERSION_REVISION);
+
+ if (protoVersion >= 5) {
+ _path->address().serialize(outp);
+ } else {
+ /* LEGACY COMPATIBILITY HACK:
+ *
+ * For a while now (since 1.0.3), ZeroTier has recognized changes in
+ * its network environment empirically by examining its external network
+ * address as reported by trusted peers. In versions prior to 1.1.0
+ * (protocol version < 5), they did this by saving a snapshot of this
+ * information (in SelfAwareness.hpp) keyed by reporting device ID and
+ * address type.
+ *
+ * This causes problems when clustering is combined with symmetric NAT.
+ * Symmetric NAT remaps ports, so different endpoints in a cluster will
+ * report back different exterior addresses. Since the old code keys
+ * this by device ID and not sending physical address and compares the
+ * entire address including port, it constantly thinks its external
+ * surface is changing and resets connections when talking to a cluster.
+ *
+ * In new code we key by sending physical address and device and we also
+ * take the more conservative position of only interpreting changes in
+ * IP address (neglecting port) as a change in network topology that
+ * necessitates a reset. But we can make older clients work here by
+ * nulling out the port field. Since this info is only used for empirical
+ * detection of link changes, it doesn't break anything else.
+ */
+ InetAddress tmpa(_path->address());
+ tmpa.setPort(0);
+ tmpa.serialize(outp);
+ }
+
+ const unsigned int worldUpdateSizeAt = outp.size();
+ outp.addSize(2); // make room for 16-bit size field
+ if ((planetWorldId)&&(RR->topology->planetWorldTimestamp() > planetWorldTimestamp)&&(planetWorldId == RR->topology->planetWorldId())) {
+ RR->topology->planet().serialize(outp,false);
+ }
+ if (moonIdsAndTimestamps.size() > 0) {
+ std::vector<World> moons(RR->topology->moons());
+ for(std::vector<World>::const_iterator m(moons.begin());m!=moons.end();++m) {
+ for(std::vector< std::pair<uint64_t,uint64_t> >::const_iterator i(moonIdsAndTimestamps.begin());i!=moonIdsAndTimestamps.end();++i) {
+ if (i->first == m->id()) {
+ if (m->timestamp() > i->second)
+ m->serialize(outp,false);
+ break;
+ }
+ }
+ }
+ }
+ outp.setAt<uint16_t>(worldUpdateSizeAt,(uint16_t)(outp.size() - (worldUpdateSizeAt + 2)));
+
+ outp.armor(peer->key(),true);
+ _path->send(RR,tPtr,outp.data(),outp.size(),now);
+
+ peer->setRemoteVersion(protoVersion,vMajor,vMinor,vRevision); // important for this to go first so received() knows the version
+ peer->received(tPtr,_path,hops(),pid,Packet::VERB_HELLO,0,Packet::VERB_NOP,false,0);
+
return true;
}
-bool IncomingPacket::_doOK(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer)
+bool IncomingPacket::_doOK(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer)
{
- try {
- const Packet::Verb inReVerb = (Packet::Verb)(*this)[ZT_PROTO_VERB_OK_IDX_IN_RE_VERB];
- const uint64_t inRePacketId = at<uint64_t>(ZT_PROTO_VERB_OK_IDX_IN_RE_PACKET_ID);
+ const Packet::Verb inReVerb = (Packet::Verb)(*this)[ZT_PROTO_VERB_OK_IDX_IN_RE_VERB];
+ const uint64_t inRePacketId = at<uint64_t>(ZT_PROTO_VERB_OK_IDX_IN_RE_PACKET_ID);
+ uint64_t networkId = 0;
- //TRACE("%s(%s): OK(%s)",source().toString().c_str(),_remoteAddress.toString().c_str(),Packet::verbString(inReVerb));
+ if (!RR->node->expectingReplyTo(inRePacketId))
+ return true;
- switch(inReVerb) {
+ switch(inReVerb) {
- case Packet::VERB_HELLO: {
- const unsigned int latency = std::min((unsigned int)(RR->node->now() - at<uint64_t>(ZT_PROTO_VERB_HELLO__OK__IDX_TIMESTAMP)),(unsigned int)0xffff);
- const unsigned int vProto = (*this)[ZT_PROTO_VERB_HELLO__OK__IDX_PROTOCOL_VERSION];
- const unsigned int vMajor = (*this)[ZT_PROTO_VERB_HELLO__OK__IDX_MAJOR_VERSION];
- const unsigned int vMinor = (*this)[ZT_PROTO_VERB_HELLO__OK__IDX_MINOR_VERSION];
- const unsigned int vRevision = at<uint16_t>(ZT_PROTO_VERB_HELLO__OK__IDX_REVISION);
+ case Packet::VERB_HELLO: {
+ const uint64_t latency = RR->node->now() - at<uint64_t>(ZT_PROTO_VERB_HELLO__OK__IDX_TIMESTAMP);
+ if (latency > ZT_HELLO_MAX_ALLOWABLE_LATENCY)
+ return true;
- if (vProto < ZT_PROTO_VERSION_MIN) {
- TRACE("%s(%s): OK(HELLO) dropped, protocol version too old",source().toString().c_str(),_remoteAddress.toString().c_str());
- return true;
- }
+ const unsigned int vProto = (*this)[ZT_PROTO_VERB_HELLO__OK__IDX_PROTOCOL_VERSION];
+ const unsigned int vMajor = (*this)[ZT_PROTO_VERB_HELLO__OK__IDX_MAJOR_VERSION];
+ const unsigned int vMinor = (*this)[ZT_PROTO_VERB_HELLO__OK__IDX_MINOR_VERSION];
+ const unsigned int vRevision = at<uint16_t>(ZT_PROTO_VERB_HELLO__OK__IDX_REVISION);
+ if (vProto < ZT_PROTO_VERSION_MIN)
+ return true;
+
+ InetAddress externalSurfaceAddress;
+ unsigned int ptr = ZT_PROTO_VERB_HELLO__OK__IDX_REVISION + 2;
- const bool trusted = RR->topology->isRoot(peer->identity());
+ // Get reported external surface address if present
+ if (ptr < size())
+ ptr += externalSurfaceAddress.deserialize(*this,ptr);
- InetAddress externalSurfaceAddress;
- unsigned int ptr = ZT_PROTO_VERB_HELLO__OK__IDX_REVISION + 2;
- if (ptr < size()) // ZeroTier One < 1.0.3 did not include this field
- ptr += externalSurfaceAddress.deserialize(*this,ptr);
- if ((trusted)&&((ptr + 2) <= size())) { // older versions also did not include this field, and right now we only use if from a root
- World worldUpdate;
- const unsigned int worldLen = at<uint16_t>(ptr); ptr += 2;
- if (worldLen > 0) {
+ // Handle planet or moon updates if present
+ if ((ptr + 2) <= size()) {
+ const unsigned int worldsLen = at<uint16_t>(ptr); ptr += 2;
+ if (RR->topology->shouldAcceptWorldUpdateFrom(peer->address())) {
+ const unsigned int endOfWorlds = ptr + worldsLen;
+ while (ptr < endOfWorlds) {
World w;
- w.deserialize(*this,ptr);
- RR->topology->worldUpdateIfValid(w);
+ ptr += w.deserialize(*this,ptr);
+ RR->topology->addWorld(tPtr,w,false);
}
+ } else {
+ ptr += worldsLen;
}
+ }
- TRACE("%s(%s): OK(HELLO), version %u.%u.%u, latency %u, reported external address %s",source().toString().c_str(),_remoteAddress.toString().c_str(),vMajor,vMinor,vRevision,latency,((externalSurfaceAddress) ? externalSurfaceAddress.toString().c_str() : "(none)"));
-
- peer->addDirectLatencyMeasurment(latency);
- peer->setRemoteVersion(vProto,vMajor,vMinor,vRevision);
+ if (!hops())
+ _path->updateLatency((unsigned int)latency);
- if (externalSurfaceAddress)
- RR->sa->iam(peer->address(),_localAddress,_remoteAddress,externalSurfaceAddress,trusted,RR->node->now());
- } break;
+ peer->setRemoteVersion(vProto,vMajor,vMinor,vRevision);
- case Packet::VERB_WHOIS: {
- if (RR->topology->isRoot(peer->identity())) {
- const Identity id(*this,ZT_PROTO_VERB_WHOIS__OK__IDX_IDENTITY);
- // Right now we can skip this since OK(WHOIS) is only accepted from
- // roots. In the future it should be done if we query less trusted
- // sources.
- //if (id.locallyValidate())
- RR->sw->doAnythingWaitingForPeer(RR->topology->addPeer(SharedPtr<Peer>(new Peer(RR,RR->identity,id))));
- }
- } break;
-
- case Packet::VERB_NETWORK_CONFIG_REQUEST: {
- const SharedPtr<Network> nw(RR->node->network(at<uint64_t>(ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST__OK__IDX_NETWORK_ID)));
- if ((nw)&&(nw->controller() == peer->address())) {
- const unsigned int nclen = at<uint16_t>(ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST__OK__IDX_DICT_LEN);
- if (nclen) {
- Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> dconf((const char *)field(ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST__OK__IDX_DICT,nclen),nclen);
- NetworkConfig nconf;
- if (nconf.fromDictionary(dconf)) {
- nw->setConfiguration(nconf,true);
- TRACE("got network configuration for network %.16llx from %s",(unsigned long long)nw->id(),source().toString().c_str());
- }
- }
- }
- } break;
+ if ((externalSurfaceAddress)&&(hops() == 0))
+ RR->sa->iam(tPtr,peer->address(),_path->localSocket(),_path->address(),externalSurfaceAddress,RR->topology->isUpstream(peer->identity()),RR->node->now());
+ } break;
- //case Packet::VERB_ECHO: {
- //} break;
-
- case Packet::VERB_MULTICAST_GATHER: {
- const uint64_t nwid = at<uint64_t>(ZT_PROTO_VERB_MULTICAST_GATHER__OK__IDX_NETWORK_ID);
+ case Packet::VERB_WHOIS:
+ if (RR->topology->isUpstream(peer->identity())) {
+ const Identity id(*this,ZT_PROTO_VERB_WHOIS__OK__IDX_IDENTITY);
+ RR->sw->doAnythingWaitingForPeer(tPtr,RR->topology->addPeer(tPtr,SharedPtr<Peer>(new Peer(RR,RR->identity,id))));
+ }
+ break;
+
+ case Packet::VERB_NETWORK_CONFIG_REQUEST: {
+ networkId = at<uint64_t>(ZT_PROTO_VERB_OK_IDX_PAYLOAD);
+ const SharedPtr<Network> network(RR->node->network(networkId));
+ if (network)
+ network->handleConfigChunk(tPtr,packetId(),source(),*this,ZT_PROTO_VERB_OK_IDX_PAYLOAD);
+ } break;
+
+ case Packet::VERB_MULTICAST_GATHER: {
+ networkId = at<uint64_t>(ZT_PROTO_VERB_MULTICAST_GATHER__OK__IDX_NETWORK_ID);
+ const SharedPtr<Network> network(RR->node->network(networkId));
+ if (network) {
const MulticastGroup mg(MAC(field(ZT_PROTO_VERB_MULTICAST_GATHER__OK__IDX_MAC,6),6),at<uint32_t>(ZT_PROTO_VERB_MULTICAST_GATHER__OK__IDX_ADI));
- TRACE("%s(%s): OK(MULTICAST_GATHER) %.16llx/%s length %u",source().toString().c_str(),_remoteAddress.toString().c_str(),nwid,mg.toString().c_str(),size());
const unsigned int count = at<uint16_t>(ZT_PROTO_VERB_MULTICAST_GATHER__OK__IDX_GATHER_RESULTS + 4);
- RR->mc->addMultiple(RR->node->now(),nwid,mg,field(ZT_PROTO_VERB_MULTICAST_GATHER__OK__IDX_GATHER_RESULTS + 6,count * 5),count,at<uint32_t>(ZT_PROTO_VERB_MULTICAST_GATHER__OK__IDX_GATHER_RESULTS));
- } break;
-
- case Packet::VERB_MULTICAST_FRAME: {
- const unsigned int flags = (*this)[ZT_PROTO_VERB_MULTICAST_FRAME__OK__IDX_FLAGS];
- const uint64_t nwid = at<uint64_t>(ZT_PROTO_VERB_MULTICAST_FRAME__OK__IDX_NETWORK_ID);
- const MulticastGroup mg(MAC(field(ZT_PROTO_VERB_MULTICAST_FRAME__OK__IDX_MAC,6),6),at<uint32_t>(ZT_PROTO_VERB_MULTICAST_FRAME__OK__IDX_ADI));
+ RR->mc->addMultiple(tPtr,RR->node->now(),networkId,mg,field(ZT_PROTO_VERB_MULTICAST_GATHER__OK__IDX_GATHER_RESULTS + 6,count * 5),count,at<uint32_t>(ZT_PROTO_VERB_MULTICAST_GATHER__OK__IDX_GATHER_RESULTS));
+ }
+ } break;
- //TRACE("%s(%s): OK(MULTICAST_FRAME) %.16llx/%s flags %.2x",peer->address().toString().c_str(),_remoteAddress.toString().c_str(),nwid,mg.toString().c_str(),flags);
+ case Packet::VERB_MULTICAST_FRAME: {
+ const unsigned int flags = (*this)[ZT_PROTO_VERB_MULTICAST_FRAME__OK__IDX_FLAGS];
+ networkId = at<uint64_t>(ZT_PROTO_VERB_MULTICAST_FRAME__OK__IDX_NETWORK_ID);
+ const MulticastGroup mg(MAC(field(ZT_PROTO_VERB_MULTICAST_FRAME__OK__IDX_MAC,6),6),at<uint32_t>(ZT_PROTO_VERB_MULTICAST_FRAME__OK__IDX_ADI));
+ const SharedPtr<Network> network(RR->node->network(networkId));
+ if (network) {
unsigned int offset = 0;
- if ((flags & 0x01) != 0) {
- // OK(MULTICAST_FRAME) includes certificate of membership update
+ if ((flags & 0x01) != 0) { // deprecated but still used by older peers
CertificateOfMembership com;
offset += com.deserialize(*this,ZT_PROTO_VERB_MULTICAST_FRAME__OK__IDX_COM_AND_GATHER_RESULTS);
- peer->validateAndSetNetworkMembershipCertificate(nwid,com);
+ if (com)
+ network->addCredential(tPtr,com);
}
if ((flags & 0x02) != 0) {
@@ -463,901 +499,684 @@ bool IncomingPacket::_doOK(const RuntimeEnvironment *RR,const SharedPtr<Peer> &p
offset += ZT_PROTO_VERB_MULTICAST_FRAME__OK__IDX_COM_AND_GATHER_RESULTS;
unsigned int totalKnown = at<uint32_t>(offset); offset += 4;
unsigned int count = at<uint16_t>(offset); offset += 2;
- RR->mc->addMultiple(RR->node->now(),nwid,mg,field(offset,count * 5),count,totalKnown);
+ RR->mc->addMultiple(tPtr,RR->node->now(),networkId,mg,field(offset,count * 5),count,totalKnown);
}
- } break;
-
- default: break;
- }
+ }
+ } break;
- peer->received(_localAddress,_remoteAddress,hops(),packetId(),Packet::VERB_OK,inRePacketId,inReVerb);
- } catch ( ... ) {
- TRACE("dropped OK from %s(%s): unexpected exception",source().toString().c_str(),_remoteAddress.toString().c_str());
+ default: break;
}
+
+ peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_OK,inRePacketId,inReVerb,false,networkId);
+
return true;
}
-bool IncomingPacket::_doWHOIS(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer)
+bool IncomingPacket::_doWHOIS(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer)
{
- try {
- if (payloadLength() == ZT_ADDRESS_LENGTH) {
- Identity queried(RR->topology->getIdentity(Address(payload(),ZT_ADDRESS_LENGTH)));
- if (queried) {
- Packet outp(peer->address(),RR->identity.address(),Packet::VERB_OK);
- outp.append((unsigned char)Packet::VERB_WHOIS);
- outp.append(packetId());
- queried.serialize(outp,false);
- outp.armor(peer->key(),true);
- RR->node->putPacket(_localAddress,_remoteAddress,outp.data(),outp.size());
- } else {
-#ifdef ZT_ENABLE_CLUSTER
- if (RR->cluster)
- RR->cluster->sendDistributedQuery(*this);
-#endif
- }
+ if ((!RR->topology->amUpstream())&&(!peer->rateGateInboundWhoisRequest(RR->node->now())))
+ return true;
+
+ Packet outp(peer->address(),RR->identity.address(),Packet::VERB_OK);
+ outp.append((unsigned char)Packet::VERB_WHOIS);
+ outp.append(packetId());
+
+ unsigned int count = 0;
+ unsigned int ptr = ZT_PACKET_IDX_PAYLOAD;
+ while ((ptr + ZT_ADDRESS_LENGTH) <= size()) {
+ const Address addr(field(ptr,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH);
+ ptr += ZT_ADDRESS_LENGTH;
+
+ const Identity id(RR->topology->getIdentity(tPtr,addr));
+ if (id) {
+ id.serialize(outp,false);
+ ++count;
} else {
- TRACE("dropped WHOIS from %s(%s): missing or invalid address",source().toString().c_str(),_remoteAddress.toString().c_str());
+ // Request unknown WHOIS from upstream from us (if we have one)
+ RR->sw->requestWhois(tPtr,RR->node->now(),addr);
}
- peer->received(_localAddress,_remoteAddress,hops(),packetId(),Packet::VERB_WHOIS,0,Packet::VERB_NOP);
- } catch ( ... ) {
- TRACE("dropped WHOIS from %s(%s): unexpected exception",source().toString().c_str(),_remoteAddress.toString().c_str());
}
+
+ if (count > 0) {
+ outp.armor(peer->key(),true);
+ _path->send(RR,tPtr,outp.data(),outp.size(),RR->node->now());
+ }
+
+ peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_WHOIS,0,Packet::VERB_NOP,false,0);
+
return true;
}
-bool IncomingPacket::_doRENDEZVOUS(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer)
+bool IncomingPacket::_doRENDEZVOUS(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer)
{
- try {
- if (RR->topology->isUpstream(peer->identity())) {
- const Address with(field(ZT_PROTO_VERB_RENDEZVOUS_IDX_ZTADDRESS,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH);
- const SharedPtr<Peer> withPeer(RR->topology->getPeer(with));
- if (withPeer) {
- const unsigned int port = at<uint16_t>(ZT_PROTO_VERB_RENDEZVOUS_IDX_PORT);
- const unsigned int addrlen = (*this)[ZT_PROTO_VERB_RENDEZVOUS_IDX_ADDRLEN];
- if ((port > 0)&&((addrlen == 4)||(addrlen == 16))) {
- peer->received(_localAddress,_remoteAddress,hops(),packetId(),Packet::VERB_RENDEZVOUS,0,Packet::VERB_NOP);
-
- 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",peer->address().toString().c_str(),with.toString().c_str(),atAddr.toString().c_str());
- if (RR->node->shouldUsePathForZeroTierTraffic(_localAddress,atAddr))
- RR->sw->rendezvous(withPeer,_localAddress,atAddr);
- } else {
- TRACE("dropped corrupt RENDEZVOUS from %s(%s) (bad address or port)",peer->address().toString().c_str(),_remoteAddress.toString().c_str());
+ if (RR->topology->isUpstream(peer->identity())) {
+ const Address with(field(ZT_PROTO_VERB_RENDEZVOUS_IDX_ZTADDRESS,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH);
+ const SharedPtr<Peer> rendezvousWith(RR->topology->getPeer(tPtr,with));
+ if (rendezvousWith) {
+ const unsigned int port = at<uint16_t>(ZT_PROTO_VERB_RENDEZVOUS_IDX_PORT);
+ const unsigned int addrlen = (*this)[ZT_PROTO_VERB_RENDEZVOUS_IDX_ADDRLEN];
+ if ((port > 0)&&((addrlen == 4)||(addrlen == 16))) {
+ const InetAddress atAddr(field(ZT_PROTO_VERB_RENDEZVOUS_IDX_ADDRESS,addrlen),addrlen,port);
+ if (RR->node->shouldUsePathForZeroTierTraffic(tPtr,with,_path->localSocket(),atAddr)) {
+ const uint64_t junk = RR->node->prng();
+ RR->node->putPacket(tPtr,_path->localSocket(),atAddr,&junk,4,2); // send low-TTL junk packet to 'open' local NAT(s) and stateful firewalls
+ rendezvousWith->attemptToContactAt(tPtr,_path->localSocket(),atAddr,RR->node->now(),false);
}
- } else {
- RR->sw->requestWhois(with);
- TRACE("ignored RENDEZVOUS from %s(%s) to meet unknown peer %s",peer->address().toString().c_str(),_remoteAddress.toString().c_str(),with.toString().c_str());
}
- } else {
- TRACE("ignored RENDEZVOUS from %s(%s): not a root server or a network relay",peer->address().toString().c_str(),_remoteAddress.toString().c_str());
}
- } catch ( ... ) {
- TRACE("dropped RENDEZVOUS from %s(%s): unexpected exception",peer->address().toString().c_str(),_remoteAddress.toString().c_str());
}
+
+ peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_RENDEZVOUS,0,Packet::VERB_NOP,false,0);
+
return true;
}
-bool IncomingPacket::_doFRAME(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer)
+bool IncomingPacket::_doFRAME(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer)
{
- try {
- const SharedPtr<Network> network(RR->node->network(at<uint64_t>(ZT_PROTO_VERB_FRAME_IDX_NETWORK_ID)));
- if (network) {
+ const uint64_t nwid = at<uint64_t>(ZT_PROTO_VERB_FRAME_IDX_NETWORK_ID);
+ const SharedPtr<Network> network(RR->node->network(nwid));
+ bool trustEstablished = false;
+ if (network) {
+ if (network->gate(tPtr,peer)) {
+ trustEstablished = true;
if (size() > ZT_PROTO_VERB_FRAME_IDX_PAYLOAD) {
- if (!network->isAllowed(peer)) {
- TRACE("dropped FRAME from %s(%s): not a member of private network %.16llx",peer->address().toString().c_str(),_remoteAddress.toString().c_str(),(unsigned long long)network->id());
- _sendErrorNeedCertificate(RR,peer,network->id());
- return true;
- }
-
const unsigned int etherType = at<uint16_t>(ZT_PROTO_VERB_FRAME_IDX_ETHERTYPE);
- if (!network->config().permitsEtherType(etherType)) {
- TRACE("dropped FRAME from %s(%s): ethertype %.4x not allowed on %.16llx",peer->address().toString().c_str(),_remoteAddress.toString().c_str(),(unsigned int)etherType,(unsigned long long)network->id());
- return true;
- }
-
- const unsigned int payloadLen = size() - ZT_PROTO_VERB_FRAME_IDX_PAYLOAD;
- RR->node->putFrame(network->id(),network->userPtr(),MAC(peer->address(),network->id()),network->mac(),etherType,0,field(ZT_PROTO_VERB_FRAME_IDX_PAYLOAD,payloadLen),payloadLen);
+ const MAC sourceMac(peer->address(),nwid);
+ const unsigned int frameLen = size() - ZT_PROTO_VERB_FRAME_IDX_PAYLOAD;
+ const uint8_t *const frameData = reinterpret_cast<const uint8_t *>(data()) + ZT_PROTO_VERB_FRAME_IDX_PAYLOAD;
+ if (network->filterIncomingPacket(tPtr,peer,RR->identity.address(),sourceMac,network->mac(),frameData,frameLen,etherType,0) > 0)
+ RR->node->putFrame(tPtr,nwid,network->userPtr(),sourceMac,network->mac(),etherType,0,(const void *)frameData,frameLen);
}
-
- peer->received(_localAddress,_remoteAddress,hops(),packetId(),Packet::VERB_FRAME,0,Packet::VERB_NOP);
} else {
- 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));
+ _sendErrorNeedCredentials(RR,tPtr,peer,nwid);
+ RR->t->incomingNetworkAccessDenied(tPtr,network,_path,packetId(),size(),peer->address(),Packet::VERB_FRAME,true);
}
- } catch ( ... ) {
- TRACE("dropped FRAME from %s(%s): unexpected exception",source().toString().c_str(),_remoteAddress.toString().c_str());
+ } else {
+ _sendErrorNeedCredentials(RR,tPtr,peer,nwid);
}
+
+ peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_FRAME,0,Packet::VERB_NOP,trustEstablished,nwid);
+
return true;
}
-bool IncomingPacket::_doEXT_FRAME(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer)
+bool IncomingPacket::_doEXT_FRAME(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer)
{
- try {
- SharedPtr<Network> network(RR->node->network(at<uint64_t>(ZT_PROTO_VERB_EXT_FRAME_IDX_NETWORK_ID)));
- if (network) {
- if (size() > ZT_PROTO_VERB_EXT_FRAME_IDX_PAYLOAD) {
- const unsigned int flags = (*this)[ZT_PROTO_VERB_EXT_FRAME_IDX_FLAGS];
-
- unsigned int comLen = 0;
- if ((flags & 0x01) != 0) {
- CertificateOfMembership com;
- comLen = com.deserialize(*this,ZT_PROTO_VERB_EXT_FRAME_IDX_COM);
- peer->validateAndSetNetworkMembershipCertificate(network->id(),com);
- }
-
- if (!network->isAllowed(peer)) {
- TRACE("dropped EXT_FRAME from %s(%s): not a member of private network %.16llx",peer->address().toString().c_str(),_remoteAddress.toString().c_str(),network->id());
- _sendErrorNeedCertificate(RR,peer,network->id());
- return true;
- }
-
- // Everything after flags must be adjusted based on the length
- // of the certificate, if there was one...
-
- const unsigned int etherType = at<uint16_t>(comLen + ZT_PROTO_VERB_EXT_FRAME_IDX_ETHERTYPE);
- if (!network->config().permitsEtherType(etherType)) {
- TRACE("dropped EXT_FRAME from %s(%s): ethertype %.4x not allowed on network %.16llx",peer->address().toString().c_str(),_remoteAddress.toString().c_str(),(unsigned int)etherType,(unsigned long long)network->id());
- return true;
- }
+ const uint64_t nwid = at<uint64_t>(ZT_PROTO_VERB_EXT_FRAME_IDX_NETWORK_ID);
+ const SharedPtr<Network> network(RR->node->network(nwid));
+ if (network) {
+ const unsigned int flags = (*this)[ZT_PROTO_VERB_EXT_FRAME_IDX_FLAGS];
+
+ unsigned int comLen = 0;
+ if ((flags & 0x01) != 0) { // inline COM with EXT_FRAME is deprecated but still used with old peers
+ CertificateOfMembership com;
+ comLen = com.deserialize(*this,ZT_PROTO_VERB_EXT_FRAME_IDX_COM);
+ if (com)
+ network->addCredential(tPtr,com);
+ }
- const MAC to(field(comLen + ZT_PROTO_VERB_EXT_FRAME_IDX_TO,ZT_PROTO_VERB_EXT_FRAME_LEN_TO),ZT_PROTO_VERB_EXT_FRAME_LEN_TO);
- const MAC from(field(comLen + ZT_PROTO_VERB_EXT_FRAME_IDX_FROM,ZT_PROTO_VERB_EXT_FRAME_LEN_FROM),ZT_PROTO_VERB_EXT_FRAME_LEN_FROM);
+ if (!network->gate(tPtr,peer)) {
+ RR->t->incomingNetworkAccessDenied(tPtr,network,_path,packetId(),size(),peer->address(),Packet::VERB_EXT_FRAME,true);
+ _sendErrorNeedCredentials(RR,tPtr,peer,nwid);
+ peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_EXT_FRAME,0,Packet::VERB_NOP,false,nwid);
+ return true;
+ }
- if (to.isMulticast()) {
- TRACE("dropped EXT_FRAME from %s@%s(%s) to %s: destination is multicast, must use MULTICAST_FRAME",from.toString().c_str(),peer->address().toString().c_str(),_remoteAddress.toString().c_str(),to.toString().c_str());
- return true;
- }
+ if (size() > ZT_PROTO_VERB_EXT_FRAME_IDX_PAYLOAD) {
+ const unsigned int etherType = at<uint16_t>(comLen + ZT_PROTO_VERB_EXT_FRAME_IDX_ETHERTYPE);
+ const MAC to(field(comLen + ZT_PROTO_VERB_EXT_FRAME_IDX_TO,ZT_PROTO_VERB_EXT_FRAME_LEN_TO),ZT_PROTO_VERB_EXT_FRAME_LEN_TO);
+ const MAC from(field(comLen + ZT_PROTO_VERB_EXT_FRAME_IDX_FROM,ZT_PROTO_VERB_EXT_FRAME_LEN_FROM),ZT_PROTO_VERB_EXT_FRAME_LEN_FROM);
+ const unsigned int frameLen = size() - (comLen + ZT_PROTO_VERB_EXT_FRAME_IDX_PAYLOAD);
+ const uint8_t *const frameData = (const uint8_t *)field(comLen + ZT_PROTO_VERB_EXT_FRAME_IDX_PAYLOAD,frameLen);
- if ((!from)||(from.isMulticast())||(from == network->mac())) {
- TRACE("dropped EXT_FRAME from %s@%s(%s) to %s: invalid source MAC",from.toString().c_str(),peer->address().toString().c_str(),_remoteAddress.toString().c_str(),to.toString().c_str());
- return true;
- }
+ if ((!from)||(from == network->mac())) {
+ peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_EXT_FRAME,0,Packet::VERB_NOP,true,nwid); // trustEstablished because COM is okay
+ return true;
+ }
- if (from != MAC(peer->address(),network->id())) {
- if (network->config().permitsBridging(peer->address())) {
- network->learnBridgeRoute(from,peer->address());
- } else {
- TRACE("dropped EXT_FRAME from %s@%s(%s) to %s: sender not allowed to bridge into %.16llx",from.toString().c_str(),peer->address().toString().c_str(),_remoteAddress.toString().c_str(),to.toString().c_str(),network->id());
- return true;
- }
- } else if (to != network->mac()) {
- if (!network->config().permitsBridging(RR->identity.address())) {
- TRACE("dropped EXT_FRAME from %s@%s(%s) to %s: I cannot bridge to %.16llx or bridging disabled on network",from.toString().c_str(),peer->address().toString().c_str(),_remoteAddress.toString().c_str(),to.toString().c_str(),network->id());
- return true;
+ switch (network->filterIncomingPacket(tPtr,peer,RR->identity.address(),from,to,frameData,frameLen,etherType,0)) {
+ case 1:
+ if (from != MAC(peer->address(),nwid)) {
+ if (network->config().permitsBridging(peer->address())) {
+ network->learnBridgeRoute(from,peer->address());
+ } else {
+ RR->t->incomingNetworkFrameDropped(tPtr,network,_path,packetId(),size(),peer->address(),Packet::VERB_EXT_FRAME,from,to,"bridging not allowed (remote)");
+ peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_EXT_FRAME,0,Packet::VERB_NOP,true,nwid); // trustEstablished because COM is okay
+ return true;
+ }
+ } else if (to != network->mac()) {
+ if (to.isMulticast()) {
+ if (network->config().multicastLimit == 0) {
+ RR->t->incomingNetworkFrameDropped(tPtr,network,_path,packetId(),size(),peer->address(),Packet::VERB_EXT_FRAME,from,to,"multicast disabled");
+ peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_EXT_FRAME,0,Packet::VERB_NOP,true,nwid); // trustEstablished because COM is okay
+ return true;
+ }
+ } else if (!network->config().permitsBridging(RR->identity.address())) {
+ RR->t->incomingNetworkFrameDropped(tPtr,network,_path,packetId(),size(),peer->address(),Packet::VERB_EXT_FRAME,from,to,"bridging not allowed (local)");
+ peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_EXT_FRAME,0,Packet::VERB_NOP,true,nwid); // trustEstablished because COM is okay
+ return true;
+ }
}
- }
-
- const unsigned int payloadLen = size() - (comLen + ZT_PROTO_VERB_EXT_FRAME_IDX_PAYLOAD);
- RR->node->putFrame(network->id(),network->userPtr(),from,to,etherType,0,field(comLen + ZT_PROTO_VERB_EXT_FRAME_IDX_PAYLOAD,payloadLen),payloadLen);
+ // fall through -- 2 means accept regardless of bridging checks or other restrictions
+ case 2:
+ RR->node->putFrame(tPtr,nwid,network->userPtr(),from,to,etherType,0,(const void *)frameData,frameLen);
+ break;
}
-
- peer->received(_localAddress,_remoteAddress,hops(),packetId(),Packet::VERB_EXT_FRAME,0,Packet::VERB_NOP);
- } else {
- TRACE("dropped EXT_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));
}
- } catch ( ... ) {
- TRACE("dropped EXT_FRAME from %s(%s): unexpected exception",source().toString().c_str(),_remoteAddress.toString().c_str());
- }
- return true;
-}
-
-bool IncomingPacket::_doECHO(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer)
-{
- try {
- const uint64_t pid = packetId();
- Packet outp(peer->address(),RR->identity.address(),Packet::VERB_OK);
- outp.append((unsigned char)Packet::VERB_ECHO);
- outp.append((uint64_t)pid);
- if (size() > ZT_PACKET_IDX_PAYLOAD)
- outp.append(reinterpret_cast<const unsigned char *>(data()) + ZT_PACKET_IDX_PAYLOAD,size() - ZT_PACKET_IDX_PAYLOAD);
- outp.armor(peer->key(),true);
- RR->node->putPacket(_localAddress,_remoteAddress,outp.data(),outp.size());
- peer->received(_localAddress,_remoteAddress,hops(),pid,Packet::VERB_ECHO,0,Packet::VERB_NOP);
- } catch ( ... ) {
- TRACE("dropped ECHO from %s(%s): unexpected exception",source().toString().c_str(),_remoteAddress.toString().c_str());
- }
- return true;
-}
-
-bool IncomingPacket::_doMULTICAST_LIKE(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer)
-{
- try {
- const uint64_t now = RR->node->now();
- // Iterate through 18-byte network,MAC,ADI tuples
- for(unsigned int ptr=ZT_PACKET_IDX_PAYLOAD;ptr<size();ptr+=18) {
- const uint64_t nwid = at<uint64_t>(ptr);
- const MulticastGroup group(MAC(field(ptr + 8,6),6),at<uint32_t>(ptr + 14));
- RR->mc->add(now,nwid,group,peer->address());
+ if ((flags & 0x10) != 0) { // ACK requested
+ Packet outp(peer->address(),RR->identity.address(),Packet::VERB_OK);
+ outp.append((uint8_t)Packet::VERB_EXT_FRAME);
+ outp.append((uint64_t)packetId());
+ outp.append((uint64_t)nwid);
+ outp.armor(peer->key(),true);
+ _path->send(RR,tPtr,outp.data(),outp.size(),RR->node->now());
}
- peer->received(_localAddress,_remoteAddress,hops(),packetId(),Packet::VERB_MULTICAST_LIKE,0,Packet::VERB_NOP);
- } catch ( ... ) {
- TRACE("dropped MULTICAST_LIKE from %s(%s): unexpected exception",source().toString().c_str(),_remoteAddress.toString().c_str());
+ peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_EXT_FRAME,0,Packet::VERB_NOP,true,nwid);
+ } else {
+ peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_EXT_FRAME,0,Packet::VERB_NOP,false,nwid);
}
+
return true;
}
-bool IncomingPacket::_doNETWORK_MEMBERSHIP_CERTIFICATE(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer)
+bool IncomingPacket::_doECHO(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer)
{
- try {
- CertificateOfMembership com;
+ if (!peer->rateGateEchoRequest(RR->node->now()))
+ return true;
- unsigned int ptr = ZT_PACKET_IDX_PAYLOAD;
- while (ptr < size()) {
- ptr += com.deserialize(*this,ptr);
- peer->validateAndSetNetworkMembershipCertificate(com.networkId(),com);
- }
+ const uint64_t pid = packetId();
+ Packet outp(peer->address(),RR->identity.address(),Packet::VERB_OK);
+ outp.append((unsigned char)Packet::VERB_ECHO);
+ outp.append((uint64_t)pid);
+ if (size() > ZT_PACKET_IDX_PAYLOAD)
+ outp.append(reinterpret_cast<const unsigned char *>(data()) + ZT_PACKET_IDX_PAYLOAD,size() - ZT_PACKET_IDX_PAYLOAD);
+ outp.armor(peer->key(),true);
+ _path->send(RR,tPtr,outp.data(),outp.size(),RR->node->now());
+
+ peer->received(tPtr,_path,hops(),pid,Packet::VERB_ECHO,0,Packet::VERB_NOP,false,0);
- peer->received(_localAddress,_remoteAddress,hops(),packetId(),Packet::VERB_NETWORK_MEMBERSHIP_CERTIFICATE,0,Packet::VERB_NOP);
- } catch ( ... ) {
- TRACE("dropped NETWORK_MEMBERSHIP_CERTIFICATE from %s(%s): unexpected exception",source().toString().c_str(),_remoteAddress.toString().c_str());
- }
return true;
}
-bool IncomingPacket::_doNETWORK_CONFIG_REQUEST(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer)
+bool IncomingPacket::_doMULTICAST_LIKE(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer)
{
- try {
- const uint64_t nwid = at<uint64_t>(ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST_IDX_NETWORK_ID);
-
- const unsigned int metaDataLength = at<uint16_t>(ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST_IDX_DICT_LEN);
- const char *metaDataBytes = (const char *)field(ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST_IDX_DICT,metaDataLength);
- const Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> metaData(metaDataBytes,metaDataLength);
-
- //const uint64_t haveRevision = ((ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST_IDX_DICT + metaDataLength + 8) <= size()) ? at<uint64_t>(ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST_IDX_DICT + metaDataLength) : 0ULL;
-
- const unsigned int h = hops();
- const uint64_t pid = packetId();
- peer->received(_localAddress,_remoteAddress,h,pid,Packet::VERB_NETWORK_CONFIG_REQUEST,0,Packet::VERB_NOP);
-
- if (RR->localNetworkController) {
- NetworkConfig netconf;
- switch(RR->localNetworkController->doNetworkConfigRequest((h > 0) ? InetAddress() : _remoteAddress,RR->identity,peer->identity(),nwid,metaData,netconf)) {
-
- case NetworkController::NETCONF_QUERY_OK: {
- Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> dconf;
- if (netconf.toDictionary(dconf,metaData.getUI(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_VERSION,0) < 6)) {
- Packet outp(peer->address(),RR->identity.address(),Packet::VERB_OK);
- outp.append((unsigned char)Packet::VERB_NETWORK_CONFIG_REQUEST);
- outp.append(pid);
- outp.append(nwid);
- const unsigned int dlen = dconf.sizeBytes();
- outp.append((uint16_t)dlen);
- outp.append((const void *)dconf.data(),dlen);
- outp.compress();
- RR->sw->send(outp,true,0);
- }
- } break;
-
- case NetworkController::NETCONF_QUERY_OBJECT_NOT_FOUND: {
- Packet outp(peer->address(),RR->identity.address(),Packet::VERB_ERROR);
- outp.append((unsigned char)Packet::VERB_NETWORK_CONFIG_REQUEST);
- outp.append(pid);
- outp.append((unsigned char)Packet::ERROR_OBJ_NOT_FOUND);
- outp.append(nwid);
- outp.armor(peer->key(),true);
- RR->node->putPacket(_localAddress,_remoteAddress,outp.data(),outp.size());
- } break;
-
- case NetworkController::NETCONF_QUERY_ACCESS_DENIED: {
- Packet outp(peer->address(),RR->identity.address(),Packet::VERB_ERROR);
- outp.append((unsigned char)Packet::VERB_NETWORK_CONFIG_REQUEST);
- outp.append(pid);
- outp.append((unsigned char)Packet::ERROR_NETWORK_ACCESS_DENIED_);
- outp.append(nwid);
- outp.armor(peer->key(),true);
- RR->node->putPacket(_localAddress,_remoteAddress,outp.data(),outp.size());
- } break;
-
- case NetworkController::NETCONF_QUERY_INTERNAL_SERVER_ERROR:
- // TRACE("NETWORK_CONFIG_REQUEST failed: internal error: %s",netconf.get("error","(unknown)").c_str());
- break;
+ const int64_t now = RR->node->now();
- case NetworkController::NETCONF_QUERY_IGNORE:
- break;
+ uint64_t authOnNetwork[256]; // cache for approved network IDs
+ unsigned int authOnNetworkCount = 0;
+ SharedPtr<Network> network;
+ bool trustEstablished = false;
- default:
- TRACE("NETWORK_CONFIG_REQUEST failed: invalid return value from NetworkController::doNetworkConfigRequest()");
- break;
+ // Iterate through 18-byte network,MAC,ADI tuples
+ for(unsigned int ptr=ZT_PACKET_IDX_PAYLOAD;ptr<size();ptr+=18) {
+ const uint64_t nwid = at<uint64_t>(ptr);
+ bool auth = false;
+ for(unsigned int i=0;i<authOnNetworkCount;++i) {
+ if (nwid == authOnNetwork[i]) {
+ auth = true;
+ break;
+ }
+ }
+ if (!auth) {
+ if ((!network)||(network->id() != nwid))
+ network = RR->node->network(nwid);
+ const bool authOnNet = ((network)&&(network->gate(tPtr,peer)));
+ if (!authOnNet)
+ _sendErrorNeedCredentials(RR,tPtr,peer,nwid);
+ trustEstablished |= authOnNet;
+ if (authOnNet||RR->mc->cacheAuthorized(peer->address(),nwid,now)) {
+ auth = true;
+ if (authOnNetworkCount < 256) // sanity check, packets can't really be this big
+ authOnNetwork[authOnNetworkCount++] = nwid;
}
- } else {
- Packet outp(peer->address(),RR->identity.address(),Packet::VERB_ERROR);
- outp.append((unsigned char)Packet::VERB_NETWORK_CONFIG_REQUEST);
- outp.append(pid);
- outp.append((unsigned char)Packet::ERROR_UNSUPPORTED_OPERATION);
- outp.append(nwid);
- outp.armor(peer->key(),true);
- RR->node->putPacket(_localAddress,_remoteAddress,outp.data(),outp.size());
}
- } catch ( ... ) {
- TRACE("dropped NETWORK_CONFIG_REQUEST from %s(%s): unexpected exception",source().toString().c_str(),_remoteAddress.toString().c_str());
- }
- return true;
-}
-bool IncomingPacket::_doNETWORK_CONFIG_REFRESH(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer)
-{
- try {
- unsigned int ptr = ZT_PACKET_IDX_PAYLOAD;
- while ((ptr + 8) <= size()) {
- uint64_t nwid = at<uint64_t>(ptr);
- SharedPtr<Network> nw(RR->node->network(nwid));
- if ((nw)&&(peer->address() == nw->controller()))
- nw->requestConfiguration();
- ptr += 8;
+ if (auth) {
+ const MulticastGroup group(MAC(field(ptr + 8,6),6),at<uint32_t>(ptr + 14));
+ RR->mc->add(tPtr,now,nwid,group,peer->address());
}
- peer->received(_localAddress,_remoteAddress,hops(),packetId(),Packet::VERB_NETWORK_CONFIG_REFRESH,0,Packet::VERB_NOP);
- } catch ( ... ) {
- TRACE("dropped NETWORK_CONFIG_REFRESH from %s(%s): unexpected exception",source().toString().c_str(),_remoteAddress.toString().c_str());
}
- return true;
-}
-
-bool IncomingPacket::_doMULTICAST_GATHER(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer)
-{
- try {
- const uint64_t nwid = at<uint64_t>(ZT_PROTO_VERB_MULTICAST_GATHER_IDX_NETWORK_ID);
- const MulticastGroup mg(MAC(field(ZT_PROTO_VERB_MULTICAST_GATHER_IDX_MAC,6),6),at<uint32_t>(ZT_PROTO_VERB_MULTICAST_GATHER_IDX_ADI));
- const unsigned int gatherLimit = at<uint32_t>(ZT_PROTO_VERB_MULTICAST_GATHER_IDX_GATHER_LIMIT);
- //TRACE("<<MC %s(%s) GATHER up to %u in %.16llx/%s",source().toString().c_str(),_remoteAddress.toString().c_str(),gatherLimit,nwid,mg.toString().c_str());
+ peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_MULTICAST_LIKE,0,Packet::VERB_NOP,trustEstablished,(network) ? network->id() : 0);
- if (gatherLimit) {
- Packet outp(peer->address(),RR->identity.address(),Packet::VERB_OK);
- outp.append((unsigned char)Packet::VERB_MULTICAST_GATHER);
- outp.append(packetId());
- outp.append(nwid);
- mg.mac().appendTo(outp);
- outp.append((uint32_t)mg.adi());
- const unsigned int gatheredLocally = RR->mc->gather(peer->address(),nwid,mg,outp,gatherLimit);
- if (gatheredLocally) {
- outp.armor(peer->key(),true);
- RR->node->putPacket(_localAddress,_remoteAddress,outp.data(),outp.size());
- }
-
-#ifdef ZT_ENABLE_CLUSTER
- if ((RR->cluster)&&(gatheredLocally < gatherLimit))
- RR->cluster->sendDistributedQuery(*this);
-#endif
- }
-
- peer->received(_localAddress,_remoteAddress,hops(),packetId(),Packet::VERB_MULTICAST_GATHER,0,Packet::VERB_NOP);
- } catch ( ... ) {
- TRACE("dropped MULTICAST_GATHER from %s(%s): unexpected exception",source().toString().c_str(),_remoteAddress.toString().c_str());
- }
return true;
}
-bool IncomingPacket::_doMULTICAST_FRAME(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer)
+bool IncomingPacket::_doNETWORK_CREDENTIALS(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer)
{
- try {
- const uint64_t nwid = at<uint64_t>(ZT_PROTO_VERB_MULTICAST_FRAME_IDX_NETWORK_ID);
- const unsigned int flags = (*this)[ZT_PROTO_VERB_MULTICAST_FRAME_IDX_FLAGS];
-
- const SharedPtr<Network> network(RR->node->network(nwid));
- if (network) {
- // Offset -- size of optional fields added to position of later fields
- unsigned int offset = 0;
-
- if ((flags & 0x01) != 0) {
- CertificateOfMembership com;
- offset += com.deserialize(*this,ZT_PROTO_VERB_MULTICAST_FRAME_IDX_COM);
- peer->validateAndSetNetworkMembershipCertificate(nwid,com);
- }
-
- // Check membership after we've read any included COM, since
- // that cert might be what we needed.
- if (!network->isAllowed(peer)) {
- TRACE("dropped MULTICAST_FRAME from %s(%s): not a member of private network %.16llx",peer->address().toString().c_str(),_remoteAddress.toString().c_str(),(unsigned long long)network->id());
- _sendErrorNeedCertificate(RR,peer,network->id());
- return true;
- }
-
- unsigned int gatherLimit = 0;
- if ((flags & 0x02) != 0) {
- gatherLimit = at<uint32_t>(offset + ZT_PROTO_VERB_MULTICAST_FRAME_IDX_GATHER_LIMIT);
- offset += 4;
- }
-
- MAC from;
- if ((flags & 0x04) != 0) {
- from.setTo(field(offset + ZT_PROTO_VERB_MULTICAST_FRAME_IDX_SOURCE_MAC,6),6);
- offset += 6;
- } else {
- from.fromAddress(peer->address(),nwid);
- }
-
- const MulticastGroup to(MAC(field(offset + ZT_PROTO_VERB_MULTICAST_FRAME_IDX_DEST_MAC,6),6),at<uint32_t>(offset + ZT_PROTO_VERB_MULTICAST_FRAME_IDX_DEST_ADI));
- const unsigned int etherType = at<uint16_t>(offset + ZT_PROTO_VERB_MULTICAST_FRAME_IDX_ETHERTYPE);
- const unsigned int payloadLen = size() - (offset + ZT_PROTO_VERB_MULTICAST_FRAME_IDX_FRAME);
-
- //TRACE("<<MC FRAME %.16llx/%s from %s@%s flags %.2x length %u",nwid,to.toString().c_str(),from.toString().c_str(),peer->address().toString().c_str(),flags,payloadLen);
+ if (!peer->rateGateCredentialsReceived(RR->node->now()))
+ return true;
- if ((payloadLen > 0)&&(payloadLen <= ZT_IF_MTU)) {
- if (!to.mac().isMulticast()) {
- TRACE("dropped MULTICAST_FRAME from %s@%s(%s) to %s: destination is unicast, must use FRAME or EXT_FRAME",from.toString().c_str(),peer->address().toString().c_str(),_remoteAddress.toString().c_str(),to.toString().c_str());
- return true;
+ CertificateOfMembership com;
+ Capability cap;
+ Tag tag;
+ Revocation revocation;
+ CertificateOfOwnership coo;
+ bool trustEstablished = false;
+ SharedPtr<Network> network;
+
+ unsigned int p = ZT_PACKET_IDX_PAYLOAD;
+ while ((p < size())&&((*this)[p] != 0)) {
+ p += com.deserialize(*this,p);
+ if (com) {
+ network = RR->node->network(com.networkId());
+ if (network) {
+ switch (network->addCredential(tPtr,com)) {
+ case Membership::ADD_REJECTED:
+ break;
+ case Membership::ADD_ACCEPTED_NEW:
+ case Membership::ADD_ACCEPTED_REDUNDANT:
+ trustEstablished = true;
+ break;
+ case Membership::ADD_DEFERRED_FOR_WHOIS:
+ return false;
}
- if ((!from)||(from.isMulticast())||(from == network->mac())) {
- TRACE("dropped MULTICAST_FRAME from %s@%s(%s) to %s: invalid source MAC",from.toString().c_str(),peer->address().toString().c_str(),_remoteAddress.toString().c_str(),to.toString().c_str());
- return true;
+ } else RR->mc->addCredential(tPtr,com,false);
+ }
+ }
+ ++p; // skip trailing 0 after COMs if present
+
+ if (p < size()) { // older ZeroTier versions do not send capabilities, tags, or revocations
+ const unsigned int numCapabilities = at<uint16_t>(p); p += 2;
+ for(unsigned int i=0;i<numCapabilities;++i) {
+ p += cap.deserialize(*this,p);
+ if ((!network)||(network->id() != cap.networkId()))
+ network = RR->node->network(cap.networkId());
+ if (network) {
+ switch (network->addCredential(tPtr,cap)) {
+ case Membership::ADD_REJECTED:
+ break;
+ case Membership::ADD_ACCEPTED_NEW:
+ case Membership::ADD_ACCEPTED_REDUNDANT:
+ trustEstablished = true;
+ break;
+ case Membership::ADD_DEFERRED_FOR_WHOIS:
+ return false;
}
+ }
+ }
- if (from != MAC(peer->address(),network->id())) {
- if (network->config().permitsBridging(peer->address())) {
- network->learnBridgeRoute(from,peer->address());
- } else {
- TRACE("dropped MULTICAST_FRAME from %s@%s(%s) to %s: sender not allowed to bridge into %.16llx",from.toString().c_str(),peer->address().toString().c_str(),_remoteAddress.toString().c_str(),to.toString().c_str(),network->id());
- return true;
- }
+ if (p >= size()) return true;
+
+ const unsigned int numTags = at<uint16_t>(p); p += 2;
+ for(unsigned int i=0;i<numTags;++i) {
+ p += tag.deserialize(*this,p);
+ if ((!network)||(network->id() != tag.networkId()))
+ network = RR->node->network(tag.networkId());
+ if (network) {
+ switch (network->addCredential(tPtr,tag)) {
+ case Membership::ADD_REJECTED:
+ break;
+ case Membership::ADD_ACCEPTED_NEW:
+ case Membership::ADD_ACCEPTED_REDUNDANT:
+ trustEstablished = true;
+ break;
+ case Membership::ADD_DEFERRED_FOR_WHOIS:
+ return false;
}
-
- RR->node->putFrame(network->id(),network->userPtr(),from,to.mac(),etherType,0,field(offset + ZT_PROTO_VERB_MULTICAST_FRAME_IDX_FRAME,payloadLen),payloadLen);
}
+ }
- if (gatherLimit) {
- Packet outp(source(),RR->identity.address(),Packet::VERB_OK);
- outp.append((unsigned char)Packet::VERB_MULTICAST_FRAME);
- outp.append(packetId());
- outp.append(nwid);
- to.mac().appendTo(outp);
- outp.append((uint32_t)to.adi());
- outp.append((unsigned char)0x02); // flag 0x02 = contains gather results
- if (RR->mc->gather(peer->address(),nwid,to,outp,gatherLimit)) {
- outp.armor(peer->key(),true);
- RR->node->putPacket(_localAddress,_remoteAddress,outp.data(),outp.size());
+ if (p >= size()) return true;
+
+ const unsigned int numRevocations = at<uint16_t>(p); p += 2;
+ for(unsigned int i=0;i<numRevocations;++i) {
+ p += revocation.deserialize(*this,p);
+ if ((!network)||(network->id() != revocation.networkId()))
+ network = RR->node->network(revocation.networkId());
+ if (network) {
+ switch(network->addCredential(tPtr,peer->address(),revocation)) {
+ case Membership::ADD_REJECTED:
+ break;
+ case Membership::ADD_ACCEPTED_NEW:
+ case Membership::ADD_ACCEPTED_REDUNDANT:
+ trustEstablished = true;
+ break;
+ case Membership::ADD_DEFERRED_FOR_WHOIS:
+ return false;
}
}
- } // else ignore -- not a member of this network
+ }
- peer->received(_localAddress,_remoteAddress,hops(),packetId(),Packet::VERB_MULTICAST_FRAME,0,Packet::VERB_NOP);
- } catch ( ... ) {
- TRACE("dropped MULTICAST_FRAME from %s(%s): unexpected exception",source().toString().c_str(),_remoteAddress.toString().c_str());
+ if (p >= size()) return true;
+
+ const unsigned int numCoos = at<uint16_t>(p); p += 2;
+ for(unsigned int i=0;i<numCoos;++i) {
+ p += coo.deserialize(*this,p);
+ if ((!network)||(network->id() != coo.networkId()))
+ network = RR->node->network(coo.networkId());
+ if (network) {
+ switch(network->addCredential(tPtr,coo)) {
+ case Membership::ADD_REJECTED:
+ break;
+ case Membership::ADD_ACCEPTED_NEW:
+ case Membership::ADD_ACCEPTED_REDUNDANT:
+ trustEstablished = true;
+ break;
+ case Membership::ADD_DEFERRED_FOR_WHOIS:
+ return false;
+ }
+ }
+ }
}
+
+ peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_NETWORK_CREDENTIALS,0,Packet::VERB_NOP,trustEstablished,(network) ? network->id() : 0);
+
return true;
}
-bool IncomingPacket::_doPUSH_DIRECT_PATHS(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer)
+bool IncomingPacket::_doNETWORK_CONFIG_REQUEST(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer)
{
- try {
- const uint64_t now = RR->node->now();
-
- // First, subject this to a rate limit
- if (!peer->shouldRespondToDirectPathPush(now)) {
- TRACE("dropped PUSH_DIRECT_PATHS from %s(%s): circuit breaker tripped",source().toString().c_str(),_remoteAddress.toString().c_str());
- return true;
- }
-
- // Second, limit addresses by scope and type
- uint8_t countPerScope[ZT_INETADDRESS_MAX_SCOPE+1][2]; // [][0] is v4, [][1] is v6
- memset(countPerScope,0,sizeof(countPerScope));
-
- unsigned int count = at<uint16_t>(ZT_PACKET_IDX_PAYLOAD);
- unsigned int ptr = ZT_PACKET_IDX_PAYLOAD + 2;
-
- while (count--) { // if ptr overflows Buffer will throw
- // TODO: some flags are not yet implemented
+ const uint64_t nwid = at<uint64_t>(ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST_IDX_NETWORK_ID);
+ const unsigned int hopCount = hops();
+ const uint64_t requestPacketId = packetId();
+
+ if (RR->localNetworkController) {
+ const unsigned int metaDataLength = (ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST_IDX_DICT_LEN <= size()) ? at<uint16_t>(ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST_IDX_DICT_LEN) : 0;
+ const char *metaDataBytes = (metaDataLength != 0) ? (const char *)field(ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST_IDX_DICT,metaDataLength) : (const char *)0;
+ const Dictionary<ZT_NETWORKCONFIG_METADATA_DICT_CAPACITY> metaData(metaDataBytes,metaDataLength);
+ RR->localNetworkController->request(nwid,(hopCount > 0) ? InetAddress() : _path->address(),requestPacketId,peer->identity(),metaData);
+ } else {
+ Packet outp(peer->address(),RR->identity.address(),Packet::VERB_ERROR);
+ outp.append((unsigned char)Packet::VERB_NETWORK_CONFIG_REQUEST);
+ outp.append(requestPacketId);
+ outp.append((unsigned char)Packet::ERROR_UNSUPPORTED_OPERATION);
+ outp.append(nwid);
+ outp.armor(peer->key(),true);
+ _path->send(RR,tPtr,outp.data(),outp.size(),RR->node->now());
+ }
- unsigned int flags = (*this)[ptr++];
- unsigned int extLen = at<uint16_t>(ptr); ptr += 2;
- ptr += extLen; // unused right now
- unsigned int addrType = (*this)[ptr++];
- unsigned int addrLen = (*this)[ptr++];
+ peer->received(tPtr,_path,hopCount,requestPacketId,Packet::VERB_NETWORK_CONFIG_REQUEST,0,Packet::VERB_NOP,false,nwid);
- switch(addrType) {
- case 4: {
- InetAddress a(field(ptr,4),4,at<uint16_t>(ptr + 4));
+ return true;
+}
- bool redundant = false;
- if ((flags & ZT_PUSH_DIRECT_PATHS_FLAG_CLUSTER_REDIRECT) != 0) {
- peer->setClusterOptimalPathForAddressFamily(a);
- } else {
- redundant = peer->hasActivePathTo(now,a);
- }
+bool IncomingPacket::_doNETWORK_CONFIG(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer)
+{
+ const SharedPtr<Network> network(RR->node->network(at<uint64_t>(ZT_PACKET_IDX_PAYLOAD)));
+ if (network) {
+ const uint64_t configUpdateId = network->handleConfigChunk(tPtr,packetId(),source(),*this,ZT_PACKET_IDX_PAYLOAD);
+ if (configUpdateId) {
+ Packet outp(peer->address(),RR->identity.address(),Packet::VERB_OK);
+ outp.append((uint8_t)Packet::VERB_ECHO);
+ outp.append((uint64_t)packetId());
+ outp.append((uint64_t)network->id());
+ outp.append((uint64_t)configUpdateId);
+ outp.armor(peer->key(),true);
+ _path->send(RR,tPtr,outp.data(),outp.size(),RR->node->now());
+ }
+ }
- if ( ((flags & ZT_PUSH_DIRECT_PATHS_FLAG_FORGET_PATH) == 0) && (!redundant) && (RR->node->shouldUsePathForZeroTierTraffic(_localAddress,a)) ) {
- if (++countPerScope[(int)a.ipScope()][0] <= ZT_PUSH_DIRECT_PATHS_MAX_PER_SCOPE_AND_FAMILY) {
- TRACE("attempting to contact %s at pushed direct path %s",peer->address().toString().c_str(),a.toString().c_str());
- peer->sendHELLO(InetAddress(),a,now);
- } else {
- TRACE("ignoring contact for %s at %s -- too many per scope",peer->address().toString().c_str(),a.toString().c_str());
- }
- }
- } break;
- case 6: {
- InetAddress a(field(ptr,16),16,at<uint16_t>(ptr + 16));
+ peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_NETWORK_CONFIG,0,Packet::VERB_NOP,false,(network) ? network->id() : 0);
- bool redundant = false;
- if ((flags & ZT_PUSH_DIRECT_PATHS_FLAG_CLUSTER_REDIRECT) != 0) {
- peer->setClusterOptimalPathForAddressFamily(a);
- } else {
- redundant = peer->hasActivePathTo(now,a);
- }
+ return true;
+}
- if ( ((flags & ZT_PUSH_DIRECT_PATHS_FLAG_FORGET_PATH) == 0) && (!redundant) && (RR->node->shouldUsePathForZeroTierTraffic(_localAddress,a)) ) {
- if (++countPerScope[(int)a.ipScope()][1] <= ZT_PUSH_DIRECT_PATHS_MAX_PER_SCOPE_AND_FAMILY) {
- TRACE("attempting to contact %s at pushed direct path %s",peer->address().toString().c_str(),a.toString().c_str());
- peer->sendHELLO(InetAddress(),a,now);
- } else {
- TRACE("ignoring contact for %s at %s -- too many per scope",peer->address().toString().c_str(),a.toString().c_str());
- }
- }
- } break;
+bool IncomingPacket::_doMULTICAST_GATHER(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer)
+{
+ const uint64_t nwid = at<uint64_t>(ZT_PROTO_VERB_MULTICAST_GATHER_IDX_NETWORK_ID);
+ const unsigned int flags = (*this)[ZT_PROTO_VERB_MULTICAST_GATHER_IDX_FLAGS];
+ const MulticastGroup mg(MAC(field(ZT_PROTO_VERB_MULTICAST_GATHER_IDX_MAC,6),6),at<uint32_t>(ZT_PROTO_VERB_MULTICAST_GATHER_IDX_ADI));
+ const unsigned int gatherLimit = at<uint32_t>(ZT_PROTO_VERB_MULTICAST_GATHER_IDX_GATHER_LIMIT);
+
+ const SharedPtr<Network> network(RR->node->network(nwid));
+
+ if ((flags & 0x01) != 0) {
+ try {
+ CertificateOfMembership com;
+ com.deserialize(*this,ZT_PROTO_VERB_MULTICAST_GATHER_IDX_COM);
+ if (com) {
+ if (network)
+ network->addCredential(tPtr,com);
+ else RR->mc->addCredential(tPtr,com,false);
}
- ptr += addrLen;
- }
+ } catch ( ... ) {} // discard invalid COMs
+ }
- peer->received(_localAddress,_remoteAddress,hops(),packetId(),Packet::VERB_PUSH_DIRECT_PATHS,0,Packet::VERB_NOP);
- } catch ( ... ) {
- TRACE("dropped PUSH_DIRECT_PATHS from %s(%s): unexpected exception",source().toString().c_str(),_remoteAddress.toString().c_str());
+ const bool trustEstablished = ((network)&&(network->gate(tPtr,peer)));
+ if (!trustEstablished)
+ _sendErrorNeedCredentials(RR,tPtr,peer,nwid);
+ if ( ( trustEstablished || RR->mc->cacheAuthorized(peer->address(),nwid,RR->node->now()) ) && (gatherLimit > 0) ) {
+ Packet outp(peer->address(),RR->identity.address(),Packet::VERB_OK);
+ outp.append((unsigned char)Packet::VERB_MULTICAST_GATHER);
+ outp.append(packetId());
+ outp.append(nwid);
+ mg.mac().appendTo(outp);
+ outp.append((uint32_t)mg.adi());
+ const unsigned int gatheredLocally = RR->mc->gather(peer->address(),nwid,mg,outp,gatherLimit);
+ if (gatheredLocally > 0) {
+ outp.armor(peer->key(),true);
+ _path->send(RR,tPtr,outp.data(),outp.size(),RR->node->now());
+ }
}
+
+ peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_MULTICAST_GATHER,0,Packet::VERB_NOP,trustEstablished,nwid);
+
return true;
}
-bool IncomingPacket::_doCIRCUIT_TEST(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer)
+bool IncomingPacket::_doMULTICAST_FRAME(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer)
{
- try {
- const Address originatorAddress(field(ZT_PACKET_IDX_PAYLOAD,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH);
- SharedPtr<Peer> originator(RR->topology->getPeer(originatorAddress));
- if (!originator) {
- RR->sw->requestWhois(originatorAddress);
- return false;
+ const uint64_t nwid = at<uint64_t>(ZT_PROTO_VERB_MULTICAST_FRAME_IDX_NETWORK_ID);
+ const unsigned int flags = (*this)[ZT_PROTO_VERB_MULTICAST_FRAME_IDX_FLAGS];
+
+ const SharedPtr<Network> network(RR->node->network(nwid));
+ if (network) {
+ // Offset -- size of optional fields added to position of later fields
+ unsigned int offset = 0;
+
+ if ((flags & 0x01) != 0) {
+ // This is deprecated but may still be sent by old peers
+ CertificateOfMembership com;
+ offset += com.deserialize(*this,ZT_PROTO_VERB_MULTICAST_FRAME_IDX_COM);
+ if (com)
+ network->addCredential(tPtr,com);
}
- const unsigned int flags = at<uint16_t>(ZT_PACKET_IDX_PAYLOAD + 5);
- const uint64_t timestamp = at<uint64_t>(ZT_PACKET_IDX_PAYLOAD + 7);
- const uint64_t testId = at<uint64_t>(ZT_PACKET_IDX_PAYLOAD + 15);
-
- // Tracks total length of variable length fields, initialized to originator credential length below
- unsigned int vlf;
-
- // Originator credentials
- const unsigned int originatorCredentialLength = vlf = at<uint16_t>(ZT_PACKET_IDX_PAYLOAD + 23);
- uint64_t originatorCredentialNetworkId = 0;
- if (originatorCredentialLength >= 1) {
- switch((*this)[ZT_PACKET_IDX_PAYLOAD + 25]) {
- case 0x01: { // 64-bit network ID, originator must be controller
- if (originatorCredentialLength >= 9)
- originatorCredentialNetworkId = at<uint64_t>(ZT_PACKET_IDX_PAYLOAD + 26);
- } break;
- default: break;
- }
+ if (!network->gate(tPtr,peer)) {
+ RR->t->incomingNetworkAccessDenied(tPtr,network,_path,packetId(),size(),peer->address(),Packet::VERB_MULTICAST_FRAME,true);
+ _sendErrorNeedCredentials(RR,tPtr,peer,nwid);
+ peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_MULTICAST_FRAME,0,Packet::VERB_NOP,false,nwid);
+ return true;
}
- // Add length of "additional fields," which are currently unused
- vlf += at<uint16_t>(ZT_PACKET_IDX_PAYLOAD + 25 + vlf);
+ unsigned int gatherLimit = 0;
+ if ((flags & 0x02) != 0) {
+ gatherLimit = at<uint32_t>(offset + ZT_PROTO_VERB_MULTICAST_FRAME_IDX_GATHER_LIMIT);
+ offset += 4;
+ }
- // Verify signature -- only tests signed by their originators are allowed
- const unsigned int signatureLength = at<uint16_t>(ZT_PACKET_IDX_PAYLOAD + 27 + vlf);
- if (!originator->identity().verify(field(ZT_PACKET_IDX_PAYLOAD,27 + vlf),27 + vlf,field(ZT_PACKET_IDX_PAYLOAD + 29 + vlf,signatureLength),signatureLength)) {
- TRACE("dropped CIRCUIT_TEST from %s(%s): signature by originator %s invalid",source().toString().c_str(),_remoteAddress.toString().c_str(),originatorAddress.toString().c_str());
+ MAC from;
+ if ((flags & 0x04) != 0) {
+ from.setTo(field(offset + ZT_PROTO_VERB_MULTICAST_FRAME_IDX_SOURCE_MAC,6),6);
+ offset += 6;
+ } else {
+ from.fromAddress(peer->address(),nwid);
+ }
+
+ const MulticastGroup to(MAC(field(offset + ZT_PROTO_VERB_MULTICAST_FRAME_IDX_DEST_MAC,6),6),at<uint32_t>(offset + ZT_PROTO_VERB_MULTICAST_FRAME_IDX_DEST_ADI));
+ const unsigned int etherType = at<uint16_t>(offset + ZT_PROTO_VERB_MULTICAST_FRAME_IDX_ETHERTYPE);
+ const unsigned int frameLen = size() - (offset + ZT_PROTO_VERB_MULTICAST_FRAME_IDX_FRAME);
+
+ if (network->config().multicastLimit == 0) {
+ RR->t->incomingNetworkFrameDropped(tPtr,network,_path,packetId(),size(),peer->address(),Packet::VERB_MULTICAST_FRAME,from,to.mac(),"multicast disabled");
+ peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_MULTICAST_FRAME,0,Packet::VERB_NOP,false,nwid);
return true;
}
- vlf += signatureLength;
-
- // Save this length so we can copy the immutable parts of this test
- // into the one we send along to next hops.
- const unsigned int lengthOfSignedPortionAndSignature = 29 + vlf;
-
- // Get previous hop's credential, if any
- const unsigned int previousHopCredentialLength = at<uint16_t>(ZT_PACKET_IDX_PAYLOAD + 29 + vlf);
- CertificateOfMembership previousHopCom;
- if (previousHopCredentialLength >= 1) {
- switch((*this)[ZT_PACKET_IDX_PAYLOAD + 31 + vlf]) {
- case 0x01: { // network certificate of membership for previous hop
- const unsigned int phcl = previousHopCom.deserialize(*this,ZT_PACKET_IDX_PAYLOAD + 32 + vlf);
- if (phcl != (previousHopCredentialLength - 1)) {
- TRACE("dropped CIRCUIT_TEST from %s(%s): previous hop COM invalid (%u != %u)",source().toString().c_str(),_remoteAddress.toString().c_str(),phcl,(previousHopCredentialLength - 1));
- return true;
- }
- } break;
- default: break;
+
+ if ((frameLen > 0)&&(frameLen <= ZT_MAX_MTU)) {
+ if (!to.mac().isMulticast()) {
+ RR->t->incomingPacketInvalid(tPtr,_path,packetId(),source(),hops(),Packet::VERB_MULTICAST_FRAME,"destination not multicast");
+ peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_MULTICAST_FRAME,0,Packet::VERB_NOP,true,nwid); // trustEstablished because COM is okay
+ return true;
}
- }
- vlf += previousHopCredentialLength;
-
- // Check credentials (signature already verified)
- NetworkConfig originatorCredentialNetworkConfig;
- if (originatorCredentialNetworkId) {
- if (Network::controllerFor(originatorCredentialNetworkId) == originatorAddress) {
- SharedPtr<Network> nw(RR->node->network(originatorCredentialNetworkId));
- if ((nw)&&(nw->hasConfig())) {
- originatorCredentialNetworkConfig = nw->config();
- if ( ( (originatorCredentialNetworkConfig.isPublic()) || (peer->address() == originatorAddress) || ((originatorCredentialNetworkConfig.com)&&(previousHopCom)&&(originatorCredentialNetworkConfig.com.agreesWith(previousHopCom))) ) ) {
- TRACE("CIRCUIT_TEST %.16llx received from hop %s(%s) and originator %s with valid network ID credential %.16llx (verified from originator and next hop)",testId,source().toString().c_str(),_remoteAddress.toString().c_str(),originatorAddress.toString().c_str(),originatorCredentialNetworkId);
- } else {
- TRACE("dropped CIRCUIT_TEST from %s(%s): originator %s specified network ID %.16llx as credential, and previous hop %s did not supply a valid COM",source().toString().c_str(),_remoteAddress.toString().c_str(),originatorAddress.toString().c_str(),originatorCredentialNetworkId,peer->address().toString().c_str());
- return true;
- }
- } else {
- TRACE("dropped CIRCUIT_TEST from %s(%s): originator %s specified network ID %.16llx as credential, and we are not a member",source().toString().c_str(),_remoteAddress.toString().c_str(),originatorAddress.toString().c_str(),originatorCredentialNetworkId);
- return true;
- }
- } else {
- TRACE("dropped CIRCUIT_TEST from %s(%s): originator %s specified network ID as credential, is not controller for %.16llx",source().toString().c_str(),_remoteAddress.toString().c_str(),originatorAddress.toString().c_str(),originatorCredentialNetworkId);
+ if ((!from)||(from.isMulticast())||(from == network->mac())) {
+ RR->t->incomingPacketInvalid(tPtr,_path,packetId(),source(),hops(),Packet::VERB_MULTICAST_FRAME,"invalid source MAC");
+ peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_MULTICAST_FRAME,0,Packet::VERB_NOP,true,nwid); // trustEstablished because COM is okay
return true;
}
- } else {
- TRACE("dropped CIRCUIT_TEST from %s(%s): originator %s did not specify a credential or credential type",source().toString().c_str(),_remoteAddress.toString().c_str(),originatorAddress.toString().c_str());
- return true;
- }
- const uint64_t now = RR->node->now();
-
- unsigned int breadth = 0;
- Address nextHop[256]; // breadth is a uin8_t, so this is the max
- InetAddress nextHopBestPathAddress[256];
- unsigned int remainingHopsPtr = ZT_PACKET_IDX_PAYLOAD + 33 + vlf;
- if ((ZT_PACKET_IDX_PAYLOAD + 31 + vlf) < size()) {
- // unsigned int nextHopFlags = (*this)[ZT_PACKET_IDX_PAYLOAD + 31 + vlf]
- breadth = (*this)[ZT_PACKET_IDX_PAYLOAD + 32 + vlf];
- for(unsigned int h=0;h<breadth;++h) {
- nextHop[h].setTo(field(remainingHopsPtr,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH);
- remainingHopsPtr += ZT_ADDRESS_LENGTH;
- SharedPtr<Peer> nhp(RR->topology->getPeer(nextHop[h]));
- if (nhp) {
- Path *const rp = nhp->getBestPath(now);
- if (rp)
- nextHopBestPathAddress[h] = rp->address();
+ const uint8_t *const frameData = (const uint8_t *)field(offset + ZT_PROTO_VERB_MULTICAST_FRAME_IDX_FRAME,frameLen);
+
+ if ((flags & 0x08)&&(network->config().isMulticastReplicator(RR->identity.address())))
+ RR->mc->send(tPtr,RR->node->now(),network,peer->address(),to,from,etherType,frameData,frameLen);
+
+ if (from != MAC(peer->address(),nwid)) {
+ if (network->config().permitsBridging(peer->address())) {
+ network->learnBridgeRoute(from,peer->address());
+ } else {
+ RR->t->incomingNetworkFrameDropped(tPtr,network,_path,packetId(),size(),peer->address(),Packet::VERB_MULTICAST_FRAME,from,to.mac(),"bridging not allowed (remote)");
+ peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_MULTICAST_FRAME,0,Packet::VERB_NOP,true,nwid); // trustEstablished because COM is okay
+ return true;
}
}
- }
- // Report back to originator, depending on flags and whether we are last hop
- if ( ((flags & 0x01) != 0) || ((breadth == 0)&&((flags & 0x02) != 0)) ) {
- Packet outp(originatorAddress,RR->identity.address(),Packet::VERB_CIRCUIT_TEST_REPORT);
- outp.append((uint64_t)timestamp);
- outp.append((uint64_t)testId);
- outp.append((uint64_t)0); // field reserved for future use
- outp.append((uint8_t)ZT_VENDOR_ZEROTIER);
- outp.append((uint8_t)ZT_PROTO_VERSION);
- outp.append((uint8_t)ZEROTIER_ONE_VERSION_MAJOR);
- outp.append((uint8_t)ZEROTIER_ONE_VERSION_MINOR);
- outp.append((uint16_t)ZEROTIER_ONE_VERSION_REVISION);
- outp.append((uint16_t)ZT_PLATFORM_UNSPECIFIED);
- outp.append((uint16_t)ZT_ARCHITECTURE_UNSPECIFIED);
- outp.append((uint16_t)0); // error code, currently unused
- outp.append((uint64_t)0); // flags, currently unused
- outp.append((uint64_t)packetId());
- peer->address().appendTo(outp);
- outp.append((uint8_t)hops());
- _localAddress.serialize(outp);
- _remoteAddress.serialize(outp);
- outp.append((uint16_t)0); // no additional fields
- outp.append((uint8_t)breadth);
- for(unsigned int h=0;h<breadth;++h) {
- nextHop[h].appendTo(outp);
- nextHopBestPathAddress[h].serialize(outp); // appends 0 if null InetAddress
- }
- RR->sw->send(outp,true,0);
+ if (network->filterIncomingPacket(tPtr,peer,RR->identity.address(),from,to.mac(),frameData,frameLen,etherType,0) > 0)
+ RR->node->putFrame(tPtr,nwid,network->userPtr(),from,to.mac(),etherType,0,(const void *)frameData,frameLen);
}
- // If there are next hops, forward the test along through the graph
- if (breadth > 0) {
- Packet outp(Address(),RR->identity.address(),Packet::VERB_CIRCUIT_TEST);
- outp.append(field(ZT_PACKET_IDX_PAYLOAD,lengthOfSignedPortionAndSignature),lengthOfSignedPortionAndSignature);
- const unsigned int previousHopCredentialPos = outp.size();
- outp.append((uint16_t)0); // no previous hop credentials: default
- if ((originatorCredentialNetworkConfig)&&(!originatorCredentialNetworkConfig.isPublic())&&(originatorCredentialNetworkConfig.com)) {
- outp.append((uint8_t)0x01); // COM
- originatorCredentialNetworkConfig.com.serialize(outp);
- outp.setAt<uint16_t>(previousHopCredentialPos,(uint16_t)(outp.size() - (previousHopCredentialPos + 2)));
- }
- if (remainingHopsPtr < size())
- outp.append(field(remainingHopsPtr,size() - remainingHopsPtr),size() - remainingHopsPtr);
-
- for(unsigned int h=0;h<breadth;++h) {
- if (RR->identity.address() != nextHop[h]) { // next hops that loop back to the current hop are not valid
- outp.newInitializationVector();
- outp.setDestination(nextHop[h]);
- RR->sw->send(outp,true,originatorCredentialNetworkId);
- }
+ if (gatherLimit) {
+ Packet outp(source(),RR->identity.address(),Packet::VERB_OK);
+ outp.append((unsigned char)Packet::VERB_MULTICAST_FRAME);
+ outp.append(packetId());
+ outp.append(nwid);
+ to.mac().appendTo(outp);
+ outp.append((uint32_t)to.adi());
+ outp.append((unsigned char)0x02); // flag 0x02 = contains gather results
+ if (RR->mc->gather(peer->address(),nwid,to,outp,gatherLimit)) {
+ outp.armor(peer->key(),true);
+ _path->send(RR,tPtr,outp.data(),outp.size(),RR->node->now());
}
}
- peer->received(_localAddress,_remoteAddress,hops(),packetId(),Packet::VERB_CIRCUIT_TEST,0,Packet::VERB_NOP);
- } catch ( ... ) {
- TRACE("dropped CIRCUIT_TEST from %s(%s): unexpected exception",source().toString().c_str(),_remoteAddress.toString().c_str());
+ peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_MULTICAST_FRAME,0,Packet::VERB_NOP,true,nwid);
+ } else {
+ _sendErrorNeedCredentials(RR,tPtr,peer,nwid);
+ peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_MULTICAST_FRAME,0,Packet::VERB_NOP,false,nwid);
}
+
return true;
}
-bool IncomingPacket::_doCIRCUIT_TEST_REPORT(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer)
+bool IncomingPacket::_doPUSH_DIRECT_PATHS(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer)
{
- try {
- ZT_CircuitTestReport report;
- memset(&report,0,sizeof(report));
-
- report.current = peer->address().toInt();
- report.upstream = Address(field(ZT_PACKET_IDX_PAYLOAD + 52,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH).toInt();
- report.testId = at<uint64_t>(ZT_PACKET_IDX_PAYLOAD + 8);
- report.timestamp = at<uint64_t>(ZT_PACKET_IDX_PAYLOAD);
- report.remoteTimestamp = at<uint64_t>(ZT_PACKET_IDX_PAYLOAD + 16);
- report.sourcePacketId = at<uint64_t>(ZT_PACKET_IDX_PAYLOAD + 44);
- report.flags = at<uint64_t>(ZT_PACKET_IDX_PAYLOAD + 36);
- report.sourcePacketHopCount = (*this)[ZT_PACKET_IDX_PAYLOAD + 57]; // end of fixed length headers: 58
- report.errorCode = at<uint16_t>(ZT_PACKET_IDX_PAYLOAD + 34);
- report.vendor = (enum ZT_Vendor)((*this)[ZT_PACKET_IDX_PAYLOAD + 24]);
- report.protocolVersion = (*this)[ZT_PACKET_IDX_PAYLOAD + 25];
- report.majorVersion = (*this)[ZT_PACKET_IDX_PAYLOAD + 26];
- report.minorVersion = (*this)[ZT_PACKET_IDX_PAYLOAD + 27];
- report.revision = at<uint16_t>(ZT_PACKET_IDX_PAYLOAD + 28);
- report.platform = (enum ZT_Platform)at<uint16_t>(ZT_PACKET_IDX_PAYLOAD + 30);
- report.architecture = (enum ZT_Architecture)at<uint16_t>(ZT_PACKET_IDX_PAYLOAD + 32);
-
- const unsigned int receivedOnLocalAddressLen = reinterpret_cast<InetAddress *>(&(report.receivedOnLocalAddress))->deserialize(*this,ZT_PACKET_IDX_PAYLOAD + 58);
- const unsigned int receivedFromRemoteAddressLen = reinterpret_cast<InetAddress *>(&(report.receivedFromRemoteAddress))->deserialize(*this,ZT_PACKET_IDX_PAYLOAD + 58 + receivedOnLocalAddressLen);
-
- unsigned int nhptr = ZT_PACKET_IDX_PAYLOAD + 58 + receivedOnLocalAddressLen + receivedFromRemoteAddressLen;
- nhptr += at<uint16_t>(nhptr) + 2; // add "additional field" length, which right now will be zero
-
- report.nextHopCount = (*this)[nhptr++];
- if (report.nextHopCount > ZT_CIRCUIT_TEST_MAX_HOP_BREADTH) // sanity check, shouldn't be possible
- report.nextHopCount = ZT_CIRCUIT_TEST_MAX_HOP_BREADTH;
- for(unsigned int h=0;h<report.nextHopCount;++h) {
- report.nextHops[h].address = Address(field(nhptr,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH).toInt(); nhptr += ZT_ADDRESS_LENGTH;
- nhptr += reinterpret_cast<InetAddress *>(&(report.nextHops[h].physicalAddress))->deserialize(*this,nhptr);
- }
+ const int64_t now = RR->node->now();
- RR->node->postCircuitTestReport(&report);
- } catch ( ... ) {
- TRACE("dropped CIRCUIT_TEST_REPORT from %s(%s): unexpected exception",source().toString().c_str(),_remoteAddress.toString().c_str());
+ // First, subject this to a rate limit
+ if (!peer->rateGatePushDirectPaths(now)) {
+ peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_PUSH_DIRECT_PATHS,0,Packet::VERB_NOP,false,0);
+ return true;
}
- return true;
-}
-bool IncomingPacket::_doREQUEST_PROOF_OF_WORK(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer)
-{
- try {
- // If this were allowed from anyone, it would itself be a DOS vector. Right
- // now we only allow it from roots and controllers of networks you have joined.
- bool allowed = RR->topology->isRoot(peer->identity());
- if (!allowed) {
- std::vector< SharedPtr<Network> > allNetworks(RR->node->allNetworks());
- for(std::vector< SharedPtr<Network> >::const_iterator n(allNetworks.begin());n!=allNetworks.end();++n) {
- if (peer->address() == (*n)->controller()) {
- allowed = true;
- break;
+ // Second, limit addresses by scope and type
+ uint8_t countPerScope[ZT_INETADDRESS_MAX_SCOPE+1][2]; // [][0] is v4, [][1] is v6
+ memset(countPerScope,0,sizeof(countPerScope));
+
+ unsigned int count = at<uint16_t>(ZT_PACKET_IDX_PAYLOAD);
+ unsigned int ptr = ZT_PACKET_IDX_PAYLOAD + 2;
+
+ while (count--) { // if ptr overflows Buffer will throw
+ // TODO: some flags are not yet implemented
+
+ unsigned int flags = (*this)[ptr++];
+ unsigned int extLen = at<uint16_t>(ptr); ptr += 2;
+ ptr += extLen; // unused right now
+ unsigned int addrType = (*this)[ptr++];
+ unsigned int addrLen = (*this)[ptr++];
+
+ switch(addrType) {
+ case 4: {
+ const InetAddress a(field(ptr,4),4,at<uint16_t>(ptr + 4));
+ if (
+ ((flags & ZT_PUSH_DIRECT_PATHS_FLAG_FORGET_PATH) == 0) && // not being told to forget
+ (!( ((flags & ZT_PUSH_DIRECT_PATHS_FLAG_CLUSTER_REDIRECT) == 0) && (peer->hasActivePathTo(now,a)) )) && // not already known
+ (RR->node->shouldUsePathForZeroTierTraffic(tPtr,peer->address(),_path->localSocket(),a)) ) // should use path
+ {
+ if ((flags & ZT_PUSH_DIRECT_PATHS_FLAG_CLUSTER_REDIRECT) != 0) {
+ peer->clusterRedirect(tPtr,_path,a,now);
+ } else if (++countPerScope[(int)a.ipScope()][0] <= ZT_PUSH_DIRECT_PATHS_MAX_PER_SCOPE_AND_FAMILY) {
+ peer->attemptToContactAt(tPtr,InetAddress(),a,now,false);
+ }
}
- }
- }
-
- if (allowed) {
- const uint64_t pid = packetId();
- const unsigned int difficulty = (*this)[ZT_PACKET_IDX_PAYLOAD + 1];
- const unsigned int challengeLength = at<uint16_t>(ZT_PACKET_IDX_PAYLOAD + 2);
- if (challengeLength > ZT_PROTO_MAX_PACKET_LENGTH)
- return true; // sanity check, drop invalid size
- const unsigned char *challenge = field(ZT_PACKET_IDX_PAYLOAD + 4,challengeLength);
-
- switch((*this)[ZT_PACKET_IDX_PAYLOAD]) {
-
- // Salsa20/12+SHA512 hashcash
- case 0x01: {
- if (difficulty <= 14) {
- unsigned char result[16];
- computeSalsa2012Sha512ProofOfWork(difficulty,challenge,challengeLength,result);
- TRACE("PROOF_OF_WORK computed for %s: difficulty==%u, challengeLength==%u, result: %.16llx%.16llx",peer->address().toString().c_str(),difficulty,challengeLength,Utils::ntoh(*(reinterpret_cast<const uint64_t *>(result))),Utils::ntoh(*(reinterpret_cast<const uint64_t *>(result + 8))));
- Packet outp(peer->address(),RR->identity.address(),Packet::VERB_OK);
- outp.append((unsigned char)Packet::VERB_REQUEST_PROOF_OF_WORK);
- outp.append(pid);
- outp.append((uint16_t)sizeof(result));
- outp.append(result,sizeof(result));
- outp.armor(peer->key(),true);
- RR->node->putPacket(_localAddress,_remoteAddress,outp.data(),outp.size());
- } else {
- Packet outp(peer->address(),RR->identity.address(),Packet::VERB_ERROR);
- outp.append((unsigned char)Packet::VERB_REQUEST_PROOF_OF_WORK);
- outp.append(pid);
- outp.append((unsigned char)Packet::ERROR_INVALID_REQUEST);
- outp.armor(peer->key(),true);
- RR->node->putPacket(_localAddress,_remoteAddress,outp.data(),outp.size());
+ } break;
+ case 6: {
+ const InetAddress a(field(ptr,16),16,at<uint16_t>(ptr + 16));
+ if (
+ ((flags & ZT_PUSH_DIRECT_PATHS_FLAG_FORGET_PATH) == 0) && // not being told to forget
+ (!( ((flags & ZT_PUSH_DIRECT_PATHS_FLAG_CLUSTER_REDIRECT) == 0) && (peer->hasActivePathTo(now,a)) )) && // not already known
+ (RR->node->shouldUsePathForZeroTierTraffic(tPtr,peer->address(),_path->localSocket(),a)) ) // should use path
+ {
+ if ((flags & ZT_PUSH_DIRECT_PATHS_FLAG_CLUSTER_REDIRECT) != 0) {
+ peer->clusterRedirect(tPtr,_path,a,now);
+ } else if (++countPerScope[(int)a.ipScope()][1] <= ZT_PUSH_DIRECT_PATHS_MAX_PER_SCOPE_AND_FAMILY) {
+ peer->attemptToContactAt(tPtr,InetAddress(),a,now,false);
}
- } break;
-
- default:
- TRACE("dropped REQUEST_PROOF_OF_WORK from %s(%s): unrecognized proof of work type",peer->address().toString().c_str(),_remoteAddress.toString().c_str());
- break;
- }
-
- peer->received(_localAddress,_remoteAddress,hops(),pid,Packet::VERB_REQUEST_PROOF_OF_WORK,0,Packet::VERB_NOP);
- } else {
- TRACE("dropped REQUEST_PROOF_OF_WORK from %s(%s): not trusted enough",peer->address().toString().c_str(),_remoteAddress.toString().c_str());
+ }
+ } break;
}
- } catch ( ... ) {
- TRACE("dropped REQUEST_PROOF_OF_WORK from %s(%s): unexpected exception",peer->address().toString().c_str(),_remoteAddress.toString().c_str());
+ ptr += addrLen;
}
+
+ peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_PUSH_DIRECT_PATHS,0,Packet::VERB_NOP,false,0);
+
return true;
}
-void IncomingPacket::computeSalsa2012Sha512ProofOfWork(unsigned int difficulty,const void *challenge,unsigned int challengeLength,unsigned char result[16])
+bool IncomingPacket::_doUSER_MESSAGE(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer)
{
- unsigned char salsabuf[131072]; // 131072 == protocol constant, size of memory buffer for this proof of work function
- char candidatebuf[ZT_PROTO_MAX_PACKET_LENGTH + 256];
- unsigned char shabuf[ZT_SHA512_DIGEST_LEN];
- const uint64_t s20iv = 0; // zero IV for Salsa20
- char *const candidate = (char *)(( ((uintptr_t)&(candidatebuf[0])) | 0xf ) + 1); // align to 16-byte boundary to ensure that uint64_t type punning of initial nonce is okay
- Salsa20 s20;
- unsigned int d;
- unsigned char *p;
-
- Utils::getSecureRandom(candidate,16);
- memcpy(candidate + 16,challenge,challengeLength);
-
- if (difficulty > 512)
- difficulty = 512; // sanity check
-
-try_salsa2012sha512_again:
- ++*(reinterpret_cast<volatile uint64_t *>(candidate));
-
- SHA512::hash(shabuf,candidate,16 + challengeLength);
- s20.init(shabuf,256,&s20iv);
- memset(salsabuf,0,sizeof(salsabuf));
- s20.encrypt12(salsabuf,salsabuf,sizeof(salsabuf));
- SHA512::hash(shabuf,salsabuf,sizeof(salsabuf));
-
- d = difficulty;
- p = shabuf;
- while (d >= 8) {
- if (*(p++))
- goto try_salsa2012sha512_again;
- d -= 8;
- }
- if (d > 0) {
- if ( ((((unsigned int)*p) << d) & 0xff00) != 0 )
- goto try_salsa2012sha512_again;
+ if (likely(size() >= (ZT_PACKET_IDX_PAYLOAD + 8))) {
+ ZT_UserMessage um;
+ um.origin = peer->address().toInt();
+ um.typeId = at<uint64_t>(ZT_PACKET_IDX_PAYLOAD);
+ um.data = reinterpret_cast<const void *>(reinterpret_cast<const uint8_t *>(data()) + ZT_PACKET_IDX_PAYLOAD + 8);
+ um.length = size() - (ZT_PACKET_IDX_PAYLOAD + 8);
+ RR->node->postEvent(tPtr,ZT_EVENT_USER_MESSAGE,reinterpret_cast<const void *>(&um));
}
- memcpy(result,candidate,16);
+ peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_USER_MESSAGE,0,Packet::VERB_NOP,false,0);
+
+ return true;
}
-bool IncomingPacket::testSalsa2012Sha512ProofOfWorkResult(unsigned int difficulty,const void *challenge,unsigned int challengeLength,const unsigned char proposedResult[16])
+bool IncomingPacket::_doREMOTE_TRACE(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer)
{
- unsigned char salsabuf[131072]; // 131072 == protocol constant, size of memory buffer for this proof of work function
- char candidate[ZT_PROTO_MAX_PACKET_LENGTH + 256];
- unsigned char shabuf[ZT_SHA512_DIGEST_LEN];
- const uint64_t s20iv = 0; // zero IV for Salsa20
- Salsa20 s20;
- unsigned int d;
- unsigned char *p;
-
- if (difficulty > 512)
- difficulty = 512; // sanity check
-
- memcpy(candidate,proposedResult,16);
- memcpy(candidate + 16,challenge,challengeLength);
-
- SHA512::hash(shabuf,candidate,16 + challengeLength);
- s20.init(shabuf,256,&s20iv);
- memset(salsabuf,0,sizeof(salsabuf));
- s20.encrypt12(salsabuf,salsabuf,sizeof(salsabuf));
- SHA512::hash(shabuf,salsabuf,sizeof(salsabuf));
-
- d = difficulty;
- p = shabuf;
- while (d >= 8) {
- if (*(p++))
- return false;
- d -= 8;
- }
- if (d > 0) {
- if ( ((((unsigned int)*p) << d) & 0xff00) != 0 )
- return false;
+ ZT_RemoteTrace rt;
+ const char *ptr = reinterpret_cast<const char *>(data()) + ZT_PACKET_IDX_PAYLOAD;
+ const char *const eof = reinterpret_cast<const char *>(data()) + size();
+ rt.origin = peer->address().toInt();
+ rt.data = const_cast<char *>(ptr); // start of first string
+ while (ptr < eof) {
+ if (!*ptr) { // end of string
+ rt.len = (unsigned int)(ptr - rt.data);
+ if ((rt.len > 0)&&(rt.len <= ZT_MAX_REMOTE_TRACE_SIZE)) {
+ RR->node->postEvent(tPtr,ZT_EVENT_REMOTE_TRACE,&rt);
+ }
+ rt.data = const_cast<char *>(++ptr); // start of next string, if any
+ } else {
+ ++ptr;
+ }
}
+ peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_REMOTE_TRACE,0,Packet::VERB_NOP,false,0);
+
return true;
}
-void IncomingPacket::_sendErrorNeedCertificate(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer,uint64_t nwid)
+void IncomingPacket::_sendErrorNeedCredentials(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer,const uint64_t nwid)
{
- Packet outp(source(),RR->identity.address(),Packet::VERB_ERROR);
- outp.append((unsigned char)verb());
- outp.append(packetId());
- outp.append((unsigned char)Packet::ERROR_NEED_MEMBERSHIP_CERTIFICATE);
- outp.append(nwid);
- outp.armor(peer->key(),true);
- RR->node->putPacket(_localAddress,_remoteAddress,outp.data(),outp.size());
+ const int64_t now = RR->node->now();
+ if (peer->rateGateOutgoingComRequest(now)) {
+ Packet outp(source(),RR->identity.address(),Packet::VERB_ERROR);
+ outp.append((uint8_t)verb());
+ outp.append(packetId());
+ outp.append((uint8_t)Packet::ERROR_NEED_MEMBERSHIP_CERTIFICATE);
+ outp.append(nwid);
+ outp.armor(peer->key(),true);
+ _path->send(RR,tPtr,outp.data(),outp.size(),now);
+ }
}
} // namespace ZeroTier
diff --git a/node/IncomingPacket.hpp b/node/IncomingPacket.hpp
index cd0b7dcf..88f4f066 100644
--- a/node/IncomingPacket.hpp
+++ b/node/IncomingPacket.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_INCOMINGPACKET_HPP
@@ -22,7 +30,7 @@
#include <stdexcept>
#include "Packet.hpp"
-#include "InetAddress.hpp"
+#include "Path.hpp"
#include "Utils.hpp"
#include "MulticastGroup.hpp"
#include "Peer.hpp"
@@ -56,16 +64,8 @@ class IncomingPacket : public Packet
public:
IncomingPacket() :
Packet(),
- _receiveTime(0),
- _localAddress(),
- _remoteAddress()
- {
- }
-
- IncomingPacket(const IncomingPacket &p)
+ _receiveTime(0)
{
- // All fields including InetAddress are memcpy'able
- memcpy(this,&p,sizeof(IncomingPacket));
}
/**
@@ -73,42 +73,31 @@ public:
*
* @param data Packet data
* @param len Packet length
- * @param localAddress Local interface address
- * @param remoteAddress Address from which packet came
+ * @param path Path over which packet arrived
* @param now Current time
* @throws std::out_of_range Range error processing packet
*/
- IncomingPacket(const void *data,unsigned int len,const InetAddress &localAddress,const InetAddress &remoteAddress,uint64_t now) :
+ IncomingPacket(const void *data,unsigned int len,const SharedPtr<Path> &path,int64_t now) :
Packet(data,len),
_receiveTime(now),
- _localAddress(localAddress),
- _remoteAddress(remoteAddress)
+ _path(path)
{
}
- inline IncomingPacket &operator=(const IncomingPacket &p)
- {
- // All fields including InetAddress are memcpy'able
- memcpy(this,&p,sizeof(IncomingPacket));
- return *this;
- }
-
/**
* Init packet-in-decode in place
*
* @param data Packet data
* @param len Packet length
- * @param localAddress Local interface address
- * @param remoteAddress Address from which packet came
+ * @param path Path over which packet arrived
* @param now Current time
* @throws std::out_of_range Range error processing packet
*/
- inline void init(const void *data,unsigned int len,const InetAddress &localAddress,const InetAddress &remoteAddress,uint64_t now)
+ inline void init(const void *data,unsigned int len,const SharedPtr<Path> &path,int64_t now)
{
copyFrom(data,len);
_receiveTime = now;
- _localAddress = localAddress;
- _remoteAddress = remoteAddress;
+ _path = path;
}
/**
@@ -118,76 +107,44 @@ public:
* about whether the packet was valid. A rejection is 'complete.'
*
* Once true is returned, this must not be called again. The packet's state
- * may no longer be valid. The only exception is deferred decoding. In this
- * case true is returned to indicate to the normal decode path that it is
- * finished with the packet. The packet will have added itself to the
- * deferred queue and will expect tryDecode() to be called one more time
- * with deferred set to true.
- *
- * Deferred decoding is performed by DeferredPackets.cpp and should not be
- * done elsewhere. Under deferred decoding packets only get one shot and
- * so the return value of tryDecode() is ignored.
+ * may no longer be valid.
*
* @param RR Runtime environment
- * @param deferred If true, this is a deferred decode and the return is ignored
+ * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
* @return True if decoding and processing is complete, false if caller should try again
*/
- bool tryDecode(const RuntimeEnvironment *RR,bool deferred);
+ bool tryDecode(const RuntimeEnvironment *RR,void *tPtr);
/**
* @return Time of packet receipt / start of decode
*/
- inline uint64_t receiveTime() const throw() { return _receiveTime; }
-
- /**
- * Compute the Salsa20/12+SHA512 proof of work function
- *
- * @param difficulty Difficulty in bits (max: 64)
- * @param challenge Challenge string
- * @param challengeLength Length of challenge in bytes (max allowed: ZT_PROTO_MAX_PACKET_LENGTH)
- * @param result Buffer to fill with 16-byte result
- */
- static void computeSalsa2012Sha512ProofOfWork(unsigned int difficulty,const void *challenge,unsigned int challengeLength,unsigned char result[16]);
-
- /**
- * Verify the result of Salsa20/12+SHA512 proof of work
- *
- * @param difficulty Difficulty in bits (max: 64)
- * @param challenge Challenge bytes
- * @param challengeLength Length of challenge in bytes (max allowed: ZT_PROTO_MAX_PACKET_LENGTH)
- * @param proposedResult Result supplied by client
- * @return True if result is valid
- */
- static bool testSalsa2012Sha512ProofOfWorkResult(unsigned int difficulty,const void *challenge,unsigned int challengeLength,const unsigned char proposedResult[16]);
+ inline uint64_t receiveTime() const { return _receiveTime; }
private:
// These are called internally to handle packet contents once it has
// been authenticated, decrypted, decompressed, and classified.
- bool _doERROR(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
- bool _doHELLO(const RuntimeEnvironment *RR,SharedPtr<Peer> &peer); // can be called with NULL peer, while all others cannot
- bool _doOK(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
- bool _doWHOIS(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
- bool _doRENDEZVOUS(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
- bool _doFRAME(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
- bool _doEXT_FRAME(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
- bool _doECHO(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
- bool _doMULTICAST_LIKE(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
- bool _doNETWORK_MEMBERSHIP_CERTIFICATE(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
- bool _doNETWORK_CONFIG_REQUEST(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
- bool _doNETWORK_CONFIG_REFRESH(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
- bool _doMULTICAST_GATHER(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
- bool _doMULTICAST_FRAME(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
- bool _doPUSH_DIRECT_PATHS(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
- bool _doCIRCUIT_TEST(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
- bool _doCIRCUIT_TEST_REPORT(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
- bool _doREQUEST_PROOF_OF_WORK(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
-
- // Send an ERROR_NEED_MEMBERSHIP_CERTIFICATE to a peer indicating that an updated cert is needed to communicate
- void _sendErrorNeedCertificate(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer,uint64_t nwid);
+ bool _doERROR(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer);
+ bool _doHELLO(const RuntimeEnvironment *RR,void *tPtr,const bool alreadyAuthenticated);
+ bool _doOK(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer);
+ bool _doWHOIS(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer);
+ bool _doRENDEZVOUS(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer);
+ bool _doFRAME(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer);
+ bool _doEXT_FRAME(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer);
+ bool _doECHO(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer);
+ bool _doMULTICAST_LIKE(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer);
+ bool _doNETWORK_CREDENTIALS(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer);
+ bool _doNETWORK_CONFIG_REQUEST(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer);
+ bool _doNETWORK_CONFIG(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer);
+ bool _doMULTICAST_GATHER(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer);
+ bool _doMULTICAST_FRAME(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer);
+ bool _doPUSH_DIRECT_PATHS(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer);
+ bool _doUSER_MESSAGE(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer);
+ bool _doREMOTE_TRACE(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer);
+
+ void _sendErrorNeedCredentials(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer,const uint64_t nwid);
uint64_t _receiveTime;
- InetAddress _localAddress;
- InetAddress _remoteAddress;
+ SharedPtr<Path> _path;
};
} // namespace ZeroTier
diff --git a/node/InetAddress.cpp b/node/InetAddress.cpp
index 3f6b9be6..36b4e434 100644
--- a/node/InetAddress.cpp
+++ b/node/InetAddress.cpp
@@ -1,11 +1,11 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
+ * (at your oion) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#include <stdio.h>
@@ -32,7 +40,6 @@ const InetAddress InetAddress::LO4((const void *)("\x7f\x00\x00\x01"),4,0);
const InetAddress InetAddress::LO6((const void *)("\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01"),16,0);
InetAddress::IpScope InetAddress::ipScope() const
- throw()
{
switch(ss_family) {
@@ -55,7 +62,7 @@ InetAddress::IpScope InetAddress::ipScope() const
case 0x37: return IP_SCOPE_PSEUDOPRIVATE; // 55.0.0.0/8 (US DoD)
case 0x38: return IP_SCOPE_PSEUDOPRIVATE; // 56.0.0.0/8 (US Postal Service)
case 0x64:
- if ((ip & 0xffc00000) == 0x64400000) return IP_SCOPE_SHARED; // 100.64.0.0/10
+ if ((ip & 0xffc00000) == 0x64400000) return IP_SCOPE_PRIVATE; // 100.64.0.0/10
break;
case 0x7f: return IP_SCOPE_LOOPBACK; // 127.0.0.0/8
case 0xa9:
@@ -103,126 +110,114 @@ InetAddress::IpScope InetAddress::ipScope() const
return IP_SCOPE_NONE;
}
-void InetAddress::set(const std::string &ip,unsigned int port)
- throw()
-{
- memset(this,0,sizeof(InetAddress));
- if (ip.find(':') != std::string::npos) {
- struct sockaddr_in6 *sin6 = reinterpret_cast<struct sockaddr_in6 *>(this);
- ss_family = AF_INET6;
- sin6->sin6_port = Utils::hton((uint16_t)port);
- if (inet_pton(AF_INET6,ip.c_str(),(void *)&(sin6->sin6_addr.s6_addr)) <= 0)
- memset(this,0,sizeof(InetAddress));
- } else {
- struct sockaddr_in *sin = reinterpret_cast<struct sockaddr_in *>(this);
- ss_family = AF_INET;
- sin->sin_port = Utils::hton((uint16_t)port);
- if (inet_pton(AF_INET,ip.c_str(),(void *)&(sin->sin_addr.s_addr)) <= 0)
- memset(this,0,sizeof(InetAddress));
- }
-}
-
void InetAddress::set(const void *ipBytes,unsigned int ipLen,unsigned int port)
- throw()
{
memset(this,0,sizeof(InetAddress));
if (ipLen == 4) {
uint32_t ipb[1];
- memcpy(ipb,ipBytes,4);
+ ZT_FAST_MEMCPY(ipb,ipBytes,4);
ss_family = AF_INET;
reinterpret_cast<struct sockaddr_in *>(this)->sin_addr.s_addr = ipb[0];
reinterpret_cast<struct sockaddr_in *>(this)->sin_port = Utils::hton((uint16_t)port);
} else if (ipLen == 16) {
ss_family = AF_INET6;
- memcpy(reinterpret_cast<struct sockaddr_in6 *>(this)->sin6_addr.s6_addr,ipBytes,16);
+ ZT_FAST_MEMCPY(reinterpret_cast<struct sockaddr_in6 *>(this)->sin6_addr.s6_addr,ipBytes,16);
reinterpret_cast<struct sockaddr_in6 *>(this)->sin6_port = Utils::hton((uint16_t)port);
}
}
-std::string InetAddress::toString() const
+char *InetAddress::toString(char buf[64]) const
{
- char buf[128];
- switch(ss_family) {
- case AF_INET:
- Utils::snprintf(buf,sizeof(buf),"%d.%d.%d.%d/%d",
- (int)(reinterpret_cast<const unsigned char *>(&(reinterpret_cast<const struct sockaddr_in *>(this)->sin_addr.s_addr)))[0],
- (int)(reinterpret_cast<const unsigned char *>(&(reinterpret_cast<const struct sockaddr_in *>(this)->sin_addr.s_addr)))[1],
- (int)(reinterpret_cast<const unsigned char *>(&(reinterpret_cast<const struct sockaddr_in *>(this)->sin_addr.s_addr)))[2],
- (int)(reinterpret_cast<const unsigned char *>(&(reinterpret_cast<const struct sockaddr_in *>(this)->sin_addr.s_addr)))[3],
- (int)Utils::ntoh((uint16_t)(reinterpret_cast<const struct sockaddr_in *>(this)->sin_port))
- );
- return std::string(buf);
- case AF_INET6:
- Utils::snprintf(buf,sizeof(buf),"%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x/%d",
- (int)(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr[0]),
- (int)(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr[1]),
- (int)(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr[2]),
- (int)(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr[3]),
- (int)(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr[4]),
- (int)(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr[5]),
- (int)(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr[6]),
- (int)(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr[7]),
- (int)(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr[8]),
- (int)(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr[9]),
- (int)(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr[10]),
- (int)(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr[11]),
- (int)(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr[12]),
- (int)(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr[13]),
- (int)(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr[14]),
- (int)(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr[15]),
- (int)Utils::ntoh((uint16_t)(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_port))
- );
- return std::string(buf);
+ char *p = toIpString(buf);
+ if (*p) {
+ while (*p) ++p;
+ *(p++) = '/';
+ Utils::decimal(port(),p);
}
- return std::string();
+ return buf;
}
-std::string InetAddress::toIpString() const
+char *InetAddress::toIpString(char buf[64]) const
{
- char buf[128];
switch(ss_family) {
- case AF_INET:
- Utils::snprintf(buf,sizeof(buf),"%d.%d.%d.%d",
- (int)(reinterpret_cast<const unsigned char *>(&(reinterpret_cast<const struct sockaddr_in *>(this)->sin_addr.s_addr)))[0],
- (int)(reinterpret_cast<const unsigned char *>(&(reinterpret_cast<const struct sockaddr_in *>(this)->sin_addr.s_addr)))[1],
- (int)(reinterpret_cast<const unsigned char *>(&(reinterpret_cast<const struct sockaddr_in *>(this)->sin_addr.s_addr)))[2],
- (int)(reinterpret_cast<const unsigned char *>(&(reinterpret_cast<const struct sockaddr_in *>(this)->sin_addr.s_addr)))[3]
- );
- return std::string(buf);
- case AF_INET6:
- Utils::snprintf(buf,sizeof(buf),"%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x",
- (int)(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr[0]),
- (int)(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr[1]),
- (int)(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr[2]),
- (int)(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr[3]),
- (int)(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr[4]),
- (int)(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr[5]),
- (int)(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr[6]),
- (int)(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr[7]),
- (int)(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr[8]),
- (int)(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr[9]),
- (int)(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr[10]),
- (int)(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr[11]),
- (int)(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr[12]),
- (int)(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr[13]),
- (int)(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr[14]),
- (int)(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr[15])
- );
- return std::string(buf);
+ case AF_INET: {
+ const uint8_t *a = reinterpret_cast<const uint8_t *>(&(reinterpret_cast<const struct sockaddr_in *>(this)->sin_addr.s_addr));
+ char *p = buf;
+ for(int i=0;;++i) {
+ Utils::decimal((unsigned long)a[i],p);
+ if (i != 3) {
+ while (*p) ++p;
+ *(p++) = '.';
+ } else break;
+ }
+ } break;
+
+ case AF_INET6: {
+ uint16_t a[8];
+ ZT_FAST_MEMCPY(a,reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr,16);
+ char *p = buf;
+ for(int i=0;i<8;++i) {
+ Utils::hex(Utils::ntoh(a[i]),p);
+ p[4] = (i == 7) ? (char)0 : ':';
+ p += 5;
+ }
+ } break;
+
+ default:
+ buf[0] = (char)0;
+ break;
}
- return std::string();
+ return buf;
}
-void InetAddress::fromString(const std::string &ipSlashPort)
+bool InetAddress::fromString(const char *ipSlashPort)
{
- const std::size_t slashAt = ipSlashPort.find('/');
- if (slashAt == std::string::npos) {
- set(ipSlashPort,0);
+ char buf[64];
+
+ memset(this,0,sizeof(InetAddress));
+
+ if (!*ipSlashPort)
+ return true;
+ if (!Utils::scopy(buf,sizeof(buf),ipSlashPort))
+ return false;
+
+ char *portAt = buf;
+ while ((*portAt)&&(*portAt != '/'))
+ ++portAt;
+ unsigned int port = 0;
+ if (*portAt) {
+ *(portAt++) = (char)0;
+ port = Utils::strToUInt(portAt) & 0xffff;
+ }
+
+ if (strchr(buf,':')) {
+ uint16_t a[8];
+ unsigned int b = 0;
+ char *saveptr = (char *)0;
+ for(char *s=Utils::stok(buf,":",&saveptr);((s)&&(b<8));s=Utils::stok((char *)0,":",&saveptr))
+ a[b++] = Utils::hton((uint16_t)(Utils::hexStrToUInt(s) & 0xffff));
+
+ struct sockaddr_in6 *const in6 = reinterpret_cast<struct sockaddr_in6 *>(this);
+ in6->sin6_family = AF_INET6;
+ ZT_FAST_MEMCPY(in6->sin6_addr.s6_addr,a,16);
+ in6->sin6_port = Utils::hton((uint16_t)port);
+
+ return true;
+ } else if (strchr(buf,'.')) {
+ uint8_t a[4];
+ unsigned int b = 0;
+ char *saveptr = (char *)0;
+ for(char *s=Utils::stok(buf,".",&saveptr);((s)&&(b<4));s=Utils::stok((char *)0,".",&saveptr))
+ a[b++] = (uint8_t)(Utils::strToUInt(s) & 0xff);
+
+ struct sockaddr_in *const in = reinterpret_cast<struct sockaddr_in *>(this);
+ in->sin_family = AF_INET;
+ ZT_FAST_MEMCPY(&(in->sin_addr.s_addr),a,4);
+ in->sin_port = Utils::hton((uint16_t)port);
+
+ return true;
} else {
- long p = strtol(ipSlashPort.substr(slashAt+1).c_str(),(char **)0,10);
- if ((p > 0)&&(p <= 0xffff))
- set(ipSlashPort.substr(0,slashAt),(unsigned int)p);
- else set(ipSlashPort.substr(0,slashAt),0);
+ return false;
}
}
@@ -236,9 +231,14 @@ InetAddress InetAddress::netmask() const
case AF_INET6: {
uint64_t nm[2];
const unsigned int bits = netmaskBits();
- nm[0] = Utils::hton((uint64_t)((bits >= 64) ? 0xffffffffffffffffULL : (0xffffffffffffffffULL << (64 - bits))));
- nm[1] = Utils::hton((uint64_t)((bits <= 64) ? 0ULL : (0xffffffffffffffffULL << (128 - bits))));
- memcpy(reinterpret_cast<struct sockaddr_in6 *>(&r)->sin6_addr.s6_addr,nm,16);
+ if(bits) {
+ nm[0] = Utils::hton((uint64_t)((bits >= 64) ? 0xffffffffffffffffULL : (0xffffffffffffffffULL << (64 - bits))));
+ nm[1] = Utils::hton((uint64_t)((bits <= 64) ? 0ULL : (0xffffffffffffffffULL << (128 - bits))));
+ } else {
+ nm[0] = 0;
+ nm[1] = 0;
+ }
+ ZT_FAST_MEMCPY(reinterpret_cast<struct sockaddr_in6 *>(&r)->sin6_addr.s6_addr,nm,16);
} break;
}
return r;
@@ -264,15 +264,37 @@ InetAddress InetAddress::network() const
case AF_INET6: {
uint64_t nm[2];
const unsigned int bits = netmaskBits();
- memcpy(nm,reinterpret_cast<struct sockaddr_in6 *>(&r)->sin6_addr.s6_addr,16);
+ ZT_FAST_MEMCPY(nm,reinterpret_cast<struct sockaddr_in6 *>(&r)->sin6_addr.s6_addr,16);
nm[0] &= Utils::hton((uint64_t)((bits >= 64) ? 0xffffffffffffffffULL : (0xffffffffffffffffULL << (64 - bits))));
nm[1] &= Utils::hton((uint64_t)((bits <= 64) ? 0ULL : (0xffffffffffffffffULL << (128 - bits))));
- memcpy(reinterpret_cast<struct sockaddr_in6 *>(&r)->sin6_addr.s6_addr,nm,16);
+ ZT_FAST_MEMCPY(reinterpret_cast<struct sockaddr_in6 *>(&r)->sin6_addr.s6_addr,nm,16);
} break;
}
return r;
}
+bool InetAddress::isEqualPrefix(const InetAddress &addr) const
+{
+ if (addr.ss_family == ss_family) {
+ switch(ss_family) {
+ case AF_INET6: {
+ const InetAddress mask(netmask());
+ InetAddress addr_mask(addr.netmask());
+ const uint8_t *n = reinterpret_cast<const uint8_t *>(reinterpret_cast<const struct sockaddr_in6 *>(&addr_mask)->sin6_addr.s6_addr);
+ const uint8_t *m = reinterpret_cast<const uint8_t *>(reinterpret_cast<const struct sockaddr_in6 *>(&mask)->sin6_addr.s6_addr);
+ const uint8_t *a = reinterpret_cast<const uint8_t *>(reinterpret_cast<const struct sockaddr_in6 *>(&addr)->sin6_addr.s6_addr);
+ const uint8_t *b = reinterpret_cast<const uint8_t *>(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr);
+ for(unsigned int i=0;i<16;++i) {
+ if ((a[i] & m[i]) != (b[i] & n[i]))
+ return false;
+ }
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
bool InetAddress::containsAddress(const InetAddress &addr) const
{
if (addr.ss_family == ss_family) {
@@ -300,7 +322,6 @@ bool InetAddress::containsAddress(const InetAddress &addr) const
}
bool InetAddress::isNetwork() const
- throw()
{
switch(ss_family) {
case AF_INET: {
@@ -333,7 +354,6 @@ bool InetAddress::isNetwork() const
}
bool InetAddress::operator==(const InetAddress &a) const
- throw()
{
if (ss_family == a.ss_family) {
switch(ss_family) {
@@ -357,7 +377,6 @@ bool InetAddress::operator==(const InetAddress &a) const
}
bool InetAddress::operator<(const InetAddress &a) const
- throw()
{
if (ss_family < a.ss_family)
return true;
diff --git a/node/InetAddress.hpp b/node/InetAddress.hpp
index e03deb71..f69b3cc2 100644
--- a/node/InetAddress.hpp
+++ b/node/InetAddress.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_INETADDRESS_HPP
@@ -23,8 +31,6 @@
#include <string.h>
#include <stdint.h>
-#include <string>
-
#include "Constants.hpp"
#include "../include/ZeroTierOne.h"
#include "Utils.hpp"
@@ -73,110 +79,106 @@ struct InetAddress : public sockaddr_storage
IP_SCOPE_PSEUDOPRIVATE = 3, // 28.x.x.x, etc. -- unofficially unrouted IPv4 blocks often "bogarted"
IP_SCOPE_GLOBAL = 4, // globally routable IP address (all others)
IP_SCOPE_LINK_LOCAL = 5, // 169.254.x.x, IPv6 LL
- IP_SCOPE_SHARED = 6, // 100.64.0.0/10, shared space for e.g. carrier-grade NAT
+ IP_SCOPE_SHARED = 6, // currently unused, formerly used for carrier-grade NAT ranges
IP_SCOPE_PRIVATE = 7 // 10.x.x.x, 192.168.x.x, etc.
};
- InetAddress() throw() { memset(this,0,sizeof(InetAddress)); }
- InetAddress(const InetAddress &a) throw() { memcpy(this,&a,sizeof(InetAddress)); }
- InetAddress(const InetAddress *a) throw() { memcpy(this,a,sizeof(InetAddress)); }
- InetAddress(const struct sockaddr_storage &ss) throw() { *this = ss; }
- InetAddress(const struct sockaddr_storage *ss) throw() { *this = ss; }
- InetAddress(const struct sockaddr &sa) throw() { *this = sa; }
- InetAddress(const struct sockaddr *sa) throw() { *this = sa; }
- InetAddress(const struct sockaddr_in &sa) throw() { *this = sa; }
- InetAddress(const struct sockaddr_in *sa) throw() { *this = sa; }
- InetAddress(const struct sockaddr_in6 &sa) throw() { *this = sa; }
- InetAddress(const struct sockaddr_in6 *sa) throw() { *this = sa; }
- InetAddress(const void *ipBytes,unsigned int ipLen,unsigned int port) throw() { this->set(ipBytes,ipLen,port); }
- InetAddress(const uint32_t ipv4,unsigned int port) throw() { this->set(&ipv4,4,port); }
- InetAddress(const std::string &ip,unsigned int port) throw() { this->set(ip,port); }
- InetAddress(const std::string &ipSlashPort) throw() { this->fromString(ipSlashPort); }
- InetAddress(const char *ipSlashPort) throw() { this->fromString(std::string(ipSlashPort)); }
+ // Can be used with the unordered maps and sets in c++11. We don't use C++11 in the core
+ // but this is safe to put here.
+ struct Hasher
+ {
+ inline std::size_t operator()(const InetAddress &a) const { return (std::size_t)a.hashCode(); }
+ };
+
+ InetAddress() { memset(this,0,sizeof(InetAddress)); }
+ InetAddress(const InetAddress &a) { ZT_FAST_MEMCPY(this,&a,sizeof(InetAddress)); }
+ InetAddress(const InetAddress *a) { ZT_FAST_MEMCPY(this,a,sizeof(InetAddress)); }
+ InetAddress(const struct sockaddr_storage &ss) { *this = ss; }
+ InetAddress(const struct sockaddr_storage *ss) { *this = ss; }
+ InetAddress(const struct sockaddr &sa) { *this = sa; }
+ InetAddress(const struct sockaddr *sa) { *this = sa; }
+ InetAddress(const struct sockaddr_in &sa) { *this = sa; }
+ InetAddress(const struct sockaddr_in *sa) { *this = sa; }
+ InetAddress(const struct sockaddr_in6 &sa) { *this = sa; }
+ InetAddress(const struct sockaddr_in6 *sa) { *this = sa; }
+ InetAddress(const void *ipBytes,unsigned int ipLen,unsigned int port) { this->set(ipBytes,ipLen,port); }
+ InetAddress(const uint32_t ipv4,unsigned int port) { this->set(&ipv4,4,port); }
+ InetAddress(const char *ipSlashPort) { this->fromString(ipSlashPort); }
inline InetAddress &operator=(const InetAddress &a)
- throw()
{
if (&a != this)
- memcpy(this,&a,sizeof(InetAddress));
+ ZT_FAST_MEMCPY(this,&a,sizeof(InetAddress));
return *this;
}
inline InetAddress &operator=(const InetAddress *a)
- throw()
{
if (a != this)
- memcpy(this,a,sizeof(InetAddress));
+ ZT_FAST_MEMCPY(this,a,sizeof(InetAddress));
return *this;
}
inline InetAddress &operator=(const struct sockaddr_storage &ss)
- throw()
{
if (reinterpret_cast<const InetAddress *>(&ss) != this)
- memcpy(this,&ss,sizeof(InetAddress));
+ ZT_FAST_MEMCPY(this,&ss,sizeof(InetAddress));
return *this;
}
inline InetAddress &operator=(const struct sockaddr_storage *ss)
- throw()
{
if (reinterpret_cast<const InetAddress *>(ss) != this)
- memcpy(this,ss,sizeof(InetAddress));
+ ZT_FAST_MEMCPY(this,ss,sizeof(InetAddress));
return *this;
}
inline InetAddress &operator=(const struct sockaddr_in &sa)
- throw()
{
if (reinterpret_cast<const InetAddress *>(&sa) != this) {
memset(this,0,sizeof(InetAddress));
- memcpy(this,&sa,sizeof(struct sockaddr_in));
+ ZT_FAST_MEMCPY(this,&sa,sizeof(struct sockaddr_in));
}
return *this;
}
inline InetAddress &operator=(const struct sockaddr_in *sa)
- throw()
{
if (reinterpret_cast<const InetAddress *>(sa) != this) {
memset(this,0,sizeof(InetAddress));
- memcpy(this,sa,sizeof(struct sockaddr_in));
+ ZT_FAST_MEMCPY(this,sa,sizeof(struct sockaddr_in));
}
return *this;
}
inline InetAddress &operator=(const struct sockaddr_in6 &sa)
- throw()
{
if (reinterpret_cast<const InetAddress *>(&sa) != this) {
memset(this,0,sizeof(InetAddress));
- memcpy(this,&sa,sizeof(struct sockaddr_in6));
+ ZT_FAST_MEMCPY(this,&sa,sizeof(struct sockaddr_in6));
}
return *this;
}
inline InetAddress &operator=(const struct sockaddr_in6 *sa)
- throw()
{
if (reinterpret_cast<const InetAddress *>(sa) != this) {
memset(this,0,sizeof(InetAddress));
- memcpy(this,sa,sizeof(struct sockaddr_in6));
+ ZT_FAST_MEMCPY(this,sa,sizeof(struct sockaddr_in6));
}
return *this;
}
inline InetAddress &operator=(const struct sockaddr &sa)
- throw()
{
if (reinterpret_cast<const InetAddress *>(&sa) != this) {
memset(this,0,sizeof(InetAddress));
switch(sa.sa_family) {
case AF_INET:
- memcpy(this,&sa,sizeof(struct sockaddr_in));
+ ZT_FAST_MEMCPY(this,&sa,sizeof(struct sockaddr_in));
break;
case AF_INET6:
- memcpy(this,&sa,sizeof(struct sockaddr_in6));
+ ZT_FAST_MEMCPY(this,&sa,sizeof(struct sockaddr_in6));
break;
}
}
@@ -184,16 +186,15 @@ struct InetAddress : public sockaddr_storage
}
inline InetAddress &operator=(const struct sockaddr *sa)
- throw()
{
if (reinterpret_cast<const InetAddress *>(sa) != this) {
memset(this,0,sizeof(InetAddress));
switch(sa->sa_family) {
case AF_INET:
- memcpy(this,sa,sizeof(struct sockaddr_in));
+ ZT_FAST_MEMCPY(this,sa,sizeof(struct sockaddr_in));
break;
case AF_INET6:
- memcpy(this,sa,sizeof(struct sockaddr_in6));
+ ZT_FAST_MEMCPY(this,sa,sizeof(struct sockaddr_in6));
break;
}
}
@@ -203,17 +204,7 @@ struct InetAddress : public sockaddr_storage
/**
* @return IP scope classification (e.g. loopback, link-local, private, global)
*/
- IpScope ipScope() const
- throw();
-
- /**
- * Set from a string-format IP and a port
- *
- * @param ip IP address in V4 or V6 ASCII notation
- * @param port Port or 0 for none
- */
- void set(const std::string &ip,unsigned int port)
- throw();
+ IpScope ipScope() const;
/**
* Set from a raw IP and port number
@@ -222,8 +213,7 @@ struct InetAddress : public sockaddr_storage
* @param ipLen Length of IP address: 4 or 16
* @param port Port number or 0 for none
*/
- void set(const void *ipBytes,unsigned int ipLen,unsigned int port)
- throw();
+ void set(const void *ipBytes,unsigned int ipLen,unsigned int port);
/**
* Set the port component
@@ -264,23 +254,23 @@ struct InetAddress : public sockaddr_storage
/**
* @return ASCII IP/port format representation
*/
- std::string toString() const;
+ char *toString(char buf[64]) const;
/**
* @return IP portion only, in ASCII string format
*/
- std::string toIpString() const;
+ char *toIpString(char buf[64]) const;
/**
- * @param ipSlashPort ASCII IP/port format notation
+ * @param ipSlashPort IP/port (port is optional, will be 0 if not included)
+ * @return True if address appeared to be valid
*/
- void fromString(const std::string &ipSlashPort);
+ bool fromString(const char *ipSlashPort);
/**
* @return Port or 0 if no port component defined
*/
inline unsigned int port() const
- throw()
{
switch(ss_family) {
case AF_INET: return Utils::ntoh((uint16_t)(reinterpret_cast<const struct sockaddr_in *>(this)->sin_port));
@@ -298,7 +288,20 @@ struct InetAddress : public sockaddr_storage
*
* @return Netmask bits
*/
- inline unsigned int netmaskBits() const throw() { return port(); }
+ inline unsigned int netmaskBits() const { return port(); }
+
+ /**
+ * @return True if netmask bits is valid for the address type
+ */
+ inline bool netmaskBitsValid() const
+ {
+ const unsigned int n = port();
+ switch(ss_family) {
+ case AF_INET: return (n <= 32);
+ case AF_INET6: return (n <= 128);
+ }
+ return false;
+ }
/**
* Alias for port()
@@ -308,7 +311,7 @@ struct InetAddress : public sockaddr_storage
*
* @return Gateway metric
*/
- inline unsigned int metric() const throw() { return port(); }
+ inline unsigned int metric() const { return port(); }
/**
* Construct a full netmask as an InetAddress
@@ -335,6 +338,14 @@ struct InetAddress : public sockaddr_storage
InetAddress network() const;
/**
+ * Test whether this IPv6 prefix matches the prefix of a given IPv6 address
+ *
+ * @param addr Address to check
+ * @return True if this IPv6 prefix matches the prefix of a given IPv6 address
+ */
+ bool isEqualPrefix(const InetAddress &addr) const;
+
+ /**
* Test whether this IP/netmask contains this address
*
* @param addr Address to check
@@ -345,18 +356,17 @@ struct InetAddress : public sockaddr_storage
/**
* @return True if this is an IPv4 address
*/
- inline bool isV4() const throw() { return (ss_family == AF_INET); }
+ inline bool isV4() const { return (ss_family == AF_INET); }
/**
* @return True if this is an IPv6 address
*/
- inline bool isV6() const throw() { return (ss_family == AF_INET6); }
+ inline bool isV6() const { return (ss_family == AF_INET6); }
/**
* @return pointer to raw address bytes or NULL if not available
*/
inline const void *rawIpData() const
- throw()
{
switch(ss_family) {
case AF_INET: return (const void *)&(reinterpret_cast<const struct sockaddr_in *>(this)->sin_addr.s_addr);
@@ -366,6 +376,25 @@ struct InetAddress : public sockaddr_storage
}
/**
+ * @return InetAddress containing only the IP portion of this address and a zero port, or NULL if not IPv4 or IPv6
+ */
+ inline InetAddress ipOnly() const
+ {
+ InetAddress r;
+ switch(ss_family) {
+ case AF_INET:
+ r.ss_family = AF_INET;
+ reinterpret_cast<struct sockaddr_in *>(&r)->sin_addr.s_addr = reinterpret_cast<const struct sockaddr_in *>(this)->sin_addr.s_addr;
+ break;
+ case AF_INET6:
+ r.ss_family = AF_INET6;
+ ZT_FAST_MEMCPY(reinterpret_cast<struct sockaddr_in6 *>(&r)->sin6_addr.s6_addr,reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr,16);
+ break;
+ }
+ return r;
+ }
+
+ /**
* Performs an IP-only comparison or, if that is impossible, a memcmp()
*
* @param a InetAddress to compare again
@@ -384,9 +413,48 @@ struct InetAddress : public sockaddr_storage
}
/**
+ * Performs an IP-only comparison or, if that is impossible, a memcmp()
+ *
+ * This version compares only the first 64 bits of IPv6 addresses.
+ *
+ * @param a InetAddress to compare again
+ * @return True if only IP portions are equal (false for non-IP or null addresses)
+ */
+ inline bool ipsEqual2(const InetAddress &a) const
+ {
+ if (ss_family == a.ss_family) {
+ if (ss_family == AF_INET)
+ return (reinterpret_cast<const struct sockaddr_in *>(this)->sin_addr.s_addr == reinterpret_cast<const struct sockaddr_in *>(&a)->sin_addr.s_addr);
+ if (ss_family == AF_INET6)
+ return (memcmp(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr,reinterpret_cast<const struct sockaddr_in6 *>(&a)->sin6_addr.s6_addr,8) == 0);
+ return (memcmp(this,&a,sizeof(InetAddress)) == 0);
+ }
+ return false;
+ }
+
+ inline unsigned long hashCode() const
+ {
+ if (ss_family == AF_INET) {
+ return ((unsigned long)reinterpret_cast<const struct sockaddr_in *>(this)->sin_addr.s_addr + (unsigned long)reinterpret_cast<const struct sockaddr_in *>(this)->sin_port);
+ } else if (ss_family == AF_INET6) {
+ unsigned long tmp = reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_port;
+ const uint8_t *a = reinterpret_cast<const uint8_t *>(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr);
+ for(long i=0;i<16;++i)
+ reinterpret_cast<uint8_t *>(&tmp)[i % sizeof(tmp)] ^= a[i];
+ return tmp;
+ } else {
+ unsigned long tmp = reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_port;
+ const uint8_t *a = reinterpret_cast<const uint8_t *>(this);
+ for(long i=0;i<(long)sizeof(InetAddress);++i)
+ reinterpret_cast<uint8_t *>(&tmp)[i % sizeof(tmp)] ^= a[i];
+ return tmp;
+ }
+ }
+
+ /**
* Set to null/zero
*/
- inline void zero() throw() { memset(this,0,sizeof(InetAddress)); }
+ inline void zero() { memset(this,0,sizeof(InetAddress)); }
/**
* Check whether this is a network/route rather than an IP assignment
@@ -396,13 +464,36 @@ struct InetAddress : public sockaddr_storage
*
* @return True if everything after netmask bits is zero
*/
- bool isNetwork() const
- throw();
+ bool isNetwork() const;
+
+ /**
+ * @return 14-bit (0-16383) hash of this IP's first 24 or 48 bits (for V4 or V6) for rate limiting code, or 0 if non-IP
+ */
+ inline unsigned long rateGateHash() const
+ {
+ unsigned long h = 0;
+ switch(ss_family) {
+ case AF_INET:
+ h = (Utils::ntoh((uint32_t)reinterpret_cast<const struct sockaddr_in *>(this)->sin_addr.s_addr) & 0xffffff00) >> 8;
+ h ^= (h >> 14);
+ break;
+ case AF_INET6: {
+ const uint8_t *ip = reinterpret_cast<const uint8_t *>(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr);
+ h = ((unsigned long)ip[0]); h <<= 1;
+ h += ((unsigned long)ip[1]); h <<= 1;
+ h += ((unsigned long)ip[2]); h <<= 1;
+ h += ((unsigned long)ip[3]); h <<= 1;
+ h += ((unsigned long)ip[4]); h <<= 1;
+ h += ((unsigned long)ip[5]);
+ } break;
+ }
+ return (h & 0x3fff);
+ }
/**
* @return True if address family is non-zero
*/
- inline operator bool() const throw() { return (ss_family != 0); }
+ inline operator bool() const { return (ss_family != 0); }
template<unsigned int C>
inline void serialize(Buffer<C> &b) const
@@ -446,26 +537,26 @@ struct InetAddress : public sockaddr_storage
return (unsigned int)(b.template at<uint16_t>(p) + 3); // other addresses begin with 16-bit non-inclusive length
case 0x04:
ss_family = AF_INET;
- memcpy(&(reinterpret_cast<struct sockaddr_in *>(this)->sin_addr.s_addr),b.field(p,4),4); p += 4;
+ ZT_FAST_MEMCPY(&(reinterpret_cast<struct sockaddr_in *>(this)->sin_addr.s_addr),b.field(p,4),4); p += 4;
reinterpret_cast<struct sockaddr_in *>(this)->sin_port = Utils::hton(b.template at<uint16_t>(p)); p += 2;
break;
case 0x06:
ss_family = AF_INET6;
- memcpy(reinterpret_cast<struct sockaddr_in6 *>(this)->sin6_addr.s6_addr,b.field(p,16),16); p += 16;
+ ZT_FAST_MEMCPY(reinterpret_cast<struct sockaddr_in6 *>(this)->sin6_addr.s6_addr,b.field(p,16),16); p += 16;
reinterpret_cast<struct sockaddr_in *>(this)->sin_port = Utils::hton(b.template at<uint16_t>(p)); p += 2;
break;
default:
- throw std::invalid_argument("invalid serialized InetAddress");
+ throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_BAD_ENCODING;
}
return (p - startAt);
}
- bool operator==(const InetAddress &a) const throw();
- bool operator<(const InetAddress &a) const throw();
- inline bool operator!=(const InetAddress &a) const throw() { return !(*this == a); }
- inline bool operator>(const InetAddress &a) const throw() { return (a < *this); }
- inline bool operator<=(const InetAddress &a) const throw() { return !(a < *this); }
- inline bool operator>=(const InetAddress &a) const throw() { return !(*this < a); }
+ bool operator==(const InetAddress &a) const;
+ bool operator<(const InetAddress &a) const;
+ inline bool operator!=(const InetAddress &a) const { return !(*this == a); }
+ inline bool operator>(const InetAddress &a) const { return (a < *this); }
+ inline bool operator<=(const InetAddress &a) const { return !(a < *this); }
+ inline bool operator>=(const InetAddress &a) const { return !(*this < a); }
/**
* @param mac MAC address seed
diff --git a/node/MAC.hpp b/node/MAC.hpp
index 95623f12..c08323a4 100644
--- a/node/MAC.hpp
+++ b/node/MAC.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_MAC_HPP
@@ -36,30 +44,24 @@ namespace ZeroTier {
class MAC
{
public:
- MAC() throw() : _m(0ULL) {}
- MAC(const MAC &m) throw() : _m(m._m) {}
+ MAC() : _m(0ULL) {}
+ MAC(const MAC &m) : _m(m._m) {}
- MAC(const unsigned char a,const unsigned char b,const unsigned char c,const unsigned char d,const unsigned char e,const unsigned char f) throw() :
+ MAC(const unsigned char a,const unsigned char b,const unsigned char c,const unsigned char d,const unsigned char e,const unsigned char f) :
_m( ((((uint64_t)a) & 0xffULL) << 40) |
((((uint64_t)b) & 0xffULL) << 32) |
((((uint64_t)c) & 0xffULL) << 24) |
((((uint64_t)d) & 0xffULL) << 16) |
((((uint64_t)e) & 0xffULL) << 8) |
(((uint64_t)f) & 0xffULL) ) {}
-
- MAC(const char *s) throw() { fromString(s); }
- MAC(const std::string &s) throw() { fromString(s.c_str()); }
-
- MAC(const void *bits,unsigned int len) throw() { setTo(bits,len); }
-
- MAC(const Address &ztaddr,uint64_t nwid) throw() { fromAddress(ztaddr,nwid); }
-
- MAC(const uint64_t m) throw() : _m(m & 0xffffffffffffULL) {}
+ MAC(const void *bits,unsigned int len) { setTo(bits,len); }
+ MAC(const Address &ztaddr,uint64_t nwid) { fromAddress(ztaddr,nwid); }
+ MAC(const uint64_t m) : _m(m & 0xffffffffffffULL) {}
/**
* @return MAC in 64-bit integer
*/
- inline uint64_t toInt() const throw() { return _m; }
+ inline uint64_t toInt() const { return _m; }
/**
* Set MAC to zero
@@ -69,14 +71,13 @@ public:
/**
* @return True if MAC is non-zero
*/
- inline operator bool() const throw() { return (_m != 0ULL); }
+ inline operator bool() const { return (_m != 0ULL); }
/**
* @param bits Raw MAC in big-endian byte order
* @param len Length, must be >= 6 or result is zero
*/
inline void setTo(const void *bits,unsigned int len)
- throw()
{
if (len < 6) {
_m = 0ULL;
@@ -96,7 +97,6 @@ public:
* @param len Length of buffer, must be >= 6 or nothing is copied
*/
inline void copyTo(void *buf,unsigned int len) const
- throw()
{
if (len < 6)
return;
@@ -116,7 +116,6 @@ public:
*/
template<unsigned int C>
inline void appendTo(Buffer<C> &b) const
- throw(std::out_of_range)
{
unsigned char *p = (unsigned char *)b.appendField(6);
*(p++) = (unsigned char)((_m >> 40) & 0xff);
@@ -130,48 +129,17 @@ public:
/**
* @return True if this is broadcast (all 0xff)
*/
- inline bool isBroadcast() const throw() { return (_m == 0xffffffffffffULL); }
+ inline bool isBroadcast() const { return (_m == 0xffffffffffffULL); }
/**
* @return True if this is a multicast MAC
*/
- inline bool isMulticast() const throw() { return ((_m & 0x010000000000ULL) != 0ULL); }
+ inline bool isMulticast() const { return ((_m & 0x010000000000ULL) != 0ULL); }
/**
* @param True if this is a locally-administered MAC
*/
- inline bool isLocallyAdministered() const throw() { return ((_m & 0x020000000000ULL) != 0ULL); }
-
- /**
- * @param s Hex MAC, with or without : delimiters
- */
- inline void fromString(const char *s)
- {
- char tmp[8];
- for(int i=0;i<6;++i)
- tmp[i] = (char)0;
- Utils::unhex(s,tmp,6);
- setTo(tmp,6);
- }
-
- /**
- * @return MAC address in standard :-delimited hex format
- */
- inline std::string toString() const
- {
- char tmp[24];
- toString(tmp,sizeof(tmp));
- return std::string(tmp);
- }
-
- /**
- * @param buf Buffer to contain human-readable MAC
- * @param len Length of buffer
- */
- inline void toString(char *buf,unsigned int len) const
- {
- Utils::snprintf(buf,len,"%.2x:%.2x:%.2x:%.2x:%.2x:%.2x",(int)(*this)[0],(int)(*this)[1],(int)(*this)[2],(int)(*this)[3],(int)(*this)[4],(int)(*this)[5]);
- }
+ inline bool isLocallyAdministered() const { return ((_m & 0x020000000000ULL) != 0ULL); }
/**
* Set this MAC to a MAC derived from an address and a network ID
@@ -180,7 +148,6 @@ public:
* @param nwid 64-bit network ID
*/
inline void fromAddress(const Address &ztaddr,uint64_t nwid)
- throw()
{
uint64_t m = ((uint64_t)firstOctetForNetwork(nwid)) << 40;
m |= ztaddr.toInt(); // a is 40 bits
@@ -200,7 +167,6 @@ public:
* @param nwid Network ID
*/
inline Address toAddress(uint64_t nwid) const
- throw()
{
uint64_t a = _m & 0xffffffffffULL; // least significant 40 bits of MAC are formed from address
a ^= ((nwid >> 8) & 0xff) << 32; // ... XORed with bits 8-48 of the nwid in little-endian byte order, so unmask it
@@ -216,7 +182,6 @@ public:
* @return First octet of MAC for this network
*/
static inline unsigned char firstOctetForNetwork(uint64_t nwid)
- throw()
{
unsigned char a = ((unsigned char)(nwid & 0xfe) | 0x02); // locally administered, not multicast, from LSB of network ID
return ((a == 0x52) ? 0x32 : a); // blacklist 0x52 since it's used by KVM, libvirt, and other popular virtualization engines... seems de-facto standard on Linux
@@ -226,34 +191,55 @@ public:
* @param i Value from 0 to 5 (inclusive)
* @return Byte at said position (address interpreted in big-endian order)
*/
- inline unsigned char operator[](unsigned int i) const throw() { return (unsigned char)((_m >> (40 - (i * 8))) & 0xff); }
+ inline unsigned char operator[](unsigned int i) const { return (unsigned char)((_m >> (40 - (i * 8))) & 0xff); }
/**
* @return 6, which is the number of bytes in a MAC, for container compliance
*/
- inline unsigned int size() const throw() { return 6; }
+ inline unsigned int size() const { return 6; }
- inline unsigned long hashCode() const throw() { return (unsigned long)_m; }
+ inline unsigned long hashCode() const { return (unsigned long)_m; }
+
+ inline char *toString(char buf[18]) const
+ {
+ buf[0] = Utils::HEXCHARS[(_m >> 44) & 0xf];
+ buf[1] = Utils::HEXCHARS[(_m >> 40) & 0xf];
+ buf[2] = ':';
+ buf[3] = Utils::HEXCHARS[(_m >> 36) & 0xf];
+ buf[4] = Utils::HEXCHARS[(_m >> 32) & 0xf];
+ buf[5] = ':';
+ buf[6] = Utils::HEXCHARS[(_m >> 28) & 0xf];
+ buf[7] = Utils::HEXCHARS[(_m >> 24) & 0xf];
+ buf[8] = ':';
+ buf[9] = Utils::HEXCHARS[(_m >> 20) & 0xf];
+ buf[10] = Utils::HEXCHARS[(_m >> 16) & 0xf];
+ buf[11] = ':';
+ buf[12] = Utils::HEXCHARS[(_m >> 12) & 0xf];
+ buf[13] = Utils::HEXCHARS[(_m >> 8) & 0xf];
+ buf[14] = ':';
+ buf[15] = Utils::HEXCHARS[(_m >> 4) & 0xf];
+ buf[16] = Utils::HEXCHARS[_m & 0xf];
+ buf[17] = (char)0;
+ return buf;
+ }
inline MAC &operator=(const MAC &m)
- throw()
{
_m = m._m;
return *this;
}
inline MAC &operator=(const uint64_t m)
- throw()
{
_m = m;
return *this;
}
- inline bool operator==(const MAC &m) const throw() { return (_m == m._m); }
- inline bool operator!=(const MAC &m) const throw() { return (_m != m._m); }
- inline bool operator<(const MAC &m) const throw() { return (_m < m._m); }
- inline bool operator<=(const MAC &m) const throw() { return (_m <= m._m); }
- inline bool operator>(const MAC &m) const throw() { return (_m > m._m); }
- inline bool operator>=(const MAC &m) const throw() { return (_m >= m._m); }
+ inline bool operator==(const MAC &m) const { return (_m == m._m); }
+ inline bool operator!=(const MAC &m) const { return (_m != m._m); }
+ inline bool operator<(const MAC &m) const { return (_m < m._m); }
+ inline bool operator<=(const MAC &m) const { return (_m <= m._m); }
+ inline bool operator>(const MAC &m) const { return (_m > m._m); }
+ inline bool operator>=(const MAC &m) const { return (_m >= m._m); }
private:
uint64_t _m;
diff --git a/node/Membership.cpp b/node/Membership.cpp
new file mode 100644
index 00000000..affe7a71
--- /dev/null
+++ b/node/Membership.cpp
@@ -0,0 +1,237 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
+ */
+
+#include <algorithm>
+
+#include "Membership.hpp"
+#include "RuntimeEnvironment.hpp"
+#include "Peer.hpp"
+#include "Topology.hpp"
+#include "Switch.hpp"
+#include "Packet.hpp"
+#include "Node.hpp"
+#include "Trace.hpp"
+
+#define ZT_CREDENTIAL_PUSH_EVERY (ZT_NETWORK_AUTOCONF_DELAY / 3)
+
+namespace ZeroTier {
+
+Membership::Membership() :
+ _lastUpdatedMulticast(0),
+ _lastPushedCom(0),
+ _comRevocationThreshold(0),
+ _revocations(4),
+ _remoteTags(4),
+ _remoteCaps(4),
+ _remoteCoos(4)
+{
+ resetPushState();
+}
+
+void Membership::pushCredentials(const RuntimeEnvironment *RR,void *tPtr,const int64_t now,const Address &peerAddress,const NetworkConfig &nconf,int localCapabilityIndex,const bool force)
+{
+ bool sendCom = ( (nconf.com) && ( ((now - _lastPushedCom) >= ZT_CREDENTIAL_PUSH_EVERY) || (force) ) );
+
+ const Capability *sendCap;
+ if (localCapabilityIndex >= 0) {
+ sendCap = &(nconf.capabilities[localCapabilityIndex]);
+ if ( ((now - _localCredLastPushed.cap[localCapabilityIndex]) >= ZT_CREDENTIAL_PUSH_EVERY) || (force) )
+ _localCredLastPushed.cap[localCapabilityIndex] = now;
+ else sendCap = (const Capability *)0;
+ } else sendCap = (const Capability *)0;
+
+ const Tag *sendTags[ZT_MAX_NETWORK_TAGS];
+ unsigned int sendTagCount = 0;
+ for(unsigned int t=0;t<nconf.tagCount;++t) {
+ if ( ((now - _localCredLastPushed.tag[t]) >= ZT_CREDENTIAL_PUSH_EVERY) || (force) ) {
+ _localCredLastPushed.tag[t] = now;
+ sendTags[sendTagCount++] = &(nconf.tags[t]);
+ }
+ }
+
+ const CertificateOfOwnership *sendCoos[ZT_MAX_CERTIFICATES_OF_OWNERSHIP];
+ unsigned int sendCooCount = 0;
+ for(unsigned int c=0;c<nconf.certificateOfOwnershipCount;++c) {
+ if ( ((now - _localCredLastPushed.coo[c]) >= ZT_CREDENTIAL_PUSH_EVERY) || (force) ) {
+ _localCredLastPushed.coo[c] = now;
+ sendCoos[sendCooCount++] = &(nconf.certificatesOfOwnership[c]);
+ }
+ }
+
+ unsigned int tagPtr = 0;
+ unsigned int cooPtr = 0;
+ while ((tagPtr < sendTagCount)||(cooPtr < sendCooCount)||(sendCom)||(sendCap)) {
+ Packet outp(peerAddress,RR->identity.address(),Packet::VERB_NETWORK_CREDENTIALS);
+
+ if (sendCom) {
+ sendCom = false;
+ nconf.com.serialize(outp);
+ _lastPushedCom = now;
+ }
+ outp.append((uint8_t)0x00);
+
+ if (sendCap) {
+ outp.append((uint16_t)1);
+ sendCap->serialize(outp);
+ sendCap = (const Capability *)0;
+ } else outp.append((uint16_t)0);
+
+ const unsigned int tagCountAt = outp.size();
+ outp.addSize(2);
+ unsigned int thisPacketTagCount = 0;
+ while ((tagPtr < sendTagCount)&&((outp.size() + sizeof(Tag) + 16) < ZT_PROTO_MAX_PACKET_LENGTH)) {
+ sendTags[tagPtr++]->serialize(outp);
+ ++thisPacketTagCount;
+ }
+ outp.setAt(tagCountAt,(uint16_t)thisPacketTagCount);
+
+ // No revocations, these propagate differently
+ outp.append((uint16_t)0);
+
+ const unsigned int cooCountAt = outp.size();
+ outp.addSize(2);
+ unsigned int thisPacketCooCount = 0;
+ while ((cooPtr < sendCooCount)&&((outp.size() + sizeof(CertificateOfOwnership) + 16) < ZT_PROTO_MAX_PACKET_LENGTH)) {
+ sendCoos[cooPtr++]->serialize(outp);
+ ++thisPacketCooCount;
+ }
+ outp.setAt(cooCountAt,(uint16_t)thisPacketCooCount);
+
+ outp.compress();
+ RR->sw->send(tPtr,outp,true);
+ }
+}
+
+Membership::AddCredentialResult Membership::addCredential(const RuntimeEnvironment *RR,void *tPtr,const NetworkConfig &nconf,const CertificateOfMembership &com)
+{
+ const int64_t newts = com.timestamp();
+ if (newts <= _comRevocationThreshold) {
+ RR->t->credentialRejected(tPtr,com,"revoked");
+ return ADD_REJECTED;
+ }
+
+ const int64_t oldts = _com.timestamp();
+ if (newts < oldts) {
+ RR->t->credentialRejected(tPtr,com,"old");
+ return ADD_REJECTED;
+ }
+ if ((newts == oldts)&&(_com == com))
+ return ADD_ACCEPTED_REDUNDANT;
+
+ switch(com.verify(RR,tPtr)) {
+ default:
+ RR->t->credentialRejected(tPtr,com,"invalid");
+ return ADD_REJECTED;
+ case 0:
+ _com = com;
+ return ADD_ACCEPTED_NEW;
+ case 1:
+ return ADD_DEFERRED_FOR_WHOIS;
+ }
+}
+
+// Template out addCredential() for many cred types to avoid copypasta
+template<typename C>
+static Membership::AddCredentialResult _addCredImpl(Hashtable<uint32_t,C> &remoteCreds,const Hashtable<uint64_t,int64_t> &revocations,const RuntimeEnvironment *RR,void *tPtr,const NetworkConfig &nconf,const C &cred)
+{
+ C *rc = remoteCreds.get(cred.id());
+ if (rc) {
+ if (rc->timestamp() > cred.timestamp()) {
+ RR->t->credentialRejected(tPtr,cred,"old");
+ return Membership::ADD_REJECTED;
+ }
+ if (*rc == cred)
+ return Membership::ADD_ACCEPTED_REDUNDANT;
+ }
+
+ const int64_t *const rt = revocations.get(Membership::credentialKey(C::credentialType(),cred.id()));
+ if ((rt)&&(*rt >= cred.timestamp())) {
+ RR->t->credentialRejected(tPtr,cred,"revoked");
+ return Membership::ADD_REJECTED;
+ }
+
+ switch(cred.verify(RR,tPtr)) {
+ default:
+ RR->t->credentialRejected(tPtr,cred,"invalid");
+ return Membership::ADD_REJECTED;
+ case 0:
+ if (!rc)
+ rc = &(remoteCreds[cred.id()]);
+ *rc = cred;
+ return Membership::ADD_ACCEPTED_NEW;
+ case 1:
+ return Membership::ADD_DEFERRED_FOR_WHOIS;
+ }
+}
+
+Membership::AddCredentialResult Membership::addCredential(const RuntimeEnvironment *RR,void *tPtr,const NetworkConfig &nconf,const Tag &tag) { return _addCredImpl<Tag>(_remoteTags,_revocations,RR,tPtr,nconf,tag); }
+Membership::AddCredentialResult Membership::addCredential(const RuntimeEnvironment *RR,void *tPtr,const NetworkConfig &nconf,const Capability &cap) { return _addCredImpl<Capability>(_remoteCaps,_revocations,RR,tPtr,nconf,cap); }
+Membership::AddCredentialResult Membership::addCredential(const RuntimeEnvironment *RR,void *tPtr,const NetworkConfig &nconf,const CertificateOfOwnership &coo) { return _addCredImpl<CertificateOfOwnership>(_remoteCoos,_revocations,RR,tPtr,nconf,coo); }
+
+Membership::AddCredentialResult Membership::addCredential(const RuntimeEnvironment *RR,void *tPtr,const NetworkConfig &nconf,const Revocation &rev)
+{
+ int64_t *rt;
+ switch(rev.verify(RR,tPtr)) {
+ default:
+ RR->t->credentialRejected(tPtr,rev,"invalid");
+ return ADD_REJECTED;
+ case 0: {
+ const Credential::Type ct = rev.type();
+ switch(ct) {
+ case Credential::CREDENTIAL_TYPE_COM:
+ if (rev.threshold() > _comRevocationThreshold) {
+ _comRevocationThreshold = rev.threshold();
+ return ADD_ACCEPTED_NEW;
+ }
+ return ADD_ACCEPTED_REDUNDANT;
+ case Credential::CREDENTIAL_TYPE_CAPABILITY:
+ case Credential::CREDENTIAL_TYPE_TAG:
+ case Credential::CREDENTIAL_TYPE_COO:
+ rt = &(_revocations[credentialKey(ct,rev.credentialId())]);
+ if (*rt < rev.threshold()) {
+ *rt = rev.threshold();
+ _comRevocationThreshold = rev.threshold();
+ return ADD_ACCEPTED_NEW;
+ }
+ return ADD_ACCEPTED_REDUNDANT;
+ default:
+ RR->t->credentialRejected(tPtr,rev,"invalid");
+ return ADD_REJECTED;
+ }
+ }
+ case 1:
+ return ADD_DEFERRED_FOR_WHOIS;
+ }
+}
+
+void Membership::clean(const int64_t now,const NetworkConfig &nconf)
+{
+ _cleanCredImpl<Tag>(nconf,_remoteTags);
+ _cleanCredImpl<Capability>(nconf,_remoteCaps);
+ _cleanCredImpl<CertificateOfOwnership>(nconf,_remoteCoos);
+}
+
+} // namespace ZeroTier
diff --git a/node/Membership.hpp b/node/Membership.hpp
new file mode 100644
index 00000000..ad0bb73e
--- /dev/null
+++ b/node/Membership.hpp
@@ -0,0 +1,285 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
+ */
+
+#ifndef ZT_MEMBERSHIP_HPP
+#define ZT_MEMBERSHIP_HPP
+
+#include <stdint.h>
+
+#include "Constants.hpp"
+#include "../include/ZeroTierOne.h"
+#include "Credential.hpp"
+#include "Hashtable.hpp"
+#include "CertificateOfMembership.hpp"
+#include "Capability.hpp"
+#include "Tag.hpp"
+#include "Revocation.hpp"
+#include "NetworkConfig.hpp"
+
+#define ZT_MEMBERSHIP_CRED_ID_UNUSED 0xffffffffffffffffULL
+
+namespace ZeroTier {
+
+class RuntimeEnvironment;
+class Network;
+
+/**
+ * A container for certificates of membership and other network credentials
+ *
+ * This is essentially a relational join between Peer and Network.
+ *
+ * This class is not thread safe. It must be locked externally.
+ */
+class Membership
+{
+public:
+ enum AddCredentialResult
+ {
+ ADD_REJECTED,
+ ADD_ACCEPTED_NEW,
+ ADD_ACCEPTED_REDUNDANT,
+ ADD_DEFERRED_FOR_WHOIS
+ };
+
+ Membership();
+
+ /**
+ * Send COM and other credentials to this peer if needed
+ *
+ * This checks last pushed times for our COM and for other credentials and
+ * sends VERB_NETWORK_CREDENTIALS if the recipient might need them.
+ *
+ * @param RR Runtime environment
+ * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
+ * @param now Current time
+ * @param peerAddress Address of member peer (the one that this Membership describes)
+ * @param nconf My network config
+ * @param localCapabilityIndex Index of local capability to include (in nconf.capabilities[]) or -1 if none
+ * @param force If true, send objects regardless of last push time
+ */
+ void pushCredentials(const RuntimeEnvironment *RR,void *tPtr,const int64_t now,const Address &peerAddress,const NetworkConfig &nconf,int localCapabilityIndex,const bool force);
+
+ /**
+ * Check whether we should push MULTICAST_LIKEs to this peer, and update last sent time if true
+ *
+ * @param now Current time
+ * @return True if we should update multicasts
+ */
+ inline bool multicastLikeGate(const int64_t now)
+ {
+ if ((now - _lastUpdatedMulticast) >= ZT_MULTICAST_ANNOUNCE_PERIOD) {
+ _lastUpdatedMulticast = now;
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Check whether the peer represented by this Membership should be allowed on this network at all
+ *
+ * @param nconf Our network config
+ * @return True if this peer is allowed on this network at all
+ */
+ inline bool isAllowedOnNetwork(const NetworkConfig &nconf) const
+ {
+ if (nconf.isPublic()) return true;
+ if (_com.timestamp() <= _comRevocationThreshold) return false;
+ return nconf.com.agreesWith(_com);
+ }
+
+ inline bool recentlyAssociated(const int64_t now) const
+ {
+ return ((_com)&&((now - _com.timestamp()) < ZT_PEER_ACTIVITY_TIMEOUT));
+ }
+
+ /**
+ * Check whether the peer represented by this Membership owns a given resource
+ *
+ * @tparam Type of resource: InetAddress or MAC
+ * @param nconf Our network config
+ * @param r Resource to check
+ * @return True if this peer has a certificate of ownership for the given resource
+ */
+ template<typename T>
+ inline bool hasCertificateOfOwnershipFor(const NetworkConfig &nconf,const T &r) const
+ {
+ uint32_t *k = (uint32_t *)0;
+ CertificateOfOwnership *v = (CertificateOfOwnership *)0;
+ Hashtable< uint32_t,CertificateOfOwnership >::Iterator i(*(const_cast< Hashtable< uint32_t,CertificateOfOwnership> *>(&_remoteCoos)));
+ while (i.next(k,v)) {
+ if (_isCredentialTimestampValid(nconf,*v)&&(v->owns(r)))
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Get a remote member's tag (if we have it)
+ *
+ * @param nconf Network configuration
+ * @param id Tag ID
+ * @return Pointer to tag or NULL if not found
+ */
+ inline const Tag *getTag(const NetworkConfig &nconf,const uint32_t id) const
+ {
+ const Tag *const t = _remoteTags.get(id);
+ return (((t)&&(_isCredentialTimestampValid(nconf,*t))) ? t : (Tag *)0);
+ }
+
+ /**
+ * Validate and add a credential if signature is okay and it's otherwise good
+ */
+ AddCredentialResult addCredential(const RuntimeEnvironment *RR,void *tPtr,const NetworkConfig &nconf,const CertificateOfMembership &com);
+
+ /**
+ * Validate and add a credential if signature is okay and it's otherwise good
+ */
+ AddCredentialResult addCredential(const RuntimeEnvironment *RR,void *tPtr,const NetworkConfig &nconf,const Tag &tag);
+
+ /**
+ * Validate and add a credential if signature is okay and it's otherwise good
+ */
+ AddCredentialResult addCredential(const RuntimeEnvironment *RR,void *tPtr,const NetworkConfig &nconf,const Capability &cap);
+
+ /**
+ * Validate and add a credential if signature is okay and it's otherwise good
+ */
+ AddCredentialResult addCredential(const RuntimeEnvironment *RR,void *tPtr,const NetworkConfig &nconf,const CertificateOfOwnership &coo);
+
+ /**
+ * Validate and add a credential if signature is okay and it's otherwise good
+ */
+ AddCredentialResult addCredential(const RuntimeEnvironment *RR,void *tPtr,const NetworkConfig &nconf,const Revocation &rev);
+
+ /**
+ * Clean internal databases of stale entries
+ *
+ * @param now Current time
+ * @param nconf Current network configuration
+ */
+ void clean(const int64_t now,const NetworkConfig &nconf);
+
+ /**
+ * Reset last pushed time for local credentials
+ *
+ * This is done when we update our network configuration and our credentials have changed
+ */
+ inline void resetPushState()
+ {
+ _lastPushedCom = 0;
+ memset(&_localCredLastPushed,0,sizeof(_localCredLastPushed));
+ }
+
+ /**
+ * Generates a key for the internal use in indexing credentials by type and credential ID
+ */
+ static uint64_t credentialKey(const Credential::Type &t,const uint32_t i) { return (((uint64_t)t << 32) | (uint64_t)i); }
+
+private:
+ template<typename C>
+ inline bool _isCredentialTimestampValid(const NetworkConfig &nconf,const C &remoteCredential) const
+ {
+ const int64_t ts = remoteCredential.timestamp();
+ if (((ts >= nconf.timestamp) ? (ts - nconf.timestamp) : (nconf.timestamp - ts)) <= nconf.credentialTimeMaxDelta) {
+ const int64_t *threshold = _revocations.get(credentialKey(C::credentialType(),remoteCredential.id()));
+ return ((!threshold)||(ts > *threshold));
+ }
+ return false;
+ }
+
+ template<typename C>
+ void _cleanCredImpl(const NetworkConfig &nconf,Hashtable<uint32_t,C> &remoteCreds)
+ {
+ uint32_t *k = (uint32_t *)0;
+ C *v = (C *)0;
+ typename Hashtable<uint32_t,C>::Iterator i(remoteCreds);
+ while (i.next(k,v)) {
+ if (!_isCredentialTimestampValid(nconf,*v))
+ remoteCreds.erase(*k);
+ }
+ }
+
+ // Last time we pushed MULTICAST_LIKE(s)
+ int64_t _lastUpdatedMulticast;
+
+ // Last time we pushed our COM to this peer
+ int64_t _lastPushedCom;
+
+ // Revocation threshold for COM or 0 if none
+ int64_t _comRevocationThreshold;
+
+ // Remote member's latest network COM
+ CertificateOfMembership _com;
+
+ // Revocations by credentialKey()
+ Hashtable< uint64_t,int64_t > _revocations;
+
+ // Remote credentials that we have received from this member (and that are valid)
+ Hashtable< uint32_t,Tag > _remoteTags;
+ Hashtable< uint32_t,Capability > _remoteCaps;
+ Hashtable< uint32_t,CertificateOfOwnership > _remoteCoos;
+
+ // Time we last pushed our local credentials to this member
+ struct {
+ uint64_t tag[ZT_MAX_NETWORK_TAGS];
+ uint64_t cap[ZT_MAX_NETWORK_CAPABILITIES];
+ uint64_t coo[ZT_MAX_CERTIFICATES_OF_OWNERSHIP];
+ } _localCredLastPushed;
+
+public:
+ class CapabilityIterator
+ {
+ public:
+ CapabilityIterator(Membership &m,const NetworkConfig &nconf) :
+ _hti(m._remoteCaps),
+ _k((uint32_t *)0),
+ _c((Capability *)0),
+ _m(m),
+ _nconf(nconf)
+ {
+ }
+
+ inline Capability *next()
+ {
+ while (_hti.next(_k,_c)) {
+ if (_m._isCredentialTimestampValid(_nconf,*_c))
+ return _c;
+ }
+ return (Capability *)0;
+ }
+
+ private:
+ Hashtable< uint32_t,Capability >::Iterator _hti;
+ uint32_t *_k;
+ Capability *_c;
+ Membership &_m;
+ const NetworkConfig &_nconf;
+ };
+};
+
+} // namespace ZeroTier
+
+#endif
diff --git a/node/MulticastGroup.hpp b/node/MulticastGroup.hpp
index dbf38998..0f4a621e 100644
--- a/node/MulticastGroup.hpp
+++ b/node/MulticastGroup.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_MULTICASTGROUP_HPP
@@ -21,8 +29,6 @@
#include <stdint.h>
-#include <string>
-
#include "MAC.hpp"
#include "InetAddress.hpp"
@@ -46,30 +52,18 @@ namespace ZeroTier {
class MulticastGroup
{
public:
- MulticastGroup()
- throw() :
+ MulticastGroup() :
_mac(),
_adi(0)
{
}
- MulticastGroup(const MAC &m,uint32_t a)
- throw() :
+ MulticastGroup(const MAC &m,uint32_t a) :
_mac(m),
_adi(a)
{
}
- MulticastGroup(const char *s)
- {
- fromString(s);
- }
-
- MulticastGroup(const std::string &s)
- {
- fromString(s.c_str());
- }
-
/**
* Derive the multicast group used for address resolution (ARP/NDP) for an IP
*
@@ -77,7 +71,6 @@ public:
* @return Multicat group for ARP/NDP
*/
static inline MulticastGroup deriveMulticastGroupForAddressResolution(const InetAddress &ip)
- throw()
{
if (ip.isV4()) {
// IPv4 wants broadcast MACs, so we shove the V4 address itself into
@@ -97,46 +90,20 @@ public:
}
/**
- * @return Human readable string representing this group (MAC/ADI in hex)
- */
- inline std::string toString() const
- {
- char buf[64];
- Utils::snprintf(buf,sizeof(buf),"%.2x%.2x%.2x%.2x%.2x%.2x/%.8lx",(unsigned int)_mac[0],(unsigned int)_mac[1],(unsigned int)_mac[2],(unsigned int)_mac[3],(unsigned int)_mac[4],(unsigned int)_mac[5],(unsigned long)_adi);
- return std::string(buf);
- }
-
- /**
- * Parse a human-readable multicast group
- *
- * @param s Multicast group in hex MAC/ADI format
- */
- inline void fromString(const char *s)
- {
- char hex[17];
- unsigned int hexlen = 0;
- while ((*s)&&(*s != '/')&&(hexlen < (sizeof(hex) - 1)))
- hex[hexlen++] = *s;
- hex[hexlen] = (char)0;
- _mac.fromString(hex);
- _adi = (*s == '/') ? (uint32_t)Utils::hexStrToULong(s + 1) : (uint32_t)0;
- }
-
- /**
* @return Multicast address
*/
- inline const MAC &mac() const throw() { return _mac; }
+ inline const MAC &mac() const { return _mac; }
/**
* @return Additional distinguishing information
*/
- inline uint32_t adi() const throw() { return _adi; }
+ inline uint32_t adi() const { return _adi; }
- inline unsigned long hashCode() const throw() { return (_mac.hashCode() ^ (unsigned long)_adi); }
+ inline unsigned long hashCode() const { return (_mac.hashCode() ^ (unsigned long)_adi); }
- inline bool operator==(const MulticastGroup &g) const throw() { return ((_mac == g._mac)&&(_adi == g._adi)); }
- inline bool operator!=(const MulticastGroup &g) const throw() { return ((_mac != g._mac)||(_adi != g._adi)); }
- inline bool operator<(const MulticastGroup &g) const throw()
+ inline bool operator==(const MulticastGroup &g) const { return ((_mac == g._mac)&&(_adi == g._adi)); }
+ inline bool operator!=(const MulticastGroup &g) const { return ((_mac != g._mac)||(_adi != g._adi)); }
+ inline bool operator<(const MulticastGroup &g) const
{
if (_mac < g._mac)
return true;
@@ -144,9 +111,9 @@ public:
return (_adi < g._adi);
return false;
}
- inline bool operator>(const MulticastGroup &g) const throw() { return (g < *this); }
- inline bool operator<=(const MulticastGroup &g) const throw() { return !(g < *this); }
- inline bool operator>=(const MulticastGroup &g) const throw() { return !(*this < g); }
+ inline bool operator>(const MulticastGroup &g) const { return (g < *this); }
+ inline bool operator<=(const MulticastGroup &g) const { return !(g < *this); }
+ inline bool operator>=(const MulticastGroup &g) const { return !(*this < g); }
private:
MAC _mac;
diff --git a/node/Multicaster.cpp b/node/Multicaster.cpp
index e1d4567a..753e4ee0 100644
--- a/node/Multicaster.cpp
+++ b/node/Multicaster.cpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,13 +14,20 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#include <algorithm>
#include "Constants.hpp"
#include "RuntimeEnvironment.hpp"
-#include "SharedPtr.hpp"
#include "Multicaster.hpp"
#include "Topology.hpp"
#include "Switch.hpp"
@@ -29,13 +36,14 @@
#include "C25519.hpp"
#include "CertificateOfMembership.hpp"
#include "Node.hpp"
+#include "Network.hpp"
namespace ZeroTier {
Multicaster::Multicaster(const RuntimeEnvironment *renv) :
RR(renv),
- _groups(1024),
- _groups_m()
+ _groups(256),
+ _gatherAuth(256)
{
}
@@ -43,14 +51,14 @@ Multicaster::~Multicaster()
{
}
-void Multicaster::addMultiple(uint64_t now,uint64_t nwid,const MulticastGroup &mg,const void *addresses,unsigned int count,unsigned int totalKnown)
+void Multicaster::addMultiple(void *tPtr,int64_t now,uint64_t nwid,const MulticastGroup &mg,const void *addresses,unsigned int count,unsigned int totalKnown)
{
const unsigned char *p = (const unsigned char *)addresses;
const unsigned char *e = p + (5 * count);
Mutex::Lock _l(_groups_m);
MulticastGroupStatus &gs = _groups[Multicaster::Key(nwid,mg)];
while (p != e) {
- _add(now,nwid,mg,gs,Address(p,5));
+ _add(tPtr,now,nwid,mg,gs,Address(p,5));
p += 5;
}
}
@@ -103,7 +111,7 @@ unsigned int Multicaster::gather(const Address &queryingPeer,uint64_t nwid,const
// Members are returned in random order so that repeated gather queries
// will return different subsets of a large multicast group.
k = 0;
- while ((added < limit)&&(k < s->members.size())&&((appendTo.size() + ZT_ADDRESS_LENGTH) <= ZT_UDP_DEFAULT_PAYLOAD_MTU)) {
+ while ((added < limit)&&(k < s->members.size())&&((appendTo.size() + ZT_ADDRESS_LENGTH) <= ZT_PROTO_MAX_PACKET_LENGTH)) {
rptr = (unsigned int)RR->node->prng();
restart_member_scan:
@@ -131,8 +139,6 @@ restart_member_scan:
appendTo.setAt(totalAt,(uint32_t)totalKnown);
appendTo.setAt(addedAt,(uint16_t)added);
- //TRACE("..MC Multicaster::gather() attached %u of %u peers for %.16llx/%s (2)",n,(unsigned int)(gs->second.members.size() - skipped),nwid,mg.toString().c_str());
-
return added;
}
@@ -152,23 +158,67 @@ std::vector<Address> Multicaster::getMembers(uint64_t nwid,const MulticastGroup
}
void Multicaster::send(
- const CertificateOfMembership *com,
- unsigned int limit,
- uint64_t now,
- uint64_t nwid,
- const std::vector<Address> &alwaysSendTo,
+ void *tPtr,
+ int64_t now,
+ const SharedPtr<Network> &network,
+ const Address &origin,
const MulticastGroup &mg,
const MAC &src,
unsigned int etherType,
const void *data,
unsigned int len)
{
- unsigned long idxbuf[8194];
+ unsigned long idxbuf[4096];
unsigned long *indexes = idxbuf;
+ // If we're in hub-and-spoke designated multicast replication mode, see if we
+ // have a multicast replicator active. If so, pick the best and send it
+ // there. If we are a multicast replicator or if none are alive, fall back
+ // to sender replication. Note that bridges do not do this since this would
+ // break bridge route learning. This is sort of an edge case limitation of
+ // the current protocol and could be fixed, but fixing it would add more
+ // complexity than the fix is probably worth. Bridges are generally high
+ // bandwidth nodes.
+ if (!network->config().isActiveBridge(RR->identity.address())) {
+ Address multicastReplicators[ZT_MAX_NETWORK_SPECIALISTS];
+ const unsigned int multicastReplicatorCount = network->config().multicastReplicators(multicastReplicators);
+ if (multicastReplicatorCount) {
+ if (std::find(multicastReplicators,multicastReplicators + multicastReplicatorCount,RR->identity.address()) == (multicastReplicators + multicastReplicatorCount)) {
+ SharedPtr<Peer> bestMulticastReplicator;
+ SharedPtr<Path> bestMulticastReplicatorPath;
+ unsigned int bestMulticastReplicatorLatency = 0xffff;
+ for(unsigned int i=0;i<multicastReplicatorCount;++i) {
+ const SharedPtr<Peer> p(RR->topology->getPeerNoCache(multicastReplicators[i]));
+ if ((p)&&(p->isAlive(now))) {
+ const SharedPtr<Path> pp(p->getBestPath(now,false));
+ if ((pp)&&(pp->latency() < bestMulticastReplicatorLatency)) {
+ bestMulticastReplicatorLatency = pp->latency();
+ bestMulticastReplicatorPath = pp;
+ bestMulticastReplicator = p;
+ }
+ }
+ }
+ if (bestMulticastReplicator) {
+ Packet outp(bestMulticastReplicator->address(),RR->identity.address(),Packet::VERB_MULTICAST_FRAME);
+ outp.append((uint64_t)network->id());
+ outp.append((uint8_t)0x0c); // includes source MAC | please replicate
+ ((src) ? src : MAC(RR->identity.address(),network->id())).appendTo(outp);
+ mg.mac().appendTo(outp);
+ outp.append((uint32_t)mg.adi());
+ outp.append((uint16_t)etherType);
+ outp.append(data,len);
+ if (!network->config().disableCompression()) outp.compress();
+ outp.armor(bestMulticastReplicator->key(),true);
+ bestMulticastReplicatorPath->send(RR,tPtr,outp.data(),outp.size(),now);
+ return;
+ }
+ }
+ }
+ }
+
try {
Mutex::Lock _l(_groups_m);
- MulticastGroupStatus &gs = _groups[Multicaster::Key(nwid,mg)];
+ MulticastGroupStatus &gs = _groups[Multicaster::Key(network->id(),mg)];
if (!gs.members.empty()) {
// Allocate a memory buffer if group is monstrous
@@ -186,6 +236,10 @@ void Multicaster::send(
}
}
+ Address activeBridges[ZT_MAX_NETWORK_SPECIALISTS];
+ const unsigned int activeBridgeCount = network->config().activeBridges(activeBridges);
+ const unsigned int limit = network->config().multicastLimit;
+
if (gs.members.size() >= limit) {
// Skip queue if we already have enough members to complete the send operation
OutboundMulticast out;
@@ -193,8 +247,8 @@ void Multicaster::send(
out.init(
RR,
now,
- nwid,
- com,
+ network->id(),
+ network->config().disableCompression(),
limit,
1, // we'll still gather a little from peers to keep multicast list fresh
src,
@@ -205,9 +259,9 @@ void Multicaster::send(
unsigned int count = 0;
- for(std::vector<Address>::const_iterator ast(alwaysSendTo.begin());ast!=alwaysSendTo.end();++ast) {
- if (*ast != RR->identity.address()) {
- out.sendOnly(RR,*ast); // optimization: don't use dedup log if it's a one-pass send
+ for(unsigned int i=0;i<activeBridgeCount;++i) {
+ if ((activeBridges[i] != RR->identity.address())&&(activeBridges[i] != origin)) {
+ out.sendOnly(RR,tPtr,activeBridges[i]); // optimization: don't use dedup log if it's a one-pass send
if (++count >= limit)
break;
}
@@ -215,46 +269,68 @@ void Multicaster::send(
unsigned long idx = 0;
while ((count < limit)&&(idx < gs.members.size())) {
- Address ma(gs.members[indexes[idx++]].address);
- if (std::find(alwaysSendTo.begin(),alwaysSendTo.end(),ma) == alwaysSendTo.end()) {
- out.sendOnly(RR,ma); // optimization: don't use dedup log if it's a one-pass send
+ const Address ma(gs.members[indexes[idx++]].address);
+ if ((std::find(activeBridges,activeBridges + activeBridgeCount,ma) == (activeBridges + activeBridgeCount))&&(ma != origin)) {
+ out.sendOnly(RR,tPtr,ma); // optimization: don't use dedup log if it's a one-pass send
++count;
}
}
} else {
- unsigned int gatherLimit = (limit - (unsigned int)gs.members.size()) + 1;
+ const unsigned int gatherLimit = (limit - (unsigned int)gs.members.size()) + 1;
if ((gs.members.empty())||((now - gs.lastExplicitGather) >= ZT_MULTICAST_EXPLICIT_GATHER_DELAY)) {
gs.lastExplicitGather = now;
- SharedPtr<Peer> explicitGatherPeers[2];
- explicitGatherPeers[0] = RR->topology->getBestRoot();
- const Address nwidc(Network::controllerFor(nwid));
- if (nwidc != RR->identity.address())
- explicitGatherPeers[1] = RR->topology->getPeer(nwidc);
- for(unsigned int k=0;k<2;++k) {
- const SharedPtr<Peer> &p = explicitGatherPeers[k];
- if (!p)
- continue;
- //TRACE(">>MC upstream GATHER up to %u for group %.16llx/%s",gatherLimit,nwid,mg.toString().c_str());
-
- const CertificateOfMembership *com = (CertificateOfMembership *)0;
- {
- SharedPtr<Network> nw(RR->node->network(nwid));
- if ((nw)&&(nw->hasConfig())&&(nw->config().com)&&(nw->config().isPrivate())&&(p->needsOurNetworkMembershipCertificate(nwid,now,true)))
- com = &(nw->config().com);
+
+ Address explicitGatherPeers[16];
+ unsigned int numExplicitGatherPeers = 0;
+
+ SharedPtr<Peer> bestRoot(RR->topology->getUpstreamPeer());
+ if (bestRoot)
+ explicitGatherPeers[numExplicitGatherPeers++] = bestRoot->address();
+
+ explicitGatherPeers[numExplicitGatherPeers++] = network->controller();
+
+ Address ac[ZT_MAX_NETWORK_SPECIALISTS];
+ const unsigned int accnt = network->config().alwaysContactAddresses(ac);
+ unsigned int shuffled[ZT_MAX_NETWORK_SPECIALISTS];
+ for(unsigned int i=0;i<accnt;++i)
+ shuffled[i] = i;
+ for(unsigned int i=0,k=accnt>>1;i<k;++i) {
+ const uint64_t x = RR->node->prng();
+ const unsigned int x1 = shuffled[(unsigned int)x % accnt];
+ const unsigned int x2 = shuffled[(unsigned int)(x >> 32) % accnt];
+ const unsigned int tmp = shuffled[x1];
+ shuffled[x1] = shuffled[x2];
+ shuffled[x2] = tmp;
+ }
+ for(unsigned int i=0;i<accnt;++i) {
+ explicitGatherPeers[numExplicitGatherPeers++] = ac[shuffled[i]];
+ if (numExplicitGatherPeers == 16)
+ break;
+ }
+
+ std::vector<Address> anchors(network->config().anchors());
+ for(std::vector<Address>::const_iterator a(anchors.begin());a!=anchors.end();++a) {
+ if (*a != RR->identity.address()) {
+ explicitGatherPeers[numExplicitGatherPeers++] = *a;
+ if (numExplicitGatherPeers == 16)
+ break;
}
+ }
- Packet outp(p->address(),RR->identity.address(),Packet::VERB_MULTICAST_GATHER);
- outp.append(nwid);
- outp.append((uint8_t)(com ? 0x01 : 0x00));
+ for(unsigned int k=0;k<numExplicitGatherPeers;++k) {
+ const CertificateOfMembership *com = (network) ? ((network->config().com) ? &(network->config().com) : (const CertificateOfMembership *)0) : (const CertificateOfMembership *)0;
+ Packet outp(explicitGatherPeers[k],RR->identity.address(),Packet::VERB_MULTICAST_GATHER);
+ outp.append(network->id());
+ outp.append((uint8_t)((com) ? 0x01 : 0x00));
mg.mac().appendTo(outp);
outp.append((uint32_t)mg.adi());
outp.append((uint32_t)gatherLimit);
if (com)
com->serialize(outp);
- RR->sw->send(outp,true,0);
+ RR->node->expectReplyTo(outp.packetId());
+ RR->sw->send(tPtr,outp,true);
}
- gatherLimit = 0;
}
gs.txQueue.push_back(OutboundMulticast());
@@ -263,8 +339,8 @@ void Multicaster::send(
out.init(
RR,
now,
- nwid,
- com,
+ network->id(),
+ network->config().disableCompression(),
limit,
gatherLimit,
src,
@@ -273,11 +349,14 @@ void Multicaster::send(
data,
len);
+ if (origin)
+ out.logAsSent(origin);
+
unsigned int count = 0;
- for(std::vector<Address>::const_iterator ast(alwaysSendTo.begin());ast!=alwaysSendTo.end();++ast) {
- if (*ast != RR->identity.address()) {
- out.sendAndLog(RR,*ast);
+ for(unsigned int i=0;i<activeBridgeCount;++i) {
+ if (activeBridges[i] != RR->identity.address()) {
+ out.sendAndLog(RR,tPtr,activeBridges[i]);
if (++count >= limit)
break;
}
@@ -286,8 +365,8 @@ void Multicaster::send(
unsigned long idx = 0;
while ((count < limit)&&(idx < gs.members.size())) {
Address ma(gs.members[indexes[idx++]].address);
- if (std::find(alwaysSendTo.begin(),alwaysSendTo.end(),ma) == alwaysSendTo.end()) {
- out.sendAndLog(RR,ma);
+ if (std::find(activeBridges,activeBridges + activeBridgeCount,ma) == (activeBridges + activeBridgeCount)) {
+ out.sendAndLog(RR,tPtr,ma);
++count;
}
}
@@ -299,45 +378,65 @@ void Multicaster::send(
delete [] indexes;
}
-void Multicaster::clean(uint64_t now)
+void Multicaster::clean(int64_t now)
{
- Mutex::Lock _l(_groups_m);
-
- Multicaster::Key *k = (Multicaster::Key *)0;
- MulticastGroupStatus *s = (MulticastGroupStatus *)0;
- Hashtable<Multicaster::Key,MulticastGroupStatus>::Iterator mm(_groups);
- while (mm.next(k,s)) {
- for(std::list<OutboundMulticast>::iterator tx(s->txQueue.begin());tx!=s->txQueue.end();) {
- if ((tx->expired(now))||(tx->atLimit()))
- s->txQueue.erase(tx++);
- else ++tx;
- }
+ {
+ Mutex::Lock _l(_groups_m);
+ Multicaster::Key *k = (Multicaster::Key *)0;
+ MulticastGroupStatus *s = (MulticastGroupStatus *)0;
+ Hashtable<Multicaster::Key,MulticastGroupStatus>::Iterator mm(_groups);
+ while (mm.next(k,s)) {
+ for(std::list<OutboundMulticast>::iterator tx(s->txQueue.begin());tx!=s->txQueue.end();) {
+ if ((tx->expired(now))||(tx->atLimit()))
+ s->txQueue.erase(tx++);
+ else ++tx;
+ }
- unsigned long count = 0;
- {
- std::vector<MulticastGroupMember>::iterator reader(s->members.begin());
- std::vector<MulticastGroupMember>::iterator writer(reader);
- while (reader != s->members.end()) {
- if ((now - reader->timestamp) < ZT_MULTICAST_LIKE_EXPIRE) {
- *writer = *reader;
- ++writer;
- ++count;
+ unsigned long count = 0;
+ {
+ std::vector<MulticastGroupMember>::iterator reader(s->members.begin());
+ std::vector<MulticastGroupMember>::iterator writer(reader);
+ while (reader != s->members.end()) {
+ if ((now - reader->timestamp) < ZT_MULTICAST_LIKE_EXPIRE) {
+ *writer = *reader;
+ ++writer;
+ ++count;
+ }
+ ++reader;
}
- ++reader;
+ }
+
+ if (count) {
+ s->members.resize(count);
+ } else if (s->txQueue.empty()) {
+ _groups.erase(*k);
+ } else {
+ s->members.clear();
}
}
+ }
- if (count) {
- s->members.resize(count);
- } else if (s->txQueue.empty()) {
- _groups.erase(*k);
- } else {
- s->members.clear();
+ {
+ Mutex::Lock _l(_gatherAuth_m);
+ _GatherAuthKey *k = (_GatherAuthKey *)0;
+ uint64_t *ts = NULL;
+ Hashtable<_GatherAuthKey,uint64_t>::Iterator i(_gatherAuth);
+ while (i.next(k,ts)) {
+ if ((now - *ts) >= ZT_MULTICAST_CREDENTIAL_EXPIRATON)
+ _gatherAuth.erase(*k);
}
}
}
-void Multicaster::_add(uint64_t now,uint64_t nwid,const MulticastGroup &mg,MulticastGroupStatus &gs,const Address &member)
+void Multicaster::addCredential(void *tPtr,const CertificateOfMembership &com,bool alreadyValidated)
+{
+ if ((alreadyValidated)||(com.verify(RR,tPtr) == 0)) {
+ Mutex::Lock _l(_gatherAuth_m);
+ _gatherAuth[_GatherAuthKey(com.networkId(),com.issuedTo())] = RR->node->now();
+ }
+}
+
+void Multicaster::_add(void *tPtr,int64_t now,uint64_t nwid,const MulticastGroup &mg,MulticastGroupStatus &gs,const Address &member)
{
// assumes _groups_m is locked
@@ -354,13 +453,11 @@ void Multicaster::_add(uint64_t now,uint64_t nwid,const MulticastGroup &mg,Multi
gs.members.push_back(MulticastGroupMember(member,now));
- //TRACE("..MC %s joined multicast group %.16llx/%s via %s",member.toString().c_str(),nwid,mg.toString().c_str(),((learnedFrom) ? learnedFrom.toString().c_str() : "(direct)"));
-
for(std::list<OutboundMulticast>::iterator tx(gs.txQueue.begin());tx!=gs.txQueue.end();) {
if (tx->atLimit())
gs.txQueue.erase(tx++);
else {
- tx->sendIfNew(RR,member);
+ tx->sendIfNew(RR,tPtr,member);
if (tx->atLimit())
gs.txQueue.erase(tx++);
else ++tx;
diff --git a/node/Multicaster.hpp b/node/Multicaster.hpp
index c43c8d93..e57f81fe 100644
--- a/node/Multicaster.hpp
+++ b/node/Multicaster.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_MULTICASTER_HPP
@@ -34,50 +42,20 @@
#include "OutboundMulticast.hpp"
#include "Utils.hpp"
#include "Mutex.hpp"
-#include "NonCopyable.hpp"
+#include "SharedPtr.hpp"
namespace ZeroTier {
class RuntimeEnvironment;
class CertificateOfMembership;
class Packet;
+class Network;
/**
* Database of known multicast peers within a network
*/
-class Multicaster : NonCopyable
+class Multicaster
{
-private:
- struct Key
- {
- Key() : nwid(0),mg() {}
- Key(uint64_t n,const MulticastGroup &g) : nwid(n),mg(g) {}
-
- uint64_t nwid;
- MulticastGroup mg;
-
- inline bool operator==(const Key &k) const throw() { return ((nwid == k.nwid)&&(mg == k.mg)); }
- inline unsigned long hashCode() const throw() { return (mg.hashCode() ^ (unsigned long)(nwid ^ (nwid >> 32))); }
- };
-
- struct MulticastGroupMember
- {
- MulticastGroupMember() {}
- MulticastGroupMember(const Address &a,uint64_t ts) : address(a),timestamp(ts) {}
-
- Address address;
- uint64_t timestamp; // time of last notification
- };
-
- struct MulticastGroupStatus
- {
- MulticastGroupStatus() : lastExplicitGather(0) {}
-
- uint64_t lastExplicitGather;
- std::list<OutboundMulticast> txQueue; // pending outbound multicasts
- std::vector<MulticastGroupMember> members; // members of this group
- };
-
public:
Multicaster(const RuntimeEnvironment *renv);
~Multicaster();
@@ -90,10 +68,10 @@ public:
* @param mg Multicast group
* @param member New member address
*/
- inline void add(uint64_t now,uint64_t nwid,const MulticastGroup &mg,const Address &member)
+ inline void add(void *tPtr,int64_t now,uint64_t nwid,const MulticastGroup &mg,const Address &member)
{
Mutex::Lock _l(_groups_m);
- _add(now,nwid,mg,_groups[Multicaster::Key(nwid,mg)],member);
+ _add(tPtr,now,nwid,mg,_groups[Multicaster::Key(nwid,mg)],member);
}
/**
@@ -101,6 +79,7 @@ public:
*
* It's up to the caller to check bounds on the array before calling this.
*
+ * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
* @param now Current time
* @param nwid Network ID
* @param mg Multicast group
@@ -108,7 +87,7 @@ public:
* @param count Number of addresses
* @param totalKnown Total number of known addresses as reported by peer
*/
- void addMultiple(uint64_t now,uint64_t nwid,const MulticastGroup &mg,const void *addresses,unsigned int count,unsigned int totalKnown);
+ void addMultiple(void *tPtr,int64_t now,uint64_t nwid,const MulticastGroup &mg,const void *addresses,unsigned int count,unsigned int totalKnown);
/**
* Remove a multicast group member (if present)
@@ -150,11 +129,10 @@ public:
/**
* Send a multicast
*
- * @param com Certificate of membership to include or NULL for none
- * @param limit Multicast limit
+ * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
* @param now Current time
- * @param nwid Network ID
- * @param alwaysSendTo Send to these peers first and even if not included in subscriber list
+ * @param network Network
+ * @param origin Origin of multicast (to not return to sender) or NULL if none
* @param mg Multicast group
* @param src Source Ethernet MAC address or NULL to skip in packet and compute from ZT address (non-bridged mode)
* @param etherType Ethernet frame type
@@ -162,11 +140,10 @@ public:
* @param len Length of packet data
*/
void send(
- const CertificateOfMembership *com,
- unsigned int limit,
- uint64_t now,
- uint64_t nwid,
- const std::vector<Address> &alwaysSendTo,
+ void *tPtr,
+ int64_t now,
+ const SharedPtr<Network> &network,
+ const Address &origin,
const MulticastGroup &mg,
const MAC &src,
unsigned int etherType,
@@ -179,14 +156,84 @@ public:
* @param RR Runtime environment
* @param now Current time
*/
- void clean(uint64_t now);
+ void clean(int64_t now);
+
+ /**
+ * Add an authorization credential
+ *
+ * The Multicaster keeps its own track of when valid credentials of network
+ * membership are presented. This allows it to control MULTICAST_LIKE
+ * GATHER authorization for networks this node does not belong to.
+ *
+ * @param com Certificate of membership
+ * @param alreadyValidated If true, COM has already been checked and found to be valid and signed
+ */
+ void addCredential(void *tPtr,const CertificateOfMembership &com,bool alreadyValidated);
+
+ /**
+ * Check authorization for GATHER and LIKE for non-network-members
+ *
+ * @param a Address of peer
+ * @param nwid Network ID
+ * @param now Current time
+ * @return True if GATHER and LIKE should be allowed
+ */
+ bool cacheAuthorized(const Address &a,const uint64_t nwid,const int64_t now) const
+ {
+ Mutex::Lock _l(_gatherAuth_m);
+ const uint64_t *p = _gatherAuth.get(_GatherAuthKey(nwid,a));
+ return ((p)&&((now - *p) < ZT_MULTICAST_CREDENTIAL_EXPIRATON));
+ }
private:
- void _add(uint64_t now,uint64_t nwid,const MulticastGroup &mg,MulticastGroupStatus &gs,const Address &member);
+ struct Key
+ {
+ Key() : nwid(0),mg() {}
+ Key(uint64_t n,const MulticastGroup &g) : nwid(n),mg(g) {}
+
+ uint64_t nwid;
+ MulticastGroup mg;
+
+ inline bool operator==(const Key &k) const { return ((nwid == k.nwid)&&(mg == k.mg)); }
+ inline unsigned long hashCode() const { return (mg.hashCode() ^ (unsigned long)(nwid ^ (nwid >> 32))); }
+ };
+
+ struct MulticastGroupMember
+ {
+ MulticastGroupMember() {}
+ MulticastGroupMember(const Address &a,uint64_t ts) : address(a),timestamp(ts) {}
+
+ Address address;
+ uint64_t timestamp; // time of last notification
+ };
+
+ struct MulticastGroupStatus
+ {
+ MulticastGroupStatus() : lastExplicitGather(0) {}
+
+ uint64_t lastExplicitGather;
+ std::list<OutboundMulticast> txQueue; // pending outbound multicasts
+ std::vector<MulticastGroupMember> members; // members of this group
+ };
+
+ void _add(void *tPtr,int64_t now,uint64_t nwid,const MulticastGroup &mg,MulticastGroupStatus &gs,const Address &member);
+
+ const RuntimeEnvironment *const RR;
- const RuntimeEnvironment *RR;
Hashtable<Multicaster::Key,MulticastGroupStatus> _groups;
Mutex _groups_m;
+
+ struct _GatherAuthKey
+ {
+ _GatherAuthKey() : member(0),networkId(0) {}
+ _GatherAuthKey(const uint64_t nwid,const Address &a) : member(a.toInt()),networkId(nwid) {}
+ inline unsigned long hashCode() const { return (unsigned long)(member ^ networkId); }
+ inline bool operator==(const _GatherAuthKey &k) const { return ((member == k.member)&&(networkId == k.networkId)); }
+ uint64_t member;
+ uint64_t networkId;
+ };
+ Hashtable< _GatherAuthKey,uint64_t > _gatherAuth;
+ Mutex _gatherAuth_m;
};
} // namespace ZeroTier
diff --git a/node/Mutex.hpp b/node/Mutex.hpp
index d451ede0..a60a00b2 100644
--- a/node/Mutex.hpp
+++ b/node/Mutex.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,74 +14,126 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_MUTEX_HPP
#define ZT_MUTEX_HPP
#include "Constants.hpp"
-#include "NonCopyable.hpp"
#ifdef __UNIX_LIKE__
+#include <stdint.h>
#include <stdlib.h>
#include <pthread.h>
namespace ZeroTier {
-class Mutex : NonCopyable
+#if defined(__GNUC__) && (defined(__amd64) || defined(__amd64__) || defined(__x86_64) || defined(__x86_64__) || defined(__AMD64) || defined(__AMD64__) || defined(_M_X64))
+
+// Inline ticket lock on x64 systems with GCC and CLANG (Mac, Linux) -- this is really fast as long as locking durations are very short
+class Mutex
{
public:
- Mutex()
- throw()
+ Mutex() :
+ nextTicket(0),
+ nowServing(0)
{
- pthread_mutex_init(&_mh,(const pthread_mutexattr_t *)0);
}
- ~Mutex()
+ inline void lock() const
{
- pthread_mutex_destroy(&_mh);
+ const uint16_t myTicket = __sync_fetch_and_add(&(const_cast<Mutex *>(this)->nextTicket),1);
+ while (nowServing != myTicket) {
+ __asm__ __volatile__("rep;nop"::);
+ __asm__ __volatile__("":::"memory");
+ }
}
- inline void lock()
- throw()
+ inline void unlock() const
{
- pthread_mutex_lock(&_mh);
+ ++(const_cast<Mutex *>(this)->nowServing);
}
- inline void unlock()
- throw()
+ /**
+ * Uses C++ contexts and constructor/destructor to lock/unlock automatically
+ */
+ class Lock
+ {
+ public:
+ Lock(Mutex &m) :
+ _m(&m)
+ {
+ m.lock();
+ }
+
+ Lock(const Mutex &m) :
+ _m(const_cast<Mutex *>(&m))
+ {
+ _m->lock();
+ }
+
+ ~Lock()
+ {
+ _m->unlock();
+ }
+
+ private:
+ Mutex *const _m;
+ };
+
+private:
+ Mutex(const Mutex &) {}
+ const Mutex &operator=(const Mutex &) { return *this; }
+
+ uint16_t nextTicket;
+ uint16_t nowServing;
+};
+
+#else
+
+// libpthread based mutex lock
+class Mutex
+{
+public:
+ Mutex()
{
- pthread_mutex_unlock(&_mh);
+ pthread_mutex_init(&_mh,(const pthread_mutexattr_t *)0);
+ }
+
+ ~Mutex()
+ {
+ pthread_mutex_destroy(&_mh);
}
inline void lock() const
- throw()
{
- (const_cast <Mutex *> (this))->lock();
+ pthread_mutex_lock(&((const_cast <Mutex *> (this))->_mh));
}
inline void unlock() const
- throw()
{
- (const_cast <Mutex *> (this))->unlock();
+ pthread_mutex_unlock(&((const_cast <Mutex *> (this))->_mh));
}
- /**
- * Uses C++ contexts and constructor/destructor to lock/unlock automatically
- */
- class Lock : NonCopyable
+ class Lock
{
public:
- Lock(Mutex &m)
- throw() :
+ Lock(Mutex &m) :
_m(&m)
{
m.lock();
}
- Lock(const Mutex &m)
- throw() :
+ Lock(const Mutex &m) :
_m(const_cast<Mutex *>(&m))
{
_m->lock();
@@ -97,9 +149,14 @@ public:
};
private:
+ Mutex(const Mutex &) {}
+ const Mutex &operator=(const Mutex &) { return *this; }
+
pthread_mutex_t _mh;
};
+#endif
+
} // namespace ZeroTier
#endif // Apple / Linux
@@ -111,11 +168,11 @@ private:
namespace ZeroTier {
-class Mutex : NonCopyable
+// Windows critical section based lock
+class Mutex
{
public:
Mutex()
- throw()
{
InitializeCriticalSection(&_cs);
}
@@ -126,41 +183,35 @@ public:
}
inline void lock()
- throw()
{
EnterCriticalSection(&_cs);
}
inline void unlock()
- throw()
{
LeaveCriticalSection(&_cs);
}
inline void lock() const
- throw()
{
(const_cast <Mutex *> (this))->lock();
}
inline void unlock() const
- throw()
{
(const_cast <Mutex *> (this))->unlock();
}
- class Lock : NonCopyable
+ class Lock
{
public:
- Lock(Mutex &m)
- throw() :
+ Lock(Mutex &m) :
_m(&m)
{
m.lock();
}
- Lock(const Mutex &m)
- throw() :
+ Lock(const Mutex &m) :
_m(const_cast<Mutex *>(&m))
{
_m->lock();
@@ -176,6 +227,9 @@ public:
};
private:
+ Mutex(const Mutex &) {}
+ const Mutex &operator=(const Mutex &) { return *this; }
+
CRITICAL_SECTION _cs;
};
diff --git a/node/Network.cpp b/node/Network.cpp
index 25116647..a75d9fd1 100644
--- a/node/Network.cpp
+++ b/node/Network.cpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#include <stdio.h>
@@ -22,24 +30,527 @@
#include <math.h>
#include "Constants.hpp"
+#include "../version.h"
#include "Network.hpp"
#include "RuntimeEnvironment.hpp"
+#include "MAC.hpp"
+#include "Address.hpp"
+#include "InetAddress.hpp"
#include "Switch.hpp"
-#include "Packet.hpp"
#include "Buffer.hpp"
+#include "Packet.hpp"
#include "NetworkController.hpp"
#include "Node.hpp"
+#include "Peer.hpp"
+#include "Trace.hpp"
-#include "../version.h"
+#include <set>
namespace ZeroTier {
+namespace {
+
+// Returns true if packet appears valid; pos and proto will be set
+static bool _ipv6GetPayload(const uint8_t *frameData,unsigned int frameLen,unsigned int &pos,unsigned int &proto)
+{
+ if (frameLen < 40)
+ return false;
+ pos = 40;
+ proto = frameData[6];
+ while (pos <= frameLen) {
+ switch(proto) {
+ case 0: // hop-by-hop options
+ case 43: // routing
+ case 60: // destination options
+ case 135: // mobility options
+ if ((pos + 8) > frameLen)
+ return false; // invalid!
+ proto = frameData[pos];
+ pos += ((unsigned int)frameData[pos + 1] * 8) + 8;
+ break;
+
+ //case 44: // fragment -- we currently can't parse these and they are deprecated in IPv6 anyway
+ //case 50:
+ //case 51: // IPSec ESP and AH -- we have to stop here since this is encrypted stuff
+ default:
+ return true;
+ }
+ }
+ return false; // overflow == invalid
+}
+
+enum _doZtFilterResult
+{
+ DOZTFILTER_NO_MATCH,
+ DOZTFILTER_DROP,
+ DOZTFILTER_REDIRECT,
+ DOZTFILTER_ACCEPT,
+ DOZTFILTER_SUPER_ACCEPT
+};
+
+static _doZtFilterResult _doZtFilter(
+ const RuntimeEnvironment *RR,
+ Trace::RuleResultLog &rrl,
+ const NetworkConfig &nconf,
+ const Membership *membership, // can be NULL
+ const bool inbound,
+ const Address &ztSource,
+ Address &ztDest, // MUTABLE -- is changed on REDIRECT actions
+ const MAC &macSource,
+ const MAC &macDest,
+ const uint8_t *const frameData,
+ const unsigned int frameLen,
+ const unsigned int etherType,
+ const unsigned int vlanId,
+ const ZT_VirtualNetworkRule *rules, // cannot be NULL
+ const unsigned int ruleCount,
+ Address &cc, // MUTABLE -- set to TEE destination if TEE action is taken or left alone otherwise
+ unsigned int &ccLength, // MUTABLE -- set to length of packet payload to TEE
+ bool &ccWatch) // MUTABLE -- set to true for WATCH target as opposed to normal TEE
+{
+ // Set to true if we are a TEE/REDIRECT/WATCH target
+ bool superAccept = false;
+
+ // The default match state for each set of entries starts as 'true' since an
+ // ACTION with no MATCH entries preceding it is always taken.
+ uint8_t thisSetMatches = 1;
+
+ rrl.clear();
+
+ for(unsigned int rn=0;rn<ruleCount;++rn) {
+ const ZT_VirtualNetworkRuleType rt = (ZT_VirtualNetworkRuleType)(rules[rn].t & 0x3f);
+
+ // First check if this is an ACTION
+ if ((unsigned int)rt <= (unsigned int)ZT_NETWORK_RULE_ACTION__MAX_ID) {
+ if (thisSetMatches) {
+ switch(rt) {
+ case ZT_NETWORK_RULE_ACTION_DROP:
+ return DOZTFILTER_DROP;
+
+ case ZT_NETWORK_RULE_ACTION_ACCEPT:
+ return (superAccept ? DOZTFILTER_SUPER_ACCEPT : DOZTFILTER_ACCEPT); // match, accept packet
+
+ // These are initially handled together since preliminary logic is common
+ case ZT_NETWORK_RULE_ACTION_TEE:
+ case ZT_NETWORK_RULE_ACTION_WATCH:
+ case ZT_NETWORK_RULE_ACTION_REDIRECT: {
+ const Address fwdAddr(rules[rn].v.fwd.address);
+ if (fwdAddr == ztSource) {
+ // Skip as no-op since source is target
+ } else if (fwdAddr == RR->identity.address()) {
+ if (inbound) {
+ return DOZTFILTER_SUPER_ACCEPT;
+ } else {
+ }
+ } else if (fwdAddr == ztDest) {
+ } else {
+ if (rt == ZT_NETWORK_RULE_ACTION_REDIRECT) {
+ ztDest = fwdAddr;
+ return DOZTFILTER_REDIRECT;
+ } else {
+ cc = fwdAddr;
+ ccLength = (rules[rn].v.fwd.length != 0) ? ((frameLen < (unsigned int)rules[rn].v.fwd.length) ? frameLen : (unsigned int)rules[rn].v.fwd.length) : frameLen;
+ ccWatch = (rt == ZT_NETWORK_RULE_ACTION_WATCH);
+ }
+ }
+ } continue;
+
+ case ZT_NETWORK_RULE_ACTION_BREAK:
+ return DOZTFILTER_NO_MATCH;
+
+ // Unrecognized ACTIONs are ignored as no-ops
+ default:
+ continue;
+ }
+ } else {
+ // If this is an incoming packet and we are a TEE or REDIRECT target, we should
+ // super-accept if we accept at all. This will cause us to accept redirected or
+ // tee'd packets in spite of MAC and ZT addressing checks.
+ if (inbound) {
+ switch(rt) {
+ case ZT_NETWORK_RULE_ACTION_TEE:
+ case ZT_NETWORK_RULE_ACTION_WATCH:
+ case ZT_NETWORK_RULE_ACTION_REDIRECT:
+ if (RR->identity.address() == rules[rn].v.fwd.address)
+ superAccept = true;
+ break;
+ default:
+ break;
+ }
+ }
+
+ thisSetMatches = 1; // reset to default true for next batch of entries
+ continue;
+ }
+ }
+
+ // Circuit breaker: no need to evaluate an AND if the set's match state
+ // is currently false since anything AND false is false.
+ if ((!thisSetMatches)&&(!(rules[rn].t & 0x40))) {
+ rrl.logSkipped(rn,thisSetMatches);
+ continue;
+ }
+
+ // If this was not an ACTION evaluate next MATCH and update thisSetMatches with (AND [result])
+ uint8_t thisRuleMatches = 0;
+ uint64_t ownershipVerificationMask = 1; // this magic value means it hasn't been computed yet -- this is done lazily the first time it's needed
+ switch(rt) {
+ case ZT_NETWORK_RULE_MATCH_SOURCE_ZEROTIER_ADDRESS:
+ thisRuleMatches = (uint8_t)(rules[rn].v.zt == ztSource.toInt());
+ break;
+ case ZT_NETWORK_RULE_MATCH_DEST_ZEROTIER_ADDRESS:
+ thisRuleMatches = (uint8_t)(rules[rn].v.zt == ztDest.toInt());
+ break;
+ case ZT_NETWORK_RULE_MATCH_VLAN_ID:
+ thisRuleMatches = (uint8_t)(rules[rn].v.vlanId == (uint16_t)vlanId);
+ break;
+ case ZT_NETWORK_RULE_MATCH_VLAN_PCP:
+ // NOT SUPPORTED YET
+ thisRuleMatches = (uint8_t)(rules[rn].v.vlanPcp == 0);
+ break;
+ case ZT_NETWORK_RULE_MATCH_VLAN_DEI:
+ // NOT SUPPORTED YET
+ thisRuleMatches = (uint8_t)(rules[rn].v.vlanDei == 0);
+ break;
+ case ZT_NETWORK_RULE_MATCH_MAC_SOURCE:
+ thisRuleMatches = (uint8_t)(MAC(rules[rn].v.mac,6) == macSource);
+ break;
+ case ZT_NETWORK_RULE_MATCH_MAC_DEST:
+ thisRuleMatches = (uint8_t)(MAC(rules[rn].v.mac,6) == macDest);
+ break;
+ case ZT_NETWORK_RULE_MATCH_IPV4_SOURCE:
+ if ((etherType == ZT_ETHERTYPE_IPV4)&&(frameLen >= 20)) {
+ thisRuleMatches = (uint8_t)(InetAddress((const void *)&(rules[rn].v.ipv4.ip),4,rules[rn].v.ipv4.mask).containsAddress(InetAddress((const void *)(frameData + 12),4,0)));
+ } else {
+ thisRuleMatches = 0;
+ }
+ break;
+ case ZT_NETWORK_RULE_MATCH_IPV4_DEST:
+ if ((etherType == ZT_ETHERTYPE_IPV4)&&(frameLen >= 20)) {
+ thisRuleMatches = (uint8_t)(InetAddress((const void *)&(rules[rn].v.ipv4.ip),4,rules[rn].v.ipv4.mask).containsAddress(InetAddress((const void *)(frameData + 16),4,0)));
+ } else {
+ thisRuleMatches = 0;
+ }
+ break;
+ case ZT_NETWORK_RULE_MATCH_IPV6_SOURCE:
+ if ((etherType == ZT_ETHERTYPE_IPV6)&&(frameLen >= 40)) {
+ thisRuleMatches = (uint8_t)(InetAddress((const void *)rules[rn].v.ipv6.ip,16,rules[rn].v.ipv6.mask).containsAddress(InetAddress((const void *)(frameData + 8),16,0)));
+ } else {
+ thisRuleMatches = 0;
+ }
+ break;
+ case ZT_NETWORK_RULE_MATCH_IPV6_DEST:
+ if ((etherType == ZT_ETHERTYPE_IPV6)&&(frameLen >= 40)) {
+ thisRuleMatches = (uint8_t)(InetAddress((const void *)rules[rn].v.ipv6.ip,16,rules[rn].v.ipv6.mask).containsAddress(InetAddress((const void *)(frameData + 24),16,0)));
+ } else {
+ thisRuleMatches = 0;
+ }
+ break;
+ case ZT_NETWORK_RULE_MATCH_IP_TOS:
+ if ((etherType == ZT_ETHERTYPE_IPV4)&&(frameLen >= 20)) {
+ const uint8_t tosMasked = frameData[1] & rules[rn].v.ipTos.mask;
+ thisRuleMatches = (uint8_t)((tosMasked >= rules[rn].v.ipTos.value[0])&&(tosMasked <= rules[rn].v.ipTos.value[1]));
+ } else if ((etherType == ZT_ETHERTYPE_IPV6)&&(frameLen >= 40)) {
+ const uint8_t tosMasked = (((frameData[0] << 4) & 0xf0) | ((frameData[1] >> 4) & 0x0f)) & rules[rn].v.ipTos.mask;
+ thisRuleMatches = (uint8_t)((tosMasked >= rules[rn].v.ipTos.value[0])&&(tosMasked <= rules[rn].v.ipTos.value[1]));
+ } else {
+ thisRuleMatches = 0;
+ }
+ break;
+ case ZT_NETWORK_RULE_MATCH_IP_PROTOCOL:
+ if ((etherType == ZT_ETHERTYPE_IPV4)&&(frameLen >= 20)) {
+ thisRuleMatches = (uint8_t)(rules[rn].v.ipProtocol == frameData[9]);
+ } else if (etherType == ZT_ETHERTYPE_IPV6) {
+ unsigned int pos = 0,proto = 0;
+ if (_ipv6GetPayload(frameData,frameLen,pos,proto)) {
+ thisRuleMatches = (uint8_t)(rules[rn].v.ipProtocol == (uint8_t)proto);
+ } else {
+ thisRuleMatches = 0;
+ }
+ } else {
+ thisRuleMatches = 0;
+ }
+ break;
+ case ZT_NETWORK_RULE_MATCH_ETHERTYPE:
+ thisRuleMatches = (uint8_t)(rules[rn].v.etherType == (uint16_t)etherType);
+ break;
+ case ZT_NETWORK_RULE_MATCH_ICMP:
+ if ((etherType == ZT_ETHERTYPE_IPV4)&&(frameLen >= 20)) {
+ if (frameData[9] == 0x01) { // IP protocol == ICMP
+ const unsigned int ihl = (frameData[0] & 0xf) * 4;
+ if (frameLen >= (ihl + 2)) {
+ if (rules[rn].v.icmp.type == frameData[ihl]) {
+ if ((rules[rn].v.icmp.flags & 0x01) != 0) {
+ thisRuleMatches = (uint8_t)(frameData[ihl+1] == rules[rn].v.icmp.code);
+ } else {
+ thisRuleMatches = 1;
+ }
+ } else {
+ thisRuleMatches = 0;
+ }
+ } else {
+ thisRuleMatches = 0;
+ }
+ } else {
+ thisRuleMatches = 0;
+ }
+ } else if (etherType == ZT_ETHERTYPE_IPV6) {
+ unsigned int pos = 0,proto = 0;
+ if (_ipv6GetPayload(frameData,frameLen,pos,proto)) {
+ if ((proto == 0x3a)&&(frameLen >= (pos+2))) {
+ if (rules[rn].v.icmp.type == frameData[pos]) {
+ if ((rules[rn].v.icmp.flags & 0x01) != 0) {
+ thisRuleMatches = (uint8_t)(frameData[pos+1] == rules[rn].v.icmp.code);
+ } else {
+ thisRuleMatches = 1;
+ }
+ } else {
+ thisRuleMatches = 0;
+ }
+ } else {
+ thisRuleMatches = 0;
+ }
+ } else {
+ thisRuleMatches = 0;
+ }
+ } else {
+ thisRuleMatches = 0;
+ }
+ break;
+ case ZT_NETWORK_RULE_MATCH_IP_SOURCE_PORT_RANGE:
+ case ZT_NETWORK_RULE_MATCH_IP_DEST_PORT_RANGE:
+ if ((etherType == ZT_ETHERTYPE_IPV4)&&(frameLen >= 20)) {
+ const unsigned int headerLen = 4 * (frameData[0] & 0xf);
+ int p = -1;
+ switch(frameData[9]) { // IP protocol number
+ // All these start with 16-bit source and destination port in that order
+ case 0x06: // TCP
+ case 0x11: // UDP
+ case 0x84: // SCTP
+ case 0x88: // UDPLite
+ if (frameLen > (headerLen + 4)) {
+ unsigned int pos = headerLen + ((rt == ZT_NETWORK_RULE_MATCH_IP_DEST_PORT_RANGE) ? 2 : 0);
+ p = (int)frameData[pos++] << 8;
+ p |= (int)frameData[pos];
+ }
+ break;
+ }
+
+ thisRuleMatches = (p >= 0) ? (uint8_t)((p >= (int)rules[rn].v.port[0])&&(p <= (int)rules[rn].v.port[1])) : (uint8_t)0;
+ } else if (etherType == ZT_ETHERTYPE_IPV6) {
+ unsigned int pos = 0,proto = 0;
+ if (_ipv6GetPayload(frameData,frameLen,pos,proto)) {
+ int p = -1;
+ switch(proto) { // IP protocol number
+ // All these start with 16-bit source and destination port in that order
+ case 0x06: // TCP
+ case 0x11: // UDP
+ case 0x84: // SCTP
+ case 0x88: // UDPLite
+ if (frameLen > (pos + 4)) {
+ if (rt == ZT_NETWORK_RULE_MATCH_IP_DEST_PORT_RANGE) pos += 2;
+ p = (int)frameData[pos++] << 8;
+ p |= (int)frameData[pos];
+ }
+ break;
+ }
+ thisRuleMatches = (p > 0) ? (uint8_t)((p >= (int)rules[rn].v.port[0])&&(p <= (int)rules[rn].v.port[1])) : (uint8_t)0;
+ } else {
+ thisRuleMatches = 0;
+ }
+ } else {
+ thisRuleMatches = 0;
+ }
+ break;
+ case ZT_NETWORK_RULE_MATCH_CHARACTERISTICS: {
+ uint64_t cf = (inbound) ? ZT_RULE_PACKET_CHARACTERISTICS_INBOUND : 0ULL;
+ if (macDest.isMulticast()) cf |= ZT_RULE_PACKET_CHARACTERISTICS_MULTICAST;
+ if (macDest.isBroadcast()) cf |= ZT_RULE_PACKET_CHARACTERISTICS_BROADCAST;
+ if (ownershipVerificationMask == 1) {
+ ownershipVerificationMask = 0;
+ InetAddress src;
+ if ((etherType == ZT_ETHERTYPE_IPV4)&&(frameLen >= 20)) {
+ src.set((const void *)(frameData + 12),4,0);
+ } else if ((etherType == ZT_ETHERTYPE_IPV6)&&(frameLen >= 40)) {
+ // IPv6 NDP requires special handling, since the src and dest IPs in the packet are empty or link-local.
+ if ( (frameLen >= (40 + 8 + 16)) && (frameData[6] == 0x3a) && ((frameData[40] == 0x87)||(frameData[40] == 0x88)) ) {
+ if (frameData[40] == 0x87) {
+ // Neighbor solicitations contain no reliable source address, so we implement a small
+ // hack by considering them authenticated. Otherwise you would pretty much have to do
+ // this manually in the rule set for IPv6 to work at all.
+ ownershipVerificationMask |= ZT_RULE_PACKET_CHARACTERISTICS_SENDER_IP_AUTHENTICATED;
+ } else {
+ // Neighbor advertisements on the other hand can absolutely be authenticated.
+ src.set((const void *)(frameData + 40 + 8),16,0);
+ }
+ } else {
+ // Other IPv6 packets can be handled normally
+ src.set((const void *)(frameData + 8),16,0);
+ }
+ } else if ((etherType == ZT_ETHERTYPE_ARP)&&(frameLen >= 28)) {
+ src.set((const void *)(frameData + 14),4,0);
+ }
+ if (inbound) {
+ if (membership) {
+ if ((src)&&(membership->hasCertificateOfOwnershipFor<InetAddress>(nconf,src)))
+ ownershipVerificationMask |= ZT_RULE_PACKET_CHARACTERISTICS_SENDER_IP_AUTHENTICATED;
+ if (membership->hasCertificateOfOwnershipFor<MAC>(nconf,macSource))
+ ownershipVerificationMask |= ZT_RULE_PACKET_CHARACTERISTICS_SENDER_MAC_AUTHENTICATED;
+ }
+ } else {
+ for(unsigned int i=0;i<nconf.certificateOfOwnershipCount;++i) {
+ if ((src)&&(nconf.certificatesOfOwnership[i].owns(src)))
+ ownershipVerificationMask |= ZT_RULE_PACKET_CHARACTERISTICS_SENDER_IP_AUTHENTICATED;
+ if (nconf.certificatesOfOwnership[i].owns(macSource))
+ ownershipVerificationMask |= ZT_RULE_PACKET_CHARACTERISTICS_SENDER_MAC_AUTHENTICATED;
+ }
+ }
+ }
+ cf |= ownershipVerificationMask;
+ if ((etherType == ZT_ETHERTYPE_IPV4)&&(frameLen >= 20)&&(frameData[9] == 0x06)) {
+ const unsigned int headerLen = 4 * (frameData[0] & 0xf);
+ cf |= (uint64_t)frameData[headerLen + 13];
+ cf |= (((uint64_t)(frameData[headerLen + 12] & 0x0f)) << 8);
+ } else if (etherType == ZT_ETHERTYPE_IPV6) {
+ unsigned int pos = 0,proto = 0;
+ if (_ipv6GetPayload(frameData,frameLen,pos,proto)) {
+ if ((proto == 0x06)&&(frameLen > (pos + 14))) {
+ cf |= (uint64_t)frameData[pos + 13];
+ cf |= (((uint64_t)(frameData[pos + 12] & 0x0f)) << 8);
+ }
+ }
+ }
+ thisRuleMatches = (uint8_t)((cf & rules[rn].v.characteristics) != 0);
+ } break;
+ case ZT_NETWORK_RULE_MATCH_FRAME_SIZE_RANGE:
+ thisRuleMatches = (uint8_t)((frameLen >= (unsigned int)rules[rn].v.frameSize[0])&&(frameLen <= (unsigned int)rules[rn].v.frameSize[1]));
+ break;
+ case ZT_NETWORK_RULE_MATCH_RANDOM:
+ thisRuleMatches = (uint8_t)((uint32_t)(RR->node->prng() & 0xffffffffULL) <= rules[rn].v.randomProbability);
+ break;
+ case ZT_NETWORK_RULE_MATCH_TAGS_DIFFERENCE:
+ case ZT_NETWORK_RULE_MATCH_TAGS_BITWISE_AND:
+ case ZT_NETWORK_RULE_MATCH_TAGS_BITWISE_OR:
+ case ZT_NETWORK_RULE_MATCH_TAGS_BITWISE_XOR:
+ case ZT_NETWORK_RULE_MATCH_TAGS_EQUAL: {
+ const Tag *const localTag = std::lower_bound(&(nconf.tags[0]),&(nconf.tags[nconf.tagCount]),rules[rn].v.tag.id,Tag::IdComparePredicate());
+ if ((localTag != &(nconf.tags[nconf.tagCount]))&&(localTag->id() == rules[rn].v.tag.id)) {
+ const Tag *const remoteTag = ((membership) ? membership->getTag(nconf,rules[rn].v.tag.id) : (const Tag *)0);
+ if (remoteTag) {
+ const uint32_t ltv = localTag->value();
+ const uint32_t rtv = remoteTag->value();
+ if (rt == ZT_NETWORK_RULE_MATCH_TAGS_DIFFERENCE) {
+ const uint32_t diff = (ltv > rtv) ? (ltv - rtv) : (rtv - ltv);
+ thisRuleMatches = (uint8_t)(diff <= rules[rn].v.tag.value);
+ } else if (rt == ZT_NETWORK_RULE_MATCH_TAGS_BITWISE_AND) {
+ thisRuleMatches = (uint8_t)((ltv & rtv) == rules[rn].v.tag.value);
+ } else if (rt == ZT_NETWORK_RULE_MATCH_TAGS_BITWISE_OR) {
+ thisRuleMatches = (uint8_t)((ltv | rtv) == rules[rn].v.tag.value);
+ } else if (rt == ZT_NETWORK_RULE_MATCH_TAGS_BITWISE_XOR) {
+ thisRuleMatches = (uint8_t)((ltv ^ rtv) == rules[rn].v.tag.value);
+ } else if (rt == ZT_NETWORK_RULE_MATCH_TAGS_EQUAL) {
+ thisRuleMatches = (uint8_t)((ltv == rules[rn].v.tag.value)&&(rtv == rules[rn].v.tag.value));
+ } else { // sanity check, can't really happen
+ thisRuleMatches = 0;
+ }
+ } else {
+ if ((inbound)&&(!superAccept)) {
+ thisRuleMatches = 0;
+ } else {
+ // Outbound side is not strict since if we have to match both tags and
+ // we are sending a first packet to a recipient, we probably do not know
+ // about their tags yet. They will filter on inbound and we will filter
+ // once we get their tag. If we are a tee/redirect target we are also
+ // not strict since we likely do not have these tags.
+ thisRuleMatches = 1;
+ }
+ }
+ } else {
+ thisRuleMatches = 0;
+ }
+ } break;
+ case ZT_NETWORK_RULE_MATCH_TAG_SENDER:
+ case ZT_NETWORK_RULE_MATCH_TAG_RECEIVER: {
+ if (superAccept) {
+ thisRuleMatches = 1;
+ } else if ( ((rt == ZT_NETWORK_RULE_MATCH_TAG_SENDER)&&(inbound)) || ((rt == ZT_NETWORK_RULE_MATCH_TAG_RECEIVER)&&(!inbound)) ) {
+ const Tag *const remoteTag = ((membership) ? membership->getTag(nconf,rules[rn].v.tag.id) : (const Tag *)0);
+ if (remoteTag) {
+ thisRuleMatches = (uint8_t)(remoteTag->value() == rules[rn].v.tag.value);
+ } else {
+ if (rt == ZT_NETWORK_RULE_MATCH_TAG_RECEIVER) {
+ // If we are checking the receiver and this is an outbound packet, we
+ // can't be strict since we may not yet know the receiver's tag.
+ thisRuleMatches = 1;
+ } else {
+ thisRuleMatches = 0;
+ }
+ }
+ } else { // sender and outbound or receiver and inbound
+ const Tag *const localTag = std::lower_bound(&(nconf.tags[0]),&(nconf.tags[nconf.tagCount]),rules[rn].v.tag.id,Tag::IdComparePredicate());
+ if ((localTag != &(nconf.tags[nconf.tagCount]))&&(localTag->id() == rules[rn].v.tag.id)) {
+ thisRuleMatches = (uint8_t)(localTag->value() == rules[rn].v.tag.value);
+ } else {
+ thisRuleMatches = 0;
+ }
+ }
+ } break;
+ case ZT_NETWORK_RULE_MATCH_INTEGER_RANGE: {
+ uint64_t integer = 0;
+ const unsigned int bits = (rules[rn].v.intRange.format & 63) + 1;
+ const unsigned int bytes = ((bits + 8 - 1) / 8); // integer ceiling of division by 8
+ if ((rules[rn].v.intRange.format & 0x80) == 0) {
+ // Big-endian
+ unsigned int idx = rules[rn].v.intRange.idx + (8 - bytes);
+ const unsigned int eof = idx + bytes;
+ if (eof <= frameLen) {
+ while (idx < eof) {
+ integer <<= 8;
+ integer |= frameData[idx++];
+ }
+ }
+ integer &= 0xffffffffffffffffULL >> (64 - bits);
+ } else {
+ // Little-endian
+ unsigned int idx = rules[rn].v.intRange.idx;
+ const unsigned int eof = idx + bytes;
+ if (eof <= frameLen) {
+ while (idx < eof) {
+ integer >>= 8;
+ integer |= ((uint64_t)frameData[idx++]) << 56;
+ }
+ }
+ integer >>= (64 - bits);
+ }
+ thisRuleMatches = (uint8_t)((integer >= rules[rn].v.intRange.start)&&(integer <= (rules[rn].v.intRange.start + (uint64_t)rules[rn].v.intRange.end)));
+ } break;
+
+ // The result of an unsupported MATCH is configurable at the network
+ // level via a flag.
+ default:
+ thisRuleMatches = (uint8_t)((nconf.flags & ZT_NETWORKCONFIG_FLAG_RULES_RESULT_OF_UNSUPPORTED_MATCH) != 0);
+ break;
+ }
+
+ rrl.log(rn,thisRuleMatches,thisSetMatches);
+
+ if ((rules[rn].t & 0x40))
+ thisSetMatches |= (thisRuleMatches ^ ((rules[rn].t >> 7) & 1));
+ else thisSetMatches &= (thisRuleMatches ^ ((rules[rn].t >> 7) & 1));
+ }
+
+ return DOZTFILTER_NO_MATCH;
+}
+
+} // anonymous namespace
+
const ZeroTier::MulticastGroup Network::BROADCAST(ZeroTier::MAC(0xffffffffffffULL),0);
-Network::Network(const RuntimeEnvironment *renv,uint64_t nwid,void *uptr) :
+Network::Network(const RuntimeEnvironment *renv,void *tPtr,uint64_t nwid,void *uptr,const NetworkConfig *nconf) :
RR(renv),
_uPtr(uptr),
_id(nwid),
+ _lastAnnouncedMulticastGroupsUpstream(0),
_mac(renv->identity.address(),nwid),
_portInitialized(false),
_lastConfigUpdate(0),
@@ -47,43 +558,42 @@ Network::Network(const RuntimeEnvironment *renv,uint64_t nwid,void *uptr) :
_netconfFailure(NETCONF_FAILURE_NONE),
_portError(0)
{
- char confn[128],mcdbn[128];
- Utils::snprintf(confn,sizeof(confn),"networks.d/%.16llx.conf",_id);
- Utils::snprintf(mcdbn,sizeof(mcdbn),"networks.d/%.16llx.mcerts",_id);
-
- // These files are no longer used, so clean them.
- RR->node->dataStoreDelete(mcdbn);
-
- if (_id == ZT_TEST_NETWORK_ID) {
- applyConfiguration(NetworkConfig::createTestNetworkConfig(RR->identity.address()));
+ for(int i=0;i<ZT_NETWORK_MAX_INCOMING_UPDATES;++i)
+ _incomingConfigChunks[i].ts = 0;
- // Save a one-byte CR to persist membership in the test network
- RR->node->dataStorePut(confn,"\n",1,false);
+ if (nconf) {
+ this->setConfiguration(tPtr,*nconf,false);
+ _lastConfigUpdate = 0; // still want to re-request since it's likely outdated
} else {
- bool gotConf = false;
+ uint64_t tmp[2];
+ tmp[0] = nwid; tmp[1] = 0;
+
+ bool got = false;
+ Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> *dict = new Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY>();
try {
- std::string conf(RR->node->dataStoreGet(confn));
- if (conf.length()) {
- Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> dconf(conf.c_str());
- NetworkConfig nconf;
- if (nconf.fromDictionary(dconf)) {
- this->setConfiguration(nconf,false);
- _lastConfigUpdate = 0; // we still want to re-request a new config from the network
- gotConf = true;
- }
+ int n = RR->node->stateObjectGet(tPtr,ZT_STATE_OBJECT_NETWORK_CONFIG,tmp,dict->unsafeData(),ZT_NETWORKCONFIG_DICT_CAPACITY - 1);
+ if (n > 1) {
+ NetworkConfig *nconf = new NetworkConfig();
+ try {
+ if (nconf->fromDictionary(*dict)) {
+ this->setConfiguration(tPtr,*nconf,false);
+ _lastConfigUpdate = 0; // still want to re-request an update since it's likely outdated
+ got = true;
+ }
+ } catch ( ... ) {}
+ delete nconf;
}
- } catch ( ... ) {} // ignore invalids, we'll re-request
+ } catch ( ... ) {}
+ delete dict;
- if (!gotConf) {
- // Save a one-byte CR to persist membership while we request a real netconf
- RR->node->dataStorePut(confn,"\n",1,false);
- }
+ if (!got)
+ RR->node->stateObjectPut(tPtr,ZT_STATE_OBJECT_NETWORK_CONFIG,tmp,"\n",1);
}
if (!_portInitialized) {
ZT_VirtualNetworkConfig ctmp;
_externalConfig(&ctmp);
- _portError = RR->node->configureVirtualNetworkPort(_id,&_uPtr,ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_UP,&ctmp);
+ _portError = RR->node->configureVirtualNetworkPort(tPtr,_id,&_uPtr,ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_UP,&ctmp);
_portInitialized = true;
}
}
@@ -93,16 +603,260 @@ Network::~Network()
ZT_VirtualNetworkConfig ctmp;
_externalConfig(&ctmp);
- char n[128];
if (_destroyed) {
- RR->node->configureVirtualNetworkPort(_id,&_uPtr,ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_DESTROY,&ctmp);
- Utils::snprintf(n,sizeof(n),"networks.d/%.16llx.conf",_id);
- RR->node->dataStoreDelete(n);
+ // This is done in Node::leave() so we can pass tPtr properly
+ //RR->node->configureVirtualNetworkPort((void *)0,_id,&_uPtr,ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_DESTROY,&ctmp);
} else {
- RR->node->configureVirtualNetworkPort(_id,&_uPtr,ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_DOWN,&ctmp);
+ RR->node->configureVirtualNetworkPort((void *)0,_id,&_uPtr,ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_DOWN,&ctmp);
}
}
+bool Network::filterOutgoingPacket(
+ void *tPtr,
+ const bool noTee,
+ const Address &ztSource,
+ const Address &ztDest,
+ const MAC &macSource,
+ const MAC &macDest,
+ const uint8_t *frameData,
+ const unsigned int frameLen,
+ const unsigned int etherType,
+ const unsigned int vlanId)
+{
+ const int64_t now = RR->node->now();
+ Address ztFinalDest(ztDest);
+ int localCapabilityIndex = -1;
+ int accept = 0;
+ Trace::RuleResultLog rrl,crrl;
+ Address cc;
+ unsigned int ccLength = 0;
+ bool ccWatch = false;
+
+ Mutex::Lock _l(_lock);
+
+ Membership *const membership = (ztDest) ? _memberships.get(ztDest) : (Membership *)0;
+
+ switch(_doZtFilter(RR,rrl,_config,membership,false,ztSource,ztFinalDest,macSource,macDest,frameData,frameLen,etherType,vlanId,_config.rules,_config.ruleCount,cc,ccLength,ccWatch)) {
+
+ case DOZTFILTER_NO_MATCH: {
+ for(unsigned int c=0;c<_config.capabilityCount;++c) {
+ ztFinalDest = ztDest; // sanity check, shouldn't be possible if there was no match
+ Address cc2;
+ unsigned int ccLength2 = 0;
+ bool ccWatch2 = false;
+ switch (_doZtFilter(RR,crrl,_config,membership,false,ztSource,ztFinalDest,macSource,macDest,frameData,frameLen,etherType,vlanId,_config.capabilities[c].rules(),_config.capabilities[c].ruleCount(),cc2,ccLength2,ccWatch2)) {
+ case DOZTFILTER_NO_MATCH:
+ case DOZTFILTER_DROP: // explicit DROP in a capability just terminates its evaluation and is an anti-pattern
+ break;
+
+ case DOZTFILTER_REDIRECT: // interpreted as ACCEPT but ztFinalDest will have been changed in _doZtFilter()
+ case DOZTFILTER_ACCEPT:
+ case DOZTFILTER_SUPER_ACCEPT: // no difference in behavior on outbound side in capabilities
+ localCapabilityIndex = (int)c;
+ accept = 1;
+
+ if ((!noTee)&&(cc2)) {
+ Membership &m2 = _membership(cc2);
+ m2.pushCredentials(RR,tPtr,now,cc2,_config,localCapabilityIndex,false);
+
+ Packet outp(cc2,RR->identity.address(),Packet::VERB_EXT_FRAME);
+ outp.append(_id);
+ outp.append((uint8_t)(ccWatch2 ? 0x16 : 0x02));
+ macDest.appendTo(outp);
+ macSource.appendTo(outp);
+ outp.append((uint16_t)etherType);
+ outp.append(frameData,ccLength2);
+ outp.compress();
+ RR->sw->send(tPtr,outp,true);
+ }
+
+ break;
+ }
+ if (accept)
+ break;
+ }
+ } break;
+
+ case DOZTFILTER_DROP:
+ if (_config.remoteTraceTarget)
+ RR->t->networkFilter(tPtr,*this,rrl,(Trace::RuleResultLog *)0,(Capability *)0,ztSource,ztDest,macSource,macDest,frameData,frameLen,etherType,vlanId,noTee,false,0);
+ return false;
+
+ case DOZTFILTER_REDIRECT: // interpreted as ACCEPT but ztFinalDest will have been changed in _doZtFilter()
+ case DOZTFILTER_ACCEPT:
+ accept = 1;
+ break;
+
+ case DOZTFILTER_SUPER_ACCEPT:
+ accept = 2;
+ break;
+ }
+
+ if (accept) {
+ if (membership)
+ membership->pushCredentials(RR,tPtr,now,ztDest,_config,localCapabilityIndex,false);
+
+ if ((!noTee)&&(cc)) {
+ Membership &m2 = _membership(cc);
+ m2.pushCredentials(RR,tPtr,now,cc,_config,localCapabilityIndex,false);
+
+ Packet outp(cc,RR->identity.address(),Packet::VERB_EXT_FRAME);
+ outp.append(_id);
+ outp.append((uint8_t)(ccWatch ? 0x16 : 0x02));
+ macDest.appendTo(outp);
+ macSource.appendTo(outp);
+ outp.append((uint16_t)etherType);
+ outp.append(frameData,ccLength);
+ outp.compress();
+ RR->sw->send(tPtr,outp,true);
+ }
+
+ if ((ztDest != ztFinalDest)&&(ztFinalDest)) {
+ Membership &m2 = _membership(ztFinalDest);
+ m2.pushCredentials(RR,tPtr,now,ztFinalDest,_config,localCapabilityIndex,false);
+
+ Packet outp(ztFinalDest,RR->identity.address(),Packet::VERB_EXT_FRAME);
+ outp.append(_id);
+ outp.append((uint8_t)0x04);
+ macDest.appendTo(outp);
+ macSource.appendTo(outp);
+ outp.append((uint16_t)etherType);
+ outp.append(frameData,frameLen);
+ outp.compress();
+ RR->sw->send(tPtr,outp,true);
+
+ if (_config.remoteTraceTarget)
+ RR->t->networkFilter(tPtr,*this,rrl,(localCapabilityIndex >= 0) ? &crrl : (Trace::RuleResultLog *)0,(localCapabilityIndex >= 0) ? &(_config.capabilities[localCapabilityIndex]) : (Capability *)0,ztSource,ztDest,macSource,macDest,frameData,frameLen,etherType,vlanId,noTee,false,0);
+ return false; // DROP locally, since we redirected
+ } else {
+ if (_config.remoteTraceTarget)
+ RR->t->networkFilter(tPtr,*this,rrl,(localCapabilityIndex >= 0) ? &crrl : (Trace::RuleResultLog *)0,(localCapabilityIndex >= 0) ? &(_config.capabilities[localCapabilityIndex]) : (Capability *)0,ztSource,ztDest,macSource,macDest,frameData,frameLen,etherType,vlanId,noTee,false,1);
+ return true;
+ }
+ } else {
+ if (_config.remoteTraceTarget)
+ RR->t->networkFilter(tPtr,*this,rrl,(localCapabilityIndex >= 0) ? &crrl : (Trace::RuleResultLog *)0,(localCapabilityIndex >= 0) ? &(_config.capabilities[localCapabilityIndex]) : (Capability *)0,ztSource,ztDest,macSource,macDest,frameData,frameLen,etherType,vlanId,noTee,false,0);
+ return false;
+ }
+}
+
+int Network::filterIncomingPacket(
+ void *tPtr,
+ const SharedPtr<Peer> &sourcePeer,
+ const Address &ztDest,
+ const MAC &macSource,
+ const MAC &macDest,
+ const uint8_t *frameData,
+ const unsigned int frameLen,
+ const unsigned int etherType,
+ const unsigned int vlanId)
+{
+ Address ztFinalDest(ztDest);
+ Trace::RuleResultLog rrl,crrl;
+ int accept = 0;
+ Address cc;
+ unsigned int ccLength = 0;
+ bool ccWatch = false;
+ const Capability *c = (Capability *)0;
+
+ Mutex::Lock _l(_lock);
+
+ Membership &membership = _membership(sourcePeer->address());
+
+ switch (_doZtFilter(RR,rrl,_config,&membership,true,sourcePeer->address(),ztFinalDest,macSource,macDest,frameData,frameLen,etherType,vlanId,_config.rules,_config.ruleCount,cc,ccLength,ccWatch)) {
+
+ case DOZTFILTER_NO_MATCH: {
+ Membership::CapabilityIterator mci(membership,_config);
+ while ((c = mci.next())) {
+ ztFinalDest = ztDest; // sanity check, should be unmodified if there was no match
+ Address cc2;
+ unsigned int ccLength2 = 0;
+ bool ccWatch2 = false;
+ switch(_doZtFilter(RR,crrl,_config,&membership,true,sourcePeer->address(),ztFinalDest,macSource,macDest,frameData,frameLen,etherType,vlanId,c->rules(),c->ruleCount(),cc2,ccLength2,ccWatch2)) {
+ case DOZTFILTER_NO_MATCH:
+ case DOZTFILTER_DROP: // explicit DROP in a capability just terminates its evaluation and is an anti-pattern
+ break;
+ case DOZTFILTER_REDIRECT: // interpreted as ACCEPT but ztDest will have been changed in _doZtFilter()
+ case DOZTFILTER_ACCEPT:
+ accept = 1; // ACCEPT
+ break;
+ case DOZTFILTER_SUPER_ACCEPT:
+ accept = 2; // super-ACCEPT
+ break;
+ }
+
+ if (accept) {
+ if (cc2) {
+ _membership(cc2).pushCredentials(RR,tPtr,RR->node->now(),cc2,_config,-1,false);
+
+ Packet outp(cc2,RR->identity.address(),Packet::VERB_EXT_FRAME);
+ outp.append(_id);
+ outp.append((uint8_t)(ccWatch2 ? 0x1c : 0x08));
+ macDest.appendTo(outp);
+ macSource.appendTo(outp);
+ outp.append((uint16_t)etherType);
+ outp.append(frameData,ccLength2);
+ outp.compress();
+ RR->sw->send(tPtr,outp,true);
+ }
+ break;
+ }
+ }
+ } break;
+
+ case DOZTFILTER_DROP:
+ if (_config.remoteTraceTarget)
+ RR->t->networkFilter(tPtr,*this,rrl,(Trace::RuleResultLog *)0,(Capability *)0,sourcePeer->address(),ztDest,macSource,macDest,frameData,frameLen,etherType,vlanId,false,true,0);
+ return 0; // DROP
+
+ case DOZTFILTER_REDIRECT: // interpreted as ACCEPT but ztFinalDest will have been changed in _doZtFilter()
+ case DOZTFILTER_ACCEPT:
+ accept = 1; // ACCEPT
+ break;
+ case DOZTFILTER_SUPER_ACCEPT:
+ accept = 2; // super-ACCEPT
+ break;
+ }
+
+ if (accept) {
+ if (cc) {
+ _membership(cc).pushCredentials(RR,tPtr,RR->node->now(),cc,_config,-1,false);
+
+ Packet outp(cc,RR->identity.address(),Packet::VERB_EXT_FRAME);
+ outp.append(_id);
+ outp.append((uint8_t)(ccWatch ? 0x1c : 0x08));
+ macDest.appendTo(outp);
+ macSource.appendTo(outp);
+ outp.append((uint16_t)etherType);
+ outp.append(frameData,ccLength);
+ outp.compress();
+ RR->sw->send(tPtr,outp,true);
+ }
+
+ if ((ztDest != ztFinalDest)&&(ztFinalDest)) {
+ _membership(ztFinalDest).pushCredentials(RR,tPtr,RR->node->now(),ztFinalDest,_config,-1,false);
+
+ Packet outp(ztFinalDest,RR->identity.address(),Packet::VERB_EXT_FRAME);
+ outp.append(_id);
+ outp.append((uint8_t)0x0a);
+ macDest.appendTo(outp);
+ macSource.appendTo(outp);
+ outp.append((uint16_t)etherType);
+ outp.append(frameData,frameLen);
+ outp.compress();
+ RR->sw->send(tPtr,outp,true);
+
+ if (_config.remoteTraceTarget)
+ RR->t->networkFilter(tPtr,*this,rrl,(c) ? &crrl : (Trace::RuleResultLog *)0,c,sourcePeer->address(),ztDest,macSource,macDest,frameData,frameLen,etherType,vlanId,false,true,0);
+ return 0; // DROP locally, since we redirected
+ }
+ }
+
+ if (_config.remoteTraceTarget)
+ RR->t->networkFilter(tPtr,*this,rrl,(c) ? &crrl : (Trace::RuleResultLog *)0,c,sourcePeer->address(),ztDest,macSource,macDest,frameData,frameLen,etherType,vlanId,false,true,accept);
+ return accept;
+}
+
bool Network::subscribedToMulticastGroup(const MulticastGroup &mg,bool includeBridgedGroups) const
{
Mutex::Lock _l(_lock);
@@ -110,150 +864,419 @@ bool Network::subscribedToMulticastGroup(const MulticastGroup &mg,bool includeBr
return true;
else if (includeBridgedGroups)
return _multicastGroupsBehindMe.contains(mg);
- else return false;
+ return false;
}
-void Network::multicastSubscribe(const MulticastGroup &mg)
+void Network::multicastSubscribe(void *tPtr,const MulticastGroup &mg)
{
- {
- Mutex::Lock _l(_lock);
- if (std::binary_search(_myMulticastGroups.begin(),_myMulticastGroups.end(),mg))
- return;
- _myMulticastGroups.push_back(mg);
- std::sort(_myMulticastGroups.begin(),_myMulticastGroups.end());
+ Mutex::Lock _l(_lock);
+ if (!std::binary_search(_myMulticastGroups.begin(),_myMulticastGroups.end(),mg)) {
+ _myMulticastGroups.insert(std::upper_bound(_myMulticastGroups.begin(),_myMulticastGroups.end(),mg),mg);
+ _sendUpdatesToMembers(tPtr,&mg);
}
- _announceMulticastGroups();
}
void Network::multicastUnsubscribe(const MulticastGroup &mg)
{
Mutex::Lock _l(_lock);
- std::vector<MulticastGroup> nmg;
- for(std::vector<MulticastGroup>::const_iterator i(_myMulticastGroups.begin());i!=_myMulticastGroups.end();++i) {
- if (*i != mg)
- nmg.push_back(*i);
- }
- if (nmg.size() != _myMulticastGroups.size())
- _myMulticastGroups.swap(nmg);
+ std::vector<MulticastGroup>::iterator i(std::lower_bound(_myMulticastGroups.begin(),_myMulticastGroups.end(),mg));
+ if ( (i != _myMulticastGroups.end()) && (*i == mg) )
+ _myMulticastGroups.erase(i);
}
-bool Network::tryAnnounceMulticastGroupsTo(const SharedPtr<Peer> &peer)
+uint64_t Network::handleConfigChunk(void *tPtr,const uint64_t packetId,const Address &source,const Buffer<ZT_PROTO_MAX_PACKET_LENGTH> &chunk,unsigned int ptr)
{
- Mutex::Lock _l(_lock);
- if (
- (_isAllowed(peer)) ||
- (peer->address() == this->controller()) ||
- (RR->topology->isRoot(peer->identity()))
- ) {
- _announceMulticastGroupsTo(peer,_allMulticastGroups());
- return true;
- }
- return false;
-}
+ if (_destroyed)
+ return 0;
-bool Network::applyConfiguration(const NetworkConfig &conf)
-{
- if (_destroyed) // sanity check
- return false;
- try {
- if ((conf.networkId == _id)&&(conf.issuedTo == RR->identity.address())) {
- ZT_VirtualNetworkConfig ctmp;
- bool portInitialized;
- {
- Mutex::Lock _l(_lock);
- _config = conf;
- _lastConfigUpdate = RR->node->now();
- _netconfFailure = NETCONF_FAILURE_NONE;
- _externalConfig(&ctmp);
- portInitialized = _portInitialized;
- _portInitialized = true;
+ const unsigned int start = ptr;
+
+ ptr += 8; // skip network ID, which is already obviously known
+ const unsigned int chunkLen = chunk.at<uint16_t>(ptr); ptr += 2;
+ const void *chunkData = chunk.field(ptr,chunkLen); ptr += chunkLen;
+
+ NetworkConfig *nc = (NetworkConfig *)0;
+ uint64_t configUpdateId;
+ {
+ Mutex::Lock _l(_lock);
+
+ _IncomingConfigChunk *c = (_IncomingConfigChunk *)0;
+ uint64_t chunkId = 0;
+ unsigned long totalLength,chunkIndex;
+ if (ptr < chunk.size()) {
+ const bool fastPropagate = ((chunk[ptr++] & 0x01) != 0);
+ configUpdateId = chunk.at<uint64_t>(ptr); ptr += 8;
+ totalLength = chunk.at<uint32_t>(ptr); ptr += 4;
+ chunkIndex = chunk.at<uint32_t>(ptr); ptr += 4;
+
+ if (((chunkIndex + chunkLen) > totalLength)||(totalLength >= ZT_NETWORKCONFIG_DICT_CAPACITY)) // >= since we need room for a null at the end
+ return 0;
+ if ((chunk[ptr] != 1)||(chunk.at<uint16_t>(ptr + 1) != ZT_C25519_SIGNATURE_LEN))
+ return 0;
+ const uint8_t *sig = reinterpret_cast<const uint8_t *>(chunk.field(ptr + 3,ZT_C25519_SIGNATURE_LEN));
+
+ // We can use the signature, which is unique per chunk, to get a per-chunk ID for local deduplication use
+ for(unsigned int i=0;i<16;++i)
+ reinterpret_cast<uint8_t *>(&chunkId)[i & 7] ^= sig[i];
+
+ // Find existing or new slot for this update and check if this is a duplicate chunk
+ for(int i=0;i<ZT_NETWORK_MAX_INCOMING_UPDATES;++i) {
+ if (_incomingConfigChunks[i].updateId == configUpdateId) {
+ c = &(_incomingConfigChunks[i]);
+
+ for(unsigned long j=0;j<c->haveChunks;++j) {
+ if (c->haveChunkIds[j] == chunkId)
+ return 0;
+ }
+
+ break;
+ } else if ((!c)||(_incomingConfigChunks[i].ts < c->ts)) {
+ c = &(_incomingConfigChunks[i]);
+ }
+ }
+
+ // If it's not a duplicate, check chunk signature
+ const Identity controllerId(RR->topology->getIdentity(tPtr,controller()));
+ if (!controllerId) // we should always have the controller identity by now, otherwise how would we have queried it the first time?
+ return 0;
+ if (!controllerId.verify(chunk.field(start,ptr - start),ptr - start,sig,ZT_C25519_SIGNATURE_LEN))
+ return 0;
+
+ // New properly verified chunks can be flooded "virally" through the network
+ if (fastPropagate) {
+ Address *a = (Address *)0;
+ Membership *m = (Membership *)0;
+ Hashtable<Address,Membership>::Iterator i(_memberships);
+ while (i.next(a,m)) {
+ if ((*a != source)&&(*a != controller())) {
+ Packet outp(*a,RR->identity.address(),Packet::VERB_NETWORK_CONFIG);
+ outp.append(reinterpret_cast<const uint8_t *>(chunk.data()) + start,chunk.size() - start);
+ RR->sw->send(tPtr,outp,true);
+ }
+ }
+ }
+ } else if ((source == controller())||(!source)) { // since old chunks aren't signed, only accept from controller itself (or via cluster backplane)
+ // Legacy support for OK(NETWORK_CONFIG_REQUEST) from older controllers
+ chunkId = packetId;
+ configUpdateId = chunkId;
+ totalLength = chunkLen;
+ chunkIndex = 0;
+
+ if (totalLength >= ZT_NETWORKCONFIG_DICT_CAPACITY)
+ return 0;
+
+ for(int i=0;i<ZT_NETWORK_MAX_INCOMING_UPDATES;++i) {
+ if ((!c)||(_incomingConfigChunks[i].ts < c->ts))
+ c = &(_incomingConfigChunks[i]);
}
- _portError = RR->node->configureVirtualNetworkPort(_id,&_uPtr,(portInitialized) ? ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_CONFIG_UPDATE : ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_UP,&ctmp);
- return true;
} else {
- TRACE("ignored invalid configuration for network %.16llx (configuration contains mismatched network ID or issued-to address)",(unsigned long long)_id);
+ // Single-chunk unsigned legacy configs are only allowed from the controller itself
+ return 0;
+ }
+
+ ++c->ts; // newer is higher, that's all we need
+
+ if (c->updateId != configUpdateId) {
+ c->updateId = configUpdateId;
+ c->haveChunks = 0;
+ c->haveBytes = 0;
+ }
+ if (c->haveChunks >= ZT_NETWORK_MAX_UPDATE_CHUNKS)
+ return false;
+ c->haveChunkIds[c->haveChunks++] = chunkId;
+
+ ZT_FAST_MEMCPY(c->data.unsafeData() + chunkIndex,chunkData,chunkLen);
+ c->haveBytes += chunkLen;
+
+ if (c->haveBytes == totalLength) {
+ c->data.unsafeData()[c->haveBytes] = (char)0; // ensure null terminated
+
+ nc = new NetworkConfig();
+ try {
+ if (!nc->fromDictionary(c->data)) {
+ delete nc;
+ nc = (NetworkConfig *)0;
+ }
+ } catch ( ... ) {
+ delete nc;
+ nc = (NetworkConfig *)0;
+ }
}
- } catch (std::exception &exc) {
- TRACE("ignored invalid configuration for network %.16llx (%s)",(unsigned long long)_id,exc.what());
- } catch ( ... ) {
- TRACE("ignored invalid configuration for network %.16llx (unknown exception)",(unsigned long long)_id);
}
- return false;
+
+ if (nc) {
+ this->setConfiguration(tPtr,*nc,true);
+ delete nc;
+ return configUpdateId;
+ } else {
+ return 0;
+ }
+
+ return 0;
}
-int Network::setConfiguration(const NetworkConfig &nconf,bool saveToDisk)
+int Network::setConfiguration(void *tPtr,const NetworkConfig &nconf,bool saveToDisk)
{
+ if (_destroyed)
+ return 0;
+
+ // _lock is NOT locked when this is called
try {
- {
+ if ((nconf.issuedTo != RR->identity.address())||(nconf.networkId != _id))
+ return 0; // invalid config that is not for us or not for this network
+ if (_config == nconf)
+ return 1; // OK config, but duplicate of what we already have
+
+ ZT_VirtualNetworkConfig ctmp;
+ bool oldPortInitialized;
+ { // do things that require lock here, but unlock before calling callbacks
Mutex::Lock _l(_lock);
- if (_config == nconf)
- return 1; // OK config, but duplicate of what we already have
+
+ _config = nconf;
+ _lastConfigUpdate = RR->node->now();
+ _netconfFailure = NETCONF_FAILURE_NONE;
+
+ oldPortInitialized = _portInitialized;
+ _portInitialized = true;
+
+ _externalConfig(&ctmp);
+
+ Address *a = (Address *)0;
+ Membership *m = (Membership *)0;
+ Hashtable<Address,Membership>::Iterator i(_memberships);
+ while (i.next(a,m))
+ m->resetPushState();
}
- if (applyConfiguration(nconf)) {
- if (saveToDisk) {
- char n[64];
- Utils::snprintf(n,sizeof(n),"networks.d/%.16llx.conf",_id);
- Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> d;
- if (nconf.toDictionary(d,false))
- RR->node->dataStorePut(n,(const void *)d.data(),d.sizeBytes(),true);
- }
- return 2; // OK and configuration has changed
+
+ _portError = RR->node->configureVirtualNetworkPort(tPtr,_id,&_uPtr,(oldPortInitialized) ? ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_CONFIG_UPDATE : ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_UP,&ctmp);
+
+ if (saveToDisk) {
+ Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> *d = new Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY>();
+ try {
+ if (nconf.toDictionary(*d,false)) {
+ uint64_t tmp[2];
+ tmp[0] = _id; tmp[1] = 0;
+ RR->node->stateObjectPut(tPtr,ZT_STATE_OBJECT_NETWORK_CONFIG,tmp,d->data(),d->sizeBytes());
+ }
+ } catch ( ... ) {}
+ delete d;
}
- } catch ( ... ) {
- TRACE("ignored invalid configuration for network %.16llx",(unsigned long long)_id);
- }
+
+ return 2; // OK and configuration has changed
+ } catch ( ... ) {} // ignore invalid configs
return 0;
}
-void Network::requestConfiguration()
+void Network::requestConfiguration(void *tPtr)
{
- if (_id == ZT_TEST_NETWORK_ID) // pseudo-network-ID, uses locally generated static config
+ if (_destroyed)
return;
- Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> rmd;
+ if ((_id >> 56) == 0xff) {
+ if ((_id & 0xffffff) == 0) {
+ const uint16_t startPortRange = (uint16_t)((_id >> 40) & 0xffff);
+ const uint16_t endPortRange = (uint16_t)((_id >> 24) & 0xffff);
+ if (endPortRange >= startPortRange) {
+ NetworkConfig *const nconf = new NetworkConfig();
+
+ nconf->networkId = _id;
+ nconf->timestamp = RR->node->now();
+ nconf->credentialTimeMaxDelta = ZT_NETWORKCONFIG_DEFAULT_CREDENTIAL_TIME_MAX_MAX_DELTA;
+ nconf->revision = 1;
+ nconf->issuedTo = RR->identity.address();
+ nconf->flags = ZT_NETWORKCONFIG_FLAG_ENABLE_IPV6_NDP_EMULATION;
+ nconf->mtu = ZT_DEFAULT_MTU;
+ nconf->multicastLimit = 0;
+ nconf->staticIpCount = 1;
+ nconf->ruleCount = 14;
+ nconf->staticIps[0] = InetAddress::makeIpv66plane(_id,RR->identity.address().toInt());
+
+ // Drop everything but IPv6
+ nconf->rules[0].t = (uint8_t)ZT_NETWORK_RULE_MATCH_ETHERTYPE | 0x80; // NOT
+ nconf->rules[0].v.etherType = 0x86dd; // IPv6
+ nconf->rules[1].t = (uint8_t)ZT_NETWORK_RULE_ACTION_DROP;
+
+ // Allow ICMPv6
+ nconf->rules[2].t = (uint8_t)ZT_NETWORK_RULE_MATCH_IP_PROTOCOL;
+ nconf->rules[2].v.ipProtocol = 0x3a; // ICMPv6
+ nconf->rules[3].t = (uint8_t)ZT_NETWORK_RULE_ACTION_ACCEPT;
+
+ // Allow destination ports within range
+ nconf->rules[4].t = (uint8_t)ZT_NETWORK_RULE_MATCH_IP_PROTOCOL;
+ nconf->rules[4].v.ipProtocol = 0x11; // UDP
+ nconf->rules[5].t = (uint8_t)ZT_NETWORK_RULE_MATCH_IP_PROTOCOL | 0x40; // OR
+ nconf->rules[5].v.ipProtocol = 0x06; // TCP
+ nconf->rules[6].t = (uint8_t)ZT_NETWORK_RULE_MATCH_IP_DEST_PORT_RANGE;
+ nconf->rules[6].v.port[0] = startPortRange;
+ nconf->rules[6].v.port[1] = endPortRange;
+ nconf->rules[7].t = (uint8_t)ZT_NETWORK_RULE_ACTION_ACCEPT;
+
+ // Allow non-SYN TCP packets to permit non-connection-initiating traffic
+ nconf->rules[8].t = (uint8_t)ZT_NETWORK_RULE_MATCH_CHARACTERISTICS | 0x80; // NOT
+ nconf->rules[8].v.characteristics = ZT_RULE_PACKET_CHARACTERISTICS_TCP_SYN;
+ nconf->rules[9].t = (uint8_t)ZT_NETWORK_RULE_ACTION_ACCEPT;
+
+ // Also allow SYN+ACK which are replies to SYN
+ nconf->rules[10].t = (uint8_t)ZT_NETWORK_RULE_MATCH_CHARACTERISTICS;
+ nconf->rules[10].v.characteristics = ZT_RULE_PACKET_CHARACTERISTICS_TCP_SYN;
+ nconf->rules[11].t = (uint8_t)ZT_NETWORK_RULE_MATCH_CHARACTERISTICS;
+ nconf->rules[11].v.characteristics = ZT_RULE_PACKET_CHARACTERISTICS_TCP_ACK;
+ nconf->rules[12].t = (uint8_t)ZT_NETWORK_RULE_ACTION_ACCEPT;
+
+ nconf->rules[13].t = (uint8_t)ZT_NETWORK_RULE_ACTION_DROP;
+
+ nconf->type = ZT_NETWORK_TYPE_PUBLIC;
+
+ nconf->name[0] = 'a';
+ nconf->name[1] = 'd';
+ nconf->name[2] = 'h';
+ nconf->name[3] = 'o';
+ nconf->name[4] = 'c';
+ nconf->name[5] = '-';
+ Utils::hex((uint16_t)startPortRange,nconf->name + 6);
+ nconf->name[10] = '-';
+ Utils::hex((uint16_t)endPortRange,nconf->name + 11);
+ nconf->name[15] = (char)0;
+
+ this->setConfiguration(tPtr,*nconf,false);
+ delete nconf;
+ } else {
+ this->setNotFound();
+ }
+ } else if ((_id & 0xff) == 0x01) {
+ // ffAAaaaaaaaaaa01 -- where AA is the IPv4 /8 to use and aaaaaaaaaa is the anchor node for multicast gather and replication
+ const uint64_t myAddress = RR->identity.address().toInt();
+ const uint64_t networkHub = (_id >> 8) & 0xffffffffffULL;
+
+ uint8_t ipv4[4];
+ ipv4[0] = (uint8_t)((_id >> 48) & 0xff);
+ ipv4[1] = (uint8_t)((myAddress >> 16) & 0xff);
+ ipv4[2] = (uint8_t)((myAddress >> 8) & 0xff);
+ ipv4[3] = (uint8_t)(myAddress & 0xff);
+
+ char v4ascii[24];
+ Utils::decimal(ipv4[0],v4ascii);
+
+ NetworkConfig *const nconf = new NetworkConfig();
+
+ nconf->networkId = _id;
+ nconf->timestamp = RR->node->now();
+ nconf->credentialTimeMaxDelta = ZT_NETWORKCONFIG_DEFAULT_CREDENTIAL_TIME_MAX_MAX_DELTA;
+ nconf->revision = 1;
+ nconf->issuedTo = RR->identity.address();
+ nconf->flags = ZT_NETWORKCONFIG_FLAG_ENABLE_IPV6_NDP_EMULATION;
+ nconf->mtu = ZT_DEFAULT_MTU;
+ nconf->multicastLimit = 1024;
+ nconf->specialistCount = (networkHub == 0) ? 0 : 1;
+ nconf->staticIpCount = 2;
+ nconf->ruleCount = 1;
+
+ if (networkHub != 0)
+ nconf->specialists[0] = networkHub;
+
+ nconf->staticIps[0] = InetAddress::makeIpv66plane(_id,myAddress);
+ nconf->staticIps[1].set(ipv4,4,8);
+
+ nconf->rules[0].t = (uint8_t)ZT_NETWORK_RULE_ACTION_ACCEPT;
+
+ nconf->type = ZT_NETWORK_TYPE_PUBLIC;
+
+ nconf->name[0] = 'a';
+ nconf->name[1] = 'd';
+ nconf->name[2] = 'h';
+ nconf->name[3] = 'o';
+ nconf->name[4] = 'c';
+ nconf->name[5] = '-';
+ unsigned long nn = 6;
+ while ((nconf->name[nn] = v4ascii[nn - 6])) ++nn;
+ nconf->name[nn++] = '.';
+ nconf->name[nn++] = '0';
+ nconf->name[nn++] = '.';
+ nconf->name[nn++] = '0';
+ nconf->name[nn++] = '.';
+ nconf->name[nn++] = '0';
+ nconf->name[nn++] = (char)0;
+
+ this->setConfiguration(tPtr,*nconf,false);
+ delete nconf;
+ }
+ return;
+ }
+
+ const Address ctrl(controller());
+
+ Dictionary<ZT_NETWORKCONFIG_METADATA_DICT_CAPACITY> rmd;
rmd.add(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_VERSION,(uint64_t)ZT_NETWORKCONFIG_VERSION);
+ rmd.add(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_NODE_VENDOR,(uint64_t)ZT_VENDOR_ZEROTIER);
rmd.add(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_PROTOCOL_VERSION,(uint64_t)ZT_PROTO_VERSION);
rmd.add(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_NODE_MAJOR_VERSION,(uint64_t)ZEROTIER_ONE_VERSION_MAJOR);
rmd.add(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_NODE_MINOR_VERSION,(uint64_t)ZEROTIER_ONE_VERSION_MINOR);
rmd.add(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_NODE_REVISION,(uint64_t)ZEROTIER_ONE_VERSION_REVISION);
+ rmd.add(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_MAX_NETWORK_RULES,(uint64_t)ZT_MAX_NETWORK_RULES);
+ rmd.add(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_MAX_NETWORK_CAPABILITIES,(uint64_t)ZT_MAX_NETWORK_CAPABILITIES);
+ rmd.add(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_MAX_CAPABILITY_RULES,(uint64_t)ZT_MAX_CAPABILITY_RULES);
+ rmd.add(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_MAX_NETWORK_TAGS,(uint64_t)ZT_MAX_NETWORK_TAGS);
+ rmd.add(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_FLAGS,(uint64_t)0);
+ rmd.add(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_RULES_ENGINE_REV,(uint64_t)ZT_RULES_ENGINE_REVISION);
- if (controller() == RR->identity.address()) {
+ RR->t->networkConfigRequestSent(tPtr,*this,ctrl);
+
+ if (ctrl == RR->identity.address()) {
if (RR->localNetworkController) {
- NetworkConfig nconf;
- switch(RR->localNetworkController->doNetworkConfigRequest(InetAddress(),RR->identity,RR->identity,_id,rmd,nconf)) {
- case NetworkController::NETCONF_QUERY_OK:
- this->setConfiguration(nconf,true);
- return;
- case NetworkController::NETCONF_QUERY_OBJECT_NOT_FOUND:
- this->setNotFound();
- return;
- case NetworkController::NETCONF_QUERY_ACCESS_DENIED:
- this->setAccessDenied();
- return;
- default:
- return;
- }
+ RR->localNetworkController->request(_id,InetAddress(),0xffffffffffffffffULL,RR->identity,rmd);
} else {
this->setNotFound();
- return;
}
+ return;
}
- TRACE("requesting netconf for network %.16llx from controller %s",(unsigned long long)_id,controller().toString().c_str());
-
- Packet outp(controller(),RR->identity.address(),Packet::VERB_NETWORK_CONFIG_REQUEST);
+ Packet outp(ctrl,RR->identity.address(),Packet::VERB_NETWORK_CONFIG_REQUEST);
outp.append((uint64_t)_id);
const unsigned int rmdSize = rmd.sizeBytes();
outp.append((uint16_t)rmdSize);
outp.append((const void *)rmd.data(),rmdSize);
- outp.append((_config) ? (uint64_t)_config.revision : (uint64_t)0);
+ if (_config) {
+ outp.append((uint64_t)_config.revision);
+ outp.append((uint64_t)_config.timestamp);
+ } else {
+ outp.append((unsigned char)0,16);
+ }
outp.compress();
- RR->sw->send(outp,true,0);
+ RR->node->expectReplyTo(outp.packetId());
+ RR->sw->send(tPtr,outp,true);
+}
+
+bool Network::gate(void *tPtr,const SharedPtr<Peer> &peer)
+{
+ const int64_t now = RR->node->now();
+ Mutex::Lock _l(_lock);
+ try {
+ if (_config) {
+ Membership *m = _memberships.get(peer->address());
+ if ( (_config.isPublic()) || ((m)&&(m->isAllowedOnNetwork(_config))) ) {
+ if (!m)
+ m = &(_membership(peer->address()));
+ if (m->multicastLikeGate(now)) {
+ m->pushCredentials(RR,tPtr,now,peer->address(),_config,-1,false);
+ _announceMulticastGroupsTo(tPtr,peer->address(),_allMulticastGroups());
+ }
+ return true;
+ }
+ }
+ } catch ( ... ) {}
+ return false;
+}
+
+bool Network::recentlyAssociatedWith(const Address &addr)
+{
+ Mutex::Lock _l(_lock);
+ const Membership *m = _memberships.get(addr);
+ return ((m)&&(m->recentlyAssociated(RR->node->now())));
}
void Network::clean()
{
- const uint64_t now = RR->node->now();
+ const int64_t now = RR->node->now();
Mutex::Lock _l(_lock);
if (_destroyed)
@@ -268,6 +1291,17 @@ void Network::clean()
_multicastGroupsBehindMe.erase(*mg);
}
}
+
+ {
+ Address *a = (Address *)0;
+ Membership *m = (Membership *)0;
+ Hashtable<Address,Membership>::Iterator i(_memberships);
+ while (i.next(a,m)) {
+ if (!RR->topology->getPeerNoCache(*a))
+ _memberships.erase(*a);
+ else m->clean(now,_config);
+ }
+ }
}
void Network::learnBridgeRoute(const MAC &mac,const Address &addr)
@@ -307,13 +1341,59 @@ void Network::learnBridgeRoute(const MAC &mac,const Address &addr)
}
}
-void Network::learnBridgedMulticastGroup(const MulticastGroup &mg,uint64_t now)
+void Network::learnBridgedMulticastGroup(void *tPtr,const MulticastGroup &mg,int64_t now)
{
Mutex::Lock _l(_lock);
const unsigned long tmp = (unsigned long)_multicastGroupsBehindMe.size();
_multicastGroupsBehindMe.set(mg,now);
if (tmp != _multicastGroupsBehindMe.size())
- _announceMulticastGroups();
+ _sendUpdatesToMembers(tPtr,&mg);
+}
+
+Membership::AddCredentialResult Network::addCredential(void *tPtr,const CertificateOfMembership &com)
+{
+ if (com.networkId() != _id)
+ return Membership::ADD_REJECTED;
+ const Address a(com.issuedTo());
+ Mutex::Lock _l(_lock);
+ Membership &m = _membership(a);
+ const Membership::AddCredentialResult result = m.addCredential(RR,tPtr,_config,com);
+ if ((result == Membership::ADD_ACCEPTED_NEW)||(result == Membership::ADD_ACCEPTED_REDUNDANT)) {
+ m.pushCredentials(RR,tPtr,RR->node->now(),a,_config,-1,false);
+ RR->mc->addCredential(tPtr,com,true);
+ }
+ return result;
+}
+
+Membership::AddCredentialResult Network::addCredential(void *tPtr,const Address &sentFrom,const Revocation &rev)
+{
+ if (rev.networkId() != _id)
+ return Membership::ADD_REJECTED;
+
+ Mutex::Lock _l(_lock);
+ Membership &m = _membership(rev.target());
+
+ const Membership::AddCredentialResult result = m.addCredential(RR,tPtr,_config,rev);
+
+ if ((result == Membership::ADD_ACCEPTED_NEW)&&(rev.fastPropagate())) {
+ Address *a = (Address *)0;
+ Membership *m = (Membership *)0;
+ Hashtable<Address,Membership>::Iterator i(_memberships);
+ while (i.next(a,m)) {
+ if ((*a != sentFrom)&&(*a != rev.signer())) {
+ Packet outp(*a,RR->identity.address(),Packet::VERB_NETWORK_CREDENTIALS);
+ outp.append((uint8_t)0x00); // no COM
+ outp.append((uint16_t)0); // no capabilities
+ outp.append((uint16_t)0); // no tags
+ outp.append((uint16_t)1); // one revocation!
+ rev.serialize(outp);
+ outp.append((uint16_t)0); // no certificates of ownership
+ RR->sw->send(tPtr,outp,true);
+ }
+ }
+ }
+
+ return result;
}
void Network::destroy()
@@ -349,10 +1429,10 @@ void Network::_externalConfig(ZT_VirtualNetworkConfig *ec) const
else ec->name[0] = (char)0;
ec->status = _status();
ec->type = (_config) ? (_config.isPrivate() ? ZT_NETWORK_TYPE_PRIVATE : ZT_NETWORK_TYPE_PUBLIC) : ZT_NETWORK_TYPE_PRIVATE;
- ec->mtu = ZT_IF_MTU;
+ ec->mtu = (_config) ? _config.mtu : ZT_DEFAULT_MTU;
ec->dhcp = 0;
std::vector<Address> ab(_config.activeBridges());
- ec->bridge = ((_config.allowPassiveBridging())||(std::find(ab.begin(),ab.end(),RR->identity.address()) != ab.end())) ? 1 : 0;
+ ec->bridge = (std::find(ab.begin(),ab.end(),RR->identity.address()) != ab.end()) ? 1 : 0;
ec->broadcastEnabled = (_config) ? (_config.enableBroadcast() ? 1 : 0) : 0;
ec->portError = _portError;
ec->netconfRevision = (_config) ? (unsigned long)_config.revision : 0;
@@ -360,7 +1440,7 @@ void Network::_externalConfig(ZT_VirtualNetworkConfig *ec) const
ec->assignedAddressCount = 0;
for(unsigned int i=0;i<ZT_MAX_ZT_ASSIGNED_ADDRESSES;++i) {
if (i < _config.staticIpCount) {
- memcpy(&(ec->assignedAddresses[i]),&(_config.staticIps[i]),sizeof(struct sockaddr_storage));
+ ZT_FAST_MEMCPY(&(ec->assignedAddresses[i]),&(_config.staticIps[i]),sizeof(struct sockaddr_storage));
++ec->assignedAddressCount;
} else {
memset(&(ec->assignedAddresses[i]),0,sizeof(struct sockaddr_storage));
@@ -370,7 +1450,7 @@ void Network::_externalConfig(ZT_VirtualNetworkConfig *ec) const
ec->routeCount = 0;
for(unsigned int i=0;i<ZT_MAX_NETWORK_ROUTES;++i) {
if (i < _config.routeCount) {
- memcpy(&(ec->routes[i]),&(_config.routes[i]),sizeof(ZT_VirtualNetworkRoute));
+ ZT_FAST_MEMCPY(&(ec->routes[i]),&(_config.routes[i]),sizeof(ZT_VirtualNetworkRoute));
++ec->routeCount;
} else {
memset(&(ec->routes[i]),0,sizeof(ZT_VirtualNetworkRoute));
@@ -378,96 +1458,87 @@ void Network::_externalConfig(ZT_VirtualNetworkConfig *ec) const
}
}
-bool Network::_isAllowed(const SharedPtr<Peer> &peer) const
+void Network::_sendUpdatesToMembers(void *tPtr,const MulticastGroup *const newMulticastGroup)
{
// Assumes _lock is locked
- try {
- if (!_config)
- return false;
- if (_config.isPublic())
- return true;
- return ((_config.com)&&(peer->networkMembershipCertificatesAgree(_id,_config.com)));
- } catch (std::exception &exc) {
- TRACE("isAllowed() check failed for peer %s: unexpected exception: %s",peer->address().toString().c_str(),exc.what());
- } catch ( ... ) {
- TRACE("isAllowed() check failed for peer %s: unexpected exception: unknown exception",peer->address().toString().c_str());
+ const int64_t now = RR->node->now();
+
+ std::vector<MulticastGroup> groups;
+ if (newMulticastGroup)
+ groups.push_back(*newMulticastGroup);
+ else groups = _allMulticastGroups();
+
+ std::vector<Address> alwaysAnnounceTo;
+
+ if ((newMulticastGroup)||((now - _lastAnnouncedMulticastGroupsUpstream) >= ZT_MULTICAST_ANNOUNCE_PERIOD)) {
+ if (!newMulticastGroup)
+ _lastAnnouncedMulticastGroupsUpstream = now;
+
+ alwaysAnnounceTo = _config.alwaysContactAddresses();
+ if (std::find(alwaysAnnounceTo.begin(),alwaysAnnounceTo.end(),controller()) == alwaysAnnounceTo.end())
+ alwaysAnnounceTo.push_back(controller());
+ const std::vector<Address> upstreams(RR->topology->upstreamAddresses());
+ for(std::vector<Address>::const_iterator a(upstreams.begin());a!=upstreams.end();++a) {
+ if (std::find(alwaysAnnounceTo.begin(),alwaysAnnounceTo.end(),*a) == alwaysAnnounceTo.end())
+ alwaysAnnounceTo.push_back(*a);
+ }
+ std::sort(alwaysAnnounceTo.begin(),alwaysAnnounceTo.end());
+
+ for(std::vector<Address>::const_iterator a(alwaysAnnounceTo.begin());a!=alwaysAnnounceTo.end();++a) {
+ // push COM to non-members so they can do multicast request auth
+ if ( (_config.com) && (!_memberships.contains(*a)) && (*a != RR->identity.address()) ) {
+ Packet outp(*a,RR->identity.address(),Packet::VERB_NETWORK_CREDENTIALS);
+ _config.com.serialize(outp);
+ outp.append((uint8_t)0x00);
+ outp.append((uint16_t)0); // no capabilities
+ outp.append((uint16_t)0); // no tags
+ outp.append((uint16_t)0); // no revocations
+ outp.append((uint16_t)0); // no certificates of ownership
+ RR->sw->send(tPtr,outp,true);
+ }
+ _announceMulticastGroupsTo(tPtr,*a,groups);
+ }
}
- return false; // default position on any failure
-}
-class _MulticastAnnounceAll
-{
-public:
- _MulticastAnnounceAll(const RuntimeEnvironment *renv,Network *nw) :
- _now(renv->node->now()),
- _controller(nw->controller()),
- _network(nw),
- _anchors(nw->config().anchors()),
- _rootAddresses(renv->topology->rootAddresses())
- {}
- inline void operator()(Topology &t,const SharedPtr<Peer> &p)
{
- if ( (_network->_isAllowed(p)) || // FIXME: this causes multicast LIKEs for public networks to get spammed
- (p->address() == _controller) ||
- (std::find(_rootAddresses.begin(),_rootAddresses.end(),p->address()) != _rootAddresses.end()) ||
- (std::find(_anchors.begin(),_anchors.end(),p->address()) != _anchors.end()) ) {
- peers.push_back(p);
+ Address *a = (Address *)0;
+ Membership *m = (Membership *)0;
+ Hashtable<Address,Membership>::Iterator i(_memberships);
+ while (i.next(a,m)) {
+ m->pushCredentials(RR,tPtr,now,*a,_config,-1,false);
+ if ( ( m->multicastLikeGate(now) || (newMulticastGroup) ) && (m->isAllowedOnNetwork(_config)) && (!std::binary_search(alwaysAnnounceTo.begin(),alwaysAnnounceTo.end(),*a)) )
+ _announceMulticastGroupsTo(tPtr,*a,groups);
}
}
- std::vector< SharedPtr<Peer> > peers;
-private:
- const uint64_t _now;
- const Address _controller;
- Network *const _network;
- const std::vector<Address> _anchors;
- const std::vector<Address> _rootAddresses;
-};
-void Network::_announceMulticastGroups()
-{
- // Assumes _lock is locked
- std::vector<MulticastGroup> allMulticastGroups(_allMulticastGroups());
- _MulticastAnnounceAll gpfunc(RR,this);
- RR->topology->eachPeer<_MulticastAnnounceAll &>(gpfunc);
- for(std::vector< SharedPtr<Peer> >::const_iterator i(gpfunc.peers.begin());i!=gpfunc.peers.end();++i)
- _announceMulticastGroupsTo(*i,allMulticastGroups);
}
-void Network::_announceMulticastGroupsTo(const SharedPtr<Peer> &peer,const std::vector<MulticastGroup> &allMulticastGroups) const
+void Network::_announceMulticastGroupsTo(void *tPtr,const Address &peer,const std::vector<MulticastGroup> &allMulticastGroups)
{
// Assumes _lock is locked
+ Packet outp(peer,RR->identity.address(),Packet::VERB_MULTICAST_LIKE);
- // We push COMs ahead of MULTICAST_LIKE since they're used for access control -- a COM is a public
- // credential so "over-sharing" isn't really an issue (and we only do so with roots).
- if ((_config)&&(_config.com)&&(!_config.isPublic())&&(peer->needsOurNetworkMembershipCertificate(_id,RR->node->now(),true))) {
- Packet outp(peer->address(),RR->identity.address(),Packet::VERB_NETWORK_MEMBERSHIP_CERTIFICATE);
- _config.com.serialize(outp);
- RR->sw->send(outp,true,0);
- }
-
- {
- Packet outp(peer->address(),RR->identity.address(),Packet::VERB_MULTICAST_LIKE);
-
- for(std::vector<MulticastGroup>::const_iterator mg(allMulticastGroups.begin());mg!=allMulticastGroups.end();++mg) {
- if ((outp.size() + 18) >= ZT_UDP_DEFAULT_PAYLOAD_MTU) {
- RR->sw->send(outp,true,0);
- outp.reset(peer->address(),RR->identity.address(),Packet::VERB_MULTICAST_LIKE);
- }
-
- // network ID, MAC, ADI
- outp.append((uint64_t)_id);
- mg->mac().appendTo(outp);
- outp.append((uint32_t)mg->adi());
+ for(std::vector<MulticastGroup>::const_iterator mg(allMulticastGroups.begin());mg!=allMulticastGroups.end();++mg) {
+ if ((outp.size() + 24) >= ZT_PROTO_MAX_PACKET_LENGTH) {
+ outp.compress();
+ RR->sw->send(tPtr,outp,true);
+ outp.reset(peer,RR->identity.address(),Packet::VERB_MULTICAST_LIKE);
}
- if (outp.size() > ZT_PROTO_MIN_PACKET_LENGTH)
- RR->sw->send(outp,true,0);
+ // network ID, MAC, ADI
+ outp.append((uint64_t)_id);
+ mg->mac().appendTo(outp);
+ outp.append((uint32_t)mg->adi());
+ }
+
+ if (outp.size() > ZT_PROTO_MIN_PACKET_LENGTH) {
+ outp.compress();
+ RR->sw->send(tPtr,outp,true);
}
}
std::vector<MulticastGroup> Network::_allMulticastGroups() const
{
// Assumes _lock is locked
-
std::vector<MulticastGroup> mgs;
mgs.reserve(_myMulticastGroups.size() + _multicastGroupsBehindMe.size() + 1);
mgs.insert(mgs.end(),_myMulticastGroups.begin(),_myMulticastGroups.end());
@@ -476,8 +1547,13 @@ std::vector<MulticastGroup> Network::_allMulticastGroups() const
mgs.push_back(Network::BROADCAST);
std::sort(mgs.begin(),mgs.end());
mgs.erase(std::unique(mgs.begin(),mgs.end()),mgs.end());
-
return mgs;
}
+Membership &Network::_membership(const Address &a)
+{
+ // assumes _lock is locked
+ return _memberships[a];
+}
+
} // namespace ZeroTier
diff --git a/node/Network.hpp b/node/Network.hpp
index 17eed4bd..95b5483a 100644
--- a/node/Network.hpp
+++ b/node/Network.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_NETWORK_HPP
@@ -30,7 +38,6 @@
#include <stdexcept>
#include "Constants.hpp"
-#include "NonCopyable.hpp"
#include "Hashtable.hpp"
#include "Address.hpp"
#include "Mutex.hpp"
@@ -40,22 +47,24 @@
#include "MAC.hpp"
#include "Dictionary.hpp"
#include "Multicaster.hpp"
+#include "Membership.hpp"
#include "NetworkConfig.hpp"
#include "CertificateOfMembership.hpp"
+#define ZT_NETWORK_MAX_INCOMING_UPDATES 3
+#define ZT_NETWORK_MAX_UPDATE_CHUNKS ((ZT_NETWORKCONFIG_DICT_CAPACITY / 1024) + 1)
+
namespace ZeroTier {
class RuntimeEnvironment;
class Peer;
-class _MulticastAnnounceAll;
/**
* A virtual LAN
*/
-class Network : NonCopyable
+class Network
{
friend class SharedPtr<Network>;
- friend class _MulticastAnnounceAll; // internal function object
public:
/**
@@ -64,56 +73,102 @@ public:
static const MulticastGroup BROADCAST;
/**
+ * Compute primary controller device ID from network ID
+ */
+ static inline Address controllerFor(uint64_t nwid) { return Address(nwid >> 24); }
+
+ /**
* Construct a new network
*
* Note that init() should be called immediately after the network is
* constructed to actually configure the port.
*
* @param renv Runtime environment
+ * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
* @param nwid Network ID
* @param uptr Arbitrary pointer used by externally-facing API (for user use)
+ * @param nconf Network config, if known
*/
- Network(const RuntimeEnvironment *renv,uint64_t nwid,void *uptr);
+ Network(const RuntimeEnvironment *renv,void *tPtr,uint64_t nwid,void *uptr,const NetworkConfig *nconf);
~Network();
- /**
- * @return Network ID
- */
- inline uint64_t id() const throw() { return _id; }
-
- /**
- * @return Address of network's controller (most significant 40 bits of ID)
- */
- inline Address controller() const throw() { return Address(_id >> 24); }
+ inline uint64_t id() const { return _id; }
+ inline Address controller() const { return Address(_id >> 24); }
+ inline bool multicastEnabled() const { return (_config.multicastLimit > 0); }
+ inline bool hasConfig() const { return (_config); }
+ inline uint64_t lastConfigUpdate() const { return _lastConfigUpdate; }
+ inline ZT_VirtualNetworkStatus status() const { Mutex::Lock _l(_lock); return _status(); }
+ inline const NetworkConfig &config() const { return _config; }
+ inline const MAC &mac() const { return _mac; }
/**
- * @param nwid Network ID
- * @return Address of network's controller
+ * Apply filters to an outgoing packet
+ *
+ * This applies filters from our network config and, if that doesn't match,
+ * our capabilities in ascending order of capability ID. Additional actions
+ * such as TEE may be taken, and credentials may be pushed, so this is not
+ * side-effect-free. It's basically step one in sending something over VL2.
+ *
+ * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
+ * @param noTee If true, do not TEE anything anywhere (for two-pass filtering as done with multicast and bridging)
+ * @param ztSource Source ZeroTier address
+ * @param ztDest Destination ZeroTier address
+ * @param macSource Ethernet layer source address
+ * @param macDest Ethernet layer destination address
+ * @param frameData Ethernet frame data
+ * @param frameLen Ethernet frame payload length
+ * @param etherType 16-bit ethernet type ID
+ * @param vlanId 16-bit VLAN ID
+ * @return True if packet should be sent, false if dropped or redirected
*/
- static inline Address controllerFor(uint64_t nwid) throw() { return Address(nwid >> 24); }
+ bool filterOutgoingPacket(
+ void *tPtr,
+ const bool noTee,
+ const Address &ztSource,
+ const Address &ztDest,
+ const MAC &macSource,
+ const MAC &macDest,
+ const uint8_t *frameData,
+ const unsigned int frameLen,
+ const unsigned int etherType,
+ const unsigned int vlanId);
/**
- * @return Multicast group memberships for this network's port (local, not learned via bridging)
+ * Apply filters to an incoming packet
+ *
+ * This applies filters from our network config and, if that doesn't match,
+ * the peer's capabilities in ascending order of capability ID. If there is
+ * a match certain actions may be taken such as sending a copy of the packet
+ * to a TEE or REDIRECT target.
+ *
+ * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
+ * @param sourcePeer Source Peer
+ * @param ztDest Destination ZeroTier address
+ * @param macSource Ethernet layer source address
+ * @param macDest Ethernet layer destination address
+ * @param frameData Ethernet frame data
+ * @param frameLen Ethernet frame payload length
+ * @param etherType 16-bit ethernet type ID
+ * @param vlanId 16-bit VLAN ID
+ * @return 0 == drop, 1 == accept, 2 == accept even if bridged
*/
- inline std::vector<MulticastGroup> multicastGroups() const
- {
- Mutex::Lock _l(_lock);
- return _myMulticastGroups;
- }
-
- /**
- * @return All multicast groups including learned groups that are behind any bridges we're attached to
- */
- inline std::vector<MulticastGroup> allMulticastGroups() const
- {
- Mutex::Lock _l(_lock);
- return _allMulticastGroups();
- }
+ int filterIncomingPacket(
+ void *tPtr,
+ const SharedPtr<Peer> &sourcePeer,
+ const Address &ztDest,
+ const MAC &macSource,
+ const MAC &macDest,
+ const uint8_t *frameData,
+ const unsigned int frameLen,
+ const unsigned int etherType,
+ const unsigned int vlanId);
/**
+ * Check whether we are subscribed to a multicast group
+ *
* @param mg Multicast group
- * @param includeBridgedGroups If true, also include any groups we've learned via bridging
+ * @param includeBridgedGroups If true, also check groups we've learned via bridging
* @return True if this network endpoint / peer is a member
*/
bool subscribedToMulticastGroup(const MulticastGroup &mg,bool includeBridgedGroups) const;
@@ -121,9 +176,10 @@ public:
/**
* Subscribe to a multicast group
*
+ * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
* @param mg New multicast group
*/
- void multicastSubscribe(const MulticastGroup &mg);
+ void multicastSubscribe(void *tPtr,const MulticastGroup &mg);
/**
* Unsubscribe from a multicast group
@@ -133,29 +189,30 @@ public:
void multicastUnsubscribe(const MulticastGroup &mg);
/**
- * Announce multicast groups to a peer if that peer is authorized on this network
+ * Handle an inbound network config chunk
*
- * @param peer Peer to try to announce multicast groups to
- * @return True if peer was authorized and groups were announced
- */
- bool tryAnnounceMulticastGroupsTo(const SharedPtr<Peer> &peer);
-
- /**
- * Apply a NetworkConfig to this network
+ * This is called from IncomingPacket to handle incoming network config
+ * chunks via OK(NETWORK_CONFIG_REQUEST) or NETWORK_CONFIG. It verifies
+ * each chunk and once assembled applies the configuration.
*
- * @param conf Configuration in NetworkConfig form
- * @return True if configuration was accepted
+ * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
+ * @param packetId Packet ID or 0 if none (e.g. via cluster path)
+ * @param source Address of sender of chunk or NULL if none (e.g. via cluster path)
+ * @param chunk Buffer containing chunk
+ * @param ptr Index of chunk and related fields in packet
+ * @return Update ID if update was fully assembled and accepted or 0 otherwise
*/
- bool applyConfiguration(const NetworkConfig &conf);
+ uint64_t handleConfigChunk(void *tPtr,const uint64_t packetId,const Address &source,const Buffer<ZT_PROTO_MAX_PACKET_LENGTH> &chunk,unsigned int ptr);
/**
- * Set or update this network's configuration
+ * Set network configuration
*
+ * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
* @param nconf Network configuration
- * @param saveToDisk IF true (default), write config to disk
- * @return 0 -- rejected, 1 -- accepted but not new, 2 -- accepted new config
+ * @param saveToDisk Save to disk? Used during loading, should usually be true otherwise.
+ * @return 0 == bad, 1 == accepted but duplicate/unchanged, 2 == accepted and new
*/
- int setConfiguration(const NetworkConfig &nconf,bool saveToDisk);
+ int setConfiguration(void *tPtr,const NetworkConfig &nconf,bool saveToDisk);
/**
* Set netconf failure to 'access denied' -- called in IncomingPacket when controller reports this
@@ -167,7 +224,7 @@ public:
}
/**
- * Set netconf failure to 'not found' -- called by PacketDecider when controller reports this
+ * Set netconf failure to 'not found' -- called by IncomingPacket when controller reports this
*/
inline void setNotFound()
{
@@ -177,151 +234,194 @@ public:
/**
* Causes this network to request an updated configuration from its master node now
+ *
+ * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
*/
- void requestConfiguration();
+ void requestConfiguration(void *tPtr);
/**
+ * Determine whether this peer is permitted to communicate on this network
+ *
+ * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
* @param peer Peer to check
- * @return True if peer is allowed to communicate on this network
*/
- inline bool isAllowed(const SharedPtr<Peer> &peer) const
- {
- Mutex::Lock _l(_lock);
- return _isAllowed(peer);
- }
+ bool gate(void *tPtr,const SharedPtr<Peer> &peer);
/**
- * Perform cleanup and possibly save state
+ * Check whether a given peer has recently had an association with this network
+ *
+ * This checks whether a peer has communicated with us recently about this
+ * network and has possessed a valid certificate of membership. This may return
+ * true even if the peer has been offline for a while or no longer has a valid
+ * certificate of membership but had one recently.
+ *
+ * @param addr Peer address
+ * @return True if peer has recently associated
*/
- void clean();
+ bool recentlyAssociatedWith(const Address &addr);
/**
- * @return Time of last updated configuration or 0 if none
+ * Do periodic cleanup and housekeeping tasks
*/
- inline uint64_t lastConfigUpdate() const throw() { return _lastConfigUpdate; }
+ void clean();
/**
- * @return Status of this network
+ * Push state to members such as multicast group memberships and latest COM (if needed)
+ *
+ * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
*/
- inline ZT_VirtualNetworkStatus status() const
+ inline void sendUpdatesToMembers(void *tPtr)
{
Mutex::Lock _l(_lock);
- return _status();
+ _sendUpdatesToMembers(tPtr,(const MulticastGroup *)0);
}
/**
- * @param ec Buffer to fill with externally-visible network configuration
+ * Find the node on this network that has this MAC behind it (if any)
+ *
+ * @param mac MAC address
+ * @return ZeroTier address of bridge to this MAC
*/
- inline void externalConfig(ZT_VirtualNetworkConfig *ec) const
+ inline Address findBridgeTo(const MAC &mac) const
{
Mutex::Lock _l(_lock);
- _externalConfig(ec);
+ const Address *const br = _remoteBridgeRoutes.get(mac);
+ return ((br) ? *br : Address());
}
/**
- * Get current network config
+ * Set a bridge route
*
- * This returns a const reference to the network config in place, which is safe
- * to concurrently access but *may* change during access. Normally this isn't a
- * problem, but if it is use configCopy().
+ * @param mac MAC address of destination
+ * @param addr Bridge this MAC is reachable behind
+ */
+ void learnBridgeRoute(const MAC &mac,const Address &addr);
+
+ /**
+ * Learn a multicast group that is bridged to our tap device
*
- * @return Network configuration (may be a null config if we don't have one yet)
+ * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
+ * @param mg Multicast group
+ * @param now Current time
*/
- inline const NetworkConfig &config() const { return _config; }
+ void learnBridgedMulticastGroup(void *tPtr,const MulticastGroup &mg,int64_t now);
/**
- * @return A thread-safe copy of our NetworkConfig instead of a const reference
+ * Validate a credential and learn it if it passes certificate and other checks
*/
- inline NetworkConfig configCopy() const
+ Membership::AddCredentialResult addCredential(void *tPtr,const CertificateOfMembership &com);
+
+ /**
+ * Validate a credential and learn it if it passes certificate and other checks
+ */
+ inline Membership::AddCredentialResult addCredential(void *tPtr,const Capability &cap)
{
+ if (cap.networkId() != _id)
+ return Membership::ADD_REJECTED;
Mutex::Lock _l(_lock);
- return _config;
+ return _membership(cap.issuedTo()).addCredential(RR,tPtr,_config,cap);
}
/**
- * @return True if this network has a valid config
+ * Validate a credential and learn it if it passes certificate and other checks
*/
- inline bool hasConfig() const { return (_config); }
+ inline Membership::AddCredentialResult addCredential(void *tPtr,const Tag &tag)
+ {
+ if (tag.networkId() != _id)
+ return Membership::ADD_REJECTED;
+ Mutex::Lock _l(_lock);
+ return _membership(tag.issuedTo()).addCredential(RR,tPtr,_config,tag);
+ }
/**
- * @return Ethernet MAC address for this network's local interface
+ * Validate a credential and learn it if it passes certificate and other checks
*/
- inline const MAC &mac() const throw() { return _mac; }
+ Membership::AddCredentialResult addCredential(void *tPtr,const Address &sentFrom,const Revocation &rev);
/**
- * Find the node on this network that has this MAC behind it (if any)
- *
- * @param mac MAC address
- * @return ZeroTier address of bridge to this MAC
+ * Validate a credential and learn it if it passes certificate and other checks
*/
- inline Address findBridgeTo(const MAC &mac) const
+ inline Membership::AddCredentialResult addCredential(void *tPtr,const CertificateOfOwnership &coo)
{
+ if (coo.networkId() != _id)
+ return Membership::ADD_REJECTED;
Mutex::Lock _l(_lock);
- const Address *const br = _remoteBridgeRoutes.get(mac);
- if (br)
- return *br;
- return Address();
+ return _membership(coo.issuedTo()).addCredential(RR,tPtr,_config,coo);
}
/**
- * Set a bridge route
- *
- * @param mac MAC address of destination
- * @param addr Bridge this MAC is reachable behind
- */
- void learnBridgeRoute(const MAC &mac,const Address &addr);
-
- /**
- * Learn a multicast group that is bridged to our tap device
+ * Force push credentials (COM, etc.) to a peer now
*
- * @param mg Multicast group
+ * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
+ * @param to Destination peer address
* @param now Current time
*/
- void learnBridgedMulticastGroup(const MulticastGroup &mg,uint64_t now);
+ inline void pushCredentialsNow(void *tPtr,const Address &to,const int64_t now)
+ {
+ Mutex::Lock _l(_lock);
+ _membership(to).pushCredentials(RR,tPtr,now,to,_config,-1,true);
+ }
/**
* Destroy this network
*
- * This causes the network to disable itself, destroy its tap device, and on
- * delete to delete all trace of itself on disk and remove any persistent tap
- * device instances. Call this when a network is being removed from the system.
+ * This sets the network to completely remove itself on delete. This also prevents the
+ * call of the normal port shutdown event on delete.
*/
void destroy();
/**
- * @return Pointer to user PTR (modifiable user ptr used in API)
+ * Get this network's config for export via the ZT core API
+ *
+ * @param ec Buffer to fill with externally-visible network configuration
*/
- inline void **userPtr() throw() { return &_uPtr; }
+ inline void externalConfig(ZT_VirtualNetworkConfig *ec) const
+ {
+ Mutex::Lock _l(_lock);
+ _externalConfig(ec);
+ }
- inline bool operator==(const Network &n) const throw() { return (_id == n._id); }
- inline bool operator!=(const Network &n) const throw() { return (_id != n._id); }
- inline bool operator<(const Network &n) const throw() { return (_id < n._id); }
- inline bool operator>(const Network &n) const throw() { return (_id > n._id); }
- inline bool operator<=(const Network &n) const throw() { return (_id <= n._id); }
- inline bool operator>=(const Network &n) const throw() { return (_id >= n._id); }
+ /**
+ * @return Externally usable pointer-to-pointer exported via the core API
+ */
+ inline void **userPtr() { return &_uPtr; }
private:
ZT_VirtualNetworkStatus _status() const;
void _externalConfig(ZT_VirtualNetworkConfig *ec) const; // assumes _lock is locked
- bool _isAllowed(const SharedPtr<Peer> &peer) const;
- void _announceMulticastGroups();
- void _announceMulticastGroupsTo(const SharedPtr<Peer> &peer,const std::vector<MulticastGroup> &allMulticastGroups) const;
+ bool _gate(const SharedPtr<Peer> &peer);
+ void _sendUpdatesToMembers(void *tPtr,const MulticastGroup *const newMulticastGroup);
+ void _announceMulticastGroupsTo(void *tPtr,const Address &peer,const std::vector<MulticastGroup> &allMulticastGroups);
std::vector<MulticastGroup> _allMulticastGroups() const;
+ Membership &_membership(const Address &a);
- const RuntimeEnvironment *RR;
+ const RuntimeEnvironment *const RR;
void *_uPtr;
- uint64_t _id;
+ const uint64_t _id;
+ uint64_t _lastAnnouncedMulticastGroupsUpstream;
MAC _mac; // local MAC address
- volatile bool _portInitialized;
+ bool _portInitialized;
std::vector< MulticastGroup > _myMulticastGroups; // multicast groups that we belong to (according to tap)
Hashtable< MulticastGroup,uint64_t > _multicastGroupsBehindMe; // multicast groups that seem to be behind us and when we last saw them (if we are a bridge)
Hashtable< MAC,Address > _remoteBridgeRoutes; // remote addresses where given MACs are reachable (for tracking devices behind remote bridges)
NetworkConfig _config;
- volatile uint64_t _lastConfigUpdate;
+ uint64_t _lastConfigUpdate;
- volatile bool _destroyed;
+ struct _IncomingConfigChunk
+ {
+ _IncomingConfigChunk() { memset(this,0,sizeof(_IncomingConfigChunk)); }
+ uint64_t ts;
+ uint64_t updateId;
+ uint64_t haveChunkIds[ZT_NETWORK_MAX_UPDATE_CHUNKS];
+ unsigned long haveChunks;
+ unsigned long haveBytes;
+ Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> data;
+ };
+ _IncomingConfigChunk _incomingConfigChunks[ZT_NETWORK_MAX_INCOMING_UPDATES];
+
+ bool _destroyed;
enum {
NETCONF_FAILURE_NONE,
@@ -329,7 +429,9 @@ private:
NETCONF_FAILURE_NOT_FOUND,
NETCONF_FAILURE_INIT_FAILED
} _netconfFailure;
- volatile int _portError; // return value from port config callback
+ int _portError; // return value from port config callback
+
+ Hashtable<Address,Membership> _memberships;
Mutex _lock;
diff --git a/node/NetworkConfig.cpp b/node/NetworkConfig.cpp
index 9d5c5f17..db051699 100644
--- a/node/NetworkConfig.cpp
+++ b/node/NetworkConfig.cpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,252 +14,185 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#include <stdint.h>
+#include <algorithm>
+
#include "NetworkConfig.hpp"
-#include "Utils.hpp"
namespace ZeroTier {
bool NetworkConfig::toDictionary(Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> &d,bool includeLegacy) const
{
- Buffer<ZT_NETWORKCONFIG_DICT_CAPACITY> tmp;
-
- d.clear();
-
- // Try to put the more human-readable fields first
+ Buffer<ZT_NETWORKCONFIG_DICT_CAPACITY> *tmp = new Buffer<ZT_NETWORKCONFIG_DICT_CAPACITY>();
+ char tmp2[128];
- if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_VERSION,(uint64_t)ZT_NETWORKCONFIG_VERSION)) return false;
- if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_NETWORK_ID,this->networkId)) return false;
- if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_TIMESTAMP,this->timestamp)) return false;
- if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_REVISION,this->revision)) return false;
- if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_ISSUED_TO,this->issuedTo)) return false;
- if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_FLAGS,this->flags)) return false;
- if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_MULTICAST_LIMIT,(uint64_t)this->multicastLimit)) return false;
- if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_TYPE,(uint64_t)this->type)) return false;
- if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_NAME,this->name)) return false;
+ try {
+ d.clear();
+
+ // Try to put the more human-readable fields first
+
+ if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_VERSION,(uint64_t)ZT_NETWORKCONFIG_VERSION)) return false;
+ if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_NETWORK_ID,this->networkId)) return false;
+ if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_TIMESTAMP,this->timestamp)) return false;
+ if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_CREDENTIAL_TIME_MAX_DELTA,this->credentialTimeMaxDelta)) return false;
+ if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_REVISION,this->revision)) return false;
+ if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_ISSUED_TO,this->issuedTo.toString(tmp2))) return false;
+ if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_REMOTE_TRACE_TARGET,this->remoteTraceTarget.toString(tmp2))) return false;
+ if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_REMOTE_TRACE_LEVEL,(uint64_t)this->remoteTraceLevel)) return false;
+ if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_FLAGS,this->flags)) return false;
+ if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_MULTICAST_LIMIT,(uint64_t)this->multicastLimit)) return false;
+ if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_TYPE,(uint64_t)this->type)) return false;
+ if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_NAME,this->name)) return false;
+ if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_MTU,(uint64_t)this->mtu)) return false;
#ifdef ZT_SUPPORT_OLD_STYLE_NETCONF
- if (includeLegacy) {
- if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_ALLOW_PASSIVE_BRIDGING_OLD,this->allowPassiveBridging())) return false;
- if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_ENABLE_BROADCAST_OLD,this->enableBroadcast())) return false;
- if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_PRIVATE_OLD,this->isPrivate())) return false;
-
- std::string v4s;
- for(unsigned int i=0;i<staticIpCount;++i) {
- if (this->staticIps[i].ss_family == AF_INET) {
- if (v4s.length() > 0)
- v4s.push_back(',');
- v4s.append(this->staticIps[i].toString());
+ if (includeLegacy) {
+ if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_ENABLE_BROADCAST_OLD,this->enableBroadcast())) return false;
+ if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_PRIVATE_OLD,this->isPrivate())) return false;
+
+ std::string v4s;
+ for(unsigned int i=0;i<staticIpCount;++i) {
+ if (this->staticIps[i].ss_family == AF_INET) {
+ if (v4s.length() > 0)
+ v4s.push_back(',');
+ char buf[64];
+ v4s.append(this->staticIps[i].toString(buf));
+ }
}
- }
- if (v4s.length() > 0) {
- if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_IPV4_STATIC_OLD,v4s.c_str())) return false;
- }
- std::string v6s;
- for(unsigned int i=0;i<staticIpCount;++i) {
- if (this->staticIps[i].ss_family == AF_INET6) {
- if (v6s.length() > 0)
- v6s.push_back(',');
- v6s.append(this->staticIps[i].toString());
+ if (v4s.length() > 0) {
+ if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_IPV4_STATIC_OLD,v4s.c_str())) return false;
}
- }
- if (v6s.length() > 0) {
- if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_IPV6_STATIC_OLD,v6s.c_str())) return false;
- }
-
- std::string ets;
- unsigned int et = 0;
- ZT_VirtualNetworkRuleType lastrt = ZT_NETWORK_RULE_ACTION_ACCEPT;
- for(unsigned int i=0;i<ruleCount;++i) {
- ZT_VirtualNetworkRuleType rt = (ZT_VirtualNetworkRuleType)(rules[i].t & 0x7f);
- if (rt == ZT_NETWORK_RULE_MATCH_ETHERTYPE) {
- et = rules[i].v.etherType;
- } else if (rt == ZT_NETWORK_RULE_ACTION_ACCEPT) {
- if (((int)lastrt < 32)||(lastrt == ZT_NETWORK_RULE_MATCH_ETHERTYPE)) {
- if (ets.length() > 0)
- ets.push_back(',');
- char tmp[16];
- Utils::snprintf(tmp,sizeof(tmp),"%x",et);
- ets.append(tmp);
+ std::string v6s;
+ for(unsigned int i=0;i<staticIpCount;++i) {
+ if (this->staticIps[i].ss_family == AF_INET6) {
+ if (v6s.length() > 0)
+ v6s.push_back(',');
+ char buf[64];
+ v6s.append(this->staticIps[i].toString(buf));
}
- et = 0;
}
- lastrt = rt;
- }
- if (ets.length() > 0) {
- if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_ALLOWED_ETHERNET_TYPES_OLD,ets.c_str())) return false;
- }
+ if (v6s.length() > 0) {
+ if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_IPV6_STATIC_OLD,v6s.c_str())) return false;
+ }
- if (this->com) {
- if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_CERTIFICATE_OF_MEMBERSHIP_OLD,this->com.toString().c_str())) return false;
- }
+ std::string ets;
+ unsigned int et = 0;
+ ZT_VirtualNetworkRuleType lastrt = ZT_NETWORK_RULE_ACTION_ACCEPT;
+ for(unsigned int i=0;i<ruleCount;++i) {
+ ZT_VirtualNetworkRuleType rt = (ZT_VirtualNetworkRuleType)(rules[i].t & 0x7f);
+ if (rt == ZT_NETWORK_RULE_MATCH_ETHERTYPE) {
+ et = rules[i].v.etherType;
+ } else if (rt == ZT_NETWORK_RULE_ACTION_ACCEPT) {
+ if (((int)lastrt < 32)||(lastrt == ZT_NETWORK_RULE_MATCH_ETHERTYPE)) {
+ if (ets.length() > 0)
+ ets.push_back(',');
+ char tmp2[16];
+ ets.append(Utils::hex((uint16_t)et,tmp2));
+ }
+ et = 0;
+ }
+ lastrt = rt;
+ }
+ if (ets.length() > 0) {
+ if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_ALLOWED_ETHERNET_TYPES_OLD,ets.c_str())) return false;
+ }
- std::string ab;
- for(unsigned int i=0;i<this->specialistCount;++i) {
- if ((this->specialists[i] & ZT_NETWORKCONFIG_SPECIALIST_TYPE_ACTIVE_BRIDGE) != 0) {
- if (ab.length() > 0)
- ab.push_back(',');
- ab.append(Address(this->specialists[i]).toString().c_str());
+ if (this->com) {
+ if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_CERTIFICATE_OF_MEMBERSHIP_OLD,this->com.toString().c_str())) return false;
}
- }
- if (ab.length() > 0) {
- if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_ACTIVE_BRIDGES_OLD,ab.c_str())) return false;
- }
- std::vector<Relay> rvec(this->relays());
- std::string rl;
- for(std::vector<Relay>::const_iterator i(rvec.begin());i!=rvec.end();++i) {
- if (rl.length() > 0)
- rl.push_back(',');
- rl.append(i->address.toString());
- if (i->phy4) {
- rl.push_back(';');
- rl.append(i->phy4.toString());
- } else if (i->phy6) {
- rl.push_back(';');
- rl.append(i->phy6.toString());
+ std::string ab;
+ for(unsigned int i=0;i<this->specialistCount;++i) {
+ if ((this->specialists[i] & ZT_NETWORKCONFIG_SPECIALIST_TYPE_ACTIVE_BRIDGE) != 0) {
+ if (ab.length() > 0)
+ ab.push_back(',');
+ char tmp2[16];
+ ab.append(Address(this->specialists[i]).toString(tmp2));
+ }
+ }
+ if (ab.length() > 0) {
+ if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_ACTIVE_BRIDGES_OLD,ab.c_str())) return false;
}
}
- if (rl.length() > 0) {
- if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_RELAYS_OLD,rl.c_str())) return false;
- }
- }
#endif // ZT_SUPPORT_OLD_STYLE_NETCONF
- // Then add binary blobs
+ // Then add binary blobs
- if (this->com) {
- tmp.clear();
- this->com.serialize(tmp);
- if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_COM,tmp)) return false;
- }
+ if (this->com) {
+ tmp->clear();
+ this->com.serialize(*tmp);
+ if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_COM,*tmp)) return false;
+ }
- tmp.clear();
- for(unsigned int i=0;i<this->specialistCount;++i) {
- tmp.append((uint64_t)this->specialists[i]);
- }
- if (tmp.size()) {
- if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_SPECIALISTS,tmp)) return false;
- }
+ tmp->clear();
+ for(unsigned int i=0;i<this->capabilityCount;++i)
+ this->capabilities[i].serialize(*tmp);
+ if (tmp->size()) {
+ if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_CAPABILITIES,*tmp)) return false;
+ }
- tmp.clear();
- for(unsigned int i=0;i<this->routeCount;++i) {
- reinterpret_cast<const InetAddress *>(&(this->routes[i].target))->serialize(tmp);
- reinterpret_cast<const InetAddress *>(&(this->routes[i].via))->serialize(tmp);
- tmp.append((uint16_t)this->routes[i].flags);
- tmp.append((uint16_t)this->routes[i].metric);
- }
- if (tmp.size()) {
- if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_ROUTES,tmp)) return false;
- }
+ tmp->clear();
+ for(unsigned int i=0;i<this->tagCount;++i)
+ this->tags[i].serialize(*tmp);
+ if (tmp->size()) {
+ if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_TAGS,*tmp)) return false;
+ }
- tmp.clear();
- for(unsigned int i=0;i<this->staticIpCount;++i) {
- this->staticIps[i].serialize(tmp);
- }
- if (tmp.size()) {
- if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_STATIC_IPS,tmp)) return false;
- }
+ tmp->clear();
+ for(unsigned int i=0;i<this->certificateOfOwnershipCount;++i)
+ this->certificatesOfOwnership[i].serialize(*tmp);
+ if (tmp->size()) {
+ if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_CERTIFICATES_OF_OWNERSHIP,*tmp)) return false;
+ }
- tmp.clear();
- for(unsigned int i=0;i<this->pinnedCount;++i) {
- this->pinned[i].zt.appendTo(tmp);
- this->pinned[i].phy.serialize(tmp);
- }
- if (tmp.size()) {
- if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_PINNED,tmp)) return false;
- }
+ tmp->clear();
+ for(unsigned int i=0;i<this->specialistCount;++i)
+ tmp->append((uint64_t)this->specialists[i]);
+ if (tmp->size()) {
+ if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_SPECIALISTS,*tmp)) return false;
+ }
- tmp.clear();
- for(unsigned int i=0;i<this->ruleCount;++i) {
- tmp.append((uint8_t)rules[i].t);
- switch((ZT_VirtualNetworkRuleType)(rules[i].t & 0x7f)) {
- //case ZT_NETWORK_RULE_ACTION_DROP:
- //case ZT_NETWORK_RULE_ACTION_ACCEPT:
- default:
- tmp.append((uint8_t)0);
- break;
- case ZT_NETWORK_RULE_ACTION_TEE:
- case ZT_NETWORK_RULE_ACTION_REDIRECT:
- case ZT_NETWORK_RULE_MATCH_SOURCE_ZEROTIER_ADDRESS:
- case ZT_NETWORK_RULE_MATCH_DEST_ZEROTIER_ADDRESS:
- tmp.append((uint8_t)5);
- Address(rules[i].v.zt).appendTo(tmp);
- break;
- case ZT_NETWORK_RULE_MATCH_VLAN_ID:
- tmp.append((uint8_t)2);
- tmp.append((uint16_t)rules[i].v.vlanId);
- break;
- case ZT_NETWORK_RULE_MATCH_VLAN_PCP:
- tmp.append((uint8_t)1);
- tmp.append((uint8_t)rules[i].v.vlanPcp);
- break;
- case ZT_NETWORK_RULE_MATCH_VLAN_DEI:
- tmp.append((uint8_t)1);
- tmp.append((uint8_t)rules[i].v.vlanDei);
- break;
- case ZT_NETWORK_RULE_MATCH_ETHERTYPE:
- tmp.append((uint8_t)2);
- tmp.append((uint16_t)rules[i].v.etherType);
- break;
- case ZT_NETWORK_RULE_MATCH_MAC_SOURCE:
- case ZT_NETWORK_RULE_MATCH_MAC_DEST:
- tmp.append((uint8_t)6);
- tmp.append(rules[i].v.mac,6);
- break;
- case ZT_NETWORK_RULE_MATCH_IPV4_SOURCE:
- case ZT_NETWORK_RULE_MATCH_IPV4_DEST:
- tmp.append((uint8_t)5);
- tmp.append(&(rules[i].v.ipv4.ip),4);
- tmp.append((uint8_t)rules[i].v.ipv4.mask);
- break;
- case ZT_NETWORK_RULE_MATCH_IPV6_SOURCE:
- case ZT_NETWORK_RULE_MATCH_IPV6_DEST:
- tmp.append((uint8_t)17);
- tmp.append(rules[i].v.ipv6.ip,16);
- tmp.append((uint8_t)rules[i].v.ipv6.mask);
- break;
- case ZT_NETWORK_RULE_MATCH_IP_TOS:
- tmp.append((uint8_t)1);
- tmp.append((uint8_t)rules[i].v.ipTos);
- break;
- case ZT_NETWORK_RULE_MATCH_IP_PROTOCOL:
- tmp.append((uint8_t)1);
- tmp.append((uint8_t)rules[i].v.ipProtocol);
- break;
- case ZT_NETWORK_RULE_MATCH_IP_SOURCE_PORT_RANGE:
- case ZT_NETWORK_RULE_MATCH_IP_DEST_PORT_RANGE:
- tmp.append((uint8_t)4);
- tmp.append((uint16_t)rules[i].v.port[0]);
- tmp.append((uint16_t)rules[i].v.port[1]);
- break;
- case ZT_NETWORK_RULE_MATCH_CHARACTERISTICS:
- tmp.append((uint8_t)8);
- tmp.append((uint64_t)rules[i].v.characteristics);
- break;
- case ZT_NETWORK_RULE_MATCH_FRAME_SIZE_RANGE:
- tmp.append((uint8_t)4);
- tmp.append((uint16_t)rules[i].v.frameSize[0]);
- tmp.append((uint16_t)rules[i].v.frameSize[1]);
- break;
- case ZT_NETWORK_RULE_MATCH_TCP_RELATIVE_SEQUENCE_NUMBER_RANGE:
- tmp.append((uint8_t)8);
- tmp.append((uint32_t)rules[i].v.tcpseq[0]);
- tmp.append((uint32_t)rules[i].v.tcpseq[1]);
- break;
- case ZT_NETWORK_RULE_MATCH_COM_FIELD_GE:
- case ZT_NETWORK_RULE_MATCH_COM_FIELD_LE:
- tmp.append((uint8_t)16);
- tmp.append((uint64_t)rules[i].v.comIV[0]);
- tmp.append((uint64_t)rules[i].v.comIV[1]);
- break;
+ tmp->clear();
+ for(unsigned int i=0;i<this->routeCount;++i) {
+ reinterpret_cast<const InetAddress *>(&(this->routes[i].target))->serialize(*tmp);
+ reinterpret_cast<const InetAddress *>(&(this->routes[i].via))->serialize(*tmp);
+ tmp->append((uint16_t)this->routes[i].flags);
+ tmp->append((uint16_t)this->routes[i].metric);
}
- }
- if (tmp.size()) {
- if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_RULES,tmp)) return false;
+ if (tmp->size()) {
+ if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_ROUTES,*tmp)) return false;
+ }
+
+ tmp->clear();
+ for(unsigned int i=0;i<this->staticIpCount;++i)
+ this->staticIps[i].serialize(*tmp);
+ if (tmp->size()) {
+ if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_STATIC_IPS,*tmp)) return false;
+ }
+
+ if (this->ruleCount) {
+ tmp->clear();
+ Capability::serializeRules(*tmp,rules,ruleCount);
+ if (tmp->size()) {
+ if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_RULES,*tmp)) return false;
+ }
+ }
+
+ delete tmp;
+ } catch ( ... ) {
+ delete tmp;
+ throw;
}
return true;
@@ -267,29 +200,41 @@ bool NetworkConfig::toDictionary(Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> &d,b
bool NetworkConfig::fromDictionary(const Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> &d)
{
- try {
- Buffer<ZT_NETWORKCONFIG_DICT_CAPACITY> tmp;
- char tmp2[ZT_NETWORKCONFIG_DICT_CAPACITY];
+ Buffer<ZT_NETWORKCONFIG_DICT_CAPACITY> *tmp = new Buffer<ZT_NETWORKCONFIG_DICT_CAPACITY>();
+ try {
memset(this,0,sizeof(NetworkConfig));
// Fields that are always present, new or old
this->networkId = d.getUI(ZT_NETWORKCONFIG_DICT_KEY_NETWORK_ID,0);
- if (!this->networkId)
+ if (!this->networkId) {
+ delete tmp;
return false;
+ }
this->timestamp = d.getUI(ZT_NETWORKCONFIG_DICT_KEY_TIMESTAMP,0);
+ this->credentialTimeMaxDelta = d.getUI(ZT_NETWORKCONFIG_DICT_KEY_CREDENTIAL_TIME_MAX_DELTA,0);
this->revision = d.getUI(ZT_NETWORKCONFIG_DICT_KEY_REVISION,0);
this->issuedTo = d.getUI(ZT_NETWORKCONFIG_DICT_KEY_ISSUED_TO,0);
- if (!this->issuedTo)
+ if (!this->issuedTo) {
+ delete tmp;
return false;
+ }
+ this->remoteTraceTarget = d.getUI(ZT_NETWORKCONFIG_DICT_KEY_REMOTE_TRACE_TARGET);
+ this->remoteTraceLevel = (Trace::Level)d.getUI(ZT_NETWORKCONFIG_DICT_KEY_REMOTE_TRACE_LEVEL);
this->multicastLimit = (unsigned int)d.getUI(ZT_NETWORKCONFIG_DICT_KEY_MULTICAST_LIMIT,0);
d.get(ZT_NETWORKCONFIG_DICT_KEY_NAME,this->name,sizeof(this->name));
+ this->mtu = (unsigned int)d.getUI(ZT_NETWORKCONFIG_DICT_KEY_MTU,ZT_DEFAULT_MTU);
+ if (this->mtu < 1280)
+ this->mtu = 1280; // minimum MTU allowed by IPv6 standard and others
+ else if (this->mtu > ZT_MAX_MTU)
+ this->mtu = ZT_MAX_MTU;
+
if (d.getUI(ZT_NETWORKCONFIG_DICT_KEY_VERSION,0) < 6) {
#ifdef ZT_SUPPORT_OLD_STYLE_NETCONF
+ char tmp2[1024];
+
// Decode legacy fields if version is old
- if (d.getB(ZT_NETWORKCONFIG_DICT_KEY_ALLOW_PASSIVE_BRIDGING_OLD))
- this->flags |= ZT_NETWORKCONFIG_FLAG_ALLOW_PASSIVE_BRIDGING;
if (d.getB(ZT_NETWORKCONFIG_DICT_KEY_ENABLE_BROADCAST_OLD))
this->flags |= ZT_NETWORKCONFIG_FLAG_ENABLE_BROADCAST;
this->flags |= ZT_NETWORKCONFIG_FLAG_ENABLE_IPV6_NDP_EMULATION; // always enable for old-style netconf
@@ -338,36 +283,11 @@ bool NetworkConfig::fromDictionary(const Dictionary<ZT_NETWORKCONFIG_DICT_CAPACI
if (d.get(ZT_NETWORKCONFIG_DICT_KEY_ACTIVE_BRIDGES_OLD,tmp2,sizeof(tmp2)) > 0) {
char *saveptr = (char *)0;
for(char *f=Utils::stok(tmp2,",",&saveptr);(f);f=Utils::stok((char *)0,",",&saveptr)) {
- this->addSpecialist(Address(f),ZT_NETWORKCONFIG_SPECIALIST_TYPE_ACTIVE_BRIDGE);
- }
- }
-
- if (d.get(ZT_NETWORKCONFIG_DICT_KEY_RELAYS_OLD,tmp2,sizeof(tmp2)) > 0) {
- char *saveptr = (char *)0;
- for(char *f=Utils::stok(tmp2,",",&saveptr);(f);f=Utils::stok((char *)0,",",&saveptr)) {
- char tmp3[256];
- Utils::scopy(tmp3,sizeof(tmp3),f);
-
- InetAddress phy;
- char *semi = tmp3;
- while (*semi) {
- if (*semi == ';') {
- *semi = (char)0;
- ++semi;
- phy = InetAddress(semi);
- } else ++semi;
- }
- Address zt(tmp3);
-
- this->addSpecialist(zt,ZT_NETWORKCONFIG_SPECIALIST_TYPE_NETWORK_PREFERRED_RELAY);
- if ((phy)&&(this->pinnedCount < ZT_MAX_NETWORK_PINNED)) {
- this->pinned[this->pinnedCount].zt = zt;
- this->pinned[this->pinnedCount].phy = phy;
- ++this->pinnedCount;
- }
+ this->addSpecialist(Address(Utils::hexStrToU64(f)),ZT_NETWORKCONFIG_SPECIALIST_TYPE_ACTIVE_BRIDGE);
}
}
#else
+ delete tmp;
return false;
#endif // ZT_SUPPORT_OLD_STYLE_NETCONF
} else {
@@ -375,116 +295,76 @@ bool NetworkConfig::fromDictionary(const Dictionary<ZT_NETWORKCONFIG_DICT_CAPACI
this->flags = d.getUI(ZT_NETWORKCONFIG_DICT_KEY_FLAGS,0);
this->type = (ZT_VirtualNetworkType)d.getUI(ZT_NETWORKCONFIG_DICT_KEY_TYPE,(uint64_t)ZT_NETWORK_TYPE_PRIVATE);
- if (d.get(ZT_NETWORKCONFIG_DICT_KEY_COM,tmp)) {
- this->com.deserialize(tmp,0);
+ if (d.get(ZT_NETWORKCONFIG_DICT_KEY_COM,*tmp))
+ this->com.deserialize(*tmp,0);
+
+ if (d.get(ZT_NETWORKCONFIG_DICT_KEY_CAPABILITIES,*tmp)) {
+ try {
+ unsigned int p = 0;
+ while (p < tmp->size()) {
+ Capability cap;
+ p += cap.deserialize(*tmp,p);
+ this->capabilities[this->capabilityCount++] = cap;
+ }
+ } catch ( ... ) {}
+ std::sort(&(this->capabilities[0]),&(this->capabilities[this->capabilityCount]));
}
- if (d.get(ZT_NETWORKCONFIG_DICT_KEY_SPECIALISTS,tmp)) {
+ if (d.get(ZT_NETWORKCONFIG_DICT_KEY_TAGS,*tmp)) {
+ try {
+ unsigned int p = 0;
+ while (p < tmp->size()) {
+ Tag tag;
+ p += tag.deserialize(*tmp,p);
+ this->tags[this->tagCount++] = tag;
+ }
+ } catch ( ... ) {}
+ std::sort(&(this->tags[0]),&(this->tags[this->tagCount]));
+ }
+
+ if (d.get(ZT_NETWORKCONFIG_DICT_KEY_CERTIFICATES_OF_OWNERSHIP,*tmp)) {
unsigned int p = 0;
- while (((p + 8) <= tmp.size())&&(specialistCount < ZT_MAX_NETWORK_SPECIALISTS)) {
- this->specialists[this->specialistCount++] = tmp.at<uint64_t>(p);
- p += 8;
+ while (p < tmp->size()) {
+ if (certificateOfOwnershipCount < ZT_MAX_CERTIFICATES_OF_OWNERSHIP)
+ p += certificatesOfOwnership[certificateOfOwnershipCount++].deserialize(*tmp,p);
+ else {
+ CertificateOfOwnership foo;
+ p += foo.deserialize(*tmp,p);
+ }
}
}
- if (d.get(ZT_NETWORKCONFIG_DICT_KEY_ROUTES,tmp)) {
+ if (d.get(ZT_NETWORKCONFIG_DICT_KEY_SPECIALISTS,*tmp)) {
unsigned int p = 0;
- while ((p < tmp.size())&&(routeCount < ZT_MAX_NETWORK_ROUTES)) {
- p += reinterpret_cast<InetAddress *>(&(this->routes[this->routeCount].target))->deserialize(tmp,p);
- p += reinterpret_cast<InetAddress *>(&(this->routes[this->routeCount].via))->deserialize(tmp,p);
- this->routes[this->routeCount].flags = tmp.at<uint16_t>(p); p += 2;
- this->routes[this->routeCount].metric = tmp.at<uint16_t>(p); p += 2;
- ++this->routeCount;
+ while ((p + 8) <= tmp->size()) {
+ if (specialistCount < ZT_MAX_NETWORK_SPECIALISTS)
+ this->specialists[this->specialistCount++] = tmp->at<uint64_t>(p);
+ p += 8;
}
}
- if (d.get(ZT_NETWORKCONFIG_DICT_KEY_STATIC_IPS,tmp)) {
+ if (d.get(ZT_NETWORKCONFIG_DICT_KEY_ROUTES,*tmp)) {
unsigned int p = 0;
- while ((p < tmp.size())&&(staticIpCount < ZT_MAX_ZT_ASSIGNED_ADDRESSES)) {
- p += this->staticIps[this->staticIpCount++].deserialize(tmp,p);
+ while ((p < tmp->size())&&(routeCount < ZT_MAX_NETWORK_ROUTES)) {
+ p += reinterpret_cast<InetAddress *>(&(this->routes[this->routeCount].target))->deserialize(*tmp,p);
+ p += reinterpret_cast<InetAddress *>(&(this->routes[this->routeCount].via))->deserialize(*tmp,p);
+ this->routes[this->routeCount].flags = tmp->at<uint16_t>(p); p += 2;
+ this->routes[this->routeCount].metric = tmp->at<uint16_t>(p); p += 2;
+ ++this->routeCount;
}
}
- if (d.get(ZT_NETWORKCONFIG_DICT_KEY_PINNED,tmp)) {
+ if (d.get(ZT_NETWORKCONFIG_DICT_KEY_STATIC_IPS,*tmp)) {
unsigned int p = 0;
- while ((p < tmp.size())&&(pinnedCount < ZT_MAX_NETWORK_PINNED)) {
- this->pinned[this->pinnedCount].zt.setTo(tmp.field(p,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH); p += ZT_ADDRESS_LENGTH;
- p += this->pinned[this->pinnedCount].phy.deserialize(tmp,p);
- ++this->pinnedCount;
+ while ((p < tmp->size())&&(staticIpCount < ZT_MAX_ZT_ASSIGNED_ADDRESSES)) {
+ p += this->staticIps[this->staticIpCount++].deserialize(*tmp,p);
}
}
- if (d.get(ZT_NETWORKCONFIG_DICT_KEY_RULES,tmp)) {
+ if (d.get(ZT_NETWORKCONFIG_DICT_KEY_RULES,*tmp)) {
+ this->ruleCount = 0;
unsigned int p = 0;
- while ((p < tmp.size())&&(ruleCount < ZT_MAX_NETWORK_RULES)) {
- rules[ruleCount].t = (uint8_t)tmp[p++];
- unsigned int fieldLen = (unsigned int)tmp[p++];
- switch((ZT_VirtualNetworkRuleType)(rules[ruleCount].t & 0x7f)) {
- default:
- break;
- case ZT_NETWORK_RULE_ACTION_TEE:
- case ZT_NETWORK_RULE_ACTION_REDIRECT:
- case ZT_NETWORK_RULE_MATCH_SOURCE_ZEROTIER_ADDRESS:
- case ZT_NETWORK_RULE_MATCH_DEST_ZEROTIER_ADDRESS:
- rules[ruleCount].v.zt = Address(tmp.field(p,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH).toInt();
- break;
- case ZT_NETWORK_RULE_MATCH_VLAN_ID:
- rules[ruleCount].v.vlanId = tmp.at<uint16_t>(p);
- break;
- case ZT_NETWORK_RULE_MATCH_VLAN_PCP:
- rules[ruleCount].v.vlanPcp = (uint8_t)tmp[p];
- break;
- case ZT_NETWORK_RULE_MATCH_VLAN_DEI:
- rules[ruleCount].v.vlanDei = (uint8_t)tmp[p];
- break;
- case ZT_NETWORK_RULE_MATCH_ETHERTYPE:
- rules[ruleCount].v.etherType = tmp.at<uint16_t>(p);
- break;
- case ZT_NETWORK_RULE_MATCH_MAC_SOURCE:
- case ZT_NETWORK_RULE_MATCH_MAC_DEST:
- memcpy(rules[ruleCount].v.mac,tmp.field(p,6),6);
- break;
- case ZT_NETWORK_RULE_MATCH_IPV4_SOURCE:
- case ZT_NETWORK_RULE_MATCH_IPV4_DEST:
- memcpy(&(rules[ruleCount].v.ipv4.ip),tmp.field(p,4),4);
- rules[ruleCount].v.ipv4.mask = (uint8_t)tmp[p + 4];
- break;
- case ZT_NETWORK_RULE_MATCH_IPV6_SOURCE:
- case ZT_NETWORK_RULE_MATCH_IPV6_DEST:
- memcpy(rules[ruleCount].v.ipv6.ip,tmp.field(p,16),16);
- rules[ruleCount].v.ipv6.mask = (uint8_t)tmp[p + 16];
- break;
- case ZT_NETWORK_RULE_MATCH_IP_TOS:
- rules[ruleCount].v.ipTos = (uint8_t)tmp[p];
- break;
- case ZT_NETWORK_RULE_MATCH_IP_PROTOCOL:
- rules[ruleCount].v.ipProtocol = (uint8_t)tmp[p];
- break;
- case ZT_NETWORK_RULE_MATCH_IP_SOURCE_PORT_RANGE:
- case ZT_NETWORK_RULE_MATCH_IP_DEST_PORT_RANGE:
- rules[ruleCount].v.port[0] = tmp.at<uint16_t>(p);
- rules[ruleCount].v.port[1] = tmp.at<uint16_t>(p + 2);
- break;
- case ZT_NETWORK_RULE_MATCH_CHARACTERISTICS:
- rules[ruleCount].v.characteristics = tmp.at<uint64_t>(p);
- break;
- case ZT_NETWORK_RULE_MATCH_FRAME_SIZE_RANGE:
- rules[ruleCount].v.frameSize[0] = tmp.at<uint16_t>(p);
- rules[ruleCount].v.frameSize[0] = tmp.at<uint16_t>(p + 2);
- break;
- case ZT_NETWORK_RULE_MATCH_TCP_RELATIVE_SEQUENCE_NUMBER_RANGE:
- rules[ruleCount].v.tcpseq[0] = tmp.at<uint32_t>(p);
- rules[ruleCount].v.tcpseq[1] = tmp.at<uint32_t>(p + 4);
- break;
- case ZT_NETWORK_RULE_MATCH_COM_FIELD_GE:
- case ZT_NETWORK_RULE_MATCH_COM_FIELD_LE:
- rules[ruleCount].v.comIV[0] = tmp.at<uint64_t>(p);
- rules[ruleCount].v.comIV[1] = tmp.at<uint64_t>(p + 8);
- break;
- }
- p += fieldLen;
- ++ruleCount;
- }
+ Capability::deserializeRules(*tmp,p,this->rules,this->ruleCount,ZT_MAX_NETWORK_RULES);
}
}
@@ -492,8 +372,10 @@ bool NetworkConfig::fromDictionary(const Dictionary<ZT_NETWORKCONFIG_DICT_CAPACI
//dump();
//printf("~~~\n");
+ delete tmp;
return true;
} catch ( ... ) {
+ delete tmp;
return false;
}
}
diff --git a/node/NetworkConfig.hpp b/node/NetworkConfig.hpp
index 5271c5ad..44066c86 100644
--- a/node/NetworkConfig.hpp
+++ b/node/NetworkConfig.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_NETWORKCONFIG_HPP
@@ -35,12 +43,30 @@
#include "MulticastGroup.hpp"
#include "Address.hpp"
#include "CertificateOfMembership.hpp"
+#include "CertificateOfOwnership.hpp"
+#include "Capability.hpp"
+#include "Tag.hpp"
#include "Dictionary.hpp"
+#include "Hashtable.hpp"
+#include "Identity.hpp"
+#include "Utils.hpp"
+#include "Trace.hpp"
/**
- * Flag: allow passive bridging (experimental)
+ * Default maximum time delta for COMs, tags, and capabilities
+ *
+ * The current value is two hours, providing ample time for a controller to
+ * experience fail-over, etc.
*/
-#define ZT_NETWORKCONFIG_FLAG_ALLOW_PASSIVE_BRIDGING 0x0000000000000001ULL
+#define ZT_NETWORKCONFIG_DEFAULT_CREDENTIAL_TIME_MAX_MAX_DELTA 7200000ULL
+
+/**
+ * Default minimum credential TTL and maxDelta for COM timestamps
+ *
+ * This is just slightly over three minutes and provides three retries for
+ * all currently online members to refresh.
+ */
+#define ZT_NETWORKCONFIG_DEFAULT_CREDENTIAL_TIME_MIN_MAX_DELTA 185000ULL
/**
* Flag: enable broadcast
@@ -53,36 +79,72 @@
#define ZT_NETWORKCONFIG_FLAG_ENABLE_IPV6_NDP_EMULATION 0x0000000000000004ULL
/**
- * Device is a network preferred relay
+ * Flag: result of unrecognized MATCH entries in a rules table: match if set, no-match if clear
*/
-#define ZT_NETWORKCONFIG_SPECIALIST_TYPE_NETWORK_PREFERRED_RELAY 0x0000010000000000ULL
+#define ZT_NETWORKCONFIG_FLAG_RULES_RESULT_OF_UNSUPPORTED_MATCH 0x0000000000000008ULL
/**
- * Device is an active bridge
+ * Flag: disable frame compression
+ */
+#define ZT_NETWORKCONFIG_FLAG_DISABLE_COMPRESSION 0x0000000000000010ULL
+
+/**
+ * Device can bridge to other Ethernet networks and gets unknown recipient multicasts
*/
#define ZT_NETWORKCONFIG_SPECIALIST_TYPE_ACTIVE_BRIDGE 0x0000020000000000ULL
/**
- * An anchor is a device that is willing to be one and has been online/stable for a long time on this network
+ * Anchors are stable devices on this network that can act like roots when none are up
*/
#define ZT_NETWORKCONFIG_SPECIALIST_TYPE_ANCHOR 0x0000040000000000ULL
+/**
+ * Designated multicast replicators replicate multicast in place of sender-side replication
+ */
+#define ZT_NETWORKCONFIG_SPECIALIST_TYPE_MULTICAST_REPLICATOR 0x0000080000000000ULL
+
namespace ZeroTier {
-// Maximum size of a network config dictionary (can be increased)
-#define ZT_NETWORKCONFIG_DICT_CAPACITY 8194
+// Dictionary capacity needed for max size network config
+#define ZT_NETWORKCONFIG_DICT_CAPACITY (1024 + (sizeof(ZT_VirtualNetworkRule) * ZT_MAX_NETWORK_RULES) + (sizeof(Capability) * ZT_MAX_NETWORK_CAPABILITIES) + (sizeof(Tag) * ZT_MAX_NETWORK_TAGS) + (sizeof(CertificateOfOwnership) * ZT_MAX_CERTIFICATES_OF_OWNERSHIP))
+
+// Dictionary capacity needed for max size network meta-data
+#define ZT_NETWORKCONFIG_METADATA_DICT_CAPACITY 1024
// Network config version
-#define ZT_NETWORKCONFIG_VERSION 6
+#define ZT_NETWORKCONFIG_VERSION 7
// Fields for meta-data sent with network config requests
+
+// Network config version
#define ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_VERSION "v"
+// Protocol version (see Packet.hpp)
#define ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_PROTOCOL_VERSION "pv"
+// Software vendor
+#define ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_NODE_VENDOR "vend"
+// Software major version
#define ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_NODE_MAJOR_VERSION "majv"
+// Software minor version
#define ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_NODE_MINOR_VERSION "minv"
+// Software revision
#define ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_NODE_REVISION "revv"
+// Rules engine revision
+#define ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_RULES_ENGINE_REV "revr"
+// Maximum number of rules per network this node can accept
+#define ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_MAX_NETWORK_RULES "mr"
+// Maximum number of capabilities this node can accept
+#define ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_MAX_NETWORK_CAPABILITIES "mc"
+// Maximum number of rules per capability this node can accept
+#define ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_MAX_CAPABILITY_RULES "mcr"
+// Maximum number of tags this node can accept
+#define ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_MAX_NETWORK_TAGS "mt"
+// Network join authorization token (if any)
+#define ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_AUTH "a"
+// Network configuration meta-data flags
+#define ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_FLAGS "f"
// These dictionary keys are short so they don't take up much room.
+// By convention we use upper case for binary blobs, but it doesn't really matter.
// network config version
#define ZT_NETWORKCONFIG_DICT_KEY_VERSION "v"
@@ -94,6 +156,10 @@ namespace ZeroTier {
#define ZT_NETWORKCONFIG_DICT_KEY_REVISION "r"
// address of member
#define ZT_NETWORKCONFIG_DICT_KEY_ISSUED_TO "id"
+// remote trace target
+#define ZT_NETWORKCONFIG_DICT_KEY_REMOTE_TRACE_TARGET "tt"
+// remote trace level
+#define ZT_NETWORKCONFIG_DICT_KEY_REMOTE_TRACE_LEVEL "tl"
// flags(hex)
#define ZT_NETWORKCONFIG_DICT_KEY_FLAGS "f"
// integer(hex)
@@ -102,6 +168,10 @@ namespace ZeroTier {
#define ZT_NETWORKCONFIG_DICT_KEY_TYPE "t"
// text
#define ZT_NETWORKCONFIG_DICT_KEY_NAME "n"
+// network MTU
+#define ZT_NETWORKCONFIG_DICT_KEY_MTU "mtu"
+// credential time max delta in ms
+#define ZT_NETWORKCONFIG_DICT_KEY_CREDENTIAL_TIME_MAX_DELTA "ctmd"
// binary serialized certificate of membership
#define ZT_NETWORKCONFIG_DICT_KEY_COM "C"
// specialists (binary array of uint64_t)
@@ -110,16 +180,18 @@ namespace ZeroTier {
#define ZT_NETWORKCONFIG_DICT_KEY_ROUTES "RT"
// static IPs (binary blob)
#define ZT_NETWORKCONFIG_DICT_KEY_STATIC_IPS "I"
-// pinned address physical route mappings (binary blob)
-#define ZT_NETWORKCONFIG_DICT_KEY_PINNED "P"
// rules (binary blob)
#define ZT_NETWORKCONFIG_DICT_KEY_RULES "R"
+// capabilities (binary blobs)
+#define ZT_NETWORKCONFIG_DICT_KEY_CAPABILITIES "CAP"
+// tags (binary blobs)
+#define ZT_NETWORKCONFIG_DICT_KEY_TAGS "TAG"
+// tags (binary blobs)
+#define ZT_NETWORKCONFIG_DICT_KEY_CERTIFICATES_OF_OWNERSHIP "COO"
// Legacy fields -- these are obsoleted but are included when older clients query
// boolean (now a flag)
-#define ZT_NETWORKCONFIG_DICT_KEY_ALLOW_PASSIVE_BRIDGING_OLD "pb"
-// boolean (now a flag)
#define ZT_NETWORKCONFIG_DICT_KEY_ENABLE_BROADCAST_OLD "eb"
// IP/bits[,IP/bits,...]
// Note that IPs that end in all zeroes are routes with no assignment in them.
@@ -138,6 +210,8 @@ namespace ZeroTier {
// node;IP/port[,node;IP/port]
#define ZT_NETWORKCONFIG_DICT_KEY_RELAYS_OLD "rl"
+// End legacy fields
+
/**
* Network configuration received from network controller nodes
*
@@ -147,93 +221,9 @@ namespace ZeroTier {
class NetworkConfig
{
public:
- /**
- * Network preferred relay with optional physical endpoint addresses
- *
- * This is used by the convenience relays() method.
- */
- struct Relay
- {
- Address address;
- InetAddress phy4,phy6;
- };
-
- /**
- * Create an instance of a NetworkConfig for the test network ID
- *
- * The test network ID is defined as ZT_TEST_NETWORK_ID. This is a
- * "fake" network with no real controller and default options.
- *
- * @param self This node's ZT address
- * @return Configuration for test network ID
- */
- static inline NetworkConfig createTestNetworkConfig(const Address &self)
- {
- NetworkConfig nc;
-
- nc.networkId = ZT_TEST_NETWORK_ID;
- nc.timestamp = 1;
- nc.revision = 1;
- nc.issuedTo = self;
- nc.multicastLimit = ZT_MULTICAST_DEFAULT_LIMIT;
- nc.flags = ZT_NETWORKCONFIG_FLAG_ENABLE_BROADCAST;
- nc.type = ZT_NETWORK_TYPE_PUBLIC;
-
- nc.rules[0].t = ZT_NETWORK_RULE_ACTION_ACCEPT;
- nc.ruleCount = 1;
-
- Utils::snprintf(nc.name,sizeof(nc.name),"ZT_TEST_NETWORK");
-
- // Make up a V4 IP from 'self' in the 10.0.0.0/8 range -- no
- // guarantee of uniqueness but collisions are unlikely.
- uint32_t ip = (uint32_t)((self.toInt() & 0x00ffffff) | 0x0a000000); // 10.x.x.x
- if ((ip & 0x000000ff) == 0x000000ff) ip ^= 0x00000001; // but not ending in .255
- if ((ip & 0x000000ff) == 0x00000000) ip ^= 0x00000001; // or .0
- nc.staticIps[0] = InetAddress(Utils::hton(ip),8);
-
- // Assign an RFC4193-compliant IPv6 address -- will never collide
- nc.staticIps[1] = InetAddress::makeIpv6rfc4193(ZT_TEST_NETWORK_ID,self.toInt());
-
- nc.staticIpCount = 2;
-
- return nc;
- }
-
- NetworkConfig()
- {
- memset(this,0,sizeof(NetworkConfig));
- }
-
- NetworkConfig(const NetworkConfig &nc)
- {
- memcpy(this,&nc,sizeof(NetworkConfig));
- }
-
- inline NetworkConfig &operator=(const NetworkConfig &nc)
- {
- memcpy(this,&nc,sizeof(NetworkConfig));
- return *this;
- }
-
- /**
- * @param etherType Ethernet frame type to check
- * @return True if allowed on this network
- */
- inline bool permitsEtherType(unsigned int etherType) const
- {
- unsigned int et = 0;
- for(unsigned int i=0;i<ruleCount;++i) {
- ZT_VirtualNetworkRuleType rt = (ZT_VirtualNetworkRuleType)(rules[i].t & 0x7f);
- if (rt == ZT_NETWORK_RULE_MATCH_ETHERTYPE) {
- et = rules[i].v.etherType;
- } else if (rt == ZT_NETWORK_RULE_ACTION_ACCEPT) {
- if ((!et)||(et == etherType))
- return true;
- et = 0;
- }
- }
- return false;
- }
+ NetworkConfig() { memset(this,0,sizeof(NetworkConfig)); }
+ NetworkConfig(const NetworkConfig &nc) { ZT_FAST_MEMCPY(this,&nc,sizeof(NetworkConfig)); }
+ inline NetworkConfig &operator=(const NetworkConfig &nc) { ZT_FAST_MEMCPY(this,&nc,sizeof(NetworkConfig)); return *this; }
/**
* Write this network config to a dictionary for transport
@@ -247,35 +237,35 @@ public:
/**
* Read this network config from a dictionary
*
- * @param d Dictionary
+ * @param d Dictionary (non-const since it might be modified during parse, should not be used after call)
* @return True if dictionary was valid and network config successfully initialized
*/
bool fromDictionary(const Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> &d);
/**
- * @return True if passive bridging is allowed (experimental)
+ * @return True if broadcast (ff:ff:ff:ff:ff:ff) address should work on this network
*/
- inline bool allowPassiveBridging() const throw() { return ((this->flags & ZT_NETWORKCONFIG_FLAG_ALLOW_PASSIVE_BRIDGING) != 0); }
+ inline bool enableBroadcast() const { return ((this->flags & ZT_NETWORKCONFIG_FLAG_ENABLE_BROADCAST) != 0); }
/**
- * @return True if broadcast (ff:ff:ff:ff:ff:ff) address should work on this network
+ * @return True if IPv6 NDP emulation should be allowed for certain "magic" IPv6 address patterns
*/
- inline bool enableBroadcast() const throw() { return ((this->flags & ZT_NETWORKCONFIG_FLAG_ENABLE_BROADCAST) != 0); }
+ inline bool ndpEmulation() const { return ((this->flags & ZT_NETWORKCONFIG_FLAG_ENABLE_IPV6_NDP_EMULATION) != 0); }
/**
- * @return True if IPv6 NDP emulation should be allowed for certain "magic" IPv6 address patterns
+ * @return True if frames should not be compressed
*/
- inline bool ndpEmulation() const throw() { return ((this->flags & ZT_NETWORKCONFIG_FLAG_ENABLE_IPV6_NDP_EMULATION) != 0); }
+ inline bool disableCompression() const { return ((this->flags & ZT_NETWORKCONFIG_FLAG_DISABLE_COMPRESSION) != 0); }
/**
* @return Network type is public (no access control)
*/
- inline bool isPublic() const throw() { return (this->type == ZT_NETWORK_TYPE_PUBLIC); }
+ inline bool isPublic() const { return (this->type == ZT_NETWORK_TYPE_PUBLIC); }
/**
* @return Network type is private (certificate access control)
*/
- inline bool isPrivate() const throw() { return (this->type == ZT_NETWORK_TYPE_PRIVATE); }
+ inline bool isPrivate() const { return (this->type == ZT_NETWORK_TYPE_PRIVATE); }
/**
* @return ZeroTier addresses of devices on this network designated as active bridges
@@ -290,9 +280,25 @@ public:
return r;
}
- /**
- * @return ZeroTier addresses of "anchor" devices on this network
- */
+ inline unsigned int activeBridges(Address ab[ZT_MAX_NETWORK_SPECIALISTS]) const
+ {
+ unsigned int c = 0;
+ for(unsigned int i=0;i<specialistCount;++i) {
+ if ((specialists[i] & ZT_NETWORKCONFIG_SPECIALIST_TYPE_ACTIVE_BRIDGE) != 0)
+ ab[c++] = specialists[i];
+ }
+ return c;
+ }
+
+ inline bool isActiveBridge(const Address &a) const
+ {
+ for(unsigned int i=0;i<specialistCount;++i) {
+ if (((specialists[i] & ZT_NETWORKCONFIG_SPECIALIST_TYPE_ACTIVE_BRIDGE) != 0)&&(a == specialists[i]))
+ return true;
+ }
+ return false;
+ }
+
inline std::vector<Address> anchors() const
{
std::vector<Address> r;
@@ -303,130 +309,81 @@ public:
return r;
}
- /**
- * Get pinned physical address for a given ZeroTier address, if any
- *
- * @param zt ZeroTier address
- * @param af Address family (e.g. AF_INET) or 0 for the first we find of any type
- * @return Physical address, if any
- */
- inline InetAddress findPinnedAddress(const Address &zt,unsigned int af) const
+ inline std::vector<Address> multicastReplicators() const
{
- for(unsigned int i=0;i<pinnedCount;++i) {
- if (pinned[i].zt == zt) {
- if ((af == 0)||((unsigned int)pinned[i].phy.ss_family == af))
- return pinned[i].phy;
- }
+ std::vector<Address> r;
+ for(unsigned int i=0;i<specialistCount;++i) {
+ if ((specialists[i] & ZT_NETWORKCONFIG_SPECIALIST_TYPE_MULTICAST_REPLICATOR) != 0)
+ r.push_back(Address(specialists[i]));
}
- return InetAddress();
+ return r;
}
- /**
- * This gets network preferred relays with their static physical address if one is defined
- *
- * @return Network-preferred relays for this network (if none, only roots will be used)
- */
- inline std::vector<Relay> relays() const
+ inline unsigned int multicastReplicators(Address mr[ZT_MAX_NETWORK_SPECIALISTS]) const
{
- std::vector<Relay> r;
+ unsigned int c = 0;
for(unsigned int i=0;i<specialistCount;++i) {
- if ((specialists[i] & ZT_NETWORKCONFIG_SPECIALIST_TYPE_NETWORK_PREFERRED_RELAY) != 0) {
- r.push_back(Relay());
- r.back().address = specialists[i];
- r.back().phy4 = findPinnedAddress(r.back().address,AF_INET);
- r.back().phy6 = findPinnedAddress(r.back().address,AF_INET6);
- }
+ if ((specialists[i] & ZT_NETWORKCONFIG_SPECIALIST_TYPE_MULTICAST_REPLICATOR) != 0)
+ mr[c++] = specialists[i];
}
- return r;
+ return c;
}
- /**
- * @param fromPeer Peer attempting to bridge other Ethernet peers onto network
- * @return True if this network allows bridging
- */
- inline bool permitsBridging(const Address &fromPeer) const
+ inline bool isMulticastReplicator(const Address &a) const
{
- if ((flags & ZT_NETWORKCONFIG_FLAG_ALLOW_PASSIVE_BRIDGING) != 0)
- return true;
for(unsigned int i=0;i<specialistCount;++i) {
- if ((fromPeer == specialists[i])&&((specialists[i] & ZT_NETWORKCONFIG_SPECIALIST_TYPE_ACTIVE_BRIDGE) != 0))
+ if (((specialists[i] & ZT_NETWORKCONFIG_SPECIALIST_TYPE_MULTICAST_REPLICATOR) != 0)&&(a == specialists[i]))
return true;
}
return false;
}
- /**
- * Iterate through relays efficiently
- *
- * @param ptr Value-result parameter -- start by initializing with zero, then call until return is null
- * @return Address of relay or NULL if no more
- */
- Address nextRelay(unsigned int &ptr) const
+ inline std::vector<Address> alwaysContactAddresses() const
+ {
+ std::vector<Address> r;
+ for(unsigned int i=0;i<specialistCount;++i) {
+ if ((specialists[i] & (ZT_NETWORKCONFIG_SPECIALIST_TYPE_ANCHOR | ZT_NETWORKCONFIG_SPECIALIST_TYPE_MULTICAST_REPLICATOR)) != 0)
+ r.push_back(Address(specialists[i]));
+ }
+ return r;
+ }
+
+ inline unsigned int alwaysContactAddresses(Address ac[ZT_MAX_NETWORK_SPECIALISTS]) const
+ {
+ unsigned int c = 0;
+ for(unsigned int i=0;i<specialistCount;++i) {
+ if ((specialists[i] & (ZT_NETWORKCONFIG_SPECIALIST_TYPE_ANCHOR | ZT_NETWORKCONFIG_SPECIALIST_TYPE_MULTICAST_REPLICATOR)) != 0)
+ ac[c++] = specialists[i];
+ }
+ return c;
+ }
+
+ inline void alwaysContactAddresses(Hashtable< Address,std::vector<InetAddress> > &a) const
{
- while (ptr < specialistCount) {
- if ((specialists[ptr] & ZT_NETWORKCONFIG_SPECIALIST_TYPE_NETWORK_PREFERRED_RELAY) != 0) {
- return Address(specialists[ptr++]);
- } else {
- ++ptr;
+ for(unsigned int i=0;i<specialistCount;++i) {
+ if ((specialists[i] & (ZT_NETWORKCONFIG_SPECIALIST_TYPE_ANCHOR | ZT_NETWORKCONFIG_SPECIALIST_TYPE_MULTICAST_REPLICATOR)) != 0) {
+ a[Address(specialists[i])];
}
}
- return Address();
}
/**
- * @param zt ZeroTier address
- * @return True if this address is a relay
+ * @param fromPeer Peer attempting to bridge other Ethernet peers onto network
+ * @return True if this network allows bridging
*/
- bool isRelay(const Address &zt) const
+ inline bool permitsBridging(const Address &fromPeer) const
{
for(unsigned int i=0;i<specialistCount;++i) {
- if ((zt == specialists[i])&&((specialists[i] & ZT_NETWORKCONFIG_SPECIALIST_TYPE_NETWORK_PREFERRED_RELAY) != 0))
+ if ((fromPeer == specialists[i])&&((specialists[i] & ZT_NETWORKCONFIG_SPECIALIST_TYPE_ACTIVE_BRIDGE) != 0))
return true;
}
return false;
}
- /**
- * @return True if this network config is non-NULL
- */
- inline operator bool() const throw() { return (networkId != 0); }
-
+ inline operator bool() const { return (networkId != 0); }
inline bool operator==(const NetworkConfig &nc) const { return (memcmp(this,&nc,sizeof(NetworkConfig)) == 0); }
inline bool operator!=(const NetworkConfig &nc) const { return (!(*this == nc)); }
- /*
- inline void dump() const
- {
- printf("networkId==%.16llx\n",networkId);
- printf("timestamp==%llu\n",timestamp);
- printf("revision==%llu\n",revision);
- printf("issuedTo==%.10llx\n",issuedTo.toInt());
- printf("multicastLimit==%u\n",multicastLimit);
- printf("flags=%.8lx\n",(unsigned long)flags);
- printf("specialistCount==%u\n",specialistCount);
- for(unsigned int i=0;i<specialistCount;++i)
- printf(" specialists[%u]==%.16llx\n",i,specialists[i]);
- printf("routeCount==%u\n",routeCount);
- for(unsigned int i=0;i<routeCount;++i) {
- printf(" routes[i].target==%s\n",reinterpret_cast<const InetAddress *>(&(routes[i].target))->toString().c_str());
- printf(" routes[i].via==%s\n",reinterpret_cast<const InetAddress *>(&(routes[i].via))->toIpString().c_str());
- printf(" routes[i].flags==%.4x\n",(unsigned int)routes[i].flags);
- printf(" routes[i].metric==%u\n",(unsigned int)routes[i].metric);
- }
- printf("staticIpCount==%u\n",staticIpCount);
- for(unsigned int i=0;i<staticIpCount;++i)
- printf(" staticIps[i]==%s\n",staticIps[i].toString().c_str());
- printf("pinnedCount==%u\n",pinnedCount);
- for(unsigned int i=0;i<pinnedCount;++i) {
- printf(" pinned[i].zt==%s\n",pinned[i].zt.toString().c_str());
- printf(" pinned[i].phy==%s\n",pinned[i].phy.toString().c_str());
- }
- printf("ruleCount==%u\n",ruleCount);
- printf("name==%s\n",name);
- printf("com==%s\n",com.toString().c_str());
- }
- */
-
/**
* Add a specialist or mask flags if already present
*
@@ -453,6 +410,24 @@ public:
return false;
}
+ const Capability *capability(const uint32_t id) const
+ {
+ for(unsigned int i=0;i<capabilityCount;++i) {
+ if (capabilities[i].id() == id)
+ return &(capabilities[i]);
+ }
+ return (Capability *)0;
+ }
+
+ const Tag *tag(const uint32_t id) const
+ {
+ for(unsigned int i=0;i<tagCount;++i) {
+ if (tags[i].id() == id)
+ return &(tags[i]);
+ }
+ return (Tag *)0;
+ }
+
/**
* Network ID that this configuration applies to
*/
@@ -461,7 +436,12 @@ public:
/**
* Controller-side time of config generation/issue
*/
- uint64_t timestamp;
+ int64_t timestamp;
+
+ /**
+ * Max difference between timestamp and tag/capability timestamp
+ */
+ int64_t credentialTimeMaxDelta;
/**
* Controller-side revision counter for this configuration
@@ -474,11 +454,26 @@ public:
Address issuedTo;
/**
+ * If non-NULL, remote traces related to this network are sent here
+ */
+ Address remoteTraceTarget;
+
+ /**
* Flags (64-bit)
*/
uint64_t flags;
/**
+ * Remote trace level
+ */
+ Trace::Level remoteTraceLevel;
+
+ /**
+ * Network MTU
+ */
+ unsigned int mtu;
+
+ /**
* Maximum number of recipients per multicast (not including active bridges)
*/
unsigned int multicastLimit;
@@ -499,14 +494,24 @@ public:
unsigned int staticIpCount;
/**
- * Number of pinned devices (devices with physical address hints)
+ * Number of rule table entries
*/
- unsigned int pinnedCount;
+ unsigned int ruleCount;
/**
- * Number of rule table entries
+ * Number of capabilities
*/
- unsigned int ruleCount;
+ unsigned int capabilityCount;
+
+ /**
+ * Number of tags
+ */
+ unsigned int tagCount;
+
+ /**
+ * Number of certificates of ownership
+ */
+ unsigned int certificateOfOwnershipCount;
/**
* Specialist devices
@@ -527,20 +532,24 @@ public:
InetAddress staticIps[ZT_MAX_ZT_ASSIGNED_ADDRESSES];
/**
- * Pinned devices with physical address hints
- *
- * These can be used to specify a physical address where a given device
- * can be reached. It's usually used with network relays (specialists).
+ * Base network rules
*/
- struct {
- Address zt;
- InetAddress phy;
- } pinned[ZT_MAX_NETWORK_PINNED];
+ ZT_VirtualNetworkRule rules[ZT_MAX_NETWORK_RULES];
/**
- * Rules table
+ * Capabilities for this node on this network, in ascending order of capability ID
*/
- ZT_VirtualNetworkRule rules[ZT_MAX_NETWORK_RULES];
+ Capability capabilities[ZT_MAX_NETWORK_CAPABILITIES];
+
+ /**
+ * Tags for this node on this network, in ascending order of tag ID
+ */
+ Tag tags[ZT_MAX_NETWORK_TAGS];
+
+ /**
+ * Certificates of ownership for this network member
+ */
+ CertificateOfOwnership certificatesOfOwnership[ZT_MAX_CERTIFICATES_OF_OWNERSHIP];
/**
* Network type (currently just public or private)
diff --git a/node/NetworkController.hpp b/node/NetworkController.hpp
index fa90fb75..393bcc91 100644
--- a/node/NetworkController.hpp
+++ b/node/NetworkController.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_NETWORKCONFIGMASTER_HPP
@@ -24,12 +32,12 @@
#include "Constants.hpp"
#include "Dictionary.hpp"
#include "NetworkConfig.hpp"
+#include "Revocation.hpp"
+#include "Address.hpp"
namespace ZeroTier {
-class RuntimeEnvironment;
class Identity;
-class Address;
struct InetAddress;
/**
@@ -38,45 +46,77 @@ struct InetAddress;
class NetworkController
{
public:
+ enum ErrorCode
+ {
+ NC_ERROR_NONE = 0,
+ NC_ERROR_OBJECT_NOT_FOUND = 1,
+ NC_ERROR_ACCESS_DENIED = 2,
+ NC_ERROR_INTERNAL_SERVER_ERROR = 3
+ };
+
/**
- * Return value of doNetworkConfigRequest
+ * Interface for sender used to send pushes and replies
*/
- enum ResultCode
+ class Sender
{
- NETCONF_QUERY_OK = 0,
- NETCONF_QUERY_OBJECT_NOT_FOUND = 1,
- NETCONF_QUERY_ACCESS_DENIED = 2,
- NETCONF_QUERY_INTERNAL_SERVER_ERROR = 3,
- NETCONF_QUERY_IGNORE = 4
+ public:
+ /**
+ * Send a configuration to a remote peer
+ *
+ * @param nwid Network ID
+ * @param requestPacketId Request packet ID to send OK(NETWORK_CONFIG_REQUEST) or 0 to send NETWORK_CONFIG (push)
+ * @param destination Destination peer Address
+ * @param nc Network configuration to send
+ * @param sendLegacyFormatConfig If true, send an old-format network config
+ */
+ virtual void ncSendConfig(uint64_t nwid,uint64_t requestPacketId,const Address &destination,const NetworkConfig &nc,bool sendLegacyFormatConfig) = 0;
+
+ /**
+ * Send revocation to a node
+ *
+ * @param destination Destination node address
+ * @param rev Revocation to send
+ */
+ virtual void ncSendRevocation(const Address &destination,const Revocation &rev) = 0;
+
+ /**
+ * Send a network configuration request error
+ *
+ * @param nwid Network ID
+ * @param requestPacketId Request packet ID or 0 if none
+ * @param destination Destination peer Address
+ * @param errorCode Error code
+ */
+ virtual void ncSendError(uint64_t nwid,uint64_t requestPacketId,const Address &destination,NetworkController::ErrorCode errorCode) = 0;
};
NetworkController() {}
virtual ~NetworkController() {}
/**
- * Handle a network config request, sending replies if necessary
- *
- * This call is permitted to block, and may be called concurrently from more
- * than one thread. Implementations must use locks if needed.
+ * Called when this is added to a Node to initialize and supply info
*
- * On internal server errors, the 'error' field in result can be filled in
- * to indicate the error.
+ * @param signingId Identity for signing of network configurations, certs, etc.
+ * @param sender Sender implementation for sending replies or config pushes
+ */
+ virtual void init(const Identity &signingId,Sender *sender) = 0;
+
+ /**
+ * Handle a network configuration request
*
- * @param fromAddr Originating wire address or null address if packet is not direct (or from self)
- * @param signingId Identity that should be used to sign results -- must include private key
- * @param identity Originating peer ZeroTier identity
* @param nwid 64-bit network ID
+ * @param fromAddr Originating wire address or null address if packet is not direct (or from self)
+ * @param requestPacketId Packet ID of request packet or 0 if not initiated by remote request
+ * @param identity ZeroTier identity of originating peer
* @param metaData Meta-data bundled with request (if any)
- * @param nc NetworkConfig to fill with results
* @return Returns NETCONF_QUERY_OK if result 'nc' is valid, or an error code on error
*/
- virtual NetworkController::ResultCode doNetworkConfigRequest(
+ virtual void request(
+ uint64_t nwid,
const InetAddress &fromAddr,
- const Identity &signingId,
+ uint64_t requestPacketId,
const Identity &identity,
- uint64_t nwid,
- const Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> &metaData,
- NetworkConfig &nc) = 0;
+ const Dictionary<ZT_NETWORKCONFIG_METADATA_DICT_CAPACITY> &metaData) = 0;
};
} // namespace ZeroTier
diff --git a/node/Node.cpp b/node/Node.cpp
index 13085028..db511430 100644
--- a/node/Node.cpp
+++ b/node/Node.cpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#include <stdio.h>
@@ -25,6 +33,7 @@
#include "../version.h"
#include "Constants.hpp"
+#include "SharedPtr.hpp"
#include "Node.hpp"
#include "RuntimeEnvironment.hpp"
#include "NetworkController.hpp"
@@ -36,10 +45,8 @@
#include "Address.hpp"
#include "Identity.hpp"
#include "SelfAwareness.hpp"
-#include "Cluster.hpp"
-#include "DeferredPackets.hpp"
-
-const struct sockaddr_storage ZT_SOCKADDR_NULL = {0};
+#include "Network.hpp"
+#include "Trace.hpp"
namespace ZeroTier {
@@ -47,112 +54,126 @@ namespace ZeroTier {
/* Public Node interface (C++, exposed via CAPI bindings) */
/****************************************************************************/
-Node::Node(
- uint64_t now,
- void *uptr,
- ZT_DataStoreGetFunction dataStoreGetFunction,
- ZT_DataStorePutFunction dataStorePutFunction,
- ZT_WirePacketSendFunction wirePacketSendFunction,
- ZT_VirtualNetworkFrameFunction virtualNetworkFrameFunction,
- ZT_VirtualNetworkConfigFunction virtualNetworkConfigFunction,
- ZT_PathCheckFunction pathCheckFunction,
- ZT_EventCallback eventCallback) :
+Node::Node(void *uptr,void *tptr,const struct ZT_Node_Callbacks *callbacks,int64_t now) :
_RR(this),
RR(&_RR),
_uPtr(uptr),
- _dataStoreGetFunction(dataStoreGetFunction),
- _dataStorePutFunction(dataStorePutFunction),
- _wirePacketSendFunction(wirePacketSendFunction),
- _virtualNetworkFrameFunction(virtualNetworkFrameFunction),
- _virtualNetworkConfigFunction(virtualNetworkConfigFunction),
- _pathCheckFunction(pathCheckFunction),
- _eventCallback(eventCallback),
- _networks(),
- _networks_m(),
- _prngStreamPtr(0),
+ _networks(8),
_now(now),
_lastPingCheck(0),
- _lastHousekeepingRun(0)
+ _lastHousekeepingRun(0),
+ _lastMemoizedTraceSettings(0)
{
+ if (callbacks->version != 0)
+ throw ZT_EXCEPTION_INVALID_ARGUMENT;
+ ZT_FAST_MEMCPY(&_cb,callbacks,sizeof(ZT_Node_Callbacks));
+
+ // Initialize non-cryptographic PRNG from a good random source
+ Utils::getSecureRandom((void *)_prngState,sizeof(_prngState));
+
_online = false;
- // Use Salsa20 alone as a high-quality non-crypto PRNG
- {
- char foo[32];
- Utils::getSecureRandom(foo,32);
- _prng.init(foo,256,foo);
- memset(_prngStream,0,sizeof(_prngStream));
- _prng.encrypt12(_prngStream,_prngStream,sizeof(_prngStream));
+ memset(_expectingRepliesToBucketPtr,0,sizeof(_expectingRepliesToBucketPtr));
+ memset(_expectingRepliesTo,0,sizeof(_expectingRepliesTo));
+ memset(_lastIdentityVerification,0,sizeof(_lastIdentityVerification));
+
+ uint64_t idtmp[2];
+ idtmp[0] = 0; idtmp[1] = 0;
+ char tmp[2048];
+ int n = stateObjectGet(tptr,ZT_STATE_OBJECT_IDENTITY_SECRET,idtmp,tmp,sizeof(tmp) - 1);
+ if (n > 0) {
+ tmp[n] = (char)0;
+ if (RR->identity.fromString(tmp)) {
+ RR->identity.toString(false,RR->publicIdentityStr);
+ RR->identity.toString(true,RR->secretIdentityStr);
+ } else {
+ n = -1;
+ }
}
- {
- std::string idtmp(dataStoreGet("identity.secret"));
- if ((!idtmp.length())||(!RR->identity.fromString(idtmp))||(!RR->identity.hasPrivate())) {
- TRACE("identity.secret not found, generating...");
- RR->identity.generate();
- idtmp = RR->identity.toString(true);
- if (!dataStorePut("identity.secret",idtmp,true))
- throw std::runtime_error("unable to write identity.secret");
- }
- RR->publicIdentityStr = RR->identity.toString(false);
- RR->secretIdentityStr = RR->identity.toString(true);
- idtmp = dataStoreGet("identity.public");
- if (idtmp != RR->publicIdentityStr) {
- if (!dataStorePut("identity.public",RR->publicIdentityStr,false))
- throw std::runtime_error("unable to write identity.public");
+ if (n <= 0) {
+ RR->identity.generate();
+ RR->identity.toString(false,RR->publicIdentityStr);
+ RR->identity.toString(true,RR->secretIdentityStr);
+ idtmp[0] = RR->identity.address().toInt(); idtmp[1] = 0;
+ stateObjectPut(tptr,ZT_STATE_OBJECT_IDENTITY_SECRET,idtmp,RR->secretIdentityStr,(unsigned int)strlen(RR->secretIdentityStr));
+ stateObjectPut(tptr,ZT_STATE_OBJECT_IDENTITY_PUBLIC,idtmp,RR->publicIdentityStr,(unsigned int)strlen(RR->publicIdentityStr));
+ } else {
+ idtmp[0] = RR->identity.address().toInt(); idtmp[1] = 0;
+ n = stateObjectGet(tptr,ZT_STATE_OBJECT_IDENTITY_PUBLIC,idtmp,tmp,sizeof(tmp) - 1);
+ if ((n > 0)&&(n < (int)sizeof(RR->publicIdentityStr))&&(n < (int)sizeof(tmp))) {
+ if (memcmp(tmp,RR->publicIdentityStr,n))
+ stateObjectPut(tptr,ZT_STATE_OBJECT_IDENTITY_PUBLIC,idtmp,RR->publicIdentityStr,(unsigned int)strlen(RR->publicIdentityStr));
}
}
+ char *m = (char *)0;
try {
- RR->sw = new Switch(RR);
- RR->mc = new Multicaster(RR);
- RR->topology = new Topology(RR);
- RR->sa = new SelfAwareness(RR);
- RR->dp = new DeferredPackets(RR);
+ const unsigned long ts = sizeof(Trace) + (((sizeof(Trace) & 0xf) != 0) ? (16 - (sizeof(Trace) & 0xf)) : 0);
+ const unsigned long sws = sizeof(Switch) + (((sizeof(Switch) & 0xf) != 0) ? (16 - (sizeof(Switch) & 0xf)) : 0);
+ const unsigned long mcs = sizeof(Multicaster) + (((sizeof(Multicaster) & 0xf) != 0) ? (16 - (sizeof(Multicaster) & 0xf)) : 0);
+ const unsigned long topologys = sizeof(Topology) + (((sizeof(Topology) & 0xf) != 0) ? (16 - (sizeof(Topology) & 0xf)) : 0);
+ const unsigned long sas = sizeof(SelfAwareness) + (((sizeof(SelfAwareness) & 0xf) != 0) ? (16 - (sizeof(SelfAwareness) & 0xf)) : 0);
+
+ m = reinterpret_cast<char *>(::malloc(16 + ts + sws + mcs + topologys + sas));
+ if (!m)
+ throw std::bad_alloc();
+ RR->rtmem = m;
+ while (((uintptr_t)m & 0xf) != 0) ++m;
+
+ RR->t = new (m) Trace(RR);
+ m += ts;
+ RR->sw = new (m) Switch(RR);
+ m += sws;
+ RR->mc = new (m) Multicaster(RR);
+ m += mcs;
+ RR->topology = new (m) Topology(RR,tptr);
+ m += topologys;
+ RR->sa = new (m) SelfAwareness(RR);
} catch ( ... ) {
- delete RR->dp;
- delete RR->sa;
- delete RR->topology;
- delete RR->mc;
- delete RR->sw;
+ if (RR->sa) RR->sa->~SelfAwareness();
+ if (RR->topology) RR->topology->~Topology();
+ if (RR->mc) RR->mc->~Multicaster();
+ if (RR->sw) RR->sw->~Switch();
+ if (RR->t) RR->t->~Trace();
+ ::free(m);
throw;
}
- postEvent(ZT_EVENT_UP);
+ postEvent(tptr,ZT_EVENT_UP);
}
Node::~Node()
{
- Mutex::Lock _l(_networks_m);
-
- _networks.clear(); // ensure that networks are destroyed before shutdow
-
- RR->dpEnabled = 0;
- delete RR->dp;
- delete RR->sa;
- delete RR->topology;
- delete RR->mc;
- delete RR->sw;
-#ifdef ZT_ENABLE_CLUSTER
- delete RR->cluster;
-#endif
+ {
+ Mutex::Lock _l(_networks_m);
+ _networks.clear(); // destroy all networks before shutdown
+ }
+ if (RR->sa) RR->sa->~SelfAwareness();
+ if (RR->topology) RR->topology->~Topology();
+ if (RR->mc) RR->mc->~Multicaster();
+ if (RR->sw) RR->sw->~Switch();
+ if (RR->t) RR->t->~Trace();
+ ::free(RR->rtmem);
}
ZT_ResultCode Node::processWirePacket(
- uint64_t now,
- const struct sockaddr_storage *localAddress,
+ void *tptr,
+ int64_t now,
+ int64_t localSocket,
const struct sockaddr_storage *remoteAddress,
const void *packetData,
unsigned int packetLength,
- volatile uint64_t *nextBackgroundTaskDeadline)
+ volatile int64_t *nextBackgroundTaskDeadline)
{
_now = now;
- RR->sw->onRemotePacket(*(reinterpret_cast<const InetAddress *>(localAddress)),*(reinterpret_cast<const InetAddress *>(remoteAddress)),packetData,packetLength);
+ RR->sw->onRemotePacket(tptr,localSocket,*(reinterpret_cast<const InetAddress *>(remoteAddress)),packetData,packetLength);
return ZT_RESULT_OK;
}
ZT_ResultCode Node::processVirtualNetworkFrame(
- uint64_t now,
+ void *tptr,
+ int64_t now,
uint64_t nwid,
uint64_t sourceMac,
uint64_t destMac,
@@ -160,157 +181,145 @@ ZT_ResultCode Node::processVirtualNetworkFrame(
unsigned int vlanId,
const void *frameData,
unsigned int frameLength,
- volatile uint64_t *nextBackgroundTaskDeadline)
+ volatile int64_t *nextBackgroundTaskDeadline)
{
_now = now;
SharedPtr<Network> nw(this->network(nwid));
if (nw) {
- RR->sw->onLocalEthernet(nw,MAC(sourceMac),MAC(destMac),etherType,vlanId,frameData,frameLength);
+ RR->sw->onLocalEthernet(tptr,nw,MAC(sourceMac),MAC(destMac),etherType,vlanId,frameData,frameLength);
return ZT_RESULT_OK;
} else return ZT_RESULT_ERROR_NETWORK_NOT_FOUND;
}
+// Closure used to ping upstream and active/online peers
class _PingPeersThatNeedPing
{
public:
- _PingPeersThatNeedPing(const RuntimeEnvironment *renv,uint64_t now,const std::vector<NetworkConfig::Relay> &relays) :
- lastReceiveFromUpstream(0),
+ _PingPeersThatNeedPing(const RuntimeEnvironment *renv,void *tPtr,Hashtable< Address,std::vector<InetAddress> > &alwaysContact,int64_t now) :
RR(renv),
+ _tPtr(tPtr),
+ _alwaysContact(alwaysContact),
_now(now),
- _relays(relays),
- _world(RR->topology->world())
+ _bestCurrentUpstream(RR->topology->getUpstreamPeer())
{
}
- uint64_t lastReceiveFromUpstream; // tracks last time we got a packet from an 'upstream' peer like a root or a relay
-
inline void operator()(Topology &t,const SharedPtr<Peer> &p)
{
- bool upstream = false;
- InetAddress stableEndpoint4,stableEndpoint6;
-
- // If this is a world root, pick (if possible) both an IPv4 and an IPv6 stable endpoint to use if link isn't currently alive.
- for(std::vector<World::Root>::const_iterator r(_world.roots().begin());r!=_world.roots().end();++r) {
- if (r->identity == p->identity()) {
- upstream = true;
- for(unsigned long k=0,ptr=(unsigned long)RR->node->prng();k<(unsigned long)r->stableEndpoints.size();++k) {
- const InetAddress &addr = r->stableEndpoints[ptr++ % r->stableEndpoints.size()];
- if (!stableEndpoint4) {
- if (addr.ss_family == AF_INET)
- stableEndpoint4 = addr;
- }
- if (!stableEndpoint6) {
- if (addr.ss_family == AF_INET6)
- stableEndpoint6 = addr;
+ const std::vector<InetAddress> *const alwaysContactEndpoints = _alwaysContact.get(p->address());
+ if (alwaysContactEndpoints) {
+ const unsigned int sent = p->doPingAndKeepalive(_tPtr,_now);
+ bool contacted = (sent != 0);
+
+ if ((sent & 0x1) == 0) { // bit 0x1 == IPv4 sent
+ for(unsigned long k=0,ptr=(unsigned long)RR->node->prng();k<(unsigned long)alwaysContactEndpoints->size();++k) {
+ const InetAddress &addr = (*alwaysContactEndpoints)[ptr++ % alwaysContactEndpoints->size()];
+ if (addr.ss_family == AF_INET) {
+ p->sendHELLO(_tPtr,-1,addr,_now);
+ contacted = true;
+ break;
}
}
- break;
- }
- }
-
- if (!upstream) {
- // If I am a root server, only ping other root servers -- roots don't ping "down"
- // since that would just be a waste of bandwidth and could potentially cause route
- // flapping in Cluster mode.
- if (RR->topology->amRoot())
- return;
-
- // Check for network preferred relays, also considered 'upstream' and thus always
- // pinged to keep links up. If they have stable addresses we will try them there.
- for(std::vector<NetworkConfig::Relay>::const_iterator r(_relays.begin());r!=_relays.end();++r) {
- if (r->address == p->address()) {
- stableEndpoint4 = r->phy4;
- stableEndpoint6 = r->phy6;
- upstream = true;
- break;
- }
}
- }
- if (upstream) {
- // "Upstream" devices are roots and relays and get special treatment -- they stay alive
- // forever and we try to keep (if available) both IPv4 and IPv6 channels open to them.
- bool needToContactIndirect = true;
- if (p->doPingAndKeepalive(_now,AF_INET)) {
- needToContactIndirect = false;
- } else {
- if (stableEndpoint4) {
- needToContactIndirect = false;
- p->sendHELLO(InetAddress(),stableEndpoint4,_now);
- }
- }
- if (p->doPingAndKeepalive(_now,AF_INET6)) {
- needToContactIndirect = false;
- } else {
- if (stableEndpoint6) {
- needToContactIndirect = false;
- p->sendHELLO(InetAddress(),stableEndpoint6,_now);
+ if ((sent & 0x2) == 0) { // bit 0x2 == IPv6 sent
+ for(unsigned long k=0,ptr=(unsigned long)RR->node->prng();k<(unsigned long)alwaysContactEndpoints->size();++k) {
+ const InetAddress &addr = (*alwaysContactEndpoints)[ptr++ % alwaysContactEndpoints->size()];
+ if (addr.ss_family == AF_INET6) {
+ p->sendHELLO(_tPtr,-1,addr,_now);
+ contacted = true;
+ break;
+ }
}
}
- if (needToContactIndirect) {
- // If this is an upstream and we have no stable endpoint for either IPv4 or IPv6,
- // send a NOP indirectly if possible to see if we can get to this peer in any
- // way whatsoever. This will e.g. find network preferred relays that lack
- // stable endpoints by using root servers.
- Packet outp(p->address(),RR->identity.address(),Packet::VERB_NOP);
- RR->sw->send(outp,true,0);
+ if ((!contacted)&&(_bestCurrentUpstream)) {
+ const SharedPtr<Path> up(_bestCurrentUpstream->getBestPath(_now,true));
+ if (up)
+ p->sendHELLO(_tPtr,up->localSocket(),up->address(),_now);
}
- lastReceiveFromUpstream = std::max(p->lastReceive(),lastReceiveFromUpstream);
- } else if (p->activelyTransferringFrames(_now)) {
- // Normal nodes get their preferred link kept alive if the node has generated frame traffic recently
- p->doPingAndKeepalive(_now,0);
+ _alwaysContact.erase(p->address()); // after this we'll WHOIS all upstreams that remain
+ } else if (p->isActive(_now)) {
+ p->doPingAndKeepalive(_tPtr,_now);
}
}
private:
const RuntimeEnvironment *RR;
- uint64_t _now;
- const std::vector<NetworkConfig::Relay> &_relays;
- World _world;
+ void *_tPtr;
+ Hashtable< Address,std::vector<InetAddress> > &_alwaysContact;
+ const int64_t _now;
+ const SharedPtr<Peer> _bestCurrentUpstream;
};
-ZT_ResultCode Node::processBackgroundTasks(uint64_t now,volatile uint64_t *nextBackgroundTaskDeadline)
+ZT_ResultCode Node::processBackgroundTasks(void *tptr,int64_t now,volatile int64_t *nextBackgroundTaskDeadline)
{
_now = now;
Mutex::Lock bl(_backgroundTasksLock);
unsigned long timeUntilNextPingCheck = ZT_PING_CHECK_INVERVAL;
- const uint64_t timeSinceLastPingCheck = now - _lastPingCheck;
+ const int64_t timeSinceLastPingCheck = now - _lastPingCheck;
if (timeSinceLastPingCheck >= ZT_PING_CHECK_INVERVAL) {
try {
_lastPingCheck = now;
- // Get relays and networks that need config without leaving the mutex locked
- std::vector< NetworkConfig::Relay > networkRelays;
- std::vector< SharedPtr<Network> > needConfig;
+ // Get designated VL1 upstreams
+ Hashtable< Address,std::vector<InetAddress> > alwaysContact;
+ RR->topology->getUpstreamsToContact(alwaysContact);
+
+ // Check last receive time on designated upstreams to see if we seem to be online
+ int64_t lastReceivedFromUpstream = 0;
{
- Mutex::Lock _l(_networks_m);
- for(std::vector< std::pair< uint64_t,SharedPtr<Network> > >::const_iterator n(_networks.begin());n!=_networks.end();++n) {
- if (((now - n->second->lastConfigUpdate()) >= ZT_NETWORK_AUTOCONF_DELAY)||(!n->second->hasConfig())) {
- needConfig.push_back(n->second);
- }
- if (n->second->hasConfig()) {
- std::vector<NetworkConfig::Relay> r(n->second->config().relays());
- networkRelays.insert(networkRelays.end(),r.begin(),r.end());
- }
+ Hashtable< Address,std::vector<InetAddress> >::Iterator i(alwaysContact);
+ Address *upstreamAddress = (Address *)0;
+ std::vector<InetAddress> *upstreamStableEndpoints = (std::vector<InetAddress> *)0;
+ while (i.next(upstreamAddress,upstreamStableEndpoints)) {
+ SharedPtr<Peer> p(RR->topology->getPeerNoCache(*upstreamAddress));
+ if (p)
+ lastReceivedFromUpstream = std::max(p->lastReceive(),lastReceivedFromUpstream);
}
}
- // Request updated configuration for networks that need it
- for(std::vector< SharedPtr<Network> >::const_iterator n(needConfig.begin());n!=needConfig.end();++n)
- (*n)->requestConfiguration();
+ // Get peers we should stay connected to according to network configs
+ // Also get networks and whether they need config so we only have to do one pass over networks
+ std::vector< std::pair< SharedPtr<Network>,bool > > networkConfigNeeded;
+ {
+ Mutex::Lock l(_networks_m);
+ Hashtable< uint64_t,SharedPtr<Network> >::Iterator i(_networks);
+ uint64_t *nwid = (uint64_t *)0;
+ SharedPtr<Network> *network = (SharedPtr<Network> *)0;
+ while (i.next(nwid,network)) {
+ (*network)->config().alwaysContactAddresses(alwaysContact);
+ networkConfigNeeded.push_back( std::pair< SharedPtr<Network>,bool >(*network,(((now - (*network)->lastConfigUpdate()) >= ZT_NETWORK_AUTOCONF_DELAY)||(!(*network)->hasConfig()))) );
+ }
+ }
- // Do pings and keepalives
- _PingPeersThatNeedPing pfunc(RR,now,networkRelays);
+ // Ping active peers, upstreams, and others that we should always contact
+ _PingPeersThatNeedPing pfunc(RR,tptr,alwaysContact,now);
RR->topology->eachPeer<_PingPeersThatNeedPing &>(pfunc);
+ // Run WHOIS to create Peer for alwaysContact addresses that could not be contacted
+ {
+ Hashtable< Address,std::vector<InetAddress> >::Iterator i(alwaysContact);
+ Address *upstreamAddress = (Address *)0;
+ std::vector<InetAddress> *upstreamStableEndpoints = (std::vector<InetAddress> *)0;
+ while (i.next(upstreamAddress,upstreamStableEndpoints))
+ RR->sw->requestWhois(tptr,now,*upstreamAddress);
+ }
+
+ // Refresh network config or broadcast network updates to members as needed
+ for(std::vector< std::pair< SharedPtr<Network>,bool > >::const_iterator n(networkConfigNeeded.begin());n!=networkConfigNeeded.end();++n) {
+ if (n->second)
+ n->first->requestConfiguration(tptr);
+ n->first->sendUpdatesToMembers(tptr);
+ }
+
// Update online status, post status change as event
const bool oldOnline = _online;
- _online = (((now - pfunc.lastReceiveFromUpstream) < ZT_PEER_ACTIVITY_TIMEOUT)||(RR->topology->amRoot()));
+ _online = (((now - lastReceivedFromUpstream) < ZT_PEER_ACTIVITY_TIMEOUT)||(RR->topology->amUpstream()));
if (oldOnline != _online)
- postEvent(_online ? ZT_EVENT_ONLINE : ZT_EVENT_OFFLINE);
+ postEvent(tptr,_online ? ZT_EVENT_ONLINE : ZT_EVENT_OFFLINE);
} catch ( ... ) {
return ZT_RESULT_FATAL_ERROR_INTERNAL;
}
@@ -318,10 +327,15 @@ ZT_ResultCode Node::processBackgroundTasks(uint64_t now,volatile uint64_t *nextB
timeUntilNextPingCheck -= (unsigned long)timeSinceLastPingCheck;
}
+ if ((now - _lastMemoizedTraceSettings) >= (ZT_HOUSEKEEPING_PERIOD / 4)) {
+ _lastMemoizedTraceSettings = now;
+ RR->t->updateMemoizedSettings();
+ }
+
if ((now - _lastHousekeepingRun) >= ZT_HOUSEKEEPING_PERIOD) {
+ _lastHousekeepingRun = now;
try {
- _lastHousekeepingRun = now;
- RR->topology->clean(now);
+ RR->topology->doPeriodicTasks(tptr,now);
RR->sa->clean(now);
RR->mc->clean(now);
} catch ( ... ) {
@@ -330,18 +344,7 @@ ZT_ResultCode Node::processBackgroundTasks(uint64_t now,volatile uint64_t *nextB
}
try {
-#ifdef ZT_ENABLE_CLUSTER
- // If clustering is enabled we have to call cluster->doPeriodicTasks() very often, so we override normal timer deadline behavior
- if (RR->cluster) {
- RR->sw->doTimerTasks(now);
- RR->cluster->doPeriodicTasks();
- *nextBackgroundTaskDeadline = now + ZT_CLUSTER_PERIODIC_TASK_PERIOD; // this is really short so just tick at this rate
- } else {
-#endif
- *nextBackgroundTaskDeadline = now + (uint64_t)std::max(std::min(timeUntilNextPingCheck,RR->sw->doTimerTasks(now)),(unsigned long)ZT_CORE_TIMER_TASK_GRANULARITY);
-#ifdef ZT_ENABLE_CLUSTER
- }
-#endif
+ *nextBackgroundTaskDeadline = now + (int64_t)std::max(std::min(timeUntilNextPingCheck,RR->sw->doTimerTasks(tptr,now)),(unsigned long)ZT_CORE_TIMER_TASK_GRANULARITY);
} catch ( ... ) {
return ZT_RESULT_FATAL_ERROR_INTERNAL;
}
@@ -349,38 +352,51 @@ ZT_ResultCode Node::processBackgroundTasks(uint64_t now,volatile uint64_t *nextB
return ZT_RESULT_OK;
}
-ZT_ResultCode Node::join(uint64_t nwid,void *uptr)
+ZT_ResultCode Node::join(uint64_t nwid,void *uptr,void *tptr)
{
Mutex::Lock _l(_networks_m);
- SharedPtr<Network> nw = _network(nwid);
- if(!nw)
- _networks.push_back(std::pair< uint64_t,SharedPtr<Network> >(nwid,SharedPtr<Network>(new Network(RR,nwid,uptr))));
- std::sort(_networks.begin(),_networks.end()); // will sort by nwid since it's the first in a pair<>
+ SharedPtr<Network> &nw = _networks[nwid];
+ if (!nw)
+ nw = SharedPtr<Network>(new Network(RR,tptr,nwid,uptr,(const NetworkConfig *)0));
return ZT_RESULT_OK;
}
-ZT_ResultCode Node::leave(uint64_t nwid,void **uptr)
+ZT_ResultCode Node::leave(uint64_t nwid,void **uptr,void *tptr)
{
- std::vector< std::pair< uint64_t,SharedPtr<Network> > > newn;
- Mutex::Lock _l(_networks_m);
- for(std::vector< std::pair< uint64_t,SharedPtr<Network> > >::const_iterator n(_networks.begin());n!=_networks.end();++n) {
- if (n->first != nwid)
- newn.push_back(*n);
- else {
- if (uptr)
- *uptr = n->second->userPtr();
- n->second->destroy();
- }
+ ZT_VirtualNetworkConfig ctmp;
+ void **nUserPtr = (void **)0;
+ {
+ Mutex::Lock _l(_networks_m);
+ SharedPtr<Network> *nw = _networks.get(nwid);
+ if (!nw)
+ return ZT_RESULT_OK;
+ if (uptr)
+ *uptr = (*nw)->userPtr();
+ (*nw)->externalConfig(&ctmp);
+ (*nw)->destroy();
+ nUserPtr = (*nw)->userPtr();
}
- _networks.swap(newn);
+
+ if (nUserPtr)
+ RR->node->configureVirtualNetworkPort(tptr,nwid,nUserPtr,ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_DESTROY,&ctmp);
+
+ {
+ Mutex::Lock _l(_networks_m);
+ _networks.erase(nwid);
+ }
+
+ uint64_t tmp[2];
+ tmp[0] = nwid; tmp[1] = 0;
+ RR->node->stateObjectDelete(tptr,ZT_STATE_OBJECT_NETWORK_CONFIG,tmp);
+
return ZT_RESULT_OK;
}
-ZT_ResultCode Node::multicastSubscribe(uint64_t nwid,uint64_t multicastGroup,unsigned long multicastAdi)
+ZT_ResultCode Node::multicastSubscribe(void *tptr,uint64_t nwid,uint64_t multicastGroup,unsigned long multicastAdi)
{
SharedPtr<Network> nw(this->network(nwid));
if (nw) {
- nw->multicastSubscribe(MulticastGroup(MAC(multicastGroup),(uint32_t)(multicastAdi & 0xffffffff)));
+ nw->multicastSubscribe(tptr,MulticastGroup(MAC(multicastGroup),(uint32_t)(multicastAdi & 0xffffffff)));
return ZT_RESULT_OK;
} else return ZT_RESULT_ERROR_NETWORK_NOT_FOUND;
}
@@ -394,6 +410,18 @@ ZT_ResultCode Node::multicastUnsubscribe(uint64_t nwid,uint64_t multicastGroup,u
} else return ZT_RESULT_ERROR_NETWORK_NOT_FOUND;
}
+ZT_ResultCode Node::orbit(void *tptr,uint64_t moonWorldId,uint64_t moonSeed)
+{
+ RR->topology->addMoon(tptr,moonWorldId,Address(moonSeed));
+ return ZT_RESULT_OK;
+}
+
+ZT_ResultCode Node::deorbit(void *tptr,uint64_t moonWorldId)
+{
+ RR->topology->removeMoon(tptr,moonWorldId);
+ return ZT_RESULT_OK;
+}
+
uint64_t Node::address() const
{
return RR->identity.address().toInt();
@@ -402,10 +430,8 @@ uint64_t Node::address() const
void Node::status(ZT_NodeStatus *status) const
{
status->address = RR->identity.address().toInt();
- status->worldId = RR->topology->worldId();
- status->worldTimestamp = RR->topology->worldTimestamp();
- status->publicIdentity = RR->publicIdentityStr.c_str();
- status->secretIdentity = RR->secretIdentityStr.c_str();
+ status->publicIdentity = RR->publicIdentityStr;
+ status->secretIdentity = RR->secretIdentityStr;
status->online = _online ? 1 : 0;
}
@@ -424,8 +450,6 @@ ZT_PeerList *Node::peers() const
for(std::vector< std::pair< Address,SharedPtr<Peer> > >::iterator pi(peers.begin());pi!=peers.end();++pi) {
ZT_Peer *p = &(pl->peers[pl->peerCount++]);
p->address = pi->second->address().toInt();
- p->lastUnicastFrame = pi->second->lastUnicastFrame();
- p->lastMulticastFrame = pi->second->lastMulticastFrame();
if (pi->second->remoteVersionKnown()) {
p->versionMajor = pi->second->remoteVersionMajor();
p->versionMinor = pi->second->remoteVersionMinor();
@@ -435,19 +459,21 @@ ZT_PeerList *Node::peers() const
p->versionMinor = -1;
p->versionRev = -1;
}
- p->latency = pi->second->latency();
- p->role = RR->topology->isRoot(pi->second->identity()) ? ZT_PEER_ROLE_ROOT : ZT_PEER_ROLE_LEAF;
+ p->latency = pi->second->latency(_now);
+ if (p->latency >= 0xffff)
+ p->latency = -1;
+ p->role = RR->topology->role(pi->second->identity().address());
- std::vector<Path> paths(pi->second->paths());
- Path *bestPath = pi->second->getBestPath(_now);
+ std::vector< SharedPtr<Path> > paths(pi->second->paths(_now));
+ SharedPtr<Path> bestp(pi->second->getBestPath(_now,false));
p->pathCount = 0;
- for(std::vector<Path>::iterator path(paths.begin());path!=paths.end();++path) {
- memcpy(&(p->paths[p->pathCount].address),&(path->address()),sizeof(struct sockaddr_storage));
- p->paths[p->pathCount].lastSend = path->lastSend();
- p->paths[p->pathCount].lastReceive = path->lastReceived();
- p->paths[p->pathCount].active = path->active(_now) ? 1 : 0;
- p->paths[p->pathCount].preferred = ((bestPath)&&(*path == *bestPath)) ? 1 : 0;
- p->paths[p->pathCount].trustedPathId = RR->topology->getOutboundPathTrust(path->address());
+ for(std::vector< SharedPtr<Path> >::iterator path(paths.begin());path!=paths.end();++path) {
+ ZT_FAST_MEMCPY(&(p->paths[p->pathCount].address),&((*path)->address()),sizeof(struct sockaddr_storage));
+ p->paths[p->pathCount].lastSend = (*path)->lastOut();
+ p->paths[p->pathCount].lastReceive = (*path)->lastIn();
+ p->paths[p->pathCount].trustedPathId = RR->topology->getOutboundPathTrust((*path)->address());
+ p->paths[p->pathCount].expired = 0;
+ p->paths[p->pathCount].preferred = ((*path) == bestp) ? 1 : 0;
++p->pathCount;
}
}
@@ -458,10 +484,10 @@ ZT_PeerList *Node::peers() const
ZT_VirtualNetworkConfig *Node::networkConfig(uint64_t nwid) const
{
Mutex::Lock _l(_networks_m);
- SharedPtr<Network> nw = _network(nwid);
- if(nw) {
+ const SharedPtr<Network> *nw = _networks.get(nwid);
+ if (nw) {
ZT_VirtualNetworkConfig *nc = (ZT_VirtualNetworkConfig *)::malloc(sizeof(ZT_VirtualNetworkConfig));
- nw->externalConfig(nc);
+ (*nw)->externalConfig(nc);
return nc;
}
return (ZT_VirtualNetworkConfig *)0;
@@ -478,8 +504,11 @@ ZT_VirtualNetworkList *Node::networks() const
nl->networks = (ZT_VirtualNetworkConfig *)(buf + sizeof(ZT_VirtualNetworkList));
nl->networkCount = 0;
- for(std::vector< std::pair< uint64_t,SharedPtr<Network> > >::const_iterator n(_networks.begin());n!=_networks.end();++n)
- n->second->externalConfig(&(nl->networks[nl->networkCount++]));
+ Hashtable< uint64_t,SharedPtr<Network> >::Iterator i(*const_cast< Hashtable< uint64_t,SharedPtr<Network> > *>(&_networks));
+ uint64_t *k = (uint64_t *)0;
+ SharedPtr<Network> *v = (SharedPtr<Network> *)0;
+ while (i.next(k,v))
+ (*v)->externalConfig(&(nl->networks[nl->networkCount++]));
return nl;
}
@@ -508,247 +537,188 @@ void Node::clearLocalInterfaceAddresses()
_directPaths.clear();
}
+int Node::sendUserMessage(void *tptr,uint64_t dest,uint64_t typeId,const void *data,unsigned int len)
+{
+ try {
+ if (RR->identity.address().toInt() != dest) {
+ Packet outp(Address(dest),RR->identity.address(),Packet::VERB_USER_MESSAGE);
+ outp.append(typeId);
+ outp.append(data,len);
+ outp.compress();
+ RR->sw->send(tptr,outp,true);
+ return 1;
+ }
+ } catch ( ... ) {}
+ return 0;
+}
+
void Node::setNetconfMaster(void *networkControllerInstance)
{
RR->localNetworkController = reinterpret_cast<NetworkController *>(networkControllerInstance);
+ if (networkControllerInstance)
+ RR->localNetworkController->init(RR->identity,this);
}
-ZT_ResultCode Node::circuitTestBegin(ZT_CircuitTest *test,void (*reportCallback)(ZT_Node *,ZT_CircuitTest *,const ZT_CircuitTestReport *))
+/****************************************************************************/
+/* Node methods used only within node/ */
+/****************************************************************************/
+
+bool Node::shouldUsePathForZeroTierTraffic(void *tPtr,const Address &ztaddr,const int64_t localSocket,const InetAddress &remoteAddress)
{
- if (test->hopCount > 0) {
- try {
- Packet outp(Address(),RR->identity.address(),Packet::VERB_CIRCUIT_TEST);
- RR->identity.address().appendTo(outp);
- outp.append((uint16_t)((test->reportAtEveryHop != 0) ? 0x03 : 0x02));
- outp.append((uint64_t)test->timestamp);
- outp.append((uint64_t)test->testId);
- outp.append((uint16_t)0); // originator credential length, updated later
- if (test->credentialNetworkId) {
- outp.append((uint8_t)0x01);
- outp.append((uint64_t)test->credentialNetworkId);
- outp.setAt<uint16_t>(ZT_PACKET_IDX_PAYLOAD + 23,(uint16_t)9);
- }
- outp.append((uint16_t)0);
- C25519::Signature sig(RR->identity.sign(reinterpret_cast<const char *>(outp.data()) + ZT_PACKET_IDX_PAYLOAD,outp.size() - ZT_PACKET_IDX_PAYLOAD));
- outp.append((uint16_t)sig.size());
- outp.append(sig.data,(unsigned int)sig.size());
- outp.append((uint16_t)0); // originator doesn't need an extra credential, since it's the originator
- for(unsigned int h=1;h<test->hopCount;++h) {
- outp.append((uint8_t)0);
- outp.append((uint8_t)(test->hops[h].breadth & 0xff));
- for(unsigned int a=0;a<test->hops[h].breadth;++a)
- Address(test->hops[h].addresses[a]).appendTo(outp);
- }
+ if (!Path::isAddressValidForPath(remoteAddress))
+ return false;
- for(unsigned int a=0;a<test->hops[0].breadth;++a) {
- outp.newInitializationVector();
- outp.setDestination(Address(test->hops[0].addresses[a]));
- RR->sw->send(outp,true,0);
- }
- } catch ( ... ) {
- return ZT_RESULT_FATAL_ERROR_INTERNAL; // probably indicates FIFO too big for packet
- }
- }
+ if (RR->topology->isProhibitedEndpoint(ztaddr,remoteAddress))
+ return false;
{
- test->_internalPtr = reinterpret_cast<void *>(reportCallback);
- Mutex::Lock _l(_circuitTests_m);
- if (std::find(_circuitTests.begin(),_circuitTests.end(),test) == _circuitTests.end())
- _circuitTests.push_back(test);
+ Mutex::Lock _l(_networks_m);
+ Hashtable< uint64_t,SharedPtr<Network> >::Iterator i(_networks);
+ uint64_t *k = (uint64_t *)0;
+ SharedPtr<Network> *v = (SharedPtr<Network> *)0;
+ while (i.next(k,v)) {
+ if ((*v)->hasConfig()) {
+ for(unsigned int k=0;k<(*v)->config().staticIpCount;++k) {
+ if ((*v)->config().staticIps[k].containsAddress(remoteAddress))
+ return false;
+ }
+ }
+ }
}
- return ZT_RESULT_OK;
-}
-
-void Node::circuitTestEnd(ZT_CircuitTest *test)
-{
- Mutex::Lock _l(_circuitTests_m);
- for(;;) {
- std::vector< ZT_CircuitTest * >::iterator ct(std::find(_circuitTests.begin(),_circuitTests.end(),test));
- if (ct == _circuitTests.end())
- break;
- else _circuitTests.erase(ct);
- }
+ return ( (_cb.pathCheckFunction) ? (_cb.pathCheckFunction(reinterpret_cast<ZT_Node *>(this),_uPtr,tPtr,ztaddr.toInt(),localSocket,reinterpret_cast<const struct sockaddr_storage *>(&remoteAddress)) != 0) : true);
}
-ZT_ResultCode Node::clusterInit(
- unsigned int myId,
- const struct sockaddr_storage *zeroTierPhysicalEndpoints,
- unsigned int numZeroTierPhysicalEndpoints,
- int x,
- int y,
- int z,
- void (*sendFunction)(void *,unsigned int,const void *,unsigned int),
- void *sendFunctionArg,
- int (*addressToLocationFunction)(void *,const struct sockaddr_storage *,int *,int *,int *),
- void *addressToLocationFunctionArg)
+uint64_t Node::prng()
{
-#ifdef ZT_ENABLE_CLUSTER
- if (RR->cluster)
- return ZT_RESULT_ERROR_BAD_PARAMETER;
-
- std::vector<InetAddress> eps;
- for(unsigned int i=0;i<numZeroTierPhysicalEndpoints;++i)
- eps.push_back(InetAddress(zeroTierPhysicalEndpoints[i]));
- std::sort(eps.begin(),eps.end());
- RR->cluster = new Cluster(RR,myId,eps,x,y,z,sendFunction,sendFunctionArg,addressToLocationFunction,addressToLocationFunctionArg);
-
- return ZT_RESULT_OK;
-#else
- return ZT_RESULT_ERROR_UNSUPPORTED_OPERATION;
-#endif
+ // https://en.wikipedia.org/wiki/Xorshift#xorshift.2B
+ uint64_t x = _prngState[0];
+ const uint64_t y = _prngState[1];
+ _prngState[0] = y;
+ x ^= x << 23;
+ const uint64_t z = x ^ y ^ (x >> 17) ^ (y >> 26);
+ _prngState[1] = z;
+ return z + y;
}
-ZT_ResultCode Node::clusterAddMember(unsigned int memberId)
+ZT_ResultCode Node::setPhysicalPathConfiguration(const struct sockaddr_storage *pathNetwork, const ZT_PhysicalPathConfiguration *pathConfig)
{
-#ifdef ZT_ENABLE_CLUSTER
- if (!RR->cluster)
- return ZT_RESULT_ERROR_BAD_PARAMETER;
- RR->cluster->addMember((uint16_t)memberId);
+ RR->topology->setPhysicalPathConfiguration(pathNetwork,pathConfig);
return ZT_RESULT_OK;
-#else
- return ZT_RESULT_ERROR_UNSUPPORTED_OPERATION;
-#endif
-}
-
-void Node::clusterRemoveMember(unsigned int memberId)
-{
-#ifdef ZT_ENABLE_CLUSTER
- if (RR->cluster)
- RR->cluster->removeMember((uint16_t)memberId);
-#endif
}
-void Node::clusterHandleIncomingMessage(const void *msg,unsigned int len)
+World Node::planet() const
{
-#ifdef ZT_ENABLE_CLUSTER
- if (RR->cluster)
- RR->cluster->handleIncomingStateMessage(msg,len);
-#endif
+ return RR->topology->planet();
}
-void Node::clusterStatus(ZT_ClusterStatus *cs)
+std::vector<World> Node::moons() const
{
- if (!cs)
- return;
-#ifdef ZT_ENABLE_CLUSTER
- if (RR->cluster)
- RR->cluster->status(*cs);
- else
-#endif
- memset(cs,0,sizeof(ZT_ClusterStatus));
+ return RR->topology->moons();
}
-void Node::backgroundThreadMain()
+void Node::ncSendConfig(uint64_t nwid,uint64_t requestPacketId,const Address &destination,const NetworkConfig &nc,bool sendLegacyFormatConfig)
{
- ++RR->dpEnabled;
- for(;;) {
+ if (destination == RR->identity.address()) {
+ SharedPtr<Network> n(network(nwid));
+ if (!n) return;
+ n->setConfiguration((void *)0,nc,true);
+ } else {
+ Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> *dconf = new Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY>();
try {
- if (RR->dp->process() < 0)
- break;
- } catch ( ... ) {} // sanity check -- should not throw
- }
- --RR->dpEnabled;
-}
+ if (nc.toDictionary(*dconf,sendLegacyFormatConfig)) {
+ uint64_t configUpdateId = prng();
+ if (!configUpdateId) ++configUpdateId;
+
+ const unsigned int totalSize = dconf->sizeBytes();
+ unsigned int chunkIndex = 0;
+ while (chunkIndex < totalSize) {
+ const unsigned int chunkLen = std::min(totalSize - chunkIndex,(unsigned int)(ZT_PROTO_MAX_PACKET_LENGTH - (ZT_PACKET_IDX_PAYLOAD + 256)));
+ Packet outp(destination,RR->identity.address(),(requestPacketId) ? Packet::VERB_OK : Packet::VERB_NETWORK_CONFIG);
+ if (requestPacketId) {
+ outp.append((unsigned char)Packet::VERB_NETWORK_CONFIG_REQUEST);
+ outp.append(requestPacketId);
+ }
-/****************************************************************************/
-/* Node methods used only within node/ */
-/****************************************************************************/
+ const unsigned int sigStart = outp.size();
+ outp.append(nwid);
+ outp.append((uint16_t)chunkLen);
+ outp.append((const void *)(dconf->data() + chunkIndex),chunkLen);
-std::string Node::dataStoreGet(const char *name)
-{
- char buf[1024];
- std::string r;
- unsigned long olen = 0;
- do {
- long n = _dataStoreGetFunction(reinterpret_cast<ZT_Node *>(this),_uPtr,name,buf,sizeof(buf),(unsigned long)r.length(),&olen);
- if (n <= 0)
- return std::string();
- r.append(buf,n);
- } while (r.length() < olen);
- return r;
-}
+ outp.append((uint8_t)0); // no flags
+ outp.append((uint64_t)configUpdateId);
+ outp.append((uint32_t)totalSize);
+ outp.append((uint32_t)chunkIndex);
-bool Node::shouldUsePathForZeroTierTraffic(const InetAddress &localAddress,const InetAddress &remoteAddress)
-{
- if (!Path::isAddressValidForPath(remoteAddress))
- return false;
+ C25519::Signature sig(RR->identity.sign(reinterpret_cast<const uint8_t *>(outp.data()) + sigStart,outp.size() - sigStart));
+ outp.append((uint8_t)1);
+ outp.append((uint16_t)ZT_C25519_SIGNATURE_LEN);
+ outp.append(sig.data,ZT_C25519_SIGNATURE_LEN);
- {
- Mutex::Lock _l(_networks_m);
- for(std::vector< std::pair< uint64_t, SharedPtr<Network> > >::const_iterator i=_networks.begin();i!=_networks.end();++i) {
- if (i->second->hasConfig()) {
- for(unsigned int k=0;k<i->second->config().staticIpCount;++k) {
- if (i->second->config().staticIps[k].containsAddress(remoteAddress))
- return false;
+ outp.compress();
+ RR->sw->send((void *)0,outp,true);
+ chunkIndex += chunkLen;
}
}
+ delete dconf;
+ } catch ( ... ) {
+ delete dconf;
+ throw;
}
}
-
- if (_pathCheckFunction)
- return (_pathCheckFunction(reinterpret_cast<ZT_Node *>(this),_uPtr,reinterpret_cast<const struct sockaddr_storage *>(&localAddress),reinterpret_cast<const struct sockaddr_storage *>(&remoteAddress)) != 0);
- else return true;
}
-#ifdef ZT_TRACE
-void Node::postTrace(const char *module,unsigned int line,const char *fmt,...)
+void Node::ncSendRevocation(const Address &destination,const Revocation &rev)
{
- static Mutex traceLock;
-
- va_list ap;
- char tmp1[1024],tmp2[1024],tmp3[256];
-
- Mutex::Lock _l(traceLock);
-
- time_t now = (time_t)(_now / 1000ULL);
-#ifdef __WINDOWS__
- ctime_s(tmp3,sizeof(tmp3),&now);
- char *nowstr = tmp3;
-#else
- char *nowstr = ctime_r(&now,tmp3);
-#endif
- unsigned long nowstrlen = (unsigned long)strlen(nowstr);
- if (nowstr[nowstrlen-1] == '\n')
- nowstr[--nowstrlen] = (char)0;
- if (nowstr[nowstrlen-1] == '\r')
- nowstr[--nowstrlen] = (char)0;
-
- va_start(ap,fmt);
- vsnprintf(tmp2,sizeof(tmp2),fmt,ap);
- va_end(ap);
- tmp2[sizeof(tmp2)-1] = (char)0;
-
- Utils::snprintf(tmp1,sizeof(tmp1),"[%s] %s:%u %s",nowstr,module,line,tmp2);
- postEvent(ZT_EVENT_TRACE,tmp1);
-}
-#endif // ZT_TRACE
-
-uint64_t Node::prng()
-{
- unsigned int p = (++_prngStreamPtr % (sizeof(_prngStream) / sizeof(uint64_t)));
- if (!p)
- _prng.encrypt12(_prngStream,_prngStream,sizeof(_prngStream));
- return _prngStream[p];
-}
+ if (destination == RR->identity.address()) {
+ SharedPtr<Network> n(network(rev.networkId()));
+ if (!n) return;
+ n->addCredential((void *)0,RR->identity.address(),rev);
+ } else {
+ Packet outp(destination,RR->identity.address(),Packet::VERB_NETWORK_CREDENTIALS);
+ outp.append((uint8_t)0x00);
+ outp.append((uint16_t)0);
+ outp.append((uint16_t)0);
+ outp.append((uint16_t)1);
+ rev.serialize(outp);
+ outp.append((uint16_t)0);
+ RR->sw->send((void *)0,outp,true);
+ }
+}
+
+void Node::ncSendError(uint64_t nwid,uint64_t requestPacketId,const Address &destination,NetworkController::ErrorCode errorCode)
+{
+ if (destination == RR->identity.address()) {
+ SharedPtr<Network> n(network(nwid));
+ if (!n) return;
+ switch(errorCode) {
+ case NetworkController::NC_ERROR_OBJECT_NOT_FOUND:
+ case NetworkController::NC_ERROR_INTERNAL_SERVER_ERROR:
+ n->setNotFound();
+ break;
+ case NetworkController::NC_ERROR_ACCESS_DENIED:
+ n->setAccessDenied();
+ break;
-void Node::postCircuitTestReport(const ZT_CircuitTestReport *report)
-{
- std::vector< ZT_CircuitTest * > toNotify;
- {
- Mutex::Lock _l(_circuitTests_m);
- for(std::vector< ZT_CircuitTest * >::iterator i(_circuitTests.begin());i!=_circuitTests.end();++i) {
- if ((*i)->testId == report->testId)
- toNotify.push_back(*i);
+ default: break;
}
- }
- for(std::vector< ZT_CircuitTest * >::iterator i(toNotify.begin());i!=toNotify.end();++i)
- (reinterpret_cast<void (*)(ZT_Node *,ZT_CircuitTest *,const ZT_CircuitTestReport *)>((*i)->_internalPtr))(reinterpret_cast<ZT_Node *>(this),*i,report);
-}
-
-void Node::setTrustedPaths(const struct sockaddr_storage *networks,const uint64_t *ids,unsigned int count)
-{
- RR->topology->setTrustedPaths(reinterpret_cast<const InetAddress *>(networks),ids,count);
+ } else if (requestPacketId) {
+ Packet outp(destination,RR->identity.address(),Packet::VERB_ERROR);
+ outp.append((unsigned char)Packet::VERB_NETWORK_CONFIG_REQUEST);
+ outp.append(requestPacketId);
+ switch(errorCode) {
+ //case NetworkController::NC_ERROR_OBJECT_NOT_FOUND:
+ //case NetworkController::NC_ERROR_INTERNAL_SERVER_ERROR:
+ default:
+ outp.append((unsigned char)Packet::ERROR_OBJ_NOT_FOUND);
+ break;
+ case NetworkController::NC_ERROR_ACCESS_DENIED:
+ outp.append((unsigned char)Packet::ERROR_NETWORK_ACCESS_DENIED_);
+ break;
+ }
+ outp.append(nwid);
+ RR->sw->send((void *)0,outp,true);
+ } // else we can't send an ERROR() in response to nothing, so discard
}
} // namespace ZeroTier
@@ -759,21 +729,11 @@ void Node::setTrustedPaths(const struct sockaddr_storage *networks,const uint64_
extern "C" {
-enum ZT_ResultCode ZT_Node_new(
- ZT_Node **node,
- void *uptr,
- uint64_t now,
- ZT_DataStoreGetFunction dataStoreGetFunction,
- ZT_DataStorePutFunction dataStorePutFunction,
- ZT_WirePacketSendFunction wirePacketSendFunction,
- ZT_VirtualNetworkFrameFunction virtualNetworkFrameFunction,
- ZT_VirtualNetworkConfigFunction virtualNetworkConfigFunction,
- ZT_PathCheckFunction pathCheckFunction,
- ZT_EventCallback eventCallback)
+enum ZT_ResultCode ZT_Node_new(ZT_Node **node,void *uptr,void *tptr,const struct ZT_Node_Callbacks *callbacks,int64_t now)
{
*node = (ZT_Node *)0;
try {
- *node = reinterpret_cast<ZT_Node *>(new ZeroTier::Node(now,uptr,dataStoreGetFunction,dataStorePutFunction,wirePacketSendFunction,virtualNetworkFrameFunction,virtualNetworkConfigFunction,pathCheckFunction,eventCallback));
+ *node = reinterpret_cast<ZT_Node *>(new ZeroTier::Node(uptr,tptr,callbacks,now));
return ZT_RESULT_OK;
} catch (std::bad_alloc &exc) {
return ZT_RESULT_FATAL_ERROR_OUT_OF_MEMORY;
@@ -793,15 +753,16 @@ void ZT_Node_delete(ZT_Node *node)
enum ZT_ResultCode ZT_Node_processWirePacket(
ZT_Node *node,
- uint64_t now,
- const struct sockaddr_storage *localAddress,
+ void *tptr,
+ int64_t now,
+ int64_t localSocket,
const struct sockaddr_storage *remoteAddress,
const void *packetData,
unsigned int packetLength,
- volatile uint64_t *nextBackgroundTaskDeadline)
+ volatile int64_t *nextBackgroundTaskDeadline)
{
try {
- return reinterpret_cast<ZeroTier::Node *>(node)->processWirePacket(now,localAddress,remoteAddress,packetData,packetLength,nextBackgroundTaskDeadline);
+ return reinterpret_cast<ZeroTier::Node *>(node)->processWirePacket(tptr,now,localSocket,remoteAddress,packetData,packetLength,nextBackgroundTaskDeadline);
} catch (std::bad_alloc &exc) {
return ZT_RESULT_FATAL_ERROR_OUT_OF_MEMORY;
} catch ( ... ) {
@@ -811,7 +772,8 @@ enum ZT_ResultCode ZT_Node_processWirePacket(
enum ZT_ResultCode ZT_Node_processVirtualNetworkFrame(
ZT_Node *node,
- uint64_t now,
+ void *tptr,
+ int64_t now,
uint64_t nwid,
uint64_t sourceMac,
uint64_t destMac,
@@ -819,10 +781,10 @@ enum ZT_ResultCode ZT_Node_processVirtualNetworkFrame(
unsigned int vlanId,
const void *frameData,
unsigned int frameLength,
- volatile uint64_t *nextBackgroundTaskDeadline)
+ volatile int64_t *nextBackgroundTaskDeadline)
{
try {
- return reinterpret_cast<ZeroTier::Node *>(node)->processVirtualNetworkFrame(now,nwid,sourceMac,destMac,etherType,vlanId,frameData,frameLength,nextBackgroundTaskDeadline);
+ return reinterpret_cast<ZeroTier::Node *>(node)->processVirtualNetworkFrame(tptr,now,nwid,sourceMac,destMac,etherType,vlanId,frameData,frameLength,nextBackgroundTaskDeadline);
} catch (std::bad_alloc &exc) {
return ZT_RESULT_FATAL_ERROR_OUT_OF_MEMORY;
} catch ( ... ) {
@@ -830,10 +792,10 @@ enum ZT_ResultCode ZT_Node_processVirtualNetworkFrame(
}
}
-enum ZT_ResultCode ZT_Node_processBackgroundTasks(ZT_Node *node,uint64_t now,volatile uint64_t *nextBackgroundTaskDeadline)
+enum ZT_ResultCode ZT_Node_processBackgroundTasks(ZT_Node *node,void *tptr,int64_t now,volatile int64_t *nextBackgroundTaskDeadline)
{
try {
- return reinterpret_cast<ZeroTier::Node *>(node)->processBackgroundTasks(now,nextBackgroundTaskDeadline);
+ return reinterpret_cast<ZeroTier::Node *>(node)->processBackgroundTasks(tptr,now,nextBackgroundTaskDeadline);
} catch (std::bad_alloc &exc) {
return ZT_RESULT_FATAL_ERROR_OUT_OF_MEMORY;
} catch ( ... ) {
@@ -841,10 +803,10 @@ enum ZT_ResultCode ZT_Node_processBackgroundTasks(ZT_Node *node,uint64_t now,vol
}
}
-enum ZT_ResultCode ZT_Node_join(ZT_Node *node,uint64_t nwid,void *uptr)
+enum ZT_ResultCode ZT_Node_join(ZT_Node *node,uint64_t nwid,void *uptr,void *tptr)
{
try {
- return reinterpret_cast<ZeroTier::Node *>(node)->join(nwid,uptr);
+ return reinterpret_cast<ZeroTier::Node *>(node)->join(nwid,uptr,tptr);
} catch (std::bad_alloc &exc) {
return ZT_RESULT_FATAL_ERROR_OUT_OF_MEMORY;
} catch ( ... ) {
@@ -852,10 +814,10 @@ enum ZT_ResultCode ZT_Node_join(ZT_Node *node,uint64_t nwid,void *uptr)
}
}
-enum ZT_ResultCode ZT_Node_leave(ZT_Node *node,uint64_t nwid,void **uptr)
+enum ZT_ResultCode ZT_Node_leave(ZT_Node *node,uint64_t nwid,void **uptr,void *tptr)
{
try {
- return reinterpret_cast<ZeroTier::Node *>(node)->leave(nwid,uptr);
+ return reinterpret_cast<ZeroTier::Node *>(node)->leave(nwid,uptr,tptr);
} catch (std::bad_alloc &exc) {
return ZT_RESULT_FATAL_ERROR_OUT_OF_MEMORY;
} catch ( ... ) {
@@ -863,10 +825,10 @@ enum ZT_ResultCode ZT_Node_leave(ZT_Node *node,uint64_t nwid,void **uptr)
}
}
-enum ZT_ResultCode ZT_Node_multicastSubscribe(ZT_Node *node,uint64_t nwid,uint64_t multicastGroup,unsigned long multicastAdi)
+enum ZT_ResultCode ZT_Node_multicastSubscribe(ZT_Node *node,void *tptr,uint64_t nwid,uint64_t multicastGroup,unsigned long multicastAdi)
{
try {
- return reinterpret_cast<ZeroTier::Node *>(node)->multicastSubscribe(nwid,multicastGroup,multicastAdi);
+ return reinterpret_cast<ZeroTier::Node *>(node)->multicastSubscribe(tptr,nwid,multicastGroup,multicastAdi);
} catch (std::bad_alloc &exc) {
return ZT_RESULT_FATAL_ERROR_OUT_OF_MEMORY;
} catch ( ... ) {
@@ -885,6 +847,24 @@ enum ZT_ResultCode ZT_Node_multicastUnsubscribe(ZT_Node *node,uint64_t nwid,uint
}
}
+enum ZT_ResultCode ZT_Node_orbit(ZT_Node *node,void *tptr,uint64_t moonWorldId,uint64_t moonSeed)
+{
+ try {
+ return reinterpret_cast<ZeroTier::Node *>(node)->orbit(tptr,moonWorldId,moonSeed);
+ } catch ( ... ) {
+ return ZT_RESULT_FATAL_ERROR_INTERNAL;
+ }
+}
+
+enum ZT_ResultCode ZT_Node_deorbit(ZT_Node *node,void *tptr,uint64_t moonWorldId)
+{
+ try {
+ return reinterpret_cast<ZeroTier::Node *>(node)->deorbit(tptr,moonWorldId);
+ } catch ( ... ) {
+ return ZT_RESULT_FATAL_ERROR_INTERNAL;
+ }
+}
+
uint64_t ZT_Node_address(ZT_Node *node)
{
return reinterpret_cast<ZeroTier::Node *>(node)->address();
@@ -947,93 +927,31 @@ void ZT_Node_clearLocalInterfaceAddresses(ZT_Node *node)
} catch ( ... ) {}
}
-void ZT_Node_setNetconfMaster(ZT_Node *node,void *networkControllerInstance)
-{
- try {
- reinterpret_cast<ZeroTier::Node *>(node)->setNetconfMaster(networkControllerInstance);
- } catch ( ... ) {}
-}
-
-enum ZT_ResultCode ZT_Node_circuitTestBegin(ZT_Node *node,ZT_CircuitTest *test,void (*reportCallback)(ZT_Node *,ZT_CircuitTest *,const ZT_CircuitTestReport *))
+int ZT_Node_sendUserMessage(ZT_Node *node,void *tptr,uint64_t dest,uint64_t typeId,const void *data,unsigned int len)
{
try {
- return reinterpret_cast<ZeroTier::Node *>(node)->circuitTestBegin(test,reportCallback);
+ return reinterpret_cast<ZeroTier::Node *>(node)->sendUserMessage(tptr,dest,typeId,data,len);
} catch ( ... ) {
- return ZT_RESULT_FATAL_ERROR_INTERNAL;
+ return 0;
}
}
-void ZT_Node_circuitTestEnd(ZT_Node *node,ZT_CircuitTest *test)
+void ZT_Node_setNetconfMaster(ZT_Node *node,void *networkControllerInstance)
{
try {
- reinterpret_cast<ZeroTier::Node *>(node)->circuitTestEnd(test);
+ reinterpret_cast<ZeroTier::Node *>(node)->setNetconfMaster(networkControllerInstance);
} catch ( ... ) {}
}
-enum ZT_ResultCode ZT_Node_clusterInit(
- ZT_Node *node,
- unsigned int myId,
- const struct sockaddr_storage *zeroTierPhysicalEndpoints,
- unsigned int numZeroTierPhysicalEndpoints,
- int x,
- int y,
- int z,
- void (*sendFunction)(void *,unsigned int,const void *,unsigned int),
- void *sendFunctionArg,
- int (*addressToLocationFunction)(void *,const struct sockaddr_storage *,int *,int *,int *),
- void *addressToLocationFunctionArg)
-{
- try {
- return reinterpret_cast<ZeroTier::Node *>(node)->clusterInit(myId,zeroTierPhysicalEndpoints,numZeroTierPhysicalEndpoints,x,y,z,sendFunction,sendFunctionArg,addressToLocationFunction,addressToLocationFunctionArg);
- } catch ( ... ) {
- return ZT_RESULT_FATAL_ERROR_INTERNAL;
- }
-}
-
-enum ZT_ResultCode ZT_Node_clusterAddMember(ZT_Node *node,unsigned int memberId)
+enum ZT_ResultCode ZT_Node_setPhysicalPathConfiguration(ZT_Node *node,const struct sockaddr_storage *pathNetwork,const ZT_PhysicalPathConfiguration *pathConfig)
{
try {
- return reinterpret_cast<ZeroTier::Node *>(node)->clusterAddMember(memberId);
+ return reinterpret_cast<ZeroTier::Node *>(node)->setPhysicalPathConfiguration(pathNetwork,pathConfig);
} catch ( ... ) {
return ZT_RESULT_FATAL_ERROR_INTERNAL;
}
}
-void ZT_Node_clusterRemoveMember(ZT_Node *node,unsigned int memberId)
-{
- try {
- reinterpret_cast<ZeroTier::Node *>(node)->clusterRemoveMember(memberId);
- } catch ( ... ) {}
-}
-
-void ZT_Node_clusterHandleIncomingMessage(ZT_Node *node,const void *msg,unsigned int len)
-{
- try {
- reinterpret_cast<ZeroTier::Node *>(node)->clusterHandleIncomingMessage(msg,len);
- } catch ( ... ) {}
-}
-
-void ZT_Node_clusterStatus(ZT_Node *node,ZT_ClusterStatus *cs)
-{
- try {
- reinterpret_cast<ZeroTier::Node *>(node)->clusterStatus(cs);
- } catch ( ... ) {}
-}
-
-void ZT_Node_setTrustedPaths(ZT_Node *node,const struct sockaddr_storage *networks,const uint64_t *ids,unsigned int count)
-{
- try {
- reinterpret_cast<ZeroTier::Node *>(node)->setTrustedPaths(networks,ids,count);
- } catch ( ... ) {}
-}
-
-void ZT_Node_backgroundThreadMain(ZT_Node *node)
-{
- try {
- reinterpret_cast<ZeroTier::Node *>(node)->backgroundThreadMain();
- } catch ( ... ) {}
-}
-
void ZT_version(int *major,int *minor,int *revision)
{
if (major) *major = ZEROTIER_ONE_VERSION_MAJOR;
diff --git a/node/Node.hpp b/node/Node.hpp
index 0a39d1ee..79284b63 100644
--- a/node/Node.hpp
+++ b/node/Node.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_NODE_HPP
@@ -24,6 +32,7 @@
#include <string.h>
#include <map>
+#include <vector>
#include "Constants.hpp"
@@ -36,48 +45,47 @@
#include "Network.hpp"
#include "Path.hpp"
#include "Salsa20.hpp"
+#include "NetworkController.hpp"
+#include "Hashtable.hpp"
-#undef TRACE
-#ifdef ZT_TRACE
-#define TRACE(f,...) RR->node->postTrace(__FILE__,__LINE__,f,##__VA_ARGS__)
-#else
-#define TRACE(f,...) {}
-#endif
+// Bit mask for "expecting reply" hash
+#define ZT_EXPECTING_REPLIES_BUCKET_MASK1 255
+#define ZT_EXPECTING_REPLIES_BUCKET_MASK2 31
namespace ZeroTier {
+class World;
+
/**
* Implementation of Node object as defined in CAPI
*
* The pointer returned by ZT_Node_new() is an instance of this class.
*/
-class Node
+class Node : public NetworkController::Sender
{
public:
- Node(
- uint64_t now,
- void *uptr,
- ZT_DataStoreGetFunction dataStoreGetFunction,
- ZT_DataStorePutFunction dataStorePutFunction,
- ZT_WirePacketSendFunction wirePacketSendFunction,
- ZT_VirtualNetworkFrameFunction virtualNetworkFrameFunction,
- ZT_VirtualNetworkConfigFunction virtualNetworkConfigFunction,
- ZT_PathCheckFunction pathCheckFunction,
- ZT_EventCallback eventCallback);
-
- ~Node();
+ Node(void *uptr,void *tptr,const struct ZT_Node_Callbacks *callbacks,int64_t now);
+ virtual ~Node();
+
+ // Get rid of alignment warnings on 32-bit Windows and possibly improve performance
+#ifdef __WINDOWS__
+ void * operator new(size_t i) { return _mm_malloc(i,16); }
+ void operator delete(void* p) { _mm_free(p); }
+#endif
// Public API Functions ----------------------------------------------------
ZT_ResultCode processWirePacket(
- uint64_t now,
- const struct sockaddr_storage *localAddress,
+ void *tptr,
+ int64_t now,
+ int64_t localSocket,
const struct sockaddr_storage *remoteAddress,
const void *packetData,
unsigned int packetLength,
- volatile uint64_t *nextBackgroundTaskDeadline);
+ volatile int64_t *nextBackgroundTaskDeadline);
ZT_ResultCode processVirtualNetworkFrame(
- uint64_t now,
+ void *tptr,
+ int64_t now,
uint64_t nwid,
uint64_t sourceMac,
uint64_t destMac,
@@ -85,12 +93,14 @@ public:
unsigned int vlanId,
const void *frameData,
unsigned int frameLength,
- volatile uint64_t *nextBackgroundTaskDeadline);
- ZT_ResultCode processBackgroundTasks(uint64_t now,volatile uint64_t *nextBackgroundTaskDeadline);
- ZT_ResultCode join(uint64_t nwid,void *uptr);
- ZT_ResultCode leave(uint64_t nwid,void **uptr);
- ZT_ResultCode multicastSubscribe(uint64_t nwid,uint64_t multicastGroup,unsigned long multicastAdi);
+ volatile int64_t *nextBackgroundTaskDeadline);
+ ZT_ResultCode processBackgroundTasks(void *tptr,int64_t now,volatile int64_t *nextBackgroundTaskDeadline);
+ ZT_ResultCode join(uint64_t nwid,void *uptr,void *tptr);
+ ZT_ResultCode leave(uint64_t nwid,void **uptr,void *tptr);
+ ZT_ResultCode multicastSubscribe(void *tptr,uint64_t nwid,uint64_t multicastGroup,unsigned long multicastAdi);
ZT_ResultCode multicastUnsubscribe(uint64_t nwid,uint64_t multicastGroup,unsigned long multicastAdi);
+ ZT_ResultCode orbit(void *tptr,uint64_t moonWorldId,uint64_t moonSeed);
+ ZT_ResultCode deorbit(void *tptr,uint64_t moonWorldId);
uint64_t address() const;
void status(ZT_NodeStatus *status) const;
ZT_PeerList *peers() const;
@@ -99,80 +109,32 @@ public:
void freeQueryResult(void *qr);
int addLocalInterfaceAddress(const struct sockaddr_storage *addr);
void clearLocalInterfaceAddresses();
+ int sendUserMessage(void *tptr,uint64_t dest,uint64_t typeId,const void *data,unsigned int len);
void setNetconfMaster(void *networkControllerInstance);
- ZT_ResultCode circuitTestBegin(ZT_CircuitTest *test,void (*reportCallback)(ZT_Node *,ZT_CircuitTest *,const ZT_CircuitTestReport *));
- void circuitTestEnd(ZT_CircuitTest *test);
- ZT_ResultCode clusterInit(
- unsigned int myId,
- const struct sockaddr_storage *zeroTierPhysicalEndpoints,
- unsigned int numZeroTierPhysicalEndpoints,
- int x,
- int y,
- int z,
- void (*sendFunction)(void *,unsigned int,const void *,unsigned int),
- void *sendFunctionArg,
- int (*addressToLocationFunction)(void *,const struct sockaddr_storage *,int *,int *,int *),
- void *addressToLocationFunctionArg);
- ZT_ResultCode clusterAddMember(unsigned int memberId);
- void clusterRemoveMember(unsigned int memberId);
- void clusterHandleIncomingMessage(const void *msg,unsigned int len);
- void clusterStatus(ZT_ClusterStatus *cs);
- void backgroundThreadMain();
// Internal functions ------------------------------------------------------
- /**
- * Convenience threadMain() for easy background thread launch
- *
- * This allows background threads to be launched with Thread::start
- * that will run against this node.
- */
- inline void threadMain() throw() { this->backgroundThreadMain(); }
-
- /**
- * @return Time as of last call to run()
- */
- inline uint64_t now() const throw() { return _now; }
+ inline int64_t now() const { return _now; }
- /**
- * Enqueue a ZeroTier message to be sent
- *
- * @param localAddress Local address
- * @param addr Destination address
- * @param data Packet data
- * @param len Packet length
- * @param ttl Desired TTL (default: 0 for unchanged/default TTL)
- * @return True if packet appears to have been sent
- */
- inline bool putPacket(const InetAddress &localAddress,const InetAddress &addr,const void *data,unsigned int len,unsigned int ttl = 0)
+ inline bool putPacket(void *tPtr,const int64_t localSocket,const InetAddress &addr,const void *data,unsigned int len,unsigned int ttl = 0)
{
- return (_wirePacketSendFunction(
+ return (_cb.wirePacketSendFunction(
reinterpret_cast<ZT_Node *>(this),
_uPtr,
- reinterpret_cast<const struct sockaddr_storage *>(&localAddress),
+ tPtr,
+ localSocket,
reinterpret_cast<const struct sockaddr_storage *>(&addr),
data,
len,
ttl) == 0);
}
- /**
- * Enqueue a frame to be injected into a tap device (port)
- *
- * @param nwid Network ID
- * @param nuptr Network user ptr
- * @param source Source MAC
- * @param dest Destination MAC
- * @param etherType 16-bit ethernet type
- * @param vlanId VLAN ID or 0 if none
- * @param data Frame data
- * @param len Frame length
- */
- inline void putFrame(uint64_t nwid,void **nuptr,const MAC &source,const MAC &dest,unsigned int etherType,unsigned int vlanId,const void *data,unsigned int len)
+ 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)
{
- _virtualNetworkFrameFunction(
+ _cb.virtualNetworkFrameFunction(
reinterpret_cast<ZT_Node *>(this),
_uPtr,
+ tPtr,
nwid,
nuptr,
source.toInt(),
@@ -183,123 +145,150 @@ public:
len);
}
- /**
- * @param localAddress Local address
- * @param remoteAddress Remote address
- * @return True if path should be used
- */
- bool shouldUsePathForZeroTierTraffic(const InetAddress &localAddress,const InetAddress &remoteAddress);
-
inline SharedPtr<Network> network(uint64_t nwid) const
{
Mutex::Lock _l(_networks_m);
- return _network(nwid);
+ const SharedPtr<Network> *n = _networks.get(nwid);
+ if (n)
+ return *n;
+ return SharedPtr<Network>();
}
inline bool belongsToNetwork(uint64_t nwid) const
{
Mutex::Lock _l(_networks_m);
- for(std::vector< std::pair< uint64_t, SharedPtr<Network> > >::const_iterator i=_networks.begin();i!=_networks.end();++i) {
- if (i->first == nwid)
- return true;
- }
- return false;
+ return _networks.contains(nwid);
}
inline std::vector< SharedPtr<Network> > allNetworks() const
{
std::vector< SharedPtr<Network> > nw;
Mutex::Lock _l(_networks_m);
- nw.reserve(_networks.size());
- for(std::vector< std::pair< uint64_t, SharedPtr<Network> > >::const_iterator i=_networks.begin();i!=_networks.end();++i)
- nw.push_back(i->second);
+ Hashtable< uint64_t,SharedPtr<Network> >::Iterator i(*const_cast< Hashtable< uint64_t,SharedPtr<Network> > * >(&_networks));
+ uint64_t *k = (uint64_t *)0;
+ SharedPtr<Network> *v = (SharedPtr<Network> *)0;
+ while (i.next(k,v))
+ nw.push_back(*v);
return nw;
}
- /**
- * @return Potential direct paths to me a.k.a. local interface addresses
- */
inline std::vector<InetAddress> directPaths() const
{
Mutex::Lock _l(_directPaths_m);
return _directPaths;
}
- inline bool dataStorePut(const char *name,const void *data,unsigned int len,bool secure) { return (_dataStorePutFunction(reinterpret_cast<ZT_Node *>(this),_uPtr,name,data,len,(int)secure) == 0); }
- inline bool dataStorePut(const char *name,const std::string &data,bool secure) { return dataStorePut(name,(const void *)data.data(),(unsigned int)data.length(),secure); }
- inline void dataStoreDelete(const char *name) { _dataStorePutFunction(reinterpret_cast<ZT_Node *>(this),_uPtr,name,(const void *)0,0,0); }
- std::string dataStoreGet(const char *name);
+ inline void postEvent(void *tPtr,ZT_Event ev,const void *md = (const void *)0) { _cb.eventCallback(reinterpret_cast<ZT_Node *>(this),_uPtr,tPtr,ev,md); }
+
+ inline int configureVirtualNetworkPort(void *tPtr,uint64_t nwid,void **nuptr,ZT_VirtualNetworkConfigOperation op,const ZT_VirtualNetworkConfig *nc) { return _cb.virtualNetworkConfigFunction(reinterpret_cast<ZT_Node *>(this),_uPtr,tPtr,nwid,nuptr,op,nc); }
+
+ inline bool online() const { return _online; }
+
+ inline int stateObjectGet(void *const tPtr,ZT_StateObjectType type,const uint64_t id[2],void *const data,const unsigned int maxlen) { return _cb.stateGetFunction(reinterpret_cast<ZT_Node *>(this),_uPtr,tPtr,type,id,data,maxlen); }
+ inline void stateObjectPut(void *const tPtr,ZT_StateObjectType type,const uint64_t id[2],const void *const data,const unsigned int len) { _cb.statePutFunction(reinterpret_cast<ZT_Node *>(this),_uPtr,tPtr,type,id,data,(int)len); }
+ inline void stateObjectDelete(void *const tPtr,ZT_StateObjectType type,const uint64_t id[2]) { _cb.statePutFunction(reinterpret_cast<ZT_Node *>(this),_uPtr,tPtr,type,id,(const void *)0,-1); }
+
+ bool shouldUsePathForZeroTierTraffic(void *tPtr,const Address &ztaddr,const int64_t localSocket,const InetAddress &remoteAddress);
+ inline bool externalPathLookup(void *tPtr,const Address &ztaddr,int family,InetAddress &addr) { return ( (_cb.pathLookupFunction) ? (_cb.pathLookupFunction(reinterpret_cast<ZT_Node *>(this),_uPtr,tPtr,ztaddr.toInt(),family,reinterpret_cast<struct sockaddr_storage *>(&addr)) != 0) : false ); }
+
+ uint64_t prng();
+ ZT_ResultCode setPhysicalPathConfiguration(const struct sockaddr_storage *pathNetwork,const ZT_PhysicalPathConfiguration *pathConfig);
+
+ World planet() const;
+ std::vector<World> moons() const;
+
+ inline const Identity &identity() const { return _RR.identity; }
/**
- * Post an event to the external user
+ * Register that we are expecting a reply to a packet ID
*
- * @param ev Event type
- * @param md Meta-data (default: NULL/none)
+ * This only uses the most significant bits of the packet ID, both to save space
+ * and to avoid using the higher bits that can be modified during armor() to
+ * mask against the packet send counter used for QoS detection.
+ *
+ * @param packetId Packet ID to expect reply to
*/
- inline void postEvent(ZT_Event ev,const void *md = (const void *)0) { _eventCallback(reinterpret_cast<ZT_Node *>(this),_uPtr,ev,md); }
+ inline void expectReplyTo(const uint64_t packetId)
+ {
+ const unsigned long pid2 = (unsigned long)(packetId >> 32);
+ const unsigned long bucket = (unsigned long)(pid2 & ZT_EXPECTING_REPLIES_BUCKET_MASK1);
+ _expectingRepliesTo[bucket][_expectingRepliesToBucketPtr[bucket]++ & ZT_EXPECTING_REPLIES_BUCKET_MASK2] = (uint32_t)pid2;
+ }
/**
- * Update virtual network port configuration
+ * Check whether a given packet ID is something we are expecting a reply to
+ *
+ * This only uses the most significant bits of the packet ID, both to save space
+ * and to avoid using the higher bits that can be modified during armor() to
+ * mask against the packet send counter used for QoS detection.
*
- * @param nwid Network ID
- * @param nuptr Network user ptr
- * @param op Configuration operation
- * @param nc Network configuration
+ * @param packetId Packet ID to check
+ * @return True if we're expecting a reply
*/
- inline int configureVirtualNetworkPort(uint64_t nwid,void **nuptr,ZT_VirtualNetworkConfigOperation op,const ZT_VirtualNetworkConfig *nc) { return _virtualNetworkConfigFunction(reinterpret_cast<ZT_Node *>(this),_uPtr,nwid,nuptr,op,nc); }
-
- inline bool online() const throw() { return _online; }
-
-#ifdef ZT_TRACE
- void postTrace(const char *module,unsigned int line,const char *fmt,...);
-#endif
-
- uint64_t prng();
- void postCircuitTestReport(const ZT_CircuitTestReport *report);
- void setTrustedPaths(const struct sockaddr_storage *networks,const uint64_t *ids,unsigned int count);
+ inline bool expectingReplyTo(const uint64_t packetId) const
+ {
+ const uint32_t pid2 = (uint32_t)(packetId >> 32);
+ const unsigned long bucket = (unsigned long)(pid2 & ZT_EXPECTING_REPLIES_BUCKET_MASK1);
+ for(unsigned long i=0;i<=ZT_EXPECTING_REPLIES_BUCKET_MASK2;++i) {
+ if (_expectingRepliesTo[bucket][i] == pid2)
+ return true;
+ }
+ return false;
+ }
-private:
- inline SharedPtr<Network> _network(uint64_t nwid) const
+ /**
+ * Check whether we should do potentially expensive identity verification (rate limit)
+ *
+ * @param now Current time
+ * @param from Source address of packet
+ * @return True if within rate limits
+ */
+ inline bool rateGateIdentityVerification(const int64_t now,const InetAddress &from)
{
- // assumes _networks_m is locked
- for(std::vector< std::pair< uint64_t, SharedPtr<Network> > >::const_iterator i=_networks.begin();i!=_networks.end();++i) {
- if (i->first == nwid)
- return i->second;
+ unsigned long iph = from.rateGateHash();
+ if ((now - _lastIdentityVerification[iph]) >= ZT_IDENTITY_VALIDATION_SOURCE_RATE_LIMIT) {
+ _lastIdentityVerification[iph] = now;
+ return true;
}
- return SharedPtr<Network>();
+ return false;
}
+ virtual void ncSendConfig(uint64_t nwid,uint64_t requestPacketId,const Address &destination,const NetworkConfig &nc,bool sendLegacyFormatConfig);
+ virtual void ncSendRevocation(const Address &destination,const Revocation &rev);
+ virtual void ncSendError(uint64_t nwid,uint64_t requestPacketId,const Address &destination,NetworkController::ErrorCode errorCode);
+
+ inline const Address &remoteTraceTarget() const { return _remoteTraceTarget; }
+ inline Trace::Level remoteTraceLevel() const { return _remoteTraceLevel; }
+
+private:
RuntimeEnvironment _RR;
RuntimeEnvironment *RR;
-
void *_uPtr; // _uptr (lower case) is reserved in Visual Studio :P
+ ZT_Node_Callbacks _cb;
- ZT_DataStoreGetFunction _dataStoreGetFunction;
- ZT_DataStorePutFunction _dataStorePutFunction;
- ZT_WirePacketSendFunction _wirePacketSendFunction;
- ZT_VirtualNetworkFrameFunction _virtualNetworkFrameFunction;
- ZT_VirtualNetworkConfigFunction _virtualNetworkConfigFunction;
- ZT_PathCheckFunction _pathCheckFunction;
- ZT_EventCallback _eventCallback;
+ // For tracking packet IDs to filter out OK/ERROR replies to packets we did not send
+ uint8_t _expectingRepliesToBucketPtr[ZT_EXPECTING_REPLIES_BUCKET_MASK1 + 1];
+ uint32_t _expectingRepliesTo[ZT_EXPECTING_REPLIES_BUCKET_MASK1 + 1][ZT_EXPECTING_REPLIES_BUCKET_MASK2 + 1];
- std::vector< std::pair< uint64_t, SharedPtr<Network> > > _networks;
- Mutex _networks_m;
+ // Time of last identity verification indexed by InetAddress.rateGateHash() -- used in IncomingPacket::_doHELLO() via rateGateIdentityVerification()
+ int64_t _lastIdentityVerification[16384];
- std::vector< ZT_CircuitTest * > _circuitTests;
- Mutex _circuitTests_m;
+ Hashtable< uint64_t,SharedPtr<Network> > _networks;
+ Mutex _networks_m;
std::vector<InetAddress> _directPaths;
Mutex _directPaths_m;
Mutex _backgroundTasksLock;
- unsigned int _prngStreamPtr;
- Salsa20 _prng;
- uint64_t _prngStream[16]; // repeatedly encrypted with _prng to yield a high-quality non-crypto PRNG stream
+ Address _remoteTraceTarget;
+ enum Trace::Level _remoteTraceLevel;
- uint64_t _now;
- uint64_t _lastPingCheck;
- uint64_t _lastHousekeepingRun;
+ volatile int64_t _now;
+ int64_t _lastPingCheck;
+ int64_t _lastHousekeepingRun;
+ int64_t _lastMemoizedTraceSettings;
+ volatile int64_t _prngState[2];
bool _online;
};
diff --git a/node/NonCopyable.hpp b/node/NonCopyable.hpp
deleted file mode 100644
index 6d4daa86..00000000
--- a/node/NonCopyable.hpp
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef ZT_NONCOPYABLE_HPP__
-#define ZT_NONCOPYABLE_HPP__
-
-namespace ZeroTier {
-
-/**
- * A simple concept that belongs in the C++ language spec
- */
-class NonCopyable
-{
-protected:
- NonCopyable() throw() {}
-private:
- NonCopyable(const NonCopyable&);
- const NonCopyable& operator=(const NonCopyable&);
-};
-
-} // namespace ZeroTier
-
-#endif
diff --git a/node/OutboundMulticast.cpp b/node/OutboundMulticast.cpp
index eea1132c..d7a7b4d8 100644
--- a/node/OutboundMulticast.cpp
+++ b/node/OutboundMulticast.cpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#include "Constants.hpp"
@@ -21,8 +29,9 @@
#include "OutboundMulticast.hpp"
#include "Switch.hpp"
#include "Network.hpp"
-#include "CertificateOfMembership.hpp"
#include "Node.hpp"
+#include "Peer.hpp"
+#include "Topology.hpp"
namespace ZeroTier {
@@ -30,7 +39,7 @@ void OutboundMulticast::init(
const RuntimeEnvironment *RR,
uint64_t timestamp,
uint64_t nwid,
- const CertificateOfMembership *com,
+ bool disableCompression,
unsigned int limit,
unsigned int gatherLimit,
const MAC &src,
@@ -39,75 +48,51 @@ void OutboundMulticast::init(
const void *payload,
unsigned int len)
{
+ uint8_t flags = 0;
+
_timestamp = timestamp;
_nwid = nwid;
+ if (src) {
+ _macSrc = src;
+ flags |= 0x04;
+ } else {
+ _macSrc.fromAddress(RR->identity.address(),nwid);
+ }
+ _macDest = dest.mac();
_limit = limit;
+ _frameLen = (len < ZT_MAX_MTU) ? len : ZT_MAX_MTU;
+ _etherType = etherType;
- uint8_t flags = 0;
if (gatherLimit) flags |= 0x02;
- if (src) flags |= 0x04;
-
- /*
- TRACE(">>MC %.16llx INIT %.16llx/%s limit %u gatherLimit %u from %s to %s length %u com==%d",
- (unsigned long long)this,
- nwid,
- dest.toString().c_str(),
- limit,
- gatherLimit,
- (src) ? src.toString().c_str() : MAC(RR->identity.address(),nwid).toString().c_str(),
- dest.toString().c_str(),
- len,
- (com) ? 1 : 0);
- */
-
- _packetNoCom.setSource(RR->identity.address());
- _packetNoCom.setVerb(Packet::VERB_MULTICAST_FRAME);
- _packetNoCom.append((uint64_t)nwid);
- _packetNoCom.append(flags);
- if (gatherLimit) _packetNoCom.append((uint32_t)gatherLimit);
- if (src) src.appendTo(_packetNoCom);
- dest.mac().appendTo(_packetNoCom);
- _packetNoCom.append((uint32_t)dest.adi());
- _packetNoCom.append((uint16_t)etherType);
- _packetNoCom.append(payload,len);
- _packetNoCom.compress();
- if (com) {
- _haveCom = true;
- flags |= 0x01;
+ _packet.setSource(RR->identity.address());
+ _packet.setVerb(Packet::VERB_MULTICAST_FRAME);
+ _packet.append((uint64_t)nwid);
+ _packet.append(flags);
+ if (gatherLimit) _packet.append((uint32_t)gatherLimit);
+ if (src) src.appendTo(_packet);
+ dest.mac().appendTo(_packet);
+ _packet.append((uint32_t)dest.adi());
+ _packet.append((uint16_t)etherType);
+ _packet.append(payload,_frameLen);
+ if (!disableCompression)
+ _packet.compress();
- _packetWithCom.setSource(RR->identity.address());
- _packetWithCom.setVerb(Packet::VERB_MULTICAST_FRAME);
- _packetWithCom.append((uint64_t)nwid);
- _packetWithCom.append(flags);
- com->serialize(_packetWithCom);
- if (gatherLimit) _packetWithCom.append((uint32_t)gatherLimit);
- if (src) src.appendTo(_packetWithCom);
- dest.mac().appendTo(_packetWithCom);
- _packetWithCom.append((uint32_t)dest.adi());
- _packetWithCom.append((uint16_t)etherType);
- _packetWithCom.append(payload,len);
- _packetWithCom.compress();
- } else _haveCom = false;
+ ZT_FAST_MEMCPY(_frameData,payload,_frameLen);
}
-void OutboundMulticast::sendOnly(const RuntimeEnvironment *RR,const Address &toAddr)
+void OutboundMulticast::sendOnly(const RuntimeEnvironment *RR,void *tPtr,const Address &toAddr)
{
- if (_haveCom) {
- SharedPtr<Peer> peer(RR->topology->getPeer(toAddr));
- if ( (!peer) || (peer->needsOurNetworkMembershipCertificate(_nwid,RR->node->now(),true)) ) {
- //TRACE(">>MC %.16llx -> %s (with COM)",(unsigned long long)this,toAddr.toString().c_str());
- _packetWithCom.newInitializationVector();
- _packetWithCom.setDestination(toAddr);
- RR->sw->send(_packetWithCom,true,_nwid);
- return;
- }
- }
+ const SharedPtr<Network> nw(RR->node->network(_nwid));
+ const Address toAddr2(toAddr);
+ if ((nw)&&(nw->filterOutgoingPacket(tPtr,true,RR->identity.address(),toAddr2,_macSrc,_macDest,_frameData,_frameLen,_etherType,0))) {
+ _packet.newInitializationVector();
+ _packet.setDestination(toAddr2);
+ RR->node->expectReplyTo(_packet.packetId());
- //TRACE(">>MC %.16llx -> %s (without COM)",(unsigned long long)this,toAddr.toString().c_str());
- _packetNoCom.newInitializationVector();
- _packetNoCom.setDestination(toAddr);
- RR->sw->send(_packetNoCom,true,_nwid);
+ Packet tmp(_packet); // make a copy of packet so as not to garble the original -- GitHub issue #461
+ RR->sw->send(tPtr,tmp,true);
+ }
}
} // namespace ZeroTier
diff --git a/node/OutboundMulticast.hpp b/node/OutboundMulticast.hpp
index 3818172e..a735f52b 100644
--- a/node/OutboundMulticast.hpp
+++ b/node/OutboundMulticast.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_OUTBOUNDMULTICAST_HPP
@@ -56,7 +64,7 @@ public:
* @param RR Runtime environment
* @param timestamp Creation time
* @param nwid Network ID
- * @param com Certificate of membership or NULL if none available
+ * @param disableCompression Disable compression of frame payload
* @param limit Multicast limit for desired number of packets to send
* @param gatherLimit Number to lazily/implicitly gather with this frame or 0 for none
* @param src Source MAC address of frame or NULL to imply compute from sender ZT address
@@ -70,7 +78,7 @@ public:
const RuntimeEnvironment *RR,
uint64_t timestamp,
uint64_t nwid,
- const CertificateOfMembership *com,
+ bool disableCompression,
unsigned int limit,
unsigned int gatherLimit,
const MAC &src,
@@ -82,62 +90,80 @@ public:
/**
* @return Multicast creation time
*/
- inline uint64_t timestamp() const throw() { return _timestamp; }
+ inline uint64_t timestamp() const { return _timestamp; }
/**
* @param now Current time
* @return True if this multicast is expired (has exceeded transmit timeout)
*/
- inline bool expired(uint64_t now) const throw() { return ((now - _timestamp) >= ZT_MULTICAST_TRANSMIT_TIMEOUT); }
+ inline bool expired(int64_t now) const { return ((now - _timestamp) >= ZT_MULTICAST_TRANSMIT_TIMEOUT); }
/**
* @return True if this outbound multicast has been sent to enough peers
*/
- inline bool atLimit() const throw() { return (_alreadySentTo.size() >= _limit); }
+ inline bool atLimit() const { return (_alreadySentTo.size() >= _limit); }
/**
* Just send without checking log
*
* @param RR Runtime environment
+ * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
* @param toAddr Destination address
*/
- void sendOnly(const RuntimeEnvironment *RR,const Address &toAddr);
+ void sendOnly(const RuntimeEnvironment *RR,void *tPtr,const Address &toAddr);
/**
* Just send and log but do not check sent log
*
* @param RR Runtime environment
+ * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
* @param toAddr Destination address
*/
- inline void sendAndLog(const RuntimeEnvironment *RR,const Address &toAddr)
+ inline void sendAndLog(const RuntimeEnvironment *RR,void *tPtr,const Address &toAddr)
+ {
+ _alreadySentTo.push_back(toAddr);
+ sendOnly(RR,tPtr,toAddr);
+ }
+
+ /**
+ * Log an address as having been used so we will not send there in the future
+ *
+ * @param toAddr Address to log as sent
+ */
+ inline void logAsSent(const Address &toAddr)
{
_alreadySentTo.push_back(toAddr);
- sendOnly(RR,toAddr);
}
/**
* Try to send this to a given peer if it hasn't been sent to them already
*
* @param RR Runtime environment
+ * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
* @param toAddr Destination address
* @return True if address is new and packet was sent to switch, false if duplicate
*/
- inline bool sendIfNew(const RuntimeEnvironment *RR,const Address &toAddr)
+ inline bool sendIfNew(const RuntimeEnvironment *RR,void *tPtr,const Address &toAddr)
{
if (std::find(_alreadySentTo.begin(),_alreadySentTo.end(),toAddr) == _alreadySentTo.end()) {
- sendAndLog(RR,toAddr);
+ sendAndLog(RR,tPtr,toAddr);
return true;
- } else return false;
+ } else {
+ return false;
+ }
}
private:
uint64_t _timestamp;
uint64_t _nwid;
+ MAC _macSrc;
+ MAC _macDest;
unsigned int _limit;
- Packet _packetNoCom;
- Packet _packetWithCom;
+ unsigned int _frameLen;
+ unsigned int _etherType;
+ Packet _packet;
std::vector<Address> _alreadySentTo;
- bool _haveCom;
+ uint8_t _frameData[ZT_MAX_MTU];
};
} // namespace ZeroTier
diff --git a/node/Packet.cpp b/node/Packet.cpp
index 3330a927..2eeceffa 100644
--- a/node/Packet.cpp
+++ b/node/Packet.cpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,143 +14,1013 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
+#include <stdint.h>
+#include <stddef.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+
#include "Packet.hpp"
+#ifdef ZT_USE_X64_ASM_SALSA2012
+#include "../ext/x64-salsa2012-asm/salsa2012.h"
+#endif
+#ifdef ZT_USE_ARM32_NEON_ASM_SALSA2012
+#include "../ext/arm32-neon-salsa2012-asm/salsa2012.h"
+#endif
+
+#ifdef _MSC_VER
+#define FORCE_INLINE static __forceinline
+#include <intrin.h>
+#pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */
+#pragma warning(disable : 4293) /* disable: C4293: too large shift (32-bits) */
+#else
+#define FORCE_INLINE static inline
+#endif
+
namespace ZeroTier {
-const unsigned char Packet::ZERO_KEY[32] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 };
+/************************************************************************** */
+
+/* Set up macros for fast single-pass ASM Salsa20/12 crypto, if we have it */
+
+// x64 SSE crypto
+#ifdef ZT_USE_X64_ASM_SALSA2012
+#define ZT_HAS_FAST_CRYPTO() (true)
+#define ZT_FAST_SINGLE_PASS_SALSA2012(b,l,n,k) zt_salsa2012_amd64_xmm6(reinterpret_cast<unsigned char *>(b),(l),reinterpret_cast<const unsigned char *>(n),reinterpret_cast<const unsigned char *>(k))
+#endif
+
+// ARM (32-bit) NEON crypto (must be detected)
+#ifdef ZT_USE_ARM32_NEON_ASM_SALSA2012
+class _FastCryptoChecker
+{
+public:
+ _FastCryptoChecker() : canHas(zt_arm_has_neon()) {}
+ bool canHas;
+};
+static const _FastCryptoChecker _ZT_FAST_CRYPTO_CHECK;
+#define ZT_HAS_FAST_CRYPTO() (_ZT_FAST_CRYPTO_CHECK.canHas)
+#define ZT_FAST_SINGLE_PASS_SALSA2012(b,l,n,k) zt_salsa2012_armneon3_xor(reinterpret_cast<unsigned char *>(b),(const unsigned char *)0,(l),reinterpret_cast<const unsigned char *>(n),reinterpret_cast<const unsigned char *>(k))
+#endif
+
+// No fast crypto available
+#ifndef ZT_HAS_FAST_CRYPTO
+#define ZT_HAS_FAST_CRYPTO() (false)
+#define ZT_FAST_SINGLE_PASS_SALSA2012(b,l,n,k) {}
+#endif
+
+/************************************************************************** */
+
+/* LZ4 is shipped encapsulated into Packet in an anonymous namespace.
+ *
+ * We're doing this as a deliberate workaround for various Linux distribution
+ * policies that forbid static linking of support libraries.
+ *
+ * The reason is that relying on distribution versions of LZ4 has been too
+ * big a source of bugs and compatibility issues. The LZ4 API is not stable
+ * enough across versions, and dependency hell ensues. So fark it. */
+
+/* Needless to say the code in this anonymous namespace should be considered
+ * BSD 2-clause licensed. */
+
+namespace {
+
+/* lz4.h ------------------------------------------------------------------ */
+
+/*
+ * LZ4 - Fast LZ compression algorithm
+ * Header File
+ * Copyright (C) 2011-2016, Yann Collet.
+
+ BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are
+ met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following disclaimer
+ in the documentation and/or other materials provided with the
+ distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ You can contact the author at :
+ - LZ4 homepage : http://www.lz4.org
+ - LZ4 source repository : https://github.com/lz4/lz4
+*/
-//#ifdef ZT_TRACE
-
-const char *Packet::verbString(Verb v)
- throw()
-{
- switch(v) {
- case VERB_NOP: return "NOP";
- case VERB_HELLO: return "HELLO";
- case VERB_ERROR: return "ERROR";
- case VERB_OK: return "OK";
- case VERB_WHOIS: return "WHOIS";
- case VERB_RENDEZVOUS: return "RENDEZVOUS";
- case VERB_FRAME: return "FRAME";
- case VERB_EXT_FRAME: return "EXT_FRAME";
- case VERB_ECHO: return "ECHO";
- case VERB_MULTICAST_LIKE: return "MULTICAST_LIKE";
- case VERB_NETWORK_MEMBERSHIP_CERTIFICATE: return "NETWORK_MEMBERSHIP_CERTIFICATE";
- case VERB_NETWORK_CONFIG_REQUEST: return "NETWORK_CONFIG_REQUEST";
- case VERB_NETWORK_CONFIG_REFRESH: return "NETWORK_CONFIG_REFRESH";
- case VERB_MULTICAST_GATHER: return "MULTICAST_GATHER";
- case VERB_MULTICAST_FRAME: return "MULTICAST_FRAME";
- case VERB_PUSH_DIRECT_PATHS: return "PUSH_DIRECT_PATHS";
- case VERB_CIRCUIT_TEST: return "CIRCUIT_TEST";
- case VERB_CIRCUIT_TEST_REPORT: return "CIRCUIT_TEST_REPORT";
- case VERB_REQUEST_PROOF_OF_WORK: return "REQUEST_PROOF_OF_WORK";
+/**
+ Introduction
+
+ LZ4 is lossless compression algorithm, providing compression speed at 400 MB/s per core,
+ scalable with multi-cores CPU. It features an extremely fast decoder, with speed in
+ multiple GB/s per core, typically reaching RAM speed limits on multi-core systems.
+
+ The LZ4 compression library provides in-memory compression and decompression functions.
+ Compression can be done in:
+ - a single step (described as Simple Functions)
+ - a single step, reusing a context (described in Advanced Functions)
+ - unbounded multiple steps (described as Streaming compression)
+
+ lz4.h provides block compression functions. It gives full buffer control to user.
+ Decompressing an lz4-compressed block also requires metadata (such as compressed size).
+ Each application is free to encode such metadata in whichever way it wants.
+
+ An additional format, called LZ4 frame specification (doc/lz4_Frame_format.md),
+ take care of encoding standard metadata alongside LZ4-compressed blocks.
+ If your application requires interoperability, it's recommended to use it.
+ A library is provided to take care of it, see lz4frame.h.
+*/
+
+#define LZ4_VERSION_MAJOR 1 /* for breaking interface changes */
+#define LZ4_VERSION_MINOR 7 /* for new (non-breaking) interface capabilities */
+#define LZ4_VERSION_RELEASE 5 /* for tweaks, bug-fixes, or development */
+#define LZ4_VERSION_NUMBER (LZ4_VERSION_MAJOR *100*100 + LZ4_VERSION_MINOR *100 + LZ4_VERSION_RELEASE)
+#define LZ4_LIB_VERSION LZ4_VERSION_MAJOR.LZ4_VERSION_MINOR.LZ4_VERSION_RELEASE
+#define LZ4_QUOTE(str) #str
+#define LZ4_EXPAND_AND_QUOTE(str) LZ4_QUOTE(str)
+#define LZ4_VERSION_STRING LZ4_EXPAND_AND_QUOTE(LZ4_LIB_VERSION)
+#define LZ4_MEMORY_USAGE 14
+#define LZ4_MAX_INPUT_SIZE 0x7E000000 /* 2 113 929 216 bytes */
+#define LZ4_COMPRESSBOUND(isize) ((unsigned)(isize) > (unsigned)LZ4_MAX_INPUT_SIZE ? 0 : (isize) + ((isize)/255) + 16)
+
+typedef union LZ4_stream_u LZ4_stream_t; /* incomplete type (defined later) */
+
+static inline void LZ4_resetStream (LZ4_stream_t* streamPtr);
+
+#define LZ4_HASHLOG (LZ4_MEMORY_USAGE-2)
+#define LZ4_HASHTABLESIZE (1 << LZ4_MEMORY_USAGE)
+#define LZ4_HASH_SIZE_U32 (1 << LZ4_HASHLOG) /* required as macro for static allocation */
+
+typedef struct {
+ uint32_t hashTable[LZ4_HASH_SIZE_U32];
+ uint32_t currentOffset;
+ uint32_t initCheck;
+ const uint8_t* dictionary;
+ uint8_t* bufferStart; /* obsolete, used for slideInputBuffer */
+ uint32_t dictSize;
+} LZ4_stream_t_internal;
+
+typedef struct {
+ const uint8_t* externalDict;
+ size_t extDictSize;
+ const uint8_t* prefixEnd;
+ size_t prefixSize;
+} LZ4_streamDecode_t_internal;
+
+#define LZ4_STREAMSIZE_U64 ((1 << (LZ4_MEMORY_USAGE-3)) + 4)
+#define LZ4_STREAMSIZE (LZ4_STREAMSIZE_U64 * sizeof(unsigned long long))
+union LZ4_stream_u {
+ unsigned long long table[LZ4_STREAMSIZE_U64];
+ LZ4_stream_t_internal internal_donotuse;
+} ; /* previously typedef'd to LZ4_stream_t */
+
+#define LZ4_STREAMDECODESIZE_U64 4
+#define LZ4_STREAMDECODESIZE (LZ4_STREAMDECODESIZE_U64 * sizeof(unsigned long long))
+union LZ4_streamDecode_u {
+ unsigned long long table[LZ4_STREAMDECODESIZE_U64];
+ LZ4_streamDecode_t_internal internal_donotuse;
+} ; /* previously typedef'd to LZ4_streamDecode_t */
+
+#ifndef HEAPMODE
+#define HEAPMODE 0
+#endif
+
+#ifdef ZT_NO_TYPE_PUNNING
+#define LZ4_FORCE_MEMORY_ACCESS 0
+#else
+#define LZ4_FORCE_MEMORY_ACCESS 2
+#endif
+
+#if defined(_MSC_VER) && defined(_WIN32_WCE) /* Visual Studio for Windows CE does not support Hardware bit count */
+#define LZ4_FORCE_SW_BITCOUNT
+#endif
+
+#ifndef FORCE_INLINE
+#define FORCE_INLINE static inline
+#endif
+
+#define ALLOCATOR(n,s) calloc(n,s)
+#define FREEMEM free
+#define MEM_INIT memset
+
+typedef uint8_t BYTE;
+typedef uint16_t U16;
+typedef uint32_t U32;
+typedef int32_t S32;
+typedef uint64_t U64;
+typedef uintptr_t uptrval;
+typedef uintptr_t reg_t;
+
+static inline unsigned LZ4_isLittleEndian(void)
+{
+ const union { U32 u; BYTE c[4]; } one = { 1 }; /* don't use static : performance detrimental */
+ return one.c[0];
+}
+
+#if defined(LZ4_FORCE_MEMORY_ACCESS) && (LZ4_FORCE_MEMORY_ACCESS==2)
+static U16 LZ4_read16(const void* memPtr) { return *(const U16*) memPtr; }
+static U32 LZ4_read32(const void* memPtr) { return *(const U32*) memPtr; }
+static reg_t LZ4_read_ARCH(const void* memPtr) { return *(const reg_t*) memPtr; }
+static void LZ4_write16(void* memPtr, U16 value) { *(U16*)memPtr = value; }
+static void LZ4_write32(void* memPtr, U32 value) { *(U32*)memPtr = value; }
+#elif defined(LZ4_FORCE_MEMORY_ACCESS) && (LZ4_FORCE_MEMORY_ACCESS==1)
+typedef union { U16 u16; U32 u32; reg_t uArch; } __attribute__((packed)) unalign;
+static U16 LZ4_read16(const void* ptr) { return ((const unalign*)ptr)->u16; }
+static U32 LZ4_read32(const void* ptr) { return ((const unalign*)ptr)->u32; }
+static reg_t LZ4_read_ARCH(const void* ptr) { return ((const unalign*)ptr)->uArch; }
+static void LZ4_write16(void* memPtr, U16 value) { ((unalign*)memPtr)->u16 = value; }
+static void LZ4_write32(void* memPtr, U32 value) { ((unalign*)memPtr)->u32 = value; }
+#else /* safe and portable access through memcpy() */
+static inline U16 LZ4_read16(const void* memPtr)
+{
+ U16 val; ZT_FAST_MEMCPY(&val, memPtr, sizeof(val)); return val;
+}
+static inline U32 LZ4_read32(const void* memPtr)
+{
+ U32 val; ZT_FAST_MEMCPY(&val, memPtr, sizeof(val)); return val;
+}
+static inline reg_t LZ4_read_ARCH(const void* memPtr)
+{
+ reg_t val; ZT_FAST_MEMCPY(&val, memPtr, sizeof(val)); return val;
+}
+static inline void LZ4_write16(void* memPtr, U16 value)
+{
+ ZT_FAST_MEMCPY(memPtr, &value, sizeof(value));
+}
+static inline void LZ4_write32(void* memPtr, U32 value)
+{
+ ZT_FAST_MEMCPY(memPtr, &value, sizeof(value));
+}
+#endif /* LZ4_FORCE_MEMORY_ACCESS */
+
+static inline U16 LZ4_readLE16(const void* memPtr)
+{
+ if (LZ4_isLittleEndian()) {
+ return LZ4_read16(memPtr);
+ } else {
+ const BYTE* p = (const BYTE*)memPtr;
+ return (U16)((U16)p[0] + (p[1]<<8));
}
- return "(unknown)";
-}
-
-const char *Packet::errorString(ErrorCode e)
- throw()
-{
- switch(e) {
- case ERROR_NONE: return "NONE";
- case ERROR_INVALID_REQUEST: return "INVALID_REQUEST";
- case ERROR_BAD_PROTOCOL_VERSION: return "BAD_PROTOCOL_VERSION";
- case ERROR_OBJ_NOT_FOUND: return "OBJECT_NOT_FOUND";
- case ERROR_IDENTITY_COLLISION: return "IDENTITY_COLLISION";
- case ERROR_UNSUPPORTED_OPERATION: return "UNSUPPORTED_OPERATION";
- case ERROR_NEED_MEMBERSHIP_CERTIFICATE: return "NEED_MEMBERSHIP_CERTIFICATE";
- case ERROR_NETWORK_ACCESS_DENIED_: return "NETWORK_ACCESS_DENIED";
- case ERROR_UNWANTED_MULTICAST: return "UNWANTED_MULTICAST";
+}
+
+static inline void LZ4_writeLE16(void* memPtr, U16 value)
+{
+ if (LZ4_isLittleEndian()) {
+ LZ4_write16(memPtr, value);
+ } else {
+ BYTE* p = (BYTE*)memPtr;
+ p[0] = (BYTE) value;
+ p[1] = (BYTE)(value>>8);
}
- return "(unknown)";
}
-//#endif // ZT_TRACE
+static inline void LZ4_copy8(void* dst, const void* src)
+{
+ ZT_FAST_MEMCPY(dst,src,8);
+}
+
+static inline void LZ4_wildCopy(void* dstPtr, const void* srcPtr, void* dstEnd)
+{
+ BYTE* d = (BYTE*)dstPtr;
+ const BYTE* s = (const BYTE*)srcPtr;
+ BYTE* const e = (BYTE*)dstEnd;
+ do { LZ4_copy8(d,s); d+=8; s+=8; } while (d<e);
+}
+
+#define MINMATCH 4
+
+#define WILDCOPYLENGTH 8
+#define LASTLITERALS 5
+#define MFLIMIT (WILDCOPYLENGTH+MINMATCH)
+static const int LZ4_minLength = (MFLIMIT+1);
+
+#define KB *(1 <<10)
+#define MB *(1 <<20)
+#define GB *(1U<<30)
+
+#define MAXD_LOG 16
+#define MAX_DISTANCE ((1 << MAXD_LOG) - 1)
+
+#define ML_BITS 4
+#define ML_MASK ((1U<<ML_BITS)-1)
+#define RUN_BITS (8-ML_BITS)
+#define RUN_MASK ((1U<<RUN_BITS)-1)
+
+#define LZ4_STATIC_ASSERT(c) { enum { LZ4_static_assert = 1/(int)(!!(c)) }; } /* use only *after* variable declarations */
+
+static inline unsigned LZ4_NbCommonBytes (register reg_t val)
+{
+ if (LZ4_isLittleEndian()) {
+ if (sizeof(val)==8) {
+# if defined(_MSC_VER) && defined(_WIN64) && !defined(LZ4_FORCE_SW_BITCOUNT)
+ unsigned long r = 0;
+ _BitScanForward64( &r, (U64)val );
+ return (int)(r>>3);
+# elif (defined(__clang__) || (defined(__GNUC__) && (__GNUC__>=3))) && !defined(LZ4_FORCE_SW_BITCOUNT)
+ return (__builtin_ctzll((U64)val) >> 3);
+# else
+ static const int DeBruijnBytePos[64] = { 0, 0, 0, 0, 0, 1, 1, 2, 0, 3, 1, 3, 1, 4, 2, 7, 0, 2, 3, 6, 1, 5, 3, 5, 1, 3, 4, 4, 2, 5, 6, 7, 7, 0, 1, 2, 3, 3, 4, 6, 2, 6, 5, 5, 3, 4, 5, 6, 7, 1, 2, 4, 6, 4, 4, 5, 7, 2, 6, 5, 7, 6, 7, 7 };
+ return DeBruijnBytePos[((U64)((val & -(long long)val) * 0x0218A392CDABBD3FULL)) >> 58];
+# endif
+ } else /* 32 bits */ {
+# if defined(_MSC_VER) && !defined(LZ4_FORCE_SW_BITCOUNT)
+ unsigned long r;
+ _BitScanForward( &r, (U32)val );
+ return (int)(r>>3);
+# elif (defined(__clang__) || (defined(__GNUC__) && (__GNUC__>=3))) && !defined(LZ4_FORCE_SW_BITCOUNT)
+ return (__builtin_ctz((U32)val) >> 3);
+# else
+ static const int DeBruijnBytePos[32] = { 0, 0, 3, 0, 3, 1, 3, 0, 3, 2, 2, 1, 3, 2, 0, 1, 3, 3, 1, 2, 2, 2, 2, 0, 3, 1, 2, 0, 1, 0, 1, 1 };
+ return DeBruijnBytePos[((U32)((val & -(S32)val) * 0x077CB531U)) >> 27];
+# endif
+ }
+ } else /* Big Endian CPU */ {
+ if (sizeof(val)==8) {
+# if defined(_MSC_VER) && defined(_WIN64) && !defined(LZ4_FORCE_SW_BITCOUNT)
+ unsigned long r = 0;
+ _BitScanReverse64( &r, val );
+ return (unsigned)(r>>3);
+# elif (defined(__clang__) || (defined(__GNUC__) && (__GNUC__>=3))) && !defined(LZ4_FORCE_SW_BITCOUNT)
+ return (__builtin_clzll((U64)val) >> 3);
+# else
+ unsigned r;
+ if (!(val>>32)) { r=4; } else { r=0; val>>=32; }
+ if (!(val>>16)) { r+=2; val>>=8; } else { val>>=24; }
+ r += (!val);
+ return r;
+# endif
+ } else /* 32 bits */ {
+# if defined(_MSC_VER) && !defined(LZ4_FORCE_SW_BITCOUNT)
+ unsigned long r = 0;
+ _BitScanReverse( &r, (unsigned long)val );
+ return (unsigned)(r>>3);
+# elif (defined(__clang__) || (defined(__GNUC__) && (__GNUC__>=3))) && !defined(LZ4_FORCE_SW_BITCOUNT)
+ return (__builtin_clz((U32)val) >> 3);
+# else
+ unsigned r;
+ if (!(val>>16)) { r=2; val>>=8; } else { r=0; val>>=24; }
+ r += (!val);
+ return r;
+# endif
+ }
+ }
+}
+
+#define STEPSIZE sizeof(reg_t)
+static inline unsigned LZ4_count(const BYTE* pIn, const BYTE* pMatch, const BYTE* pInLimit)
+{
+ const BYTE* const pStart = pIn;
+
+ while (likely(pIn<pInLimit-(STEPSIZE-1))) {
+ reg_t const diff = LZ4_read_ARCH(pMatch) ^ LZ4_read_ARCH(pIn);
+ if (!diff) { pIn+=STEPSIZE; pMatch+=STEPSIZE; continue; }
+ pIn += LZ4_NbCommonBytes(diff);
+ return (unsigned)(pIn - pStart);
+ }
+
+ if ((STEPSIZE==8) && (pIn<(pInLimit-3)) && (LZ4_read32(pMatch) == LZ4_read32(pIn))) { pIn+=4; pMatch+=4; }
+ if ((pIn<(pInLimit-1)) && (LZ4_read16(pMatch) == LZ4_read16(pIn))) { pIn+=2; pMatch+=2; }
+ if ((pIn<pInLimit) && (*pMatch == *pIn)) pIn++;
+ return (unsigned)(pIn - pStart);
+}
+
+static const int LZ4_64Klimit = ((64 KB) + (MFLIMIT-1));
+static const U32 LZ4_skipTrigger = 6; /* Increase this value ==> compression run slower on incompressible data */
+
+typedef enum { notLimited = 0, limitedOutput = 1 } limitedOutput_directive;
+typedef enum { byPtr, byU32, byU16 } tableType_t;
+
+typedef enum { noDict = 0, withPrefix64k, usingExtDict } dict_directive;
+typedef enum { noDictIssue = 0, dictSmall } dictIssue_directive;
+
+typedef enum { endOnOutputSize = 0, endOnInputSize = 1 } endCondition_directive;
+typedef enum { full = 0, partial = 1 } earlyEnd_directive;
+
+static inline int LZ4_compressBound(int isize) { return LZ4_COMPRESSBOUND(isize); }
+
+static inline U32 LZ4_hash4(U32 sequence, tableType_t const tableType)
+{
+ if (tableType == byU16)
+ return ((sequence * 2654435761U) >> ((MINMATCH*8)-(LZ4_HASHLOG+1)));
+ else
+ return ((sequence * 2654435761U) >> ((MINMATCH*8)-LZ4_HASHLOG));
+}
+
+static inline U32 LZ4_hash5(U64 sequence, tableType_t const tableType)
+{
+ static const U64 prime5bytes = 889523592379ULL;
+ static const U64 prime8bytes = 11400714785074694791ULL;
+ const U32 hashLog = (tableType == byU16) ? LZ4_HASHLOG+1 : LZ4_HASHLOG;
+ if (LZ4_isLittleEndian())
+ return (U32)(((sequence << 24) * prime5bytes) >> (64 - hashLog));
+ else
+ return (U32)(((sequence >> 24) * prime8bytes) >> (64 - hashLog));
+}
+
+FORCE_INLINE U32 LZ4_hashPosition(const void* const p, tableType_t const tableType)
+{
+ if ((sizeof(reg_t)==8) && (tableType != byU16)) return LZ4_hash5(LZ4_read_ARCH(p), tableType);
+ return LZ4_hash4(LZ4_read32(p), tableType);
+}
+
+static inline void LZ4_putPositionOnHash(const BYTE* p, U32 h, void* tableBase, tableType_t const tableType, const BYTE* srcBase)
+{
+ switch (tableType)
+ {
+ case byPtr: { const BYTE** hashTable = (const BYTE**)tableBase; hashTable[h] = p; return; }
+ case byU32: { U32* hashTable = (U32*) tableBase; hashTable[h] = (U32)(p-srcBase); return; }
+ case byU16: { U16* hashTable = (U16*) tableBase; hashTable[h] = (U16)(p-srcBase); return; }
+ }
+}
+
+FORCE_INLINE void LZ4_putPosition(const BYTE* p, void* tableBase, tableType_t tableType, const BYTE* srcBase)
+{
+ U32 const h = LZ4_hashPosition(p, tableType);
+ LZ4_putPositionOnHash(p, h, tableBase, tableType, srcBase);
+}
+
+static inline const BYTE* LZ4_getPositionOnHash(U32 h, void* tableBase, tableType_t tableType, const BYTE* srcBase)
+{
+ if (tableType == byPtr) { const BYTE** hashTable = (const BYTE**) tableBase; return hashTable[h]; }
+ if (tableType == byU32) { const U32* const hashTable = (U32*) tableBase; return hashTable[h] + srcBase; }
+ { const U16* const hashTable = (U16*) tableBase; return hashTable[h] + srcBase; } /* default, to ensure a return */
+}
+
+FORCE_INLINE const BYTE* LZ4_getPosition(const BYTE* p, void* tableBase, tableType_t tableType, const BYTE* srcBase)
+{
+ U32 const h = LZ4_hashPosition(p, tableType);
+ return LZ4_getPositionOnHash(h, tableBase, tableType, srcBase);
+}
+
+FORCE_INLINE int LZ4_compress_generic(
+ LZ4_stream_t_internal* const cctx,
+ const char* const source,
+ char* const dest,
+ const int inputSize,
+ const int maxOutputSize,
+ const limitedOutput_directive outputLimited,
+ const tableType_t tableType,
+ const dict_directive dict,
+ const dictIssue_directive dictIssue,
+ const U32 acceleration)
+{
+ const BYTE* ip = (const BYTE*) source;
+ const BYTE* base;
+ const BYTE* lowLimit;
+ const BYTE* const lowRefLimit = ip - cctx->dictSize;
+ const BYTE* const dictionary = cctx->dictionary;
+ const BYTE* const dictEnd = dictionary + cctx->dictSize;
+ const ptrdiff_t dictDelta = dictEnd - (const BYTE*)source;
+ const BYTE* anchor = (const BYTE*) source;
+ const BYTE* const iend = ip + inputSize;
+ const BYTE* const mflimit = iend - MFLIMIT;
+ const BYTE* const matchlimit = iend - LASTLITERALS;
+
+ BYTE* op = (BYTE*) dest;
+ BYTE* const olimit = op + maxOutputSize;
+
+ U32 forwardH;
+
+ /* Init conditions */
+ if ((U32)inputSize > (U32)LZ4_MAX_INPUT_SIZE) return 0; /* Unsupported inputSize, too large (or negative) */
+ switch(dict)
+ {
+ case noDict:
+ default:
+ base = (const BYTE*)source;
+ lowLimit = (const BYTE*)source;
+ break;
+ case withPrefix64k:
+ base = (const BYTE*)source - cctx->currentOffset;
+ lowLimit = (const BYTE*)source - cctx->dictSize;
+ break;
+ case usingExtDict:
+ base = (const BYTE*)source - cctx->currentOffset;
+ lowLimit = (const BYTE*)source;
+ break;
+ }
+ if ((tableType == byU16) && (inputSize>=LZ4_64Klimit)) return 0; /* Size too large (not within 64K limit) */
+ if (inputSize<LZ4_minLength) goto _last_literals; /* Input too small, no compression (all literals) */
+
+ /* First Byte */
+ LZ4_putPosition(ip, cctx->hashTable, tableType, base);
+ ip++; forwardH = LZ4_hashPosition(ip, tableType);
+
+ /* Main Loop */
+ for ( ; ; ) {
+ ptrdiff_t refDelta = 0;
+ const BYTE* match;
+ BYTE* token;
+
+ /* Find a match */
+ { const BYTE* forwardIp = ip;
+ unsigned step = 1;
+ unsigned searchMatchNb = acceleration << LZ4_skipTrigger;
+ do {
+ U32 const h = forwardH;
+ ip = forwardIp;
+ forwardIp += step;
+ step = (searchMatchNb++ >> LZ4_skipTrigger);
+
+ if (unlikely(forwardIp > mflimit)) goto _last_literals;
+
+ match = LZ4_getPositionOnHash(h, cctx->hashTable, tableType, base);
+ if (dict==usingExtDict) {
+ if (match < (const BYTE*)source) {
+ refDelta = dictDelta;
+ lowLimit = dictionary;
+ } else {
+ refDelta = 0;
+ lowLimit = (const BYTE*)source;
+ } }
+ forwardH = LZ4_hashPosition(forwardIp, tableType);
+ LZ4_putPositionOnHash(ip, h, cctx->hashTable, tableType, base);
+
+ } while ( ((dictIssue==dictSmall) ? (match < lowRefLimit) : 0)
+ || ((tableType==byU16) ? 0 : (match + MAX_DISTANCE < ip))
+ || (LZ4_read32(match+refDelta) != LZ4_read32(ip)) );
+ }
+
+ /* Catch up */
+ while (((ip>anchor) & (match+refDelta > lowLimit)) && (unlikely(ip[-1]==match[refDelta-1]))) { ip--; match--; }
+
+ /* Encode Literals */
+ { unsigned const litLength = (unsigned)(ip - anchor);
+ token = op++;
+ if ((outputLimited) && /* Check output buffer overflow */
+ (unlikely(op + litLength + (2 + 1 + LASTLITERALS) + (litLength/255) > olimit)))
+ return 0;
+ if (litLength >= RUN_MASK) {
+ int len = (int)litLength-RUN_MASK;
+ *token = (RUN_MASK<<ML_BITS);
+ for(; len >= 255 ; len-=255) *op++ = 255;
+ *op++ = (BYTE)len;
+ }
+ else *token = (BYTE)(litLength<<ML_BITS);
+
+ /* Copy Literals */
+ LZ4_wildCopy(op, anchor, op+litLength);
+ op+=litLength;
+ }
+
+_next_match:
+ /* Encode Offset */
+ LZ4_writeLE16(op, (U16)(ip-match)); op+=2;
+
+ /* Encode MatchLength */
+ { unsigned matchCode;
+
+ if ((dict==usingExtDict) && (lowLimit==dictionary)) {
+ const BYTE* limit;
+ match += refDelta;
+ limit = ip + (dictEnd-match);
+ if (limit > matchlimit) limit = matchlimit;
+ matchCode = LZ4_count(ip+MINMATCH, match+MINMATCH, limit);
+ ip += MINMATCH + matchCode;
+ if (ip==limit) {
+ unsigned const more = LZ4_count(ip, (const BYTE*)source, matchlimit);
+ matchCode += more;
+ ip += more;
+ }
+ } else {
+ matchCode = LZ4_count(ip+MINMATCH, match+MINMATCH, matchlimit);
+ ip += MINMATCH + matchCode;
+ }
+
+ if ( outputLimited && /* Check output buffer overflow */
+ (unlikely(op + (1 + LASTLITERALS) + (matchCode>>8) > olimit)) )
+ return 0;
+ if (matchCode >= ML_MASK) {
+ *token += ML_MASK;
+ matchCode -= ML_MASK;
+ LZ4_write32(op, 0xFFFFFFFF);
+ while (matchCode >= 4*255) op+=4, LZ4_write32(op, 0xFFFFFFFF), matchCode -= 4*255;
+ op += matchCode / 255;
+ *op++ = (BYTE)(matchCode % 255);
+ } else
+ *token += (BYTE)(matchCode);
+ }
+
+ anchor = ip;
+
+ /* Test end of chunk */
+ if (ip > mflimit) break;
+
+ /* Fill table */
+ LZ4_putPosition(ip-2, cctx->hashTable, tableType, base);
+
+ /* Test next position */
+ match = LZ4_getPosition(ip, cctx->hashTable, tableType, base);
+ if (dict==usingExtDict) {
+ if (match < (const BYTE*)source) {
+ refDelta = dictDelta;
+ lowLimit = dictionary;
+ } else {
+ refDelta = 0;
+ lowLimit = (const BYTE*)source;
+ } }
+ LZ4_putPosition(ip, cctx->hashTable, tableType, base);
+ if ( ((dictIssue==dictSmall) ? (match>=lowRefLimit) : 1)
+ && (match+MAX_DISTANCE>=ip)
+ && (LZ4_read32(match+refDelta)==LZ4_read32(ip)) )
+ { token=op++; *token=0; goto _next_match; }
+
+ /* Prepare next loop */
+ forwardH = LZ4_hashPosition(++ip, tableType);
+ }
+
+_last_literals:
+ /* Encode Last Literals */
+ { size_t const lastRun = (size_t)(iend - anchor);
+ if ( (outputLimited) && /* Check output buffer overflow */
+ ((op - (BYTE*)dest) + lastRun + 1 + ((lastRun+255-RUN_MASK)/255) > (U32)maxOutputSize) )
+ return 0;
+ if (lastRun >= RUN_MASK) {
+ size_t accumulator = lastRun - RUN_MASK;
+ *op++ = RUN_MASK << ML_BITS;
+ for(; accumulator >= 255 ; accumulator-=255) *op++ = 255;
+ *op++ = (BYTE) accumulator;
+ } else {
+ *op++ = (BYTE)(lastRun<<ML_BITS);
+ }
+ ZT_FAST_MEMCPY(op, anchor, lastRun);
+ op += lastRun;
+ }
+
+ /* End */
+ return (int) (((char*)op)-dest);
+}
+
+static inline int LZ4_compress_fast_extState(void* state, const char* source, char* dest, int inputSize, int maxOutputSize, int acceleration)
+{
+ LZ4_stream_t_internal* ctx = &((LZ4_stream_t*)state)->internal_donotuse;
+ LZ4_resetStream((LZ4_stream_t*)state);
+ //if (acceleration < 1) acceleration = ACCELERATION_DEFAULT;
+
+ if (maxOutputSize >= LZ4_compressBound(inputSize)) {
+ if (inputSize < LZ4_64Klimit)
+ return LZ4_compress_generic(ctx, source, dest, inputSize, 0, notLimited, byU16, noDict, noDictIssue, acceleration);
+ else
+ return LZ4_compress_generic(ctx, source, dest, inputSize, 0, notLimited, (sizeof(void*)==8) ? byU32 : byPtr, noDict, noDictIssue, acceleration);
+ } else {
+ if (inputSize < LZ4_64Klimit)
+ return LZ4_compress_generic(ctx, source, dest, inputSize, maxOutputSize, limitedOutput, byU16, noDict, noDictIssue, acceleration);
+ else
+ return LZ4_compress_generic(ctx, source, dest, inputSize, maxOutputSize, limitedOutput, (sizeof(void*)==8) ? byU32 : byPtr, noDict, noDictIssue, acceleration);
+ }
+}
+
+static inline int LZ4_compress_fast(const char* source, char* dest, int inputSize, int maxOutputSize, int acceleration)
+{
+#if (HEAPMODE)
+ void* ctxPtr = ALLOCATOR(1, sizeof(LZ4_stream_t)); /* malloc-calloc always properly aligned */
+#else
+ LZ4_stream_t ctx;
+ void* const ctxPtr = &ctx;
+#endif
+
+ int const result = LZ4_compress_fast_extState(ctxPtr, source, dest, inputSize, maxOutputSize, acceleration);
+
+#if (HEAPMODE)
+ FREEMEM(ctxPtr);
+#endif
+ return result;
+}
+
+static inline void LZ4_resetStream (LZ4_stream_t* LZ4_stream)
+{
+ MEM_INIT(LZ4_stream, 0, sizeof(LZ4_stream_t));
+}
+
+FORCE_INLINE int LZ4_decompress_generic(
+ const char* const source,
+ char* const dest,
+ int inputSize,
+ int outputSize, /* If endOnInput==endOnInputSize, this value is the max size of Output Buffer. */
+
+ int endOnInput, /* endOnOutputSize, endOnInputSize */
+ int partialDecoding, /* full, partial */
+ int targetOutputSize, /* only used if partialDecoding==partial */
+ int dict, /* noDict, withPrefix64k, usingExtDict */
+ const BYTE* const lowPrefix, /* == dest when no prefix */
+ const BYTE* const dictStart, /* only if dict==usingExtDict */
+ const size_t dictSize /* note : = 0 if noDict */
+ )
+{
+ /* Local Variables */
+ const BYTE* ip = (const BYTE*) source;
+ const BYTE* const iend = ip + inputSize;
+
+ BYTE* op = (BYTE*) dest;
+ BYTE* const oend = op + outputSize;
+ BYTE* cpy;
+ BYTE* oexit = op + targetOutputSize;
+ const BYTE* const lowLimit = lowPrefix - dictSize;
+
+ const BYTE* const dictEnd = (const BYTE*)dictStart + dictSize;
+ const unsigned dec32table[] = {0, 1, 2, 1, 4, 4, 4, 4};
+ const int dec64table[] = {0, 0, 0, -1, 0, 1, 2, 3};
+
+ const int safeDecode = (endOnInput==endOnInputSize);
+ const int checkOffset = ((safeDecode) && (dictSize < (int)(64 KB)));
+
+
+ /* Special cases */
+ if ((partialDecoding) && (oexit > oend-MFLIMIT)) oexit = oend-MFLIMIT; /* targetOutputSize too high => decode everything */
+ if ((endOnInput) && (unlikely(outputSize==0))) return ((inputSize==1) && (*ip==0)) ? 0 : -1; /* Empty output buffer */
+ if ((!endOnInput) && (unlikely(outputSize==0))) return (*ip==0?1:-1);
+
+ /* Main Loop : decode sequences */
+ while (1) {
+ size_t length;
+ const BYTE* match;
+ size_t offset;
+
+ /* get literal length */
+ unsigned const token = *ip++;
+ if ((length=(token>>ML_BITS)) == RUN_MASK) {
+ unsigned s;
+ do {
+ s = *ip++;
+ length += s;
+ } while ( likely(endOnInput ? ip<iend-RUN_MASK : 1) & (s==255) );
+ if ((safeDecode) && unlikely((uptrval)(op)+length<(uptrval)(op))) goto _output_error; /* overflow detection */
+ if ((safeDecode) && unlikely((uptrval)(ip)+length<(uptrval)(ip))) goto _output_error; /* overflow detection */
+ }
+
+ /* copy literals */
+ cpy = op+length;
+ if ( ((endOnInput) && ((cpy>(partialDecoding?oexit:oend-MFLIMIT)) || (ip+length>iend-(2+1+LASTLITERALS))) )
+ || ((!endOnInput) && (cpy>oend-WILDCOPYLENGTH)) )
+ {
+ if (partialDecoding) {
+ if (cpy > oend) goto _output_error; /* Error : write attempt beyond end of output buffer */
+ if ((endOnInput) && (ip+length > iend)) goto _output_error; /* Error : read attempt beyond end of input buffer */
+ } else {
+ if ((!endOnInput) && (cpy != oend)) goto _output_error; /* Error : block decoding must stop exactly there */
+ if ((endOnInput) && ((ip+length != iend) || (cpy > oend))) goto _output_error; /* Error : input must be consumed */
+ }
+ ZT_FAST_MEMCPY(op, ip, length);
+ ip += length;
+ op += length;
+ break; /* Necessarily EOF, due to parsing restrictions */
+ }
+ LZ4_wildCopy(op, ip, cpy);
+ ip += length; op = cpy;
+
+ /* get offset */
+ offset = LZ4_readLE16(ip); ip+=2;
+ match = op - offset;
+ if ((checkOffset) && (unlikely(match < lowLimit))) goto _output_error; /* Error : offset outside buffers */
+ LZ4_write32(op, (U32)offset); /* costs ~1%; silence an msan warning when offset==0 */
+
+ /* get matchlength */
+ length = token & ML_MASK;
+ if (length == ML_MASK) {
+ unsigned s;
+ do {
+ s = *ip++;
+ if ((endOnInput) && (ip > iend-LASTLITERALS)) goto _output_error;
+ length += s;
+ } while (s==255);
+ if ((safeDecode) && unlikely((uptrval)(op)+length<(uptrval)op)) goto _output_error; /* overflow detection */
+ }
+ length += MINMATCH;
+
+ /* check external dictionary */
+ if ((dict==usingExtDict) && (match < lowPrefix)) {
+ if (unlikely(op+length > oend-LASTLITERALS)) goto _output_error; /* doesn't respect parsing restriction */
+
+ if (length <= (size_t)(lowPrefix-match)) {
+ /* match can be copied as a single segment from external dictionary */
+ memmove(op, dictEnd - (lowPrefix-match), length);
+ op += length;
+ } else {
+ /* match encompass external dictionary and current block */
+ size_t const copySize = (size_t)(lowPrefix-match);
+ size_t const restSize = length - copySize;
+ ZT_FAST_MEMCPY(op, dictEnd - copySize, copySize);
+ op += copySize;
+ if (restSize > (size_t)(op-lowPrefix)) { /* overlap copy */
+ BYTE* const endOfMatch = op + restSize;
+ const BYTE* copyFrom = lowPrefix;
+ while (op < endOfMatch) *op++ = *copyFrom++;
+ } else {
+ ZT_FAST_MEMCPY(op, lowPrefix, restSize);
+ op += restSize;
+ } }
+ continue;
+ }
+
+ /* copy match within block */
+ cpy = op + length;
+ if (unlikely(offset<8)) {
+ const int dec64 = dec64table[offset];
+ op[0] = match[0];
+ op[1] = match[1];
+ op[2] = match[2];
+ op[3] = match[3];
+ match += dec32table[offset];
+ ZT_FAST_MEMCPY(op+4, match, 4);
+ match -= dec64;
+ } else { LZ4_copy8(op, match); match+=8; }
+ op += 8;
+
+ if (unlikely(cpy>oend-12)) {
+ BYTE* const oCopyLimit = oend-(WILDCOPYLENGTH-1);
+ if (cpy > oend-LASTLITERALS) goto _output_error; /* Error : last LASTLITERALS bytes must be literals (uncompressed) */
+ if (op < oCopyLimit) {
+ LZ4_wildCopy(op, match, oCopyLimit);
+ match += oCopyLimit - op;
+ op = oCopyLimit;
+ }
+ while (op<cpy) *op++ = *match++;
+ } else {
+ LZ4_copy8(op, match);
+ if (length>16) LZ4_wildCopy(op+8, match+8, cpy);
+ }
+ op=cpy; /* correction */
+ }
+
+ /* end of decoding */
+ if (endOnInput)
+ return (int) (((char*)op)-dest); /* Nb of output bytes decoded */
+ else
+ return (int) (((const char*)ip)-source); /* Nb of input bytes read */
+
+ /* Overflow error detected */
+_output_error:
+ return (int) (-(((const char*)ip)-source))-1;
+}
+
+static inline int LZ4_decompress_safe(const char* source, char* dest, int compressedSize, int maxDecompressedSize)
+{
+ return LZ4_decompress_generic(source, dest, compressedSize, maxDecompressedSize, endOnInputSize, full, 0, noDict, (BYTE*)dest, NULL, 0);
+}
+
+} // anonymous namespace
+
+/************************************************************************** */
+/************************************************************************** */
+
+const unsigned char Packet::ZERO_KEY[32] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 };
void Packet::armor(const void *key,bool encryptPayload)
{
- unsigned char mangledKey[32];
- unsigned char macKey[32];
- unsigned char mac[16];
- const unsigned int payloadLen = size() - ZT_PACKET_IDX_VERB;
- unsigned char *const payload = field(ZT_PACKET_IDX_VERB,payloadLen);
+ uint8_t mangledKey[32];
+ uint8_t *const data = reinterpret_cast<uint8_t *>(unsafeData());
// Set flag now, since it affects key mangle function
setCipher(encryptPayload ? ZT_PROTO_CIPHER_SUITE__C25519_POLY1305_SALSA2012 : ZT_PROTO_CIPHER_SUITE__C25519_POLY1305_NONE);
_salsa20MangleKey((const unsigned char *)key,mangledKey);
- Salsa20 s20(mangledKey,256,field(ZT_PACKET_IDX_IV,8)/*,ZT_PROTO_SALSA20_ROUNDS*/);
-
- // MAC key is always the first 32 bytes of the Salsa20 key stream
- // This is the same construction DJB's NaCl library uses
- s20.encrypt12(ZERO_KEY,macKey,sizeof(macKey));
- if (encryptPayload)
- s20.encrypt12(payload,payload,payloadLen);
-
- Poly1305::compute(mac,payload,payloadLen,macKey);
- memcpy(field(ZT_PACKET_IDX_MAC,8),mac,8);
+ if (ZT_HAS_FAST_CRYPTO()) {
+ const unsigned int encryptLen = (encryptPayload) ? (size() - ZT_PACKET_IDX_VERB) : 0;
+ uint64_t keyStream[(ZT_PROTO_MAX_PACKET_LENGTH + 64 + 8) / 8];
+ ZT_FAST_SINGLE_PASS_SALSA2012(keyStream,encryptLen + 64,(data + ZT_PACKET_IDX_IV),mangledKey);
+ Salsa20::memxor(data + ZT_PACKET_IDX_VERB,reinterpret_cast<const uint8_t *>(keyStream + 8),encryptLen);
+ uint64_t mac[2];
+ Poly1305::compute(mac,data + ZT_PACKET_IDX_VERB,size() - ZT_PACKET_IDX_VERB,keyStream);
+#ifdef ZT_NO_TYPE_PUNNING
+ memcpy(data + ZT_PACKET_IDX_MAC,mac,8);
+#else
+ (*reinterpret_cast<uint64_t *>(data + ZT_PACKET_IDX_MAC)) = mac[0];
+#endif
+ } else {
+ Salsa20 s20(mangledKey,data + ZT_PACKET_IDX_IV);
+ uint64_t macKey[4];
+ s20.crypt12(ZERO_KEY,macKey,sizeof(macKey));
+ uint8_t *const payload = data + ZT_PACKET_IDX_VERB;
+ const unsigned int payloadLen = size() - ZT_PACKET_IDX_VERB;
+ if (encryptPayload)
+ s20.crypt12(payload,payload,payloadLen);
+ uint64_t mac[2];
+ Poly1305::compute(mac,payload,payloadLen,macKey);
+ ZT_FAST_MEMCPY(data + ZT_PACKET_IDX_MAC,mac,8);
+ }
}
bool Packet::dearmor(const void *key)
{
- unsigned char mangledKey[32];
- unsigned char macKey[32];
- unsigned char mac[16];
+ uint8_t mangledKey[32];
+ uint8_t *const data = reinterpret_cast<uint8_t *>(unsafeData());
const unsigned int payloadLen = size() - ZT_PACKET_IDX_VERB;
- unsigned char *const payload = field(ZT_PACKET_IDX_VERB,payloadLen);
- unsigned int cs = cipher();
+ unsigned char *const payload = data + ZT_PACKET_IDX_VERB;
+ const unsigned int cs = cipher();
if ((cs == ZT_PROTO_CIPHER_SUITE__C25519_POLY1305_NONE)||(cs == ZT_PROTO_CIPHER_SUITE__C25519_POLY1305_SALSA2012)) {
_salsa20MangleKey((const unsigned char *)key,mangledKey);
- Salsa20 s20(mangledKey,256,field(ZT_PACKET_IDX_IV,8)/*,ZT_PROTO_SALSA20_ROUNDS*/);
-
- s20.encrypt12(ZERO_KEY,macKey,sizeof(macKey));
- Poly1305::compute(mac,payload,payloadLen,macKey);
- if (!Utils::secureEq(mac,field(ZT_PACKET_IDX_MAC,8),8))
- return false;
-
- if (cs == ZT_PROTO_CIPHER_SUITE__C25519_POLY1305_SALSA2012)
- s20.decrypt12(payload,payload,payloadLen);
+ if (ZT_HAS_FAST_CRYPTO()) {
+ uint64_t keyStream[(ZT_PROTO_MAX_PACKET_LENGTH + 64 + 8) / 8];
+ ZT_FAST_SINGLE_PASS_SALSA2012(keyStream,((cs == ZT_PROTO_CIPHER_SUITE__C25519_POLY1305_SALSA2012) ? (payloadLen + 64) : 64),(data + ZT_PACKET_IDX_IV),mangledKey);
+ uint64_t mac[2];
+ Poly1305::compute(mac,payload,payloadLen,keyStream);
+#ifdef ZT_NO_TYPE_PUNNING
+ if (!Utils::secureEq(mac,data + ZT_PACKET_IDX_MAC,8))
+ return false;
+#else
+ if ((*reinterpret_cast<const uint64_t *>(data + ZT_PACKET_IDX_MAC)) != mac[0]) // also secure, constant time
+ return false;
+#endif
+ if (cs == ZT_PROTO_CIPHER_SUITE__C25519_POLY1305_SALSA2012)
+ Salsa20::memxor(data + ZT_PACKET_IDX_VERB,reinterpret_cast<const uint8_t *>(keyStream + 8),payloadLen);
+ } else {
+ Salsa20 s20(mangledKey,data + ZT_PACKET_IDX_IV);
+ uint64_t macKey[4];
+ s20.crypt12(ZERO_KEY,macKey,sizeof(macKey));
+ uint64_t mac[2];
+ Poly1305::compute(mac,payload,payloadLen,macKey);
+#ifdef ZT_NO_TYPE_PUNNING
+ if (!Utils::secureEq(mac,data + ZT_PACKET_IDX_MAC,8))
+ return false;
+#else
+ if ((*reinterpret_cast<const uint64_t *>(data + ZT_PACKET_IDX_MAC)) != mac[0]) // also secure, constant time
+ return false;
+#endif
+ if (cs == ZT_PROTO_CIPHER_SUITE__C25519_POLY1305_SALSA2012)
+ s20.crypt12(payload,payload,payloadLen);
+ }
return true;
- } else return false; // unrecognized cipher suite
+ } else {
+ return false; // unrecognized cipher suite
+ }
+}
+
+void Packet::cryptField(const void *key,unsigned int start,unsigned int len)
+{
+ uint8_t *const data = reinterpret_cast<uint8_t *>(unsafeData());
+ uint8_t iv[8];
+ for(int i=0;i<8;++i) iv[i] = data[i];
+ iv[7] &= 0xf8; // mask off least significant 3 bits of packet ID / IV since this is unset when this function gets called
+ Salsa20 s20(key,iv);
+ s20.crypt12(data + start,data + start,len);
}
bool Packet::compress()
{
- unsigned char buf[ZT_PROTO_MAX_PACKET_LENGTH * 2];
- if ((!compressed())&&(size() > (ZT_PACKET_IDX_PAYLOAD + 32))) {
+ char *const data = reinterpret_cast<char *>(unsafeData());
+ char buf[ZT_PROTO_MAX_PACKET_LENGTH * 2];
+
+ if ((!compressed())&&(size() > (ZT_PACKET_IDX_PAYLOAD + 64))) { // don't bother compressing tiny packets
int pl = (int)(size() - ZT_PACKET_IDX_PAYLOAD);
- int cl = LZ4_compress((const char *)field(ZT_PACKET_IDX_PAYLOAD,(unsigned int)pl),(char *)buf,pl);
+ int cl = LZ4_compress_fast(data + ZT_PACKET_IDX_PAYLOAD,buf,pl,ZT_PROTO_MAX_PACKET_LENGTH * 2,2);
if ((cl > 0)&&(cl < pl)) {
- (*this)[ZT_PACKET_IDX_VERB] |= (char)ZT_PROTO_VERB_FLAG_COMPRESSED;
+ data[ZT_PACKET_IDX_VERB] |= (char)ZT_PROTO_VERB_FLAG_COMPRESSED;
setSize((unsigned int)cl + ZT_PACKET_IDX_PAYLOAD);
- memcpy(field(ZT_PACKET_IDX_PAYLOAD,(unsigned int)cl),buf,cl);
+ ZT_FAST_MEMCPY(data + ZT_PACKET_IDX_PAYLOAD,buf,cl);
return true;
}
}
- (*this)[ZT_PACKET_IDX_VERB] &= (char)(~ZT_PROTO_VERB_FLAG_COMPRESSED);
+ data[ZT_PACKET_IDX_VERB] &= (char)(~ZT_PROTO_VERB_FLAG_COMPRESSED);
+
return false;
}
bool Packet::uncompress()
{
- unsigned char buf[ZT_PROTO_MAX_PACKET_LENGTH];
+ char *const data = reinterpret_cast<char *>(unsafeData());
+ char buf[ZT_PROTO_MAX_PACKET_LENGTH];
+
if ((compressed())&&(size() >= ZT_PROTO_MIN_PACKET_LENGTH)) {
if (size() > ZT_PACKET_IDX_PAYLOAD) {
unsigned int compLen = size() - ZT_PACKET_IDX_PAYLOAD;
- int ucl = LZ4_decompress_safe((const char *)field(ZT_PACKET_IDX_PAYLOAD,compLen),(char *)buf,compLen,sizeof(buf));
+ int ucl = LZ4_decompress_safe((const char *)data + ZT_PACKET_IDX_PAYLOAD,buf,compLen,sizeof(buf));
if ((ucl > 0)&&(ucl <= (int)(capacity() - ZT_PACKET_IDX_PAYLOAD))) {
setSize((unsigned int)ucl + ZT_PACKET_IDX_PAYLOAD);
- memcpy(field(ZT_PACKET_IDX_PAYLOAD,(unsigned int)ucl),buf,ucl);
- } else return false;
+ ZT_FAST_MEMCPY(data + ZT_PACKET_IDX_PAYLOAD,buf,ucl);
+ } else {
+ return false;
+ }
}
- (*this)[ZT_PACKET_IDX_VERB] &= (char)(~ZT_PROTO_VERB_FLAG_COMPRESSED);
+ data[ZT_PACKET_IDX_VERB] &= (char)(~ZT_PROTO_VERB_FLAG_COMPRESSED);
}
+
return true;
}
diff --git a/node/Packet.hpp b/node/Packet.hpp
index 3d95b0ba..27da6fb5 100644
--- a/node/Packet.hpp
+++ b/node/Packet.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_N_PACKET_HPP
@@ -34,12 +42,6 @@
#include "Utils.hpp"
#include "Buffer.hpp"
-#ifdef ZT_USE_SYSTEM_LZ4
-#include <lz4.h>
-#else
-#include "../ext/lz4/lz4.h"
-#endif
-
/**
* Protocol version -- incremented only for major changes
*
@@ -51,19 +53,23 @@
* + Yet another multicast redesign
* + New crypto completely changes key agreement cipher
* 4 - 0.6.0 ... 1.0.6
- * + New identity format based on hashcash design
+ * + BREAKING CHANGE: New identity format based on hashcash design
* 5 - 1.1.0 ... 1.1.5
- * + Supports circuit test, proof of work, and echo
+ * + Supports echo
* + Supports in-band world (root server definition) updates
* + Clustering! (Though this will work with protocol v4 clients.)
* + Otherwise backward compatible with protocol v4
* 6 - 1.1.5 ... 1.1.10
- * + Deprecate old dictionary-based network config format
- * + Introduce new binary serialized network config and meta-data
- * 7 - 1.1.10 -- CURRENT
+ * + Network configuration format revisions including binary values
+ * 7 - 1.1.10 ... 1.1.17
* + Introduce trusted paths for local SDN use
+ * 8 - 1.1.17 ... 1.2.0
+ * + Multipart network configurations for large network configs
+ * + Tags and Capabilities
+ * + Inline push of CertificateOfMembership deprecated
+ * 9 - 1.2.0 ... CURRENT
*/
-#define ZT_PROTO_VERSION 7
+#define ZT_PROTO_VERSION 9
/**
* Minimum supported protocol version
@@ -217,12 +223,8 @@
/**
* Packet buffer size (can be changed)
- *
- * The current value is big enough for ZT_MAX_PACKET_FRAGMENTS, the pragmatic
- * packet fragment limit, times the default UDP MTU. Most packets won't be
- * this big.
*/
-#define ZT_PROTO_MAX_PACKET_LENGTH (ZT_MAX_PACKET_FRAGMENTS * ZT_UDP_DEFAULT_PAYLOAD_MTU)
+#define ZT_PROTO_MAX_PACKET_LENGTH (ZT_MAX_PACKET_FRAGMENTS * ZT_DEFAULT_PHYSMTU)
/**
* Minimum viable packet length (a.k.a. header length)
@@ -303,6 +305,7 @@
#define ZT_PROTO_VERB_MULTICAST_GATHER_IDX_MAC (ZT_PROTO_VERB_MULTICAST_GATHER_IDX_FLAGS + 1)
#define ZT_PROTO_VERB_MULTICAST_GATHER_IDX_ADI (ZT_PROTO_VERB_MULTICAST_GATHER_IDX_MAC + 6)
#define ZT_PROTO_VERB_MULTICAST_GATHER_IDX_GATHER_LIMIT (ZT_PROTO_VERB_MULTICAST_GATHER_IDX_ADI + 4)
+#define ZT_PROTO_VERB_MULTICAST_GATHER_IDX_COM (ZT_PROTO_VERB_MULTICAST_GATHER_IDX_GATHER_LIMIT + 4)
// Note: COM, GATHER_LIMIT, and SOURCE_MAC are optional, and so are specified without size
#define ZT_PROTO_VERB_MULTICAST_FRAME_IDX_NETWORK_ID (ZT_PACKET_IDX_PAYLOAD)
@@ -346,7 +349,7 @@ namespace ZeroTier {
* ZeroTier packet
*
* Packet format:
- * <[8] 64-bit random packet ID and crypto initialization vector>
+ * <[8] 64-bit packet ID / crypto IV / packet counter>
* <[5] destination ZT address>
* <[5] source ZT address>
* <[1] flags/cipher/hops>
@@ -357,6 +360,14 @@ namespace ZeroTier {
*
* Packets smaller than 28 bytes are invalid and silently discarded.
*
+ * The 64-bit packet ID is a strongly random value used as a crypto IV.
+ * Its least significant 3 bits are also used as a monotonically increasing
+ * (and looping) counter for sending packets to a particular recipient. This
+ * can be used for link quality monitoring and reporting and has no crypto
+ * impact as it does not increase the likelihood of an IV collision. (The
+ * crypto we use is not sensitive to the nature of the IV, only that it does
+ * not repeat.)
+ *
* The flags/cipher/hops bit field is: FFCCCHHH where C is a 3-bit cipher
* selection allowing up to 7 cipher suites, F is outside-envelope flags,
* and H is hop count.
@@ -407,8 +418,7 @@ public:
}
template<unsigned int C2>
- Fragment(const Buffer<C2> &b)
- throw(std::out_of_range) :
+ Fragment(const Buffer<C2> &b) :
Buffer<ZT_PROTO_MAX_PACKET_LENGTH>(b)
{
}
@@ -426,10 +436,8 @@ public:
* @param fragLen Length of fragment in bytes
* @param fragNo Which fragment (>= 1, since 0 is Packet with end chopped off)
* @param fragTotal Total number of fragments (including 0)
- * @throws std::out_of_range Packet size would exceed buffer
*/
Fragment(const Packet &p,unsigned int fragStart,unsigned int fragLen,unsigned int fragNo,unsigned int fragTotal)
- throw(std::out_of_range)
{
init(p,fragStart,fragLen,fragNo,fragTotal);
}
@@ -442,13 +450,11 @@ public:
* @param fragLen Length of fragment in bytes
* @param fragNo Which fragment (>= 1, since 0 is Packet with end chopped off)
* @param fragTotal Total number of fragments (including 0)
- * @throws std::out_of_range Packet size would exceed buffer
*/
inline void init(const Packet &p,unsigned int fragStart,unsigned int fragLen,unsigned int fragNo,unsigned int fragTotal)
- throw(std::out_of_range)
{
if ((fragStart + fragLen) > p.size())
- throw std::out_of_range("Packet::Fragment: tried to construct fragment of packet past its length");
+ throw ZT_EXCEPTION_OUT_OF_BOUNDS;
setSize(fragLen + ZT_PROTO_MIN_FRAGMENT_LENGTH);
// NOTE: this copies both the IV/packet ID and the destination address.
@@ -523,50 +529,56 @@ public:
/**
* No operation (ignored, no reply)
*/
- VERB_NOP = 0,
+ VERB_NOP = 0x00,
/**
- * Announcement of a node's existence:
+ * Announcement of a node's existence and vitals:
* <[1] protocol version>
* <[1] software major version>
* <[1] software minor version>
* <[2] software revision>
- * <[8] timestamp (ms since epoch)>
+ * <[8] timestamp for determining latency>
* <[...] binary serialized identity (see Identity)>
- * <[1] destination address type>
- * [<[...] destination address>]
- * <[8] 64-bit world ID of current world>
- * <[8] 64-bit timestamp of current world>
- *
- * This is the only message that ever must be sent in the clear, since it
- * is used to push an identity to a new peer.
+ * <[...] physical destination address of packet>
+ * <[8] 64-bit world ID of current planet>
+ * <[8] 64-bit timestamp of current planet>
+ * [... remainder if packet is encrypted using cryptField() ...]
+ * <[2] 16-bit number of moons>
+ * [<[1] 8-bit type ID of moon>]
+ * [<[8] 64-bit world ID of moon>]
+ * [<[8] 64-bit timestamp of moon>]
+ * [... additional moon type/ID/timestamp tuples ...]
+ *
+ * HELLO is sent in the clear as it is how peers share their identity
+ * public keys. A few additional fields are sent in the clear too, but
+ * these are things that are public info or are easy to determine. As
+ * of 1.2.0 we have added a few more fields, but since these could have
+ * the potential to be sensitive we introduced the encryption of the
+ * remainder of the packet. See cryptField(). Packet MAC is still
+ * performed of course, so authentication occurs as normal.
+ *
+ * Destination address is the actual wire address to which the packet
+ * was sent. See InetAddress::serialize() for format.
*
- * The destination address is the wire address to which this packet is
- * being sent, and in OK is *also* the destination address of the OK
- * packet. This can be used by the receiver to detect NAT, learn its real
- * external address if behind NAT, and detect changes to its external
- * address that require re-establishing connectivity.
+ * OK payload:
+ * <[8] HELLO timestamp field echo>
+ * <[1] protocol version>
+ * <[1] software major version>
+ * <[1] software minor version>
+ * <[2] software revision>
+ * <[...] physical destination address of packet>
+ * <[2] 16-bit length of world update(s) or 0 if none>
+ * [[...] updates to planets and/or moons]
*
- * Destination address types and formats (not all of these are used now):
- * 0x00 - None -- no destination address data present
- * 0x01 - Ethernet address -- format: <[6] Ethernet MAC>
- * 0x04 - 6-byte IPv4 UDP address/port -- format: <[4] IP>, <[2] port>
- * 0x06 - 18-byte IPv6 UDP address/port -- format: <[16] IP>, <[2] port>
+ * With the exception of the timestamp, the other fields pertain to the
+ * respondent who is sending OK and are not echoes.
*
- * OK payload:
- * <[8] timestamp (echoed from original HELLO)>
- * <[1] protocol version (of responder)>
- * <[1] software major version (of responder)>
- * <[1] software minor version (of responder)>
- * <[2] software revision (of responder)>
- * <[1] destination address type (for this OK, not copied from HELLO)>
- * [<[...] destination address>]
- * <[2] 16-bit length of world update or 0 if none>
- * [[...] world update]
+ * Note that OK is fully encrypted so no selective cryptField() of
+ * potentially sensitive fields is needed.
*
* ERROR has no payload.
*/
- VERB_HELLO = 1,
+ VERB_HELLO = 0x01,
/**
* Error response:
@@ -575,7 +587,7 @@ public:
* <[1] error code>
* <[...] error-dependent payload>
*/
- VERB_ERROR = 2,
+ VERB_ERROR = 0x02,
/**
* Success response:
@@ -583,50 +595,43 @@ public:
* <[8] in-re packet ID>
* <[...] request-specific payload>
*/
- VERB_OK = 3,
+ VERB_OK = 0x03,
/**
* Query an identity by address:
* <[5] address to look up>
+ * [<[...] additional addresses to look up>
*
* OK response payload:
* <[...] binary serialized identity>
+ * [<[...] additional binary serialized identities>]
*
* If querying a cluster, duplicate OK responses may occasionally occur.
- * These should be discarded.
+ * These must be tolerated, which is easy since they'll have info you
+ * already have.
*
- * If the address is not found, no response is generated. WHOIS requests
- * will time out much like ARP requests and similar do in L2.
+ * If the address is not found, no response is generated. The semantics
+ * of WHOIS is similar to ARP and NDP in that persistent retrying can
+ * be performed.
*/
- VERB_WHOIS = 4,
+ VERB_WHOIS = 0x04,
/**
- * Meet another node at a given protocol address:
+ * Relay-mediated NAT traversal or firewall punching initiation:
* <[1] flags (unused, currently 0)>
* <[5] ZeroTier address of peer that might be found at this address>
* <[2] 16-bit protocol address port>
* <[1] protocol address length (4 for IPv4, 16 for IPv6)>
* <[...] protocol address (network byte order)>
*
- * This is sent by a relaying node to initiate NAT traversal between two
- * peers that are communicating by way of indirect relay. The relay will
- * send this to both peers at the same time on a periodic basis, telling
- * each where it might find the other on the network.
+ * An upstream node can send this to inform both sides of a relay of
+ * information they might use to establish a direct connection.
*
* Upon receipt a peer sends HELLO to establish a direct link.
*
- * Nodes should implement rate control, limiting the rate at which they
- * respond to these packets to prevent their use in DDOS attacks. Nodes
- * may also ignore these messages if a peer is not known or is not being
- * actively communicated with.
- *
- * Unfortunately the physical address format in this message pre-dates
- * InetAddress's serialization format. :( ZeroTier is four years old and
- * yes we've accumulated a tiny bit of cruft here and there.
- *
* No OK or ERROR is generated.
*/
- VERB_RENDEZVOUS = 5,
+ VERB_RENDEZVOUS = 0x05,
/**
* ZT-to-ZT unicast ethernet frame (shortened EXT_FRAME):
@@ -642,31 +647,44 @@ public:
* ERROR may be generated if a membership certificate is needed for a
* closed network. Payload will be network ID.
*/
- VERB_FRAME = 6,
+ VERB_FRAME = 0x06,
/**
* Full Ethernet frame with MAC addressing and optional fields:
* <[8] 64-bit network ID>
* <[1] flags>
- * [<[...] certificate of network membership>]
* <[6] destination MAC or all zero for destination node>
* <[6] source MAC or all zero for node of origin>
* <[2] 16-bit ethertype>
* <[...] ethernet payload>
*
* Flags:
- * 0x01 - Certificate of network membership is attached
- *
- * An extended frame carries full MAC addressing, making them a
- * superset of VERB_FRAME. They're used for bridging or when we
- * want to attach a certificate since FRAME does not support that.
- *
- * Multicast frames may not be sent as EXT_FRAME.
- *
- * ERROR may be generated if a membership certificate is needed for a
- * closed network. Payload will be network ID.
+ * 0x01 - Certificate of network membership attached (DEPRECATED)
+ * 0x02 - Most significant bit of subtype (see below)
+ * 0x04 - Middle bit of subtype (see below)
+ * 0x08 - Least significant bit of subtype (see below)
+ * 0x10 - ACK requested in the form of OK(EXT_FRAME)
+ *
+ * Subtypes (0..7):
+ * 0x0 - Normal frame (bridging can be determined by checking MAC)
+ * 0x1 - TEEd outbound frame
+ * 0x2 - REDIRECTed outbound frame
+ * 0x3 - WATCHed outbound frame (TEE with ACK, ACK bit also set)
+ * 0x4 - TEEd inbound frame
+ * 0x5 - REDIRECTed inbound frame
+ * 0x6 - WATCHed inbound frame
+ * 0x7 - (reserved for future use)
+ *
+ * An extended frame carries full MAC addressing, making it a
+ * superset of VERB_FRAME. It is used for bridged traffic,
+ * redirected or observed traffic via rules, and can in theory
+ * be used for multicast though MULTICAST_FRAME exists for that
+ * purpose and has additional options and capabilities.
+ *
+ * OK payload (if ACK flag is set):
+ * <[8] 64-bit network ID>
*/
- VERB_EXT_FRAME = 7,
+ VERB_EXT_FRAME = 0x07,
/**
* ECHO request (a.k.a. ping):
@@ -676,7 +694,7 @@ public:
* is generated. Response to ECHO requests is optional and ECHO may be
* ignored if a node detects a possible flood.
*/
- VERB_ECHO = 8,
+ VERB_ECHO = 0x08,
/**
* Announce interest in multicast group(s):
@@ -690,77 +708,117 @@ public:
* controllers and root servers. In the current network, root servers
* will provide the service of final multicast cache.
*
- * It is recommended that NETWORK_MEMBERSHIP_CERTIFICATE pushes be sent
- * along with MULTICAST_LIKE when pushing LIKEs to peers that do not
- * share a network membership (such as root servers), since this can be
- * used to authenticate GATHER requests and limit responses to peers
- * authorized to talk on a network. (Should be an optional field here,
- * but saving one or two packets every five minutes is not worth an
- * ugly hack or protocol rev.)
+ * VERB_NETWORK_CREDENTIALS should be pushed along with this, especially
+ * if using upstream (e.g. root) nodes as multicast databases. This allows
+ * GATHERs to be authenticated.
*
* OK/ERROR are not generated.
*/
- VERB_MULTICAST_LIKE = 9,
+ VERB_MULTICAST_LIKE = 0x09,
/**
- * Network member certificate replication/push:
- * <[...] serialized certificate of membership>
- * [ ... additional certificates may follow ...]
- *
- * This is sent in response to ERROR_NEED_MEMBERSHIP_CERTIFICATE and may
- * be pushed at any other time to keep exchanged certificates up to date.
+ * Network credentials push:
+ * [<[...] one or more certificates of membership>]
+ * <[1] 0x00, null byte marking end of COM array>
+ * <[2] 16-bit number of capabilities>
+ * <[...] one or more serialized Capability>
+ * <[2] 16-bit number of tags>
+ * <[...] one or more serialized Tags>
+ * <[2] 16-bit number of revocations>
+ * <[...] one or more serialized Revocations>
+ * <[2] 16-bit number of certificates of ownership>
+ * <[...] one or more serialized CertificateOfOwnership>
+ *
+ * This can be sent by anyone at any time to push network credentials.
+ * These will of course only be accepted if they are properly signed.
+ * Credentials can be for any number of networks.
+ *
+ * The use of a zero byte to terminate the COM section is for legacy
+ * backward compatiblity. Newer fields are prefixed with a length.
*
* OK/ERROR are not generated.
*/
- VERB_NETWORK_MEMBERSHIP_CERTIFICATE = 10,
+ VERB_NETWORK_CREDENTIALS = 0x0a,
/**
* Network configuration request:
* <[8] 64-bit network ID>
* <[2] 16-bit length of request meta-data dictionary>
* <[...] string-serialized request meta-data>
- * [<[8] 64-bit revision of netconf we currently have>]
+ * <[8] 64-bit revision of netconf we currently have>
+ * <[8] 64-bit timestamp of netconf we currently have>
*
* This message requests network configuration from a node capable of
- * providing it. If the optional revision is included, a response is
- * only generated if there is a newer network configuration available.
+ * providing it.
*
- * OK response payload:
- * <[8] 64-bit network ID>
- * <[2] 16-bit length of network configuration dictionary>
- * <[...] network configuration dictionary>
+ * Respones to this are always whole configs intended for the recipient.
+ * For patches and other updates a NETWORK_CONFIG is sent instead.
*
- * OK returns a Dictionary (string serialized) containing the network's
- * configuration and IP address assignment information for the querying
- * node. It also contains a membership certificate that the querying
- * node can push to other peers to demonstrate its right to speak on
- * a given network.
+ * It would be valid and correct as of 1.2.0 to use NETWORK_CONFIG always,
+ * but OK(NTEWORK_CONFIG_REQUEST) should be sent for compatibility.
*
- * When a new network configuration is received, another config request
- * should be sent with the new netconf's revision. This confirms receipt
- * and also causes any subsequent changes to rapidly propagate as this
- * cycle will repeat until there are no changes. This is optional but
- * recommended behavior.
+ * OK response payload:
+ * <[8] 64-bit network ID>
+ * <[2] 16-bit length of network configuration dictionary chunk>
+ * <[...] network configuration dictionary (may be incomplete)>
+ * [ ... end of legacy single chunk response ... ]
+ * <[1] 8-bit flags>
+ * <[8] 64-bit config update ID (should never be 0)>
+ * <[4] 32-bit total length of assembled dictionary>
+ * <[4] 32-bit index of chunk>
+ * [ ... end signed portion ... ]
+ * <[1] 8-bit chunk signature type>
+ * <[2] 16-bit length of chunk signature>
+ * <[...] chunk signature>
+ *
+ * The chunk signature signs the entire payload of the OK response.
+ * Currently only one signature type is supported: ed25519 (1).
+ *
+ * Each config chunk is signed to prevent memory exhaustion or
+ * traffic crowding DOS attacks against config fragment assembly.
+ *
+ * If the packet is from the network controller it is permitted to end
+ * before the config update ID or other chunking related or signature
+ * fields. This is to support older controllers that don't include
+ * these fields and may be removed in the future.
*
* ERROR response payload:
* <[8] 64-bit network ID>
- *
- * UNSUPPORTED_OPERATION is returned if this service is not supported,
- * and OBJ_NOT_FOUND if the queried network ID was not found.
*/
- VERB_NETWORK_CONFIG_REQUEST = 11,
+ VERB_NETWORK_CONFIG_REQUEST = 0x0b,
/**
- * Network configuration refresh request:
- * <[...] array of 64-bit network IDs>
+ * Network configuration data push:
+ * <[8] 64-bit network ID>
+ * <[2] 16-bit length of network configuration dictionary chunk>
+ * <[...] network configuration dictionary (may be incomplete)>
+ * <[1] 8-bit flags>
+ * <[8] 64-bit config update ID (should never be 0)>
+ * <[4] 32-bit total length of assembled dictionary>
+ * <[4] 32-bit index of chunk>
+ * [ ... end signed portion ... ]
+ * <[1] 8-bit chunk signature type>
+ * <[2] 16-bit length of chunk signature>
+ * <[...] chunk signature>
+ *
+ * This is a direct push variant for network config updates. It otherwise
+ * carries the same payload as OK(NETWORK_CONFIG_REQUEST) and has the same
+ * semantics.
+ *
+ * The legacy mode missing the additional chunking fields is not supported
+ * here.
+ *
+ * Flags:
+ * 0x01 - Use fast propagation
*
- * This can be sent by the network controller to inform a node that it
- * should now make a NETWORK_CONFIG_REQUEST.
+ * An OK should be sent if the config is successfully received and
+ * accepted.
*
- * It does not generate an OK or ERROR message, and is treated only as
- * a hint to refresh now.
+ * OK payload:
+ * <[8] 64-bit network ID>
+ * <[8] 64-bit config update ID>
*/
- VERB_NETWORK_CONFIG_REFRESH = 12,
+ VERB_NETWORK_CONFIG = 0x0c,
/**
* Request endpoints for multicast distribution:
@@ -769,10 +827,10 @@ public:
* <[6] MAC address of multicast group being queried>
* <[4] 32-bit ADI for multicast group being queried>
* <[4] 32-bit requested max number of multicast peers>
- * [<[...] network certificate of membership>]
+ * [<[...] network certificate of membership>]
*
* Flags:
- * 0x01 - Network certificate of membership is attached
+ * 0x01 - COM is attached
*
* This message asks a peer for additional known endpoints that have
* LIKEd a given multicast group. It's sent when the sender wishes
@@ -782,6 +840,9 @@ public:
* More than one OK response can occur if the response is broken up across
* multiple packets or if querying a clustered node.
*
+ * The COM should be included so that upstream nodes that are not
+ * members of our network can validate our request.
+ *
* OK response payload:
* <[8] 64-bit network ID>
* <[6] MAC address of multicast group being queried>
@@ -793,13 +854,12 @@ public:
*
* ERROR is not generated; queries that return no response are dropped.
*/
- VERB_MULTICAST_GATHER = 13,
+ VERB_MULTICAST_GATHER = 0x0d,
/**
* Multicast frame:
* <[8] 64-bit network ID>
* <[1] flags>
- * [<[...] network certificate of membership>]
* [<[4] 32-bit implicit gather limit>]
* [<[6] source MAC>]
* <[6] destination MAC (multicast address)>
@@ -808,9 +868,10 @@ public:
* <[...] ethernet payload>
*
* Flags:
- * 0x01 - Network certificate of membership is attached
+ * 0x01 - Network certificate of membership attached (DEPRECATED)
* 0x02 - Implicit gather limit field is present
* 0x04 - Source MAC is specified -- otherwise it's computed from sender
+ * 0x08 - Please replicate (sent to multicast replicators)
*
* OK and ERROR responses are optional. OK may be generated if there are
* implicit gather results or if the recipient wants to send its own
@@ -823,11 +884,11 @@ public:
* <[6] MAC address of multicast group>
* <[4] 32-bit ADI for multicast group>
* <[1] flags>
- * [<[...] network certficate of membership>]
+ * [<[...] network certficate of membership (DEPRECATED)>]
* [<[...] implicit gather results if flag 0x01 is set>]
*
* OK flags (same bits as request flags):
- * 0x01 - OK includes certificate of network membership
+ * 0x01 - OK includes certificate of network membership (DEPRECATED)
* 0x02 - OK includes implicit gather results
*
* ERROR response payload:
@@ -835,7 +896,7 @@ public:
* <[6] multicast group MAC>
* <[4] 32-bit multicast group ADI>
*/
- VERB_MULTICAST_FRAME = 14,
+ VERB_MULTICAST_FRAME = 0x0e,
/**
* Push of potential endpoints for direct communication:
@@ -865,172 +926,42 @@ public:
* be used unless they are blacklisted explicitly or unless flag 0x01
* is set.
*
- * Only a subset of this functionality is currently implemented: basic
- * path pushing and learning. Blacklisting and trust are not fully
- * implemented yet (encryption is still always used).
- *
* OK and ERROR are not generated.
*/
- VERB_PUSH_DIRECT_PATHS = 16,
+ VERB_PUSH_DIRECT_PATHS = 0x10,
- /**
- * Source-routed circuit test message:
- * <[5] address of originator of circuit test>
- * <[2] 16-bit flags>
- * <[8] 64-bit timestamp>
- * <[8] 64-bit test ID (arbitrary, set by tester)>
- * <[2] 16-bit originator credential length (includes type)>
- * [[1] originator credential type (for authorizing test)]
- * [[...] originator credential]
- * <[2] 16-bit length of additional fields>
- * [[...] additional fields]
- * [ ... end of signed portion of request ... ]
- * <[2] 16-bit length of signature of request>
- * <[...] signature of request by originator>
- * <[2] 16-bit previous hop credential length (including type)>
- * [[1] previous hop credential type]
- * [[...] previous hop credential]
- * <[...] next hop(s) in path>
- *
- * Flags:
- * 0x01 - Report back to originator at middle hops
- * 0x02 - Report back to originator at last hop
- *
- * Originator credential types:
- * 0x01 - 64-bit network ID for which originator is controller
- *
- * Previous hop credential types:
- * 0x01 - Certificate of network membership
- *
- * Path record format:
- * <[1] 8-bit flags (unused, must be zero)>
- * <[1] 8-bit breadth (number of next hops)>
- * <[...] one or more ZeroTier addresses of next hops>
- *
- * The circuit test allows a device to send a message that will traverse
- * the network along a specified path, with each hop optionally reporting
- * back to the tester via VERB_CIRCUIT_TEST_REPORT.
- *
- * Each circuit test packet includes a digital signature by the originator
- * of the request, as well as a credential by which that originator claims
- * authorization to perform the test. Currently this signature is ed25519,
- * but in the future flags might be used to indicate an alternative
- * algorithm. For example, the originator might be a network controller.
- * In this case the test might be authorized if the recipient is a member
- * of a network controlled by it, and if the previous hop(s) are also
- * members. Each hop may include its certificate of network membership.
- *
- * Circuit test paths consist of a series of records. When a node receives
- * an authorized circuit test, it:
- *
- * (1) Reports back to circuit tester as flags indicate
- * (2) Reads and removes the next hop from the packet's path
- * (3) Sends the packet along to next hop(s), if any.
- *
- * It is perfectly legal for a path to contain the same hop more than
- * once. In fact, this can be a very useful test to determine if a hop
- * can be reached bidirectionally and if so what that connectivity looks
- * like.
- *
- * The breadth field in source-routed path records allows a hop to forward
- * to more than one recipient, allowing the tester to specify different
- * forms of graph traversal in a test.
- *
- * There is no hard limit to the number of hops in a test, but it is
- * practically limited by the maximum size of a (possibly fragmented)
- * ZeroTier packet.
- *
- * Support for circuit tests is optional. If they are not supported, the
- * node should respond with an UNSUPPORTED_OPERATION error. If a circuit
- * test request is not authorized, it may be ignored or reported as
- * an INVALID_REQUEST. No OK messages are generated, but TEST_REPORT
- * messages may be sent (see below).
- *
- * ERROR packet format:
- * <[8] 64-bit timestamp (echoed from original>
- * <[8] 64-bit test ID (echoed from original)>
- */
- VERB_CIRCUIT_TEST = 17,
+ // 0x11, 0x12 -- deprecated
/**
- * Circuit test hop report:
- * <[8] 64-bit timestamp (from original test)>
- * <[8] 64-bit test ID (from original test)>
- * <[8] 64-bit reserved field (set to 0, currently unused)>
- * <[1] 8-bit vendor ID (set to 0, currently unused)>
- * <[1] 8-bit reporter protocol version>
- * <[1] 8-bit reporter major version>
- * <[1] 8-bit reporter minor version>
- * <[2] 16-bit reporter revision>
- * <[2] 16-bit reporter OS/platform>
- * <[2] 16-bit reporter architecture>
- * <[2] 16-bit error code (set to 0, currently unused)>
- * <[8] 64-bit report flags (set to 0, currently unused)>
- * <[8] 64-bit source packet ID>
- * <[5] upstream ZeroTier address from which test was received>
- * <[1] 8-bit source packet hop count (ZeroTier hop count)>
- * <[...] local wire address on which packet was received>
- * <[...] remote wire address from which packet was received>
- * <[2] 16-bit length of additional fields>
- * <[...] additional fields>
- * <[1] 8-bit number of next hops (breadth)>
- * <[...] next hop information>
- *
- * Next hop information record format:
- * <[5] ZeroTier address of next hop>
- * <[...] current best direct path address, if any, 0 if none>
+ * A message with arbitrary user-definable content:
+ * <[8] 64-bit arbitrary message type ID>
+ * [<[...] message payload>]
*
- * Circuit test reports can be sent by hops in a circuit test to report
- * back results. They should include information about the sender as well
- * as about the paths to which next hops are being sent.
+ * This can be used to send arbitrary messages over VL1. It generates no
+ * OK or ERROR and has no special semantics outside of whatever the user
+ * (via the ZeroTier core API) chooses to give it.
*
- * If a test report is received and no circuit test was sent, it should be
- * ignored. This message generates no OK or ERROR response.
+ * Message type IDs less than or equal to 65535 are reserved for use by
+ * ZeroTier, Inc. itself. We recommend making up random ones for your own
+ * implementations.
*/
- VERB_CIRCUIT_TEST_REPORT = 18,
+ VERB_USER_MESSAGE = 0x14,
/**
- * Request proof of work:
- * <[1] 8-bit proof of work type>
- * <[1] 8-bit proof of work difficulty>
- * <[2] 16-bit length of proof of work challenge>
- * <[...] proof of work challenge>
- *
- * This requests that a peer perform a proof of work calucation. It can be
- * sent by highly trusted peers (e.g. root servers, network controllers)
- * under suspected denial of service conditions in an attempt to filter
- * out "non-serious" peers and remain responsive to those proving their
- * intent to actually communicate.
- *
- * If the peer obliges to perform the work, it does so and responds with
- * an OK containing the result. Otherwise it may ignore the message or
- * response with an ERROR_INVALID_REQUEST or ERROR_UNSUPPORTED_OPERATION.
- *
- * Proof of work type IDs:
- * 0x01 - Salsa20/12+SHA512 hashcash function
- *
- * Salsa20/12+SHA512 is based on the following composite hash function:
- *
- * (1) Compute SHA512(candidate)
- * (2) Use the first 256 bits of the result of #1 as a key to encrypt
- * 131072 zero bytes with Salsa20/12 (with a zero IV).
- * (3) Compute SHA512(the result of step #2)
- * (4) Accept this candiate if the first [difficulty] bits of the result
- * from step #3 are zero. Otherwise generate a new candidate and try
- * again.
- *
- * This is performed repeatedly on candidates generated by appending the
- * supplied challenge to an arbitrary nonce until a valid candidate
- * is found. This chosen prepended nonce is then returned as the result
- * in OK.
- *
- * OK payload:
- * <[2] 16-bit length of result>
- * <[...] computed proof of work>
- *
- * ERROR has no payload.
+ * A trace for remote debugging or diagnostics:
+ * <[...] null-terminated dictionary containing trace information>
+ * [<[...] additional null-terminated dictionaries>]
+ *
+ * This message contains a remote trace event. Remote trace events can
+ * be sent to observers configured at the network level for those that
+ * pertain directly to actiity on a network, or to global observers if
+ * locally configured.
+ *
+ * The instance ID is a random 64-bit value generated by each ZeroTier
+ * node on startup. This is helpful in identifying traces from different
+ * members of a cluster.
*/
- VERB_REQUEST_PROOF_OF_WORK = 19
+ VERB_REMOTE_TRACE = 0x15
};
/**
@@ -1039,40 +970,33 @@ public:
enum ErrorCode
{
/* No error, not actually used in transit */
- ERROR_NONE = 0,
+ ERROR_NONE = 0x00,
/* Invalid request */
- ERROR_INVALID_REQUEST = 1,
+ ERROR_INVALID_REQUEST = 0x01,
/* Bad/unsupported protocol version */
- ERROR_BAD_PROTOCOL_VERSION = 2,
+ ERROR_BAD_PROTOCOL_VERSION = 0x02,
/* Unknown object queried */
- ERROR_OBJ_NOT_FOUND = 3,
+ ERROR_OBJ_NOT_FOUND = 0x03,
/* HELLO pushed an identity whose address is already claimed */
- ERROR_IDENTITY_COLLISION = 4,
+ ERROR_IDENTITY_COLLISION = 0x04,
/* Verb or use case not supported/enabled by this node */
- ERROR_UNSUPPORTED_OPERATION = 5,
+ ERROR_UNSUPPORTED_OPERATION = 0x05,
- /* Message to private network rejected -- no unexpired certificate on file */
- ERROR_NEED_MEMBERSHIP_CERTIFICATE = 6,
+ /* Network membership certificate update needed */
+ ERROR_NEED_MEMBERSHIP_CERTIFICATE = 0x06,
/* Tried to join network, but you're not a member */
- ERROR_NETWORK_ACCESS_DENIED_ = 7, /* extra _ to avoid Windows name conflict */
+ ERROR_NETWORK_ACCESS_DENIED_ = 0x07, /* extra _ at end to avoid Windows name conflict */
/* Multicasts to this group are not wanted */
- ERROR_UNWANTED_MULTICAST = 8
+ ERROR_UNWANTED_MULTICAST = 0x08
};
-//#ifdef ZT_TRACE
- static const char *verbString(Verb v)
- throw();
- static const char *errorString(ErrorCode e)
- throw();
-//#endif
-
template<unsigned int C2>
Packet(const Buffer<C2> &b) :
Buffer<ZT_PROTO_MAX_PACKET_LENGTH>(b)
@@ -1268,6 +1192,12 @@ public:
/**
* Get this packet's unique ID (the IV field interpreted as uint64_t)
*
+ * Note that the least significant 3 bits of this ID will change when armor()
+ * is called to armor the packet for transport. This is because armor() will
+ * mask the last 3 bits against the send counter for QoS monitoring use prior
+ * to actually using the IV to encrypt and MAC the packet. Be aware of this
+ * when grabbing the packetId of a new packet prior to armor/send.
+ *
* @return Packet ID
*/
inline uint64_t packetId() const { return at<uint64_t>(ZT_PACKET_IDX_IV); }
@@ -1318,6 +1248,21 @@ public:
bool dearmor(const void *key);
/**
+ * Encrypt/decrypt a separately armored portion of a packet
+ *
+ * This is currently only used to mask portions of HELLO as an extra
+ * security precation since most of that message is sent in the clear.
+ *
+ * This must NEVER be used more than once in the same packet, as doing
+ * so will result in re-use of the same key stream.
+ *
+ * @param key 32-byte key
+ * @param start Start of encrypted portion
+ * @param len Length of encrypted portion
+ */
+ void cryptField(const void *key,unsigned int start,unsigned int len);
+
+ /**
* Attempt to compress payload if not already (must be unencrypted)
*
* This requires that the payload at least contain the verb byte already
diff --git a/node/Path.cpp b/node/Path.cpp
index 5692af66..b1b3dd06 100644
--- a/node/Path.cpp
+++ b/node/Path.cpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#include "Path.hpp"
@@ -22,10 +30,10 @@
namespace ZeroTier {
-bool Path::send(const RuntimeEnvironment *RR,const void *data,unsigned int len,uint64_t now)
+bool Path::send(const RuntimeEnvironment *RR,void *tPtr,const void *data,unsigned int len,int64_t now)
{
- if (RR->node->putPacket(_localAddress,address(),data,len)) {
- sent(now);
+ if (RR->node->putPacket(tPtr,_localSocket,_addr,data,len)) {
+ _lastOut = now;
return true;
}
return false;
diff --git a/node/Path.hpp b/node/Path.hpp
index ecf4be24..e12328ff 100644
--- a/node/Path.hpp
+++ b/node/Path.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_PATH_HPP
@@ -21,33 +29,16 @@
#include <stdint.h>
#include <string.h>
+#include <stdlib.h>
#include <stdexcept>
#include <algorithm>
#include "Constants.hpp"
#include "InetAddress.hpp"
-
-// Note: if you change these flags check the logic below. Some of it depends
-// on these bits being what they are.
-
-/**
- * Flag indicating that this path is suboptimal
- *
- * Clusters set this flag on remote paths if GeoIP or other routing decisions
- * indicate that a peer should be handed off to another cluster member.
- */
-#define ZT_PATH_FLAG_CLUSTER_SUBOPTIMAL 0x0001
-
-/**
- * Flag indicating that this path is optimal
- *
- * Peers set this flag on paths that are pushed by a cluster and indicated as
- * optimal. A second flag is needed since we want to prioritize cluster optimal
- * paths and de-prioritize sub-optimal paths and for new paths we don't know
- * which one they are. So we want a trinary state: optimal, suboptimal, unknown.
- */
-#define ZT_PATH_FLAG_CLUSTER_OPTIMAL 0x0002
+#include "SharedPtr.hpp"
+#include "AtomicCounter.hpp"
+#include "Utils.hpp"
/**
* Maximum return value of preferenceRank()
@@ -59,210 +50,142 @@ namespace ZeroTier {
class RuntimeEnvironment;
/**
- * Base class for paths
- *
- * The base Path class is an immutable value.
+ * A path across the physical network
*/
class Path
{
+ friend class SharedPtr<Path>;
+
public:
+ /**
+ * Efficient unique key for paths in a Hashtable
+ */
+ class HashKey
+ {
+ public:
+ HashKey() {}
+
+ HashKey(const int64_t l,const InetAddress &r)
+ {
+ if (r.ss_family == AF_INET) {
+ _k[0] = (uint64_t)reinterpret_cast<const struct sockaddr_in *>(&r)->sin_addr.s_addr;
+ _k[1] = (uint64_t)reinterpret_cast<const struct sockaddr_in *>(&r)->sin_port;
+ _k[2] = (uint64_t)l;
+ } else if (r.ss_family == AF_INET6) {
+ ZT_FAST_MEMCPY(_k,reinterpret_cast<const struct sockaddr_in6 *>(&r)->sin6_addr.s6_addr,16);
+ _k[2] = ((uint64_t)reinterpret_cast<const struct sockaddr_in6 *>(&r)->sin6_port << 32) ^ (uint64_t)l;
+ } else {
+ ZT_FAST_MEMCPY(_k,&r,std::min(sizeof(_k),sizeof(InetAddress)));
+ _k[2] += (uint64_t)l;
+ }
+ }
+
+ inline unsigned long hashCode() const { return (unsigned long)(_k[0] + _k[1] + _k[2]); }
+
+ inline bool operator==(const HashKey &k) const { return ( (_k[0] == k._k[0]) && (_k[1] == k._k[1]) && (_k[2] == k._k[2]) ); }
+ inline bool operator!=(const HashKey &k) const { return (!(*this == k)); }
+
+ private:
+ uint64_t _k[3];
+ };
+
Path() :
- _lastSend(0),
- _lastPing(0),
- _lastKeepalive(0),
- _lastReceived(0),
+ _lastOut(0),
+ _lastIn(0),
+ _lastTrustEstablishedPacketReceived(0),
+ _localSocket(-1),
+ _latency(0xffff),
_addr(),
- _localAddress(),
- _flags(0),
_ipScope(InetAddress::IP_SCOPE_NONE)
{
}
- Path(const InetAddress &localAddress,const InetAddress &addr) :
- _lastSend(0),
- _lastPing(0),
- _lastKeepalive(0),
- _lastReceived(0),
+ Path(const int64_t localSocket,const InetAddress &addr) :
+ _lastOut(0),
+ _lastIn(0),
+ _lastTrustEstablishedPacketReceived(0),
+ _localSocket(localSocket),
+ _latency(0xffff),
_addr(addr),
- _localAddress(localAddress),
- _flags(0),
_ipScope(addr.ipScope())
{
}
- inline Path &operator=(const Path &p)
- {
- if (this != &p)
- memcpy(this,&p,sizeof(Path));
- return *this;
- }
-
/**
- * Called when a packet is sent to this remote path
- *
- * This is called automatically by Path::send().
- *
- * @param t Time of send
- */
- inline void sent(uint64_t t) { _lastSend = t; }
-
- /**
- * Called when we've sent a ping or echo
- *
- * @param t Time of send
- */
- inline void pinged(uint64_t t) { _lastPing = t; }
-
- /**
- * Called when we send a NAT keepalive
- *
- * @param t Time of send
- */
- inline void sentKeepalive(uint64_t t) { _lastKeepalive = t; }
-
- /**
- * Called when a packet is received from this remote path
+ * Called when a packet is received from this remote path, regardless of content
*
* @param t Time of receive
*/
- inline void received(uint64_t t)
- {
- _lastReceived = t;
- _probation = 0;
- }
+ inline void received(const uint64_t t) { _lastIn = t; }
/**
- * @param now Current time
- * @return True if this path appears active
+ * Set time last trusted packet was received (done in Peer::received())
*/
- inline bool active(uint64_t now) const
- {
- return ( ((now - _lastReceived) < ZT_PATH_ACTIVITY_TIMEOUT) && (_probation < ZT_PEER_DEAD_PATH_DETECTION_MAX_PROBATION) );
- }
+ inline void trustedPacketReceived(const uint64_t t) { _lastTrustEstablishedPacketReceived = t; }
/**
- * Send a packet via this path
+ * Send a packet via this path (last out time is also updated)
*
* @param RR Runtime environment
+ * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
* @param data Packet data
* @param len Packet length
* @param now Current time
* @return True if transport reported success
*/
- bool send(const RuntimeEnvironment *RR,const void *data,unsigned int len,uint64_t now);
-
- /**
- * @return Address of local side of this path or NULL if unspecified
- */
- inline const InetAddress &localAddress() const throw() { return _localAddress; }
+ bool send(const RuntimeEnvironment *RR,void *tPtr,const void *data,unsigned int len,int64_t now);
/**
- * @return Time of last send to this path
- */
- inline uint64_t lastSend() const throw() { return _lastSend; }
-
- /**
- * @return Time we last pinged or dead path checked this link
+ * Manually update last sent time
+ *
+ * @param t Time of send
*/
- inline uint64_t lastPing() const throw() { return _lastPing; }
+ inline void sent(const int64_t t) { _lastOut = t; }
/**
- * @return Time of last keepalive
+ * Update path latency with a new measurement
+ *
+ * @param l Measured latency
*/
- inline uint64_t lastKeepalive() const throw() { return _lastKeepalive; }
+ inline void updateLatency(const unsigned int l)
+ {
+ unsigned int pl = _latency;
+ if (pl < 0xffff)
+ _latency = (pl + l) / 2;
+ else _latency = l;
+ }
/**
- * @return Time of last receive from this path
+ * @return Local socket as specified by external code
*/
- inline uint64_t lastReceived() const throw() { return _lastReceived; }
+ inline int64_t localSocket() const { return _localSocket; }
/**
* @return Physical address
*/
- inline const InetAddress &address() const throw() { return _addr; }
+ inline const InetAddress &address() const { return _addr; }
/**
* @return IP scope -- faster shortcut for address().ipScope()
*/
- inline InetAddress::IpScope ipScope() const throw() { return _ipScope; }
+ inline InetAddress::IpScope ipScope() const { return _ipScope; }
/**
- * @param f Valuve of ZT_PATH_FLAG_CLUSTER_SUBOPTIMAL and inverse of ZT_PATH_FLAG_CLUSTER_OPTIMAL (both are changed)
+ * @return True if path has received a trust established packet (e.g. common network membership) in the past ZT_TRUST_EXPIRATION ms
*/
- inline void setClusterSuboptimal(bool f)
- {
- if (f) {
- _flags = (_flags | ZT_PATH_FLAG_CLUSTER_SUBOPTIMAL) & ~ZT_PATH_FLAG_CLUSTER_OPTIMAL;
- } else {
- _flags = (_flags | ZT_PATH_FLAG_CLUSTER_OPTIMAL) & ~ZT_PATH_FLAG_CLUSTER_SUBOPTIMAL;
- }
- }
-
- /**
- * @return True if ZT_PATH_FLAG_CLUSTER_SUBOPTIMAL is set
- */
- inline bool isClusterSuboptimal() const { return ((_flags & ZT_PATH_FLAG_CLUSTER_SUBOPTIMAL) != 0); }
+ inline bool trustEstablished(const int64_t now) const { return ((now - _lastTrustEstablishedPacketReceived) < ZT_TRUST_EXPIRATION); }
/**
- * @return True if ZT_PATH_FLAG_CLUSTER_OPTIMAL is set
+ * @return Preference rank, higher == better
*/
- inline bool isClusterOptimal() const { return ((_flags & ZT_PATH_FLAG_CLUSTER_OPTIMAL) != 0); }
-
- /**
- * @return Preference rank, higher == better (will be less than 255)
- */
- inline unsigned int preferenceRank() const throw()
+ inline unsigned int preferenceRank() const
{
- /* First, since the scope enum values in InetAddress.hpp are in order of
- * use preference rank, we take that. Then we multiple by two, yielding
- * a sequence like 0, 2, 4, 6, etc. Then if it's IPv6 we add one. This
- * makes IPv6 addresses of a given scope outrank IPv4 addresses of the
- * same scope -- e.g. 1 outranks 0. This makes us prefer IPv6, but not
- * if the address scope/class is of a fundamentally lower rank. */
+ // This causes us to rank paths in order of IP scope rank (see InetAdddress.hpp) but
+ // within each IP scope class to prefer IPv6 over IPv4.
return ( ((unsigned int)_ipScope << 1) | (unsigned int)(_addr.ss_family == AF_INET6) );
}
/**
- * @return This path's overall quality score (higher is better)
- */
- inline uint64_t score() const throw()
- {
- // This is a little bit convoluted because we try to be branch-free, using multiplication instead of branches for boolean flags
-
- // Start with the last time this path was active, and add a fudge factor to prevent integer underflow if _lastReceived is 0
- uint64_t score = _lastReceived + (ZT_PEER_DIRECT_PING_DELAY * (ZT_PEER_DEAD_PATH_DETECTION_MAX_PROBATION + 1));
-
- // Increase score based on path preference rank, which is based on IP scope and address family
- score += preferenceRank() * (ZT_PEER_DIRECT_PING_DELAY / ZT_PATH_MAX_PREFERENCE_RANK);
-
- // Increase score if this is known to be an optimal path to a cluster
- score += (uint64_t)(_flags & ZT_PATH_FLAG_CLUSTER_OPTIMAL) * (ZT_PEER_DIRECT_PING_DELAY / 2); // /2 because CLUSTER_OPTIMAL is flag 0x0002
-
- // Decrease score if this is known to be a sub-optimal path to a cluster
- score -= (uint64_t)(_flags & ZT_PATH_FLAG_CLUSTER_SUBOPTIMAL) * ZT_PEER_DIRECT_PING_DELAY;
-
- // Penalize for missed ECHO tests in dead path detection
- score -= (uint64_t)((ZT_PEER_DIRECT_PING_DELAY / 2) * _probation);
-
- return score;
- }
-
- /**
- * @return True if path is considered reliable (no NAT keepalives etc. are needed)
- */
- inline bool reliable() const throw()
- {
- if ((_addr.ss_family == AF_INET)||(_addr.ss_family == AF_INET6))
- return ((_ipScope != InetAddress::IP_SCOPE_GLOBAL)&&(_ipScope != InetAddress::IP_SCOPE_PSEUDOPRIVATE));
- return true;
- }
-
- /**
- * @return True if address is non-NULL
- */
- inline operator bool() const throw() { return (_addr); }
-
- /**
* Check whether this address is valid for a ZeroTier path
*
* This checks the address type and scope against address types and scopes
@@ -272,7 +195,6 @@ public:
* @return True if address is good for ZeroTier path use
*/
static inline bool isAddressValidForPath(const InetAddress &a)
- throw()
{
if ((a.ss_family == AF_INET)||(a.ss_family == AF_INET6)) {
switch(a.ipScope()) {
@@ -304,60 +226,54 @@ public:
}
/**
- * @return Current path probation count (for dead path detect)
+ * @return Latency or 0xffff if unknown
*/
- inline unsigned int probation() const { return _probation; }
+ inline unsigned int latency() const { return _latency; }
/**
- * Increase this path's probation violation count (for dead path detect)
+ * @return Path quality -- lower is better
*/
- inline void increaseProbation() { ++_probation; }
-
- template<unsigned int C>
- inline void serialize(Buffer<C> &b) const
+ inline long quality(const int64_t now) const
{
- b.append((uint8_t)2); // version
- b.append((uint64_t)_lastSend);
- b.append((uint64_t)_lastPing);
- b.append((uint64_t)_lastKeepalive);
- b.append((uint64_t)_lastReceived);
- _addr.serialize(b);
- _localAddress.serialize(b);
- b.append((uint16_t)_flags);
- b.append((uint16_t)_probation);
+ const int l = (long)_latency;
+ const int age = (long)std::min((now - _lastIn),(int64_t)(ZT_PATH_HEARTBEAT_PERIOD * 10)); // set an upper sanity limit to avoid overflow
+ return (((age < (ZT_PATH_HEARTBEAT_PERIOD + 5000)) ? l : (l + 0xffff + age)) * (long)((ZT_INETADDRESS_MAX_SCOPE - _ipScope) + 1));
}
- template<unsigned int C>
- inline unsigned int deserialize(const Buffer<C> &b,unsigned int startAt = 0)
- {
- unsigned int p = startAt;
- if (b[p++] != 2)
- throw std::invalid_argument("invalid serialized Path");
- _lastSend = b.template at<uint64_t>(p); p += 8;
- _lastPing = b.template at<uint64_t>(p); p += 8;
- _lastKeepalive = b.template at<uint64_t>(p); p += 8;
- _lastReceived = b.template at<uint64_t>(p); p += 8;
- p += _addr.deserialize(b,p);
- p += _localAddress.deserialize(b,p);
- _flags = b.template at<uint16_t>(p); p += 2;
- _probation = b.template at<uint16_t>(p); p += 2;
- _ipScope = _addr.ipScope();
- return (p - startAt);
- }
+ /**
+ * @return True if this path is alive (receiving heartbeats)
+ */
+ inline bool alive(const int64_t now) const { return ((now - _lastIn) < (ZT_PATH_HEARTBEAT_PERIOD + 5000)); }
+
+ /**
+ * @return True if this path needs a heartbeat
+ */
+ inline bool needsHeartbeat(const int64_t now) const { return ((now - _lastOut) >= ZT_PATH_HEARTBEAT_PERIOD); }
+
+ /**
+ * @return Last time we sent something
+ */
+ inline int64_t lastOut() const { return _lastOut; }
+
+ /**
+ * @return Last time we received anything
+ */
+ inline int64_t lastIn() const { return _lastIn; }
- inline bool operator==(const Path &p) const { return ((p._addr == _addr)&&(p._localAddress == _localAddress)); }
- inline bool operator!=(const Path &p) const { return ((p._addr != _addr)||(p._localAddress != _localAddress)); }
+ /**
+ * @return Time last trust-established packet was received
+ */
+ inline int64_t lastTrustEstablishedPacketReceived() const { return _lastTrustEstablishedPacketReceived; }
private:
- uint64_t _lastSend;
- uint64_t _lastPing;
- uint64_t _lastKeepalive;
- uint64_t _lastReceived;
+ volatile int64_t _lastOut;
+ volatile int64_t _lastIn;
+ volatile int64_t _lastTrustEstablishedPacketReceived;
+ int64_t _localSocket;
+ volatile unsigned int _latency;
InetAddress _addr;
- InetAddress _localAddress;
- unsigned int _flags;
- unsigned int _probation;
InetAddress::IpScope _ipScope; // memoize this since it's a computed value checked often
+ AtomicCounter __refCount;
};
} // namespace ZeroTier
diff --git a/node/Peer.cpp b/node/Peer.cpp
index cc581004..71afd852 100644
--- a/node/Peer.cpp
+++ b/node/Peer.cpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#include "../version.h"
@@ -24,535 +32,522 @@
#include "Switch.hpp"
#include "Network.hpp"
#include "SelfAwareness.hpp"
-#include "Cluster.hpp"
#include "Packet.hpp"
-
-#include <algorithm>
-
-#define ZT_PEER_PATH_SORT_INTERVAL 5000
+#include "Trace.hpp"
+#include "InetAddress.hpp"
namespace ZeroTier {
-// Used to send varying values for NAT keepalive
-static uint32_t _natKeepaliveBuf = 0;
-
Peer::Peer(const RuntimeEnvironment *renv,const Identity &myIdentity,const Identity &peerIdentity) :
RR(renv),
- _lastUsed(0),
_lastReceive(0),
- _lastUnicastFrame(0),
- _lastMulticastFrame(0),
- _lastAnnouncedTo(0),
+ _lastNontrivialReceive(0),
+ _lastTriedMemorizedPath(0),
_lastDirectPathPushSent(0),
_lastDirectPathPushReceive(0),
- _lastPathSort(0),
+ _lastCredentialRequestSent(0),
+ _lastWhoisRequestReceived(0),
+ _lastEchoRequestReceived(0),
+ _lastComRequestReceived(0),
+ _lastComRequestSent(0),
+ _lastCredentialsReceived(0),
+ _lastTrustEstablishedPacketReceived(0),
+ _lastSentFullHello(0),
_vProto(0),
_vMajor(0),
_vMinor(0),
_vRevision(0),
_id(peerIdentity),
- _numPaths(0),
- _latency(0),
_directPathPushCutoffCount(0),
- _networkComs(4),
- _lastPushedComs(4)
+ _credentialsCutoffCount(0)
{
if (!myIdentity.agree(peerIdentity,_key,ZT_PEER_SECRET_KEY_LENGTH))
- throw std::runtime_error("new peer identity key agreement failed");
+ throw ZT_EXCEPTION_INVALID_ARGUMENT;
}
void Peer::received(
- const InetAddress &localAddr,
- const InetAddress &remoteAddr,
- unsigned int hops,
- uint64_t packetId,
- Packet::Verb verb,
- uint64_t inRePacketId,
- Packet::Verb inReVerb)
+ void *tPtr,
+ const SharedPtr<Path> &path,
+ const unsigned int hops,
+ const uint64_t packetId,
+ const Packet::Verb verb,
+ const uint64_t inRePacketId,
+ const Packet::Verb inReVerb,
+ const bool trustEstablished,
+ const uint64_t networkId)
{
-#ifdef ZT_ENABLE_CLUSTER
- bool suboptimalPath = false;
- if ((RR->cluster)&&(hops == 0)) {
- // Note: findBetterEndpoint() is first since we still want to check
- // for a better endpoint even if we don't actually send a redirect.
- InetAddress redirectTo;
- if ( (verb != Packet::VERB_OK) && (verb != Packet::VERB_ERROR) && (verb != Packet::VERB_RENDEZVOUS) && (verb != Packet::VERB_PUSH_DIRECT_PATHS) && (RR->cluster->findBetterEndpoint(redirectTo,_id.address(),remoteAddr,false)) ) {
- if (_vProto >= 5) {
- // For newer peers we can send a more idiomatic verb: PUSH_DIRECT_PATHS.
- Packet outp(_id.address(),RR->identity.address(),Packet::VERB_PUSH_DIRECT_PATHS);
- outp.append((uint16_t)1); // count == 1
- outp.append((uint8_t)ZT_PUSH_DIRECT_PATHS_FLAG_CLUSTER_REDIRECT); // flags: cluster redirect
- outp.append((uint16_t)0); // no extensions
- if (redirectTo.ss_family == AF_INET) {
- outp.append((uint8_t)4);
- outp.append((uint8_t)6);
- outp.append(redirectTo.rawIpData(),4);
- } else {
- outp.append((uint8_t)6);
- outp.append((uint8_t)18);
- outp.append(redirectTo.rawIpData(),16);
- }
- outp.append((uint16_t)redirectTo.port());
- outp.armor(_key,true);
- RR->node->putPacket(localAddr,remoteAddr,outp.data(),outp.size());
- } else {
- // For older peers we use RENDEZVOUS to coax them into contacting us elsewhere.
- Packet outp(_id.address(),RR->identity.address(),Packet::VERB_RENDEZVOUS);
- outp.append((uint8_t)0); // no flags
- RR->identity.address().appendTo(outp);
- outp.append((uint16_t)redirectTo.port());
- if (redirectTo.ss_family == AF_INET) {
- outp.append((uint8_t)4);
- outp.append(redirectTo.rawIpData(),4);
- } else {
- outp.append((uint8_t)16);
- outp.append(redirectTo.rawIpData(),16);
- }
- outp.armor(_key,true);
- RR->node->putPacket(localAddr,remoteAddr,outp.data(),outp.size());
- }
- suboptimalPath = true;
- }
- }
-#endif
+ const int64_t now = RR->node->now();
- const uint64_t now = RR->node->now();
_lastReceive = now;
- if ((verb == Packet::VERB_FRAME)||(verb == Packet::VERB_EXT_FRAME))
- _lastUnicastFrame = now;
- else if (verb == Packet::VERB_MULTICAST_FRAME)
- _lastMulticastFrame = now;
+ switch (verb) {
+ case Packet::VERB_FRAME:
+ case Packet::VERB_EXT_FRAME:
+ case Packet::VERB_NETWORK_CONFIG_REQUEST:
+ case Packet::VERB_NETWORK_CONFIG:
+ case Packet::VERB_MULTICAST_FRAME:
+ _lastNontrivialReceive = now;
+ break;
+ default: break;
+ }
+
+ if (trustEstablished) {
+ _lastTrustEstablishedPacketReceived = now;
+ path->trustedPacketReceived(now);
+ }
if (hops == 0) {
- bool pathIsConfirmed = false;
- unsigned int np = _numPaths;
- for(unsigned int p=0;p<np;++p) {
- if ((_paths[p].address() == remoteAddr)&&(_paths[p].localAddress() == localAddr)) {
- _paths[p].received(now);
-#ifdef ZT_ENABLE_CLUSTER
- _paths[p].setClusterSuboptimal(suboptimalPath);
-#endif
- pathIsConfirmed = true;
- break;
+ // If this is a direct packet (no hops), update existing paths or learn new ones
+
+ bool havePath = false;
+ {
+ Mutex::Lock _l(_paths_m);
+ for(unsigned int i=0;i<ZT_MAX_PEER_NETWORK_PATHS;++i) {
+ if (_paths[i].p) {
+ if (_paths[i].p == path) {
+ _paths[i].lr = now;
+ havePath = true;
+ break;
+ }
+ } else break;
}
}
- if ((!pathIsConfirmed)&&(RR->node->shouldUsePathForZeroTierTraffic(localAddr,remoteAddr))) {
- if (verb == Packet::VERB_OK) {
+ bool attemptToContact = false;
+ if ((!havePath)&&(RR->node->shouldUsePathForZeroTierTraffic(tPtr,_id.address(),path->localSocket(),path->address()))) {
+ Mutex::Lock _l(_paths_m);
+
+ // Paths are redunant if they duplicate an alive path to the same IP or
+ // with the same local socket and address family.
+ bool redundant = false;
+ for(unsigned int i=0;i<ZT_MAX_PEER_NETWORK_PATHS;++i) {
+ if (_paths[i].p) {
+ if ( (_paths[i].p->alive(now)) && ( ((_paths[i].p->localSocket() == path->localSocket())&&(_paths[i].p->address().ss_family == path->address().ss_family)) || (_paths[i].p->address().ipsEqual2(path->address())) ) ) {
+ redundant = true;
+ break;
+ }
+ } else break;
+ }
- Path *slot = (Path *)0;
- if (np < ZT_MAX_PEER_NETWORK_PATHS) {
- slot = &(_paths[np++]);
- } else {
- uint64_t slotWorstScore = 0xffffffffffffffffULL;
- for(unsigned int p=0;p<ZT_MAX_PEER_NETWORK_PATHS;++p) {
- if (!_paths[p].active(now)) {
- slot = &(_paths[p]);
- break;
- } else {
- const uint64_t score = _paths[p].score();
- if (score <= slotWorstScore) {
- slotWorstScore = score;
- slot = &(_paths[p]);
- }
+ if (!redundant) {
+ unsigned int replacePath = ZT_MAX_PEER_NETWORK_PATHS;
+ int replacePathQuality = 0;
+ for(unsigned int i=0;i<ZT_MAX_PEER_NETWORK_PATHS;++i) {
+ if (_paths[i].p) {
+ const int q = _paths[i].p->quality(now);
+ if (q > replacePathQuality) {
+ replacePathQuality = q;
+ replacePath = i;
}
+ } else {
+ replacePath = i;
+ break;
}
}
- if (slot) {
- *slot = Path(localAddr,remoteAddr);
- slot->received(now);
-#ifdef ZT_ENABLE_CLUSTER
- slot->setClusterSuboptimal(suboptimalPath);
-#endif
- _numPaths = np;
- }
-#ifdef ZT_ENABLE_CLUSTER
- if (RR->cluster)
- RR->cluster->broadcastHavePeer(_id);
-#endif
-
- } else {
-
- TRACE("got %s via unknown path %s(%s), confirming...",Packet::verbString(verb),_id.address().toString().c_str(),remoteAddr.toString().c_str());
-
- if ( (_vProto >= 5) && ( !((_vMajor == 1)&&(_vMinor == 1)&&(_vRevision == 0)) ) ) {
- Packet outp(_id.address(),RR->identity.address(),Packet::VERB_ECHO);
- outp.armor(_key,true);
- RR->node->putPacket(localAddr,remoteAddr,outp.data(),outp.size());
- } else {
- sendHELLO(localAddr,remoteAddr,now);
+ if (replacePath != ZT_MAX_PEER_NETWORK_PATHS) {
+ if (verb == Packet::VERB_OK) {
+ RR->t->peerLearnedNewPath(tPtr,networkId,*this,path,packetId);
+ _paths[replacePath].lr = now;
+ _paths[replacePath].p = path;
+ _paths[replacePath].priority = 1;
+ } else {
+ attemptToContact = true;
+ }
}
-
}
}
- }
- if ((now - _lastAnnouncedTo) >= ((ZT_MULTICAST_LIKE_EXPIRE / 2) - 1000)) {
- _lastAnnouncedTo = now;
- const std::vector< SharedPtr<Network> > networks(RR->node->allNetworks());
- for(std::vector< SharedPtr<Network> >::const_iterator n(networks.begin());n!=networks.end();++n)
- (*n)->tryAnnounceMulticastGroupsTo(SharedPtr<Peer>(this));
+ if (attemptToContact) {
+ attemptToContactAt(tPtr,path->localSocket(),path->address(),now,true);
+ path->sent(now);
+ RR->t->peerConfirmingUnknownPath(tPtr,networkId,*this,path,packetId,verb);
+ }
}
-}
-void Peer::sendHELLO(const InetAddress &localAddr,const InetAddress &atAddress,uint64_t now,unsigned int ttl)
-{
- Packet outp(_id.address(),RR->identity.address(),Packet::VERB_HELLO);
- outp.append((unsigned char)ZT_PROTO_VERSION);
- outp.append((unsigned char)ZEROTIER_ONE_VERSION_MAJOR);
- outp.append((unsigned char)ZEROTIER_ONE_VERSION_MINOR);
- outp.append((uint16_t)ZEROTIER_ONE_VERSION_REVISION);
- outp.append(now);
- RR->identity.serialize(outp,false);
- atAddress.serialize(outp);
- outp.append((uint64_t)RR->topology->worldId());
- outp.append((uint64_t)RR->topology->worldTimestamp());
+ // If we have a trust relationship periodically push a message enumerating
+ // all known external addresses for ourselves. We now do this even if we
+ // have a current path since we'll want to use new ones too.
+ if (this->trustEstablished(now)) {
+ if ((now - _lastDirectPathPushSent) >= ZT_DIRECT_PATH_PUSH_INTERVAL) {
+ _lastDirectPathPushSent = now;
+
+ std::vector<InetAddress> pathsToPush;
+
+ std::vector<InetAddress> dps(RR->node->directPaths());
+ for(std::vector<InetAddress>::const_iterator i(dps.begin());i!=dps.end();++i)
+ pathsToPush.push_back(*i);
+
+ // Do symmetric NAT prediction if we are communicating indirectly.
+ if (hops > 0) {
+ std::vector<InetAddress> sym(RR->sa->getSymmetricNatPredictions());
+ for(unsigned long i=0,added=0;i<sym.size();++i) {
+ InetAddress tmp(sym[(unsigned long)RR->node->prng() % sym.size()]);
+ if (std::find(pathsToPush.begin(),pathsToPush.end(),tmp) == pathsToPush.end()) {
+ pathsToPush.push_back(tmp);
+ if (++added >= ZT_PUSH_DIRECT_PATHS_MAX_PER_SCOPE_AND_FAMILY)
+ break;
+ }
+ }
+ }
- outp.armor(_key,false); // HELLO is sent in the clear
- RR->node->putPacket(localAddr,atAddress,outp.data(),outp.size(),ttl);
-}
+ if (pathsToPush.size() > 0) {
+ std::vector<InetAddress>::const_iterator p(pathsToPush.begin());
+ while (p != pathsToPush.end()) {
+ Packet outp(_id.address(),RR->identity.address(),Packet::VERB_PUSH_DIRECT_PATHS);
+ outp.addSize(2); // leave room for count
+
+ unsigned int count = 0;
+ while ((p != pathsToPush.end())&&((outp.size() + 24) < 1200)) {
+ uint8_t addressType = 4;
+ switch(p->ss_family) {
+ case AF_INET:
+ break;
+ case AF_INET6:
+ addressType = 6;
+ break;
+ default: // we currently only push IP addresses
+ ++p;
+ continue;
+ }
-bool Peer::doPingAndKeepalive(uint64_t now,int inetAddressFamily)
-{
- Path *p = (Path *)0;
+ outp.append((uint8_t)0); // no flags
+ outp.append((uint16_t)0); // no extensions
+ outp.append(addressType);
+ outp.append((uint8_t)((addressType == 4) ? 6 : 18));
+ outp.append(p->rawIpData(),((addressType == 4) ? 4 : 16));
+ outp.append((uint16_t)p->port());
- if (inetAddressFamily != 0) {
- p = _getBestPath(now,inetAddressFamily);
- } else {
- p = _getBestPath(now);
- }
+ ++count;
+ ++p;
+ }
- if (p) {
- if ((now - p->lastReceived()) >= ZT_PEER_DIRECT_PING_DELAY) {
- //TRACE("PING %s(%s) after %llums/%llums send/receive inactivity",_id.address().toString().c_str(),p->address().toString().c_str(),now - p->lastSend(),now - p->lastReceived());
- sendHELLO(p->localAddress(),p->address(),now);
- p->sent(now);
- p->pinged(now);
- } else if ( ((now - std::max(p->lastSend(),p->lastKeepalive())) >= ZT_NAT_KEEPALIVE_DELAY) && (!p->reliable()) ) {
- //TRACE("NAT keepalive %s(%s) after %llums/%llums send/receive inactivity",_id.address().toString().c_str(),p->address().toString().c_str(),now - p->lastSend(),now - p->lastReceived());
- _natKeepaliveBuf += (uint32_t)((now * 0x9e3779b1) >> 1); // tumble this around to send constantly varying (meaningless) payloads
- RR->node->putPacket(p->localAddress(),p->address(),&_natKeepaliveBuf,sizeof(_natKeepaliveBuf));
- p->sentKeepalive(now);
- } else {
- //TRACE("no PING or NAT keepalive: addr==%s reliable==%d %llums/%llums send/receive inactivity",p->address().toString().c_str(),(int)p->reliable(),now - p->lastSend(),now - p->lastReceived());
+ if (count) {
+ outp.setAt(ZT_PACKET_IDX_PAYLOAD,(uint16_t)count);
+ outp.armor(_key,true);
+ path->send(RR,tPtr,outp.data(),outp.size(),now);
+ }
+ }
+ }
}
- return true;
}
-
- return false;
}
-bool Peer::pushDirectPaths(const InetAddress &localAddr,const InetAddress &toAddress,uint64_t now,bool force,bool includePrivatePaths)
+SharedPtr<Path> Peer::getBestPath(int64_t now,bool includeExpired) const
{
-#ifdef ZT_ENABLE_CLUSTER
- // Cluster mode disables normal PUSH_DIRECT_PATHS in favor of cluster-based peer redirection
- if (RR->cluster)
- return false;
-#endif
-
- if (!force) {
- if ((now - _lastDirectPathPushSent) < ZT_DIRECT_PATH_PUSH_INTERVAL)
- return false;
- else _lastDirectPathPushSent = now;
+ Mutex::Lock _l(_paths_m);
+
+ unsigned int bestPath = ZT_MAX_PEER_NETWORK_PATHS;
+ long bestPathQuality = 2147483647;
+ for(unsigned int i=0;i<ZT_MAX_PEER_NETWORK_PATHS;++i) {
+ if (_paths[i].p) {
+ if ((includeExpired)||((now - _paths[i].lr) < ZT_PEER_PATH_EXPIRATION)) {
+ const long q = _paths[i].p->quality(now) / _paths[i].priority;
+ if (q <= bestPathQuality) {
+ bestPathQuality = q;
+ bestPath = i;
+ }
+ }
+ } else break;
}
- std::vector<InetAddress> pathsToPush;
+ if (bestPath != ZT_MAX_PEER_NETWORK_PATHS)
+ return _paths[bestPath].p;
+ return SharedPtr<Path>();
+}
- std::vector<InetAddress> dps(RR->node->directPaths());
- for(std::vector<InetAddress>::const_iterator i(dps.begin());i!=dps.end();++i) {
- if ((includePrivatePaths)||(i->ipScope() == InetAddress::IP_SCOPE_GLOBAL))
- pathsToPush.push_back(*i);
+void Peer::introduce(void *const tPtr,const int64_t now,const SharedPtr<Peer> &other) const
+{
+ unsigned int myBestV4ByScope[ZT_INETADDRESS_MAX_SCOPE+1];
+ unsigned int myBestV6ByScope[ZT_INETADDRESS_MAX_SCOPE+1];
+ long myBestV4QualityByScope[ZT_INETADDRESS_MAX_SCOPE+1];
+ long myBestV6QualityByScope[ZT_INETADDRESS_MAX_SCOPE+1];
+ unsigned int theirBestV4ByScope[ZT_INETADDRESS_MAX_SCOPE+1];
+ unsigned int theirBestV6ByScope[ZT_INETADDRESS_MAX_SCOPE+1];
+ long theirBestV4QualityByScope[ZT_INETADDRESS_MAX_SCOPE+1];
+ long theirBestV6QualityByScope[ZT_INETADDRESS_MAX_SCOPE+1];
+ for(int i=0;i<=ZT_INETADDRESS_MAX_SCOPE;++i) {
+ myBestV4ByScope[i] = ZT_MAX_PEER_NETWORK_PATHS;
+ myBestV6ByScope[i] = ZT_MAX_PEER_NETWORK_PATHS;
+ myBestV4QualityByScope[i] = 2147483647;
+ myBestV6QualityByScope[i] = 2147483647;
+ theirBestV4ByScope[i] = ZT_MAX_PEER_NETWORK_PATHS;
+ theirBestV6ByScope[i] = ZT_MAX_PEER_NETWORK_PATHS;
+ theirBestV4QualityByScope[i] = 2147483647;
+ theirBestV6QualityByScope[i] = 2147483647;
}
- std::vector<InetAddress> sym(RR->sa->getSymmetricNatPredictions());
- for(unsigned long i=0,added=0;i<sym.size();++i) {
- InetAddress tmp(sym[(unsigned long)RR->node->prng() % sym.size()]);
- if (std::find(pathsToPush.begin(),pathsToPush.end(),tmp) == pathsToPush.end()) {
- pathsToPush.push_back(tmp);
- if (++added >= ZT_PUSH_DIRECT_PATHS_MAX_PER_SCOPE_AND_FAMILY)
- break;
- }
- }
- if (pathsToPush.empty())
- return false;
+ Mutex::Lock _l1(_paths_m);
-#ifdef ZT_TRACE
- {
- std::string ps;
- for(std::vector<InetAddress>::const_iterator p(pathsToPush.begin());p!=pathsToPush.end();++p) {
- if (ps.length() > 0)
- ps.push_back(',');
- ps.append(p->toString());
- }
- TRACE("pushing %u direct paths to %s: %s",(unsigned int)pathsToPush.size(),_id.address().toString().c_str(),ps.c_str());
+ for(unsigned int i=0;i<ZT_MAX_PEER_NETWORK_PATHS;++i) {
+ if (_paths[i].p) {
+ const long q = _paths[i].p->quality(now) / _paths[i].priority;
+ const unsigned int s = (unsigned int)_paths[i].p->ipScope();
+ switch(_paths[i].p->address().ss_family) {
+ case AF_INET:
+ if (q <= myBestV4QualityByScope[s]) {
+ myBestV4QualityByScope[s] = q;
+ myBestV4ByScope[s] = i;
+ }
+ break;
+ case AF_INET6:
+ if (q <= myBestV6QualityByScope[s]) {
+ myBestV6QualityByScope[s] = q;
+ myBestV6ByScope[s] = i;
+ }
+ break;
+ }
+ } else break;
}
-#endif
- std::vector<InetAddress>::const_iterator p(pathsToPush.begin());
- while (p != pathsToPush.end()) {
- Packet outp(_id.address(),RR->identity.address(),Packet::VERB_PUSH_DIRECT_PATHS);
- outp.addSize(2); // leave room for count
+ Mutex::Lock _l2(other->_paths_m);
- unsigned int count = 0;
- while ((p != pathsToPush.end())&&((outp.size() + 24) < 1200)) {
- uint8_t addressType = 4;
- switch(p->ss_family) {
+ for(unsigned int i=0;i<ZT_MAX_PEER_NETWORK_PATHS;++i) {
+ if (other->_paths[i].p) {
+ const long q = other->_paths[i].p->quality(now) / other->_paths[i].priority;
+ const unsigned int s = (unsigned int)other->_paths[i].p->ipScope();
+ switch(other->_paths[i].p->address().ss_family) {
case AF_INET:
+ if (q <= theirBestV4QualityByScope[s]) {
+ theirBestV4QualityByScope[s] = q;
+ theirBestV4ByScope[s] = i;
+ }
break;
case AF_INET6:
- addressType = 6;
+ if (q <= theirBestV6QualityByScope[s]) {
+ theirBestV6QualityByScope[s] = q;
+ theirBestV6ByScope[s] = i;
+ }
break;
- default: // we currently only push IP addresses
- ++p;
- continue;
}
-
- outp.append((uint8_t)0); // no flags
- outp.append((uint16_t)0); // no extensions
- outp.append(addressType);
- outp.append((uint8_t)((addressType == 4) ? 6 : 18));
- outp.append(p->rawIpData(),((addressType == 4) ? 4 : 16));
- outp.append((uint16_t)p->port());
-
- ++count;
- ++p;
- }
-
- if (count) {
- outp.setAt(ZT_PACKET_IDX_PAYLOAD,(uint16_t)count);
- outp.armor(_key,true);
- RR->node->putPacket(localAddr,toAddress,outp.data(),outp.size(),0);
- }
+ } else break;
}
- return true;
-}
+ unsigned int mine = ZT_MAX_PEER_NETWORK_PATHS;
+ unsigned int theirs = ZT_MAX_PEER_NETWORK_PATHS;
-bool Peer::resetWithinScope(InetAddress::IpScope scope,uint64_t now)
-{
- unsigned int np = _numPaths;
- unsigned int x = 0;
- unsigned int y = 0;
- while (x < np) {
- if (_paths[x].address().ipScope() == scope) {
- // Resetting a path means sending a HELLO and then forgetting it. If we
- // get OK(HELLO) then it will be re-learned.
- sendHELLO(_paths[x].localAddress(),_paths[x].address(),now);
- } else {
- _paths[y++] = _paths[x];
+ for(int s=ZT_INETADDRESS_MAX_SCOPE;s>=0;--s) {
+ if ((myBestV6ByScope[s] != ZT_MAX_PEER_NETWORK_PATHS)&&(theirBestV6ByScope[s] != ZT_MAX_PEER_NETWORK_PATHS)) {
+ mine = myBestV6ByScope[s];
+ theirs = theirBestV6ByScope[s];
+ break;
+ }
+ if ((myBestV4ByScope[s] != ZT_MAX_PEER_NETWORK_PATHS)&&(theirBestV4ByScope[s] != ZT_MAX_PEER_NETWORK_PATHS)) {
+ mine = myBestV4ByScope[s];
+ theirs = theirBestV4ByScope[s];
+ break;
}
- ++x;
}
- _numPaths = y;
- return (y < np);
-}
-void Peer::getBestActiveAddresses(uint64_t now,InetAddress &v4,InetAddress &v6) const
-{
- uint64_t bestV4 = 0,bestV6 = 0;
- for(unsigned int p=0,np=_numPaths;p<np;++p) {
- if (_paths[p].active(now)) {
- uint64_t lr = _paths[p].lastReceived();
- if (lr) {
- if (_paths[p].address().isV4()) {
- if (lr >= bestV4) {
- bestV4 = lr;
- v4 = _paths[p].address();
- }
- } else if (_paths[p].address().isV6()) {
- if (lr >= bestV6) {
- bestV6 = lr;
- v6 = _paths[p].address();
- }
+ if (mine != ZT_MAX_PEER_NETWORK_PATHS) {
+ unsigned int alt = (unsigned int)RR->node->prng() & 1; // randomize which hint we send first for black magickal NAT-t reasons
+ const unsigned int completed = alt + 2;
+ while (alt != completed) {
+ if ((alt & 1) == 0) {
+ Packet outp(_id.address(),RR->identity.address(),Packet::VERB_RENDEZVOUS);
+ outp.append((uint8_t)0);
+ other->_id.address().appendTo(outp);
+ outp.append((uint16_t)other->_paths[theirs].p->address().port());
+ if (other->_paths[theirs].p->address().ss_family == AF_INET6) {
+ outp.append((uint8_t)16);
+ outp.append(other->_paths[theirs].p->address().rawIpData(),16);
+ } else {
+ outp.append((uint8_t)4);
+ outp.append(other->_paths[theirs].p->address().rawIpData(),4);
}
+ outp.armor(_key,true);
+ _paths[mine].p->send(RR,tPtr,outp.data(),outp.size(),now);
+ } else {
+ Packet outp(other->_id.address(),RR->identity.address(),Packet::VERB_RENDEZVOUS);
+ outp.append((uint8_t)0);
+ _id.address().appendTo(outp);
+ outp.append((uint16_t)_paths[mine].p->address().port());
+ if (_paths[mine].p->address().ss_family == AF_INET6) {
+ outp.append((uint8_t)16);
+ outp.append(_paths[mine].p->address().rawIpData(),16);
+ } else {
+ outp.append((uint8_t)4);
+ outp.append(_paths[mine].p->address().rawIpData(),4);
+ }
+ outp.armor(other->_key,true);
+ other->_paths[theirs].p->send(RR,tPtr,outp.data(),outp.size(),now);
}
+ ++alt;
}
}
}
-bool Peer::networkMembershipCertificatesAgree(uint64_t nwid,const CertificateOfMembership &com) const
+void Peer::sendHELLO(void *tPtr,const int64_t localSocket,const InetAddress &atAddress,int64_t now)
{
- Mutex::Lock _l(_networkComs_m);
- const _NetworkCom *ourCom = _networkComs.get(nwid);
- if (ourCom)
- return ourCom->com.agreesWith(com);
- return false;
-}
+ Packet outp(_id.address(),RR->identity.address(),Packet::VERB_HELLO);
-bool Peer::validateAndSetNetworkMembershipCertificate(uint64_t nwid,const CertificateOfMembership &com)
-{
- // Sanity checks
- if ((!com)||(com.issuedTo() != _id.address()))
- return false;
+ outp.append((unsigned char)ZT_PROTO_VERSION);
+ outp.append((unsigned char)ZEROTIER_ONE_VERSION_MAJOR);
+ outp.append((unsigned char)ZEROTIER_ONE_VERSION_MINOR);
+ outp.append((uint16_t)ZEROTIER_ONE_VERSION_REVISION);
+ outp.append(now);
+ RR->identity.serialize(outp,false);
+ atAddress.serialize(outp);
- // Return true if we already have this *exact* COM
- {
- Mutex::Lock _l(_networkComs_m);
- _NetworkCom *ourCom = _networkComs.get(nwid);
- if ((ourCom)&&(ourCom->com == com))
- return true;
- }
+ outp.append((uint64_t)RR->topology->planetWorldId());
+ outp.append((uint64_t)RR->topology->planetWorldTimestamp());
+
+ const unsigned int startCryptedPortionAt = outp.size();
- // Check signature, log and return if cert is invalid
- if (com.signedBy() != Network::controllerFor(nwid)) {
- TRACE("rejected network membership certificate for %.16llx signed by %s: signer not a controller of this network",(unsigned long long)nwid,com.signedBy().toString().c_str());
- return false; // invalid signer
+ std::vector<World> moons(RR->topology->moons());
+ std::vector<uint64_t> moonsWanted(RR->topology->moonsWanted());
+ outp.append((uint16_t)(moons.size() + moonsWanted.size()));
+ for(std::vector<World>::const_iterator m(moons.begin());m!=moons.end();++m) {
+ outp.append((uint8_t)m->type());
+ outp.append((uint64_t)m->id());
+ outp.append((uint64_t)m->timestamp());
+ }
+ for(std::vector<uint64_t>::const_iterator m(moonsWanted.begin());m!=moonsWanted.end();++m) {
+ outp.append((uint8_t)World::TYPE_MOON);
+ outp.append(*m);
+ outp.append((uint64_t)0);
}
- if (com.signedBy() == RR->identity.address()) {
+ outp.cryptField(_key,startCryptedPortionAt,outp.size() - startCryptedPortionAt);
- // We are the controller: RR->identity.address() == controller() == cert.signedBy()
- // So, verify that we signed th cert ourself
- if (!com.verify(RR->identity)) {
- TRACE("rejected network membership certificate for %.16llx self signed by %s: signature check failed",(unsigned long long)nwid,com.signedBy().toString().c_str());
- return false; // invalid signature
- }
+ RR->node->expectReplyTo(outp.packetId());
+ if (atAddress) {
+ outp.armor(_key,false); // false == don't encrypt full payload, but add MAC
+ RR->node->putPacket(tPtr,localSocket,atAddress,outp.data(),outp.size());
} else {
-
- SharedPtr<Peer> signer(RR->topology->getPeer(com.signedBy()));
-
- if (!signer) {
- // This would be rather odd, since this is our controller... could happen
- // if we get packets before we've gotten config.
- RR->sw->requestWhois(com.signedBy());
- return false; // signer unknown
- }
-
- if (!com.verify(signer->identity())) {
- TRACE("rejected network membership certificate for %.16llx signed by %s: signature check failed",(unsigned long long)nwid,com.signedBy().toString().c_str());
- return false; // invalid signature
- }
+ RR->sw->send(tPtr,outp,false); // false == don't encrypt full payload, but add MAC
}
+}
- // If we made it past all those checks, add or update cert in our cert info store
- {
- Mutex::Lock _l(_networkComs_m);
- _networkComs.set(nwid,_NetworkCom(RR->node->now(),com));
+void Peer::attemptToContactAt(void *tPtr,const int64_t localSocket,const InetAddress &atAddress,int64_t now,bool sendFullHello)
+{
+ if ( (!sendFullHello) && (_vProto >= 5) && (!((_vMajor == 1)&&(_vMinor == 1)&&(_vRevision == 0))) ) {
+ Packet outp(_id.address(),RR->identity.address(),Packet::VERB_ECHO);
+ RR->node->expectReplyTo(outp.packetId());
+ outp.armor(_key,true);
+ RR->node->putPacket(tPtr,localSocket,atAddress,outp.data(),outp.size());
+ } else {
+ sendHELLO(tPtr,localSocket,atAddress,now);
}
-
- return true;
}
-bool Peer::needsOurNetworkMembershipCertificate(uint64_t nwid,uint64_t now,bool updateLastPushedTime)
+void Peer::tryMemorizedPath(void *tPtr,int64_t now)
{
- Mutex::Lock _l(_networkComs_m);
- uint64_t &lastPushed = _lastPushedComs[nwid];
- const uint64_t tmp = lastPushed;
- if (updateLastPushedTime)
- lastPushed = now;
- return ((now - tmp) >= (ZT_NETWORK_AUTOCONF_DELAY / 3));
+ if ((now - _lastTriedMemorizedPath) >= ZT_TRY_MEMORIZED_PATH_INTERVAL) {
+ _lastTriedMemorizedPath = now;
+ InetAddress mp;
+ if (RR->node->externalPathLookup(tPtr,_id.address(),-1,mp))
+ attemptToContactAt(tPtr,-1,mp,now,true);
+ }
}
-void Peer::clean(uint64_t now)
+unsigned int Peer::doPingAndKeepalive(void *tPtr,int64_t now)
{
- {
- unsigned int np = _numPaths;
- unsigned int x = 0;
- unsigned int y = 0;
- while (x < np) {
- if (_paths[x].active(now))
- _paths[y++] = _paths[x];
- ++x;
- }
- _numPaths = y;
+ unsigned int sent = 0;
+
+ Mutex::Lock _l(_paths_m);
+
+ const bool sendFullHello = ((now - _lastSentFullHello) >= ZT_PEER_PING_PERIOD);
+ _lastSentFullHello = now;
+
+ // Right now we only keep pinging links that have the maximum priority. The
+ // priority is used to track cluster redirections, meaning that when a cluster
+ // redirects us its redirect target links override all other links and we
+ // let those old links expire.
+ long maxPriority = 0;
+ for(unsigned int i=0;i<ZT_MAX_PEER_NETWORK_PATHS;++i) {
+ if (_paths[i].p)
+ maxPriority = std::max(_paths[i].priority,maxPriority);
+ else break;
}
- {
- Mutex::Lock _l(_networkComs_m);
- {
- uint64_t *k = (uint64_t *)0;
- _NetworkCom *v = (_NetworkCom *)0;
- Hashtable< uint64_t,_NetworkCom >::Iterator i(_networkComs);
- while (i.next(k,v)) {
- if ( (!RR->node->belongsToNetwork(*k)) && ((now - v->ts) >= ZT_PEER_NETWORK_COM_EXPIRATION) )
- _networkComs.erase(*k);
- }
- }
- {
- uint64_t *k = (uint64_t *)0;
- uint64_t *v = (uint64_t *)0;
- Hashtable< uint64_t,uint64_t >::Iterator i(_lastPushedComs);
- while (i.next(k,v)) {
- if ((now - *v) > (ZT_NETWORK_AUTOCONF_DELAY * 2))
- _lastPushedComs.erase(*k);
+ unsigned int j = 0;
+ for(unsigned int i=0;i<ZT_MAX_PEER_NETWORK_PATHS;++i) {
+ if (_paths[i].p) {
+ // Clean expired and reduced priority paths
+ if ( ((now - _paths[i].lr) < ZT_PEER_PATH_EXPIRATION) && (_paths[i].priority == maxPriority) ) {
+ if ((sendFullHello)||(_paths[i].p->needsHeartbeat(now))) {
+ attemptToContactAt(tPtr,_paths[i].p->localSocket(),_paths[i].p->address(),now,sendFullHello);
+ _paths[i].p->sent(now);
+ sent |= (_paths[i].p->address().ss_family == AF_INET) ? 0x1 : 0x2;
+ }
+ if (i != j)
+ _paths[j] = _paths[i];
+ ++j;
}
- }
+ } else break;
}
+ while(j < ZT_MAX_PEER_NETWORK_PATHS) {
+ _paths[j].lr = 0;
+ _paths[j].p.zero();
+ _paths[j].priority = 1;
+ ++j;
+ }
+
+ return sent;
}
-void Peer::_doDeadPathDetection(Path &p,const uint64_t now)
+void Peer::clusterRedirect(void *tPtr,const SharedPtr<Path> &originatingPath,const InetAddress &remoteAddress,const int64_t now)
{
- /* Dead path detection: if we have sent something to this peer and have not
- * yet received a reply, double check this path. The majority of outbound
- * packets including Ethernet frames do generate some kind of reply either
- * immediately or at some point in the near future. This will occasionally
- * (every NO_ANSWER_TIMEOUT ms) check paths unnecessarily if traffic that
- * does not generate a response is being sent such as multicast announcements
- * or frames belonging to unidirectional UDP protocols, but the cost is very
- * tiny and the benefit in reliability is very large. This takes care of many
- * failure modes including crap NATs that forget links and spurious changes
- * to physical network topology that cannot be otherwise detected.
- *
- * Each time we do this we increment a probation counter in the path. This
- * counter is reset on any packet receive over this path. If it reaches the
- * MAX_PROBATION threshold the path is considred dead. */
-
- if (
- (p.lastSend() > p.lastReceived()) &&
- ((p.lastSend() - p.lastReceived()) >= ZT_PEER_DEAD_PATH_DETECTION_NO_ANSWER_TIMEOUT) &&
- ((now - p.lastPing()) >= ZT_PEER_DEAD_PATH_DETECTION_NO_ANSWER_TIMEOUT) &&
- (!p.isClusterSuboptimal()) &&
- (!RR->topology->amRoot())
- ) {
- TRACE("%s(%s) does not seem to be answering in a timely manner, checking if dead (probation == %u)",_id.address().toString().c_str(),p.address().toString().c_str(),p.probation());
-
- if ( (_vProto >= 5) && ( !((_vMajor == 1)&&(_vMinor == 1)&&(_vRevision == 0)) ) ) {
- Packet outp(_id.address(),RR->identity.address(),Packet::VERB_ECHO);
- outp.armor(_key,true);
- p.send(RR,outp.data(),outp.size(),now);
- p.pinged(now);
- } else {
- sendHELLO(p.localAddress(),p.address(),now);
- p.sent(now);
- p.pinged(now);
- }
+ SharedPtr<Path> np(RR->topology->getPath(originatingPath->localSocket(),remoteAddress));
+ RR->t->peerRedirected(tPtr,0,*this,np);
- p.increaseProbation();
- }
-}
+ attemptToContactAt(tPtr,originatingPath->localSocket(),remoteAddress,now,true);
-Path *Peer::_getBestPath(const uint64_t now)
-{
- Path *bestPath = (Path *)0;
- uint64_t bestPathScore = 0;
- for(unsigned int i=0;i<_numPaths;++i) {
- const uint64_t score = _paths[i].score();
- if ((score >= bestPathScore)&&(_paths[i].active(now))) {
- bestPathScore = score;
- bestPath = &(_paths[i]);
+ {
+ Mutex::Lock _l(_paths_m);
+
+ // New priority is higher than the priority of the originating path (if known)
+ long newPriority = 1;
+ for(unsigned int i=0;i<ZT_MAX_PEER_NETWORK_PATHS;++i) {
+ if (_paths[i].p) {
+ if (_paths[i].p == originatingPath) {
+ newPriority = _paths[i].priority;
+ break;
+ }
+ } else break;
+ }
+ newPriority += 2;
+
+ // Erase any paths with lower priority than this one or that are duplicate
+ // IPs and add this path.
+ unsigned int j = 0;
+ for(unsigned int i=0;i<ZT_MAX_PEER_NETWORK_PATHS;++i) {
+ if (_paths[i].p) {
+ if ((_paths[i].priority >= newPriority)&&(!_paths[i].p->address().ipsEqual2(remoteAddress))) {
+ if (i != j)
+ _paths[j] = _paths[i];
+ ++j;
+ }
+ }
+ }
+ if (j < ZT_MAX_PEER_NETWORK_PATHS) {
+ _paths[j].lr = now;
+ _paths[j].p = np;
+ _paths[j].priority = newPriority;
+ ++j;
+ while (j < ZT_MAX_PEER_NETWORK_PATHS) {
+ _paths[j].lr = 0;
+ _paths[j].p.zero();
+ _paths[j].priority = 1;
+ ++j;
+ }
}
}
- if (bestPath)
- _doDeadPathDetection(*bestPath,now);
- return bestPath;
}
-Path *Peer::_getBestPath(const uint64_t now,int inetAddressFamily)
+void Peer::resetWithinScope(void *tPtr,InetAddress::IpScope scope,int inetAddressFamily,int64_t now)
{
- Path *bestPath = (Path *)0;
- uint64_t bestPathScore = 0;
- for(unsigned int i=0;i<_numPaths;++i) {
- const uint64_t score = _paths[i].score();
- if (((int)_paths[i].address().ss_family == inetAddressFamily)&&(score >= bestPathScore)&&(_paths[i].active(now))) {
- bestPathScore = score;
- bestPath = &(_paths[i]);
- }
+ Mutex::Lock _l(_paths_m);
+ for(unsigned int i=0;i<ZT_MAX_PEER_NETWORK_PATHS;++i) {
+ if (_paths[i].p) {
+ if ((_paths[i].p->address().ss_family == inetAddressFamily)&&(_paths[i].p->ipScope() == scope)) {
+ attemptToContactAt(tPtr,_paths[i].p->localSocket(),_paths[i].p->address(),now,false);
+ _paths[i].p->sent(now);
+ _paths[i].lr = 0; // path will not be used unless it speaks again
+ }
+ } else break;
}
- if (bestPath)
- _doDeadPathDetection(*bestPath,now);
- return bestPath;
}
} // namespace ZeroTier
diff --git a/node/Peer.hpp b/node/Peer.hpp
index 445535c8..b6f3c695 100644
--- a/node/Peer.hpp
+++ b/node/Peer.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_PEER_HPP
@@ -31,7 +39,6 @@
#include "../include/ZeroTierOne.h"
#include "RuntimeEnvironment.hpp"
-#include "CertificateOfMembership.hpp"
#include "Path.hpp"
#include "Address.hpp"
#include "Utils.hpp"
@@ -42,18 +49,15 @@
#include "AtomicCounter.hpp"
#include "Hashtable.hpp"
#include "Mutex.hpp"
-#include "NonCopyable.hpp"
-// Very rough computed estimate: (8 + 256 + 80 + (16 * 64) + (128 * 256) + (128 * 16))
-// 1048576 provides tons of headroom -- overflow would just cause peer not to be persisted
-#define ZT_PEER_SUGGESTED_SERIALIZATION_BUFFER_SIZE 1048576
+#define ZT_PEER_MAX_SERIALIZED_STATE_SIZE (sizeof(Peer) + 32 + (sizeof(Path) * 2))
namespace ZeroTier {
/**
* Peer on P2P Network (virtual layer 1)
*/
-class Peer : NonCopyable
+class Peer
{
friend class SharedPtr<Peer>;
@@ -74,26 +78,14 @@ public:
Peer(const RuntimeEnvironment *renv,const Identity &myIdentity,const Identity &peerIdentity);
/**
- * @return Time peer record was last used in any way
- */
- inline uint64_t lastUsed() const throw() { return _lastUsed; }
-
- /**
- * Log a use of this peer record (done by Topology when peers are looked up)
- *
- * @param now New time of last use
- */
- inline void use(uint64_t now) throw() { _lastUsed = now; }
-
- /**
* @return This peer's ZT address (short for identity().address())
*/
- inline const Address &address() const throw() { return _id.address(); }
+ inline const Address &address() const { return _id.address(); }
/**
* @return This peer's identity
*/
- inline const Identity &identity() const throw() { return _id; }
+ inline const Identity &identity() const { return _id; }
/**
* Log receipt of an authenticated packet
@@ -101,154 +93,190 @@ public:
* This is called by the decode pipe when a packet is proven to be authentic
* and appears to be valid.
*
- * @param RR Runtime environment
- * @param localAddr Local address
- * @param remoteAddr Internet address of sender
+ * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
+ * @param path Path over which packet was received
* @param hops ZeroTier (not IP) hops
* @param packetId Packet ID
* @param verb Packet verb
* @param inRePacketId Packet ID in reply to (default: none)
* @param inReVerb Verb in reply to (for OK/ERROR, default: VERB_NOP)
+ * @param trustEstablished If true, some form of non-trivial trust (like allowed in network) has been established
+ * @param networkId Network ID if this pertains to a network, or 0 otherwise
*/
void received(
- const InetAddress &localAddr,
- const InetAddress &remoteAddr,
- unsigned int hops,
- uint64_t packetId,
- Packet::Verb verb,
- uint64_t inRePacketId = 0,
- Packet::Verb inReVerb = Packet::VERB_NOP);
-
- /**
- * Get the current best direct path to this peer
+ void *tPtr,
+ const SharedPtr<Path> &path,
+ const unsigned int hops,
+ const uint64_t packetId,
+ const Packet::Verb verb,
+ const uint64_t inRePacketId,
+ const Packet::Verb inReVerb,
+ const bool trustEstablished,
+ const uint64_t networkId);
+
+ /**
+ * Check whether we have an active path to this peer via the given address
*
* @param now Current time
- * @return Best path or NULL if there are no active direct paths
- */
- inline Path *getBestPath(uint64_t now) { return _getBestPath(now); }
-
- /**
- * @param now Current time
* @param addr Remote address
* @return True if we have an active path to this destination
*/
- inline bool hasActivePathTo(uint64_t now,const InetAddress &addr) const
+ inline bool hasActivePathTo(int64_t now,const InetAddress &addr) const
{
- for(unsigned int p=0;p<_numPaths;++p) {
- if ((_paths[p].active(now))&&(_paths[p].address() == addr))
- return true;
+ Mutex::Lock _l(_paths_m);
+ for(unsigned int i=0;i<ZT_MAX_PEER_NETWORK_PATHS;++i) {
+ if (_paths[i].p) {
+ if (((now - _paths[i].lr) < ZT_PEER_PATH_EXPIRATION)&&(_paths[i].p->address() == addr))
+ return true;
+ } else break;
}
return false;
}
/**
- * Set all paths in the same ss_family that are not this one to cluster suboptimal
- *
- * Addresses in other families are not affected.
+ * Send via best direct path
*
- * @param addr Address to make exclusive
+ * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
+ * @param data Packet data
+ * @param len Packet length
+ * @param now Current time
+ * @param force If true, send even if path is not alive
+ * @return True if we actually sent something
*/
- inline void setClusterOptimalPathForAddressFamily(const InetAddress &addr)
+ inline bool sendDirect(void *tPtr,const void *data,unsigned int len,int64_t now,bool force)
{
- for(unsigned int p=0;p<_numPaths;++p) {
- if (_paths[p].address().ss_family == addr.ss_family) {
- _paths[p].setClusterSuboptimal(_paths[p].address() != addr);
- }
- }
+ SharedPtr<Path> bp(getBestPath(now,force));
+ if (bp)
+ return bp->send(RR,tPtr,data,len,now);
+ return false;
}
/**
- * Send via best path
+ * Get the best current direct path
*
- * @param data Packet data
- * @param len Packet length
* @param now Current time
- * @return Path used on success or NULL on failure
+ * @param includeExpired If true, include even expired paths
+ * @return Best current path or NULL if none
*/
- inline Path *send(const void *data,unsigned int len,uint64_t now)
- {
- Path *const bestPath = getBestPath(now);
- if (bestPath) {
- if (bestPath->send(RR,data,len,now))
- return bestPath;
- }
- return (Path *)0;
- }
+ SharedPtr<Path> getBestPath(int64_t now,bool includeExpired) const;
+
+ /**
+ * Send VERB_RENDEZVOUS to this and another peer via the best common IP scope and path
+ */
+ void introduce(void *const tPtr,const int64_t now,const SharedPtr<Peer> &other) const;
/**
* Send a HELLO to this peer at a specified physical address
*
- * This does not update any statistics. It's used to send initial HELLOs
- * for NAT traversal and path verification.
+ * No statistics or sent times are updated here.
*
- * @param localAddr Local address
+ * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
+ * @param localSocket Local source socket
* @param atAddress Destination address
* @param now Current time
- * @param ttl Desired IP TTL (default: 0 to leave alone)
*/
- void sendHELLO(const InetAddress &localAddr,const InetAddress &atAddress,uint64_t now,unsigned int ttl = 0);
+ void sendHELLO(void *tPtr,const int64_t localSocket,const InetAddress &atAddress,int64_t now);
/**
- * Send pings or keepalives depending on configured timeouts
+ * Send ECHO (or HELLO for older peers) to this peer at the given address
*
+ * No statistics or sent times are updated here.
+ *
+ * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
+ * @param localSocket Local source socket
+ * @param atAddress Destination address
* @param now Current time
- * @param inetAddressFamily Keep this address family alive, or 0 to simply pick current best ignoring family
- * @return True if at least one direct path seems alive
+ * @param sendFullHello If true, always send a full HELLO instead of just an ECHO
*/
- bool doPingAndKeepalive(uint64_t now,int inetAddressFamily);
+ void attemptToContactAt(void *tPtr,const int64_t localSocket,const InetAddress &atAddress,int64_t now,bool sendFullHello);
/**
- * Push direct paths back to self if we haven't done so in the configured timeout
+ * Try a memorized or statically defined path if any are known
+ *
+ * Under the hood this is done periodically based on ZT_TRY_MEMORIZED_PATH_INTERVAL.
*
- * @param localAddr Local address
- * @param toAddress Remote address to send push to (usually from path)
+ * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
* @param now Current time
- * @param force If true, push regardless of rate limit
- * @param includePrivatePaths If true, include local interface address paths (should only be done to peers with a trust relationship)
- * @return True if something was actually sent
*/
- bool pushDirectPaths(const InetAddress &localAddr,const InetAddress &toAddress,uint64_t now,bool force,bool includePrivatePaths);
+ void tryMemorizedPath(void *tPtr,int64_t now);
/**
- * @return All known direct paths to this peer (active or inactive)
+ * Send pings or keepalives depending on configured timeouts
+ *
+ * This also cleans up some internal data structures. It's called periodically from Node.
+ *
+ * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
+ * @param now Current time
+ * @param inetAddressFamily Keep this address family alive, or -1 for any
+ * @return 0 if nothing sent or bit mask: bit 0x1 if IPv4 sent, bit 0x2 if IPv6 sent (0x3 means both sent)
*/
- inline std::vector<Path> paths() const
- {
- std::vector<Path> pp;
- for(unsigned int p=0,np=_numPaths;p<np;++p)
- pp.push_back(_paths[p]);
- return pp;
- }
+ unsigned int doPingAndKeepalive(void *tPtr,int64_t now);
/**
- * @return Time of last receive of anything, whether direct or relayed
+ * Process a cluster redirect sent by this peer
+ *
+ * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
+ * @param originatingPath Path from which redirect originated
+ * @param remoteAddress Remote address
+ * @param now Current time
*/
- inline uint64_t lastReceive() const throw() { return _lastReceive; }
+ void clusterRedirect(void *tPtr,const SharedPtr<Path> &originatingPath,const InetAddress &remoteAddress,const int64_t now);
/**
- * @return Time of most recent unicast frame received
+ * Reset paths within a given IP scope and address family
+ *
+ * Resetting a path involves sending an ECHO to it and then deactivating
+ * it until or unless it responds. This is done when we detect a change
+ * to our external IP or another system change that might invalidate
+ * many or all current paths.
+ *
+ * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
+ * @param scope IP scope
+ * @param inetAddressFamily Family e.g. AF_INET
+ * @param now Current time
*/
- inline uint64_t lastUnicastFrame() const throw() { return _lastUnicastFrame; }
+ void resetWithinScope(void *tPtr,InetAddress::IpScope scope,int inetAddressFamily,int64_t now);
/**
- * @return Time of most recent multicast frame received
+ * @param now Current time
+ * @return All known paths to this peer
*/
- inline uint64_t lastMulticastFrame() const throw() { return _lastMulticastFrame; }
+ inline std::vector< SharedPtr<Path> > paths(const int64_t now) const
+ {
+ std::vector< SharedPtr<Path> > pp;
+ Mutex::Lock _l(_paths_m);
+ for(unsigned int i=0;i<ZT_MAX_PEER_NETWORK_PATHS;++i) {
+ if (!_paths[i].p) break;
+ pp.push_back(_paths[i].p);
+ }
+ return pp;
+ }
/**
- * @return Time of most recent frame of any kind (unicast or multicast)
+ * @return Time of last receive of anything, whether direct or relayed
*/
- inline uint64_t lastFrame() const throw() { return std::max(_lastUnicastFrame,_lastMulticastFrame); }
+ inline int64_t lastReceive() const { return _lastReceive; }
+
+ /**
+ * @return True if we've heard from this peer in less than ZT_PEER_ACTIVITY_TIMEOUT
+ */
+ inline bool isAlive(const int64_t now) const { return ((now - _lastReceive) < ZT_PEER_ACTIVITY_TIMEOUT); }
/**
* @return True if this peer has sent us real network traffic recently
*/
- inline uint64_t activelyTransferringFrames(uint64_t now) const throw() { return ((now - lastFrame()) < ZT_PEER_ACTIVITY_TIMEOUT); }
+ inline int64_t isActive(int64_t now) const { return ((now - _lastNontrivialReceive) < ZT_PEER_ACTIVITY_TIMEOUT); }
/**
- * @return Latency in milliseconds or 0 if unknown
+ * @return Latency in milliseconds of best path or 0xffff if unknown / no paths
*/
- inline unsigned int latency() const { return _latency; }
+ inline unsigned int latency(const int64_t now) const
+ {
+ SharedPtr<Path> bp(getBestPath(now,false));
+ if (bp)
+ return bp->latency();
+ return 0xffff;
+ }
/**
* This computes a quality score for relays and root servers
@@ -261,71 +289,21 @@ public:
*
* @return Relay quality score computed from latency and other factors, lower is better
*/
- inline unsigned int relayQuality(const uint64_t now) const
+ inline unsigned int relayQuality(const int64_t now) const
{
const uint64_t tsr = now - _lastReceive;
if (tsr >= ZT_PEER_ACTIVITY_TIMEOUT)
return (~(unsigned int)0);
- unsigned int l = _latency;
+ unsigned int l = latency(now);
if (!l)
l = 0xffff;
- return (l * (((unsigned int)tsr / (ZT_PEER_DIRECT_PING_DELAY + 1000)) + 1));
+ return (l * (((unsigned int)tsr / (ZT_PEER_PING_PERIOD + 1000)) + 1));
}
/**
- * Update latency with a new direct measurment
- *
- * @param l Direct latency measurment in ms
- */
- inline void addDirectLatencyMeasurment(unsigned int l)
- {
- unsigned int ol = _latency;
- if ((ol > 0)&&(ol < 10000))
- _latency = (ol + std::min(l,(unsigned int)65535)) / 2;
- else _latency = std::min(l,(unsigned int)65535);
- }
-
- /**
- * @param now Current time
- * @return True if this peer has at least one active direct path
- */
- inline bool hasActiveDirectPath(uint64_t now) const
- {
- for(unsigned int p=0;p<_numPaths;++p) {
- if (_paths[p].active(now))
- return true;
- }
- return false;
- }
-
-#ifdef ZT_ENABLE_CLUSTER
- /**
- * @param now Current time
- * @return True if this peer has at least one active direct path that is not cluster-suboptimal
- */
- inline bool hasClusterOptimalPath(uint64_t now) const
- {
- for(unsigned int p=0,np=_numPaths;p<np;++p) {
- if ((_paths[p].active(now))&&(!_paths[p].isClusterSuboptimal()))
- return true;
- }
- return false;
- }
-#endif
-
- /**
- * Reset paths within a given scope
- *
- * @param scope IP scope of paths to reset
- * @param now Current time
- * @return True if at least one path was forgotten
- */
- bool resetWithinScope(InetAddress::IpScope scope,uint64_t now);
-
- /**
* @return 256-bit secret symmetric encryption key
*/
- inline const unsigned char *key() const throw() { return _key; }
+ inline const unsigned char *key() const { return _key; }
/**
* Set the currently known remote version of this peer's client
@@ -343,259 +321,213 @@ public:
_vRevision = (uint16_t)vrev;
}
- inline unsigned int remoteVersionProtocol() const throw() { return _vProto; }
- inline unsigned int remoteVersionMajor() const throw() { return _vMajor; }
- inline unsigned int remoteVersionMinor() const throw() { return _vMinor; }
- inline unsigned int remoteVersionRevision() const throw() { return _vRevision; }
+ inline unsigned int remoteVersionProtocol() const { return _vProto; }
+ inline unsigned int remoteVersionMajor() const { return _vMajor; }
+ inline unsigned int remoteVersionMinor() const { return _vMinor; }
+ inline unsigned int remoteVersionRevision() const { return _vRevision; }
- inline bool remoteVersionKnown() const throw() { return ((_vMajor > 0)||(_vMinor > 0)||(_vRevision > 0)); }
+ inline bool remoteVersionKnown() const { return ((_vMajor > 0)||(_vMinor > 0)||(_vRevision > 0)); }
/**
- * Get most recently active path addresses for IPv4 and/or IPv6
- *
- * Note that v4 and v6 are not modified if they are not found, so
- * initialize these to a NULL address to be able to check.
- *
- * @param now Current time
- * @param v4 Result parameter to receive active IPv4 address, if any
- * @param v6 Result parameter to receive active IPv6 address, if any
+ * @return True if peer has received a trust established packet (e.g. common network membership) in the past ZT_TRUST_EXPIRATION ms
*/
- void getBestActiveAddresses(uint64_t now,InetAddress &v4,InetAddress &v6) const;
+ inline bool trustEstablished(const int64_t now) const { return ((now - _lastTrustEstablishedPacketReceived) < ZT_TRUST_EXPIRATION); }
/**
- * Check network COM agreement with this peer
- *
- * @param nwid Network ID
- * @param com Another certificate of membership
- * @return True if supplied COM agrees with ours, false if not or if we don't have one
+ * Rate limit gate for VERB_PUSH_DIRECT_PATHS
*/
- bool networkMembershipCertificatesAgree(uint64_t nwid,const CertificateOfMembership &com) const;
+ inline bool rateGatePushDirectPaths(const int64_t now)
+ {
+ if ((now - _lastDirectPathPushReceive) <= ZT_PUSH_DIRECT_PATHS_CUTOFF_TIME)
+ ++_directPathPushCutoffCount;
+ else _directPathPushCutoffCount = 0;
+ _lastDirectPathPushReceive = now;
+ return (_directPathPushCutoffCount < ZT_PUSH_DIRECT_PATHS_CUTOFF_LIMIT);
+ }
/**
- * Check the validity of the COM and add/update if valid and new
- *
- * @param nwid Network ID
- * @param com Externally supplied COM
+ * Rate limit gate for VERB_NETWORK_CREDENTIALS
*/
- bool validateAndSetNetworkMembershipCertificate(uint64_t nwid,const CertificateOfMembership &com);
+ inline bool rateGateCredentialsReceived(const int64_t now)
+ {
+ if ((now - _lastCredentialsReceived) <= ZT_PEER_CREDENTIALS_CUTOFF_TIME)
+ ++_credentialsCutoffCount;
+ else _credentialsCutoffCount = 0;
+ _lastCredentialsReceived = now;
+ return (_directPathPushCutoffCount < ZT_PEER_CREDEITIALS_CUTOFF_LIMIT);
+ }
/**
- * @param nwid Network ID
- * @param now Current time
- * @param updateLastPushedTime If true, go ahead and update the last pushed time regardless of return value
- * @return Whether or not this peer needs another COM push from us
+ * Rate limit gate for sending of ERROR_NEED_MEMBERSHIP_CERTIFICATE
*/
- bool needsOurNetworkMembershipCertificate(uint64_t nwid,uint64_t now,bool updateLastPushedTime);
+ inline bool rateGateRequestCredentials(const int64_t now)
+ {
+ if ((now - _lastCredentialRequestSent) >= ZT_PEER_GENERAL_RATE_LIMIT) {
+ _lastCredentialRequestSent = now;
+ return true;
+ }
+ return false;
+ }
/**
- * Perform periodic cleaning operations
- *
- * @param now Current time
+ * Rate limit gate for inbound WHOIS requests
*/
- void clean(uint64_t now);
+ inline bool rateGateInboundWhoisRequest(const int64_t now)
+ {
+ if ((now - _lastWhoisRequestReceived) >= ZT_PEER_WHOIS_RATE_LIMIT) {
+ _lastWhoisRequestReceived = now;
+ return true;
+ }
+ return false;
+ }
/**
- * Update direct path push stats and return true if we should respond
- *
- * This is a circuit breaker to make VERB_PUSH_DIRECT_PATHS not particularly
- * useful as a DDOS amplification attack vector. Otherwise a malicious peer
- * could send loads of these and cause others to bombard arbitrary IPs with
- * traffic.
- *
- * @param now Current time
- * @return True if we should respond
+ * Rate limit gate for inbound ECHO requests
*/
- inline bool shouldRespondToDirectPathPush(const uint64_t now)
+ inline bool rateGateEchoRequest(const int64_t now)
{
- if ((now - _lastDirectPathPushReceive) <= ZT_PUSH_DIRECT_PATHS_CUTOFF_TIME)
- ++_directPathPushCutoffCount;
- else _directPathPushCutoffCount = 0;
- _lastDirectPathPushReceive = now;
- return (_directPathPushCutoffCount < ZT_PUSH_DIRECT_PATHS_CUTOFF_LIMIT);
+ if ((now - _lastEchoRequestReceived) >= ZT_PEER_GENERAL_RATE_LIMIT) {
+ _lastEchoRequestReceived = now;
+ return true;
+ }
+ return false;
}
/**
- * 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)
+ * Rate gate incoming requests for network COM
*/
- static inline std::pair<InetAddress,InetAddress> findCommonGround(const Peer &a,const Peer &b,uint64_t now)
+ inline bool rateGateIncomingComRequest(const int64_t now)
{
- std::pair<InetAddress,InetAddress> v4,v6;
- b.getBestActiveAddresses(now,v4.first,v6.first);
- a.getBestActiveAddresses(now,v4.second,v6.second);
- if ((v6.first)&&(v6.second)) // prefer IPv6 if both have it since NAT-t is (almost) unnecessary
- return v6;
- else if ((v4.first)&&(v4.second))
- return v4;
- else return std::pair<InetAddress,InetAddress>();
+ if ((now - _lastComRequestReceived) >= ZT_PEER_GENERAL_RATE_LIMIT) {
+ _lastComRequestReceived = now;
+ return true;
+ }
+ return false;
}
- template<unsigned int C>
- inline void serialize(Buffer<C> &b) const
+ /**
+ * Rate gate outgoing requests for network COM
+ */
+ inline bool rateGateOutgoingComRequest(const int64_t now)
{
- Mutex::Lock _l(_networkComs_m);
-
- const unsigned int recSizePos = b.size();
- b.addSize(4); // space for uint32_t field length
+ if ((now - _lastComRequestSent) >= ZT_PEER_GENERAL_RATE_LIMIT) {
+ _lastComRequestSent = now;
+ return true;
+ }
+ return false;
+ }
- b.append((uint16_t)1); // version of serialized Peer data
+ /**
+ * Serialize a peer for storage in local cache
+ *
+ * This does not serialize everything, just non-ephemeral information.
+ */
+ template<unsigned int C>
+ inline void serializeForCache(Buffer<C> &b) const
+ {
+ b.append((uint8_t)1);
- _id.serialize(b,false);
+ _id.serialize(b);
- b.append((uint64_t)_lastUsed);
- b.append((uint64_t)_lastReceive);
- b.append((uint64_t)_lastUnicastFrame);
- b.append((uint64_t)_lastMulticastFrame);
- b.append((uint64_t)_lastAnnouncedTo);
- b.append((uint64_t)_lastDirectPathPushSent);
- b.append((uint64_t)_lastDirectPathPushReceive);
- b.append((uint64_t)_lastPathSort);
b.append((uint16_t)_vProto);
b.append((uint16_t)_vMajor);
b.append((uint16_t)_vMinor);
b.append((uint16_t)_vRevision);
- b.append((uint32_t)_latency);
- b.append((uint16_t)_directPathPushCutoffCount);
-
- b.append((uint16_t)_numPaths);
- for(unsigned int i=0;i<_numPaths;++i)
- _paths[i].serialize(b);
-
- b.append((uint32_t)_networkComs.size());
- {
- uint64_t *k = (uint64_t *)0;
- _NetworkCom *v = (_NetworkCom *)0;
- Hashtable<uint64_t,_NetworkCom>::Iterator i(const_cast<Peer *>(this)->_networkComs);
- while (i.next(k,v)) {
- b.append((uint64_t)*k);
- b.append((uint64_t)v->ts);
- v->com.serialize(b);
- }
- }
- b.append((uint32_t)_lastPushedComs.size());
{
- uint64_t *k = (uint64_t *)0;
- uint64_t *v = (uint64_t *)0;
- Hashtable<uint64_t,uint64_t>::Iterator i(const_cast<Peer *>(this)->_lastPushedComs);
- while (i.next(k,v)) {
- b.append((uint64_t)*k);
- b.append((uint64_t)*v);
+ Mutex::Lock _l(_paths_m);
+ unsigned int pc = 0;
+ for(unsigned int i=0;i<ZT_MAX_PEER_NETWORK_PATHS;++i) {
+ if (_paths[i].p)
+ ++pc;
+ else break;
}
+ b.append((uint16_t)pc);
+ for(unsigned int i=0;i<pc;++i)
+ _paths[i].p->address().serialize(b);
}
-
- b.template setAt<uint32_t>(recSizePos,(uint32_t)(b.size() - (recSizePos + 4))); // set size
}
- /**
- * Create a new Peer from a serialized instance
- *
- * @param renv Runtime environment
- * @param myIdentity This node's identity
- * @param b Buffer containing serialized Peer data
- * @param p Pointer to current position in buffer, will be updated in place as buffer is read (value/result)
- * @return New instance of Peer or NULL if serialized data was corrupt or otherwise invalid (may also throw an exception via Buffer)
- */
template<unsigned int C>
- static inline SharedPtr<Peer> deserializeNew(const RuntimeEnvironment *renv,const Identity &myIdentity,const Buffer<C> &b,unsigned int &p)
+ inline static SharedPtr<Peer> deserializeFromCache(int64_t now,void *tPtr,Buffer<C> &b,const RuntimeEnvironment *renv)
{
- const unsigned int recSize = b.template at<uint32_t>(p); p += 4;
- if ((p + recSize) > b.size())
- return SharedPtr<Peer>(); // size invalid
- if (b.template at<uint16_t>(p) != 1)
- return SharedPtr<Peer>(); // version mismatch
- p += 2;
-
- Identity npid;
- p += npid.deserialize(b,p);
- if (!npid)
- return SharedPtr<Peer>();
-
- SharedPtr<Peer> np(new Peer(renv,myIdentity,npid));
-
- np->_lastUsed = b.template at<uint64_t>(p); p += 8;
- np->_lastReceive = b.template at<uint64_t>(p); p += 8;
- np->_lastUnicastFrame = b.template at<uint64_t>(p); p += 8;
- np->_lastMulticastFrame = b.template at<uint64_t>(p); p += 8;
- np->_lastAnnouncedTo = b.template at<uint64_t>(p); p += 8;
- np->_lastDirectPathPushSent = b.template at<uint64_t>(p); p += 8;
- np->_lastDirectPathPushReceive = b.template at<uint64_t>(p); p += 8;
- np->_lastPathSort = b.template at<uint64_t>(p); p += 8;
- np->_vProto = b.template at<uint16_t>(p); p += 2;
- np->_vMajor = b.template at<uint16_t>(p); p += 2;
- np->_vMinor = b.template at<uint16_t>(p); p += 2;
- np->_vRevision = b.template at<uint16_t>(p); p += 2;
- np->_latency = b.template at<uint32_t>(p); p += 4;
- np->_directPathPushCutoffCount = b.template at<uint16_t>(p); p += 2;
-
- const unsigned int numPaths = b.template at<uint16_t>(p); p += 2;
- for(unsigned int i=0;i<numPaths;++i) {
- if (i < ZT_MAX_PEER_NETWORK_PATHS) {
- p += np->_paths[np->_numPaths++].deserialize(b,p);
- } else {
- // Skip any paths beyond max, but still read stream
- Path foo;
- p += foo.deserialize(b,p);
+ try {
+ unsigned int ptr = 0;
+ if (b[ptr++] != 1)
+ return SharedPtr<Peer>();
+
+ Identity id;
+ ptr += id.deserialize(b,ptr);
+ if (!id)
+ return SharedPtr<Peer>();
+
+ SharedPtr<Peer> p(new Peer(renv,renv->identity,id));
+
+ p->_vProto = b.template at<uint16_t>(ptr); ptr += 2;
+ p->_vMajor = b.template at<uint16_t>(ptr); ptr += 2;
+ p->_vMinor = b.template at<uint16_t>(ptr); ptr += 2;
+ p->_vRevision = b.template at<uint16_t>(ptr); ptr += 2;
+
+ // When we deserialize from the cache we don't actually restore paths. We
+ // just try them and then re-learn them if they happen to still be up.
+ // Paths are fairly ephemeral in the real world in most cases.
+ const unsigned int tryPathCount = b.template at<uint16_t>(ptr); ptr += 2;
+ for(unsigned int i=0;i<tryPathCount;++i) {
+ InetAddress inaddr;
+ try {
+ ptr += inaddr.deserialize(b,ptr);
+ if (inaddr)
+ p->attemptToContactAt(tPtr,-1,inaddr,now,true);
+ } catch ( ... ) {
+ break;
+ }
}
- }
-
- const unsigned int numNetworkComs = b.template at<uint32_t>(p); p += 4;
- for(unsigned int i=0;i<numNetworkComs;++i) {
- _NetworkCom &c = np->_networkComs[b.template at<uint64_t>(p)]; p += 8;
- c.ts = b.template at<uint64_t>(p); p += 8;
- p += c.com.deserialize(b,p);
- }
- const unsigned int numLastPushed = b.template at<uint32_t>(p); p += 4;
- for(unsigned int i=0;i<numLastPushed;++i) {
- const uint64_t nwid = b.template at<uint64_t>(p); p += 8;
- const uint64_t ts = b.template at<uint64_t>(p); p += 8;
- np->_lastPushedComs.set(nwid,ts);
+ return p;
+ } catch ( ... ) {
+ return SharedPtr<Peer>();
}
-
- return np;
}
private:
- void _doDeadPathDetection(Path &p,const uint64_t now);
- Path *_getBestPath(const uint64_t now);
- Path *_getBestPath(const uint64_t now,int inetAddressFamily);
+ struct _PeerPath
+ {
+ _PeerPath() : lr(0),p(),priority(1) {}
+ int64_t lr; // time of last valid ZeroTier packet
+ SharedPtr<Path> p;
+ long priority; // >= 1, higher is better
+ };
- unsigned char _key[ZT_PEER_SECRET_KEY_LENGTH]; // computed with key agreement, not serialized
+ uint8_t _key[ZT_PEER_SECRET_KEY_LENGTH];
const RuntimeEnvironment *RR;
- uint64_t _lastUsed;
- uint64_t _lastReceive; // direct or indirect
- uint64_t _lastUnicastFrame;
- uint64_t _lastMulticastFrame;
- uint64_t _lastAnnouncedTo;
- uint64_t _lastDirectPathPushSent;
- uint64_t _lastDirectPathPushReceive;
- uint64_t _lastPathSort;
+
+ int64_t _lastReceive; // direct or indirect
+ int64_t _lastNontrivialReceive; // frames, things like netconf, etc.
+ int64_t _lastTriedMemorizedPath;
+ int64_t _lastDirectPathPushSent;
+ int64_t _lastDirectPathPushReceive;
+ int64_t _lastCredentialRequestSent;
+ int64_t _lastWhoisRequestReceived;
+ int64_t _lastEchoRequestReceived;
+ int64_t _lastComRequestReceived;
+ int64_t _lastComRequestSent;
+ int64_t _lastCredentialsReceived;
+ int64_t _lastTrustEstablishedPacketReceived;
+ int64_t _lastSentFullHello;
+
uint16_t _vProto;
uint16_t _vMajor;
uint16_t _vMinor;
uint16_t _vRevision;
+
+ _PeerPath _paths[ZT_MAX_PEER_NETWORK_PATHS];
+ Mutex _paths_m;
+
Identity _id;
- Path _paths[ZT_MAX_PEER_NETWORK_PATHS];
- unsigned int _numPaths;
- unsigned int _latency;
- unsigned int _directPathPushCutoffCount;
- struct _NetworkCom
- {
- _NetworkCom() {}
- _NetworkCom(uint64_t t,const CertificateOfMembership &c) : ts(t),com(c) {}
- uint64_t ts;
- CertificateOfMembership com;
- };
- Hashtable<uint64_t,_NetworkCom> _networkComs;
- Hashtable<uint64_t,uint64_t> _lastPushedComs;
- Mutex _networkComs_m;
+ unsigned int _directPathPushCutoffCount;
+ unsigned int _credentialsCutoffCount;
AtomicCounter __refCount;
};
diff --git a/node/Poly1305.cpp b/node/Poly1305.cpp
index b78071f6..eceb57b3 100644
--- a/node/Poly1305.cpp
+++ b/node/Poly1305.cpp
@@ -121,7 +121,6 @@ static inline int crypto_onetimeauth(unsigned char *out,const unsigned char *in,
}
void Poly1305::compute(void *auth,const void *data,unsigned int len,const void *key)
- throw()
{
crypto_onetimeauth((unsigned char *)auth,(const unsigned char *)data,len,(const unsigned char *)key);
}
@@ -135,7 +134,7 @@ typedef struct poly1305_context {
unsigned char opaque[136];
} poly1305_context;
-#if (defined(_MSC_VER) || defined(__GNUC__)) && (defined(__amd64) || defined(__amd64__) || defined(__x86_64) || defined(__x86_64__) || defined(__AMD64) || defined(__AMD64__))
+#if (defined(_MSC_VER) || defined(__GNUC__)) && (defined(__amd64) || defined(__amd64__) || defined(__x86_64) || defined(__x86_64__) || defined(__AMD64) || defined(__AMD64__) || defined(_M_X64))
//////////////////////////////////////////////////////////////////////////////
// 128-bit implementation for MSC and GCC from Poly1305-donna
@@ -183,9 +182,9 @@ typedef struct poly1305_state_internal_t {
unsigned char final;
} poly1305_state_internal_t;
-/* interpret eight 8 bit unsigned integers as a 64 bit unsigned integer in little endian */
-static inline unsigned long long
-U8TO64(const unsigned char *p) {
+#if defined(ZT_NO_TYPE_PUNNING) || (__BYTE_ORDER != __LITTLE_ENDIAN)
+static inline unsigned long long U8TO64(const unsigned char *p)
+{
return
(((unsigned long long)(p[0] & 0xff) ) |
((unsigned long long)(p[1] & 0xff) << 8) |
@@ -196,10 +195,13 @@ U8TO64(const unsigned char *p) {
((unsigned long long)(p[6] & 0xff) << 48) |
((unsigned long long)(p[7] & 0xff) << 56));
}
+#else
+#define U8TO64(p) (*reinterpret_cast<const unsigned long long *>(p))
+#endif
-/* store a 64 bit unsigned integer as eight 8 bit unsigned integers in little endian */
-static inline void
-U64TO8(unsigned char *p, unsigned long long v) {
+#if defined(ZT_NO_TYPE_PUNNING) || (__BYTE_ORDER != __LITTLE_ENDIAN)
+static inline void U64TO8(unsigned char *p, unsigned long long v)
+{
p[0] = (v ) & 0xff;
p[1] = (v >> 8) & 0xff;
p[2] = (v >> 16) & 0xff;
@@ -209,6 +211,9 @@ U64TO8(unsigned char *p, unsigned long long v) {
p[6] = (v >> 48) & 0xff;
p[7] = (v >> 56) & 0xff;
}
+#else
+#define U64TO8(p,v) ((*reinterpret_cast<unsigned long long *>(p)) = (v))
+#endif
static inline void
poly1305_init(poly1305_context *ctx, const unsigned char key[32]) {
@@ -617,7 +622,6 @@ poly1305_update(poly1305_context *ctx, const unsigned char *m, size_t bytes) {
} // anonymous namespace
void Poly1305::compute(void *auth,const void *data,unsigned int len,const void *key)
- throw()
{
poly1305_context ctx;
poly1305_init(&ctx,reinterpret_cast<const unsigned char *>(key));
diff --git a/node/Poly1305.hpp b/node/Poly1305.hpp
index 62d57546..adcc2410 100644
--- a/node/Poly1305.hpp
+++ b/node/Poly1305.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_POLY1305_HPP
@@ -46,8 +54,7 @@ public:
* @param len Length of data to authenticate in bytes
* @param key 32-byte one-time use key to authenticate data (must not be reused)
*/
- static void compute(void *auth,const void *data,unsigned int len,const void *key)
- throw();
+ static void compute(void *auth,const void *data,unsigned int len,const void *key);
};
} // namespace ZeroTier
diff --git a/node/README.md b/node/README.md
index 01378c75..1728400c 100644
--- a/node/README.md
+++ b/node/README.md
@@ -1,4 +1,4 @@
-ZeroTier Virtual Switch Core
+ZeroTier Network Hypervisor Core
======
This directory contains the *real* ZeroTier: a completely OS-independent global virtual Ethernet switch engine. This is where the magic happens.
diff --git a/node/Revocation.cpp b/node/Revocation.cpp
new file mode 100644
index 00000000..78098f8c
--- /dev/null
+++ b/node/Revocation.cpp
@@ -0,0 +1,55 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
+ */
+
+#include "Revocation.hpp"
+#include "RuntimeEnvironment.hpp"
+#include "Identity.hpp"
+#include "Topology.hpp"
+#include "Switch.hpp"
+#include "Network.hpp"
+#include "Node.hpp"
+
+namespace ZeroTier {
+
+int Revocation::verify(const RuntimeEnvironment *RR,void *tPtr) const
+{
+ if ((!_signedBy)||(_signedBy != Network::controllerFor(_networkId)))
+ return -1;
+ const Identity id(RR->topology->getIdentity(tPtr,_signedBy));
+ if (!id) {
+ RR->sw->requestWhois(tPtr,RR->node->now(),_signedBy);
+ return 1;
+ }
+ try {
+ Buffer<sizeof(Revocation) + 64> tmp;
+ this->serialize(tmp,true);
+ return (id.verify(tmp.data(),tmp.size(),_signature) ? 0 : -1);
+ } catch ( ... ) {
+ return -1;
+ }
+}
+
+} // namespace ZeroTier
diff --git a/node/Revocation.hpp b/node/Revocation.hpp
new file mode 100644
index 00000000..eaf01915
--- /dev/null
+++ b/node/Revocation.hpp
@@ -0,0 +1,197 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
+ */
+
+#ifndef ZT_REVOCATION_HPP
+#define ZT_REVOCATION_HPP
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+
+#include "Constants.hpp"
+#include "../include/ZeroTierOne.h"
+#include "Credential.hpp"
+#include "Address.hpp"
+#include "C25519.hpp"
+#include "Utils.hpp"
+#include "Buffer.hpp"
+#include "Identity.hpp"
+
+/**
+ * Flag: fast propagation via rumor mill algorithm
+ */
+#define ZT_REVOCATION_FLAG_FAST_PROPAGATE 0x1ULL
+
+namespace ZeroTier {
+
+class RuntimeEnvironment;
+
+/**
+ * Revocation certificate to instantaneously revoke a COM, capability, or tag
+ */
+class Revocation : public Credential
+{
+public:
+ static inline Credential::Type credentialType() { return Credential::CREDENTIAL_TYPE_REVOCATION; }
+
+ Revocation()
+ {
+ memset(this,0,sizeof(Revocation));
+ }
+
+ /**
+ * @param i ID (arbitrary for revocations, currently random)
+ * @param nwid Network ID
+ * @param cid Credential ID being revoked (0 for all or for COMs, which lack IDs)
+ * @param thr Revocation time threshold before which credentials will be revoked
+ * @param fl Flags
+ * @param tgt Target node whose credential(s) are being revoked
+ * @param ct Credential type being revoked
+ */
+ Revocation(const uint32_t i,const uint64_t nwid,const uint32_t cid,const uint64_t thr,const uint64_t fl,const Address &tgt,const Credential::Type ct) :
+ _id(i),
+ _credentialId(cid),
+ _networkId(nwid),
+ _threshold(thr),
+ _flags(fl),
+ _target(tgt),
+ _signedBy(),
+ _type(ct) {}
+
+ inline uint32_t id() const { return _id; }
+ inline uint32_t credentialId() const { return _credentialId; }
+ inline uint64_t networkId() const { return _networkId; }
+ inline int64_t threshold() const { return _threshold; }
+ inline const Address &target() const { return _target; }
+ inline const Address &signer() const { return _signedBy; }
+ inline Credential::Type type() const { return _type; }
+
+ inline bool fastPropagate() const { return ((_flags & ZT_REVOCATION_FLAG_FAST_PROPAGATE) != 0); }
+
+ /**
+ * @param signer Signing identity, must have private key
+ * @return True if signature was successful
+ */
+ inline bool sign(const Identity &signer)
+ {
+ if (signer.hasPrivate()) {
+ Buffer<sizeof(Revocation) + 64> tmp;
+ _signedBy = signer.address();
+ this->serialize(tmp,true);
+ _signature = signer.sign(tmp.data(),tmp.size());
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Verify this revocation's signature
+ *
+ * @param RR Runtime environment to provide for peer lookup, etc.
+ * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
+ * @return 0 == OK, 1 == waiting for WHOIS, -1 == BAD signature or chain
+ */
+ int verify(const RuntimeEnvironment *RR,void *tPtr) const;
+
+ template<unsigned int C>
+ inline void serialize(Buffer<C> &b,const bool forSign = false) const
+ {
+ if (forSign) b.append((uint64_t)0x7f7f7f7f7f7f7f7fULL);
+
+ b.append((uint32_t)0); // 4 unused bytes, currently set to 0
+ b.append(_id);
+ b.append(_networkId);
+ b.append((uint32_t)0); // 4 unused bytes, currently set to 0
+ b.append(_credentialId);
+ b.append(_threshold);
+ b.append(_flags);
+ _target.appendTo(b);
+ _signedBy.appendTo(b);
+ b.append((uint8_t)_type);
+
+ if (!forSign) {
+ b.append((uint8_t)1); // 1 == Ed25519 signature
+ b.append((uint16_t)ZT_C25519_SIGNATURE_LEN);
+ b.append(_signature.data,ZT_C25519_SIGNATURE_LEN);
+ }
+
+ // This is the size of any additional fields, currently 0.
+ b.append((uint16_t)0);
+
+ if (forSign) b.append((uint64_t)0x7f7f7f7f7f7f7f7fULL);
+ }
+
+ template<unsigned int C>
+ inline unsigned int deserialize(const Buffer<C> &b,unsigned int startAt = 0)
+ {
+ memset(this,0,sizeof(Revocation));
+
+ unsigned int p = startAt;
+
+ p += 4; // 4 bytes, currently unused
+ _id = b.template at<uint32_t>(p); p += 4;
+ _networkId = b.template at<uint64_t>(p); p += 8;
+ p += 4; // 4 bytes, currently unused
+ _credentialId = b.template at<uint32_t>(p); p += 4;
+ _threshold = b.template at<uint64_t>(p); p += 8;
+ _flags = b.template at<uint64_t>(p); p += 8;
+ _target.setTo(b.field(p,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH); p += ZT_ADDRESS_LENGTH;
+ _signedBy.setTo(b.field(p,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH); p += ZT_ADDRESS_LENGTH;
+ _type = (Credential::Type)b[p++];
+
+ if (b[p++] == 1) {
+ if (b.template at<uint16_t>(p) == ZT_C25519_SIGNATURE_LEN) {
+ p += 2;
+ ZT_FAST_MEMCPY(_signature.data,b.field(p,ZT_C25519_SIGNATURE_LEN),ZT_C25519_SIGNATURE_LEN);
+ p += ZT_C25519_SIGNATURE_LEN;
+ } else throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_INVALID_CRYPTOGRAPHIC_TOKEN;
+ } else {
+ p += 2 + b.template at<uint16_t>(p);
+ }
+
+ p += 2 + b.template at<uint16_t>(p);
+ if (p > b.size())
+ throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_OVERFLOW;
+
+ return (p - startAt);
+ }
+
+private:
+ uint32_t _id;
+ uint32_t _credentialId;
+ uint64_t _networkId;
+ int64_t _threshold;
+ uint64_t _flags;
+ Address _target;
+ Address _signedBy;
+ Credential::Type _type;
+ C25519::Signature _signature;
+};
+
+} // namespace ZeroTier
+
+#endif
diff --git a/node/RuntimeEnvironment.hpp b/node/RuntimeEnvironment.hpp
index 1f527733..46350b4a 100644
--- a/node/RuntimeEnvironment.hpp
+++ b/node/RuntimeEnvironment.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,16 +14,24 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_RUNTIMEENVIRONMENT_HPP
#define ZT_RUNTIMEENVIRONMENT_HPP
-#include <string>
+#include <string.h>
#include "Constants.hpp"
+#include "Utils.hpp"
#include "Identity.hpp"
-#include "Mutex.hpp"
namespace ZeroTier {
@@ -34,8 +42,7 @@ class Node;
class Multicaster;
class NetworkController;
class SelfAwareness;
-class Cluster;
-class DeferredPackets;
+class Trace;
/**
* Holds global state for an instance of ZeroTier::Node
@@ -45,52 +52,47 @@ class RuntimeEnvironment
public:
RuntimeEnvironment(Node *n) :
node(n)
- ,identity()
,localNetworkController((NetworkController *)0)
+ ,rtmem((void *)0)
,sw((Switch *)0)
,mc((Multicaster *)0)
,topology((Topology *)0)
,sa((SelfAwareness *)0)
- ,dp((DeferredPackets *)0)
-#ifdef ZT_ENABLE_CLUSTER
- ,cluster((Cluster *)0)
-#endif
- ,dpEnabled(0)
{
+ publicIdentityStr[0] = (char)0;
+ secretIdentityStr[0] = (char)0;
+ }
+
+ ~RuntimeEnvironment()
+ {
+ Utils::burn(secretIdentityStr,sizeof(secretIdentityStr));
}
// Node instance that owns this RuntimeEnvironment
Node *const node;
- // This node's identity
- Identity identity;
- std::string publicIdentityStr;
- std::string secretIdentityStr;
-
// This is set externally to an instance of this base class
NetworkController *localNetworkController;
- /*
- * Order matters a bit here. These are constructed in this order
+ // Memory actually occupied by Trace, Switch, etc.
+ void *rtmem;
+
+ /* Order matters a bit here. These are constructed in this order
* and then deleted in the opposite order on Node exit. The order ensures
* that things that are needed are there before they're needed.
*
- * These are constant and never null after startup unless indicated.
- */
+ * These are constant and never null after startup unless indicated. */
+ Trace *t;
Switch *sw;
Multicaster *mc;
Topology *topology;
SelfAwareness *sa;
- DeferredPackets *dp;
-#ifdef ZT_ENABLE_CLUSTER
- Cluster *cluster;
-#endif
-
- // This is set to >0 if background threads are waiting on deferred
- // packets, otherwise 'dp' should not be used.
- volatile int dpEnabled;
+ // This node's identity and string representations thereof
+ Identity identity;
+ char publicIdentityStr[ZT_IDENTITY_STRING_BUFFER_LENGTH];
+ char secretIdentityStr[ZT_IDENTITY_STRING_BUFFER_LENGTH];
};
} // namespace ZeroTier
diff --git a/node/SHA512.cpp b/node/SHA512.cpp
index 76737d37..d3c938af 100644
--- a/node/SHA512.cpp
+++ b/node/SHA512.cpp
@@ -1,20 +1,11 @@
+// Code taken from NaCl by D. J. Bernstein and others
+// Public domain
+
/*
- * ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
+20080913
+D. J. Bernstein
+Public domain.
+*/
#include <stdint.h>
#include <stdlib.h>
@@ -23,19 +14,37 @@
#include "SHA512.hpp"
#include "Utils.hpp"
+#ifdef __APPLE__
+#include <CommonCrypto/CommonDigest.h>
+#define ZT_HAVE_NATIVE_SHA512
namespace ZeroTier {
+void SHA512::hash(void *digest,const void *data,unsigned int len)
+{
+ CC_SHA512_CTX ctx;
+ CC_SHA512_Init(&ctx);
+ CC_SHA512_Update(&ctx,data,len);
+ CC_SHA512_Final(reinterpret_cast<unsigned char *>(digest),&ctx);
+}
+}
+#endif
-//////////////////////////////////////////////////////////////////////////////
-//////////////////////////////////////////////////////////////////////////////
+#ifdef ZT_USE_LIBCRYPTO
+#include <openssl/sha.h>
+#define ZT_HAVE_NATIVE_SHA512
+namespace ZeroTier {
+void SHA512::hash(void *digest,const void *data,unsigned int len)
+{
+ SHA512_CTX ctx;
+ SHA512_Init(&ctx);
+ SHA512_Update(&ctx,data,len);
+ SHA512_Final(reinterpret_cast<unsigned char *>(digest),&ctx);
+}
+}
+#endif
-// Code taken from NaCl by D. J. Bernstein and others
-// Public domain
+#ifndef ZT_HAVE_NATIVE_SHA512
-/*
-20080913
-D. J. Bernstein
-Public domain.
-*/
+namespace ZeroTier {
#define uint64 uint64_t
@@ -43,28 +52,28 @@ Public domain.
static uint64 load_bigendian(const unsigned char *x)
{
- return
- (uint64) (x[7]) \
- | (((uint64) (x[6])) << 8) \
- | (((uint64) (x[5])) << 16) \
- | (((uint64) (x[4])) << 24) \
- | (((uint64) (x[3])) << 32) \
- | (((uint64) (x[2])) << 40) \
- | (((uint64) (x[1])) << 48) \
- | (((uint64) (x[0])) << 56)
- ;
+ return
+ (uint64) (x[7]) \
+ | (((uint64) (x[6])) << 8) \
+ | (((uint64) (x[5])) << 16) \
+ | (((uint64) (x[4])) << 24) \
+ | (((uint64) (x[3])) << 32) \
+ | (((uint64) (x[2])) << 40) \
+ | (((uint64) (x[1])) << 48) \
+ | (((uint64) (x[0])) << 56)
+ ;
}
static void store_bigendian(unsigned char *x,uint64 u)
{
- x[7] = u; u >>= 8;
- x[6] = u; u >>= 8;
- x[5] = u; u >>= 8;
- x[4] = u; u >>= 8;
- x[3] = u; u >>= 8;
- x[2] = u; u >>= 8;
- x[1] = u; u >>= 8;
- x[0] = u;
+ x[7] = u; u >>= 8;
+ x[6] = u; u >>= 8;
+ x[5] = u; u >>= 8;
+ x[4] = u; u >>= 8;
+ x[3] = u; u >>= 8;
+ x[2] = u; u >>= 8;
+ x[1] = u; u >>= 8;
+ x[0] = u;
}
#else // !ZT_NO_TYPE_PUNNING
@@ -87,266 +96,272 @@ static void store_bigendian(unsigned char *x,uint64 u)
#define M(w0,w14,w9,w1) w0 = sigma1(w14) + w9 + sigma0(w1) + w0;
#define EXPAND \
- M(w0 ,w14,w9 ,w1 ) \
- M(w1 ,w15,w10,w2 ) \
- M(w2 ,w0 ,w11,w3 ) \
- M(w3 ,w1 ,w12,w4 ) \
- M(w4 ,w2 ,w13,w5 ) \
- M(w5 ,w3 ,w14,w6 ) \
- M(w6 ,w4 ,w15,w7 ) \
- M(w7 ,w5 ,w0 ,w8 ) \
- M(w8 ,w6 ,w1 ,w9 ) \
- M(w9 ,w7 ,w2 ,w10) \
- M(w10,w8 ,w3 ,w11) \
- M(w11,w9 ,w4 ,w12) \
- M(w12,w10,w5 ,w13) \
- M(w13,w11,w6 ,w14) \
- M(w14,w12,w7 ,w15) \
- M(w15,w13,w8 ,w0 )
+ M(w0 ,w14,w9 ,w1 ) \
+ M(w1 ,w15,w10,w2 ) \
+ M(w2 ,w0 ,w11,w3 ) \
+ M(w3 ,w1 ,w12,w4 ) \
+ M(w4 ,w2 ,w13,w5 ) \
+ M(w5 ,w3 ,w14,w6 ) \
+ M(w6 ,w4 ,w15,w7 ) \
+ M(w7 ,w5 ,w0 ,w8 ) \
+ M(w8 ,w6 ,w1 ,w9 ) \
+ M(w9 ,w7 ,w2 ,w10) \
+ M(w10,w8 ,w3 ,w11) \
+ M(w11,w9 ,w4 ,w12) \
+ M(w12,w10,w5 ,w13) \
+ M(w13,w11,w6 ,w14) \
+ M(w14,w12,w7 ,w15) \
+ M(w15,w13,w8 ,w0 )
#define F(w,k) \
- T1 = h + Sigma1(e) + Ch(e,f,g) + k + w; \
- T2 = Sigma0(a) + Maj(a,b,c); \
- h = g; \
- g = f; \
- f = e; \
- e = d + T1; \
- d = c; \
- c = b; \
- b = a; \
- a = T1 + T2;
+ T1 = h + Sigma1(e) + Ch(e,f,g) + k + w; \
+ T2 = Sigma0(a) + Maj(a,b,c); \
+ h = g; \
+ g = f; \
+ f = e; \
+ e = d + T1; \
+ d = c; \
+ c = b; \
+ b = a; \
+ a = T1 + T2;
static inline int crypto_hashblocks(unsigned char *statebytes,const unsigned char *in,unsigned long long inlen)
{
- uint64 state[8];
- uint64 a;
- uint64 b;
- uint64 c;
- uint64 d;
- uint64 e;
- uint64 f;
- uint64 g;
- uint64 h;
- uint64 T1;
- uint64 T2;
-
- a = load_bigendian(statebytes + 0); state[0] = a;
- b = load_bigendian(statebytes + 8); state[1] = b;
- c = load_bigendian(statebytes + 16); state[2] = c;
- d = load_bigendian(statebytes + 24); state[3] = d;
- e = load_bigendian(statebytes + 32); state[4] = e;
- f = load_bigendian(statebytes + 40); state[5] = f;
- g = load_bigendian(statebytes + 48); state[6] = g;
- h = load_bigendian(statebytes + 56); state[7] = h;
-
- while (inlen >= 128) {
- uint64 w0 = load_bigendian(in + 0);
- uint64 w1 = load_bigendian(in + 8);
- uint64 w2 = load_bigendian(in + 16);
- uint64 w3 = load_bigendian(in + 24);
- uint64 w4 = load_bigendian(in + 32);
- uint64 w5 = load_bigendian(in + 40);
- uint64 w6 = load_bigendian(in + 48);
- uint64 w7 = load_bigendian(in + 56);
- uint64 w8 = load_bigendian(in + 64);
- uint64 w9 = load_bigendian(in + 72);
- uint64 w10 = load_bigendian(in + 80);
- uint64 w11 = load_bigendian(in + 88);
- uint64 w12 = load_bigendian(in + 96);
- uint64 w13 = load_bigendian(in + 104);
- uint64 w14 = load_bigendian(in + 112);
- uint64 w15 = load_bigendian(in + 120);
-
- F(w0 ,0x428a2f98d728ae22ULL)
- F(w1 ,0x7137449123ef65cdULL)
- F(w2 ,0xb5c0fbcfec4d3b2fULL)
- F(w3 ,0xe9b5dba58189dbbcULL)
- F(w4 ,0x3956c25bf348b538ULL)
- F(w5 ,0x59f111f1b605d019ULL)
- F(w6 ,0x923f82a4af194f9bULL)
- F(w7 ,0xab1c5ed5da6d8118ULL)
- F(w8 ,0xd807aa98a3030242ULL)
- F(w9 ,0x12835b0145706fbeULL)
- F(w10,0x243185be4ee4b28cULL)
- F(w11,0x550c7dc3d5ffb4e2ULL)
- F(w12,0x72be5d74f27b896fULL)
- F(w13,0x80deb1fe3b1696b1ULL)
- F(w14,0x9bdc06a725c71235ULL)
- F(w15,0xc19bf174cf692694ULL)
-
- EXPAND
-
- F(w0 ,0xe49b69c19ef14ad2ULL)
- F(w1 ,0xefbe4786384f25e3ULL)
- F(w2 ,0x0fc19dc68b8cd5b5ULL)
- F(w3 ,0x240ca1cc77ac9c65ULL)
- F(w4 ,0x2de92c6f592b0275ULL)
- F(w5 ,0x4a7484aa6ea6e483ULL)
- F(w6 ,0x5cb0a9dcbd41fbd4ULL)
- F(w7 ,0x76f988da831153b5ULL)
- F(w8 ,0x983e5152ee66dfabULL)
- F(w9 ,0xa831c66d2db43210ULL)
- F(w10,0xb00327c898fb213fULL)
- F(w11,0xbf597fc7beef0ee4ULL)
- F(w12,0xc6e00bf33da88fc2ULL)
- F(w13,0xd5a79147930aa725ULL)
- F(w14,0x06ca6351e003826fULL)
- F(w15,0x142929670a0e6e70ULL)
-
- EXPAND
-
- F(w0 ,0x27b70a8546d22ffcULL)
- F(w1 ,0x2e1b21385c26c926ULL)
- F(w2 ,0x4d2c6dfc5ac42aedULL)
- F(w3 ,0x53380d139d95b3dfULL)
- F(w4 ,0x650a73548baf63deULL)
- F(w5 ,0x766a0abb3c77b2a8ULL)
- F(w6 ,0x81c2c92e47edaee6ULL)
- F(w7 ,0x92722c851482353bULL)
- F(w8 ,0xa2bfe8a14cf10364ULL)
- F(w9 ,0xa81a664bbc423001ULL)
- F(w10,0xc24b8b70d0f89791ULL)
- F(w11,0xc76c51a30654be30ULL)
- F(w12,0xd192e819d6ef5218ULL)
- F(w13,0xd69906245565a910ULL)
- F(w14,0xf40e35855771202aULL)
- F(w15,0x106aa07032bbd1b8ULL)
-
- EXPAND
-
- F(w0 ,0x19a4c116b8d2d0c8ULL)
- F(w1 ,0x1e376c085141ab53ULL)
- F(w2 ,0x2748774cdf8eeb99ULL)
- F(w3 ,0x34b0bcb5e19b48a8ULL)
- F(w4 ,0x391c0cb3c5c95a63ULL)
- F(w5 ,0x4ed8aa4ae3418acbULL)
- F(w6 ,0x5b9cca4f7763e373ULL)
- F(w7 ,0x682e6ff3d6b2b8a3ULL)
- F(w8 ,0x748f82ee5defb2fcULL)
- F(w9 ,0x78a5636f43172f60ULL)
- F(w10,0x84c87814a1f0ab72ULL)
- F(w11,0x8cc702081a6439ecULL)
- F(w12,0x90befffa23631e28ULL)
- F(w13,0xa4506cebde82bde9ULL)
- F(w14,0xbef9a3f7b2c67915ULL)
- F(w15,0xc67178f2e372532bULL)
-
- EXPAND
-
- F(w0 ,0xca273eceea26619cULL)
- F(w1 ,0xd186b8c721c0c207ULL)
- F(w2 ,0xeada7dd6cde0eb1eULL)
- F(w3 ,0xf57d4f7fee6ed178ULL)
- F(w4 ,0x06f067aa72176fbaULL)
- F(w5 ,0x0a637dc5a2c898a6ULL)
- F(w6 ,0x113f9804bef90daeULL)
- F(w7 ,0x1b710b35131c471bULL)
- F(w8 ,0x28db77f523047d84ULL)
- F(w9 ,0x32caab7b40c72493ULL)
- F(w10,0x3c9ebe0a15c9bebcULL)
- F(w11,0x431d67c49c100d4cULL)
- F(w12,0x4cc5d4becb3e42b6ULL)
- F(w13,0x597f299cfc657e2aULL)
- F(w14,0x5fcb6fab3ad6faecULL)
- F(w15,0x6c44198c4a475817ULL)
-
- a += state[0];
- b += state[1];
- c += state[2];
- d += state[3];
- e += state[4];
- f += state[5];
- g += state[6];
- h += state[7];
-
- state[0] = a;
- state[1] = b;
- state[2] = c;
- state[3] = d;
- state[4] = e;
- state[5] = f;
- state[6] = g;
- state[7] = h;
-
- in += 128;
- inlen -= 128;
- }
-
- store_bigendian(statebytes + 0,state[0]);
- store_bigendian(statebytes + 8,state[1]);
- store_bigendian(statebytes + 16,state[2]);
- store_bigendian(statebytes + 24,state[3]);
- store_bigendian(statebytes + 32,state[4]);
- store_bigendian(statebytes + 40,state[5]);
- store_bigendian(statebytes + 48,state[6]);
- store_bigendian(statebytes + 56,state[7]);
-
- return 0;
+ uint64 state[8];
+ uint64 a;
+ uint64 b;
+ uint64 c;
+ uint64 d;
+ uint64 e;
+ uint64 f;
+ uint64 g;
+ uint64 h;
+ uint64 T1;
+ uint64 T2;
+
+ a = load_bigendian(statebytes + 0); state[0] = a;
+ b = load_bigendian(statebytes + 8); state[1] = b;
+ c = load_bigendian(statebytes + 16); state[2] = c;
+ d = load_bigendian(statebytes + 24); state[3] = d;
+ e = load_bigendian(statebytes + 32); state[4] = e;
+ f = load_bigendian(statebytes + 40); state[5] = f;
+ g = load_bigendian(statebytes + 48); state[6] = g;
+ h = load_bigendian(statebytes + 56); state[7] = h;
+
+ while (inlen >= 128) {
+ uint64 w0 = load_bigendian(in + 0);
+ uint64 w1 = load_bigendian(in + 8);
+ uint64 w2 = load_bigendian(in + 16);
+ uint64 w3 = load_bigendian(in + 24);
+ uint64 w4 = load_bigendian(in + 32);
+ uint64 w5 = load_bigendian(in + 40);
+ uint64 w6 = load_bigendian(in + 48);
+ uint64 w7 = load_bigendian(in + 56);
+ uint64 w8 = load_bigendian(in + 64);
+ uint64 w9 = load_bigendian(in + 72);
+ uint64 w10 = load_bigendian(in + 80);
+ uint64 w11 = load_bigendian(in + 88);
+ uint64 w12 = load_bigendian(in + 96);
+ uint64 w13 = load_bigendian(in + 104);
+ uint64 w14 = load_bigendian(in + 112);
+ uint64 w15 = load_bigendian(in + 120);
+
+ F(w0 ,0x428a2f98d728ae22ULL)
+ F(w1 ,0x7137449123ef65cdULL)
+ F(w2 ,0xb5c0fbcfec4d3b2fULL)
+ F(w3 ,0xe9b5dba58189dbbcULL)
+ F(w4 ,0x3956c25bf348b538ULL)
+ F(w5 ,0x59f111f1b605d019ULL)
+ F(w6 ,0x923f82a4af194f9bULL)
+ F(w7 ,0xab1c5ed5da6d8118ULL)
+ F(w8 ,0xd807aa98a3030242ULL)
+ F(w9 ,0x12835b0145706fbeULL)
+ F(w10,0x243185be4ee4b28cULL)
+ F(w11,0x550c7dc3d5ffb4e2ULL)
+ F(w12,0x72be5d74f27b896fULL)
+ F(w13,0x80deb1fe3b1696b1ULL)
+ F(w14,0x9bdc06a725c71235ULL)
+ F(w15,0xc19bf174cf692694ULL)
+
+ EXPAND
+
+ F(w0 ,0xe49b69c19ef14ad2ULL)
+ F(w1 ,0xefbe4786384f25e3ULL)
+ F(w2 ,0x0fc19dc68b8cd5b5ULL)
+ F(w3 ,0x240ca1cc77ac9c65ULL)
+ F(w4 ,0x2de92c6f592b0275ULL)
+ F(w5 ,0x4a7484aa6ea6e483ULL)
+ F(w6 ,0x5cb0a9dcbd41fbd4ULL)
+ F(w7 ,0x76f988da831153b5ULL)
+ F(w8 ,0x983e5152ee66dfabULL)
+ F(w9 ,0xa831c66d2db43210ULL)
+ F(w10,0xb00327c898fb213fULL)
+ F(w11,0xbf597fc7beef0ee4ULL)
+ F(w12,0xc6e00bf33da88fc2ULL)
+ F(w13,0xd5a79147930aa725ULL)
+ F(w14,0x06ca6351e003826fULL)
+ F(w15,0x142929670a0e6e70ULL)
+
+ EXPAND
+
+ F(w0 ,0x27b70a8546d22ffcULL)
+ F(w1 ,0x2e1b21385c26c926ULL)
+ F(w2 ,0x4d2c6dfc5ac42aedULL)
+ F(w3 ,0x53380d139d95b3dfULL)
+ F(w4 ,0x650a73548baf63deULL)
+ F(w5 ,0x766a0abb3c77b2a8ULL)
+ F(w6 ,0x81c2c92e47edaee6ULL)
+ F(w7 ,0x92722c851482353bULL)
+ F(w8 ,0xa2bfe8a14cf10364ULL)
+ F(w9 ,0xa81a664bbc423001ULL)
+ F(w10,0xc24b8b70d0f89791ULL)
+ F(w11,0xc76c51a30654be30ULL)
+ F(w12,0xd192e819d6ef5218ULL)
+ F(w13,0xd69906245565a910ULL)
+ F(w14,0xf40e35855771202aULL)
+ F(w15,0x106aa07032bbd1b8ULL)
+
+ EXPAND
+
+ F(w0 ,0x19a4c116b8d2d0c8ULL)
+ F(w1 ,0x1e376c085141ab53ULL)
+ F(w2 ,0x2748774cdf8eeb99ULL)
+ F(w3 ,0x34b0bcb5e19b48a8ULL)
+ F(w4 ,0x391c0cb3c5c95a63ULL)
+ F(w5 ,0x4ed8aa4ae3418acbULL)
+ F(w6 ,0x5b9cca4f7763e373ULL)
+ F(w7 ,0x682e6ff3d6b2b8a3ULL)
+ F(w8 ,0x748f82ee5defb2fcULL)
+ F(w9 ,0x78a5636f43172f60ULL)
+ F(w10,0x84c87814a1f0ab72ULL)
+ F(w11,0x8cc702081a6439ecULL)
+ F(w12,0x90befffa23631e28ULL)
+ F(w13,0xa4506cebde82bde9ULL)
+ F(w14,0xbef9a3f7b2c67915ULL)
+ F(w15,0xc67178f2e372532bULL)
+
+ EXPAND
+
+ F(w0 ,0xca273eceea26619cULL)
+ F(w1 ,0xd186b8c721c0c207ULL)
+ F(w2 ,0xeada7dd6cde0eb1eULL)
+ F(w3 ,0xf57d4f7fee6ed178ULL)
+ F(w4 ,0x06f067aa72176fbaULL)
+ F(w5 ,0x0a637dc5a2c898a6ULL)
+ F(w6 ,0x113f9804bef90daeULL)
+ F(w7 ,0x1b710b35131c471bULL)
+ F(w8 ,0x28db77f523047d84ULL)
+ F(w9 ,0x32caab7b40c72493ULL)
+ F(w10,0x3c9ebe0a15c9bebcULL)
+ F(w11,0x431d67c49c100d4cULL)
+ F(w12,0x4cc5d4becb3e42b6ULL)
+ F(w13,0x597f299cfc657e2aULL)
+ F(w14,0x5fcb6fab3ad6faecULL)
+ F(w15,0x6c44198c4a475817ULL)
+
+ a += state[0];
+ b += state[1];
+ c += state[2];
+ d += state[3];
+ e += state[4];
+ f += state[5];
+ g += state[6];
+ h += state[7];
+
+ state[0] = a;
+ state[1] = b;
+ state[2] = c;
+ state[3] = d;
+ state[4] = e;
+ state[5] = f;
+ state[6] = g;
+ state[7] = h;
+
+ in += 128;
+ inlen -= 128;
+ }
+
+ store_bigendian(statebytes + 0,state[0]);
+ store_bigendian(statebytes + 8,state[1]);
+ store_bigendian(statebytes + 16,state[2]);
+ store_bigendian(statebytes + 24,state[3]);
+ store_bigendian(statebytes + 32,state[4]);
+ store_bigendian(statebytes + 40,state[5]);
+ store_bigendian(statebytes + 48,state[6]);
+ store_bigendian(statebytes + 56,state[7]);
+
+ return 0;
}
#define blocks crypto_hashblocks
static const unsigned char iv[64] = {
- 0x6a,0x09,0xe6,0x67,0xf3,0xbc,0xc9,0x08,
- 0xbb,0x67,0xae,0x85,0x84,0xca,0xa7,0x3b,
- 0x3c,0x6e,0xf3,0x72,0xfe,0x94,0xf8,0x2b,
- 0xa5,0x4f,0xf5,0x3a,0x5f,0x1d,0x36,0xf1,
- 0x51,0x0e,0x52,0x7f,0xad,0xe6,0x82,0xd1,
- 0x9b,0x05,0x68,0x8c,0x2b,0x3e,0x6c,0x1f,
- 0x1f,0x83,0xd9,0xab,0xfb,0x41,0xbd,0x6b,
- 0x5b,0xe0,0xcd,0x19,0x13,0x7e,0x21,0x79
+ 0x6a,0x09,0xe6,0x67,0xf3,0xbc,0xc9,0x08,
+ 0xbb,0x67,0xae,0x85,0x84,0xca,0xa7,0x3b,
+ 0x3c,0x6e,0xf3,0x72,0xfe,0x94,0xf8,0x2b,
+ 0xa5,0x4f,0xf5,0x3a,0x5f,0x1d,0x36,0xf1,
+ 0x51,0x0e,0x52,0x7f,0xad,0xe6,0x82,0xd1,
+ 0x9b,0x05,0x68,0x8c,0x2b,0x3e,0x6c,0x1f,
+ 0x1f,0x83,0xd9,0xab,0xfb,0x41,0xbd,0x6b,
+ 0x5b,0xe0,0xcd,0x19,0x13,0x7e,0x21,0x79
};
-//////////////////////////////////////////////////////////////////////////////
-//////////////////////////////////////////////////////////////////////////////
-
void SHA512::hash(void *digest,const void *data,unsigned int len)
{
- unsigned char h[64];
- unsigned char padded[256];
- int i;
- uint64_t bytes = len;
-
- const unsigned char *in = (const unsigned char *)data;
- unsigned int inlen = len;
-
- for (i = 0;i < 64;++i) h[i] = iv[i];
-
- blocks(h,in,inlen);
- in += inlen;
- inlen &= 127;
- in -= inlen;
-
- for (i = 0;i < (int)inlen;++i) padded[i] = in[i];
- padded[inlen] = 0x80;
-
- if (inlen < 112) {
- for (i = inlen + 1;i < 119;++i) padded[i] = 0;
- padded[119] = (unsigned char)((bytes >> 61) & 0xff);
- padded[120] = (unsigned char)((bytes >> 53) & 0xff);
- padded[121] = (unsigned char)((bytes >> 45) & 0xff);
- padded[122] = (unsigned char)((bytes >> 37) & 0xff);
- padded[123] = (unsigned char)((bytes >> 29) & 0xff);
- padded[124] = (unsigned char)((bytes >> 21) & 0xff);
- padded[125] = (unsigned char)((bytes >> 13) & 0xff);
- padded[126] = (unsigned char)((bytes >> 5) & 0xff);
- padded[127] = (unsigned char)((bytes << 3) & 0xff);
- blocks(h,padded,128);
- } else {
- for (i = inlen + 1;i < 247;++i) padded[i] = 0;
- padded[247] = (unsigned char)((bytes >> 61) & 0xff);
- padded[248] = (unsigned char)((bytes >> 53) & 0xff);
- padded[249] = (unsigned char)((bytes >> 45) & 0xff);
- padded[250] = (unsigned char)((bytes >> 37) & 0xff);
- padded[251] = (unsigned char)((bytes >> 29) & 0xff);
- padded[252] = (unsigned char)((bytes >> 21) & 0xff);
- padded[253] = (unsigned char)((bytes >> 13) & 0xff);
- padded[254] = (unsigned char)((bytes >> 5) & 0xff);
- padded[255] = (unsigned char)((bytes << 3) & 0xff);
- blocks(h,padded,256);
- }
-
- for (i = 0;i < 64;++i) ((unsigned char *)digest)[i] = h[i];
+ unsigned char h[64];
+ unsigned char padded[256];
+ int i;
+ uint64_t bytes = len;
+
+ const unsigned char *in = (const unsigned char *)data;
+ unsigned int inlen = len;
+
+ for (i = 0;i < 64;++i) h[i] = iv[i];
+
+ blocks(h,in,inlen);
+ in += inlen;
+ inlen &= 127;
+ in -= inlen;
+
+ for (i = 0;i < (int)inlen;++i) padded[i] = in[i];
+ padded[inlen] = 0x80;
+
+ if (inlen < 112) {
+ for (i = inlen + 1;i < 119;++i) padded[i] = 0;
+ padded[119] = (unsigned char)((bytes >> 61) & 0xff);
+ padded[120] = (unsigned char)((bytes >> 53) & 0xff);
+ padded[121] = (unsigned char)((bytes >> 45) & 0xff);
+ padded[122] = (unsigned char)((bytes >> 37) & 0xff);
+ padded[123] = (unsigned char)((bytes >> 29) & 0xff);
+ padded[124] = (unsigned char)((bytes >> 21) & 0xff);
+ padded[125] = (unsigned char)((bytes >> 13) & 0xff);
+ padded[126] = (unsigned char)((bytes >> 5) & 0xff);
+ padded[127] = (unsigned char)((bytes << 3) & 0xff);
+ blocks(h,padded,128);
+ } else {
+ for (i = inlen + 1;i < 247;++i) padded[i] = 0;
+ padded[247] = (unsigned char)((bytes >> 61) & 0xff);
+ padded[248] = (unsigned char)((bytes >> 53) & 0xff);
+ padded[249] = (unsigned char)((bytes >> 45) & 0xff);
+ padded[250] = (unsigned char)((bytes >> 37) & 0xff);
+ padded[251] = (unsigned char)((bytes >> 29) & 0xff);
+ padded[252] = (unsigned char)((bytes >> 21) & 0xff);
+ padded[253] = (unsigned char)((bytes >> 13) & 0xff);
+ padded[254] = (unsigned char)((bytes >> 5) & 0xff);
+ padded[255] = (unsigned char)((bytes << 3) & 0xff);
+ blocks(h,padded,256);
+ }
+
+ for (i = 0;i < 64;++i) ((unsigned char *)digest)[i] = h[i];
}
} // namespace ZeroTier
+
+#endif // !ZT_HAVE_NATIVE_SHA512
+
+// Internally re-export to included C code, which includes some fast crypto code ported in on some platforms.
+// This eliminates the need to link against a third party SHA512() from this code
+extern "C" void ZT_sha512internal(void *digest,const void *data,unsigned int len)
+{
+ ZeroTier::SHA512::hash(digest,data,len);
+}
diff --git a/node/SHA512.hpp b/node/SHA512.hpp
index 639a7dfd..eedc284a 100644
--- a/node/SHA512.hpp
+++ b/node/SHA512.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_SHA512_HPP
diff --git a/node/Salsa20.cpp b/node/Salsa20.cpp
index 3aa19ac6..1d4117e3 100644
--- a/node/Salsa20.cpp
+++ b/node/Salsa20.cpp
@@ -66,65 +66,49 @@ static const _s20sseconsts _S20SSECONSTANTS;
namespace ZeroTier {
-void Salsa20::init(const void *key,unsigned int kbits,const void *iv)
- throw()
+void Salsa20::init(const void *key,const void *iv)
{
#ifdef ZT_SALSA20_SSE
- const uint32_t *k = (const uint32_t *)key;
-
+ const uint32_t *const k = (const uint32_t *)key;
_state.i[0] = 0x61707865;
+ _state.i[1] = 0x3320646e;
+ _state.i[2] = 0x79622d32;
_state.i[3] = 0x6b206574;
- _state.i[13] = k[0];
- _state.i[10] = k[1];
- _state.i[7] = k[2];
_state.i[4] = k[3];
- if (kbits == 256) {
- k += 4;
- _state.i[1] = 0x3320646e;
- _state.i[2] = 0x79622d32;
- } else {
- _state.i[1] = 0x3120646e;
- _state.i[2] = 0x79622d36;
- }
- _state.i[15] = k[0];
- _state.i[12] = k[1];
- _state.i[9] = k[2];
- _state.i[6] = k[3];
- _state.i[14] = ((const uint32_t *)iv)[0];
- _state.i[11] = ((const uint32_t *)iv)[1];
_state.i[5] = 0;
+ _state.i[6] = k[7];
+ _state.i[7] = k[2];
_state.i[8] = 0;
+ _state.i[9] = k[6];
+ _state.i[10] = k[1];
+ _state.i[11] = ((const uint32_t *)iv)[1];
+ _state.i[12] = k[5];
+ _state.i[13] = k[0];
+ _state.i[14] = ((const uint32_t *)iv)[0];
+ _state.i[15] = k[4];
#else
- const char *constants;
- const uint8_t *k = (const uint8_t *)key;
-
+ const char *const constants = "expand 32-byte k";
+ const uint8_t *const k = (const uint8_t *)key;
+ _state.i[0] = U8TO32_LITTLE(constants + 0);
_state.i[1] = U8TO32_LITTLE(k + 0);
_state.i[2] = U8TO32_LITTLE(k + 4);
_state.i[3] = U8TO32_LITTLE(k + 8);
_state.i[4] = U8TO32_LITTLE(k + 12);
- if (kbits == 256) { /* recommended */
- k += 16;
- constants = "expand 32-byte k";
- } else { /* kbits == 128 */
- constants = "expand 16-byte k";
- }
_state.i[5] = U8TO32_LITTLE(constants + 4);
_state.i[6] = U8TO32_LITTLE(((const uint8_t *)iv) + 0);
_state.i[7] = U8TO32_LITTLE(((const uint8_t *)iv) + 4);
_state.i[8] = 0;
_state.i[9] = 0;
_state.i[10] = U8TO32_LITTLE(constants + 8);
- _state.i[11] = U8TO32_LITTLE(k + 0);
- _state.i[12] = U8TO32_LITTLE(k + 4);
- _state.i[13] = U8TO32_LITTLE(k + 8);
- _state.i[14] = U8TO32_LITTLE(k + 12);
+ _state.i[11] = U8TO32_LITTLE(k + 16);
+ _state.i[12] = U8TO32_LITTLE(k + 20);
+ _state.i[13] = U8TO32_LITTLE(k + 24);
+ _state.i[14] = U8TO32_LITTLE(k + 28);
_state.i[15] = U8TO32_LITTLE(constants + 12);
- _state.i[0] = U8TO32_LITTLE(constants + 0);
#endif
}
-void Salsa20::encrypt12(const void *in,void *out,unsigned int bytes)
- throw()
+void Salsa20::crypt12(const void *in,void *out,unsigned int bytes)
{
uint8_t tmp[64];
const uint8_t *m = (const uint8_t *)in;
@@ -623,8 +607,7 @@ void Salsa20::encrypt12(const void *in,void *out,unsigned int bytes)
}
}
-void Salsa20::encrypt20(const void *in,void *out,unsigned int bytes)
- throw()
+void Salsa20::crypt20(const void *in,void *out,unsigned int bytes)
{
uint8_t tmp[64];
const uint8_t *m = (const uint8_t *)in;
diff --git a/node/Salsa20.hpp b/node/Salsa20.hpp
index 7e4c1e53..bfb6d9d9 100644
--- a/node/Salsa20.hpp
+++ b/node/Salsa20.hpp
@@ -10,6 +10,7 @@
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
+#include <string.h>
#include "Constants.hpp"
#include "Utils.hpp"
@@ -30,76 +31,119 @@ namespace ZeroTier {
class Salsa20
{
public:
- Salsa20() throw() {}
-
+ Salsa20() {}
~Salsa20() { Utils::burn(&_state,sizeof(_state)); }
/**
- * @param key Key bits
- * @param kbits Number of key bits: 128 or 256 (recommended)
- * @param iv 64-bit initialization vector
+ * XOR d with s
+ *
+ * This is done efficiently using e.g. SSE if available. It's used when
+ * alternative Salsa20 implementations are used in Packet and is here
+ * since this is where all the SSE stuff is already included.
+ *
+ * @param d Destination to XOR
+ * @param s Source bytes to XOR with destination
+ * @param len Length of s and d
*/
- Salsa20(const void *key,unsigned int kbits,const void *iv)
- throw()
+ static inline void memxor(uint8_t *d,const uint8_t *s,unsigned int len)
{
- init(key,kbits,iv);
+#ifdef ZT_SALSA20_SSE
+ while (len >= 128) {
+ __m128i s0 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(s));
+ __m128i s1 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(s + 16));
+ __m128i s2 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(s + 32));
+ __m128i s3 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(s + 48));
+ __m128i s4 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(s + 64));
+ __m128i s5 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(s + 80));
+ __m128i s6 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(s + 96));
+ __m128i s7 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(s + 112));
+ __m128i d0 = _mm_loadu_si128(reinterpret_cast<__m128i *>(d));
+ __m128i d1 = _mm_loadu_si128(reinterpret_cast<__m128i *>(d + 16));
+ __m128i d2 = _mm_loadu_si128(reinterpret_cast<__m128i *>(d + 32));
+ __m128i d3 = _mm_loadu_si128(reinterpret_cast<__m128i *>(d + 48));
+ __m128i d4 = _mm_loadu_si128(reinterpret_cast<__m128i *>(d + 64));
+ __m128i d5 = _mm_loadu_si128(reinterpret_cast<__m128i *>(d + 80));
+ __m128i d6 = _mm_loadu_si128(reinterpret_cast<__m128i *>(d + 96));
+ __m128i d7 = _mm_loadu_si128(reinterpret_cast<__m128i *>(d + 112));
+ d0 = _mm_xor_si128(d0,s0);
+ d1 = _mm_xor_si128(d1,s1);
+ d2 = _mm_xor_si128(d2,s2);
+ d3 = _mm_xor_si128(d3,s3);
+ d4 = _mm_xor_si128(d4,s4);
+ d5 = _mm_xor_si128(d5,s5);
+ d6 = _mm_xor_si128(d6,s6);
+ d7 = _mm_xor_si128(d7,s7);
+ _mm_storeu_si128(reinterpret_cast<__m128i *>(d),d0);
+ _mm_storeu_si128(reinterpret_cast<__m128i *>(d + 16),d1);
+ _mm_storeu_si128(reinterpret_cast<__m128i *>(d + 32),d2);
+ _mm_storeu_si128(reinterpret_cast<__m128i *>(d + 48),d3);
+ _mm_storeu_si128(reinterpret_cast<__m128i *>(d + 64),d4);
+ _mm_storeu_si128(reinterpret_cast<__m128i *>(d + 80),d5);
+ _mm_storeu_si128(reinterpret_cast<__m128i *>(d + 96),d6);
+ _mm_storeu_si128(reinterpret_cast<__m128i *>(d + 112),d7);
+ s += 128;
+ d += 128;
+ len -= 128;
+ }
+ while (len >= 16) {
+ _mm_storeu_si128(reinterpret_cast<__m128i *>(d),_mm_xor_si128(_mm_loadu_si128(reinterpret_cast<__m128i *>(d)),_mm_loadu_si128(reinterpret_cast<const __m128i *>(s))));
+ s += 16;
+ d += 16;
+ len -= 16;
+ }
+#else
+#ifndef ZT_NO_TYPE_PUNNING
+ while (len >= 16) {
+ (*reinterpret_cast<uint64_t *>(d)) ^= (*reinterpret_cast<const uint64_t *>(s));
+ s += 8;
+ d += 8;
+ (*reinterpret_cast<uint64_t *>(d)) ^= (*reinterpret_cast<const uint64_t *>(s));
+ s += 8;
+ d += 8;
+ len -= 16;
+ }
+#endif
+#endif
+ while (len) {
+ --len;
+ *(d++) ^= *(s++);
+ }
}
/**
- * Initialize cipher
- *
- * @param key Key bits
- * @param kbits Number of key bits: 128 or 256 (recommended)
+ * @param key 256-bit (32 byte) key
* @param iv 64-bit initialization vector
*/
- void init(const void *key,unsigned int kbits,const void *iv)
- throw();
-
- /**
- * Encrypt data using Salsa20/12
- *
- * @param in Input data
- * @param out Output buffer
- * @param bytes Length of data
- */
- void encrypt12(const void *in,void *out,unsigned int bytes)
- throw();
+ Salsa20(const void *key,const void *iv)
+ {
+ init(key,iv);
+ }
/**
- * Encrypt data using Salsa20/20
+ * Initialize cipher
*
- * @param in Input data
- * @param out Output buffer
- * @param bytes Length of data
+ * @param key Key bits
+ * @param iv 64-bit initialization vector
*/
- void encrypt20(const void *in,void *out,unsigned int bytes)
- throw();
+ void init(const void *key,const void *iv);
/**
- * Decrypt data
+ * Encrypt/decrypt data using Salsa20/12
*
* @param in Input data
* @param out Output buffer
* @param bytes Length of data
*/
- inline void decrypt12(const void *in,void *out,unsigned int bytes)
- throw()
- {
- encrypt12(in,out,bytes);
- }
+ void crypt12(const void *in,void *out,unsigned int bytes);
/**
- * Decrypt data
+ * Encrypt/decrypt data using Salsa20/20
*
* @param in Input data
* @param out Output buffer
* @param bytes Length of data
*/
- inline void decrypt20(const void *in,void *out,unsigned int bytes)
- throw()
- {
- encrypt20(in,out,bytes);
- }
+ void crypt20(const void *in,void *out,unsigned int bytes);
private:
union {
diff --git a/node/SelfAwareness.cpp b/node/SelfAwareness.cpp
index 8bed0c51..c4f107fb 100644
--- a/node/SelfAwareness.cpp
+++ b/node/SelfAwareness.cpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#include <stdio.h>
@@ -31,43 +39,38 @@
#include "Packet.hpp"
#include "Peer.hpp"
#include "Switch.hpp"
+#include "Trace.hpp"
// Entry timeout -- make it fairly long since this is just to prevent stale buildup
-#define ZT_SELFAWARENESS_ENTRY_TIMEOUT 3600000
+#define ZT_SELFAWARENESS_ENTRY_TIMEOUT 600000
namespace ZeroTier {
class _ResetWithinScope
{
public:
- _ResetWithinScope(uint64_t now,InetAddress::IpScope scope) :
+ _ResetWithinScope(void *tPtr,int64_t now,int inetAddressFamily,InetAddress::IpScope scope) :
_now(now),
+ _tPtr(tPtr),
+ _family(inetAddressFamily),
_scope(scope) {}
- inline void operator()(Topology &t,const SharedPtr<Peer> &p)
- {
- if (p->resetWithinScope(_scope,_now))
- peersReset.push_back(p);
- }
-
- std::vector< SharedPtr<Peer> > peersReset;
+ inline void operator()(Topology &t,const SharedPtr<Peer> &p) { p->resetWithinScope(_tPtr,_scope,_family,_now); }
private:
uint64_t _now;
+ void *_tPtr;
+ int _family;
InetAddress::IpScope _scope;
};
SelfAwareness::SelfAwareness(const RuntimeEnvironment *renv) :
RR(renv),
- _phy(32)
+ _phy(128)
{
}
-SelfAwareness::~SelfAwareness()
-{
-}
-
-void SelfAwareness::iam(const Address &reporter,const InetAddress &receivedOnLocalAddress,const InetAddress &reporterPhysicalAddress,const InetAddress &myPhysicalAddress,bool trusted,uint64_t now)
+void SelfAwareness::iam(void *tPtr,const Address &reporter,const int64_t receivedOnLocalSocket,const InetAddress &reporterPhysicalAddress,const InetAddress &myPhysicalAddress,bool trusted,int64_t now)
{
const InetAddress::IpScope scope = myPhysicalAddress.ipScope();
@@ -75,13 +78,15 @@ void SelfAwareness::iam(const Address &reporter,const InetAddress &receivedOnLoc
return;
Mutex::Lock _l(_phy_m);
- PhySurfaceEntry &entry = _phy[PhySurfaceKey(reporter,receivedOnLocalAddress,reporterPhysicalAddress,scope)];
+ PhySurfaceEntry &entry = _phy[PhySurfaceKey(reporter,receivedOnLocalSocket,reporterPhysicalAddress,scope)];
if ( (trusted) && ((now - entry.ts) < ZT_SELFAWARENESS_ENTRY_TIMEOUT) && (!entry.mySurface.ipsEqual(myPhysicalAddress)) ) {
// Changes to external surface reported by trusted peers causes path reset in this scope
+ RR->t->resettingPathsInScope(tPtr,reporter,reporterPhysicalAddress,myPhysicalAddress,scope);
+
entry.mySurface = myPhysicalAddress;
entry.ts = now;
- TRACE("physical address %s for scope %u as seen from %s(%s) differs from %s, resetting paths in scope",myPhysicalAddress.toString().c_str(),(unsigned int)scope,reporter.toString().c_str(),reporterPhysicalAddress.toString().c_str(),entry.mySurface.toString().c_str());
+ entry.trusted = trusted;
// Erase all entries in this scope that were not reported from this remote address to prevent 'thrashing'
// due to multiple reports of endpoint change.
@@ -96,27 +101,18 @@ void SelfAwareness::iam(const Address &reporter,const InetAddress &receivedOnLoc
}
}
- // Reset all paths within this scope
- _ResetWithinScope rset(now,(InetAddress::IpScope)scope);
+ // Reset all paths within this scope and address family
+ _ResetWithinScope rset(tPtr,now,myPhysicalAddress.ss_family,(InetAddress::IpScope)scope);
RR->topology->eachPeer<_ResetWithinScope &>(rset);
-
- // Send a NOP to all peers for whom we forgot a path. This will cause direct
- // links to be re-established if possible, possibly using a root server or some
- // other relay.
- for(std::vector< SharedPtr<Peer> >::const_iterator p(rset.peersReset.begin());p!=rset.peersReset.end();++p) {
- if ((*p)->activelyTransferringFrames(now)) {
- Packet outp((*p)->address(),RR->identity.address(),Packet::VERB_NOP);
- RR->sw->send(outp,true,0);
- }
- }
} else {
// Otherwise just update DB to use to determine external surface info
entry.mySurface = myPhysicalAddress;
entry.ts = now;
+ entry.trusted = trusted;
}
}
-void SelfAwareness::clean(uint64_t now)
+void SelfAwareness::clean(int64_t now)
{
Mutex::Lock _l(_phy_m);
Hashtable< PhySurfaceKey,PhySurfaceEntry >::Iterator i(_phy);
@@ -133,55 +129,82 @@ std::vector<InetAddress> SelfAwareness::getSymmetricNatPredictions()
/* This is based on ideas and strategies found here:
* https://tools.ietf.org/html/draft-takeda-symmetric-nat-traversal-00
*
- * In short: a great many symmetric NATs allocate ports sequentially.
- * This is common on enterprise and carrier grade NATs as well as consumer
- * devices. This code generates a list of "you might try this" addresses by
- * extrapolating likely port assignments from currently known external
- * global IPv4 surfaces. These can then be included in a PUSH_DIRECT_PATHS
- * message to another peer, causing it to possibly try these addresses and
- * bust our local symmetric NAT. It works often enough to be worth the
- * extra bit of code and does no harm in cases where it fails. */
-
- // Gather unique surfaces indexed by local received-on address and flag
- // us as behind a symmetric NAT if there is more than one.
- std::map< InetAddress,std::set<InetAddress> > surfaces;
- bool symmetric = false;
+ * For each IP address reported by a trusted (upstream) peer, we find
+ * the external port most recently reported by ANY peer for that IP.
+ *
+ * We only do any of this for global IPv4 addresses since private IPs
+ * and IPv6 are not going to have symmetric NAT.
+ *
+ * SECURITY NOTE:
+ *
+ * We never use IPs reported by non-trusted peers, since this could lead
+ * to a minor vulnerability whereby a peer could poison our cache with
+ * bad external surface reports via OK(HELLO) and then possibly coax us
+ * into suggesting their IP to other peers via PUSH_DIRECT_PATHS. This
+ * in turn could allow them to MITM flows.
+ *
+ * Since flows are encrypted and authenticated they could not actually
+ * read or modify traffic, but they could gather meta-data for forensics
+ * purpsoes or use this as a DOS attack vector. */
+
+ std::map< uint32_t,unsigned int > maxPortByIp;
+ InetAddress theOneTrueSurface;
{
Mutex::Lock _l(_phy_m);
- Hashtable< PhySurfaceKey,PhySurfaceEntry >::Iterator i(_phy);
- PhySurfaceKey *k = (PhySurfaceKey *)0;
- PhySurfaceEntry *e = (PhySurfaceEntry *)0;
- while (i.next(k,e)) {
- if ((e->mySurface.ss_family == AF_INET)&&(e->mySurface.ipScope() == InetAddress::IP_SCOPE_GLOBAL)) {
- std::set<InetAddress> &s = surfaces[k->receivedOnLocalAddress];
- s.insert(e->mySurface);
- symmetric = symmetric||(s.size() > 1);
+
+ // First check to see if this is a symmetric NAT and enumerate external IPs learned from trusted peers
+ bool symmetric = false;
+ {
+ Hashtable< PhySurfaceKey,PhySurfaceEntry >::Iterator i(_phy);
+ PhySurfaceKey *k = (PhySurfaceKey *)0;
+ PhySurfaceEntry *e = (PhySurfaceEntry *)0;
+ while (i.next(k,e)) {
+ if ((e->trusted)&&(e->mySurface.ss_family == AF_INET)&&(e->mySurface.ipScope() == InetAddress::IP_SCOPE_GLOBAL)) {
+ if (!theOneTrueSurface)
+ theOneTrueSurface = e->mySurface;
+ else if (theOneTrueSurface != e->mySurface)
+ symmetric = true;
+ maxPortByIp[reinterpret_cast<const struct sockaddr_in *>(&(e->mySurface))->sin_addr.s_addr] = e->mySurface.port();
+ }
}
}
- }
+ if (!symmetric)
+ return std::vector<InetAddress>();
- // If we appear to be symmetrically NATed, generate and return extrapolations
- // of those surfaces. Since PUSH_DIRECT_PATHS is sent multiple times, we
- // probabilistically generate extrapolations of anywhere from +1 to +5 to
- // increase the odds that it will work "eventually".
- if (symmetric) {
- std::vector<InetAddress> r;
- for(std::map< InetAddress,std::set<InetAddress> >::iterator si(surfaces.begin());si!=surfaces.end();++si) {
- for(std::set<InetAddress>::iterator i(si->second.begin());i!=si->second.end();++i) {
- InetAddress ipp(*i);
- unsigned int p = ipp.port() + 1 + ((unsigned int)RR->node->prng() & 3);
- if (p >= 65535)
- p -= 64510; // NATs seldom use ports <=1024 so wrap to 1025
- ipp.setPort(p);
- if ((si->second.count(ipp) == 0)&&(std::find(r.begin(),r.end(),ipp) == r.end())) {
- r.push_back(ipp);
+ { // Then find the highest issued port per IP
+ Hashtable< PhySurfaceKey,PhySurfaceEntry >::Iterator i(_phy);
+ PhySurfaceKey *k = (PhySurfaceKey *)0;
+ PhySurfaceEntry *e = (PhySurfaceEntry *)0;
+ while (i.next(k,e)) {
+ if ((e->mySurface.ss_family == AF_INET)&&(e->mySurface.ipScope() == InetAddress::IP_SCOPE_GLOBAL)) {
+ const unsigned int port = e->mySurface.port();
+ std::map< uint32_t,unsigned int >::iterator mp(maxPortByIp.find(reinterpret_cast<const struct sockaddr_in *>(&(e->mySurface))->sin_addr.s_addr));
+ if ((mp != maxPortByIp.end())&&(mp->second < port))
+ mp->second = port;
}
}
}
- return r;
}
- return std::vector<InetAddress>();
+ std::vector<InetAddress> r;
+
+ // Try next port up from max for each
+ for(std::map< uint32_t,unsigned int >::iterator i(maxPortByIp.begin());i!=maxPortByIp.end();++i) {
+ unsigned int p = i->second + 1;
+ if (p > 65535) p -= 64511;
+ const InetAddress pred(&(i->first),4,p);
+ if (std::find(r.begin(),r.end(),pred) == r.end())
+ r.push_back(pred);
+ }
+
+ // Try a random port for each -- there are only 65535 so eventually it should work
+ for(std::map< uint32_t,unsigned int >::iterator i(maxPortByIp.begin());i!=maxPortByIp.end();++i) {
+ const InetAddress pred(&(i->first),4,1024 + ((unsigned int)RR->node->prng() % 64511));
+ if (std::find(r.begin(),r.end(),pred) == r.end())
+ r.push_back(pred);
+ }
+
+ return r;
}
} // namespace ZeroTier
diff --git a/node/SelfAwareness.hpp b/node/SelfAwareness.hpp
index 06c264a9..ce6e8c76 100644
--- a/node/SelfAwareness.hpp
+++ b/node/SelfAwareness.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_SELFAWARENESS_HPP
@@ -36,7 +44,6 @@ class SelfAwareness
{
public:
SelfAwareness(const RuntimeEnvironment *renv);
- ~SelfAwareness();
/**
* Called when a trusted remote peer informs us of our external network address
@@ -48,14 +55,14 @@ public:
* @param trusted True if this peer is trusted as an authority to inform us of external address changes
* @param now Current time
*/
- void iam(const Address &reporter,const InetAddress &receivedOnLocalAddress,const InetAddress &reporterPhysicalAddress,const InetAddress &myPhysicalAddress,bool trusted,uint64_t now);
+ void iam(void *tPtr,const Address &reporter,const int64_t receivedOnLocalSocket,const InetAddress &reporterPhysicalAddress,const InetAddress &myPhysicalAddress,bool trusted,int64_t now);
/**
* Clean up database periodically
*
* @param now Current time
*/
- void clean(uint64_t now);
+ void clean(int64_t now);
/**
* If we appear to be behind a symmetric NAT, get predictions for possible external endpoints
@@ -68,23 +75,24 @@ private:
struct PhySurfaceKey
{
Address reporter;
- InetAddress receivedOnLocalAddress;
+ int64_t receivedOnLocalSocket;
InetAddress reporterPhysicalAddress;
InetAddress::IpScope scope;
PhySurfaceKey() : reporter(),scope(InetAddress::IP_SCOPE_NONE) {}
- PhySurfaceKey(const Address &r,const InetAddress &rol,const InetAddress &ra,InetAddress::IpScope s) : reporter(r),receivedOnLocalAddress(rol),reporterPhysicalAddress(ra),scope(s) {}
+ PhySurfaceKey(const Address &r,const int64_t rol,const InetAddress &ra,InetAddress::IpScope s) : reporter(r),receivedOnLocalSocket(rol),reporterPhysicalAddress(ra),scope(s) {}
- inline unsigned long hashCode() const throw() { return ((unsigned long)reporter.toInt() + (unsigned long)scope); }
- inline bool operator==(const PhySurfaceKey &k) const throw() { return ((reporter == k.reporter)&&(receivedOnLocalAddress == k.receivedOnLocalAddress)&&(reporterPhysicalAddress == k.reporterPhysicalAddress)&&(scope == k.scope)); }
+ inline unsigned long hashCode() const { return ((unsigned long)reporter.toInt() + (unsigned long)scope); }
+ inline bool operator==(const PhySurfaceKey &k) const { return ((reporter == k.reporter)&&(receivedOnLocalSocket == k.receivedOnLocalSocket)&&(reporterPhysicalAddress == k.reporterPhysicalAddress)&&(scope == k.scope)); }
};
struct PhySurfaceEntry
{
InetAddress mySurface;
uint64_t ts;
+ bool trusted;
- PhySurfaceEntry() : mySurface(),ts(0) {}
- PhySurfaceEntry(const InetAddress &a,const uint64_t t) : mySurface(a),ts(t) {}
+ PhySurfaceEntry() : mySurface(),ts(0),trusted(false) {}
+ PhySurfaceEntry(const InetAddress &a,const uint64_t t) : mySurface(a),ts(t),trusted(false) {}
};
const RuntimeEnvironment *RR;
diff --git a/node/SharedPtr.hpp b/node/SharedPtr.hpp
index 3ff5ed18..2f0e50c9 100644
--- a/node/SharedPtr.hpp
+++ b/node/SharedPtr.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_SHAREDPTR_HPP
@@ -25,41 +33,19 @@
namespace ZeroTier {
/**
- * Simple reference counted pointer
+ * Simple zero-overhead introspective reference counted pointer
*
* This is an introspective shared pointer. Classes that need to be reference
* counted must list this as a 'friend' and must have a private instance of
- * AtomicCounter called __refCount. They should also have private destructors,
- * since only this class should delete them.
- *
- * Because this is introspective, it is safe to apply to a naked pointer
- * multiple times provided there is always at least one holding SharedPtr.
- *
- * Once C++11 is ubiquitous, this and a few other things like Thread might get
- * torn out for their standard equivalents.
+ * AtomicCounter called __refCount.
*/
template<typename T>
class SharedPtr
{
public:
- SharedPtr()
- throw() :
- _ptr((T *)0)
- {
- }
-
- SharedPtr(T *obj)
- throw() :
- _ptr(obj)
- {
- ++obj->__refCount;
- }
-
- SharedPtr(const SharedPtr &sp)
- throw() :
- _ptr(sp._getAndInc())
- {
- }
+ SharedPtr() : _ptr((T *)0) {}
+ SharedPtr(T *obj) : _ptr(obj) { ++obj->__refCount; }
+ SharedPtr(const SharedPtr &sp) : _ptr(sp._getAndInc()) {}
~SharedPtr()
{
@@ -90,8 +76,9 @@ public:
*
* @param ptr Naked pointer to assign
*/
- inline void setToUnsafe(T *ptr)
+ inline void set(T *ptr)
{
+ zero();
++ptr->__refCount;
_ptr = ptr;
}
@@ -102,50 +89,57 @@ public:
* @param with Pointer to swap with
*/
inline void swap(SharedPtr &with)
- throw()
{
T *tmp = _ptr;
_ptr = with._ptr;
with._ptr = tmp;
}
- inline operator bool() const throw() { return (_ptr != (T *)0); }
- inline T &operator*() const throw() { return *_ptr; }
- inline T *operator->() const throw() { return _ptr; }
+ inline operator bool() const { return (_ptr != (T *)0); }
+ inline T &operator*() const { return *_ptr; }
+ inline T *operator->() const { return _ptr; }
/**
* @return Raw pointer to held object
*/
- inline T *ptr() const throw() { return _ptr; }
+ inline T *ptr() const { return _ptr; }
/**
- * Set this pointer to null
+ * Set this pointer to NULL
*/
inline void zero()
{
if (_ptr) {
if (--_ptr->__refCount <= 0)
delete _ptr;
+ _ptr = (T *)0;
}
- _ptr = (T *)0;
}
- inline bool operator==(const SharedPtr &sp) const throw() { return (_ptr == sp._ptr); }
- inline bool operator!=(const SharedPtr &sp) const throw() { return (_ptr != sp._ptr); }
- inline bool operator>(const SharedPtr &sp) const throw() { return (_ptr > sp._ptr); }
- inline bool operator<(const SharedPtr &sp) const throw() { return (_ptr < sp._ptr); }
- inline bool operator>=(const SharedPtr &sp) const throw() { return (_ptr >= sp._ptr); }
- inline bool operator<=(const SharedPtr &sp) const throw() { return (_ptr <= sp._ptr); }
+ /**
+ * @return Number of references according to this object's ref count or 0 if NULL
+ */
+ inline int references()
+ {
+ if (_ptr)
+ return _ptr->__refCount.load();
+ return 0;
+ }
+
+ inline bool operator==(const SharedPtr &sp) const { return (_ptr == sp._ptr); }
+ inline bool operator!=(const SharedPtr &sp) const { return (_ptr != sp._ptr); }
+ inline bool operator>(const SharedPtr &sp) const { return (_ptr > sp._ptr); }
+ inline bool operator<(const SharedPtr &sp) const { return (_ptr < sp._ptr); }
+ inline bool operator>=(const SharedPtr &sp) const { return (_ptr >= sp._ptr); }
+ inline bool operator<=(const SharedPtr &sp) const { return (_ptr <= sp._ptr); }
private:
inline T *_getAndInc() const
- throw()
{
if (_ptr)
++_ptr->__refCount;
return _ptr;
}
-
T *_ptr;
};
diff --git a/node/Switch.cpp b/node/Switch.cpp
index bf3afe33..eb1ebadb 100644
--- a/node/Switch.cpp
+++ b/node/Switch.cpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#include <stdio.h>
@@ -35,43 +43,25 @@
#include "Peer.hpp"
#include "SelfAwareness.hpp"
#include "Packet.hpp"
-#include "Cluster.hpp"
+#include "Trace.hpp"
namespace ZeroTier {
-#ifdef ZT_TRACE
-static const char *etherTypeName(const unsigned int etherType)
-{
- switch(etherType) {
- case ZT_ETHERTYPE_IPV4: return "IPV4";
- case ZT_ETHERTYPE_ARP: return "ARP";
- case ZT_ETHERTYPE_RARP: return "RARP";
- case ZT_ETHERTYPE_ATALK: return "ATALK";
- case ZT_ETHERTYPE_AARP: return "AARP";
- case ZT_ETHERTYPE_IPX_A: return "IPX_A";
- case ZT_ETHERTYPE_IPX_B: return "IPX_B";
- case ZT_ETHERTYPE_IPV6: return "IPV6";
- }
- return "UNKNOWN";
-}
-#endif // ZT_TRACE
-
Switch::Switch(const RuntimeEnvironment *renv) :
RR(renv),
_lastBeaconResponse(0),
- _outstandingWhoisRequests(32),
+ _lastCheckedQueues(0),
_lastUniteAttempt(8) // only really used on root servers and upstreams, and it'll grow there just fine
{
}
-Switch::~Switch()
-{
-}
-
-void Switch::onRemotePacket(const InetAddress &localAddr,const InetAddress &fromAddr,const void *data,unsigned int len)
+void Switch::onRemotePacket(void *tPtr,const int64_t localSocket,const InetAddress &fromAddr,const void *data,unsigned int len)
{
try {
- const uint64_t now = RR->node->now();
+ const int64_t now = RR->node->now();
+
+ const SharedPtr<Path> path(RR->topology->getPath(localSocket,fromAddr));
+ path->received(now);
if (len == 13) {
/* LEGACY: before VERB_PUSH_DIRECT_PATHS, peers used broadcast
@@ -79,22 +69,22 @@ void Switch::onRemotePacket(const InetAddress &localAddr,const InetAddress &from
* no longer send these, but we'll listen for them for a while to
* locate peers with versions <1.0.4. */
- Address beaconAddr(reinterpret_cast<const char *>(data) + 8,5);
+ const Address beaconAddr(reinterpret_cast<const char *>(data) + 8,5);
if (beaconAddr == RR->identity.address())
return;
- if (!RR->node->shouldUsePathForZeroTierTraffic(localAddr,fromAddr))
+ if (!RR->node->shouldUsePathForZeroTierTraffic(tPtr,beaconAddr,localSocket,fromAddr))
return;
- SharedPtr<Peer> peer(RR->topology->getPeer(beaconAddr));
+ const SharedPtr<Peer> peer(RR->topology->getPeer(tPtr,beaconAddr));
if (peer) { // we'll only respond to beacons from known peers
if ((now - _lastBeaconResponse) >= 2500) { // limit rate of responses
_lastBeaconResponse = now;
Packet outp(peer->address(),RR->identity.address(),Packet::VERB_NOP);
outp.armor(peer->key(),true);
- RR->node->putPacket(localAddr,fromAddr,outp.data(),outp.size());
+ path->send(RR,tPtr,outp.data(),outp.size(),now);
}
}
- } else if (len > ZT_PROTO_MIN_FRAGMENT_LENGTH) { // min length check is important!
+ } else if (len > ZT_PROTO_MIN_FRAGMENT_LENGTH) { // SECURITY: min length check is important since we do some C-style stuff below!
if (reinterpret_cast<const uint8_t *>(data)[ZT_PACKET_FRAGMENT_IDX_FRAGMENT_INDICATOR] == ZT_PACKET_FRAGMENT_INDICATOR) {
// Handle fragment ----------------------------------------------------
@@ -102,28 +92,21 @@ void Switch::onRemotePacket(const InetAddress &localAddr,const InetAddress &from
const Address destination(fragment.destination());
if (destination != RR->identity.address()) {
- // Fragment is not for us, so try to relay it
+ if ( (!RR->topology->amUpstream()) && (!path->trustEstablished(now)) )
+ return;
+
if (fragment.hops() < ZT_RELAY_MAX_HOPS) {
fragment.incrementHops();
// Note: we don't bother initiating NAT-t for fragments, since heads will set that off.
// It wouldn't hurt anything, just redundant and unnecessary.
- SharedPtr<Peer> relayTo = RR->topology->getPeer(destination);
- if ((!relayTo)||(!relayTo->send(fragment.data(),fragment.size(),now))) {
-#ifdef ZT_ENABLE_CLUSTER
- if (RR->cluster) {
- RR->cluster->sendViaCluster(Address(),destination,fragment.data(),fragment.size(),false);
- return;
- }
-#endif
-
- // Don't know peer or no direct path -- so relay via root server
- relayTo = RR->topology->getBestRoot();
+ SharedPtr<Peer> relayTo = RR->topology->getPeer(tPtr,destination);
+ if ((!relayTo)||(!relayTo->sendDirect(tPtr,fragment.data(),fragment.size(),now,false))) {
+ // Don't know peer or no direct path -- so relay via someone upstream
+ relayTo = RR->topology->getUpstreamPeer();
if (relayTo)
- relayTo->send(fragment.data(),fragment.size(),now);
+ relayTo->sendDirect(tPtr,fragment.data(),fragment.size(),now,true);
}
- } else {
- TRACE("dropped relay [fragment](%s) -> %s, max hops exceeded",fromAddr.toString().c_str(),destination.toString().c_str());
}
} else {
// Fragment looks like ours
@@ -137,12 +120,9 @@ void Switch::onRemotePacket(const InetAddress &localAddr,const InetAddress &from
// Total fragments must be more than 1, otherwise why are we
// seeing a Packet::Fragment?
- Mutex::Lock _l(_rxQueue_m);
- RXQueueEntry *const rq = _findRXQueueEntry(now,fragmentPacketId);
-
- if ((!rq->timestamp)||(rq->packetId != fragmentPacketId)) {
+ RXQueueEntry *const rq = _findRXQueueEntry(fragmentPacketId);
+ if (rq->packetId != fragmentPacketId) {
// No packet found, so we received a fragment without its head.
- //TRACE("fragment (%u/%u) of %.16llx from %s",fragmentNumber + 1,totalFragments,fragmentPacketId,fromAddr.toString().c_str());
rq->timestamp = now;
rq->packetId = fragmentPacketId;
@@ -152,19 +132,17 @@ void Switch::onRemotePacket(const InetAddress &localAddr,const InetAddress &from
rq->complete = false;
} else if (!(rq->haveFragments & (1 << fragmentNumber))) {
// We have other fragments and maybe the head, so add this one and check
- //TRACE("fragment (%u/%u) of %.16llx from %s",fragmentNumber + 1,totalFragments,fragmentPacketId,fromAddr.toString().c_str());
rq->frags[fragmentNumber - 1] = fragment;
rq->totalFragments = totalFragments;
if (Utils::countBits(rq->haveFragments |= (1 << fragmentNumber)) == totalFragments) {
// We have all fragments -- assemble and process full Packet
- //TRACE("packet %.16llx is complete, assembling and processing...",fragmentPacketId);
for(unsigned int f=1;f<totalFragments;++f)
rq->frag0.append(rq->frags[f - 1].payload(),rq->frags[f - 1].payloadLength());
- if (rq->frag0.tryDecode(RR,false)) {
+ if (rq->frag0.tryDecode(RR,tPtr)) {
rq->timestamp = 0; // packet decoded, free entry
} else {
rq->complete = true; // set complete flag but leave entry since it probably needs WHOIS or something
@@ -178,77 +156,59 @@ void Switch::onRemotePacket(const InetAddress &localAddr,const InetAddress &from
} else if (len >= ZT_PROTO_MIN_PACKET_LENGTH) { // min length check is important!
// Handle packet head -------------------------------------------------
- // See packet format in Packet.hpp to understand this
- const uint64_t packetId = (
- (((uint64_t)reinterpret_cast<const uint8_t *>(data)[0]) << 56) |
- (((uint64_t)reinterpret_cast<const uint8_t *>(data)[1]) << 48) |
- (((uint64_t)reinterpret_cast<const uint8_t *>(data)[2]) << 40) |
- (((uint64_t)reinterpret_cast<const uint8_t *>(data)[3]) << 32) |
- (((uint64_t)reinterpret_cast<const uint8_t *>(data)[4]) << 24) |
- (((uint64_t)reinterpret_cast<const uint8_t *>(data)[5]) << 16) |
- (((uint64_t)reinterpret_cast<const uint8_t *>(data)[6]) << 8) |
- ((uint64_t)reinterpret_cast<const uint8_t *>(data)[7])
- );
const Address destination(reinterpret_cast<const uint8_t *>(data) + 8,ZT_ADDRESS_LENGTH);
const Address source(reinterpret_cast<const uint8_t *>(data) + 13,ZT_ADDRESS_LENGTH);
- // Catch this and toss it -- it would never work, but it could happen if we somehow
- // mistakenly guessed an address we're bound to as a destination for another peer.
if (source == RR->identity.address())
return;
- //TRACE("<< %.16llx %s -> %s (size: %u)",(unsigned long long)packet->packetId(),source.toString().c_str(),destination.toString().c_str(),packet->size());
-
if (destination != RR->identity.address()) {
+ if ( (!RR->topology->amUpstream()) && (!path->trustEstablished(now)) && (source != RR->identity.address()) )
+ return;
+
Packet packet(data,len);
- // Packet is not for us, so try to relay it
if (packet.hops() < ZT_RELAY_MAX_HOPS) {
packet.incrementHops();
-
- SharedPtr<Peer> relayTo = RR->topology->getPeer(destination);
- if ((relayTo)&&((relayTo->send(packet.data(),packet.size(),now)))) {
- Mutex::Lock _l(_lastUniteAttempt_m);
- uint64_t &luts = _lastUniteAttempt[_LastUniteKey(source,destination)];
- if ((now - luts) >= ZT_MIN_UNITE_INTERVAL) {
- luts = now;
- unite(source,destination);
+ SharedPtr<Peer> relayTo = RR->topology->getPeer(tPtr,destination);
+ if ((relayTo)&&(relayTo->sendDirect(tPtr,packet.data(),packet.size(),now,false))) {
+ if ((source != RR->identity.address())&&(_shouldUnite(now,source,destination))) {
+ const SharedPtr<Peer> sourcePeer(RR->topology->getPeer(tPtr,source));
+ if (sourcePeer)
+ relayTo->introduce(tPtr,now,sourcePeer);
}
} else {
-#ifdef ZT_ENABLE_CLUSTER
- if (RR->cluster) {
- bool shouldUnite;
- {
- Mutex::Lock _l(_lastUniteAttempt_m);
- uint64_t &luts = _lastUniteAttempt[_LastUniteKey(source,destination)];
- shouldUnite = ((now - luts) >= ZT_MIN_UNITE_INTERVAL);
- if (shouldUnite)
- luts = now;
+ relayTo = RR->topology->getUpstreamPeer();
+ if ((relayTo)&&(relayTo->address() != source)) {
+ if (relayTo->sendDirect(tPtr,packet.data(),packet.size(),now,true)) {
+ const SharedPtr<Peer> sourcePeer(RR->topology->getPeer(tPtr,source));
+ if (sourcePeer)
+ relayTo->introduce(tPtr,now,sourcePeer);
}
- RR->cluster->sendViaCluster(source,destination,packet.data(),packet.size(),shouldUnite);
- return;
}
-#endif
- relayTo = RR->topology->getBestRoot(&source,1,true);
- if (relayTo)
- relayTo->send(packet.data(),packet.size(),now);
}
- } else {
- TRACE("dropped relay %s(%s) -> %s, max hops exceeded",packet.source().toString().c_str(),fromAddr.toString().c_str(),destination.toString().c_str());
}
} else if ((reinterpret_cast<const uint8_t *>(data)[ZT_PACKET_IDX_FLAGS] & ZT_PROTO_FLAG_FRAGMENTED) != 0) {
// Packet is the head of a fragmented packet series
- Mutex::Lock _l(_rxQueue_m);
- RXQueueEntry *const rq = _findRXQueueEntry(now,packetId);
-
- if ((!rq->timestamp)||(rq->packetId != packetId)) {
+ const uint64_t packetId = (
+ (((uint64_t)reinterpret_cast<const uint8_t *>(data)[0]) << 56) |
+ (((uint64_t)reinterpret_cast<const uint8_t *>(data)[1]) << 48) |
+ (((uint64_t)reinterpret_cast<const uint8_t *>(data)[2]) << 40) |
+ (((uint64_t)reinterpret_cast<const uint8_t *>(data)[3]) << 32) |
+ (((uint64_t)reinterpret_cast<const uint8_t *>(data)[4]) << 24) |
+ (((uint64_t)reinterpret_cast<const uint8_t *>(data)[5]) << 16) |
+ (((uint64_t)reinterpret_cast<const uint8_t *>(data)[6]) << 8) |
+ ((uint64_t)reinterpret_cast<const uint8_t *>(data)[7])
+ );
+
+ RXQueueEntry *const rq = _findRXQueueEntry(packetId);
+ if (rq->packetId != packetId) {
// If we have no other fragments yet, create an entry and save the head
- //TRACE("fragment (0/?) of %.16llx from %s",pid,fromAddr.toString().c_str());
rq->timestamp = now;
rq->packetId = packetId;
- rq->frag0.init(data,len,localAddr,fromAddr,now);
+ rq->frag0.init(data,len,path,now);
rq->totalFragments = 0;
rq->haveFragments = 1;
rq->complete = false;
@@ -257,36 +217,28 @@ void Switch::onRemotePacket(const InetAddress &localAddr,const InetAddress &from
if ((rq->totalFragments > 1)&&(Utils::countBits(rq->haveFragments |= 1) == rq->totalFragments)) {
// We have all fragments -- assemble and process full Packet
- //TRACE("packet %.16llx is complete, assembling and processing...",pid);
- rq->frag0.init(data,len,localAddr,fromAddr,now);
+ rq->frag0.init(data,len,path,now);
for(unsigned int f=1;f<rq->totalFragments;++f)
rq->frag0.append(rq->frags[f - 1].payload(),rq->frags[f - 1].payloadLength());
- if (rq->frag0.tryDecode(RR,false)) {
+ if (rq->frag0.tryDecode(RR,tPtr)) {
rq->timestamp = 0; // packet decoded, free entry
} else {
rq->complete = true; // set complete flag but leave entry since it probably needs WHOIS or something
}
} else {
// Still waiting on more fragments, but keep the head
- rq->frag0.init(data,len,localAddr,fromAddr,now);
+ rq->frag0.init(data,len,path,now);
}
} // else this is a duplicate head, ignore
} else {
// Packet is unfragmented, so just process it
- IncomingPacket packet(data,len,localAddr,fromAddr,now);
- if (!packet.tryDecode(RR,false)) {
- Mutex::Lock _l(_rxQueue_m);
- RXQueueEntry *rq = &(_rxQueue[ZT_RX_QUEUE_SIZE - 1]);
- unsigned long i = ZT_RX_QUEUE_SIZE - 1;
- while ((i)&&(rq->timestamp)) {
- RXQueueEntry *tmp = &(_rxQueue[--i]);
- if (tmp->timestamp < rq->timestamp)
- rq = tmp;
- }
+ IncomingPacket packet(data,len,path,now);
+ if (!packet.tryDecode(RR,tPtr)) {
+ RXQueueEntry *const rq = _nextRXQueueEntry();
rq->timestamp = now;
- rq->packetId = packetId;
+ rq->packetId = packet.packetId();
rq->frag0 = packet;
rq->totalFragments = 1;
rq->haveFragments = 1;
@@ -297,41 +249,25 @@ void Switch::onRemotePacket(const InetAddress &localAddr,const InetAddress &from
// --------------------------------------------------------------------
}
}
- } catch (std::exception &ex) {
- TRACE("dropped packet from %s: unexpected exception: %s",fromAddr.toString().c_str(),ex.what());
- } catch ( ... ) {
- TRACE("dropped packet from %s: unexpected exception: (unknown)",fromAddr.toString().c_str());
- }
+ } catch ( ... ) {} // sanity check, should be caught elsewhere
}
-void Switch::onLocalEthernet(const SharedPtr<Network> &network,const MAC &from,const MAC &to,unsigned int etherType,unsigned int vlanId,const void *data,unsigned int len)
+void Switch::onLocalEthernet(void *tPtr,const SharedPtr<Network> &network,const MAC &from,const MAC &to,unsigned int etherType,unsigned int vlanId,const void *data,unsigned int len)
{
if (!network->hasConfig())
return;
- // Sanity check -- bridge loop? OS problem?
- if (to == network->mac())
- return;
-
- // Check to make sure this protocol is allowed on this network
- if (!network->config().permitsEtherType(etherType)) {
- TRACE("%.16llx: ignored tap: %s -> %s: ethertype %s not allowed on network %.16llx",network->id(),from.toString().c_str(),to.toString().c_str(),etherTypeName(etherType),(unsigned long long)network->id());
- return;
- }
-
// Check if this packet is from someone other than the tap -- i.e. bridged in
- bool fromBridged = false;
- if (from != network->mac()) {
+ bool fromBridged;
+ if ((fromBridged = (from != network->mac()))) {
if (!network->config().permitsBridging(RR->identity.address())) {
- TRACE("%.16llx: %s -> %s %s not forwarded, bridging disabled or this peer not a bridge",network->id(),from.toString().c_str(),to.toString().c_str(),etherTypeName(etherType));
+ RR->t->outgoingNetworkFrameDropped(tPtr,network,from,to,etherType,vlanId,len,"not a bridge");
return;
}
- fromBridged = true;
}
if (to.isMulticast()) {
- // Destination is a multicast address (including broadcast)
- MulticastGroup mg(to,0);
+ MulticastGroup multicastGroup(to,0);
if (to.isBroadcast()) {
if ( (etherType == ZT_ETHERTYPE_ARP) && (len >= 28) && ((((const uint8_t *)data)[2] == 0x08)&&(((const uint8_t *)data)[3] == 0x00)&&(((const uint8_t *)data)[4] == 6)&&(((const uint8_t *)data)[5] == 4)&&(((const uint8_t *)data)[7] == 0x01)) ) {
@@ -344,10 +280,10 @@ void Switch::onLocalEthernet(const SharedPtr<Network> &network,const MAC &from,c
* them into multicasts by stuffing the IP address being queried into
* the 32-bit ADI field. In practice this uses our multicast pub/sub
* system to implement a kind of extended/distributed ARP table. */
- mg = MulticastGroup::deriveMulticastGroupForAddressResolution(InetAddress(((const unsigned char *)data) + 24,4,0));
+ multicastGroup = MulticastGroup::deriveMulticastGroupForAddressResolution(InetAddress(((const unsigned char *)data) + 24,4,0));
} else if (!network->config().enableBroadcast()) {
// Don't transmit broadcasts if this network doesn't want them
- TRACE("%.16llx: dropped broadcast since ff:ff:ff:ff:ff:ff is not enabled",network->id());
+ RR->t->outgoingNetworkFrameDropped(tPtr,network,from,to,etherType,vlanId,len,"broadcast disabled");
return;
}
} else if ((etherType == ZT_ETHERTYPE_IPV6)&&(len >= (40 + 8 + 16))) {
@@ -400,7 +336,6 @@ void Switch::onLocalEthernet(const SharedPtr<Network> &network,const MAC &from,c
if ((v6EmbeddedAddress)&&(v6EmbeddedAddress != RR->identity.address())) {
const MAC peerMac(v6EmbeddedAddress,network->id());
- TRACE("IPv6 NDP emulation: %.16llx: forging response for %s/%s",network->id(),v6EmbeddedAddress.toString().c_str(),peerMac.toString().c_str());
uint8_t adv[72];
adv[0] = 0x60; adv[1] = 0x00; adv[2] = 0x00; adv[3] = 0x00;
@@ -428,74 +363,87 @@ void Switch::onLocalEthernet(const SharedPtr<Network> &network,const MAC &from,c
adv[42] = (checksum >> 8) & 0xff;
adv[43] = checksum & 0xff;
- RR->node->putFrame(network->id(),network->userPtr(),peerMac,from,ZT_ETHERTYPE_IPV6,0,adv,72);
+ RR->node->putFrame(tPtr,network->id(),network->userPtr(),peerMac,from,ZT_ETHERTYPE_IPV6,0,adv,72);
return; // NDP emulation done. We have forged a "fake" reply, so no need to send actual NDP query.
} // else no NDP emulation
} // else no NDP emulation
}
+ // Check this after NDP emulation, since that has to be allowed in exactly this case
+ if (network->config().multicastLimit == 0) {
+ RR->t->outgoingNetworkFrameDropped(tPtr,network,from,to,etherType,vlanId,len,"multicast disabled");
+ return;
+ }
+
/* Learn multicast groups for bridged-in hosts.
* Note that some OSes, most notably Linux, do this for you by learning
* multicast addresses on bridge interfaces and subscribing each slave.
* But in that case this does no harm, as the sets are just merged. */
if (fromBridged)
- network->learnBridgedMulticastGroup(mg,RR->node->now());
+ network->learnBridgedMulticastGroup(tPtr,multicastGroup,RR->node->now());
- //TRACE("%.16llx: MULTICAST %s -> %s %s %u",network->id(),from.toString().c_str(),mg.toString().c_str(),etherTypeName(etherType),len);
+ // First pass sets noTee to false, but noTee is set to true in OutboundMulticast to prevent duplicates.
+ if (!network->filterOutgoingPacket(tPtr,false,RR->identity.address(),Address(),from,to,(const uint8_t *)data,len,etherType,vlanId)) {
+ RR->t->outgoingNetworkFrameDropped(tPtr,network,from,to,etherType,vlanId,len,"filter blocked");
+ return;
+ }
RR->mc->send(
- ((!network->config().isPublic())&&(network->config().com)) ? &(network->config().com) : (const CertificateOfMembership *)0,
- network->config().multicastLimit,
+ tPtr,
RR->node->now(),
- network->id(),
- network->config().activeBridges(),
- mg,
+ network,
+ Address(),
+ multicastGroup,
(fromBridged) ? from : MAC(),
etherType,
data,
len);
-
- return;
- }
-
- if (to[0] == MAC::firstOctetForNetwork(network->id())) {
+ } else if (to == network->mac()) {
+ // Destination is this node, so just reinject it
+ RR->node->putFrame(tPtr,network->id(),network->userPtr(),from,to,etherType,vlanId,data,len);
+ } else if (to[0] == MAC::firstOctetForNetwork(network->id())) {
// Destination is another ZeroTier peer on the same network
Address toZT(to.toAddress(network->id())); // since in-network MACs are derived from addresses and network IDs, we can reverse this
- SharedPtr<Peer> toPeer(RR->topology->getPeer(toZT));
- const bool includeCom = ( (network->config().isPrivate()) && (network->config().com) && ((!toPeer)||(toPeer->needsOurNetworkMembershipCertificate(network->id(),RR->node->now(),true))) );
- if ((fromBridged)||(includeCom)) {
+ SharedPtr<Peer> toPeer(RR->topology->getPeer(tPtr,toZT));
+
+ if (!network->filterOutgoingPacket(tPtr,false,RR->identity.address(),toZT,from,to,(const uint8_t *)data,len,etherType,vlanId)) {
+ RR->t->outgoingNetworkFrameDropped(tPtr,network,from,to,etherType,vlanId,len,"filter blocked");
+ return;
+ }
+
+ if (fromBridged) {
Packet outp(toZT,RR->identity.address(),Packet::VERB_EXT_FRAME);
outp.append(network->id());
- if (includeCom) {
- outp.append((unsigned char)0x01); // 0x01 -- COM included
- network->config().com.serialize(outp);
- } else {
- outp.append((unsigned char)0x00);
- }
+ outp.append((unsigned char)0x00);
to.appendTo(outp);
from.appendTo(outp);
outp.append((uint16_t)etherType);
outp.append(data,len);
- outp.compress();
- send(outp,true,network->id());
+ if (!network->config().disableCompression())
+ outp.compress();
+ send(tPtr,outp,true);
} else {
Packet outp(toZT,RR->identity.address(),Packet::VERB_FRAME);
outp.append(network->id());
outp.append((uint16_t)etherType);
outp.append(data,len);
- outp.compress();
- send(outp,true,network->id());
+ if (!network->config().disableCompression())
+ outp.compress();
+ send(tPtr,outp,true);
}
- //TRACE("%.16llx: UNICAST: %s -> %s etherType==%s(%.4x) vlanId==%u len==%u fromBridged==%d includeCom==%d",network->id(),from.toString().c_str(),to.toString().c_str(),etherTypeName(etherType),etherType,vlanId,len,(int)fromBridged,(int)includeCom);
-
- return;
- }
-
- {
+ } else {
// Destination is bridged behind a remote peer
+ // We filter with a NULL destination ZeroTier address first. Filtrations
+ // for each ZT destination are also done below. This is the same rationale
+ // and design as for multicast.
+ if (!network->filterOutgoingPacket(tPtr,false,RR->identity.address(),Address(),from,to,(const uint8_t *)data,len,etherType,vlanId)) {
+ RR->t->outgoingNetworkFrameDropped(tPtr,network,from,to,etherType,vlanId,len,"filter blocked");
+ return;
+ }
+
Address bridges[ZT_MAX_BRIDGE_SPAM];
unsigned int numBridges = 0;
@@ -529,245 +477,132 @@ void Switch::onLocalEthernet(const SharedPtr<Network> &network,const MAC &from,c
}
for(unsigned int b=0;b<numBridges;++b) {
- SharedPtr<Peer> bridgePeer(RR->topology->getPeer(bridges[b]));
- Packet outp(bridges[b],RR->identity.address(),Packet::VERB_EXT_FRAME);
- outp.append(network->id());
- if ( (network->config().isPrivate()) && (network->config().com) && ((!bridgePeer)||(bridgePeer->needsOurNetworkMembershipCertificate(network->id(),RR->node->now(),true))) ) {
- outp.append((unsigned char)0x01); // 0x01 -- COM included
- network->config().com.serialize(outp);
+ if (network->filterOutgoingPacket(tPtr,true,RR->identity.address(),bridges[b],from,to,(const uint8_t *)data,len,etherType,vlanId)) {
+ Packet outp(bridges[b],RR->identity.address(),Packet::VERB_EXT_FRAME);
+ outp.append(network->id());
+ outp.append((uint8_t)0x00);
+ to.appendTo(outp);
+ from.appendTo(outp);
+ outp.append((uint16_t)etherType);
+ outp.append(data,len);
+ if (!network->config().disableCompression())
+ outp.compress();
+ send(tPtr,outp,true);
} else {
- outp.append((unsigned char)0);
+ RR->t->outgoingNetworkFrameDropped(tPtr,network,from,to,etherType,vlanId,len,"filter blocked (bridge replication)");
}
- to.appendTo(outp);
- from.appendTo(outp);
- outp.append((uint16_t)etherType);
- outp.append(data,len);
- outp.compress();
- send(outp,true,network->id());
}
}
}
-void Switch::send(const Packet &packet,bool encrypt,uint64_t nwid)
+void Switch::send(void *tPtr,Packet &packet,bool encrypt)
{
- if (packet.destination() == RR->identity.address()) {
- TRACE("BUG: caught attempt to send() to self, ignored");
+ const Address dest(packet.destination());
+ if (dest == RR->identity.address())
return;
- }
-
- //TRACE(">> %s to %s (%u bytes, encrypt==%d, nwid==%.16llx)",Packet::verbString(packet.verb()),packet.destination().toString().c_str(),packet.size(),(int)encrypt,nwid);
-
- if (!_trySend(packet,encrypt,nwid)) {
- Mutex::Lock _l(_txQueue_m);
- _txQueue.push_back(TXQueueEntry(packet.destination(),RR->node->now(),packet,encrypt,nwid));
- }
-}
-
-bool Switch::unite(const Address &p1,const Address &p2)
-{
- if ((p1 == RR->identity.address())||(p2 == RR->identity.address()))
- return false;
- SharedPtr<Peer> p1p = RR->topology->getPeer(p1);
- if (!p1p)
- return false;
- SharedPtr<Peer> p2p = RR->topology->getPeer(p2);
- if (!p2p)
- return false;
-
- const uint64_t now = RR->node->now();
-
- std::pair<InetAddress,InetAddress> cg(Peer::findCommonGround(*p1p,*p2p,now));
- if ((!(cg.first))||(cg.first.ipScope() != cg.second.ipScope()))
- return false;
-
- TRACE("unite: %s(%s) <> %s(%s)",p1.toString().c_str(),cg.second.toString().c_str(),p2.toString().c_str(),cg.first.toString().c_str());
-
- /* Tell P1 where to find P2 and vice versa, sending the packets to P1 and
- * P2 in randomized order in terms of which gets sent first. This is done
- * since in a few cases NAT-t can be sensitive to slight timing differences
- * in terms of when the two peers initiate. Normally this is accounted for
- * by the nearly-simultaneous RENDEZVOUS kickoff from the relay, but
- * given that relay are hosted on cloud providers this can in some
- * cases have a few ms of latency between packet departures. By randomizing
- * the order we make each attempted NAT-t favor one or the other going
- * first, meaning if it doesn't succeed the first time it might the second
- * and so forth. */
- unsigned int alt = (unsigned int)RR->node->prng() & 1;
- unsigned int completed = alt + 2;
- while (alt != completed) {
- if ((alt & 1) == 0) {
- // Tell p1 where to find p2.
- Packet outp(p1,RR->identity.address(),Packet::VERB_RENDEZVOUS);
- outp.append((unsigned char)0);
- p2.appendTo(outp);
- outp.append((uint16_t)cg.first.port());
- if (cg.first.isV6()) {
- outp.append((unsigned char)16);
- outp.append(cg.first.rawIpData(),16);
- } else {
- outp.append((unsigned char)4);
- outp.append(cg.first.rawIpData(),4);
- }
- outp.armor(p1p->key(),true);
- p1p->send(outp.data(),outp.size(),now);
- } else {
- // Tell p2 where to find p1.
- Packet outp(p2,RR->identity.address(),Packet::VERB_RENDEZVOUS);
- outp.append((unsigned char)0);
- p1.appendTo(outp);
- outp.append((uint16_t)cg.second.port());
- if (cg.second.isV6()) {
- outp.append((unsigned char)16);
- outp.append(cg.second.rawIpData(),16);
- } else {
- outp.append((unsigned char)4);
- outp.append(cg.second.rawIpData(),4);
- }
- outp.armor(p2p->key(),true);
- p2p->send(outp.data(),outp.size(),now);
+ if (!_trySend(tPtr,packet,encrypt)) {
+ {
+ Mutex::Lock _l(_txQueue_m);
+ _txQueue.push_back(TXQueueEntry(dest,RR->node->now(),packet,encrypt));
}
- ++alt; // counts up and also flips LSB
+ if (!RR->topology->getPeer(tPtr,dest))
+ requestWhois(tPtr,RR->node->now(),dest);
}
-
- return true;
}
-void Switch::rendezvous(const SharedPtr<Peer> &peer,const InetAddress &localAddr,const InetAddress &atAddr)
+void Switch::requestWhois(void *tPtr,const int64_t now,const Address &addr)
{
- TRACE("sending NAT-t message to %s(%s)",peer->address().toString().c_str(),atAddr.toString().c_str());
- const uint64_t now = RR->node->now();
- peer->sendHELLO(localAddr,atAddr,now,2); // first attempt: send low-TTL packet to 'open' local NAT
+ if (addr == RR->identity.address())
+ return;
+
{
- Mutex::Lock _l(_contactQueue_m);
- _contactQueue.push_back(ContactQueueEntry(peer,now + ZT_NAT_T_TACTICAL_ESCALATION_DELAY,localAddr,atAddr));
+ Mutex::Lock _l(_lastSentWhoisRequest_m);
+ int64_t &last = _lastSentWhoisRequest[addr];
+ if ((now - last) < ZT_WHOIS_RETRY_DELAY)
+ return;
+ else last = now;
}
-}
-void Switch::requestWhois(const Address &addr)
-{
- bool inserted = false;
- {
- Mutex::Lock _l(_outstandingWhoisRequests_m);
- WhoisRequest &r = _outstandingWhoisRequests[addr];
- if (r.lastSent) {
- r.retries = 0; // reset retry count if entry already existed, but keep waiting and retry again after normal timeout
- } else {
- r.lastSent = RR->node->now();
- inserted = true;
- }
+ const SharedPtr<Peer> upstream(RR->topology->getUpstreamPeer());
+ if (upstream) {
+ Packet outp(upstream->address(),RR->identity.address(),Packet::VERB_WHOIS);
+ addr.appendTo(outp);
+ RR->node->expectReplyTo(outp.packetId());
+ send(tPtr,outp,true);
}
- if (inserted)
- _sendWhoisRequest(addr,(const Address *)0,0);
}
-void Switch::doAnythingWaitingForPeer(const SharedPtr<Peer> &peer)
+void Switch::doAnythingWaitingForPeer(void *tPtr,const SharedPtr<Peer> &peer)
{
- { // cancel pending WHOIS since we now know this peer
- Mutex::Lock _l(_outstandingWhoisRequests_m);
- _outstandingWhoisRequests.erase(peer->address());
+ {
+ Mutex::Lock _l(_lastSentWhoisRequest_m);
+ _lastSentWhoisRequest.erase(peer->address());
}
- { // finish processing any packets waiting on peer's public key / identity
- Mutex::Lock _l(_rxQueue_m);
- unsigned long i = ZT_RX_QUEUE_SIZE;
- while (i) {
- RXQueueEntry *rq = &(_rxQueue[--i]);
- if ((rq->timestamp)&&(rq->complete)) {
- if (rq->frag0.tryDecode(RR,false))
- rq->timestamp = 0;
- }
+ const int64_t now = RR->node->now();
+ for(unsigned int ptr=0;ptr<ZT_RX_QUEUE_SIZE;++ptr) {
+ RXQueueEntry *const rq = &(_rxQueue[ptr]);
+ if ((rq->timestamp)&&(rq->complete)) {
+ if ((rq->frag0.tryDecode(RR,tPtr))||((now - rq->timestamp) > ZT_RECEIVE_QUEUE_TIMEOUT))
+ rq->timestamp = 0;
}
}
- { // finish sending any packets waiting on peer's public key / identity
+ {
Mutex::Lock _l(_txQueue_m);
for(std::list< TXQueueEntry >::iterator txi(_txQueue.begin());txi!=_txQueue.end();) {
if (txi->dest == peer->address()) {
- if (_trySend(txi->packet,txi->encrypt,txi->nwid))
+ if (_trySend(tPtr,txi->packet,txi->encrypt)) {
_txQueue.erase(txi++);
- else ++txi;
- } else ++txi;
- }
- }
-}
-
-unsigned long Switch::doTimerTasks(uint64_t now)
-{
- unsigned long nextDelay = 0xffffffff; // ceiling delay, caller will cap to minimum
-
- { // Iterate through NAT traversal strategies for entries in contact queue
- Mutex::Lock _l(_contactQueue_m);
- for(std::list<ContactQueueEntry>::iterator qi(_contactQueue.begin());qi!=_contactQueue.end();) {
- if (now >= qi->fireAtTime) {
- if (!qi->peer->pushDirectPaths(qi->localAddr,qi->inaddr,now,true,false))
- qi->peer->sendHELLO(qi->localAddr,qi->inaddr,now);
- _contactQueue.erase(qi++);
- continue;
- /* Old symmetric NAT buster code, obsoleted by port prediction alg in SelfAwareness but left around for now in case we revert
- if (qi->strategyIteration == 0) {
- // First strategy: send packet directly to destination
- qi->peer->sendHELLO(qi->localAddr,qi->inaddr,now);
- } else if (qi->strategyIteration <= 3) {
- // Strategies 1-3: try escalating ports for symmetric NATs that remap sequentially
- InetAddress tmpaddr(qi->inaddr);
- int p = (int)qi->inaddr.port() + qi->strategyIteration;
- if (p > 65535)
- p -= 64511;
- tmpaddr.setPort((unsigned int)p);
- qi->peer->sendHELLO(qi->localAddr,tmpaddr,now);
} else {
- // All strategies tried, expire entry
- _contactQueue.erase(qi++);
- continue;
+ ++txi;
}
- ++qi->strategyIteration;
- qi->fireAtTime = now + ZT_NAT_T_TACTICAL_ESCALATION_DELAY;
- nextDelay = std::min(nextDelay,(unsigned long)ZT_NAT_T_TACTICAL_ESCALATION_DELAY);
- */
} else {
- nextDelay = std::min(nextDelay,(unsigned long)(qi->fireAtTime - now));
+ ++txi;
}
- ++qi; // if qi was erased, loop will have continued before here
}
}
+}
- { // Retry outstanding WHOIS requests
- Mutex::Lock _l(_outstandingWhoisRequests_m);
- Hashtable< Address,WhoisRequest >::Iterator i(_outstandingWhoisRequests);
- Address *a = (Address *)0;
- WhoisRequest *r = (WhoisRequest *)0;
- while (i.next(a,r)) {
- const unsigned long since = (unsigned long)(now - r->lastSent);
- if (since >= ZT_WHOIS_RETRY_DELAY) {
- if (r->retries >= ZT_MAX_WHOIS_RETRIES) {
- TRACE("WHOIS %s timed out",a->toString().c_str());
- _outstandingWhoisRequests.erase(*a);
- } else {
- r->lastSent = now;
- r->peersConsulted[r->retries] = _sendWhoisRequest(*a,r->peersConsulted,r->retries);
- ++r->retries;
- TRACE("WHOIS %s (retry %u)",a->toString().c_str(),r->retries);
- nextDelay = std::min(nextDelay,(unsigned long)ZT_WHOIS_RETRY_DELAY);
- }
- } else {
- nextDelay = std::min(nextDelay,ZT_WHOIS_RETRY_DELAY - since);
- }
- }
- }
+unsigned long Switch::doTimerTasks(void *tPtr,int64_t now)
+{
+ const uint64_t timeSinceLastCheck = now - _lastCheckedQueues;
+ if (timeSinceLastCheck < ZT_WHOIS_RETRY_DELAY)
+ return (unsigned long)(ZT_WHOIS_RETRY_DELAY - timeSinceLastCheck);
+ _lastCheckedQueues = now;
- { // Time out TX queue packets that never got WHOIS lookups or other info.
+ std::vector<Address> needWhois;
+ {
Mutex::Lock _l(_txQueue_m);
for(std::list< TXQueueEntry >::iterator txi(_txQueue.begin());txi!=_txQueue.end();) {
- if (_trySend(txi->packet,txi->encrypt,txi->nwid))
+ if (_trySend(tPtr,txi->packet,txi->encrypt)) {
_txQueue.erase(txi++);
- else if ((now - txi->creationTime) > ZT_TRANSMIT_QUEUE_TIMEOUT) {
- TRACE("TX %s -> %s timed out",txi->packet.source().toString().c_str(),txi->packet.destination().toString().c_str());
+ } else if ((now - txi->creationTime) > ZT_TRANSMIT_QUEUE_TIMEOUT) {
_txQueue.erase(txi++);
- } else ++txi;
+ } else {
+ if (!RR->topology->getPeer(tPtr,txi->dest))
+ needWhois.push_back(txi->dest);
+ ++txi;
+ }
+ }
+ }
+ for(std::vector<Address>::const_iterator i(needWhois.begin());i!=needWhois.end();++i)
+ requestWhois(tPtr,now,*i);
+
+ for(unsigned int ptr=0;ptr<ZT_RX_QUEUE_SIZE;++ptr) {
+ RXQueueEntry *const rq = &(_rxQueue[ptr]);
+ if ((rq->timestamp)&&(rq->complete)) {
+ if ((rq->frag0.tryDecode(RR,tPtr))||((now - rq->timestamp) > ZT_RECEIVE_QUEUE_TIMEOUT)) {
+ rq->timestamp = 0;
+ } else {
+ const Address src(rq->frag0.source());
+ if (!RR->topology->getPeer(tPtr,src))
+ requestWhois(tPtr,now,src);
+ }
}
}
- { // Remove really old last unite attempt entries to keep table size controlled
+ {
Mutex::Lock _l(_lastUniteAttempt_m);
Hashtable< _LastUniteKey,uint64_t >::Iterator i(_lastUniteAttempt);
_LastUniteKey *k = (_LastUniteKey *)0;
@@ -778,109 +613,86 @@ unsigned long Switch::doTimerTasks(uint64_t now)
}
}
- return nextDelay;
+ {
+ Mutex::Lock _l(_lastSentWhoisRequest_m);
+ Hashtable< Address,int64_t >::Iterator i(_lastSentWhoisRequest);
+ Address *a = (Address *)0;
+ int64_t *ts = (int64_t *)0;
+ while (i.next(a,ts)) {
+ if ((now - *ts) > (ZT_WHOIS_RETRY_DELAY * 2))
+ _lastSentWhoisRequest.erase(*a);
+ }
+ }
+
+ return ZT_WHOIS_RETRY_DELAY;
}
-Address Switch::_sendWhoisRequest(const Address &addr,const Address *peersAlreadyConsulted,unsigned int numPeersAlreadyConsulted)
+bool Switch::_shouldUnite(const int64_t now,const Address &source,const Address &destination)
{
- SharedPtr<Peer> root(RR->topology->getBestRoot(peersAlreadyConsulted,numPeersAlreadyConsulted,false));
- if (root) {
- Packet outp(root->address(),RR->identity.address(),Packet::VERB_WHOIS);
- addr.appendTo(outp);
- outp.armor(root->key(),true);
- if (root->send(outp.data(),outp.size(),RR->node->now()))
- return root->address();
+ Mutex::Lock _l(_lastUniteAttempt_m);
+ uint64_t &ts = _lastUniteAttempt[_LastUniteKey(source,destination)];
+ if ((now - ts) >= ZT_MIN_UNITE_INTERVAL) {
+ ts = now;
+ return true;
}
- return Address();
+ return false;
}
-bool Switch::_trySend(const Packet &packet,bool encrypt,uint64_t nwid)
+bool Switch::_trySend(void *tPtr,Packet &packet,bool encrypt)
{
- SharedPtr<Peer> peer(RR->topology->getPeer(packet.destination()));
+ SharedPtr<Path> viaPath;
+ const int64_t now = RR->node->now();
+ const Address destination(packet.destination());
+ const SharedPtr<Peer> peer(RR->topology->getPeer(tPtr,destination));
if (peer) {
- const uint64_t now = RR->node->now();
-
- SharedPtr<Network> network;
- if (nwid) {
- network = RR->node->network(nwid);
- if ((!network)||(!network->hasConfig()))
- return false; // we probably just left this network, let its packets die
- }
-
- Path *viaPath = peer->getBestPath(now);
- SharedPtr<Peer> relay;
-
+ viaPath = peer->getBestPath(now,false);
if (!viaPath) {
- if (network) {
- unsigned int bestq = ~((unsigned int)0); // max unsigned int since quality is lower==better
- unsigned int ptr = 0;
- for(;;) {
- const Address raddr(network->config().nextRelay(ptr));
- if (raddr) {
- SharedPtr<Peer> rp(RR->topology->getPeer(raddr));
- if (rp) {
- const unsigned int q = rp->relayQuality(now);
- if (q < bestq) {
- bestq = q;
- rp.swap(relay);
- }
- }
- } else break;
- }
+ peer->tryMemorizedPath(tPtr,now); // periodically attempt memorized or statically defined paths, if any are known
+ const SharedPtr<Peer> relay(RR->topology->getUpstreamPeer());
+ if ( (!relay) || (!(viaPath = relay->getBestPath(now,false))) ) {
+ if (!(viaPath = peer->getBestPath(now,true)))
+ return false;
}
-
- if (!relay)
- relay = RR->topology->getBestRoot();
-
- if ( (!relay) || (!(viaPath = relay->getBestPath(now))) )
- return false;
- }
- // viaPath will not be null if we make it here
-
- // Push possible direct paths to us if we are relaying
- if (relay) {
- peer->pushDirectPaths(viaPath->localAddress(),viaPath->address(),now,false,( (network)&&(network->isAllowed(peer)) ));
- viaPath->sent(now);
}
+ } else {
+ return false;
+ }
- Packet tmp(packet);
+ unsigned int mtu = ZT_DEFAULT_PHYSMTU;
+ uint64_t trustedPathId = 0;
+ RR->topology->getOutboundPathInfo(viaPath->address(),mtu,trustedPathId);
- unsigned int chunkSize = std::min(tmp.size(),(unsigned int)ZT_UDP_DEFAULT_PAYLOAD_MTU);
- tmp.setFragmented(chunkSize < tmp.size());
+ unsigned int chunkSize = std::min(packet.size(),mtu);
+ packet.setFragmented(chunkSize < packet.size());
- const uint64_t trustedPathId = RR->topology->getOutboundPathTrust(viaPath->address());
- if (trustedPathId) {
- tmp.setTrusted(trustedPathId);
- } else {
- tmp.armor(peer->key(),encrypt);
- }
+ if (trustedPathId) {
+ packet.setTrusted(trustedPathId);
+ } else {
+ packet.armor(peer->key(),encrypt);
+ }
- if (viaPath->send(RR,tmp.data(),chunkSize,now)) {
- if (chunkSize < tmp.size()) {
- // Too big for one packet, fragment the rest
- unsigned int fragStart = chunkSize;
- unsigned int remaining = tmp.size() - chunkSize;
- unsigned int fragsRemaining = (remaining / (ZT_UDP_DEFAULT_PAYLOAD_MTU - ZT_PROTO_MIN_FRAGMENT_LENGTH));
- if ((fragsRemaining * (ZT_UDP_DEFAULT_PAYLOAD_MTU - ZT_PROTO_MIN_FRAGMENT_LENGTH)) < remaining)
- ++fragsRemaining;
- unsigned int totalFragments = fragsRemaining + 1;
-
- for(unsigned int fno=1;fno<totalFragments;++fno) {
- chunkSize = std::min(remaining,(unsigned int)(ZT_UDP_DEFAULT_PAYLOAD_MTU - ZT_PROTO_MIN_FRAGMENT_LENGTH));
- Packet::Fragment frag(tmp,fragStart,chunkSize,fno,totalFragments);
- viaPath->send(RR,frag.data(),frag.size(),now);
- fragStart += chunkSize;
- remaining -= chunkSize;
- }
+ if (viaPath->send(RR,tPtr,packet.data(),chunkSize,now)) {
+ if (chunkSize < packet.size()) {
+ // Too big for one packet, fragment the rest
+ unsigned int fragStart = chunkSize;
+ unsigned int remaining = packet.size() - chunkSize;
+ unsigned int fragsRemaining = (remaining / (mtu - ZT_PROTO_MIN_FRAGMENT_LENGTH));
+ if ((fragsRemaining * (mtu - ZT_PROTO_MIN_FRAGMENT_LENGTH)) < remaining)
+ ++fragsRemaining;
+ const unsigned int totalFragments = fragsRemaining + 1;
+
+ for(unsigned int fno=1;fno<totalFragments;++fno) {
+ chunkSize = std::min(remaining,(unsigned int)(mtu - ZT_PROTO_MIN_FRAGMENT_LENGTH));
+ Packet::Fragment frag(packet,fragStart,chunkSize,fno,totalFragments);
+ viaPath->send(RR,tPtr,frag.data(),frag.size(),now);
+ fragStart += chunkSize;
+ remaining -= chunkSize;
}
-
- return true;
}
- } else {
- requestWhois(packet.destination());
}
- return false;
+
+ return true;
}
} // namespace ZeroTier
diff --git a/node/Switch.hpp b/node/Switch.hpp
index ce4f00a1..906f418e 100644
--- a/node/Switch.hpp
+++ b/node/Switch.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_N_SWITCH_HPP
@@ -27,12 +35,10 @@
#include "Constants.hpp"
#include "Mutex.hpp"
#include "MAC.hpp"
-#include "NonCopyable.hpp"
#include "Packet.hpp"
#include "Utils.hpp"
#include "InetAddress.hpp"
#include "Topology.hpp"
-#include "Array.hpp"
#include "Network.hpp"
#include "SharedPtr.hpp"
#include "IncomingPacket.hpp"
@@ -51,25 +57,26 @@ class Peer;
* packets from tap devices, and this sends them where they need to go and
* wraps/unwraps accordingly. It also handles queues and timeouts and such.
*/
-class Switch : NonCopyable
+class Switch
{
public:
Switch(const RuntimeEnvironment *renv);
- ~Switch();
/**
* Called when a packet is received from the real network
*
- * @param localAddr Local interface address
+ * @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
* @param data Packet data
* @param len Packet length
*/
- void onRemotePacket(const InetAddress &localAddr,const InetAddress &fromAddr,const void *data,unsigned int len);
+ void onRemotePacket(void *tPtr,const int64_t localSocket,const InetAddress &fromAddr,const void *data,unsigned int len);
/**
* Called when a packet comes from a local Ethernet tap
*
+ * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
* @param network Which network's TAP did this packet come from?
* @param from Originating MAC address
* @param to Destination MAC address
@@ -78,7 +85,7 @@ public:
* @param data Ethernet payload
* @param len Frame length
*/
- void onLocalEthernet(const SharedPtr<Network> &network,const MAC &from,const MAC &to,unsigned int etherType,unsigned int vlanId,const void *data,unsigned int len);
+ void onLocalEthernet(void *tPtr,const SharedPtr<Network> &network,const MAC &from,const MAC &to,unsigned int etherType,unsigned int vlanId,const void *data,unsigned int len);
/**
* Send a packet to a ZeroTier address (destination in packet)
@@ -92,51 +99,30 @@ public:
* Needless to say, the packet's source must be this node. Otherwise it
* won't be encrypted right. (This is not used for relaying.)
*
- * The network ID should only be specified for frames and other actual
- * network traffic. Other traffic such as controller requests and regular
- * protocol messages should specify zero.
- *
- * @param packet Packet to send
+ * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
+ * @param packet Packet to send (buffer may be modified)
* @param encrypt Encrypt packet payload? (always true except for HELLO)
- * @param nwid Related network ID or 0 if message is not in-network traffic
- */
- void send(const Packet &packet,bool encrypt,uint64_t nwid);
-
- /**
- * Send RENDEZVOUS to two peers to permit them to directly connect
- *
- * This only works if both peers are known, with known working direct
- * links to this peer. The best link for each peer is sent to the other.
- *
- * @param p1 One of two peers (order doesn't matter)
- * @param p2 Second of pair
- */
- bool unite(const Address &p1,const Address &p2);
-
- /**
- * Attempt NAT traversal to peer at a given physical address
- *
- * @param peer Peer to contact
- * @param localAddr Local interface address
- * @param atAddr Address of peer
*/
- void rendezvous(const SharedPtr<Peer> &peer,const InetAddress &localAddr,const InetAddress &atAddr);
+ void send(void *tPtr,Packet &packet,bool encrypt);
/**
* Request WHOIS on a given address
*
+ * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
+ * @param now Current time
* @param addr Address to look up
*/
- void requestWhois(const Address &addr);
+ void requestWhois(void *tPtr,const int64_t now,const Address &addr);
/**
* Run any processes that are waiting for this peer's identity
*
* Called when we learn of a peer's identity from HELLO, OK(WHOIS), etc.
*
+ * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
* @param peer New peer
*/
- void doAnythingWaitingForPeer(const SharedPtr<Peer> &peer);
+ void doAnythingWaitingForPeer(void *tPtr,const SharedPtr<Peer> &peer);
/**
* Perform retries and other periodic timer tasks
@@ -144,77 +130,70 @@ public:
* This can return a very long delay if there are no pending timer
* tasks. The caller should cap this comparatively vs. other values.
*
+ * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
* @param now Current time
* @return Number of milliseconds until doTimerTasks() should be run again
*/
- unsigned long doTimerTasks(uint64_t now);
+ unsigned long doTimerTasks(void *tPtr,int64_t now);
private:
- Address _sendWhoisRequest(const Address &addr,const Address *peersAlreadyConsulted,unsigned int numPeersAlreadyConsulted);
- bool _trySend(const Packet &packet,bool encrypt,uint64_t nwid);
+ bool _shouldUnite(const int64_t now,const Address &source,const Address &destination);
+ bool _trySend(void *tPtr,Packet &packet,bool encrypt); // packet is modified if return is true
const RuntimeEnvironment *const RR;
- uint64_t _lastBeaconResponse;
+ int64_t _lastBeaconResponse;
+ volatile int64_t _lastCheckedQueues;
- // Outstanding WHOIS requests and how many retries they've undergone
- struct WhoisRequest
- {
- WhoisRequest() : lastSent(0),retries(0) {}
- uint64_t lastSent;
- Address peersConsulted[ZT_MAX_WHOIS_RETRIES]; // by retry
- unsigned int retries; // 0..ZT_MAX_WHOIS_RETRIES
- };
- Hashtable< Address,WhoisRequest > _outstandingWhoisRequests;
- Mutex _outstandingWhoisRequests_m;
+ // Time we last sent a WHOIS request for each address
+ Hashtable< Address,int64_t > _lastSentWhoisRequest;
+ Mutex _lastSentWhoisRequest_m;
// Packets waiting for WHOIS replies or other decode info or missing fragments
struct RXQueueEntry
{
RXQueueEntry() : timestamp(0) {}
- uint64_t timestamp; // 0 if entry is not in use
- uint64_t packetId;
+ volatile int64_t timestamp; // 0 if entry is not in use
+ volatile uint64_t packetId;
IncomingPacket frag0; // head of packet
Packet::Fragment frags[ZT_MAX_PACKET_FRAGMENTS - 1]; // later fragments (if any)
unsigned int totalFragments; // 0 if only frag0 received, waiting for frags
uint32_t haveFragments; // bit mask, LSB to MSB
- bool complete; // if true, packet is complete
+ volatile bool complete; // if true, packet is complete
};
RXQueueEntry _rxQueue[ZT_RX_QUEUE_SIZE];
- Mutex _rxQueue_m;
+ AtomicCounter _rxQueuePtr;
- /* Returns the matching or oldest entry. Caller must check timestamp and
- * packet ID to determine which. */
- inline RXQueueEntry *_findRXQueueEntry(uint64_t now,uint64_t packetId)
+ // Returns matching or next available RX queue entry
+ inline RXQueueEntry *_findRXQueueEntry(uint64_t packetId)
{
- RXQueueEntry *rq;
- RXQueueEntry *oldest = &(_rxQueue[ZT_RX_QUEUE_SIZE - 1]);
- unsigned long i = ZT_RX_QUEUE_SIZE;
- while (i) {
- rq = &(_rxQueue[--i]);
+ const unsigned int current = static_cast<unsigned int>(_rxQueuePtr.load());
+ for(unsigned int k=1;k<=ZT_RX_QUEUE_SIZE;++k) {
+ RXQueueEntry *rq = &(_rxQueue[(current - k) % ZT_RX_QUEUE_SIZE]);
if ((rq->packetId == packetId)&&(rq->timestamp))
return rq;
- if ((now - rq->timestamp) >= ZT_RX_QUEUE_EXPIRE)
- rq->timestamp = 0;
- if (rq->timestamp < oldest->timestamp)
- oldest = rq;
}
- return oldest;
+ ++_rxQueuePtr;
+ return &(_rxQueue[static_cast<unsigned int>(current) % ZT_RX_QUEUE_SIZE]);
+ }
+
+ // Returns current entry in rx queue ring buffer and increments ring pointer
+ inline RXQueueEntry *_nextRXQueueEntry()
+ {
+ return &(_rxQueue[static_cast<unsigned int>((++_rxQueuePtr) - 1) % ZT_RX_QUEUE_SIZE]);
}
// ZeroTier-layer TX queue entry
struct TXQueueEntry
{
TXQueueEntry() {}
- TXQueueEntry(Address d,uint64_t ct,const Packet &p,bool enc,uint64_t nw) :
+ TXQueueEntry(Address d,uint64_t ct,const Packet &p,bool enc) :
dest(d),
creationTime(ct),
- nwid(nw),
packet(p),
encrypt(enc) {}
Address dest;
uint64_t creationTime;
- uint64_t nwid;
Packet packet; // unencrypted/unMAC'd packet -- this is done at send time
bool encrypt;
};
@@ -235,32 +214,12 @@ private:
y = a2.toInt();
}
}
- inline unsigned long hashCode() const throw() { return ((unsigned long)x ^ (unsigned long)y); }
- inline bool operator==(const _LastUniteKey &k) const throw() { return ((x == k.x)&&(y == k.y)); }
+ inline unsigned long hashCode() const { return ((unsigned long)x ^ (unsigned long)y); }
+ inline bool operator==(const _LastUniteKey &k) const { return ((x == k.x)&&(y == k.y)); }
uint64_t x,y;
};
Hashtable< _LastUniteKey,uint64_t > _lastUniteAttempt; // key is always sorted in ascending order, for set-like behavior
Mutex _lastUniteAttempt_m;
-
- // Active attempts to contact remote peers, including state of multi-phase NAT traversal
- struct ContactQueueEntry
- {
- ContactQueueEntry() {}
- ContactQueueEntry(const SharedPtr<Peer> &p,uint64_t ft,const InetAddress &laddr,const InetAddress &a) :
- peer(p),
- fireAtTime(ft),
- inaddr(a),
- localAddr(laddr),
- strategyIteration(0) {}
-
- SharedPtr<Peer> peer;
- uint64_t fireAtTime;
- InetAddress inaddr;
- InetAddress localAddr;
- unsigned int strategyIteration;
- };
- std::list<ContactQueueEntry> _contactQueue;
- Mutex _contactQueue_m;
};
} // namespace ZeroTier
diff --git a/node/Tag.cpp b/node/Tag.cpp
new file mode 100644
index 00000000..62d9cb2e
--- /dev/null
+++ b/node/Tag.cpp
@@ -0,0 +1,55 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
+ */
+
+#include "Tag.hpp"
+#include "RuntimeEnvironment.hpp"
+#include "Identity.hpp"
+#include "Topology.hpp"
+#include "Switch.hpp"
+#include "Network.hpp"
+#include "Node.hpp"
+
+namespace ZeroTier {
+
+int Tag::verify(const RuntimeEnvironment *RR,void *tPtr) const
+{
+ if ((!_signedBy)||(_signedBy != Network::controllerFor(_networkId)))
+ return -1;
+ const Identity id(RR->topology->getIdentity(tPtr,_signedBy));
+ if (!id) {
+ RR->sw->requestWhois(tPtr,RR->node->now(),_signedBy);
+ return 1;
+ }
+ try {
+ Buffer<(sizeof(Tag) * 2)> tmp;
+ this->serialize(tmp,true);
+ return (id.verify(tmp.data(),tmp.size(),_signature) ? 0 : -1);
+ } catch ( ... ) {
+ return -1;
+ }
+}
+
+} // namespace ZeroTier
diff --git a/node/Tag.hpp b/node/Tag.hpp
new file mode 100644
index 00000000..d2e932c2
--- /dev/null
+++ b/node/Tag.hpp
@@ -0,0 +1,210 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
+ */
+
+#ifndef ZT_TAG_HPP
+#define ZT_TAG_HPP
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "Constants.hpp"
+#include "Credential.hpp"
+#include "C25519.hpp"
+#include "Address.hpp"
+#include "Identity.hpp"
+#include "Buffer.hpp"
+
+namespace ZeroTier {
+
+class RuntimeEnvironment;
+
+/**
+ * A tag that can be associated with members and matched in rules
+ *
+ * Capabilities group rules, while tags group members subject to those
+ * rules. Tag values can be matched in rules, and tags relevant to a
+ * capability are presented along with it.
+ *
+ * E.g. a capability might be "can speak Samba/CIFS within your
+ * department." This cap might have a rule to allow TCP/137 but
+ * only if a given tag ID's value matches between two peers. The
+ * capability is what members can do, while the tag is who they are.
+ * Different departments might have tags with the same ID but different
+ * values.
+ *
+ * Unlike capabilities tags are signed only by the issuer and are never
+ * transferrable.
+ */
+class Tag : public Credential
+{
+public:
+ static inline Credential::Type credentialType() { return Credential::CREDENTIAL_TYPE_TAG; }
+
+ Tag()
+ {
+ memset(this,0,sizeof(Tag));
+ }
+
+ /**
+ * @param nwid Network ID
+ * @param ts Timestamp
+ * @param issuedTo Address to which this tag was issued
+ * @param id Tag ID
+ * @param value Tag value
+ */
+ Tag(const uint64_t nwid,const int64_t ts,const Address &issuedTo,const uint32_t id,const uint32_t value) :
+ _id(id),
+ _value(value),
+ _networkId(nwid),
+ _ts(ts),
+ _issuedTo(issuedTo),
+ _signedBy()
+ {
+ }
+
+ inline uint32_t id() const { return _id; }
+ inline const uint32_t &value() const { return _value; }
+ inline uint64_t networkId() const { return _networkId; }
+ inline int64_t timestamp() const { return _ts; }
+ inline const Address &issuedTo() const { return _issuedTo; }
+ inline const Address &signedBy() const { return _signedBy; }
+
+ /**
+ * Sign this tag
+ *
+ * @param signer Signing identity, must have private key
+ * @return True if signature was successful
+ */
+ inline bool sign(const Identity &signer)
+ {
+ if (signer.hasPrivate()) {
+ Buffer<sizeof(Tag) + 64> tmp;
+ _signedBy = signer.address();
+ this->serialize(tmp,true);
+ _signature = signer.sign(tmp.data(),tmp.size());
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Check this tag's signature
+ *
+ * @param RR Runtime environment to allow identity lookup for signedBy
+ * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
+ * @return 0 == OK, 1 == waiting for WHOIS, -1 == BAD signature or tag
+ */
+ int verify(const RuntimeEnvironment *RR,void *tPtr) const;
+
+ template<unsigned int C>
+ inline void serialize(Buffer<C> &b,const bool forSign = false) const
+ {
+ if (forSign) b.append((uint64_t)0x7f7f7f7f7f7f7f7fULL);
+
+ b.append(_networkId);
+ b.append(_ts);
+ b.append(_id);
+ b.append(_value);
+
+ _issuedTo.appendTo(b);
+ _signedBy.appendTo(b);
+ if (!forSign) {
+ b.append((uint8_t)1); // 1 == Ed25519
+ b.append((uint16_t)ZT_C25519_SIGNATURE_LEN); // length of signature
+ b.append(_signature.data,ZT_C25519_SIGNATURE_LEN);
+ }
+
+ b.append((uint16_t)0); // length of additional fields, currently 0
+
+ if (forSign) b.append((uint64_t)0x7f7f7f7f7f7f7f7fULL);
+ }
+
+ template<unsigned int C>
+ inline unsigned int deserialize(const Buffer<C> &b,unsigned int startAt = 0)
+ {
+ unsigned int p = startAt;
+
+ memset(this,0,sizeof(Tag));
+
+ _networkId = b.template at<uint64_t>(p); p += 8;
+ _ts = b.template at<uint64_t>(p); p += 8;
+ _id = b.template at<uint32_t>(p); p += 4;
+
+ _value = b.template at<uint32_t>(p); p += 4;
+
+ _issuedTo.setTo(b.field(p,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH); p += ZT_ADDRESS_LENGTH;
+ _signedBy.setTo(b.field(p,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH); p += ZT_ADDRESS_LENGTH;
+ if (b[p++] == 1) {
+ if (b.template at<uint16_t>(p) != ZT_C25519_SIGNATURE_LEN)
+ throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_INVALID_CRYPTOGRAPHIC_TOKEN;
+ p += 2;
+ ZT_FAST_MEMCPY(_signature.data,b.field(p,ZT_C25519_SIGNATURE_LEN),ZT_C25519_SIGNATURE_LEN); p += ZT_C25519_SIGNATURE_LEN;
+ } else {
+ p += 2 + b.template at<uint16_t>(p);
+ }
+
+ p += 2 + b.template at<uint16_t>(p);
+ if (p > b.size())
+ throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_OVERFLOW;
+
+ return (p - startAt);
+ }
+
+ // Provides natural sort order by ID
+ inline bool operator<(const Tag &t) const { return (_id < t._id); }
+
+ inline bool operator==(const Tag &t) const { return (memcmp(this,&t,sizeof(Tag)) == 0); }
+ inline bool operator!=(const Tag &t) const { return (memcmp(this,&t,sizeof(Tag)) != 0); }
+
+ // For searching sorted arrays or lists of Tags by ID
+ struct IdComparePredicate
+ {
+ inline bool operator()(const Tag &a,const Tag &b) const { return (a.id() < b.id()); }
+ inline bool operator()(const uint32_t a,const Tag &b) const { return (a < b.id()); }
+ inline bool operator()(const Tag &a,const uint32_t b) const { return (a.id() < b); }
+ inline bool operator()(const Tag *a,const Tag *b) const { return (a->id() < b->id()); }
+ inline bool operator()(const Tag *a,const Tag &b) const { return (a->id() < b.id()); }
+ inline bool operator()(const Tag &a,const Tag *b) const { return (a.id() < b->id()); }
+ inline bool operator()(const uint32_t a,const Tag *b) const { return (a < b->id()); }
+ inline bool operator()(const Tag *a,const uint32_t b) const { return (a->id() < b); }
+ inline bool operator()(const uint32_t a,const uint32_t b) const { return (a < b); }
+ };
+
+private:
+ uint32_t _id;
+ uint32_t _value;
+ uint64_t _networkId;
+ int64_t _ts;
+ Address _issuedTo;
+ Address _signedBy;
+ C25519::Signature _signature;
+};
+
+} // namespace ZeroTier
+
+#endif
diff --git a/node/Topology.cpp b/node/Topology.cpp
index 6e96f2eb..7c526b41 100644
--- a/node/Topology.cpp
+++ b/node/Topology.cpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#include "Constants.hpp"
@@ -23,342 +31,410 @@
#include "Network.hpp"
#include "NetworkConfig.hpp"
#include "Buffer.hpp"
+#include "Switch.hpp"
namespace ZeroTier {
-// 2015-11-16 -- The Fabulous Four (should have named them after Beatles!)
-//#define ZT_DEFAULT_WORLD_LENGTH 494
-//static const unsigned char ZT_DEFAULT_WORLD[ZT_DEFAULT_WORLD_LENGTH] = {0x01,0x00,0x00,0x00,0x00,0x08,0xea,0xc9,0x0a,0x00,0x00,0x01,0x51,0x11,0x70,0xb2,0xfb,0xb8,0xb3,0x88,0xa4,0x69,0x22,0x14,0x91,0xaa,0x9a,0xcd,0x66,0xcc,0x76,0x4c,0xde,0xfd,0x56,0x03,0x9f,0x10,0x67,0xae,0x15,0xe6,0x9c,0x6f,0xb4,0x2d,0x7b,0x55,0x33,0x0e,0x3f,0xda,0xac,0x52,0x9c,0x07,0x92,0xfd,0x73,0x40,0xa6,0xaa,0x21,0xab,0xa8,0xa4,0x89,0xfd,0xae,0xa4,0x4a,0x39,0xbf,0x2d,0x00,0x65,0x9a,0xc9,0xc8,0x18,0xeb,0x80,0x31,0xa4,0x65,0x95,0x45,0x06,0x1c,0xfb,0xc2,0x4e,0x5d,0xe7,0x0a,0x40,0x7a,0x97,0xce,0x36,0xa2,0x3d,0x05,0xca,0x87,0xc7,0x59,0x27,0x5c,0x8b,0x0d,0x4c,0xb4,0xbb,0x26,0x2f,0x77,0x17,0x5e,0xb7,0x4d,0xb8,0xd3,0xb4,0xe9,0x23,0x5d,0xcc,0xa2,0x71,0xa8,0xdf,0xf1,0x23,0xa3,0xb2,0x66,0x74,0xea,0xe5,0xdc,0x8d,0xef,0xd3,0x0a,0xa9,0xac,0xcb,0xda,0x93,0xbd,0x6c,0xcd,0x43,0x1d,0xa7,0x98,0x6a,0xde,0x70,0xc0,0xc6,0x1c,0xaf,0xf0,0xfd,0x7f,0x8a,0xb9,0x76,0x13,0xe1,0xde,0x4f,0xf3,0xd6,0x13,0x04,0x7e,0x19,0x87,0x6a,0xba,0x00,0x2a,0x6e,0x2b,0x23,0x18,0x93,0x0f,0x60,0xeb,0x09,0x7f,0x70,0xd0,0xf4,0xb0,0x28,0xb2,0xcd,0x6d,0x3d,0x0c,0x63,0xc0,0x14,0xb9,0x03,0x9f,0xf3,0x53,0x90,0xe4,0x11,0x81,0xf2,0x16,0xfb,0x2e,0x6f,0xa8,0xd9,0x5c,0x1e,0xe9,0x66,0x71,0x56,0x41,0x19,0x05,0xc3,0xdc,0xcf,0xea,0x78,0xd8,0xc6,0xdf,0xaf,0xba,0x68,0x81,0x70,0xb3,0xfa,0x00,0x01,0x04,0xc6,0xc7,0x61,0xdc,0x27,0x09,0x88,0x41,0x40,0x8a,0x2e,0x00,0xbb,0x1d,0x31,0xf2,0xc3,0x23,0xe2,0x64,0xe9,0xe6,0x41,0x72,0xc1,0xa7,0x4f,0x77,0x89,0x95,0x55,0xed,0x10,0x75,0x1c,0xd5,0x6e,0x86,0x40,0x5c,0xde,0x11,0x8d,0x02,0xdf,0xfe,0x55,0x5d,0x46,0x2c,0xcf,0x6a,0x85,0xb5,0x63,0x1c,0x12,0x35,0x0c,0x8d,0x5d,0xc4,0x09,0xba,0x10,0xb9,0x02,0x5d,0x0f,0x44,0x5c,0xf4,0x49,0xd9,0x2b,0x1c,0x00,0x01,0x04,0x6b,0xbf,0x2e,0xd2,0x27,0x09,0x8a,0xcf,0x05,0x9f,0xe3,0x00,0x48,0x2f,0x6e,0xe5,0xdf,0xe9,0x02,0x31,0x9b,0x41,0x9d,0xe5,0xbd,0xc7,0x65,0x20,0x9c,0x0e,0xcd,0xa3,0x8c,0x4d,0x6e,0x4f,0xcf,0x0d,0x33,0x65,0x83,0x98,0xb4,0x52,0x7d,0xcd,0x22,0xf9,0x31,0x12,0xfb,0x9b,0xef,0xd0,0x2f,0xd7,0x8b,0xf7,0x26,0x1b,0x33,0x3f,0xc1,0x05,0xd1,0x92,0xa6,0x23,0xca,0x9e,0x50,0xfc,0x60,0xb3,0x74,0xa5,0x00,0x01,0x04,0xa2,0xf3,0x4d,0x6f,0x27,0x09,0x9d,0x21,0x90,0x39,0xf3,0x00,0x01,0xf0,0x92,0x2a,0x98,0xe3,0xb3,0x4e,0xbc,0xbf,0xf3,0x33,0x26,0x9d,0xc2,0x65,0xd7,0xa0,0x20,0xaa,0xb6,0x9d,0x72,0xbe,0x4d,0x4a,0xcc,0x9c,0x8c,0x92,0x94,0x78,0x57,0x71,0x25,0x6c,0xd1,0xd9,0x42,0xa9,0x0d,0x1b,0xd1,0xd2,0xdc,0xa3,0xea,0x84,0xef,0x7d,0x85,0xaf,0xe6,0x61,0x1f,0xb4,0x3f,0xf0,0xb7,0x41,0x26,0xd9,0x0a,0x6e,0x00,0x01,0x04,0x80,0xc7,0xc5,0xd9,0x27,0x09};
-
-// 2015-11-20 -- Alice and Bob are live, and we're now IPv6 dual-stack!
-//#define ZT_DEFAULT_WORLD_LENGTH 792
-//static const unsigned char ZT_DEFAULT_WORLD[ZT_DEFAULT_WORLD_LENGTH] = {0x01,0x00,0x00,0x00,0x00,0x08,0xea,0xc9,0x0a,0x00,0x00,0x01,0x51,0x26,0x6f,0x7c,0x8a,0xb8,0xb3,0x88,0xa4,0x69,0x22,0x14,0x91,0xaa,0x9a,0xcd,0x66,0xcc,0x76,0x4c,0xde,0xfd,0x56,0x03,0x9f,0x10,0x67,0xae,0x15,0xe6,0x9c,0x6f,0xb4,0x2d,0x7b,0x55,0x33,0x0e,0x3f,0xda,0xac,0x52,0x9c,0x07,0x92,0xfd,0x73,0x40,0xa6,0xaa,0x21,0xab,0xa8,0xa4,0x89,0xfd,0xae,0xa4,0x4a,0x39,0xbf,0x2d,0x00,0x65,0x9a,0xc9,0xc8,0x18,0xeb,0xe8,0x0a,0xf5,0xbc,0xf8,0x3d,0x97,0xcd,0xc3,0xf8,0xe2,0x41,0x16,0x42,0x0f,0xc7,0x76,0x8e,0x07,0xf3,0x7e,0x9e,0x7d,0x1b,0xb3,0x23,0x21,0x79,0xce,0xb9,0xd0,0xcb,0xb5,0x94,0x7b,0x89,0x21,0x57,0x72,0xf6,0x70,0xa1,0xdd,0x67,0x38,0xcf,0x45,0x45,0xc2,0x8d,0x46,0xec,0x00,0x2c,0xe0,0x2a,0x63,0x3f,0x63,0x8d,0x33,0x08,0x51,0x07,0x77,0x81,0x5b,0x32,0x49,0xae,0x87,0x89,0xcf,0x31,0xaa,0x41,0xf1,0x52,0x97,0xdc,0xa2,0x55,0xe1,0x4a,0x6e,0x3c,0x04,0xf0,0x4f,0x8a,0x0e,0xe9,0xca,0xec,0x24,0x30,0x04,0x9d,0x21,0x90,0x39,0xf3,0x00,0x01,0xf0,0x92,0x2a,0x98,0xe3,0xb3,0x4e,0xbc,0xbf,0xf3,0x33,0x26,0x9d,0xc2,0x65,0xd7,0xa0,0x20,0xaa,0xb6,0x9d,0x72,0xbe,0x4d,0x4a,0xcc,0x9c,0x8c,0x92,0x94,0x78,0x57,0x71,0x25,0x6c,0xd1,0xd9,0x42,0xa9,0x0d,0x1b,0xd1,0xd2,0xdc,0xa3,0xea,0x84,0xef,0x7d,0x85,0xaf,0xe6,0x61,0x1f,0xb4,0x3f,0xf0,0xb7,0x41,0x26,0xd9,0x0a,0x6e,0x00,0x0c,0x04,0xbc,0xa6,0x5e,0xb1,0x27,0x09,0x06,0x2a,0x03,0xb0,0xc0,0x00,0x02,0x00,0xd0,0x00,0x00,0x00,0x00,0x00,0x7d,0x00,0x01,0x27,0x09,0x04,0x9a,0x42,0xc5,0x21,0x27,0x09,0x06,0x2c,0x0f,0xf8,0x50,0x01,0x54,0x01,0x97,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x33,0x27,0x09,0x04,0x9f,0xcb,0x61,0xab,0x27,0x09,0x06,0x26,0x04,0xa8,0x80,0x08,0x00,0x00,0xa1,0x00,0x00,0x00,0x00,0x00,0x54,0x60,0x01,0x27,0x09,0x04,0xa9,0x39,0x8f,0x68,0x27,0x09,0x06,0x26,0x07,0xf0,0xd0,0x1d,0x01,0x00,0x57,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x27,0x09,0x04,0x6b,0xaa,0xc5,0x0e,0x27,0x09,0x06,0x26,0x04,0xa8,0x80,0x00,0x01,0x00,0x20,0x00,0x00,0x00,0x00,0x02,0x00,0xe0,0x01,0x27,0x09,0x04,0x80,0xc7,0xc5,0xd9,0x27,0x09,0x06,0x24,0x00,0x61,0x80,0x00,0x00,0x00,0xd0,0x00,0x00,0x00,0x00,0x00,0xb7,0x40,0x01,0x27,0x09,0x88,0x41,0x40,0x8a,0x2e,0x00,0xbb,0x1d,0x31,0xf2,0xc3,0x23,0xe2,0x64,0xe9,0xe6,0x41,0x72,0xc1,0xa7,0x4f,0x77,0x89,0x95,0x55,0xed,0x10,0x75,0x1c,0xd5,0x6e,0x86,0x40,0x5c,0xde,0x11,0x8d,0x02,0xdf,0xfe,0x55,0x5d,0x46,0x2c,0xcf,0x6a,0x85,0xb5,0x63,0x1c,0x12,0x35,0x0c,0x8d,0x5d,0xc4,0x09,0xba,0x10,0xb9,0x02,0x5d,0x0f,0x44,0x5c,0xf4,0x49,0xd9,0x2b,0x1c,0x00,0x0c,0x04,0x2d,0x20,0xc6,0x82,0x27,0x09,0x06,0x20,0x01,0x19,0xf0,0x64,0x00,0x81,0xc3,0x54,0x00,0x00,0xff,0xfe,0x18,0x1d,0x61,0x27,0x09,0x04,0x2e,0x65,0xa0,0xf9,0x27,0x09,0x06,0x2a,0x03,0xb0,0xc0,0x00,0x03,0x00,0xd0,0x00,0x00,0x00,0x00,0x00,0x6a,0x30,0x01,0x27,0x09,0x04,0x6b,0xbf,0x2e,0xd2,0x27,0x09,0x06,0x20,0x01,0x19,0xf0,0x68,0x00,0x83,0xa4,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x64,0x27,0x09,0x04,0x2d,0x20,0xf6,0xb3,0x27,0x09,0x06,0x20,0x01,0x19,0xf0,0x58,0x00,0x8b,0xf8,0x54,0x00,0x00,0xff,0xfe,0x15,0xb3,0x9a,0x27,0x09,0x04,0x2d,0x20,0xf8,0x57,0x27,0x09,0x06,0x20,0x01,0x19,0xf0,0x70,0x00,0x9b,0xc9,0x54,0x00,0x00,0xff,0xfe,0x15,0xc4,0xf5,0x27,0x09,0x04,0x9f,0xcb,0x02,0x9a,0x27,0x09,0x06,0x26,0x04,0xa8,0x80,0x0c,0xad,0x00,0xd0,0x00,0x00,0x00,0x00,0x00,0x26,0x70,0x01,0x27,0x09,0x7e,0x19,0x87,0x6a,0xba,0x00,0x2a,0x6e,0x2b,0x23,0x18,0x93,0x0f,0x60,0xeb,0x09,0x7f,0x70,0xd0,0xf4,0xb0,0x28,0xb2,0xcd,0x6d,0x3d,0x0c,0x63,0xc0,0x14,0xb9,0x03,0x9f,0xf3,0x53,0x90,0xe4,0x11,0x81,0xf2,0x16,0xfb,0x2e,0x6f,0xa8,0xd9,0x5c,0x1e,0xe9,0x66,0x71,0x56,0x41,0x19,0x05,0xc3,0xdc,0xcf,0xea,0x78,0xd8,0xc6,0xdf,0xaf,0xba,0x68,0x81,0x70,0xb3,0xfa,0x00,0x01,0x04,0xc6,0xc7,0x61,0xdc,0x27,0x09,0x8a,0xcf,0x05,0x9f,0xe3,0x00,0x48,0x2f,0x6e,0xe5,0xdf,0xe9,0x02,0x31,0x9b,0x41,0x9d,0xe5,0xbd,0xc7,0x65,0x20,0x9c,0x0e,0xcd,0xa3,0x8c,0x4d,0x6e,0x4f,0xcf,0x0d,0x33,0x65,0x83,0x98,0xb4,0x52,0x7d,0xcd,0x22,0xf9,0x31,0x12,0xfb,0x9b,0xef,0xd0,0x2f,0xd7,0x8b,0xf7,0x26,0x1b,0x33,0x3f,0xc1,0x05,0xd1,0x92,0xa6,0x23,0xca,0x9e,0x50,0xfc,0x60,0xb3,0x74,0xa5,0x00,0x01,0x04,0xa2,0xf3,0x4d,0x6f,0x27,0x09};
-
-// 2015-12-17 -- Old New York root is dead, old SF still alive
-//#define ZT_DEFAULT_WORLD_LENGTH 732
-//static const unsigned char ZT_DEFAULT_WORLD[ZT_DEFAULT_WORLD_LENGTH] = {0x01,0x00,0x00,0x00,0x00,0x08,0xea,0xc9,0x0a,0x00,0x00,0x01,0x51,0xb1,0x7e,0x39,0x9d,0xb8,0xb3,0x88,0xa4,0x69,0x22,0x14,0x91,0xaa,0x9a,0xcd,0x66,0xcc,0x76,0x4c,0xde,0xfd,0x56,0x03,0x9f,0x10,0x67,0xae,0x15,0xe6,0x9c,0x6f,0xb4,0x2d,0x7b,0x55,0x33,0x0e,0x3f,0xda,0xac,0x52,0x9c,0x07,0x92,0xfd,0x73,0x40,0xa6,0xaa,0x21,0xab,0xa8,0xa4,0x89,0xfd,0xae,0xa4,0x4a,0x39,0xbf,0x2d,0x00,0x65,0x9a,0xc9,0xc8,0x18,0xeb,0x8a,0xca,0xf2,0x3d,0x71,0x2e,0xc2,0x39,0x45,0x66,0xb3,0xe9,0x39,0x79,0xb1,0x55,0xc4,0xa9,0xfc,0xbc,0xfc,0x55,0xaf,0x8a,0x2f,0x38,0xc8,0xcd,0xe9,0x02,0x5b,0x86,0xa9,0x72,0xf7,0x16,0x00,0x35,0xb7,0x84,0xc9,0xfc,0xe4,0xfa,0x96,0x8b,0xf4,0x1e,0xba,0x60,0x9f,0x85,0x14,0xc2,0x07,0x4b,0xfd,0xd1,0x6c,0x19,0x69,0xd3,0xf9,0x09,0x9c,0x9d,0xe3,0xb9,0x8f,0x11,0x78,0x71,0xa7,0x4a,0x05,0xd8,0xcc,0x60,0xa2,0x06,0x66,0x9f,0x47,0xc2,0x71,0xb8,0x54,0x80,0x9c,0x45,0x16,0x10,0xa9,0xd0,0xbd,0xf7,0x03,0x9d,0x21,0x90,0x39,0xf3,0x00,0x01,0xf0,0x92,0x2a,0x98,0xe3,0xb3,0x4e,0xbc,0xbf,0xf3,0x33,0x26,0x9d,0xc2,0x65,0xd7,0xa0,0x20,0xaa,0xb6,0x9d,0x72,0xbe,0x4d,0x4a,0xcc,0x9c,0x8c,0x92,0x94,0x78,0x57,0x71,0x25,0x6c,0xd1,0xd9,0x42,0xa9,0x0d,0x1b,0xd1,0xd2,0xdc,0xa3,0xea,0x84,0xef,0x7d,0x85,0xaf,0xe6,0x61,0x1f,0xb4,0x3f,0xf0,0xb7,0x41,0x26,0xd9,0x0a,0x6e,0x00,0x0c,0x04,0xbc,0xa6,0x5e,0xb1,0x27,0x09,0x06,0x2a,0x03,0xb0,0xc0,0x00,0x02,0x00,0xd0,0x00,0x00,0x00,0x00,0x00,0x7d,0x00,0x01,0x27,0x09,0x04,0x9a,0x42,0xc5,0x21,0x27,0x09,0x06,0x2c,0x0f,0xf8,0x50,0x01,0x54,0x01,0x97,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x33,0x27,0x09,0x04,0x9f,0xcb,0x61,0xab,0x27,0x09,0x06,0x26,0x04,0xa8,0x80,0x08,0x00,0x00,0xa1,0x00,0x00,0x00,0x00,0x00,0x54,0x60,0x01,0x27,0x09,0x04,0xa9,0x39,0x8f,0x68,0x27,0x09,0x06,0x26,0x07,0xf0,0xd0,0x1d,0x01,0x00,0x57,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x27,0x09,0x04,0x6b,0xaa,0xc5,0x0e,0x27,0x09,0x06,0x26,0x04,0xa8,0x80,0x00,0x01,0x00,0x20,0x00,0x00,0x00,0x00,0x02,0x00,0xe0,0x01,0x27,0x09,0x04,0x80,0xc7,0xc5,0xd9,0x27,0x09,0x06,0x24,0x00,0x61,0x80,0x00,0x00,0x00,0xd0,0x00,0x00,0x00,0x00,0x00,0xb7,0x40,0x01,0x27,0x09,0x88,0x41,0x40,0x8a,0x2e,0x00,0xbb,0x1d,0x31,0xf2,0xc3,0x23,0xe2,0x64,0xe9,0xe6,0x41,0x72,0xc1,0xa7,0x4f,0x77,0x89,0x95,0x55,0xed,0x10,0x75,0x1c,0xd5,0x6e,0x86,0x40,0x5c,0xde,0x11,0x8d,0x02,0xdf,0xfe,0x55,0x5d,0x46,0x2c,0xcf,0x6a,0x85,0xb5,0x63,0x1c,0x12,0x35,0x0c,0x8d,0x5d,0xc4,0x09,0xba,0x10,0xb9,0x02,0x5d,0x0f,0x44,0x5c,0xf4,0x49,0xd9,0x2b,0x1c,0x00,0x0c,0x04,0x2d,0x20,0xc6,0x82,0x27,0x09,0x06,0x20,0x01,0x19,0xf0,0x64,0x00,0x81,0xc3,0x54,0x00,0x00,0xff,0xfe,0x18,0x1d,0x61,0x27,0x09,0x04,0x2e,0x65,0xa0,0xf9,0x27,0x09,0x06,0x2a,0x03,0xb0,0xc0,0x00,0x03,0x00,0xd0,0x00,0x00,0x00,0x00,0x00,0x6a,0x30,0x01,0x27,0x09,0x04,0x6b,0xbf,0x2e,0xd2,0x27,0x09,0x06,0x20,0x01,0x19,0xf0,0x68,0x00,0x83,0xa4,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x64,0x27,0x09,0x04,0x2d,0x20,0xf6,0xb3,0x27,0x09,0x06,0x20,0x01,0x19,0xf0,0x58,0x00,0x8b,0xf8,0x54,0x00,0x00,0xff,0xfe,0x15,0xb3,0x9a,0x27,0x09,0x04,0x2d,0x20,0xf8,0x57,0x27,0x09,0x06,0x20,0x01,0x19,0xf0,0x70,0x00,0x9b,0xc9,0x54,0x00,0x00,0xff,0xfe,0x15,0xc4,0xf5,0x27,0x09,0x04,0x9f,0xcb,0x02,0x9a,0x27,0x09,0x06,0x26,0x04,0xa8,0x80,0x0c,0xad,0x00,0xd0,0x00,0x00,0x00,0x00,0x00,0x26,0x70,0x01,0x27,0x09,0x7e,0x19,0x87,0x6a,0xba,0x00,0x2a,0x6e,0x2b,0x23,0x18,0x93,0x0f,0x60,0xeb,0x09,0x7f,0x70,0xd0,0xf4,0xb0,0x28,0xb2,0xcd,0x6d,0x3d,0x0c,0x63,0xc0,0x14,0xb9,0x03,0x9f,0xf3,0x53,0x90,0xe4,0x11,0x81,0xf2,0x16,0xfb,0x2e,0x6f,0xa8,0xd9,0x5c,0x1e,0xe9,0x66,0x71,0x56,0x41,0x19,0x05,0xc3,0xdc,0xcf,0xea,0x78,0xd8,0xc6,0xdf,0xaf,0xba,0x68,0x81,0x70,0xb3,0xfa,0x00,0x02,0x04,0xc6,0xc7,0x61,0xdc,0x27,0x09,0x06,0x26,0x04,0xa8,0x80,0x00,0x01,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0xc5,0xf0,0x01,0x27,0x09};
-
-// 2016-01-13 -- Old San Francisco 1.0.1 root is dead, now we're just on Alice and Bob!
+/*
+ * 2016-01-13 ZeroTier planet definition for the third planet of Sol:
+ *
+ * There are two roots, each of which is a cluster spread across multiple
+ * continents and providers. They are named Alice and Bob after the
+ * canonical example names used in cryptography.
+ *
+ * Alice:
+ *
+ * root-alice-ams-01: Amsterdam, Netherlands
+ * root-alice-joh-01: Johannesburg, South Africa
+ * root-alice-nyc-01: New York, New York, USA
+ * root-alice-sao-01: Sao Paolo, Brazil
+ * root-alice-sfo-01: San Francisco, California, USA
+ * root-alice-sgp-01: Singapore
+ *
+ * Bob:
+ *
+ * root-bob-dfw-01: Dallas, Texas, USA
+ * root-bob-fra-01: Frankfurt, Germany
+ * root-bob-par-01: Paris, France
+ * root-bob-syd-01: Sydney, Australia
+ * root-bob-tok-01: Tokyo, Japan
+ * root-bob-tor-01: Toronto, Canada
+ */
#define ZT_DEFAULT_WORLD_LENGTH 634
static const unsigned char ZT_DEFAULT_WORLD[ZT_DEFAULT_WORLD_LENGTH] = {0x01,0x00,0x00,0x00,0x00,0x08,0xea,0xc9,0x0a,0x00,0x00,0x01,0x52,0x3c,0x32,0x50,0x1a,0xb8,0xb3,0x88,0xa4,0x69,0x22,0x14,0x91,0xaa,0x9a,0xcd,0x66,0xcc,0x76,0x4c,0xde,0xfd,0x56,0x03,0x9f,0x10,0x67,0xae,0x15,0xe6,0x9c,0x6f,0xb4,0x2d,0x7b,0x55,0x33,0x0e,0x3f,0xda,0xac,0x52,0x9c,0x07,0x92,0xfd,0x73,0x40,0xa6,0xaa,0x21,0xab,0xa8,0xa4,0x89,0xfd,0xae,0xa4,0x4a,0x39,0xbf,0x2d,0x00,0x65,0x9a,0xc9,0xc8,0x18,0xeb,0x4a,0xf7,0x86,0xa8,0x40,0xd6,0x52,0xea,0xae,0x9e,0x7a,0xbf,0x4c,0x97,0x66,0xab,0x2d,0x6f,0xaf,0xc9,0x2b,0x3a,0xff,0xed,0xd6,0x30,0x3e,0xc4,0x6a,0x65,0xf2,0xbd,0x83,0x52,0xf5,0x40,0xe9,0xcc,0x0d,0x6e,0x89,0x3f,0x9a,0xa0,0xb8,0xdf,0x42,0xd2,0x2f,0x84,0xe6,0x03,0x26,0x0f,0xa8,0xe3,0xcc,0x05,0x05,0x03,0xef,0x12,0x80,0x0d,0xce,0x3e,0xb6,0x58,0x3b,0x1f,0xa8,0xad,0xc7,0x25,0xf9,0x43,0x71,0xa7,0x5c,0x9a,0xc7,0xe1,0xa3,0xb8,0x88,0xd0,0x71,0x6c,0x94,0x99,0x73,0x41,0x0b,0x1b,0x48,0x84,0x02,0x9d,0x21,0x90,0x39,0xf3,0x00,0x01,0xf0,0x92,0x2a,0x98,0xe3,0xb3,0x4e,0xbc,0xbf,0xf3,0x33,0x26,0x9d,0xc2,0x65,0xd7,0xa0,0x20,0xaa,0xb6,0x9d,0x72,0xbe,0x4d,0x4a,0xcc,0x9c,0x8c,0x92,0x94,0x78,0x57,0x71,0x25,0x6c,0xd1,0xd9,0x42,0xa9,0x0d,0x1b,0xd1,0xd2,0xdc,0xa3,0xea,0x84,0xef,0x7d,0x85,0xaf,0xe6,0x61,0x1f,0xb4,0x3f,0xf0,0xb7,0x41,0x26,0xd9,0x0a,0x6e,0x00,0x0c,0x04,0xbc,0xa6,0x5e,0xb1,0x27,0x09,0x06,0x2a,0x03,0xb0,0xc0,0x00,0x02,0x00,0xd0,0x00,0x00,0x00,0x00,0x00,0x7d,0x00,0x01,0x27,0x09,0x04,0x9a,0x42,0xc5,0x21,0x27,0x09,0x06,0x2c,0x0f,0xf8,0x50,0x01,0x54,0x01,0x97,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x33,0x27,0x09,0x04,0x9f,0xcb,0x61,0xab,0x27,0x09,0x06,0x26,0x04,0xa8,0x80,0x08,0x00,0x00,0xa1,0x00,0x00,0x00,0x00,0x00,0x54,0x60,0x01,0x27,0x09,0x04,0xa9,0x39,0x8f,0x68,0x27,0x09,0x06,0x26,0x07,0xf0,0xd0,0x1d,0x01,0x00,0x57,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x27,0x09,0x04,0x6b,0xaa,0xc5,0x0e,0x27,0x09,0x06,0x26,0x04,0xa8,0x80,0x00,0x01,0x00,0x20,0x00,0x00,0x00,0x00,0x02,0x00,0xe0,0x01,0x27,0x09,0x04,0x80,0xc7,0xc5,0xd9,0x27,0x09,0x06,0x24,0x00,0x61,0x80,0x00,0x00,0x00,0xd0,0x00,0x00,0x00,0x00,0x00,0xb7,0x40,0x01,0x27,0x09,0x88,0x41,0x40,0x8a,0x2e,0x00,0xbb,0x1d,0x31,0xf2,0xc3,0x23,0xe2,0x64,0xe9,0xe6,0x41,0x72,0xc1,0xa7,0x4f,0x77,0x89,0x95,0x55,0xed,0x10,0x75,0x1c,0xd5,0x6e,0x86,0x40,0x5c,0xde,0x11,0x8d,0x02,0xdf,0xfe,0x55,0x5d,0x46,0x2c,0xcf,0x6a,0x85,0xb5,0x63,0x1c,0x12,0x35,0x0c,0x8d,0x5d,0xc4,0x09,0xba,0x10,0xb9,0x02,0x5d,0x0f,0x44,0x5c,0xf4,0x49,0xd9,0x2b,0x1c,0x00,0x0c,0x04,0x2d,0x20,0xc6,0x82,0x27,0x09,0x06,0x20,0x01,0x19,0xf0,0x64,0x00,0x81,0xc3,0x54,0x00,0x00,0xff,0xfe,0x18,0x1d,0x61,0x27,0x09,0x04,0x2e,0x65,0xa0,0xf9,0x27,0x09,0x06,0x2a,0x03,0xb0,0xc0,0x00,0x03,0x00,0xd0,0x00,0x00,0x00,0x00,0x00,0x6a,0x30,0x01,0x27,0x09,0x04,0x6b,0xbf,0x2e,0xd2,0x27,0x09,0x06,0x20,0x01,0x19,0xf0,0x68,0x00,0x83,0xa4,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x64,0x27,0x09,0x04,0x2d,0x20,0xf6,0xb3,0x27,0x09,0x06,0x20,0x01,0x19,0xf0,0x58,0x00,0x8b,0xf8,0x54,0x00,0x00,0xff,0xfe,0x15,0xb3,0x9a,0x27,0x09,0x04,0x2d,0x20,0xf8,0x57,0x27,0x09,0x06,0x20,0x01,0x19,0xf0,0x70,0x00,0x9b,0xc9,0x54,0x00,0x00,0xff,0xfe,0x15,0xc4,0xf5,0x27,0x09,0x04,0x9f,0xcb,0x02,0x9a,0x27,0x09,0x06,0x26,0x04,0xa8,0x80,0x0c,0xad,0x00,0xd0,0x00,0x00,0x00,0x00,0x00,0x26,0x70,0x01,0x27,0x09};
-Topology::Topology(const RuntimeEnvironment *renv) :
+Topology::Topology(const RuntimeEnvironment *renv,void *tPtr) :
RR(renv),
- _trustedPathCount(0),
- _amRoot(false)
+ _numConfiguredPhysicalPaths(0),
+ _amUpstream(false)
{
- std::string alls(RR->node->dataStoreGet("peers.save"));
- const uint8_t *all = reinterpret_cast<const uint8_t *>(alls.data());
- RR->node->dataStoreDelete("peers.save");
-
- Buffer<ZT_PEER_SUGGESTED_SERIALIZATION_BUFFER_SIZE> *deserializeBuf = new Buffer<ZT_PEER_SUGGESTED_SERIALIZATION_BUFFER_SIZE>();
- unsigned int ptr = 0;
- while ((ptr + 4) < alls.size()) {
+ uint8_t tmp[ZT_WORLD_MAX_SERIALIZED_LENGTH];
+ uint64_t idtmp[2];
+ idtmp[0] = 0; idtmp[1] = 0;
+ int n = RR->node->stateObjectGet(tPtr,ZT_STATE_OBJECT_PLANET,idtmp,tmp,sizeof(tmp));
+ if (n > 0) {
try {
- const unsigned int reclen = ( // each Peer serialized record is prefixed by a record length
- ((((unsigned int)all[ptr]) & 0xff) << 24) |
- ((((unsigned int)all[ptr + 1]) & 0xff) << 16) |
- ((((unsigned int)all[ptr + 2]) & 0xff) << 8) |
- (((unsigned int)all[ptr + 3]) & 0xff)
- );
- unsigned int pos = 0;
- deserializeBuf->copyFrom(all + ptr,reclen + 4);
- SharedPtr<Peer> p(Peer::deserializeNew(RR,RR->identity,*deserializeBuf,pos));
- ptr += pos;
- if (!p)
- break; // stop if invalid records
- if (p->address() != RR->identity.address())
- _peers.set(p->address(),p);
- } catch ( ... ) {
- break; // stop if invalid records
- }
+ World cachedPlanet;
+ cachedPlanet.deserialize(Buffer<ZT_WORLD_MAX_SERIALIZED_LENGTH>(tmp,(unsigned int)n),0);
+ addWorld(tPtr,cachedPlanet,false);
+ } catch ( ... ) {} // ignore invalid cached planets
}
- delete deserializeBuf;
-
- clean(RR->node->now());
- std::string dsWorld(RR->node->dataStoreGet("world"));
- World cachedWorld;
- if (dsWorld.length() > 0) {
- try {
- Buffer<ZT_WORLD_MAX_SERIALIZED_LENGTH> dswtmp(dsWorld.data(),(unsigned int)dsWorld.length());
- cachedWorld.deserialize(dswtmp,0);
- } catch ( ... ) {
- cachedWorld = World(); // clear if cached world is invalid
- }
- }
- World defaultWorld;
+ World defaultPlanet;
{
Buffer<ZT_DEFAULT_WORLD_LENGTH> wtmp(ZT_DEFAULT_WORLD,ZT_DEFAULT_WORLD_LENGTH);
- defaultWorld.deserialize(wtmp,0); // throws on error, which would indicate a bad static variable up top
+ defaultPlanet.deserialize(wtmp,0); // throws on error, which would indicate a bad static variable up top
}
- if (cachedWorld.shouldBeReplacedBy(defaultWorld,false)) {
- _setWorld(defaultWorld);
- if (dsWorld.length() > 0)
- RR->node->dataStoreDelete("world");
- } else _setWorld(cachedWorld);
+ addWorld(tPtr,defaultPlanet,false);
}
Topology::~Topology()
{
- Buffer<ZT_PEER_SUGGESTED_SERIALIZATION_BUFFER_SIZE> *pbuf = 0;
- try {
- pbuf = new Buffer<ZT_PEER_SUGGESTED_SERIALIZATION_BUFFER_SIZE>();
- std::string all;
-
- Address *a = (Address *)0;
- SharedPtr<Peer> *p = (SharedPtr<Peer> *)0;
- Hashtable< Address,SharedPtr<Peer> >::Iterator i(_peers);
- while (i.next(a,p)) {
- if (std::find(_rootAddresses.begin(),_rootAddresses.end(),*a) == _rootAddresses.end()) {
- pbuf->clear();
- try {
- (*p)->serialize(*pbuf);
- try {
- all.append((const char *)pbuf->data(),pbuf->size());
- } catch ( ... ) {
- return; // out of memory? just skip
- }
- } catch ( ... ) {} // peer too big? shouldn't happen, but it so skip
- }
- }
-
- RR->node->dataStorePut("peers.save",all,true);
-
- delete pbuf;
- } catch ( ... ) {
- delete pbuf;
- }
+ Hashtable< Address,SharedPtr<Peer> >::Iterator i(_peers);
+ Address *a = (Address *)0;
+ SharedPtr<Peer> *p = (SharedPtr<Peer> *)0;
+ while (i.next(a,p))
+ _savePeer((void *)0,*p);
}
-SharedPtr<Peer> Topology::addPeer(const SharedPtr<Peer> &peer)
+SharedPtr<Peer> Topology::addPeer(void *tPtr,const SharedPtr<Peer> &peer)
{
-#ifdef ZT_TRACE
- if ((!peer)||(peer->address() == RR->identity.address())) {
- if (!peer)
- fprintf(stderr,"FATAL BUG: addPeer() caught attempt to add NULL peer" ZT_EOL_S);
- else fprintf(stderr,"FATAL BUG: addPeer() caught attempt to add peer for self" ZT_EOL_S);
- abort();
- }
-#endif
-
SharedPtr<Peer> np;
{
- Mutex::Lock _l(_lock);
+ Mutex::Lock _l(_peers_m);
SharedPtr<Peer> &hp = _peers[peer->address()];
if (!hp)
hp = peer;
np = hp;
}
-
- np->use(RR->node->now());
- saveIdentity(np->identity());
-
return np;
}
-SharedPtr<Peer> Topology::getPeer(const Address &zta)
+SharedPtr<Peer> Topology::getPeer(void *tPtr,const Address &zta)
{
- if (zta == RR->identity.address()) {
- TRACE("BUG: ignored attempt to getPeer() for self, returned NULL");
+ if (zta == RR->identity.address())
return SharedPtr<Peer>();
- }
{
- Mutex::Lock _l(_lock);
+ Mutex::Lock _l(_peers_m);
const SharedPtr<Peer> *const ap = _peers.get(zta);
- if (ap) {
- (*ap)->use(RR->node->now());
+ if (ap)
return *ap;
- }
}
try {
- Identity id(_getIdentity(zta));
- if (id) {
- SharedPtr<Peer> np(new Peer(RR,RR->identity,id));
- {
- Mutex::Lock _l(_lock);
- SharedPtr<Peer> &ap = _peers[zta];
- if (!ap)
- ap.swap(np);
- ap->use(RR->node->now());
+ Buffer<ZT_PEER_MAX_SERIALIZED_STATE_SIZE> buf;
+ uint64_t idbuf[2]; idbuf[0] = zta.toInt(); idbuf[1] = 0;
+ int len = RR->node->stateObjectGet(tPtr,ZT_STATE_OBJECT_PEER,idbuf,buf.unsafeData(),ZT_PEER_MAX_SERIALIZED_STATE_SIZE);
+ if (len > 0) {
+ buf.setSize(len);
+ Mutex::Lock _l(_peers_m);
+ SharedPtr<Peer> &ap = _peers[zta];
+ if (ap)
return ap;
+ ap = Peer::deserializeFromCache(RR->node->now(),tPtr,buf,RR);
+ if (!ap) {
+ _peers.erase(zta);
}
+ return SharedPtr<Peer>();
}
- } catch ( ... ) {
- fprintf(stderr,"EXCEPTION in getPeer() part 2\n");
- abort();
- } // invalid identity on disk?
+ } catch ( ... ) {} // ignore invalid identities or other strage failures
return SharedPtr<Peer>();
}
-Identity Topology::getIdentity(const Address &zta)
+Identity Topology::getIdentity(void *tPtr,const Address &zta)
{
- {
- Mutex::Lock _l(_lock);
+ if (zta == RR->identity.address()) {
+ return RR->identity;
+ } else {
+ Mutex::Lock _l(_peers_m);
const SharedPtr<Peer> *const ap = _peers.get(zta);
if (ap)
return (*ap)->identity();
}
- return _getIdentity(zta);
+ return Identity();
+}
+
+SharedPtr<Peer> Topology::getUpstreamPeer()
+{
+ const int64_t now = RR->node->now();
+ unsigned int bestq = ~((unsigned int)0);
+ const SharedPtr<Peer> *best = (const SharedPtr<Peer> *)0;
+
+ Mutex::Lock _l2(_peers_m);
+ Mutex::Lock _l1(_upstreams_m);
+
+ for(std::vector<Address>::const_iterator a(_upstreamAddresses.begin());a!=_upstreamAddresses.end();++a) {
+ const SharedPtr<Peer> *p = _peers.get(*a);
+ if (p) {
+ const unsigned int q = (*p)->relayQuality(now);
+ if (q <= bestq) {
+ bestq = q;
+ best = p;
+ }
+ }
+ }
+
+ if (!best)
+ return SharedPtr<Peer>();
+ return *best;
+}
+
+bool Topology::isUpstream(const Identity &id) const
+{
+ Mutex::Lock _l(_upstreams_m);
+ return (std::find(_upstreamAddresses.begin(),_upstreamAddresses.end(),id.address()) != _upstreamAddresses.end());
+}
+
+bool Topology::shouldAcceptWorldUpdateFrom(const Address &addr) const
+{
+ Mutex::Lock _l(_upstreams_m);
+ if (std::find(_upstreamAddresses.begin(),_upstreamAddresses.end(),addr) != _upstreamAddresses.end())
+ return true;
+ for(std::vector< std::pair< uint64_t,Address> >::const_iterator s(_moonSeeds.begin());s!=_moonSeeds.end();++s) {
+ if (s->second == addr)
+ return true;
+ }
+ return false;
}
-void Topology::saveIdentity(const Identity &id)
+ZT_PeerRole Topology::role(const Address &ztaddr) const
{
- if (id) {
- char p[128];
- Utils::snprintf(p,sizeof(p),"iddb.d/%.10llx",(unsigned long long)id.address().toInt());
- RR->node->dataStorePut(p,id.toString(false),false);
+ Mutex::Lock _l(_upstreams_m);
+ if (std::find(_upstreamAddresses.begin(),_upstreamAddresses.end(),ztaddr) != _upstreamAddresses.end()) {
+ for(std::vector<World::Root>::const_iterator i(_planet.roots().begin());i!=_planet.roots().end();++i) {
+ if (i->identity.address() == ztaddr)
+ return ZT_PEER_ROLE_PLANET;
+ }
+ return ZT_PEER_ROLE_MOON;
}
+ return ZT_PEER_ROLE_LEAF;
}
-SharedPtr<Peer> Topology::getBestRoot(const Address *avoid,unsigned int avoidCount,bool strictAvoid)
+bool Topology::isProhibitedEndpoint(const Address &ztaddr,const InetAddress &ipaddr) const
{
- const uint64_t now = RR->node->now();
- Mutex::Lock _l(_lock);
-
- if (_amRoot) {
- /* If I am a root server, the "best" root server is the one whose address
- * is numerically greater than mine (with wrap at top of list). This
- * causes packets searching for a route to pretty much literally
- * circumnavigate the globe rather than bouncing between just two. */
-
- for(unsigned long p=0;p<_rootAddresses.size();++p) {
- if (_rootAddresses[p] == RR->identity.address()) {
- for(unsigned long q=1;q<_rootAddresses.size();++q) {
- const SharedPtr<Peer> *const nextsn = _peers.get(_rootAddresses[(p + q) % _rootAddresses.size()]);
- if ((nextsn)&&((*nextsn)->hasActiveDirectPath(now))) {
- (*nextsn)->use(now);
- return *nextsn;
+ Mutex::Lock _l(_upstreams_m);
+
+ // For roots the only permitted addresses are those defined. This adds just a little
+ // bit of extra security against spoofing, replaying, etc.
+ if (std::find(_upstreamAddresses.begin(),_upstreamAddresses.end(),ztaddr) != _upstreamAddresses.end()) {
+ for(std::vector<World::Root>::const_iterator r(_planet.roots().begin());r!=_planet.roots().end();++r) {
+ if (r->identity.address() == ztaddr) {
+ if (r->stableEndpoints.size() == 0)
+ return false; // no stable endpoints specified, so allow dynamic paths
+ for(std::vector<InetAddress>::const_iterator e(r->stableEndpoints.begin());e!=r->stableEndpoints.end();++e) {
+ if (ipaddr.ipsEqual(*e))
+ return false;
+ }
+ }
+ }
+ for(std::vector<World>::const_iterator m(_moons.begin());m!=_moons.end();++m) {
+ for(std::vector<World::Root>::const_iterator r(m->roots().begin());r!=m->roots().end();++r) {
+ if (r->identity.address() == ztaddr) {
+ if (r->stableEndpoints.size() == 0)
+ return false; // no stable endpoints specified, so allow dynamic paths
+ for(std::vector<InetAddress>::const_iterator e(r->stableEndpoints.begin());e!=r->stableEndpoints.end();++e) {
+ if (ipaddr.ipsEqual(*e))
+ return false;
}
}
- break;
}
}
+ return true;
+ }
- } else {
- /* If I am not a root server, the best root server is the active one with
- * the lowest quality score. (lower == better) */
-
- unsigned int bestQualityOverall = ~((unsigned int)0);
- unsigned int bestQualityNotAvoid = ~((unsigned int)0);
- const SharedPtr<Peer> *bestOverall = (const SharedPtr<Peer> *)0;
- const SharedPtr<Peer> *bestNotAvoid = (const SharedPtr<Peer> *)0;
-
- for(std::vector< SharedPtr<Peer> >::const_iterator r(_rootPeers.begin());r!=_rootPeers.end();++r) {
- bool avoiding = false;
- for(unsigned int i=0;i<avoidCount;++i) {
- if (avoid[i] == (*r)->address()) {
- avoiding = true;
+ return false;
+}
+
+bool Topology::addWorld(void *tPtr,const World &newWorld,bool alwaysAcceptNew)
+{
+ if ((newWorld.type() != World::TYPE_PLANET)&&(newWorld.type() != World::TYPE_MOON))
+ return false;
+
+ Mutex::Lock _l2(_peers_m);
+ Mutex::Lock _l1(_upstreams_m);
+
+ World *existing = (World *)0;
+ switch(newWorld.type()) {
+ case World::TYPE_PLANET:
+ existing = &_planet;
+ break;
+ case World::TYPE_MOON:
+ for(std::vector< World >::iterator m(_moons.begin());m!=_moons.end();++m) {
+ if (m->id() == newWorld.id()) {
+ existing = &(*m);
break;
}
}
- const unsigned int q = (*r)->relayQuality(now);
- if (q <= bestQualityOverall) {
- bestQualityOverall = q;
- bestOverall = &(*r);
- }
- if ((!avoiding)&&(q <= bestQualityNotAvoid)) {
- bestQualityNotAvoid = q;
- bestNotAvoid = &(*r);
+ break;
+ default:
+ return false;
+ }
+
+ if (existing) {
+ if (existing->shouldBeReplacedBy(newWorld))
+ *existing = newWorld;
+ else return false;
+ } else if (newWorld.type() == World::TYPE_MOON) {
+ if (alwaysAcceptNew) {
+ _moons.push_back(newWorld);
+ existing = &(_moons.back());
+ } else {
+ for(std::vector< std::pair<uint64_t,Address> >::iterator m(_moonSeeds.begin());m!=_moonSeeds.end();++m) {
+ if (m->first == newWorld.id()) {
+ for(std::vector<World::Root>::const_iterator r(newWorld.roots().begin());r!=newWorld.roots().end();++r) {
+ if (r->identity.address() == m->second) {
+ _moonSeeds.erase(m);
+ _moons.push_back(newWorld);
+ existing = &(_moons.back());
+ break;
+ }
+ }
+ if (existing)
+ break;
+ }
}
}
+ if (!existing)
+ return false;
+ } else {
+ return false;
+ }
- if (bestNotAvoid) {
- (*bestNotAvoid)->use(now);
- return *bestNotAvoid;
- } else if ((!strictAvoid)&&(bestOverall)) {
- (*bestOverall)->use(now);
- return *bestOverall;
- }
+ try {
+ Buffer<ZT_WORLD_MAX_SERIALIZED_LENGTH> sbuf;
+ existing->serialize(sbuf,false);
+ uint64_t idtmp[2];
+ idtmp[0] = existing->id(); idtmp[1] = 0;
+ RR->node->stateObjectPut(tPtr,(existing->type() == World::TYPE_PLANET) ? ZT_STATE_OBJECT_PLANET : ZT_STATE_OBJECT_MOON,idtmp,sbuf.data(),sbuf.size());
+ } catch ( ... ) {}
- }
+ _memoizeUpstreams(tPtr);
- return SharedPtr<Peer>();
+ return true;
}
-bool Topology::isUpstream(const Identity &id) const
+void Topology::addMoon(void *tPtr,const uint64_t id,const Address &seed)
{
- if (isRoot(id))
- return true;
- std::vector< SharedPtr<Network> > nws(RR->node->allNetworks());
- for(std::vector< SharedPtr<Network> >::const_iterator nw(nws.begin());nw!=nws.end();++nw) {
- if ((*nw)->config().isRelay(id.address())) {
- return true;
- }
+ char tmp[ZT_WORLD_MAX_SERIALIZED_LENGTH];
+ uint64_t idtmp[2];
+ idtmp[0] = id; idtmp[1] = 0;
+ int n = RR->node->stateObjectGet(tPtr,ZT_STATE_OBJECT_MOON,idtmp,tmp,sizeof(tmp));
+ if (n > 0) {
+ try {
+ World w;
+ w.deserialize(Buffer<ZT_WORLD_MAX_SERIALIZED_LENGTH>(tmp,(unsigned int)n));
+ if ((w.type() == World::TYPE_MOON)&&(w.id() == id)) {
+ addWorld(tPtr,w,true);
+ return;
+ }
+ } catch ( ... ) {}
}
- return false;
-}
-bool Topology::worldUpdateIfValid(const World &newWorld)
-{
- Mutex::Lock _l(_lock);
- if (_world.shouldBeReplacedBy(newWorld,true)) {
- _setWorld(newWorld);
- try {
- Buffer<ZT_WORLD_MAX_SERIALIZED_LENGTH> dswtmp;
- newWorld.serialize(dswtmp,false);
- RR->node->dataStorePut("world",dswtmp.data(),dswtmp.size(),false);
- } catch ( ... ) {
- RR->node->dataStoreDelete("world");
- }
- return true;
+ if (seed) {
+ Mutex::Lock _l(_upstreams_m);
+ if (std::find(_moonSeeds.begin(),_moonSeeds.end(),std::pair<uint64_t,Address>(id,seed)) == _moonSeeds.end())
+ _moonSeeds.push_back(std::pair<uint64_t,Address>(id,seed));
}
- return false;
}
-void Topology::clean(uint64_t now)
+void Topology::removeMoon(void *tPtr,const uint64_t id)
{
- Mutex::Lock _l(_lock);
- Hashtable< Address,SharedPtr<Peer> >::Iterator i(_peers);
- Address *a = (Address *)0;
- SharedPtr<Peer> *p = (SharedPtr<Peer> *)0;
- while (i.next(a,p)) {
- if (((now - (*p)->lastUsed()) >= ZT_PEER_IN_MEMORY_EXPIRATION)&&(std::find(_rootAddresses.begin(),_rootAddresses.end(),*a) == _rootAddresses.end())) {
- _peers.erase(*a);
+ Mutex::Lock _l2(_peers_m);
+ Mutex::Lock _l1(_upstreams_m);
+
+ std::vector<World> nm;
+ for(std::vector<World>::const_iterator m(_moons.begin());m!=_moons.end();++m) {
+ if (m->id() != id) {
+ nm.push_back(*m);
} else {
- (*p)->clean(now);
+ uint64_t idtmp[2];
+ idtmp[0] = id; idtmp[1] = 0;
+ RR->node->stateObjectDelete(tPtr,ZT_STATE_OBJECT_MOON,idtmp);
}
}
+ _moons.swap(nm);
+
+ std::vector< std::pair<uint64_t,Address> > cm;
+ for(std::vector< std::pair<uint64_t,Address> >::const_iterator m(_moonSeeds.begin());m!=_moonSeeds.end();++m) {
+ if (m->first != id)
+ cm.push_back(*m);
+ }
+ _moonSeeds.swap(cm);
+
+ _memoizeUpstreams(tPtr);
}
-Identity Topology::_getIdentity(const Address &zta)
+void Topology::doPeriodicTasks(void *tPtr,int64_t now)
{
- char p[128];
- Utils::snprintf(p,sizeof(p),"iddb.d/%.10llx",(unsigned long long)zta.toInt());
- std::string ids(RR->node->dataStoreGet(p));
- if (ids.length() > 0) {
- try {
- return Identity(ids);
- } catch ( ... ) {} // ignore invalid IDs
+ {
+ Mutex::Lock _l1(_peers_m);
+ Mutex::Lock _l2(_upstreams_m);
+ Hashtable< Address,SharedPtr<Peer> >::Iterator i(_peers);
+ Address *a = (Address *)0;
+ SharedPtr<Peer> *p = (SharedPtr<Peer> *)0;
+ while (i.next(a,p)) {
+ if ( (!(*p)->isAlive(now)) && (std::find(_upstreamAddresses.begin(),_upstreamAddresses.end(),*a) == _upstreamAddresses.end()) ) {
+ _savePeer(tPtr,*p);
+ _peers.erase(*a);
+ }
+ }
+ }
+
+ {
+ Mutex::Lock _l(_paths_m);
+ Hashtable< Path::HashKey,SharedPtr<Path> >::Iterator i(_paths);
+ Path::HashKey *k = (Path::HashKey *)0;
+ SharedPtr<Path> *p = (SharedPtr<Path> *)0;
+ while (i.next(k,p)) {
+ if (p->references() <= 1)
+ _paths.erase(*k);
+ }
}
- return Identity();
}
-void Topology::_setWorld(const World &newWorld)
+void Topology::_memoizeUpstreams(void *tPtr)
{
- // assumed _lock is locked (or in constructor)
- _world = newWorld;
- _amRoot = false;
- _rootAddresses.clear();
- _rootPeers.clear();
- for(std::vector<World::Root>::const_iterator r(_world.roots().begin());r!=_world.roots().end();++r) {
- _rootAddresses.push_back(r->identity.address());
- if (r->identity.address() == RR->identity.address()) {
- _amRoot = true;
- } else {
- SharedPtr<Peer> *rp = _peers.get(r->identity.address());
- if (rp) {
- _rootPeers.push_back(*rp);
- } else {
- SharedPtr<Peer> newrp(new Peer(RR,RR->identity,r->identity));
- _peers.set(r->identity.address(),newrp);
- _rootPeers.push_back(newrp);
+ // assumes _upstreams_m and _peers_m are locked
+ _upstreamAddresses.clear();
+ _amUpstream = false;
+
+ for(std::vector<World::Root>::const_iterator i(_planet.roots().begin());i!=_planet.roots().end();++i) {
+ if (i->identity == RR->identity) {
+ _amUpstream = true;
+ } else if (std::find(_upstreamAddresses.begin(),_upstreamAddresses.end(),i->identity.address()) == _upstreamAddresses.end()) {
+ _upstreamAddresses.push_back(i->identity.address());
+ SharedPtr<Peer> &hp = _peers[i->identity.address()];
+ if (!hp)
+ hp = new Peer(RR,RR->identity,i->identity);
+ }
+ }
+
+ for(std::vector<World>::const_iterator m(_moons.begin());m!=_moons.end();++m) {
+ for(std::vector<World::Root>::const_iterator i(m->roots().begin());i!=m->roots().end();++i) {
+ if (i->identity == RR->identity) {
+ _amUpstream = true;
+ } else if (std::find(_upstreamAddresses.begin(),_upstreamAddresses.end(),i->identity.address()) == _upstreamAddresses.end()) {
+ _upstreamAddresses.push_back(i->identity.address());
+ SharedPtr<Peer> &hp = _peers[i->identity.address()];
+ if (!hp)
+ hp = new Peer(RR,RR->identity,i->identity);
}
}
}
+
+ std::sort(_upstreamAddresses.begin(),_upstreamAddresses.end());
+}
+
+void Topology::_savePeer(void *tPtr,const SharedPtr<Peer> &peer)
+{
+ try {
+ Buffer<ZT_PEER_MAX_SERIALIZED_STATE_SIZE> buf;
+ peer->serializeForCache(buf);
+ uint64_t tmpid[2]; tmpid[0] = peer->address().toInt(); tmpid[1] = 0;
+ RR->node->stateObjectPut(tPtr,ZT_STATE_OBJECT_PEER,tmpid,buf.data(),buf.size());
+ } catch ( ... ) {} // sanity check, discard invalid entries
}
} // namespace ZeroTier
diff --git a/node/Topology.hpp b/node/Topology.hpp
index 03c491e5..63946a32 100644
--- a/node/Topology.hpp
+++ b/node/Topology.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_TOPOLOGY_HPP
@@ -33,6 +41,7 @@
#include "Address.hpp"
#include "Identity.hpp"
#include "Peer.hpp"
+#include "Path.hpp"
#include "Mutex.hpp"
#include "InetAddress.hpp"
#include "Hashtable.hpp"
@@ -48,7 +57,7 @@ class RuntimeEnvironment;
class Topology
{
public:
- Topology(const RuntimeEnvironment *renv);
+ Topology(const RuntimeEnvironment *renv,void *tPtr);
~Topology();
/**
@@ -57,18 +66,27 @@ public:
* This will not replace existing peers. In that case the existing peer
* record is returned.
*
+ * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
* @param peer Peer to add
* @return New or existing peer (should replace 'peer')
*/
- SharedPtr<Peer> addPeer(const SharedPtr<Peer> &peer);
+ SharedPtr<Peer> addPeer(void *tPtr,const SharedPtr<Peer> &peer);
/**
* Get a peer from its address
*
+ * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
* @param zta ZeroTier address of peer
* @return Peer or NULL if not found
*/
- SharedPtr<Peer> getPeer(const Address &zta);
+ SharedPtr<Peer> getPeer(void *tPtr,const Address &zta);
+
+ /**
+ * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
+ * @param zta ZeroTier address of peer
+ * @return Identity or NULL identity if not found
+ */
+ Identity getIdentity(void *tPtr,const Address &zta);
/**
* Get a peer only if it is presently in memory (no disk cache)
@@ -82,7 +100,7 @@ public:
*/
inline SharedPtr<Peer> getPeerNoCache(const Address &zta)
{
- Mutex::Lock _l(_lock);
+ Mutex::Lock _l(_peers_m);
const SharedPtr<Peer> *const ap = _peers.get(zta);
if (ap)
return *ap;
@@ -90,120 +108,200 @@ public:
}
/**
- * Get the identity of a peer
+ * Get a Path object for a given local and remote physical address, creating if needed
*
- * @param zta ZeroTier address of peer
- * @return Identity or NULL Identity if not found
+ * @param l Local socket
+ * @param r Remote address
+ * @return Pointer to canonicalized Path object
*/
- Identity getIdentity(const Address &zta);
+ inline SharedPtr<Path> getPath(const int64_t l,const InetAddress &r)
+ {
+ Mutex::Lock _l(_paths_m);
+ SharedPtr<Path> &p = _paths[Path::HashKey(l,r)];
+ if (!p)
+ p.set(new Path(l,r));
+ return p;
+ }
/**
- * Cache an identity
- *
- * This is done automatically on addPeer(), and so is only useful for
- * cluster identity replication.
+ * Get the current best upstream peer
*
- * @param id Identity to cache
+ * @return Upstream or NULL if none available
*/
- void saveIdentity(const Identity &id);
+ SharedPtr<Peer> getUpstreamPeer();
/**
- * Get the current favorite root server
- *
- * @return Root server with lowest latency or NULL if none
+ * @param id Identity to check
+ * @return True if this is a root server or a network preferred relay from one of our networks
*/
- inline SharedPtr<Peer> getBestRoot() { return getBestRoot((const Address *)0,0,false); }
+ bool isUpstream(const Identity &id) const;
/**
- * Get the best root server, avoiding root servers listed in an array
+ * @param addr Address to check
+ * @return True if we should accept a world update from this address
+ */
+ bool shouldAcceptWorldUpdateFrom(const Address &addr) const;
+
+ /**
+ * @param ztaddr ZeroTier address
+ * @return Peer role for this device
+ */
+ ZT_PeerRole role(const Address &ztaddr) const;
+
+ /**
+ * Check for prohibited endpoints
+ *
+ * Right now this returns true if the designated ZT address is a root and if
+ * the IP (IP only, not port) does not equal any of the IPs defined in the
+ * current World. This is an extra little security feature in case root keys
+ * get appropriated or something.
*
- * This will get the best root server (lowest latency, etc.) but will
- * try to avoid the listed root servers, only using them if no others
- * are available.
+ * Otherwise it returns false.
*
- * @param avoid Nodes to avoid
- * @param avoidCount Number of nodes to avoid
- * @param strictAvoid If false, consider avoided root servers anyway if no non-avoid root servers are available
- * @return Root server or NULL if none available
+ * @param ztaddr ZeroTier address
+ * @param ipaddr IP address
+ * @return True if this ZT/IP pair should not be allowed to be used
*/
- SharedPtr<Peer> getBestRoot(const Address *avoid,unsigned int avoidCount,bool strictAvoid);
+ bool isProhibitedEndpoint(const Address &ztaddr,const InetAddress &ipaddr) const;
/**
- * @param id Identity to check
- * @return True if this is a designated root server in this world
+ * Gets upstreams to contact and their stable endpoints (if known)
+ *
+ * @param eps Hash table to fill with addresses and their stable endpoints
*/
- inline bool isRoot(const Identity &id) const
+ inline void getUpstreamsToContact(Hashtable< Address,std::vector<InetAddress> > &eps) const
{
- Mutex::Lock _l(_lock);
- return (std::find(_rootAddresses.begin(),_rootAddresses.end(),id.address()) != _rootAddresses.end());
+ Mutex::Lock _l(_upstreams_m);
+ for(std::vector<World::Root>::const_iterator i(_planet.roots().begin());i!=_planet.roots().end();++i) {
+ if (i->identity != RR->identity) {
+ std::vector<InetAddress> &ips = eps[i->identity.address()];
+ for(std::vector<InetAddress>::const_iterator j(i->stableEndpoints.begin());j!=i->stableEndpoints.end();++j) {
+ if (std::find(ips.begin(),ips.end(),*j) == ips.end())
+ ips.push_back(*j);
+ }
+ }
+ }
+ for(std::vector<World>::const_iterator m(_moons.begin());m!=_moons.end();++m) {
+ for(std::vector<World::Root>::const_iterator i(m->roots().begin());i!=m->roots().end();++i) {
+ if (i->identity != RR->identity) {
+ std::vector<InetAddress> &ips = eps[i->identity.address()];
+ for(std::vector<InetAddress>::const_iterator j(i->stableEndpoints.begin());j!=i->stableEndpoints.end();++j) {
+ if (std::find(ips.begin(),ips.end(),*j) == ips.end())
+ ips.push_back(*j);
+ }
+ }
+ }
+ }
+ for(std::vector< std::pair<uint64_t,Address> >::const_iterator m(_moonSeeds.begin());m!=_moonSeeds.end();++m)
+ eps[m->second];
}
/**
- * @param id Identity to check
- * @return True if this is a root server or a network preferred relay from one of our networks
+ * @return Vector of active upstream addresses (including roots)
*/
- bool isUpstream(const Identity &id) const;
+ inline std::vector<Address> upstreamAddresses() const
+ {
+ Mutex::Lock _l(_upstreams_m);
+ return _upstreamAddresses;
+ }
/**
- * @return Vector of root server addresses
+ * @return Current moons
*/
- inline std::vector<Address> rootAddresses() const
+ inline std::vector<World> moons() const
{
- Mutex::Lock _l(_lock);
- return _rootAddresses;
+ Mutex::Lock _l(_upstreams_m);
+ return _moons;
}
/**
- * @return Current World (copy)
+ * @return Moon IDs we are waiting for from seeds
*/
- inline World world() const
+ inline std::vector<uint64_t> moonsWanted() const
{
- Mutex::Lock _l(_lock);
- return _world;
+ Mutex::Lock _l(_upstreams_m);
+ std::vector<uint64_t> mw;
+ for(std::vector< std::pair<uint64_t,Address> >::const_iterator s(_moonSeeds.begin());s!=_moonSeeds.end();++s) {
+ if (std::find(mw.begin(),mw.end(),s->first) == mw.end())
+ mw.push_back(s->first);
+ }
+ return mw;
}
/**
- * @return Current world ID
+ * @return Current planet
*/
- inline uint64_t worldId() const
+ inline World planet() const
{
- return _world.id(); // safe to read without lock, and used from within eachPeer() so don't lock
+ Mutex::Lock _l(_upstreams_m);
+ return _planet;
}
/**
- * @return Current world timestamp
+ * @return Current planet's world ID
*/
- inline uint64_t worldTimestamp() const
+ inline uint64_t planetWorldId() const
{
- return _world.timestamp(); // safe to read without lock, and used from within eachPeer() so don't lock
+ return _planet.id(); // safe to read without lock, and used from within eachPeer() so don't lock
+ }
+
+ /**
+ * @return Current planet's world timestamp
+ */
+ inline uint64_t planetWorldTimestamp() const
+ {
+ return _planet.timestamp(); // safe to read without lock, and used from within eachPeer() so don't lock
}
/**
* Validate new world and update if newer and signature is okay
*
- * @param newWorld Potential new world definition revision
- * @return True if an update actually occurred
+ * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
+ * @param newWorld A new or updated planet or moon to learn
+ * @param alwaysAcceptNew If true, always accept new moons even if we're not waiting for one
+ * @return True if it was valid and newer than current (or totally new for moons)
+ */
+ bool addWorld(void *tPtr,const World &newWorld,bool alwaysAcceptNew);
+
+ /**
+ * Add a moon
+ *
+ * This loads it from moons.d if present, and if not adds it to
+ * a list of moons that we want to contact.
+ *
+ * @param id Moon ID
+ * @param seed If non-NULL, an address of any member of the moon to contact
+ */
+ void addMoon(void *tPtr,const uint64_t id,const Address &seed);
+
+ /**
+ * Remove a moon
+ *
+ * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
+ * @param id Moon's world ID
*/
- bool worldUpdateIfValid(const World &newWorld);
+ void removeMoon(void *tPtr,const uint64_t id);
/**
* Clean and flush database
*/
- void clean(uint64_t now);
+ void doPeriodicTasks(void *tPtr,int64_t now);
/**
* @param now Current time
* @return Number of peers with active direct paths
*/
- inline unsigned long countActive(uint64_t now) const
+ inline unsigned long countActive(int64_t now) const
{
unsigned long cnt = 0;
- Mutex::Lock _l(_lock);
+ Mutex::Lock _l(_peers_m);
Hashtable< Address,SharedPtr<Peer> >::Iterator i(const_cast<Topology *>(this)->_peers);
Address *a = (Address *)0;
SharedPtr<Peer> *p = (SharedPtr<Peer> *)0;
while (i.next(a,p)) {
- cnt += (unsigned long)((*p)->hasActiveDirectPath(now));
+ const SharedPtr<Path> pp((*p)->getBestPath(now,false));
+ if (pp)
+ ++cnt;
}
return cnt;
}
@@ -211,30 +309,17 @@ public:
/**
* Apply a function or function object to all peers
*
- * Note: explicitly template this by reference if you want the object
- * passed by reference instead of copied.
- *
- * Warning: be careful not to use features in these that call any other
- * methods of Topology that may lock _lock, otherwise a recursive lock
- * and deadlock or lock corruption may occur.
- *
* @param f Function to apply
* @tparam F Function or function object type
*/
template<typename F>
inline void eachPeer(F f)
{
- Mutex::Lock _l(_lock);
+ Mutex::Lock _l(_peers_m);
Hashtable< Address,SharedPtr<Peer> >::Iterator i(_peers);
Address *a = (Address *)0;
SharedPtr<Peer> *p = (SharedPtr<Peer> *)0;
while (i.next(a,p)) {
-#ifdef ZT_TRACE
- if (!(*p)) {
- fprintf(stderr,"FATAL BUG: eachPeer() caught NULL peer for %s -- peer pointers in Topology should NEVER be NULL" ZT_EOL_S,a->toString().c_str());
- abort();
- }
-#endif
f(*this,*((const SharedPtr<Peer> *)p));
}
}
@@ -244,14 +329,49 @@ public:
*/
inline std::vector< std::pair< Address,SharedPtr<Peer> > > allPeers() const
{
- Mutex::Lock _l(_lock);
+ Mutex::Lock _l(_peers_m);
return _peers.entries();
}
/**
- * @return True if I am a root server in the current World
+ * @return True if I am a root server in a planet or moon
+ */
+ inline bool amUpstream() const { return _amUpstream; }
+
+ /**
+ * Get info about a path
+ *
+ * The supplied result variables are not modified if no special config info is found.
+ *
+ * @param physicalAddress Physical endpoint address
+ * @param mtu Variable set to MTU
+ * @param trustedPathId Variable set to trusted path ID
+ */
+ inline void getOutboundPathInfo(const InetAddress &physicalAddress,unsigned int &mtu,uint64_t &trustedPathId)
+ {
+ for(unsigned int i=0,j=_numConfiguredPhysicalPaths;i<j;++i) {
+ if (_physicalPathConfig[i].first.containsAddress(physicalAddress)) {
+ trustedPathId = _physicalPathConfig[i].second.trustedPathId;
+ mtu = _physicalPathConfig[i].second.mtu;
+ return;
+ }
+ }
+ }
+
+ /**
+ * Get the payload MTU for an outbound physical path (returns default if not configured)
+ *
+ * @param physicalAddress Physical endpoint address
+ * @return MTU
*/
- inline bool amRoot() const throw() { return _amRoot; }
+ inline unsigned int getOutboundPathMtu(const InetAddress &physicalAddress)
+ {
+ for(unsigned int i=0,j=_numConfiguredPhysicalPaths;i<j;++i) {
+ if (_physicalPathConfig[i].first.containsAddress(physicalAddress))
+ return _physicalPathConfig[i].second.mtu;
+ }
+ return ZT_DEFAULT_PHYSMTU;
+ }
/**
* Get the outbound trusted path ID for a physical address, or 0 if none
@@ -261,9 +381,9 @@ public:
*/
inline uint64_t getOutboundPathTrust(const InetAddress &physicalAddress)
{
- for(unsigned int i=0;i<_trustedPathCount;++i) {
- if (_trustedPathNetworks[i].containsAddress(physicalAddress))
- return _trustedPathIds[i];
+ for(unsigned int i=0,j=_numConfiguredPhysicalPaths;i<j;++i) {
+ if (_physicalPathConfig[i].first.containsAddress(physicalAddress))
+ return _physicalPathConfig[i].second.trustedPathId;
}
return 0;
}
@@ -276,48 +396,72 @@ public:
*/
inline bool shouldInboundPathBeTrusted(const InetAddress &physicalAddress,const uint64_t trustedPathId)
{
- for(unsigned int i=0;i<_trustedPathCount;++i) {
- if ((_trustedPathIds[i] == trustedPathId)&&(_trustedPathNetworks[i].containsAddress(physicalAddress)))
+ for(unsigned int i=0,j=_numConfiguredPhysicalPaths;i<j;++i) {
+ if ((_physicalPathConfig[i].second.trustedPathId == trustedPathId)&&(_physicalPathConfig[i].first.containsAddress(physicalAddress)))
return true;
}
return false;
}
/**
- * Set trusted paths in this topology
- *
- * @param networks Array of networks (prefix/netmask bits)
- * @param ids Array of trusted path IDs
- * @param count Number of trusted paths (if larger than ZT_MAX_TRUSTED_PATHS overflow is ignored)
+ * Set or clear physical path configuration (called via Node::setPhysicalPathConfiguration)
*/
- inline void setTrustedPaths(const InetAddress *networks,const uint64_t *ids,unsigned int count)
+ inline void setPhysicalPathConfiguration(const struct sockaddr_storage *pathNetwork,const ZT_PhysicalPathConfiguration *pathConfig)
{
- if (count > ZT_MAX_TRUSTED_PATHS)
- count = ZT_MAX_TRUSTED_PATHS;
- Mutex::Lock _l(_lock);
- for(unsigned int i=0;i<count;++i) {
- _trustedPathIds[i] = ids[i];
- _trustedPathNetworks[i] = networks[i];
+ if (!pathNetwork) {
+ _numConfiguredPhysicalPaths = 0;
+ } else {
+ std::map<InetAddress,ZT_PhysicalPathConfiguration> cpaths;
+ for(unsigned int i=0,j=_numConfiguredPhysicalPaths;i<j;++i)
+ cpaths[_physicalPathConfig[i].first] = _physicalPathConfig[i].second;
+
+ if (pathConfig) {
+ ZT_PhysicalPathConfiguration pc(*pathConfig);
+
+ if (pc.mtu <= 0)
+ pc.mtu = ZT_DEFAULT_PHYSMTU;
+ else if (pc.mtu < ZT_MIN_PHYSMTU)
+ pc.mtu = ZT_MIN_PHYSMTU;
+ else if (pc.mtu > ZT_MAX_PHYSMTU)
+ pc.mtu = ZT_MAX_PHYSMTU;
+
+ cpaths[*(reinterpret_cast<const InetAddress *>(pathNetwork))] = pc;
+ } else {
+ cpaths.erase(*(reinterpret_cast<const InetAddress *>(pathNetwork)));
+ }
+
+ unsigned int cnt = 0;
+ for(std::map<InetAddress,ZT_PhysicalPathConfiguration>::const_iterator i(cpaths.begin());((i!=cpaths.end())&&(cnt<ZT_MAX_CONFIGURABLE_PATHS));++i) {
+ _physicalPathConfig[cnt].first = i->first;
+ _physicalPathConfig[cnt].second = i->second;
+ ++cnt;
+ }
+ _numConfiguredPhysicalPaths = cnt;
}
- _trustedPathCount = count;
}
private:
- Identity _getIdentity(const Address &zta);
- void _setWorld(const World &newWorld);
+ Identity _getIdentity(void *tPtr,const Address &zta);
+ void _memoizeUpstreams(void *tPtr);
+ void _savePeer(void *tPtr,const SharedPtr<Peer> &peer);
const RuntimeEnvironment *const RR;
- uint64_t _trustedPathIds[ZT_MAX_TRUSTED_PATHS];
- InetAddress _trustedPathNetworks[ZT_MAX_TRUSTED_PATHS];
- unsigned int _trustedPathCount;
- World _world;
+ std::pair<InetAddress,ZT_PhysicalPathConfiguration> _physicalPathConfig[ZT_MAX_CONFIGURABLE_PATHS];
+ volatile unsigned int _numConfiguredPhysicalPaths;
+
Hashtable< Address,SharedPtr<Peer> > _peers;
- std::vector< Address > _rootAddresses;
- std::vector< SharedPtr<Peer> > _rootPeers;
- bool _amRoot;
+ Mutex _peers_m;
+
+ Hashtable< Path::HashKey,SharedPtr<Path> > _paths;
+ Mutex _paths_m;
- Mutex _lock;
+ World _planet;
+ std::vector<World> _moons;
+ std::vector< std::pair<uint64_t,Address> > _moonSeeds;
+ std::vector<Address> _upstreamAddresses;
+ bool _amUpstream;
+ Mutex _upstreams_m; // locks worlds, upstream info, moon info, etc.
};
} // namespace ZeroTier
diff --git a/node/Trace.cpp b/node/Trace.cpp
new file mode 100644
index 00000000..386edaac
--- /dev/null
+++ b/node/Trace.cpp
@@ -0,0 +1,540 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
+ */
+
+//#define ZT_TRACE
+
+#include <stdio.h>
+#include <stdarg.h>
+
+#include "Trace.hpp"
+#include "RuntimeEnvironment.hpp"
+#include "Switch.hpp"
+#include "Node.hpp"
+#include "Utils.hpp"
+#include "Dictionary.hpp"
+#include "CertificateOfMembership.hpp"
+#include "CertificateOfOwnership.hpp"
+#include "Tag.hpp"
+#include "Capability.hpp"
+#include "Revocation.hpp"
+
+namespace ZeroTier {
+
+#ifdef ZT_TRACE
+static void ZT_LOCAL_TRACE(void *const tPtr,const RuntimeEnvironment *const RR,const char *const fmt,...)
+{
+ char traceMsgBuf[1024];
+ va_list ap;
+ va_start(ap,fmt);
+ vsnprintf(traceMsgBuf,sizeof(traceMsgBuf),fmt,ap);
+ va_end(ap);
+ traceMsgBuf[sizeof(traceMsgBuf) - 1] = (char)0;
+ RR->node->postEvent(tPtr,ZT_EVENT_TRACE,traceMsgBuf);
+}
+#else
+#define ZT_LOCAL_TRACE(...)
+#endif
+
+void Trace::resettingPathsInScope(void *const tPtr,const Address &reporter,const InetAddress &reporterPhysicalAddress,const InetAddress &myPhysicalAddress,const InetAddress::IpScope scope)
+{
+ char tmp[128];
+
+ ZT_LOCAL_TRACE(tPtr,RR,"RESET and revalidate paths in scope %d; new phy address %s reported by trusted peer %.10llx",(int)scope,myPhysicalAddress.toIpString(tmp),reporter.toInt());
+
+ Dictionary<ZT_MAX_REMOTE_TRACE_SIZE> d;
+ d.add(ZT_REMOTE_TRACE_FIELD__EVENT,ZT_REMOTE_TRACE_EVENT__RESETTING_PATHS_IN_SCOPE_S);
+ d.add(ZT_REMOTE_TRACE_FIELD__REMOTE_ZTADDR,reporter);
+ d.add(ZT_REMOTE_TRACE_FIELD__REMOTE_PHYADDR,reporterPhysicalAddress.toString(tmp));
+ d.add(ZT_REMOTE_TRACE_FIELD__LOCAL_PHYADDR,myPhysicalAddress.toString(tmp));
+ d.add(ZT_REMOTE_TRACE_FIELD__IP_SCOPE,(uint64_t)scope);
+
+ if (_globalTarget)
+ _send(tPtr,d,_globalTarget);
+ _spamToAllNetworks(tPtr,d,Trace::LEVEL_NORMAL);
+}
+
+void Trace::peerConfirmingUnknownPath(void *const tPtr,const uint64_t networkId,Peer &peer,const SharedPtr<Path> &path,const uint64_t packetId,const Packet::Verb verb)
+{
+ char tmp[128];
+ if (!path) return; // sanity check
+
+ ZT_LOCAL_TRACE(tPtr,RR,"trying unknown path %s to %.10llx (packet %.16llx verb %d local socket %lld network %.16llx)",path->address().toString(tmp),peer.address().toInt(),packetId,(double)verb,path->localSocket(),networkId);
+
+ std::pair<Address,Trace::Level> byn;
+ if (networkId) { Mutex::Lock l(_byNet_m); _byNet.get(networkId,byn); }
+
+ if ((_globalTarget)||(byn.first)) {
+ Dictionary<ZT_MAX_REMOTE_TRACE_SIZE> d;
+ d.add(ZT_REMOTE_TRACE_FIELD__EVENT,ZT_REMOTE_TRACE_EVENT__PEER_CONFIRMING_UNKNOWN_PATH_S);
+ d.add(ZT_REMOTE_TRACE_FIELD__PACKET_ID,packetId);
+ d.add(ZT_REMOTE_TRACE_FIELD__PACKET_VERB,(uint64_t)verb);
+ if (networkId)
+ d.add(ZT_REMOTE_TRACE_FIELD__NETWORK_ID,networkId);
+ d.add(ZT_REMOTE_TRACE_FIELD__REMOTE_ZTADDR,peer.address());
+ if (path) {
+ d.add(ZT_REMOTE_TRACE_FIELD__REMOTE_PHYADDR,path->address().toString(tmp));
+ d.add(ZT_REMOTE_TRACE_FIELD__LOCAL_SOCKET,path->localSocket());
+ }
+
+ if (_globalTarget)
+ _send(tPtr,d,_globalTarget);
+ if (byn.first)
+ _send(tPtr,d,byn.first);
+ }
+}
+
+void Trace::peerLearnedNewPath(void *const tPtr,const uint64_t networkId,Peer &peer,const SharedPtr<Path> &newPath,const uint64_t packetId)
+{
+ char tmp[128];
+ if (!newPath) return; // sanity check
+
+ ZT_LOCAL_TRACE(tPtr,RR,"learned new path %s to %.10llx (packet %.16llx local socket %lld network %.16llx)",newPath->address().toString(tmp),peer.address().toInt(),packetId,newPath->localSocket(),networkId);
+
+ std::pair<Address,Trace::Level> byn;
+ if (networkId) { Mutex::Lock l(_byNet_m); _byNet.get(networkId,byn); }
+
+ if ((_globalTarget)||(byn.first)) {
+ Dictionary<ZT_MAX_REMOTE_TRACE_SIZE> d;
+ d.add(ZT_REMOTE_TRACE_FIELD__EVENT,ZT_REMOTE_TRACE_EVENT__PEER_LEARNED_NEW_PATH_S);
+ d.add(ZT_REMOTE_TRACE_FIELD__PACKET_ID,packetId);
+ if (networkId)
+ d.add(ZT_REMOTE_TRACE_FIELD__NETWORK_ID,networkId);
+ d.add(ZT_REMOTE_TRACE_FIELD__REMOTE_ZTADDR,peer.address());
+ d.add(ZT_REMOTE_TRACE_FIELD__REMOTE_PHYADDR,newPath->address().toString(tmp));
+ d.add(ZT_REMOTE_TRACE_FIELD__LOCAL_SOCKET,newPath->localSocket());
+
+ if (_globalTarget)
+ _send(tPtr,d,_globalTarget);
+ if (byn.first)
+ _send(tPtr,d,byn.first);
+ }
+}
+
+void Trace::peerRedirected(void *const tPtr,const uint64_t networkId,Peer &peer,const SharedPtr<Path> &newPath)
+{
+ char tmp[128];
+ if (!newPath) return; // sanity check
+
+ ZT_LOCAL_TRACE(tPtr,RR,"explicit redirect from %.10llx to path %s",peer.address().toInt(),newPath->address().toString(tmp));
+
+ std::pair<Address,Trace::Level> byn;
+ if (networkId) { Mutex::Lock l(_byNet_m); _byNet.get(networkId,byn); }
+
+ if ((_globalTarget)||(byn.first)) {
+ Dictionary<ZT_MAX_REMOTE_TRACE_SIZE> d;
+ d.add(ZT_REMOTE_TRACE_FIELD__EVENT,ZT_REMOTE_TRACE_EVENT__PEER_REDIRECTED_S);
+ if (networkId)
+ d.add(ZT_REMOTE_TRACE_FIELD__NETWORK_ID,networkId);
+ d.add(ZT_REMOTE_TRACE_FIELD__REMOTE_ZTADDR,peer.address());
+ d.add(ZT_REMOTE_TRACE_FIELD__REMOTE_PHYADDR,newPath->address().toString(tmp));
+ d.add(ZT_REMOTE_TRACE_FIELD__LOCAL_SOCKET,newPath->localSocket());
+
+ if (_globalTarget)
+ _send(tPtr,d,_globalTarget);
+ if (byn.first)
+ _send(tPtr,d,byn.first);
+ }
+}
+
+void Trace::outgoingNetworkFrameDropped(void *const tPtr,const SharedPtr<Network> &network,const MAC &sourceMac,const MAC &destMac,const unsigned int etherType,const unsigned int vlanId,const unsigned int frameLen,const char *reason)
+{
+#ifdef ZT_TRACE
+ char tmp[128],tmp2[128];
+#endif
+ if (!network) return; // sanity check
+
+ ZT_LOCAL_TRACE(tPtr,RR,"%.16llx DROP frame %s -> %s etherType %.4x size %u (%s)",network->id(),sourceMac.toString(tmp),destMac.toString(tmp2),etherType,frameLen,(reason) ? reason : "unknown reason");
+
+ std::pair<Address,Trace::Level> byn;
+ { Mutex::Lock l(_byNet_m); _byNet.get(network->id(),byn); }
+
+ if ( ((_globalTarget)&&((int)_globalLevel >= (int)Trace::LEVEL_VERBOSE)) || ((byn.first)&&((int)byn.second >= (int)Trace::LEVEL_VERBOSE)) ) {
+ Dictionary<ZT_MAX_REMOTE_TRACE_SIZE> d;
+ d.add(ZT_REMOTE_TRACE_FIELD__EVENT,ZT_REMOTE_TRACE_EVENT__OUTGOING_NETWORK_FRAME_DROPPED_S);
+ d.add(ZT_REMOTE_TRACE_FIELD__NETWORK_ID,network->id());
+ d.add(ZT_REMOTE_TRACE_FIELD__SOURCE_MAC,sourceMac.toInt());
+ d.add(ZT_REMOTE_TRACE_FIELD__DEST_MAC,destMac.toInt());
+ d.add(ZT_REMOTE_TRACE_FIELD__ETHERTYPE,(uint64_t)etherType);
+ d.add(ZT_REMOTE_TRACE_FIELD__VLAN_ID,(uint64_t)vlanId);
+ d.add(ZT_REMOTE_TRACE_FIELD__FRAME_LENGTH,(uint64_t)frameLen);
+ if (reason)
+ d.add(ZT_REMOTE_TRACE_FIELD__REASON,reason);
+
+ if ((_globalTarget)&&((int)_globalLevel >= (int)Trace::LEVEL_VERBOSE))
+ _send(tPtr,d,_globalTarget);
+ if ((byn.first)&&((int)byn.second >= (int)Trace::LEVEL_VERBOSE))
+ _send(tPtr,d,byn.first);
+ }
+}
+
+void Trace::incomingNetworkAccessDenied(void *const tPtr,const SharedPtr<Network> &network,const SharedPtr<Path> &path,const uint64_t packetId,const unsigned int packetLength,const Address &source,const Packet::Verb verb,bool credentialsRequested)
+{
+ char tmp[128];
+ if (!network) return; // sanity check
+
+ ZT_LOCAL_TRACE(tPtr,RR,"%.16llx DENIED packet from %.10llx(%s) verb %d size %u%s",network->id(),source.toInt(),(path) ? (path->address().toString(tmp)) : "???",(int)verb,packetLength,credentialsRequested ? " (credentials requested)" : " (credentials not requested)");
+
+ std::pair<Address,Trace::Level> byn;
+ { Mutex::Lock l(_byNet_m); _byNet.get(network->id(),byn); }
+
+ if ( ((_globalTarget)&&((int)_globalLevel >= (int)Trace::LEVEL_VERBOSE)) || ((byn.first)&&((int)byn.second >= (int)Trace::LEVEL_VERBOSE)) ) {
+ Dictionary<ZT_MAX_REMOTE_TRACE_SIZE> d;
+ d.add(ZT_REMOTE_TRACE_FIELD__EVENT,ZT_REMOTE_TRACE_EVENT__INCOMING_NETWORK_ACCESS_DENIED_S);
+ d.add(ZT_REMOTE_TRACE_FIELD__PACKET_ID,packetId);
+ d.add(ZT_REMOTE_TRACE_FIELD__PACKET_VERB,(uint64_t)verb);
+ d.add(ZT_REMOTE_TRACE_FIELD__REMOTE_ZTADDR,source);
+ if (path) {
+ d.add(ZT_REMOTE_TRACE_FIELD__REMOTE_PHYADDR,path->address().toString(tmp));
+ d.add(ZT_REMOTE_TRACE_FIELD__LOCAL_SOCKET,path->localSocket());
+ }
+ d.add(ZT_REMOTE_TRACE_FIELD__NETWORK_ID,network->id());
+
+ if ((_globalTarget)&&((int)_globalLevel >= (int)Trace::LEVEL_VERBOSE))
+ _send(tPtr,d,_globalTarget);
+ if ((byn.first)&&((int)byn.second >= (int)Trace::LEVEL_VERBOSE))
+ _send(tPtr,d,byn.first);
+ }
+}
+
+void Trace::incomingNetworkFrameDropped(void *const tPtr,const SharedPtr<Network> &network,const SharedPtr<Path> &path,const uint64_t packetId,const unsigned int packetLength,const Address &source,const Packet::Verb verb,const MAC &sourceMac,const MAC &destMac,const char *reason)
+{
+ char tmp[128];
+ if (!network) return; // sanity check
+
+ ZT_LOCAL_TRACE(tPtr,RR,"%.16llx DROPPED frame from %.10llx(%s) verb %d size %u",network->id(),source.toInt(),(path) ? (path->address().toString(tmp)) : "???",(int)verb,packetLength);
+
+ std::pair<Address,Trace::Level> byn;
+ { Mutex::Lock l(_byNet_m); _byNet.get(network->id(),byn); }
+
+ if ( ((_globalTarget)&&((int)_globalLevel >= (int)Trace::LEVEL_VERBOSE)) || ((byn.first)&&((int)byn.second >= (int)Trace::LEVEL_VERBOSE)) ) {
+ Dictionary<ZT_MAX_REMOTE_TRACE_SIZE> d;
+ d.add(ZT_REMOTE_TRACE_FIELD__EVENT,ZT_REMOTE_TRACE_EVENT__INCOMING_NETWORK_FRAME_DROPPED_S);
+ d.add(ZT_REMOTE_TRACE_FIELD__PACKET_ID,packetId);
+ d.add(ZT_REMOTE_TRACE_FIELD__PACKET_VERB,(uint64_t)verb);
+ d.add(ZT_REMOTE_TRACE_FIELD__REMOTE_ZTADDR,source);
+ if (path) {
+ d.add(ZT_REMOTE_TRACE_FIELD__REMOTE_PHYADDR,path->address().toString(tmp));
+ d.add(ZT_REMOTE_TRACE_FIELD__LOCAL_SOCKET,path->localSocket());
+ }
+ d.add(ZT_REMOTE_TRACE_FIELD__NETWORK_ID,network->id());
+ d.add(ZT_REMOTE_TRACE_FIELD__SOURCE_MAC,sourceMac.toInt());
+ d.add(ZT_REMOTE_TRACE_FIELD__DEST_MAC,destMac.toInt());
+ if (reason)
+ d.add(ZT_REMOTE_TRACE_FIELD__REASON,reason);
+
+ if ((_globalTarget)&&((int)_globalLevel >= (int)Trace::LEVEL_VERBOSE))
+ _send(tPtr,d,_globalTarget);
+ if ((byn.first)&&((int)byn.second >= (int)Trace::LEVEL_VERBOSE))
+ _send(tPtr,d,byn.first);
+ }
+}
+
+void Trace::incomingPacketMessageAuthenticationFailure(void *const tPtr,const SharedPtr<Path> &path,const uint64_t packetId,const Address &source,const unsigned int hops,const char *reason)
+{
+ char tmp[128];
+
+ ZT_LOCAL_TRACE(tPtr,RR,"MAC failed for packet %.16llx from %.10llx(%s)",packetId,source.toInt(),(path) ? path->address().toString(tmp) : "???");
+
+ if ((_globalTarget)&&((int)_globalLevel >= Trace::LEVEL_DEBUG)) {
+ Dictionary<ZT_MAX_REMOTE_TRACE_SIZE> d;
+ d.add(ZT_REMOTE_TRACE_FIELD__EVENT,ZT_REMOTE_TRACE_EVENT__PACKET_MAC_FAILURE_S);
+ d.add(ZT_REMOTE_TRACE_FIELD__PACKET_ID,packetId);
+ d.add(ZT_REMOTE_TRACE_FIELD__PACKET_HOPS,(uint64_t)hops);
+ d.add(ZT_REMOTE_TRACE_FIELD__REMOTE_ZTADDR,source);
+ if (path) {
+ d.add(ZT_REMOTE_TRACE_FIELD__REMOTE_PHYADDR,path->address().toString(tmp));
+ d.add(ZT_REMOTE_TRACE_FIELD__LOCAL_SOCKET,path->localSocket());
+ }
+ if (reason)
+ d.add(ZT_REMOTE_TRACE_FIELD__REASON,reason);
+
+ _send(tPtr,d,_globalTarget);
+ }
+}
+
+void Trace::incomingPacketInvalid(void *const tPtr,const SharedPtr<Path> &path,const uint64_t packetId,const Address &source,const unsigned int hops,const Packet::Verb verb,const char *reason)
+{
+ char tmp[128];
+
+ ZT_LOCAL_TRACE(tPtr,RR,"INVALID packet %.16llx from %.10llx(%s) (%s)",packetId,source.toInt(),(path) ? path->address().toString(tmp) : "???",(reason) ? reason : "unknown reason");
+
+ if ((_globalTarget)&&((int)_globalLevel >= Trace::LEVEL_DEBUG)) {
+ Dictionary<ZT_MAX_REMOTE_TRACE_SIZE> d;
+ d.add(ZT_REMOTE_TRACE_FIELD__EVENT,ZT_REMOTE_TRACE_EVENT__PACKET_INVALID_S);
+ d.add(ZT_REMOTE_TRACE_FIELD__PACKET_ID,packetId);
+ d.add(ZT_REMOTE_TRACE_FIELD__PACKET_VERB,(uint64_t)verb);
+ d.add(ZT_REMOTE_TRACE_FIELD__REMOTE_ZTADDR,source);
+ if (path) {
+ d.add(ZT_REMOTE_TRACE_FIELD__REMOTE_PHYADDR,path->address().toString(tmp));
+ d.add(ZT_REMOTE_TRACE_FIELD__LOCAL_SOCKET,path->localSocket());
+ }
+ d.add(ZT_REMOTE_TRACE_FIELD__PACKET_HOPS,(uint64_t)hops);
+ if (reason)
+ d.add(ZT_REMOTE_TRACE_FIELD__REASON,reason);
+
+ _send(tPtr,d,_globalTarget);
+ }
+}
+
+void Trace::incomingPacketDroppedHELLO(void *const tPtr,const SharedPtr<Path> &path,const uint64_t packetId,const Address &source,const char *reason)
+{
+ char tmp[128];
+
+ ZT_LOCAL_TRACE(tPtr,RR,"DROPPED HELLO from %.10llx(%s) (%s)",source.toInt(),(path) ? path->address().toString(tmp) : "???",(reason) ? reason : "???");
+
+ if ((_globalTarget)&&((int)_globalLevel >= Trace::LEVEL_DEBUG)) {
+ Dictionary<ZT_MAX_REMOTE_TRACE_SIZE> d;
+ d.add(ZT_REMOTE_TRACE_FIELD__EVENT,ZT_REMOTE_TRACE_EVENT__PACKET_INVALID_S);
+ d.add(ZT_REMOTE_TRACE_FIELD__PACKET_ID,packetId);
+ d.add(ZT_REMOTE_TRACE_FIELD__REMOTE_ZTADDR,source);
+ if (path) {
+ d.add(ZT_REMOTE_TRACE_FIELD__REMOTE_PHYADDR,path->address().toString(tmp));
+ d.add(ZT_REMOTE_TRACE_FIELD__LOCAL_SOCKET,path->localSocket());
+ }
+ if (reason)
+ d.add(ZT_REMOTE_TRACE_FIELD__REASON,reason);
+
+ _send(tPtr,d,_globalTarget);
+ }
+}
+
+void Trace::networkConfigRequestSent(void *const tPtr,const Network &network,const Address &controller)
+{
+ ZT_LOCAL_TRACE(tPtr,RR,"requesting configuration for network %.16llx",network.id());
+ if ((_globalTarget)&&((int)_globalLevel >= Trace::LEVEL_DEBUG)) {
+ Dictionary<ZT_MAX_REMOTE_TRACE_SIZE> d;
+ d.add(ZT_REMOTE_TRACE_FIELD__EVENT,ZT_REMOTE_TRACE_EVENT__NETWORK_CONFIG_REQUEST_SENT_S);
+ d.add(ZT_REMOTE_TRACE_FIELD__NETWORK_ID,network.id());
+ d.add(ZT_REMOTE_TRACE_FIELD__NETWORK_CONTROLLER_ID,controller);
+ _send(tPtr,d,_globalTarget);
+ }
+}
+
+void Trace::networkFilter(
+ void *const tPtr,
+ const Network &network,
+ const RuleResultLog &primaryRuleSetLog,
+ const RuleResultLog *const matchingCapabilityRuleSetLog,
+ const Capability *const matchingCapability,
+ const Address &ztSource,
+ const Address &ztDest,
+ const MAC &macSource,
+ const MAC &macDest,
+ const uint8_t *const frameData,
+ const unsigned int frameLen,
+ const unsigned int etherType,
+ const unsigned int vlanId,
+ const bool noTee,
+ const bool inbound,
+ const int accept)
+{
+ std::pair<Address,Trace::Level> byn;
+ { Mutex::Lock l(_byNet_m); _byNet.get(network.id(),byn); }
+
+ if ( ((_globalTarget)&&((int)_globalLevel >= (int)Trace::LEVEL_RULES)) || ((byn.first)&&((int)byn.second >= (int)Trace::LEVEL_RULES)) ) {
+ Dictionary<ZT_MAX_REMOTE_TRACE_SIZE> d;
+ d.add(ZT_REMOTE_TRACE_FIELD__EVENT,ZT_REMOTE_TRACE_EVENT__NETWORK_FILTER_TRACE_S);
+ d.add(ZT_REMOTE_TRACE_FIELD__NETWORK_ID,network.id());
+ d.add(ZT_REMOTE_TRACE_FIELD__SOURCE_ZTADDR,ztSource);
+ d.add(ZT_REMOTE_TRACE_FIELD__DEST_ZTADDR,ztDest);
+ d.add(ZT_REMOTE_TRACE_FIELD__SOURCE_MAC,macSource.toInt());
+ d.add(ZT_REMOTE_TRACE_FIELD__DEST_MAC,macDest.toInt());
+ d.add(ZT_REMOTE_TRACE_FIELD__ETHERTYPE,(uint64_t)etherType);
+ d.add(ZT_REMOTE_TRACE_FIELD__VLAN_ID,(uint64_t)vlanId);
+ d.add(ZT_REMOTE_TRACE_FIELD__FILTER_FLAG_NOTEE,noTee ? "1" : "0");
+ d.add(ZT_REMOTE_TRACE_FIELD__FILTER_FLAG_INBOUND,inbound ? "1" : "0");
+ d.add(ZT_REMOTE_TRACE_FIELD__FILTER_RESULT,(int64_t)accept);
+ d.add(ZT_REMOTE_TRACE_FIELD__FILTER_BASE_RULE_LOG,(const char *)primaryRuleSetLog.data(),(int)primaryRuleSetLog.sizeBytes());
+ if (matchingCapabilityRuleSetLog)
+ d.add(ZT_REMOTE_TRACE_FIELD__FILTER_CAP_RULE_LOG,(const char *)matchingCapabilityRuleSetLog->data(),(int)matchingCapabilityRuleSetLog->sizeBytes());
+ if (matchingCapability)
+ d.add(ZT_REMOTE_TRACE_FIELD__FILTER_CAP_ID,(uint64_t)matchingCapability->id());
+ d.add(ZT_REMOTE_TRACE_FIELD__FRAME_LENGTH,(uint64_t)frameLen);
+ if (frameLen > 0)
+ d.add(ZT_REMOTE_TRACE_FIELD__FRAME_DATA,(const char *)frameData,(frameLen > 256) ? (int)256 : (int)frameLen);
+
+ if ((_globalTarget)&&((int)_globalLevel >= (int)Trace::LEVEL_RULES))
+ _send(tPtr,d,_globalTarget);
+ if ((byn.first)&&((int)byn.second >= (int)Trace::LEVEL_RULES))
+ _send(tPtr,d,byn.first);
+ }
+}
+
+void Trace::credentialRejected(void *const tPtr,const CertificateOfMembership &c,const char *reason)
+{
+ std::pair<Address,Trace::Level> byn;
+ if (c.networkId()) { Mutex::Lock l(_byNet_m); _byNet.get(c.networkId(),byn); }
+
+ if ((_globalTarget)||(byn.first)) {
+ Dictionary<ZT_MAX_REMOTE_TRACE_SIZE> d;
+ d.add(ZT_REMOTE_TRACE_FIELD__EVENT,ZT_REMOTE_TRACE_EVENT__CREDENTIAL_REJECTED_S);
+ d.add(ZT_REMOTE_TRACE_FIELD__NETWORK_ID,c.networkId());
+ d.add(ZT_REMOTE_TRACE_FIELD__CREDENTIAL_TYPE,(uint64_t)c.credentialType());
+ d.add(ZT_REMOTE_TRACE_FIELD__CREDENTIAL_ID,(uint64_t)c.id());
+ d.add(ZT_REMOTE_TRACE_FIELD__CREDENTIAL_TIMESTAMP,c.timestamp());
+ d.add(ZT_REMOTE_TRACE_FIELD__CREDENTIAL_ISSUED_TO,c.issuedTo());
+ if (reason)
+ d.add(ZT_REMOTE_TRACE_FIELD__REASON,reason);
+
+ if (_globalTarget)
+ _send(tPtr,d,_globalTarget);
+ if (byn.first)
+ _send(tPtr,d,byn.first);
+ }
+}
+
+void Trace::credentialRejected(void *const tPtr,const CertificateOfOwnership &c,const char *reason)
+{
+ std::pair<Address,Trace::Level> byn;
+ if (c.networkId()) { Mutex::Lock l(_byNet_m); _byNet.get(c.networkId(),byn); }
+
+ if ((_globalTarget)||(byn.first)) {
+ Dictionary<ZT_MAX_REMOTE_TRACE_SIZE> d;
+ d.add(ZT_REMOTE_TRACE_FIELD__EVENT,ZT_REMOTE_TRACE_EVENT__CREDENTIAL_REJECTED_S);
+ d.add(ZT_REMOTE_TRACE_FIELD__NETWORK_ID,c.networkId());
+ d.add(ZT_REMOTE_TRACE_FIELD__CREDENTIAL_TYPE,(uint64_t)c.credentialType());
+ d.add(ZT_REMOTE_TRACE_FIELD__CREDENTIAL_ID,(uint64_t)c.id());
+ d.add(ZT_REMOTE_TRACE_FIELD__CREDENTIAL_TIMESTAMP,c.timestamp());
+ d.add(ZT_REMOTE_TRACE_FIELD__CREDENTIAL_ISSUED_TO,c.issuedTo());
+ if (reason)
+ d.add(ZT_REMOTE_TRACE_FIELD__REASON,reason);
+
+ if (_globalTarget)
+ _send(tPtr,d,_globalTarget);
+ if (byn.first)
+ _send(tPtr,d,byn.first);
+ }
+}
+
+void Trace::credentialRejected(void *const tPtr,const Capability &c,const char *reason)
+{
+ std::pair<Address,Trace::Level> byn;
+ if (c.networkId()) { Mutex::Lock l(_byNet_m); _byNet.get(c.networkId(),byn); }
+
+ if ((_globalTarget)||(byn.first)) {
+ Dictionary<ZT_MAX_REMOTE_TRACE_SIZE> d;
+ d.add(ZT_REMOTE_TRACE_FIELD__EVENT,ZT_REMOTE_TRACE_EVENT__CREDENTIAL_REJECTED_S);
+ d.add(ZT_REMOTE_TRACE_FIELD__NETWORK_ID,c.networkId());
+ d.add(ZT_REMOTE_TRACE_FIELD__CREDENTIAL_TYPE,(uint64_t)c.credentialType());
+ d.add(ZT_REMOTE_TRACE_FIELD__CREDENTIAL_ID,(uint64_t)c.id());
+ d.add(ZT_REMOTE_TRACE_FIELD__CREDENTIAL_TIMESTAMP,c.timestamp());
+ d.add(ZT_REMOTE_TRACE_FIELD__CREDENTIAL_ISSUED_TO,c.issuedTo());
+ if (reason)
+ d.add(ZT_REMOTE_TRACE_FIELD__REASON,reason);
+
+ if (_globalTarget)
+ _send(tPtr,d,_globalTarget);
+ if (byn.first)
+ _send(tPtr,d,byn.first);
+ }
+}
+
+void Trace::credentialRejected(void *const tPtr,const Tag &c,const char *reason)
+{
+ std::pair<Address,Trace::Level> byn;
+ if (c.networkId()) { Mutex::Lock l(_byNet_m); _byNet.get(c.networkId(),byn); }
+
+ if ((_globalTarget)||(byn.first)) {
+ Dictionary<ZT_MAX_REMOTE_TRACE_SIZE> d;
+ d.add(ZT_REMOTE_TRACE_FIELD__EVENT,ZT_REMOTE_TRACE_EVENT__CREDENTIAL_REJECTED_S);
+ d.add(ZT_REMOTE_TRACE_FIELD__NETWORK_ID,c.networkId());
+ d.add(ZT_REMOTE_TRACE_FIELD__CREDENTIAL_TYPE,(uint64_t)c.credentialType());
+ d.add(ZT_REMOTE_TRACE_FIELD__CREDENTIAL_ID,(uint64_t)c.id());
+ d.add(ZT_REMOTE_TRACE_FIELD__CREDENTIAL_TIMESTAMP,c.timestamp());
+ d.add(ZT_REMOTE_TRACE_FIELD__CREDENTIAL_ISSUED_TO,c.issuedTo());
+ d.add(ZT_REMOTE_TRACE_FIELD__CREDENTIAL_INFO,(uint64_t)c.value());
+ if (reason)
+ d.add(ZT_REMOTE_TRACE_FIELD__REASON,reason);
+
+ if (_globalTarget)
+ _send(tPtr,d,_globalTarget);
+ if (byn.first)
+ _send(tPtr,d,byn.first);
+ }
+}
+
+void Trace::credentialRejected(void *const tPtr,const Revocation &c,const char *reason)
+{
+ std::pair<Address,Trace::Level> byn;
+ if (c.networkId()) { Mutex::Lock l(_byNet_m); _byNet.get(c.networkId(),byn); }
+
+ if ((_globalTarget)||(byn.first)) {
+ Dictionary<ZT_MAX_REMOTE_TRACE_SIZE> d;
+ d.add(ZT_REMOTE_TRACE_FIELD__EVENT,ZT_REMOTE_TRACE_EVENT__CREDENTIAL_REJECTED_S);
+ d.add(ZT_REMOTE_TRACE_FIELD__NETWORK_ID,c.networkId());
+ d.add(ZT_REMOTE_TRACE_FIELD__CREDENTIAL_TYPE,(uint64_t)c.credentialType());
+ d.add(ZT_REMOTE_TRACE_FIELD__CREDENTIAL_ID,(uint64_t)c.id());
+ d.add(ZT_REMOTE_TRACE_FIELD__CREDENTIAL_REVOCATION_TARGET,c.target());
+ if (reason)
+ d.add(ZT_REMOTE_TRACE_FIELD__REASON,reason);
+
+ if (_globalTarget)
+ _send(tPtr,d,_globalTarget);
+ if (byn.first)
+ _send(tPtr,d,byn.first);
+ }
+}
+
+void Trace::updateMemoizedSettings()
+{
+ _globalTarget = RR->node->remoteTraceTarget();
+ _globalLevel = RR->node->remoteTraceLevel();
+ const std::vector< SharedPtr<Network> > nws(RR->node->allNetworks());
+ {
+ Mutex::Lock l(_byNet_m);
+ _byNet.clear();
+ for(std::vector< SharedPtr<Network> >::const_iterator n(nws.begin());n!=nws.end();++n) {
+ const Address dest((*n)->config().remoteTraceTarget);
+ if (dest) {
+ std::pair<Address,Trace::Level> &m = _byNet[(*n)->id()];
+ m.first = dest;
+ m.second = (*n)->config().remoteTraceLevel;
+ }
+ }
+ }
+}
+
+void Trace::_send(void *const tPtr,const Dictionary<ZT_MAX_REMOTE_TRACE_SIZE> &d,const Address &dest)
+{
+ Packet outp(dest,RR->identity.address(),Packet::VERB_REMOTE_TRACE);
+ outp.appendCString(d.data());
+ outp.compress();
+ RR->sw->send(tPtr,outp,true);
+}
+
+void Trace::_spamToAllNetworks(void *const tPtr,const Dictionary<ZT_MAX_REMOTE_TRACE_SIZE> &d,const Level level)
+{
+ Mutex::Lock l(_byNet_m);
+ Hashtable< uint64_t,std::pair< Address,Trace::Level > >::Iterator i(_byNet);
+ uint64_t *k = (uint64_t *)0;
+ std::pair<Address,Trace::Level> *v = (std::pair<Address,Trace::Level> *)0;
+ while (i.next(k,v)) {
+ if ((v)&&(v->first)&&((int)v->second >= (int)level))
+ _send(tPtr,d,v->first);
+ }
+}
+
+} // namespace ZeroTier
diff --git a/node/Trace.hpp b/node/Trace.hpp
new file mode 100644
index 00000000..2a2fca6c
--- /dev/null
+++ b/node/Trace.hpp
@@ -0,0 +1,176 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
+ */
+
+#ifndef ZT_TRACE_HPP
+#define ZT_TRACE_HPP
+
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "../include/ZeroTierOne.h"
+
+#include "Constants.hpp"
+#include "SharedPtr.hpp"
+#include "Packet.hpp"
+#include "Credential.hpp"
+#include "InetAddress.hpp"
+#include "Dictionary.hpp"
+#include "Mutex.hpp"
+#include "Hashtable.hpp"
+
+namespace ZeroTier {
+
+class RuntimeEnvironment;
+class Address;
+class Identity;
+class Peer;
+class Path;
+class Network;
+class NetworkConfig;
+class MAC;
+class CertificateOfMembership;
+class CertificateOfOwnership;
+class Revocation;
+class Tag;
+class Capability;
+
+/**
+ * Remote tracing and trace logging handler
+ */
+class Trace
+{
+public:
+ /**
+ * Trace verbosity level
+ */
+ enum Level
+ {
+ LEVEL_NORMAL = 0,
+ LEVEL_VERBOSE = 10,
+ LEVEL_RULES = 15,
+ LEVEL_DEBUG = 20,
+ LEVEL_INSANE = 30
+ };
+
+ /**
+ * Filter rule evaluation result log
+ *
+ * Each rule in a rule set gets a four-bit log entry. A log entry
+ * of zero means not evaluated. Otherwise each four-bit log entry
+ * contains two two-bit values of 01 for 'false' and 10 for 'true'.
+ * As with four-bit rules an 00 value here means this was not
+ * evaluated or was not relevant.
+ */
+ class RuleResultLog
+ {
+ public:
+ RuleResultLog() {}
+
+ inline void log(const unsigned int rn,const uint8_t thisRuleMatches,const uint8_t thisSetMatches)
+ {
+ _l[rn >> 1] |= ( ((thisRuleMatches + 1) << 2) | (thisSetMatches + 1) ) << ((rn & 1) << 2);
+ }
+ inline void logSkipped(const unsigned int rn,const uint8_t thisSetMatches)
+ {
+ _l[rn >> 1] |= (thisSetMatches + 1) << ((rn & 1) << 2);
+ }
+
+ inline void clear()
+ {
+ memset(_l,0,sizeof(_l));
+ }
+
+ inline const uint8_t *data() const { return _l; }
+ inline unsigned int sizeBytes() const { return (ZT_MAX_NETWORK_RULES / 2); }
+
+ private:
+ uint8_t _l[ZT_MAX_NETWORK_RULES / 2];
+ };
+
+ Trace(const RuntimeEnvironment *renv) :
+ RR(renv),
+ _byNet(8)
+ {
+ }
+
+ void resettingPathsInScope(void *const tPtr,const Address &reporter,const InetAddress &reporterPhysicalAddress,const InetAddress &myPhysicalAddress,const InetAddress::IpScope scope);
+
+ void peerConfirmingUnknownPath(void *const tPtr,const uint64_t networkId,Peer &peer,const SharedPtr<Path> &path,const uint64_t packetId,const Packet::Verb verb);
+ void peerLearnedNewPath(void *const tPtr,const uint64_t networkId,Peer &peer,const SharedPtr<Path> &newPath,const uint64_t packetId);
+ void peerRedirected(void *const tPtr,const uint64_t networkId,Peer &peer,const SharedPtr<Path> &newPath);
+
+ void incomingPacketMessageAuthenticationFailure(void *const tPtr,const SharedPtr<Path> &path,const uint64_t packetId,const Address &source,const unsigned int hops,const char *reason);
+ void incomingPacketInvalid(void *const tPtr,const SharedPtr<Path> &path,const uint64_t packetId,const Address &source,const unsigned int hops,const Packet::Verb verb,const char *reason);
+ void incomingPacketDroppedHELLO(void *const tPtr,const SharedPtr<Path> &path,const uint64_t packetId,const Address &source,const char *reason);
+
+ void outgoingNetworkFrameDropped(void *const tPtr,const SharedPtr<Network> &network,const MAC &sourceMac,const MAC &destMac,const unsigned int etherType,const unsigned int vlanId,const unsigned int frameLen,const char *reason);
+ void incomingNetworkAccessDenied(void *const tPtr,const SharedPtr<Network> &network,const SharedPtr<Path> &path,const uint64_t packetId,const unsigned int packetLength,const Address &source,const Packet::Verb verb,bool credentialsRequested);
+ void incomingNetworkFrameDropped(void *const tPtr,const SharedPtr<Network> &network,const SharedPtr<Path> &path,const uint64_t packetId,const unsigned int packetLength,const Address &source,const Packet::Verb verb,const MAC &sourceMac,const MAC &destMac,const char *reason);
+
+ void networkConfigRequestSent(void *const tPtr,const Network &network,const Address &controller);
+ void networkFilter(
+ void *const tPtr,
+ const Network &network,
+ const RuleResultLog &primaryRuleSetLog,
+ const RuleResultLog *const matchingCapabilityRuleSetLog,
+ const Capability *const matchingCapability,
+ const Address &ztSource,
+ const Address &ztDest,
+ const MAC &macSource,
+ const MAC &macDest,
+ const uint8_t *const frameData,
+ const unsigned int frameLen,
+ const unsigned int etherType,
+ const unsigned int vlanId,
+ const bool noTee,
+ const bool inbound,
+ const int accept);
+
+ void credentialRejected(void *const tPtr,const CertificateOfMembership &c,const char *reason);
+ void credentialRejected(void *const tPtr,const CertificateOfOwnership &c,const char *reason);
+ void credentialRejected(void *const tPtr,const Capability &c,const char *reason);
+ void credentialRejected(void *const tPtr,const Tag &c,const char *reason);
+ void credentialRejected(void *const tPtr,const Revocation &c,const char *reason);
+
+ void updateMemoizedSettings();
+
+private:
+ const RuntimeEnvironment *const RR;
+
+ void _send(void *const tPtr,const Dictionary<ZT_MAX_REMOTE_TRACE_SIZE> &d,const Address &dest);
+ void _spamToAllNetworks(void *const tPtr,const Dictionary<ZT_MAX_REMOTE_TRACE_SIZE> &d,const Level level);
+
+ Address _globalTarget;
+ Trace::Level _globalLevel;
+ Hashtable< uint64_t,std::pair< Address,Trace::Level > > _byNet;
+ Mutex _byNet_m;
+};
+
+} // namespace ZeroTier
+
+#endif
diff --git a/node/Utils.cpp b/node/Utils.cpp
index 2d9515ee..a69a575e 100644
--- a/node/Utils.cpp
+++ b/node/Utils.cpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#include <stdio.h>
@@ -47,96 +55,34 @@ namespace ZeroTier {
const char Utils::HEXCHARS[16] = { '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f' };
-static void _Utils_doBurn(char *ptr,unsigned int len)
-{
- for(unsigned int i=0;i<len;++i)
- ptr[i] = (char)0;
-}
-void (*volatile _Utils_doBurn_ptr)(char *,unsigned int) = _Utils_doBurn;
-void Utils::burn(void *ptr,unsigned int len)
- throw()
+// Crazy hack to force memory to be securely zeroed in spite of the best efforts of optimizing compilers.
+static void _Utils_doBurn(volatile uint8_t *ptr,unsigned int len)
{
- // Ridiculous hack: call _doBurn() via a volatile function pointer to
- // hold down compiler optimizers and beat them mercilessly until they
- // cry and mumble something about never eliding secure memory zeroing
- // again.
- (_Utils_doBurn_ptr)((char *)ptr,len);
+ volatile uint8_t *const end = ptr + len;
+ while (ptr != end) *(ptr++) = (uint8_t)0;
}
+static void (*volatile _Utils_doBurn_ptr)(volatile uint8_t *,unsigned int) = _Utils_doBurn;
+void Utils::burn(void *ptr,unsigned int len) { (_Utils_doBurn_ptr)((volatile uint8_t *)ptr,len); }
-std::string Utils::hex(const void *data,unsigned int len)
+static unsigned long _Utils_itoa(unsigned long n,char *s)
{
- std::string r;
- r.reserve(len * 2);
- for(unsigned int i=0;i<len;++i) {
- r.push_back(HEXCHARS[(((const unsigned char *)data)[i] & 0xf0) >> 4]);
- r.push_back(HEXCHARS[((const unsigned char *)data)[i] & 0x0f]);
- }
- return r;
+ if (n == 0)
+ return 0;
+ unsigned long pos = _Utils_itoa(n / 10,s);
+ if (pos >= 22) // sanity check, should be impossible
+ pos = 22;
+ s[pos] = '0' + (char)(n % 10);
+ return pos + 1;
}
-
-std::string Utils::unhex(const char *hex,unsigned int maxlen)
+char *Utils::decimal(unsigned long n,char s[24])
{
- int n = 1;
- unsigned char c,b = 0;
- const char *eof = hex + maxlen;
- std::string r;
-
- if (!maxlen)
- return r;
-
- while ((c = (unsigned char)*(hex++))) {
- if ((c >= 48)&&(c <= 57)) { // 0..9
- if ((n ^= 1))
- r.push_back((char)(b | (c - 48)));
- else b = (c - 48) << 4;
- } else if ((c >= 65)&&(c <= 70)) { // A..F
- if ((n ^= 1))
- r.push_back((char)(b | (c - (65 - 10))));
- else b = (c - (65 - 10)) << 4;
- } else if ((c >= 97)&&(c <= 102)) { // a..f
- if ((n ^= 1))
- r.push_back((char)(b | (c - (97 - 10))));
- else b = (c - (97 - 10)) << 4;
- }
- if (hex == eof)
- break;
+ if (n == 0) {
+ s[0] = '0';
+ s[1] = (char)0;
+ return s;
}
-
- return r;
-}
-
-unsigned int Utils::unhex(const char *hex,unsigned int maxlen,void *buf,unsigned int len)
-{
- int n = 1;
- unsigned char c,b = 0;
- unsigned int l = 0;
- const char *eof = hex + maxlen;
-
- if (!maxlen)
- return 0;
-
- while ((c = (unsigned char)*(hex++))) {
- if ((c >= 48)&&(c <= 57)) { // 0..9
- if ((n ^= 1)) {
- if (l >= len) break;
- ((unsigned char *)buf)[l++] = (b | (c - 48));
- } else b = (c - 48) << 4;
- } else if ((c >= 65)&&(c <= 70)) { // A..F
- if ((n ^= 1)) {
- if (l >= len) break;
- ((unsigned char *)buf)[l++] = (b | (c - (65 - 10)));
- } else b = (c - (65 - 10)) << 4;
- } else if ((c >= 97)&&(c <= 102)) { // a..f
- if ((n ^= 1)) {
- if (l >= len) break;
- ((unsigned char *)buf)[l++] = (b | (c - (97 - 10)));
- } else b = (c - (97 - 10)) << 4;
- }
- if (hex == eof)
- break;
- }
-
- return l;
+ s[_Utils_itoa(n,s)] = (char)0;
+ return s;
}
void Utils::getSecureRandom(void *buf,unsigned int bytes)
@@ -144,6 +90,8 @@ void Utils::getSecureRandom(void *buf,unsigned int bytes)
static Mutex globalLock;
static Salsa20 s20;
static bool s20Initialized = false;
+ static uint8_t randomBuf[65536];
+ static unsigned int randomPtr = sizeof(randomBuf);
Mutex::Lock _l(globalLock);
@@ -161,34 +109,39 @@ void Utils::getSecureRandom(void *buf,unsigned int bytes)
s20Key[1] = (uint64_t)buf; // address of buf
s20Key[2] = (uint64_t)s20Key; // address of s20Key[]
s20Key[3] = (uint64_t)&s20; // address of s20
- s20.init(s20Key,256,s20Key);
+ s20.init(s20Key,s20Key);
}
#ifdef __WINDOWS__
static HCRYPTPROV cryptProvider = NULL;
- if (cryptProvider == NULL) {
- if (!CryptAcquireContextA(&cryptProvider,NULL,NULL,PROV_RSA_FULL,CRYPT_VERIFYCONTEXT|CRYPT_SILENT)) {
- fprintf(stderr,"FATAL ERROR: Utils::getSecureRandom() unable to obtain WinCrypt context!\r\n");
- exit(1);
- return;
+ for(unsigned int i=0;i<bytes;++i) {
+ if (randomPtr >= sizeof(randomBuf)) {
+ if (cryptProvider == NULL) {
+ if (!CryptAcquireContextA(&cryptProvider,NULL,NULL,PROV_RSA_FULL,CRYPT_VERIFYCONTEXT|CRYPT_SILENT)) {
+ fprintf(stderr,"FATAL ERROR: Utils::getSecureRandom() unable to obtain WinCrypt context!\r\n");
+ exit(1);
+ }
+ }
+ if (!CryptGenRandom(cryptProvider,(DWORD)sizeof(randomBuf),(BYTE *)randomBuf)) {
+ fprintf(stderr,"FATAL ERROR: Utils::getSecureRandom() CryptGenRandom failed!\r\n");
+ exit(1);
+ }
+ randomPtr = 0;
+ s20.crypt12(randomBuf,randomBuf,sizeof(randomBuf));
+ s20.init(randomBuf,randomBuf);
}
- }
- if (!CryptGenRandom(cryptProvider,(DWORD)bytes,(BYTE *)buf)) {
- fprintf(stderr,"FATAL ERROR: Utils::getSecureRandom() CryptGenRandom failed!\r\n");
- exit(1);
+ ((uint8_t *)buf)[i] = randomBuf[randomPtr++];
}
#else // not __WINDOWS__
- static char randomBuf[131072];
- static unsigned int randomPtr = sizeof(randomBuf);
static int devURandomFd = -1;
- if (devURandomFd <= 0) {
+ if (devURandomFd < 0) {
devURandomFd = ::open("/dev/urandom",O_RDONLY);
- if (devURandomFd <= 0) {
+ if (devURandomFd < 0) {
fprintf(stderr,"FATAL ERROR: Utils::getSecureRandom() unable to open /dev/urandom\n");
exit(1);
return;
@@ -201,7 +154,7 @@ void Utils::getSecureRandom(void *buf,unsigned int bytes)
if ((int)::read(devURandomFd,randomBuf,sizeof(randomBuf)) != (int)sizeof(randomBuf)) {
::close(devURandomFd);
devURandomFd = ::open("/dev/urandom",O_RDONLY);
- if (devURandomFd <= 0) {
+ if (devURandomFd < 0) {
fprintf(stderr,"FATAL ERROR: Utils::getSecureRandom() unable to open /dev/urandom\n");
exit(1);
return;
@@ -209,93 +162,13 @@ void Utils::getSecureRandom(void *buf,unsigned int bytes)
} else break;
}
randomPtr = 0;
+ s20.crypt12(randomBuf,randomBuf,sizeof(randomBuf));
+ s20.init(randomBuf,randomBuf);
}
- ((char *)buf)[i] = randomBuf[randomPtr++];
+ ((uint8_t *)buf)[i] = randomBuf[randomPtr++];
}
#endif // __WINDOWS__ or not
-
- s20.encrypt12(buf,buf,bytes);
-}
-
-std::vector<std::string> Utils::split(const char *s,const char *const sep,const char *esc,const char *quot)
-{
- std::vector<std::string> fields;
- std::string buf;
-
- if (!esc)
- esc = "";
- if (!quot)
- quot = "";
-
- bool escapeState = false;
- char quoteState = 0;
- while (*s) {
- if (escapeState) {
- escapeState = false;
- buf.push_back(*s);
- } else if (quoteState) {
- if (*s == quoteState) {
- quoteState = 0;
- fields.push_back(buf);
- buf.clear();
- } else buf.push_back(*s);
- } else {
- const char *quotTmp;
- if (strchr(esc,*s))
- escapeState = true;
- else if ((buf.size() <= 0)&&((quotTmp = strchr(quot,*s))))
- quoteState = *quotTmp;
- else if (strchr(sep,*s)) {
- if (buf.size() > 0) {
- fields.push_back(buf);
- buf.clear();
- } // else skip runs of seperators
- } else buf.push_back(*s);
- }
- ++s;
- }
-
- if (buf.size())
- fields.push_back(buf);
-
- return fields;
-}
-
-bool Utils::scopy(char *dest,unsigned int len,const char *src)
-{
- if (!len)
- return false; // sanity check
- if (!src) {
- *dest = (char)0;
- return true;
- }
- char *end = dest + len;
- while ((*dest++ = *src++)) {
- if (dest == end) {
- *(--dest) = (char)0;
- return false;
- }
- }
- return true;
-}
-
-unsigned int Utils::snprintf(char *buf,unsigned int len,const char *fmt,...)
- throw(std::length_error)
-{
- va_list ap;
-
- va_start(ap,fmt);
- int n = (int)vsnprintf(buf,len,fmt,ap);
- va_end(ap);
-
- if ((n >= (int)len)||(n < 0)) {
- if (len)
- buf[len - 1] = (char)0;
- throw std::length_error("buf[] overflow in Utils::snprintf");
- }
-
- return (unsigned int)n;
}
} // namespace ZeroTier
diff --git a/node/Utils.hpp b/node/Utils.hpp
index cfe56501..a24f2c9a 100644
--- a/node/Utils.hpp
+++ b/node/Utils.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_UTILS_HPP
@@ -32,6 +40,44 @@
#include "Constants.hpp"
+#ifdef __LINUX__
+//#if (defined(_MSC_VER) || defined(__GNUC__)) && (defined(__amd64) || defined(__amd64__) || defined(__x86_64) || defined(__x86_64__) || defined(__AMD64) || defined(__AMD64__) || defined(_M_X64))
+#if 0
+#include <emmintrin.h>
+static inline void ZT_FAST_MEMCPY(void *a,const void *b,unsigned long k)
+{
+ char *aa = reinterpret_cast<char *>(a);
+ const char *bb = reinterpret_cast<const char *>(b);
+ while (k >= 64) {
+ __m128 t1 = _mm_loadu_ps(reinterpret_cast<const float *>(bb));
+ __m128 t2 = _mm_loadu_ps(reinterpret_cast<const float *>(bb + 16));
+ __m128 t3 = _mm_loadu_ps(reinterpret_cast<const float *>(bb + 32));
+ __m128 t4 = _mm_loadu_ps(reinterpret_cast<const float *>(bb + 48));
+ _mm_storeu_ps(reinterpret_cast<float *>(aa),t1);
+ _mm_storeu_ps(reinterpret_cast<float *>(aa + 16),t2);
+ _mm_storeu_ps(reinterpret_cast<float *>(aa + 32),t3);
+ _mm_storeu_ps(reinterpret_cast<float *>(aa + 48),t4);
+ bb += 64;
+ aa += 64;
+ k -= 64;
+ }
+ while (k >= 16) {
+ __m128 t1 = _mm_loadu_ps(reinterpret_cast<const float *>(bb));
+ _mm_storeu_ps(reinterpret_cast<float *>(aa),t1);
+ bb += 16;
+ aa += 16;
+ k -= 16;
+ }
+ for(unsigned long i=0;i<k;++i)
+ aa[i] = bb[i];
+}
+#else
+#define ZT_FAST_MEMCPY(a,b,c) memcpy(a,b,c)
+#endif
+#else
+#define ZT_FAST_MEMCPY(a,b,c) memcpy(a,b,c)
+#endif
+
namespace ZeroTier {
/**
@@ -59,46 +105,161 @@ public:
/**
* Securely zero memory, avoiding compiler optimizations and such
*/
- static void burn(void *ptr,unsigned int len)
- throw();
+ static void burn(void *ptr,unsigned int len);
/**
- * Convert binary data to hexadecimal
- *
- * @param data Data to convert to hex
- * @param len Length of data
- * @return Hexadecimal string
+ * @param n Number to convert
+ * @param s Buffer, at least 24 bytes in size
+ * @return String containing 'n' in base 10 form
*/
- static std::string hex(const void *data,unsigned int len);
- static inline std::string hex(const std::string &data) { return hex(data.data(),(unsigned int)data.length()); }
+ static char *decimal(unsigned long n,char s[24]);
- /**
- * Convert hexadecimal to binary data
- *
- * This ignores all non-hex characters, just stepping over them and
- * continuing. Upper and lower case are supported for letters a-f.
- *
- * @param hex Hexadecimal ASCII code (non-hex chars are ignored, stops at zero or maxlen)
- * @param maxlen Maximum length of hex string buffer
- * @return Binary data
- */
- static std::string unhex(const char *hex,unsigned int maxlen);
- static inline std::string unhex(const std::string &hex) { return unhex(hex.c_str(),(unsigned int)hex.length()); }
+ static inline char *hex(uint64_t i,char s[17])
+ {
+ s[0] = HEXCHARS[(i >> 60) & 0xf];
+ s[1] = HEXCHARS[(i >> 56) & 0xf];
+ s[2] = HEXCHARS[(i >> 52) & 0xf];
+ s[3] = HEXCHARS[(i >> 48) & 0xf];
+ s[4] = HEXCHARS[(i >> 44) & 0xf];
+ s[5] = HEXCHARS[(i >> 40) & 0xf];
+ s[6] = HEXCHARS[(i >> 36) & 0xf];
+ s[7] = HEXCHARS[(i >> 32) & 0xf];
+ s[8] = HEXCHARS[(i >> 28) & 0xf];
+ s[9] = HEXCHARS[(i >> 24) & 0xf];
+ s[10] = HEXCHARS[(i >> 20) & 0xf];
+ s[11] = HEXCHARS[(i >> 16) & 0xf];
+ s[12] = HEXCHARS[(i >> 12) & 0xf];
+ s[13] = HEXCHARS[(i >> 8) & 0xf];
+ s[14] = HEXCHARS[(i >> 4) & 0xf];
+ s[15] = HEXCHARS[i & 0xf];
+ s[16] = (char)0;
+ return s;
+ }
- /**
- * Convert hexadecimal to binary data
- *
- * This ignores all non-hex characters, just stepping over them and
- * continuing. Upper and lower case are supported for letters a-f.
- *
- * @param hex Hexadecimal ASCII
- * @param maxlen Maximum length of hex string buffer
- * @param buf Buffer to fill
- * @param len Length of buffer
- * @return Number of characters actually written
- */
- static unsigned int unhex(const char *hex,unsigned int maxlen,void *buf,unsigned int len);
- static inline unsigned int unhex(const std::string &hex,void *buf,unsigned int len) { return unhex(hex.c_str(),(unsigned int)hex.length(),buf,len); }
+ static inline char *hex10(uint64_t i,char s[11])
+ {
+ s[0] = HEXCHARS[(i >> 36) & 0xf];
+ s[1] = HEXCHARS[(i >> 32) & 0xf];
+ s[2] = HEXCHARS[(i >> 28) & 0xf];
+ s[3] = HEXCHARS[(i >> 24) & 0xf];
+ s[4] = HEXCHARS[(i >> 20) & 0xf];
+ s[5] = HEXCHARS[(i >> 16) & 0xf];
+ s[6] = HEXCHARS[(i >> 12) & 0xf];
+ s[7] = HEXCHARS[(i >> 8) & 0xf];
+ s[8] = HEXCHARS[(i >> 4) & 0xf];
+ s[9] = HEXCHARS[i & 0xf];
+ s[10] = (char)0;
+ return s;
+ }
+
+ static inline char *hex(uint32_t i,char s[9])
+ {
+ s[0] = HEXCHARS[(i >> 28) & 0xf];
+ s[1] = HEXCHARS[(i >> 24) & 0xf];
+ s[2] = HEXCHARS[(i >> 20) & 0xf];
+ s[3] = HEXCHARS[(i >> 16) & 0xf];
+ s[4] = HEXCHARS[(i >> 12) & 0xf];
+ s[5] = HEXCHARS[(i >> 8) & 0xf];
+ s[6] = HEXCHARS[(i >> 4) & 0xf];
+ s[7] = HEXCHARS[i & 0xf];
+ s[8] = (char)0;
+ return s;
+ }
+
+ static inline char *hex(uint16_t i,char s[5])
+ {
+ s[0] = HEXCHARS[(i >> 12) & 0xf];
+ s[1] = HEXCHARS[(i >> 8) & 0xf];
+ s[2] = HEXCHARS[(i >> 4) & 0xf];
+ s[3] = HEXCHARS[i & 0xf];
+ s[4] = (char)0;
+ return s;
+ }
+
+ static inline char *hex(uint8_t i,char s[3])
+ {
+ s[0] = HEXCHARS[(i >> 4) & 0xf];
+ s[1] = HEXCHARS[i & 0xf];
+ s[2] = (char)0;
+ return s;
+ }
+
+ static inline char *hex(const void *d,unsigned int l,char *s)
+ {
+ char *const save = s;
+ for(unsigned int i=0;i<l;++i) {
+ const unsigned int b = reinterpret_cast<const uint8_t *>(d)[i];
+ *(s++) = HEXCHARS[b >> 4];
+ *(s++) = HEXCHARS[b & 0xf];
+ }
+ *s = (char)0;
+ return save;
+ }
+
+ static inline unsigned int unhex(const char *h,void *buf,unsigned int buflen)
+ {
+ unsigned int l = 0;
+ while (l < buflen) {
+ uint8_t hc = *(reinterpret_cast<const uint8_t *>(h++));
+ if (!hc) break;
+
+ uint8_t c = 0;
+ if ((hc >= 48)&&(hc <= 57)) // 0..9
+ c = hc - 48;
+ else if ((hc >= 97)&&(hc <= 102)) // a..f
+ c = hc - 87;
+ else if ((hc >= 65)&&(hc <= 70)) // A..F
+ c = hc - 55;
+
+ hc = *(reinterpret_cast<const uint8_t *>(h++));
+ if (!hc) break;
+
+ c <<= 4;
+ if ((hc >= 48)&&(hc <= 57))
+ c |= hc - 48;
+ else if ((hc >= 97)&&(hc <= 102))
+ c |= hc - 87;
+ else if ((hc >= 65)&&(hc <= 70))
+ c |= hc - 55;
+
+ reinterpret_cast<uint8_t *>(buf)[l++] = c;
+ }
+ return l;
+ }
+
+ static inline unsigned int unhex(const char *h,unsigned int hlen,void *buf,unsigned int buflen)
+ {
+ unsigned int l = 0;
+ const char *hend = h + hlen;
+ while (l < buflen) {
+ if (h == hend) break;
+ uint8_t hc = *(reinterpret_cast<const uint8_t *>(h++));
+ if (!hc) break;
+
+ uint8_t c = 0;
+ if ((hc >= 48)&&(hc <= 57))
+ c = hc - 48;
+ else if ((hc >= 97)&&(hc <= 102))
+ c = hc - 87;
+ else if ((hc >= 65)&&(hc <= 70))
+ c = hc - 55;
+
+ if (h == hend) break;
+ hc = *(reinterpret_cast<const uint8_t *>(h++));
+ if (!hc) break;
+
+ c <<= 4;
+ if ((hc >= 48)&&(hc <= 57))
+ c |= hc - 48;
+ else if ((hc >= 97)&&(hc <= 102))
+ c |= hc - 87;
+ else if ((hc >= 65)&&(hc <= 70))
+ c |= hc - 55;
+
+ reinterpret_cast<uint8_t *>(buf)[l++] = c;
+ }
+ return l;
+ }
/**
* Generate secure random bytes
@@ -112,17 +273,6 @@ public:
static void getSecureRandom(void *buf,unsigned int bytes);
/**
- * Split a string by delimiter, with optional escape and quote characters
- *
- * @param s String to split
- * @param sep One or more separators
- * @param esc Zero or more escape characters
- * @param quot Zero or more quote characters
- * @return Vector of tokens
- */
- static std::vector<std::string> split(const char *s,const char *const sep,const char *esc,const char *quot);
-
- /**
* Tokenize a string (alias for strtok_r or strtok_s depending on platform)
*
* @param str String to split
@@ -130,7 +280,6 @@ public:
* @param saveptr Pointer to a char * for temporary reentrant storage
*/
static inline char *stok(char *str,const char *delim,char **saveptr)
- throw()
{
#ifdef __WINDOWS__
return strtok_s(str,delim,saveptr);
@@ -139,30 +288,11 @@ public:
#endif
}
- // String to number converters -- defined here to permit portability
- // ifdefs for platforms that lack some of the strtoXX functions.
- static inline unsigned int strToUInt(const char *s)
- throw()
- {
- return (unsigned int)strtoul(s,(char **)0,10);
- }
- static inline int strToInt(const char *s)
- throw()
- {
- return (int)strtol(s,(char **)0,10);
- }
- static inline unsigned long strToULong(const char *s)
- throw()
- {
- return strtoul(s,(char **)0,10);
- }
- static inline long strToLong(const char *s)
- throw()
- {
- return strtol(s,(char **)0,10);
- }
+ static inline unsigned int strToUInt(const char *s) { return (unsigned int)strtoul(s,(char **)0,10); }
+ static inline int strToInt(const char *s) { return (int)strtol(s,(char **)0,10); }
+ static inline unsigned long strToULong(const char *s) { return strtoul(s,(char **)0,10); }
+ static inline long strToLong(const char *s) { return strtol(s,(char **)0,10); }
static inline unsigned long long strToU64(const char *s)
- throw()
{
#ifdef __WINDOWS__
return (unsigned long long)_strtoui64(s,(char **)0,10);
@@ -171,7 +301,6 @@ public:
#endif
}
static inline long long strTo64(const char *s)
- throw()
{
#ifdef __WINDOWS__
return (long long)_strtoi64(s,(char **)0,10);
@@ -179,28 +308,11 @@ public:
return strtoll(s,(char **)0,10);
#endif
}
- static inline unsigned int hexStrToUInt(const char *s)
- throw()
- {
- return (unsigned int)strtoul(s,(char **)0,16);
- }
- static inline int hexStrToInt(const char *s)
- throw()
- {
- return (int)strtol(s,(char **)0,16);
- }
- static inline unsigned long hexStrToULong(const char *s)
- throw()
- {
- return strtoul(s,(char **)0,16);
- }
- static inline long hexStrToLong(const char *s)
- throw()
- {
- return strtol(s,(char **)0,16);
- }
+ static inline unsigned int hexStrToUInt(const char *s) { return (unsigned int)strtoul(s,(char **)0,16); }
+ static inline int hexStrToInt(const char *s) { return (int)strtol(s,(char **)0,16); }
+ static inline unsigned long hexStrToULong(const char *s) { return strtoul(s,(char **)0,16); }
+ static inline long hexStrToLong(const char *s) { return strtol(s,(char **)0,16); }
static inline unsigned long long hexStrToU64(const char *s)
- throw()
{
#ifdef __WINDOWS__
return (unsigned long long)_strtoui64(s,(char **)0,16);
@@ -209,7 +321,6 @@ public:
#endif
}
static inline long long hexStrTo64(const char *s)
- throw()
{
#ifdef __WINDOWS__
return (long long)_strtoi64(s,(char **)0,16);
@@ -217,11 +328,6 @@ public:
return strtoll(s,(char **)0,16);
#endif
}
- static inline double strToDouble(const char *s)
- throw()
- {
- return strtod(s,(char **)0);
- }
/**
* Perform a safe C string copy, ALWAYS null-terminating the result
@@ -234,22 +340,23 @@ public:
* @param src Source string (if NULL, dest will receive a zero-length string and true is returned)
* @return True on success, false on overflow (buffer will still be 0-terminated)
*/
- static bool scopy(char *dest,unsigned int len,const char *src);
-
- /**
- * Variant of snprintf that is portable and throws an exception
- *
- * This just wraps the local implementation whatever it's called, while
- * performing a few other checks and adding exceptions for overflow.
- *
- * @param buf Buffer to write to
- * @param len Length of buffer in bytes
- * @param fmt Format string
- * @param ... Format arguments
- * @throws std::length_error buf[] too short (buf[] will still be left null-terminated)
- */
- static unsigned int snprintf(char *buf,unsigned int len,const char *fmt,...)
- throw(std::length_error);
+ static inline bool scopy(char *dest,unsigned int len,const char *src)
+ {
+ if (!len)
+ return false; // sanity check
+ if (!src) {
+ *dest = (char)0;
+ return true;
+ }
+ char *end = dest + len;
+ while ((*dest++ = *src++)) {
+ if (dest == end) {
+ *(--dest) = (char)0;
+ return false;
+ }
+ }
+ return true;
+ }
/**
* Count the number of bits set in an integer
@@ -265,6 +372,20 @@ public:
}
/**
+ * Count the number of bits set in an integer
+ *
+ * @param v 64-bit integer
+ * @return Number of bits set in this integer (0-64)
+ */
+ static inline uint64_t countBits(uint64_t v)
+ {
+ v = v - ((v >> 1) & (uint64_t)~(uint64_t)0/3);
+ v = (v & (uint64_t)~(uint64_t)0/15*3) + ((v >> 2) & (uint64_t)~(uint64_t)0/15*3);
+ v = (v + (v >> 4)) & (uint64_t)~(uint64_t)0/255*15;
+ return (uint64_t)(v * ((uint64_t)~(uint64_t)0/255)) >> 56;
+ }
+
+ /**
* Check if a memory buffer is all-zero
*
* @param p Memory to scan
@@ -281,14 +402,13 @@ public:
}
// Byte swappers for big/little endian conversion
- static inline uint8_t hton(uint8_t n) throw() { return n; }
- static inline int8_t hton(int8_t n) throw() { return n; }
- static inline uint16_t hton(uint16_t n) throw() { return htons(n); }
- static inline int16_t hton(int16_t n) throw() { return (int16_t)htons((uint16_t)n); }
- static inline uint32_t hton(uint32_t n) throw() { return htonl(n); }
- static inline int32_t hton(int32_t n) throw() { return (int32_t)htonl((uint32_t)n); }
+ static inline uint8_t hton(uint8_t n) { return n; }
+ static inline int8_t hton(int8_t n) { return n; }
+ static inline uint16_t hton(uint16_t n) { return htons(n); }
+ static inline int16_t hton(int16_t n) { return (int16_t)htons((uint16_t)n); }
+ static inline uint32_t hton(uint32_t n) { return htonl(n); }
+ static inline int32_t hton(int32_t n) { return (int32_t)htonl((uint32_t)n); }
static inline uint64_t hton(uint64_t n)
- throw()
{
#if __BYTE_ORDER == __LITTLE_ENDIAN
#if defined(__GNUC__) && (!defined(__OpenBSD__))
@@ -309,16 +429,15 @@ public:
return n;
#endif
}
- static inline int64_t hton(int64_t n) throw() { return (int64_t)hton((uint64_t)n); }
-
- static inline uint8_t ntoh(uint8_t n) throw() { return n; }
- static inline int8_t ntoh(int8_t n) throw() { return n; }
- static inline uint16_t ntoh(uint16_t n) throw() { return ntohs(n); }
- static inline int16_t ntoh(int16_t n) throw() { return (int16_t)ntohs((uint16_t)n); }
- static inline uint32_t ntoh(uint32_t n) throw() { return ntohl(n); }
- static inline int32_t ntoh(int32_t n) throw() { return (int32_t)ntohl((uint32_t)n); }
+ static inline int64_t hton(int64_t n) { return (int64_t)hton((uint64_t)n); }
+
+ static inline uint8_t ntoh(uint8_t n) { return n; }
+ static inline int8_t ntoh(int8_t n) { return n; }
+ static inline uint16_t ntoh(uint16_t n) { return ntohs(n); }
+ static inline int16_t ntoh(int16_t n) { return (int16_t)ntohs((uint16_t)n); }
+ static inline uint32_t ntoh(uint32_t n) { return ntohl(n); }
+ static inline int32_t ntoh(int32_t n) { return (int32_t)ntohl((uint32_t)n); }
static inline uint64_t ntoh(uint64_t n)
- throw()
{
#if __BYTE_ORDER == __LITTLE_ENDIAN
#if defined(__GNUC__) && !defined(__OpenBSD__)
@@ -339,34 +458,7 @@ public:
return n;
#endif
}
- static inline int64_t ntoh(int64_t n) throw() { return (int64_t)ntoh((uint64_t)n); }
-
- /**
- * Compare Peer version tuples
- *
- * @return -1, 0, or 1 based on whether first tuple is less than, equal to, or greater than second
- */
- static inline int compareVersion(unsigned int maj1,unsigned int min1,unsigned int rev1,unsigned int maj2,unsigned int min2,unsigned int rev2)
- throw()
- {
- if (maj1 > maj2)
- return 1;
- else if (maj1 < maj2)
- return -1;
- else {
- if (min1 > min2)
- return 1;
- else if (min1 < min2)
- return -1;
- else {
- if (rev1 > rev2)
- return 1;
- else if (rev1 < rev2)
- return -1;
- else return 0;
- }
- }
- }
+ static inline int64_t ntoh(int64_t n) { return (int64_t)ntoh((uint64_t)n); }
/**
* Hexadecimal characters 0-f
diff --git a/node/World.hpp b/node/World.hpp
index fdada2ad..9edf2a6a 100644
--- a/node/World.hpp
+++ b/node/World.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_WORLD_HPP
@@ -49,16 +57,6 @@
#define ZT_WORLD_MAX_SERIALIZED_LENGTH (((1024 + (32 * ZT_WORLD_MAX_STABLE_ENDPOINTS_PER_ROOT)) * ZT_WORLD_MAX_ROOTS) + ZT_C25519_PUBLIC_KEY_LEN + ZT_C25519_SIGNATURE_LEN + 128)
/**
- * World ID indicating null / empty World object
- */
-#define ZT_WORLD_ID_NULL 0
-
-/**
- * World ID for a test network with ephemeral or temporary roots
- */
-#define ZT_WORLD_ID_TESTNET 1
-
-/**
* World ID for Earth
*
* This is the ID for the ZeroTier World used on planet Earth. It is unrelated
@@ -90,68 +88,85 @@ namespace ZeroTier {
* orbits, the Moon (about 1.3 light seconds), and nearby Lagrange points. A
* world ID for Mars and nearby space is defined but not yet used, and a test
* world ID is provided for testing purposes.
- *
- * If you absolutely must run your own "unofficial" ZeroTier network, please
- * define your world IDs above 0xffffffff (4294967295). Code to make a World
- * is in mkworld.cpp in the parent directory and must be edited to change
- * settings.
*/
class World
{
public:
+ /**
+ * World type -- do not change IDs
+ */
+ enum Type
+ {
+ TYPE_NULL = 0,
+ TYPE_PLANET = 1, // Planets, of which there is currently one (Earth)
+ TYPE_MOON = 127 // Moons, which are user-created and many
+ };
+
+ /**
+ * Upstream server definition in world/moon
+ */
struct Root
{
Identity identity;
std::vector<InetAddress> stableEndpoints;
- inline bool operator==(const Root &r) const throw() { return ((identity == r.identity)&&(stableEndpoints == r.stableEndpoints)); }
- inline bool operator!=(const Root &r) const throw() { return (!(*this == r)); }
- inline bool operator<(const Root &r) const throw() { return (identity < r.identity); } // for sorting
+ inline bool operator==(const Root &r) const { return ((identity == r.identity)&&(stableEndpoints == r.stableEndpoints)); }
+ inline bool operator!=(const Root &r) const { return (!(*this == r)); }
+ inline bool operator<(const Root &r) const { return (identity < r.identity); } // for sorting
};
/**
* Construct an empty / null World
*/
World() :
- _id(ZT_WORLD_ID_NULL),
- _ts(0) {}
+ _id(0),
+ _ts(0),
+ _type(TYPE_NULL) {}
/**
* @return Root servers for this world and their stable endpoints
*/
- inline const std::vector<World::Root> &roots() const throw() { return _roots; }
+ inline const std::vector<World::Root> &roots() const { return _roots; }
+
+ /**
+ * @return World type: planet or moon
+ */
+ inline Type type() const { return _type; }
/**
* @return World unique identifier
*/
- inline uint64_t id() const throw() { return _id; }
+ inline uint64_t id() const { return _id; }
/**
* @return World definition timestamp
*/
- inline uint64_t timestamp() const throw() { return _ts; }
+ inline uint64_t timestamp() const { return _ts; }
+
+ /**
+ * @return C25519 signature
+ */
+ inline const C25519::Signature &signature() const { return _signature; }
+
+ /**
+ * @return Public key that must sign next update
+ */
+ inline const C25519::Public &updatesMustBeSignedBy() const { return _updatesMustBeSignedBy; }
/**
* Check whether a world update should replace this one
*
- * A new world update is valid if it is for the same world ID, is newer,
- * and is signed by the current world's signing key. If this world object
- * is null, it can always be updated.
- *
* @param update Candidate update
- * @param fullSignatureCheck Perform full cryptographic signature check (true == yes, false == skip)
- * @return True if update is newer than current and is properly signed
+ * @return True if update is newer than current, matches its ID and type, and is properly signed (or if current is NULL)
*/
- inline bool shouldBeReplacedBy(const World &update,bool fullSignatureCheck)
+ inline bool shouldBeReplacedBy(const World &update)
{
- if (_id == ZT_WORLD_ID_NULL)
+ if ((_id == 0)||(_type == TYPE_NULL))
return true;
- if ((_id == update._id)&&(_ts < update._ts)) {
- if (fullSignatureCheck) {
- Buffer<ZT_WORLD_MAX_SERIALIZED_LENGTH> tmp;
- update.serialize(tmp,true);
- return C25519::verify(_updateSigningKey,tmp.data(),tmp.size(),update._signature);
- } else return true;
+ if ((_id == update._id)&&(_ts < update._ts)&&(_type == update._type)) {
+ Buffer<ZT_WORLD_MAX_SERIALIZED_LENGTH> tmp;
+ update.serialize(tmp,true);
+ return C25519::verify(_updatesMustBeSignedBy,tmp.data(),tmp.size(),update._signature);
}
return false;
}
@@ -159,17 +174,17 @@ public:
/**
* @return True if this World is non-empty
*/
- inline operator bool() const throw() { return (_id != ZT_WORLD_ID_NULL); }
+ inline operator bool() const { return (_type != TYPE_NULL); }
template<unsigned int C>
inline void serialize(Buffer<C> &b,bool forSign = false) const
{
- if (forSign)
- b.append((uint64_t)0x7f7f7f7f7f7f7f7fULL);
- b.append((uint8_t)0x01); // version -- only one valid value for now
+ if (forSign) b.append((uint64_t)0x7f7f7f7f7f7f7f7fULL);
+
+ b.append((uint8_t)_type);
b.append((uint64_t)_id);
b.append((uint64_t)_ts);
- b.append(_updateSigningKey.data,ZT_C25519_PUBLIC_KEY_LEN);
+ b.append(_updatesMustBeSignedBy.data,ZT_C25519_PUBLIC_KEY_LEN);
if (!forSign)
b.append(_signature.data,ZT_C25519_SIGNATURE_LEN);
b.append((uint8_t)_roots.size());
@@ -179,8 +194,10 @@ public:
for(std::vector<InetAddress>::const_iterator ep(r->stableEndpoints.begin());ep!=r->stableEndpoints.end();++ep)
ep->serialize(b);
}
- if (forSign)
- b.append((uint64_t)0xf7f7f7f7f7f7f7f7ULL);
+ if (_type == TYPE_MOON)
+ b.append((uint16_t)0); // no attached dictionary (for future use)
+
+ if (forSign) b.append((uint64_t)0xf7f7f7f7f7f7f7f7ULL);
}
template<unsigned int C>
@@ -190,39 +207,74 @@ public:
_roots.clear();
- if (b[p++] != 0x01)
- throw std::invalid_argument("invalid World serialized version");
+ switch((Type)b[p++]) {
+ case TYPE_NULL: _type = TYPE_NULL; break; // shouldn't ever really happen in serialized data but it's not invalid
+ case TYPE_PLANET: _type = TYPE_PLANET; break;
+ case TYPE_MOON: _type = TYPE_MOON; break;
+ default:
+ throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_INVALID_TYPE;
+ }
_id = b.template at<uint64_t>(p); p += 8;
_ts = b.template at<uint64_t>(p); p += 8;
- memcpy(_updateSigningKey.data,b.field(p,ZT_C25519_PUBLIC_KEY_LEN),ZT_C25519_PUBLIC_KEY_LEN); p += ZT_C25519_PUBLIC_KEY_LEN;
- memcpy(_signature.data,b.field(p,ZT_C25519_SIGNATURE_LEN),ZT_C25519_SIGNATURE_LEN); p += ZT_C25519_SIGNATURE_LEN;
- unsigned int numRoots = b[p++];
+ ZT_FAST_MEMCPY(_updatesMustBeSignedBy.data,b.field(p,ZT_C25519_PUBLIC_KEY_LEN),ZT_C25519_PUBLIC_KEY_LEN); p += ZT_C25519_PUBLIC_KEY_LEN;
+ ZT_FAST_MEMCPY(_signature.data,b.field(p,ZT_C25519_SIGNATURE_LEN),ZT_C25519_SIGNATURE_LEN); p += ZT_C25519_SIGNATURE_LEN;
+ const unsigned int numRoots = (unsigned int)b[p++];
if (numRoots > ZT_WORLD_MAX_ROOTS)
- throw std::invalid_argument("too many roots in World");
+ throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_OVERFLOW;
for(unsigned int k=0;k<numRoots;++k) {
_roots.push_back(Root());
Root &r = _roots.back();
p += r.identity.deserialize(b,p);
unsigned int numStableEndpoints = b[p++];
if (numStableEndpoints > ZT_WORLD_MAX_STABLE_ENDPOINTS_PER_ROOT)
- throw std::invalid_argument("too many stable endpoints in World/Root");
+ throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_OVERFLOW;
for(unsigned int kk=0;kk<numStableEndpoints;++kk) {
r.stableEndpoints.push_back(InetAddress());
p += r.stableEndpoints.back().deserialize(b,p);
}
}
+ if (_type == TYPE_MOON)
+ p += b.template at<uint16_t>(p) + 2;
return (p - startAt);
}
- inline bool operator==(const World &w) const throw() { return ((_id == w._id)&&(_ts == w._ts)&&(_updateSigningKey == w._updateSigningKey)&&(_signature == w._signature)&&(_roots == w._roots)); }
- inline bool operator!=(const World &w) const throw() { return (!(*this == w)); }
+ inline bool operator==(const World &w) const { return ((_id == w._id)&&(_ts == w._ts)&&(memcmp(_updatesMustBeSignedBy.data,w._updatesMustBeSignedBy.data,ZT_C25519_PUBLIC_KEY_LEN) == 0)&&(memcmp(_signature.data,w._signature.data,ZT_C25519_SIGNATURE_LEN) == 0)&&(_roots == w._roots)&&(_type == w._type)); }
+ inline bool operator!=(const World &w) const { return (!(*this == w)); }
+
+ /**
+ * Create a World object signed with a key pair
+ *
+ * @param t World type
+ * @param id World ID
+ * @param ts World timestamp / revision
+ * @param sk Key that must be used to sign the next future update to this world
+ * @param roots Roots and their stable endpoints
+ * @param signWith Key to sign this World with (can have the same public as the next-update signing key, but doesn't have to)
+ * @return Signed World object
+ */
+ static inline World make(World::Type t,uint64_t id,uint64_t ts,const C25519::Public &sk,const std::vector<World::Root> &roots,const C25519::Pair &signWith)
+ {
+ World w;
+ w._id = id;
+ w._ts = ts;
+ w._type = t;
+ w._updatesMustBeSignedBy = sk;
+ w._roots = roots;
+
+ Buffer<ZT_WORLD_MAX_SERIALIZED_LENGTH> tmp;
+ w.serialize(tmp,true);
+ w._signature = C25519::sign(signWith,tmp.data(),tmp.size());
+
+ return w;
+ }
protected:
uint64_t _id;
uint64_t _ts;
- C25519::Public _updateSigningKey;
+ Type _type;
+ C25519::Public _updatesMustBeSignedBy;
C25519::Signature _signature;
std::vector<Root> _roots;
};