upgraded core to 1.2.0
This commit is contained in:
202
zto/node/Address.hpp
Normal file
202
zto/node/Address.hpp
Normal file
@@ -0,0 +1,202 @@
|
||||
/*
|
||||
* 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_ADDRESS_HPP
|
||||
#define ZT_ADDRESS_HPP
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "Constants.hpp"
|
||||
#include "Utils.hpp"
|
||||
#include "Buffer.hpp"
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
/**
|
||||
* A ZeroTier address
|
||||
*/
|
||||
class Address
|
||||
{
|
||||
public:
|
||||
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)
|
||||
{
|
||||
setTo(bits,len);
|
||||
}
|
||||
|
||||
inline Address &operator=(const Address &a)
|
||||
{
|
||||
_a = a._a;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline Address &operator=(const uint64_t a)
|
||||
{
|
||||
_a = (a & 0xffffffffffULL);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bits Raw address -- 5 bytes, big-endian byte order
|
||||
* @param len Length of array
|
||||
*/
|
||||
inline void setTo(const void *bits,unsigned int len)
|
||||
{
|
||||
if (len < ZT_ADDRESS_LENGTH) {
|
||||
_a = 0;
|
||||
return;
|
||||
}
|
||||
const unsigned char *b = (const unsigned char *)bits;
|
||||
uint64_t a = ((uint64_t)*b++) << 32;
|
||||
a |= ((uint64_t)*b++) << 24;
|
||||
a |= ((uint64_t)*b++) << 16;
|
||||
a |= ((uint64_t)*b++) << 8;
|
||||
a |= ((uint64_t)*b);
|
||||
_a = a;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bits Buffer to hold 5-byte address in big-endian byte order
|
||||
* @param len Length of array
|
||||
*/
|
||||
inline void copyTo(void *bits,unsigned int len) const
|
||||
{
|
||||
if (len < ZT_ADDRESS_LENGTH)
|
||||
return;
|
||||
unsigned char *b = (unsigned char *)bits;
|
||||
*(b++) = (unsigned char)((_a >> 32) & 0xff);
|
||||
*(b++) = (unsigned char)((_a >> 24) & 0xff);
|
||||
*(b++) = (unsigned char)((_a >> 16) & 0xff);
|
||||
*(b++) = (unsigned char)((_a >> 8) & 0xff);
|
||||
*b = (unsigned char)(_a & 0xff);
|
||||
}
|
||||
|
||||
/**
|
||||
* Append to a buffer in big-endian byte order
|
||||
*
|
||||
* @param b Buffer to append to
|
||||
*/
|
||||
template<unsigned int C>
|
||||
inline void appendTo(Buffer<C> &b) const
|
||||
{
|
||||
unsigned char *p = (unsigned char *)b.appendField(ZT_ADDRESS_LENGTH);
|
||||
*(p++) = (unsigned char)((_a >> 32) & 0xff);
|
||||
*(p++) = (unsigned char)((_a >> 24) & 0xff);
|
||||
*(p++) = (unsigned char)((_a >> 16) & 0xff);
|
||||
*(p++) = (unsigned char)((_a >> 8) & 0xff);
|
||||
*p = (unsigned char)(_a & 0xff);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Integer containing address (0 to 2^40)
|
||||
*/
|
||||
inline uint64_t toInt() const
|
||||
{
|
||||
return _a;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Hash code for use with Hashtable
|
||||
*/
|
||||
inline unsigned long hashCode() const
|
||||
{
|
||||
return (unsigned long)_a;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Hexadecimal string
|
||||
*/
|
||||
inline std::string toString() const
|
||||
{
|
||||
char buf[16];
|
||||
Utils::snprintf(buf,sizeof(buf),"%.10llx",(unsigned long long)_a);
|
||||
return std::string(buf);
|
||||
};
|
||||
|
||||
/**
|
||||
* @param buf Buffer to fill
|
||||
* @param len Length of buffer
|
||||
*/
|
||||
inline void toString(char *buf,unsigned int len) const
|
||||
{
|
||||
Utils::snprintf(buf,len,"%.10llx",(unsigned long long)_a);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return True if this address is not zero
|
||||
*/
|
||||
inline operator bool() const { return (_a != 0); }
|
||||
|
||||
/**
|
||||
* Set to null/zero
|
||||
*/
|
||||
inline void zero() { _a = 0; }
|
||||
|
||||
/**
|
||||
* Check if this address is reserved
|
||||
*
|
||||
* The all-zero null address and any address beginning with 0xff are
|
||||
* reserved. (0xff is reserved for future use to designate possibly
|
||||
* longer addresses, addresses based on IPv6 innards, etc.)
|
||||
*
|
||||
* @return True if address is reserved and may not be used
|
||||
*/
|
||||
inline bool isReserved() const
|
||||
{
|
||||
return ((!_a)||((_a >> 32) == ZT_ADDRESS_RESERVED_PREFIX));
|
||||
}
|
||||
|
||||
/**
|
||||
* @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 { return (unsigned char)((_a >> (32 - (i * 8))) & 0xff); }
|
||||
|
||||
inline bool operator==(const uint64_t &a) const { return (_a == (a & 0xffffffffffULL)); }
|
||||
inline bool operator!=(const uint64_t &a) const { return (_a != (a & 0xffffffffffULL)); }
|
||||
inline bool operator>(const uint64_t &a) const { return (_a > (a & 0xffffffffffULL)); }
|
||||
inline bool operator<(const uint64_t &a) const { return (_a < (a & 0xffffffffffULL)); }
|
||||
inline bool operator>=(const uint64_t &a) const { return (_a >= (a & 0xffffffffffULL)); }
|
||||
inline bool operator<=(const uint64_t &a) const { return (_a <= (a & 0xffffffffffULL)); }
|
||||
|
||||
inline bool operator==(const Address &a) const { return (_a == a._a); }
|
||||
inline bool operator!=(const Address &a) const { return (_a != a._a); }
|
||||
inline bool operator>(const Address &a) const { return (_a > a._a); }
|
||||
inline bool operator<(const Address &a) const { return (_a < a._a); }
|
||||
inline bool operator>=(const Address &a) const { return (_a >= a._a); }
|
||||
inline bool operator<=(const Address &a) const { return (_a <= a._a); }
|
||||
|
||||
private:
|
||||
uint64_t _a;
|
||||
};
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
||||
#endif
|
||||
107
zto/node/Array.hpp
Normal file
107
zto/node/Array.hpp
Normal file
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* 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
|
||||
70
zto/node/AtomicCounter.hpp
Normal file
70
zto/node/AtomicCounter.hpp
Normal file
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* 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_ATOMICCOUNTER_HPP
|
||||
#define ZT_ATOMICCOUNTER_HPP
|
||||
|
||||
#include "Constants.hpp"
|
||||
#include "NonCopyable.hpp"
|
||||
|
||||
#ifndef __GNUC__
|
||||
#include <atomic>
|
||||
#endif
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
/**
|
||||
* Simple atomic counter supporting increment and decrement
|
||||
*/
|
||||
class AtomicCounter : NonCopyable
|
||||
{
|
||||
public:
|
||||
AtomicCounter()
|
||||
{
|
||||
_v = 0;
|
||||
}
|
||||
|
||||
inline int operator++()
|
||||
{
|
||||
#ifdef __GNUC__
|
||||
return __sync_add_and_fetch(&_v,1);
|
||||
#else
|
||||
return ++_v;
|
||||
#endif
|
||||
}
|
||||
|
||||
inline int operator--()
|
||||
{
|
||||
#ifdef __GNUC__
|
||||
return __sync_sub_and_fetch(&_v,1);
|
||||
#else
|
||||
return --_v;
|
||||
#endif
|
||||
}
|
||||
|
||||
private:
|
||||
#ifdef __GNUC__
|
||||
int _v;
|
||||
#else
|
||||
std::atomic_int _v;
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
||||
#endif
|
||||
496
zto/node/Buffer.hpp
Normal file
496
zto/node/Buffer.hpp
Normal file
@@ -0,0 +1,496 @@
|
||||
/*
|
||||
* 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_BUFFER_HPP
|
||||
#define ZT_BUFFER_HPP
|
||||
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
#include <utility>
|
||||
|
||||
#include "Constants.hpp"
|
||||
#include "Utils.hpp"
|
||||
|
||||
#if defined(__GNUC__) && (!defined(ZT_NO_TYPE_PUNNING))
|
||||
#define ZT_VAR_MAY_ALIAS __attribute__((__may_alias__))
|
||||
#else
|
||||
#define ZT_VAR_MAY_ALIAS
|
||||
#endif
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
/**
|
||||
* A variable length but statically allocated buffer
|
||||
*
|
||||
* Bounds-checking is done everywhere, since this is used in security
|
||||
* critical code. This supports construction and assignment from buffers
|
||||
* of differing capacities, provided the data actually in them fits.
|
||||
* It throws std::out_of_range on any boundary violation.
|
||||
*
|
||||
* The at(), append(), etc. methods encode integers larger than 8-bit in
|
||||
* big-endian (network) byte order.
|
||||
*
|
||||
* @tparam C Total capacity
|
||||
*/
|
||||
template<unsigned int C>
|
||||
class Buffer
|
||||
{
|
||||
// I love me!
|
||||
template <unsigned int C2> friend class Buffer;
|
||||
|
||||
public:
|
||||
// STL container idioms
|
||||
typedef unsigned char value_type;
|
||||
typedef unsigned char * pointer;
|
||||
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;
|
||||
typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
|
||||
inline iterator begin() { return _b; }
|
||||
inline iterator end() { return (_b + _l); }
|
||||
inline const_iterator begin() const { return _b; }
|
||||
inline const_iterator end() const { return (_b + _l); }
|
||||
inline reverse_iterator rbegin() { return reverse_iterator(begin()); }
|
||||
inline reverse_iterator rend() { return reverse_iterator(end()); }
|
||||
inline const_reverse_iterator rbegin() const { return const_reverse_iterator(begin()); }
|
||||
inline const_reverse_iterator rend() const { return const_reverse_iterator(end()); }
|
||||
|
||||
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");
|
||||
_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());
|
||||
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");
|
||||
_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");
|
||||
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");
|
||||
return ((unsigned char *)_b)[i];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a raw pointer to a field with bounds checking
|
||||
*
|
||||
* This isn't perfectly safe in that the caller could still overflow
|
||||
* the pointer, but its use provides both a sanity check and
|
||||
* documentation / reminder to the calling code to treat the returned
|
||||
* pointer as being of size [l].
|
||||
*
|
||||
* @param i Index of field in buffer
|
||||
* @param l Length of field in bytes
|
||||
* @return Pointer to field data
|
||||
* @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");
|
||||
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");
|
||||
return (const unsigned char *)(_b + i);
|
||||
}
|
||||
|
||||
/**
|
||||
* Place a primitive integer value at a given position
|
||||
*
|
||||
* @param i Index to place value
|
||||
* @param v Value
|
||||
* @tparam T Integer type (e.g. uint16_t, int64_t)
|
||||
*/
|
||||
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");
|
||||
#ifdef ZT_NO_TYPE_PUNNING
|
||||
uint8_t *p = reinterpret_cast<uint8_t *>(_b + i);
|
||||
for(unsigned int x=1;x<=sizeof(T);++x)
|
||||
*(p++) = (uint8_t)(v >> (8 * (sizeof(T) - x)));
|
||||
#else
|
||||
T *const ZT_VAR_MAY_ALIAS p = reinterpret_cast<T *>(_b + i);
|
||||
*p = Utils::hton(v);
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a primitive integer value at a given position
|
||||
*
|
||||
* @param i Index to get integer
|
||||
* @tparam T Integer type (e.g. uint16_t, int64_t)
|
||||
* @return Integer value
|
||||
*/
|
||||
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");
|
||||
#ifdef ZT_NO_TYPE_PUNNING
|
||||
T v = 0;
|
||||
const uint8_t *p = reinterpret_cast<const uint8_t *>(_b + i);
|
||||
for(unsigned int x=0;x<sizeof(T);++x) {
|
||||
v <<= 8;
|
||||
v |= (T)*(p++);
|
||||
}
|
||||
return v;
|
||||
#else
|
||||
const T *const ZT_VAR_MAY_ALIAS p = reinterpret_cast<const T *>(_b + i);
|
||||
return Utils::ntoh(*p);
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Append an integer type to this buffer
|
||||
*
|
||||
* @param v Value to append
|
||||
* @tparam T Integer type (e.g. uint16_t, int64_t)
|
||||
* @throws std::out_of_range Attempt to append beyond capacity
|
||||
*/
|
||||
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");
|
||||
#ifdef ZT_NO_TYPE_PUNNING
|
||||
uint8_t *p = reinterpret_cast<uint8_t *>(_b + _l);
|
||||
for(unsigned int x=1;x<=sizeof(T);++x)
|
||||
*(p++) = (uint8_t)(v >> (8 * (sizeof(T) - x)));
|
||||
#else
|
||||
T *const ZT_VAR_MAY_ALIAS p = reinterpret_cast<T *>(_b + _l);
|
||||
*p = Utils::hton(v);
|
||||
#endif
|
||||
_l += sizeof(T);
|
||||
}
|
||||
|
||||
/**
|
||||
* Append a run of bytes
|
||||
*
|
||||
* @param c Character value to append
|
||||
* @param n Number of times to append
|
||||
* @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");
|
||||
for(unsigned int i=0;i<n;++i)
|
||||
_b[_l++] = (char)c;
|
||||
}
|
||||
|
||||
/**
|
||||
* Append a C-array of bytes
|
||||
*
|
||||
* @param b Data
|
||||
* @param l Length
|
||||
* @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);
|
||||
_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
|
||||
*
|
||||
* @param s C string
|
||||
* @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 (!(_b[_l++] = *(s++)))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Append a buffer
|
||||
*
|
||||
* @param b Buffer to append
|
||||
* @tparam C2 Capacity of second buffer (typically inferred)
|
||||
* @throws std::out_of_range Attempt to append beyond capacity
|
||||
*/
|
||||
template<unsigned int C2>
|
||||
inline void append(const Buffer<C2> &b)
|
||||
throw(std::out_of_range)
|
||||
{
|
||||
append(b._b,b._l);
|
||||
}
|
||||
|
||||
/**
|
||||
* Increment size and return pointer to field of specified size
|
||||
*
|
||||
* Nothing is actually written to the memory. This is a shortcut
|
||||
* for addSize() followed by field() to reference the previous
|
||||
* position and the new size.
|
||||
*
|
||||
* @param l Length of field to append
|
||||
* @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");
|
||||
char *r = _b + _l;
|
||||
_l += l;
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* Increment size by a given number of bytes
|
||||
*
|
||||
* The contents of new space are undefined.
|
||||
*
|
||||
* @param i Bytes to increment
|
||||
* @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");
|
||||
_l += i;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set size of data in buffer
|
||||
*
|
||||
* The contents of new space are undefined.
|
||||
*
|
||||
* @param i New size
|
||||
* @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");
|
||||
_l = i;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
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");
|
||||
::memmove(_b,_b + at,_l -= at);
|
||||
}
|
||||
|
||||
/**
|
||||
* Erase something from the middle of the buffer
|
||||
*
|
||||
* @param start Starting position
|
||||
* @param length Length of block to erase
|
||||
* @throw 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");
|
||||
::memmove(_b + at,_b + endr,_l - endr);
|
||||
_l -= length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set buffer data length to zero
|
||||
*/
|
||||
inline void clear() { _l = 0; }
|
||||
|
||||
/**
|
||||
* Zero buffer up to size()
|
||||
*/
|
||||
inline void zero() { memset(_b,0,_l); }
|
||||
|
||||
/**
|
||||
* Zero unused capacity area
|
||||
*/
|
||||
inline void zeroUnused() { memset(_b + _l,0,C - _l); }
|
||||
|
||||
/**
|
||||
* Unconditionally and securely zero buffer's underlying memory
|
||||
*/
|
||||
inline void burn() { Utils::burn(_b,sizeof(_b)); }
|
||||
|
||||
/**
|
||||
* @return Constant pointer to data in buffer
|
||||
*/
|
||||
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 { return _l; }
|
||||
|
||||
/**
|
||||
* @return Capacity of buffer
|
||||
*/
|
||||
inline unsigned int capacity() const { return C; }
|
||||
|
||||
template<unsigned int C2>
|
||||
inline bool operator==(const Buffer<C2> &b) const
|
||||
{
|
||||
return ((_l == b._l)&&(!memcmp(_b,b._b,_l)));
|
||||
}
|
||||
template<unsigned int C2>
|
||||
inline bool operator!=(const Buffer<C2> &b) const
|
||||
{
|
||||
return ((_l != b._l)||(memcmp(_b,b._b,_l)));
|
||||
}
|
||||
template<unsigned int C2>
|
||||
inline bool operator<(const Buffer<C2> &b) const
|
||||
{
|
||||
return (memcmp(_b,b._b,std::min(_l,b._l)) < 0);
|
||||
}
|
||||
template<unsigned int C2>
|
||||
inline bool operator>(const Buffer<C2> &b) const
|
||||
{
|
||||
return (b < *this);
|
||||
}
|
||||
template<unsigned int C2>
|
||||
inline bool operator<=(const Buffer<C2> &b) const
|
||||
{
|
||||
return !(b < *this);
|
||||
}
|
||||
template<unsigned int C2>
|
||||
inline bool operator>=(const Buffer<C2> &b) const
|
||||
{
|
||||
return !(*this < b);
|
||||
}
|
||||
|
||||
private:
|
||||
unsigned int _l;
|
||||
char ZT_VAR_MAY_ALIAS _b[C];
|
||||
};
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
||||
#endif
|
||||
2398
zto/node/C25519.cpp
Normal file
2398
zto/node/C25519.cpp
Normal file
File diff suppressed because it is too large
Load Diff
213
zto/node/C25519.hpp
Normal file
213
zto/node/C25519.hpp
Normal file
@@ -0,0 +1,213 @@
|
||||
/*
|
||||
* 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_C25519_HPP
|
||||
#define ZT_C25519_HPP
|
||||
|
||||
#include "Array.hpp"
|
||||
#include "Utils.hpp"
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
#define ZT_C25519_PUBLIC_KEY_LEN 64
|
||||
#define ZT_C25519_PRIVATE_KEY_LEN 64
|
||||
#define ZT_C25519_SIGNATURE_LEN 96
|
||||
|
||||
/**
|
||||
* A combined Curve25519 ECDH and Ed25519 signature engine
|
||||
*/
|
||||
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;
|
||||
|
||||
/**
|
||||
* Generate a C25519 elliptic curve key pair
|
||||
*/
|
||||
static inline Pair generate()
|
||||
throw()
|
||||
{
|
||||
Pair kp;
|
||||
Utils::getSecureRandom(kp.priv.data,(unsigned int)kp.priv.size());
|
||||
_calcPubDH(kp);
|
||||
_calcPubED(kp);
|
||||
return kp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a key pair satisfying a condition
|
||||
*
|
||||
* This begins with a random keypair from a random secret key and then
|
||||
* iteratively increments the random secret until cond(kp) returns true.
|
||||
* This is used to compute key pairs in which the public key, its hash
|
||||
* or some other aspect of it satisfies some condition, such as for a
|
||||
* hashcash criteria.
|
||||
*
|
||||
* @param cond Condition function or function object
|
||||
* @return Key pair where cond(kp) returns true
|
||||
* @tparam F Type of 'cond'
|
||||
*/
|
||||
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());
|
||||
_calcPubED(kp); // do Ed25519 key -- bytes 32-63 of pub and priv
|
||||
do {
|
||||
++(((uint64_t *)priv)[1]);
|
||||
--(((uint64_t *)priv)[2]);
|
||||
_calcPubDH(kp); // keep regenerating bytes 0-31 until satisfied
|
||||
} while (!cond(kp));
|
||||
return kp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform C25519 ECC key agreement
|
||||
*
|
||||
* Actual key bytes are generated from one or more SHA-512 digests of
|
||||
* the raw result of key agreement.
|
||||
*
|
||||
* @param mine My private key
|
||||
* @param their Their public key
|
||||
* @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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sign a message with a sender's key pair
|
||||
*
|
||||
* This takes the SHA-521 of msg[] and then signs the first 32 bytes of this
|
||||
* digest, returning it and the 64-byte ed25519 signature in signature[].
|
||||
* This results in a signature that verifies both the signer's authenticity
|
||||
* and the integrity of the message.
|
||||
*
|
||||
* This is based on the original ed25519 code from NaCl and the SUPERCOP
|
||||
* cipher benchmark suite, but with the modification that it always
|
||||
* produces a signature of fixed 96-byte length based on the hash of an
|
||||
* arbitrary-length message.
|
||||
*
|
||||
* @param myPrivate My private key
|
||||
* @param myPublic My public key
|
||||
* @param msg Message to sign
|
||||
* @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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sign a message with a sender's key pair
|
||||
*
|
||||
* @param myPrivate My private key
|
||||
* @param myPublic My public key
|
||||
* @param msg Message to sign
|
||||
* @param len Length of message in bytes
|
||||
* @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);
|
||||
return sig;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify a message's signature
|
||||
*
|
||||
* @param their Public key to verify against
|
||||
* @param msg Message to verify signature integrity against
|
||||
* @param len Length of message in bytes
|
||||
* @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();
|
||||
|
||||
/**
|
||||
* Verify a message's signature
|
||||
*
|
||||
* @param their Public key to verify against
|
||||
* @param msg Message to verify signature integrity against
|
||||
* @param len Length of message in bytes
|
||||
* @param signature 96-byte signature
|
||||
* @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);
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
// 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();
|
||||
};
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
||||
#endif
|
||||
65
zto/node/Capability.cpp
Normal file
65
zto/node/Capability.cpp
Normal file
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* 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 "Capability.hpp"
|
||||
#include "RuntimeEnvironment.hpp"
|
||||
#include "Identity.hpp"
|
||||
#include "Topology.hpp"
|
||||
#include "Switch.hpp"
|
||||
#include "Network.hpp"
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
int Capability::verify(const RuntimeEnvironment *RR) 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(_custody[c].from));
|
||||
if (id) {
|
||||
if (!id.verify(tmp.data(),tmp.size(),_custody[c].signature))
|
||||
return -1;
|
||||
} else {
|
||||
RR->sw->requestWhois(_custody[c].from);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
// We reached max custody chain length and everything was valid
|
||||
return 0;
|
||||
} catch ( ... ) {}
|
||||
return -1;
|
||||
}
|
||||
|
||||
} // namespace ZeroTier
|
||||
469
zto/node/Capability.hpp
Normal file
469
zto/node/Capability.hpp
Normal file
@@ -0,0 +1,469 @@
|
||||
/*
|
||||
* 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_CAPABILITY_HPP
|
||||
#define ZT_CAPABILITY_HPP
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "Constants.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:
|
||||
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,uint64_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)
|
||||
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 uint64_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) 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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:
|
||||
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:
|
||||
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:
|
||||
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;
|
||||
}
|
||||
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 std::runtime_error("rule overflow");
|
||||
deserializeRules(b,p,_rules,_ruleCount,rc);
|
||||
|
||||
_maxCustodyChainLength = (unsigned int)b[p++];
|
||||
if ((_maxCustodyChainLength < 1)||(_maxCustodyChainLength > ZT_MAX_CAPABILITY_CUSTODY_CHAIN_LENGTH))
|
||||
throw std::runtime_error("invalid max custody chain length");
|
||||
|
||||
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 std::runtime_error("unterminated custody chain");
|
||||
_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 std::runtime_error("invalid signature");
|
||||
p += 2;
|
||||
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 std::runtime_error("extended field 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;
|
||||
uint64_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
|
||||
231
zto/node/CertificateOfMembership.cpp
Normal file
231
zto/node/CertificateOfMembership.cpp
Normal file
@@ -0,0 +1,231 @@
|
||||
/*
|
||||
* 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 "CertificateOfMembership.hpp"
|
||||
#include "RuntimeEnvironment.hpp"
|
||||
#include "Topology.hpp"
|
||||
#include "Switch.hpp"
|
||||
#include "Network.hpp"
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
void CertificateOfMembership::setQualifier(uint64_t id,uint64_t value,uint64_t maxDelta)
|
||||
{
|
||||
_signedBy.zero();
|
||||
|
||||
for(unsigned int i=0;i<_qualifierCount;++i) {
|
||||
if (_qualifiers[i].id == id) {
|
||||
_qualifiers[i].value = value;
|
||||
_qualifiers[i].maxDelta = maxDelta;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (_qualifierCount < ZT_NETWORK_COM_MAX_QUALIFIERS) {
|
||||
_qualifiers[_qualifierCount].id = id;
|
||||
_qualifiers[_qualifierCount].value = value;
|
||||
_qualifiers[_qualifierCount].maxDelta = maxDelta;
|
||||
++_qualifierCount;
|
||||
std::sort(&(_qualifiers[0]),&(_qualifiers[_qualifierCount]));
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef ZT_SUPPORT_OLD_STYLE_NETCONF
|
||||
|
||||
std::string CertificateOfMembership::toString() const
|
||||
{
|
||||
std::string s;
|
||||
|
||||
s.append("1:"); // COM_UINT64_ED25519
|
||||
|
||||
uint64_t *const buf = new uint64_t[_qualifierCount * 3];
|
||||
try {
|
||||
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);
|
||||
}
|
||||
s.append(Utils::hex(buf,ptr * sizeof(uint64_t)));
|
||||
delete [] buf;
|
||||
} catch ( ... ) {
|
||||
delete [] buf;
|
||||
throw;
|
||||
}
|
||||
|
||||
s.push_back(':');
|
||||
|
||||
s.append(_signedBy.toString());
|
||||
|
||||
if (_signedBy) {
|
||||
s.push_back(':');
|
||||
s.append(Utils::hex(_signature.data,(unsigned int)_signature.size()));
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
void CertificateOfMembership::fromString(const char *s)
|
||||
{
|
||||
_qualifierCount = 0;
|
||||
_signedBy.zero();
|
||||
memset(_signature.data,0,_signature.size());
|
||||
|
||||
if (!*s)
|
||||
return;
|
||||
|
||||
unsigned int colonAt = 0;
|
||||
while ((s[colonAt])&&(s[colonAt] != ':')) ++colonAt;
|
||||
|
||||
if (!((colonAt == 1)&&(s[0] == '1'))) // COM_UINT64_ED25519?
|
||||
return;
|
||||
|
||||
s += colonAt + 1;
|
||||
colonAt = 0;
|
||||
while ((s[colonAt])&&(s[colonAt] != ':')) ++colonAt;
|
||||
|
||||
if (colonAt) {
|
||||
const unsigned int buflen = colonAt / 2;
|
||||
char *const buf = new char[buflen];
|
||||
unsigned int bufactual = Utils::unhex(s,colonAt,buf,buflen);
|
||||
char *bufptr = buf;
|
||||
try {
|
||||
while (bufactual >= 24) {
|
||||
if (_qualifierCount < ZT_NETWORK_COM_MAX_QUALIFIERS) {
|
||||
_qualifiers[_qualifierCount].id = Utils::ntoh(*((uint64_t *)bufptr)); bufptr += 8;
|
||||
_qualifiers[_qualifierCount].value = Utils::ntoh(*((uint64_t *)bufptr)); bufptr += 8;
|
||||
_qualifiers[_qualifierCount].maxDelta = Utils::ntoh(*((uint64_t *)bufptr)); bufptr += 8;
|
||||
++_qualifierCount;
|
||||
} else {
|
||||
bufptr += 24;
|
||||
}
|
||||
bufactual -= 24;
|
||||
}
|
||||
} catch ( ... ) {}
|
||||
delete [] buf;
|
||||
}
|
||||
|
||||
if (s[colonAt]) {
|
||||
s += colonAt + 1;
|
||||
colonAt = 0;
|
||||
while ((s[colonAt])&&(s[colonAt] != ':')) ++colonAt;
|
||||
|
||||
if (colonAt) {
|
||||
char addrbuf[ZT_ADDRESS_LENGTH];
|
||||
if (Utils::unhex(s,colonAt,addrbuf,sizeof(addrbuf)) == ZT_ADDRESS_LENGTH)
|
||||
_signedBy.setTo(addrbuf,ZT_ADDRESS_LENGTH);
|
||||
|
||||
if ((_signedBy)&&(s[colonAt])) {
|
||||
s += colonAt + 1;
|
||||
colonAt = 0;
|
||||
while ((s[colonAt])&&(s[colonAt] != ':')) ++colonAt;
|
||||
if (colonAt) {
|
||||
if (Utils::unhex(s,colonAt,_signature.data,(unsigned int)_signature.size()) != _signature.size())
|
||||
_signedBy.zero();
|
||||
} else {
|
||||
_signedBy.zero();
|
||||
}
|
||||
} else {
|
||||
_signedBy.zero();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::sort(&(_qualifiers[0]),&(_qualifiers[_qualifierCount]));
|
||||
}
|
||||
|
||||
#endif // ZT_SUPPORT_OLD_STYLE_NETCONF
|
||||
|
||||
bool CertificateOfMembership::agreesWith(const CertificateOfMembership &other) const
|
||||
{
|
||||
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.
|
||||
if (otheridx >= other._qualifierCount)
|
||||
return false;
|
||||
|
||||
// Seek to corresponding tuple in other, ignoring tuples that
|
||||
// we may not have. If we run off the end of other, the tuple is
|
||||
// missing. This works because tuples are sorted by ID.
|
||||
while (other._qualifiers[otheridx].id != _qualifiers[myidx].id) {
|
||||
++otheridx;
|
||||
if (otheridx >= other._qualifierCount)
|
||||
return false;
|
||||
}
|
||||
|
||||
// Compare to determine if the absolute value of the difference
|
||||
// between these two parameters is within our maxDelta.
|
||||
const uint64_t a = _qualifiers[myidx].value;
|
||||
const uint64_t b = other._qualifiers[myidx].value;
|
||||
if (((a >= b) ? (a - b) : (b - a)) > _qualifiers[myidx].maxDelta)
|
||||
return false;
|
||||
|
||||
++myidx;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CertificateOfMembership::sign(const Identity &with)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
try {
|
||||
_signature = with.sign(buf,ptr * sizeof(uint64_t));
|
||||
_signedBy = with.address();
|
||||
return true;
|
||||
} catch ( ... ) {
|
||||
_signedBy.zero();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
int CertificateOfMembership::verify(const RuntimeEnvironment *RR) const
|
||||
{
|
||||
if ((!_signedBy)||(_signedBy != Network::controllerFor(networkId()))||(_qualifierCount > ZT_NETWORK_COM_MAX_QUALIFIERS))
|
||||
return -1;
|
||||
|
||||
const Identity id(RR->topology->getIdentity(_signedBy));
|
||||
if (!id) {
|
||||
RR->sw->requestWhois(_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);
|
||||
}
|
||||
return (id.verify(buf,ptr * sizeof(uint64_t),_signature) ? 0 : -1);
|
||||
}
|
||||
|
||||
} // namespace ZeroTier
|
||||
357
zto/node/CertificateOfMembership.hpp
Normal file
357
zto/node/CertificateOfMembership.hpp
Normal file
@@ -0,0 +1,357 @@
|
||||
/*
|
||||
* 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_CERTIFICATEOFMEMBERSHIP_HPP
|
||||
#define ZT_CERTIFICATEOFMEMBERSHIP_HPP
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <string>
|
||||
#include <stdexcept>
|
||||
#include <algorithm>
|
||||
|
||||
#include "Constants.hpp"
|
||||
#include "Buffer.hpp"
|
||||
#include "Address.hpp"
|
||||
#include "C25519.hpp"
|
||||
#include "Identity.hpp"
|
||||
#include "Utils.hpp"
|
||||
|
||||
/**
|
||||
* Maximum number of qualifiers allowed in a COM (absolute max: 65535)
|
||||
*/
|
||||
#define ZT_NETWORK_COM_MAX_QUALIFIERS 8
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
class RuntimeEnvironment;
|
||||
|
||||
/**
|
||||
* Certificate of network membership
|
||||
*
|
||||
* The COM contains a sorted set of three-element tuples called qualifiers.
|
||||
* These contain an id, a value, and a maximum delta.
|
||||
*
|
||||
* The ID is arbitrary and should be assigned using a scheme that makes
|
||||
* every ID globally unique. IDs beneath 65536 are reserved for global
|
||||
* assignment by ZeroTier Networks.
|
||||
*
|
||||
* The value's meaning is ID-specific and isn't important here. What's
|
||||
* important is the value and the third member of the tuple: the maximum
|
||||
* delta. The maximum delta is the maximum difference permitted between
|
||||
* values for a given ID between certificates for the two certificates to
|
||||
* themselves agree.
|
||||
*
|
||||
* Network membership is checked by checking whether a peer's certificate
|
||||
* agrees with your own. The timestamp provides the fundamental criterion--
|
||||
* each member of a private network must constantly obtain new certificates
|
||||
* often enough to stay within the max delta for this qualifier. But other
|
||||
* criteria could be added in the future for very special behaviors, things
|
||||
* like latitude and longitude for instance.
|
||||
*
|
||||
* This is a memcpy()'able structure and is safe (in a crash sense) to modify
|
||||
* without locks.
|
||||
*/
|
||||
class CertificateOfMembership
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Reserved qualifier IDs
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
enum ReservedId
|
||||
{
|
||||
/**
|
||||
* Timestamp of certificate
|
||||
*/
|
||||
COM_RESERVED_ID_TIMESTAMP = 0,
|
||||
|
||||
/**
|
||||
* Network ID for which certificate was issued
|
||||
*/
|
||||
COM_RESERVED_ID_NETWORK_ID = 1,
|
||||
|
||||
/**
|
||||
* ZeroTier address to whom certificate was issued
|
||||
*/
|
||||
COM_RESERVED_ID_ISSUED_TO = 2
|
||||
};
|
||||
|
||||
/**
|
||||
* Create an empty certificate of membership
|
||||
*/
|
||||
CertificateOfMembership()
|
||||
{
|
||||
memset(this,0,sizeof(CertificateOfMembership));
|
||||
}
|
||||
|
||||
CertificateOfMembership(const CertificateOfMembership &c)
|
||||
{
|
||||
memcpy(this,&c,sizeof(CertificateOfMembership));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create from required fields common to all networks
|
||||
*
|
||||
* @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 timestamp,uint64_t timestampMaxDelta,uint64_t nwid,const Address &issuedTo)
|
||||
{
|
||||
_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;
|
||||
_qualifiers[2].id = COM_RESERVED_ID_ISSUED_TO;
|
||||
_qualifiers[2].value = issuedTo.toInt();
|
||||
_qualifiers[2].maxDelta = 0xffffffffffffffffULL;
|
||||
_qualifierCount = 3;
|
||||
memset(_signature.data,0,_signature.size());
|
||||
}
|
||||
|
||||
inline CertificateOfMembership &operator=(const CertificateOfMembership &c)
|
||||
{
|
||||
memcpy(this,&c,sizeof(CertificateOfMembership));
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create from binary-serialized COM in buffer
|
||||
*
|
||||
* @param b Buffer to deserialize from
|
||||
* @param startAt Position to start in buffer
|
||||
*/
|
||||
template<unsigned int C>
|
||||
CertificateOfMembership(const Buffer<C> &b,unsigned int startAt = 0)
|
||||
{
|
||||
deserialize(b,startAt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return True if there's something here
|
||||
*/
|
||||
inline operator bool() const throw() { return (_qualifierCount != 0); }
|
||||
|
||||
/**
|
||||
* @return Timestamp for this cert and maximum delta for timestamp
|
||||
*/
|
||||
inline std::pair<uint64_t,uint64_t> timestamp() const
|
||||
{
|
||||
for(unsigned int i=0;i<_qualifierCount;++i) {
|
||||
if (_qualifiers[i].id == COM_RESERVED_ID_TIMESTAMP)
|
||||
return std::pair<uint64_t,uint64_t>(_qualifiers[i].value,_qualifiers[i].maxDelta);
|
||||
}
|
||||
return std::pair<uint64_t,uint64_t>(0ULL,0ULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Address to which this cert was issued
|
||||
*/
|
||||
inline Address issuedTo() const
|
||||
{
|
||||
for(unsigned int i=0;i<_qualifierCount;++i) {
|
||||
if (_qualifiers[i].id == COM_RESERVED_ID_ISSUED_TO)
|
||||
return Address(_qualifiers[i].value);
|
||||
}
|
||||
return Address();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Network ID for which this cert was issued
|
||||
*/
|
||||
inline uint64_t networkId() const
|
||||
{
|
||||
for(unsigned int i=0;i<_qualifierCount;++i) {
|
||||
if (_qualifiers[i].id == COM_RESERVED_ID_NETWORK_ID)
|
||||
return _qualifiers[i].value;
|
||||
}
|
||||
return 0ULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add or update a qualifier in this certificate
|
||||
*
|
||||
* Any signature is invalidated and signedBy is set to null.
|
||||
*
|
||||
* @param id Qualifier ID
|
||||
* @param value Qualifier value
|
||||
* @param maxDelta Qualifier maximum allowed difference (absolute value of difference)
|
||||
*/
|
||||
void setQualifier(uint64_t id,uint64_t value,uint64_t maxDelta);
|
||||
inline void setQualifier(ReservedId id,uint64_t value,uint64_t maxDelta) { setQualifier((uint64_t)id,value,maxDelta); }
|
||||
|
||||
#ifdef ZT_SUPPORT_OLD_STYLE_NETCONF
|
||||
/**
|
||||
* @return String-serialized representation of this certificate
|
||||
*/
|
||||
std::string toString() const;
|
||||
|
||||
/**
|
||||
* Set this certificate equal to the hex-serialized string
|
||||
*
|
||||
* Invalid strings will result in invalid or undefined certificate
|
||||
* contents. These will subsequently fail validation and comparison.
|
||||
* Empty strings will result in an empty certificate.
|
||||
*
|
||||
* @param s String to deserialize
|
||||
*/
|
||||
void fromString(const char *s);
|
||||
#endif // ZT_SUPPORT_OLD_STYLE_NETCONF
|
||||
|
||||
/**
|
||||
* Compare two certificates for parameter agreement
|
||||
*
|
||||
* This compares this certificate with the other and returns true if all
|
||||
* paramters in this cert are present in the other and if they agree to
|
||||
* within this cert's max delta value for each given parameter.
|
||||
*
|
||||
* Tuples present in other but not in this cert are ignored, but any
|
||||
* tuples present in this cert but not in other result in 'false'.
|
||||
*
|
||||
* @param other Cert to compare with
|
||||
* @return True if certs agree and 'other' may be communicated with
|
||||
*/
|
||||
bool agreesWith(const CertificateOfMembership &other) const;
|
||||
|
||||
/**
|
||||
* Sign this certificate
|
||||
*
|
||||
* @param with Identity to sign with, must include private key
|
||||
* @return True if signature was successful
|
||||
*/
|
||||
bool sign(const Identity &with);
|
||||
|
||||
/**
|
||||
* Verify this COM and its signature
|
||||
*
|
||||
* @param RR Runtime environment for looking up peers
|
||||
* @return 0 == OK, 1 == waiting for WHOIS, -1 == BAD signature or credential
|
||||
*/
|
||||
int verify(const RuntimeEnvironment *RR) const;
|
||||
|
||||
/**
|
||||
* @return True if signed
|
||||
*/
|
||||
inline bool isSigned() const throw() { return (_signedBy); }
|
||||
|
||||
/**
|
||||
* @return Address that signed this certificate or null address if none
|
||||
*/
|
||||
inline const Address &signedBy() const throw() { return _signedBy; }
|
||||
|
||||
template<unsigned int C>
|
||||
inline void serialize(Buffer<C> &b) const
|
||||
{
|
||||
b.append((uint8_t)1);
|
||||
b.append((uint16_t)_qualifierCount);
|
||||
for(unsigned int i=0;i<_qualifierCount;++i) {
|
||||
b.append(_qualifiers[i].id);
|
||||
b.append(_qualifiers[i].value);
|
||||
b.append(_qualifiers[i].maxDelta);
|
||||
}
|
||||
_signedBy.appendTo(b);
|
||||
if (_signedBy)
|
||||
b.append(_signature.data,(unsigned int)_signature.size());
|
||||
}
|
||||
|
||||
template<unsigned int C>
|
||||
inline unsigned int deserialize(const Buffer<C> &b,unsigned int startAt = 0)
|
||||
{
|
||||
unsigned int p = startAt;
|
||||
|
||||
_qualifierCount = 0;
|
||||
_signedBy.zero();
|
||||
|
||||
if (b[p++] != 1)
|
||||
throw std::invalid_argument("invalid object");
|
||||
|
||||
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");
|
||||
else lastId = qid;
|
||||
if (_qualifierCount < ZT_NETWORK_COM_MAX_QUALIFIERS) {
|
||||
_qualifiers[_qualifierCount].id = qid;
|
||||
_qualifiers[_qualifierCount].value = b.template at<uint64_t>(p + 8);
|
||||
_qualifiers[_qualifierCount].maxDelta = b.template at<uint64_t>(p + 16);
|
||||
p += 24;
|
||||
++_qualifierCount;
|
||||
} else {
|
||||
throw std::invalid_argument("too many qualifiers");
|
||||
}
|
||||
}
|
||||
|
||||
_signedBy.setTo(b.field(p,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH);
|
||||
p += ZT_ADDRESS_LENGTH;
|
||||
|
||||
if (_signedBy) {
|
||||
memcpy(_signature.data,b.field(p,(unsigned int)_signature.size()),_signature.size());
|
||||
p += (unsigned int)_signature.size();
|
||||
}
|
||||
|
||||
return (p - startAt);
|
||||
}
|
||||
|
||||
inline bool operator==(const CertificateOfMembership &c) const
|
||||
throw()
|
||||
{
|
||||
if (_signedBy != c._signedBy)
|
||||
return false;
|
||||
if (_qualifierCount != c._qualifierCount)
|
||||
return false;
|
||||
for(unsigned int i=0;i<_qualifierCount;++i) {
|
||||
const _Qualifier &a = _qualifiers[i];
|
||||
const _Qualifier &b = c._qualifiers[i];
|
||||
if ((a.id != b.id)||(a.value != b.value)||(a.maxDelta != b.maxDelta))
|
||||
return false;
|
||||
}
|
||||
return (_signature == c._signature);
|
||||
}
|
||||
inline bool operator!=(const CertificateOfMembership &c) const throw() { return (!(*this == c)); }
|
||||
|
||||
private:
|
||||
struct _Qualifier
|
||||
{
|
||||
_Qualifier() : id(0),value(0),maxDelta(0) {}
|
||||
uint64_t id;
|
||||
uint64_t value;
|
||||
uint64_t maxDelta;
|
||||
inline bool operator<(const _Qualifier &q) const throw() { return (id < q.id); } // sort order
|
||||
};
|
||||
|
||||
Address _signedBy;
|
||||
_Qualifier _qualifiers[ZT_NETWORK_COM_MAX_QUALIFIERS];
|
||||
unsigned int _qualifierCount;
|
||||
C25519::Signature _signature;
|
||||
};
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
||||
#endif
|
||||
63
zto/node/CertificateOfOwnership.cpp
Normal file
63
zto/node/CertificateOfOwnership.cpp
Normal file
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* 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 "CertificateOfOwnership.hpp"
|
||||
#include "RuntimeEnvironment.hpp"
|
||||
#include "Identity.hpp"
|
||||
#include "Topology.hpp"
|
||||
#include "Switch.hpp"
|
||||
#include "Network.hpp"
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
int CertificateOfOwnership::verify(const RuntimeEnvironment *RR) const
|
||||
{
|
||||
if ((!_signedBy)||(_signedBy != Network::controllerFor(_networkId)))
|
||||
return -1;
|
||||
const Identity id(RR->topology->getIdentity(_signedBy));
|
||||
if (!id) {
|
||||
RR->sw->requestWhois(_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
|
||||
236
zto/node/CertificateOfOwnership.hpp
Normal file
236
zto/node/CertificateOfOwnership.hpp
Normal file
@@ -0,0 +1,236 @@
|
||||
/*
|
||||
* 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_CERTIFICATEOFOWNERSHIP_HPP
|
||||
#define ZT_CERTIFICATEOFOWNERSHIP_HPP
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "Constants.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:
|
||||
enum Thing
|
||||
{
|
||||
THING_NULL = 0,
|
||||
THING_MAC_ADDRESS = 1,
|
||||
THING_IPV4_ADDRESS = 2,
|
||||
THING_IPV6_ADDRESS = 3
|
||||
};
|
||||
|
||||
CertificateOfOwnership() :
|
||||
_networkId(0),
|
||||
_ts(0),
|
||||
_id(0),
|
||||
_thingCount(0)
|
||||
{
|
||||
}
|
||||
|
||||
CertificateOfOwnership(const uint64_t nwid,const uint64_t ts,const Address &issuedTo,const uint32_t id) :
|
||||
_networkId(nwid),
|
||||
_ts(ts),
|
||||
_flags(0),
|
||||
_id(id),
|
||||
_thingCount(0),
|
||||
_issuedTo(issuedTo)
|
||||
{
|
||||
}
|
||||
|
||||
inline uint64_t networkId() const { return _networkId; }
|
||||
inline uint64_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;
|
||||
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;
|
||||
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
|
||||
* @return 0 == OK, 1 == waiting for WHOIS, -1 == BAD signature
|
||||
*/
|
||||
int verify(const RuntimeEnvironment *RR) 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++];
|
||||
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 std::runtime_error("invalid signature length");
|
||||
p += 2;
|
||||
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 std::runtime_error("extended field 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;
|
||||
uint64_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
|
||||
176
zto/node/CertificateOfRepresentation.hpp
Normal file
176
zto/node/CertificateOfRepresentation.hpp
Normal file
@@ -0,0 +1,176 @@
|
||||
/*
|
||||
* 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_CERTIFICATEOFREPRESENTATION_HPP
|
||||
#define ZT_CERTIFICATEOFREPRESENTATION_HPP
|
||||
|
||||
#include "Constants.hpp"
|
||||
#include "Address.hpp"
|
||||
#include "C25519.hpp"
|
||||
#include "Identity.hpp"
|
||||
#include "Buffer.hpp"
|
||||
|
||||
/**
|
||||
* Maximum number of addresses allowed in a COR
|
||||
*/
|
||||
#define ZT_CERTIFICATEOFREPRESENTATION_MAX_ADDRESSES ZT_MAX_UPSTREAMS
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
/**
|
||||
* A signed enumeration of a node's roots (planet and moons)
|
||||
*
|
||||
* This is sent as part of HELLO and attests to which roots a node trusts
|
||||
* to represent it on the network. Federated roots (moons) can send these
|
||||
* further upstream to tell global roots which nodes they represent, making
|
||||
* them reachable via federated roots if they are not reachable directly.
|
||||
*
|
||||
* As of 1.2.0 this is sent but not used. Right now nodes still always
|
||||
* announce to planetary roots no matter what. In the future this can be
|
||||
* used to implement even better fault tolerance for federation for the
|
||||
* no roots are reachable case as well as a "privacy mode" where federated
|
||||
* roots can shield nodes entirely and p2p connectivity behind them can
|
||||
* be disabled. This will be desirable for a number of use cases.
|
||||
*/
|
||||
class CertificateOfRepresentation
|
||||
{
|
||||
public:
|
||||
CertificateOfRepresentation()
|
||||
{
|
||||
memset(this,0,sizeof(CertificateOfRepresentation));
|
||||
}
|
||||
|
||||
inline uint64_t timestamp() const { return _timestamp; }
|
||||
inline const Address &representative(const unsigned int i) const { return _reps[i]; }
|
||||
inline unsigned int repCount() const { return _repCount; }
|
||||
|
||||
inline void clear()
|
||||
{
|
||||
memset(this,0,sizeof(CertificateOfRepresentation));
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a representative if space remains
|
||||
*
|
||||
* @param r Representative to add
|
||||
* @return True if representative was added
|
||||
*/
|
||||
inline bool addRepresentative(const Address &r)
|
||||
{
|
||||
if (_repCount < ZT_CERTIFICATEOFREPRESENTATION_MAX_ADDRESSES) {
|
||||
_reps[_repCount++] = r;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sign this COR with my identity
|
||||
*
|
||||
* @param myIdentity This node's identity
|
||||
* @param ts COR timestamp for establishing new vs. old
|
||||
*/
|
||||
inline void sign(const Identity &myIdentity,const uint64_t ts)
|
||||
{
|
||||
_timestamp = ts;
|
||||
Buffer<sizeof(CertificateOfRepresentation) + 32> tmp;
|
||||
this->serialize(tmp,true);
|
||||
_signature = myIdentity.sign(tmp.data(),tmp.size());
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify this COR's signature
|
||||
*
|
||||
* @param senderIdentity Identity of sender of COR
|
||||
* @return True if COR is valid
|
||||
*/
|
||||
inline bool verify(const Identity &senderIdentity)
|
||||
{
|
||||
try {
|
||||
Buffer<sizeof(CertificateOfRepresentation) + 32> tmp;
|
||||
this->serialize(tmp,true);
|
||||
return senderIdentity.verify(tmp.data(),tmp.size(),_signature.data,ZT_C25519_SIGNATURE_LEN);
|
||||
} catch ( ... ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
template<unsigned int C>
|
||||
inline void serialize(Buffer<C> &b,const bool forSign = false) const
|
||||
{
|
||||
if (forSign) b.append((uint64_t)0x7f7f7f7f7f7f7f7fULL);
|
||||
|
||||
b.append((uint64_t)_timestamp);
|
||||
b.append((uint16_t)_repCount);
|
||||
for(unsigned int i=0;i<_repCount;++i)
|
||||
_reps[i].appendTo(b);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
b.append((uint16_t)0); // size of any 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)
|
||||
{
|
||||
clear();
|
||||
|
||||
unsigned int p = startAt;
|
||||
|
||||
_timestamp = b.template at<uint64_t>(p); p += 8;
|
||||
const unsigned int rc = b.template at<uint16_t>(p); p += 2;
|
||||
for(unsigned int i=0;i<rc;++i) {
|
||||
if (i < ZT_CERTIFICATEOFREPRESENTATION_MAX_ADDRESSES)
|
||||
_reps[i].setTo(b.field(p,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH);
|
||||
p += ZT_ADDRESS_LENGTH;
|
||||
}
|
||||
_repCount = (rc > ZT_CERTIFICATEOFREPRESENTATION_MAX_ADDRESSES) ? ZT_CERTIFICATEOFREPRESENTATION_MAX_ADDRESSES : rc;
|
||||
|
||||
if (b[p++] == 1) {
|
||||
if (b.template at<uint16_t>(p) == ZT_C25519_SIGNATURE_LEN) {
|
||||
p += 2;
|
||||
memcpy(_signature.data,b.field(p,ZT_C25519_SIGNATURE_LEN),ZT_C25519_SIGNATURE_LEN);
|
||||
p += ZT_C25519_SIGNATURE_LEN;
|
||||
} else throw std::runtime_error("invalid signature");
|
||||
} else {
|
||||
p += 2 + b.template at<uint16_t>(p);
|
||||
}
|
||||
|
||||
p += 2 + b.template at<uint16_t>(p);
|
||||
if (p > b.size())
|
||||
throw std::runtime_error("extended field overflow");
|
||||
|
||||
return (p - startAt);
|
||||
}
|
||||
|
||||
private:
|
||||
uint64_t _timestamp;
|
||||
Address _reps[ZT_CERTIFICATEOFREPRESENTATION_MAX_ADDRESSES];
|
||||
unsigned int _repCount;
|
||||
C25519::Signature _signature;
|
||||
};
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
||||
#endif
|
||||
1034
zto/node/Cluster.cpp
Normal file
1034
zto/node/Cluster.cpp
Normal file
File diff suppressed because it is too large
Load Diff
455
zto/node/Cluster.hpp
Normal file
455
zto/node/Cluster.hpp
Normal file
@@ -0,0 +1,455 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
/**
|
||||
* We won't send WANT_PEER to other members more than every (ms) per recipient
|
||||
*/
|
||||
#define ZT_CLUSTER_WANT_PEER_EVERY 1000
|
||||
|
||||
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:
|
||||
* <[...] network config chunk>
|
||||
*
|
||||
* This is used by clusters to avoid every member having to query
|
||||
* for the same netconf for networks all members belong to.
|
||||
*
|
||||
* The first field of a network config chunk is the network ID,
|
||||
* so this can be checked to look up the network on receipt.
|
||||
*/
|
||||
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);
|
||||
|
||||
/**
|
||||
* Broadcast a network config chunk to other members of cluster
|
||||
*
|
||||
* @param chunk Chunk data
|
||||
* @param len Length of chunk
|
||||
*/
|
||||
void broadcastNetworkConfigChunk(const void *chunk,unsigned int len);
|
||||
|
||||
/**
|
||||
* If the cluster has this peer, prepare the packet to send via cluster
|
||||
*
|
||||
* Note that outp is only armored (or modified at all) if the return value is a member ID.
|
||||
*
|
||||
* @param toPeerAddress Value of outp.destination(), simply to save additional lookup
|
||||
* @param ts Result: set to time of last HAVE_PEER from the cluster
|
||||
* @param peerSecret Result: Buffer to fill with peer secret on valid return value, must be at least ZT_PEER_SECRET_KEY_LENGTH bytes
|
||||
* @return -1 if cluster does not know this peer, or a member ID to pass to sendViaCluster()
|
||||
*/
|
||||
int checkSendViaCluster(const Address &toPeerAddress,uint64_t &mostRecentTs,void *peerSecret);
|
||||
|
||||
/**
|
||||
* Send data via cluster front plane (packet head or fragment)
|
||||
*
|
||||
* @param haveMemberId Member ID that has this peer as returned by prepSendviaCluster()
|
||||
* @param toPeerAddress Destination peer address
|
||||
* @param data Packet or packet fragment data
|
||||
* @param len Length of packet or fragment
|
||||
* @return True if packet was sent (and outp was modified via armoring)
|
||||
*/
|
||||
bool sendViaCluster(int haveMemberId,const Address &toPeerAddress,const void *data,unsigned int len);
|
||||
|
||||
/**
|
||||
* Relay a packet via the cluster
|
||||
*
|
||||
* 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 relayViaCluster(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);
|
||||
|
||||
/**
|
||||
* @param ip Address to check
|
||||
* @return True if this is a cluster frontplane address (excluding our addresses)
|
||||
*/
|
||||
bool isClusterPeerFrontplane(const InetAddress &ip) const;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
struct _RemotePeer
|
||||
{
|
||||
_RemotePeer() : lastHavePeerReceived(0),lastSentWantPeer(0) {}
|
||||
~_RemotePeer() { Utils::burn(key,ZT_PEER_SECRET_KEY_LENGTH); }
|
||||
uint64_t lastHavePeerReceived;
|
||||
uint64_t lastSentWantPeer;
|
||||
uint8_t key[ZT_PEER_SECRET_KEY_LENGTH]; // secret key from identity agreement
|
||||
};
|
||||
std::map< std::pair<Address,unsigned int>,_RemotePeer > _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
|
||||
457
zto/node/Constants.hpp
Normal file
457
zto/node/Constants.hpp
Normal file
@@ -0,0 +1,457 @@
|
||||
/*
|
||||
* 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_CONSTANTS_HPP
|
||||
#define ZT_CONSTANTS_HPP
|
||||
|
||||
#include "../include/ZeroTierOne.h"
|
||||
|
||||
//
|
||||
// This include file also auto-detects and canonicalizes some environment
|
||||
// information defines:
|
||||
//
|
||||
// __LINUX__
|
||||
// __APPLE__
|
||||
// __BSD__ (OSX also defines this)
|
||||
// __UNIX_LIKE__ (Linux, BSD, etc.)
|
||||
// __WINDOWS__
|
||||
//
|
||||
// Also makes sure __BYTE_ORDER is defined reasonably.
|
||||
//
|
||||
|
||||
// Hack: make sure __GCC__ is defined on old GCC compilers
|
||||
#ifndef __GCC__
|
||||
#if defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_1) || defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_2) || defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4)
|
||||
#define __GCC__
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(__linux__) || defined(linux) || defined(__LINUX__) || defined(__linux)
|
||||
#ifndef __LINUX__
|
||||
#define __LINUX__
|
||||
#endif
|
||||
#ifndef __UNIX_LIKE__
|
||||
#define __UNIX_LIKE__
|
||||
#endif
|
||||
#include <endian.h>
|
||||
#endif
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include <TargetConditionals.h>
|
||||
#ifndef __UNIX_LIKE__
|
||||
#define __UNIX_LIKE__
|
||||
#endif
|
||||
#ifndef __BSD__
|
||||
#define __BSD__
|
||||
#endif
|
||||
#include <machine/endian.h>
|
||||
#endif
|
||||
|
||||
// Defined this macro to disable "type punning" on a number of targets that
|
||||
// have issues with unaligned memory access.
|
||||
#if defined(__arm__) || defined(__ARMEL__) || (defined(__APPLE__) && ( (defined(TARGET_OS_IPHONE) && (TARGET_OS_IPHONE != 0)) || (defined(TARGET_OS_WATCH) && (TARGET_OS_WATCH != 0)) || (defined(TARGET_IPHONE_SIMULATOR) && (TARGET_IPHONE_SIMULATOR != 0)) ) )
|
||||
#ifndef ZT_NO_TYPE_PUNNING
|
||||
#define ZT_NO_TYPE_PUNNING
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(__FreeBSD__) || defined(__OpenBSD__)
|
||||
#ifndef __UNIX_LIKE__
|
||||
#define __UNIX_LIKE__
|
||||
#endif
|
||||
#ifndef __BSD__
|
||||
#define __BSD__
|
||||
#endif
|
||||
#include <machine/endian.h>
|
||||
#ifndef __BYTE_ORDER
|
||||
#define __BYTE_ORDER _BYTE_ORDER
|
||||
#define __LITTLE_ENDIAN _LITTLE_ENDIAN
|
||||
#define __BIG_ENDIAN _BIG_ENDIAN
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
#ifndef __WINDOWS__
|
||||
#define __WINDOWS__
|
||||
#endif
|
||||
#ifndef NOMINMAX
|
||||
#define NOMINMAX
|
||||
#endif
|
||||
#pragma warning(disable : 4290)
|
||||
#pragma warning(disable : 4996)
|
||||
#pragma warning(disable : 4101)
|
||||
#undef __UNIX_LIKE__
|
||||
#undef __BSD__
|
||||
#define ZT_PATH_SEPARATOR '\\'
|
||||
#define ZT_PATH_SEPARATOR_S "\\"
|
||||
#define ZT_EOL_S "\r\n"
|
||||
#include <WinSock2.h>
|
||||
#include <Windows.h>
|
||||
#endif
|
||||
|
||||
// Assume little endian if not defined
|
||||
#if (defined(__APPLE__) || defined(__WINDOWS__)) && (!defined(__BYTE_ORDER))
|
||||
#undef __BYTE_ORDER
|
||||
#undef __LITTLE_ENDIAN
|
||||
#undef __BIG_ENDIAN
|
||||
#define __BIG_ENDIAN 4321
|
||||
#define __LITTLE_ENDIAN 1234
|
||||
#define __BYTE_ORDER 1234
|
||||
#endif
|
||||
|
||||
#ifdef __UNIX_LIKE__
|
||||
#define ZT_PATH_SEPARATOR '/'
|
||||
#define ZT_PATH_SEPARATOR_S "/"
|
||||
#define ZT_EOL_S "\n"
|
||||
#endif
|
||||
|
||||
#ifndef __BYTE_ORDER
|
||||
#include <endian.h>
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Length of a ZeroTier address in bytes
|
||||
*/
|
||||
#define ZT_ADDRESS_LENGTH 5
|
||||
|
||||
/**
|
||||
* Length of a hexadecimal ZeroTier address
|
||||
*/
|
||||
#define ZT_ADDRESS_LENGTH_HEX 10
|
||||
|
||||
/**
|
||||
* Addresses beginning with this byte are reserved for the joy of in-band signaling
|
||||
*/
|
||||
#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
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
#define ZT_MAX_PACKET_FRAGMENTS 4
|
||||
|
||||
/**
|
||||
* Size of RX queue
|
||||
*
|
||||
* This is about 2mb, and can be decreased for small devices. A queue smaller
|
||||
* than about 4 is probably going to cause a lot of lost packets.
|
||||
*/
|
||||
#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
|
||||
|
||||
/**
|
||||
* Minimum delay between timer task checks to prevent thrashing
|
||||
*/
|
||||
#define ZT_CORE_TIMER_TASK_GRANULARITY 500
|
||||
|
||||
/**
|
||||
* How often Topology::clean() and Network::clean() and similar are called, in ms
|
||||
*/
|
||||
#define ZT_HOUSEKEEPING_PERIOD 120000
|
||||
|
||||
/**
|
||||
* How long to remember peer records in RAM if they haven't been used
|
||||
*/
|
||||
#define ZT_PEER_IN_MEMORY_EXPIRATION 600000
|
||||
|
||||
/**
|
||||
* 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 4
|
||||
|
||||
/**
|
||||
* Transmit queue entry timeout
|
||||
*/
|
||||
#define ZT_TRANSMIT_QUEUE_TIMEOUT (ZT_WHOIS_RETRY_DELAY * (ZT_MAX_WHOIS_RETRIES + 1))
|
||||
|
||||
/**
|
||||
* Receive queue entry timeout
|
||||
*/
|
||||
#define ZT_RECEIVE_QUEUE_TIMEOUT (ZT_WHOIS_RETRY_DELAY * (ZT_MAX_WHOIS_RETRIES + 1))
|
||||
|
||||
/**
|
||||
* Maximum latency to allow for OK(HELLO) before packet is discarded
|
||||
*/
|
||||
#define ZT_HELLO_MAX_ALLOWABLE_LATENCY 60000
|
||||
|
||||
/**
|
||||
* Maximum number of ZT hops allowed (this is not IP hops/TTL)
|
||||
*
|
||||
* The protocol allows up to 7, but we limit it to something smaller.
|
||||
*/
|
||||
#define ZT_RELAY_MAX_HOPS 3
|
||||
|
||||
/**
|
||||
* Maximum number of upstreams to use (far more than we should ever need)
|
||||
*/
|
||||
#define ZT_MAX_UPSTREAMS 64
|
||||
|
||||
/**
|
||||
* Expire time for multicast 'likes' and indirect multicast memberships in ms
|
||||
*/
|
||||
#define ZT_MULTICAST_LIKE_EXPIRE 600000
|
||||
|
||||
/**
|
||||
* Period for multicast LIKE announcements
|
||||
*/
|
||||
#define ZT_MULTICAST_ANNOUNCE_PERIOD 120000
|
||||
|
||||
/**
|
||||
* Delay between explicit MULTICAST_GATHER requests for a given multicast channel
|
||||
*/
|
||||
#define ZT_MULTICAST_EXPLICIT_GATHER_DELAY (ZT_MULTICAST_LIKE_EXPIRE / 10)
|
||||
|
||||
/**
|
||||
* Expiration for credentials presented for MULTICAST_LIKE or MULTICAST_GATHER (for non-network-members)
|
||||
*/
|
||||
#define ZT_MULTICAST_CREDENTIAL_EXPIRATON ZT_MULTICAST_LIKE_EXPIRE
|
||||
|
||||
/**
|
||||
* Timeout for outgoing multicasts
|
||||
*
|
||||
* This is how long we wait for explicit or implicit gather results.
|
||||
*/
|
||||
#define ZT_MULTICAST_TRANSMIT_TIMEOUT 5000
|
||||
|
||||
/**
|
||||
* Delay between checks of peer pings, etc., and also related housekeeping tasks
|
||||
*/
|
||||
#define ZT_PING_CHECK_INVERVAL 5000
|
||||
|
||||
/**
|
||||
* How frequently to send heartbeats over in-use paths
|
||||
*/
|
||||
#define ZT_PATH_HEARTBEAT_PERIOD 14000
|
||||
|
||||
/**
|
||||
* Paths are considered inactive if they have not received traffic in this long
|
||||
*/
|
||||
#define ZT_PATH_ALIVE_TIMEOUT 45000
|
||||
|
||||
/**
|
||||
* Minimum time between attempts to check dead paths to see if they can be re-awakened
|
||||
*/
|
||||
#define ZT_PATH_MIN_REACTIVATE_INTERVAL 2500
|
||||
|
||||
/**
|
||||
* Do not accept HELLOs over a given path more often than this
|
||||
*/
|
||||
#define ZT_PATH_HELLO_RATE_LIMIT 1000
|
||||
|
||||
/**
|
||||
* Delay between full-fledge pings of directly connected peers
|
||||
*/
|
||||
#define ZT_PEER_PING_PERIOD 60000
|
||||
|
||||
/**
|
||||
* Paths are considered expired if they have not produced a real packet in this long
|
||||
*/
|
||||
#define ZT_PEER_PATH_EXPIRATION ((ZT_PEER_PING_PERIOD * 4) + 3000)
|
||||
|
||||
/**
|
||||
* Send a full HELLO every this often (ms)
|
||||
*/
|
||||
#define ZT_PEER_SEND_FULL_HELLO_EVERY (ZT_PEER_PING_PERIOD * 2)
|
||||
|
||||
/**
|
||||
* How often to retry expired paths that we're still remembering
|
||||
*/
|
||||
#define ZT_PEER_EXPIRED_PATH_TRIAL_PERIOD (ZT_PEER_PING_PERIOD * 10)
|
||||
|
||||
/**
|
||||
* Timeout for overall peer activity (measured from last receive)
|
||||
*/
|
||||
#define ZT_PEER_ACTIVITY_TIMEOUT 500000
|
||||
|
||||
/**
|
||||
* General rate limit timeout for multiple packet types (HELLO, etc.)
|
||||
*/
|
||||
#define ZT_PEER_GENERAL_INBOUND_RATE_LIMIT 500
|
||||
|
||||
/**
|
||||
* General limit for max RTT for requests over the network
|
||||
*/
|
||||
#define ZT_GENERAL_RTT_LIMIT 5000
|
||||
|
||||
/**
|
||||
* Delay between requests for updated network autoconf information
|
||||
*
|
||||
* Don't lengthen this as it affects things like QoS / uptime monitoring
|
||||
* via ZeroTier Central. This is the heartbeat, basically.
|
||||
*/
|
||||
#define ZT_NETWORK_AUTOCONF_DELAY 60000
|
||||
|
||||
/**
|
||||
* Minimum interval between attempts by relays to unite peers
|
||||
*
|
||||
* When a relay gets a packet destined for another peer, it sends both peers
|
||||
* a RENDEZVOUS message no more than this often. This instructs the peers
|
||||
* to attempt NAT-t and gives each the other's corresponding IP:port pair.
|
||||
*/
|
||||
#define ZT_MIN_UNITE_INTERVAL 30000
|
||||
|
||||
/**
|
||||
* How often should peers try memorized or statically defined paths?
|
||||
*/
|
||||
#define ZT_TRY_MEMORIZED_PATH_INTERVAL 30000
|
||||
|
||||
/**
|
||||
* Sanity limit on maximum bridge routes
|
||||
*
|
||||
* If the number of bridge routes exceeds this, we cull routes from the
|
||||
* bridges with the most MACs behind them until it doesn't. This is a
|
||||
* sanity limit to prevent memory-filling DOS attacks, nothing more. No
|
||||
* physical LAN has anywhere even close to this many nodes. Note that this
|
||||
* does not limit the size of ZT virtual LANs, only bridge routing.
|
||||
*/
|
||||
#define ZT_MAX_BRIDGE_ROUTES 67108864
|
||||
|
||||
/**
|
||||
* If there is no known route, spam to up to this many active bridges
|
||||
*/
|
||||
#define ZT_MAX_BRIDGE_SPAM 32
|
||||
|
||||
/**
|
||||
* Interval between direct path pushes in milliseconds
|
||||
*/
|
||||
#define ZT_DIRECT_PATH_PUSH_INTERVAL 120000
|
||||
|
||||
/**
|
||||
* Time horizon for push direct paths cutoff
|
||||
*/
|
||||
#define ZT_PUSH_DIRECT_PATHS_CUTOFF_TIME 60000
|
||||
|
||||
/**
|
||||
* Maximum number of direct path pushes within cutoff time
|
||||
*
|
||||
* This limits response to PUSH_DIRECT_PATHS to CUTOFF_LIMIT responses
|
||||
* per CUTOFF_TIME milliseconds per peer to prevent this from being
|
||||
* useful for DOS amplification attacks.
|
||||
*/
|
||||
#define ZT_PUSH_DIRECT_PATHS_CUTOFF_LIMIT 5
|
||||
|
||||
/**
|
||||
* 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
|
||||
|
||||
/**
|
||||
* 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
|
||||
|
||||
/**
|
||||
* Desired buffer size for UDP sockets (used in service and osdep but defined here)
|
||||
*/
|
||||
#if (defined(__amd64) || defined(__amd64__) || defined(__x86_64) || defined(__x86_64__) || defined(__AMD64) || defined(__AMD64__))
|
||||
#define ZT_UDP_DESIRED_BUF_SIZE 1048576
|
||||
#else
|
||||
#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
|
||||
#define ZT_ETHERTYPE_RARP 0x8035
|
||||
#define ZT_ETHERTYPE_ATALK 0x809b
|
||||
#define ZT_ETHERTYPE_AARP 0x80f3
|
||||
#define ZT_ETHERTYPE_IPX_A 0x8137
|
||||
#define ZT_ETHERTYPE_IPX_B 0x8138
|
||||
#define ZT_ETHERTYPE_IPV6 0x86dd
|
||||
|
||||
#endif
|
||||
460
zto/node/Dictionary.hpp
Normal file
460
zto/node/Dictionary.hpp
Normal file
@@ -0,0 +1,460 @@
|
||||
/*
|
||||
* 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_DICTIONARY_HPP
|
||||
#define ZT_DICTIONARY_HPP
|
||||
|
||||
#include "Constants.hpp"
|
||||
#include "Utils.hpp"
|
||||
#include "Buffer.hpp"
|
||||
#include "Address.hpp"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
/**
|
||||
* A small (in code and data) packed key=value store
|
||||
*
|
||||
* This stores data in the form of a compact blob that is sort of human
|
||||
* readable (depending on whether you put binary data in it) and is backward
|
||||
* compatible with older versions. Binary data is escaped such that the
|
||||
* serialized form of a Dictionary is always a valid null-terminated C string.
|
||||
*
|
||||
* Keys are restricted: no binary data, no CR/LF, and no equals (=). If a key
|
||||
* contains these characters it may not be retrievable. This is not checked.
|
||||
*
|
||||
* Lookup is via linear search and will be slow with a lot of keys. It's
|
||||
* designed for small things.
|
||||
*
|
||||
* There is code to test and fuzz this in selftest.cpp. Fuzzing a blob of
|
||||
* pointer tricks like this is important after any modifications.
|
||||
*
|
||||
* This is used for network configurations and for saving some things on disk
|
||||
* in the ZeroTier One service code.
|
||||
*
|
||||
* @tparam C Dictionary max capacity in bytes
|
||||
*/
|
||||
template<unsigned int C>
|
||||
class Dictionary
|
||||
{
|
||||
public:
|
||||
Dictionary()
|
||||
{
|
||||
_d[0] = (char)0;
|
||||
}
|
||||
|
||||
Dictionary(const char *s)
|
||||
{
|
||||
Utils::scopy(_d,sizeof(_d),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);
|
||||
}
|
||||
|
||||
inline Dictionary &operator=(const Dictionary &d)
|
||||
{
|
||||
Utils::scopy(_d,sizeof(_d),d._d);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a dictionary from a C-string
|
||||
*
|
||||
* @param s Dictionary in string form
|
||||
* @return False if 's' was longer than our capacity
|
||||
*/
|
||||
inline bool load(const char *s)
|
||||
{
|
||||
return Utils::scopy(_d,sizeof(_d),s);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete all entries
|
||||
*/
|
||||
inline void clear()
|
||||
{
|
||||
_d[0] = (char)0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Size of dictionary in bytes not including terminating NULL
|
||||
*/
|
||||
inline unsigned int sizeBytes() const
|
||||
{
|
||||
for(unsigned int i=0;i<C;++i) {
|
||||
if (!_d[i])
|
||||
return i;
|
||||
}
|
||||
return C-1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an entry
|
||||
*
|
||||
* Note that to get binary values, dest[] should be at least one more than
|
||||
* the maximum size of the value being retrieved. That's because even if
|
||||
* the data is binary a terminating 0 is still appended to dest[] after it.
|
||||
*
|
||||
* If the key is not found, dest[0] is set to 0 to make dest[] an empty
|
||||
* C string in that case. The dest[] array will *never* be unterminated
|
||||
* after this call.
|
||||
*
|
||||
* Security note: if 'key' is ever directly based on anything that is not
|
||||
* a hard-code or internally-generated name, it must be checked to ensure
|
||||
* that the buffer is NULL-terminated since key[] does not take a secondary
|
||||
* size parameter. In NetworkConfig all keys are hard-coded strings so this
|
||||
* isn't a problem in the core.
|
||||
*
|
||||
* @param key Key to look up
|
||||
* @param dest Destination buffer
|
||||
* @param destlen Size of destination buffer
|
||||
* @return -1 if not found, or actual number of bytes stored in dest[] minus trailing 0
|
||||
*/
|
||||
inline int get(const char *key,char *dest,unsigned int destlen) const
|
||||
{
|
||||
const char *p = _d;
|
||||
const char *const eof = p + C;
|
||||
const char *k;
|
||||
bool esc;
|
||||
int j;
|
||||
|
||||
if (!destlen) // sanity check
|
||||
return -1;
|
||||
|
||||
while (*p) {
|
||||
k = key;
|
||||
while ((*k)&&(*p)) {
|
||||
if (*p != *k)
|
||||
break;
|
||||
++k;
|
||||
if (++p == eof) {
|
||||
dest[0] = (char)0;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if ((!*k)&&(*p == '=')) {
|
||||
j = 0;
|
||||
esc = false;
|
||||
++p;
|
||||
while ((*p != 0)&&(*p != '\r')&&(*p != '\n')) {
|
||||
if (esc) {
|
||||
esc = false;
|
||||
switch(*p) {
|
||||
case 'r': dest[j++] = '\r'; break;
|
||||
case 'n': dest[j++] = '\n'; break;
|
||||
case '0': dest[j++] = (char)0; break;
|
||||
case 'e': dest[j++] = '='; break;
|
||||
default: dest[j++] = *p; break;
|
||||
}
|
||||
if (j == (int)destlen) {
|
||||
dest[j-1] = (char)0;
|
||||
return j-1;
|
||||
}
|
||||
} else if (*p == '\\') {
|
||||
esc = true;
|
||||
} else {
|
||||
dest[j++] = *p;
|
||||
if (j == (int)destlen) {
|
||||
dest[j-1] = (char)0;
|
||||
return j-1;
|
||||
}
|
||||
}
|
||||
if (++p == eof) {
|
||||
dest[0] = (char)0;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
dest[j] = (char)0;
|
||||
return j;
|
||||
} else {
|
||||
while ((*p)&&(*p != '\r')&&(*p != '\n')) {
|
||||
if (++p == eof) {
|
||||
dest[0] = (char)0;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
if (*p) {
|
||||
if (++p == eof) {
|
||||
dest[0] = (char)0;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else break;
|
||||
}
|
||||
}
|
||||
|
||||
dest[0] = (char)0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the contents of a key into a buffer
|
||||
*
|
||||
* @param key Key to get
|
||||
* @param dest Destination buffer
|
||||
* @return True if key was found (if false, dest will be empty)
|
||||
* @tparam BC Buffer capacity (usually inferred)
|
||||
*/
|
||||
template<unsigned int BC>
|
||||
inline bool get(const char *key,Buffer<BC> &dest) const
|
||||
{
|
||||
const int r = this->get(key,const_cast<char *>(reinterpret_cast<const char *>(dest.data())),BC);
|
||||
if (r >= 0) {
|
||||
dest.setSize((unsigned int)r);
|
||||
return true;
|
||||
} else {
|
||||
dest.clear();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a boolean value
|
||||
*
|
||||
* @param key Key to look up
|
||||
* @param dfl Default value if not found in dictionary
|
||||
* @return Boolean value of key or 'dfl' if not found
|
||||
*/
|
||||
bool getB(const char *key,bool dfl = false) const
|
||||
{
|
||||
char tmp[4];
|
||||
if (this->get(key,tmp,sizeof(tmp)) >= 0)
|
||||
return ((*tmp == '1')||(*tmp == 't')||(*tmp == 'T'));
|
||||
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 uint64_t getUI(const char *key,uint64_t dfl = 0) const
|
||||
{
|
||||
char tmp[128];
|
||||
if (this->get(key,tmp,sizeof(tmp)) >= 1)
|
||||
return Utils::hexStrToU64(tmp);
|
||||
return dfl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new key=value pair
|
||||
*
|
||||
* If the key is already present this will append another, but the first
|
||||
* will always be returned by get(). This is not checked. If you want to
|
||||
* ensure a key is not present use erase() first.
|
||||
*
|
||||
* Use the vlen parameter to add binary values. Nulls will be escaped.
|
||||
*
|
||||
* @param key Key -- nulls, CR/LF, and equals (=) are illegal characters
|
||||
* @param value Value to set
|
||||
* @param vlen Length of value in bytes or -1 to treat value[] as a C-string and look for terminating 0
|
||||
* @return True if there was enough room to add this key=value pair
|
||||
*/
|
||||
inline bool add(const char *key,const char *value,int vlen = -1)
|
||||
{
|
||||
for(unsigned int i=0;i<C;++i) {
|
||||
if (!_d[i]) {
|
||||
unsigned int j = i;
|
||||
|
||||
if (j > 0) {
|
||||
_d[j++] = '\n';
|
||||
if (j == C) {
|
||||
_d[i] = (char)0;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const char *p = key;
|
||||
while (*p) {
|
||||
_d[j++] = *(p++);
|
||||
if (j == C) {
|
||||
_d[i] = (char)0;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
_d[j++] = '=';
|
||||
if (j == C) {
|
||||
_d[i] = (char)0;
|
||||
return false;
|
||||
}
|
||||
|
||||
p = value;
|
||||
int k = 0;
|
||||
while ( ((vlen < 0)&&(*p)) || (k < vlen) ) {
|
||||
switch(*p) {
|
||||
case 0:
|
||||
case '\r':
|
||||
case '\n':
|
||||
case '\\':
|
||||
case '=':
|
||||
_d[j++] = '\\';
|
||||
if (j == C) {
|
||||
_d[i] = (char)0;
|
||||
return false;
|
||||
}
|
||||
switch(*p) {
|
||||
case 0: _d[j++] = '0'; break;
|
||||
case '\r': _d[j++] = 'r'; break;
|
||||
case '\n': _d[j++] = 'n'; break;
|
||||
case '\\': _d[j++] = '\\'; break;
|
||||
case '=': _d[j++] = 'e'; break;
|
||||
}
|
||||
if (j == C) {
|
||||
_d[i] = (char)0;
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
_d[j++] = *p;
|
||||
if (j == C) {
|
||||
_d[i] = (char)0;
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
++p;
|
||||
++k;
|
||||
}
|
||||
|
||||
_d[j] = (char)0;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a boolean as a '1' or a '0'
|
||||
*/
|
||||
inline bool add(const char *key,bool value)
|
||||
{
|
||||
return this->add(key,(value) ? "1" : "0",1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a 64-bit integer (unsigned) as a hex value
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a 64-bit integer (unsigned) as a hex value
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a binary buffer's contents as a value
|
||||
*
|
||||
* @tparam BC Buffer capacity (usually inferred)
|
||||
*/
|
||||
template<unsigned int BC>
|
||||
inline bool add(const char *key,const Buffer<BC> &value)
|
||||
{
|
||||
return this->add(key,(const char *)value.data(),(int)value.size());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param key Key to check
|
||||
* @return True if key is present
|
||||
*/
|
||||
inline bool contains(const char *key) const
|
||||
{
|
||||
char tmp[2];
|
||||
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 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];
|
||||
};
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
||||
#endif
|
||||
415
zto/node/Hashtable.hpp
Normal file
415
zto/node/Hashtable.hpp
Normal file
@@ -0,0 +1,415 @@
|
||||
/*
|
||||
* 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_HASHTABLE_HPP
|
||||
#define ZT_HASHTABLE_HPP
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <stdexcept>
|
||||
#include <vector>
|
||||
#include <utility>
|
||||
#include <algorithm>
|
||||
|
||||
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
|
||||
{
|
||||
private:
|
||||
struct _Bucket
|
||||
{
|
||||
_Bucket(const K &k,const V &v) : k(k),v(v) {}
|
||||
_Bucket(const K &k) : k(k),v() {}
|
||||
_Bucket(const _Bucket &b) : k(b.k),v(b.v) {}
|
||||
inline _Bucket &operator=(const _Bucket &b) { k = b.k; v = b.v; return *this; }
|
||||
K k;
|
||||
V v;
|
||||
_Bucket *next; // must be set manually for each _Bucket
|
||||
};
|
||||
|
||||
public:
|
||||
/**
|
||||
* A simple forward iterator (different from STL)
|
||||
*
|
||||
* It's safe to erase the last key, but not others. Don't use set() since that
|
||||
* may rehash and invalidate the iterator. Note the erasing the key will destroy
|
||||
* the targets of the pointers returned by next().
|
||||
*/
|
||||
class Iterator
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @param ht Hash table to iterate over
|
||||
*/
|
||||
Iterator(Hashtable &ht) :
|
||||
_idx(0),
|
||||
_ht(&ht),
|
||||
_b(ht._t[0])
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @param kptr Pointer to set to point to next key
|
||||
* @param vptr Pointer to set to point to next value
|
||||
* @return True if kptr and vptr are set, false if no more entries
|
||||
*/
|
||||
inline bool next(K *&kptr,V *&vptr)
|
||||
{
|
||||
for(;;) {
|
||||
if (_b) {
|
||||
kptr = &(_b->k);
|
||||
vptr = &(_b->v);
|
||||
_b = _b->next;
|
||||
return true;
|
||||
}
|
||||
++_idx;
|
||||
if (_idx >= _ht->_bc)
|
||||
return false;
|
||||
_b = _ht->_t[_idx];
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
unsigned long _idx;
|
||||
Hashtable *_ht;
|
||||
_Bucket *_b;
|
||||
};
|
||||
friend class Hashtable::Iterator;
|
||||
|
||||
/**
|
||||
* @param bc Initial capacity in buckets (default: 64, must be nonzero)
|
||||
*/
|
||||
Hashtable(unsigned long bc = 64) :
|
||||
_t(reinterpret_cast<_Bucket **>(::malloc(sizeof(_Bucket *) * bc))),
|
||||
_bc(bc),
|
||||
_s(0)
|
||||
{
|
||||
if (!_t)
|
||||
throw std::bad_alloc();
|
||||
for(unsigned long i=0;i<bc;++i)
|
||||
_t[i] = (_Bucket *)0;
|
||||
}
|
||||
|
||||
Hashtable(const Hashtable<K,V> &ht) :
|
||||
_t(reinterpret_cast<_Bucket **>(::malloc(sizeof(_Bucket *) * ht._bc))),
|
||||
_bc(ht._bc),
|
||||
_s(ht._s)
|
||||
{
|
||||
if (!_t)
|
||||
throw std::bad_alloc();
|
||||
for(unsigned long i=0;i<_bc;++i)
|
||||
_t[i] = (_Bucket *)0;
|
||||
for(unsigned long i=0;i<_bc;++i) {
|
||||
const _Bucket *b = ht._t[i];
|
||||
while (b) {
|
||||
_Bucket *nb = new _Bucket(*b);
|
||||
nb->next = _t[i];
|
||||
_t[i] = nb;
|
||||
b = b->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
~Hashtable()
|
||||
{
|
||||
this->clear();
|
||||
::free(_t);
|
||||
}
|
||||
|
||||
inline Hashtable &operator=(const Hashtable<K,V> &ht)
|
||||
{
|
||||
this->clear();
|
||||
if (ht._s) {
|
||||
for(unsigned long i=0;i<ht._bc;++i) {
|
||||
const _Bucket *b = ht._t[i];
|
||||
while (b) {
|
||||
this->set(b->k,b->v);
|
||||
b = b->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Erase all entries
|
||||
*/
|
||||
inline void clear()
|
||||
{
|
||||
if (_s) {
|
||||
for(unsigned long i=0;i<_bc;++i) {
|
||||
_Bucket *b = _t[i];
|
||||
while (b) {
|
||||
_Bucket *const nb = b->next;
|
||||
delete b;
|
||||
b = nb;
|
||||
}
|
||||
_t[i] = (_Bucket *)0;
|
||||
}
|
||||
_s = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Vector of all keys
|
||||
*/
|
||||
inline typename std::vector<K> keys() const
|
||||
{
|
||||
typename std::vector<K> k;
|
||||
if (_s) {
|
||||
k.reserve(_s);
|
||||
for(unsigned long i=0;i<_bc;++i) {
|
||||
_Bucket *b = _t[i];
|
||||
while (b) {
|
||||
k.push_back(b->k);
|
||||
b = b->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
return k;
|
||||
}
|
||||
|
||||
/**
|
||||
* Append all keys (in unspecified order) to the supplied vector or list
|
||||
*
|
||||
* @param v Vector, list, or other compliant container
|
||||
* @tparam Type of V (generally inferred)
|
||||
*/
|
||||
template<typename C>
|
||||
inline void appendKeys(C &v) const
|
||||
{
|
||||
if (_s) {
|
||||
for(unsigned long i=0;i<_bc;++i) {
|
||||
_Bucket *b = _t[i];
|
||||
while (b) {
|
||||
v.push_back(b->k);
|
||||
b = b->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Vector of all entries (pairs of K,V)
|
||||
*/
|
||||
inline typename std::vector< std::pair<K,V> > entries() const
|
||||
{
|
||||
typename std::vector< std::pair<K,V> > k;
|
||||
if (_s) {
|
||||
k.reserve(_s);
|
||||
for(unsigned long i=0;i<_bc;++i) {
|
||||
_Bucket *b = _t[i];
|
||||
while (b) {
|
||||
k.push_back(std::pair<K,V>(b->k,b->v));
|
||||
b = b->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
return k;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param k Key
|
||||
* @return Pointer to value or NULL if not found
|
||||
*/
|
||||
inline V *get(const K &k)
|
||||
{
|
||||
_Bucket *b = _t[_hc(k) % _bc];
|
||||
while (b) {
|
||||
if (b->k == k)
|
||||
return &(b->v);
|
||||
b = b->next;
|
||||
}
|
||||
return (V *)0;
|
||||
}
|
||||
inline const V *get(const K &k) const { return const_cast<Hashtable *>(this)->get(k); }
|
||||
|
||||
/**
|
||||
* @param k Key to check
|
||||
* @return True if key is present
|
||||
*/
|
||||
inline bool contains(const K &k) const
|
||||
{
|
||||
_Bucket *b = _t[_hc(k) % _bc];
|
||||
while (b) {
|
||||
if (b->k == k)
|
||||
return true;
|
||||
b = b->next;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param k Key
|
||||
* @return True if value was present
|
||||
*/
|
||||
inline bool erase(const K &k)
|
||||
{
|
||||
const unsigned long bidx = _hc(k) % _bc;
|
||||
_Bucket *lastb = (_Bucket *)0;
|
||||
_Bucket *b = _t[bidx];
|
||||
while (b) {
|
||||
if (b->k == k) {
|
||||
if (lastb)
|
||||
lastb->next = b->next;
|
||||
else _t[bidx] = b->next;
|
||||
delete b;
|
||||
--_s;
|
||||
return true;
|
||||
}
|
||||
lastb = b;
|
||||
b = b->next;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param k Key
|
||||
* @param v Value
|
||||
* @return Reference to value in table
|
||||
*/
|
||||
inline V &set(const K &k,const V &v)
|
||||
{
|
||||
const unsigned long h = _hc(k);
|
||||
unsigned long bidx = h % _bc;
|
||||
|
||||
_Bucket *b = _t[bidx];
|
||||
while (b) {
|
||||
if (b->k == k) {
|
||||
b->v = v;
|
||||
return b->v;
|
||||
}
|
||||
b = b->next;
|
||||
}
|
||||
|
||||
if (_s >= _bc) {
|
||||
_grow();
|
||||
bidx = h % _bc;
|
||||
}
|
||||
|
||||
b = new _Bucket(k,v);
|
||||
b->next = _t[bidx];
|
||||
_t[bidx] = b;
|
||||
++_s;
|
||||
return b->v;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param k Key
|
||||
* @return Value, possibly newly created
|
||||
*/
|
||||
inline V &operator[](const K &k)
|
||||
{
|
||||
const unsigned long h = _hc(k);
|
||||
unsigned long bidx = h % _bc;
|
||||
|
||||
_Bucket *b = _t[bidx];
|
||||
while (b) {
|
||||
if (b->k == k)
|
||||
return b->v;
|
||||
b = b->next;
|
||||
}
|
||||
|
||||
if (_s >= _bc) {
|
||||
_grow();
|
||||
bidx = h % _bc;
|
||||
}
|
||||
|
||||
b = new _Bucket(k);
|
||||
b->next = _t[bidx];
|
||||
_t[bidx] = b;
|
||||
++_s;
|
||||
return b->v;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Number of entries
|
||||
*/
|
||||
inline unsigned long size() const throw() { return _s; }
|
||||
|
||||
/**
|
||||
* @return True if table is empty
|
||||
*/
|
||||
inline bool empty() const throw() { return (_s == 0); }
|
||||
|
||||
private:
|
||||
template<typename O>
|
||||
static inline unsigned long _hc(const O &obj)
|
||||
{
|
||||
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;
|
||||
}
|
||||
static inline unsigned long _hc(const uint32_t i)
|
||||
{
|
||||
return ((unsigned long)i * (unsigned long)0x9e3779b1);
|
||||
}
|
||||
static inline unsigned long _hc(const uint16_t i)
|
||||
{
|
||||
return ((unsigned long)i * (unsigned long)0x9e3779b1);
|
||||
}
|
||||
|
||||
inline void _grow()
|
||||
{
|
||||
const unsigned long nc = _bc * 2;
|
||||
_Bucket **nt = reinterpret_cast<_Bucket **>(::malloc(sizeof(_Bucket *) * nc));
|
||||
if (nt) {
|
||||
for(unsigned long i=0;i<nc;++i)
|
||||
nt[i] = (_Bucket *)0;
|
||||
for(unsigned long i=0;i<_bc;++i) {
|
||||
_Bucket *b = _t[i];
|
||||
while (b) {
|
||||
_Bucket *const nb = b->next;
|
||||
const unsigned long nidx = _hc(b->k) % nc;
|
||||
b->next = nt[nidx];
|
||||
nt[nidx] = b;
|
||||
b = nb;
|
||||
}
|
||||
}
|
||||
::free(_t);
|
||||
_t = nt;
|
||||
_bc = nc;
|
||||
}
|
||||
}
|
||||
|
||||
_Bucket **_t;
|
||||
unsigned long _bc;
|
||||
unsigned long _s;
|
||||
};
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
||||
#endif
|
||||
190
zto/node/Identity.cpp
Normal file
190
zto/node/Identity.cpp
Normal file
@@ -0,0 +1,190 @@
|
||||
/*
|
||||
* 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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "Constants.hpp"
|
||||
#include "Identity.hpp"
|
||||
#include "SHA512.hpp"
|
||||
#include "Salsa20.hpp"
|
||||
#include "Utils.hpp"
|
||||
|
||||
// These can't be changed without a new identity type. They define the
|
||||
// parameters of the hashcash hashing/searching algorithm.
|
||||
|
||||
#define ZT_IDENTITY_GEN_HASHCASH_FIRST_BYTE_LESS_THAN 17
|
||||
#define ZT_IDENTITY_GEN_MEMORY 2097152
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
// A memory-hard composition of SHA-512 and Salsa20 for hashcash hashing
|
||||
static inline void _computeMemoryHardHash(const void *publicKey,unsigned int publicKeyBytes,void *digest,void *genmem)
|
||||
{
|
||||
// Digest publicKey[] to obtain initial digest
|
||||
SHA512::hash(digest,publicKey,publicKeyBytes);
|
||||
|
||||
// Initialize genmem[] using Salsa20 in a CBC-like configuration since
|
||||
// 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.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));
|
||||
*((uint64_t *)((char *)genmem + i + 8)) = *((uint64_t *)((char *)genmem + k + 8));
|
||||
*((uint64_t *)((char *)genmem + i + 16)) = *((uint64_t *)((char *)genmem + k + 16));
|
||||
*((uint64_t *)((char *)genmem + i + 24)) = *((uint64_t *)((char *)genmem + k + 24));
|
||||
*((uint64_t *)((char *)genmem + i + 32)) = *((uint64_t *)((char *)genmem + k + 32));
|
||||
*((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.crypt20((char *)genmem + i,(char *)genmem + i,64);
|
||||
}
|
||||
|
||||
// Render final digest using genmem as a lookup table
|
||||
for(unsigned long i=0;i<(ZT_IDENTITY_GEN_MEMORY / sizeof(uint64_t));) {
|
||||
unsigned long idx1 = (unsigned long)(Utils::ntoh(((uint64_t *)genmem)[i++]) % (64 / sizeof(uint64_t)));
|
||||
unsigned long idx2 = (unsigned long)(Utils::ntoh(((uint64_t *)genmem)[i++]) % (ZT_IDENTITY_GEN_MEMORY / sizeof(uint64_t)));
|
||||
uint64_t tmp = ((uint64_t *)genmem)[idx2];
|
||||
((uint64_t *)genmem)[idx2] = ((uint64_t *)digest)[idx1];
|
||||
((uint64_t *)digest)[idx1] = tmp;
|
||||
s20.crypt20(digest,digest,64);
|
||||
}
|
||||
}
|
||||
|
||||
// Hashcash generation halting condition -- halt when first byte is less than
|
||||
// threshold value.
|
||||
struct _Identity_generate_cond
|
||||
{
|
||||
_Identity_generate_cond() throw() {}
|
||||
_Identity_generate_cond(unsigned char *sb,char *gm) throw() : digest(sb),genmem(gm) {}
|
||||
inline bool operator()(const C25519::Pair &kp) const
|
||||
throw()
|
||||
{
|
||||
_computeMemoryHardHash(kp.pub.data,(unsigned int)kp.pub.size(),digest,genmem);
|
||||
return (digest[0] < ZT_IDENTITY_GEN_HASHCASH_FIRST_BYTE_LESS_THAN);
|
||||
}
|
||||
unsigned char *digest;
|
||||
char *genmem;
|
||||
};
|
||||
|
||||
void Identity::generate()
|
||||
{
|
||||
unsigned char digest[64];
|
||||
char *genmem = new char[ZT_IDENTITY_GEN_MEMORY];
|
||||
|
||||
C25519::Pair kp;
|
||||
do {
|
||||
kp = C25519::generateSatisfying(_Identity_generate_cond(digest,genmem));
|
||||
_address.setTo(digest + 59,ZT_ADDRESS_LENGTH); // last 5 bytes are address
|
||||
} while (_address.isReserved());
|
||||
|
||||
_publicKey = kp.pub;
|
||||
if (!_privateKey)
|
||||
_privateKey = new C25519::Private();
|
||||
*_privateKey = kp.priv;
|
||||
|
||||
delete [] genmem;
|
||||
}
|
||||
|
||||
bool Identity::locallyValidate() const
|
||||
{
|
||||
if (_address.isReserved())
|
||||
return false;
|
||||
|
||||
unsigned char digest[64];
|
||||
char *genmem = new char[ZT_IDENTITY_GEN_MEMORY];
|
||||
_computeMemoryHardHash(_publicKey.data,(unsigned int)_publicKey.size(),digest,genmem);
|
||||
delete [] genmem;
|
||||
|
||||
unsigned char addrb[5];
|
||||
_address.copyTo(addrb,5);
|
||||
|
||||
return (
|
||||
(digest[0] < ZT_IDENTITY_GEN_HASHCASH_FIRST_BYTE_LESS_THAN)&&
|
||||
(digest[59] == addrb[0])&&
|
||||
(digest[60] == addrb[1])&&
|
||||
(digest[61] == addrb[2])&&
|
||||
(digest[62] == addrb[3])&&
|
||||
(digest[63] == addrb[4]));
|
||||
}
|
||||
|
||||
std::string Identity::toString(bool includePrivate) const
|
||||
{
|
||||
std::string r;
|
||||
|
||||
r.append(_address.toString());
|
||||
r.append(":0:"); // 0 == ZT_OBJECT_TYPE_IDENTITY
|
||||
r.append(Utils::hex(_publicKey.data,(unsigned int)_publicKey.size()));
|
||||
if ((_privateKey)&&(includePrivate)) {
|
||||
r.push_back(':');
|
||||
r.append(Utils::hex(_privateKey->data,(unsigned int)_privateKey->size()));
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
bool Identity::fromString(const char *str)
|
||||
{
|
||||
if (!str)
|
||||
return false;
|
||||
|
||||
char *saveptr = (char *)0;
|
||||
char tmp[1024];
|
||||
if (!Utils::scopy(tmp,sizeof(tmp),str))
|
||||
return false;
|
||||
|
||||
delete _privateKey;
|
||||
_privateKey = (C25519::Private *)0;
|
||||
|
||||
int fno = 0;
|
||||
for(char *f=Utils::stok(tmp,":",&saveptr);(f);f=Utils::stok((char *)0,":",&saveptr)) {
|
||||
switch(fno++) {
|
||||
case 0:
|
||||
_address = Address(Utils::hexStrToU64(f));
|
||||
if (_address.isReserved())
|
||||
return false;
|
||||
break;
|
||||
case 1:
|
||||
if ((f[0] != '0')||(f[1]))
|
||||
return false;
|
||||
break;
|
||||
case 2:
|
||||
if (Utils::unhex(f,_publicKey.data,(unsigned int)_publicKey.size()) != _publicKey.size())
|
||||
return false;
|
||||
break;
|
||||
case 3:
|
||||
_privateKey = new C25519::Private();
|
||||
if (Utils::unhex(f,_privateKey->data,(unsigned int)_privateKey->size()) != _privateKey->size())
|
||||
return false;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (fno < 3)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace ZeroTier
|
||||
323
zto/node/Identity.hpp
Normal file
323
zto/node/Identity.hpp
Normal file
@@ -0,0 +1,323 @@
|
||||
/*
|
||||
* 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_IDENTITY_HPP
|
||||
#define ZT_IDENTITY_HPP
|
||||
|
||||
#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"
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
/**
|
||||
* A ZeroTier identity
|
||||
*
|
||||
* An identity consists of a public key, a 40-bit ZeroTier address computed
|
||||
* from that key in a collision-resistant fashion, and a self-signature.
|
||||
*
|
||||
* The address derivation algorithm makes it computationally very expensive to
|
||||
* search for a different public key that duplicates an existing address. (See
|
||||
* code for deriveAddress() for this algorithm.)
|
||||
*/
|
||||
class Identity
|
||||
{
|
||||
public:
|
||||
Identity() :
|
||||
_privateKey((C25519::Private *)0)
|
||||
{
|
||||
}
|
||||
|
||||
Identity(const Identity &id) :
|
||||
_address(id._address),
|
||||
_publicKey(id._publicKey),
|
||||
_privateKey((id._privateKey) ? new C25519::Private(*(id._privateKey)) : (C25519::Private *)0)
|
||||
{
|
||||
}
|
||||
|
||||
Identity(const char *str)
|
||||
throw(std::invalid_argument) :
|
||||
_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);
|
||||
}
|
||||
|
||||
template<unsigned int C>
|
||||
Identity(const Buffer<C> &b,unsigned int startAt = 0) :
|
||||
_privateKey((C25519::Private *)0)
|
||||
{
|
||||
deserialize(b,startAt);
|
||||
}
|
||||
|
||||
~Identity()
|
||||
{
|
||||
delete _privateKey;
|
||||
}
|
||||
|
||||
inline Identity &operator=(const Identity &id)
|
||||
{
|
||||
_address = id._address;
|
||||
_publicKey = id._publicKey;
|
||||
if (id._privateKey) {
|
||||
if (!_privateKey)
|
||||
_privateKey = new C25519::Private();
|
||||
*_privateKey = *(id._privateKey);
|
||||
} else {
|
||||
delete _privateKey;
|
||||
_privateKey = (C25519::Private *)0;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a new identity (address, key pair)
|
||||
*
|
||||
* This is a time consuming operation.
|
||||
*/
|
||||
void generate();
|
||||
|
||||
/**
|
||||
* Check the validity of this identity's pairing of key to address
|
||||
*
|
||||
* @return True if validation check passes
|
||||
*/
|
||||
bool locallyValidate() const;
|
||||
|
||||
/**
|
||||
* @return True if this identity contains a private key
|
||||
*/
|
||||
inline bool hasPrivate() const throw() { return (_privateKey != (C25519::Private *)0); }
|
||||
|
||||
/**
|
||||
* Compute the SHA512 hash of our private key (if we have one)
|
||||
*
|
||||
* @param sha Buffer to receive SHA512 (MUST be ZT_SHA512_DIGEST_LEN (64) bytes in length)
|
||||
* @return True on success, false if no private key
|
||||
*/
|
||||
inline bool sha512PrivateKey(void *sha) const
|
||||
{
|
||||
if (_privateKey) {
|
||||
SHA512::hash(sha,_privateKey->data,ZT_C25519_PRIVATE_KEY_LEN);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sign a message with this identity (private key required)
|
||||
*
|
||||
* @param data Data to sign
|
||||
* @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");
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify a message signature against this identity
|
||||
*
|
||||
* @param data Data to check
|
||||
* @param len Length of data
|
||||
* @param signature Signature bytes
|
||||
* @param siglen Length of signature in bytes
|
||||
* @return True if signature validates and data integrity checks
|
||||
*/
|
||||
inline bool verify(const void *data,unsigned int len,const void *signature,unsigned int siglen) const
|
||||
{
|
||||
if (siglen != ZT_C25519_SIGNATURE_LEN)
|
||||
return false;
|
||||
return C25519::verify(_publicKey,data,len,signature);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify a message signature against this identity
|
||||
*
|
||||
* @param data Data to check
|
||||
* @param len Length of data
|
||||
* @param signature Signature
|
||||
* @return True if signature validates and data integrity checks
|
||||
*/
|
||||
inline bool verify(const void *data,unsigned int len,const C25519::Signature &signature) const
|
||||
{
|
||||
return C25519::verify(_publicKey,data,len,signature);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shortcut method to perform key agreement with another identity
|
||||
*
|
||||
* This identity must have a private key. (Check hasPrivate())
|
||||
*
|
||||
* @param id Identity to agree with
|
||||
* @param key Result parameter to fill with key bytes
|
||||
* @param klen Length of key in bytes
|
||||
* @return Was agreement successful?
|
||||
*/
|
||||
inline bool agree(const Identity &id,void *key,unsigned int klen) const
|
||||
{
|
||||
if (_privateKey) {
|
||||
C25519::agree(*_privateKey,id._publicKey,key,klen);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return This identity's address
|
||||
*/
|
||||
inline const Address &address() const throw() { return _address; }
|
||||
|
||||
/**
|
||||
* Serialize this identity (binary)
|
||||
*
|
||||
* @param b Destination buffer to append to
|
||||
* @param includePrivate If true, include private key component (if present) (default: false)
|
||||
* @throws std::out_of_range Buffer too small
|
||||
*/
|
||||
template<unsigned int C>
|
||||
inline void serialize(Buffer<C> &b,bool includePrivate = false) const
|
||||
{
|
||||
_address.appendTo(b);
|
||||
b.append((uint8_t)0); // C25519/Ed25519 identity type
|
||||
b.append(_publicKey.data,(unsigned int)_publicKey.size());
|
||||
if ((_privateKey)&&(includePrivate)) {
|
||||
b.append((unsigned char)_privateKey->size());
|
||||
b.append(_privateKey->data,(unsigned int)_privateKey->size());
|
||||
} else b.append((unsigned char)0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deserialize a binary serialized identity
|
||||
*
|
||||
* If an exception is thrown, the Identity object is left in an undefined
|
||||
* state and should not be used.
|
||||
*
|
||||
* @param b Buffer containing serialized data
|
||||
* @param startAt Index within buffer of serialized data (default: 0)
|
||||
* @return Length of serialized data read from buffer
|
||||
* @throws std::out_of_range Serialized data invalid
|
||||
* @throws std::invalid_argument Serialized data invalid
|
||||
*/
|
||||
template<unsigned int C>
|
||||
inline unsigned int deserialize(const Buffer<C> &b,unsigned int startAt = 0)
|
||||
{
|
||||
delete _privateKey;
|
||||
_privateKey = (C25519::Private *)0;
|
||||
|
||||
unsigned int p = startAt;
|
||||
|
||||
_address.setTo(b.field(p,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH);
|
||||
p += ZT_ADDRESS_LENGTH;
|
||||
|
||||
if (b[p++] != 0)
|
||||
throw std::invalid_argument("unsupported identity type");
|
||||
|
||||
memcpy(_publicKey.data,b.field(p,(unsigned int)_publicKey.size()),(unsigned int)_publicKey.size());
|
||||
p += (unsigned int)_publicKey.size();
|
||||
|
||||
unsigned int privateKeyLength = (unsigned int)b[p++];
|
||||
if (privateKeyLength) {
|
||||
if (privateKeyLength != ZT_C25519_PRIVATE_KEY_LEN)
|
||||
throw std::invalid_argument("invalid private key");
|
||||
_privateKey = new C25519::Private();
|
||||
memcpy(_privateKey->data,b.field(p,ZT_C25519_PRIVATE_KEY_LEN),ZT_C25519_PRIVATE_KEY_LEN);
|
||||
p += ZT_C25519_PRIVATE_KEY_LEN;
|
||||
}
|
||||
|
||||
return (p - startAt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize to a more human-friendly string
|
||||
*
|
||||
* @param includePrivate If true, include private key (if it exists)
|
||||
* @return ASCII string representation of identity
|
||||
*/
|
||||
std::string toString(bool includePrivate) const;
|
||||
|
||||
/**
|
||||
* Deserialize a human-friendly string
|
||||
*
|
||||
* Note: validation is for the format only. The locallyValidate() method
|
||||
* must be used to check signature and address/key correspondence.
|
||||
*
|
||||
* @param str String to deserialize
|
||||
* @return True if deserialization appears successful
|
||||
*/
|
||||
bool fromString(const char *str);
|
||||
inline bool fromString(const std::string &str) { return fromString(str.c_str()); }
|
||||
|
||||
/**
|
||||
* @return C25519 public key
|
||||
*/
|
||||
inline const C25519::Public &publicKey() const { return _publicKey; }
|
||||
|
||||
/**
|
||||
* @return C25519 key pair (only returns valid pair if private key is present in this Identity object)
|
||||
*/
|
||||
inline const C25519::Pair privateKeyPair() const
|
||||
{
|
||||
C25519::Pair pair;
|
||||
pair.pub = _publicKey;
|
||||
if (_privateKey)
|
||||
pair.priv = *_privateKey;
|
||||
else memset(pair.priv.data,0,ZT_C25519_PRIVATE_KEY_LEN);
|
||||
return pair;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return True if this identity contains something
|
||||
*/
|
||||
inline operator bool() const throw() { return (_address); }
|
||||
|
||||
inline bool operator==(const Identity &id) const throw() { return ((_address == id._address)&&(_publicKey == id._publicKey)); }
|
||||
inline bool operator<(const Identity &id) const throw() { return ((_address < id._address)||((_address == id._address)&&(_publicKey < id._publicKey))); }
|
||||
inline bool operator!=(const Identity &id) const throw() { return !(*this == id); }
|
||||
inline bool operator>(const Identity &id) const throw() { return (id < *this); }
|
||||
inline bool operator<=(const Identity &id) const throw() { return !(id < *this); }
|
||||
inline bool operator>=(const Identity &id) const throw() { return !(*this < id); }
|
||||
|
||||
private:
|
||||
Address _address;
|
||||
C25519::Public _publicKey;
|
||||
C25519::Private *_privateKey;
|
||||
};
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
||||
#endif
|
||||
1465
zto/node/IncomingPacket.cpp
Normal file
1465
zto/node/IncomingPacket.cpp
Normal file
File diff suppressed because it is too large
Load Diff
144
zto/node/IncomingPacket.hpp
Normal file
144
zto/node/IncomingPacket.hpp
Normal file
@@ -0,0 +1,144 @@
|
||||
/*
|
||||
* 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_INCOMINGPACKET_HPP
|
||||
#define ZT_INCOMINGPACKET_HPP
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
#include "Packet.hpp"
|
||||
#include "Path.hpp"
|
||||
#include "Utils.hpp"
|
||||
#include "MulticastGroup.hpp"
|
||||
#include "Peer.hpp"
|
||||
|
||||
/*
|
||||
* The big picture:
|
||||
*
|
||||
* tryDecode gets called for a given fully-assembled packet until it returns
|
||||
* true or the packet's time to live has been exceeded, in which case it is
|
||||
* discarded as failed decode. Any exception thrown by tryDecode also causes
|
||||
* the packet to be discarded.
|
||||
*
|
||||
* Thus a return of false from tryDecode() indicates that it should be called
|
||||
* again. Logic is very simple as to when, and it's in doAnythingWaitingForPeer
|
||||
* in Switch. This might be expanded to be more fine grained in the future.
|
||||
*
|
||||
* A return value of true indicates that the packet is done. tryDecode must
|
||||
* never be called again after that.
|
||||
*/
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
class RuntimeEnvironment;
|
||||
class Network;
|
||||
|
||||
/**
|
||||
* Subclass of packet that handles the decoding of it
|
||||
*/
|
||||
class IncomingPacket : public Packet
|
||||
{
|
||||
public:
|
||||
IncomingPacket() :
|
||||
Packet(),
|
||||
_receiveTime(0)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new packet-in-decode
|
||||
*
|
||||
* @param data Packet data
|
||||
* @param len Packet length
|
||||
* @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 SharedPtr<Path> &path,uint64_t now) :
|
||||
Packet(data,len),
|
||||
_receiveTime(now),
|
||||
_path(path)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Init packet-in-decode in place
|
||||
*
|
||||
* @param data Packet data
|
||||
* @param len Packet length
|
||||
* @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 SharedPtr<Path> &path,uint64_t now)
|
||||
{
|
||||
copyFrom(data,len);
|
||||
_receiveTime = now;
|
||||
_path = path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to decode this packet
|
||||
*
|
||||
* Note that this returns 'true' if processing is complete. This says nothing
|
||||
* 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.
|
||||
*
|
||||
* @param RR Runtime environment
|
||||
* @return True if decoding and processing is complete, false if caller should try again
|
||||
*/
|
||||
bool tryDecode(const RuntimeEnvironment *RR);
|
||||
|
||||
/**
|
||||
* @return Time of packet receipt / start of decode
|
||||
*/
|
||||
inline uint64_t receiveTime() const throw() { 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,const bool alreadyAuthenticated);
|
||||
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_CREDENTIALS(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
|
||||
bool _doNETWORK_CONFIG_REQUEST(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
|
||||
bool _doNETWORK_CONFIG(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 _doUSER_MESSAGE(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
|
||||
|
||||
void _sendErrorNeedCredentials(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer,const uint64_t nwid);
|
||||
|
||||
uint64_t _receiveTime;
|
||||
SharedPtr<Path> _path;
|
||||
};
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
||||
#endif
|
||||
473
zto/node/InetAddress.cpp
Normal file
473
zto/node/InetAddress.cpp
Normal file
@@ -0,0 +1,473 @@
|
||||
/*
|
||||
* 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 <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "Constants.hpp"
|
||||
#include "InetAddress.hpp"
|
||||
#include "Utils.hpp"
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
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) {
|
||||
|
||||
case AF_INET: {
|
||||
const uint32_t ip = Utils::ntoh((uint32_t)reinterpret_cast<const struct sockaddr_in *>(this)->sin_addr.s_addr);
|
||||
switch(ip >> 24) {
|
||||
case 0x00: return IP_SCOPE_NONE; // 0.0.0.0/8 (reserved, never used)
|
||||
case 0x06: return IP_SCOPE_PSEUDOPRIVATE; // 6.0.0.0/8 (US Army)
|
||||
case 0x0a: return IP_SCOPE_PRIVATE; // 10.0.0.0/8
|
||||
case 0x0b: return IP_SCOPE_PSEUDOPRIVATE; // 11.0.0.0/8 (US DoD)
|
||||
case 0x15: return IP_SCOPE_PSEUDOPRIVATE; // 21.0.0.0/8 (US DDN-RVN)
|
||||
case 0x16: return IP_SCOPE_PSEUDOPRIVATE; // 22.0.0.0/8 (US DISA)
|
||||
case 0x19: return IP_SCOPE_PSEUDOPRIVATE; // 25.0.0.0/8 (UK Ministry of Defense)
|
||||
case 0x1a: return IP_SCOPE_PSEUDOPRIVATE; // 26.0.0.0/8 (US DISA)
|
||||
case 0x1c: return IP_SCOPE_PSEUDOPRIVATE; // 28.0.0.0/8 (US DSI-North)
|
||||
case 0x1d: return IP_SCOPE_PSEUDOPRIVATE; // 29.0.0.0/8 (US DISA)
|
||||
case 0x1e: return IP_SCOPE_PSEUDOPRIVATE; // 30.0.0.0/8 (US DISA)
|
||||
case 0x2c: return IP_SCOPE_PSEUDOPRIVATE; // 44.0.0.0/8 (Amateur Radio)
|
||||
case 0x33: return IP_SCOPE_PSEUDOPRIVATE; // 51.0.0.0/8 (UK Department of Social Security)
|
||||
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
|
||||
break;
|
||||
case 0x7f: return IP_SCOPE_LOOPBACK; // 127.0.0.0/8
|
||||
case 0xa9:
|
||||
if ((ip & 0xffff0000) == 0xa9fe0000) return IP_SCOPE_LINK_LOCAL; // 169.254.0.0/16
|
||||
break;
|
||||
case 0xac:
|
||||
if ((ip & 0xfff00000) == 0xac100000) return IP_SCOPE_PRIVATE; // 172.16.0.0/12
|
||||
break;
|
||||
case 0xc0:
|
||||
if ((ip & 0xffff0000) == 0xc0a80000) return IP_SCOPE_PRIVATE; // 192.168.0.0/16
|
||||
break;
|
||||
case 0xff: return IP_SCOPE_NONE; // 255.0.0.0/8 (broadcast, or unused/unusable)
|
||||
}
|
||||
switch(ip >> 28) {
|
||||
case 0xe: return IP_SCOPE_MULTICAST; // 224.0.0.0/4
|
||||
case 0xf: return IP_SCOPE_PSEUDOPRIVATE; // 240.0.0.0/4 ("reserved," usually unusable)
|
||||
}
|
||||
return IP_SCOPE_GLOBAL;
|
||||
} break;
|
||||
|
||||
case AF_INET6: {
|
||||
const unsigned char *ip = reinterpret_cast<const unsigned char *>(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr);
|
||||
if ((ip[0] & 0xf0) == 0xf0) {
|
||||
if (ip[0] == 0xff) return IP_SCOPE_MULTICAST; // ff00::/8
|
||||
if ((ip[0] == 0xfe)&&((ip[1] & 0xc0) == 0x80)) {
|
||||
unsigned int k = 2;
|
||||
while ((!ip[k])&&(k < 15)) ++k;
|
||||
if ((k == 15)&&(ip[15] == 0x01))
|
||||
return IP_SCOPE_LOOPBACK; // fe80::1/128
|
||||
else return IP_SCOPE_LINK_LOCAL; // fe80::/10
|
||||
}
|
||||
if ((ip[0] & 0xfe) == 0xfc) return IP_SCOPE_PRIVATE; // fc00::/7
|
||||
}
|
||||
unsigned int k = 0;
|
||||
while ((!ip[k])&&(k < 15)) ++k;
|
||||
if (k == 15) { // all 0's except last byte
|
||||
if (ip[15] == 0x01) return IP_SCOPE_LOOPBACK; // ::1/128
|
||||
if (ip[15] == 0x00) return IP_SCOPE_NONE; // ::/128
|
||||
}
|
||||
return IP_SCOPE_GLOBAL;
|
||||
} break;
|
||||
|
||||
}
|
||||
|
||||
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 if (ip.find('.') != std::string::npos) {
|
||||
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);
|
||||
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);
|
||||
reinterpret_cast<struct sockaddr_in6 *>(this)->sin6_port = Utils::hton((uint16_t)port);
|
||||
}
|
||||
}
|
||||
|
||||
std::string InetAddress::toString() 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);
|
||||
}
|
||||
return std::string();
|
||||
}
|
||||
|
||||
std::string InetAddress::toIpString() 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);
|
||||
}
|
||||
return std::string();
|
||||
}
|
||||
|
||||
void InetAddress::fromString(const std::string &ipSlashPort)
|
||||
{
|
||||
const std::size_t slashAt = ipSlashPort.find('/');
|
||||
if (slashAt == std::string::npos) {
|
||||
set(ipSlashPort,0);
|
||||
} 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);
|
||||
}
|
||||
}
|
||||
|
||||
InetAddress InetAddress::netmask() const
|
||||
{
|
||||
InetAddress r(*this);
|
||||
switch(r.ss_family) {
|
||||
case AF_INET:
|
||||
reinterpret_cast<struct sockaddr_in *>(&r)->sin_addr.s_addr = Utils::hton((uint32_t)(0xffffffff << (32 - netmaskBits())));
|
||||
break;
|
||||
case AF_INET6: {
|
||||
uint64_t nm[2];
|
||||
const unsigned int bits = netmaskBits();
|
||||
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;
|
||||
}
|
||||
memcpy(reinterpret_cast<struct sockaddr_in6 *>(&r)->sin6_addr.s6_addr,nm,16);
|
||||
} break;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
InetAddress InetAddress::broadcast() const
|
||||
{
|
||||
if (ss_family == AF_INET) {
|
||||
InetAddress r(*this);
|
||||
reinterpret_cast<struct sockaddr_in *>(&r)->sin_addr.s_addr |= Utils::hton((uint32_t)(0xffffffff >> netmaskBits()));
|
||||
return r;
|
||||
}
|
||||
return InetAddress();
|
||||
}
|
||||
|
||||
InetAddress InetAddress::network() const
|
||||
{
|
||||
InetAddress r(*this);
|
||||
switch(r.ss_family) {
|
||||
case AF_INET:
|
||||
reinterpret_cast<struct sockaddr_in *>(&r)->sin_addr.s_addr &= Utils::hton((uint32_t)(0xffffffff << (32 - netmaskBits())));
|
||||
break;
|
||||
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);
|
||||
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);
|
||||
} break;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
bool InetAddress::containsAddress(const InetAddress &addr) const
|
||||
{
|
||||
if (addr.ss_family == ss_family) {
|
||||
switch(ss_family) {
|
||||
case AF_INET: {
|
||||
const unsigned int bits = netmaskBits();
|
||||
if (bits == 0)
|
||||
return true;
|
||||
return ( (Utils::ntoh((uint32_t)reinterpret_cast<const struct sockaddr_in *>(&addr)->sin_addr.s_addr) >> (32 - bits)) == (Utils::ntoh((uint32_t)reinterpret_cast<const struct sockaddr_in *>(this)->sin_addr.s_addr) >> (32 - bits)) );
|
||||
}
|
||||
case AF_INET6: {
|
||||
const InetAddress mask(netmask());
|
||||
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])
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool InetAddress::isNetwork() const
|
||||
throw()
|
||||
{
|
||||
switch(ss_family) {
|
||||
case AF_INET: {
|
||||
unsigned int bits = netmaskBits();
|
||||
if (bits <= 0)
|
||||
return false;
|
||||
if (bits >= 32)
|
||||
return false;
|
||||
uint32_t ip = Utils::ntoh((uint32_t)reinterpret_cast<const struct sockaddr_in *>(this)->sin_addr.s_addr);
|
||||
return ((ip & (0xffffffff >> bits)) == 0);
|
||||
}
|
||||
case AF_INET6: {
|
||||
unsigned int bits = netmaskBits();
|
||||
if (bits <= 0)
|
||||
return false;
|
||||
if (bits >= 128)
|
||||
return false;
|
||||
const unsigned char *ip = reinterpret_cast<const unsigned char *>(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr);
|
||||
unsigned int p = bits / 8;
|
||||
if ((ip[p++] & (0xff >> (bits % 8))) != 0)
|
||||
return false;
|
||||
while (p < 16) {
|
||||
if (ip[p++])
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool InetAddress::operator==(const InetAddress &a) const
|
||||
throw()
|
||||
{
|
||||
if (ss_family == a.ss_family) {
|
||||
switch(ss_family) {
|
||||
case AF_INET:
|
||||
return (
|
||||
(reinterpret_cast<const struct sockaddr_in *>(this)->sin_port == reinterpret_cast<const struct sockaddr_in *>(&a)->sin_port)&&
|
||||
(reinterpret_cast<const struct sockaddr_in *>(this)->sin_addr.s_addr == reinterpret_cast<const struct sockaddr_in *>(&a)->sin_addr.s_addr));
|
||||
break;
|
||||
case AF_INET6:
|
||||
return (
|
||||
(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_port == reinterpret_cast<const struct sockaddr_in6 *>(&a)->sin6_port)&&
|
||||
(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_flowinfo == reinterpret_cast<const struct sockaddr_in6 *>(&a)->sin6_flowinfo)&&
|
||||
(memcmp(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr,reinterpret_cast<const struct sockaddr_in6 *>(&a)->sin6_addr.s6_addr,16) == 0)&&
|
||||
(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_scope_id == reinterpret_cast<const struct sockaddr_in6 *>(&a)->sin6_scope_id));
|
||||
break;
|
||||
default:
|
||||
return (memcmp(this,&a,sizeof(InetAddress)) == 0);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool InetAddress::operator<(const InetAddress &a) const
|
||||
throw()
|
||||
{
|
||||
if (ss_family < a.ss_family)
|
||||
return true;
|
||||
else if (ss_family == a.ss_family) {
|
||||
switch(ss_family) {
|
||||
case AF_INET:
|
||||
if (reinterpret_cast<const struct sockaddr_in *>(this)->sin_port < reinterpret_cast<const struct sockaddr_in *>(&a)->sin_port)
|
||||
return true;
|
||||
else if (reinterpret_cast<const struct sockaddr_in *>(this)->sin_port == reinterpret_cast<const struct sockaddr_in *>(&a)->sin_port) {
|
||||
if (reinterpret_cast<const struct sockaddr_in *>(this)->sin_addr.s_addr < reinterpret_cast<const struct sockaddr_in *>(&a)->sin_addr.s_addr)
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case AF_INET6:
|
||||
if (reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_port < reinterpret_cast<const struct sockaddr_in6 *>(&a)->sin6_port)
|
||||
return true;
|
||||
else if (reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_port == reinterpret_cast<const struct sockaddr_in6 *>(&a)->sin6_port) {
|
||||
if (reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_flowinfo < reinterpret_cast<const struct sockaddr_in6 *>(&a)->sin6_flowinfo)
|
||||
return true;
|
||||
else if (reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_flowinfo == reinterpret_cast<const struct sockaddr_in6 *>(&a)->sin6_flowinfo) {
|
||||
if (memcmp(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr,reinterpret_cast<const struct sockaddr_in6 *>(&a)->sin6_addr.s6_addr,16) < 0)
|
||||
return true;
|
||||
else if (memcmp(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr,reinterpret_cast<const struct sockaddr_in6 *>(&a)->sin6_addr.s6_addr,16) == 0) {
|
||||
if (reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_scope_id < reinterpret_cast<const struct sockaddr_in6 *>(&a)->sin6_scope_id)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return (memcmp(this,&a,sizeof(InetAddress)) < 0);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
InetAddress InetAddress::makeIpv6LinkLocal(const MAC &mac)
|
||||
{
|
||||
struct sockaddr_in6 sin6;
|
||||
sin6.sin6_family = AF_INET6;
|
||||
sin6.sin6_addr.s6_addr[0] = 0xfe;
|
||||
sin6.sin6_addr.s6_addr[1] = 0x80;
|
||||
sin6.sin6_addr.s6_addr[2] = 0x00;
|
||||
sin6.sin6_addr.s6_addr[3] = 0x00;
|
||||
sin6.sin6_addr.s6_addr[4] = 0x00;
|
||||
sin6.sin6_addr.s6_addr[5] = 0x00;
|
||||
sin6.sin6_addr.s6_addr[6] = 0x00;
|
||||
sin6.sin6_addr.s6_addr[7] = 0x00;
|
||||
sin6.sin6_addr.s6_addr[8] = mac[0] & 0xfd;
|
||||
sin6.sin6_addr.s6_addr[9] = mac[1];
|
||||
sin6.sin6_addr.s6_addr[10] = mac[2];
|
||||
sin6.sin6_addr.s6_addr[11] = 0xff;
|
||||
sin6.sin6_addr.s6_addr[12] = 0xfe;
|
||||
sin6.sin6_addr.s6_addr[13] = mac[3];
|
||||
sin6.sin6_addr.s6_addr[14] = mac[4];
|
||||
sin6.sin6_addr.s6_addr[15] = mac[5];
|
||||
sin6.sin6_port = Utils::hton((uint16_t)64);
|
||||
return InetAddress(sin6);
|
||||
}
|
||||
|
||||
InetAddress InetAddress::makeIpv6rfc4193(uint64_t nwid,uint64_t zeroTierAddress)
|
||||
{
|
||||
InetAddress r;
|
||||
struct sockaddr_in6 *const sin6 = reinterpret_cast<struct sockaddr_in6 *>(&r);
|
||||
sin6->sin6_family = AF_INET6;
|
||||
sin6->sin6_addr.s6_addr[0] = 0xfd;
|
||||
sin6->sin6_addr.s6_addr[1] = (uint8_t)(nwid >> 56);
|
||||
sin6->sin6_addr.s6_addr[2] = (uint8_t)(nwid >> 48);
|
||||
sin6->sin6_addr.s6_addr[3] = (uint8_t)(nwid >> 40);
|
||||
sin6->sin6_addr.s6_addr[4] = (uint8_t)(nwid >> 32);
|
||||
sin6->sin6_addr.s6_addr[5] = (uint8_t)(nwid >> 24);
|
||||
sin6->sin6_addr.s6_addr[6] = (uint8_t)(nwid >> 16);
|
||||
sin6->sin6_addr.s6_addr[7] = (uint8_t)(nwid >> 8);
|
||||
sin6->sin6_addr.s6_addr[8] = (uint8_t)nwid;
|
||||
sin6->sin6_addr.s6_addr[9] = 0x99;
|
||||
sin6->sin6_addr.s6_addr[10] = 0x93;
|
||||
sin6->sin6_addr.s6_addr[11] = (uint8_t)(zeroTierAddress >> 32);
|
||||
sin6->sin6_addr.s6_addr[12] = (uint8_t)(zeroTierAddress >> 24);
|
||||
sin6->sin6_addr.s6_addr[13] = (uint8_t)(zeroTierAddress >> 16);
|
||||
sin6->sin6_addr.s6_addr[14] = (uint8_t)(zeroTierAddress >> 8);
|
||||
sin6->sin6_addr.s6_addr[15] = (uint8_t)zeroTierAddress;
|
||||
sin6->sin6_port = Utils::hton((uint16_t)88); // /88 includes 0xfd + network ID, discriminating by device ID below that
|
||||
return r;
|
||||
}
|
||||
|
||||
InetAddress InetAddress::makeIpv66plane(uint64_t nwid,uint64_t zeroTierAddress)
|
||||
{
|
||||
nwid ^= (nwid >> 32);
|
||||
InetAddress r;
|
||||
struct sockaddr_in6 *const sin6 = reinterpret_cast<struct sockaddr_in6 *>(&r);
|
||||
sin6->sin6_family = AF_INET6;
|
||||
sin6->sin6_addr.s6_addr[0] = 0xfc;
|
||||
sin6->sin6_addr.s6_addr[1] = (uint8_t)(nwid >> 24);
|
||||
sin6->sin6_addr.s6_addr[2] = (uint8_t)(nwid >> 16);
|
||||
sin6->sin6_addr.s6_addr[3] = (uint8_t)(nwid >> 8);
|
||||
sin6->sin6_addr.s6_addr[4] = (uint8_t)nwid;
|
||||
sin6->sin6_addr.s6_addr[5] = (uint8_t)(zeroTierAddress >> 32);
|
||||
sin6->sin6_addr.s6_addr[6] = (uint8_t)(zeroTierAddress >> 24);
|
||||
sin6->sin6_addr.s6_addr[7] = (uint8_t)(zeroTierAddress >> 16);
|
||||
sin6->sin6_addr.s6_addr[8] = (uint8_t)(zeroTierAddress >> 8);
|
||||
sin6->sin6_addr.s6_addr[9] = (uint8_t)zeroTierAddress;
|
||||
sin6->sin6_addr.s6_addr[15] = 0x01;
|
||||
sin6->sin6_port = Utils::hton((uint16_t)40);
|
||||
return r;
|
||||
}
|
||||
|
||||
} // namespace ZeroTier
|
||||
601
zto/node/InetAddress.hpp
Normal file
601
zto/node/InetAddress.hpp
Normal file
@@ -0,0 +1,601 @@
|
||||
/*
|
||||
* 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_INETADDRESS_HPP
|
||||
#define ZT_INETADDRESS_HPP
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "Constants.hpp"
|
||||
#include "../include/ZeroTierOne.h"
|
||||
#include "Utils.hpp"
|
||||
#include "MAC.hpp"
|
||||
#include "Buffer.hpp"
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
/**
|
||||
* Maximum integer value of enum IpScope
|
||||
*/
|
||||
#define ZT_INETADDRESS_MAX_SCOPE 7
|
||||
|
||||
/**
|
||||
* Extends sockaddr_storage with friendly C++ methods
|
||||
*
|
||||
* This is basically a "mixin" for sockaddr_storage. It adds methods and
|
||||
* operators, but does not modify the structure. This can be cast to/from
|
||||
* sockaddr_storage and used interchangeably. DO NOT change this by e.g.
|
||||
* adding non-static fields, since much code depends on this identity.
|
||||
*/
|
||||
struct InetAddress : public sockaddr_storage
|
||||
{
|
||||
/**
|
||||
* Loopback IPv4 address (no port)
|
||||
*/
|
||||
static const InetAddress LO4;
|
||||
|
||||
/**
|
||||
* Loopback IPV6 address (no port)
|
||||
*/
|
||||
static const InetAddress LO6;
|
||||
|
||||
/**
|
||||
* IP address scope
|
||||
*
|
||||
* Note that these values are in ascending order of path preference and
|
||||
* MUST remain that way or Path must be changed to reflect. Also be sure
|
||||
* to change ZT_INETADDRESS_MAX_SCOPE if the max changes.
|
||||
*/
|
||||
enum IpScope
|
||||
{
|
||||
IP_SCOPE_NONE = 0, // NULL or not an IP address
|
||||
IP_SCOPE_MULTICAST = 1, // 224.0.0.0 and other V4/V6 multicast IPs
|
||||
IP_SCOPE_LOOPBACK = 2, // 127.0.0.1, ::1, etc.
|
||||
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_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)); }
|
||||
|
||||
inline InetAddress &operator=(const InetAddress &a)
|
||||
throw()
|
||||
{
|
||||
if (&a != this)
|
||||
memcpy(this,&a,sizeof(InetAddress));
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline InetAddress &operator=(const InetAddress *a)
|
||||
throw()
|
||||
{
|
||||
if (a != this)
|
||||
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));
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline InetAddress &operator=(const struct sockaddr_storage *ss)
|
||||
throw()
|
||||
{
|
||||
if (reinterpret_cast<const InetAddress *>(ss) != this)
|
||||
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));
|
||||
}
|
||||
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));
|
||||
}
|
||||
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));
|
||||
}
|
||||
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));
|
||||
}
|
||||
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));
|
||||
break;
|
||||
case AF_INET6:
|
||||
memcpy(this,&sa,sizeof(struct sockaddr_in6));
|
||||
break;
|
||||
}
|
||||
}
|
||||
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));
|
||||
break;
|
||||
case AF_INET6:
|
||||
memcpy(this,sa,sizeof(struct sockaddr_in6));
|
||||
break;
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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();
|
||||
|
||||
/**
|
||||
* Set from a raw IP and port number
|
||||
*
|
||||
* @param ipBytes Bytes of IP address in network byte order
|
||||
* @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();
|
||||
|
||||
/**
|
||||
* Set the port component
|
||||
*
|
||||
* @param port Port, 0 to 65535
|
||||
*/
|
||||
inline void setPort(unsigned int port)
|
||||
{
|
||||
switch(ss_family) {
|
||||
case AF_INET:
|
||||
reinterpret_cast<struct sockaddr_in *>(this)->sin_port = Utils::hton((uint16_t)port);
|
||||
break;
|
||||
case AF_INET6:
|
||||
reinterpret_cast<struct sockaddr_in6 *>(this)->sin6_port = Utils::hton((uint16_t)port);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return True if this network/netmask route describes a default route (e.g. 0.0.0.0/0)
|
||||
*/
|
||||
inline bool isDefaultRoute() const
|
||||
{
|
||||
switch(ss_family) {
|
||||
case AF_INET:
|
||||
return ( (reinterpret_cast<const struct sockaddr_in *>(this)->sin_addr.s_addr == 0) && (reinterpret_cast<const struct sockaddr_in *>(this)->sin_port == 0) );
|
||||
case AF_INET6:
|
||||
const uint8_t *ipb = reinterpret_cast<const uint8_t *>(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr);
|
||||
for(int i=0;i<16;++i) {
|
||||
if (ipb[i])
|
||||
return false;
|
||||
}
|
||||
return (reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_port == 0);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ASCII IP/port format representation
|
||||
*/
|
||||
std::string toString() const;
|
||||
|
||||
/**
|
||||
* @return IP portion only, in ASCII string format
|
||||
*/
|
||||
std::string toIpString() const;
|
||||
|
||||
/**
|
||||
* @param ipSlashPort ASCII IP/port format notation
|
||||
*/
|
||||
void fromString(const std::string &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));
|
||||
case AF_INET6: return Utils::ntoh((uint16_t)(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_port));
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias for port()
|
||||
*
|
||||
* This just aliases port() to make code more readable when netmask bits
|
||||
* are stuffed there, as they are in Network, EthernetTap, and a few other
|
||||
* spots.
|
||||
*
|
||||
* @return Netmask bits
|
||||
*/
|
||||
inline unsigned int netmaskBits() const throw() { 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()
|
||||
*
|
||||
* This just aliases port() because for gateways we use this field to
|
||||
* store the gateway metric.
|
||||
*
|
||||
* @return Gateway metric
|
||||
*/
|
||||
inline unsigned int metric() const throw() { return port(); }
|
||||
|
||||
/**
|
||||
* Construct a full netmask as an InetAddress
|
||||
*
|
||||
* @return Netmask such as 255.255.255.0 if this address is /24 (port field will be unchanged)
|
||||
*/
|
||||
InetAddress netmask() const;
|
||||
|
||||
/**
|
||||
* Constructs a broadcast address from a network/netmask address
|
||||
*
|
||||
* This is only valid for IPv4 and will return a NULL InetAddress for other
|
||||
* address families.
|
||||
*
|
||||
* @return Broadcast address (only IP portion is meaningful)
|
||||
*/
|
||||
InetAddress broadcast() const;
|
||||
|
||||
/**
|
||||
* Return the network -- a.k.a. the IP ANDed with the netmask
|
||||
*
|
||||
* @return Network e.g. 10.0.1.0/24 from 10.0.1.200/24
|
||||
*/
|
||||
InetAddress network() const;
|
||||
|
||||
/**
|
||||
* Test whether this IP/netmask contains this address
|
||||
*
|
||||
* @param addr Address to check
|
||||
* @return True if this IP/netmask (route) contains this address
|
||||
*/
|
||||
bool containsAddress(const InetAddress &addr) const;
|
||||
|
||||
/**
|
||||
* @return True if this is an IPv4 address
|
||||
*/
|
||||
inline bool isV4() const throw() { return (ss_family == AF_INET); }
|
||||
|
||||
/**
|
||||
* @return True if this is an IPv6 address
|
||||
*/
|
||||
inline bool isV6() const throw() { return (ss_family == AF_INET6); }
|
||||
|
||||
/**
|
||||
* @return pointer to raw address bytes or NULL if not available
|
||||
*/
|
||||
inline const void *rawIpData() const
|
||||
{
|
||||
switch(ss_family) {
|
||||
case AF_INET: return (const void *)&(reinterpret_cast<const struct sockaddr_in *>(this)->sin_addr.s_addr);
|
||||
case AF_INET6: return (const void *)(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr);
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @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;
|
||||
memcpy(reinterpret_cast<struct sockaddr_in6 *>(&r)->sin6_addr.s6_addr,reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr,16);
|
||||
break;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs an IP-only comparison or, if that is impossible, a memcmp()
|
||||
*
|
||||
* @param a InetAddress to compare again
|
||||
* @return True if only IP portions are equal (false for non-IP or null addresses)
|
||||
*/
|
||||
inline bool ipsEqual(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,16) == 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)); }
|
||||
|
||||
/**
|
||||
* Check whether this is a network/route rather than an IP assignment
|
||||
*
|
||||
* A network is an IP/netmask where everything after the netmask is
|
||||
* zero e.g. 10.0.0.0/8.
|
||||
*
|
||||
* @return True if everything after netmask bits is zero
|
||||
*/
|
||||
bool isNetwork() const
|
||||
throw();
|
||||
|
||||
/**
|
||||
* @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); }
|
||||
|
||||
template<unsigned int C>
|
||||
inline void serialize(Buffer<C> &b) const
|
||||
{
|
||||
// This is used in the protocol and must be the same as describe in places
|
||||
// like VERB_HELLO in Packet.hpp.
|
||||
switch(ss_family) {
|
||||
case AF_INET:
|
||||
b.append((uint8_t)0x04);
|
||||
b.append(&(reinterpret_cast<const struct sockaddr_in *>(this)->sin_addr.s_addr),4);
|
||||
b.append((uint16_t)port()); // just in case sin_port != uint16_t
|
||||
return;
|
||||
case AF_INET6:
|
||||
b.append((uint8_t)0x06);
|
||||
b.append(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr,16);
|
||||
b.append((uint16_t)port()); // just in case sin_port != uint16_t
|
||||
return;
|
||||
default:
|
||||
b.append((uint8_t)0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
template<unsigned int C>
|
||||
inline unsigned int deserialize(const Buffer<C> &b,unsigned int startAt = 0)
|
||||
{
|
||||
memset(this,0,sizeof(InetAddress));
|
||||
unsigned int p = startAt;
|
||||
switch(b[p++]) {
|
||||
case 0:
|
||||
return 1;
|
||||
case 0x01:
|
||||
// TODO: Ethernet address (but accept for forward compatibility)
|
||||
return 7;
|
||||
case 0x02:
|
||||
// TODO: Bluetooth address (but accept for forward compatibility)
|
||||
return 7;
|
||||
case 0x03:
|
||||
// TODO: Other address types (but accept for forward compatibility)
|
||||
// These could be extended/optional things like AF_UNIX, LTE Direct, shared memory, etc.
|
||||
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;
|
||||
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;
|
||||
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");
|
||||
}
|
||||
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); }
|
||||
|
||||
/**
|
||||
* @param mac MAC address seed
|
||||
* @return IPv6 link-local address
|
||||
*/
|
||||
static InetAddress makeIpv6LinkLocal(const MAC &mac);
|
||||
|
||||
/**
|
||||
* Compute private IPv6 unicast address from network ID and ZeroTier address
|
||||
*
|
||||
* This generates a private unicast IPv6 address that is mostly compliant
|
||||
* with the letter of RFC4193 and certainly compliant in spirit.
|
||||
*
|
||||
* RFC4193 specifies a format of:
|
||||
*
|
||||
* | 7 bits |1| 40 bits | 16 bits | 64 bits |
|
||||
* | Prefix |L| Global ID | Subnet ID | Interface ID |
|
||||
*
|
||||
* The 'L' bit is set to 1, yielding an address beginning with 0xfd. Then
|
||||
* the network ID is filled into the global ID, subnet ID, and first byte
|
||||
* of the "interface ID" field. Since the first 40 bits of the network ID
|
||||
* is the unique ZeroTier address of its controller, this makes a very
|
||||
* good random global ID. Since network IDs have 24 more bits, we let it
|
||||
* overflow into the interface ID.
|
||||
*
|
||||
* After that we pad with two bytes: 0x99, 0x93, namely the default ZeroTier
|
||||
* port in hex.
|
||||
*
|
||||
* Finally we fill the remaining 40 bits of the interface ID field with
|
||||
* the 40-bit unique ZeroTier device ID of the network member.
|
||||
*
|
||||
* This yields a valid RFC4193 address with a random global ID, a
|
||||
* meaningful subnet ID, and a unique interface ID, all mappable back onto
|
||||
* ZeroTier space.
|
||||
*
|
||||
* This in turn could allow us, on networks numbered this way, to emulate
|
||||
* IPv6 NDP and eliminate all multicast. This could be beneficial for
|
||||
* small devices and huge networks, e.g. IoT applications.
|
||||
*
|
||||
* The returned address is given an odd prefix length of /88, since within
|
||||
* a given network only the last 40 bits (device ID) are variable. This
|
||||
* is a bit unusual but as far as we know should not cause any problems with
|
||||
* any non-braindead IPv6 stack.
|
||||
*
|
||||
* @param nwid 64-bit network ID
|
||||
* @param zeroTierAddress 40-bit device address (in least significant 40 bits, highest 24 bits ignored)
|
||||
* @return IPv6 private unicast address with /88 netmask
|
||||
*/
|
||||
static InetAddress makeIpv6rfc4193(uint64_t nwid,uint64_t zeroTierAddress);
|
||||
|
||||
/**
|
||||
* Compute a private IPv6 "6plane" unicast address from network ID and ZeroTier address
|
||||
*/
|
||||
static InetAddress makeIpv66plane(uint64_t nwid,uint64_t zeroTierAddress);
|
||||
};
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
||||
#endif
|
||||
264
zto/node/MAC.hpp
Normal file
264
zto/node/MAC.hpp
Normal file
@@ -0,0 +1,264 @@
|
||||
/*
|
||||
* 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_MAC_HPP
|
||||
#define ZT_MAC_HPP
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "Constants.hpp"
|
||||
#include "Utils.hpp"
|
||||
#include "Address.hpp"
|
||||
#include "Buffer.hpp"
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
/**
|
||||
* 48-byte Ethernet MAC address
|
||||
*/
|
||||
class MAC
|
||||
{
|
||||
public:
|
||||
MAC() throw() : _m(0ULL) {}
|
||||
MAC(const MAC &m) throw() : _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() :
|
||||
_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) {}
|
||||
|
||||
/**
|
||||
* @return MAC in 64-bit integer
|
||||
*/
|
||||
inline uint64_t toInt() const throw() { return _m; }
|
||||
|
||||
/**
|
||||
* Set MAC to zero
|
||||
*/
|
||||
inline void zero() { _m = 0ULL; }
|
||||
|
||||
/**
|
||||
* @return True if MAC is non-zero
|
||||
*/
|
||||
inline operator bool() const throw() { 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;
|
||||
return;
|
||||
}
|
||||
const unsigned char *b = (const unsigned char *)bits;
|
||||
_m = ((((uint64_t)*b) & 0xff) << 40); ++b;
|
||||
_m |= ((((uint64_t)*b) & 0xff) << 32); ++b;
|
||||
_m |= ((((uint64_t)*b) & 0xff) << 24); ++b;
|
||||
_m |= ((((uint64_t)*b) & 0xff) << 16); ++b;
|
||||
_m |= ((((uint64_t)*b) & 0xff) << 8); ++b;
|
||||
_m |= (((uint64_t)*b) & 0xff);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param buf Destination buffer for MAC in big-endian byte order
|
||||
* @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;
|
||||
unsigned char *b = (unsigned char *)buf;
|
||||
*(b++) = (unsigned char)((_m >> 40) & 0xff);
|
||||
*(b++) = (unsigned char)((_m >> 32) & 0xff);
|
||||
*(b++) = (unsigned char)((_m >> 24) & 0xff);
|
||||
*(b++) = (unsigned char)((_m >> 16) & 0xff);
|
||||
*(b++) = (unsigned char)((_m >> 8) & 0xff);
|
||||
*b = (unsigned char)(_m & 0xff);
|
||||
}
|
||||
|
||||
/**
|
||||
* Append to a buffer in big-endian byte order
|
||||
*
|
||||
* @param b Buffer to append to
|
||||
*/
|
||||
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);
|
||||
*(p++) = (unsigned char)((_m >> 32) & 0xff);
|
||||
*(p++) = (unsigned char)((_m >> 24) & 0xff);
|
||||
*(p++) = (unsigned char)((_m >> 16) & 0xff);
|
||||
*(p++) = (unsigned char)((_m >> 8) & 0xff);
|
||||
*p = (unsigned char)(_m & 0xff);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return True if this is broadcast (all 0xff)
|
||||
*/
|
||||
inline bool isBroadcast() const throw() { return (_m == 0xffffffffffffULL); }
|
||||
|
||||
/**
|
||||
* @return True if this is a multicast MAC
|
||||
*/
|
||||
inline bool isMulticast() const throw() { 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]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set this MAC to a MAC derived from an address and a network ID
|
||||
*
|
||||
* @param ztaddr ZeroTier address
|
||||
* @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
|
||||
m ^= ((nwid >> 8) & 0xff) << 32;
|
||||
m ^= ((nwid >> 16) & 0xff) << 24;
|
||||
m ^= ((nwid >> 24) & 0xff) << 16;
|
||||
m ^= ((nwid >> 32) & 0xff) << 8;
|
||||
m ^= (nwid >> 40) & 0xff;
|
||||
_m = m;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the ZeroTier address for this MAC on this network (assuming no bridging of course, basic unicast)
|
||||
*
|
||||
* This just XORs the next-lest-significant 5 bytes of the network ID again to unmask.
|
||||
*
|
||||
* @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
|
||||
a ^= ((nwid >> 16) & 0xff) << 24;
|
||||
a ^= ((nwid >> 24) & 0xff) << 16;
|
||||
a ^= ((nwid >> 32) & 0xff) << 8;
|
||||
a ^= (nwid >> 40) & 0xff;
|
||||
return Address(a);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param nwid Network ID
|
||||
* @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
|
||||
}
|
||||
|
||||
/**
|
||||
* @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); }
|
||||
|
||||
/**
|
||||
* @return 6, which is the number of bytes in a MAC, for container compliance
|
||||
*/
|
||||
inline unsigned int size() const throw() { return 6; }
|
||||
|
||||
inline unsigned long hashCode() const throw() { return (unsigned long)_m; }
|
||||
|
||||
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); }
|
||||
|
||||
private:
|
||||
uint64_t _m;
|
||||
};
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
||||
#endif
|
||||
395
zto/node/Membership.cpp
Normal file
395
zto/node/Membership.cpp
Normal file
@@ -0,0 +1,395 @@
|
||||
/*
|
||||
* 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 <algorithm>
|
||||
|
||||
#include "Membership.hpp"
|
||||
#include "RuntimeEnvironment.hpp"
|
||||
#include "Peer.hpp"
|
||||
#include "Topology.hpp"
|
||||
#include "Switch.hpp"
|
||||
#include "Packet.hpp"
|
||||
#include "Node.hpp"
|
||||
|
||||
#define ZT_CREDENTIAL_PUSH_EVERY (ZT_NETWORK_AUTOCONF_DELAY / 3)
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
Membership::Membership() :
|
||||
_lastUpdatedMulticast(0),
|
||||
_lastPushedCom(0),
|
||||
_comRevocationThreshold(0)
|
||||
{
|
||||
for(unsigned int i=0;i<ZT_MAX_NETWORK_TAGS;++i) _remoteTags[i] = &(_tagMem[i]);
|
||||
for(unsigned int i=0;i<ZT_MAX_NETWORK_CAPABILITIES;++i) _remoteCaps[i] = &(_capMem[i]);
|
||||
for(unsigned int i=0;i<ZT_MAX_CERTIFICATES_OF_OWNERSHIP;++i) _remoteCoos[i] = &(_cooMem[i]);
|
||||
}
|
||||
|
||||
void Membership::pushCredentials(const RuntimeEnvironment *RR,const uint64_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 ( (_localCaps[localCapabilityIndex].id != sendCap->id()) || ((now - _localCaps[localCapabilityIndex].lastPushed) >= ZT_CREDENTIAL_PUSH_EVERY) || (force) ) {
|
||||
_localCaps[localCapabilityIndex].lastPushed = now;
|
||||
_localCaps[localCapabilityIndex].id = sendCap->id();
|
||||
} 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 ( (_localTags[t].id != nconf.tags[t].id()) || ((now - _localTags[t].lastPushed) >= ZT_CREDENTIAL_PUSH_EVERY) || (force) ) {
|
||||
_localTags[t].lastPushed = now;
|
||||
_localTags[t].id = nconf.tags[t].id();
|
||||
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 ( (_localCoos[c].id != nconf.certificatesOfOwnership[c].id()) || ((now - _localCoos[c].lastPushed) >= ZT_CREDENTIAL_PUSH_EVERY) || (force) ) {
|
||||
_localCoos[c].lastPushed = now;
|
||||
_localCoos[c].id = nconf.certificatesOfOwnership[c].id();
|
||||
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(outp,true);
|
||||
}
|
||||
}
|
||||
|
||||
const Tag *Membership::getTag(const NetworkConfig &nconf,const uint32_t id) const
|
||||
{
|
||||
const _RemoteCredential<Tag> *const *t = std::lower_bound(&(_remoteTags[0]),&(_remoteTags[ZT_MAX_NETWORK_TAGS]),(uint64_t)id,_RemoteCredentialComp<Tag>());
|
||||
return ( ((t != &(_remoteTags[ZT_MAX_NETWORK_CAPABILITIES]))&&((*t)->id == (uint64_t)id)) ? ((((*t)->lastReceived)&&(_isCredentialTimestampValid(nconf,**t))) ? &((*t)->credential) : (const Tag *)0) : (const Tag *)0);
|
||||
}
|
||||
|
||||
Membership::AddCredentialResult Membership::addCredential(const RuntimeEnvironment *RR,const NetworkConfig &nconf,const CertificateOfMembership &com)
|
||||
{
|
||||
const uint64_t newts = com.timestamp().first;
|
||||
if (newts <= _comRevocationThreshold) {
|
||||
TRACE("addCredential(CertificateOfMembership) for %s on %.16llx REJECTED (revoked)",com.issuedTo().toString().c_str(),com.networkId());
|
||||
return ADD_REJECTED;
|
||||
}
|
||||
|
||||
const uint64_t oldts = _com.timestamp().first;
|
||||
if (newts < oldts) {
|
||||
TRACE("addCredential(CertificateOfMembership) for %s on %.16llx REJECTED (older than current)",com.issuedTo().toString().c_str(),com.networkId());
|
||||
return ADD_REJECTED;
|
||||
}
|
||||
if ((newts == oldts)&&(_com == com)) {
|
||||
TRACE("addCredential(CertificateOfMembership) for %s on %.16llx ACCEPTED (redundant)",com.issuedTo().toString().c_str(),com.networkId());
|
||||
return ADD_ACCEPTED_REDUNDANT;
|
||||
}
|
||||
|
||||
switch(com.verify(RR)) {
|
||||
default:
|
||||
TRACE("addCredential(CertificateOfMembership) for %s on %.16llx REJECTED (invalid signature or object)",com.issuedTo().toString().c_str(),com.networkId());
|
||||
return ADD_REJECTED;
|
||||
case 0:
|
||||
TRACE("addCredential(CertificateOfMembership) for %s on %.16llx ACCEPTED (new)",com.issuedTo().toString().c_str(),com.networkId());
|
||||
_com = com;
|
||||
return ADD_ACCEPTED_NEW;
|
||||
case 1:
|
||||
return ADD_DEFERRED_FOR_WHOIS;
|
||||
}
|
||||
}
|
||||
|
||||
Membership::AddCredentialResult Membership::addCredential(const RuntimeEnvironment *RR,const NetworkConfig &nconf,const Tag &tag)
|
||||
{
|
||||
_RemoteCredential<Tag> *const *htmp = std::lower_bound(&(_remoteTags[0]),&(_remoteTags[ZT_MAX_NETWORK_TAGS]),(uint64_t)tag.id(),_RemoteCredentialComp<Tag>());
|
||||
_RemoteCredential<Tag> *have = ((htmp != &(_remoteTags[ZT_MAX_NETWORK_TAGS]))&&((*htmp)->id == (uint64_t)tag.id())) ? *htmp : (_RemoteCredential<Tag> *)0;
|
||||
if (have) {
|
||||
if ( (!_isCredentialTimestampValid(nconf,*have)) || (have->credential.timestamp() > tag.timestamp()) ) {
|
||||
TRACE("addCredential(Tag) for %s on %.16llx REJECTED (revoked or too old)",tag.issuedTo().toString().c_str(),tag.networkId());
|
||||
return ADD_REJECTED;
|
||||
}
|
||||
if (have->credential == tag) {
|
||||
TRACE("addCredential(Tag) for %s on %.16llx ACCEPTED (redundant)",tag.issuedTo().toString().c_str(),tag.networkId());
|
||||
return ADD_ACCEPTED_REDUNDANT;
|
||||
}
|
||||
}
|
||||
|
||||
switch(tag.verify(RR)) {
|
||||
default:
|
||||
TRACE("addCredential(Tag) for %s on %.16llx REJECTED (invalid)",tag.issuedTo().toString().c_str(),tag.networkId());
|
||||
return ADD_REJECTED;
|
||||
case 0:
|
||||
TRACE("addCredential(Tag) for %s on %.16llx ACCEPTED (new)",tag.issuedTo().toString().c_str(),tag.networkId());
|
||||
if (!have) have = _newTag(tag.id());
|
||||
have->lastReceived = RR->node->now();
|
||||
have->credential = tag;
|
||||
return ADD_ACCEPTED_NEW;
|
||||
case 1:
|
||||
return ADD_DEFERRED_FOR_WHOIS;
|
||||
}
|
||||
}
|
||||
|
||||
Membership::AddCredentialResult Membership::addCredential(const RuntimeEnvironment *RR,const NetworkConfig &nconf,const Capability &cap)
|
||||
{
|
||||
_RemoteCredential<Capability> *const *htmp = std::lower_bound(&(_remoteCaps[0]),&(_remoteCaps[ZT_MAX_NETWORK_CAPABILITIES]),(uint64_t)cap.id(),_RemoteCredentialComp<Capability>());
|
||||
_RemoteCredential<Capability> *have = ((htmp != &(_remoteCaps[ZT_MAX_NETWORK_CAPABILITIES]))&&((*htmp)->id == (uint64_t)cap.id())) ? *htmp : (_RemoteCredential<Capability> *)0;
|
||||
if (have) {
|
||||
if ( (!_isCredentialTimestampValid(nconf,*have)) || (have->credential.timestamp() > cap.timestamp()) ) {
|
||||
TRACE("addCredential(Capability) for %s on %.16llx REJECTED (revoked or too old)",cap.issuedTo().toString().c_str(),cap.networkId());
|
||||
return ADD_REJECTED;
|
||||
}
|
||||
if (have->credential == cap) {
|
||||
TRACE("addCredential(Capability) for %s on %.16llx ACCEPTED (redundant)",cap.issuedTo().toString().c_str(),cap.networkId());
|
||||
return ADD_ACCEPTED_REDUNDANT;
|
||||
}
|
||||
}
|
||||
|
||||
switch(cap.verify(RR)) {
|
||||
default:
|
||||
TRACE("addCredential(Capability) for %s on %.16llx REJECTED (invalid)",cap.issuedTo().toString().c_str(),cap.networkId());
|
||||
return ADD_REJECTED;
|
||||
case 0:
|
||||
TRACE("addCredential(Capability) for %s on %.16llx ACCEPTED (new)",cap.issuedTo().toString().c_str(),cap.networkId());
|
||||
if (!have) have = _newCapability(cap.id());
|
||||
have->lastReceived = RR->node->now();
|
||||
have->credential = cap;
|
||||
return ADD_ACCEPTED_NEW;
|
||||
case 1:
|
||||
return ADD_DEFERRED_FOR_WHOIS;
|
||||
}
|
||||
}
|
||||
|
||||
Membership::AddCredentialResult Membership::addCredential(const RuntimeEnvironment *RR,const NetworkConfig &nconf,const Revocation &rev)
|
||||
{
|
||||
switch(rev.verify(RR)) {
|
||||
default:
|
||||
return ADD_REJECTED;
|
||||
case 0: {
|
||||
const uint64_t now = RR->node->now();
|
||||
switch(rev.type()) {
|
||||
default:
|
||||
return ADD_REJECTED;
|
||||
case Revocation::CREDENTIAL_TYPE_COM:
|
||||
return (_revokeCom(rev) ? ADD_ACCEPTED_NEW : ADD_ACCEPTED_REDUNDANT);
|
||||
case Revocation::CREDENTIAL_TYPE_CAPABILITY:
|
||||
return (_revokeCap(rev,now) ? ADD_ACCEPTED_NEW : ADD_ACCEPTED_REDUNDANT);
|
||||
case Revocation::CREDENTIAL_TYPE_TAG:
|
||||
return (_revokeTag(rev,now) ? ADD_ACCEPTED_NEW : ADD_ACCEPTED_REDUNDANT);
|
||||
case Revocation::CREDENTIAL_TYPE_COO:
|
||||
return (_revokeCoo(rev,now) ? ADD_ACCEPTED_NEW : ADD_ACCEPTED_REDUNDANT);
|
||||
}
|
||||
}
|
||||
case 1:
|
||||
return ADD_DEFERRED_FOR_WHOIS;
|
||||
}
|
||||
}
|
||||
|
||||
Membership::AddCredentialResult Membership::addCredential(const RuntimeEnvironment *RR,const NetworkConfig &nconf,const CertificateOfOwnership &coo)
|
||||
{
|
||||
_RemoteCredential<CertificateOfOwnership> *const *htmp = std::lower_bound(&(_remoteCoos[0]),&(_remoteCoos[ZT_MAX_CERTIFICATES_OF_OWNERSHIP]),(uint64_t)coo.id(),_RemoteCredentialComp<CertificateOfOwnership>());
|
||||
_RemoteCredential<CertificateOfOwnership> *have = ((htmp != &(_remoteCoos[ZT_MAX_CERTIFICATES_OF_OWNERSHIP]))&&((*htmp)->id == (uint64_t)coo.id())) ? *htmp : (_RemoteCredential<CertificateOfOwnership> *)0;
|
||||
if (have) {
|
||||
if ( (!_isCredentialTimestampValid(nconf,*have)) || (have->credential.timestamp() > coo.timestamp()) ) {
|
||||
TRACE("addCredential(CertificateOfOwnership) for %s on %.16llx REJECTED (revoked or too old)",coo.issuedTo().toString().c_str(),coo.networkId());
|
||||
return ADD_REJECTED;
|
||||
}
|
||||
if (have->credential == coo) {
|
||||
TRACE("addCredential(CertificateOfOwnership) for %s on %.16llx ACCEPTED (redundant)",coo.issuedTo().toString().c_str(),coo.networkId());
|
||||
return ADD_ACCEPTED_REDUNDANT;
|
||||
}
|
||||
}
|
||||
|
||||
switch(coo.verify(RR)) {
|
||||
default:
|
||||
TRACE("addCredential(CertificateOfOwnership) for %s on %.16llx REJECTED (invalid)",coo.issuedTo().toString().c_str(),coo.networkId());
|
||||
return ADD_REJECTED;
|
||||
case 0:
|
||||
TRACE("addCredential(CertificateOfOwnership) for %s on %.16llx ACCEPTED (new)",coo.issuedTo().toString().c_str(),coo.networkId());
|
||||
if (!have) have = _newCoo(coo.id());
|
||||
have->lastReceived = RR->node->now();
|
||||
have->credential = coo;
|
||||
return ADD_ACCEPTED_NEW;
|
||||
case 1:
|
||||
return ADD_DEFERRED_FOR_WHOIS;
|
||||
}
|
||||
}
|
||||
|
||||
Membership::_RemoteCredential<Tag> *Membership::_newTag(const uint64_t id)
|
||||
{
|
||||
_RemoteCredential<Tag> *t = NULL;
|
||||
uint64_t minlr = 0xffffffffffffffffULL;
|
||||
for(unsigned int i=0;i<ZT_MAX_NETWORK_TAGS;++i) {
|
||||
if (_remoteTags[i]->id == ZT_MEMBERSHIP_CRED_ID_UNUSED) {
|
||||
t = _remoteTags[i];
|
||||
break;
|
||||
} else if (_remoteTags[i]->lastReceived <= minlr) {
|
||||
t = _remoteTags[i];
|
||||
minlr = _remoteTags[i]->lastReceived;
|
||||
}
|
||||
}
|
||||
|
||||
if (t) {
|
||||
t->id = id;
|
||||
t->lastReceived = 0;
|
||||
t->revocationThreshold = 0;
|
||||
t->credential = Tag();
|
||||
}
|
||||
|
||||
std::sort(&(_remoteTags[0]),&(_remoteTags[ZT_MAX_NETWORK_TAGS]),_RemoteCredentialComp<Tag>());
|
||||
return t;
|
||||
}
|
||||
|
||||
Membership::_RemoteCredential<Capability> *Membership::_newCapability(const uint64_t id)
|
||||
{
|
||||
_RemoteCredential<Capability> *c = NULL;
|
||||
uint64_t minlr = 0xffffffffffffffffULL;
|
||||
for(unsigned int i=0;i<ZT_MAX_NETWORK_CAPABILITIES;++i) {
|
||||
if (_remoteCaps[i]->id == ZT_MEMBERSHIP_CRED_ID_UNUSED) {
|
||||
c = _remoteCaps[i];
|
||||
break;
|
||||
} else if (_remoteCaps[i]->lastReceived <= minlr) {
|
||||
c = _remoteCaps[i];
|
||||
minlr = _remoteCaps[i]->lastReceived;
|
||||
}
|
||||
}
|
||||
|
||||
if (c) {
|
||||
c->id = id;
|
||||
c->lastReceived = 0;
|
||||
c->revocationThreshold = 0;
|
||||
c->credential = Capability();
|
||||
}
|
||||
|
||||
std::sort(&(_remoteCaps[0]),&(_remoteCaps[ZT_MAX_NETWORK_CAPABILITIES]),_RemoteCredentialComp<Capability>());
|
||||
return c;
|
||||
}
|
||||
|
||||
Membership::_RemoteCredential<CertificateOfOwnership> *Membership::_newCoo(const uint64_t id)
|
||||
{
|
||||
_RemoteCredential<CertificateOfOwnership> *c = NULL;
|
||||
uint64_t minlr = 0xffffffffffffffffULL;
|
||||
for(unsigned int i=0;i<ZT_MAX_CERTIFICATES_OF_OWNERSHIP;++i) {
|
||||
if (_remoteCoos[i]->id == ZT_MEMBERSHIP_CRED_ID_UNUSED) {
|
||||
c = _remoteCoos[i];
|
||||
break;
|
||||
} else if (_remoteCoos[i]->lastReceived <= minlr) {
|
||||
c = _remoteCoos[i];
|
||||
minlr = _remoteCoos[i]->lastReceived;
|
||||
}
|
||||
}
|
||||
|
||||
if (c) {
|
||||
c->id = id;
|
||||
c->lastReceived = 0;
|
||||
c->revocationThreshold = 0;
|
||||
c->credential = CertificateOfOwnership();
|
||||
}
|
||||
|
||||
std::sort(&(_remoteCoos[0]),&(_remoteCoos[ZT_MAX_CERTIFICATES_OF_OWNERSHIP]),_RemoteCredentialComp<CertificateOfOwnership>());
|
||||
return c;
|
||||
}
|
||||
|
||||
bool Membership::_revokeCom(const Revocation &rev)
|
||||
{
|
||||
if (rev.threshold() > _comRevocationThreshold) {
|
||||
_comRevocationThreshold = rev.threshold();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Membership::_revokeCap(const Revocation &rev,const uint64_t now)
|
||||
{
|
||||
_RemoteCredential<Capability> *const *htmp = std::lower_bound(&(_remoteCaps[0]),&(_remoteCaps[ZT_MAX_NETWORK_CAPABILITIES]),(uint64_t)rev.credentialId(),_RemoteCredentialComp<Capability>());
|
||||
_RemoteCredential<Capability> *have = ((htmp != &(_remoteCaps[ZT_MAX_NETWORK_CAPABILITIES]))&&((*htmp)->id == (uint64_t)rev.credentialId())) ? *htmp : (_RemoteCredential<Capability> *)0;
|
||||
if (!have) have = _newCapability(rev.credentialId());
|
||||
if (rev.threshold() > have->revocationThreshold) {
|
||||
have->lastReceived = now;
|
||||
have->revocationThreshold = rev.threshold();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Membership::_revokeTag(const Revocation &rev,const uint64_t now)
|
||||
{
|
||||
_RemoteCredential<Tag> *const *htmp = std::lower_bound(&(_remoteTags[0]),&(_remoteTags[ZT_MAX_NETWORK_TAGS]),(uint64_t)rev.credentialId(),_RemoteCredentialComp<Tag>());
|
||||
_RemoteCredential<Tag> *have = ((htmp != &(_remoteTags[ZT_MAX_NETWORK_TAGS]))&&((*htmp)->id == (uint64_t)rev.credentialId())) ? *htmp : (_RemoteCredential<Tag> *)0;
|
||||
if (!have) have = _newTag(rev.credentialId());
|
||||
if (rev.threshold() > have->revocationThreshold) {
|
||||
have->lastReceived = now;
|
||||
have->revocationThreshold = rev.threshold();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Membership::_revokeCoo(const Revocation &rev,const uint64_t now)
|
||||
{
|
||||
_RemoteCredential<CertificateOfOwnership> *const *htmp = std::lower_bound(&(_remoteCoos[0]),&(_remoteCoos[ZT_MAX_CERTIFICATES_OF_OWNERSHIP]),(uint64_t)rev.credentialId(),_RemoteCredentialComp<CertificateOfOwnership>());
|
||||
_RemoteCredential<CertificateOfOwnership> *have = ((htmp != &(_remoteCoos[ZT_MAX_CERTIFICATES_OF_OWNERSHIP]))&&((*htmp)->id == (uint64_t)rev.credentialId())) ? *htmp : (_RemoteCredential<CertificateOfOwnership> *)0;
|
||||
if (!have) have = _newCoo(rev.credentialId());
|
||||
if (rev.threshold() > have->revocationThreshold) {
|
||||
have->lastReceived = now;
|
||||
have->revocationThreshold = rev.threshold();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace ZeroTier
|
||||
299
zto/node/Membership.hpp
Normal file
299
zto/node/Membership.hpp
Normal file
@@ -0,0 +1,299 @@
|
||||
/*
|
||||
* 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_MEMBERSHIP_HPP
|
||||
#define ZT_MEMBERSHIP_HPP
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "Constants.hpp"
|
||||
#include "../include/ZeroTierOne.h"
|
||||
#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
|
||||
{
|
||||
private:
|
||||
template<typename T>
|
||||
struct _RemoteCredential
|
||||
{
|
||||
_RemoteCredential() : id(ZT_MEMBERSHIP_CRED_ID_UNUSED),lastReceived(0),revocationThreshold(0) {}
|
||||
uint64_t id;
|
||||
uint64_t lastReceived; // last time we got this credential
|
||||
uint64_t revocationThreshold; // credentials before this time are invalid
|
||||
T credential;
|
||||
inline bool operator<(const _RemoteCredential &c) const { return (id < c.id); }
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct _RemoteCredentialComp
|
||||
{
|
||||
inline bool operator()(const _RemoteCredential<T> *a,const _RemoteCredential<T> *b) const { return (a->id < b->id); }
|
||||
inline bool operator()(const uint64_t a,const _RemoteCredential<T> *b) const { return (a < b->id); }
|
||||
inline bool operator()(const _RemoteCredential<T> *a,const uint64_t b) const { return (a->id < b); }
|
||||
inline bool operator()(const uint64_t a,const uint64_t b) const { return (a < b); }
|
||||
};
|
||||
|
||||
// Used to track push state for network config tags[] and capabilities[] entries
|
||||
struct _LocalCredentialPushState
|
||||
{
|
||||
_LocalCredentialPushState() : lastPushed(0),id(0) {}
|
||||
uint64_t lastPushed; // last time we sent our own copy of this credential
|
||||
uint64_t id;
|
||||
};
|
||||
|
||||
public:
|
||||
enum AddCredentialResult
|
||||
{
|
||||
ADD_REJECTED,
|
||||
ADD_ACCEPTED_NEW,
|
||||
ADD_ACCEPTED_REDUNDANT,
|
||||
ADD_DEFERRED_FOR_WHOIS
|
||||
};
|
||||
|
||||
/**
|
||||
* Iterator to scan forward through capabilities in ascending order of ID
|
||||
*/
|
||||
class CapabilityIterator
|
||||
{
|
||||
public:
|
||||
CapabilityIterator(const Membership &m,const NetworkConfig &nconf) :
|
||||
_m(&m),
|
||||
_c(&nconf),
|
||||
_i(&(m._remoteCaps[0])) {}
|
||||
|
||||
inline const Capability *next()
|
||||
{
|
||||
for(;;) {
|
||||
if ((_i != &(_m->_remoteCaps[ZT_MAX_NETWORK_CAPABILITIES]))&&((*_i)->id != ZT_MEMBERSHIP_CRED_ID_UNUSED)) {
|
||||
const Capability *tmp = &((*_i)->credential);
|
||||
if (_m->_isCredentialTimestampValid(*_c,**_i)) {
|
||||
++_i;
|
||||
return tmp;
|
||||
} else ++_i;
|
||||
} else {
|
||||
return (const Capability *)0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
const Membership *_m;
|
||||
const NetworkConfig *_c;
|
||||
const _RemoteCredential<Capability> *const *_i;
|
||||
};
|
||||
friend class CapabilityIterator;
|
||||
|
||||
/**
|
||||
* Iterator to scan forward through tags in ascending order of ID
|
||||
*/
|
||||
class TagIterator
|
||||
{
|
||||
public:
|
||||
TagIterator(const Membership &m,const NetworkConfig &nconf) :
|
||||
_m(&m),
|
||||
_c(&nconf),
|
||||
_i(&(m._remoteTags[0])) {}
|
||||
|
||||
inline const Tag *next()
|
||||
{
|
||||
for(;;) {
|
||||
if ((_i != &(_m->_remoteTags[ZT_MAX_NETWORK_TAGS]))&&((*_i)->id != ZT_MEMBERSHIP_CRED_ID_UNUSED)) {
|
||||
const Tag *tmp = &((*_i)->credential);
|
||||
if (_m->_isCredentialTimestampValid(*_c,**_i)) {
|
||||
++_i;
|
||||
return tmp;
|
||||
} else ++_i;
|
||||
} else {
|
||||
return (const Tag *)0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
const Membership *_m;
|
||||
const NetworkConfig *_c;
|
||||
const _RemoteCredential<Tag> *const *_i;
|
||||
};
|
||||
friend class TagIterator;
|
||||
|
||||
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 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,const uint64_t now,const Address &peerAddress,const NetworkConfig &nconf,int localCapabilityIndex,const bool force);
|
||||
|
||||
/**
|
||||
* Check whether we should push MULTICAST_LIKEs to this peer
|
||||
*
|
||||
* @param now Current time
|
||||
* @return True if we should update multicasts
|
||||
*/
|
||||
inline bool shouldLikeMulticasts(const uint64_t now) const { return ((now - _lastUpdatedMulticast) >= ZT_MULTICAST_ANNOUNCE_PERIOD); }
|
||||
|
||||
/**
|
||||
* Set time we last updated multicasts for this peer
|
||||
*
|
||||
* @param now Current time
|
||||
*/
|
||||
inline void likingMulticasts(const uint64_t now) { _lastUpdatedMulticast = now; }
|
||||
|
||||
/**
|
||||
* 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().first <= _comRevocationThreshold)
|
||||
return false;
|
||||
return nconf.com.agreesWith(_com);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
{
|
||||
for(unsigned int i=0;i<ZT_MAX_CERTIFICATES_OF_OWNERSHIP;++i) {
|
||||
if (_remoteCoos[i]->id == ZT_MEMBERSHIP_CRED_ID_UNUSED)
|
||||
break;
|
||||
if ((_isCredentialTimestampValid(nconf,*_remoteCoos[i]))&&(_remoteCoos[i]->credential.owns(r)))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param nconf Network configuration
|
||||
* @param id Tag ID
|
||||
* @return Pointer to tag or NULL if not found
|
||||
*/
|
||||
const Tag *getTag(const NetworkConfig &nconf,const uint32_t id) const;
|
||||
|
||||
/**
|
||||
* Validate and add a credential if signature is okay and it's otherwise good
|
||||
*/
|
||||
AddCredentialResult addCredential(const RuntimeEnvironment *RR,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,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,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,const NetworkConfig &nconf,const Revocation &rev);
|
||||
|
||||
/**
|
||||
* Validate and add a credential if signature is okay and it's otherwise good
|
||||
*/
|
||||
AddCredentialResult addCredential(const RuntimeEnvironment *RR,const NetworkConfig &nconf,const CertificateOfOwnership &coo);
|
||||
|
||||
private:
|
||||
_RemoteCredential<Tag> *_newTag(const uint64_t id);
|
||||
_RemoteCredential<Capability> *_newCapability(const uint64_t id);
|
||||
_RemoteCredential<CertificateOfOwnership> *_newCoo(const uint64_t id);
|
||||
bool _revokeCom(const Revocation &rev);
|
||||
bool _revokeCap(const Revocation &rev,const uint64_t now);
|
||||
bool _revokeTag(const Revocation &rev,const uint64_t now);
|
||||
bool _revokeCoo(const Revocation &rev,const uint64_t now);
|
||||
|
||||
template<typename C>
|
||||
inline bool _isCredentialTimestampValid(const NetworkConfig &nconf,const _RemoteCredential<C> &remoteCredential) const
|
||||
{
|
||||
if (!remoteCredential.lastReceived)
|
||||
return false;
|
||||
const uint64_t ts = remoteCredential.credential.timestamp();
|
||||
return ( (((ts >= nconf.timestamp) ? (ts - nconf.timestamp) : (nconf.timestamp - ts)) <= nconf.credentialTimeMaxDelta) && (ts > remoteCredential.revocationThreshold) );
|
||||
}
|
||||
|
||||
// Last time we pushed MULTICAST_LIKE(s)
|
||||
uint64_t _lastUpdatedMulticast;
|
||||
|
||||
// Last time we pushed our COM to this peer
|
||||
uint64_t _lastPushedCom;
|
||||
|
||||
// Revocation threshold for COM or 0 if none
|
||||
uint64_t _comRevocationThreshold;
|
||||
|
||||
// Remote member's latest network COM
|
||||
CertificateOfMembership _com;
|
||||
|
||||
// Sorted (in ascending order of ID) arrays of pointers to remote credentials
|
||||
_RemoteCredential<Tag> *_remoteTags[ZT_MAX_NETWORK_TAGS];
|
||||
_RemoteCredential<Capability> *_remoteCaps[ZT_MAX_NETWORK_CAPABILITIES];
|
||||
_RemoteCredential<CertificateOfOwnership> *_remoteCoos[ZT_MAX_CERTIFICATES_OF_OWNERSHIP];
|
||||
|
||||
// This is the RAM allocated for remote credential cache objects
|
||||
_RemoteCredential<Tag> _tagMem[ZT_MAX_NETWORK_TAGS];
|
||||
_RemoteCredential<Capability> _capMem[ZT_MAX_NETWORK_CAPABILITIES];
|
||||
_RemoteCredential<CertificateOfOwnership> _cooMem[ZT_MAX_CERTIFICATES_OF_OWNERSHIP];
|
||||
|
||||
// Local credential push state tracking
|
||||
_LocalCredentialPushState _localTags[ZT_MAX_NETWORK_TAGS];
|
||||
_LocalCredentialPushState _localCaps[ZT_MAX_NETWORK_CAPABILITIES];
|
||||
_LocalCredentialPushState _localCoos[ZT_MAX_CERTIFICATES_OF_OWNERSHIP];
|
||||
};
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
||||
#endif
|
||||
132
zto/node/MulticastGroup.hpp
Normal file
132
zto/node/MulticastGroup.hpp
Normal file
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
* 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_MULTICASTGROUP_HPP
|
||||
#define ZT_MULTICASTGROUP_HPP
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "MAC.hpp"
|
||||
#include "InetAddress.hpp"
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
/**
|
||||
* A multicast group composed of a multicast MAC and a 32-bit ADI field
|
||||
*
|
||||
* ADI stands for additional distinguishing information. ADI is primarily for
|
||||
* adding additional information to broadcast (ff:ff:ff:ff:ff:ff) memberships,
|
||||
* since straight-up broadcast won't scale. Right now it's zero except for
|
||||
* IPv4 ARP, where it holds the IPv4 address itself to make ARP into a
|
||||
* selective multicast query that can scale.
|
||||
*
|
||||
* In the future we might add some kind of plugin architecture that can add
|
||||
* ADI for things like mDNS (multicast DNS) to improve the selectivity of
|
||||
* those protocols.
|
||||
*
|
||||
* MulticastGroup behaves as an immutable value object.
|
||||
*/
|
||||
class MulticastGroup
|
||||
{
|
||||
public:
|
||||
MulticastGroup()
|
||||
throw() :
|
||||
_mac(),
|
||||
_adi(0)
|
||||
{
|
||||
}
|
||||
|
||||
MulticastGroup(const MAC &m,uint32_t a)
|
||||
throw() :
|
||||
_mac(m),
|
||||
_adi(a)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Derive the multicast group used for address resolution (ARP/NDP) for an IP
|
||||
*
|
||||
* @param ip IP address (port field is ignored)
|
||||
* @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
|
||||
// the Multicast Group ADI field. Making V4 ARP work is basically why
|
||||
// ADI was added, as well as handling other things that want mindless
|
||||
// Ethernet broadcast to all.
|
||||
return MulticastGroup(MAC(0xffffffffffffULL),Utils::ntoh(*((const uint32_t *)ip.rawIpData())));
|
||||
} else if (ip.isV6()) {
|
||||
// IPv6 is better designed in this respect. We can compute the IPv6
|
||||
// multicast address directly from the IP address, and it gives us
|
||||
// 24 bits of uniqueness. Collisions aren't likely to be common enough
|
||||
// to care about.
|
||||
const unsigned char *a = (const unsigned char *)ip.rawIpData();
|
||||
return MulticastGroup(MAC(0x33,0x33,0xff,a[13],a[14],a[15]),0);
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Multicast address
|
||||
*/
|
||||
inline const MAC &mac() const throw() { return _mac; }
|
||||
|
||||
/**
|
||||
* @return Additional distinguishing information
|
||||
*/
|
||||
inline uint32_t adi() const throw() { return _adi; }
|
||||
|
||||
inline unsigned long hashCode() const throw() { 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()
|
||||
{
|
||||
if (_mac < g._mac)
|
||||
return true;
|
||||
else if (_mac == g._mac)
|
||||
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); }
|
||||
|
||||
private:
|
||||
MAC _mac;
|
||||
uint32_t _adi;
|
||||
};
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
||||
#endif
|
||||
394
zto/node/Multicaster.cpp
Normal file
394
zto/node/Multicaster.cpp
Normal file
@@ -0,0 +1,394 @@
|
||||
/*
|
||||
* 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 <algorithm>
|
||||
|
||||
#include "Constants.hpp"
|
||||
#include "RuntimeEnvironment.hpp"
|
||||
#include "SharedPtr.hpp"
|
||||
#include "Multicaster.hpp"
|
||||
#include "Topology.hpp"
|
||||
#include "Switch.hpp"
|
||||
#include "Packet.hpp"
|
||||
#include "Peer.hpp"
|
||||
#include "C25519.hpp"
|
||||
#include "CertificateOfMembership.hpp"
|
||||
#include "Node.hpp"
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
Multicaster::Multicaster(const RuntimeEnvironment *renv) :
|
||||
RR(renv),
|
||||
_groups(256),
|
||||
_gatherAuth(256)
|
||||
{
|
||||
}
|
||||
|
||||
Multicaster::~Multicaster()
|
||||
{
|
||||
}
|
||||
|
||||
void Multicaster::addMultiple(uint64_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));
|
||||
p += 5;
|
||||
}
|
||||
}
|
||||
|
||||
void Multicaster::remove(uint64_t nwid,const MulticastGroup &mg,const Address &member)
|
||||
{
|
||||
Mutex::Lock _l(_groups_m);
|
||||
MulticastGroupStatus *s = _groups.get(Multicaster::Key(nwid,mg));
|
||||
if (s) {
|
||||
for(std::vector<MulticastGroupMember>::iterator m(s->members.begin());m!=s->members.end();++m) {
|
||||
if (m->address == member) {
|
||||
s->members.erase(m);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int Multicaster::gather(const Address &queryingPeer,uint64_t nwid,const MulticastGroup &mg,Buffer<ZT_PROTO_MAX_PACKET_LENGTH> &appendTo,unsigned int limit) const
|
||||
{
|
||||
unsigned char *p;
|
||||
unsigned int added = 0,i,k,rptr,totalKnown = 0;
|
||||
uint64_t a,picked[(ZT_PROTO_MAX_PACKET_LENGTH / 5) + 2];
|
||||
|
||||
if (!limit)
|
||||
return 0;
|
||||
else if (limit > 0xffff)
|
||||
limit = 0xffff;
|
||||
|
||||
const unsigned int totalAt = appendTo.size();
|
||||
appendTo.addSize(4); // sizeof(uint32_t)
|
||||
const unsigned int addedAt = appendTo.size();
|
||||
appendTo.addSize(2); // sizeof(uint16_t)
|
||||
|
||||
{ // Return myself if I am a member of this group
|
||||
SharedPtr<Network> network(RR->node->network(nwid));
|
||||
if ((network)&&(network->subscribedToMulticastGroup(mg,true))) {
|
||||
RR->identity.address().appendTo(appendTo);
|
||||
++totalKnown;
|
||||
++added;
|
||||
}
|
||||
}
|
||||
|
||||
Mutex::Lock _l(_groups_m);
|
||||
|
||||
const MulticastGroupStatus *s = _groups.get(Multicaster::Key(nwid,mg));
|
||||
if ((s)&&(!s->members.empty())) {
|
||||
totalKnown += (unsigned int)s->members.size();
|
||||
|
||||
// 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)) {
|
||||
rptr = (unsigned int)RR->node->prng();
|
||||
|
||||
restart_member_scan:
|
||||
a = s->members[rptr % (unsigned int)s->members.size()].address.toInt();
|
||||
for(i=0;i<k;++i) {
|
||||
if (picked[i] == a) {
|
||||
++rptr;
|
||||
goto restart_member_scan;
|
||||
}
|
||||
}
|
||||
picked[k++] = a;
|
||||
|
||||
if (queryingPeer.toInt() != a) { // do not return the peer that is making the request as a result
|
||||
p = (unsigned char *)appendTo.appendField(ZT_ADDRESS_LENGTH);
|
||||
*(p++) = (unsigned char)((a >> 32) & 0xff);
|
||||
*(p++) = (unsigned char)((a >> 24) & 0xff);
|
||||
*(p++) = (unsigned char)((a >> 16) & 0xff);
|
||||
*(p++) = (unsigned char)((a >> 8) & 0xff);
|
||||
*p = (unsigned char)(a & 0xff);
|
||||
++added;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
std::vector<Address> Multicaster::getMembers(uint64_t nwid,const MulticastGroup &mg,unsigned int limit) const
|
||||
{
|
||||
std::vector<Address> ls;
|
||||
Mutex::Lock _l(_groups_m);
|
||||
const MulticastGroupStatus *s = _groups.get(Multicaster::Key(nwid,mg));
|
||||
if (!s)
|
||||
return ls;
|
||||
for(std::vector<MulticastGroupMember>::const_reverse_iterator m(s->members.rbegin());m!=s->members.rend();++m) {
|
||||
ls.push_back(m->address);
|
||||
if (ls.size() >= limit)
|
||||
break;
|
||||
}
|
||||
return ls;
|
||||
}
|
||||
|
||||
void Multicaster::send(
|
||||
unsigned int limit,
|
||||
uint64_t now,
|
||||
uint64_t nwid,
|
||||
bool disableCompression,
|
||||
const std::vector<Address> &alwaysSendTo,
|
||||
const MulticastGroup &mg,
|
||||
const MAC &src,
|
||||
unsigned int etherType,
|
||||
const void *data,
|
||||
unsigned int len)
|
||||
{
|
||||
unsigned long idxbuf[8194];
|
||||
unsigned long *indexes = idxbuf;
|
||||
|
||||
try {
|
||||
Mutex::Lock _l(_groups_m);
|
||||
MulticastGroupStatus &gs = _groups[Multicaster::Key(nwid,mg)];
|
||||
|
||||
if (!gs.members.empty()) {
|
||||
// Allocate a memory buffer if group is monstrous
|
||||
if (gs.members.size() > (sizeof(idxbuf) / sizeof(unsigned long)))
|
||||
indexes = new unsigned long[gs.members.size()];
|
||||
|
||||
// Generate a random permutation of member indexes
|
||||
for(unsigned long i=0;i<gs.members.size();++i)
|
||||
indexes[i] = i;
|
||||
for(unsigned long i=(unsigned long)gs.members.size()-1;i>0;--i) {
|
||||
unsigned long j = (unsigned long)RR->node->prng() % (i + 1);
|
||||
unsigned long tmp = indexes[j];
|
||||
indexes[j] = indexes[i];
|
||||
indexes[i] = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
if (gs.members.size() >= limit) {
|
||||
// Skip queue if we already have enough members to complete the send operation
|
||||
OutboundMulticast out;
|
||||
|
||||
out.init(
|
||||
RR,
|
||||
now,
|
||||
nwid,
|
||||
disableCompression,
|
||||
limit,
|
||||
1, // we'll still gather a little from peers to keep multicast list fresh
|
||||
src,
|
||||
mg,
|
||||
etherType,
|
||||
data,
|
||||
len);
|
||||
|
||||
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
|
||||
if (++count >= limit)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
++count;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
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;
|
||||
|
||||
Address explicitGatherPeers[16];
|
||||
unsigned int numExplicitGatherPeers = 0;
|
||||
SharedPtr<Peer> bestRoot(RR->topology->getUpstreamPeer());
|
||||
if (bestRoot)
|
||||
explicitGatherPeers[numExplicitGatherPeers++] = bestRoot->address();
|
||||
explicitGatherPeers[numExplicitGatherPeers++] = Network::controllerFor(nwid);
|
||||
SharedPtr<Network> network(RR->node->network(nwid));
|
||||
if (network) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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(nwid);
|
||||
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->node->expectReplyTo(outp.packetId());
|
||||
RR->sw->send(outp,true);
|
||||
}
|
||||
}
|
||||
|
||||
gs.txQueue.push_back(OutboundMulticast());
|
||||
OutboundMulticast &out = gs.txQueue.back();
|
||||
|
||||
out.init(
|
||||
RR,
|
||||
now,
|
||||
nwid,
|
||||
disableCompression,
|
||||
limit,
|
||||
gatherLimit,
|
||||
src,
|
||||
mg,
|
||||
etherType,
|
||||
data,
|
||||
len);
|
||||
|
||||
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);
|
||||
if (++count >= limit)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
++count;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch ( ... ) {} // this is a sanity check to catch any failures and make sure indexes[] still gets deleted
|
||||
|
||||
// Free allocated memory buffer if any
|
||||
if (indexes != idxbuf)
|
||||
delete [] indexes;
|
||||
}
|
||||
|
||||
void Multicaster::clean(uint64_t now)
|
||||
{
|
||||
{
|
||||
Mutex::Lock _l(_groups_m);
|
||||
Multicaster::Key *k = (Multicaster::Key *)0;
|
||||
MulticastGroupStatus *s = (MulticastGroupStatus *)0;
|
||||
Hashtable<Multicaster::Key,MulticastGroupStatus>::Iterator mm(_groups);
|
||||
while (mm.next(k,s)) {
|
||||
for(std::list<OutboundMulticast>::iterator tx(s->txQueue.begin());tx!=s->txQueue.end();) {
|
||||
if ((tx->expired(now))||(tx->atLimit()))
|
||||
s->txQueue.erase(tx++);
|
||||
else ++tx;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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::addCredential(const CertificateOfMembership &com,bool alreadyValidated)
|
||||
{
|
||||
if ((alreadyValidated)||(com.verify(RR) == 0)) {
|
||||
Mutex::Lock _l(_gatherAuth_m);
|
||||
_gatherAuth[_GatherAuthKey(com.networkId(),com.issuedTo())] = RR->node->now();
|
||||
}
|
||||
}
|
||||
|
||||
void Multicaster::_add(uint64_t now,uint64_t nwid,const MulticastGroup &mg,MulticastGroupStatus &gs,const Address &member)
|
||||
{
|
||||
// assumes _groups_m is locked
|
||||
|
||||
// Do not add self -- even if someone else returns it
|
||||
if (member == RR->identity.address())
|
||||
return;
|
||||
|
||||
for(std::vector<MulticastGroupMember>::iterator m(gs.members.begin());m!=gs.members.end();++m) {
|
||||
if (m->address == member) {
|
||||
m->timestamp = now;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
if (tx->atLimit())
|
||||
gs.txQueue.erase(tx++);
|
||||
else ++tx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ZeroTier
|
||||
234
zto/node/Multicaster.hpp
Normal file
234
zto/node/Multicaster.hpp
Normal file
@@ -0,0 +1,234 @@
|
||||
/*
|
||||
* 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_MULTICASTER_HPP
|
||||
#define ZT_MULTICASTER_HPP
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <list>
|
||||
|
||||
#include "Constants.hpp"
|
||||
#include "Hashtable.hpp"
|
||||
#include "Address.hpp"
|
||||
#include "MAC.hpp"
|
||||
#include "MulticastGroup.hpp"
|
||||
#include "OutboundMulticast.hpp"
|
||||
#include "Utils.hpp"
|
||||
#include "Mutex.hpp"
|
||||
#include "NonCopyable.hpp"
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
class RuntimeEnvironment;
|
||||
class CertificateOfMembership;
|
||||
class Packet;
|
||||
|
||||
/**
|
||||
* Database of known multicast peers within a network
|
||||
*/
|
||||
class Multicaster : NonCopyable
|
||||
{
|
||||
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();
|
||||
|
||||
/**
|
||||
* Add or update a member in a multicast group
|
||||
*
|
||||
* @param now Current time
|
||||
* @param nwid Network ID
|
||||
* @param mg Multicast group
|
||||
* @param member New member address
|
||||
*/
|
||||
inline void add(uint64_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 multiple addresses from a binary array of 5-byte address fields
|
||||
*
|
||||
* It's up to the caller to check bounds on the array before calling this.
|
||||
*
|
||||
* @param now Current time
|
||||
* @param nwid Network ID
|
||||
* @param mg Multicast group
|
||||
* @param addresses Raw binary addresses in big-endian format, as a series of 5-byte fields
|
||||
* @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);
|
||||
|
||||
/**
|
||||
* Remove a multicast group member (if present)
|
||||
*
|
||||
* @param nwid Network ID
|
||||
* @param mg Multicast group
|
||||
* @param member Member to unsubscribe
|
||||
*/
|
||||
void remove(uint64_t nwid,const MulticastGroup &mg,const Address &member);
|
||||
|
||||
/**
|
||||
* Append gather results to a packet by choosing registered multicast recipients at random
|
||||
*
|
||||
* This appends the following fields to the packet:
|
||||
* <[4] 32-bit total number of known members in this multicast group>
|
||||
* <[2] 16-bit number of members enumerated in this packet>
|
||||
* <[...] series of 5-byte ZeroTier addresses of enumerated members>
|
||||
*
|
||||
* If zero is returned, the first two fields will still have been appended.
|
||||
*
|
||||
* @param queryingPeer Peer asking for gather (to skip in results)
|
||||
* @param nwid Network ID
|
||||
* @param mg Multicast group
|
||||
* @param appendTo Packet to append to
|
||||
* @param limit Maximum number of 5-byte addresses to append
|
||||
* @return Number of addresses appended
|
||||
* @throws std::out_of_range Buffer overflow writing to packet
|
||||
*/
|
||||
unsigned int gather(const Address &queryingPeer,uint64_t nwid,const MulticastGroup &mg,Buffer<ZT_PROTO_MAX_PACKET_LENGTH> &appendTo,unsigned int limit) const;
|
||||
|
||||
/**
|
||||
* Get subscribers to a multicast group
|
||||
*
|
||||
* @param nwid Network ID
|
||||
* @param mg Multicast group
|
||||
*/
|
||||
std::vector<Address> getMembers(uint64_t nwid,const MulticastGroup &mg,unsigned int limit) const;
|
||||
|
||||
/**
|
||||
* Send a multicast
|
||||
*
|
||||
* @param limit Multicast limit
|
||||
* @param now Current time
|
||||
* @param nwid Network ID
|
||||
* @param disableCompression Disable packet payload compression?
|
||||
* @param alwaysSendTo Send to these peers first and even if not included in subscriber list
|
||||
* @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
|
||||
* @param data Packet data
|
||||
* @param len Length of packet data
|
||||
*/
|
||||
void send(
|
||||
unsigned int limit,
|
||||
uint64_t now,
|
||||
uint64_t nwid,
|
||||
bool disableCompression,
|
||||
const std::vector<Address> &alwaysSendTo,
|
||||
const MulticastGroup &mg,
|
||||
const MAC &src,
|
||||
unsigned int etherType,
|
||||
const void *data,
|
||||
unsigned int len);
|
||||
|
||||
/**
|
||||
* Clean up and resort database
|
||||
*
|
||||
* @param RR Runtime environment
|
||||
* @param now Current time
|
||||
*/
|
||||
void clean(uint64_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(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 uint64_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);
|
||||
|
||||
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
|
||||
|
||||
#endif
|
||||
186
zto/node/Mutex.hpp
Normal file
186
zto/node/Mutex.hpp
Normal file
@@ -0,0 +1,186 @@
|
||||
/*
|
||||
* 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_MUTEX_HPP
|
||||
#define ZT_MUTEX_HPP
|
||||
|
||||
#include "Constants.hpp"
|
||||
#include "NonCopyable.hpp"
|
||||
|
||||
#ifdef __UNIX_LIKE__
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <pthread.h>
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
class Mutex : NonCopyable
|
||||
{
|
||||
public:
|
||||
Mutex()
|
||||
throw()
|
||||
{
|
||||
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();
|
||||
}
|
||||
|
||||
inline void unlock() const
|
||||
throw()
|
||||
{
|
||||
(const_cast <Mutex *> (this))->unlock();
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses C++ contexts and constructor/destructor to lock/unlock automatically
|
||||
*/
|
||||
class Lock : NonCopyable
|
||||
{
|
||||
public:
|
||||
Lock(Mutex &m)
|
||||
throw() :
|
||||
_m(&m)
|
||||
{
|
||||
m.lock();
|
||||
}
|
||||
|
||||
Lock(const Mutex &m)
|
||||
throw() :
|
||||
_m(const_cast<Mutex *>(&m))
|
||||
{
|
||||
_m->lock();
|
||||
}
|
||||
|
||||
~Lock()
|
||||
{
|
||||
_m->unlock();
|
||||
}
|
||||
|
||||
private:
|
||||
Mutex *const _m;
|
||||
};
|
||||
|
||||
private:
|
||||
pthread_mutex_t _mh;
|
||||
};
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
||||
#endif // Apple / Linux
|
||||
|
||||
#ifdef __WINDOWS__
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <Windows.h>
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
class Mutex : NonCopyable
|
||||
{
|
||||
public:
|
||||
Mutex()
|
||||
throw()
|
||||
{
|
||||
InitializeCriticalSection(&_cs);
|
||||
}
|
||||
|
||||
~Mutex()
|
||||
{
|
||||
DeleteCriticalSection(&_cs);
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
public:
|
||||
Lock(Mutex &m)
|
||||
throw() :
|
||||
_m(&m)
|
||||
{
|
||||
m.lock();
|
||||
}
|
||||
|
||||
Lock(const Mutex &m)
|
||||
throw() :
|
||||
_m(const_cast<Mutex *>(&m))
|
||||
{
|
||||
_m->lock();
|
||||
}
|
||||
|
||||
~Lock()
|
||||
{
|
||||
_m->unlock();
|
||||
}
|
||||
|
||||
private:
|
||||
Mutex *const _m;
|
||||
};
|
||||
|
||||
private:
|
||||
CRITICAL_SECTION _cs;
|
||||
};
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
||||
#endif // _WIN32
|
||||
|
||||
#endif
|
||||
1605
zto/node/Network.cpp
Normal file
1605
zto/node/Network.cpp
Normal file
File diff suppressed because it is too large
Load Diff
405
zto/node/Network.hpp
Normal file
405
zto/node/Network.hpp
Normal file
@@ -0,0 +1,405 @@
|
||||
/*
|
||||
* 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_NETWORK_HPP
|
||||
#define ZT_NETWORK_HPP
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "../include/ZeroTierOne.h"
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "Constants.hpp"
|
||||
#include "NonCopyable.hpp"
|
||||
#include "Hashtable.hpp"
|
||||
#include "Address.hpp"
|
||||
#include "Mutex.hpp"
|
||||
#include "SharedPtr.hpp"
|
||||
#include "AtomicCounter.hpp"
|
||||
#include "MulticastGroup.hpp"
|
||||
#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;
|
||||
|
||||
/**
|
||||
* A virtual LAN
|
||||
*/
|
||||
class Network : NonCopyable
|
||||
{
|
||||
friend class SharedPtr<Network>;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Broadcast multicast group: ff:ff:ff:ff:ff:ff / 0
|
||||
*/
|
||||
static const MulticastGroup BROADCAST;
|
||||
|
||||
/**
|
||||
* Compute primary controller device ID from network ID
|
||||
*/
|
||||
static inline Address controllerFor(uint64_t nwid) throw() { return Address(nwid >> 24); }
|
||||
|
||||
/**
|
||||
* Construct a new network
|
||||
*
|
||||
* Note that init() should be called immediately after the network is
|
||||
* constructed to actually configure the port.
|
||||
*
|
||||
* @param renv Runtime environment
|
||||
* @param nwid Network ID
|
||||
* @param uptr Arbitrary pointer used by externally-facing API (for user use)
|
||||
*/
|
||||
Network(const RuntimeEnvironment *renv,uint64_t nwid,void *uptr);
|
||||
|
||||
~Network();
|
||||
|
||||
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 throw() { 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; }
|
||||
|
||||
/**
|
||||
* 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 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
|
||||
*/
|
||||
bool filterOutgoingPacket(
|
||||
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);
|
||||
|
||||
/**
|
||||
* 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 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
|
||||
*/
|
||||
int filterIncomingPacket(
|
||||
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 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;
|
||||
|
||||
/**
|
||||
* Subscribe to a multicast group
|
||||
*
|
||||
* @param mg New multicast group
|
||||
*/
|
||||
void multicastSubscribe(const MulticastGroup &mg);
|
||||
|
||||
/**
|
||||
* Unsubscribe from a multicast group
|
||||
*
|
||||
* @param mg Multicast group
|
||||
*/
|
||||
void multicastUnsubscribe(const MulticastGroup &mg);
|
||||
|
||||
/**
|
||||
* Handle an inbound network config chunk
|
||||
*
|
||||
* 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 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
|
||||
*/
|
||||
uint64_t handleConfigChunk(const uint64_t packetId,const Address &source,const Buffer<ZT_PROTO_MAX_PACKET_LENGTH> &chunk,unsigned int ptr);
|
||||
|
||||
/**
|
||||
* Set network configuration
|
||||
*
|
||||
* @param nconf Network configuration
|
||||
* @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);
|
||||
|
||||
/**
|
||||
* Set netconf failure to 'access denied' -- called in IncomingPacket when controller reports this
|
||||
*/
|
||||
inline void setAccessDenied()
|
||||
{
|
||||
Mutex::Lock _l(_lock);
|
||||
_netconfFailure = NETCONF_FAILURE_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set netconf failure to 'not found' -- called by IncomingPacket when controller reports this
|
||||
*/
|
||||
inline void setNotFound()
|
||||
{
|
||||
Mutex::Lock _l(_lock);
|
||||
_netconfFailure = NETCONF_FAILURE_NOT_FOUND;
|
||||
}
|
||||
|
||||
/**
|
||||
* Causes this network to request an updated configuration from its master node now
|
||||
*/
|
||||
void requestConfiguration();
|
||||
|
||||
/**
|
||||
* Determine whether this peer is permitted to communicate on this network
|
||||
*/
|
||||
bool gate(const SharedPtr<Peer> &peer);
|
||||
|
||||
/**
|
||||
* Do periodic cleanup and housekeeping tasks
|
||||
*/
|
||||
void clean();
|
||||
|
||||
/**
|
||||
* Push state to members such as multicast group memberships and latest COM (if needed)
|
||||
*/
|
||||
inline void sendUpdatesToMembers()
|
||||
{
|
||||
Mutex::Lock _l(_lock);
|
||||
_sendUpdatesToMembers((const MulticastGroup *)0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the node on this network that has this MAC behind it (if any)
|
||||
*
|
||||
* @param mac MAC address
|
||||
* @return ZeroTier address of bridge to this MAC
|
||||
*/
|
||||
inline Address findBridgeTo(const MAC &mac) const
|
||||
{
|
||||
Mutex::Lock _l(_lock);
|
||||
const Address *const br = _remoteBridgeRoutes.get(mac);
|
||||
return ((br) ? *br : Address());
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a bridge route
|
||||
*
|
||||
* @param mac MAC address of destination
|
||||
* @param addr Bridge this MAC is reachable behind
|
||||
*/
|
||||
void learnBridgeRoute(const MAC &mac,const Address &addr);
|
||||
|
||||
/**
|
||||
* Learn a multicast group that is bridged to our tap device
|
||||
*
|
||||
* @param mg Multicast group
|
||||
* @param now Current time
|
||||
*/
|
||||
void learnBridgedMulticastGroup(const MulticastGroup &mg,uint64_t now);
|
||||
|
||||
/**
|
||||
* Validate a credential and learn it if it passes certificate and other checks
|
||||
*/
|
||||
Membership::AddCredentialResult addCredential(const CertificateOfMembership &com);
|
||||
|
||||
/**
|
||||
* Validate a credential and learn it if it passes certificate and other checks
|
||||
*/
|
||||
inline Membership::AddCredentialResult addCredential(const Capability &cap)
|
||||
{
|
||||
if (cap.networkId() != _id)
|
||||
return Membership::ADD_REJECTED;
|
||||
Mutex::Lock _l(_lock);
|
||||
return _membership(cap.issuedTo()).addCredential(RR,_config,cap);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate a credential and learn it if it passes certificate and other checks
|
||||
*/
|
||||
inline Membership::AddCredentialResult addCredential(const Tag &tag)
|
||||
{
|
||||
if (tag.networkId() != _id)
|
||||
return Membership::ADD_REJECTED;
|
||||
Mutex::Lock _l(_lock);
|
||||
return _membership(tag.issuedTo()).addCredential(RR,_config,tag);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate a credential and learn it if it passes certificate and other checks
|
||||
*/
|
||||
Membership::AddCredentialResult addCredential(const Address &sentFrom,const Revocation &rev);
|
||||
|
||||
/**
|
||||
* Validate a credential and learn it if it passes certificate and other checks
|
||||
*/
|
||||
inline Membership::AddCredentialResult addCredential(const CertificateOfOwnership &coo)
|
||||
{
|
||||
if (coo.networkId() != _id)
|
||||
return Membership::ADD_REJECTED;
|
||||
Mutex::Lock _l(_lock);
|
||||
return _membership(coo.issuedTo()).addCredential(RR,_config,coo);
|
||||
}
|
||||
|
||||
/**
|
||||
* Force push credentials (COM, etc.) to a peer now
|
||||
*
|
||||
* @param to Destination peer address
|
||||
* @param now Current time
|
||||
*/
|
||||
inline void pushCredentialsNow(const Address &to,const uint64_t now)
|
||||
{
|
||||
Mutex::Lock _l(_lock);
|
||||
_membership(to).pushCredentials(RR,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.
|
||||
*/
|
||||
void destroy();
|
||||
|
||||
/**
|
||||
* Get this network's config for export via the ZT core API
|
||||
*
|
||||
* @param ec Buffer to fill with externally-visible network configuration
|
||||
*/
|
||||
inline void externalConfig(ZT_VirtualNetworkConfig *ec) const
|
||||
{
|
||||
Mutex::Lock _l(_lock);
|
||||
_externalConfig(ec);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Externally usable pointer-to-pointer exported via the core API
|
||||
*/
|
||||
inline void **userPtr() throw() { return &_uPtr; }
|
||||
|
||||
private:
|
||||
ZT_VirtualNetworkStatus _status() const;
|
||||
void _externalConfig(ZT_VirtualNetworkConfig *ec) const; // assumes _lock is locked
|
||||
bool _gate(const SharedPtr<Peer> &peer);
|
||||
void _sendUpdatesToMembers(const MulticastGroup *const newMulticastGroup);
|
||||
void _announceMulticastGroupsTo(const Address &peer,const std::vector<MulticastGroup> &allMulticastGroups);
|
||||
std::vector<MulticastGroup> _allMulticastGroups() const;
|
||||
Membership &_membership(const Address &a);
|
||||
|
||||
const RuntimeEnvironment *const RR;
|
||||
void *_uPtr;
|
||||
const uint64_t _id;
|
||||
uint64_t _lastAnnouncedMulticastGroupsUpstream;
|
||||
MAC _mac; // local MAC address
|
||||
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;
|
||||
uint64_t _lastConfigUpdate;
|
||||
|
||||
struct _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,
|
||||
NETCONF_FAILURE_ACCESS_DENIED,
|
||||
NETCONF_FAILURE_NOT_FOUND,
|
||||
NETCONF_FAILURE_INIT_FAILED
|
||||
} _netconfFailure;
|
||||
int _portError; // return value from port config callback
|
||||
|
||||
Hashtable<Address,Membership> _memberships;
|
||||
|
||||
Mutex _lock;
|
||||
|
||||
AtomicCounter __refCount;
|
||||
};
|
||||
|
||||
} // naemspace ZeroTier
|
||||
|
||||
#endif
|
||||
364
zto/node/NetworkConfig.cpp
Normal file
364
zto/node/NetworkConfig.cpp
Normal file
@@ -0,0 +1,364 @@
|
||||
/*
|
||||
* 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 <algorithm>
|
||||
|
||||
#include "NetworkConfig.hpp"
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
bool NetworkConfig::toDictionary(Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> &d,bool includeLegacy) const
|
||||
{
|
||||
Buffer<ZT_NETWORKCONFIG_DICT_CAPACITY> *tmp = new Buffer<ZT_NETWORKCONFIG_DICT_CAPACITY>();
|
||||
|
||||
try {
|
||||
d.clear();
|
||||
|
||||
// Try to put the more human-readable fields first
|
||||
|
||||
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_VERSION,(uint64_t)ZT_NETWORKCONFIG_VERSION)) return false;
|
||||
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_NETWORK_ID,this->networkId)) return false;
|
||||
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_TIMESTAMP,this->timestamp)) return false;
|
||||
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_CREDENTIAL_TIME_MAX_DELTA,this->credentialTimeMaxDelta)) return false;
|
||||
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_REVISION,this->revision)) return false;
|
||||
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_ISSUED_TO,this->issuedTo)) 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;
|
||||
|
||||
#ifdef ZT_SUPPORT_OLD_STYLE_NETCONF
|
||||
if (includeLegacy) {
|
||||
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_ALLOW_PASSIVE_BRIDGING_OLD,this->allowPassiveBridging())) return false;
|
||||
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_ENABLE_BROADCAST_OLD,this->enableBroadcast())) return false;
|
||||
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_PRIVATE_OLD,this->isPrivate())) return false;
|
||||
|
||||
std::string v4s;
|
||||
for(unsigned int i=0;i<staticIpCount;++i) {
|
||||
if (this->staticIps[i].ss_family == AF_INET) {
|
||||
if (v4s.length() > 0)
|
||||
v4s.push_back(',');
|
||||
v4s.append(this->staticIps[i].toString());
|
||||
}
|
||||
}
|
||||
if (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 tmp2[16];
|
||||
Utils::snprintf(tmp2,sizeof(tmp2),"%x",et);
|
||||
ets.append(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;
|
||||
}
|
||||
|
||||
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(',');
|
||||
ab.append(Address(this->specialists[i]).toString().c_str());
|
||||
}
|
||||
}
|
||||
if (ab.length() > 0) {
|
||||
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_ACTIVE_BRIDGES_OLD,ab.c_str())) return false;
|
||||
}
|
||||
}
|
||||
#endif // ZT_SUPPORT_OLD_STYLE_NETCONF
|
||||
|
||||
// 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->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;
|
||||
}
|
||||
|
||||
bool NetworkConfig::fromDictionary(const Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> &d)
|
||||
{
|
||||
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) {
|
||||
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) {
|
||||
delete tmp;
|
||||
return false;
|
||||
}
|
||||
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));
|
||||
|
||||
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
|
||||
this->type = (d.getB(ZT_NETWORKCONFIG_DICT_KEY_PRIVATE_OLD,true)) ? ZT_NETWORK_TYPE_PRIVATE : ZT_NETWORK_TYPE_PUBLIC;
|
||||
|
||||
if (d.get(ZT_NETWORKCONFIG_DICT_KEY_IPV4_STATIC_OLD,tmp2,sizeof(tmp2)) > 0) {
|
||||
char *saveptr = (char *)0;
|
||||
for(char *f=Utils::stok(tmp2,",",&saveptr);(f);f=Utils::stok((char *)0,",",&saveptr)) {
|
||||
if (this->staticIpCount >= ZT_MAX_ZT_ASSIGNED_ADDRESSES) break;
|
||||
InetAddress ip(f);
|
||||
if (!ip.isNetwork())
|
||||
this->staticIps[this->staticIpCount++] = ip;
|
||||
}
|
||||
}
|
||||
if (d.get(ZT_NETWORKCONFIG_DICT_KEY_IPV6_STATIC_OLD,tmp2,sizeof(tmp2)) > 0) {
|
||||
char *saveptr = (char *)0;
|
||||
for(char *f=Utils::stok(tmp2,",",&saveptr);(f);f=Utils::stok((char *)0,",",&saveptr)) {
|
||||
if (this->staticIpCount >= ZT_MAX_ZT_ASSIGNED_ADDRESSES) break;
|
||||
InetAddress ip(f);
|
||||
if (!ip.isNetwork())
|
||||
this->staticIps[this->staticIpCount++] = ip;
|
||||
}
|
||||
}
|
||||
|
||||
if (d.get(ZT_NETWORKCONFIG_DICT_KEY_CERTIFICATE_OF_MEMBERSHIP_OLD,tmp2,sizeof(tmp2)) > 0) {
|
||||
this->com.fromString(tmp2);
|
||||
}
|
||||
|
||||
if (d.get(ZT_NETWORKCONFIG_DICT_KEY_ALLOWED_ETHERNET_TYPES_OLD,tmp2,sizeof(tmp2)) > 0) {
|
||||
char *saveptr = (char *)0;
|
||||
for(char *f=Utils::stok(tmp2,",",&saveptr);(f);f=Utils::stok((char *)0,",",&saveptr)) {
|
||||
unsigned int et = Utils::hexStrToUInt(f) & 0xffff;
|
||||
if ((this->ruleCount + 2) > ZT_MAX_NETWORK_RULES) break;
|
||||
if (et > 0) {
|
||||
this->rules[this->ruleCount].t = (uint8_t)ZT_NETWORK_RULE_MATCH_ETHERTYPE;
|
||||
this->rules[this->ruleCount].v.etherType = (uint16_t)et;
|
||||
++this->ruleCount;
|
||||
}
|
||||
this->rules[this->ruleCount++].t = (uint8_t)ZT_NETWORK_RULE_ACTION_ACCEPT;
|
||||
}
|
||||
} else {
|
||||
this->rules[0].t = ZT_NETWORK_RULE_ACTION_ACCEPT;
|
||||
this->ruleCount = 1;
|
||||
}
|
||||
|
||||
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(Utils::hexStrToU64(f)),ZT_NETWORKCONFIG_SPECIALIST_TYPE_ACTIVE_BRIDGE);
|
||||
}
|
||||
}
|
||||
#else
|
||||
delete tmp;
|
||||
return false;
|
||||
#endif // ZT_SUPPORT_OLD_STYLE_NETCONF
|
||||
} else {
|
||||
// Otherwise we can use the new fields
|
||||
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_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_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 < 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)) {
|
||||
unsigned int p = 0;
|
||||
while ((p < tmp->size())&&(routeCount < ZT_MAX_NETWORK_ROUTES)) {
|
||||
p += reinterpret_cast<InetAddress *>(&(this->routes[this->routeCount].target))->deserialize(*tmp,p);
|
||||
p += reinterpret_cast<InetAddress *>(&(this->routes[this->routeCount].via))->deserialize(*tmp,p);
|
||||
this->routes[this->routeCount].flags = tmp->at<uint16_t>(p); p += 2;
|
||||
this->routes[this->routeCount].metric = tmp->at<uint16_t>(p); p += 2;
|
||||
++this->routeCount;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
if (d.get(ZT_NETWORKCONFIG_DICT_KEY_RULES,*tmp)) {
|
||||
this->ruleCount = 0;
|
||||
unsigned int p = 0;
|
||||
Capability::deserializeRules(*tmp,p,this->rules,this->ruleCount,ZT_MAX_NETWORK_RULES);
|
||||
}
|
||||
}
|
||||
|
||||
//printf("~~~\n%s\n~~~\n",d.data());
|
||||
//dump();
|
||||
//printf("~~~\n");
|
||||
|
||||
delete tmp;
|
||||
return true;
|
||||
} catch ( ... ) {
|
||||
delete tmp;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ZeroTier
|
||||
556
zto/node/NetworkConfig.hpp
Normal file
556
zto/node/NetworkConfig.hpp
Normal file
@@ -0,0 +1,556 @@
|
||||
/*
|
||||
* 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_NETWORKCONFIG_HPP
|
||||
#define ZT_NETWORKCONFIG_HPP
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <vector>
|
||||
#include <stdexcept>
|
||||
#include <algorithm>
|
||||
|
||||
#include "../include/ZeroTierOne.h"
|
||||
|
||||
#include "Constants.hpp"
|
||||
#include "Buffer.hpp"
|
||||
#include "InetAddress.hpp"
|
||||
#include "MulticastGroup.hpp"
|
||||
#include "Address.hpp"
|
||||
#include "CertificateOfMembership.hpp"
|
||||
#include "CertificateOfOwnership.hpp"
|
||||
#include "Capability.hpp"
|
||||
#include "Tag.hpp"
|
||||
#include "Dictionary.hpp"
|
||||
#include "Identity.hpp"
|
||||
#include "Utils.hpp"
|
||||
|
||||
/**
|
||||
* 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_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: allow passive bridging (experimental)
|
||||
*/
|
||||
#define ZT_NETWORKCONFIG_FLAG_ALLOW_PASSIVE_BRIDGING 0x0000000000000001ULL
|
||||
|
||||
/**
|
||||
* Flag: enable broadcast
|
||||
*/
|
||||
#define ZT_NETWORKCONFIG_FLAG_ENABLE_BROADCAST 0x0000000000000002ULL
|
||||
|
||||
/**
|
||||
* Flag: enable IPv6 NDP emulation for certain V6 address patterns
|
||||
*/
|
||||
#define ZT_NETWORKCONFIG_FLAG_ENABLE_IPV6_NDP_EMULATION 0x0000000000000004ULL
|
||||
|
||||
/**
|
||||
* Flag: result of unrecognized MATCH entries in a rules table: match if set, no-match if clear
|
||||
*/
|
||||
#define ZT_NETWORKCONFIG_FLAG_RULES_RESULT_OF_UNSUPPORTED_MATCH 0x0000000000000008ULL
|
||||
|
||||
/**
|
||||
* Flag: disable frame compression
|
||||
*/
|
||||
#define ZT_NETWORKCONFIG_FLAG_DISABLE_COMPRESSION 0x0000000000000010ULL
|
||||
|
||||
/**
|
||||
* Device is an active bridge
|
||||
*/
|
||||
#define ZT_NETWORKCONFIG_SPECIALIST_TYPE_ACTIVE_BRIDGE 0x0000020000000000ULL
|
||||
|
||||
/**
|
||||
* Anchors are stable devices on this network that can cache multicast info, etc.
|
||||
*/
|
||||
#define ZT_NETWORKCONFIG_SPECIALIST_TYPE_ANCHOR 0x0000040000000000ULL
|
||||
|
||||
/**
|
||||
* Device can send CIRCUIT_TESTs for this network
|
||||
*/
|
||||
#define ZT_NETWORKCONFIG_SPECIALIST_TYPE_CIRCUIT_TESTER 0x0000080000000000ULL
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
// 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 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"
|
||||
// network ID
|
||||
#define ZT_NETWORKCONFIG_DICT_KEY_NETWORK_ID "nwid"
|
||||
// integer(hex)
|
||||
#define ZT_NETWORKCONFIG_DICT_KEY_TIMESTAMP "ts"
|
||||
// integer(hex)
|
||||
#define ZT_NETWORKCONFIG_DICT_KEY_REVISION "r"
|
||||
// address of member
|
||||
#define ZT_NETWORKCONFIG_DICT_KEY_ISSUED_TO "id"
|
||||
// flags(hex)
|
||||
#define ZT_NETWORKCONFIG_DICT_KEY_FLAGS "f"
|
||||
// integer(hex)
|
||||
#define ZT_NETWORKCONFIG_DICT_KEY_MULTICAST_LIMIT "ml"
|
||||
// network type (hex)
|
||||
#define ZT_NETWORKCONFIG_DICT_KEY_TYPE "t"
|
||||
// text
|
||||
#define ZT_NETWORKCONFIG_DICT_KEY_NAME "n"
|
||||
// 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)
|
||||
#define ZT_NETWORKCONFIG_DICT_KEY_SPECIALISTS "S"
|
||||
// routes (binary blob)
|
||||
#define ZT_NETWORKCONFIG_DICT_KEY_ROUTES "RT"
|
||||
// static IPs (binary blob)
|
||||
#define ZT_NETWORKCONFIG_DICT_KEY_STATIC_IPS "I"
|
||||
// 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"
|
||||
// curve25519 signature
|
||||
#define ZT_NETWORKCONFIG_DICT_KEY_SIGNATURE "C25519"
|
||||
|
||||
// Legacy fields -- these are obsoleted but are included when older clients query
|
||||
|
||||
// boolean (now a flag)
|
||||
#define ZT_NETWORKCONFIG_DICT_KEY_ALLOW_PASSIVE_BRIDGING_OLD "pb"
|
||||
// boolean (now a flag)
|
||||
#define ZT_NETWORKCONFIG_DICT_KEY_ENABLE_BROADCAST_OLD "eb"
|
||||
// IP/bits[,IP/bits,...]
|
||||
// Note that IPs that end in all zeroes are routes with no assignment in them.
|
||||
#define ZT_NETWORKCONFIG_DICT_KEY_IPV4_STATIC_OLD "v4s"
|
||||
// IP/bits[,IP/bits,...]
|
||||
// Note that IPs that end in all zeroes are routes with no assignment in them.
|
||||
#define ZT_NETWORKCONFIG_DICT_KEY_IPV6_STATIC_OLD "v6s"
|
||||
// 0/1
|
||||
#define ZT_NETWORKCONFIG_DICT_KEY_PRIVATE_OLD "p"
|
||||
// integer(hex)[,integer(hex),...]
|
||||
#define ZT_NETWORKCONFIG_DICT_KEY_ALLOWED_ETHERNET_TYPES_OLD "et"
|
||||
// string-serialized CertificateOfMembership
|
||||
#define ZT_NETWORKCONFIG_DICT_KEY_CERTIFICATE_OF_MEMBERSHIP_OLD "com"
|
||||
// node[,node,...]
|
||||
#define ZT_NETWORKCONFIG_DICT_KEY_ACTIVE_BRIDGES_OLD "ab"
|
||||
// node;IP/port[,node;IP/port]
|
||||
#define ZT_NETWORKCONFIG_DICT_KEY_RELAYS_OLD "rl"
|
||||
|
||||
// End legacy fields
|
||||
|
||||
/**
|
||||
* Network configuration received from network controller nodes
|
||||
*
|
||||
* This is a memcpy()'able structure and is safe (in a crash sense) to modify
|
||||
* without locks.
|
||||
*/
|
||||
class NetworkConfig
|
||||
{
|
||||
public:
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write this network config to a dictionary for transport
|
||||
*
|
||||
* @param d Dictionary
|
||||
* @param includeLegacy If true, include legacy fields for old node versions
|
||||
* @return True if dictionary was successfully created, false if e.g. overflow
|
||||
*/
|
||||
bool toDictionary(Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> &d,bool includeLegacy) const;
|
||||
|
||||
/**
|
||||
* Read this network config from a 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); }
|
||||
|
||||
/**
|
||||
* @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); }
|
||||
|
||||
/**
|
||||
* @return True if frames should not be compressed
|
||||
*/
|
||||
inline bool disableCompression() const throw() { 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); }
|
||||
|
||||
/**
|
||||
* @return Network type is private (certificate access control)
|
||||
*/
|
||||
inline bool isPrivate() const throw() { return (this->type == ZT_NETWORK_TYPE_PRIVATE); }
|
||||
|
||||
/**
|
||||
* @return ZeroTier addresses of devices on this network designated as active bridges
|
||||
*/
|
||||
inline std::vector<Address> activeBridges() const
|
||||
{
|
||||
std::vector<Address> r;
|
||||
for(unsigned int i=0;i<specialistCount;++i) {
|
||||
if ((specialists[i] & ZT_NETWORKCONFIG_SPECIALIST_TYPE_ACTIVE_BRIDGE) != 0)
|
||||
r.push_back(Address(specialists[i]));
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ZeroTier addresses of "anchor" devices on this network
|
||||
*/
|
||||
inline std::vector<Address> anchors() const
|
||||
{
|
||||
std::vector<Address> r;
|
||||
for(unsigned int i=0;i<specialistCount;++i) {
|
||||
if ((specialists[i] & ZT_NETWORKCONFIG_SPECIALIST_TYPE_ANCHOR) != 0)
|
||||
r.push_back(Address(specialists[i]));
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param a Address to check
|
||||
* @return True if address is an anchor
|
||||
*/
|
||||
inline bool isAnchor(const Address &a) const
|
||||
{
|
||||
for(unsigned int i=0;i<specialistCount;++i) {
|
||||
if ((a == specialists[i])&&((specialists[i] & ZT_NETWORKCONFIG_SPECIALIST_TYPE_ANCHOR) != 0))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param byPeer Address to check
|
||||
* @return True if this peer is allowed to do circuit tests on this network (controller is always true)
|
||||
*/
|
||||
inline bool circuitTestingAllowed(const Address &byPeer) const
|
||||
{
|
||||
if (byPeer.toInt() == ((networkId >> 24) & 0xffffffffffULL))
|
||||
return true;
|
||||
for(unsigned int i=0;i<specialistCount;++i) {
|
||||
if ((byPeer == specialists[i])&&((specialists[i] & ZT_NETWORKCONFIG_SPECIALIST_TYPE_CIRCUIT_TESTER) != 0))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return True if this network config is non-NULL
|
||||
*/
|
||||
inline operator bool() const throw() { 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)); }
|
||||
|
||||
/**
|
||||
* Add a specialist or mask flags if already present
|
||||
*
|
||||
* This masks the existing flags if the specialist is already here or adds
|
||||
* it otherwise.
|
||||
*
|
||||
* @param a Address of specialist
|
||||
* @param f Flags (OR of specialist role/type flags)
|
||||
* @return True if successfully masked or added
|
||||
*/
|
||||
inline bool addSpecialist(const Address &a,const uint64_t f)
|
||||
{
|
||||
const uint64_t aint = a.toInt();
|
||||
for(unsigned int i=0;i<specialistCount;++i) {
|
||||
if ((specialists[i] & 0xffffffffffULL) == aint) {
|
||||
specialists[i] |= f;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (specialistCount < ZT_MAX_NETWORK_SPECIALISTS) {
|
||||
specialists[specialistCount++] = f | aint;
|
||||
return true;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
/*
|
||||
inline void dump() const
|
||||
{
|
||||
printf("networkId==%.16llx\n",networkId);
|
||||
printf("timestamp==%llu\n",timestamp);
|
||||
printf("credentialTimeMaxDelta==%llu\n",credentialTimeMaxDelta);
|
||||
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("ruleCount==%u\n",ruleCount);
|
||||
printf("name==%s\n",name);
|
||||
printf("com==%s\n",com.toString().c_str());
|
||||
}
|
||||
*/
|
||||
|
||||
/**
|
||||
* Network ID that this configuration applies to
|
||||
*/
|
||||
uint64_t networkId;
|
||||
|
||||
/**
|
||||
* Controller-side time of config generation/issue
|
||||
*/
|
||||
uint64_t timestamp;
|
||||
|
||||
/**
|
||||
* Max difference between timestamp and tag/capability timestamp
|
||||
*/
|
||||
uint64_t credentialTimeMaxDelta;
|
||||
|
||||
/**
|
||||
* Controller-side revision counter for this configuration
|
||||
*/
|
||||
uint64_t revision;
|
||||
|
||||
/**
|
||||
* Address of device to which this config is issued
|
||||
*/
|
||||
Address issuedTo;
|
||||
|
||||
/**
|
||||
* Flags (64-bit)
|
||||
*/
|
||||
uint64_t flags;
|
||||
|
||||
/**
|
||||
* Maximum number of recipients per multicast (not including active bridges)
|
||||
*/
|
||||
unsigned int multicastLimit;
|
||||
|
||||
/**
|
||||
* Number of specialists
|
||||
*/
|
||||
unsigned int specialistCount;
|
||||
|
||||
/**
|
||||
* Number of routes
|
||||
*/
|
||||
unsigned int routeCount;
|
||||
|
||||
/**
|
||||
* Number of ZT-managed static IP assignments
|
||||
*/
|
||||
unsigned int staticIpCount;
|
||||
|
||||
/**
|
||||
* 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
|
||||
*
|
||||
* For each entry the least significant 40 bits are the device's ZeroTier
|
||||
* address and the most significant 24 bits are flags indicating its role.
|
||||
*/
|
||||
uint64_t specialists[ZT_MAX_NETWORK_SPECIALISTS];
|
||||
|
||||
/**
|
||||
* Statically defined "pushed" routes (including default gateways)
|
||||
*/
|
||||
ZT_VirtualNetworkRoute routes[ZT_MAX_NETWORK_ROUTES];
|
||||
|
||||
/**
|
||||
* Static IP assignments
|
||||
*/
|
||||
InetAddress staticIps[ZT_MAX_ZT_ASSIGNED_ADDRESSES];
|
||||
|
||||
/**
|
||||
* 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)
|
||||
*/
|
||||
ZT_VirtualNetworkType type;
|
||||
|
||||
/**
|
||||
* Network short name or empty string if not defined
|
||||
*/
|
||||
char name[ZT_MAX_NETWORK_SHORT_NAME_LENGTH + 1];
|
||||
|
||||
/**
|
||||
* Certficiate of membership (for private networks)
|
||||
*/
|
||||
CertificateOfMembership com;
|
||||
};
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
||||
#endif
|
||||
116
zto/node/NetworkController.hpp
Normal file
116
zto/node/NetworkController.hpp
Normal file
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
* 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_NETWORKCONFIGMASTER_HPP
|
||||
#define ZT_NETWORKCONFIGMASTER_HPP
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "Constants.hpp"
|
||||
#include "Dictionary.hpp"
|
||||
#include "NetworkConfig.hpp"
|
||||
#include "Revocation.hpp"
|
||||
#include "Address.hpp"
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
class Identity;
|
||||
struct InetAddress;
|
||||
|
||||
/**
|
||||
* Interface for network controller implementations
|
||||
*/
|
||||
class NetworkController
|
||||
{
|
||||
public:
|
||||
enum ErrorCode
|
||||
{
|
||||
NC_ERROR_NONE = 0,
|
||||
NC_ERROR_OBJECT_NOT_FOUND = 1,
|
||||
NC_ERROR_ACCESS_DENIED = 2,
|
||||
NC_ERROR_INTERNAL_SERVER_ERROR = 3
|
||||
};
|
||||
|
||||
/**
|
||||
* 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() {}
|
||||
|
||||
/**
|
||||
* Called when this is added to a Node to initialize and supply info
|
||||
*
|
||||
* @param signingId Identity for signing of network configurations, certs, etc.
|
||||
* @param sender Sender implementation for sending replies or config pushes
|
||||
*/
|
||||
virtual void init(const Identity &signingId,Sender *sender) = 0;
|
||||
|
||||
/**
|
||||
* Handle a network configuration request
|
||||
*
|
||||
* @param 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)
|
||||
* @return Returns NETCONF_QUERY_OK if result 'nc' is valid, or an error code on error
|
||||
*/
|
||||
virtual void request(
|
||||
uint64_t nwid,
|
||||
const InetAddress &fromAddr,
|
||||
uint64_t requestPacketId,
|
||||
const Identity &identity,
|
||||
const Dictionary<ZT_NETWORKCONFIG_METADATA_DICT_CAPACITY> &metaData) = 0;
|
||||
};
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
||||
#endif
|
||||
1130
zto/node/Node.cpp
Normal file
1130
zto/node/Node.cpp
Normal file
File diff suppressed because it is too large
Load Diff
323
zto/node/Node.hpp
Normal file
323
zto/node/Node.hpp
Normal file
@@ -0,0 +1,323 @@
|
||||
/*
|
||||
* 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_NODE_HPP
|
||||
#define ZT_NODE_HPP
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
#include "Constants.hpp"
|
||||
|
||||
#include "../include/ZeroTierOne.h"
|
||||
|
||||
#include "RuntimeEnvironment.hpp"
|
||||
#include "InetAddress.hpp"
|
||||
#include "Mutex.hpp"
|
||||
#include "MAC.hpp"
|
||||
#include "Network.hpp"
|
||||
#include "Path.hpp"
|
||||
#include "Salsa20.hpp"
|
||||
#include "NetworkController.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
|
||||
|
||||
// Size of PRNG stream buffer
|
||||
#define ZT_NODE_PRNG_BUF_SIZE 64
|
||||
|
||||
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 : public NetworkController::Sender
|
||||
{
|
||||
public:
|
||||
Node(void *uptr,const struct ZT_Node_Callbacks *callbacks,uint64_t now);
|
||||
virtual ~Node();
|
||||
|
||||
// Get rid of alignment warnings on 32-bit Windows and possibly improve performance
|
||||
#ifdef __WINDOWS__
|
||||
void * operator new(size_t i) { return _mm_malloc(i,16); }
|
||||
void operator delete(void* p) { _mm_free(p); }
|
||||
#endif
|
||||
|
||||
// Public API Functions ----------------------------------------------------
|
||||
|
||||
ZT_ResultCode processWirePacket(
|
||||
uint64_t now,
|
||||
const struct sockaddr_storage *localAddress,
|
||||
const struct sockaddr_storage *remoteAddress,
|
||||
const void *packetData,
|
||||
unsigned int packetLength,
|
||||
volatile uint64_t *nextBackgroundTaskDeadline);
|
||||
ZT_ResultCode processVirtualNetworkFrame(
|
||||
uint64_t now,
|
||||
uint64_t nwid,
|
||||
uint64_t sourceMac,
|
||||
uint64_t destMac,
|
||||
unsigned int etherType,
|
||||
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);
|
||||
ZT_ResultCode multicastUnsubscribe(uint64_t nwid,uint64_t multicastGroup,unsigned long multicastAdi);
|
||||
ZT_ResultCode orbit(uint64_t moonWorldId,uint64_t moonSeed);
|
||||
ZT_ResultCode deorbit(uint64_t moonWorldId);
|
||||
uint64_t address() const;
|
||||
void status(ZT_NodeStatus *status) const;
|
||||
ZT_PeerList *peers() const;
|
||||
ZT_VirtualNetworkConfig *networkConfig(uint64_t nwid) const;
|
||||
ZT_VirtualNetworkList *networks() const;
|
||||
void freeQueryResult(void *qr);
|
||||
int addLocalInterfaceAddress(const struct sockaddr_storage *addr);
|
||||
void clearLocalInterfaceAddresses();
|
||||
int sendUserMessage(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);
|
||||
|
||||
// Internal functions ------------------------------------------------------
|
||||
|
||||
inline uint64_t now() const throw() { return _now; }
|
||||
|
||||
inline bool putPacket(const InetAddress &localAddress,const InetAddress &addr,const void *data,unsigned int len,unsigned int ttl = 0)
|
||||
{
|
||||
return (_cb.wirePacketSendFunction(
|
||||
reinterpret_cast<ZT_Node *>(this),
|
||||
_uPtr,
|
||||
reinterpret_cast<const struct sockaddr_storage *>(&localAddress),
|
||||
reinterpret_cast<const struct sockaddr_storage *>(&addr),
|
||||
data,
|
||||
len,
|
||||
ttl) == 0);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
_cb.virtualNetworkFrameFunction(
|
||||
reinterpret_cast<ZT_Node *>(this),
|
||||
_uPtr,
|
||||
nwid,
|
||||
nuptr,
|
||||
source.toInt(),
|
||||
dest.toInt(),
|
||||
etherType,
|
||||
vlanId,
|
||||
data,
|
||||
len);
|
||||
}
|
||||
|
||||
inline SharedPtr<Network> network(uint64_t nwid) const
|
||||
{
|
||||
Mutex::Lock _l(_networks_m);
|
||||
return _network(nwid);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
return nw;
|
||||
}
|
||||
|
||||
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 (_cb.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) { _cb.dataStorePutFunction(reinterpret_cast<ZT_Node *>(this),_uPtr,name,(const void *)0,0,0); }
|
||||
std::string dataStoreGet(const char *name);
|
||||
|
||||
inline void postEvent(ZT_Event ev,const void *md = (const void *)0) { _cb.eventCallback(reinterpret_cast<ZT_Node *>(this),_uPtr,ev,md); }
|
||||
|
||||
inline int configureVirtualNetworkPort(uint64_t nwid,void **nuptr,ZT_VirtualNetworkConfigOperation op,const ZT_VirtualNetworkConfig *nc) { return _cb.virtualNetworkConfigFunction(reinterpret_cast<ZT_Node *>(this),_uPtr,nwid,nuptr,op,nc); }
|
||||
|
||||
inline bool online() const throw() { return _online; }
|
||||
|
||||
#ifdef ZT_TRACE
|
||||
void postTrace(const char *module,unsigned int line,const char *fmt,...);
|
||||
#endif
|
||||
|
||||
bool shouldUsePathForZeroTierTraffic(const Address &ztaddr,const InetAddress &localAddress,const InetAddress &remoteAddress);
|
||||
inline bool externalPathLookup(const Address &ztaddr,int family,InetAddress &addr) { return ( (_cb.pathLookupFunction) ? (_cb.pathLookupFunction(reinterpret_cast<ZT_Node *>(this),_uPtr,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);
|
||||
|
||||
World planet() const;
|
||||
std::vector<World> moons() const;
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
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 uint64_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);
|
||||
|
||||
private:
|
||||
inline SharedPtr<Network> _network(uint64_t nwid) const
|
||||
{
|
||||
// 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>();
|
||||
}
|
||||
|
||||
RuntimeEnvironment _RR;
|
||||
RuntimeEnvironment *RR;
|
||||
void *_uPtr; // _uptr (lower case) is reserved in Visual Studio :P
|
||||
ZT_Node_Callbacks _cb;
|
||||
|
||||
// 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];
|
||||
|
||||
// Time of last identity verification indexed by InetAddress.rateGateHash() -- used in IncomingPacket::_doHELLO() via rateGateIdentityVerification()
|
||||
uint64_t _lastIdentityVerification[16384];
|
||||
|
||||
std::vector< std::pair< 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[ZT_NODE_PRNG_BUF_SIZE]; // repeatedly encrypted with _prng to yield a high-quality non-crypto PRNG stream
|
||||
|
||||
uint64_t _now;
|
||||
uint64_t _lastPingCheck;
|
||||
uint64_t _lastHousekeepingRun;
|
||||
bool _online;
|
||||
};
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
||||
#endif
|
||||
38
zto/node/NonCopyable.hpp
Normal file
38
zto/node/NonCopyable.hpp
Normal file
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 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
|
||||
101
zto/node/OutboundMulticast.cpp
Normal file
101
zto/node/OutboundMulticast.cpp
Normal file
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* 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 "RuntimeEnvironment.hpp"
|
||||
#include "OutboundMulticast.hpp"
|
||||
#include "Switch.hpp"
|
||||
#include "Network.hpp"
|
||||
#include "Node.hpp"
|
||||
#include "Peer.hpp"
|
||||
#include "Topology.hpp"
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
void OutboundMulticast::init(
|
||||
const RuntimeEnvironment *RR,
|
||||
uint64_t timestamp,
|
||||
uint64_t nwid,
|
||||
bool disableCompression,
|
||||
unsigned int limit,
|
||||
unsigned int gatherLimit,
|
||||
const MAC &src,
|
||||
const MulticastGroup &dest,
|
||||
unsigned int etherType,
|
||||
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;
|
||||
|
||||
if (gatherLimit) flags |= 0x02;
|
||||
|
||||
/*
|
||||
TRACE(">>MC %.16llx INIT %.16llx/%s limit %u gatherLimit %u from %s to %s length %u",
|
||||
(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);
|
||||
*/
|
||||
|
||||
_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();
|
||||
|
||||
memcpy(_frameData,payload,_frameLen);
|
||||
}
|
||||
|
||||
void OutboundMulticast::sendOnly(const RuntimeEnvironment *RR,const Address &toAddr)
|
||||
{
|
||||
const SharedPtr<Network> nw(RR->node->network(_nwid));
|
||||
const Address toAddr2(toAddr);
|
||||
if ((nw)&&(nw->filterOutgoingPacket(true,RR->identity.address(),toAddr2,_macSrc,_macDest,_frameData,_frameLen,_etherType,0))) {
|
||||
//TRACE(">>MC %.16llx -> %s",(unsigned long long)this,toAddr.toString().c_str());
|
||||
_packet.newInitializationVector();
|
||||
_packet.setDestination(toAddr2);
|
||||
RR->node->expectReplyTo(_packet.packetId());
|
||||
RR->sw->send(_packet,true);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ZeroTier
|
||||
150
zto/node/OutboundMulticast.hpp
Normal file
150
zto/node/OutboundMulticast.hpp
Normal file
@@ -0,0 +1,150 @@
|
||||
/*
|
||||
* 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_OUTBOUNDMULTICAST_HPP
|
||||
#define ZT_OUTBOUNDMULTICAST_HPP
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
|
||||
#include "Constants.hpp"
|
||||
#include "MAC.hpp"
|
||||
#include "MulticastGroup.hpp"
|
||||
#include "Address.hpp"
|
||||
#include "Packet.hpp"
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
class CertificateOfMembership;
|
||||
class RuntimeEnvironment;
|
||||
|
||||
/**
|
||||
* An outbound multicast packet
|
||||
*
|
||||
* This object isn't guarded by a mutex; caller must synchronize access.
|
||||
*/
|
||||
class OutboundMulticast
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Create an uninitialized outbound multicast
|
||||
*
|
||||
* It must be initialized with init().
|
||||
*/
|
||||
OutboundMulticast() {}
|
||||
|
||||
/**
|
||||
* Initialize outbound multicast
|
||||
*
|
||||
* @param RR Runtime environment
|
||||
* @param timestamp Creation time
|
||||
* @param nwid Network ID
|
||||
* @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
|
||||
* @param dest Destination multicast group (MAC + ADI)
|
||||
* @param etherType 16-bit Ethernet type ID
|
||||
* @param payload Data
|
||||
* @param len Length of data
|
||||
* @throws std::out_of_range Data too large to fit in a MULTICAST_FRAME
|
||||
*/
|
||||
void init(
|
||||
const RuntimeEnvironment *RR,
|
||||
uint64_t timestamp,
|
||||
uint64_t nwid,
|
||||
bool disableCompression,
|
||||
unsigned int limit,
|
||||
unsigned int gatherLimit,
|
||||
const MAC &src,
|
||||
const MulticastGroup &dest,
|
||||
unsigned int etherType,
|
||||
const void *payload,
|
||||
unsigned int len);
|
||||
|
||||
/**
|
||||
* @return Multicast creation time
|
||||
*/
|
||||
inline uint64_t timestamp() const throw() { 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); }
|
||||
|
||||
/**
|
||||
* @return True if this outbound multicast has been sent to enough peers
|
||||
*/
|
||||
inline bool atLimit() const throw() { return (_alreadySentTo.size() >= _limit); }
|
||||
|
||||
/**
|
||||
* Just send without checking log
|
||||
*
|
||||
* @param RR Runtime environment
|
||||
* @param toAddr Destination address
|
||||
*/
|
||||
void sendOnly(const RuntimeEnvironment *RR,const Address &toAddr);
|
||||
|
||||
/**
|
||||
* Just send and log but do not check sent log
|
||||
*
|
||||
* @param RR Runtime environment
|
||||
* @param toAddr Destination address
|
||||
*/
|
||||
inline void sendAndLog(const RuntimeEnvironment *RR,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 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)
|
||||
{
|
||||
if (std::find(_alreadySentTo.begin(),_alreadySentTo.end(),toAddr) == _alreadySentTo.end()) {
|
||||
sendAndLog(RR,toAddr);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
uint64_t _timestamp;
|
||||
uint64_t _nwid;
|
||||
MAC _macSrc;
|
||||
MAC _macDest;
|
||||
unsigned int _limit;
|
||||
unsigned int _frameLen;
|
||||
unsigned int _etherType;
|
||||
Packet _packet;
|
||||
std::vector<Address> _alreadySentTo;
|
||||
uint8_t _frameData[ZT_MAX_MTU];
|
||||
};
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
||||
#endif
|
||||
2070
zto/node/Packet.cpp
Normal file
2070
zto/node/Packet.cpp
Normal file
File diff suppressed because it is too large
Load Diff
1457
zto/node/Packet.hpp
Normal file
1457
zto/node/Packet.hpp
Normal file
File diff suppressed because it is too large
Load Diff
34
zto/node/Path.cpp
Normal file
34
zto/node/Path.cpp
Normal file
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* 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 "Path.hpp"
|
||||
#include "RuntimeEnvironment.hpp"
|
||||
#include "Node.hpp"
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
bool Path::send(const RuntimeEnvironment *RR,const void *data,unsigned int len,uint64_t now)
|
||||
{
|
||||
if (RR->node->putPacket(_localAddress,address(),data,len)) {
|
||||
_lastOut = now;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace ZeroTier
|
||||
318
zto/node/Path.hpp
Normal file
318
zto/node/Path.hpp
Normal file
@@ -0,0 +1,318 @@
|
||||
/*
|
||||
* 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_PATH_HPP
|
||||
#define ZT_PATH_HPP
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <stdexcept>
|
||||
#include <algorithm>
|
||||
|
||||
#include "Constants.hpp"
|
||||
#include "InetAddress.hpp"
|
||||
#include "SharedPtr.hpp"
|
||||
#include "AtomicCounter.hpp"
|
||||
#include "NonCopyable.hpp"
|
||||
#include "Utils.hpp"
|
||||
|
||||
/**
|
||||
* Maximum return value of preferenceRank()
|
||||
*/
|
||||
#define ZT_PATH_MAX_PREFERENCE_RANK ((ZT_INETADDRESS_MAX_SCOPE << 1) | 1)
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
class RuntimeEnvironment;
|
||||
|
||||
/**
|
||||
* A path across the physical network
|
||||
*/
|
||||
class Path : NonCopyable
|
||||
{
|
||||
friend class SharedPtr<Path>;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Efficient unique key for paths in a Hashtable
|
||||
*/
|
||||
class HashKey
|
||||
{
|
||||
public:
|
||||
HashKey() {}
|
||||
|
||||
HashKey(const InetAddress &l,const InetAddress &r)
|
||||
{
|
||||
// This is an ad-hoc bit packing algorithm to yield unique keys for
|
||||
// remote addresses and their local-side counterparts if defined.
|
||||
// Portability across runtimes is not needed.
|
||||
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;
|
||||
if (l.ss_family == AF_INET) {
|
||||
_k[2] = (uint64_t)reinterpret_cast<const struct sockaddr_in *>(&l)->sin_addr.s_addr;
|
||||
_k[3] = (uint64_t)reinterpret_cast<const struct sockaddr_in *>(&r)->sin_port;
|
||||
} else {
|
||||
_k[2] = 0;
|
||||
_k[3] = 0;
|
||||
}
|
||||
} else if (r.ss_family == AF_INET6) {
|
||||
const uint8_t *a = reinterpret_cast<const uint8_t *>(reinterpret_cast<const struct sockaddr_in6 *>(&r)->sin6_addr.s6_addr);
|
||||
uint8_t *b = reinterpret_cast<uint8_t *>(_k);
|
||||
for(unsigned int i=0;i<16;++i) b[i] = a[i];
|
||||
_k[2] = ~((uint64_t)reinterpret_cast<const struct sockaddr_in6 *>(&r)->sin6_port);
|
||||
if (l.ss_family == AF_INET6) {
|
||||
_k[2] ^= ((uint64_t)reinterpret_cast<const struct sockaddr_in6 *>(&r)->sin6_port) << 32;
|
||||
a = reinterpret_cast<const uint8_t *>(reinterpret_cast<const struct sockaddr_in6 *>(&l)->sin6_addr.s6_addr);
|
||||
b += 24;
|
||||
for(unsigned int i=0;i<8;++i) b[i] = a[i];
|
||||
a += 8;
|
||||
for(unsigned int i=0;i<8;++i) b[i] ^= a[i];
|
||||
}
|
||||
} else {
|
||||
_k[0] = 0;
|
||||
_k[1] = 0;
|
||||
_k[2] = 0;
|
||||
_k[3] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
inline unsigned long hashCode() const { return (unsigned long)(_k[0] + _k[1] + _k[2] + _k[3]); }
|
||||
|
||||
inline bool operator==(const HashKey &k) const { return ( (_k[0] == k._k[0]) && (_k[1] == k._k[1]) && (_k[2] == k._k[2]) && (_k[3] == k._k[3]) ); }
|
||||
inline bool operator!=(const HashKey &k) const { return (!(*this == k)); }
|
||||
|
||||
private:
|
||||
uint64_t _k[4];
|
||||
};
|
||||
|
||||
Path() :
|
||||
_lastOut(0),
|
||||
_lastIn(0),
|
||||
_lastTrustEstablishedPacketReceived(0),
|
||||
_incomingLinkQualityFastLog(0xffffffffffffffffULL),
|
||||
_incomingLinkQualitySlowLogPtr(0),
|
||||
_incomingLinkQualitySlowLogCounter(-64), // discard first fast log
|
||||
_incomingLinkQualityPreviousPacketCounter(0),
|
||||
_outgoingPacketCounter(0),
|
||||
_addr(),
|
||||
_localAddress(),
|
||||
_ipScope(InetAddress::IP_SCOPE_NONE)
|
||||
{
|
||||
for(int i=0;i<(int)sizeof(_incomingLinkQualitySlowLog);++i)
|
||||
_incomingLinkQualitySlowLog[i] = ZT_PATH_LINK_QUALITY_MAX;
|
||||
}
|
||||
|
||||
Path(const InetAddress &localAddress,const InetAddress &addr) :
|
||||
_lastOut(0),
|
||||
_lastIn(0),
|
||||
_lastTrustEstablishedPacketReceived(0),
|
||||
_incomingLinkQualityFastLog(0xffffffffffffffffULL),
|
||||
_incomingLinkQualitySlowLogPtr(0),
|
||||
_incomingLinkQualitySlowLogCounter(-64), // discard first fast log
|
||||
_incomingLinkQualityPreviousPacketCounter(0),
|
||||
_outgoingPacketCounter(0),
|
||||
_addr(addr),
|
||||
_localAddress(localAddress),
|
||||
_ipScope(addr.ipScope())
|
||||
{
|
||||
for(int i=0;i<(int)sizeof(_incomingLinkQualitySlowLog);++i)
|
||||
_incomingLinkQualitySlowLog[i] = ZT_PATH_LINK_QUALITY_MAX;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a packet is received from this remote path, regardless of content
|
||||
*
|
||||
* @param t Time of receive
|
||||
*/
|
||||
inline void received(const uint64_t t) { _lastIn = t; }
|
||||
|
||||
/**
|
||||
* Update link quality using a counter from an incoming packet (or packet head in fragmented case)
|
||||
*
|
||||
* @param counter Packet link quality counter (range 0 to 7, must not have other bits set)
|
||||
*/
|
||||
inline void updateLinkQuality(const unsigned int counter)
|
||||
{
|
||||
const unsigned int prev = _incomingLinkQualityPreviousPacketCounter;
|
||||
_incomingLinkQualityPreviousPacketCounter = counter;
|
||||
const uint64_t fl = (_incomingLinkQualityFastLog = ((_incomingLinkQualityFastLog << 1) | (uint64_t)(prev == ((counter - 1) & 0x7))));
|
||||
if (++_incomingLinkQualitySlowLogCounter >= 64) {
|
||||
_incomingLinkQualitySlowLogCounter = 0;
|
||||
_incomingLinkQualitySlowLog[_incomingLinkQualitySlowLogPtr++ % sizeof(_incomingLinkQualitySlowLog)] = (uint8_t)Utils::countBits(fl);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Link quality from 0 (min) to 255 (max)
|
||||
*/
|
||||
inline unsigned int linkQuality() const
|
||||
{
|
||||
unsigned long slsize = _incomingLinkQualitySlowLogPtr;
|
||||
if (slsize > (unsigned long)sizeof(_incomingLinkQualitySlowLog))
|
||||
slsize = (unsigned long)sizeof(_incomingLinkQualitySlowLog);
|
||||
else if (!slsize)
|
||||
return 255; // ZT_PATH_LINK_QUALITY_MAX
|
||||
unsigned long lq = 0;
|
||||
for(unsigned long i=0;i<slsize;++i)
|
||||
lq += (unsigned long)_incomingLinkQualitySlowLog[i] * 4;
|
||||
lq /= slsize;
|
||||
return (unsigned int)((lq >= 255) ? 255 : lq);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set time last trusted packet was received (done in Peer::received())
|
||||
*/
|
||||
inline void trustedPacketReceived(const uint64_t t) { _lastTrustEstablishedPacketReceived = t; }
|
||||
|
||||
/**
|
||||
* Send a packet via this path (last out time is also updated)
|
||||
*
|
||||
* @param RR Runtime environment
|
||||
* @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);
|
||||
|
||||
/**
|
||||
* Manually update last sent time
|
||||
*
|
||||
* @param t Time of send
|
||||
*/
|
||||
inline void sent(const uint64_t t) { _lastOut = t; }
|
||||
|
||||
/**
|
||||
* @return Address of local side of this path or NULL if unspecified
|
||||
*/
|
||||
inline const InetAddress &localAddress() const { return _localAddress; }
|
||||
|
||||
/**
|
||||
* @return Physical address
|
||||
*/
|
||||
inline const InetAddress &address() const { return _addr; }
|
||||
|
||||
/**
|
||||
* @return IP scope -- faster shortcut for address().ipScope()
|
||||
*/
|
||||
inline InetAddress::IpScope ipScope() const { return _ipScope; }
|
||||
|
||||
/**
|
||||
* @return True if path has received a trust established packet (e.g. common network membership) in the past ZT_TRUST_EXPIRATION ms
|
||||
*/
|
||||
inline bool trustEstablished(const uint64_t now) const { return ((now - _lastTrustEstablishedPacketReceived) < ZT_TRUST_EXPIRATION); }
|
||||
|
||||
/**
|
||||
* @return Preference rank, higher == better
|
||||
*/
|
||||
inline unsigned int preferenceRank() const
|
||||
{
|
||||
// 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) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether this address is valid for a ZeroTier path
|
||||
*
|
||||
* This checks the address type and scope against address types and scopes
|
||||
* that we currently support for ZeroTier communication.
|
||||
*
|
||||
* @param a Address to check
|
||||
* @return True if address is good for ZeroTier path use
|
||||
*/
|
||||
static inline bool isAddressValidForPath(const InetAddress &a)
|
||||
{
|
||||
if ((a.ss_family == AF_INET)||(a.ss_family == AF_INET6)) {
|
||||
switch(a.ipScope()) {
|
||||
/* Note: we don't do link-local at the moment. Unfortunately these
|
||||
* cause several issues. The first is that they usually require a
|
||||
* device qualifier, which we don't handle yet and can't portably
|
||||
* push in PUSH_DIRECT_PATHS. The second is that some OSes assign
|
||||
* these very ephemerally or otherwise strangely. So we'll use
|
||||
* private, pseudo-private, shared (e.g. carrier grade NAT), or
|
||||
* global IP addresses. */
|
||||
case InetAddress::IP_SCOPE_PRIVATE:
|
||||
case InetAddress::IP_SCOPE_PSEUDOPRIVATE:
|
||||
case InetAddress::IP_SCOPE_SHARED:
|
||||
case InetAddress::IP_SCOPE_GLOBAL:
|
||||
if (a.ss_family == AF_INET6) {
|
||||
// TEMPORARY HACK: for now, we are going to blacklist he.net IPv6
|
||||
// tunnels due to very spotty performance and low MTU issues over
|
||||
// these IPv6 tunnel links.
|
||||
const uint8_t *ipd = reinterpret_cast<const uint8_t *>(reinterpret_cast<const struct sockaddr_in6 *>(&a)->sin6_addr.s6_addr);
|
||||
if ((ipd[0] == 0x20)&&(ipd[1] == 0x01)&&(ipd[2] == 0x04)&&(ipd[3] == 0x70))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return True if path appears alive
|
||||
*/
|
||||
inline bool alive(const uint64_t now) const { return ((now - _lastIn) <= ZT_PATH_ALIVE_TIMEOUT); }
|
||||
|
||||
/**
|
||||
* @return True if this path needs a heartbeat
|
||||
*/
|
||||
inline bool needsHeartbeat(const uint64_t now) const { return ((now - _lastOut) >= ZT_PATH_HEARTBEAT_PERIOD); }
|
||||
|
||||
/**
|
||||
* @return Last time we sent something
|
||||
*/
|
||||
inline uint64_t lastOut() const { return _lastOut; }
|
||||
|
||||
/**
|
||||
* @return Last time we received anything
|
||||
*/
|
||||
inline uint64_t lastIn() const { return _lastIn; }
|
||||
|
||||
/**
|
||||
* Return and increment outgoing packet counter (used with Packet::armor())
|
||||
*
|
||||
* @return Next value that should be used for outgoing packet counter (only least significant 3 bits are used)
|
||||
*/
|
||||
inline unsigned int nextOutgoingCounter() { return _outgoingPacketCounter++; }
|
||||
|
||||
private:
|
||||
volatile uint64_t _lastOut;
|
||||
volatile uint64_t _lastIn;
|
||||
volatile uint64_t _lastTrustEstablishedPacketReceived;
|
||||
volatile uint64_t _incomingLinkQualityFastLog;
|
||||
volatile unsigned long _incomingLinkQualitySlowLogPtr;
|
||||
volatile signed int _incomingLinkQualitySlowLogCounter;
|
||||
volatile unsigned int _incomingLinkQualityPreviousPacketCounter;
|
||||
volatile unsigned int _outgoingPacketCounter;
|
||||
InetAddress _addr;
|
||||
InetAddress _localAddress;
|
||||
InetAddress::IpScope _ipScope; // memoize this since it's a computed value checked often
|
||||
volatile uint8_t _incomingLinkQualitySlowLog[32];
|
||||
AtomicCounter __refCount;
|
||||
};
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
||||
#endif
|
||||
497
zto/node/Peer.cpp
Normal file
497
zto/node/Peer.cpp
Normal file
@@ -0,0 +1,497 @@
|
||||
/*
|
||||
* 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 "../version.h"
|
||||
|
||||
#include "Constants.hpp"
|
||||
#include "Peer.hpp"
|
||||
#include "Node.hpp"
|
||||
#include "Switch.hpp"
|
||||
#include "Network.hpp"
|
||||
#include "SelfAwareness.hpp"
|
||||
#include "Cluster.hpp"
|
||||
#include "Packet.hpp"
|
||||
|
||||
#ifndef AF_MAX
|
||||
#if AF_INET > AF_INET6
|
||||
#define AF_MAX AF_INET
|
||||
#else
|
||||
#define AF_MAX AF_INET6
|
||||
#endif
|
||||
#endif
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
Peer::Peer(const RuntimeEnvironment *renv,const Identity &myIdentity,const Identity &peerIdentity) :
|
||||
RR(renv),
|
||||
_lastReceive(0),
|
||||
_lastNontrivialReceive(0),
|
||||
_lastTriedMemorizedPath(0),
|
||||
_lastDirectPathPushSent(0),
|
||||
_lastDirectPathPushReceive(0),
|
||||
_lastCredentialRequestSent(0),
|
||||
_lastWhoisRequestReceived(0),
|
||||
_lastEchoRequestReceived(0),
|
||||
_lastComRequestReceived(0),
|
||||
_lastComRequestSent(0),
|
||||
_lastCredentialsReceived(0),
|
||||
_lastTrustEstablishedPacketReceived(0),
|
||||
_remoteClusterOptimal4(0),
|
||||
_vProto(0),
|
||||
_vMajor(0),
|
||||
_vMinor(0),
|
||||
_vRevision(0),
|
||||
_id(peerIdentity),
|
||||
_numPaths(0),
|
||||
_latency(0),
|
||||
_directPathPushCutoffCount(0),
|
||||
_credentialsCutoffCount(0)
|
||||
{
|
||||
memset(_remoteClusterOptimal6,0,sizeof(_remoteClusterOptimal6));
|
||||
if (!myIdentity.agree(peerIdentity,_key,ZT_PEER_SECRET_KEY_LENGTH))
|
||||
throw std::runtime_error("new peer identity key agreement failed");
|
||||
}
|
||||
|
||||
void Peer::received(
|
||||
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 now = RR->node->now();
|
||||
|
||||
#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(),path->address(),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,path->nextOutgoingCounter());
|
||||
path->send(RR,outp.data(),outp.size(),now);
|
||||
} 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,path->nextOutgoingCounter());
|
||||
path->send(RR,outp.data(),outp.size(),now);
|
||||
}
|
||||
suboptimalPath = true;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
_lastReceive = 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 (_vProto >= 9)
|
||||
path->updateLinkQuality((unsigned int)(packetId & 7));
|
||||
|
||||
if (hops == 0) {
|
||||
bool pathIsConfirmed = false;
|
||||
{
|
||||
Mutex::Lock _l(_paths_m);
|
||||
for(unsigned int p=0;p<_numPaths;++p) {
|
||||
if (_paths[p].path->address() == path->address()) {
|
||||
_paths[p].lastReceive = now;
|
||||
_paths[p].path = path; // local address may have changed!
|
||||
#ifdef ZT_ENABLE_CLUSTER
|
||||
_paths[p].localClusterSuboptimal = suboptimalPath;
|
||||
#endif
|
||||
pathIsConfirmed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( (!pathIsConfirmed) && (RR->node->shouldUsePathForZeroTierTraffic(_id.address(),path->localAddress(),path->address())) ) {
|
||||
if (verb == Packet::VERB_OK) {
|
||||
Mutex::Lock _l(_paths_m);
|
||||
|
||||
// Since this is a new path, figure out where to put it (possibly replacing an old/dead one)
|
||||
unsigned int slot;
|
||||
if (_numPaths < ZT_MAX_PEER_NETWORK_PATHS) {
|
||||
slot = _numPaths++;
|
||||
} else {
|
||||
// First try to replace the worst within the same address family, if possible
|
||||
int worstSlot = -1;
|
||||
uint64_t worstScore = 0xffffffffffffffffULL;
|
||||
for(unsigned int p=0;p<_numPaths;++p) {
|
||||
if (_paths[p].path->address().ss_family == path->address().ss_family) {
|
||||
const uint64_t s = _pathScore(p,now);
|
||||
if (s < worstScore) {
|
||||
worstScore = s;
|
||||
worstSlot = (int)p;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (worstSlot >= 0) {
|
||||
slot = (unsigned int)worstSlot;
|
||||
} else {
|
||||
// If we can't find one with the same family, replace the worst of any family
|
||||
slot = ZT_MAX_PEER_NETWORK_PATHS - 1;
|
||||
for(unsigned int p=0;p<_numPaths;++p) {
|
||||
const uint64_t s = _pathScore(p,now);
|
||||
if (s < worstScore) {
|
||||
worstScore = s;
|
||||
slot = p;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_paths[slot].lastReceive = now;
|
||||
_paths[slot].path = path;
|
||||
#ifdef ZT_ENABLE_CLUSTER
|
||||
_paths[slot].localClusterSuboptimal = suboptimalPath;
|
||||
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(),path->address().toString().c_str());
|
||||
attemptToContactAt(path->localAddress(),path->address(),now,true,path->nextOutgoingCounter());
|
||||
path->sent(now);
|
||||
}
|
||||
}
|
||||
} else if (this->trustEstablished(now)) {
|
||||
// Send PUSH_DIRECT_PATHS if hops>0 (relayed) and we have a trust relationship (common network membership)
|
||||
#ifdef ZT_ENABLE_CLUSTER
|
||||
// Cluster mode disables normal PUSH_DIRECT_PATHS in favor of cluster-based peer redirection
|
||||
const bool haveCluster = (RR->cluster);
|
||||
#else
|
||||
const bool haveCluster = false;
|
||||
#endif
|
||||
if ( ((now - _lastDirectPathPushSent) >= ZT_DIRECT_PATH_PUSH_INTERVAL) && (!haveCluster) ) {
|
||||
_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);
|
||||
|
||||
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) {
|
||||
#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;
|
||||
}
|
||||
|
||||
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->nextOutgoingCounter());
|
||||
path->send(RR,outp.data(),outp.size(),now);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Peer::hasActivePathTo(uint64_t now,const InetAddress &addr) const
|
||||
{
|
||||
Mutex::Lock _l(_paths_m);
|
||||
for(unsigned int p=0;p<_numPaths;++p) {
|
||||
if ( (_paths[p].path->address() == addr) && ((now - _paths[p].lastReceive) <= ZT_PEER_PATH_EXPIRATION) && (_paths[p].path->alive(now)) )
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Peer::sendDirect(const void *data,unsigned int len,uint64_t now,bool forceEvenIfDead)
|
||||
{
|
||||
Mutex::Lock _l(_paths_m);
|
||||
|
||||
int bestp = -1;
|
||||
uint64_t best = 0ULL;
|
||||
for(unsigned int p=0;p<_numPaths;++p) {
|
||||
if ( ((now - _paths[p].lastReceive) <= ZT_PEER_PATH_EXPIRATION) && (_paths[p].path->alive(now)||(forceEvenIfDead)) ) {
|
||||
const uint64_t s = _pathScore(p,now);
|
||||
if (s >= best) {
|
||||
best = s;
|
||||
bestp = (int)p;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (bestp >= 0) {
|
||||
return _paths[bestp].path->send(RR,data,len,now);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
SharedPtr<Path> Peer::getBestPath(uint64_t now,bool includeExpired)
|
||||
{
|
||||
Mutex::Lock _l(_paths_m);
|
||||
|
||||
int bestp = -1;
|
||||
uint64_t best = 0ULL;
|
||||
for(unsigned int p=0;p<_numPaths;++p) {
|
||||
if ( ((now - _paths[p].lastReceive) <= ZT_PEER_PATH_EXPIRATION) || (includeExpired) ) {
|
||||
const uint64_t s = _pathScore(p,now);
|
||||
if (s >= best) {
|
||||
best = s;
|
||||
bestp = (int)p;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (bestp >= 0) {
|
||||
return _paths[bestp].path;
|
||||
} else {
|
||||
return SharedPtr<Path>();
|
||||
}
|
||||
}
|
||||
|
||||
void Peer::sendHELLO(const InetAddress &localAddr,const InetAddress &atAddress,uint64_t now,unsigned int counter)
|
||||
{
|
||||
Packet outp(_id.address(),RR->identity.address(),Packet::VERB_HELLO);
|
||||
|
||||
outp.append((unsigned char)ZT_PROTO_VERSION);
|
||||
outp.append((unsigned char)ZEROTIER_ONE_VERSION_MAJOR);
|
||||
outp.append((unsigned char)ZEROTIER_ONE_VERSION_MINOR);
|
||||
outp.append((uint16_t)ZEROTIER_ONE_VERSION_REVISION);
|
||||
outp.append(now);
|
||||
RR->identity.serialize(outp,false);
|
||||
atAddress.serialize(outp);
|
||||
|
||||
outp.append((uint64_t)RR->topology->planetWorldId());
|
||||
outp.append((uint64_t)RR->topology->planetWorldTimestamp());
|
||||
|
||||
const unsigned int startCryptedPortionAt = outp.size();
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
const unsigned int corSizeAt = outp.size();
|
||||
outp.addSize(2);
|
||||
RR->topology->appendCertificateOfRepresentation(outp);
|
||||
outp.setAt(corSizeAt,(uint16_t)(outp.size() - (corSizeAt + 2)));
|
||||
|
||||
outp.cryptField(_key,startCryptedPortionAt,outp.size() - startCryptedPortionAt);
|
||||
|
||||
RR->node->expectReplyTo(outp.packetId());
|
||||
|
||||
if (atAddress) {
|
||||
outp.armor(_key,false,counter); // false == don't encrypt full payload, but add MAC
|
||||
RR->node->putPacket(localAddr,atAddress,outp.data(),outp.size());
|
||||
} else {
|
||||
RR->sw->send(outp,false); // false == don't encrypt full payload, but add MAC
|
||||
}
|
||||
}
|
||||
|
||||
void Peer::attemptToContactAt(const InetAddress &localAddr,const InetAddress &atAddress,uint64_t now,bool sendFullHello,unsigned int counter)
|
||||
{
|
||||
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,counter);
|
||||
RR->node->putPacket(localAddr,atAddress,outp.data(),outp.size());
|
||||
} else {
|
||||
sendHELLO(localAddr,atAddress,now,counter);
|
||||
}
|
||||
}
|
||||
|
||||
void Peer::tryMemorizedPath(uint64_t now)
|
||||
{
|
||||
if ((now - _lastTriedMemorizedPath) >= ZT_TRY_MEMORIZED_PATH_INTERVAL) {
|
||||
_lastTriedMemorizedPath = now;
|
||||
InetAddress mp;
|
||||
if (RR->node->externalPathLookup(_id.address(),-1,mp))
|
||||
attemptToContactAt(InetAddress(),mp,now,true,0);
|
||||
}
|
||||
}
|
||||
|
||||
bool Peer::doPingAndKeepalive(uint64_t now,int inetAddressFamily)
|
||||
{
|
||||
Mutex::Lock _l(_paths_m);
|
||||
|
||||
int bestp = -1;
|
||||
uint64_t best = 0ULL;
|
||||
for(unsigned int p=0;p<_numPaths;++p) {
|
||||
if ( ((now - _paths[p].lastReceive) <= ZT_PEER_PATH_EXPIRATION) && ((inetAddressFamily < 0)||((int)_paths[p].path->address().ss_family == inetAddressFamily)) ) {
|
||||
const uint64_t s = _pathScore(p,now);
|
||||
if (s >= best) {
|
||||
best = s;
|
||||
bestp = (int)p;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (bestp >= 0) {
|
||||
if ( ((now - _paths[bestp].lastReceive) >= ZT_PEER_PING_PERIOD) || (_paths[bestp].path->needsHeartbeat(now)) ) {
|
||||
attemptToContactAt(_paths[bestp].path->localAddress(),_paths[bestp].path->address(),now,false,_paths[bestp].path->nextOutgoingCounter());
|
||||
_paths[bestp].path->sent(now);
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool Peer::hasActiveDirectPath(uint64_t now) const
|
||||
{
|
||||
Mutex::Lock _l(_paths_m);
|
||||
for(unsigned int p=0;p<_numPaths;++p) {
|
||||
if (((now - _paths[p].lastReceive) <= ZT_PEER_PATH_EXPIRATION)&&(_paths[p].path->alive(now)))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Peer::resetWithinScope(InetAddress::IpScope scope,int inetAddressFamily,uint64_t now)
|
||||
{
|
||||
Mutex::Lock _l(_paths_m);
|
||||
for(unsigned int p=0;p<_numPaths;++p) {
|
||||
if ( (_paths[p].path->address().ss_family == inetAddressFamily) && (_paths[p].path->address().ipScope() == scope) ) {
|
||||
attemptToContactAt(_paths[p].path->localAddress(),_paths[p].path->address(),now,false,_paths[p].path->nextOutgoingCounter());
|
||||
_paths[p].path->sent(now);
|
||||
_paths[p].lastReceive = 0; // path will not be used unless it speaks again
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Peer::getRendezvousAddresses(uint64_t now,InetAddress &v4,InetAddress &v6) const
|
||||
{
|
||||
Mutex::Lock _l(_paths_m);
|
||||
|
||||
int bestp4 = -1,bestp6 = -1;
|
||||
uint64_t best4 = 0ULL,best6 = 0ULL;
|
||||
for(unsigned int p=0;p<_numPaths;++p) {
|
||||
if ( ((now - _paths[p].lastReceive) <= ZT_PEER_PATH_EXPIRATION) && (_paths[p].path->alive(now)) ) {
|
||||
if (_paths[p].path->address().ss_family == AF_INET) {
|
||||
const uint64_t s = _pathScore(p,now);
|
||||
if (s >= best4) {
|
||||
best4 = s;
|
||||
bestp4 = (int)p;
|
||||
}
|
||||
} else if (_paths[p].path->address().ss_family == AF_INET6) {
|
||||
const uint64_t s = _pathScore(p,now);
|
||||
if (s >= best6) {
|
||||
best6 = s;
|
||||
bestp6 = (int)p;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (bestp4 >= 0)
|
||||
v4 = _paths[bestp4].path->address();
|
||||
if (bestp6 >= 0)
|
||||
v6 = _paths[bestp6].path->address();
|
||||
}
|
||||
|
||||
} // namespace ZeroTier
|
||||
499
zto/node/Peer.hpp
Normal file
499
zto/node/Peer.hpp
Normal file
@@ -0,0 +1,499 @@
|
||||
/*
|
||||
* 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_PEER_HPP
|
||||
#define ZT_PEER_HPP
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "Constants.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "../include/ZeroTierOne.h"
|
||||
|
||||
#include "RuntimeEnvironment.hpp"
|
||||
#include "Path.hpp"
|
||||
#include "Address.hpp"
|
||||
#include "Utils.hpp"
|
||||
#include "Identity.hpp"
|
||||
#include "InetAddress.hpp"
|
||||
#include "Packet.hpp"
|
||||
#include "SharedPtr.hpp"
|
||||
#include "AtomicCounter.hpp"
|
||||
#include "Hashtable.hpp"
|
||||
#include "Mutex.hpp"
|
||||
#include "NonCopyable.hpp"
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
/**
|
||||
* Peer on P2P Network (virtual layer 1)
|
||||
*/
|
||||
class Peer : NonCopyable
|
||||
{
|
||||
friend class SharedPtr<Peer>;
|
||||
|
||||
private:
|
||||
Peer() {} // disabled to prevent bugs -- should not be constructed uninitialized
|
||||
|
||||
public:
|
||||
~Peer() { Utils::burn(_key,sizeof(_key)); }
|
||||
|
||||
/**
|
||||
* Construct a new peer
|
||||
*
|
||||
* @param renv Runtime environment
|
||||
* @param myIdentity Identity of THIS node (for key agreement)
|
||||
* @param peerIdentity Identity of peer
|
||||
* @throws std::runtime_error Key agreement with peer's identity failed
|
||||
*/
|
||||
Peer(const RuntimeEnvironment *renv,const Identity &myIdentity,const Identity &peerIdentity);
|
||||
|
||||
/**
|
||||
* @return This peer's ZT address (short for identity().address())
|
||||
*/
|
||||
inline const Address &address() const throw() { return _id.address(); }
|
||||
|
||||
/**
|
||||
* @return This peer's identity
|
||||
*/
|
||||
inline const Identity &identity() const throw() { return _id; }
|
||||
|
||||
/**
|
||||
* Log receipt of an authenticated packet
|
||||
*
|
||||
* This is called by the decode pipe when a packet is proven to be authentic
|
||||
* and appears to be valid.
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
void received(
|
||||
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);
|
||||
|
||||
/**
|
||||
* @param now Current time
|
||||
* @param addr Remote address
|
||||
* @return True if we have an active path to this destination
|
||||
*/
|
||||
bool hasActivePathTo(uint64_t now,const InetAddress &addr) const;
|
||||
|
||||
/**
|
||||
* Set which known path for an address family is optimal
|
||||
*
|
||||
* @param addr Address to make exclusive
|
||||
*/
|
||||
inline void setClusterOptimal(const InetAddress &addr)
|
||||
{
|
||||
if (addr.ss_family == AF_INET) {
|
||||
_remoteClusterOptimal4 = (uint32_t)reinterpret_cast<const struct sockaddr_in *>(&addr)->sin_addr.s_addr;
|
||||
} else if (addr.ss_family == AF_INET6) {
|
||||
memcpy(_remoteClusterOptimal6,reinterpret_cast<const struct sockaddr_in6 *>(&addr)->sin6_addr.s6_addr,16);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send via best direct path
|
||||
*
|
||||
* @param data Packet data
|
||||
* @param len Packet length
|
||||
* @param now Current time
|
||||
* @param forceEvenIfDead If true, send even if the path is not 'alive'
|
||||
* @return True if we actually sent something
|
||||
*/
|
||||
bool sendDirect(const void *data,unsigned int len,uint64_t now,bool forceEvenIfDead);
|
||||
|
||||
/**
|
||||
* 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(uint64_t now,bool includeExpired);
|
||||
|
||||
/**
|
||||
* Send a HELLO to this peer at a specified physical address
|
||||
*
|
||||
* No statistics or sent times are updated here.
|
||||
*
|
||||
* @param localAddr Local address
|
||||
* @param atAddress Destination address
|
||||
* @param now Current time
|
||||
* @param counter Outgoing packet counter
|
||||
*/
|
||||
void sendHELLO(const InetAddress &localAddr,const InetAddress &atAddress,uint64_t now,unsigned int counter);
|
||||
|
||||
/**
|
||||
* Send ECHO (or HELLO for older peers) to this peer at the given address
|
||||
*
|
||||
* No statistics or sent times are updated here.
|
||||
*
|
||||
* @param localAddr Local address
|
||||
* @param atAddress Destination address
|
||||
* @param now Current time
|
||||
* @param sendFullHello If true, always send a full HELLO instead of just an ECHO
|
||||
* @param counter Outgoing packet counter
|
||||
*/
|
||||
void attemptToContactAt(const InetAddress &localAddr,const InetAddress &atAddress,uint64_t now,bool sendFullHello,unsigned int counter);
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
void tryMemorizedPath(uint64_t now);
|
||||
|
||||
/**
|
||||
* Send pings or keepalives depending on configured timeouts
|
||||
*
|
||||
* @param now Current time
|
||||
* @param inetAddressFamily Keep this address family alive, or -1 for any
|
||||
* @return True if we have at least one direct path of the given family (or any if family is -1)
|
||||
*/
|
||||
bool doPingAndKeepalive(uint64_t now,int inetAddressFamily);
|
||||
|
||||
/**
|
||||
* @param now Current time
|
||||
* @return True if this peer has at least one active and alive direct path
|
||||
*/
|
||||
bool hasActiveDirectPath(uint64_t now) const;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @param scope IP scope
|
||||
* @param inetAddressFamily Family e.g. AF_INET
|
||||
* @param now Current time
|
||||
*/
|
||||
void resetWithinScope(InetAddress::IpScope scope,int inetAddressFamily,uint64_t now);
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
void getRendezvousAddresses(uint64_t now,InetAddress &v4,InetAddress &v6) const;
|
||||
|
||||
/**
|
||||
* @param now Current time
|
||||
* @return All known direct paths to this peer and whether they are expired (true == expired)
|
||||
*/
|
||||
inline std::vector< std::pair< SharedPtr<Path>,bool > > paths(const uint64_t now) const
|
||||
{
|
||||
std::vector< std::pair< SharedPtr<Path>,bool > > pp;
|
||||
Mutex::Lock _l(_paths_m);
|
||||
for(unsigned int p=0,np=_numPaths;p<np;++p)
|
||||
pp.push_back(std::pair< SharedPtr<Path>,bool >(_paths[p].path,(now - _paths[p].lastReceive) > ZT_PEER_PATH_EXPIRATION));
|
||||
return pp;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Time of last receive of anything, whether direct or relayed
|
||||
*/
|
||||
inline uint64_t lastReceive() const { return _lastReceive; }
|
||||
|
||||
/**
|
||||
* @return True if we've heard from this peer in less than ZT_PEER_ACTIVITY_TIMEOUT
|
||||
*/
|
||||
inline bool isAlive(const uint64_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 isActive(uint64_t now) const { return ((now - _lastNontrivialReceive) < ZT_PEER_ACTIVITY_TIMEOUT); }
|
||||
|
||||
/**
|
||||
* @return Latency in milliseconds or 0 if unknown
|
||||
*/
|
||||
inline unsigned int latency() const { return _latency; }
|
||||
|
||||
/**
|
||||
* This computes a quality score for relays and root servers
|
||||
*
|
||||
* If we haven't heard anything from these in ZT_PEER_ACTIVITY_TIMEOUT, they
|
||||
* receive the worst possible quality (max unsigned int). Otherwise the
|
||||
* quality is a product of latency and the number of potential missed
|
||||
* pings. This causes roots and relays to switch over a bit faster if they
|
||||
* fail.
|
||||
*
|
||||
* @return Relay quality score computed from latency and other factors, lower is better
|
||||
*/
|
||||
inline unsigned int relayQuality(const uint64_t now) const
|
||||
{
|
||||
const uint64_t tsr = now - _lastReceive;
|
||||
if (tsr >= ZT_PEER_ACTIVITY_TIMEOUT)
|
||||
return (~(unsigned int)0);
|
||||
unsigned int l = _latency;
|
||||
if (!l)
|
||||
l = 0xffff;
|
||||
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);
|
||||
}
|
||||
|
||||
#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 hasLocalClusterOptimalPath(uint64_t now) const
|
||||
{
|
||||
for(unsigned int p=0,np=_numPaths;p<np;++p) {
|
||||
if ( (_paths[p].path->alive(now)) && (!_paths[p].localClusterSuboptimal) )
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @return 256-bit secret symmetric encryption key
|
||||
*/
|
||||
inline const unsigned char *key() const { return _key; }
|
||||
|
||||
/**
|
||||
* Set the currently known remote version of this peer's client
|
||||
*
|
||||
* @param vproto Protocol version
|
||||
* @param vmaj Major version
|
||||
* @param vmin Minor version
|
||||
* @param vrev Revision
|
||||
*/
|
||||
inline void setRemoteVersion(unsigned int vproto,unsigned int vmaj,unsigned int vmin,unsigned int vrev)
|
||||
{
|
||||
_vProto = (uint16_t)vproto;
|
||||
_vMajor = (uint16_t)vmaj;
|
||||
_vMinor = (uint16_t)vmin;
|
||||
_vRevision = (uint16_t)vrev;
|
||||
}
|
||||
|
||||
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 { return ((_vMajor > 0)||(_vMinor > 0)||(_vRevision > 0)); }
|
||||
|
||||
/**
|
||||
* @return True if peer has received a trust established packet (e.g. common network membership) in the past ZT_TRUST_EXPIRATION ms
|
||||
*/
|
||||
inline bool trustEstablished(const uint64_t now) const { return ((now - _lastTrustEstablishedPacketReceived) < ZT_TRUST_EXPIRATION); }
|
||||
|
||||
/**
|
||||
* Rate limit gate for VERB_PUSH_DIRECT_PATHS
|
||||
*/
|
||||
inline bool rateGatePushDirectPaths(const uint64_t now)
|
||||
{
|
||||
if ((now - _lastDirectPathPushReceive) <= ZT_PUSH_DIRECT_PATHS_CUTOFF_TIME)
|
||||
++_directPathPushCutoffCount;
|
||||
else _directPathPushCutoffCount = 0;
|
||||
_lastDirectPathPushReceive = now;
|
||||
return (_directPathPushCutoffCount < ZT_PUSH_DIRECT_PATHS_CUTOFF_LIMIT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Rate limit gate for VERB_NETWORK_CREDENTIALS
|
||||
*/
|
||||
inline bool rateGateCredentialsReceived(const uint64_t now)
|
||||
{
|
||||
if ((now - _lastCredentialsReceived) <= ZT_PEER_CREDENTIALS_CUTOFF_TIME)
|
||||
++_credentialsCutoffCount;
|
||||
else _credentialsCutoffCount = 0;
|
||||
_lastCredentialsReceived = now;
|
||||
return (_directPathPushCutoffCount < ZT_PEER_CREDEITIALS_CUTOFF_LIMIT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Rate limit gate for sending of ERROR_NEED_MEMBERSHIP_CERTIFICATE
|
||||
*/
|
||||
inline bool rateGateRequestCredentials(const uint64_t now)
|
||||
{
|
||||
if ((now - _lastCredentialRequestSent) >= ZT_PEER_GENERAL_RATE_LIMIT) {
|
||||
_lastCredentialRequestSent = now;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rate limit gate for inbound WHOIS requests
|
||||
*/
|
||||
inline bool rateGateInboundWhoisRequest(const uint64_t now)
|
||||
{
|
||||
if ((now - _lastWhoisRequestReceived) >= ZT_PEER_WHOIS_RATE_LIMIT) {
|
||||
_lastWhoisRequestReceived = now;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rate limit gate for inbound ECHO requests
|
||||
*/
|
||||
inline bool rateGateEchoRequest(const uint64_t now)
|
||||
{
|
||||
if ((now - _lastEchoRequestReceived) >= ZT_PEER_GENERAL_RATE_LIMIT) {
|
||||
_lastEchoRequestReceived = now;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rate gate incoming requests for network COM
|
||||
*/
|
||||
inline bool rateGateIncomingComRequest(const uint64_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 uint64_t now)
|
||||
{
|
||||
if ((now - _lastComRequestSent) >= ZT_PEER_GENERAL_RATE_LIMIT) {
|
||||
_lastComRequestSent = now;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
inline uint64_t _pathScore(const unsigned int p,const uint64_t now) const
|
||||
{
|
||||
uint64_t s = ZT_PEER_PING_PERIOD + _paths[p].lastReceive + (uint64_t)(_paths[p].path->preferenceRank() * (ZT_PEER_PING_PERIOD / ZT_PATH_MAX_PREFERENCE_RANK));
|
||||
|
||||
if (_paths[p].path->address().ss_family == AF_INET) {
|
||||
s += (uint64_t)(ZT_PEER_PING_PERIOD * (unsigned long)(reinterpret_cast<const struct sockaddr_in *>(&(_paths[p].path->address()))->sin_addr.s_addr == _remoteClusterOptimal4));
|
||||
} else if (_paths[p].path->address().ss_family == AF_INET6) {
|
||||
uint64_t clusterWeight = ZT_PEER_PING_PERIOD;
|
||||
const uint8_t *a = reinterpret_cast<const uint8_t *>(reinterpret_cast<const struct sockaddr_in6 *>(&(_paths[p].path->address()))->sin6_addr.s6_addr);
|
||||
for(long i=0;i<16;++i) {
|
||||
if (a[i] != _remoteClusterOptimal6[i]) {
|
||||
clusterWeight = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
s += clusterWeight;
|
||||
}
|
||||
|
||||
s += (ZT_PEER_PING_PERIOD / 2) * (uint64_t)_paths[p].path->alive(now);
|
||||
|
||||
#ifdef ZT_ENABLE_CLUSTER
|
||||
s -= ZT_PEER_PING_PERIOD * (uint64_t)_paths[p].localClusterSuboptimal;
|
||||
#endif
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
uint8_t _key[ZT_PEER_SECRET_KEY_LENGTH];
|
||||
|
||||
const RuntimeEnvironment *RR;
|
||||
|
||||
uint64_t _lastReceive; // direct or indirect
|
||||
uint64_t _lastNontrivialReceive; // frames, things like netconf, etc.
|
||||
uint64_t _lastTriedMemorizedPath;
|
||||
uint64_t _lastDirectPathPushSent;
|
||||
uint64_t _lastDirectPathPushReceive;
|
||||
uint64_t _lastCredentialRequestSent;
|
||||
uint64_t _lastWhoisRequestReceived;
|
||||
uint64_t _lastEchoRequestReceived;
|
||||
uint64_t _lastComRequestReceived;
|
||||
uint64_t _lastComRequestSent;
|
||||
uint64_t _lastCredentialsReceived;
|
||||
uint64_t _lastTrustEstablishedPacketReceived;
|
||||
|
||||
uint8_t _remoteClusterOptimal6[16];
|
||||
uint32_t _remoteClusterOptimal4;
|
||||
|
||||
uint16_t _vProto;
|
||||
uint16_t _vMajor;
|
||||
uint16_t _vMinor;
|
||||
uint16_t _vRevision;
|
||||
|
||||
Identity _id;
|
||||
|
||||
struct {
|
||||
uint64_t lastReceive;
|
||||
SharedPtr<Path> path;
|
||||
#ifdef ZT_ENABLE_CLUSTER
|
||||
bool localClusterSuboptimal;
|
||||
#endif
|
||||
} _paths[ZT_MAX_PEER_NETWORK_PATHS];
|
||||
Mutex _paths_m;
|
||||
|
||||
unsigned int _numPaths;
|
||||
unsigned int _latency;
|
||||
unsigned int _directPathPushCutoffCount;
|
||||
unsigned int _credentialsCutoffCount;
|
||||
|
||||
AtomicCounter __refCount;
|
||||
};
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
||||
// Add a swap() for shared ptr's to peers to speed up peer sorts
|
||||
namespace std {
|
||||
template<>
|
||||
inline void swap(ZeroTier::SharedPtr<ZeroTier::Peer> &a,ZeroTier::SharedPtr<ZeroTier::Peer> &b)
|
||||
{
|
||||
a.swap(b);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
628
zto/node/Poly1305.cpp
Normal file
628
zto/node/Poly1305.cpp
Normal file
@@ -0,0 +1,628 @@
|
||||
/*
|
||||
20080912
|
||||
D. J. Bernstein
|
||||
Public domain.
|
||||
*/
|
||||
|
||||
#include "Constants.hpp"
|
||||
#include "Poly1305.hpp"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef __WINDOWS__
|
||||
#pragma warning(disable: 4146)
|
||||
#endif
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
#if 0
|
||||
|
||||
// "Naive" implementation, which is slower... might still want this on some older
|
||||
// or weird platforms if the later versions have issues.
|
||||
|
||||
static inline void add(unsigned int h[17],const unsigned int c[17])
|
||||
{
|
||||
unsigned int j;
|
||||
unsigned int u;
|
||||
u = 0;
|
||||
for (j = 0;j < 17;++j) { u += h[j] + c[j]; h[j] = u & 255; u >>= 8; }
|
||||
}
|
||||
|
||||
static inline void squeeze(unsigned int h[17])
|
||||
{
|
||||
unsigned int j;
|
||||
unsigned int u;
|
||||
u = 0;
|
||||
for (j = 0;j < 16;++j) { u += h[j]; h[j] = u & 255; u >>= 8; }
|
||||
u += h[16]; h[16] = u & 3;
|
||||
u = 5 * (u >> 2);
|
||||
for (j = 0;j < 16;++j) { u += h[j]; h[j] = u & 255; u >>= 8; }
|
||||
u += h[16]; h[16] = u;
|
||||
}
|
||||
|
||||
static const unsigned int minusp[17] = {
|
||||
5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 252
|
||||
} ;
|
||||
|
||||
static inline void freeze(unsigned int h[17])
|
||||
{
|
||||
unsigned int horig[17];
|
||||
unsigned int j;
|
||||
unsigned int negative;
|
||||
for (j = 0;j < 17;++j) horig[j] = h[j];
|
||||
add(h,minusp);
|
||||
negative = -(h[16] >> 7);
|
||||
for (j = 0;j < 17;++j) h[j] ^= negative & (horig[j] ^ h[j]);
|
||||
}
|
||||
|
||||
static inline void mulmod(unsigned int h[17],const unsigned int r[17])
|
||||
{
|
||||
unsigned int hr[17];
|
||||
unsigned int i;
|
||||
unsigned int j;
|
||||
unsigned int u;
|
||||
|
||||
for (i = 0;i < 17;++i) {
|
||||
u = 0;
|
||||
for (j = 0;j <= i;++j) u += h[j] * r[i - j];
|
||||
for (j = i + 1;j < 17;++j) u += 320 * h[j] * r[i + 17 - j];
|
||||
hr[i] = u;
|
||||
}
|
||||
for (i = 0;i < 17;++i) h[i] = hr[i];
|
||||
squeeze(h);
|
||||
}
|
||||
|
||||
static inline int crypto_onetimeauth(unsigned char *out,const unsigned char *in,unsigned long long inlen,const unsigned char *k)
|
||||
{
|
||||
unsigned int j;
|
||||
unsigned int r[17];
|
||||
unsigned int h[17];
|
||||
unsigned int c[17];
|
||||
|
||||
r[0] = k[0];
|
||||
r[1] = k[1];
|
||||
r[2] = k[2];
|
||||
r[3] = k[3] & 15;
|
||||
r[4] = k[4] & 252;
|
||||
r[5] = k[5];
|
||||
r[6] = k[6];
|
||||
r[7] = k[7] & 15;
|
||||
r[8] = k[8] & 252;
|
||||
r[9] = k[9];
|
||||
r[10] = k[10];
|
||||
r[11] = k[11] & 15;
|
||||
r[12] = k[12] & 252;
|
||||
r[13] = k[13];
|
||||
r[14] = k[14];
|
||||
r[15] = k[15] & 15;
|
||||
r[16] = 0;
|
||||
|
||||
for (j = 0;j < 17;++j) h[j] = 0;
|
||||
|
||||
while (inlen > 0) {
|
||||
for (j = 0;j < 17;++j) c[j] = 0;
|
||||
for (j = 0;(j < 16) && (j < inlen);++j) c[j] = in[j];
|
||||
c[j] = 1;
|
||||
in += j; inlen -= j;
|
||||
add(h,c);
|
||||
mulmod(h,r);
|
||||
}
|
||||
|
||||
freeze(h);
|
||||
|
||||
for (j = 0;j < 16;++j) c[j] = k[j + 16];
|
||||
c[16] = 0;
|
||||
add(h,c);
|
||||
for (j = 0;j < 16;++j) out[j] = h[j];
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Poly1305::compute(void *auth,const void *data,unsigned int len,const void *key)
|
||||
throw()
|
||||
{
|
||||
crypto_onetimeauth((unsigned char *)auth,(const unsigned char *)data,len,(const unsigned char *)key);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
|
||||
typedef struct poly1305_context {
|
||||
size_t aligner;
|
||||
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__))
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// 128-bit implementation for MSC and GCC from Poly1305-donna
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#include <intrin.h>
|
||||
|
||||
typedef struct uint128_t {
|
||||
unsigned long long lo;
|
||||
unsigned long long hi;
|
||||
} uint128_t;
|
||||
|
||||
#define MUL(out, x, y) out.lo = _umul128((x), (y), &out.hi)
|
||||
#define ADD(out, in) { unsigned long long t = out.lo; out.lo += in.lo; out.hi += (out.lo < t) + in.hi; }
|
||||
#define ADDLO(out, in) { unsigned long long t = out.lo; out.lo += in; out.hi += (out.lo < t); }
|
||||
#define SHR(in, shift) (__shiftright128(in.lo, in.hi, (shift)))
|
||||
#define LO(in) (in.lo)
|
||||
|
||||
// #define POLY1305_NOINLINE __declspec(noinline)
|
||||
#elif defined(__GNUC__)
|
||||
#if defined(__SIZEOF_INT128__)
|
||||
typedef unsigned __int128 uint128_t;
|
||||
#else
|
||||
typedef unsigned uint128_t __attribute__((mode(TI)));
|
||||
#endif
|
||||
|
||||
#define MUL(out, x, y) out = ((uint128_t)x * y)
|
||||
#define ADD(out, in) out += in
|
||||
#define ADDLO(out, in) out += in
|
||||
#define SHR(in, shift) (unsigned long long)(in >> (shift))
|
||||
#define LO(in) (unsigned long long)(in)
|
||||
|
||||
// #define POLY1305_NOINLINE __attribute__((noinline))
|
||||
#endif
|
||||
|
||||
#define poly1305_block_size 16
|
||||
|
||||
/* 17 + sizeof(size_t) + 8*sizeof(unsigned long long) */
|
||||
typedef struct poly1305_state_internal_t {
|
||||
unsigned long long r[3];
|
||||
unsigned long long h[3];
|
||||
unsigned long long pad[2];
|
||||
size_t leftover;
|
||||
unsigned char buffer[poly1305_block_size];
|
||||
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) {
|
||||
return
|
||||
(((unsigned long long)(p[0] & 0xff) ) |
|
||||
((unsigned long long)(p[1] & 0xff) << 8) |
|
||||
((unsigned long long)(p[2] & 0xff) << 16) |
|
||||
((unsigned long long)(p[3] & 0xff) << 24) |
|
||||
((unsigned long long)(p[4] & 0xff) << 32) |
|
||||
((unsigned long long)(p[5] & 0xff) << 40) |
|
||||
((unsigned long long)(p[6] & 0xff) << 48) |
|
||||
((unsigned long long)(p[7] & 0xff) << 56));
|
||||
}
|
||||
|
||||
/* 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) {
|
||||
p[0] = (v ) & 0xff;
|
||||
p[1] = (v >> 8) & 0xff;
|
||||
p[2] = (v >> 16) & 0xff;
|
||||
p[3] = (v >> 24) & 0xff;
|
||||
p[4] = (v >> 32) & 0xff;
|
||||
p[5] = (v >> 40) & 0xff;
|
||||
p[6] = (v >> 48) & 0xff;
|
||||
p[7] = (v >> 56) & 0xff;
|
||||
}
|
||||
|
||||
static inline void
|
||||
poly1305_init(poly1305_context *ctx, const unsigned char key[32]) {
|
||||
poly1305_state_internal_t *st = (poly1305_state_internal_t *)ctx;
|
||||
unsigned long long t0,t1;
|
||||
|
||||
/* r &= 0xffffffc0ffffffc0ffffffc0fffffff */
|
||||
t0 = U8TO64(&key[0]);
|
||||
t1 = U8TO64(&key[8]);
|
||||
|
||||
st->r[0] = ( t0 ) & 0xffc0fffffff;
|
||||
st->r[1] = ((t0 >> 44) | (t1 << 20)) & 0xfffffc0ffff;
|
||||
st->r[2] = ((t1 >> 24) ) & 0x00ffffffc0f;
|
||||
|
||||
/* h = 0 */
|
||||
st->h[0] = 0;
|
||||
st->h[1] = 0;
|
||||
st->h[2] = 0;
|
||||
|
||||
/* save pad for later */
|
||||
st->pad[0] = U8TO64(&key[16]);
|
||||
st->pad[1] = U8TO64(&key[24]);
|
||||
|
||||
st->leftover = 0;
|
||||
st->final = 0;
|
||||
}
|
||||
|
||||
static inline void
|
||||
poly1305_blocks(poly1305_state_internal_t *st, const unsigned char *m, size_t bytes) {
|
||||
const unsigned long long hibit = (st->final) ? 0 : ((unsigned long long)1 << 40); /* 1 << 128 */
|
||||
unsigned long long r0,r1,r2;
|
||||
unsigned long long s1,s2;
|
||||
unsigned long long h0,h1,h2;
|
||||
unsigned long long c;
|
||||
uint128_t d0,d1,d2,d;
|
||||
|
||||
r0 = st->r[0];
|
||||
r1 = st->r[1];
|
||||
r2 = st->r[2];
|
||||
|
||||
h0 = st->h[0];
|
||||
h1 = st->h[1];
|
||||
h2 = st->h[2];
|
||||
|
||||
s1 = r1 * (5 << 2);
|
||||
s2 = r2 * (5 << 2);
|
||||
|
||||
while (bytes >= poly1305_block_size) {
|
||||
unsigned long long t0,t1;
|
||||
|
||||
/* h += m[i] */
|
||||
t0 = U8TO64(&m[0]);
|
||||
t1 = U8TO64(&m[8]);
|
||||
|
||||
h0 += (( t0 ) & 0xfffffffffff);
|
||||
h1 += (((t0 >> 44) | (t1 << 20)) & 0xfffffffffff);
|
||||
h2 += (((t1 >> 24) ) & 0x3ffffffffff) | hibit;
|
||||
|
||||
/* h *= r */
|
||||
MUL(d0, h0, r0); MUL(d, h1, s2); ADD(d0, d); MUL(d, h2, s1); ADD(d0, d);
|
||||
MUL(d1, h0, r1); MUL(d, h1, r0); ADD(d1, d); MUL(d, h2, s2); ADD(d1, d);
|
||||
MUL(d2, h0, r2); MUL(d, h1, r1); ADD(d2, d); MUL(d, h2, r0); ADD(d2, d);
|
||||
|
||||
/* (partial) h %= p */
|
||||
c = SHR(d0, 44); h0 = LO(d0) & 0xfffffffffff;
|
||||
ADDLO(d1, c); c = SHR(d1, 44); h1 = LO(d1) & 0xfffffffffff;
|
||||
ADDLO(d2, c); c = SHR(d2, 42); h2 = LO(d2) & 0x3ffffffffff;
|
||||
h0 += c * 5; c = (h0 >> 44); h0 = h0 & 0xfffffffffff;
|
||||
h1 += c;
|
||||
|
||||
m += poly1305_block_size;
|
||||
bytes -= poly1305_block_size;
|
||||
}
|
||||
|
||||
st->h[0] = h0;
|
||||
st->h[1] = h1;
|
||||
st->h[2] = h2;
|
||||
}
|
||||
|
||||
static inline void
|
||||
poly1305_finish(poly1305_context *ctx, unsigned char mac[16]) {
|
||||
poly1305_state_internal_t *st = (poly1305_state_internal_t *)ctx;
|
||||
unsigned long long h0,h1,h2,c;
|
||||
unsigned long long g0,g1,g2;
|
||||
unsigned long long t0,t1;
|
||||
|
||||
/* process the remaining block */
|
||||
if (st->leftover) {
|
||||
size_t i = st->leftover;
|
||||
st->buffer[i] = 1;
|
||||
for (i = i + 1; i < poly1305_block_size; i++)
|
||||
st->buffer[i] = 0;
|
||||
st->final = 1;
|
||||
poly1305_blocks(st, st->buffer, poly1305_block_size);
|
||||
}
|
||||
|
||||
/* fully carry h */
|
||||
h0 = st->h[0];
|
||||
h1 = st->h[1];
|
||||
h2 = st->h[2];
|
||||
|
||||
c = (h1 >> 44); h1 &= 0xfffffffffff;
|
||||
h2 += c; c = (h2 >> 42); h2 &= 0x3ffffffffff;
|
||||
h0 += c * 5; c = (h0 >> 44); h0 &= 0xfffffffffff;
|
||||
h1 += c; c = (h1 >> 44); h1 &= 0xfffffffffff;
|
||||
h2 += c; c = (h2 >> 42); h2 &= 0x3ffffffffff;
|
||||
h0 += c * 5; c = (h0 >> 44); h0 &= 0xfffffffffff;
|
||||
h1 += c;
|
||||
|
||||
/* compute h + -p */
|
||||
g0 = h0 + 5; c = (g0 >> 44); g0 &= 0xfffffffffff;
|
||||
g1 = h1 + c; c = (g1 >> 44); g1 &= 0xfffffffffff;
|
||||
g2 = h2 + c - ((unsigned long long)1 << 42);
|
||||
|
||||
/* select h if h < p, or h + -p if h >= p */
|
||||
c = (g2 >> ((sizeof(unsigned long long) * 8) - 1)) - 1;
|
||||
g0 &= c;
|
||||
g1 &= c;
|
||||
g2 &= c;
|
||||
c = ~c;
|
||||
h0 = (h0 & c) | g0;
|
||||
h1 = (h1 & c) | g1;
|
||||
h2 = (h2 & c) | g2;
|
||||
|
||||
/* h = (h + pad) */
|
||||
t0 = st->pad[0];
|
||||
t1 = st->pad[1];
|
||||
|
||||
h0 += (( t0 ) & 0xfffffffffff) ; c = (h0 >> 44); h0 &= 0xfffffffffff;
|
||||
h1 += (((t0 >> 44) | (t1 << 20)) & 0xfffffffffff) + c; c = (h1 >> 44); h1 &= 0xfffffffffff;
|
||||
h2 += (((t1 >> 24) ) & 0x3ffffffffff) + c; h2 &= 0x3ffffffffff;
|
||||
|
||||
/* mac = h % (2^128) */
|
||||
h0 = ((h0 ) | (h1 << 44));
|
||||
h1 = ((h1 >> 20) | (h2 << 24));
|
||||
|
||||
U64TO8(&mac[0], h0);
|
||||
U64TO8(&mac[8], h1);
|
||||
|
||||
/* zero out the state */
|
||||
st->h[0] = 0;
|
||||
st->h[1] = 0;
|
||||
st->h[2] = 0;
|
||||
st->r[0] = 0;
|
||||
st->r[1] = 0;
|
||||
st->r[2] = 0;
|
||||
st->pad[0] = 0;
|
||||
st->pad[1] = 0;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#else
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// More portable 64-bit implementation
|
||||
|
||||
#define poly1305_block_size 16
|
||||
|
||||
/* 17 + sizeof(size_t) + 14*sizeof(unsigned long) */
|
||||
typedef struct poly1305_state_internal_t {
|
||||
unsigned long r[5];
|
||||
unsigned long h[5];
|
||||
unsigned long pad[4];
|
||||
size_t leftover;
|
||||
unsigned char buffer[poly1305_block_size];
|
||||
unsigned char final;
|
||||
} poly1305_state_internal_t;
|
||||
|
||||
/* interpret four 8 bit unsigned integers as a 32 bit unsigned integer in little endian */
|
||||
static unsigned long
|
||||
U8TO32(const unsigned char *p) {
|
||||
return
|
||||
(((unsigned long)(p[0] & 0xff) ) |
|
||||
((unsigned long)(p[1] & 0xff) << 8) |
|
||||
((unsigned long)(p[2] & 0xff) << 16) |
|
||||
((unsigned long)(p[3] & 0xff) << 24));
|
||||
}
|
||||
|
||||
/* store a 32 bit unsigned integer as four 8 bit unsigned integers in little endian */
|
||||
static void
|
||||
U32TO8(unsigned char *p, unsigned long v) {
|
||||
p[0] = (v ) & 0xff;
|
||||
p[1] = (v >> 8) & 0xff;
|
||||
p[2] = (v >> 16) & 0xff;
|
||||
p[3] = (v >> 24) & 0xff;
|
||||
}
|
||||
|
||||
static inline void
|
||||
poly1305_init(poly1305_context *ctx, const unsigned char key[32]) {
|
||||
poly1305_state_internal_t *st = (poly1305_state_internal_t *)ctx;
|
||||
|
||||
/* r &= 0xffffffc0ffffffc0ffffffc0fffffff */
|
||||
st->r[0] = (U8TO32(&key[ 0]) ) & 0x3ffffff;
|
||||
st->r[1] = (U8TO32(&key[ 3]) >> 2) & 0x3ffff03;
|
||||
st->r[2] = (U8TO32(&key[ 6]) >> 4) & 0x3ffc0ff;
|
||||
st->r[3] = (U8TO32(&key[ 9]) >> 6) & 0x3f03fff;
|
||||
st->r[4] = (U8TO32(&key[12]) >> 8) & 0x00fffff;
|
||||
|
||||
/* h = 0 */
|
||||
st->h[0] = 0;
|
||||
st->h[1] = 0;
|
||||
st->h[2] = 0;
|
||||
st->h[3] = 0;
|
||||
st->h[4] = 0;
|
||||
|
||||
/* save pad for later */
|
||||
st->pad[0] = U8TO32(&key[16]);
|
||||
st->pad[1] = U8TO32(&key[20]);
|
||||
st->pad[2] = U8TO32(&key[24]);
|
||||
st->pad[3] = U8TO32(&key[28]);
|
||||
|
||||
st->leftover = 0;
|
||||
st->final = 0;
|
||||
}
|
||||
|
||||
static inline void
|
||||
poly1305_blocks(poly1305_state_internal_t *st, const unsigned char *m, size_t bytes) {
|
||||
const unsigned long hibit = (st->final) ? 0 : (1 << 24); /* 1 << 128 */
|
||||
unsigned long r0,r1,r2,r3,r4;
|
||||
unsigned long s1,s2,s3,s4;
|
||||
unsigned long h0,h1,h2,h3,h4;
|
||||
unsigned long long d0,d1,d2,d3,d4;
|
||||
unsigned long c;
|
||||
|
||||
r0 = st->r[0];
|
||||
r1 = st->r[1];
|
||||
r2 = st->r[2];
|
||||
r3 = st->r[3];
|
||||
r4 = st->r[4];
|
||||
|
||||
s1 = r1 * 5;
|
||||
s2 = r2 * 5;
|
||||
s3 = r3 * 5;
|
||||
s4 = r4 * 5;
|
||||
|
||||
h0 = st->h[0];
|
||||
h1 = st->h[1];
|
||||
h2 = st->h[2];
|
||||
h3 = st->h[3];
|
||||
h4 = st->h[4];
|
||||
|
||||
while (bytes >= poly1305_block_size) {
|
||||
/* h += m[i] */
|
||||
h0 += (U8TO32(m+ 0) ) & 0x3ffffff;
|
||||
h1 += (U8TO32(m+ 3) >> 2) & 0x3ffffff;
|
||||
h2 += (U8TO32(m+ 6) >> 4) & 0x3ffffff;
|
||||
h3 += (U8TO32(m+ 9) >> 6) & 0x3ffffff;
|
||||
h4 += (U8TO32(m+12) >> 8) | hibit;
|
||||
|
||||
/* h *= r */
|
||||
d0 = ((unsigned long long)h0 * r0) + ((unsigned long long)h1 * s4) + ((unsigned long long)h2 * s3) + ((unsigned long long)h3 * s2) + ((unsigned long long)h4 * s1);
|
||||
d1 = ((unsigned long long)h0 * r1) + ((unsigned long long)h1 * r0) + ((unsigned long long)h2 * s4) + ((unsigned long long)h3 * s3) + ((unsigned long long)h4 * s2);
|
||||
d2 = ((unsigned long long)h0 * r2) + ((unsigned long long)h1 * r1) + ((unsigned long long)h2 * r0) + ((unsigned long long)h3 * s4) + ((unsigned long long)h4 * s3);
|
||||
d3 = ((unsigned long long)h0 * r3) + ((unsigned long long)h1 * r2) + ((unsigned long long)h2 * r1) + ((unsigned long long)h3 * r0) + ((unsigned long long)h4 * s4);
|
||||
d4 = ((unsigned long long)h0 * r4) + ((unsigned long long)h1 * r3) + ((unsigned long long)h2 * r2) + ((unsigned long long)h3 * r1) + ((unsigned long long)h4 * r0);
|
||||
|
||||
/* (partial) h %= p */
|
||||
c = (unsigned long)(d0 >> 26); h0 = (unsigned long)d0 & 0x3ffffff;
|
||||
d1 += c; c = (unsigned long)(d1 >> 26); h1 = (unsigned long)d1 & 0x3ffffff;
|
||||
d2 += c; c = (unsigned long)(d2 >> 26); h2 = (unsigned long)d2 & 0x3ffffff;
|
||||
d3 += c; c = (unsigned long)(d3 >> 26); h3 = (unsigned long)d3 & 0x3ffffff;
|
||||
d4 += c; c = (unsigned long)(d4 >> 26); h4 = (unsigned long)d4 & 0x3ffffff;
|
||||
h0 += c * 5; c = (h0 >> 26); h0 = h0 & 0x3ffffff;
|
||||
h1 += c;
|
||||
|
||||
m += poly1305_block_size;
|
||||
bytes -= poly1305_block_size;
|
||||
}
|
||||
|
||||
st->h[0] = h0;
|
||||
st->h[1] = h1;
|
||||
st->h[2] = h2;
|
||||
st->h[3] = h3;
|
||||
st->h[4] = h4;
|
||||
}
|
||||
|
||||
static inline void
|
||||
poly1305_finish(poly1305_context *ctx, unsigned char mac[16]) {
|
||||
poly1305_state_internal_t *st = (poly1305_state_internal_t *)ctx;
|
||||
unsigned long h0,h1,h2,h3,h4,c;
|
||||
unsigned long g0,g1,g2,g3,g4;
|
||||
unsigned long long f;
|
||||
unsigned long mask;
|
||||
|
||||
/* process the remaining block */
|
||||
if (st->leftover) {
|
||||
size_t i = st->leftover;
|
||||
st->buffer[i++] = 1;
|
||||
for (; i < poly1305_block_size; i++)
|
||||
st->buffer[i] = 0;
|
||||
st->final = 1;
|
||||
poly1305_blocks(st, st->buffer, poly1305_block_size);
|
||||
}
|
||||
|
||||
/* fully carry h */
|
||||
h0 = st->h[0];
|
||||
h1 = st->h[1];
|
||||
h2 = st->h[2];
|
||||
h3 = st->h[3];
|
||||
h4 = st->h[4];
|
||||
|
||||
c = h1 >> 26; h1 = h1 & 0x3ffffff;
|
||||
h2 += c; c = h2 >> 26; h2 = h2 & 0x3ffffff;
|
||||
h3 += c; c = h3 >> 26; h3 = h3 & 0x3ffffff;
|
||||
h4 += c; c = h4 >> 26; h4 = h4 & 0x3ffffff;
|
||||
h0 += c * 5; c = h0 >> 26; h0 = h0 & 0x3ffffff;
|
||||
h1 += c;
|
||||
|
||||
/* compute h + -p */
|
||||
g0 = h0 + 5; c = g0 >> 26; g0 &= 0x3ffffff;
|
||||
g1 = h1 + c; c = g1 >> 26; g1 &= 0x3ffffff;
|
||||
g2 = h2 + c; c = g2 >> 26; g2 &= 0x3ffffff;
|
||||
g3 = h3 + c; c = g3 >> 26; g3 &= 0x3ffffff;
|
||||
g4 = h4 + c - (1 << 26);
|
||||
|
||||
/* select h if h < p, or h + -p if h >= p */
|
||||
mask = (g4 >> ((sizeof(unsigned long) * 8) - 1)) - 1;
|
||||
g0 &= mask;
|
||||
g1 &= mask;
|
||||
g2 &= mask;
|
||||
g3 &= mask;
|
||||
g4 &= mask;
|
||||
mask = ~mask;
|
||||
h0 = (h0 & mask) | g0;
|
||||
h1 = (h1 & mask) | g1;
|
||||
h2 = (h2 & mask) | g2;
|
||||
h3 = (h3 & mask) | g3;
|
||||
h4 = (h4 & mask) | g4;
|
||||
|
||||
/* h = h % (2^128) */
|
||||
h0 = ((h0 ) | (h1 << 26)) & 0xffffffff;
|
||||
h1 = ((h1 >> 6) | (h2 << 20)) & 0xffffffff;
|
||||
h2 = ((h2 >> 12) | (h3 << 14)) & 0xffffffff;
|
||||
h3 = ((h3 >> 18) | (h4 << 8)) & 0xffffffff;
|
||||
|
||||
/* mac = (h + pad) % (2^128) */
|
||||
f = (unsigned long long)h0 + st->pad[0] ; h0 = (unsigned long)f;
|
||||
f = (unsigned long long)h1 + st->pad[1] + (f >> 32); h1 = (unsigned long)f;
|
||||
f = (unsigned long long)h2 + st->pad[2] + (f >> 32); h2 = (unsigned long)f;
|
||||
f = (unsigned long long)h3 + st->pad[3] + (f >> 32); h3 = (unsigned long)f;
|
||||
|
||||
U32TO8(mac + 0, h0);
|
||||
U32TO8(mac + 4, h1);
|
||||
U32TO8(mac + 8, h2);
|
||||
U32TO8(mac + 12, h3);
|
||||
|
||||
/* zero out the state */
|
||||
st->h[0] = 0;
|
||||
st->h[1] = 0;
|
||||
st->h[2] = 0;
|
||||
st->h[3] = 0;
|
||||
st->h[4] = 0;
|
||||
st->r[0] = 0;
|
||||
st->r[1] = 0;
|
||||
st->r[2] = 0;
|
||||
st->r[3] = 0;
|
||||
st->r[4] = 0;
|
||||
st->pad[0] = 0;
|
||||
st->pad[1] = 0;
|
||||
st->pad[2] = 0;
|
||||
st->pad[3] = 0;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#endif // MSC/GCC or not
|
||||
|
||||
static inline void
|
||||
poly1305_update(poly1305_context *ctx, const unsigned char *m, size_t bytes) {
|
||||
poly1305_state_internal_t *st = (poly1305_state_internal_t *)ctx;
|
||||
size_t i;
|
||||
|
||||
/* handle leftover */
|
||||
if (st->leftover) {
|
||||
size_t want = (poly1305_block_size - st->leftover);
|
||||
if (want > bytes)
|
||||
want = bytes;
|
||||
for (i = 0; i < want; i++)
|
||||
st->buffer[st->leftover + i] = m[i];
|
||||
bytes -= want;
|
||||
m += want;
|
||||
st->leftover += want;
|
||||
if (st->leftover < poly1305_block_size)
|
||||
return;
|
||||
poly1305_blocks(st, st->buffer, poly1305_block_size);
|
||||
st->leftover = 0;
|
||||
}
|
||||
|
||||
/* process full blocks */
|
||||
if (bytes >= poly1305_block_size) {
|
||||
size_t want = (bytes & ~(poly1305_block_size - 1));
|
||||
poly1305_blocks(st, m, want);
|
||||
m += want;
|
||||
bytes -= want;
|
||||
}
|
||||
|
||||
/* store leftover */
|
||||
if (bytes) {
|
||||
for (i = 0; i < bytes; i++)
|
||||
st->buffer[st->leftover + i] = m[i];
|
||||
st->leftover += 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));
|
||||
poly1305_update(&ctx,reinterpret_cast<const unsigned char *>(data),(size_t)len);
|
||||
poly1305_finish(&ctx,reinterpret_cast<unsigned char *>(auth));
|
||||
}
|
||||
|
||||
} // namespace ZeroTier
|
||||
55
zto/node/Poly1305.hpp
Normal file
55
zto/node/Poly1305.hpp
Normal file
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* 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_POLY1305_HPP
|
||||
#define ZT_POLY1305_HPP
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
#define ZT_POLY1305_KEY_LEN 32
|
||||
#define ZT_POLY1305_MAC_LEN 16
|
||||
|
||||
/**
|
||||
* Poly1305 one-time authentication code
|
||||
*
|
||||
* This takes a one-time-use 32-byte key and generates a 16-byte message
|
||||
* authentication code. The key must never be re-used for a different
|
||||
* message.
|
||||
*
|
||||
* In Packet this is done by using the first 32 bytes of the stream cipher
|
||||
* keystream as a one-time-use key. These 32 bytes are then discarded and
|
||||
* the packet is encrypted with the next N bytes.
|
||||
*/
|
||||
class Poly1305
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Compute a one-time authentication code
|
||||
*
|
||||
* @param auth Buffer to receive code -- MUST be 16 bytes in length
|
||||
* @param data Data to authenticate
|
||||
* @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();
|
||||
};
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
||||
#endif
|
||||
14
zto/node/README.md
Normal file
14
zto/node/README.md
Normal file
@@ -0,0 +1,14 @@
|
||||
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.
|
||||
|
||||
Give it wire packets and it gives you Ethernet packets, and vice versa. The core contains absolutely no actual I/O, port configuration, or other OS-specific code (except Utils::getSecureRandom()). It provides a simple C API via [/include/ZeroTierOne.h](../include/ZeroTierOne.h). It's designed to be small and maximally portable for future use on small embedded and special purpose systems.
|
||||
|
||||
Code in here follows these guidelines:
|
||||
|
||||
- Keep it minimal, especially in terms of code footprint and memory use.
|
||||
- There should be no OS-dependent code here unless absolutely necessary (e.g. getSecureRandom).
|
||||
- If it's not part of the core virtual Ethernet switch it does not belong here.
|
||||
- No C++11 or C++14 since older and embedded compilers don't support it yet and this should be maximally portable.
|
||||
- Minimize the use of complex C++ features since at some point we might end up "minus-minus'ing" this code if doing so proves necessary to port to tiny embedded systems.
|
||||
46
zto/node/Revocation.cpp
Normal file
46
zto/node/Revocation.cpp
Normal file
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* 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 "Revocation.hpp"
|
||||
#include "RuntimeEnvironment.hpp"
|
||||
#include "Identity.hpp"
|
||||
#include "Topology.hpp"
|
||||
#include "Switch.hpp"
|
||||
#include "Network.hpp"
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
int Revocation::verify(const RuntimeEnvironment *RR) const
|
||||
{
|
||||
if ((!_signedBy)||(_signedBy != Network::controllerFor(_networkId)))
|
||||
return -1;
|
||||
const Identity id(RR->topology->getIdentity(_signedBy));
|
||||
if (!id) {
|
||||
RR->sw->requestWhois(_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
|
||||
193
zto/node/Revocation.hpp
Normal file
193
zto/node/Revocation.hpp
Normal file
@@ -0,0 +1,193 @@
|
||||
/*
|
||||
* 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_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 "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 type being revoked
|
||||
*/
|
||||
enum CredentialType
|
||||
{
|
||||
CREDENTIAL_TYPE_NULL = 0,
|
||||
CREDENTIAL_TYPE_COM = 1, // CertificateOfMembership
|
||||
CREDENTIAL_TYPE_CAPABILITY = 2,
|
||||
CREDENTIAL_TYPE_TAG = 3,
|
||||
CREDENTIAL_TYPE_COO = 4 // CertificateOfOwnership
|
||||
};
|
||||
|
||||
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 uint64_t i,const uint64_t nwid,const uint64_t cid,const uint64_t thr,const uint64_t fl,const Address &tgt,const CredentialType ct) :
|
||||
_id(i),
|
||||
_networkId(nwid),
|
||||
_credentialId(cid),
|
||||
_threshold(thr),
|
||||
_flags(fl),
|
||||
_target(tgt),
|
||||
_signedBy(),
|
||||
_type(ct) {}
|
||||
|
||||
inline uint64_t id() const { return _id; }
|
||||
inline uint64_t networkId() const { return _networkId; }
|
||||
inline uint64_t credentialId() const { return _credentialId; }
|
||||
inline uint64_t threshold() const { return _threshold; }
|
||||
inline const Address &target() const { return _target; }
|
||||
inline const Address &signer() const { return _signedBy; }
|
||||
inline CredentialType 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.
|
||||
* @return 0 == OK, 1 == waiting for WHOIS, -1 == BAD signature or chain
|
||||
*/
|
||||
int verify(const RuntimeEnvironment *RR) 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(_id);
|
||||
b.append(_networkId);
|
||||
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;
|
||||
|
||||
_id = b.template at<uint64_t>(p); p += 8;
|
||||
_networkId = b.template at<uint64_t>(p); p += 8;
|
||||
_credentialId = b.template at<uint64_t>(p); p += 8;
|
||||
_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 = (CredentialType)b[p++];
|
||||
|
||||
if (b[p++] == 1) {
|
||||
if (b.template at<uint16_t>(p) == ZT_C25519_SIGNATURE_LEN) {
|
||||
p += 2;
|
||||
memcpy(_signature.data,b.field(p,ZT_C25519_SIGNATURE_LEN),ZT_C25519_SIGNATURE_LEN);
|
||||
p += ZT_C25519_SIGNATURE_LEN;
|
||||
} else throw std::runtime_error("invalid signature");
|
||||
} else {
|
||||
p += 2 + b.template at<uint16_t>(p);
|
||||
}
|
||||
|
||||
p += 2 + b.template at<uint16_t>(p);
|
||||
if (p > b.size())
|
||||
throw std::runtime_error("extended field overflow");
|
||||
|
||||
return (p - startAt);
|
||||
}
|
||||
|
||||
private:
|
||||
uint64_t _id;
|
||||
uint64_t _networkId;
|
||||
uint64_t _credentialId;
|
||||
uint64_t _threshold;
|
||||
uint64_t _flags;
|
||||
Address _target;
|
||||
Address _signedBy;
|
||||
CredentialType _type;
|
||||
C25519::Signature _signature;
|
||||
};
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
||||
#endif
|
||||
89
zto/node/RuntimeEnvironment.hpp
Normal file
89
zto/node/RuntimeEnvironment.hpp
Normal file
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* 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_RUNTIMEENVIRONMENT_HPP
|
||||
#define ZT_RUNTIMEENVIRONMENT_HPP
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "Constants.hpp"
|
||||
#include "Identity.hpp"
|
||||
#include "Mutex.hpp"
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
class NodeConfig;
|
||||
class Switch;
|
||||
class Topology;
|
||||
class Node;
|
||||
class Multicaster;
|
||||
class NetworkController;
|
||||
class SelfAwareness;
|
||||
class Cluster;
|
||||
|
||||
/**
|
||||
* Holds global state for an instance of ZeroTier::Node
|
||||
*/
|
||||
class RuntimeEnvironment
|
||||
{
|
||||
public:
|
||||
RuntimeEnvironment(Node *n) :
|
||||
node(n)
|
||||
,identity()
|
||||
,localNetworkController((NetworkController *)0)
|
||||
,sw((Switch *)0)
|
||||
,mc((Multicaster *)0)
|
||||
,topology((Topology *)0)
|
||||
,sa((SelfAwareness *)0)
|
||||
#ifdef ZT_ENABLE_CLUSTER
|
||||
,cluster((Cluster *)0)
|
||||
#endif
|
||||
{
|
||||
}
|
||||
|
||||
// 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
|
||||
* 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.
|
||||
*/
|
||||
|
||||
Switch *sw;
|
||||
Multicaster *mc;
|
||||
Topology *topology;
|
||||
SelfAwareness *sa;
|
||||
#ifdef ZT_ENABLE_CLUSTER
|
||||
Cluster *cluster;
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
||||
#endif
|
||||
352
zto/node/SHA512.cpp
Normal file
352
zto/node/SHA512.cpp
Normal file
@@ -0,0 +1,352 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
/*
|
||||
20080913
|
||||
D. J. Bernstein
|
||||
Public domain.
|
||||
*/
|
||||
|
||||
#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)
|
||||
;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
#else // !ZT_NO_TYPE_PUNNING
|
||||
|
||||
#define load_bigendian(x) Utils::ntoh(*((const uint64_t *)(x)))
|
||||
#define store_bigendian(x,u) (*((uint64_t *)(x)) = Utils::hton((u)))
|
||||
|
||||
#endif // ZT_NO_TYPE_PUNNING
|
||||
|
||||
#define SHR(x,c) ((x) >> (c))
|
||||
#define ROTR(x,c) (((x) >> (c)) | ((x) << (64 - (c))))
|
||||
|
||||
#define Ch(x,y,z) ((x & y) ^ (~x & z))
|
||||
#define Maj(x,y,z) ((x & y) ^ (x & z) ^ (y & z))
|
||||
#define Sigma0(x) (ROTR(x,28) ^ ROTR(x,34) ^ ROTR(x,39))
|
||||
#define Sigma1(x) (ROTR(x,14) ^ ROTR(x,18) ^ ROTR(x,41))
|
||||
#define sigma0(x) (ROTR(x, 1) ^ ROTR(x, 8) ^ SHR(x,7))
|
||||
#define sigma1(x) (ROTR(x,19) ^ ROTR(x,61) ^ SHR(x,6))
|
||||
|
||||
#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 )
|
||||
|
||||
#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;
|
||||
|
||||
static inline int crypto_hashblocks(unsigned char *statebytes,const unsigned char *in,unsigned long long inlen)
|
||||
{
|
||||
uint64 state[8];
|
||||
uint64 a;
|
||||
uint64 b;
|
||||
uint64 c;
|
||||
uint64 d;
|
||||
uint64 e;
|
||||
uint64 f;
|
||||
uint64 g;
|
||||
uint64 h;
|
||||
uint64 T1;
|
||||
uint64 T2;
|
||||
|
||||
a = load_bigendian(statebytes + 0); state[0] = a;
|
||||
b = load_bigendian(statebytes + 8); state[1] = b;
|
||||
c = load_bigendian(statebytes + 16); state[2] = c;
|
||||
d = load_bigendian(statebytes + 24); state[3] = d;
|
||||
e = load_bigendian(statebytes + 32); state[4] = e;
|
||||
f = load_bigendian(statebytes + 40); state[5] = f;
|
||||
g = load_bigendian(statebytes + 48); state[6] = g;
|
||||
h = load_bigendian(statebytes + 56); state[7] = h;
|
||||
|
||||
while (inlen >= 128) {
|
||||
uint64 w0 = load_bigendian(in + 0);
|
||||
uint64 w1 = load_bigendian(in + 8);
|
||||
uint64 w2 = load_bigendian(in + 16);
|
||||
uint64 w3 = load_bigendian(in + 24);
|
||||
uint64 w4 = load_bigendian(in + 32);
|
||||
uint64 w5 = load_bigendian(in + 40);
|
||||
uint64 w6 = load_bigendian(in + 48);
|
||||
uint64 w7 = load_bigendian(in + 56);
|
||||
uint64 w8 = load_bigendian(in + 64);
|
||||
uint64 w9 = load_bigendian(in + 72);
|
||||
uint64 w10 = load_bigendian(in + 80);
|
||||
uint64 w11 = load_bigendian(in + 88);
|
||||
uint64 w12 = load_bigendian(in + 96);
|
||||
uint64 w13 = load_bigendian(in + 104);
|
||||
uint64 w14 = load_bigendian(in + 112);
|
||||
uint64 w15 = load_bigendian(in + 120);
|
||||
|
||||
F(w0 ,0x428a2f98d728ae22ULL)
|
||||
F(w1 ,0x7137449123ef65cdULL)
|
||||
F(w2 ,0xb5c0fbcfec4d3b2fULL)
|
||||
F(w3 ,0xe9b5dba58189dbbcULL)
|
||||
F(w4 ,0x3956c25bf348b538ULL)
|
||||
F(w5 ,0x59f111f1b605d019ULL)
|
||||
F(w6 ,0x923f82a4af194f9bULL)
|
||||
F(w7 ,0xab1c5ed5da6d8118ULL)
|
||||
F(w8 ,0xd807aa98a3030242ULL)
|
||||
F(w9 ,0x12835b0145706fbeULL)
|
||||
F(w10,0x243185be4ee4b28cULL)
|
||||
F(w11,0x550c7dc3d5ffb4e2ULL)
|
||||
F(w12,0x72be5d74f27b896fULL)
|
||||
F(w13,0x80deb1fe3b1696b1ULL)
|
||||
F(w14,0x9bdc06a725c71235ULL)
|
||||
F(w15,0xc19bf174cf692694ULL)
|
||||
|
||||
EXPAND
|
||||
|
||||
F(w0 ,0xe49b69c19ef14ad2ULL)
|
||||
F(w1 ,0xefbe4786384f25e3ULL)
|
||||
F(w2 ,0x0fc19dc68b8cd5b5ULL)
|
||||
F(w3 ,0x240ca1cc77ac9c65ULL)
|
||||
F(w4 ,0x2de92c6f592b0275ULL)
|
||||
F(w5 ,0x4a7484aa6ea6e483ULL)
|
||||
F(w6 ,0x5cb0a9dcbd41fbd4ULL)
|
||||
F(w7 ,0x76f988da831153b5ULL)
|
||||
F(w8 ,0x983e5152ee66dfabULL)
|
||||
F(w9 ,0xa831c66d2db43210ULL)
|
||||
F(w10,0xb00327c898fb213fULL)
|
||||
F(w11,0xbf597fc7beef0ee4ULL)
|
||||
F(w12,0xc6e00bf33da88fc2ULL)
|
||||
F(w13,0xd5a79147930aa725ULL)
|
||||
F(w14,0x06ca6351e003826fULL)
|
||||
F(w15,0x142929670a0e6e70ULL)
|
||||
|
||||
EXPAND
|
||||
|
||||
F(w0 ,0x27b70a8546d22ffcULL)
|
||||
F(w1 ,0x2e1b21385c26c926ULL)
|
||||
F(w2 ,0x4d2c6dfc5ac42aedULL)
|
||||
F(w3 ,0x53380d139d95b3dfULL)
|
||||
F(w4 ,0x650a73548baf63deULL)
|
||||
F(w5 ,0x766a0abb3c77b2a8ULL)
|
||||
F(w6 ,0x81c2c92e47edaee6ULL)
|
||||
F(w7 ,0x92722c851482353bULL)
|
||||
F(w8 ,0xa2bfe8a14cf10364ULL)
|
||||
F(w9 ,0xa81a664bbc423001ULL)
|
||||
F(w10,0xc24b8b70d0f89791ULL)
|
||||
F(w11,0xc76c51a30654be30ULL)
|
||||
F(w12,0xd192e819d6ef5218ULL)
|
||||
F(w13,0xd69906245565a910ULL)
|
||||
F(w14,0xf40e35855771202aULL)
|
||||
F(w15,0x106aa07032bbd1b8ULL)
|
||||
|
||||
EXPAND
|
||||
|
||||
F(w0 ,0x19a4c116b8d2d0c8ULL)
|
||||
F(w1 ,0x1e376c085141ab53ULL)
|
||||
F(w2 ,0x2748774cdf8eeb99ULL)
|
||||
F(w3 ,0x34b0bcb5e19b48a8ULL)
|
||||
F(w4 ,0x391c0cb3c5c95a63ULL)
|
||||
F(w5 ,0x4ed8aa4ae3418acbULL)
|
||||
F(w6 ,0x5b9cca4f7763e373ULL)
|
||||
F(w7 ,0x682e6ff3d6b2b8a3ULL)
|
||||
F(w8 ,0x748f82ee5defb2fcULL)
|
||||
F(w9 ,0x78a5636f43172f60ULL)
|
||||
F(w10,0x84c87814a1f0ab72ULL)
|
||||
F(w11,0x8cc702081a6439ecULL)
|
||||
F(w12,0x90befffa23631e28ULL)
|
||||
F(w13,0xa4506cebde82bde9ULL)
|
||||
F(w14,0xbef9a3f7b2c67915ULL)
|
||||
F(w15,0xc67178f2e372532bULL)
|
||||
|
||||
EXPAND
|
||||
|
||||
F(w0 ,0xca273eceea26619cULL)
|
||||
F(w1 ,0xd186b8c721c0c207ULL)
|
||||
F(w2 ,0xeada7dd6cde0eb1eULL)
|
||||
F(w3 ,0xf57d4f7fee6ed178ULL)
|
||||
F(w4 ,0x06f067aa72176fbaULL)
|
||||
F(w5 ,0x0a637dc5a2c898a6ULL)
|
||||
F(w6 ,0x113f9804bef90daeULL)
|
||||
F(w7 ,0x1b710b35131c471bULL)
|
||||
F(w8 ,0x28db77f523047d84ULL)
|
||||
F(w9 ,0x32caab7b40c72493ULL)
|
||||
F(w10,0x3c9ebe0a15c9bebcULL)
|
||||
F(w11,0x431d67c49c100d4cULL)
|
||||
F(w12,0x4cc5d4becb3e42b6ULL)
|
||||
F(w13,0x597f299cfc657e2aULL)
|
||||
F(w14,0x5fcb6fab3ad6faecULL)
|
||||
F(w15,0x6c44198c4a475817ULL)
|
||||
|
||||
a += state[0];
|
||||
b += state[1];
|
||||
c += state[2];
|
||||
d += state[3];
|
||||
e += state[4];
|
||||
f += state[5];
|
||||
g += state[6];
|
||||
h += state[7];
|
||||
|
||||
state[0] = a;
|
||||
state[1] = b;
|
||||
state[2] = c;
|
||||
state[3] = d;
|
||||
state[4] = e;
|
||||
state[5] = f;
|
||||
state[6] = g;
|
||||
state[7] = h;
|
||||
|
||||
in += 128;
|
||||
inlen -= 128;
|
||||
}
|
||||
|
||||
store_bigendian(statebytes + 0,state[0]);
|
||||
store_bigendian(statebytes + 8,state[1]);
|
||||
store_bigendian(statebytes + 16,state[2]);
|
||||
store_bigendian(statebytes + 24,state[3]);
|
||||
store_bigendian(statebytes + 32,state[4]);
|
||||
store_bigendian(statebytes + 40,state[5]);
|
||||
store_bigendian(statebytes + 48,state[6]);
|
||||
store_bigendian(statebytes + 56,state[7]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#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
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void SHA512::hash(void *digest,const void *data,unsigned int len)
|
||||
{
|
||||
unsigned char h[64];
|
||||
unsigned char padded[256];
|
||||
int i;
|
||||
uint64_t bytes = len;
|
||||
|
||||
const unsigned char *in = (const unsigned char *)data;
|
||||
unsigned int inlen = len;
|
||||
|
||||
for (i = 0;i < 64;++i) h[i] = iv[i];
|
||||
|
||||
blocks(h,in,inlen);
|
||||
in += inlen;
|
||||
inlen &= 127;
|
||||
in -= inlen;
|
||||
|
||||
for (i = 0;i < (int)inlen;++i) padded[i] = in[i];
|
||||
padded[inlen] = 0x80;
|
||||
|
||||
if (inlen < 112) {
|
||||
for (i = inlen + 1;i < 119;++i) padded[i] = 0;
|
||||
padded[119] = (unsigned char)((bytes >> 61) & 0xff);
|
||||
padded[120] = (unsigned char)((bytes >> 53) & 0xff);
|
||||
padded[121] = (unsigned char)((bytes >> 45) & 0xff);
|
||||
padded[122] = (unsigned char)((bytes >> 37) & 0xff);
|
||||
padded[123] = (unsigned char)((bytes >> 29) & 0xff);
|
||||
padded[124] = (unsigned char)((bytes >> 21) & 0xff);
|
||||
padded[125] = (unsigned char)((bytes >> 13) & 0xff);
|
||||
padded[126] = (unsigned char)((bytes >> 5) & 0xff);
|
||||
padded[127] = (unsigned char)((bytes << 3) & 0xff);
|
||||
blocks(h,padded,128);
|
||||
} else {
|
||||
for (i = inlen + 1;i < 247;++i) padded[i] = 0;
|
||||
padded[247] = (unsigned char)((bytes >> 61) & 0xff);
|
||||
padded[248] = (unsigned char)((bytes >> 53) & 0xff);
|
||||
padded[249] = (unsigned char)((bytes >> 45) & 0xff);
|
||||
padded[250] = (unsigned char)((bytes >> 37) & 0xff);
|
||||
padded[251] = (unsigned char)((bytes >> 29) & 0xff);
|
||||
padded[252] = (unsigned char)((bytes >> 21) & 0xff);
|
||||
padded[253] = (unsigned char)((bytes >> 13) & 0xff);
|
||||
padded[254] = (unsigned char)((bytes >> 5) & 0xff);
|
||||
padded[255] = (unsigned char)((bytes << 3) & 0xff);
|
||||
blocks(h,padded,256);
|
||||
}
|
||||
|
||||
for (i = 0;i < 64;++i) ((unsigned char *)digest)[i] = h[i];
|
||||
}
|
||||
|
||||
} // namespace ZeroTier
|
||||
37
zto/node/SHA512.hpp
Normal file
37
zto/node/SHA512.hpp
Normal file
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* 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_SHA512_HPP
|
||||
#define ZT_SHA512_HPP
|
||||
|
||||
#define ZT_SHA512_DIGEST_LEN 64
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
/**
|
||||
* SHA-512 digest algorithm
|
||||
*/
|
||||
class SHA512
|
||||
{
|
||||
public:
|
||||
static void hash(void *digest,const void *data,unsigned int len);
|
||||
};
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
||||
#endif
|
||||
1358
zto/node/Salsa20.cpp
Normal file
1358
zto/node/Salsa20.cpp
Normal file
File diff suppressed because it is too large
Load Diff
89
zto/node/Salsa20.hpp
Normal file
89
zto/node/Salsa20.hpp
Normal file
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Based on public domain code available at: http://cr.yp.to/snuffle.html
|
||||
*
|
||||
* This therefore is public domain.
|
||||
*/
|
||||
|
||||
#ifndef ZT_SALSA20_HPP
|
||||
#define ZT_SALSA20_HPP
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "Constants.hpp"
|
||||
#include "Utils.hpp"
|
||||
|
||||
#if (!defined(ZT_SALSA20_SSE)) && (defined(__SSE2__) || defined(__WINDOWS__))
|
||||
#define ZT_SALSA20_SSE 1
|
||||
#endif
|
||||
|
||||
#ifdef ZT_SALSA20_SSE
|
||||
#include <emmintrin.h>
|
||||
#endif // ZT_SALSA20_SSE
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
/**
|
||||
* Salsa20 stream cipher
|
||||
*/
|
||||
class Salsa20
|
||||
{
|
||||
public:
|
||||
Salsa20() throw() {}
|
||||
|
||||
~Salsa20() { Utils::burn(&_state,sizeof(_state)); }
|
||||
|
||||
/**
|
||||
* @param key Key bits
|
||||
* @param kbits Number of key bits: 128 or 256 (recommended)
|
||||
* @param iv 64-bit initialization vector
|
||||
*/
|
||||
Salsa20(const void *key,unsigned int kbits,const void *iv)
|
||||
throw()
|
||||
{
|
||||
init(key,kbits,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();
|
||||
|
||||
/**
|
||||
* Encrypt/decrypt data using Salsa20/12
|
||||
*
|
||||
* @param in Input data
|
||||
* @param out Output buffer
|
||||
* @param bytes Length of data
|
||||
*/
|
||||
void crypt12(const void *in,void *out,unsigned int bytes)
|
||||
throw();
|
||||
|
||||
/**
|
||||
* Encrypt/decrypt data using Salsa20/20
|
||||
*
|
||||
* @param in Input data
|
||||
* @param out Output buffer
|
||||
* @param bytes Length of data
|
||||
*/
|
||||
void crypt20(const void *in,void *out,unsigned int bytes)
|
||||
throw();
|
||||
|
||||
private:
|
||||
union {
|
||||
#ifdef ZT_SALSA20_SSE
|
||||
__m128i v[4];
|
||||
#endif // ZT_SALSA20_SSE
|
||||
uint32_t i[16];
|
||||
} _state;
|
||||
};
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
||||
#endif
|
||||
193
zto/node/SelfAwareness.cpp
Normal file
193
zto/node/SelfAwareness.cpp
Normal file
@@ -0,0 +1,193 @@
|
||||
/*
|
||||
* 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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <set>
|
||||
#include <vector>
|
||||
|
||||
#include "Constants.hpp"
|
||||
#include "SelfAwareness.hpp"
|
||||
#include "RuntimeEnvironment.hpp"
|
||||
#include "Node.hpp"
|
||||
#include "Topology.hpp"
|
||||
#include "Packet.hpp"
|
||||
#include "Peer.hpp"
|
||||
#include "Switch.hpp"
|
||||
|
||||
// Entry timeout -- make it fairly long since this is just to prevent stale buildup
|
||||
#define ZT_SELFAWARENESS_ENTRY_TIMEOUT 600000
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
class _ResetWithinScope
|
||||
{
|
||||
public:
|
||||
_ResetWithinScope(uint64_t now,int inetAddressFamily,InetAddress::IpScope scope) :
|
||||
_now(now),
|
||||
_family(inetAddressFamily),
|
||||
_scope(scope) {}
|
||||
|
||||
inline void operator()(Topology &t,const SharedPtr<Peer> &p) { p->resetWithinScope(_scope,_family,_now); }
|
||||
|
||||
private:
|
||||
uint64_t _now;
|
||||
int _family;
|
||||
InetAddress::IpScope _scope;
|
||||
};
|
||||
|
||||
SelfAwareness::SelfAwareness(const RuntimeEnvironment *renv) :
|
||||
RR(renv),
|
||||
_phy(128)
|
||||
{
|
||||
}
|
||||
|
||||
void SelfAwareness::iam(const Address &reporter,const InetAddress &receivedOnLocalAddress,const InetAddress &reporterPhysicalAddress,const InetAddress &myPhysicalAddress,bool trusted,uint64_t now)
|
||||
{
|
||||
const InetAddress::IpScope scope = myPhysicalAddress.ipScope();
|
||||
|
||||
if ((scope != reporterPhysicalAddress.ipScope())||(scope == InetAddress::IP_SCOPE_NONE)||(scope == InetAddress::IP_SCOPE_LOOPBACK)||(scope == InetAddress::IP_SCOPE_MULTICAST))
|
||||
return;
|
||||
|
||||
Mutex::Lock _l(_phy_m);
|
||||
PhySurfaceEntry &entry = _phy[PhySurfaceKey(reporter,receivedOnLocalAddress,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
|
||||
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.mySurface = myPhysicalAddress;
|
||||
entry.ts = now;
|
||||
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.
|
||||
// Don't use 'entry' after this since hash table gets modified.
|
||||
{
|
||||
Hashtable< PhySurfaceKey,PhySurfaceEntry >::Iterator i(_phy);
|
||||
PhySurfaceKey *k = (PhySurfaceKey *)0;
|
||||
PhySurfaceEntry *e = (PhySurfaceEntry *)0;
|
||||
while (i.next(k,e)) {
|
||||
if ((k->reporterPhysicalAddress != reporterPhysicalAddress)&&(k->scope == scope))
|
||||
_phy.erase(*k);
|
||||
}
|
||||
}
|
||||
|
||||
// Reset all paths within this scope and address family
|
||||
_ResetWithinScope rset(now,myPhysicalAddress.ss_family,(InetAddress::IpScope)scope);
|
||||
RR->topology->eachPeer<_ResetWithinScope &>(rset);
|
||||
} 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)
|
||||
{
|
||||
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 ((now - e->ts) >= ZT_SELFAWARENESS_ENTRY_TIMEOUT)
|
||||
_phy.erase(*k);
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
*
|
||||
* For each IP address reported by a trusted (upstream) peer, we find
|
||||
* the external port most recently reported by ANY peer for that IP.
|
||||
*
|
||||
* We only do any of this for global IPv4 addresses since private IPs
|
||||
* and IPv6 are not going to have symmetric NAT.
|
||||
*
|
||||
* SECURITY NOTE:
|
||||
*
|
||||
* We never use IPs reported by non-trusted peers, since this could lead
|
||||
* to a minor vulnerability whereby a peer could poison our cache with
|
||||
* bad external surface reports via OK(HELLO) and then possibly coax us
|
||||
* into suggesting their IP to other peers via PUSH_DIRECT_PATHS. This
|
||||
* in turn could allow them to MITM flows.
|
||||
*
|
||||
* Since flows are encrypted and authenticated they could not actually
|
||||
* read or modify traffic, but they could gather meta-data for forensics
|
||||
* purpsoes or use this as a DOS attack vector. */
|
||||
|
||||
std::map< uint32_t,std::pair<uint64_t,unsigned int> > maxPortByIp;
|
||||
InetAddress theOneTrueSurface;
|
||||
bool symmetric = false;
|
||||
{
|
||||
Mutex::Lock _l(_phy_m);
|
||||
|
||||
{ // First get IPs from only trusted peers, and perform basic NAT type characterization
|
||||
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] = std::pair<uint64_t,unsigned int>(e->ts,e->mySurface.port());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{ // Then find max port per IP from a trusted peer
|
||||
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::map< uint32_t,std::pair<uint64_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.first < e->ts)) {
|
||||
mp->second.first = e->ts;
|
||||
mp->second.second = e->mySurface.port();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (symmetric) {
|
||||
std::vector<InetAddress> r;
|
||||
for(unsigned int k=1;k<=3;++k) {
|
||||
for(std::map< uint32_t,std::pair<uint64_t,unsigned int> >::iterator i(maxPortByIp.begin());i!=maxPortByIp.end();++i) {
|
||||
unsigned int p = i->second.second + k;
|
||||
if (p > 65535) p -= 64511;
|
||||
InetAddress pred(&(i->first),4,p);
|
||||
if (std::find(r.begin(),r.end(),pred) == r.end())
|
||||
r.push_back(pred);
|
||||
}
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
return std::vector<InetAddress>();
|
||||
}
|
||||
|
||||
} // namespace ZeroTier
|
||||
98
zto/node/SelfAwareness.hpp
Normal file
98
zto/node/SelfAwareness.hpp
Normal file
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
* 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_SELFAWARENESS_HPP
|
||||
#define ZT_SELFAWARENESS_HPP
|
||||
|
||||
#include "Constants.hpp"
|
||||
#include "InetAddress.hpp"
|
||||
#include "Hashtable.hpp"
|
||||
#include "Address.hpp"
|
||||
#include "Mutex.hpp"
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
class RuntimeEnvironment;
|
||||
|
||||
/**
|
||||
* Tracks changes to this peer's real world addresses
|
||||
*/
|
||||
class SelfAwareness
|
||||
{
|
||||
public:
|
||||
SelfAwareness(const RuntimeEnvironment *renv);
|
||||
|
||||
/**
|
||||
* Called when a trusted remote peer informs us of our external network address
|
||||
*
|
||||
* @param reporter ZeroTier address of reporting peer
|
||||
* @param receivedOnLocalAddress Local address on which report was received
|
||||
* @param reporterPhysicalAddress Physical address that reporting peer seems to have
|
||||
* @param myPhysicalAddress Physical address that peer says we have
|
||||
* @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);
|
||||
|
||||
/**
|
||||
* Clean up database periodically
|
||||
*
|
||||
* @param now Current time
|
||||
*/
|
||||
void clean(uint64_t now);
|
||||
|
||||
/**
|
||||
* If we appear to be behind a symmetric NAT, get predictions for possible external endpoints
|
||||
*
|
||||
* @return Symmetric NAT predictions or empty vector if none
|
||||
*/
|
||||
std::vector<InetAddress> getSymmetricNatPredictions();
|
||||
|
||||
private:
|
||||
struct PhySurfaceKey
|
||||
{
|
||||
Address reporter;
|
||||
InetAddress receivedOnLocalAddress;
|
||||
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) {}
|
||||
|
||||
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)); }
|
||||
};
|
||||
struct PhySurfaceEntry
|
||||
{
|
||||
InetAddress mySurface;
|
||||
uint64_t ts;
|
||||
bool trusted;
|
||||
|
||||
PhySurfaceEntry() : mySurface(),ts(0),trusted(false) {}
|
||||
PhySurfaceEntry(const InetAddress &a,const uint64_t t) : mySurface(a),ts(t),trusted(false) {}
|
||||
};
|
||||
|
||||
const RuntimeEnvironment *RR;
|
||||
|
||||
Hashtable< PhySurfaceKey,PhySurfaceEntry > _phy;
|
||||
Mutex _phy_m;
|
||||
};
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
||||
#endif
|
||||
178
zto/node/SharedPtr.hpp
Normal file
178
zto/node/SharedPtr.hpp
Normal file
@@ -0,0 +1,178 @@
|
||||
/*
|
||||
* 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_SHAREDPTR_HPP
|
||||
#define ZT_SHAREDPTR_HPP
|
||||
|
||||
#include "Mutex.hpp"
|
||||
#include "AtomicCounter.hpp"
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
/**
|
||||
* Simple 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.
|
||||
*/
|
||||
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()
|
||||
{
|
||||
if (_ptr) {
|
||||
if (--_ptr->__refCount <= 0)
|
||||
delete _ptr;
|
||||
}
|
||||
}
|
||||
|
||||
inline SharedPtr &operator=(const SharedPtr &sp)
|
||||
{
|
||||
if (_ptr != sp._ptr) {
|
||||
T *p = sp._getAndInc();
|
||||
if (_ptr) {
|
||||
if (--_ptr->__refCount <= 0)
|
||||
delete _ptr;
|
||||
}
|
||||
_ptr = p;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to a naked pointer and increment its reference count
|
||||
*
|
||||
* This assumes this SharedPtr is NULL and that ptr is not a 'zombie.' No
|
||||
* checks are performed.
|
||||
*
|
||||
* @param ptr Naked pointer to assign
|
||||
*/
|
||||
inline void setToUnsafe(T *ptr)
|
||||
{
|
||||
++ptr->__refCount;
|
||||
_ptr = ptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Swap with another pointer 'for free' without ref count overhead
|
||||
*
|
||||
* @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; }
|
||||
|
||||
/**
|
||||
* @return Raw pointer to held object
|
||||
*/
|
||||
inline T *ptr() const throw() { return _ptr; }
|
||||
|
||||
/**
|
||||
* Set this pointer to NULL
|
||||
*/
|
||||
inline void zero()
|
||||
{
|
||||
if (_ptr) {
|
||||
if (--_ptr->__refCount <= 0)
|
||||
delete _ptr;
|
||||
_ptr = (T *)0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set this pointer to NULL if this is the only pointer holding the object
|
||||
*
|
||||
* @return True if object was deleted and SharedPtr is now NULL (or was already NULL)
|
||||
*/
|
||||
inline bool reclaimIfWeak()
|
||||
{
|
||||
if (_ptr) {
|
||||
if (++_ptr->__refCount <= 2) {
|
||||
if (--_ptr->__refCount <= 1) {
|
||||
delete _ptr;
|
||||
_ptr = (T *)0;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
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); }
|
||||
|
||||
private:
|
||||
inline T *_getAndInc() const
|
||||
throw()
|
||||
{
|
||||
if (_ptr)
|
||||
++_ptr->__refCount;
|
||||
return _ptr;
|
||||
}
|
||||
|
||||
T *_ptr;
|
||||
};
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
||||
#endif
|
||||
873
zto/node/Switch.cpp
Normal file
873
zto/node/Switch.cpp
Normal file
@@ -0,0 +1,873 @@
|
||||
/*
|
||||
* 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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <utility>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "../version.h"
|
||||
#include "../include/ZeroTierOne.h"
|
||||
|
||||
#include "Constants.hpp"
|
||||
#include "RuntimeEnvironment.hpp"
|
||||
#include "Switch.hpp"
|
||||
#include "Node.hpp"
|
||||
#include "InetAddress.hpp"
|
||||
#include "Topology.hpp"
|
||||
#include "Peer.hpp"
|
||||
#include "SelfAwareness.hpp"
|
||||
#include "Packet.hpp"
|
||||
#include "Cluster.hpp"
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
#ifdef ZT_TRACE
|
||||
static const char *etherTypeName(const unsigned int etherType)
|
||||
{
|
||||
switch(etherType) {
|
||||
case ZT_ETHERTYPE_IPV4: return "IPV4";
|
||||
case ZT_ETHERTYPE_ARP: return "ARP";
|
||||
case ZT_ETHERTYPE_RARP: return "RARP";
|
||||
case ZT_ETHERTYPE_ATALK: return "ATALK";
|
||||
case ZT_ETHERTYPE_AARP: return "AARP";
|
||||
case ZT_ETHERTYPE_IPX_A: return "IPX_A";
|
||||
case ZT_ETHERTYPE_IPX_B: return "IPX_B";
|
||||
case ZT_ETHERTYPE_IPV6: return "IPV6";
|
||||
}
|
||||
return "UNKNOWN";
|
||||
}
|
||||
#endif // ZT_TRACE
|
||||
|
||||
Switch::Switch(const RuntimeEnvironment *renv) :
|
||||
RR(renv),
|
||||
_lastBeaconResponse(0),
|
||||
_outstandingWhoisRequests(32),
|
||||
_lastUniteAttempt(8) // only really used on root servers and upstreams, and it'll grow there just fine
|
||||
{
|
||||
}
|
||||
|
||||
void Switch::onRemotePacket(const InetAddress &localAddr,const InetAddress &fromAddr,const void *data,unsigned int len)
|
||||
{
|
||||
try {
|
||||
const uint64_t now = RR->node->now();
|
||||
|
||||
SharedPtr<Path> path(RR->topology->getPath(localAddr,fromAddr));
|
||||
path->received(now);
|
||||
|
||||
if (len == 13) {
|
||||
/* LEGACY: before VERB_PUSH_DIRECT_PATHS, peers used broadcast
|
||||
* announcements on the LAN to solve the 'same network problem.' We
|
||||
* no longer send these, but we'll listen for them for a while to
|
||||
* locate peers with versions <1.0.4. */
|
||||
|
||||
const Address beaconAddr(reinterpret_cast<const char *>(data) + 8,5);
|
||||
if (beaconAddr == RR->identity.address())
|
||||
return;
|
||||
if (!RR->node->shouldUsePathForZeroTierTraffic(beaconAddr,localAddr,fromAddr))
|
||||
return;
|
||||
const SharedPtr<Peer> peer(RR->topology->getPeer(beaconAddr));
|
||||
if (peer) { // we'll only respond to beacons from known peers
|
||||
if ((now - _lastBeaconResponse) >= 2500) { // limit rate of responses
|
||||
_lastBeaconResponse = now;
|
||||
Packet outp(peer->address(),RR->identity.address(),Packet::VERB_NOP);
|
||||
outp.armor(peer->key(),true,path->nextOutgoingCounter());
|
||||
path->send(RR,outp.data(),outp.size(),now);
|
||||
}
|
||||
}
|
||||
|
||||
} else if (len > ZT_PROTO_MIN_FRAGMENT_LENGTH) { // SECURITY: min length check is important since we do some C-style stuff below!
|
||||
if (reinterpret_cast<const uint8_t *>(data)[ZT_PACKET_FRAGMENT_IDX_FRAGMENT_INDICATOR] == ZT_PACKET_FRAGMENT_INDICATOR) {
|
||||
// Handle fragment ----------------------------------------------------
|
||||
|
||||
Packet::Fragment fragment(data,len);
|
||||
const Address destination(fragment.destination());
|
||||
|
||||
if (destination != RR->identity.address()) {
|
||||
#ifdef ZT_ENABLE_CLUSTER
|
||||
const bool isClusterFrontplane = ((RR->cluster)&&(RR->cluster->isClusterPeerFrontplane(fromAddr)));
|
||||
#else
|
||||
const bool isClusterFrontplane = false;
|
||||
#endif
|
||||
|
||||
if ( (!RR->topology->amRoot()) && (!path->trustEstablished(now)) && (!isClusterFrontplane) )
|
||||
return;
|
||||
|
||||
if (fragment.hops() < ZT_RELAY_MAX_HOPS) {
|
||||
fragment.incrementHops();
|
||||
|
||||
// Note: we don't bother initiating NAT-t for fragments, since heads will set that off.
|
||||
// It wouldn't hurt anything, just redundant and unnecessary.
|
||||
SharedPtr<Peer> relayTo = RR->topology->getPeer(destination);
|
||||
if ((!relayTo)||(!relayTo->sendDirect(fragment.data(),fragment.size(),now,false))) {
|
||||
#ifdef ZT_ENABLE_CLUSTER
|
||||
if ((RR->cluster)&&(!isClusterFrontplane)) {
|
||||
RR->cluster->relayViaCluster(Address(),destination,fragment.data(),fragment.size(),false);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Don't know peer or no direct path -- so relay via someone upstream
|
||||
relayTo = RR->topology->getUpstreamPeer();
|
||||
if (relayTo)
|
||||
relayTo->sendDirect(fragment.data(),fragment.size(),now,true);
|
||||
}
|
||||
} else {
|
||||
TRACE("dropped relay [fragment](%s) -> %s, max hops exceeded",fromAddr.toString().c_str(),destination.toString().c_str());
|
||||
}
|
||||
} else {
|
||||
// Fragment looks like ours
|
||||
const uint64_t fragmentPacketId = fragment.packetId();
|
||||
const unsigned int fragmentNumber = fragment.fragmentNumber();
|
||||
const unsigned int totalFragments = fragment.totalFragments();
|
||||
|
||||
if ((totalFragments <= ZT_MAX_PACKET_FRAGMENTS)&&(fragmentNumber < ZT_MAX_PACKET_FRAGMENTS)&&(fragmentNumber > 0)&&(totalFragments > 1)) {
|
||||
// Fragment appears basically sane. Its fragment number must be
|
||||
// 1 or more, since a Packet with fragmented bit set is fragment 0.
|
||||
// Total fragments must be more than 1, otherwise why are we
|
||||
// seeing a Packet::Fragment?
|
||||
|
||||
Mutex::Lock _l(_rxQueue_m);
|
||||
RXQueueEntry *const rq = _findRXQueueEntry(now,fragmentPacketId);
|
||||
|
||||
if ((!rq->timestamp)||(rq->packetId != fragmentPacketId)) {
|
||||
// No packet found, so we received a fragment without its head.
|
||||
//TRACE("fragment (%u/%u) of %.16llx from %s",fragmentNumber + 1,totalFragments,fragmentPacketId,fromAddr.toString().c_str());
|
||||
|
||||
rq->timestamp = now;
|
||||
rq->packetId = fragmentPacketId;
|
||||
rq->frags[fragmentNumber - 1] = fragment;
|
||||
rq->totalFragments = totalFragments; // total fragment count is known
|
||||
rq->haveFragments = 1 << fragmentNumber; // we have only this fragment
|
||||
rq->complete = false;
|
||||
} else if (!(rq->haveFragments & (1 << fragmentNumber))) {
|
||||
// We have other fragments and maybe the head, so add this one and check
|
||||
//TRACE("fragment (%u/%u) of %.16llx from %s",fragmentNumber + 1,totalFragments,fragmentPacketId,fromAddr.toString().c_str());
|
||||
|
||||
rq->frags[fragmentNumber - 1] = fragment;
|
||||
rq->totalFragments = totalFragments;
|
||||
|
||||
if (Utils::countBits(rq->haveFragments |= (1 << fragmentNumber)) == totalFragments) {
|
||||
// We have all fragments -- assemble and process full Packet
|
||||
//TRACE("packet %.16llx is complete, assembling and processing...",fragmentPacketId);
|
||||
|
||||
for(unsigned int f=1;f<totalFragments;++f)
|
||||
rq->frag0.append(rq->frags[f - 1].payload(),rq->frags[f - 1].payloadLength());
|
||||
|
||||
if (rq->frag0.tryDecode(RR)) {
|
||||
rq->timestamp = 0; // packet decoded, free entry
|
||||
} else {
|
||||
rq->complete = true; // set complete flag but leave entry since it probably needs WHOIS or something
|
||||
}
|
||||
}
|
||||
} // else this is a duplicate fragment, ignore
|
||||
}
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
} else if (len >= ZT_PROTO_MIN_PACKET_LENGTH) { // min length check is important!
|
||||
// Handle packet head -------------------------------------------------
|
||||
|
||||
const Address destination(reinterpret_cast<const uint8_t *>(data) + 8,ZT_ADDRESS_LENGTH);
|
||||
const Address source(reinterpret_cast<const uint8_t *>(data) + 13,ZT_ADDRESS_LENGTH);
|
||||
|
||||
//TRACE("<< %.16llx %s -> %s (size: %u)",(unsigned long long)packet->packetId(),source.toString().c_str(),destination.toString().c_str(),packet->size());
|
||||
|
||||
#ifdef ZT_ENABLE_CLUSTER
|
||||
if ( (source == RR->identity.address()) && ((!RR->cluster)||(!RR->cluster->isClusterPeerFrontplane(fromAddr))) )
|
||||
return;
|
||||
#else
|
||||
if (source == RR->identity.address())
|
||||
return;
|
||||
#endif
|
||||
|
||||
if (destination != RR->identity.address()) {
|
||||
if ( (!RR->topology->amRoot()) && (!path->trustEstablished(now)) && (source != RR->identity.address()) )
|
||||
return;
|
||||
|
||||
Packet packet(data,len);
|
||||
|
||||
if (packet.hops() < ZT_RELAY_MAX_HOPS) {
|
||||
#ifdef ZT_ENABLE_CLUSTER
|
||||
if (source != RR->identity.address()) // don't increment hops for cluster frontplane relays
|
||||
packet.incrementHops();
|
||||
#else
|
||||
packet.incrementHops();
|
||||
#endif
|
||||
|
||||
SharedPtr<Peer> relayTo = RR->topology->getPeer(destination);
|
||||
if ((relayTo)&&(relayTo->sendDirect(packet.data(),packet.size(),now,false))) {
|
||||
if ((source != RR->identity.address())&&(_shouldUnite(now,source,destination))) { // don't send RENDEZVOUS for cluster frontplane relays
|
||||
const InetAddress *hintToSource = (InetAddress *)0;
|
||||
const InetAddress *hintToDest = (InetAddress *)0;
|
||||
|
||||
InetAddress destV4,destV6;
|
||||
InetAddress sourceV4,sourceV6;
|
||||
relayTo->getRendezvousAddresses(now,destV4,destV6);
|
||||
|
||||
const SharedPtr<Peer> sourcePeer(RR->topology->getPeer(source));
|
||||
if (sourcePeer) {
|
||||
sourcePeer->getRendezvousAddresses(now,sourceV4,sourceV6);
|
||||
if ((destV6)&&(sourceV6)) {
|
||||
hintToSource = &destV6;
|
||||
hintToDest = &sourceV6;
|
||||
} else if ((destV4)&&(sourceV4)) {
|
||||
hintToSource = &destV4;
|
||||
hintToDest = &sourceV4;
|
||||
}
|
||||
|
||||
if ((hintToSource)&&(hintToDest)) {
|
||||
unsigned int alt = (unsigned int)RR->node->prng() & 1; // randomize which hint we send first for obscure NAT-t reasons
|
||||
const unsigned int completed = alt + 2;
|
||||
while (alt != completed) {
|
||||
if ((alt & 1) == 0) {
|
||||
Packet outp(source,RR->identity.address(),Packet::VERB_RENDEZVOUS);
|
||||
outp.append((uint8_t)0);
|
||||
destination.appendTo(outp);
|
||||
outp.append((uint16_t)hintToSource->port());
|
||||
if (hintToSource->ss_family == AF_INET6) {
|
||||
outp.append((uint8_t)16);
|
||||
outp.append(hintToSource->rawIpData(),16);
|
||||
} else {
|
||||
outp.append((uint8_t)4);
|
||||
outp.append(hintToSource->rawIpData(),4);
|
||||
}
|
||||
send(outp,true);
|
||||
} else {
|
||||
Packet outp(destination,RR->identity.address(),Packet::VERB_RENDEZVOUS);
|
||||
outp.append((uint8_t)0);
|
||||
source.appendTo(outp);
|
||||
outp.append((uint16_t)hintToDest->port());
|
||||
if (hintToDest->ss_family == AF_INET6) {
|
||||
outp.append((uint8_t)16);
|
||||
outp.append(hintToDest->rawIpData(),16);
|
||||
} else {
|
||||
outp.append((uint8_t)4);
|
||||
outp.append(hintToDest->rawIpData(),4);
|
||||
}
|
||||
send(outp,true);
|
||||
}
|
||||
++alt;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
#ifdef ZT_ENABLE_CLUSTER
|
||||
if ((RR->cluster)&&(source != RR->identity.address())) {
|
||||
RR->cluster->relayViaCluster(source,destination,packet.data(),packet.size(),_shouldUnite(now,source,destination));
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
relayTo = RR->topology->getUpstreamPeer(&source,1,true);
|
||||
if (relayTo)
|
||||
relayTo->sendDirect(packet.data(),packet.size(),now,true);
|
||||
}
|
||||
} else {
|
||||
TRACE("dropped relay %s(%s) -> %s, max hops exceeded",packet.source().toString().c_str(),fromAddr.toString().c_str(),destination.toString().c_str());
|
||||
}
|
||||
} else if ((reinterpret_cast<const uint8_t *>(data)[ZT_PACKET_IDX_FLAGS] & ZT_PROTO_FLAG_FRAGMENTED) != 0) {
|
||||
// Packet is the head of a fragmented packet series
|
||||
|
||||
const uint64_t packetId = (
|
||||
(((uint64_t)reinterpret_cast<const uint8_t *>(data)[0]) << 56) |
|
||||
(((uint64_t)reinterpret_cast<const uint8_t *>(data)[1]) << 48) |
|
||||
(((uint64_t)reinterpret_cast<const uint8_t *>(data)[2]) << 40) |
|
||||
(((uint64_t)reinterpret_cast<const uint8_t *>(data)[3]) << 32) |
|
||||
(((uint64_t)reinterpret_cast<const uint8_t *>(data)[4]) << 24) |
|
||||
(((uint64_t)reinterpret_cast<const uint8_t *>(data)[5]) << 16) |
|
||||
(((uint64_t)reinterpret_cast<const uint8_t *>(data)[6]) << 8) |
|
||||
((uint64_t)reinterpret_cast<const uint8_t *>(data)[7])
|
||||
);
|
||||
|
||||
Mutex::Lock _l(_rxQueue_m);
|
||||
RXQueueEntry *const rq = _findRXQueueEntry(now,packetId);
|
||||
|
||||
if ((!rq->timestamp)||(rq->packetId != packetId)) {
|
||||
// If we have no other fragments yet, create an entry and save the head
|
||||
//TRACE("fragment (0/?) of %.16llx from %s",pid,fromAddr.toString().c_str());
|
||||
|
||||
rq->timestamp = now;
|
||||
rq->packetId = packetId;
|
||||
rq->frag0.init(data,len,path,now);
|
||||
rq->totalFragments = 0;
|
||||
rq->haveFragments = 1;
|
||||
rq->complete = false;
|
||||
} else if (!(rq->haveFragments & 1)) {
|
||||
// If we have other fragments but no head, see if we are complete with the head
|
||||
|
||||
if ((rq->totalFragments > 1)&&(Utils::countBits(rq->haveFragments |= 1) == rq->totalFragments)) {
|
||||
// We have all fragments -- assemble and process full Packet
|
||||
//TRACE("packet %.16llx is complete, assembling and processing...",pid);
|
||||
|
||||
rq->frag0.init(data,len,path,now);
|
||||
for(unsigned int f=1;f<rq->totalFragments;++f)
|
||||
rq->frag0.append(rq->frags[f - 1].payload(),rq->frags[f - 1].payloadLength());
|
||||
|
||||
if (rq->frag0.tryDecode(RR)) {
|
||||
rq->timestamp = 0; // packet decoded, free entry
|
||||
} else {
|
||||
rq->complete = true; // set complete flag but leave entry since it probably needs WHOIS or something
|
||||
}
|
||||
} else {
|
||||
// Still waiting on more fragments, but keep the head
|
||||
rq->frag0.init(data,len,path,now);
|
||||
}
|
||||
} // else this is a duplicate head, ignore
|
||||
} else {
|
||||
// Packet is unfragmented, so just process it
|
||||
IncomingPacket packet(data,len,path,now);
|
||||
if (!packet.tryDecode(RR)) {
|
||||
Mutex::Lock _l(_rxQueue_m);
|
||||
RXQueueEntry *rq = &(_rxQueue[ZT_RX_QUEUE_SIZE - 1]);
|
||||
unsigned long i = ZT_RX_QUEUE_SIZE - 1;
|
||||
while ((i)&&(rq->timestamp)) {
|
||||
RXQueueEntry *tmp = &(_rxQueue[--i]);
|
||||
if (tmp->timestamp < rq->timestamp)
|
||||
rq = tmp;
|
||||
}
|
||||
rq->timestamp = now;
|
||||
rq->packetId = packet.packetId();
|
||||
rq->frag0 = packet;
|
||||
rq->totalFragments = 1;
|
||||
rq->haveFragments = 1;
|
||||
rq->complete = true;
|
||||
}
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
}
|
||||
}
|
||||
} catch (std::exception &ex) {
|
||||
TRACE("dropped packet from %s: unexpected exception: %s",fromAddr.toString().c_str(),ex.what());
|
||||
} catch ( ... ) {
|
||||
TRACE("dropped packet from %s: unexpected exception: (unknown)",fromAddr.toString().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void Switch::onLocalEthernet(const SharedPtr<Network> &network,const MAC &from,const MAC &to,unsigned int etherType,unsigned int vlanId,const void *data,unsigned int len)
|
||||
{
|
||||
if (!network->hasConfig())
|
||||
return;
|
||||
|
||||
// Check if this packet is from someone other than the tap -- i.e. bridged in
|
||||
bool fromBridged;
|
||||
if ((fromBridged = (from != network->mac()))) {
|
||||
if (!network->config().permitsBridging(RR->identity.address())) {
|
||||
TRACE("%.16llx: %s -> %s %s not forwarded, bridging disabled or this peer not a bridge",network->id(),from.toString().c_str(),to.toString().c_str(),etherTypeName(etherType));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (to.isMulticast()) {
|
||||
MulticastGroup multicastGroup(to,0);
|
||||
|
||||
if (to.isBroadcast()) {
|
||||
if ( (etherType == ZT_ETHERTYPE_ARP) && (len >= 28) && ((((const uint8_t *)data)[2] == 0x08)&&(((const uint8_t *)data)[3] == 0x00)&&(((const uint8_t *)data)[4] == 6)&&(((const uint8_t *)data)[5] == 4)&&(((const uint8_t *)data)[7] == 0x01)) ) {
|
||||
/* IPv4 ARP is one of the few special cases that we impose upon what is
|
||||
* otherwise a straightforward Ethernet switch emulation. Vanilla ARP
|
||||
* is dumb old broadcast and simply doesn't scale. ZeroTier multicast
|
||||
* groups have an additional field called ADI (additional distinguishing
|
||||
* information) which was added specifically for ARP though it could
|
||||
* be used for other things too. We then take ARP broadcasts and turn
|
||||
* them into multicasts by stuffing the IP address being queried into
|
||||
* the 32-bit ADI field. In practice this uses our multicast pub/sub
|
||||
* system to implement a kind of extended/distributed ARP table. */
|
||||
multicastGroup = MulticastGroup::deriveMulticastGroupForAddressResolution(InetAddress(((const unsigned char *)data) + 24,4,0));
|
||||
} else if (!network->config().enableBroadcast()) {
|
||||
// Don't transmit broadcasts if this network doesn't want them
|
||||
TRACE("%.16llx: dropped broadcast since ff:ff:ff:ff:ff:ff is not enabled",network->id());
|
||||
return;
|
||||
}
|
||||
} else if ((etherType == ZT_ETHERTYPE_IPV6)&&(len >= (40 + 8 + 16))) {
|
||||
// IPv6 NDP emulation for certain very special patterns of private IPv6 addresses -- if enabled
|
||||
if ((network->config().ndpEmulation())&&(reinterpret_cast<const uint8_t *>(data)[6] == 0x3a)&&(reinterpret_cast<const uint8_t *>(data)[40] == 0x87)) { // ICMPv6 neighbor solicitation
|
||||
Address v6EmbeddedAddress;
|
||||
const uint8_t *const pkt6 = reinterpret_cast<const uint8_t *>(data) + 40 + 8;
|
||||
const uint8_t *my6 = (const uint8_t *)0;
|
||||
|
||||
// ZT-RFC4193 address: fdNN:NNNN:NNNN:NNNN:NN99:93DD:DDDD:DDDD / 88 (one /128 per actual host)
|
||||
|
||||
// ZT-6PLANE address: fcXX:XXXX:XXDD:DDDD:DDDD:####:####:#### / 40 (one /80 per actual host)
|
||||
// (XX - lower 32 bits of network ID XORed with higher 32 bits)
|
||||
|
||||
// For these to work, we must have a ZT-managed address assigned in one of the
|
||||
// above formats, and the query must match its prefix.
|
||||
for(unsigned int sipk=0;sipk<network->config().staticIpCount;++sipk) {
|
||||
const InetAddress *const sip = &(network->config().staticIps[sipk]);
|
||||
if (sip->ss_family == AF_INET6) {
|
||||
my6 = reinterpret_cast<const uint8_t *>(reinterpret_cast<const struct sockaddr_in6 *>(&(*sip))->sin6_addr.s6_addr);
|
||||
const unsigned int sipNetmaskBits = Utils::ntoh((uint16_t)reinterpret_cast<const struct sockaddr_in6 *>(&(*sip))->sin6_port);
|
||||
if ((sipNetmaskBits == 88)&&(my6[0] == 0xfd)&&(my6[9] == 0x99)&&(my6[10] == 0x93)) { // ZT-RFC4193 /88 ???
|
||||
unsigned int ptr = 0;
|
||||
while (ptr != 11) {
|
||||
if (pkt6[ptr] != my6[ptr])
|
||||
break;
|
||||
++ptr;
|
||||
}
|
||||
if (ptr == 11) { // prefix match!
|
||||
v6EmbeddedAddress.setTo(pkt6 + ptr,5);
|
||||
break;
|
||||
}
|
||||
} else if (sipNetmaskBits == 40) { // ZT-6PLANE /40 ???
|
||||
const uint32_t nwid32 = (uint32_t)((network->id() ^ (network->id() >> 32)) & 0xffffffff);
|
||||
if ( (my6[0] == 0xfc) && (my6[1] == (uint8_t)((nwid32 >> 24) & 0xff)) && (my6[2] == (uint8_t)((nwid32 >> 16) & 0xff)) && (my6[3] == (uint8_t)((nwid32 >> 8) & 0xff)) && (my6[4] == (uint8_t)(nwid32 & 0xff))) {
|
||||
unsigned int ptr = 0;
|
||||
while (ptr != 5) {
|
||||
if (pkt6[ptr] != my6[ptr])
|
||||
break;
|
||||
++ptr;
|
||||
}
|
||||
if (ptr == 5) { // prefix match!
|
||||
v6EmbeddedAddress.setTo(pkt6 + ptr,5);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((v6EmbeddedAddress)&&(v6EmbeddedAddress != RR->identity.address())) {
|
||||
const MAC peerMac(v6EmbeddedAddress,network->id());
|
||||
TRACE("IPv6 NDP emulation: %.16llx: forging response for %s/%s",network->id(),v6EmbeddedAddress.toString().c_str(),peerMac.toString().c_str());
|
||||
|
||||
uint8_t adv[72];
|
||||
adv[0] = 0x60; adv[1] = 0x00; adv[2] = 0x00; adv[3] = 0x00;
|
||||
adv[4] = 0x00; adv[5] = 0x20;
|
||||
adv[6] = 0x3a; adv[7] = 0xff;
|
||||
for(int i=0;i<16;++i) adv[8 + i] = pkt6[i];
|
||||
for(int i=0;i<16;++i) adv[24 + i] = my6[i];
|
||||
adv[40] = 0x88; adv[41] = 0x00;
|
||||
adv[42] = 0x00; adv[43] = 0x00; // future home of checksum
|
||||
adv[44] = 0x60; adv[45] = 0x00; adv[46] = 0x00; adv[47] = 0x00;
|
||||
for(int i=0;i<16;++i) adv[48 + i] = pkt6[i];
|
||||
adv[64] = 0x02; adv[65] = 0x01;
|
||||
adv[66] = peerMac[0]; adv[67] = peerMac[1]; adv[68] = peerMac[2]; adv[69] = peerMac[3]; adv[70] = peerMac[4]; adv[71] = peerMac[5];
|
||||
|
||||
uint16_t pseudo_[36];
|
||||
uint8_t *const pseudo = reinterpret_cast<uint8_t *>(pseudo_);
|
||||
for(int i=0;i<32;++i) pseudo[i] = adv[8 + i];
|
||||
pseudo[32] = 0x00; pseudo[33] = 0x00; pseudo[34] = 0x00; pseudo[35] = 0x20;
|
||||
pseudo[36] = 0x00; pseudo[37] = 0x00; pseudo[38] = 0x00; pseudo[39] = 0x3a;
|
||||
for(int i=0;i<32;++i) pseudo[40 + i] = adv[40 + i];
|
||||
uint32_t checksum = 0;
|
||||
for(int i=0;i<36;++i) checksum += Utils::hton(pseudo_[i]);
|
||||
while ((checksum >> 16)) checksum = (checksum & 0xffff) + (checksum >> 16);
|
||||
checksum = ~checksum;
|
||||
adv[42] = (checksum >> 8) & 0xff;
|
||||
adv[43] = checksum & 0xff;
|
||||
|
||||
RR->node->putFrame(network->id(),network->userPtr(),peerMac,from,ZT_ETHERTYPE_IPV6,0,adv,72);
|
||||
return; // NDP emulation done. We have forged a "fake" reply, so no need to send actual NDP query.
|
||||
} // else no NDP emulation
|
||||
} // else no NDP emulation
|
||||
}
|
||||
|
||||
// Check this after NDP emulation, since that has to be allowed in exactly this case
|
||||
if (network->config().multicastLimit == 0) {
|
||||
TRACE("%.16llx: dropped multicast: not allowed on network",network->id());
|
||||
return;
|
||||
}
|
||||
|
||||
/* Learn multicast groups for bridged-in hosts.
|
||||
* Note that some OSes, most notably Linux, do this for you by learning
|
||||
* multicast addresses on bridge interfaces and subscribing each slave.
|
||||
* But in that case this does no harm, as the sets are just merged. */
|
||||
if (fromBridged)
|
||||
network->learnBridgedMulticastGroup(multicastGroup,RR->node->now());
|
||||
|
||||
//TRACE("%.16llx: MULTICAST %s -> %s %s %u",network->id(),from.toString().c_str(),multicastGroup.toString().c_str(),etherTypeName(etherType),len);
|
||||
|
||||
// First pass sets noTee to false, but noTee is set to true in OutboundMulticast to prevent duplicates.
|
||||
if (!network->filterOutgoingPacket(false,RR->identity.address(),Address(),from,to,(const uint8_t *)data,len,etherType,vlanId)) {
|
||||
TRACE("%.16llx: %s -> %s %s packet not sent: filterOutgoingPacket() returned false",network->id(),from.toString().c_str(),to.toString().c_str(),etherTypeName(etherType));
|
||||
return;
|
||||
}
|
||||
|
||||
RR->mc->send(
|
||||
network->config().multicastLimit,
|
||||
RR->node->now(),
|
||||
network->id(),
|
||||
network->config().disableCompression(),
|
||||
network->config().activeBridges(),
|
||||
multicastGroup,
|
||||
(fromBridged) ? from : MAC(),
|
||||
etherType,
|
||||
data,
|
||||
len);
|
||||
} else if (to == network->mac()) {
|
||||
// Destination is this node, so just reinject it
|
||||
RR->node->putFrame(network->id(),network->userPtr(),from,to,etherType,vlanId,data,len);
|
||||
} else if (to[0] == MAC::firstOctetForNetwork(network->id())) {
|
||||
// Destination is another ZeroTier peer on the same network
|
||||
|
||||
Address toZT(to.toAddress(network->id())); // since in-network MACs are derived from addresses and network IDs, we can reverse this
|
||||
SharedPtr<Peer> toPeer(RR->topology->getPeer(toZT));
|
||||
|
||||
if (!network->filterOutgoingPacket(false,RR->identity.address(),toZT,from,to,(const uint8_t *)data,len,etherType,vlanId)) {
|
||||
TRACE("%.16llx: %s -> %s %s packet not sent: filterOutgoingPacket() returned false",network->id(),from.toString().c_str(),to.toString().c_str(),etherTypeName(etherType));
|
||||
return;
|
||||
}
|
||||
|
||||
if (fromBridged) {
|
||||
Packet outp(toZT,RR->identity.address(),Packet::VERB_EXT_FRAME);
|
||||
outp.append(network->id());
|
||||
outp.append((unsigned char)0x00);
|
||||
to.appendTo(outp);
|
||||
from.appendTo(outp);
|
||||
outp.append((uint16_t)etherType);
|
||||
outp.append(data,len);
|
||||
if (!network->config().disableCompression())
|
||||
outp.compress();
|
||||
send(outp,true);
|
||||
} else {
|
||||
Packet outp(toZT,RR->identity.address(),Packet::VERB_FRAME);
|
||||
outp.append(network->id());
|
||||
outp.append((uint16_t)etherType);
|
||||
outp.append(data,len);
|
||||
if (!network->config().disableCompression())
|
||||
outp.compress();
|
||||
send(outp,true);
|
||||
}
|
||||
|
||||
//TRACE("%.16llx: UNICAST: %s -> %s etherType==%s(%.4x) vlanId==%u len==%u fromBridged==%d includeCom==%d",network->id(),from.toString().c_str(),to.toString().c_str(),etherTypeName(etherType),etherType,vlanId,len,(int)fromBridged,(int)includeCom);
|
||||
} else {
|
||||
// Destination is bridged behind a remote peer
|
||||
|
||||
// We filter with a NULL destination ZeroTier address first. Filtrations
|
||||
// for each ZT destination are also done below. This is the same rationale
|
||||
// and design as for multicast.
|
||||
if (!network->filterOutgoingPacket(false,RR->identity.address(),Address(),from,to,(const uint8_t *)data,len,etherType,vlanId)) {
|
||||
TRACE("%.16llx: %s -> %s %s packet not sent: filterOutgoingPacket() returned false",network->id(),from.toString().c_str(),to.toString().c_str(),etherTypeName(etherType));
|
||||
return;
|
||||
}
|
||||
|
||||
Address bridges[ZT_MAX_BRIDGE_SPAM];
|
||||
unsigned int numBridges = 0;
|
||||
|
||||
/* Create an array of up to ZT_MAX_BRIDGE_SPAM recipients for this bridged frame. */
|
||||
bridges[0] = network->findBridgeTo(to);
|
||||
std::vector<Address> activeBridges(network->config().activeBridges());
|
||||
if ((bridges[0])&&(bridges[0] != RR->identity.address())&&(network->config().permitsBridging(bridges[0]))) {
|
||||
/* We have a known bridge route for this MAC, send it there. */
|
||||
++numBridges;
|
||||
} else if (!activeBridges.empty()) {
|
||||
/* If there is no known route, spam to up to ZT_MAX_BRIDGE_SPAM active
|
||||
* bridges. If someone responds, we'll learn the route. */
|
||||
std::vector<Address>::const_iterator ab(activeBridges.begin());
|
||||
if (activeBridges.size() <= ZT_MAX_BRIDGE_SPAM) {
|
||||
// If there are <= ZT_MAX_BRIDGE_SPAM active bridges, spam them all
|
||||
while (ab != activeBridges.end()) {
|
||||
bridges[numBridges++] = *ab;
|
||||
++ab;
|
||||
}
|
||||
} else {
|
||||
// Otherwise pick a random set of them
|
||||
while (numBridges < ZT_MAX_BRIDGE_SPAM) {
|
||||
if (ab == activeBridges.end())
|
||||
ab = activeBridges.begin();
|
||||
if (((unsigned long)RR->node->prng() % (unsigned long)activeBridges.size()) == 0) {
|
||||
bridges[numBridges++] = *ab;
|
||||
++ab;
|
||||
} else ++ab;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(unsigned int b=0;b<numBridges;++b) {
|
||||
if (network->filterOutgoingPacket(true,RR->identity.address(),bridges[b],from,to,(const uint8_t *)data,len,etherType,vlanId)) {
|
||||
Packet outp(bridges[b],RR->identity.address(),Packet::VERB_EXT_FRAME);
|
||||
outp.append(network->id());
|
||||
outp.append((uint8_t)0x00);
|
||||
to.appendTo(outp);
|
||||
from.appendTo(outp);
|
||||
outp.append((uint16_t)etherType);
|
||||
outp.append(data,len);
|
||||
if (!network->config().disableCompression())
|
||||
outp.compress();
|
||||
send(outp,true);
|
||||
} else {
|
||||
TRACE("%.16llx: %s -> %s %s packet not sent: filterOutgoingPacket() returned false",network->id(),from.toString().c_str(),to.toString().c_str(),etherTypeName(etherType));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Switch::send(Packet &packet,bool encrypt)
|
||||
{
|
||||
if (packet.destination() == RR->identity.address()) {
|
||||
TRACE("BUG: caught attempt to send() to self, ignored");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_trySend(packet,encrypt)) {
|
||||
Mutex::Lock _l(_txQueue_m);
|
||||
_txQueue.push_back(TXQueueEntry(packet.destination(),RR->node->now(),packet,encrypt));
|
||||
}
|
||||
}
|
||||
|
||||
void Switch::requestWhois(const Address &addr)
|
||||
{
|
||||
bool inserted = false;
|
||||
{
|
||||
Mutex::Lock _l(_outstandingWhoisRequests_m);
|
||||
WhoisRequest &r = _outstandingWhoisRequests[addr];
|
||||
if (r.lastSent) {
|
||||
r.retries = 0; // reset retry count if entry already existed, but keep waiting and retry again after normal timeout
|
||||
} else {
|
||||
r.lastSent = RR->node->now();
|
||||
inserted = true;
|
||||
}
|
||||
}
|
||||
if (inserted)
|
||||
_sendWhoisRequest(addr,(const Address *)0,0);
|
||||
}
|
||||
|
||||
void Switch::doAnythingWaitingForPeer(const SharedPtr<Peer> &peer)
|
||||
{
|
||||
{ // cancel pending WHOIS since we now know this peer
|
||||
Mutex::Lock _l(_outstandingWhoisRequests_m);
|
||||
_outstandingWhoisRequests.erase(peer->address());
|
||||
}
|
||||
|
||||
{ // finish processing any packets waiting on peer's public key / identity
|
||||
Mutex::Lock _l(_rxQueue_m);
|
||||
unsigned long i = ZT_RX_QUEUE_SIZE;
|
||||
while (i) {
|
||||
RXQueueEntry *rq = &(_rxQueue[--i]);
|
||||
if ((rq->timestamp)&&(rq->complete)) {
|
||||
if (rq->frag0.tryDecode(RR))
|
||||
rq->timestamp = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{ // finish sending any packets waiting on peer's public key / identity
|
||||
Mutex::Lock _l(_txQueue_m);
|
||||
for(std::list< TXQueueEntry >::iterator txi(_txQueue.begin());txi!=_txQueue.end();) {
|
||||
if (txi->dest == peer->address()) {
|
||||
if (_trySend(txi->packet,txi->encrypt))
|
||||
_txQueue.erase(txi++);
|
||||
else ++txi;
|
||||
} else ++txi;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsigned long Switch::doTimerTasks(uint64_t now)
|
||||
{
|
||||
unsigned long nextDelay = 0xffffffff; // ceiling delay, caller will cap to minimum
|
||||
|
||||
{ // Retry outstanding WHOIS requests
|
||||
Mutex::Lock _l(_outstandingWhoisRequests_m);
|
||||
Hashtable< Address,WhoisRequest >::Iterator i(_outstandingWhoisRequests);
|
||||
Address *a = (Address *)0;
|
||||
WhoisRequest *r = (WhoisRequest *)0;
|
||||
while (i.next(a,r)) {
|
||||
const unsigned long since = (unsigned long)(now - r->lastSent);
|
||||
if (since >= ZT_WHOIS_RETRY_DELAY) {
|
||||
if (r->retries >= ZT_MAX_WHOIS_RETRIES) {
|
||||
TRACE("WHOIS %s timed out",a->toString().c_str());
|
||||
_outstandingWhoisRequests.erase(*a);
|
||||
} else {
|
||||
r->lastSent = now;
|
||||
r->peersConsulted[r->retries] = _sendWhoisRequest(*a,r->peersConsulted,(r->retries > 1) ? r->retries : 0);
|
||||
TRACE("WHOIS %s (retry %u)",a->toString().c_str(),r->retries);
|
||||
++r->retries;
|
||||
nextDelay = std::min(nextDelay,(unsigned long)ZT_WHOIS_RETRY_DELAY);
|
||||
}
|
||||
} else {
|
||||
nextDelay = std::min(nextDelay,ZT_WHOIS_RETRY_DELAY - since);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{ // Time out TX queue packets that never got WHOIS lookups or other info.
|
||||
Mutex::Lock _l(_txQueue_m);
|
||||
for(std::list< TXQueueEntry >::iterator txi(_txQueue.begin());txi!=_txQueue.end();) {
|
||||
if (_trySend(txi->packet,txi->encrypt))
|
||||
_txQueue.erase(txi++);
|
||||
else if ((now - txi->creationTime) > ZT_TRANSMIT_QUEUE_TIMEOUT) {
|
||||
TRACE("TX %s -> %s timed out",txi->packet.source().toString().c_str(),txi->packet.destination().toString().c_str());
|
||||
_txQueue.erase(txi++);
|
||||
} else ++txi;
|
||||
}
|
||||
}
|
||||
|
||||
{ // Remove really old last unite attempt entries to keep table size controlled
|
||||
Mutex::Lock _l(_lastUniteAttempt_m);
|
||||
Hashtable< _LastUniteKey,uint64_t >::Iterator i(_lastUniteAttempt);
|
||||
_LastUniteKey *k = (_LastUniteKey *)0;
|
||||
uint64_t *v = (uint64_t *)0;
|
||||
while (i.next(k,v)) {
|
||||
if ((now - *v) >= (ZT_MIN_UNITE_INTERVAL * 8))
|
||||
_lastUniteAttempt.erase(*k);
|
||||
}
|
||||
}
|
||||
|
||||
return nextDelay;
|
||||
}
|
||||
|
||||
bool Switch::_shouldUnite(const uint64_t now,const Address &source,const Address &destination)
|
||||
{
|
||||
Mutex::Lock _l(_lastUniteAttempt_m);
|
||||
uint64_t &ts = _lastUniteAttempt[_LastUniteKey(source,destination)];
|
||||
if ((now - ts) >= ZT_MIN_UNITE_INTERVAL) {
|
||||
ts = now;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Address Switch::_sendWhoisRequest(const Address &addr,const Address *peersAlreadyConsulted,unsigned int numPeersAlreadyConsulted)
|
||||
{
|
||||
SharedPtr<Peer> upstream(RR->topology->getUpstreamPeer(peersAlreadyConsulted,numPeersAlreadyConsulted,false));
|
||||
if (upstream) {
|
||||
Packet outp(upstream->address(),RR->identity.address(),Packet::VERB_WHOIS);
|
||||
addr.appendTo(outp);
|
||||
RR->node->expectReplyTo(outp.packetId());
|
||||
send(outp,true);
|
||||
}
|
||||
return Address();
|
||||
}
|
||||
|
||||
bool Switch::_trySend(Packet &packet,bool encrypt)
|
||||
{
|
||||
SharedPtr<Path> viaPath;
|
||||
const uint64_t now = RR->node->now();
|
||||
const Address destination(packet.destination());
|
||||
|
||||
#ifdef ZT_ENABLE_CLUSTER
|
||||
uint64_t clusterMostRecentTs = 0;
|
||||
int clusterMostRecentMemberId = -1;
|
||||
uint8_t clusterPeerSecret[ZT_PEER_SECRET_KEY_LENGTH];
|
||||
if (RR->cluster)
|
||||
clusterMostRecentMemberId = RR->cluster->checkSendViaCluster(destination,clusterMostRecentTs,clusterPeerSecret);
|
||||
#endif
|
||||
|
||||
const SharedPtr<Peer> peer(RR->topology->getPeer(destination));
|
||||
if (peer) {
|
||||
/* First get the best path, and if it's dead (and this is not a root)
|
||||
* we attempt to re-activate that path but this packet will flow
|
||||
* upstream. If the path comes back alive, it will be used in the future.
|
||||
* For roots we don't do the alive check since roots are not required
|
||||
* to send heartbeats "down" and because we have to at least try to
|
||||
* go somewhere. */
|
||||
|
||||
viaPath = peer->getBestPath(now,false);
|
||||
if ( (viaPath) && (!viaPath->alive(now)) && (!RR->topology->isUpstream(peer->identity())) ) {
|
||||
#ifdef ZT_ENABLE_CLUSTER
|
||||
if ((clusterMostRecentMemberId < 0)||(viaPath->lastIn() > clusterMostRecentTs)) {
|
||||
#endif
|
||||
if ((now - viaPath->lastOut()) > std::max((now - viaPath->lastIn()) * 4,(uint64_t)ZT_PATH_MIN_REACTIVATE_INTERVAL)) {
|
||||
peer->attemptToContactAt(viaPath->localAddress(),viaPath->address(),now,false,viaPath->nextOutgoingCounter());
|
||||
viaPath->sent(now);
|
||||
}
|
||||
#ifdef ZT_ENABLE_CLUSTER
|
||||
}
|
||||
#endif
|
||||
viaPath.zero();
|
||||
}
|
||||
|
||||
#ifdef ZT_ENABLE_CLUSTER
|
||||
if (clusterMostRecentMemberId >= 0) {
|
||||
if ((viaPath)&&(viaPath->lastIn() < clusterMostRecentTs))
|
||||
viaPath.zero();
|
||||
} else if (!viaPath) {
|
||||
#else
|
||||
if (!viaPath) {
|
||||
#endif
|
||||
peer->tryMemorizedPath(now); // periodically attempt memorized or statically defined paths, if any are known
|
||||
const SharedPtr<Peer> relay(RR->topology->getUpstreamPeer());
|
||||
if ( (!relay) || (!(viaPath = relay->getBestPath(now,false))) ) {
|
||||
if (!(viaPath = peer->getBestPath(now,true)))
|
||||
return false;
|
||||
}
|
||||
#ifdef ZT_ENABLE_CLUSTER
|
||||
}
|
||||
#else
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
#ifdef ZT_ENABLE_CLUSTER
|
||||
if (clusterMostRecentMemberId < 0) {
|
||||
#else
|
||||
requestWhois(destination);
|
||||
return false; // if we are not in cluster mode, there is no way we can send without knowing the peer directly
|
||||
#endif
|
||||
#ifdef ZT_ENABLE_CLUSTER
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
unsigned int chunkSize = std::min(packet.size(),(unsigned int)ZT_UDP_DEFAULT_PAYLOAD_MTU);
|
||||
packet.setFragmented(chunkSize < packet.size());
|
||||
|
||||
#ifdef ZT_ENABLE_CLUSTER
|
||||
const uint64_t trustedPathId = (viaPath) ? RR->topology->getOutboundPathTrust(viaPath->address()) : 0;
|
||||
if (trustedPathId) {
|
||||
packet.setTrusted(trustedPathId);
|
||||
} else {
|
||||
packet.armor((clusterMostRecentMemberId >= 0) ? clusterPeerSecret : peer->key(),encrypt,(viaPath) ? viaPath->nextOutgoingCounter() : 0);
|
||||
}
|
||||
#else
|
||||
const uint64_t trustedPathId = RR->topology->getOutboundPathTrust(viaPath->address());
|
||||
if (trustedPathId) {
|
||||
packet.setTrusted(trustedPathId);
|
||||
} else {
|
||||
packet.armor(peer->key(),encrypt,viaPath->nextOutgoingCounter());
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ZT_ENABLE_CLUSTER
|
||||
if ( ((viaPath)&&(viaPath->send(RR,packet.data(),chunkSize,now))) || ((clusterMostRecentMemberId >= 0)&&(RR->cluster->sendViaCluster(clusterMostRecentMemberId,destination,packet.data(),chunkSize))) ) {
|
||||
#else
|
||||
if (viaPath->send(RR,packet.data(),chunkSize,now)) {
|
||||
#endif
|
||||
if (chunkSize < packet.size()) {
|
||||
// Too big for one packet, fragment the rest
|
||||
unsigned int fragStart = chunkSize;
|
||||
unsigned int remaining = packet.size() - chunkSize;
|
||||
unsigned int fragsRemaining = (remaining / (ZT_UDP_DEFAULT_PAYLOAD_MTU - ZT_PROTO_MIN_FRAGMENT_LENGTH));
|
||||
if ((fragsRemaining * (ZT_UDP_DEFAULT_PAYLOAD_MTU - ZT_PROTO_MIN_FRAGMENT_LENGTH)) < remaining)
|
||||
++fragsRemaining;
|
||||
const unsigned int totalFragments = fragsRemaining + 1;
|
||||
|
||||
for(unsigned int fno=1;fno<totalFragments;++fno) {
|
||||
chunkSize = std::min(remaining,(unsigned int)(ZT_UDP_DEFAULT_PAYLOAD_MTU - ZT_PROTO_MIN_FRAGMENT_LENGTH));
|
||||
Packet::Fragment frag(packet,fragStart,chunkSize,fno,totalFragments);
|
||||
#ifdef ZT_ENABLE_CLUSTER
|
||||
if (viaPath)
|
||||
viaPath->send(RR,frag.data(),frag.size(),now);
|
||||
else if (clusterMostRecentMemberId >= 0)
|
||||
RR->cluster->sendViaCluster(clusterMostRecentMemberId,destination,frag.data(),frag.size());
|
||||
#else
|
||||
viaPath->send(RR,frag.data(),frag.size(),now);
|
||||
#endif
|
||||
fragStart += chunkSize;
|
||||
remaining -= chunkSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace ZeroTier
|
||||
221
zto/node/Switch.hpp
Normal file
221
zto/node/Switch.hpp
Normal file
@@ -0,0 +1,221 @@
|
||||
/*
|
||||
* 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_N_SWITCH_HPP
|
||||
#define ZT_N_SWITCH_HPP
|
||||
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
#include <list>
|
||||
|
||||
#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"
|
||||
#include "Hashtable.hpp"
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
class RuntimeEnvironment;
|
||||
class Peer;
|
||||
|
||||
/**
|
||||
* Core of the distributed Ethernet switch and protocol implementation
|
||||
*
|
||||
* This class is perhaps a bit misnamed, but it's basically where everything
|
||||
* meets. Transport-layer ZT packets come in here, as do virtual network
|
||||
* 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
|
||||
{
|
||||
public:
|
||||
Switch(const RuntimeEnvironment *renv);
|
||||
|
||||
/**
|
||||
* Called when a packet is received from the real network
|
||||
*
|
||||
* @param localAddr Local interface address
|
||||
* @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);
|
||||
|
||||
/**
|
||||
* Called when a packet comes from a local Ethernet tap
|
||||
*
|
||||
* @param network Which network's TAP did this packet come from?
|
||||
* @param from Originating MAC address
|
||||
* @param to Destination MAC address
|
||||
* @param etherType Ethernet packet type
|
||||
* @param vlanId VLAN ID or 0 if none
|
||||
* @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);
|
||||
|
||||
/**
|
||||
* Send a packet to a ZeroTier address (destination in packet)
|
||||
*
|
||||
* The packet must be fully composed with source and destination but not
|
||||
* yet encrypted. If the destination peer is known the packet
|
||||
* is sent immediately. Otherwise it is queued and a WHOIS is dispatched.
|
||||
*
|
||||
* The packet may be compressed. Compression isn't done here.
|
||||
*
|
||||
* Needless to say, the packet's source must be this node. Otherwise it
|
||||
* won't be encrypted right. (This is not used for relaying.)
|
||||
*
|
||||
* @param packet Packet to send (buffer may be modified)
|
||||
* @param encrypt Encrypt packet payload? (always true except for HELLO)
|
||||
*/
|
||||
void send(Packet &packet,bool encrypt);
|
||||
|
||||
/**
|
||||
* Request WHOIS on a given address
|
||||
*
|
||||
* @param addr Address to look up
|
||||
*/
|
||||
void requestWhois(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 peer New peer
|
||||
*/
|
||||
void doAnythingWaitingForPeer(const SharedPtr<Peer> &peer);
|
||||
|
||||
/**
|
||||
* Perform retries and other periodic timer tasks
|
||||
*
|
||||
* This can return a very long delay if there are no pending timer
|
||||
* tasks. The caller should cap this comparatively vs. other values.
|
||||
*
|
||||
* @param now Current time
|
||||
* @return Number of milliseconds until doTimerTasks() should be run again
|
||||
*/
|
||||
unsigned long doTimerTasks(uint64_t now);
|
||||
|
||||
private:
|
||||
bool _shouldUnite(const uint64_t now,const Address &source,const Address &destination);
|
||||
Address _sendWhoisRequest(const Address &addr,const Address *peersAlreadyConsulted,unsigned int numPeersAlreadyConsulted);
|
||||
bool _trySend(Packet &packet,bool encrypt); // packet is modified if return is true
|
||||
|
||||
const RuntimeEnvironment *const RR;
|
||||
uint64_t _lastBeaconResponse;
|
||||
|
||||
// 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;
|
||||
|
||||
// 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;
|
||||
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
|
||||
};
|
||||
RXQueueEntry _rxQueue[ZT_RX_QUEUE_SIZE];
|
||||
Mutex _rxQueue_m;
|
||||
|
||||
/* 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)
|
||||
{
|
||||
RXQueueEntry *rq;
|
||||
RXQueueEntry *oldest = &(_rxQueue[ZT_RX_QUEUE_SIZE - 1]);
|
||||
unsigned long i = ZT_RX_QUEUE_SIZE;
|
||||
while (i) {
|
||||
rq = &(_rxQueue[--i]);
|
||||
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;
|
||||
}
|
||||
|
||||
// ZeroTier-layer TX queue entry
|
||||
struct TXQueueEntry
|
||||
{
|
||||
TXQueueEntry() {}
|
||||
TXQueueEntry(Address d,uint64_t ct,const Packet &p,bool enc) :
|
||||
dest(d),
|
||||
creationTime(ct),
|
||||
packet(p),
|
||||
encrypt(enc) {}
|
||||
|
||||
Address dest;
|
||||
uint64_t creationTime;
|
||||
Packet packet; // unencrypted/unMAC'd packet -- this is done at send time
|
||||
bool encrypt;
|
||||
};
|
||||
std::list< TXQueueEntry > _txQueue;
|
||||
Mutex _txQueue_m;
|
||||
|
||||
// Tracks sending of VERB_RENDEZVOUS to relaying peers
|
||||
struct _LastUniteKey
|
||||
{
|
||||
_LastUniteKey() : x(0),y(0) {}
|
||||
_LastUniteKey(const Address &a1,const Address &a2)
|
||||
{
|
||||
if (a1 > a2) {
|
||||
x = a2.toInt();
|
||||
y = a1.toInt();
|
||||
} else {
|
||||
x = a1.toInt();
|
||||
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)); }
|
||||
uint64_t x,y;
|
||||
};
|
||||
Hashtable< _LastUniteKey,uint64_t > _lastUniteAttempt; // key is always sorted in ascending order, for set-like behavior
|
||||
Mutex _lastUniteAttempt_m;
|
||||
};
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
||||
#endif
|
||||
46
zto/node/Tag.cpp
Normal file
46
zto/node/Tag.cpp
Normal file
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* 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 "Tag.hpp"
|
||||
#include "RuntimeEnvironment.hpp"
|
||||
#include "Identity.hpp"
|
||||
#include "Topology.hpp"
|
||||
#include "Switch.hpp"
|
||||
#include "Network.hpp"
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
int Tag::verify(const RuntimeEnvironment *RR) const
|
||||
{
|
||||
if ((!_signedBy)||(_signedBy != Network::controllerFor(_networkId)))
|
||||
return -1;
|
||||
const Identity id(RR->topology->getIdentity(_signedBy));
|
||||
if (!id) {
|
||||
RR->sw->requestWhois(_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
|
||||
200
zto/node/Tag.hpp
Normal file
200
zto/node/Tag.hpp
Normal file
@@ -0,0 +1,200 @@
|
||||
/*
|
||||
* 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_TAG_HPP
|
||||
#define ZT_TAG_HPP
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "Constants.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:
|
||||
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 uint64_t ts,const Address &issuedTo,const uint32_t id,const uint32_t value) :
|
||||
_networkId(nwid),
|
||||
_ts(ts),
|
||||
_id(id),
|
||||
_value(value),
|
||||
_issuedTo(issuedTo),
|
||||
_signedBy()
|
||||
{
|
||||
}
|
||||
|
||||
inline uint64_t networkId() const { return _networkId; }
|
||||
inline uint64_t timestamp() const { return _ts; }
|
||||
inline uint32_t id() const { return _id; }
|
||||
inline const uint32_t &value() const { return _value; }
|
||||
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
|
||||
* @return 0 == OK, 1 == waiting for WHOIS, -1 == BAD signature or tag
|
||||
*/
|
||||
int verify(const RuntimeEnvironment *RR) const;
|
||||
|
||||
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(_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 std::runtime_error("invalid signature length");
|
||||
p += 2;
|
||||
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 std::runtime_error("extended field 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:
|
||||
uint64_t _networkId;
|
||||
uint64_t _ts;
|
||||
uint32_t _id;
|
||||
uint32_t _value;
|
||||
Address _issuedTo;
|
||||
Address _signedBy;
|
||||
C25519::Signature _signature;
|
||||
};
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
||||
#endif
|
||||
475
zto/node/Topology.cpp
Normal file
475
zto/node/Topology.cpp
Normal file
@@ -0,0 +1,475 @@
|
||||
/*
|
||||
* 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 "Topology.hpp"
|
||||
#include "RuntimeEnvironment.hpp"
|
||||
#include "Node.hpp"
|
||||
#include "Network.hpp"
|
||||
#include "NetworkConfig.hpp"
|
||||
#include "Buffer.hpp"
|
||||
#include "Switch.hpp"
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
/*
|
||||
* 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) :
|
||||
RR(renv),
|
||||
_trustedPathCount(0),
|
||||
_amRoot(false)
|
||||
{
|
||||
try {
|
||||
World cachedPlanet;
|
||||
std::string buf(RR->node->dataStoreGet("planet"));
|
||||
if (buf.length() > 0) {
|
||||
Buffer<ZT_WORLD_MAX_SERIALIZED_LENGTH> dswtmp(buf.data(),(unsigned int)buf.length());
|
||||
cachedPlanet.deserialize(dswtmp,0);
|
||||
}
|
||||
addWorld(cachedPlanet,false);
|
||||
} catch ( ... ) {}
|
||||
|
||||
World defaultPlanet;
|
||||
{
|
||||
Buffer<ZT_DEFAULT_WORLD_LENGTH> wtmp(ZT_DEFAULT_WORLD,ZT_DEFAULT_WORLD_LENGTH);
|
||||
defaultPlanet.deserialize(wtmp,0); // throws on error, which would indicate a bad static variable up top
|
||||
}
|
||||
addWorld(defaultPlanet,false);
|
||||
}
|
||||
|
||||
SharedPtr<Peer> Topology::addPeer(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(_peers_m);
|
||||
SharedPtr<Peer> &hp = _peers[peer->address()];
|
||||
if (!hp)
|
||||
hp = peer;
|
||||
np = hp;
|
||||
}
|
||||
|
||||
saveIdentity(np->identity());
|
||||
|
||||
return np;
|
||||
}
|
||||
|
||||
SharedPtr<Peer> Topology::getPeer(const Address &zta)
|
||||
{
|
||||
if (zta == RR->identity.address()) {
|
||||
TRACE("BUG: ignored attempt to getPeer() for self, returned NULL");
|
||||
return SharedPtr<Peer>();
|
||||
}
|
||||
|
||||
{
|
||||
Mutex::Lock _l(_peers_m);
|
||||
const SharedPtr<Peer> *const ap = _peers.get(zta);
|
||||
if (ap)
|
||||
return *ap;
|
||||
}
|
||||
|
||||
try {
|
||||
Identity id(_getIdentity(zta));
|
||||
if (id) {
|
||||
SharedPtr<Peer> np(new Peer(RR,RR->identity,id));
|
||||
{
|
||||
Mutex::Lock _l(_peers_m);
|
||||
SharedPtr<Peer> &ap = _peers[zta];
|
||||
if (!ap)
|
||||
ap.swap(np);
|
||||
return ap;
|
||||
}
|
||||
}
|
||||
} catch ( ... ) {} // invalid identity on disk?
|
||||
|
||||
return SharedPtr<Peer>();
|
||||
}
|
||||
|
||||
Identity Topology::getIdentity(const Address &zta)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
void Topology::saveIdentity(const Identity &id)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
SharedPtr<Peer> Topology::getUpstreamPeer(const Address *avoid,unsigned int avoidCount,bool strictAvoid)
|
||||
{
|
||||
const uint64_t now = RR->node->now();
|
||||
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;
|
||||
|
||||
Mutex::Lock _l1(_peers_m);
|
||||
Mutex::Lock _l2(_upstreams_m);
|
||||
|
||||
for(std::vector<Address>::const_iterator a(_upstreamAddresses.begin());a!=_upstreamAddresses.end();++a) {
|
||||
const SharedPtr<Peer> *p = _peers.get(*a);
|
||||
if (p) {
|
||||
bool avoiding = false;
|
||||
for(unsigned int i=0;i<avoidCount;++i) {
|
||||
if (avoid[i] == (*p)->address()) {
|
||||
avoiding = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
const unsigned int q = (*p)->relayQuality(now);
|
||||
if (q <= bestQualityOverall) {
|
||||
bestQualityOverall = q;
|
||||
bestOverall = &(*p);
|
||||
}
|
||||
if ((!avoiding)&&(q <= bestQualityNotAvoid)) {
|
||||
bestQualityNotAvoid = q;
|
||||
bestNotAvoid = &(*p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (bestNotAvoid) {
|
||||
return *bestNotAvoid;
|
||||
} else if ((!strictAvoid)&&(bestOverall)) {
|
||||
return *bestOverall;
|
||||
}
|
||||
|
||||
return SharedPtr<Peer>();
|
||||
}
|
||||
|
||||
bool Topology::isUpstream(const Identity &id) const
|
||||
{
|
||||
Mutex::Lock _l(_upstreams_m);
|
||||
return (std::find(_upstreamAddresses.begin(),_upstreamAddresses.end(),id.address()) != _upstreamAddresses.end());
|
||||
}
|
||||
|
||||
bool Topology::shouldAcceptWorldUpdateFrom(const Address &addr) const
|
||||
{
|
||||
Mutex::Lock _l(_upstreams_m);
|
||||
if (std::find(_upstreamAddresses.begin(),_upstreamAddresses.end(),addr) != _upstreamAddresses.end())
|
||||
return true;
|
||||
for(std::vector< std::pair< uint64_t,Address> >::const_iterator s(_moonSeeds.begin());s!=_moonSeeds.end();++s) {
|
||||
if (s->second == addr)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
ZT_PeerRole Topology::role(const Address &ztaddr) const
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
bool Topology::addWorld(const World &newWorld,bool alwaysAcceptNew)
|
||||
{
|
||||
if ((newWorld.type() != World::TYPE_PLANET)&&(newWorld.type() != World::TYPE_MOON))
|
||||
return false;
|
||||
|
||||
Mutex::Lock _l1(_upstreams_m);
|
||||
Mutex::Lock _l2(_peers_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 {
|
||||
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;
|
||||
}
|
||||
|
||||
char savePath[64];
|
||||
if (existing->type() == World::TYPE_MOON) {
|
||||
Utils::snprintf(savePath,sizeof(savePath),"moons.d/%.16llx.moon",existing->id());
|
||||
} else {
|
||||
Utils::scopy(savePath,sizeof(savePath),"planet");
|
||||
}
|
||||
try {
|
||||
Buffer<ZT_WORLD_MAX_SERIALIZED_LENGTH> dswtmp;
|
||||
existing->serialize(dswtmp,false);
|
||||
RR->node->dataStorePut(savePath,dswtmp.data(),dswtmp.size(),false);
|
||||
} catch ( ... ) {
|
||||
RR->node->dataStoreDelete(savePath);
|
||||
}
|
||||
|
||||
_memoizeUpstreams();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Topology::addMoon(const uint64_t id,const Address &seed)
|
||||
{
|
||||
char savePath[64];
|
||||
Utils::snprintf(savePath,sizeof(savePath),"moons.d/%.16llx.moon",id);
|
||||
|
||||
try {
|
||||
std::string moonBin(RR->node->dataStoreGet(savePath));
|
||||
if (moonBin.length() > 1) {
|
||||
Buffer<ZT_WORLD_MAX_SERIALIZED_LENGTH> wtmp(moonBin.data(),(unsigned int)moonBin.length());
|
||||
World w;
|
||||
w.deserialize(wtmp);
|
||||
if ((w.type() == World::TYPE_MOON)&&(w.id() == id)) {
|
||||
addWorld(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(const uint64_t id)
|
||||
{
|
||||
Mutex::Lock _l1(_upstreams_m);
|
||||
Mutex::Lock _l2(_peers_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 {
|
||||
char savePath[64];
|
||||
Utils::snprintf(savePath,sizeof(savePath),"moons.d/%.16llx.moon",id);
|
||||
RR->node->dataStoreDelete(savePath);
|
||||
}
|
||||
}
|
||||
_moons.swap(nm);
|
||||
|
||||
std::vector< std::pair<uint64_t,Address> > cm;
|
||||
for(std::vector< std::pair<uint64_t,Address> >::const_iterator m(_moonSeeds.begin());m!=_moonSeeds.end();++m) {
|
||||
if (m->first != id)
|
||||
cm.push_back(*m);
|
||||
}
|
||||
_moonSeeds.swap(cm);
|
||||
|
||||
_memoizeUpstreams();
|
||||
}
|
||||
|
||||
void Topology::clean(uint64_t now)
|
||||
{
|
||||
{
|
||||
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()) )
|
||||
_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->reclaimIfWeak())
|
||||
_paths.erase(*k);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
return Identity();
|
||||
}
|
||||
|
||||
void Topology::_memoizeUpstreams()
|
||||
{
|
||||
// assumes _upstreams_m and _peers_m are locked
|
||||
_upstreamAddresses.clear();
|
||||
_amRoot = false;
|
||||
|
||||
for(std::vector<World::Root>::const_iterator i(_planet.roots().begin());i!=_planet.roots().end();++i) {
|
||||
if (i->identity == RR->identity) {
|
||||
_amRoot = 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);
|
||||
saveIdentity(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) {
|
||||
_amRoot = 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);
|
||||
saveIdentity(i->identity);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::sort(_upstreamAddresses.begin(),_upstreamAddresses.end());
|
||||
|
||||
_cor.clear();
|
||||
for(std::vector<Address>::const_iterator a(_upstreamAddresses.begin());a!=_upstreamAddresses.end();++a) {
|
||||
if (!_cor.addRepresentative(*a))
|
||||
break;
|
||||
}
|
||||
_cor.sign(RR->identity,RR->node->now());
|
||||
}
|
||||
|
||||
} // namespace ZeroTier
|
||||
446
zto/node/Topology.hpp
Normal file
446
zto/node/Topology.hpp
Normal file
@@ -0,0 +1,446 @@
|
||||
/*
|
||||
* 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_TOPOLOGY_HPP
|
||||
#define ZT_TOPOLOGY_HPP
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <vector>
|
||||
#include <stdexcept>
|
||||
#include <algorithm>
|
||||
#include <utility>
|
||||
|
||||
#include "Constants.hpp"
|
||||
#include "../include/ZeroTierOne.h"
|
||||
|
||||
#include "Address.hpp"
|
||||
#include "Identity.hpp"
|
||||
#include "Peer.hpp"
|
||||
#include "Path.hpp"
|
||||
#include "Mutex.hpp"
|
||||
#include "InetAddress.hpp"
|
||||
#include "Hashtable.hpp"
|
||||
#include "World.hpp"
|
||||
#include "CertificateOfRepresentation.hpp"
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
class RuntimeEnvironment;
|
||||
|
||||
/**
|
||||
* Database of network topology
|
||||
*/
|
||||
class Topology
|
||||
{
|
||||
public:
|
||||
Topology(const RuntimeEnvironment *renv);
|
||||
|
||||
/**
|
||||
* Add a peer to database
|
||||
*
|
||||
* This will not replace existing peers. In that case the existing peer
|
||||
* record is returned.
|
||||
*
|
||||
* @param peer Peer to add
|
||||
* @return New or existing peer (should replace 'peer')
|
||||
*/
|
||||
SharedPtr<Peer> addPeer(const SharedPtr<Peer> &peer);
|
||||
|
||||
/**
|
||||
* Get a peer from its address
|
||||
*
|
||||
* @param zta ZeroTier address of peer
|
||||
* @return Peer or NULL if not found
|
||||
*/
|
||||
SharedPtr<Peer> getPeer(const Address &zta);
|
||||
|
||||
/**
|
||||
* Get a peer only if it is presently in memory (no disk cache)
|
||||
*
|
||||
* This also does not update the lastUsed() time for peers, which means
|
||||
* that it won't prevent them from falling out of RAM. This is currently
|
||||
* used in the Cluster code to update peer info without forcing all peers
|
||||
* across the entire cluster to remain in memory cache.
|
||||
*
|
||||
* @param zta ZeroTier address
|
||||
*/
|
||||
inline SharedPtr<Peer> getPeerNoCache(const Address &zta)
|
||||
{
|
||||
Mutex::Lock _l(_peers_m);
|
||||
const SharedPtr<Peer> *const ap = _peers.get(zta);
|
||||
if (ap)
|
||||
return *ap;
|
||||
return SharedPtr<Peer>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a Path object for a given local and remote physical address, creating if needed
|
||||
*
|
||||
* @param l Local address or NULL for 'any' or 'wildcard'
|
||||
* @param r Remote address
|
||||
* @return Pointer to canonicalized Path object
|
||||
*/
|
||||
inline SharedPtr<Path> getPath(const InetAddress &l,const InetAddress &r)
|
||||
{
|
||||
Mutex::Lock _l(_paths_m);
|
||||
SharedPtr<Path> &p = _paths[Path::HashKey(l,r)];
|
||||
if (!p)
|
||||
p.setToUnsafe(new Path(l,r));
|
||||
return p;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the identity of a peer
|
||||
*
|
||||
* @param zta ZeroTier address of peer
|
||||
* @return Identity or NULL Identity if not found
|
||||
*/
|
||||
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 best upstream peer
|
||||
*
|
||||
* @return Root server with lowest latency or NULL if none
|
||||
*/
|
||||
inline SharedPtr<Peer> getUpstreamPeer() { return getUpstreamPeer((const Address *)0,0,false); }
|
||||
|
||||
/**
|
||||
* Get the current best upstream peer, avoiding those in the supplied avoid list
|
||||
*
|
||||
* @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> getUpstreamPeer(const Address *avoid,unsigned int avoidCount,bool strictAvoid);
|
||||
|
||||
/**
|
||||
* @param id Identity to check
|
||||
* @return True if this is a root server or a network preferred relay from one of our networks
|
||||
*/
|
||||
bool isUpstream(const Identity &id) const;
|
||||
|
||||
/**
|
||||
* @param addr Address to check
|
||||
* @return True if we should accept a world update from this address
|
||||
*/
|
||||
bool shouldAcceptWorldUpdateFrom(const Address &addr) const;
|
||||
|
||||
/**
|
||||
* @param ztaddr ZeroTier address
|
||||
* @return Peer role for this device
|
||||
*/
|
||||
ZT_PeerRole role(const Address &ztaddr) const;
|
||||
|
||||
/**
|
||||
* Check for prohibited endpoints
|
||||
*
|
||||
* Right now this returns true if the designated ZT address is a root and if
|
||||
* the IP (IP only, not port) does not equal any of the IPs defined in the
|
||||
* current World. This is an extra little security feature in case root keys
|
||||
* get appropriated or something.
|
||||
*
|
||||
* 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(_upstreams_m);
|
||||
for(std::vector<World::Root>::const_iterator i(_planet.roots().begin());i!=_planet.roots().end();++i) {
|
||||
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) {
|
||||
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 Vector of active upstream addresses (including roots)
|
||||
*/
|
||||
inline std::vector<Address> upstreamAddresses() const
|
||||
{
|
||||
Mutex::Lock _l(_upstreams_m);
|
||||
return _upstreamAddresses;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Current moons
|
||||
*/
|
||||
inline std::vector<World> moons() const
|
||||
{
|
||||
Mutex::Lock _l(_upstreams_m);
|
||||
return _moons;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Moon IDs we are waiting for from seeds
|
||||
*/
|
||||
inline std::vector<uint64_t> moonsWanted() const
|
||||
{
|
||||
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 A new or updated planet or moon to learn
|
||||
* @param alwaysAcceptNew If true, always accept new moons even if we're not waiting for one
|
||||
* @return True if it was valid and newer than current (or totally new for moons)
|
||||
*/
|
||||
bool addWorld(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(const uint64_t id,const Address &seed);
|
||||
|
||||
/**
|
||||
* Remove a moon
|
||||
*
|
||||
* @param id Moon's world ID
|
||||
*/
|
||||
void removeMoon(const uint64_t id);
|
||||
|
||||
/**
|
||||
* Clean and flush database
|
||||
*/
|
||||
void clean(uint64_t now);
|
||||
|
||||
/**
|
||||
* @param now Current time
|
||||
* @return Number of peers with active direct paths
|
||||
*/
|
||||
inline unsigned long countActive(uint64_t now) const
|
||||
{
|
||||
unsigned long cnt = 0;
|
||||
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));
|
||||
}
|
||||
return cnt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply a function or function object to all peers
|
||||
*
|
||||
* @param f Function to apply
|
||||
* @tparam F Function or function object type
|
||||
*/
|
||||
template<typename F>
|
||||
inline void eachPeer(F f)
|
||||
{
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return All currently active peers by address (unsorted)
|
||||
*/
|
||||
inline std::vector< std::pair< Address,SharedPtr<Peer> > > allPeers() const
|
||||
{
|
||||
Mutex::Lock _l(_peers_m);
|
||||
return _peers.entries();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return True if I am a root server in a planet or moon
|
||||
*/
|
||||
inline bool amRoot() const { return _amRoot; }
|
||||
|
||||
/**
|
||||
* Get the outbound trusted path ID for a physical address, or 0 if none
|
||||
*
|
||||
* @param physicalAddress Physical address to which we are sending the packet
|
||||
* @return Trusted path ID or 0 if none (0 is not a valid trusted path ID)
|
||||
*/
|
||||
inline uint64_t getOutboundPathTrust(const InetAddress &physicalAddress)
|
||||
{
|
||||
for(unsigned int i=0;i<_trustedPathCount;++i) {
|
||||
if (_trustedPathNetworks[i].containsAddress(physicalAddress))
|
||||
return _trustedPathIds[i];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether in incoming trusted path marked packet is valid
|
||||
*
|
||||
* @param physicalAddress Originating physical address
|
||||
* @param trustedPathId Trusted path ID from packet (from MAC field)
|
||||
*/
|
||||
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)))
|
||||
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)
|
||||
*/
|
||||
inline void setTrustedPaths(const InetAddress *networks,const uint64_t *ids,unsigned int count)
|
||||
{
|
||||
if (count > ZT_MAX_TRUSTED_PATHS)
|
||||
count = ZT_MAX_TRUSTED_PATHS;
|
||||
Mutex::Lock _l(_trustedPaths_m);
|
||||
for(unsigned int i=0;i<count;++i) {
|
||||
_trustedPathIds[i] = ids[i];
|
||||
_trustedPathNetworks[i] = networks[i];
|
||||
}
|
||||
_trustedPathCount = count;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Current certificate of representation (copy)
|
||||
*/
|
||||
inline CertificateOfRepresentation certificateOfRepresentation() const
|
||||
{
|
||||
Mutex::Lock _l(_upstreams_m);
|
||||
return _cor;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param buf Buffer to receive COR
|
||||
*/
|
||||
template<unsigned int C>
|
||||
void appendCertificateOfRepresentation(Buffer<C> &buf)
|
||||
{
|
||||
Mutex::Lock _l(_upstreams_m);
|
||||
_cor.serialize(buf);
|
||||
}
|
||||
|
||||
private:
|
||||
Identity _getIdentity(const Address &zta);
|
||||
void _memoizeUpstreams();
|
||||
|
||||
const RuntimeEnvironment *const RR;
|
||||
|
||||
uint64_t _trustedPathIds[ZT_MAX_TRUSTED_PATHS];
|
||||
InetAddress _trustedPathNetworks[ZT_MAX_TRUSTED_PATHS];
|
||||
unsigned int _trustedPathCount;
|
||||
Mutex _trustedPaths_m;
|
||||
|
||||
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;
|
||||
CertificateOfRepresentation _cor;
|
||||
bool _amRoot;
|
||||
Mutex _upstreams_m; // locks worlds, upstream info, moon info, etc.
|
||||
};
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
||||
#endif
|
||||
255
zto/node/Utils.cpp
Normal file
255
zto/node/Utils.cpp
Normal file
@@ -0,0 +1,255 @@
|
||||
/*
|
||||
* 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 <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <time.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "Constants.hpp"
|
||||
|
||||
#ifdef __UNIX_LIKE__
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/uio.h>
|
||||
#include <dirent.h>
|
||||
#endif
|
||||
|
||||
#ifdef __WINDOWS__
|
||||
#include <wincrypt.h>
|
||||
#endif
|
||||
|
||||
#include "Utils.hpp"
|
||||
#include "Mutex.hpp"
|
||||
#include "Salsa20.hpp"
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
const char Utils::HEXCHARS[16] = { '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f' };
|
||||
|
||||
// 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)
|
||||
{
|
||||
volatile uint8_t *const end = ptr + len;
|
||||
while (ptr != end) *(ptr++) = (uint8_t)0;
|
||||
}
|
||||
static void (*volatile _Utils_doBurn_ptr)(volatile uint8_t *,unsigned int) = _Utils_doBurn;
|
||||
void Utils::burn(void *ptr,unsigned int len) { (_Utils_doBurn_ptr)((volatile uint8_t *)ptr,len); }
|
||||
|
||||
std::string Utils::hex(const void *data,unsigned int len)
|
||||
{
|
||||
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)
|
||||
return 0;
|
||||
|
||||
while ((c = (unsigned char)*(hex++))) {
|
||||
if ((c >= 48)&&(c <= 57)) { // 0..9
|
||||
if ((n ^= 1)) {
|
||||
if (l >= len) break;
|
||||
((unsigned char *)buf)[l++] = (b | (c - 48));
|
||||
} else b = (c - 48) << 4;
|
||||
} else if ((c >= 65)&&(c <= 70)) { // A..F
|
||||
if ((n ^= 1)) {
|
||||
if (l >= len) break;
|
||||
((unsigned char *)buf)[l++] = (b | (c - (65 - 10)));
|
||||
} else b = (c - (65 - 10)) << 4;
|
||||
} else if ((c >= 97)&&(c <= 102)) { // a..f
|
||||
if ((n ^= 1)) {
|
||||
if (l >= len) break;
|
||||
((unsigned char *)buf)[l++] = (b | (c - (97 - 10)));
|
||||
} else b = (c - (97 - 10)) << 4;
|
||||
}
|
||||
if (hex == eof)
|
||||
break;
|
||||
}
|
||||
|
||||
return l;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
/* Just for posterity we Salsa20 encrypt the result of whatever system
|
||||
* CSPRNG we use. There have been several bugs at the OS or OS distribution
|
||||
* level in the past that resulted in systematically weak or predictable
|
||||
* keys due to random seeding problems. This mitigates that by grabbing
|
||||
* a bit of extra entropy and further randomizing the result, and comes
|
||||
* at almost no cost and with no real downside if the random source is
|
||||
* good. */
|
||||
if (!s20Initialized) {
|
||||
s20Initialized = true;
|
||||
uint64_t s20Key[4];
|
||||
s20Key[0] = (uint64_t)time(0); // system clock
|
||||
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);
|
||||
}
|
||||
|
||||
#ifdef __WINDOWS__
|
||||
|
||||
static HCRYPTPROV cryptProvider = NULL;
|
||||
|
||||
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));
|
||||
}
|
||||
((uint8_t *)buf)[i] = randomBuf[randomPtr++];
|
||||
}
|
||||
|
||||
#else // not __WINDOWS__
|
||||
|
||||
static int devURandomFd = -1;
|
||||
|
||||
if (devURandomFd < 0) {
|
||||
devURandomFd = ::open("/dev/urandom",O_RDONLY);
|
||||
if (devURandomFd < 0) {
|
||||
fprintf(stderr,"FATAL ERROR: Utils::getSecureRandom() unable to open /dev/urandom\n");
|
||||
exit(1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
for(unsigned int i=0;i<bytes;++i) {
|
||||
if (randomPtr >= sizeof(randomBuf)) {
|
||||
for(;;) {
|
||||
if ((int)::read(devURandomFd,randomBuf,sizeof(randomBuf)) != (int)sizeof(randomBuf)) {
|
||||
::close(devURandomFd);
|
||||
devURandomFd = ::open("/dev/urandom",O_RDONLY);
|
||||
if (devURandomFd < 0) {
|
||||
fprintf(stderr,"FATAL ERROR: Utils::getSecureRandom() unable to open /dev/urandom\n");
|
||||
exit(1);
|
||||
return;
|
||||
}
|
||||
} else break;
|
||||
}
|
||||
randomPtr = 0;
|
||||
s20.crypt12(randomBuf,randomBuf,sizeof(randomBuf));
|
||||
}
|
||||
((uint8_t *)buf)[i] = randomBuf[randomPtr++];
|
||||
}
|
||||
|
||||
#endif // __WINDOWS__ or not
|
||||
}
|
||||
|
||||
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
|
||||
386
zto/node/Utils.hpp
Normal file
386
zto/node/Utils.hpp
Normal file
@@ -0,0 +1,386 @@
|
||||
/*
|
||||
* 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_UTILS_HPP
|
||||
#define ZT_UTILS_HPP
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#include <string>
|
||||
#include <stdexcept>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
#include "Constants.hpp"
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
/**
|
||||
* Miscellaneous utility functions and global constants
|
||||
*/
|
||||
class Utils
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Perform a time-invariant binary comparison
|
||||
*
|
||||
* @param a First binary string
|
||||
* @param b Second binary string
|
||||
* @param len Length of strings
|
||||
* @return True if strings are equal
|
||||
*/
|
||||
static inline bool secureEq(const void *a,const void *b,unsigned int len)
|
||||
{
|
||||
uint8_t diff = 0;
|
||||
for(unsigned int i=0;i<len;++i)
|
||||
diff |= ( (reinterpret_cast<const uint8_t *>(a))[i] ^ (reinterpret_cast<const uint8_t *>(b))[i] );
|
||||
return (diff == 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Securely zero memory, avoiding compiler optimizations and such
|
||||
*/
|
||||
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
|
||||
*/
|
||||
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()); }
|
||||
|
||||
/**
|
||||
* 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()); }
|
||||
|
||||
/**
|
||||
* 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); }
|
||||
|
||||
/**
|
||||
* Generate secure random bytes
|
||||
*
|
||||
* This will try to use whatever OS sources of entropy are available. It's
|
||||
* guarded by an internal mutex so it's thread-safe.
|
||||
*
|
||||
* @param buf Buffer to fill
|
||||
* @param bytes Number of random bytes to generate
|
||||
*/
|
||||
static void getSecureRandom(void *buf,unsigned int bytes);
|
||||
|
||||
/**
|
||||
* Tokenize a string (alias for strtok_r or strtok_s depending on platform)
|
||||
*
|
||||
* @param str String to split
|
||||
* @param delim Delimiters
|
||||
* @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);
|
||||
#else
|
||||
return strtok_r(str,delim,saveptr);
|
||||
#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 long long strToU64(const char *s)
|
||||
throw()
|
||||
{
|
||||
#ifdef __WINDOWS__
|
||||
return (unsigned long long)_strtoui64(s,(char **)0,10);
|
||||
#else
|
||||
return strtoull(s,(char **)0,10);
|
||||
#endif
|
||||
}
|
||||
static inline long long strTo64(const char *s)
|
||||
throw()
|
||||
{
|
||||
#ifdef __WINDOWS__
|
||||
return (long long)_strtoi64(s,(char **)0,10);
|
||||
#else
|
||||
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 long long hexStrToU64(const char *s)
|
||||
throw()
|
||||
{
|
||||
#ifdef __WINDOWS__
|
||||
return (unsigned long long)_strtoui64(s,(char **)0,16);
|
||||
#else
|
||||
return strtoull(s,(char **)0,16);
|
||||
#endif
|
||||
}
|
||||
static inline long long hexStrTo64(const char *s)
|
||||
throw()
|
||||
{
|
||||
#ifdef __WINDOWS__
|
||||
return (long long)_strtoi64(s,(char **)0,16);
|
||||
#else
|
||||
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
|
||||
*
|
||||
* This will never ever EVER result in dest[] not being null-terminated
|
||||
* regardless of any input parameter (other than len==0 which is invalid).
|
||||
*
|
||||
* @param dest Destination buffer (must not be NULL)
|
||||
* @param len Length of dest[] (if zero, false is returned and nothing happens)
|
||||
* @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);
|
||||
|
||||
/**
|
||||
* Count the number of bits set in an integer
|
||||
*
|
||||
* @param v 32-bit integer
|
||||
* @return Number of bits set in this integer (0-32)
|
||||
*/
|
||||
static inline uint32_t countBits(uint32_t v)
|
||||
{
|
||||
v = v - ((v >> 1) & (uint32_t)0x55555555);
|
||||
v = (v & (uint32_t)0x33333333) + ((v >> 2) & (uint32_t)0x33333333);
|
||||
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
|
||||
*
|
||||
* @param p Memory to scan
|
||||
* @param len Length of memory
|
||||
* @return True if memory is all zero
|
||||
*/
|
||||
static inline bool isZero(const void *p,unsigned int len)
|
||||
{
|
||||
for(unsigned int i=0;i<len;++i) {
|
||||
if (((const unsigned char *)p)[i])
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// 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 uint64_t hton(uint64_t n)
|
||||
throw()
|
||||
{
|
||||
#if __BYTE_ORDER == __LITTLE_ENDIAN
|
||||
#if defined(__GNUC__) && (!defined(__OpenBSD__))
|
||||
return __builtin_bswap64(n);
|
||||
#else
|
||||
return (
|
||||
((n & 0x00000000000000FFULL) << 56) |
|
||||
((n & 0x000000000000FF00ULL) << 40) |
|
||||
((n & 0x0000000000FF0000ULL) << 24) |
|
||||
((n & 0x00000000FF000000ULL) << 8) |
|
||||
((n & 0x000000FF00000000ULL) >> 8) |
|
||||
((n & 0x0000FF0000000000ULL) >> 24) |
|
||||
((n & 0x00FF000000000000ULL) >> 40) |
|
||||
((n & 0xFF00000000000000ULL) >> 56)
|
||||
);
|
||||
#endif
|
||||
#else
|
||||
return n;
|
||||
#endif
|
||||
}
|
||||
static inline int64_t hton(int64_t n) throw() { return (int64_t)hton((uint64_t)n); }
|
||||
|
||||
static inline uint8_t ntoh(uint8_t n) throw() { return n; }
|
||||
static inline int8_t ntoh(int8_t n) throw() { return n; }
|
||||
static inline uint16_t ntoh(uint16_t n) throw() { return ntohs(n); }
|
||||
static inline int16_t ntoh(int16_t n) throw() { return (int16_t)ntohs((uint16_t)n); }
|
||||
static inline uint32_t ntoh(uint32_t n) throw() { return ntohl(n); }
|
||||
static inline int32_t ntoh(int32_t n) throw() { return (int32_t)ntohl((uint32_t)n); }
|
||||
static inline uint64_t ntoh(uint64_t n)
|
||||
throw()
|
||||
{
|
||||
#if __BYTE_ORDER == __LITTLE_ENDIAN
|
||||
#if defined(__GNUC__) && !defined(__OpenBSD__)
|
||||
return __builtin_bswap64(n);
|
||||
#else
|
||||
return (
|
||||
((n & 0x00000000000000FFULL) << 56) |
|
||||
((n & 0x000000000000FF00ULL) << 40) |
|
||||
((n & 0x0000000000FF0000ULL) << 24) |
|
||||
((n & 0x00000000FF000000ULL) << 8) |
|
||||
((n & 0x000000FF00000000ULL) >> 8) |
|
||||
((n & 0x0000FF0000000000ULL) >> 24) |
|
||||
((n & 0x00FF000000000000ULL) >> 40) |
|
||||
((n & 0xFF00000000000000ULL) >> 56)
|
||||
);
|
||||
#endif
|
||||
#else
|
||||
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 b1,unsigned int maj2,unsigned int min2,unsigned int rev2,unsigned int b2)
|
||||
{
|
||||
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 {
|
||||
if (b1 > b2)
|
||||
return 1;
|
||||
else if (b1 < b2)
|
||||
return -1;
|
||||
else return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Hexadecimal characters 0-f
|
||||
*/
|
||||
static const char HEXCHARS[16];
|
||||
};
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
||||
#endif
|
||||
278
zto/node/World.hpp
Normal file
278
zto/node/World.hpp
Normal file
@@ -0,0 +1,278 @@
|
||||
/*
|
||||
* 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_WORLD_HPP
|
||||
#define ZT_WORLD_HPP
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
#include "Constants.hpp"
|
||||
#include "InetAddress.hpp"
|
||||
#include "Identity.hpp"
|
||||
#include "Buffer.hpp"
|
||||
#include "C25519.hpp"
|
||||
|
||||
/**
|
||||
* Maximum number of roots (sanity limit, okay to increase)
|
||||
*
|
||||
* A given root can (through multi-homing) be distributed across any number of
|
||||
* physical endpoints, but having more than one is good to permit total failure
|
||||
* of one root or its withdrawal due to compromise without taking the whole net
|
||||
* down.
|
||||
*/
|
||||
#define ZT_WORLD_MAX_ROOTS 4
|
||||
|
||||
/**
|
||||
* Maximum number of stable endpoints per root (sanity limit, okay to increase)
|
||||
*/
|
||||
#define ZT_WORLD_MAX_STABLE_ENDPOINTS_PER_ROOT 32
|
||||
|
||||
/**
|
||||
* The (more than) maximum length of a serialized World
|
||||
*/
|
||||
#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 for Earth
|
||||
*
|
||||
* This is the ID for the ZeroTier World used on planet Earth. It is unrelated
|
||||
* to the public network 8056c2e21c000001 of the same name. It was chosen
|
||||
* from Earth's approximate distance from the sun in kilometers.
|
||||
*/
|
||||
#define ZT_WORLD_ID_EARTH 149604618
|
||||
|
||||
/**
|
||||
* World ID for Mars -- for future use by SpaceX or others
|
||||
*/
|
||||
#define ZT_WORLD_ID_MARS 227883110
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
/**
|
||||
* A world definition (formerly known as a root topology)
|
||||
*
|
||||
* Think of a World as a single data center. Within this data center a set
|
||||
* of distributed fault tolerant root servers provide stable anchor points
|
||||
* for a peer to peer network that provides VLAN service. Updates to a world
|
||||
* definition can be published by signing them with the previous revision's
|
||||
* signing key, and should be very infrequent.
|
||||
*
|
||||
* The maximum data center size is approximately 2.5 cubic light seconds,
|
||||
* since many protocols have issues with >5s RTT latencies.
|
||||
*
|
||||
* ZeroTier operates a World for Earth capable of encompassing the planet, its
|
||||
* 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.
|
||||
*/
|
||||
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
|
||||
};
|
||||
|
||||
/**
|
||||
* Construct an empty / null World
|
||||
*/
|
||||
World() :
|
||||
_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 { return _roots; }
|
||||
|
||||
/**
|
||||
* @return World type: planet or moon
|
||||
*/
|
||||
inline Type type() const { return _type; }
|
||||
|
||||
/**
|
||||
* @return World unique identifier
|
||||
*/
|
||||
inline uint64_t id() const { return _id; }
|
||||
|
||||
/**
|
||||
* @return World definition timestamp
|
||||
*/
|
||||
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
|
||||
*
|
||||
* @param update Candidate update
|
||||
* @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)
|
||||
{
|
||||
if ((_id == 0)||(_type == TYPE_NULL))
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return True if this World is non-empty
|
||||
*/
|
||||
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)_type);
|
||||
b.append((uint64_t)_id);
|
||||
b.append((uint64_t)_ts);
|
||||
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());
|
||||
for(std::vector<Root>::const_iterator r(_roots.begin());r!=_roots.end();++r) {
|
||||
r->identity.serialize(b);
|
||||
b.append((uint8_t)r->stableEndpoints.size());
|
||||
for(std::vector<InetAddress>::const_iterator ep(r->stableEndpoints.begin());ep!=r->stableEndpoints.end();++ep)
|
||||
ep->serialize(b);
|
||||
}
|
||||
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>
|
||||
inline unsigned int deserialize(const Buffer<C> &b,unsigned int startAt = 0)
|
||||
{
|
||||
unsigned int p = startAt;
|
||||
|
||||
_roots.clear();
|
||||
|
||||
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 std::invalid_argument("invalid world type");
|
||||
}
|
||||
|
||||
_id = b.template at<uint64_t>(p); p += 8;
|
||||
_ts = b.template at<uint64_t>(p); p += 8;
|
||||
memcpy(_updatesMustBeSignedBy.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;
|
||||
const unsigned int numRoots = (unsigned int)b[p++];
|
||||
if (numRoots > ZT_WORLD_MAX_ROOTS)
|
||||
throw std::invalid_argument("too many roots in World");
|
||||
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");
|
||||
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 { return ((_id == w._id)&&(_ts == w._ts)&&(_updatesMustBeSignedBy == w._updatesMustBeSignedBy)&&(_signature == w._signature)&&(_roots == w._roots)&&(_type == w._type)); }
|
||||
inline bool operator!=(const World &w) const { return (!(*this == w)); }
|
||||
|
||||
inline bool operator<(const World &w) const { return (((int)_type < (int)w._type) ? true : ((_type == w._type) ? (_id < w._id) : false)); }
|
||||
|
||||
/**
|
||||
* 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;
|
||||
Type _type;
|
||||
C25519::Public _updatesMustBeSignedBy;
|
||||
C25519::Signature _signature;
|
||||
std::vector<Root> _roots;
|
||||
};
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user