Merge branch 'dev' into systemtray

This commit is contained in:
Grant Limberg 2016-11-16 16:23:56 -08:00
commit b4bacd50a1
36 changed files with 1781 additions and 1139 deletions

File diff suppressed because it is too large Load Diff

View File

@ -37,11 +37,15 @@
#include "../osdep/OSUtils.hpp"
#include "../osdep/Thread.hpp"
#include "../osdep/BlockingQueue.hpp"
#include "../ext/json/json.hpp"
#include "JSONDB.hpp"
// Number of background threads to start -- not actually started until needed
#define ZT_EMBEDDEDNETWORKCONTROLLER_BACKGROUND_THREAD_COUNT 2
namespace ZeroTier {
class Node;
@ -52,13 +56,14 @@ public:
EmbeddedNetworkController(Node *node,const char *dbPath);
virtual ~EmbeddedNetworkController();
virtual NetworkController::ResultCode doNetworkConfigRequest(
const InetAddress &fromAddr,
const Identity &signingId,
const Identity &identity,
virtual void init(const Identity &signingId,Sender *sender);
virtual void request(
uint64_t nwid,
const Dictionary<ZT_NETWORKCONFIG_METADATA_DICT_CAPACITY> &metaData,
NetworkConfig &nc);
const InetAddress &fromAddr,
uint64_t requestPacketId,
const Identity &identity,
const Dictionary<ZT_NETWORKCONFIG_METADATA_DICT_CAPACITY> &metaData);
unsigned int handleControlPlaneHttpGET(
const std::vector<std::string> &path,
@ -82,8 +87,31 @@ public:
std::string &responseBody,
std::string &responseContentType);
void threadMain()
throw();
private:
static void _circuitTestCallback(ZT_Node *node,ZT_CircuitTest *test,const ZT_CircuitTestReport *report);
void _request(
uint64_t nwid,
const InetAddress &fromAddr,
uint64_t requestPacketId,
const Identity &identity,
const Dictionary<ZT_NETWORKCONFIG_METADATA_DICT_CAPACITY> &metaData);
struct _RQEntry
{
uint64_t nwid;
uint64_t requestPacketId;
InetAddress fromAddr;
Identity identity;
Dictionary<ZT_NETWORKCONFIG_METADATA_DICT_CAPACITY> metaData;
};
BlockingQueue<_RQEntry *> _queue;
Thread _threads[ZT_EMBEDDEDNETWORKCONTROLLER_BACKGROUND_THREAD_COUNT];
bool _threadsStarted;
Mutex _threads_m;
// Gathers a bunch of statistics about members of a network, IP assignments, etc. that we need in various places
// This does lock _networkMemberCache_m
@ -96,9 +124,14 @@ private:
unsigned long activeMemberCount;
unsigned long totalMemberCount;
uint64_t mostRecentDeauthTime;
uint64_t nmiTimestamp; // time this NMI structure was computed
};
std::map<uint64_t,_NetworkMemberInfo> _nmiCache;
Mutex _nmiCache_m;
void _getNetworkMemberInfo(uint64_t now,uint64_t nwid,_NetworkMemberInfo &nmi);
void _pushMemberUpdate(uint64_t now,uint64_t nwid,const nlohmann::json &member);
// These init objects with default and static/informational fields
inline void _initMember(nlohmann::json &member)
{
@ -112,7 +145,8 @@ private:
if (!member.count("creationTime")) member["creationTime"] = OSUtils::now();
if (!member.count("noAutoAssignIps")) member["noAutoAssignIps"] = false;
if (!member.count("revision")) member["revision"] = 0ULL;
if (!member.count("enableBroadcast")) member["enableBroadcast"] = true;
if (!member.count("lastDeauthorizedTime")) member["lastDeauthorizedTime"] = 0ULL;
if (!member.count("lastAuthorizedTime")) member["lastAuthorizedTime"] = 0ULL;
member["objtype"] = "member";
}
inline void _initNetwork(nlohmann::json &network)
@ -121,6 +155,7 @@ private:
if (!network.count("creationTime")) network["creationTime"] = OSUtils::now();
if (!network.count("name")) network["name"] = "";
if (!network.count("multicastLimit")) network["multicastLimit"] = (uint64_t)32;
if (!network.count("enableBroadcast")) network["enableBroadcast"] = true;
if (!network.count("v4AssignMode")) network["v4AssignMode"] = {{"zt",false}};
if (!network.count("v6AssignMode")) network["v6AssignMode"] = {{"rfc4193",false},{"zt",false},{"6plane",false}};
if (!network.count("authTokens")) network["authTokens"] = nlohmann::json::array();
@ -154,6 +189,9 @@ private:
Node *const _node;
std::string _path;
NetworkController::Sender *_sender;
Identity _signingId;
struct _CircuitTestEntry
{
ZT_CircuitTest *test;

View File

@ -79,7 +79,7 @@ public:
{
for(std::map<std::string,_E>::iterator i(_db.lower_bound(prefix));i!=_db.end();) {
if ((i->first.length() >= prefix.length())&&(!memcmp(i->first.data(),prefix.data(),prefix.length()))) {
if (!func(i->first,get(i->second.obj,maxSinceCheck))) {
if (!func(i->first,get(i->first,maxSinceCheck))) {
std::map<std::string,_E>::iterator i2(i); ++i2;
this->erase(i->first);
i = i2;

View File

@ -1018,16 +1018,6 @@ typedef struct
*/
uint64_t address;
/**
* Time we last received a unicast frame from this peer
*/
uint64_t lastUnicastFrame;
/**
* Time we last received a multicast rame from this peer
*/
uint64_t lastMulticastFrame;
/**
* Remote major version or -1 if not known
*/

View File

@ -475,8 +475,6 @@ jobject newPeer(JNIEnv *env, const ZT_Peer &peer)
jclass peerClass = NULL;
jfieldID addressField = NULL;
jfieldID lastUnicastFrameField = NULL;
jfieldID lastMulticastFrameField = NULL;
jfieldID versionMajorField = NULL;
jfieldID versionMinorField = NULL;
jfieldID versionRevField = NULL;
@ -500,20 +498,6 @@ jobject newPeer(JNIEnv *env, const ZT_Peer &peer)
return NULL;
}
lastUnicastFrameField = lookup.findField(peerClass, "lastUnicastFrame", "J");
if(env->ExceptionCheck() || lastUnicastFrameField == NULL)
{
LOGE("Error finding lastUnicastFrame field of Peer object");
return NULL;
}
lastMulticastFrameField = lookup.findField(peerClass, "lastMulticastFrame", "J");
if(env->ExceptionCheck() || lastMulticastFrameField == NULL)
{
LOGE("Error finding lastMulticastFrame field of Peer object");
return NULL;
}
versionMajorField = lookup.findField(peerClass, "versionMajor", "I");
if(env->ExceptionCheck() || versionMajorField == NULL)
{
@ -571,8 +555,6 @@ jobject newPeer(JNIEnv *env, const ZT_Peer &peer)
}
env->SetLongField(peerObject, addressField, (jlong)peer.address);
env->SetLongField(peerObject, lastUnicastFrameField, (jlong)peer.lastUnicastFrame);
env->SetLongField(peerObject, lastMulticastFrameField, (jlong)peer.lastMulticastFrame);
env->SetIntField(peerObject, versionMajorField, peer.versionMajor);
env->SetIntField(peerObject, versionMinorField, peer.versionMinor);
env->SetIntField(peerObject, versionRevField, peer.versionRev);

View File

@ -34,8 +34,6 @@ import java.util.ArrayList;
*/
public final class Peer {
private long address;
private long lastUnicastFrame;
private long lastMulticastFrame;
private int versionMajor;
private int versionMinor;
private int versionRev;
@ -52,20 +50,6 @@ public final class Peer {
return address;
}
/**
* Time we last received a unicast frame from this peer
*/
public final long lastUnicastFrame() {
return lastUnicastFrame;
}
/**
* Time we last received a multicast rame from this peer
*/
public final long lastMulticastFrame() {
return lastMulticastFrame;
}
/**
* Remote major version or -1 if not known
*/

View File

@ -59,4 +59,10 @@ enum NetworkType {
- (NSString*)statusString;
- (NSString*)typeString;
- (BOOL)hasSameNetworkId:(UInt64)networkId;
- (BOOL)isEqualToNetwork:(Network*)network;
- (BOOL)isEqual:(id)object;
- (NSUInteger)hash;
@end

View File

@ -275,4 +275,63 @@ NSString *NetworkAllowDefaultKey = @"allowDefault";
}
}
- (BOOL)hasSameNetworkId:(UInt64)networkId
{
return self.nwid == networkId;
}
- (BOOL)isEqualToNetwork:(Network*)network
{
return [self.assignedAddresses isEqualToArray:network.assignedAddresses] &&
self.bridge == network.bridge &&
self.broadcastEnabled == network.broadcastEnabled &&
self.dhcp == network.dhcp &&
[self.mac isEqualToString:network.mac] &&
self.mtu == network.mtu &&
self.netconfRevision == network.netconfRevision &&
[self.name isEqualToString:network.name] &&
self.nwid == network.nwid &&
[self.portDeviceName isEqualToString:network.portDeviceName] &&
self.status == network.status &&
self.type == network.type &&
self.allowManaged == network.allowManaged &&
self.allowGlobal == network.allowGlobal &&
self.allowDefault == network.allowDefault &&
self.connected == network.connected;
}
- (BOOL)isEqual:(id)object
{
if (self == object) {
return YES;
}
if (![object isKindOfClass:[Network class]]) {
return NO;
}
return [self isEqualToNetwork:object];
}
- (NSUInteger)hash
{
return [self.assignedAddresses hash] ^
self.bridge ^
self.broadcastEnabled ^
self.dhcp ^
[self.mac hash] ^
self.mtu ^
self.netconfRevision ^
[self.name hash] ^
self.nwid ^
[self.portDeviceName hash] ^
self.portError ^
self.status ^
self.type ^
self.allowManaged ^
self.allowGlobal ^
self.allowDefault ^
self.connected;
}
@end

View File

@ -23,7 +23,7 @@
@interface ShowNetworksViewController : NSViewController<NSTableViewDelegate, NSTableViewDataSource>
@property (nonatomic) NSArray<Network*> *networkList;
@property (nonatomic) NSMutableArray<Network*> *networkList;
@property (nonatomic) NetworkMonitor *netMonitor;
@property (nonatomic) BOOL visible;

View File

@ -21,6 +21,17 @@
#import "NetworkInfoCell.h"
#import "Network.h"
BOOL hasNetworkWithID(NSArray<Network*> *list, UInt64 nwid)
{
for(Network *n in list) {
if(n.nwid == nwid) {
return YES;
}
}
return NO;
}
@interface ShowNetworksViewController ()
@end
@ -30,6 +41,8 @@
- (void)viewDidLoad {
[super viewDidLoad];
self.networkList = [NSMutableArray array];
[self.tableView setDelegate:self];
[self.tableView setDataSource:self];
[self.tableView setBackgroundColor:[NSColor clearColor]];
@ -50,9 +63,30 @@
}
- (void)setNetworks:(NSArray<Network *> *)list {
_networkList = list;
if(_visible) {
[_tableView reloadData];
for (Network *n in list) {
if ([_networkList containsObject:n]) {
// don't need to do anything here. Already an identical object in the list
continue;
}
else {
// network not in the list based on equality. Did an object change? or is it a new item?
if (hasNetworkWithID(_networkList, n.nwid)) {
for (int i = 0; i < [_networkList count]; ++i) {
Network *n2 = [_networkList objectAtIndex:i];
if (n.nwid == n2.nwid)
{
[_networkList replaceObjectAtIndex:i withObject:n];
[_tableView reloadDataForRowIndexes:[NSIndexSet indexSetWithIndex:i]
columnIndexes:[NSIndexSet indexSetWithIndex:0]];
}
}
}
else {
[_networkList addObject:n];
[_tableView reloadData];
}
}
}
}

View File

@ -1,8 +1,9 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="10116" systemVersion="15G31" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="11542" systemVersion="16B2555" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<dependencies>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="10116"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="11542"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="ShowNetworksViewController" customModule="ZeroTier_One" customModuleProvider="target">
@ -24,7 +25,7 @@
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<tableView focusRingType="none" verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="lastColumnOnly" selectionHighlightStyle="none" columnReordering="NO" multipleSelection="NO" autosaveColumns="NO" rowHeight="386" rowSizeStyle="automatic" viewBased="YES" id="w5O-vn-cYB">
<rect key="frame" x="0.0" y="0.0" width="530" height="0.0"/>
<rect key="frame" x="0.0" y="0.0" width="530" height="481"/>
<autoresizingMask key="autoresizingMask"/>
<size key="intercellSpacing" width="3" height="2"/>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="0.59999999999999998" colorSpace="calibratedRGB"/>
@ -50,7 +51,7 @@
<subviews>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="EUT-1A-lgY">
<rect key="frame" x="480" y="364" width="44" height="19"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Label" id="bf8-gi-tpp">
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" sendsActionOnEndEditing="YES" title="Label" id="bf8-gi-tpp">
<font key="font" size="13" name="AndaleMono"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
@ -242,7 +243,7 @@
</textField>
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="GEJ-6D-gWU">
<rect key="frame" x="102" y="86" width="424" height="19"/>
<textFieldCell key="cell" sendsActionOnEndEditing="YES" alignment="right" title="Multiline Label" id="A3M-ZZ-6h7">
<textFieldCell key="cell" selectable="YES" sendsActionOnEndEditing="YES" alignment="right" title="Multiline Label" id="A3M-ZZ-6h7">
<font key="font" size="13" name="AndaleMono"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
@ -250,7 +251,7 @@
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="lO9-Jg-9f8">
<rect key="frame" x="1" y="364" width="44" height="19"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Label" id="p7O-rs-RvR">
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" sendsActionOnEndEditing="YES" title="Label" id="p7O-rs-RvR">
<font key="font" size="13" name="AndaleMono"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>

View File

@ -84,6 +84,10 @@ ifeq ($(ZT_TRACE),1)
DEFS+=-DZT_TRACE
endif
ifeq ($(ZT_RULES_ENGINE_DEBUGGING),1)
DEFS+=-DZT_RULES_ENGINE_DEBUGGING
endif
ifeq ($(ZT_DEBUG),1)
DEFS+=-DZT_TRACE
override CFLAGS+=-Wall -g -O -pthread $(INCLUDES) $(DEFS)

View File

@ -256,12 +256,12 @@
/**
* How frequently to send heartbeats over in-use paths
*/
#define ZT_PATH_HEARTBEAT_PERIOD 10000
#define ZT_PATH_HEARTBEAT_PERIOD 14000
/**
* Paths are considered inactive if they have not received traffic in this long
*/
#define ZT_PATH_ALIVE_TIMEOUT 25000
#define ZT_PATH_ALIVE_TIMEOUT 45000
/**
* Minimum time between attempts to check dead paths to see if they can be re-awakened

View File

@ -865,92 +865,12 @@ bool IncomingPacket::_doNETWORK_CONFIG_REQUEST(const RuntimeEnvironment *RR,cons
const uint64_t nwid = at<uint64_t>(ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST_IDX_NETWORK_ID);
const unsigned int hopCount = hops();
const uint64_t requestPacketId = packetId();
bool trustEstablished = false;
if (RR->localNetworkController) {
const unsigned int metaDataLength = at<uint16_t>(ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST_IDX_DICT_LEN);
const char *metaDataBytes = (const char *)field(ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST_IDX_DICT,metaDataLength);
const Dictionary<ZT_NETWORKCONFIG_METADATA_DICT_CAPACITY> metaData(metaDataBytes,metaDataLength);
NetworkConfig *netconf = new NetworkConfig();
try {
switch(RR->localNetworkController->doNetworkConfigRequest((hopCount > 0) ? InetAddress() : _path->address(),RR->identity,peer->identity(),nwid,metaData,*netconf)) {
case NetworkController::NETCONF_QUERY_OK: {
trustEstablished = true;
Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> *dconf = new Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY>();
try {
if (netconf->toDictionary(*dconf,metaData.getUI(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_VERSION,0) < 6)) {
uint64_t configUpdateId = RR->node->prng();
if (!configUpdateId) ++configUpdateId;
const unsigned int totalSize = dconf->sizeBytes();
unsigned int chunkIndex = 0;
while (chunkIndex < totalSize) {
const unsigned int chunkLen = std::min(totalSize - chunkIndex,(unsigned int)(ZT_UDP_DEFAULT_PAYLOAD_MTU - (ZT_PACKET_IDX_PAYLOAD + 256)));
Packet outp(peer->address(),RR->identity.address(),Packet::VERB_OK);
outp.append((unsigned char)Packet::VERB_NETWORK_CONFIG_REQUEST);
outp.append(requestPacketId);
const unsigned int sigStart = outp.size();
outp.append(nwid);
outp.append((uint16_t)chunkLen);
outp.append((const void *)(dconf->data() + chunkIndex),chunkLen);
outp.append((uint8_t)0); // no flags
outp.append((uint64_t)configUpdateId);
outp.append((uint32_t)totalSize);
outp.append((uint32_t)chunkIndex);
C25519::Signature sig(RR->identity.sign(reinterpret_cast<const uint8_t *>(outp.data()) + sigStart,outp.size() - sigStart));
outp.append((uint8_t)1);
outp.append((uint16_t)ZT_C25519_SIGNATURE_LEN);
outp.append(sig.data,ZT_C25519_SIGNATURE_LEN);
outp.compress();
RR->sw->send(outp,true);
chunkIndex += chunkLen;
}
}
delete dconf;
} catch ( ... ) {
delete dconf;
throw;
}
} break;
case NetworkController::NETCONF_QUERY_OBJECT_NOT_FOUND: {
Packet outp(peer->address(),RR->identity.address(),Packet::VERB_ERROR);
outp.append((unsigned char)Packet::VERB_NETWORK_CONFIG_REQUEST);
outp.append(requestPacketId);
outp.append((unsigned char)Packet::ERROR_OBJ_NOT_FOUND);
outp.append(nwid);
outp.armor(peer->key(),true);
_path->send(RR,outp.data(),outp.size(),RR->node->now());
} break;
case NetworkController::NETCONF_QUERY_ACCESS_DENIED: {
Packet outp(peer->address(),RR->identity.address(),Packet::VERB_ERROR);
outp.append((unsigned char)Packet::VERB_NETWORK_CONFIG_REQUEST);
outp.append(requestPacketId);
outp.append((unsigned char)Packet::ERROR_NETWORK_ACCESS_DENIED_);
outp.append(nwid);
outp.armor(peer->key(),true);
_path->send(RR,outp.data(),outp.size(),RR->node->now());
} break;
case NetworkController::NETCONF_QUERY_INTERNAL_SERVER_ERROR:
break;
case NetworkController::NETCONF_QUERY_IGNORE:
break;
default:
TRACE("NETWORK_CONFIG_REQUEST failed: invalid return value from NetworkController::doNetworkConfigRequest()");
break;
}
delete netconf;
} catch ( ... ) {
delete netconf;
throw;
}
RR->localNetworkController->request(nwid,(hopCount > 0) ? InetAddress() : _path->address(),requestPacketId,peer->identity(),metaData);
} else {
Packet outp(peer->address(),RR->identity.address(),Packet::VERB_ERROR);
outp.append((unsigned char)Packet::VERB_NETWORK_CONFIG_REQUEST);
@ -961,7 +881,7 @@ bool IncomingPacket::_doNETWORK_CONFIG_REQUEST(const RuntimeEnvironment *RR,cons
_path->send(RR,outp.data(),outp.size(),RR->node->now());
}
peer->received(_path,hopCount,requestPacketId,Packet::VERB_NETWORK_CONFIG_REQUEST,0,Packet::VERB_NOP,trustEstablished);
peer->received(_path,hopCount,requestPacketId,Packet::VERB_NETWORK_CONFIG_REQUEST,0,Packet::VERB_NOP,false);
} catch (std::exception &exc) {
fprintf(stderr,"WARNING: network config request failed with exception: %s" ZT_EOL_S,exc.what());
TRACE("dropped NETWORK_CONFIG_REQUEST from %s(%s): %s",source().toString().c_str(),_path->address().toString().c_str(),exc.what());

View File

@ -599,7 +599,7 @@ Network::Network(const RuntimeEnvironment *renv,uint64_t nwid,void *uptr) :
if (conf.length()) {
dconf->load(conf.c_str());
if (nconf->fromDictionary(*dconf)) {
this->_setConfiguration(*nconf,false);
this->setConfiguration(*nconf,false);
_lastConfigUpdate = 0; // we still want to re-request a new config from the network
gotConf = true;
}
@ -1015,7 +1015,7 @@ uint64_t Network::handleConfigChunk(const Packet &chunk,unsigned int ptr)
}
if (nc) {
this->_setConfiguration(*nc,true);
this->setConfiguration(*nc,true);
delete nc;
return configUpdateId;
} else {
@ -1025,6 +1025,46 @@ uint64_t Network::handleConfigChunk(const Packet &chunk,unsigned int ptr)
return 0;
}
int Network::setConfiguration(const NetworkConfig &nconf,bool saveToDisk)
{
// _lock is NOT locked when this is called
try {
if ((nconf.issuedTo != RR->identity.address())||(nconf.networkId != _id))
return 0;
if (_config == nconf)
return 1; // OK config, but duplicate of what we already have
ZT_VirtualNetworkConfig ctmp;
bool oldPortInitialized;
{
Mutex::Lock _l(_lock);
_config = nconf;
_lastConfigUpdate = RR->node->now();
_netconfFailure = NETCONF_FAILURE_NONE;
oldPortInitialized = _portInitialized;
_portInitialized = true;
_externalConfig(&ctmp);
}
_portError = RR->node->configureVirtualNetworkPort(_id,&_uPtr,(oldPortInitialized) ? ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_CONFIG_UPDATE : ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_UP,&ctmp);
if (saveToDisk) {
Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> *d = new Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY>();
try {
char n[64];
Utils::snprintf(n,sizeof(n),"networks.d/%.16llx.conf",_id);
if (nconf.toDictionary(*d,false))
RR->node->dataStorePut(n,(const void *)d->data(),d->sizeBytes(),true);
} catch ( ... ) {}
delete d;
}
return 2; // OK and configuration has changed
} catch ( ... ) {
TRACE("ignored invalid configuration for network %.16llx",(unsigned long long)_id);
}
return 0;
}
void Network::requestConfiguration()
{
const Address ctrl(controller());
@ -1046,26 +1086,7 @@ void Network::requestConfiguration()
if (ctrl == RR->identity.address()) {
if (RR->localNetworkController) {
NetworkConfig *nconf = new NetworkConfig();
try {
switch(RR->localNetworkController->doNetworkConfigRequest(InetAddress(),RR->identity,RR->identity,_id,rmd,*nconf)) {
case NetworkController::NETCONF_QUERY_OK:
this->_setConfiguration(*nconf,true);
break;
case NetworkController::NETCONF_QUERY_OBJECT_NOT_FOUND:
this->setNotFound();
break;
case NetworkController::NETCONF_QUERY_ACCESS_DENIED:
this->setAccessDenied();
break;
default:
this->setNotFound();
break;
}
} catch ( ... ) {
this->setNotFound();
}
delete nconf;
RR->localNetworkController->request(_id,InetAddress(),0xffffffffffffffffULL,RR->identity,rmd);
} else {
this->setNotFound();
}
@ -1257,46 +1278,6 @@ ZT_VirtualNetworkStatus Network::_status() const
}
}
int Network::_setConfiguration(const NetworkConfig &nconf,bool saveToDisk)
{
// _lock is NOT locked when this is called
try {
if ((nconf.issuedTo != RR->identity.address())||(nconf.networkId != _id))
return 0;
if (_config == nconf)
return 1; // OK config, but duplicate of what we already have
ZT_VirtualNetworkConfig ctmp;
bool oldPortInitialized;
{
Mutex::Lock _l(_lock);
_config = nconf;
_lastConfigUpdate = RR->node->now();
_netconfFailure = NETCONF_FAILURE_NONE;
oldPortInitialized = _portInitialized;
_portInitialized = true;
_externalConfig(&ctmp);
}
_portError = RR->node->configureVirtualNetworkPort(_id,&_uPtr,(oldPortInitialized) ? ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_CONFIG_UPDATE : ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_UP,&ctmp);
if (saveToDisk) {
Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> *d = new Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY>();
try {
char n[64];
Utils::snprintf(n,sizeof(n),"networks.d/%.16llx.conf",_id);
if (nconf.toDictionary(*d,false))
RR->node->dataStorePut(n,(const void *)d->data(),d->sizeBytes(),true);
} catch ( ... ) {}
delete d;
}
return 2; // OK and configuration has changed
} catch ( ... ) {
TRACE("ignored invalid configuration for network %.16llx",(unsigned long long)_id);
}
return 0;
}
void Network::_externalConfig(ZT_VirtualNetworkConfig *ec) const
{
// assumes _lock is locked

View File

@ -187,6 +187,15 @@ public:
*/
uint64_t handleConfigChunk(const Packet &chunk,unsigned int ptr);
/**
* Set network configuration
*
* @param nconf Network configuration
* @param saveToDisk Save to disk? Used during loading, should usually be true otherwise.
* @return 0 == bad, 1 == accepted but duplicate/unchanged, 2 == accepted and new
*/
int setConfiguration(const NetworkConfig &nconf,bool saveToDisk);
/**
* Set netconf failure to 'access denied' -- called in IncomingPacket when controller reports this
*/
@ -328,7 +337,6 @@ public:
inline void **userPtr() throw() { return &_uPtr; }
private:
int _setConfiguration(const NetworkConfig &nconf,bool saveToDisk);
ZT_VirtualNetworkStatus _status() const;
void _externalConfig(ZT_VirtualNetworkConfig *ec) const; // assumes _lock is locked
bool _gate(const SharedPtr<Peer> &peer);

View File

@ -27,7 +27,6 @@
namespace ZeroTier {
class RuntimeEnvironment;
class Identity;
class Address;
struct InetAddress;
@ -38,45 +37,69 @@ struct InetAddress;
class NetworkController
{
public:
/**
* Return value of doNetworkConfigRequest
*/
enum ResultCode
enum ErrorCode
{
NETCONF_QUERY_OK = 0,
NETCONF_QUERY_OBJECT_NOT_FOUND = 1,
NETCONF_QUERY_ACCESS_DENIED = 2,
NETCONF_QUERY_INTERNAL_SERVER_ERROR = 3,
NETCONF_QUERY_IGNORE = 4
NC_ERROR_NONE = 0,
NC_ERROR_OBJECT_NOT_FOUND = 1,
NC_ERROR_ACCESS_DENIED = 2,
NC_ERROR_INTERNAL_SERVER_ERROR = 3
};
/**
* Interface for sender used to send pushes and replies
*/
class Sender
{
public:
/**
* Send a configuration to a remote peer
*
* @param nwid Network ID
* @param requestPacketId Request packet ID to send OK(NETWORK_CONFIG_REQUEST) or 0 to send NETWORK_CONFIG (push)
* @param destination Destination peer Address
* @param nc Network configuration to send
* @param sendLegacyFormatConfig If true, send an old-format network config
*/
virtual void ncSendConfig(uint64_t nwid,uint64_t requestPacketId,const Address &destination,const NetworkConfig &nc,bool sendLegacyFormatConfig) = 0;
/**
* Send a network configuration request error
*
* @param nwid Network ID
* @param requestPacketId Request packet ID or 0 if none
* @param destination Destination peer Address
* @param errorCode Error code
*/
virtual void ncSendError(uint64_t nwid,uint64_t requestPacketId,const Address &destination,NetworkController::ErrorCode errorCode) = 0;
};
NetworkController() {}
virtual ~NetworkController() {}
/**
* Handle a network config request, sending replies if necessary
* Called when this is added to a Node to initialize and supply info
*
* This call is permitted to block, and may be called concurrently from more
* than one thread. Implementations must use locks if needed.
* @param signingId Identity for signing of network configurations, certs, etc.
* @param sender Sender implementation for sending replies or config pushes
*/
virtual void init(const Identity &signingId,Sender *sender) = 0;
/**
* Handle a network configuration request
*
* On internal server errors, the 'error' field in result can be filled in
* to indicate the error.
*
* @param fromAddr Originating wire address or null address if packet is not direct (or from self)
* @param signingId Identity that should be used to sign results -- must include private key
* @param identity Originating peer ZeroTier identity
* @param nwid 64-bit network ID
* @param fromAddr Originating wire address or null address if packet is not direct (or from self)
* @param requestPacketId Packet ID of request packet or 0 if not initiated by remote request
* @param identity ZeroTier identity of originating peer
* @param metaData Meta-data bundled with request (if any)
* @param nc NetworkConfig to fill with results
* @return Returns NETCONF_QUERY_OK if result 'nc' is valid, or an error code on error
*/
virtual NetworkController::ResultCode doNetworkConfigRequest(
const InetAddress &fromAddr,
const Identity &signingId,
const Identity &identity,
virtual void request(
uint64_t nwid,
const Dictionary<ZT_NETWORKCONFIG_METADATA_DICT_CAPACITY> &metaData,
NetworkConfig &nc) = 0;
const InetAddress &fromAddr,
uint64_t requestPacketId,
const Identity &identity,
const Dictionary<ZT_NETWORKCONFIG_METADATA_DICT_CAPACITY> &metaData) = 0;
};
} // namespace ZeroTier

View File

@ -405,8 +405,6 @@ ZT_PeerList *Node::peers() const
for(std::vector< std::pair< Address,SharedPtr<Peer> > >::iterator pi(peers.begin());pi!=peers.end();++pi) {
ZT_Peer *p = &(pl->peers[pl->peerCount++]);
p->address = pi->second->address().toInt();
p->lastUnicastFrame = pi->second->lastUnicastFrame();
p->lastMulticastFrame = pi->second->lastMulticastFrame();
if (pi->second->remoteVersionKnown()) {
p->versionMajor = pi->second->remoteVersionMajor();
p->versionMinor = pi->second->remoteVersionMinor();
@ -492,6 +490,7 @@ void Node::clearLocalInterfaceAddresses()
void Node::setNetconfMaster(void *networkControllerInstance)
{
RR->localNetworkController = reinterpret_cast<NetworkController *>(networkControllerInstance);
RR->localNetworkController->init(RR->identity,this);
}
ZT_ResultCode Node::circuitTestBegin(ZT_CircuitTest *test,void (*reportCallback)(ZT_Node *,ZT_CircuitTest *,const ZT_CircuitTestReport *))
@ -720,6 +719,92 @@ void Node::setTrustedPaths(const struct sockaddr_storage *networks,const uint64_
RR->topology->setTrustedPaths(reinterpret_cast<const InetAddress *>(networks),ids,count);
}
void Node::ncSendConfig(uint64_t nwid,uint64_t requestPacketId,const Address &destination,const NetworkConfig &nc,bool sendLegacyFormatConfig)
{
if (destination == RR->identity.address()) {
SharedPtr<Network> n(network(nwid));
if (!n) return;
n->setConfiguration(nc,true);
} else {
Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> *dconf = new Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY>();
try {
if (nc.toDictionary(*dconf,sendLegacyFormatConfig)) {
uint64_t configUpdateId = prng();
if (!configUpdateId) ++configUpdateId;
const unsigned int totalSize = dconf->sizeBytes();
unsigned int chunkIndex = 0;
while (chunkIndex < totalSize) {
const unsigned int chunkLen = std::min(totalSize - chunkIndex,(unsigned int)(ZT_UDP_DEFAULT_PAYLOAD_MTU - (ZT_PACKET_IDX_PAYLOAD + 256)));
Packet outp(destination,RR->identity.address(),(requestPacketId) ? Packet::VERB_OK : Packet::VERB_NETWORK_CONFIG);
if (requestPacketId) {
outp.append((unsigned char)Packet::VERB_NETWORK_CONFIG_REQUEST);
outp.append(requestPacketId);
}
const unsigned int sigStart = outp.size();
outp.append(nwid);
outp.append((uint16_t)chunkLen);
outp.append((const void *)(dconf->data() + chunkIndex),chunkLen);
outp.append((uint8_t)0); // no flags
outp.append((uint64_t)configUpdateId);
outp.append((uint32_t)totalSize);
outp.append((uint32_t)chunkIndex);
C25519::Signature sig(RR->identity.sign(reinterpret_cast<const uint8_t *>(outp.data()) + sigStart,outp.size() - sigStart));
outp.append((uint8_t)1);
outp.append((uint16_t)ZT_C25519_SIGNATURE_LEN);
outp.append(sig.data,ZT_C25519_SIGNATURE_LEN);
outp.compress();
RR->sw->send(outp,true);
chunkIndex += chunkLen;
}
}
delete dconf;
} catch ( ... ) {
delete dconf;
throw;
}
}
}
void Node::ncSendError(uint64_t nwid,uint64_t requestPacketId,const Address &destination,NetworkController::ErrorCode errorCode)
{
if (destination == RR->identity.address()) {
SharedPtr<Network> n(network(nwid));
if (!n) return;
switch(errorCode) {
case NetworkController::NC_ERROR_OBJECT_NOT_FOUND:
case NetworkController::NC_ERROR_INTERNAL_SERVER_ERROR:
n->setNotFound();
break;
case NetworkController::NC_ERROR_ACCESS_DENIED:
n->setAccessDenied();
break;
default: break;
}
} else if (requestPacketId) {
Packet outp(destination,RR->identity.address(),Packet::VERB_ERROR);
outp.append((unsigned char)Packet::VERB_NETWORK_CONFIG_REQUEST);
outp.append(requestPacketId);
switch(errorCode) {
//case NetworkController::NC_ERROR_OBJECT_NOT_FOUND:
//case NetworkController::NC_ERROR_INTERNAL_SERVER_ERROR:
default:
outp.append((unsigned char)Packet::ERROR_OBJ_NOT_FOUND);
break;
case NetworkController::NC_ERROR_ACCESS_DENIED:
outp.append((unsigned char)Packet::ERROR_NETWORK_ACCESS_DENIED_);
break;
}
outp.append(nwid);
RR->sw->send(outp,true);
} // else we can't send an ERROR() in response to nothing, so discard
}
} // namespace ZeroTier
/****************************************************************************/

View File

@ -36,6 +36,7 @@
#include "Network.hpp"
#include "Path.hpp"
#include "Salsa20.hpp"
#include "NetworkController.hpp"
#undef TRACE
#ifdef ZT_TRACE
@ -55,7 +56,7 @@ namespace ZeroTier {
*
* The pointer returned by ZT_Node_new() is an instance of this class.
*/
class Node
class Node : public NetworkController::Sender
{
public:
Node(
@ -69,7 +70,7 @@ public:
ZT_PathCheckFunction pathCheckFunction,
ZT_EventCallback eventCallback);
~Node();
virtual ~Node();
// Public API Functions ----------------------------------------------------
@ -282,6 +283,9 @@ public:
return false;
}
virtual void ncSendConfig(uint64_t nwid,uint64_t requestPacketId,const Address &destination,const NetworkConfig &nc,bool sendLegacyFormatConfig);
virtual void ncSendError(uint64_t nwid,uint64_t requestPacketId,const Address &destination,NetworkController::ErrorCode errorCode);
private:
inline SharedPtr<Network> _network(uint64_t nwid) const
{

View File

@ -42,8 +42,7 @@ static uint32_t _natKeepaliveBuf = 0;
Peer::Peer(const RuntimeEnvironment *renv,const Identity &myIdentity,const Identity &peerIdentity) :
_lastReceive(0),
_lastUnicastFrame(0),
_lastMulticastFrame(0),
_lastNontrivialReceive(0),
_lastDirectPathPushSent(0),
_lastDirectPathPushReceive(0),
_lastCredentialRequestSent(0),
@ -128,10 +127,16 @@ void Peer::received(
#endif
_lastReceive = now;
if ((verb == Packet::VERB_FRAME)||(verb == Packet::VERB_EXT_FRAME))
_lastUnicastFrame = now;
else if (verb == Packet::VERB_MULTICAST_FRAME)
_lastMulticastFrame = now;
switch (verb) {
case Packet::VERB_FRAME:
case Packet::VERB_EXT_FRAME:
case Packet::VERB_NETWORK_CONFIG_REQUEST:
case Packet::VERB_NETWORK_CONFIG:
case Packet::VERB_MULTICAST_FRAME:
_lastNontrivialReceive = now;
break;
default: break;
}
if (trustEstablished) {
_lastTrustEstablishedPacketReceived = now;

View File

@ -226,25 +226,10 @@ public:
*/
inline bool isAlive(const uint64_t now) const { return ((now - _lastReceive) < ZT_PEER_ACTIVITY_TIMEOUT); }
/**
* @return Time of most recent unicast frame received
*/
inline uint64_t lastUnicastFrame() const { return _lastUnicastFrame; }
/**
* @return Time of most recent multicast frame received
*/
inline uint64_t lastMulticastFrame() const { return _lastMulticastFrame; }
/**
* @return Time of most recent frame of any kind (unicast or multicast)
*/
inline uint64_t lastFrame() const { return std::max(_lastUnicastFrame,_lastMulticastFrame); }
/**
* @return True if this peer has sent us real network traffic recently
*/
inline uint64_t isActive(uint64_t now) const { return ((now - lastFrame()) < ZT_PEER_ACTIVITY_TIMEOUT); }
inline uint64_t isActive(uint64_t now) const { return ((now - _lastNontrivialReceive) < ZT_PEER_ACTIVITY_TIMEOUT); }
/**
* @return Latency in milliseconds or 0 if unknown
@ -469,8 +454,7 @@ private:
uint8_t _key[ZT_PEER_SECRET_KEY_LENGTH];
uint8_t _remoteClusterOptimal6[16];
uint64_t _lastReceive; // direct or indirect
uint64_t _lastUnicastFrame;
uint64_t _lastMulticastFrame;
uint64_t _lastNontrivialReceive; // frames, things like netconf, etc.
uint64_t _lastDirectPathPushSent;
uint64_t _lastDirectPathPushReceive;
uint64_t _lastCredentialRequestSent;

11
one.cpp
View File

@ -973,6 +973,7 @@ int main(int argc,char **argv)
std::string homeDir;
unsigned int port = ZT_DEFAULT_PORT;
bool skipRootCheck = false;
const char *allowManagementFrom = (const char *)0;
for(int i=1;i<argc;++i) {
if (argv[i][0] == '-') {
@ -986,6 +987,14 @@ int main(int argc,char **argv)
}
break;
case 'M': // allow management from this IP/bits network
allowManagementFrom = argv[i] + 2;
if (!strlen(allowManagementFrom)) {
printHelp(argv[0],stdout);
return 1;
}
break;
#ifdef __UNIX_LIKE__
case 'd': // Run in background as daemon
runAsDaemon = true;
@ -1167,7 +1176,7 @@ int main(int argc,char **argv)
unsigned int returnValue = 0;
for(;;) {
zt1Service = OneService::newInstance(homeDir.c_str(),port);
zt1Service = OneService::newInstance(homeDir.c_str(),port,allowManagementFrom);
switch(zt1Service->run()) {
case OneService::ONE_STILL_RUNNING: // shouldn't happen, run() won't return until done
case OneService::ONE_NORMAL_TERMINATION:

64
osdep/BlockingQueue.hpp Normal file
View File

@ -0,0 +1,64 @@
/*
* 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_BLOCKINGQUEUE_HPP
#define ZT_BLOCKINGQUEUE_HPP
#include <queue>
#include <mutex>
#include <condition_variable>
namespace ZeroTier {
/**
* Simple C++11 thread-safe queue
*
* Do not use in node/ since we have not gone C++11 there yet.
*/
template <class T>
class BlockingQueue
{
public:
BlockingQueue(void) {}
inline void post(T t)
{
std::lock_guard<std::mutex> lock(m);
q.push(t);
c.notify_one();
}
inline T get(void)
{
std::unique_lock<std::mutex> lock(m);
while(q.empty())
c.wait(lock);
T val = q.front();
q.pop();
return val;
}
private:
std::queue<T> q;
mutable std::mutex m;
std::condition_variable c;
};
} // namespace ZeroTier
#endif

View File

@ -102,6 +102,8 @@ void dropPrivileges(std::string homeDir) {
return;
}
createOwnedHomedir(homeDir, targetUser);
if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_IS_SET, CAP_NET_RAW, 0, 0) < 0) {
// Kernel has no support for ambient capabilities.
notDropping(homeDir);
@ -113,8 +115,6 @@ void dropPrivileges(std::string homeDir) {
return;
}
createOwnedHomedir(homeDir, targetUser);
if (setCapabilities((1 << CAP_NET_ADMIN) | (1 << CAP_NET_RAW) | (1 << CAP_SETUID) | (1 << CAP_SETGID)) < 0) {
fprintf(stderr, "ERROR: failed to set capabilities (not running as real root?)\n");
exit(1);

View File

@ -222,8 +222,6 @@ static void _jsonAppend(unsigned int depth,std::string &buf,const ZT_Peer *peer)
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"
@ -234,8 +232,6 @@ static void _jsonAppend(unsigned int depth,std::string &buf,const ZT_Peer *peer)
"%s}",
prefix,
prefix,peer->address,
prefix,peer->lastUnicastFrame,
prefix,peer->lastMulticastFrame,
prefix,peer->versionMajor,
prefix,peer->versionMinor,
prefix,peer->versionRev,
@ -274,9 +270,6 @@ unsigned int ControlPlane::handleRequest(
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

View File

@ -483,6 +483,7 @@ public:
const std::string _homePath;
BackgroundResolver _tcpFallbackResolver;
InetAddress _allowManagementFrom;
EmbeddedNetworkController *_controller;
Phy<OneServiceImpl *> _phy;
Node *_node;
@ -570,7 +571,7 @@ public:
// end member variables ----------------------------------------------------
OneServiceImpl(const char *hp,unsigned int port) :
OneServiceImpl(const char *hp,unsigned int port,const char *allowManagementFrom) :
_homePath((hp) ? hp : ".")
,_tcpFallbackResolver(ZT_TCP_FALLBACK_RELAY)
,_controller((EmbeddedNetworkController *)0)
@ -595,6 +596,9 @@ public:
#endif
,_run(true)
{
if (allowManagementFrom)
_allowManagementFrom.fromString(allowManagementFrom);
_ports[0] = 0;
_ports[1] = 0;
_ports[2] = 0;
@ -614,7 +618,7 @@ public:
struct sockaddr_in in4;
memset(&in4,0,sizeof(in4));
in4.sin_family = AF_INET;
in4.sin_addr.s_addr = Utils::hton((uint32_t)0x7f000001); // right now we just listen for TCP @127.0.0.1
in4.sin_addr.s_addr = Utils::hton((uint32_t)((allowManagementFrom) ? 0 : 0x7f000001)); // right now we just listen for TCP @127.0.0.1
in4.sin_port = Utils::hton((uint16_t)port);
_v4TcpControlSocket = _phy.tcpListen((const struct sockaddr *)&in4,this);
@ -622,7 +626,8 @@ public:
memset((void *)&in6,0,sizeof(in6));
in6.sin6_family = AF_INET6;
in6.sin6_port = in4.sin_port;
in6.sin6_addr.s6_addr[15] = 1; // IPv6 localhost == ::1
if (!allowManagementFrom)
in6.sin6_addr.s6_addr[15] = 1; // IPv6 localhost == ::1
_v6TcpControlSocket = _phy.tcpListen((const struct sockaddr *)&in6,this);
// We must bind one of IPv4 or IPv6 -- support either failing to support hosts that
@ -1259,12 +1264,10 @@ public:
inline void phyOnTcpAccept(PhySocket *sockL,PhySocket *sockN,void **uptrL,void **uptrN,const struct sockaddr *from)
{
if ((!from)||(reinterpret_cast<const InetAddress *>(from)->ipScope() != InetAddress::IP_SCOPE_LOOPBACK)) {
// Non-Loopback: deny (for now)
if (!from) {
_phy.close(sockN,false);
return;
} else {
// Loopback == HTTP JSON API request
TcpConnection *tc = new TcpConnection();
_tcpConnections.insert(tc);
tc->type = TcpConnection::TCP_HTTP_INCOMING;
@ -1701,16 +1704,20 @@ public:
std::string contentType("text/plain"); // default if not changed in handleRequest()
unsigned int scode = 404;
try {
if (_controlPlane)
scode = _controlPlane->handleRequest(tc->from,tc->parser.method,tc->url,tc->headers,tc->body,data,contentType);
else scode = 500;
} catch (std::exception &exc) {
fprintf(stderr,"WARNING: unexpected exception processing control HTTP request: %s" ZT_EOL_S,exc.what());
scode = 500;
} catch ( ... ) {
fprintf(stderr,"WARNING: unexpected exception processing control HTTP request: unknown exceptino" ZT_EOL_S);
scode = 500;
if ( ((!_allowManagementFrom)&&(tc->from.ipScope() == InetAddress::IP_SCOPE_LOOPBACK)) || (_allowManagementFrom.containsAddress(tc->from)) ) {
try {
if (_controlPlane)
scode = _controlPlane->handleRequest(tc->from,tc->parser.method,tc->url,tc->headers,tc->body,data,contentType);
else scode = 500;
} catch (std::exception &exc) {
fprintf(stderr,"WARNING: unexpected exception processing control HTTP request: %s" ZT_EOL_S,exc.what());
scode = 500;
} catch ( ... ) {
fprintf(stderr,"WARNING: unexpected exception processing control HTTP request: unknown exceptino" ZT_EOL_S);
scode = 500;
}
} else {
scode = 401;
}
const char *scodestr;
@ -1975,7 +1982,7 @@ std::string OneService::autoUpdateUrl()
return std::string();
}
OneService *OneService::newInstance(const char *hp,unsigned int port) { return new OneServiceImpl(hp,port); }
OneService *OneService::newInstance(const char *hp,unsigned int port,const char *allowManagementFrom) { return new OneServiceImpl(hp,port,allowManagementFrom); }
OneService::~OneService() {}
} // namespace ZeroTier

View File

@ -98,10 +98,12 @@ public:
*
* @param hp Home path
* @param port TCP and UDP port for packets and HTTP control (if 0, pick random port)
* @param allowManagementFrom If non-NULL, allow control from supplied IP/netmask
*/
static OneService *newInstance(
const char *hp,
unsigned int port);
unsigned int port,
const char *allowManagementFrom = (const char *)0);
virtual ~OneService();

View File

@ -25,6 +25,8 @@ A *jsonp* URL argument may be supplied to request JSONP encapsulation. A JSONP r
<tr><td><b>Field</b></td><td><b>Type</b></td><td><b>Description</b></td><td><b>Writable</b></td></tr>
<tr><td>address</td><td>string</td><td>10-digit hexadecimal ZeroTier address of this node</td><td>no</td></tr>
<tr><td>publicIdentity</td><td>string</td><td>Full public ZeroTier identity of this node</td><td>no</td></tr>
<tr><td>worldId</td><td>integer</td><td>Fixed value representing the virtual data center of Earth.</td><td>no</td></tr>
<tr><td>worldTimestamp</td><td>integer</td><td>Timestamp of the last root server topology change.</td><td>no</td></tr>
<tr><td>online</td><td>boolean</td><td>Does this node appear to have upstream network access?</td><td>no</td></tr>
<tr><td>tcpFallbackActive</td><td>boolean</td><td>Is TCP fallback mode active?</td><td>no</td></tr>
<tr><td>versionMajor</td><td>integer</td><td>ZeroTier major version</td><td>no</td></tr>
@ -77,9 +79,22 @@ Most network settings are not writable, as they are defined by the network contr
<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>routes</td><td>[route]</td><td>ZeroTier-managed route assignments for a network. See below for a description of the route object.</td><td>no</td></tr>
<tr><td>portDeviceName</td><td>string</td><td>OS-specific network device name (if available)</td><td>no</td></tr>
<tr><td>allowManaged</td><td>boolean</td><td>Whether ZeroTier-managed IP addresses are allowed.</td><td>yes</td></tr>
<tr><td>allowGlobal</td><td>boolean</td><td>Whether globally-reachable IP addresses are allowed to be assigned.</td><td>yes</td></tr>
<tr><td>allowDefault</td><td>boolean</td><td>Whether a default route is allowed to be assigned for the network (route all traffic via ZeroTier)</td><td>yes</td></tr>
</table>
`route` objects
<table>
<tr><td><b>Field</b></td><td><b>Type</b></td><td><b>Description</b></td><td><b>Writable</b></td></tr>
<tr><td>target</td><td>string</td><td>Target network / netmask bits, NULL, or 0.0.0.0/0 for default route</td><td>no</td></tr>
<tr><td>via</td><td>string</td><td>Gateway IP address</td><td>no</td></tr>
<tr><td>flags</td><td>integer</td><td>Route flags</td><td>no</td></tr>
<tr><td>metric</td><td>integer</td><td>Route metric (not currently used)</td><td>no</td></tr>
</table>
#### /peer
@ -99,8 +114,6 @@ Getting /peer returns an array of peer objects for all current peers. See below
<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>

View File

@ -0,0 +1,233 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Timers;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Threading;
namespace WinUI
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
APIHandler handler;
Regex charRegex = new Regex("[0-9a-fxA-FX]");
Regex wholeStringRegex = new Regex("^[0-9a-fxA-FX]+$");
Timer timer = new Timer();
bool connected = false;
public MainWindow()
{
InitializeComponent();
if (InitAPIHandler())
{
networksPage.SetAPIHandler(handler);
updateStatus();
if (!connected)
{
MessageBox.Show("Unable to connect to ZeroTier Service.");
}
updateNetworks();
//updatePeers();
DataObject.AddPastingHandler(joinNetworkID, OnPaste);
timer.Elapsed += new ElapsedEventHandler(OnUpdateTimer);
timer.Interval = 2000;
timer.Enabled = true;
}
}
private String readAuthToken(String path)
{
String authToken = "";
if (File.Exists(path))
{
try
{
byte[] tmp = File.ReadAllBytes(path);
authToken = System.Text.Encoding.UTF8.GetString(tmp).Trim();
}
catch
{
MessageBox.Show("Unable to read ZeroTier One Auth Token from:\r\n" + path, "ZeroTier One");
}
}
return authToken;
}
private Int32 readPort(String path)
{
Int32 port = 9993;
try
{
byte[] tmp = File.ReadAllBytes(path);
port = Int32.Parse(System.Text.Encoding.ASCII.GetString(tmp).Trim());
if ((port <= 0) || (port > 65535))
port = 9993;
}
catch
{
}
return port;
}
private bool InitAPIHandler()
{
String localZtDir = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) + "\\ZeroTier\\One";
String globalZtDir = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData) + "\\ZeroTier\\One";
String authToken = "";
Int32 port = 9993;
if (!File.Exists(localZtDir + "\\authtoken.secret") || !File.Exists(localZtDir + "\\zerotier-one.port"))
{
// launch external process to copy file into place
String curPath = System.Reflection.Assembly.GetEntryAssembly().Location;
int index = curPath.LastIndexOf("\\");
curPath = curPath.Substring(0, index);
ProcessStartInfo startInfo = new ProcessStartInfo(curPath + "\\copyutil.exe", globalZtDir + " " + localZtDir);
startInfo.Verb = "runas";
var process = Process.Start(startInfo);
process.WaitForExit();
}
authToken = readAuthToken(localZtDir + "\\authtoken.secret");
if ((authToken == null) || (authToken.Length <= 0))
{
MessageBox.Show("Unable to read ZeroTier One authtoken", "ZeroTier One");
this.Close();
return false;
}
port = readPort(localZtDir + "\\zerotier-one.port");
handler = new APIHandler(port, authToken);
return true;
}
private void updateStatus()
{
var status = handler.GetStatus();
if (status != null)
{
connected = true;
networkId.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() =>
{
this.networkId.Text = status.Address;
}));
versionString.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() =>
{
this.versionString.Content = status.Version;
}));
onlineStatus.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() =>
{
this.onlineStatus.Content = (status.Online ? "ONLINE" : "OFFLINE");
}));
}
else
{
connected = false;
networkId.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() =>
{
this.networkId.Text = "";
}));
versionString.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() =>
{
this.versionString.Content = "0";
}));
onlineStatus.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() =>
{
this.onlineStatus.Content = "OFFLINE";
}));
}
}
private void updateNetworks()
{
var networks = handler.GetNetworks();
networksPage.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() =>
{
networksPage.setNetworks(networks);
}));
}
private void updatePeers()
{
//var peers = handler.GetPeers();
//peersPage.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() =>
//{
// peersPage.SetPeers(peers);
//}));
}
private void OnUpdateTimer(object source, ElapsedEventArgs e)
{
updateStatus();
updateNetworks();
//updatePeers();
}
private void joinButton_Click(object sender, RoutedEventArgs e)
{
if (joinNetworkID.Text.Length < 16)
{
MessageBox.Show("Invalid Network ID");
}
else
{
handler.JoinNetwork(joinNetworkID.Text);
}
}
private void OnNetworkEntered(object sender, TextCompositionEventArgs e)
{
e.Handled = !charRegex.IsMatch(e.Text);
}
private void OnPaste(object sender, DataObjectPastingEventArgs e)
{
var isText = e.SourceDataObject.GetDataPresent(DataFormats.UnicodeText, true);
if (!isText) return;
var text = e.SourceDataObject.GetData(DataFormats.UnicodeText) as string;
if (!wholeStringRegex.IsMatch(text))
{
e.CancelCommand();
}
}
}
}

