Merge branch 'dev' into netbsd-support
This commit is contained in:
@@ -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
|
||||
inline char *toString(char buf[11]) 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
|
||||
{
|
||||
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 unsigned char operator[](unsigned int i) const { 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 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 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 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;
|
||||
|
||||
107
node/Array.hpp
107
node/Array.hpp
@@ -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
|
||||
@@ -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
|
||||
};
|
||||
|
||||
|
||||
@@ -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
|
||||
209
node/Buffer.hpp
209
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,14 +250,26 @@ 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 secure random bytes
|
||||
*
|
||||
* @param n Number of random bytes to append
|
||||
*/
|
||||
inline void appendRandom(unsigned int n)
|
||||
{
|
||||
if (unlikely((_l + n) > C))
|
||||
throw ZT_EXCEPTION_OUT_OF_BOUNDS;
|
||||
Utils::getSecureRandom(_b + _l,n);
|
||||
_l += n;
|
||||
}
|
||||
|
||||
/**
|
||||
* Append a C-array of bytes
|
||||
*
|
||||
@@ -280,26 +278,13 @@ public:
|
||||
* @throws std::out_of_range Attempt to append beyond capacity
|
||||
*/
|
||||
inline void append(const void *b,unsigned int l)
|
||||
throw(std::out_of_range)
|
||||
{
|
||||
if ((_l + l) > C)
|
||||
throw std::out_of_range("Buffer: append beyond capacity");
|
||||
memcpy(_b + _l,b,l);
|
||||
if (unlikely((_l + l) > C))
|
||||
throw ZT_EXCEPTION_OUT_OF_BOUNDS;
|
||||
ZT_FAST_MEMCPY(_b + _l,b,l);
|
||||
_l += l;
|
||||
}
|
||||
|
||||
/**
|
||||
* Append a string
|
||||
*
|
||||
* @param s String to append
|
||||
* @throws std::out_of_range Attempt to append beyond capacity
|
||||
*/
|
||||
inline void append(const std::string &s)
|
||||
throw(std::out_of_range)
|
||||
{
|
||||
append(s.data(),(unsigned int)s.length());
|
||||
}
|
||||
|
||||
/**
|
||||
* Append a C string including null termination byte
|
||||
*
|
||||
@@ -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
|
||||
|
||||
2522
node/C25519.cpp
2522
node/C25519.cpp
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
|
||||
74
node/Capability.cpp
Normal file
74
node/Capability.cpp
Normal file
@@ -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
|
||||
493
node/Capability.hpp
Normal file
493
node/Capability.hpp
Normal file
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
* Maximum number of qualifiers allowed in a COM (absolute max: 65535)
|
||||
*/
|
||||
#define ZT_NETWORK_COM_DEFAULT_REVISION_MAX_DELTA (ZT_NETWORK_AUTOCONF_DELAY * 5)
|
||||
|
||||
/**
|
||||
* Maximum number of qualifiers in a COM
|
||||
*/
|
||||
#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;
|
||||
|
||||
72
node/CertificateOfOwnership.cpp
Normal file
72
node/CertificateOfOwnership.cpp
Normal file
@@ -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
|
||||
247
node/CertificateOfOwnership.hpp
Normal file
247
node/CertificateOfOwnership.hpp
Normal file
@@ -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
|
||||
913
node/Cluster.cpp
913
node/Cluster.cpp
@@ -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
|
||||
406
node/Cluster.hpp
406
node/Cluster.hpp
@@ -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
|
||||
@@ -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
|
||||
*/
|
||||
@@ -143,27 +175,15 @@
|
||||
*/
|
||||
#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
|
||||
@@ -173,50 +193,40 @@
|
||||
*/
|
||||
#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
|
||||
|
||||
/**
|
||||
* Maximum identity WHOIS retries (each attempt tries consulting a different peer)
|
||||
*/
|
||||
#define ZT_MAX_WHOIS_RETRIES 3
|
||||
#define ZT_WHOIS_RETRY_DELAY 500
|
||||
|
||||
/**
|
||||
* Transmit queue entry timeout
|
||||
*/
|
||||
#define ZT_TRANSMIT_QUEUE_TIMEOUT (ZT_WHOIS_RETRY_DELAY * (ZT_MAX_WHOIS_RETRIES + 1))
|
||||
#define ZT_TRANSMIT_QUEUE_TIMEOUT 5000
|
||||
|
||||
/**
|
||||
* Receive queue entry timeout
|
||||
*/
|
||||
#define ZT_RECEIVE_QUEUE_TIMEOUT (ZT_WHOIS_RETRY_DELAY * (ZT_MAX_WHOIS_RETRIES + 1))
|
||||
#define ZT_RECEIVE_QUEUE_TIMEOUT 5000
|
||||
|
||||
/**
|
||||
* Maximum latency to allow for OK(HELLO) before packet is discarded
|
||||
*/
|
||||
#define ZT_HELLO_MAX_ALLOWABLE_LATENCY 120000
|
||||
|
||||
/**
|
||||
* Maximum number of ZT hops allowed (this is not IP hops/TTL)
|
||||
@@ -230,11 +240,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
|
||||
*
|
||||
@@ -243,30 +263,34 @@
|
||||
#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
|
||||
|
||||
/**
|
||||
* Paths are considered expired if they have not sent us a real packet in this long
|
||||
*/
|
||||
#define ZT_PEER_PATH_EXPIRATION ((ZT_PEER_PING_PERIOD * 4) + 3000)
|
||||
|
||||
/**
|
||||
* 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)
|
||||
@@ -274,19 +298,14 @@
|
||||
#define ZT_PEER_ACTIVITY_TIMEOUT 500000
|
||||
|
||||
/**
|
||||
* Timeout for path activity
|
||||
* General rate limit timeout for multiple packet types (HELLO, etc.)
|
||||
*/
|
||||
#define ZT_PATH_ACTIVITY_TIMEOUT ZT_PEER_ACTIVITY_TIMEOUT
|
||||
#define ZT_PEER_GENERAL_INBOUND_RATE_LIMIT 500
|
||||
|
||||
/**
|
||||
* No answer timeout to trigger dead path detection
|
||||
* General limit for max RTT for requests over the network
|
||||
*/
|
||||
#define ZT_PEER_DEAD_PATH_DETECTION_NO_ANSWER_TIMEOUT 2000
|
||||
|
||||
/**
|
||||
* Probation threshold after which a path becomes dead
|
||||
*/
|
||||
#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,31 +362,63 @@
|
||||
* 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_PEER_CREDENTIALS_CUTOFF_TIME 60000
|
||||
|
||||
/**
|
||||
* 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
|
||||
*
|
||||
* 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_TRUST_EXPIRATION 600000
|
||||
|
||||
/**
|
||||
* Enable support for older network configurations from older (pre-1.1.6) controllers
|
||||
*/
|
||||
#define ZT_SUPPORT_OLD_STYLE_NETCONF 1
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* This is an impossible real network ID since 0xff is a reserved address
|
||||
* prefix.
|
||||
*/
|
||||
#define ZT_TEST_NETWORK_ID 0xffffffffffffffffULL
|
||||
|
||||
/**
|
||||
* 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
|
||||
|
||||
65
node/Credential.hpp
Normal file
65
node/Credential.hpp
Normal file
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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;
|
||||
@@ -265,6 +275,21 @@ public:
|
||||
return dfl;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -403,56 +440,14 @@ public:
|
||||
return (this->get(key,tmp,2) >= 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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];
|
||||
};
|
||||
|
||||
@@ -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) {
|
||||
@@ -250,6 +255,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)
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -205,15 +198,10 @@ public:
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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 operator bool() const { 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 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;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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,59 +64,40 @@ class IncomingPacket : public Packet
|
||||
public:
|
||||
IncomingPacket() :
|
||||
Packet(),
|
||||
_receiveTime(0),
|
||||
_localAddress(),
|
||||
_remoteAddress()
|
||||
_receiveTime(0)
|
||||
{
|
||||
}
|
||||
|
||||
IncomingPacket(const IncomingPacket &p)
|
||||
{
|
||||
// All fields including InetAddress are memcpy'able
|
||||
memcpy(this,&p,sizeof(IncomingPacket));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new packet-in-decode
|
||||
*
|
||||
* @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);
|
||||
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);
|
||||
|
||||
// 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);
|
||||
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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
@@ -334,6 +337,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
|
||||
*
|
||||
@@ -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);
|
||||
@@ -365,6 +375,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()
|
||||
*
|
||||
@@ -383,10 +412,49 @@ struct InetAddress : public sockaddr_storage
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
|
||||
118
node/MAC.hpp
118
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;
|
||||
|
||||
237
node/Membership.cpp
Normal file
237
node/Membership.cpp
Normal file
@@ -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
|
||||
285
node/Membership.hpp
Normal file
285
node/Membership.hpp
Normal file
@@ -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
|
||||
@@ -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
|
||||
@@ -96,47 +89,21 @@ public:
|
||||
return MulticastGroup();
|
||||
}
|
||||
|
||||
/**
|
||||
* @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;
|
||||
|
||||
@@ -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);
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
@@ -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
|
||||
|
||||
140
node/Mutex.hpp
140
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,68 @@
|
||||
*
|
||||
* 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()
|
||||
{
|
||||
pthread_mutex_destroy(&_mh);
|
||||
}
|
||||
|
||||
inline void lock()
|
||||
throw()
|
||||
{
|
||||
pthread_mutex_lock(&_mh);
|
||||
}
|
||||
|
||||
inline void unlock()
|
||||
throw()
|
||||
{
|
||||
pthread_mutex_unlock(&_mh);
|
||||
}
|
||||
|
||||
inline void lock() const
|
||||
throw()
|
||||
{
|
||||
(const_cast <Mutex *> (this))->lock();
|
||||
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 unlock() const
|
||||
throw()
|
||||
{
|
||||
(const_cast <Mutex *> (this))->unlock();
|
||||
++(const_cast<Mutex *>(this)->nowServing);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 +91,72 @@ public:
|
||||
};
|
||||
|
||||
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_init(&_mh,(const pthread_mutexattr_t *)0);
|
||||
}
|
||||
|
||||
~Mutex()
|
||||
{
|
||||
pthread_mutex_destroy(&_mh);
|
||||
}
|
||||
|
||||
inline void lock() const
|
||||
{
|
||||
pthread_mutex_lock(&((const_cast <Mutex *> (this))->_mh));
|
||||
}
|
||||
|
||||
inline void unlock() const
|
||||
{
|
||||
pthread_mutex_unlock(&((const_cast <Mutex *> (this))->_mh));
|
||||
}
|
||||
|
||||
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; }
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
|
||||
1508
node/Network.cpp
1508
node/Network.cpp
File diff suppressed because it is too large
Load Diff
362
node/Network.hpp
362
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:
|
||||
/**
|
||||
@@ -63,6 +72,11 @@ 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
|
||||
*
|
||||
@@ -70,50 +84,91 @@ public:
|
||||
* 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; }
|
||||
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; }
|
||||
|
||||
/**
|
||||
* @return Address of network's controller (most significant 40 bits of ID)
|
||||
* 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
|
||||
*/
|
||||
inline Address controller() const throw() { return Address(_id >> 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);
|
||||
|
||||
/**
|
||||
* @param nwid Network ID
|
||||
* @return Address of network's controller
|
||||
* 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
|
||||
*/
|
||||
static inline Address controllerFor(uint64_t nwid) throw() { return Address(nwid >> 24); }
|
||||
|
||||
/**
|
||||
* @return Multicast group memberships for this network's port (local, not learned via bridging)
|
||||
*/
|
||||
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
|
||||
* 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 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 tryAnnounceMulticastGroupsTo(const SharedPtr<Peer> &peer);
|
||||
uint64_t handleConfigChunk(void *tPtr,const uint64_t packetId,const Address &source,const Buffer<ZT_PROTO_MAX_PACKET_LENGTH> &chunk,unsigned int ptr);
|
||||
|
||||
/**
|
||||
* Apply a NetworkConfig to this network
|
||||
*
|
||||
* @param conf Configuration in NetworkConfig form
|
||||
* @return True if configuration was accepted
|
||||
*/
|
||||
bool applyConfiguration(const NetworkConfig &conf);
|
||||
|
||||
/**
|
||||
* 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,77 +234,48 @@ 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
|
||||
*/
|
||||
bool recentlyAssociatedWith(const Address &addr);
|
||||
|
||||
/**
|
||||
* Do periodic cleanup and housekeeping tasks
|
||||
*/
|
||||
void clean();
|
||||
|
||||
/**
|
||||
* @return Time of last updated configuration or 0 if none
|
||||
*/
|
||||
inline uint64_t lastConfigUpdate() const throw() { return _lastConfigUpdate; }
|
||||
|
||||
/**
|
||||
* @return Status of this network
|
||||
*/
|
||||
inline ZT_VirtualNetworkStatus status() const
|
||||
{
|
||||
Mutex::Lock _l(_lock);
|
||||
return _status();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ec Buffer to fill with externally-visible network configuration
|
||||
*/
|
||||
inline void externalConfig(ZT_VirtualNetworkConfig *ec) const
|
||||
{
|
||||
Mutex::Lock _l(_lock);
|
||||
_externalConfig(ec);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current network config
|
||||
* Push state to members such as multicast group memberships and latest COM (if needed)
|
||||
*
|
||||
* 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().
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
inline const NetworkConfig &config() const { return _config; }
|
||||
|
||||
/**
|
||||
* @return A thread-safe copy of our NetworkConfig instead of a const reference
|
||||
*/
|
||||
inline NetworkConfig configCopy() const
|
||||
inline void sendUpdatesToMembers(void *tPtr)
|
||||
{
|
||||
Mutex::Lock _l(_lock);
|
||||
return _config;
|
||||
_sendUpdatesToMembers(tPtr,(const MulticastGroup *)0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return True if this network has a valid config
|
||||
*/
|
||||
inline bool hasConfig() const { return (_config); }
|
||||
|
||||
/**
|
||||
* @return Ethernet MAC address for this network's local interface
|
||||
*/
|
||||
inline const MAC &mac() const throw() { return _mac; }
|
||||
|
||||
/**
|
||||
* Find the node on this network that has this MAC behind it (if any)
|
||||
*
|
||||
@@ -258,9 +286,7 @@ public:
|
||||
{
|
||||
Mutex::Lock _l(_lock);
|
||||
const Address *const br = _remoteBridgeRoutes.get(mac);
|
||||
if (br)
|
||||
return *br;
|
||||
return Address();
|
||||
return ((br) ? *br : Address());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -274,54 +300,128 @@ public:
|
||||
/**
|
||||
* Learn a multicast group that is bridged to our tap device
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
void learnBridgedMulticastGroup(const MulticastGroup &mg,uint64_t now);
|
||||
void learnBridgedMulticastGroup(void *tPtr,const MulticastGroup &mg,int64_t now);
|
||||
|
||||
/**
|
||||
* Validate a credential and learn it if it passes certificate and other checks
|
||||
*/
|
||||
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 _membership(cap.issuedTo()).addCredential(RR,tPtr,_config,cap);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate a credential and learn it if it passes certificate and other checks
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate a credential and learn it if it passes certificate and other checks
|
||||
*/
|
||||
Membership::AddCredentialResult addCredential(void *tPtr,const Address &sentFrom,const Revocation &rev);
|
||||
|
||||
/**
|
||||
* Validate a credential and learn it if it passes certificate and other checks
|
||||
*/
|
||||
inline Membership::AddCredentialResult addCredential(void *tPtr,const CertificateOfOwnership &coo)
|
||||
{
|
||||
if (coo.networkId() != _id)
|
||||
return Membership::ADD_REJECTED;
|
||||
Mutex::Lock _l(_lock);
|
||||
return _membership(coo.issuedTo()).addCredential(RR,tPtr,_config,coo);
|
||||
}
|
||||
|
||||
/**
|
||||
* Force push credentials (COM, etc.) to a peer now
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
Buffer<ZT_NETWORKCONFIG_DICT_CAPACITY> *tmp = new Buffer<ZT_NETWORKCONFIG_DICT_CAPACITY>();
|
||||
char tmp2[128];
|
||||
|
||||
d.clear();
|
||||
try {
|
||||
d.clear();
|
||||
|
||||
// Try to put the more human-readable fields first
|
||||
// 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_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;
|
||||
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;
|
||||
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(',');
|
||||
v4s.append(this->staticIps[i].toString());
|
||||
}
|
||||
}
|
||||
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 (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 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));
|
||||
}
|
||||
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 (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(',');
|
||||
char buf[64];
|
||||
v6s.append(this->staticIps[i].toString(buf));
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
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 (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());
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
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->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->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->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->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;
|
||||
if (this->com) {
|
||||
tmp->clear();
|
||||
this->com.serialize(*tmp);
|
||||
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_COM,*tmp)) return false;
|
||||
}
|
||||
}
|
||||
if (tmp.size()) {
|
||||
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_RULES,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->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->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->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->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->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);
|
||||
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_SPECIALISTS,*tmp)) {
|
||||
unsigned int p = 0;
|
||||
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_ROUTES,tmp)) {
|
||||
if (d.get(ZT_NETWORKCONFIG_DICT_KEY_ROUTES,*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;
|
||||
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_STATIC_IPS,tmp)) {
|
||||
if (d.get(ZT_NETWORKCONFIG_DICT_KEY_STATIC_IPS,*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())&&(staticIpCount < ZT_MAX_ZT_ASSIGNED_ADDRESSES)) {
|
||||
p += this->staticIps[this->staticIpCount++].deserialize(*tmp,p);
|
||||
}
|
||||
}
|
||||
|
||||
if (d.get(ZT_NETWORKCONFIG_DICT_KEY_PINNED,tmp)) {
|
||||
if (d.get(ZT_NETWORKCONFIG_DICT_KEY_RULES,*tmp)) {
|
||||
this->ruleCount = 0;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
if (d.get(ZT_NETWORKCONFIG_DICT_KEY_RULES,tmp)) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,15 +180,17 @@ 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,...]
|
||||
@@ -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)
|
||||
*/
|
||||
inline bool allowPassiveBridging() const throw() { return ((this->flags & ZT_NETWORKCONFIG_FLAG_ALLOW_PASSIVE_BRIDGING) != 0); }
|
||||
|
||||
/**
|
||||
* @return True if broadcast (ff:ff:ff:ff:ff:ff) address should work on this network
|
||||
*/
|
||||
inline bool enableBroadcast() const throw() { return ((this->flags & ZT_NETWORKCONFIG_FLAG_ENABLE_BROADCAST) != 0); }
|
||||
inline bool enableBroadcast() const { return ((this->flags & ZT_NETWORKCONFIG_FLAG_ENABLE_BROADCAST) != 0); }
|
||||
|
||||
/**
|
||||
* @return True if IPv6 NDP emulation should be allowed for certain "magic" IPv6 address patterns
|
||||
*/
|
||||
inline bool ndpEmulation() const throw() { return ((this->flags & ZT_NETWORKCONFIG_FLAG_ENABLE_IPV6_NDP_EMULATION) != 0); }
|
||||
inline bool ndpEmulation() const { return ((this->flags & ZT_NETWORKCONFIG_FLAG_ENABLE_IPV6_NDP_EMULATION) != 0); }
|
||||
|
||||
/**
|
||||
* @return True if frames should not be compressed
|
||||
*/
|
||||
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,51 +309,70 @@ 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;
|
||||
}
|
||||
}
|
||||
return InetAddress();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
{
|
||||
std::vector<Relay> r;
|
||||
std::vector<Address> r;
|
||||
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)
|
||||
r.push_back(Address(specialists[i]));
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
inline unsigned int multicastReplicators(Address mr[ZT_MAX_NETWORK_SPECIALISTS]) const
|
||||
{
|
||||
unsigned int c = 0;
|
||||
for(unsigned int i=0;i<specialistCount;++i) {
|
||||
if ((specialists[i] & ZT_NETWORKCONFIG_SPECIALIST_TYPE_MULTICAST_REPLICATOR) != 0)
|
||||
mr[c++] = specialists[i];
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
inline bool isMulticastReplicator(const Address &a) const
|
||||
{
|
||||
for(unsigned int i=0;i<specialistCount;++i) {
|
||||
if (((specialists[i] & ZT_NETWORKCONFIG_SPECIALIST_TYPE_MULTICAST_REPLICATOR) != 0)&&(a == specialists[i]))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
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])];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @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
|
||||
{
|
||||
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))
|
||||
return true;
|
||||
@@ -355,78 +380,10 @@ public:
|
||||
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
|
||||
{
|
||||
while (ptr < specialistCount) {
|
||||
if ((specialists[ptr] & ZT_NETWORKCONFIG_SPECIALIST_TYPE_NETWORK_PREFERRED_RELAY) != 0) {
|
||||
return Address(specialists[ptr++]);
|
||||
} else {
|
||||
++ptr;
|
||||
}
|
||||
}
|
||||
return Address();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param zt ZeroTier address
|
||||
* @return True if this address is a relay
|
||||
*/
|
||||
bool isRelay(const Address &zt) const
|
||||
{
|
||||
for(unsigned int i=0;i<specialistCount;++i) {
|
||||
if ((zt == specialists[i])&&((specialists[i] & ZT_NETWORKCONFIG_SPECIALIST_TYPE_NETWORK_PREFERRED_RELAY) != 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
|
||||
@@ -473,11 +453,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)
|
||||
*/
|
||||
@@ -498,16 +493,26 @@ public:
|
||||
*/
|
||||
unsigned int staticIpCount;
|
||||
|
||||
/**
|
||||
* Number of pinned devices (devices with physical address hints)
|
||||
*/
|
||||
unsigned int pinnedCount;
|
||||
|
||||
/**
|
||||
* Number of rule table entries
|
||||
*/
|
||||
unsigned int ruleCount;
|
||||
|
||||
/**
|
||||
* Number of capabilities
|
||||
*/
|
||||
unsigned int capabilityCount;
|
||||
|
||||
/**
|
||||
* Number of tags
|
||||
*/
|
||||
unsigned int tagCount;
|
||||
|
||||
/**
|
||||
* Number of certificates of ownership
|
||||
*/
|
||||
unsigned int certificateOfOwnershipCount;
|
||||
|
||||
/**
|
||||
* Specialist devices
|
||||
*
|
||||
@@ -527,21 +532,25 @@ 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).
|
||||
*/
|
||||
struct {
|
||||
Address zt;
|
||||
InetAddress phy;
|
||||
} pinned[ZT_MAX_NETWORK_PINNED];
|
||||
|
||||
/**
|
||||
* Rules table
|
||||
* Base network rules
|
||||
*/
|
||||
ZT_VirtualNetworkRule rules[ZT_MAX_NETWORK_RULES];
|
||||
|
||||
/**
|
||||
* Capabilities for this node on this network, in ascending order of capability ID
|
||||
*/
|
||||
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)
|
||||
*/
|
||||
|
||||
@@ -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:
|
||||
/**
|
||||
* Return value of doNetworkConfigRequest
|
||||
*/
|
||||
enum ResultCode
|
||||
enum ErrorCode
|
||||
{
|
||||
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
|
||||
NC_ERROR_NONE = 0,
|
||||
NC_ERROR_OBJECT_NOT_FOUND = 1,
|
||||
NC_ERROR_ACCESS_DENIED = 2,
|
||||
NC_ERROR_INTERNAL_SERVER_ERROR = 3
|
||||
};
|
||||
|
||||
/**
|
||||
* Interface for sender used to send pushes and replies
|
||||
*/
|
||||
class Sender
|
||||
{
|
||||
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
|
||||
* Called when this is added to a Node to initialize and supply info
|
||||
*
|
||||
* This call is permitted to block, and may be called concurrently from more
|
||||
* than one thread. Implementations must use locks if needed.
|
||||
* @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
|
||||
*
|
||||
* On internal server errors, the 'error' field in result can be filled in
|
||||
* to indicate the error.
|
||||
*
|
||||
* @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(
|
||||
const InetAddress &fromAddr,
|
||||
const Identity &signingId,
|
||||
const Identity &identity,
|
||||
virtual void request(
|
||||
uint64_t nwid,
|
||||
const Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> &metaData,
|
||||
NetworkConfig &nc) = 0;
|
||||
const InetAddress &fromAddr,
|
||||
uint64_t requestPacketId,
|
||||
const Identity &identity,
|
||||
const Dictionary<ZT_NETWORKCONFIG_METADATA_DICT_CAPACITY> &metaData) = 0;
|
||||
};
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
||||
994
node/Node.cpp
994
node/Node.cpp
File diff suppressed because it is too large
Load Diff
301
node/Node.hpp
301
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(void *uptr,void *tptr,const struct ZT_Node_Callbacks *callbacks,int64_t now);
|
||||
virtual ~Node();
|
||||
|
||||
~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(); }
|
||||
inline int64_t now() const { return _now; }
|
||||
|
||||
/**
|
||||
* @return Time as of last call to run()
|
||||
*/
|
||||
inline uint64_t now() const throw() { 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); }
|
||||
|
||||
/**
|
||||
* Post an event to the external user
|
||||
*
|
||||
* @param ev Event type
|
||||
* @param md Meta-data (default: NULL/none)
|
||||
*/
|
||||
inline void postEvent(ZT_Event ev,const void *md = (const void *)0) { _eventCallback(reinterpret_cast<ZT_Node *>(this),_uPtr,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); }
|
||||
|
||||
/**
|
||||
* Update virtual network port configuration
|
||||
*
|
||||
* @param nwid Network ID
|
||||
* @param nuptr Network user ptr
|
||||
* @param op Configuration operation
|
||||
* @param nc Network configuration
|
||||
*/
|
||||
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 { return _online; }
|
||||
|
||||
inline bool online() const throw() { 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); }
|
||||
|
||||
#ifdef ZT_TRACE
|
||||
void postTrace(const char *module,unsigned int line,const char *fmt,...);
|
||||
#endif
|
||||
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();
|
||||
void postCircuitTestReport(const ZT_CircuitTestReport *report);
|
||||
void setTrustedPaths(const struct sockaddr_storage *networks,const uint64_t *ids,unsigned int count);
|
||||
ZT_ResultCode setPhysicalPathConfiguration(const struct sockaddr_storage *pathNetwork,const ZT_PhysicalPathConfiguration *pathConfig);
|
||||
|
||||
private:
|
||||
inline SharedPtr<Network> _network(uint64_t nwid) const
|
||||
World planet() const;
|
||||
std::vector<World> moons() const;
|
||||
|
||||
inline const Identity &identity() const { return _RR.identity; }
|
||||
|
||||
/**
|
||||
* Register that we are expecting a reply to a packet ID
|
||||
*
|
||||
* 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 expectReplyTo(const uint64_t packetId)
|
||||
{
|
||||
// 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;
|
||||
}
|
||||
return SharedPtr<Network>();
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 packetId Packet ID to check
|
||||
* @return True if we're expecting a reply
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
unsigned long iph = from.rateGateHash();
|
||||
if ((now - _lastIdentityVerification[iph]) >= ZT_IDENTITY_VALIDATION_SOURCE_RATE_LIMIT) {
|
||||
_lastIdentityVerification[iph] = now;
|
||||
return true;
|
||||
}
|
||||
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;
|
||||
// Time of last identity verification indexed by InetAddress.rateGateHash() -- used in IncomingPacket::_doHELLO() via rateGateIdentityVerification()
|
||||
int64_t _lastIdentityVerification[16384];
|
||||
|
||||
Hashtable< uint64_t,SharedPtr<Network> > _networks;
|
||||
Mutex _networks_m;
|
||||
|
||||
std::vector< ZT_CircuitTest * > _circuitTests;
|
||||
Mutex _circuitTests_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;
|
||||
};
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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);
|
||||
*/
|
||||
_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();
|
||||
|
||||
_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;
|
||||
|
||||
_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
|
||||
|
||||
@@ -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
|
||||
|
||||
1050
node/Packet.cpp
1050
node/Packet.cpp
File diff suppressed because it is too large
Load Diff
559
node/Packet.hpp
559
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>
|
||||
* <[...] 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 ...]
|
||||
*
|
||||
* 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.
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* 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>
|
||||
* Destination address is the actual wire address to which the packet
|
||||
* was sent. See InetAddress::serialize() for format.
|
||||
*
|
||||
* 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]
|
||||
* <[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]
|
||||
*
|
||||
* With the exception of the timestamp, the other fields pertain to the
|
||||
* respondent who is sending OK and are not echoes.
|
||||
*
|
||||
* 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
|
||||
* 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)
|
||||
*
|
||||
* 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.
|
||||
* 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)
|
||||
*
|
||||
* Multicast frames may not be sent as EXT_FRAME.
|
||||
* 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.
|
||||
*
|
||||
* ERROR may be generated if a membership certificate is needed for a
|
||||
* closed network. Payload will be network ID.
|
||||
* 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 ...]
|
||||
* 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 is sent in response to ERROR_NEED_MEMBERSHIP_CERTIFICATE and may
|
||||
* be pushed at any other time to keep exchanged certificates up to date.
|
||||
* 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.
|
||||
*
|
||||
* Respones to this are always whole configs intended for the recipient.
|
||||
* For patches and other updates a NETWORK_CONFIG is sent instead.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* OK response payload:
|
||||
* <[8] 64-bit network ID>
|
||||
* <[2] 16-bit length of network configuration dictionary>
|
||||
* <[...] network configuration dictionary>
|
||||
* <[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>
|
||||
*
|
||||
* 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.
|
||||
* The chunk signature signs the entire payload of the OK response.
|
||||
* Currently only one signature type is supported: ed25519 (1).
|
||||
*
|
||||
* 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.
|
||||
* 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 can be sent by the network controller to inform a node that it
|
||||
* should now make a NETWORK_CONFIG_REQUEST.
|
||||
* 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.
|
||||
*
|
||||
* It does not generate an OK or ERROR message, and is treated only as
|
||||
* a hint to refresh now.
|
||||
* The legacy mode missing the additional chunking fields is not supported
|
||||
* here.
|
||||
*
|
||||
* Flags:
|
||||
* 0x01 - Use fast propagation
|
||||
*
|
||||
* An OK should be sent if the config is successfully received and
|
||||
* accepted.
|
||||
*
|
||||
* 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,
|
||||
|
||||
// 0x11, 0x12 -- deprecated
|
||||
|
||||
/**
|
||||
* 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>
|
||||
* A message with arbitrary user-definable content:
|
||||
* <[8] 64-bit arbitrary message type ID>
|
||||
* [<[...] message payload>]
|
||||
*
|
||||
* Flags:
|
||||
* 0x01 - Report back to originator at middle hops
|
||||
* 0x02 - Report back to originator at last hop
|
||||
* 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.
|
||||
*
|
||||
* 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)>
|
||||
* 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 = 17,
|
||||
VERB_USER_MESSAGE = 0x14,
|
||||
|
||||
/**
|
||||
* 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>
|
||||
* A trace for remote debugging or diagnostics:
|
||||
* <[...] null-terminated dictionary containing trace information>
|
||||
* [<[...] additional null-terminated dictionaries>]
|
||||
*
|
||||
* Next hop information record format:
|
||||
* <[5] ZeroTier address of next hop>
|
||||
* <[...] current best direct path address, if any, 0 if none>
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* If a test report is received and no circuit test was sent, it should be
|
||||
* ignored. This message generates no OK or ERROR response.
|
||||
* 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_CIRCUIT_TEST_REPORT = 18,
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
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); }
|
||||
@@ -1317,6 +1247,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)
|
||||
*
|
||||
|
||||
@@ -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;
|
||||
|
||||
342
node/Path.hpp
342
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,209 +50,141 @@ 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);
|
||||
bool send(const RuntimeEnvironment *RR,void *tPtr,const void *data,unsigned int len,int64_t now);
|
||||
|
||||
/**
|
||||
* @return Address of local side of this path or NULL if unspecified
|
||||
* Manually update last sent time
|
||||
*
|
||||
* @param t Time of send
|
||||
*/
|
||||
inline const InetAddress &localAddress() const throw() { return _localAddress; }
|
||||
inline void sent(const int64_t t) { _lastOut = t; }
|
||||
|
||||
/**
|
||||
* @return Time of last send to this path
|
||||
* Update path latency with a new measurement
|
||||
*
|
||||
* @param l Measured latency
|
||||
*/
|
||||
inline uint64_t lastSend() const throw() { return _lastSend; }
|
||||
inline void updateLatency(const unsigned int l)
|
||||
{
|
||||
unsigned int pl = _latency;
|
||||
if (pl < 0xffff)
|
||||
_latency = (pl + l) / 2;
|
||||
else _latency = l;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Time we last pinged or dead path checked this link
|
||||
* @return Local socket as specified by external code
|
||||
*/
|
||||
inline uint64_t lastPing() const throw() { return _lastPing; }
|
||||
|
||||
/**
|
||||
* @return Time of last keepalive
|
||||
*/
|
||||
inline uint64_t lastKeepalive() const throw() { return _lastKeepalive; }
|
||||
|
||||
/**
|
||||
* @return Time of last receive from this path
|
||||
*/
|
||||
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)
|
||||
inline bool trustEstablished(const int64_t now) const { return ((now - _lastTrustEstablishedPacketReceived) < ZT_TRUST_EXPIRATION); }
|
||||
|
||||
/**
|
||||
* @return Preference rank, higher == better
|
||||
*/
|
||||
inline unsigned int preferenceRank() const
|
||||
{
|
||||
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); }
|
||||
|
||||
/**
|
||||
* @return True if ZT_PATH_FLAG_CLUSTER_OPTIMAL is set
|
||||
*/
|
||||
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()
|
||||
{
|
||||
/* 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
|
||||
*
|
||||
@@ -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)); }
|
||||
|
||||
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 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; }
|
||||
|
||||
/**
|
||||
* @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
|
||||
|
||||
899
node/Peer.cpp
899
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,181 +32,354 @@
|
||||
#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);
|
||||
|
||||
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]);
|
||||
}
|
||||
// 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;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
#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 (attemptToContact) {
|
||||
attemptToContactAt(tPtr,path->localSocket(),path->address(),now,true);
|
||||
path->sent(now);
|
||||
RR->t->peerConfirmingUnknownPath(tPtr,networkId,*this,path,packetId,verb);
|
||||
}
|
||||
}
|
||||
|
||||
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 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
path->send(RR,tPtr,outp.data(),outp.size(),now);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Peer::sendHELLO(const InetAddress &localAddr,const InetAddress &atAddress,uint64_t now,unsigned int ttl)
|
||||
SharedPtr<Path> Peer::getBestPath(int64_t now,bool includeExpired) const
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
if (bestPath != ZT_MAX_PEER_NETWORK_PATHS)
|
||||
return _paths[bestPath].p;
|
||||
return SharedPtr<Path>();
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
Mutex::Lock _l1(_paths_m);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
Mutex::Lock _l2(other->_paths_m);
|
||||
|
||||
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:
|
||||
if (q <= theirBestV6QualityByScope[s]) {
|
||||
theirBestV6QualityByScope[s] = q;
|
||||
theirBestV6ByScope[s] = i;
|
||||
}
|
||||
break;
|
||||
}
|
||||
} else break;
|
||||
}
|
||||
|
||||
unsigned int mine = ZT_MAX_PEER_NETWORK_PATHS;
|
||||
unsigned int theirs = ZT_MAX_PEER_NETWORK_PATHS;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Peer::sendHELLO(void *tPtr,const int64_t localSocket,const InetAddress &atAddress,int64_t now)
|
||||
{
|
||||
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);
|
||||
@@ -206,353 +387,167 @@ void Peer::sendHELLO(const InetAddress &localAddr,const InetAddress &atAddress,u
|
||||
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());
|
||||
|
||||
outp.armor(_key,false); // HELLO is sent in the clear
|
||||
RR->node->putPacket(localAddr,atAddress,outp.data(),outp.size(),ttl);
|
||||
}
|
||||
outp.append((uint64_t)RR->topology->planetWorldId());
|
||||
outp.append((uint64_t)RR->topology->planetWorldTimestamp());
|
||||
|
||||
bool Peer::doPingAndKeepalive(uint64_t now,int inetAddressFamily)
|
||||
{
|
||||
Path *p = (Path *)0;
|
||||
const unsigned int startCryptedPortionAt = outp.size();
|
||||
|
||||
if (inetAddressFamily != 0) {
|
||||
p = _getBestPath(now,inetAddressFamily);
|
||||
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);
|
||||
}
|
||||
|
||||
outp.cryptField(_key,startCryptedPortionAt,outp.size() - startCryptedPortionAt);
|
||||
|
||||
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 {
|
||||
p = _getBestPath(now);
|
||||
RR->sw->send(tPtr,outp,false); // false == don't encrypt full payload, but add MAC
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Peer::pushDirectPaths(const InetAddress &localAddr,const InetAddress &toAddress,uint64_t now,bool force,bool includePrivatePaths)
|
||||
void Peer::attemptToContactAt(void *tPtr,const int64_t localSocket,const InetAddress &atAddress,int64_t now,bool sendFullHello)
|
||||
{
|
||||
#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 ( (!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);
|
||||
}
|
||||
}
|
||||
|
||||
if (!force) {
|
||||
if ((now - _lastDirectPathPushSent) < ZT_DIRECT_PATH_PUSH_INTERVAL)
|
||||
return false;
|
||||
else _lastDirectPathPushSent = now;
|
||||
void Peer::tryMemorizedPath(void *tPtr,int64_t now)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int Peer::doPingAndKeepalive(void *tPtr,int64_t now)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
std::vector<InetAddress> pathsToPush;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
#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());
|
||||
}
|
||||
#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
|
||||
|
||||
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;
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
while(j < ZT_MAX_PEER_NETWORK_PATHS) {
|
||||
_paths[j].lr = 0;
|
||||
_paths[j].p.zero();
|
||||
_paths[j].priority = 1;
|
||||
++j;
|
||||
}
|
||||
|
||||
return true;
|
||||
return sent;
|
||||
}
|
||||
|
||||
bool Peer::resetWithinScope(InetAddress::IpScope scope,uint64_t now)
|
||||
void Peer::clusterRedirect(void *tPtr,const SharedPtr<Path> &originatingPath,const InetAddress &remoteAddress,const int64_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];
|
||||
SharedPtr<Path> np(RR->topology->getPath(originatingPath->localSocket(),remoteAddress));
|
||||
RR->t->peerRedirected(tPtr,0,*this,np);
|
||||
|
||||
attemptToContactAt(tPtr,originatingPath->localSocket(),remoteAddress,now,true);
|
||||
|
||||
{
|
||||
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;
|
||||
}
|
||||
++x;
|
||||
}
|
||||
_numPaths = y;
|
||||
return (y < np);
|
||||
}
|
||||
newPriority += 2;
|
||||
|
||||
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();
|
||||
}
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Peer::networkMembershipCertificatesAgree(uint64_t nwid,const CertificateOfMembership &com) const
|
||||
{
|
||||
Mutex::Lock _l(_networkComs_m);
|
||||
const _NetworkCom *ourCom = _networkComs.get(nwid);
|
||||
if (ourCom)
|
||||
return ourCom->com.agreesWith(com);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Peer::validateAndSetNetworkMembershipCertificate(uint64_t nwid,const CertificateOfMembership &com)
|
||||
{
|
||||
// Sanity checks
|
||||
if ((!com)||(com.issuedTo() != _id.address()))
|
||||
return false;
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
if (com.signedBy() == RR->identity.address()) {
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
} 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
|
||||
}
|
||||
}
|
||||
|
||||
// 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));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Peer::needsOurNetworkMembershipCertificate(uint64_t nwid,uint64_t now,bool updateLastPushedTime)
|
||||
{
|
||||
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));
|
||||
}
|
||||
|
||||
void Peer::clean(uint64_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;
|
||||
}
|
||||
|
||||
{
|
||||
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);
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Peer::_doDeadPathDetection(Path &p,const uint64_t now)
|
||||
void Peer::resetWithinScope(void *tPtr,InetAddress::IpScope scope,int inetAddressFamily,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);
|
||||
}
|
||||
|
||||
p.increaseProbation();
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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]);
|
||||
}
|
||||
}
|
||||
if (bestPath)
|
||||
_doDeadPathDetection(*bestPath,now);
|
||||
return bestPath;
|
||||
}
|
||||
|
||||
Path *Peer::_getBestPath(const uint64_t now,int inetAddressFamily)
|
||||
{
|
||||
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]);
|
||||
}
|
||||
}
|
||||
if (bestPath)
|
||||
_doDeadPathDetection(*bestPath,now);
|
||||
return bestPath;
|
||||
}
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
||||
650
node/Peer.hpp
650
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>;
|
||||
|
||||
@@ -73,27 +77,15 @@ 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);
|
||||
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);
|
||||
|
||||
/**
|
||||
* Get the current best direct path to this peer
|
||||
* 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.
|
||||
*
|
||||
* @param addr Address to make exclusive
|
||||
*/
|
||||
inline void setClusterOptimalPathForAddressFamily(const InetAddress &addr)
|
||||
{
|
||||
for(unsigned int p=0;p<_numPaths;++p) {
|
||||
if (_paths[p].address().ss_family == addr.ss_family) {
|
||||
_paths[p].setClusterSuboptimal(_paths[p].address() != addr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send via best path
|
||||
* Send via best direct path
|
||||
*
|
||||
* @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 Path used on success or NULL on failure
|
||||
* @param force If true, send even if path is not alive
|
||||
* @return True if we actually sent something
|
||||
*/
|
||||
inline Path *send(const void *data,unsigned int len,uint64_t now)
|
||||
inline bool sendDirect(void *tPtr,const void *data,unsigned int len,int64_t now,bool force)
|
||||
{
|
||||
Path *const bestPath = getBestPath(now);
|
||||
if (bestPath) {
|
||||
if (bestPath->send(RR,data,len,now))
|
||||
return bestPath;
|
||||
}
|
||||
return (Path *)0;
|
||||
SharedPtr<Path> bp(getBestPath(now,force));
|
||||
if (bp)
|
||||
return bp->send(RR,tPtr,data,len,now);
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the best current direct path
|
||||
*
|
||||
* @param now Current time
|
||||
* @param includeExpired If true, include even expired paths
|
||||
* @return Best current path or NULL if none
|
||||
*/
|
||||
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 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 sendFullHello If true, always send a full HELLO instead of just an ECHO
|
||||
*/
|
||||
void attemptToContactAt(void *tPtr,const int64_t localSocket,const InetAddress &atAddress,int64_t now,bool sendFullHello);
|
||||
|
||||
/**
|
||||
* 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 tPtr Thread pointer to be handed through to any callbacks called as a result of this call
|
||||
* @param now Current time
|
||||
*/
|
||||
void tryMemorizedPath(void *tPtr,int64_t now);
|
||||
|
||||
/**
|
||||
* Send pings or keepalives depending on configured timeouts
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
bool doPingAndKeepalive(uint64_t now,int inetAddressFamily);
|
||||
|
||||
/**
|
||||
* Push direct paths back to self if we haven't done so in the configured timeout
|
||||
* This also cleans up some internal data structures. It's called periodically from Node.
|
||||
*
|
||||
* @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
|
||||
* @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)
|
||||
*/
|
||||
bool pushDirectPaths(const InetAddress &localAddr,const InetAddress &toAddress,uint64_t now,bool force,bool includePrivatePaths);
|
||||
unsigned int doPingAndKeepalive(void *tPtr,int64_t now);
|
||||
|
||||
/**
|
||||
* @return All known direct paths to this peer (active or inactive)
|
||||
* 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 std::vector<Path> paths() const
|
||||
void clusterRedirect(void *tPtr,const SharedPtr<Path> &originatingPath,const InetAddress &remoteAddress,const int64_t now);
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
void resetWithinScope(void *tPtr,InetAddress::IpScope scope,int inetAddressFamily,int64_t now);
|
||||
|
||||
/**
|
||||
* @param now Current time
|
||||
* @return All known paths to this peer
|
||||
*/
|
||||
inline std::vector< SharedPtr<Path> > paths(const int64_t now) const
|
||||
{
|
||||
std::vector<Path> pp;
|
||||
for(unsigned int p=0,np=_numPaths;p<np;++p)
|
||||
pp.push_back(_paths[p]);
|
||||
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 last receive of anything, whether direct or relayed
|
||||
*/
|
||||
inline uint64_t lastReceive() const throw() { return _lastReceive; }
|
||||
inline int64_t lastReceive() const { return _lastReceive; }
|
||||
|
||||
/**
|
||||
* @return Time of most recent unicast frame received
|
||||
* @return True if we've heard from this peer in less than ZT_PEER_ACTIVITY_TIMEOUT
|
||||
*/
|
||||
inline uint64_t lastUnicastFrame() const throw() { return _lastUnicastFrame; }
|
||||
|
||||
/**
|
||||
* @return Time of most recent multicast frame received
|
||||
*/
|
||||
inline uint64_t lastMulticastFrame() const throw() { return _lastMulticastFrame; }
|
||||
|
||||
/**
|
||||
* @return Time of most recent frame of any kind (unicast or multicast)
|
||||
*/
|
||||
inline uint64_t lastFrame() const throw() { return std::max(_lastUnicastFrame,_lastMulticastFrame); }
|
||||
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,69 +321,22 @@ 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;
|
||||
|
||||
/**
|
||||
* Check the validity of the COM and add/update if valid and new
|
||||
*
|
||||
* @param nwid Network ID
|
||||
* @param com Externally supplied COM
|
||||
*/
|
||||
bool validateAndSetNetworkMembershipCertificate(uint64_t nwid,const CertificateOfMembership &com);
|
||||
|
||||
/**
|
||||
* @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
|
||||
*/
|
||||
bool needsOurNetworkMembershipCertificate(uint64_t nwid,uint64_t now,bool updateLastPushedTime);
|
||||
|
||||
/**
|
||||
* Perform periodic cleaning operations
|
||||
*
|
||||
* @param now Current time
|
||||
*/
|
||||
void clean(uint64_t now);
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
inline bool shouldRespondToDirectPathPush(const uint64_t now)
|
||||
inline bool rateGatePushDirectPaths(const int64_t now)
|
||||
{
|
||||
if ((now - _lastDirectPathPushReceive) <= ZT_PUSH_DIRECT_PATHS_CUTOFF_TIME)
|
||||
++_directPathPushCutoffCount;
|
||||
@@ -415,187 +346,188 @@ public:
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 limit gate for VERB_NETWORK_CREDENTIALS
|
||||
*/
|
||||
static inline std::pair<InetAddress,InetAddress> findCommonGround(const Peer &a,const Peer &b,uint64_t now)
|
||||
inline bool rateGateCredentialsReceived(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 - _lastCredentialsReceived) <= ZT_PEER_CREDENTIALS_CUTOFF_TIME)
|
||||
++_credentialsCutoffCount;
|
||||
else _credentialsCutoffCount = 0;
|
||||
_lastCredentialsReceived = now;
|
||||
return (_directPathPushCutoffCount < ZT_PEER_CREDEITIALS_CUTOFF_LIMIT);
|
||||
}
|
||||
|
||||
template<unsigned int C>
|
||||
inline void serialize(Buffer<C> &b) const
|
||||
/**
|
||||
* Rate limit gate for sending of ERROR_NEED_MEMBERSHIP_CERTIFICATE
|
||||
*/
|
||||
inline bool rateGateRequestCredentials(const int64_t now)
|
||||
{
|
||||
Mutex::Lock _l(_networkComs_m);
|
||||
if ((now - _lastCredentialRequestSent) >= ZT_PEER_GENERAL_RATE_LIMIT) {
|
||||
_lastCredentialRequestSent = now;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
const unsigned int recSizePos = b.size();
|
||||
b.addSize(4); // space for uint32_t field length
|
||||
/**
|
||||
* Rate limit gate for inbound WHOIS requests
|
||||
*/
|
||||
inline bool rateGateInboundWhoisRequest(const int64_t now)
|
||||
{
|
||||
if ((now - _lastWhoisRequestReceived) >= ZT_PEER_WHOIS_RATE_LIMIT) {
|
||||
_lastWhoisRequestReceived = now;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
b.append((uint16_t)1); // version of serialized Peer data
|
||||
/**
|
||||
* Rate limit gate for inbound ECHO requests
|
||||
*/
|
||||
inline bool rateGateEchoRequest(const int64_t now)
|
||||
{
|
||||
if ((now - _lastEchoRequestReceived) >= ZT_PEER_GENERAL_RATE_LIMIT) {
|
||||
_lastEchoRequestReceived = now;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
_id.serialize(b,false);
|
||||
/**
|
||||
* Rate gate incoming requests for network COM
|
||||
*/
|
||||
inline bool rateGateIncomingComRequest(const int64_t now)
|
||||
{
|
||||
if ((now - _lastComRequestReceived) >= ZT_PEER_GENERAL_RATE_LIMIT) {
|
||||
_lastComRequestReceived = now;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rate gate outgoing requests for network COM
|
||||
*/
|
||||
inline bool rateGateOutgoingComRequest(const int64_t now)
|
||||
{
|
||||
if ((now - _lastComRequestSent) >= ZT_PEER_GENERAL_RATE_LIMIT) {
|
||||
_lastComRequestSent = now;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
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);
|
||||
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.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);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
try {
|
||||
unsigned int ptr = 0;
|
||||
if (b[ptr++] != 1)
|
||||
return SharedPtr<Peer>();
|
||||
|
||||
Identity npid;
|
||||
p += npid.deserialize(b,p);
|
||||
if (!npid)
|
||||
return SharedPtr<Peer>();
|
||||
Identity id;
|
||||
ptr += id.deserialize(b,ptr);
|
||||
if (!id)
|
||||
return SharedPtr<Peer>();
|
||||
|
||||
SharedPtr<Peer> np(new Peer(renv,myIdentity,npid));
|
||||
SharedPtr<Peer> p(new Peer(renv,renv->identity,id));
|
||||
|
||||
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;
|
||||
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;
|
||||
|
||||
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);
|
||||
// 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);
|
||||
return p;
|
||||
} catch ( ... ) {
|
||||
return SharedPtr<Peer>();
|
||||
}
|
||||
|
||||
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 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;
|
||||
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;
|
||||
_PeerPath _paths[ZT_MAX_PEER_NETWORK_PATHS];
|
||||
Mutex _paths_m;
|
||||
|
||||
Identity _id;
|
||||
|
||||
unsigned int _directPathPushCutoffCount;
|
||||
unsigned int _credentialsCutoffCount;
|
||||
|
||||
AtomicCounter __refCount;
|
||||
};
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
55
node/Revocation.cpp
Normal file
55
node/Revocation.cpp
Normal file
@@ -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
|
||||
197
node/Revocation.hpp
Normal file
197
node/Revocation.hpp
Normal file
@@ -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
|
||||
@@ -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
|
||||
|
||||
559
node/SHA512.cpp
559
node/SHA512.cpp
@@ -1,33 +1,3 @@
|
||||
/*
|
||||
* 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 <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "SHA512.hpp"
|
||||
#include "Utils.hpp"
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Code taken from NaCl by D. J. Bernstein and others
|
||||
// Public domain
|
||||
|
||||
@@ -37,34 +7,73 @@ D. J. Bernstein
|
||||
Public domain.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#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
|
||||
|
||||
#ifndef ZT_HAVE_NATIVE_SHA512
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
#define uint64 uint64_t
|
||||
|
||||
#ifdef ZT_NO_TYPE_PUNNING
|
||||
|
||||
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;
|
||||
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;
|
||||
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);
|
||||
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)
|
||||
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
|
||||
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)
|
||||
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
|
||||
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)
|
||||
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
|
||||
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)
|
||||
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
|
||||
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)
|
||||
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];
|
||||
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;
|
||||
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;
|
||||
}
|
||||
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]);
|
||||
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;
|
||||
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;
|
||||
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;
|
||||
const unsigned char *in = (const unsigned char *)data;
|
||||
unsigned int inlen = len;
|
||||
|
||||
for (i = 0;i < 64;++i) h[i] = iv[i];
|
||||
for (i = 0;i < 64;++i) h[i] = iv[i];
|
||||
|
||||
blocks(h,in,inlen);
|
||||
in += inlen;
|
||||
inlen &= 127;
|
||||
in -= inlen;
|
||||
blocks(h,in,inlen);
|
||||
in += inlen;
|
||||
inlen &= 127;
|
||||
in -= inlen;
|
||||
|
||||
for (i = 0;i < (int)inlen;++i) padded[i] = in[i];
|
||||
padded[inlen] = 0x80;
|
||||
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);
|
||||
}
|
||||
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];
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
128
node/Salsa20.hpp
128
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)
|
||||
* 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
|
||||
*/
|
||||
static inline void memxor(uint8_t *d,const uint8_t *s,unsigned int len)
|
||||
{
|
||||
#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++);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param key 256-bit (32 byte) key
|
||||
* @param iv 64-bit initialization vector
|
||||
*/
|
||||
Salsa20(const void *key,unsigned int kbits,const void *iv)
|
||||
throw()
|
||||
Salsa20(const void *key,const void *iv)
|
||||
{
|
||||
init(key,kbits,iv);
|
||||
init(key,iv);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize cipher
|
||||
*
|
||||
* @param key Key bits
|
||||
* @param kbits Number of key bits: 128 or 256 (recommended)
|
||||
* @param iv 64-bit initialization vector
|
||||
*/
|
||||
void init(const void *key,unsigned int kbits,const void *iv)
|
||||
throw();
|
||||
void init(const void *key,const void *iv);
|
||||
|
||||
/**
|
||||
* Encrypt data using Salsa20/12
|
||||
* Encrypt/decrypt 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();
|
||||
void crypt12(const void *in,void *out,unsigned int bytes);
|
||||
|
||||
/**
|
||||
* Encrypt data using Salsa20/20
|
||||
* Encrypt/decrypt data using Salsa20/20
|
||||
*
|
||||
* @param in Input data
|
||||
* @param out Output buffer
|
||||
* @param bytes Length of data
|
||||
*/
|
||||
void encrypt20(const void *in,void *out,unsigned int bytes)
|
||||
throw();
|
||||
|
||||
/**
|
||||
* Decrypt data
|
||||
*
|
||||
* @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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrypt data
|
||||
*
|
||||
* @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 {
|
||||
|
||||
@@ -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. */
|
||||
* 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. */
|
||||
|
||||
// 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;
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
// 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>();
|
||||
|
||||
{ // 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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
746
node/Switch.cpp
746
node/Switch.cpp
File diff suppressed because it is too large
Load Diff
145
node/Switch.hpp
145
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
|
||||
|
||||
55
node/Tag.cpp
Normal file
55
node/Tag.cpp
Normal file
@@ -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
|
||||
210
node/Tag.hpp
Normal file
210
node/Tag.hpp
Normal file
@@ -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
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
void Topology::saveIdentity(const Identity &id)
|
||||
SharedPtr<Peer> Topology::getUpstreamPeer()
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
const int64_t now = RR->node->now();
|
||||
unsigned int bestq = ~((unsigned int)0);
|
||||
const SharedPtr<Peer> *best = (const SharedPtr<Peer> *)0;
|
||||
|
||||
SharedPtr<Peer> Topology::getBestRoot(const Address *avoid,unsigned int avoidCount,bool strictAvoid)
|
||||
{
|
||||
const uint64_t now = RR->node->now();
|
||||
Mutex::Lock _l(_lock);
|
||||
Mutex::Lock _l2(_peers_m);
|
||||
Mutex::Lock _l1(_upstreams_m);
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
break;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
} 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;
|
||||
break;
|
||||
}
|
||||
}
|
||||
const unsigned int q = (*r)->relayQuality(now);
|
||||
if (q <= bestQualityOverall) {
|
||||
bestQualityOverall = q;
|
||||
bestOverall = &(*r);
|
||||
}
|
||||
if ((!avoiding)&&(q <= bestQualityNotAvoid)) {
|
||||
bestQualityNotAvoid = q;
|
||||
bestNotAvoid = &(*r);
|
||||
}
|
||||
}
|
||||
|
||||
if (bestNotAvoid) {
|
||||
(*bestNotAvoid)->use(now);
|
||||
return *bestNotAvoid;
|
||||
} else if ((!strictAvoid)&&(bestOverall)) {
|
||||
(*bestOverall)->use(now);
|
||||
return *bestOverall;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return SharedPtr<Peer>();
|
||||
if (!best)
|
||||
return SharedPtr<Peer>();
|
||||
return *best;
|
||||
}
|
||||
|
||||
bool Topology::isUpstream(const Identity &id) const
|
||||
{
|
||||
if (isRoot(id))
|
||||
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;
|
||||
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())) {
|
||||
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;
|
||||
}
|
||||
|
||||
bool Topology::worldUpdateIfValid(const World &newWorld)
|
||||
ZT_PeerRole Topology::role(const Address &ztaddr) const
|
||||
{
|
||||
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");
|
||||
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;
|
||||
}
|
||||
|
||||
bool Topology::isProhibitedEndpoint(const Address &ztaddr,const InetAddress &ipaddr) const
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void Topology::clean(uint64_t now)
|
||||
bool Topology::addWorld(void *tPtr,const World &newWorld,bool alwaysAcceptNew)
|
||||
{
|
||||
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);
|
||||
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;
|
||||
}
|
||||
}
|
||||
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 {
|
||||
(*p)->clean(now);
|
||||
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;
|
||||
}
|
||||
|
||||
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 true;
|
||||
}
|
||||
|
||||
void Topology::addMoon(void *tPtr,const uint64_t id,const Address &seed)
|
||||
{
|
||||
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 ( ... ) {}
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
void Topology::removeMoon(void *tPtr,const uint64_t id)
|
||||
{
|
||||
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 {
|
||||
uint64_t idtmp[2];
|
||||
idtmp[0] = id; idtmp[1] = 0;
|
||||
RR->node->stateObjectDelete(tPtr,ZT_STATE_OBJECT_MOON,idtmp);
|
||||
}
|
||||
}
|
||||
}
|
||||
_moons.swap(nm);
|
||||
|
||||
Identity Topology::_getIdentity(const Address &zta)
|
||||
{
|
||||
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
|
||||
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);
|
||||
}
|
||||
return Identity();
|
||||
_moonSeeds.swap(cm);
|
||||
|
||||
_memoizeUpstreams(tPtr);
|
||||
}
|
||||
|
||||
void Topology::_setWorld(const World &newWorld)
|
||||
void Topology::doPeriodicTasks(void *tPtr,int64_t now)
|
||||
{
|
||||
// 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);
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Topology::_memoizeUpstreams(void *tPtr)
|
||||
{
|
||||
// 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
|
||||
|
||||
@@ -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,54 +108,28 @@ 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);
|
||||
|
||||
/**
|
||||
* Cache an identity
|
||||
*
|
||||
* This is done automatically on addPeer(), and so is only useful for
|
||||
* cluster identity replication.
|
||||
*
|
||||
* @param id Identity to cache
|
||||
*/
|
||||
void saveIdentity(const Identity &id);
|
||||
|
||||
/**
|
||||
* Get the current favorite root server
|
||||
*
|
||||
* @return Root server with lowest latency or NULL if none
|
||||
*/
|
||||
inline SharedPtr<Peer> getBestRoot() { return getBestRoot((const Address *)0,0,false); }
|
||||
|
||||
/**
|
||||
* Get the best root server, avoiding root servers listed in an array
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
SharedPtr<Peer> getBestRoot(const Address *avoid,unsigned int avoidCount,bool strictAvoid);
|
||||
|
||||
/**
|
||||
* @param id Identity to check
|
||||
* @return True if this is a designated root server in this world
|
||||
*/
|
||||
inline bool isRoot(const Identity &id) const
|
||||
inline SharedPtr<Path> getPath(const int64_t l,const InetAddress &r)
|
||||
{
|
||||
Mutex::Lock _l(_lock);
|
||||
return (std::find(_rootAddresses.begin(),_rootAddresses.end(),id.address()) != _rootAddresses.end());
|
||||
Mutex::Lock _l(_paths_m);
|
||||
SharedPtr<Path> &p = _paths[Path::HashKey(l,r)];
|
||||
if (!p)
|
||||
p.set(new Path(l,r));
|
||||
return p;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current best upstream peer
|
||||
*
|
||||
* @return Upstream or NULL if none available
|
||||
*/
|
||||
SharedPtr<Peer> getUpstreamPeer();
|
||||
|
||||
/**
|
||||
* @param id Identity to check
|
||||
* @return True if this is a root server or a network preferred relay from one of our networks
|
||||
@@ -145,65 +137,171 @@ public:
|
||||
bool isUpstream(const Identity &id) const;
|
||||
|
||||
/**
|
||||
* @return Vector of root server addresses
|
||||
* @param addr Address to check
|
||||
* @return True if we should accept a world update from this address
|
||||
*/
|
||||
inline std::vector<Address> rootAddresses() const
|
||||
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.
|
||||
*
|
||||
* Otherwise it returns false.
|
||||
*
|
||||
* @param ztaddr ZeroTier address
|
||||
* @param ipaddr IP address
|
||||
* @return True if this ZT/IP pair should not be allowed to be used
|
||||
*/
|
||||
bool isProhibitedEndpoint(const Address &ztaddr,const InetAddress &ipaddr) const;
|
||||
|
||||
/**
|
||||
* Gets upstreams to contact and their stable endpoints (if known)
|
||||
*
|
||||
* @param eps Hash table to fill with addresses and their stable endpoints
|
||||
*/
|
||||
inline void getUpstreamsToContact(Hashtable< Address,std::vector<InetAddress> > &eps) const
|
||||
{
|
||||
Mutex::Lock _l(_lock);
|
||||
return _rootAddresses;
|
||||
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];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Current World (copy)
|
||||
* @return Vector of active upstream addresses (including roots)
|
||||
*/
|
||||
inline World world() const
|
||||
inline std::vector<Address> upstreamAddresses() const
|
||||
{
|
||||
Mutex::Lock _l(_lock);
|
||||
return _world;
|
||||
Mutex::Lock _l(_upstreams_m);
|
||||
return _upstreamAddresses;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Current world ID
|
||||
* @return Current moons
|
||||
*/
|
||||
inline uint64_t worldId() const
|
||||
inline std::vector<World> moons() const
|
||||
{
|
||||
return _world.id(); // safe to read without lock, and used from within eachPeer() so don't lock
|
||||
Mutex::Lock _l(_upstreams_m);
|
||||
return _moons;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Current world timestamp
|
||||
* @return Moon IDs we are waiting for from seeds
|
||||
*/
|
||||
inline uint64_t worldTimestamp() const
|
||||
inline std::vector<uint64_t> moonsWanted() const
|
||||
{
|
||||
return _world.timestamp(); // safe to read without lock, and used from within eachPeer() so don't lock
|
||||
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 planet
|
||||
*/
|
||||
inline World planet() const
|
||||
{
|
||||
Mutex::Lock _l(_upstreams_m);
|
||||
return _planet;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Current planet's world ID
|
||||
*/
|
||||
inline uint64_t planetWorldId() const
|
||||
{
|
||||
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 worldUpdateIfValid(const World &newWorld);
|
||||
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
|
||||
*/
|
||||
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 amRoot() const throw() { return _amRoot; }
|
||||
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 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;
|
||||
Hashtable< Address,SharedPtr<Peer> > _peers;
|
||||
std::vector< Address > _rootAddresses;
|
||||
std::vector< SharedPtr<Peer> > _rootPeers;
|
||||
bool _amRoot;
|
||||
std::pair<InetAddress,ZT_PhysicalPathConfiguration> _physicalPathConfig[ZT_MAX_CONFIGURABLE_PATHS];
|
||||
volatile unsigned int _numConfiguredPhysicalPaths;
|
||||
|
||||
Mutex _lock;
|
||||
Hashtable< Address,SharedPtr<Peer> > _peers;
|
||||
Mutex _peers_m;
|
||||
|
||||
Hashtable< Path::HashKey,SharedPtr<Path> > _paths;
|
||||
Mutex _paths_m;
|
||||
|
||||
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
|
||||
|
||||
540
node/Trace.cpp
Normal file
540
node/Trace.cpp
Normal file
@@ -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
|
||||
176
node/Trace.hpp
Normal file
176
node/Trace.hpp
Normal file
@@ -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
|
||||
241
node/Utils.cpp
241
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)
|
||||
// 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)
|
||||
{
|
||||
for(unsigned int i=0;i<len;++i)
|
||||
ptr[i] = (char)0;
|
||||
volatile uint8_t *const end = ptr + len;
|
||||
while (ptr != end) *(ptr++) = (uint8_t)0;
|
||||
}
|
||||
void (*volatile _Utils_doBurn_ptr)(char *,unsigned int) = _Utils_doBurn;
|
||||
void Utils::burn(void *ptr,unsigned int len)
|
||||
throw()
|
||||
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); }
|
||||
|
||||
static unsigned long _Utils_itoa(unsigned long n,char *s)
|
||||
{
|
||||
// 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);
|
||||
}
|
||||
|
||||
std::string Utils::hex(const void *data,unsigned int len)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
std::string Utils::unhex(const char *hex,unsigned int maxlen)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
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)
|
||||
if (n == 0)
|
||||
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;
|
||||
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;
|
||||
}
|
||||
char *Utils::decimal(unsigned long n,char s[24])
|
||||
{
|
||||
if (n == 0) {
|
||||
s[0] = '0';
|
||||
s[1] = (char)0;
|
||||
return s;
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
408
node/Utils.hpp
408
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
|
||||
@@ -111,17 +272,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)
|
||||
*
|
||||
@@ -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
|
||||
@@ -264,6 +371,20 @@ public:
|
||||
return ((((v + (v >> 4)) & (uint32_t)0xF0F0F0F) * (uint32_t)0x1010101) >> 24);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*
|
||||
@@ -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 int64_t hton(int64_t n) { 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 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
|
||||
|
||||
162
node/World.hpp
162
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
|
||||
@@ -48,16 +56,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
|
||||
*
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user