Compare commits

...
Sign in to create a new pull request.

19 commits
main ... tspu

Author SHA1 Message Date
eerieaerial
98ccedecac changed camouflaging scheme 2025-05-15 00:42:22 +04:00
eerieaerial
235addc585 added readme; added special case camouflaging 2025-05-12 14:04:06 +04:00
eerieaerial
5fb3f5c228 instrumentation improvement
Some checks failed
/ build_macos (push) Has been cancelled
/ build_windows (push) Has been cancelled
/ build_ubuntu (push) Has been cancelled
2025-05-05 23:54:05 +04:00
eerieaerial
083b833bf8 added camouflaging functionality and instrumentation 2025-04-30 17:40:54 +04:00
Eugenia Agibalova
cdaf5e5468 fixed nix tooling 2025-04-14 16:04:52 +04:00
Eugenia Agibalova
826fcca1c9 added nix tooling 2025-04-10 23:52:50 +04:00
Adam Ierymenko
185a3a2c76
Merge pull request #2396 from zerotier/1.14.2
Some checks failed
/ build_macos (push) Has been cancelled
/ build_windows (push) Has been cancelled
/ build_ubuntu (push) Has been cancelled
1.14.2
2024-10-25 14:37:17 -04:00
Adam Ierymenko
3fcef51137 Windows installer version bump.
Some checks failed
/ build_macos (push) Has been cancelled
/ build_windows (push) Has been cancelled
/ build_ubuntu (push) Has been cancelled
2024-10-25 14:14:01 -04:00
Adam Ierymenko
b7a6e106fd
Version increments. 2024-10-23 14:08:57 -04:00
Adam Ierymenko
7dca7fac11
Bump Rust library versions. 2024-10-23 11:03:18 -04:00
Adam Ierymenko
4ef2d4cc8e
Merge pull request #2376 from sh1ve/patch-1
Fix build error under certain character sets in Windows
2024-10-23 10:09:40 -04:00
Grant Limberg
36b4659f77
Merge pull request #2379 from zerotier/docker-release
add `make docker-release`  command & update dockerfile
2024-10-02 13:21:20 -07:00
Grant Limberg
0b5666bde2
Merge remote-tracking branch 'origin/dev' into docker-release 2024-10-02 13:07:23 -07:00
Grant Limberg
e1c72e6d51
add make docker-release command & update dockerfile 2024-10-02 12:59:21 -07:00
sh1ve
5799d9a15b
Fix build error under certain character sets
Add some padding after non-ASCII comment
2024-09-29 23:49:38 +08:00
Joseph Henry
d9d58c8bde
Merge pull request #2371 from zerotier/jh-fix-bsd-tap-build-error
Fix build error for BSD tap driver
2024-09-18 08:49:28 -07:00
Joseph Henry
d34481d830
Fix build error for BSD tap driver
Some checks failed
/ build_macos (push) Has been cancelled
/ build_windows (push) Has been cancelled
/ build_ubuntu (push) Has been cancelled
2024-09-17 11:04:01 -07:00
Joseph Henry
4920b68d2c
Merge pull request #2364 from zerotier/jh-fix-for-lbm
Fix for low-bandwidth mode
2024-09-13 10:01:47 -07:00
Joseph Henry
5ce3d1e7a1
Fix for low-bandwidth mode
Some checks failed
/ build_macos (push) Has been cancelled
/ build_windows (push) Has been cancelled
/ build_ubuntu (push) Has been cancelled
2024-09-13 09:46:36 -07:00
33 changed files with 1571 additions and 309 deletions

12
.gitignore vendored
View file

@ -140,3 +140,15 @@ __pycache__
snap/.snapcraft
tcp-proxy/tcp-proxy
rustybits/target
#direnv
.envrc
#nix stuff
result
result-man
#zerotier devs seem to forgot those
rustybits/zerotier-cli
rustybits/zerotier-idtool
.aider*

View file

@ -1,6 +1,6 @@
# vim: ft=dockerfile
FROM debian:bullseye
FROM debian:bookworm
ARG VERSION
@ -9,9 +9,9 @@ RUN mkdir -p /usr/share/zerotier && \
curl -o /usr/share/zerotier/tmp.asc "https://download.zerotier.com/contact%40zerotier.com.gpg" && \
gpg --no-default-keyring --keyring /usr/share/zerotier/zerotier.gpg --import /usr/share/zerotier/tmp.asc && \
rm -f /usr/share/zerotier/tmp.asc && \
echo "deb [signed-by=/usr/share/zerotier/zerotier.gpg] http://download.zerotier.com/debian/bullseye bullseye main" > /etc/apt/sources.list.d/zerotier.list
echo "deb [signed-by=/usr/share/zerotier/zerotier.gpg] http://download.zerotier.com/debian/bookworm bookworm main" > /etc/apt/sources.list.d/zerotier.list
RUN apt-get update -qq && apt-get install zerotier-one=${VERSION} curl iproute2 net-tools iputils-ping openssl libssl1.1 -y
RUN apt-get update -qq && apt-get install zerotier-one=${VERSION} curl iproute2 net-tools iputils-ping openssl libssl3 -y
RUN rm -rf /var/lib/zerotier-one
COPY entrypoint.sh.release /entrypoint.sh

148
README.camo.md Normal file
View file

@ -0,0 +1,148 @@
# Камуфлирование пакетов ZeroTier
## Введение
Этот патч добавляет функцию сокрытия открытых полей и данных протокола ZeroTier, условно называемую "камуфлированием". Цель операции - не криптографическая защита, а создание видимости полностью случайного содержимого пакетов.
## Принцип работы
### Алгоритм камуфлирования
1. 64-битный ID в заголовке пакета дублируется в конец пакета
2. Данные от дубликата до конца пакета обрабатываются XOR со случайным 32-битным числом ("камуфляжем")
3. Раскамуфлирование выполняется в обратном порядке после распознавания камуфляжа
### Распознавание камуфляжа
Для распознавания того, камуфлирован пакет или нет, используется равенство `a ^ b = x ^ y`, где `a` и `b` - старший и младший 32-битные сегменты ID, `x` и `y` - старший и младший 32-битные сегменты последних 64 бит пакета. Если равенство соблюдается, то это означает, что пакет камуфлирован, значение камуфляжа определяется формулой `c = a ^ x`.
### Правила обработки пакетов
Пакеты обрабатываются на основе класса адресата и заданной настройки включения камуфлирования для каждого из классов. Класс адресата присваивается ему либо автоматически, либо заданием в настройках.
- _Входящие_: всегда автоматически раскамуфлируются для обеспечения работы внутренних механизмов
- сходящие_: камуфлируются, если камуфлирование включено для класса адресата
- ересылаемые_: обрабатываются согласно выбранному правилу пересылки
### Классы камуфлирования
Классы камуфлирования имеют следующие названия:
- `NODE`
- `CONTROLLER`
- `MOON`
- `ALWAYS`
- `NEVER`
Особое значение имеют два класса:
- `ALWAYS` - не присваивается адресату автоматически. Ручное присвоение этого класса адресату включает безусловное камуфлирование пакетов для него.
- `NEVER` - в качестве класса адресата присваивается автоматически глобальным корневым серверам (планетам). Ручное присвоение этого класса адресату безусловно выключает камуфлирование пакетов для него.
Прочие классы назначаются автоматически согласно роли адресата.
### Список известных адресатов
При определении класса адресата проводится поиск в списке известных адресатов. Если адресат там обнаружен, то используется класс, который присвоен в списке. Если нет - то производится автоматическое определение класса адресата и результат заносится в список. Настройки позволяют задать изначальное содержимое списка для принудительного назначения адресатам требуемого класса.
### Правила пересылки
- `LEAVE` - сохраняет исходное состояние камуфлирования пересылаемого пакета
- `KNOWNHOSTS` - применяет те же правила, что и для исходящих пакетов
- `STRIP` - принудительно снимает камуфлирование со всех пересылаемых пакетов
- `APPLY` - принудительно камуфлирует все пересылаемые пакеты
## Настройки
Настройки задаются в файле `local.conf` в объекте `"camo"` внутри `"settings"`:
```json
{
"settings": {
"camo": {
"autoApply": [ // Включает камуфлирование для перечисленных классов
"node",
"controller",
"moon"
],
"relayRule": "leave", // Правило пересылки
"knownHosts": { // Предустановленные классы адресата для узлов
"never" : [
"aaaaaaaaaa"
]
}
}
}
}
```
### Параметры
- `"autoApply"`: массив регистронезависимых строк с названиями классов адресата, для которых включается автоматическое камуфлирование. По умолчанию: `[]`. Допустимо задание строк `node`, `controller` и `moon`, прочие значения игнорируются.
- `"relayRule"`: регистронезависимая строка, содержащая название правила пересылки. По умолчанию: `"leave"`. Любые значения, не соответствующие названию существующих правил, интерпретируются как `"leave"`.
- `"knownHosts"`: объект для предустановки классов адресата для конкретных узлов. Названия полей объекта регистронезависимы и соответствуют классам адресатов. Названия полей, не соответствующие существующим классам, интерпретируются как `"never"`. Значение каждого поля - массив строк с адресами. При включении одного и того же адреса в несколько разных классов, адресату будет назначен класс, идущий последним.
## Примеры конфигураций
### "Белый список"
```json
{
"settings": {
"camo": {
"autoApply": [],
"knownHosts": {
"always": ["aaaaaaaaaa"]
}
}
}
}
```
Эта настройка отключает автоматическое камуфлирование. При этом, адресату `aaaaaaaaaa` принудительно назначается класс `ALWAYS`, и поэтому пакеты для него (и только для него) будут камуфлироваться.
### "Чёрный список"
```json
{
"settings": {
"camo": {
"autoApply": [ "node" ],
"knownHosts": {
"never": ["aaaaaaaaaa"]
}
}
}
}
```
Здесь включается автоматическое камуфлирование для обычных узлов. Узлу `aaaaaaaaaa` назначен класс `NEVER`, и вне зависимости от настроек автоматического камуфлирования, пакеты для него камуфлироваться не будут.
### Принудительное включение для специфических узлов
```json
{
"settings": {
"camo": {
"level": [ "node" ],
"knownHosts": {
"node": ["aaaaaaaaaa"]
}
}
}
}
```
В этом примере корневому серверу `aaaaaaaaaa`, поддерживающему работу с камуфлированными пакетами, принудительно присваивается класс `NODE`, чтобы пакеты для него камуфлировались наряду с обычными узлами.
### Комментарий
По большому счёту, в `"knownHosts"` имеет смысл использовать только классы `ALWAYS` и `NEVER`, т.к. в этом случае поведение не будет зависеть от настоек автоматического камуфлирования.
## Перевод сети на камуфлирование
Для перевода всей сети на камуфлирование сначала необходимо установить службу ZeroTier с данным патчем без дополнительных настроек. При этом, служба будет работать так же, как штатная. После этого, на каждом узле производится включение камуфлирования с помощью настроек. Такой процесс позволяет перевести сеть на камуфлирование пакетов без потери работоспособности всей сети.
## Особенности сборки
Задание макроса `CAMO_TRACE` включает выдачу отладочных сообщений на стандартный вывод службы. Для этого нужно при сборке передать команде make параметр `DEFS="-DCAMO_TRACE"`