View File

@ -64,7 +64,7 @@
<TextBlock x:Name="broadcastEnabled" FontFamily="Lucida Console" TextWrapping="Wrap" Text="ENABLED" HorizontalAlignment="Right" Grid.Column="2" Grid.Row="6" Foreground="#FF000000"/>
<TextBlock x:Name="bridgingEnabled" FontFamily="Lucida Console" TextWrapping="Wrap" Text="DISABLED" HorizontalAlignment="Right" Grid.Column="2" Grid.Row="7" Background="#FFEEEEEE" Foreground="#FF000000"/>
<TextBlock x:Name="deviceName" FontFamily="Lucida Console" TextWrapping="Wrap" HorizontalAlignment="Right" Grid.Column="2" Grid.Row="8" Foreground="#FF000000"><Span><Run Text="ethernet_32771"/></Span></TextBlock>
<TextBlock x:Name="managedIps" TextWrapping="Wrap" FontFamily="Lucida Console" HorizontalAlignment="Right" TextAlignment="Right" Grid.Column="2" Grid.Row="9" Foreground="#FF000000"><Span><Run Text="28.2.169.248/7 "/></Span><LineBreak/><Span><Run Text="fd80:56c2:e21c:0000:0199:9383:4a02:a9f8/88"/></Span></TextBlock>
<TextBox x:Name="managedIps" TextWrapping="Wrap" FontFamily="Lucida Console" HorizontalAlignment="Right" TextAlignment="Right" Grid.Column="2" Grid.Row="9" Foreground="#FF000000" IsReadOnly="True" BorderThickness="0" Background="#FFEEEEEE" Text="28.2.169.248/7&#x0a;fd80:56c2:e21c:0000:0199:9383:4a02:a9f8/88"/>
<CheckBox x:Name="allowGlobal" HorizontalAlignment="Right" Grid.Column="2" Grid.Row="10" />
<CheckBox x:Name="allowManaged" HorizontalAlignment="Right" Grid.Column="2" Grid.Row="11" />
<CheckBox x:Name="allowDefault" HorizontalAlignment="Right" Grid.Column="2" Grid.Row="12" />

