mirror of
https://github.com/NAStools/zerotierone.git
synced 2025-08-14 10:37:44 -07:00
Imported Upstream version 1.1.14
This commit is contained in:
commit
bb232b9d52
557 changed files with 115164 additions and 0 deletions
160
service/ClusterDefinition.hpp
Normal file
160
service/ClusterDefinition.hpp
Normal file
|
@ -0,0 +1,160 @@
|
|||
/*
|
||||
* ZeroTier One - Network Virtualization Everywhere
|
||||
* Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef ZT_CLUSTERDEFINITION_HPP
|
||||
#define ZT_CLUSTERDEFINITION_HPP
|
||||
|
||||
#ifdef ZT_ENABLE_CLUSTER
|
||||
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
|
||||
#include "../node/Constants.hpp"
|
||||
#include "../node/Utils.hpp"
|
||||
#include "../node/NonCopyable.hpp"
|
||||
#include "../osdep/OSUtils.hpp"
|
||||
|
||||
#include "ClusterGeoIpService.hpp"
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
/**
|
||||
* Parser for cluster definition file
|
||||
*/
|
||||
class ClusterDefinition : NonCopyable
|
||||
{
|
||||
public:
|
||||
struct MemberDefinition
|
||||
{
|
||||
MemberDefinition() : id(0),x(0),y(0),z(0) { name[0] = (char)0; }
|
||||
|
||||
unsigned int id;
|
||||
int x,y,z;
|
||||
char name[256];
|
||||
InetAddress clusterEndpoint;
|
||||
std::vector<InetAddress> zeroTierEndpoints;
|
||||
};
|
||||
|
||||
/**
|
||||
* Load and initialize cluster definition and GeoIP data if any
|
||||
*
|
||||
* @param myAddress My ZeroTier address
|
||||
* @param pathToClusterFile Path to cluster definition file
|
||||
* @throws std::runtime_error Invalid cluster definition or unable to load data
|
||||
*/
|
||||
ClusterDefinition(uint64_t myAddress,const char *pathToClusterFile)
|
||||
{
|
||||
std::string cf;
|
||||
if (!OSUtils::readFile(pathToClusterFile,cf))
|
||||
return;
|
||||
|
||||
char myAddressStr[64];
|
||||
Utils::snprintf(myAddressStr,sizeof(myAddressStr),"%.10llx",myAddress);
|
||||
|
||||
std::vector<std::string> lines(Utils::split(cf.c_str(),"\r\n","",""));
|
||||
for(std::vector<std::string>::iterator l(lines.begin());l!=lines.end();++l) {
|
||||
std::vector<std::string> fields(Utils::split(l->c_str()," \t","",""));
|
||||
if ((fields.size() < 5)||(fields[0][0] == '#')||(fields[0] != myAddressStr))
|
||||
continue;
|
||||
|
||||
// <address> geo <CSV path> <ip start column> <ip end column> <latitutde column> <longitude column>
|
||||
if (fields[1] == "geo") {
|
||||
if ((fields.size() >= 7)&&(OSUtils::fileExists(fields[2].c_str()))) {
|
||||
int ipStartColumn = Utils::strToInt(fields[3].c_str());
|
||||
int ipEndColumn = Utils::strToInt(fields[4].c_str());
|
||||
int latitudeColumn = Utils::strToInt(fields[5].c_str());
|
||||
int longitudeColumn = Utils::strToInt(fields[6].c_str());
|
||||
if (_geo.load(fields[2].c_str(),ipStartColumn,ipEndColumn,latitudeColumn,longitudeColumn) <= 0)
|
||||
throw std::runtime_error(std::string("failed to load geo-ip data from ")+fields[2]);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// <address> <ID> <name> <backplane IP/port(s)> <ZT frontplane IP/port(s)> <x,y,z>
|
||||
int id = Utils::strToUInt(fields[1].c_str());
|
||||
if ((id < 0)||(id > ZT_CLUSTER_MAX_MEMBERS))
|
||||
throw std::runtime_error(std::string("invalid cluster member ID: ")+fields[1]);
|
||||
MemberDefinition &md = _md[id];
|
||||
|
||||
md.id = (unsigned int)id;
|
||||
if (fields.size() >= 6) {
|
||||
std::vector<std::string> xyz(Utils::split(fields[5].c_str(),",","",""));
|
||||
md.x = (xyz.size() > 0) ? Utils::strToInt(xyz[0].c_str()) : 0;
|
||||
md.y = (xyz.size() > 1) ? Utils::strToInt(xyz[1].c_str()) : 0;
|
||||
md.z = (xyz.size() > 2) ? Utils::strToInt(xyz[2].c_str()) : 0;
|
||||
}
|
||||
Utils::scopy(md.name,sizeof(md.name),fields[2].c_str());
|
||||
md.clusterEndpoint.fromString(fields[3]);
|
||||
if (!md.clusterEndpoint)
|
||||
continue;
|
||||
std::vector<std::string> zips(Utils::split(fields[4].c_str(),",","",""));
|
||||
for(std::vector<std::string>::iterator zip(zips.begin());zip!=zips.end();++zip) {
|
||||
InetAddress i;
|
||||
i.fromString(*zip);
|
||||
if (i)
|
||||
md.zeroTierEndpoints.push_back(i);
|
||||
}
|
||||
|
||||
_ids.push_back((unsigned int)id);
|
||||
}
|
||||
|
||||
std::sort(_ids.begin(),_ids.end());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return All member definitions in this cluster by ID (ID is array index)
|
||||
*/
|
||||
inline const MemberDefinition &operator[](unsigned int id) const throw() { return _md[id]; }
|
||||
|
||||
/**
|
||||
* @return Number of members in this cluster
|
||||
*/
|
||||
inline unsigned int size() const throw() { return (unsigned int)_ids.size(); }
|
||||
|
||||
/**
|
||||
* @return IDs of members in this cluster sorted by ID
|
||||
*/
|
||||
inline const std::vector<unsigned int> &ids() const throw() { return _ids; }
|
||||
|
||||
/**
|
||||
* @return GeoIP service for this cluster
|
||||
*/
|
||||
inline ClusterGeoIpService &geo() throw() { return _geo; }
|
||||
|
||||
/**
|
||||
* @return A vector (new copy) containing all cluster members
|
||||
*/
|
||||
inline std::vector<MemberDefinition> members() const
|
||||
{
|
||||
std::vector<MemberDefinition> m;
|
||||
for(std::vector<unsigned int>::const_iterator i(_ids.begin());i!=_ids.end();++i)
|
||||
m.push_back(_md[*i]);
|
||||
return m;
|
||||
}
|
||||
|
||||
private:
|
||||
MemberDefinition _md[ZT_CLUSTER_MAX_MEMBERS];
|
||||
std::vector<unsigned int> _ids;
|
||||
ClusterGeoIpService _geo;
|
||||
};
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
||||
#endif // ZT_ENABLE_CLUSTER
|
||||
|
||||
#endif
|
235
service/ClusterGeoIpService.cpp
Normal file
235
service/ClusterGeoIpService.cpp
Normal file
|
@ -0,0 +1,235 @@
|
|||
/*
|
||||
* ZeroTier One - Network Virtualization Everywhere
|
||||
* Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifdef ZT_ENABLE_CLUSTER
|
||||
|
||||
#include <math.h>
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#include "ClusterGeoIpService.hpp"
|
||||
|
||||
#include "../node/Utils.hpp"
|
||||
#include "../osdep/OSUtils.hpp"
|
||||
|
||||
#define ZT_CLUSTERGEOIPSERVICE_FILE_MODIFICATION_CHECK_EVERY 10000
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
ClusterGeoIpService::ClusterGeoIpService() :
|
||||
_pathToCsv(),
|
||||
_ipStartColumn(-1),
|
||||
_ipEndColumn(-1),
|
||||
_latitudeColumn(-1),
|
||||
_longitudeColumn(-1),
|
||||
_lastFileCheckTime(0),
|
||||
_csvModificationTime(0),
|
||||
_csvFileSize(0)
|
||||
{
|
||||
}
|
||||
|
||||
ClusterGeoIpService::~ClusterGeoIpService()
|
||||
{
|
||||
}
|
||||
|
||||
bool ClusterGeoIpService::locate(const InetAddress &ip,int &x,int &y,int &z)
|
||||
{
|
||||
Mutex::Lock _l(_lock);
|
||||
|
||||
if ((_pathToCsv.length() > 0)&&((OSUtils::now() - _lastFileCheckTime) > ZT_CLUSTERGEOIPSERVICE_FILE_MODIFICATION_CHECK_EVERY)) {
|
||||
_lastFileCheckTime = OSUtils::now();
|
||||
if ((_csvFileSize != OSUtils::getFileSize(_pathToCsv.c_str()))||(_csvModificationTime != OSUtils::getLastModified(_pathToCsv.c_str())))
|
||||
_load(_pathToCsv.c_str(),_ipStartColumn,_ipEndColumn,_latitudeColumn,_longitudeColumn);
|
||||
}
|
||||
|
||||
/* We search by looking up the upper bound of the sorted vXdb vectors
|
||||
* and then iterating down for a matching IP range. We stop when we hit
|
||||
* the beginning or an entry whose start and end are before the IP we
|
||||
* are searching. */
|
||||
|
||||
if ((ip.ss_family == AF_INET)&&(_v4db.size() > 0)) {
|
||||
_V4E key;
|
||||
key.start = Utils::ntoh((uint32_t)(reinterpret_cast<const struct sockaddr_in *>(&ip)->sin_addr.s_addr));
|
||||
std::vector<_V4E>::const_iterator i(std::upper_bound(_v4db.begin(),_v4db.end(),key));
|
||||
while (i != _v4db.begin()) {
|
||||
--i;
|
||||
if ((key.start >= i->start)&&(key.start <= i->end)) {
|
||||
x = i->x;
|
||||
y = i->y;
|
||||
z = i->z;
|
||||
//printf("%s : %f,%f %d,%d,%d\n",ip.toIpString().c_str(),i->lat,i->lon,x,y,z);
|
||||
return true;
|
||||
} else if ((key.start > i->start)&&(key.start > i->end))
|
||||
break;
|
||||
}
|
||||
} else if ((ip.ss_family == AF_INET6)&&(_v6db.size() > 0)) {
|
||||
_V6E key;
|
||||
memcpy(key.start,reinterpret_cast<const struct sockaddr_in6 *>(&ip)->sin6_addr.s6_addr,16);
|
||||
std::vector<_V6E>::const_iterator i(std::upper_bound(_v6db.begin(),_v6db.end(),key));
|
||||
while (i != _v6db.begin()) {
|
||||
--i;
|
||||
const int s_vs_s = memcmp(key.start,i->start,16);
|
||||
const int s_vs_e = memcmp(key.start,i->end,16);
|
||||
if ((s_vs_s >= 0)&&(s_vs_e <= 0)) {
|
||||
x = i->x;
|
||||
y = i->y;
|
||||
z = i->z;
|
||||
//printf("%s : %f,%f %d,%d,%d\n",ip.toIpString().c_str(),i->lat,i->lon,x,y,z);
|
||||
return true;
|
||||
} else if ((s_vs_s > 0)&&(s_vs_e > 0))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void ClusterGeoIpService::_parseLine(const char *line,std::vector<_V4E> &v4db,std::vector<_V6E> &v6db,int ipStartColumn,int ipEndColumn,int latitudeColumn,int longitudeColumn)
|
||||
{
|
||||
std::vector<std::string> ls(Utils::split(line,",\t","\\","\"'"));
|
||||
if ( ((ipStartColumn >= 0)&&(ipStartColumn < (int)ls.size()))&&
|
||||
((ipEndColumn >= 0)&&(ipEndColumn < (int)ls.size()))&&
|
||||
((latitudeColumn >= 0)&&(latitudeColumn < (int)ls.size()))&&
|
||||
((longitudeColumn >= 0)&&(longitudeColumn < (int)ls.size())) ) {
|
||||
InetAddress ipStart(ls[ipStartColumn].c_str(),0);
|
||||
InetAddress ipEnd(ls[ipEndColumn].c_str(),0);
|
||||
const double lat = strtod(ls[latitudeColumn].c_str(),(char **)0);
|
||||
const double lon = strtod(ls[longitudeColumn].c_str(),(char **)0);
|
||||
|
||||
if ((ipStart.ss_family == ipEnd.ss_family)&&(ipStart)&&(ipEnd)&&(std::isfinite(lat))&&(std::isfinite(lon))) {
|
||||
const double latRadians = lat * 0.01745329251994; // PI / 180
|
||||
const double lonRadians = lon * 0.01745329251994; // PI / 180
|
||||
const double cosLat = cos(latRadians);
|
||||
const int x = (int)round((-6371.0) * cosLat * cos(lonRadians)); // 6371 == Earth's approximate radius in kilometers
|
||||
const int y = (int)round(6371.0 * sin(latRadians));
|
||||
const int z = (int)round(6371.0 * cosLat * sin(lonRadians));
|
||||
|
||||
if (ipStart.ss_family == AF_INET) {
|
||||
v4db.push_back(_V4E());
|
||||
v4db.back().start = Utils::ntoh((uint32_t)(reinterpret_cast<const struct sockaddr_in *>(&ipStart)->sin_addr.s_addr));
|
||||
v4db.back().end = Utils::ntoh((uint32_t)(reinterpret_cast<const struct sockaddr_in *>(&ipEnd)->sin_addr.s_addr));
|
||||
v4db.back().lat = (float)lat;
|
||||
v4db.back().lon = (float)lon;
|
||||
v4db.back().x = x;
|
||||
v4db.back().y = y;
|
||||
v4db.back().z = z;
|
||||
//printf("%s - %s : %d,%d,%d\n",ipStart.toIpString().c_str(),ipEnd.toIpString().c_str(),x,y,z);
|
||||
} else if (ipStart.ss_family == AF_INET6) {
|
||||
v6db.push_back(_V6E());
|
||||
memcpy(v6db.back().start,reinterpret_cast<const struct sockaddr_in6 *>(&ipStart)->sin6_addr.s6_addr,16);
|
||||
memcpy(v6db.back().end,reinterpret_cast<const struct sockaddr_in6 *>(&ipEnd)->sin6_addr.s6_addr,16);
|
||||
v6db.back().lat = (float)lat;
|
||||
v6db.back().lon = (float)lon;
|
||||
v6db.back().x = x;
|
||||
v6db.back().y = y;
|
||||
v6db.back().z = z;
|
||||
//printf("%s - %s : %d,%d,%d\n",ipStart.toIpString().c_str(),ipEnd.toIpString().c_str(),x,y,z);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
long ClusterGeoIpService::_load(const char *pathToCsv,int ipStartColumn,int ipEndColumn,int latitudeColumn,int longitudeColumn)
|
||||
{
|
||||
// assumes _lock is locked
|
||||
|
||||
FILE *f = fopen(pathToCsv,"rb");
|
||||
if (!f)
|
||||
return -1;
|
||||
|
||||
std::vector<_V4E> v4db;
|
||||
std::vector<_V6E> v6db;
|
||||
v4db.reserve(16777216);
|
||||
v6db.reserve(16777216);
|
||||
|
||||
char buf[4096];
|
||||
char linebuf[1024];
|
||||
unsigned int lineptr = 0;
|
||||
for(;;) {
|
||||
int n = (int)fread(buf,1,sizeof(buf),f);
|
||||
if (n <= 0)
|
||||
break;
|
||||
for(int i=0;i<n;++i) {
|
||||
if ((buf[i] == '\r')||(buf[i] == '\n')||(buf[i] == (char)0)) {
|
||||
if (lineptr) {
|
||||
linebuf[lineptr] = (char)0;
|
||||
_parseLine(linebuf,v4db,v6db,ipStartColumn,ipEndColumn,latitudeColumn,longitudeColumn);
|
||||
}
|
||||
lineptr = 0;
|
||||
} else if (lineptr < (unsigned int)sizeof(linebuf))
|
||||
linebuf[lineptr++] = buf[i];
|
||||
}
|
||||
}
|
||||
if (lineptr) {
|
||||
linebuf[lineptr] = (char)0;
|
||||
_parseLine(linebuf,v4db,v6db,ipStartColumn,ipEndColumn,latitudeColumn,longitudeColumn);
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
|
||||
if ((v4db.size() > 0)||(v6db.size() > 0)) {
|
||||
std::sort(v4db.begin(),v4db.end());
|
||||
std::sort(v6db.begin(),v6db.end());
|
||||
|
||||
_pathToCsv = pathToCsv;
|
||||
_ipStartColumn = ipStartColumn;
|
||||
_ipEndColumn = ipEndColumn;
|
||||
_latitudeColumn = latitudeColumn;
|
||||
_longitudeColumn = longitudeColumn;
|
||||
|
||||
_lastFileCheckTime = OSUtils::now();
|
||||
_csvModificationTime = OSUtils::getLastModified(pathToCsv);
|
||||
_csvFileSize = OSUtils::getFileSize(pathToCsv);
|
||||
|
||||
_v4db.swap(v4db);
|
||||
_v6db.swap(v6db);
|
||||
|
||||
return (long)(_v4db.size() + _v6db.size());
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
||||
#endif // ZT_ENABLE_CLUSTER
|
||||
|
||||
/*
|
||||
int main(int argc,char **argv)
|
||||
{
|
||||
char buf[1024];
|
||||
|
||||
ZeroTier::ClusterGeoIpService gip;
|
||||
printf("loading...\n");
|
||||
gip.load("/Users/api/Code/ZeroTier/Infrastructure/root-servers/zerotier-one/cluster-geoip.csv",0,1,5,6);
|
||||
printf("... done!\n"); fflush(stdout);
|
||||
|
||||
while (gets(buf)) { // unsafe, testing only
|
||||
ZeroTier::InetAddress addr(buf,0);
|
||||
printf("looking up: %s\n",addr.toString().c_str()); fflush(stdout);
|
||||
int x = 0,y = 0,z = 0;
|
||||
if (gip.locate(addr,x,y,z)) {
|
||||
//printf("%s: %d,%d,%d\n",addr.toString().c_str(),x,y,z); fflush(stdout);
|
||||
} else {
|
||||
printf("%s: not found!\n",addr.toString().c_str()); fflush(stdout);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
*/
|
143
service/ClusterGeoIpService.hpp
Normal file
143
service/ClusterGeoIpService.hpp
Normal file
|
@ -0,0 +1,143 @@
|
|||
/*
|
||||
* ZeroTier One - Network Virtualization Everywhere
|
||||
* Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef ZT_CLUSTERGEOIPSERVICE_HPP
|
||||
#define ZT_CLUSTERGEOIPSERVICE_HPP
|
||||
|
||||
#ifdef ZT_ENABLE_CLUSTER
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
|
||||
#include "../node/Constants.hpp"
|
||||
#include "../node/Mutex.hpp"
|
||||
#include "../node/NonCopyable.hpp"
|
||||
#include "../node/InetAddress.hpp"
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
/**
|
||||
* Loads a GeoIP CSV into memory for fast lookup, reloading as needed
|
||||
*
|
||||
* This was designed around the CSV from https://db-ip.com but can be used
|
||||
* with any similar GeoIP CSV database that is presented in the form of an
|
||||
* IP range and lat/long coordinates.
|
||||
*
|
||||
* It loads the whole database into memory, which can be kind of large. If
|
||||
* the CSV file changes, the changes are loaded automatically.
|
||||
*/
|
||||
class ClusterGeoIpService : NonCopyable
|
||||
{
|
||||
public:
|
||||
ClusterGeoIpService();
|
||||
~ClusterGeoIpService();
|
||||
|
||||
/**
|
||||
* Load or reload CSV file
|
||||
*
|
||||
* CSV column indexes start at zero. CSVs can be quoted with single or
|
||||
* double quotes. Whitespace before or after commas is ignored. Backslash
|
||||
* may be used for escaping whitespace as well.
|
||||
*
|
||||
* @param pathToCsv Path to (uncompressed) CSV file
|
||||
* @param ipStartColumn Column with IP range start
|
||||
* @param ipEndColumn Column with IP range end (inclusive)
|
||||
* @param latitudeColumn Column with latitude
|
||||
* @param longitudeColumn Column with longitude
|
||||
* @return Number of valid records loaded or -1 on error (invalid file, not found, etc.)
|
||||
*/
|
||||
inline long load(const char *pathToCsv,int ipStartColumn,int ipEndColumn,int latitudeColumn,int longitudeColumn)
|
||||
{
|
||||
Mutex::Lock _l(_lock);
|
||||
return _load(pathToCsv,ipStartColumn,ipEndColumn,latitudeColumn,longitudeColumn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to locate an IP
|
||||
*
|
||||
* This returns true if x, y, and z are set. If the return value is false
|
||||
* the values of x, y, and z are undefined.
|
||||
*
|
||||
* @param ip IPv4 or IPv6 address
|
||||
* @param x Reference to variable to receive X
|
||||
* @param y Reference to variable to receive Y
|
||||
* @param z Reference to variable to receive Z
|
||||
* @return True if coordinates were set
|
||||
*/
|
||||
bool locate(const InetAddress &ip,int &x,int &y,int &z);
|
||||
|
||||
/**
|
||||
* @return True if IP database/service is available for queries (otherwise locate() will always be false)
|
||||
*/
|
||||
inline bool available() const
|
||||
{
|
||||
Mutex::Lock _l(_lock);
|
||||
return ((_v4db.size() + _v6db.size()) > 0);
|
||||
}
|
||||
|
||||
private:
|
||||
struct _V4E
|
||||
{
|
||||
uint32_t start;
|
||||
uint32_t end;
|
||||
float lat,lon;
|
||||
int16_t x,y,z;
|
||||
|
||||
inline bool operator<(const _V4E &e) const { return (start < e.start); }
|
||||
};
|
||||
|
||||
struct _V6E
|
||||
{
|
||||
uint8_t start[16];
|
||||
uint8_t end[16];
|
||||
float lat,lon;
|
||||
int16_t x,y,z;
|
||||
|
||||
inline bool operator<(const _V6E &e) const { return (memcmp(start,e.start,16) < 0); }
|
||||
};
|
||||
|
||||
static void _parseLine(const char *line,std::vector<_V4E> &v4db,std::vector<_V6E> &v6db,int ipStartColumn,int ipEndColumn,int latitudeColumn,int longitudeColumn);
|
||||
long _load(const char *pathToCsv,int ipStartColumn,int ipEndColumn,int latitudeColumn,int longitudeColumn);
|
||||
|
||||
std::string _pathToCsv;
|
||||
int _ipStartColumn;
|
||||
int _ipEndColumn;
|
||||
int _latitudeColumn;
|
||||
int _longitudeColumn;
|
||||
|
||||
uint64_t _lastFileCheckTime;
|
||||
uint64_t _csvModificationTime;
|
||||
int64_t _csvFileSize;
|
||||
|
||||
std::vector<_V4E> _v4db;
|
||||
std::vector<_V6E> _v6db;
|
||||
|
||||
Mutex _lock;
|
||||
};
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
||||
#endif // ZT_ENABLE_CLUSTER
|
||||
|
||||
#endif
|
628
service/ControlPlane.cpp
Normal file
628
service/ControlPlane.cpp
Normal file
|
@ -0,0 +1,628 @@
|
|||
/*
|
||||
* ZeroTier One - Network Virtualization Everywhere
|
||||
* Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "ControlPlane.hpp"
|
||||
#include "OneService.hpp"
|
||||
|
||||
#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
|
||||
|
||||
#ifdef ZT_USE_SYSTEM_JSON_PARSER
|
||||
#include <json-parser/json.h>
|
||||
#else
|
||||
#include "../ext/json-parser/json.h"
|
||||
#endif
|
||||
|
||||
#ifdef ZT_ENABLE_NETWORK_CONTROLLER
|
||||
#include "../controller/SqliteNetworkController.hpp"
|
||||
#endif
|
||||
|
||||
#include "../node/InetAddress.hpp"
|
||||
#include "../node/Node.hpp"
|
||||
#include "../node/Utils.hpp"
|
||||
#include "../osdep/OSUtils.hpp"
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
static std::string _jsonEscape(const char *s)
|
||||
{
|
||||
std::string buf;
|
||||
for(const char *p=s;(*p);++p) {
|
||||
switch(*p) {
|
||||
case '\t': buf.append("\\t"); break;
|
||||
case '\b': buf.append("\\b"); break;
|
||||
case '\r': buf.append("\\r"); break;
|
||||
case '\n': buf.append("\\n"); break;
|
||||
case '\f': buf.append("\\f"); break;
|
||||
case '"': buf.append("\\\""); break;
|
||||
case '\\': buf.append("\\\\"); break;
|
||||
case '/': buf.append("\\/"); break;
|
||||
default: buf.push_back(*p); break;
|
||||
}
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
static std::string _jsonEscape(const std::string &s) { return _jsonEscape(s.c_str()); }
|
||||
|
||||
static std::string _jsonEnumerate(const struct sockaddr_storage *ss,unsigned int count)
|
||||
{
|
||||
std::string buf;
|
||||
buf.push_back('[');
|
||||
for(unsigned int i=0;i<count;++i) {
|
||||
if (i > 0)
|
||||
buf.push_back(',');
|
||||
buf.push_back('"');
|
||||
buf.append(_jsonEscape(reinterpret_cast<const InetAddress *>(&(ss[i]))->toString()));
|
||||
buf.push_back('"');
|
||||
}
|
||||
buf.push_back(']');
|
||||
return buf;
|
||||
}
|
||||
static std::string _jsonEnumerate(const ZT_VirtualNetworkRoute *routes,unsigned int count)
|
||||
{
|
||||
std::string buf;
|
||||
buf.push_back('[');
|
||||
for(unsigned int i=0;i<count;++i) {
|
||||
if (i > 0)
|
||||
buf.push_back(',');
|
||||
buf.append("{\"target\":\"");
|
||||
buf.append(_jsonEscape(reinterpret_cast<const InetAddress *>(&(routes[i].target))->toString()));
|
||||
buf.append("\",\"via\":");
|
||||
if (routes[i].via.ss_family == routes[i].target.ss_family) {
|
||||
buf.push_back('"');
|
||||
buf.append(_jsonEscape(reinterpret_cast<const InetAddress *>(&(routes[i].via))->toIpString()));
|
||||
buf.append("\",");
|
||||
} else buf.append("null,");
|
||||
char tmp[1024];
|
||||
Utils::snprintf(tmp,sizeof(tmp),"\"flags\":%u,\"metric\":%u}",(unsigned int)routes[i].flags,(unsigned int)routes[i].metric);
|
||||
buf.append(tmp);
|
||||
}
|
||||
buf.push_back(']');
|
||||
return buf;
|
||||
}
|
||||
|
||||
static void _jsonAppend(unsigned int depth,std::string &buf,const ZT_VirtualNetworkConfig *nc,const std::string &portDeviceName,const OneService::NetworkSettings &localSettings)
|
||||
{
|
||||
char json[4096];
|
||||
char prefix[32];
|
||||
|
||||
if (depth >= sizeof(prefix)) // sanity check -- shouldn't be possible
|
||||
return;
|
||||
for(unsigned int i=0;i<depth;++i)
|
||||
prefix[i] = '\t';
|
||||
prefix[depth] = '\0';
|
||||
|
||||
const char *nstatus = "",*ntype = "";
|
||||
switch(nc->status) {
|
||||
case ZT_NETWORK_STATUS_REQUESTING_CONFIGURATION: nstatus = "REQUESTING_CONFIGURATION"; break;
|
||||
case ZT_NETWORK_STATUS_OK: nstatus = "OK"; break;
|
||||
case ZT_NETWORK_STATUS_ACCESS_DENIED: nstatus = "ACCESS_DENIED"; break;
|
||||
case ZT_NETWORK_STATUS_NOT_FOUND: nstatus = "NOT_FOUND"; break;
|
||||
case ZT_NETWORK_STATUS_PORT_ERROR: nstatus = "PORT_ERROR"; break;
|
||||
case ZT_NETWORK_STATUS_CLIENT_TOO_OLD: nstatus = "CLIENT_TOO_OLD"; break;
|
||||
}
|
||||
switch(nc->type) {
|
||||
case ZT_NETWORK_TYPE_PRIVATE: ntype = "PRIVATE"; break;
|
||||
case ZT_NETWORK_TYPE_PUBLIC: ntype = "PUBLIC"; break;
|
||||
}
|
||||
|
||||
Utils::snprintf(json,sizeof(json),
|
||||
"%s{\n"
|
||||
"%s\t\"nwid\": \"%.16llx\",\n"
|
||||
"%s\t\"mac\": \"%.2x:%.2x:%.2x:%.2x:%.2x:%.2x\",\n"
|
||||
"%s\t\"name\": \"%s\",\n"
|
||||
"%s\t\"status\": \"%s\",\n"
|
||||
"%s\t\"type\": \"%s\",\n"
|
||||
"%s\t\"mtu\": %u,\n"
|
||||
"%s\t\"dhcp\": %s,\n"
|
||||
"%s\t\"bridge\": %s,\n"
|
||||
"%s\t\"broadcastEnabled\": %s,\n"
|
||||
"%s\t\"portError\": %d,\n"
|
||||
"%s\t\"netconfRevision\": %lu,\n"
|
||||
"%s\t\"assignedAddresses\": %s,\n"
|
||||
"%s\t\"routes\": %s,\n"
|
||||
"%s\t\"portDeviceName\": \"%s\",\n"
|
||||
"%s\t\"allowManaged\": %s,\n"
|
||||
"%s\t\"allowGlobal\": %s,\n"
|
||||
"%s\t\"allowDefault\": %s\n"
|
||||
"%s}",
|
||||
prefix,
|
||||
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,
|
||||
prefix,ntype,
|
||||
prefix,nc->mtu,
|
||||
prefix,(nc->dhcp == 0) ? "false" : "true",
|
||||
prefix,(nc->bridge == 0) ? "false" : "true",
|
||||
prefix,(nc->broadcastEnabled == 0) ? "false" : "true",
|
||||
prefix,nc->portError,
|
||||
prefix,nc->netconfRevision,
|
||||
prefix,_jsonEnumerate(nc->assignedAddresses,nc->assignedAddressCount).c_str(),
|
||||
prefix,_jsonEnumerate(nc->routes,nc->routeCount).c_str(),
|
||||
prefix,_jsonEscape(portDeviceName).c_str(),
|
||||
prefix,(localSettings.allowManaged) ? "true" : "false",
|
||||
prefix,(localSettings.allowGlobal) ? "true" : "false",
|
||||
prefix,(localSettings.allowDefault) ? "true" : "false",
|
||||
prefix);
|
||||
buf.append(json);
|
||||
}
|
||||
|
||||
static std::string _jsonEnumerate(unsigned int depth,const ZT_PeerPhysicalPath *pp,unsigned int count)
|
||||
{
|
||||
char json[1024];
|
||||
char prefix[32];
|
||||
|
||||
if (depth >= sizeof(prefix)) // sanity check -- shouldn't be possible
|
||||
return std::string();
|
||||
for(unsigned int i=0;i<depth;++i)
|
||||
prefix[i] = '\t';
|
||||
prefix[depth] = '\0';
|
||||
|
||||
std::string buf;
|
||||
for(unsigned int i=0;i<count;++i) {
|
||||
if (i > 0)
|
||||
buf.push_back(',');
|
||||
Utils::snprintf(json,sizeof(json),
|
||||
"{\n"
|
||||
"%s\t\"address\": \"%s\",\n"
|
||||
"%s\t\"lastSend\": %llu,\n"
|
||||
"%s\t\"lastReceive\": %llu,\n"
|
||||
"%s\t\"active\": %s,\n"
|
||||
"%s\t\"preferred\": %s,\n"
|
||||
"%s\t\"trustedPathId\": %llu\n"
|
||||
"%s}",
|
||||
prefix,_jsonEscape(reinterpret_cast<const InetAddress *>(&(pp[i].address))->toString()).c_str(),
|
||||
prefix,pp[i].lastSend,
|
||||
prefix,pp[i].lastReceive,
|
||||
prefix,(pp[i].active == 0) ? "false" : "true",
|
||||
prefix,(pp[i].preferred == 0) ? "false" : "true",
|
||||
prefix,pp[i].trustedPathId,
|
||||
prefix);
|
||||
buf.append(json);
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
static void _jsonAppend(unsigned int depth,std::string &buf,const ZT_Peer *peer)
|
||||
{
|
||||
char json[1024];
|
||||
char prefix[32];
|
||||
|
||||
if (depth >= sizeof(prefix)) // sanity check -- shouldn't be possible
|
||||
return;
|
||||
for(unsigned int i=0;i<depth;++i)
|
||||
prefix[i] = '\t';
|
||||
prefix[depth] = '\0';
|
||||
|
||||
const char *prole = "";
|
||||
switch(peer->role) {
|
||||
case ZT_PEER_ROLE_LEAF: prole = "LEAF"; break;
|
||||
case ZT_PEER_ROLE_RELAY: prole = "RELAY"; break;
|
||||
case ZT_PEER_ROLE_ROOT: prole = "ROOT"; break;
|
||||
}
|
||||
|
||||
Utils::snprintf(json,sizeof(json),
|
||||
"%s{\n"
|
||||
"%s\t\"address\": \"%.10llx\",\n"
|
||||
"%s\t\"lastUnicastFrame\": %llu,\n"
|
||||
"%s\t\"lastMulticastFrame\": %llu,\n"
|
||||
"%s\t\"versionMajor\": %d,\n"
|
||||
"%s\t\"versionMinor\": %d,\n"
|
||||
"%s\t\"versionRev\": %d,\n"
|
||||
"%s\t\"version\": \"%d.%d.%d\",\n"
|
||||
"%s\t\"latency\": %u,\n"
|
||||
"%s\t\"role\": \"%s\",\n"
|
||||
"%s\t\"paths\": [%s]\n"
|
||||
"%s}",
|
||||
prefix,
|
||||
prefix,peer->address,
|
||||
prefix,peer->lastUnicastFrame,
|
||||
prefix,peer->lastMulticastFrame,
|
||||
prefix,peer->versionMajor,
|
||||
prefix,peer->versionMinor,
|
||||
prefix,peer->versionRev,
|
||||
prefix,peer->versionMajor,peer->versionMinor,peer->versionRev,
|
||||
prefix,peer->latency,
|
||||
prefix,prole,
|
||||
prefix,_jsonEnumerate(depth+1,peer->paths,peer->pathCount).c_str(),
|
||||
prefix);
|
||||
buf.append(json);
|
||||
}
|
||||
|
||||
ControlPlane::ControlPlane(OneService *svc,Node *n,const char *uiStaticPath) :
|
||||
_svc(svc),
|
||||
_node(n),
|
||||
#ifdef ZT_ENABLE_NETWORK_CONTROLLER
|
||||
_controller((SqliteNetworkController *)0),
|
||||
#endif
|
||||
_uiStaticPath((uiStaticPath) ? uiStaticPath : "")
|
||||
{
|
||||
}
|
||||
|
||||
ControlPlane::~ControlPlane()
|
||||
{
|
||||
}
|
||||
|
||||
unsigned int ControlPlane::handleRequest(
|
||||
const InetAddress &fromAddress,
|
||||
unsigned int httpMethod,
|
||||
const std::string &path,
|
||||
const std::map<std::string,std::string> &headers,
|
||||
const std::string &body,
|
||||
std::string &responseBody,
|
||||
std::string &responseContentType)
|
||||
{
|
||||
char json[8194];
|
||||
unsigned int scode = 404;
|
||||
std::vector<std::string> ps(Utils::split(path.c_str(),"/","",""));
|
||||
std::map<std::string,std::string> urlArgs;
|
||||
Mutex::Lock _l(_lock);
|
||||
|
||||
if (!((fromAddress.ipsEqual(InetAddress::LO4))||(fromAddress.ipsEqual(InetAddress::LO6))))
|
||||
return 403; // Forbidden: we only allow access from localhost right now
|
||||
|
||||
/* Note: this is kind of restricted in what it'll take. It does not support
|
||||
* URL encoding, and /'s in URL args will screw it up. But the only URL args
|
||||
* it really uses in ?jsonp=funcionName, and otherwise it just takes simple
|
||||
* paths to simply-named resources. */
|
||||
if (ps.size() > 0) {
|
||||
std::size_t qpos = ps[ps.size() - 1].find('?');
|
||||
if (qpos != std::string::npos) {
|
||||
std::string args(ps[ps.size() - 1].substr(qpos + 1));
|
||||
ps[ps.size() - 1] = ps[ps.size() - 1].substr(0,qpos);
|
||||
std::vector<std::string> asplit(Utils::split(args.c_str(),"&","",""));
|
||||
for(std::vector<std::string>::iterator a(asplit.begin());a!=asplit.end();++a) {
|
||||
std::size_t eqpos = a->find('=');
|
||||
if (eqpos == std::string::npos)
|
||||
urlArgs[*a] = "";
|
||||
else urlArgs[a->substr(0,eqpos)] = a->substr(eqpos + 1);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ps.push_back(std::string("index.html"));
|
||||
}
|
||||
|
||||
bool isAuth = false;
|
||||
{
|
||||
std::map<std::string,std::string>::const_iterator ah(headers.find("x-zt1-auth"));
|
||||
if ((ah != headers.end())&&(_authTokens.count(ah->second) > 0)) {
|
||||
isAuth = true;
|
||||
} else {
|
||||
ah = urlArgs.find("auth");
|
||||
if ((ah != urlArgs.end())&&(_authTokens.count(ah->second) > 0))
|
||||
isAuth = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (httpMethod == HTTP_GET) {
|
||||
|
||||
std::string ext;
|
||||
std::size_t dotIdx = ps[0].find_last_of('.');
|
||||
if (dotIdx != std::string::npos)
|
||||
ext = ps[0].substr(dotIdx);
|
||||
|
||||
if ((ps.size() == 1)&&(ext.length() >= 2)&&(ext[0] == '.')) {
|
||||
/* Static web pages can be served without authentication to enable a simple web
|
||||
* UI. This is still only allowed from approved IP addresses. Anything with a
|
||||
* dot in the first path element (e.g. foo.html) is considered a static page,
|
||||
* as nothing in the API is so named. */
|
||||
|
||||
if (_uiStaticPath.length() > 0) {
|
||||
if (ext == ".html")
|
||||
responseContentType = "text/html";
|
||||
else if (ext == ".js")
|
||||
responseContentType = "application/javascript";
|
||||
else if (ext == ".jsx")
|
||||
responseContentType = "text/jsx";
|
||||
else if (ext == ".json")
|
||||
responseContentType = "application/json";
|
||||
else if (ext == ".css")
|
||||
responseContentType = "text/css";
|
||||
else if (ext == ".png")
|
||||
responseContentType = "image/png";
|
||||
else if (ext == ".jpg")
|
||||
responseContentType = "image/jpeg";
|
||||
else if (ext == ".gif")
|
||||
responseContentType = "image/gif";
|
||||
else if (ext == ".txt")
|
||||
responseContentType = "text/plain";
|
||||
else if (ext == ".xml")
|
||||
responseContentType = "text/xml";
|
||||
else if (ext == ".svg")
|
||||
responseContentType = "image/svg+xml";
|
||||
else responseContentType = "application/octet-stream";
|
||||
scode = OSUtils::readFile((_uiStaticPath + ZT_PATH_SEPARATOR_S + ps[0]).c_str(),responseBody) ? 200 : 404;
|
||||
} else {
|
||||
scode = 404;
|
||||
}
|
||||
|
||||
} else if (isAuth) {
|
||||
/* Things that require authentication -- a.k.a. everything but static web app pages. */
|
||||
|
||||
if (ps[0] == "status") {
|
||||
responseContentType = "application/json";
|
||||
|
||||
ZT_NodeStatus status;
|
||||
_node->status(&status);
|
||||
|
||||
std::string clusterJson;
|
||||
#ifdef ZT_ENABLE_CLUSTER
|
||||
{
|
||||
ZT_ClusterStatus cs;
|
||||
_node->clusterStatus(&cs);
|
||||
|
||||
if (cs.clusterSize >= 1) {
|
||||
char t[1024];
|
||||
Utils::snprintf(t,sizeof(t),"{\n\t\t\"myId\": %u,\n\t\t\"clusterSize\": %u,\n\t\t\"members\": [",cs.myId,cs.clusterSize);
|
||||
clusterJson.append(t);
|
||||
for(unsigned int i=0;i<cs.clusterSize;++i) {
|
||||
Utils::snprintf(t,sizeof(t),"%s\t\t\t{\n\t\t\t\t\"id\": %u,\n\t\t\t\t\"msSinceLastHeartbeat\": %u,\n\t\t\t\t\"alive\": %s,\n\t\t\t\t\"x\": %d,\n\t\t\t\t\"y\": %d,\n\t\t\t\t\"z\": %d,\n\t\t\t\t\"load\": %llu,\n\t\t\t\t\"peers\": %llu\n\t\t\t}",
|
||||
((i == 0) ? "\n" : ",\n"),
|
||||
cs.members[i].id,
|
||||
cs.members[i].msSinceLastHeartbeat,
|
||||
(cs.members[i].alive != 0) ? "true" : "false",
|
||||
cs.members[i].x,
|
||||
cs.members[i].y,
|
||||
cs.members[i].z,
|
||||
cs.members[i].load,
|
||||
cs.members[i].peers);
|
||||
clusterJson.append(t);
|
||||
}
|
||||
clusterJson.append(" ]\n\t\t}");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
Utils::snprintf(json,sizeof(json),
|
||||
"{\n"
|
||||
"\t\"address\": \"%.10llx\",\n"
|
||||
"\t\"publicIdentity\": \"%s\",\n"
|
||||
"\t\"worldId\": %llu,\n"
|
||||
"\t\"worldTimestamp\": %llu,\n"
|
||||
"\t\"online\": %s,\n"
|
||||
"\t\"tcpFallbackActive\": %s,\n"
|
||||
"\t\"versionMajor\": %d,\n"
|
||||
"\t\"versionMinor\": %d,\n"
|
||||
"\t\"versionRev\": %d,\n"
|
||||
"\t\"version\": \"%d.%d.%d\",\n"
|
||||
"\t\"clock\": %llu,\n"
|
||||
"\t\"cluster\": %s\n"
|
||||
"}\n",
|
||||
status.address,
|
||||
status.publicIdentity,
|
||||
status.worldId,
|
||||
status.worldTimestamp,
|
||||
(status.online) ? "true" : "false",
|
||||
(_svc->tcpFallbackActive()) ? "true" : "false",
|
||||
ZEROTIER_ONE_VERSION_MAJOR,
|
||||
ZEROTIER_ONE_VERSION_MINOR,
|
||||
ZEROTIER_ONE_VERSION_REVISION,
|
||||
ZEROTIER_ONE_VERSION_MAJOR,ZEROTIER_ONE_VERSION_MINOR,ZEROTIER_ONE_VERSION_REVISION,
|
||||
(unsigned long long)OSUtils::now(),
|
||||
((clusterJson.length() > 0) ? clusterJson.c_str() : "null"));
|
||||
responseBody = json;
|
||||
scode = 200;
|
||||
} else if (ps[0] == "config") {
|
||||
responseContentType = "application/json";
|
||||
responseBody = "{}"; // TODO
|
||||
scode = 200;
|
||||
} else if (ps[0] == "network") {
|
||||
ZT_VirtualNetworkList *nws = _node->networks();
|
||||
if (nws) {
|
||||
if (ps.size() == 1) {
|
||||
// Return [array] of all networks
|
||||
responseContentType = "application/json";
|
||||
responseBody = "[\n";
|
||||
for(unsigned long i=0;i<nws->networkCount;++i) {
|
||||
if (i > 0)
|
||||
responseBody.append(",");
|
||||
OneService::NetworkSettings localSettings;
|
||||
_svc->getNetworkSettings(nws->networks[i].nwid,localSettings);
|
||||
_jsonAppend(1,responseBody,&(nws->networks[i]),_svc->portDeviceName(nws->networks[i].nwid),localSettings);
|
||||
}
|
||||
responseBody.append("\n]\n");
|
||||
scode = 200;
|
||||
} else if (ps.size() == 2) {
|
||||
// Return a single network by ID or 404 if not found
|
||||
uint64_t wantnw = Utils::hexStrToU64(ps[1].c_str());
|
||||
for(unsigned long i=0;i<nws->networkCount;++i) {
|
||||
if (nws->networks[i].nwid == wantnw) {
|
||||
responseContentType = "application/json";
|
||||
OneService::NetworkSettings localSettings;
|
||||
_svc->getNetworkSettings(nws->networks[i].nwid,localSettings);
|
||||
_jsonAppend(0,responseBody,&(nws->networks[i]),_svc->portDeviceName(nws->networks[i].nwid),localSettings);
|
||||
responseBody.push_back('\n');
|
||||
scode = 200;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} // else 404
|
||||
_node->freeQueryResult((void *)nws);
|
||||
} else scode = 500;
|
||||
} else if (ps[0] == "peer") {
|
||||
ZT_PeerList *pl = _node->peers();
|
||||
if (pl) {
|
||||
if (ps.size() == 1) {
|
||||
// Return [array] of all peers
|
||||
responseContentType = "application/json";
|
||||
responseBody = "[\n";
|
||||
for(unsigned long i=0;i<pl->peerCount;++i) {
|
||||
if (i > 0)
|
||||
responseBody.append(",\n");
|
||||
_jsonAppend(1,responseBody,&(pl->peers[i]));
|
||||
}
|
||||
responseBody.append("\n]\n");
|
||||
scode = 200;
|
||||
} else if (ps.size() == 2) {
|
||||
// Return a single peer by ID or 404 if not found
|
||||
uint64_t wantp = Utils::hexStrToU64(ps[1].c_str());
|
||||
for(unsigned long i=0;i<pl->peerCount;++i) {
|
||||
if (pl->peers[i].address == wantp) {
|
||||
responseContentType = "application/json";
|
||||
_jsonAppend(0,responseBody,&(pl->peers[i]));
|
||||
responseBody.push_back('\n');
|
||||
scode = 200;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} // else 404
|
||||
_node->freeQueryResult((void *)pl);
|
||||
} else scode = 500;
|
||||
} else if (ps[0] == "newIdentity") {
|
||||
// Return a newly generated ZeroTier identity -- this is primarily for debugging
|
||||
// and testing to make it easy for automated test scripts to generate test IDs.
|
||||
Identity newid;
|
||||
newid.generate();
|
||||
responseBody = newid.toString(true);
|
||||
responseContentType = "text/plain";
|
||||
scode = 200;
|
||||
} else {
|
||||
#ifdef ZT_ENABLE_NETWORK_CONTROLLER
|
||||
if (_controller)
|
||||
scode = _controller->handleControlPlaneHttpGET(std::vector<std::string>(ps.begin()+1,ps.end()),urlArgs,headers,body,responseBody,responseContentType);
|
||||
else scode = 404;
|
||||
#else
|
||||
scode = 404;
|
||||
#endif
|
||||
}
|
||||
|
||||
} else scode = 401; // isAuth == false
|
||||
|
||||
} else if ((httpMethod == HTTP_POST)||(httpMethod == HTTP_PUT)) {
|
||||
|
||||
if (isAuth) {
|
||||
|
||||
if (ps[0] == "config") {
|
||||
// TODO
|
||||
} else if (ps[0] == "network") {
|
||||
if (ps.size() == 2) {
|
||||
uint64_t wantnw = Utils::hexStrToU64(ps[1].c_str());
|
||||
_node->join(wantnw,(void *)0); // does nothing if we are a member
|
||||
ZT_VirtualNetworkList *nws = _node->networks();
|
||||
if (nws) {
|
||||
for(unsigned long i=0;i<nws->networkCount;++i) {
|
||||
if (nws->networks[i].nwid == wantnw) {
|
||||
OneService::NetworkSettings localSettings;
|
||||
_svc->getNetworkSettings(nws->networks[i].nwid,localSettings);
|
||||
|
||||
json_value *j = json_parse(body.c_str(),body.length());
|
||||
if (j) {
|
||||
if (j->type == json_object) {
|
||||
for(unsigned int k=0;k<j->u.object.length;++k) {
|
||||
if (!strcmp(j->u.object.values[k].name,"allowManaged")) {
|
||||
if (j->u.object.values[k].value->type == json_boolean)
|
||||
localSettings.allowManaged = (j->u.object.values[k].value->u.boolean != 0);
|
||||
} else if (!strcmp(j->u.object.values[k].name,"allowGlobal")) {
|
||||
if (j->u.object.values[k].value->type == json_boolean)
|
||||
localSettings.allowGlobal = (j->u.object.values[k].value->u.boolean != 0);
|
||||
} else if (!strcmp(j->u.object.values[k].name,"allowDefault")) {
|
||||
if (j->u.object.values[k].value->type == json_boolean)
|
||||
localSettings.allowDefault = (j->u.object.values[k].value->u.boolean != 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
json_value_free(j);
|
||||
}
|
||||
|
||||
_svc->setNetworkSettings(nws->networks[i].nwid,localSettings);
|
||||
|
||||
responseContentType = "application/json";
|
||||
_jsonAppend(0,responseBody,&(nws->networks[i]),_svc->portDeviceName(nws->networks[i].nwid),localSettings);
|
||||
responseBody.push_back('\n');
|
||||
scode = 200;
|
||||
break;
|
||||
}
|
||||
}
|
||||
_node->freeQueryResult((void *)nws);
|
||||
} else scode = 500;
|
||||
}
|
||||
} else {
|
||||
#ifdef ZT_ENABLE_NETWORK_CONTROLLER
|
||||
if (_controller)
|
||||
scode = _controller->handleControlPlaneHttpPOST(std::vector<std::string>(ps.begin()+1,ps.end()),urlArgs,headers,body,responseBody,responseContentType);
|
||||
else scode = 404;
|
||||
#else
|
||||
scode = 404;
|
||||
#endif
|
||||
}
|
||||
|
||||
} else scode = 401; // isAuth == false
|
||||
|
||||
} else if (httpMethod == HTTP_DELETE) {
|
||||
|
||||
if (isAuth) {
|
||||
|
||||
if (ps[0] == "config") {
|
||||
// TODO
|
||||
} else if (ps[0] == "network") {
|
||||
ZT_VirtualNetworkList *nws = _node->networks();
|
||||
if (nws) {
|
||||
if (ps.size() == 2) {
|
||||
uint64_t wantnw = Utils::hexStrToU64(ps[1].c_str());
|
||||
for(unsigned long i=0;i<nws->networkCount;++i) {
|
||||
if (nws->networks[i].nwid == wantnw) {
|
||||
_node->leave(wantnw,(void **)0);
|
||||
responseBody = "true";
|
||||
responseContentType = "application/json";
|
||||
scode = 200;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} // else 404
|
||||
_node->freeQueryResult((void *)nws);
|
||||
} else scode = 500;
|
||||
} else {
|
||||
#ifdef ZT_ENABLE_NETWORK_CONTROLLER
|
||||
if (_controller)
|
||||
scode = _controller->handleControlPlaneHttpDELETE(std::vector<std::string>(ps.begin()+1,ps.end()),urlArgs,headers,body,responseBody,responseContentType);
|
||||
else scode = 404;
|
||||
#else
|
||||
scode = 404;
|
||||
#endif
|
||||
}
|
||||
|
||||
} else {
|
||||
scode = 401; // isAuth = false
|
||||
}
|
||||
|
||||
} else {
|
||||
scode = 400;
|
||||
responseBody = "Method not supported.";
|
||||
}
|
||||
|
||||
// Wrap result in jsonp function call if the user included a jsonp= url argument.
|
||||
// Also double-check isAuth since forbidding this without auth feels safer.
|
||||
std::map<std::string,std::string>::const_iterator jsonp(urlArgs.find("jsonp"));
|
||||
if ((isAuth)&&(jsonp != urlArgs.end())&&(responseContentType == "application/json")) {
|
||||
if (responseBody.length() > 0)
|
||||
responseBody = jsonp->second + "(" + responseBody + ");";
|
||||
else responseBody = jsonp->second + "(null);";
|
||||
responseContentType = "application/javascript";
|
||||
}
|
||||
|
||||
return scode;
|
||||
}
|
||||
|
||||
} // namespace ZeroTier
|
102
service/ControlPlane.hpp
Normal file
102
service/ControlPlane.hpp
Normal file
|
@ -0,0 +1,102 @@
|
|||
/*
|
||||
* ZeroTier One - Network Virtualization Everywhere
|
||||
* Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef ZT_ONE_CONTROLPLANE_HPP
|
||||
#define ZT_ONE_CONTROLPLANE_HPP
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <set>
|
||||
|
||||
#include "../include/ZeroTierOne.h"
|
||||
|
||||
#include "../node/Mutex.hpp"
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
class OneService;
|
||||
class Node;
|
||||
class SqliteNetworkController;
|
||||
struct InetAddress;
|
||||
|
||||
/**
|
||||
* HTTP control plane and static web server
|
||||
*/
|
||||
class ControlPlane
|
||||
{
|
||||
public:
|
||||
ControlPlane(OneService *svc,Node *n,const char *uiStaticPath);
|
||||
~ControlPlane();
|
||||
|
||||
#ifdef ZT_ENABLE_NETWORK_CONTROLLER
|
||||
/**
|
||||
* Set controller, which will be available under /controller
|
||||
*
|
||||
* @param c Network controller instance
|
||||
*/
|
||||
inline void setController(SqliteNetworkController *c)
|
||||
{
|
||||
Mutex::Lock _l(_lock);
|
||||
_controller = c;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Add an authentication token for API access
|
||||
*/
|
||||
inline void addAuthToken(const char *tok)
|
||||
{
|
||||
Mutex::Lock _l(_lock);
|
||||
_authTokens.insert(std::string(tok));
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle HTTP request
|
||||
*
|
||||
* @param fromAddress Originating IP address of request
|
||||
* @param httpMethod HTTP method (as defined in ext/http-parser/http_parser.h)
|
||||
* @param path Request path
|
||||
* @param headers Request headers
|
||||
* @param body Request body
|
||||
* @param responseBody Result parameter: fill with response data
|
||||
* @param responseContentType Result parameter: fill with content type
|
||||
* @return HTTP response code
|
||||
*/
|
||||
unsigned int handleRequest(
|
||||
const InetAddress &fromAddress,
|
||||
unsigned int httpMethod,
|
||||
const std::string &path,
|
||||
const std::map<std::string,std::string> &headers,
|
||||
const std::string &body,
|
||||
std::string &responseBody,
|
||||
std::string &responseContentType);
|
||||
|
||||
private:
|
||||
OneService *const _svc;
|
||||
Node *const _node;
|
||||
#ifdef ZT_ENABLE_NETWORK_CONTROLLER
|
||||
SqliteNetworkController *_controller;
|
||||
#endif
|
||||
std::string _uiStaticPath;
|
||||
std::set<std::string> _authTokens;
|
||||
Mutex _lock;
|
||||
};
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
||||
#endif
|
1993
service/OneService.cpp
Normal file
1993
service/OneService.cpp
Normal file
File diff suppressed because it is too large
Load diff
187
service/OneService.hpp
Normal file
187
service/OneService.hpp
Normal file
|
@ -0,0 +1,187 @@
|
|||
/*
|
||||
* ZeroTier One - Network Virtualization Everywhere
|
||||
* Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef ZT_ONESERVICE_HPP
|
||||
#define ZT_ONESERVICE_HPP
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
/**
|
||||
* Local service for ZeroTier One as system VPN/NFV provider
|
||||
*
|
||||
* If built with ZT_ENABLE_NETWORK_CONTROLLER defined, this includes and
|
||||
* runs controller/SqliteNetworkController with a database called
|
||||
* controller.db in the specified home directory.
|
||||
*
|
||||
* If built with ZT_AUTO_UPDATE, an official ZeroTier update URL is
|
||||
* periodically checked and updates are automatically downloaded, verified
|
||||
* against a built-in list of update signing keys, and installed. This is
|
||||
* only supported for certain platforms.
|
||||
*
|
||||
* If built with ZT_ENABLE_CLUSTER, a 'cluster' file is checked and if
|
||||
* present is read to determine the identity of other cluster members.
|
||||
*/
|
||||
class OneService
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Returned by node main if/when it terminates
|
||||
*/
|
||||
enum ReasonForTermination
|
||||
{
|
||||
/**
|
||||
* Instance is still running
|
||||
*/
|
||||
ONE_STILL_RUNNING = 0,
|
||||
|
||||
/**
|
||||
* Normal shutdown
|
||||
*/
|
||||
ONE_NORMAL_TERMINATION = 1,
|
||||
|
||||
/**
|
||||
* A serious unrecoverable error has occurred
|
||||
*/
|
||||
ONE_UNRECOVERABLE_ERROR = 2,
|
||||
|
||||
/**
|
||||
* Your identity has collided with another
|
||||
*/
|
||||
ONE_IDENTITY_COLLISION = 3
|
||||
};
|
||||
|
||||
/**
|
||||
* Local settings for each network
|
||||
*/
|
||||
struct NetworkSettings
|
||||
{
|
||||
/**
|
||||
* Allow this network to configure IP addresses and routes?
|
||||
*/
|
||||
bool allowManaged;
|
||||
|
||||
/**
|
||||
* Allow configuration of IPs and routes within global (Internet) IP space?
|
||||
*/
|
||||
bool allowGlobal;
|
||||
|
||||
/**
|
||||
* Allow overriding of system default routes for "full tunnel" operation?
|
||||
*/
|
||||
bool allowDefault;
|
||||
};
|
||||
|
||||
/**
|
||||
* @return Platform default home path or empty string if this platform doesn't have one
|
||||
*/
|
||||
static std::string platformDefaultHomePath();
|
||||
|
||||
/**
|
||||
* @return Auto-update URL or empty string if auto-updates unsupported or not enabled
|
||||
*/
|
||||
static std::string autoUpdateUrl();
|
||||
|
||||
/**
|
||||
* Create a new instance of the service
|
||||
*
|
||||
* Once created, you must call the run() method to actually start
|
||||
* processing.
|
||||
*
|
||||
* The port is saved to a file in the home path called zerotier-one.port,
|
||||
* which is used by the CLI and can be used to see which port was chosen if
|
||||
* 0 (random port) is picked.
|
||||
*
|
||||
* @param hp Home path
|
||||
* @param port TCP and UDP port for packets and HTTP control (if 0, pick random port)
|
||||
*/
|
||||
static OneService *newInstance(
|
||||
const char *hp,
|
||||
unsigned int port);
|
||||
|
||||
virtual ~OneService();
|
||||
|
||||
/**
|
||||
* Execute the service main I/O loop until terminated
|
||||
*
|
||||
* The terminate() method may be called from a signal handler or another
|
||||
* thread to terminate execution. Otherwise this will not return unless
|
||||
* another condition terminates execution such as a fatal error.
|
||||
*/
|
||||
virtual ReasonForTermination run() = 0;
|
||||
|
||||
/**
|
||||
* @return Reason for terminating or ONE_STILL_RUNNING if running
|
||||
*/
|
||||
virtual ReasonForTermination reasonForTermination() const = 0;
|
||||
|
||||
/**
|
||||
* @return Fatal error message or empty string if none
|
||||
*/
|
||||
virtual std::string fatalErrorMessage() const = 0;
|
||||
|
||||
/**
|
||||
* @return System device name corresponding with a given ZeroTier network ID or empty string if not opened yet or network ID not found
|
||||
*/
|
||||
virtual std::string portDeviceName(uint64_t nwid) const = 0;
|
||||
|
||||
/**
|
||||
* @return True if TCP fallback is currently active
|
||||
*/
|
||||
virtual bool tcpFallbackActive() const = 0;
|
||||
|
||||
/**
|
||||
* Terminate background service (can be called from other threads)
|
||||
*/
|
||||
virtual void terminate() = 0;
|
||||
|
||||
/**
|
||||
* Get local settings for a network
|
||||
*
|
||||
* @param nwid Network ID
|
||||
* @param settings Buffer to fill with local network settings
|
||||
* @return True if network was found and settings is filled
|
||||
*/
|
||||
virtual bool getNetworkSettings(const uint64_t nwid,NetworkSettings &settings) const = 0;
|
||||
|
||||
/**
|
||||
* Set local settings for a network
|
||||
*
|
||||
* @param nwid Network ID
|
||||
* @param settings New network local settings
|
||||
* @return True if network was found and setting modified
|
||||
*/
|
||||
virtual bool setNetworkSettings(const uint64_t nwid,const NetworkSettings &settings) = 0;
|
||||
|
||||
/**
|
||||
* @return True if service is still running
|
||||
*/
|
||||
inline bool isRunning() const { return (this->reasonForTermination() == ONE_STILL_RUNNING); }
|
||||
|
||||
protected:
|
||||
OneService() {}
|
||||
|
||||
private:
|
||||
OneService(const OneService &one) {}
|
||||
inline OneService &operator=(const OneService &one) { return *this; }
|
||||
};
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
||||
#endif
|
122
service/README.md
Normal file
122
service/README.md
Normal file
|
@ -0,0 +1,122 @@
|
|||
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*.
|
||||
|
||||
### Network Virtualization Service API
|
||||
|
||||
The JSON API supports GET, POST/PUT, and DELETE. PUT is treated as a synonym for POST. Other methods including HEAD are not supported.
|
||||
|
||||
Values POSTed to the JSON API are *extremely* type sensitive. Things *must* be of the indicated type, otherwise they will be ignored or will generate an error. Anything quoted is a string so booleans and integers must lack quotes. Booleans must be *true* or *false* and nothing else. Integers cannot contain decimal points or they are floats (and vice versa). If something seems to be getting ignored or set to a strange value, or if you receive errors, check the type of all JSON fields you are submitting against the types listed below. Unrecognized fields in JSON objects are also ignored.
|
||||
|
||||
API requests must be authenticated via an authentication token. ZeroTier One saves this token in the *authtoken.secret* file in its working directory. This token may be supplied via the *auth* URL parameter (e.g. '?auth=...') or via the *X-ZT1-Auth* HTTP request header. Static UI pages are the only thing the server will allow without authentication.
|
||||
|
||||
A *jsonp* URL argument may be supplied to request JSONP encapsulation. A JSONP response is sent as a script with its JSON response payload wrapped in a call to the function name supplied as the argument to *jsonp*.
|
||||
|
||||
#### /status
|
||||
|
||||
* Purpose: Get running node status and addressing info
|
||||
* 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>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>
|
||||
|
||||
#### /network
|
||||
|
||||
* Purpose: Get all network memberships
|
||||
* Methods: GET
|
||||
* Returns: [ {object}, ... ]
|
||||
|
||||
Getting /network returns an array of all networks that this node has joined. See below for network object format.
|
||||
|
||||
#### /network/\<network ID\>
|
||||
|
||||
* Purpose: Get, join, or leave a network
|
||||
* Methods: GET, POST, DELETE
|
||||
* Returns: { object }
|
||||
|
||||
To join a network, POST to it. Since networks have no mandatory writable parameters, POST data is optional and may be omitted. Example: POST to /network/8056c2e21c000001 to join the public "Earth" network. To leave a network, DELETE it e.g. DELETE /network/8056c2e21c000001.
|
||||
|
||||
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>multicastSubscriptions</td><td>[string]</td><td>Multicast memberships as array of MAC/ADI tuples</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>portDeviceName</td><td>string</td><td>OS-specific network device name (if available)</td><td>no</td></tr>
|
||||
</table>
|
||||
|
||||
#### /peer
|
||||
|
||||
* Purpose: Get all peers
|
||||
* Methods: GET
|
||||
* Returns: [ {object}, ... ]
|
||||
|
||||
Getting /peer returns an array of peer objects for all current peers. See below for peer object format.
|
||||
|
||||
#### /peer/\<address\>
|
||||
|
||||
* Purpose: Get information about a peer
|
||||
* 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 hex ZeroTier address</td><td>no</td></tr>
|
||||
<tr><td>lastUnicastFrame</td><td>integer</td><td>Time of last unicast frame in ms since epoch</td><td>no</td></tr>
|
||||
<tr><td>lastMulticastFrame</td><td>integer</td><td>Time of last multicast frame in ms since epoch</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>
|
Loading…
Add table
Add a link
Reference in a new issue