/* * Copyright (c)2013-2021 ZeroTier, Inc. * * 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: 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. */ /****/ /** * @file * * Node / Network control interface */ #include "Constants.hpp" #include "Events.hpp" #include "Mutex.hpp" #include "Node.hpp" #include "NodeService.hpp" #include "OSUtils.hpp" #include "Signals.hpp" #include "Thread.hpp" #include "VirtualTap.hpp" #include "ZeroTierSockets.h" #include "lwip/stats.h" #include #include #include using namespace ZeroTier; #ifdef __WINDOWS__ #include WSADATA wsaData; #endif #ifdef ZTS_ENABLE_PYTHON #define ZTS_ENABLE_CUSTOM_SIGNAL_HANDLERS 1 #endif namespace ZeroTier { #ifdef ZTS_ENABLE_PYTHON #endif #ifdef ZTS_ENABLE_PINVOKE extern void (*_userEventCallback)(void*); #endif #ifdef ZTS_C_API_ONLY extern void (*_userEventCallback)(void*); #endif extern uint8_t allowNetworkCaching; extern uint8_t allowPeerCaching; NodeService* zts_service; Events* zts_events; extern Mutex events_m; Mutex service_m; /** Set up service and callback threads and tell them about one another. * A separate thread is used for callbacks so that if the user fails to return * control it won't affect the core service's operations. */ int init_subsystems() { if (zts_events->getState(ZTS_STATE_FREE_CALLED)) { return ZTS_ERR_SERVICE; } #ifdef ZTS_ENABLE_CUSTOM_SIGNAL_HANDLERS _install_signal_handlers(); #endif // ZTS_ENABLE_CUSTOM_SIGNAL_HANDLERS if (! zts_events) { zts_events = new Events(); } if (! zts_service) { zts_service = new NodeService(); zts_service->setUserEventSystem(zts_events); } return ZTS_ERR_OK; } #ifdef __cplusplus extern "C" { #endif int zts_init_from_storage(const char* path) { ACQUIRE_SERVICE_OFFLINE(); zts_service->setHomePath(path); return ZTS_ERR_OK; } int zts_init_from_memory(const char* keypair, uint16_t len) { ACQUIRE_SERVICE_OFFLINE(); return zts_service->setIdentity(keypair, len); } #ifdef ZTS_ENABLE_PYTHON int zts_init_set_event_handler(PythonDirectorCallbackClass* callback); #endif #ifdef ZTS_ENABLE_PINVOKE int zts_init_set_event_handler(CppCallback callback); #endif #ifdef ZTS_C_API_ONLY int zts_init_set_event_handler(void (*callback)(void*)) #endif { ACQUIRE_SERVICE_OFFLINE(); if (! callback) { return ZTS_ERR_ARG; } _userEventCallback = callback; return ZTS_ERR_OK; } int zts_init_blacklist_if(const char* prefix, int len) { ACQUIRE_SERVICE_OFFLINE(); return zts_service->addInterfacePrefixToBlacklist(prefix, len); } int zts_init_set_planet(const char* planet_data, int len) { ACQUIRE_SERVICE_OFFLINE(); return zts_service->setPlanet(planet_data, len); } int zts_init_set_port(unsigned short port) { ACQUIRE_SERVICE_OFFLINE(); zts_service->setPrimaryPort(port); return ZTS_ERR_OK; } int zts_init_allow_peer_cache(int allowed) { ACQUIRE_SERVICE_OFFLINE(); return zts_service->allowPeerCaching(allowed); } int zts_init_allow_net_cache(int allowed) { ACQUIRE_SERVICE_OFFLINE(); return zts_service->allowNetworkCaching(allowed); } int zts_init_clear() { ACQUIRE_SERVICE_OFFLINE(); ACQUIRE_EVENTS(); _userEventCallback = NULL; zts_service->uninitialize(); return ZTS_ERR_OK; } int zts_addr_compute_6plane( const uint64_t net_id, const uint64_t node_id, struct zts_sockaddr_storage* addr) { if (! addr || ! net_id || ! node_id) { return ZTS_ERR_ARG; } InetAddress _6planeAddr = InetAddress::makeIpv66plane(net_id, node_id); struct sockaddr_in6* in6 = (struct sockaddr_in6*)addr; memcpy(in6->sin6_addr.s6_addr, _6planeAddr.rawIpData(), sizeof(struct in6_addr)); return ZTS_ERR_OK; } int zts_addr_compute_rfc4193( const uint64_t net_id, const uint64_t node_id, struct zts_sockaddr_storage* addr) { if (! addr || ! net_id || ! node_id) { return ZTS_ERR_ARG; } InetAddress _rfc4193Addr = InetAddress::makeIpv6rfc4193(net_id, node_id); struct sockaddr_in6* in6 = (struct sockaddr_in6*)addr; memcpy(in6->sin6_addr.s6_addr, _rfc4193Addr.rawIpData(), sizeof(struct in6_addr)); return ZTS_ERR_OK; } int zts_addr_compute_rfc4193_str(uint64_t net_id, uint64_t node_id, char* dst, int len) { if (! net_id || ! node_id || ! dst || len != ZTS_IP_MAX_STR_LEN) { return ZTS_ERR_ARG; } struct zts_sockaddr_storage ss; int err = ZTS_ERR_OK; if ((err = zts_addr_compute_rfc4193(net_id, node_id, &ss)) != ZTS_ERR_OK) { return err; } struct zts_sockaddr_in6* in6 = (struct zts_sockaddr_in6*)&ss; zts_inet_ntop(ZTS_AF_INET6, &(in6->sin6_addr), dst, ZTS_IP_MAX_STR_LEN); return ZTS_ERR_OK; } int zts_addr_compute_6plane_str(uint64_t net_id, uint64_t node_id, char* dst, int len) { if (! net_id || ! node_id || ! dst || len != ZTS_IP_MAX_STR_LEN) { return ZTS_ERR_ARG; } struct zts_sockaddr_storage ss; int err = ZTS_ERR_OK; if ((err = zts_addr_compute_6plane(net_id, node_id, &ss)) != ZTS_ERR_OK) { return err; } struct zts_sockaddr_in6* in6 = (struct zts_sockaddr_in6*)&ss; zts_inet_ntop(ZTS_AF_INET6, &(in6->sin6_addr), dst, ZTS_IP_MAX_STR_LEN); return ZTS_ERR_OK; } uint64_t zts_net_compute_adhoc_id(uint16_t start_port, uint16_t end_port) { char net_id_str[ZTS_INET6_ADDRSTRLEN] = { 0 }; OSUtils::ztsnprintf(net_id_str, ZTS_INET6_ADDRSTRLEN, "ff%04x%04x000000", start_port, end_port); return strtoull(net_id_str, NULL, 16); } int zts_id_new(char* key, uint16_t* dst_len) { if (key == NULL || *dst_len != ZT_IDENTITY_STRING_BUFFER_LENGTH) { return ZTS_ERR_ARG; } Identity id; id.generate(); char idtmp[1024] = { 0 }; std::string idser = id.toString(true, idtmp); uint16_t key_pair_len = idser.length(); if (key_pair_len > *dst_len) { return ZTS_ERR_ARG; } memcpy(key, idser.c_str(), key_pair_len); *dst_len = key_pair_len; return ZTS_ERR_OK; } int zts_id_pair_is_valid(const char* key, int len) { if (key == NULL || len != ZT_IDENTITY_STRING_BUFFER_LENGTH) { return false; } Identity id; if ((strlen(key) > 32) && (key[10] == ':')) { if (id.fromString(key)) { return id.locallyValidate(); } } return false; } int zts_node_get_id_pair(char* key, uint16_t* dst_len) { ACQUIRE_SERVICE(ZTS_ERR_SERVICE); zts_service->getIdentity(key, dst_len); return *dst_len > 0 ? ZTS_ERR_OK : ZTS_ERR_GENERAL; } #if defined(__WINDOWS__) DWORD WINAPI cbRun(LPVOID arg) #else void* cbRun(void* arg) #endif { #if defined(__APPLE__) pthread_setname_np(ZTS_EVENT_CALLBACK_THREAD_NAME); #endif zts_events->run(); #if ZTS_ENABLE_JAVA _java_detach_from_thread(); // pthread_exit(0); #endif return NULL; } int zts_addr_is_assigned(uint64_t net_id, int family) { ACQUIRE_SERVICE(0); return zts_service->addrIsAssigned(net_id, family); } int zts_addr_get(uint64_t net_id, int family, struct zts_sockaddr_storage* addr) { ACQUIRE_SERVICE(ZTS_ERR_SERVICE); return zts_service->getFirstAssignedAddr(net_id, family, addr); } int zts_addr_get_str(uint64_t net_id, int family, char* dst, int len) { // No service lock required since zts_addr_get will lock it if (net_id == 0) { return ZTS_ERR_ARG; } if (len < ZTS_INET6_ADDRSTRLEN) { return ZTS_ERR_ARG; } if (family == ZTS_AF_INET) { struct zts_sockaddr_storage ss; int err = ZTS_ERR_OK; if ((err = zts_addr_get(net_id, family, &ss)) != ZTS_ERR_OK) { return err; } struct zts_sockaddr_in* in4 = (struct zts_sockaddr_in*)&ss; zts_inet_ntop(family, &(in4->sin_addr), dst, ZTS_INET6_ADDRSTRLEN); } if (family == ZTS_AF_INET6) { struct zts_sockaddr_storage ss; int err = ZTS_ERR_OK; if ((err = zts_addr_get(net_id, family, &ss)) != ZTS_ERR_OK) { return err; } struct zts_sockaddr_in6* in6 = (struct zts_sockaddr_in6*)&ss; zts_inet_ntop(family, &(in6->sin6_addr), dst, ZTS_INET6_ADDRSTRLEN); } return ZTS_ERR_OK; } int zts_addr_get_all(uint64_t net_id, struct zts_sockaddr_storage* addr, int* count) { ACQUIRE_SERVICE(ZTS_ERR_SERVICE); return zts_service->getAllAssignedAddr(net_id, addr, count); } int zts_net_join(const uint64_t net_id) { ACQUIRE_SERVICE(ZTS_ERR_SERVICE); return zts_service->join(net_id); } int zts_net_leave(const uint64_t net_id) { ACQUIRE_SERVICE(ZTS_ERR_SERVICE); return zts_service->leave(net_id); } int zts_net_count() { ACQUIRE_SERVICE(ZTS_ERR_SERVICE); return zts_service->networkCount(); } uint64_t zts_net_get_mac(uint64_t net_id) { ACQUIRE_SERVICE(ZTS_ERR_SERVICE); return zts_service->getMACAddress(net_id); } ZTS_API int ZTCALL zts_net_get_mac_str(uint64_t net_id, char* dst, int len) { ACQUIRE_SERVICE(ZTS_ERR_SERVICE); if (! dst || len < ZTS_MAC_ADDRSTRLEN) { return ZTS_ERR_ARG; } uint64_t mac = zts_service->getMACAddress(net_id); OSUtils::ztsnprintf( dst, ZTS_MAC_ADDRSTRLEN, "%x:%x:%x:%x:%x:%x", (mac >> 48) & 0xFF, (mac >> 32) & 0xFF, (mac >> 24) & 0xFF, (mac >> 16) & 0xFF, (mac >> 8) & 0xFF, (mac >> 0) & 0xFF); return ZTS_ERR_OK; } int zts_net_get_broadcast(uint64_t net_id) { ACQUIRE_SERVICE(ZTS_ERR_SERVICE); return zts_service->getNetworkBroadcast(net_id); } int zts_net_get_mtu(uint64_t net_id) { ACQUIRE_SERVICE(ZTS_ERR_SERVICE); return zts_service->getNetworkMTU(net_id); } int zts_net_get_name(uint64_t net_id, char* dst, int len) { ACQUIRE_SERVICE(ZTS_ERR_SERVICE); return zts_service->getNetworkName(net_id, dst, len); } int zts_net_get_status(uint64_t net_id) { ACQUIRE_SERVICE(ZTS_ERR_SERVICE); return zts_service->getNetworkStatus(net_id); } int zts_net_get_type(uint64_t net_id) { ACQUIRE_SERVICE(ZTS_ERR_SERVICE); return zts_service->getNetworkType(net_id); } int zts_route_is_assigned(uint64_t net_id, int family) { ACQUIRE_SERVICE(ZTS_ERR_SERVICE); return zts_service->networkHasRoute(net_id, family); } // Start a ZeroTier NodeService background thread #if defined(__WINDOWS__) DWORD WINAPI _runNodeService(LPVOID arg) #else void* _runNodeService(void* arg) #endif { #if defined(__APPLE__) pthread_setname_np(ZTS_SERVICE_THREAD_NAME); #endif try { for (;;) { switch (zts_service->run()) { case NodeService::ONE_STILL_RUNNING: case NodeService::ONE_NORMAL_TERMINATION: // zts_events->enqueue(ZTS_EVENT_NODE_NORMAL_TERMINATION, NULL); break; case NodeService::ONE_UNRECOVERABLE_ERROR: // DEBUG_ERROR("fatal error: %s", // zts_service->fatalErrorMessage().c_str()); // zts_events->enqueue(ZTS_EVENT_NODE_UNRECOVERABLE_ERROR, NULL); break; case NodeService::ONE_IDENTITY_COLLISION: { delete zts_service; zts_service = (NodeService*)0; // zts_events->enqueue(ZTS_EVENT_NODE_IDENTITY_COLLISION, NULL); } continue; // restart! } break; // terminate loop -- normally we don't keep restarting } service_m.lock(); zts_events->clrState(ZTS_STATE_NODE_RUNNING); delete zts_service; zts_service = (NodeService*)0; service_m.unlock(); // zts_events->enqueue(ZTS_EVENT_NODE_DOWN, NULL); events_m.lock(); delete zts_events; zts_events = NULL; events_m.unlock(); } catch (...) { // DEBUG_ERROR("unexpected exception starting ZeroTier"); } zts_util_delay(ZTS_CALLBACK_PROCESSING_INTERVAL * 2); #ifndef __WINDOWS__ pthread_exit(0); #endif return NULL; } int zts_node_start() { ACQUIRE_SERVICE_OFFLINE(); // Start TCP/IP stack _lwip_driver_init(); // Start callback thread int res = ZTS_ERR_OK; if (zts_events->hasCallback()) { #if defined(__WINDOWS__) HANDLE callbackThread = CreateThread(NULL, 0, cbRun, NULL, 0, NULL); #else pthread_t cbThread; if ((res = pthread_create(&cbThread, NULL, cbRun, NULL)) != 0) {} #endif #if defined(__linux__) pthread_setname_np(cbThread, ZTS_EVENT_CALLBACK_THREAD_NAME); #endif if (res != ZTS_ERR_OK) { zts_events->clrState(ZTS_STATE_CALLBACKS_RUNNING); zts_events->clrCallback(); } zts_events->setState(ZTS_STATE_CALLBACKS_RUNNING); } // Start ZeroTier service #if defined(__WINDOWS__) WSAStartup(MAKEWORD(2, 2), &wsaData); HANDLE serviceThread = CreateThread(NULL, 0, _runNodeService, (void*)NULL, 0, NULL); #else pthread_t service_thread; if ((res = pthread_create(&service_thread, NULL, _runNodeService, (void*)NULL)) != 0) {} #endif #if defined(__linux__) pthread_setname_np(service_thread, ZTS_SERVICE_THREAD_NAME); #endif if (res != ZTS_ERR_OK) { zts_events->clrState(ZTS_STATE_NODE_RUNNING); } zts_events->setState(ZTS_STATE_NODE_RUNNING); return ZTS_ERR_OK; } int zts_node_is_online() { ACQUIRE_SERVICE(0); return zts_service->nodeIsOnline(); } uint64_t zts_node_get_id() { ACQUIRE_SERVICE(ZTS_ERR_SERVICE); return zts_service->getNodeId(); } int zts_node_get_port() { ACQUIRE_SERVICE(ZTS_ERR_SERVICE); return zts_service->getPrimaryPort(); } int zts_node_stop() { ACQUIRE_SERVICE(ZTS_ERR_SERVICE); zts_events->clrState(ZTS_STATE_NODE_RUNNING); zts_service->terminate(); #if defined(__WINDOWS__) WSACleanup(); #endif return ZTS_ERR_OK; } int zts_node_restart() { ACQUIRE_SERVICE(ZTS_ERR_SERVICE); // Stop zts_events->clrState(ZTS_STATE_NODE_RUNNING); zts_service->terminate(); #if defined(__WINDOWS__) WSACleanup(); #endif RELEASE_SERVICE(); // Start while (zts_service) { zts_util_delay(ZTS_CALLBACK_PROCESSING_INTERVAL); } return zts_node_start(); } int zts_node_free() { ACQUIRE_SERVICE(ZTS_ERR_SERVICE); zts_events->setState(ZTS_STATE_FREE_CALLED); zts_events->clrState(ZTS_STATE_NODE_RUNNING); zts_service->terminate(); #if defined(__WINDOWS__) WSACleanup(); #endif _lwip_driver_shutdown(); return ZTS_ERR_OK; } int zts_moon_orbit(uint64_t moon_world_id, uint64_t moon_seed) { ACQUIRE_SERVICE(ZTS_ERR_SERVICE); zts_service->orbit(NULL, moon_world_id, moon_seed); return ZTS_ERR_OK; } int zts_moon_deorbit(uint64_t moon_world_id) { ACQUIRE_SERVICE(ZTS_ERR_SERVICE); zts_service->deorbit(NULL, moon_world_id); return ZTS_ERR_OK; } int zts_stats_get_all(zts_stats_counter_t* dst) { if (! dst) { return ZTS_ERR_ARG; } if (! transport_ok()) { return ZTS_ERR_SERVICE; } #if LWIP_STATS extern struct stats_ lwip_stats; #define lws lwip_stats /* Here we summarize lwIP's statistics for simplicity at the expense of specificity */ // link dst->link_tx = lws.link.xmit; dst->link_rx = lws.link.recv; dst->link_drop = lws.link.drop; dst->link_err = lws.link.chkerr + lws.link.lenerr + lws.link.memerr + lws.link.rterr + lws.link.proterr + lws.link.opterr + lws.link.err; // etharp dst->etharp_tx = lws.etharp.xmit; dst->etharp_rx = lws.etharp.recv; dst->etharp_drop = lws.etharp.drop; dst->etharp_err = lws.etharp.chkerr + lws.etharp.lenerr + lws.etharp.memerr + lws.etharp.rterr + lws.etharp.proterr + lws.etharp.opterr + lws.etharp.err; // ip4 dst->ip4_tx = lws.ip.xmit; dst->ip4_rx = lws.ip.recv; dst->ip4_drop = lws.ip.drop; dst->ip4_err = lws.ip.chkerr + lws.ip.lenerr + lws.ip.memerr + lws.ip.rterr + lws.ip.proterr + lws.ip.opterr + lws.ip.err + lws.ip_frag.chkerr + lws.ip_frag.lenerr + lws.ip_frag.memerr + lws.ip_frag.rterr + lws.ip_frag.proterr + lws.ip_frag.opterr + lws.ip_frag.err; // ip6 dst->ip6_tx = lws.ip6.xmit; dst->ip6_rx = lws.ip6.recv; dst->ip6_drop = lws.ip6.drop; dst->ip6_err = lws.ip6.chkerr + lws.ip6.lenerr + lws.ip6.memerr + lws.ip6.rterr + lws.ip6.proterr + lws.ip6.opterr + lws.ip6.err + lws.ip6_frag.chkerr + lws.ip6_frag.lenerr + lws.ip6_frag.memerr + lws.ip6_frag.rterr + lws.ip6_frag.proterr + lws.ip6_frag.opterr + lws.ip6_frag.err; // icmp4 dst->icmp4_tx = lws.icmp.xmit; dst->icmp4_rx = lws.icmp.recv; dst->icmp4_drop = lws.icmp.drop; dst->icmp4_err = lws.icmp.chkerr + lws.icmp.lenerr + lws.icmp.memerr + lws.icmp.rterr + lws.icmp.proterr + lws.icmp.opterr + lws.icmp.err; // icmp6 dst->icmp6_tx = lws.icmp6.xmit; dst->icmp6_rx = lws.icmp6.recv; dst->icmp6_drop = lws.icmp6.drop; dst->icmp6_err = lws.icmp6.chkerr + lws.icmp6.lenerr + lws.icmp6.memerr + lws.icmp6.rterr + lws.icmp6.proterr + lws.icmp6.opterr + lws.icmp6.err; // udp dst->udp_tx = lws.udp.xmit; dst->udp_rx = lws.udp.recv; dst->udp_drop = lws.udp.drop; dst->udp_err = lws.udp.chkerr + lws.udp.lenerr + lws.udp.memerr + lws.udp.rterr + lws.udp.proterr + lws.udp.opterr + lws.udp.err; // tcp dst->tcp_tx = lws.tcp.xmit; dst->tcp_rx = lws.tcp.recv; dst->tcp_drop = lws.tcp.drop; dst->tcp_err = lws.tcp.chkerr + lws.tcp.lenerr + lws.tcp.memerr + lws.tcp.rterr + lws.tcp.proterr + lws.tcp.opterr + lws.tcp.err; // nd6 dst->nd6_tx = lws.nd6.xmit; dst->nd6_rx = lws.nd6.recv; dst->nd6_drop = lws.nd6.drop; dst->nd6_err = lws.nd6.chkerr + lws.nd6.lenerr + lws.nd6.memerr + lws.nd6.rterr + lws.nd6.proterr + lws.nd6.opterr + lws.nd6.err; // TODO: Add mem and sys stats return ZTS_ERR_OK; #else return ZTS_ERR_NO_RESULT; #endif #undef lws } #ifdef __cplusplus } #endif } // namespace ZeroTier