summaryrefslogtreecommitdiff
path: root/service
diff options
context:
space:
mode:
authorGrant Limberg <grant.limberg@zerotier.com>2016-11-30 10:21:19 -0800
committerGrant Limberg <grant.limberg@zerotier.com>2016-11-30 10:21:19 -0800
commit32478845b2df919d7546a889e49d276ea416a1f3 (patch)
tree57e4d52862e90222a540a733935d237a03fdbe52 /service
parenta39f6e9b4c3ccba677f58f3c0aafe9e6186c3031 (diff)
parent44f0e3097ee081b50628821b6db2b740489a89c6 (diff)
downloadinfinitytier-32478845b2df919d7546a889e49d276ea416a1f3.tar.gz
infinitytier-32478845b2df919d7546a889e49d276ea416a1f3.zip
Merge branch 'dev' into systemtray
Diffstat (limited to 'service')
-rw-r--r--service/ControlPlane.cpp22
-rw-r--r--service/OneService.cpp321
-rw-r--r--service/README.md206
3 files changed, 413 insertions, 136 deletions
diff --git a/service/ControlPlane.cpp b/service/ControlPlane.cpp
index 07304fc9..3bdfdf1b 100644
--- a/service/ControlPlane.cpp
+++ b/service/ControlPlane.cpp
@@ -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) {
diff --git a/service/OneService.cpp b/service/OneService.cpp
index 91063bad..6cfaeb0e 100644
--- a/service/OneService.cpp
+++ b/service/OneService.cpp
@@ -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)
diff --git a/service/README.md b/service/README.md
index f487f2bc..bd278752 100644
--- a/service/README.md
+++ b/service/README.md
@@ -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.
-
-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*.
+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.)
+
+### 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>
-
-`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 |
+| --------------------- | ------------- | ------------------------------------------------- | -------- |
+| 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:
+
+| 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>
-
-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:
-
-<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 | 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:
+
+| 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 |