From ab208bb8f9e41343dad3d1e6385ac45aa73b9df0 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Tue, 12 Aug 2025 12:34:54 -0400 Subject: [PATCH] Make encrypted HELLO a local.conf setting -- 99.999999% of users do not need it and it introduces scalability problems on large controllers. --- include/ZeroTierOne.h | 30 +++++++++++++++++++++++++++++- node/Node.cpp | 7 ++++--- node/Node.hpp | 20 ++++++++++++++------ node/Peer.cpp | 2 +- service/OneService.cpp | 7 ++++++- version.h | 2 +- 6 files changed, 55 insertions(+), 13 deletions(-) diff --git a/include/ZeroTierOne.h b/include/ZeroTierOne.h index f0581a525..1f09c99c7 100644 --- a/include/ZeroTierOne.h +++ b/include/ZeroTierOne.h @@ -1745,6 +1745,33 @@ struct ZT_Node_Callbacks { ZT_PathLookupFunction pathLookupFunction; }; +/** + * Node configuration options + */ +struct ZT_Node_Config { + /** + * If non-zero enable encrypted HELLO packets. + * + * This attaches an ephemeral key to HELLO packets and encrypts them. It + * increases CPU usage slightly, which can matter at scale for nodes that + * handle huge numbers of clients like controllers. HELLO packets only + * contain keys and a small amount of meta-data like node version, never + * user data or information about things like network membership, so the + * security impact of this is negligable. ZT1 does not and never has + * guaranteed meta-data privacy, only data privacy. Enable only if you + * need it for compliance reasons. + */ + int enableEncryptedHello; + + /** + * If non-zero enable low bandwidth mode. + * + * This reduces keepalive and path sensing traffic, which can slow fail- + * over but reduces idle bandwidth. Enable in low bandwidth environments. + */ + int lowBandwidthMode; +}; + /** * Create a new ZeroTier node * @@ -1754,13 +1781,14 @@ struct ZT_Node_Callbacks { * to a few seconds depending on your CPU speed. * * @param node Result: pointer is set to new node instance on success + * @param config Node-wide configuration options set on startup * @param uptr User pointer to pass to functions/callbacks * @param tptr Thread pointer to pass to functions/callbacks resulting from this call * @param callbacks Callback function configuration * @param now Current clock in milliseconds * @return OK (0) or error code if a fatal error condition has occurred */ -ZT_SDK_API enum ZT_ResultCode ZT_Node_new(ZT_Node** node, void* uptr, void* tptr, const struct ZT_Node_Callbacks* callbacks, int64_t now); +ZT_SDK_API enum ZT_ResultCode ZT_Node_new(ZT_Node** node, const struct ZT_Node_Config* config, void* uptr, void* tptr, const struct ZT_Node_Callbacks* callbacks, int64_t now); /** * Delete a node and free all resources it consumes diff --git a/node/Node.cpp b/node/Node.cpp index 4f7a1d618..f704ff09a 100644 --- a/node/Node.cpp +++ b/node/Node.cpp @@ -43,7 +43,7 @@ namespace ZeroTier { /* Public Node interface (C++, exposed via CAPI bindings) */ /****************************************************************************/ -Node::Node(void* uptr, void* tptr, const struct ZT_Node_Callbacks* callbacks, int64_t now) +Node::Node(void* uptr, void* tptr, const struct ZT_Node_Config* config, const struct ZT_Node_Callbacks* callbacks, int64_t now) : _RR(this) , RR(&_RR) , _uPtr(uptr) @@ -59,6 +59,7 @@ Node::Node(void* uptr, void* tptr, const struct ZT_Node_Callbacks* callbacks, in throw ZT_EXCEPTION_INVALID_ARGUMENT; } memcpy(&_cb, callbacks, sizeof(ZT_Node_Callbacks)); + memcpy(&_config, config, sizeof(ZT_Node_Config)); // Initialize non-cryptographic PRNG from a good random source Utils::getSecureRandom((void*)_prngState, sizeof(_prngState)); @@ -918,11 +919,11 @@ void Node::ncSendError(uint64_t nwid, uint64_t requestPacketId, const Address& d extern "C" { -enum ZT_ResultCode ZT_Node_new(ZT_Node** node, void* uptr, void* tptr, const struct ZT_Node_Callbacks* callbacks, int64_t now) +enum ZT_ResultCode ZT_Node_new(ZT_Node** node, const struct ZT_Node_Config* config, void* uptr, void* tptr, const struct ZT_Node_Callbacks* callbacks, int64_t now) { *node = (ZT_Node*)0; try { - *node = reinterpret_cast(new ZeroTier::Node(uptr, tptr, callbacks, now)); + *node = reinterpret_cast(new ZeroTier::Node(uptr, tptr, config, callbacks, now)); return ZT_RESULT_OK; } catch (std::bad_alloc& exc) { diff --git a/node/Node.hpp b/node/Node.hpp index c659b1649..8ea09f551 100644 --- a/node/Node.hpp +++ b/node/Node.hpp @@ -20,13 +20,10 @@ #include "NetworkController.hpp" #include "Path.hpp" #include "RuntimeEnvironment.hpp" -#include "Salsa20.hpp" #include "SelfAwareness.hpp" -#include #include #include -#include #include // Bit mask for "expecting reply" hash @@ -44,7 +41,7 @@ class World; */ class Node : public NetworkController::Sender { public: - Node(void* uptr, void* tptr, const struct ZT_Node_Callbacks* callbacks, int64_t now); + Node(void* uptr, void* tptr, const struct ZT_Node_Config* config, const struct ZT_Node_Callbacks* callbacks, int64_t now); virtual ~Node(); // Get rid of alignment warnings on 32-bit Windows and possibly improve performance @@ -285,12 +282,22 @@ class Node : public NetworkController::Sender { inline void setLowBandwidthMode(bool isEnabled) { - _lowBandwidthMode = isEnabled; + _config.lowBandwidthMode = (int)isEnabled; + } + + inline void setEncryptedHelloEnabled(bool isEnabled) + { + _config.enableEncryptedHello = (int)isEnabled; } inline bool lowBandwidthModeEnabled() { - return _lowBandwidthMode; + return _config.lowBandwidthMode != 0; + } + + inline bool encryptedHelloEnabled() + { + return _config.enableEncryptedHello != 0; } void initMultithreading(unsigned int concurrency, bool cpuPinningEnabled); @@ -300,6 +307,7 @@ class Node : public NetworkController::Sender { RuntimeEnvironment* RR; void* _uPtr; // _uptr (lower case) is reserved in Visual Studio :P ZT_Node_Callbacks _cb; + ZT_Node_Config _config; // For tracking packet IDs to filter out OK/ERROR replies to packets we did not send uint8_t _expectingRepliesToBucketPtr[ZT_EXPECTING_REPLIES_BUCKET_MASK1 + 1]; diff --git a/node/Peer.cpp b/node/Peer.cpp index 9d44cf951..65199badb 100644 --- a/node/Peer.cpp +++ b/node/Peer.cpp @@ -459,7 +459,7 @@ void Peer::sendHELLO(void* tPtr, const int64_t localSocket, const InetAddress& a Metrics::pkt_hello_out++; if (atAddress) { - outp.armor(_key, false, true, nullptr, _id); + outp.armor(_key, false, RR->node->encryptedHelloEnabled(), nullptr, _id); RR->node->expectReplyTo(outp.packetId()); RR->node->putPacket(tPtr, RR->node->lowBandwidthModeEnabled() ? localSocket : -1, atAddress, outp.data(), outp.size()); } diff --git a/service/OneService.cpp b/service/OneService.cpp index 464c3eefe..206fe6e7a 100644 --- a/service/OneService.cpp +++ b/service/OneService.cpp @@ -1158,7 +1158,11 @@ class OneServiceImpl : public OneService { cb.eventCallback = SnodeEventCallback; cb.pathCheckFunction = SnodePathCheckFunction; cb.pathLookupFunction = SnodePathLookupFunction; - _node = new Node(this, (void*)0, &cb, OSUtils::now()); + // These settings can get set later when local.conf is checked. + struct ZT_Node_Config config; + config.enableEncryptedHello = 0; + config.lowBandwidthMode = 0; + _node = new Node(this, (void*)0, &config, &cb, OSUtils::now()); } // local.conf @@ -2880,6 +2884,7 @@ class OneServiceImpl : public OneService { fprintf(stderr, "WARNING: using manually-specified secondary and/or tertiary ports. This can cause NAT issues." ZT_EOL_S); } _portMappingEnabled = OSUtils::jsonBool(settings["portMappingEnabled"], true); + _node->setEncryptedHelloEnabled(OSUtils::jsonBool(settings["encryptedHelloEnabled"], false)); _node->setLowBandwidthMode(OSUtils::jsonBool(settings["lowBandwidthMode"], false)); #if defined(__LINUX__) || defined(__FreeBSD__) _multicoreEnabled = OSUtils::jsonBool(settings["multicoreEnabled"], false); diff --git a/version.h b/version.h index 9c417f5b7..846a14e77 100644 --- a/version.h +++ b/version.h @@ -27,7 +27,7 @@ /** * Revision */ -#define ZEROTIER_ONE_VERSION_REVISION 2 +#define ZEROTIER_ONE_VERSION_REVISION 3 /** * Build version