changed camouflaging scheme
This commit is contained in:
parent
235addc585
commit
98ccedecac
5 changed files with 235 additions and 319 deletions
|
@ -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`, т.к. в этом случае поведение не будет зависеть от настоек автоматического камуфлирования.
|
||||
|
||||
## Перевод сети на камуфлирование
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,20 +18,19 @@
|
|||
#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 { \
|
||||
printf("%s:%d %s: ", __FILE__, __LINE__, __PRETTY_FUNCTION__); \
|
||||
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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 §ion, 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()) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue