A bunch of wiring up of stuff...

This commit is contained in:
Adam Ierymenko 2020-01-17 14:01:22 -08:00
parent b53b7f4950
commit 3ff9ffd5d4
No known key found for this signature in database
GPG key ID: C8877CF2D7A5D7F3
32 changed files with 1401 additions and 695 deletions

View file

@ -14,7 +14,7 @@ debug:
mkdir -p ${BUILDDIR} && cd ${BUILDDIR} && cmake .. -DCMAKE_BUILD_TYPE=Debug && $(MAKE)
clean:
rm -rf ${BUILDDIR} cmake-build-*
rm -rf ${BUILDDIR}
distclean:
rm -rf ${BUILDDIR}

178
attic/TinyVector.hpp Normal file
View file

@ -0,0 +1,178 @@
/*
* Copyright (c)2019 ZeroTier, Inc.
*
* Use of this software is governed by the Business Source License included
* in the LICENSE.TXT file in the project's root directory.
*
* Change Date: 2023-01-01
*
* On the date above, in accordance with the Business Source License, use
* of this software will be governed by version 2.0 of the Apache License.
*/
/****/
#ifndef ZT_TINYVECTOR_HPP
#define ZT_TINYVECTOR_HPP
#include "Constants.hpp"
#include "Utils.hpp"
#include <utility>
#include <stdexcept>
#include <algorithm>
namespace ZeroTier {
/**
* Tiny vector with a static base capacity for allocation-free operation at small sizes
*
* This doesn't support all of std::vector, uses low-level memcpy to relocate things, and
* lacks bounds checking. It's only intended for uses where a minimal subset of the vector
* container is needed, the objects are primitive or safe to handle in this way, and the
* number of items is typically less than or equal to some statically definable value.
*
* Examples of safe objects for this include primitive types, Str, SharedPtr, InetAddress,
* Address, MAC, etc.
*
* @tparam T Type to encapsulate
* @tparam BASE Base number of items to allocate storage inside the object itself (default: 4)
*/
template<typename T,unsigned long BASE = 4>
class TinyVector
{
public:
typedef unsigned long size_t;
typedef T * iterator;
typedef const T * const_iterator;
typedef T & reference;
typedef const T & const_reference;
ZT_ALWAYS_INLINE TinyVector() :
_v((void *)_baseMem),
_c(BASE),
_l(0)
{
}
ZT_ALWAYS_INLINE TinyVector(const TinyVector &vec) :
_v((void *)_baseMem),
_c(BASE),
_l(0)
{
*this = vec;
}
ZT_ALWAYS_INLINE ~TinyVector()
{
clear();
if (_v != (void *)_baseMem)
free(_v);
}
ZT_ALWAYS_INLINE TinyVector &operator=(const TinyVector &vec)
{
unsigned long i = 0;
if (_l < vec._l) {
while (i < _l) {
reinterpret_cast<T *>(_v)[i] = reinterpret_cast<const T *>(vec._v)[i];
++i;
}
if (vec._l > _c) {
unsigned long nc = vec._c;
void *nv;
if (_v == (void *)_baseMem) {
nv = malloc(nc);
memcpy(nv,_v,sizeof(T) * _l);
} else {
nv = realloc(_v,nc);
if (!nv)
throw std::bad_alloc();
}
_v = nv;
_c = nc;
}
while (i < vec._l) {
new (reinterpret_cast<T *>(_v) + i) T(reinterpret_cast<const T *>(vec._v)[i]);
++i;
}
} else {
while (i < vec._l) {
reinterpret_cast<T *>(_v)[i] = reinterpret_cast<const T *>(vec._v)[i];
++i;
}
if (!Utils::isPrimitiveType<T>()) {
while (i < _l)
reinterpret_cast<T *>(_v)[i++]->~T();
}
}
_l = vec._l;
}
ZT_ALWAYS_INLINE void clear()
{
if (!Utils::isPrimitiveType<T>()) {
for (unsigned long i = 0; i < _l; ++i)
reinterpret_cast<T *>(_v)[i]->~T();
}
_l = 0;
}
ZT_ALWAYS_INLINE void push_back(const T &v)
{
if (_l >= _c) {
unsigned long nc = _c << 1U;
void *nv;
if (_v == (void *)_baseMem) {
nv = malloc(sizeof(T) * nc);
memcpy(nv,_v,sizeof(T) * _l);
} else {
nv = realloc(_v,sizeof(T) * nc);
if (!nv)
throw std::bad_alloc();
}
_v = nv;
_c = nc;
}
new (reinterpret_cast<T *>(_v) + _l++) T(v);
}
ZT_ALWAYS_INLINE void pop_back()
{
if (!Utils::isPrimitiveType<T>())
reinterpret_cast<T *>(_v)[_l]->~T();
--_l;
}
ZT_ALWAYS_INLINE reference front() { reinterpret_cast<T *>(_v)[0]; }
ZT_ALWAYS_INLINE const_reference front() const { reinterpret_cast<T *>(_v)[0]; }
ZT_ALWAYS_INLINE reference back() { reinterpret_cast<T *>(_v)[_l - 1]; }
ZT_ALWAYS_INLINE const_reference back() const { reinterpret_cast<T *>(_v)[_l - 1]; }
ZT_ALWAYS_INLINE unsigned long size() const { return _l; }
ZT_ALWAYS_INLINE bool empty() const { return (_l == 0); }
ZT_ALWAYS_INLINE iterator begin() { return reinterpret_cast<T *>(_v); }
ZT_ALWAYS_INLINE iterator end() { return (reinterpret_cast<T *>(_v) + _l); }
ZT_ALWAYS_INLINE const_iterator begin() const { return reinterpret_cast<T *>(_v); }
ZT_ALWAYS_INLINE const_iterator end() const { return (reinterpret_cast<T *>(_v) + _l); }
ZT_ALWAYS_INLINE T *data() { return reinterpret_cast<T *>(_v); }
ZT_ALWAYS_INLINE const T *data() const { return reinterpret_cast<T *>(_v); }
ZT_ALWAYS_INLINE reference operator[](const unsigned long i) { return reinterpret_cast<T *>(_v)[i]; }
ZT_ALWAYS_INLINE const_reference operator[](const unsigned long i) const { return reinterpret_cast<T *>(_v)[i]; }
ZT_ALWAYS_INLINE reference at(const unsigned long i) { return reinterpret_cast<T *>(_v)[i]; }
ZT_ALWAYS_INLINE const_reference at(const unsigned long i) const { return reinterpret_cast<T *>(_v)[i]; }
private:
uint8_t _baseMem[BASE * sizeof(T)];
void *_v;
unsigned long _c;
unsigned long _l;
};
} // namespace ZeroTier
#endif

View file

