/* * 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 #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 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>::Iterator i(RR->node->_networks); uint64_t * k = (uint64_t *)0; SharedPtr *v = (SharedPtr *)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 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