Make encrypted HELLO a local.conf setting -- 99.999999% of users do not need it and it introduces scalability problems on large controllers.
Some checks are pending
/ build_macos (push) Waiting to run
/ build_windows (push) Waiting to run
/ build_ubuntu (push) Waiting to run

This commit is contained in:
Adam Ierymenko 2025-08-12 12:34:54 -04:00
commit ab208bb8f9
No known key found for this signature in database
GPG key ID: C8877CF2D7A5D7F3
6 changed files with 55 additions and 13 deletions

View file

@ -1745,6 +1745,33 @@ struct ZT_Node_Callbacks {
ZT_PathLookupFunction pathLookupFunction; 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 * Create a new ZeroTier node
* *
@ -1754,13 +1781,14 @@ struct ZT_Node_Callbacks {
* to a few seconds depending on your CPU speed. * to a few seconds depending on your CPU speed.
* *
* @param node Result: pointer is set to new node instance on success * @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 uptr User pointer to pass to functions/callbacks
* @param tptr Thread pointer to pass to functions/callbacks resulting from this call * @param tptr Thread pointer to pass to functions/callbacks resulting from this call
* @param callbacks Callback function configuration * @param callbacks Callback function configuration
* @param now Current clock in milliseconds * @param now Current clock in milliseconds
* @return OK (0) or error code if a fatal error condition has occurred * @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 * Delete a node and free all resources it consumes

View file

@ -43,7 +43,7 @@ namespace ZeroTier {
/* Public Node interface (C++, exposed via CAPI bindings) */ /* 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(this)
, RR(&_RR) , RR(&_RR)
, _uPtr(uptr) , _uPtr(uptr)
@ -59,6 +59,7 @@ Node::Node(void* uptr, void* tptr, const struct ZT_Node_Callbacks* callbacks, in
throw ZT_EXCEPTION_INVALID_ARGUMENT; throw ZT_EXCEPTION_INVALID_ARGUMENT;
} }
memcpy(&_cb, callbacks, sizeof(ZT_Node_Callbacks)); memcpy(&_cb, callbacks, sizeof(ZT_Node_Callbacks));
memcpy(&_config, config, sizeof(ZT_Node_Config));
// Initialize non-cryptographic PRNG from a good random source // Initialize non-cryptographic PRNG from a good random source
Utils::getSecureRandom((void*)_prngState, sizeof(_prngState)); Utils::getSecureRandom((void*)_prngState, sizeof(_prngState));
@ -918,11 +919,11 @@ void Node::ncSendError(uint64_t nwid, uint64_t requestPacketId, const Address& d
extern "C" { 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; *node = (ZT_Node*)0;
try { try {
*node = reinterpret_cast<ZT_Node*>(new ZeroTier::Node(uptr, tptr, callbacks, now)); *node = reinterpret_cast<ZT_Node*>(new ZeroTier::Node(uptr, tptr, config, callbacks, now));
return ZT_RESULT_OK; return ZT_RESULT_OK;
} }
catch (std::bad_alloc& exc) { catch (std::bad_alloc& exc) {

View file

@ -20,13 +20,10 @@
#include "NetworkController.hpp" #include "NetworkController.hpp"
#include "Path.hpp" #include "Path.hpp"
#include "RuntimeEnvironment.hpp" #include "RuntimeEnvironment.hpp"
#include "Salsa20.hpp"
#include "SelfAwareness.hpp" #include "SelfAwareness.hpp"
#include <map>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h>
#include <vector> #include <vector>
// Bit mask for "expecting reply" hash // Bit mask for "expecting reply" hash
@ -44,7 +41,7 @@ class World;
*/ */
class Node : public NetworkController::Sender { class Node : public NetworkController::Sender {
public: 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(); virtual ~Node();
// Get rid of alignment warnings on 32-bit Windows and possibly improve performance // 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) inline void setLowBandwidthMode(bool isEnabled)
{ {
_lowBandwidthMode = isEnabled; _config.lowBandwidthMode = (int)isEnabled;
}
inline void setEncryptedHelloEnabled(bool isEnabled)
{
_config.enableEncryptedHello = (int)isEnabled;
} }
inline bool lowBandwidthModeEnabled() inline bool lowBandwidthModeEnabled()
{ {
return _lowBandwidthMode; return _config.lowBandwidthMode != 0;
}
inline bool encryptedHelloEnabled()
{
return _config.enableEncryptedHello != 0;
} }
void initMultithreading(unsigned int concurrency, bool cpuPinningEnabled); void initMultithreading(unsigned int concurrency, bool cpuPinningEnabled);
@ -300,6 +307,7 @@ class Node : public NetworkController::Sender {
RuntimeEnvironment* RR; RuntimeEnvironment* RR;
void* _uPtr; // _uptr (lower case) is reserved in Visual Studio :P void* _uPtr; // _uptr (lower case) is reserved in Visual Studio :P
ZT_Node_Callbacks _cb; ZT_Node_Callbacks _cb;
ZT_Node_Config _config;
// For tracking packet IDs to filter out OK/ERROR replies to packets we did not send // 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]; uint8_t _expectingRepliesToBucketPtr[ZT_EXPECTING_REPLIES_BUCKET_MASK1 + 1];

View file

@ -459,7 +459,7 @@ void Peer::sendHELLO(void* tPtr, const int64_t localSocket, const InetAddress& a
Metrics::pkt_hello_out++; Metrics::pkt_hello_out++;
if (atAddress) { 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->expectReplyTo(outp.packetId());
RR->node->putPacket(tPtr, RR->node->lowBandwidthModeEnabled() ? localSocket : -1, atAddress, outp.data(), outp.size()); RR->node->putPacket(tPtr, RR->node->lowBandwidthModeEnabled() ? localSocket : -1, atAddress, outp.data(), outp.size());
} }

View file

@ -1158,7 +1158,11 @@ class OneServiceImpl : public OneService {
cb.eventCallback = SnodeEventCallback; cb.eventCallback = SnodeEventCallback;
cb.pathCheckFunction = SnodePathCheckFunction; cb.pathCheckFunction = SnodePathCheckFunction;
cb.pathLookupFunction = SnodePathLookupFunction; 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 // 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); 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); _portMappingEnabled = OSUtils::jsonBool(settings["portMappingEnabled"], true);
_node->setEncryptedHelloEnabled(OSUtils::jsonBool(settings["encryptedHelloEnabled"], false));
_node->setLowBandwidthMode(OSUtils::jsonBool(settings["lowBandwidthMode"], false)); _node->setLowBandwidthMode(OSUtils::jsonBool(settings["lowBandwidthMode"], false));
#if defined(__LINUX__) || defined(__FreeBSD__) #if defined(__LINUX__) || defined(__FreeBSD__)
_multicoreEnabled = OSUtils::jsonBool(settings["multicoreEnabled"], false); _multicoreEnabled = OSUtils::jsonBool(settings["multicoreEnabled"], false);

View file

@ -27,7 +27,7 @@
/** /**
* Revision * Revision
*/ */
#define ZEROTIER_ONE_VERSION_REVISION 2 #define ZEROTIER_ONE_VERSION_REVISION 3
/** /**
* Build version * Build version