View File

@ -20,7 +20,7 @@ namespace WinUI
/// </summary>
public partial class NetworkInfoView : UserControl
{
private ZeroTierNetwork network;
public ZeroTierNetwork network;
public NetworkInfoView(ZeroTierNetwork network)
{
@ -29,19 +29,41 @@ namespace WinUI
this.network = network;
UpdateNetworkData();
allowDefault.Checked += AllowDefault_CheckStateChanged;
allowDefault.Unchecked += AllowDefault_CheckStateChanged;
allowGlobal.Checked += AllowGlobal_CheckStateChanged;
allowGlobal.Unchecked += AllowGlobal_CheckStateChanged;
allowManaged.Checked += AllowManaged_CheckStateChanged;
allowManaged.Unchecked += AllowManaged_CheckStateChanged;
}
private void UpdateNetworkData()
{
this.networkId.Text = network.NetworkId;
this.networkName.Text = network.NetworkName;
this.networkStatus.Text = network.NetworkStatus;
this.networkType.Text = network.NetworkType;
this.macAddress.Text = network.MacAddress;
this.mtu.Text = network.MTU.ToString();
if (this.networkId.Text != network.NetworkId)
this.networkId.Text = network.NetworkId;
if (this.networkName.Text != network.NetworkName)
this.networkName.Text = network.NetworkName;
if (this.networkStatus.Text != network.NetworkStatus)
this.networkStatus.Text = network.NetworkStatus;
if (this.networkType.Text != network.NetworkType)
this.networkType.Text = network.NetworkType;
if (this.macAddress.Text != network.MacAddress)
this.macAddress.Text = network.MacAddress;
if (this.mtu.Text != network.MTU.ToString())
this.mtu.Text = network.MTU.ToString();
this.broadcastEnabled.Text = (network.BroadcastEnabled ? "ENABLED" : "DISABLED");
this.bridgingEnabled.Text = (network.Bridge ? "ENABLED" : "DISABLED");
this.deviceName.Text = network.DeviceName;
if (this.deviceName.Text != network.DeviceName)
this.deviceName.Text = network.DeviceName;
string iplist = "";
for (int i = 0; i < network.AssignedAddresses.Length; ++i)
@ -51,19 +73,12 @@ namespace WinUI
iplist += "\n";
}
this.managedIps.Text = iplist;
if (this.managedIps.Text != iplist)
this.managedIps.Text = iplist;
this.allowDefault.IsChecked = network.AllowDefault;
this.allowGlobal.IsChecked = network.AllowGlobal;
this.allowManaged.IsChecked = network.AllowManaged;
allowDefault.Checked += AllowDefault_CheckStateChanged;
allowDefault.Unchecked += AllowDefault_CheckStateChanged;
allowGlobal.Checked += AllowGlobal_CheckStateChanged;
allowGlobal.Unchecked += AllowGlobal_CheckStateChanged;
allowManaged.Checked += AllowManaged_CheckStateChanged;
allowManaged.Unchecked += AllowManaged_CheckStateChanged;
}
public bool HasNetwork(ZeroTierNetwork network)
@ -74,6 +89,13 @@ namespace WinUI
return false;
}
public void SetNetworkInfo(ZeroTierNetwork network)
{
this.network = network;
UpdateNetworkData();
}
private void leaveButton_Click(object sender, RoutedEventArgs e)
{
APIHandler.Instance.LeaveNetwork(network.NetworkId);

View File

@ -93,7 +93,9 @@
</Grid>
</ItemsPanelTemplate>
</StatusBar.ItemsPanel>
<StatusBarItem Grid.Column="0" x:Name="networkId" Content="deadbeef00" Foreground="White" FontFamily="Lucida Console"/>
<StatusBarItem Grid.Column="0" x:Name="networkId_placeholder">
<TextBox x:Name="networkId" Text="deadbeef00" HorizontalAlignment="Left" Grid.Column="0" Foreground="White" FontFamily="Lucida Console" BorderThickness="0" IsReadOnly="true" Background="Transparent"/>
</StatusBarItem>
<StatusBarItem Grid.Column="1" x:Name="onlineStatus" Content="ONLINE" Foreground="White" FontFamily="Lucida Console"/>
<StatusBarItem Grid.Column="2" x:Name="versionString" Content="1.0.5" Foreground="White" FontFamily="Lucida Console"/>
<StatusBarItem Grid.Column="3" x:Name="blank" Content="" Height="43" Foreground="White"/>

View File

@ -63,7 +63,7 @@ namespace WinUI
networkId.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() =>
{
this.networkId.Content = status.Address;
this.networkId.Text = status.Address;
}));
versionString.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() =>
{
@ -80,7 +80,7 @@ namespace WinUI
networkId.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() =>
{
this.networkId.Content = "";
this.networkId.Text = "";
}));
versionString.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() =>
{

View File

@ -27,18 +27,73 @@ namespace WinUI
public void setNetworks(List<ZeroTierNetwork> networks)
{
this.wrapPanel.Children.Clear();
if (networks == null)
{
this.wrapPanel.Children.Clear();
return;
}
for (int i = 0; i < networks.Count; ++i)
foreach (ZeroTierNetwork network in networks)
{
this.wrapPanel.Children.Add(
new NetworkInfoView(
networks.ElementAt<ZeroTierNetwork>(i)));
NetworkInfoView view = ChildWithNetwork(network);
if (view != null)
{
view.SetNetworkInfo(network);
}
else
{
wrapPanel.Children.Add(
new NetworkInfoView(
network));
}
}
// remove networks we're no longer joined to.
List<ZeroTierNetwork> tmpList = GetNetworksFromChildren();
foreach (ZeroTierNetwork n in networks)
{
if (tmpList.Contains(n))
{
tmpList.Remove(n);
}
}
foreach (ZeroTierNetwork n in tmpList)
{
NetworkInfoView view = ChildWithNetwork(n);
if (view != null)
{
wrapPanel.Children.Remove(view);
}
}
}
private NetworkInfoView ChildWithNetwork(ZeroTierNetwork network)
{
List<NetworkInfoView> list = wrapPanel.Children.OfType<NetworkInfoView>().ToList();
foreach (NetworkInfoView view in list)
{
if (view.HasNetwork(network))
{
return view;
}
}
return null;
}
private List<ZeroTierNetwork> GetNetworksFromChildren()
{
List<ZeroTierNetwork> networks = new List<ZeroTierNetwork>(wrapPanel.Children.Count);
List<NetworkInfoView> list = wrapPanel.Children.OfType<NetworkInfoView>().ToList();
foreach (NetworkInfoView n in list)
{
networks.Add(n.network);
}
return networks;
}
}
}

