summaryrefslogtreecommitdiff
path: root/service/OneService.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'service/OneService.cpp')
-rw-r--r--service/OneService.cpp715
1 files changed, 546 insertions, 169 deletions
diff --git a/service/OneService.cpp b/service/OneService.cpp
index 389cdc91..40ffce2f 100644
--- a/service/OneService.cpp
+++ b/service/OneService.cpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2019 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -13,7 +13,7 @@
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* --
*
@@ -34,6 +34,9 @@
#include <vector>
#include <algorithm>
#include <list>
+#include <thread>
+#include <mutex>
+#include <condition_variable>
#include "../version.h"
#include "../include/ZeroTierOne.h"
@@ -57,6 +60,7 @@
#include "../osdep/PortMapper.hpp"
#include "../osdep/Binder.hpp"
#include "../osdep/ManagedRoute.hpp"
+#include "../osdep/BlockingQueue.hpp"
#include "OneService.hpp"
#include "SoftwareUpdater.hpp"
@@ -67,9 +71,12 @@
#include <ShlObj.h>
#include <netioapi.h>
#include <iphlpapi.h>
+//#include <unistd.h>
+#define stat _stat
#else
#include <sys/types.h>
#include <sys/socket.h>
+#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>
#include <ifaddrs.h>
@@ -81,11 +88,18 @@
#include "../ext/http-parser/http_parser.h"
#endif
+#if ZT_VAULT_SUPPORT
+extern "C" {
+#include <curl/curl.h>
+}
+#endif
+
#include "../ext/json/json.hpp"
using json = nlohmann::json;
#include "../controller/EmbeddedNetworkController.hpp"
+#include "../controller/RabbitMQ.hpp"
#ifdef ZT_USE_TEST_TAP
@@ -99,14 +113,14 @@ namespace ZeroTier { typedef TestEthernetTap EthernetTap; }
#include "../controller/EmbeddedNetworkController.hpp"
#include "../node/Node.hpp"
// Use the virtual netcon endpoint instead of a tun/tap port driver
-#include "../include/VirtualTap.h"
+#include "../include/VirtualTap.hpp"
namespace ZeroTier { typedef VirtualTap EthernetTap; }
#else
#ifdef __APPLE__
-#include "../osdep/OSXEthernetTap.hpp"
-namespace ZeroTier { typedef OSXEthernetTap EthernetTap; }
+#include "../osdep/MacEthernetTap.hpp"
+namespace ZeroTier { typedef MacEthernetTap EthernetTap; }
#endif // __APPLE__
#ifdef __LINUX__
#include "../osdep/LinuxEthernetTap.hpp"
@@ -129,10 +143,14 @@ namespace ZeroTier { typedef NetBSDEthernetTap EthernetTap; }
namespace ZeroTier { typedef BSDEthernetTap EthernetTap; }
#endif // __OpenBSD__
-#endif // ZT_SERVICE_NETCON
+#endif // ZT_SDK
#endif // ZT_USE_TEST_TAP
+#ifndef ZT_SOFTWARE_UPDATE_DEFAULT
+#define ZT_SOFTWARE_UPDATE_DEFAULT "disable"
+#endif
+
// Sanity limits for HTTP
#define ZT_MAX_HTTP_MESSAGE_SIZE (1024 * 1024 * 64)
#define ZT_MAX_HTTP_CONNECTIONS 65536
@@ -145,7 +163,9 @@ namespace ZeroTier { typedef BSDEthernetTap EthernetTap; }
#define ZT_TAP_CHECK_MULTICAST_INTERVAL 5000
// TCP fallback relay (run by ZeroTier, Inc. -- this will eventually go away)
+#ifndef ZT_SDK
#define ZT_TCP_FALLBACK_RELAY "204.80.128.1/443"
+#endif
// Frequency at which we re-resolve the TCP fallback relay
#define ZT_TCP_FALLBACK_RERESOLVE_DELAY 86400000
@@ -162,10 +182,20 @@ namespace ZeroTier { typedef BSDEthernetTap EthernetTap; }
// TCP activity timeout
#define ZT_TCP_ACTIVITY_TIMEOUT 60000
+#if ZT_VAULT_SUPPORT
+size_t curlResponseWrite(void *ptr, size_t size, size_t nmemb, std::string *data)
+{
+ data->append((char*)ptr, size * nmemb);
+ return size * nmemb;
+}
+#endif
+
namespace ZeroTier {
namespace {
+static const InetAddress NULL_INET_ADDR;
+
// Fake TLS hello for TCP tunnel outgoing connections (TUNNELED mode)
static const char ZT_TCP_TUNNEL_HELLO[9] = { 0x17,0x03,0x03,0x00,0x04,(char)ZEROTIER_ONE_VERSION_MAJOR,(char)ZEROTIER_ONE_VERSION_MINOR,(char)((ZEROTIER_ONE_VERSION_REVISION >> 8) & 0xff),(char)(ZEROTIER_ONE_VERSION_REVISION & 0xff) };
@@ -283,6 +313,39 @@ static void _peerToJson(nlohmann::json &pj,const ZT_Peer *peer)
pj["paths"] = pa;
}
+static void _peerAggregateLinkToJson(nlohmann::json &pj,const ZT_Peer *peer)
+{
+ char tmp[256];
+ OSUtils::ztsnprintf(tmp,sizeof(tmp),"%.10llx",peer->address);
+ pj["aggregateLinkLatency"] = peer->latency;
+
+ nlohmann::json pa = nlohmann::json::array();
+ for(unsigned int i=0;i<peer->pathCount;++i) {
+ int64_t lastSend = peer->paths[i].lastSend;
+ int64_t lastReceive = peer->paths[i].lastReceive;
+ nlohmann::json j;
+ j["address"] = reinterpret_cast<const InetAddress *>(&(peer->paths[i].address))->toString(tmp);
+ j["lastSend"] = (lastSend < 0) ? 0 : lastSend;
+ j["lastReceive"] = (lastReceive < 0) ? 0 : lastReceive;
+ //j["trustedPathId"] = peer->paths[i].trustedPathId;
+ //j["active"] = (bool)(peer->paths[i].expired == 0);
+ //j["expired"] = (bool)(peer->paths[i].expired != 0);
+ //j["preferred"] = (bool)(peer->paths[i].preferred != 0);
+ j["latency"] = peer->paths[i].latency;
+ j["pdv"] = peer->paths[i].packetDelayVariance;
+ //j["throughputDisturbCoeff"] = peer->paths[i].throughputDisturbCoeff;
+ //j["packetErrorRatio"] = peer->paths[i].packetErrorRatio;
+ //j["packetLossRatio"] = peer->paths[i].packetLossRatio;
+ j["stability"] = peer->paths[i].stability;
+ j["throughput"] = peer->paths[i].throughput;
+ //j["maxThroughput"] = peer->paths[i].maxThroughput;
+ j["allocation"] = peer->paths[i].allocation;
+ j["ifname"] = peer->paths[i].ifname;
+ pa.push_back(j);
+ }
+ pj["paths"] = pa;
+}
+
static void _moonToJson(nlohmann::json &mj,const World &world)
{
char tmp[4096];
@@ -384,6 +447,15 @@ struct TcpConnection
Mutex writeq_m;
};
+struct OneServiceIncomingPacket
+{
+ uint64_t now;
+ int64_t sock;
+ struct sockaddr_storage from;
+ unsigned int size;
+ uint8_t data[ZT_MAX_MTU];
+};
+
class OneServiceImpl : public OneService
{
public:
@@ -403,7 +475,11 @@ public:
PhySocket *_localControlSocket6;
bool _updateAutoApply;
bool _allowTcpFallbackRelay;
+ bool _allowSecondaryPort;
+ unsigned int _multipathMode;
unsigned int _primaryPort;
+ unsigned int _secondaryPort;
+ unsigned int _tertiaryPort;
volatile unsigned int _udpPortPickerCounter;
// Local configuration and memo-ized information from it
@@ -418,11 +494,13 @@ public:
std::vector< std::string > _interfacePrefixBlacklist;
Mutex _localConfig_m;
+ std::vector<InetAddress> explicitBind;
+
/*
* To attempt to handle NAT/gateway craziness we use three local UDP ports:
*
* [0] is the normal/default port, usually 9993
- * [1] is a port dervied from our ZeroTier address
+ * [1] is a port derived from our ZeroTier address
* [2] is a port computed from the normal/default for use with uPnP/NAT-PMP mappings
*
* [2] exists because on some gateways trying to do regular NAT-t interferes
@@ -481,10 +559,20 @@ public:
PortMapper *_portMapper;
#endif
+ // HashiCorp Vault Settings
+#if ZT_VAULT_SUPPORT
+ bool _vaultEnabled;
+ std::string _vaultURL;
+ std::string _vaultToken;
+ std::string _vaultPath; // defaults to cubbyhole/zerotier/identity.secret for per-access key storage
+#endif
+
// Set to false to force service to stop
volatile bool _run;
Mutex _run_m;
+ MQConfig *_mqc;
+
// end member variables ----------------------------------------------------
OneServiceImpl(const char *hp,unsigned int port) :
@@ -513,11 +601,22 @@ public:
#ifdef ZT_USE_MINIUPNPC
,_portMapper((PortMapper *)0)
#endif
+#ifdef ZT_VAULT_SUPPORT
+ ,_vaultEnabled(false)
+ ,_vaultURL()
+ ,_vaultToken()
+ ,_vaultPath("cubbyhole/zerotier")
+#endif
,_run(true)
+ ,_mqc(NULL)
{
_ports[0] = 0;
_ports[1] = 0;
_ports[2] = 0;
+
+#if ZT_VAULT_SUPPORT
+ curl_global_init(CURL_GLOBAL_DEFAULT);
+#endif
}
virtual ~OneServiceImpl()
@@ -525,10 +624,16 @@ public:
_binder.closeAll(_phy);
_phy.close(_localControlSocket4);
_phy.close(_localControlSocket6);
+
+#if ZT_VAULT_SUPPORT
+ curl_global_cleanup();
+#endif
+
#ifdef ZT_USE_MINIUPNPC
delete _portMapper;
#endif
delete _controller;
+ delete _mqc;
}
virtual ReasonForTermination run()
@@ -568,98 +673,8 @@ public:
_node = new Node(this,(void *)0,&cb,OSUtils::now());
}
- // Read local configuration
- std::vector<InetAddress> explicitBind;
- {
- std::map<InetAddress,ZT_PhysicalPathConfiguration> ppc;
-
- // LEGACY: support old "trustedpaths" flat file
- FILE *trustpaths = fopen((_homePath + ZT_PATH_SEPARATOR_S "trustedpaths").c_str(),"r");
- if (trustpaths) {
- fprintf(stderr,"WARNING: 'trustedpaths' flat file format is deprecated in favor of path definitions in local.conf" ZT_EOL_S);
- char buf[1024];
- while (fgets(buf,sizeof(buf),trustpaths)) {
- int fno = 0;
- char *saveptr = (char *)0;
- uint64_t trustedPathId = 0;
- InetAddress trustedPathNetwork;
- for(char *f=Utils::stok(buf,"=\r\n \t",&saveptr);(f);f=Utils::stok((char *)0,"=\r\n \t",&saveptr)) {
- if (fno == 0) {
- trustedPathId = Utils::hexStrToU64(f);
- } else if (fno == 1) {
- trustedPathNetwork = InetAddress(f);
- } else break;
- ++fno;
- }
- if ( (trustedPathId != 0) && ((trustedPathNetwork.ss_family == AF_INET)||(trustedPathNetwork.ss_family == AF_INET6)) && (trustedPathNetwork.netmaskBits() > 0) ) {
- ppc[trustedPathNetwork].trustedPathId = trustedPathId;
- ppc[trustedPathNetwork].mtu = 0; // use default
- }
- }
- fclose(trustpaths);
- }
-
- // 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 = OSUtils::jsonParse(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(OSUtils::jsonString(phy.key(),"").c_str());
- if (net) {
- if (phy.value().is_object()) {
- uint64_t tpid;
- if ((tpid = OSUtils::jsonInt(phy.value()["trustedPathId"],0ULL)) != 0ULL) {
- if ((net.ss_family == AF_INET)||(net.ss_family == AF_INET6))
- ppc[net].trustedPathId = tpid;
- }
- ppc[net].mtu = (int)OSUtils::jsonInt(phy.value()["mtu"],0ULL); // 0 means use default
- }
- }
- }
- }
-
- json &settings = _localConfig["settings"];
- if (settings.is_object()) {
- // Allow controller DB path to be put somewhere else
- const std::string cdbp(OSUtils::jsonString(settings["controllerDbPath"],""));
- if (cdbp.length() > 0)
- _controllerDbPath = cdbp;
-
- // Bind to wildcard instead of to specific interfaces (disables full tunnel capability)
- json &bind = settings["bind"];
- if (bind.is_array()) {
- for(unsigned long i=0;i<bind.size();++i) {
- const std::string ips(OSUtils::jsonString(bind[i],""));
- if (ips.length() > 0) {
- InetAddress ip(ips.c_str());
- if ((ip.ss_family == AF_INET)||(ip.ss_family == AF_INET6))
- explicitBind.push_back(ip);
- }
- }
- }
- }
-
- // Set trusted paths if there are any
- if (ppc.size() > 0) {
- for(std::map<InetAddress,ZT_PhysicalPathConfiguration>::iterator i(ppc.begin());i!=ppc.end();++i)
- _node->setPhysicalPathConfiguration(reinterpret_cast<const struct sockaddr_storage *>(&(i->first)),&(i->second));
- }
- }
-
- // Apply other runtime configuration from local.conf
+ // local.conf
+ readLocalSettings();
applyLocalConfig();
// Make sure we can use the primary port, and hunt for one if configured to do so
@@ -708,16 +723,18 @@ public:
// This exists because there are buggy NATs out there that fail if more
// than one device behind the same NAT tries to use the same internal
// private address port number. Buggy NATs are a running theme.
- _ports[1] = 20000 + ((unsigned int)_node->address() % 45500);
- for(int i=0;;++i) {
- if (i > 1000) {
- _ports[1] = 0;
- break;
- } else if (++_ports[1] >= 65536) {
- _ports[1] = 20000;
+ if (_allowSecondaryPort) {
+ _ports[1] = (_secondaryPort == 0) ? 20000 + ((unsigned int)_node->address() % 45500) : _secondaryPort;
+ for(int i=0;;++i) {
+ if (i > 1000) {
+ _ports[1] = 0;
+ break;
+ } else if (++_ports[1] >= 65536) {
+ _ports[1] = 20000;
+ }
+ if (_trialBind(_ports[1]))
+ break;
}
- if (_trialBind(_ports[1]))
- break;
}
#ifdef ZT_USE_MINIUPNPC
@@ -726,7 +743,7 @@ public:
// use the other two ports for that because some NATs do really funky
// stuff with ports that are explicitly mapped that breaks things.
if (_ports[1]) {
- _ports[2] = _ports[1];
+ _ports[2] = (_tertiaryPort == 0) ? _ports[1] : _tertiaryPort;
for(int i=0;;++i) {
if (i > 1000) {
_ports[2] = 0;
@@ -750,7 +767,7 @@ public:
OSUtils::rmDashRf((_homePath + ZT_PATH_SEPARATOR_S "iddb.d").c_str());
// Network controller is now enabled by default for desktop and server
- _controller = new EmbeddedNetworkController(_node,_controllerDbPath.c_str());
+ _controller = new EmbeddedNetworkController(_node,_controllerDbPath.c_str(),_ports[0], _mqc);
_node->setNetconfMaster((void *)_controller);
// Join existing networks in networks.d
@@ -780,8 +797,10 @@ public:
int64_t lastTapMulticastGroupCheck = 0;
int64_t lastBindRefresh = 0;
int64_t lastUpdateCheck = clockShouldBe;
+ int64_t lastMultipathModeUpdate = 0;
int64_t lastCleanedPeersDb = 0;
int64_t lastLocalInterfaceAddressCheck = (clockShouldBe - ZT_LOCAL_INTERFACE_CHECK_INTERVAL) + 15000; // do this in 15s to give portmapper time to configure and other things time to settle
+ int64_t lastLocalConfFileCheck = OSUtils::now();
for(;;) {
_run_m.lock();
if (!_run) {
@@ -810,8 +829,21 @@ public:
_updater->apply();
}
+ // Reload local.conf if anything changed recently
+ if ((now - lastLocalConfFileCheck) >= ZT_LOCAL_CONF_FILE_CHECK_INTERVAL) {
+ lastLocalConfFileCheck = now;
+ struct stat result;
+ if(stat((_homePath + ZT_PATH_SEPARATOR_S "local.conf").c_str(), &result)==0) {
+ int64_t mod_time = result.st_mtime * 1000;
+ if ((now - mod_time) <= ZT_LOCAL_CONF_FILE_CHECK_INTERVAL) {
+ readLocalSettings();
+ applyLocalConfig();
+ }
+ }
+ }
+
// Refresh bindings in case device's interfaces have changed, and also sync routes to update any shadow routes (e.g. shadow default)
- if (((now - lastBindRefresh) >= ZT_BINDER_REFRESH_PERIOD)||(restarted)) {
+ if (((now - lastBindRefresh) >= (_multipathMode ? ZT_BINDER_REFRESH_PERIOD / 8 : ZT_BINDER_REFRESH_PERIOD))||(restarted)) {
lastBindRefresh = now;
unsigned int p[3];
unsigned int pc = 0;
@@ -828,6 +860,11 @@ public:
}
}
}
+ // Update multipath mode (if needed)
+ if (((now - lastMultipathModeUpdate) >= ZT_BINDER_REFRESH_PERIOD / 8)||(restarted)) {
+ lastMultipathModeUpdate = now;
+ _node->setMultipathMode(_multipathMode);
+ }
// Run background task processor in core if it's time to do so
int64_t dl = _nextBackgroundTaskDeadline;
@@ -863,7 +900,7 @@ public:
}
// Sync information about physical network interfaces
- if ((now - lastLocalInterfaceAddressCheck) >= ZT_LOCAL_INTERFACE_CHECK_INTERVAL) {
+ if ((now - lastLocalInterfaceAddressCheck) >= (_multipathMode ? ZT_LOCAL_INTERFACE_CHECK_INTERVAL / 8 : ZT_LOCAL_INTERFACE_CHECK_INTERVAL)) {
lastLocalInterfaceAddressCheck = now;
_node->clearLocalInterfaceAddresses();
@@ -922,6 +959,109 @@ public:
return _termReason;
}
+ void readLocalSettings()
+ {
+ // Read local configuration
+ std::map<InetAddress,ZT_PhysicalPathConfiguration> ppc;
+
+ // LEGACY: support old "trustedpaths" flat file
+ FILE *trustpaths = fopen((_homePath + ZT_PATH_SEPARATOR_S "trustedpaths").c_str(),"r");
+ if (trustpaths) {
+ fprintf(stderr,"WARNING: 'trustedpaths' flat file format is deprecated in favor of path definitions in local.conf" ZT_EOL_S);
+ char buf[1024];
+ while (fgets(buf,sizeof(buf),trustpaths)) {
+ int fno = 0;
+ char *saveptr = (char *)0;
+ uint64_t trustedPathId = 0;
+ InetAddress trustedPathNetwork;
+ for(char *f=Utils::stok(buf,"=\r\n \t",&saveptr);(f);f=Utils::stok((char *)0,"=\r\n \t",&saveptr)) {
+ if (fno == 0) {
+ trustedPathId = Utils::hexStrToU64(f);
+ } else if (fno == 1) {
+ trustedPathNetwork = InetAddress(f);
+ } else break;
+ ++fno;
+ }
+ if ( (trustedPathId != 0) && ((trustedPathNetwork.ss_family == AF_INET)||(trustedPathNetwork.ss_family == AF_INET6)) && (trustedPathNetwork.netmaskBits() > 0) ) {
+ ppc[trustedPathNetwork].trustedPathId = trustedPathId;
+ ppc[trustedPathNetwork].mtu = 0; // use default
+ }
+ }
+ fclose(trustpaths);
+ }
+
+ // 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 = OSUtils::jsonParse(lcbuf);
+ if (!_localConfig.is_object()) {
+ fprintf(stderr,"ERROR: unable to parse local.conf (root element is not a JSON object)" ZT_EOL_S);
+ exit(1);
+ }
+ } catch ( ... ) {
+ fprintf(stderr,"ERROR: unable to parse local.conf (invalid JSON)" ZT_EOL_S);
+ exit(1);
+ }
+ }
+
+ // 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(OSUtils::jsonString(phy.key(),"").c_str());
+ if (net) {
+ if (phy.value().is_object()) {
+ uint64_t tpid;
+ if ((tpid = OSUtils::jsonInt(phy.value()["trustedPathId"],0ULL)) != 0ULL) {
+ if ((net.ss_family == AF_INET)||(net.ss_family == AF_INET6))
+ ppc[net].trustedPathId = tpid;
+ }
+ ppc[net].mtu = (int)OSUtils::jsonInt(phy.value()["mtu"],0ULL); // 0 means use default
+ }
+ }
+ }
+ }
+
+ json &settings = _localConfig["settings"];
+ if (settings.is_object()) {
+ // Allow controller DB path to be put somewhere else
+ const std::string cdbp(OSUtils::jsonString(settings["controllerDbPath"],""));
+ if (cdbp.length() > 0)
+ _controllerDbPath = cdbp;
+
+ json &rmq = settings["rabbitmq"];
+ if (rmq.is_object() && _mqc == NULL) {
+ fprintf(stderr, "Reading RabbitMQ Config\n");
+ _mqc = new MQConfig;
+ _mqc->port = rmq["port"];
+ _mqc->host = OSUtils::jsonString(rmq["host"], "").c_str();
+ _mqc->username = OSUtils::jsonString(rmq["username"], "").c_str();
+ _mqc->password = OSUtils::jsonString(rmq["password"], "").c_str();
+ }
+
+ // Bind to wildcard instead of to specific interfaces (disables full tunnel capability)
+ json &bind = settings["bind"];
+ if (bind.is_array()) {
+ for(unsigned long i=0;i<bind.size();++i) {
+ const std::string ips(OSUtils::jsonString(bind[i],""));
+ if (ips.length() > 0) {
+ InetAddress ip(ips.c_str());
+ if ((ip.ss_family == AF_INET)||(ip.ss_family == AF_INET6))
+ explicitBind.push_back(ip);
+ }
+ }
+ }
+ }
+
+ // Set trusted paths if there are any
+ if (ppc.size() > 0) {
+ for(std::map<InetAddress,ZT_PhysicalPathConfiguration>::iterator i(ppc.begin());i!=ppc.end();++i)
+ _node->setPhysicalPathConfiguration(reinterpret_cast<const struct sockaddr_storage *>(&(i->first)),&(i->second));
+ }
+ }
+
virtual ReasonForTermination reasonForTermination() const
{
Mutex::Lock _l(_termReason_m);
@@ -944,44 +1084,26 @@ public:
}
#ifdef ZT_SDK
- virtual void leave(const uint64_t hp)
- {
- _node->leave(hp, NULL, NULL);
- }
-
- virtual void join(const uint64_t hp)
- {
- _node->join(hp, NULL, NULL);
- }
-
virtual std::string givenHomePath()
{
return _homePath;
}
- std::vector<ZT_VirtualNetworkRoute> *getRoutes(uint64_t nwid)
+ void getRoutes(uint64_t nwid, void *routeArray, unsigned int *numRoutes)
{
Mutex::Lock _l(_nets_m);
NetworkState &n = _nets[nwid];
- std::vector<ZT_VirtualNetworkRoute> *routes = new std::vector<ZT_VirtualNetworkRoute>();
- for(int i=0; i<ZT_MAX_NETWORK_ROUTES; i++) {
- routes->push_back(n.config.routes[i]);
+ *numRoutes = *numRoutes < n.config.routeCount ? *numRoutes : n.config.routeCount;
+ for(unsigned int i=0; i<*numRoutes; i++) {
+ ZT_VirtualNetworkRoute *vnr = (ZT_VirtualNetworkRoute*)routeArray;
+ memcpy(&vnr[i], &(n.config.routes[i]), sizeof(ZT_VirtualNetworkRoute));
}
- return routes;
}
virtual Node *getNode()
{
return _node;
}
-
- virtual void removeNets()
- {
- Mutex::Lock _l(_nets_m);
- std::map<uint64_t,NetworkState>::iterator i;
- for(i = _nets.begin(); i != _nets.end(); i++)
- delete i->second.tap;
- }
#endif // ZT_SDK
virtual void terminate()
@@ -1082,16 +1204,6 @@ public:
#ifdef __SYNOLOGY__
// Authenticate via Synology's built-in cgi script
if (!isAuth) {
- /*
- fprintf(stderr, "path = %s\n", path.c_str());
- fprintf(stderr, "headers.size=%d\n", headers.size());
- std::map<std::string, std::string>::const_iterator it(headers.begin());
- while(it != headers.end()) {
- fprintf(stderr,"header[%s] = %s\n", (it->first).c_str(), (it->second).c_str());
- it++;
- }
- */
- // parse out url args
int synotoken_pos = path.find("SynoToken");
int argpos = path.find("?");
if(synotoken_pos != std::string::npos && argpos != std::string::npos) {
@@ -1104,10 +1216,6 @@ public:
setenv("HTTP_COOKIE", cookie_val.c_str(), true);
setenv("HTTP_X_SYNO_TOKEN", synotoken_val.c_str(), true);
setenv("REMOTE_ADDR", ah2->second.c_str(),true);
- //fprintf(stderr, "HTTP_COOKIE: %s\n",std::getenv ("HTTP_COOKIE"));
- //fprintf(stderr, "HTTP_X_SYNO_TOKEN: %s\n",std::getenv ("HTTP_X_SYNO_TOKEN"));
- //fprintf(stderr, "REMOTE_ADDR: %s\n",std::getenv ("REMOTE_ADDR"));
- // check synology web auth
char user[256], buf[1024];
FILE *fp = NULL;
bzero(user, 256);
@@ -1153,6 +1261,23 @@ public:
json &settings = res["config"]["settings"];
settings["primaryPort"] = OSUtils::jsonInt(settings["primaryPort"],(uint64_t)_primaryPort) & 0xffff;
settings["allowTcpFallbackRelay"] = OSUtils::jsonBool(settings["allowTcpFallbackRelay"],_allowTcpFallbackRelay);
+
+ if (_multipathMode) {
+ json &multipathConfig = res["multipath"];
+ ZT_PeerList *pl = _node->peers();
+ char peerAddrStr[256];
+ if (pl) {
+ for(unsigned long i=0;i<pl->peerCount;++i) {
+ if (pl->peers[i].hadAggregateLink) {
+ nlohmann::json pj;
+ _peerAggregateLinkToJson(pj,&(pl->peers[i]));
+ OSUtils::ztsnprintf(peerAddrStr,sizeof(peerAddrStr),"%.10llx",pl->peers[i].address);
+ multipathConfig[peerAddrStr] = (pj);
+ }
+ }
+ }
+ }
+
#ifdef ZT_USE_MINIUPNPC
settings["portMappingEnabled"] = OSUtils::jsonBool(settings["portMappingEnabled"],true);
#else
@@ -1481,6 +1606,17 @@ public:
_primaryPort = (unsigned int)OSUtils::jsonInt(settings["primaryPort"],(uint64_t)_primaryPort) & 0xffff;
_allowTcpFallbackRelay = OSUtils::jsonBool(settings["allowTcpFallbackRelay"],true);
+ _allowSecondaryPort = OSUtils::jsonBool(settings["allowSecondaryPort"],true);
+ _secondaryPort = (unsigned int)OSUtils::jsonInt(settings["secondaryPort"],0);
+ _tertiaryPort = (unsigned int)OSUtils::jsonInt(settings["tertiaryPort"],0);
+ if (_secondaryPort != 0 || _tertiaryPort != 0) {
+ fprintf(stderr,"WARNING: using manually-specified ports. This can cause NAT issues." ZT_EOL_S);
+ }
+ _multipathMode = (unsigned int)OSUtils::jsonInt(settings["multipathMode"],0);
+ if (_multipathMode != 0 && _allowTcpFallbackRelay) {
+ fprintf(stderr,"WARNING: multipathMode cannot be used with allowTcpFallbackRelay. Disabling allowTcpFallbackRelay" ZT_EOL_S);
+ _allowTcpFallbackRelay = false;
+ }
_portMappingEnabled = OSUtils::jsonBool(settings["portMappingEnabled"],true);
#ifndef ZT_SDK
@@ -1518,6 +1654,47 @@ public:
}
}
+#if ZT_VAULT_SUPPORT
+ json &vault = settings["vault"];
+ if (vault.is_object()) {
+ const std::string url(OSUtils::jsonString(vault["vaultURL"], "").c_str());
+ if (!url.empty()) {
+ _vaultURL = url;
+ }
+
+ const std::string token(OSUtils::jsonString(vault["vaultToken"], "").c_str());
+ if (!token.empty()) {
+ _vaultToken = token;
+ }
+
+ const std::string path(OSUtils::jsonString(vault["vaultPath"], "").c_str());
+ if (!path.empty()) {
+ _vaultPath = path;
+ }
+ }
+
+ // also check environment variables for values. Environment variables
+ // will override local.conf variables
+ const std::string envURL(getenv("VAULT_ADDR"));
+ if (!envURL.empty()) {
+ _vaultURL = envURL;
+ }
+
+ const std::string envToken(getenv("VAULT_TOKEN"));
+ if (!envToken.empty()) {
+ _vaultToken = envToken;
+ }
+
+ const std::string envPath(getenv("VAULT_PATH"));
+ if (!envPath.empty()) {
+ _vaultPath = envPath;
+ }
+
+ if (!_vaultURL.empty() && !_vaultToken.empty()) {
+ _vaultEnabled = true;
+ }
+#endif
+
// Checks if a managed IP or route target is allowed
bool checkIfManagedIsAllowed(const NetworkState &n,const InetAddress &target)
{
@@ -1632,21 +1809,37 @@ public:
const InetAddress *const target = reinterpret_cast<const InetAddress *>(&(n.config.routes[i].target));
const InetAddress *const via = reinterpret_cast<const InetAddress *>(&(n.config.routes[i].via));
+ const InetAddress *src = NULL;
+ for (unsigned int j=0; j<n.config.assignedAddressCount; ++j) {
+ const InetAddress *const tmp = reinterpret_cast<const InetAddress *>(&(n.config.assignedAddresses[j]));
+ if (target->isV4() && tmp->isV4()) {
+ src = reinterpret_cast<InetAddress *>(&(n.config.assignedAddresses[j]));
+ break;
+ } else if (target->isV6() && tmp->isV6()) {
+ src = reinterpret_cast<InetAddress *>(&(n.config.assignedAddresses[j]));
+ break;
+ }
+ }
+ if (!src)
+ src = &NULL_INET_ADDR;
+
if ( (!checkIfManagedIsAllowed(n,*target)) || ((via->ss_family == target->ss_family)&&(matchIpOnly(myIps,*via))) )
continue;
bool haveRoute = false;
// Ignore routes implied by local managed IPs since adding the IP adds the route
+#ifndef __APPLE__
for(std::vector<InetAddress>::iterator ip(n.managedIps.begin());ip!=n.managedIps.end();++ip) {
if ((target->netmaskBits() == ip->netmaskBits())&&(target->containsAddress(*ip))) {
haveRoute = true;
break;
}
}
+#endif
if (haveRoute)
continue;
-
+#ifndef ZT_SDK
// If we've already applied this route, just sync it and continue
for(std::list< SharedPtr<ManagedRoute> >::iterator mr(n.managedRoutes.begin());mr!=n.managedRoutes.end();++mr) {
if ( ((*mr)->target() == *target) && ( ((via->ss_family == target->ss_family)&&((*mr)->via().ipsEqual(*via))) || (tapdev == (*mr)->device()) ) ) {
@@ -1659,9 +1852,10 @@ public:
continue;
// Add and apply new routes
- n.managedRoutes.push_back(SharedPtr<ManagedRoute>(new ManagedRoute(*target,*via,tapdev)));
+ n.managedRoutes.push_back(SharedPtr<ManagedRoute>(new ManagedRoute(*target,*via,*src,tapdev)));
if (!n.managedRoutes.back()->sync())
n.managedRoutes.pop_back();
+#endif
}
}
}
@@ -1672,16 +1866,10 @@ public:
inline void phyOnDatagram(PhySocket *sock,void **uptr,const struct sockaddr *localAddr,const struct sockaddr *from,void *data,unsigned long len)
{
+ const uint64_t now = OSUtils::now();
if ((len >= 16)&&(reinterpret_cast<const InetAddress *>(from)->ipScope() == InetAddress::IP_SCOPE_GLOBAL))
- _lastDirectReceiveFromGlobal = OSUtils::now();
- const ZT_ResultCode rc = _node->processWirePacket(
- (void *)0,
- OSUtils::now(),
- reinterpret_cast<int64_t>(sock),
- reinterpret_cast<const struct sockaddr_storage *>(from), // Phy<> uses sockaddr_storage, so it'll always be that big
- data,
- len,
- &_nextBackgroundTaskDeadline);
+ _lastDirectReceiveFromGlobal = now;
+ const ZT_ResultCode rc = _node->processWirePacket(nullptr,now,reinterpret_cast<int64_t>(sock),reinterpret_cast<const struct sockaddr_storage *>(from),data,len,&_nextBackgroundTaskDeadline);
if (ZT_ResultCode_isFatal(rc)) {
char tmp[256];
OSUtils::ztsnprintf(tmp,sizeof(tmp),"fatal error code from processWirePacket: %d",(int)rc);
@@ -1722,6 +1910,13 @@ public:
_phy.close(sockN,false);
return;
} else {
+#ifdef ZT_SDK
+ // Immediately close new local connections. The intention is to prevent the backplane from being accessed when operating as libzt
+ if (!allowHttpBackplaneManagement && ((InetAddress*)from)->ipScope() == InetAddress::IP_SCOPE_LOOPBACK) {
+ _phy.close(sockN,false);
+ return;
+ }
+#endif
TcpConnection *tc = new TcpConnection();
{
Mutex::Lock _l(_tcpConnections_m);
@@ -1921,7 +2116,7 @@ public:
inline void phyOnUnixAccept(PhySocket *sockL,PhySocket *sockN,void **uptrL,void **uptrN) {}
inline void phyOnUnixClose(PhySocket *sock,void **uptr) {}
inline void phyOnUnixData(PhySocket *sock,void **uptr,void *data,unsigned long len) {}
- inline void phyOnUnixWritable(PhySocket *sock,void **uptr,bool lwip_invoked) {}
+ inline void phyOnUnixWritable(PhySocket *sock,void **uptr) {}
inline int nodeVirtualNetworkConfigFunction(uint64_t nwid,void **nuptr,enum ZT_VirtualNetworkConfigOperation op,const ZT_VirtualNetworkConfig *nwc)
{
@@ -1999,7 +2194,7 @@ public:
// After setting up tap, fall through to CONFIG_UPDATE since we also want to do this...
case ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_CONFIG_UPDATE:
- ZT_FAST_MEMCPY(&(n.config),nwc,sizeof(ZT_VirtualNetworkConfig));
+ memcpy(&(n.config),nwc,sizeof(ZT_VirtualNetworkConfig));
if (n.tap) { // sanity check
#if defined(__WINDOWS__) && !defined(ZT_SDK)
// wait for up to 5 seconds for the WindowsEthernetTap to actually be initialized
@@ -2081,8 +2276,88 @@ public:
}
}
+#if ZT_VAULT_SUPPORT
+ inline bool nodeVaultPutIdentity(enum ZT_StateObjectType type, const void *data, int len)
+ {
+ bool retval = false;
+ if (type != ZT_STATE_OBJECT_IDENTITY_PUBLIC && type != ZT_STATE_OBJECT_IDENTITY_SECRET) {
+ return retval;
+ }
+
+ CURL *curl = curl_easy_init();
+ if (curl) {
+ char token[512] = { 0 };
+ snprintf(token, sizeof(token), "X-Vault-Token: %s", _vaultToken.c_str());
+
+ struct curl_slist *chunk = NULL;
+ chunk = curl_slist_append(chunk, token);
+
+
+ char content_type[512] = { 0 };
+ snprintf(content_type, sizeof(content_type), "Content-Type: application/json");
+
+ chunk = curl_slist_append(chunk, content_type);
+
+ curl_easy_setopt(curl, CURLOPT_HTTPHEADER, chunk);
+
+ char url[2048] = { 0 };
+ snprintf(url, sizeof(url), "%s/v1/%s", _vaultURL.c_str(), _vaultPath.c_str());
+
+ curl_easy_setopt(curl, CURLOPT_URL, url);
+
+ json d = json::object();
+ if (type == ZT_STATE_OBJECT_IDENTITY_PUBLIC) {
+ std::string key((const char*)data, len);
+ d["public"] = key;
+ }
+ else if (type == ZT_STATE_OBJECT_IDENTITY_SECRET) {
+ std::string key((const char*)data, len);
+ d["secret"] = key;
+ }
+
+ if (!d.empty()) {
+ std::string post = d.dump();
+
+ if (!post.empty()) {
+ curl_easy_setopt(curl, CURLOPT_POSTFIELDS, post.c_str());
+ curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, post.length());
+
+#ifndef NDEBUG
+ curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
+#endif
+
+ CURLcode res = curl_easy_perform(curl);
+ if (res == CURLE_OK) {
+ long response_code = 0;
+ curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code);
+ if (response_code == 200 || response_code == 204) {
+ retval = true;
+ }
+ }
+ }
+ }
+
+ curl_easy_cleanup(curl);
+ curl = NULL;
+ curl_slist_free_all(chunk);
+ chunk = NULL;
+ }
+
+ return retval;
+ }
+#endif
+
inline void nodeStatePutFunction(enum ZT_StateObjectType type,const uint64_t id[2],const void *data,int len)
{
+#if ZT_VAULT_SUPPORT
+ if (_vaultEnabled && (type == ZT_STATE_OBJECT_IDENTITY_SECRET || type == ZT_STATE_OBJECT_IDENTITY_PUBLIC)) {
+ if (nodeVaultPutIdentity(type, data, len)) {
+ // value successfully written to Vault
+ return;
+ }
+ // else fallback to disk
+ }
+#endif
char p[1024];
FILE *f;
bool secure = false;
@@ -2117,17 +2392,22 @@ public:
return;
}
- if (len >= 0) {
+ if ((len >= 0)&&(data)) {
// Check to see if we've already written this first. This reduces
// redundant writes and I/O overhead on most platforms and has
// little effect on others.
f = fopen(p,"rb");
if (f) {
- char buf[65535];
- long l = (long)fread(buf,1,sizeof(buf),f);
- fclose(f);
- if ((l == (long)len)&&(memcmp(data,buf,l) == 0))
- return;
+ char *const buf = (char *)malloc(len*4);
+ if (buf) {
+ long l = (long)fread(buf,1,(size_t)(len*4),f);
+ fclose(f);
+ if ((l == (long)len)&&(memcmp(data,buf,l) == 0)) {
+ free(buf);
+ return;
+ }
+ free(buf);
+ }
}
f = fopen(p,"wb");
@@ -2149,8 +2429,93 @@ public:
}
}
+#if ZT_VAULT_SUPPORT
+ inline int nodeVaultGetIdentity(enum ZT_StateObjectType type, void *data, unsigned int maxlen)
+ {
+ if (type != ZT_STATE_OBJECT_IDENTITY_SECRET && type != ZT_STATE_OBJECT_IDENTITY_PUBLIC) {
+ return -1;
+ }
+
+ int ret = -1;
+ CURL *curl = curl_easy_init();
+ if (curl) {
+ char token[512] = { 0 };
+ snprintf(token, sizeof(token), "X-Vault-Token: %s", _vaultToken.c_str());
+
+ struct curl_slist *chunk = NULL;
+ chunk = curl_slist_append(chunk, token);
+ curl_easy_setopt(curl, CURLOPT_HTTPHEADER, chunk);
+
+ char url[2048] = { 0 };
+ snprintf(url, sizeof(url), "%s/v1/%s", _vaultURL.c_str(), _vaultPath.c_str());
+
+ curl_easy_setopt(curl, CURLOPT_URL, url);
+
+ std::string response;
+ std::string res_headers;
+
+ curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &curlResponseWrite);
+ curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
+ curl_easy_setopt(curl, CURLOPT_HEADERDATA, &res_headers);
+
+#ifndef NDEBUG
+ curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
+#endif
+
+ CURLcode res = curl_easy_perform(curl);
+
+ if (res == CURLE_OK) {
+ long response_code = 0;
+ curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code);
+ if (response_code == 200) {
+ try {
+ json payload = json::parse(response);
+ if (!payload["data"].is_null()) {
+ json &d = payload["data"];
+ if (type == ZT_STATE_OBJECT_IDENTITY_SECRET) {
+ std::string secret = OSUtils::jsonString(d["secret"],"");
+
+ if (!secret.empty()) {
+ ret = (int)secret.length();
+ memcpy(data, secret.c_str(), ret);
+ }
+ }
+ else if (type == ZT_STATE_OBJECT_IDENTITY_PUBLIC) {
+ std::string pub = OSUtils::jsonString(d["public"],"");
+
+ if (!pub.empty()) {
+ ret = (int)pub.length();
+ memcpy(data, pub.c_str(), ret);
+ }
+ }
+ }
+ }
+ catch (...) {
+ ret = -1;
+ }
+ }
+ }
+
+ curl_easy_cleanup(curl);
+ curl = NULL;
+ curl_slist_free_all(chunk);
+ chunk = NULL;
+ }
+ return ret;
+ }
+#endif
+
inline int nodeStateGetFunction(enum ZT_StateObjectType type,const uint64_t id[2],void *data,unsigned int maxlen)
{
+#if ZT_VAULT_SUPPORT
+ if (_vaultEnabled && (type == ZT_STATE_OBJECT_IDENTITY_SECRET || type == ZT_STATE_OBJECT_IDENTITY_PUBLIC) ) {
+ int retval = nodeVaultGetIdentity(type, data, maxlen);
+ if (retval >= 0)
+ return retval;
+
+ // else continue file based lookup
+ }
+#endif
char p[4096];
switch(type) {
case ZT_STATE_OBJECT_IDENTITY_PUBLIC:
@@ -2178,6 +2543,17 @@ public:
if (f) {
int n = (int)fread(data,1,maxlen,f);
fclose(f);
+#if ZT_VAULT_SUPPORT
+ if (_vaultEnabled && (type == ZT_STATE_OBJECT_IDENTITY_SECRET || type == ZT_STATE_OBJECT_IDENTITY_PUBLIC)) {
+ // If we've gotten here while Vault is enabled, Vault does not know the key and it's been
+ // read from disk instead.
+ //
+ // We should put the value in Vault and remove the local file.
+ if (nodeVaultPutIdentity(type, data, n)) {
+ unlink(p);
+ }
+ }
+#endif
if (n >= 0)
return n;
}
@@ -2330,7 +2706,7 @@ public:
else return 0;
const std::vector<InetAddress> *l = lh->get(ztaddr);
if ((l)&&(l->size() > 0)) {
- ZT_FAST_MEMCPY(result,&((*l)[(unsigned long)_node->prng() % l->size()]),sizeof(struct sockaddr_storage));
+ memcpy(result,&((*l)[(unsigned long)_node->prng() % l->size()]),sizeof(struct sockaddr_storage));
return 1;
} else return 0;
}
@@ -2403,6 +2779,7 @@ public:
#endif
#ifdef __APPLE__
+ if ((ifname[0] == 'f')&&(ifname[1] == 'e')&&(ifname[2] == 't')&&(ifname[3] == 'h')) return false; // ... as is feth#
if ((ifname[0] == 'l')&&(ifname[1] == 'o')) return false; // loopback
if ((ifname[0] == 'z')&&(ifname[1] == 't')) return false; // sanity check: zt#
if ((ifname[0] == 't')&&(ifname[1] == 'u')&&(ifname[2] == 'n')) return false; // tun# is probably an OpenVPN tunnel or similar