From 2d0adb562d4d710fe8db87ad47a50e334f3fafbf Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Sun, 27 Sep 2015 11:37:39 -0700 Subject: Specify circuit test messages. --- node/Packet.hpp | 172 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 167 insertions(+), 5 deletions(-) (limited to 'node/Packet.hpp') diff --git a/node/Packet.hpp b/node/Packet.hpp index fa377964..2d439b11 100644 --- a/node/Packet.hpp +++ b/node/Packet.hpp @@ -553,10 +553,10 @@ public: * address that require re-establishing connectivity. * * Destination address types and formats (not all of these are used now): - * 0 - None -- no destination address data present - * 1 - Ethernet address -- format: <[6] Ethernet MAC> - * 4 - 6-byte IPv4 UDP address/port -- format: <[4] IP>, <[2] port> - * 6 - 18-byte IPv6 UDP address/port -- format: <[16] IP>, <[2] port> + * 0x00 - None -- no destination address data present + * 0x01 - Ethernet address -- format: <[6] Ethernet MAC> + * 0x04 - 6-byte IPv4 UDP address/port -- format: <[4] IP>, <[2] port> + * 0x06 - 18-byte IPv6 UDP address/port -- format: <[16] IP>, <[2] port> * * OK payload: * <[8] timestamp (echoed from original HELLO)> @@ -904,7 +904,169 @@ public: * * OK and ERROR are not generated. */ - VERB_PUSH_DIRECT_PATHS = 16 + VERB_PUSH_DIRECT_PATHS = 16, + + /* Source-routed circuit test message: + * <[5] address of originator of circuit test> + * <[2] 16-bit flags> + * <[8] 64-bit timestamp> + * <[8] 64-bit test ID (arbitrary, set by tester)> + * <[1] originator credential type (for authorizing test)> + * <[...] credential> + * <[2] 16-bit length of additional fields> + * <[...] additional fields> + * <[2] 16-bit length of signature of request> + * <[...] signature of request by originator> + * <[1] previous hop credential type> + * <[...] previous hop credential> + * <[...] next hop(s) in path> + * + * Flags: + * 0x01 - Report back to originator at each hop + * 0x02 - Report back to originator at last hop + * + * Originator credential types: + * 0x00 - No credentials included + * 0x01 - 64-bit network ID for which originator is controller + * + * Previous hop credential types: + * 0x00 - No credentials included + * 0x01 - Certificate of network membership + * + * Path record format: + * <[1] 8-bit flags> + * <[1] 8-bit breadth (number of next hops)> + * <[...] one or more ZeroTier addresses of next hops> + * + * Path record flags (in each path record): + * 0x80 - End of path (should be set on last entry) + * + * The circuit test allows a device to send a message that will traverse + * the network along a specified path, with each hop optionally reporting + * back to the tester via VERB_CIRCUIT_TEST_REPORT. + * + * Each circuit test packet includes a digital signature by the originator + * of the request, as well as a credential by which that originator claims + * authorization to perform the test. Currently this signature is ed25519, + * but in the future flags might be used to indicate an alternative + * algorithm. For example, the originator might be a network controller. + * In this case the test might be authorized if the recipient is a member + * of a network controlled by it, and if the previous hop(s) are also + * members. Each hop may include its certificate of network membership. + * + * Circuit test paths consist of a series of records. When a node receives + * an authorized circuit test, it: + * + * (1) Reports back to circuit tester as flags indicate + * (2) Reads and removes the next hop from the packet's path + * (3) Sends the packet along to next hop(s), if any. + * + * It is perfectly legal for a path to contain the same hop more than + * once. In fact, this can be a very useful test to determine if a hop + * can be reached bidirectionally and if so what that connectivity looks + * like. + * + * The breadth field in source-routed path records allows a hop to forward + * to more than one recipient, allowing the tester to specify different + * forms of graph traversal in a test. + * + * There is no hard limit to the number of hops in a test, but it is + * practically limited by the maximum size of a (possibly fragmented) + * ZeroTier packet. + * + * Support for circuit tests is optional. If they are not supported, the + * node should respond with an UNSUPPORTED_OPERATION error. If a circuit + * test request is not authorized, it may be ignored or reported as + * an INVALID_REQUEST. No OK messages are generated, but TEST_REPORT + * messages may be sent (see below). + * + * ERROR packet format: + * <[8] 64-bit timestamp (echoed from original> + * <[8] 64-bit test ID (echoed from original)> + */ + VERB_CIRCUIT_TEST = 17, + + /* Circuit test hop report: + * <[8] 64-bit timestamp (from original test)> + * <[8] 64-bit test ID (from original test)> + * <[8] 64-bit reporter timestamp (reporter's clock, 0 if unspec)> + * <[1] 8-bit vendor ID (set to 0, currently unused)> + * <[1] 8-bit reporter protocol version> + * <[1] 8-bit reporter major version> + * <[1] 8-bit reporter minor version> + * <[2] 16-bit reporter revision> + * <[2] 16-bit reporter OS/platform> + * <[2] 16-bit reporter architecture> + * <[2] 16-bit error code (set to 0, currently unused)> + * <[8] 64-bit report flags> + * <[8] 64-bit source packet ID> + * <[1] 8-bit source packet hop count> + * <[1] 8-bit source address type> + * [<[...] source address>] + * <[2] 16-bit length of network information> + * <[...] network information> + * <[2] 16-bit length of additional fields> + * <[...] additional fields> + * <[2] 16-bit number of next hops to which something is being sent> + * <[...] next hop information> + * + * Circuit test report flags: + * (currently none, must be zero) + * + * Next hop information record format: + * <[5] ZeroTier address of next hop> + * <[1] 8-bit destination wire address type> + * <[...] destination wire address> + * + * See enums below for OS/platform and architecture. Source address format + * is the same as specified in HELLO. + * + * Circuit test reports can be sent by hops in a circuit test to report + * back results. They should include information about the sender as well + * as about the paths to which next hops are being sent. + * + * If a test report is received and no circuit test was sent, it should be + * ignored. This message generates no OK or ERROR response. + */ + VERB_CIRCUIT_TEST_REPORT = 18 + }; + + /** + * Platforms reported in circuit tests + */ + enum CircuitTestReportPlatform + { + CIRCUIT_TEST_REPORT_PLATFORM_UNSPECIFIED = 0, + CIRCUIT_TEST_REPORT_PLATFORM_LINUX = 1, + CIRCUIT_TEST_REPORT_PLATFORM_WINDOWS = 2, + CIRCUIT_TEST_REPORT_PLATFORM_MACOS = 3, + CIRCUIT_TEST_REPORT_PLATFORM_ANDROID = 4, + CIRCUIT_TEST_REPORT_PLATFORM_IOS = 5, + CIRCUIT_TEST_REPORT_PLATFORM_SOLARIS_SMARTOS = 6, + CIRCUIT_TEST_REPORT_PLATFORM_FREEBSD = 7, + CIRCUIT_TEST_REPORT_PLATFORM_NETBSD = 8, + CIRCUIT_TEST_REPORT_PLATFORM_OPENBSD = 9, + CIRCUIT_TEST_REPORT_PLATFORM_RISCOS = 10, + CIRCUIT_TEST_REPORT_PLATFORM_VXWORKS = 11, + CIRCUIT_TEST_REPORT_PLATFORM_FREERTOS = 12, + CIRCUIT_TEST_REPORT_PLATFORM_SYSBIOS = 13, + CIRCUIT_TEST_REPORT_PLATFORM_HURD = 14 + }; + + /** + * Architectures reported in circuit tests + */ + enum CircuitTestReportArchitecture + { + CIRCUIT_TEST_REPORT_ARCH_UNSPECIFIED = 0, + CIRCUIT_TEST_REPORT_ARCH_X86 = 1, + CIRCUIT_TEST_REPORT_ARCH_X64 = 2, + CIRCUIT_TEST_REPORT_ARCH_ARM32 = 3, + CIRCUIT_TEST_REPORT_ARCH_ARM64 = 4, + CIRCUIT_TEST_REPORT_ARCH_MIPS32 = 5, + CIRCUIT_TEST_REPORT_ARCH_MIPS64 = 6, + CIRCUIT_TEST_REPORT_ARCH_POWER32 = 7, + CIRCUIT_TEST_REPORT_ARCH_POWER64 = 8 }; /** -- cgit v1.2.3 From 1a4f16e0ed1b62ebf3bdbb539858a3874c689fa4 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Wed, 30 Sep 2015 13:59:05 -0700 Subject: More work on circuit testing... --- node/IncomingPacket.cpp | 63 +++++++++++++++++++++++++++++++++++++++++++++++++ node/IncomingPacket.hpp | 4 +++- node/Packet.hpp | 5 +++- 3 files changed, 70 insertions(+), 2 deletions(-) (limited to 'node/Packet.hpp') diff --git a/node/IncomingPacket.cpp b/node/IncomingPacket.cpp index c94ffe2e..3866abf1 100644 --- a/node/IncomingPacket.cpp +++ b/node/IncomingPacket.cpp @@ -85,6 +85,8 @@ bool IncomingPacket::tryDecode(const RuntimeEnvironment *RR) case Packet::VERB_MULTICAST_GATHER: return _doMULTICAST_GATHER(RR,peer); case Packet::VERB_MULTICAST_FRAME: return _doMULTICAST_FRAME(RR,peer); case Packet::VERB_PUSH_DIRECT_PATHS: return _doPUSH_DIRECT_PATHS(RR,peer); + case Packet::VERB_CIRCUIT_TEST: return _doCIRCUIT_TEST(RR,peer); + case Packet::VERB_CIRCUIT_TEST_REPORT: return _doCIRCUIT_TEST_REPORT(RR,peer); } } else { RR->sw->requestWhois(source()); @@ -926,6 +928,67 @@ bool IncomingPacket::_doPUSH_DIRECT_PATHS(const RuntimeEnvironment *RR,const Sha return true; } +bool IncomingPacket::_doCIRCUIT_TEST(const RuntimeEnvironment *RR,const SharedPtr &peer) +{ + try { + const Address originatorAddress(field(ZT_PACKET_IDX_PAYLOAD,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH); + SharedPtr originator(RR->topology->getPeer(originatorAddress)); + if (!originator) { + RR->sw->requestWhois(originatorAddress); + return false; + } + + const unsigned int flags = at(ZT_PACKET_IDX_PAYLOAD + 5); + const uint64_t timestamp = at(ZT_PACKET_IDX_PAYLOAD + 7); + const uint64_t testId = at(ZT_PACKET_IDX_PAYLOAD + 15); + + unsigned int vlf = at(ZT_PACKET_IDX_PAYLOAD + 23); // variable length field length + switch((*this)[ZT_PACKET_IDX_PAYLOAD + 25]) { + case 0x01: { // 64-bit network ID, originator must be controller + } break; + default: break; + } + + vlf += at(ZT_PACKET_IDX_PAYLOAD + 26 + vlf); // length of additional fields, currently unused + + const unsigned int signatureLength = at(ZT_PACKET_IDX_PAYLOAD + 28 + vlf); + if (!originator->identity().verify(field(ZT_PACKET_IDX_PAYLOAD,28 + vlf),28 + vlf,field(30 + vlf,signatureLength),signatureLength)) { + TRACE("dropped CIRCUIT_TEST from %s(%s): signature by originator %s invalid",source().toString().c_str(),_remoteAddress.toString().c_str(),originatorAddress.toString().c_str()); + return true; + } + vlf += signatureLength; + + vlf += at(ZT_PACKET_IDX_PAYLOAD + 30 + vlf); + switch((*this)[ZT_PACKET_IDX_PAYLOAD + 32 + vlf]) { + case 0x01: { // network certificate of membership for previous hop + } break; + default: break; + } + + if ((ZT_PACKET_IDX_PAYLOAD + 33 + vlf) < size()) { + const unsigned int breadth = (*this)[ZT_PACKET_IDX_PAYLOAD + 33 + vlf]; + Address nextHops[255]; + SharedPtr nextHopPeers[255]; + unsigned int hptr = ZT_PACKET_IDX_PAYLOAD + 34 + vlf; + for(unsigned int h=0;((h256 but be safe anyway + nextHops[h].setTo(field(hptr,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH); + hptr += ZT_ADDRESS_LENGTH; + nextHopPeers[h] = RR->topology->getPeer(nextHops[h]); + } + } + } catch (std::exception &exc) { + TRACE("dropped CIRCUIT_TEST from %s(%s): unexpected exception: %s",source().toString().c_str(),_remoteAddress.toString().c_str(),exc.what()); + } catch ( ... ) { + TRACE("dropped CIRCUIT_TEST from %s(%s): unexpected exception: (unknown)",source().toString().c_str(),_remoteAddress.toString().c_str()); + } + return true; +} + +bool IncomingPacket::_doCIRCUIT_TEST_REPORT(const RuntimeEnvironment *RR,const SharedPtr &peer) +{ + return true; +} + void IncomingPacket::_sendErrorNeedCertificate(const RuntimeEnvironment *RR,const SharedPtr &peer,uint64_t nwid) { Packet outp(source(),RR->identity.address(),Packet::VERB_ERROR); diff --git a/node/IncomingPacket.hpp b/node/IncomingPacket.hpp index d19eb5c6..06220c4b 100644 --- a/node/IncomingPacket.hpp +++ b/node/IncomingPacket.hpp @@ -124,8 +124,10 @@ private: bool _doMULTICAST_GATHER(const RuntimeEnvironment *RR,const SharedPtr &peer); bool _doMULTICAST_FRAME(const RuntimeEnvironment *RR,const SharedPtr &peer); bool _doPUSH_DIRECT_PATHS(const RuntimeEnvironment *RR,const SharedPtr &peer); + bool _doCIRCUIT_TEST(const RuntimeEnvironment *RR,const SharedPtr &peer); + bool _doCIRCUIT_TEST_REPORT(const RuntimeEnvironment *RR,const SharedPtr &peer); - // Send an ERROR_NEED_MEMBERSHIP_CERTIFICATE to a peer indicating that an updated cert is needed to join + // Send an ERROR_NEED_MEMBERSHIP_CERTIFICATE to a peer indicating that an updated cert is needed to communicate void _sendErrorNeedCertificate(const RuntimeEnvironment *RR,const SharedPtr &peer,uint64_t nwid); uint64_t _receiveTime; diff --git a/node/Packet.hpp b/node/Packet.hpp index 2d439b11..2a1a39af 100644 --- a/node/Packet.hpp +++ b/node/Packet.hpp @@ -911,12 +911,15 @@ public: * <[2] 16-bit flags> * <[8] 64-bit timestamp> * <[8] 64-bit test ID (arbitrary, set by tester)> + * <[2] 16-bit originator credential length> * <[1] originator credential type (for authorizing test)> * <[...] credential> * <[2] 16-bit length of additional fields> * <[...] additional fields> + * [ ... end of signed portion of request ... ] * <[2] 16-bit length of signature of request> * <[...] signature of request by originator> + * <[2] 16-bit previous hop credential length> * <[1] previous hop credential type> * <[...] previous hop credential> * <[...] next hop(s) in path> @@ -939,7 +942,7 @@ public: * <[...] one or more ZeroTier addresses of next hops> * * Path record flags (in each path record): - * 0x80 - End of path (should be set on last entry) + * (unused, must be zero) * * The circuit test allows a device to send a message that will traverse * the network along a specified path, with each hop optionally reporting -- cgit v1.2.3 From 0d0039674fefbfc14eb41f3bdbec7bf3e6dba9a5 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Wed, 30 Sep 2015 14:48:07 -0700 Subject: Add new verb names, and fix some Mac compiler flags. --- make-mac.mk | 4 ++-- node/Packet.cpp | 6 ++++++ node/Packet.hpp | 11 ++--------- 3 files changed, 10 insertions(+), 11 deletions(-) (limited to 'node/Packet.hpp') diff --git a/make-mac.mk b/make-mac.mk index c0b3f89d..6daa6aa0 100644 --- a/make-mac.mk +++ b/make-mac.mk @@ -58,8 +58,8 @@ ifeq ($(ZT_DEBUG),1) # C25519 in particular is almost UNUSABLE in heavy testing without it. ext/lz4/lz4.o node/Salsa20.o node/SHA512.o node/C25519.o node/Poly1305.o: CFLAGS = -Wall -O2 -g -pthread $(INCLUDES) $(DEFS) else - CFLAGS?=-O3 -fstack-protector - CFLAGS+=$(ARCH_FLAGS) -Wall -flto -fPIE -fvectorize -pthread -mmacosx-version-min=10.7 -DNDEBUG -Wno-unused-private-field $(INCLUDES) $(DEFS) + CFLAGS?=-Ofast -fstack-protector + CFLAGS+=$(ARCH_FLAGS) -Wall -flto -fPIE -pthread -mmacosx-version-min=10.7 -DNDEBUG -Wno-unused-private-field $(INCLUDES) $(DEFS) STRIP=strip endif diff --git a/node/Packet.cpp b/node/Packet.cpp index 2c73a087..f69e4e79 100644 --- a/node/Packet.cpp +++ b/node/Packet.cpp @@ -31,6 +31,8 @@ namespace ZeroTier { const unsigned char Packet::ZERO_KEY[32] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }; +#ifdef ZT_TRACE + const char *Packet::verbString(Verb v) throw() { @@ -52,6 +54,8 @@ const char *Packet::verbString(Verb v) case VERB_MULTICAST_FRAME: return "MULTICAST_FRAME"; case VERB_SET_EPHEMERAL_KEY: return "SET_EPHEMERAL_KEY"; case VERB_PUSH_DIRECT_PATHS: return "PUSH_DIRECT_PATHS"; + case VERB_CIRCUIT_TEST: return "CIRCUIT_TEST"; + case VERB_CIRCUIT_TEST_REPORT: return "CIRCUIT_TEST_REPORT"; } return "(unknown)"; } @@ -73,6 +77,8 @@ const char *Packet::errorString(ErrorCode e) return "(unknown)"; } +#endif // ZT_TRACE + void Packet::armor(const void *key,bool encryptPayload) { unsigned char mangledKey[32]; diff --git a/node/Packet.hpp b/node/Packet.hpp index 2a1a39af..71fa6e56 100644 --- a/node/Packet.hpp +++ b/node/Packet.hpp @@ -1105,19 +1105,12 @@ public: ERROR_UNWANTED_MULTICAST = 8 }; - /** - * @param v Verb - * @return String representation (e.g. HELLO, OK) - */ +#ifdef ZT_TRACE static const char *verbString(Verb v) throw(); - - /** - * @param e Error code - * @return String error name - */ static const char *errorString(ErrorCode e) throw(); +#endif template Packet(const Buffer &b) : -- cgit v1.2.3 From 5341afcdcd7d2d1ae5546ae44024d039b03ccad3 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Tue, 6 Oct 2015 11:47:16 -0700 Subject: Handling of CIRCUIT_TEST, should be ready to test. --- node/Buffer.hpp | 17 ++++++ node/IncomingPacket.cpp | 152 +++++++++++++++++++++++++++++++++++++++++------- node/Packet.hpp | 44 +++++--------- 3 files changed, 162 insertions(+), 51 deletions(-) (limited to 'node/Packet.hpp') diff --git a/node/Buffer.hpp b/node/Buffer.hpp index 789b835a..46924c14 100644 --- a/node/Buffer.hpp +++ b/node/Buffer.hpp @@ -391,6 +391,23 @@ public: ::memmove(_b,_b + at,_l -= at); } + /** + * Erase something from the middle of the buffer + * + * @param start Starting position + * @param length Length of block to erase + * @throw std::out_of_range Position plus length is beyond size of buffer + */ + inline void erase(const unsigned int at,const unsigned int length) + throw(std::out_of_range) + { + const unsigned int endr = at + length; + if (endr > _l) + throw std::out_of_range("Buffer: erase() range beyond end of buffer"); + ::memmove(_b + at,_b + endr,_l - endr); + _l -= length; + } + /** * Set buffer data length to zero */ diff --git a/node/IncomingPacket.cpp b/node/IncomingPacket.cpp index e892edc2..49a5981b 100644 --- a/node/IncomingPacket.cpp +++ b/node/IncomingPacket.cpp @@ -933,38 +933,146 @@ bool IncomingPacket::_doCIRCUIT_TEST(const RuntimeEnvironment *RR,const SharedPt const uint64_t timestamp = at(ZT_PACKET_IDX_PAYLOAD + 7); const uint64_t testId = at(ZT_PACKET_IDX_PAYLOAD + 15); - unsigned int vlf = at(ZT_PACKET_IDX_PAYLOAD + 23); // variable length field length - switch((*this)[ZT_PACKET_IDX_PAYLOAD + 25]) { - case 0x01: { // 64-bit network ID, originator must be controller - } break; - default: break; + // Tracks total length of variable length fields, initialized to originator credential length below + unsigned int vlf; + + // Originator credentials + const unsigned int originatorCredentialLength = vlf = at(ZT_PACKET_IDX_PAYLOAD + 23); + uint64_t originatorCredentialNetworkId = 0; + if (originatorCredentialLength >= 1) { + switch((*this)[ZT_PACKET_IDX_PAYLOAD + 25]) { + case 0x01: { // 64-bit network ID, originator must be controller + if (originatorCredentialLength >= 9) + originatorCredentialNetworkId = at(ZT_PACKET_IDX_PAYLOAD + 26); + } break; + default: break; + } } - vlf += at(ZT_PACKET_IDX_PAYLOAD + 26 + vlf); // length of additional fields, currently unused + // Add length of "additional fields," which are currently unused + vlf += at(ZT_PACKET_IDX_PAYLOAD + 25 + vlf); - const unsigned int signatureLength = at(ZT_PACKET_IDX_PAYLOAD + 28 + vlf); - if (!originator->identity().verify(field(ZT_PACKET_IDX_PAYLOAD,28 + vlf),28 + vlf,field(30 + vlf,signatureLength),signatureLength)) { + // Verify signature -- only tests signed by their originators are allowed + const unsigned int signatureLength = at(ZT_PACKET_IDX_PAYLOAD + 27 + vlf); + if (!originator->identity().verify(field(ZT_PACKET_IDX_PAYLOAD,27 + vlf),27 + vlf,field(ZT_PACKET_IDX_PAYLOAD + 29 + vlf,signatureLength),signatureLength)) { TRACE("dropped CIRCUIT_TEST from %s(%s): signature by originator %s invalid",source().toString().c_str(),_remoteAddress.toString().c_str(),originatorAddress.toString().c_str()); return true; } vlf += signatureLength; - vlf += at(ZT_PACKET_IDX_PAYLOAD + 30 + vlf); - switch((*this)[ZT_PACKET_IDX_PAYLOAD + 32 + vlf]) { - case 0x01: { // network certificate of membership for previous hop - } break; - default: break; + // Save this length so we can copy the immutable parts of this test + // into the one we send along to next hops. + const unsigned int lengthOfSignedPortionAndSignature = 29 + vlf; + + // Get previous hop's credential, if any + const unsigned int previousHopCredentialLength = at(ZT_PACKET_IDX_PAYLOAD + 29 + vlf); + CertificateOfMembership previousHopCom; + if (previousHopCredentialLength >= 1) { + switch((*this)[ZT_PACKET_IDX_PAYLOAD + 31 + vlf]) { + case 0x01: { // network certificate of membership for previous hop + if (previousHopCom.deserialize(*this,ZT_PACKET_IDX_PAYLOAD + 32 + vlf) != (previousHopCredentialLength - 1)) { + TRACE("dropped CIRCUIT_TEST from %s(%s): previous hop COM invalid",source().toString().c_str(),_remoteAddress.toString().c_str()); + return true; + } + } break; + default: break; + } } + vlf += previousHopCredentialLength; + + // Check credentials (signature already verified) + SharedPtr originatorCredentialNetworkConfig; + if (originatorCredentialNetworkId) { + if (Network::controllerFor(originatorCredentialNetworkId) == originatorAddress) { + SharedPtr nw(RR->node->network(originatorCredentialNetworkId)); + if (nw) { + originatorCredentialNetworkConfig = nw->config2(); + if ( (originatorCredentialNetworkConfig) && (originatorCredentialNetworkConfig->isPublic()||((originatorCredentialNetworkConfig->com())&&(previousHopCom)&&(originatorCredentialNetworkConfig->com().agreesWith(previousHopCom)))) ) { + TRACE("CIRCUIT_TEST %.16llx received from hop %s(%s) and originator %s with valid network ID credential %.16llx (verified from originator and next hop)",testId,source().toString().c_str(),_remoteAddress.toString().c_str(),originatorAddress.toString().c_str(),originatorCredentialNetworkId); + } else { + TRACE("dropped CIRCUIT_TEST from %s(%s): originator %s specified network ID %.16llx as credential, and previous hop %s did not supply a valid COM",source().toString().c_str(),_remoteAddress.toString().c_str(),originatorAddress.toString().c_str(),originatorCredentialNetworkId,peer->address().toString().c_str()); + return true; + } + } else { + TRACE("dropped CIRCUIT_TEST from %s(%s): originator %s specified network ID %.16llx as credential, and we are not a member",source().toString().c_str(),_remoteAddress.toString().c_str(),originatorAddress.toString().c_str(),originatorCredentialNetworkId); + return true; + } + } else { + TRACE("dropped CIRCUIT_TEST from %s(%s): originator %s specified network ID as credential, is not controller for %.16llx",source().toString().c_str(),_remoteAddress.toString().c_str(),originatorAddress.toString().c_str(),originatorCredentialNetworkId); + return true; + } + } else { + TRACE("dropped CIRCUIT_TEST from %s(%s): originator %s did not specify a credential or credential type",source().toString().c_str(),_remoteAddress.toString().c_str(),originatorAddress.toString().c_str()); + return true; + } + + const uint64_t now = RR->node->now(); + + unsigned int breadth = 0; + Address nextHop[256]; // breadth is a uin8_t, so this is the max + InetAddress nextHopBestPathAddress[256]; + unsigned int remainingHopsPtr = ZT_PACKET_IDX_PAYLOAD + 33 + vlf; + if ((ZT_PACKET_IDX_PAYLOAD + 31 + vlf) < size()) { + // unsigned int nextHopFlags = (*this)[ZT_PACKET_IDX_PAYLOAD + 31 + vlf] + breadth = (*this)[ZT_PACKET_IDX_PAYLOAD + 32 + vlf]; + for(unsigned int h=0;h nhp(RR->topology->getPeer(nextHop[h])); + if (nhp) { + RemotePath *const rp = nhp->getBestPath(now); + if (rp) + nextHopBestPathAddress[h] = rp->address(); + } + } + } + + // Report back to originator, depending on flags and whether we are last hop + if ( ((flags & 0x01) != 0) || ((breadth == 0)&&((flags & 0x02) != 0)) ) { + Packet outp(originatorAddress,RR->identity.address(),Packet::VERB_CIRCUIT_TEST_REPORT); + outp.append((uint64_t)timestamp); + outp.append((uint64_t)testId); + outp.append((uint64_t)now); + outp.append((uint8_t)0); // vendor ID, currently unused + outp.append((uint8_t)ZT_PROTO_VERSION); + outp.append((uint8_t)ZEROTIER_ONE_VERSION_MAJOR); + outp.append((uint8_t)ZEROTIER_ONE_VERSION_MINOR); + outp.append((uint16_t)ZEROTIER_ONE_VERSION_REVISION); + outp.append((uint16_t)CIRCUIT_TEST_REPORT_PLATFORM_UNSPECIFIED); + outp.append((uint16_t)CIRCUIT_TEST_REPORT_ARCH_UNSPECIFIED); + outp.append((uint16_t)0); // error code, currently unused + outp.append((uint64_t)0); // flags, currently unused + outp.append((uint64_t)packetId()); + outp.append((uint8_t)hops()); + _localAddress.serialize(outp); + _remoteAddress.serialize(outp); + outp.append((uint16_t)0); // no additional fields + outp.append((uint8_t)breadth); + for(unsigned int h=0;hsw->send(outp,true,0); + } + + // If there are next hops, forward the test along through the graph + if (breadth > 0) { + Packet outp(Address(),RR->identity.address(),Packet::VERB_CIRCUIT_TEST); + outp.append(field(ZT_PACKET_IDX_PAYLOAD,lengthOfSignedPortionAndSignature),lengthOfSignedPortionAndSignature); + const unsigned int previousHopCredentialPos = outp.size(); + outp.append((uint16_t)0); // no previous hop credentials: default + if ((originatorCredentialNetworkConfig)&&(!originatorCredentialNetworkConfig->isPublic())&&(originatorCredentialNetworkConfig->com())) { + outp.append((uint8_t)0x01); // COM + originatorCredentialNetworkConfig->com().serialize(outp); + outp.setAt(previousHopCredentialPos,(uint16_t)(size() - previousHopCredentialPos)); + } + if (remainingHopsPtr < size()) + outp.append(field(remainingHopsPtr,size() - remainingHopsPtr),size() - remainingHopsPtr); - if ((ZT_PACKET_IDX_PAYLOAD + 33 + vlf) < size()) { - const unsigned int breadth = (*this)[ZT_PACKET_IDX_PAYLOAD + 33 + vlf]; - Address nextHops[255]; - SharedPtr nextHopPeers[255]; - unsigned int hptr = ZT_PACKET_IDX_PAYLOAD + 34 + vlf; - for(unsigned int h=0;((h256 but be safe anyway - nextHops[h].setTo(field(hptr,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH); - hptr += ZT_ADDRESS_LENGTH; - nextHopPeers[h] = RR->topology->getPeer(nextHops[h]); + for(unsigned int h=0;hsw->send(outp,true,originatorCredentialNetworkId); } } } catch (std::exception &exc) { diff --git a/node/Packet.hpp b/node/Packet.hpp index 71fa6e56..eaffb922 100644 --- a/node/Packet.hpp +++ b/node/Packet.hpp @@ -911,39 +911,34 @@ public: * <[2] 16-bit flags> * <[8] 64-bit timestamp> * <[8] 64-bit test ID (arbitrary, set by tester)> - * <[2] 16-bit originator credential length> - * <[1] originator credential type (for authorizing test)> - * <[...] credential> + * <[2] 16-bit originator credential length (includes type)> + * [[1] originator credential type (for authorizing test)] + * [[...] originator credential] * <[2] 16-bit length of additional fields> - * <[...] additional fields> + * [[...] additional fields] * [ ... end of signed portion of request ... ] * <[2] 16-bit length of signature of request> * <[...] signature of request by originator> - * <[2] 16-bit previous hop credential length> - * <[1] previous hop credential type> - * <[...] previous hop credential> + * <[2] 16-bit previous hop credential length (including type)> + * [[1] previous hop credential type] + * [[...] previous hop credential] * <[...] next hop(s) in path> * * Flags: - * 0x01 - Report back to originator at each hop + * 0x01 - Report back to originator at middle hops * 0x02 - Report back to originator at last hop * * Originator credential types: - * 0x00 - No credentials included * 0x01 - 64-bit network ID for which originator is controller * * Previous hop credential types: - * 0x00 - No credentials included * 0x01 - Certificate of network membership * * Path record format: - * <[1] 8-bit flags> + * <[1] 8-bit flags (unused, must be zero)> * <[1] 8-bit breadth (number of next hops)> * <[...] one or more ZeroTier addresses of next hops> * - * Path record flags (in each path record): - * (unused, must be zero) - * * The circuit test allows a device to send a message that will traverse * the network along a specified path, with each hop optionally reporting * back to the tester via VERB_CIRCUIT_TEST_REPORT. @@ -1001,28 +996,19 @@ public: * <[2] 16-bit reporter OS/platform> * <[2] 16-bit reporter architecture> * <[2] 16-bit error code (set to 0, currently unused)> - * <[8] 64-bit report flags> + * <[8] 64-bit report flags (set to 0, currently unused)> * <[8] 64-bit source packet ID> - * <[1] 8-bit source packet hop count> - * <[1] 8-bit source address type> - * [<[...] source address>] - * <[2] 16-bit length of network information> - * <[...] network information> + * <[1] 8-bit source packet hop count (ZeroTier hop count)> + * <[...] local wire address on which packet was received> + * <[...] remote wire address from which packet was received> * <[2] 16-bit length of additional fields> * <[...] additional fields> - * <[2] 16-bit number of next hops to which something is being sent> + * <[1] 8-bit number of next hops (breadth)> * <[...] next hop information> * - * Circuit test report flags: - * (currently none, must be zero) - * * Next hop information record format: * <[5] ZeroTier address of next hop> - * <[1] 8-bit destination wire address type> - * <[...] destination wire address> - * - * See enums below for OS/platform and architecture. Source address format - * is the same as specified in HELLO. + * <[...] current best direct path address, if any, 0 if none> * * Circuit test reports can be sent by hops in a circuit test to report * back results. They should include information about the sender as well -- cgit v1.2.3 From d3f29d09e8eb7fdbbbed682b383da73bc44928d6 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Tue, 6 Oct 2015 14:42:51 -0700 Subject: Plumbing through circuit test stuff. --- include/ZeroTierOne.h | 271 ++++++++++++++++++++++++++++++++++++++++++++++++ node/IncomingPacket.cpp | 7 +- node/Node.cpp | 52 ++++++++++ node/Node.hpp | 12 +++ node/Packet.hpp | 38 ------- 5 files changed, 339 insertions(+), 41 deletions(-) (limited to 'node/Packet.hpp') diff --git a/include/ZeroTierOne.h b/include/ZeroTierOne.h index 8eacc993..43c8fc0b 100644 --- a/include/ZeroTierOne.h +++ b/include/ZeroTierOne.h @@ -115,6 +115,19 @@ extern "C" { */ #define ZT_FEATURE_FLAG_FIPS 0x00000002 +/** + * Maximum number of hops in a ZeroTier circuit test + * + * This is more or less the max that can be fit in a given packet (with + * fragmentation) and only one address per hop. + */ +#define ZT_CIRCUIT_TEST_MAX_HOPS 512 + +/** + * Maximum number of addresses per hop in a circuit test + */ +#define ZT_CIRCUIT_TEST_MAX_HOP_BREADTH 256 + /** * A null/empty sockaddr (all zero) to signify an unspecified socket address */ @@ -631,6 +644,231 @@ typedef enum { ZT_LOCAL_INTERFACE_ADDRESS_TRUST_ULTIMATE = 20 } ZT_LocalInterfaceAddressTrust; +/** + * Vendor ID + */ +typedef enum { + ZT_VENDOR_UNSPECIFIED = 0, + ZT_VENDOR_ZEROTIER = 1 +} ZT_Vendor; + +/** + * Platform type + */ +typedef enum { + ZT_PLATFORM_UNSPECIFIED = 0, + ZT_PLATFORM_LINUX = 1, + ZT_PLATFORM_WINDOWS = 2, + ZT_PLATFORM_MACOS = 3, + ZT_PLATFORM_ANDROID = 4, + ZT_PLATFORM_IOS = 5, + ZT_PLATFORM_SOLARIS_SMARTOS = 6, + ZT_PLATFORM_FREEBSD = 7, + ZT_PLATFORM_NETBSD = 8, + ZT_PLATFORM_OPENBSD = 9, + ZT_PLATFORM_RISCOS = 10, + ZT_PLATFORM_VXWORKS = 11, + ZT_PLATFORM_FREERTOS = 12, + ZT_PLATFORM_SYSBIOS = 13, + ZT_PLATFORM_HURD = 14 +} ZT_Platform; + +/** + * Architecture type + */ +typedef enum { + ZT_ARCHITECTURE_UNSPECIFIED = 0, + ZT_ARCHITECTURE_X86 = 1, + ZT_ARCHITECTURE_X64 = 2, + ZT_ARCHITECTURE_ARM32 = 3, + ZT_ARCHITECTURE_ARM64 = 4, + ZT_ARCHITECTURE_MIPS32 = 5, + ZT_ARCHITECTURE_MIPS64 = 6, + ZT_ARCHITECTURE_POWER32 = 7, + ZT_ARCHITECTURE_POWER64 = 8 +} ZT_Architecture; + +/** + * ZeroTier circuit test configuration and path + */ +typedef struct { + /** + * Test ID -- an arbitrary 64-bit identifier + */ + uint64_t testId; + + /** + * Timestamp -- sent with test and echoed back by each reporter + */ + uint64_t timestamp; + + /** + * Originator credential: network ID + * + * If this is nonzero, a network ID will be set for this test and + * the originator must be its primary network controller. This is + * currently the only authorization method available, so it must + * be set to run a test. + */ + uint64_t credentialNetworkId; + + /** + * Hops in circuit test (a.k.a. FIFO for graph traversal) + */ + struct { + /** + * Hop flags (currently unused, must be zero) + */ + unsigned int flags; + + /** + * Number of addresses in this hop (max: ZT_CIRCUIT_TEST_MAX_HOP_BREADTH) + */ + unsigned int breadth; + + /** + * 40-bit ZeroTier addresses (most significant 24 bits ignored) + */ + uint64_t addresses[ZT_CIRCUIT_TEST_MAX_HOP_BREADTH]; + } hops[ZT_CIRCUIT_TEST_MAX_HOPS]; + + /** + * Number of hops (max: ZT_CIRCUIT_TEST_MAX_HOPS) + */ + unsigned int hopCount; + + /** + * If non-zero, circuit test will report back at every hop + */ + int reportAtEveryHop; + + /** + * An arbitrary user-settable pointer + */ + void *ptr; + + /** + * Pointer for internal use -- initialize to zero and do not modify + */ + void *_internalPtr; +} ZT_CircuitTest; + +/** + * Circuit test result report + */ +typedef struct { + /** + * 64-bit test ID + */ + uint64_t testId; + + /** + * Timestamp from original test (echoed back at each hop) + */ + uint64_t timestamp; + + /** + * Timestamp on remote device + */ + uint64_t remoteTimestamp; + + /** + * 64-bit packet ID of packet received by the reporting device + */ + uint64_t sourcePacketId; + + /** + * Flags (currently unused, will be zero) + */ + uint64_t flags; + + /** + * ZeroTier protocol-level hop count of packet received by reporting device (>0 indicates relayed) + */ + unsigned int sourcePacketHopCount; + + /** + * Error code (currently unused, will be zero) + */ + unsigned int errorCode; + + /** + * Remote device vendor ID + */ + ZT_Vendor vendor; + + /** + * Remote device protocol compliance version + */ + unsigned int protocolVersion; + + /** + * Software major version + */ + unsigned int majorVersion; + + /** + * Software minor version + */ + unsigned int minorVersion; + + /** + * Software revision + */ + unsigned int revision; + + /** + * Platform / OS + */ + ZT_Platform platform; + + /** + * System architecture + */ + ZT_Architecture architecture; + + /** + * Local device address on which packet was received by reporting device + * + * This may have ss_family equal to zero (null address) if unspecified. + */ + struct sockaddr_storage receivedOnLocalAddress; + + /** + * Remote address from which reporter received the test packet + * + * This may have ss_family set to zero (null address) if unspecified. + */ + struct sockaddr_storage receivedFromAddress; + + /** + * Next hops to which packets are being or will be sent by the reporter + * + * In addition to reporting back, the reporter may send the test on if + * there are more recipients in the FIFO. If it does this, it can report + * back the address(es) that make up the next hop and the physical address + * for each if it has one. The physical address being null/unspecified + * typically indicates that no direct path exists and the next packet + * will be relayed. + */ + struct { + /** + * 40-bit ZeroTier address + */ + uint64_t address; + + /** + * Physical address or null address (ss_family == 0) if unspecified or unknown + */ + struct sockaddr_storage physicalAddress; + } nextHops[ZT_CIRCUIT_TEST_MAX_HOP_BREADTH]; + + /** + * Number of next hops reported in nextHops[] + */ + unsigned int nextHopCount; +} ZT_CircuitTestReport; + /** * An instance of a ZeroTier One node (opaque) */ @@ -1061,6 +1299,39 @@ void ZT_Node_clearLocalInterfaceAddresses(ZT_Node *node); */ void ZT_Node_setNetconfMaster(ZT_Node *node,void *networkConfigMasterInstance); +/** + * Initiate a VL1 circuit test + * + * This sends an initial VERB_CIRCUIT_TEST and reports results back to the + * supplied callback until circuitTestEnd() is called. The supplied + * ZT_CircuitTest structure should be initially zeroed and then filled + * in with settings and hops. + * + * It is the caller's responsibility to call circuitTestEnd() and then + * to dispose of the test structure. Otherwise this node will listen + * for results forever. + * + * @param node Node instance + * @param test Test configuration + * @param reportCallback Function to call each time a report is received + * @return OK or error if, for example, test is too big for a packet or support isn't compiled in + */ +ZT_ResultCode ZT_Node_circuitTestBegin(ZT_Node *node,ZT_CircuitTest *test,void (*reportCallback)(ZT_Node *,ZT_CircuitTest *,const ZT_CircuitTestReport *)); + +/** + * Stop listening for results to a given circuit test + * + * This does not free the 'test' structure. The caller may do that + * after calling this method to unregister it. + * + * Any reports that are received for a given test ID after it is + * terminated are ignored. + * + * @param node Node instance + * @param test Test configuration to unregister + */ +void ZT_Node_circuitTestEnd(ZT_Node *node,ZT_CircuitTest *test); + /** * Get ZeroTier One version * diff --git a/node/IncomingPacket.cpp b/node/IncomingPacket.cpp index 49a5981b..c8e4cf5f 100644 --- a/node/IncomingPacket.cpp +++ b/node/IncomingPacket.cpp @@ -30,6 +30,7 @@ #include #include "../version.h" +#include "../include/ZeroTierOne.h" #include "Constants.hpp" #include "Defaults.hpp" @@ -1033,13 +1034,13 @@ bool IncomingPacket::_doCIRCUIT_TEST(const RuntimeEnvironment *RR,const SharedPt outp.append((uint64_t)timestamp); outp.append((uint64_t)testId); outp.append((uint64_t)now); - outp.append((uint8_t)0); // vendor ID, currently unused + outp.append((uint8_t)ZT_VENDOR_ZEROTIER); outp.append((uint8_t)ZT_PROTO_VERSION); outp.append((uint8_t)ZEROTIER_ONE_VERSION_MAJOR); outp.append((uint8_t)ZEROTIER_ONE_VERSION_MINOR); outp.append((uint16_t)ZEROTIER_ONE_VERSION_REVISION); - outp.append((uint16_t)CIRCUIT_TEST_REPORT_PLATFORM_UNSPECIFIED); - outp.append((uint16_t)CIRCUIT_TEST_REPORT_ARCH_UNSPECIFIED); + outp.append((uint16_t)ZT_PLATFORM_UNSPECIFIED); + outp.append((uint16_t)ZT_ARCHITECTURE_UNSPECIFIED); outp.append((uint16_t)0); // error code, currently unused outp.append((uint64_t)0); // flags, currently unused outp.append((uint64_t)packetId()); diff --git a/node/Node.cpp b/node/Node.cpp index 6dc83d4e..cd20972b 100644 --- a/node/Node.cpp +++ b/node/Node.cpp @@ -464,6 +464,28 @@ void Node::setNetconfMaster(void *networkControllerInstance) RR->localNetworkController = reinterpret_cast(networkControllerInstance); } +ZT_ResultCode Node::circuitTestBegin(ZT_CircuitTest *test,void (*reportCallback)(ZT_Node *,ZT_CircuitTest *,const ZT_CircuitTestReport *)) +{ + { + test->_internalPtr = reinterpret_cast(reportCallback); + Mutex::Lock _l(_circuitTests_m); + if (std::find(_circuitTests.begin(),_circuitTests.end(),test) == _circuitTests.end()) + _circuitTests.push_back(test); + } + return ZT_RESULT_OK; +} + +void Node::circuitTestEnd(ZT_CircuitTest *test) +{ + Mutex::Lock _l(_circuitTests_m); + for(;;) { + std::vector< ZT_CircuitTest * >::iterator ct(std::find(_circuitTests.begin(),_circuitTests.end(),test)); + if (ct == _circuitTests.end()) + break; + else _circuitTests.erase(ct); + } +} + /****************************************************************************/ /* Node methods used only within node/ */ /****************************************************************************/ @@ -533,6 +555,20 @@ uint64_t Node::prng() return _prngStream[p]; } +void Node::postCircuitTestReport(const ZT_CircuitTestReport *report) +{ + std::vector< ZT_CircuitTest * > toNotify; + { + Mutex::Lock _l(_circuitTests_m); + for(std::vector< ZT_CircuitTest * >::iterator i(_circuitTests.begin());i!=_circuitTests.end();++i) { + if ((*i)->testId == report->testId) + toNotify.push_back(*i); + } + } + for(std::vector< ZT_CircuitTest * >::iterator i(toNotify.begin());i!=toNotify.end();++i) + (reinterpret_cast((*i)->_internalPtr))(reinterpret_cast(this),*i,report); +} + } // namespace ZeroTier /****************************************************************************/ @@ -721,6 +757,22 @@ void ZT_Node_setNetconfMaster(ZT_Node *node,void *networkControllerInstance) } catch ( ... ) {} } +ZT_ResultCode ZT_Node_circuitTestBegin(ZT_Node *node,ZT_CircuitTest *test,void (*reportCallback)(ZT_Node *,ZT_CircuitTest *,const ZT_CircuitTestReport *)) +{ + try { + return reinterpret_cast(node)->circuitTestBegin(test,reportCallback); + } catch ( ... ) { + return ZT_RESULT_FATAL_ERROR_INTERNAL; + } +} + +void ZT_Node_circuitTestEnd(ZT_Node *node,ZT_CircuitTest *test) +{ + try { + reinterpret_cast(node)->circuitTestEnd(test); + } catch ( ... ) {} +} + int ZT_Node_addLocalInterfaceAddress(ZT_Node *node,const struct sockaddr_storage *addr,int metric,ZT_LocalInterfaceAddressTrust trust) { try { diff --git a/node/Node.hpp b/node/Node.hpp index 0f659f47..20c54471 100644 --- a/node/Node.hpp +++ b/node/Node.hpp @@ -109,6 +109,8 @@ public: int addLocalInterfaceAddress(const struct sockaddr_storage *addr,int metric,ZT_LocalInterfaceAddressTrust trust); void clearLocalInterfaceAddresses(); void setNetconfMaster(void *networkControllerInstance); + ZT_ResultCode circuitTestBegin(ZT_CircuitTest *test,void (*reportCallback)(ZT_Node *,ZT_CircuitTest *,const ZT_CircuitTestReport *)); + void circuitTestEnd(ZT_CircuitTest *test); // Internal functions ------------------------------------------------------ @@ -238,6 +240,13 @@ public: */ uint64_t prng(); + /** + * Post a circuit test report to any listeners for a given test ID + * + * @param report Report (includes test ID) + */ + void postCircuitTestReport(const ZT_CircuitTestReport *report); + private: inline SharedPtr _network(uint64_t nwid) const { @@ -264,6 +273,9 @@ private: std::vector< std::pair< uint64_t, SharedPtr > > _networks; Mutex _networks_m; + std::vector< ZT_CircuitTest * > _circuitTests; + Mutex _circuitTests_m; + std::vector _directPaths; Mutex _directPaths_m; diff --git a/node/Packet.hpp b/node/Packet.hpp index eaffb922..409762c7 100644 --- a/node/Packet.hpp +++ b/node/Packet.hpp @@ -1020,44 +1020,6 @@ public: VERB_CIRCUIT_TEST_REPORT = 18 }; - /** - * Platforms reported in circuit tests - */ - enum CircuitTestReportPlatform - { - CIRCUIT_TEST_REPORT_PLATFORM_UNSPECIFIED = 0, - CIRCUIT_TEST_REPORT_PLATFORM_LINUX = 1, - CIRCUIT_TEST_REPORT_PLATFORM_WINDOWS = 2, - CIRCUIT_TEST_REPORT_PLATFORM_MACOS = 3, - CIRCUIT_TEST_REPORT_PLATFORM_ANDROID = 4, - CIRCUIT_TEST_REPORT_PLATFORM_IOS = 5, - CIRCUIT_TEST_REPORT_PLATFORM_SOLARIS_SMARTOS = 6, - CIRCUIT_TEST_REPORT_PLATFORM_FREEBSD = 7, - CIRCUIT_TEST_REPORT_PLATFORM_NETBSD = 8, - CIRCUIT_TEST_REPORT_PLATFORM_OPENBSD = 9, - CIRCUIT_TEST_REPORT_PLATFORM_RISCOS = 10, - CIRCUIT_TEST_REPORT_PLATFORM_VXWORKS = 11, - CIRCUIT_TEST_REPORT_PLATFORM_FREERTOS = 12, - CIRCUIT_TEST_REPORT_PLATFORM_SYSBIOS = 13, - CIRCUIT_TEST_REPORT_PLATFORM_HURD = 14 - }; - - /** - * Architectures reported in circuit tests - */ - enum CircuitTestReportArchitecture - { - CIRCUIT_TEST_REPORT_ARCH_UNSPECIFIED = 0, - CIRCUIT_TEST_REPORT_ARCH_X86 = 1, - CIRCUIT_TEST_REPORT_ARCH_X64 = 2, - CIRCUIT_TEST_REPORT_ARCH_ARM32 = 3, - CIRCUIT_TEST_REPORT_ARCH_ARM64 = 4, - CIRCUIT_TEST_REPORT_ARCH_MIPS32 = 5, - CIRCUIT_TEST_REPORT_ARCH_MIPS64 = 6, - CIRCUIT_TEST_REPORT_ARCH_POWER32 = 7, - CIRCUIT_TEST_REPORT_ARCH_POWER64 = 8 - }; - /** * Error codes for VERB_ERROR */ -- cgit v1.2.3