Merge branch 'dev' into systemtray

This commit is contained in:
Grant Limberg 2016-11-30 10:21:19 -08:00
commit 32478845b2
17 changed files with 1616 additions and 919 deletions

19
Jenkinsfile vendored
View File

@ -55,6 +55,25 @@ parallel 'centos7': {
currentBuild.result = "FAILURE"
slackSend color: '#ff0000', message: "${env.JOB_NAME} broken on macOS (<${env.BUILD_URL}|Open>)"
throw err
}
}
}, 'windows': {
node('windows') {
try {
checkout scm
stage('Build Windows') {
bat '''CALL "C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\vcvarsall.bat" amd64
git clean -dfx
msbuild windows\\ZeroTierOne.sln
'''
}
}
catch (err) {
currentBuild.result = "FAILURE"
slackSend color: '#ff0000', message: "${env.JOB_NAME} broken on Windows (<${env.BUILD_URL}|Open>)"
throw err
}
}

View File

@ -1,24 +1,24 @@
[![JSON for Modern C++](https://raw.githubusercontent.com/nlohmann/json/master/doc/json.gif)](https://github.com/nlohmann/json/releases)
[![Build Status](https://travis-ci.org/nlohmann/json.svg?branch=master)](https://travis-ci.org/nlohmann/json)
[![Build Status](https://ci.appveyor.com/api/projects/status/1acb366xfyg3qybk?svg=true)](https://ci.appveyor.com/project/nlohmann/json)
[![Build Status](https://ci.appveyor.com/api/projects/status/1acb366xfyg3qybk/branch/develop?svg=true)](https://ci.appveyor.com/project/nlohmann/json)
[![Coverage Status](https://img.shields.io/coveralls/nlohmann/json.svg)](https://coveralls.io/r/nlohmann/json)
[![Coverity Scan Build Status](https://scan.coverity.com/projects/5550/badge.svg)](https://scan.coverity.com/projects/nlohmann-json)
[![Try online](https://img.shields.io/badge/try-online-blue.svg)](http://melpon.org/wandbox/permlink/p5o4znPnGHJpDVqN)
[![Try online](https://img.shields.io/badge/try-online-blue.svg)](http://melpon.org/wandbox/permlink/fsf5FqYe6GoX68W6)
[![Documentation](https://img.shields.io/badge/docs-doxygen-blue.svg)](http://nlohmann.github.io/json)
[![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/nlohmann/json/master/LICENSE.MIT)
[![Github Releases](https://img.shields.io/github/release/nlohmann/json.svg)](https://github.com/nlohmann/json/releases)
[![Github Issues](https://img.shields.io/github/issues/nlohmann/json.svg)](http://github.com/nlohmann/json/issues)
[![CII Best Practices](https://bestpractices.coreinfrastructure.org/projects/289/badge)](https://bestpractices.coreinfrastructure.org/projects/289)
## Design goals
There are myriads of [JSON](http://json.org) libraries out there, and each may even have its reason to exist. Our class had these design goals:
- **Intuitive syntax**. In languages such as Python, JSON feels like a first class data type. We used all the operator magic of modern C++ to achieve the same feeling in your code. Check out the [examples below](#examples) and you know, what I mean.
- **Intuitive syntax**. In languages such as Python, JSON feels like a first class data type. We used all the operator magic of modern C++ to achieve the same feeling in your code. Check out the [examples below](#examples) and you'll know what I mean.
- **Trivial integration**. Our whole code consists of a single header file [`json.hpp`](https://github.com/nlohmann/json/blob/develop/src/json.hpp). That's it. No library, no subproject, no dependencies, no complex build system. The class is written in vanilla C++11. All in all, everything should require no adjustment of your compiler flags or project settings.
- **Serious testing**. Our class is heavily [unit-tested](https://github.com/nlohmann/json/blob/master/test/src/unit.cpp) and covers [100%](https://coveralls.io/r/nlohmann/json) of the code, including all exceptional behavior. Furthermore, we checked with [Valgrind](http://valgrind.org) that there are no memory leaks.
- **Serious testing**. Our class is heavily [unit-tested](https://github.com/nlohmann/json/blob/master/test/src/unit.cpp) and covers [100%](https://coveralls.io/r/nlohmann/json) of the code, including all exceptional behavior. Furthermore, we checked with [Valgrind](http://valgrind.org) that there are no memory leaks. To maintain high quality, the project is following the [Core Infrastructure Initiative (CII) best practices](https://bestpractices.coreinfrastructure.org/projects/289).
Other aspects were not so important to us:
@ -283,8 +283,8 @@ json j_uset(c_uset); // only one entry for "one" is used
// maybe ["two", "three", "four", "one"]
std::multiset<std::string> c_mset {"one", "two", "one", "four"};
json j_mset(c_mset); // only one entry for "one" is used
// maybe ["one", "two", "four"]
json j_mset(c_mset); // both entries for "one" are used
// maybe ["one", "two", "one", "four"]
std::unordered_multiset<std::string> c_umset {"one", "two", "one", "four"};
json j_umset(c_umset); // both entries for "one" are used
@ -416,7 +416,13 @@ The following compilers are currently used in continuous integration at [Travis]
| GCC 4.9.3 | Ubuntu 14.04.4 LTS | g++-4.9 (Ubuntu 4.9.3-8ubuntu2~14.04) 4.9.3 |
| GCC 5.3.0 | Ubuntu 14.04.4 LTS | g++-5 (Ubuntu 5.3.0-3ubuntu1~14.04) 5.3.0 20151204 |
| GCC 6.1.1 | Ubuntu 14.04.4 LTS | g++-6 (Ubuntu 6.1.1-3ubuntu11~14.04.1) 6.1.1 20160511 |
| Clang 3.6.0 | Ubuntu 14.04.4 LTS | clang version 3.6.0 (tags/RELEASE_360/final) |
| Clang 3.6.1 | Ubuntu 14.04.4 LTS | clang version 3.6.1 (tags/RELEASE_361/final) |
| Clang 3.6.2 | Ubuntu 14.04.4 LTS | clang version 3.6.2 (tags/RELEASE_362/final) |
| Clang 3.7.0 | Ubuntu 14.04.4 LTS | clang version 3.7.0 (tags/RELEASE_370/final) |
| Clang 3.7.1 | Ubuntu 14.04.4 LTS | clang version 3.7.1 (tags/RELEASE_371/final) |
| Clang 3.8.0 | Ubuntu 14.04.4 LTS | clang version 3.8.0 (tags/RELEASE_380/final) |
| Clang 3.8.1 | Ubuntu 14.04.4 LTS | clang version 3.8.1 (tags/RELEASE_381/final) |
| Clang Xcode 6.1 | Darwin Kernel Version 13.4.0 (OSX 10.9.5) | Apple LLVM version 6.0 (clang-600.0.54) (based on LLVM 3.5svn) |
| Clang Xcode 6.2 | Darwin Kernel Version 13.4.0 (OSX 10.9.5) | Apple LLVM version 6.0 (clang-600.0.57) (based on LLVM 3.5svn) |
| Clang Xcode 6.3 | Darwin Kernel Version 14.3.0 (OSX 10.10.3) | Apple LLVM version 6.1.0 (clang-602.0.49) (based on LLVM 3.6.0svn) |
@ -424,7 +430,7 @@ The following compilers are currently used in continuous integration at [Travis]
| Clang Xcode 7.1 | Darwin Kernel Version 14.5.0 (OSX 10.10.5) | Apple LLVM version 7.0.0 (clang-700.1.76) |
| Clang Xcode 7.2 | Darwin Kernel Version 15.0.0 (OSX 10.10.5) | Apple LLVM version 7.0.2 (clang-700.1.81) |
| Clang Xcode 7.3 | Darwin Kernel Version 15.0.0 (OSX 10.10.5) | Apple LLVM version 7.3.0 (clang-703.0.29) |
| Clang Xcode 8.0 | Darwin Kernel Version 15.5.0 (OSX 10.11.5) | Apple LLVM version 8.0.0 (clang-800.0.24.1) |
| Clang Xcode 8.0 | Darwin Kernel Version 15.6.0 (OSX 10.11.6) | Apple LLVM version 8.0.0 (clang-800.0.38) |
| Visual Studio 14 2015 | Windows Server 2012 R2 (x64) | Microsoft (R) Build Engine version 14.0.25123.0 |
@ -486,14 +492,26 @@ I deeply appreciate the help of the following people.
- [Mário Feroldi](https://github.com/thelostt) fixed a small typo.
- [duncanwerner](https://github.com/duncanwerner) found a really embarrassing performance regression in the 2.0.0 release.
- [Damien](https://github.com/dtoma) fixed one of the last conversion warnings.
- [Thomas Braun](https://github.com/t-b) fixed a warning in a test case.
- [Théo DELRIEU](https://github.com/theodelrieu) patiently and constructively oversaw the long way toward [iterator-range parsing](https://github.com/nlohmann/json/issues/290).
- [Stefan](https://github.com/5tefan) fixed a minor issue in the documentation.
- [Vasil Dimov](https://github.com/vasild) fixed the documentation regarding conversions from `std::multiset`.
- [ChristophJud](https://github.com/ChristophJud) overworked the CMake files to ease project inclusion.
- [Vladimir Petrigo](https://github.com/vpetrigo) made a SFINAE hack more readable.
- [Denis Andrejew](https://github.com/seeekr) fixed a grammar issue in the README file.
Thanks a lot for helping out!
## Notes
- The code contains numerous debug **assertions** which can be switched off by defining the preprocessor macro `NDEBUG`, see the [documentation of `assert`](http://en.cppreference.com/w/cpp/error/assert).
- The code contains numerous debug **assertions** which can be switched off by defining the preprocessor macro `NDEBUG`, see the [documentation of `assert`](http://en.cppreference.com/w/cpp/error/assert). In particular, note [`operator[]`](https://nlohmann.github.io/json/classnlohmann_1_1basic__json_a2e26bd0b0168abb61f67ad5bcd5b9fa1.html#a2e26bd0b0168abb61f67ad5bcd5b9fa1) implements **unchecked access** for const objects: If the given key is not present, the behavior is undefined (think of a dereferenced null pointer) and yields an [assertion failure](https://github.com/nlohmann/json/issues/289) if assertions are switched on. If you are not sure whether an element in an object exists, use checked access with the [`at()` function](https://nlohmann.github.io/json/classnlohmann_1_1basic__json_a674de1ee73e6bf4843fc5dc1351fb726.html#a674de1ee73e6bf4843fc5dc1351fb726).
- As the exact type of a number is not defined in the [JSON specification](http://rfc7159.net/rfc7159), this library tries to choose the best fitting C++ number type automatically. As a result, the type `double` may be used to store numbers which may yield [**floating-point exceptions**](https://github.com/nlohmann/json/issues/181) in certain rare situations if floating-point exceptions have been unmasked in the calling code. These exceptions are not caused by the library and need to be fixed in the calling code, such as by re-masking the exceptions prior to calling library functions.
- The library supports **Unicode input** as follows:
- Only **UTF-8** encoded input is supported which is the default encoding for JSON according to [RFC 7159](http://rfc7159.net/rfc7159#rfc.section.8.1).
- Other encodings such as Latin-1, UTF-16, or UTF-32 are not supported and will yield parse errors.
- [Unicode noncharacters](http://www.unicode.org/faq/private_use.html#nonchar1) will not be replaced by the library.
- Invalid surrogates (e.g., incomplete pairs such as `\uDEAD`) will yield parse errors.
## Execute unit tests
@ -501,11 +519,20 @@ Thanks a lot for helping out!
To compile and run the tests, you need to execute
```sh
$ make
$ ./json_unit "*"
$ make check
===============================================================================
All tests passed (8905012 assertions in 32 test cases)
All tests passed (8905491 assertions in 36 test cases)
```
Alternatively, you can use [CMake](https://cmake.org) and run
```sh
$ mkdir build
$ cd build
$ cmake ..
$ make
$ ctest
```
For more information, have a look at the file [.travis.yml](https://github.com/nlohmann/json/blob/master/.travis.yml).

File diff suppressed because it is too large Load Diff

View File

@ -396,6 +396,16 @@ enum ZT_Event
ZT_EVENT_TRACE = 5
};
/**
* Node relay policy
*/
enum ZT_RelayPolicy
{
ZT_RELAY_POLICY_NEVER = 0,
ZT_RELAY_POLICY_TRUSTED = 1,
ZT_RELAY_POLICY_ALWAYS = 2
};
/**
* Current node status
*/
@ -430,6 +440,11 @@ typedef struct
*/
const char *secretIdentity;
/**
* Node relay policy
*/
enum ZT_RelayPolicy relayPolicy;
/**
* True if some kind of connectivity appears available
*/
@ -791,13 +806,6 @@ enum ZT_VirtualNetworkConfigOperation
ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_DESTROY = 4
};
enum ZT_RelayPolicy
{
ZT_RELAY_POLICY_NEVER = 0,
ZT_RELAY_POLICY_TRUSTED = 1,
ZT_RELAY_POLICY_ALWAYS = 2
};
/**
* What trust hierarchy role does this peer have?
*/
@ -1487,8 +1495,9 @@ typedef int (*ZT_WirePacketSendFunction)(
* Paramters:
* (1) Node
* (2) User pointer
* (3) Local interface address
* (4) Remote address
* (3) ZeroTier address or 0 for none/any
* (4) Local interface address
* (5) Remote address
*
* This function must return nonzero (true) if the path should be used.
*
@ -1507,13 +1516,87 @@ typedef int (*ZT_WirePacketSendFunction)(
typedef int (*ZT_PathCheckFunction)(
ZT_Node *, /* Node */
void *, /* User ptr */
uint64_t, /* ZeroTier address */
const struct sockaddr_storage *, /* Local address */
const struct sockaddr_storage *); /* Remote address */
/**
* Function to get physical addresses for ZeroTier peers
*
* Parameters:
* (1) Node
* (2) User pointer
* (3) ZeroTier address (least significant 40 bits)
* (4) Desried address family or -1 for any
* (5) Buffer to fill with result
*
* If provided this function will be occasionally called to get physical
* addresses that might be tried to reach a ZeroTier address. It must
* return a nonzero (true) value if the result buffer has been filled
* with an address.
*/
typedef int (*ZT_PathLookupFunction)(
ZT_Node *, /* Node */
void *, /* User ptr */
uint64_t, /* ZeroTier address (40 bits) */
int, /* Desired ss_family or -1 for any */
struct sockaddr_storage *); /* Result buffer */
/****************************************************************************/
/* C Node API */
/****************************************************************************/
/**
* Structure for configuring ZeroTier core callback functions
*/
struct ZT_Node_Callbacks
{
/**
* Struct version -- must currently be 0
*/
long version;
/**
* REQUIRED: Function to get objects from persistent storage
*/
ZT_DataStoreGetFunction dataStoreGetFunction;
/**
* REQUIRED: Function to store objects in persistent storage
*/
ZT_DataStorePutFunction dataStorePutFunction;
/**
* REQUIRED: Function to send packets over the physical wire
*/
ZT_WirePacketSendFunction wirePacketSendFunction;
/**
* REQUIRED: Function to inject frames into a virtual network's TAP
*/
ZT_VirtualNetworkFrameFunction virtualNetworkFrameFunction;
/**
* REQUIRED: Function to be called when virtual networks are configured or changed
*/
ZT_VirtualNetworkConfigFunction virtualNetworkConfigFunction;
/**
* REQUIRED: Function to be called to notify external code of important events
*/
ZT_EventCallback eventCallback;
/**
* OPTIONAL: Function to check whether a given physical path should be used
*/
ZT_PathCheckFunction pathCheckFunction;
/**
* OPTIONAL: Function to get hints to physical paths to ZeroTier addresses
*/
ZT_PathLookupFunction pathLookupFunction;
};
/**
* Create a new ZeroTier One node
*
@ -1525,25 +1608,11 @@ typedef int (*ZT_PathCheckFunction)(
*
* @param node Result: pointer is set to new node instance on success
* @param uptr User pointer to pass to functions/callbacks
* @param callbacks Callback function configuration
* @param now Current clock in milliseconds
* @param dataStoreGetFunction Function called to get objects from persistent storage
* @param dataStorePutFunction Function called to put objects in persistent storage
* @param virtualNetworkConfigFunction Function to be called when virtual LANs are created, deleted, or their config parameters change
* @param pathCheckFunction A function to check whether a path should be used for ZeroTier traffic, or NULL to allow any path
* @param eventCallback Function to receive status updates and non-fatal error notices
* @return OK (0) or error code if a fatal error condition has occurred
*/
enum ZT_ResultCode ZT_Node_new(
ZT_Node **node,
void *uptr,
uint64_t now,
ZT_DataStoreGetFunction dataStoreGetFunction,
ZT_DataStorePutFunction dataStorePutFunction,
ZT_WirePacketSendFunction wirePacketSendFunction,
ZT_VirtualNetworkFrameFunction virtualNetworkFrameFunction,
ZT_VirtualNetworkConfigFunction virtualNetworkConfigFunction,
ZT_PathCheckFunction pathCheckFunction,
ZT_EventCallback eventCallback);
enum ZT_ResultCode ZT_Node_new(ZT_Node **node,void *uptr,const struct ZT_Node_Callbacks *callbacks,uint64_t now);
/**
* Delete a node and free all resources it consumes
@ -1793,7 +1862,7 @@ void ZT_Node_clearLocalInterfaceAddresses(ZT_Node *node);
* @param ztAddress ZeroTier address (least significant 40 bits)
* @param role New peer role (LEAF or UPSTREAM)
*/
void ZT_Node_setRole(ZT_Node *node,uint64_t ztAddress,ZT_PeerRole role);
void ZT_Node_setRole(ZT_Node *node,uint64_t ztAddress,enum ZT_PeerRole role);
/**
* Set a network configuration master instance for this node

View File

@ -56,7 +56,11 @@ namespace {
, eventListener(NULL)
, frameListener(NULL)
, configListener(NULL)
{}
, callbacks(NULL)
{
callbacks = (ZT_Node_Callbacks*)malloc(sizeof(ZT_Node_Callbacks));
memset(callbacks, 0, sizeof(ZT_Node_Callbacks));
}
~JniRef()
{
@ -69,6 +73,9 @@ namespace {
env->DeleteGlobalRef(eventListener);
env->DeleteGlobalRef(frameListener);
env->DeleteGlobalRef(configListener);
free(callbacks);
callbacks = NULL;
}
uint64_t id;
@ -83,6 +90,8 @@ namespace {
jobject eventListener;
jobject frameListener;
jobject configListener;
ZT_Node_Callbacks *callbacks;
};
@ -602,17 +611,18 @@ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_node_1init(
}
ref->eventListener = env->NewGlobalRef(tmp);
ref->callbacks->dataStoreGetFunction = &DataStoreGetFunction;
ref->callbacks->dataStorePutFunction = &DataStorePutFunction;
ref->callbacks->wirePacketSendFunction = &WirePacketSendFunction;
ref->callbacks->virtualNetworkFrameFunction = &VirtualNetworkFrameFunctionCallback;
ref->callbacks->virtualNetworkConfigFunction = &VirtualNetworkConfigFunctionCallback;
ref->callbacks->eventCallback = &EventCallback;
ZT_ResultCode rc = ZT_Node_new(
&node,
ref,
(uint64_t)now,
&DataStoreGetFunction,
&DataStorePutFunction,
&WirePacketSendFunction,
&VirtualNetworkFrameFunctionCallback,
&VirtualNetworkConfigFunctionCallback,
NULL,
&EventCallback);
ref->callbacks,
(uint64_t)now);
if(rc != ZT_RESULT_OK)
{

View File

@ -43,7 +43,7 @@
{
self = [super init];
if(self) {
baseURL = @"http://localhost:9993";
baseURL = @"http://127.0.0.1:9993";
session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration ephemeralSessionConfiguration]];
_isQuitting = NO;
}

View File

@ -320,6 +320,11 @@
*/
#define ZT_MIN_UNITE_INTERVAL 30000
/**
* How often should peers try memorized or statically defined paths?
*/
#define ZT_TRY_MEMORIZED_PATH_INTERVAL 30000
/**
* Sanity limit on maximum bridge routes
*

View File

@ -552,7 +552,7 @@ bool IncomingPacket::_doRENDEZVOUS(const RuntimeEnvironment *RR,const SharedPtr<
const unsigned int addrlen = (*this)[ZT_PROTO_VERB_RENDEZVOUS_IDX_ADDRLEN];
if ((port > 0)&&((addrlen == 4)||(addrlen == 16))) {
const InetAddress atAddr(field(ZT_PROTO_VERB_RENDEZVOUS_IDX_ADDRESS,addrlen),addrlen,port);
if (RR->node->shouldUsePathForZeroTierTraffic(_path->localAddress(),atAddr)) {
if (RR->node->shouldUsePathForZeroTierTraffic(with,_path->localAddress(),atAddr)) {
RR->node->putPacket(_path->localAddress(),atAddr,"ABRE",4,2); // send low-TTL junk packet to 'open' local NAT(s) and stateful firewalls
rendezvousWith->attemptToContactAt(_path->localAddress(),atAddr,RR->node->now());
TRACE("RENDEZVOUS from %s says %s might be at %s, sent verification attempt",peer->address().toString().c_str(),with.toString().c_str(),atAddr.toString().c_str());
@ -1120,7 +1120,7 @@ bool IncomingPacket::_doPUSH_DIRECT_PATHS(const RuntimeEnvironment *RR,const Sha
redundant = peer->hasActivePathTo(now,a);
}
if ( ((flags & ZT_PUSH_DIRECT_PATHS_FLAG_FORGET_PATH) == 0) && (!redundant) && (RR->node->shouldUsePathForZeroTierTraffic(_path->localAddress(),a)) ) {
if ( ((flags & ZT_PUSH_DIRECT_PATHS_FLAG_FORGET_PATH) == 0) && (!redundant) && (RR->node->shouldUsePathForZeroTierTraffic(peer->address(),_path->localAddress(),a)) ) {
if (++countPerScope[(int)a.ipScope()][0] <= ZT_PUSH_DIRECT_PATHS_MAX_PER_SCOPE_AND_FAMILY) {
TRACE("attempting to contact %s at pushed direct path %s",peer->address().toString().c_str(),a.toString().c_str());
peer->attemptToContactAt(InetAddress(),a,now);
@ -1139,7 +1139,7 @@ bool IncomingPacket::_doPUSH_DIRECT_PATHS(const RuntimeEnvironment *RR,const Sha
redundant = peer->hasActivePathTo(now,a);
}
if ( ((flags & ZT_PUSH_DIRECT_PATHS_FLAG_FORGET_PATH) == 0) && (!redundant) && (RR->node->shouldUsePathForZeroTierTraffic(_path->localAddress(),a)) ) {
if ( ((flags & ZT_PUSH_DIRECT_PATHS_FLAG_FORGET_PATH) == 0) && (!redundant) && (RR->node->shouldUsePathForZeroTierTraffic(peer->address(),_path->localAddress(),a)) ) {
if (++countPerScope[(int)a.ipScope()][1] <= ZT_PUSH_DIRECT_PATHS_MAX_PER_SCOPE_AND_FAMILY) {
TRACE("attempting to contact %s at pushed direct path %s",peer->address().toString().c_str(),a.toString().c_str());
peer->attemptToContactAt(InetAddress(),a,now);

View File

@ -46,34 +46,20 @@ namespace ZeroTier {
/* Public Node interface (C++, exposed via CAPI bindings) */
/****************************************************************************/
Node::Node(
uint64_t now,
void *uptr,
ZT_DataStoreGetFunction dataStoreGetFunction,
ZT_DataStorePutFunction dataStorePutFunction,
ZT_WirePacketSendFunction wirePacketSendFunction,
ZT_VirtualNetworkFrameFunction virtualNetworkFrameFunction,
ZT_VirtualNetworkConfigFunction virtualNetworkConfigFunction,
ZT_PathCheckFunction pathCheckFunction,
ZT_EventCallback eventCallback) :
Node::Node(void *uptr,const struct ZT_Node_Callbacks *callbacks,uint64_t now) :
_RR(this),
RR(&_RR),
_uPtr(uptr),
_dataStoreGetFunction(dataStoreGetFunction),
_dataStorePutFunction(dataStorePutFunction),
_wirePacketSendFunction(wirePacketSendFunction),
_virtualNetworkFrameFunction(virtualNetworkFrameFunction),
_virtualNetworkConfigFunction(virtualNetworkConfigFunction),
_pathCheckFunction(pathCheckFunction),
_eventCallback(eventCallback),
_networks(),
_networks_m(),
_prngStreamPtr(0),
_now(now),
_lastPingCheck(0),
_lastHousekeepingRun(0),
_relayPolicy(ZT_RELAY_POLICY_TRUSTED)
{
if (callbacks->version != 0)
throw std::runtime_error("callbacks struct version mismatch");
memcpy(&_cb,callbacks,sizeof(ZT_Node_Callbacks));
_online = false;
memset(_expectingRepliesToBucketPtr,0,sizeof(_expectingRepliesToBucketPtr));
@ -81,30 +67,26 @@ Node::Node(
memset(_lastIdentityVerification,0,sizeof(_lastIdentityVerification));
// Use Salsa20 alone as a high-quality non-crypto PRNG
{
char foo[32];
Utils::getSecureRandom(foo,32);
_prng.init(foo,256,foo);
memset(_prngStream,0,sizeof(_prngStream));
_prng.encrypt12(_prngStream,_prngStream,sizeof(_prngStream));
}
char foo[32];
Utils::getSecureRandom(foo,32);
_prng.init(foo,256,foo);
memset(_prngStream,0,sizeof(_prngStream));
_prng.encrypt12(_prngStream,_prngStream,sizeof(_prngStream));
{
std::string idtmp(dataStoreGet("identity.secret"));
if ((!idtmp.length())||(!RR->identity.fromString(idtmp))||(!RR->identity.hasPrivate())) {
TRACE("identity.secret not found, generating...");
RR->identity.generate();
idtmp = RR->identity.toString(true);
if (!dataStorePut("identity.secret",idtmp,true))
throw std::runtime_error("unable to write identity.secret");
}
RR->publicIdentityStr = RR->identity.toString(false);
RR->secretIdentityStr = RR->identity.toString(true);
idtmp = dataStoreGet("identity.public");
if (idtmp != RR->publicIdentityStr) {
if (!dataStorePut("identity.public",RR->publicIdentityStr,false))
throw std::runtime_error("unable to write identity.public");
}
std::string idtmp(dataStoreGet("identity.secret"));
if ((!idtmp.length())||(!RR->identity.fromString(idtmp))||(!RR->identity.hasPrivate())) {
TRACE("identity.secret not found, generating...");
RR->identity.generate();
idtmp = RR->identity.toString(true);
if (!dataStorePut("identity.secret",idtmp,true))
throw std::runtime_error("unable to write identity.secret");
}
RR->publicIdentityStr = RR->identity.toString(false);
RR->secretIdentityStr = RR->identity.toString(true);
idtmp = dataStoreGet("identity.public");
if (idtmp != RR->publicIdentityStr) {
if (!dataStorePut("identity.public",RR->publicIdentityStr,false))
throw std::runtime_error("unable to write identity.public");
}
try {
@ -174,12 +156,14 @@ ZT_ResultCode Node::processVirtualNetworkFrame(
} else return ZT_RESULT_ERROR_NETWORK_NOT_FOUND;
}
// Closure used to ping upstream and active/online peers
class _PingPeersThatNeedPing
{
public:
_PingPeersThatNeedPing(const RuntimeEnvironment *renv,uint64_t now) :
_PingPeersThatNeedPing(const RuntimeEnvironment *renv,const std::vector<Address> &upstreams,uint64_t now) :
lastReceiveFromUpstream(0),
RR(renv),
_upstreams(upstreams),
_now(now),
_world(RR->topology->world())
{
@ -189,29 +173,25 @@ public:
inline void operator()(Topology &t,const SharedPtr<Peer> &p)
{
bool upstream = false;
InetAddress stableEndpoint4,stableEndpoint6;
// If this is a world root, pick (if possible) both an IPv4 and an IPv6 stable endpoint to use if link isn't currently alive.
for(std::vector<World::Root>::const_iterator r(_world.roots().begin());r!=_world.roots().end();++r) {
if (r->identity == p->identity()) {
upstream = true;
for(unsigned long k=0,ptr=(unsigned long)RR->node->prng();k<(unsigned long)r->stableEndpoints.size();++k) {
const InetAddress &addr = r->stableEndpoints[ptr++ % r->stableEndpoints.size()];
if (!stableEndpoint4) {
if (addr.ss_family == AF_INET)
stableEndpoint4 = addr;
}
if (!stableEndpoint6) {
if (addr.ss_family == AF_INET6)
stableEndpoint6 = addr;
if (std::find(_upstreams.begin(),_upstreams.end(),p->address()) != _upstreams.end()) {
InetAddress stableEndpoint4,stableEndpoint6;
for(std::vector<World::Root>::const_iterator r(_world.roots().begin());r!=_world.roots().end();++r) {
if (r->identity == p->identity()) {
for(unsigned long k=0,ptr=(unsigned long)RR->node->prng();k<(unsigned long)r->stableEndpoints.size();++k) {
const InetAddress &addr = r->stableEndpoints[ptr++ % r->stableEndpoints.size()];
if (!stableEndpoint4) {
if (addr.ss_family == AF_INET)
stableEndpoint4 = addr;
}
if (!stableEndpoint6) {
if (addr.ss_family == AF_INET6)
stableEndpoint6 = addr;
}
}
break;
}
break;
}
}
if (upstream) {
// We keep connections to upstream peers alive forever.
bool needToContactIndirect = true;
if (p->doPingAndKeepalive(_now,AF_INET)) {
@ -246,6 +226,7 @@ public:
private:
const RuntimeEnvironment *RR;
const std::vector<Address> &_upstreams;
uint64_t _now;
World _world;
};
@ -274,8 +255,15 @@ ZT_ResultCode Node::processBackgroundTasks(uint64_t now,volatile uint64_t *nextB
for(std::vector< SharedPtr<Network> >::const_iterator n(needConfig.begin());n!=needConfig.end();++n)
(*n)->requestConfiguration();
// Run WHOIS on upstreams we don't know about
const std::vector<Address> upstreams(RR->topology->upstreamAddresses());
for(std::vector<Address>::const_iterator a(upstreams.begin());a!=upstreams.end();++a) {
if (!RR->topology->getPeer(*a))
RR->sw->requestWhois(*a);
}
// Do pings and keepalives
_PingPeersThatNeedPing pfunc(RR,now);
_PingPeersThatNeedPing pfunc(RR,upstreams,now);
RR->topology->eachPeer<_PingPeersThatNeedPing &>(pfunc);
// Update online status, post status change as event
@ -384,6 +372,7 @@ void Node::status(ZT_NodeStatus *status) const
status->worldTimestamp = RR->topology->worldTimestamp();
status->publicIdentity = RR->publicIdentityStr.c_str();
status->secretIdentity = RR->secretIdentityStr.c_str();
status->relayPolicy = _relayPolicy;
status->online = _online ? 1 : 0;
}
@ -631,7 +620,7 @@ std::string Node::dataStoreGet(const char *name)
std::string r;
unsigned long olen = 0;
do {
long n = _dataStoreGetFunction(reinterpret_cast<ZT_Node *>(this),_uPtr,name,buf,sizeof(buf),(unsigned long)r.length(),&olen);
long n = _cb.dataStoreGetFunction(reinterpret_cast<ZT_Node *>(this),_uPtr,name,buf,sizeof(buf),(unsigned long)r.length(),&olen);
if (n <= 0)
return std::string();
r.append(buf,n);
@ -639,7 +628,7 @@ std::string Node::dataStoreGet(const char *name)
return r;
}
bool Node::shouldUsePathForZeroTierTraffic(const InetAddress &localAddress,const InetAddress &remoteAddress)
bool Node::shouldUsePathForZeroTierTraffic(const Address &ztaddr,const InetAddress &localAddress,const InetAddress &remoteAddress)
{
if (!Path::isAddressValidForPath(remoteAddress))
return false;
@ -656,9 +645,7 @@ bool Node::shouldUsePathForZeroTierTraffic(const InetAddress &localAddress,const
}
}
if (_pathCheckFunction)
return (_pathCheckFunction(reinterpret_cast<ZT_Node *>(this),_uPtr,reinterpret_cast<const struct sockaddr_storage *>(&localAddress),reinterpret_cast<const struct sockaddr_storage *>(&remoteAddress)) != 0);
else return true;
return ( (_cb.pathCheckFunction) ? (_cb.pathCheckFunction(reinterpret_cast<ZT_Node *>(this),_uPtr,ztaddr.toInt(),reinterpret_cast<const struct sockaddr_storage *>(&localAddress),reinterpret_cast<const struct sockaddr_storage *>(&remoteAddress)) != 0) : true);
}
#ifdef ZT_TRACE
@ -696,7 +683,7 @@ void Node::postTrace(const char *module,unsigned int line,const char *fmt,...)
uint64_t Node::prng()
{
unsigned int p = (++_prngStreamPtr % (sizeof(_prngStream) / sizeof(uint64_t)));
unsigned int p = (++_prngStreamPtr % ZT_NODE_PRNG_BUF_SIZE);
if (!p)
_prng.encrypt12(_prngStream,_prngStream,sizeof(_prngStream));
return _prngStream[p];
@ -815,21 +802,11 @@ void Node::ncSendError(uint64_t nwid,uint64_t requestPacketId,const Address &des
extern "C" {
enum ZT_ResultCode ZT_Node_new(
ZT_Node **node,
void *uptr,
uint64_t now,
ZT_DataStoreGetFunction dataStoreGetFunction,
ZT_DataStorePutFunction dataStorePutFunction,
ZT_WirePacketSendFunction wirePacketSendFunction,
ZT_VirtualNetworkFrameFunction virtualNetworkFrameFunction,
ZT_VirtualNetworkConfigFunction virtualNetworkConfigFunction,
ZT_PathCheckFunction pathCheckFunction,
ZT_EventCallback eventCallback)
enum ZT_ResultCode ZT_Node_new(ZT_Node **node,void *uptr,const struct ZT_Node_Callbacks *callbacks,uint64_t now)
{
*node = (ZT_Node *)0;
try {
*node = reinterpret_cast<ZT_Node *>(new ZeroTier::Node(now,uptr,dataStoreGetFunction,dataStorePutFunction,wirePacketSendFunction,virtualNetworkFrameFunction,virtualNetworkConfigFunction,pathCheckFunction,eventCallback));
*node = reinterpret_cast<ZT_Node *>(new ZeroTier::Node(uptr,callbacks,now));
return ZT_RESULT_OK;
} catch (std::bad_alloc &exc) {
return ZT_RESULT_FATAL_ERROR_OUT_OF_MEMORY;

View File

@ -49,6 +49,9 @@
#define ZT_EXPECTING_REPLIES_BUCKET_MASK1 255
#define ZT_EXPECTING_REPLIES_BUCKET_MASK2 31
// Size of PRNG stream buffer
#define ZT_NODE_PRNG_BUF_SIZE 64
namespace ZeroTier {
/**
@ -59,17 +62,7 @@ namespace ZeroTier {
class Node : public NetworkController::Sender
{
public:
Node(
uint64_t now,
void *uptr,
ZT_DataStoreGetFunction dataStoreGetFunction,
ZT_DataStorePutFunction dataStorePutFunction,
ZT_WirePacketSendFunction wirePacketSendFunction,
ZT_VirtualNetworkFrameFunction virtualNetworkFrameFunction,
ZT_VirtualNetworkConfigFunction virtualNetworkConfigFunction,
ZT_PathCheckFunction pathCheckFunction,
ZT_EventCallback eventCallback);
Node(void *uptr,const struct ZT_Node_Callbacks *callbacks,uint64_t now);
virtual ~Node();
// Public API Functions ----------------------------------------------------
@ -127,24 +120,11 @@ public:
// Internal functions ------------------------------------------------------
/**
* @return Time as of last call to run()
*/
inline uint64_t now() const throw() { return _now; }
/**
* Enqueue a ZeroTier message to be sent
*
* @param localAddress Local address
* @param addr Destination address
* @param data Packet data
* @param len Packet length
* @param ttl Desired TTL (default: 0 for unchanged/default TTL)
* @return True if packet appears to have been sent
*/
inline bool putPacket(const InetAddress &localAddress,const InetAddress &addr,const void *data,unsigned int len,unsigned int ttl = 0)
{
return (_wirePacketSendFunction(
return (_cb.wirePacketSendFunction(
reinterpret_cast<ZT_Node *>(this),
_uPtr,
reinterpret_cast<const struct sockaddr_storage *>(&localAddress),
@ -154,21 +134,9 @@ public:
ttl) == 0);
}
/**
* Enqueue a frame to be injected into a tap device (port)
*
* @param nwid Network ID
* @param nuptr Network user ptr
* @param source Source MAC
* @param dest Destination MAC
* @param etherType 16-bit ethernet type
* @param vlanId VLAN ID or 0 if none
* @param data Frame data
* @param len Frame length
*/
inline void putFrame(uint64_t nwid,void **nuptr,const MAC &source,const MAC &dest,unsigned int etherType,unsigned int vlanId,const void *data,unsigned int len)
{
_virtualNetworkFrameFunction(
_cb.virtualNetworkFrameFunction(
reinterpret_cast<ZT_Node *>(this),
_uPtr,
nwid,
@ -181,13 +149,6 @@ public:
len);
}
/**
* @param localAddress Local address
* @param remoteAddress Remote address
* @return True if path should be used
*/
bool shouldUsePathForZeroTierTraffic(const InetAddress &localAddress,const InetAddress &remoteAddress);
inline SharedPtr<Network> network(uint64_t nwid) const
{
Mutex::Lock _l(_networks_m);
@ -214,37 +175,20 @@ public:
return nw;
}
/**
* @return Potential direct paths to me a.k.a. local interface addresses
*/
inline std::vector<InetAddress> directPaths() const
{
Mutex::Lock _l(_directPaths_m);
return _directPaths;
}
inline bool dataStorePut(const char *name,const void *data,unsigned int len,bool secure) { return (_dataStorePutFunction(reinterpret_cast<ZT_Node *>(this),_uPtr,name,data,len,(int)secure) == 0); }
inline bool dataStorePut(const char *name,const void *data,unsigned int len,bool secure) { return (_cb.dataStorePutFunction(reinterpret_cast<ZT_Node *>(this),_uPtr,name,data,len,(int)secure) == 0); }
inline bool dataStorePut(const char *name,const std::string &data,bool secure) { return dataStorePut(name,(const void *)data.data(),(unsigned int)data.length(),secure); }
inline void dataStoreDelete(const char *name) { _dataStorePutFunction(reinterpret_cast<ZT_Node *>(this),_uPtr,name,(const void *)0,0,0); }
inline void dataStoreDelete(const char *name) { _cb.dataStorePutFunction(reinterpret_cast<ZT_Node *>(this),_uPtr,name,(const void *)0,0,0); }
std::string dataStoreGet(const char *name);
/**
* Post an event to the external user
*
* @param ev Event type
* @param md Meta-data (default: NULL/none)
*/
inline void postEvent(ZT_Event ev,const void *md = (const void *)0) { _eventCallback(reinterpret_cast<ZT_Node *>(this),_uPtr,ev,md); }
inline void postEvent(ZT_Event ev,const void *md = (const void *)0) { _cb.eventCallback(reinterpret_cast<ZT_Node *>(this),_uPtr,ev,md); }
/**
* Update virtual network port configuration
*
* @param nwid Network ID
* @param nuptr Network user ptr
* @param op Configuration operation
* @param nc Network configuration
*/
inline int configureVirtualNetworkPort(uint64_t nwid,void **nuptr,ZT_VirtualNetworkConfigOperation op,const ZT_VirtualNetworkConfig *nc) { return _virtualNetworkConfigFunction(reinterpret_cast<ZT_Node *>(this),_uPtr,nwid,nuptr,op,nc); }
inline int configureVirtualNetworkPort(uint64_t nwid,void **nuptr,ZT_VirtualNetworkConfigOperation op,const ZT_VirtualNetworkConfig *nc) { return _cb.virtualNetworkConfigFunction(reinterpret_cast<ZT_Node *>(this),_uPtr,nwid,nuptr,op,nc); }
inline bool online() const throw() { return _online; }
inline ZT_RelayPolicy relayPolicy() const { return _relayPolicy; }
@ -253,6 +197,9 @@ public:
void postTrace(const char *module,unsigned int line,const char *fmt,...);
#endif
bool shouldUsePathForZeroTierTraffic(const Address &ztaddr,const InetAddress &localAddress,const InetAddress &remoteAddress);
inline bool externalPathLookup(const Address &ztaddr,int family,InetAddress &addr) { return ( (_cb.pathLookupFunction) ? (_cb.pathLookupFunction(reinterpret_cast<ZT_Node *>(this),_uPtr,ztaddr.toInt(),family,reinterpret_cast<struct sockaddr_storage *>(&addr)) != 0) : false ); }
uint64_t prng();
void postCircuitTestReport(const ZT_CircuitTestReport *report);
void setTrustedPaths(const struct sockaddr_storage *networks,const uint64_t *ids,unsigned int count);
@ -317,8 +264,8 @@ private:
RuntimeEnvironment _RR;
RuntimeEnvironment *RR;
void *_uPtr; // _uptr (lower case) is reserved in Visual Studio :P
ZT_Node_Callbacks _cb;
// For tracking packet IDs to filter out OK/ERROR replies to packets we did not send
uint8_t _expectingRepliesToBucketPtr[ZT_EXPECTING_REPLIES_BUCKET_MASK1 + 1];
@ -327,14 +274,6 @@ private:
// Time of last identity verification indexed by InetAddress.rateGateHash()
uint64_t _lastIdentityVerification[16384];
ZT_DataStoreGetFunction _dataStoreGetFunction;
ZT_DataStorePutFunction _dataStorePutFunction;
ZT_WirePacketSendFunction _wirePacketSendFunction;
ZT_VirtualNetworkFrameFunction _virtualNetworkFrameFunction;
ZT_VirtualNetworkConfigFunction _virtualNetworkConfigFunction;
ZT_PathCheckFunction _pathCheckFunction;
ZT_EventCallback _eventCallback;
std::vector< std::pair< uint64_t, SharedPtr<Network> > > _networks;
Mutex _networks_m;
@ -348,7 +287,7 @@ private:
unsigned int _prngStreamPtr;
Salsa20 _prng;
uint64_t _prngStream[16]; // repeatedly encrypted with _prng to yield a high-quality non-crypto PRNG stream
uint64_t _prngStream[ZT_NODE_PRNG_BUF_SIZE]; // repeatedly encrypted with _prng to yield a high-quality non-crypto PRNG stream
uint64_t _now;
uint64_t _lastPingCheck;

View File

@ -43,6 +43,7 @@ static uint32_t _natKeepaliveBuf = 0;
Peer::Peer(const RuntimeEnvironment *renv,const Identity &myIdentity,const Identity &peerIdentity) :
_lastReceive(0),
_lastNontrivialReceive(0),
_lastTriedMemorizedPath(0),
_lastDirectPathPushSent(0),
_lastDirectPathPushReceive(0),
_lastCredentialRequestSent(0),
@ -160,7 +161,7 @@ void Peer::received(
}
}
if ( (!pathIsConfirmed) && (RR->node->shouldUsePathForZeroTierTraffic(path->localAddress(),path->address())) ) {
if ( (!pathIsConfirmed) && (RR->node->shouldUsePathForZeroTierTraffic(_id.address(),path->localAddress(),path->address())) ) {
if (verb == Packet::VERB_OK) {
Mutex::Lock _l(_paths_m);
@ -373,6 +374,16 @@ void Peer::attemptToContactAt(const InetAddress &localAddr,const InetAddress &at
}
}
void Peer::tryMemorizedPath(uint64_t now)
{
if ((now - _lastTriedMemorizedPath) >= ZT_TRY_MEMORIZED_PATH_INTERVAL) {
_lastTriedMemorizedPath = now;
InetAddress mp;
if (RR->node->externalPathLookup(_id.address(),-1,mp))
attemptToContactAt(InetAddress(),mp,now);
}
}
bool Peer::doPingAndKeepalive(uint64_t now,int inetAddressFamily)
{
Mutex::Lock _l(_paths_m);

View File

@ -164,6 +164,13 @@ public:
*/
void attemptToContactAt(const InetAddress &localAddr,const InetAddress &atAddress,uint64_t now);
/**
* Try a memorized or statically defined path if any are known
*
* Under the hood this is done periodically based on ZT_TRY_MEMORIZED_PATH_INTERVAL.
*/
void tryMemorizedPath(uint64_t now);
/**
* Send pings or keepalives depending on configured timeouts
*
@ -435,6 +442,7 @@ private:
uint8_t _remoteClusterOptimal6[16];
uint64_t _lastReceive; // direct or indirect
uint64_t _lastNontrivialReceive; // frames, things like netconf, etc.
uint64_t _lastTriedMemorizedPath;
uint64_t _lastDirectPathPushSent;
uint64_t _lastDirectPathPushReceive;
uint64_t _lastCredentialRequestSent;

View File

@ -85,7 +85,7 @@ void Switch::onRemotePacket(const InetAddress &localAddr,const InetAddress &from
Address beaconAddr(reinterpret_cast<const char *>(data) + 8,5);
if (beaconAddr == RR->identity.address())
return;
if (!RR->node->shouldUsePathForZeroTierTraffic(localAddr,fromAddr))
if (!RR->node->shouldUsePathForZeroTierTraffic(beaconAddr,localAddr,fromAddr))
return;
SharedPtr<Peer> peer(RR->topology->getPeer(beaconAddr));
if (peer) { // we'll only respond to beacons from known peers
@ -710,12 +710,12 @@ bool Switch::_trySend(const Packet &packet,bool encrypt)
if (peer) {
const uint64_t now = RR->node->now();
// First get the best path, and if it's dead (and this is not a root)
// we attempt to re-activate that path but this packet will flow
// upstream. If the path comes back alive, it will be used in the future.
// For roots we don't do the alive check since roots are not required
// to send heartbeats "down" and because we have to at least try to
// go somewhere.
/* First get the best path, and if it's dead (and this is not a root)
* we attempt to re-activate that path but this packet will flow
* upstream. If the path comes back alive, it will be used in the future.
* For roots we don't do the alive check since roots are not required
* to send heartbeats "down" and because we have to at least try to
* go somewhere. */
SharedPtr<Path> viaPath(peer->getBestPath(now,false));
if ( (viaPath) && (!viaPath->alive(now)) && (!RR->topology->isRoot(peer->identity())) ) {
@ -724,7 +724,8 @@ bool Switch::_trySend(const Packet &packet,bool encrypt)
viaPath.zero();
}
if (!viaPath) {
SharedPtr<Peer> relay(RR->topology->getUpstreamPeer());
peer->tryMemorizedPath(now); // periodically attempt memorized or statically defined paths, if any are known
const SharedPtr<Peer> relay(RR->topology->getUpstreamPeer());
if ( (!relay) || (!(viaPath = relay->getBestPath(now,false))) ) {
if (!(viaPath = peer->getBestPath(now,true)))
return false;

View File

@ -191,32 +191,23 @@ SharedPtr<Peer> Topology::getUpstreamPeer(const Address *avoid,unsigned int avoi
for(std::vector<Address>::const_iterator a(_upstreamAddresses.begin());a!=_upstreamAddresses.end();++a) {
const SharedPtr<Peer> *p = _peers.get(*a);
if (!p) {
const Identity id(_getIdentity(*a));
if (id) {
p = &(_peers.set(*a,SharedPtr<Peer>(new Peer(RR,RR->identity,id))));
} else {
RR->sw->requestWhois(*a);
if (p) {
bool avoiding = false;
for(unsigned int i=0;i<avoidCount;++i) {
if (avoid[i] == (*p)->address()) {
avoiding = true;
break;
}
}
continue; // always skip since even if we loaded it, it's not going to be ready
}
bool avoiding = false;
for(unsigned int i=0;i<avoidCount;++i) {
if (avoid[i] == (*p)->address()) {
avoiding = true;
break;
const unsigned int q = (*p)->relayQuality(now);
if (q <= bestQualityOverall) {
bestQualityOverall = q;
bestOverall = &(*p);
}
if ((!avoiding)&&(q <= bestQualityNotAvoid)) {
bestQualityNotAvoid = q;
bestNotAvoid = &(*p);
}
}
const unsigned int q = (*p)->relayQuality(now);
if (q <= bestQualityOverall) {
bestQualityOverall = q;
bestOverall = &(*p);
}
if ((!avoiding)&&(q <= bestQualityNotAvoid)) {
bestQualityNotAvoid = q;
bestNotAvoid = &(*p);
}
}
@ -245,31 +236,35 @@ bool Topology::isUpstream(const Identity &id) const
void Topology::setUpstream(const Address &a,bool upstream)
{
Mutex::Lock _l(_lock);
if (std::find(_rootAddresses.begin(),_rootAddresses.end(),a) == _rootAddresses.end()) {
if (upstream) {
if (std::find(_upstreamAddresses.begin(),_upstreamAddresses.end(),a) == _upstreamAddresses.end()) {
_upstreamAddresses.push_back(a);
const SharedPtr<Peer> *p = _peers.get(a);
if (!p) {
const Identity id(_getIdentity(a));
if (id) {
_peers.set(a,SharedPtr<Peer>(new Peer(RR,RR->identity,id)));
} else {
RR->sw->requestWhois(a);
bool needWhois = false;
{
Mutex::Lock _l(_lock);
if (std::find(_rootAddresses.begin(),_rootAddresses.end(),a) == _rootAddresses.end()) {
if (upstream) {
if (std::find(_upstreamAddresses.begin(),_upstreamAddresses.end(),a) == _upstreamAddresses.end()) {
_upstreamAddresses.push_back(a);
const SharedPtr<Peer> *p = _peers.get(a);
if (!p) {
const Identity id(_getIdentity(a));
if (id) {
_peers.set(a,SharedPtr<Peer>(new Peer(RR,RR->identity,id)));
} else {
needWhois = true; // need to do this later due to _lock
}
}
}
} else {
std::vector<Address> ua;
for(std::vector<Address>::iterator i(_upstreamAddresses.begin());i!=_upstreamAddresses.end();++i) {
if (a != *i)
ua.push_back(*i);
}
_upstreamAddresses.swap(ua);
}
} else {
std::vector<Address> ua;
for(std::vector<Address>::iterator i(_upstreamAddresses.begin());i!=_upstreamAddresses.end();++i) {
if (a != *i)
ua.push_back(*i);
}
_upstreamAddresses.swap(ua);
}
}
if (needWhois)
RR->sw->requestWhois(a);
}
bool Topology::worldUpdateIfValid(const World &newWorld)

View File

@ -123,6 +123,7 @@ static void _jsonAppend(unsigned int depth,std::string &buf,const ZT_VirtualNetw
Utils::snprintf(json,sizeof(json),
"%s{\n"
"%s\t\"id\": \"%.16llx\",\n"
"%s\t\"nwid\": \"%.16llx\",\n"
"%s\t\"mac\": \"%.2x:%.2x:%.2x:%.2x:%.2x:%.2x\",\n"
"%s\t\"name\": \"%s\",\n"
@ -143,6 +144,7 @@ static void _jsonAppend(unsigned int depth,std::string &buf,const ZT_VirtualNetw
"%s}",
prefix,
prefix,nc->nwid,
prefix,nc->nwid,
prefix,(unsigned int)((nc->mac >> 40) & 0xff),(unsigned int)((nc->mac >> 32) & 0xff),(unsigned int)((nc->mac >> 24) & 0xff),(unsigned int)((nc->mac >> 16) & 0xff),(unsigned int)((nc->mac >> 8) & 0xff),(unsigned int)(nc->mac & 0xff),
prefix,_jsonEscape(nc->name).c_str(),
prefix,nstatus,
@ -214,9 +216,9 @@ static void _jsonAppend(unsigned int depth,std::string &buf,const ZT_Peer *peer)
const char *prole = "";
switch(peer->role) {
case ZT_PEER_ROLE_LEAF: prole = "LEAF"; break;
case ZT_PEER_ROLE_LEAF: prole = "LEAF"; break;
case ZT_PEER_ROLE_UPSTREAM: prole = "UPSTREAM"; break;
case ZT_PEER_ROLE_ROOT: prole = "ROOT"; break;
case ZT_PEER_ROLE_ROOT: prole = "ROOT"; break;
}
Utils::snprintf(json,sizeof(json),
@ -389,6 +391,7 @@ unsigned int ControlPlane::handleRequest(
"\t\"worldId\": %llu,\n"
"\t\"worldTimestamp\": %llu,\n"
"\t\"online\": %s,\n"
"\t\"relayPolicy\": \"%s\",\n"
"\t\"tcpFallbackActive\": %s,\n"
"\t\"versionMajor\": %d,\n"
"\t\"versionMinor\": %d,\n"
@ -402,6 +405,7 @@ unsigned int ControlPlane::handleRequest(
status.worldId,
status.worldTimestamp,
(status.online) ? "true" : "false",
((status.relayPolicy == ZT_RELAY_POLICY_ALWAYS) ? "always" : ((status.relayPolicy == ZT_RELAY_POLICY_NEVER) ? "never" : "trusted")),
(_svc->tcpFallbackActive()) ? "true" : "false",
ZEROTIER_ONE_VERSION_MAJOR,
ZEROTIER_ONE_VERSION_MINOR,
@ -411,7 +415,7 @@ unsigned int ControlPlane::handleRequest(
((clusterJson.length() > 0) ? clusterJson.c_str() : "null"));
responseBody = json;
scode = 200;
} else if (ps[0] == "config") {
} else if (ps[0] == "settings") {
responseContentType = "application/json";
responseBody = "{}"; // TODO
scode = 200;
@ -497,7 +501,7 @@ unsigned int ControlPlane::handleRequest(
if (isAuth) {
if (ps[0] == "config") {
if (ps[0] == "settings") {
// TODO
} else if (ps[0] == "network") {
if (ps.size() == 2) {
@ -513,11 +517,11 @@ unsigned int ControlPlane::handleRequest(
try {
nlohmann::json j(nlohmann::json::parse(body));
if (j.is_object()) {
auto allowManaged = j["allowManaged"];
nlohmann::json &allowManaged = j["allowManaged"];
if (allowManaged.is_boolean()) localSettings.allowManaged = (bool)allowManaged;
auto allowGlobal = j["allowGlobal"];
nlohmann::json &allowGlobal = j["allowGlobal"];
if (allowGlobal.is_boolean()) localSettings.allowGlobal = (bool)allowGlobal;
auto allowDefault = j["allowDefault"];
nlohmann::json &allowDefault = j["allowDefault"];
if (allowDefault.is_boolean()) localSettings.allowDefault = (bool)allowDefault;
}
} catch ( ... ) {
@ -548,9 +552,7 @@ unsigned int ControlPlane::handleRequest(
if (isAuth) {
if (ps[0] == "config") {
// TODO
} else if (ps[0] == "network") {
if (ps[0] == "network") {
ZT_VirtualNetworkList *nws = _node->networks();
if (nws) {
if (ps.size() == 2) {

View File

@ -31,12 +31,6 @@
#include "../version.h"
#include "../include/ZeroTierOne.h"
#ifdef ZT_USE_SYSTEM_HTTP_PARSER
#include <http_parser.h>
#else
#include "../ext/http-parser/http_parser.h"
#endif
#include "../node/Constants.hpp"
#include "../node/Mutex.hpp"
#include "../node/Node.hpp"
@ -59,6 +53,16 @@
#include "ClusterGeoIpService.hpp"
#include "ClusterDefinition.hpp"
#ifdef ZT_USE_SYSTEM_HTTP_PARSER
#include <http_parser.h>
#else
#include "../ext/http-parser/http_parser.h"
#endif
#include "../ext/json/json.hpp"
using json = nlohmann::json;
/**
* Uncomment to enable UDP breakage switch
*
@ -144,6 +148,52 @@ namespace ZeroTier {
namespace {
static uint64_t _jI(const json &jv,const uint64_t dfl)
{
if (jv.is_number()) {
return (uint64_t)jv;
} else if (jv.is_string()) {
std::string s = jv;
return Utils::strToU64(s.c_str());
} else if (jv.is_boolean()) {
return ((bool)jv ? 1ULL : 0ULL);
}
return dfl;
}
static bool _jB(const json &jv,const bool dfl)
{
if (jv.is_boolean()) {
return (bool)jv;
} else if (jv.is_number()) {
return ((uint64_t)jv > 0ULL);
} else if (jv.is_string()) {
std::string s = jv;
if (s.length() > 0) {
switch(s[0]) {
case 't':
case 'T':
case '1':
return true;
}
}
return false;
}
return dfl;
}
static std::string _jS(const json &jv,const char *dfl)
{
if (jv.is_string()) {
return jv;
} else if (jv.is_number()) {
char tmp[64];
Utils::snprintf(tmp,sizeof(tmp),"%llu",(uint64_t)jv);
return tmp;
} else if (jv.is_boolean()) {
return ((bool)jv ? std::string("1") : std::string("0"));
}
return std::string((dfl) ? dfl : "");
}
#ifdef ZT_AUTO_UPDATE
#define ZT_AUTO_UPDATE_MAX_HTTP_RESPONSE_SIZE (1024 * 1024 * 64)
#define ZT_AUTO_UPDATE_CHECK_PERIOD 21600000
@ -400,7 +450,8 @@ static long SnodeDataStoreGetFunction(ZT_Node *node,void *uptr,const char *name,
static int SnodeDataStorePutFunction(ZT_Node *node,void *uptr,const char *name,const void *data,unsigned long len,int secure);
static int SnodeWirePacketSendFunction(ZT_Node *node,void *uptr,const struct sockaddr_storage *localAddr,const struct sockaddr_storage *addr,const void *data,unsigned int len,unsigned int ttl);
static void SnodeVirtualNetworkFrameFunction(ZT_Node *node,void *uptr,uint64_t nwid,void **nuptr,uint64_t sourceMac,uint64_t destMac,unsigned int etherType,unsigned int vlanId,const void *data,unsigned int len);
static int SnodePathCheckFunction(ZT_Node *node,void *uptr,const struct sockaddr_storage *localAddr,const struct sockaddr_storage *remoteAddr);
static int SnodePathCheckFunction(ZT_Node *node,void *uptr,uint64_t ztaddr,const struct sockaddr_storage *localAddr,const struct sockaddr_storage *remoteAddr);
static int SnodePathLookupFunction(ZT_Node *node,void *uptr,uint64_t ztaddr,int family,struct sockaddr_storage *result);
#ifdef ZT_ENABLE_CLUSTER
static void SclusterSendFunction(void *uptr,unsigned int toMemberId,const void *data,unsigned int len);
@ -488,6 +539,16 @@ public:
Phy<OneServiceImpl *> _phy;
Node *_node;
// Local configuration and memo-ized static path definitions
json _localConfig;
Hashtable< uint64_t,std::vector<InetAddress> > _v4Hints;
Hashtable< uint64_t,std::vector<InetAddress> > _v6Hints;
Hashtable< uint64_t,std::vector<InetAddress> > _v4Blacklists;
Hashtable< uint64_t,std::vector<InetAddress> > _v6Blacklists;
std::vector< InetAddress > _globalV4Blacklist;
std::vector< InetAddress > _globalV6Blacklist;
Mutex _localConfig_m;
/*
* To attempt to handle NAT/gateway craziness we use three local UDP ports:
*
@ -499,7 +560,6 @@ public:
* destructively with uPnP port mapping behavior in very weird buggy ways.
* It's only used if uPnP/NAT-PMP is enabled in this build.
*/
Binder _bindings[3];
unsigned int _ports[3];
uint16_t _portsBE[3]; // ports in big-endian network byte order as in sockaddr
@ -703,16 +763,19 @@ public:
// Clean up any legacy files if present
OSUtils::rm((_homePath + ZT_PATH_SEPARATOR_S + "peers.save").c_str());
_node = new Node(
OSUtils::now(),
this,
SnodeDataStoreGetFunction,
SnodeDataStorePutFunction,
SnodeWirePacketSendFunction,
SnodeVirtualNetworkFrameFunction,
SnodeVirtualNetworkConfigFunction,
SnodePathCheckFunction,
SnodeEventCallback);
{
struct ZT_Node_Callbacks cb;
cb.version = 0;
cb.dataStoreGetFunction = SnodeDataStoreGetFunction;
cb.dataStorePutFunction = SnodeDataStorePutFunction;
cb.wirePacketSendFunction = SnodeWirePacketSendFunction;
cb.virtualNetworkFrameFunction = SnodeVirtualNetworkFrameFunction;
cb.virtualNetworkConfigFunction = SnodeVirtualNetworkConfigFunction;
cb.eventCallback = SnodeEventCallback;
cb.pathCheckFunction = SnodePathCheckFunction;
cb.pathLookupFunction = SnodePathLookupFunction;
_node = new Node(this,&cb,OSUtils::now());
}
// Attempt to bind to a secondary port chosen from our ZeroTier address.
// This exists because there are buggy NATs out there that fail if more
@ -758,13 +821,15 @@ public:
_portsBE[i] = Utils::hton((uint16_t)_ports[i]);
{
uint64_t trustedPathIds[ZT_MAX_TRUSTED_PATHS];
InetAddress trustedPathNetworks[ZT_MAX_TRUSTED_PATHS];
unsigned int trustedPathCount = 0;
// Old style "trustedpaths" flat file -- will eventually go away
FILE *trustpaths = fopen((_homePath + ZT_PATH_SEPARATOR_S + "trustedpaths").c_str(),"r");
uint64_t ids[ZT_MAX_TRUSTED_PATHS];
InetAddress addresses[ZT_MAX_TRUSTED_PATHS];
if (trustpaths) {
char buf[1024];
unsigned int count = 0;
while ((fgets(buf,sizeof(buf),trustpaths))&&(count < ZT_MAX_TRUSTED_PATHS)) {
while ((fgets(buf,sizeof(buf),trustpaths))&&(trustedPathCount < ZT_MAX_TRUSTED_PATHS)) {
int fno = 0;
char *saveptr = (char *)0;
uint64_t trustedPathId = 0;
@ -778,16 +843,53 @@ public:
++fno;
}
if ( (trustedPathId != 0) && ((trustedPathNetwork.ss_family == AF_INET)||(trustedPathNetwork.ss_family == AF_INET6)) && (trustedPathNetwork.ipScope() != InetAddress::IP_SCOPE_GLOBAL) && (trustedPathNetwork.netmaskBits() > 0) ) {
ids[count] = trustedPathId;
addresses[count] = trustedPathNetwork;
++count;
trustedPathIds[trustedPathCount] = trustedPathId;
trustedPathNetworks[trustedPathCount] = trustedPathNetwork;
++trustedPathCount;
}
}
fclose(trustpaths);
if (count)
_node->setTrustedPaths(reinterpret_cast<const struct sockaddr_storage *>(addresses),ids,count);
}
// Read local config file
Mutex::Lock _l2(_localConfig_m);
std::string lcbuf;
if (OSUtils::readFile((_homePath + ZT_PATH_SEPARATOR_S + "local.conf").c_str(),lcbuf)) {
try {
_localConfig = json::parse(lcbuf);
if (!_localConfig.is_object()) {
fprintf(stderr,"WARNING: unable to parse local.conf (root element is not a JSON object)" ZT_EOL_S);
}
} catch ( ... ) {
fprintf(stderr,"WARNING: unable to parse local.conf (invalid JSON)" ZT_EOL_S);
}
}
// Get any trusted paths in local.conf (we'll parse the rest of physical[] elsewhere)
json &physical = _localConfig["physical"];
if (physical.is_object()) {
for(json::iterator phy(physical.begin());phy!=physical.end();++phy) {
InetAddress net(_jS(phy.key(),""));
if (net) {
if (phy.value().is_object()) {
uint64_t tpid;
if ((tpid = _jI(phy.value()["trustedPathId"],0ULL)) != 0ULL) {
if ( ((net.ss_family == AF_INET)||(net.ss_family == AF_INET6)) && (trustedPathCount < ZT_MAX_TRUSTED_PATHS) && (net.ipScope() != InetAddress::IP_SCOPE_GLOBAL) && (net.netmaskBits() > 0) ) {
trustedPathIds[trustedPathCount] = tpid;
trustedPathNetworks[trustedPathCount] = net;
++trustedPathCount;
}
}
}
}
}
}
// Set trusted paths if there are any
if (trustedPathCount)
_node->setTrustedPaths(reinterpret_cast<const struct sockaddr_storage *>(trustedPathNetworks),trustedPathIds,trustedPathCount);
}
applyLocalConfig();
_controller = new EmbeddedNetworkController(_node,(_homePath + ZT_PATH_SEPARATOR_S + ZT_CONTROLLER_DB_PATH).c_str());
_node->setNetconfMaster((void *)_controller);
@ -806,7 +908,7 @@ public:
Mutex::Lock _l(_termReason_m);
_termReason = ONE_UNRECOVERABLE_ERROR;
_fatalErrorMessage = "Cluster: can't determine my cluster member ID: able to bind more than one cluster message socket IP/port!";
_fatalErrorMessage = "cluster: can't determine my cluster member ID: able to bind more than one cluster message socket IP/port!";
return _termReason;
}
_clusterMessageSocket = cs;
@ -817,7 +919,7 @@ public:
if (!_clusterMessageSocket) {
Mutex::Lock _l(_termReason_m);
_termReason = ONE_UNRECOVERABLE_ERROR;
_fatalErrorMessage = "Cluster: can't determine my cluster member ID: unable to bind to any cluster message socket IP/port.";
_fatalErrorMessage = "cluster: can't determine my cluster member ID: unable to bind to any cluster message socket IP/port.";
return _termReason;
}
@ -1059,7 +1161,92 @@ public:
return true;
}
// Begin private implementation methods
// Internal implementation methods -----------------------------------------
// Must be called after _localConfig is read or modified
void applyLocalConfig()
{
Mutex::Lock _l(_localConfig_m);
_v4Hints.clear();
_v6Hints.clear();
_v4Blacklists.clear();
_v6Blacklists.clear();
json &virt = _localConfig["virtual"];
if (virt.is_object()) {
for(json::iterator v(virt.begin());v!=virt.end();++v) {
const std::string nstr = v.key();
if ((nstr.length() == ZT_ADDRESS_LENGTH_HEX)&&(v.value().is_object())) {
const Address ztaddr(nstr.c_str());
if (ztaddr) {
const std::string rstr(_jS(v.value()["role"],""));
_node->setRole(ztaddr.toInt(),((rstr == "upstream")||(rstr == "UPSTREAM")) ? ZT_PEER_ROLE_UPSTREAM : ZT_PEER_ROLE_LEAF);
const uint64_t ztaddr2 = ztaddr.toInt();
std::vector<InetAddress> &v4h = _v4Hints[ztaddr2];
std::vector<InetAddress> &v6h = _v6Hints[ztaddr2];
std::vector<InetAddress> &v4b = _v4Blacklists[ztaddr2];
std::vector<InetAddress> &v6b = _v6Blacklists[ztaddr2];
json &tryAddrs = v.value()["try"];
if (tryAddrs.is_array()) {
for(unsigned long i=0;i<tryAddrs.size();++i) {
const InetAddress ip(_jS(tryAddrs[i],""));
if (ip.ss_family == AF_INET)
v4h.push_back(ip);
else if (ip.ss_family == AF_INET6)
v6h.push_back(ip);
}
}
json &blAddrs = v.value()["blacklist"];
if (blAddrs.is_array()) {
for(unsigned long i=0;i<blAddrs.size();++i) {
const InetAddress ip(_jS(tryAddrs[i],""));
if (ip.ss_family == AF_INET)
v4b.push_back(ip);
else if (ip.ss_family == AF_INET6)
v6b.push_back(ip);
}
}
if (v4h.empty()) _v4Hints.erase(ztaddr2);
if (v6h.empty()) _v6Hints.erase(ztaddr2);
if (v4b.empty()) _v4Blacklists.erase(ztaddr2);
if (v6b.empty()) _v6Blacklists.erase(ztaddr2);
}
}
}
}
_globalV4Blacklist.clear();
_globalV6Blacklist.clear();
json &physical = _localConfig["physical"];
if (physical.is_object()) {
for(json::iterator phy(physical.begin());phy!=physical.end();++phy) {
const InetAddress net(_jS(phy.key(),""));
if ((net)&&(net.netmaskBits() > 0)) {
if (phy.value().is_object()) {
if (_jB(phy.value()["blacklist"],false)) {
if (net.ss_family == AF_INET)
_globalV4Blacklist.push_back(net);
else if (net.ss_family == AF_INET6)
_globalV6Blacklist.push_back(net);
}
}
}
}
}
json &settings = _localConfig["settings"];
if (settings.is_object()) {
const std::string rp(_jS(settings["relayPolicy"],""));
if ((rp == "always")||(rp == "ALWAYS"))
_node->setRelayPolicy(ZT_RELAY_POLICY_ALWAYS);
else if ((rp == "never")||(rp == "NEVER"))
_node->setRelayPolicy(ZT_RELAY_POLICY_NEVER);
else _node->setRelayPolicy(ZT_RELAY_POLICY_TRUSTED);
}
}
// Checks if a managed IP or route target is allowed
bool checkIfManagedIsAllowed(const NetworkState &n,const InetAddress &target)
@ -1191,6 +1378,8 @@ public:
}
}
// Handlers for Node and Phy<> callbacks -----------------------------------
inline void phyOnDatagram(PhySocket *sock,void **uptr,const struct sockaddr *localAddr,const struct sockaddr *from,void *data,unsigned long len)
{
#ifdef ZT_ENABLE_CLUSTER
@ -1668,30 +1857,74 @@ public:
n->tap->put(MAC(sourceMac),MAC(destMac),etherType,data,len);
}
inline int nodePathCheckFunction(const struct sockaddr_storage *localAddr,const struct sockaddr_storage *remoteAddr)
inline int nodePathCheckFunction(uint64_t ztaddr,const struct sockaddr_storage *localAddr,const struct sockaddr_storage *remoteAddr)
{
Mutex::Lock _l(_nets_m);
for(std::map<uint64_t,NetworkState>::const_iterator n(_nets.begin());n!=_nets.end();++n) {
if (n->second.tap) {
std::vector<InetAddress> ips(n->second.tap->ips());
for(std::vector<InetAddress>::const_iterator i(ips.begin());i!=ips.end();++i) {
if (i->containsAddress(*(reinterpret_cast<const InetAddress *>(remoteAddr)))) {
return 0;
// Make sure we're not trying to do ZeroTier-over-ZeroTier
{
Mutex::Lock _l(_nets_m);
for(std::map<uint64_t,NetworkState>::const_iterator n(_nets.begin());n!=_nets.end();++n) {
if (n->second.tap) {
std::vector<InetAddress> ips(n->second.tap->ips());
for(std::vector<InetAddress>::const_iterator i(ips.begin());i!=ips.end();++i) {
if (i->containsAddress(*(reinterpret_cast<const InetAddress *>(remoteAddr)))) {
return 0;
}
}
}
}
}
/* Note: I do not think we need to scan for overlap with managed routes
* because of the "route forking" and interface binding that we do. This
* ensures (we hope) that ZeroTier traffic will still take the physical
* path even if its managed routes override this for other traffic. Will
* revisit if we see problems with this. */
* revisit if we see recursion problems. */
// Check blacklists
const Hashtable< uint64_t,std::vector<InetAddress> > *blh = (const Hashtable< uint64_t,std::vector<InetAddress> > *)0;
const std::vector<InetAddress> *gbl = (const std::vector<InetAddress> *)0;
if (remoteAddr->ss_family == AF_INET) {
blh = &_v4Blacklists;
gbl = &_globalV4Blacklist;
} else if (remoteAddr->ss_family == AF_INET6) {
blh = &_v6Blacklists;
gbl = &_globalV6Blacklist;
}
if (blh) {
Mutex::Lock _l(_localConfig_m);
const std::vector<InetAddress> *l = blh->get(ztaddr);
if (l) {
for(std::vector<InetAddress>::const_iterator a(l->begin());a!=l->end();++a) {
if (a->containsAddress(*reinterpret_cast<const InetAddress *>(remoteAddr)))
return 0;
}
}
for(std::vector<InetAddress>::const_iterator a(gbl->begin());a!=gbl->end();++a) {
if (a->containsAddress(*reinterpret_cast<const InetAddress *>(remoteAddr)))
return 0;
}
}
return 1;
}
inline int nodePathLookupFunction(uint64_t ztaddr,int family,struct sockaddr_storage *result)
{
const Hashtable< uint64_t,std::vector<InetAddress> > *lh = (const Hashtable< uint64_t,std::vector<InetAddress> > *)0;
if (family < 0)
lh = (_node->prng() & 1) ? &_v4Hints : &_v6Hints;
else if (family == AF_INET)
lh = &_v4Hints;
else if (family == AF_INET6)
lh = &_v6Hints;
else return 0;
const std::vector<InetAddress> *l = lh->get(ztaddr);
if ((l)&&(l->size() > 0)) {
memcpy(result,&((*l)[(unsigned long)_node->prng() % l->size()]),sizeof(struct sockaddr_storage));
return 1;
} else return 0;
}
inline void tapFrameHandler(uint64_t nwid,const MAC &from,const MAC &to,unsigned int etherType,unsigned int vlanId,const void *data,unsigned int len)
{
_node->processVirtualNetworkFrame(OSUtils::now(),nwid,from.toInt(),to.toInt(),etherType,vlanId,data,len,&_nextBackgroundTaskDeadline);
@ -1841,8 +2074,10 @@ static int SnodeWirePacketSendFunction(ZT_Node *node,void *uptr,const struct soc
{ return reinterpret_cast<OneServiceImpl *>(uptr)->nodeWirePacketSendFunction(localAddr,addr,data,len,ttl); }
static void SnodeVirtualNetworkFrameFunction(ZT_Node *node,void *uptr,uint64_t nwid,void **nuptr,uint64_t sourceMac,uint64_t destMac,unsigned int etherType,unsigned int vlanId,const void *data,unsigned int len)
{ reinterpret_cast<OneServiceImpl *>(uptr)->nodeVirtualNetworkFrameFunction(nwid,nuptr,sourceMac,destMac,etherType,vlanId,data,len); }
static int SnodePathCheckFunction(ZT_Node *node,void *uptr,const struct sockaddr_storage *localAddr,const struct sockaddr_storage *remoteAddr)
{ return reinterpret_cast<OneServiceImpl *>(uptr)->nodePathCheckFunction(localAddr,remoteAddr); }
static int SnodePathCheckFunction(ZT_Node *node,void *uptr,uint64_t ztaddr,const struct sockaddr_storage *localAddr,const struct sockaddr_storage *remoteAddr)
{ return reinterpret_cast<OneServiceImpl *>(uptr)->nodePathCheckFunction(ztaddr,localAddr,remoteAddr); }
static int SnodePathLookupFunction(ZT_Node *node,void *uptr,uint64_t ztaddr,int family,struct sockaddr_storage *result)
{ return reinterpret_cast<OneServiceImpl *>(uptr)->nodePathLookupFunction(ztaddr,family,result); }
#ifdef ZT_ENABLE_CLUSTER
static void SclusterSendFunction(void *uptr,unsigned int toMemberId,const void *data,unsigned int len)

View File

@ -1,9 +1,62 @@
ZeroTier One Network Virtualization Service
======
This is the common background service implementation for ZeroTier One, the VPN-like OS-level network virtualization service.
This is the actual implementation of ZeroTier One, a service providing connectivity to ZeroTier virtual networks for desktops, laptops, servers, VMs, etc. (Mobile versions for iOS and Android have their own implementations in native Java and Objective C that leverage only the ZeroTier core engine.)
It provides a ready-made core I/O loop and a local HTTP-based JSON control bus for controlling the service. This control bus HTTP server can also serve the files in ui/ if this folder's contents are installed in the ZeroTier home folder. The ui/ implements a React-based HTML5 user interface which is then wrappered for various platforms via MacGap, Windows .NET WebControl, etc. It can also be used locally from scripts or via *curl*.
### Local Configuration File
A file called `local.conf` in the ZeroTier home folder contains configuration options that apply to the local node. It can be used to set up trusted paths, blacklist physical paths, set up physical path hints for certain nodes, and define trusted upstream devices (federated roots). In a large deployment it can be deployed using a tool like Puppet, Chef, SaltStack, etc. to set a uniform configuration across systems. It's a JSON format file that can also be edited and rewritten by ZeroTier One itself, so ensure that proper JSON formatting is used.
Settings available in `local.conf` (this is not valid JSON, and JSON does not allow comments):
```javascript
{
"physical": { /* Settings that apply to physical L2/L3 network paths. */
"NETWORK/bits": { /* Network e.g. 10.0.0.0/24 or fd00::/32 */
"blacklist": true|false, /* If true, blacklist this path for all ZeroTier traffic */
"trustedPathId": 0|!0 /* If present and nonzero, define this as a trusted path (see below) */
} /* ,... additional networks */
},
"virtual": { /* Settings applied to ZeroTier virtual network devices (VL1) */
"##########": { /* 10-digit ZeroTier address */
"role": "UPSTREAM"|"LEAF", /* If UPSTREAM, define this as a trusted "federated root" */
"try": [ "IP/port"/*,...*/ ], /* Hints on where to reach this peer if no upstreams/roots are online */
"blacklist": [ "NETWORK/bits"/*,...*/ ] /* Blacklist a physical path for only this peer. */
}
},
"settings": { /* Other global settings */
"relayPolicy": "TRUSTED"|"ALWAYS"|"NEVER" /* Policy for relaying others' traffic (see below) */
}
}
```
* **trustedPathId**: A trusted path is a physical network over which encryption and authentication are not required. This provides a performance boost but sacrifices all ZeroTier's security features when communicating over this path. Only use this if you know what you are doing and really need the performance! To set up a trusted path, all devices using it *MUST* have the *same trusted path ID* for the same network. Trusted path IDs are arbitrary positive non-zero integers. For example a group of devices on a LAN with IPs in 10.0.0.0/24 could use it as a fast trusted path if they all had the same trusted path ID of "25" defined for that network.
* **relayPolicy**: Under what circumstances should this device relay traffic for other devices? The default is TRUSTED, meaning that we'll only relay for devices we know to be members of a network we have joined. NEVER is the default on mobile devices (iOS/Android) and tells us to never relay traffic. ALWAYS is usually only set for upstreams and roots, allowing them to act as promiscuous relays for anyone who desires it.
An example `local.conf`:
```javascript
{
"physical": {
"10.0.0.0/24": {
"blacklist": true
},
"10.10.10.0/24": {
"trustedPathId": 101010024
},
},
"virtual": {
"feedbeef12": {
"role": "UPSTREAM",
"try": [ "10.10.20.1/9993" ],
"blacklist": [ "192.168.0.0/24" ]
}
},
"settings": {
"relayPolicy": "ALWAYS"
}
}
```
### Network Virtualization Service API
@ -21,32 +74,20 @@ A *jsonp* URL argument may be supplied to request JSONP encapsulation. A JSONP r
* Methods: GET
* Returns: { object }
<table>
<tr><td><b>Field</b></td><td><b>Type</b></td><td><b>Description</b></td><td><b>Writable</b></td></tr>
<tr><td>address</td><td>string</td><td>10-digit hexadecimal ZeroTier address of this node</td><td>no</td></tr>
<tr><td>publicIdentity</td><td>string</td><td>Full public ZeroTier identity of this node</td><td>no</td></tr>
<tr><td>worldId</td><td>integer</td><td>Fixed value representing the virtual data center of Earth.</td><td>no</td></tr>
<tr><td>worldTimestamp</td><td>integer</td><td>Timestamp of the last root server topology change.</td><td>no</td></tr>
<tr><td>online</td><td>boolean</td><td>Does this node appear to have upstream network access?</td><td>no</td></tr>
<tr><td>tcpFallbackActive</td><td>boolean</td><td>Is TCP fallback mode active?</td><td>no</td></tr>
<tr><td>versionMajor</td><td>integer</td><td>ZeroTier major version</td><td>no</td></tr>
<tr><td>versionMinor</td><td>integer</td><td>ZeroTier minor version</td><td>no</td></tr>
<tr><td>versionRev</td><td>integer</td><td>ZeroTier revision</td><td>no</td></tr>
<tr><td>version</td><td>string</td><td>Version in major.minor.rev format</td><td>no</td></tr>
<tr><td>clock</td><td>integer</td><td>Node system clock in ms since epoch</td><td>no</td></tr>
</table>
#### /config
* Purpose: Get or set local configuration
* Methods: GET, POST
* Returns: { object }
No local configuration options are exposed yet.
<table>
<tr><td><b>Field</b></td><td><b>Type</b></td><td><b>Description</b></td><td><b>Writable</b></td></tr>
</table>
| Field | Type | Description | Writable |
| --------------------- | ------------- | ------------------------------------------------- | -------- |
| address | string | 10-digit hex ZeroTier address of this node | no |
| publicIdentity | string | This node's ZeroTier identity.public | no |
| worldId | integer | ZeroTier world ID (never changes except for test) | no |
| worldTimestamp | integer | Timestamp of most recent world definition | no |
| online | boolean | If true at least one upstream peer is reachable | no |
| tcpFallbackActive | boolean | If true we are using slow TCP fallback | no |
| relayPolicy | string | Relay policy: ALWAYS, TRUSTED, or NEVER | no |
| versionMajor | integer | Software major version | no |
| versionMinor | integer | Software minor version | no |
| versionRev | integer | Software revision | no |
| version | string | major.minor.revision | no |
| clock | integer | Current system clock at node (ms since epoch) | no |
#### /network
@ -66,36 +107,35 @@ To join a network, POST to it. Since networks have no mandatory writable paramet
Most network settings are not writable, as they are defined by the network controller.
<table>
<tr><td><b>Field</b></td><td><b>Type</b></td><td><b>Description</b></td><td><b>Writable</b></td></tr>
<tr><td>nwid</td><td>string</td><td>16-digit hex network ID</td><td>no</td></tr>
<tr><td>mac</td><td>string</td><td>Ethernet MAC address of virtual network port</td><td>no</td></tr>
<tr><td>name</td><td>string</td><td>Network short name as configured on network controller</td><td>no</td></tr>
<tr><td>status</td><td>string</td><td>Network status: OK, ACCESS_DENIED, PORT_ERROR, etc.</td><td>no</td></tr>
<tr><td>type</td><td>string</td><td>Network type, currently PUBLIC or PRIVATE</td><td>no</td></tr>
<tr><td>mtu</td><td>integer</td><td>Ethernet MTU</td><td>no</td></tr>
<tr><td>dhcp</td><td>boolean</td><td>If true, DHCP may be used to obtain an IP address</td><td>no</td></tr>
<tr><td>bridge</td><td>boolean</td><td>If true, this node may bridge in other Ethernet devices</td><td>no</td></tr>
<tr><td>broadcastEnabled</td><td>boolean</td><td>Is Ethernet broadcast (ff:ff:ff:ff:ff:ff) allowed?</td><td>no</td></tr>
<tr><td>portError</td><td>integer</td><td>Error code (if any) returned by underlying OS "tap" driver</td><td>no</td></tr>
<tr><td>netconfRevision</td><td>integer</td><td>Network configuration revision ID</td><td>no</td></tr>
<tr><td>assignedAddresses</td><td>[string]</td><td>ZeroTier-managed IP address assignments as array of IP/netmask bits tuples</td><td>no</td></tr>
<tr><td>routes</td><td>[route]</td><td>ZeroTier-managed route assignments for a network. See below for a description of the route object.</td><td>no</td></tr>
<tr><td>portDeviceName</td><td>string</td><td>OS-specific network device name (if available)</td><td>no</td></tr>
<tr><td>allowManaged</td><td>boolean</td><td>Whether ZeroTier-managed IP addresses are allowed.</td><td>yes</td></tr>
<tr><td>allowGlobal</td><td>boolean</td><td>Whether globally-reachable IP addresses are allowed to be assigned.</td><td>yes</td></tr>
<tr><td>allowDefault</td><td>boolean</td><td>Whether a default route is allowed to be assigned for the network (route all traffic via ZeroTier)</td><td>yes</td></tr>
</table>
| Field | Type | Description | Writable |
| --------------------- | ------------- | ------------------------------------------------- | -------- |
| id | string | 16-digit hex network ID | no |
| nwid | string | 16-digit hex network ID (legacy field) | no |
| mac | string | MAC address of network device for this network | no |
| name | string | Short name of this network (from controller) | no |
| status | string | Network status (OK, ACCESS_DENIED, etc.) | no |
| type | string | Network type (PUBLIC or PRIVATE) | no |
| mtu | integer | Ethernet MTU | no |
| dhcp | boolean | If true, DHCP should be used to get IP info | no |
| bridge | boolean | If true, this device can bridge others | no |
| broadcastEnabled | boolean | If true ff:ff:ff:ff:ff:ff broadcasts work | no |
| portError | integer | Error code returned by underlying tap driver | no |
| netconfRevision | integer | Network configuration revision ID | no |
| assignedAddresses | [string] | Array of ZeroTier-assigned IP addresses (/bits) | no |
| routes | [object] | Array of ZeroTier-assigned routes (see below) | no |
| portDeviceName | string | Name of virtual network device (if any) | no |
| allowManaged | boolean | Allow IP and route management | yes |
| allowGlobal | boolean | Allow IPs and routes that overlap with global IPs | yes |
| allowDefault | boolean | Allow overriding of system default route | yes |
`route` objects
Route objects:
<table>
<tr><td><b>Field</b></td><td><b>Type</b></td><td><b>Description</b></td><td><b>Writable</b></td></tr>
<tr><td>target</td><td>string</td><td>Target network / netmask bits, NULL, or 0.0.0.0/0 for default route</td><td>no</td></tr>
<tr><td>via</td><td>string</td><td>Gateway IP address</td><td>no</td></tr>
<tr><td>flags</td><td>integer</td><td>Route flags</td><td>no</td></tr>
<tr><td>metric</td><td>integer</td><td>Route metric (not currently used)</td><td>no</td></tr>
</table>
| Field | Type | Description | Writable |
| --------------------- | ------------- | ------------------------------------------------- | -------- |
| target | string | Target network / netmask bits | no |
| via | string | Gateway IP address (next hop) or null for LAN | no |
| flags | integer | Flags, currently always 0 | no |
| metric | integer | Route metric (not currently used) | no |
#### /peer
@ -107,29 +147,29 @@ Getting /peer returns an array of peer objects for all current peers. See below
#### /peer/\<address\>
* Purpose: Get information about a peer
* Methods: GET
* Purpose: Get or set information about a peer
* Methods: GET, POST
* Returns: { object }
<table>
<tr><td><b>Field</b></td><td><b>Type</b></td><td><b>Description</b></td><td><b>Writable</b></td></tr>
<tr><td>address</td><td>string</td><td>10-digit hex ZeroTier address</td><td>no</td></tr>
<tr><td>versionMajor</td><td>integer</td><td>Major version of remote if known</td><td>no</td></tr>
<tr><td>versionMinor</td><td>integer</td><td>Minor version of remote if known</td><td>no</td></tr>
<tr><td>versionRev</td><td>integer</td><td>Revision of remote if known</td><td>no</td></tr>
<tr><td>version</td><td>string</td><td>Version in major.minor.rev format</td><td>no</td></tr>
<tr><td>latency</td><td>integer</td><td>Latency in milliseconds if known</td><td>no</td></tr>
<tr><td>role</td><td>string</td><td>LEAF, HUB, or ROOTSERVER</td><td>no</td></tr>
<tr><td>paths</td><td>[object]</td><td>Array of path objects (see below)</td><td>no</td></tr>
</table>
| Field | Type | Description | Writable |
| --------------------- | ------------- | ------------------------------------------------- | -------- |
| address | string | 10-digit hex ZeroTier address of peer | no |
| versionMajor | integer | Major version of remote (if known) | no |
| versionMinor | integer | Minor version of remote (if known) | no |
| versionRev | integer | Software revision of remote (if known) | no |
| version | string | major.minor.revision | no |
| latency | integer | Latency in milliseconds if known | no |
| role | string | LEAF, UPSTREAM, or ROOT | no |
| paths | [object] | Currently active physical paths (see below) | no |
Path objects describe direct physical paths to peer. If no path objects are listed, peer is only reachable via indirect relay fallback. Path object format is:
Path objects:
<table>
<tr><td><b>Field</b></td><td><b>Type</b></td><td><b>Description</b></td><td><b>Writable</b></td></tr>
<tr><td>address</td><td>string</td><td>Physical socket address e.g. IP/port for UDP</td><td>no</td></tr>
<tr><td>lastSend</td><td>integer</td><td>Last send via this path in ms since epoch</td><td>no</td></tr>
<tr><td>lastReceive</td><td>integer</td><td>Last receive via this path in ms since epoch</td><td>no</td></tr>
<tr><td>fixed</td><td>boolean</td><td>If true, this is a statically-defined "fixed" path</td><td>no</td></tr>
<tr><td>preferred</td><td>boolean</td><td>If true, this is the current preferred path</td><td>no</td></tr>
</table>
| Field | Type | Description | Writable |
| --------------------- | ------------- | ------------------------------------------------- | -------- |
| address | string | Physical socket address e.g. IP/port | no |
| lastSend | integer | Time of last send through this path | no |
| lastReceive | integer | Time of last receive through this path | no |
| active | boolean | Is this path in use? | no |
| expired | boolean | Is this path expired? | no |
| preferred | boolean | Is this a current preferred path? | no |
| trustedPathId | integer | If nonzero this is a trusted path (unencrypted) | no |