@ -30,47 +30,45 @@ func Help() {
Usage: zerotier [-options] <command> [command args]
Global Options:
-j Output raw JSON where applicable
-p <path> Use alternate base path
-t <path> Use secret auth token from this file
-j Output raw JSON where applicable
-p <path> Use alternate base path
-t <path> Use secret auth token from this file
Commands:
help Show this help
version Print version
selftest Run internal tests
service [mode] Start as service (default mode: node)
node Start in normal node mode (default)
root [options] Start in root server mode (see docs)
status Show ZeroTier status and config
peers Show VL1 peers
roots Show configured VL1 root servers
addroot <url|identity> [ip/port] [...] Add VL1 root server
removeroot <identity|address> Remove VL1 root server
identity <command> [args] Identity management commands
new [c25519|p384] Create identity (including secret)
getpublic <identity> Extract only public part of identity
validate <identity> Locally validate an identity
sign <identity> <file> Sign a file with an identity's key
verify <identity> <file> <sig> Verify a signature
networks List joined VL2 virtual networks
network <network ID> Show verbose network info
join <network ID> Join a virtual network
leave <network ID> Leave a virtual network
set <network ID> <option> <value> Set a network local config option
manageips <boolean> Is IP management allowed?
manageroutes <boolean> Is route management allowed?
globalips <boolean> Allow assignment of global IPs?
globalroutes <boolean> Can global IP space routes be set?
defaultroute <boolean> Can default route be overridden?
set <local config option> <value> Set a local configuration option
phy <IP/bits> blacklist <boolean> Set or clear blacklist for CIDR
phy <IP/bits> trust <path ID/0> Set or clear trusted path ID for CIDR
virt <address> try <IP/port> [...] Set explicit IPs for reaching a peer
port <port> Set primary local port for VL1 P2P
secondaryport <port/0> Set or disable secondary VL1 P2P port
tertiaryport <port/0> Set or disable tertiary VL1 P2P port
portsearch <boolean> Set or disable port search on startup
portmapping <boolean> Set or disable use of uPnP/NAT-PMP
help Show this help
version Print version
selftest Run internal tests
service Start as service
status Show ZeroTier status and config
peers Show VL1 peers
roots Show configured VL1 root servers
addroot <identity> [IP/port] Add VL1 root
removeroot <identity|address> Remove VL1 root server
identity <command> [args] Identity management commands
new [c25519|p384] Create identity (including secret)
getpublic <identity> Extract only public part of identity
validate <identity> Locally validate an identity
sign <identity> <file> Sign a file with an identity's key
verify <identity> <file> <sig> Verify a signature
networks List joined VL2 virtual networks
network <network ID> Show verbose network info
join <network ID> Join a virtual network
leave <network ID> Leave a virtual network
set <network ID> <option> <value> Set a network local config option
manageips <boolean> Is IP management allowed?
manageroutes <boolean> Is route management allowed?
globalips <boolean> Allow assignment of global IPs?
globalroutes <boolean> Can global IP space routes be set?
defaultroute <boolean> Can default route be overridden?
set <local config option> <value> Set a local configuration option
phy <IP/bits> blacklist <boolean> Set or clear blacklist for CIDR
phy <IP/bits> trust <path ID/0> Set or clear trusted path ID for CIDR
virt <address> try <IP/port> [...] Set explicit IPs for reaching a peer
port <port> Set primary local port for VL1 P2P
secondaryport <port/0> Set or disable secondary VL1 P2P port
tertiaryport <port/0> Set or disable tertiary VL1 P2P port
portsearch <boolean> Set or disable port search on startup
portmapping <boolean> Set or disable use of uPnP/NAT-PMP
Most commands require a secret token to permit control of a running ZeroTier
service. The CLI will automatically try to read this token from the

View file

@ -16,8 +16,14 @@ const (
EndpointTypeUnrecognized = 255
)
// Endpoint wraps a variety of different ways of describing a node's physical network location.
type Endpoint struct {
// Type is this endpoint's type
Type int
// Location is the X, Y, Z coordinate of this endpoint or 0,0,0 if unspecified.
Location [3]int
value, value2 interface{}
}
@ -26,90 +32,114 @@ var (
)
func (ep *Endpoint) unmarshalZT(b []byte) (int, error) {
if len(b) == 0 {
if len(b) < 7 {
return 0, ErrInvalidEndpoint
}
switch b[0] {
ep.Type = int(b[0])
ep.Location[0] = int(binary.BigEndian.Uint16(b[1:3]))
ep.Location[1] = int(binary.BigEndian.Uint16(b[3:5]))
ep.Location[2] = int(binary.BigEndian.Uint16(b[5:7]))
ep.value = nil
ep.value2 = nil
switch ep.Type {
case EndpointTypeNil:
*ep = Endpoint{Type: EndpointTypeNil}
return 1, nil
return 7, nil
case EndpointTypeInetAddr:
ina := new(InetAddress)
inlen, err := ina.unmarshalZT(b[1:])
inlen, err := ina.unmarshalZT(b[7:])
if err != nil {
return 0, err
}
*ep = Endpoint{
Type: EndpointTypeInetAddr,
value: ina,
}
return 1 + inlen, nil
ep.value = ina
return 7 + inlen, nil
case EndpointTypeDnsName:
zeroAt := 1
for i := 1; i < len(b); i++ {
stringEnd := 0
for i := 7; i < len(b); i++ {
if b[i] == 0 {
zeroAt = i
stringEnd = i + 1
break
}
}
if zeroAt == 1 || (1 + zeroAt + 3) > len(b) {
if stringEnd == 0 || (stringEnd + 2) > len(b) {
return 0, ErrInvalidEndpoint
}
port := binary.BigEndian.Uint16(b[zeroAt+1:zeroAt+3])
*ep = Endpoint{
Type: EndpointTypeDnsName,
value: string(b[1:zeroAt]),
value2: &port,
}
return zeroAt + 3, nil
ep.value = string(b[7:stringEnd])
port := binary.BigEndian.Uint16(b[stringEnd:stringEnd+2])
ep.value2 = &port
return stringEnd + 2, nil
case EndpointTypeZeroTier:
if len(b) != 54 {
if len(b) < 60 {
return 0, ErrInvalidEndpoint
}
a, err := NewAddressFromBytes(b[1:6])
a, err := NewAddressFromBytes(b[7:12])
if err != nil {
return 0, err
}
*ep = Endpoint{
Type: EndpointTypeZeroTier,
value: a,
value2: append(make([]byte, 0, 48), b[6:54]...),
}
return 54, nil
ep.value = a
ep.value2 = append(make([]byte,0,48),b[12:60]...)
return 60, nil
case EndpointTypeUrl:
zeroAt := 1
for i := 1; i < len(b); i++ {
stringEnd := 0
for i := 7; i < len(b); i++ {
if b[i] == 0 {
zeroAt = i
stringEnd = i + 1
break
}
}
if zeroAt == 1 {
if stringEnd == 0 {
return 0, ErrInvalidEndpoint
}
*ep = Endpoint{
Type: EndpointTypeUrl,
value: string(b[1:zeroAt]),
}
return zeroAt + 2, nil
ep.value = string(b[7:stringEnd])
return stringEnd, nil
case EndpointTypeEthernet:
if len(b) != 7 {
if len(b) < 13 {
return 0, ErrInvalidEndpoint
}
m, err := NewMACFromBytes(b[1:7])
m, err := NewMACFromBytes(b[7:13])
if err != nil {
return 0, err
}
*ep = Endpoint{
Type: EndpointTypeEthernet,
value: m,
}
return 7, nil
ep.value = m
return 13, nil
default:
if len(b) < 2 {
if len(b) < 8 {
return 0, ErrInvalidEndpoint
}
*ep = Endpoint{Type: EndpointTypeUnrecognized}
return 1 + int(b[1]), nil
ep.Type = EndpointTypeUnrecognized
return 8 + int(b[1]), nil
}
}
// InetAddress gets the address associated with this endpoint or nil if it is not of this type.
func (ep *Endpoint) InetAddress() *InetAddress {
v, _ := ep.value.(*InetAddress)
return v
}
// Address gets the address associated with this endpoint or nil if it is not of this type.
func (ep *Endpoint) Address() *Address {
v, _ := ep.value.(*Address)
return v
}
// DNSName gets the DNS name and port associated with this endpoint or an empty string and -1 if it is not of this type.
func (ep *Endpoint) DNSName() (string, int) {
if ep.Type == EndpointTypeDnsName {
return ep.value.(string), int(*(ep.value2.(*uint16)))
}
return "", -1
}
// InetAddress gets the URL assocaited with this endpoint or an empty string if it is not of this type.
func (ep *Endpoint) URL() string {
if ep.Type == EndpointTypeUrl {
return ep.value.(string)
}
return ""
}
// Ethernet gets the address associated with this endpoint or nil if it is not of this type.
func (ep *Endpoint) Ethernet() *MAC {
v, _ := ep.value.(*MAC)
return v
}

View file

@ -29,8 +29,6 @@ const (
ErrTapInitFailed Err = "unable to create native Tap instance"
ErrUnrecognizedIdentityType Err = "unrecognized identity type"
ErrInvalidKey Err = "invalid key data"
ErrInvalidSignature Err = "invalid signature"
ErrSecretKeyRequired Err = "secret key required"
)
// APIErr is returned by the JSON API when a call fails

View file

@ -24,20 +24,21 @@ import (
"net"
"strconv"
"strings"
"syscall"
"unsafe"
)
func sockaddrStorageToIPNet(ss *C.struct_sockaddr_storage) *net.IPNet {
var a net.IPNet
switch ss.ss_family {
case AFInet:
case syscall.AF_INET:
sa4 := (*C.struct_sockaddr_in)(unsafe.Pointer(ss))
var ip4 [4]byte
copy(ip4[:], (*[4]byte)(unsafe.Pointer(&sa4.sin_addr))[:])
a.IP = ip4[:]
a.Mask = net.CIDRMask(int(binary.BigEndian.Uint16(((*[2]byte)(unsafe.Pointer(&sa4.sin_port)))[:])), 32)
return &a
case AFInet6:
case syscall.AF_INET6:
sa6 := (*C.struct_sockaddr_in6)(unsafe.Pointer(ss))
var ip6 [16]byte
copy(ip6[:], (*[16]byte)(unsafe.Pointer(&sa6.sin6_addr))[:])
@ -51,14 +52,14 @@ func sockaddrStorageToIPNet(ss *C.struct_sockaddr_storage) *net.IPNet {
func sockaddrStorageToUDPAddr(ss *C.struct_sockaddr_storage) *net.UDPAddr {
var a net.UDPAddr
switch ss.ss_family {
case AFInet:
case syscall.AF_INET:
sa4 := (*C.struct_sockaddr_in)(unsafe.Pointer(ss))
var ip4 [4]byte
copy(ip4[:], (*[4]byte)(unsafe.Pointer(&sa4.sin_addr))[:])
a.IP = ip4[:]
a.Port = int(binary.BigEndian.Uint16(((*[2]byte)(unsafe.Pointer(&sa4.sin_port)))[:]))
return &a
case AFInet6:
case syscall.AF_INET6:
sa6 := (*C.struct_sockaddr_in6)(unsafe.Pointer(ss))
var ip6 [16]byte
copy(ip6[:], (*[16]byte)(unsafe.Pointer(&sa6.sin6_addr))[:])
@ -77,14 +78,14 @@ func makeSockaddrStorage(ip net.IP, port int, ss *C.struct_sockaddr_storage) boo
C.memset(unsafe.Pointer(ss), 0, C.sizeof_struct_sockaddr_storage)
if len(ip) == 4 {
sa4 := (*C.struct_sockaddr_in)(unsafe.Pointer(ss))
sa4.sin_family = AFInet
sa4.sin_family = syscall.AF_INET
copy(((*[4]byte)(unsafe.Pointer(&sa4.sin_addr)))[:], ip)
binary.BigEndian.PutUint16(((*[2]byte)(unsafe.Pointer(&sa4.sin_port)))[:], uint16(port))
return true
}
if len(ip) == 16 {
sa6 := (*C.struct_sockaddr_in6)(unsafe.Pointer(ss))
sa6.sin6_family = AFInet6
sa6.sin6_family = syscall.AF_INET6
copy(((*[16]byte)(unsafe.Pointer(&sa6.sin6_addr)))[:], ip)
binary.BigEndian.PutUint16(((*[2]byte)(unsafe.Pointer(&sa6.sin6_port)))[:], uint16(port))
return true
@ -158,9 +159,9 @@ func (ina *InetAddress) String() string {
func (ina *InetAddress) Family() int {
switch len(ina.IP) {
case 4:
return AFInet
return syscall.AF_INET
case 16:
return AFInet6
return syscall.AF_INET6
}
return 0
}

View file

@ -16,7 +16,6 @@ package zerotier
import (
"encoding/json"
"io/ioutil"
rand "math/rand"
"os"
"runtime"
)
@ -92,15 +91,21 @@ type LocalConfig struct {
Settings LocalConfigSettings `json:"settings,omitempty"`
}
// Read this local config from a file, initializing to defaults if the file does not exist
func (lc *LocalConfig) Read(p string, saveDefaultsIfNotExist bool) error {
// Read this local config from a file, initializing to defaults if the file does not exist.
func (lc *LocalConfig) Read(p string, saveDefaultsIfNotExist bool,isTotallyNewNode bool) error {
if lc.Physical == nil {
lc.Physical = make(map[string]LocalConfigPhysicalPathConfiguration)
lc.Virtual = make(map[Address]LocalConfigVirtualAddressConfiguration)
lc.Network = make(map[NetworkID]NetworkLocalSettings)
lc.Settings.PrimaryPort = 9993
lc.Settings.SecondaryPort = 16384 + (rand.Int() % 16384)
lc.Settings.TertiaryPort = 32768 + (rand.Int() % 16384)
// LocalConfig default settings
if isTotallyNewNode {
lc.Settings.PrimaryPort = 893
} else {
lc.Settings.PrimaryPort = 9993
}
lc.Settings.SecondaryPort = unassignedPrivilegedPorts[randomUInt() % uint(len(unassignedPrivilegedPorts))]
lc.Settings.TertiaryPort = int(32768 + (randomUInt() % 16384))
lc.Settings.PortSearch = true
lc.Settings.PortMapping = true
lc.Settings.LogSizeMax = 128
@ -108,6 +113,8 @@ func (lc *LocalConfig) Read(p string, saveDefaultsIfNotExist bool) error {
switch runtime.GOOS {
case "windows":
lc.Settings.InterfacePrefixBlacklist = []string{"loopback"}
case "darwin":
lc.Settings.InterfacePrefixBlacklist = []string{"lo","utun","feth"}
default:
lc.Settings.InterfacePrefixBlacklist = []string{"lo"}
}

View file

@ -12,35 +12,45 @@ var (
ErrInvalidLocator = errors.New("invalid marshaled locator object")
)
// Timestamp returns this locator's timestamp in milliseconds since epoch.
func (l Locator) Timestamp() int64 {
if len(l) >= 8 {
return int64(binary.BigEndian.Uint64(l))
return int64(binary.BigEndian.Uint64(l[0:8]))
}
return 0
}
// Nil returns true if this is a nil/empty locator.
func (l Locator) Nil() bool {
return len(l) < 8 || int64(binary.BigEndian.Uint64(l[0:8])) <= 0
}
// Endpoints obtains the endpoints described by this locator.
func (l Locator) Endpoints() (eps []Endpoint,err error) {
if len(l) <= (8 + 2) {
if len(l) < 8 {
err = ErrInvalidLocator
return
}
endpointCount := int(binary.BigEndian.Uint16(l[8:10]))
eps = make([]Endpoint,endpointCount)
p := 10
for e:=0;e<endpointCount;e++ {
if p >= len(l) {
if int64(binary.BigEndian.Uint64(l[0:8])) > 0 {
if len(l) < 10 {
err = ErrInvalidLocator
return
}
var elen int
elen, err = eps[e].unmarshalZT(l[p:])
if err != nil {
return
endpointCount := int(binary.BigEndian.Uint16(l[8:10]))
eps = make([]Endpoint, endpointCount)
p := 10
for e := 0; e < endpointCount; e++ {
if p >= len(l) {
err = ErrInvalidLocator
return
}
var elen int
elen, err = eps[e].unmarshalZT(l[p:])
if err != nil {
return
}
p += elen
}
p += elen
}
return
}

View file

@ -16,17 +16,75 @@ package zerotier
import (
"encoding/base32"
"encoding/binary"
"math/rand"
"net"
"sync"
"time"
"unsafe"
)
// ZeroTierLogoChar is the unicode character that is ZeroTier's logo
const ZeroTierLogoChar = "⏁"
// LogoChar is the unicode character that is ZeroTier's logo
const LogoChar = "⏁"
// Base32StdLowerCase is a base32 encoder/decoder using a lower-case standard alphabet and no padding.
var Base32StdLowerCase = base32.NewEncoding("abcdefghijklmnopqrstuvwxyz234567").WithPadding(base32.NoPadding)
var unassignedPrivilegedPorts = []int{
4,
6,
8,
10,
12,
14,
15,
16,
26,
28,
30,
32,
34,
36,
40,
60,
269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279,
285,
288, 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, 307,
323, 324, 325, 326, 327, 328, 329, 330, 331, 332,
334, 335, 336, 337, 338, 339, 340, 341, 342, 343,
703,
708,
713, 714, 715, 716, 717, 718, 719, 720, 721, 722, 723, 724, 725, 726, 727, 728,
732, 733, 734, 735, 736, 737, 738, 739, 740,
743,
745, 746,
755, 756,
766,
768,
778, 779,
781, 782, 783, 784, 785, 786, 787, 788, 789, 790, 791, 792, 793, 794, 795, 796, 797, 798, 799,
802, 803, 804, 805, 806, 807, 808, 809,
811, 812, 813, 814, 815, 816, 817, 818, 819, 820, 821, 822, 823, 824, 825, 826, 827,
834, 835, 836, 837, 838, 839, 840, 841, 842, 843, 844, 845, 846,
849, 850, 851, 852, 853, 854, 855, 856, 857, 858, 859,
862, 863, 864, 865, 866, 867, 868, 869, 870, 871, 872,
874, 875, 876, 877, 878, 879, 880, 881, 882, 883, 884, 885,
889, 890, 891, 892, 893, 894, 895, 896, 897, 898, 899,
904, 905, 906, 907, 908, 909, 910, 911,
914, 915, 916, 917, 918, 919, 920, 921, 922, 923, 924, 925, 926, 927, 928, 929, 930, 931, 932, 933, 934, 935, 936, 937, 938, 939, 940, 941, 942, 943, 944, 945, 946, 947, 948, 949, 950, 951, 952, 953, 954, 955, 956, 957, 958, 959, 960, 961, 962, 963, 964, 965, 966, 967, 968, 969, 970, 971, 972, 973, 974, 975, 976, 977, 978, 979, 980, 981, 982, 983, 984, 985, 986, 987, 988,
1001, 1002, 1003, 1004, 1005, 1006, 1007, 1008, 1009,
1023,
}
var prng = rand.NewSource(time.Now().UnixNano())
var prngLock sync.Mutex
func randomUInt() uint {
prngLock.Lock()
i := prng.Int63()
prngLock.Unlock()
return uint(i)
}
// TimeMs returns the time in milliseconds since epoch.
func TimeMs() int64 { return int64(time.Now().UnixNano()) / int64(1000000) }

View file

@ -24,6 +24,7 @@ import (
"net"
"sync"
"sync/atomic"
"syscall"
"unsafe"
)
@ -70,12 +71,12 @@ func (t *nativeTap) AddIP(ip *net.IPNet) error {
if bits > 128 || bits < 0 {
return ErrInvalidParameter
}
C.ZT_GoTap_addIp(t.tap, C.int(AFInet6), unsafe.Pointer(&ip.IP[0]), C.int(bits))
C.ZT_GoTap_addIp(t.tap, C.int(syscall.AF_INET6), unsafe.Pointer(&ip.IP[0]), C.int(bits))
} else if len(ip.IP) == 4 {
if bits > 32 || bits < 0 {
return ErrInvalidParameter
}
C.ZT_GoTap_addIp(t.tap, C.int(AFInet), unsafe.Pointer(&ip.IP[0]), C.int(bits))
C.ZT_GoTap_addIp(t.tap, C.int(syscall.AF_INET), unsafe.Pointer(&ip.IP[0]), C.int(bits))
}
return ErrInvalidParameter
}
@ -87,14 +88,14 @@ func (t *nativeTap) RemoveIP(ip *net.IPNet) error {
if bits > 128 || bits < 0 {
return ErrInvalidParameter
}
C.ZT_GoTap_removeIp(t.tap, C.int(AFInet6), unsafe.Pointer(&ip.IP[0]), C.int(bits))
C.ZT_GoTap_removeIp(t.tap, C.int(syscall.AF_INET6), unsafe.Pointer(&ip.IP[0]), C.int(bits))
return nil
}
if len(ip.IP) == 4 {
if bits > 32 || bits < 0 {
return ErrInvalidParameter
}
C.ZT_GoTap_removeIp(t.tap, C.int(AFInet), unsafe.Pointer(&ip.IP[0]), C.int(bits))
C.ZT_GoTap_removeIp(t.tap, C.int(syscall.AF_INET), unsafe.Pointer(&ip.IP[0]), C.int(bits))
return nil
}
return ErrInvalidParameter
@ -115,7 +116,7 @@ func (t *nativeTap) IPs() (ips []net.IPNet, err error) {
af := int(ipbuf[ipptr])
ipptr++
switch af {
case AFInet:
case syscall.AF_INET:
var ip [4]byte
for j := 0; j < 4; j++ {
ip[j] = ipbuf[ipptr]
@ -124,7 +125,7 @@ func (t *nativeTap) IPs() (ips []net.IPNet, err error) {
bits := ipbuf[ipptr]
ipptr++
ips = append(ips, net.IPNet{IP: net.IP(ip[:]), Mask: net.CIDRMask(int(bits), 32)})
case AFInet6:
case syscall.AF_INET6:
var ip [16]byte
for j := 0; j < 16; j++ {
ip[j] = ipbuf[ipptr]
@ -168,16 +169,16 @@ func (t *nativeTap) AddRoute(r *Route) error {
if len(r.Target.IP) == 4 {
mask, _ := r.Target.Mask.Size()
if len(via) == 4 {
rc = int(C.ZT_GoTap_addRoute(t.tap, AFInet, unsafe.Pointer(&r.Target.IP[0]), C.int(mask), AFInet, unsafe.Pointer(&via[0]), C.uint(r.Metric)))
rc = int(C.ZT_GoTap_addRoute(t.tap, syscall.AF_INET, unsafe.Pointer(&r.Target.IP[0]), C.int(mask), syscall.AF_INET, unsafe.Pointer(&via[0]), C.uint(r.Metric)))
} else {
rc = int(C.ZT_GoTap_addRoute(t.tap, AFInet, unsafe.Pointer(&r.Target.IP[0]), C.int(mask), 0, nil, C.uint(r.Metric)))
rc = int(C.ZT_GoTap_addRoute(t.tap, syscall.AF_INET, unsafe.Pointer(&r.Target.IP[0]), C.int(mask), 0, nil, C.uint(r.Metric)))
}
} else if len(r.Target.IP) == 16 {
mask, _ := r.Target.Mask.Size()
if len(via) == 16 {
rc = int(C.ZT_GoTap_addRoute(t.tap, AFInet6, unsafe.Pointer(&r.Target.IP[0]), C.int(mask), AFInet6, unsafe.Pointer(&via[0]), C.uint(r.Metric)))
rc = int(C.ZT_GoTap_addRoute(t.tap, syscall.AF_INET6, unsafe.Pointer(&r.Target.IP[0]), C.int(mask), syscall.AF_INET6, unsafe.Pointer(&via[0]), C.uint(r.Metric)))
} else {
rc = int(C.ZT_GoTap_addRoute(t.tap, AFInet6, unsafe.Pointer(&r.Target.IP[0]), C.int(mask), 0, nil, C.uint(r.Metric)))
rc = int(C.ZT_GoTap_addRoute(t.tap, syscall.AF_INET6, unsafe.Pointer(&r.Target.IP[0]), C.int(mask), 0, nil, C.uint(r.Metric)))
}
}
}
@ -198,16 +199,16 @@ func (t *nativeTap) RemoveRoute(r *Route) error {
if len(r.Target.IP) == 4 {
mask, _ := r.Target.Mask.Size()
if len(via) == 4 {
rc = int(C.ZT_GoTap_removeRoute(t.tap, AFInet, unsafe.Pointer(&r.Target.IP[0]), C.int(mask), AFInet, unsafe.Pointer(&(via[0])), C.uint(r.Metric)))
rc = int(C.ZT_GoTap_removeRoute(t.tap, syscall.AF_INET, unsafe.Pointer(&r.Target.IP[0]), C.int(mask), syscall.AF_INET, unsafe.Pointer(&(via[0])), C.uint(r.Metric)))
} else {
rc = int(C.ZT_GoTap_removeRoute(t.tap, AFInet, unsafe.Pointer(&r.Target.IP[0]), C.int(mask), 0, nil, C.uint(r.Metric)))
rc = int(C.ZT_GoTap_removeRoute(t.tap, syscall.AF_INET, unsafe.Pointer(&r.Target.IP[0]), C.int(mask), 0, nil, C.uint(r.Metric)))
}
} else if len(r.Target.IP) == 16 {
mask, _ := r.Target.Mask.Size()
if len(via) == 16 {
rc = int(C.ZT_GoTap_removeRoute(t.tap, AFInet6, unsafe.Pointer(&r.Target.IP[0]), C.int(mask), AFInet6, unsafe.Pointer(&via[0]), C.uint(r.Metric)))
rc = int(C.ZT_GoTap_removeRoute(t.tap, syscall.AF_INET6, unsafe.Pointer(&r.Target.IP[0]), C.int(mask), syscall.AF_INET6, unsafe.Pointer(&via[0]), C.uint(r.Metric)))
} else {
rc = int(C.ZT_GoTap_removeRoute(t.tap, AFInet6, unsafe.Pointer(&r.Target.IP[0]), C.int(mask), 0, nil, C.uint(r.Metric)))
rc = int(C.ZT_GoTap_removeRoute(t.tap, syscall.AF_INET6, unsafe.Pointer(&r.Target.IP[0]), C.int(mask), 0, nil, C.uint(r.Metric)))
}
}
}

View file

@ -35,6 +35,7 @@ import (
"strings"
"sync"
"sync/atomic"
"syscall"
"time"
"unsafe"
@ -65,12 +66,6 @@ const (
// CoreVersionBuild is the build version of the ZeroTier core
CoreVersionBuild int = C.ZEROTIER_ONE_VERSION_BUILD
// AFInet is the address family for IPv4
AFInet = C.AF_INET
// AFInet6 is the address family for IPv6
AFInet6 = C.AF_INET6
networkConfigOpUp int = C.ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_UP
networkConfigOpUpdate int = C.ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_CONFIG_UPDATE
@ -136,15 +131,16 @@ func NewNode(basePath string) (n *Node, err error) {
}
n.localConfigPath = path.Join(basePath, "local.conf")
err = n.localConfig.Read(n.localConfigPath, true)
_, identitySecretNotFoundErr := os.Stat(path.Join(basePath,"identity.secret"))
err = n.localConfig.Read(n.localConfigPath, true, identitySecretNotFoundErr != nil)
if err != nil {
return nil, err
return
}
if n.localConfig.Settings.LogSizeMax >= 0 {
n.logW, err = sizeLimitWriterOpen(path.Join(basePath, "service.log"))
n.logW, err = sizeLimitWriterOpen(path.Join(basePath, "node.log"))
if err != nil {
return nil, err
return
}
n.log = log.New(n.logW, "", log.LstdFlags)
} else {
@ -155,50 +151,62 @@ func NewNode(basePath string) (n *Node, err error) {
portsChanged := false
portCheckCount := 0
for portCheckCount < 2048 {
origPort := n.localConfig.Settings.PrimaryPort
for portCheckCount < 256 {
portCheckCount++
if checkPort(n.localConfig.Settings.PrimaryPort) {
if n.localConfig.Settings.PrimaryPort != origPort {
n.log.Printf("primary port %d unavailable, found port %d (port search enabled)", origPort, n.localConfig.Settings.PrimaryPort)
}
break
}
n.log.Printf("primary port %d unavailable, trying next port (port search enabled)", n.localConfig.Settings.PrimaryPort)
n.localConfig.Settings.PrimaryPort++
n.localConfig.Settings.PrimaryPort &= 0xffff
n.localConfig.Settings.PrimaryPort = int(4096 + (randomUInt() % 16384))
portsChanged = true
}
if portCheckCount == 2048 {
if portCheckCount == 256 {
return nil, errors.New("unable to bind to primary port, tried 2048 later ports")
}
if n.localConfig.Settings.SecondaryPort > 0 {
portCheckCount = 0
for portCheckCount < 2048 {
origPort = n.localConfig.Settings.SecondaryPort
for portCheckCount < 256 {
portCheckCount++
if checkPort(n.localConfig.Settings.SecondaryPort) {
if n.localConfig.Settings.SecondaryPort != origPort {
n.log.Printf("secondary port %d unavailable, found port %d (port search enabled)", origPort, n.localConfig.Settings.SecondaryPort)
}
break
}
n.log.Printf("secondary port %d unavailable, trying next port (port search enabled)", n.localConfig.Settings.SecondaryPort)
n.localConfig.Settings.SecondaryPort++
n.localConfig.Settings.SecondaryPort &= 0xffff
n.log.Printf("secondary port %d unavailable, trying a random port (port search enabled)", n.localConfig.Settings.SecondaryPort)
if portCheckCount <= 64 {
n.localConfig.Settings.SecondaryPort = unassignedPrivilegedPorts[randomUInt() % uint(len(unassignedPrivilegedPorts))]
} else {
n.localConfig.Settings.SecondaryPort = int(16384 + (randomUInt() % 16384))
}
portsChanged = true
}
if portCheckCount == 2048 {
if portCheckCount == 256 {
n.localConfig.Settings.SecondaryPort = 0
}
}
if n.localConfig.Settings.TertiaryPort > 0 {
portCheckCount = 0
for portCheckCount < 2048 {
origPort = n.localConfig.Settings.TertiaryPort
for portCheckCount < 256 {
portCheckCount++
if checkPort(n.localConfig.Settings.TertiaryPort) {
if n.localConfig.Settings.TertiaryPort != origPort {
n.log.Printf("tertiary port %d unavailable, found port %d (port search enabled)", origPort, n.localConfig.Settings.TertiaryPort)
}
break
}
n.log.Printf("tertiary port %d unavailable, trying next port (port search enabled)", n.localConfig.Settings.TertiaryPort)
n.localConfig.Settings.TertiaryPort++
n.localConfig.Settings.TertiaryPort &= 0xffff
n.log.Printf("tertiary port %d unavailable, trying a random port (port search enabled)", n.localConfig.Settings.TertiaryPort)
n.localConfig.Settings.TertiaryPort = int(32768 + (randomUInt() % 16384))
portsChanged = true
}
if portCheckCount == 2048 {
if portCheckCount == 256 {
n.localConfig.Settings.TertiaryPort = 0
}
}
@ -691,9 +699,9 @@ func goPathCheckFunc(gn unsafe.Pointer, ztAddress C.uint64_t, af C.int, ip unsaf
node := nodesByUserPtr[uintptr(gn)]
nodesByUserPtrLock.RUnlock()
var nip net.IP
if af == AFInet {
if af == syscall.AF_INET {
nip = ((*[4]byte)(ip))[:]
} else if af == AFInet6 {
} else if af == syscall.AF_INET6 {
nip = ((*[16]byte)(ip))[:]
} else {
return 0
@ -717,16 +725,18 @@ func goPathLookupFunc(gn unsafe.Pointer, ztAddress C.uint64_t, desiredFamily int
if err != nil {
return 0
}
ip, port := node.pathLookup(id)
if len(ip) > 0 && port > 0 && port <= 65535 {
ip4 := ip.To4()
if len(ip4) == 4 {
*((*C.int)(familyP)) = C.int(AFInet)
*((*C.int)(familyP)) = C.int(syscall.AF_INET)
copy((*[4]byte)(ipP)[:], ip4)
*((*C.int)(portP)) = C.int(port)
return 1
} else if len(ip) == 16 {
*((*C.int)(familyP)) = C.int(AFInet6)
*((*C.int)(familyP)) = C.int(syscall.AF_INET6)
copy((*[16]byte)(ipP)[:], ip)
*((*C.int)(portP)) = C.int(port)
return 1

View file

@ -6,9 +6,8 @@ type Root struct {
Identity Identity `json:"identity"`
// Locator describes the endpoints where this root may be found.
Locator Locator `json:"locator"`
Locator Locator `json:"locator,omitempty"`
// URL is an optional URL where the latest Root may be fetched.
// This is one method of locator update, while in-band mechanisms are the other.
URL string `json:"url"`
// Bootstrap is an array of IP/port locations where this root might be found if a locator is not known.
Bootstrap []InetAddress `json:"bootstrap,omitempty"`
}

View file

@ -50,8 +50,11 @@ extern "C" {
/**
* Default UDP port for devices running a ZeroTier endpoint
*
* NOTE: as of V2 this has changed to 893 since many NATs (even symmetric)
* treat privileged ports in a special way. The old default was 9993.
*/
#define ZT_DEFAULT_PORT 9993
#define ZT_DEFAULT_PORT 893
/**
* Minimum MTU, which is the minimum allowed by IPv6 and several specs
@ -108,7 +111,10 @@ extern "C" {
#define ZT_MAX_ZT_ASSIGNED_ADDRESSES 32
/**
* Maximum number of "specialists" on a network -- bridges, anchors, etc.
* Maximum number of "specialists" on a network -- bridges, etc.
*
* A specialist is a node tagged with some special role like acting as
* a promiscuous bridge, open relay, administrator, etc.
*/
#define ZT_MAX_NETWORK_SPECIALISTS 256
@ -134,6 +140,10 @@ extern "C" {
/**
* Maximum number of direct network paths to a given peer
*
* Note that dual-stack configs may end up resulting in both IPv6 and IPv4
* paths existing. This gives enough headroom for multipath configs with
* dual stacks across the board.
*/
#define ZT_MAX_PEER_NETWORK_PATHS 16
@ -144,11 +154,18 @@ extern "C" {
/**
* Maximum number of rules per capability object
*
* Capabilities normally contain only a few rules. The rules in a capability
* should be short and to the point.
*/
#define ZT_MAX_CAPABILITY_RULES 64
/**
* Maximum number of certificates of ownership to assign to a single network member
*
* Network members can have more than four IPs, etc., but right now there
* is a protocol limit on how many COOs can be assigned. If your config needs
* more than four authenticated IPs per node you may have personal problems.
*/
#define ZT_MAX_CERTIFICATES_OF_OWNERSHIP 4
@ -159,14 +176,11 @@ extern "C" {
/**
* Maximum number of multicast group subscriptions on a local virtual network interface
*
* This coincides with many operating systems' maximum values and is rather huge.
*/
#define ZT_MAX_MULTICAST_SUBSCRIPTIONS 1024
/**
* Maximum value for link quality (min is 0)
*/
#define ZT_PATH_LINK_QUALITY_MAX 255
/* Rule specification contants **********************************************/
/**
@ -1395,8 +1409,9 @@ ZT_SDK_API enum ZT_ResultCode ZT_Node_new(ZT_Node **node,void *uptr,void *tptr,c
* first. This can crash if processXXX() methods are in progress.
*
* @param node Node to delete
* @param tptr Thread pointer to pass to functions/callbacks resulting from this call
*/
ZT_SDK_API void ZT_Node_delete(ZT_Node *node);
ZT_SDK_API void ZT_Node_delete(ZT_Node *node,void *tptr);
/**
* Process a packet received from the physical wire

View file

@ -173,7 +173,7 @@
/**
* Interval between direct path pushes in milliseconds if we don't have a path
*/
#define ZT_DIRECT_PATH_PUSH_INTERVAL 15000
#define ZT_DIRECT_PATH_PUSH_INTERVAL 30000
/**
* Interval between direct path pushes in milliseconds if we already have a path

View file

@ -53,13 +53,15 @@ bool Endpoint::operator<(const Endpoint &ep) const
int Endpoint::marshal(uint8_t data[ZT_ENDPOINT_MARSHAL_SIZE_MAX]) const
{
int p;
data[0] = (uint8_t)_t;
Utils::storeBigEndian(data + 1,(int16_t)_l[0]);
Utils::storeBigEndian(data + 3,(int16_t)_l[1]);
Utils::storeBigEndian(data + 5,(int16_t)_l[2]);
switch(_t) {
case INETADDR:
data[0] = (uint8_t)INETADDR;
return 1 + reinterpret_cast<const InetAddress *>(&_v.sa)->marshal(data+1);
return 7 + reinterpret_cast<const InetAddress *>(&_v.sa)->marshal(data+1);
case DNSNAME:
data[0] = (uint8_t)DNSNAME;
p = 1;
p = 7;
for (;;) {
if ((data[p] = (uint8_t)_v.dns.name[p-1]) == 0)
break;
@ -71,17 +73,15 @@ int Endpoint::marshal(uint8_t data[ZT_ENDPOINT_MARSHAL_SIZE_MAX]) const
data[p++] = (uint8_t)_v.dns.port;
return p;
case ZEROTIER:
data[0] = (uint8_t)ZEROTIER;
data[1] = (uint8_t)(_v.zt.a >> 32U);
data[2] = (uint8_t)(_v.zt.a >> 24U);
data[3] = (uint8_t)(_v.zt.a >> 16U);
data[4] = (uint8_t)(_v.zt.a >> 8U);
data[5] = (uint8_t)_v.zt.a;
memcpy(data + 6,_v.zt.idh,ZT_IDENTITY_HASH_SIZE);
return (ZT_IDENTITY_HASH_SIZE + 6);
data[7] = (uint8_t)(_v.zt.a >> 32U);
data[8] = (uint8_t)(_v.zt.a >> 24U);
data[9] = (uint8_t)(_v.zt.a >> 16U);
data[10] = (uint8_t)(_v.zt.a >> 8U);
data[11] = (uint8_t)_v.zt.a;
memcpy(data + 12,_v.zt.idh,ZT_IDENTITY_HASH_SIZE);
return ZT_IDENTITY_HASH_SIZE + 12;
case URL:
data[0] = (uint8_t)URL;
p = 1;
p = 7;
for (;;) {
if ((data[p] = (uint8_t)_v.url[p-1]) == 0)
break;
@ -91,65 +91,63 @@ int Endpoint::marshal(uint8_t data[ZT_ENDPOINT_MARSHAL_SIZE_MAX]) const
}
return p;
case ETHERNET:
data[0] = (uint8_t)ETHERNET;
data[1] = (uint8_t)(_v.eth >> 40U);
data[2] = (uint8_t)(_v.eth >> 32U);
data[3] = (uint8_t)(_v.eth >> 24U);
data[4] = (uint8_t)(_v.eth >> 16U);
data[5] = (uint8_t)(_v.eth >> 8U);
data[6] = (uint8_t)_v.eth;
return 7;
data[7] = (uint8_t)(_v.eth >> 40U);
data[8] = (uint8_t)(_v.eth >> 32U);
data[9] = (uint8_t)(_v.eth >> 24U);
data[10] = (uint8_t)(_v.eth >> 16U);
data[11] = (uint8_t)(_v.eth >> 8U);
data[12] = (uint8_t)_v.eth;
return 13;
default:
data[0] = (uint8_t)NIL;
return 1;
return 7;
}
}
int Endpoint::unmarshal(const uint8_t *restrict data,const int len)
{
if (len <= 0)
if (len < 7)
return -1;
int p;
switch((Type)data[0]) {
_t = (Type)data[0];
_l[0] = Utils::loadBigEndian<int16_t>(data + 1);
_l[1] = Utils::loadBigEndian<int16_t>(data + 3);
_l[2] = Utils::loadBigEndian<int16_t>(data + 5);
switch(_t) {
case NIL:
_t = NIL;
return 1;
return 7;
case INETADDR:
_t = INETADDR;
return reinterpret_cast<InetAddress *>(&_v.sa)->unmarshal(data+1,len-1);
return 7 + reinterpret_cast<InetAddress *>(&_v.sa)->unmarshal(data+7,len-7);
case DNSNAME:
if (len < 4)
if (len < 10)
return -1;
_t = DNSNAME;
p = 1;
p = 7;
for (;;) {
if ((_v.dns.name[p-1] = (char)data[p]) == 0) {
++p;
break;
}
++p;
if ((p >= (ZT_ENDPOINT_MAX_NAME_SIZE+1))||(p >= (len-2)))
if ((p >= (ZT_ENDPOINT_MARSHAL_SIZE_MAX-2))||(p >= (len-2)))
return -1;
}
_v.dns.port = (uint16_t)(((unsigned int)data[p++]) << 8U);
_v.dns.port |= (uint16_t)data[p++];
return p;
case ZEROTIER:
if (len < (ZT_IDENTITY_HASH_SIZE + 6))
if (len < 60)
return -1;
_t = ZEROTIER;
_v.zt.a = ((uint64_t)data[1]) << 32U;
_v.zt.a |= ((uint64_t)data[2]) << 24U;
_v.zt.a |= ((uint64_t)data[3]) << 16U;
_v.zt.a |= ((uint64_t)data[4]) << 8U;
_v.zt.a |= (uint64_t)data[5];
memcpy(_v.zt.idh,data + 6,ZT_IDENTITY_HASH_SIZE);
return (ZT_IDENTITY_HASH_SIZE + 6);
_v.zt.a = ((uint64_t)data[7]) << 32U;
_v.zt.a |= ((uint64_t)data[8]) << 24U;
_v.zt.a |= ((uint64_t)data[9]) << 16U;
_v.zt.a |= ((uint64_t)data[10]) << 8U;
_v.zt.a |= (uint64_t)data[11];
memcpy(_v.zt.idh,data + 12,48);
return 60;
case URL:
if (len < 2)
if (len < 8)
return -1;
_t = URL;
p = 1;
p = 7;
for (;;) {
if ((_v.url[p-1] = (char)data[p]) == 0) {
++p;
@ -161,25 +159,22 @@ int Endpoint::unmarshal(const uint8_t *restrict data,const int len)
}
return p;
case ETHERNET:
if (len < 7)
if (len < 13)
return -1;
_t = ZEROTIER;
_v.eth = ((uint64_t)data[1]) << 40U;
_v.eth |= ((uint64_t)data[2]) << 32U;
_v.eth |= ((uint64_t)data[3]) << 24U;
_v.eth |= ((uint64_t)data[4]) << 16U;
_v.eth |= ((uint64_t)data[5]) << 8U;
_v.eth |= (uint64_t)data[6];
return 7;
_v.eth = ((uint64_t)data[7]) << 40U;
_v.eth |= ((uint64_t)data[8]) << 32U;
_v.eth |= ((uint64_t)data[9]) << 24U;
_v.eth |= ((uint64_t)data[10]) << 16U;
_v.eth |= ((uint64_t)data[11]) << 8U;
_v.eth |= (uint64_t)data[12];
return 13;
default:
// Unrecognized endpoint types not yet specified must start with a byte
// length size so that older versions of ZeroTier can skip them.
if (len < 2)
if (len < 8)
return -1;
_t = UNRECOGNIZED;
return 1 + (int)data[1];
return 8 + (int)data[7];
}
return false;
}
} // namespace ZeroTier

View file

@ -24,12 +24,16 @@
#include "Address.hpp"
#include "Utils.hpp"
#define ZT_ENDPOINT_MARSHAL_SIZE_MAX (ZT_ENDPOINT_MAX_NAME_SIZE+3)
// max name size + type byte + port (for DNS name/port) + 3x 16-bit coordinate for location
#define ZT_ENDPOINT_MARSHAL_SIZE_MAX (ZT_ENDPOINT_MAX_NAME_SIZE+1+2+2+2+2)
namespace ZeroTier {
/**
* Endpoint variant specifying some form of network endpoint
*
* This data structure supports a number of types that are not yet actually used:
* DNSNAME, URL, and ETHERNET. These are present to reserve them for future use.
*/
class Endpoint
{
@ -52,10 +56,10 @@ public:
ZT_ALWAYS_INLINE Endpoint(const char *name,const int port) : _t(DNSNAME) { Utils::scopy(_v.dns.name,sizeof(_v.dns.name),name); _v.dns.port = port; }
explicit ZT_ALWAYS_INLINE Endpoint(const char *url) : _t(URL) { Utils::scopy(_v.url,sizeof(_v.url),url); }
ZT_ALWAYS_INLINE const InetAddress *sockaddr() const { return (_t == INETADDR) ? reinterpret_cast<const InetAddress *>(&_v.sa) : nullptr; }
ZT_ALWAYS_INLINE const InetAddress *inetAddr() const { return (_t == INETADDR) ? reinterpret_cast<const InetAddress *>(&_v.sa) : nullptr; }
ZT_ALWAYS_INLINE const char *dnsName() const { return (_t == DNSNAME) ? _v.dns.name : nullptr; }
ZT_ALWAYS_INLINE int dnsPort() const { return (_t == DNSNAME) ? _v.dns.port : -1; }
ZT_ALWAYS_INLINE Address ztAddress() const { return (_t == ZEROTIER) ? Address(_v.zt.a) : Address(); }
ZT_ALWAYS_INLINE Address ztAddress() const { return Address((_t == ZEROTIER) ? _v.zt.a : (uint64_t)0); }
ZT_ALWAYS_INLINE const uint8_t *ztIdentityHash() const { return (_t == ZEROTIER) ? _v.zt.idh : nullptr; }
ZT_ALWAYS_INLINE const char *url() const { return (_t == URL) ? _v.url : nullptr; }
ZT_ALWAYS_INLINE MAC ethernet() const { return (_t == ETHERNET) ? MAC(_v.eth) : MAC(); }
@ -75,6 +79,7 @@ public:
private:
Type _t;
int _l[3]; // X,Y,Z location in kilometers from the nearest gravitational center of mass
union {
struct sockaddr_storage sa;
struct {

View file

@ -16,8 +16,8 @@
#include "Constants.hpp"
#include <stdlib.h>
#include <stdio.h>
#include <cstdlib>
#include <stdexcept>
#include <vector>
namespace ZeroTier {
@ -94,44 +94,29 @@ public:
/**
* @param bc Initial capacity in buckets (default: 32, must be nonzero)
*/
inline Hashtable(unsigned long bc = 32) :
ZT_ALWAYS_INLINE Hashtable(unsigned long bc = 32) :
_t(reinterpret_cast<_Bucket **>(::malloc(sizeof(_Bucket *) * bc))),
_bc(bc),
_s(0)
{
if (!_t)
throw ZT_EXCEPTION_OUT_OF_MEMORY;
for(unsigned long i=0;i<bc;++i)
_t[i] = (_Bucket *)0;
throw std::bad_alloc();
memset(_t,0,sizeof(_Bucket *) * bc);
}
inline Hashtable(const Hashtable<K,V> &ht) :
_t(reinterpret_cast<_Bucket **>(::malloc(sizeof(_Bucket *) * ht._bc))),
_bc(ht._bc),
_s(ht._s)
ZT_ALWAYS_INLINE Hashtable(const Hashtable<K,V> &ht) :
Hashtable()
{
if (!_t)
throw ZT_EXCEPTION_OUT_OF_MEMORY;
for(unsigned long i=0;i<_bc;++i)
_t[i] = (_Bucket *)0;
for(unsigned long i=0;i<_bc;++i) {
const _Bucket *b = ht._t[i];
while (b) {
_Bucket *nb = new _Bucket(*b);
nb->next = _t[i];
_t[i] = nb;
b = b->next;
}
}
*this = ht;
}
inline ~Hashtable()
ZT_ALWAYS_INLINE ~Hashtable()
{
this->clear();
::free(_t);
}
inline Hashtable &operator=(const Hashtable<K,V> &ht)
ZT_ALWAYS_INLINE Hashtable &operator=(const Hashtable<K,V> &ht)
{
this->clear();
if (ht._s) {
@ -149,7 +134,7 @@ public:
/**
* Erase all entries
*/
inline void clear()
ZT_ALWAYS_INLINE void clear()
{
if (_s) {
for(unsigned long i=0;i<_bc;++i) {
@ -168,7 +153,7 @@ public:
/**
* @return Vector of all keys
*/
inline typename std::vector<K> keys() const
ZT_ALWAYS_INLINE typename std::vector<K> keys() const
{
typename std::vector<K> k;
if (_s) {
@ -191,7 +176,7 @@ public:
* @tparam Type of V (generally inferred)
*/
template<typename C>
inline void appendKeys(C &v) const
ZT_ALWAYS_INLINE void appendKeys(C &v) const
{
if (_s) {
for(unsigned long i=0;i<_bc;++i) {
@ -207,7 +192,7 @@ public:
/**
* @return Vector of all entries (pairs of K,V)
*/
inline typename std::vector< std::pair<K,V> > entries() const
ZT_ALWAYS_INLINE typename std::vector< std::pair<K,V> > entries() const
{
typename std::vector< std::pair<K,V> > k;
if (_s) {
@ -227,7 +212,7 @@ public:
* @param k Key
* @return Pointer to value or NULL if not found
*/
inline V *get(const K k)
ZT_ALWAYS_INLINE V *get(const K k)
{
_Bucket *b = _t[_hc(k) % _bc];
while (b) {
@ -237,14 +222,14 @@ public:
}
return (V *)0;
}
inline const V *get(const K k) const { return const_cast<Hashtable *>(this)->get(k); }
ZT_ALWAYS_INLINE const V *get(const K k) const { return const_cast<Hashtable *>(this)->get(k); }
/**
* @param k Key
* @param v Value to fill with result
* @return True if value was found and set (if false, v is not modified)
*/
inline bool get(const K &k,V &v) const
ZT_ALWAYS_INLINE bool get(const K &k,V &v) const
{
_Bucket *b = _t[_hc(k) % _bc];
while (b) {
@ -261,7 +246,7 @@ public:
* @param k Key to check
* @return True if key is present
*/
inline bool contains(const K &k) const
ZT_ALWAYS_INLINE bool contains(const K &k) const
{
_Bucket *b = _t[_hc(k) % _bc];
while (b) {
@ -276,7 +261,7 @@ public:
* @param k Key
* @return True if value was present
*/
inline bool erase(const K &k)
ZT_ALWAYS_INLINE bool erase(const K &k)
{
const unsigned long bidx = _hc(k) % _bc;
_Bucket *lastb = (_Bucket *)0;
@ -301,7 +286,7 @@ public:
* @param v Value
* @return Reference to value in table
*/
inline V &set(const K &k,const V &v)
ZT_ALWAYS_INLINE V &set(const K &k,const V &v)
{
const unsigned long h = _hc(k);
unsigned long bidx = h % _bc;
@ -331,7 +316,7 @@ public:
* @param k Key
* @return Value, possibly newly created
*/
inline V &operator[](const K k)
ZT_ALWAYS_INLINE V &operator[](const K k)
{
const unsigned long h = _hc(k);
unsigned long bidx = h % _bc;
@ -368,15 +353,18 @@ public:
private:
template<typename O>
static ZT_ALWAYS_INLINE unsigned long _hc(const O &obj) { return (unsigned long)obj.hashCode(); }
static ZT_ALWAYS_INLINE unsigned long _hc(const uint64_t i) { return (unsigned long)(i ^ (i >> 32)); }
static ZT_ALWAYS_INLINE unsigned long _hc(const uint64_t i) { return (unsigned long)(i ^ (i >> 32U)); }
static ZT_ALWAYS_INLINE unsigned long _hc(const uint32_t i) { return ((unsigned long)i * (unsigned long)0x9e3779b1); }
static ZT_ALWAYS_INLINE unsigned long _hc(const uint16_t i) { return ((unsigned long)i * (unsigned long)0x9e3779b1); }
static ZT_ALWAYS_INLINE unsigned long _hc(const int i) { return ((unsigned long)i * (unsigned long)0x9e3379b1); }
static ZT_ALWAYS_INLINE unsigned long _hc(const uint8_t i) { return ((unsigned long)i * (unsigned long)0x9e3779b1); }
static ZT_ALWAYS_INLINE unsigned long _hc(const int64_t i) { return (unsigned long)(i ^ (i >> 32U)); }
static ZT_ALWAYS_INLINE unsigned long _hc(const int32_t i) { return ((unsigned long)i * (unsigned long)0x9e3779b1); }
static ZT_ALWAYS_INLINE unsigned long _hc(const int16_t i) { return ((unsigned long)i * (unsigned long)0x9e3779b1); }
static ZT_ALWAYS_INLINE unsigned long _hc(const int8_t i) { return ((unsigned long)i * (unsigned long)0x9e3779b1); }
static ZT_ALWAYS_INLINE unsigned long _hc(void *p) { return ((unsigned long)((uintptr_t)p) * (unsigned long)0x9e3779b1); }
static ZT_ALWAYS_INLINE unsigned long _hc(const void *p) { return ((unsigned long)((uintptr_t)p) * (unsigned long)0x9e3779b1); }
inline void _grow()
ZT_ALWAYS_INLINE void _grow()
{
const unsigned long nc = _bc * 2;
_Bucket **nt = reinterpret_cast<_Bucket **>(::malloc(sizeof(_Bucket *) * nc));

View file

@ -32,6 +32,8 @@ namespace ZeroTier {
*/
#define ZT_INETADDRESS_MAX_SCOPE 7
#define ZT_INETADDRESS_MARSHAL_SIZE_MAX 19
/**
* Extends sockaddr_storage with friendly C++ methods
*
@ -463,7 +465,7 @@ public:
*/
explicit ZT_ALWAYS_INLINE operator bool() const { return (ss_family != 0); }
static ZT_ALWAYS_INLINE int marshalSizeMax() { return 19; }
static ZT_ALWAYS_INLINE int marshalSizeMax() { return ZT_INETADDRESS_MARSHAL_SIZE_MAX; }
int marshal(uint8_t data[19]) const;
int unmarshal(const uint8_t *restrict data,const int len);
@ -588,9 +590,19 @@ public:
static ZT_ALWAYS_INLINE InetAddress *asInetAddress(sockaddr_in *p) { return reinterpret_cast<InetAddress *>(p); }
static ZT_ALWAYS_INLINE InetAddress *asInetAddress(sockaddr_in6 *p) { return reinterpret_cast<InetAddress *>(p); }
static ZT_ALWAYS_INLINE InetAddress *asInetAddress(sockaddr *p) { return reinterpret_cast<InetAddress *>(p); }
static ZT_ALWAYS_INLINE InetAddress *asInetAddress(sockaddr_storage *p) { return reinterpret_cast<InetAddress *>(p); }
static ZT_ALWAYS_INLINE const InetAddress *asInetAddress(const sockaddr_in *p) { return reinterpret_cast<const InetAddress *>(p); }
static ZT_ALWAYS_INLINE const InetAddress *asInetAddress(const sockaddr_in6 *p) { return reinterpret_cast<const InetAddress *>(p); }
static ZT_ALWAYS_INLINE const InetAddress *asInetAddress(const sockaddr *p) { return reinterpret_cast<const InetAddress *>(p); }
static ZT_ALWAYS_INLINE const InetAddress *asInetAddress(const sockaddr_storage *p) { return reinterpret_cast<const InetAddress *>(p); }
static ZT_ALWAYS_INLINE InetAddress &asInetAddress(sockaddr_in &p) { return *reinterpret_cast<InetAddress *>(&p); }
static ZT_ALWAYS_INLINE InetAddress &asInetAddress(sockaddr_in6 &p) { return *reinterpret_cast<InetAddress *>(&p); }
static ZT_ALWAYS_INLINE InetAddress &asInetAddress(sockaddr &p) { return *reinterpret_cast<InetAddress *>(&p); }
static ZT_ALWAYS_INLINE InetAddress &asInetAddress(sockaddr_storage &p) { return *reinterpret_cast<InetAddress *>(&p); }
static ZT_ALWAYS_INLINE const InetAddress &asInetAddress(const sockaddr_in &p) { return *reinterpret_cast<const InetAddress *>(&p); }
static ZT_ALWAYS_INLINE const InetAddress &asInetAddress(const sockaddr_in6 &p) { return *reinterpret_cast<const InetAddress *>(&p); }
static ZT_ALWAYS_INLINE const InetAddress &asInetAddress(const sockaddr &p) { return *reinterpret_cast<const InetAddress *>(&p); }
static ZT_ALWAYS_INLINE const InetAddress &asInetAddress(const sockaddr_storage &p) { return *reinterpret_cast<const InetAddress *>(&p); }
} // namespace ZeroTier

View file

@ -42,23 +42,25 @@ int Locator::marshal(uint8_t data[ZT_LOCATOR_MARSHAL_SIZE_MAX],const bool exclud
if ((_endpointCount > ZT_LOCATOR_MAX_ENDPOINTS)||(_signatureLength > ZT_SIGNATURE_BUFFER_SIZE))
return -1;
Utils::putUInt64(data,(uint64_t)_ts);
Utils::storeBigEndian<int64_t>(data,_ts);
int p = 8;
data[p++] = (uint8_t)(_endpointCount >> 8U);
data[p++] = (uint8_t)_endpointCount;
for(unsigned int i=0;i<_endpointCount;++i) {
int tmp = _at[i].marshal(data + p);
if (tmp < 0)
return -1;
p += tmp;
}
if (_ts > 0) {
data[p++] = (uint8_t)(_endpointCount >> 8U);
data[p++] = (uint8_t)_endpointCount;
for (unsigned int i = 0; i < _endpointCount; ++i) {
int tmp = _at[i].marshal(data + p);
if (tmp < 0)
return -1;
p += tmp;
}
if (!excludeSignature) {
data[p++] = (uint8_t)(_signatureLength >> 8U);
data[p++] = (uint8_t)_signatureLength;
memcpy(data + p,_signature,_signatureLength);
p += (int)_signatureLength;
if (!excludeSignature) {
data[p++] = (uint8_t)(_signatureLength >> 8U);
data[p++] = (uint8_t)_signatureLength;
memcpy(data + p,_signature,_signatureLength);
p += (int)_signatureLength;
}
}
return p;
@ -69,34 +71,38 @@ int Locator::unmarshal(const uint8_t *restrict data,const int len)
if (len <= (8 + 2 + 48))
return -1;
_ts = (int64_t)Utils::readUInt64(data);
_ts = Utils::loadBigEndian<int64_t>(data);
int p = 8;
unsigned int ec = (int)data[p++];
ec <<= 8U;
ec |= data[p++];
if (ec > ZT_LOCATOR_MAX_ENDPOINTS)
return -1;
_endpointCount = ec;
for(int i=0;i<ec;++i) {
int tmp = _at[i].unmarshal(data + p,len - p);
if (tmp < 0)
if (_ts > 0) {
unsigned int ec = (int)data[p++];
ec <<= 8U;
ec |= data[p++];
if (ec > ZT_LOCATOR_MAX_ENDPOINTS)
return -1;
p += tmp;
}
_endpointCount = ec;
for (int i = 0; i < ec; ++i) {
int tmp = _at[i].unmarshal(data + p,len - p);
if (tmp < 0)
return -1;
p += tmp;
}
if ((p + 2) > len)
return -1;
unsigned int sl = data[p++];
sl <<= 8U;
sl |= data[p++];
if (sl > ZT_SIGNATURE_BUFFER_SIZE)
return -1;
_signatureLength = sl;
if ((p + sl) > len)
return -1;
memcpy(_signature,data + p,sl);
p += (int)sl;
if ((p + 2) > len)
return -1;
unsigned int sl = data[p++];
sl <<= 8U;
sl |= data[p++];
if (sl > ZT_SIGNATURE_BUFFER_SIZE)
return -1;
_signatureLength = sl;
if ((p + sl) > len)
return -1;
memcpy(_signature,data + p,sl);
p += (int)sl;
} else {
_ts = 0;
}
return p;
}

View file

@ -42,16 +42,20 @@ Node::Node(void *uPtr, void *tPtr, const struct ZT_Node_Callbacks *callbacks, in
RR(&_RR),
_cb(*callbacks),
_uPtr(uPtr),
_networks(8),
_networks(),
_networksMask(255),
_now(now),
_lastPing(0),
_lastHousekeepingRun(0),
_lastNetworkHousekeepingRun(0),
_lastPathKeepaliveCheck(0),
_online(false)
{
memset(_expectingRepliesToBucketPtr,0,sizeof(_expectingRepliesToBucketPtr));
memset(_expectingRepliesTo,0,sizeof(_expectingRepliesTo));
memset(_lastIdentityVerification,0,sizeof(_lastIdentityVerification));
_networks.resize(256); // _networksMask + 1, must be power of two
memset((void *)_expectingRepliesToBucketPtr,0,sizeof(_expectingRepliesToBucketPtr));
memset((void *)_expectingRepliesTo,0,sizeof(_expectingRepliesTo));
memset((void *)_lastIdentityVerification,0,sizeof(_lastIdentityVerification));
uint64_t idtmp[2];
idtmp[0] = 0; idtmp[1] = 0;
@ -118,7 +122,7 @@ Node::Node(void *uPtr, void *tPtr, const struct ZT_Node_Callbacks *callbacks, in
Node::~Node()
{
{
Mutex::Lock _l(_networks_m);
RWMutex::Lock _l(_networks_m);
_networks.clear(); // destroy all networks before shutdown
}
if (RR->sa) RR->sa->~SelfAwareness();
@ -128,6 +132,11 @@ Node::~Node()
free(RR->rtmem);
}
void Node::shutdown(void *tPtr)
{
RR->topology->saveAll(tPtr);
}
ZT_ResultCode Node::processWirePacket(
void *tptr,
int64_t now,
@ -164,38 +173,33 @@ ZT_ResultCode Node::processVirtualNetworkFrame(
}
}
// This function object is run past every peer every ZT_PEER_PING_PERIOD.
struct _processBackgroundTasks_ping_eachPeer
{
int64_t now;
Node *parent;
void *tPtr;
bool online;
ZT_ALWAYS_INLINE bool operator()(const SharedPtr<Peer> &peer,const bool isRoot)
ZT_ALWAYS_INLINE void operator()(const SharedPtr<Peer> &peer,const bool isRoot)
{
unsigned int v4SendCount = 0,v6SendCount = 0;
peer->ping(tPtr,now,v4SendCount,v6SendCount,isRoot);
peer->ping(tPtr,now,isRoot);
if ((isRoot)&&((now - peer->lastReceive()) <= (ZT_PEER_PING_PERIOD + 5000)))
online = true;
}
};
if (isRoot) {
if ((now - peer->lastReceive()) <= (ZT_PEER_PING_PERIOD + 5000))
online = true;
if (v4SendCount == 0) {
InetAddress try4;
parent->externalPathLookup(tPtr,peer->identity(),AF_INET,try4);
if (try4.ss_family == AF_INET)
peer->sendHELLO(tPtr,-1,try4,now);
}
if (v6SendCount == 0) {
InetAddress try6;
parent->externalPathLookup(tPtr,peer->identity(),AF_INET6,try6);
if (try6.ss_family == AF_INET6)
peer->sendHELLO(tPtr,-1,try6,now);
}
static uint16_t junk = 0;
struct _processBackgroundTasks_path_keepalive
{
int64_t now;
RuntimeEnvironment *RR;
void *tPtr;
ZT_ALWAYS_INLINE void operator()(const SharedPtr<Path> &path)
{
if ((now - path->lastOut()) >= ZT_PATH_KEEPALIVE_PERIOD) {
++junk;
path->send(RR,tPtr,&junk,sizeof(junk),now);
path->sent(now);
}
return true;
}
};
@ -204,14 +208,6 @@ ZT_ResultCode Node::processBackgroundTasks(void *tPtr, int64_t now, volatile int
_now = now;
Mutex::Lock bl(_backgroundTasksLock);
// Initialize these on first call so these things happen just a few seconds after
// startup, since right at startup things are likely to not be ready to communicate
// at all yet.
if (_lastNetworkHousekeepingRun <= 0)
_lastNetworkHousekeepingRun = now - (ZT_NETWORK_HOUSEKEEPING_PERIOD / 3);
if (_lastHousekeepingRun <= 0)
_lastHousekeepingRun = now;
if ((now - _lastPing) >= ZT_PEER_PING_PERIOD) {
_lastPing = now;
try {
@ -236,12 +232,10 @@ ZT_ResultCode Node::processBackgroundTasks(void *tPtr, int64_t now, volatile int
if ((now - _lastNetworkHousekeepingRun) >= ZT_NETWORK_HOUSEKEEPING_PERIOD) {
_lastHousekeepingRun = now;
{
Mutex::Lock l(_networks_m);
Hashtable< uint64_t,SharedPtr<Network> >::Iterator i(_networks);
uint64_t *nwid = (uint64_t *)0;
SharedPtr<Network> *network = (SharedPtr<Network> *)0;
while (i.next(nwid,network)) {
(*network)->doPeriodicTasks(tPtr, now);
RWMutex::RLock l(_networks_m);
for(std::vector< SharedPtr<Network> >::const_iterator i(_networks.begin());i!=_networks.end();++i) {
if ((*i))
(*i)->doPeriodicTasks(tPtr,now);
}
}
}
@ -265,13 +259,22 @@ ZT_ResultCode Node::processBackgroundTasks(void *tPtr, int64_t now, volatile int
_localControllerAuthorizations_m.unlock();
}
RR->topology->doPeriodicTasks(now);
RR->topology->doPeriodicTasks(tPtr, now);
RR->sa->clean(now);
} catch ( ... ) {
return ZT_RESULT_FATAL_ERROR_INTERNAL;
}
}
if ((now - _lastPathKeepaliveCheck) >= ZT_PATH_KEEPALIVE_PERIOD) {
_lastPathKeepaliveCheck = now;
_processBackgroundTasks_path_keepalive pf;
pf.now = now;
pf.RR = RR;
pf.tPtr = tPtr;
RR->topology->eachPath<_processBackgroundTasks_path_keepalive &>(pf);
}
try {
*nextBackgroundTaskDeadline = now + (int64_t)std::max(std::min((unsigned long)ZT_MAX_TIMER_TASK_INTERVAL,RR->sw->doTimerTasks(tPtr, now)), (unsigned long)ZT_MIN_TIMER_TASK_INTERVAL);
} catch ( ... ) {
@ -283,35 +286,68 @@ ZT_ResultCode Node::processBackgroundTasks(void *tPtr, int64_t now, volatile int
ZT_ResultCode Node::join(uint64_t nwid,void *uptr,void *tptr)
{
Mutex::Lock _l(_networks_m);
SharedPtr<Network> &nw = _networks[nwid];
if (!nw)
nw = SharedPtr<Network>(new Network(RR,tptr,nwid,uptr,(const NetworkConfig *)0));
RWMutex::Lock l(_networks_m);
const uint64_t nwidHashed = nwid + (nwid >> 32U);
SharedPtr<Network> *nw = &(_networks[(unsigned long)(nwidHashed & _networksMask)]);
// Enlarge flat hash table of networks until all networks fit without collisions.
if (*nw) {
unsigned long newNetworksSize = (unsigned long)_networks.size();
std::vector< SharedPtr<Network> > newNetworks;
uint64_t newNetworksMask;
std::vector< SharedPtr<Network> >::const_iterator i;
try_larger_network_hashtable:
newNetworksSize <<= 1U; // must remain a power of two
newNetworks.clear();
newNetworks.resize(newNetworksSize);
newNetworksMask = (uint64_t)(newNetworksSize - 1);
for(i=_networks.begin();i!=_networks.end();++i) {
const uint64_t id = (*i)->id();
nw = &(newNetworks[(unsigned long)((id + (id >> 32U)) & newNetworksMask)]);
if (*nw)
goto try_larger_network_hashtable;
*nw = *i;
}
if (newNetworks[(unsigned long)(nwidHashed & newNetworksMask)])
goto try_larger_network_hashtable;
_networks.swap(newNetworks);
_networksMask = newNetworksMask;
nw = &(_networks[(unsigned long)(nwidHashed & newNetworksMask)]);
}
nw->set(new Network(RR,tptr,nwid,uptr,(const NetworkConfig *)0));
return ZT_RESULT_OK;
}
ZT_ResultCode Node::leave(uint64_t nwid,void **uptr,void *tptr)
{
const uint64_t nwidHashed = nwid + (nwid >> 32U);
ZT_VirtualNetworkConfig ctmp;
void **nUserPtr = (void **)0;
{
Mutex::Lock _l(_networks_m);
SharedPtr<Network> *nw = _networks.get(nwid);
RWMutex::RLock l(_networks_m);
SharedPtr<Network> &nw = _networks[(unsigned long)(nwidHashed & _networksMask)];
if (!nw)
return ZT_RESULT_OK;
if (uptr)
*uptr = (*nw)->userPtr();
(*nw)->externalConfig(&ctmp);
(*nw)->destroy();
nUserPtr = (*nw)->userPtr();
*uptr = nw->userPtr();
nw->externalConfig(&ctmp);
nw->destroy();
nUserPtr = nw->userPtr();
}
if (nUserPtr)
RR->node->configureVirtualNetworkPort(tptr,nwid,nUserPtr,ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_DESTROY,&ctmp);
{
Mutex::Lock _l(_networks_m);
_networks.erase(nwid);
RWMutex::Lock _l(_networks_m);
_networks[(unsigned long)(nwidHashed & _networksMask)].zero();
}
uint64_t tmp[2];
@ -433,11 +469,10 @@ ZT_PeerList *Node::peers() const
ZT_VirtualNetworkConfig *Node::networkConfig(uint64_t nwid) const
{
Mutex::Lock _l(_networks_m);
const SharedPtr<Network> *nw = _networks.get(nwid);
SharedPtr<Network> nw(network(nwid));
if (nw) {
ZT_VirtualNetworkConfig *nc = (ZT_VirtualNetworkConfig *)::malloc(sizeof(ZT_VirtualNetworkConfig));
(*nw)->externalConfig(nc);
ZT_VirtualNetworkConfig *const nc = (ZT_VirtualNetworkConfig *)::malloc(sizeof(ZT_VirtualNetworkConfig));
nw->externalConfig(nc);
return nc;
}
return (ZT_VirtualNetworkConfig *)0;
@ -445,30 +480,34 @@ ZT_VirtualNetworkConfig *Node::networkConfig(uint64_t nwid) const
ZT_VirtualNetworkList *Node::networks() const
{
Mutex::Lock _l(_networks_m);
RWMutex::RLock l(_networks_m);
char *buf = (char *)::malloc(sizeof(ZT_VirtualNetworkList) + (sizeof(ZT_VirtualNetworkConfig) * _networks.size()));
unsigned long networkCount = 0;
for(std::vector< SharedPtr<Network> >::const_iterator i(_networks.begin());i!=_networks.end();++i) {
if ((*i))
++networkCount;
}
char *const buf = (char *)::malloc(sizeof(ZT_VirtualNetworkList) + (sizeof(ZT_VirtualNetworkConfig) * networkCount));
if (!buf)
return (ZT_VirtualNetworkList *)0;
ZT_VirtualNetworkList *nl = (ZT_VirtualNetworkList *)buf;
nl->networks = (ZT_VirtualNetworkConfig *)(buf + sizeof(ZT_VirtualNetworkList));
nl->networkCount = 0;
Hashtable< uint64_t,SharedPtr<Network> >::Iterator i(*const_cast< Hashtable< uint64_t,SharedPtr<Network> > *>(&_networks));
uint64_t *k = (uint64_t *)0;
SharedPtr<Network> *v = (SharedPtr<Network> *)0;
while (i.next(k,v))
(*v)->externalConfig(&(nl->networks[nl->networkCount++]));
for(std::vector< SharedPtr<Network> >::const_iterator i(_networks.begin());i!=_networks.end();++i) {
if ((*i))
(*i)->externalConfig(&(nl->networks[nl->networkCount++]));
}
return nl;
}
void Node::setNetworkUserPtr(uint64_t nwid,void *ptr)
{
Mutex::Lock _l(_networks_m);
const SharedPtr<Network> *const nw = _networks.get(nwid);
SharedPtr<Network> nw(network(nwid));
if (nw)
*((*nw)->userPtr()) = ptr;
*(nw->userPtr()) = ptr;
}
void Node::freeQueryResult(void *qr)
@ -524,23 +563,20 @@ void Node::setController(void *networkControllerInstance)
bool Node::shouldUsePathForZeroTierTraffic(void *tPtr,const Address &ztaddr,const int64_t localSocket,const InetAddress &remoteAddress)
{
if (!Path::isAddressValidForPath(remoteAddress))
return false;
{
Mutex::Lock _l(_networks_m);
Hashtable< uint64_t,SharedPtr<Network> >::Iterator i(_networks);
uint64_t *k = (uint64_t *)0;
SharedPtr<Network> *v = (SharedPtr<Network> *)0;
while (i.next(k,v)) {
if ((*v)->hasConfig()) {
for(unsigned int k=0;k<(*v)->config().staticIpCount;++k) {
if ((*v)->config().staticIps[k].containsAddress(remoteAddress))
if (Path::isAddressValidForPath(remoteAddress)) {
RWMutex::RLock l(_networks_m);
for(std::vector< SharedPtr<Network> >::iterator i(_networks.begin());i!=_networks.end();++i) {
if ((*i)) {
for(unsigned int k=0,j=(*i)->config().staticIpCount;k<j;++k) {
if ((*i)->config().staticIps[k].containsAddress(remoteAddress))
return false;
}
}
}
} else {
return false;
}
return ( (_cb.pathCheckFunction) ? (_cb.pathCheckFunction(reinterpret_cast<ZT_Node *>(this),_uPtr,tPtr,ztaddr.toInt(),localSocket,reinterpret_cast<const struct sockaddr_storage *>(&remoteAddress)) != 0) : true);
return ((_cb.pathCheckFunction) ? (_cb.pathCheckFunction(reinterpret_cast<ZT_Node *>(this),_uPtr,tPtr,ztaddr.toInt(),localSocket,reinterpret_cast<const struct sockaddr_storage *>(&remoteAddress)) != 0) : true);
}
bool Node::externalPathLookup(void *tPtr,const Identity &id,int family,InetAddress &addr)
@ -564,6 +600,16 @@ ZT_ResultCode Node::setPhysicalPathConfiguration(const struct sockaddr_storage *
return ZT_RESULT_OK;
}
bool Node::localControllerHasAuthorized(const int64_t now,const uint64_t nwid,const Address &addr) const
{
_localControllerAuthorizations_m.lock();
const int64_t *const at = _localControllerAuthorizations.get(_LocalControllerAuth(nwid,addr));
_localControllerAuthorizations_m.unlock();
if (at)
return ((now - *at) < (ZT_NETWORK_AUTOCONF_DELAY * 3));
return false;
}
void Node::ncSendConfig(uint64_t nwid,uint64_t requestPacketId,const Address &destination,const NetworkConfig &nc,bool sendLegacyFormatConfig)
{
_localControllerAuthorizations_m.lock();
@ -690,9 +736,10 @@ enum ZT_ResultCode ZT_Node_new(ZT_Node **node,void *uptr,void *tptr,const struct
}
}
void ZT_Node_delete(ZT_Node *node)
void ZT_Node_delete(ZT_Node *node,void *tPtr)
{
try {
reinterpret_cast<ZeroTier::Node *>(node)->shutdown(tPtr);
delete (reinterpret_cast<ZeroTier::Node *>(node));
} catch ( ... ) {}
}

View file

@ -51,9 +51,18 @@ class Locator;
class Node : public NetworkController::Sender
{
public:
Node(void *uPtr, void *tPtr, const struct ZT_Node_Callbacks *callbacks, int64_t now);
Node(void *uPtr,void *tPtr,const struct ZT_Node_Callbacks *callbacks,int64_t now);
virtual ~Node();
/**
* Perform any operations that should be done prior to deleting a Node
*
* This is technically optional but recommended.
*
* @param tPtr Thread pointer to pass through to callbacks
*/
void shutdown(void *tPtr);
// 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); }
@ -101,8 +110,22 @@ public:
// Internal functions ------------------------------------------------------
/**
* @return Most recent time value supplied to core via API
*/
ZT_ALWAYS_INLINE int64_t now() const { return _now; }
/**
* Send packet to to the physical wire via callback
*
* @param tPtr Thread pointer
* @param localSocket Local socket or -1 to use all/any
* @param addr Destination address
* @param data Data to send
* @param len Length in bytes
* @param ttl TTL or 0 for default/max
* @return True if send appears successful
*/
ZT_ALWAYS_INLINE bool putPacket(void *tPtr,const int64_t localSocket,const InetAddress &addr,const void *data,unsigned int len,unsigned int ttl = 0)
{
return (_cb.wirePacketSendFunction(
@ -116,6 +139,19 @@ public:
ttl) == 0);
}
/**
* Inject frame into virtual Ethernet tap
*
* @param tPtr Thread pointer
* @param nwid Network ID
* @param nuptr Network-associated user pointer
* @param source Source MAC address
* @param dest Destination MAC address
* @param etherType 16-bit Ethernet type
* @param vlanId Ethernet VLAN ID (currently unused)
* @param data Ethernet frame data
* @param len Ethernet frame length in bytes
*/
ZT_ALWAYS_INLINE void putFrame(void *tPtr,uint64_t nwid,void **nuptr,const MAC &source,const MAC &dest,unsigned int etherType,unsigned int vlanId,const void *data,unsigned int len)
{
_cb.virtualNetworkFrameFunction(
@ -132,30 +168,118 @@ public:
len);
}
/**
* @param nwid Network ID
* @return Network associated with ID
*/
ZT_ALWAYS_INLINE SharedPtr<Network> network(uint64_t nwid) const
{
Mutex::Lock _l(_networks_m);
const SharedPtr<Network> *n = _networks.get(nwid);
if (n)
return *n;
return SharedPtr<Network>();
RWMutex::RLock l(_networks_m);
return _networks[(unsigned long)((nwid + (nwid >> 32U)) & _networksMask)];
}
ZT_ALWAYS_INLINE std::vector<ZT_InterfaceAddress> directPaths() const
/**
* @return Known local interface addresses for this node
*/
ZT_ALWAYS_INLINE std::vector<ZT_InterfaceAddress> localInterfaceAddresses() const
{
Mutex::Lock _l(_localInterfaceAddresses_m);
return _localInterfaceAddresses;
}
/**
* Post an event via external callback
*
* @param tPtr Thread pointer
* @param ev Event object
* @param md Event data or NULL if none
*/
ZT_ALWAYS_INLINE void postEvent(void *tPtr,ZT_Event ev,const void *md = (const void *)0) { _cb.eventCallback(reinterpret_cast<ZT_Node *>(this),_uPtr,tPtr,ev,md); }
/**
* Post network port configuration via external callback
*
* @param tPtr Thread pointer
* @param nwid Network ID
* @param nuptr Network-associated user pointer
* @param op Config operation or event type
* @param nc Network config info
*/
ZT_ALWAYS_INLINE void configureVirtualNetworkPort(void *tPtr,uint64_t nwid,void **nuptr,ZT_VirtualNetworkConfigOperation op,const ZT_VirtualNetworkConfig *nc) { _cb.virtualNetworkConfigFunction(reinterpret_cast<ZT_Node *>(this),_uPtr,tPtr,nwid,nuptr,op,nc); }
/**
* @return True if node appears online
*/
ZT_ALWAYS_INLINE bool online() const { return _online; }
/**
* Get a state object
*
* @param tPtr Thread pointer
* @param type Object type to get
* @param id Object ID
* @param data Data buffer
* @param maxlen Maximum data length
* @return Number of bytes actually read or 0 if not found
*/
ZT_ALWAYS_INLINE int stateObjectGet(void *const tPtr,ZT_StateObjectType type,const uint64_t id[2],void *const data,const unsigned int maxlen) { return _cb.stateGetFunction(reinterpret_cast<ZT_Node *>(this),_uPtr,tPtr,type,id,data,maxlen); }
/**
* Store a state object
*
* @param tPtr Thread pointer
* @param type Object type to get
* @param id Object ID
* @param data Data to store
* @param len Length of data
*/
ZT_ALWAYS_INLINE void stateObjectPut(void *const tPtr,ZT_StateObjectType type,const uint64_t id[2],const void *const data,const unsigned int len) { _cb.statePutFunction(reinterpret_cast<ZT_Node *>(this),_uPtr,tPtr,type,id,data,(int)len); }
/**
* Delete a state object
*
* @param tPtr Thread pointer
* @param type Object type to delete
* @param id Object ID
*/
ZT_ALWAYS_INLINE void stateObjectDelete(void *const tPtr,ZT_StateObjectType type,const uint64_t id[2]) { _cb.statePutFunction(reinterpret_cast<ZT_Node *>(this),_uPtr,tPtr,type,id,(const void *)0,-1); }
/**
* Check whether a path should be used for ZeroTier traffic
*
* This performs internal checks and also calls out to an external callback if one is defined.
*
* @param tPtr Thread pointer
* @param ztaddr ZeroTier address
* @param localSocket Local socket or -1 if unknown
* @param remoteAddress Remote address
* @return True if path should be used
*/
bool shouldUsePathForZeroTierTraffic(void *tPtr,const Address &ztaddr,const int64_t localSocket,const InetAddress &remoteAddress);
/**
* Query callback for a physical address for a peer
*
* @param tPtr Thread pointer
* @param id Full identity of ZeroTier node
* @param family Desired address family or -1 for any
* @param addr Buffer to store address (result paramter)
* @return True if addr was filled with something
*/
bool externalPathLookup(void *tPtr,const Identity &id,int family,InetAddress &addr);
/**
* Set physical path configuration
*
* @param pathNetwork Physical path network/netmask bits (CIDR notation)
* @param pathConfig Path configuration
* @return Return to pass through to external API
*/
ZT_ResultCode setPhysicalPathConfiguration(const struct sockaddr_storage *pathNetwork,const ZT_PhysicalPathConfiguration *pathConfig);
/**
* @return This node's identity
*/
ZT_ALWAYS_INLINE const Identity &identity() const { return _RR.identity; }
/**
@ -212,23 +336,26 @@ public:
return false;
}
/**
* Check whether a local controller has authorized a member on a network
*
* This is used by controllers to avoid needless certificate checks when we already
* know if this has occurred. It's a bit of a hack but saves a massive amount of
* controller CPU. It's easiest to put this here, and it imposes no overhead on
* non-controllers.
*
* @param now Current time
* @param nwid Network ID
* @param addr Member address to check
* @return True if member has been authorized
*/
bool localControllerHasAuthorized(int64_t now,uint64_t nwid,const Address &addr) const;
// Implementation of NetworkController::Sender interface
virtual void ncSendConfig(uint64_t nwid,uint64_t requestPacketId,const Address &destination,const NetworkConfig &nc,bool sendLegacyFormatConfig);
virtual void ncSendRevocation(const Address &destination,const Revocation &rev);
virtual void ncSendError(uint64_t nwid,uint64_t requestPacketId,const Address &destination,NetworkController::ErrorCode errorCode);
inline bool localControllerHasAuthorized(const int64_t now,const uint64_t nwid,const Address &addr) const
{
_localControllerAuthorizations_m.lock();
const int64_t *const at = _localControllerAuthorizations.get(_LocalControllerAuth(nwid,addr));
_localControllerAuthorizations_m.unlock();
if (at)
return ((now - *at) < (ZT_NETWORK_AUTOCONF_DELAY * 3));
return false;
}
inline void setMultipathMode(uint8_t mode) { _multipathMode = mode; }
inline uint8_t getMultipathMode() { return _multipathMode; }
private:
RuntimeEnvironment _RR;
RuntimeEnvironment *RR;
@ -236,11 +363,11 @@ private:
void *_uPtr; // _uptr (lower case) is reserved in Visual Studio :P
// 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];
volatile uint8_t _expectingRepliesToBucketPtr[ZT_EXPECTING_REPLIES_BUCKET_MASK1 + 1];
volatile 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()
int64_t _lastIdentityVerification[16384];
volatile int64_t _lastIdentityVerification[16384];
/* Map that remembers if we have recently sent a network config to someone
* querying us as a controller. This is an optimization to allow network
@ -256,21 +383,25 @@ private:
ZT_ALWAYS_INLINE bool operator!=(const _LocalControllerAuth &a) const { return ((a.nwid != nwid)||(a.address != address)); }
};
Hashtable< _LocalControllerAuth,int64_t > _localControllerAuthorizations;
Hashtable< uint64_t,SharedPtr<Network> > _networks;
// Networks are stored in a flat hash table that is resized on any network ID collision. This makes
// network lookup by network ID a few bitwise ops and an array index.
std::vector< SharedPtr<Network> > _networks;
uint64_t _networksMask;
std::vector< ZT_InterfaceAddress > _localInterfaceAddresses;
Mutex _localControllerAuthorizations_m;
Mutex _networks_m;
RWMutex _networks_m;
Mutex _localInterfaceAddresses_m;
Mutex _backgroundTasksLock;
uint8_t _multipathMode;
volatile int64_t _now;
int64_t _lastPing;
int64_t _lastHousekeepingRun;
int64_t _lastNetworkHousekeepingRun;
bool _online;
volatile int64_t _lastPing;
volatile int64_t _lastHousekeepingRun;
volatile int64_t _lastNetworkHousekeepingRun;
volatile int64_t _lastPathKeepaliveCheck;
volatile bool _online;
};
} // namespace ZeroTier

View file

@ -33,6 +33,15 @@
#endif
#endif
#if !defined(__GCC__) && !defined (__clang__) && !defined(__INTEL_COMPILER)
#define ZT_PACKED_STRUCT(D) __pragma(pack(push,1)) D __pragma(pack(pop))
#pragma warning(disable : 4290)
#pragma warning(disable : 4996)
#pragma warning(disable : 4101)
#else
#define ZT_PACKED_STRUCT(D) D __attribute__((packed))
#endif
#if defined(_WIN32) || defined(_WIN64)
#ifndef __WINDOWS__
#define __WINDOWS__
@ -42,14 +51,6 @@
#endif
#undef __UNIX_LIKE__
#undef __BSD__
#if !defined(__GNUC__) && !defined (__clang__) && !defined(__INTEL_COMPILER)
#define ZT_PACKED_STRUCT(D) __pragma(pack(push,1)) D __pragma(pack(pop))
#pragma warning(disable : 4290)
#pragma warning(disable : 4996)
#pragma warning(disable : 4101)
#else
#define ZT_PACKED_STRUCT(D) D __attribute__((packed))
#endif
#include <WinSock2.h>
#include <Windows.h>
#endif

View file

@ -237,17 +237,7 @@
/**
* Signed locator for this node
*/
#define ZT_PROTO_NODE_META_LOCATOR "L"
/**
* Dictionary mapping identity hash to timestamp to request newer locators for other nodes if known
*/
#define ZT_PROTO_NODE_META_REFRESH_LOCATORS_IF_NEWER "lt"
/**
* Dictionary mapping identity hash to locator to supply newer revisions of requested locators
*/
#define ZT_PROTO_NODE_META_REFRESH_LOCATORS "lr"
#define ZT_PROTO_NODE_META_LOCATOR "l"
/**
* Ephemeral C25519 public key
@ -523,16 +513,17 @@ public:
* [<[...] additional addresses to look up>
*
* OK response payload:
* <[...] binary serialized identity>
* [<[...] additional binary serialized identities>]
*
* If querying a cluster, duplicate OK responses may occasionally occur.
* These must be tolerated, which is easy since they'll have info you
* already have.
* <[...] identity>
* <[...] locator>
* [... additional identity/locator pairs]
*
* If the address is not found, no response is generated. The semantics
* of WHOIS is similar to ARP and NDP in that persistent retrying can
* be performed.
*
* It is possible for an identity but a null/empty locator to be returned
* if no locator is known for a node. Older versions will also send no
* locator field at all.
*/
VERB_WHOIS = 0x04,

View file

@ -97,14 +97,14 @@ public:
*
* @param t Time of send
*/
ZT_ALWAYS_INLINE void sent(const uint64_t t) { _lastOut = t; }
ZT_ALWAYS_INLINE void sent(const int64_t t) { _lastOut = t; }
/**
* Called when a packet is received from this remote path, regardless of content
*
* @param t Time of receive
*/
ZT_ALWAYS_INLINE void received(const uint64_t t) { _lastIn = t; }
ZT_ALWAYS_INLINE void received(const int64_t t) { _lastIn = t; }
/**
* Check path aliveness

View file

@ -21,6 +21,8 @@
#include "Trace.hpp"
#include "InetAddress.hpp"
#include <set>
namespace ZeroTier {
struct _PathPriorityComparisonOperator
@ -31,25 +33,28 @@ struct _PathPriorityComparisonOperator
}
};
Peer::Peer(const RuntimeEnvironment *renv,const Identity &myIdentity,const Identity &peerIdentity) :
Peer::Peer(const RuntimeEnvironment *renv) :
RR(renv),
_lastReceive(0),
_lastWhoisRequestReceived(0),
_lastEchoRequestReceived(0),
_lastPushDirectPathsReceived(0),
_lastPushDirectPathsSent(0),
_lastAttemptedP2PInit(0),
_lastTriedStaticPath(0),
_lastPrioritizedPaths(0),
_latency(0xffff),
_alivePathCount(0),
_id(peerIdentity),
_vProto(0),
_vMajor(0),
_vMinor(0),
_vRevision(0)
_alivePathCount(0)
{
if (!myIdentity.agree(peerIdentity,_key))
throw ZT_EXCEPTION_INVALID_ARGUMENT;
}
bool Peer::init(const Identity &myIdentity,const Identity &peerIdentity)
{
_id = peerIdentity;
_vProto = 0;
_vMajor = 0;
_vMinor = 0;
_vRevision = 0;
return myIdentity.agree(peerIdentity,_key);
}
void Peer::received(
@ -67,17 +72,17 @@ void Peer::received(
_lastReceive = now;
if (hops == 0) {
_paths_l.rlock();
for(int i=0;i<(int)_alivePathCount; ++i) {
_lock.rlock();
for(int i=0;i<(int)_alivePathCount;++i) {
if (_paths[i] == path) {
_paths_l.runlock();
_lock.runlock();
goto path_check_done;
}
}
_paths_l.runlock();
_lock.runlock();
if (verb == Packet::VERB_OK) {
RWMutex::Lock l(_paths_l);
RWMutex::Lock l(_lock);
int64_t lastReceiveTimeMax = 0;
int lastReceiveTimeMaxAt = 0;
@ -105,6 +110,7 @@ void Peer::received(
_lastPrioritizedPaths = now;
_paths[lastReceiveTimeMaxAt] = path;
_bootstrap = path->address();
_prioritizePaths(now);
RR->t->peerLearnedNewPath(tPtr,networkId,*this,path,packetId);
} else {
@ -117,72 +123,84 @@ void Peer::received(
}
path_check_done:
const int64_t sinceLastPush = now - _lastPushDirectPathsSent;
if (sinceLastPush >= ((hops == 0) ? ZT_DIRECT_PATH_PUSH_INTERVAL_HAVEPATH : ZT_DIRECT_PATH_PUSH_INTERVAL)) {
_lastPushDirectPathsReceived = now;
}
const int64_t sinceLastP2PInit = now - _lastAttemptedP2PInit;
if (sinceLastP2PInit >= ((hops == 0) ? ZT_DIRECT_PATH_PUSH_INTERVAL_HAVEPATH : ZT_DIRECT_PATH_PUSH_INTERVAL)) {
_lastAttemptedP2PInit = now;
/*
const int64_t sinceLastPush = now - _lastDirectPathPushSent;
if (sinceLastPush >= ((hops == 0) ? ZT_DIRECT_PATH_PUSH_INTERVAL_HAVEPATH : ZT_DIRECT_PATH_PUSH_INTERVAL)) {
_lastDirectPathPushSent = now;
std::vector<ZT_InterfaceAddress> pathsToPush(RR->node->directPaths());
if (pathsToPush.size() > 0) {
std::vector<ZT_InterfaceAddress>::const_iterator p(pathsToPush.begin());
while (p != pathsToPush.end()) {
ScopedPtr<Packet> outp(new Packet(_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;
uint8_t addressLength = 6;
unsigned int ipLength = 4;
const void *rawIpData;
const void *rawIpPort;
switch(p->address.ss_family) {
case AF_INET:
rawIpData = &(reinterpret_cast<const struct sockaddr_in *>(&(p->address))->sin_addr.s_addr);
rawIpPort = &(reinterpret_cast<const struct sockaddr_in *>(&(p->address))->sin_port);
break;
case AF_INET6:
rawIpData = reinterpret_cast<const struct sockaddr_in6 *>(&(p->address))->sin6_addr.s6_addr;
rawIpPort = &(reinterpret_cast<const struct sockaddr_in6 *>(&(p->address))->sin6_port);
addressType = 6;
addressLength = 18;
ipLength = 16;
break;
default: // we currently only push IP addresses
++p;
continue;
}
InetAddress addr;
if (_bootstrap)
sendHELLO(tPtr,-1,_bootstrap,now);
if (RR->node->externalPathLookup(tPtr,_id,-1,addr)) {
if (RR->node->shouldUsePathForZeroTierTraffic(tPtr,_id.address(),-1,addr))
sendHELLO(tPtr,-1,addr,now);
}
outp->append((uint8_t)0); // no flags
outp->append((uint16_t)0); // no extensions
outp->append(addressType);
outp->append(addressLength);
outp->append(rawIpData,ipLength);
outp->append(rawIpPort,2);
std::vector<ZT_InterfaceAddress> localInterfaceAddresses(RR->node->localInterfaceAddresses());
std::multimap<unsigned long,InetAddress> detectedAddresses(RR->sa->externalAddresses(now));
std::set<InetAddress> addrs;
for(std::vector<ZT_InterfaceAddress>::const_iterator i(localInterfaceAddresses.begin());i!=localInterfaceAddresses.end();++i)
addrs.insert(asInetAddress(i->address));
for(std::multimap<unsigned long,InetAddress>::const_reverse_iterator i(detectedAddresses.rbegin());i!=detectedAddresses.rend();++i) {
if (i->first <= 1)
break;
if (addrs.count(i->second) == 0) {
addrs.insert(i->second);
break;
}
}
++count;
++p;
}
if (count) {
outp->setAt(ZT_PACKET_IDX_PAYLOAD,(uint16_t)count);
outp->compress();
outp->armor(_key,true);
path->send(RR,tPtr,outp->data(),outp->size(),now);
if (!addrs.empty()) {
ScopedPtr<Packet> outp(new Packet(_id.address(),RR->identity.address(),Packet::VERB_PUSH_DIRECT_PATHS));
outp->addSize(2); // leave room for count
unsigned int count = 0;
for(std::set<InetAddress>::iterator a(addrs.begin());a!=addrs.end();++a) {
uint8_t addressType = 4;
uint8_t addressLength = 6;
unsigned int ipLength = 4;
const void *rawIpData = (const void *)0;
uint16_t port = 0;
switch(a->ss_family) {
case AF_INET:
rawIpData = &(reinterpret_cast<const sockaddr_in *>(&(*a))->sin_addr.s_addr);
port = Utils::ntoh((uint16_t)reinterpret_cast<const sockaddr_in *>(&(*a))->sin_port);
break;
case AF_INET6:
rawIpData = reinterpret_cast<const sockaddr_in6 *>(&(*a))->sin6_addr.s6_addr;
port = Utils::ntoh((uint16_t)reinterpret_cast<const sockaddr_in6 *>(&(*a))->sin6_port);
addressType = 6;
addressLength = 18;
ipLength = 16;
break;
default:
continue;
}
outp->append((uint8_t)0); // no flags
outp->append((uint16_t)0); // no extensions
outp->append(addressType);
outp->append(addressLength);
outp->append(rawIpData,ipLength);
outp->append(port);
++count;
if (outp->size() >= (ZT_PROTO_MAX_PACKET_LENGTH - 32))
break;
}
if (count > 0) {
outp->setAt(ZT_PACKET_IDX_PAYLOAD,(uint16_t)count);
outp->compress();
outp->armor(_key,true);
path->send(RR,tPtr,outp->data(),outp->size(),now);
}
}
}
*/
}
bool Peer::shouldTryPath(void *tPtr,int64_t now,const SharedPtr<Peer> &suggestedBy,const InetAddress &addr) const
{
int maxHaveScope = -1;
{
RWMutex::RLock l(_paths_l);
RWMutex::RLock l(_lock);
for (unsigned int i = 0; i < _alivePathCount; ++i) {
if (_paths[i]) {
if (_paths[i]->address().ipsEqual2(addr))
@ -219,9 +237,9 @@ void Peer::sendHELLO(void *tPtr,const int64_t localSocket,const InetAddress &atA
}
}
void Peer::ping(void *tPtr,int64_t now,unsigned int &v4SendCount,unsigned int &v6SendCount,const bool pingAllAddressTypes)
void Peer::ping(void *tPtr,int64_t now,const bool pingAllAddressTypes)
{
RWMutex::RLock l(_paths_l);
RWMutex::RLock l(_lock);
_lastPrioritizedPaths = now;
_prioritizePaths(now);
@ -230,30 +248,29 @@ void Peer::ping(void *tPtr,int64_t now,unsigned int &v4SendCount,unsigned int &v
for (unsigned int i = 0; i < _alivePathCount; ++i) {
sendHELLO(tPtr,_paths[i]->localSocket(),_paths[i]->address(),now);
_paths[i]->sent(now);
if (_paths[i]->address().isV4())
++v4SendCount;
else if (_paths[i]->address().isV6())
++v6SendCount;
if (!pingAllAddressTypes)
break;
return;
}
} else {
SharedPtr<Peer> r(RR->topology->root());
if (r) {
SharedPtr<Path> rp(r->path(now));
if (rp) {
sendHELLO(tPtr,rp->localSocket(),rp->address(),now);
rp->sent(now);
}
return;
}
if (_bootstrap)
sendHELLO(tPtr,-1,_bootstrap,now);
SharedPtr<Peer> r(RR->topology->root());
if (r) {
SharedPtr<Path> rp(r->path(now));
if (rp) {
sendHELLO(tPtr,rp->localSocket(),rp->address(),now);
rp->sent(now);
return;
}
}
}
void Peer::resetWithinScope(void *tPtr,InetAddress::IpScope scope,int inetAddressFamily,int64_t now)
{
RWMutex::RLock l(_paths_l);
RWMutex::RLock l(_lock);
for(unsigned int i=0; i < _alivePathCount; ++i) {
if ((_paths[i])&&((_paths[i]->address().ss_family == inetAddressFamily)&&(_paths[i]->address().ipScope() == scope))) {
sendHELLO(tPtr,_paths[i]->localSocket(),_paths[i]->address(),now);
@ -278,23 +295,23 @@ bool Peer::sendDirect(void *tPtr,const void *data,const unsigned int len,const i
{
if ((now - _lastPrioritizedPaths) > ZT_PEER_PRIORITIZE_PATHS_INTERVAL) {
_lastPrioritizedPaths = now;
_paths_l.lock();
_lock.lock();
_prioritizePaths(now);
if (_alivePathCount == 0) {
_paths_l.unlock();
_lock.unlock();
return false;
}
const bool r = _paths[0]->send(RR,tPtr,data,len,now);
_paths_l.unlock();
_lock.unlock();
return r;
} else {
_paths_l.rlock();
_lock.rlock();
if (_alivePathCount == 0) {
_paths_l.runlock();
_lock.runlock();
return false;
}
const bool r = _paths[0]->send(RR,tPtr,data,len,now);
_paths_l.runlock();
_lock.runlock();
return r;
}
}
@ -303,13 +320,13 @@ SharedPtr<Path> Peer::path(const int64_t now)
{
if ((now - _lastPrioritizedPaths) > ZT_PEER_PRIORITIZE_PATHS_INTERVAL) {
_lastPrioritizedPaths = now;
RWMutex::Lock l(_paths_l);
RWMutex::Lock l(_lock);
_prioritizePaths(now);
if (_alivePathCount == 0)
return SharedPtr<Path>();
return _paths[0];
} else {
RWMutex::RLock l(_paths_l);
RWMutex::RLock l(_lock);
if (_alivePathCount == 0)
return SharedPtr<Path>();
return _paths[0];
@ -318,14 +335,104 @@ SharedPtr<Path> Peer::path(const int64_t now)
void Peer::getAllPaths(std::vector< SharedPtr<Path> > &paths)
{
RWMutex::RLock l(_paths_l);
RWMutex::RLock l(_lock);
paths.clear();
paths.assign(_paths,_paths + _alivePathCount);
}
void Peer::save(void *tPtr) const
{
uint8_t *const buf = (uint8_t *)malloc(ZT_PEER_MARSHAL_SIZE_MAX);
if (!buf) return;
_lock.rlock();
const int len = marshal(buf);
_lock.runlock();
if (len > 0) {
uint64_t id[2];
id[0] = _id.address().toInt();
id[1] = 0;
RR->node->stateObjectPut(tPtr,ZT_STATE_OBJECT_PEER,id,buf,(unsigned int)len);
}
free(buf);
}
int Peer::marshal(uint8_t data[ZT_PEER_MARSHAL_SIZE_MAX]) const
{
RWMutex::RLock l(_lock);
data[0] = 0; // serialized peer version
int s = _id.marshal(data + 1,false);
if (s <= 0)
return s;
int p = 1 + s;
s = _locator.marshal(data + p);
if (s <= 0)
return s;
p += s;
s = _bootstrap.marshal(data + p);
if (s <= 0)
return s;
p += s;
Utils::storeBigEndian(data + p,(uint16_t)_vProto);
p += 2;
Utils::storeBigEndian(data + p,(uint16_t)_vMajor);
p += 2;
Utils::storeBigEndian(data + p,(uint16_t)_vMinor);
p += 2;
Utils::storeBigEndian(data + p,(uint16_t)_vRevision);
p += 2;
data[p++] = 0;
data[p++] = 0;
return p;
}
int Peer::unmarshal(const uint8_t *restrict data,const int len)
{
RWMutex::Lock l(_lock);
if ((len <= 1)||(data[0] != 0))
return -1;
int s = _id.unmarshal(data + 1,len - 1);
if (s <= 0)
return s;
int p = 1 + s;
s = _locator.unmarshal(data + p,len - p);
if (s <= 0)
return s;
p += s;
s = _bootstrap.unmarshal(data + p,len - p);
if (s <= 0)
return s;
p += s;
if ((p + 10) > len)
return -1;
_vProto = Utils::loadBigEndian<uint16_t>(data + p);
p += 2;
_vMajor = Utils::loadBigEndian<uint16_t>(data + p);
p += 2;
_vMinor = Utils::loadBigEndian<uint16_t>(data + p);
p += 2;
_vRevision = Utils::loadBigEndian<uint16_t>(data + p);
p += 2;
p += 2 + (int)Utils::loadBigEndian<uint16_t>(data + p);
if (p > len)
return -1;
return p;
}
void Peer::_prioritizePaths(const int64_t now)
{
// assumes _paths_l is locked for writing
// assumes _lock is locked for writing
std::sort(_paths,_paths + ZT_MAX_PEER_NETWORK_PATHS,_PathPriorityComparisonOperator());
for(int i=0;i<ZT_MAX_PEER_NETWORK_PATHS;++i) {

View file

@ -27,33 +27,49 @@
#include "AtomicCounter.hpp"
#include "Hashtable.hpp"
#include "Mutex.hpp"
#include "Locator.hpp"
#include <vector>
// version, identity, locator, bootstrap, version info, length of any additional fields
#define ZT_PEER_MARSHAL_SIZE_MAX (1 + ZT_IDENTITY_MARSHAL_SIZE_MAX + ZT_LOCATOR_MARSHAL_SIZE_MAX + ZT_INETADDRESS_MARSHAL_SIZE_MAX + (2*4) + 2)
namespace ZeroTier {
class Topology;
/**
* Peer on P2P Network (virtual layer 1)
*/
class Peer
{
friend class SharedPtr<Peer>;
friend class Topology;
private:
ZT_ALWAYS_INLINE Peer() {}
public:
/**
* Create an uninitialized peer
*
* The peer will need to be initialized with init() or unmarshal() before
* it can be used.
*
* @param renv Runtime environment
*/
Peer(const RuntimeEnvironment *renv);
ZT_ALWAYS_INLINE ~Peer() { Utils::burn(_key,sizeof(_key)); }
/**
* Construct a new peer
* Initialize peer with an identity
*
* @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
* @param myIdentity This node's identity including secret key
* @param peerIdentity The peer's identity
* @return True if initialization was succcesful
*/
Peer(const RuntimeEnvironment *renv,const Identity &myIdentity,const Identity &peerIdentity);
bool init(const Identity &myIdentity,const Identity &peerIdentity);
/**
* @return This peer's ZT address (short for identity().address())
@ -118,11 +134,9 @@ public:
*
* @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
* @param now Current time
* @param v4SendCount Number of IPv4 packets sent (result parameter)
* @param v6SendCount Number of IPv6 packets sent (result parameter)
* @param pingAllAddressTypes If true, try to keep a link up for each address type/family
*/
void ping(void *tPtr,int64_t now,unsigned int &v4SendCount,unsigned int &v6SendCount,bool pingAllAddressTypes);
void ping(void *tPtr,int64_t now,bool pingAllAddressTypes);
/**
* Reset paths within a given IP scope and address family
@ -139,6 +153,15 @@ public:
*/
void resetWithinScope(void *tPtr,InetAddress::IpScope scope,int inetAddressFamily,int64_t now);
/**
* Update peer latency information
*
* This is called from packet parsing code.
*
* @param l New latency measurment (in milliseconds)
*/
void updateLatency(unsigned int l);
/**
* @return Time of last receive of anything, whether direct or relayed
*/
@ -154,15 +177,6 @@ public:
*/
ZT_ALWAYS_INLINE unsigned int latency() const { return _latency; }
/**
* Update peer latency information
*
* This is called from packet parsing code.
*
* @param l New latency measurment (in milliseconds)
*/
void updateLatency(const unsigned int l);
/**
* @return 256-bit secret symmetric encryption key
*/
@ -226,18 +240,6 @@ public:
return false;
}
/**
* Rate limit gate for trying externally defined or static path
*/
ZT_ALWAYS_INLINE bool rateGateTryStaticPath(const int64_t now)
{
if ((now - _lastTriedStaticPath) >= ZT_PEER_PING_PERIOD) {
_lastTriedStaticPath = now;
return true;
}
return false;
}
/**
* Send directly if a direct path exists
*
@ -247,7 +249,7 @@ public:
* @param now Current time
* @return True if packet appears to have been sent, false if no path or send failed
*/
bool sendDirect(void *tPtr,const void *data,unsigned int len,const int64_t now);
bool sendDirect(void *tPtr,const void *data,unsigned int len,int64_t now);
/**
* @return Current best path
@ -261,6 +263,17 @@ public:
*/
void getAllPaths(std::vector< SharedPtr<Path> > &paths);
/**
* Save the latest version of this peer to the data store
*/
void save(void *tPtr) const;
// NOTE: peer marshal/unmarshal only saves/restores the identity, locator, most
// recent bootstrap address, and version information.
static ZT_ALWAYS_INLINE int marshalSizeMax() { return ZT_PEER_MARSHAL_SIZE_MAX; }
int marshal(uint8_t data[ZT_PEER_MARSHAL_SIZE_MAX]) const;
int unmarshal(const uint8_t *restrict data,int len);
private:
void _prioritizePaths(int64_t now);
@ -272,18 +285,21 @@ private:
volatile int64_t _lastWhoisRequestReceived;
volatile int64_t _lastEchoRequestReceived;
volatile int64_t _lastPushDirectPathsReceived;
volatile int64_t _lastPushDirectPathsSent;
volatile int64_t _lastAttemptedP2PInit;
volatile int64_t _lastTriedStaticPath;
volatile int64_t _lastPrioritizedPaths;
volatile unsigned int _latency;
AtomicCounter __refCount;
RWMutex _lock; // locks _alivePathCount, _paths, _locator, and _bootstrap.
unsigned int _alivePathCount;
SharedPtr<Path> _paths[ZT_MAX_PEER_NETWORK_PATHS];
RWMutex _paths_l;
Identity _id;
Locator _locator;
InetAddress _bootstrap;
uint16_t _vProto;
uint16_t _vMajor;

View file

@ -19,9 +19,7 @@
#include "Constants.hpp"
#include "SelfAwareness.hpp"
#include "RuntimeEnvironment.hpp"
#include "Node.hpp"
#include "Topology.hpp"
#include "Packet.hpp"
#include "Peer.hpp"
#include "Switch.hpp"
#include "Trace.hpp"
@ -34,20 +32,16 @@ namespace ZeroTier {
class _ResetWithinScope
{
public:
inline _ResetWithinScope(void *tPtr,int64_t now,int inetAddressFamily,InetAddress::IpScope scope) :
ZT_ALWAYS_INLINE _ResetWithinScope(void *tPtr,int64_t now,int inetAddressFamily,InetAddress::IpScope scope) :
_now(now),
_tPtr(tPtr),
_family(inetAddressFamily),
_scope(scope) {}
inline bool operator()(const SharedPtr<Peer> &p)
{
p->resetWithinScope(_tPtr,_scope,_family,_now);
return true;
}
ZT_ALWAYS_INLINE void operator()(const SharedPtr<Peer> &p) { p->resetWithinScope(_tPtr,_scope,_family,_now); }
private:
uint64_t _now;
int64_t _now;
void *_tPtr;
int _family;
InetAddress::IpScope _scope;
@ -55,7 +49,13 @@ private:
SelfAwareness::SelfAwareness(const RuntimeEnvironment *renv) :
RR(renv),
_phy(128) {}
_phy(256)
{
}
SelfAwareness::~SelfAwareness()
{
}
void SelfAwareness::iam(void *tPtr,const Address &reporter,const int64_t receivedOnLocalSocket,const InetAddress &reporterPhysicalAddress,const InetAddress &myPhysicalAddress,bool trusted,int64_t now)
{
@ -64,7 +64,7 @@ void SelfAwareness::iam(void *tPtr,const Address &reporter,const int64_t receive
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);
Mutex::Lock l(_phy_l);
PhySurfaceEntry &entry = _phy[PhySurfaceKey(reporter,receivedOnLocalSocket,reporterPhysicalAddress,scope)];
if ( (trusted) && ((now - entry.ts) < ZT_SELFAWARENESS_ENTRY_TIMEOUT) && (!entry.mySurface.ipsEqual(myPhysicalAddress)) ) {
@ -101,7 +101,7 @@ void SelfAwareness::iam(void *tPtr,const Address &reporter,const int64_t receive
void SelfAwareness::clean(int64_t now)
{
Mutex::Lock _l(_phy_m);
Mutex::Lock l(_phy_l);
Hashtable< PhySurfaceKey,PhySurfaceEntry >::Iterator i(_phy);
PhySurfaceKey *k = (PhySurfaceKey *)0;
PhySurfaceEntry *e = (PhySurfaceEntry *)0;
@ -111,4 +111,26 @@ void SelfAwareness::clean(int64_t now)
}
}
std::multimap<unsigned long,InetAddress> SelfAwareness::externalAddresses(const int64_t now) const
{
Hashtable<InetAddress,unsigned long> counts;
{
Mutex::Lock l(_phy_l);
Hashtable<PhySurfaceKey,PhySurfaceEntry>::Iterator i(const_cast<SelfAwareness *>(this)->_phy);
PhySurfaceKey *k = (PhySurfaceKey *)0;
PhySurfaceEntry *e = (PhySurfaceEntry *)0;
while (i.next(k,e)) {
if ((now - e->ts) < ZT_SELFAWARENESS_ENTRY_TIMEOUT)
++counts[e->mySurface];
}
}
std::multimap<unsigned long,InetAddress> r;
Hashtable<InetAddress,unsigned long>::Iterator i(counts);
InetAddress *k = (InetAddress *)0;
unsigned long *c = (unsigned long *)0;
while (i.next(k,c))
r.insert(std::pair<unsigned long,InetAddress>(*c,*k));
return r;
}
} // namespace ZeroTier

View file

@ -20,6 +20,8 @@
#include "Address.hpp"
#include "Mutex.hpp"
#include <map>
namespace ZeroTier {
class RuntimeEnvironment;
@ -30,10 +32,11 @@ class RuntimeEnvironment;
class SelfAwareness
{
public:
SelfAwareness(const RuntimeEnvironment *renv);
explicit SelfAwareness(const RuntimeEnvironment *renv);
~SelfAwareness();
/**
* Called when a trusted remote peer informs us of our external network address
* Called when a 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
@ -42,7 +45,7 @@ public:
* @param trusted True if this peer is trusted as an authority to inform us of external address changes
* @param now Current time
*/
void iam(void *tPtr,const Address &reporter,const int64_t receivedOnLocalSocket,const InetAddress &reporterPhysicalAddress,const InetAddress &myPhysicalAddress,bool trusted,int64_t now);
void iam(void *tPtr,const Address &reporter,int64_t receivedOnLocalSocket,const InetAddress &reporterPhysicalAddress,const InetAddress &myPhysicalAddress,bool trusted,int64_t now);
/**
* Clean up database periodically
@ -51,6 +54,14 @@ public:
*/
void clean(int64_t now);
/**
* Get external address consensus, which is the statistical "mode" of external addresses.
*
* @param now Current time
* @return Map of count to IP/port representing how many endpoints reported each address
*/
std::multimap<unsigned long,InetAddress> externalAddresses(int64_t now) const;
private:
struct PhySurfaceKey
{
@ -59,13 +70,13 @@ private:
InetAddress reporterPhysicalAddress;
InetAddress::IpScope scope;
inline PhySurfaceKey() : reporter(),scope(InetAddress::IP_SCOPE_NONE) {}
inline PhySurfaceKey(const Address &r,const int64_t rol,const InetAddress &ra,InetAddress::IpScope s) : reporter(r),receivedOnLocalSocket(rol),reporterPhysicalAddress(ra),scope(s) {}
ZT_ALWAYS_INLINE PhySurfaceKey() {}
ZT_ALWAYS_INLINE PhySurfaceKey(const Address &r,const int64_t rol,const InetAddress &ra,InetAddress::IpScope s) : reporter(r),receivedOnLocalSocket(rol),reporterPhysicalAddress(ra),scope(s) {}
inline unsigned long hashCode() const { return ((unsigned long)reporter.toInt() + (unsigned long)scope); }
ZT_ALWAYS_INLINE unsigned long hashCode() const { return ((unsigned long)reporter.toInt() + (unsigned long)receivedOnLocalSocket + (unsigned long)scope); }
inline bool operator==(const PhySurfaceKey &k) const { return ((reporter == k.reporter)&&(receivedOnLocalSocket == k.receivedOnLocalSocket)&&(reporterPhysicalAddress == k.reporterPhysicalAddress)&&(scope == k.scope)); }
inline bool operator!=(const PhySurfaceKey &k) const { return (!(*this == k)); }
ZT_ALWAYS_INLINE bool operator==(const PhySurfaceKey &k) const { return ((reporter == k.reporter)&&(receivedOnLocalSocket == k.receivedOnLocalSocket)&&(reporterPhysicalAddress == k.reporterPhysicalAddress)&&(scope == k.scope)); }
ZT_ALWAYS_INLINE bool operator!=(const PhySurfaceKey &k) const { return (!(*this == k)); }
};
struct PhySurfaceEntry
{
@ -73,14 +84,13 @@ private:
uint64_t ts;
bool trusted;
inline PhySurfaceEntry() : mySurface(),ts(0),trusted(false) {}
inline PhySurfaceEntry(const InetAddress &a,const uint64_t t) : mySurface(a),ts(t),trusted(false) {}
ZT_ALWAYS_INLINE PhySurfaceEntry() : mySurface(),ts(0),trusted(false) {}
ZT_ALWAYS_INLINE PhySurfaceEntry(const InetAddress &a,const uint64_t t) : mySurface(a),ts(t),trusted(false) {}
};
const RuntimeEnvironment *RR;
Hashtable< PhySurfaceKey,PhySurfaceEntry > _phy;
Mutex _phy_m;
Mutex _phy_l;
};
} // namespace ZeroTier

View file

@ -579,34 +579,20 @@ unsigned long Switch::doTimerTasks(void *tPtr,int64_t now)
bool Switch::_trySend(void *tPtr,Packet &packet,bool encrypt)
{
SharedPtr<Path> viaPath;
const int64_t now = RR->node->now();
const Address destination(packet.destination());
const SharedPtr<Peer> peer(RR->topology->get(destination));
const SharedPtr<Peer> peer(RR->topology->get(packet.destination()));
SharedPtr<Path> viaPath;
if (peer) {
viaPath = peer->path(now);
if (!viaPath) {
if (peer->rateGateTryStaticPath(now)) {
InetAddress tryAddr;
bool gotPath = RR->node->externalPathLookup(tPtr,peer->identity(),AF_INET6,tryAddr);
if ((gotPath)&&(tryAddr)) {
peer->sendHELLO(tPtr,-1,tryAddr,now);
} else {
gotPath = RR->node->externalPathLookup(tPtr,peer->identity(),AF_INET,tryAddr);
if ((gotPath)&&(tryAddr))
peer->sendHELLO(tPtr,-1,tryAddr,now);
}
}
const SharedPtr<Peer> relay(RR->topology->root());
if (relay) {
viaPath = relay->path(now);
if (!viaPath)
return false;
} else {
return false;
}
return false;
}
} else {
return false;

View file

@ -44,6 +44,19 @@ Topology::~Topology()
{
}
SharedPtr<Peer> Topology::add(void *tPtr,const SharedPtr<Peer> &peer)
{
RWMutex::Lock _l(_peers_l);
SharedPtr<Peer> &hp = _peers[peer->address()];
if (hp)
return hp;
_loadCached(tPtr,peer->address(),hp);
if (hp)
return hp;
hp = peer;
return peer;
}
void Topology::getAllPeers(std::vector< SharedPtr<Peer> > &allPeers) const
{
RWMutex::RLock l(_peers_l);
@ -97,8 +110,10 @@ void Topology::addRoot(const Identity &id)
std::pair< std::set<Identity>::iterator,bool > ir(_roots.insert(id));
if (ir.second) {
SharedPtr<Peer> &p = _peers[id.address()];
if (!p)
p.set(new Peer(RR,_myIdentity,id));
if (!p) {
p.set(new Peer(RR));
p->init(_myIdentity,id);
}
_rootPeers.push_back(p);
}
}
@ -126,7 +141,7 @@ void Topology::rankRoots(const int64_t now)
std::sort(_rootPeers.begin(),_rootPeers.end(),_RootSortComparisonOperator(now));
}
void Topology::doPeriodicTasks(const int64_t now)
void Topology::doPeriodicTasks(void *tPtr,const int64_t now)
{
{
RWMutex::Lock l1(_peers_l);
@ -134,8 +149,10 @@ void Topology::doPeriodicTasks(const int64_t now)
Address *a = (Address *)0;
SharedPtr<Peer> *p = (SharedPtr<Peer> *)0;
while (i.next(a,p)) {
if ( (!(*p)->alive(now)) && (_roots.count((*p)->identity()) == 0) )
if ( (!(*p)->alive(now)) && (_roots.count((*p)->identity()) == 0) ) {
(*p)->save(tPtr);
_peers.erase(*a);
}
}
}
{
@ -150,4 +167,21 @@ void Topology::doPeriodicTasks(const int64_t now)
}
}
void Topology::saveAll(void *tPtr)
{
RWMutex::RLock l(_peers_l);
Hashtable< Address,SharedPtr<Peer> >::Iterator i(_peers);
Address *a = (Address *)0;
SharedPtr<Peer> *p = (SharedPtr<Peer> *)0;
while (i.next(a,p)) {
if ( (!(*p)->alive(RR->node->now())) && (_roots.count((*p)->identity()) == 0) ) {
(*p)->save((void *)0);
}
}
}
void Topology::_loadCached(void *tPtr,const Address &zta,SharedPtr<Peer> &peer)
{
}
} // namespace ZeroTier

View file

@ -49,7 +49,7 @@ public:
~Topology();
/**
* Add a peer to database
* Add peer to database
*
* This will not replace existing peers. In that case the existing peer
* record is returned.
@ -57,14 +57,7 @@ public:
* @param peer Peer to add
* @return New or existing peer (should replace 'peer')
*/
ZT_ALWAYS_INLINE SharedPtr<Peer> add(const SharedPtr<Peer> &peer)
{
RWMutex::Lock _l(_peers_l);
SharedPtr<Peer> &hp = _peers[peer->address()];
if (!hp)
hp = peer;
return hp;
}
SharedPtr<Peer> add(void *tPtr,const SharedPtr<Peer> &peer);
/**
* Get a peer from its address
@ -73,29 +66,25 @@ public:
* @param zta ZeroTier address of peer
* @return Peer or NULL if not found
*/
ZT_ALWAYS_INLINE SharedPtr<Peer> get(const Address &zta) const
ZT_ALWAYS_INLINE SharedPtr<Peer> get(void *tPtr,const Address &zta)
{
RWMutex::RLock l1(_peers_l);
const SharedPtr<Peer> *const ap = _peers.get(zta);
return (ap) ? *ap : SharedPtr<Peer>();
}
/**
* @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
* @param zta ZeroTier address of peer
* @return Identity or NULL identity if not found
*/
ZT_ALWAYS_INLINE Identity getIdentity(void *tPtr,const Address &zta) const
{
if (zta == _myIdentity.address()) {
return _myIdentity;
} else {
{
RWMutex::RLock _l(_peers_l);
const SharedPtr<Peer> *const ap = _peers.get(zta);
if (ap)
return (*ap)->identity();
return *ap;
}
return Identity();
SharedPtr<Peer> p;
_loadCached(tPtr,zta,p);
if (p) {
RWMutex::Lock _l(_peers_l);
SharedPtr<Peer> &hp = _peers[zta];
if (!hp)
hp = p;
}
return p;
}
/**
@ -171,8 +160,7 @@ public:
Address *a = (Address *)0;
SharedPtr<Peer> *p = (SharedPtr<Peer> *)0;
while (i.next(a,p)) {
if (!f(*((const SharedPtr<Peer> *)p)))
break;
f(*((const SharedPtr<Peer> *)p));
}
}
@ -190,17 +178,42 @@ public:
{
RWMutex::RLock l(_peers_l);
std::vector<uintptr_t> rootPeerPtrs;
for(std::vector< SharedPtr<Peer> >::const_iterator i(_rootPeers.begin());i!=_rootPeers.end();++i)
rootPeerPtrs.push_back((uintptr_t)i->ptr());
std::sort(rootPeerPtrs.begin(),rootPeerPtrs.end());
const unsigned long rootPeerCnt = _rootPeers.size();
uintptr_t *const rootPeerPtrs = (uintptr_t *)malloc(sizeof(uintptr_t) * rootPeerCnt);
if (!rootPeerPtrs)
throw std::bad_alloc();
for(unsigned long i=0;i<rootPeerCnt;++i)
rootPeerPtrs[i] = (uintptr_t)_rootPeers[i].ptr();
std::sort(rootPeerPtrs,rootPeerPtrs + rootPeerCnt);
uintptr_t *const rootPeerPtrsEnd = rootPeerPtrs + rootPeerCnt;
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)) {
if (!f(*((const SharedPtr<Peer> *)p),std::binary_search(rootPeerPtrs.begin(),rootPeerPtrs.end(),(uintptr_t)p->ptr())))
break;
try {
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)) {
f(*((const SharedPtr<Peer> *)p),std::binary_search(rootPeerPtrs,rootPeerPtrsEnd,(uintptr_t)p->ptr()));
}
} catch ( ... ) {} // should not throw
free((void *)rootPeerPtrs);
}
/**
* Iterate through all paths in the system
*
* @tparam F Function to call for each path
* @param f
*/
template<typename F>
ZT_ALWAYS_INLINE void eachPath(F f) const
{
RWMutex::RLock l(_paths_l);
Hashtable< Path::HashKey,SharedPtr<Path> >::Iterator i(const_cast<Topology *>(this)->_paths);
Path::HashKey *k = (Path::HashKey *)0;
SharedPtr<Path> *p = (SharedPtr<Path> *)0;
while (i.next(k,p)) {
f(*((const SharedPtr<Peer> *)p));
}
}
@ -284,14 +297,21 @@ public:
*
* @param now Current time
*/
void rankRoots(const int64_t now);
void rankRoots(int64_t now);
/**
* Do periodic tasks such as database cleanup
*/
void doPeriodicTasks(const int64_t now);
void doPeriodicTasks(void *tPtr,int64_t now);
/**
* Save all currently known peers to data store
*/
void saveAll(void *tPtr);
private:
void _loadCached(void *tPtr,const Address &zta,SharedPtr<Peer> &peer);
const RuntimeEnvironment *const RR;
const Identity _myIdentity;

View file

@ -380,41 +380,71 @@ template<typename T>
static ZT_ALWAYS_INLINE T ntoh(T n) { return n; }
#endif
static ZT_ALWAYS_INLINE uint64_t readUInt64(const void *const p)
template<typename I>
static ZT_ALWAYS_INLINE I loadBigEndian(const void *const p)
{
#ifdef ZT_NO_TYPE_PUNNING
const uint8_t *const b = reinterpret_cast<const uint8_t *>(p);
return (
((uint64_t)b[0] << 56) |
((uint64_t)b[1] << 48) |
((uint64_t)b[2] << 40) |
((uint64_t)b[3] << 32) |
((uint64_t)b[4] << 24) |
((uint64_t)b[5] << 16) |
((uint64_t)b[6] << 8) |
(uint64_t)b[7]);
I x = (I)0;
for(unsigned int k=0;k<sizeof(I);++k) {
#if __BYTE_ORDER == __LITTLE_ENDIAN
reinterpret_cast<uint8_t *>(&x)[k] = reinterpret_cast<const uint8_t *>(p)[(sizeof(I)-1)-k];
#else
return ntoh(*reinterpret_cast<const uint64_t *>(p));
reinterpret_cast<uint8_t *>(&x)[k] = reinterpret_cast<const uint8_t *>(p)[k];
#endif
}
return x;
#else
return ntoh(*reinterpret_cast<const I *>(p));
#endif
}
static ZT_ALWAYS_INLINE void putUInt64(void *const p,const uint64_t i)
template<typename I>
static ZT_ALWAYS_INLINE void storeBigEndian(void *const p,const I i)
{
#ifdef ZT_NO_TYPE_PUNNING
uint8_t *const b = reinterpret_cast<uint8_t *>(p);
p[0] = (uint8_t)(i << 56);
p[1] = (uint8_t)(i << 48);
p[2] = (uint8_t)(i << 40);
p[3] = (uint8_t)(i << 32);
p[4] = (uint8_t)(i << 24);
p[5] = (uint8_t)(i << 16);
p[6] = (uint8_t)(i << 8);
p[7] = (uint8_t)i;
for(unsigned int k=0;k<sizeof(I);++k) {
#if __BYTE_ORDER == __LITTLE_ENDIAN
reinterpret_cast<uint8_t *>(p)[k] = reinterpret_cast<const uint8_t *>(&i)[(sizeof(I)-1)-k];
#else
*reinterpret_cast<uint64_t *>(p) = Utils::hton(i);
reinterpret_cast<uint8_t *>(p)[k] = reinterpret_cast<const uint8_t *>(&i)[k];
#endif
}
#else
*reinterpret_cast<I *>(p) = Utils::hton(i);
#endif
}
#if 0
template<typename T>
static ZT_ALWAYS_INLINE bool isPrimitiveType() { return false; }
template<>
ZT_ALWAYS_INLINE bool isPrimitiveType<void *>() { return true; }
template<>
ZT_ALWAYS_INLINE bool isPrimitiveType<const void *>() { return true; }
template<>
ZT_ALWAYS_INLINE bool isPrimitiveType<bool>() { return true; }
template<>
ZT_ALWAYS_INLINE bool isPrimitiveType<float>() { return true; }
template<>
ZT_ALWAYS_INLINE bool isPrimitiveType<double>() { return true; }
template<>
ZT_ALWAYS_INLINE bool isPrimitiveType<int8_t>() { return true; }
template<>
ZT_ALWAYS_INLINE bool isPrimitiveType<int16_t>() { return true; }
template<>
ZT_ALWAYS_INLINE bool isPrimitiveType<int32_t>() { return true; }
template<>
ZT_ALWAYS_INLINE bool isPrimitiveType<int64_t>() { return true; }
template<>
ZT_ALWAYS_INLINE bool isPrimitiveType<uint8_t>() { return true; }
template<>
ZT_ALWAYS_INLINE bool isPrimitiveType<uint16_t>() { return true; }
template<>
ZT_ALWAYS_INLINE bool isPrimitiveType<uint32_t>() { return true; }
template<>
ZT_ALWAYS_INLINE bool isPrimitiveType<uint64_t>() { return true; }
#endif
} // namespace Utils
} // namespace ZeroTier