added camouflaging functionality and instrumentation

This commit is contained in:
eerieaerial 2025-04-30 17:40:54 +04:00
parent cdaf5e5468
commit 083b833bf8
16 changed files with 904 additions and 43 deletions

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,199 @@ 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();
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 +1382,7 @@ void Switch::_sendViaSpecificPath(void *tPtr,SharedPtr<Peer> peer,SharedPtr<Path
}
}
}
#endif
void Switch::_recordOutgoingPacketMetrics(const Packet &p) {
switch (p.verb()) {