changed camouflaging scheme

This commit is contained in:
eerieaerial 2025-05-14 22:26:08 +04:00
parent 235addc585
commit 98ccedecac
5 changed files with 235 additions and 319 deletions

View file

@ -6,58 +6,44 @@
## Принцип работы
### Узоры камуфлирования
Основу механизма камуфлирования составляют 16 случайных 32-битных узоров, которые:
- Генерируются при инициализации стандартизованным ГСЧ (вихрь Мерсенна MT19937, определённый в стандарте C++11) с фиксированным порождающим словом
- Одинаковы для всех узлов с одинаковым порождающим словом
- Используются для XOR-преобразования содержимого пакета
- Служат признаком распознавания камуфлированных пакетов
Размер списка в 16 узоров выбран из соображений баланса между разнообразием узоров и вероятностью коллизии между признаком камуфлирования и случайно сгенерированным ID некамуфлированного пакета. Размер списка можно изменить путём задания нового значения макроса `PATTERN_COUNT` в файле `CamoPattern.hpp`. Рекомендуется использовать значения, равные степеням 2.
### Алгоритм камуфлирования
1. 64-битный ID в заголовке пакета разделяется на два 32-битных сегмента
2. Младший сегмент перемещается в конец пакета
3. Старший сегмент дублируется на место младшего
4. Данные от дубликата до конца пакета обрабатываются XOR со случайно выбранным узором
5. Раскамуфлирование выполняется в обратном порядке после распознавания узора
1. 64-битный ID в заголовке пакета дублируется в конец пакета
2. Данные от дубликата до конца пакета обрабатываются XOR со случайным 32-битным числом ("камуфляжем")
3. Раскамуфлирование выполняется в обратном порядке после распознавания камуфляжа
### Распознавание камуфлированных пакетов
### Распознавание камуфляжа
Для распознавания того, камуфлирован пакет или нет, используется XOR между старшим и младшим сегментами ID. Если результат обнаружен в списке узоров, то это означает, что пакет закамуфлирован этим узором.
Для распознавания того, камуфлирован пакет или нет, используется равенство `a ^ b = x ^ y`, где `a` и `b` - старший и младший 32-битные сегменты ID, `x` и `y` - старший и младший 32-битные сегменты последних 64 бит пакета. Если равенство соблюдается, то это означает, что пакет камуфлирован, значение камуфляжа определяется формулой `c = a ^ x`.
### Правила обработки пакетов
Пакеты обрабатываются на основе сравнения уровня камуфлирования адресата и отправителя. Уровень адресата определяется либо автоматически, либо заданием в настройках. Уровень отправителя определяется настройкой и относится только непосредственно к тому узлу, на котором производится обработка пакета в данный момент.
Пакеты обрабатываются на основе класса адресата и заданной настройки включения камуфлирования для каждого из классов. Класс адресата присваивается ему либо автоматически, либо заданием в настройках.
- _Входящие_: всегда автоматически раскамуфлируются для обеспечения работы внутренних механизмов
- сходящие_: камуфлируются, если уровень адресата равен уровню отправителя или меньше
- сходящие_: камуфлируются, если камуфлирование включено для класса адресата
- ересылаемые_: обрабатываются согласно выбранному правилу пересылки
### Уровни камуфлирования
### Классы камуфлирования
Уровни камуфлирования имеют следующие названия от низшего к высшему:
Классы камуфлирования имеют следующие названия:
- `NONE`
- `NODE`
- `CONTROLLER`
- `MOON`
- `PLANET`
- `INAPPLICABLE`
- `ALWAYS`
- `NEVER`
Особое значение имеют два уровня:
Особое значение имеют два класса:
- `NONE` - не назначается автоматически в качестве уровня адресата. Выбор этого уровня в качестве уровня отправителя выключает камуфлирование пакетов при автоматическом выборе уровня адресата.
- `INAPPLICABLE` - в качестве уровня адресата назначается автоматически глобальным корневым серверам. Для выбора в качестве уровня отправителя недоступен.
- `ALWAYS` - не присваивается адресату автоматически. Ручное присвоение этого класса адресату включает безусловное камуфлирование пакетов для него.
- `NEVER` - в качестве класса адресата присваивается автоматически глобальным корневым серверам (планетам). Ручное присвоение этого класса адресату безусловно выключает камуфлирование пакетов для него.
Прочие уровни назначаются автоматически согласно роли адресата.
Прочие классы назначаются автоматически согласно роли адресата.
### Список известных адресатов
При определении уровня камуфлирования для адресата проводится поиск в списке известных адресатов. Если адресат там обнаружен, то используется уровень, который присвоен в списке. Если нет - то производится автоматическое определение уровня адресата и результат заносится в список. Настройки позволяют задать изначальное содержимое списка для принудительного назначения адресатам требуемого уровня.
При определении класса адресата проводится поиск в списке известных адресатов. Если адресат там обнаружен, то используется класс, который присвоен в списке. Если нет - то производится автоматическое определение класса адресата и результат заносится в список. Настройки позволяют задать изначальное содержимое списка для принудительного назначения адресатам требуемого класса.
### Правила пересылки
@ -74,11 +60,16 @@
{
"settings": {
"camo": {
"level": "none", // Уровень отправителя
"word": "DLGE", // Порождающее слово для узоров
"autoApply": [ // Включает камуфлирование для перечисленных классов
"node",
"controller",
"moon"
],
"relayRule": "leave", // Правило пересылки
"knownHosts": { // Предустановленные уровни адресата для узлов
"none" : []
"knownHosts": { // Предустановленные классы адресата для узлов
"never" : [
"aaaaaaaaaa"
]
}
}
}
@ -87,10 +78,9 @@
### Параметры
- `"level"`: регистронезависимая строка с названием уровня отправителя. По умолчанию: `"none"`. Любые значения, не соответствующие названию существующих уровней, интерпретируются как `"none"`. Значение `"inapplicable"` также интерпретируется как `"none"`, т.к. этот уровень недоступен для выбора.
- `"word"`: строка, содержащая порождающее слово для списка узоров. Если в строке меньше 4 символов, она дополняется до 4 символов последовательностью `"DLGE"`. Строка длиннее 4 символов обрезается. Если строка начинается с `"0x"`, она интерпретируется как 32-битное шестнадцатеричное число. По умолчанию: `"DLGE"`.
- `"autoApply"`: массив регистронезависимых строк с названиями классов адресата, для которых включается автоматическое камуфлирование. По умолчанию: `[]`. Допустимо задание строк `node`, `controller` и `moon`, прочие значения игнорируются.
- `"relayRule"`: регистронезависимая строка, содержащая название правила пересылки. По умолчанию: `"leave"`. Любые значения, не соответствующие названию существующих правил, интерпретируются как `"leave"`.
- `"knownHosts"`: объект для предустановки уровней для конкретных узлов. Названия полей объекта соответствуют уровням камуфлирования. Названия полей, не соответствующие существующим уровням, интерпретируются как `"inapplicable"`. Значение каждого поля - массив строк с адресами.
- `"knownHosts"`: объект для предустановки классов адресата для конкретных узлов. Названия полей объекта регистронезависимы и соответствуют классам адресатов. Названия полей, не соответствующие существующим классам, интерпретируются как `"never"`. Значение каждого поля - массив строк с адресами. При включении одного и того же адреса в несколько разных классов, адресату будет назначен класс, идущий последним.
## Примеры конфигураций
@ -100,16 +90,16 @@
{
"settings": {
"camo": {
"level": "none",
"autoApply": [],
"knownHosts": {
"none": ["aaaaaaaaaa"]
"always": ["aaaaaaaaaa"]
}
}
}
}
```
Эта настройка выбирает уровень отправителя `NONE`, который автоматически не назначается адресатам. Это отключает камуфлирование для всех автоматически определяемых адресатов. Адресату `aaaaaaaaaa` принудительно назначается уровень `NONE`, и т.к. этот уровень равен выбранному, пакеты для него будут камуфлироваться.
Эта настройка отключает автоматическое камуфлирование. При этом, адресату `aaaaaaaaaa` принудительно назначается класс `ALWAYS`, и поэтому пакеты для него (и только для него) будут камуфлироваться.
### "Чёрный список"
@ -117,16 +107,16 @@
{
"settings": {
"camo": {
"level": "node",
"autoApply": [ "node" ],
"knownHosts": {
"inapplicable": ["aaaaaaaaaa"]
"never": ["aaaaaaaaaa"]
}
}
}
}
```
Здесь выбран уровень отправителя `NODE`, что включает камуфлирование для адресатов этого уровня и ниже. Адресату `aaaaaaaaaa` назначен уровень `INAPPLICABLE`, поэтому вне зависимости от выбранного уровня, пакеты для него камуфлироваться не будут.
Здесь включается автоматическое камуфлирование для обычных узлов. Узлу `aaaaaaaaaa` назначен класс `NEVER`, и вне зависимости от настроек автоматического камуфлирования, пакеты для него камуфлироваться не будут.
### Принудительное включение для специфических узлов
@ -134,7 +124,7 @@
{
"settings": {
"camo": {
"level": "node",
"level": [ "node" ],
"knownHosts": {
"node": ["aaaaaaaaaa"]
}
@ -143,11 +133,11 @@
}
```
В этом примере корневому серверу `aaaaaaaaaa`, поддерживающему работу с камуфлированными пакетами, принудительно присваивается уровень `NODE`, чтобы пакеты для него камуфлировались наряду с обычными узлами.
В этом примере корневому серверу `aaaaaaaaaa`, поддерживающему работу с камуфлированными пакетами, принудительно присваивается класс `NODE`, чтобы пакеты для него камуфлировались наряду с обычными узлами.
### Комментарий
По большому счёту, в `"knownHosts"` имеет смысл использовать только уровни `NONE` и `INAPPLICABLE`. Первый - принудительно включает камуфлирование для указанных адресатов, второй - выключает.
По большому счёту, в `"knownHosts"` имеет смысл использовать только классы `ALWAYS` и `NEVER`, т.к. в этом случае поведение не будет зависеть от настоек автоматического камуфлирования.
## Перевод сети на камуфлирование

View file

@ -9,39 +9,34 @@
* 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 "RuntimeEnvironment.hpp"
#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;
CamoAutoApplyBits CamoPattern::camoAutoApply;
std::mt19937 CamoPattern::rng(std::random_device{}());
// Implementation of getCamoLevel
CamoLevel CamoPattern::getCamoLevel(const Address host, const RuntimeEnvironment * const RR)
CamoClass CamoPattern::getCamoClass(const Address host, const RuntimeEnvironment * const RR)
{
CamoLevel result = CamoLevel::INAPPLICABLE;
// First check if we already know this host's camo level
CamoClass result = CamoClass::NEVER;
if (isInitialized)
{
char buf[64];
host.toString(buf);
CT("GETTING CAMO LEVEL FOR HOST %s", buf);
CT("GETTING CAMO CLASS FOR HOST %s", buf);
auto it = knownHosts.find(host);
if (it != knownHosts.end()) {
result = it->second;
CT("HOST IS KNOWN, LEVEL: %u", result);
CT("HOST IS KNOWN, CLASS: %u", result);
}
else
{
@ -52,25 +47,22 @@ CamoLevel CamoPattern::getCamoLevel(const Address host, const RuntimeEnvironment
it = knownHosts.find(host);
if (it != knownHosts.end()) {
result = it->second;
CT("HOST IS KNOWN AFTER LOCK WAITING");
CT("HOST IS KNOWN AFTER LOCK WAITING, CLASS: %u", result);
}
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;
result = CamoClass::MOON;
break;
default:
result = CamoLevel::NODE;
result = CamoClass::NODE;
Mutex::Lock _l(RR->node->_networks_m);
Hashtable<uint64_t, SharedPtr<Network>>::Iterator i(RR->node->_networks);
uint64_t * k = (uint64_t *)0;
@ -80,21 +72,16 @@ CamoLevel CamoPattern::getCamoLevel(const Address host, const RuntimeEnvironment
if (host == ((*v)->controller()))
{
CT("HOST IS A CONTROLLER");
result = CamoLevel::CONTROLLER;
result = CamoClass::CONTROLLER;
break;
}
}
if (result == CamoLevel::NODE)
if (result == CamoClass::NODE)
{
CT("HOST IS A SIMPLE NODE");
}
break;
}
}
else
{
CT("HOST IS A ZT GLOBAL ROOT");
}
knownHosts[host] = result;
}
}
@ -102,10 +89,18 @@ CamoLevel CamoPattern::getCamoLevel(const Address host, const RuntimeEnvironment
return result;
}
// Implementation of isCamoRequired
// Implementation of isCamoRequired - determines if camouflage should be applied based on host and rules
bool CamoPattern::isCamoRequired(const Address host, const RuntimeEnvironment * const RR, const bool hadCamo, const bool isRelay)
{
bool result = false;
auto isRequiredByClass = [](const Address host, const RuntimeEnvironment * const RR) -> bool {
CamoClass camoClass = getCamoClass(host, RR);
return camoClass < CamoClass::AUTO_APPLY_COUNT ?
camoAutoApply[camoClass] :
camoClass == CamoClass::ALWAYS;
};
if (isInitialized && isRelay)
{
switch(relayRule)
@ -116,7 +111,7 @@ bool CamoPattern::isCamoRequired(const Address host, const RuntimeEnvironment *
break;
case CamoRelayRule::KNOWNHOSTS:
CT("IS RELAY, APPLYING KNOWNHOSTS RULE");
result = getCamoLevel(host, RR) <= camoLevel;
result = isRequiredByClass(host, RR);
break;
case CamoRelayRule::STRIP:
CT("IS RELAY, APPLYING STRIP RULE");
@ -130,39 +125,24 @@ bool CamoPattern::isCamoRequired(const Address host, const RuntimeEnvironment *
}
else if (isInitialized)
{
result = getCamoLevel(host, RR) <= camoLevel;
result = isRequiredByClass(host, RR);
CT("IS CAMO REQUIRED: %b", result);
}
return result;
}
// Implementation of init
void CamoPattern::init(CamoLevel level, uint32_t word, KnownHostsMap hosts, CamoRelayRule rule)
// Implementation of init - initializes the camouflage system with the specified settings
void CamoPattern::init(CamoAutoApplyBits autoApply, KnownHostsMap hosts, CamoRelayRule rule)
{
std::lock_guard<std::mutex> lock(camoMutex);
if (!isInitialized)
{
camoLevel = level;
camoWord = word;
camoAutoApply = autoApply;
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;
}
CT("KNOWN HOSTS COUNT: %lu, RELAY RULE: %u", hosts.size(), rule);
isInitialized = true;
}
}
} // namespace ZeroTier

View file

@ -9,7 +9,6 @@
* 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.
*/
/****/
#ifndef ZT_N_CAMOPATTERN_HPP
#define ZT_N_CAMOPATTERN_HPP
@ -19,19 +18,18 @@
#include <unordered_map>
#include <mutex>
#include <random>
#include <bitset>
#include "Address.hpp"
#include "Buffer.hpp"
#include "RuntimeEnvironment.hpp"
#define BYTES_IN_WORD (sizeof(uint32_t) / sizeof(uint8_t))
#define PATTERN_COUNT 16
#define NO_CAMO PATTERN_COUNT
#define ZT_CAMO_DEFAULT_WORDSTR "DLGE"
#define ID_SIZE (BYTES_IN_WORD * 2)
/**
* Camo functions debug trace macro
* Enables debug output when CAMO_TRACE is defined
*/
#ifdef CAMO_TRACE
#define CT(...) do { \
@ -43,35 +41,29 @@
#define CT(...) ((void)0)
#endif
/**
* Type shorthands
*/
namespace ZeroTier {
enum class CamoLevel;
/**
* Camouflage class enum
*/
enum CamoClass {
NODE,
CONTROLLER,
MOON,
AUTO_APPLY_COUNT,
ALWAYS = AUTO_APPLY_COUNT,
NEVER
};
typedef std::bitset<CamoClass::AUTO_APPLY_COUNT> CamoAutoApplyBits;
typedef std::array<uint8_t, BYTES_IN_WORD> CamoPatternBytes;
typedef std::array<CamoPatternBytes, PATTERN_COUNT> CamoPatternArray;
typedef std::unordered_map<CamoPatternBytes, size_t> CamoIndexMap;
typedef std::unordered_map<Address, CamoLevel> KnownHostsMap;
typedef std::unordered_map<Address, CamoClass> KnownHostsMap;
}
/**
* Hash functions for the respective unordered_maps
* Hash function for the KnownHostsMap
*/
namespace std {
template<>
struct hash<ZeroTier::CamoPatternBytes> {
size_t operator()(const ZeroTier::CamoPatternBytes& bytes) const {
uint32_t word = 0;
for (const auto& byte : bytes) {
word <<= 8;
word |= byte;
}
return std::hash<uint32_t>{}(word);
}
};
template<>
struct hash<ZeroTier::Address> {
size_t operator()(const ZeroTier::Address& address) const {
@ -82,18 +74,6 @@ namespace std {
namespace ZeroTier {
/**
* Camouflage level enum
*/
enum class CamoLevel {
NONE,
NODE,
CONTROLLER,
MOON,
PLANET,
INAPPLICABLE
};
enum class CamoRelayRule {
LEAVE,
KNOWNHOSTS,
@ -107,146 +87,153 @@ enum class CamoRelayRule {
class CamoPattern
{
/**
* Check if buffer has camouflage applied
* Check if the buffer has camo
*
* @param buffer Buffer to check
* @return True if buffer has camouflage
* @return True if the buffer has camo
*/
template<unsigned int C>
static size_t getCamoIndex(const Buffer<C> &buffer)
static bool hasCamo(const Buffer<C> &buffer)
{
size_t result = NO_CAMO;
CamoPatternBytes camo;
if (buffer.size() > BYTES_IN_WORD * 2)
bool result = false;
if (buffer.size() > (ID_SIZE * 2))
{
size_t a = 0;
size_t b = BYTES_IN_WORD;
size_t x = buffer.size() - ID_SIZE;
size_t y = buffer.size() - BYTES_IN_WORD;
result = true;
for (size_t i = 0; i < BYTES_IN_WORD; i++)
{
camo[i] = buffer[i] ^ buffer[i + BYTES_IN_WORD];
}
auto it = camoIndices.find(camo);
if (it != camoIndices.end())
if ((buffer[a + i] ^ buffer[b + i]) != (buffer[x + i] ^ buffer [y + i]))
{
result = it->second;
CT("CAMO DETECTED, INDEX: %u", result);
}
else
{
CT("CAMO NOT DETECTED");
result = false;
break;
}
}
}
CT("PACKET HAS CAMO: %b", result);
return result;
}
/**
* Check the host camo level
* Check the host camo class
*
* @param host Destination address
* @param RR RuntimeEnvironment pointer
* @return Camo Level for this host
* @return Camo class for this host
*/
static CamoLevel getCamoLevel(const Address host, const RuntimeEnvironment * const RR);
static CamoClass getCamoClass(const Address host, const RuntimeEnvironment * const RR);
public:
/**
* Determine if camouflage is required for a specific host
*
* @param host Destination address
* @param RR RuntimeEnvironment pointer
* @return True if host requires camo
* @param hadCamo Whether the packet previously had camouflage applied
* @param isRelay Whether this is a relay operation
* @return True if host requires camouflage
*/
static bool isCamoRequired(const Address host, const RuntimeEnvironment * const RR, const bool hadCamo = false, const bool isRelay = false);
/**
* Apply camouflage to buffer
*
* This increases buffer length by adding ID word to the end
* This increases buffer length by adding ID to the end
*
* @param buffer Buffer to apply camouflage to
*/
template<unsigned int C>
static void applyCamo(Buffer<C> &buffer)
{
size_t camoIndex = getCamoIndex(buffer);
if (isInitialized && (camoIndex == NO_CAMO)) { // Only apply if not already applied
CT("PACKET CONTENTS BEFORE APPLYING CAMO:");
buffer.dump();
camoIndex = std::minstd_rand(std::time(nullptr))() % PATTERN_COUNT;
CamoPatternBytes camo = camoValues[camoIndex];
CT("APPLYING CAMO");
if (isInitialized && !hasCamo(buffer))
{
// load random number into an array
uint32_t camo = rng();
CamoPatternBytes camoBytes;
for (size_t i = 0; i < BYTES_IN_WORD; i++)
{
camoBytes[i] = camo >> 24;
CT("CAMO BYTE %u: %02x", i, camoBytes[i]);
camo <<= 8;
}
// Increase buffer size first to avoid overflow
//camouflage all the data
uint8_t * const data = reinterpret_cast<uint8_t * const>(buffer.unsafeData());
for (size_t i = ID_SIZE; i < buffer.size(); i++)
{
data[i] ^= camoBytes[i % BYTES_IN_WORD];
}
// expand the buffer
size_t originalSize = buffer.size();
buffer.setSize(originalSize + BYTES_IN_WORD);
uint8_t * const data = reinterpret_cast<uint8_t *>(buffer.unsafeData());
// Copy the second word to the end
for (size_t i = 0; i < BYTES_IN_WORD; i++) {
data[i + originalSize] = data[i + BYTES_IN_WORD];
buffer.setSize(originalSize + ID_SIZE);
//copy the id
for (size_t i = 0; i < ID_SIZE; i++)
{
data[i + originalSize] = data[i] ^ camoBytes[i % BYTES_IN_WORD];
}
// Copy the first word on the second word's place
for (size_t i = 0; i < BYTES_IN_WORD; i++) {
data[i + BYTES_IN_WORD] = data[i];
}
// Apply XOR to the rest of the buffer
for (size_t i = BYTES_IN_WORD; i < buffer.size(); i++) {
data[i] ^= camo[i % BYTES_IN_WORD];
}
CT("PACKET CONTENTS AFTER APPLYING CAMO:");
buffer.dump();
CT("DONE");
}
}
/**
* Remove camouflage from buffer
*
* This decreases buffer length by removing stored ID word from the end
* This decreases buffer length by removing stored ID from the end
*
* @param buffer Buffer to remove camouflage from
* @return True if buffer had camouflage and it was stripped
*/
template<unsigned int C>
static bool stripCamo(Buffer<C> &buffer)
{
bool result = false;
size_t camoIndex = NO_CAMO;
if (isInitialized)
{
camoIndex = getCamoIndex(buffer);
}
if (camoIndex != NO_CAMO) {
CT("PACKET CONTENTS BEFORE STRIPPING CAMO:");
buffer.dump();
uint8_t * const data = reinterpret_cast<uint8_t *>(buffer.unsafeData());
CamoPatternBytes camo = camoValues[camoIndex];
for (size_t i = BYTES_IN_WORD * 2; i < buffer.size(); i++)
{
data[i] ^= camo[i % BYTES_IN_WORD];
}
size_t storedWordIndex = buffer.size() - BYTES_IN_WORD;
if (isInitialized && hasCamo(buffer)) {
//retrieve the camo bytes
uint8_t * a = &buffer[0];
uint8_t * x = &buffer[buffer.size() - ID_SIZE];
CamoPatternBytes camoBytes;
for (size_t i = 0; i < BYTES_IN_WORD; i++)
{
data[i + BYTES_IN_WORD] = data[i + storedWordIndex];
camoBytes[i] = a[i] ^ x[i];
CT("CAMO BYTE %u: %02x", i, camoBytes[i]);
}
//remove the duplicated id
buffer.setSize(buffer.size() - ID_SIZE);
//strip camo
uint8_t * const data = reinterpret_cast<uint8_t * const>(buffer.unsafeData());
for (size_t i = ID_SIZE; i < buffer.size(); i++)
{
data[i] ^= camoBytes[i % BYTES_IN_WORD];
}
buffer.setSize(buffer.size() - BYTES_IN_WORD);
result = true;
CT("PACKET CONTENTS AFTER STRIPPING CAMO:");
buffer.dump();
}
CT("CAMO STRIPPED: %d", result);
return result;
}
static void init(CamoLevel level, uint32_t word, KnownHostsMap hosts, CamoRelayRule rule);
/**
* Initialize the camo system
*
* @param autoApply Bits controlling automatic application to different host classes
* @param hosts knownHosts preloading
* @param relayRule Packet relay rule
*/
static void init(CamoAutoApplyBits autoApply, KnownHostsMap hosts, CamoRelayRule rule);
private:
static bool isInitialized;
static CamoLevel camoLevel;
static uint32_t camoWord;
static CamoAutoApplyBits camoAutoApply;
static CamoRelayRule relayRule;
static CamoPatternArray camoValues;
static CamoIndexMap camoIndices;
static std::mutex camoMutex;
static KnownHostsMap knownHosts;
static std::mt19937 rng;
};
} // namespace ZeroTier

View file

@ -232,7 +232,7 @@
/**
* Packet buffer size (can be changed)
*/
#define ZT_PROTO_ADDITIONAL_CAMO_LENGTH 4
#define ZT_PROTO_ADDITIONAL_CAMO_LENGTH 8
#define ZT_PROTO_MAX_PACKET_LENGTH (ZT_MAX_PACKET_FRAGMENTS * ZT_DEFAULT_PHYSMTU)

View file

@ -1456,14 +1456,7 @@ public:
}
// Camo settings defaults
CamoLevel camoLevel = CamoLevel::NONE;
std::string camoWordStr = ZT_CAMO_DEFAULT_WORDSTR;
uint32_t camoWord = 0;
for (size_t i = 0; i < BYTES_IN_WORD; i++)
{
camoWord <<= 8;
camoWord |= camoWordStr[i];
}
CamoAutoApplyBits camoAutoApply;
CamoRelayRule relayRule = CamoRelayRule::LEAVE;
KnownHostsMap knownHosts;
@ -1506,58 +1499,55 @@ public:
if (camo.is_object())
{
CT("CAMO CONFIG SECTION FOUND");
std::string camoLevelStr = OSUtils::jsonString(camo["level"], "none");
std::transform(camoLevelStr.begin(), camoLevelStr.end(), camoLevelStr.begin(), [](unsigned char c)
auto stringToClass = [](std::string &str) -> CamoClass {
CamoClass result = CamoClass::NEVER;
std::transform(str.begin(), str.end(), str.begin(), [](unsigned char c)
{
return std::tolower(c);
});
CT("CAMO LEVEL SETTING: %s", camoLevelStr.c_str());
if (camoLevelStr == "node")
if (str == "always")
{
camoLevel = CamoLevel::NODE;
CT("CAMO CLASS: ALWAYS");
result = CamoClass::ALWAYS;
}
else if (camoLevelStr == "controller")
else if (str == "node")
{
camoLevel = CamoLevel::CONTROLLER;
CT("CAMO CLASS: NODE");
result = CamoClass::NODE;
}
else if (camoLevelStr == "moon")
else if (str == "controller")
{
camoLevel = CamoLevel::MOON;
CT("CAMO CLASS: CONTROLLER");
result = CamoClass::CONTROLLER;
}
else if (camoLevelStr == "planet")
else if (str == "moon")
{
camoLevel = CamoLevel::PLANET;
}
if (camo.contains("word"))
{
std::string temp = OSUtils::jsonString(camo["word"], ZT_CAMO_DEFAULT_WORDSTR);
if (temp.substr(0, 2) == "0x")
{
try
{
camoWord = std::stoul(temp.substr(2), nullptr, 16);
}
catch (...)
{
}
CT("CAMO CLASS: MOON");
result = CamoClass::MOON;
}
else
{
for (size_t i = 0; i < BYTES_IN_WORD; i++)
CT("CAMO CLASS: NEVER");
}
return result;
};
json &autoApply = camo["autoApply"];
if (autoApply.is_array())
{
char c = ' ';
if (i < temp.size())
CT("AUTO APPLY SETTING FOUND");
for (const auto &entry: autoApply)
{
c = temp[i];
}
camoWord |= c;
camoWord <<= 8;
std::string entryStr = OSUtils::jsonString(entry, "");
CT("AUTO APPLY SETTING: %s", entryStr.c_str());
CamoClass camoClass = stringToClass(entryStr);
if (camoClass < CamoClass::AUTO_APPLY_COUNT)
{
camoAutoApply[camoClass] = true;
}
}
}
CT("CAMO WORD SETTING: %x", camoWord);
std::string relayRuleStr = OSUtils::jsonString(camo["relayRule"], "leave");
std::transform(relayRuleStr.begin(), relayRuleStr.end(), relayRuleStr.begin(), [](unsigned char c)
@ -1582,11 +1572,15 @@ public:
if (knownHostsSection.is_object())
{
CT("KNOWN HOSTS SECTION FOUND");
auto processLevelSection = [&knownHosts](const json &section, CamoLevel level)
for (auto it = knownHostsSection.begin(); it != knownHostsSection.end(); it++)
{
if (section.is_array())
std::string classKey = it.key();
CT("FOUND CAMO CLASS: %s", classKey.c_str());
if (it.value().is_array())
{
for (const auto &addrStr: section)
for (const auto &addrStr: it.value())
{
std::string addr = OSUtils::jsonString(addrStr, "");
if (!addr.empty())
@ -1595,46 +1589,11 @@ public:
if (host)
{
CT("VALID HOST FOUND: %s", addr.c_str());
knownHosts[host] = level;
knownHosts[host] = stringToClass(classKey);
}
}
}
}
};
for (auto it = knownHostsSection.begin(); it != knownHostsSection.end(); it++)
{
std::string levelKey = it.key();
std::transform(levelKey.begin(), levelKey.end(), levelKey.begin(), [](unsigned char c)
{
return std::tolower(c);
});
CT("FOUND CAMO LEVEL: %s", levelKey.c_str());
if (levelKey == "none")
{
processLevelSection(it.value(), CamoLevel::NONE);
}
else if (levelKey == "node")
{
processLevelSection(it.value(), CamoLevel::NODE);
}
else if (levelKey == "controller")
{
processLevelSection(it.value(), CamoLevel::CONTROLLER);
}
else if (levelKey == "moon")
{
processLevelSection(it.value(), CamoLevel::MOON);
}
else if (levelKey == "planet")
{
processLevelSection(it.value(), CamoLevel::PLANET);
}
else
{
processLevelSection(it.value(), CamoLevel::INAPPLICABLE);
}
}
}
}
@ -1643,7 +1602,7 @@ public:
CT("CAMO CONFIG SECTION NOT FOUND");
}
}
CamoPattern::init(camoLevel, camoWord, knownHosts, relayRule);
CamoPattern::init(camoAutoApply, knownHosts, relayRule);
// Set trusted paths if there are any
if (!ppc.empty()) {