From 765082fdb68d8847cbd53cb442cbed5006b28d5f Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Tue, 12 Jul 2016 08:29:50 -0700 Subject: [PATCH] Trusted path support, and version bump to 1.1.9 --- include/ZeroTierOne.h | 28 ++++++++++++++++++++++ node/IncomingPacket.cpp | 26 ++++++++++++++++---- node/Node.cpp | 12 ++++++++++ node/Node.hpp | 13 +--------- node/Packet.hpp | 50 +++++++++++++++++++++++++++++++------- node/Switch.cpp | 7 +++++- node/Topology.hpp | 53 +++++++++++++++++++++++++++++++++++++++++ version.h | 2 +- 8 files changed, 164 insertions(+), 27 deletions(-) diff --git a/include/ZeroTierOne.h b/include/ZeroTierOne.h index d46c64b8d..f55234618 100644 --- a/include/ZeroTierOne.h +++ b/include/ZeroTierOne.h @@ -116,6 +116,11 @@ extern "C" { */ #define ZT_MAX_PEER_NETWORK_PATHS 4 +/** + * Maximum number of trusted physical network paths + */ +#define ZT_MAX_TRUSTED_PATHS 16 + /** * Maximum number of hops in a ZeroTier circuit test * @@ -1837,6 +1842,29 @@ void ZT_Node_clusterHandleIncomingMessage(ZT_Node *node,const void *msg,unsigned */ void ZT_Node_clusterStatus(ZT_Node *node,ZT_ClusterStatus *cs); +/** + * Set trusted paths + * + * A trusted path is a physical network (network/bits) over which both + * encryption and authentication can be skipped to improve performance. + * Each trusted path must have a non-zero unique ID that is the same across + * all participating nodes. + * + * We don't recommend using trusted paths at all unless you really *need* + * near-bare-metal performance. Even on a LAN authentication and encryption + * are never a bad thing, and anything that introduces an "escape hatch" + * for encryption should be treated with the utmost care. + * + * Calling with NULL pointers for networks and ids and a count of zero clears + * all trusted paths. + * + * @param node Node instance + * @param networks Array of [count] networks + * @param ids Array of [count] corresponding non-zero path IDs (zero path IDs are ignored) + * @param count Number of trusted paths-- values greater than ZT_MAX_TRUSTED_PATHS are clipped + */ +void ZT_Node_setTrustedPaths(ZT_Node *node,const struct sockaddr_storage *networks,const uint64_t *ids,unsigned int count); + /** * Do things in the background until Node dies * diff --git a/node/IncomingPacket.cpp b/node/IncomingPacket.cpp index 871297f75..fb4562ab6 100644 --- a/node/IncomingPacket.cpp +++ b/node/IncomingPacket.cpp @@ -42,9 +42,21 @@ namespace ZeroTier { bool IncomingPacket::tryDecode(const RuntimeEnvironment *RR,bool deferred) { - const Address sourceAddress(source()); try { - if ((cipher() == ZT_PROTO_CIPHER_SUITE__C25519_POLY1305_NONE)&&(verb() == Packet::VERB_HELLO)) { + // Check for trusted paths or unencrypted HELLOs (HELLO is the only packet sent in the clear) + const unsigned int c = cipher(); + bool trusted = false; + if (c == ZT_PROTO_CIPHER_SUITE__NO_CRYPTO_TRUSTED_PATH) { + // If this is marked as a packet via a trusted path, check source address and path ID. + // Obviously if no trusted paths are configured this always returns false and such + // packets are dropped on the floor. + if (RR->topology->shouldInboundPathBeTrusted(_remoteAddress,trustedPathId())) { + trusted = true; + } else { + TRACE("dropped packet from %s(%s), cipher set to trusted path mode but path %.16llx@%s is not trusted!",peer->address().toString().c_str(),_remoteAddress.toString().c_str(),trustedPathId(),_remoteAddress.toString().c_str()); + return true; + } + } else if ((c == ZT_PROTO_CIPHER_SUITE__C25519_POLY1305_NONE)&&(verb() == Packet::VERB_HELLO)) { // Unencrypted HELLOs require some potentially expensive verification, so // do this in the background if background processing is enabled. if ((RR->dpEnabled > 0)&&(!deferred)) { @@ -59,12 +71,16 @@ bool IncomingPacket::tryDecode(const RuntimeEnvironment *RR,bool deferred) } } + const Address sourceAddress(source()); SharedPtr peer(RR->topology->getPeer(sourceAddress)); if (peer) { - if (!dearmor(peer->key())) { - TRACE("dropped packet from %s(%s), MAC authentication failed (size: %u)",peer->address().toString().c_str(),_remoteAddress.toString().c_str(),size()); - return true; + if (!trusted) { + if (!dearmor(peer->key())) { + TRACE("dropped packet from %s(%s), MAC authentication failed (size: %u)",peer->address().toString().c_str(),_remoteAddress.toString().c_str(),size()); + return true; + } } + if (!uncompress()) { TRACE("dropped packet from %s(%s), compressed data invalid",peer->address().toString().c_str(),_remoteAddress.toString().c_str()); return true; diff --git a/node/Node.cpp b/node/Node.cpp index bedbba946..058df32d2 100644 --- a/node/Node.cpp +++ b/node/Node.cpp @@ -745,6 +745,11 @@ void Node::postCircuitTestReport(const ZT_CircuitTestReport *report) (reinterpret_cast((*i)->_internalPtr))(reinterpret_cast(this),*i,report); } +void Node::setTrustedPaths(const struct sockaddr_storage *networks,const uint64_t *ids,unsigned int count) +{ + RR->topology->setTrustedPaths(reinterpret_cast(networks),ids,count); +} + } // namespace ZeroTier /****************************************************************************/ @@ -1014,6 +1019,13 @@ void ZT_Node_clusterStatus(ZT_Node *node,ZT_ClusterStatus *cs) } catch ( ... ) {} } +void ZT_Node_setTrustedPaths(ZT_Node *node,const struct sockaddr_storage *networks,const uint64_t *ids,unsigned int count) +{ + try { + reinterpret_cast(node)->setTrustedPaths(networks,ids,count); + } catch ( ... ) {} +} + void ZT_Node_backgroundThreadMain(ZT_Node *node) { try { diff --git a/node/Node.hpp b/node/Node.hpp index 6ac23ca0f..0a39d1eed 100644 --- a/node/Node.hpp +++ b/node/Node.hpp @@ -248,26 +248,15 @@ public: */ inline int configureVirtualNetworkPort(uint64_t nwid,void **nuptr,ZT_VirtualNetworkConfigOperation op,const ZT_VirtualNetworkConfig *nc) { return _virtualNetworkConfigFunction(reinterpret_cast(this),_uPtr,nwid,nuptr,op,nc); } - /** - * @return True if we appear to be online - */ inline bool online() const throw() { return _online; } #ifdef ZT_TRACE void postTrace(const char *module,unsigned int line,const char *fmt,...); #endif - /** - * @return Next 64-bit random number (not for cryptographic use) - */ 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); + void setTrustedPaths(const struct sockaddr_storage *networks,const uint64_t *ids,unsigned int count); private: inline SharedPtr _network(uint64_t nwid) const diff --git a/node/Packet.hpp b/node/Packet.hpp index 79fff344d..3d95b0baa 100644 --- a/node/Packet.hpp +++ b/node/Packet.hpp @@ -57,11 +57,13 @@ * + Supports in-band world (root server definition) updates * + Clustering! (Though this will work with protocol v4 clients.) * + Otherwise backward compatible with protocol v4 - * 6 - 1.1.5 ... CURRENT + * 6 - 1.1.5 ... 1.1.10 * + Deprecate old dictionary-based network config format * + Introduce new binary serialized network config and meta-data + * 7 - 1.1.10 -- CURRENT + * + Introduce trusted paths for local SDN use */ -#define ZT_PROTO_VERSION 6 +#define ZT_PROTO_VERSION 7 /** * Minimum supported protocol version @@ -100,10 +102,21 @@ #define ZT_PROTO_CIPHER_SUITE__C25519_POLY1305_SALSA2012 1 /** - * DEPRECATED payload encrypted flag, will be removed for re-use soon. + * Cipher suite: NONE * - * This has been replaced by the three-bit cipher suite selection field where - * a value of 0 indicates unencrypted (but authenticated) messages. + * This differs from POLY1305/NONE in that *no* crypto is done, not even + * authentication. This is for trusted local LAN interconnects for internal + * SDN use within a data center. + * + * For this mode the MAC field becomes a trusted path ID and must match the + * configured ID of a trusted path or the packet is discarded. + */ +#define ZT_PROTO_CIPHER_SUITE__NO_CRYPTO_TRUSTED_PATH 2 + +/** + * DEPRECATED payload encrypted flag, may be re-used in the future. + * + * This has been replaced by the three-bit cipher suite selection field. */ #define ZT_PROTO_FLAG_ENCRYPTED 0x80 @@ -337,7 +350,7 @@ namespace ZeroTier { * <[5] destination ZT address> * <[5] source ZT address> * <[1] flags/cipher/hops> - * <[8] 64-bit MAC> + * <[8] 64-bit MAC (or trusted path ID in trusted path mode)> * [... -- begin encryption envelope -- ...] * <[1] encrypted flags (MS 3 bits) and verb (LS 5 bits)> * [... verb-specific payload ...] @@ -1218,7 +1231,6 @@ public: */ inline unsigned int cipher() const { - // Note: this uses the new cipher spec field, which is incompatible with <1.0.0 peers return (((unsigned int)(*this)[ZT_PACKET_IDX_FLAGS] & 0x38) >> 3); } @@ -1229,12 +1241,30 @@ public: { unsigned char &b = (*this)[ZT_PACKET_IDX_FLAGS]; b = (b & 0xc7) | (unsigned char)((c << 3) & 0x38); // bits: FFCCCHHH - // DEPRECATED "encrypted" flag -- used by pre-1.0.3 peers + // Set DEPRECATED "encrypted" flag -- used by pre-1.0.3 peers if (c == ZT_PROTO_CIPHER_SUITE__C25519_POLY1305_SALSA2012) b |= ZT_PROTO_FLAG_ENCRYPTED; else b &= (~ZT_PROTO_FLAG_ENCRYPTED); } + /** + * Get the trusted path ID for this packet (only meaningful if cipher is trusted path) + * + * @return Trusted path ID (from MAC field) + */ + inline uint64_t trustedPathId() const { return at(ZT_PACKET_IDX_MAC); } + + /** + * Set this packet's trusted path ID and set the cipher spec to trusted path + * + * @param tpid Trusted path ID + */ + inline void setTrusted(const uint64_t tpid) + { + setCipher(ZT_PROTO_CIPHER_SUITE__NO_CRYPTO_TRUSTED_PATH); + setAt(ZT_PACKET_IDX_MAC,tpid); + } + /** * Get this packet's unique ID (the IV field interpreted as uint64_t) * @@ -1278,6 +1308,10 @@ public: /** * Verify and (if encrypted) decrypt packet * + * This does not handle trusted path mode packets and will return false + * for these. These are handled in IncomingPacket if the sending physical + * address and MAC field match a trusted path. + * * @param key 32-byte key * @return False if packet is invalid or failed MAC authenticity check */ diff --git a/node/Switch.cpp b/node/Switch.cpp index b134cc693..bf3afe33c 100644 --- a/node/Switch.cpp +++ b/node/Switch.cpp @@ -849,7 +849,12 @@ bool Switch::_trySend(const Packet &packet,bool encrypt,uint64_t nwid) unsigned int chunkSize = std::min(tmp.size(),(unsigned int)ZT_UDP_DEFAULT_PAYLOAD_MTU); tmp.setFragmented(chunkSize < tmp.size()); - tmp.armor(peer->key(),encrypt); + const uint64_t trustedPathId = RR->topology->getOutboundPathTrust(viaPath->address()); + if (trustedPathId) { + tmp.setTrusted(trustedPathId); + } else { + tmp.armor(peer->key(),encrypt); + } if (viaPath->send(RR,tmp.data(),chunkSize,now)) { if (chunkSize < tmp.size()) { diff --git a/node/Topology.hpp b/node/Topology.hpp index 86fbb011f..03c491e50 100644 --- a/node/Topology.hpp +++ b/node/Topology.hpp @@ -28,6 +28,7 @@ #include #include "Constants.hpp" +#include "../include/ZeroTierOne.h" #include "Address.hpp" #include "Identity.hpp" @@ -252,12 +253,64 @@ public: */ inline bool amRoot() const throw() { return _amRoot; } + /** + * Get the outbound trusted path ID for a physical address, or 0 if none + * + * @param physicalAddress Physical address to which we are sending the packet + * @return Trusted path ID or 0 if none (0 is not a valid trusted path ID) + */ + inline uint64_t getOutboundPathTrust(const InetAddress &physicalAddress) + { + for(unsigned int i=0;i<_trustedPathCount;++i) { + if (_trustedPathNetworks[i].containsAddress(physicalAddress)) + return _trustedPathIds[i]; + } + return 0; + } + + /** + * Check whether in incoming trusted path marked packet is valid + * + * @param physicalAddress Originating physical address + * @param trustedPathId Trusted path ID from packet (from MAC field) + */ + inline bool shouldInboundPathBeTrusted(const InetAddress &physicalAddress,const uint64_t trustedPathId) + { + for(unsigned int i=0;i<_trustedPathCount;++i) { + if ((_trustedPathIds[i] == trustedPathId)&&(_trustedPathNetworks[i].containsAddress(physicalAddress))) + return true; + } + return false; + } + + /** + * Set trusted paths in this topology + * + * @param networks Array of networks (prefix/netmask bits) + * @param ids Array of trusted path IDs + * @param count Number of trusted paths (if larger than ZT_MAX_TRUSTED_PATHS overflow is ignored) + */ + inline void setTrustedPaths(const InetAddress *networks,const uint64_t *ids,unsigned int count) + { + if (count > ZT_MAX_TRUSTED_PATHS) + count = ZT_MAX_TRUSTED_PATHS; + Mutex::Lock _l(_lock); + for(unsigned int i=0;i > _peers; std::vector< Address > _rootAddresses; diff --git a/version.h b/version.h index 012660dd4..b0df04f8f 100644 --- a/version.h +++ b/version.h @@ -32,6 +32,6 @@ /** * Revision */ -#define ZEROTIER_ONE_VERSION_REVISION 8 +#define ZEROTIER_ONE_VERSION_REVISION 9 #endif