View File

@ -20,6 +20,7 @@
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\..\controller\EmbeddedNetworkController.cpp" />
<ClCompile Include="..\..\controller\JSONDB.cpp" />
<ClCompile Include="..\..\ext\http-parser\http_parser.c" />
<ClCompile Include="..\..\ext\libnatpmp\getgateway.c" />
<ClCompile Include="..\..\ext\libnatpmp\natpmp.c" />
@ -269,7 +270,7 @@
<SDLCheck>true</SDLCheck>
<AdditionalIncludeDirectories>
</AdditionalIncludeDirectories>
<PreprocessorDefinitions>NOMINMAX;STATICLIB;WIN32;ZT_TRACE;ZT_USE_MINIUPNPC;MINIUPNP_STATICLIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>NOMINMAX;STATICLIB;WIN32;ZT_TRACE;ZT_RULES_ENGINE_DEBUGGING;ZT_USE_MINIUPNPC;MINIUPNP_STATICLIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<MultiProcessorCompilation>false</MultiProcessorCompilation>
<DisableSpecificWarnings>4996</DisableSpecificWarnings>
</ClCompile>

View File

@ -261,6 +261,9 @@
<ClCompile Include="..\..\controller\EmbeddedNetworkController.cpp">
<Filter>Source Files\controller</Filter>
</ClCompile>
<ClCompile Include="..\..\controller\JSONDB.cpp">
<Filter>Source Files\controller</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="resource.h">