added camouflaging functionality and instrumentation
This commit is contained in:
parent
cdaf5e5468
commit
083b833bf8
16 changed files with 904 additions and 43 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -151,3 +151,4 @@ result-man
|
||||||
#zerotier devs seem to forgot those
|
#zerotier devs seem to forgot those
|
||||||
rustybits/zerotier-cli
|
rustybits/zerotier-cli
|
||||||
rustybits/zerotier-idtool
|
rustybits/zerotier-idtool
|
||||||
|
.aider*
|
||||||
|
|
|
@ -445,6 +445,11 @@ public:
|
||||||
*/
|
*/
|
||||||
inline unsigned int capacity() const { return C; }
|
inline unsigned int capacity() const { return C; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dump buffer contents to stdout in hex format
|
||||||
|
*/
|
||||||
|
void dump() const;
|
||||||
|
|
||||||
template<unsigned int C2>
|
template<unsigned int C2>
|
||||||
inline bool operator==(const Buffer<C2> &b) const
|
inline bool operator==(const Buffer<C2> &b) const
|
||||||
{
|
{
|
||||||
|
|
168
node/CamoPattern.cpp
Normal file
168
node/CamoPattern.cpp
Normal file
|
@ -0,0 +1,168 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c)2013-2020 ZeroTier, Inc.
|
||||||
|
*
|
||||||
|
* Use of this software is governed by the Business Source License included
|
||||||
|
* in the LICENSE.TXT file in the project's root directory.
|
||||||
|
*
|
||||||
|
* Change Date: 2026-01-01
|
||||||
|
*
|
||||||
|
* On the date above, in accordance with the Business Source License, use
|
||||||
|
* of this software will be governed by version 2.0 of the Apache License.
|
||||||
|
*/
|
||||||
|
/****/
|
||||||
|
|
||||||
|
#include "CamoPattern.hpp"
|
||||||
|
#include <ctime>
|
||||||
|
#include "Topology.hpp"
|
||||||
|
|
||||||
|
namespace ZeroTier {
|
||||||
|
|
||||||
|
// Initialize static members of CamoPattern
|
||||||
|
bool CamoPattern::isInitialized = false;
|
||||||
|
CamoLevel CamoPattern::camoLevel;
|
||||||
|
uint32_t CamoPattern::camoWord;
|
||||||
|
CamoRelayRule CamoPattern::relayRule;
|
||||||
|
std::array<std::array<uint8_t, BYTES_IN_WORD>, PATTERN_COUNT> CamoPattern::camoValues;
|
||||||
|
std::unordered_map<std::array<uint8_t, BYTES_IN_WORD>, size_t> CamoPattern::camoIndices;
|
||||||
|
std::mutex CamoPattern::camoMutex;
|
||||||
|
std::unordered_map<Address, CamoLevel> CamoPattern::knownHosts;
|
||||||
|
|
||||||
|
|
||||||
|
// Implementation of getCamoLevel
|
||||||
|
CamoLevel CamoPattern::getCamoLevel(const Address host, const RuntimeEnvironment * const RR)
|
||||||
|
{
|
||||||
|
CamoLevel result = CamoLevel::INAPPLICABLE;
|
||||||
|
// First check if we already know this host's camo level
|
||||||
|
if (isInitialized)
|
||||||
|
{
|
||||||
|
char buf[64];
|
||||||
|
host.toString(buf);
|
||||||
|
CT("GETTING CAMO LEVEL FOR HOST %s", buf);
|
||||||
|
auto it = knownHosts.find(host);
|
||||||
|
if (it != knownHosts.end()) {
|
||||||
|
result = it->second;
|
||||||
|
CT("HOST IS KNOWN, LEVEL: %u", result);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Host not found in known hosts, run mutex-protected section
|
||||||
|
std::lock_guard<std::mutex> lock(camoMutex);
|
||||||
|
|
||||||
|
// Check again in case another thread added it while we were waiting
|
||||||
|
it = knownHosts.find(host);
|
||||||
|
if (it != knownHosts.end()) {
|
||||||
|
result = it->second;
|
||||||
|
CT("HOST IS KNOWN AFTER LOCK WAITING");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
CT("HOST IS NOT KNOWN");
|
||||||
|
if (!RR->topology->isProhibitedEndpoint(host, InetAddress()))
|
||||||
|
{
|
||||||
|
switch(RR->topology->role(host))
|
||||||
|
{
|
||||||
|
case ZT_PEER_ROLE_PLANET:
|
||||||
|
CT("HOST IS A PLANET");
|
||||||
|
result = CamoLevel::PLANET;
|
||||||
|
break;
|
||||||
|
case ZT_PEER_ROLE_MOON:
|
||||||
|
CT("HOST IS A MOON");
|
||||||
|
result = CamoLevel::MOON;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
result = CamoLevel::NODE;
|
||||||
|
Mutex::Lock _l(RR->node->_networks_m);
|
||||||
|
Hashtable<uint64_t, SharedPtr<Network>>::Iterator i(RR->node->_networks);
|
||||||
|
uint64_t * k = (uint64_t *)0;
|
||||||
|
SharedPtr<Network> *v = (SharedPtr<Network> *)0;
|
||||||
|
while(i.next(k, v))
|
||||||
|
{
|
||||||
|
if (host == ((*v)->controller()))
|
||||||
|
{
|
||||||
|
CT("HOST IS A CONTROLLER");
|
||||||
|
result = CamoLevel::CONTROLLER;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (result == CamoLevel::NODE)
|
||||||
|
{
|
||||||
|
CT("HOST IS A SIMPLE NODE");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
CT("HOST IS A ZT GLOBAL ROOT");
|
||||||
|
}
|
||||||
|
knownHosts[host] = result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Implementation of isCamoRequired
|
||||||
|
bool CamoPattern::isCamoRequired(const Address host, const RuntimeEnvironment * const RR, const bool hadCamo, const bool isRelay)
|
||||||
|
{
|
||||||
|
bool result = false;
|
||||||
|
if (isInitialized && isRelay)
|
||||||
|
{
|
||||||
|
switch(relayRule)
|
||||||
|
{
|
||||||
|
case CamoRelayRule::LEAVE:
|
||||||
|
CT("IS RELAY, APPLYING LEAVE RULE");
|
||||||
|
result = hadCamo;
|
||||||
|
break;
|
||||||
|
case CamoRelayRule::KNOWNHOSTS:
|
||||||
|
CT("IS RELAY, APPLYING KNOWNHOSTS RULE");
|
||||||
|
result = getCamoLevel(host, RR) <= camoLevel;
|
||||||
|
break;
|
||||||
|
case CamoRelayRule::STRIP:
|
||||||
|
CT("IS RELAY, APPLYING STRIP RULE");
|
||||||
|
result = false;
|
||||||
|
break;
|
||||||
|
case CamoRelayRule::APPLY:
|
||||||
|
CT("IS RELAY, APPLYING APPLY RULE");
|
||||||
|
result = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (isInitialized)
|
||||||
|
{
|
||||||
|
result = getCamoLevel(host, RR) <= camoLevel;
|
||||||
|
CT("IS CAMO REQUIRED: %b", result);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Implementation of init
|
||||||
|
void CamoPattern::init(CamoLevel level, uint32_t word, std::unordered_map<Address, CamoLevel> hosts, CamoRelayRule rule)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(camoMutex);
|
||||||
|
if (!isInitialized)
|
||||||
|
{
|
||||||
|
camoLevel = level;
|
||||||
|
camoWord = word;
|
||||||
|
knownHosts = hosts;
|
||||||
|
relayRule = rule;
|
||||||
|
CT("CAMO LEVEL: %u, WORD: %08x, KNOWN HOSTS COUNT: %lu, RELAY RULE: %u", level, word, hosts.size(), rule);
|
||||||
|
std::mt19937 rng(camoWord);
|
||||||
|
for (size_t i = 0; i < PATTERN_COUNT; i++)
|
||||||
|
{
|
||||||
|
uint32_t random = rng();
|
||||||
|
CT("CAMO INDEX: %lu, VALUE: %08x", i, random);
|
||||||
|
for (size_t j = 0; j < BYTES_IN_WORD; j++)
|
||||||
|
{
|
||||||
|
camoValues[i][j] = (random >> (j * 8)) & 0xff;
|
||||||
|
}
|
||||||
|
camoIndices[camoValues[i]] = i;
|
||||||
|
}
|
||||||
|
isInitialized = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace ZeroTier
|
262
node/CamoPattern.hpp
Normal file
262
node/CamoPattern.hpp
Normal file
|
@ -0,0 +1,262 @@
|
||||||
|
/*
|
||||||
|
* 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
|
||||||
|
|
||||||
|
#define CAMO_TRACE
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <array>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <mutex>
|
||||||
|
#include <random>
|
||||||
|
|
||||||
|
#include "Address.hpp"
|
||||||
|
#include "Buffer.hpp"
|
||||||
|
#include "RuntimeEnvironment.hpp"
|
||||||
|
|
||||||
|
#define BYTES_IN_WORD 4
|
||||||
|
#define PATTERN_COUNT 16
|
||||||
|
#define PATTERN_INDEX_MASK 0xf
|
||||||
|
#define NO_CAMO PATTERN_COUNT
|
||||||
|
|
||||||
|
#define ZT_CAMO_DEFAULT_WORDSTR "DLGE"
|
||||||
|
|
||||||
|
namespace std {
|
||||||
|
template<typename T, size_t N>
|
||||||
|
struct hash<std::array<T, N>> {
|
||||||
|
size_t operator()(const std::array<T, N>& array) const {
|
||||||
|
size_t hash = 0;
|
||||||
|
for (const auto& element : array) {
|
||||||
|
hash = hash * 31 + std::hash<T>{}(element);
|
||||||
|
}
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<>
|
||||||
|
struct hash<ZeroTier::Address> {
|
||||||
|
size_t operator()(const ZeroTier::Address& address) const {
|
||||||
|
return std::hash<uint64_t>{}(address.toInt());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Camo functions debug trace macro
|
||||||
|
*/
|
||||||
|
#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 level enum
|
||||||
|
*/
|
||||||
|
enum class CamoLevel {
|
||||||
|
NONE,
|
||||||
|
NODE,
|
||||||
|
CONTROLLER,
|
||||||
|
MOON,
|
||||||
|
PLANET,
|
||||||
|
INAPPLICABLE
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class CamoRelayRule {
|
||||||
|
LEAVE,
|
||||||
|
KNOWNHOSTS,
|
||||||
|
STRIP,
|
||||||
|
APPLY
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper class for packet camouflage operations
|
||||||
|
*/
|
||||||
|
class CamoPattern
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Check if buffer has camouflage applied
|
||||||
|
*
|
||||||
|
* @param buffer Buffer to check
|
||||||
|
* @return True if buffer has camouflage
|
||||||
|
*/
|
||||||
|
template<unsigned int C>
|
||||||
|
static size_t getCamoIndex(const Buffer<C> &buffer)
|
||||||
|
{
|
||||||
|
size_t result = NO_CAMO;
|
||||||
|
std::array<uint8_t, BYTES_IN_WORD> camo;
|
||||||
|
if (buffer.size() > BYTES_IN_WORD * 2)
|
||||||
|
{
|
||||||
|
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())
|
||||||
|
{
|
||||||
|
result = it->second;
|
||||||
|
CT("CAMO DETECTED, INDEX: %u", result);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
CT("CAMO NOT DETECTED");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check the host camo level
|
||||||
|
*
|
||||||
|
* @param host Destination address
|
||||||
|
* @param RR RuntimeEnvironment pointer
|
||||||
|
* @return Camo Level for this host
|
||||||
|
*/
|
||||||
|
static CamoLevel getCamoLevel(const Address host, const RuntimeEnvironment * const RR);
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param host Destination address
|
||||||
|
* @param RR RuntimeEnvironment pointer
|
||||||
|
* @return True if host requires camo
|
||||||
|
*/
|
||||||
|
static bool isCamoRequired(const Address host, const RuntimeEnvironment * const RR, const bool hadCamo = false, const bool isRelay = false);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply camouflage to buffer
|
||||||
|
*
|
||||||
|
* @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_INDEX_MASK;
|
||||||
|
std::array<uint8_t, BYTES_IN_WORD> camo = camoValues[camoIndex];
|
||||||
|
|
||||||
|
// Increase buffer size first to avoid overflow
|
||||||
|
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];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply XOR to the rest of the buffer
|
||||||
|
for (size_t i = BYTES_IN_WORD * 2; i < buffer.size(); i++) {
|
||||||
|
data[i] ^= camo[i % BYTES_IN_WORD];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply XOR to create the camouflage pattern in the second word
|
||||||
|
for (size_t i = 0; i < BYTES_IN_WORD; i++) {
|
||||||
|
data[i + BYTES_IN_WORD] = data[i] ^ camo[i];
|
||||||
|
}
|
||||||
|
CT("PACKET CONTENTS AFTER APPLYING CAMO:");
|
||||||
|
buffer.dump();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove camouflage from buffer
|
||||||
|
*
|
||||||
|
* This decreases buffer length by removing 'CAMO' from the end
|
||||||
|
*
|
||||||
|
* @param buffer Buffer to remove camouflage from
|
||||||
|
*/
|
||||||
|
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());
|
||||||
|
std::array<uint8_t, BYTES_IN_WORD> 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;
|
||||||
|
for (size_t i = 0; i < BYTES_IN_WORD; i++)
|
||||||
|
{
|
||||||
|
data[i + BYTES_IN_WORD] = data[i + storedWordIndex];
|
||||||
|
}
|
||||||
|
buffer.setSize(buffer.size() - BYTES_IN_WORD);
|
||||||
|
result = true;
|
||||||
|
CT("PACKET CONTENTS AFTER STRIPPING CAMO:");
|
||||||
|
buffer.dump();
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void init(CamoLevel level, uint32_t word, std::unordered_map<Address, CamoLevel> hosts, CamoRelayRule rule);
|
||||||
|
|
||||||
|
|
||||||
|
private:
|
||||||
|
static bool isInitialized;
|
||||||
|
static CamoLevel camoLevel;
|
||||||
|
static uint32_t camoWord;
|
||||||
|
static CamoRelayRule relayRule;
|
||||||
|
static std::array<std::array<uint8_t, BYTES_IN_WORD>, PATTERN_COUNT> camoValues;
|
||||||
|
static std::unordered_map<std::array<uint8_t, BYTES_IN_WORD>, size_t> camoIndices;
|
||||||
|
static std::mutex camoMutex;
|
||||||
|
static std::unordered_map<Address, CamoLevel> knownHosts;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // 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
|
|
@ -39,6 +39,7 @@
|
||||||
#include "Bond.hpp"
|
#include "Bond.hpp"
|
||||||
#include "Metrics.hpp"
|
#include "Metrics.hpp"
|
||||||
#include "PacketMultiplexer.hpp"
|
#include "PacketMultiplexer.hpp"
|
||||||
|
#include "CamoPattern.hpp"
|
||||||
|
|
||||||
namespace ZeroTier {
|
namespace ZeroTier {
|
||||||
|
|
||||||
|
@ -407,6 +408,7 @@ bool IncomingPacket::_doHELLO(const RuntimeEnvironment *RR,void *tPtr,const bool
|
||||||
outp.armor(key,true,peer->aesKeysIfSupported());
|
outp.armor(key,true,peer->aesKeysIfSupported());
|
||||||
Metrics::pkt_error_out++;
|
Metrics::pkt_error_out++;
|
||||||
Metrics::pkt_error_identity_collision_out++;
|
Metrics::pkt_error_identity_collision_out++;
|
||||||
|
CT("UNPROCESSED");
|
||||||
_path->send(RR,tPtr,outp.data(),outp.size(),RR->node->now());
|
_path->send(RR,tPtr,outp.data(),outp.size(),RR->node->now());
|
||||||
} else {
|
} else {
|
||||||
RR->t->incomingPacketMessageAuthenticationFailure(tPtr,_path,pid,fromAddress,hops(),"invalid MAC");
|
RR->t->incomingPacketMessageAuthenticationFailure(tPtr,_path,pid,fromAddress,hops(),"invalid MAC");
|
||||||
|
@ -565,6 +567,7 @@ bool IncomingPacket::_doHELLO(const RuntimeEnvironment *RR,void *tPtr,const bool
|
||||||
outp.armor(peer->key(),true,peer->aesKeysIfSupported());
|
outp.armor(peer->key(),true,peer->aesKeysIfSupported());
|
||||||
peer->recordOutgoingPacket(_path,outp.packetId(),outp.payloadLength(),outp.verb(),ZT_QOS_NO_FLOW,now);
|
peer->recordOutgoingPacket(_path,outp.packetId(),outp.payloadLength(),outp.verb(),ZT_QOS_NO_FLOW,now);
|
||||||
Metrics::pkt_ok_out++;
|
Metrics::pkt_ok_out++;
|
||||||
|
CT("UNPROCESSED, packetId: %lx", outp.packetId());
|
||||||
_path->send(RR,tPtr,outp.data(),outp.size(),now);
|
_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
|
peer->setRemoteVersion(protoVersion,vMajor,vMinor,vRevision); // important for this to go first so received() knows the version
|
||||||
|
@ -725,6 +728,7 @@ bool IncomingPacket::_doWHOIS(const RuntimeEnvironment *RR,void *tPtr,const Shar
|
||||||
if (count > 0) {
|
if (count > 0) {
|
||||||
Metrics::pkt_ok_out++;
|
Metrics::pkt_ok_out++;
|
||||||
outp.armor(peer->key(),true,peer->aesKeysIfSupported());
|
outp.armor(peer->key(),true,peer->aesKeysIfSupported());
|
||||||
|
CT("UNPROCESSED");
|
||||||
_path->send(RR,tPtr,outp.data(),outp.size(),RR->node->now());
|
_path->send(RR,tPtr,outp.data(),outp.size(),RR->node->now());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -955,6 +959,7 @@ bool IncomingPacket::_doEXT_FRAME(const RuntimeEnvironment *RR,void *tPtr,const
|
||||||
outp.armor(peer->key(),true,peer->aesKeysIfSupported());
|
outp.armor(peer->key(),true,peer->aesKeysIfSupported());
|
||||||
peer->recordOutgoingPacket(_path,outp.packetId(),outp.payloadLength(),outp.verb(),ZT_QOS_NO_FLOW,now);
|
peer->recordOutgoingPacket(_path,outp.packetId(),outp.payloadLength(),outp.verb(),ZT_QOS_NO_FLOW,now);
|
||||||
Metrics::pkt_ok_out++;
|
Metrics::pkt_ok_out++;
|
||||||
|
CT("UNPROCESSED");
|
||||||
_path->send(RR,tPtr,outp.data(),outp.size(),RR->node->now());
|
_path->send(RR,tPtr,outp.data(),outp.size(),RR->node->now());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -984,6 +989,7 @@ bool IncomingPacket::_doECHO(const RuntimeEnvironment *RR,void *tPtr,const Share
|
||||||
outp.armor(peer->key(),true,peer->aesKeysIfSupported());
|
outp.armor(peer->key(),true,peer->aesKeysIfSupported());
|
||||||
peer->recordOutgoingPacket(_path,outp.packetId(),outp.payloadLength(),outp.verb(),ZT_QOS_NO_FLOW,now);
|
peer->recordOutgoingPacket(_path,outp.packetId(),outp.payloadLength(),outp.verb(),ZT_QOS_NO_FLOW,now);
|
||||||
Metrics::pkt_ok_out++;
|
Metrics::pkt_ok_out++;
|
||||||
|
CT("UNPROCESSED, packetID: %lx", outp.packetId());
|
||||||
_path->send(RR,tPtr,outp.data(),outp.size(),RR->node->now());
|
_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);
|
peer->received(tPtr,_path,hops(),pid,payloadLength(),Packet::VERB_ECHO,0,Packet::VERB_NOP,false,0,ZT_QOS_NO_FLOW);
|
||||||
|
@ -1180,6 +1186,7 @@ bool IncomingPacket::_doNETWORK_CONFIG_REQUEST(const RuntimeEnvironment *RR,void
|
||||||
outp.armor(peer->key(),true,peer->aesKeysIfSupported());
|
outp.armor(peer->key(),true,peer->aesKeysIfSupported());
|
||||||
Metrics::pkt_error_out++;
|
Metrics::pkt_error_out++;
|
||||||
Metrics::pkt_error_unsupported_op_out++;
|
Metrics::pkt_error_unsupported_op_out++;
|
||||||
|
CT("UNPROCESSED");
|
||||||
_path->send(RR,tPtr,outp.data(),outp.size(),RR->node->now());
|
_path->send(RR,tPtr,outp.data(),outp.size(),RR->node->now());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1204,6 +1211,7 @@ bool IncomingPacket::_doNETWORK_CONFIG(const RuntimeEnvironment *RR,void *tPtr,c
|
||||||
outp.armor(peer->key(),true,peer->aesKeysIfSupported());
|
outp.armor(peer->key(),true,peer->aesKeysIfSupported());
|
||||||
peer->recordOutgoingPacket(_path,outp.packetId(),outp.payloadLength(),outp.verb(),ZT_QOS_NO_FLOW,now);
|
peer->recordOutgoingPacket(_path,outp.packetId(),outp.payloadLength(),outp.verb(),ZT_QOS_NO_FLOW,now);
|
||||||
Metrics::pkt_ok_out++;
|
Metrics::pkt_ok_out++;
|
||||||
|
CT("UNPROCESSED");
|
||||||
_path->send(RR,tPtr,outp.data(),outp.size(),RR->node->now());
|
_path->send(RR,tPtr,outp.data(),outp.size(),RR->node->now());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1247,6 +1255,7 @@ bool IncomingPacket::_doMULTICAST_GATHER(const RuntimeEnvironment *RR,void *tPtr
|
||||||
outp.armor(peer->key(),true,peer->aesKeysIfSupported());
|
outp.armor(peer->key(),true,peer->aesKeysIfSupported());
|
||||||
peer->recordOutgoingPacket(_path,outp.packetId(),outp.payloadLength(),outp.verb(),ZT_QOS_NO_FLOW,now);
|
peer->recordOutgoingPacket(_path,outp.packetId(),outp.payloadLength(),outp.verb(),ZT_QOS_NO_FLOW,now);
|
||||||
Metrics::pkt_ok_out++;
|
Metrics::pkt_ok_out++;
|
||||||
|
CT("UNPROCESSED");
|
||||||
_path->send(RR,tPtr,outp.data(),outp.size(),now);
|
_path->send(RR,tPtr,outp.data(),outp.size(),now);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1320,6 +1329,7 @@ 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);
|
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()))) {
|
if ((flags & 0x08)&&(network->config().isMulticastReplicator(RR->identity.address()))) {
|
||||||
|
CT("UNPROCESSED");
|
||||||
RR->mc->send(tPtr,RR->node->now(),network,peer->address(),to,from,etherType,frameData,frameLen);
|
RR->mc->send(tPtr,RR->node->now(),network,peer->address(),to,from,etherType,frameData,frameLen);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1351,6 +1361,7 @@ bool IncomingPacket::_doMULTICAST_FRAME(const RuntimeEnvironment *RR,void *tPtr,
|
||||||
outp.armor(peer->key(),true,peer->aesKeysIfSupported());
|
outp.armor(peer->key(),true,peer->aesKeysIfSupported());
|
||||||
peer->recordOutgoingPacket(_path,outp.packetId(),outp.payloadLength(),outp.verb(),ZT_QOS_NO_FLOW,now);
|
peer->recordOutgoingPacket(_path,outp.packetId(),outp.payloadLength(),outp.verb(),ZT_QOS_NO_FLOW,now);
|
||||||
Metrics::pkt_ok_out++;
|
Metrics::pkt_ok_out++;
|
||||||
|
CT("UNPROCESSED");
|
||||||
_path->send(RR,tPtr,outp.data(),outp.size(),RR->node->now());
|
_path->send(RR,tPtr,outp.data(),outp.size(),RR->node->now());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1493,6 +1504,7 @@ void IncomingPacket::_sendErrorNeedCredentials(const RuntimeEnvironment *RR,void
|
||||||
outp.armor(peer->key(),true,peer->aesKeysIfSupported());
|
outp.armor(peer->key(),true,peer->aesKeysIfSupported());
|
||||||
Metrics::pkt_error_out++;
|
Metrics::pkt_error_out++;
|
||||||
Metrics::pkt_error_need_membership_cert_out++;
|
Metrics::pkt_error_need_membership_cert_out++;
|
||||||
|
CT("UNPROCESSED");
|
||||||
_path->send(RR,tPtr,outp.data(),outp.size(),RR->node->now());
|
_path->send(RR,tPtr,outp.data(),outp.size(),RR->node->now());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
* Init packet-in-decode in place
|
||||||
*
|
*
|
||||||
|
@ -91,6 +107,22 @@ public:
|
||||||
_authenticated = false;
|
_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
|
* Attempt to decode this packet
|
||||||
*
|
*
|
||||||
|
|
|
@ -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 char *p;
|
||||||
unsigned int added = 0,i,k,rptr,totalKnown = 0;
|
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) {
|
if (!limit) {
|
||||||
return 0;
|
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
|
// Members are returned in random order so that repeated gather queries
|
||||||
// will return different subsets of a large multicast group.
|
// will return different subsets of a large multicast group.
|
||||||
k = 0;
|
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();
|
rptr = (unsigned int)RR->node->prng();
|
||||||
|
|
||||||
restart_member_scan:
|
restart_member_scan:
|
||||||
|
@ -201,6 +201,7 @@ void Multicaster::send(
|
||||||
}
|
}
|
||||||
outp.armor(bestMulticastReplicator->key(),true,bestMulticastReplicator->aesKeysIfSupported());
|
outp.armor(bestMulticastReplicator->key(),true,bestMulticastReplicator->aesKeysIfSupported());
|
||||||
Metrics::pkt_multicast_frame_out++;
|
Metrics::pkt_multicast_frame_out++;
|
||||||
|
CT("UNPROCESSED");
|
||||||
bestMulticastReplicatorPath->send(RR,tPtr,outp.data(),outp.size(),now);
|
bestMulticastReplicatorPath->send(RR,tPtr,outp.data(),outp.size(),now);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
#include "MAC.hpp"
|
#include "MAC.hpp"
|
||||||
#include "MulticastGroup.hpp"
|
#include "MulticastGroup.hpp"
|
||||||
#include "OutboundMulticast.hpp"
|
#include "OutboundMulticast.hpp"
|
||||||
|
#include "Packet.hpp"
|
||||||
#include "Utils.hpp"
|
#include "Utils.hpp"
|
||||||
#include "Mutex.hpp"
|
#include "Mutex.hpp"
|
||||||
#include "SharedPtr.hpp"
|
#include "SharedPtr.hpp"
|
||||||
|
@ -103,7 +104,7 @@ public:
|
||||||
* @return Number of addresses appended
|
* @return Number of addresses appended
|
||||||
* @throws std::out_of_range Buffer overflow writing to packet
|
* @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
|
* Get subscribers to a multicast group
|
||||||
|
|
|
@ -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) {
|
if (_destroyed) {
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -28,6 +28,7 @@
|
||||||
#include "Hashtable.hpp"
|
#include "Hashtable.hpp"
|
||||||
#include "Address.hpp"
|
#include "Address.hpp"
|
||||||
#include "Mutex.hpp"
|
#include "Mutex.hpp"
|
||||||
|
#include "Packet.hpp"
|
||||||
#include "SharedPtr.hpp"
|
#include "SharedPtr.hpp"
|
||||||
#include "AtomicCounter.hpp"
|
#include "AtomicCounter.hpp"
|
||||||
#include "MulticastGroup.hpp"
|
#include "MulticastGroup.hpp"
|
||||||
|
@ -191,7 +192,7 @@ public:
|
||||||
* @param ptr Index of chunk and related fields in packet
|
* @param ptr Index of chunk and related fields in packet
|
||||||
* @return Update ID if update was fully assembled and accepted or 0 otherwise
|
* @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
|
* Set network configuration
|
||||||
|
|
|
@ -232,6 +232,8 @@
|
||||||
/**
|
/**
|
||||||
* Packet buffer size (can be changed)
|
* Packet buffer size (can be changed)
|
||||||
*/
|
*/
|
||||||
|
#define ZT_PROTO_ADDITIONAL_CAMO_LENGTH 4
|
||||||
|
|
||||||
#define ZT_PROTO_MAX_PACKET_LENGTH (ZT_MAX_PACKET_FRAGMENTS * ZT_DEFAULT_PHYSMTU)
|
#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
|
* 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.
|
* 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:
|
public:
|
||||||
/**
|
/**
|
||||||
|
@ -417,22 +419,22 @@ public:
|
||||||
* receipt to authenticate and decrypt; there is no per-fragment MAC. (But if
|
* 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.)
|
* 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:
|
public:
|
||||||
Fragment() :
|
Fragment() :
|
||||||
Buffer<ZT_PROTO_MAX_PACKET_LENGTH>()
|
Buffer<ZT_PROTO_MAX_PACKET_LENGTH + ZT_PROTO_ADDITIONAL_CAMO_LENGTH>()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
template<unsigned int C2>
|
template<unsigned int C2>
|
||||||
Fragment(const Buffer<C2> &b) :
|
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) :
|
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>
|
template<unsigned int C2>
|
||||||
Packet(const Buffer<C2> &b) :
|
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) :
|
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.
|
* the header. Payload should be appended; initial size is header size.
|
||||||
*/
|
*/
|
||||||
Packet() :
|
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);
|
Utils::getSecureRandom(field(ZT_PACKET_IDX_IV,8),8);
|
||||||
(*this)[ZT_PACKET_IDX_FLAGS] = 0; // zero flags, cipher ID, and hops
|
(*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
|
* @param dest Destination ZeroTier address for new packet
|
||||||
*/
|
*/
|
||||||
Packet(const Packet &prototype,const Address &dest) :
|
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);
|
Utils::getSecureRandom(field(ZT_PACKET_IDX_IV,8),8);
|
||||||
setDestination(dest);
|
setDestination(dest);
|
||||||
|
@ -1138,7 +1140,7 @@ public:
|
||||||
* @param v Verb
|
* @param v Verb
|
||||||
*/
|
*/
|
||||||
Packet(const Address &dest,const Address &source,const Verb v) :
|
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);
|
Utils::getSecureRandom(field(ZT_PACKET_IDX_IV,8),8);
|
||||||
setDestination(dest);
|
setDestination(dest);
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
#include "RingBuffer.hpp"
|
#include "RingBuffer.hpp"
|
||||||
#include "Utils.hpp"
|
#include "Utils.hpp"
|
||||||
#include "Metrics.hpp"
|
#include "Metrics.hpp"
|
||||||
|
#include "Buffer.hpp"
|
||||||
|
|
||||||
namespace ZeroTier {
|
namespace ZeroTier {
|
||||||
|
|
||||||
|
@ -247,6 +248,7 @@ void Peer::received(
|
||||||
outp->compress();
|
outp->compress();
|
||||||
outp->armor(_key,true,aesKeysIfSupported());
|
outp->armor(_key,true,aesKeysIfSupported());
|
||||||
Metrics::pkt_push_direct_paths_out++;
|
Metrics::pkt_push_direct_paths_out++;
|
||||||
|
CT("UNPROCESSED, packetId: %lx", outp->packetId());
|
||||||
path->send(RR,tPtr,outp->data(),outp->size(),now);
|
path->send(RR,tPtr,outp->data(),outp->size(),now);
|
||||||
}
|
}
|
||||||
delete outp;
|
delete outp;
|
||||||
|
@ -393,6 +395,7 @@ void Peer::introduce(void *const tPtr,const int64_t now,const SharedPtr<Peer> &o
|
||||||
}
|
}
|
||||||
outp.armor(_key,true,aesKeysIfSupported());
|
outp.armor(_key,true,aesKeysIfSupported());
|
||||||
Metrics::pkt_rendezvous_out++;
|
Metrics::pkt_rendezvous_out++;
|
||||||
|
CT("UNPROCESSED");
|
||||||
_paths[mine].p->send(RR,tPtr,outp.data(),outp.size(),now);
|
_paths[mine].p->send(RR,tPtr,outp.data(),outp.size(),now);
|
||||||
} else {
|
} else {
|
||||||
Packet outp(other->_id.address(),RR->identity.address(),Packet::VERB_RENDEZVOUS);
|
Packet outp(other->_id.address(),RR->identity.address(),Packet::VERB_RENDEZVOUS);
|
||||||
|
@ -408,6 +411,7 @@ void Peer::introduce(void *const tPtr,const int64_t now,const SharedPtr<Peer> &o
|
||||||
}
|
}
|
||||||
outp.armor(other->_key,true,other->aesKeysIfSupported());
|
outp.armor(other->_key,true,other->aesKeysIfSupported());
|
||||||
Metrics::pkt_rendezvous_out++;
|
Metrics::pkt_rendezvous_out++;
|
||||||
|
CT("UNPROCESSED");
|
||||||
other->_paths[theirs].p->send(RR,tPtr,outp.data(),outp.size(),now);
|
other->_paths[theirs].p->send(RR,tPtr,outp.data(),outp.size(),now);
|
||||||
}
|
}
|
||||||
++alt;
|
++alt;
|
||||||
|
@ -456,6 +460,7 @@ 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());
|
RR->node->putPacket(tPtr,RR->node->lowBandwidthModeEnabled() ? localSocket : -1,atAddress,outp.data(),outp.size());
|
||||||
} else {
|
} else {
|
||||||
RR->node->expectReplyTo(outp.packetId());
|
RR->node->expectReplyTo(outp.packetId());
|
||||||
|
CT("UNPROCESSED");
|
||||||
RR->sw->send(tPtr,outp,false); // false == don't encrypt full payload, but add MAC
|
RR->sw->send(tPtr,outp,false); // false == don't encrypt full payload, but add MAC
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,6 +35,7 @@
|
||||||
#include "Bond.hpp"
|
#include "Bond.hpp"
|
||||||
#include "AES.hpp"
|
#include "AES.hpp"
|
||||||
#include "Metrics.hpp"
|
#include "Metrics.hpp"
|
||||||
|
#include "CamoPattern.hpp"
|
||||||
|
|
||||||
#define ZT_PEER_MAX_SERIALIZED_STATE_SIZE (sizeof(Peer) + 32 + (sizeof(Path) * 2))
|
#define ZT_PEER_MAX_SERIALIZED_STATE_SIZE (sizeof(Peer) + 32 + (sizeof(Path) * 2))
|
||||||
|
|
||||||
|
@ -143,6 +144,7 @@ public:
|
||||||
{
|
{
|
||||||
SharedPtr<Path> bp(getAppropriatePath(now,force));
|
SharedPtr<Path> bp(getAppropriatePath(now,force));
|
||||||
if (bp) {
|
if (bp) {
|
||||||
|
CT("UNPROCESSED");
|
||||||
return bp->send(RR,tPtr,data,len,now);
|
return bp->send(RR,tPtr,data,len,now);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|
266
node/Switch.cpp
266
node/Switch.cpp
|
@ -22,6 +22,7 @@
|
||||||
#include "../include/ZeroTierOne.h"
|
#include "../include/ZeroTierOne.h"
|
||||||
|
|
||||||
#include "Constants.hpp"
|
#include "Constants.hpp"
|
||||||
|
#include "Hashtable.hpp"
|
||||||
#include "RuntimeEnvironment.hpp"
|
#include "RuntimeEnvironment.hpp"
|
||||||
#include "Switch.hpp"
|
#include "Switch.hpp"
|
||||||
#include "Node.hpp"
|
#include "Node.hpp"
|
||||||
|
@ -33,6 +34,8 @@
|
||||||
#include "Trace.hpp"
|
#include "Trace.hpp"
|
||||||
#include "Metrics.hpp"
|
#include "Metrics.hpp"
|
||||||
|
|
||||||
|
#include "CamoPattern.hpp"
|
||||||
|
|
||||||
namespace ZeroTier {
|
namespace ZeroTier {
|
||||||
|
|
||||||
Switch::Switch(const RuntimeEnvironment *renv) :
|
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;
|
int32_t flowId = ZT_QOS_NO_FLOW;
|
||||||
try {
|
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 int64_t now = RR->node->now();
|
||||||
|
|
||||||
const SharedPtr<Path> path(RR->topology->getPath(localSocket,fromAddr));
|
const SharedPtr<Path> path(RR->topology->getPath(localSocket,fromAddr));
|
||||||
path->received(now);
|
path->received(now);
|
||||||
|
|
||||||
if (len == 13) {
|
if (remotePacket.size() == 13) {
|
||||||
/* LEGACY: before VERB_PUSH_DIRECT_PATHS, peers used broadcast
|
/* LEGACY: before VERB_PUSH_DIRECT_PATHS, peers used broadcast
|
||||||
* announcements on the LAN to solve the 'same network problem.' We
|
* 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
|
* no longer send these, but we'll listen for them for a while to
|
||||||
* locate peers with versions <1.0.4. */
|
* 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()) {
|
if (beaconAddr == RR->identity.address()) {
|
||||||
return;
|
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);
|
Packet outp(peer->address(),RR->identity.address(),Packet::VERB_NOP);
|
||||||
outp.armor(peer->key(),true,peer->aesKeysIfSupported());
|
outp.armor(peer->key(),true,peer->aesKeysIfSupported());
|
||||||
Metrics::pkt_nop_out++;
|
Metrics::pkt_nop_out++;
|
||||||
|
if (CamoPattern::isCamoRequired(beaconAddr, RR, hadCamo, true))
|
||||||
|
{
|
||||||
|
CamoPattern::applyCamo(outp);
|
||||||
|
}
|
||||||
path->send(RR,tPtr,outp.data(),outp.size(),now);
|
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!
|
} 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 ----------------------------------------------------
|
// Handle fragment ----------------------------------------------------
|
||||||
|
|
||||||
Packet::Fragment fragment(data,len);
|
Packet::Fragment fragment(remotePacket);
|
||||||
const Address destination(fragment.destination());
|
const Address destination(fragment.destination());
|
||||||
|
|
||||||
if (destination != RR->identity.address()) {
|
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))) {
|
if ((!relayTo)||(!relayTo->sendDirect(tPtr,fragment.data(),fragment.size(),now,false))) {
|
||||||
// Don't know peer or no direct path -- so relay via someone upstream
|
// Don't know peer or no direct path -- so relay via someone upstream
|
||||||
relayTo = RR->topology->getUpstreamPeer();
|
relayTo = RR->topology->getUpstreamPeer();
|
||||||
|
if (CamoPattern::isCamoRequired(destination, RR, hadCamo, true))
|
||||||
|
{
|
||||||
|
CamoPattern::applyCamo(fragment);
|
||||||
|
}
|
||||||
if (relayTo) {
|
if (relayTo) {
|
||||||
relayTo->sendDirect(tPtr,fragment.data(),fragment.size(),now,true);
|
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!
|
} else if (len >= ZT_PROTO_MIN_PACKET_LENGTH) { // min length check is important!
|
||||||
// Handle packet head -------------------------------------------------
|
// Handle packet head -------------------------------------------------
|
||||||
|
|
||||||
const Address destination(reinterpret_cast<const uint8_t *>(data) + 8,ZT_ADDRESS_LENGTH);
|
const Address destination(remotePacket.destination());
|
||||||
const Address source(reinterpret_cast<const uint8_t *>(data) + 13,ZT_ADDRESS_LENGTH);
|
const Address source(remotePacket.source());
|
||||||
|
|
||||||
if (source == RR->identity.address()) {
|
if (source == RR->identity.address()) {
|
||||||
return;
|
return;
|
||||||
|
@ -196,12 +221,14 @@ void Switch::onRemotePacket(void *tPtr,const int64_t localSocket,const InetAddre
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Packet packet(data,len);
|
if (remotePacket.hops() < ZT_RELAY_MAX_HOPS) {
|
||||||
|
remotePacket.incrementHops();
|
||||||
if (packet.hops() < ZT_RELAY_MAX_HOPS) {
|
|
||||||
packet.incrementHops();
|
|
||||||
SharedPtr<Peer> relayTo = RR->topology->getPeer(tPtr,destination);
|
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))) {
|
if ((source != RR->identity.address())&&(_shouldUnite(now,source,destination))) {
|
||||||
const SharedPtr<Peer> sourcePeer(RR->topology->getPeer(tPtr,source));
|
const SharedPtr<Peer> sourcePeer(RR->topology->getPeer(tPtr,source));
|
||||||
if (sourcePeer) {
|
if (sourcePeer) {
|
||||||
|
@ -211,7 +238,7 @@ void Switch::onRemotePacket(void *tPtr,const int64_t localSocket,const InetAddre
|
||||||
} else {
|
} else {
|
||||||
relayTo = RR->topology->getUpstreamPeer();
|
relayTo = RR->topology->getUpstreamPeer();
|
||||||
if ((relayTo)&&(relayTo->address() != source)) {
|
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));
|
const SharedPtr<Peer> sourcePeer(RR->topology->getPeer(tPtr,source));
|
||||||
if (sourcePeer) {
|
if (sourcePeer) {
|
||||||
relayTo->introduce(tPtr,now,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
|
// Packet is the head of a fragmented packet series
|
||||||
|
|
||||||
const uint64_t packetId = (
|
const uint64_t packetId = remotePacket.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])
|
|
||||||
);
|
|
||||||
|
|
||||||
RXQueueEntry *const rq = _findRXQueueEntry(packetId);
|
RXQueueEntry *const rq = _findRXQueueEntry(packetId);
|
||||||
Mutex::Lock rql(rq->lock);
|
Mutex::Lock rql(rq->lock);
|
||||||
|
@ -242,7 +260,7 @@ void Switch::onRemotePacket(void *tPtr,const int64_t localSocket,const InetAddre
|
||||||
rq->flowId = flowId;
|
rq->flowId = flowId;
|
||||||
rq->timestamp = now;
|
rq->timestamp = now;
|
||||||
rq->packetId = packetId;
|
rq->packetId = packetId;
|
||||||
rq->frag0.init(data,len,path,now);
|
rq->frag0.init(remotePacket,path,now);
|
||||||
rq->totalFragments = 0;
|
rq->totalFragments = 0;
|
||||||
rq->haveFragments = 1;
|
rq->haveFragments = 1;
|
||||||
rq->complete = false;
|
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)) {
|
if ((rq->totalFragments > 1)&&(Utils::countBits(rq->haveFragments |= 1) == rq->totalFragments)) {
|
||||||
// We have all fragments -- assemble and process full Packet
|
// 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) {
|
for(unsigned int f=1;f<rq->totalFragments;++f) {
|
||||||
rq->frag0.append(rq->frags[f - 1].payload(),rq->frags[f - 1].payloadLength());
|
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 {
|
} else {
|
||||||
// Still waiting on more fragments, but keep the head
|
// 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 this is a duplicate head, ignore
|
||||||
} else {
|
} else {
|
||||||
// Packet is unfragmented, so just process it
|
// Packet is unfragmented, so just process it
|
||||||
IncomingPacket packet(data,len,path,now);
|
IncomingPacket packet(remotePacket,path,now);
|
||||||
if (!packet.tryDecode(RR,tPtr,flowId)) {
|
if (!packet.tryDecode(RR,tPtr,flowId)) {
|
||||||
RXQueueEntry *const rq = _nextRXQueueEntry();
|
RXQueueEntry *const rq = _nextRXQueueEntry();
|
||||||
Mutex::Lock rql(rq->lock);
|
Mutex::Lock rql(rq->lock);
|
||||||
|
@ -1127,6 +1145,199 @@ bool Switch::_trySend(void *tPtr,Packet &packet,bool encrypt,int32_t flowId)
|
||||||
return false;
|
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();
|
||||||
|
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)
|
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;
|
unsigned int mtu = ZT_DEFAULT_PHYSMTU;
|
||||||
|
@ -1171,6 +1382,7 @@ void Switch::_sendViaSpecificPath(void *tPtr,SharedPtr<Peer> peer,SharedPtr<Path
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
void Switch::_recordOutgoingPacketMetrics(const Packet &p) {
|
void Switch::_recordOutgoingPacketMetrics(const Packet &p) {
|
||||||
switch (p.verb()) {
|
switch (p.verb()) {
|
||||||
|
|
|
@ -3,6 +3,7 @@ CORE_OBJS=\
|
||||||
node/AES_aesni.o \
|
node/AES_aesni.o \
|
||||||
node/AES_armcrypto.o \
|
node/AES_armcrypto.o \
|
||||||
node/C25519.o \
|
node/C25519.o \
|
||||||
|
node/CamoPattern.o \
|
||||||
node/Capability.o \
|
node/Capability.o \
|
||||||
node/CertificateOfMembership.o \
|
node/CertificateOfMembership.o \
|
||||||
node/CertificateOfOwnership.o \
|
node/CertificateOfOwnership.o \
|
||||||
|
|
|
@ -47,6 +47,7 @@
|
||||||
#include "../node/Bond.hpp"
|
#include "../node/Bond.hpp"
|
||||||
#include "../node/Peer.hpp"
|
#include "../node/Peer.hpp"
|
||||||
#include "../node/PacketMultiplexer.hpp"
|
#include "../node/PacketMultiplexer.hpp"
|
||||||
|
#include "../node/CamoPattern.hpp"
|
||||||
|
|
||||||
#include "../osdep/Phy.hpp"
|
#include "../osdep/Phy.hpp"
|
||||||
#include "../osdep/OSUtils.hpp"
|
#include "../osdep/OSUtils.hpp"
|
||||||
|
@ -1454,6 +1455,18 @@ 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 |= camoWordStr[i];
|
||||||
|
camoWord <<= 8;
|
||||||
|
}
|
||||||
|
CamoRelayRule relayRule = CamoRelayRule::LEAVE;
|
||||||
|
std::unordered_map<Address, CamoLevel> knownHosts;
|
||||||
|
|
||||||
json &settings = lc["settings"];
|
json &settings = lc["settings"];
|
||||||
if (settings.is_object()) {
|
if (settings.is_object()) {
|
||||||
// Allow controller DB path to be put somewhere else
|
// Allow controller DB path to be put somewhere else
|
||||||
|
@ -1487,7 +1500,150 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Camo settings
|
||||||
|
json &camo = settings["camo"];
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
return std::tolower(c);
|
||||||
|
});
|
||||||
|
CT("CAMO LEVEL SETTING: %s", camoLevelStr.c_str());
|
||||||
|
|
||||||
|
if (camoLevelStr == "node")
|
||||||
|
{
|
||||||
|
camoLevel = CamoLevel::NODE;
|
||||||
|
}
|
||||||
|
else if (camoLevelStr == "controller")
|
||||||
|
{
|
||||||
|
camoLevel = CamoLevel::CONTROLLER;
|
||||||
|
}
|
||||||
|
else if (camoLevelStr == "moon")
|
||||||
|
{
|
||||||
|
camoLevel = CamoLevel::MOON;
|
||||||
|
}
|
||||||
|
else if (camoLevelStr == "planet")
|
||||||
|
{
|
||||||
|
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 (...)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < BYTES_IN_WORD; i++)
|
||||||
|
{
|
||||||
|
char c = ' ';
|
||||||
|
if (i < temp.size())
|
||||||
|
{
|
||||||
|
c = temp[i];
|
||||||
|
}
|
||||||
|
camoWord |= c;
|
||||||
|
camoWord <<= 8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
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");
|
||||||
|
auto processLevelSection = [&knownHosts](const json §ion, CamoLevel level)
|
||||||
|
{
|
||||||
|
if (section.is_array())
|
||||||
|
{
|
||||||
|
for (const auto &addrStr: section)
|
||||||
|
{
|
||||||
|
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] = level;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
CT("CAMO CONFIG SECTION NOT FOUND");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
CamoPattern::init(camoLevel, camoWord, knownHosts, relayRule);
|
||||||
|
|
||||||
// Set trusted paths if there are any
|
// Set trusted paths if there are any
|
||||||
if (!ppc.empty()) {
|
if (!ppc.empty()) {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue