Expand C API and simplify NodeService

This commit is contained in:
Joseph Henry
2021-04-22 11:20:04 -07:00
parent 565b56d290
commit 43350691b5
30 changed files with 7697 additions and 5467 deletions

View File

@@ -4,7 +4,7 @@
* Use of this software is governed by the Business Source License included
* in the LICENSE.TXT file in the project's root directory.
*
* Change Date: 2025-01-01
* Change Date: 2026-01-01
*
* On the date above, in accordance with the Business Source License, use
* of this software will be governed by version 2.0 of the Apache License.
@@ -14,16 +14,20 @@
/**
* @file
*
* Header for ZeroTier Node Service (a distant relative of OneService)
* ZeroTier Node Service
*/
#ifndef ZT_NODE_SERVICE_HPP
#define ZT_NODE_SERVICE_HPP
#ifndef ZTS_NODE_SERVICE_HPP
#define ZTS_NODE_SERVICE_HPP
#include "Binder.hpp"
#include "Constants.hpp"
#include "Events.hpp"
#include "InetAddress.hpp"
#include "Mutex.hpp"
#include "Node.hpp"
#include "PortMapper.hpp"
#include "VirtualTap.hpp"
#include "ZeroTierSockets.h"
#include <string>
@@ -31,8 +35,9 @@
#define ZTS_SERVICE_THREAD_NAME "ZTServiceThread"
#define ZTS_EVENT_CALLBACK_THREAD_NAME "ZTEventCallbackThread"
// Interface metric for ZeroTier taps -- this ensures that if we are on WiFi and also
// bridged via ZeroTier to the same LAN traffic will (if the OS is sane) prefer WiFi.
// Interface metric for ZeroTier taps -- this ensures that if we are on WiFi and
// also bridged via ZeroTier to the same LAN traffic will (if the OS is sane)
// prefer WiFi.
#define ZT_IF_METRIC 5000
// How often to check for new multicast subscriptions on a tap device
#define ZT_TAP_CHECK_MULTICAST_INTERVAL 5000
@@ -40,21 +45,16 @@
#define ZT_LOCAL_INTERFACE_CHECK_INTERVAL 60000
#ifdef __WINDOWS__
#include <Windows.h>
#include <Windows.h>
#endif
namespace ZeroTier {
/**
* Local service for ZeroTier One as system VPN/NFV provider
* ZeroTier node service
*/
class NodeService {
public:
uint16_t _userProvidedPort;
std::string _userProvidedPath;
char _userProvidedPublicIdentity[ZT_IDENTITY_STRING_BUFFER_LENGTH];
char _userProvidedSecretIdentity[ZT_IDENTITY_STRING_BUFFER_LENGTH];
/**
* Returned by node main if/when it terminates
*/
@@ -91,140 +91,455 @@ class NodeService {
/**
* Whitelist of addresses that can be configured by this network.
* If empty and allowManaged is true, allow all private/pseudoprivate addresses.
* If empty and allowManaged is true, allow all
* private/pseudoprivate addresses.
*/
std::vector<InetAddress> allowManagedWhitelist;
/**
* Allow configuration of IPs and routes within global (Internet) IP space?
* Allow configuration of IPs and routes within global (Internet) IP
* space?
*/
bool allowGlobal;
/**
* Allow overriding of system default routes for "full tunnel" operation?
* 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();
Phy<NodeService*> _phy;
Node* _node;
unsigned int _primaryPort = 0;
unsigned int _secondaryPort = 0;
unsigned int _tertiaryPort = 0;
volatile unsigned int _udpPortPickerCounter;
/**
* Create a new instance of the service
std::map<uint64_t, unsigned int> peerCache;
// Local configuration and memo-ized information from it
Hashtable<uint64_t, std::vector<InetAddress> > _v4Hints;
Hashtable<uint64_t, std::vector<InetAddress> > _v6Hints;
Hashtable<uint64_t, std::vector<InetAddress> > _v4Blacklists;
Hashtable<uint64_t, std::vector<InetAddress> > _v6Blacklists;
std::vector<InetAddress> _globalV4Blacklist;
std::vector<InetAddress> _globalV6Blacklist;
std::vector<InetAddress> _allowManagementFrom;
std::vector<std::string> _interfacePrefixBlacklist;
Mutex _localConfig_m;
std::vector<InetAddress> explicitBind;
/*
* To attempt to handle NAT/gateway craziness we use three local UDP
* ports:
*
* Once created, you must call the run() method to actually start
* processing.
* [0] is the normal/default port, usually 9993
* [1] is a port derived from our ZeroTier address
* [2] is a port computed from the normal/default for use with
* uPnP/NAT-PMP mappings
*
* 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)
* [2] exists because on some gateways trying to do regular NAT-t
* interferes destructively with uPnP port mapping behavior in very
* weird buggy ways. It's only used if uPnP/NAT-PMP is enabled in this
* build.
*/
static NodeService* newInstance(const char* hp, unsigned int port);
unsigned int _ports[3] = { 0 };
Binder _binder;
virtual ~NodeService();
// Time we last received a packet from a global address
uint64_t _lastDirectReceiveFromGlobal;
/**
* 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;
// Last potential sleep/wake event
uint64_t _lastRestart;
/**
* @return Reason for terminating or ONE_STILL_RUNNING if running
*/
virtual ReasonForTermination reasonForTermination() const = 0;
// Deadline for the next background task service function
volatile int64_t _nextBackgroundTaskDeadline;
/**
* @return Fatal error message or empty string if none
*/
virtual std::string fatalErrorMessage() const = 0;
// Configured networks
struct NetworkState {
NetworkState() : tap((VirtualTap*)0)
{
// Real defaults are in network 'up' code in network event
// handler
settings.allowManaged = true;
settings.allowGlobal = false;
settings.allowDefault = false;
}
/**
* @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;
VirtualTap* tap;
ZT_VirtualNetworkConfig config; // memcpy() of raw config from core
std::vector<InetAddress> managedIps;
NetworkSettings settings;
};
std::map<uint64_t, NetworkState> _nets;
/**
* Whether we allow access to the service via local HTTP requests (disabled by default in libzt)
*/
bool allowHttpBackplaneManagement = false;
/** Lock to control access to network configuration data */
Mutex _nets_m;
/** Lock to control access to storage data */
Mutex _store_m;
/** Lock to control access to service run state */
Mutex _run_m;
// Set to false to force service to stop
volatile bool _run;
/** Lock to control access to termination reason */
Mutex _termReason_m;
// Termination status information
ReasonForTermination _termReason;
/**
* @return Reference to the Node
*/
virtual Node* getNode() = 0;
std::string _fatalErrorMessage;
/**
* Fills out a structure with network-specific route information
*/
virtual void getRoutes(uint64_t nwid, void* routeArray, unsigned int* numRoutes) = 0;
virtual void join(uint64_t nwid) = 0;
virtual void leave(uint64_t nwid) = 0;
virtual void getIdentity(char* key_pair_str, uint16_t* key_buf_len) = 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;
/**
* @return True if service is still running
*/
inline bool isRunning() const
{
return (this->reasonForTermination() == ONE_STILL_RUNNING);
}
protected:
NodeService()
{
}
private:
NodeService(const NodeService& one)
{
}
inline NodeService& operator=(const NodeService& one)
{
return *this;
}
};
struct serviceParameters {
int port;
std::string path;
char publicIdentityStr[ZT_IDENTITY_STRING_BUFFER_LENGTH];
char secretIdentityStr[ZT_IDENTITY_STRING_BUFFER_LENGTH];
};
#ifdef __WINDOWS__
DWORD WINAPI _runNodeService(LPVOID arg);
#else
/**
* NodeService thread
*/
void* _runNodeService(void* arg);
// uPnP/NAT-PMP port mapper if enabled
bool _portMappingEnabled; // local.conf settings
#ifdef ZT_USE_MINIUPNPC
PortMapper* _portMapper;
#endif
/** Whether we allow caching network configs to storage */
uint8_t _allowNetworkCaching;
/** Whether we allow caching peer hints to storage */
uint8_t _allowPeerCaching;
char _publicIdStr[ZT_IDENTITY_STRING_BUFFER_LENGTH] = { 0 };
char _secretIdStr[ZT_IDENTITY_STRING_BUFFER_LENGTH] = { 0 };
char _planetData[ZTS_STORE_DATA_LEN] = { 0 };
/** Whether the node has successfully come online */
bool _nodeIsOnline;
/** Whether we allow the NodeService to generate events for the user */
bool _eventsEnabled;
/** Primary port defined by the user */
uint16_t _userProvidedPort;
/** Storage path defined by the user */
std::string _homePath;
/** System to ingest events from this class and emit them to the user */
Events* _events;
/**
* Constructor
*/
NodeService();
/**
* Destructor
*/
~NodeService();
ReasonForTermination run();
ReasonForTermination reasonForTermination() const;
std::string fatalErrorMessage() const;
void getRoutes(uint64_t net_id, void* routeArray, unsigned int* numRoutes);
void terminate();
void uninitialize();
int getNetworkSettings(const uint64_t net_id, NetworkSettings& settings) const;
int checkIfManagedIsAllowed(const NetworkState& n, const InetAddress& target);
void syncManagedStuff(NetworkState& n);
void phyOnDatagram(
PhySocket* sock,
void** uptr,
const struct sockaddr* localAddr,
const struct sockaddr* from,
void* data,
unsigned long len);
int nodeVirtualNetworkConfigFunction(
uint64_t net_id,
void** nuptr,
enum ZT_VirtualNetworkConfigOperation op,
const ZT_VirtualNetworkConfig* nwc);
void nodeEventCallback(enum ZT_Event event, const void* metaData);
zts_net_info_t* prepare_network_details_msg(const NetworkState& n);
void generateEventMsgs();
/** Join a network */
int join(uint64_t net_id);
/** Leave a network */
int leave(uint64_t net_id);
/** Return number of networks joined */
int networkCount();
/** Orbit a moon */
int orbit(void* tptr, uint64_t moonWorldId, uint64_t moonSeed);
/** De-orbit a moon */
int deorbit(void* tptr, uint64_t moonWorldId);
uint64_t getNodeId();
int getIdentity(char* keypair, uint16_t* len);
int setIdentity(const char* keypair, uint16_t len);
void nodeStatePutFunction(
enum ZT_StateObjectType type,
const uint64_t id[2],
const void* data,
int len);
int nodeStateGetFunction(
enum ZT_StateObjectType type,
const uint64_t id[2],
void* data,
unsigned int maxlen);
int nodeWirePacketSendFunction(
const int64_t localSocket,
const struct sockaddr_storage* addr,
const void* data,
unsigned int len,
unsigned int ttl);
void nodeVirtualNetworkFrameFunction(
uint64_t net_id,
void** nuptr,
uint64_t sourceMac,
uint64_t destMac,
unsigned int etherType,
unsigned int vlanId,
const void* data,
unsigned int len);
int nodePathCheckFunction(
uint64_t ztaddr,
const int64_t localSocket,
const struct sockaddr_storage* remoteAddr);
int nodePathLookupFunction(uint64_t ztaddr, int family, struct sockaddr_storage* result);
void tapFrameHandler(
uint64_t net_id,
const MAC& from,
const MAC& to,
unsigned int etherType,
unsigned int vlanId,
const void* data,
unsigned int len);
int shouldBindInterface(const char* ifname, const InetAddress& ifaddr);
int _trialBind(unsigned int port);
/** Return whether the NodeService is running */
int isRunning();
/** Return whether the node is online */
int nodeIsOnline();
/** Instruct the NodeService on where to look for identity files and caches */
int setHomePath(const char* homePath);
/** Set the NodeService's primary port */
int setPrimaryPort(unsigned short primaryPort);
/** Get the NodeService's primary port */
unsigned short getPrimaryPort();
/** Set the event system instance used to convey messages to the user */
int setUserEventSystem(Events* events);
/** Set the planet definition */
int setPlanet(const char* data, int len);
/** Add Interface prefix to blacklist (prevents ZeroTier from using that interface) */
int addInterfacePrefixToBlacklist(const char* prefix, int len);
/** Return the MAC Address of the node in the given network */
uint64_t getMACAddress(uint64_t net_id);
int getNetworkName(uint64_t net_id, char* dst, int len);
int allowPeerCaching(int allowed);
int allowNetworkCaching(int allowed);
int getNetworkBroadcast(uint64_t net_id);
int getNetworkMTU(uint64_t net_id);
int getNetworkType(uint64_t net_id);
int getNetworkStatus(uint64_t net_id);
int getFirstAssignedAddr(uint64_t net_id, int family, struct zts_sockaddr_storage* addr);
int getAllAssignedAddr(uint64_t net_id, struct zts_sockaddr_storage* addr, int* count);
int networkHasRoute(uint64_t net_id, int family);
int addrIsAssigned(uint64_t net_id, int family);
void phyOnTcpConnect(PhySocket* sock, void** uptr, bool success)
{
}
void phyOnTcpAccept(
PhySocket* sockL,
PhySocket* sockN,
void** uptrL,
void** uptrN,
const struct sockaddr* from)
{
}
void phyOnTcpClose(PhySocket* sock, void** uptr)
{
}
void phyOnTcpData(PhySocket* sock, void** uptr, void* data, unsigned long len)
{
}
void phyOnTcpWritable(PhySocket* sock, void** uptr)
{
}
void phyOnFileDescriptorActivity(PhySocket* sock, void** uptr, bool readable, bool writable)
{
}
void phyOnUnixAccept(PhySocket* sockL, PhySocket* sockN, void** uptrL, void** uptrN)
{
}
void phyOnUnixClose(PhySocket* sock, void** uptr)
{
}
void phyOnUnixData(PhySocket* sock, void** uptr, void* data, unsigned long len)
{
}
void phyOnUnixWritable(PhySocket* sock, void** uptr)
{
}
};
static int SnodeVirtualNetworkConfigFunction(
ZT_Node* node,
void* uptr,
void* tptr,
uint64_t net_id,
void** nuptr,
enum ZT_VirtualNetworkConfigOperation op,
const ZT_VirtualNetworkConfig* nwconf)
{
return reinterpret_cast<NodeService*>(uptr)
->nodeVirtualNetworkConfigFunction(net_id, nuptr, op, nwconf);
}
static void
SnodeEventCallback(ZT_Node* node, void* uptr, void* tptr, enum ZT_Event event, const void* metaData)
{
reinterpret_cast<NodeService*>(uptr)->nodeEventCallback(event, metaData);
}
static void SnodeStatePutFunction(
ZT_Node* node,
void* uptr,
void* tptr,
enum ZT_StateObjectType type,
const uint64_t id[2],
const void* data,
int len)
{
reinterpret_cast<NodeService*>(uptr)->nodeStatePutFunction(type, id, data, len);
}
static int SnodeStateGetFunction(
ZT_Node* node,
void* uptr,
void* tptr,
enum ZT_StateObjectType type,
const uint64_t id[2],
void* data,
unsigned int maxlen)
{
return reinterpret_cast<NodeService*>(uptr)->nodeStateGetFunction(type, id, data, maxlen);
}
static int SnodeWirePacketSendFunction(
ZT_Node* node,
void* uptr,
void* tptr,
int64_t localSocket,
const struct sockaddr_storage* addr,
const void* data,
unsigned int len,
unsigned int ttl)
{
return reinterpret_cast<NodeService*>(uptr)
->nodeWirePacketSendFunction(localSocket, addr, data, len, ttl);
}
static void SnodeVirtualNetworkFrameFunction(
ZT_Node* node,
void* uptr,
void* tptr,
uint64_t net_id,
void** nuptr,
uint64_t sourceMac,
uint64_t destMac,
unsigned int etherType,
unsigned int vlanId,
const void* data,
unsigned int len)
{
reinterpret_cast<NodeService*>(uptr)->nodeVirtualNetworkFrameFunction(
net_id,
nuptr,
sourceMac,
destMac,
etherType,
vlanId,
data,
len);
}
static int SnodePathCheckFunction(
ZT_Node* node,
void* uptr,
void* tptr,
uint64_t ztaddr,
int64_t localSocket,
const struct sockaddr_storage* remoteAddr)
{
return reinterpret_cast<NodeService*>(uptr)->nodePathCheckFunction(
ztaddr,
localSocket,
remoteAddr);
}
static int SnodePathLookupFunction(
ZT_Node* node,
void* uptr,
void* tptr,
uint64_t ztaddr,
int family,
struct sockaddr_storage* result)
{
return reinterpret_cast<NodeService*>(uptr)->nodePathLookupFunction(ztaddr, family, result);
}
static void StapFrameHandler(
void* uptr,
void* tptr,
uint64_t net_id,
const MAC& from,
const MAC& to,
unsigned int etherType,
unsigned int vlanId,
const void* data,
unsigned int len)
{
reinterpret_cast<NodeService*>(uptr)
->tapFrameHandler(net_id, from, to, etherType, vlanId, data, len);
}
} // namespace ZeroTier
#endif