diff options
-rw-r--r-- | controller/EmbeddedNetworkController.cpp | 67 | ||||
-rw-r--r-- | controller/EmbeddedNetworkController.hpp | 4 | ||||
-rw-r--r-- | include/ZeroTierOne.h | 45 | ||||
-rw-r--r-- | node/IncomingPacket.cpp | 27 | ||||
-rw-r--r-- | node/IncomingPacket.hpp | 1 | ||||
-rw-r--r-- | node/Packet.hpp | 3 | ||||
-rw-r--r-- | service/OneService.cpp | 6 |
7 files changed, 148 insertions, 5 deletions
diff --git a/controller/EmbeddedNetworkController.cpp b/controller/EmbeddedNetworkController.cpp index b57a37e8..8b8a93bd 100644 --- a/controller/EmbeddedNetworkController.cpp +++ b/controller/EmbeddedNetworkController.cpp @@ -621,6 +621,15 @@ unsigned int EmbeddedNetworkController::handleControlPlaneHttpPOST( if (b.count("activeBridge")) member["activeBridge"] = OSUtils::jsonBool(b["activeBridge"],false); if (b.count("noAutoAssignIps")) member["noAutoAssignIps"] = OSUtils::jsonBool(b["noAutoAssignIps"],false); + if (b.count("remoteTraceTarget")) { + const std::string rtt(OSUtils::jsonString(b["remoteTraceTarget"],"")); + if (rtt.length() == 10) { + member["remoteTraceTarget"] = rtt; + } else { + member["remoteTraceTarget"] = json(); + } + } + if (b.count("authorized")) { const bool newAuth = OSUtils::jsonBool(b["authorized"],false); if (newAuth != OSUtils::jsonBool(member["authorized"],false)) { @@ -764,6 +773,15 @@ unsigned int EmbeddedNetworkController::handleControlPlaneHttpPOST( if (b.count("multicastLimit")) network["multicastLimit"] = OSUtils::jsonInt(b["multicastLimit"],32ULL); if (b.count("mtu")) network["mtu"] = std::max(std::min((unsigned int)OSUtils::jsonInt(b["mtu"],ZT_DEFAULT_MTU),(unsigned int)ZT_MAX_MTU),(unsigned int)ZT_MIN_MTU); + if (b.count("remoteTraceTarget")) { + const std::string rtt(OSUtils::jsonString(b["remoteTraceTarget"],"")); + if (rtt.length() == 10) { + network["remoteTraceTarget"] = rtt; + } else { + network["remoteTraceTarget"] = json(); + } + } + if (b.count("v4AssignMode")) { json nv4m; json &v4m = b["v4AssignMode"]; @@ -1065,6 +1083,55 @@ unsigned int EmbeddedNetworkController::handleControlPlaneHttpDELETE( return 404; } +void EmbeddedNetworkController::handleRemoteTrace(const ZT_RemoteTrace &rt) +{ + // Convert Dictionary into JSON object + json d; + char *saveptr = (char *)0; + for(char *l=Utils::stok(rt.data,"\n",&saveptr);(l);l=Utils::stok((char *)0,"\n",&saveptr)) { + char *eq = strchr(l,'='); + if (eq > l) { + std::string k(l,(unsigned long)(eq - l)); + std::string v; + ++eq; + while (*eq) { + if (*eq == '\\') { + ++eq; + if (*eq) { + switch(*eq) { + case 'r': + v.push_back('\r'); + break; + case 'n': + v.push_back('\n'); + break; + case '0': + v.push_back((char)0); + break; + case 'e': + v.push_back('='); + break; + default: + v.push_back(*eq); + break; + } + ++eq; + } + } else { + v.push_back(*(eq++)); + } + } + if (v.length() > 0) + d[k] = v; + } + } + + char p[128]; + OSUtils::ztsnprintf(p,sizeof(p),"trace/%.10llx_%.16llx.json",rt.origin,OSUtils::now()); + _db.writeRaw(p,OSUtils::jsonDump(d)); + //fprintf(stdout,"%s\n",OSUtils::jsonDump(d).c_str()); fflush(stdout); +} + void EmbeddedNetworkController::threadMain() throw() { diff --git a/controller/EmbeddedNetworkController.hpp b/controller/EmbeddedNetworkController.hpp index 1589ea71..03ba0b95 100644 --- a/controller/EmbeddedNetworkController.hpp +++ b/controller/EmbeddedNetworkController.hpp @@ -90,6 +90,8 @@ public: std::string &responseBody, std::string &responseContentType); + void handleRemoteTrace(const ZT_RemoteTrace &rt); + void threadMain() throw(); @@ -142,6 +144,7 @@ private: if (!member.count("vRev")) member["vRev"] = -1; if (!member.count("vProto")) member["vProto"] = -1; if (!member.count("physicalAddr")) member["physicalAddr"] = nlohmann::json(); + if (!member.count("remoteTraceTarget")) member["remoteTraceTarget"] = nlohmann::json(); member["objtype"] = "member"; } inline void _initNetwork(nlohmann::json &network) @@ -159,6 +162,7 @@ private: if (!network.count("routes")) network["routes"] = nlohmann::json::array(); if (!network.count("ipAssignmentPools")) network["ipAssignmentPools"] = nlohmann::json::array(); if (!network.count("mtu")) network["mtu"] = ZT_DEFAULT_MTU; + if (!network.count("remoteTraceTarget")) network["remoteTraceTarget"] = nlohmann::json(); if (!network.count("rules")) { // If unspecified, rules are set to allow anything and behave like a flat L2 segment network["rules"] = {{ diff --git a/include/ZeroTierOne.h b/include/ZeroTierOne.h index e4c39fbc..14ddc7fe 100644 --- a/include/ZeroTierOne.h +++ b/include/ZeroTierOne.h @@ -470,10 +470,53 @@ enum ZT_Event * * Meta-data: ZT_UserMessage structure */ - ZT_EVENT_USER_MESSAGE = 6 + ZT_EVENT_USER_MESSAGE = 6, + + /** + * Remote trace received + * + * These are generated when a VERB_REMOTE_TRACE is received. Note + * that any node can fling one of these at us. It is your responsibility + * to filter and determine if it's worth paying attention to. If it's + * not just drop it. Most nodes that are not active controllers ignore + * these, and controllers only save them if they pertain to networks + * with remote tracing enabled. + * + * Meta-data: ZT_RemoteTrace structure + */ + ZT_EVENT_REMOTE_TRACE = 7 }; /** + * Payload of REMOTE_TRACE event + */ +typedef struct +{ + /** + * ZeroTier address of sender + */ + uint64_t origin; + + /** + * Null-terminated Dictionary containing key/value pairs sent by origin + * + * This *should* be a dictionary, but the implementation only checks + * that it is a valid non-empty C-style null-terminated string. Be very + * careful to use a well-tested parser to parse this as it represents + * data received from a potentially un-trusted peer on the network. + * Invalid payloads should be dropped. + * + * The contents of data[] may be modified. + */ + char *data; + + /** + * Length of dict[] in bytes, including terminating null + */ + unsigned int len; +} ZT_RemoteTrace; + +/** * User message used with ZT_EVENT_USER_MESSAGE * * These are direct VL1 P2P messages for application use. Encryption and diff --git a/node/IncomingPacket.cpp b/node/IncomingPacket.cpp index a5875d1e..5e5d1d72 100644 --- a/node/IncomingPacket.cpp +++ b/node/IncomingPacket.cpp @@ -1192,7 +1192,7 @@ bool IncomingPacket::_doPUSH_DIRECT_PATHS(const RuntimeEnvironment *RR,void *tPt bool IncomingPacket::_doUSER_MESSAGE(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer) { try { - if (size() >= (ZT_PACKET_IDX_PAYLOAD + 8)) { + if (likely(size() >= (ZT_PACKET_IDX_PAYLOAD + 8))) { ZT_UserMessage um; um.origin = peer->address().toInt(); um.typeId = at<uint64_t>(ZT_PACKET_IDX_PAYLOAD); @@ -1207,6 +1207,31 @@ bool IncomingPacket::_doUSER_MESSAGE(const RuntimeEnvironment *RR,void *tPtr,con return true; } +bool IncomingPacket::_doREMOTE_TRACE(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer) +{ + ZT_RemoteTrace rt; + try { + const char *ptr = reinterpret_cast<const char *>(data()) + ZT_PACKET_IDX_PAYLOAD; + const char *const eof = reinterpret_cast<const char *>(data()) + size(); + rt.origin = peer->address().toInt(); + rt.data = const_cast<char *>(ptr); // start of first string + while (ptr < eof) { + if (!*ptr) { // end of string + rt.len = (unsigned int)(ptr - rt.data); + if ((rt.len > 0)&&(rt.len <= ZT_MAX_REMOTE_TRACE_SIZE)) + RR->node->postEvent(tPtr,ZT_EVENT_REMOTE_TRACE,&rt); + rt.data = const_cast<char *>(++ptr); // start of next string, if any + } else { + ++ptr; + } + } + peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_REMOTE_TRACE,0,Packet::VERB_NOP,false,0); + } catch ( ... ) { + RR->t->incomingPacketInvalid(tPtr,_path,packetId(),source(),hops(),Packet::VERB_REMOTE_TRACE,"unexpected exception"); + } + return true; +} + void IncomingPacket::_sendErrorNeedCredentials(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer,const uint64_t nwid) { const uint64_t now = RR->node->now(); diff --git a/node/IncomingPacket.hpp b/node/IncomingPacket.hpp index 11b60712..692c63df 100644 --- a/node/IncomingPacket.hpp +++ b/node/IncomingPacket.hpp @@ -139,6 +139,7 @@ private: bool _doMULTICAST_FRAME(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer); bool _doPUSH_DIRECT_PATHS(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer); bool _doUSER_MESSAGE(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer); + bool _doREMOTE_TRACE(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer); void _sendErrorNeedCredentials(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer,const uint64_t nwid); diff --git a/node/Packet.hpp b/node/Packet.hpp index a1ea73e1..b8e69fa9 100644 --- a/node/Packet.hpp +++ b/node/Packet.hpp @@ -978,9 +978,6 @@ public: * The instance ID is a random 64-bit value generated by each ZeroTier * node on startup. This is helpful in identifying traces from different * members of a cluster. - * - * The Dictionary serialization format is the same as used for network - * configurations. The maximum size of a trace is 10000 bytes. */ VERB_REMOTE_TRACE = 0x15 }; diff --git a/service/OneService.cpp b/service/OneService.cpp index 1b07eb79..115830e5 100644 --- a/service/OneService.cpp +++ b/service/OneService.cpp @@ -2058,6 +2058,12 @@ public: } } break; + case ZT_EVENT_REMOTE_TRACE: { + const ZT_RemoteTrace *rt = reinterpret_cast<const ZT_RemoteTrace *>(metaData); + if ((rt)&&(rt->len > 0)&&(rt->len <= ZT_MAX_REMOTE_TRACE_SIZE)&&(rt->data)) + _controller->handleRemoteTrace(*rt); + } + default: break; } |