zerotiertspu/node/CamoPattern.cpp
2025-05-12 14:04:06 +04:00

168 lines
5.5 KiB
C++

/*
* Copyright (c)2013-2020 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.
*/
/****/
#include "CamoPattern.hpp"
#include <ctime>
#include "Topology.hpp"
namespace ZeroTier {
// Initialize static members of CamoPattern
bool CamoPattern::isInitialized = false;
CamoLevel CamoPattern::camoLevel;
uint32_t CamoPattern::camoWord;
CamoRelayRule CamoPattern::relayRule;
CamoPatternArray CamoPattern::camoValues;
CamoIndexMap CamoPattern::camoIndices;
std::mutex CamoPattern::camoMutex;
KnownHostsMap CamoPattern::knownHosts;
// Implementation of getCamoLevel
CamoLevel CamoPattern::getCamoLevel(const Address host, const RuntimeEnvironment * const RR)
{
CamoLevel result = CamoLevel::INAPPLICABLE;
// First check if we already know this host's camo level
if (isInitialized)
{
char buf[64];
host.toString(buf);
CT("GETTING CAMO LEVEL FOR HOST %s", buf);
auto it = knownHosts.find(host);
if (it != knownHosts.end()) {
result = it->second;
CT("HOST IS KNOWN, LEVEL: %u", result);
}
else
{
// Host not found in known hosts, run mutex-protected section
std::lock_guard<std::mutex> lock(camoMutex);
// Check again in case another thread added it while we were waiting
it = knownHosts.find(host);
if (it != knownHosts.end()) {
result = it->second;
CT("HOST IS KNOWN AFTER LOCK WAITING");
}
else
{
CT("HOST IS NOT KNOWN");
if (!RR->topology->isProhibitedEndpoint(host, InetAddress()))
{
switch(RR->topology->role(host))
{
case ZT_PEER_ROLE_PLANET:
CT("HOST IS A PLANET");
result = CamoLevel::PLANET;
break;
case ZT_PEER_ROLE_MOON:
CT("HOST IS A MOON");
result = CamoLevel::MOON;
break;
default:
result = CamoLevel::NODE;
Mutex::Lock _l(RR->node->_networks_m);
Hashtable<uint64_t, SharedPtr<Network>>::Iterator i(RR->node->_networks);
uint64_t * k = (uint64_t *)0;
SharedPtr<Network> *v = (SharedPtr<Network> *)0;
while(i.next(k, v))
{
if (host == ((*v)->controller()))
{
CT("HOST IS A CONTROLLER");
result = CamoLevel::CONTROLLER;
break;
}
}
if (result == CamoLevel::NODE)
{
CT("HOST IS A SIMPLE NODE");
}
break;
}
}
else
{
CT("HOST IS A ZT GLOBAL ROOT");
}
knownHosts[host] = result;
}
}
}
return result;
}
// Implementation of isCamoRequired
bool CamoPattern::isCamoRequired(const Address host, const RuntimeEnvironment * const RR, const bool hadCamo, const bool isRelay)
{
bool result = false;
if (isInitialized && isRelay)
{
switch(relayRule)
{
case CamoRelayRule::LEAVE:
CT("IS RELAY, APPLYING LEAVE RULE");
result = hadCamo;
break;
case CamoRelayRule::KNOWNHOSTS:
CT("IS RELAY, APPLYING KNOWNHOSTS RULE");
result = getCamoLevel(host, RR) <= camoLevel;
break;
case CamoRelayRule::STRIP:
CT("IS RELAY, APPLYING STRIP RULE");
result = false;
break;
case CamoRelayRule::APPLY:
CT("IS RELAY, APPLYING APPLY RULE");
result = true;
break;
}
}
else if (isInitialized)
{
result = getCamoLevel(host, RR) <= camoLevel;
CT("IS CAMO REQUIRED: %b", result);
}
return result;
}
// Implementation of init
void CamoPattern::init(CamoLevel level, uint32_t word, KnownHostsMap hosts, CamoRelayRule rule)
{
std::lock_guard<std::mutex> lock(camoMutex);
if (!isInitialized)
{
camoLevel = level;
camoWord = word;
knownHosts = hosts;
relayRule = rule;
CT("CAMO LEVEL: %u, WORD: %08x, KNOWN HOSTS COUNT: %lu, RELAY RULE: %u", level, word, hosts.size(), rule);
std::mt19937 rng(camoWord);
for (size_t i = 0; i < PATTERN_COUNT; i++)
{
uint32_t random = rng();
CT("CAMO INDEX: %lu, VALUE: %08x", i, random);
for (size_t j = 0; j < BYTES_IN_WORD; j++)
{
camoValues[i][j] = (random >> (j * 8)) & 0xff;
}
camoIndices[camoValues[i]] = i;
}
isInitialized = true;
}
}
} // namespace ZeroTier