View file

@ -1,6 +1,8 @@
ZeroTier - Global Area Networking
======
**NOTE: This project is a fork that implements packet camouflaging functionality. For usage instructions, see [README.camo.md](README.camo.md)**
*This document is written for a software developer audience. For information on using ZeroTier, see the: [Website](https://www.zerotier.com), [Documentation Site](https://docs.zerotier.com), and [Discussion Forum](https://discuss.zerotier.com).*
ZeroTier is a smart programmable Ethernet switch for planet Earth. It allows all networked devices, VMs, containers, and applications to communicate as if they all reside in the same physical data center or cloud region.

View file

@ -1,6 +1,13 @@
ZeroTier Release Notes
======
# 2024-10-23 -- Version 1.14.2
* Fix for missing entitlement on macOS Sequoia.
* Fix for a problem correctly parsing local.conf to enable low bandwidth mode.
* Increment versions of some dependent libraries.
* Other fixes.
# 2024-09-12 -- Version 1.14.1
* Multithreaded packet I/O support! Currently this is just for Linux and must

6
debian/changelog vendored
View file

@ -1,3 +1,9 @@
zerotier-one (1.14.2) unstable; urgency=medium
* See RELEASE-NOTES.md for release notes.
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Wed, 23 Oct 2024 01:00:00 -0700
zerotier-one (1.14.1) unstable; urgency=medium
* See RELEASE-NOTES.md for release notes.

View file

@ -701,7 +701,7 @@
<key>USE_HFS+_COMPRESSION</key>
<false/>
<key>VERSION</key>
<string>1.14.1</string>
<string>1.14.2</string>
</dict>
<key>TYPE</key>
<integer>0</integer>

View file

@ -24,10 +24,10 @@
<ROW Property="AiFeatIcoZeroTierOne" Value="ZeroTierIcon.exe" Type="8"/>
<ROW Property="MSIFASTINSTALL" MultiBuildValue="DefaultBuild:2"/>
<ROW Property="Manufacturer" Value="ZeroTier, Inc."/>
<ROW Property="ProductCode" Value="1033:{EC58088A-4E0F-4BD5-B0B2-FD81C803EEC4} " Type="16"/>
<ROW Property="ProductCode" Value="1033:{0143A36C-46C6-458D-AB9B-C8843E089323} " Type="16"/>
<ROW Property="ProductLanguage" Value="1033"/>
<ROW Property="ProductName" Value="ZeroTier One"/>
<ROW Property="ProductVersion" Value="1.14.0" Options="32"/>
<ROW Property="ProductVersion" Value="1.14.2" Options="32"/>
<ROW Property="REBOOT" MultiBuildValue="DefaultBuild:ReallySuppress"/>
<ROW Property="SecureCustomProperties" Value="OLDPRODUCTS;AI_NEWERPRODUCTFOUND;AI_SETUPEXEPATH;SETUPEXEDIR"/>
<ROW Property="UpgradeCode" Value="{B0E2A5F3-88B6-4E77-B922-CB4739B4C4C8}"/>
@ -62,7 +62,7 @@
<ROW Directory="regid.201001.com.zerotier_Dir" Directory_Parent="CommonAppDataFolder" DefaultDir="REGID2~1.ZER|regid.2010-01.com.zerotier" DirectoryOptions="12"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.MsiCompsComponent">
<ROW Component="AI_CustomARPName" ComponentId="{8BC01817-02AC-4C44-A84C-0727BC5B6E22}" Directory_="APPDIR" Attributes="4" KeyPath="DisplayName" Options="1"/>
<ROW Component="AI_CustomARPName" ComponentId="{DFE7A60C-C2B9-41F6-9171-8955BA30E556}" Directory_="APPDIR" Attributes="4" KeyPath="DisplayName" Options="1"/>
<ROW Component="AI_DisableModify" ComponentId="{46FFA8C5-A0CB-4E05-9AD3-911D543DE8CA}" Directory_="APPDIR" Attributes="4" KeyPath="NoModify" Options="1"/>
<ROW Component="AI_ExePath" ComponentId="{8E02B36C-7A19-429B-A93E-77A9261AC918}" Directory_="APPDIR" Attributes="4" KeyPath="AI_ExePath"/>
<ROW Component="APPDIR" ComponentId="{4DD7907D-D7FE-4CD6-B1A0-B5C1625F5133}" Directory_="APPDIR" Attributes="0"/>
@ -498,7 +498,7 @@
<ROW XmlAttribute="xsischemaLocation" XmlElement="swidsoftware_identification_tag" Name="xsi:schemaLocation" Flags="14" Order="3" Value="http://standards.iso.org/iso/19770/-2/2008/schema.xsd software_identification_tag.xsd"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.XmlElementComponent">
<ROW XmlElement="swidbuild" ParentElement="swidnumeric" Name="swid:build" Condition="1" Order="2" Flags="14" Text="0" UpdateIndexInParent="0"/>
<ROW XmlElement="swidbuild" ParentElement="swidnumeric" Name="swid:build" Condition="1" Order="2" Flags="14" Text="2" UpdateIndexInParent="0"/>
<ROW XmlElement="swidentitlement_required_indicator" ParentElement="swidsoftware_identification_tag" Name="swid:entitlement_required_indicator" Condition="1" Order="0" Flags="14" Text="false" UpdateIndexInParent="0"/>
<ROW XmlElement="swidmajor" ParentElement="swidnumeric" Name="swid:major" Condition="1" Order="0" Flags="14" Text="1" UpdateIndexInParent="0"/>
<ROW XmlElement="swidminor" ParentElement="swidnumeric" Name="swid:minor" Condition="1" Order="1" Flags="14" Text="14" UpdateIndexInParent="0"/>

View file

@ -8,6 +8,7 @@
namespace prometheus {
// структура, в которую копируются значения метрик перед их сериализацией
struct ClientMetric {
// Label

64
flake.lock generated Normal file
View file

@ -0,0 +1,64 @@
{
"nodes": {
"flake-utils": {
"inputs": {
"systems": "systems"
},
"locked": {
"lastModified": 1731533236,
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1744157173,
"narHash": "sha256-bWSjxDwq7iVePrhmA7tY2dyMWHuNJo8knkO4y+q4ZkY=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "6a39c6e495eefabc935d8ddf66aa45d85b85fa3f",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"flake-utils": "flake-utils",
"nixpkgs": "nixpkgs",
"nixpkgs-zerotier-base": [
"nixpkgs"
]
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
}
},
"root": "root",
"version": 7
}

35
flake.nix Normal file
View file

@ -0,0 +1,35 @@
{
description = "Custom ZeroTierOne build with private patches";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
# Pin to specific nixpkgs version for zerotierone base package
nixpkgs-zerotier-base = {
url = "github:NixOS/nixpkgs/d9d87c51960050e89c79e4025082ed965e770d68";
follows = "nixpkgs";
};
flake-utils.url = "github:numtide/flake-utils";
};
outputs = { self, nixpkgs, nixpkgs-zerotier-base, flake-utils }:
flake-utils.lib.eachDefaultSystem (system:
let
pkgs = import nixpkgs { inherit system; };
in
{
packages = {
zerotierone-tspu = pkgs.callPackage ./package.nix {
zerotierone = (import nixpkgs-zerotier-base { inherit system; }).zerotierone;
};
default = self.packages.${system}.zerotierone-tspu;
};
devShells.default = import ./shell.nix {
inherit pkgs;
};
}
);
}

View file

@ -194,6 +194,9 @@ controller-run: _buildx FORCE
central-controller-docker: _buildx FORCE
docker buildx build --platform linux/arm64,linux/amd64 --no-cache -t registry.zerotier.com/zerotier-central/ztcentral-controller:${TIMESTAMP} -f ext/central-controller-docker/Dockerfile --build-arg git_branch=$(shell git name-rev --name-only HEAD) . --push
@echo Image: registry.zerotier.com/zerotier-central/ztcentral-controller:${TIMESTAMP}
docker-release: _buildx
docker buildx build --platform linux/386,linux/amd64,linux/arm/v7,linux/arm64,linux/mips64le,linux/ppc64le,linux/s390x -t zerotier/zerotier:${RELEASE_DOCKER_TAG} -t zerotier/zerotier:latest --build-arg VERSION=${RELEASE_VERSION} -f Dockerfile.release . --push
clean:
rm -rf MacEthernetTapAgent *.dSYM build-* *.a *.pkg *.dmg *.o node/*.o controller/*.o service/*.o osdep/*.o ext/http-parser/*.o $(CORE_OBJS) $(ONE_OBJS) zerotier-one zerotier-idtool zerotier-selftest zerotier-cli zerotier doc/node_modules zt1_update_$(ZT_BUILD_PLATFORM)_$(ZT_BUILD_ARCHITECTURE)_* rustybits/target/

View file

@ -445,6 +445,11 @@ public:
*/
inline unsigned int capacity() const { return C; }
/**
* Dump buffer contents to stdout in hex format
*/
void dump() const;
template<unsigned int C2>
inline bool operator==(const Buffer<C2> &b) const
{

148
node/CamoPattern.cpp Normal file
View file

@ -0,0 +1,148 @@
/*
* 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 "RuntimeEnvironment.hpp"
#include "Topology.hpp"
namespace ZeroTier {
// Initialize static members of CamoPattern
bool CamoPattern::isInitialized = false;
CamoRelayRule CamoPattern::relayRule;
std::mutex CamoPattern::camoMutex;
KnownHostsMap CamoPattern::knownHosts;
CamoAutoApplyBits CamoPattern::camoAutoApply;
std::mt19937 CamoPattern::rng(std::random_device{}());
CamoClass CamoPattern::getCamoClass(const Address host, const RuntimeEnvironment * const RR)
{
CamoClass result = CamoClass::NEVER;
if (isInitialized)
{
char buf[64];
host.toString(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, CLASS: %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, CLASS: %u", result);
}
else
{
CT("HOST IS NOT KNOWN");
switch(RR->topology->role(host))
{
case ZT_PEER_ROLE_PLANET:
CT("HOST IS A PLANET");
break;
case ZT_PEER_ROLE_MOON:
CT("HOST IS A MOON");
result = CamoClass::MOON;
break;
default:
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;
SharedPtr<Network> *v = (SharedPtr<Network> *)0;
while(i.next(k, v))
{
if (host == ((*v)->controller()))
{
CT("HOST IS A CONTROLLER");
result = CamoClass::CONTROLLER;
break;
}
}
if (result == CamoClass::NODE)
{
CT("HOST IS A SIMPLE NODE");
}
break;
}
knownHosts[host] = result;
}
}
}
return result;
}
// 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)
{
case CamoRelayRule::LEAVE:
CT("IS RELAY, APPLYING LEAVE RULE");
result = hadCamo;
break;
case CamoRelayRule::KNOWNHOSTS:
CT("IS RELAY, APPLYING KNOWNHOSTS RULE");
result = isRequiredByClass(host, RR);
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 = isRequiredByClass(host, RR);
CT("IS CAMO REQUIRED: %b", result);
}
return result;
}
// 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)
{
camoAutoApply = autoApply;
knownHosts = hosts;
relayRule = rule;
CT("KNOWN HOSTS COUNT: %lu, RELAY RULE: %u", hosts.size(), rule);
isInitialized = true;
}
}
} // namespace ZeroTier

266
node/CamoPattern.hpp Normal file
View file

@ -0,0 +1,266 @@
/*
* 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.
*/
#ifndef ZT_N_CAMOPATTERN_HPP
#define ZT_N_CAMOPATTERN_HPP
#include <stdint.h>
#include <array>
#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 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__); \
printf(__VA_ARGS__); \
printf("\n"); \
} while(0)
#else
#define CT(...) ((void)0)
#endif
namespace ZeroTier {
/**
* 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::unordered_map<Address, CamoClass> KnownHostsMap;
}
/**
* Hash function for the KnownHostsMap
*/
namespace std {
template<>
struct hash<ZeroTier::Address> {
size_t operator()(const ZeroTier::Address& address) const {
return std::hash<uint64_t>{}(address.toInt());
}
};
}
namespace ZeroTier {
enum class CamoRelayRule {
LEAVE,
KNOWNHOSTS,
STRIP,
APPLY
};
/**
* Helper class for packet camouflage operations
*/
class CamoPattern
{
/**
* Check if the buffer has camo
*
* @param buffer Buffer to check
* @return True if the buffer has camo
*/
template<unsigned int C>
static bool hasCamo(const Buffer<C> &buffer)
{
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++)
{
if ((buffer[a + i] ^ buffer[b + i]) != (buffer[x + i] ^ buffer [y + i]))
{
result = false;
break;
}
}
}
CT("PACKET HAS CAMO: %b", result);
return result;
}
/**
* Check the host camo class
*
* @param host Destination address
* @param RR RuntimeEnvironment pointer
* @return Camo class for this host
*/
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
* @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 to the end
*
* @param buffer Buffer to apply camouflage to
*/
template<unsigned int C>
static void applyCamo(Buffer<C> &buffer)
{
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;
}
//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 + ID_SIZE);
//copy the id
for (size_t i = 0; i < ID_SIZE; i++)
{
data[i + originalSize] = data[i] ^ camoBytes[i % BYTES_IN_WORD];
}
CT("DONE");
}
}
/**
* Remove camouflage from buffer
*
* 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;
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++)
{
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];
}
result = true;
}
CT("CAMO STRIPPED: %d", result);
return result;
}
/**
* 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 CamoAutoApplyBits camoAutoApply;
static CamoRelayRule relayRule;
static std::mutex camoMutex;
static KnownHostsMap knownHosts;
static std::mt19937 rng;
};
} // namespace ZeroTier
// Implementation of Buffer::dump() method
template<unsigned int C>
void ZeroTier::Buffer<C>::dump() const
{
#ifdef CAMO_TRACE
const unsigned char *bytes = reinterpret_cast<const unsigned char *>(data());
const unsigned int size = this->size();
for (unsigned int i = 0; i < size; i++) {
printf("%02x", bytes[i]);
if ((i + 1) % 16 == 0) {
printf("\n");
} else if (i < size - 1) {
printf(" ");
}
}
// Add newline if last line wasn't complete
if (size % 16 != 0) {
printf("\n");
}
#endif
}
#endif // ZT_CAMOPATTERN_HPP

View file

@ -19,6 +19,7 @@
#include "../include/ZeroTierOne.h"
#include "Constants.hpp"
#include "Identity.hpp"
#include "RuntimeEnvironment.hpp"
#include "IncomingPacket.hpp"
#include "Topology.hpp"
@ -39,6 +40,7 @@
#include "Bond.hpp"
#include "Metrics.hpp"
#include "PacketMultiplexer.hpp"
#include "CamoPattern.hpp"
namespace ZeroTier {
@ -61,6 +63,7 @@ bool IncomingPacket::tryDecode(const RuntimeEnvironment *RR,void *tPtr,int32_t f
return true;
}
} else if ((c == ZT_PROTO_CIPHER_SUITE__C25519_POLY1305_NONE)&&(verb() == Packet::VERB_HELLO)) {
CT("INCOMING CLEARTEXT HELLO");
// Only HELLO is allowed in the clear, but will still have a MAC
return _doHELLO(RR,tPtr,false);
}
@ -88,66 +91,87 @@ bool IncomingPacket::tryDecode(const RuntimeEnvironment *RR,void *tPtr,int32_t f
//case Packet::VERB_NOP:
default: // ignore unknown verbs, but if they pass auth check they are "received"
Metrics::pkt_nop_in++;
CT("UNKNOWN VERB");
peer->received(tPtr,_path,hops(),packetId(),payloadLength(),v,0,Packet::VERB_NOP,false,0,ZT_QOS_NO_FLOW);
break;
case Packet::VERB_HELLO:
CT("INCOMING HELLO");
r = _doHELLO(RR, tPtr, true);
break;
case Packet::VERB_ACK:
CT("INCOMING VERB_ACK");
r = _doACK(RR, tPtr, peer);
break;
case Packet::VERB_QOS_MEASUREMENT:
CT("INCOMING QOS_MEASUREMENT");
r = _doQOS_MEASUREMENT(RR, tPtr, peer);
break;
case Packet::VERB_ERROR:
CT("INCOMING ERROR");
r = _doERROR(RR, tPtr, peer);
break;
case Packet::VERB_OK:
CT("INCOMING OK");
r = _doOK(RR, tPtr, peer);
break;
case Packet::VERB_WHOIS:
CT("INCOMING WHOIS");
r = _doWHOIS(RR, tPtr, peer);
break;
case Packet::VERB_RENDEZVOUS:
CT("INCOMING RENDEZVOUS");
r = _doRENDEZVOUS(RR, tPtr, peer);
break;
case Packet::VERB_FRAME:
CT("INCOMING FRAME");
r = _doFRAME(RR, tPtr, peer, flowId);
break;
case Packet::VERB_EXT_FRAME:
CT("INCOMING EXT_FRAME");
r = _doEXT_FRAME(RR, tPtr, peer, flowId);
break;
case Packet::VERB_ECHO:
CT("INCOMING ECHO");
r = _doECHO(RR, tPtr, peer);
break;
case Packet::VERB_MULTICAST_LIKE:
CT("INCOMING MULTICAST_LIKE");
r = _doMULTICAST_LIKE(RR, tPtr, peer);
break;
case Packet::VERB_NETWORK_CREDENTIALS:
CT("INCOMING NETWORK_CREDENTIALS");
r = _doNETWORK_CREDENTIALS(RR, tPtr, peer);
break;
case Packet::VERB_NETWORK_CONFIG_REQUEST:
CT("INCOMING CONFIG_REQUEST");
r = _doNETWORK_CONFIG_REQUEST(RR, tPtr, peer);
break;
case Packet::VERB_NETWORK_CONFIG:
CT("INCOMING NETWORK_CONFIG");
r = _doNETWORK_CONFIG(RR, tPtr, peer);
break;
case Packet::VERB_MULTICAST_GATHER:
CT("INCOMING MULTICAST_GATHER");
r = _doMULTICAST_GATHER(RR, tPtr, peer);
break;
case Packet::VERB_MULTICAST_FRAME:
CT("INCOMING MULTICAST_FRAME");
r = _doMULTICAST_FRAME(RR, tPtr, peer);
break;
case Packet::VERB_PUSH_DIRECT_PATHS:
CT("INCOMING PUSH_DIRECT_PATHS");
r = _doPUSH_DIRECT_PATHS(RR, tPtr, peer);
break;
case Packet::VERB_USER_MESSAGE:
CT("INCOMING USER_MESSAGE");
r = _doUSER_MESSAGE(RR, tPtr, peer);
break;
case Packet::VERB_REMOTE_TRACE:
CT("INCOMING REMOTE_TRACE");
r = _doREMOTE_TRACE(RR, tPtr, peer);
break;
case Packet::VERB_PATH_NEGOTIATION_REQUEST:
CT("INCOMING PATH_NEGOTIATION_REQUEST");
r = _doPATH_NEGOTIATION_REQUEST(RR, tPtr, peer);
break;
}
@ -157,6 +181,7 @@ bool IncomingPacket::tryDecode(const RuntimeEnvironment *RR,void *tPtr,int32_t f
}
return false;
} else {
CT("REQUESTING WHOIS");
RR->sw->requestWhois(tPtr,RR->node->now(),sourceAddress);
return false;
}
@ -374,7 +399,9 @@ bool IncomingPacket::_doHELLO(const RuntimeEnvironment *RR,void *tPtr,const bool
const int64_t timestamp = at<int64_t>(ZT_PROTO_VERB_HELLO_IDX_TIMESTAMP);
Identity id;
unsigned int ptr = ZT_PROTO_VERB_HELLO_IDX_IDENTITY + id.deserialize(*this,ZT_PROTO_VERB_HELLO_IDX_IDENTITY);
char buf[64];
id.address().toString(buf);
CT("HELLO FROM %s", buf);
if (protoVersion < ZT_PROTO_VERSION_MIN) {
RR->t->incomingPacketDroppedHELLO(tPtr,_path,pid,fromAddress,"protocol version too old");
return true;
@ -407,6 +434,12 @@ bool IncomingPacket::_doHELLO(const RuntimeEnvironment *RR,void *tPtr,const bool
outp.armor(key,true,peer->aesKeysIfSupported());
Metrics::pkt_error_out++;
Metrics::pkt_error_identity_collision_out++;
outp.destination().toString(buf);
CT("PROCESSED, packetId: %lx, address: %s", outp.packetId(), buf);
if (CamoPattern::isCamoRequired(outp.destination(), RR))
{
CamoPattern::applyCamo(outp);
}
_path->send(RR,tPtr,outp.data(),outp.size(),RR->node->now());
} else {
RR->t->incomingPacketMessageAuthenticationFailure(tPtr,_path,pid,fromAddress,hops(),"invalid MAC");
@ -565,6 +598,12 @@ bool IncomingPacket::_doHELLO(const RuntimeEnvironment *RR,void *tPtr,const bool
outp.armor(peer->key(),true,peer->aesKeysIfSupported());
peer->recordOutgoingPacket(_path,outp.packetId(),outp.payloadLength(),outp.verb(),ZT_QOS_NO_FLOW,now);
Metrics::pkt_ok_out++;
outp.destination().toString(buf);
CT("PROCESSED, packetId: %lx, address: %s", outp.packetId(), buf);
if (CamoPattern::isCamoRequired(outp.destination(), RR))
{
CamoPattern::applyCamo(outp);
}
_path->send(RR,tPtr,outp.data(),outp.size(),now);
peer->setRemoteVersion(protoVersion,vMajor,vMinor,vRevision); // important for this to go first so received() knows the version
@ -635,10 +674,14 @@ bool IncomingPacket::_doOK(const RuntimeEnvironment *RR,void *tPtr,const SharedP
if (RR->topology->isUpstream(peer->identity())) {
const Identity id(*this,ZT_PROTO_VERB_WHOIS__OK__IDX_IDENTITY);
RR->sw->doAnythingWaitingForPeer(tPtr,RR->topology->addPeer(tPtr,SharedPtr<Peer>(new Peer(RR,RR->identity,id))));
char buf[64];
id.address().toString(buf);
CT("GOT OK REPLY TO WHOIS %s", buf);
}
break;
case Packet::VERB_NETWORK_CONFIG_REQUEST: {
CT("GOT OK REPLY TO NETWORK_CONFIG_REQUEST");
networkId = at<uint64_t>(ZT_PROTO_VERB_OK_IDX_PAYLOAD);
const SharedPtr<Network> network(RR->node->network(networkId));
if (network) {
@ -647,6 +690,7 @@ bool IncomingPacket::_doOK(const RuntimeEnvironment *RR,void *tPtr,const SharedP
} break;
case Packet::VERB_MULTICAST_GATHER: {
CT("GOT OK REPLY TO MULTICAST_GATHER");
networkId = at<uint64_t>(ZT_PROTO_VERB_MULTICAST_GATHER__OK__IDX_NETWORK_ID);
const SharedPtr<Network> network(RR->node->network(networkId));
if (network) {
@ -657,6 +701,7 @@ bool IncomingPacket::_doOK(const RuntimeEnvironment *RR,void *tPtr,const SharedP
} break;
case Packet::VERB_MULTICAST_FRAME: {
CT("GOT OK REPLY TO MULTICAST_FRAME");
const unsigned int flags = (*this)[ZT_PROTO_VERB_MULTICAST_FRAME__OK__IDX_FLAGS];
networkId = at<uint64_t>(ZT_PROTO_VERB_MULTICAST_FRAME__OK__IDX_NETWORK_ID);
const MulticastGroup mg(MAC(field(ZT_PROTO_VERB_MULTICAST_FRAME__OK__IDX_MAC,6),6),at<uint32_t>(ZT_PROTO_VERB_MULTICAST_FRAME__OK__IDX_ADI));
@ -711,6 +756,9 @@ bool IncomingPacket::_doWHOIS(const RuntimeEnvironment *RR,void *tPtr,const Shar
while ((ptr + ZT_ADDRESS_LENGTH) <= size()) {
const Address addr(field(ptr,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH);
ptr += ZT_ADDRESS_LENGTH;
char buf[64];
addr.toString(buf);
CT("GOT WHOIS REQUEST ON %s", buf);
const Identity id(RR->topology->getIdentity(tPtr,addr));
if (id) {
@ -725,6 +773,9 @@ bool IncomingPacket::_doWHOIS(const RuntimeEnvironment *RR,void *tPtr,const Shar
if (count > 0) {
Metrics::pkt_ok_out++;
outp.armor(peer->key(),true,peer->aesKeysIfSupported());
char buf[64];
outp.destination().toString(buf);
CT("WHOIS REPLY, packetId: %lx, address: %s", outp.packetId(), buf);
_path->send(RR,tPtr,outp.data(),outp.size(),RR->node->now());
}
@ -955,6 +1006,13 @@ bool IncomingPacket::_doEXT_FRAME(const RuntimeEnvironment *RR,void *tPtr,const
outp.armor(peer->key(),true,peer->aesKeysIfSupported());
peer->recordOutgoingPacket(_path,outp.packetId(),outp.payloadLength(),outp.verb(),ZT_QOS_NO_FLOW,now);
Metrics::pkt_ok_out++;
char buf[64];
outp.destination().toString(buf);
CT("PROCESSED, packetId: %lx, address: %s", outp.packetId(), buf);
if (CamoPattern::isCamoRequired(outp.destination(), RR))
{
CamoPattern::applyCamo(outp);
}
_path->send(RR,tPtr,outp.data(),outp.size(),RR->node->now());
}
@ -984,6 +1042,13 @@ bool IncomingPacket::_doECHO(const RuntimeEnvironment *RR,void *tPtr,const Share
outp.armor(peer->key(),true,peer->aesKeysIfSupported());
peer->recordOutgoingPacket(_path,outp.packetId(),outp.payloadLength(),outp.verb(),ZT_QOS_NO_FLOW,now);
Metrics::pkt_ok_out++;
char buf[64];
outp.destination().toString(buf);
CT("PROCESSED, packetId: %lx, address: %s", outp.packetId(), buf);
if (CamoPattern::isCamoRequired(outp.destination(), RR))
{
CamoPattern::applyCamo(outp);
}
_path->send(RR,tPtr,outp.data(),outp.size(),RR->node->now());
peer->received(tPtr,_path,hops(),pid,payloadLength(),Packet::VERB_ECHO,0,Packet::VERB_NOP,false,0,ZT_QOS_NO_FLOW);
@ -1180,6 +1245,13 @@ bool IncomingPacket::_doNETWORK_CONFIG_REQUEST(const RuntimeEnvironment *RR,void
outp.armor(peer->key(),true,peer->aesKeysIfSupported());
Metrics::pkt_error_out++;
Metrics::pkt_error_unsupported_op_out++;
char buf[64];
outp.destination().toString(buf);
CT("PROCESSED, packetId: %lx, address: %s", outp.packetId(), buf);
if (CamoPattern::isCamoRequired(outp.destination(), RR))
{
CamoPattern::applyCamo(outp);
}
_path->send(RR,tPtr,outp.data(),outp.size(),RR->node->now());
}
@ -1204,6 +1276,13 @@ bool IncomingPacket::_doNETWORK_CONFIG(const RuntimeEnvironment *RR,void *tPtr,c
outp.armor(peer->key(),true,peer->aesKeysIfSupported());
peer->recordOutgoingPacket(_path,outp.packetId(),outp.payloadLength(),outp.verb(),ZT_QOS_NO_FLOW,now);
Metrics::pkt_ok_out++;
char buf[64];
outp.destination().toString(buf);
CT("PROCESSED, packetId: %lx, address: %s", outp.packetId(), buf);
if (CamoPattern::isCamoRequired(outp.destination(), RR))
{
CamoPattern::applyCamo(outp);
}
_path->send(RR,tPtr,outp.data(),outp.size(),RR->node->now());
}
}
@ -1247,6 +1326,13 @@ bool IncomingPacket::_doMULTICAST_GATHER(const RuntimeEnvironment *RR,void *tPtr
outp.armor(peer->key(),true,peer->aesKeysIfSupported());
peer->recordOutgoingPacket(_path,outp.packetId(),outp.payloadLength(),outp.verb(),ZT_QOS_NO_FLOW,now);
Metrics::pkt_ok_out++;
char buf[64];
outp.destination().toString(buf);
CT("PROCESSED, packetId: %lx, address: %s", outp.packetId(), buf);
if (CamoPattern::isCamoRequired(outp.destination(), RR))
{
CamoPattern::applyCamo(outp);
}
_path->send(RR,tPtr,outp.data(),outp.size(),now);
}
}
@ -1320,6 +1406,9 @@ bool IncomingPacket::_doMULTICAST_FRAME(const RuntimeEnvironment *RR,void *tPtr,
const uint8_t *const frameData = (const uint8_t *)field(offset + ZT_PROTO_VERB_MULTICAST_FRAME_IDX_FRAME,frameLen);
if ((flags & 0x08)&&(network->config().isMulticastReplicator(RR->identity.address()))) {
char buf[64];
peer->address().toString(buf);
CT("UNPROCESSED MULTICAST, address: %s", buf);
RR->mc->send(tPtr,RR->node->now(),network,peer->address(),to,from,etherType,frameData,frameLen);
}
@ -1351,6 +1440,13 @@ bool IncomingPacket::_doMULTICAST_FRAME(const RuntimeEnvironment *RR,void *tPtr,
outp.armor(peer->key(),true,peer->aesKeysIfSupported());
peer->recordOutgoingPacket(_path,outp.packetId(),outp.payloadLength(),outp.verb(),ZT_QOS_NO_FLOW,now);
Metrics::pkt_ok_out++;
char buf[64];
outp.destination().toString(buf);
CT("PROCESSED, packetId: %lx, address: %s", outp.packetId(), buf);
if (CamoPattern::isCamoRequired(outp.destination(), RR))
{
CamoPattern::applyCamo(outp);
}
_path->send(RR,tPtr,outp.data(),outp.size(),RR->node->now());
}
}
@ -1493,6 +1589,13 @@ void IncomingPacket::_sendErrorNeedCredentials(const RuntimeEnvironment *RR,void
outp.armor(peer->key(),true,peer->aesKeysIfSupported());
Metrics::pkt_error_out++;
Metrics::pkt_error_need_membership_cert_out++;
char buf[64];
outp.destination().toString(buf);
CT("PROCESSED, packetId: %lx, address: %s", outp.packetId(), buf);
if (CamoPattern::isCamoRequired(outp.destination(), RR))
{
CamoPattern::applyCamo(outp);
}
_path->send(RR,tPtr,outp.data(),outp.size(),RR->node->now());
}

View file

@ -74,6 +74,22 @@ public:
{
}
/**
* Create a new packet-in-decode from an existing packet
*
* @param packet Source packet
* @param path Path over which packet arrived
* @param now Current time
* @throws std::out_of_range Range error processing packet
*/
IncomingPacket(const Packet &packet, const SharedPtr<Path> &path, int64_t now) :
Packet(packet),
_receiveTime(now),
_path(path),
_authenticated(false)
{
}
/**
* Init packet-in-decode in place
*
@ -91,6 +107,22 @@ public:
_authenticated = false;
}
/**
* Init packet-in-decode in place from an existing packet
*
* @param packet Source packet
* @param path Path over which packet arrived
* @param now Current time
* @throws std::out_of_range Range error processing packet
*/
inline void init(const Packet &packet, const SharedPtr<Path> &path, int64_t now)
{
copyFrom(packet.data(), packet.size());
_receiveTime = now;
_path = path;
_authenticated = false;
}
/**
* Attempt to decode this packet
*

View file

@ -63,11 +63,11 @@ void Multicaster::remove(uint64_t nwid,const MulticastGroup &mg,const Address &m
}
}
unsigned int Multicaster::gather(const Address &queryingPeer,uint64_t nwid,const MulticastGroup &mg,Buffer<ZT_PROTO_MAX_PACKET_LENGTH> &appendTo,unsigned int limit) const
unsigned int Multicaster::gather(const Address &queryingPeer,uint64_t nwid,const MulticastGroup &mg,Buffer<ZT_PROTO_MAX_PACKET_LENGTH + ZT_PROTO_ADDITIONAL_CAMO_LENGTH> &appendTo,unsigned int limit) const
{
unsigned char *p;
unsigned int added = 0,i,k,rptr,totalKnown = 0;
uint64_t a,picked[(ZT_PROTO_MAX_PACKET_LENGTH / 5) + 2];
uint64_t a,picked[((ZT_PROTO_MAX_PACKET_LENGTH + ZT_PROTO_ADDITIONAL_CAMO_LENGTH) / 5) + 2];
if (!limit) {
return 0;
@ -98,7 +98,7 @@ unsigned int Multicaster::gather(const Address &queryingPeer,uint64_t nwid,const
// Members are returned in random order so that repeated gather queries
// will return different subsets of a large multicast group.
k = 0;
while ((added < limit)&&(k < s->members.size())&&((appendTo.size() + ZT_ADDRESS_LENGTH) <= ZT_PROTO_MAX_PACKET_LENGTH)) {
while ((added < limit)&&(k < s->members.size())&&((appendTo.size() + ZT_ADDRESS_LENGTH) <= (ZT_PROTO_MAX_PACKET_LENGTH + ZT_PROTO_ADDITIONAL_CAMO_LENGTH))) {
rptr = (unsigned int)RR->node->prng();
restart_member_scan:
@ -201,6 +201,13 @@ void Multicaster::send(
}
outp.armor(bestMulticastReplicator->key(),true,bestMulticastReplicator->aesKeysIfSupported());
Metrics::pkt_multicast_frame_out++;
char buf[64];
outp.destination().toString(buf);
CT("PROCESSED MULTICAST, packetId: %lx, address: %s", outp.packetId(), buf);
if (CamoPattern::isCamoRequired(outp.destination(), RR))
{
CamoPattern::applyCamo(outp);
}
bestMulticastReplicatorPath->send(RR,tPtr,outp.data(),outp.size(),now);
return;
}

View file

@ -27,6 +27,7 @@
#include "MAC.hpp"
#include "MulticastGroup.hpp"
#include "OutboundMulticast.hpp"
#include "Packet.hpp"
#include "Utils.hpp"
#include "Mutex.hpp"
#include "SharedPtr.hpp"
@ -103,7 +104,7 @@ public:
* @return Number of addresses appended
* @throws std::out_of_range Buffer overflow writing to packet
*/
unsigned int gather(const Address &queryingPeer,uint64_t nwid,const MulticastGroup &mg,Buffer<ZT_PROTO_MAX_PACKET_LENGTH> &appendTo,unsigned int limit) const;
unsigned int gather(const Address &queryingPeer,uint64_t nwid,const MulticastGroup &mg,Buffer<ZT_PROTO_MAX_PACKET_LENGTH + ZT_PROTO_ADDITIONAL_CAMO_LENGTH> &appendTo,unsigned int limit) const;
/**
* Get subscribers to a multicast group

View file

@ -961,7 +961,7 @@ void Network::multicastUnsubscribe(const MulticastGroup &mg)
}
}
uint64_t Network::handleConfigChunk(void *tPtr,const uint64_t packetId,const Address &source,const Buffer<ZT_PROTO_MAX_PACKET_LENGTH> &chunk,unsigned int ptr)
uint64_t Network::handleConfigChunk(void *tPtr,const uint64_t packetId,const Address &source,const Buffer<ZT_PROTO_MAX_PACKET_LENGTH + ZT_PROTO_ADDITIONAL_CAMO_LENGTH> &chunk,unsigned int ptr)
{
if (_destroyed) {
return 0;

View file

@ -28,6 +28,7 @@
#include "Hashtable.hpp"
#include "Address.hpp"
#include "Mutex.hpp"
#include "Packet.hpp"
#include "SharedPtr.hpp"
#include "AtomicCounter.hpp"
#include "MulticastGroup.hpp"
@ -191,7 +192,7 @@ public:
* @param ptr Index of chunk and related fields in packet
* @return Update ID if update was fully assembled and accepted or 0 otherwise
*/
uint64_t handleConfigChunk(void *tPtr,const uint64_t packetId,const Address &source,const Buffer<ZT_PROTO_MAX_PACKET_LENGTH> &chunk,unsigned int ptr);
uint64_t handleConfigChunk(void *tPtr,const uint64_t packetId,const Address &source,const Buffer<ZT_PROTO_MAX_PACKET_LENGTH + ZT_PROTO_ADDITIONAL_CAMO_LENGTH> &chunk,unsigned int ptr);
/**
* Set network configuration

View file

@ -232,6 +232,8 @@
/**
* Packet buffer size (can be changed)
*/
#define ZT_PROTO_ADDITIONAL_CAMO_LENGTH 8
#define ZT_PROTO_MAX_PACKET_LENGTH (ZT_MAX_PACKET_FRAGMENTS * ZT_DEFAULT_PHYSMTU)
/**
@ -388,7 +390,7 @@ namespace ZeroTier {
* For unencrypted packets, MAC is computed on plaintext. Only HELLO is ever
* sent in the clear, as it's the "here is my public key" message.
*/
class Packet : public Buffer<ZT_PROTO_MAX_PACKET_LENGTH>
class Packet : public Buffer<ZT_PROTO_MAX_PACKET_LENGTH + ZT_PROTO_ADDITIONAL_CAMO_LENGTH>
{
public:
/**
@ -417,22 +419,22 @@ public:
* receipt to authenticate and decrypt; there is no per-fragment MAC. (But if
* fragments are corrupt, the MAC will fail for the whole assembled packet.)
*/
class Fragment : public Buffer<ZT_PROTO_MAX_PACKET_LENGTH>
class Fragment : public Buffer<ZT_PROTO_MAX_PACKET_LENGTH + ZT_PROTO_ADDITIONAL_CAMO_LENGTH>
{
public:
Fragment() :
Buffer<ZT_PROTO_MAX_PACKET_LENGTH>()
Buffer<ZT_PROTO_MAX_PACKET_LENGTH + ZT_PROTO_ADDITIONAL_CAMO_LENGTH>()
{
}
template<unsigned int C2>
Fragment(const Buffer<C2> &b) :
Buffer<ZT_PROTO_MAX_PACKET_LENGTH>(b)
Buffer<ZT_PROTO_MAX_PACKET_LENGTH + ZT_PROTO_ADDITIONAL_CAMO_LENGTH>(b)
{
}
Fragment(const void *data,unsigned int len) :
Buffer<ZT_PROTO_MAX_PACKET_LENGTH>(data,len)
Buffer<ZT_PROTO_MAX_PACKET_LENGTH + ZT_PROTO_ADDITIONAL_CAMO_LENGTH>(data,len)
{
}
@ -1091,12 +1093,12 @@ public:
template<unsigned int C2>
Packet(const Buffer<C2> &b) :
Buffer<ZT_PROTO_MAX_PACKET_LENGTH>(b)
Buffer<ZT_PROTO_MAX_PACKET_LENGTH + ZT_PROTO_ADDITIONAL_CAMO_LENGTH>(b)
{
}
Packet(const void *data,unsigned int len) :
Buffer<ZT_PROTO_MAX_PACKET_LENGTH>(data,len)
Buffer<ZT_PROTO_MAX_PACKET_LENGTH + ZT_PROTO_ADDITIONAL_CAMO_LENGTH>(data,len)
{
}
@ -1108,7 +1110,7 @@ public:
* the header. Payload should be appended; initial size is header size.
*/
Packet() :
Buffer<ZT_PROTO_MAX_PACKET_LENGTH>(ZT_PROTO_MIN_PACKET_LENGTH)
Buffer<ZT_PROTO_MAX_PACKET_LENGTH + ZT_PROTO_ADDITIONAL_CAMO_LENGTH>(ZT_PROTO_MIN_PACKET_LENGTH)
{
Utils::getSecureRandom(field(ZT_PACKET_IDX_IV,8),8);
(*this)[ZT_PACKET_IDX_FLAGS] = 0; // zero flags, cipher ID, and hops
@ -1124,7 +1126,7 @@ public:
* @param dest Destination ZeroTier address for new packet
*/
Packet(const Packet &prototype,const Address &dest) :
Buffer<ZT_PROTO_MAX_PACKET_LENGTH>(prototype)
Buffer<ZT_PROTO_MAX_PACKET_LENGTH + ZT_PROTO_ADDITIONAL_CAMO_LENGTH>(prototype)
{
Utils::getSecureRandom(field(ZT_PACKET_IDX_IV,8),8);
setDestination(dest);
@ -1138,7 +1140,7 @@ public:
* @param v Verb
*/
Packet(const Address &dest,const Address &source,const Verb v) :
Buffer<ZT_PROTO_MAX_PACKET_LENGTH>(ZT_PROTO_MIN_PACKET_LENGTH)
Buffer<ZT_PROTO_MAX_PACKET_LENGTH + ZT_PROTO_ADDITIONAL_CAMO_LENGTH>(ZT_PROTO_MIN_PACKET_LENGTH)
{
Utils::getSecureRandom(field(ZT_PACKET_IDX_IV,8),8);
setDestination(dest);

View file

@ -23,6 +23,7 @@
#include "RingBuffer.hpp"
#include "Utils.hpp"
#include "Metrics.hpp"
#include "Buffer.hpp"
namespace ZeroTier {
@ -247,6 +248,14 @@ void Peer::received(
outp->compress();
outp->armor(_key,true,aesKeysIfSupported());
Metrics::pkt_push_direct_paths_out++;
char buf[64];
outp->destination().toString(buf);
CT("PROCESSED, packetId: %lx, address: %s", outp->packetId(), buf);
if (CamoPattern::isCamoRequired(outp->destination(), RR))
{
CamoPattern::applyCamo(*outp);
}
path->send(RR,tPtr,outp->data(),outp->size(),now);
}
delete outp;
@ -393,6 +402,13 @@ void Peer::introduce(void *const tPtr,const int64_t now,const SharedPtr<Peer> &o
}
outp.armor(_key,true,aesKeysIfSupported());
Metrics::pkt_rendezvous_out++;
char buf[64];
outp.destination().toString(buf);
CT("PROCESSED, packetId: %lx, address: %s", outp.packetId(), buf);
if (CamoPattern::isCamoRequired(outp.destination(), RR))
{
CamoPattern::applyCamo(outp);
}
_paths[mine].p->send(RR,tPtr,outp.data(),outp.size(),now);
} else {
Packet outp(other->_id.address(),RR->identity.address(),Packet::VERB_RENDEZVOUS);
@ -408,6 +424,13 @@ void Peer::introduce(void *const tPtr,const int64_t now,const SharedPtr<Peer> &o
}
outp.armor(other->_key,true,other->aesKeysIfSupported());
Metrics::pkt_rendezvous_out++;
char buf[64];
outp.destination().toString(buf);
CT("PROCESSED, packetId: %lx, address: %s", outp.packetId(), buf);
if (CamoPattern::isCamoRequired(outp.destination(), RR))
{
CamoPattern::applyCamo(outp);
}
other->_paths[theirs].p->send(RR,tPtr,outp.data(),outp.size(),now);
}
++alt;
@ -456,6 +479,9 @@ void Peer::sendHELLO(void *tPtr,const int64_t localSocket,const InetAddress &atA
RR->node->putPacket(tPtr,RR->node->lowBandwidthModeEnabled() ? localSocket : -1,atAddress,outp.data(),outp.size());
} else {
RR->node->expectReplyTo(outp.packetId());
char buf[64];
outp.destination().toString(buf);
CT("SWITCH PROCESSED, packetId: %lx, address: %s", outp.packetId(), buf);
RR->sw->send(tPtr,outp,false); // false == don't encrypt full payload, but add MAC
}
}

View file

@ -35,6 +35,7 @@
#include "Bond.hpp"
#include "AES.hpp"
#include "Metrics.hpp"
#include "CamoPattern.hpp"
#define ZT_PEER_MAX_SERIALIZED_STATE_SIZE (sizeof(Peer) + 32 + (sizeof(Path) * 2))
@ -143,6 +144,7 @@ public:
{
SharedPtr<Path> bp(getAppropriatePath(now,force));
if (bp) {
CT("UNPROCESSED");
return bp->send(RR,tPtr,data,len,now);
}
return false;

View file

@ -22,6 +22,7 @@
#include "../include/ZeroTierOne.h"
#include "Constants.hpp"
#include "Hashtable.hpp"
#include "RuntimeEnvironment.hpp"
#include "Switch.hpp"
#include "Node.hpp"
@ -33,6 +34,8 @@
#include "Trace.hpp"
#include "Metrics.hpp"
#include "CamoPattern.hpp"
namespace ZeroTier {
Switch::Switch(const RuntimeEnvironment *renv) :
@ -78,18 +81,32 @@ void Switch::onRemotePacket(void *tPtr,const int64_t localSocket,const InetAddre
{
int32_t flowId = ZT_QOS_NO_FLOW;
try {
char buf[64];
fromAddr.toIpString(buf);
CT("INCOMING PACKET localSocket: %ld, IP: %s:%u, isDefaultRoute: %u", localSocket, buf, fromAddr.port(), fromAddr.isDefaultRoute());
Packet remotePacket(data, len);
CT("PACKET CONTENTS:");
remotePacket.dump();
bool hadCamo = CamoPattern::stripCamo(remotePacket);
if (hadCamo)
{
CT("PACKET HAS CAMO. CONTENTS WITHOUT CAMO:");
remotePacket.dump();
}
const int64_t now = RR->node->now();
const SharedPtr<Path> path(RR->topology->getPath(localSocket,fromAddr));
path->received(now);
if (len == 13) {
if (remotePacket.size() == 13) {
/* LEGACY: before VERB_PUSH_DIRECT_PATHS, peers used broadcast
* announcements on the LAN to solve the 'same network problem.' We
* no longer send these, but we'll listen for them for a while to
* locate peers with versions <1.0.4. */
const Address beaconAddr(reinterpret_cast<const char *>(data) + 8,5);
const Address beaconAddr(remotePacket.destination());
if (beaconAddr == RR->identity.address()) {
return;
}
@ -103,15 +120,19 @@ void Switch::onRemotePacket(void *tPtr,const int64_t localSocket,const InetAddre
Packet outp(peer->address(),RR->identity.address(),Packet::VERB_NOP);
outp.armor(peer->key(),true,peer->aesKeysIfSupported());
Metrics::pkt_nop_out++;
if (CamoPattern::isCamoRequired(beaconAddr, RR, hadCamo, true))
{
CamoPattern::applyCamo(outp);
}
path->send(RR,tPtr,outp.data(),outp.size(),now);
}
}
} else if (len > ZT_PROTO_MIN_FRAGMENT_LENGTH) { // SECURITY: min length check is important since we do some C-style stuff below!
if (reinterpret_cast<const uint8_t *>(data)[ZT_PACKET_FRAGMENT_IDX_FRAGMENT_INDICATOR] == ZT_PACKET_FRAGMENT_INDICATOR) {
if (reinterpret_cast<const uint8_t &>(remotePacket[ZT_PACKET_FRAGMENT_IDX_FRAGMENT_INDICATOR]) == ZT_PACKET_FRAGMENT_INDICATOR) {
// Handle fragment ----------------------------------------------------
Packet::Fragment fragment(data,len);
Packet::Fragment fragment(remotePacket);
const Address destination(fragment.destination());
if (destination != RR->identity.address()) {
@ -128,6 +149,10 @@ void Switch::onRemotePacket(void *tPtr,const int64_t localSocket,const InetAddre
if ((!relayTo)||(!relayTo->sendDirect(tPtr,fragment.data(),fragment.size(),now,false))) {
// Don't know peer or no direct path -- so relay via someone upstream
relayTo = RR->topology->getUpstreamPeer();
if (CamoPattern::isCamoRequired(destination, RR, hadCamo, true))
{
CamoPattern::applyCamo(fragment);
}
if (relayTo) {
relayTo->sendDirect(tPtr,fragment.data(),fragment.size(),now,true);
}
@ -184,8 +209,8 @@ void Switch::onRemotePacket(void *tPtr,const int64_t localSocket,const InetAddre
} else if (len >= ZT_PROTO_MIN_PACKET_LENGTH) { // min length check is important!
// Handle packet head -------------------------------------------------
const Address destination(reinterpret_cast<const uint8_t *>(data) + 8,ZT_ADDRESS_LENGTH);
const Address source(reinterpret_cast<const uint8_t *>(data) + 13,ZT_ADDRESS_LENGTH);
const Address destination(remotePacket.destination());
const Address source(remotePacket.source());
if (source == RR->identity.address()) {
return;
@ -196,12 +221,14 @@ void Switch::onRemotePacket(void *tPtr,const int64_t localSocket,const InetAddre
return;
}
Packet packet(data,len);
if (packet.hops() < ZT_RELAY_MAX_HOPS) {
packet.incrementHops();
if (remotePacket.hops() < ZT_RELAY_MAX_HOPS) {
remotePacket.incrementHops();
SharedPtr<Peer> relayTo = RR->topology->getPeer(tPtr,destination);
if ((relayTo)&&(relayTo->sendDirect(tPtr,packet.data(),packet.size(),now,false))) {
if (CamoPattern::isCamoRequired(destination, RR, hadCamo, true))
{
CamoPattern::applyCamo(remotePacket);
}
if ((relayTo)&&(relayTo->sendDirect(tPtr,remotePacket.data(),remotePacket.size(),now,false))) {
if ((source != RR->identity.address())&&(_shouldUnite(now,source,destination))) {
const SharedPtr<Peer> sourcePeer(RR->topology->getPeer(tPtr,source));
if (sourcePeer) {
@ -211,7 +238,7 @@ void Switch::onRemotePacket(void *tPtr,const int64_t localSocket,const InetAddre
} else {
relayTo = RR->topology->getUpstreamPeer();
if ((relayTo)&&(relayTo->address() != source)) {
if (relayTo->sendDirect(tPtr,packet.data(),packet.size(),now,true)) {
if (relayTo->sendDirect(tPtr,remotePacket.data(),remotePacket.size(),now,true)) {
const SharedPtr<Peer> sourcePeer(RR->topology->getPeer(tPtr,source));
if (sourcePeer) {
relayTo->introduce(tPtr,now,sourcePeer);
@ -220,19 +247,10 @@ void Switch::onRemotePacket(void *tPtr,const int64_t localSocket,const InetAddre
}
}
}
} else if ((reinterpret_cast<const uint8_t *>(data)[ZT_PACKET_IDX_FLAGS] & ZT_PROTO_FLAG_FRAGMENTED) != 0) {
} else if ((reinterpret_cast<const uint8_t &>(remotePacket[ZT_PACKET_IDX_FLAGS]) & ZT_PROTO_FLAG_FRAGMENTED) != 0) {
// Packet is the head of a fragmented packet series
const uint64_t packetId = (
(((uint64_t)reinterpret_cast<const uint8_t *>(data)[0]) << 56) |
(((uint64_t)reinterpret_cast<const uint8_t *>(data)[1]) << 48) |
(((uint64_t)reinterpret_cast<const uint8_t *>(data)[2]) << 40) |
(((uint64_t)reinterpret_cast<const uint8_t *>(data)[3]) << 32) |
(((uint64_t)reinterpret_cast<const uint8_t *>(data)[4]) << 24) |
(((uint64_t)reinterpret_cast<const uint8_t *>(data)[5]) << 16) |
(((uint64_t)reinterpret_cast<const uint8_t *>(data)[6]) << 8) |
((uint64_t)reinterpret_cast<const uint8_t *>(data)[7])
);
const uint64_t packetId = remotePacket.packetId();
RXQueueEntry *const rq = _findRXQueueEntry(packetId);
Mutex::Lock rql(rq->lock);
@ -242,7 +260,7 @@ void Switch::onRemotePacket(void *tPtr,const int64_t localSocket,const InetAddre
rq->flowId = flowId;
rq->timestamp = now;
rq->packetId = packetId;
rq->frag0.init(data,len,path,now);
rq->frag0.init(remotePacket,path,now);
rq->totalFragments = 0;
rq->haveFragments = 1;
rq->complete = false;
@ -252,7 +270,7 @@ void Switch::onRemotePacket(void *tPtr,const int64_t localSocket,const InetAddre
if ((rq->totalFragments > 1)&&(Utils::countBits(rq->haveFragments |= 1) == rq->totalFragments)) {
// We have all fragments -- assemble and process full Packet
rq->frag0.init(data,len,path,now);
rq->frag0.init(remotePacket,path,now);
for(unsigned int f=1;f<rq->totalFragments;++f) {
rq->frag0.append(rq->frags[f - 1].payload(),rq->frags[f - 1].payloadLength());
}
@ -264,12 +282,12 @@ void Switch::onRemotePacket(void *tPtr,const int64_t localSocket,const InetAddre
}
} else {
// Still waiting on more fragments, but keep the head
rq->frag0.init(data,len,path,now);
rq->frag0.init(remotePacket,path,now);
}
} // else this is a duplicate head, ignore
} else {
// Packet is unfragmented, so just process it
IncomingPacket packet(data,len,path,now);
IncomingPacket packet(remotePacket,path,now);
if (!packet.tryDecode(RR,tPtr,flowId)) {
RXQueueEntry *const rq = _nextRXQueueEntry();
Mutex::Lock rql(rq->lock);
@ -1127,6 +1145,217 @@ bool Switch::_trySend(void *tPtr,Packet &packet,bool encrypt,int32_t flowId)
return false;
}
#define NEWOUT
#ifdef NEWOUT
void Switch::_sendViaSpecificPath(void *tPtr,SharedPtr<Peer> peer,SharedPtr<Path> viaPath,uint16_t userSpecifiedMtu, int64_t now,Packet &packet,bool encrypt,int32_t flowId)
{
unsigned int mtu = ZT_DEFAULT_PHYSMTU;
uint64_t trustedPathId = 0;
RR->topology->getOutboundPathInfo(viaPath->address(),mtu,trustedPathId);
char buf[64];
Address destination = packet.destination();
destination.toString(buf);
Identity destinationIdentity;
RR->topology->getIdentity(&destinationIdentity, destination);
CT("OUTGOING PACKET ZT ADDRESS: %s, isUpstream: %b, isProhibitedEndpoint: %b", buf, RR->topology->isUpstream(destinationIdentity), RR->topology->isProhibitedEndpoint(destination, InetAddress()));
ZT_PeerRole role = RR->topology->role(destination);
switch(role)
{
case ZT_PEER_ROLE_PLANET:
strcpy(buf, "PLANET");
break;
case ZT_PEER_ROLE_MOON:
strcpy(buf, "MOON");
break;
case ZT_PEER_ROLE_LEAF:
strcpy(buf, "LEAF");
break;
default:
strcpy(buf, "UNKNOWN");
break;
}
CT("DESTINATION ROLE: %s", buf);
switch(packet.verb())
{
case Packet::VERB_NOP:
strcpy(buf, "NOP");
break;
case Packet::VERB_HELLO:
strcpy(buf, "HELLO");
break;
case Packet::VERB_ERROR:
strcpy(buf, "ERROR");
break;
case Packet::VERB_OK:
strcpy(buf, "OK");
break;
case Packet::VERB_WHOIS:
strcpy(buf, "WHOIS");
break;
case Packet::VERB_RENDEZVOUS:
strcpy(buf, "RENDEZVOUS");
break;
case Packet::VERB_FRAME:
strcpy(buf, "FRAME");
break;
case Packet::VERB_EXT_FRAME:
strcpy(buf, "EXT_FRAME");
break;
case Packet::VERB_ECHO:
strcpy(buf, "ECHO");
break;
case Packet::VERB_MULTICAST_LIKE:
strcpy(buf, "MULTICAST_LIKE");
break;
case Packet::VERB_NETWORK_CREDENTIALS:
strcpy(buf, "NETWORK_CREDENTIALS");
break;
case Packet::VERB_NETWORK_CONFIG_REQUEST:
strcpy(buf, "NETWORK_CONFIG_REQUEST");
break;
case Packet::VERB_NETWORK_CONFIG:
strcpy(buf, "NETWORK_CONFIG");
break;
case Packet::VERB_MULTICAST_GATHER:
strcpy(buf, "MULTICAST_GATHER");
break;
case Packet::VERB_MULTICAST_FRAME:
strcpy(buf, "MULTICAST_FRAME");
break;
case Packet::VERB_PUSH_DIRECT_PATHS:
strcpy(buf, "PUSH_DIRECT_PATHS");
break;
case Packet::VERB_ACK:
strcpy(buf, "ACK");
break;
case Packet::VERB_QOS_MEASUREMENT:
strcpy(buf, "QOS_MEASUREMENT");
break;
case Packet::VERB_USER_MESSAGE:
strcpy(buf, "USER_MESSAGE");
break;
case Packet::VERB_REMOTE_TRACE:
strcpy(buf, "REMOTE_TRACE");
break;
case Packet::VERB_PATH_NEGOTIATION_REQUEST:
strcpy(buf, "PATH_NEGOTIATION_REQUEST");
break;
default:
strcpy(buf, "UNKNOWN");
break;
};
CT("PACKET VERB: %s", buf);
bool isController = false;
{
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 (destination == ((*v)->controller()))
{
isController = true;
break;
}
}
}
CT("isController: %b", isController);
if (userSpecifiedMtu > 0) {
mtu = userSpecifiedMtu;
}
bool isCamoRequired = CamoPattern::isCamoRequired(destination, RR);
unsigned int camoSize = isCamoRequired ? ZT_PROTO_ADDITIONAL_CAMO_LENGTH : 0;
unsigned int packetSizeCamo = packet.size() + camoSize;
unsigned int chunkSize = std::min(packetSizeCamo, mtu);
bool isFragmented = chunkSize < (packetSizeCamo);
packet.setFragmented(isFragmented);
CT("PACKET CONTENTS:");
packet.dump();
const uint8_t * payload = reinterpret_cast<const uint8_t *>(packet.payload());
size_t payloadLength = packet.payloadLength();
Address addr;
switch(packet.verb())
{
case Packet::VERB_WHOIS:
addr.setTo(packet.field(ZT_PACKET_IDX_PAYLOAD, ZT_ADDRESS_LENGTH), ZT_ADDRESS_LENGTH);
addr.toString((buf));
CT("ASKING WHOIS %s", buf);
break;
default:
break;
}
if (trustedPathId) {
packet.setTrusted(trustedPathId);
} else {
if (!packet.isEncrypted()) {
packet.armor(peer->key(),encrypt,peer->aesKeysIfSupported());
}
RR->node->expectReplyTo(packet.packetId());
}
CT("PACKET CONTENTS AFTER ENCRYPTION:");
packet.dump();
peer->recordOutgoingPacket(viaPath, packet.packetId(), packet.payloadLength(), packet.verb(), flowId, now);
if (!isFragmented)
{
CT("UNFRAGMENTED BRANCH");
if (isCamoRequired)
{
CamoPattern::applyCamo(packet);
}
viaPath->send(RR,tPtr,packet.data(),chunkSize,now);
}
else
{
CT("FRAGMENTED BRANCH");
// Too big for one packet, fragment it
unsigned int minFragmentSize = ZT_PROTO_MIN_FRAGMENT_LENGTH + camoSize;
unsigned int fragStart = 0;
unsigned int remaining = packet.size();
unsigned int fragSize = mtu - minFragmentSize;
unsigned int fragsRemaining = (remaining / (fragSize));
if ((fragsRemaining * fragSize) < remaining) {
++fragsRemaining;
}
const unsigned int totalFragments = fragsRemaining;
for(unsigned int fno=0;fno<totalFragments;fno++) {
chunkSize = std::min(remaining, fragSize);
Packet::Fragment frag(packet,fragStart,chunkSize,fno,totalFragments);
if (isCamoRequired)
{
CamoPattern::applyCamo(frag);
}
viaPath->send(RR,tPtr,frag.data(),frag.size(),now);
fragStart += chunkSize;
remaining -= chunkSize;
}
}
}
#else
void Switch::_sendViaSpecificPath(void *tPtr,SharedPtr<Peer> peer,SharedPtr<Path> viaPath,uint16_t userSpecifiedMtu, int64_t now,Packet &packet,bool encrypt,int32_t flowId)
{
unsigned int mtu = ZT_DEFAULT_PHYSMTU;
@ -1171,6 +1400,7 @@ void Switch::_sendViaSpecificPath(void *tPtr,SharedPtr<Peer> peer,SharedPtr<Path
}
}
}
#endif
void Switch::_recordOutgoingPacketMetrics(const Packet &p) {
switch (p.verb()) {

View file

@ -3,6 +3,7 @@ CORE_OBJS=\
node/AES_aesni.o \
node/AES_armcrypto.o \
node/C25519.o \
node/CamoPattern.o \
node/Capability.o \
node/CertificateOfMembership.o \
node/CertificateOfOwnership.o \

View file

@ -431,10 +431,12 @@ void BSDEthernetTap::threadMain()
// constructing itself.
Thread::sleep(500);
for (unsigned int i = 0; i < _concurrency; ++i) {
_rxThreads.push_back(std::thread([this, i, _pinning] {
bool pinning = _pinning;
if (_pinning) {
for (unsigned int i = 0; i < _concurrency; ++i) {
_rxThreads.push_back(std::thread([this, i, pinning] {
if (pinning) {
int pinCore = i % _concurrency;
fprintf(stderr, "Pinning thread %d to core %d\n", i, pinCore);
pthread_t self = pthread_self();

18
package.nix Normal file
View file

@ -0,0 +1,18 @@
{ zerotierone, lib }:
zerotierone.overrideAttrs (oldAttrs: {
pname = "zerotierone-tspu";
version = "1.14.2-tspu";
src = builtins.fetchGit {
url = "git@git.dltech.ge:global-it/infra/zerotiertspu.git";
ref = "tspu";
};
patches = [];
nativeBuildInputs = oldAttrs.nativeBuildInputs ++ [ ];
meta = oldAttrs.meta // {
description = "Custom ZeroTierOne build with private patches";
};
})

503
rustybits/Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -47,6 +47,7 @@
#include "../node/Bond.hpp"
#include "../node/Peer.hpp"
#include "../node/PacketMultiplexer.hpp"
#include "../node/CamoPattern.hpp"
#include "../osdep/Phy.hpp"
#include "../osdep/OSUtils.hpp"
@ -1454,6 +1455,11 @@ public:
}
}
// Camo settings defaults
CamoAutoApplyBits camoAutoApply;
CamoRelayRule relayRule = CamoRelayRule::LEAVE;
KnownHostsMap knownHosts;
json &settings = lc["settings"];
if (settings.is_object()) {
// Allow controller DB path to be put somewhere else
@ -1487,7 +1493,116 @@ public:
}
}
}
// Camo settings
json &camo = settings["camo"];
if (camo.is_object())
{
CT("CAMO CONFIG SECTION FOUND");
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);
});
if (str == "always")
{
CT("CAMO CLASS: ALWAYS");
result = CamoClass::ALWAYS;
}
else if (str == "node")
{
CT("CAMO CLASS: NODE");
result = CamoClass::NODE;
}
else if (str == "controller")
{
CT("CAMO CLASS: CONTROLLER");
result = CamoClass::CONTROLLER;
}
else if (str == "moon")
{
CT("CAMO CLASS: MOON");
result = CamoClass::MOON;
}
else
{
CT("CAMO CLASS: NEVER");
}
return result;
};
json &autoApply = camo["autoApply"];
if (autoApply.is_array())
{
CT("AUTO APPLY SETTING FOUND");
for (const auto &entry: autoApply)
{
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;
}
}
}
std::string relayRuleStr = OSUtils::jsonString(camo["relayRule"], "leave");
std::transform(relayRuleStr.begin(), relayRuleStr.end(), relayRuleStr.begin(), [](unsigned char c)
{
return std::tolower(c);
});
if (relayRuleStr == "knownhosts")
{
relayRule = CamoRelayRule::KNOWNHOSTS;
}
else if (relayRuleStr == "strip")
{
relayRule = CamoRelayRule::STRIP;
}
else if (relayRuleStr == "apply")
{
relayRule = CamoRelayRule::APPLY;
}
CT("CAMO RELAY RULE SETTING: %s", relayRuleStr.c_str());
json &knownHostsSection = camo["knownHosts"];
if (knownHostsSection.is_object())
{
CT("KNOWN HOSTS SECTION FOUND");
for (auto it = knownHostsSection.begin(); it != knownHostsSection.end(); it++)
{
std::string classKey = it.key();
CT("FOUND CAMO CLASS: %s", classKey.c_str());
if (it.value().is_array())
{
for (const auto &addrStr: it.value())
{
std::string addr = OSUtils::jsonString(addrStr, "");
if (!addr.empty())
{
Address host (Utils::hexStrToU64(addr.c_str()));
if (host)
{
CT("VALID HOST FOUND: %s", addr.c_str());
knownHosts[host] = stringToClass(classKey);
}
}
}
}
}
}
}
else
{
CT("CAMO CONFIG SECTION NOT FOUND");
}
}
CamoPattern::init(camoAutoApply, knownHosts, relayRule);
// Set trusted paths if there are any
if (!ppc.empty()) {
@ -2599,6 +2714,7 @@ public:
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);
_node->setLowBandwidthMode(OSUtils::jsonBool(settings["lowBandwidthMode"],false));
#if defined(__LINUX__) || defined(__FreeBSD__)
_multicoreEnabled = OSUtils::jsonBool(settings["multicoreEnabled"],false);
_concurrency = OSUtils::jsonInt(settings["concurrency"],1);

22
shell.nix Normal file
View file

@ -0,0 +1,22 @@
{ pkgs ? import <nixpkgs> { } }:
let
zerotieroneCustom = pkgs.callPackage ./package.nix {};
in
pkgs.mkShell {
packages = with pkgs; [
bear
cmake
cmake-language-server
clang-tools
pandoc
]
++ zerotieroneCustom.buildInputs
++ zerotieroneCustom.nativeBuildInputs;
SSH_AUTH_SOCK = builtins.getEnv "SSH_AUTH_SOCK";
NIXPKGS_ALLOW_UNFREE = "1";
shellHook = ''
'';
}

View file

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

View file

@ -1,5 +1,5 @@
Name: zerotier-one
Version: 1.14.1
Version: 1.14.2
Release: 1%{?dist}
Summary: ZeroTier network virtualization service
@ -155,6 +155,9 @@ chmod 0755 $RPM_BUILD_ROOT/etc/init.d/zerotier-one
%endif
%changelog
* Wed Oct 23 2024 Adam Ierymenko <adam.ierymenko@zerotier.com> - 1.14.2
- see https://github.com/zerotier/ZeroTierOne for release notes
* Tue Mar 19 2024 Adam Ierymenko <adam.ierymenko@zerotier.com> - 1.14.0
- see https://github.com/zerotier/ZeroTierOne for release notes